summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/platform
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2018-05-15 10:20:33 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2018-05-15 10:28:57 +0000
commitd17ea114e5ef69ad5d5d7413280a13e6428098aa (patch)
tree2c01a75df69f30d27b1432467cfe7c1467a498da /chromium/third_party/blink/renderer/platform
parent8c5c43c7b138c9b4b0bf56d946e61d3bbc111bec (diff)
downloadqtwebengine-chromium-d17ea114e5ef69ad5d5d7413280a13e6428098aa.tar.gz
BASELINE: Update Chromium to 67.0.3396.47
Change-Id: Idcb1341782e417561a2473eeecc82642dafda5b7 Reviewed-by: Michal Klocek <michal.klocek@qt.io>
Diffstat (limited to 'chromium/third_party/blink/renderer/platform')
-rw-r--r--chromium/third_party/blink/renderer/platform/BUILD.gn2275
-rw-r--r--chromium/third_party/blink/renderer/platform/DEPS76
-rw-r--r--chromium/third_party/blink/renderer/platform/InstanceCountersMemoryDumpProvider.cc32
-rw-r--r--chromium/third_party/blink/renderer/platform/OWNERS30
-rw-r--r--chromium/third_party/blink/renderer/platform/PRESUBMIT.py62
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/DEPS4
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/OWNERS6
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/animation_translation_util.cc130
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/animation_translation_util.h47
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/animation_translation_util_test.cc73
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/animation_utilities.h91
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_animation.cc124
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_animation.h82
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_animation_client.cc11
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_animation_client.h24
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_animation_curve.h26
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_animation_delegate.h36
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_animation_host.cc71
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_animation_host.h50
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_animation_host_test.cc36
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_animation_test.cc142
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_animation_timeline.cc48
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_animation_timeline.h44
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_animation_timeline_test.cc44
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_filter_animation_curve.cc38
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_filter_animation_curve.h53
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_filter_keyframe.cc36
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_filter_keyframe.h39
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_float_animation_curve.cc71
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_float_animation_curve.h67
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_float_animation_curve_test.cc267
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_float_keyframe.cc38
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_float_keyframe.h38
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_keyframe.cc16
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_keyframe.h33
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_keyframe_model.cc133
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_keyframe_model.h89
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_keyframe_model_test.cc45
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_scroll_offset_animation_curve.cc85
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_scroll_offset_animation_curve.h66
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_target_property.h16
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_transform_animation_curve.cc40
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_transform_animation_curve.h54
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_transform_keyframe.cc36
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_transform_keyframe.h38
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_transform_operations.cc65
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_transform_operations.h41
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/timing_function.cc262
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/timing_function.h252
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/timing_function_test.cc550
-rw-r--r--chromium/third_party/blink/renderer/platform/async_file_system_callbacks.h116
-rw-r--r--chromium/third_party/blink/renderer/platform/async_method_runner.h142
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/OWNERS5
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/android/fft_frame_open_maxdl_android.cc160
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/audio_array.h162
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/audio_bus.cc733
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/audio_bus.h187
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/audio_channel.cc122
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/audio_channel.h134
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/audio_delay_dsp_kernel.cc167
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/audio_delay_dsp_kernel.h74
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/audio_destination.cc287
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/audio_destination.h153
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/audio_destination_consumer.h49
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/audio_dsp_kernel.cc37
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/audio_dsp_kernel.h84
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/audio_dsp_kernel_processor.cc168
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/audio_dsp_kernel_processor.h84
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/audio_file_reader.h64
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/audio_io_callback.h62
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/audio_processor.cc37
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/audio_processor.h95
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/audio_resampler.cc120
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/audio_resampler.h77
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/audio_resampler_kernel.cc145
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/audio_resampler_kernel.h87
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/audio_source_provider.h63
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/audio_source_provider_client.h53
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/audio_utilities.cc106
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/audio_utilities.h66
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/biquad.cc901
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/biquad.h135
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/cone.cc73
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/cone.h69
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/cpu/arm/vector_math_neon.h253
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/cpu/mips/vector_math_msa.h219
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/cpu/x86/vector_math_avx.cc31
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/cpu/x86/vector_math_avx.h90
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/cpu/x86/vector_math_impl.h357
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/cpu/x86/vector_math_sse.cc31
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/cpu/x86/vector_math_sse.h90
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/cpu/x86/vector_math_x8_6.h413
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/denormal_disabler.h151
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/direct_convolver.cc99
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/direct_convolver.h65
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/distance_effect.cc93
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/distance_effect.h80
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/down_sampler.cc161
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/down_sampler.h77
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/dynamics_compressor.cc199
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/dynamics_compressor.h118
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/dynamics_compressor_kernel.cc509
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/dynamics_compressor_kernel.h142
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/equal_power_panner.cc248
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/equal_power_panner.h67
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/ffmpeg/fft_frame_ffmpeg.cc164
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/fft_convolver.cc125
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/fft_convolver.h82
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/fft_frame.cc273
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/fft_frame.h129
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/fft_frame_stub.cc74
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/hrtf_database.cc130
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/hrtf_database.h101
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/hrtf_database_loader.cc138
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/hrtf_database_loader.h98
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/hrtf_elevation.cc362
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/hrtf_elevation.h132
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/hrtf_kernel.cc147
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/hrtf_kernel.h110
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/hrtf_panner.cc373
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/hrtf_panner.h124
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/iir_filter.cc223
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/iir_filter.h68
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/mac/fft_frame_mac.cc142
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/mac/vector_math_mac.h150
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/multi_channel_resampler.cc131
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/multi_channel_resampler.h68
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/panner.cc53
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/panner.h88
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/push_pull_fifo.cc215
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/push_pull_fifo.h101
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/push_pull_fifo_multithread_test.cc225
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/push_pull_fifo_test.cc367
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/Composite.flacbin0 -> 145812 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P000.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P015.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P030.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P045.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P060.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P075.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P090.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P315.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P330.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P345.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P000.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P015.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P030.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P045.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P060.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P075.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P090.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P315.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P330.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P345.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P000.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P015.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P030.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P045.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P060.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P075.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P090.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P315.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P330.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P345.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P000.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P015.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P030.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P045.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P060.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P075.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P090.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P315.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P330.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P345.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P000.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P015.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P030.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P045.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P060.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P075.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P090.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P315.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P330.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P345.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P000.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P015.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P030.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P045.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P060.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P075.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P090.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P315.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P330.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P345.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P000.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P015.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P030.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P045.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P060.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P075.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P090.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P315.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P330.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P345.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P000.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P015.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P030.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P045.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P060.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P075.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P090.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P315.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P330.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P345.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P000.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P015.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P030.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P045.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P060.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P075.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P090.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P315.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P330.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P345.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P000.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P015.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P030.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P045.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P060.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P075.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P090.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P315.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P330.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P345.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P000.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P015.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P030.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P045.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P060.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P075.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P090.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P315.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P330.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P345.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P000.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P015.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P030.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P045.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P060.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P075.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P090.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P315.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P330.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P345.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P000.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P015.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P030.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P045.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P060.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P075.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P090.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P315.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P330.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P345.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P000.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P015.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P030.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P045.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P060.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P075.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P090.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P315.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P330.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P345.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P000.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P015.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P030.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P045.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P060.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P075.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P090.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P315.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P330.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P345.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P000.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P015.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P030.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P045.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P060.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P075.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P090.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P315.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P330.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P345.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P000.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P015.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P030.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P045.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P060.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P075.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P090.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P315.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P330.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P345.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P000.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P015.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P030.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P045.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P060.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P075.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P090.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P315.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P330.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P345.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P000.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P015.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P030.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P045.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P060.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P075.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P090.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P315.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P330.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P345.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P000.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P015.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P030.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P045.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P060.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P075.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P090.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P315.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P330.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P345.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P000.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P015.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P030.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P045.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P060.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P075.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P090.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P315.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P330.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P345.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P000.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P015.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P030.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P045.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P060.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P075.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P090.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P315.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P330.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P345.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P000.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P015.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P030.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P045.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P060.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P075.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P090.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P315.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P330.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P345.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P000.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P015.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P030.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P045.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P060.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P075.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P090.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P315.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P330.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P345.wavbin0 -> 1068 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/resources/README20
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/reverb.cc293
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/reverb.h87
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/reverb_accumulation_buffer.cc116
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/reverb_accumulation_buffer.h80
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/reverb_convolver.cc226
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/reverb_convolver.h101
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/reverb_convolver_stage.cc202
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/reverb_convolver_stage.h104
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/reverb_input_buffer.cc79
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/reverb_input_buffer.h82
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/sinc_resampler.cc498
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/sinc_resampler.h103
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/stereo_panner.cc169
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/stereo_panner.h43
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/up_sampler.cc143
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/up_sampler.h79
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/vector_math.cc204
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/vector_math.h147
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/vector_math_scalar.h433
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/vector_math_test.cc519
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/DEPS3
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/OWNERS5
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/README.md10
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/RuntimeCallStats.md23
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/TraceWrapperReference.md265
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/active_script_wrappable_base.cc60
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/active_script_wrappable_base.h42
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/callback_function_base.cc37
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/callback_function_base.h130
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/callback_interface_base.cc40
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/callback_interface_base.h147
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/dom_data_store.h215
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/dom_wrapper_map.h179
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/dom_wrapper_world.cc325
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/dom_wrapper_world.h237
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/microtask.cc60
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/microtask.h77
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/origin_trial_features.cc70
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/origin_trial_features.h59
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/runtime_call_stats.cc208
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/runtime_call_stats.h390
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/runtime_call_stats_test.cc330
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/scoped_persistent.h111
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/script_forbidden_scope.cc23
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/script_forbidden_scope.h68
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/script_promise_properties.h18
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/script_state.cc58
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/script_state.h199
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/script_wrappable.cc52
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/script_wrappable.h209
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/script_wrappable_marking_visitor.cc328
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/script_wrappable_marking_visitor.h232
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/script_wrappable_visitor.cc28
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/script_wrappable_visitor.h153
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/script_wrappable_visitor_verifier.h39
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/shared_persistent.h72
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/string_resource.cc166
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/string_resource.h140
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/to_v8.h298
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/trace_wrapper_base.h32
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/trace_wrapper_member.h120
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h81
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/trace_wrapper_v8_string.cc29
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/trace_wrapper_v8_string.h48
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/v0_custom_element_binding.cc53
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/v0_custom_element_binding.h59
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/v8_binding.cc53
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/v8_binding.h374
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/v8_binding_macros.h70
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/v8_dom_activity_logger.cc134
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/v8_dom_activity_logger.h95
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/v8_dom_wrapper.cc104
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/v8_dom_wrapper.h200
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/v8_global_value_map.h118
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/v8_object_constructor.cc119
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/v8_object_constructor.h99
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/v8_per_context_data.cc154
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/v8_per_context_data.h169
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.cc369
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h311
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/v8_private_property.cc33
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/v8_private_property.h232
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/v8_throw_exception.cc54
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/v8_throw_exception.h65
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/v8_value_cache.cc162
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/v8_value_cache.h128
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/wrapper_creation_security_check.cc41
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/wrapper_creation_security_check.h46
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/wrapper_type_info.cc14
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/wrapper_type_info.h203
-rw-r--r--chromium/third_party/blink/renderer/platform/blob/BUILD.gn88
-rw-r--r--chromium/third_party/blink/renderer/platform/blob/OWNERS10
-rw-r--r--chromium/third_party/blink/renderer/platform/blob/blob_bytes_provider.cc261
-rw-r--r--chromium/third_party/blink/renderer/platform/blob/blob_bytes_provider.h65
-rw-r--r--chromium/third_party/blink/renderer/platform/blob/blob_bytes_provider_test.cc361
-rw-r--r--chromium/third_party/blink/renderer/platform/blob/blob_data.cc396
-rw-r--r--chromium/third_party/blink/renderer/platform/blob/blob_data.h229
-rw-r--r--chromium/third_party/blink/renderer/platform/blob/blob_data_test.cc456
-rw-r--r--chromium/third_party/blink/renderer/platform/blob/blob_registry.cc64
-rw-r--r--chromium/third_party/blink/renderer/platform/blob/blob_registry.h69
-rw-r--r--chromium/third_party/blink/renderer/platform/blob/blob_url.cc62
-rw-r--r--chromium/third_party/blink/renderer/platform/blob/blob_url.h64
-rw-r--r--chromium/third_party/blink/renderer/platform/blob/serialized_blob.typemap14
-rw-r--r--chromium/third_party/blink/renderer/platform/blob/serialized_blob_struct_traits.cc23
-rw-r--r--chromium/third_party/blink/renderer/platform/blob/serialized_blob_struct_traits.h52
-rw-r--r--chromium/third_party/blink/renderer/platform/blob/testing/fake_blob.cc43
-rw-r--r--chromium/third_party/blink/renderer/platform/blob/testing/fake_blob.h35
-rw-r--r--chromium/third_party/blink/renderer/platform/blob/testing/fake_blob_registry.cc48
-rw-r--r--chromium/third_party/blink/renderer/platform/blob/testing/fake_blob_registry.h54
-rw-r--r--chromium/third_party/blink/renderer/platform/blob/testing/fake_blob_url_store.cc43
-rw-r--r--chromium/third_party/blink/renderer/platform/blob/testing/fake_blob_url_store.h35
-rw-r--r--chromium/third_party/blink/renderer/platform/calculation_value.h73
-rw-r--r--chromium/third_party/blink/renderer/platform/clipboard/clipboard_mime_types.cc46
-rw-r--r--chromium/third_party/blink/renderer/platform/clipboard/clipboard_mime_types.h51
-rw-r--r--chromium/third_party/blink/renderer/platform/clipboard/clipboard_utilities.cc65
-rw-r--r--chromium/third_party/blink/renderer/platform/clipboard/clipboard_utilities.h48
-rw-r--r--chromium/third_party/blink/renderer/platform/clipboard/clipboard_utilities_win.cc48
-rw-r--r--chromium/third_party/blink/renderer/platform/color_data.gperf177
-rw-r--r--chromium/third_party/blink/renderer/platform/content_decryption_module_result.h44
-rw-r--r--chromium/third_party/blink/renderer/platform/content_setting_callbacks.cc51
-rw-r--r--chromium/third_party/blink/renderer/platform/content_setting_callbacks.h39
-rw-r--r--chromium/third_party/blink/renderer/platform/context_menu.cc51
-rw-r--r--chromium/third_party/blink/renderer/platform/context_menu.h53
-rw-r--r--chromium/third_party/blink/renderer/platform/context_menu_item.cc112
-rw-r--r--chromium/third_party/blink/renderer/platform/context_menu_item.h105
-rw-r--r--chromium/third_party/blink/renderer/platform/cpu/mips/common_macros_msa.h814
-rw-r--r--chromium/third_party/blink/renderer/platform/cross_origin_attribute_value.h18
-rw-r--r--chromium/third_party/blink/renderer/platform/cross_thread_copier.cc103
-rw-r--r--chromium/third_party/blink/renderer/platform/cross_thread_copier.h293
-rw-r--r--chromium/third_party/blink/renderer/platform/cross_thread_functional.h46
-rw-r--r--chromium/third_party/blink/renderer/platform/crypto.cc70
-rw-r--r--chromium/third_party/blink/renderer/platform/crypto.h77
-rw-r--r--chromium/third_party/blink/renderer/platform/crypto_result.h68
-rw-r--r--chromium/third_party/blink/renderer/platform/cursor.cc362
-rw-r--r--chromium/third_party/blink/renderer/platform/cursor.h175
-rw-r--r--chromium/third_party/blink/renderer/platform/data_resource_helper.cc30
-rw-r--r--chromium/third_party/blink/renderer/platform/data_resource_helper.h17
-rw-r--r--chromium/third_party/blink/renderer/platform/date_components.cc724
-rw-r--r--chromium/third_party/blink/renderer/platform/date_components.h238
-rw-r--r--chromium/third_party/blink/renderer/platform/decimal.cc1007
-rw-r--r--chromium/third_party/blink/renderer/platform/decimal.h194
-rw-r--r--chromium/third_party/blink/renderer/platform/decimal_test.cc1161
-rw-r--r--chromium/third_party/blink/renderer/platform/drag_image.cc341
-rw-r--r--chromium/third_party/blink/renderer/platform/drag_image.h96
-rw-r--r--chromium/third_party/blink/renderer/platform/drag_image_test.cc183
-rw-r--r--chromium/third_party/blink/renderer/platform/encrypted_media_request.h38
-rw-r--r--chromium/third_party/blink/renderer/platform/event_dispatch_forbidden_scope.cc13
-rw-r--r--chromium/third_party/blink/renderer/platform/event_dispatch_forbidden_scope.h74
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/file_path_conversion.cc46
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/file_path_conversion_test.cc54
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/interface_registry.cc29
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/platform.cc292
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/service_registry.cc21
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/url_conversion.cc29
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_active_gesture_animation.cc60
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_active_gesture_animation.h70
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_audio_bus.cc93
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_audio_device.cc38
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_blob_info.cc114
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_cache.cc85
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_canvas_capture_handler.cc9
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_clipboard_impl.cc284
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_clipboard_impl.h56
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_coalesced_input_event.cc142
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_content_decryption_module.cc31
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_content_decryption_module_access.cc11
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_content_decryption_module_result.cc57
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_content_decryption_module_session.cc36
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_content_setting_callbacks.cc57
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_cors.cc254
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_cors_test.cc109
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_crypto_algorithm.cc547
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_crypto_key.cc122
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_crypto_key_algorithm.cc185
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_crypto_result.cc101
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_cursor_info.cc49
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_data.cc75
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_data_consumer_handle.cc38
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_drag_data.cc50
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_encrypted_media_client.cc11
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_encrypted_media_key_information.cc38
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_encrypted_media_request.cc60
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_file_system_callbacks.cc167
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_font.cc122
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_font_description.cc75
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_http_body.cc192
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_http_header_map.cc76
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_http_load_info.cc132
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_image.cc195
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_image_generator.cc43
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_image_test.cc90
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_input_event.cc83
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_media_constraints.cc558
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_media_player_client.cc12
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_media_player_encrypted_media_client.cc12
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_media_player_source.cc36
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_media_recorder_handler.cc9
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_media_stream.cc155
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_media_stream_source.cc246
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_media_stream_track.cc151
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_memory_coordinator.cc24
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_mixed_content.cc96
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_mock_clipboard.cc23
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_network_state_notifier.cc58
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_prerender.cc126
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_prerendering_support.cc49
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_presentation_receiver.cc11
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_rtc_answer_options.cc27
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_rtc_offer_options.cc51
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_rtc_peer_connection_handler_client.cc14
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_rtc_rtp_contributing_source.cc11
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_rtc_rtp_receiver.cc11
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_rtc_rtp_sender.cc11
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_rtc_session_description.cc103
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_rtc_session_description_request.cc64
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_rtc_stats.cc17
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_rtc_stats_request.cc69
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_rtc_stats_response.cc52
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_rtc_void_request.cc60
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_runtime_features.cc521
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_scrollbar_impl.cc124
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_scrollbar_impl.h82
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_scrollbar_theme_client_impl.cc184
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_scrollbar_theme_client_impl.h89
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_scrollbar_theme_geometry_native.cc99
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_scrollbar_theme_geometry_native.h72
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_scrollbar_theme_painter.cc174
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_security_origin.cc142
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_service_worker_installed_scripts_manager.cc54
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_service_worker_request.cc248
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_service_worker_response.cc222
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_service_worker_stream_handle.cc31
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_speech_synthesis_utterance.cc86
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_speech_synthesis_voice.cc68
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_speech_synthesizer_client_impl.cc91
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_speech_synthesizer_client_impl.h68
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_string.cc186
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_string_test.cc50
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_surface_layer_bridge.cc20
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_text_run.cc43
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_thread_safe_data.cc80
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_url.cc62
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_url_error.cc39
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_url_load_timing.cc207
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_url_loader_client.cc11
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_url_loader_test_delegate.cc52
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_url_request.cc433
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_url_request_test.cc73
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_url_response.cc456
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_url_response_test.cc85
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_video_frame_submitter.cc38
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/wrapped_resource_request.h58
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/wrapped_resource_response.h57
-rw-r--r--chromium/third_party/blink/renderer/platform/feature_policy/feature_policy.cc300
-rw-r--r--chromium/third_party/blink/renderer/platform/feature_policy/feature_policy.h79
-rw-r--r--chromium/third_party/blink/renderer/platform/feature_policy/feature_policy_fuzzer.cc26
-rw-r--r--chromium/third_party/blink/renderer/platform/feature_policy/feature_policy_test.cc322
-rw-r--r--chromium/third_party/blink/renderer/platform/file_metadata.cc78
-rw-r--r--chromium/third_party/blink/renderer/platform/file_metadata.h79
-rw-r--r--chromium/third_party/blink/renderer/platform/file_system_type.h56
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/LocaleInFonts.md128
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/README.md382
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/accept_languages_resolver.cc42
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/accept_languages_resolver.h27
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/accept_languages_resolver_test.cc65
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/alternate_font_family.h119
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/android/font_cache_android.cc136
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/android/font_cache_android_test.cc57
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/bitmap_glyphs_blacklist.cc32
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/bitmap_glyphs_blacklist.h21
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/bitmap_glyphs_blacklist_test.cc45
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/canvas_rotation_in_vertical.h13
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/character_range.h28
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/custom_font_data.h50
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/fallback_list_composite_key.h101
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font.cc535
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font.h262
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_baseline.h35
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_cache.cc432
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_cache.h354
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_cache_client.h50
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_cache_key.h112
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_cache_memory_dump_provider.cc26
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_cache_memory_dump_provider.h36
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_cache_test.cc73
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_custom_platform_data.cc187
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_custom_platform_data.h85
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_data.cc32
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_data.h70
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_data_cache.cc159
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_data_cache.h100
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_data_for_range_set.cc22
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_data_for_range_set.h78
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_description.cc507
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_description.h423
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_description_test.cc122
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_face_creation_params.h139
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_fallback_iterator.cc246
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_fallback_iterator.h82
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_fallback_list.cc251
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_fallback_list.h107
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_fallback_priority.cc14
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_fallback_priority.h29
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_family.cc59
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_family.h105
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_family_names.json540
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_family_test.cc42
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_global_context.cc46
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_global_context.h118
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_metrics.cc141
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_metrics.h208
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_orientation.cc25
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_orientation.h80
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_platform_data.cc339
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_platform_data.h205
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_platform_data_test.cc71
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_selection_algorithm.cc160
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_selection_algorithm.h63
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_selection_types.cc84
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_selection_types.h484
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_selection_types_test.cc55
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_selector.cc50
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_selector.h86
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_selector_client.h25
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_smoothing_mode.cc25
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_smoothing_mode.h45
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_test.cc50
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_test_utilities.cc14
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_test_utilities.h14
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_variant_east_asian.cc51
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_variant_east_asian.h85
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_variant_numeric.cc80
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_variant_numeric.h103
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_vertical_position_type.h25
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_width_variant.cc25
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_width_variant.h52
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/fuchsia/font_cache_fuchsia.cc63
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/generic_font_family_settings.cc229
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/generic_font_family_settings.h129
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/generic_font_family_settings_test.cc29
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/glyph.h39
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/glyph_metrics_map.h141
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/linux/font_cache_linux.cc167
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/mac/core_text_variations_support.cc20
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/mac/core_text_variations_support.h13
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/mac/font_cache_mac.mm296
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/mac/font_family_matcher_mac.h47
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/mac/font_family_matcher_mac.mm337
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/mac/font_family_matcher_mac_test.mm41
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/mac/font_platform_data_mac.mm207
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/ng_text_fragment_paint_info.h31
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/opentype/font_format_check.cc63
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/opentype/font_format_check.h39
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/opentype/font_settings.cc34
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/opentype/font_settings.h108
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/opentype/font_settings_test.cc64
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.cc162
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.h57
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support_mpl.cc52
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_types.h102
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_vertical_data.cc315
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_vertical_data.h83
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_vertical_data_test.cc75
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/orientation_iterator.cc53
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/orientation_iterator.h42
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/orientation_iterator_test.cc169
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/script_run_iterator.cc365
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/script_run_iterator.h116
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/script_run_iterator_test.cc691
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/segmented_font_data.cc89
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/segmented_font_data.h68
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.cc33
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.h218
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.cc136
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.h75
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper_test.cc422
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/case_mapping_harf_buzz_buffer_filler.cc94
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/case_mapping_harf_buzz_buffer_filler.h42
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_face.cc448
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_face.h87
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_font_cache.h151
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.cc947
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.h109
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper_fuzzer.cc52
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper_test.cc1258
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/run_segmenter.cc108
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/run_segmenter.h67
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/run_segmenter_test.cc251
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/shape_cache.cc42
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/shape_cache.h252
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc953
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result.h212
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer.cc420
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer.h170
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer_test.cc356
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_buffer.cc235
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_buffer.h77
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_inline_headers.h229
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.cc195
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.h80
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_test_info.cc66
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_test_info.h71
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.cc390
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.h116
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker_test.cc344
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/simple_font_data.cc366
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/simple_font_data.h250
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/skia/font_cache_skia.cc305
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/skia/skia_text_metrics.cc91
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/skia/skia_text_metrics.h33
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/small_caps_iterator.cc50
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/small_caps_iterator.h44
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/small_caps_iterator_test.cc117
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/symbols_iterator.cc131
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/symbols_iterator.h41
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/symbols_iterator_test.cc377
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/text_rendering_mode.cc25
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/text_rendering_mode.h45
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/typesetting_features.cc32
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/typesetting_features.h49
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/typesetting_features_test.cc27
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/unicode_range_set.cc94
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/unicode_range_set.h77
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/unicode_range_set_test.cc89
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/utf16_text_iterator.cc66
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/utf16_text_iterator.h79
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/vdmx_parser.cc196
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/vdmx_parser.h45
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/web_font_decoder.cc231
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/web_font_decoder.h66
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/web_font_render_style.cc119
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/web_font_typeface_factory.cc116
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/web_font_typeface_factory.h51
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/win/font_cache_skia_win.cc426
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/win/font_fallback_win.cc544
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/win/font_fallback_win.h61
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/win/font_platform_data_win.cc123
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/DEPS5
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/cg/float_point_cg.cc39
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/cg/float_rect_cg.cc39
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/cg/float_size_cg.cc39
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/cg/int_point_cg.cc39
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/cg/int_rect_cg.cc44
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/cg/int_size_cg.cc40
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/double_point.cc36
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/double_point.h137
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/double_rect.cc59
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/double_rect.h92
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/double_rect_test.cc23
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/double_size.cc30
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/double_size.h107
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/float_box.cc43
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/float_box.h145
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/float_box_test.cc125
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/float_box_test_helpers.cc81
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/float_box_test_helpers.h47
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/float_point.cc125
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/float_point.h242
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/float_point_3d.cc67
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/float_point_3d.h165
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/float_point_test.cc31
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/float_polygon.cc246
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/float_polygon.h172
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/float_polygon_test.cc342
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/float_quad.cc262
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/float_quad.h185
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/float_quad_test.cc113
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/float_rect.cc257
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/float_rect.h263
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/float_rect_outsets.cc19
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/float_rect_outsets.h49
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/float_rect_test.cc260
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/float_rounded_rect.cc394
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/float_rounded_rect.h246
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/float_rounded_rect_test.cc251
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/float_shape_helpers.h18
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/float_size.cc69
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/float_size.h206
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/float_size_test.cc32
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/geometry_as_json.cc52
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/geometry_as_json.h46
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/geometry_test_helpers.cc43
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/geometry_test_helpers.h23
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/int_point.cc19
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/int_point.h161
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/int_rect.cc207
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/int_rect.h232
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/int_rect_outsets.h80
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/int_rect_test.cc20
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/int_size.cc24
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/int_size.h160
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/layout_point.cc29
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/layout_point.h218
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/layout_rect.cc177
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/layout_rect.h333
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/layout_rect_outsets.cc67
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/layout_rect_outsets.h160
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/layout_rect_outsets_test.cc40
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/layout_rect_test.cc185
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/layout_size.cc20
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/layout_size.h225
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/layout_size_test.cc28
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/region.cc626
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/region.h180
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/region_test.cc384
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/transform_state.cc236
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/transform_state.h157
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/DEPS32
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/OWNERS15
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.cc300
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h127
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image_test.cc157
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/bitmap_image.cc424
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/bitmap_image.h183
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.cc83
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h60
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/bitmap_image_test.cc694
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/box_reflection.cc43
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/box_reflection.h78
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc743
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h223
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc1407
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/canvas_color_params.cc221
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/canvas_color_params.h99
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/canvas_color_params_test.cc73
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/canvas_heuristic_parameters.h87
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/canvas_metrics.cc21
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/canvas_metrics.h34
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/canvas_resource.cc303
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/canvas_resource.h158
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/canvas_resource_host.h24
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc542
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h176
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/canvas_resource_test.cc188
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/color.cc468
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/color.h164
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/color_behavior.cc25
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/color_behavior.h49
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/color_blend.h62
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/color_correction_test_utils.cc194
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/color_correction_test_utils.h52
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/color_space_gamut.cc70
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/color_space_gamut.h41
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/color_space_profile_data.cc111
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/color_space_profile_data.h18
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositing/README.md57
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositing/chunk_to_layer_mapper.cc120
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositing/chunk_to_layer_mapper.h68
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositing/chunk_to_layer_mapper_test.cc234
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositing/composited_layer_raster_invalidator.cc337
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositing/composited_layer_raster_invalidator.h124
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositing/composited_layer_raster_invalidator_test.cc565
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc201
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h90
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc844
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h228
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc3361
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.cc731
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.h78
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer_test.cc890
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc610
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.h192
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositing_reasons.cc209
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositing_reasons.h174
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositor_animator.h24
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositor_animators_state.h18
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositor_element_id.cc60
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositor_element_id.h87
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositor_element_id_test.cc26
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositor_filter_operations.cc139
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositor_filter_operations.h71
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositor_mutator.h26
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositor_mutator_client.cc47
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositor_mutator_client.h35
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositor_mutator_impl.cc114
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositor_mutator_impl.h101
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/contiguous_container.cc180
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/contiguous_container.h254
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/contiguous_container_test.cc542
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/cpu/arm/webgl_image_conversion_neon.h297
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/cpu/mips/webgl_image_conversion_msa.h1154
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/cpu/x86/webgl_image_conversion_sse.h200
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/crossfade_generated_image.cc117
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/crossfade_generated_image.h86
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/dash_array.h35
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/decoding_image_generator.cc224
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/decoding_image_generator.h102
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/decoding_image_generator_test.cc68
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/deferred_image_decoder.cc339
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/deferred_image_decoder.h119
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/deferred_image_decoder_test.cc465
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/deferred_image_decoder_test_wo_platform.cc131
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/draw_looper_builder.cc104
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/draw_looper_builder.h81
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/distant_light_source.cc58
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/distant_light_source.h55
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/fe_blend.cc78
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/fe_blend.h50
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/fe_box_reflect.cc35
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/fe_box_reflect.h37
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/fe_color_matrix.cc225
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/fe_color_matrix.h72
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/fe_component_transfer.cc222
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/fe_component_transfer.h96
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/fe_composite.cc259
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/fe_composite.h99
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/fe_composite_test.cc133
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/fe_convolve_matrix.cc197
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/fe_convolve_matrix.h88
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/fe_diffuse_lighting.cc110
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/fe_diffuse_lighting.h65
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/fe_displacement_map.cc174
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/fe_displacement_map.h74
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/fe_drop_shadow.cc107
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/fe_drop_shadow.h62
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/fe_flood.cc84
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/fe_flood.h59
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/fe_gaussian_blur.cc105
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/fe_gaussian_blur.h53
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/fe_lighting.cc124
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/fe_lighting.h72
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/fe_merge.cc61
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/fe_merge.h44
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/fe_morphology.cc133
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/fe_morphology.h67
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/fe_offset.cc83
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/fe_offset.h55
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/fe_specular_lighting.cc128
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/fe_specular_lighting.h68
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/fe_tile.cc66
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/fe_tile.h50
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/fe_turbulence.cc180
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/fe_turbulence.h78
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/filter.cc101
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/filter.h88
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/filter_effect.cc179
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/filter_effect.h173
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/image_filter_builder_test.cc141
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/light_source.cc36
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/light_source.h63
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/paint_filter_builder.cc188
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/paint_filter_builder.h67
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/paint_filter_effect.cc36
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/paint_filter_effect.h33
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/point_light_source.cc50
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/point_light_source.h50
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/source_alpha.cc60
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/source_alpha.h46
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/source_graphic.cc53
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/source_graphic.h53
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/spot_light_source.cc77
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/spot_light_source.h74
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/first_paint_invalidation_tracking.cc11
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/first_paint_invalidation_tracking.h35
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/generated_image.cc76
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/generated_image.h70
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/DEPS13
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/OWNERS4
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc1603
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h585
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_software_rendering_test.cc117
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test.cc744
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test_helpers.h452
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.cc93
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.h55
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/graphics_context_3d_utils.cc83
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/graphics_context_3d_utils.h46
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.cc217
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.h87
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/shared_context_rate_limiter.cc84
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/shared_context_rate_limiter.h59
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.cc178
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h66
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context_test.cc272
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion.cc3260
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion.h293
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion_test.cc173
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.cc266
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.h100
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.cc626
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.h164
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu_memory_buffer_image_copy.cc104
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu_memory_buffer_image_copy.h38
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gradient.cc340
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gradient.h135
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gradient_generated_image.cc74
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gradient_generated_image.h67
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/graphics_context.cc1452
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/graphics_context.h506
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/graphics_context_state.cc126
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/graphics_context_state.h140
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/graphics_context_state_saver.h84
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/graphics_context_test.cc280
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/graphics_layer.cc1482
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/graphics_layer.h467
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/graphics_layer_client.h111
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/graphics_layer_debug_info.cc110
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/graphics_layer_debug_info.h110
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/graphics_layer_test.cc278
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/graphics_types.cc200
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/graphics_types.h213
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/graphics_types_3d.h59
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/high_contrast_image_classifier.cc366
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/high_contrast_image_classifier.h96
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/high_contrast_image_classifier_test.cc120
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/high_contrast_settings.h37
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/highcontrast/README.md5
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/highcontrast/highcontrast_classifier.cc1157
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/highcontrast/highcontrast_classifier.h34
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/image.cc470
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/image.h318
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/image_animation_policy.h24
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/image_data_buffer.cc168
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/image_data_buffer.h76
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/image_decoding_store.cc307
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/image_decoding_store.h339
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/image_decoding_store_test.cc182
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/image_frame_generator.cc442
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/image_frame_generator.h171
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/image_frame_generator_test.cc342
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/image_layer_chromium_test.cc139
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/image_observer.cc36
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/image_observer.h59
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/image_orientation.cc63
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/image_orientation.h102
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/image_pattern.cc82
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/image_pattern.h34
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/intercepting_canvas.cc31
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/intercepting_canvas.h368
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/interpolation_space.cc81
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/interpolation_space.h64
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/link_highlight.h26
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/logging_canvas.cc948
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/logging_canvas.h131
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/mailbox_texture_holder.cc163
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/mailbox_texture_holder.h63
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/offscreen_canvas_frame_dispatcher.h53
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/offscreen_canvas_frame_dispatcher_impl.cc449
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/offscreen_canvas_frame_dispatcher_impl.h111
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/offscreen_canvas_frame_dispatcher_impl_test.cc160
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/offscreen_canvas_placeholder.cc155
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/offscreen_canvas_placeholder.h69
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/offscreen_canvas_resource_provider.cc151
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/offscreen_canvas_resource_provider.h66
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/README.md285
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/clip_display_item.cc55
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/clip_display_item.h76
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.cc40
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.h154
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/clip_path_display_item.cc48
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/clip_path_display_item.h60
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/clip_path_recorder.cc29
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/clip_path_recorder.h32
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/clip_recorder.cc31
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/clip_recorder.h35
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/compositing_display_item.cc48
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/compositing_display_item.h82
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/compositing_recorder.cc36
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/compositing_recorder.h41
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/cull_rect.cc63
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/cull_rect.h75
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/cull_rect_test.cc82
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/display_item.cc252
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/display_item.h436
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/display_item_cache_skipper.h33
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/display_item_client.cc54
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/display_item_client.h192
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/display_item_client_test.cc26
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/display_item_list.cc72
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/display_item_list.h111
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/display_item_test.cc24
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/drawing_display_item.cc119
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h75
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/drawing_display_item_test.cc129
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/drawing_recorder.cc80
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h89
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/drawing_recorder_test.cc85
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.cc55
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h201
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/filter_display_item.cc53
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/filter_display_item.h77
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/float_clip_display_item.cc41
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/float_clip_display_item.h64
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/float_clip_rect.h112
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/float_clip_rect_test.cc106
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/foreign_layer_display_item.cc73
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/foreign_layer_display_item.h67
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper.cc372
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h152
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_clip_cache.cc54
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_clip_cache.h76
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_test.cc846
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache.cc74
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache.h123
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_artifact.cc113
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_artifact.h96
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_canvas.h17
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunk.cc24
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunk.h158
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunk_properties.cc44
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunk_properties.h60
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunk_subset.h66
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunk_test.cc89
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunker.cc97
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunker.h80
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunker_test.cc409
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc1186
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller.h472
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller_debug_data.cc186
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.cc2443
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.h135
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_filter.h36
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_flags.h14
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_font.h14
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_image.h19
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_property_node.cc93
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_property_node.h210
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_property_node_test.cc134
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_record.h15
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_record_builder.cc69
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_record_builder.h75
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_record_builder_test.cc105
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_recorder.h14
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_shader.h14
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_text_blob.h16
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_typeface.h14
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/property_tree_state.cc57
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/property_tree_state.h78
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/property_tree_state_test.cc95
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/raster_invalidation_tracking.cc208
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/raster_invalidation_tracking.h95
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/ref_counted_property_tree_state.cc43
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/ref_counted_property_tree_state.h88
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/scoped_display_item_fragment.h36
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/scoped_paint_chunk_properties.h133
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/scroll_display_item.cc42
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/scroll_display_item.h65
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/scroll_hit_test_display_item.cc70
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/scroll_hit_test_display_item.h65
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.cc45
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h181
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/subsequence_recorder.h60
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/transform_3d_display_item.cc47
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/transform_3d_display_item.h75
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/transform_display_item.cc41
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/transform_display_item.h60
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.cc59
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h205
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint_generated_image.cc37
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint_generated_image.h41
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint_invalidation_reason.cc72
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint_invalidation_reason.h66
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint_invalidation_reason_test.cc27
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint_record_pattern.cc43
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint_record_pattern.h36
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/path.cc544
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/path.h216
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/path_traversal_state.cc220
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/path_traversal_state.h75
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/pattern.cc69
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/pattern.h85
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/picture_snapshot.cc177
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/picture_snapshot.h81
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/placeholder_image.cc332
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/placeholder_image.h94
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/placeholder_image_test.cc89
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/profiling_canvas.cc59
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/profiling_canvas.h65
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/replaying_canvas.cc78
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/replaying_canvas.h74
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/scoped_interpolation_quality.h40
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/skia/image_pixel_locker.cc63
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/skia/image_pixel_locker.h36
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/skia/sk_size_hash.h90
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/skia/skia_utils.cc383
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/skia/skia_utils.h179
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/skia_texture_holder.cc79
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/skia_texture_holder.h49
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/squashing_disallowed_reasons.cc120
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/squashing_disallowed_reasons.h63
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/static_bitmap_image.cc166
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/static_bitmap_image.h145
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/static_bitmap_image_test.cc35
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/stroke_data.cc144
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/stroke_data.h128
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/surface_layer_bridge.cc103
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/surface_layer_bridge.h65
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/texture_holder.h83
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/touch_action.h17
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.cc93
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h46
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.cc128
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.h67
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc210
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/video_frame_submitter.h99
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/video_frame_submitter_test.cc412
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/web_graphics_context_3d_provider_wrapper.cc24
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/web_graphics_context_3d_provider_wrapper.h57
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/BUILD.gn137
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/BlinkGCAPIReference.md577
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/BlinkGCDesign.md200
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/DEPS15
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/OWNERS7
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/README.md8
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/asm/BUILD.gn55
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/asm/SaveRegisters_arm.S77
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/asm/SaveRegisters_arm64.S62
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/asm/SaveRegisters_mips.S64
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/asm/SaveRegisters_mips64.S41
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/asm/SaveRegisters_x86.asm159
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/blink_gc.h138
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/blink_gc_memory_dump_provider.cc137
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/blink_gc_memory_dump_provider.h66
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/blink_gc_memory_dump_provider_test.cc24
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/garbage_collected.h342
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/gc_info.cc79
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/gc_info.h281
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/gc_task_runner.h90
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/handle.h82
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/heap.cc765
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/heap.h750
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/heap_allocator.cc136
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/heap_allocator.h947
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/heap_compact.cc478
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/heap_compact.h199
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/heap_compact_test.cc496
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/heap_linked_stack.h122
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/heap_page.cc1818
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/heap_page.h1195
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/heap_terminated_array.h71
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/heap_terminated_array_builder.h26
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/heap_test.cc6851
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/heap_test_utilities.cc37
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/heap_test_utilities.h18
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/heap_traits.h40
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/heap_traits_test.cc126
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/incremental_marking_test.cc1663
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/marking_verifier.h74
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/marking_visitor.cc134
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/marking_visitor.h191
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/member.h621
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/object_start_bitmap_test.cc169
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/page_memory.cc179
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/page_memory.h218
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/page_pool.cc55
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/page_pool.h48
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/persistent.h852
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/persistent_node.cc199
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/persistent_node.h235
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/persistent_test.cc54
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/process_heap.cc66
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/process_heap.h71
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/run_all_tests.cc58
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/safe_point.h39
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/self_keep_alive.h74
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/sparse_heap_bitmap.cc111
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/sparse_heap_bitmap.h137
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/stack_frame_depth.cc65
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/stack_frame_depth.h107
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/thread_state.cc1532
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/thread_state.h770
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/threading_traits.h207
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/trace_traits.h817
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/visitor.h259
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/worklist.h420
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/worklist_test.cc337
-rw-r--r--chromium/third_party/blink/renderer/platform/histogram.cc63
-rw-r--r--chromium/third_party/blink/renderer/platform/histogram.h104
-rw-r--r--chromium/third_party/blink/renderer/platform/histogram_test.cc36
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/DEPS3
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/OWNERS6
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_decoder.cc127
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_decoder.h86
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_decoder_test.cc98
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_reader.cc857
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_reader.h367
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/fast_shared_buffer_reader.cc96
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/fast_shared_buffer_reader.h97
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/fast_shared_buffer_reader_test.cc238
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/gif/gif_image_decoder.cc283
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/gif/gif_image_decoder.h102
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/gif/gif_image_decoder_test.cc507
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/gif/gif_image_reader.cc902
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/gif/gif_image_reader.h371
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/ico/ico_image_decoder.cc344
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/ico/ico_image_decoder.h186
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/ico/ico_image_decoder_test.cc110
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/image_animation.h37
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/image_decoder.cc564
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/image_decoder.h437
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/image_decoder_test.cc266
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/image_decoder_test_helpers.cc598
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/image_decoder_test_helpers.h122
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/image_frame.cc209
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/image_frame.h304
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.cc1074
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.h86
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder_test.cc387
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder.cc706
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder.h84
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder_test.cc1084
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/png/png_image_reader.cc701
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/png/png_image_reader.h174
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/segment_reader.cc199
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/segment_reader.h52
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/testing/bad-code.gifbin0 -> 1034 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/testing/bad-initial-code.gifbin0 -> 362 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/testing/broken.gifbin0 -> 906 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/testing/first-frame-has-greater-size-than-screen-size.gifbin0 -> 2113 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/testing/invalid-disposal-method.gifbin0 -> 901 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/testing/many-progressive-scans.jpgbin0 -> 4477 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/testing/radient-bad-terminator.gifbin0 -> 1034 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/testing/radient.gifbin0 -> 1034 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/webp/webp_image_decoder.cc557
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/webp/webp_image_decoder.h121
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/webp/webp_image_decoder_test.cc539
-rw-r--r--chromium/third_party/blink/renderer/platform/image-encoders/image_encoder.cc84
-rw-r--r--chromium/third_party/blink/renderer/platform/image-encoders/image_encoder.h91
-rw-r--r--chromium/third_party/blink/renderer/platform/image-encoders/image_encoder_utils.cc84
-rw-r--r--chromium/third_party/blink/renderer/platform/image-encoders/image_encoder_utils.h29
-rw-r--r--chromium/third_party/blink/renderer/platform/instance_counters.cc41
-rw-r--r--chromium/third_party/blink/renderer/platform/instance_counters.h97
-rw-r--r--chromium/third_party/blink/renderer/platform/instance_counters_memory_dump_provider.h35
-rw-r--r--chromium/third_party/blink/renderer/platform/instrumentation/BUILD.gn50
-rw-r--r--chromium/third_party/blink/renderer/platform/instrumentation/DEPS11
-rw-r--r--chromium/third_party/blink/renderer/platform/instrumentation/OWNERS4
-rw-r--r--chromium/third_party/blink/renderer/platform/instrumentation/platform_instrumentation.cc50
-rw-r--r--chromium/third_party/blink/renderer/platform/instrumentation/platform_instrumentation.h72
-rw-r--r--chromium/third_party/blink/renderer/platform/instrumentation/resource_coordinator/DEPS3
-rw-r--r--chromium/third_party/blink/renderer/platform/instrumentation/resource_coordinator/OWNERS1
-rw-r--r--chromium/third_party/blink/renderer/platform/instrumentation/resource_coordinator/blink_resource_coordinator_base.h28
-rw-r--r--chromium/third_party/blink/renderer/platform/instrumentation/resource_coordinator/frame_resource_coordinator.cc36
-rw-r--r--chromium/third_party/blink/renderer/platform/instrumentation/resource_coordinator/frame_resource_coordinator.h36
-rw-r--r--chromium/third_party/blink/renderer/platform/instrumentation/resource_coordinator/renderer_resource_coordinator.cc65
-rw-r--r--chromium/third_party/blink/renderer/platform/instrumentation/resource_coordinator/renderer_resource_coordinator.h45
-rw-r--r--chromium/third_party/blink/renderer/platform/instrumentation/tracing/memory_cache_dump_provider.cc47
-rw-r--r--chromium/third_party/blink/renderer/platform/instrumentation/tracing/memory_cache_dump_provider.h57
-rw-r--r--chromium/third_party/blink/renderer/platform/instrumentation/tracing/trace_event.cc23
-rw-r--r--chromium/third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h46
-rw-r--r--chromium/third_party/blink/renderer/platform/instrumentation/tracing/traced_value.cc120
-rw-r--r--chromium/third_party/blink/renderer/platform/instrumentation/tracing/traced_value.h65
-rw-r--r--chromium/third_party/blink/renderer/platform/instrumentation/tracing/traced_value_test.cc170
-rw-r--r--chromium/third_party/blink/renderer/platform/instrumentation/tracing/web_memory_allocator_dump.cc37
-rw-r--r--chromium/third_party/blink/renderer/platform/instrumentation/tracing/web_memory_allocator_dump.h60
-rw-r--r--chromium/third_party/blink/renderer/platform/instrumentation/tracing/web_process_memory_dump.cc187
-rw-r--r--chromium/third_party/blink/renderer/platform/instrumentation/tracing/web_process_memory_dump.h171
-rw-r--r--chromium/third_party/blink/renderer/platform/instrumentation/tracing/web_process_memory_dump_test.cc136
-rw-r--r--chromium/third_party/blink/renderer/platform/json/OWNERS4
-rw-r--r--chromium/third_party/blink/renderer/platform/json/json_parser.cc544
-rw-r--r--chromium/third_party/blink/renderer/platform/json/json_parser.h24
-rw-r--r--chromium/third_party/blink/renderer/platform/json/json_parser_fuzzer.cc18
-rw-r--r--chromium/third_party/blink/renderer/platform/json/json_parser_test.cc550
-rw-r--r--chromium/third_party/blink/renderer/platform/json/json_values.cc491
-rw-r--r--chromium/third_party/blink/renderer/platform/json/json_values.h302
-rw-r--r--chromium/third_party/blink/renderer/platform/json/json_values_test.cc42
-rw-r--r--chromium/third_party/blink/renderer/platform/keyboard_codes.h577
-rw-r--r--chromium/third_party/blink/renderer/platform/kill_ring.h51
-rw-r--r--chromium/third_party/blink/renderer/platform/kill_ring_none.cc42
-rw-r--r--chromium/third_party/blink/renderer/platform/language.cc163
-rw-r--r--chromium/third_party/blink/renderer/platform/language.h51
-rw-r--r--chromium/third_party/blink/renderer/platform/layout_locale.cc226
-rw-r--r--chromium/third_party/blink/renderer/platform/layout_locale.h88
-rw-r--r--chromium/third_party/blink/renderer/platform/layout_locale_test.cc155
-rw-r--r--chromium/third_party/blink/renderer/platform/layout_test_support.cc93
-rw-r--r--chromium/third_party/blink/renderer/platform/layout_test_support.h55
-rw-r--r--chromium/third_party/blink/renderer/platform/layout_unit.cc28
-rw-r--r--chromium/third_party/blink/renderer/platform/layout_unit.h717
-rw-r--r--chromium/third_party/blink/renderer/platform/layout_unit_test.cc298
-rw-r--r--chromium/third_party/blink/renderer/platform/length.cc198
-rw-r--r--chromium/third_party/blink/renderer/platform/length.h308
-rw-r--r--chromium/third_party/blink/renderer/platform/length_box.h82
-rw-r--r--chromium/third_party/blink/renderer/platform/length_functions.cc136
-rw-r--r--chromium/third_party/blink/renderer/platform/length_functions.h54
-rw-r--r--chromium/third_party/blink/renderer/platform/length_point.h66
-rw-r--r--chromium/third_party/blink/renderer/platform/length_size.h55
-rw-r--r--chromium/third_party/blink/renderer/platform/lifecycle_context_test.cc138
-rw-r--r--chromium/third_party/blink/renderer/platform/lifecycle_notifier.h165
-rw-r--r--chromium/third_party/blink/renderer/platform/lifecycle_observer.h81
-rw-r--r--chromium/third_party/blink/renderer/platform/link_hash.cc67
-rw-r--r--chromium/third_party/blink/renderer/platform/link_hash.h67
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/BUILD.gn164
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/DEPS13
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/OWNERS7
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/README.md22
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/cors/cors.cc215
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/cors/cors.h71
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/cors/cors_error_string.cc344
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/cors/cors_error_string.h112
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/cors/cors_status.h27
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/README.md6
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/access_control_status.h18
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/buffering_data_pipe_writer.cc109
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/buffering_data_pipe_writer.h46
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/buffering_data_pipe_writer_test.cc78
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/cached_metadata.cc54
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/cached_metadata.h95
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h70
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.cc150
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.h77
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences_test.cc158
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/fetch_context.cc126
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/fetch_context.h292
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/fetch_initiator_info.h82
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/fetch_initiator_type_names.json522
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.cc176
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h251
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/fetch_utils.cc97
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/fetch_utils.h38
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/fetch_utils_test.cc40
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/integrity_metadata.cc33
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/integrity_metadata.h69
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache.cc476
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache.h220
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache_correctness_test.cc564
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache_test.cc419
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/preload_key.h75
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc470
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource.h247
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource_test.cc230
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource.cc1207
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource.h596
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_client.h113
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_client_walker.h69
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_error.cc190
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_error.h120
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc1700
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h345
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc852
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_finish_observer.h38
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_info.h48
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_priority.h37
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.cc722
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h318
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler_test.cc520
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_timing.cc126
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_timing.h115
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc873
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader.h205
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h209
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_options_test.cc108
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_test.cc155
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_loading_log.h21
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_priority.h52
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_request.cc469
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_request.h498
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_request_test.cc161
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_response.cc625
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_response.h625
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_response_test.cc87
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_status.h20
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_test.cc424
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_timing_info.cc61
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h165
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.cc69
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.h82
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.cc218
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.h81
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler_test.cc459
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/substitute_data.h70
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/text_resource_decoder_options.cc65
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/text_resource_decoder_options.h99
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/unique_identifier.cc43
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/unique_identifier.h41
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/link_header.cc102
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/link_header.h87
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/link_header_test.cc287
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/subresource_integrity.cc503
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/subresource_integrity.h152
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/subresource_integrity_test.cc713
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/testing/crypto_testing_platform_support.h51
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/testing/fetch_testing_platform_support.cc48
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/testing/fetch_testing_platform_support.h39
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h166
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/testing/mock_resource.cc103
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/testing/mock_resource.h60
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/testing/mock_resource_client.h61
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/testing/web_url_loader_factory_with_mock.cc24
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/testing/web_url_loader_factory_with_mock.h32
-rw-r--r--chromium/third_party/blink/renderer/platform/long_task_detector.cc54
-rw-r--r--chromium/third_party/blink/renderer/platform/long_task_detector.h56
-rw-r--r--chromium/third_party/blink/renderer/platform/long_task_detector_test.cc100
-rw-r--r--chromium/third_party/blink/renderer/platform/mac/block_exceptions.h42
-rw-r--r--chromium/third_party/blink/renderer/platform/mac/block_exceptions.mm41
-rw-r--r--chromium/third_party/blink/renderer/platform/mac/color_mac.h42
-rw-r--r--chromium/third_party/blink/renderer/platform/mac/color_mac.mm88
-rw-r--r--chromium/third_party/blink/renderer/platform/mac/graphics_context_canvas.h50
-rw-r--r--chromium/third_party/blink/renderer/platform/mac/graphics_context_canvas.mm80
-rw-r--r--chromium/third_party/blink/renderer/platform/mac/graphics_context_canvas_test.mm72
-rw-r--r--chromium/third_party/blink/renderer/platform/mac/kill_ring_mac.mm76
-rw-r--r--chromium/third_party/blink/renderer/platform/mac/local_current_graphics_context.h57
-rw-r--r--chromium/third_party/blink/renderer/platform/mac/local_current_graphics_context.mm100
-rw-r--r--chromium/third_party/blink/renderer/platform/mac/ns_scroller_imp_details.h94
-rw-r--r--chromium/third_party/blink/renderer/platform/mac/scroll_animator_mac.h178
-rw-r--r--chromium/third_party/blink/renderer/platform/mac/scroll_animator_mac.mm1100
-rw-r--r--chromium/third_party/blink/renderer/platform/mac/theme_mac.h126
-rw-r--r--chromium/third_party/blink/renderer/platform/mac/theme_mac.mm558
-rw-r--r--chromium/third_party/blink/renderer/platform/mac/version_util_mac.h32
-rw-r--r--chromium/third_party/blink/renderer/platform/mac/version_util_mac.mm56
-rw-r--r--chromium/third_party/blink/renderer/platform/mac/version_util_mac_test.mm51
-rw-r--r--chromium/third_party/blink/renderer/platform/mac/web_core_ns_cell_extras.h35
-rw-r--r--chromium/third_party/blink/renderer/platform/mac/web_core_ns_cell_extras.mm48
-rw-r--r--chromium/third_party/blink/renderer/platform/media/BUILD.gn17
-rw-r--r--chromium/third_party/blink/renderer/platform/media/DEPS4
-rw-r--r--chromium/third_party/blink/renderer/platform/media/resource_bundle_helper.cc34
-rw-r--r--chromium/third_party/blink/renderer/platform/media/resource_bundle_helper.h30
-rw-r--r--chromium/third_party/blink/renderer/platform/mediastream/media_stream_center.cc123
-rw-r--r--chromium/third_party/blink/renderer/platform/mediastream/media_stream_center.h87
-rw-r--r--chromium/third_party/blink/renderer/platform/mediastream/media_stream_component.cc143
-rw-r--r--chromium/third_party/blink/renderer/platform/mediastream/media_stream_component.h153
-rw-r--r--chromium/third_party/blink/renderer/platform/mediastream/media_stream_descriptor.cc168
-rw-r--r--chromium/third_party/blink/renderer/platform/mediastream/media_stream_descriptor.h137
-rw-r--r--chromium/third_party/blink/renderer/platform/mediastream/media_stream_source.cc159
-rw-r--r--chromium/third_party/blink/renderer/platform/mediastream/media_stream_source.h159
-rw-r--r--chromium/third_party/blink/renderer/platform/mediastream/media_stream_web_audio_source.cc64
-rw-r--r--chromium/third_party/blink/renderer/platform/mediastream/media_stream_web_audio_source.h68
-rw-r--r--chromium/third_party/blink/renderer/platform/memory_coordinator.cc139
-rw-r--r--chromium/third_party/blink/renderer/platform/memory_coordinator.h88
-rw-r--r--chromium/third_party/blink/renderer/platform/mhtml/archive_resource.cc57
-rw-r--r--chromium/third_party/blink/renderer/platform/mhtml/archive_resource.h75
-rw-r--r--chromium/third_party/blink/renderer/platform/mhtml/mhtml_archive.cc372
-rw-r--r--chromium/third_party/blink/renderer/platform/mhtml/mhtml_archive.h122
-rw-r--r--chromium/third_party/blink/renderer/platform/mhtml/mhtml_fuzzer.cc31
-rw-r--r--chromium/third_party/blink/renderer/platform/mhtml/mhtml_parser.cc439
-rw-r--r--chromium/third_party/blink/renderer/platform/mhtml/mhtml_parser.h83
-rw-r--r--chromium/third_party/blink/renderer/platform/mhtml/mhtml_parser_test.cc472
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/BUILD.gn15
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/DEPS16
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/OWNERS6
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/big_buffer.typemap12
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/big_string.typemap11
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/big_string_mojom_traits.cc45
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/big_string_mojom_traits.h30
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/big_string_mojom_traits_test.cc70
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/blink_typemaps.gni23
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/bluetooth_struct_traits.cc34
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/bluetooth_struct_traits.h37
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/fetch_api_request_struct_traits.cc339
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/fetch_api_request_struct_traits.h99
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/file.typemap12
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/geometry.typemap32
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/geometry_struct_traits.cc64
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/geometry_struct_traits.h58
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/geometry_struct_traits_test.cc144
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/interface_invalidator.cc35
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/interface_invalidator.h43
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/interface_invalidator_test.cc614
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/kurl.typemap15
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/kurl_security_origin_test.cc89
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/kurl_struct_traits.h42
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/mojo_helper.h22
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/notification_struct_traits.cc287
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/notification_struct_traits.h140
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/notification_struct_traits_test.cc175
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/referrer.typemap14
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/referrer_struct_traits.h47
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/revocable_binding.h181
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/revocable_interface_ptr.h237
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/revocable_strong_binding.h135
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/security_origin.typemap18
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/security_origin_struct_traits.h56
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/string.typemap13
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/string16_mojom_traits.cc102
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/string16_mojom_traits.h46
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/string16_mojom_traits_test.cc95
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/time.typemap19
-rw-r--r--chromium/third_party/blink/renderer/platform/network/BUILD.gn118
-rw-r--r--chromium/third_party/blink/renderer/platform/network/DEPS11
-rw-r--r--chromium/third_party/blink/renderer/platform/network/HTTPParsersFuzzer.dict24
-rw-r--r--chromium/third_party/blink/renderer/platform/network/content_security_policy_parsers.cc69
-rw-r--r--chromium/third_party/blink/renderer/platform/network/content_security_policy_parsers.h51
-rw-r--r--chromium/third_party/blink/renderer/platform/network/content_security_policy_response_headers.cc58
-rw-r--r--chromium/third_party/blink/renderer/platform/network/content_security_policy_response_headers.h77
-rw-r--r--chromium/third_party/blink/renderer/platform/network/encoded_form_data.cc204
-rw-r--r--chromium/third_party/blink/renderer/platform/network/encoded_form_data.h199
-rw-r--r--chromium/third_party/blink/renderer/platform/network/encoded_form_data_test.cc99
-rw-r--r--chromium/third_party/blink/renderer/platform/network/form_data_encoder.cc262
-rw-r--r--chromium/third_party/blink/renderer/platform/network/form_data_encoder.h77
-rw-r--r--chromium/third_party/blink/renderer/platform/network/header_field_tokenizer.cc154
-rw-r--r--chromium/third_party/blink/renderer/platform/network/header_field_tokenizer.h52
-rw-r--r--chromium/third_party/blink/renderer/platform/network/http_header_map.cc63
-rw-r--r--chromium/third_party/blink/renderer/platform/network/http_header_map.h91
-rw-r--r--chromium/third_party/blink/renderer/platform/network/http_names.json573
-rw-r--r--chromium/third_party/blink/renderer/platform/network/http_parsers.cc699
-rw-r--r--chromium/third_party/blink/renderer/platform/network/http_parsers.h170
-rw-r--r--chromium/third_party/blink/renderer/platform/network/http_parsers_fuzzer.cc38
-rw-r--r--chromium/third_party/blink/renderer/platform/network/http_parsers_test.cc735
-rw-r--r--chromium/third_party/blink/renderer/platform/network/mime/content_type.cc77
-rw-r--r--chromium/third_party/blink/renderer/platform/network/mime/content_type.h52
-rw-r--r--chromium/third_party/blink/renderer/platform/network/mime/mime_type_from_url.cc49
-rw-r--r--chromium/third_party/blink/renderer/platform/network/mime/mime_type_from_url.h39
-rw-r--r--chromium/third_party/blink/renderer/platform/network/mime/mime_type_registry.cc203
-rw-r--r--chromium/third_party/blink/renderer/platform/network/mime/mime_type_registry.h106
-rw-r--r--chromium/third_party/blink/renderer/platform/network/mime/mime_type_registry_test.cc37
-rw-r--r--chromium/third_party/blink/renderer/platform/network/mime/mock_mime_registry.h32
-rw-r--r--chromium/third_party/blink/renderer/platform/network/network_hints.cc48
-rw-r--r--chromium/third_party/blink/renderer/platform/network/network_hints.h42
-rw-r--r--chromium/third_party/blink/renderer/platform/network/network_instrumentation.cc99
-rw-r--r--chromium/third_party/blink/renderer/platform/network/network_instrumentation.h43
-rw-r--r--chromium/third_party/blink/renderer/platform/network/network_log.h20
-rw-r--r--chromium/third_party/blink/renderer/platform/network/network_state_notifier.cc450
-rw-r--r--chromium/third_party/blink/renderer/platform/network/network_state_notifier.h339
-rw-r--r--chromium/third_party/blink/renderer/platform/network/network_state_notifier_test.cc965
-rw-r--r--chromium/third_party/blink/renderer/platform/network/network_utils.cc132
-rw-r--r--chromium/third_party/blink/renderer/platform/network/network_utils.h51
-rw-r--r--chromium/third_party/blink/renderer/platform/network/network_utils_test.cc112
-rw-r--r--chromium/third_party/blink/renderer/platform/network/parsed_content_disposition.cc31
-rw-r--r--chromium/third_party/blink/renderer/platform/network/parsed_content_disposition.h44
-rw-r--r--chromium/third_party/blink/renderer/platform/network/parsed_content_disposition_test.cc93
-rw-r--r--chromium/third_party/blink/renderer/platform/network/parsed_content_header_field_parameters.cc96
-rw-r--r--chromium/third_party/blink/renderer/platform/network/parsed_content_header_field_parameters.h72
-rw-r--r--chromium/third_party/blink/renderer/platform/network/parsed_content_header_field_parameters_test.cc166
-rw-r--r--chromium/third_party/blink/renderer/platform/network/parsed_content_type.cc116
-rw-r--r--chromium/third_party/blink/renderer/platform/network/parsed_content_type.h76
-rw-r--r--chromium/third_party/blink/renderer/platform/network/parsed_content_type_test.cc94
-rw-r--r--chromium/third_party/blink/renderer/platform/network/server_timing_header.cc23
-rw-r--r--chromium/third_party/blink/renderer/platform/network/server_timing_header.h35
-rw-r--r--chromium/third_party/blink/renderer/platform/network/web_socket_handshake_request.cc59
-rw-r--r--chromium/third_party/blink/renderer/platform/network/web_socket_handshake_request.h93
-rw-r--r--chromium/third_party/blink/renderer/platform/network/web_socket_handshake_response.cc70
-rw-r--r--chromium/third_party/blink/renderer/platform/network/web_socket_handshake_response.h63
-rw-r--r--chromium/third_party/blink/renderer/platform/partition_alloc_memory_dump_provider.cc211
-rw-r--r--chromium/third_party/blink/renderer/platform/partition_alloc_memory_dump_provider.h42
-rw-r--r--chromium/third_party/blink/renderer/platform/paste_mode.h43
-rw-r--r--chromium/third_party/blink/renderer/platform/peerconnection/rtc_answer_options_platform.h32
-rw-r--r--chromium/third_party/blink/renderer/platform/peerconnection/rtc_offer_options_platform.h49
-rw-r--r--chromium/third_party/blink/renderer/platform/peerconnection/rtc_session_description_request.h56
-rw-r--r--chromium/third_party/blink/renderer/platform/peerconnection/rtc_stats_request.h60
-rw-r--r--chromium/third_party/blink/renderer/platform/peerconnection/rtc_stats_response_base.h43
-rw-r--r--chromium/third_party/blink/renderer/platform/peerconnection/rtc_void_request.h55
-rw-r--r--chromium/third_party/blink/renderer/platform/platform.gni58
-rw-r--r--chromium/third_party/blink/renderer/platform/platform_chrome_client.h66
-rw-r--r--chromium/third_party/blink/renderer/platform/platform_export.h127
-rw-r--r--chromium/third_party/blink/renderer/platform/platform_frame_view.h25
-rw-r--r--chromium/third_party/blink/renderer/platform/platform_generated.gni5
-rw-r--r--chromium/third_party/blink/renderer/platform/plugins/plugin_data.cc139
-rw-r--r--chromium/third_party/blink/renderer/platform/plugins/plugin_data.h124
-rw-r--r--chromium/third_party/blink/renderer/platform/plugins/plugin_list_builder.cc64
-rw-r--r--chromium/third_party/blink/renderer/platform/plugins/plugin_list_builder.h63
-rw-r--r--chromium/third_party/blink/renderer/platform/plugins/plugin_script_forbidden_scope.cc29
-rw-r--r--chromium/third_party/blink/renderer/platform/plugins/plugin_script_forbidden_scope.h54
-rw-r--r--chromium/third_party/blink/renderer/platform/png_fuzzer.cc63
-rw-r--r--chromium/third_party/blink/renderer/platform/pod_arena.h196
-rw-r--r--chromium/third_party/blink/renderer/platform/pod_arena_test.cc96
-rw-r--r--chromium/third_party/blink/renderer/platform/pod_free_list_arena.h128
-rw-r--r--chromium/third_party/blink/renderer/platform/pod_free_list_arena_test.cc166
-rw-r--r--chromium/third_party/blink/renderer/platform/pod_interval.h153
-rw-r--r--chromium/third_party/blink/renderer/platform/pod_interval_tree.h258
-rw-r--r--chromium/third_party/blink/renderer/platform/pod_interval_tree_test.cc345
-rw-r--r--chromium/third_party/blink/renderer/platform/pod_red_black_tree.h799
-rw-r--r--chromium/third_party/blink/renderer/platform/pod_red_black_tree_test.cc196
-rw-r--r--chromium/third_party/blink/renderer/platform/precompile_platform.h21
-rw-r--r--chromium/third_party/blink/renderer/platform/prerender.cc92
-rw-r--r--chromium/third_party/blink/renderer/platform/prerender.h107
-rw-r--r--chromium/third_party/blink/renderer/platform/prerender_client.h54
-rw-r--r--chromium/third_party/blink/renderer/platform/probe/OWNERS2
-rw-r--r--chromium/third_party/blink/renderer/platform/probe/PlatformProbes.json519
-rw-r--r--chromium/third_party/blink/renderer/platform/probe/PlatformProbes.pidl12
-rw-r--r--chromium/third_party/blink/renderer/platform/probe/PlatformTraceEventsAgent.cpp33
-rw-r--r--chromium/third_party/blink/renderer/platform/probe/PlatformTraceEventsAgent.h28
-rw-r--r--chromium/third_party/blink/renderer/platform/probe/platform_probes.cc28
-rw-r--r--chromium/third_party/blink/renderer/platform/probe/platform_probes.h41
-rw-r--r--chromium/third_party/blink/renderer/platform/resolution_units.h42
-rw-r--r--chromium/third_party/blink/renderer/platform/runtime_enabled_features.json51366
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/BUILD.gn258
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/DEPS41
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/OWNERS7
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/README.md96
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/DEPS5
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/enqueue_order.cc25
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/enqueue_order.h52
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/graceful_queue_shutdown_helper.cc40
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/graceful_queue_shutdown_helper.h47
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/intrusive_heap.h227
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/intrusive_heap_unittest.cc374
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/lazy_now.cc18
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/lazy_now.h40
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/moveable_auto_lock.h41
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/real_time_domain.cc70
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/real_time_domain.h45
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/sequenced_task_source.h40
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/task_queue.cc279
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/task_queue.h307
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_impl.cc1039
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_impl.h451
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_manager.h103
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_manager_delegate_for_test.cc65
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_manager_delegate_for_test.h59
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_manager_impl.cc669
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_manager_impl.h334
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_manager_impl_unittest.cc3312
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_manager_perftest.cc230
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_selector.cc388
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_selector.h225
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_selector_logic.h35
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_selector_unittest.cc714
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/task_time_observer.h36
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/test_count_uses_time_source.cc22
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/test_count_uses_time_source.h31
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/test_task_time_observer.h23
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/thread_controller.h94
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/thread_controller_impl.cc255
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/thread_controller_impl.h132
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/time_domain.cc119
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/time_domain.h142
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/time_domain_unittest.cc348
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/virtual_time_domain.cc69
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/virtual_time_domain.h55
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/work_queue.cc234
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/work_queue.h156
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/work_queue_sets.cc172
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/work_queue_sets.h104
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/work_queue_sets_unittest.cc329
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/base/work_queue_unittest.cc474
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/DEPS10
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/OWNERS7
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/cancelable_closure_holder.cc30
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/cancelable_closure_holder.h42
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/compositor_metrics_helper.cc29
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/compositor_metrics_helper.h32
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/default_params.h19
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/features.h19
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/idle_canceled_delayed_task_sweeper.cc40
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/idle_canceled_delayed_task_sweeper.h38
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/idle_canceled_delayed_task_sweeper_unittest.cc132
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/idle_helper.cc497
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/idle_helper.h249
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/idle_helper_unittest.cc1190
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/metrics_helper.cc84
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/metrics_helper.h68
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/metrics_helper_unittest.cc78
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/page_visibility_state.cc22
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/page_visibility_state.h19
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/pollable_thread_safe_flag.cc19
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/pollable_thread_safe_flag.h36
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/process_state.cc26
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/process_state.h27
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/single_thread_idle_task_runner.cc100
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/task_runner_impl.cc50
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/task_runner_impl.h54
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/web_scheduler.h105
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/web_scheduler_impl.cc87
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/web_scheduler_impl.h67
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/webthread_base.cc144
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/webthread_impl_for_worker_scheduler.cc128
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/webthread_impl_for_worker_scheduler.h88
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/webthread_impl_for_worker_scheduler_unittest.cc205
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/worker_global_scope_scheduler.cc88
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/worker_global_scope_scheduler.h55
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/worker_global_scope_scheduler_unittest.cc91
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/worker_metrics_helper.cc66
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/worker_metrics_helper.h48
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/worker_scheduler_proxy.cc51
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/worker_scheduler_proxy.h74
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/worker_scheduler_proxy_unittest.cc187
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/worker_task_queue.cc41
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/child/worker_task_queue.h35
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/common/DEPS6
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.cc133
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h108
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/common/scheduler_helper_unittest.cc180
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.cc95
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h119
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool_unittest.cc186
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/common/throttling/cpu_time_budget_pool.cc186
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/common/throttling/cpu_time_budget_pool.h133
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.cc578
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h222
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler_unittest.cc1406
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/common/throttling/throttled_time_domain.cc51
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/common/throttling/throttled_time_domain.h42
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/common/throttling/wake_up_budget_pool.cc125
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/common/throttling/wake_up_budget_pool.h67
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/main_thread/DEPS10
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_origin_type.cc42
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_origin_type.h30
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc647
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h192
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc480
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_helper.cc64
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_helper.h46
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc2792
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h832
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc4116
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc400
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h140
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc1174
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/main_thread/web_main_thread_scheduler.cc55
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/main_thread/web_render_widget_scheduling_state.cc68
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/public/frame_or_worker_global_scope_scheduler.h37
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h172
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/public/non_main_thread_scheduler.h60
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/public/page_scheduler.h152
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/renderer/DEPS15
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain.cc172
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain.h120
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain_unittest.cc248
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/renderer/deadline_task_runner.cc41
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/renderer/deadline_task_runner.h51
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/renderer/deadline_task_runner_unittest.cc104
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/renderer/frame_status.cc84
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/renderer/frame_status.h85
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/renderer/idle_time_estimator.cc76
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/renderer/idle_time_estimator.h60
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/renderer/idle_time_estimator_unittest.cc165
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/renderer/main_thread_task_queue.cc177
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/renderer/main_thread_task_queue.h212
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/renderer/queueing_time_estimator.cc411
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/renderer/queueing_time_estimator.h167
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/renderer/queueing_time_estimator_unittest.cc1230
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/renderer/render_widget_signals.cc64
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/renderer/render_widget_signals.h61
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/renderer/render_widget_signals_unittest.cc269
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/renderer/renderer_metrics_helper.cc501
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/renderer/renderer_metrics_helper.h144
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/renderer/renderer_metrics_helper_unittest.cc607
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/renderer/renderer_web_scheduler_impl.cc61
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/renderer/renderer_web_scheduler_impl.h41
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/renderer/task_cost_estimator.cc45
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/renderer/task_cost_estimator.h52
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/renderer/task_cost_estimator_unittest.cc74
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/renderer/use_case.h42
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/renderer/user_model.cc168
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/renderer/user_model.h86
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/renderer/user_model_unittest.cc266
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/renderer/web_scoped_virtual_time_pauser.cc87
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/renderer/webthread_impl_for_renderer_scheduler.cc67
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/renderer/webthread_impl_for_renderer_scheduler.h56
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/renderer/webthread_impl_for_renderer_scheduler_unittest.cc205
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/util/aggregated_metric_reporter.h89
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/util/task_duration_metric_reporter.cc19
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/util/task_duration_metric_reporter.h57
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/util/task_duration_metric_reporter_unittest.cc87
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/util/thread_cpu_throttler.cc221
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/util/thread_cpu_throttler.h49
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/util/thread_load_tracker.cc125
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/util/thread_load_tracker.h73
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/util/thread_load_tracker_unittest.cc148
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/util/tracing_helper.cc101
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/util/tracing_helper.h367
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/util/tracing_helper_unittest.cc98
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/utility/webthread_impl_for_utility_thread.cc41
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/utility/webthread_impl_for_utility_thread.h41
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/worker/DEPS5
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.cc102
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.h73
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/worker/non_main_thread_scheduler.cc37
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_helper.cc52
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_helper.h43
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.cc201
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h110
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler_unittest.cc407
-rw-r--r--chromium/third_party/blink/renderer/platform/scoped_orientation_change_indicator.cc33
-rw-r--r--chromium/third_party/blink/renderer/platform/scoped_orientation_change_indicator.h37
-rw-r--r--chromium/third_party/blink/renderer/platform/scoped_orientation_change_indicator_test.cc44
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/DEPS4
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/main_thread_scrolling_reason.h17
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/programmatic_scroll_animator.cc205
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/programmatic_scroll_animator.h76
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/scroll_alignment.cc193
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/scroll_alignment.h115
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/scroll_animator.cc449
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/scroll_animator.h164
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/scroll_animator_base.cc89
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/scroll_animator_base.h128
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/scroll_animator_compositor_coordinator.cc349
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/scroll_animator_compositor_coordinator.h199
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/scroll_animator_test.cc698
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/scroll_customization.cc35
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/scroll_customization.h33
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/scroll_snap_data.h25
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/scroll_state_data.h19
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/scroll_types.h299
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/scrollable_area.cc726
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/scrollable_area.h514
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/scrollable_area_test.cc308
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/scrollbar.cc671
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/scrollbar.h255
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/scrollbar_test_suite.h142
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme.cc435
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme.h233
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_android.cc36
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_aura.cc364
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_aura.h87
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_aura_test.cc107
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_client.h90
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_mac.h133
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_mac.mm425
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_mock.cc93
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_mock.h73
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_overlay.cc289
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_overlay.h102
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_overlay_mock.h74
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_overlay_test.cc144
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/smooth_scroll_sequencer.cc59
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/smooth_scroll_sequencer.h64
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/web_scroll_into_view_params.cc122
-rw-r--r--chromium/third_party/blink/renderer/platform/scroll/web_scrollbar_theme.mm63
-rw-r--r--chromium/third_party/blink/renderer/platform/serialized_resource.h55
-rw-r--r--chromium/third_party/blink/renderer/platform/shared_buffer.cc288
-rw-r--r--chromium/third_party/blink/renderer/platform/shared_buffer.h195
-rw-r--r--chromium/third_party/blink/renderer/platform/shared_buffer_chunk_reader.cc151
-rw-r--r--chromium/third_party/blink/renderer/platform/shared_buffer_chunk_reader.h83
-rw-r--r--chromium/third_party/blink/renderer/platform/shared_buffer_test.cc184
-rw-r--r--chromium/third_party/blink/renderer/platform/speech/platform_speech_synthesis_utterance.cc43
-rw-r--r--chromium/third_party/blink/renderer/platform/speech/platform_speech_synthesis_utterance.h94
-rw-r--r--chromium/third_party/blink/renderer/platform/speech/platform_speech_synthesis_voice.cc60
-rw-r--r--chromium/third_party/blink/renderer/platform/speech/platform_speech_synthesis_voice.h79
-rw-r--r--chromium/third_party/blink/renderer/platform/speech/platform_speech_synthesizer.cc116
-rw-r--r--chromium/third_party/blink/renderer/platform/speech/platform_speech_synthesizer.h116
-rw-r--r--chromium/third_party/blink/renderer/platform/supplementable.cc13
-rw-r--r--chromium/third_party/blink/renderer/platform/supplementable.h246
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/DEPS23
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/arena_test_helpers.h71
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.cc35
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.h24
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/blink_perf_test_suite.cc55
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/blink_perf_test_suite.h22
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/compositor_test.cc14
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/compositor_test.h32
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/data/Ahem.woffbin0 -> 2972 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/data/AhemSpaceLigature.woffbin0 -> 1748 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/data/third_party/Amiri/README90
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/data/third_party/Amiri/amiri_arabic.woff2bin0 -> 59388 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/data/third_party/MEgalopolis/MEgalopolisExtra.woffbin0 -> 43020 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/data/third_party/MEgalopolis/README37
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/data/white-1x1.pngbin0 -> 103 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/empty_web_media_player.cc32
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/empty_web_media_player.h63
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/fake_display_item_client.h49
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/fake_graphics_layer.h19
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/fake_graphics_layer_client.h55
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/font_test_helpers.cc99
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/font_test_helpers.h26
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/fuzzed_data_provider.cc33
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/fuzzed_data_provider.h55
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/histogram_tester.cc43
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/histogram_tester.h40
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/image_decode_bench.cc160
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/message_loop_for_mojo.h14
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/mock_web_crypto.cc70
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/mock_web_crypto.h170
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/paint_property_test_helpers.h38
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/paint_test_configurations.h59
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/picture_matchers.cc148
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/picture_matchers.h40
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/run_all_perf_tests.cc9
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/run_all_tests.cc66
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/scoped_mocked_url.cc28
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/scoped_mocked_url.h41
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/shaping_line_breaker_perf_test.cc146
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/test_paint_artifact.cc155
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/test_paint_artifact.h103
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/testing_platform_support.cc267
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/testing_platform_support.h192
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.cc151
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h79
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.cc127
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.h58
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/tree_test_helpers.cc43
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/tree_test_helpers.h50
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/unit_test_helpers.cc146
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/unit_test_helpers.h88
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/url_test_helpers.cc107
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/url_test_helpers.h91
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/use_mock_scrollbar_settings.h51
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/web_layer_tree_view_impl_for_testing.cc211
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/web_layer_tree_view_impl_for_testing.h107
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/weburl_loader_mock.cc171
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/weburl_loader_mock.h82
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/weburl_loader_mock_factory_impl.cc236
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/weburl_loader_mock_factory_impl.h130
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/wtf/scoped_mock_clock.cc32
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/wtf/scoped_mock_clock.h33
-rw-r--r--chromium/third_party/blink/renderer/platform/text/bidi_character_run.h103
-rw-r--r--chromium/third_party/blink/renderer/platform/text/bidi_context.cc136
-rw-r--r--chromium/third_party/blink/renderer/platform/text/bidi_context.h104
-rw-r--r--chromium/third_party/blink/renderer/platform/text/bidi_resolver.h1252
-rw-r--r--chromium/third_party/blink/renderer/platform/text/bidi_resolver_test.cc324
-rw-r--r--chromium/third_party/blink/renderer/platform/text/bidi_run_list.h249
-rw-r--r--chromium/third_party/blink/renderer/platform/text/bidi_test_harness.h414
-rw-r--r--chromium/third_party/blink/renderer/platform/text/bidi_text_run.cc71
-rw-r--r--chromium/third_party/blink/renderer/platform/text/bidi_text_run.h51
-rw-r--r--chromium/third_party/blink/renderer/platform/text/character.cc295
-rw-r--r--chromium/third_party/blink/renderer/platform/text/character.h186
-rw-r--r--chromium/third_party/blink/renderer/platform/text/character_emoji.cc243
-rw-r--r--chromium/third_party/blink/renderer/platform/text/character_property.h37
-rw-r--r--chromium/third_party/blink/renderer/platform/text/character_property_data_generator.cc134
-rw-r--r--chromium/third_party/blink/renderer/platform/text/character_property_data_generator.h228
-rw-r--r--chromium/third_party/blink/renderer/platform/text/character_test.cc334
-rw-r--r--chromium/third_party/blink/renderer/platform/text/date_time_format.cc275
-rw-r--r--chromium/third_party/blink/renderer/platform/text/date_time_format.h121
-rw-r--r--chromium/third_party/blink/renderer/platform/text/date_time_format_test.cc304
-rw-r--r--chromium/third_party/blink/renderer/platform/text/decode_escape_sequences.h190
-rw-r--r--chromium/third_party/blink/renderer/platform/text/fuchsia/hyphenation_fuchsia.cc15
-rw-r--r--chromium/third_party/blink/renderer/platform/text/hyphenation.cc48
-rw-r--r--chromium/third_party/blink/renderer/platform/text/hyphenation.h48
-rw-r--r--chromium/third_party/blink/renderer/platform/text/hyphenation/hyphenation_minikin.cc209
-rw-r--r--chromium/third_party/blink/renderer/platform/text/hyphenation/hyphenation_minikin.h44
-rw-r--r--chromium/third_party/blink/renderer/platform/text/hyphenation/hyphenator_aosp.cc248
-rw-r--r--chromium/third_party/blink/renderer/platform/text/hyphenation/hyphenator_aosp.h90
-rw-r--r--chromium/third_party/blink/renderer/platform/text/hyphenation_test.cc176
-rw-r--r--chromium/third_party/blink/renderer/platform/text/icu_error.cc30
-rw-r--r--chromium/third_party/blink/renderer/platform/text/icu_error.h44
-rw-r--r--chromium/third_party/blink/renderer/platform/text/icu_error_test.cc24
-rw-r--r--chromium/third_party/blink/renderer/platform/text/line_ending.cc214
-rw-r--r--chromium/third_party/blink/renderer/platform/text/line_ending.h57
-rw-r--r--chromium/third_party/blink/renderer/platform/text/linux/hyphenation_linux.cc14
-rw-r--r--chromium/third_party/blink/renderer/platform/text/locale_icu.cc464
-rw-r--r--chromium/third_party/blink/renderer/platform/text/locale_icu.h119
-rw-r--r--chromium/third_party/blink/renderer/platform/text/locale_icu_test.cc299
-rw-r--r--chromium/third_party/blink/renderer/platform/text/locale_mac.h99
-rw-r--r--chromium/third_party/blink/renderer/platform/text/locale_mac.mm332
-rw-r--r--chromium/third_party/blink/renderer/platform/text/locale_mac_test.cc452
-rw-r--r--chromium/third_party/blink/renderer/platform/text/locale_to_script_mapping.cc506
-rw-r--r--chromium/third_party/blink/renderer/platform/text/locale_to_script_mapping.h57
-rw-r--r--chromium/third_party/blink/renderer/platform/text/locale_win.cc501
-rw-r--r--chromium/third_party/blink/renderer/platform/text/locale_win.h93
-rw-r--r--chromium/third_party/blink/renderer/platform/text/locale_win_test.cc323
-rw-r--r--chromium/third_party/blink/renderer/platform/text/mac/hyphenation_mac.cc62
-rw-r--r--chromium/third_party/blink/renderer/platform/text/platform_locale.cc488
-rw-r--r--chromium/third_party/blink/renderer/platform/text/platform_locale.h194
-rw-r--r--chromium/third_party/blink/renderer/platform/text/platform_locale_test.cc18
-rw-r--r--chromium/third_party/blink/renderer/platform/text/quoted_printable.cc163
-rw-r--r--chromium/third_party/blink/renderer/platform/text/quoted_printable.h75
-rw-r--r--chromium/third_party/blink/renderer/platform/text/segmented_string.cc180
-rw-r--r--chromium/third_party/blink/renderer/platform/text/segmented_string.h326
-rw-r--r--chromium/third_party/blink/renderer/platform/text/segmented_string_test.cc107
-rw-r--r--chromium/third_party/blink/renderer/platform/text/string_truncator.cc221
-rw-r--r--chromium/third_party/blink/renderer/platform/text/string_truncator.h51
-rw-r--r--chromium/third_party/blink/renderer/platform/text/suffix_tree.h145
-rw-r--r--chromium/third_party/blink/renderer/platform/text/suffix_tree_test.cc32
-rw-r--r--chromium/third_party/blink/renderer/platform/text/tab_size.h38
-rw-r--r--chromium/third_party/blink/renderer/platform/text/text_boundaries.cc136
-rw-r--r--chromium/third_party/blink/renderer/platform/text/text_boundaries.h60
-rw-r--r--chromium/third_party/blink/renderer/platform/text/text_boundaries_test.cc175
-rw-r--r--chromium/third_party/blink/renderer/platform/text/text_break_iterator.cc452
-rw-r--r--chromium/third_party/blink/renderer/platform/text/text_break_iterator.h367
-rw-r--r--chromium/third_party/blink/renderer/platform/text/text_break_iterator_icu.cc989
-rw-r--r--chromium/third_party/blink/renderer/platform/text/text_break_iterator_internal_icu.cc46
-rw-r--r--chromium/third_party/blink/renderer/platform/text/text_break_iterator_internal_icu.h35
-rw-r--r--chromium/third_party/blink/renderer/platform/text/text_break_iterator_test.cc271
-rw-r--r--chromium/third_party/blink/renderer/platform/text/text_direction.cc15
-rw-r--r--chromium/third_party/blink/renderer/platform/text/text_direction.h62
-rw-r--r--chromium/third_party/blink/renderer/platform/text/text_encoding_detector.cc87
-rw-r--r--chromium/third_party/blink/renderer/platform/text/text_encoding_detector.h60
-rw-r--r--chromium/third_party/blink/renderer/platform/text/text_encoding_detector_test.cc120
-rw-r--r--chromium/third_party/blink/renderer/platform/text/text_justify.h19
-rw-r--r--chromium/third_party/blink/renderer/platform/text/text_run.cc113
-rw-r--r--chromium/third_party/blink/renderer/platform/text/text_run.h318
-rw-r--r--chromium/third_party/blink/renderer/platform/text/text_run_iterator.h75
-rw-r--r--chromium/third_party/blink/renderer/platform/text/text_run_test.cc36
-rw-r--r--chromium/third_party/blink/renderer/platform/text/text_stream.cc181
-rw-r--r--chromium/third_party/blink/renderer/platform/text/text_stream.h105
-rw-r--r--chromium/third_party/blink/renderer/platform/text/truncation.h27
-rw-r--r--chromium/third_party/blink/renderer/platform/text/unicode_bidi.h53
-rw-r--r--chromium/third_party/blink/renderer/platform/text/unicode_range.cc436
-rw-r--r--chromium/third_party/blink/renderer/platform/text/unicode_range.h120
-rw-r--r--chromium/third_party/blink/renderer/platform/text/unicode_utilities.cc434
-rw-r--r--chromium/third_party/blink/renderer/platform/text/unicode_utilities.h55
-rw-r--r--chromium/third_party/blink/renderer/platform/text/unicode_utilities_test.cc253
-rw-r--r--chromium/third_party/blink/renderer/platform/text/web_entities.cc81
-rw-r--r--chromium/third_party/blink/renderer/platform/text/web_entities.h78
-rw-r--r--chromium/third_party/blink/renderer/platform/text/win/hyphenation_win.cc14
-rw-r--r--chromium/third_party/blink/renderer/platform/text/writing_mode.h73
-rw-r--r--chromium/third_party/blink/renderer/platform/text/writing_mode_utils.h312
-rw-r--r--chromium/third_party/blink/renderer/platform/text/writing_mode_utils_test.cc298
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer.cc91
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0091b89571c844a754df4bdd133ad4d5b628e51dbin0 -> 64 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/017fd5359998719d5fa3b3df5fc7b72fdb163936bin0 -> 46 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/031ccb60903b3cd5780b6d3743051e216068b7ccbin0 -> 60 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/037017bbfe44de68091da219ac9f83d9100e4b6abin0 -> 37 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/03cc7ffa27b3a0309adfa4eed49fae9c7a91c970bin0 -> 14 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0425ce6fe2ee20810034f7c8347fe3f3a323f0e4bin0 -> 52 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/04bff48dcd66a033a9892df1ea38ca038436b3bbbin0 -> 27 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/04ecc32312c98ebb1c08f6d57d98c7520a3b0d0fbin0 -> 7 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/06ae60aae063a917d795388c4c36230286745111bin0 -> 48 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/075d2498e7c5370f5b4ad4d3a39b0aeb7e7b6242bin0 -> 33 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/07a75c4fd6ce06b8b82c40108c1333fab8cdf7a7bin0 -> 18 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/07e318cd144fc7efe7b9e95f065bd8e35189287bbin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/07ecf359453ac3521a0254b50fd2f2c2e90b481dbin0 -> 66 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0860ddb77abbc133ca7cf871020979abf1f82c0dbin0 -> 45 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/090a9ed30399e75da9d5ec8ee1b09668881c82bebin0 -> 43 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0952e648c4e17520dd85935964fee52375ecfedbbin0 -> 24 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/096684dc01d867661fe2f7455f9ab22e0857d7e4bin0 -> 60 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/09ec95829dbcf10db5349e44d7b4d442cc3646b6bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0a80baa1797615faddb0ccfaa6d46382a6b3e0e2bin0 -> 4 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0b49f431e39df252e4b2404c7ead05fd97f3e23fbin0 -> 46 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0c23817d368a1d420721709b639d99933b51fa45bin0 -> 32 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0cdb4b25ce305c78897a92f359a2c8c1477ba571bin0 -> 23 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0d015b964100880a09aaa492447c5ea1ca60aaa4bin0 -> 61 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0d3ce4f01204e526d5bc5afb7a754235423044c1bin0 -> 47 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0db82720176ea60c0948406dc77d8fe6d225f377bin0 -> 61 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0df9cfd31ac0bd6d932145f32e68bc65cc883da8bin0 -> 63 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0eb1c6c9f8227944aef1fd3dcb413b7e592d7bfebin0 -> 54 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0ed3b05749ab8c5346fe983701ed388589f31012bin0 -> 35 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0f639c2b9e1a8ce651e9f78dbba9bf5d1cdec07dbin0 -> 51 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0f63d1414ef452e3a0cd6f02426027be24cf05eabin0 -> 23 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/106e06e64de76e45fc8b350b9ac8c86b5c6bc511bin0 -> 35 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/12108e195d16ed36f3a2342cbd6fa681e14d27bebin0 -> 40 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/122ca249dac2943200b259fd23e18a4cb31035d9bin0 -> 48 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/13a965f9e03c788dda767783dedbdee04fc921d9bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/14be6cc2655b8c0eca2e16a205a48b619d4ab329bin0 -> 35 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/155fa724bd473d8d66ea8e5866261ffad848589dbin0 -> 40 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1599e9fa41ec68c80230491902786bee889f5bcbbin0 -> 4 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/160fd1afc477f8f5f46b25291b4b33098f4f00f6bin0 -> 63 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/165435e80cf32dfbb9fad8d24e0d2c92d1814b1fbin0 -> 61 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/167609a843b8eaa0d6db4375f66fda15461950bebin0 -> 47 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/167749d0e421d223150a6c4248f8b355e8ce66b0bin0 -> 32 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1714db8daebbab059a62b71f571565125c0a297bbin0 -> 21 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/18310219d1b7f501d07dff568926bcf8d997e6d3bin0 -> 37 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/19c42d45aa7d98a0ef2f2de2997b6e22aa1a8f1cbin0 -> 37 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1a0cb01355c7db499eccf0dc45dc27ec89856f39bin0 -> 39 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1b181fded6cd8ca58cfde81415b702b0e3da82a1bin0 -> 52 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1d8207d5b9b1abea52fb7d5d8c6ef2a0d5068ffebin0 -> 63 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1e1946a1b74ecf5e21460531d89043b59b15270abin0 -> 58 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1e1d033b68bcb40e166166d8d86be1350231d232bin0 -> 22 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1eb24b4138ebcc36e94021f06f7f89c64cd4b583bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1ee6992c347409ea83901afbcad6666cf169f983bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1f412eca783baa2cebfdfa7d20b6d64e4ae712cdbin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/201c21a10bd4fb38e8f399951ba4d303821f2deabin0 -> 65 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/20f68a183711473f34885a340a83207791d73004bin0 -> 5 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/22064b5d3d2b8920c85c3cd43ff9d0442f21c021bin0 -> 6 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/226493237f5a5d1ef606df325650862279f45c80bin0 -> 51 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/22a3b3ca448b9873d362154bb4a0889e4ae72e7cbin0 -> 28 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2311b8b93a21c9565e1f39ff3749a982a8b21559bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/238b22352e0766896b15e26ee0b184690b39b7c9bin0 -> 24 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2393e7d3d00847697336ab38f7b812c2b09ecd60bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/24c08e3d9ca730a81eac96c1345ae775b10acf5bbin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/259b3a4f074e9cb311971fee987a232eaca8a589bin0 -> 10 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/25a03ae6dc0c77f9f5546031d22050a9374067bbbin0 -> 36 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/264ff1528d215678b7d0cab6f30d75067a8b69cebin0 -> 40 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/265e53fceb8d465cbc82135e9dfdc45d78c50af9bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/266ddd93d187e4bd0ac6495cedd8ebcbf55aa171bin0 -> 39 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/267e1b92f50fb25b7d6995d72ba2b607c491094ebin0 -> 45 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/268554ad42965d1d12fc2e14f6cd25dde82cbf22bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/26b5604da5596b2733b603a176babce7db65084fbin0 -> 63 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/26e268692cf273e5ca7686759ffba67ab0c2a962bin0 -> 22 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2751c451ecf0e2e1e61b14d5bc42fc96f8ee9a90bin0 -> 50 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/29ed2cf7b7d7844677c23fcebf5d6d7c7c745433bin0 -> 45 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/29ed3e6d98b4e03cb088941a472c74e258991dcabin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/29f498a7b643fad9f03c5563bec1d57177b55655bin0 -> 64 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2a2568f696a5396ebf6a29f30d3e7da7517d583fbin0 -> 44 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2b3dece417ae8dd46a921e70290367680dbc2ad4bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2b465dd11f7a27dca616dfe36281f590128f4eb6bin0 -> 27 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2bf3a0edb8d97471d635d43bbb2126018649437fbin0 -> 49 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2c3338b0400e039743ad2fa537cbbd89d4aa8824bin0 -> 53 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2c3a0197ebe801d0982f5230568081c936f95e28bin0 -> 5 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2cbf6e11f35337aba018755545eacb3a08443337bin0 -> 47 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2dd374ed8f7a33fdaeab8e161a010400340210c4bin0 -> 64 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2f05530ca067c990cdc6283661f950ddca781d4abin0 -> 63 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2f235b7ba2d613888ad53a1430e1043752a3fb9abin0 -> 61 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2f336d0e17a48e2edc13a2e8a437aa9773099608bin0 -> 5 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2f3693dfdbb384b066ab8b2f69473456b03e5c2bbin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2fb8064bc537fea2daec0ab6ae773577314b6876bin0 -> 14 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2fe44b618a89d1cdf9627297460f36d6e927ebd6bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/30140397fe38ee61f01eff44b5cfa48285e47889bin0 -> 4 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/302de7fd35416714976c8b80d1aa3e64b604c671bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/30af3da9ce882193e3588303797edb80ebaabd6bbin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/30fd500a65dd24adaa7b516381301a183f08508ebin0 -> 51 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/315c8c4c6165a05a6003907cf44bc7ac1d367d82bin0 -> 55 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/31a9912eb9e093fcdce27d83ff1e9bbee5fe34e3bin0 -> 65 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/31fd332b18f4960afad7af5b4a8ed9027e74da0fbin0 -> 65 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/32ae073bdd9501d7617c7b2afeb53ca0c15f2ea4bin0 -> 38 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/331c576d7334011219b2455e07e52c3633769585bin0 -> 22 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3382c113667455cd7b38c8b41662fdddd3e60bf3bin0 -> 66 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/345cdc7aadb5e1f24dd5708eaafe88ad5e1852afbin0 -> 54 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/345ebd88fd96892142cef88bdefc7c16d3216a0dbin0 -> 61 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3479cba50395ea6785a51cfe3d247634f5e601febin0 -> 62 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/34bc251961f054bf752bf103738399ba4eaa09c0bin0 -> 46 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/34c2d14e77be385419644a3f15dbb3fea62724c2bin0 -> 45 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/365791bfe51a4809b7b1be97a6c6064fff34245ebin0 -> 66 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/36a915cb8f078a9ed3b744c4f876ecc946a2ec5fbin0 -> 29 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/370b4d8ccb0d0671e7c66651585703815fe16c09bin0 -> 8 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/37b1e6b8ddaac24c3f3fa3b83646c4cc5ea4fcefbin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/37daaae08ca0337f2b50a5db48171e8680583241bin0 -> 52 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3807d30510e275eeba48f261f1e1d72eca14b095bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/383c7323b00bd06cc911bf28f025752cdb9d088ebin0 -> 45 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/386563dc4dbfd50a632e9ecf03b99e6955e56ee1bin0 -> 19 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/38e857fcb5086ca80620c1c77dacd93dd70c7247bin0 -> 35 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/38f1413cd7389276c46425fbc06623ffca06ab8bbin0 -> 7 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3a387a4da1d05575e4154e689d3b7aea816c9955bin0 -> 12 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3a3e4ccd65af3efcb6717be0828690ad6196b532bin0 -> 32 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3a5a90e4668e4112a67184bda480990ed4e25a18bin0 -> 5 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3a8bcfee35966bf857a833a91d6b3d17c333b961bin0 -> 65 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3c4b8716d0701aec5792c6ef51a033487d9f2052bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3d15fbef3d7252f176dffffb42d9064ddd10f08ebin0 -> 64 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3d8fbe87ab0c655db7c32a846112ad2c9d3e7f4abin0 -> 47 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3d9fbb9019796c63c14425c56eb154f8e5d82717bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/405a2d2c0bd3f6762605b612a668efe1763ce75bbin0 -> 13 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/425098d86e6ab28bd6f17ad53ae09db75b5772e1bin0 -> 64 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/428bc20500605fc0cf424f870d1e8f83c5059df5bin0 -> 18 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/42bd94f85db3f5ac4c01ff7ba172bc8ddef60432bin0 -> 62 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/43889ce379baa9132173fc398fafe55f3d356505bin0 -> 35 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/438f603cb985b367fc217425d8f98200203a4dcabin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/43c775987fb5eddb58c8c5df5efbc83b06b87546bin0 -> 46 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/45620f98144efb07521ce60df2d85adabb0df2ffbin0 -> 50 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4622094cc10659c5b7185c529a4216821d481eeebin0 -> 47 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/465fe4b5501c930d1d14e6ce04057889967c1c27bin0 -> 66 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/46bf374d6cbe5dcbc6d6642efdc0e21a742bf648bin0 -> 65 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/47bd0fe45358302b5d0ea59d5c81e8a0fe2aed7dbin0 -> 55 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4974f2975b9edf3b5f045838364dbb141911ad17bin0 -> 66 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4988df6115609e79c6a37a06046f47c571ddddebbin0 -> 32 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/498b00ddd7f75a93d2b0731372c290e10020b17bbin0 -> 56 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4a6483b5d52b34b4a0aa9a3a1bc38340161c226ebin0 -> 54 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4ae70fa44b52ce5c6828ddc648ed0e0748629640bin0 -> 51 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4af42e15d5fa2e5f6f83942c024a11dc7e70d7e7bin0 -> 23 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4b61b7402eefc4f49ff187c549ce09bc5ea6f549bin0 -> 54 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4b8cbf97d57464de8802b869c7a16d91647f305bbin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4be79e98814c59631c5737400ad09d67907b2416bin0 -> 41 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4dd140b8aa76604ca93c928056fee0b7061712d2bin0 -> 47 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4e511d2c95640f8646e9aed3527aea1b9bac35bebin0 -> 53 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4e75b118acd9bbe263e8c1a00ba1cb8f71054c0dbin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4efdcd497072f85306dca983b56b030a61bfdb5dbin0 -> 35 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4f3d0b3312bb9e4897859408ff1455a70d83c56bbin0 -> 51 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4f6d30eb731ef1bb8a2faf1d61363c21366455b4bin0 -> 64 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4f97056b65a42fb1f93b2fe25d70bb58a5e36180bin0 -> 31 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4fb0166607616ed8c7ffb9061cc214949974575bbin0 -> 45 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4fc2b1701d23d3ef1bd18dad25da22b9d6b2a24cbin0 -> 24 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/500645591c80e1bf4641774d972da02865cdc697bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/50781232df4526cfdae1952e84e912352d0b92a0bin0 -> 24 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/50a32d6d96c3273717e1c8b8e4daf4e7f5ff72debin0 -> 35 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/50b87134e7464b32a37740403190c8ef03116565bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5308c7cd09d4b244fbbc544bcffdf7501f23b87bbin0 -> 63 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/53438950d2b47be5ab6849cac445825732abffdbbin0 -> 51 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/546fcaca292b59045074e9d78560511341987accbin0 -> 41 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/549c9f3fffff6112030056a212563dab0ccfc3eabin0 -> 65 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/55228c704fd6a9b6468d470ed6877332f7d912d0bin0 -> 63 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/569eae3c857f63840f56560014ff4b2adb5e0b07bin0 -> 33 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5723227540d0bc577b6d06b42942e939b9fd1f85bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5742af4a414373bf4d465c268766765a388272c1bin0 -> 25 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/57dc2437d9c8773f1d728052253e849ff093c64bbin0 -> 56 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/58036d855a77e281076e0ab54e4e63500317a00abin0 -> 5 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/59dc4f0b83a23844023b09cc4fe0f277121c7be4bin0 -> 31 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5a8c02ca27412b03a77d81d55e8f612b3139f3d6bin0 -> 38 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5a9b1c687e2ffdd959dce79456b0c3808c49a021bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5ac4c39d8de57e953d752984c2a454eaa6ac3921bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5af236be1e7908f68252598cfaba2b668151d65bbin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5b3a38217ea491444c80a17d739936b0eb7d6276bin0 -> 48 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5bbf36ae3e0d4375b257eaddac36a20aea30e289bin0 -> 57 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5d12e34c71e5d001fa6ced3b5946164f0a4e8157bin0 -> 39 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5d2e30fd6f0b5e45ca6a53957d45666ded1f7e62bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5d32f59baad9f63b365f61d2fe7604a83e668782bin0 -> 46 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5d5d543446198ca0c1ee0a90d9bd1dc1c906f1e7bin0 -> 38 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5db3b1cd26803a8f8f3570720b1c8bda531d7973bin0 -> 36 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5dda51b9d05f7d99590bd108e0fcbd51cfea8b92bin0 -> 13 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5e6e9f58761723d241d8a5d131b289f0b4931142bin0 -> 65 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5e8f156ea2e2f8b82a31c57d387c9d73e3048b53bin0 -> 56 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5ec6996fffe4c392ad8a7ee446e17466c31eba6ebin0 -> 64 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5f10e932ff3d91c82027093fcd7ebf7963622c82bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5f32e883581eda6501dc45377d2cb092246c807bbin0 -> 61 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6021fff400694a589fbe487512fe0a75acbefa81bin0 -> 46 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6116459314bee4a46c07a849105edd8dd31086debin0 -> 31 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/62275b63bcdf4ddcff849c7c0d5b74685f951781bin0 -> 66 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/628e5bbbae6fed6ea8e56c0020648fe805a4b81dbin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/646a8e6c1455092fbdfeeff8511739ff88c4c31cbin0 -> 28 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/64b68bf5b882b9bd0b37267287980ecfa0e44a85bin0 -> 4 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6585f4e2b5be491aea76a1174036796535c01570bin0 -> 32 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6638644549adf34f2549da316a538874827f2dc4bin0 -> 33 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/66e303967b5f0df9441edfebc843f974bca85616bin0 -> 35 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/675fb106acc92ecfe6d5e13081ac49bcd0366992bin0 -> 23 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/676c9720a8e137a235adbd9857109ec4a80a3975bin0 -> 66 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/67a24c8ca1c9501b488ed1ebd0a6dca5b2beb18fbin0 -> 56 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/683dc48c0057cd1f0fb8dd57299d7919d238e087bin0 -> 51 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/68c906d67e12fb6eedef7accf599793b63424e07bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6907c48d082962b116a35a157470c25b3d615042bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6982d23746dd5380bdbdc870b04d913975ebda35bin0 -> 27 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6a2f9f7369d31e0961e6c066265fa93e677c1e98bin0 -> 12 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6a6d36fc57672f18dc8687c67f92fc7916dfa8abbin0 -> 64 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6a6fe8f178f97ec588885365b8921044b567bb65bin0 -> 7 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6b4d1620e335083dd08e08e55407509336d45ad8bin0 -> 44 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6bc10c9d19b66c285f2f34787ded5ebacb3fd5f1bin0 -> 34 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6cac1f78df3a53057534598c63d258e441da9260bin0 -> 26 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6cb3b692a507eab869acfec4f59553dac54ee13dbin0 -> 11 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6d485d07919ee9e2ce40dbfe85af6d91180b37debin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6e9248b93031bf55ca151bf7c4997efe03391194bin0 -> 20 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6e9f26c319655235857db9f515b4d7ce3540aa72bin0 -> 66 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6f57614f7968d1ec1272bf99eee1755ec0c4ec28bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6fd2b983ababa634e057f1c36a83e246882939ffbin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6ff63cce00d07decba06d52e8968057477421b7dbin0 -> 39 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/70c0f0a855145d47e1f43c4e2dfbc43c7ae83cb7bin0 -> 58 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/70d13ee99f89b4766262b612910eed696f97c63bbin0 -> 13 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/70fb57e41949e4f274f796571f918a1a4ed90bf9bin0 -> 47 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7115b5fac7a322124258809ee267e218dcede33bbin0 -> 28 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/71db046590e7096f4adb4df3870bc7a83be32a06bin0 -> 8 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/720b5f41cee126560e096c86e63fbad79f27539cbin0 -> 20 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/72152e023c4acac71fcb17eb190a5006d9cc326dbin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/72428a53149aa6814271b34e8739ca94a5186467bin0 -> 61 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/749d4d53263431538db0aad0955a00d27a6290d3bin0 -> 48 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7512e4b6487628aab55a3bfcac9d98a044b35d4dbin0 -> 12 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7521a524b21f3bca4914862fcaec7c93b17ee456bin0 -> 57 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/754326af79272baf12eed6135e279f05760b090dbin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7544ab036f63e3d8d6fac775a1726b2b2b98880dbin0 -> 11 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7567cd6037ee58f9c0e185b3ab9431b7c105621dbin0 -> 66 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/756a757748d1dd648d369ce8cf9a2a8236305280bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7649cb328ba87f23e0f5a920223376b2342f9d91bin0 -> 65 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/76aef317f1628a04c82e21bf6fe33d2d562e753abin0 -> 49 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7777e7e27a1dd2804cf12bab78987a69286ab44ebin0 -> 53 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/782b07efd1d43b640dc84626ec375c6593cb0e4bbin0 -> 7 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/78a974488d0ef6bf8f585e03d1306edeba5ad2b4bin0 -> 47 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/78f4b1ff07a84ba09491b0d159eb65b8f17e97c9bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/797006985d47c774d618d5b5f9552348977fd70fbin0 -> 53 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/79e206d6a750918f6e73475d0961ee52d2899928bin0 -> 33 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7a6d3a6255de7ef65924ef6cf2beca37e07f2ab5bin0 -> 26 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7ab9d193d63c4b48353e8eb18fb515d35acfeaa9bin0 -> 47 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7af0ea65622381e22bfa8d5992bf39396f142033bin0 -> 43 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7b8f7e6cca594c099cd64168f7d231a52d4a78ffbin0 -> 57 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7bc82f8d0c482f7e90b3c29e7bee82dfdc177d89bin0 -> 25 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7cb507bbc72d774252939608d5dded947bd15e5dbin0 -> 49 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7d39cf0e7187394e31a37fbc45edc3825ad3df67bin0 -> 52 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7e80c5307b5b65c62ce8f97509a3588eaf0ad162bin0 -> 7 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7efd9ce5b0523f09f7b4f37a838f07739038aba9bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7f2f798b8893e00cfba14ebac6d233360ebfc101bin0 -> 39 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7f92472679d1050f3e766d2d34dc482d7e22e654bin0 -> 56 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/80ec0565eb3e7900903c134584d494b618d3abf5bin0 -> 27 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/844121ae58d6bb36f81e92c42723528a7123b5c0bin0 -> 56 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/847c3d55a452bf83a6eb209b0411da3477491300bin0 -> 62 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/84ff3957ecc05bc806b183ee8d2310b09f8fc5dabin0 -> 53 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/856f3b79f3a56514f0fbbb7c63f361bb6cc07be8bin0 -> 32 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/859f2ac933251900bc8ac45ae8b6ae64c25f54e7bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/85e53271e14006f0265921d02d4d736cdc580b0bbin0 -> 4 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/86592b6d5006db6b8515fcf98800e0154e0e1894bin0 -> 13 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/868ff4054649d579a1d0efa5356a4363848b9fdbbin0 -> 66 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/87040a63d8cb013d0acf4dae68805d2e6b5cf251bin0 -> 19 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8727e0346f1575344192b5457954528744289af4bin0 -> 39 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/885f266cf4582b510d66e9de969beccd6b4d450fbin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8887b2f899212d3292a2e67015a9622938d79a8bbin0 -> 64 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/892c6b54361baf9960565fcf047da435a7c6d27ebin0 -> 52 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/894d0bc2371a815befbafda9971c31bfc65e27b6bin0 -> 43 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8963c57885ebbd41fe8a12b7459efe6d5295fab9bin0 -> 38 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/897c5f0c74ca1f0f63befc2b09e67acccf302acfbin0 -> 52 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8a39167b995cd88282a5c9f9ac54441325e80403bin0 -> 37 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8a78efcd5256d9523588dc0b6baf5deede51f309bin0 -> 47 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8accdac41a14523199975b8919241f3c2bbc7e72bin0 -> 10 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8b0d9bf66f3fb977a9c7c57c3cb7ca4667391d0abin0 -> 45 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8b455cb428e79e300de4ffdf63d523a1cc8c5f6ebin0 -> 41 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8b641c5fd96962ec14679365774e141e5f417af3bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8cba0484abbb6537f3b95f2a9553b454fc67b175bin0 -> 47 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8cbd2c17b14d60e47de147325d2c2feae299c19ebin0 -> 64 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8ce2e0a7e0b40b3aefbc5d78ae51d7a137bc7351bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8d352b0bfe03414b30083dc56aace56eef975bbabin0 -> 46 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8ec10c7b8b7bad61459153975bece3912b7ba55bbin0 -> 49 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8ef73a357235fae6f3ce4738b2af4e9918f4861fbin0 -> 28 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8f3b0d41963bd53158153fea42d73a6521289d7cbin0 -> 25 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/901a58adfcedf69d1eb9ede77bf365944564b653bin0 -> 66 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/92258e1bb3be4b1c16cc3ea9f83c0c1ad69a81ffbin0 -> 45 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/9364fa0234f0aa53d7e20fc960c9b1ed7b0a6c57bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/93683cc766ed6e4582c992e97f9a883be22772a9bin0 -> 56 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/93865ee370b7c5b46eb9690fa88f70d288817a63bin0 -> 29 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/93e914167275387141c1cafcff5c799b0310a81fbin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/941cb1a3f06cae3ded8242d250eb7a4cdd0c00f0bin0 -> 10 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/943d3677ac55bcc70ef846300b077cd3521f6c63bin0 -> 65 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/943e337039d71a32c0d2800434b11a8f3ab0a3ffbin0 -> 51 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/947507c0cf74bd7525de63a30de65067d5c753c0bin0 -> 41 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/94abddc43e4382ddd011e570a40d4d6a8c6a83ffbin0 -> 6 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/94bf062093c396d7a7915dc0e826e13233522815bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/95358d5226cce134ef7383a07b3088fefa510fa7bin0 -> 20 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/95750c0f6ae3297204e59b120601c5a349dde5d1bin0 -> 45 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/959432e49a04a4d29f98efbaee5994c8f19bae2abin0 -> 66 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/960a49d0fc52b2cae053dce5d78d47d0a8273b84bin0 -> 57 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/96706073f398f6811f43000954ec6cf2a6ac6e2cbin0 -> 5 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/96847b259da89a453d233ade419eb7db38484193bin0 -> 61 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/991e91d666edbf577e009fdf1705df66b34c0e2bbin0 -> 26 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/993836301f85ee87b8049c3d277be35699da6b53bin0 -> 40 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/99a15414b858c8899c547b872c14d885ccfd7732bin0 -> 6 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/99f8d3a4b9e03c55d49051c37facc09060a27a17bin0 -> 49 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/9afde5ff243e9e0b83bd41fe9e14a2b51700f3b0bin0 -> 54 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/9b0f3060c0c827affd4d9d09ae7c5ef8d9a02bdbbin0 -> 57 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/9b3cc9238c57e1fbf96b881d3fc7a50b5fe1adc8bin0 -> 57 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/9be325272f875f0ac5da945ae13eddf54f478d2bbin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/9bec724abc6ad8aa4736674b0400f24e0964acb8bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/9eeaded113dc382607cd40008ff95ffc3931698ebin0 -> 58 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/9f044ac98aafab31444e6b40701aa1aa24c4f16bbin0 -> 44 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a01dadac65200d320c4237dcfd2b5a918f0e75b3bin0 -> 36 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a1a7715c7596c77b892dc6d4debb7c108ca4ef97bin0 -> 4 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a1fb0023740e215a79615b048ae2cd44c5c5a21cbin0 -> 59 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a2ac47e1458897c2106586cd82ba4cf955250db9bin0 -> 5 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a331fcf28f87a61b052f8a1625166e4aed890951bin0 -> 63 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a33286264113976a15d42b41d7365c9f9c7fdb7fbin0 -> 20 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a3dfc0c77acade0ee48dcc73e795a597d0270a73bin0 -> 5 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a425a34727d6676a695037d5466f691b551778f6bin0 -> 53 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a5a1aad89237ae6074cecd5359b7632c57153a7dbin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a74d9214eba82e2d2e619f60087628a690970811bin0 -> 50 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a79e5e7e7e73e60bfe334f7700723467399f8292bin0 -> 66 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a869cc1ba8c28159882e53171a480f3ac97138eabin0 -> 61 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a8954512ba38ba4fddc38c8918ab99bce9f35c02bin0 -> 37 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a922e9e2cc2edb309b2a8ddc0cf7b062657b72e5bin0 -> 61 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a9b6b42e3c65774dcfb7af278e1d10f8a6f24b71bin0 -> 51 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ab131dde88bf9765ecdb4f21c6e0ba6c7254d809bin0 -> 45 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ab2902184de2ffc88cabfdbc9fa6171d6b7a6f2fbin0 -> 36 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ab76a7aaa7285c49488be4d8e9f4a6a86e4ab51bbin0 -> 39 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/abc389bb60ba2d57b1366067aac6fe90e0ce246fbin0 -> 28 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ac6e5a17a260ee1601f58e997e9943888a07bf07bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/acbaf77d495a9cdc3a6602ccdfb98ac7cce3d1e7bin0 -> 66 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ad3048344db255a3ce132b52c0bed0519d746779bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ad323408848448bccf938cc89d39b019766052b8bin0 -> 39 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ae0e0a09eefd341e3c578df802dc004f83c61e9dbin0 -> 51 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/af26968b71caf11c882b07cc25306d748e28c4f6bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b1b3ab17cfd331677d32b65e52fb3ebc3fdae431bin0 -> 44 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b1d3810c7343c79212acdaada973ee18b7db156fbin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b20bcec80dcb1d4ac6d26070a6032e7e21c1b8edbin0 -> 48 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b2c4d853da500f3766902c2d0f4bc5b11605a7c1bin0 -> 59 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b2fe70050194511642fb0ce458f7eabf99c827febin0 -> 65 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b37e56fe2d9ef05a0383a9ea4eb4858711528321bin0 -> 30 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b5245e2f6770333319794ee0148866173b2f4941bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b5af0f0f8e809e2de1a516ce42d5930f5b913326bin0 -> 22 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b69aae002ffa00a7db0fdfe2d2ab8bd8ddb192b1bin0 -> 61 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b8bc0fbaf88034370ef49d7cf791f32783221d00bin0 -> 48 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b8d8a865f23bcaa4c6428712bec185e0bdc421d2bin0 -> 19 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b98689a4ba024cfb07535194d9b9f23d95ef70b3bin0 -> 6 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ba714f908ae61e60c111d21a75a6126b02afe85abin0 -> 29 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/bb6d10fd85abd60bb59bde89d770f99d3ed25454bin0 -> 54 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/bbfbcb079db8785b0ff8a27814a4298d97a24a0cbin0 -> 61 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/bd38ac80bdfada6fdbd70689d28052206b5da757bin0 -> 34 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/be00ab687a5c33e75468f68662b87b13e2393fe5bin0 -> 61 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/be3950f02753b8e0cdf26e70367c1eb0fb41e20abin0 -> 22 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/be4ae7a571dd64ddbfdedec01560f4795e8d83ccbin0 -> 38 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/be71e72303e85788184ead184fac1dd90c84030ebin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/bf44d29be7a2eadad300f06b57f65a1af8368426bin0 -> 49 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c0887dd7e04bd38e49e30842cbea040b6d725302bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c15d4f25799729e0df173d00b9344a41866872c6bin0 -> 25 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c20e3f03556e0c3bdcc145929b39b724d554d24cbin0 -> 53 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c3ee8765cdf7204dd0e84c749b488a2d57ed6faebin0 -> 56 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c50f46e18d37df40e28d1757cec8f461e4a2643bbin0 -> 19 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c5248449556ff7961b05cdcb46e26915bf71f232bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c5abe4b58a57cd820ba85d4b4f8c978c4b6fc325bin0 -> 39 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c668a201cf1472bfd7b1133cf3037e43d8ddac44bin0 -> 42 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c799bee0f970f0026ac15c5fd69f9ab129672c65bin0 -> 42 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c80a0213adf212446effe69a790c2ae590e5d1dbbin0 -> 15 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c9a7f5a196e67f9e9d8269fdf913c8c6cc7ee320bin0 -> 62 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ca736d07e01117faa9efdb2eb46fd76fe1a4b361bin0 -> 24 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cab3cd4da6ace0baf6e2098437a20e67df16c854bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cb00a2de12f9d97ced5e76216cf9c3421a5572a7bin0 -> 48 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cb29f52b15a3d9ce259a78569e3889e874d0a1f2bin0 -> 59 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cc7c3847fd9c3dae275a047ad9768e7b0cf0d34ebin0 -> 62 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cc8cfcc9023fb2641c1edd4deb1082d4fa7e3b48bin0 -> 26 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ccd8917b4913548142a88960662a064e51e675adbin0 -> 7 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cd428d63c2a5b48f6034bc6e2ab9c6dcba3cc010bin0 -> 39 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cd7dacec5470c7822000513f2b1c2c98b4cd0845bin0 -> 63 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cd9aeb9f43d9aadb4e36572c8a6b81aebdd52c15bin0 -> 5 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cef34c646eaffb941f130a68d2059f48d5c7ab46bin0 -> 61 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cf7167d0b37666c882b00c3f5e1237dbf4e83204bin0 -> 42 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cf8a91ce4797721dd124ecdd875b9b9934154f62bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cfde91bf46ff3ae67a9ecfbdb38f37173e71c8f4bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d0559199e54c28f9e946d0dce5ab2dbb377a9375bin0 -> 65 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d07e4bc786c88b8d2304f84c7db2098666f822c0bin0 -> 4 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d146392e0062eeb2ce5a7c41ed2bdde4c7496309bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d1aa3d34981242af940bd5aed0da9c4a3ab8df66bin0 -> 16 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d257fd5032bef81e055650924b99bf3f9edc9241bin0 -> 6 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d26ec55cbd5c5b8e4caa7d3db1e3a28050279061bin0 -> 39 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d35f44f256dd19b4c3a57552b3c31c7ac2f10c71bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d3b49ca37e4587315ae85884c71eadb46fd216b2bin0 -> 63 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d4167367d1f2f8f8a215d26a9e8d769ad76088d7bin0 -> 24 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d515ed898716e2cb64a423f9616226222e2cc094bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d59af7f859402a6e5f5ecd70f89da19fbdc91df8bin0 -> 26 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d665dabc1a3fd8b71784e4bd1f7ee30d8ae5810dbin0 -> 43 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d69e544084fd583fcc259a395fe27ae401493fbabin0 -> 48 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d748324df83ffc92899a87a249da3b22a766ace0bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d7a63b95c19318604e338a59367e4c1e482cf23fbin0 -> 55 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d901627df99ef6e0dd39f069fe57f8a6b4d07eafbin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d9bdb949b4b12ecc07bb2a6f069945184154b5b2bin0 -> 10 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/da0759b181fdb1786c825b9269dfcd6576294540bin0 -> 53 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/db081156226de488cba28da316a06b0584e47dacbin0 -> 5 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/db2a2a890cece89427cdac0f3da25870f79de5b5bin0 -> 45 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/db41c3fd66b879bc44734149c16673506bdb5cb1bin0 -> 42 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/dc2550cc5d4d010b825bc19b7a199776826e78f8bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/dc2d035effe5ef99e8053dd0e491bb46a0823eedbin0 -> 44 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/dceca140e820d93bae46c226db040532fcbf44a2bin0 -> 31 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/dcf3999552449b6700c3216bbbc6ef1bdc77505bbin0 -> 5 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/dd42e9c767d472a425beeb8acc32d564860b7d2ebin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/dd97e6544dd275f099f8061d623706f1c13f2138bin0 -> 44 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/de5319c8c809e45e7b0e488d3cc9acf6bf752687bin0 -> 6 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/decb0cfd19b3258049933b6fbcb46fec73a4d548bin0 -> 52 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/df10bfdd5da26e30797873ef8674a13fb1c3f053bin0 -> 39 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/dffb62d6a8e866560a740ed82a9c393a38b637a9bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e03108134e00e531868244ee6bf423e99ebbfc46bin0 -> 45 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e03f6f90b2caae447cc9e98e84d6a75e35e58751bin0 -> 48 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e0512998437db37bd3cb8a7aad0b3ab0242ce797bin0 -> 10 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e2e4bc47f012d97d70d1956e49bddae2e1244d0fbin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e3db9eacfc32f38da67361cba3b3eb3752634164bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e4969d40e8341427dbf49bf74b7d97afe1169bf1bin0 -> 41 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e50634e178601a5938c5a0448cb4157b02e6a1b4bin0 -> 6 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e5179878e3fb95877a41c20778ee5016dea8d8d3bin0 -> 27 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e5ee3fabcb52e7f8858042a856fff30efe6c7accbin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e62b3eb166d52861354ca337ded5d43daba4bb66bin0 -> 49 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e6896001a225f621044aec7ddb845e5cb38eff00bin0 -> 60 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e730ae86386fa1fe0c4450398b1e54ae1c39634bbin0 -> 27 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e7b71ef22ca8436fde215810dae55aaa887ccb32bin0 -> 64 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e7c8785213e42e2f7697a217806e5de4a0997b26bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e83aed593d8d3ff801af1b34880f50125dba6c33bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ea28a8c84a47b956dc3e847c6145bb91d3cd82d0bin0 -> 54 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ea768448f4902b2b15ebdb25938aa739b2d6e881bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/eadf0ead393cb49cfbb02f765da794d368393401bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ebb6456eb1eb805f71c55cbc46a42a14a622d8dcbin0 -> 54 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ecbbcb5ad293d71d49e3cc3885b7f96e5b1dcf5fbin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ece5af415cc041bb735aa86afa06b075b4c54834bin0 -> 57 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ed8e0e4cb585c3ff11e7f8d6efafec59e471e2ccbin0 -> 43 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ee336547d0acfd8958ec01b018e8530947520977bin0 -> 63 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/eefa939889601e0d205e95378595edc06832711ebin0 -> 40 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/efc3c7f8c4fd74deed16b5bb2aee0615e90c47a9bin0 -> 39 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f0af4ce54b32c3d9dd2b0711541bbe9f7c2a38adbin0 -> 38 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f0d760923f2a897a427b03ce6c9a47cd48181150bin0 -> 45 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f1a2437e8045ddca09679937f1f992eccdf78cf5bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f1c41d56e520130a00a1eb903ff65ff56959b383bin0 -> 45 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f375c6bb597d19eba397de0c632e3827b2ab0e60bin0 -> 18 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f3c93dcf82cbd38a84d53163889ed83583c1757abin0 -> 39 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f3d896c80afd90ba7aaad52e5a58a6847d3b06b2bin0 -> 20 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f3f1d178edbab5fbc6ad3633279b6e2c6122ba92bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f406f3f60cf7f821824b7f6ed96e8464059da397bin0 -> 15 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f433573fbc60e06dbef10cab93d4da29824aaa66bin0 -> 10 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f4374cd2ce923bfa39f1c610ebd98c20b9479bb3bin0 -> 57 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f4be1d5768af0d4d87947a5d9d7047304df57e10bin0 -> 55 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f503dd5d6d53129eb2a3788297afe481249cbed7bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f69cee58c4edfe1fa3d9f5bb55101e23bfa2904dbin0 -> 39 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f6ac1e6f1bab62eb2249b23ebff5c37c7433dfdebin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f6c4066e45d76615b04825c9b15a577877e55bdfbin0 -> 55 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f7272e731ab47fb9d5a040a54ef562cd8dfba839bin0 -> 61 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f8753a03428cb55091e1e616cbfae5ef4d34dfd1bin0 -> 7 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/fa547ad152f92ad07d5b92b4fb237bf8e0dbae2cbin0 -> 25 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/fae3a5206c778c8bac02f916f55fd3f18d100173bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/fb77df6aaea88f191f0e615f8637544f1d9908f7bin0 -> 56 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/fb8e8dcceaed38fe038d6a8b7aaa45841f0897eabin0 -> 25 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/fb95bb125f3d370e97278aaff87dc7650b6c89a5bin0 -> 65 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/fdd02b03ce9fdcb1ad0677308ede92f1f6b80951bin0 -> 57 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/fe58d5023d23b8960c8d7018a96dcb7659fe809dbin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/fe7afd1d477b946c02e7609d4c5fddf2df911c49bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/fe8488887062749e9c47274436d3b4a3586c8585bin0 -> 67 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ff4efc7e62cbf78e3bb877eee51602eb0d335a61bin0 -> 30 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/theme.cc65
-rw-r--r--chromium/third_party/blink/renderer/platform/theme.h159
-rw-r--r--chromium/third_party/blink/renderer/platform/theme_types.h153
-rw-r--r--chromium/third_party/blink/renderer/platform/threading/DEPS6
-rw-r--r--chromium/third_party/blink/renderer/platform/threading/background_task_runner.cc20
-rw-r--r--chromium/third_party/blink/renderer/platform/threading/background_task_runner.h23
-rw-r--r--chromium/third_party/blink/renderer/platform/threading/background_task_runner_test.cc34
-rw-r--r--chromium/third_party/blink/renderer/platform/time_clamper.cc54
-rw-r--r--chromium/third_party/blink/renderer/platform/time_clamper.h42
-rw-r--r--chromium/third_party/blink/renderer/platform/time_clamper_test.cc99
-rw-r--r--chromium/third_party/blink/renderer/platform/timer.cc177
-rw-r--r--chromium/third_party/blink/renderer/platform/timer.h188
-rw-r--r--chromium/third_party/blink/renderer/platform/timer_perf_test.cc117
-rw-r--r--chromium/third_party/blink/renderer/platform/timer_test.cc806
-rw-r--r--chromium/third_party/blink/renderer/platform/transforms/affine_transform.cc411
-rw-r--r--chromium/third_party/blink/renderer/platform/transforms/affine_transform.h203
-rw-r--r--chromium/third_party/blink/renderer/platform/transforms/affine_transform_test.cc31
-rw-r--r--chromium/third_party/blink/renderer/platform/transforms/identity_transform_operation.h66
-rw-r--r--chromium/third_party/blink/renderer/platform/transforms/interpolated_transform_operation.cc78
-rw-r--r--chromium/third_party/blink/renderer/platform/transforms/interpolated_transform_operation.h86
-rw-r--r--chromium/third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.cc64
-rw-r--r--chromium/third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.h81
-rw-r--r--chromium/third_party/blink/renderer/platform/transforms/matrix_transform_operation.cc58
-rw-r--r--chromium/third_party/blink/renderer/platform/transforms/matrix_transform_operation.h109
-rw-r--r--chromium/third_party/blink/renderer/platform/transforms/perspective_transform_operation.cc75
-rw-r--r--chromium/third_party/blink/renderer/platform/transforms/perspective_transform_operation.h80
-rw-r--r--chromium/third_party/blink/renderer/platform/transforms/rotate_transform_operation.cc138
-rw-r--r--chromium/third_party/blink/renderer/platform/transforms/rotate_transform_operation.h142
-rw-r--r--chromium/third_party/blink/renderer/platform/transforms/rotation.cc131
-rw-r--r--chromium/third_party/blink/renderer/platform/transforms/rotation.h45
-rw-r--r--chromium/third_party/blink/renderer/platform/transforms/rotation_test.cc67
-rw-r--r--chromium/third_party/blink/renderer/platform/transforms/scale_transform_operation.cc57
-rw-r--r--chromium/third_party/blink/renderer/platform/transforms/scale_transform_operation.h102
-rw-r--r--chromium/third_party/blink/renderer/platform/transforms/skew_transform_operation.cc55
-rw-r--r--chromium/third_party/blink/renderer/platform/transforms/skew_transform_operation.h83
-rw-r--r--chromium/third_party/blink/renderer/platform/transforms/transform_operation.h116
-rw-r--r--chromium/third_party/blink/renderer/platform/transforms/transform_operations.cc435
-rw-r--r--chromium/third_party/blink/renderer/platform/transforms/transform_operations.h120
-rw-r--r--chromium/third_party/blink/renderer/platform/transforms/transform_operations_test.cc548
-rw-r--r--chromium/third_party/blink/renderer/platform/transforms/transformation_matrix.cc1924
-rw-r--r--chromium/third_party/blink/renderer/platform/transforms/transformation_matrix.h526
-rw-r--r--chromium/third_party/blink/renderer/platform/transforms/transformation_matrix_test.cc288
-rw-r--r--chromium/third_party/blink/renderer/platform/transforms/translate_transform_operation.cc65
-rw-r--r--chromium/third_party/blink/renderer/platform/transforms/translate_transform_operation.h122
-rw-r--r--chromium/third_party/blink/renderer/platform/ukm_time_aggregator.cc116
-rw-r--r--chromium/third_party/blink/renderer/platform/ukm_time_aggregator.h166
-rw-r--r--chromium/third_party/blink/renderer/platform/ukm_time_aggregator_test.cc149
-rw-r--r--chromium/third_party/blink/renderer/platform/uuid.cc24
-rw-r--r--chromium/third_party/blink/renderer/platform/uuid.h59
-rw-r--r--chromium/third_party/blink/renderer/platform/uuid_test.cc76
-rw-r--r--chromium/third_party/blink/renderer/platform/waitable_event.cc49
-rw-r--r--chromium/third_party/blink/renderer/platform/waitable_event.h90
-rw-r--r--chromium/third_party/blink/renderer/platform/web_gesture_event.cc158
-rw-r--r--chromium/third_party/blink/renderer/platform/web_icon_sizes_fuzzer.cc25
-rw-r--r--chromium/third_party/blink/renderer/platform/web_icon_sizes_parser.cc111
-rw-r--r--chromium/third_party/blink/renderer/platform/web_icon_sizes_parser_test.cc100
-rw-r--r--chromium/third_party/blink/renderer/platform/web_mouse_event.cc74
-rw-r--r--chromium/third_party/blink/renderer/platform/web_mouse_wheel_event.cc25
-rw-r--r--chromium/third_party/blink/renderer/platform/web_pointer_event.cc102
-rw-r--r--chromium/third_party/blink/renderer/platform/web_screen_info_test.cc57
-rw-r--r--chromium/third_party/blink/renderer/platform/web_task_runner.cc162
-rw-r--r--chromium/third_party/blink/renderer/platform/web_task_runner.h84
-rw-r--r--chromium/third_party/blink/renderer/platform/web_task_runner_test.cc159
-rw-r--r--chromium/third_party/blink/renderer/platform/web_text_input_info.cc44
-rw-r--r--chromium/third_party/blink/renderer/platform/web_thread.cc46
-rw-r--r--chromium/third_party/blink/renderer/platform/web_thread_supporting_gc.cc78
-rw-r--r--chromium/third_party/blink/renderer/platform/web_thread_supporting_gc.h93
-rw-r--r--chromium/third_party/blink/renderer/platform/web_thread_type.cc53
-rw-r--r--chromium/third_party/blink/renderer/platform/web_touch_event.cc39
-rw-r--r--chromium/third_party/blink/renderer/platform/web_vector_test.cc195
-rw-r--r--chromium/third_party/blink/renderer/platform/weborigin/DEPS5
-rw-r--r--chromium/third_party/blink/renderer/platform/weborigin/OWNERS5
-rw-r--r--chromium/third_party/blink/renderer/platform/weborigin/known_ports.cc83
-rw-r--r--chromium/third_party/blink/renderer/platform/weborigin/known_ports.h51
-rw-r--r--chromium/third_party/blink/renderer/platform/weborigin/known_ports_test.cc90
-rw-r--r--chromium/third_party/blink/renderer/platform/weborigin/kurl.cc886
-rw-r--r--chromium/third_party/blink/renderer/platform/weborigin/kurl.h330
-rw-r--r--chromium/third_party/blink/renderer/platform/weborigin/kurl_hash.h70
-rw-r--r--chromium/third_party/blink/renderer/platform/weborigin/kurl_test.cc841
-rw-r--r--chromium/third_party/blink/renderer/platform/weborigin/origin_access_entry.cc166
-rw-r--r--chromium/third_party/blink/renderer/platform/weborigin/origin_access_entry.h105
-rw-r--r--chromium/third_party/blink/renderer/platform/weborigin/origin_access_entry_test.cc380
-rw-r--r--chromium/third_party/blink/renderer/platform/weborigin/referrer.h56
-rw-r--r--chromium/third_party/blink/renderer/platform/weborigin/referrer_policy.h62
-rw-r--r--chromium/third_party/blink/renderer/platform/weborigin/reporting_service_proxy_ptr_holder.h81
-rw-r--r--chromium/third_party/blink/renderer/platform/weborigin/scheme_registry.cc400
-rw-r--r--chromium/third_party/blink/renderer/platform/weborigin/scheme_registry.h162
-rw-r--r--chromium/third_party/blink/renderer/platform/weborigin/scheme_registry_test.cc72
-rw-r--r--chromium/third_party/blink/renderer/platform/weborigin/security_origin.cc528
-rw-r--r--chromium/third_party/blink/renderer/platform/weborigin/security_origin.h268
-rw-r--r--chromium/third_party/blink/renderer/platform/weborigin/security_origin_hash.h97
-rw-r--r--chromium/third_party/blink/renderer/platform/weborigin/security_origin_test.cc490
-rw-r--r--chromium/third_party/blink/renderer/platform/weborigin/security_policy.cc443
-rw-r--r--chromium/third_party/blink/renderer/platform/weborigin/security_policy.h110
-rw-r--r--chromium/third_party/blink/renderer/platform/weborigin/security_policy_test.cc428
-rw-r--r--chromium/third_party/blink/renderer/platform/weborigin/security_violation_reporting_policy.h17
-rw-r--r--chromium/third_party/blink/renderer/platform/weborigin/url_security_origin_map.h54
-rw-r--r--chromium/third_party/blink/renderer/platform/win/precompile_platform.cc7
-rw-r--r--chromium/third_party/blink/renderer/platform/windows_keyboard_codes.h334
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/Allocator.md186
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/BUILD.gn369
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/CONTRIBUTORS.pthreads-win32137
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/DEPS36
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/OWNERS10
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/README.md122
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/ScopedLogger.md102
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/address_sanitizer.h40
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/alignment.h48
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/allocator.h155
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/allocator/Allocator.md43
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/allocator/partition_allocator.cc37
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/allocator/partition_allocator.h152
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/allocator/partitions.cc246
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/allocator/partitions.h174
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/ascii_ctype.cc57
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/ascii_ctype.h184
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/ascii_ctype_test.cc18
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/assertions.cc100
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/assertions.h162
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/assertions_test.cc38
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/atomics.h402
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/auto_reset.h42
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/bit_vector.cc114
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/bit_vector.h223
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/bloom_filter.h149
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/byte_order.h74
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/byte_swap.h77
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/checked_numeric.h55
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/compiler.h81
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/conditional_destructor.h28
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/construct_traits.h40
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/container_annotations.h47
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/cpu.h58
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/cryptographically_random_number.cc37
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/cryptographically_random_number.h44
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/date_math.cc846
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/date_math.h122
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/deque.h719
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/deque_test.cc548
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/doubly_linked_list.h207
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/dtoa.cc147
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/dtoa.h86
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/dtoa/COPYING26
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/dtoa/LICENSE26
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/dtoa/README11
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/dtoa/bignum-dtoa.cc680
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/dtoa/bignum-dtoa.h90
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/dtoa/bignum.cc770
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/dtoa/bignum.h146
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/dtoa/cached-powers.cc204
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/dtoa/cached-powers.h66
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/dtoa/diy-fp.cc61
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/dtoa/diy-fp.h121
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/dtoa/double-conversion.cc596
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/dtoa/double-conversion.h377
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/dtoa/double.h249
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/dtoa/fast-dtoa.cc743
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/dtoa/fast-dtoa.h88
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/dtoa/fixed-dtoa.cc410
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/dtoa/fixed-dtoa.h63
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/dtoa/strtod.cc447
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/dtoa/strtod.h45
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/dtoa/utils.h305
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/dtoa_test.cc42
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/dynamic_annotations.cc62
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/dynamic_annotations.h113
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/experimental/README.md17
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/experimental/container_type_operations.h620
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/experimental/container_type_operations_test.cc83
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/forward.h88
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/functional.h423
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/functional_test.cc175
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/get_ptr.h46
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/hash_counted_set.h190
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/hash_functions.h286
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/hash_iterators.h282
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/hash_map.h764
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/hash_map_test.cc602
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/hash_set.h331
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/hash_set_test.cc375
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/hash_table.cc102
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/hash_table.h2266
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/hash_table_deleted_value_type.h40
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/hash_traits.h470
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/hex_number.h111
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/leak_annotations.h142
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/linked_hash_set.h945
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/list_hash_set.h1139
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/list_hash_set_test.cc734
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/locker.h54
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/math_extras.h442
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/math_extras_test.cc262
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/noncopyable.h29
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/not_found.h37
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/optional.h37
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/optional_test.cc58
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/process_metrics.h20
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/ref_counted.h71
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/ref_ptr_test.cc107
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/ref_vector.h52
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/retain_ptr.h328
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/saturated_arithmetic.h107
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/saturated_arithmetic_arm.h78
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/saturated_arithmetic_test.cc69
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/scoped_logger.cc101
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/scoped_logger.h71
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/scoped_logger_test.cc49
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/size_assertions.h29
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/size_limits.cc91
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/stack_util.cc200
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/stack_util.h48
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/static_constructors.h39
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/std_lib_extras.h307
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/string_extras.h49
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/string_hasher.h232
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/string_hasher_test.cc430
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/terminated_array.h130
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/terminated_array_builder.h84
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/testing/run_all_tests.cc40
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/OWNERS8
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/ascii_fast_path.h185
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/atomic_string.cc142
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/atomic_string.h320
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/atomic_string_cf.cc60
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h75
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/atomic_string_table.cc228
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/atomic_string_table.h66
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/atomic_string_test.cc78
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/base64.cc272
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/base64.h129
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/character_names.h238
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/collator.h78
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/cstring.cc126
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/cstring.h120
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/cstring_test.cc203
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/icu/collator_icu.cc143
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/icu/unicode_icu.h226
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/integer_to_string_conversion.h82
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/integer_to_string_conversion_test.cc18
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/number_parsing_options.h51
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/parsing_utilities.h108
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/string_buffer.h87
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/string_buffer_test.cc41
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/string_builder.cc247
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/string_builder.h306
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/string_builder_test.cc370
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/string_concatenate.cc76
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/string_concatenate.h223
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/string_hash.h180
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/string_impl.cc2005
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/string_impl.h855
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/string_impl_cf.cc166
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/string_impl_mac.mm45
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/string_impl_test.cc161
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/string_mac.mm53
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/string_operators.h204
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/string_statics.cc97
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/string_statics.h47
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/string_to_number.cc320
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/string_to_number.h159
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/string_to_number_test.cc385
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h85
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/string_view.cc108
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/string_view.h284
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/string_view_test.cc464
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/text_codec.cc57
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/text_codec.h119
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/text_codec_ascii_fast_path.h77
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/text_codec_icu.cc740
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/text_codec_icu.h101
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/text_codec_icu_test.cc36
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/text_codec_latin1.cc290
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/text_codec_latin1.h53
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/text_codec_replacement.cc60
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/text_codec_replacement.h37
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/text_codec_replacement_test.cc60
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/text_codec_test.cc67
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/text_codec_user_defined.cc145
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/text_codec_user_defined.h53
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/text_codec_utf16.cc209
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/text_codec_utf16.h58
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/text_codec_utf8.cc502
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/text_codec_utf8.h75
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/text_codec_utf8_test.cc91
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/text_encoding.cc147
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/text_encoding.h91
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/text_encoding_registry.cc297
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/text_encoding_registry.h68
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/text_encoding_test.cc54
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/text_position.cc74
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/text_position.h120
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/unicode.h35
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/utf8.cc476
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/utf8.h93
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/wtf_string.cc803
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/wtf_string.h675
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/wtf_string_test.cc500
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/thread_restriction_verifier.h108
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h72
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/thread_specific.h157
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/threading.h68
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/threading_primitives.h191
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/threading_pthreads.cc273
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/threading_win.cc318
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/time.cc79
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/time.h75
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/tree_node.h212
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/tree_node_test.cc216
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/type_traits.h269
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/type_traits_test.cc276
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer.cc105
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer.h278
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_builder.cc120
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_builder.h103
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_builder_test.cc225
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_contents.cc199
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_contents.h274
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_view.cc99
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_view.h131
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_piece.cc67
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_piece.h57
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/typed_arrays/bigint64_array.h71
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/typed_arrays/biguint64_array.h71
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/typed_arrays/float32_array.h101
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/typed_arrays/float64_array.h93
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/typed_arrays/int16_array.h82
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/typed_arrays/int32_array.h81
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/typed_arrays/int8_array.h85
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/typed_arrays/integral_typed_array_base.h63
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/typed_arrays/typed_array_base.h126
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/typed_arrays/uint16_array.h86
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/typed_arrays/uint32_array.h85
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/typed_arrays/uint8_array.h85
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/typed_arrays/uint8_clamped_array.h96
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/vector.h1999
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/vector_test.cc676
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/vector_traits.h204
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/wtf.cc80
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/wtf.h54
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/wtf_export.h52
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/wtf_test_helper.h250
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/wtf_thread_data.cc64
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/wtf_thread_data.h92
2945 files changed, 393914 insertions, 0 deletions
diff --git a/chromium/third_party/blink/renderer/platform/BUILD.gn b/chromium/third_party/blink/renderer/platform/BUILD.gn
new file mode 100644
index 00000000000..bc9fe2cc200
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/BUILD.gn
@@ -0,0 +1,2275 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/buildflag_header.gni")
+import("//build/compiled_action.gni")
+import("//build/config/features.gni")
+import("//build/config/jumbo.gni")
+import("//build/config/ui.gni")
+import("//testing/libfuzzer/fuzzer_test.gni")
+import("//testing/test.gni")
+import("//third_party/blink/public/public_features.gni")
+import("//third_party/blink/renderer/build/scripts/scripts.gni")
+import("//third_party/blink/renderer/config.gni")
+import("//third_party/blink/renderer/platform/platform_generated.gni")
+
+# Most targets in this file are private actions so use that as the default.
+visibility = [ ":*" ]
+
+blink_platform_avx_files = [
+ "audio/cpu/x86/vector_math_avx.cc",
+ "audio/cpu/x86/vector_math_avx.h",
+]
+
+blink_platform_neon_files = [
+ "audio/cpu/arm/vector_math_neon.h",
+ "graphics/cpu/arm/webgl_image_conversion_neon.h",
+]
+
+blink_platform_msa_files = [
+ "audio/cpu/mips/vector_math_msa.h",
+ "graphics/cpu/mips/webgl_image_conversion_msa.h",
+]
+
+blink_platform_sse_files = [
+ "audio/cpu/x86/vector_math_sse.h",
+ "graphics/cpu/x86/webgl_image_conversion_sse.h",
+]
+
+make_names("font_family_names") {
+ in_files = [ "fonts/font_family_names.json5" ]
+ output_dir = blink_platform_output_dir
+}
+
+action("runtime_enabled_features") {
+ script = "../build/scripts/make_runtime_features.py"
+
+ runtime_enabled_features_json5 = "runtime_enabled_features.json5"
+ inputs = scripts_for_json5_files + [
+ runtime_enabled_features_json5,
+ "../build/scripts/templates/runtime_enabled_features.cc.tmpl",
+ "../build/scripts/templates/runtime_enabled_features.h.tmpl",
+ "../build/scripts/templates/runtime_enabled_features_test_helpers.h.tmpl",
+ ]
+
+ outputs = [
+ "$blink_platform_output_dir/runtime_enabled_features.cc",
+ "$blink_platform_output_dir/runtime_enabled_features.h",
+ "$blink_platform_output_dir/testing/runtime_enabled_features_test_helpers.h",
+ ]
+
+ args = [
+ rebase_path(runtime_enabled_features_json5, root_build_dir),
+ "--output_dir",
+ rebase_path(blink_platform_output_dir, root_build_dir),
+ ]
+}
+
+action("color_data") {
+ script = "../build/scripts/gperf.py"
+
+ color_data_gperf = "color_data.gperf"
+ inputs = [
+ color_data_gperf,
+ ]
+
+ output_file = "$blink_platform_output_dir/color_data.cc"
+ outputs = [
+ output_file,
+ ]
+
+ args = []
+ if (is_mac && !use_system_xcode) {
+ args += [
+ "--developer_dir",
+ hermetic_xcode_path,
+ ]
+ }
+
+ args += [
+ gperf_exe,
+ "--key-positions=*",
+ "-D",
+ "-s",
+ "2",
+ rebase_path(color_data_gperf, root_build_dir),
+ "--output-file=" + rebase_path(output_file, root_build_dir),
+ ]
+}
+
+compiled_action("character_data") {
+ tool = ":character_data_generator"
+ outputs = [
+ "$blink_platform_output_dir/character_property_data.cc",
+ ]
+ args = rebase_path(outputs, root_build_dir)
+}
+
+action("instrumentation_probes") {
+ script = "../build/scripts/make_instrumenting_probes.py"
+
+ input_file = "probe/PlatformProbes.pidl"
+ inputs = [
+ input_file,
+ "probe/PlatformProbes.json5",
+ "../build/scripts/templates/InstrumentingProbesImpl.cpp.tmpl",
+ "../build/scripts/templates/InstrumentingProbesInl.h.tmpl",
+ "../build/scripts/templates/ProbeSink.h.tmpl",
+ ]
+
+ outputs = [
+ "$blink_platform_output_dir/PlatformProbeSink.h",
+ "$blink_platform_output_dir/PlatformProbesImpl.cpp",
+ "$blink_platform_output_dir/PlatformProbesInl.h",
+ ]
+
+ args = [
+ rebase_path(inputs[0], root_build_dir),
+ "--config",
+ rebase_path("probe/PlatformProbes.json5", root_build_dir),
+ "--output_dir",
+ rebase_path(blink_platform_output_dir, root_build_dir),
+ ]
+}
+
+executable("character_data_generator") {
+ sources = [
+ "text/character_property_data_generator.cc",
+ "text/character_property_data_generator.h",
+ ]
+ configs += [ "//third_party/blink/renderer:config" ]
+ deps = [
+ "//build/config:exe_and_shlib_deps",
+
+ # Default manifest on Windows (a no-op elsewhere).
+ "//build/win:default_exe_manifest",
+ "//third_party/icu",
+ ]
+}
+
+# This isn't strictly necessary since we can just add the deps to "platform",
+# but it helps to have the targets match the GYP build.
+group("make_platform_generated") {
+ visibility = [] # Allow re-assignment of list.
+ visibility = [ "//third_party/blink/renderer/*" ]
+ public_deps = [
+ ":character_data",
+ ":color_data",
+ ":font_family_names",
+ ":instrumentation_probes",
+ ":runtime_enabled_features",
+ "//third_party/blink/renderer/platform/network:make_generated",
+ ]
+}
+
+import("//build/config/pch.gni")
+
+config("blink_platform_config") {
+ include_dirs = [
+ #"$angle_path/include",
+ "$root_gen_dir/third_party/blink/renderer",
+ ]
+
+ configs = [
+ "//third_party/blink/renderer:config",
+ "//third_party/blink/renderer:inside_blink",
+
+ # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+ "//build/config/compiler:no_size_t_to_int_warning",
+ ]
+}
+
+# Deps and configs required to include platform headers. Applied to all
+# platform constituent targets and exported to dependents.
+group("blink_platform_public_deps") {
+ visibility = [] # Allow re-assignment of list.
+ visibility = [ "//third_party/blink/renderer/platform/*" ]
+ public_deps = [
+ ":make_platform_generated",
+ ":rcs_count_everything_buildflags",
+ "//base",
+ "//cc",
+ "//cc/animation",
+ "//cc/paint",
+ "//gpu/command_buffer/client:client",
+ "//gpu/command_buffer/client:gles2_interface",
+ "//gpu/command_buffer/common:common",
+ "//media",
+ "//net",
+ "//services/device/public/mojom:generic_sensor_blink",
+ "//services/device/public/mojom:mojom_blink",
+ "//services/network/public/cpp:cpp",
+ "//services/network/public/mojom",
+ "//services/network/public/mojom:mojom_blink",
+ "//services/resource_coordinator/public/cpp:resource_coordinator_cpp",
+ "//services/resource_coordinator/public/mojom:mojom_blink",
+ "//services/service_manager/public/mojom:mojom_blink",
+ "//skia",
+ "//third_party:jpeg",
+ "//third_party/blink/public:blink_headers",
+ "//third_party/blink/public:mojo_bindings_blink",
+ "//third_party/blink/public/common",
+ "//third_party/blink/public/mojom:mojom_platform_blink",
+ "//third_party/blink/renderer/platform/heap:blink_heap_buildflags",
+ "//third_party/blink/renderer/platform/network:make_generated",
+ "//third_party/blink/renderer/platform/wtf",
+ "//third_party/iccjpeg",
+ "//third_party/libpng",
+ "//third_party/libwebp",
+ "//third_party/ots",
+ "//url",
+ "//v8",
+ ]
+ public_configs = [ "//third_party/blink/renderer:features" ]
+}
+
+declare_args() {
+ runtime_call_stats_count_everything = false
+}
+
+buildflag_header("rcs_count_everything_buildflags") {
+ header = "runtime_call_stats_count_everything_buildflags.h"
+
+ header_dir = "third_party/blink/renderer/platform/bindings"
+
+ flags = [ "RCS_COUNT_EVERYTHING=$runtime_call_stats_count_everything" ]
+}
+
+config("blink_platform_implementation") {
+ defines = [ "BLINK_PLATFORM_IMPLEMENTATION=1" ]
+}
+
+config("blink_platform_pch") {
+ if (enable_precompiled_headers) {
+ if (is_win) {
+ # This is a string rather than a file GN knows about. It has to match
+ # exactly what's in the /FI flag below, and what might appear in the
+ # source code in quotes for an #include directive.
+ precompiled_header = rebase_path("precompile_platform.h", root_build_dir)
+
+ # This is a file that GN will compile with the above header. It will be
+ # implicitly added to the sources (potentially multiple times, with one
+ # variant for each language used in the target).
+ precompiled_source =
+ "//third_party/blink/renderer/platform/win/precompile_platform.cc"
+
+ # Force include the header.
+ cflags = [ "/FI$precompiled_header" ]
+ } else if (is_mac) {
+ precompiled_source =
+ "//third_party/blink/renderer/platform/precompile_platform.h"
+ }
+ }
+}
+
+source_set("platform_export") {
+ sources = [
+ "platform_export.h",
+ ]
+
+ visibility = [] # Allow re-assignment of list.
+ visibility = [
+ ":platform",
+ "//mojo/public/cpp/bindings:wtf_support",
+ ]
+}
+
+jumbo_component("platform") {
+ visibility = [] # Allow re-assignment of list.
+ visibility = [
+ "//components/pdf/common:interfaces_blink",
+ "//services/device/public/mojom:mojom_blink",
+ "//third_party/blink/*",
+ "//url/mojom:url_mojom_origin_blink",
+ "//url/mojom:url_mojom_gurl_blink",
+ ]
+ output_name = "blink_platform"
+
+ sources = [
+ "InstanceCountersMemoryDumpProvider.cc",
+ "animation/animation_translation_util.cc",
+ "animation/animation_translation_util.h",
+ "animation/animation_utilities.h",
+ "animation/compositor_animation.cc",
+ "animation/compositor_animation.h",
+ "animation/compositor_animation_client.cc",
+ "animation/compositor_animation_client.h",
+ "animation/compositor_animation_curve.h",
+ "animation/compositor_animation_delegate.h",
+ "animation/compositor_animation_host.cc",
+ "animation/compositor_animation_host.h",
+ "animation/compositor_animation_timeline.cc",
+ "animation/compositor_animation_timeline.h",
+ "animation/compositor_filter_animation_curve.cc",
+ "animation/compositor_filter_animation_curve.h",
+ "animation/compositor_filter_keyframe.cc",
+ "animation/compositor_filter_keyframe.h",
+ "animation/compositor_float_animation_curve.cc",
+ "animation/compositor_float_animation_curve.h",
+ "animation/compositor_float_keyframe.cc",
+ "animation/compositor_float_keyframe.h",
+ "animation/compositor_keyframe.cc",
+ "animation/compositor_keyframe.h",
+ "animation/compositor_keyframe_model.cc",
+ "animation/compositor_keyframe_model.h",
+ "animation/compositor_scroll_offset_animation_curve.cc",
+ "animation/compositor_scroll_offset_animation_curve.h",
+ "animation/compositor_target_property.h",
+ "animation/compositor_transform_animation_curve.cc",
+ "animation/compositor_transform_animation_curve.h",
+ "animation/compositor_transform_keyframe.cc",
+ "animation/compositor_transform_keyframe.h",
+ "animation/compositor_transform_operations.cc",
+ "animation/compositor_transform_operations.h",
+ "animation/timing_function.cc",
+ "animation/timing_function.h",
+ "async_file_system_callbacks.h",
+ "async_method_runner.h",
+ "audio/android/fft_frame_open_maxdl_android.cc",
+ "audio/audio_array.h",
+ "audio/audio_bus.cc",
+ "audio/audio_bus.h",
+ "audio/audio_channel.cc",
+ "audio/audio_channel.h",
+ "audio/audio_delay_dsp_kernel.cc",
+ "audio/audio_delay_dsp_kernel.h",
+ "audio/audio_destination.cc",
+ "audio/audio_destination.h",
+ "audio/audio_destination_consumer.h",
+ "audio/audio_dsp_kernel.cc",
+ "audio/audio_dsp_kernel.h",
+ "audio/audio_dsp_kernel_processor.cc",
+ "audio/audio_dsp_kernel_processor.h",
+ "audio/audio_file_reader.h",
+ "audio/audio_io_callback.h",
+ "audio/audio_processor.cc",
+ "audio/audio_processor.h",
+ "audio/audio_resampler.cc",
+ "audio/audio_resampler.h",
+ "audio/audio_resampler_kernel.cc",
+ "audio/audio_resampler_kernel.h",
+ "audio/audio_source_provider.h",
+ "audio/audio_source_provider_client.h",
+ "audio/audio_utilities.cc",
+ "audio/audio_utilities.h",
+ "audio/biquad.cc",
+ "audio/biquad.h",
+ "audio/cone.cc",
+ "audio/cone.h",
+ "audio/cpu/arm/vector_math_neon.h",
+ "audio/cpu/mips/vector_math_msa.h",
+ "audio/cpu/x86/vector_math_avx.cc",
+ "audio/cpu/x86/vector_math_avx.h",
+ "audio/cpu/x86/vector_math_impl.h",
+ "audio/cpu/x86/vector_math_sse.cc",
+ "audio/cpu/x86/vector_math_sse.h",
+ "audio/cpu/x86/vector_math_x8_6.h",
+ "audio/denormal_disabler.h",
+ "audio/direct_convolver.cc",
+ "audio/direct_convolver.h",
+ "audio/distance_effect.cc",
+ "audio/distance_effect.h",
+ "audio/down_sampler.cc",
+ "audio/down_sampler.h",
+ "audio/dynamics_compressor.cc",
+ "audio/dynamics_compressor.h",
+ "audio/dynamics_compressor_kernel.cc",
+ "audio/dynamics_compressor_kernel.h",
+ "audio/equal_power_panner.cc",
+ "audio/equal_power_panner.h",
+ "audio/ffmpeg/fft_frame_ffmpeg.cc",
+ "audio/fft_convolver.cc",
+ "audio/fft_convolver.h",
+ "audio/fft_frame.cc",
+ "audio/fft_frame.h",
+ "audio/fft_frame_stub.cc",
+ "audio/hrtf_database.cc",
+ "audio/hrtf_database.h",
+ "audio/hrtf_database_loader.cc",
+ "audio/hrtf_database_loader.h",
+ "audio/hrtf_elevation.cc",
+ "audio/hrtf_elevation.h",
+ "audio/hrtf_kernel.cc",
+ "audio/hrtf_kernel.h",
+ "audio/hrtf_panner.cc",
+ "audio/hrtf_panner.h",
+ "audio/iir_filter.cc",
+ "audio/iir_filter.h",
+ "audio/mac/fft_frame_mac.cc",
+ "audio/mac/vector_math_mac.h",
+ "audio/multi_channel_resampler.cc",
+ "audio/multi_channel_resampler.h",
+ "audio/panner.cc",
+ "audio/panner.h",
+ "audio/push_pull_fifo.cc",
+ "audio/push_pull_fifo.h",
+ "audio/reverb.cc",
+ "audio/reverb.h",
+ "audio/reverb_accumulation_buffer.cc",
+ "audio/reverb_accumulation_buffer.h",
+ "audio/reverb_convolver.cc",
+ "audio/reverb_convolver.h",
+ "audio/reverb_convolver_stage.cc",
+ "audio/reverb_convolver_stage.h",
+ "audio/reverb_input_buffer.cc",
+ "audio/reverb_input_buffer.h",
+ "audio/sinc_resampler.cc",
+ "audio/sinc_resampler.h",
+ "audio/stereo_panner.cc",
+ "audio/stereo_panner.h",
+ "audio/up_sampler.cc",
+ "audio/up_sampler.h",
+ "audio/vector_math.cc",
+ "audio/vector_math.h",
+ "audio/vector_math_scalar.h",
+ "bindings/active_script_wrappable_base.cc",
+ "bindings/active_script_wrappable_base.h",
+ "bindings/callback_function_base.cc",
+ "bindings/callback_function_base.h",
+ "bindings/callback_interface_base.cc",
+ "bindings/callback_interface_base.h",
+ "bindings/dom_data_store.h",
+ "bindings/dom_wrapper_map.h",
+ "bindings/dom_wrapper_world.cc",
+ "bindings/dom_wrapper_world.h",
+ "bindings/microtask.cc",
+ "bindings/microtask.h",
+ "bindings/origin_trial_features.cc",
+ "bindings/origin_trial_features.h",
+ "bindings/runtime_call_stats.cc",
+ "bindings/runtime_call_stats.h",
+ "bindings/scoped_persistent.h",
+ "bindings/script_forbidden_scope.cc",
+ "bindings/script_forbidden_scope.h",
+ "bindings/script_promise_properties.h",
+ "bindings/script_state.cc",
+ "bindings/script_state.h",
+ "bindings/script_wrappable.cc",
+ "bindings/script_wrappable.h",
+ "bindings/script_wrappable_marking_visitor.cc",
+ "bindings/script_wrappable_marking_visitor.h",
+ "bindings/script_wrappable_visitor.cc",
+ "bindings/script_wrappable_visitor.h",
+ "bindings/script_wrappable_visitor_verifier.h",
+ "bindings/shared_persistent.h",
+ "bindings/string_resource.cc",
+ "bindings/string_resource.h",
+ "bindings/to_v8.h",
+ "bindings/trace_wrapper_base.h",
+ "bindings/trace_wrapper_member.h",
+ "bindings/trace_wrapper_v8_reference.h",
+ "bindings/trace_wrapper_v8_string.cc",
+ "bindings/trace_wrapper_v8_string.h",
+ "bindings/v0_custom_element_binding.cc",
+ "bindings/v0_custom_element_binding.h",
+ "bindings/v8_binding.cc",
+ "bindings/v8_binding.h",
+ "bindings/v8_binding_macros.h",
+ "bindings/v8_dom_activity_logger.cc",
+ "bindings/v8_dom_activity_logger.h",
+ "bindings/v8_dom_wrapper.cc",
+ "bindings/v8_dom_wrapper.h",
+ "bindings/v8_global_value_map.h",
+ "bindings/v8_object_constructor.cc",
+ "bindings/v8_object_constructor.h",
+ "bindings/v8_per_context_data.cc",
+ "bindings/v8_per_context_data.h",
+ "bindings/v8_per_isolate_data.cc",
+ "bindings/v8_per_isolate_data.h",
+ "bindings/v8_private_property.cc",
+ "bindings/v8_private_property.h",
+ "bindings/v8_throw_exception.cc",
+ "bindings/v8_throw_exception.h",
+ "bindings/v8_value_cache.cc",
+ "bindings/v8_value_cache.h",
+ "bindings/wrapper_creation_security_check.cc",
+ "bindings/wrapper_creation_security_check.h",
+ "bindings/wrapper_type_info.cc",
+ "bindings/wrapper_type_info.h",
+ "calculation_value.h",
+ "clipboard/clipboard_mime_types.cc",
+ "clipboard/clipboard_mime_types.h",
+ "clipboard/clipboard_utilities.cc",
+ "clipboard/clipboard_utilities.h",
+ "content_decryption_module_result.h",
+ "content_setting_callbacks.cc",
+ "content_setting_callbacks.h",
+ "context_menu.cc",
+ "context_menu.h",
+ "context_menu_item.cc",
+ "context_menu_item.h",
+ "cpu/mips/common_macros_msa.h",
+ "cross_origin_attribute_value.h",
+ "cross_thread_copier.cc",
+ "cross_thread_copier.h",
+ "cross_thread_functional.h",
+ "crypto.cc",
+ "crypto.h",
+ "crypto_result.h",
+ "cursor.cc",
+ "cursor.h",
+ "data_resource_helper.cc",
+ "data_resource_helper.h",
+ "date_components.cc",
+ "date_components.h",
+ "decimal.cc",
+ "decimal.h",
+ "drag_image.cc",
+ "drag_image.h",
+ "event_dispatch_forbidden_scope.cc",
+ "event_dispatch_forbidden_scope.h",
+ "exported/file_path_conversion.cc",
+ "exported/interface_registry.cc",
+ "exported/platform.cc",
+ "exported/service_registry.cc",
+ "exported/url_conversion.cc",
+ "exported/web_active_gesture_animation.cc",
+ "exported/web_active_gesture_animation.h",
+ "exported/web_audio_bus.cc",
+ "exported/web_audio_device.cc",
+ "exported/web_blob_info.cc",
+ "exported/web_cache.cc",
+ "exported/web_canvas_capture_handler.cc",
+ "exported/web_clipboard_impl.cc",
+ "exported/web_clipboard_impl.h",
+ "exported/web_coalesced_input_event.cc",
+ "exported/web_content_decryption_module.cc",
+ "exported/web_content_decryption_module_access.cc",
+ "exported/web_content_decryption_module_result.cc",
+ "exported/web_content_decryption_module_session.cc",
+ "exported/web_content_setting_callbacks.cc",
+ "exported/web_cors.cc",
+ "exported/web_crypto_algorithm.cc",
+ "exported/web_crypto_key.cc",
+ "exported/web_crypto_key_algorithm.cc",
+ "exported/web_crypto_result.cc",
+ "exported/web_cursor_info.cc",
+ "exported/web_data.cc",
+ "exported/web_data_consumer_handle.cc",
+ "exported/web_drag_data.cc",
+ "exported/web_encrypted_media_client.cc",
+ "exported/web_encrypted_media_key_information.cc",
+ "exported/web_encrypted_media_request.cc",
+ "exported/web_file_system_callbacks.cc",
+ "exported/web_font.cc",
+ "exported/web_font_description.cc",
+ "exported/web_http_body.cc",
+ "exported/web_http_header_map.cc",
+ "exported/web_http_load_info.cc",
+ "exported/web_image.cc",
+ "exported/web_image_generator.cc",
+ "exported/web_input_event.cc",
+ "exported/web_media_constraints.cc",
+ "exported/web_media_player_client.cc",
+ "exported/web_media_player_encrypted_media_client.cc",
+ "exported/web_media_player_source.cc",
+ "exported/web_media_recorder_handler.cc",
+ "exported/web_media_stream.cc",
+ "exported/web_media_stream_source.cc",
+ "exported/web_media_stream_track.cc",
+ "exported/web_memory_coordinator.cc",
+ "exported/web_mixed_content.cc",
+ "exported/web_mock_clipboard.cc",
+ "exported/web_network_state_notifier.cc",
+ "exported/web_prerender.cc",
+ "exported/web_prerendering_support.cc",
+ "exported/web_presentation_receiver.cc",
+ "exported/web_rtc_answer_options.cc",
+ "exported/web_rtc_offer_options.cc",
+ "exported/web_rtc_peer_connection_handler_client.cc",
+ "exported/web_rtc_rtp_contributing_source.cc",
+ "exported/web_rtc_rtp_receiver.cc",
+ "exported/web_rtc_rtp_sender.cc",
+ "exported/web_rtc_session_description.cc",
+ "exported/web_rtc_session_description_request.cc",
+ "exported/web_rtc_stats.cc",
+ "exported/web_rtc_stats_request.cc",
+ "exported/web_rtc_stats_response.cc",
+ "exported/web_rtc_void_request.cc",
+ "exported/web_runtime_features.cc",
+ "exported/web_scrollbar_impl.cc",
+ "exported/web_scrollbar_impl.h",
+ "exported/web_scrollbar_theme_client_impl.cc",
+ "exported/web_scrollbar_theme_client_impl.h",
+ "exported/web_scrollbar_theme_geometry_native.cc",
+ "exported/web_scrollbar_theme_geometry_native.h",
+ "exported/web_scrollbar_theme_painter.cc",
+ "exported/web_security_origin.cc",
+ "exported/web_service_worker_installed_scripts_manager.cc",
+ "exported/web_service_worker_request.cc",
+ "exported/web_service_worker_response.cc",
+ "exported/web_service_worker_stream_handle.cc",
+ "exported/web_speech_synthesis_utterance.cc",
+ "exported/web_speech_synthesis_voice.cc",
+ "exported/web_speech_synthesizer_client_impl.cc",
+ "exported/web_speech_synthesizer_client_impl.h",
+ "exported/web_string.cc",
+ "exported/web_surface_layer_bridge.cc",
+ "exported/web_text_run.cc",
+ "exported/web_thread_safe_data.cc",
+ "exported/web_url.cc",
+ "exported/web_url_error.cc",
+ "exported/web_url_load_timing.cc",
+ "exported/web_url_loader_client.cc",
+ "exported/web_url_loader_test_delegate.cc",
+ "exported/web_url_request.cc",
+ "exported/web_url_response.cc",
+ "exported/web_video_frame_submitter.cc",
+ "exported/wrapped_resource_request.h",
+ "exported/wrapped_resource_response.h",
+ "feature_policy/feature_policy.cc",
+ "feature_policy/feature_policy.h",
+ "file_metadata.cc",
+ "file_metadata.h",
+ "file_system_type.h",
+ "fonts/accept_languages_resolver.cc",
+ "fonts/accept_languages_resolver.h",
+ "fonts/alternate_font_family.h",
+ "fonts/android/font_cache_android.cc",
+ "fonts/bitmap_glyphs_blacklist.cc",
+ "fonts/bitmap_glyphs_blacklist.h",
+ "fonts/canvas_rotation_in_vertical.h",
+ "fonts/character_range.h",
+ "fonts/custom_font_data.h",
+ "fonts/fallback_list_composite_key.h",
+ "fonts/font.cc",
+ "fonts/font.h",
+ "fonts/font_baseline.h",
+ "fonts/font_cache.cc",
+ "fonts/font_cache.h",
+ "fonts/font_cache_client.h",
+ "fonts/font_cache_key.h",
+ "fonts/font_cache_memory_dump_provider.cc",
+ "fonts/font_cache_memory_dump_provider.h",
+ "fonts/font_custom_platform_data.cc",
+ "fonts/font_custom_platform_data.h",
+ "fonts/font_data.cc",
+ "fonts/font_data.h",
+ "fonts/font_data_cache.cc",
+ "fonts/font_data_cache.h",
+ "fonts/font_data_for_range_set.cc",
+ "fonts/font_data_for_range_set.h",
+ "fonts/font_description.cc",
+ "fonts/font_description.h",
+ "fonts/font_face_creation_params.h",
+ "fonts/font_fallback_iterator.cc",
+ "fonts/font_fallback_iterator.h",
+ "fonts/font_fallback_list.cc",
+ "fonts/font_fallback_list.h",
+ "fonts/font_fallback_priority.cc",
+ "fonts/font_fallback_priority.h",
+ "fonts/font_family.cc",
+ "fonts/font_family.h",
+ "fonts/font_global_context.cc",
+ "fonts/font_global_context.h",
+ "fonts/font_metrics.cc",
+ "fonts/font_metrics.h",
+ "fonts/font_orientation.cc",
+ "fonts/font_orientation.h",
+ "fonts/font_platform_data.cc",
+ "fonts/font_platform_data.h",
+ "fonts/font_selection_algorithm.cc",
+ "fonts/font_selection_algorithm.h",
+ "fonts/font_selection_types.cc",
+ "fonts/font_selection_types.h",
+ "fonts/font_selector.cc",
+ "fonts/font_selector.h",
+ "fonts/font_selector_client.h",
+ "fonts/font_smoothing_mode.cc",
+ "fonts/font_smoothing_mode.h",
+ "fonts/font_variant_east_asian.cc",
+ "fonts/font_variant_east_asian.h",
+ "fonts/font_variant_numeric.cc",
+ "fonts/font_variant_numeric.h",
+ "fonts/font_vertical_position_type.h",
+ "fonts/font_width_variant.cc",
+ "fonts/font_width_variant.h",
+ "fonts/generic_font_family_settings.cc",
+ "fonts/generic_font_family_settings.h",
+ "fonts/glyph_metrics_map.h",
+ "fonts/linux/font_cache_linux.cc",
+ "fonts/mac/core_text_variations_support.cc",
+ "fonts/mac/core_text_variations_support.h",
+ "fonts/mac/font_cache_mac.mm",
+ "fonts/mac/font_family_matcher_mac.h",
+ "fonts/mac/font_family_matcher_mac.mm",
+ "fonts/mac/font_platform_data_mac.mm",
+ "fonts/ng_text_fragment_paint_info.h",
+ "fonts/opentype/font_format_check.cc",
+ "fonts/opentype/font_format_check.h",
+ "fonts/opentype/font_settings.cc",
+ "fonts/opentype/font_settings.h",
+ "fonts/opentype/open_type_caps_support.cc",
+ "fonts/opentype/open_type_caps_support.h",
+ "fonts/opentype/open_type_caps_support_mpl.cc",
+ "fonts/opentype/open_type_types.h",
+ "fonts/opentype/open_type_vertical_data.cc",
+ "fonts/opentype/open_type_vertical_data.h",
+ "fonts/orientation_iterator.cc",
+ "fonts/orientation_iterator.h",
+ "fonts/script_run_iterator.cc",
+ "fonts/script_run_iterator.h",
+ "fonts/segmented_font_data.cc",
+ "fonts/segmented_font_data.h",
+ "fonts/shaping/caching_word_shape_iterator.cc",
+ "fonts/shaping/caching_word_shape_iterator.h",
+ "fonts/shaping/caching_word_shaper.cc",
+ "fonts/shaping/caching_word_shaper.h",
+ "fonts/shaping/case_mapping_harf_buzz_buffer_filler.cc",
+ "fonts/shaping/case_mapping_harf_buzz_buffer_filler.h",
+ "fonts/shaping/harf_buzz_face.cc",
+ "fonts/shaping/harf_buzz_face.h",
+ "fonts/shaping/harf_buzz_font_cache.h",
+ "fonts/shaping/harf_buzz_shaper.cc",
+ "fonts/shaping/harf_buzz_shaper.h",
+ "fonts/shaping/run_segmenter.cc",
+ "fonts/shaping/run_segmenter.h",
+ "fonts/shaping/shape_cache.cc",
+ "fonts/shaping/shape_cache.h",
+ "fonts/shaping/shape_result.cc",
+ "fonts/shaping/shape_result.h",
+ "fonts/shaping/shape_result_bloberizer.cc",
+ "fonts/shaping/shape_result_bloberizer.h",
+ "fonts/shaping/shape_result_buffer.cc",
+ "fonts/shaping/shape_result_buffer.h",
+ "fonts/shaping/shape_result_inline_headers.h",
+ "fonts/shaping/shape_result_spacing.cc",
+ "fonts/shaping/shape_result_spacing.h",
+ "fonts/shaping/shape_result_test_info.cc",
+ "fonts/shaping/shape_result_test_info.h",
+ "fonts/shaping/shaping_line_breaker.cc",
+ "fonts/shaping/shaping_line_breaker.h",
+ "fonts/simple_font_data.cc",
+ "fonts/simple_font_data.h",
+ "fonts/skia/font_cache_skia.cc",
+ "fonts/skia/skia_text_metrics.cc",
+ "fonts/skia/skia_text_metrics.h",
+ "fonts/small_caps_iterator.cc",
+ "fonts/small_caps_iterator.h",
+ "fonts/symbols_iterator.cc",
+ "fonts/symbols_iterator.h",
+ "fonts/text_rendering_mode.cc",
+ "fonts/text_rendering_mode.h",
+ "fonts/typesetting_features.cc",
+ "fonts/typesetting_features.h",
+ "fonts/unicode_range_set.cc",
+ "fonts/unicode_range_set.h",
+ "fonts/utf16_text_iterator.cc",
+ "fonts/utf16_text_iterator.h",
+ "fonts/vdmx_parser.cc",
+ "fonts/vdmx_parser.h",
+ "fonts/web_font_decoder.cc",
+ "fonts/web_font_decoder.h",
+ "fonts/web_font_render_style.cc",
+ "fonts/web_font_typeface_factory.cc",
+ "fonts/web_font_typeface_factory.h",
+ "fonts/win/font_cache_skia_win.cc",
+ "fonts/win/font_fallback_win.cc",
+ "fonts/win/font_fallback_win.h",
+ "fonts/win/font_platform_data_win.cc",
+ "geometry/cg/float_point_cg.cc",
+ "geometry/cg/float_rect_cg.cc",
+ "geometry/cg/float_size_cg.cc",
+ "geometry/cg/int_point_cg.cc",
+ "geometry/cg/int_rect_cg.cc",
+ "geometry/cg/int_size_cg.cc",
+ "geometry/double_point.cc",
+ "geometry/double_point.h",
+ "geometry/double_rect.cc",
+ "geometry/double_rect.h",
+ "geometry/double_size.cc",
+ "geometry/double_size.h",
+ "geometry/float_box.cc",
+ "geometry/float_box.h",
+ "geometry/float_point.cc",
+ "geometry/float_point.h",
+ "geometry/float_point_3d.cc",
+ "geometry/float_point_3d.h",
+ "geometry/float_polygon.cc",
+ "geometry/float_polygon.h",
+ "geometry/float_quad.cc",
+ "geometry/float_quad.h",
+ "geometry/float_rect.cc",
+ "geometry/float_rect.h",
+ "geometry/float_rect_outsets.cc",
+ "geometry/float_rect_outsets.h",
+ "geometry/float_rounded_rect.cc",
+ "geometry/float_rounded_rect.h",
+ "geometry/float_size.cc",
+ "geometry/float_size.h",
+ "geometry/geometry_as_json.cc",
+ "geometry/geometry_as_json.h",
+ "geometry/int_point.cc",
+ "geometry/int_point.h",
+ "geometry/int_rect.cc",
+ "geometry/int_rect.h",
+ "geometry/int_rect_outsets.h",
+ "geometry/int_size.cc",
+ "geometry/int_size.h",
+ "geometry/layout_point.cc",
+ "geometry/layout_point.h",
+ "geometry/layout_rect.cc",
+ "geometry/layout_rect.h",
+ "geometry/layout_rect_outsets.cc",
+ "geometry/layout_rect_outsets.h",
+ "geometry/layout_size.cc",
+ "geometry/layout_size.h",
+ "geometry/region.cc",
+ "geometry/region.h",
+ "geometry/transform_state.cc",
+ "geometry/transform_state.h",
+ "graphics/accelerated_static_bitmap_image.cc",
+ "graphics/accelerated_static_bitmap_image.h",
+ "graphics/bitmap_image.cc",
+ "graphics/bitmap_image.h",
+ "graphics/bitmap_image_metrics.cc",
+ "graphics/bitmap_image_metrics.h",
+ "graphics/box_reflection.cc",
+ "graphics/box_reflection.h",
+ "graphics/canvas_2d_layer_bridge.cc",
+ "graphics/canvas_2d_layer_bridge.h",
+ "graphics/canvas_color_params.cc",
+ "graphics/canvas_color_params.h",
+ "graphics/canvas_heuristic_parameters.h",
+ "graphics/canvas_metrics.cc",
+ "graphics/canvas_metrics.h",
+ "graphics/canvas_resource.cc",
+ "graphics/canvas_resource.h",
+ "graphics/canvas_resource_host.h",
+ "graphics/canvas_resource_provider.cc",
+ "graphics/canvas_resource_provider.h",
+ "graphics/color.cc",
+ "graphics/color.h",
+ "graphics/color_behavior.cc",
+ "graphics/color_behavior.h",
+ "graphics/color_blend.h",
+ "graphics/color_space_gamut.cc",
+ "graphics/color_space_gamut.h",
+ "graphics/color_space_profile_data.cc",
+ "graphics/color_space_profile_data.h",
+ "graphics/compositing/chunk_to_layer_mapper.cc",
+ "graphics/compositing/chunk_to_layer_mapper.h",
+ "graphics/compositing/composited_layer_raster_invalidator.cc",
+ "graphics/compositing/composited_layer_raster_invalidator.h",
+ "graphics/compositing/content_layer_client_impl.cc",
+ "graphics/compositing/content_layer_client_impl.h",
+ "graphics/compositing/paint_artifact_compositor.cc",
+ "graphics/compositing/paint_artifact_compositor.h",
+ "graphics/compositing/paint_chunks_to_cc_layer.cc",
+ "graphics/compositing/paint_chunks_to_cc_layer.h",
+ "graphics/compositing/property_tree_manager.cc",
+ "graphics/compositing/property_tree_manager.h",
+ "graphics/compositing_reasons.cc",
+ "graphics/compositing_reasons.h",
+ "graphics/compositor_animators_state.h",
+ "graphics/compositor_element_id.cc",
+ "graphics/compositor_element_id.h",
+ "graphics/compositor_filter_operations.cc",
+ "graphics/compositor_filter_operations.h",
+ "graphics/compositor_mutator.h",
+ "graphics/compositor_mutator.h",
+ "graphics/compositor_mutator_client.cc",
+ "graphics/compositor_mutator_client.h",
+ "graphics/compositor_mutator_impl.cc",
+ "graphics/contiguous_container.cc",
+ "graphics/contiguous_container.h",
+ "graphics/cpu/arm/webgl_image_conversion_neon.h",
+ "graphics/cpu/mips/webgl_image_conversion_msa.h",
+ "graphics/cpu/x86/webgl_image_conversion_sse.h",
+ "graphics/crossfade_generated_image.cc",
+ "graphics/crossfade_generated_image.h",
+ "graphics/dash_array.h",
+ "graphics/decoding_image_generator.cc",
+ "graphics/decoding_image_generator.h",
+ "graphics/deferred_image_decoder.cc",
+ "graphics/deferred_image_decoder.h",
+ "graphics/draw_looper_builder.cc",
+ "graphics/draw_looper_builder.h",
+ "graphics/filters/distant_light_source.cc",
+ "graphics/filters/distant_light_source.h",
+ "graphics/filters/fe_blend.cc",
+ "graphics/filters/fe_blend.h",
+ "graphics/filters/fe_box_reflect.cc",
+ "graphics/filters/fe_box_reflect.h",
+ "graphics/filters/fe_color_matrix.cc",
+ "graphics/filters/fe_color_matrix.h",
+ "graphics/filters/fe_component_transfer.cc",
+ "graphics/filters/fe_component_transfer.h",
+ "graphics/filters/fe_composite.cc",
+ "graphics/filters/fe_composite.h",
+ "graphics/filters/fe_convolve_matrix.cc",
+ "graphics/filters/fe_convolve_matrix.h",
+ "graphics/filters/fe_diffuse_lighting.cc",
+ "graphics/filters/fe_diffuse_lighting.h",
+ "graphics/filters/fe_displacement_map.cc",
+ "graphics/filters/fe_displacement_map.h",
+ "graphics/filters/fe_drop_shadow.cc",
+ "graphics/filters/fe_drop_shadow.h",
+ "graphics/filters/fe_flood.cc",
+ "graphics/filters/fe_flood.h",
+ "graphics/filters/fe_gaussian_blur.cc",
+ "graphics/filters/fe_gaussian_blur.h",
+ "graphics/filters/fe_lighting.cc",
+ "graphics/filters/fe_lighting.h",
+ "graphics/filters/fe_merge.cc",
+ "graphics/filters/fe_merge.h",
+ "graphics/filters/fe_morphology.cc",
+ "graphics/filters/fe_morphology.h",
+ "graphics/filters/fe_offset.cc",
+ "graphics/filters/fe_offset.h",
+ "graphics/filters/fe_specular_lighting.cc",
+ "graphics/filters/fe_specular_lighting.h",
+ "graphics/filters/fe_tile.cc",
+ "graphics/filters/fe_tile.h",
+ "graphics/filters/fe_turbulence.cc",
+ "graphics/filters/fe_turbulence.h",
+ "graphics/filters/filter.cc",
+ "graphics/filters/filter.h",
+ "graphics/filters/filter_effect.cc",
+ "graphics/filters/filter_effect.h",
+ "graphics/filters/light_source.cc",
+ "graphics/filters/light_source.h",
+ "graphics/filters/paint_filter_builder.cc",
+ "graphics/filters/paint_filter_builder.h",
+ "graphics/filters/paint_filter_effect.cc",
+ "graphics/filters/paint_filter_effect.h",
+ "graphics/filters/point_light_source.cc",
+ "graphics/filters/point_light_source.h",
+ "graphics/filters/source_alpha.cc",
+ "graphics/filters/source_alpha.h",
+ "graphics/filters/source_graphic.cc",
+ "graphics/filters/source_graphic.h",
+ "graphics/filters/spot_light_source.cc",
+ "graphics/filters/spot_light_source.h",
+ "graphics/first_paint_invalidation_tracking.cc",
+ "graphics/first_paint_invalidation_tracking.h",
+ "graphics/generated_image.cc",
+ "graphics/generated_image.h",
+ "graphics/gpu/drawing_buffer.cc",
+ "graphics/gpu/drawing_buffer.h",
+ "graphics/gpu/extensions_3d_util.cc",
+ "graphics/gpu/extensions_3d_util.h",
+ "graphics/gpu/graphics_context_3d_utils.cc",
+ "graphics/gpu/graphics_context_3d_utils.h",
+ "graphics/gpu/image_layer_bridge.cc",
+ "graphics/gpu/image_layer_bridge.h",
+ "graphics/gpu/shared_context_rate_limiter.cc",
+ "graphics/gpu/shared_context_rate_limiter.h",
+ "graphics/gpu/shared_gpu_context.cc",
+ "graphics/gpu/shared_gpu_context.h",
+ "graphics/gpu/webgl_image_conversion.cc",
+ "graphics/gpu/webgl_image_conversion.h",
+ "graphics/gpu/xr_frame_transport.cc",
+ "graphics/gpu/xr_frame_transport.h",
+ "graphics/gpu/xr_webgl_drawing_buffer.cc",
+ "graphics/gpu/xr_webgl_drawing_buffer.h",
+ "graphics/gpu_memory_buffer_image_copy.cc",
+ "graphics/gpu_memory_buffer_image_copy.h",
+ "graphics/gradient.cc",
+ "graphics/gradient.h",
+ "graphics/gradient_generated_image.cc",
+ "graphics/gradient_generated_image.h",
+ "graphics/graphics_context.cc",
+ "graphics/graphics_context.h",
+ "graphics/graphics_context_state.cc",
+ "graphics/graphics_context_state.h",
+ "graphics/graphics_context_state_saver.h",
+ "graphics/graphics_layer.cc",
+ "graphics/graphics_layer.h",
+ "graphics/graphics_layer_client.h",
+ "graphics/graphics_layer_debug_info.cc",
+ "graphics/graphics_layer_debug_info.h",
+ "graphics/graphics_types.cc",
+ "graphics/graphics_types.h",
+ "graphics/graphics_types_3d.h",
+ "graphics/high_contrast_image_classifier.cc",
+ "graphics/high_contrast_image_classifier.h",
+ "graphics/high_contrast_settings.h",
+ "graphics/highcontrast/highcontrast_classifier.cc",
+ "graphics/highcontrast/highcontrast_classifier.h",
+ "graphics/image.cc",
+ "graphics/image.h",
+ "graphics/image_animation_policy.h",
+ "graphics/image_data_buffer.cc",
+ "graphics/image_data_buffer.h",
+ "graphics/image_decoding_store.cc",
+ "graphics/image_decoding_store.h",
+ "graphics/image_frame_generator.cc",
+ "graphics/image_frame_generator.h",
+ "graphics/image_observer.cc",
+ "graphics/image_observer.h",
+ "graphics/image_orientation.cc",
+ "graphics/image_orientation.h",
+ "graphics/image_pattern.cc",
+ "graphics/image_pattern.h",
+ "graphics/intercepting_canvas.cc",
+ "graphics/intercepting_canvas.h",
+ "graphics/interpolation_space.cc",
+ "graphics/interpolation_space.h",
+ "graphics/link_highlight.h",
+ "graphics/logging_canvas.cc",
+ "graphics/logging_canvas.h",
+ "graphics/mailbox_texture_holder.cc",
+ "graphics/mailbox_texture_holder.h",
+ "graphics/offscreen_canvas_frame_dispatcher.h",
+ "graphics/offscreen_canvas_frame_dispatcher_impl.cc",
+ "graphics/offscreen_canvas_frame_dispatcher_impl.h",
+ "graphics/offscreen_canvas_placeholder.cc",
+ "graphics/offscreen_canvas_placeholder.h",
+ "graphics/offscreen_canvas_resource_provider.cc",
+ "graphics/offscreen_canvas_resource_provider.h",
+ "graphics/paint/clip_display_item.cc",
+ "graphics/paint/clip_display_item.h",
+ "graphics/paint/clip_paint_property_node.cc",
+ "graphics/paint/clip_paint_property_node.h",
+ "graphics/paint/clip_path_display_item.cc",
+ "graphics/paint/clip_path_display_item.h",
+ "graphics/paint/clip_path_recorder.cc",
+ "graphics/paint/clip_path_recorder.h",
+ "graphics/paint/clip_recorder.cc",
+ "graphics/paint/clip_recorder.h",
+ "graphics/paint/compositing_display_item.cc",
+ "graphics/paint/compositing_display_item.h",
+ "graphics/paint/compositing_recorder.cc",
+ "graphics/paint/compositing_recorder.h",
+ "graphics/paint/cull_rect.cc",
+ "graphics/paint/cull_rect.h",
+ "graphics/paint/display_item.cc",
+ "graphics/paint/display_item.h",
+ "graphics/paint/display_item_cache_skipper.h",
+ "graphics/paint/display_item_client.cc",
+ "graphics/paint/display_item_client.h",
+ "graphics/paint/display_item_list.cc",
+ "graphics/paint/display_item_list.h",
+ "graphics/paint/drawing_display_item.cc",
+ "graphics/paint/drawing_display_item.h",
+ "graphics/paint/drawing_recorder.cc",
+ "graphics/paint/drawing_recorder.h",
+ "graphics/paint/effect_paint_property_node.cc",
+ "graphics/paint/effect_paint_property_node.h",
+ "graphics/paint/filter_display_item.cc",
+ "graphics/paint/filter_display_item.h",
+ "graphics/paint/float_clip_display_item.cc",
+ "graphics/paint/float_clip_display_item.h",
+ "graphics/paint/float_clip_rect.h",
+ "graphics/paint/foreign_layer_display_item.cc",
+ "graphics/paint/foreign_layer_display_item.h",
+ "graphics/paint/geometry_mapper.cc",
+ "graphics/paint/geometry_mapper.h",
+ "graphics/paint/geometry_mapper_clip_cache.cc",
+ "graphics/paint/geometry_mapper_clip_cache.h",
+ "graphics/paint/geometry_mapper_transform_cache.cc",
+ "graphics/paint/geometry_mapper_transform_cache.h",
+ "graphics/paint/paint_artifact.cc",
+ "graphics/paint/paint_artifact.h",
+ "graphics/paint/paint_canvas.h",
+ "graphics/paint/paint_chunk.cc",
+ "graphics/paint/paint_chunk.h",
+ "graphics/paint/paint_chunk_properties.cc",
+ "graphics/paint/paint_chunk_properties.h",
+ "graphics/paint/paint_chunk_subset.h",
+ "graphics/paint/paint_chunker.cc",
+ "graphics/paint/paint_chunker.h",
+ "graphics/paint/paint_controller.cc",
+ "graphics/paint/paint_controller.h",
+ "graphics/paint/paint_controller_debug_data.cc",
+ "graphics/paint/paint_filter.h",
+ "graphics/paint/paint_flags.h",
+ "graphics/paint/paint_font.h",
+ "graphics/paint/paint_property_node.cc",
+ "graphics/paint/paint_property_node.h",
+ "graphics/paint/paint_record.h",
+ "graphics/paint/paint_record_builder.cc",
+ "graphics/paint/paint_record_builder.h",
+ "graphics/paint/paint_recorder.h",
+ "graphics/paint/paint_shader.h",
+ "graphics/paint/paint_text_blob.h",
+ "graphics/paint/paint_typeface.h",
+ "graphics/paint/property_tree_state.cc",
+ "graphics/paint/property_tree_state.h",
+ "graphics/paint/raster_invalidation_tracking.cc",
+ "graphics/paint/raster_invalidation_tracking.h",
+ "graphics/paint/ref_counted_property_tree_state.cc",
+ "graphics/paint/ref_counted_property_tree_state.h",
+ "graphics/paint/scoped_display_item_fragment.h",
+ "graphics/paint/scoped_paint_chunk_properties.h",
+ "graphics/paint/scroll_display_item.cc",
+ "graphics/paint/scroll_display_item.h",
+ "graphics/paint/scroll_hit_test_display_item.cc",
+ "graphics/paint/scroll_hit_test_display_item.h",
+ "graphics/paint/scroll_paint_property_node.cc",
+ "graphics/paint/scroll_paint_property_node.h",
+ "graphics/paint/subsequence_recorder.h",
+ "graphics/paint/transform_3d_display_item.cc",
+ "graphics/paint/transform_3d_display_item.h",
+ "graphics/paint/transform_display_item.cc",
+ "graphics/paint/transform_display_item.h",
+ "graphics/paint/transform_paint_property_node.cc",
+ "graphics/paint/transform_paint_property_node.h",
+ "graphics/paint_generated_image.cc",
+ "graphics/paint_generated_image.h",
+ "graphics/paint_invalidation_reason.cc",
+ "graphics/paint_invalidation_reason.h",
+ "graphics/paint_record_pattern.cc",
+ "graphics/paint_record_pattern.h",
+ "graphics/path.cc",
+ "graphics/path.h",
+ "graphics/path_traversal_state.cc",
+ "graphics/path_traversal_state.h",
+ "graphics/pattern.cc",
+ "graphics/pattern.h",
+ "graphics/picture_snapshot.cc",
+ "graphics/picture_snapshot.h",
+ "graphics/placeholder_image.cc",
+ "graphics/placeholder_image.h",
+ "graphics/profiling_canvas.cc",
+ "graphics/profiling_canvas.h",
+ "graphics/replaying_canvas.cc",
+ "graphics/replaying_canvas.h",
+ "graphics/scoped_interpolation_quality.h",
+ "graphics/skia/image_pixel_locker.cc",
+ "graphics/skia/image_pixel_locker.h",
+ "graphics/skia/sk_size_hash.h",
+ "graphics/skia/skia_utils.cc",
+ "graphics/skia/skia_utils.h",
+ "graphics/skia_texture_holder.cc",
+ "graphics/skia_texture_holder.h",
+ "graphics/squashing_disallowed_reasons.cc",
+ "graphics/squashing_disallowed_reasons.h",
+ "graphics/static_bitmap_image.cc",
+ "graphics/static_bitmap_image.h",
+ "graphics/stroke_data.cc",
+ "graphics/stroke_data.h",
+ "graphics/surface_layer_bridge.cc",
+ "graphics/surface_layer_bridge.h",
+ "graphics/texture_holder.h",
+ "graphics/touch_action.h",
+ "graphics/unaccelerated_static_bitmap_image.cc",
+ "graphics/unaccelerated_static_bitmap_image.h",
+ "graphics/video_frame_resource_provider.cc",
+ "graphics/video_frame_resource_provider.h",
+ "graphics/video_frame_submitter.cc",
+ "graphics/video_frame_submitter.h",
+ "graphics/web_graphics_context_3d_provider_wrapper.cc",
+ "graphics/web_graphics_context_3d_provider_wrapper.h",
+ "histogram.cc",
+ "histogram.h",
+ "image-decoders/bmp/bmp_image_decoder.cc",
+ "image-decoders/bmp/bmp_image_decoder.h",
+ "image-decoders/bmp/bmp_image_reader.cc",
+ "image-decoders/bmp/bmp_image_reader.h",
+ "image-decoders/fast_shared_buffer_reader.cc",
+ "image-decoders/fast_shared_buffer_reader.h",
+ "image-decoders/gif/gif_image_decoder.cc",
+ "image-decoders/gif/gif_image_decoder.h",
+ "image-decoders/gif/gif_image_reader.cc",
+ "image-decoders/gif/gif_image_reader.h",
+ "image-decoders/ico/ico_image_decoder.cc",
+ "image-decoders/ico/ico_image_decoder.h",
+ "image-decoders/image_animation.h",
+ "image-decoders/image_decoder.cc",
+ "image-decoders/image_decoder.h",
+ "image-decoders/image_frame.cc",
+ "image-decoders/image_frame.h",
+ "image-decoders/jpeg/jpeg_image_decoder.cc",
+ "image-decoders/jpeg/jpeg_image_decoder.h",
+ "image-decoders/png/png_image_decoder.cc",
+ "image-decoders/png/png_image_decoder.h",
+ "image-decoders/png/png_image_reader.cc",
+ "image-decoders/png/png_image_reader.h",
+ "image-decoders/segment_reader.cc",
+ "image-decoders/segment_reader.h",
+ "image-decoders/webp/webp_image_decoder.cc",
+ "image-decoders/webp/webp_image_decoder.h",
+ "image-encoders/image_encoder.cc",
+ "image-encoders/image_encoder.h",
+ "image-encoders/image_encoder_utils.cc",
+ "image-encoders/image_encoder_utils.h",
+ "instance_counters.cc",
+ "instance_counters.h",
+ "instance_counters_memory_dump_provider.h",
+ "json/json_parser.cc",
+ "json/json_parser.h",
+ "json/json_values.cc",
+ "json/json_values.h",
+ "keyboard_codes.h",
+ "kill_ring.h",
+ "kill_ring_none.cc",
+ "language.cc",
+ "language.h",
+ "layout_locale.cc",
+ "layout_locale.h",
+ "layout_test_support.cc",
+ "layout_test_support.h",
+ "layout_unit.cc",
+ "layout_unit.h",
+ "length.cc",
+ "length.h",
+ "length_box.h",
+ "length_functions.cc",
+ "length_functions.h",
+ "length_point.h",
+ "length_size.h",
+ "lifecycle_notifier.h",
+ "lifecycle_observer.h",
+ "link_hash.cc",
+ "link_hash.h",
+ "long_task_detector.cc",
+ "long_task_detector.h",
+ "mac/block_exceptions.h",
+ "mac/block_exceptions.mm",
+ "mac/color_mac.h",
+ "mac/color_mac.mm",
+ "mac/graphics_context_canvas.h",
+ "mac/graphics_context_canvas.mm",
+ "mac/kill_ring_mac.mm",
+ "mac/local_current_graphics_context.h",
+ "mac/local_current_graphics_context.mm",
+ "mac/ns_scroller_imp_details.h",
+ "mac/scroll_animator_mac.h",
+ "mac/scroll_animator_mac.mm",
+ "mac/theme_mac.h",
+ "mac/theme_mac.mm",
+ "mac/version_util_mac.h",
+ "mac/version_util_mac.mm",
+ "mac/web_core_ns_cell_extras.h",
+ "mac/web_core_ns_cell_extras.mm",
+ "mediastream/media_stream_center.cc",
+ "mediastream/media_stream_center.h",
+ "mediastream/media_stream_component.cc",
+ "mediastream/media_stream_component.h",
+ "mediastream/media_stream_descriptor.cc",
+ "mediastream/media_stream_descriptor.h",
+ "mediastream/media_stream_source.cc",
+ "mediastream/media_stream_source.h",
+ "mediastream/media_stream_web_audio_source.cc",
+ "mediastream/media_stream_web_audio_source.h",
+ "memory_coordinator.cc",
+ "memory_coordinator.h",
+ "mhtml/archive_resource.cc",
+ "mhtml/archive_resource.h",
+ "mhtml/mhtml_archive.cc",
+ "mhtml/mhtml_archive.h",
+ "mhtml/mhtml_parser.cc",
+ "mhtml/mhtml_parser.h",
+ "mojo/big_string_mojom_traits.cc",
+ "mojo/big_string_mojom_traits.h",
+ "mojo/bluetooth_struct_traits.cc",
+ "mojo/bluetooth_struct_traits.h",
+ "mojo/fetch_api_request_struct_traits.cc",
+ "mojo/interface_invalidator.cc",
+ "mojo/interface_invalidator.h",
+ "mojo/mojo_helper.h",
+ "mojo/notification_struct_traits.cc",
+ "mojo/notification_struct_traits.h",
+ "mojo/revocable_binding.h",
+ "mojo/revocable_interface_ptr.h",
+ "mojo/revocable_strong_binding.h",
+ "mojo/string16_mojom_traits.cc",
+ "mojo/string16_mojom_traits.h",
+ "partition_alloc_memory_dump_provider.cc",
+ "partition_alloc_memory_dump_provider.h",
+ "paste_mode.h",
+ "peerconnection/rtc_answer_options_platform.h",
+ "peerconnection/rtc_offer_options_platform.h",
+ "peerconnection/rtc_session_description_request.h",
+ "peerconnection/rtc_stats_request.h",
+ "peerconnection/rtc_stats_response_base.h",
+ "peerconnection/rtc_void_request.h",
+ "platform_chrome_client.h",
+ "platform_frame_view.h",
+ "plugins/plugin_data.cc",
+ "plugins/plugin_data.h",
+ "plugins/plugin_list_builder.cc",
+ "plugins/plugin_list_builder.h",
+ "plugins/plugin_script_forbidden_scope.cc",
+ "plugins/plugin_script_forbidden_scope.h",
+ "pod_arena.h",
+ "pod_free_list_arena.h",
+ "pod_interval.h",
+ "pod_interval_tree.h",
+ "pod_red_black_tree.h",
+ "prerender.cc",
+ "prerender.h",
+ "prerender_client.h",
+ "probe/PlatformTraceEventsAgent.cpp",
+ "probe/PlatformTraceEventsAgent.h",
+ "probe/platform_probes.cc",
+ "probe/platform_probes.h",
+ "resolution_units.h",
+ "scoped_orientation_change_indicator.cc",
+ "scoped_orientation_change_indicator.h",
+ "scroll/main_thread_scrolling_reason.h",
+ "scroll/programmatic_scroll_animator.cc",
+ "scroll/programmatic_scroll_animator.h",
+ "scroll/scroll_alignment.cc",
+ "scroll/scroll_alignment.h",
+ "scroll/scroll_animator.cc",
+ "scroll/scroll_animator.h",
+ "scroll/scroll_animator_base.cc",
+ "scroll/scroll_animator_base.h",
+ "scroll/scroll_animator_compositor_coordinator.cc",
+ "scroll/scroll_animator_compositor_coordinator.h",
+ "scroll/scroll_customization.cc",
+ "scroll/scroll_customization.h",
+ "scroll/scroll_snap_data.h",
+ "scroll/scroll_state_data.h",
+ "scroll/scroll_types.h",
+ "scroll/scrollable_area.cc",
+ "scroll/scrollable_area.h",
+ "scroll/scrollbar.cc",
+ "scroll/scrollbar.h",
+ "scroll/scrollbar_theme.cc",
+ "scroll/scrollbar_theme.h",
+ "scroll/scrollbar_theme_android.cc",
+ "scroll/scrollbar_theme_aura.cc",
+ "scroll/scrollbar_theme_aura.h",
+ "scroll/scrollbar_theme_client.h",
+ "scroll/scrollbar_theme_mac.h",
+ "scroll/scrollbar_theme_mac.mm",
+ "scroll/scrollbar_theme_mock.cc",
+ "scroll/scrollbar_theme_mock.h",
+ "scroll/scrollbar_theme_overlay.cc",
+ "scroll/scrollbar_theme_overlay.h",
+ "scroll/scrollbar_theme_overlay_mock.h",
+ "scroll/smooth_scroll_sequencer.cc",
+ "scroll/smooth_scroll_sequencer.h",
+ "scroll/web_scroll_into_view_params.cc",
+ "scroll/web_scrollbar_theme.mm",
+ "serialized_resource.h",
+ "shared_buffer.cc",
+ "shared_buffer.h",
+ "shared_buffer_chunk_reader.cc",
+ "shared_buffer_chunk_reader.h",
+ "speech/platform_speech_synthesis_utterance.cc",
+ "speech/platform_speech_synthesis_utterance.h",
+ "speech/platform_speech_synthesis_voice.cc",
+ "speech/platform_speech_synthesis_voice.h",
+ "speech/platform_speech_synthesizer.cc",
+ "speech/platform_speech_synthesizer.h",
+ "supplementable.cc",
+ "supplementable.h",
+ "text/bidi_character_run.h",
+ "text/bidi_context.cc",
+ "text/bidi_context.h",
+ "text/bidi_resolver.h",
+ "text/bidi_run_list.h",
+ "text/bidi_text_run.cc",
+ "text/bidi_text_run.h",
+ "text/character.cc",
+ "text/character.h",
+ "text/character_emoji.cc",
+ "text/character_property.h",
+ "text/date_time_format.cc",
+ "text/date_time_format.h",
+ "text/decode_escape_sequences.h",
+ "text/hyphenation.cc",
+ "text/hyphenation.h",
+ "text/icu_error.cc",
+ "text/icu_error.h",
+ "text/line_ending.cc",
+ "text/line_ending.h",
+ "text/linux/hyphenation_linux.cc",
+ "text/locale_icu.cc",
+ "text/locale_icu.h",
+ "text/locale_mac.h",
+ "text/locale_mac.mm",
+ "text/locale_to_script_mapping.cc",
+ "text/locale_to_script_mapping.h",
+ "text/locale_win.cc",
+ "text/locale_win.h",
+ "text/mac/hyphenation_mac.cc",
+ "text/platform_locale.cc",
+ "text/platform_locale.h",
+ "text/quoted_printable.cc",
+ "text/quoted_printable.h",
+ "text/segmented_string.cc",
+ "text/segmented_string.h",
+ "text/string_truncator.cc",
+ "text/string_truncator.h",
+ "text/suffix_tree.h",
+ "text/text_boundaries.cc",
+ "text/text_boundaries.h",
+ "text/text_break_iterator.cc",
+ "text/text_break_iterator.h",
+ "text/text_break_iterator_icu.cc",
+ "text/text_break_iterator_internal_icu.cc",
+ "text/text_break_iterator_internal_icu.h",
+ "text/text_direction.cc",
+ "text/text_direction.h",
+ "text/text_encoding_detector.cc",
+ "text/text_encoding_detector.h",
+ "text/text_justify.h",
+ "text/text_run.cc",
+ "text/text_run.h",
+ "text/text_run_iterator.h",
+ "text/text_stream.cc",
+ "text/text_stream.h",
+ "text/truncation.h",
+ "text/unicode_bidi.h",
+ "text/unicode_range.cc",
+ "text/unicode_range.h",
+ "text/unicode_utilities.cc",
+ "text/unicode_utilities.h",
+ "text/web_entities.cc",
+ "text/web_entities.h",
+ "text/win/hyphenation_win.cc",
+ "text/writing_mode.h",
+ "text/writing_mode_utils.h",
+ "theme.cc",
+ "theme.h",
+ "theme_types.h",
+ "threading/background_task_runner.cc",
+ "threading/background_task_runner.h",
+ "time_clamper.cc",
+ "time_clamper.h",
+ "timer.cc",
+ "timer.h",
+ "transforms/affine_transform.cc",
+ "transforms/affine_transform.h",
+ "transforms/identity_transform_operation.h",
+ "transforms/interpolated_transform_operation.cc",
+ "transforms/interpolated_transform_operation.h",
+ "transforms/matrix_3d_transform_operation.cc",
+ "transforms/matrix_3d_transform_operation.h",
+ "transforms/matrix_transform_operation.cc",
+ "transforms/matrix_transform_operation.h",
+ "transforms/perspective_transform_operation.cc",
+ "transforms/perspective_transform_operation.h",
+ "transforms/rotate_transform_operation.cc",
+ "transforms/rotate_transform_operation.h",
+ "transforms/rotation.cc",
+ "transforms/rotation.h",
+ "transforms/scale_transform_operation.cc",
+ "transforms/scale_transform_operation.h",
+ "transforms/skew_transform_operation.cc",
+ "transforms/skew_transform_operation.h",
+ "transforms/transform_operations.cc",
+ "transforms/transform_operations.h",
+ "transforms/transformation_matrix.cc",
+ "transforms/transformation_matrix.h",
+ "transforms/translate_transform_operation.cc",
+ "transforms/translate_transform_operation.h",
+ "ukm_time_aggregator.cc",
+ "ukm_time_aggregator.h",
+ "uuid.cc",
+ "uuid.h",
+ "waitable_event.cc",
+ "waitable_event.h",
+ "web_gesture_event.cc",
+ "web_icon_sizes_parser.cc",
+ "web_mouse_event.cc",
+ "web_mouse_wheel_event.cc",
+ "web_pointer_event.cc",
+ "web_task_runner.cc",
+ "web_task_runner.h",
+ "web_text_input_info.cc",
+ "web_thread.cc",
+ "web_thread_supporting_gc.cc",
+ "web_thread_supporting_gc.h",
+ "web_thread_type.cc",
+ "web_touch_event.cc",
+ "weborigin/known_ports.cc",
+ "weborigin/known_ports.h",
+ "weborigin/kurl.cc",
+ "weborigin/kurl.h",
+ "weborigin/kurl_hash.h",
+ "weborigin/origin_access_entry.cc",
+ "weborigin/origin_access_entry.h",
+ "weborigin/referrer.h",
+ "weborigin/referrer_policy.h",
+ "weborigin/scheme_registry.cc",
+ "weborigin/scheme_registry.h",
+ "weborigin/security_origin.cc",
+ "weborigin/security_origin.h",
+ "weborigin/security_origin_hash.h",
+ "weborigin/security_policy.cc",
+ "weborigin/security_policy.h",
+ "weborigin/security_violation_reporting_policy.h",
+ "weborigin/url_security_origin_map.h",
+ "windows_keyboard_codes.h",
+ ]
+
+ sources -= blink_platform_avx_files
+ sources -= blink_platform_neon_files
+ sources -= blink_platform_msa_files
+ sources -= blink_platform_sse_files
+
+ # Add in the generated files.
+ sources += get_target_outputs(":character_data") +
+ get_target_outputs(":color_data") +
+ get_target_outputs(":font_family_names") +
+ get_target_outputs(":instrumentation_probes") +
+ get_target_outputs(":runtime_enabled_features")
+
+ if (is_win) {
+ jumbo_excluded_sources = [
+ # https://crbug.com/775979 - Uses libjpeg_turbo which uses a
+ # "boolean" typedef which is different (int) from the Windows
+ # standard "boolean" typedef (unsigned char), resulting in
+ # compilation errors when both are joined in a translation unit.
+ "image-decoders/jpeg/jpeg_image_decoder.cc",
+ ]
+
+ if (is_component_build) {
+ # https://crbug.com/764823 - Mixing certain //url/ headers and
+ # using url::RawCanonOutputT<char> in one translation unit breaks
+ # the Windows component build. These files use RawCanonOutput.
+ jumbo_excluded_sources += [
+ "link_hash.cc",
+ "weborigin/kurl.cc",
+ "weborigin/origin_access_entry.cc",
+ "weborigin/security_origin.cc",
+ ]
+ }
+ }
+ configs += [
+ ":blink_platform_pch",
+ ":blink_platform_config",
+ ":blink_platform_implementation",
+ "//third_party/blink/renderer:non_test_config",
+ ]
+
+ include_dirs = []
+
+ public_deps = [
+ ":blink_platform_public_deps",
+ "//third_party/blink/renderer/platform/blob",
+ "//third_party/blink/renderer/platform/heap",
+ "//third_party/blink/renderer/platform/instrumentation",
+ "//third_party/blink/renderer/platform/loader",
+ "//third_party/blink/renderer/platform/media",
+ "//third_party/blink/renderer/platform/network",
+ "//third_party/blink/renderer/platform/scheduler",
+ ]
+ deps = [
+ ":platform_export",
+ "//base/allocator:buildflags",
+ "//components/viz/service",
+ "//device/base/synchronization",
+ "//device/vr/public/mojom:mojom_blink",
+ "//gin",
+ "//mojo/public/cpp/base",
+ "//mojo/public/cpp/bindings",
+ "//mojo/public/cpp/bindings:wtf_support",
+ "//services/service_manager/public/cpp",
+ "//third_party:freetype_harfbuzz",
+ "//third_party/blink/public:offscreen_canvas_mojo_bindings_blink",
+ "//third_party/ced",
+ "//third_party/icu",
+ "//ui/gfx",
+ "//ui/gfx/geometry",
+ ]
+
+ if (is_mac) {
+ sources -= [
+ # Uses KillRingMac.mm instead.
+ "fonts/skia/font_cache_skia.cc",
+ "fonts/web_font_render_style.cc",
+ "kill_ring_none.cc",
+ "scroll/scroll_animator.cc",
+ "scroll/scroll_animator.h",
+
+ # Uses LocaleMac instead.
+ "text/locale_icu.cc",
+ "text/locale_icu.h",
+ ]
+
+ libs = [
+ "AppKit.framework",
+ "Accelerate.framework",
+ "Carbon.framework",
+ "Foundation.framework",
+ ]
+ } else {
+ sources -= [
+ "geometry/cg/float_point_cg.cc",
+ "geometry/cg/float_rect_cg.cc",
+ "geometry/cg/float_size_cg.cc",
+ "geometry/cg/int_point_cg.cc",
+ "geometry/cg/int_rect_cg.cc",
+ "geometry/cg/int_size_cg.cc",
+ ]
+ }
+
+ if (is_win) {
+ sources += [ "clipboard/clipboard_utilities_win.cc" ]
+ sources -= [
+ # TODO(https://crbug.com/808221): Use WebFontRenderStyle on Windows.
+ "fonts/web_font_render_style.cc",
+
+ # Uses LocaleWin instead.
+ "text/locale_icu.cc",
+ "text/locale_icu.h",
+ ]
+ cflags = [
+ "/wd4334", # Result of 32-bit shift implicitly converted to 64 bits.
+ "/wd4724", # Modulo by 0.
+ ]
+ }
+
+ if (is_fuchsia) {
+ sources += [
+ "fonts/fuchsia/font_cache_fuchsia.cc",
+ "text/fuchsia/hyphenation_fuchsia.cc",
+ ]
+ }
+
+ if (use_minikin_hyphenation) {
+ sources += [
+ "text/hyphenation/hyphenation_minikin.cc",
+ "text/hyphenation/hyphenation_minikin.h",
+ "text/hyphenation/hyphenator_aosp.cc",
+ "text/hyphenation/hyphenator_aosp.h",
+ ]
+ }
+
+ if (!use_default_render_theme) {
+ sources -= [
+ "scroll/scrollbar_theme_aura.cc",
+ "scroll/scrollbar_theme_aura.h",
+ ]
+ }
+
+ if (current_cpu == "arm") {
+ deps += [ ":blink_arm_neon" ]
+ }
+
+ if (current_cpu == "mipsel" || current_cpu == "mips64el") {
+ deps += [ ":blink_mips_msa" ]
+ }
+
+ if (current_cpu == "x86" || current_cpu == "x64") {
+ deps += [
+ ":blink_x86_avx",
+ ":blink_x86_sse",
+ ]
+ }
+
+ if (use_webaudio_ffmpeg) {
+ include_dirs += [ "//third_party/ffmpeg" ]
+ deps += [ "//third_party/ffmpeg" ]
+ }
+ if (use_openmax_dl_fft) {
+ include_dirs += [ "//third_party/openmax_dl" ]
+ deps += [ "//third_party/openmax_dl/dl" ]
+ }
+
+ configs -= [ "//build/config/compiler:default_symbols" ]
+ configs += blink_symbols_config
+}
+
+jumbo_static_library("test_support") {
+ visibility += [ "//third_party/blink/*" ]
+ testonly = true
+
+ sources = [
+ "graphics/gpu/drawing_buffer_test_helpers.h",
+ "scroll/scrollbar_test_suite.h",
+ "testing/compositor_test.cc",
+ "testing/compositor_test.h",
+ "testing/empty_web_media_player.cc",
+ "testing/empty_web_media_player.h",
+ "testing/fake_display_item_client.h",
+ "testing/fake_graphics_layer.h",
+ "testing/fake_graphics_layer_client.h",
+ "testing/font_test_helpers.cc",
+ "testing/font_test_helpers.h",
+ "testing/fuzzed_data_provider.cc",
+ "testing/fuzzed_data_provider.h",
+ "testing/histogram_tester.cc",
+ "testing/histogram_tester.h",
+ "testing/message_loop_for_mojo.h",
+ "testing/mock_web_crypto.cc",
+ "testing/mock_web_crypto.h",
+ "testing/paint_property_test_helpers.h",
+ "testing/paint_test_configurations.h",
+ "testing/picture_matchers.cc",
+ "testing/picture_matchers.h",
+ "testing/scoped_mocked_url.cc",
+ "testing/scoped_mocked_url.h",
+ "testing/test_paint_artifact.cc",
+ "testing/test_paint_artifact.h",
+ "testing/testing_platform_support.cc",
+ "testing/testing_platform_support.h",
+ "testing/testing_platform_support_with_mock_scheduler.cc",
+ "testing/testing_platform_support_with_mock_scheduler.h",
+ "testing/testing_platform_support_with_web_rtc.cc",
+ "testing/testing_platform_support_with_web_rtc.h",
+ "testing/unit_test_helpers.cc",
+ "testing/unit_test_helpers.h",
+ "testing/url_test_helpers.cc",
+ "testing/url_test_helpers.h",
+ "testing/use_mock_scrollbar_settings.h",
+ "testing/web_layer_tree_view_impl_for_testing.cc",
+ "testing/web_layer_tree_view_impl_for_testing.h",
+ "testing/weburl_loader_mock.cc",
+ "testing/weburl_loader_mock.h",
+ "testing/weburl_loader_mock_factory_impl.cc",
+ "testing/weburl_loader_mock_factory_impl.h",
+ "testing/wtf/scoped_mock_clock.cc",
+ "testing/wtf/scoped_mock_clock.h",
+ ]
+
+ configs += [
+ ":blink_platform_pch",
+ "//third_party/blink/renderer:config",
+ "//third_party/blink/renderer:inside_blink",
+ "//third_party/blink/renderer:non_test_config",
+ ]
+
+ public_deps = [
+ ":platform",
+ "//cc",
+ "//cc:test_support",
+ "//cc/blink",
+ "//components/viz/test:test_support",
+ "//testing/gmock",
+ "//testing/gtest:gtest",
+ "//testing/perf",
+ ]
+
+ deps = [
+ "//base/test:test_support",
+ "//mojo/edk",
+ "//third_party/blink/renderer/platform/blob:test_support",
+ "//third_party/blink/renderer/platform/loader:test_support",
+ "//third_party/blink/renderer/platform/network:test_support",
+ "//third_party/blink/renderer/platform/scheduler:test_support",
+ ]
+
+ if (is_win) {
+ cflags = [ "/wd4267" ] # Truncation from size_t to int.
+ }
+}
+
+test("blink_platform_unittests") {
+ deps = [
+ ":blink_platform_unittests_sources",
+ ]
+}
+
+jumbo_source_set("blink_platform_unittests_sources") {
+ visibility = [] # Allow re-assignment of list.
+ visibility = [ "*" ]
+ testonly = true
+
+ sources = [
+ "animation/animation_translation_util_test.cc",
+ "animation/compositor_animation_host_test.cc",
+ "animation/compositor_animation_test.cc",
+ "animation/compositor_animation_timeline_test.cc",
+ "animation/compositor_float_animation_curve_test.cc",
+ "animation/compositor_keyframe_model_test.cc",
+ "animation/timing_function_test.cc",
+ "audio/push_pull_fifo_multithread_test.cc",
+ "audio/push_pull_fifo_test.cc",
+ "audio/vector_math_test.cc",
+ "bindings/runtime_call_stats_test.cc",
+ "decimal_test.cc",
+ "drag_image_test.cc",
+ "exported/file_path_conversion_test.cc",
+ "exported/web_cors_test.cc",
+ "exported/web_string_test.cc",
+ "feature_policy/feature_policy_test.cc",
+ "fonts/accept_languages_resolver_test.cc",
+ "fonts/android/font_cache_android_test.cc",
+ "fonts/bitmap_glyphs_blacklist_test.cc",
+ "fonts/font_cache_test.cc",
+ "fonts/font_description_test.cc",
+ "fonts/font_family_test.cc",
+ "fonts/font_platform_data_test.cc",
+ "fonts/font_selection_types_test.cc",
+ "fonts/font_test.cc",
+ "fonts/font_test_utilities.cc",
+ "fonts/generic_font_family_settings_test.cc",
+ "fonts/mac/font_family_matcher_mac_test.mm",
+ "fonts/opentype/font_settings_test.cc",
+ "fonts/opentype/open_type_vertical_data_test.cc",
+ "fonts/orientation_iterator_test.cc",
+ "fonts/script_run_iterator_test.cc",
+ "fonts/shaping/caching_word_shaper_test.cc",
+ "fonts/shaping/harf_buzz_shaper_test.cc",
+ "fonts/shaping/run_segmenter_test.cc",
+ "fonts/shaping/shape_result_bloberizer_test.cc",
+ "fonts/shaping/shaping_line_breaker_test.cc",
+ "fonts/small_caps_iterator_test.cc",
+ "fonts/symbols_iterator_test.cc",
+ "fonts/typesetting_features_test.cc",
+ "fonts/unicode_range_set_test.cc",
+ "geometry/double_rect_test.cc",
+ "geometry/float_box_test.cc",
+ "geometry/float_box_test_helpers.cc",
+ "geometry/float_box_test_helpers.h",
+ "geometry/float_point_test.cc",
+ "geometry/float_polygon_test.cc",
+ "geometry/float_quad_test.cc",
+ "geometry/float_rect_test.cc",
+ "geometry/float_rounded_rect_test.cc",
+ "geometry/float_size_test.cc",
+ "geometry/geometry_test_helpers.cc",
+ "geometry/geometry_test_helpers.h",
+ "geometry/int_rect_test.cc",
+ "geometry/layout_rect_outsets_test.cc",
+ "geometry/layout_rect_test.cc",
+ "geometry/layout_size_test.cc",
+ "geometry/region_test.cc",
+ "graphics/accelerated_static_bitmap_image_test.cc",
+ "graphics/bitmap_image_test.cc",
+ "graphics/compositing/chunk_to_layer_mapper_test.cc",
+ "graphics/compositing/composited_layer_raster_invalidator_test.cc",
+ "graphics/compositing/paint_artifact_compositor_test.cc",
+ "graphics/compositing/paint_chunks_to_cc_layer_test.cc",
+ "graphics/compositor_element_id_test.cc",
+ "graphics/contiguous_container_test.cc",
+ "graphics/decoding_image_generator_test.cc",
+ "graphics/deferred_image_decoder_test_wo_platform.cc",
+ "graphics/filters/fe_composite_test.cc",
+ "graphics/filters/image_filter_builder_test.cc",
+ "graphics/gpu/drawing_buffer_test.cc",
+ "graphics/gpu/shared_gpu_context_test.cc",
+ "graphics/gpu/webgl_image_conversion_test.cc",
+ "graphics/graphics_context_test.cc",
+ "graphics/high_contrast_image_classifier_test.cc",
+ "graphics/paint/cull_rect_test.cc",
+ "graphics/paint/display_item_client_test.cc",
+ "graphics/paint/display_item_test.cc",
+ "graphics/paint/drawing_display_item_test.cc",
+ "graphics/paint/drawing_recorder_test.cc",
+ "graphics/paint/float_clip_rect_test.cc",
+ "graphics/paint/geometry_mapper_test.cc",
+ "graphics/paint/paint_chunk_test.cc",
+ "graphics/paint/paint_chunker_test.cc",
+ "graphics/paint/paint_controller_test.cc",
+ "graphics/paint/paint_controller_test.h",
+ "graphics/paint/paint_property_node_test.cc",
+ "graphics/paint/paint_record_builder_test.cc",
+ "graphics/paint/property_tree_state_test.cc",
+ "graphics/paint_invalidation_reason_test.cc",
+ "graphics/placeholder_image_test.cc",
+ "graphics/static_bitmap_image_test.cc",
+ "graphics/video_frame_submitter_test.cc",
+ "histogram_test.cc",
+ "image-decoders/bmp/bmp_image_decoder_test.cc",
+ "image-decoders/fast_shared_buffer_reader_test.cc",
+ "image-decoders/gif/gif_image_decoder_test.cc",
+ "image-decoders/ico/ico_image_decoder_test.cc",
+ "image-decoders/image_decoder_test.cc",
+ "image-decoders/image_decoder_test_helpers.cc",
+ "image-decoders/image_decoder_test_helpers.h",
+ "image-decoders/jpeg/jpeg_image_decoder_test.cc",
+ "image-decoders/png/png_image_decoder_test.cc",
+ "image-decoders/webp/webp_image_decoder_test.cc",
+ "json/json_parser_test.cc",
+ "json/json_values_test.cc",
+ "layout_locale_test.cc",
+ "layout_unit_test.cc",
+ "lifecycle_context_test.cc",
+ "long_task_detector_test.cc",
+ "mac/graphics_context_canvas_test.mm",
+ "mac/version_util_mac_test.mm",
+ "mhtml/mhtml_parser_test.cc",
+ "mojo/big_string_mojom_traits_test.cc",
+ "mojo/geometry_struct_traits_test.cc",
+ "mojo/interface_invalidator_test.cc",
+ "mojo/kurl_security_origin_test.cc",
+ "mojo/notification_struct_traits_test.cc",
+ "mojo/string16_mojom_traits_test.cc",
+ "pod_arena_test.cc",
+ "pod_free_list_arena_test.cc",
+ "pod_interval_tree_test.cc",
+ "pod_red_black_tree_test.cc",
+ "scoped_orientation_change_indicator_test.cc",
+ "scroll/scrollable_area_test.cc",
+ "scroll/scrollbar_theme_overlay_test.cc",
+ "shared_buffer_test.cc",
+ "testing/arena_test_helpers.h",
+ "testing/tree_test_helpers.cc",
+ "testing/tree_test_helpers.h",
+ "text/bidi_resolver_test.cc",
+ "text/bidi_test_harness.h",
+ "text/character_test.cc",
+ "text/date_time_format_test.cc",
+ "text/hyphenation_test.cc",
+ "text/icu_error_test.cc",
+ "text/platform_locale_test.cc",
+ "text/segmented_string_test.cc",
+ "text/suffix_tree_test.cc",
+ "text/text_boundaries_test.cc",
+ "text/text_break_iterator_test.cc",
+ "text/text_encoding_detector_test.cc",
+ "text/text_run_test.cc",
+ "text/unicode_utilities_test.cc",
+ "text/writing_mode_utils_test.cc",
+ "threading/background_task_runner_test.cc",
+ "time_clamper_test.cc",
+ "timer_test.cc",
+ "transforms/affine_transform_test.cc",
+ "transforms/rotation_test.cc",
+ "transforms/transform_operations_test.cc",
+ "transforms/transformation_matrix_test.cc",
+ "ukm_time_aggregator_test.cc",
+ "uuid_test.cc",
+ "web_icon_sizes_parser_test.cc",
+ "web_screen_info_test.cc",
+ "web_task_runner_test.cc",
+ "web_vector_test.cc",
+ "weborigin/known_ports_test.cc",
+ "weborigin/kurl_test.cc",
+ "weborigin/origin_access_entry_test.cc",
+ "weborigin/scheme_registry_test.cc",
+ "weborigin/security_origin_test.cc",
+ "weborigin/security_policy_test.cc",
+ ]
+
+ if (is_win) {
+ sources += [ "text/locale_win_test.cc" ]
+ } else if (is_mac) {
+ sources += [ "text/locale_mac_test.cc" ]
+ } else if (is_posix || is_fuchsia) {
+ sources += [ "text/locale_icu_test.cc" ]
+ }
+
+ if (!is_mac) {
+ sources += [ "scroll/scroll_animator_test.cc" ]
+ }
+
+ if (use_default_render_theme) {
+ sources += [ "scroll/scrollbar_theme_aura_test.cc" ]
+ }
+
+ sources += [ "testing/run_all_tests.cc" ]
+
+ configs += [
+ ":blink_platform_pch",
+ "//third_party/blink/renderer/platform/wtf:wtf_config",
+ "//third_party/blink/renderer:config",
+ ]
+
+ deps = [
+ ":platform",
+ ":test_support",
+ "//base",
+ "//base/test:test_support",
+ "//cc",
+ "//cc:test_support",
+ "//cc/blink",
+ "//device/base/synchronization",
+ "//mojo/common:test_common_custom_types_blink",
+ "//mojo/edk",
+ "//mojo/public/cpp/bindings/tests:for_blink_tests",
+ "//mojo/public/cpp/test_support:test_utils",
+ "//mojo/public/interfaces/bindings/tests:test_interfaces_blink",
+ "//services/viz/public/interfaces",
+ "//services/viz/public/interfaces:interfaces_blink",
+ "//skia",
+ "//testing/gmock",
+ "//testing/gtest",
+ "//third_party:freetype_harfbuzz",
+ "//third_party/blink/renderer/platform/blob:unit_tests",
+ "//third_party/blink/renderer/platform/instrumentation:unit_tests",
+ "//third_party/blink/renderer/platform/loader:unit_tests",
+ "//third_party/blink/renderer/platform/network:unit_tests",
+ "//third_party/blink/renderer/platform/scheduler:unit_tests",
+ "//third_party/blink/renderer/platform/wtf",
+ "//ui/gfx",
+ "//ui/gfx/geometry",
+ "//ui/gfx/geometry/mojo:test_interfaces_blink",
+ "//url",
+ "//url/mojom:test_url_mojom_gurl_blink",
+ ]
+
+ data_deps = [
+ ":blink_platform_unittests_data",
+ ]
+
+ defines = [ "INSIDE_BLINK" ]
+
+ include_dirs = [ "$root_gen_dir/third_party/blink/renderer" ]
+}
+
+executable("image_decode_bench") {
+ visibility = [] # Allow re-assignment of list.
+ visibility = [ "*" ]
+
+ sources = [
+ "testing/image_decode_bench.cc",
+ ]
+
+ deps = [
+ ":platform",
+ "//build/config:exe_and_shlib_deps",
+ "//mojo/edk",
+ "//third_party/blink/renderer/platform/wtf",
+ ]
+
+ configs += [
+ "//third_party/blink/renderer/platform/wtf:wtf_config",
+ "//third_party/blink/renderer:config",
+ ]
+
+ defines = [ "INSIDE_BLINK" ]
+}
+
+test("blink_platform_perftests") {
+ sources = [
+ "testing/blink_perf_test_suite.cc",
+ "testing/blink_perf_test_suite.h",
+ "testing/run_all_perf_tests.cc",
+ "testing/shaping_line_breaker_perf_test.cc",
+ ]
+
+ configs += [
+ # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+ "//build/config/compiler:no_size_t_to_int_warning",
+ "//third_party/blink/renderer/platform/wtf:wtf_config",
+ "//third_party/blink/renderer:config",
+ ]
+
+ deps = [
+ ":platform",
+ ":test_support",
+ "//base",
+ "//base/test:test_support",
+ "//services/network/public/cpp:cpp",
+ "//testing/gtest",
+ "//testing/perf",
+ "//third_party:freetype_harfbuzz",
+ "//third_party/blink/renderer/platform/scheduler:perf_tests",
+ ]
+}
+
+group("blink_platform_unittests_data") {
+ data = [
+ "testing/data/",
+
+ # Required by some image decoder tests.
+ "image-decoders/testing/",
+ "//third_party/WebKit/LayoutTests/images/resources/",
+ ]
+}
+
+if (current_cpu == "arm") {
+ source_set("blink_arm_neon") {
+ sources = blink_platform_neon_files
+ # The *NEON.cpp files fail to compile when -mthumb is passed. Force
+ # them to build in ARM mode.
+ # See https://bugs.webkit.org/show_bug.cgi?id=62916.
+ # TODO(GYP)
+ #'cflags': ['-marm'],
+ # 'conditions': [
+ # ['OS=="android"', {
+ # 'cflags!': ['-mthumb'],
+ # }],
+ # ],
+ }
+}
+
+if (current_cpu == "mipsel" || current_cpu == "mips64el") {
+ source_set("blink_mips_msa") {
+ sources = blink_platform_msa_files
+ }
+}
+
+if (current_cpu == "x86" || current_cpu == "x64") {
+ source_set("blink_x86_avx") {
+ sources = blink_platform_avx_files
+ configs += [
+ ":blink_platform_config",
+ ":blink_platform_implementation",
+ "//third_party/blink/renderer:non_test_config",
+ ]
+ if (!is_clang && is_win) {
+ cflags = [ "/arch:AVX" ]
+ } else {
+ cflags = [ "-mavx" ]
+ }
+ }
+ source_set("blink_x86_sse") {
+ sources = blink_platform_sse_files
+ }
+}
+
+# This source set is used for fuzzers that need an environment similar to unit
+# tests.
+jumbo_source_set("blink_fuzzer_test_support") {
+ testonly = true
+ visibility = [] # Allow re-assignment of list.
+ visibility = [ "*" ]
+ sources = [
+ "testing/blink_fuzzer_test_support.cc",
+ "testing/blink_fuzzer_test_support.h",
+ ]
+ deps = [
+ ":platform",
+ ":test_support",
+ "//content/test:test_support",
+ ]
+}
+
+# Fuzzer for blink::MHTMLParser.
+fuzzer_test("mhtml_parser_fuzzer") {
+ sources = [
+ "mhtml/mhtml_fuzzer.cc",
+ ]
+ deps = [
+ ":blink_fuzzer_test_support",
+ ":platform",
+ ]
+ dict = "//testing/libfuzzer/fuzzers/dicts/mhtml.dict"
+ seed_corpus = "//third_party/WebKit/LayoutTests/mhtml"
+ additional_configs = [
+ "//third_party/blink/renderer/platform/wtf:wtf_config",
+ "//third_party/blink/renderer:config",
+ ]
+}
+
+# Fuzzer for blink::WebIconSizesParser.
+fuzzer_test("web_icon_sizes_fuzzer") {
+ sources = [
+ "web_icon_sizes_fuzzer.cc",
+ ]
+ deps = [
+ ":blink_fuzzer_test_support",
+ ":platform",
+ ]
+ dict = "//testing/libfuzzer/fuzzers/dicts/web_icon_sizes.dict"
+}
+
+fuzzer_test("blink_png_decoder_fuzzer") {
+ sources = [
+ "png_fuzzer.cc",
+ ]
+ deps = [
+ ":blink_fuzzer_test_support",
+ ":platform",
+ "//third_party/libpng",
+ ]
+ dict = "//testing/libfuzzer/fuzzers/dicts/png.dict"
+ seed_corpuses = [
+ "//components/viz/test/data",
+ "//third_party/WebKit/LayoutTests/images/png-suite/samples",
+ "//third_party/WebKit/LayoutTests/images/resources/pngfuzz",
+ ]
+}
+
+# Fuzzer for blink::JSONParser.
+fuzzer_test("blink_json_parser_fuzzer") {
+ sources = [
+ "json/json_parser_fuzzer.cc",
+ ]
+ deps = [
+ ":blink_fuzzer_test_support",
+ ":platform",
+ ]
+ dict = "//testing/libfuzzer/fuzzers/dicts/json.dict"
+}
+
+# Fuzzer for blink::FeaturePolicy.
+fuzzer_test("feature_policy_fuzzer") {
+ sources = [
+ "feature_policy/feature_policy_fuzzer.cc",
+ ]
+ deps = [
+ ":blink_fuzzer_test_support",
+ ":platform",
+ ]
+ dict = "//testing/libfuzzer/fuzzers/dicts/feature_policy.dict"
+ seed_corpus = "//testing/libfuzzer/fuzzers/feature_policy_corpus"
+}
+
+fuzzer_test("blink_harfbuzz_shaper_fuzzer") {
+ sources = [
+ "fonts/shaping/harf_buzz_shaper_fuzzer.cc",
+ ]
+ deps = [
+ ":blink_fuzzer_test_support",
+ ":platform",
+ "//third_party/icu",
+ ]
+ libfuzzer_options = [ "max_len=256" ]
+}
+
+fuzzer_test("blink_http_parsers_fuzzer") {
+ sources = [
+ "network/http_parsers_fuzzer.cc",
+ ]
+ deps = [
+ ":blink_fuzzer_test_support",
+ ":platform",
+ ]
+ dict = "network/HTTPParsersFuzzer.dict"
+}
+
+# Fuzzer template for WTF::TextCodec.
+template("blink_text_codec_fuzzer") {
+ forward_variables_from(invoker, "*")
+ config(target_name + "_config") {
+ defines = [ "$target_name" ]
+ }
+ name = target_name
+ fuzzer_test("blink_text_codec_" + name + "_fuzzer") {
+ sources = [
+ "text_codec_fuzzer.cc",
+ ]
+ deps = [
+ ":blink_fuzzer_test_support",
+ ":platform",
+ ]
+ additional_configs = [ ":" + name + "_config" ]
+ seed_corpus = "text_codec_fuzzer_seed_corpus"
+ }
+}
+
+blink_text_codec_fuzzer("UTF_8") {
+}
+
+blink_text_codec_fuzzer("WINDOWS_1252") {
+}
+
+# NOTE: These are legacy unit tests and tests that require a Platform
+# object. Do not add more unless the test requires a Platform object.
+# These tests are a part of the webkit_unit_tests binary.
+jumbo_source_set("unit_tests") {
+ testonly = true
+ visibility = []
+ visibility = [ "//third_party/blink/renderer/*" ]
+ sources = [
+ "graphics/canvas_2d_layer_bridge_test.cc",
+ "graphics/canvas_color_params_test.cc",
+ "graphics/canvas_resource_test.cc",
+ "graphics/color_correction_test_utils.cc",
+ "graphics/deferred_image_decoder_test.cc",
+ "graphics/gpu/drawing_buffer_software_rendering_test.cc",
+ "graphics/graphics_layer_test.cc",
+ "graphics/image_decoding_store_test.cc",
+ "graphics/image_frame_generator_test.cc",
+ "graphics/image_layer_chromium_test.cc",
+ "graphics/offscreen_canvas_frame_dispatcher_impl_test.cc",
+ "graphics/test/fake_gles2_interface.h",
+ "graphics/test/fake_scrollable_area.h",
+ "graphics/test/fake_web_graphics_context_3d_provider.h",
+ "graphics/test/mock_image_decoder.h",
+ "graphics/test/mock_paint_canvas.h",
+ "graphics/test/stub_image.h",
+
+ # Tests migrated from the web/tests directory.
+ "exported/web_image_test.cc",
+ "exported/web_url_request_test.cc",
+ "exported/web_url_response_test.cc",
+ "timer_perf_test.cc",
+ ]
+
+ configs += [
+ ":blink_platform_pch",
+ "//third_party/blink/renderer:config",
+ "//third_party/blink/renderer:inside_blink",
+ ]
+
+ deps = [
+ ":test_support",
+ "//testing/gmock",
+ "//testing/gtest",
+ ]
+}
diff --git a/chromium/third_party/blink/renderer/platform/DEPS b/chromium/third_party/blink/renderer/platform/DEPS
new file mode 100644
index 00000000000..7b43092ccd2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/DEPS
@@ -0,0 +1,76 @@
+include_rules = [
+ # To whitelist base/ stuff Blink is allowed to include, we list up all
+ # directories and files instead of writing 'base/'.
+ "+base/allocator/partition_allocator/oom.h",
+ "+base/bind.h",
+ "+base/bind_helpers.h",
+ "+base/bit_cast.h",
+ "+base/cpu.h",
+ "+base/feature_list.h",
+ "+base/files",
+ "+base/guid.h",
+ "+base/json",
+ "+base/location.h",
+ "+base/logging.h",
+ "+base/memory",
+ "+base/observer_list.h",
+ "+base/message_loop/message_loop.h",
+ "+base/metrics/histogram.h",
+ "+base/metrics/histogram_base.h",
+ "+base/metrics/histogram_macros.h",
+ "+base/metrics/histogram_samples.h",
+ "+base/metrics/sparse_histogram.h",
+ "+base/numerics/checked_math.h",
+ "+base/numerics/safe_conversions.h",
+ "+base/rand_util.h",
+ "+base/run_loop.h",
+ "+base/sampling_heap_profiler/sampling_heap_profiler.h",
+ "+base/strings/string_util.h",
+ "+base/strings/stringprintf.h",
+ "+base/synchronization/waitable_event.h",
+ "+base/sys_info.h",
+ "+base/android/sys_utils.h",
+ "+base/test",
+ "+base/test/fuzzed_data_provider.h",
+ "+base/threading/thread_task_runner_handle.h",
+ "+base/time",
+ "+base/timer",
+ "+base/trace_event",
+ "+base/values.h",
+ "+base/lazy_instance.h",
+ "+net/base/escape.h",
+ "+net/http/http_util.h",
+ "+net/http/http_request_headers.h",
+ "+net/http/http_response_headers.h",
+ "+device",
+ "+gpu/GLES2",
+ "+mojo/public",
+ "+mozilla",
+ "+services/metrics/public/cpp/ukm_entry_builder.h",
+ "+services/metrics/public/cpp/ukm_recorder.h",
+ "+services/network/public/mojom/data_pipe_getter.mojom-blink.h",
+ "+services/viz/public/interfaces/compositing/compositor_frame_sink.mojom-blink.h",
+ "+skia/ext",
+ #TODO(nverne): remove this
+ "+third_party/blink/public/web/blink.h",
+ "+third_party/ced/src/compact_enc_det/compact_enc_det.h",
+ "+third_party/khronos",
+ "+third_party/skia",
+ "+ui/gfx",
+ "+url",
+ "+webp",
+ "+build/mac",
+ "+build/win",
+ "-third_party/blink/renderer/bindings",
+ "-third_party/blink/renderer/core",
+ "-third_party/blink/renderer/modules",
+]
+
+specific_include_rules = {
+ "web_url_error\.cc": [
+ "+net/base/net_errors.h"
+ ],
+ "ukm_time_aggregator_test.cc" : [
+ "+components/ukm/test_ukm_recorder.h"
+ ],
+}
diff --git a/chromium/third_party/blink/renderer/platform/InstanceCountersMemoryDumpProvider.cc b/chromium/third_party/blink/renderer/platform/InstanceCountersMemoryDumpProvider.cc
new file mode 100644
index 00000000000..8f93777a4ba
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/InstanceCountersMemoryDumpProvider.cc
@@ -0,0 +1,32 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/instance_counters_memory_dump_provider.h"
+
+#include "base/trace_event/process_memory_dump.h"
+#include "third_party/blink/renderer/platform/instance_counters.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+
+namespace blink {
+
+InstanceCountersMemoryDumpProvider*
+InstanceCountersMemoryDumpProvider::Instance() {
+ DEFINE_STATIC_LOCAL(InstanceCountersMemoryDumpProvider, instance, ());
+ return &instance;
+}
+
+bool InstanceCountersMemoryDumpProvider::OnMemoryDump(
+ const base::trace_event::MemoryDumpArgs& args,
+ base::trace_event::ProcessMemoryDump* memory_dump) {
+ using base::trace_event::MemoryAllocatorDump;
+#define DUMP_COUNTER(CounterType) \
+ memory_dump->CreateAllocatorDump("blink_objects/" #CounterType) \
+ ->AddScalar("object_count", MemoryAllocatorDump::kUnitsObjects, \
+ InstanceCounters::CounterValue( \
+ InstanceCounters::k##CounterType##Counter));
+ INSTANCE_COUNTERS_LIST(DUMP_COUNTER)
+ return true;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/OWNERS b/chromium/third_party/blink/renderer/platform/OWNERS
new file mode 100644
index 00000000000..d52a1b2e054
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/OWNERS
@@ -0,0 +1,30 @@
+dgozman@chromium.org
+drott@chromium.org
+eae@chromium.org
+fmalita@chromium.org
+haraken@chromium.org
+jbroman@chromium.org
+jochen@chromium.org
+junov@chromium.org
+kbr@chromium.org
+kinuko@chromium.org
+mkwst@chromium.org
+noel@chromium.org
+pdr@chromium.org
+pfeldman@chromium.org
+rtoy@chromium.org
+schenney@chromium.org
+senorblanco@chromium.org
+skyostil@chromium.org
+thakis@chromium.org
+tkent@chromium.org
+vollick@chromium.org
+wangxianzhu@chromium.org
+
+# Any API or core owner can also approve changes to runtime-enabled features.
+# Please make sure to get a review from someone with expertise on the feature
+# you are changing.
+per-file runtime_enabled_features.json5=file://third_party/WebKit/API_OWNERS
+per-file runtime_enabled_features.json5=file://third_party/blink/renderer/core/OWNERS
+
+# COMPONENT: Platform
diff --git a/chromium/third_party/blink/renderer/platform/PRESUBMIT.py b/chromium/third_party/blink/renderer/platform/PRESUBMIT.py
new file mode 100644
index 00000000000..e053782b759
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/PRESUBMIT.py
@@ -0,0 +1,62 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Presubmit script for changes affecting Source/platform.
+
+See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
+for more details about the presubmit API built into depot_tools.
+"""
+
+import difflib
+import os
+import re
+
+RUNTIMEENABLED_NAME = re.compile(r'\s*name\s*:\s*"([^"]*)"')
+
+
+def RuntimeEnabledFeatureNames(filename):
+ """Reads the 'name' of each feature in runtime_enabled_features.json5."""
+ # Note: We don't have a JSON5 parser available, so just use a regex.
+ with open(filename) as f:
+ for line in f:
+ match = RUNTIMEENABLED_NAME.match(line)
+ if match:
+ yield match.group(1)
+
+
+def _CheckRuntimeEnabledFeaturesSorted(input_api, output_api):
+ """Check: runtime_enabled_features.json5 feature list sorted alphabetically.
+ """
+ # Read runtime_enabled_features.json5 using the JSON5 parser.
+ filename = os.path.join(input_api.PresubmitLocalPath(),
+ 'runtime_enabled_features.json5')
+ features = list(RuntimeEnabledFeatureNames(filename))
+
+ # Sort the 'data' section by name.
+ features_sorted = sorted(features, key=lambda s: s.lower())
+
+ if features == features_sorted:
+ return []
+
+ # Diff the sorted/unsorted versions.
+ differ = difflib.Differ()
+ diff = differ.compare(features, features_sorted)
+ return [output_api.PresubmitError(
+ 'runtime_enabled_features.json5 features must be sorted alphabetically. '
+ 'Diff of feature order follows:', long_text='\n'.join(diff))]
+
+
+def _CommonChecks(input_api, output_api):
+ """Checks common to both upload and commit."""
+ results = []
+ results.extend(_CheckRuntimeEnabledFeaturesSorted(input_api, output_api))
+ return results
+
+
+def CheckChangeOnUpload(input_api, output_api):
+ return _CommonChecks(input_api, output_api)
+
+
+def CheckChangeOnCommit(input_api, output_api):
+ return _CommonChecks(input_api, output_api)
diff --git a/chromium/third_party/blink/renderer/platform/animation/DEPS b/chromium/third_party/blink/renderer/platform/animation/DEPS
new file mode 100644
index 00000000000..8d825ff202e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+cc",
+ "-cc/blink",
+]
diff --git a/chromium/third_party/blink/renderer/platform/animation/OWNERS b/chromium/third_party/blink/renderer/platform/animation/OWNERS
new file mode 100644
index 00000000000..b19b85b5c5a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/OWNERS
@@ -0,0 +1,6 @@
+file://third_party/blink/renderer/core/animation/OWNERS
+
+loyso@chromium.org
+
+# TEAM: animations-dev@chromium.org
+# COMPONENT: Blink>Animation
diff --git a/chromium/third_party/blink/renderer/platform/animation/animation_translation_util.cc b/chromium/third_party/blink/renderer/platform/animation/animation_translation_util.cc
new file mode 100644
index 00000000000..7180abd59a8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/animation_translation_util.cc
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2012 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/animation/animation_translation_util.h"
+
+#include "third_party/blink/renderer/platform/animation/compositor_transform_operations.h"
+#include "third_party/blink/renderer/platform/transforms/interpolated_transform_operation.h"
+#include "third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.h"
+#include "third_party/blink/renderer/platform/transforms/matrix_transform_operation.h"
+#include "third_party/blink/renderer/platform/transforms/perspective_transform_operation.h"
+#include "third_party/blink/renderer/platform/transforms/rotate_transform_operation.h"
+#include "third_party/blink/renderer/platform/transforms/scale_transform_operation.h"
+#include "third_party/blink/renderer/platform/transforms/skew_transform_operation.h"
+#include "third_party/blink/renderer/platform/transforms/transform_operations.h"
+#include "third_party/blink/renderer/platform/transforms/transformation_matrix.h"
+#include "third_party/blink/renderer/platform/transforms/translate_transform_operation.h"
+
+namespace blink {
+
+void ToCompositorTransformOperations(
+ const TransformOperations& transform_operations,
+ CompositorTransformOperations* out_transform_operations) {
+ // We need to do a deep copy the transformOperations may contain ref pointers
+ // to TransformOperation objects.
+ for (const auto& operation : transform_operations.Operations()) {
+ switch (operation->GetType()) {
+ case TransformOperation::kScaleX:
+ case TransformOperation::kScaleY:
+ case TransformOperation::kScaleZ:
+ case TransformOperation::kScale3D:
+ case TransformOperation::kScale: {
+ auto transform =
+ static_cast<const ScaleTransformOperation*>(operation.get());
+ out_transform_operations->AppendScale(transform->X(), transform->Y(),
+ transform->Z());
+ break;
+ }
+ case TransformOperation::kTranslateX:
+ case TransformOperation::kTranslateY:
+ case TransformOperation::kTranslateZ:
+ case TransformOperation::kTranslate3D:
+ case TransformOperation::kTranslate: {
+ auto transform =
+ static_cast<const TranslateTransformOperation*>(operation.get());
+ DCHECK(transform->X().IsFixed() && transform->Y().IsFixed());
+ out_transform_operations->AppendTranslate(
+ transform->X().Value(), transform->Y().Value(), transform->Z());
+ break;
+ }
+ case TransformOperation::kRotateX:
+ case TransformOperation::kRotateY:
+ case TransformOperation::kRotate3D:
+ case TransformOperation::kRotate: {
+ auto transform =
+ static_cast<const RotateTransformOperation*>(operation.get());
+ out_transform_operations->AppendRotate(
+ transform->X(), transform->Y(), transform->Z(), transform->Angle());
+ break;
+ }
+ case TransformOperation::kSkewX:
+ case TransformOperation::kSkewY:
+ case TransformOperation::kSkew: {
+ auto transform =
+ static_cast<const SkewTransformOperation*>(operation.get());
+ out_transform_operations->AppendSkew(transform->AngleX(),
+ transform->AngleY());
+ break;
+ }
+ case TransformOperation::kMatrix: {
+ auto transform =
+ static_cast<const MatrixTransformOperation*>(operation.get());
+ TransformationMatrix m = transform->Matrix();
+ out_transform_operations->AppendMatrix(
+ TransformationMatrix::ToSkMatrix44(m));
+ break;
+ }
+ case TransformOperation::kMatrix3D: {
+ auto transform =
+ static_cast<const Matrix3DTransformOperation*>(operation.get());
+ TransformationMatrix m = transform->Matrix();
+ out_transform_operations->AppendMatrix(
+ TransformationMatrix::ToSkMatrix44(m));
+ break;
+ }
+ case TransformOperation::kPerspective: {
+ auto transform =
+ static_cast<const PerspectiveTransformOperation*>(operation.get());
+ out_transform_operations->AppendPerspective(transform->Perspective());
+ break;
+ }
+ case TransformOperation::kRotateAroundOrigin:
+ case TransformOperation::kInterpolated: {
+ TransformationMatrix m;
+ operation->Apply(m, FloatSize());
+ out_transform_operations->AppendMatrix(
+ TransformationMatrix::ToSkMatrix44(m));
+ break;
+ }
+ case TransformOperation::kIdentity:
+ out_transform_operations->AppendIdentity();
+ break;
+ default:
+ NOTREACHED();
+ break;
+ } // switch
+ } // for each operation
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/animation/animation_translation_util.h b/chromium/third_party/blink/renderer/platform/animation/animation_translation_util.h
new file mode 100644
index 00000000000..cd8affcc8b9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/animation_translation_util.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2010 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_ANIMATION_TRANSLATION_UTIL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_ANIMATION_TRANSLATION_UTIL_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+
+class CompositorTransformOperations;
+class TransformOperations;
+
+PLATFORM_EXPORT void ToCompositorTransformOperations(
+ const TransformOperations& in_operations,
+ CompositorTransformOperations* out_operations);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_ANIMATION_TRANSLATION_UTIL_H_
diff --git a/chromium/third_party/blink/renderer/platform/animation/animation_translation_util_test.cc b/chromium/third_party/blink/renderer/platform/animation/animation_translation_util_test.cc
new file mode 100644
index 00000000000..2a15f39d1ac
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/animation_translation_util_test.cc
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2012 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/animation/animation_translation_util.h"
+
+#include <memory>
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/animation/compositor_transform_operations.h"
+#include "third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.h"
+#include "third_party/blink/renderer/platform/transforms/rotate_transform_operation.h"
+#include "third_party/blink/renderer/platform/transforms/scale_transform_operation.h"
+#include "third_party/blink/renderer/platform/transforms/transform_operations.h"
+#include "third_party/blink/renderer/platform/transforms/translate_transform_operation.h"
+
+namespace blink {
+
+TEST(AnimationTranslationUtilTest, transformsWork) {
+ TransformOperations ops;
+ CompositorTransformOperations out_ops;
+
+ ops.Operations().push_back(TranslateTransformOperation::Create(
+ Length(2, kFixed), Length(0, kFixed), TransformOperation::kTranslateX));
+ ops.Operations().push_back(RotateTransformOperation::Create(
+ 0.1, 0.2, 0.3, 200000.4, TransformOperation::kRotate3D));
+ ops.Operations().push_back(ScaleTransformOperation::Create(
+ 50.2, 100, -4, TransformOperation::kScale3D));
+ ToCompositorTransformOperations(ops, &out_ops);
+
+ EXPECT_EQ(3UL, out_ops.AsCcTransformOperations().size());
+ const float kErr = 0.0001;
+
+ auto& op0 = out_ops.AsCcTransformOperations().at(0);
+ EXPECT_EQ(cc::TransformOperation::TRANSFORM_OPERATION_TRANSLATE, op0.type);
+ EXPECT_NEAR(op0.translate.x, 2.0f, kErr);
+ EXPECT_NEAR(op0.translate.y, 0.0f, kErr);
+ EXPECT_NEAR(op0.translate.z, 0.0f, kErr);
+
+ auto& op1 = out_ops.AsCcTransformOperations().at(1);
+ EXPECT_EQ(cc::TransformOperation::TRANSFORM_OPERATION_ROTATE, op1.type);
+ EXPECT_NEAR(op1.rotate.axis.x, 0.1f, kErr);
+ EXPECT_NEAR(op1.rotate.axis.y, 0.2f, kErr);
+ EXPECT_NEAR(op1.rotate.axis.z, 0.3f, kErr);
+ EXPECT_NEAR(op1.rotate.angle, 200000.4f, 0.01f);
+
+ auto& op2 = out_ops.AsCcTransformOperations().at(2);
+ EXPECT_EQ(cc::TransformOperation::TRANSFORM_OPERATION_SCALE, op2.type);
+ EXPECT_NEAR(op2.scale.x, 50.2f, kErr);
+ EXPECT_NEAR(op2.scale.y, 100.0f, kErr);
+ EXPECT_NEAR(op2.scale.z, -4.0f, kErr);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/animation/animation_utilities.h b/chromium/third_party/blink/renderer/platform/animation/animation_utilities.h
new file mode 100644
index 00000000000..fd6a05c818a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/animation_utilities.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2011 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_ANIMATION_UTILITIES_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_ANIMATION_UTILITIES_H_
+
+#include "third_party/blink/renderer/platform/geometry/float_point.h"
+#include "third_party/blink/renderer/platform/geometry/int_point.h"
+#include "third_party/blink/renderer/platform/layout_unit.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "ui/gfx/geometry/cubic_bezier.h"
+
+#include <type_traits>
+
+namespace blink {
+
+inline int Blend(int from, int to, double progress) {
+ return lround(from + (to - from) * progress);
+}
+
+// For unsigned types.
+template <typename T>
+inline T Blend(T from, T to, double progress) {
+ static_assert(std::is_integral<T>::value,
+ "blend can only be used with integer types");
+ return clampTo<T>(round(to > from ? from + (to - from) * progress
+ : from - (from - to) * progress));
+}
+
+inline double Blend(double from, double to, double progress) {
+ return from + (to - from) * progress;
+}
+
+inline float Blend(float from, float to, double progress) {
+ return static_cast<float>(from + (to - from) * progress);
+}
+
+inline LayoutUnit Blend(LayoutUnit from, LayoutUnit to, double progress) {
+ return LayoutUnit(from + (to - from) * progress);
+}
+
+inline IntPoint Blend(const IntPoint& from,
+ const IntPoint& to,
+ double progress) {
+ return IntPoint(Blend(from.X(), to.X(), progress),
+ Blend(from.Y(), to.Y(), progress));
+}
+
+inline FloatPoint Blend(const FloatPoint& from,
+ const FloatPoint& to,
+ double progress) {
+ return FloatPoint(Blend(from.X(), to.X(), progress),
+ Blend(from.Y(), to.Y(), progress));
+}
+
+// Calculates the accuracy for evaluating a timing function for an animation
+// with the specified duration.
+inline double AccuracyForDuration(double duration) {
+ double accuracy = 1.0 / (200.0 * duration);
+ double default_epsilon = gfx::CubicBezier::GetDefaultEpsilon();
+ // Avoid min()/max() from std here in the header, because that would require
+ // inclusion of <algorithm>, which is slow to compile.
+ return accuracy > default_epsilon ? accuracy : default_epsilon;
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_ANIMATION_UTILITIES_H_
diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_animation.cc b/chromium/third_party/blink/renderer/platform/animation/compositor_animation.cc
new file mode 100644
index 00000000000..2da7d25d62a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_animation.cc
@@ -0,0 +1,124 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/animation/compositor_animation.h"
+
+#include "cc/animation/animation_id_provider.h"
+#include "cc/animation/animation_timeline.h"
+#include "third_party/blink/renderer/platform/animation/compositor_animation_delegate.h"
+#include "third_party/blink/renderer/platform/animation/compositor_keyframe_model.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+std::unique_ptr<CompositorAnimation> CompositorAnimation::Create() {
+ return std::make_unique<CompositorAnimation>(
+ cc::SingleKeyframeEffectAnimation::Create(
+ cc::AnimationIdProvider::NextAnimationId()));
+}
+
+std::unique_ptr<CompositorAnimation>
+CompositorAnimation::CreateWorkletAnimation(
+ const String& name,
+ std::unique_ptr<CompositorScrollTimeline> scroll_timeline) {
+ return std::make_unique<CompositorAnimation>(cc::WorkletAnimation::Create(
+ cc::AnimationIdProvider::NextAnimationId(),
+ std::string(name.Ascii().data(), name.length()),
+ std::move(scroll_timeline)));
+}
+
+CompositorAnimation::CompositorAnimation(
+ scoped_refptr<cc::SingleKeyframeEffectAnimation> animation)
+ : animation_(animation), delegate_() {}
+
+CompositorAnimation::~CompositorAnimation() {
+ SetAnimationDelegate(nullptr);
+ // Detach animation from timeline, otherwise it stays there (leaks) until
+ // compositor shutdown.
+ if (animation_->animation_timeline())
+ animation_->animation_timeline()->DetachAnimation(animation_);
+}
+
+cc::SingleKeyframeEffectAnimation* CompositorAnimation::CcAnimation() const {
+ return animation_.get();
+}
+
+void CompositorAnimation::SetAnimationDelegate(
+ CompositorAnimationDelegate* delegate) {
+ delegate_ = delegate;
+ animation_->set_animation_delegate(delegate ? this : nullptr);
+}
+
+void CompositorAnimation::AttachElement(const CompositorElementId& id) {
+ animation_->AttachElement(id);
+}
+
+void CompositorAnimation::DetachElement() {
+ animation_->DetachElement();
+}
+
+bool CompositorAnimation::IsElementAttached() const {
+ return !!animation_->element_id();
+}
+
+void CompositorAnimation::AddKeyframeModel(
+ std::unique_ptr<CompositorKeyframeModel> keyframe_model) {
+ animation_->AddKeyframeModel(keyframe_model->ReleaseCcKeyframeModel());
+}
+
+void CompositorAnimation::RemoveKeyframeModel(int keyframe_model_id) {
+ animation_->RemoveKeyframeModel(keyframe_model_id);
+}
+
+void CompositorAnimation::PauseKeyframeModel(int keyframe_model_id,
+ double time_offset) {
+ animation_->PauseKeyframeModel(keyframe_model_id, time_offset);
+}
+
+void CompositorAnimation::AbortKeyframeModel(int keyframe_model_id) {
+ animation_->AbortKeyframeModel(keyframe_model_id);
+}
+
+void CompositorAnimation::NotifyAnimationStarted(base::TimeTicks monotonic_time,
+ int target_property,
+ int group) {
+ if (delegate_) {
+ delegate_->NotifyAnimationStarted(
+ (monotonic_time - base::TimeTicks()).InSecondsF(), group);
+ }
+}
+
+void CompositorAnimation::NotifyAnimationFinished(
+ base::TimeTicks monotonic_time,
+ int target_property,
+ int group) {
+ if (delegate_) {
+ delegate_->NotifyAnimationFinished(
+ (monotonic_time - base::TimeTicks()).InSecondsF(), group);
+ }
+}
+
+void CompositorAnimation::NotifyAnimationAborted(base::TimeTicks monotonic_time,
+ int target_property,
+ int group) {
+ if (delegate_) {
+ delegate_->NotifyAnimationAborted(
+ (monotonic_time - base::TimeTicks()).InSecondsF(), group);
+ }
+}
+
+void CompositorAnimation::NotifyAnimationTakeover(
+ base::TimeTicks monotonic_time,
+ int target_property,
+ base::TimeTicks animation_start_time,
+ std::unique_ptr<cc::AnimationCurve> curve) {
+ if (delegate_) {
+ delegate_->NotifyAnimationTakeover(
+ (monotonic_time - base::TimeTicks()).InSecondsF(),
+ (animation_start_time - base::TimeTicks()).InSecondsF(),
+ std::move(curve));
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_animation.h b/chromium/third_party/blink/renderer/platform/animation/compositor_animation.h
new file mode 100644
index 00000000000..fed54f402e9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_animation.h
@@ -0,0 +1,82 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_ANIMATION_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_ANIMATION_H_
+
+#include <memory>
+#include "base/memory/scoped_refptr.h"
+#include "cc/animation/animation_delegate.h"
+#include "cc/animation/scroll_timeline.h"
+#include "cc/animation/single_keyframe_effect_animation.h"
+#include "cc/animation/worklet_animation.h"
+#include "third_party/blink/renderer/platform/graphics/compositor_element_id.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace cc {
+class AnimationCurve;
+}
+
+namespace blink {
+
+using CompositorScrollTimeline = cc::ScrollTimeline;
+
+class CompositorAnimationDelegate;
+class CompositorKeyframeModel;
+
+// A compositor representation for Animation.
+class PLATFORM_EXPORT CompositorAnimation : public cc::AnimationDelegate {
+ WTF_MAKE_NONCOPYABLE(CompositorAnimation);
+
+ public:
+ static std::unique_ptr<CompositorAnimation> Create();
+ static std::unique_ptr<CompositorAnimation> CreateWorkletAnimation(
+ const String& name,
+ std::unique_ptr<CompositorScrollTimeline>);
+
+ explicit CompositorAnimation(
+ scoped_refptr<cc::SingleKeyframeEffectAnimation>);
+ ~CompositorAnimation();
+
+ cc::SingleKeyframeEffectAnimation* CcAnimation() const;
+
+ // An animation delegate is notified when animations are started and stopped.
+ // The CompositorAnimation does not take ownership of the delegate, and
+ // it is the responsibility of the client to reset the layer's delegate before
+ // deleting the delegate.
+ void SetAnimationDelegate(CompositorAnimationDelegate*);
+
+ void AttachElement(const CompositorElementId&);
+ void DetachElement();
+ bool IsElementAttached() const;
+
+ void AddKeyframeModel(std::unique_ptr<CompositorKeyframeModel>);
+ void RemoveKeyframeModel(int keyframe_model_id);
+ void PauseKeyframeModel(int keyframe_model_id, double time_offset);
+ void AbortKeyframeModel(int keyframe_model_id);
+
+ private:
+ // cc::AnimationDelegate implementation.
+ void NotifyAnimationStarted(base::TimeTicks monotonic_time,
+ int target_property,
+ int group) override;
+ void NotifyAnimationFinished(base::TimeTicks monotonic_time,
+ int target_property,
+ int group) override;
+ void NotifyAnimationAborted(base::TimeTicks monotonic_time,
+ int target_property,
+ int group) override;
+ void NotifyAnimationTakeover(base::TimeTicks monotonic_time,
+ int target_property,
+ base::TimeTicks animation_start_time,
+ std::unique_ptr<cc::AnimationCurve>) override;
+
+ scoped_refptr<cc::SingleKeyframeEffectAnimation> animation_;
+ CompositorAnimationDelegate* delegate_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_ANIMATION_H_
diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_animation_client.cc b/chromium/third_party/blink/renderer/platform/animation/compositor_animation_client.cc
new file mode 100644
index 00000000000..45a3526bc24
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_animation_client.cc
@@ -0,0 +1,11 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/animation/compositor_animation_client.h"
+
+namespace blink {
+
+CompositorAnimationClient::~CompositorAnimationClient() = default;
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_animation_client.h b/chromium/third_party/blink/renderer/platform/animation/compositor_animation_client.h
new file mode 100644
index 00000000000..095c7a359d3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_animation_client.h
@@ -0,0 +1,24 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_ANIMATION_CLIENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_ANIMATION_CLIENT_H_
+
+#include "third_party/blink/public/platform/web_common.h"
+
+namespace blink {
+
+class CompositorAnimation;
+
+// A client for compositor representation of Animation.
+class BLINK_PLATFORM_EXPORT CompositorAnimationClient {
+ public:
+ virtual ~CompositorAnimationClient();
+
+ virtual CompositorAnimation* GetCompositorAnimation() const = 0;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_ANIMATION_CLIENT_H_
diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_animation_curve.h b/chromium/third_party/blink/renderer/platform/animation/compositor_animation_curve.h
new file mode 100644
index 00000000000..f64a009ea1f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_animation_curve.h
@@ -0,0 +1,26 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_ANIMATION_CURVE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_ANIMATION_CURVE_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+#include <memory>
+
+namespace cc {
+class AnimationCurve;
+}
+
+namespace blink {
+
+class PLATFORM_EXPORT CompositorAnimationCurve {
+ public:
+ virtual ~CompositorAnimationCurve() = default;
+ virtual std::unique_ptr<cc::AnimationCurve> CloneToAnimationCurve() const = 0;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_ANIMATION_CURVE_H_
diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_animation_delegate.h b/chromium/third_party/blink/renderer/platform/animation/compositor_animation_delegate.h
new file mode 100644
index 00000000000..428827f892a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_animation_delegate.h
@@ -0,0 +1,36 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_ANIMATION_DELEGATE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_ANIMATION_DELEGATE_H_
+
+#include "cc/animation/animation_curve.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+#include <memory>
+
+namespace blink {
+
+class PLATFORM_EXPORT CompositorAnimationDelegate {
+ public:
+ virtual ~CompositorAnimationDelegate() = default;
+
+ // TODO(yigu): The Notify* methods should be called from cc once per
+ // animation.
+ virtual void NotifyAnimationStarted(double monotonic_time, int group) = 0;
+ virtual void NotifyAnimationFinished(double monotonic_time, int group) = 0;
+ virtual void NotifyAnimationAborted(double monotonic_time, int group) = 0;
+ // In the current state of things, notifyAnimationTakeover only applies to
+ // scroll offset animations since main thread scrolling reasons can be added
+ // while the compositor is animating. Keeping this non-pure virtual since
+ // it doesn't apply to CSS animations.
+ virtual void NotifyAnimationTakeover(
+ double monotonic_time,
+ double animation_start_time,
+ std::unique_ptr<cc::AnimationCurve> curve) {}
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_ANIMATION_DELEGATE_H_
diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_animation_host.cc b/chromium/third_party/blink/renderer/platform/animation/compositor_animation_host.cc
new file mode 100644
index 00000000000..43badac34ea
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_animation_host.cc
@@ -0,0 +1,71 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/animation/compositor_animation_host.h"
+
+#include "cc/animation/animation_host.h"
+#include "cc/animation/scroll_offset_animations.h"
+#include "third_party/blink/renderer/platform/animation/compositor_animation_timeline.h"
+
+namespace blink {
+
+CompositorAnimationHost::CompositorAnimationHost(cc::AnimationHost* host)
+ : animation_host_(host) {
+ DCHECK(animation_host_);
+}
+
+void CompositorAnimationHost::AddTimeline(
+ const CompositorAnimationTimeline& timeline) {
+ animation_host_->AddAnimationTimeline(timeline.GetAnimationTimeline());
+}
+
+void CompositorAnimationHost::RemoveTimeline(
+ const CompositorAnimationTimeline& timeline) {
+ animation_host_->RemoveAnimationTimeline(timeline.GetAnimationTimeline());
+}
+
+void CompositorAnimationHost::AdjustImplOnlyScrollOffsetAnimation(
+ CompositorElementId element_id,
+ const gfx::Vector2dF& adjustment) {
+ animation_host_->scroll_offset_animations().AddAdjustmentUpdate(element_id,
+ adjustment);
+}
+
+void CompositorAnimationHost::TakeOverImplOnlyScrollOffsetAnimation(
+ CompositorElementId element_id) {
+ animation_host_->scroll_offset_animations().AddTakeoverUpdate(element_id);
+}
+
+void CompositorAnimationHost::SetAnimationCounts(
+ size_t total_animations_count,
+ size_t main_thread_compositable_animations_count,
+ bool current_frame_had_raf,
+ bool next_frame_has_pending_raf) {
+ animation_host_->SetAnimationCounts(
+ total_animations_count, main_thread_compositable_animations_count,
+ current_frame_had_raf, next_frame_has_pending_raf);
+}
+
+size_t CompositorAnimationHost::GetMainThreadAnimationsCountForTesting() {
+ return animation_host_->MainThreadAnimationsCount();
+}
+
+size_t
+CompositorAnimationHost::GetMainThreadCompositableAnimationsCountForTesting() {
+ return animation_host_->MainThreadCompositableAnimationsCount();
+}
+
+size_t CompositorAnimationHost::GetCompositedAnimationsCountForTesting() {
+ return animation_host_->CompositedAnimationsCount();
+}
+
+bool CompositorAnimationHost::CurrentFrameHadRAFForTesting() {
+ return animation_host_->CurrentFrameHadRAF();
+}
+
+bool CompositorAnimationHost::NextFrameHasPendingRAFForTesting() {
+ return animation_host_->NextFrameHasPendingRAF();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_animation_host.h b/chromium/third_party/blink/renderer/platform/animation/compositor_animation_host.h
new file mode 100644
index 00000000000..ec80db3e8e4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_animation_host.h
@@ -0,0 +1,50 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_ANIMATION_HOST_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_ANIMATION_HOST_H_
+
+#include "third_party/blink/renderer/platform/graphics/compositor_element_id.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "ui/gfx/geometry/vector2d.h"
+
+namespace cc {
+class AnimationHost;
+}
+
+namespace blink {
+
+class CompositorAnimationTimeline;
+
+// A compositor representation for cc::AnimationHost.
+class PLATFORM_EXPORT CompositorAnimationHost {
+ WTF_MAKE_NONCOPYABLE(CompositorAnimationHost);
+
+ public:
+ explicit CompositorAnimationHost(cc::AnimationHost*);
+
+ void AddTimeline(const CompositorAnimationTimeline&);
+ void RemoveTimeline(const CompositorAnimationTimeline&);
+
+ void AdjustImplOnlyScrollOffsetAnimation(CompositorElementId,
+ const gfx::Vector2dF& adjustment);
+ void TakeOverImplOnlyScrollOffsetAnimation(CompositorElementId);
+ void SetAnimationCounts(size_t total_animations_count,
+ size_t main_thread_compositable_animations_count,
+ bool current_frame_had_raf,
+ bool next_frame_has_pending_raf);
+ size_t GetMainThreadAnimationsCountForTesting();
+ size_t GetMainThreadCompositableAnimationsCountForTesting();
+ size_t GetCompositedAnimationsCountForTesting();
+ bool CurrentFrameHadRAFForTesting();
+ bool NextFrameHasPendingRAFForTesting();
+
+ private:
+ cc::AnimationHost* animation_host_;
+};
+
+} // namespace blink
+
+#endif // CompositorAnimationTimeline_h
diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_animation_host_test.cc b/chromium/third_party/blink/renderer/platform/animation/compositor_animation_host_test.cc
new file mode 100644
index 00000000000..569889d559c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_animation_host_test.cc
@@ -0,0 +1,36 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/animation/compositor_animation_host.h"
+
+#include <memory>
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/animation/compositor_animation_timeline.h"
+#include "third_party/blink/renderer/platform/testing/compositor_test.h"
+#include "third_party/blink/renderer/platform/testing/web_layer_tree_view_impl_for_testing.h"
+
+namespace blink {
+
+class CompositorAnimationHostTest : public CompositorTest {};
+
+TEST_F(CompositorAnimationHostTest, AnimationHostNullWhenTimelineDetached) {
+ std::unique_ptr<CompositorAnimationTimeline> timeline =
+ CompositorAnimationTimeline::Create();
+
+ scoped_refptr<cc::AnimationTimeline> cc_timeline =
+ timeline->GetAnimationTimeline();
+ EXPECT_FALSE(cc_timeline->animation_host());
+
+ WebLayerTreeViewImplForTesting layer_tree_view;
+ CompositorAnimationHost compositor_animation_host(
+ layer_tree_view.CompositorAnimationHost());
+
+ compositor_animation_host.AddTimeline(*timeline);
+ EXPECT_TRUE(cc_timeline->animation_host());
+
+ compositor_animation_host.RemoveTimeline(*timeline);
+ EXPECT_FALSE(cc_timeline->animation_host());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_animation_test.cc b/chromium/third_party/blink/renderer/platform/animation/compositor_animation_test.cc
new file mode 100644
index 00000000000..99644785f25
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_animation_test.cc
@@ -0,0 +1,142 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/animation/compositor_animation.h"
+
+#include "base/time/time.h"
+#include "third_party/blink/renderer/platform/animation/compositor_animation_client.h"
+#include "third_party/blink/renderer/platform/animation/compositor_animation_delegate.h"
+#include "third_party/blink/renderer/platform/animation/compositor_animation_timeline.h"
+#include "third_party/blink/renderer/platform/animation/compositor_float_animation_curve.h"
+#include "third_party/blink/renderer/platform/animation/compositor_keyframe_model.h"
+#include "third_party/blink/renderer/platform/animation/compositor_target_property.h"
+#include "third_party/blink/renderer/platform/testing/compositor_test.h"
+
+#include <memory>
+
+namespace blink {
+
+class CompositorAnimationDelegateForTesting
+ : public CompositorAnimationDelegate {
+ public:
+ CompositorAnimationDelegateForTesting() { ResetFlags(); }
+
+ void ResetFlags() {
+ started_ = false;
+ finished_ = false;
+ aborted_ = false;
+ }
+
+ void NotifyAnimationStarted(double, int) override { started_ = true; }
+ void NotifyAnimationFinished(double, int) override { finished_ = true; }
+ void NotifyAnimationAborted(double, int) override { aborted_ = true; }
+
+ bool started_;
+ bool finished_;
+ bool aborted_;
+};
+
+class CompositorAnimationTestClient : public CompositorAnimationClient {
+ public:
+ CompositorAnimationTestClient() : animation_(CompositorAnimation::Create()) {}
+
+ CompositorAnimation* GetCompositorAnimation() const override {
+ return animation_.get();
+ }
+
+ std::unique_ptr<CompositorAnimation> animation_;
+};
+
+class CompositorAnimationTest : public CompositorTest {};
+
+// Test that when the animation delegate is null, the animation animation
+// doesn't forward the finish notification.
+TEST_F(CompositorAnimationTest, NullDelegate) {
+ std::unique_ptr<CompositorAnimationDelegateForTesting> delegate(
+ new CompositorAnimationDelegateForTesting);
+
+ std::unique_ptr<CompositorAnimation> animation =
+ CompositorAnimation::Create();
+ cc::SingleKeyframeEffectAnimation* cc_animation = animation->CcAnimation();
+
+ std::unique_ptr<CompositorAnimationCurve> curve =
+ CompositorFloatAnimationCurve::Create();
+ std::unique_ptr<CompositorKeyframeModel> keyframe_model =
+ CompositorKeyframeModel::Create(
+ *curve, CompositorTargetProperty::TRANSFORM, 1, 0);
+ animation->AddKeyframeModel(std::move(keyframe_model));
+
+ animation->SetAnimationDelegate(delegate.get());
+ EXPECT_FALSE(delegate->finished_);
+
+ cc_animation->NotifyKeyframeModelFinishedForTesting(
+ CompositorTargetProperty::TRANSFORM, 1);
+ EXPECT_TRUE(delegate->finished_);
+
+ delegate->ResetFlags();
+
+ animation->SetAnimationDelegate(nullptr);
+ cc_animation->NotifyKeyframeModelFinishedForTesting(
+ CompositorTargetProperty::TRANSFORM, 1);
+ EXPECT_FALSE(delegate->finished_);
+}
+
+TEST_F(CompositorAnimationTest, NotifyFromCCAfterCompositorAnimationDeletion) {
+ std::unique_ptr<CompositorAnimationDelegateForTesting> delegate(
+ new CompositorAnimationDelegateForTesting);
+
+ std::unique_ptr<CompositorAnimation> animation =
+ CompositorAnimation::Create();
+ scoped_refptr<cc::SingleKeyframeEffectAnimation> cc_animation =
+ animation->CcAnimation();
+
+ std::unique_ptr<CompositorAnimationCurve> curve =
+ CompositorFloatAnimationCurve::Create();
+ std::unique_ptr<CompositorKeyframeModel> keyframe_model =
+ CompositorKeyframeModel::Create(*curve, CompositorTargetProperty::OPACITY,
+ 1, 0);
+ animation->AddKeyframeModel(std::move(keyframe_model));
+
+ animation->SetAnimationDelegate(delegate.get());
+ EXPECT_FALSE(delegate->finished_);
+
+ cc_animation->NotifyKeyframeModelFinishedForTesting(
+ CompositorTargetProperty::OPACITY, 1);
+ EXPECT_TRUE(delegate->finished_);
+ delegate->finished_ = false;
+
+ // Delete CompositorAnimation. ccAnimation stays alive.
+ animation = nullptr;
+
+ // No notifications. Doesn't crash.
+ cc_animation->NotifyKeyframeModelFinishedForTesting(
+ CompositorTargetProperty::OPACITY, 1);
+ EXPECT_FALSE(delegate->finished_);
+}
+
+TEST_F(CompositorAnimationTest,
+ CompositorAnimationDeletionDetachesFromCCTimeline) {
+ std::unique_ptr<CompositorAnimationTimeline> timeline =
+ CompositorAnimationTimeline::Create();
+ std::unique_ptr<CompositorAnimationTestClient> client(
+ new CompositorAnimationTestClient);
+
+ scoped_refptr<cc::AnimationTimeline> cc_timeline =
+ timeline->GetAnimationTimeline();
+ scoped_refptr<cc::SingleKeyframeEffectAnimation> cc_animation =
+ client->animation_->CcAnimation();
+ EXPECT_FALSE(cc_animation->animation_timeline());
+
+ timeline->AnimationAttached(*client);
+ EXPECT_TRUE(cc_animation->animation_timeline());
+ EXPECT_TRUE(cc_timeline->GetAnimationById(cc_animation->id()));
+
+ // Delete client and CompositorAnimation while attached to timeline.
+ client = nullptr;
+
+ EXPECT_FALSE(cc_animation->animation_timeline());
+ EXPECT_FALSE(cc_timeline->GetAnimationById(cc_animation->id()));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_animation_timeline.cc b/chromium/third_party/blink/renderer/platform/animation/compositor_animation_timeline.cc
new file mode 100644
index 00000000000..228f8a653df
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_animation_timeline.cc
@@ -0,0 +1,48 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/animation/compositor_animation_timeline.h"
+
+#include "cc/animation/animation_host.h"
+#include "cc/animation/animation_id_provider.h"
+#include "third_party/blink/renderer/platform/animation/compositor_animation.h"
+#include "third_party/blink/renderer/platform/animation/compositor_animation_client.h"
+#include "third_party/blink/renderer/platform/animation/compositor_animation_host.h"
+
+namespace blink {
+
+CompositorAnimationTimeline::CompositorAnimationTimeline()
+ : animation_timeline_(cc::AnimationTimeline::Create(
+ cc::AnimationIdProvider::NextTimelineId())) {}
+
+CompositorAnimationTimeline::~CompositorAnimationTimeline() {
+ // Detach timeline from host, otherwise it stays there (leaks) until
+ // compositor shutdown.
+ if (animation_timeline_->animation_host())
+ animation_timeline_->animation_host()->RemoveAnimationTimeline(
+ animation_timeline_);
+}
+
+cc::AnimationTimeline* CompositorAnimationTimeline::GetAnimationTimeline()
+ const {
+ return animation_timeline_.get();
+}
+
+void CompositorAnimationTimeline::AnimationAttached(
+ const blink::CompositorAnimationClient& client) {
+ if (client.GetCompositorAnimation()) {
+ animation_timeline_->AttachAnimation(
+ client.GetCompositorAnimation()->CcAnimation());
+ }
+}
+
+void CompositorAnimationTimeline::AnimationDestroyed(
+ const blink::CompositorAnimationClient& client) {
+ if (client.GetCompositorAnimation()) {
+ animation_timeline_->DetachAnimation(
+ client.GetCompositorAnimation()->CcAnimation());
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_animation_timeline.h b/chromium/third_party/blink/renderer/platform/animation/compositor_animation_timeline.h
new file mode 100644
index 00000000000..1d0ab8680bd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_animation_timeline.h
@@ -0,0 +1,44 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_ANIMATION_TIMELINE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_ANIMATION_TIMELINE_H_
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "base/memory/scoped_refptr.h"
+#include "cc/animation/animation_timeline.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class CompositorAnimationClient;
+
+// A compositor representation for cc::AnimationTimeline.
+class PLATFORM_EXPORT CompositorAnimationTimeline {
+ WTF_MAKE_NONCOPYABLE(CompositorAnimationTimeline);
+
+ public:
+ static std::unique_ptr<CompositorAnimationTimeline> Create() {
+ return base::WrapUnique(new CompositorAnimationTimeline());
+ }
+
+ ~CompositorAnimationTimeline();
+
+ cc::AnimationTimeline* GetAnimationTimeline() const;
+
+ void AnimationAttached(const CompositorAnimationClient&);
+ void AnimationDestroyed(const CompositorAnimationClient&);
+
+ private:
+ CompositorAnimationTimeline();
+
+ scoped_refptr<cc::AnimationTimeline> animation_timeline_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_ANIMATION_TIMELINE_H_
diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_animation_timeline_test.cc b/chromium/third_party/blink/renderer/platform/animation/compositor_animation_timeline_test.cc
new file mode 100644
index 00000000000..d35bee94611
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_animation_timeline_test.cc
@@ -0,0 +1,44 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/animation/compositor_animation_timeline.h"
+
+#include <memory>
+#include "base/memory/scoped_refptr.h"
+#include "cc/animation/animation_host.h"
+#include "third_party/blink/renderer/platform/animation/compositor_animation.h"
+#include "third_party/blink/renderer/platform/animation/compositor_animation_host.h"
+#include "third_party/blink/renderer/platform/testing/compositor_test.h"
+#include "third_party/blink/renderer/platform/testing/web_layer_tree_view_impl_for_testing.h"
+
+namespace blink {
+
+class CompositorAnimationTimelineTest : public CompositorTest {};
+
+TEST_F(CompositorAnimationTimelineTest,
+ CompositorTimelineDeletionDetachesFromAnimationHost) {
+ std::unique_ptr<CompositorAnimationTimeline> timeline =
+ CompositorAnimationTimeline::Create();
+
+ scoped_refptr<cc::AnimationTimeline> cc_timeline =
+ timeline->GetAnimationTimeline();
+ EXPECT_FALSE(cc_timeline->animation_host());
+
+ WebLayerTreeViewImplForTesting layer_tree_view;
+ CompositorAnimationHost compositor_animation_host(
+ layer_tree_view.CompositorAnimationHost());
+
+ compositor_animation_host.AddTimeline(*timeline);
+ cc::AnimationHost* animation_host = cc_timeline->animation_host();
+ EXPECT_TRUE(animation_host);
+ EXPECT_TRUE(animation_host->GetTimelineById(cc_timeline->id()));
+
+ // Delete CompositorAnimationTimeline while attached to host.
+ timeline = nullptr;
+
+ EXPECT_FALSE(cc_timeline->animation_host());
+ EXPECT_FALSE(animation_host->GetTimelineById(cc_timeline->id()));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_filter_animation_curve.cc b/chromium/third_party/blink/renderer/platform/animation/compositor_filter_animation_curve.cc
new file mode 100644
index 00000000000..eefee742789
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_filter_animation_curve.cc
@@ -0,0 +1,38 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/animation/compositor_filter_animation_curve.h"
+
+#include "cc/animation/keyframed_animation_curve.h"
+#include "cc/animation/timing_function.h"
+#include "cc/paint/filter_operations.h"
+#include "third_party/blink/renderer/platform/graphics/compositor_filter_operations.h"
+
+namespace blink {
+
+CompositorFilterAnimationCurve::CompositorFilterAnimationCurve()
+ : curve_(cc::KeyframedFilterAnimationCurve::Create()) {}
+
+CompositorFilterAnimationCurve::~CompositorFilterAnimationCurve() = default;
+
+void CompositorFilterAnimationCurve::AddKeyframe(
+ const CompositorFilterKeyframe& keyframe) {
+ curve_->AddKeyframe(keyframe.CloneToCC());
+}
+
+void CompositorFilterAnimationCurve::SetTimingFunction(
+ const TimingFunction& timing_function) {
+ curve_->SetTimingFunction(timing_function.CloneToCC());
+}
+
+void CompositorFilterAnimationCurve::SetScaledDuration(double scaled_duration) {
+ curve_->set_scaled_duration(scaled_duration);
+}
+
+std::unique_ptr<cc::AnimationCurve>
+CompositorFilterAnimationCurve::CloneToAnimationCurve() const {
+ return curve_->Clone();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_filter_animation_curve.h b/chromium/third_party/blink/renderer/platform/animation/compositor_filter_animation_curve.h
new file mode 100644
index 00000000000..d7769adfc01
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_filter_animation_curve.h
@@ -0,0 +1,53 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_FILTER_ANIMATION_CURVE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_FILTER_ANIMATION_CURVE_H_
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/platform/animation/compositor_animation_curve.h"
+#include "third_party/blink/renderer/platform/animation/compositor_filter_keyframe.h"
+#include "third_party/blink/renderer/platform/animation/timing_function.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace cc {
+class KeyframedFilterAnimationCurve;
+}
+
+namespace blink {
+class CompositorFilterKeyframe;
+}
+
+namespace blink {
+
+// A keyframed filter animation curve.
+class PLATFORM_EXPORT CompositorFilterAnimationCurve
+ : public CompositorAnimationCurve {
+ WTF_MAKE_NONCOPYABLE(CompositorFilterAnimationCurve);
+
+ public:
+ static std::unique_ptr<CompositorFilterAnimationCurve> Create() {
+ return base::WrapUnique(new CompositorFilterAnimationCurve());
+ }
+ ~CompositorFilterAnimationCurve() override;
+
+ void AddKeyframe(const CompositorFilterKeyframe&);
+ void SetTimingFunction(const TimingFunction&);
+ void SetScaledDuration(double);
+
+ // blink::CompositorAnimationCurve implementation.
+ std::unique_ptr<cc::AnimationCurve> CloneToAnimationCurve() const override;
+
+ private:
+ CompositorFilterAnimationCurve();
+
+ std::unique_ptr<cc::KeyframedFilterAnimationCurve> curve_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_FILTER_ANIMATION_CURVE_H_
diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_filter_keyframe.cc b/chromium/third_party/blink/renderer/platform/animation/compositor_filter_keyframe.cc
new file mode 100644
index 00000000000..2a7eb269082
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_filter_keyframe.cc
@@ -0,0 +1,36 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/animation/compositor_filter_keyframe.h"
+
+#include <memory>
+#include "third_party/blink/renderer/platform/animation/timing_function.h"
+
+namespace blink {
+
+CompositorFilterKeyframe::CompositorFilterKeyframe(
+ double time,
+ CompositorFilterOperations value,
+ const TimingFunction& timing_function)
+ : filter_keyframe_(
+ cc::FilterKeyframe::Create(base::TimeDelta::FromSecondsD(time),
+ value.ReleaseCcFilterOperations(),
+ timing_function.CloneToCC())) {}
+
+CompositorFilterKeyframe::~CompositorFilterKeyframe() = default;
+
+double CompositorFilterKeyframe::Time() const {
+ return filter_keyframe_->Time().InSecondsF();
+}
+
+const cc::TimingFunction* CompositorFilterKeyframe::CcTimingFunction() const {
+ return filter_keyframe_->timing_function();
+}
+
+std::unique_ptr<cc::FilterKeyframe> CompositorFilterKeyframe::CloneToCC()
+ const {
+ return filter_keyframe_->Clone();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_filter_keyframe.h b/chromium/third_party/blink/renderer/platform/animation/compositor_filter_keyframe.h
new file mode 100644
index 00000000000..f26806a9d77
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_filter_keyframe.h
@@ -0,0 +1,39 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_FILTER_KEYFRAME_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_FILTER_KEYFRAME_H_
+
+#include "cc/animation/keyframed_animation_curve.h"
+#include "third_party/blink/renderer/platform/animation/compositor_keyframe.h"
+#include "third_party/blink/renderer/platform/graphics/compositor_filter_operations.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class TimingFunction;
+
+class PLATFORM_EXPORT CompositorFilterKeyframe : public CompositorKeyframe {
+ WTF_MAKE_NONCOPYABLE(CompositorFilterKeyframe);
+
+ public:
+ CompositorFilterKeyframe(double time,
+ CompositorFilterOperations value,
+ const TimingFunction&);
+ ~CompositorFilterKeyframe();
+
+ std::unique_ptr<cc::FilterKeyframe> CloneToCC() const;
+
+ // CompositorKeyframe implementation.
+ double Time() const override;
+ const cc::TimingFunction* CcTimingFunction() const override;
+
+ private:
+ std::unique_ptr<cc::FilterKeyframe> filter_keyframe_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_FILTER_KEYFRAME_H_
diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_float_animation_curve.cc b/chromium/third_party/blink/renderer/platform/animation/compositor_float_animation_curve.cc
new file mode 100644
index 00000000000..1e405f45c57
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_float_animation_curve.cc
@@ -0,0 +1,71 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/animation/compositor_float_animation_curve.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "cc/animation/animation_curve.h"
+#include "cc/animation/keyframed_animation_curve.h"
+#include "cc/animation/timing_function.h"
+
+namespace blink {
+
+CompositorFloatAnimationCurve::CompositorFloatAnimationCurve()
+ : curve_(cc::KeyframedFloatAnimationCurve::Create()) {}
+
+CompositorFloatAnimationCurve::CompositorFloatAnimationCurve(
+ std::unique_ptr<cc::KeyframedFloatAnimationCurve> curve)
+ : curve_(std::move(curve)) {}
+
+CompositorFloatAnimationCurve::~CompositorFloatAnimationCurve() = default;
+
+std::unique_ptr<CompositorFloatAnimationCurve>
+CompositorFloatAnimationCurve::CreateForTesting(
+ std::unique_ptr<cc::KeyframedFloatAnimationCurve> curve) {
+ return base::WrapUnique(new CompositorFloatAnimationCurve(std::move(curve)));
+}
+
+CompositorFloatAnimationCurve::Keyframes
+CompositorFloatAnimationCurve::KeyframesForTesting() const {
+ Keyframes keyframes;
+ for (const auto& cc_keyframe : curve_->keyframes_for_testing()) {
+ keyframes.push_back(
+ base::WrapUnique(new CompositorFloatKeyframe(cc_keyframe->Clone())));
+ }
+ return keyframes;
+}
+
+scoped_refptr<TimingFunction>
+CompositorFloatAnimationCurve::GetTimingFunctionForTesting() const {
+ return CreateCompositorTimingFunctionFromCC(
+ curve_->timing_function_for_testing());
+}
+
+void CompositorFloatAnimationCurve::AddKeyframe(
+ const CompositorFloatKeyframe& keyframe) {
+ curve_->AddKeyframe(keyframe.CloneToCC());
+}
+
+void CompositorFloatAnimationCurve::SetTimingFunction(
+ const TimingFunction& timing_function) {
+ curve_->SetTimingFunction(timing_function.CloneToCC());
+}
+
+void CompositorFloatAnimationCurve::SetScaledDuration(double scaled_duration) {
+ curve_->set_scaled_duration(scaled_duration);
+}
+
+float CompositorFloatAnimationCurve::GetValue(double time) const {
+ return curve_->GetValue(base::TimeDelta::FromSecondsD(time));
+}
+
+std::unique_ptr<cc::AnimationCurve>
+CompositorFloatAnimationCurve::CloneToAnimationCurve() const {
+ return curve_->Clone();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_float_animation_curve.h b/chromium/third_party/blink/renderer/platform/animation/compositor_float_animation_curve.h
new file mode 100644
index 00000000000..97bb9d524f0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_float_animation_curve.h
@@ -0,0 +1,67 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_FLOAT_ANIMATION_CURVE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_FLOAT_ANIMATION_CURVE_H_
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/animation/compositor_animation_curve.h"
+#include "third_party/blink/renderer/platform/animation/compositor_float_keyframe.h"
+#include "third_party/blink/renderer/platform/animation/timing_function.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace cc {
+class KeyframedFloatAnimationCurve;
+}
+
+namespace blink {
+class CompositorFloatKeyframe;
+}
+
+namespace blink {
+
+// A keyframed float animation curve.
+class PLATFORM_EXPORT CompositorFloatAnimationCurve
+ : public CompositorAnimationCurve {
+ WTF_MAKE_NONCOPYABLE(CompositorFloatAnimationCurve);
+
+ public:
+ static std::unique_ptr<CompositorFloatAnimationCurve> Create() {
+ return base::WrapUnique(new CompositorFloatAnimationCurve());
+ }
+
+ ~CompositorFloatAnimationCurve() override;
+
+ void AddKeyframe(const CompositorFloatKeyframe&);
+ void SetTimingFunction(const TimingFunction&);
+ void SetScaledDuration(double);
+ float GetValue(double time) const;
+
+ // CompositorAnimationCurve implementation.
+ std::unique_ptr<cc::AnimationCurve> CloneToAnimationCurve() const override;
+
+ static std::unique_ptr<CompositorFloatAnimationCurve> CreateForTesting(
+ std::unique_ptr<cc::KeyframedFloatAnimationCurve>);
+
+ using Keyframes = Vector<std::unique_ptr<CompositorFloatKeyframe>>;
+ Keyframes KeyframesForTesting() const;
+
+ scoped_refptr<TimingFunction> GetTimingFunctionForTesting() const;
+
+ private:
+ CompositorFloatAnimationCurve();
+ CompositorFloatAnimationCurve(
+ std::unique_ptr<cc::KeyframedFloatAnimationCurve>);
+
+ std::unique_ptr<cc::KeyframedFloatAnimationCurve> curve_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_FLOAT_ANIMATION_CURVE_H_
diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_float_animation_curve_test.cc b/chromium/third_party/blink/renderer/platform/animation/compositor_float_animation_curve_test.cc
new file mode 100644
index 00000000000..e9693a2160c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_float_animation_curve_test.cc
@@ -0,0 +1,267 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/animation/compositor_float_animation_curve.h"
+
+#include "cc/animation/timing_function.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include <memory>
+
+using blink::CompositorAnimationCurve;
+using blink::CompositorFloatAnimationCurve;
+using blink::CompositorFloatKeyframe;
+
+namespace blink {
+
+// Tests that a float animation with one keyframe works as expected.
+TEST(WebFloatAnimationCurveTest, OneFloatKeyframe) {
+ std::unique_ptr<CompositorFloatAnimationCurve> curve =
+ CompositorFloatAnimationCurve::Create();
+ curve->AddKeyframe(
+ CompositorFloatKeyframe(0, 2, *LinearTimingFunction::Shared()));
+ EXPECT_FLOAT_EQ(2, curve->GetValue(-1));
+ EXPECT_FLOAT_EQ(2, curve->GetValue(0));
+ EXPECT_FLOAT_EQ(2, curve->GetValue(0.5));
+ EXPECT_FLOAT_EQ(2, curve->GetValue(1));
+ EXPECT_FLOAT_EQ(2, curve->GetValue(2));
+}
+
+// Tests that a float animation with two keyframes works as expected.
+TEST(WebFloatAnimationCurveTest, TwoFloatKeyframe) {
+ std::unique_ptr<CompositorFloatAnimationCurve> curve =
+ CompositorFloatAnimationCurve::Create();
+ curve->AddKeyframe(
+ CompositorFloatKeyframe(0, 2, *LinearTimingFunction::Shared()));
+ curve->AddKeyframe(
+ CompositorFloatKeyframe(1, 4, *LinearTimingFunction::Shared()));
+ EXPECT_FLOAT_EQ(2, curve->GetValue(-1));
+ EXPECT_FLOAT_EQ(2, curve->GetValue(0));
+ EXPECT_FLOAT_EQ(3, curve->GetValue(0.5));
+ EXPECT_FLOAT_EQ(4, curve->GetValue(1));
+ EXPECT_FLOAT_EQ(4, curve->GetValue(2));
+}
+
+// Tests that a float animation with three keyframes works as expected.
+TEST(WebFloatAnimationCurveTest, ThreeFloatKeyframe) {
+ std::unique_ptr<CompositorFloatAnimationCurve> curve =
+ CompositorFloatAnimationCurve::Create();
+ curve->AddKeyframe(
+ CompositorFloatKeyframe(0, 2, *LinearTimingFunction::Shared()));
+ curve->AddKeyframe(
+ CompositorFloatKeyframe(1, 4, *LinearTimingFunction::Shared()));
+ curve->AddKeyframe(
+ CompositorFloatKeyframe(2, 8, *LinearTimingFunction::Shared()));
+ EXPECT_FLOAT_EQ(2, curve->GetValue(-1));
+ EXPECT_FLOAT_EQ(2, curve->GetValue(0));
+ EXPECT_FLOAT_EQ(3, curve->GetValue(0.5));
+ EXPECT_FLOAT_EQ(4, curve->GetValue(1));
+ EXPECT_FLOAT_EQ(6, curve->GetValue(1.5));
+ EXPECT_FLOAT_EQ(8, curve->GetValue(2));
+ EXPECT_FLOAT_EQ(8, curve->GetValue(3));
+}
+
+// Tests that a float animation with multiple keys at a given time works sanely.
+TEST(WebFloatAnimationCurveTest, RepeatedFloatKeyTimes) {
+ std::unique_ptr<CompositorFloatAnimationCurve> curve =
+ CompositorFloatAnimationCurve::Create();
+ curve->AddKeyframe(
+ CompositorFloatKeyframe(0, 4, *LinearTimingFunction::Shared()));
+ curve->AddKeyframe(
+ CompositorFloatKeyframe(1, 4, *LinearTimingFunction::Shared()));
+ curve->AddKeyframe(
+ CompositorFloatKeyframe(1, 6, *LinearTimingFunction::Shared()));
+ curve->AddKeyframe(
+ CompositorFloatKeyframe(2, 6, *LinearTimingFunction::Shared()));
+
+ EXPECT_FLOAT_EQ(4, curve->GetValue(-1));
+ EXPECT_FLOAT_EQ(4, curve->GetValue(0));
+ EXPECT_FLOAT_EQ(4, curve->GetValue(0.5));
+
+ // There is a discontinuity at 1. Any value between 4 and 6 is valid.
+ float value = curve->GetValue(1);
+ EXPECT_TRUE(value >= 4 && value <= 6);
+
+ EXPECT_FLOAT_EQ(6, curve->GetValue(1.5));
+ EXPECT_FLOAT_EQ(6, curve->GetValue(2));
+ EXPECT_FLOAT_EQ(6, curve->GetValue(3));
+}
+
+// Tests that the keyframes may be added out of order.
+TEST(WebFloatAnimationCurveTest, UnsortedKeyframes) {
+ std::unique_ptr<CompositorFloatAnimationCurve> curve =
+ CompositorFloatAnimationCurve::Create();
+ curve->AddKeyframe(
+ CompositorFloatKeyframe(2, 8, *LinearTimingFunction::Shared()));
+ curve->AddKeyframe(
+ CompositorFloatKeyframe(0, 2, *LinearTimingFunction::Shared()));
+ curve->AddKeyframe(
+ CompositorFloatKeyframe(1, 4, *LinearTimingFunction::Shared()));
+
+ EXPECT_FLOAT_EQ(2, curve->GetValue(-1));
+ EXPECT_FLOAT_EQ(2, curve->GetValue(0));
+ EXPECT_FLOAT_EQ(3, curve->GetValue(0.5));
+ EXPECT_FLOAT_EQ(4, curve->GetValue(1));
+ EXPECT_FLOAT_EQ(6, curve->GetValue(1.5));
+ EXPECT_FLOAT_EQ(8, curve->GetValue(2));
+ EXPECT_FLOAT_EQ(8, curve->GetValue(3));
+}
+
+// Tests that a cubic bezier timing function works as expected.
+TEST(WebFloatAnimationCurveTest, CubicBezierTimingFunction) {
+ std::unique_ptr<CompositorFloatAnimationCurve> curve =
+ CompositorFloatAnimationCurve::Create();
+ scoped_refptr<CubicBezierTimingFunction> cubic =
+ CubicBezierTimingFunction::Create(0.25, 0, 0.75, 1);
+ curve->AddKeyframe(CompositorFloatKeyframe(0, 0, *cubic));
+ curve->AddKeyframe(
+ CompositorFloatKeyframe(1, 1, *LinearTimingFunction::Shared()));
+
+ EXPECT_FLOAT_EQ(0, curve->GetValue(0));
+ EXPECT_LT(0, curve->GetValue(0.25));
+ EXPECT_GT(0.25, curve->GetValue(0.25));
+ EXPECT_NEAR(curve->GetValue(0.5), 0.5, 0.00015);
+ EXPECT_LT(0.75, curve->GetValue(0.75));
+ EXPECT_GT(1, curve->GetValue(0.75));
+ EXPECT_FLOAT_EQ(1, curve->GetValue(1));
+}
+
+// Tests that an ease timing function works as expected.
+TEST(WebFloatAnimationCurveTest, EaseTimingFunction) {
+ std::unique_ptr<CompositorFloatAnimationCurve> curve =
+ CompositorFloatAnimationCurve::Create();
+ curve->AddKeyframe(
+ CompositorFloatKeyframe(0, 0,
+ *CubicBezierTimingFunction::Preset(
+ CubicBezierTimingFunction::EaseType::EASE)));
+ curve->AddKeyframe(
+ CompositorFloatKeyframe(1, 1, *LinearTimingFunction::Shared()));
+
+ std::unique_ptr<cc::TimingFunction> timing_function(
+ cc::CubicBezierTimingFunction::CreatePreset(
+ CubicBezierTimingFunction::EaseType::EASE));
+ for (int i = 0; i <= 4; ++i) {
+ const double time = i * 0.25;
+ EXPECT_FLOAT_EQ(timing_function->GetValue(time), curve->GetValue(time));
+ }
+}
+
+// Tests using a linear timing function.
+TEST(WebFloatAnimationCurveTest, LinearTimingFunction) {
+ std::unique_ptr<CompositorFloatAnimationCurve> curve =
+ CompositorFloatAnimationCurve::Create();
+ curve->AddKeyframe(
+ CompositorFloatKeyframe(0, 0, *LinearTimingFunction::Shared()));
+ curve->AddKeyframe(
+ CompositorFloatKeyframe(1, 1, *LinearTimingFunction::Shared()));
+
+ for (int i = 0; i <= 4; ++i) {
+ const double time = i * 0.25;
+ EXPECT_FLOAT_EQ(time, curve->GetValue(time));
+ }
+}
+
+// Tests that an ease in timing function works as expected.
+TEST(WebFloatAnimationCurveTest, EaseInTimingFunction) {
+ std::unique_ptr<CompositorFloatAnimationCurve> curve =
+ CompositorFloatAnimationCurve::Create();
+ curve->AddKeyframe(CompositorFloatKeyframe(
+ 0, 0,
+ *CubicBezierTimingFunction::Preset(
+ CubicBezierTimingFunction::EaseType::EASE_IN)));
+ curve->AddKeyframe(
+ CompositorFloatKeyframe(1, 1, *LinearTimingFunction::Shared()));
+
+ std::unique_ptr<cc::TimingFunction> timing_function(
+ cc::CubicBezierTimingFunction::CreatePreset(
+ CubicBezierTimingFunction::EaseType::EASE_IN));
+ for (int i = 0; i <= 4; ++i) {
+ const double time = i * 0.25;
+ EXPECT_FLOAT_EQ(timing_function->GetValue(time), curve->GetValue(time));
+ }
+}
+
+// Tests that an ease in timing function works as expected.
+TEST(WebFloatAnimationCurveTest, EaseOutTimingFunction) {
+ std::unique_ptr<CompositorFloatAnimationCurve> curve =
+ CompositorFloatAnimationCurve::Create();
+ curve->AddKeyframe(CompositorFloatKeyframe(
+ 0, 0,
+ *CubicBezierTimingFunction::Preset(
+ CubicBezierTimingFunction::EaseType::EASE_OUT)));
+ curve->AddKeyframe(
+ CompositorFloatKeyframe(1, 1, *LinearTimingFunction::Shared()));
+
+ std::unique_ptr<cc::TimingFunction> timing_function(
+ cc::CubicBezierTimingFunction::CreatePreset(
+ CubicBezierTimingFunction::EaseType::EASE_OUT));
+ for (int i = 0; i <= 4; ++i) {
+ const double time = i * 0.25;
+ EXPECT_FLOAT_EQ(timing_function->GetValue(time), curve->GetValue(time));
+ }
+}
+
+// Tests that an ease in timing function works as expected.
+TEST(WebFloatAnimationCurveTest, EaseInOutTimingFunction) {
+ std::unique_ptr<CompositorFloatAnimationCurve> curve =
+ CompositorFloatAnimationCurve::Create();
+ curve->AddKeyframe(CompositorFloatKeyframe(
+ 0, 0,
+ *CubicBezierTimingFunction::Preset(
+ CubicBezierTimingFunction::EaseType::EASE_IN_OUT)));
+ curve->AddKeyframe(
+ CompositorFloatKeyframe(1, 1, *LinearTimingFunction::Shared()));
+
+ std::unique_ptr<cc::TimingFunction> timing_function(
+ cc::CubicBezierTimingFunction::CreatePreset(
+ CubicBezierTimingFunction::EaseType::EASE_IN_OUT));
+ for (int i = 0; i <= 4; ++i) {
+ const double time = i * 0.25;
+ EXPECT_FLOAT_EQ(timing_function->GetValue(time), curve->GetValue(time));
+ }
+}
+
+// Tests that an ease in timing function works as expected.
+TEST(WebFloatAnimationCurveTest, CustomBezierTimingFunction) {
+ std::unique_ptr<CompositorFloatAnimationCurve> curve =
+ CompositorFloatAnimationCurve::Create();
+ double x1 = 0.3;
+ double y1 = 0.2;
+ double x2 = 0.8;
+ double y2 = 0.7;
+ scoped_refptr<CubicBezierTimingFunction> cubic =
+ CubicBezierTimingFunction::Create(x1, y1, x2, y2);
+ curve->AddKeyframe(CompositorFloatKeyframe(0, 0, *cubic));
+ curve->AddKeyframe(
+ CompositorFloatKeyframe(1, 1, *LinearTimingFunction::Shared()));
+
+ std::unique_ptr<cc::TimingFunction> timing_function(
+ cc::CubicBezierTimingFunction::Create(x1, y1, x2, y2));
+ for (int i = 0; i <= 4; ++i) {
+ const double time = i * 0.25;
+ EXPECT_FLOAT_EQ(timing_function->GetValue(time), curve->GetValue(time));
+ }
+}
+
+// Tests that the default timing function is indeed ease.
+TEST(WebFloatAnimationCurveTest, DefaultTimingFunction) {
+ std::unique_ptr<CompositorFloatAnimationCurve> curve =
+ CompositorFloatAnimationCurve::Create();
+ curve->AddKeyframe(
+ CompositorFloatKeyframe(0, 0,
+ *CubicBezierTimingFunction::Preset(
+ CubicBezierTimingFunction::EaseType::EASE)));
+ curve->AddKeyframe(
+ CompositorFloatKeyframe(1, 1, *LinearTimingFunction::Shared()));
+
+ std::unique_ptr<cc::TimingFunction> timing_function(
+ cc::CubicBezierTimingFunction::CreatePreset(
+ CubicBezierTimingFunction::EaseType::EASE));
+ for (int i = 0; i <= 4; ++i) {
+ const double time = i * 0.25;
+ EXPECT_FLOAT_EQ(timing_function->GetValue(time), curve->GetValue(time));
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_float_keyframe.cc b/chromium/third_party/blink/renderer/platform/animation/compositor_float_keyframe.cc
new file mode 100644
index 00000000000..1dabab2d42b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_float_keyframe.cc
@@ -0,0 +1,38 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/animation/compositor_float_keyframe.h"
+
+#include "third_party/blink/renderer/platform/animation/timing_function.h"
+
+namespace blink {
+
+CompositorFloatKeyframe::CompositorFloatKeyframe(
+ double time,
+ float value,
+ const TimingFunction& timing_function)
+ : float_keyframe_(
+ cc::FloatKeyframe::Create(base::TimeDelta::FromSecondsD(time),
+ value,
+ timing_function.CloneToCC())) {}
+
+CompositorFloatKeyframe::CompositorFloatKeyframe(
+ std::unique_ptr<cc::FloatKeyframe> float_keyframe)
+ : float_keyframe_(std::move(float_keyframe)) {}
+
+CompositorFloatKeyframe::~CompositorFloatKeyframe() = default;
+
+double CompositorFloatKeyframe::Time() const {
+ return float_keyframe_->Time().InSecondsF();
+}
+
+const cc::TimingFunction* CompositorFloatKeyframe::CcTimingFunction() const {
+ return float_keyframe_->timing_function();
+}
+
+std::unique_ptr<cc::FloatKeyframe> CompositorFloatKeyframe::CloneToCC() const {
+ return float_keyframe_->Clone();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_float_keyframe.h b/chromium/third_party/blink/renderer/platform/animation/compositor_float_keyframe.h
new file mode 100644
index 00000000000..e874084b0e4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_float_keyframe.h
@@ -0,0 +1,38 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_FLOAT_KEYFRAME_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_FLOAT_KEYFRAME_H_
+
+#include "cc/animation/keyframed_animation_curve.h"
+#include "third_party/blink/renderer/platform/animation/compositor_keyframe.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class TimingFunction;
+
+class PLATFORM_EXPORT CompositorFloatKeyframe : public CompositorKeyframe {
+ WTF_MAKE_NONCOPYABLE(CompositorFloatKeyframe);
+
+ public:
+ CompositorFloatKeyframe(double time, float value, const TimingFunction&);
+ CompositorFloatKeyframe(std::unique_ptr<cc::FloatKeyframe>);
+ ~CompositorFloatKeyframe() override;
+
+ // CompositorKeyframe implementation.
+ double Time() const override;
+ const cc::TimingFunction* CcTimingFunction() const override;
+
+ float Value() { return float_keyframe_->Value(); }
+ std::unique_ptr<cc::FloatKeyframe> CloneToCC() const;
+
+ private:
+ std::unique_ptr<cc::FloatKeyframe> float_keyframe_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_FLOAT_KEYFRAME_H_
diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_keyframe.cc b/chromium/third_party/blink/renderer/platform/animation/compositor_keyframe.cc
new file mode 100644
index 00000000000..c0e11987b1a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_keyframe.cc
@@ -0,0 +1,16 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/animation/compositor_keyframe.h"
+
+#include "third_party/blink/renderer/platform/animation/timing_function.h"
+
+namespace blink {
+
+scoped_refptr<TimingFunction> CompositorKeyframe::GetTimingFunctionForTesting()
+ const {
+ return CreateCompositorTimingFunctionFromCC(CcTimingFunction());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_keyframe.h b/chromium/third_party/blink/renderer/platform/animation/compositor_keyframe.h
new file mode 100644
index 00000000000..559a0f4218b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_keyframe.h
@@ -0,0 +1,33 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_KEYFRAME_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_KEYFRAME_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace cc {
+class TimingFunction;
+}
+
+namespace blink {
+
+class TimingFunction;
+
+class PLATFORM_EXPORT CompositorKeyframe {
+ public:
+ virtual ~CompositorKeyframe() = default;
+
+ virtual double Time() const = 0;
+
+ scoped_refptr<TimingFunction> GetTimingFunctionForTesting() const;
+
+ private:
+ virtual const cc::TimingFunction* CcTimingFunction() const = 0;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_KEYFRAME_H_
diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_keyframe_model.cc b/chromium/third_party/blink/renderer/platform/animation/compositor_keyframe_model.cc
new file mode 100644
index 00000000000..eece33c5b20
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_keyframe_model.cc
@@ -0,0 +1,133 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/animation/compositor_keyframe_model.h"
+
+#include <memory>
+#include "base/memory/ptr_util.h"
+#include "cc/animation/animation_curve.h"
+#include "cc/animation/animation_id_provider.h"
+#include "cc/animation/keyframed_animation_curve.h"
+#include "third_party/blink/renderer/platform/animation/compositor_animation_curve.h"
+#include "third_party/blink/renderer/platform/animation/compositor_filter_animation_curve.h"
+#include "third_party/blink/renderer/platform/animation/compositor_float_animation_curve.h"
+#include "third_party/blink/renderer/platform/animation/compositor_scroll_offset_animation_curve.h"
+#include "third_party/blink/renderer/platform/animation/compositor_transform_animation_curve.h"
+
+using cc::KeyframeModel;
+using cc::AnimationIdProvider;
+
+using blink::CompositorAnimationCurve;
+using blink::CompositorKeyframeModel;
+
+namespace blink {
+
+CompositorKeyframeModel::CompositorKeyframeModel(
+ const CompositorAnimationCurve& curve,
+ CompositorTargetProperty::Type target_property,
+ int keyframe_model_id,
+ int group_id) {
+ if (!keyframe_model_id)
+ keyframe_model_id = AnimationIdProvider::NextKeyframeModelId();
+ if (!group_id)
+ group_id = AnimationIdProvider::NextGroupId();
+
+ keyframe_model_ =
+ KeyframeModel::Create(curve.CloneToAnimationCurve(), keyframe_model_id,
+ group_id, target_property);
+}
+
+CompositorKeyframeModel::~CompositorKeyframeModel() = default;
+
+int CompositorKeyframeModel::Id() const {
+ return keyframe_model_->id();
+}
+
+int CompositorKeyframeModel::Group() const {
+ return keyframe_model_->group();
+}
+
+CompositorTargetProperty::Type CompositorKeyframeModel::TargetProperty() const {
+ return static_cast<CompositorTargetProperty::Type>(
+ keyframe_model_->target_property_id());
+}
+
+double CompositorKeyframeModel::Iterations() const {
+ return keyframe_model_->iterations();
+}
+
+void CompositorKeyframeModel::SetIterations(double n) {
+ keyframe_model_->set_iterations(n);
+}
+
+double CompositorKeyframeModel::IterationStart() const {
+ return keyframe_model_->iteration_start();
+}
+
+void CompositorKeyframeModel::SetIterationStart(double iteration_start) {
+ keyframe_model_->set_iteration_start(iteration_start);
+}
+
+double CompositorKeyframeModel::StartTime() const {
+ return (keyframe_model_->start_time() - base::TimeTicks()).InSecondsF();
+}
+
+void CompositorKeyframeModel::SetStartTime(double monotonic_time) {
+ keyframe_model_->set_start_time(base::TimeTicks::FromInternalValue(
+ monotonic_time * base::Time::kMicrosecondsPerSecond));
+}
+
+double CompositorKeyframeModel::TimeOffset() const {
+ return keyframe_model_->time_offset().InSecondsF();
+}
+
+void CompositorKeyframeModel::SetTimeOffset(double monotonic_time) {
+ keyframe_model_->set_time_offset(
+ base::TimeDelta::FromSecondsD(monotonic_time));
+}
+
+blink::CompositorKeyframeModel::Direction
+CompositorKeyframeModel::GetDirection() const {
+ return keyframe_model_->direction();
+}
+
+void CompositorKeyframeModel::SetDirection(Direction direction) {
+ keyframe_model_->set_direction(direction);
+}
+
+double CompositorKeyframeModel::PlaybackRate() const {
+ return keyframe_model_->playback_rate();
+}
+
+void CompositorKeyframeModel::SetPlaybackRate(double playback_rate) {
+ keyframe_model_->set_playback_rate(playback_rate);
+}
+
+blink::CompositorKeyframeModel::FillMode CompositorKeyframeModel::GetFillMode()
+ const {
+ return keyframe_model_->fill_mode();
+}
+
+void CompositorKeyframeModel::SetFillMode(FillMode fill_mode) {
+ keyframe_model_->set_fill_mode(fill_mode);
+}
+
+std::unique_ptr<cc::KeyframeModel>
+CompositorKeyframeModel::ReleaseCcKeyframeModel() {
+ keyframe_model_->set_needs_synchronized_start_time(true);
+ return std::move(keyframe_model_);
+}
+
+std::unique_ptr<CompositorFloatAnimationCurve>
+CompositorKeyframeModel::FloatCurveForTesting() const {
+ const cc::AnimationCurve* curve = keyframe_model_->curve();
+ DCHECK_EQ(cc::AnimationCurve::FLOAT, curve->Type());
+
+ auto keyframed_curve = base::WrapUnique(
+ static_cast<cc::KeyframedFloatAnimationCurve*>(curve->Clone().release()));
+ return CompositorFloatAnimationCurve::CreateForTesting(
+ std::move(keyframed_curve));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_keyframe_model.h b/chromium/third_party/blink/renderer/platform/animation/compositor_keyframe_model.h
new file mode 100644
index 00000000000..86d4a5bdfb6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_keyframe_model.h
@@ -0,0 +1,89 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_KEYFRAME_MODEL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_KEYFRAME_MODEL_H_
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "cc/animation/keyframe_model.h"
+#include "third_party/blink/renderer/platform/animation/compositor_target_property.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace cc {
+class KeyframeModel;
+}
+
+namespace blink {
+
+class CompositorAnimationCurve;
+class CompositorFloatAnimationCurve;
+
+// A compositor driven animation.
+class PLATFORM_EXPORT CompositorKeyframeModel {
+ WTF_MAKE_NONCOPYABLE(CompositorKeyframeModel);
+
+ public:
+ using Direction = cc::KeyframeModel::Direction;
+ using FillMode = cc::KeyframeModel::FillMode;
+
+ static std::unique_ptr<CompositorKeyframeModel> Create(
+ const blink::CompositorAnimationCurve& curve,
+ CompositorTargetProperty::Type target,
+ int group_id,
+ int keyframe_model_id) {
+ return base::WrapUnique(new CompositorKeyframeModel(
+ curve, target, keyframe_model_id, group_id));
+ }
+
+ ~CompositorKeyframeModel();
+
+ // An id must be unique.
+ int Id() const;
+ int Group() const;
+
+ CompositorTargetProperty::Type TargetProperty() const;
+
+ // This is the number of times that the animation will play. If this
+ // value is zero the animation will not play. If it is negative, then
+ // the animation will loop indefinitely.
+ double Iterations() const;
+ void SetIterations(double);
+
+ double StartTime() const;
+ void SetStartTime(double monotonic_time);
+
+ double TimeOffset() const;
+ void SetTimeOffset(double monotonic_time);
+
+ Direction GetDirection() const;
+ void SetDirection(Direction);
+
+ double PlaybackRate() const;
+ void SetPlaybackRate(double);
+
+ FillMode GetFillMode() const;
+ void SetFillMode(FillMode);
+
+ double IterationStart() const;
+ void SetIterationStart(double);
+
+ std::unique_ptr<cc::KeyframeModel> ReleaseCcKeyframeModel();
+
+ std::unique_ptr<CompositorFloatAnimationCurve> FloatCurveForTesting() const;
+
+ private:
+ CompositorKeyframeModel(const CompositorAnimationCurve&,
+ CompositorTargetProperty::Type,
+ int keyframe_model_id,
+ int group_id);
+
+ std::unique_ptr<cc::KeyframeModel> keyframe_model_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_KEYFRAME_MODEL_H_
diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_keyframe_model_test.cc b/chromium/third_party/blink/renderer/platform/animation/compositor_keyframe_model_test.cc
new file mode 100644
index 00000000000..2ab711bb7f7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_keyframe_model_test.cc
@@ -0,0 +1,45 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/animation/compositor_keyframe_model.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/animation/compositor_float_animation_curve.h"
+
+namespace blink {
+
+TEST(WebCompositorAnimationTest, DefaultSettings) {
+ std::unique_ptr<CompositorAnimationCurve> curve =
+ CompositorFloatAnimationCurve::Create();
+ std::unique_ptr<CompositorKeyframeModel> keyframe_model =
+ CompositorKeyframeModel::Create(*curve, CompositorTargetProperty::OPACITY,
+ 1, 0);
+
+ // Ensure that the defaults are correct.
+ EXPECT_EQ(1, keyframe_model->Iterations());
+ EXPECT_EQ(0, keyframe_model->StartTime());
+ EXPECT_EQ(0, keyframe_model->TimeOffset());
+ EXPECT_EQ(CompositorKeyframeModel::Direction::NORMAL,
+ keyframe_model->GetDirection());
+}
+
+TEST(WebCompositorAnimationTest, ModifiedSettings) {
+ std::unique_ptr<CompositorFloatAnimationCurve> curve =
+ CompositorFloatAnimationCurve::Create();
+ std::unique_ptr<CompositorKeyframeModel> keyframe_model =
+ CompositorKeyframeModel::Create(*curve, CompositorTargetProperty::OPACITY,
+ 1, 0);
+ keyframe_model->SetIterations(2);
+ keyframe_model->SetStartTime(2);
+ keyframe_model->SetTimeOffset(2);
+ keyframe_model->SetDirection(CompositorKeyframeModel::Direction::REVERSE);
+
+ EXPECT_EQ(2, keyframe_model->Iterations());
+ EXPECT_EQ(2, keyframe_model->StartTime());
+ EXPECT_EQ(2, keyframe_model->TimeOffset());
+ EXPECT_EQ(CompositorKeyframeModel::Direction::REVERSE,
+ keyframe_model->GetDirection());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_scroll_offset_animation_curve.cc b/chromium/third_party/blink/renderer/platform/animation/compositor_scroll_offset_animation_curve.cc
new file mode 100644
index 00000000000..8e727fd6650
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_scroll_offset_animation_curve.cc
@@ -0,0 +1,85 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/animation/compositor_scroll_offset_animation_curve.h"
+#include "third_party/blink/renderer/platform/animation/timing_function.h"
+
+#include "cc/animation/scroll_offset_animation_curve.h"
+#include "cc/animation/timing_function.h"
+
+using blink::CompositorScrollOffsetAnimationCurve;
+using DurationBehavior = cc::ScrollOffsetAnimationCurve::DurationBehavior;
+
+namespace blink {
+
+static DurationBehavior GetDurationBehavior(
+ CompositorScrollOffsetAnimationCurve::ScrollDurationBehavior
+ web_duration_behavior) {
+ switch (web_duration_behavior) {
+ case CompositorScrollOffsetAnimationCurve::kScrollDurationDeltaBased:
+ return DurationBehavior::DELTA_BASED;
+
+ case CompositorScrollOffsetAnimationCurve::kScrollDurationConstant:
+ return DurationBehavior::CONSTANT;
+
+ case CompositorScrollOffsetAnimationCurve::kScrollDurationInverseDelta:
+ return DurationBehavior::INVERSE_DELTA;
+ }
+ NOTREACHED();
+ return DurationBehavior::DELTA_BASED;
+}
+
+CompositorScrollOffsetAnimationCurve::CompositorScrollOffsetAnimationCurve(
+ FloatPoint target_value,
+ ScrollDurationBehavior duration_behavior)
+ : curve_(cc::ScrollOffsetAnimationCurve::Create(
+ gfx::ScrollOffset(target_value.X(), target_value.Y()),
+ cc::CubicBezierTimingFunction::CreatePreset(
+ CubicBezierTimingFunction::EaseType::EASE_IN_OUT),
+ GetDurationBehavior(duration_behavior))) {}
+
+CompositorScrollOffsetAnimationCurve::CompositorScrollOffsetAnimationCurve(
+ cc::ScrollOffsetAnimationCurve* curve)
+ : curve_(curve->CloneToScrollOffsetAnimationCurve()) {}
+
+CompositorScrollOffsetAnimationCurve::~CompositorScrollOffsetAnimationCurve() =
+ default;
+
+void CompositorScrollOffsetAnimationCurve::SetInitialValue(
+ FloatPoint initial_value) {
+ curve_->SetInitialValue(
+ gfx::ScrollOffset(initial_value.X(), initial_value.Y()));
+}
+
+FloatPoint CompositorScrollOffsetAnimationCurve::GetValue(double time) const {
+ gfx::ScrollOffset value =
+ curve_->GetValue(base::TimeDelta::FromSecondsD(time));
+ return FloatPoint(value.x(), value.y());
+}
+
+void CompositorScrollOffsetAnimationCurve::ApplyAdjustment(IntSize adjustment) {
+ curve_->ApplyAdjustment(
+ gfx::Vector2dF(adjustment.Width(), adjustment.Height()));
+}
+
+double CompositorScrollOffsetAnimationCurve::Duration() const {
+ return curve_->Duration().InSecondsF();
+}
+
+FloatPoint CompositorScrollOffsetAnimationCurve::TargetValue() const {
+ gfx::ScrollOffset target = curve_->target_value();
+ return FloatPoint(target.x(), target.y());
+}
+
+void CompositorScrollOffsetAnimationCurve::UpdateTarget(double time,
+ FloatPoint new_target) {
+ curve_->UpdateTarget(time, gfx::ScrollOffset(new_target.X(), new_target.Y()));
+}
+
+std::unique_ptr<cc::AnimationCurve>
+CompositorScrollOffsetAnimationCurve::CloneToAnimationCurve() const {
+ return curve_->Clone();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_scroll_offset_animation_curve.h b/chromium/third_party/blink/renderer/platform/animation/compositor_scroll_offset_animation_curve.h
new file mode 100644
index 00000000000..a4c0291c55d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_scroll_offset_animation_curve.h
@@ -0,0 +1,66 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_SCROLL_OFFSET_ANIMATION_CURVE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_SCROLL_OFFSET_ANIMATION_CURVE_H_
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/platform/animation/compositor_animation_curve.h"
+#include "third_party/blink/renderer/platform/geometry/float_point.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace cc {
+class ScrollOffsetAnimationCurve;
+}
+
+namespace blink {
+
+class PLATFORM_EXPORT CompositorScrollOffsetAnimationCurve
+ : public CompositorAnimationCurve {
+ WTF_MAKE_NONCOPYABLE(CompositorScrollOffsetAnimationCurve);
+
+ public:
+ enum ScrollDurationBehavior {
+ kScrollDurationDeltaBased = 0,
+ kScrollDurationConstant,
+ kScrollDurationInverseDelta
+ };
+
+ static std::unique_ptr<CompositorScrollOffsetAnimationCurve> Create(
+ FloatPoint target_value,
+ CompositorScrollOffsetAnimationCurve::ScrollDurationBehavior
+ duration_behavior) {
+ return base::WrapUnique(new CompositorScrollOffsetAnimationCurve(
+ target_value, duration_behavior));
+ }
+ static std::unique_ptr<CompositorScrollOffsetAnimationCurve> Create(
+ cc::ScrollOffsetAnimationCurve* curve) {
+ return base::WrapUnique(new CompositorScrollOffsetAnimationCurve(curve));
+ }
+
+ ~CompositorScrollOffsetAnimationCurve() override;
+
+ void SetInitialValue(FloatPoint);
+ FloatPoint GetValue(double time) const;
+ double Duration() const;
+ FloatPoint TargetValue() const;
+ void ApplyAdjustment(IntSize);
+ void UpdateTarget(double time, FloatPoint new_target);
+
+ // CompositorAnimationCurve implementation.
+ std::unique_ptr<cc::AnimationCurve> CloneToAnimationCurve() const override;
+
+ private:
+ CompositorScrollOffsetAnimationCurve(FloatPoint, ScrollDurationBehavior);
+ CompositorScrollOffsetAnimationCurve(cc::ScrollOffsetAnimationCurve*);
+
+ std::unique_ptr<cc::ScrollOffsetAnimationCurve> curve_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_SCROLL_OFFSET_ANIMATION_CURVE_H_
diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_target_property.h b/chromium/third_party/blink/renderer/platform/animation/compositor_target_property.h
new file mode 100644
index 00000000000..b0222f3082e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_target_property.h
@@ -0,0 +1,16 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_TARGET_PROPERTY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_TARGET_PROPERTY_H_
+
+#include "cc/trees/target_property.h"
+
+namespace blink {
+
+namespace CompositorTargetProperty = cc::TargetProperty;
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_TARGET_PROPERTY_H_
diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_transform_animation_curve.cc b/chromium/third_party/blink/renderer/platform/animation/compositor_transform_animation_curve.cc
new file mode 100644
index 00000000000..a3932b79da8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_transform_animation_curve.cc
@@ -0,0 +1,40 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/animation/compositor_transform_animation_curve.h"
+
+#include "cc/animation/keyframed_animation_curve.h"
+#include "cc/animation/timing_function.h"
+#include "cc/animation/transform_operations.h"
+#include "third_party/blink/renderer/platform/animation/compositor_transform_operations.h"
+
+namespace blink {
+
+CompositorTransformAnimationCurve::CompositorTransformAnimationCurve()
+ : curve_(cc::KeyframedTransformAnimationCurve::Create()) {}
+
+CompositorTransformAnimationCurve::~CompositorTransformAnimationCurve() =
+ default;
+
+void CompositorTransformAnimationCurve::AddKeyframe(
+ const CompositorTransformKeyframe& keyframe) {
+ curve_->AddKeyframe(keyframe.CloneToCC());
+}
+
+void CompositorTransformAnimationCurve::SetTimingFunction(
+ const TimingFunction& timing_function) {
+ curve_->SetTimingFunction(timing_function.CloneToCC());
+}
+
+void CompositorTransformAnimationCurve::SetScaledDuration(
+ double scaled_duration) {
+ curve_->set_scaled_duration(scaled_duration);
+}
+
+std::unique_ptr<cc::AnimationCurve>
+CompositorTransformAnimationCurve::CloneToAnimationCurve() const {
+ return curve_->Clone();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_transform_animation_curve.h b/chromium/third_party/blink/renderer/platform/animation/compositor_transform_animation_curve.h
new file mode 100644
index 00000000000..5b934575118
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_transform_animation_curve.h
@@ -0,0 +1,54 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_TRANSFORM_ANIMATION_CURVE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_TRANSFORM_ANIMATION_CURVE_H_
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/platform/animation/compositor_animation_curve.h"
+#include "third_party/blink/renderer/platform/animation/compositor_transform_keyframe.h"
+#include "third_party/blink/renderer/platform/animation/timing_function.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace cc {
+class KeyframedTransformAnimationCurve;
+}
+
+namespace blink {
+class CompositorTransformKeyframe;
+}
+
+namespace blink {
+
+// A keyframed transform animation curve.
+class PLATFORM_EXPORT CompositorTransformAnimationCurve
+ : public CompositorAnimationCurve {
+ WTF_MAKE_NONCOPYABLE(CompositorTransformAnimationCurve);
+
+ public:
+ static std::unique_ptr<CompositorTransformAnimationCurve> Create() {
+ return base::WrapUnique(new CompositorTransformAnimationCurve());
+ }
+
+ ~CompositorTransformAnimationCurve() override;
+
+ void AddKeyframe(const CompositorTransformKeyframe&);
+ void SetTimingFunction(const TimingFunction&);
+ void SetScaledDuration(double);
+
+ // CompositorAnimationCurve implementation.
+ std::unique_ptr<cc::AnimationCurve> CloneToAnimationCurve() const override;
+
+ private:
+ CompositorTransformAnimationCurve();
+
+ std::unique_ptr<cc::KeyframedTransformAnimationCurve> curve_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_TRANSFORM_ANIMATION_CURVE_H_
diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_transform_keyframe.cc b/chromium/third_party/blink/renderer/platform/animation/compositor_transform_keyframe.cc
new file mode 100644
index 00000000000..1c13d89a779
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_transform_keyframe.cc
@@ -0,0 +1,36 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/animation/compositor_transform_keyframe.h"
+
+#include <memory>
+
+namespace blink {
+
+CompositorTransformKeyframe::CompositorTransformKeyframe(
+ double time,
+ CompositorTransformOperations value,
+ const TimingFunction& timing_function)
+ : transform_keyframe_(
+ cc::TransformKeyframe::Create(base::TimeDelta::FromSecondsD(time),
+ value.ReleaseCcTransformOperations(),
+ timing_function.CloneToCC())) {}
+
+CompositorTransformKeyframe::~CompositorTransformKeyframe() = default;
+
+double CompositorTransformKeyframe::Time() const {
+ return transform_keyframe_->Time().InSecondsF();
+}
+
+const cc::TimingFunction* CompositorTransformKeyframe::CcTimingFunction()
+ const {
+ return transform_keyframe_->timing_function();
+}
+
+std::unique_ptr<cc::TransformKeyframe> CompositorTransformKeyframe::CloneToCC()
+ const {
+ return transform_keyframe_->Clone();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_transform_keyframe.h b/chromium/third_party/blink/renderer/platform/animation/compositor_transform_keyframe.h
new file mode 100644
index 00000000000..c9eb01c7eac
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_transform_keyframe.h
@@ -0,0 +1,38 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_TRANSFORM_KEYFRAME_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_TRANSFORM_KEYFRAME_H_
+
+#include "cc/animation/keyframed_animation_curve.h"
+#include "third_party/blink/renderer/platform/animation/compositor_keyframe.h"
+#include "third_party/blink/renderer/platform/animation/compositor_transform_operations.h"
+#include "third_party/blink/renderer/platform/animation/timing_function.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT CompositorTransformKeyframe : public CompositorKeyframe {
+ WTF_MAKE_NONCOPYABLE(CompositorTransformKeyframe);
+
+ public:
+ CompositorTransformKeyframe(double time,
+ CompositorTransformOperations value,
+ const TimingFunction&);
+ ~CompositorTransformKeyframe();
+
+ std::unique_ptr<cc::TransformKeyframe> CloneToCC() const;
+
+ // CompositorKeyframe implementation.
+ double Time() const override;
+ const cc::TimingFunction* CcTimingFunction() const override;
+
+ private:
+ std::unique_ptr<cc::TransformKeyframe> transform_keyframe_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_TRANSFORM_KEYFRAME_H_
diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_transform_operations.cc b/chromium/third_party/blink/renderer/platform/animation/compositor_transform_operations.cc
new file mode 100644
index 00000000000..6d34cfebf9a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_transform_operations.cc
@@ -0,0 +1,65 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/animation/compositor_transform_operations.h"
+
+#include "ui/gfx/transform.h"
+
+namespace blink {
+
+const cc::TransformOperations&
+CompositorTransformOperations::AsCcTransformOperations() const {
+ return transform_operations_;
+}
+
+cc::TransformOperations
+CompositorTransformOperations::ReleaseCcTransformOperations() {
+ return std::move(transform_operations_);
+}
+
+bool CompositorTransformOperations::CanBlendWith(
+ const blink::CompositorTransformOperations& other) const {
+ return transform_operations_.CanBlendWith(other.transform_operations_);
+}
+
+void CompositorTransformOperations::AppendTranslate(double x,
+ double y,
+ double z) {
+ transform_operations_.AppendTranslate(x, y, z);
+}
+
+void CompositorTransformOperations::AppendRotate(double x,
+ double y,
+ double z,
+ double degrees) {
+ transform_operations_.AppendRotate(x, y, z, degrees);
+}
+
+void CompositorTransformOperations::AppendScale(double x, double y, double z) {
+ transform_operations_.AppendScale(x, y, z);
+}
+
+void CompositorTransformOperations::AppendSkew(double x, double y) {
+ transform_operations_.AppendSkew(x, y);
+}
+
+void CompositorTransformOperations::AppendPerspective(double depth) {
+ transform_operations_.AppendPerspective(depth);
+}
+
+void CompositorTransformOperations::AppendMatrix(const SkMatrix44& matrix) {
+ gfx::Transform transform(gfx::Transform::kSkipInitialization);
+ transform.matrix() = matrix;
+ transform_operations_.AppendMatrix(transform);
+}
+
+void CompositorTransformOperations::AppendIdentity() {
+ transform_operations_.AppendIdentity();
+}
+
+bool CompositorTransformOperations::IsIdentity() const {
+ return transform_operations_.IsIdentity();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_transform_operations.h b/chromium/third_party/blink/renderer/platform/animation/compositor_transform_operations.h
new file mode 100644
index 00000000000..bad0ed5355e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_transform_operations.h
@@ -0,0 +1,41 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_TRANSFORM_OPERATIONS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_TRANSFORM_OPERATIONS_H_
+
+#include "cc/animation/transform_operations.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+class SkMatrix44;
+
+namespace blink {
+
+class PLATFORM_EXPORT CompositorTransformOperations {
+ public:
+ const cc::TransformOperations& AsCcTransformOperations() const;
+ cc::TransformOperations ReleaseCcTransformOperations();
+
+ // Returns true if these operations can be blended. It will only return
+ // false if we must resort to matrix interpolation, and matrix interpolation
+ // fails (this can happen if either matrix cannot be decomposed).
+ bool CanBlendWith(const CompositorTransformOperations& other) const;
+
+ void AppendTranslate(double x, double y, double z);
+ void AppendRotate(double x, double y, double z, double degrees);
+ void AppendScale(double x, double y, double z);
+ void AppendSkew(double x, double y);
+ void AppendPerspective(double depth);
+ void AppendMatrix(const SkMatrix44&);
+ void AppendIdentity();
+
+ bool IsIdentity() const;
+
+ private:
+ cc::TransformOperations transform_operations_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_COMPOSITOR_TRANSFORM_OPERATIONS_H_
diff --git a/chromium/third_party/blink/renderer/platform/animation/timing_function.cc b/chromium/third_party/blink/renderer/platform/animation/timing_function.cc
new file mode 100644
index 00000000000..9ea3b3a1f13
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/timing_function.cc
@@ -0,0 +1,262 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/animation/timing_function.h"
+
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+
+namespace blink {
+
+String LinearTimingFunction::ToString() const {
+ return "linear";
+}
+
+double LinearTimingFunction::Evaluate(double fraction, double) const {
+ return fraction;
+}
+
+void LinearTimingFunction::Range(double* min_value, double* max_value) const {}
+
+std::unique_ptr<cc::TimingFunction> LinearTimingFunction::CloneToCC() const {
+ return nullptr;
+}
+
+CubicBezierTimingFunction* CubicBezierTimingFunction::Preset(
+ EaseType ease_type) {
+ DEFINE_STATIC_REF(
+ CubicBezierTimingFunction, ease,
+ (base::AdoptRef(new CubicBezierTimingFunction(EaseType::EASE))));
+ DEFINE_STATIC_REF(
+ CubicBezierTimingFunction, ease_in,
+ (base::AdoptRef(new CubicBezierTimingFunction(EaseType::EASE_IN))));
+ DEFINE_STATIC_REF(
+ CubicBezierTimingFunction, ease_out,
+ (base::AdoptRef(new CubicBezierTimingFunction(EaseType::EASE_OUT))));
+ DEFINE_STATIC_REF(
+ CubicBezierTimingFunction, ease_in_out,
+ (base::AdoptRef(new CubicBezierTimingFunction(EaseType::EASE_IN_OUT))));
+
+ switch (ease_type) {
+ case EaseType::EASE:
+ return ease;
+ case EaseType::EASE_IN:
+ return ease_in;
+ case EaseType::EASE_OUT:
+ return ease_out;
+ case EaseType::EASE_IN_OUT:
+ return ease_in_out;
+ default:
+ NOTREACHED();
+ return nullptr;
+ }
+}
+
+String CubicBezierTimingFunction::ToString() const {
+ switch (this->GetEaseType()) {
+ case CubicBezierTimingFunction::EaseType::EASE:
+ return "ease";
+ case CubicBezierTimingFunction::EaseType::EASE_IN:
+ return "ease-in";
+ case CubicBezierTimingFunction::EaseType::EASE_OUT:
+ return "ease-out";
+ case CubicBezierTimingFunction::EaseType::EASE_IN_OUT:
+ return "ease-in-out";
+ case CubicBezierTimingFunction::EaseType::CUSTOM:
+ return "cubic-bezier(" + String::NumberToStringECMAScript(this->X1()) +
+ ", " + String::NumberToStringECMAScript(this->Y1()) + ", " +
+ String::NumberToStringECMAScript(this->X2()) + ", " +
+ String::NumberToStringECMAScript(this->Y2()) + ")";
+ default:
+ NOTREACHED();
+ return "";
+ }
+}
+
+double CubicBezierTimingFunction::Evaluate(double fraction,
+ double accuracy) const {
+ return bezier_->bezier().SolveWithEpsilon(fraction, accuracy);
+}
+
+void CubicBezierTimingFunction::Range(double* min_value,
+ double* max_value) const {
+ const double solution1 = bezier_->bezier().range_min();
+ const double solution2 = bezier_->bezier().range_max();
+
+ // Since our input values can be out of the range 0->1 so we must also
+ // consider the minimum and maximum points.
+ double solution_min = bezier_->bezier().SolveWithEpsilon(
+ *min_value, std::numeric_limits<double>::epsilon());
+ double solution_max = bezier_->bezier().SolveWithEpsilon(
+ *max_value, std::numeric_limits<double>::epsilon());
+ *min_value = std::min(std::min(solution_min, solution_max), 0.0);
+ *max_value = std::max(std::max(solution_min, solution_max), 1.0);
+ *min_value = std::min(std::min(*min_value, solution1), solution2);
+ *max_value = std::max(std::max(*max_value, solution1), solution2);
+}
+
+std::unique_ptr<cc::TimingFunction> CubicBezierTimingFunction::CloneToCC()
+ const {
+ return bezier_->Clone();
+}
+
+String StepsTimingFunction::ToString() const {
+ const char* position_string = nullptr;
+ switch (GetStepPosition()) {
+ case StepPosition::START:
+ position_string = "start";
+ break;
+ case StepPosition::MIDDLE:
+ position_string = "middle";
+ break;
+ case StepPosition::END:
+ // do not specify step position in output
+ break;
+ }
+
+ StringBuilder builder;
+ builder.Append("steps(");
+ builder.Append(String::NumberToStringECMAScript(this->NumberOfSteps()));
+ if (position_string) {
+ builder.Append(", ");
+ builder.Append(position_string);
+ }
+ builder.Append(')');
+ return builder.ToString();
+}
+
+void StepsTimingFunction::Range(double* min_value, double* max_value) const {
+ *min_value = 0;
+ *max_value = 1;
+}
+
+double StepsTimingFunction::Evaluate(double fraction, double) const {
+ return steps_->GetPreciseValue(fraction);
+}
+
+std::unique_ptr<cc::TimingFunction> StepsTimingFunction::CloneToCC() const {
+ return steps_->Clone();
+}
+
+String FramesTimingFunction::ToString() const {
+ StringBuilder builder;
+ builder.Append("frames(");
+ builder.Append(String::NumberToStringECMAScript(this->NumberOfFrames()));
+ builder.Append(")");
+ return builder.ToString();
+}
+
+void FramesTimingFunction::Range(double* min_value, double* max_value) const {
+ *min_value = 0;
+ *max_value = 1;
+}
+
+double FramesTimingFunction::Evaluate(double fraction, double) const {
+ return frames_->GetPreciseValue(fraction);
+}
+
+std::unique_ptr<cc::TimingFunction> FramesTimingFunction::CloneToCC() const {
+ return frames_->Clone();
+}
+
+scoped_refptr<TimingFunction> CreateCompositorTimingFunctionFromCC(
+ const cc::TimingFunction* timing_function) {
+ if (!timing_function)
+ return LinearTimingFunction::Shared();
+
+ switch (timing_function->GetType()) {
+ case cc::TimingFunction::Type::CUBIC_BEZIER: {
+ auto cubic_timing_function =
+ static_cast<const cc::CubicBezierTimingFunction*>(timing_function);
+ if (cubic_timing_function->ease_type() !=
+ cc::CubicBezierTimingFunction::EaseType::CUSTOM)
+ return CubicBezierTimingFunction::Preset(
+ cubic_timing_function->ease_type());
+
+ const auto& bezier = cubic_timing_function->bezier();
+ return CubicBezierTimingFunction::Create(bezier.GetX1(), bezier.GetY1(),
+ bezier.GetX2(), bezier.GetY2());
+ }
+
+ case cc::TimingFunction::Type::STEPS: {
+ auto steps_timing_function =
+ static_cast<const cc::StepsTimingFunction*>(timing_function);
+ return StepsTimingFunction::Create(
+ steps_timing_function->steps(),
+ steps_timing_function->step_position());
+ }
+
+ default:
+ NOTREACHED();
+ return nullptr;
+ }
+}
+
+// Equals operators
+bool operator==(const LinearTimingFunction& lhs, const TimingFunction& rhs) {
+ return rhs.GetType() == TimingFunction::Type::LINEAR;
+}
+
+bool operator==(const CubicBezierTimingFunction& lhs,
+ const TimingFunction& rhs) {
+ if (rhs.GetType() != TimingFunction::Type::CUBIC_BEZIER)
+ return false;
+
+ const CubicBezierTimingFunction& ctf = ToCubicBezierTimingFunction(rhs);
+ if ((lhs.GetEaseType() == CubicBezierTimingFunction::EaseType::CUSTOM) &&
+ (ctf.GetEaseType() == CubicBezierTimingFunction::EaseType::CUSTOM))
+ return (lhs.X1() == ctf.X1()) && (lhs.Y1() == ctf.Y1()) &&
+ (lhs.X2() == ctf.X2()) && (lhs.Y2() == ctf.Y2());
+
+ return lhs.GetEaseType() == ctf.GetEaseType();
+}
+
+bool operator==(const StepsTimingFunction& lhs, const TimingFunction& rhs) {
+ if (rhs.GetType() != TimingFunction::Type::STEPS)
+ return false;
+
+ const StepsTimingFunction& stf = ToStepsTimingFunction(rhs);
+ return (lhs.NumberOfSteps() == stf.NumberOfSteps()) &&
+ (lhs.GetStepPosition() == stf.GetStepPosition());
+}
+
+bool operator==(const FramesTimingFunction& lhs, const TimingFunction& rhs) {
+ if (rhs.GetType() != TimingFunction::Type::FRAMES)
+ return false;
+
+ const FramesTimingFunction& ftf = ToFramesTimingFunction(rhs);
+ return lhs.NumberOfFrames() == ftf.NumberOfFrames();
+}
+
+// The generic operator== *must* come after the
+// non-generic operator== otherwise it will end up calling itself.
+bool operator==(const TimingFunction& lhs, const TimingFunction& rhs) {
+ switch (lhs.GetType()) {
+ case TimingFunction::Type::LINEAR: {
+ const LinearTimingFunction& linear = ToLinearTimingFunction(lhs);
+ return (linear == rhs);
+ }
+ case TimingFunction::Type::CUBIC_BEZIER: {
+ const CubicBezierTimingFunction& cubic = ToCubicBezierTimingFunction(lhs);
+ return (cubic == rhs);
+ }
+ case TimingFunction::Type::STEPS: {
+ const StepsTimingFunction& step = ToStepsTimingFunction(lhs);
+ return (step == rhs);
+ }
+ case TimingFunction::Type::FRAMES: {
+ const FramesTimingFunction& frame = ToFramesTimingFunction(lhs);
+ return (frame == rhs);
+ }
+ default:
+ NOTREACHED();
+ }
+ return false;
+}
+
+// No need to define specific operator!= as they can all come via this function.
+bool operator!=(const TimingFunction& lhs, const TimingFunction& rhs) {
+ return !(lhs == rhs);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/animation/timing_function.h b/chromium/third_party/blink/renderer/platform/animation/timing_function.h
new file mode 100644
index 00000000000..51a9396ef29
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/timing_function.h
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_TIMING_FUNCTION_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_TIMING_FUNCTION_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "cc/animation/timing_function.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT TimingFunction : public RefCounted<TimingFunction> {
+ public:
+ using Type = cc::TimingFunction::Type;
+
+ virtual ~TimingFunction() = default;
+
+ Type GetType() const { return type_; }
+
+ virtual String ToString() const = 0;
+
+ // Evaluates the timing function at the given fraction. The accuracy parameter
+ // provides a hint as to the required accuracy and is not guaranteed.
+ virtual double Evaluate(double fraction, double accuracy) const = 0;
+
+ // This function returns the minimum and maximum values obtainable when
+ // calling evaluate();
+ virtual void Range(double* min_value, double* max_value) const = 0;
+
+ // Create CC instance.
+ virtual std::unique_ptr<cc::TimingFunction> CloneToCC() const = 0;
+
+ protected:
+ TimingFunction(Type type) : type_(type) {}
+
+ private:
+ Type type_;
+};
+
+class PLATFORM_EXPORT LinearTimingFunction final : public TimingFunction {
+ public:
+ static LinearTimingFunction* Shared() {
+ DEFINE_STATIC_REF(LinearTimingFunction, linear,
+ (base::AdoptRef(new LinearTimingFunction())));
+ return linear;
+ }
+
+ ~LinearTimingFunction() override = default;
+
+ // TimingFunction implementation.
+ String ToString() const override;
+ double Evaluate(double fraction, double) const override;
+ void Range(double* min_value, double* max_value) const override;
+ std::unique_ptr<cc::TimingFunction> CloneToCC() const override;
+
+ private:
+ LinearTimingFunction() : TimingFunction(Type::LINEAR) {}
+};
+
+class PLATFORM_EXPORT CubicBezierTimingFunction final : public TimingFunction {
+ public:
+ using EaseType = cc::CubicBezierTimingFunction::EaseType;
+
+ static scoped_refptr<CubicBezierTimingFunction> Create(double x1,
+ double y1,
+ double x2,
+ double y2) {
+ return base::AdoptRef(new CubicBezierTimingFunction(x1, y1, x2, y2));
+ }
+
+ static CubicBezierTimingFunction* Preset(EaseType);
+
+ ~CubicBezierTimingFunction() override = default;
+
+ // TimingFunction implementation.
+ String ToString() const override;
+ double Evaluate(double fraction, double accuracy) const override;
+ void Range(double* min_value, double* max_value) const override;
+ std::unique_ptr<cc::TimingFunction> CloneToCC() const override;
+
+ double X1() const {
+ DCHECK_EQ(GetEaseType(), EaseType::CUSTOM);
+ return x1_;
+ }
+ double Y1() const {
+ DCHECK_EQ(GetEaseType(), EaseType::CUSTOM);
+ return y1_;
+ }
+ double X2() const {
+ DCHECK_EQ(GetEaseType(), EaseType::CUSTOM);
+ return x2_;
+ }
+ double Y2() const {
+ DCHECK_EQ(GetEaseType(), EaseType::CUSTOM);
+ return y2_;
+ }
+ EaseType GetEaseType() const { return bezier_->ease_type(); }
+
+ private:
+ explicit CubicBezierTimingFunction(EaseType ease_type)
+ : TimingFunction(Type::CUBIC_BEZIER),
+ bezier_(cc::CubicBezierTimingFunction::CreatePreset(ease_type)),
+ x1_(),
+ y1_(),
+ x2_(),
+ y2_() {}
+
+ CubicBezierTimingFunction(double x1, double y1, double x2, double y2)
+ : TimingFunction(Type::CUBIC_BEZIER),
+ bezier_(cc::CubicBezierTimingFunction::Create(x1, y1, x2, y2)),
+ x1_(x1),
+ y1_(y1),
+ x2_(x2),
+ y2_(y2) {}
+
+ std::unique_ptr<cc::CubicBezierTimingFunction> bezier_;
+
+ // TODO(loyso): Get these values from m_bezier->bezier_ (gfx::CubicBezier)
+ const double x1_;
+ const double y1_;
+ const double x2_;
+ const double y2_;
+};
+
+class PLATFORM_EXPORT StepsTimingFunction final : public TimingFunction {
+ public:
+ using StepPosition = cc::StepsTimingFunction::StepPosition;
+
+ static scoped_refptr<StepsTimingFunction> Create(int steps,
+ StepPosition step_position) {
+ return base::AdoptRef(new StepsTimingFunction(steps, step_position));
+ }
+
+ static StepsTimingFunction* Preset(StepPosition position) {
+ DEFINE_STATIC_REF(StepsTimingFunction, start,
+ Create(1, StepPosition::START));
+ DEFINE_STATIC_REF(StepsTimingFunction, middle,
+ Create(1, StepPosition::MIDDLE));
+ DEFINE_STATIC_REF(StepsTimingFunction, end, Create(1, StepPosition::END));
+ switch (position) {
+ case StepPosition::START:
+ return start;
+ case StepPosition::MIDDLE:
+ return middle;
+ case StepPosition::END:
+ return end;
+ default:
+ NOTREACHED();
+ return end;
+ }
+ }
+
+ ~StepsTimingFunction() override = default;
+
+ // TimingFunction implementation.
+ String ToString() const override;
+ double Evaluate(double fraction, double) const override;
+ void Range(double* min_value, double* max_value) const override;
+ std::unique_ptr<cc::TimingFunction> CloneToCC() const override;
+
+ int NumberOfSteps() const { return steps_->steps(); }
+ StepPosition GetStepPosition() const { return steps_->step_position(); }
+
+ private:
+ StepsTimingFunction(int steps, StepPosition step_position)
+ : TimingFunction(Type::STEPS),
+ steps_(cc::StepsTimingFunction::Create(steps, step_position)) {}
+
+ std::unique_ptr<cc::StepsTimingFunction> steps_;
+};
+
+class PLATFORM_EXPORT FramesTimingFunction final : public TimingFunction {
+ public:
+ static scoped_refptr<FramesTimingFunction> Create(int frames) {
+ return base::AdoptRef(new FramesTimingFunction(frames));
+ }
+
+ ~FramesTimingFunction() override = default;
+
+ // TimingFunction implementation.
+ String ToString() const override;
+ double Evaluate(double fraction, double) const override;
+ void Range(double* min_value, double* max_value) const override;
+ std::unique_ptr<cc::TimingFunction> CloneToCC() const override;
+
+ int NumberOfFrames() const { return frames_->frames(); }
+
+ private:
+ FramesTimingFunction(int frames)
+ : TimingFunction(Type::FRAMES),
+ frames_(cc::FramesTimingFunction::Create(frames)) {
+ DCHECK(RuntimeEnabledFeatures::FramesTimingFunctionEnabled());
+ }
+
+ std::unique_ptr<cc::FramesTimingFunction> frames_;
+};
+
+PLATFORM_EXPORT scoped_refptr<TimingFunction>
+CreateCompositorTimingFunctionFromCC(const cc::TimingFunction*);
+
+PLATFORM_EXPORT bool operator==(const LinearTimingFunction&,
+ const TimingFunction&);
+PLATFORM_EXPORT bool operator==(const CubicBezierTimingFunction&,
+ const TimingFunction&);
+PLATFORM_EXPORT bool operator==(const StepsTimingFunction&,
+ const TimingFunction&);
+PLATFORM_EXPORT bool operator==(const FramesTimingFunction&,
+ const TimingFunction&);
+
+PLATFORM_EXPORT bool operator==(const TimingFunction&, const TimingFunction&);
+PLATFORM_EXPORT bool operator!=(const TimingFunction&, const TimingFunction&);
+
+#define DEFINE_TIMING_FUNCTION_TYPE_CASTS(typeName, enumName) \
+ DEFINE_TYPE_CASTS(typeName##TimingFunction, TimingFunction, value, \
+ value->GetType() == TimingFunction::Type::enumName, \
+ value.GetType() == TimingFunction::Type::enumName)
+
+DEFINE_TIMING_FUNCTION_TYPE_CASTS(Linear, LINEAR);
+DEFINE_TIMING_FUNCTION_TYPE_CASTS(CubicBezier, CUBIC_BEZIER);
+DEFINE_TIMING_FUNCTION_TYPE_CASTS(Steps, STEPS);
+DEFINE_TIMING_FUNCTION_TYPE_CASTS(Frames, FRAMES);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_TIMING_FUNCTION_H_
diff --git a/chromium/third_party/blink/renderer/platform/animation/timing_function_test.cc b/chromium/third_party/blink/renderer/platform/animation/timing_function_test.cc
new file mode 100644
index 00000000000..44757bcb8db
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/animation/timing_function_test.cc
@@ -0,0 +1,550 @@
+/*
+ * Copyright (c) 2013, 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 "third_party/blink/renderer/platform/animation/timing_function.h"
+
+#include <sstream>
+#include <string>
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+// Macro is only used to allow the use of streams.
+// Can be removed if a pretty failure message isn't needed.
+#define EXPECT_NE_WITH_MESSAGE(a, b) \
+ EXPECT_NE(*a.second, *b.second) \
+ << a.first << " (" << a.second->ToString().Latin1().data() << ")" \
+ << " == " << b.first << " (" << b.second->ToString().Latin1().data() \
+ << ")" \
+ << "\n";
+
+namespace blink {
+
+namespace {
+
+class TimingFunctionTest : public testing::Test,
+ private ScopedFramesTimingFunctionForTest {
+ public:
+ TimingFunctionTest() : ScopedFramesTimingFunctionForTest(true) {}
+
+ void NotEqualHelperLoop(
+ Vector<std::pair<std::string, scoped_refptr<TimingFunction>>>& v) {
+ for (size_t i = 0; i < v.size(); ++i) {
+ for (size_t j = 0; j < v.size(); ++j) {
+ if (i == j)
+ continue;
+ EXPECT_NE_WITH_MESSAGE(v[i], v[j]);
+ }
+ }
+ }
+};
+
+TEST_F(TimingFunctionTest, LinearToString) {
+ scoped_refptr<TimingFunction> linear_timing = LinearTimingFunction::Shared();
+ EXPECT_EQ(linear_timing->ToString(), "linear");
+}
+
+TEST_F(TimingFunctionTest, CubicToString) {
+ scoped_refptr<TimingFunction> cubic_ease_timing =
+ CubicBezierTimingFunction::Preset(
+ CubicBezierTimingFunction::EaseType::EASE);
+ EXPECT_EQ("ease", cubic_ease_timing->ToString());
+ scoped_refptr<TimingFunction> cubic_ease_in_timing =
+ CubicBezierTimingFunction::Preset(
+ CubicBezierTimingFunction::EaseType::EASE_IN);
+ EXPECT_EQ("ease-in", cubic_ease_in_timing->ToString());
+ scoped_refptr<TimingFunction> cubic_ease_out_timing =
+ CubicBezierTimingFunction::Preset(
+ CubicBezierTimingFunction::EaseType::EASE_OUT);
+ EXPECT_EQ("ease-out", cubic_ease_out_timing->ToString());
+ scoped_refptr<TimingFunction> cubic_ease_in_out_timing =
+ CubicBezierTimingFunction::Preset(
+ CubicBezierTimingFunction::EaseType::EASE_IN_OUT);
+ EXPECT_EQ("ease-in-out", cubic_ease_in_out_timing->ToString());
+
+ scoped_refptr<TimingFunction> cubic_custom_timing =
+ CubicBezierTimingFunction::Create(0.17, 0.67, 1, -1.73);
+ EXPECT_EQ("cubic-bezier(0.17, 0.67, 1, -1.73)",
+ cubic_custom_timing->ToString());
+}
+
+TEST_F(TimingFunctionTest, StepToString) {
+ scoped_refptr<TimingFunction> step_timing_start =
+ StepsTimingFunction::Preset(StepsTimingFunction::StepPosition::START);
+ EXPECT_EQ("steps(1, start)", step_timing_start->ToString());
+
+ scoped_refptr<TimingFunction> step_timing_middle =
+ StepsTimingFunction::Preset(StepsTimingFunction::StepPosition::MIDDLE);
+ EXPECT_EQ("steps(1, middle)", step_timing_middle->ToString());
+
+ scoped_refptr<TimingFunction> step_timing_end =
+ StepsTimingFunction::Preset(StepsTimingFunction::StepPosition::END);
+ EXPECT_EQ("steps(1)", step_timing_end->ToString());
+
+ scoped_refptr<TimingFunction> step_timing_custom_start =
+ StepsTimingFunction::Create(3, StepsTimingFunction::StepPosition::START);
+ EXPECT_EQ("steps(3, start)", step_timing_custom_start->ToString());
+
+ scoped_refptr<TimingFunction> step_timing_custom_middle =
+ StepsTimingFunction::Create(4, StepsTimingFunction::StepPosition::MIDDLE);
+ EXPECT_EQ("steps(4, middle)", step_timing_custom_middle->ToString());
+
+ scoped_refptr<TimingFunction> step_timing_custom_end =
+ StepsTimingFunction::Create(5, StepsTimingFunction::StepPosition::END);
+ EXPECT_EQ("steps(5)", step_timing_custom_end->ToString());
+}
+
+TEST_F(TimingFunctionTest, FrameToString) {
+ scoped_refptr<TimingFunction> frame_timing = FramesTimingFunction::Create(3);
+ EXPECT_EQ("frames(3)", frame_timing->ToString());
+}
+
+TEST_F(TimingFunctionTest, BaseOperatorEq) {
+ scoped_refptr<TimingFunction> linear_timing = LinearTimingFunction::Shared();
+ scoped_refptr<TimingFunction> cubic_timing1 =
+ CubicBezierTimingFunction::Preset(
+ CubicBezierTimingFunction::EaseType::EASE_IN);
+ scoped_refptr<TimingFunction> cubic_timing2 =
+ CubicBezierTimingFunction::Create(0.17, 0.67, 1, -1.73);
+ scoped_refptr<TimingFunction> steps_timing1 =
+ StepsTimingFunction::Preset(StepsTimingFunction::StepPosition::END);
+ scoped_refptr<TimingFunction> steps_timing2 =
+ StepsTimingFunction::Create(5, StepsTimingFunction::StepPosition::START);
+ scoped_refptr<TimingFunction> frames_timing = FramesTimingFunction::Create(5);
+
+ Vector<std::pair<std::string, scoped_refptr<TimingFunction>>> v;
+ v.push_back(std::make_pair("linearTiming", linear_timing));
+ v.push_back(std::make_pair("cubicTiming1", cubic_timing1));
+ v.push_back(std::make_pair("cubicTiming2", cubic_timing2));
+ v.push_back(std::make_pair("stepsTiming1", steps_timing1));
+ v.push_back(std::make_pair("stepsTiming2", steps_timing2));
+ v.push_back(std::make_pair("framesTiming", frames_timing));
+ NotEqualHelperLoop(v);
+}
+
+TEST_F(TimingFunctionTest, LinearOperatorEq) {
+ scoped_refptr<TimingFunction> linear_timing1 = LinearTimingFunction::Shared();
+ scoped_refptr<TimingFunction> linear_timing2 = LinearTimingFunction::Shared();
+ EXPECT_EQ(*linear_timing1, *linear_timing1);
+ EXPECT_EQ(*linear_timing1, *linear_timing2);
+}
+
+TEST_F(TimingFunctionTest, CubicOperatorEq) {
+ scoped_refptr<TimingFunction> cubic_ease_in_timing1 =
+ CubicBezierTimingFunction::Preset(
+ CubicBezierTimingFunction::EaseType::EASE_IN);
+ scoped_refptr<TimingFunction> cubic_ease_in_timing2 =
+ CubicBezierTimingFunction::Preset(
+ CubicBezierTimingFunction::EaseType::EASE_IN);
+ EXPECT_EQ(*cubic_ease_in_timing1, *cubic_ease_in_timing1);
+ EXPECT_EQ(*cubic_ease_in_timing1, *cubic_ease_in_timing2);
+
+ scoped_refptr<TimingFunction> cubic_ease_out_timing1 =
+ CubicBezierTimingFunction::Preset(
+ CubicBezierTimingFunction::EaseType::EASE_OUT);
+ scoped_refptr<TimingFunction> cubic_ease_out_timing2 =
+ CubicBezierTimingFunction::Preset(
+ CubicBezierTimingFunction::EaseType::EASE_OUT);
+ EXPECT_EQ(*cubic_ease_out_timing1, *cubic_ease_out_timing1);
+ EXPECT_EQ(*cubic_ease_out_timing1, *cubic_ease_out_timing2);
+
+ scoped_refptr<TimingFunction> cubic_ease_in_out_timing1 =
+ CubicBezierTimingFunction::Preset(
+ CubicBezierTimingFunction::EaseType::EASE_IN_OUT);
+ scoped_refptr<TimingFunction> cubic_ease_in_out_timing2 =
+ CubicBezierTimingFunction::Preset(
+ CubicBezierTimingFunction::EaseType::EASE_IN_OUT);
+ EXPECT_EQ(*cubic_ease_in_out_timing1, *cubic_ease_in_out_timing1);
+ EXPECT_EQ(*cubic_ease_in_out_timing1, *cubic_ease_in_out_timing2);
+
+ scoped_refptr<TimingFunction> cubic_custom_timing1 =
+ CubicBezierTimingFunction::Create(0.17, 0.67, 1, -1.73);
+ scoped_refptr<TimingFunction> cubic_custom_timing2 =
+ CubicBezierTimingFunction::Create(0.17, 0.67, 1, -1.73);
+ EXPECT_EQ(*cubic_custom_timing1, *cubic_custom_timing1);
+ EXPECT_EQ(*cubic_custom_timing1, *cubic_custom_timing2);
+
+ Vector<std::pair<std::string, scoped_refptr<TimingFunction>>> v;
+ v.push_back(std::make_pair("cubicEaseInTiming1", cubic_ease_in_timing1));
+ v.push_back(std::make_pair("cubicEaseOutTiming1", cubic_ease_out_timing1));
+ v.push_back(
+ std::make_pair("cubicEaseInOutTiming1", cubic_ease_in_out_timing1));
+ v.push_back(std::make_pair("cubicCustomTiming1", cubic_custom_timing1));
+ NotEqualHelperLoop(v);
+}
+
+TEST_F(TimingFunctionTest, CubicOperatorEqReflectivity) {
+ scoped_refptr<TimingFunction> cubic_a = CubicBezierTimingFunction::Preset(
+ CubicBezierTimingFunction::EaseType::EASE_IN);
+ scoped_refptr<TimingFunction> cubic_b =
+ CubicBezierTimingFunction::Create(0.42, 0.0, 1.0, 1.0);
+ EXPECT_NE(*cubic_a, *cubic_b);
+ EXPECT_NE(*cubic_b, *cubic_a);
+}
+
+TEST_F(TimingFunctionTest, StepsOperatorEq) {
+ scoped_refptr<TimingFunction> steps_timing_start1 =
+ StepsTimingFunction::Preset(StepsTimingFunction::StepPosition::START);
+ scoped_refptr<TimingFunction> steps_timing_start2 =
+ StepsTimingFunction::Preset(StepsTimingFunction::StepPosition::START);
+ EXPECT_EQ(*steps_timing_start1, *steps_timing_start1);
+ EXPECT_EQ(*steps_timing_start1, *steps_timing_start2);
+
+ scoped_refptr<TimingFunction> steps_timing_end1 =
+ StepsTimingFunction::Preset(StepsTimingFunction::StepPosition::END);
+ scoped_refptr<TimingFunction> steps_timing_end2 =
+ StepsTimingFunction::Preset(StepsTimingFunction::StepPosition::END);
+ EXPECT_EQ(*steps_timing_end1, *steps_timing_end1);
+ EXPECT_EQ(*steps_timing_end1, *steps_timing_end2);
+
+ scoped_refptr<TimingFunction> steps_timing_custom1 =
+ StepsTimingFunction::Create(5, StepsTimingFunction::StepPosition::START);
+ scoped_refptr<TimingFunction> steps_timing_custom2 =
+ StepsTimingFunction::Create(5, StepsTimingFunction::StepPosition::END);
+ scoped_refptr<TimingFunction> steps_timing_custom3 =
+ StepsTimingFunction::Create(7, StepsTimingFunction::StepPosition::START);
+ scoped_refptr<TimingFunction> steps_timing_custom4 =
+ StepsTimingFunction::Create(7, StepsTimingFunction::StepPosition::END);
+
+ EXPECT_EQ(
+ *StepsTimingFunction::Create(5, StepsTimingFunction::StepPosition::START),
+ *steps_timing_custom1);
+ EXPECT_EQ(
+ *StepsTimingFunction::Create(5, StepsTimingFunction::StepPosition::END),
+ *steps_timing_custom2);
+ EXPECT_EQ(
+ *StepsTimingFunction::Create(7, StepsTimingFunction::StepPosition::START),
+ *steps_timing_custom3);
+ EXPECT_EQ(
+ *StepsTimingFunction::Create(7, StepsTimingFunction::StepPosition::END),
+ *steps_timing_custom4);
+
+ Vector<std::pair<std::string, scoped_refptr<TimingFunction>>> v;
+ v.push_back(std::make_pair("stepsTimingStart1", steps_timing_start1));
+ v.push_back(std::make_pair("stepsTimingEnd1", steps_timing_end1));
+ v.push_back(std::make_pair("stepsTimingCustom1", steps_timing_custom1));
+ v.push_back(std::make_pair("stepsTimingCustom2", steps_timing_custom2));
+ v.push_back(std::make_pair("stepsTimingCustom3", steps_timing_custom3));
+ v.push_back(std::make_pair("stepsTimingCustom4", steps_timing_custom4));
+ NotEqualHelperLoop(v);
+}
+
+TEST_F(TimingFunctionTest, StepsOperatorEqPreset) {
+ scoped_refptr<TimingFunction> steps_a =
+ StepsTimingFunction::Preset(StepsTimingFunction::StepPosition::START);
+ scoped_refptr<TimingFunction> steps_b =
+ StepsTimingFunction::Create(1, StepsTimingFunction::StepPosition::START);
+ EXPECT_EQ(*steps_a, *steps_b);
+ EXPECT_EQ(*steps_b, *steps_a);
+}
+
+TEST_F(TimingFunctionTest, FramesOperatorEq) {
+ scoped_refptr<TimingFunction> frames_timing1 =
+ FramesTimingFunction::Create(5);
+ scoped_refptr<TimingFunction> frames_timing2 =
+ FramesTimingFunction::Create(7);
+
+ EXPECT_EQ(*FramesTimingFunction::Create(5), *frames_timing1);
+ EXPECT_EQ(*FramesTimingFunction::Create(7), *frames_timing2);
+
+ Vector<std::pair<std::string, scoped_refptr<TimingFunction>>> v;
+ v.push_back(std::make_pair("framesTiming1", frames_timing1));
+ v.push_back(std::make_pair("framesTiming2", frames_timing2));
+ NotEqualHelperLoop(v);
+}
+
+TEST_F(TimingFunctionTest, LinearEvaluate) {
+ scoped_refptr<TimingFunction> linear_timing = LinearTimingFunction::Shared();
+ EXPECT_EQ(0.2, linear_timing->Evaluate(0.2, 0));
+ EXPECT_EQ(0.6, linear_timing->Evaluate(0.6, 0));
+ EXPECT_EQ(-0.2, linear_timing->Evaluate(-0.2, 0));
+ EXPECT_EQ(1.6, linear_timing->Evaluate(1.6, 0));
+}
+
+TEST_F(TimingFunctionTest, LinearRange) {
+ double start = 0;
+ double end = 1;
+ scoped_refptr<TimingFunction> linear_timing = LinearTimingFunction::Shared();
+ linear_timing->Range(&start, &end);
+ EXPECT_NEAR(0, start, 0.01);
+ EXPECT_NEAR(1, end, 0.01);
+ start = -1;
+ end = 10;
+ linear_timing->Range(&start, &end);
+ EXPECT_NEAR(-1, start, 0.01);
+ EXPECT_NEAR(10, end, 0.01);
+}
+
+TEST_F(TimingFunctionTest, StepRange) {
+ double start = 0;
+ double end = 1;
+ scoped_refptr<TimingFunction> steps =
+ StepsTimingFunction::Preset(StepsTimingFunction::StepPosition::START);
+ steps->Range(&start, &end);
+ EXPECT_NEAR(0, start, 0.01);
+ EXPECT_NEAR(1, end, 0.01);
+
+ start = -1;
+ end = 10;
+ steps->Range(&start, &end);
+ EXPECT_NEAR(0, start, 0.01);
+ EXPECT_NEAR(1, end, 0.01);
+}
+
+TEST_F(TimingFunctionTest, FrameRange) {
+ double start = 0;
+ double end = 1;
+ scoped_refptr<TimingFunction> frames = FramesTimingFunction::Create(4);
+ frames->Range(&start, &end);
+ EXPECT_NEAR(0, start, 0.01);
+ EXPECT_NEAR(1, end, 0.01);
+
+ start = -1;
+ end = 10;
+ frames->Range(&start, &end);
+ EXPECT_NEAR(0, start, 0.01);
+ EXPECT_NEAR(1, end, 0.01);
+}
+
+TEST_F(TimingFunctionTest, CubicRange) {
+ double start = 0;
+ double end = 1;
+
+ scoped_refptr<TimingFunction> cubic_ease_timing =
+ CubicBezierTimingFunction::Preset(
+ CubicBezierTimingFunction::EaseType::EASE);
+ start = 0;
+ end = 1;
+ cubic_ease_timing->Range(&start, &end);
+ EXPECT_NEAR(0, start, 0.01);
+ EXPECT_NEAR(1, end, 0.01);
+ start = -1;
+ end = 10;
+ cubic_ease_timing->Range(&start, &end);
+ EXPECT_NEAR(-0.4, start, 0.01);
+ EXPECT_NEAR(1, end, 0.01);
+
+ scoped_refptr<TimingFunction> cubic_ease_in_timing =
+ CubicBezierTimingFunction::Preset(
+ CubicBezierTimingFunction::EaseType::EASE_IN);
+ start = 0;
+ end = 1;
+ cubic_ease_in_timing->Range(&start, &end);
+ EXPECT_NEAR(0, start, 0.01);
+ EXPECT_NEAR(1, end, 0.01);
+ start = -1;
+ end = 10;
+ cubic_ease_in_timing->Range(&start, &end);
+ EXPECT_NEAR(0.0, start, 0.01);
+ EXPECT_NEAR(16.51, end, 0.01);
+
+ scoped_refptr<TimingFunction> cubic_ease_out_timing =
+ CubicBezierTimingFunction::Preset(
+ CubicBezierTimingFunction::EaseType::EASE_OUT);
+ start = 0;
+ end = 1;
+ cubic_ease_out_timing->Range(&start, &end);
+ EXPECT_NEAR(0, start, 0.01);
+ EXPECT_NEAR(1, end, 0.01);
+ start = -1;
+ end = 10;
+ cubic_ease_out_timing->Range(&start, &end);
+ EXPECT_NEAR(-1.72, start, 0.01);
+ EXPECT_NEAR(1.0, end, 0.01);
+
+ scoped_refptr<TimingFunction> cubic_ease_in_out_timing =
+ CubicBezierTimingFunction::Preset(
+ CubicBezierTimingFunction::EaseType::EASE_IN_OUT);
+ start = 0;
+ end = 1;
+ cubic_ease_in_out_timing->Range(&start, &end);
+ EXPECT_NEAR(0, start, 0.01);
+ EXPECT_NEAR(1, end, 0.01);
+ start = -1;
+ end = 10;
+ cubic_ease_in_out_timing->Range(&start, &end);
+ EXPECT_NEAR(0.0, start, 0.01);
+ EXPECT_NEAR(1.0, end, 0.01);
+
+ scoped_refptr<TimingFunction> cubic_custom_timing =
+ CubicBezierTimingFunction::Create(0.17, 0.67, 1.0, -1.73);
+ start = 0;
+ end = 1;
+ cubic_custom_timing->Range(&start, &end);
+ EXPECT_NEAR(-0.33, start, 0.01);
+ EXPECT_NEAR(1.0, end, 0.01);
+
+ start = -1;
+ end = 10;
+ cubic_custom_timing->Range(&start, &end);
+ EXPECT_NEAR(-3.94, start, 0.01);
+ EXPECT_NEAR(4.578, end, 0.01);
+}
+
+TEST_F(TimingFunctionTest, CubicEvaluate) {
+ double tolerance = 0.01;
+ scoped_refptr<TimingFunction> cubic_ease_timing =
+ CubicBezierTimingFunction::Preset(
+ CubicBezierTimingFunction::EaseType::EASE);
+ EXPECT_NEAR(0.418, cubic_ease_timing->Evaluate(0.25, tolerance), tolerance);
+ EXPECT_NEAR(0.805, cubic_ease_timing->Evaluate(0.50, tolerance), tolerance);
+ EXPECT_NEAR(0.960, cubic_ease_timing->Evaluate(0.75, tolerance), tolerance);
+
+ scoped_refptr<TimingFunction> cubic_ease_in_timing =
+ CubicBezierTimingFunction::Preset(
+ CubicBezierTimingFunction::EaseType::EASE_IN);
+ EXPECT_NEAR(0.093, cubic_ease_in_timing->Evaluate(0.25, tolerance),
+ tolerance);
+ EXPECT_NEAR(0.305, cubic_ease_in_timing->Evaluate(0.50, tolerance),
+ tolerance);
+ EXPECT_NEAR(0.620, cubic_ease_in_timing->Evaluate(0.75, tolerance),
+ tolerance);
+
+ scoped_refptr<TimingFunction> cubic_ease_out_timing =
+ CubicBezierTimingFunction::Preset(
+ CubicBezierTimingFunction::EaseType::EASE_OUT);
+ EXPECT_NEAR(0.379, cubic_ease_out_timing->Evaluate(0.25, tolerance),
+ tolerance);
+ EXPECT_NEAR(0.694, cubic_ease_out_timing->Evaluate(0.50, tolerance),
+ tolerance);
+ EXPECT_NEAR(0.906, cubic_ease_out_timing->Evaluate(0.75, tolerance),
+ tolerance);
+
+ scoped_refptr<TimingFunction> cubic_ease_in_out_timing =
+ CubicBezierTimingFunction::Preset(
+ CubicBezierTimingFunction::EaseType::EASE_IN_OUT);
+ EXPECT_NEAR(0.128, cubic_ease_in_out_timing->Evaluate(0.25, tolerance),
+ tolerance);
+ EXPECT_NEAR(0.500, cubic_ease_in_out_timing->Evaluate(0.50, tolerance),
+ tolerance);
+ EXPECT_NEAR(0.871, cubic_ease_in_out_timing->Evaluate(0.75, tolerance),
+ tolerance);
+
+ scoped_refptr<TimingFunction> cubic_custom_timing =
+ CubicBezierTimingFunction::Create(0.17, 0.67, 1, -1.73);
+ EXPECT_NEAR(0.034, cubic_custom_timing->Evaluate(0.25, tolerance), tolerance);
+ EXPECT_NEAR(-0.217, cubic_custom_timing->Evaluate(0.50, tolerance),
+ tolerance);
+ EXPECT_NEAR(-0.335, cubic_custom_timing->Evaluate(0.75, tolerance),
+ tolerance);
+}
+
+TEST_F(TimingFunctionTest, StepsEvaluate) {
+ scoped_refptr<TimingFunction> steps_timing_start =
+ StepsTimingFunction::Preset(StepsTimingFunction::StepPosition::START);
+ EXPECT_EQ(-1.00, steps_timing_start->Evaluate(-1.10, 0));
+ EXPECT_EQ(0.00, steps_timing_start->Evaluate(-0.10, 0));
+ EXPECT_EQ(1.00, steps_timing_start->Evaluate(0.00, 0));
+ EXPECT_EQ(1.00, steps_timing_start->Evaluate(0.20, 0));
+ EXPECT_EQ(1.00, steps_timing_start->Evaluate(0.60, 0));
+ EXPECT_EQ(1.00, steps_timing_start->Evaluate(1.00, 0));
+ EXPECT_EQ(3.00, steps_timing_start->Evaluate(2.00, 0));
+
+ scoped_refptr<TimingFunction> steps_timing_middle =
+ StepsTimingFunction::Preset(StepsTimingFunction::StepPosition::MIDDLE);
+ EXPECT_EQ(-2.00, steps_timing_middle->Evaluate(-2.50, 0));
+ EXPECT_EQ(0.00, steps_timing_middle->Evaluate(0.00, 0));
+ EXPECT_EQ(0.00, steps_timing_middle->Evaluate(0.49, 0));
+ EXPECT_EQ(1.00, steps_timing_middle->Evaluate(0.50, 0));
+ EXPECT_EQ(1.00, steps_timing_middle->Evaluate(1.00, 0));
+ EXPECT_EQ(3.00, steps_timing_middle->Evaluate(2.50, 0));
+
+ scoped_refptr<TimingFunction> steps_timing_end =
+ StepsTimingFunction::Preset(StepsTimingFunction::StepPosition::END);
+ EXPECT_EQ(-2.00, steps_timing_end->Evaluate(-2.00, 0));
+ EXPECT_EQ(0.00, steps_timing_end->Evaluate(0.00, 0));
+ EXPECT_EQ(0.00, steps_timing_end->Evaluate(0.20, 0));
+ EXPECT_EQ(0.00, steps_timing_end->Evaluate(0.60, 0));
+ EXPECT_EQ(1.00, steps_timing_end->Evaluate(1.00, 0));
+ EXPECT_EQ(2.00, steps_timing_end->Evaluate(2.00, 0));
+
+ scoped_refptr<TimingFunction> steps_timing_custom_start =
+ StepsTimingFunction::Create(4, StepsTimingFunction::StepPosition::START);
+ EXPECT_EQ(-0.25, steps_timing_custom_start->Evaluate(-0.50, 0));
+ EXPECT_EQ(0.25, steps_timing_custom_start->Evaluate(0.00, 0));
+ EXPECT_EQ(0.25, steps_timing_custom_start->Evaluate(0.24, 0));
+ EXPECT_EQ(0.50, steps_timing_custom_start->Evaluate(0.25, 0));
+ EXPECT_EQ(0.50, steps_timing_custom_start->Evaluate(0.49, 0));
+ EXPECT_EQ(0.75, steps_timing_custom_start->Evaluate(0.50, 0));
+ EXPECT_EQ(0.75, steps_timing_custom_start->Evaluate(0.74, 0));
+ EXPECT_EQ(1.00, steps_timing_custom_start->Evaluate(0.75, 0));
+ EXPECT_EQ(1.00, steps_timing_custom_start->Evaluate(1.00, 0));
+ EXPECT_EQ(1.75, steps_timing_custom_start->Evaluate(1.50, 0));
+
+ scoped_refptr<TimingFunction> steps_timing_custom_middle =
+ StepsTimingFunction::Create(4, StepsTimingFunction::StepPosition::MIDDLE);
+ EXPECT_EQ(-2.00, steps_timing_custom_middle->Evaluate(-2.00, 0));
+ EXPECT_EQ(0.00, steps_timing_custom_middle->Evaluate(0.00, 0));
+ EXPECT_EQ(0.00, steps_timing_custom_middle->Evaluate(0.12, 0));
+ EXPECT_EQ(0.25, steps_timing_custom_middle->Evaluate(0.13, 0));
+ EXPECT_EQ(0.25, steps_timing_custom_middle->Evaluate(0.37, 0));
+ EXPECT_EQ(0.50, steps_timing_custom_middle->Evaluate(0.38, 0));
+ EXPECT_EQ(0.50, steps_timing_custom_middle->Evaluate(0.62, 0));
+ EXPECT_EQ(0.75, steps_timing_custom_middle->Evaluate(0.63, 0));
+ EXPECT_EQ(0.75, steps_timing_custom_middle->Evaluate(0.87, 0));
+ EXPECT_EQ(1.00, steps_timing_custom_middle->Evaluate(0.88, 0));
+ EXPECT_EQ(1.00, steps_timing_custom_middle->Evaluate(1.00, 0));
+ EXPECT_EQ(3.00, steps_timing_custom_middle->Evaluate(3.00, 0));
+
+ scoped_refptr<TimingFunction> steps_timing_custom_end =
+ StepsTimingFunction::Create(4, StepsTimingFunction::StepPosition::END);
+ EXPECT_EQ(-2.00, steps_timing_custom_end->Evaluate(-2.00, 0));
+ EXPECT_EQ(0.00, steps_timing_custom_end->Evaluate(0.00, 0));
+ EXPECT_EQ(0.00, steps_timing_custom_end->Evaluate(0.24, 0));
+ EXPECT_EQ(0.25, steps_timing_custom_end->Evaluate(0.25, 0));
+ EXPECT_EQ(0.25, steps_timing_custom_end->Evaluate(0.49, 0));
+ EXPECT_EQ(0.50, steps_timing_custom_end->Evaluate(0.50, 0));
+ EXPECT_EQ(0.50, steps_timing_custom_end->Evaluate(0.74, 0));
+ EXPECT_EQ(0.75, steps_timing_custom_end->Evaluate(0.75, 0));
+ EXPECT_EQ(0.75, steps_timing_custom_end->Evaluate(0.99, 0));
+ EXPECT_EQ(1.00, steps_timing_custom_end->Evaluate(1.00, 0));
+ EXPECT_EQ(2.00, steps_timing_custom_end->Evaluate(2.00, 0));
+}
+
+TEST_F(TimingFunctionTest, FramesEvaluate) {
+ scoped_refptr<TimingFunction> frames_timing = FramesTimingFunction::Create(5);
+ EXPECT_EQ(-2.50, frames_timing->Evaluate(-2.00, 0));
+ EXPECT_EQ(0.00, frames_timing->Evaluate(0.00, 0));
+ EXPECT_EQ(0.00, frames_timing->Evaluate(0.19, 0));
+ EXPECT_EQ(0.25, frames_timing->Evaluate(0.20, 0));
+ EXPECT_EQ(0.25, frames_timing->Evaluate(0.39, 0));
+ EXPECT_EQ(0.50, frames_timing->Evaluate(0.40, 0));
+ EXPECT_EQ(0.50, frames_timing->Evaluate(0.59, 0));
+ EXPECT_EQ(0.75, frames_timing->Evaluate(0.60, 0));
+ EXPECT_EQ(0.75, frames_timing->Evaluate(0.79, 0));
+ EXPECT_EQ(1.00, frames_timing->Evaluate(0.80, 0));
+ EXPECT_EQ(1.00, frames_timing->Evaluate(1.00, 0));
+ EXPECT_EQ(3.75, frames_timing->Evaluate(3.00, 0));
+}
+
+} // namespace
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/async_file_system_callbacks.h b/chromium/third_party/blink/renderer/platform/async_file_system_callbacks.h
new file mode 100644
index 00000000000..94b1cbf828f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/async_file_system_callbacks.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2010 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_ASYNC_FILE_SYSTEM_CALLBACKS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_ASYNC_FILE_SYSTEM_CALLBACKS_H_
+
+#include <memory>
+#include "third_party/blink/public/platform/web_file_writer.h"
+#include "third_party/blink/renderer/platform/blob/blob_data.h"
+#include "third_party/blink/renderer/platform/file_metadata.h"
+#include "third_party/blink/renderer/platform/file_system_type.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT AsyncFileSystemCallbacks {
+ USING_FAST_MALLOC(AsyncFileSystemCallbacks);
+ WTF_MAKE_NONCOPYABLE(AsyncFileSystemCallbacks);
+
+ public:
+ AsyncFileSystemCallbacks() : block_until_completion_(false) {}
+
+ // Called when a requested operation is completed successfully.
+ virtual void DidSucceed() { NOTREACHED(); }
+
+ // Called when a requested file system is opened.
+ virtual void DidOpenFileSystem(const String& name, const KURL& root_url) {
+ NOTREACHED();
+ }
+
+ // Called when a filesystem URL is resolved.
+ virtual void DidResolveURL(const String& name,
+ const KURL& root_url,
+ FileSystemType,
+ const String& file_path,
+ bool is_directory) {
+ NOTREACHED();
+ }
+
+ // Called when a file metadata is read successfully.
+ virtual void DidReadMetadata(const FileMetadata&) { NOTREACHED(); }
+
+ // Called when a snapshot file is created successfully.
+ virtual void DidCreateSnapshotFile(const FileMetadata&,
+ scoped_refptr<BlobDataHandle> snapshot) {
+ NOTREACHED();
+ }
+
+ // Called when a directory entry is read.
+ virtual void DidReadDirectoryEntry(const String& name, bool is_directory) {
+ NOTREACHED();
+ }
+
+ // Called after a chunk of directory entries have been read (i.e. indicates
+ // it's good time to call back to the application). If hasMore is true there
+ // can be more chunks.
+ virtual void DidReadDirectoryEntries(bool has_more) { NOTREACHED(); }
+
+ // Called when an AsyncFileWrter has been created successfully.
+ virtual void DidCreateFileWriter(std::unique_ptr<WebFileWriter>,
+ long long length) {
+ NOTREACHED();
+ }
+
+ // Called when there was an error.
+ virtual void DidFail(int code) = 0;
+
+ // Returns true if the caller expects that the calling thread blocks
+ // until completion.
+ virtual bool ShouldBlockUntilCompletion() const {
+ return block_until_completion_;
+ }
+
+ void SetShouldBlockUntilCompletion(bool flag) {
+ block_until_completion_ = flag;
+ }
+
+ virtual ~AsyncFileSystemCallbacks() = default;
+
+ private:
+ bool block_until_completion_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_ASYNC_FILE_SYSTEM_CALLBACKS_H_
diff --git a/chromium/third_party/blink/renderer/platform/async_method_runner.h b/chromium/third_party/blink/renderer/platform/async_method_runner.h
new file mode 100644
index 00000000000..f45f506c4d8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/async_method_runner.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_ASYNC_METHOD_RUNNER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_ASYNC_METHOD_RUNNER_H_
+
+#include "third_party/blink/renderer/platform/timer.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+template <typename TargetClass>
+class AsyncMethodRunner final
+ : public GarbageCollectedFinalized<AsyncMethodRunner<TargetClass>> {
+ WTF_MAKE_NONCOPYABLE(AsyncMethodRunner);
+
+ public:
+ typedef void (TargetClass::*TargetMethod)();
+
+ static AsyncMethodRunner* Create(
+ TargetClass* object,
+ TargetMethod method,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+ return new AsyncMethodRunner(object, method, std::move(task_runner));
+ }
+
+ ~AsyncMethodRunner() = default;
+
+ // Schedules to run the method asynchronously. Do nothing if it's already
+ // scheduled. If it's suspended, remember to schedule to run the method when
+ // Unpause() is called.
+ void RunAsync() {
+ if (paused_) {
+ DCHECK(!timer_.IsActive());
+ run_when_unpaused_ = true;
+ return;
+ }
+
+ // FIXME: runAsync should take a TraceLocation and pass it to timer here.
+ if (!timer_.IsActive())
+ timer_.StartOneShot(TimeDelta(), FROM_HERE);
+ }
+
+ // If it's scheduled to run the method, cancel it and remember to schedule
+ // it again when resume() is called. Mainly for implementing
+ // PausableObject::Pause().
+ void Pause() {
+ if (paused_)
+ return;
+ paused_ = true;
+
+ if (!timer_.IsActive())
+ return;
+
+ timer_.Stop();
+ run_when_unpaused_ = true;
+ }
+
+ // Resumes pending method run.
+ void Unpause() {
+ if (!paused_)
+ return;
+ paused_ = false;
+
+ if (!run_when_unpaused_)
+ return;
+
+ run_when_unpaused_ = false;
+ // FIXME: resume should take a TraceLocation and pass it to timer here.
+ timer_.StartOneShot(TimeDelta(), FROM_HERE);
+ }
+
+ void Stop() {
+ if (paused_) {
+ DCHECK(!timer_.IsActive());
+ run_when_unpaused_ = false;
+ paused_ = false;
+ return;
+ }
+
+ DCHECK(!run_when_unpaused_);
+ timer_.Stop();
+ }
+
+ bool IsActive() const { return timer_.IsActive(); }
+
+ void Trace(blink::Visitor* visitor) { visitor->Trace(object_); }
+
+ private:
+ AsyncMethodRunner(TargetClass* object,
+ TargetMethod method,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+ : timer_(std::move(task_runner),
+ this,
+ &AsyncMethodRunner<TargetClass>::Fired),
+ object_(object),
+ method_(method),
+ paused_(false),
+ run_when_unpaused_(false) {}
+
+ void Fired(TimerBase*) { (object_->*method_)(); }
+
+ TaskRunnerTimer<AsyncMethodRunner<TargetClass>> timer_;
+
+ Member<TargetClass> object_;
+ TargetMethod method_;
+
+ bool paused_;
+ bool run_when_unpaused_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/audio/OWNERS b/chromium/third_party/blink/renderer/platform/audio/OWNERS
new file mode 100644
index 00000000000..caaf05cf1d8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/OWNERS
@@ -0,0 +1,5 @@
+hongchan@chromium.org
+kbr@chromium.org
+rtoy@chromium.org
+
+# COMPONENT: Blink>WebAudio
diff --git a/chromium/third_party/blink/renderer/platform/audio/android/fft_frame_open_maxdl_android.cc b/chromium/third_party/blink/renderer/platform/audio/android/fft_frame_open_maxdl_android.cc
new file mode 100644
index 00000000000..304295aa82d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/android/fft_frame_open_maxdl_android.cc
@@ -0,0 +1,160 @@
+/* Copyright (C) 2013 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:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 "build/build_config.h"
+
+#if defined(OS_ANDROID) && defined(WTF_USE_WEBAUDIO_OPENMAX_DL_FFT)
+
+#include "third_party/blink/renderer/platform/audio/fft_frame.h"
+
+#include <dl/sp/api/armSP.h>
+#include <dl/sp/api/omxSP.h>
+#include "third_party/blink/renderer/platform/audio/audio_array.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+namespace blink {
+
+const unsigned kMaxFFTPow2Size = 15;
+
+// Normal constructor: allocates for a given fftSize.
+FFTFrame::FFTFrame(unsigned fft_size)
+ : fft_size_(fft_size),
+ log2fft_size_(static_cast<unsigned>(log2(fft_size))),
+ real_data_(fft_size / 2),
+ imag_data_(fft_size / 2),
+ forward_context_(nullptr),
+ inverse_context_(nullptr),
+ complex_data_(fft_size) {
+ // We only allow power of two.
+ DCHECK_EQ(1UL << log2fft_size_, fft_size_);
+
+ forward_context_ = ContextForSize(log2fft_size_);
+ inverse_context_ = ContextForSize(log2fft_size_);
+}
+
+// Creates a blank/empty frame (interpolate() must later be called).
+FFTFrame::FFTFrame()
+ : fft_size_(0),
+ log2fft_size_(0),
+ forward_context_(nullptr),
+ inverse_context_(nullptr) {}
+
+// Copy constructor.
+FFTFrame::FFTFrame(const FFTFrame& frame)
+ : fft_size_(frame.fft_size_),
+ log2fft_size_(frame.log2fft_size_),
+ real_data_(frame.fft_size_ / 2),
+ imag_data_(frame.fft_size_ / 2),
+ forward_context_(nullptr),
+ inverse_context_(nullptr),
+ complex_data_(frame.fft_size_) {
+ forward_context_ = ContextForSize(log2fft_size_);
+ inverse_context_ = ContextForSize(log2fft_size_);
+
+ // Copy/setup frame data.
+ unsigned nbytes = sizeof(float) * (fft_size_ / 2);
+ memcpy(RealData(), frame.RealData(), nbytes);
+ memcpy(ImagData(), frame.ImagData(), nbytes);
+}
+
+void FFTFrame::Initialize() {}
+
+void FFTFrame::Cleanup() {}
+
+FFTFrame::~FFTFrame() {
+ if (forward_context_)
+ free(forward_context_);
+ if (inverse_context_)
+ free(inverse_context_);
+}
+
+void FFTFrame::DoFFT(const float* data) {
+ DCHECK(forward_context_);
+
+ if (forward_context_) {
+ AudioFloatArray complex_fft(fft_size_ + 2);
+
+ omxSP_FFTFwd_RToCCS_F32(data, complex_fft.Data(), forward_context_);
+
+ unsigned len = fft_size_ / 2;
+
+ // Split FFT data into real and imaginary arrays.
+ const float* c = complex_fft.Data();
+ float* real = real_data_.Data();
+ float* imag = imag_data_.Data();
+ for (unsigned k = 1; k < len; ++k) {
+ int index = 2 * k;
+ real[k] = c[index];
+ imag[k] = c[index + 1];
+ }
+ real[0] = c[0];
+ imag[0] = c[fft_size_];
+ }
+}
+
+void FFTFrame::DoInverseFFT(float* data) {
+ DCHECK(inverse_context_);
+
+ if (inverse_context_) {
+ AudioFloatArray fft_data_array(fft_size_ + 2);
+
+ unsigned len = fft_size_ / 2;
+
+ // Pack the real and imaginary data into the complex array format
+ float* fft_data = fft_data_array.Data();
+ const float* real = real_data_.Data();
+ const float* imag = imag_data_.Data();
+ for (unsigned k = 1; k < len; ++k) {
+ int index = 2 * k;
+ fft_data[index] = real[k];
+ fft_data[index + 1] = imag[k];
+ }
+ fft_data[0] = real[0];
+ fft_data[1] = 0;
+ fft_data[fft_size_] = imag[0];
+ fft_data[fft_size_ + 1] = 0;
+
+ omxSP_FFTInv_CCSToR_F32(fft_data, data, inverse_context_);
+ }
+}
+
+OMXFFTSpec_R_F32* FFTFrame::ContextForSize(unsigned log2fft_size) {
+ DCHECK(log2fft_size);
+ DCHECK_LE(log2fft_size, kMaxFFTPow2Size);
+ int buf_size;
+ OMXResult status = omxSP_FFTGetBufSize_R_F32(log2fft_size, &buf_size);
+
+ if (status == OMX_Sts_NoErr) {
+ OMXFFTSpec_R_F32* context =
+ static_cast<OMXFFTSpec_R_F32*>(malloc(buf_size));
+ omxSP_FFTInit_R_F32(context, log2fft_size);
+ return context;
+ }
+
+ return nullptr;
+}
+
+} // namespace blink
+
+#endif // #if defined(OS_ANDROID) && !defined(WTF_USE_WEBAUDIO_OPENMAX_DL_FFT)
diff --git a/chromium/third_party/blink/renderer/platform/audio/audio_array.h b/chromium/third_party/blink/renderer/platform/audio/audio_array.h
new file mode 100644
index 00000000000..fc190a168ff
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/audio_array.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_ARRAY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_ARRAY_H_
+
+#include <string.h>
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+template <typename T>
+class AudioArray {
+ USING_FAST_MALLOC(AudioArray);
+ WTF_MAKE_NONCOPYABLE(AudioArray);
+
+ public:
+ AudioArray() : allocation_(nullptr), aligned_data_(nullptr), size_(0) {}
+ explicit AudioArray(size_t n)
+ : allocation_(nullptr), aligned_data_(nullptr), size_(0) {
+ Allocate(n);
+ }
+
+ ~AudioArray() { WTF::Partitions::FastFree(allocation_); }
+
+ // It's OK to call allocate() multiple times, but data will *not* be copied
+ // from an initial allocation if re-allocated. Allocations are
+ // zero-initialized.
+ void Allocate(size_t n) {
+ // Although n is a size_t, its true limit is max unsigned because we use
+ // unsigned in zeroRange() and copyToRange(). Also check for integer
+ // overflow.
+ CHECK_LE(n, std::numeric_limits<unsigned>::max() / sizeof(T));
+
+ unsigned initial_size = sizeof(T) * n;
+
+#if defined(ARCH_CPU_X86_FAMILY) || defined(WTF_USE_WEBAUDIO_FFMPEG) || \
+ defined(WTF_USE_WEBAUDIO_OPENMAX_DL_FFT)
+ const size_t kAlignment = 32;
+#else
+ const size_t kAlignment = 16;
+#endif
+
+ if (allocation_)
+ WTF::Partitions::FastFree(allocation_);
+
+ bool is_allocation_good = false;
+
+ while (!is_allocation_good) {
+ // Initially we try to allocate the exact size, but if it's not aligned
+ // then we'll have to reallocate and from then on allocate extra.
+ static size_t extra_allocation_bytes = 0;
+
+ // Again, check for integer overflow.
+ CHECK_GE(initial_size + extra_allocation_bytes, initial_size);
+
+ T* allocation = static_cast<T*>(WTF::Partitions::FastMalloc(
+ initial_size + extra_allocation_bytes,
+ WTF_HEAP_PROFILER_TYPE_NAME(AudioArray<T>)));
+ CHECK(allocation);
+
+ T* aligned_data = AlignedAddress(allocation, kAlignment);
+
+ if (aligned_data == allocation || extra_allocation_bytes == kAlignment) {
+ allocation_ = allocation;
+ aligned_data_ = aligned_data;
+ size_ = n;
+ is_allocation_good = true;
+ Zero();
+ } else {
+ // always allocate extra after the first alignment failure.
+ extra_allocation_bytes = kAlignment;
+ WTF::Partitions::FastFree(allocation);
+ }
+ }
+ }
+
+ T* Data() { return aligned_data_; }
+ const T* Data() const { return aligned_data_; }
+ size_t size() const { return size_; }
+
+ T& at(size_t i) {
+ // Note that although it is a size_t, m_size is now guaranteed to be
+ // no greater than max unsigned. This guarantee is enforced in allocate().
+ SECURITY_DCHECK(i < size());
+ return Data()[i];
+ }
+
+ T& operator[](size_t i) { return at(i); }
+
+ void Zero() {
+ // This multiplication is made safe by the check in allocate().
+ memset(this->Data(), 0, sizeof(T) * this->size());
+ }
+
+ void ZeroRange(unsigned start, unsigned end) {
+ bool is_safe = (start <= end) && (end <= this->size());
+ DCHECK(is_safe);
+ if (!is_safe)
+ return;
+
+ // This expression cannot overflow because end - start cannot be
+ // greater than m_size, which is safe due to the check in allocate().
+ memset(this->Data() + start, 0, sizeof(T) * (end - start));
+ }
+
+ void CopyToRange(const T* source_data, unsigned start, unsigned end) {
+ bool is_safe = (start <= end) && (end <= this->size());
+ DCHECK(is_safe);
+ if (!is_safe)
+ return;
+
+ // This expression cannot overflow because end - start cannot be
+ // greater than m_size, which is safe due to the check in allocate().
+ memcpy(this->Data() + start, source_data, sizeof(T) * (end - start));
+ }
+
+ private:
+ static T* AlignedAddress(T* address, intptr_t alignment) {
+ intptr_t value = reinterpret_cast<intptr_t>(address);
+ return reinterpret_cast<T*>((value + alignment - 1) & ~(alignment - 1));
+ }
+
+ T* allocation_;
+ T* aligned_data_;
+ size_t size_;
+};
+
+typedef AudioArray<float> AudioFloatArray;
+typedef AudioArray<double> AudioDoubleArray;
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_ARRAY_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/audio_bus.cc b/chromium/third_party/blink/renderer/platform/audio/audio_bus.cc
new file mode 100644
index 00000000000..2b68239b1f1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/audio_bus.cc
@@ -0,0 +1,733 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/audio/audio_bus.h"
+
+#include <assert.h>
+#include <math.h>
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_audio_bus.h"
+#include "third_party/blink/renderer/platform/audio/audio_file_reader.h"
+#include "third_party/blink/renderer/platform/audio/denormal_disabler.h"
+#include "third_party/blink/renderer/platform/audio/sinc_resampler.h"
+#include "third_party/blink/renderer/platform/audio/vector_math.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+
+namespace blink {
+
+using namespace VectorMath;
+
+const unsigned kMaxBusChannels = 32;
+
+scoped_refptr<AudioBus> AudioBus::Create(unsigned number_of_channels,
+ size_t length,
+ bool allocate) {
+ DCHECK_LE(number_of_channels, kMaxBusChannels);
+ if (number_of_channels > kMaxBusChannels)
+ return nullptr;
+
+ return base::AdoptRef(new AudioBus(number_of_channels, length, allocate));
+}
+
+AudioBus::AudioBus(unsigned number_of_channels, size_t length, bool allocate)
+ : length_(length), sample_rate_(0) {
+ channels_.ReserveInitialCapacity(number_of_channels);
+
+ for (unsigned i = 0; i < number_of_channels; ++i) {
+ std::unique_ptr<AudioChannel> channel =
+ allocate ? std::make_unique<AudioChannel>(length)
+ : std::make_unique<AudioChannel>(nullptr, length);
+ channels_.push_back(std::move(channel));
+ }
+
+ layout_ = kLayoutCanonical; // for now this is the only layout we define
+}
+
+void AudioBus::SetChannelMemory(unsigned channel_index,
+ float* storage,
+ size_t length) {
+ if (channel_index < channels_.size()) {
+ Channel(channel_index)->Set(storage, length);
+ // FIXME: verify that this length matches all the other channel lengths
+ length_ = length;
+ }
+}
+
+void AudioBus::ResizeSmaller(size_t new_length) {
+ DCHECK_LE(new_length, length_);
+ if (new_length <= length_)
+ length_ = new_length;
+
+ for (unsigned i = 0; i < channels_.size(); ++i)
+ channels_[i]->ResizeSmaller(new_length);
+}
+
+void AudioBus::Zero() {
+ for (unsigned i = 0; i < channels_.size(); ++i)
+ channels_[i]->Zero();
+}
+
+AudioChannel* AudioBus::ChannelByType(unsigned channel_type) {
+ // For now we only support canonical channel layouts...
+ if (layout_ != kLayoutCanonical)
+ return nullptr;
+
+ switch (NumberOfChannels()) {
+ case 1: // mono
+ if (channel_type == kChannelMono || channel_type == kChannelLeft)
+ return Channel(0);
+ return nullptr;
+
+ case 2: // stereo
+ switch (channel_type) {
+ case kChannelLeft:
+ return Channel(0);
+ case kChannelRight:
+ return Channel(1);
+ default:
+ return nullptr;
+ }
+
+ case 4: // quad
+ switch (channel_type) {
+ case kChannelLeft:
+ return Channel(0);
+ case kChannelRight:
+ return Channel(1);
+ case kChannelSurroundLeft:
+ return Channel(2);
+ case kChannelSurroundRight:
+ return Channel(3);
+ default:
+ return nullptr;
+ }
+
+ case 5: // 5.0
+ switch (channel_type) {
+ case kChannelLeft:
+ return Channel(0);
+ case kChannelRight:
+ return Channel(1);
+ case kChannelCenter:
+ return Channel(2);
+ case kChannelSurroundLeft:
+ return Channel(3);
+ case kChannelSurroundRight:
+ return Channel(4);
+ default:
+ return nullptr;
+ }
+
+ case 6: // 5.1
+ switch (channel_type) {
+ case kChannelLeft:
+ return Channel(0);
+ case kChannelRight:
+ return Channel(1);
+ case kChannelCenter:
+ return Channel(2);
+ case kChannelLFE:
+ return Channel(3);
+ case kChannelSurroundLeft:
+ return Channel(4);
+ case kChannelSurroundRight:
+ return Channel(5);
+ default:
+ return nullptr;
+ }
+ }
+
+ NOTREACHED();
+ return nullptr;
+}
+
+const AudioChannel* AudioBus::ChannelByType(unsigned type) const {
+ return const_cast<AudioBus*>(this)->ChannelByType(type);
+}
+
+// Returns true if the channel count and frame-size match.
+bool AudioBus::TopologyMatches(const AudioBus& bus) const {
+ if (NumberOfChannels() != bus.NumberOfChannels())
+ return false; // channel mismatch
+
+ // Make sure source bus has enough frames.
+ if (length() > bus.length())
+ return false; // frame-size mismatch
+
+ return true;
+}
+
+scoped_refptr<AudioBus> AudioBus::CreateBufferFromRange(
+ const AudioBus* source_buffer,
+ unsigned start_frame,
+ unsigned end_frame) {
+ size_t number_of_source_frames = source_buffer->length();
+ unsigned number_of_channels = source_buffer->NumberOfChannels();
+
+ // Sanity checking
+ bool is_range_safe =
+ start_frame < end_frame && end_frame <= number_of_source_frames;
+ DCHECK(is_range_safe);
+ if (!is_range_safe)
+ return nullptr;
+
+ size_t range_length = end_frame - start_frame;
+
+ scoped_refptr<AudioBus> audio_bus = Create(number_of_channels, range_length);
+ audio_bus->SetSampleRate(source_buffer->SampleRate());
+
+ for (unsigned i = 0; i < number_of_channels; ++i)
+ audio_bus->Channel(i)->CopyFromRange(source_buffer->Channel(i), start_frame,
+ end_frame);
+
+ return audio_bus;
+}
+
+float AudioBus::MaxAbsValue() const {
+ float max = 0.0f;
+ for (unsigned i = 0; i < NumberOfChannels(); ++i) {
+ const AudioChannel* channel = this->Channel(i);
+ max = std::max(max, channel->MaxAbsValue());
+ }
+
+ return max;
+}
+
+void AudioBus::Normalize() {
+ float max = MaxAbsValue();
+ if (max)
+ Scale(1.0f / max);
+}
+
+void AudioBus::Scale(float scale) {
+ for (unsigned i = 0; i < NumberOfChannels(); ++i)
+ Channel(i)->Scale(scale);
+}
+
+void AudioBus::CopyFrom(const AudioBus& source_bus,
+ ChannelInterpretation channel_interpretation) {
+ if (&source_bus == this)
+ return;
+
+ // Copying bus is equivalent to zeroing and then summing.
+ Zero();
+ SumFrom(source_bus, channel_interpretation);
+}
+
+void AudioBus::SumFrom(const AudioBus& source_bus,
+ ChannelInterpretation channel_interpretation) {
+ if (&source_bus == this)
+ return;
+
+ unsigned number_of_source_channels = source_bus.NumberOfChannels();
+ unsigned number_of_destination_channels = NumberOfChannels();
+
+ // If the channel numbers are equal, perform channels-wise summing.
+ if (number_of_source_channels == number_of_destination_channels) {
+ for (unsigned i = 0; i < number_of_source_channels; ++i)
+ Channel(i)->SumFrom(source_bus.Channel(i));
+
+ return;
+ }
+
+ // Otherwise perform up/down-mix or the discrete transfer based on the
+ // number of channels and the channel interpretation.
+ switch (channel_interpretation) {
+ case kSpeakers:
+ if (number_of_source_channels < number_of_destination_channels)
+ SumFromByUpMixing(source_bus);
+ else
+ SumFromByDownMixing(source_bus);
+ break;
+ case kDiscrete:
+ DiscreteSumFrom(source_bus);
+ break;
+ }
+}
+
+void AudioBus::DiscreteSumFrom(const AudioBus& source_bus) {
+ unsigned number_of_source_channels = source_bus.NumberOfChannels();
+ unsigned number_of_destination_channels = NumberOfChannels();
+
+ if (number_of_destination_channels < number_of_source_channels) {
+ // Down-mix by summing channels and dropping the remaining.
+ for (unsigned i = 0; i < number_of_destination_channels; ++i)
+ Channel(i)->SumFrom(source_bus.Channel(i));
+ } else if (number_of_destination_channels > number_of_source_channels) {
+ // Up-mix by summing as many channels as we have.
+ for (unsigned i = 0; i < number_of_source_channels; ++i)
+ Channel(i)->SumFrom(source_bus.Channel(i));
+ }
+}
+
+void AudioBus::SumFromByUpMixing(const AudioBus& source_bus) {
+ unsigned number_of_source_channels = source_bus.NumberOfChannels();
+ unsigned number_of_destination_channels = NumberOfChannels();
+
+ if ((number_of_source_channels == 1 && number_of_destination_channels == 2) ||
+ (number_of_source_channels == 1 && number_of_destination_channels == 4)) {
+ // Up-mixing: 1 -> 2, 1 -> 4
+ // output.L = input
+ // output.R = input
+ // output.SL = 0 (in the case of 1 -> 4)
+ // output.SR = 0 (in the case of 1 -> 4)
+ const AudioChannel* source_l = source_bus.ChannelByType(kChannelLeft);
+ ChannelByType(kChannelLeft)->SumFrom(source_l);
+ ChannelByType(kChannelRight)->SumFrom(source_l);
+ } else if (number_of_source_channels == 1 &&
+ number_of_destination_channels == 6) {
+ // Up-mixing: 1 -> 5.1
+ // output.L = 0
+ // output.R = 0
+ // output.C = input (put in center channel)
+ // output.LFE = 0
+ // output.SL = 0
+ // output.SR = 0
+ ChannelByType(kChannelCenter)
+ ->SumFrom(source_bus.ChannelByType(kChannelLeft));
+ } else if ((number_of_source_channels == 2 &&
+ number_of_destination_channels == 4) ||
+ (number_of_source_channels == 2 &&
+ number_of_destination_channels == 6)) {
+ // Up-mixing: 2 -> 4, 2 -> 5.1
+ // output.L = input.L
+ // output.R = input.R
+ // output.C = 0 (in the case of 2 -> 5.1)
+ // output.LFE = 0 (in the case of 2 -> 5.1)
+ // output.SL = 0
+ // output.SR = 0
+ ChannelByType(kChannelLeft)
+ ->SumFrom(source_bus.ChannelByType(kChannelLeft));
+ ChannelByType(kChannelRight)
+ ->SumFrom(source_bus.ChannelByType(kChannelRight));
+ } else if (number_of_source_channels == 4 &&
+ number_of_destination_channels == 6) {
+ // Up-mixing: 4 -> 5.1
+ // output.L = input.L
+ // output.R = input.R
+ // output.C = 0
+ // output.LFE = 0
+ // output.SL = input.SL
+ // output.SR = input.SR
+ ChannelByType(kChannelLeft)
+ ->SumFrom(source_bus.ChannelByType(kChannelLeft));
+ ChannelByType(kChannelRight)
+ ->SumFrom(source_bus.ChannelByType(kChannelRight));
+ ChannelByType(kChannelSurroundLeft)
+ ->SumFrom(source_bus.ChannelByType(kChannelSurroundLeft));
+ ChannelByType(kChannelSurroundRight)
+ ->SumFrom(source_bus.ChannelByType(kChannelSurroundRight));
+ } else {
+ // All other cases, fall back to the discrete sum. This will silence the
+ // excessive channels.
+ DiscreteSumFrom(source_bus);
+ }
+}
+
+void AudioBus::SumFromByDownMixing(const AudioBus& source_bus) {
+ unsigned number_of_source_channels = source_bus.NumberOfChannels();
+ unsigned number_of_destination_channels = NumberOfChannels();
+
+ if (number_of_source_channels == 2 && number_of_destination_channels == 1) {
+ // Down-mixing: 2 -> 1
+ // output = 0.5 * (input.L + input.R)
+ const float* source_l = source_bus.ChannelByType(kChannelLeft)->Data();
+ const float* source_r = source_bus.ChannelByType(kChannelRight)->Data();
+
+ float* destination = ChannelByType(kChannelLeft)->MutableData();
+ float scale = 0.5;
+
+ Vsma(source_l, 1, &scale, destination, 1, length());
+ Vsma(source_r, 1, &scale, destination, 1, length());
+ } else if (number_of_source_channels == 4 &&
+ number_of_destination_channels == 1) {
+ // Down-mixing: 4 -> 1
+ // output = 0.25 * (input.L + input.R + input.SL + input.SR)
+ const float* source_l = source_bus.ChannelByType(kChannelLeft)->Data();
+ const float* source_r = source_bus.ChannelByType(kChannelRight)->Data();
+ const float* source_sl =
+ source_bus.ChannelByType(kChannelSurroundLeft)->Data();
+ const float* source_sr =
+ source_bus.ChannelByType(kChannelSurroundRight)->Data();
+
+ float* destination = ChannelByType(kChannelLeft)->MutableData();
+ float scale = 0.25;
+
+ Vsma(source_l, 1, &scale, destination, 1, length());
+ Vsma(source_r, 1, &scale, destination, 1, length());
+ Vsma(source_sl, 1, &scale, destination, 1, length());
+ Vsma(source_sr, 1, &scale, destination, 1, length());
+ } else if (number_of_source_channels == 6 &&
+ number_of_destination_channels == 1) {
+ // Down-mixing: 5.1 -> 1
+ // output = sqrt(1/2) * (input.L + input.R) + input.C
+ // + 0.5 * (input.SL + input.SR)
+ const float* source_l = source_bus.ChannelByType(kChannelLeft)->Data();
+ const float* source_r = source_bus.ChannelByType(kChannelRight)->Data();
+ const float* source_c = source_bus.ChannelByType(kChannelCenter)->Data();
+ const float* source_sl =
+ source_bus.ChannelByType(kChannelSurroundLeft)->Data();
+ const float* source_sr =
+ source_bus.ChannelByType(kChannelSurroundRight)->Data();
+
+ float* destination = ChannelByType(kChannelLeft)->MutableData();
+ float scale_sqrt_half = sqrtf(0.5);
+ float scale_half = 0.5;
+
+ Vsma(source_l, 1, &scale_sqrt_half, destination, 1, length());
+ Vsma(source_r, 1, &scale_sqrt_half, destination, 1, length());
+ Vadd(source_c, 1, destination, 1, destination, 1, length());
+ Vsma(source_sl, 1, &scale_half, destination, 1, length());
+ Vsma(source_sr, 1, &scale_half, destination, 1, length());
+ } else if (number_of_source_channels == 4 &&
+ number_of_destination_channels == 2) {
+ // Down-mixing: 4 -> 2
+ // output.L = 0.5 * (input.L + input.SL)
+ // output.R = 0.5 * (input.R + input.SR)
+ const float* source_l = source_bus.ChannelByType(kChannelLeft)->Data();
+ const float* source_r = source_bus.ChannelByType(kChannelRight)->Data();
+ const float* source_sl =
+ source_bus.ChannelByType(kChannelSurroundLeft)->Data();
+ const float* source_sr =
+ source_bus.ChannelByType(kChannelSurroundRight)->Data();
+
+ float* destination_l = ChannelByType(kChannelLeft)->MutableData();
+ float* destination_r = ChannelByType(kChannelRight)->MutableData();
+ float scale_half = 0.5;
+
+ Vsma(source_l, 1, &scale_half, destination_l, 1, length());
+ Vsma(source_sl, 1, &scale_half, destination_l, 1, length());
+ Vsma(source_r, 1, &scale_half, destination_r, 1, length());
+ Vsma(source_sr, 1, &scale_half, destination_r, 1, length());
+ } else if (number_of_source_channels == 6 &&
+ number_of_destination_channels == 2) {
+ // Down-mixing: 5.1 -> 2
+ // output.L = input.L + sqrt(1/2) * (input.C + input.SL)
+ // output.R = input.R + sqrt(1/2) * (input.C + input.SR)
+ const float* source_l = source_bus.ChannelByType(kChannelLeft)->Data();
+ const float* source_r = source_bus.ChannelByType(kChannelRight)->Data();
+ const float* source_c = source_bus.ChannelByType(kChannelCenter)->Data();
+ const float* source_sl =
+ source_bus.ChannelByType(kChannelSurroundLeft)->Data();
+ const float* source_sr =
+ source_bus.ChannelByType(kChannelSurroundRight)->Data();
+
+ float* destination_l = ChannelByType(kChannelLeft)->MutableData();
+ float* destination_r = ChannelByType(kChannelRight)->MutableData();
+ float scale_sqrt_half = sqrtf(0.5);
+
+ Vadd(source_l, 1, destination_l, 1, destination_l, 1, length());
+ Vsma(source_c, 1, &scale_sqrt_half, destination_l, 1, length());
+ Vsma(source_sl, 1, &scale_sqrt_half, destination_l, 1, length());
+ Vadd(source_r, 1, destination_r, 1, destination_r, 1, length());
+ Vsma(source_c, 1, &scale_sqrt_half, destination_r, 1, length());
+ Vsma(source_sr, 1, &scale_sqrt_half, destination_r, 1, length());
+ } else if (number_of_source_channels == 6 &&
+ number_of_destination_channels == 4) {
+ // Down-mixing: 5.1 -> 4
+ // output.L = input.L + sqrt(1/2) * input.C
+ // output.R = input.R + sqrt(1/2) * input.C
+ // output.SL = input.SL
+ // output.SR = input.SR
+ const float* source_l = source_bus.ChannelByType(kChannelLeft)->Data();
+ const float* source_r = source_bus.ChannelByType(kChannelRight)->Data();
+ const float* source_c = source_bus.ChannelByType(kChannelCenter)->Data();
+
+ float* destination_l = ChannelByType(kChannelLeft)->MutableData();
+ float* destination_r = ChannelByType(kChannelRight)->MutableData();
+ float scale_sqrt_half = sqrtf(0.5);
+
+ Vadd(source_l, 1, destination_l, 1, destination_l, 1, length());
+ Vsma(source_c, 1, &scale_sqrt_half, destination_l, 1, length());
+ Vadd(source_r, 1, destination_r, 1, destination_r, 1, length());
+ Vsma(source_c, 1, &scale_sqrt_half, destination_r, 1, length());
+ Channel(2)->SumFrom(source_bus.Channel(4));
+ Channel(3)->SumFrom(source_bus.Channel(5));
+ } else {
+ // All other cases, fall back to the discrete sum. This will perform
+ // channel-wise sum until the destination channels run out.
+ DiscreteSumFrom(source_bus);
+ }
+}
+
+void AudioBus::CopyWithGainFrom(const AudioBus& source_bus, float gain) {
+ if (!TopologyMatches(source_bus)) {
+ NOTREACHED();
+ Zero();
+ return;
+ }
+
+ if (source_bus.IsSilent()) {
+ Zero();
+ return;
+ }
+
+ unsigned number_of_channels = this->NumberOfChannels();
+ DCHECK_LE(number_of_channels, kMaxBusChannels);
+ if (number_of_channels > kMaxBusChannels)
+ return;
+
+ // If it is copying from the same bus and no need to change gain, just return.
+ if (this == &source_bus && gain == 1)
+ return;
+
+ const float* sources[kMaxBusChannels];
+ float* destinations[kMaxBusChannels];
+
+ for (unsigned i = 0; i < number_of_channels; ++i) {
+ sources[i] = source_bus.Channel(i)->Data();
+ destinations[i] = Channel(i)->MutableData();
+ }
+
+ unsigned frames_to_process = length();
+
+ // Handle gains of 0 and 1 (exactly) specially.
+ if (gain == 1) {
+ for (unsigned channel_index = 0; channel_index < number_of_channels;
+ ++channel_index) {
+ memcpy(destinations[channel_index], sources[channel_index],
+ frames_to_process * sizeof(*destinations[channel_index]));
+ }
+ } else if (gain == 0) {
+ for (unsigned channel_index = 0; channel_index < number_of_channels;
+ ++channel_index) {
+ memset(destinations[channel_index], 0,
+ frames_to_process * sizeof(*destinations[channel_index]));
+ }
+ } else {
+ for (unsigned channel_index = 0; channel_index < number_of_channels;
+ ++channel_index) {
+ Vsmul(sources[channel_index], 1, &gain, destinations[channel_index], 1,
+ frames_to_process);
+ }
+ }
+}
+
+void AudioBus::CopyWithSampleAccurateGainValuesFrom(
+ const AudioBus& source_bus,
+ float* gain_values,
+ unsigned number_of_gain_values) {
+ // Make sure we're processing from the same type of bus.
+ // We *are* able to process from mono -> stereo
+ if (source_bus.NumberOfChannels() != 1 && !TopologyMatches(source_bus)) {
+ NOTREACHED();
+ return;
+ }
+
+ if (!gain_values || number_of_gain_values > source_bus.length()) {
+ NOTREACHED();
+ return;
+ }
+
+ if (source_bus.length() == number_of_gain_values &&
+ source_bus.length() == length() && source_bus.IsSilent()) {
+ Zero();
+ return;
+ }
+
+ // We handle both the 1 -> N and N -> N case here.
+ const float* source = source_bus.Channel(0)->Data();
+ for (unsigned channel_index = 0; channel_index < NumberOfChannels();
+ ++channel_index) {
+ if (source_bus.NumberOfChannels() == NumberOfChannels())
+ source = source_bus.Channel(channel_index)->Data();
+ float* destination = Channel(channel_index)->MutableData();
+ Vmul(source, 1, gain_values, 1, destination, 1, number_of_gain_values);
+ }
+}
+
+scoped_refptr<AudioBus> AudioBus::CreateBySampleRateConverting(
+ const AudioBus* source_bus,
+ bool mix_to_mono,
+ double new_sample_rate) {
+ // sourceBus's sample-rate must be known.
+ DCHECK(source_bus);
+ DCHECK(source_bus->SampleRate());
+ if (!source_bus || !source_bus->SampleRate())
+ return nullptr;
+
+ double source_sample_rate = source_bus->SampleRate();
+ double destination_sample_rate = new_sample_rate;
+ double sample_rate_ratio = source_sample_rate / destination_sample_rate;
+ unsigned number_of_source_channels = source_bus->NumberOfChannels();
+
+ if (number_of_source_channels == 1)
+ mix_to_mono = false; // already mono
+
+ if (source_sample_rate == destination_sample_rate) {
+ // No sample-rate conversion is necessary.
+ if (mix_to_mono)
+ return AudioBus::CreateByMixingToMono(source_bus);
+
+ // Return exact copy.
+ return AudioBus::CreateBufferFromRange(source_bus, 0, source_bus->length());
+ }
+
+ if (source_bus->IsSilent()) {
+ scoped_refptr<AudioBus> silent_bus = Create(
+ number_of_source_channels, source_bus->length() / sample_rate_ratio);
+ silent_bus->SetSampleRate(new_sample_rate);
+ return silent_bus;
+ }
+
+ // First, mix to mono (if necessary) then sample-rate convert.
+ const AudioBus* resampler_source_bus;
+ scoped_refptr<AudioBus> mixed_mono_bus;
+ if (mix_to_mono) {
+ mixed_mono_bus = AudioBus::CreateByMixingToMono(source_bus);
+ resampler_source_bus = mixed_mono_bus.get();
+ } else {
+ // Directly resample without down-mixing.
+ resampler_source_bus = source_bus;
+ }
+
+ // Calculate destination length based on the sample-rates.
+ int source_length = resampler_source_bus->length();
+ int destination_length = source_length / sample_rate_ratio;
+
+ // Create destination bus with same number of channels.
+ unsigned number_of_destination_channels =
+ resampler_source_bus->NumberOfChannels();
+ scoped_refptr<AudioBus> destination_bus =
+ Create(number_of_destination_channels, destination_length);
+
+ // Sample-rate convert each channel.
+ for (unsigned i = 0; i < number_of_destination_channels; ++i) {
+ const float* source = resampler_source_bus->Channel(i)->Data();
+ float* destination = destination_bus->Channel(i)->MutableData();
+
+ SincResampler resampler(sample_rate_ratio);
+ resampler.Process(source, destination, source_length);
+ }
+
+ destination_bus->ClearSilentFlag();
+ destination_bus->SetSampleRate(new_sample_rate);
+ return destination_bus;
+}
+
+scoped_refptr<AudioBus> AudioBus::CreateByMixingToMono(
+ const AudioBus* source_bus) {
+ if (source_bus->IsSilent())
+ return Create(1, source_bus->length());
+
+ switch (source_bus->NumberOfChannels()) {
+ case 1:
+ // Simply create an exact copy.
+ return AudioBus::CreateBufferFromRange(source_bus, 0,
+ source_bus->length());
+ case 2: {
+ unsigned n = source_bus->length();
+ scoped_refptr<AudioBus> destination_bus = Create(1, n);
+
+ const float* source_l = source_bus->Channel(0)->Data();
+ const float* source_r = source_bus->Channel(1)->Data();
+ float* destination = destination_bus->Channel(0)->MutableData();
+
+ // Do the mono mixdown.
+ for (unsigned i = 0; i < n; ++i)
+ destination[i] = (source_l[i] + source_r[i]) / 2;
+
+ destination_bus->ClearSilentFlag();
+ destination_bus->SetSampleRate(source_bus->SampleRate());
+ return destination_bus;
+ }
+ }
+
+ NOTREACHED();
+ return nullptr;
+}
+
+bool AudioBus::IsSilent() const {
+ for (size_t i = 0; i < channels_.size(); ++i) {
+ if (!channels_[i]->IsSilent())
+ return false;
+ }
+ return true;
+}
+
+void AudioBus::ClearSilentFlag() {
+ for (size_t i = 0; i < channels_.size(); ++i)
+ channels_[i]->ClearSilentFlag();
+}
+
+scoped_refptr<AudioBus> DecodeAudioFileData(const char* data, size_t size) {
+ WebAudioBus web_audio_bus;
+ if (Platform::Current()->DecodeAudioFileData(&web_audio_bus, data, size))
+ return web_audio_bus.Release();
+ return nullptr;
+}
+
+scoped_refptr<AudioBus> AudioBus::GetDataResource(const char* name,
+ float sample_rate) {
+ const WebData& resource = Platform::Current()->GetDataResource(name);
+ if (resource.IsEmpty())
+ return nullptr;
+
+ // Currently, the only client of this method is caching the result -- so
+ // it's reasonable to (potentially) pay a one-time flat access cost.
+ // If this becomes problematic, we'll have the refactor DecodeAudioFileData
+ // to take WebData and use segmented access.
+ SharedBuffer::DeprecatedFlatData flat_data(
+ resource.operator scoped_refptr<SharedBuffer>());
+ scoped_refptr<AudioBus> audio_bus =
+ DecodeAudioFileData(flat_data.Data(), flat_data.size());
+
+ if (!audio_bus.get())
+ return nullptr;
+
+ // If the bus is already at the requested sample-rate then return as is.
+ if (audio_bus->SampleRate() == sample_rate)
+ return audio_bus;
+
+ return AudioBus::CreateBySampleRateConverting(audio_bus.get(), false,
+ sample_rate);
+}
+
+scoped_refptr<AudioBus> CreateBusFromInMemoryAudioFile(const void* data,
+ size_t data_size,
+ bool mix_to_mono,
+ float sample_rate) {
+ scoped_refptr<AudioBus> audio_bus =
+ DecodeAudioFileData(static_cast<const char*>(data), data_size);
+ if (!audio_bus.get())
+ return nullptr;
+
+ // If the bus needs no conversion then return as is.
+ if ((!mix_to_mono || audio_bus->NumberOfChannels() == 1) &&
+ audio_bus->SampleRate() == sample_rate)
+ return audio_bus;
+
+ return AudioBus::CreateBySampleRateConverting(audio_bus.get(), mix_to_mono,
+ sample_rate);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/audio_bus.h b/chromium/third_party/blink/renderer/platform/audio/audio_bus.h
new file mode 100644
index 00000000000..0d29198b79d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/audio_bus.h
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_BUS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_BUS_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/audio/audio_channel.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+// An AudioBus represents a collection of one or more AudioChannels.
+// The data layout is "planar" as opposed to "interleaved". An AudioBus with
+// one channel is mono, an AudioBus with two channels is stereo, etc.
+class PLATFORM_EXPORT AudioBus : public ThreadSafeRefCounted<AudioBus> {
+ WTF_MAKE_NONCOPYABLE(AudioBus);
+
+ public:
+ enum {
+ kChannelLeft = 0,
+ kChannelRight = 1,
+ kChannelCenter = 2, // center and mono are the same
+ kChannelMono = 2,
+ kChannelLFE = 3,
+ kChannelSurroundLeft = 4,
+ kChannelSurroundRight = 5,
+ };
+
+ enum {
+ kLayoutCanonical = 0
+ // Can define non-standard layouts here
+ };
+
+ enum ChannelInterpretation {
+ kSpeakers,
+ kDiscrete,
+ };
+
+ // allocate indicates whether or not to initially have the AudioChannels
+ // created with managed storage. Normal usage is to pass true here, in which
+ // case the AudioChannels will memory-manage their own storage. If allocate
+ // is false then setChannelMemory() has to be called later on for each
+ // channel before the AudioBus is useable...
+ static scoped_refptr<AudioBus> Create(unsigned number_of_channels,
+ size_t length,
+ bool allocate = true);
+
+ // Tells the given channel to use an externally allocated buffer.
+ void SetChannelMemory(unsigned channel_index, float* storage, size_t length);
+
+ // Channels
+ unsigned NumberOfChannels() const { return channels_.size(); }
+
+ AudioChannel* Channel(unsigned channel) { return channels_[channel].get(); }
+ const AudioChannel* Channel(unsigned channel) const {
+ return channels_[channel].get();
+ }
+ AudioChannel* ChannelByType(unsigned type);
+ const AudioChannel* ChannelByType(unsigned type) const;
+
+ // Number of sample-frames
+ size_t length() const { return length_; }
+
+ // resizeSmaller() can only be called with a new length <= the current length.
+ // The data stored in the bus will remain undisturbed.
+ void ResizeSmaller(size_t new_length);
+
+ // Sample-rate : 0.0 if unknown or "don't care"
+ float SampleRate() const { return sample_rate_; }
+ void SetSampleRate(float sample_rate) { sample_rate_ = sample_rate; }
+
+ // Zeroes all channels.
+ void Zero();
+
+ // Clears the silent flag on all channels.
+ void ClearSilentFlag();
+
+ // Returns true if the silent bit is set on all channels.
+ bool IsSilent() const;
+
+ // Returns true if the channel count and frame-size match.
+ bool TopologyMatches(const AudioBus& source_bus) const;
+
+ // Creates a new buffer from a range in the source buffer.
+ // 0 may be returned if the range does not fit in the sourceBuffer
+ static scoped_refptr<AudioBus> CreateBufferFromRange(
+ const AudioBus* source_buffer,
+ unsigned start_frame,
+ unsigned end_frame);
+
+ // Creates a new AudioBus by sample-rate converting sourceBus to the
+ // newSampleRate.
+ // setSampleRate() must have been previously called on sourceBus.
+ // Note: sample-rate conversion is already handled in the file-reading code
+ // for the mac port, so we don't need this.
+ static scoped_refptr<AudioBus> CreateBySampleRateConverting(
+ const AudioBus* source_bus,
+ bool mix_to_mono,
+ double new_sample_rate);
+
+ // Creates a new AudioBus by mixing all the channels down to mono.
+ // If sourceBus is already mono, then the returned AudioBus will simply be a
+ // copy.
+ static scoped_refptr<AudioBus> CreateByMixingToMono(
+ const AudioBus* source_bus);
+
+ // Scales all samples by the same amount.
+ void Scale(float scale);
+
+ // Copies the samples from the source bus to this one.
+ // This is just a simple per-channel copy if the number of channels match,
+ // otherwise an up-mix or down-mix is done.
+ void CopyFrom(const AudioBus& source_bus, ChannelInterpretation = kSpeakers);
+
+ // Sums the samples from the source bus to this one.
+ // This is just a simple per-channel summing if the number of channels match,
+ // otherwise an up-mix or down-mix is done.
+ void SumFrom(const AudioBus& source_bus, ChannelInterpretation = kSpeakers);
+
+ // Copy each channel from |source_bus| into our corresponding channel. We
+ // scale |source_bus| by |gain| before copying into the bus.
+ void CopyWithGainFrom(const AudioBus& source_bus, float gain);
+
+ // Copies the sourceBus by scaling with sample-accurate gain values.
+ void CopyWithSampleAccurateGainValuesFrom(const AudioBus& source_bus,
+ float* gain_values,
+ unsigned number_of_gain_values);
+
+ // Returns maximum absolute value across all channels (useful for
+ // normalization).
+ float MaxAbsValue() const;
+
+ // Makes maximum absolute value == 1.0 (if possible).
+ void Normalize();
+
+ static scoped_refptr<AudioBus> GetDataResource(const char* name,
+ float sample_rate);
+
+ protected:
+ AudioBus() = default;
+
+ AudioBus(unsigned number_of_channels, size_t length, bool allocate);
+
+ void DiscreteSumFrom(const AudioBus&);
+
+ // Up/down-mix by in-place summing upon the existing channel content.
+ // http://webaudio.github.io/web-audio-api/#channel-up-mixing-and-down-mixing
+ void SumFromByUpMixing(const AudioBus&);
+ void SumFromByDownMixing(const AudioBus&);
+
+ size_t length_;
+ Vector<std::unique_ptr<AudioChannel>> channels_;
+ int layout_;
+ float sample_rate_; // 0.0 if unknown or N/A
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_BUS_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/audio_channel.cc b/chromium/third_party/blink/renderer/platform/audio/audio_channel.cc
new file mode 100644
index 00000000000..afc7343c523
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/audio_channel.cc
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 <algorithm>
+#include "third_party/blink/renderer/platform/audio/audio_channel.h"
+#include "third_party/blink/renderer/platform/audio/vector_math.h"
+
+namespace blink {
+
+using namespace VectorMath;
+
+void AudioChannel::ResizeSmaller(size_t new_length) {
+ DCHECK_LE(new_length, length_);
+ if (new_length <= length_)
+ length_ = new_length;
+}
+
+void AudioChannel::Scale(float scale) {
+ if (IsSilent())
+ return;
+
+ Vsmul(Data(), 1, &scale, MutableData(), 1, length());
+}
+
+void AudioChannel::CopyFrom(const AudioChannel* source_channel) {
+ bool is_safe = (source_channel && source_channel->length() >= length());
+ DCHECK(is_safe);
+ if (!is_safe)
+ return;
+
+ if (source_channel->IsSilent()) {
+ Zero();
+ return;
+ }
+ memcpy(MutableData(), source_channel->Data(), sizeof(float) * length());
+}
+
+void AudioChannel::CopyFromRange(const AudioChannel* source_channel,
+ unsigned start_frame,
+ unsigned end_frame) {
+ // Check that range is safe for reading from sourceChannel.
+ bool is_range_safe = source_channel && start_frame < end_frame &&
+ end_frame <= source_channel->length();
+ DCHECK(is_range_safe);
+ if (!is_range_safe)
+ return;
+
+ if (source_channel->IsSilent() && IsSilent())
+ return;
+
+ // Check that this channel has enough space.
+ size_t range_length = end_frame - start_frame;
+ bool is_range_length_safe = range_length <= length();
+ DCHECK(is_range_length_safe);
+ if (!is_range_length_safe)
+ return;
+
+ const float* source = source_channel->Data();
+ float* destination = MutableData();
+
+ if (source_channel->IsSilent()) {
+ if (range_length == length())
+ Zero();
+ else
+ memset(destination, 0, sizeof(float) * range_length);
+ } else
+ memcpy(destination, source + start_frame, sizeof(float) * range_length);
+}
+
+void AudioChannel::SumFrom(const AudioChannel* source_channel) {
+ bool is_safe = source_channel && source_channel->length() >= length();
+ DCHECK(is_safe);
+ if (!is_safe)
+ return;
+
+ if (source_channel->IsSilent())
+ return;
+
+ if (IsSilent())
+ CopyFrom(source_channel);
+ else
+ Vadd(Data(), 1, source_channel->Data(), 1, MutableData(), 1, length());
+}
+
+float AudioChannel::MaxAbsValue() const {
+ if (IsSilent())
+ return 0;
+
+ float max = 0;
+
+ Vmaxmgv(Data(), 1, &max, length());
+
+ return max;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/audio_channel.h b/chromium/third_party/blink/renderer/platform/audio/audio_channel.h
new file mode 100644
index 00000000000..38e1a9a53ff
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/audio_channel.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_CHANNEL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_CHANNEL_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/audio/audio_array.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+// An AudioChannel represents a buffer of non-interleaved floating-point audio
+// samples.
+// The PCM samples are normally assumed to be in a nominal range -1.0 -> +1.0
+class PLATFORM_EXPORT AudioChannel {
+ USING_FAST_MALLOC(AudioChannel);
+ WTF_MAKE_NONCOPYABLE(AudioChannel);
+
+ public:
+ // Memory can be externally referenced, or can be internally allocated with an
+ // AudioFloatArray.
+
+ // Reference an external buffer.
+ AudioChannel(float* storage, size_t length)
+ : length_(length), raw_pointer_(storage), silent_(false) {}
+
+ // Manage storage for us.
+ explicit AudioChannel(size_t length)
+ : length_(length), raw_pointer_(nullptr), silent_(true) {
+ mem_buffer_ = std::make_unique<AudioFloatArray>(length);
+ }
+
+ // A "blank" audio channel -- must call set() before it's useful...
+ AudioChannel() : length_(0), raw_pointer_(nullptr), silent_(true) {}
+
+ // Redefine the memory for this channel.
+ // storage represents external memory not managed by this object.
+ void Set(float* storage, size_t length) {
+ mem_buffer_.reset(); // cleanup managed storage
+ raw_pointer_ = storage;
+ length_ = length;
+ silent_ = false;
+ }
+
+ // How many sample-frames do we contain?
+ size_t length() const { return length_; }
+
+ // resizeSmaller() can only be called with a new length <= the current length.
+ // The data stored in the bus will remain undisturbed.
+ void ResizeSmaller(size_t new_length);
+
+ // Direct access to PCM sample data. Non-const accessor clears silent flag.
+ float* MutableData() {
+ ClearSilentFlag();
+ return raw_pointer_ ? raw_pointer_ : mem_buffer_->Data();
+ }
+
+ const float* Data() const {
+ return raw_pointer_ ? raw_pointer_ : mem_buffer_->Data();
+ }
+
+ // Zeroes out all sample values in buffer.
+ void Zero() {
+ if (silent_)
+ return;
+
+ silent_ = true;
+
+ if (mem_buffer_.get())
+ mem_buffer_->Zero();
+ else
+ memset(raw_pointer_, 0, sizeof(float) * length_);
+ }
+
+ // Clears the silent flag.
+ void ClearSilentFlag() { silent_ = false; }
+
+ bool IsSilent() const { return silent_; }
+
+ // Scales all samples by the same amount.
+ void Scale(float scale);
+
+ // A simple memcpy() from the source channel
+ void CopyFrom(const AudioChannel* source_channel);
+
+ // Copies the given range from the source channel.
+ void CopyFromRange(const AudioChannel* source_channel,
+ unsigned start_frame,
+ unsigned end_frame);
+
+ // Sums (with unity gain) from the source channel.
+ void SumFrom(const AudioChannel* source_channel);
+
+ // Returns maximum absolute value (useful for normalization).
+ float MaxAbsValue() const;
+
+ private:
+ size_t length_;
+
+ float* raw_pointer_;
+ std::unique_ptr<AudioFloatArray> mem_buffer_;
+ bool silent_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_CHANNEL_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/audio_delay_dsp_kernel.cc b/chromium/third_party/blink/renderer/platform/audio/audio_delay_dsp_kernel.cc
new file mode 100644
index 00000000000..97130f3e33f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/audio_delay_dsp_kernel.cc
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2010, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/audio/audio_delay_dsp_kernel.h"
+
+#include <cmath>
+#include "third_party/blink/renderer/platform/audio/audio_utilities.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+namespace blink {
+
+AudioDelayDSPKernel::AudioDelayDSPKernel(AudioDSPKernelProcessor* processor,
+ size_t processing_size_in_frames)
+ : AudioDSPKernel(processor),
+ write_index_(0),
+ delay_times_(processing_size_in_frames) {}
+
+AudioDelayDSPKernel::AudioDelayDSPKernel(double max_delay_time,
+ float sample_rate)
+ : AudioDSPKernel(sample_rate),
+ max_delay_time_(max_delay_time),
+ write_index_(0) {
+ DCHECK_GT(max_delay_time, 0.0);
+ DCHECK(!std::isnan(max_delay_time));
+ if (max_delay_time <= 0.0 || std::isnan(max_delay_time))
+ return;
+
+ size_t buffer_length = BufferLengthForDelay(max_delay_time, sample_rate);
+ DCHECK(buffer_length);
+ if (!buffer_length)
+ return;
+
+ buffer_.Allocate(buffer_length);
+ buffer_.Zero();
+}
+
+size_t AudioDelayDSPKernel::BufferLengthForDelay(double max_delay_time,
+ double sample_rate) const {
+ // Compute the length of the buffer needed to handle a max delay of
+ // |maxDelayTime|. One is added to handle the case where the actual delay
+ // equals the maximum delay.
+ return 1 + AudioUtilities::TimeToSampleFrame(max_delay_time, sample_rate);
+}
+
+bool AudioDelayDSPKernel::HasSampleAccurateValues() {
+ return false;
+}
+
+void AudioDelayDSPKernel::CalculateSampleAccurateValues(float*, size_t) {
+ NOTREACHED();
+}
+
+double AudioDelayDSPKernel::DelayTime(float sample_rate) {
+ return desired_delay_frames_ / sample_rate;
+}
+
+void AudioDelayDSPKernel::Process(const float* source,
+ float* destination,
+ size_t frames_to_process) {
+ size_t buffer_length = buffer_.size();
+ float* buffer = buffer_.Data();
+
+ DCHECK(buffer_length);
+ if (!buffer_length)
+ return;
+
+ DCHECK(source);
+ DCHECK(destination);
+ if (!source || !destination)
+ return;
+
+ float sample_rate = this->SampleRate();
+ double delay_time = 0;
+ float* delay_times = delay_times_.Data();
+ double max_time = MaxDelayTime();
+
+ bool sample_accurate = HasSampleAccurateValues();
+
+ if (sample_accurate) {
+ CalculateSampleAccurateValues(delay_times, frames_to_process);
+ } else {
+ delay_time = this->DelayTime(sample_rate);
+
+ // Make sure the delay time is in a valid range.
+ delay_time = clampTo(delay_time, 0.0, max_time);
+ }
+
+ for (unsigned i = 0; i < frames_to_process; ++i) {
+ if (sample_accurate) {
+ delay_time = delay_times[i];
+ if (std::isnan(delay_time))
+ delay_time = max_time;
+ else
+ delay_time = clampTo(delay_time, 0.0, max_time);
+ }
+
+ double desired_delay_frames = delay_time * sample_rate;
+
+ double read_position = write_index_ + buffer_length - desired_delay_frames;
+ if (read_position >= buffer_length)
+ read_position -= buffer_length;
+
+ // Linearly interpolate in-between delay times.
+ int read_index1 = static_cast<int>(read_position);
+ int read_index2 = (read_index1 + 1) % buffer_length;
+ double interpolation_factor = read_position - read_index1;
+
+ double input = static_cast<float>(*source++);
+ buffer[write_index_] = static_cast<float>(input);
+ write_index_ = (write_index_ + 1) % buffer_length;
+
+ double sample1 = buffer[read_index1];
+ double sample2 = buffer[read_index2];
+
+ double output =
+ (1.0 - interpolation_factor) * sample1 + interpolation_factor * sample2;
+
+ *destination++ = static_cast<float>(output);
+ }
+}
+
+void AudioDelayDSPKernel::Reset() {
+ buffer_.Zero();
+}
+
+bool AudioDelayDSPKernel::RequiresTailProcessing() const {
+ // Always return true even if the tail time and latency might both
+ // be zero. This is for simplicity; most interesting delay nodes
+ // have non-zero delay times anyway. And it's ok to return true. It
+ // just means the node lives a little longer than strictly
+ // necessary.
+ return true;
+}
+
+double AudioDelayDSPKernel::TailTime() const {
+ // Account for worst case delay.
+ // Don't try to track actual delay time which can change dynamically.
+ return max_delay_time_;
+}
+
+double AudioDelayDSPKernel::LatencyTime() const {
+ return 0;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/audio_delay_dsp_kernel.h b/chromium/third_party/blink/renderer/platform/audio/audio_delay_dsp_kernel.h
new file mode 100644
index 00000000000..3cfed07d62b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/audio_delay_dsp_kernel.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2010, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_DELAY_DSP_KERNEL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_DELAY_DSP_KERNEL_H_
+
+#include "third_party/blink/renderer/platform/audio/audio_array.h"
+#include "third_party/blink/renderer/platform/audio/audio_dsp_kernel.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT AudioDelayDSPKernel : public AudioDSPKernel {
+ public:
+ AudioDelayDSPKernel(double max_delay_time, float sample_rate);
+
+ void Process(const float* source,
+ float* destination,
+ size_t frames_to_process) override;
+ void Reset() override;
+
+ double MaxDelayTime() const { return max_delay_time_; }
+
+ void SetDelayFrames(double number_of_frames) {
+ desired_delay_frames_ = number_of_frames;
+ }
+
+ double TailTime() const override;
+ double LatencyTime() const override;
+ bool RequiresTailProcessing() const override;
+
+ protected:
+ AudioDelayDSPKernel(AudioDSPKernelProcessor*,
+ size_t processing_size_in_frames);
+
+ virtual bool HasSampleAccurateValues();
+ virtual void CalculateSampleAccurateValues(float* delay_times,
+ size_t frames_to_process);
+ virtual double DelayTime(float sample_rate);
+
+ AudioFloatArray buffer_;
+ double max_delay_time_;
+ int write_index_;
+ double desired_delay_frames_;
+
+ AudioFloatArray delay_times_;
+
+ size_t BufferLengthForDelay(double delay_time, double sample_rate) const;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_DELAY_DSP_KERNEL_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/audio_destination.cc b/chromium/third_party/blink/renderer/platform/audio/audio_destination.cc
new file mode 100644
index 00000000000..c72671f8713
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/audio_destination.cc
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/audio/audio_destination.h"
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_audio_latency_hint.h"
+#include "third_party/blink/public/platform/web_security_origin.h"
+#include "third_party/blink/renderer/platform/audio/audio_utilities.h"
+#include "third_party/blink/renderer/platform/audio/push_pull_fifo.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/histogram.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/web_task_runner.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+
+namespace blink {
+
+// FIFO Size.
+//
+// TODO(hongchan): This was estimated based on the largest callback buffer size
+// that we would ever need. The current UMA stats indicates that this is, in
+// fact, probably too small. There are Android devices out there with a size of
+// 8000 or so. We might need to make this larger. See: crbug.com/670747
+const size_t kFIFOSize = 96 * 128;
+
+scoped_refptr<AudioDestination> AudioDestination::Create(
+ AudioIOCallback& callback,
+ unsigned number_of_output_channels,
+ const WebAudioLatencyHint& latency_hint,
+ scoped_refptr<const SecurityOrigin> security_origin) {
+ return base::AdoptRef(
+ new AudioDestination(callback, number_of_output_channels, latency_hint,
+ std::move(security_origin)));
+}
+
+AudioDestination::AudioDestination(
+ AudioIOCallback& callback,
+ unsigned number_of_output_channels,
+ const WebAudioLatencyHint& latency_hint,
+ scoped_refptr<const SecurityOrigin> security_origin)
+ : number_of_output_channels_(number_of_output_channels),
+ is_playing_(false),
+ fifo_(
+ std::make_unique<PushPullFIFO>(number_of_output_channels, kFIFOSize)),
+ output_bus_(AudioBus::Create(number_of_output_channels,
+ AudioUtilities::kRenderQuantumFrames,
+ false)),
+ render_bus_(AudioBus::Create(number_of_output_channels,
+ AudioUtilities::kRenderQuantumFrames)),
+ callback_(callback),
+ frames_elapsed_(0) {
+ // Create WebAudioDevice. blink::WebAudioDevice is designed to support the
+ // local input (e.g. loopback from OS audio system), but Chromium's media
+ // renderer does not support it currently. Thus, we use zero for the number
+ // of input channels.
+ web_audio_device_ = Platform::Current()->CreateAudioDevice(
+ 0, number_of_output_channels, latency_hint, this, String(),
+ std::move(security_origin));
+ DCHECK(web_audio_device_);
+
+ callback_buffer_size_ = web_audio_device_->FramesPerBuffer();
+
+ // Primes the FIFO for the given callback buffer size. This is to prevent
+ // first FIFO pulls from causing "underflow" errors.
+ const unsigned priming_render_quanta =
+ ceil(callback_buffer_size_ /
+ static_cast<float>(AudioUtilities::kRenderQuantumFrames));
+ for (unsigned i = 0; i < priming_render_quanta; ++i) {
+ fifo_->Push(render_bus_.get());
+ }
+
+ if (!CheckBufferSize()) {
+ NOTREACHED();
+ }
+}
+
+AudioDestination::~AudioDestination() {
+ Stop();
+}
+
+void AudioDestination::Render(const WebVector<float*>& destination_data,
+ size_t number_of_frames,
+ double delay,
+ double delay_timestamp,
+ size_t prior_frames_skipped) {
+ TRACE_EVENT_BEGIN2("webaudio", "AudioDestination::Render",
+ "callback_buffer_size", number_of_frames, "frames skipped",
+ prior_frames_skipped);
+ CHECK_EQ(destination_data.size(), number_of_output_channels_);
+ CHECK_EQ(number_of_frames, callback_buffer_size_);
+
+ // Note that this method is called by AudioDeviceThread. If FIFO is not ready,
+ // or the requested render size is greater than FIFO size return here.
+ // (crbug.com/692423)
+ if (!fifo_ || fifo_->length() < number_of_frames) {
+ TRACE_EVENT_INSTANT1(
+ "webaudio",
+ "AudioDestination::Render - FIFO not ready or the size is too small",
+ TRACE_EVENT_SCOPE_THREAD, "fifo length", fifo_ ? fifo_->length() : 0);
+ TRACE_EVENT_END2("webaudio", "AudioDestination::Render", "timestamp (s)",
+ delay_timestamp, "delay (s)", delay);
+ return;
+ }
+
+ // Associate the destination data array with the output bus then fill the
+ // FIFO.
+ for (unsigned i = 0; i < number_of_output_channels_; ++i)
+ output_bus_->SetChannelMemory(i, destination_data[i], number_of_frames);
+
+ size_t frames_to_render = fifo_->Pull(output_bus_.get(), number_of_frames);
+
+ // Use the dual-thread rendering model if the AudioWorklet is activated.
+ if (worklet_task_runner_) {
+ PostCrossThreadTask(
+ *worklet_task_runner_,
+ FROM_HERE,
+ CrossThreadBind(&AudioDestination::RequestRender, WrapRefCounted(this),
+ number_of_frames, frames_to_render, delay,
+ delay_timestamp, prior_frames_skipped));
+ } else {
+ // Otherwise use the single-thread rendering with AudioDeviceThread.
+ RequestRender(number_of_frames, frames_to_render, delay,
+ delay_timestamp, prior_frames_skipped);
+ }
+ TRACE_EVENT_END2("webaudio", "AudioDestination::Render", "timestamp (s)",
+ delay_timestamp, "delay (s)", delay);
+}
+
+void AudioDestination::RequestRender(size_t frames_requested,
+ size_t frames_to_render,
+ double delay,
+ double delay_timestamp,
+ size_t prior_frames_skipped) {
+ TRACE_EVENT2("webaudio", "AudioDestination::RequestRender",
+ "frames_to_render", frames_to_render, "timestamp (s)",
+ delay_timestamp);
+
+ frames_elapsed_ -= std::min(frames_elapsed_, prior_frames_skipped);
+ AudioIOPosition output_position;
+ output_position.position =
+ frames_elapsed_ / static_cast<double>(web_audio_device_->SampleRate()) -
+ delay;
+ output_position.timestamp = delay_timestamp;
+ base::TimeTicks received_timestamp = base::TimeTicks::Now();
+
+ for (size_t pushed_frames = 0; pushed_frames < frames_to_render;
+ pushed_frames += AudioUtilities::kRenderQuantumFrames) {
+ // If platform buffer is more than two times longer than |framesToProcess|
+ // we do not want output position to get stuck so we promote it
+ // using the elapsed time from the moment it was initially obtained.
+ if (callback_buffer_size_ > AudioUtilities::kRenderQuantumFrames * 2) {
+ double delta = (base::TimeTicks::Now() - received_timestamp).InSecondsF();
+ output_position.position += delta;
+ output_position.timestamp += delta;
+ }
+
+ // Some implementations give only rough estimation of |delay| so
+ // we might have negative estimation |outputPosition| value.
+ if (output_position.position < 0.0)
+ output_position.position = 0.0;
+
+ // Process WebAudio graph and push the rendered output to FIFO.
+ callback_.Render(nullptr, render_bus_.get(),
+ AudioUtilities::kRenderQuantumFrames, output_position);
+ fifo_->Push(render_bus_.get());
+ }
+
+ frames_elapsed_ += frames_requested;
+}
+
+void AudioDestination::Start() {
+ DCHECK(IsMainThread());
+
+ // Start the "audio device" after the rendering thread is ready.
+ if (web_audio_device_ && !is_playing_) {
+ TRACE_EVENT0("webaudio", "AudioDestination::Start");
+ web_audio_device_->Start();
+ is_playing_ = true;
+ }
+}
+
+void AudioDestination::StartWithWorkletTaskRunner(
+ scoped_refptr<base::SingleThreadTaskRunner> worklet_task_runner) {
+ DCHECK(IsMainThread());
+
+ if (web_audio_device_ && !is_playing_) {
+ TRACE_EVENT0("webaudio", "AudioDestination::Start");
+ worklet_task_runner_ = std::move(worklet_task_runner);
+ web_audio_device_->Start();
+ is_playing_ = true;
+ }
+}
+
+void AudioDestination::Stop() {
+ DCHECK(IsMainThread());
+
+ // This assumes stopping the "audio device" is synchronous and dumping the
+ // rendering thread is safe after that.
+ if (web_audio_device_ && is_playing_) {
+ TRACE_EVENT0("webaudio", "AudioDestination::Stop");
+ web_audio_device_->Stop();
+ worklet_task_runner_ = nullptr;
+ is_playing_ = false;
+ }
+}
+
+size_t AudioDestination::CallbackBufferSize() const {
+ DCHECK(IsMainThread());
+ return callback_buffer_size_;
+}
+
+bool AudioDestination::IsPlaying() {
+ DCHECK(IsMainThread());
+ return is_playing_;
+}
+
+int AudioDestination::FramesPerBuffer() const {
+ DCHECK(IsMainThread());
+ return web_audio_device_->FramesPerBuffer();
+}
+
+size_t AudioDestination::HardwareBufferSize() {
+ return Platform::Current()->AudioHardwareBufferSize();
+}
+
+float AudioDestination::HardwareSampleRate() {
+ return static_cast<float>(Platform::Current()->AudioHardwareSampleRate());
+}
+
+unsigned long AudioDestination::MaxChannelCount() {
+ return static_cast<unsigned long>(
+ Platform::Current()->AudioHardwareOutputChannels());
+}
+
+bool AudioDestination::CheckBufferSize() {
+ // Histogram for audioHardwareBufferSize
+ DEFINE_STATIC_LOCAL(SparseHistogram, hardware_buffer_size_histogram,
+ ("WebAudio.AudioDestination.HardwareBufferSize"));
+
+ // Histogram for the actual callback size used. Typically, this is the same
+ // as audioHardwareBufferSize, but can be adjusted depending on some
+ // heuristics below.
+ DEFINE_STATIC_LOCAL(SparseHistogram, callback_buffer_size_histogram,
+ ("WebAudio.AudioDestination.CallbackBufferSize"));
+
+ // Record the sizes if we successfully created an output device.
+ hardware_buffer_size_histogram.Sample(HardwareBufferSize());
+ callback_buffer_size_histogram.Sample(callback_buffer_size_);
+
+ // Check if the requested buffer size is too large.
+ bool is_buffer_size_valid =
+ callback_buffer_size_ + AudioUtilities::kRenderQuantumFrames <= kFIFOSize;
+ DCHECK_LE(callback_buffer_size_ + AudioUtilities::kRenderQuantumFrames,
+ kFIFOSize);
+ return is_buffer_size_valid;
+}
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/audio_destination.h b/chromium/third_party/blink/renderer/platform/audio/audio_destination.h
new file mode 100644
index 00000000000..b73e61a184d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/audio_destination.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_DESTINATION_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_DESTINATION_H_
+
+#include <memory>
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/public/platform/web_audio_device.h"
+#include "third_party/blink/public/platform/web_thread.h"
+#include "third_party/blink/public/platform/web_vector.h"
+#include "third_party/blink/renderer/platform/audio/audio_bus.h"
+#include "third_party/blink/renderer/platform/audio/audio_io_callback.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
+
+namespace blink {
+
+class PushPullFIFO;
+class SecurityOrigin;
+class WebAudioLatencyHint;
+
+// The AudioDestination class is an audio sink interface between the media
+// renderer and the Blink's WebAudio module. It has a FIFO to adapt the
+// different processing block sizes of WebAudio renderer and actual hardware
+// audio callback.
+//
+// Currently AudioDestination supports two types of threading models:
+// - Single-thread (default): process the entire WebAudio render call chain by
+// AudioDeviceThread.
+// - Dual-thread (experimental): Use WebThread for the WebAudio rendering with
+// AudioWorkletThread.
+class PLATFORM_EXPORT AudioDestination
+ : public ThreadSafeRefCounted<AudioDestination>,
+ public WebAudioDevice::RenderCallback {
+ USING_FAST_MALLOC(AudioDestination);
+ WTF_MAKE_NONCOPYABLE(AudioDestination);
+
+ public:
+ AudioDestination(AudioIOCallback&,
+ unsigned number_of_output_channels,
+ const WebAudioLatencyHint&,
+ scoped_refptr<const SecurityOrigin>);
+ ~AudioDestination() override;
+
+ static scoped_refptr<AudioDestination> Create(
+ AudioIOCallback&,
+ unsigned number_of_output_channels,
+ const WebAudioLatencyHint&,
+ scoped_refptr<const SecurityOrigin>);
+
+ // The actual render function (WebAudioDevice::RenderCallback) isochronously
+ // invoked by the media renderer. This is never called after Stop() is called.
+ void Render(const WebVector<float*>& destination_data,
+ size_t number_of_frames,
+ double delay,
+ double delay_timestamp,
+ size_t prior_frames_skipped) override;
+
+ // The actual render request to the WebAudio destination node. This method
+ // can be invoked on both AudioDeviceThread (single-thread rendering) and
+ // AudioWorkletThread (dual-thread rendering).
+ void RequestRender(size_t frames_requested,
+ size_t frames_to_render,
+ double delay,
+ double delay_timestamp,
+ size_t prior_frames_skipped);
+
+ virtual void Start();
+ virtual void Stop();
+
+ // Starts the destination with the AudioWorklet support.
+ void StartWithWorkletTaskRunner(
+ scoped_refptr<base::SingleThreadTaskRunner> worklet_task_runner);
+
+ // Getters must be accessed from the main thread.
+ size_t CallbackBufferSize() const;
+ bool IsPlaying();
+
+ // TODO(hongchan): this should not be called by the rendering thread.
+ double SampleRate() const { return web_audio_device_->SampleRate(); }
+
+ // Returns the audio buffer size in frames used by the underlying audio
+ // hardware.
+ int FramesPerBuffer() const;
+
+ // The information from the actual audio hardware. (via Platform::current)
+ static float HardwareSampleRate();
+ static unsigned long MaxChannelCount();
+
+ private:
+ // Check if the buffer size chosen by the WebAudioDevice is too large.
+ bool CheckBufferSize();
+
+ size_t HardwareBufferSize();
+
+ // Accessed by the main thread.
+ std::unique_ptr<WebAudioDevice> web_audio_device_;
+ const unsigned number_of_output_channels_;
+ size_t callback_buffer_size_;
+ bool is_playing_;
+
+ // The task runner for AudioWorklet operation. This is only valid when
+ // the AudioWorklet is activated.
+ scoped_refptr<base::SingleThreadTaskRunner> worklet_task_runner_;
+
+ // Can be accessed by both threads: resolves the buffer size mismatch between
+ // the WebAudio engine and the callback function from the actual audio device.
+ std::unique_ptr<PushPullFIFO> fifo_;
+
+ // Accessed by device thread: to pass the data from FIFO to the device.
+ scoped_refptr<AudioBus> output_bus_;
+
+ // Accessed by rendering thread: to push the rendered result from WebAudio
+ // graph into the FIFO.
+ scoped_refptr<AudioBus> render_bus_;
+
+ // Accessed by rendering thread: the render callback function of WebAudio
+ // engine. (i.e. DestinationNode)
+ AudioIOCallback& callback_;
+
+ // Accessed by rendering thread.
+ size_t frames_elapsed_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_DESTINATION_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/audio_destination_consumer.h b/chromium/third_party/blink/renderer/platform/audio/audio_destination_consumer.h
new file mode 100644
index 00000000000..e6b53b4793c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/audio_destination_consumer.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ * 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 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_DESTINATION_CONSUMER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_DESTINATION_CONSUMER_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+
+class AudioBus;
+
+class PLATFORM_EXPORT AudioDestinationConsumer {
+ public:
+ virtual void SetFormat(size_t number_of_channels, float sample_rate) = 0;
+ virtual void ConsumeAudio(AudioBus*, size_t number_of_frames) = 0;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_DESTINATION_CONSUMER_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/audio_dsp_kernel.cc b/chromium/third_party/blink/renderer/platform/audio/audio_dsp_kernel.cc
new file mode 100644
index 00000000000..636fcc8c10d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/audio_dsp_kernel.cc
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/renderer/platform/audio/audio_dsp_kernel.h"
+
+namespace blink {
+
+AudioDSPKernel::~AudioDSPKernel() = default;
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/audio_dsp_kernel.h b/chromium/third_party/blink/renderer/platform/audio/audio_dsp_kernel.h
new file mode 100644
index 00000000000..04127347748
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/audio_dsp_kernel.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2010 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_DSP_KERNEL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_DSP_KERNEL_H_
+
+#include "third_party/blink/renderer/platform/audio/audio_dsp_kernel_processor.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+// AudioDSPKernel does the processing for one channel of an
+// AudioDSPKernelProcessor.
+
+class PLATFORM_EXPORT AudioDSPKernel {
+ USING_FAST_MALLOC(AudioDSPKernel);
+
+ public:
+ AudioDSPKernel(AudioDSPKernelProcessor* kernel_processor)
+ : kernel_processor_(kernel_processor),
+ sample_rate_(kernel_processor->SampleRate()) {}
+
+ AudioDSPKernel(float sample_rate)
+ : kernel_processor_(nullptr), sample_rate_(sample_rate) {}
+
+ virtual ~AudioDSPKernel();
+
+ // Subclasses must override process() to do the processing and reset() to
+ // reset DSP state.
+ virtual void Process(const float* source,
+ float* destination,
+ size_t frames_to_process) = 0;
+ // Subclasses that have AudioParams must override this to process the
+ // AudioParams.
+ virtual void ProcessOnlyAudioParams(size_t frames_to_process){};
+ virtual void Reset() = 0;
+
+ float SampleRate() const { return sample_rate_; }
+ double Nyquist() const { return 0.5 * SampleRate(); }
+
+ AudioDSPKernelProcessor* Processor() { return kernel_processor_; }
+ const AudioDSPKernelProcessor* Processor() const { return kernel_processor_; }
+
+ virtual double TailTime() const = 0;
+ virtual double LatencyTime() const = 0;
+ virtual bool RequiresTailProcessing() const = 0;
+
+ protected:
+ // This raw pointer is safe because the AudioDSPKernelProcessor object is
+ // guaranteed to be kept alive while the AudioDSPKernel object is alive.
+ AudioDSPKernelProcessor* kernel_processor_;
+ float sample_rate_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_DSP_KERNEL_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/audio_dsp_kernel_processor.cc b/chromium/third_party/blink/renderer/platform/audio/audio_dsp_kernel_processor.cc
new file mode 100644
index 00000000000..27a4021ce25
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/audio_dsp_kernel_processor.cc
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2010 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 "third_party/blink/renderer/platform/audio/audio_dsp_kernel.h"
+#include "third_party/blink/renderer/platform/audio/audio_dsp_kernel_processor.h"
+
+namespace blink {
+
+// setNumberOfChannels() may later be called if the object is not yet in an
+// "initialized" state.
+AudioDSPKernelProcessor::AudioDSPKernelProcessor(float sample_rate,
+ unsigned number_of_channels)
+ : AudioProcessor(sample_rate, number_of_channels), has_just_reset_(true) {}
+
+void AudioDSPKernelProcessor::Initialize() {
+ if (IsInitialized())
+ return;
+
+ MutexLocker locker(process_lock_);
+ DCHECK(!kernels_.size());
+
+ // Create processing kernels, one per channel.
+ for (unsigned i = 0; i < NumberOfChannels(); ++i)
+ kernels_.push_back(CreateKernel());
+
+ initialized_ = true;
+ has_just_reset_ = true;
+}
+
+void AudioDSPKernelProcessor::Uninitialize() {
+ if (!IsInitialized())
+ return;
+
+ MutexLocker locker(process_lock_);
+ kernels_.clear();
+
+ initialized_ = false;
+}
+
+void AudioDSPKernelProcessor::Process(const AudioBus* source,
+ AudioBus* destination,
+ size_t frames_to_process) {
+ DCHECK(source);
+ DCHECK(destination);
+ if (!source || !destination)
+ return;
+
+ if (!IsInitialized()) {
+ destination->Zero();
+ return;
+ }
+
+ MutexTryLocker try_locker(process_lock_);
+ if (try_locker.Locked()) {
+ bool channel_count_matches =
+ source->NumberOfChannels() == destination->NumberOfChannels() &&
+ source->NumberOfChannels() == kernels_.size();
+ DCHECK(channel_count_matches);
+ if (!channel_count_matches)
+ return;
+
+ for (unsigned i = 0; i < kernels_.size(); ++i)
+ kernels_[i]->Process(source->Channel(i)->Data(),
+ destination->Channel(i)->MutableData(),
+ frames_to_process);
+ } else {
+ // Unfortunately, the kernel is being processed by another thread.
+ // See also ConvolverNode::process().
+ destination->Zero();
+ }
+}
+
+void AudioDSPKernelProcessor::ProcessOnlyAudioParams(size_t frames_to_process) {
+ if (!IsInitialized())
+ return;
+
+ MutexTryLocker try_locker(process_lock_);
+ // Only update the AudioParams if we can get the lock. If not, some
+ // other thread is updating the kernels, so we'll have to skip it
+ // this time.
+ if (try_locker.Locked()) {
+ for (unsigned i = 0; i < kernels_.size(); ++i)
+ kernels_[i]->ProcessOnlyAudioParams(frames_to_process);
+ }
+}
+
+// Resets filter state
+void AudioDSPKernelProcessor::Reset() {
+ DCHECK(IsMainThread());
+ if (!IsInitialized())
+ return;
+
+ // Forces snap to parameter values - first time.
+ // Any processing depending on this value must set it to false at the
+ // appropriate time.
+ has_just_reset_ = true;
+
+ MutexLocker locker(process_lock_);
+ for (unsigned i = 0; i < kernels_.size(); ++i)
+ kernels_[i]->Reset();
+}
+
+void AudioDSPKernelProcessor::SetNumberOfChannels(unsigned number_of_channels) {
+ if (number_of_channels == number_of_channels_)
+ return;
+
+ DCHECK(!IsInitialized());
+ if (!IsInitialized())
+ number_of_channels_ = number_of_channels;
+}
+
+bool AudioDSPKernelProcessor::RequiresTailProcessing() const {
+ // Always return true even if the tail time and latency might both be zero.
+ return true;
+}
+
+double AudioDSPKernelProcessor::TailTime() const {
+ DCHECK(!IsMainThread());
+ MutexTryLocker try_locker(process_lock_);
+ if (try_locker.Locked()) {
+ // It is expected that all the kernels have the same tailTime.
+ return !kernels_.IsEmpty() ? kernels_.front()->TailTime() : 0;
+ }
+ // Since we don't want to block the Audio Device thread, we return a large
+ // value instead of trying to acquire the lock.
+ return std::numeric_limits<double>::infinity();
+}
+
+double AudioDSPKernelProcessor::LatencyTime() const {
+ DCHECK(!IsMainThread());
+ MutexTryLocker try_locker(process_lock_);
+ if (try_locker.Locked()) {
+ // It is expected that all the kernels have the same latencyTime.
+ return !kernels_.IsEmpty() ? kernels_.front()->LatencyTime() : 0;
+ }
+ // Since we don't want to block the Audio Device thread, we return a large
+ // value instead of trying to acquire the lock.
+ return std::numeric_limits<double>::infinity();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/audio_dsp_kernel_processor.h b/chromium/third_party/blink/renderer/platform/audio/audio_dsp_kernel_processor.h
new file mode 100644
index 00000000000..3f405d04451
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/audio_dsp_kernel_processor.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2010 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_DSP_KERNEL_PROCESSOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_DSP_KERNEL_PROCESSOR_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/audio/audio_bus.h"
+#include "third_party/blink/renderer/platform/audio/audio_processor.h"
+#include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class AudioBus;
+class AudioDSPKernel;
+class AudioProcessor;
+
+// AudioDSPKernelProcessor processes one input -> one output (N channels each)
+// It uses one AudioDSPKernel object per channel to do the processing, thus
+// there is no cross-channel processing. Despite this limitation it turns out
+// to be a very common and useful type of processor.
+
+class PLATFORM_EXPORT AudioDSPKernelProcessor : public AudioProcessor {
+ public:
+ // numberOfChannels may be later changed if object is not yet in an
+ // "initialized" state
+ AudioDSPKernelProcessor(float sample_rate, unsigned number_of_channels);
+
+ // Subclasses create the appropriate type of processing kernel here.
+ // We'll call this to create a kernel for each channel.
+ virtual std::unique_ptr<AudioDSPKernel> CreateKernel() = 0;
+
+ // AudioProcessor methods
+ void Initialize() override;
+ void Uninitialize() override;
+ void Process(const AudioBus* source,
+ AudioBus* destination,
+ size_t frames_to_process) override;
+ void ProcessOnlyAudioParams(size_t frames_to_process) override;
+ void Reset() override;
+ void SetNumberOfChannels(unsigned) override;
+ unsigned NumberOfChannels() const override { return number_of_channels_; }
+
+ double TailTime() const override;
+ double LatencyTime() const override;
+ bool RequiresTailProcessing() const override;
+
+ protected:
+ Vector<std::unique_ptr<AudioDSPKernel>> kernels_;
+ mutable Mutex process_lock_;
+ bool has_just_reset_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_DSP_KERNEL_PROCESSOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/audio_file_reader.h b/chromium/third_party/blink/renderer/platform/audio/audio_file_reader.h
new file mode 100644
index 00000000000..8a499b792b7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/audio_file_reader.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_FILE_READER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_FILE_READER_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+
+class AudioBus;
+
+// For both create functions:
+// Pass in 0.0 for sampleRate to use the file's sample-rate, otherwise a
+// sample-rate conversion to the requested sampleRate will be made (if it
+// doesn't already match the file's sample-rate). The created buffer will have
+// its sample-rate set correctly to the result.
+
+PLATFORM_EXPORT scoped_refptr<AudioBus> CreateBusFromInMemoryAudioFile(
+ const void* data,
+ size_t data_size,
+ bool mix_to_mono,
+ float sample_rate);
+
+PLATFORM_EXPORT scoped_refptr<AudioBus> CreateBusFromAudioFile(
+ const char* file_path,
+ bool mix_to_mono,
+ float sample_rate);
+
+// May pass in 0.0 for sampleRate in which case it will use the AudioBus's
+// sampleRate
+PLATFORM_EXPORT void WriteBusToAudioFile(AudioBus* bus,
+ const char* file_path,
+ double file_sample_rate);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_FILE_READER_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/audio_io_callback.h b/chromium/third_party/blink/renderer/platform/audio/audio_io_callback.h
new file mode 100644
index 00000000000..079bb199e5f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/audio_io_callback.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_IO_CALLBACK_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_IO_CALLBACK_H_
+
+#include "base/time/time.h"
+
+namespace blink {
+
+class AudioBus;
+
+struct AudioIOPosition {
+ // Audio stream position in seconds.
+ double position;
+ // System timestamp in seconds corresponding to the contained |position|
+ // value.
+ double timestamp;
+};
+
+// Abstract base-class for isochronous audio I/O client.
+class AudioIOCallback {
+ public:
+ // render() is called periodically to get the next render quantum of audio
+ // into destinationBus. Optional audio input is given in sourceBus (if it's
+ // not 0).
+ virtual void Render(AudioBus* source_bus,
+ AudioBus* destination_bus,
+ size_t frames_to_process,
+ const AudioIOPosition& output_position) = 0;
+
+ virtual ~AudioIOCallback() = default;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_IO_CALLBACK_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/audio_processor.cc b/chromium/third_party/blink/renderer/platform/audio/audio_processor.cc
new file mode 100644
index 00000000000..7f642650aa2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/audio_processor.cc
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/renderer/platform/audio/audio_processor.h"
+
+namespace blink {
+
+AudioProcessor::~AudioProcessor() = default;
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/audio_processor.h b/chromium/third_party/blink/renderer/platform/audio/audio_processor.h
new file mode 100644
index 00000000000..895eb796aa4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/audio_processor.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2010 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_PROCESSOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_PROCESSOR_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+class AudioBus;
+
+// AudioProcessor is an abstract base class representing an audio signal
+// processing object with a single input and a single output, where the number
+// of input channels equals the number of output channels. It can be used as
+// one part of a complex DSP algorithm, or as the processor for a basic (one
+// input - one output) AudioNode.
+
+class PLATFORM_EXPORT AudioProcessor {
+ USING_FAST_MALLOC(AudioProcessor);
+
+ public:
+ AudioProcessor(float sample_rate, unsigned number_of_channels)
+ : initialized_(false),
+ number_of_channels_(number_of_channels),
+ sample_rate_(sample_rate) {}
+
+ virtual ~AudioProcessor();
+
+ // Full initialization can be done here instead of in the constructor.
+ virtual void Initialize() = 0;
+ virtual void Uninitialize() = 0;
+
+ // Processes the source to destination bus. The number of channels must match
+ // in source and destination.
+ virtual void Process(const AudioBus* source,
+ AudioBus* destination,
+ size_t frames_to_process) = 0;
+
+ // Forces all AudioParams in the processor to run the timeline,
+ // bypassing any other processing the processor would do in
+ // process().
+ virtual void ProcessOnlyAudioParams(size_t frames_to_process){};
+
+ // Resets filter state
+ virtual void Reset() = 0;
+
+ virtual void SetNumberOfChannels(unsigned) = 0;
+ virtual unsigned NumberOfChannels() const = 0;
+
+ bool IsInitialized() const { return initialized_; }
+
+ float SampleRate() const { return sample_rate_; }
+
+ virtual double TailTime() const = 0;
+ virtual double LatencyTime() const = 0;
+ virtual bool RequiresTailProcessing() const = 0;
+
+ protected:
+ bool initialized_;
+ unsigned number_of_channels_;
+ float sample_rate_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_PROCESSOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/audio_resampler.cc b/chromium/third_party/blink/renderer/platform/audio/audio_resampler.cc
new file mode 100644
index 00000000000..e9cfa894182
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/audio_resampler.cc
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2010, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 <memory>
+#include "third_party/blink/renderer/platform/audio/audio_resampler.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+namespace blink {
+
+const double AudioResampler::kMaxRate = 8.0;
+
+AudioResampler::AudioResampler() : rate_(1.0) {
+ kernels_.push_back(std::make_unique<AudioResamplerKernel>(this));
+ source_bus_ = AudioBus::Create(1, 0, false);
+}
+
+AudioResampler::AudioResampler(unsigned number_of_channels) : rate_(1.0) {
+ for (unsigned i = 0; i < number_of_channels; ++i)
+ kernels_.push_back(std::make_unique<AudioResamplerKernel>(this));
+
+ source_bus_ = AudioBus::Create(number_of_channels, 0, false);
+}
+
+void AudioResampler::ConfigureChannels(unsigned number_of_channels) {
+ unsigned current_size = kernels_.size();
+ if (number_of_channels == current_size)
+ return; // already setup
+
+ // First deal with adding or removing kernels.
+ if (number_of_channels > current_size) {
+ for (unsigned i = current_size; i < number_of_channels; ++i)
+ kernels_.push_back(std::make_unique<AudioResamplerKernel>(this));
+ } else
+ kernels_.resize(number_of_channels);
+
+ // Reconfigure our source bus to the new channel size.
+ source_bus_ = AudioBus::Create(number_of_channels, 0, false);
+}
+
+void AudioResampler::Process(AudioSourceProvider* provider,
+ AudioBus* destination_bus,
+ size_t frames_to_process) {
+ DCHECK(provider);
+ if (!provider)
+ return;
+
+ unsigned number_of_channels = kernels_.size();
+
+ // Make sure our configuration matches the bus we're rendering to.
+ bool channels_match =
+ (destination_bus &&
+ destination_bus->NumberOfChannels() == number_of_channels);
+ DCHECK(channels_match);
+ if (!channels_match)
+ return;
+
+ // Setup the source bus.
+ for (unsigned i = 0; i < number_of_channels; ++i) {
+ // Figure out how many frames we need to get from the provider, and a
+ // pointer to the buffer.
+ size_t frames_needed;
+ float* fill_pointer =
+ kernels_[i]->GetSourcePointer(frames_to_process, &frames_needed);
+ DCHECK(fill_pointer);
+ if (!fill_pointer)
+ return;
+
+ source_bus_->SetChannelMemory(i, fill_pointer, frames_needed);
+ }
+
+ // Ask the provider to supply the desired number of source frames.
+ provider->ProvideInput(source_bus_.get(), source_bus_->length());
+
+ // Now that we have the source data, resample each channel into the
+ // destination bus.
+ // FIXME: optimize for the common stereo case where it's faster to process
+ // both left/right channels in the same inner loop.
+ for (unsigned i = 0; i < number_of_channels; ++i) {
+ float* destination = destination_bus->Channel(i)->MutableData();
+ kernels_[i]->Process(destination, frames_to_process);
+ }
+}
+
+void AudioResampler::SetRate(double rate) {
+ if (std::isnan(rate) || std::isinf(rate) || rate <= 0.0)
+ return;
+
+ rate_ = std::min(AudioResampler::kMaxRate, rate);
+}
+
+void AudioResampler::Reset() {
+ unsigned number_of_channels = kernels_.size();
+ for (unsigned i = 0; i < number_of_channels; ++i)
+ kernels_[i]->Reset();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/audio_resampler.h b/chromium/third_party/blink/renderer/platform/audio/audio_resampler.h
new file mode 100644
index 00000000000..5f667ed954d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/audio_resampler.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2010, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_RESAMPLER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_RESAMPLER_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/audio/audio_bus.h"
+#include "third_party/blink/renderer/platform/audio/audio_resampler_kernel.h"
+#include "third_party/blink/renderer/platform/audio/audio_source_provider.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+// AudioResampler resamples the audio stream from an AudioSourceProvider.
+// The audio stream may be single or multi-channel.
+// The default constructor defaults to single-channel (mono).
+
+class PLATFORM_EXPORT AudioResampler {
+ DISALLOW_NEW();
+ WTF_MAKE_NONCOPYABLE(AudioResampler);
+
+ public:
+ AudioResampler();
+ AudioResampler(unsigned number_of_channels);
+ ~AudioResampler() = default;
+
+ // Given an AudioSourceProvider, process() resamples the source stream into
+ // destinationBus.
+ void Process(AudioSourceProvider*,
+ AudioBus* destination_bus,
+ size_t frames_to_process);
+
+ // Resets the processing state.
+ void Reset();
+
+ void ConfigureChannels(unsigned number_of_channels);
+
+ // 0 < rate <= MaxRate
+ void SetRate(double rate);
+ double Rate() const { return rate_; }
+
+ static const double kMaxRate;
+
+ private:
+ double rate_;
+ Vector<std::unique_ptr<AudioResamplerKernel>> kernels_;
+ scoped_refptr<AudioBus> source_bus_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_RESAMPLER_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/audio_resampler_kernel.cc b/chromium/third_party/blink/renderer/platform/audio/audio_resampler_kernel.cc
new file mode 100644
index 00000000000..9a24777a14f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/audio_resampler_kernel.cc
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2010, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/audio/audio_resampler.h"
+#include "third_party/blink/renderer/platform/audio/audio_resampler_kernel.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+namespace blink {
+
+const size_t AudioResamplerKernel::kMaxFramesToProcess = 128;
+
+AudioResamplerKernel::AudioResamplerKernel(AudioResampler* resampler)
+ : resampler_(resampler),
+ // The buffer size must be large enough to hold up to two extra sample
+ // frames for the linear interpolation.
+ source_buffer_(
+ 2 + static_cast<int>(kMaxFramesToProcess * AudioResampler::kMaxRate)),
+ virtual_read_index_(0.0),
+ fill_index_(0) {
+ last_values_[0] = 0.0f;
+ last_values_[1] = 0.0f;
+}
+
+float* AudioResamplerKernel::GetSourcePointer(
+ size_t frames_to_process,
+ size_t* number_of_source_frames_needed_p) {
+ DCHECK_LE(frames_to_process, kMaxFramesToProcess);
+
+ // Calculate the next "virtual" index. After process() is called,
+ // m_virtualReadIndex will equal this value.
+ double next_fractional_index =
+ virtual_read_index_ + frames_to_process * Rate();
+
+ // Because we're linearly interpolating between the previous and next sample
+ // we need to round up so we include the next sample.
+ int end_index = static_cast<int>(next_fractional_index +
+ 1.0); // round up to next integer index
+
+ // Determine how many input frames we'll need.
+ // We need to fill the buffer up to and including endIndex (so add 1) but
+ // we've already buffered m_fillIndex frames from last time.
+ size_t frames_needed = 1 + end_index - fill_index_;
+ if (number_of_source_frames_needed_p)
+ *number_of_source_frames_needed_p = frames_needed;
+
+ // Do bounds checking for the source buffer.
+ bool is_good = fill_index_ < source_buffer_.size() &&
+ fill_index_ + frames_needed <= source_buffer_.size();
+ DCHECK(is_good);
+ if (!is_good)
+ return nullptr;
+
+ return source_buffer_.Data() + fill_index_;
+}
+
+void AudioResamplerKernel::Process(float* destination,
+ size_t frames_to_process) {
+ DCHECK_LE(frames_to_process, kMaxFramesToProcess);
+
+ float* source = source_buffer_.Data();
+
+ double rate = this->Rate();
+ rate = clampTo(rate, 0.0, AudioResampler::kMaxRate);
+
+ // Start out with the previous saved values (if any).
+ if (fill_index_ > 0) {
+ source[0] = last_values_[0];
+ source[1] = last_values_[1];
+ }
+
+ // Make a local copy.
+ double virtual_read_index = virtual_read_index_;
+
+ // Sanity check source buffer access.
+ DCHECK_GT(frames_to_process, 0u);
+ DCHECK_GE(virtual_read_index, 0);
+ DCHECK_LT(1 + static_cast<unsigned>(virtual_read_index +
+ (frames_to_process - 1) * rate),
+ source_buffer_.size());
+
+ // Do the linear interpolation.
+ int n = frames_to_process;
+ while (n--) {
+ unsigned read_index = static_cast<unsigned>(virtual_read_index);
+ double interpolation_factor = virtual_read_index - read_index;
+
+ double sample1 = source[read_index];
+ double sample2 = source[read_index + 1];
+
+ double sample =
+ (1.0 - interpolation_factor) * sample1 + interpolation_factor * sample2;
+
+ *destination++ = static_cast<float>(sample);
+
+ virtual_read_index += rate;
+ }
+
+ // Save the last two sample-frames which will later be used at the beginning
+ // of the source buffer the next time around.
+ int read_index = static_cast<int>(virtual_read_index);
+ last_values_[0] = source[read_index];
+ last_values_[1] = source[read_index + 1];
+ fill_index_ = 2;
+
+ // Wrap the virtual read index back to the start of the buffer.
+ virtual_read_index -= read_index;
+
+ // Put local copy back into member variable.
+ virtual_read_index_ = virtual_read_index;
+}
+
+void AudioResamplerKernel::Reset() {
+ virtual_read_index_ = 0.0;
+ fill_index_ = 0;
+ last_values_[0] = 0.0f;
+ last_values_[1] = 0.0f;
+}
+
+double AudioResamplerKernel::Rate() const {
+ return resampler_->Rate();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/audio_resampler_kernel.h b/chromium/third_party/blink/renderer/platform/audio/audio_resampler_kernel.h
new file mode 100644
index 00000000000..a8d2e427b16
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/audio_resampler_kernel.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2010, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_RESAMPLER_KERNEL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_RESAMPLER_KERNEL_H_
+
+#include "third_party/blink/renderer/platform/audio/audio_array.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+class AudioResampler;
+
+// AudioResamplerKernel does resampling on a single mono channel.
+// It uses a simple linear interpolation for good performance.
+
+class PLATFORM_EXPORT AudioResamplerKernel {
+ USING_FAST_MALLOC(AudioResamplerKernel);
+ WTF_MAKE_NONCOPYABLE(AudioResamplerKernel);
+
+ public:
+ AudioResamplerKernel(AudioResampler*);
+
+ // getSourcePointer() should be called each time before process() is called.
+ // Given a number of frames to process (for subsequent call to process()), it
+ // returns a pointer and numberOfSourceFramesNeeded where sample data should
+ // be copied. This sample data provides the input to the resampler when
+ // process() is called. framesToProcess must be less than or equal to
+ // MaxFramesToProcess.
+ float* GetSourcePointer(size_t frames_to_process,
+ size_t* number_of_source_frames_needed);
+
+ // process() resamples framesToProcess frames from the source into
+ // destination. Each call to process() must be preceded by a call to
+ // getSourcePointer() so that source input may be supplied. framesToProcess
+ // must be less than or equal to MaxFramesToProcess.
+ void Process(float* destination, size_t frames_to_process);
+
+ // Resets the processing state.
+ void Reset();
+
+ static const size_t kMaxFramesToProcess;
+
+ private:
+ double Rate() const;
+
+ AudioResampler* resampler_;
+ AudioFloatArray source_buffer_;
+
+ // This is a (floating point) read index on the input stream.
+ double virtual_read_index_;
+
+ // We need to have continuity from one call of process() to the next.
+ // m_lastValues stores the last two sample values from the last call to
+ // process(). m_fillIndex represents how many buffered samples we have which
+ // can be as many as 2. For the first call to process() (or after reset())
+ // there will be no buffered samples.
+ float last_values_[2];
+ unsigned fill_index_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_RESAMPLER_KERNEL_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/audio_source_provider.h b/chromium/third_party/blink/renderer/platform/audio/audio_source_provider.h
new file mode 100644
index 00000000000..174658c08a5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/audio_source_provider.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_SOURCE_PROVIDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_SOURCE_PROVIDER_H_
+
+#include <cstddef>
+#include "base/macros.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+class AudioBus;
+class AudioSourceProviderClient;
+
+// Abstract base-class for a pull-model client.
+class PLATFORM_EXPORT AudioSourceProvider {
+ USING_FAST_MALLOC(AudioSourceProvider);
+ DISALLOW_COPY_AND_ASSIGN(AudioSourceProvider);
+
+ public:
+ AudioSourceProvider() = default;
+
+ // provideInput() gets called repeatedly to render time-slices of a continuous
+ // audio stream.
+ virtual void ProvideInput(AudioBus* bus, size_t frames_to_process) = 0;
+
+ // If a client is set, we call it back when the audio format is available or
+ // changes.
+ virtual void SetClient(AudioSourceProviderClient*){};
+
+ virtual ~AudioSourceProvider() = default;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_SOURCE_PROVIDER_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/audio_source_provider_client.h b/chromium/third_party/blink/renderer/platform/audio/audio_source_provider_client.h
new file mode 100644
index 00000000000..c1537a14925
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/audio_source_provider_client.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2011, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_SOURCE_PROVIDER_CLIENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_SOURCE_PROVIDER_CLIENT_H_
+
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+
+namespace blink {
+
+class AudioSourceProviderClient : public GarbageCollectedMixin {
+ public:
+ virtual void SetFormat(size_t number_of_channels, float sample_rate) = 0;
+ // Oilpan: Callers should keep this object alive during lock() and unlock().
+ virtual void lock() {}
+ virtual void unlock() {}
+
+ // Called on the main thread when HTMLMediaElement::currentSrc() is
+ // changed.
+ virtual void OnCurrentSrcChanged(const KURL& current_src) {}
+
+ void Trace(blink::Visitor* visitor) override {}
+
+ protected:
+ virtual ~AudioSourceProviderClient() = default;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_SOURCE_PROVIDER_CLIENT_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/audio_utilities.cc b/chromium/third_party/blink/renderer/platform/audio/audio_utilities.cc
new file mode 100644
index 00000000000..4489753411f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/audio_utilities.cc
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2010, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/audio/audio_utilities.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+namespace blink {
+
+namespace AudioUtilities {
+
+float DecibelsToLinear(float decibels) {
+ return powf(10, 0.05f * decibels);
+}
+
+float LinearToDecibels(float linear) {
+ DCHECK_GE(linear, 0);
+
+ return 20 * log10f(linear);
+}
+
+double DiscreteTimeConstantForSampleRate(double time_constant,
+ double sample_rate) {
+ // From the WebAudio spec, the formula for setTargetAtTime is
+ //
+ // v(t) = V1 + (V0 - V1)*exp(-t/tau)
+ //
+ // where tau is the time constant, V1 is the target value and V0 is
+ // the starting value.
+ //
+ // Rewrite this as
+ //
+ // v(t) = V0 + (V1 - V0)*(1-exp(-t/tau))
+ //
+ // The implementation of setTargetAtTime uses this form. So at the
+ // sample points, we have
+ //
+ // v(n/Fs) = V0 + (V1 - V0)*(1-exp(-n/(Fs*tau)))
+ //
+ // where Fs is the sample rate of the sampled systme. Thus, the
+ // discrete time constant is
+ //
+ // 1 - exp(-1/(Fs*tau)
+ return 1 - exp(-1 / (sample_rate * time_constant));
+}
+
+size_t TimeToSampleFrame(double time, double sample_rate) {
+ DCHECK_GE(time, 0);
+ double frame = round(time * sample_rate);
+
+ // Just return the largest possible size_t value if necessary.
+ if (frame >= std::numeric_limits<size_t>::max()) {
+ return std::numeric_limits<size_t>::max();
+ }
+
+ return static_cast<size_t>(frame);
+}
+
+bool IsValidAudioBufferSampleRate(float sample_rate) {
+ return sample_rate >= MinAudioBufferSampleRate() &&
+ sample_rate <= MaxAudioBufferSampleRate();
+}
+
+float MinAudioBufferSampleRate() {
+ // crbug.com/344375
+ return 3000;
+}
+
+float MaxAudioBufferSampleRate() {
+ // <video> tags support sample rates up 384 kHz so audio context
+ // should too.
+ return 384000;
+}
+
+bool IsPowerOfTwo(size_t x) {
+ // From Hacker's Delight. x & (x - 1) turns off (zeroes) the
+ // rightmost 1-bit in the word x. If x is a power of two, then the
+ // result is, of course, 0.
+ return x > 0 && ((x & (x - 1)) == 0);
+}
+
+} // namespace AudioUtilities
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/audio_utilities.h b/chromium/third_party/blink/renderer/platform/audio/audio_utilities.h
new file mode 100644
index 00000000000..9855db93a24
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/audio_utilities.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2010, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_UTILITIES_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_UTILITIES_H_
+
+#include <cstddef>
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+namespace AudioUtilities {
+
+// Rendering quantum size. This is how many frames are processed at a time for
+// each node in the audio graph.
+static const unsigned kRenderQuantumFrames = 128;
+
+// Standard functions for converting to and from decibel values from linear.
+PLATFORM_EXPORT float LinearToDecibels(float);
+PLATFORM_EXPORT float DecibelsToLinear(float);
+
+// timeConstant is the time it takes a first-order linear time-invariant system
+// to reach the value 1 - 1/e (around 63.2%) given a step input response.
+// discreteTimeConstantForSampleRate() will return the discrete time-constant
+// for the specific sampleRate.
+PLATFORM_EXPORT double DiscreteTimeConstantForSampleRate(double time_constant,
+ double sample_rate);
+
+// Convert the time to a sample frame at the given sample rate.
+PLATFORM_EXPORT size_t TimeToSampleFrame(double time, double sample_rate);
+
+// Check that |sampleRate| is a valid rate for AudioBuffers.
+PLATFORM_EXPORT bool IsValidAudioBufferSampleRate(float sample_rate);
+
+// Return max/min sample rate supported by AudioBuffers.
+PLATFORM_EXPORT float MinAudioBufferSampleRate();
+PLATFORM_EXPORT float MaxAudioBufferSampleRate();
+
+// Check to see if x is a power of two. If x == 0, returns false.
+PLATFORM_EXPORT bool IsPowerOfTwo(size_t x);
+
+} // namespace AudioUtilities
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_UTILITIES_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/biquad.cc b/chromium/third_party/blink/renderer/platform/audio/biquad.cc
new file mode 100644
index 00000000000..c9a643d5505
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/biquad.cc
@@ -0,0 +1,901 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/audio/biquad.h"
+
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/audio/audio_utilities.h"
+#include "third_party/blink/renderer/platform/audio/denormal_disabler.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+#include <algorithm>
+#include <complex>
+#include <stdio.h>
+#if defined(OS_MACOSX)
+#include <Accelerate/Accelerate.h>
+#endif
+
+namespace blink {
+
+#if defined(OS_MACOSX)
+const int kBiquadBufferSize = 1024;
+#endif
+
+// Compute 10^x = exp(x*log(10))
+static double pow10(double x) {
+ return expf(x * 2.30258509299404568402);
+}
+
+Biquad::Biquad() : has_sample_accurate_values_(false) {
+#if defined(OS_MACOSX)
+ // Allocate two samples more for filter history
+ input_buffer_.Allocate(kBiquadBufferSize + 2);
+ output_buffer_.Allocate(kBiquadBufferSize + 2);
+#endif
+
+ // Allocate enough space for the a-rate filter coefficients to handle a
+ // rendering quantum of 128 frames.
+ b0_.Allocate(AudioUtilities::kRenderQuantumFrames);
+ b1_.Allocate(AudioUtilities::kRenderQuantumFrames);
+ b2_.Allocate(AudioUtilities::kRenderQuantumFrames);
+ a1_.Allocate(AudioUtilities::kRenderQuantumFrames);
+ a2_.Allocate(AudioUtilities::kRenderQuantumFrames);
+
+ // Initialize as pass-thru (straight-wire, no filter effect)
+ SetNormalizedCoefficients(0, 1, 0, 0, 1, 0, 0);
+
+ Reset(); // clear filter memory
+}
+
+Biquad::~Biquad() = default;
+
+void Biquad::Process(const float* source_p,
+ float* dest_p,
+ size_t frames_to_process) {
+ // WARNING: sourceP and destP may be pointing to the same area of memory!
+ // Be sure to read from sourceP before writing to destP!
+ if (HasSampleAccurateValues()) {
+ int n = frames_to_process;
+
+ // Create local copies of member variables
+ double x1 = x1_;
+ double x2 = x2_;
+ double y1 = y1_;
+ double y2 = y2_;
+
+ const double* b0 = b0_.Data();
+ const double* b1 = b1_.Data();
+ const double* b2 = b2_.Data();
+ const double* a1 = a1_.Data();
+ const double* a2 = a2_.Data();
+
+ for (int k = 0; k < n; ++k) {
+ // FIXME: this can be optimized by pipelining the multiply adds...
+ float x = *source_p++;
+ float y = b0[k] * x + b1[k] * x1 + b2[k] * x2 - a1[k] * y1 - a2[k] * y2;
+
+ *dest_p++ = y;
+
+ // Update state variables
+ x2 = x1;
+ x1 = x;
+ y2 = y1;
+ y1 = y;
+ }
+
+ // Local variables back to member. Flush denormals here so we
+ // don't slow down the inner loop above.
+ x1_ = DenormalDisabler::FlushDenormalFloatToZero(x1);
+ x2_ = DenormalDisabler::FlushDenormalFloatToZero(x2);
+ y1_ = DenormalDisabler::FlushDenormalFloatToZero(y1);
+ y2_ = DenormalDisabler::FlushDenormalFloatToZero(y2);
+
+ // There is an assumption here that once we have sample accurate values we
+ // can never go back to not having sample accurate values. This is
+ // currently true in the way AudioParamTimline is implemented: once an
+ // event is inserted, sample accurate processing is always enabled.
+ //
+ // If so, then we never have to update the state variables for the MACOSX
+ // path. The structure of the state variable in these cases aren't well
+ // documented so it's not clear how to update them anyway.
+ } else {
+#if defined(OS_MACOSX)
+ double* input_p = input_buffer_.Data();
+ double* output_p = output_buffer_.Data();
+
+ // Set up filter state. This is needed in case we're switching from
+ // filtering with variable coefficients (i.e., with automations) to
+ // fixed coefficients (without automations).
+ input_p[0] = x2_;
+ input_p[1] = x1_;
+ output_p[0] = y2_;
+ output_p[1] = y1_;
+
+ // Use vecLib if available
+ ProcessFast(source_p, dest_p, frames_to_process);
+
+ // Copy the last inputs and outputs to the filter memory variables.
+ // This is needed because the next rendering quantum might be an
+ // automation which needs the history to continue correctly. Because
+ // sourceP and destP can be the same block of memory, we can't read from
+ // sourceP to get the last inputs. Fortunately, processFast has put the
+ // last inputs in input[0] and input[1].
+ x1_ = input_p[1];
+ x2_ = input_p[0];
+ y1_ = dest_p[frames_to_process - 1];
+ y2_ = dest_p[frames_to_process - 2];
+
+#else
+ int n = frames_to_process;
+
+ // Create local copies of member variables
+ double x1 = x1_;
+ double x2 = x2_;
+ double y1 = y1_;
+ double y2 = y2_;
+
+ double b0 = b0_[0];
+ double b1 = b1_[0];
+ double b2 = b2_[0];
+ double a1 = a1_[0];
+ double a2 = a2_[0];
+
+ while (n--) {
+ // FIXME: this can be optimized by pipelining the multiply adds...
+ float x = *source_p++;
+ float y = b0 * x + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2;
+
+ *dest_p++ = y;
+
+ // Update state variables
+ x2 = x1;
+ x1 = x;
+ y2 = y1;
+ y1 = y;
+ }
+
+ // Local variables back to member. Flush denormals here so we
+ // don't slow down the inner loop above.
+ x1_ = DenormalDisabler::FlushDenormalFloatToZero(x1);
+ x2_ = DenormalDisabler::FlushDenormalFloatToZero(x2);
+ y1_ = DenormalDisabler::FlushDenormalFloatToZero(y1);
+ y2_ = DenormalDisabler::FlushDenormalFloatToZero(y2);
+#endif
+ }
+}
+
+#if defined(OS_MACOSX)
+
+// Here we have optimized version using Accelerate.framework
+
+void Biquad::ProcessFast(const float* source_p,
+ float* dest_p,
+ size_t frames_to_process) {
+ double filter_coefficients[5];
+ filter_coefficients[0] = b0_[0];
+ filter_coefficients[1] = b1_[0];
+ filter_coefficients[2] = b2_[0];
+ filter_coefficients[3] = a1_[0];
+ filter_coefficients[4] = a2_[0];
+
+ double* input_p = input_buffer_.Data();
+ double* output_p = output_buffer_.Data();
+
+ double* input2p = input_p + 2;
+ double* output2p = output_p + 2;
+
+ // Break up processing into smaller slices (kBiquadBufferSize) if necessary.
+
+ int n = frames_to_process;
+
+ while (n > 0) {
+ int frames_this_time = n < kBiquadBufferSize ? n : kBiquadBufferSize;
+
+ // Copy input to input buffer
+ for (int i = 0; i < frames_this_time; ++i)
+ input2p[i] = *source_p++;
+
+ ProcessSliceFast(input_p, output_p, filter_coefficients, frames_this_time);
+
+ // Copy output buffer to output (converts float -> double).
+ for (int i = 0; i < frames_this_time; ++i)
+ *dest_p++ = static_cast<float>(output2p[i]);
+
+ n -= frames_this_time;
+ }
+}
+
+void Biquad::ProcessSliceFast(double* source_p,
+ double* dest_p,
+ double* coefficients_p,
+ size_t frames_to_process) {
+ // Use double-precision for filter stability
+ vDSP_deq22D(source_p, 1, coefficients_p, dest_p, 1, frames_to_process);
+
+ // Save history. Note that sourceP and destP reference m_inputBuffer and
+ // m_outputBuffer respectively. These buffers are allocated (in the
+ // constructor) with space for two extra samples so it's OK to access array
+ // values two beyond framesToProcess.
+ source_p[0] = source_p[frames_to_process - 2 + 2];
+ source_p[1] = source_p[frames_to_process - 1 + 2];
+ dest_p[0] = dest_p[frames_to_process - 2 + 2];
+ dest_p[1] = dest_p[frames_to_process - 1 + 2];
+}
+
+#endif // defined(OS_MACOSX)
+
+void Biquad::Reset() {
+#if defined(OS_MACOSX)
+ // Two extra samples for filter history
+ double* input_p = input_buffer_.Data();
+ input_p[0] = 0;
+ input_p[1] = 0;
+
+ double* output_p = output_buffer_.Data();
+ output_p[0] = 0;
+ output_p[1] = 0;
+
+#endif
+ x1_ = x2_ = y1_ = y2_ = 0;
+}
+
+void Biquad::SetLowpassParams(int index, double cutoff, double resonance) {
+ // Limit cutoff to 0 to 1.
+ cutoff = clampTo(cutoff, 0.0, 1.0);
+
+ if (cutoff == 1) {
+ // When cutoff is 1, the z-transform is 1.
+ SetNormalizedCoefficients(index, 1, 0, 0, 1, 0, 0);
+ } else if (cutoff > 0) {
+ // Compute biquad coefficients for lowpass filter
+
+ resonance = pow10(resonance / 20);
+
+ double theta = piDouble * cutoff;
+ double alpha = sin(theta) / (2 * resonance);
+ double cosw = cos(theta);
+ double beta = (1 - cosw) / 2;
+
+ double b0 = beta;
+ double b1 = 2 * beta;
+ double b2 = beta;
+
+ double a0 = 1 + alpha;
+ double a1 = -2 * cosw;
+ double a2 = 1 - alpha;
+
+ SetNormalizedCoefficients(index, b0, b1, b2, a0, a1, a2);
+ } else {
+ // When cutoff is zero, nothing gets through the filter, so set
+ // coefficients up correctly.
+ SetNormalizedCoefficients(index, 0, 0, 0, 1, 0, 0);
+ }
+}
+
+void Biquad::SetHighpassParams(int index, double cutoff, double resonance) {
+ // Limit cutoff to 0 to 1.
+ cutoff = clampTo(cutoff, 0.0, 1.0);
+
+ if (cutoff == 1) {
+ // The z-transform is 0.
+ SetNormalizedCoefficients(index, 0, 0, 0, 1, 0, 0);
+ } else if (cutoff > 0) {
+ // Compute biquad coefficients for highpass filter
+
+ resonance = pow10(resonance / 20);
+ double theta = piDouble * cutoff;
+ double alpha = sin(theta) / (2 * resonance);
+ double cosw = cos(theta);
+ double beta = (1 + cosw) / 2;
+
+ double b0 = beta;
+ double b1 = -2 * beta;
+ double b2 = beta;
+
+ double a0 = 1 + alpha;
+ double a1 = -2 * cosw;
+ double a2 = 1 - alpha;
+
+ SetNormalizedCoefficients(index, b0, b1, b2, a0, a1, a2);
+ } else {
+ // When cutoff is zero, we need to be careful because the above
+ // gives a quadratic divided by the same quadratic, with poles
+ // and zeros on the unit circle in the same place. When cutoff
+ // is zero, the z-transform is 1.
+ SetNormalizedCoefficients(index, 1, 0, 0, 1, 0, 0);
+ }
+}
+
+void Biquad::SetNormalizedCoefficients(int index,
+ double b0,
+ double b1,
+ double b2,
+ double a0,
+ double a1,
+ double a2) {
+ double a0_inverse = 1 / a0;
+
+ b0_[index] = b0 * a0_inverse;
+ b1_[index] = b1 * a0_inverse;
+ b2_[index] = b2 * a0_inverse;
+ a1_[index] = a1 * a0_inverse;
+ a2_[index] = a2 * a0_inverse;
+}
+
+void Biquad::SetLowShelfParams(int index, double frequency, double db_gain) {
+ // Clip frequencies to between 0 and 1, inclusive.
+ frequency = clampTo(frequency, 0.0, 1.0);
+
+ double a = pow10(db_gain / 40);
+
+ if (frequency == 1) {
+ // The z-transform is a constant gain.
+ SetNormalizedCoefficients(index, a * a, 0, 0, 1, 0, 0);
+ } else if (frequency > 0) {
+ double w0 = piDouble * frequency;
+ double s = 1; // filter slope (1 is max value)
+ double alpha = 0.5 * sin(w0) * sqrt((a + 1 / a) * (1 / s - 1) + 2);
+ double k = cos(w0);
+ double k2 = 2 * sqrt(a) * alpha;
+ double a_plus_one = a + 1;
+ double a_minus_one = a - 1;
+
+ double b0 = a * (a_plus_one - a_minus_one * k + k2);
+ double b1 = 2 * a * (a_minus_one - a_plus_one * k);
+ double b2 = a * (a_plus_one - a_minus_one * k - k2);
+ double a0 = a_plus_one + a_minus_one * k + k2;
+ double a1 = -2 * (a_minus_one + a_plus_one * k);
+ double a2 = a_plus_one + a_minus_one * k - k2;
+
+ SetNormalizedCoefficients(index, b0, b1, b2, a0, a1, a2);
+ } else {
+ // When frequency is 0, the z-transform is 1.
+ SetNormalizedCoefficients(index, 1, 0, 0, 1, 0, 0);
+ }
+}
+
+void Biquad::SetHighShelfParams(int index, double frequency, double db_gain) {
+ // Clip frequencies to between 0 and 1, inclusive.
+ frequency = clampTo(frequency, 0.0, 1.0);
+
+ double a = pow10(db_gain / 40);
+
+ if (frequency == 1) {
+ // The z-transform is 1.
+ SetNormalizedCoefficients(index, 1, 0, 0, 1, 0, 0);
+ } else if (frequency > 0) {
+ double w0 = piDouble * frequency;
+ double s = 1; // filter slope (1 is max value)
+ double alpha = 0.5 * sin(w0) * sqrt((a + 1 / a) * (1 / s - 1) + 2);
+ double k = cos(w0);
+ double k2 = 2 * sqrt(a) * alpha;
+ double a_plus_one = a + 1;
+ double a_minus_one = a - 1;
+
+ double b0 = a * (a_plus_one + a_minus_one * k + k2);
+ double b1 = -2 * a * (a_minus_one + a_plus_one * k);
+ double b2 = a * (a_plus_one + a_minus_one * k - k2);
+ double a0 = a_plus_one - a_minus_one * k + k2;
+ double a1 = 2 * (a_minus_one - a_plus_one * k);
+ double a2 = a_plus_one - a_minus_one * k - k2;
+
+ SetNormalizedCoefficients(index, b0, b1, b2, a0, a1, a2);
+ } else {
+ // When frequency = 0, the filter is just a gain, A^2.
+ SetNormalizedCoefficients(index, a * a, 0, 0, 1, 0, 0);
+ }
+}
+
+void Biquad::SetPeakingParams(int index,
+ double frequency,
+ double q,
+ double db_gain) {
+ // Clip frequencies to between 0 and 1, inclusive.
+ frequency = clampTo(frequency, 0.0, 1.0);
+
+ // Don't let Q go negative, which causes an unstable filter.
+ q = std::max(0.0, q);
+
+ double a = pow10(db_gain / 40);
+
+ if (frequency > 0 && frequency < 1) {
+ if (q > 0) {
+ double w0 = piDouble * frequency;
+ double alpha = sin(w0) / (2 * q);
+ double k = cos(w0);
+
+ double b0 = 1 + alpha * a;
+ double b1 = -2 * k;
+ double b2 = 1 - alpha * a;
+ double a0 = 1 + alpha / a;
+ double a1 = -2 * k;
+ double a2 = 1 - alpha / a;
+
+ SetNormalizedCoefficients(index, b0, b1, b2, a0, a1, a2);
+ } else {
+ // When Q = 0, the above formulas have problems. If we look at
+ // the z-transform, we can see that the limit as Q->0 is A^2, so
+ // set the filter that way.
+ SetNormalizedCoefficients(index, a * a, 0, 0, 1, 0, 0);
+ }
+ } else {
+ // When frequency is 0 or 1, the z-transform is 1.
+ SetNormalizedCoefficients(index, 1, 0, 0, 1, 0, 0);
+ }
+}
+
+void Biquad::SetAllpassParams(int index, double frequency, double q) {
+ // Clip frequencies to between 0 and 1, inclusive.
+ frequency = clampTo(frequency, 0.0, 1.0);
+
+ // Don't let Q go negative, which causes an unstable filter.
+ q = std::max(0.0, q);
+
+ if (frequency > 0 && frequency < 1) {
+ if (q > 0) {
+ double w0 = piDouble * frequency;
+ double alpha = sin(w0) / (2 * q);
+ double k = cos(w0);
+
+ double b0 = 1 - alpha;
+ double b1 = -2 * k;
+ double b2 = 1 + alpha;
+ double a0 = 1 + alpha;
+ double a1 = -2 * k;
+ double a2 = 1 - alpha;
+
+ SetNormalizedCoefficients(index, b0, b1, b2, a0, a1, a2);
+ } else {
+ // When Q = 0, the above formulas have problems. If we look at
+ // the z-transform, we can see that the limit as Q->0 is -1, so
+ // set the filter that way.
+ SetNormalizedCoefficients(index, -1, 0, 0, 1, 0, 0);
+ }
+ } else {
+ // When frequency is 0 or 1, the z-transform is 1.
+ SetNormalizedCoefficients(index, 1, 0, 0, 1, 0, 0);
+ }
+}
+
+void Biquad::SetNotchParams(int index, double frequency, double q) {
+ // Clip frequencies to between 0 and 1, inclusive.
+ frequency = clampTo(frequency, 0.0, 1.0);
+
+ // Don't let Q go negative, which causes an unstable filter.
+ q = std::max(0.0, q);
+
+ if (frequency > 0 && frequency < 1) {
+ if (q > 0) {
+ double w0 = piDouble * frequency;
+ double alpha = sin(w0) / (2 * q);
+ double k = cos(w0);
+
+ double b0 = 1;
+ double b1 = -2 * k;
+ double b2 = 1;
+ double a0 = 1 + alpha;
+ double a1 = -2 * k;
+ double a2 = 1 - alpha;
+
+ SetNormalizedCoefficients(index, b0, b1, b2, a0, a1, a2);
+ } else {
+ // When Q = 0, the above formulas have problems. If we look at
+ // the z-transform, we can see that the limit as Q->0 is 0, so
+ // set the filter that way.
+ SetNormalizedCoefficients(index, 0, 0, 0, 1, 0, 0);
+ }
+ } else {
+ // When frequency is 0 or 1, the z-transform is 1.
+ SetNormalizedCoefficients(index, 1, 0, 0, 1, 0, 0);
+ }
+}
+
+void Biquad::SetBandpassParams(int index, double frequency, double q) {
+ // No negative frequencies allowed.
+ frequency = std::max(0.0, frequency);
+
+ // Don't let Q go negative, which causes an unstable filter.
+ q = std::max(0.0, q);
+
+ if (frequency > 0 && frequency < 1) {
+ double w0 = piDouble * frequency;
+ if (q > 0) {
+ double alpha = sin(w0) / (2 * q);
+ double k = cos(w0);
+
+ double b0 = alpha;
+ double b1 = 0;
+ double b2 = -alpha;
+ double a0 = 1 + alpha;
+ double a1 = -2 * k;
+ double a2 = 1 - alpha;
+
+ SetNormalizedCoefficients(index, b0, b1, b2, a0, a1, a2);
+ } else {
+ // When Q = 0, the above formulas have problems. If we look at
+ // the z-transform, we can see that the limit as Q->0 is 1, so
+ // set the filter that way.
+ SetNormalizedCoefficients(index, 1, 0, 0, 1, 0, 0);
+ }
+ } else {
+ // When the cutoff is zero, the z-transform approaches 0, if Q
+ // > 0. When both Q and cutoff are zero, the z-transform is
+ // pretty much undefined. What should we do in this case?
+ // For now, just make the filter 0. When the cutoff is 1, the
+ // z-transform also approaches 0.
+ SetNormalizedCoefficients(index, 0, 0, 0, 1, 0, 0);
+ }
+}
+
+void Biquad::GetFrequencyResponse(int n_frequencies,
+ const float* frequency,
+ float* mag_response,
+ float* phase_response) {
+ // Evaluate the Z-transform of the filter at given normalized
+ // frequency from 0 to 1. (1 corresponds to the Nyquist
+ // frequency.)
+ //
+ // The z-transform of the filter is
+ //
+ // H(z) = (b0 + b1*z^(-1) + b2*z^(-2))/(1 + a1*z^(-1) + a2*z^(-2))
+ //
+ // Evaluate as
+ //
+ // b0 + (b1 + b2*z1)*z1
+ // --------------------
+ // 1 + (a1 + a2*z1)*z1
+ //
+ // with z1 = 1/z and z = exp(j*pi*frequency). Hence z1 = exp(-j*pi*frequency)
+
+ // Make local copies of the coefficients as a micro-optimization.
+ double b0 = b0_[0];
+ double b1 = b1_[0];
+ double b2 = b2_[0];
+ double a1 = a1_[0];
+ double a2 = a2_[0];
+
+ for (int k = 0; k < n_frequencies; ++k) {
+ if (frequency[k] < 0 || frequency[k] > 1) {
+ // Out-of-bounds frequencies should return NaN.
+ mag_response[k] = std::nanf("");
+ phase_response[k] = std::nanf("");
+ } else {
+ double omega = -piDouble * frequency[k];
+ std::complex<double> z = std::complex<double>(cos(omega), sin(omega));
+ std::complex<double> numerator = b0 + (b1 + b2 * z) * z;
+ std::complex<double> denominator =
+ std::complex<double>(1, 0) + (a1 + a2 * z) * z;
+ std::complex<double> response = numerator / denominator;
+ mag_response[k] = static_cast<float>(abs(response));
+ phase_response[k] =
+ static_cast<float>(atan2(imag(response), real(response)));
+ }
+ }
+}
+
+static double RepeatedRootResponse(double n,
+ double c1,
+ double c2,
+ double r,
+ double log_eps) {
+ // The response is h(n) = r^(n-2)*[c1*(n+1)*r^2+c2]. We're looking
+ // for n such that |h(n)| = eps. Equivalently, we want a root
+ // of the equation log(|h(n)|) - log(eps) = 0 or
+ //
+ // (n-2)*log(r) + log(|c1*(n+1)*r^2+c2|) - log(eps)
+ //
+ // This helps with finding a nuemrical solution because this
+ // approximately linearizes the response for large n.
+
+ return (n - 2) * log(r) + log(fabs(c1 * (n + 1) * r * r + c2)) - log_eps;
+}
+
+// Regula Falsi root finder, Illinois variant
+// (https://en.wikipedia.org/wiki/False_position_method#The_Illinois_algorithm).
+//
+// This finds a root of the repeated root response where the root is
+// assumed to lie between |low| and |high|. The response is given by
+// |c1|, |c2|, and |r| as determined by |RepeatedRootResponse|.
+// |log_eps| is the log the the maximum allowed amplitude in the
+// response.
+static double RootFinder(double low,
+ double high,
+ double log_eps,
+ double c1,
+ double c2,
+ double r) {
+ // Desired accuray of the root (in frames). This doesn't need to be
+ // super-accurate, so half frame is good enough, and should be less
+ // than 1 because the algorithm may prematurely terminate.
+ const double kAccuracyThreshold = 0.5;
+ // Max number of iterations to do. If we haven't converged by now,
+ // just return whatever we've found.
+ const int kMaxIterations = 10;
+
+ int side = 0;
+ double root = 0;
+ double f_low = RepeatedRootResponse(low, c1, c2, r, log_eps);
+ double f_high = RepeatedRootResponse(high, c1, c2, r, log_eps);
+
+ // The function values must be finite and have opposite signs!
+ DCHECK(std::isfinite(f_low));
+ DCHECK(std::isfinite(f_high));
+ DCHECK_LE(f_low * f_high, 0);
+
+ int iteration;
+ for (iteration = 0; iteration < kMaxIterations; ++iteration) {
+ root = (f_low * high - f_high * low) / (f_low - f_high);
+ if (fabs(high - low) < kAccuracyThreshold * fabs(high + low))
+ break;
+ double fr = RepeatedRootResponse(root, c1, c2, r, log_eps);
+
+ DCHECK(std::isfinite(fr));
+
+ if (fr * f_high > 0) {
+ // fr and f_high have same sign. Copy root to f_high
+ high = root;
+ f_high = fr;
+ side = -1;
+ } else if (f_low * fr > 0) {
+ // fr and f_low have same sign. Copy root to f_low
+ low = root;
+ f_low = fr;
+ if (side == 1)
+ f_high /= 2;
+ side = 1;
+ } else {
+ // f_low * fr looks like zero, so assume we've converged.
+ break;
+ }
+ }
+
+ // Want to know if the max number of iterations is ever exceeded so
+ // we can understand why that happened.
+ DCHECK_LT(iteration, kMaxIterations);
+
+ return root;
+}
+
+double Biquad::TailFrame(int coef_index, double max_frame) {
+ // The Biquad filter is given by
+ //
+ // H(z) = (b0 + b1/z + b2/z^2)/(1 + a1/z + a2/z^2).
+ //
+ // To compute the tail time, compute the impulse response, h(n), of
+ // H(z), which we can do analytically. From this impulse response,
+ // find the value n0 where |h(n)| <= eps for n >= n0.
+ //
+ // Assume first that the two poles of H(z) are not repeated, say r1
+ // and r2. Then, we can compute a partial fraction expansion of
+ // H(z):
+ //
+ // H(z) = (b0+b1/z+b2/z^2)/[(1-r1/z)*(1-r2/z)]
+ // = b0 + C2/(z-r2) - C1/(z-r1)
+ //
+ // where
+ // C2 = (b0*r2^2+b1*r2+b2)/(r2-r1)
+ // C1 = (b0*r1^2+b1*r1+b2)/(r2-r1)
+ //
+ // Expand H(z) then this in powers of 1/z gives:
+ //
+ // H(z) = b0 -(C2/r2+C1/r1) + sum(C2*r2^(i-1)/z^i + C1*r1^(i-1)/z^i)
+ //
+ // Thus, for n > 1 (we don't care about small n),
+ //
+ // h(n) = C2*r2^(n-1) + C1*r1^(n-1)
+ //
+ // We need to find n0 such that |h(n)| < eps for n > n0.
+ //
+ // Case 1: r1 and r2 are real and distinct, with |r1|>=|r2|.
+ //
+ // Then
+ //
+ // h(n) = C1*r1^(n-1)*(1 + C2/C1*(r2/r1)^(n-1))
+ //
+ // so
+ //
+ // |h(n)| = |C1|*|r1|^(n-1)*|1+C2/C1*(r2/r1)^(n-1)|
+ // <= |C1|*|r1|^(n-1)*[1 + |C2/C1|*|r2/r1|^(n-1)]
+ // <= |C1|*|r1|^(n-1)*[1 + |C2/C1|]
+ //
+ // by using the triangle inequality and the fact that |r2|<=|r1|.
+ // And we want |h(n)|<=eps which is true if
+ //
+ // |C1|*|r1|^(n-1)*[1 + |C2/C1|] <= eps
+ //
+ // or
+ //
+ // n >= 1 + log(eps/C)/log(|r1|)
+ //
+ // where C = |C1|*[1+|C2/C1|] = |C1| + |C2|.
+ //
+ // Case 2: r1 and r2 are complex
+ //
+ // Thne we can write r1=r*exp(i*p) and r2=r*exp(-i*p). So,
+ //
+ // |h(n)| = |C2*r^(n-1)*exp(-i*p*(n-1)) + C1*r^(n-1)*exp(i*p*(n-1))|
+ // = |C1|*r^(n-1)*|1 + C2/C1*exp(-i*p*(n-1))/exp(i*n*(n-1))|
+ // <= |C1|*r^(n-1)*[1 + |C2/C1|]
+ //
+ // Again, this is easily solved to give
+ //
+ // n >= 1 + log(eps/C)/log(r)
+ //
+ // where C = |C1|*[1+|C2/C1|] = |C1| + |C2|.
+ //
+ // Case 3: Repeated roots, r1=r2=r.
+ //
+ // In this case,
+ //
+ // H(z) = (b0+b1/z+b2/z^2)/[(1-r/z)^2
+ //
+ // Expanding this in powers of 1/z gives:
+ //
+ // H(z) = C1*sum((i+1)*r^i/z^i) - C2 * sum(r^(i-2)/z^i) + b2/r^2
+ // = b2/r^2 + sum([C1*(i+1)*r^i + C2*r^(i-2)]/z^i)
+ // where
+ // C1 = (b0*r^2+b1*r+b2)/r^2
+ // C2 = b1*r+2*b2
+ //
+ // Thus, the impulse response is
+ //
+ // h(n) = C1*(n+1)*r^n + C2*r^(n-2)
+ // = r^(n-2)*[C1*(n+1)*r^2+C2]
+ //
+ // So
+ //
+ // |h(n)| = |r|^(n-2)*|C1*(n+1)*r^2+C2|
+ //
+ // To find n such that |h(n)| < eps, we need a numerical method in
+ // general, so there's no real reason to simplify this or use other
+ // approximations. Just solve |h(n)|=eps directly.
+ //
+ // Thus, for an set of filter coefficients, we can compute the tail
+ // time.
+ //
+
+ // If the maximum amplitude of the impulse response is less than
+ // this, we assume that we've reached the tail of the response.
+ // Currently, this means that the impulse is less than 1 bit of a
+ // 16-bit PCM value.
+ const double kMaxTailAmplitude = 1 / 32768.0;
+
+ // Find the roots of 1+a1/z+a2/z^2 = 0. Or equivalently,
+ // z^2+a1*z+a2 = 0. From the quadratic formula the roots are
+ // (-a1+/-sqrt(a1^2-4*a2))/2.
+
+ double a1 = a1_[coef_index];
+ double a2 = a2_[coef_index];
+ double b0 = b0_[coef_index];
+ double b1 = b1_[coef_index];
+ double b2 = b2_[coef_index];
+
+ double tail_frame = 0;
+ double discrim = a1 * a1 - 4 * a2;
+
+ if (discrim > 0) {
+ // Compute the real roots so that r1 has the largest magnitude.
+ double rplus = (-a1 + sqrt(discrim)) / 2;
+ double rminus = (-a1 - sqrt(discrim)) / 2;
+ double r1 = fabs(rplus) >= fabs(rminus) ? rplus : rminus;
+ // Use the fact that a2 = r1*r2
+ double r2 = a2 / r1;
+
+ double c1 = (b0 * r1 * r1 + b1 * r1 + b2) / (r2 - r1);
+ double c2 = (b0 * r2 * r2 + b1 * r2 + b2) / (r2 - r1);
+
+ DCHECK(std::isfinite(r1));
+ DCHECK(std::isfinite(r2));
+ DCHECK(std::isfinite(c1));
+ DCHECK(std::isfinite(c2));
+
+ // It's possible for kMaxTailAmplitude to be greater than c1 + c2.
+ // This may produce a negative tail frame. Just clamp the tail
+ // frame to 0.
+ tail_frame = clampTo(
+ 1 + log(kMaxTailAmplitude / (fabs(c1) + fabs(c2))) / log(fabs(r1)), 0);
+
+ DCHECK(std::isfinite(tail_frame));
+ } else if (discrim < 0) {
+ // Two complex roots.
+ // One root is -a1/2 + i*sqrt(-discrim)/2.
+ double x = -a1 / 2;
+ double y = sqrt(-discrim) / 2;
+ std::complex<double> r1(x, y);
+ std::complex<double> r2(x, -y);
+ double r = hypot(x, y);
+
+ DCHECK(std::isfinite(r));
+
+ // It's possible for r to be 1. (LPF with Q very large can cause this.)
+ if (r == 1) {
+ tail_frame = max_frame;
+ } else {
+ double c1 = abs((b0 * r1 * r1 + b1 * r1 + b2) / (r2 - r1));
+ double c2 = abs((b0 * r2 * r2 + b1 * r2 + b2) / (r2 - r1));
+
+ DCHECK(std::isfinite(c1));
+ DCHECK(std::isfinite(c2));
+
+ tail_frame = 1 + log(kMaxTailAmplitude / (c1 + c2)) / log(r);
+ if (c1 == 0 && c2 == 0) {
+ // If c1 = c2 = 0, then H(z) = b0. Hence, there's no tail
+ // because this is just a wire from input to output.
+ tail_frame = 0;
+ } else {
+ // Otherwise, check that the tail has finite length. Not
+ // strictly necessary, but we want to know if this ever
+ // happens.
+ DCHECK(std::isfinite(tail_frame));
+ }
+ }
+ } else {
+ // Repeated roots. This should be pretty rare because all the
+ // coefficients need to be just the right values to get a
+ // discriminant of exactly zero.
+ double r = -a1 / 2;
+
+ if (r == 0) {
+ // Double pole at 0. This just delays the signal by 2 frames,
+ // so set the tail frame to 2.
+ tail_frame = 2;
+ } else {
+ double c1 = (b0 * r * r + b1 * r + b2) / (r * r);
+ double c2 = b1 * r + 2 * b2;
+
+ DCHECK(std::isfinite(c1));
+ DCHECK(std::isfinite(c2));
+
+ // It can happen that c1=c2=0. This basically means that H(z) =
+ // constant, which is the limiting case for several of the
+ // biquad filters.
+ if (c1 == 0 && c2 == 0) {
+ tail_frame = 0;
+ } else {
+ // The function c*(n+1)*r^n is not monotonic, but it's easy to
+ // find the max point since the derivative is
+ // c*r^n*(1+(n+1)*log(r)). This has a root at
+ // -(1+log(r))/log(r). so we can start our search from that
+ // point to max_frames.
+
+ double low = clampTo(-(1 + log(r)) / log(r), 1.0,
+ static_cast<double>(max_frame - 1));
+ double high = max_frame;
+
+ DCHECK(std::isfinite(low));
+ DCHECK(std::isfinite(high));
+
+ tail_frame = RootFinder(low, high, log(kMaxTailAmplitude), c1, c2, r);
+ }
+ }
+ }
+
+ return tail_frame;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/biquad.h b/chromium/third_party/blink/renderer/platform/audio/biquad.h
new file mode 100644
index 00000000000..6b6238040ee
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/biquad.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_BIQUAD_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_BIQUAD_H_
+
+#include <sys/types.h>
+
+#include <complex>
+
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/audio/audio_array.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+// A basic biquad (two-zero / two-pole digital filter)
+//
+// It can be configured to a number of common and very useful filters:
+// lowpass, highpass, shelving, parameteric, notch, allpass, ...
+
+class PLATFORM_EXPORT Biquad final {
+ DISALLOW_NEW();
+
+ public:
+ Biquad();
+ ~Biquad();
+
+ void Process(const float* source_p, float* dest_p, size_t frames_to_process);
+
+ bool HasSampleAccurateValues() const { return has_sample_accurate_values_; }
+ void SetHasSampleAccurateValues(bool is_sample_accurate) {
+ has_sample_accurate_values_ = is_sample_accurate;
+ }
+
+ // frequency is 0 - 1 normalized, resonance and dbGain are in decibels.
+ // Q is a unitless quality factor.
+ void SetLowpassParams(int, double frequency, double resonance);
+ void SetHighpassParams(int, double frequency, double resonance);
+ void SetBandpassParams(int, double frequency, double q);
+ void SetLowShelfParams(int, double frequency, double db_gain);
+ void SetHighShelfParams(int, double frequency, double db_gain);
+ void SetPeakingParams(int, double frequency, double q, double db_gain);
+ void SetAllpassParams(int, double frequency, double q);
+ void SetNotchParams(int, double frequency, double q);
+
+ // Resets filter state
+ void Reset();
+
+ // Compute tail frame based on the filter coefficents at index
+ // |coef_index|. The tail frame is the frame number where the
+ // impulse response of the filter falls below a threshold value.
+ // The maximum allowed frame value is given by |max_frame|. This
+ // limits how much work is done in computing the frame numer.
+ double TailFrame(int coef_index, double max_frame);
+
+ // Filter response at a set of n frequencies. The magnitude and
+ // phase response are returned in magResponse and phaseResponse.
+ // The phase response is in radians.
+ void GetFrequencyResponse(int n_frequencies,
+ const float* frequency,
+ float* mag_response,
+ float* phase_response);
+
+ private:
+ void SetNormalizedCoefficients(int,
+ double b0,
+ double b1,
+ double b2,
+ double a0,
+ double a1,
+ double a2);
+
+ // If true, the filter coefficients are (possibly) time-varying due to a
+ // timeline automation on at least one filter parameter.
+ bool has_sample_accurate_values_;
+
+ // Filter coefficients. The filter is defined as
+ //
+ // y[n] + m_a1*y[n-1] + m_a2*y[n-2] = m_b0*x[n] + m_b1*x[n-1] + m_b2*x[n-2].
+ AudioDoubleArray b0_;
+ AudioDoubleArray b1_;
+ AudioDoubleArray b2_;
+ AudioDoubleArray a1_;
+ AudioDoubleArray a2_;
+
+#if defined(OS_MACOSX)
+ void ProcessFast(const float* source_p,
+ float* dest_p,
+ size_t frames_to_process);
+ void ProcessSliceFast(double* source_p,
+ double* dest_p,
+ double* coefficients_p,
+ size_t frames_to_process);
+
+ AudioDoubleArray input_buffer_;
+ AudioDoubleArray output_buffer_;
+
+#endif
+ // Filter memory
+ double x1_; // input delayed by 1 sample
+ double x2_; // input delayed by 2 samples
+ double y1_; // output delayed by 1 sample
+ double y2_; // output delayed by 2 samples
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_BIQUAD_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/cone.cc b/chromium/third_party/blink/renderer/platform/audio/cone.cc
new file mode 100644
index 00000000000..dc558870a71
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/cone.cc
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/audio/cone.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+namespace blink {
+
+ConeEffect::ConeEffect()
+ : inner_angle_(360.0), outer_angle_(360.0), outer_gain_(0.0) {}
+
+double ConeEffect::Gain(FloatPoint3D source_position,
+ FloatPoint3D source_orientation,
+ FloatPoint3D listener_position) {
+ if (source_orientation.IsZero() ||
+ ((inner_angle_ == 360.0) && (outer_angle_ == 360.0)))
+ return 1.0; // no cone specified - unity gain
+
+ // Source-listener vector
+ FloatPoint3D source_to_listener = listener_position - source_position;
+
+ // Angle between the source orientation vector and the source-listener vector
+ double angle = rad2deg(source_to_listener.AngleBetween(source_orientation));
+ double abs_angle = fabs(angle);
+
+ // Divide by 2.0 here since API is entire angle (not half-angle)
+ double abs_inner_angle = fabs(inner_angle_) / 2.0;
+ double abs_outer_angle = fabs(outer_angle_) / 2.0;
+ double gain = 1.0;
+
+ if (abs_angle <= abs_inner_angle)
+ // No attenuation
+ gain = 1.0;
+ else if (abs_angle >= abs_outer_angle)
+ // Max attenuation
+ gain = outer_gain_;
+ else {
+ // Between inner and outer cones
+ // inner -> outer, x goes from 0 -> 1
+ double x =
+ (abs_angle - abs_inner_angle) / (abs_outer_angle - abs_inner_angle);
+ gain = (1.0 - x) + outer_gain_ * x;
+ }
+
+ return gain;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/cone.h b/chromium/third_party/blink/renderer/platform/audio/cone.h
new file mode 100644
index 00000000000..beaf5673790
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/cone.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_CONE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_CONE_H_
+
+#include "third_party/blink/renderer/platform/geometry/float_point_3d.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+// Cone gain is defined according to the OpenAL specification
+
+class PLATFORM_EXPORT ConeEffect {
+ DISALLOW_NEW();
+
+ public:
+ ConeEffect();
+
+ // Returns scalar gain for the given source/listener positions/orientations
+ double Gain(FloatPoint3D source_position,
+ FloatPoint3D source_orientation,
+ FloatPoint3D listener_position);
+
+ // Angles in degrees
+ void SetInnerAngle(double inner_angle) { inner_angle_ = inner_angle; }
+ double InnerAngle() const { return inner_angle_; }
+
+ void SetOuterAngle(double outer_angle) { outer_angle_ = outer_angle; }
+ double OuterAngle() const { return outer_angle_; }
+
+ void SetOuterGain(double outer_gain) { outer_gain_ = outer_gain; }
+ double OuterGain() const { return outer_gain_; }
+
+ protected:
+ double inner_angle_;
+ double outer_angle_;
+ double outer_gain_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_CONE_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/cpu/arm/vector_math_neon.h b/chromium/third_party/blink/renderer/platform/audio/cpu/arm/vector_math_neon.h
new file mode 100644
index 00000000000..f985fbd4b06
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/cpu/arm/vector_math_neon.h
@@ -0,0 +1,253 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_CPU_ARM_VECTOR_MATH_NEON_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_CPU_ARM_VECTOR_MATH_NEON_H_
+
+#include <arm_neon.h>
+
+#include <algorithm>
+
+#include "third_party/blink/renderer/platform/audio/vector_math_scalar.h"
+
+namespace blink {
+namespace VectorMath {
+namespace NEON {
+
+// TODO: Consider optimizing this.
+using Scalar::Conv;
+
+static ALWAYS_INLINE void Vadd(const float* source1p,
+ int source_stride1,
+ const float* source2p,
+ int source_stride2,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process) {
+ int n = frames_to_process;
+
+ if (source_stride1 == 1 && source_stride2 == 1 && dest_stride == 1) {
+ int tail_frames = n % 4;
+ const float* end_p = dest_p + n - tail_frames;
+
+ while (dest_p < end_p) {
+ float32x4_t source1 = vld1q_f32(source1p);
+ float32x4_t source2 = vld1q_f32(source2p);
+ vst1q_f32(dest_p, vaddq_f32(source1, source2));
+
+ source1p += 4;
+ source2p += 4;
+ dest_p += 4;
+ }
+ n = tail_frames;
+ }
+
+ Scalar::Vadd(source1p, source_stride1, source2p, source_stride2, dest_p,
+ dest_stride, n);
+}
+
+static ALWAYS_INLINE void Vclip(const float* source_p,
+ int source_stride,
+ const float* low_threshold_p,
+ const float* high_threshold_p,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process) {
+ int n = frames_to_process;
+
+ if (source_stride == 1 && dest_stride == 1) {
+ int tail_frames = n % 4;
+ const float* end_p = dest_p + n - tail_frames;
+
+ float32x4_t low = vdupq_n_f32(*low_threshold_p);
+ float32x4_t high = vdupq_n_f32(*high_threshold_p);
+ while (dest_p < end_p) {
+ float32x4_t source = vld1q_f32(source_p);
+ vst1q_f32(dest_p, vmaxq_f32(vminq_f32(source, high), low));
+ source_p += 4;
+ dest_p += 4;
+ }
+ n = tail_frames;
+ }
+
+ Scalar::Vclip(source_p, source_stride, low_threshold_p, high_threshold_p,
+ dest_p, dest_stride, n);
+}
+
+static ALWAYS_INLINE void Vmaxmgv(const float* source_p,
+ int source_stride,
+ float* max_p,
+ size_t frames_to_process) {
+ int n = frames_to_process;
+
+ if (source_stride == 1) {
+ int tail_frames = n % 4;
+ const float* end_p = source_p + n - tail_frames;
+
+ float32x4_t four_max = vdupq_n_f32(*max_p);
+ while (source_p < end_p) {
+ float32x4_t source = vld1q_f32(source_p);
+ four_max = vmaxq_f32(four_max, vabsq_f32(source));
+ source_p += 4;
+ }
+ float32x2_t two_max =
+ vmax_f32(vget_low_f32(four_max), vget_high_f32(four_max));
+
+ float group_max[2];
+ vst1_f32(group_max, two_max);
+ *max_p = std::max(group_max[0], group_max[1]);
+
+ n = tail_frames;
+ }
+
+ Scalar::Vmaxmgv(source_p, source_stride, max_p, n);
+}
+
+static ALWAYS_INLINE void Vmul(const float* source1p,
+ int source_stride1,
+ const float* source2p,
+ int source_stride2,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process) {
+ int n = frames_to_process;
+
+ if (source_stride1 == 1 && source_stride2 == 1 && dest_stride == 1) {
+ int tail_frames = n % 4;
+ const float* end_p = dest_p + n - tail_frames;
+
+ while (dest_p < end_p) {
+ float32x4_t source1 = vld1q_f32(source1p);
+ float32x4_t source2 = vld1q_f32(source2p);
+ vst1q_f32(dest_p, vmulq_f32(source1, source2));
+
+ source1p += 4;
+ source2p += 4;
+ dest_p += 4;
+ }
+ n = tail_frames;
+ }
+
+ Scalar::Vmul(source1p, source_stride1, source2p, source_stride2, dest_p,
+ dest_stride, n);
+}
+
+static ALWAYS_INLINE void Vsma(const float* source_p,
+ int source_stride,
+ const float* scale,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process) {
+ int n = frames_to_process;
+
+ if (source_stride == 1 && dest_stride == 1) {
+ int tail_frames = n % 4;
+ const float* end_p = dest_p + n - tail_frames;
+
+ float32x4_t k = vdupq_n_f32(*scale);
+ while (dest_p < end_p) {
+ float32x4_t source = vld1q_f32(source_p);
+ float32x4_t dest = vld1q_f32(dest_p);
+
+ dest = vmlaq_f32(dest, source, k);
+ vst1q_f32(dest_p, dest);
+
+ source_p += 4;
+ dest_p += 4;
+ }
+ n = tail_frames;
+ }
+
+ Scalar::Vsma(source_p, source_stride, scale, dest_p, dest_stride, n);
+}
+
+static ALWAYS_INLINE void Vsmul(const float* source_p,
+ int source_stride,
+ const float* scale,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process) {
+ int n = frames_to_process;
+
+ if (source_stride == 1 && dest_stride == 1) {
+ float k = *scale;
+ int tail_frames = n % 4;
+ const float* end_p = dest_p + n - tail_frames;
+
+ while (dest_p < end_p) {
+ float32x4_t source = vld1q_f32(source_p);
+ vst1q_f32(dest_p, vmulq_n_f32(source, k));
+
+ source_p += 4;
+ dest_p += 4;
+ }
+ n = tail_frames;
+ }
+
+ Scalar::Vsmul(source_p, source_stride, scale, dest_p, dest_stride, n);
+}
+
+static ALWAYS_INLINE void Vsvesq(const float* source_p,
+ int source_stride,
+ float* sum_p,
+ size_t frames_to_process) {
+ int n = frames_to_process;
+
+ if (source_stride == 1) {
+ int tail_frames = n % 4;
+ const float* end_p = source_p + n - tail_frames;
+
+ float32x4_t four_sum = vdupq_n_f32(0);
+ while (source_p < end_p) {
+ float32x4_t source = vld1q_f32(source_p);
+ four_sum = vmlaq_f32(four_sum, source, source);
+ source_p += 4;
+ }
+ float32x2_t two_sum =
+ vadd_f32(vget_low_f32(four_sum), vget_high_f32(four_sum));
+
+ float group_sum[2];
+ vst1_f32(group_sum, two_sum);
+ *sum_p += group_sum[0] + group_sum[1];
+
+ n = tail_frames;
+ }
+
+ Scalar::Vsvesq(source_p, source_stride, sum_p, n);
+}
+
+static ALWAYS_INLINE void Zvmul(const float* real1p,
+ const float* imag1p,
+ const float* real2p,
+ const float* imag2p,
+ float* real_dest_p,
+ float* imag_dest_p,
+ size_t frames_to_process) {
+ unsigned i = 0;
+
+ unsigned end_size = frames_to_process - frames_to_process % 4;
+ while (i < end_size) {
+ float32x4_t real1 = vld1q_f32(real1p + i);
+ float32x4_t real2 = vld1q_f32(real2p + i);
+ float32x4_t imag1 = vld1q_f32(imag1p + i);
+ float32x4_t imag2 = vld1q_f32(imag2p + i);
+
+ float32x4_t real_result = vmlsq_f32(vmulq_f32(real1, real2), imag1, imag2);
+ float32x4_t imag_result = vmlaq_f32(vmulq_f32(real1, imag2), imag1, real2);
+
+ vst1q_f32(real_dest_p + i, real_result);
+ vst1q_f32(imag_dest_p + i, imag_result);
+
+ i += 4;
+ }
+
+ Scalar::Zvmul(real1p + i, imag1p + i, real2p + i, imag2p + i, real_dest_p + i,
+ imag_dest_p + i, frames_to_process - i);
+}
+
+} // namespace NEON
+} // namespace VectorMath
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_CPU_ARM_VECTOR_MATH_NEON_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/cpu/mips/vector_math_msa.h b/chromium/third_party/blink/renderer/platform/audio/cpu/mips/vector_math_msa.h
new file mode 100644
index 00000000000..90263606ee5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/cpu/mips/vector_math_msa.h
@@ -0,0 +1,219 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_CPU_MIPS_VECTOR_MATH_MSA_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_CPU_MIPS_VECTOR_MATH_MSA_H_
+
+#include <algorithm>
+
+#include "third_party/blink/renderer/platform/audio/vector_math_scalar.h"
+#include "third_party/blink/renderer/platform/cpu/mips/common_macros_msa.h"
+
+namespace blink {
+namespace VectorMath {
+namespace MSA {
+
+// TODO: Consider optimizing these.
+using Scalar::Conv;
+using Scalar::Vsvesq;
+using Scalar::Zvmul;
+
+static ALWAYS_INLINE void Vadd(const float* source1p,
+ int source_stride1,
+ const float* source2p,
+ int source_stride2,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process) {
+ int n = frames_to_process;
+
+ if (source_stride1 == 1 && source_stride2 == 1 && dest_stride == 1) {
+ v4f32 vSrc1P0, vSrc1P1, vSrc1P2, vSrc1P3, vSrc1P4, vSrc1P5, vSrc1P6,
+ vSrc1P7;
+ v4f32 vSrc2P0, vSrc2P1, vSrc2P2, vSrc2P3, vSrc2P4, vSrc2P5, vSrc2P6,
+ vSrc2P7;
+ v4f32 vDst0, vDst1, vDst2, vDst3, vDst4, vDst5, vDst6, vDst7;
+
+ for (; n >= 32; n -= 32) {
+ LD_SP8(source1p, 4, vSrc1P0, vSrc1P1, vSrc1P2, vSrc1P3, vSrc1P4, vSrc1P5,
+ vSrc1P6, vSrc1P7);
+ LD_SP8(source2p, 4, vSrc2P0, vSrc2P1, vSrc2P2, vSrc2P3, vSrc2P4, vSrc2P5,
+ vSrc2P6, vSrc2P7);
+ ADD4(vSrc1P0, vSrc2P0, vSrc1P1, vSrc2P1, vSrc1P2, vSrc2P2, vSrc1P3,
+ vSrc2P3, vDst0, vDst1, vDst2, vDst3);
+ ADD4(vSrc1P4, vSrc2P4, vSrc1P5, vSrc2P5, vSrc1P6, vSrc2P6, vSrc1P7,
+ vSrc2P7, vDst4, vDst5, vDst6, vDst7);
+ ST_SP8(vDst0, vDst1, vDst2, vDst3, vDst4, vDst5, vDst6, vDst7, dest_p, 4);
+ }
+ }
+
+ Scalar::Vadd(source1p, source_stride1, source2p, source_stride2, dest_p,
+ dest_stride, n);
+}
+
+static ALWAYS_INLINE void Vclip(const float* source_p,
+ int source_stride,
+ const float* low_threshold_p,
+ const float* high_threshold_p,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process) {
+ int n = frames_to_process;
+
+ if (source_stride == 1 && dest_stride == 1) {
+ v4f32 vSrc0, vSrc1, vSrc2, vSrc3, vSrc4, vSrc5, vSrc6, vSrc7;
+ v4f32 vDst0, vDst1, vDst2, vDst3, vDst4, vDst5, vDst6, vDst7;
+ v4f32 vLowThr, vHighThr;
+ FloatInt lowThr, highThr;
+
+ lowThr.floatVal = *low_threshold_p;
+ highThr.floatVal = *high_threshold_p;
+ vLowThr = (v4f32)__msa_fill_w(lowThr.intVal);
+ vHighThr = (v4f32)__msa_fill_w(highThr.intVal);
+
+ for (; n >= 32; n -= 32) {
+ LD_SP8(source_p, 4, vSrc0, vSrc1, vSrc2, vSrc3, vSrc4, vSrc5, vSrc6,
+ vSrc7);
+ VCLIP4(vSrc0, vSrc1, vSrc2, vSrc3, vLowThr, vHighThr, vDst0, vDst1, vDst2,
+ vDst3);
+ VCLIP4(vSrc4, vSrc5, vSrc6, vSrc7, vLowThr, vHighThr, vDst4, vDst5, vDst6,
+ vDst7);
+ ST_SP8(vDst0, vDst1, vDst2, vDst3, vDst4, vDst5, vDst6, vDst7, dest_p, 4);
+ }
+ }
+
+ Scalar::Vclip(source_p, source_stride, low_threshold_p, high_threshold_p,
+ dest_p, dest_stride, n);
+}
+
+static ALWAYS_INLINE void Vmaxmgv(const float* source_p,
+ int source_stride,
+ float* max_p,
+ size_t frames_to_process) {
+ int n = frames_to_process;
+
+ if (source_stride == 1) {
+ v4f32 vMax = {
+ 0,
+ };
+ v4f32 vSrc0, vSrc1, vSrc2, vSrc3, vSrc4, vSrc5, vSrc6, vSrc7;
+ const v16i8 vSignBitMask = (v16i8)__msa_fill_w(0x7FFFFFFF);
+
+ for (; n >= 32; n -= 32) {
+ LD_SP8(source_p, 4, vSrc0, vSrc1, vSrc2, vSrc3, vSrc4, vSrc5, vSrc6,
+ vSrc7);
+ AND_W4_SP(vSrc0, vSrc1, vSrc2, vSrc3, vSignBitMask);
+ VMAX_W4_SP(vSrc0, vSrc1, vSrc2, vSrc3, vMax);
+ AND_W4_SP(vSrc4, vSrc5, vSrc6, vSrc7, vSignBitMask);
+ VMAX_W4_SP(vSrc4, vSrc5, vSrc6, vSrc7, vMax);
+ }
+
+ *max_p = std::max(*max_p, vMax[0]);
+ *max_p = std::max(*max_p, vMax[1]);
+ *max_p = std::max(*max_p, vMax[2]);
+ *max_p = std::max(*max_p, vMax[3]);
+ }
+
+ Scalar::Vmaxmgv(source_p, source_stride, max_p, n);
+}
+
+static ALWAYS_INLINE void Vmul(const float* source1p,
+ int source_stride1,
+ const float* source2p,
+ int source_stride2,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process) {
+ int n = frames_to_process;
+
+ if (source_stride1 == 1 && source_stride2 == 1 && dest_stride == 1) {
+ v4f32 vSrc1P0, vSrc1P1, vSrc1P2, vSrc1P3, vSrc1P4, vSrc1P5, vSrc1P6,
+ vSrc1P7;
+ v4f32 vSrc2P0, vSrc2P1, vSrc2P2, vSrc2P3, vSrc2P4, vSrc2P5, vSrc2P6,
+ vSrc2P7;
+ v4f32 vDst0, vDst1, vDst2, vDst3, vDst4, vDst5, vDst6, vDst7;
+
+ for (; n >= 32; n -= 32) {
+ LD_SP8(source1p, 4, vSrc1P0, vSrc1P1, vSrc1P2, vSrc1P3, vSrc1P4, vSrc1P5,
+ vSrc1P6, vSrc1P7);
+ LD_SP8(source2p, 4, vSrc2P0, vSrc2P1, vSrc2P2, vSrc2P3, vSrc2P4, vSrc2P5,
+ vSrc2P6, vSrc2P7);
+ MUL4(vSrc1P0, vSrc2P0, vSrc1P1, vSrc2P1, vSrc1P2, vSrc2P2, vSrc1P3,
+ vSrc2P3, vDst0, vDst1, vDst2, vDst3);
+ MUL4(vSrc1P4, vSrc2P4, vSrc1P5, vSrc2P5, vSrc1P6, vSrc2P6, vSrc1P7,
+ vSrc2P7, vDst4, vDst5, vDst6, vDst7);
+ ST_SP8(vDst0, vDst1, vDst2, vDst3, vDst4, vDst5, vDst6, vDst7, dest_p, 4);
+ }
+ }
+
+ Scalar::Vmul(source1p, source_stride1, source2p, source_stride2, dest_p,
+ dest_stride, n);
+}
+
+static ALWAYS_INLINE void Vsma(const float* source_p,
+ int source_stride,
+ const float* scale,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process) {
+ int n = frames_to_process;
+
+ if (source_stride == 1 && dest_stride == 1) {
+ float* destPCopy = dest_p;
+ v4f32 vScale;
+ v4f32 vSrc0, vSrc1, vSrc2, vSrc3, vSrc4, vSrc5, vSrc6, vSrc7;
+ v4f32 vDst0, vDst1, vDst2, vDst3, vDst4, vDst5, vDst6, vDst7;
+ FloatInt scaleVal;
+
+ scaleVal.floatVal = *scale;
+ vScale = (v4f32)__msa_fill_w(scaleVal.intVal);
+
+ for (; n >= 32; n -= 32) {
+ LD_SP8(source_p, 4, vSrc0, vSrc1, vSrc2, vSrc3, vSrc4, vSrc5, vSrc6,
+ vSrc7);
+ LD_SP8(destPCopy, 4, vDst0, vDst1, vDst2, vDst3, vDst4, vDst5, vDst6,
+ vDst7);
+ VSMA4(vSrc0, vSrc1, vSrc2, vSrc3, vDst0, vDst1, vDst2, vDst3, vScale);
+ VSMA4(vSrc4, vSrc5, vSrc6, vSrc7, vDst4, vDst5, vDst6, vDst7, vScale);
+ ST_SP8(vDst0, vDst1, vDst2, vDst3, vDst4, vDst5, vDst6, vDst7, dest_p, 4);
+ }
+ }
+
+ Scalar::Vsma(source_p, source_stride, scale, dest_p, dest_stride, n);
+}
+
+static ALWAYS_INLINE void Vsmul(const float* source_p,
+ int source_stride,
+ const float* scale,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process) {
+ int n = frames_to_process;
+
+ if (source_stride == 1 && dest_stride == 1) {
+ v4f32 vScale;
+ v4f32 vSrc0, vSrc1, vSrc2, vSrc3, vSrc4, vSrc5, vSrc6, vSrc7;
+ v4f32 vDst0, vDst1, vDst2, vDst3, vDst4, vDst5, vDst6, vDst7;
+ FloatInt scaleVal;
+
+ scaleVal.floatVal = *scale;
+ vScale = (v4f32)__msa_fill_w(scaleVal.intVal);
+
+ for (; n >= 32; n -= 32) {
+ LD_SP8(source_p, 4, vSrc0, vSrc1, vSrc2, vSrc3, vSrc4, vSrc5, vSrc6,
+ vSrc7);
+ VSMUL4(vSrc0, vSrc1, vSrc2, vSrc3, vDst0, vDst1, vDst2, vDst3, vScale);
+ VSMUL4(vSrc4, vSrc5, vSrc6, vSrc7, vDst4, vDst5, vDst6, vDst7, vScale);
+ ST_SP8(vDst0, vDst1, vDst2, vDst3, vDst4, vDst5, vDst6, vDst7, dest_p, 4);
+ }
+ }
+
+ Scalar::Vsmul(source_p, source_stride, scale, dest_p, dest_stride, n);
+}
+
+} // namespace MSA
+} // namespace VectorMath
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_CPU_MIPS_VECTOR_MATH_MSA_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/cpu/x86/vector_math_avx.cc b/chromium/third_party/blink/renderer/platform/audio/cpu/x86/vector_math_avx.cc
new file mode 100644
index 00000000000..ade6f22898f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/cpu/x86/vector_math_avx.cc
@@ -0,0 +1,31 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "build/build_config.h"
+
+#if defined(ARCH_CPU_X86_FAMILY) && !defined(OS_MACOSX)
+
+#include "third_party/blink/renderer/platform/audio/cpu/x86/vector_math_avx.h"
+
+#include <immintrin.h>
+
+namespace blink {
+namespace VectorMath {
+namespace AVX {
+
+using MType = __m256;
+
+} // namespace AVX
+} // namespace VectorMath
+} // namespace blink
+
+#define MM_PS(name) _mm256_##name##_ps
+#define VECTOR_MATH_SIMD_NAMESPACE_NAME AVX
+
+#include "third_party/blink/renderer/platform/audio/cpu/x86/vector_math_impl.h"
+
+#undef MM_PS
+#undef VECTOR_MATH_SIMD_NAMESPACE_NAME
+
+#endif // defined(ARCH_CPU_X86_FAMILY) && !defined(OS_MACOSX)
diff --git a/chromium/third_party/blink/renderer/platform/audio/cpu/x86/vector_math_avx.h b/chromium/third_party/blink/renderer/platform/audio/cpu/x86/vector_math_avx.h
new file mode 100644
index 00000000000..488df04affe
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/cpu/x86/vector_math_avx.h
@@ -0,0 +1,90 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_CPU_X86_VECTOR_MATH_AVX_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_CPU_X86_VECTOR_MATH_AVX_H_
+
+#include <cstddef>
+
+#include "third_party/blink/renderer/platform/audio/audio_array.h"
+
+namespace blink {
+namespace VectorMath {
+namespace AVX {
+
+constexpr size_t kBitsPerRegister = 256u;
+constexpr size_t kPackedFloatsPerRegister = kBitsPerRegister / 32u;
+constexpr size_t kFramesToProcessMask = ~(kPackedFloatsPerRegister - 1u);
+
+bool IsAligned(const float*);
+
+// Direct vector convolution:
+// dest[k] = sum(source[k+m]*filter[m*filter_stride]) for all m
+// provided that |prepared_filter_p| is |prepared_filter->Data()| and that
+// |prepared_filter| is prepared with |PrepareFilterForConv|.
+void Conv(const float* source_p,
+ const float* prepared_filter_p,
+ float* dest_p,
+ size_t frames_to_process,
+ size_t filter_size);
+
+void PrepareFilterForConv(const float* filter_p,
+ int filter_stride,
+ size_t filter_size,
+ AudioFloatArray* prepared_filter);
+
+// dest[k] = source1[k] + source2[k]
+void Vadd(const float* source1p,
+ const float* source2p,
+ float* dest_p,
+ size_t frames_to_process);
+
+// dest[k] = clip(source[k], low_threshold, high_threshold)
+// = max(low_threshold, min(high_threshold, source[k]))
+void Vclip(const float* source_p,
+ const float* low_threshold_p,
+ const float* high_threshold_p,
+ float* dest_p,
+ size_t frames_to_process);
+
+// *max_p = max(*max_p, source_max) where
+// source_max = max(abs(source[k])) for all k
+void Vmaxmgv(const float* source_p, float* max_p, size_t frames_to_process);
+
+// dest[k] = source1[k] * source2[k]
+void Vmul(const float* source1p,
+ const float* source2p,
+ float* dest_p,
+ size_t frames_to_process);
+
+// dest[k] += scale * source[k]
+void Vsma(const float* source_p,
+ const float* scale,
+ float* dest_p,
+ size_t frames_to_process);
+
+// dest[k] = scale * source[k]
+void Vsmul(const float* source_p,
+ const float* scale,
+ float* dest_p,
+ size_t frames_to_process);
+
+// sum += sum(source[k]^2) for all k
+void Vsvesq(const float* source_p, float* sum_p, size_t frames_to_process);
+
+// real_dest[k] = real1[k] * real2[k] - imag1[k] * imag2[k]
+// imag_dest[k] = real1[k] * imag2[k] + imag1[k] * real2[k]
+void Zvmul(const float* real1p,
+ const float* imag1p,
+ const float* real2p,
+ const float* imag2p,
+ float* real_dest_p,
+ float* imag_dest_p,
+ size_t frames_to_process);
+
+} // namespace AVX
+} // namespace VectorMath
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_CPU_X86_VECTOR_MATH_AVX_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/cpu/x86/vector_math_impl.h b/chromium/third_party/blink/renderer/platform/audio/cpu/x86/vector_math_impl.h
new file mode 100644
index 00000000000..8562a56d8d8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/cpu/x86/vector_math_impl.h
@@ -0,0 +1,357 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file intentionally does not have header guards, it's included from
+// VectorMathAVX.h and from VectorMathSSE.h with different macro definitions.
+// The following line silences a presubmit warning that would otherwise be
+// triggered by this: no-include-guard-because-multiply-included
+
+#include "build/build_config.h"
+
+#if defined(ARCH_CPU_X86_FAMILY) && !defined(OS_MACOSX)
+
+#include <algorithm>
+#include <cmath>
+
+#include "third_party/blink/renderer/platform/audio/audio_array.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+namespace blink {
+namespace VectorMath {
+namespace VECTOR_MATH_SIMD_NAMESPACE_NAME {
+
+// This stride is chosen so that the same prepared filter created by
+// AVX::PrepareFilterForConv can be used by both AVX::Conv and SSE::Conv.
+// A prepared filter created by SSE::PrepareFilterForConv can only be used
+// by SSE::Conv.
+constexpr size_t kReversedFilterStride = 8u / kPackedFloatsPerRegister;
+
+bool IsAligned(const float* p) {
+ constexpr size_t kBytesPerRegister = kBitsPerRegister / 8u;
+ constexpr size_t kAlignmentOffsetMask = kBytesPerRegister - 1u;
+ return (reinterpret_cast<size_t>(p) & kAlignmentOffsetMask) == 0u;
+}
+
+void PrepareFilterForConv(const float* filter_p,
+ int filter_stride,
+ size_t filter_size,
+ AudioFloatArray* prepared_filter) {
+ // Only contiguous convolution is implemented. Correlation (positive
+ // |filter_stride|) and support for non-contiguous vectors are not
+ // implemented.
+ DCHECK_EQ(-1, filter_stride);
+ DCHECK(prepared_filter);
+
+ // Reverse the filter and repeat each value across a vector
+ prepared_filter->Allocate(kReversedFilterStride * kPackedFloatsPerRegister *
+ filter_size);
+ MType* reversed_filter = reinterpret_cast<MType*>(prepared_filter->Data());
+ for (size_t i = 0; i < filter_size; ++i) {
+ reversed_filter[kReversedFilterStride * i] = MM_PS(set1)(*(filter_p - i));
+ }
+}
+
+// Direct vector convolution:
+// dest[k] = sum(source[k+m]*filter[m*filter_stride]) for all m
+// provided that |prepared_filter_p| is |prepared_filter->Data()| and that
+// |prepared_filter| is prepared with |PrepareFilterForConv|.
+void Conv(const float* source_p,
+ const float* prepared_filter_p,
+ float* dest_p,
+ size_t frames_to_process,
+ size_t filter_size) {
+ const float* const dest_end_p = dest_p + frames_to_process;
+
+ DCHECK_EQ(0u, frames_to_process % kPackedFloatsPerRegister);
+ DCHECK_EQ(0u, filter_size % kPackedFloatsPerRegister);
+
+ const MType* reversed_filter =
+ reinterpret_cast<const MType*>(prepared_filter_p);
+
+ // Do convolution with kPackedFloatsPerRegister inputs at a time.
+ while (dest_p < dest_end_p) {
+ MType m_convolution_sum = MM_PS(setzero)();
+
+ // |filter_size| is a multiple of kPackedFloatsPerRegister so we can unroll
+ // the loop by kPackedFloatsPerRegister, manually.
+ for (size_t i = 0; i < filter_size; i += kPackedFloatsPerRegister) {
+ for (size_t j = 0; j < kPackedFloatsPerRegister; ++j) {
+ size_t k = i + j;
+ MType m_product;
+ MType m_source;
+
+ m_source = MM_PS(loadu)(source_p + k);
+ m_product =
+ MM_PS(mul)(reversed_filter[kReversedFilterStride * k], m_source);
+ m_convolution_sum = MM_PS(add)(m_convolution_sum, m_product);
+ }
+ }
+ MM_PS(storeu)(dest_p, m_convolution_sum);
+
+ source_p += kPackedFloatsPerRegister;
+ dest_p += kPackedFloatsPerRegister;
+ }
+}
+
+// dest[k] = source1[k] + source2[k]
+void Vadd(const float* source1p,
+ const float* source2p,
+ float* dest_p,
+ size_t frames_to_process) {
+ const float* const source1_end_p = source1p + frames_to_process;
+
+ DCHECK(IsAligned(source1p));
+ DCHECK_EQ(0u, frames_to_process % kPackedFloatsPerRegister);
+
+#define ADD_ALL(loadSource2, storeDest) \
+ while (source1p < source1_end_p) { \
+ MType m_source1 = MM_PS(load)(source1p); \
+ MType m_source2 = MM_PS(loadSource2)(source2p); \
+ MType m_dest = MM_PS(add)(m_source1, m_source2); \
+ MM_PS(storeDest)(dest_p, m_dest); \
+ source1p += kPackedFloatsPerRegister; \
+ source2p += kPackedFloatsPerRegister; \
+ dest_p += kPackedFloatsPerRegister; \
+ }
+
+ if (IsAligned(source2p)) {
+ if (IsAligned(dest_p)) {
+ ADD_ALL(load, store);
+ } else {
+ ADD_ALL(load, storeu);
+ }
+ } else {
+ if (IsAligned(dest_p)) {
+ ADD_ALL(loadu, store);
+ } else {
+ ADD_ALL(loadu, storeu);
+ }
+ }
+
+#undef ADD_ALL
+}
+
+// dest[k] = clip(source[k], low_threshold, high_threshold)
+// = max(low_threshold, min(high_threshold, source[k]))
+void Vclip(const float* source_p,
+ const float* low_threshold_p,
+ const float* high_threshold_p,
+ float* dest_p,
+ size_t frames_to_process) {
+ const float* const source_end_p = source_p + frames_to_process;
+
+ DCHECK(IsAligned(source_p));
+ DCHECK_EQ(0u, frames_to_process % kPackedFloatsPerRegister);
+
+ MType m_low_threshold = MM_PS(set1)(*low_threshold_p);
+ MType m_high_threshold = MM_PS(set1)(*high_threshold_p);
+
+#define CLIP_ALL(storeDest) \
+ while (source_p < source_end_p) { \
+ MType m_source = MM_PS(load)(source_p); \
+ MType m_dest = \
+ MM_PS(max)(m_low_threshold, MM_PS(min)(m_high_threshold, m_source)); \
+ MM_PS(storeDest)(dest_p, m_dest); \
+ source_p += kPackedFloatsPerRegister; \
+ dest_p += kPackedFloatsPerRegister; \
+ }
+
+ if (IsAligned(dest_p)) {
+ CLIP_ALL(store);
+ } else {
+ CLIP_ALL(storeu);
+ }
+
+#undef CLIP_ALL
+}
+
+// *max_p = max(*max_p, source_max) where
+// source_max = max(abs(source[k])) for all k
+void Vmaxmgv(const float* source_p, float* max_p, size_t frames_to_process) {
+ constexpr uint32_t kMask = 0x7FFFFFFFu;
+
+ const float* const source_end_p = source_p + frames_to_process;
+
+ DCHECK(IsAligned(source_p));
+ DCHECK_EQ(0u, frames_to_process % kPackedFloatsPerRegister);
+
+ MType m_mask = MM_PS(set1)(*reinterpret_cast<const float*>(&kMask));
+ MType m_max = MM_PS(setzero)();
+
+ while (source_p < source_end_p) {
+ MType m_source = MM_PS(load)(source_p);
+ // Calculate the absolute value by ANDing the source with the mask,
+ // which will set the sign bit to 0.
+ m_source = MM_PS(and)(m_source, m_mask);
+ m_max = MM_PS(max)(m_source, m_max);
+ source_p += kPackedFloatsPerRegister;
+ }
+
+ // Combine the packed floats.
+ const float* maxes = reinterpret_cast<const float*>(&m_max);
+ for (unsigned i = 0u; i < kPackedFloatsPerRegister; ++i)
+ *max_p = std::max(*max_p, maxes[i]);
+}
+
+// dest[k] = source1[k] * source2[k]
+void Vmul(const float* source1p,
+ const float* source2p,
+ float* dest_p,
+ size_t frames_to_process) {
+ const float* const source1_end_p = source1p + frames_to_process;
+
+ DCHECK(IsAligned(source1p));
+ DCHECK_EQ(0u, frames_to_process % kPackedFloatsPerRegister);
+
+#define MULTIPLY_ALL(loadSource2, storeDest) \
+ while (source1p < source1_end_p) { \
+ MType m_source1 = MM_PS(load)(source1p); \
+ MType m_source2 = MM_PS(loadSource2)(source2p); \
+ MType m_dest = MM_PS(mul)(m_source1, m_source2); \
+ MM_PS(storeDest)(dest_p, m_dest); \
+ source1p += kPackedFloatsPerRegister; \
+ source2p += kPackedFloatsPerRegister; \
+ dest_p += kPackedFloatsPerRegister; \
+ }
+
+ if (IsAligned(source2p)) {
+ if (IsAligned(dest_p)) {
+ MULTIPLY_ALL(load, store);
+ } else {
+ MULTIPLY_ALL(load, storeu);
+ }
+ } else {
+ if (IsAligned(dest_p)) {
+ MULTIPLY_ALL(loadu, store);
+ } else {
+ MULTIPLY_ALL(loadu, storeu);
+ }
+ }
+
+#undef MULTIPLY_ALL
+}
+
+// dest[k] += scale * source[k]
+void Vsma(const float* source_p,
+ const float* scale,
+ float* dest_p,
+ size_t frames_to_process) {
+ const float* const source_end_p = source_p + frames_to_process;
+
+ DCHECK(IsAligned(source_p));
+ DCHECK_EQ(0u, frames_to_process % kPackedFloatsPerRegister);
+
+ const MType m_scale = MM_PS(set1)(*scale);
+
+#define SCALAR_MULTIPLY_AND_ADD_ALL(loadDest, storeDest) \
+ while (source_p < source_end_p) { \
+ MType m_source = MM_PS(load)(source_p); \
+ MType m_dest = MM_PS(loadDest)(dest_p); \
+ m_dest = MM_PS(add)(m_dest, MM_PS(mul)(m_scale, m_source)); \
+ MM_PS(storeDest)(dest_p, m_dest); \
+ source_p += kPackedFloatsPerRegister; \
+ dest_p += kPackedFloatsPerRegister; \
+ }
+
+ if (IsAligned(dest_p)) {
+ SCALAR_MULTIPLY_AND_ADD_ALL(load, store);
+ } else {
+ SCALAR_MULTIPLY_AND_ADD_ALL(loadu, storeu);
+ }
+
+#undef SCALAR_MULTIPLY_AND_ADD_ALL
+}
+
+// dest[k] = scale * source[k]
+void Vsmul(const float* source_p,
+ const float* scale,
+ float* dest_p,
+ size_t frames_to_process) {
+ const float* const source_end_p = source_p + frames_to_process;
+
+ DCHECK(IsAligned(source_p));
+ DCHECK_EQ(0u, frames_to_process % kPackedFloatsPerRegister);
+
+ const MType m_scale = MM_PS(set1)(*scale);
+
+#define SCALAR_MULTIPLY_ALL(storeDest) \
+ while (source_p < source_end_p) { \
+ MType m_source = MM_PS(load)(source_p); \
+ MType m_dest = MM_PS(mul)(m_scale, m_source); \
+ MM_PS(storeDest)(dest_p, m_dest); \
+ source_p += kPackedFloatsPerRegister; \
+ dest_p += kPackedFloatsPerRegister; \
+ }
+
+ if (IsAligned(dest_p)) {
+ SCALAR_MULTIPLY_ALL(store);
+ } else {
+ SCALAR_MULTIPLY_ALL(storeu);
+ }
+
+#undef SCALAR_MULTIPLY_ALL
+}
+
+// sum += sum(source[k]^2) for all k
+void Vsvesq(const float* source_p, float* sum_p, size_t frames_to_process) {
+ const float* const source_end_p = source_p + frames_to_process;
+
+ DCHECK(IsAligned(source_p));
+ DCHECK_EQ(0u, frames_to_process % kPackedFloatsPerRegister);
+
+ MType m_sum = MM_PS(setzero)();
+
+ while (source_p < source_end_p) {
+ MType m_source = MM_PS(load)(source_p);
+ m_sum = MM_PS(add)(m_sum, MM_PS(mul)(m_source, m_source));
+ source_p += kPackedFloatsPerRegister;
+ }
+
+ // Combine the packed floats.
+ const float* sums = reinterpret_cast<const float*>(&m_sum);
+ for (unsigned i = 0u; i < kPackedFloatsPerRegister; ++i)
+ *sum_p += sums[i];
+}
+
+// real_dest[k] = real1[k] * real2[k] - imag1[k] * imag2[k]
+// imag_dest[k] = real1[k] * imag2[k] + imag1[k] * real2[k]
+void Zvmul(const float* real1p,
+ const float* imag1p,
+ const float* real2p,
+ const float* imag2p,
+ float* real_dest_p,
+ float* imag_dest_p,
+ size_t frames_to_process) {
+ DCHECK(IsAligned(real1p));
+ DCHECK_EQ(0u, frames_to_process % kPackedFloatsPerRegister);
+
+#define MULTIPLY_ALL(loadOtherThanReal1, storeDest) \
+ for (size_t i = 0u; i < frames_to_process; i += kPackedFloatsPerRegister) { \
+ MType real1 = MM_PS(load)(real1p + i); \
+ MType real2 = MM_PS(loadOtherThanReal1)(real2p + i); \
+ MType imag1 = MM_PS(loadOtherThanReal1)(imag1p + i); \
+ MType imag2 = MM_PS(loadOtherThanReal1)(imag2p + i); \
+ MType real = \
+ MM_PS(sub)(MM_PS(mul)(real1, real2), MM_PS(mul)(imag1, imag2)); \
+ MType imag = \
+ MM_PS(add)(MM_PS(mul)(real1, imag2), MM_PS(mul)(imag1, real2)); \
+ MM_PS(storeDest)(real_dest_p + i, real); \
+ MM_PS(storeDest)(imag_dest_p + i, imag); \
+ }
+
+ if (IsAligned(imag1p) && IsAligned(real2p) && IsAligned(imag2p) &&
+ IsAligned(real_dest_p) && IsAligned(imag_dest_p)) {
+ MULTIPLY_ALL(load, store);
+ } else {
+ MULTIPLY_ALL(loadu, storeu);
+ }
+
+#undef MULTIPLY_ALL
+}
+
+} // namespace VECTOR_MATH_SIMD_NAMESPACE_NAME
+} // namespace VectorMath
+} // namespace blink
+
+#endif // defined(ARCH_CPU_X86_FAMILY) && !defined(OS_MACOSX)
diff --git a/chromium/third_party/blink/renderer/platform/audio/cpu/x86/vector_math_sse.cc b/chromium/third_party/blink/renderer/platform/audio/cpu/x86/vector_math_sse.cc
new file mode 100644
index 00000000000..f5c44968d10
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/cpu/x86/vector_math_sse.cc
@@ -0,0 +1,31 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "build/build_config.h"
+
+#if defined(ARCH_CPU_X86_FAMILY) && !defined(OS_MACOSX)
+
+#include "third_party/blink/renderer/platform/audio/cpu/x86/vector_math_sse.h"
+
+#include <xmmintrin.h>
+
+namespace blink {
+namespace VectorMath {
+namespace SSE {
+
+using MType = __m128;
+
+} // namespace SSE
+} // namespace VectorMath
+} // namespace blink
+
+#define MM_PS(name) _mm_##name##_ps
+#define VECTOR_MATH_SIMD_NAMESPACE_NAME SSE
+
+#include "third_party/blink/renderer/platform/audio/cpu/x86/vector_math_impl.h"
+
+#undef MM_PS
+#undef VECTOR_MATH_SIMD_NAMESPACE_NAME
+
+#endif // defined(ARCH_CPU_X86_FAMILY) && !defined(OS_MACOSX)
diff --git a/chromium/third_party/blink/renderer/platform/audio/cpu/x86/vector_math_sse.h b/chromium/third_party/blink/renderer/platform/audio/cpu/x86/vector_math_sse.h
new file mode 100644
index 00000000000..14cd73ea95e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/cpu/x86/vector_math_sse.h
@@ -0,0 +1,90 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_CPU_X86_VECTOR_MATH_SSE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_CPU_X86_VECTOR_MATH_SSE_H_
+
+#include <cstddef>
+
+#include "third_party/blink/renderer/platform/audio/audio_array.h"
+
+namespace blink {
+namespace VectorMath {
+namespace SSE {
+
+constexpr size_t kBitsPerRegister = 128u;
+constexpr size_t kPackedFloatsPerRegister = kBitsPerRegister / 32u;
+constexpr size_t kFramesToProcessMask = ~(kPackedFloatsPerRegister - 1u);
+
+bool IsAligned(const float*);
+
+// Direct vector convolution:
+// dest[k] = sum(source[k+m]*filter[m*filter_stride]) for all m
+// provided that |prepared_filter_p| is |prepared_filter->Data()| and that
+// |prepared_filter| is prepared with |PrepareFilterForConv|.
+void Conv(const float* source_p,
+ const float* prepared_filter_p,
+ float* dest_p,
+ size_t frames_to_process,
+ size_t filter_size);
+
+void PrepareFilterForConv(const float* filter_p,
+ int filter_stride,
+ size_t filter_size,
+ AudioFloatArray* prepared_filter);
+
+// dest[k] = source1[k] + source2[k]
+void Vadd(const float* source1p,
+ const float* source2p,
+ float* dest_p,
+ size_t frames_to_process);
+
+// dest[k] = clip(source[k], low_threshold, high_threshold)
+// = max(low_threshold, min(high_threshold, source[k]))
+void Vclip(const float* source_p,
+ const float* low_threshold_p,
+ const float* high_threshold_p,
+ float* dest_p,
+ size_t frames_to_process);
+
+// *max_p = max(*max_p, source_max) where
+// source_max = max(abs(source[k])) for all k
+void Vmaxmgv(const float* source_p, float* max_p, size_t frames_to_process);
+
+// dest[k] = source1[k] * source2[k]
+void Vmul(const float* source1p,
+ const float* source2p,
+ float* dest_p,
+ size_t frames_to_process);
+
+// dest[k] += scale * source[k]
+void Vsma(const float* source_p,
+ const float* scale,
+ float* dest_p,
+ size_t frames_to_process);
+
+// dest[k] = scale * source[k]
+void Vsmul(const float* source_p,
+ const float* scale,
+ float* dest_p,
+ size_t frames_to_process);
+
+// sum += sum(source[k]^2) for all k
+void Vsvesq(const float* source_p, float* sum_p, size_t frames_to_process);
+
+// real_dest[k] = real1[k] * real2[k] - imag1[k] * imag2[k]
+// imag_dest[k] = real1[k] * imag2[k] + imag1[k] * real2[k]
+void Zvmul(const float* real1p,
+ const float* imag1p,
+ const float* real2p,
+ const float* imag2p,
+ float* real_dest_p,
+ float* imag_dest_p,
+ size_t frames_to_process);
+
+} // namespace SSE
+} // namespace VectorMath
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_CPU_X86_VECTOR_MATH_SSE_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/cpu/x86/vector_math_x8_6.h b/chromium/third_party/blink/renderer/platform/audio/cpu/x86/vector_math_x8_6.h
new file mode 100644
index 00000000000..cc35b44b7bf
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/cpu/x86/vector_math_x8_6.h
@@ -0,0 +1,413 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_CPU_X86_VECTOR_MATH_X8_6_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_CPU_X86_VECTOR_MATH_X8_6_H_
+
+#include "base/cpu.h"
+#include "third_party/blink/renderer/platform/audio/cpu/x86/vector_math_avx.h"
+#include "third_party/blink/renderer/platform/audio/cpu/x86/vector_math_sse.h"
+#include "third_party/blink/renderer/platform/audio/vector_math_scalar.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+namespace blink {
+namespace VectorMath {
+namespace X86 {
+
+struct FrameCounts {
+ size_t scalar_for_alignment;
+ size_t sse_for_alignment;
+ size_t avx;
+ size_t sse;
+ size_t scalar;
+};
+
+static bool CPUSupportsAVX() {
+ static bool supports = ::base::CPU().has_avx();
+ return supports;
+}
+
+static size_t GetAVXAlignmentOffsetInNumberOfFloats(const float* source_p) {
+ constexpr size_t kBytesPerRegister = AVX::kBitsPerRegister / 8u;
+ constexpr size_t kAlignmentOffsetMask = kBytesPerRegister - 1u;
+ size_t offset = reinterpret_cast<size_t>(source_p) & kAlignmentOffsetMask;
+ DCHECK_EQ(0u, offset % sizeof(*source_p));
+ return offset / sizeof(*source_p);
+}
+
+static ALWAYS_INLINE FrameCounts
+SplitFramesToProcess(const float* source_p, size_t frames_to_process) {
+ FrameCounts counts = {0u, 0u, 0u, 0u, 0u};
+
+ const size_t avx_alignment_offset =
+ GetAVXAlignmentOffsetInNumberOfFloats(source_p);
+
+ // If the first frame is not AVX aligned, the first several frames (at most
+ // seven) must be processed separately for proper alignment.
+ const size_t total_for_alignment =
+ (AVX::kPackedFloatsPerRegister - avx_alignment_offset) &
+ ~AVX::kFramesToProcessMask;
+ const size_t scalar_for_alignment =
+ total_for_alignment & ~SSE::kFramesToProcessMask;
+ const size_t sse_for_alignment =
+ total_for_alignment & SSE::kFramesToProcessMask;
+
+ // Check which CPU features can be used based on the number of frames to
+ // process and based on CPU support.
+ const bool use_at_least_avx =
+ CPUSupportsAVX() &&
+ frames_to_process >= scalar_for_alignment + sse_for_alignment +
+ AVX::kPackedFloatsPerRegister;
+ const bool use_at_least_sse =
+ use_at_least_avx ||
+ frames_to_process >= scalar_for_alignment + SSE::kPackedFloatsPerRegister;
+
+ if (use_at_least_sse) {
+ counts.scalar_for_alignment = scalar_for_alignment;
+ frames_to_process -= counts.scalar_for_alignment;
+ // The remaining frames are SSE aligned.
+ DCHECK(SSE::IsAligned(source_p + counts.scalar_for_alignment));
+
+ if (use_at_least_avx) {
+ counts.sse_for_alignment = sse_for_alignment;
+ frames_to_process -= counts.sse_for_alignment;
+ // The remaining frames are AVX aligned.
+ DCHECK(AVX::IsAligned(source_p + counts.scalar_for_alignment +
+ counts.sse_for_alignment));
+
+ // Process as many as possible of the remaining frames using AVX.
+ counts.avx = frames_to_process & AVX::kFramesToProcessMask;
+ frames_to_process -= counts.avx;
+ }
+
+ // Process as many as possible of the remaining frames using SSE.
+ counts.sse = frames_to_process & SSE::kFramesToProcessMask;
+ frames_to_process -= counts.sse;
+ }
+
+ // Process the remaining frames separately.
+ counts.scalar = frames_to_process;
+ return counts;
+}
+
+static ALWAYS_INLINE void PrepareFilterForConv(
+ const float* filter_p,
+ int filter_stride,
+ size_t filter_size,
+ AudioFloatArray* prepared_filter) {
+ if (CPUSupportsAVX()) {
+ AVX::PrepareFilterForConv(filter_p, filter_stride, filter_size,
+ prepared_filter);
+ } else {
+ SSE::PrepareFilterForConv(filter_p, filter_stride, filter_size,
+ prepared_filter);
+ }
+}
+
+static ALWAYS_INLINE void Conv(const float* source_p,
+ int source_stride,
+ const float* filter_p,
+ int filter_stride,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process,
+ size_t filter_size,
+ const AudioFloatArray* prepared_filter) {
+ const float* prepared_filter_p =
+ prepared_filter ? prepared_filter->Data() : nullptr;
+ if (source_stride == 1 && dest_stride == 1 && prepared_filter_p) {
+ if (CPUSupportsAVX() && (filter_size & ~AVX::kFramesToProcessMask) == 0u) {
+ // |frames_to_process| is always a multiply of render quantum and
+ // therefore the frames can always be processed using AVX.
+ CHECK_EQ(frames_to_process & ~AVX::kFramesToProcessMask, 0u);
+ AVX::Conv(source_p, prepared_filter_p, dest_p, frames_to_process,
+ filter_size);
+ return;
+ }
+ if ((filter_size & ~SSE::kFramesToProcessMask) == 0u) {
+ // |frames_to_process| is always a multiply of render quantum and
+ // therefore the frames can always be processed using SSE.
+ CHECK_EQ(frames_to_process & ~SSE::kFramesToProcessMask, 0u);
+ SSE::Conv(source_p, prepared_filter_p, dest_p, frames_to_process,
+ filter_size);
+ return;
+ }
+ }
+ Scalar::Conv(source_p, source_stride, filter_p, filter_stride, dest_p,
+ dest_stride, frames_to_process, filter_size, nullptr);
+}
+
+static ALWAYS_INLINE void Vadd(const float* source1p,
+ int source_stride1,
+ const float* source2p,
+ int source_stride2,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process) {
+ if (source_stride1 == 1 && source_stride2 == 1 && dest_stride == 1) {
+ const FrameCounts frame_counts =
+ SplitFramesToProcess(source1p, frames_to_process);
+
+ Scalar::Vadd(source1p, 1, source2p, 1, dest_p, 1,
+ frame_counts.scalar_for_alignment);
+ size_t i = frame_counts.scalar_for_alignment;
+ if (frame_counts.sse_for_alignment > 0u) {
+ SSE::Vadd(source1p + i, source2p + i, dest_p + i,
+ frame_counts.sse_for_alignment);
+ i += frame_counts.sse_for_alignment;
+ }
+ if (frame_counts.avx > 0u) {
+ AVX::Vadd(source1p + i, source2p + i, dest_p + i, frame_counts.avx);
+ i += frame_counts.avx;
+ }
+ if (frame_counts.sse > 0u) {
+ SSE::Vadd(source1p + i, source2p + i, dest_p + i, frame_counts.sse);
+ i += frame_counts.sse;
+ }
+ Scalar::Vadd(source1p + i, 1, source2p + i, 1, dest_p + i, 1,
+ frame_counts.scalar);
+ DCHECK_EQ(frames_to_process, i + frame_counts.scalar);
+ } else {
+ Scalar::Vadd(source1p, source_stride1, source2p, source_stride2, dest_p,
+ dest_stride, frames_to_process);
+ }
+}
+
+static ALWAYS_INLINE void Vclip(const float* source_p,
+ int source_stride,
+ const float* low_threshold_p,
+ const float* high_threshold_p,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process) {
+ if (source_stride == 1 && dest_stride == 1) {
+ const FrameCounts frame_counts =
+ SplitFramesToProcess(source_p, frames_to_process);
+
+ Scalar::Vclip(source_p, 1, low_threshold_p, high_threshold_p, dest_p, 1,
+ frame_counts.scalar_for_alignment);
+ size_t i = frame_counts.scalar_for_alignment;
+ if (frame_counts.sse_for_alignment > 0u) {
+ SSE::Vclip(source_p + i, low_threshold_p, high_threshold_p, dest_p + i,
+ frame_counts.sse_for_alignment);
+ i += frame_counts.sse_for_alignment;
+ }
+ if (frame_counts.avx > 0u) {
+ AVX::Vclip(source_p + i, low_threshold_p, high_threshold_p, dest_p + i,
+ frame_counts.avx);
+ i += frame_counts.avx;
+ }
+ if (frame_counts.sse > 0u) {
+ SSE::Vclip(source_p + i, low_threshold_p, high_threshold_p, dest_p + i,
+ frame_counts.sse);
+ i += frame_counts.sse;
+ }
+ Scalar::Vclip(source_p + i, 1, low_threshold_p, high_threshold_p,
+ dest_p + i, 1, frame_counts.scalar);
+ DCHECK_EQ(frames_to_process, i + frame_counts.scalar);
+ } else {
+ Scalar::Vclip(source_p, source_stride, low_threshold_p, high_threshold_p,
+ dest_p, dest_stride, frames_to_process);
+ }
+}
+
+static ALWAYS_INLINE void Vmaxmgv(const float* source_p,
+ int source_stride,
+ float* max_p,
+ size_t frames_to_process) {
+ if (source_stride == 1) {
+ const FrameCounts frame_counts =
+ SplitFramesToProcess(source_p, frames_to_process);
+
+ Scalar::Vmaxmgv(source_p, 1, max_p, frame_counts.scalar_for_alignment);
+ size_t i = frame_counts.scalar_for_alignment;
+ if (frame_counts.sse_for_alignment > 0u) {
+ SSE::Vmaxmgv(source_p + i, max_p, frame_counts.sse_for_alignment);
+ i += frame_counts.sse_for_alignment;
+ }
+ if (frame_counts.avx > 0u) {
+ AVX::Vmaxmgv(source_p + i, max_p, frame_counts.avx);
+ i += frame_counts.avx;
+ }
+ if (frame_counts.sse > 0u) {
+ SSE::Vmaxmgv(source_p + i, max_p, frame_counts.sse);
+ i += frame_counts.sse;
+ }
+ Scalar::Vmaxmgv(source_p + i, 1, max_p, frame_counts.scalar);
+ DCHECK_EQ(frames_to_process, i + frame_counts.scalar);
+ } else {
+ Scalar::Vmaxmgv(source_p, source_stride, max_p, frames_to_process);
+ }
+}
+
+static ALWAYS_INLINE void Vmul(const float* source1p,
+ int source_stride1,
+ const float* source2p,
+ int source_stride2,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process) {
+ if (source_stride1 == 1 && source_stride2 == 1 && dest_stride == 1) {
+ const FrameCounts frame_counts =
+ SplitFramesToProcess(source1p, frames_to_process);
+
+ Scalar::Vmul(source1p, 1, source2p, 1, dest_p, 1,
+ frame_counts.scalar_for_alignment);
+ size_t i = frame_counts.scalar_for_alignment;
+ if (frame_counts.sse_for_alignment > 0u) {
+ SSE::Vmul(source1p + i, source2p + i, dest_p + i,
+ frame_counts.sse_for_alignment);
+ i += frame_counts.sse_for_alignment;
+ }
+ if (frame_counts.avx > 0u) {
+ AVX::Vmul(source1p + i, source2p + i, dest_p + i, frame_counts.avx);
+ i += frame_counts.avx;
+ }
+ if (frame_counts.sse > 0u) {
+ SSE::Vmul(source1p + i, source2p + i, dest_p + i, frame_counts.sse);
+ i += frame_counts.sse;
+ }
+ Scalar::Vmul(source1p + i, 1, source2p + i, 1, dest_p + i, 1,
+ frame_counts.scalar);
+ DCHECK_EQ(frames_to_process, i + frame_counts.scalar);
+ } else {
+ Scalar::Vmul(source1p, source_stride1, source2p, source_stride2, dest_p,
+ dest_stride, frames_to_process);
+ }
+}
+
+static ALWAYS_INLINE void Vsma(const float* source_p,
+ int source_stride,
+ const float* scale,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process) {
+ if (source_stride == 1 && dest_stride == 1) {
+ const FrameCounts frame_counts =
+ SplitFramesToProcess(source_p, frames_to_process);
+
+ Scalar::Vsma(source_p, 1, scale, dest_p, 1,
+ frame_counts.scalar_for_alignment);
+ size_t i = frame_counts.scalar_for_alignment;
+ if (frame_counts.sse_for_alignment > 0u) {
+ SSE::Vsma(source_p + i, scale, dest_p + i,
+ frame_counts.sse_for_alignment);
+ i += frame_counts.sse_for_alignment;
+ }
+ if (frame_counts.avx > 0u) {
+ AVX::Vsma(source_p + i, scale, dest_p + i, frame_counts.avx);
+ i += frame_counts.avx;
+ }
+ if (frame_counts.sse > 0u) {
+ SSE::Vsma(source_p + i, scale, dest_p + i, frame_counts.sse);
+ i += frame_counts.sse;
+ }
+ Scalar::Vsma(source_p + i, 1, scale, dest_p + i, 1, frame_counts.scalar);
+ DCHECK_EQ(frames_to_process, i + frame_counts.scalar);
+ } else {
+ Scalar::Vsma(source_p, source_stride, scale, dest_p, dest_stride,
+ frames_to_process);
+ }
+}
+
+static ALWAYS_INLINE void Vsmul(const float* source_p,
+ int source_stride,
+ const float* scale,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process) {
+ if (source_stride == 1 && dest_stride == 1) {
+ const FrameCounts frame_counts =
+ SplitFramesToProcess(source_p, frames_to_process);
+
+ Scalar::Vsmul(source_p, 1, scale, dest_p, 1,
+ frame_counts.scalar_for_alignment);
+ size_t i = frame_counts.scalar_for_alignment;
+ if (frame_counts.sse_for_alignment > 0u) {
+ SSE::Vsmul(source_p + i, scale, dest_p + i,
+ frame_counts.sse_for_alignment);
+ i += frame_counts.sse_for_alignment;
+ }
+ if (frame_counts.avx > 0u) {
+ AVX::Vsmul(source_p + i, scale, dest_p + i, frame_counts.avx);
+ i += frame_counts.avx;
+ }
+ if (frame_counts.sse > 0u) {
+ SSE::Vsmul(source_p + i, scale, dest_p + i, frame_counts.sse);
+ i += frame_counts.sse;
+ }
+ Scalar::Vsmul(source_p + i, 1, scale, dest_p + i, 1, frame_counts.scalar);
+ DCHECK_EQ(frames_to_process, i + frame_counts.scalar);
+ } else {
+ Scalar::Vsmul(source_p, source_stride, scale, dest_p, dest_stride,
+ frames_to_process);
+ }
+}
+
+static ALWAYS_INLINE void Vsvesq(const float* source_p,
+ int source_stride,
+ float* sum_p,
+ size_t frames_to_process) {
+ if (source_stride == 1) {
+ const FrameCounts frame_counts =
+ SplitFramesToProcess(source_p, frames_to_process);
+
+ Scalar::Vsvesq(source_p, 1, sum_p, frame_counts.scalar_for_alignment);
+ size_t i = frame_counts.scalar_for_alignment;
+ if (frame_counts.sse_for_alignment > 0u) {
+ SSE::Vsvesq(source_p + i, sum_p, frame_counts.sse_for_alignment);
+ i += frame_counts.sse_for_alignment;
+ }
+ if (frame_counts.avx > 0u) {
+ AVX::Vsvesq(source_p + i, sum_p, frame_counts.avx);
+ i += frame_counts.avx;
+ }
+ if (frame_counts.sse > 0u) {
+ SSE::Vsvesq(source_p + i, sum_p, frame_counts.sse);
+ i += frame_counts.sse;
+ }
+ Scalar::Vsvesq(source_p + i, 1, sum_p, frame_counts.scalar);
+ DCHECK_EQ(frames_to_process, i + frame_counts.scalar);
+ } else {
+ Scalar::Vsvesq(source_p, source_stride, sum_p, frames_to_process);
+ }
+}
+
+static ALWAYS_INLINE void Zvmul(const float* real1p,
+ const float* imag1p,
+ const float* real2p,
+ const float* imag2p,
+ float* real_dest_p,
+ float* imag_dest_p,
+ size_t frames_to_process) {
+ FrameCounts frame_counts = SplitFramesToProcess(real1p, frames_to_process);
+
+ Scalar::Zvmul(real1p, imag1p, real2p, imag2p, real_dest_p, imag_dest_p,
+ frame_counts.scalar_for_alignment);
+ size_t i = frame_counts.scalar_for_alignment;
+ if (frame_counts.sse_for_alignment > 0u) {
+ SSE::Zvmul(real1p + i, imag1p + i, real2p + i, imag2p + i, real_dest_p + i,
+ imag_dest_p + i, frame_counts.sse_for_alignment);
+ i += frame_counts.sse_for_alignment;
+ }
+ if (frame_counts.avx > 0u) {
+ AVX::Zvmul(real1p + i, imag1p + i, real2p + i, imag2p + i, real_dest_p + i,
+ imag_dest_p + i, frame_counts.avx);
+ i += frame_counts.avx;
+ }
+ if (frame_counts.sse > 0u) {
+ SSE::Zvmul(real1p + i, imag1p + i, real2p + i, imag2p + i, real_dest_p + i,
+ imag_dest_p + i, frame_counts.sse);
+ i += frame_counts.sse;
+ }
+ Scalar::Zvmul(real1p + i, imag1p + i, real2p + i, imag2p + i, real_dest_p + i,
+ imag_dest_p + i, frame_counts.scalar);
+ DCHECK_EQ(frames_to_process, i + frame_counts.scalar);
+}
+
+} // namespace X86
+} // namespace VectorMath
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_CPU_X86_VECTOR_MATH_X8_6_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/denormal_disabler.h b/chromium/third_party/blink/renderer/platform/audio/denormal_disabler.h
new file mode 100644
index 00000000000..c506a70c945
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/denormal_disabler.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2011, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_DENORMAL_DISABLER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_DENORMAL_DISABLER_H_
+
+#include <float.h>
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+namespace blink {
+
+// Deal with denormals. They can very seriously impact performance on x86.
+
+// Define HAVE_DENORMAL if we support flushing denormals to zero.
+
+#if defined(OS_WIN) && defined(COMPILER_MSVC)
+// Windows compiled using MSVC with SSE2
+#define HAVE_DENORMAL 1
+#endif
+
+#if defined(COMPILER_GCC) && defined(ARCH_CPU_X86_FAMILY)
+// X86 chips can flush denormals
+#define HAVE_DENORMAL 1
+#endif
+
+#if defined(ARCH_CPU_ARM_FAMILY)
+#define HAVE_DENORMAL 1
+#endif
+
+#if defined(HAVE_DENORMAL)
+class DenormalDisabler {
+ DISALLOW_NEW();
+
+ public:
+ DenormalDisabler() : saved_csr_(0) { DisableDenormals(); }
+
+ ~DenormalDisabler() { RestoreState(); }
+
+ // This is a nop if we can flush denormals to zero in hardware.
+ static inline float FlushDenormalFloatToZero(float f) { return f; }
+
+ private:
+ unsigned saved_csr_;
+
+#if defined(COMPILER_GCC) && defined(ARCH_CPU_X86_FAMILY)
+ inline void DisableDenormals() {
+ saved_csr_ = GetCSR();
+ SetCSR(saved_csr_ | 0x8040);
+ }
+
+ inline void RestoreState() { SetCSR(saved_csr_); }
+
+ inline int GetCSR() {
+ int result;
+ asm volatile("stmxcsr %0" : "=m"(result));
+ return result;
+ }
+
+ inline void SetCSR(int a) {
+ int temp = a;
+ asm volatile("ldmxcsr %0" : : "m"(temp));
+ }
+
+#elif defined(OS_WIN) && defined(COMPILER_MSVC)
+ inline void DisableDenormals() {
+ // Save the current state, and set mode to flush denormals.
+ //
+ // http://stackoverflow.com/questions/637175/possible-bug-in-controlfp-s-may-not-restore-control-word-correctly
+ _controlfp_s(&saved_csr_, 0, 0);
+ unsigned unused;
+ _controlfp_s(&unused, _DN_FLUSH, _MCW_DN);
+ }
+
+ inline void RestoreState() {
+ unsigned unused;
+ _controlfp_s(&unused, saved_csr_, _MCW_DN);
+ }
+#elif defined(ARCH_CPU_ARM_FAMILY)
+ inline void DisableDenormals() {
+ saved_csr_ = GetStatusWord();
+ // Bit 24 is the flush-to-zero mode control bit. Setting it to 1 flushes
+ // denormals to 0.
+ SetStatusWord(saved_csr_ | (1 << 24));
+ }
+
+ inline void RestoreState() { SetStatusWord(saved_csr_); }
+
+ inline int GetStatusWord() {
+ int result;
+#if defined(ARCH_CPU_ARM64)
+ asm volatile("mrs %x[result], FPCR" : [result] "=r"(result));
+#else
+ asm volatile("vmrs %[result], FPSCR" : [result] "=r"(result));
+#endif
+ return result;
+ }
+
+ inline void SetStatusWord(int a) {
+#if defined(ARCH_CPU_ARM64)
+ asm volatile("msr FPCR, %x[src]" : : [src] "r"(a));
+#else
+ asm volatile("vmsr FPSCR, %[src]" : : [src] "r"(a));
+#endif
+ }
+
+#endif
+};
+
+#else
+// FIXME: add implementations for other architectures and compilers
+class DenormalDisabler {
+ public:
+ DenormalDisabler() {}
+
+ // Assume the worst case that other architectures and compilers
+ // need to flush denormals to zero manually.
+ static inline float FlushDenormalFloatToZero(float f) {
+ return (fabs(f) < FLT_MIN) ? 0.0f : f;
+ }
+};
+
+#endif
+
+} // namespace blink
+
+#undef HAVE_DENORMAL
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_DENORMAL_DISABLER_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/direct_convolver.cc b/chromium/third_party/blink/renderer/platform/audio/direct_convolver.cc
new file mode 100644
index 00000000000..a07206cc23b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/direct_convolver.cc
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2012 Intel 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/audio/direct_convolver.h"
+
+#include <utility>
+
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/audio/vector_math.h"
+
+#if defined(OS_MACOSX)
+#include <Accelerate/Accelerate.h>
+#endif
+
+#if defined(ARCH_CPU_X86_FAMILY) && !defined(OS_MACOSX)
+#include <emmintrin.h>
+#endif
+
+namespace blink {
+
+namespace {
+using VectorMath::Conv;
+using VectorMath::PrepareFilterForConv;
+} // namespace
+
+DirectConvolver::DirectConvolver(
+ size_t input_block_size,
+ std::unique_ptr<AudioFloatArray> convolution_kernel)
+ : input_block_size_(input_block_size),
+ buffer_(input_block_size * 2),
+ convolution_kernel_(std::move(convolution_kernel)) {
+ size_t kernel_size = ConvolutionKernelSize();
+ PrepareFilterForConv(convolution_kernel_->Data() + kernel_size - 1, -1,
+ kernel_size, &prepared_convolution_kernel_);
+}
+
+void DirectConvolver::Process(const float* source_p,
+ float* dest_p,
+ size_t frames_to_process) {
+ DCHECK_EQ(frames_to_process, input_block_size_);
+ if (frames_to_process != input_block_size_)
+ return;
+
+ // Only support kernelSize <= m_inputBlockSize
+ size_t kernel_size = ConvolutionKernelSize();
+ DCHECK_LE(kernel_size, input_block_size_);
+ if (kernel_size > input_block_size_)
+ return;
+
+ float* kernel_p = convolution_kernel_->Data();
+
+ // Sanity check
+ bool is_copy_good = kernel_p && source_p && dest_p && buffer_.Data();
+ DCHECK(is_copy_good);
+ if (!is_copy_good)
+ return;
+
+ float* input_p = buffer_.Data() + input_block_size_;
+
+ // Copy samples to 2nd half of input buffer.
+ memcpy(input_p, source_p, sizeof(float) * frames_to_process);
+
+ Conv(input_p - kernel_size + 1, 1, kernel_p + kernel_size - 1, -1, dest_p, 1,
+ frames_to_process, kernel_size, &prepared_convolution_kernel_);
+
+ // Copy 2nd half of input buffer to 1st half.
+ memcpy(buffer_.Data(), input_p, sizeof(float) * frames_to_process);
+}
+
+void DirectConvolver::Reset() {
+ buffer_.Zero();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/direct_convolver.h b/chromium/third_party/blink/renderer/platform/audio/direct_convolver.h
new file mode 100644
index 00000000000..79617a64b6e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/direct_convolver.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2012 Intel 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_DIRECT_CONVOLVER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_DIRECT_CONVOLVER_H_
+
+#include <memory>
+
+#include "third_party/blink/renderer/platform/audio/audio_array.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT DirectConvolver {
+ USING_FAST_MALLOC(DirectConvolver);
+ WTF_MAKE_NONCOPYABLE(DirectConvolver);
+
+ public:
+ DirectConvolver(size_t input_block_size,
+ std::unique_ptr<AudioFloatArray> convolution_kernel);
+
+ void Process(const float* source_p, float* dest_p, size_t frames_to_process);
+
+ void Reset();
+
+ size_t ConvolutionKernelSize() const { return convolution_kernel_->size(); }
+
+ private:
+ size_t input_block_size_;
+
+ AudioFloatArray buffer_;
+ std::unique_ptr<AudioFloatArray> convolution_kernel_;
+ AudioFloatArray prepared_convolution_kernel_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_DIRECT_CONVOLVER_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/distance_effect.cc b/chromium/third_party/blink/renderer/platform/audio/distance_effect.cc
new file mode 100644
index 00000000000..7f10ec50c0d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/distance_effect.cc
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/audio/distance_effect.h"
+
+#include <math.h>
+#include <algorithm>
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+namespace blink {
+
+DistanceEffect::DistanceEffect()
+ : model_(kModelInverse),
+ ref_distance_(1.0),
+ max_distance_(10000.0),
+ rolloff_factor_(1.0) {}
+
+double DistanceEffect::Gain(double distance) {
+ switch (model_) {
+ case kModelLinear:
+ return LinearGain(distance);
+ case kModelInverse:
+ return InverseGain(distance);
+ case kModelExponential:
+ return ExponentialGain(distance);
+ }
+ NOTREACHED();
+ return 0.0;
+}
+
+double DistanceEffect::LinearGain(double distance) {
+ // Clamp refDistance and distance according to the spec.
+ double dref = std::min(ref_distance_, max_distance_);
+ double dmax = std::max(ref_distance_, max_distance_);
+ distance = clampTo(distance, dref, dmax);
+
+ if (dref == dmax)
+ return 1 - rolloff_factor_;
+
+ // We want a gain that decreases linearly from m_refDistance to
+ // m_maxDistance. The gain is 1 at m_refDistance.
+ return (1.0 - clampTo(rolloff_factor_, 0.0, 1.0) * (distance - dref) /
+ (dmax - dref));
+}
+
+double DistanceEffect::InverseGain(double distance) {
+ if (ref_distance_ == 0)
+ return 0;
+
+ // Clamp distance according to spec
+ distance = clampTo(distance, ref_distance_);
+
+ return ref_distance_ / (ref_distance_ + clampTo(rolloff_factor_, 0.0) *
+ (distance - ref_distance_));
+}
+
+double DistanceEffect::ExponentialGain(double distance) {
+ if (ref_distance_ == 0)
+ return 0;
+
+ // Clamp distance according to spec
+ distance = clampTo(distance, ref_distance_);
+
+ return pow(distance / ref_distance_, -clampTo(rolloff_factor_, 0.0));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/distance_effect.h b/chromium/third_party/blink/renderer/platform/audio/distance_effect.h
new file mode 100644
index 00000000000..ef0e641616f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/distance_effect.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_DISTANCE_EFFECT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_DISTANCE_EFFECT_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+// Distance models are defined according to the OpenAL specification:
+// http://connect.creativelabs.com/openal/Documentation/OpenAL%201.1%20Specification.htm.
+
+class PLATFORM_EXPORT DistanceEffect {
+ DISALLOW_NEW();
+
+ public:
+ enum ModelType { kModelLinear = 0, kModelInverse = 1, kModelExponential = 2 };
+
+ DistanceEffect();
+
+ // Returns scalar gain for the given distance the current distance model is
+ // used
+ double Gain(double distance);
+
+ ModelType Model() { return model_; }
+
+ void SetModel(ModelType model) { model_ = model; }
+
+ // Distance params
+ void SetRefDistance(double ref_distance) { ref_distance_ = ref_distance; }
+ void SetMaxDistance(double max_distance) { max_distance_ = max_distance; }
+ void SetRolloffFactor(double rolloff_factor) {
+ rolloff_factor_ = rolloff_factor;
+ }
+
+ double RefDistance() const { return ref_distance_; }
+ double MaxDistance() const { return max_distance_; }
+ double RolloffFactor() const { return rolloff_factor_; }
+
+ protected:
+ double LinearGain(double distance);
+ double InverseGain(double distance);
+ double ExponentialGain(double distance);
+
+ ModelType model_;
+ double ref_distance_;
+ double max_distance_;
+ double rolloff_factor_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_DISTANCE_EFFECT_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/down_sampler.cc b/chromium/third_party/blink/renderer/platform/audio/down_sampler.cc
new file mode 100644
index 00000000000..90415979257
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/down_sampler.cc
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/renderer/platform/audio/down_sampler.h"
+
+#include <memory>
+
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+namespace blink {
+
+namespace {
+
+// Computes ideal band-limited half-band filter coefficients.
+// In other words, filter out all frequencies higher than 0.25 * Nyquist.
+std::unique_ptr<AudioFloatArray> MakeReducedKernel(size_t size) {
+ auto reduced_kernel = std::make_unique<AudioFloatArray>(size / 2);
+
+ // Blackman window parameters.
+ double alpha = 0.16;
+ double a0 = 0.5 * (1.0 - alpha);
+ double a1 = 0.5;
+ double a2 = 0.5 * alpha;
+
+ int n = size;
+ int half_size = n / 2;
+
+ // Half-band filter.
+ double sinc_scale_factor = 0.5;
+
+ // Compute only the odd terms because the even ones are zero, except right in
+ // the middle at halfSize, which is 0.5 and we'll handle specially during
+ // processing after doing the main convolution using m_reducedKernel.
+ for (int i = 1; i < n; i += 2) {
+ // Compute the sinc() with offset.
+ double s = sinc_scale_factor * piDouble * (i - half_size);
+ double sinc = !s ? 1.0 : sin(s) / s;
+ sinc *= sinc_scale_factor;
+
+ // Compute Blackman window, matching the offset of the sinc().
+ double x = static_cast<double>(i) / n;
+ double window =
+ a0 - a1 * cos(twoPiDouble * x) + a2 * cos(twoPiDouble * 2.0 * x);
+
+ // Window the sinc() function.
+ // Then store only the odd terms in the kernel.
+ // In a sense, this is shifting forward in time by one sample-frame at the
+ // destination sample-rate.
+ (*reduced_kernel)[(i - 1) / 2] = sinc * window;
+ }
+
+ return reduced_kernel;
+}
+
+} // namespace
+
+DownSampler::DownSampler(size_t input_block_size)
+ : input_block_size_(input_block_size),
+ convolver_(input_block_size / 2, // runs at 1/2 source sample-rate
+ MakeReducedKernel(kDefaultKernelSize)),
+ temp_buffer_(input_block_size / 2),
+ input_buffer_(input_block_size * 2) {}
+
+void DownSampler::Process(const float* source_p,
+ float* dest_p,
+ size_t source_frames_to_process) {
+ bool is_input_block_size_good = source_frames_to_process == input_block_size_;
+ DCHECK(is_input_block_size_good);
+ if (!is_input_block_size_good)
+ return;
+
+ size_t dest_frames_to_process = source_frames_to_process / 2;
+
+ bool is_temp_buffer_good = dest_frames_to_process == temp_buffer_.size();
+ DCHECK(is_temp_buffer_good);
+ if (!is_temp_buffer_good)
+ return;
+
+ bool is_reduced_kernel_good =
+ convolver_.ConvolutionKernelSize() == kDefaultKernelSize / 2;
+ DCHECK(is_reduced_kernel_good);
+ if (!is_reduced_kernel_good)
+ return;
+
+ size_t half_size = kDefaultKernelSize / 2;
+
+ // Copy source samples to 2nd half of input buffer.
+ bool is_input_buffer_good =
+ input_buffer_.size() == source_frames_to_process * 2 &&
+ half_size <= source_frames_to_process;
+ DCHECK(is_input_buffer_good);
+ if (!is_input_buffer_good)
+ return;
+
+ float* input_p = input_buffer_.Data() + source_frames_to_process;
+ memcpy(input_p, source_p, sizeof(float) * source_frames_to_process);
+
+ // Copy the odd sample-frames from sourceP, delayed by one sample-frame
+ // (destination sample-rate) to match shifting forward in time in
+ // m_reducedKernel.
+ float* odd_samples_p = temp_buffer_.Data();
+ for (unsigned i = 0; i < dest_frames_to_process; ++i)
+ odd_samples_p[i] = *((input_p - 1) + i * 2);
+
+ // Actually process oddSamplesP with m_reducedKernel for efficiency.
+ // The theoretical kernel is double this size with 0 values for even terms
+ // (except center).
+ convolver_.Process(odd_samples_p, dest_p, dest_frames_to_process);
+
+ // Now, account for the 0.5 term right in the middle of the kernel.
+ // This amounts to a delay-line of length halfSize (at the source
+ // sample-rate), scaled by 0.5.
+
+ // Sum into the destination.
+ for (unsigned i = 0; i < dest_frames_to_process; ++i)
+ dest_p[i] += 0.5 * *((input_p - half_size) + i * 2);
+
+ // Copy 2nd half of input buffer to 1st half.
+ memcpy(input_buffer_.Data(), input_p,
+ sizeof(float) * source_frames_to_process);
+}
+
+void DownSampler::Reset() {
+ convolver_.Reset();
+ input_buffer_.Zero();
+}
+
+size_t DownSampler::LatencyFrames() const {
+ // Divide by two since this is a linear phase kernel and the delay is at the
+ // center of the kernel.
+ return convolver_.ConvolutionKernelSize() / 2;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/down_sampler.h b/chromium/third_party/blink/renderer/platform/audio/down_sampler.h
new file mode 100644
index 00000000000..21b5f127f8a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/down_sampler.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_DOWN_SAMPLER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_DOWN_SAMPLER_H_
+
+#include "third_party/blink/renderer/platform/audio/audio_array.h"
+#include "third_party/blink/renderer/platform/audio/direct_convolver.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+// DownSampler down-samples the source stream by a factor of 2x.
+
+class PLATFORM_EXPORT DownSampler {
+ USING_FAST_MALLOC(DownSampler);
+ WTF_MAKE_NONCOPYABLE(DownSampler);
+
+ public:
+ explicit DownSampler(size_t input_block_size);
+
+ // The destination buffer |destP| is of size sourceFramesToProcess / 2.
+ void Process(const float* source_p,
+ float* dest_p,
+ size_t source_frames_to_process);
+
+ void Reset();
+
+ // Latency based on the destination sample-rate.
+ size_t LatencyFrames() const;
+
+ private:
+ enum { kDefaultKernelSize = 256 };
+
+ size_t input_block_size_;
+
+ // Half-band filter.
+ DirectConvolver convolver_;
+
+ AudioFloatArray temp_buffer_;
+
+ // Used as delay-line (FIR filter history) for the input samples to account
+ // for the 0.5 term right in the middle of the kernel.
+ AudioFloatArray input_buffer_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_DOWN_SAMPLER_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/dynamics_compressor.cc b/chromium/third_party/blink/renderer/platform/audio/dynamics_compressor.cc
new file mode 100644
index 00000000000..3cec9e8aba8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/dynamics_compressor.cc
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/audio/audio_bus.h"
+#include "third_party/blink/renderer/platform/audio/audio_utilities.h"
+#include "third_party/blink/renderer/platform/audio/dynamics_compressor.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+namespace blink {
+
+using namespace AudioUtilities;
+
+DynamicsCompressor::DynamicsCompressor(float sample_rate,
+ unsigned number_of_channels)
+ : number_of_channels_(number_of_channels),
+ sample_rate_(sample_rate),
+ compressor_(sample_rate, number_of_channels) {
+ // Uninitialized state - for parameter recalculation.
+ last_filter_stage_ratio_ = -1;
+ last_anchor_ = -1;
+ last_filter_stage_gain_ = -1;
+
+ SetNumberOfChannels(number_of_channels);
+ InitializeParameters();
+}
+
+void DynamicsCompressor::SetParameterValue(unsigned parameter_id, float value) {
+ DCHECK_LT(parameter_id, static_cast<unsigned>(kParamLast));
+ if (parameter_id < kParamLast)
+ parameters_[parameter_id] = value;
+}
+
+void DynamicsCompressor::InitializeParameters() {
+ // Initializes compressor to default values.
+
+ parameters_[kParamThreshold] = -24; // dB
+ parameters_[kParamKnee] = 30; // dB
+ parameters_[kParamRatio] = 12; // unit-less
+ parameters_[kParamAttack] = 0.003f; // seconds
+ parameters_[kParamRelease] = 0.250f; // seconds
+ parameters_[kParamPreDelay] = 0.006f; // seconds
+
+ // Release zone values 0 -> 1.
+ parameters_[kParamReleaseZone1] = 0.09f;
+ parameters_[kParamReleaseZone2] = 0.16f;
+ parameters_[kParamReleaseZone3] = 0.42f;
+ parameters_[kParamReleaseZone4] = 0.98f;
+
+ parameters_[kParamFilterStageGain] = 4.4f; // dB
+ parameters_[kParamFilterStageRatio] = 2;
+ parameters_[kParamFilterAnchor] = 15000 / Nyquist();
+
+ parameters_[kParamPostGain] = 0; // dB
+ parameters_[kParamReduction] = 0; // dB
+
+ // Linear crossfade (0 -> 1).
+ parameters_[kParamEffectBlend] = 1;
+}
+
+float DynamicsCompressor::ParameterValue(unsigned parameter_id) {
+ DCHECK_LT(parameter_id, static_cast<unsigned>(kParamLast));
+ return parameters_[parameter_id];
+}
+
+void DynamicsCompressor::Process(const AudioBus* source_bus,
+ AudioBus* destination_bus,
+ unsigned frames_to_process) {
+ // Though numberOfChannels is retrived from destinationBus, we still name it
+ // numberOfChannels instead of numberOfDestinationChannels. It's because we
+ // internally match sourceChannels's size to destinationBus by channel
+ // up/down mix. Thus we need numberOfChannels
+ // to do the loop work for both m_sourceChannels and m_destinationChannels.
+
+ unsigned number_of_channels = destination_bus->NumberOfChannels();
+ unsigned number_of_source_channels = source_bus->NumberOfChannels();
+
+ DCHECK_EQ(number_of_channels, number_of_channels_);
+ DCHECK(number_of_source_channels);
+
+ if (number_of_channels != number_of_channels_ || !number_of_source_channels) {
+ destination_bus->Zero();
+ return;
+ }
+
+ switch (number_of_channels) {
+ case 2: // stereo
+ source_channels_[0] = source_bus->Channel(0)->Data();
+
+ if (number_of_source_channels > 1)
+ source_channels_[1] = source_bus->Channel(1)->Data();
+ else
+ // Simply duplicate mono channel input data to right channel for stereo
+ // processing.
+ source_channels_[1] = source_channels_[0];
+
+ break;
+ default:
+ // FIXME : support other number of channels.
+ NOTREACHED();
+ destination_bus->Zero();
+ return;
+ }
+
+ for (unsigned i = 0; i < number_of_channels; ++i)
+ destination_channels_[i] = destination_bus->Channel(i)->MutableData();
+
+ float filter_stage_gain = ParameterValue(kParamFilterStageGain);
+ float filter_stage_ratio = ParameterValue(kParamFilterStageRatio);
+ float anchor = ParameterValue(kParamFilterAnchor);
+
+ if (filter_stage_gain != last_filter_stage_gain_ ||
+ filter_stage_ratio != last_filter_stage_ratio_ ||
+ anchor != last_anchor_) {
+ last_filter_stage_gain_ = filter_stage_gain;
+ last_filter_stage_ratio_ = filter_stage_ratio;
+ last_anchor_ = anchor;
+ }
+
+ float db_threshold = ParameterValue(kParamThreshold);
+ float db_knee = ParameterValue(kParamKnee);
+ float ratio = ParameterValue(kParamRatio);
+ float attack_time = ParameterValue(kParamAttack);
+ float release_time = ParameterValue(kParamRelease);
+ float pre_delay_time = ParameterValue(kParamPreDelay);
+
+ // This is effectively a master volume on the compressed signal
+ // (pre-blending).
+ float db_post_gain = ParameterValue(kParamPostGain);
+
+ // Linear blending value from dry to completely processed (0 -> 1)
+ // 0 means the signal is completely unprocessed.
+ // 1 mixes in only the compressed signal.
+ float effect_blend = ParameterValue(kParamEffectBlend);
+
+ float release_zone1 = ParameterValue(kParamReleaseZone1);
+ float release_zone2 = ParameterValue(kParamReleaseZone2);
+ float release_zone3 = ParameterValue(kParamReleaseZone3);
+ float release_zone4 = ParameterValue(kParamReleaseZone4);
+
+ // Apply compression to the source signal.
+ compressor_.Process(source_channels_.get(), destination_channels_.get(),
+ number_of_channels, frames_to_process,
+
+ db_threshold, db_knee, ratio, attack_time, release_time,
+ pre_delay_time, db_post_gain, effect_blend,
+
+ release_zone1, release_zone2, release_zone3,
+ release_zone4);
+
+ // Update the compression amount.
+ SetParameterValue(kParamReduction, compressor_.MeteringGain());
+}
+
+void DynamicsCompressor::Reset() {
+ last_filter_stage_ratio_ = -1; // for recalc
+ last_anchor_ = -1;
+ last_filter_stage_gain_ = -1;
+
+ compressor_.Reset();
+}
+
+void DynamicsCompressor::SetNumberOfChannels(unsigned number_of_channels) {
+ source_channels_ = std::make_unique<const float* []>(number_of_channels);
+ destination_channels_ = std::make_unique<float* []>(number_of_channels);
+
+ compressor_.SetNumberOfChannels(number_of_channels);
+ number_of_channels_ = number_of_channels;
+}
+
+double DynamicsCompressor::TailTime() const {
+ return compressor_.TailTime();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/dynamics_compressor.h b/chromium/third_party/blink/renderer/platform/audio/dynamics_compressor.h
new file mode 100644
index 00000000000..aa99c54deaa
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/dynamics_compressor.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_DYNAMICS_COMPRESSOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_DYNAMICS_COMPRESSOR_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/audio/audio_array.h"
+#include "third_party/blink/renderer/platform/audio/dynamics_compressor_kernel.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class AudioBus;
+
+// DynamicsCompressor implements a flexible audio dynamics compression effect
+// such as is commonly used in musical production and game audio. It lowers the
+// volume of the loudest parts of the signal and raises the volume of the
+// softest parts, making the sound richer, fuller, and more controlled.
+
+class PLATFORM_EXPORT DynamicsCompressor {
+ USING_FAST_MALLOC(DynamicsCompressor);
+ WTF_MAKE_NONCOPYABLE(DynamicsCompressor);
+
+ public:
+ enum {
+ kParamThreshold,
+ kParamKnee,
+ kParamRatio,
+ kParamAttack,
+ kParamRelease,
+ kParamPreDelay,
+ kParamReleaseZone1,
+ kParamReleaseZone2,
+ kParamReleaseZone3,
+ kParamReleaseZone4,
+ kParamPostGain,
+ kParamFilterStageGain,
+ kParamFilterStageRatio,
+ kParamFilterAnchor,
+ kParamEffectBlend,
+ kParamReduction,
+ kParamLast
+ };
+
+ DynamicsCompressor(float sample_rate, unsigned number_of_channels);
+
+ void Process(const AudioBus* source_bus,
+ AudioBus* destination_bus,
+ unsigned frames_to_process);
+ void Reset();
+ void SetNumberOfChannels(unsigned);
+
+ void SetParameterValue(unsigned parameter_id, float value);
+ float ParameterValue(unsigned parameter_id);
+
+ float SampleRate() const { return sample_rate_; }
+ float Nyquist() const { return sample_rate_ / 2; }
+
+ double TailTime() const;
+ double LatencyTime() const {
+ return compressor_.LatencyFrames() / static_cast<double>(SampleRate());
+ }
+ bool RequiresTailProcessing() const {
+ // Always return true even if the tail time and latency might both be zero.
+ return true;
+ }
+
+ protected:
+ unsigned number_of_channels_;
+
+ // m_parameters holds the tweakable compressor parameters.
+ float parameters_[kParamLast];
+ void InitializeParameters();
+
+ float sample_rate_;
+
+ // Emphasis filter controls.
+ float last_filter_stage_ratio_;
+ float last_anchor_;
+ float last_filter_stage_gain_;
+
+ std::unique_ptr<const float* []> source_channels_;
+ std::unique_ptr<float* []> destination_channels_;
+
+ // The core compressor.
+ DynamicsCompressorKernel compressor_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_DYNAMICS_COMPRESSOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/dynamics_compressor_kernel.cc b/chromium/third_party/blink/renderer/platform/audio/dynamics_compressor_kernel.cc
new file mode 100644
index 00000000000..94ad47bd512
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/dynamics_compressor_kernel.cc
@@ -0,0 +1,509 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/audio/dynamics_compressor_kernel.h"
+
+#include <algorithm>
+#include <cmath>
+#include "third_party/blink/renderer/platform/audio/audio_utilities.h"
+#include "third_party/blink/renderer/platform/audio/denormal_disabler.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+namespace blink {
+
+using namespace AudioUtilities;
+
+// Metering hits peaks instantly, but releases this fast (in seconds).
+const float kMeteringReleaseTimeConstant = 0.325f;
+
+const float kUninitializedValue = -1;
+
+DynamicsCompressorKernel::DynamicsCompressorKernel(float sample_rate,
+ unsigned number_of_channels)
+ : sample_rate_(sample_rate),
+ last_pre_delay_frames_(kDefaultPreDelayFrames),
+ pre_delay_read_index_(0),
+ pre_delay_write_index_(kDefaultPreDelayFrames),
+ ratio_(kUninitializedValue),
+ slope_(kUninitializedValue),
+ linear_threshold_(kUninitializedValue),
+ db_threshold_(kUninitializedValue),
+ db_knee_(kUninitializedValue),
+ knee_threshold_(kUninitializedValue),
+ knee_threshold_db_(kUninitializedValue),
+ yknee_threshold_db_(kUninitializedValue),
+ knee_(kUninitializedValue) {
+ SetNumberOfChannels(number_of_channels);
+
+ // Initializes most member variables
+ Reset();
+
+ metering_release_k_ = static_cast<float>(DiscreteTimeConstantForSampleRate(
+ kMeteringReleaseTimeConstant, sample_rate));
+}
+
+void DynamicsCompressorKernel::SetNumberOfChannels(
+ unsigned number_of_channels) {
+ if (pre_delay_buffers_.size() == number_of_channels)
+ return;
+
+ pre_delay_buffers_.clear();
+ for (unsigned i = 0; i < number_of_channels; ++i) {
+ pre_delay_buffers_.push_back(
+ std::make_unique<AudioFloatArray>(kMaxPreDelayFrames));
+ }
+}
+
+void DynamicsCompressorKernel::SetPreDelayTime(float pre_delay_time) {
+ // Re-configure look-ahead section pre-delay if delay time has changed.
+ unsigned pre_delay_frames = pre_delay_time * SampleRate();
+ if (pre_delay_frames > kMaxPreDelayFrames - 1)
+ pre_delay_frames = kMaxPreDelayFrames - 1;
+
+ if (last_pre_delay_frames_ != pre_delay_frames) {
+ last_pre_delay_frames_ = pre_delay_frames;
+ for (unsigned i = 0; i < pre_delay_buffers_.size(); ++i)
+ pre_delay_buffers_[i]->Zero();
+
+ pre_delay_read_index_ = 0;
+ pre_delay_write_index_ = pre_delay_frames;
+ }
+}
+
+// Exponential curve for the knee.
+// It is 1st derivative matched at m_linearThreshold and asymptotically
+// approaches the value m_linearThreshold + 1 / k.
+float DynamicsCompressorKernel::KneeCurve(float x, float k) {
+ // Linear up to threshold.
+ if (x < linear_threshold_)
+ return x;
+
+ return linear_threshold_ + (1 - expf(-k * (x - linear_threshold_))) / k;
+}
+
+// Full compression curve with constant ratio after knee.
+float DynamicsCompressorKernel::Saturate(float x, float k) {
+ float y;
+
+ if (x < knee_threshold_)
+ y = KneeCurve(x, k);
+ else {
+ // Constant ratio after knee.
+ float x_db = LinearToDecibels(x);
+ float y_db = yknee_threshold_db_ + slope_ * (x_db - knee_threshold_db_);
+
+ y = DecibelsToLinear(y_db);
+ }
+
+ return y;
+}
+
+// Approximate 1st derivative with input and output expressed in dB.
+// This slope is equal to the inverse of the compression "ratio".
+// In other words, a compression ratio of 20 would be a slope of 1/20.
+float DynamicsCompressorKernel::SlopeAt(float x, float k) {
+ if (x < linear_threshold_)
+ return 1;
+
+ float x2 = x * 1.001;
+
+ float x_db = LinearToDecibels(x);
+ float x2_db = LinearToDecibels(x2);
+
+ float y_db = LinearToDecibels(KneeCurve(x, k));
+ float y2_db = LinearToDecibels(KneeCurve(x2, k));
+
+ float m = (y2_db - y_db) / (x2_db - x_db);
+
+ return m;
+}
+
+float DynamicsCompressorKernel::KAtSlope(float desired_slope) {
+ float x_db = db_threshold_ + db_knee_;
+ float x = DecibelsToLinear(x_db);
+
+ // Approximate k given initial values.
+ float min_k = 0.1;
+ float max_k = 10000;
+ float k = 5;
+
+ for (int i = 0; i < 15; ++i) {
+ // A high value for k will more quickly asymptotically approach a slope of
+ // 0.
+ float slope = SlopeAt(x, k);
+
+ if (slope < desired_slope) {
+ // k is too high.
+ max_k = k;
+ } else {
+ // k is too low.
+ min_k = k;
+ }
+
+ // Re-calculate based on geometric mean.
+ k = sqrtf(min_k * max_k);
+ }
+
+ return k;
+}
+
+float DynamicsCompressorKernel::UpdateStaticCurveParameters(float db_threshold,
+ float db_knee,
+ float ratio) {
+ if (db_threshold != db_threshold_ || db_knee != db_knee_ || ratio != ratio_) {
+ // Threshold and knee.
+ db_threshold_ = db_threshold;
+ linear_threshold_ = DecibelsToLinear(db_threshold);
+ db_knee_ = db_knee;
+
+ // Compute knee parameters.
+ ratio_ = ratio;
+ slope_ = 1 / ratio_;
+
+ float k = KAtSlope(1 / ratio_);
+
+ knee_threshold_db_ = db_threshold + db_knee;
+ knee_threshold_ = DecibelsToLinear(knee_threshold_db_);
+
+ yknee_threshold_db_ = LinearToDecibels(KneeCurve(knee_threshold_, k));
+
+ knee_ = k;
+ }
+ return knee_;
+}
+
+void DynamicsCompressorKernel::Process(
+ const float* source_channels[],
+ float* destination_channels[],
+ unsigned number_of_channels,
+ unsigned frames_to_process,
+
+ float db_threshold,
+ float db_knee,
+ float ratio,
+ float attack_time,
+ float release_time,
+ float pre_delay_time,
+ float db_post_gain,
+ float effect_blend, /* equal power crossfade */
+
+ float release_zone1,
+ float release_zone2,
+ float release_zone3,
+ float release_zone4) {
+ DCHECK_EQ(pre_delay_buffers_.size(), number_of_channels);
+
+ float sample_rate = this->SampleRate();
+
+ float dry_mix = 1 - effect_blend;
+ float wet_mix = effect_blend;
+
+ float k = UpdateStaticCurveParameters(db_threshold, db_knee, ratio);
+
+ // Makeup gain.
+ float full_range_gain = Saturate(1, k);
+ float full_range_makeup_gain = 1 / full_range_gain;
+
+ // Empirical/perceptual tuning.
+ full_range_makeup_gain = powf(full_range_makeup_gain, 0.6f);
+
+ float master_linear_gain =
+ DecibelsToLinear(db_post_gain) * full_range_makeup_gain;
+
+ // Attack parameters.
+ attack_time = std::max(0.001f, attack_time);
+ float attack_frames = attack_time * sample_rate;
+
+ // Release parameters.
+ float release_frames = sample_rate * release_time;
+
+ // Detector release time.
+ float sat_release_time = 0.0025f;
+ float sat_release_frames = sat_release_time * sample_rate;
+
+ // Create a smooth function which passes through four points.
+
+ // Polynomial of the form
+ // y = a + b*x + c*x^2 + d*x^3 + e*x^4;
+
+ float y1 = release_frames * release_zone1;
+ float y2 = release_frames * release_zone2;
+ float y3 = release_frames * release_zone3;
+ float y4 = release_frames * release_zone4;
+
+ // All of these coefficients were derived for 4th order polynomial curve
+ // fitting where the y values match the evenly spaced x values as follows:
+ // (y1 : x == 0, y2 : x == 1, y3 : x == 2, y4 : x == 3)
+ float a = 0.9999999999999998f * y1 + 1.8432219684323923e-16f * y2 -
+ 1.9373394351676423e-16f * y3 + 8.824516011816245e-18f * y4;
+ float b = -1.5788320352845888f * y1 + 2.3305837032074286f * y2 -
+ 0.9141194204840429f * y3 + 0.1623677525612032f * y4;
+ float c = 0.5334142869106424f * y1 - 1.272736789213631f * y2 +
+ 0.9258856042207512f * y3 - 0.18656310191776226f * y4;
+ float d = 0.08783463138207234f * y1 - 0.1694162967925622f * y2 +
+ 0.08588057951595272f * y3 - 0.00429891410546283f * y4;
+ float e = -0.042416883008123074f * y1 + 0.1115693827987602f * y2 -
+ 0.09764676325265872f * y3 + 0.028494263462021576f * y4;
+
+ // x ranges from 0 -> 3 0 1 2 3
+ // -15 -10 -5 0db
+
+ // y calculates adaptive release frames depending on the amount of
+ // compression.
+
+ SetPreDelayTime(pre_delay_time);
+
+ const int kNDivisionFrames = 32;
+
+ const int n_divisions = frames_to_process / kNDivisionFrames;
+
+ unsigned frame_index = 0;
+ for (int i = 0; i < n_divisions; ++i) {
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ // Calculate desired gain
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ // Fix gremlins.
+ if (std::isnan(detector_average_))
+ detector_average_ = 1;
+ if (std::isinf(detector_average_))
+ detector_average_ = 1;
+
+ float desired_gain = detector_average_;
+
+ // Pre-warp so we get desiredGain after sin() warp below.
+ float scaled_desired_gain = asinf(desired_gain) / (piOverTwoFloat);
+
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ // Deal with envelopes
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ // envelopeRate is the rate we slew from current compressor level to the
+ // desired level. The exact rate depends on if we're attacking or
+ // releasing and by how much.
+ float envelope_rate;
+
+ bool is_releasing = scaled_desired_gain > compressor_gain_;
+
+ // compressionDiffDb is the difference between current compression level and
+ // the desired level.
+ float compression_diff_db =
+ LinearToDecibels(compressor_gain_ / scaled_desired_gain);
+
+ if (is_releasing) {
+ // Release mode - compressionDiffDb should be negative dB
+ max_attack_compression_diff_db_ = -1;
+
+ // Fix gremlins.
+ if (std::isnan(compression_diff_db))
+ compression_diff_db = -1;
+ if (std::isinf(compression_diff_db))
+ compression_diff_db = -1;
+
+ // Adaptive release - higher compression (lower compressionDiffDb)
+ // releases faster.
+
+ // Contain within range: -12 -> 0 then scale to go from 0 -> 3
+ float x = compression_diff_db;
+ x = clampTo(x, -12.0f, 0.0f);
+ x = 0.25f * (x + 12);
+
+ // Compute adaptive release curve using 4th order polynomial.
+ // Normal values for the polynomial coefficients would create a
+ // monotonically increasing function.
+ float x2 = x * x;
+ float x3 = x2 * x;
+ float x4 = x2 * x2;
+ float release_frames = a + b * x + c * x2 + d * x3 + e * x4;
+
+#define kSpacingDb 5
+ float db_per_frame = kSpacingDb / release_frames;
+
+ envelope_rate = DecibelsToLinear(db_per_frame);
+ } else {
+ // Attack mode - compressionDiffDb should be positive dB
+
+ // Fix gremlins.
+ if (std::isnan(compression_diff_db))
+ compression_diff_db = 1;
+ if (std::isinf(compression_diff_db))
+ compression_diff_db = 1;
+
+ // As long as we're still in attack mode, use a rate based off
+ // the largest compressionDiffDb we've encountered so far.
+ if (max_attack_compression_diff_db_ == -1 ||
+ max_attack_compression_diff_db_ < compression_diff_db)
+ max_attack_compression_diff_db_ = compression_diff_db;
+
+ float eff_atten_diff_db = std::max(0.5f, max_attack_compression_diff_db_);
+
+ float x = 0.25f / eff_atten_diff_db;
+ envelope_rate = 1 - powf(x, 1 / attack_frames);
+ }
+
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ // Inner loop - calculate shaped power average - apply compression.
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ {
+ int pre_delay_read_index = pre_delay_read_index_;
+ int pre_delay_write_index = pre_delay_write_index_;
+ float detector_average = detector_average_;
+ float compressor_gain = compressor_gain_;
+
+ int loop_frames = kNDivisionFrames;
+ while (loop_frames--) {
+ float compressor_input = 0;
+
+ // Predelay signal, computing compression amount from un-delayed
+ // version.
+ for (unsigned i = 0; i < number_of_channels; ++i) {
+ float* delay_buffer = pre_delay_buffers_[i]->Data();
+ float undelayed_source = source_channels[i][frame_index];
+ delay_buffer[pre_delay_write_index] = undelayed_source;
+
+ float abs_undelayed_source =
+ undelayed_source > 0 ? undelayed_source : -undelayed_source;
+ if (compressor_input < abs_undelayed_source)
+ compressor_input = abs_undelayed_source;
+ }
+
+ // Calculate shaped power on undelayed input.
+
+ float scaled_input = compressor_input;
+ float abs_input = scaled_input > 0 ? scaled_input : -scaled_input;
+
+ // Put through shaping curve.
+ // This is linear up to the threshold, then enters a "knee" portion
+ // followed by the "ratio" portion. The transition from the threshold
+ // to the knee is smooth (1st derivative matched). The transition from
+ // the knee to the ratio portion is smooth (1st derivative matched).
+ float shaped_input = Saturate(abs_input, k);
+
+ float attenuation = abs_input <= 0.0001f ? 1 : shaped_input / abs_input;
+
+ float attenuation_db = -LinearToDecibels(attenuation);
+ attenuation_db = std::max(2.0f, attenuation_db);
+
+ float db_per_frame = attenuation_db / sat_release_frames;
+
+ float sat_release_rate = DecibelsToLinear(db_per_frame) - 1;
+
+ bool is_release = (attenuation > detector_average);
+ float rate = is_release ? sat_release_rate : 1;
+
+ detector_average += (attenuation - detector_average) * rate;
+ detector_average = std::min(1.0f, detector_average);
+
+ // Fix gremlins.
+ if (std::isnan(detector_average))
+ detector_average = 1;
+ if (std::isinf(detector_average))
+ detector_average = 1;
+
+ // Exponential approach to desired gain.
+ if (envelope_rate < 1) {
+ // Attack - reduce gain to desired.
+ compressor_gain +=
+ (scaled_desired_gain - compressor_gain) * envelope_rate;
+ } else {
+ // Release - exponentially increase gain to 1.0
+ compressor_gain *= envelope_rate;
+ compressor_gain = std::min(1.0f, compressor_gain);
+ }
+
+ // Warp pre-compression gain to smooth out sharp exponential transition
+ // points.
+ float post_warp_compressor_gain =
+ sinf(piOverTwoFloat * compressor_gain);
+
+ // Calculate total gain using master gain and effect blend.
+ float total_gain =
+ dry_mix + wet_mix * master_linear_gain * post_warp_compressor_gain;
+
+ // Calculate metering.
+ float db_real_gain = 20 * std::log10(post_warp_compressor_gain);
+ if (db_real_gain < metering_gain_)
+ metering_gain_ = db_real_gain;
+ else
+ metering_gain_ +=
+ (db_real_gain - metering_gain_) * metering_release_k_;
+
+ // Apply final gain.
+ for (unsigned i = 0; i < number_of_channels; ++i) {
+ float* delay_buffer = pre_delay_buffers_[i]->Data();
+ destination_channels[i][frame_index] =
+ delay_buffer[pre_delay_read_index] * total_gain;
+ }
+
+ frame_index++;
+ pre_delay_read_index =
+ (pre_delay_read_index + 1) & kMaxPreDelayFramesMask;
+ pre_delay_write_index =
+ (pre_delay_write_index + 1) & kMaxPreDelayFramesMask;
+ }
+
+ // Locals back to member variables.
+ pre_delay_read_index_ = pre_delay_read_index;
+ pre_delay_write_index_ = pre_delay_write_index;
+ detector_average_ =
+ DenormalDisabler::FlushDenormalFloatToZero(detector_average);
+ compressor_gain_ =
+ DenormalDisabler::FlushDenormalFloatToZero(compressor_gain);
+ }
+ }
+}
+
+void DynamicsCompressorKernel::Reset() {
+ detector_average_ = 0;
+ compressor_gain_ = 1;
+ metering_gain_ = 1;
+
+ // Predelay section.
+ for (unsigned i = 0; i < pre_delay_buffers_.size(); ++i)
+ pre_delay_buffers_[i]->Zero();
+
+ pre_delay_read_index_ = 0;
+ pre_delay_write_index_ = kDefaultPreDelayFrames;
+
+ max_attack_compression_diff_db_ = -1; // uninitialized state
+}
+
+double DynamicsCompressorKernel::TailTime() const {
+ // The reduction value of the compressor is computed from the gain
+ // using an exponential filter with a time constant of
+ // |kMeteringReleaseTimeConstant|. We need to keep he compressor
+ // running for some time after the inputs go away so that the
+ // reduction value approaches 0. This is a tradeoff between how
+ // long we keep the node alive and how close we approach the final
+ // value. A value of 5 to 10 times the time constant is a
+ // reasonable trade-off.
+ return 5 * kMeteringReleaseTimeConstant;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/dynamics_compressor_kernel.h b/chromium/third_party/blink/renderer/platform/audio/dynamics_compressor_kernel.h
new file mode 100644
index 00000000000..fa93846a71a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/dynamics_compressor_kernel.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_DYNAMICS_COMPRESSOR_KERNEL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_DYNAMICS_COMPRESSOR_KERNEL_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/audio/audio_array.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT DynamicsCompressorKernel {
+ DISALLOW_NEW();
+ WTF_MAKE_NONCOPYABLE(DynamicsCompressorKernel);
+
+ public:
+ DynamicsCompressorKernel(float sample_rate, unsigned number_of_channels);
+
+ void SetNumberOfChannels(unsigned);
+
+ // Performs stereo-linked compression.
+ void Process(const float* source_channels[],
+ float* destination_channels[],
+ unsigned number_of_channels,
+ unsigned frames_to_process,
+
+ float db_threshold,
+ float db_knee,
+ float ratio,
+ float attack_time,
+ float release_time,
+ float pre_delay_time,
+ float db_post_gain,
+ float effect_blend,
+
+ float release_zone1,
+ float release_zone2,
+ float release_zone3,
+ float release_zone4);
+
+ void Reset();
+
+ unsigned LatencyFrames() const { return last_pre_delay_frames_; }
+
+ float SampleRate() const { return sample_rate_; }
+
+ float MeteringGain() const { return metering_gain_; }
+
+ double TailTime() const;
+
+ protected:
+ float sample_rate_;
+
+ float detector_average_;
+ float compressor_gain_;
+
+ // Metering
+ float metering_release_k_;
+ float metering_gain_;
+
+ // Lookahead section.
+ enum { kMaxPreDelayFrames = 1024 };
+ enum { kMaxPreDelayFramesMask = kMaxPreDelayFrames - 1 };
+ enum {
+ kDefaultPreDelayFrames = 256
+ }; // setPreDelayTime() will override this initial value
+ unsigned last_pre_delay_frames_;
+ void SetPreDelayTime(float);
+
+ Vector<std::unique_ptr<AudioFloatArray>> pre_delay_buffers_;
+ int pre_delay_read_index_;
+ int pre_delay_write_index_;
+
+ float max_attack_compression_diff_db_;
+
+ // Static compression curve.
+ float KneeCurve(float x, float k);
+ float Saturate(float x, float k);
+ float SlopeAt(float x, float k);
+ float KAtSlope(float desired_slope);
+
+ float UpdateStaticCurveParameters(float db_threshold,
+ float db_knee,
+ float ratio);
+
+ // Amount of input change in dB required for 1 dB of output change.
+ // This applies to the portion of the curve above m_kneeThresholdDb (see
+ // below).
+ float ratio_;
+ float slope_; // Inverse ratio.
+
+ // The input to output change below the threshold is linear 1:1.
+ float linear_threshold_;
+ float db_threshold_;
+
+ // m_dbKnee is the number of dB above the threshold before we enter the
+ // "ratio" portion of the curve.
+ // m_kneeThresholdDb = m_dbThreshold + m_dbKnee
+ // The portion between m_dbThreshold and m_kneeThresholdDb is the "soft knee"
+ // portion of the curve which transitions smoothly from the linear portion to
+ // the ratio portion.
+ float db_knee_;
+ float knee_threshold_;
+ float knee_threshold_db_;
+ float yknee_threshold_db_;
+
+ // Internal parameter for the knee portion of the curve.
+ float knee_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_DYNAMICS_COMPRESSOR_KERNEL_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/equal_power_panner.cc b/chromium/third_party/blink/renderer/platform/audio/equal_power_panner.cc
new file mode 100644
index 00000000000..c6a80d72e91
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/equal_power_panner.cc
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2010, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/audio/equal_power_panner.h"
+
+#include <algorithm>
+#include <cmath>
+#include "third_party/blink/renderer/platform/audio/audio_bus.h"
+#include "third_party/blink/renderer/platform/audio/audio_utilities.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+namespace blink {
+
+EqualPowerPanner::EqualPowerPanner(float sample_rate)
+ : Panner(kPanningModelEqualPower) {}
+
+void EqualPowerPanner::Pan(double azimuth,
+ double /*elevation*/,
+ const AudioBus* input_bus,
+ AudioBus* output_bus,
+ size_t frames_to_process,
+ AudioBus::ChannelInterpretation) {
+ bool is_input_safe = input_bus &&
+ (input_bus->NumberOfChannels() == 1 ||
+ input_bus->NumberOfChannels() == 2) &&
+ frames_to_process <= input_bus->length();
+ DCHECK(is_input_safe);
+ if (!is_input_safe)
+ return;
+
+ unsigned number_of_input_channels = input_bus->NumberOfChannels();
+
+ bool is_output_safe = output_bus && output_bus->NumberOfChannels() == 2 &&
+ frames_to_process <= output_bus->length();
+ DCHECK(is_output_safe);
+ if (!is_output_safe)
+ return;
+
+ const float* source_l = input_bus->Channel(0)->Data();
+ const float* source_r =
+ number_of_input_channels > 1 ? input_bus->Channel(1)->Data() : source_l;
+ float* destination_l =
+ output_bus->ChannelByType(AudioBus::kChannelLeft)->MutableData();
+ float* destination_r =
+ output_bus->ChannelByType(AudioBus::kChannelRight)->MutableData();
+
+ if (!source_l || !source_r || !destination_l || !destination_r)
+ return;
+
+ // Clamp azimuth to allowed range of -180 -> +180.
+ azimuth = clampTo(azimuth, -180.0, 180.0);
+
+ // Alias the azimuth ranges behind us to in front of us:
+ // -90 -> -180 to -90 -> 0 and 90 -> 180 to 90 -> 0
+ if (azimuth < -90)
+ azimuth = -180 - azimuth;
+ else if (azimuth > 90)
+ azimuth = 180 - azimuth;
+
+ double desired_pan_position;
+ double desired_gain_l;
+ double desired_gain_r;
+
+ if (number_of_input_channels == 1) { // For mono source case.
+ // Pan smoothly from left to right with azimuth going from -90 -> +90
+ // degrees.
+ desired_pan_position = (azimuth + 90) / 180;
+ } else { // For stereo source case.
+ if (azimuth <= 0) { // from -90 -> 0
+ // sourceL -> destL and "equal-power pan" sourceR as in mono case
+ // by transforming the "azimuth" value from -90 -> 0 degrees into the
+ // range -90 -> +90.
+ desired_pan_position = (azimuth + 90) / 90;
+ } else { // from 0 -> +90
+ // sourceR -> destR and "equal-power pan" sourceL as in mono case
+ // by transforming the "azimuth" value from 0 -> +90 degrees into the
+ // range -90 -> +90.
+ desired_pan_position = azimuth / 90;
+ }
+ }
+
+ desired_gain_l = std::cos(piOverTwoDouble * desired_pan_position);
+ desired_gain_r = std::sin(piOverTwoDouble * desired_pan_position);
+
+ int n = frames_to_process;
+
+ if (number_of_input_channels == 1) { // For mono source case.
+ while (n--) {
+ float input_l = *source_l++;
+
+ *destination_l++ = static_cast<float>(input_l * desired_gain_l);
+ *destination_r++ = static_cast<float>(input_l * desired_gain_r);
+ }
+ } else { // For stereo source case.
+ if (azimuth <= 0) { // from -90 -> 0
+ while (n--) {
+ float input_l = *source_l++;
+ float input_r = *source_r++;
+
+ *destination_l++ =
+ static_cast<float>(input_l + input_r * desired_gain_l);
+ *destination_r++ = static_cast<float>(input_r * desired_gain_r);
+ }
+ } else { // from 0 -> +90
+ while (n--) {
+ float input_l = *source_l++;
+ float input_r = *source_r++;
+
+ *destination_l++ = static_cast<float>(input_l * desired_gain_l);
+ *destination_r++ =
+ static_cast<float>(input_r + input_l * desired_gain_r);
+ }
+ }
+ }
+}
+
+void EqualPowerPanner::CalculateDesiredGain(double& desired_gain_l,
+ double& desired_gain_r,
+ double azimuth,
+ int number_of_input_channels) {
+ // Clamp azimuth to allowed range of -180 -> +180.
+ azimuth = clampTo(azimuth, -180.0, 180.0);
+
+ // Alias the azimuth ranges behind us to in front of us:
+ // -90 -> -180 to -90 -> 0 and 90 -> 180 to 90 -> 0
+ if (azimuth < -90)
+ azimuth = -180 - azimuth;
+ else if (azimuth > 90)
+ azimuth = 180 - azimuth;
+
+ double desired_pan_position;
+
+ if (number_of_input_channels == 1) { // For mono source case.
+ // Pan smoothly from left to right with azimuth going from -90 -> +90
+ // degrees.
+ desired_pan_position = (azimuth + 90) / 180;
+ } else { // For stereo source case.
+ if (azimuth <= 0) { // from -90 -> 0
+ // sourceL -> destL and "equal-power pan" sourceR as in mono case
+ // by transforming the "azimuth" value from -90 -> 0 degrees into the
+ // range -90 -> +90.
+ desired_pan_position = (azimuth + 90) / 90;
+ } else { // from 0 -> +90
+ // sourceR -> destR and "equal-power pan" sourceL as in mono case
+ // by transforming the "azimuth" value from 0 -> +90 degrees into the
+ // range -90 -> +90.
+ desired_pan_position = azimuth / 90;
+ }
+ }
+
+ desired_gain_l = std::cos(piOverTwoDouble * desired_pan_position);
+ desired_gain_r = std::sin(piOverTwoDouble * desired_pan_position);
+}
+
+void EqualPowerPanner::PanWithSampleAccurateValues(
+ double* azimuth,
+ double* /*elevation*/,
+ const AudioBus* input_bus,
+ AudioBus* output_bus,
+ size_t frames_to_process,
+ AudioBus::ChannelInterpretation) {
+ bool is_input_safe = input_bus &&
+ (input_bus->NumberOfChannels() == 1 ||
+ input_bus->NumberOfChannels() == 2) &&
+ frames_to_process <= input_bus->length();
+ DCHECK(is_input_safe);
+ if (!is_input_safe)
+ return;
+
+ unsigned number_of_input_channels = input_bus->NumberOfChannels();
+
+ bool is_output_safe = output_bus && output_bus->NumberOfChannels() == 2 &&
+ frames_to_process <= output_bus->length();
+ DCHECK(is_output_safe);
+ if (!is_output_safe)
+ return;
+
+ const float* source_l = input_bus->Channel(0)->Data();
+ const float* source_r =
+ number_of_input_channels > 1 ? input_bus->Channel(1)->Data() : source_l;
+ float* destination_l =
+ output_bus->ChannelByType(AudioBus::kChannelLeft)->MutableData();
+ float* destination_r =
+ output_bus->ChannelByType(AudioBus::kChannelRight)->MutableData();
+
+ if (!source_l || !source_r || !destination_l || !destination_r)
+ return;
+
+ int n = frames_to_process;
+
+ if (number_of_input_channels == 1) { // For mono source case.
+ for (int k = 0; k < n; ++k) {
+ double desired_gain_l;
+ double desired_gain_r;
+ float input_l = *source_l++;
+
+ CalculateDesiredGain(desired_gain_l, desired_gain_r, azimuth[k],
+ number_of_input_channels);
+ *destination_l++ = static_cast<float>(input_l * desired_gain_l);
+ *destination_r++ = static_cast<float>(input_l * desired_gain_r);
+ }
+ } else { // For stereo source case.
+ for (int k = 0; k < n; ++k) {
+ double desired_gain_l;
+ double desired_gain_r;
+
+ CalculateDesiredGain(desired_gain_l, desired_gain_r, azimuth[k],
+ number_of_input_channels);
+ if (azimuth[k] <= 0) { // from -90 -> 0
+ float input_l = *source_l++;
+ float input_r = *source_r++;
+ *destination_l++ =
+ static_cast<float>(input_l + input_r * desired_gain_l);
+ *destination_r++ = static_cast<float>(input_r * desired_gain_r);
+ } else { // from 0 -> +90
+ float input_l = *source_l++;
+ float input_r = *source_r++;
+ *destination_l++ = static_cast<float>(input_l * desired_gain_l);
+ *destination_r++ =
+ static_cast<float>(input_r + input_l * desired_gain_r);
+ }
+ }
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/equal_power_panner.h b/chromium/third_party/blink/renderer/platform/audio/equal_power_panner.h
new file mode 100644
index 00000000000..6ac92af746d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/equal_power_panner.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2010, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_EQUAL_POWER_PANNER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_EQUAL_POWER_PANNER_H_
+
+#include "third_party/blink/renderer/platform/audio/panner.h"
+
+namespace blink {
+
+// Common type of stereo panner as found in normal audio mixing equipment.
+
+class PLATFORM_EXPORT EqualPowerPanner final : public Panner {
+ public:
+ EqualPowerPanner(float sample_rate);
+
+ void Pan(double azimuth,
+ double elevation,
+ const AudioBus* input_bus,
+ AudioBus* output_buf,
+ size_t frames_to_process,
+ AudioBus::ChannelInterpretation) override;
+ void PanWithSampleAccurateValues(double* azimuth,
+ double* elevation,
+ const AudioBus* input_bus,
+ AudioBus* output_bus,
+ size_t frames_to_process,
+ AudioBus::ChannelInterpretation) override;
+
+ void Reset() override {}
+
+ double TailTime() const override { return 0; }
+ double LatencyTime() const override { return 0; }
+ bool RequiresTailProcessing() const override { return false; }
+
+ private:
+ void CalculateDesiredGain(double& desired_gain_l,
+ double& desired_gain_r,
+ double azimuth,
+ int number_of_channels);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_EQUAL_POWER_PANNER_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/ffmpeg/fft_frame_ffmpeg.cc b/chromium/third_party/blink/renderer/platform/audio/ffmpeg/fft_frame_ffmpeg.cc
new file mode 100644
index 00000000000..3a17a0e6d59
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/ffmpeg/fft_frame_ffmpeg.cc
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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.
+ */
+
+// FFTFrame implementation using FFmpeg's RDFT algorithm,
+// suitable for use on Windows and Linux.
+
+#if defined(WTF_USE_WEBAUDIO_FFMPEG)
+
+#include "third_party/blink/renderer/platform/audio/fft_frame.h"
+
+#include "third_party/blink/renderer/platform/audio/vector_math.h"
+
+extern "C" {
+#include <libavcodec/avfft.h>
+}
+
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+namespace blink {
+
+// Max FFT size for FFMPEG. WebAudio currently only uses FFTs up to size 15
+// (2^15 points).
+const int kMaxFFTPow2Size = 16;
+
+// Normal constructor: allocates for a given fftSize.
+FFTFrame::FFTFrame(unsigned fft_size)
+ : fft_size_(fft_size),
+ log2fft_size_(static_cast<unsigned>(log2(fft_size))),
+ real_data_(fft_size / 2),
+ imag_data_(fft_size / 2),
+ forward_context_(nullptr),
+ inverse_context_(nullptr),
+ complex_data_(fft_size) {
+ // We only allow power of two.
+ DCHECK_EQ(1UL << log2fft_size_, fft_size_);
+
+ forward_context_ = ContextForSize(fft_size, DFT_R2C);
+ inverse_context_ = ContextForSize(fft_size, IDFT_C2R);
+}
+
+// Creates a blank/empty frame (interpolate() must later be called).
+FFTFrame::FFTFrame()
+ : fft_size_(0),
+ log2fft_size_(0),
+ forward_context_(nullptr),
+ inverse_context_(nullptr) {}
+
+// Copy constructor.
+FFTFrame::FFTFrame(const FFTFrame& frame)
+ : fft_size_(frame.fft_size_),
+ log2fft_size_(frame.log2fft_size_),
+ real_data_(frame.fft_size_ / 2),
+ imag_data_(frame.fft_size_ / 2),
+ forward_context_(nullptr),
+ inverse_context_(nullptr),
+ complex_data_(frame.fft_size_) {
+ forward_context_ = ContextForSize(fft_size_, DFT_R2C);
+ inverse_context_ = ContextForSize(fft_size_, IDFT_C2R);
+
+ // Copy/setup frame data.
+ unsigned nbytes = sizeof(float) * (fft_size_ / 2);
+ memcpy(RealData(), frame.RealData(), nbytes);
+ memcpy(ImagData(), frame.ImagData(), nbytes);
+}
+
+void FFTFrame::Initialize() {}
+
+void FFTFrame::Cleanup() {}
+
+FFTFrame::~FFTFrame() {
+ av_rdft_end(forward_context_);
+ av_rdft_end(inverse_context_);
+}
+
+void FFTFrame::DoFFT(const float* data) {
+ // Copy since processing is in-place.
+ float* p = complex_data_.Data();
+ memcpy(p, data, sizeof(float) * fft_size_);
+
+ // Compute Forward transform.
+ av_rdft_calc(forward_context_, p);
+
+ // De-interleave to separate real and complex arrays.
+ int len = fft_size_ / 2;
+
+ float* real = real_data_.Data();
+ float* imag = imag_data_.Data();
+ for (int i = 0; i < len; ++i) {
+ int base_complex_index = 2 * i;
+ // m_realData[0] is the DC component and m_imagData[0] is the nyquist
+ // component since the interleaved complex data is packed.
+ real[i] = p[base_complex_index];
+ imag[i] = p[base_complex_index + 1];
+ }
+}
+
+void FFTFrame::DoInverseFFT(float* data) {
+ // Prepare interleaved data.
+ float* interleaved_data = GetUpToDateComplexData();
+
+ // Compute inverse transform.
+ av_rdft_calc(inverse_context_, interleaved_data);
+
+ // Scale so that a forward then inverse FFT yields exactly the original data.
+ // For some reason av_rdft_calc above returns values that are half of what I
+ // expect. Hence make the scale factor
+ // twice as large to compensate for that.
+ const float scale = 2.0 / fft_size_;
+ VectorMath::Vsmul(interleaved_data, 1, &scale, data, 1, fft_size_);
+}
+
+float* FFTFrame::GetUpToDateComplexData() {
+ // FIXME: if we can't completely get rid of this method, SSE
+ // optimization could be considered if it shows up hot on profiles.
+ int len = fft_size_ / 2;
+ const float* real = real_data_.Data();
+ const float* imag = imag_data_.Data();
+ float* c = complex_data_.Data();
+ for (int i = 0; i < len; ++i) {
+ int base_complex_index = 2 * i;
+ c[base_complex_index] = real[i];
+ c[base_complex_index + 1] = imag[i];
+ }
+ return c;
+}
+
+RDFTContext* FFTFrame::ContextForSize(unsigned fft_size, int trans) {
+ // FIXME: This is non-optimal. Ideally, we'd like to share the contexts for
+ // FFTFrames of the same size. But FFmpeg's RDFT uses a scratch buffer
+ // inside the context and so they are not thread-safe. We could improve this
+ // by sharing the FFTFrames on a per-thread basis.
+ DCHECK(fft_size);
+ int pow2size = static_cast<int>(log2(fft_size));
+ DCHECK_LT(pow2size, kMaxFFTPow2Size);
+
+ RDFTContext* context = av_rdft_init(pow2size, (RDFTransformType)trans);
+ return context;
+}
+
+} // namespace blink
+
+#endif // defined(WTF_USE_WEBAUDIO_FFMPEG)
diff --git a/chromium/third_party/blink/renderer/platform/audio/fft_convolver.cc b/chromium/third_party/blink/renderer/platform/audio/fft_convolver.cc
new file mode 100644
index 00000000000..e2df351a971
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/fft_convolver.cc
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/audio/fft_convolver.h"
+#include "third_party/blink/renderer/platform/audio/vector_math.h"
+
+namespace blink {
+
+using namespace VectorMath;
+
+FFTConvolver::FFTConvolver(size_t fft_size)
+ : frame_(fft_size),
+ read_write_index_(0),
+ input_buffer_(fft_size), // 2nd half of buffer is always zeroed
+ output_buffer_(fft_size),
+ last_overlap_buffer_(fft_size / 2) {}
+
+void FFTConvolver::Process(const FFTFrame* fft_kernel,
+ const float* source_p,
+ float* dest_p,
+ size_t frames_to_process) {
+ size_t half_size = FftSize() / 2;
+
+ // framesToProcess must be an exact multiple of halfSize,
+ // or halfSize is a multiple of framesToProcess when halfSize >
+ // framesToProcess.
+ bool is_good =
+ !(half_size % frames_to_process && frames_to_process % half_size);
+ DCHECK(is_good);
+ if (!is_good)
+ return;
+
+ size_t number_of_divisions =
+ half_size <= frames_to_process ? (frames_to_process / half_size) : 1;
+ size_t division_size =
+ number_of_divisions == 1 ? frames_to_process : half_size;
+
+ for (size_t i = 0; i < number_of_divisions;
+ ++i, source_p += division_size, dest_p += division_size) {
+ // Copy samples to input buffer (note contraint above!)
+ float* input_p = input_buffer_.Data();
+
+ // Sanity check
+ bool is_copy_good1 =
+ source_p && input_p &&
+ read_write_index_ + division_size <= input_buffer_.size();
+ DCHECK(is_copy_good1);
+ if (!is_copy_good1)
+ return;
+
+ memcpy(input_p + read_write_index_, source_p,
+ sizeof(float) * division_size);
+
+ // Copy samples from output buffer
+ float* output_p = output_buffer_.Data();
+
+ // Sanity check
+ bool is_copy_good2 =
+ dest_p && output_p &&
+ read_write_index_ + division_size <= output_buffer_.size();
+ DCHECK(is_copy_good2);
+ if (!is_copy_good2)
+ return;
+
+ memcpy(dest_p, output_p + read_write_index_, sizeof(float) * division_size);
+ read_write_index_ += division_size;
+
+ // Check if it's time to perform the next FFT
+ if (read_write_index_ == half_size) {
+ // The input buffer is now filled (get frequency-domain version)
+ frame_.DoFFT(input_buffer_.Data());
+ frame_.Multiply(*fft_kernel);
+ frame_.DoInverseFFT(output_buffer_.Data());
+
+ // Overlap-add 1st half from previous time
+ Vadd(output_buffer_.Data(), 1, last_overlap_buffer_.Data(), 1,
+ output_buffer_.Data(), 1, half_size);
+
+ // Finally, save 2nd half of result
+ bool is_copy_good3 = output_buffer_.size() == 2 * half_size &&
+ last_overlap_buffer_.size() == half_size;
+ DCHECK(is_copy_good3);
+ if (!is_copy_good3)
+ return;
+
+ memcpy(last_overlap_buffer_.Data(), output_buffer_.Data() + half_size,
+ sizeof(float) * half_size);
+
+ // Reset index back to start for next time
+ read_write_index_ = 0;
+ }
+ }
+}
+
+void FFTConvolver::Reset() {
+ last_overlap_buffer_.Zero();
+ read_write_index_ = 0;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/fft_convolver.h b/chromium/third_party/blink/renderer/platform/audio/fft_convolver.h
new file mode 100644
index 00000000000..421a4cefcdb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/fft_convolver.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_FFT_CONVOLVER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_FFT_CONVOLVER_H_
+
+#include "third_party/blink/renderer/platform/audio/audio_array.h"
+#include "third_party/blink/renderer/platform/audio/fft_frame.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT FFTConvolver {
+ USING_FAST_MALLOC(FFTConvolver);
+ WTF_MAKE_NONCOPYABLE(FFTConvolver);
+
+ public:
+ // fftSize must be a power of two
+ FFTConvolver(size_t fft_size);
+
+ // For now, with multiple calls to Process(), framesToProcess MUST add up
+ // EXACTLY to fftSize / 2
+ //
+ // FIXME: Later, we can do more sophisticated buffering to relax this
+ // requirement...
+ //
+ // The input to output latency is equal to fftSize / 2
+ //
+ // Processing in-place is allowed...
+ void Process(const FFTFrame* fft_kernel,
+ const float* source_p,
+ float* dest_p,
+ size_t frames_to_process);
+
+ void Reset();
+
+ size_t FftSize() const { return frame_.FftSize(); }
+
+ private:
+ FFTFrame frame_;
+
+ // Buffer input until we get fftSize / 2 samples then do an FFT
+ size_t read_write_index_;
+ AudioFloatArray input_buffer_;
+
+ // Stores output which we read a little at a time
+ AudioFloatArray output_buffer_;
+
+ // Saves the 2nd half of the FFT buffer, so we can do an overlap-add with the
+ // 1st half of the next one
+ AudioFloatArray last_overlap_buffer_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_FFT_CONVOLVER_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/fft_frame.cc b/chromium/third_party/blink/renderer/platform/audio/fft_frame.cc
new file mode 100644
index 00000000000..bde6baf4aa4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/fft_frame.cc
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/audio/fft_frame.h"
+
+#include <complex>
+#include <memory>
+#include "third_party/blink/renderer/platform/audio/vector_math.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+#ifndef NDEBUG
+#include <stdio.h>
+#endif
+
+namespace blink {
+
+void FFTFrame::DoPaddedFFT(const float* data, size_t data_size) {
+ // Zero-pad the impulse response
+ AudioFloatArray padded_response(FftSize()); // zero-initialized
+ padded_response.CopyToRange(data, 0, data_size);
+
+ // Get the frequency-domain version of padded response
+ DoFFT(padded_response.Data());
+}
+
+std::unique_ptr<FFTFrame> FFTFrame::CreateInterpolatedFrame(
+ const FFTFrame& frame1,
+ const FFTFrame& frame2,
+ double x) {
+ std::unique_ptr<FFTFrame> new_frame =
+ std::make_unique<FFTFrame>(frame1.FftSize());
+
+ new_frame->InterpolateFrequencyComponents(frame1, frame2, x);
+
+ // In the time-domain, the 2nd half of the response must be zero, to avoid
+ // circular convolution aliasing...
+ int fft_size = new_frame->FftSize();
+ AudioFloatArray buffer(fft_size);
+ new_frame->DoInverseFFT(buffer.Data());
+ buffer.ZeroRange(fft_size / 2, fft_size);
+
+ // Put back into frequency domain.
+ new_frame->DoFFT(buffer.Data());
+
+ return new_frame;
+}
+
+void FFTFrame::InterpolateFrequencyComponents(const FFTFrame& frame1,
+ const FFTFrame& frame2,
+ double interp) {
+ // FIXME : with some work, this method could be optimized
+
+ float* real_p = RealData();
+ float* imag_p = ImagData();
+
+ const float* real_p1 = frame1.RealData();
+ const float* imag_p1 = frame1.ImagData();
+ const float* real_p2 = frame2.RealData();
+ const float* imag_p2 = frame2.ImagData();
+
+ fft_size_ = frame1.FftSize();
+ log2fft_size_ = frame1.Log2FFTSize();
+
+ double s1base = (1.0 - interp);
+ double s2base = interp;
+
+ double phase_accum = 0.0;
+ double last_phase1 = 0.0;
+ double last_phase2 = 0.0;
+
+ real_p[0] = static_cast<float>(s1base * real_p1[0] + s2base * real_p2[0]);
+ imag_p[0] = static_cast<float>(s1base * imag_p1[0] + s2base * imag_p2[0]);
+
+ int n = fft_size_ / 2;
+
+ for (int i = 1; i < n; ++i) {
+ std::complex<double> c1(real_p1[i], imag_p1[i]);
+ std::complex<double> c2(real_p2[i], imag_p2[i]);
+
+ double mag1 = abs(c1);
+ double mag2 = abs(c2);
+
+ // Interpolate magnitudes in decibels
+ double mag1db = 20.0 * log10(mag1);
+ double mag2db = 20.0 * log10(mag2);
+
+ double s1 = s1base;
+ double s2 = s2base;
+
+ double magdbdiff = mag1db - mag2db;
+
+ // Empirical tweak to retain higher-frequency zeroes
+ double threshold = (i > 16) ? 5.0 : 2.0;
+
+ if (magdbdiff < -threshold && mag1db < 0.0) {
+ s1 = pow(s1, 0.75);
+ s2 = 1.0 - s1;
+ } else if (magdbdiff > threshold && mag2db < 0.0) {
+ s2 = pow(s2, 0.75);
+ s1 = 1.0 - s2;
+ }
+
+ // Average magnitude by decibels instead of linearly
+ double magdb = s1 * mag1db + s2 * mag2db;
+ double mag = pow(10.0, 0.05 * magdb);
+
+ // Now, deal with phase
+ double phase1 = arg(c1);
+ double phase2 = arg(c2);
+
+ double delta_phase1 = phase1 - last_phase1;
+ double delta_phase2 = phase2 - last_phase2;
+ last_phase1 = phase1;
+ last_phase2 = phase2;
+
+ // Unwrap phase deltas
+ if (delta_phase1 > piDouble)
+ delta_phase1 -= twoPiDouble;
+ if (delta_phase1 < -piDouble)
+ delta_phase1 += twoPiDouble;
+ if (delta_phase2 > piDouble)
+ delta_phase2 -= twoPiDouble;
+ if (delta_phase2 < -piDouble)
+ delta_phase2 += twoPiDouble;
+
+ // Blend group-delays
+ double delta_phase_blend;
+
+ if (delta_phase1 - delta_phase2 > piDouble)
+ delta_phase_blend = s1 * delta_phase1 + s2 * (twoPiDouble + delta_phase2);
+ else if (delta_phase2 - delta_phase1 > piDouble)
+ delta_phase_blend = s1 * (twoPiDouble + delta_phase1) + s2 * delta_phase2;
+ else
+ delta_phase_blend = s1 * delta_phase1 + s2 * delta_phase2;
+
+ phase_accum += delta_phase_blend;
+
+ // Unwrap
+ if (phase_accum > piDouble)
+ phase_accum -= twoPiDouble;
+ if (phase_accum < -piDouble)
+ phase_accum += twoPiDouble;
+
+ std::complex<double> c = std::polar(mag, phase_accum);
+
+ real_p[i] = static_cast<float>(c.real());
+ imag_p[i] = static_cast<float>(c.imag());
+ }
+}
+
+double FFTFrame::ExtractAverageGroupDelay() {
+ float* real_p = RealData();
+ float* imag_p = ImagData();
+
+ double ave_sum = 0.0;
+ double weight_sum = 0.0;
+ double last_phase = 0.0;
+
+ int half_size = FftSize() / 2;
+
+ const double sample_phase_delay =
+ (twoPiDouble) / static_cast<double>(FftSize());
+
+ // Calculate weighted average group delay
+ for (int i = 0; i < half_size; i++) {
+ std::complex<double> c(real_p[i], imag_p[i]);
+ double mag = abs(c);
+ double phase = arg(c);
+
+ double delta_phase = phase - last_phase;
+ last_phase = phase;
+
+ // Unwrap
+ if (delta_phase < -piDouble)
+ delta_phase += twoPiDouble;
+ if (delta_phase > piDouble)
+ delta_phase -= twoPiDouble;
+
+ ave_sum += mag * delta_phase;
+ weight_sum += mag;
+ }
+
+ // Note how we invert the phase delta wrt frequency since this is how group
+ // delay is defined
+ double ave = ave_sum / weight_sum;
+ double ave_sample_delay = -ave / sample_phase_delay;
+
+ // Leave 20 sample headroom (for leading edge of impulse)
+ if (ave_sample_delay > 20.0)
+ ave_sample_delay -= 20.0;
+
+ // Remove average group delay (minus 20 samples for headroom)
+ AddConstantGroupDelay(-ave_sample_delay);
+
+ // Remove DC offset
+ real_p[0] = 0.0f;
+
+ return ave_sample_delay;
+}
+
+void FFTFrame::AddConstantGroupDelay(double sample_frame_delay) {
+ int half_size = FftSize() / 2;
+
+ float* real_p = RealData();
+ float* imag_p = ImagData();
+
+ const double sample_phase_delay =
+ (twoPiDouble) / static_cast<double>(FftSize());
+
+ double phase_adj = -sample_frame_delay * sample_phase_delay;
+
+ // Add constant group delay
+ for (int i = 1; i < half_size; i++) {
+ std::complex<double> c(real_p[i], imag_p[i]);
+ double mag = abs(c);
+ double phase = arg(c);
+
+ phase += i * phase_adj;
+
+ std::complex<double> c2 = std::polar(mag, phase);
+
+ real_p[i] = static_cast<float>(c2.real());
+ imag_p[i] = static_cast<float>(c2.imag());
+ }
+}
+
+void FFTFrame::Multiply(const FFTFrame& frame) {
+ FFTFrame& frame1 = *this;
+ const FFTFrame& frame2 = frame;
+
+ float* real_p1 = frame1.RealData();
+ float* imag_p1 = frame1.ImagData();
+ const float* real_p2 = frame2.RealData();
+ const float* imag_p2 = frame2.ImagData();
+
+ unsigned half_size = FftSize() / 2;
+ float real0 = real_p1[0];
+ float imag0 = imag_p1[0];
+
+ VectorMath::Zvmul(real_p1, imag_p1, real_p2, imag_p2, real_p1, imag_p1,
+ half_size);
+
+ // Multiply the packed DC/nyquist component
+ real_p1[0] = real0 * real_p2[0];
+ imag_p1[0] = imag0 * imag_p2[0];
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/fft_frame.h b/chromium/third_party/blink/renderer/platform/audio/fft_frame.h
new file mode 100644
index 00000000000..65c61914613
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/fft_frame.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_FFT_FRAME_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_FFT_FRAME_H_
+
+#include <memory>
+
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/audio/audio_array.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/threading.h"
+
+#if defined(OS_MACOSX)
+#include <Accelerate/Accelerate.h>
+#elif defined(WTF_USE_WEBAUDIO_OPENMAX_DL_FFT)
+#include <dl/sp/api/omxSP.h>
+#elif defined(WTF_USE_WEBAUDIO_FFMPEG)
+struct RDFTContext;
+#endif
+
+namespace blink {
+
+// Defines the interface for an "FFT frame", an object which is able to perform
+// a forward and reverse FFT, internally storing the resultant frequency-domain
+// data.
+
+class PLATFORM_EXPORT FFTFrame {
+ USING_FAST_MALLOC(FFTFrame);
+
+ public:
+ // The constructors, destructor, and methods up to the CROSS-PLATFORM section
+ // have platform-dependent implementations.
+
+ FFTFrame(unsigned fft_size);
+ // creates a blank/empty frame for later use with createInterpolatedFrame()
+ FFTFrame();
+ FFTFrame(const FFTFrame& frame);
+ ~FFTFrame();
+
+ static void Initialize();
+ static void Cleanup();
+ void DoFFT(const float* data);
+ void DoInverseFFT(float* data);
+
+ float* RealData() { return real_data_.Data(); }
+ const float* RealData() const { return real_data_.Data(); }
+ float* ImagData() { return imag_data_.Data(); }
+ const float* ImagData() const { return imag_data_.Data(); }
+
+ unsigned FftSize() const { return fft_size_; }
+ unsigned Log2FFTSize() const { return log2fft_size_; }
+
+ // CROSS-PLATFORM
+ // The remaining public methods have cross-platform implementations:
+
+ // Interpolates from frame1 -> frame2 as x goes from 0.0 -> 1.0
+ static std::unique_ptr<FFTFrame> CreateInterpolatedFrame(
+ const FFTFrame& frame1,
+ const FFTFrame& frame2,
+ double x);
+ // zero-padding with dataSize <= fftSize
+ void DoPaddedFFT(const float* data, size_t data_size);
+ double ExtractAverageGroupDelay();
+ void AddConstantGroupDelay(double sample_frame_delay);
+ // multiplies ourself with frame : effectively operator*=()
+ void Multiply(const FFTFrame&);
+
+ private:
+ void InterpolateFrequencyComponents(const FFTFrame& frame1,
+ const FFTFrame& frame2,
+ double x);
+
+ unsigned fft_size_;
+ unsigned log2fft_size_;
+ AudioFloatArray real_data_;
+ AudioFloatArray imag_data_;
+
+#if defined(OS_MACOSX)
+ DSPSplitComplex& DspSplitComplex() { return frame_; }
+ DSPSplitComplex DspSplitComplex() const { return frame_; }
+ static FFTSetup FftSetupForSize(unsigned fft_size);
+ static FFTSetup* fft_setups_;
+ FFTSetup fft_setup_;
+ DSPSplitComplex frame_;
+#elif defined(WTF_USE_WEBAUDIO_FFMPEG)
+ static RDFTContext* ContextForSize(unsigned fft_size, int trans);
+ RDFTContext* forward_context_;
+ RDFTContext* inverse_context_;
+ float* GetUpToDateComplexData();
+ AudioFloatArray complex_data_;
+#elif defined(WTF_USE_WEBAUDIO_OPENMAX_DL_FFT)
+ static OMXFFTSpec_R_F32* ContextForSize(unsigned log2fft_size);
+ OMXFFTSpec_R_F32* forward_context_;
+ OMXFFTSpec_R_F32* inverse_context_;
+ AudioFloatArray complex_data_;
+#endif
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_FFT_FRAME_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/fft_frame_stub.cc b/chromium/third_party/blink/renderer/platform/audio/fft_frame_stub.cc
new file mode 100644
index 00000000000..a331fc4275d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/fft_frame_stub.cc
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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.
+ */
+
+// FFTFrame stub implementation to avoid link errors during bringup
+
+#include "build/build_config.h"
+
+#if !defined(OS_MACOSX) && !defined(WTF_USE_WEBAUDIO_FFMPEG) && \
+ !defined(WTF_USE_WEBAUDIO_OPENMAX_DL_FFT)
+
+#include "third_party/blink/renderer/platform/audio/fft_frame.h"
+
+namespace blink {
+
+// Normal constructor: allocates for a given fftSize.
+FFTFrame::FFTFrame(unsigned /*fftSize*/) : fft_size_(0), log2fft_size_(0) {
+ NOTREACHED();
+}
+
+// Creates a blank/empty frame (interpolate() must later be called).
+FFTFrame::FFTFrame() : fft_size_(0), log2fft_size_(0) {
+ NOTREACHED();
+}
+
+// Copy constructor.
+FFTFrame::FFTFrame(const FFTFrame& frame)
+ : fft_size_(frame.fft_size_), log2fft_size_(frame.log2fft_size_) {
+ NOTREACHED();
+}
+
+FFTFrame::~FFTFrame() {
+ NOTREACHED();
+}
+
+void FFTFrame::DoFFT(const float* data) {
+ NOTREACHED();
+}
+
+void FFTFrame::DoInverseFFT(float* data) {
+ NOTREACHED();
+}
+
+void FFTFrame::Initialize() {}
+
+void FFTFrame::Cleanup() {
+ NOTREACHED();
+}
+
+} // namespace blink
+
+#endif // !defined(OS_MACOSX) && !defined(WTF_USE_WEBAUDIO_FFMPEG) &&
+ // !defined(WTF_USE_WEBAUDIO_OPENMAX_DL_FFT)
diff --git a/chromium/third_party/blink/renderer/platform/audio/hrtf_database.cc b/chromium/third_party/blink/renderer/platform/audio/hrtf_database.cc
new file mode 100644
index 00000000000..89f88a54610
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/hrtf_database.cc
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/audio/hrtf_database.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+namespace blink {
+
+const int HRTFDatabase::kMinElevation = -45;
+const int HRTFDatabase::kMaxElevation = 90;
+const unsigned HRTFDatabase::kRawElevationAngleSpacing = 15;
+const unsigned HRTFDatabase::kNumberOfRawElevations =
+ 10; // -45 -> +90 (each 15 degrees)
+const unsigned HRTFDatabase::kInterpolationFactor = 1;
+const unsigned HRTFDatabase::kNumberOfTotalElevations =
+ kNumberOfRawElevations * kInterpolationFactor;
+
+std::unique_ptr<HRTFDatabase> HRTFDatabase::Create(float sample_rate) {
+ return base::WrapUnique(new HRTFDatabase(sample_rate));
+}
+
+HRTFDatabase::HRTFDatabase(float sample_rate)
+ : elevations_(kNumberOfTotalElevations), sample_rate_(sample_rate) {
+ unsigned elevation_index = 0;
+ for (int elevation = kMinElevation; elevation <= kMaxElevation;
+ elevation += kRawElevationAngleSpacing) {
+ std::unique_ptr<HRTFElevation> hrtf_elevation =
+ HRTFElevation::CreateForSubject("Composite", elevation, sample_rate);
+ DCHECK(hrtf_elevation.get());
+ if (!hrtf_elevation.get())
+ return;
+
+ elevations_[elevation_index] = std::move(hrtf_elevation);
+ elevation_index += kInterpolationFactor;
+ }
+
+ // Now, go back and interpolate elevations.
+ if (kInterpolationFactor > 1) {
+ for (unsigned i = 0; i < kNumberOfTotalElevations;
+ i += kInterpolationFactor) {
+ unsigned j = (i + kInterpolationFactor);
+ if (j >= kNumberOfTotalElevations)
+ j = i; // for last elevation interpolate with itself
+
+ // Create the interpolated convolution kernels and delays.
+ for (unsigned jj = 1; jj < kInterpolationFactor; ++jj) {
+ float x =
+ static_cast<float>(jj) / static_cast<float>(kInterpolationFactor);
+ elevations_[i + jj] = HRTFElevation::CreateByInterpolatingSlices(
+ elevations_[i].get(), elevations_[j].get(), x, sample_rate);
+ DCHECK(elevations_[i + jj].get());
+ }
+ }
+ }
+}
+
+void HRTFDatabase::GetKernelsFromAzimuthElevation(double azimuth_blend,
+ unsigned azimuth_index,
+ double elevation_angle,
+ HRTFKernel*& kernel_l,
+ HRTFKernel*& kernel_r,
+ double& frame_delay_l,
+ double& frame_delay_r) {
+ unsigned elevation_index = IndexFromElevationAngle(elevation_angle);
+ SECURITY_DCHECK(elevation_index < elevations_.size() &&
+ elevations_.size() > 0);
+
+ if (!elevations_.size()) {
+ kernel_l = nullptr;
+ kernel_r = nullptr;
+ return;
+ }
+
+ if (elevation_index > elevations_.size() - 1)
+ elevation_index = elevations_.size() - 1;
+
+ HRTFElevation* hrtf_elevation = elevations_[elevation_index].get();
+ DCHECK(hrtf_elevation);
+ if (!hrtf_elevation) {
+ kernel_l = nullptr;
+ kernel_r = nullptr;
+ return;
+ }
+
+ hrtf_elevation->GetKernelsFromAzimuth(azimuth_blend, azimuth_index, kernel_l,
+ kernel_r, frame_delay_l, frame_delay_r);
+}
+
+unsigned HRTFDatabase::IndexFromElevationAngle(double elevation_angle) {
+ // Clamp to allowed range.
+ elevation_angle =
+ clampTo<double, double>(elevation_angle, kMinElevation, kMaxElevation);
+
+ unsigned elevation_index = static_cast<int>(
+ kInterpolationFactor * (elevation_angle - kMinElevation) /
+ kRawElevationAngleSpacing);
+ return elevation_index;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/hrtf_database.h b/chromium/third_party/blink/renderer/platform/audio/hrtf_database.h
new file mode 100644
index 00000000000..72761b0c988
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/hrtf_database.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_HRTF_DATABASE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_HRTF_DATABASE_H_
+
+#include <memory>
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/audio/hrtf_elevation.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class HRTFKernel;
+
+class PLATFORM_EXPORT HRTFDatabase {
+ USING_FAST_MALLOC(HRTFDatabase);
+ WTF_MAKE_NONCOPYABLE(HRTFDatabase);
+
+ public:
+ static std::unique_ptr<HRTFDatabase> Create(float sample_rate);
+
+ // getKernelsFromAzimuthElevation() returns a left and right ear kernel, and
+ // an interpolated left and right frame delay for the given azimuth and
+ // elevation.
+ // azimuthBlend must be in the range 0 -> 1.
+ // Valid values for azimuthIndex are
+ // 0 -> HRTFElevation::NumberOfTotalAzimuths - 1
+ // (corresponding to angles of 0 -> 360).
+ // Valid values for elevationAngle are MinElevation -> MaxElevation.
+ void GetKernelsFromAzimuthElevation(double azimuth_blend,
+ unsigned azimuth_index,
+ double elevation_angle,
+ HRTFKernel*& kernel_l,
+ HRTFKernel*& kernel_r,
+ double& frame_delay_l,
+ double& frame_delay_r);
+
+ // Returns the number of different azimuth angles.
+ static unsigned NumberOfAzimuths() {
+ return HRTFElevation::kNumberOfTotalAzimuths;
+ }
+
+ float SampleRate() const { return sample_rate_; }
+
+ // Number of elevations loaded from resource.
+ static const unsigned kNumberOfRawElevations;
+
+ private:
+ explicit HRTFDatabase(float sample_rate);
+
+ // Minimum and maximum elevation angles (inclusive) for a HRTFDatabase.
+ static const int kMinElevation;
+ static const int kMaxElevation;
+ static const unsigned kRawElevationAngleSpacing;
+
+ // Interpolates by this factor to get the total number of elevations from
+ // every elevation loaded from resource.
+ static const unsigned kInterpolationFactor;
+
+ // Total number of elevations after interpolation.
+ static const unsigned kNumberOfTotalElevations;
+
+ // Returns the index for the correct HRTFElevation given the elevation angle.
+ static unsigned IndexFromElevationAngle(double);
+
+ Vector<std::unique_ptr<HRTFElevation>> elevations_;
+ float sample_rate_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_HRTF_DATABASE_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/hrtf_database_loader.cc b/chromium/third_party/blink/renderer/platform/audio/hrtf_database_loader.cc
new file mode 100644
index 00000000000..ef2ca352a32
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/hrtf_database_loader.cc
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/audio/hrtf_database_loader.h"
+
+#include "base/location.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/waitable_event.h"
+#include "third_party/blink/renderer/platform/web_task_runner.h"
+
+namespace blink {
+
+using LoaderMap = HashMap<double, HRTFDatabaseLoader*>;
+
+// getLoaderMap() returns the static hash map that contains the mapping between
+// the sample rate and the corresponding HRTF database.
+static LoaderMap& GetLoaderMap() {
+ DEFINE_STATIC_LOCAL(LoaderMap*, map, (new LoaderMap));
+ return *map;
+}
+
+scoped_refptr<HRTFDatabaseLoader>
+HRTFDatabaseLoader::CreateAndLoadAsynchronouslyIfNecessary(float sample_rate) {
+ DCHECK(IsMainThread());
+
+ scoped_refptr<HRTFDatabaseLoader> loader = GetLoaderMap().at(sample_rate);
+ if (loader) {
+ DCHECK_EQ(sample_rate, loader->DatabaseSampleRate());
+ return loader;
+ }
+
+ loader = base::AdoptRef(new HRTFDatabaseLoader(sample_rate));
+ GetLoaderMap().insert(sample_rate, loader.get());
+ loader->LoadAsynchronously();
+ return loader;
+}
+
+HRTFDatabaseLoader::HRTFDatabaseLoader(float sample_rate)
+ : database_sample_rate_(sample_rate) {
+ DCHECK(IsMainThread());
+}
+
+HRTFDatabaseLoader::~HRTFDatabaseLoader() {
+ DCHECK(IsMainThread());
+ DCHECK(!thread_);
+ GetLoaderMap().erase(database_sample_rate_);
+}
+
+void HRTFDatabaseLoader::LoadTask() {
+ DCHECK(!IsMainThread());
+ DCHECK(!hrtf_database_);
+
+ // Protect access to m_hrtfDatabase, which can be accessed from the audio
+ // thread.
+ MutexLocker locker(lock_);
+ // Load the default HRTF database.
+ hrtf_database_ = HRTFDatabase::Create(database_sample_rate_);
+}
+
+void HRTFDatabaseLoader::LoadAsynchronously() {
+ DCHECK(IsMainThread());
+
+ // m_hrtfDatabase and m_thread should both be unset because this should be a
+ // new HRTFDatabaseLoader object that was just created by
+ // createAndLoadAsynchronouslyIfNecessary and because we haven't started
+ // loadTask yet for this object.
+ DCHECK(!hrtf_database_);
+ DCHECK(!thread_);
+
+ // Start the asynchronous database loading process.
+ thread_ = Platform::Current()->CreateThread(
+ WebThreadCreationParams(WebThreadType::kHRTFDatabaseLoaderThread));
+ // TODO(alexclarke): Should this be posted as a loading task?
+ PostCrossThreadTask(*thread_->GetTaskRunner(), FROM_HERE,
+ CrossThreadBind(&HRTFDatabaseLoader::LoadTask,
+ CrossThreadUnretained(this)));
+}
+
+HRTFDatabase* HRTFDatabaseLoader::Database() {
+ DCHECK(!IsMainThread());
+
+ // Seeing that this is only called from the audio thread, we can't block.
+ // It's ok to return nullptr if we can't get the lock.
+ MutexTryLocker try_locker(lock_);
+
+ if (!try_locker.Locked())
+ return nullptr;
+
+ return hrtf_database_.get();
+}
+
+// This cleanup task is needed just to make sure that the loader thread finishes
+// the load task and thus the loader thread doesn't touch m_thread any more.
+void HRTFDatabaseLoader::CleanupTask(WaitableEvent* sync) {
+ sync->Signal();
+}
+
+void HRTFDatabaseLoader::WaitForLoaderThreadCompletion() {
+ if (!thread_)
+ return;
+
+ WaitableEvent sync;
+ // TODO(alexclarke): Should this be posted as a loading task?
+ PostCrossThreadTask(*thread_->GetTaskRunner(), FROM_HERE,
+ CrossThreadBind(&HRTFDatabaseLoader::CleanupTask,
+ CrossThreadUnretained(this),
+ CrossThreadUnretained(&sync)));
+ sync.Wait();
+ thread_.reset();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/hrtf_database_loader.h b/chromium/third_party/blink/renderer/platform/audio/hrtf_database_loader.h
new file mode 100644
index 00000000000..a17ffb98dce
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/hrtf_database_loader.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_HRTF_DATABASE_LOADER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_HRTF_DATABASE_LOADER_H_
+
+#include <memory>
+#include "third_party/blink/public/platform/web_thread.h"
+#include "third_party/blink/renderer/platform/audio/hrtf_database.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
+
+namespace blink {
+
+class WaitableEvent;
+
+// HRTFDatabaseLoader will asynchronously load the default HRTFDatabase in a new
+// thread.
+class PLATFORM_EXPORT HRTFDatabaseLoader final
+ : public RefCounted<HRTFDatabaseLoader> {
+ public:
+ // Lazily creates a HRTFDatabaseLoader (if not already created) for the given
+ // sample-rate and starts loading asynchronously (when created the first
+ // time).
+ // Returns the HRTFDatabaseLoader.
+ // Must be called from the main thread.
+ static scoped_refptr<HRTFDatabaseLoader>
+ CreateAndLoadAsynchronouslyIfNecessary(float sample_rate);
+
+ // Both constructor and destructor must be called from the main thread.
+ ~HRTFDatabaseLoader();
+
+ // Returns true once the default database has been completely loaded. This
+ // must be called from the audio thread.
+ bool IsLoaded() { return Database(); }
+
+ // waitForLoaderThreadCompletion() may be called more than once and is
+ // thread-safe.
+ void WaitForLoaderThreadCompletion();
+
+ // Returns the database or nullptr if the database doesn't yet exist. Must
+ // be called from the audio thread.
+ HRTFDatabase* Database();
+
+ float DatabaseSampleRate() const { return database_sample_rate_; }
+
+ private:
+ // Both constructor and destructor must be called from the main thread.
+ explicit HRTFDatabaseLoader(float sample_rate);
+
+ // If it hasn't already been loaded, creates a new thread and initiates
+ // asynchronous loading of the default database.
+ // This must be called from the main thread.
+ void LoadAsynchronously();
+
+ // Called in asynchronous loading thread.
+ void LoadTask();
+ void CleanupTask(WaitableEvent*);
+
+ // Holding a m_lock is required when accessing m_hrtfDatabase since we access
+ // it from multiple threads.
+ Mutex lock_;
+ std::unique_ptr<HRTFDatabase> hrtf_database_;
+
+ std::unique_ptr<WebThread> thread_;
+
+ float database_sample_rate_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_HRTF_DATABASE_LOADER_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/hrtf_elevation.cc b/chromium/third_party/blink/renderer/platform/audio/hrtf_elevation.cc
new file mode 100644
index 00000000000..96872c51755
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/hrtf_elevation.cc
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/audio/hrtf_elevation.h"
+
+#include <math.h>
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/platform/audio/audio_bus.h"
+#include "third_party/blink/renderer/platform/audio/hrtf_panner.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
+
+namespace blink {
+
+const unsigned HRTFElevation::kAzimuthSpacing = 15;
+const unsigned HRTFElevation::kNumberOfRawAzimuths = 360 / kAzimuthSpacing;
+const unsigned HRTFElevation::kInterpolationFactor = 8;
+const unsigned HRTFElevation::kNumberOfTotalAzimuths =
+ kNumberOfRawAzimuths * kInterpolationFactor;
+
+// Total number of components of an HRTF database.
+const size_t kTotalNumberOfResponses = 240;
+
+// Number of frames in an individual impulse response.
+const size_t kResponseFrameSize = 256;
+
+// Sample-rate of the spatialization impulse responses as stored in the resource
+// file. The impulse responses may be resampled to a different sample-rate
+// (depending on the audio hardware) when they are loaded.
+const float kResponseSampleRate = 44100;
+
+// This table maps the index into the elevation table with the corresponding
+// angle. See https://bugs.webkit.org/show_bug.cgi?id=98294#c9 for the
+// elevation angles and their order in the concatenated response.
+const int kElevationIndexTableSize = 10;
+const int kElevationIndexTable[kElevationIndexTableSize] = {
+ 0, 15, 30, 45, 60, 75, 90, 315, 330, 345};
+
+// Lazily load a concatenated HRTF database for given subject and store it in a
+// local hash table to ensure quick efficient future retrievals.
+static scoped_refptr<AudioBus> GetConcatenatedImpulseResponsesForSubject(
+ const String& subject_name) {
+ typedef HashMap<String, scoped_refptr<AudioBus>> AudioBusMap;
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(AudioBusMap, audio_bus_map, ());
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(Mutex, mutex, ());
+
+ MutexLocker locker(mutex);
+ scoped_refptr<AudioBus> bus;
+ AudioBusMap::iterator iterator = audio_bus_map.find(subject_name);
+ if (iterator == audio_bus_map.end()) {
+ scoped_refptr<AudioBus> concatenated_impulse_responses(
+ AudioBus::GetDataResource(subject_name.Utf8().data(),
+ kResponseSampleRate));
+ DCHECK(concatenated_impulse_responses);
+ if (!concatenated_impulse_responses)
+ return nullptr;
+
+ bus = concatenated_impulse_responses;
+ audio_bus_map.Set(subject_name, bus);
+ } else
+ bus = iterator->value;
+
+ size_t response_length = bus->length();
+ size_t expected_length =
+ static_cast<size_t>(kTotalNumberOfResponses * kResponseFrameSize);
+
+ // Check number of channels and length. For now these are fixed and known.
+ bool is_bus_good =
+ response_length == expected_length && bus->NumberOfChannels() == 2;
+ DCHECK(is_bus_good);
+ if (!is_bus_good)
+ return nullptr;
+
+ return bus;
+}
+
+bool HRTFElevation::CalculateKernelsForAzimuthElevation(
+ int azimuth,
+ int elevation,
+ float sample_rate,
+ const String& subject_name,
+ std::unique_ptr<HRTFKernel>& kernel_l,
+ std::unique_ptr<HRTFKernel>& kernel_r) {
+ // Valid values for azimuth are 0 -> 345 in 15 degree increments.
+ // Valid values for elevation are -45 -> +90 in 15 degree increments.
+
+ bool is_azimuth_good =
+ azimuth >= 0 && azimuth <= 345 && (azimuth / 15) * 15 == azimuth;
+ DCHECK(is_azimuth_good);
+ if (!is_azimuth_good)
+ return false;
+
+ bool is_elevation_good =
+ elevation >= -45 && elevation <= 90 && (elevation / 15) * 15 == elevation;
+ DCHECK(is_elevation_good);
+ if (!is_elevation_good)
+ return false;
+
+ // Construct the resource name from the subject name, azimuth, and elevation,
+ // for example:
+ // "IRC_Composite_C_R0195_T015_P000"
+ // Note: the passed in subjectName is not a string passed in via JavaScript or
+ // the web. It's passed in as an internal ASCII identifier and is an
+ // implementation detail.
+ int positive_elevation = elevation < 0 ? elevation + 360 : elevation;
+
+ scoped_refptr<AudioBus> bus(
+ GetConcatenatedImpulseResponsesForSubject(subject_name));
+
+ if (!bus)
+ return false;
+
+ // Just sequentially search the table to find the correct index.
+ int elevation_index = -1;
+
+ for (int k = 0; k < kElevationIndexTableSize; ++k) {
+ if (kElevationIndexTable[k] == positive_elevation) {
+ elevation_index = k;
+ break;
+ }
+ }
+
+ bool is_elevation_index_good =
+ (elevation_index >= 0) && (elevation_index < kElevationIndexTableSize);
+ DCHECK(is_elevation_index_good);
+ if (!is_elevation_index_good)
+ return false;
+
+ // The concatenated impulse response is a bus containing all
+ // the elevations per azimuth, for all azimuths by increasing
+ // order. So for a given azimuth and elevation we need to compute
+ // the index of the wanted audio frames in the concatenated table.
+ unsigned index =
+ ((azimuth / kAzimuthSpacing) * HRTFDatabase::kNumberOfRawElevations) +
+ elevation_index;
+ bool is_index_good = index < kTotalNumberOfResponses;
+ DCHECK(is_index_good);
+ if (!is_index_good)
+ return false;
+
+ // Extract the individual impulse response from the concatenated
+ // responses and potentially sample-rate convert it to the desired
+ // (hardware) sample-rate.
+ unsigned start_frame = index * kResponseFrameSize;
+ unsigned stop_frame = start_frame + kResponseFrameSize;
+ scoped_refptr<AudioBus> pre_sample_rate_converted_response(
+ AudioBus::CreateBufferFromRange(bus.get(), start_frame, stop_frame));
+ scoped_refptr<AudioBus> response(AudioBus::CreateBySampleRateConverting(
+ pre_sample_rate_converted_response.get(), false, sample_rate));
+ AudioChannel* left_ear_impulse_response =
+ response->Channel(AudioBus::kChannelLeft);
+ AudioChannel* right_ear_impulse_response =
+ response->Channel(AudioBus::kChannelRight);
+
+ // Note that depending on the fftSize returned by the panner, we may be
+ // truncating the impulse response we just loaded in.
+ const size_t fft_size = HRTFPanner::FftSizeForSampleRate(sample_rate);
+ kernel_l =
+ HRTFKernel::Create(left_ear_impulse_response, fft_size, sample_rate);
+ kernel_r =
+ HRTFKernel::Create(right_ear_impulse_response, fft_size, sample_rate);
+
+ return true;
+}
+
+// The range of elevations for the IRCAM impulse responses varies depending on
+// azimuth, but the minimum elevation appears to always be -45.
+//
+// Here's how it goes:
+static int g_max_elevations[] = {
+ // Azimuth
+ //
+ 90, // 0
+ 45, // 15
+ 60, // 30
+ 45, // 45
+ 75, // 60
+ 45, // 75
+ 60, // 90
+ 45, // 105
+ 75, // 120
+ 45, // 135
+ 60, // 150
+ 45, // 165
+ 75, // 180
+ 45, // 195
+ 60, // 210
+ 45, // 225
+ 75, // 240
+ 45, // 255
+ 60, // 270
+ 45, // 285
+ 75, // 300
+ 45, // 315
+ 60, // 330
+ 45 // 345
+};
+
+std::unique_ptr<HRTFElevation> HRTFElevation::CreateForSubject(
+ const String& subject_name,
+ int elevation,
+ float sample_rate) {
+ bool is_elevation_good =
+ elevation >= -45 && elevation <= 90 && (elevation / 15) * 15 == elevation;
+ DCHECK(is_elevation_good);
+ if (!is_elevation_good)
+ return nullptr;
+
+ std::unique_ptr<HRTFKernelList> kernel_list_l =
+ std::make_unique<HRTFKernelList>(kNumberOfTotalAzimuths);
+ std::unique_ptr<HRTFKernelList> kernel_list_r =
+ std::make_unique<HRTFKernelList>(kNumberOfTotalAzimuths);
+
+ // Load convolution kernels from HRTF files.
+ int interpolated_index = 0;
+ for (unsigned raw_index = 0; raw_index < kNumberOfRawAzimuths; ++raw_index) {
+ // Don't let elevation exceed maximum for this azimuth.
+ int max_elevation = g_max_elevations[raw_index];
+ int actual_elevation = std::min(elevation, max_elevation);
+
+ bool success = CalculateKernelsForAzimuthElevation(
+ raw_index * kAzimuthSpacing, actual_elevation, sample_rate,
+ subject_name, kernel_list_l->at(interpolated_index),
+ kernel_list_r->at(interpolated_index));
+ if (!success)
+ return nullptr;
+
+ interpolated_index += kInterpolationFactor;
+ }
+
+ // Now go back and interpolate intermediate azimuth values.
+ for (unsigned i = 0; i < kNumberOfTotalAzimuths; i += kInterpolationFactor) {
+ int j = (i + kInterpolationFactor) % kNumberOfTotalAzimuths;
+
+ // Create the interpolated convolution kernels and delays.
+ for (unsigned jj = 1; jj < kInterpolationFactor; ++jj) {
+ float x =
+ float(jj) / float(kInterpolationFactor); // interpolate from 0 -> 1
+
+ (*kernel_list_l)[i + jj] = HRTFKernel::CreateInterpolatedKernel(
+ kernel_list_l->at(i).get(), kernel_list_l->at(j).get(), x);
+ (*kernel_list_r)[i + jj] = HRTFKernel::CreateInterpolatedKernel(
+ kernel_list_r->at(i).get(), kernel_list_r->at(j).get(), x);
+ }
+ }
+
+ std::unique_ptr<HRTFElevation> hrtf_elevation = base::WrapUnique(
+ new HRTFElevation(std::move(kernel_list_l), std::move(kernel_list_r),
+ elevation, sample_rate));
+ return hrtf_elevation;
+}
+
+std::unique_ptr<HRTFElevation> HRTFElevation::CreateByInterpolatingSlices(
+ HRTFElevation* hrtf_elevation1,
+ HRTFElevation* hrtf_elevation2,
+ float x,
+ float sample_rate) {
+ DCHECK(hrtf_elevation1);
+ DCHECK(hrtf_elevation2);
+ if (!hrtf_elevation1 || !hrtf_elevation2)
+ return nullptr;
+
+ DCHECK_GE(x, 0.0);
+ DCHECK_LT(x, 1.0);
+
+ std::unique_ptr<HRTFKernelList> kernel_list_l =
+ std::make_unique<HRTFKernelList>(kNumberOfTotalAzimuths);
+ std::unique_ptr<HRTFKernelList> kernel_list_r =
+ std::make_unique<HRTFKernelList>(kNumberOfTotalAzimuths);
+
+ HRTFKernelList* kernel_list_l1 = hrtf_elevation1->KernelListL();
+ HRTFKernelList* kernel_list_r1 = hrtf_elevation1->KernelListR();
+ HRTFKernelList* kernel_list_l2 = hrtf_elevation2->KernelListL();
+ HRTFKernelList* kernel_list_r2 = hrtf_elevation2->KernelListR();
+
+ // Interpolate kernels of corresponding azimuths of the two elevations.
+ for (unsigned i = 0; i < kNumberOfTotalAzimuths; ++i) {
+ (*kernel_list_l)[i] = HRTFKernel::CreateInterpolatedKernel(
+ kernel_list_l1->at(i).get(), kernel_list_l2->at(i).get(), x);
+ (*kernel_list_r)[i] = HRTFKernel::CreateInterpolatedKernel(
+ kernel_list_r1->at(i).get(), kernel_list_r2->at(i).get(), x);
+ }
+
+ // Interpolate elevation angle.
+ double angle = (1.0 - x) * hrtf_elevation1->ElevationAngle() +
+ x * hrtf_elevation2->ElevationAngle();
+
+ std::unique_ptr<HRTFElevation> hrtf_elevation = base::WrapUnique(
+ new HRTFElevation(std::move(kernel_list_l), std::move(kernel_list_r),
+ static_cast<int>(angle), sample_rate));
+ return hrtf_elevation;
+}
+
+void HRTFElevation::GetKernelsFromAzimuth(double azimuth_blend,
+ unsigned azimuth_index,
+ HRTFKernel*& kernel_l,
+ HRTFKernel*& kernel_r,
+ double& frame_delay_l,
+ double& frame_delay_r) {
+ bool check_azimuth_blend = azimuth_blend >= 0.0 && azimuth_blend < 1.0;
+ DCHECK(check_azimuth_blend);
+ if (!check_azimuth_blend)
+ azimuth_blend = 0.0;
+
+ unsigned num_kernels = kernel_list_l_->size();
+
+ bool is_index_good = azimuth_index < num_kernels;
+ DCHECK(is_index_good);
+ if (!is_index_good) {
+ kernel_l = nullptr;
+ kernel_r = nullptr;
+ return;
+ }
+
+ // Return the left and right kernels.
+ kernel_l = kernel_list_l_->at(azimuth_index).get();
+ kernel_r = kernel_list_r_->at(azimuth_index).get();
+
+ frame_delay_l = kernel_list_l_->at(azimuth_index)->FrameDelay();
+ frame_delay_r = kernel_list_r_->at(azimuth_index)->FrameDelay();
+
+ int azimuth_index2 = (azimuth_index + 1) % num_kernels;
+ double frame_delay2l = kernel_list_l_->at(azimuth_index2)->FrameDelay();
+ double frame_delay2r = kernel_list_r_->at(azimuth_index2)->FrameDelay();
+
+ // Linearly interpolate delays.
+ frame_delay_l =
+ (1.0 - azimuth_blend) * frame_delay_l + azimuth_blend * frame_delay2l;
+ frame_delay_r =
+ (1.0 - azimuth_blend) * frame_delay_r + azimuth_blend * frame_delay2r;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/hrtf_elevation.h b/chromium/third_party/blink/renderer/platform/audio/hrtf_elevation.h
new file mode 100644
index 00000000000..c29ba2ca01d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/hrtf_elevation.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_HRTF_ELEVATION_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_HRTF_ELEVATION_H_
+
+#include <memory>
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/audio/hrtf_kernel.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+// HRTFElevation contains all of the HRTFKernels (one left ear and one right ear
+// per azimuth angle) for a particular elevation.
+
+class PLATFORM_EXPORT HRTFElevation {
+ USING_FAST_MALLOC(HRTFElevation);
+ WTF_MAKE_NONCOPYABLE(HRTFElevation);
+
+ public:
+ // Loads and returns an HRTFElevation with the given HRTF database subject
+ // name and elevation from browser (or WebKit.framework) resources.
+ // Normally, there will only be a single HRTF database set, but this API
+ // supports the possibility of multiple ones with different names.
+ // Interpolated azimuths will be generated based on InterpolationFactor.
+ // Valid values for elevation are -45 -> +90 in 15 degree increments.
+ static std::unique_ptr<HRTFElevation> CreateForSubject(
+ const String& subject_name,
+ int elevation,
+ float sample_rate);
+
+ // Given two HRTFElevations, and an interpolation factor x: 0 -> 1, returns an
+ // interpolated HRTFElevation.
+ static std::unique_ptr<HRTFElevation> CreateByInterpolatingSlices(
+ HRTFElevation* hrtf_elevation1,
+ HRTFElevation* hrtf_elevation2,
+ float x,
+ float sample_rate);
+
+ // Returns the list of left or right ear HRTFKernels for all the azimuths
+ // going from 0 to 360 degrees.
+ HRTFKernelList* KernelListL() { return kernel_list_l_.get(); }
+ HRTFKernelList* KernelListR() { return kernel_list_r_.get(); }
+
+ double ElevationAngle() const { return elevation_angle_; }
+ unsigned NumberOfAzimuths() const { return kNumberOfTotalAzimuths; }
+ float SampleRate() const { return sample_rate_; }
+
+ // Returns the left and right kernels for the given azimuth index.
+ // The interpolated delays based on azimuthBlend: 0 -> 1 are returned in
+ // frameDelayL and frameDelayR.
+ void GetKernelsFromAzimuth(double azimuth_blend,
+ unsigned azimuth_index,
+ HRTFKernel*& kernel_l,
+ HRTFKernel*& kernel_r,
+ double& frame_delay_l,
+ double& frame_delay_r);
+
+ // Spacing, in degrees, between every azimuth loaded from resource.
+ static const unsigned kAzimuthSpacing;
+
+ // Number of azimuths loaded from resource.
+ static const unsigned kNumberOfRawAzimuths;
+
+ // Interpolates by this factor to get the total number of azimuths from every
+ // azimuth loaded from resource.
+ static const unsigned kInterpolationFactor;
+
+ // Total number of azimuths after interpolation.
+ static const unsigned kNumberOfTotalAzimuths;
+
+ // Given a specific azimuth and elevation angle, returns the left and right
+ // HRTFKernel.
+ // Valid values for azimuth are 0 -> 345 in 15 degree increments.
+ // Valid values for elevation are -45 -> +90 in 15 degree increments.
+ // Returns true on success.
+ static bool CalculateKernelsForAzimuthElevation(
+ int azimuth,
+ int elevation,
+ float sample_rate,
+ const String& subject_name,
+ std::unique_ptr<HRTFKernel>& kernel_l,
+ std::unique_ptr<HRTFKernel>& kernel_r);
+
+ private:
+ HRTFElevation(std::unique_ptr<HRTFKernelList> kernel_list_l,
+ std::unique_ptr<HRTFKernelList> kernel_list_r,
+ int elevation,
+ float sample_rate)
+ : kernel_list_l_(std::move(kernel_list_l)),
+ kernel_list_r_(std::move(kernel_list_r)),
+ elevation_angle_(elevation),
+ sample_rate_(sample_rate) {}
+
+ std::unique_ptr<HRTFKernelList> kernel_list_l_;
+ std::unique_ptr<HRTFKernelList> kernel_list_r_;
+ double elevation_angle_;
+ float sample_rate_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_HRTF_ELEVATION_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/hrtf_kernel.cc b/chromium/third_party/blink/renderer/platform/audio/hrtf_kernel.cc
new file mode 100644
index 00000000000..ab0b64dc4da
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/hrtf_kernel.cc
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/audio/hrtf_kernel.h"
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+#include "third_party/blink/renderer/platform/audio/audio_channel.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+namespace blink {
+
+// Takes the input AudioChannel as an input impulse response and calculates the
+// average group delay. This represents the initial delay before the most
+// energetic part of the impulse response. The sample-frame delay is removed
+// from the impulseP impulse response, and this value is returned. The length
+// of the passed in AudioChannel must be a power of 2.
+static float ExtractAverageGroupDelay(AudioChannel* channel,
+ size_t analysis_fft_size) {
+ DCHECK(channel);
+
+ float* impulse_p = channel->MutableData();
+
+ bool is_size_good = channel->length() >= analysis_fft_size;
+ DCHECK(is_size_good);
+ if (!is_size_good)
+ return 0;
+
+ // Check for power-of-2.
+ DCHECK_EQ(1UL << static_cast<unsigned>(log2(analysis_fft_size)),
+ analysis_fft_size);
+
+ FFTFrame estimation_frame(analysis_fft_size);
+ estimation_frame.DoFFT(impulse_p);
+
+ float frame_delay =
+ clampTo<float>(estimation_frame.ExtractAverageGroupDelay());
+ estimation_frame.DoInverseFFT(impulse_p);
+
+ return frame_delay;
+}
+
+HRTFKernel::HRTFKernel(AudioChannel* channel,
+ size_t fft_size,
+ float sample_rate)
+ : frame_delay_(0), sample_rate_(sample_rate) {
+ DCHECK(channel);
+
+ // Determine the leading delay (average group delay) for the response.
+ frame_delay_ = ExtractAverageGroupDelay(channel, fft_size / 2);
+
+ float* impulse_response = channel->MutableData();
+ size_t response_length = channel->length();
+
+ // We need to truncate to fit into 1/2 the FFT size (with zero padding) in
+ // order to do proper convolution.
+ // Truncate if necessary to max impulse response length allowed by FFT.
+ size_t truncated_response_length = std::min(response_length, fft_size / 2);
+
+ // Quick fade-out (apply window) at truncation point
+ unsigned number_of_fade_out_frames = static_cast<unsigned>(
+ sample_rate / 4410); // 10 sample-frames @44.1KHz sample-rate
+ DCHECK_LT(number_of_fade_out_frames, truncated_response_length);
+ if (number_of_fade_out_frames < truncated_response_length) {
+ for (unsigned i = truncated_response_length - number_of_fade_out_frames;
+ i < truncated_response_length; ++i) {
+ float x = 1.0f - static_cast<float>(i - (truncated_response_length -
+ number_of_fade_out_frames)) /
+ number_of_fade_out_frames;
+ impulse_response[i] *= x;
+ }
+ }
+
+ fft_frame_ = std::make_unique<FFTFrame>(fft_size);
+ fft_frame_->DoPaddedFFT(impulse_response, truncated_response_length);
+}
+
+std::unique_ptr<AudioChannel> HRTFKernel::CreateImpulseResponse() {
+ std::unique_ptr<AudioChannel> channel =
+ std::make_unique<AudioChannel>(FftSize());
+ FFTFrame fft_frame(*fft_frame_);
+
+ // Add leading delay back in.
+ fft_frame.AddConstantGroupDelay(frame_delay_);
+ fft_frame.DoInverseFFT(channel->MutableData());
+
+ return channel;
+}
+
+// Interpolates two kernels with x: 0 -> 1 and returns the result.
+std::unique_ptr<HRTFKernel> HRTFKernel::CreateInterpolatedKernel(
+ HRTFKernel* kernel1,
+ HRTFKernel* kernel2,
+ float x) {
+ DCHECK(kernel1);
+ DCHECK(kernel2);
+ if (!kernel1 || !kernel2)
+ return nullptr;
+
+ DCHECK_GE(x, 0.0);
+ DCHECK_LT(x, 1.0);
+ x = clampTo(x, 0.0f, 1.0f);
+
+ float sample_rate1 = kernel1->SampleRate();
+ float sample_rate2 = kernel2->SampleRate();
+ DCHECK_EQ(sample_rate1, sample_rate2);
+ if (sample_rate1 != sample_rate2)
+ return nullptr;
+
+ float frame_delay =
+ (1 - x) * kernel1->FrameDelay() + x * kernel2->FrameDelay();
+
+ std::unique_ptr<FFTFrame> interpolated_frame =
+ FFTFrame::CreateInterpolatedFrame(*kernel1->FftFrame(),
+ *kernel2->FftFrame(), x);
+ return HRTFKernel::Create(std::move(interpolated_frame), frame_delay,
+ sample_rate1);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/hrtf_kernel.h b/chromium/third_party/blink/renderer/platform/audio/hrtf_kernel.h
new file mode 100644
index 00000000000..6758b9a15b0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/hrtf_kernel.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_HRTF_KERNEL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_HRTF_KERNEL_H_
+
+#include <memory>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/platform/audio/fft_frame.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class AudioChannel;
+
+// HRTF stands for Head-Related Transfer Function.
+// HRTFKernel is a frequency-domain representation of an impulse-response used
+// as part of the spatialized panning system. For a given azimuth / elevation
+// angle there will be one HRTFKernel for the left ear transfer function, and
+// one for the right ear. The leading delay (average group delay) for each
+// impulse response is extracted:
+// m_fftFrame is the frequency-domain representation of the impulse
+// response with the delay removed
+// m_frameDelay is the leading delay of the original impulse response.
+class PLATFORM_EXPORT HRTFKernel {
+ USING_FAST_MALLOC(HRTFKernel);
+ WTF_MAKE_NONCOPYABLE(HRTFKernel);
+
+ public:
+ // Note: this is destructive on the passed in AudioChannel.
+ // The length of channel must be a power of two.
+ static std::unique_ptr<HRTFKernel> Create(AudioChannel* channel,
+ size_t fft_size,
+ float sample_rate) {
+ return base::WrapUnique(new HRTFKernel(channel, fft_size, sample_rate));
+ }
+
+ static std::unique_ptr<HRTFKernel> Create(std::unique_ptr<FFTFrame> fft_frame,
+ float frame_delay,
+ float sample_rate) {
+ return base::WrapUnique(
+ new HRTFKernel(std::move(fft_frame), frame_delay, sample_rate));
+ }
+
+ // Given two HRTFKernels, and an interpolation factor x: 0 -> 1, returns an
+ // interpolated HRTFKernel.
+ static std::unique_ptr<HRTFKernel>
+ CreateInterpolatedKernel(HRTFKernel* kernel1, HRTFKernel* kernel2, float x);
+
+ FFTFrame* FftFrame() { return fft_frame_.get(); }
+
+ size_t FftSize() const { return fft_frame_->FftSize(); }
+ float FrameDelay() const { return frame_delay_; }
+
+ float SampleRate() const { return sample_rate_; }
+ double Nyquist() const { return 0.5 * SampleRate(); }
+
+ // Converts back into impulse-response form.
+ std::unique_ptr<AudioChannel> CreateImpulseResponse();
+
+ private:
+ // Note: this is destructive on the passed in AudioChannel.
+ HRTFKernel(AudioChannel*, size_t fft_size, float sample_rate);
+
+ HRTFKernel(std::unique_ptr<FFTFrame> fft_frame,
+ float frame_delay,
+ float sample_rate)
+ : fft_frame_(std::move(fft_frame)),
+ frame_delay_(frame_delay),
+ sample_rate_(sample_rate) {}
+
+ std::unique_ptr<FFTFrame> fft_frame_;
+ float frame_delay_;
+ float sample_rate_;
+};
+
+typedef Vector<std::unique_ptr<HRTFKernel>> HRTFKernelList;
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_HRTF_KERNEL_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/hrtf_panner.cc b/chromium/third_party/blink/renderer/platform/audio/hrtf_panner.cc
new file mode 100644
index 00000000000..ac16deebc07
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/hrtf_panner.cc
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) 2010, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/audio/audio_bus.h"
+#include "third_party/blink/renderer/platform/audio/audio_utilities.h"
+#include "third_party/blink/renderer/platform/audio/hrtf_database.h"
+#include "third_party/blink/renderer/platform/audio/hrtf_panner.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+namespace blink {
+
+// The value of 2 milliseconds is larger than the largest delay which exists in
+// any HRTFKernel from the default HRTFDatabase (0.0136 seconds).
+// We ASSERT the delay values used in process() with this value.
+const double kMaxDelayTimeSeconds = 0.002;
+
+const int kUninitializedAzimuth = -1;
+
+HRTFPanner::HRTFPanner(float sample_rate, HRTFDatabaseLoader* database_loader)
+ : Panner(kPanningModelHRTF),
+ database_loader_(database_loader),
+ sample_rate_(sample_rate),
+ crossfade_selection_(kCrossfadeSelection1),
+ azimuth_index1_(kUninitializedAzimuth),
+ elevation1_(0),
+ azimuth_index2_(kUninitializedAzimuth),
+ elevation2_(0),
+ crossfade_x_(0),
+ crossfade_incr_(0),
+ convolver_l1_(FftSizeForSampleRate(sample_rate)),
+ convolver_r1_(FftSizeForSampleRate(sample_rate)),
+ convolver_l2_(FftSizeForSampleRate(sample_rate)),
+ convolver_r2_(FftSizeForSampleRate(sample_rate)),
+ delay_line_l_(kMaxDelayTimeSeconds, sample_rate),
+ delay_line_r_(kMaxDelayTimeSeconds, sample_rate),
+ temp_l1_(AudioUtilities::kRenderQuantumFrames),
+ temp_r1_(AudioUtilities::kRenderQuantumFrames),
+ temp_l2_(AudioUtilities::kRenderQuantumFrames),
+ temp_r2_(AudioUtilities::kRenderQuantumFrames) {
+ DCHECK(database_loader);
+}
+
+HRTFPanner::~HRTFPanner() = default;
+
+size_t HRTFPanner::FftSizeForSampleRate(float sample_rate) {
+ // The HRTF impulse responses (loaded as audio resources) are 512
+ // sample-frames @44.1KHz. Currently, we truncate the impulse responses to
+ // half this size, but an FFT-size of twice impulse response size is needed
+ // (for convolution). So for sample rates around 44.1KHz an FFT size of 512
+ // is good. For different sample rates, the truncated response is resampled.
+ // The resampled length is used to compute the FFT size by choosing a power
+ // of two that is greater than or equal the resampled length. This power of
+ // two is doubled to get the actual FFT size.
+
+ DCHECK(AudioUtilities::IsValidAudioBufferSampleRate(sample_rate));
+
+ int truncated_impulse_length = 256;
+ double sample_rate_ratio = sample_rate / 44100;
+ double resampled_length = truncated_impulse_length * sample_rate_ratio;
+
+ return 2 * (1 << static_cast<unsigned>(log2(resampled_length)));
+}
+
+void HRTFPanner::Reset() {
+ convolver_l1_.Reset();
+ convolver_r1_.Reset();
+ convolver_l2_.Reset();
+ convolver_r2_.Reset();
+ delay_line_l_.Reset();
+ delay_line_r_.Reset();
+}
+
+int HRTFPanner::CalculateDesiredAzimuthIndexAndBlend(double azimuth,
+ double& azimuth_blend) {
+ // Convert the azimuth angle from the range -180 -> +180 into the range 0 ->
+ // 360. The azimuth index may then be calculated from this positive value.
+ if (azimuth < 0)
+ azimuth += 360.0;
+
+ int number_of_azimuths = HRTFDatabase::NumberOfAzimuths();
+ const double angle_between_azimuths = 360.0 / number_of_azimuths;
+
+ // Calculate the azimuth index and the blend (0 -> 1) for interpolation.
+ double desired_azimuth_index_float = azimuth / angle_between_azimuths;
+ int desired_azimuth_index = static_cast<int>(desired_azimuth_index_float);
+ azimuth_blend =
+ desired_azimuth_index_float - static_cast<double>(desired_azimuth_index);
+
+ // We don't immediately start using this azimuth index, but instead approach
+ // this index from the last index we rendered at. This minimizes the clicks
+ // and graininess for moving sources which occur otherwise.
+ desired_azimuth_index =
+ clampTo(desired_azimuth_index, 0, number_of_azimuths - 1);
+ return desired_azimuth_index;
+}
+
+void HRTFPanner::Pan(double desired_azimuth,
+ double elevation,
+ const AudioBus* input_bus,
+ AudioBus* output_bus,
+ size_t frames_to_process,
+ AudioBus::ChannelInterpretation channel_interpretation) {
+ unsigned num_input_channels = input_bus ? input_bus->NumberOfChannels() : 0;
+
+ bool is_input_good =
+ input_bus && num_input_channels >= 1 && num_input_channels <= 2;
+ DCHECK(is_input_good);
+
+ bool is_output_good = output_bus && output_bus->NumberOfChannels() == 2 &&
+ frames_to_process <= output_bus->length();
+ DCHECK(is_output_good);
+
+ if (!is_input_good || !is_output_good) {
+ if (output_bus)
+ output_bus->Zero();
+ return;
+ }
+
+ HRTFDatabase* database = database_loader_->Database();
+ if (!database) {
+ output_bus->CopyFrom(*input_bus, channel_interpretation);
+ return;
+ }
+
+ // IRCAM HRTF azimuths values from the loaded database is reversed from the
+ // panner's notion of azimuth.
+ double azimuth = -desired_azimuth;
+
+ bool is_azimuth_good = azimuth >= -180.0 && azimuth <= 180.0;
+ DCHECK(is_azimuth_good);
+ if (!is_azimuth_good) {
+ output_bus->Zero();
+ return;
+ }
+
+ // Normally, we'll just be dealing with mono sources.
+ // If we have a stereo input, implement stereo panning with left source
+ // processed by left HRTF, and right source by right HRTF.
+ const AudioChannel* input_channel_l =
+ input_bus->ChannelByType(AudioBus::kChannelLeft);
+ const AudioChannel* input_channel_r =
+ num_input_channels > 1 ? input_bus->ChannelByType(AudioBus::kChannelRight)
+ : nullptr;
+
+ // Get source and destination pointers.
+ const float* source_l = input_channel_l->Data();
+ const float* source_r =
+ num_input_channels > 1 ? input_channel_r->Data() : source_l;
+ float* destination_l =
+ output_bus->ChannelByType(AudioBus::kChannelLeft)->MutableData();
+ float* destination_r =
+ output_bus->ChannelByType(AudioBus::kChannelRight)->MutableData();
+
+ double azimuth_blend;
+ int desired_azimuth_index =
+ CalculateDesiredAzimuthIndexAndBlend(azimuth, azimuth_blend);
+
+ // Initially snap azimuth and elevation values to first values encountered.
+ if (azimuth_index1_ == kUninitializedAzimuth) {
+ azimuth_index1_ = desired_azimuth_index;
+ elevation1_ = elevation;
+ }
+ if (azimuth_index2_ == kUninitializedAzimuth) {
+ azimuth_index2_ = desired_azimuth_index;
+ elevation2_ = elevation;
+ }
+
+ // Cross-fade / transition over a period of around 45 milliseconds.
+ // This is an empirical value tuned to be a reasonable trade-off between
+ // smoothness and speed.
+ const double fade_frames = SampleRate() <= 48000 ? 2048 : 4096;
+
+ // Check for azimuth and elevation changes, initiating a cross-fade if needed.
+ if (!crossfade_x_ && crossfade_selection_ == kCrossfadeSelection1) {
+ if (desired_azimuth_index != azimuth_index1_ || elevation != elevation1_) {
+ // Cross-fade from 1 -> 2
+ crossfade_incr_ = 1 / fade_frames;
+ azimuth_index2_ = desired_azimuth_index;
+ elevation2_ = elevation;
+ }
+ }
+ if (crossfade_x_ == 1 && crossfade_selection_ == kCrossfadeSelection2) {
+ if (desired_azimuth_index != azimuth_index2_ || elevation != elevation2_) {
+ // Cross-fade from 2 -> 1
+ crossfade_incr_ = -1 / fade_frames;
+ azimuth_index1_ = desired_azimuth_index;
+ elevation1_ = elevation;
+ }
+ }
+
+ // This algorithm currently requires that we process in power-of-two size
+ // chunks at least AudioUtilities::kRenderQuantumFrames.
+ DCHECK_EQ(1UL << static_cast<int>(log2(frames_to_process)),
+ frames_to_process);
+ DCHECK_GE(frames_to_process, AudioUtilities::kRenderQuantumFrames);
+
+ const unsigned kFramesPerSegment = AudioUtilities::kRenderQuantumFrames;
+ const unsigned number_of_segments = frames_to_process / kFramesPerSegment;
+
+ for (unsigned segment = 0; segment < number_of_segments; ++segment) {
+ // Get the HRTFKernels and interpolated delays.
+ HRTFKernel* kernel_l1;
+ HRTFKernel* kernel_r1;
+ HRTFKernel* kernel_l2;
+ HRTFKernel* kernel_r2;
+ double frame_delay_l1;
+ double frame_delay_r1;
+ double frame_delay_l2;
+ double frame_delay_r2;
+ database->GetKernelsFromAzimuthElevation(azimuth_blend, azimuth_index1_,
+ elevation1_, kernel_l1, kernel_r1,
+ frame_delay_l1, frame_delay_r1);
+ database->GetKernelsFromAzimuthElevation(azimuth_blend, azimuth_index2_,
+ elevation2_, kernel_l2, kernel_r2,
+ frame_delay_l2, frame_delay_r2);
+
+ bool are_kernels_good = kernel_l1 && kernel_r1 && kernel_l2 && kernel_r2;
+ DCHECK(are_kernels_good);
+ if (!are_kernels_good) {
+ output_bus->Zero();
+ return;
+ }
+
+ DCHECK_LT(frame_delay_l1 / SampleRate(), kMaxDelayTimeSeconds);
+ DCHECK_LT(frame_delay_r1 / SampleRate(), kMaxDelayTimeSeconds);
+ DCHECK_LT(frame_delay_l2 / SampleRate(), kMaxDelayTimeSeconds);
+ DCHECK_LT(frame_delay_r2 / SampleRate(), kMaxDelayTimeSeconds);
+
+ // Crossfade inter-aural delays based on transitions.
+ double frame_delay_l =
+ (1 - crossfade_x_) * frame_delay_l1 + crossfade_x_ * frame_delay_l2;
+ double frame_delay_r =
+ (1 - crossfade_x_) * frame_delay_r1 + crossfade_x_ * frame_delay_r2;
+
+ // Calculate the source and destination pointers for the current segment.
+ unsigned offset = segment * kFramesPerSegment;
+ const float* segment_source_l = source_l + offset;
+ const float* segment_source_r = source_r + offset;
+ float* segment_destination_l = destination_l + offset;
+ float* segment_destination_r = destination_r + offset;
+
+ // First run through delay lines for inter-aural time difference.
+ delay_line_l_.SetDelayFrames(frame_delay_l);
+ delay_line_r_.SetDelayFrames(frame_delay_r);
+ delay_line_l_.Process(segment_source_l, segment_destination_l,
+ kFramesPerSegment);
+ delay_line_r_.Process(segment_source_r, segment_destination_r,
+ kFramesPerSegment);
+
+ bool needs_crossfading = crossfade_incr_;
+
+ // Have the convolvers render directly to the final destination if we're not
+ // cross-fading.
+ float* convolution_destination_l1 =
+ needs_crossfading ? temp_l1_.Data() : segment_destination_l;
+ float* convolution_destination_r1 =
+ needs_crossfading ? temp_r1_.Data() : segment_destination_r;
+ float* convolution_destination_l2 =
+ needs_crossfading ? temp_l2_.Data() : segment_destination_l;
+ float* convolution_destination_r2 =
+ needs_crossfading ? temp_r2_.Data() : segment_destination_r;
+
+ // Now do the convolutions.
+ // Note that we avoid doing convolutions on both sets of convolvers if we're
+ // not currently cross-fading.
+
+ if (crossfade_selection_ == kCrossfadeSelection1 || needs_crossfading) {
+ convolver_l1_.Process(kernel_l1->FftFrame(), segment_destination_l,
+ convolution_destination_l1, kFramesPerSegment);
+ convolver_r1_.Process(kernel_r1->FftFrame(), segment_destination_r,
+ convolution_destination_r1, kFramesPerSegment);
+ }
+
+ if (crossfade_selection_ == kCrossfadeSelection2 || needs_crossfading) {
+ convolver_l2_.Process(kernel_l2->FftFrame(), segment_destination_l,
+ convolution_destination_l2, kFramesPerSegment);
+ convolver_r2_.Process(kernel_r2->FftFrame(), segment_destination_r,
+ convolution_destination_r2, kFramesPerSegment);
+ }
+
+ if (needs_crossfading) {
+ // Apply linear cross-fade.
+ float x = crossfade_x_;
+ float incr = crossfade_incr_;
+ for (unsigned i = 0; i < kFramesPerSegment; ++i) {
+ segment_destination_l[i] = (1 - x) * convolution_destination_l1[i] +
+ x * convolution_destination_l2[i];
+ segment_destination_r[i] = (1 - x) * convolution_destination_r1[i] +
+ x * convolution_destination_r2[i];
+ x += incr;
+ }
+ // Update cross-fade value from local.
+ crossfade_x_ = x;
+
+ if (crossfade_incr_ > 0 && fabs(crossfade_x_ - 1) < crossfade_incr_) {
+ // We've fully made the crossfade transition from 1 -> 2.
+ crossfade_selection_ = kCrossfadeSelection2;
+ crossfade_x_ = 1;
+ crossfade_incr_ = 0;
+ } else if (crossfade_incr_ < 0 && fabs(crossfade_x_) < -crossfade_incr_) {
+ // We've fully made the crossfade transition from 2 -> 1.
+ crossfade_selection_ = kCrossfadeSelection1;
+ crossfade_x_ = 0;
+ crossfade_incr_ = 0;
+ }
+ }
+ }
+}
+
+void HRTFPanner::PanWithSampleAccurateValues(
+ double* desired_azimuth,
+ double* elevation,
+ const AudioBus* input_bus,
+ AudioBus* output_bus,
+ size_t frames_to_process,
+ AudioBus::ChannelInterpretation channel_interpretation) {
+ // Sample-accurate (a-rate) HRTF panner is not implemented, just k-rate. Just
+ // grab the current azimuth/elevation and use that.
+ //
+ // We are assuming that the inherent smoothing in the HRTF processing is good
+ // enough, and we don't want to increase the complexity of the HRTF panner by
+ // 15-20 times. (We need to compute one output sample for each possibly
+ // different impulse response. That N^2. Previously, we used an FFT to do
+ // them all at once for a complexity of N/log2(N). Hence, N/log2(N) times
+ // more complex.)
+ Pan(desired_azimuth[0], elevation[0], input_bus, output_bus,
+ frames_to_process, channel_interpretation);
+}
+
+bool HRTFPanner::RequiresTailProcessing() const {
+ // Always return true since the tail and latency are never zero.
+ return true;
+}
+
+double HRTFPanner::TailTime() const {
+ // Because HRTFPanner is implemented with a DelayKernel and a FFTConvolver,
+ // the tailTime of the HRTFPanner is the sum of the tailTime of the
+ // DelayKernel and the tailTime of the FFTConvolver, which is
+ // MaxDelayTimeSeconds and fftSize() / 2, respectively.
+ return kMaxDelayTimeSeconds +
+ (FftSize() / 2) / static_cast<double>(SampleRate());
+}
+
+double HRTFPanner::LatencyTime() const {
+ // The latency of a FFTConvolver is also fftSize() / 2, and is in addition to
+ // its tailTime of the same value.
+ return (FftSize() / 2) / static_cast<double>(SampleRate());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/hrtf_panner.h b/chromium/third_party/blink/renderer/platform/audio/hrtf_panner.h
new file mode 100644
index 00000000000..ce35804ec0c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/hrtf_panner.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2010, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_HRTF_PANNER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_HRTF_PANNER_H_
+
+#include "third_party/blink/renderer/platform/audio/audio_delay_dsp_kernel.h"
+#include "third_party/blink/renderer/platform/audio/fft_convolver.h"
+#include "third_party/blink/renderer/platform/audio/hrtf_database_loader.h"
+#include "third_party/blink/renderer/platform/audio/panner.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT HRTFPanner final : public Panner {
+ public:
+ HRTFPanner(float sample_rate, HRTFDatabaseLoader*);
+ ~HRTFPanner() override;
+
+ // Panner
+ void Pan(double azimuth,
+ double elevation,
+ const AudioBus* input_bus,
+ AudioBus* output_bus,
+ size_t frames_to_process,
+ AudioBus::ChannelInterpretation) override;
+ void PanWithSampleAccurateValues(double* azimuth,
+ double* elevation,
+ const AudioBus* input_bus,
+ AudioBus* output_bus,
+ size_t frames_to_process,
+ AudioBus::ChannelInterpretation) override;
+
+ void Reset() override;
+
+ size_t FftSize() const { return FftSizeForSampleRate(sample_rate_); }
+ static size_t FftSizeForSampleRate(float sample_rate);
+
+ float SampleRate() const { return sample_rate_; }
+
+ double TailTime() const override;
+ double LatencyTime() const override;
+ bool RequiresTailProcessing() const override;
+
+ private:
+ // Given an azimuth angle in the range -180 -> +180, returns the corresponding
+ // azimuth index for the database, and azimuthBlend which is an interpolation
+ // value from 0 -> 1.
+ int CalculateDesiredAzimuthIndexAndBlend(double azimuth,
+ double& azimuth_blend);
+
+ scoped_refptr<HRTFDatabaseLoader> database_loader_;
+
+ float sample_rate_;
+
+ // We maintain two sets of convolvers for smooth cross-faded interpolations
+ // when then azimuth and elevation are dynamically changing. When the
+ // azimuth and elevation are not changing, we simply process with one
+ // of the two sets. Initially we use CrossfadeSelection1 corresponding to
+ // m_convolverL1 and m_convolverR1. Whenever the azimuth or elevation
+ // changes, a crossfade is initiated to transition to the new position. So if
+ // we're currently processing with CrossfadeSelection1, then we transition to
+ // CrossfadeSelection2 (and vice versa). If we're in the middle of a
+ // transition, then we wait until it is complete before initiating a new
+ // transition.
+
+ // Selects either the convolver set (m_convolverL1, m_convolverR1) or
+ // (m_convolverL2, m_convolverR2).
+ enum CrossfadeSelection { kCrossfadeSelection1, kCrossfadeSelection2 };
+
+ CrossfadeSelection crossfade_selection_;
+
+ // azimuth/elevation for CrossfadeSelection1.
+ int azimuth_index1_;
+ double elevation1_;
+
+ // azimuth/elevation for CrossfadeSelection2.
+ int azimuth_index2_;
+ double elevation2_;
+
+ // A crossfade value 0 <= m_crossfadeX <= 1.
+ float crossfade_x_;
+
+ // Per-sample-frame crossfade value increment.
+ float crossfade_incr_;
+
+ FFTConvolver convolver_l1_;
+ FFTConvolver convolver_r1_;
+ FFTConvolver convolver_l2_;
+ FFTConvolver convolver_r2_;
+
+ AudioDelayDSPKernel delay_line_l_;
+ AudioDelayDSPKernel delay_line_r_;
+
+ AudioFloatArray temp_l1_;
+ AudioFloatArray temp_r1_;
+ AudioFloatArray temp_l2_;
+ AudioFloatArray temp_r2_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_HRTF_PANNER_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/iir_filter.cc b/chromium/third_party/blink/renderer/platform/audio/iir_filter.cc
new file mode 100644
index 00000000000..1248ff48ef3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/iir_filter.cc
@@ -0,0 +1,223 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/audio/iir_filter.h"
+
+#include <algorithm>
+#include <complex>
+
+#include "third_party/blink/renderer/platform/audio/audio_utilities.h"
+#include "third_party/blink/renderer/platform/audio/vector_math.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+namespace blink {
+
+// The length of the memory buffers for the IIR filter. This MUST be a power of
+// two and must be greater than the possible length of the filter coefficients.
+const int kBufferLength = 32;
+static_assert(kBufferLength >= IIRFilter::kMaxOrder + 1,
+ "Internal IIR buffer length must be greater than maximum IIR "
+ "Filter order.");
+
+IIRFilter::IIRFilter(const AudioDoubleArray* feedforward,
+ const AudioDoubleArray* feedback)
+ : buffer_index_(0), feedback_(feedback), feedforward_(feedforward) {
+ // These are guaranteed to be zero-initialized.
+ x_buffer_.Allocate(kBufferLength);
+ y_buffer_.Allocate(kBufferLength);
+}
+
+IIRFilter::~IIRFilter() = default;
+
+void IIRFilter::Reset() {
+ x_buffer_.Zero();
+ y_buffer_.Zero();
+ buffer_index_ = 0;
+}
+
+static std::complex<double> EvaluatePolynomial(const double* coef,
+ std::complex<double> z,
+ int order) {
+ // Use Horner's method to evaluate the polynomial P(z) = sum(coef[k]*z^k, k,
+ // 0, order);
+ std::complex<double> result = 0;
+
+ for (int k = order; k >= 0; --k)
+ result = result * z + std::complex<double>(coef[k]);
+
+ return result;
+}
+
+void IIRFilter::Process(const float* source_p,
+ float* dest_p,
+ size_t frames_to_process) {
+ // Compute
+ //
+ // y[n] = sum(b[k] * x[n - k], k = 0, M) - sum(a[k] * y[n - k], k = 1, N)
+ //
+ // where b[k] are the feedforward coefficients and a[k] are the feedback
+ // coefficients of the filter.
+
+ // This is a Direct Form I implementation of an IIR Filter. Should we
+ // consider doing a different implementation such as Transposed Direct Form
+ // II?
+ const double* feedback = feedback_->Data();
+ const double* feedforward = feedforward_->Data();
+
+ DCHECK(feedback);
+ DCHECK(feedforward);
+
+ // Sanity check to see if the feedback coefficients have been scaled
+ // appropriately. It must be EXACTLY 1!
+ DCHECK_EQ(feedback[0], 1);
+
+ int feedback_length = feedback_->size();
+ int feedforward_length = feedforward_->size();
+ int min_length = std::min(feedback_length, feedforward_length);
+
+ double* x_buffer = x_buffer_.Data();
+ double* y_buffer = y_buffer_.Data();
+
+ for (size_t n = 0; n < frames_to_process; ++n) {
+ // To help minimize roundoff, we compute using double's, even though the
+ // filter coefficients only have single precision values.
+ double yn = feedforward[0] * source_p[n];
+
+ // Run both the feedforward and feedback terms together, when possible.
+ for (int k = 1; k < min_length; ++k) {
+ int n = (buffer_index_ - k) & (kBufferLength - 1);
+ yn += feedforward[k] * x_buffer[n];
+ yn -= feedback[k] * y_buffer[n];
+ }
+
+ // Handle any remaining feedforward or feedback terms.
+ for (int k = min_length; k < feedforward_length; ++k)
+ yn +=
+ feedforward[k] * x_buffer[(buffer_index_ - k) & (kBufferLength - 1)];
+
+ for (int k = min_length; k < feedback_length; ++k)
+ yn -= feedback[k] * y_buffer[(buffer_index_ - k) & (kBufferLength - 1)];
+
+ // Save the current input and output values in the memory buffers for the
+ // next output.
+ x_buffer_[buffer_index_] = source_p[n];
+ y_buffer_[buffer_index_] = yn;
+
+ buffer_index_ = (buffer_index_ + 1) & (kBufferLength - 1);
+
+ dest_p[n] = yn;
+ }
+}
+
+void IIRFilter::GetFrequencyResponse(int n_frequencies,
+ const float* frequency,
+ float* mag_response,
+ float* phase_response) {
+ // Evaluate the z-transform of the filter at the given normalized frequencies
+ // from 0 to 1. (One corresponds to the Nyquist frequency.)
+ //
+ // The z-tranform of the filter is
+ //
+ // H(z) = sum(b[k]*z^(-k), k, 0, M) / sum(a[k]*z^(-k), k, 0, N);
+ //
+ // The desired frequency response is H(exp(j*omega)), where omega is in [0,
+ // 1).
+ //
+ // Let P(x) = sum(c[k]*x^k, k, 0, P) be a polynomial of order P. Then each of
+ // the sums in H(z) is equivalent to evaluating a polynomial at the point
+ // 1/z.
+
+ for (int k = 0; k < n_frequencies; ++k) {
+ if (frequency[k] < 0 || frequency[k] > 1) {
+ // Out-of-bounds frequencies should return NaN.
+ mag_response[k] = std::nanf("");
+ phase_response[k] = std::nanf("");
+ } else {
+ // zRecip = 1/z = exp(-j*frequency)
+ double omega = -piDouble * frequency[k];
+ std::complex<double> z_recip =
+ std::complex<double>(cos(omega), sin(omega));
+
+ std::complex<double> numerator = EvaluatePolynomial(
+ feedforward_->Data(), z_recip, feedforward_->size() - 1);
+ std::complex<double> denominator =
+ EvaluatePolynomial(feedback_->Data(), z_recip, feedback_->size() - 1);
+ std::complex<double> response = numerator / denominator;
+ mag_response[k] = static_cast<float>(abs(response));
+ phase_response[k] =
+ static_cast<float>(atan2(imag(response), real(response)));
+ }
+ }
+}
+
+double IIRFilter::TailTime(double sample_rate) {
+ // The maximum tail time. This is somewhat arbitrary, but we're assuming that
+ // no one is going to expect the IIRFilter to produce an output after this
+ // much time after the inputs have stopped.
+ const double kMaxTailTime = 10;
+
+ // If the maximum amplitude of the impulse response is less than this, we
+ // assume that we've reached the tail of the response. Currently, this means
+ // that the impulse is less than 1 bit of a 16-bit PCM value.
+ const float kMaxTailAmplitude = 1 / 32768.0;
+
+ // How to compute the tail time? We're going to filter an impulse
+ // for |kMaxTailTime| seconds, in blocks of kRenderQuantumFrames at
+ // a time. The maximum magnitude of this block is saved. After all
+ // of the samples have been computed, find the last block with a
+ // maximum magnitude greater than |kMaxTaileAmplitude|. That block
+ // index + 1 will be the tail time. We don't need to be
+ // super-accurate in computing the tail time since we process on
+ // blocks, block accuracy is good enough, and the value just needs
+ // to be larger than the "real" tail time, so we don't prematurely
+ // zero out the output of the node.
+
+ // Number of render quanta needed to reach the max tail time.
+ int number_of_blocks = std::ceil(sample_rate * kMaxTailTime /
+ AudioUtilities::kRenderQuantumFrames);
+
+ // Input and output buffers for filtering.
+ AudioFloatArray input(AudioUtilities::kRenderQuantumFrames);
+ AudioFloatArray output(AudioUtilities::kRenderQuantumFrames);
+
+ // Array to hold the max magnitudes
+ AudioFloatArray magnitudes(number_of_blocks);
+
+ // Create the impulse input signal.
+ input[0] = 1;
+
+ // Process the first block and get the max magnitude of the output.
+ Process(input.Data(), output.Data(), AudioUtilities::kRenderQuantumFrames);
+ VectorMath::Vmaxmgv(output.Data(), 1, &magnitudes[0],
+ AudioUtilities::kRenderQuantumFrames);
+
+ // Process the rest of the signal, getting the max magnitude of the
+ // output for each block.
+ input[0] = 0;
+
+ for (int k = 1; k < number_of_blocks; ++k) {
+ Process(input.Data(), output.Data(), AudioUtilities::kRenderQuantumFrames);
+ VectorMath::Vmaxmgv(output.Data(), 1, &magnitudes[k],
+ AudioUtilities::kRenderQuantumFrames);
+ }
+
+ // Done computing the impulse response; reset the state so the actual node
+ // starts in the expected initial state.
+ Reset();
+
+ // Find the last block with amplitude greater than the threshold.
+ int index = number_of_blocks - 1;
+ for (int k = index; k >= 0; --k) {
+ if (magnitudes[k] > kMaxTailAmplitude) {
+ index = k;
+ break;
+ }
+ }
+
+ // The magnitude first become lower than the threshold at the next block.
+ // Compute the corresponding time value value; that's the tail time.
+ return (index + 1) * AudioUtilities::kRenderQuantumFrames / sample_rate;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/iir_filter.h b/chromium/third_party/blink/renderer/platform/audio/iir_filter.h
new file mode 100644
index 00000000000..152f8b1282e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/iir_filter.h
@@ -0,0 +1,68 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_IIR_FILTER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_IIR_FILTER_H_
+
+#include "third_party/blink/renderer/platform/audio/audio_array.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT IIRFilter final {
+ public:
+ // The maximum IIR filter order. This also limits the number of feedforward
+ // coefficients. The maximum number of coefficients is 20 according to the
+ // spec.
+ const static size_t kMaxOrder = 19;
+ IIRFilter(const AudioDoubleArray* feedforward_coef,
+ const AudioDoubleArray* feedback_coef);
+ ~IIRFilter();
+
+ void Process(const float* source_p, float* dest_p, size_t frames_to_process);
+
+ void Reset();
+
+ void GetFrequencyResponse(int n_frequencies,
+ const float* frequency,
+ float* mag_response,
+ float* phase_response);
+
+ // Compute the tail time of the IIR filter
+ double TailTime(double sample_rate);
+
+ // Reset the internal state of the IIR filter to the initial state.
+ void ResetState();
+
+ private:
+ // Filter memory
+ //
+ // For simplicity, we assume |m_xBuffer| and |m_yBuffer| have the same length,
+ // and the length is a power of two. Since the number of coefficients has a
+ // fixed upper length, the size of xBuffer and yBuffer is fixed. |m_xBuffer|
+ // holds the old input values and |m_yBuffer| holds the old output values
+ // needed to compute the new output value.
+ //
+ // m_yBuffer[m_bufferIndex] holds the most recent output value, say, y[n].
+ // Then m_yBuffer[m_bufferIndex - k] is y[n - k]. Similarly for m_xBuffer.
+ //
+ // To minimize roundoff, these arrays are double's instead of floats.
+ AudioDoubleArray x_buffer_;
+ AudioDoubleArray y_buffer_;
+
+ // Index into the xBuffer and yBuffer arrays where the most current x and y
+ // values should be stored. xBuffer[bufferIndex] corresponds to x[n], the
+ // current x input value and yBuffer[bufferIndex] is where y[n], the current
+ // output value.
+ int buffer_index_;
+
+ // Coefficients of the IIR filter. To minimize storage, these point to the
+ // arrays given in the constructor.
+ const AudioDoubleArray* feedback_;
+ const AudioDoubleArray* feedforward_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_IIR_FILTER_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/mac/fft_frame_mac.cc b/chromium/third_party/blink/renderer/platform/audio/mac/fft_frame_mac.cc
new file mode 100644
index 00000000000..65e991098b5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/mac/fft_frame_mac.cc
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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.
+ */
+
+// Mac OS X - specific FFTFrame implementation
+
+#include "build/build_config.h"
+
+#if defined(OS_MACOSX)
+
+#include "third_party/blink/renderer/platform/audio/fft_frame.h"
+#include "third_party/blink/renderer/platform/audio/vector_math.h"
+
+namespace blink {
+
+const int kMaxFFTPow2Size = 24;
+
+FFTSetup* FFTFrame::fft_setups_ = nullptr;
+
+// Normal constructor: allocates for a given fftSize
+FFTFrame::FFTFrame(unsigned fft_size)
+ : real_data_(fft_size), imag_data_(fft_size) {
+ fft_size_ = fft_size;
+ log2fft_size_ = static_cast<unsigned>(log2(fft_size));
+
+ // We only allow power of two
+ DCHECK_EQ(1UL << log2fft_size_, fft_size_);
+
+ // Lazily create and share fftSetup with other frames
+ fft_setup_ = FftSetupForSize(fft_size);
+
+ // Setup frame data
+ frame_.realp = real_data_.Data();
+ frame_.imagp = imag_data_.Data();
+}
+
+// Creates a blank/empty frame (interpolate() must later be called)
+FFTFrame::FFTFrame() : real_data_(0), imag_data_(0) {
+ // Later will be set to correct values when interpolate() is called
+ frame_.realp = 0;
+ frame_.imagp = 0;
+
+ fft_size_ = 0;
+ log2fft_size_ = 0;
+}
+
+// Copy constructor
+FFTFrame::FFTFrame(const FFTFrame& frame)
+ : fft_size_(frame.fft_size_),
+ log2fft_size_(frame.log2fft_size_),
+ real_data_(frame.fft_size_),
+ imag_data_(frame.fft_size_),
+ fft_setup_(frame.fft_setup_) {
+ // Setup frame data
+ frame_.realp = real_data_.Data();
+ frame_.imagp = imag_data_.Data();
+
+ // Copy/setup frame data
+ unsigned nbytes = sizeof(float) * fft_size_;
+ memcpy(RealData(), frame.frame_.realp, nbytes);
+ memcpy(ImagData(), frame.frame_.imagp, nbytes);
+}
+
+FFTFrame::~FFTFrame() {}
+
+void FFTFrame::DoFFT(const float* data) {
+ AudioFloatArray scaled_data(fft_size_);
+ // veclib fft returns a result that is twice as large as would be expected.
+ // Compensate for that by scaling the input by half so the FFT has the
+ // correct scaling.
+ float scale = 0.5f;
+ VectorMath::Vsmul(data, 1, &scale, scaled_data.Data(), 1, fft_size_);
+
+ vDSP_ctoz((DSPComplex*)scaled_data.Data(), 2, &frame_, 1, fft_size_ / 2);
+ vDSP_fft_zrip(fft_setup_, &frame_, 1, log2fft_size_, FFT_FORWARD);
+}
+
+void FFTFrame::DoInverseFFT(float* data) {
+ vDSP_fft_zrip(fft_setup_, &frame_, 1, log2fft_size_, FFT_INVERSE);
+ vDSP_ztoc(&frame_, 1, (DSPComplex*)data, 2, fft_size_ / 2);
+
+ // Do final scaling so that x == IFFT(FFT(x))
+ float scale = 1.0f / fft_size_;
+ VectorMath::Vsmul(data, 1, &scale, data, 1, fft_size_);
+}
+
+FFTSetup FFTFrame::FftSetupForSize(unsigned fft_size) {
+ if (!fft_setups_) {
+ fft_setups_ = (FFTSetup*)malloc(sizeof(FFTSetup) * kMaxFFTPow2Size);
+ memset(fft_setups_, 0, sizeof(FFTSetup) * kMaxFFTPow2Size);
+ }
+
+ int pow2size = static_cast<int>(log2(fft_size));
+ DCHECK_LT(pow2size, kMaxFFTPow2Size);
+ if (!fft_setups_[pow2size])
+ fft_setups_[pow2size] = vDSP_create_fftsetup(pow2size, FFT_RADIX2);
+
+ return fft_setups_[pow2size];
+}
+
+void FFTFrame::Initialize() {}
+
+void FFTFrame::Cleanup() {
+ if (!fft_setups_)
+ return;
+
+ for (int i = 0; i < kMaxFFTPow2Size; ++i) {
+ if (fft_setups_[i])
+ vDSP_destroy_fftsetup(fft_setups_[i]);
+ }
+
+ free(fft_setups_);
+ fft_setups_ = nullptr;
+}
+
+} // namespace blink
+
+#endif // #if defined(OS_MACOSX)
diff --git a/chromium/third_party/blink/renderer/platform/audio/mac/vector_math_mac.h b/chromium/third_party/blink/renderer/platform/audio/mac/vector_math_mac.h
new file mode 100644
index 00000000000..26035dda573
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/mac/vector_math_mac.h
@@ -0,0 +1,150 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_MAC_VECTOR_MATH_MAC_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_MAC_VECTOR_MATH_MAC_H_
+
+#include <Accelerate/Accelerate.h>
+
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/audio/audio_array.h"
+
+namespace blink {
+namespace VectorMath {
+namespace Mac {
+
+// On the Mac we use the highly optimized versions in Accelerate.framework
+// In 32-bit mode (__ppc__ or __i386__) <Accelerate/Accelerate.h> includes
+// <vecLib/vDSP_translate.h> which defines macros of the same name as
+// our namespaced function names, so we must handle this case differently. Other
+// architectures (64bit, ARM, etc.) do not include this header file.
+
+static ALWAYS_INLINE void Conv(const float* source_p,
+ int source_stride,
+ const float* filter_p,
+ int filter_stride,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process,
+ size_t filter_size,
+ const AudioFloatArray* /*prepared_filter*/) {
+#if defined(ARCH_CPU_X86)
+ ::conv(source_p, source_stride, filter_p, filter_stride, dest_p, dest_stride,
+ frames_to_process, filter_size);
+#else
+ vDSP_conv(source_p, source_stride, filter_p, filter_stride, dest_p,
+ dest_stride, frames_to_process, filter_size);
+#endif
+}
+
+static ALWAYS_INLINE void Vadd(const float* source1p,
+ int source_stride1,
+ const float* source2p,
+ int source_stride2,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process) {
+#if defined(ARCH_CPU_X86)
+ ::vadd(source1p, source_stride1, source2p, source_stride2, dest_p,
+ dest_stride, frames_to_process);
+#else
+ vDSP_vadd(source1p, source_stride1, source2p, source_stride2, dest_p,
+ dest_stride, frames_to_process);
+#endif
+}
+
+static ALWAYS_INLINE void Vclip(const float* source_p,
+ int source_stride,
+ const float* low_threshold_p,
+ const float* high_threshold_p,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process) {
+ vDSP_vclip(source_p, source_stride, low_threshold_p, high_threshold_p, dest_p,
+ dest_stride, frames_to_process);
+}
+
+static ALWAYS_INLINE void Vmaxmgv(const float* source_p,
+ int source_stride,
+ float* max_p,
+ size_t frames_to_process) {
+ vDSP_maxmgv(source_p, source_stride, max_p, frames_to_process);
+}
+
+static ALWAYS_INLINE void Vmul(const float* source1p,
+ int source_stride1,
+ const float* source2p,
+ int source_stride2,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process) {
+#if defined(ARCH_CPU_X86)
+ ::vmul(source1p, source_stride1, source2p, source_stride2, dest_p,
+ dest_stride, frames_to_process);
+#else
+ vDSP_vmul(source1p, source_stride1, source2p, source_stride2, dest_p,
+ dest_stride, frames_to_process);
+#endif
+}
+
+static ALWAYS_INLINE void Vsma(const float* source_p,
+ int source_stride,
+ const float* scale,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process) {
+ vDSP_vsma(source_p, source_stride, scale, dest_p, dest_stride, dest_p,
+ dest_stride, frames_to_process);
+}
+
+static ALWAYS_INLINE void Vsmul(const float* source_p,
+ int source_stride,
+ const float* scale,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process) {
+#if defined(ARCH_CPU_X86)
+ ::vsmul(source_p, source_stride, scale, dest_p, dest_stride,
+ frames_to_process);
+#else
+ vDSP_vsmul(source_p, source_stride, scale, dest_p, dest_stride,
+ frames_to_process);
+#endif
+}
+
+static ALWAYS_INLINE void Vsvesq(const float* source_p,
+ int source_stride,
+ float* sum_p,
+ size_t frames_to_process) {
+ vDSP_svesq(source_p, source_stride, sum_p, frames_to_process);
+}
+
+static ALWAYS_INLINE void Zvmul(const float* real1p,
+ const float* imag1p,
+ const float* real2p,
+ const float* imag2p,
+ float* real_dest_p,
+ float* imag_dest_p,
+ size_t frames_to_process) {
+ DSPSplitComplex sc1;
+ DSPSplitComplex sc2;
+ DSPSplitComplex dest;
+ sc1.realp = const_cast<float*>(real1p);
+ sc1.imagp = const_cast<float*>(imag1p);
+ sc2.realp = const_cast<float*>(real2p);
+ sc2.imagp = const_cast<float*>(imag2p);
+ dest.realp = real_dest_p;
+ dest.imagp = imag_dest_p;
+#if defined(ARCH_CPU_X86)
+ ::zvmul(&sc1, 1, &sc2, 1, &dest, 1, frames_to_process, 1);
+#else
+ vDSP_zvmul(&sc1, 1, &sc2, 1, &dest, 1, frames_to_process, 1);
+#endif
+}
+
+} // namespace Mac
+} // namespace VectorMath
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_MAC_VECTOR_MATH_MAC_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/multi_channel_resampler.cc b/chromium/third_party/blink/renderer/platform/audio/multi_channel_resampler.cc
new file mode 100644
index 00000000000..1a1a7de5d3a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/multi_channel_resampler.cc
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/audio/multi_channel_resampler.h"
+
+#include <memory>
+#include "third_party/blink/renderer/platform/audio/audio_bus.h"
+
+namespace blink {
+
+namespace {
+
+// ChannelProvider provides a single channel of audio data (one channel at a
+// time) for each channel of data provided to us in a multi-channel provider.
+
+class ChannelProvider final : public AudioSourceProvider {
+ public:
+ ChannelProvider(AudioSourceProvider* multi_channel_provider,
+ unsigned number_of_channels)
+ : multi_channel_provider_(multi_channel_provider),
+ number_of_channels_(number_of_channels),
+ current_channel_(0),
+ frames_to_process_(0) {}
+
+ // provideInput() will be called once for each channel, starting with the
+ // first channel. Each time it's called, it will provide the next channel of
+ // data.
+ void ProvideInput(AudioBus* bus, size_t frames_to_process) override {
+ bool is_bus_good = bus && bus->NumberOfChannels() == 1;
+ DCHECK(is_bus_good);
+ if (!is_bus_good)
+ return;
+
+ // Get the data from the multi-channel provider when the first channel asks
+ // for it. For subsequent channels, we can just dish out the channel data
+ // from that (stored in m_multiChannelBus).
+ if (!current_channel_) {
+ frames_to_process_ = frames_to_process;
+ multi_channel_bus_ =
+ AudioBus::Create(number_of_channels_, frames_to_process);
+ multi_channel_provider_->ProvideInput(multi_channel_bus_.get(),
+ frames_to_process);
+ }
+
+ // All channels must ask for the same amount. This should always be the
+ // case, but let's just make sure.
+ bool is_good =
+ multi_channel_bus_.get() && frames_to_process == frames_to_process_;
+ DCHECK(is_good);
+ if (!is_good)
+ return;
+
+ // Copy the channel data from what we received from m_multiChannelProvider.
+ DCHECK_LE(current_channel_, number_of_channels_);
+ if (current_channel_ < number_of_channels_) {
+ memcpy(bus->Channel(0)->MutableData(),
+ multi_channel_bus_->Channel(current_channel_)->Data(),
+ sizeof(float) * frames_to_process);
+ ++current_channel_;
+ }
+ }
+
+ private:
+ AudioSourceProvider* multi_channel_provider_;
+ scoped_refptr<AudioBus> multi_channel_bus_;
+ unsigned number_of_channels_;
+ unsigned current_channel_;
+ // Used to verify that all channels ask for the same amount.
+ size_t frames_to_process_;
+};
+
+} // namespace
+
+MultiChannelResampler::MultiChannelResampler(double scale_factor,
+ unsigned number_of_channels)
+ : number_of_channels_(number_of_channels) {
+ // Create each channel's resampler.
+ for (unsigned channel_index = 0; channel_index < number_of_channels;
+ ++channel_index)
+ kernels_.push_back(std::make_unique<SincResampler>(scale_factor));
+}
+
+void MultiChannelResampler::Process(AudioSourceProvider* provider,
+ AudioBus* destination,
+ size_t frames_to_process) {
+ // The provider can provide us with multi-channel audio data. But each of our
+ // single-channel resamplers (kernels) below requires a provider which
+ // provides a single unique channel of data. channelProvider wraps the
+ // original multi-channel provider and dishes out one channel at a time.
+ ChannelProvider channel_provider(provider, number_of_channels_);
+
+ for (unsigned channel_index = 0; channel_index < number_of_channels_;
+ ++channel_index) {
+ // Depending on the sample-rate scale factor, and the internal buffering
+ // used in a SincResampler kernel, this call to process() will only
+ // sometimes call provideInput() on the channelProvider. However, if it
+ // calls provideInput() for the first channel, then it will call it for the
+ // remaining channels, since they all buffer in the same way and are
+ // processing the same number of frames.
+ kernels_[channel_index]->Process(
+ &channel_provider, destination->Channel(channel_index)->MutableData(),
+ frames_to_process);
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/multi_channel_resampler.h b/chromium/third_party/blink/renderer/platform/audio/multi_channel_resampler.h
new file mode 100644
index 00000000000..7b3dad9be25
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/multi_channel_resampler.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_MULTI_CHANNEL_RESAMPLER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_MULTI_CHANNEL_RESAMPLER_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/audio/sinc_resampler.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class AudioBus;
+
+class PLATFORM_EXPORT MultiChannelResampler {
+ USING_FAST_MALLOC(MultiChannelResampler);
+ WTF_MAKE_NONCOPYABLE(MultiChannelResampler);
+
+ public:
+ MultiChannelResampler(double scale_factor, unsigned number_of_channels);
+
+ // Process given AudioSourceProvider for streaming applications.
+ void Process(AudioSourceProvider*,
+ AudioBus* destination,
+ size_t frames_to_process);
+
+ private:
+ // FIXME: the mac port can have a more highly optimized implementation based
+ // on CoreAudio instead of SincResampler. For now the default implementation
+ // will be used on all ports.
+ // https://bugs.webkit.org/show_bug.cgi?id=75118
+
+ // Each channel will be resampled using a high-quality SincResampler.
+ Vector<std::unique_ptr<SincResampler>> kernels_;
+
+ unsigned number_of_channels_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_MULTI_CHANNEL_RESAMPLER_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/panner.cc b/chromium/third_party/blink/renderer/platform/audio/panner.cc
new file mode 100644
index 00000000000..dfb9d7cf7c4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/panner.cc
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/audio/panner.h"
+
+#include <memory>
+#include "third_party/blink/renderer/platform/audio/equal_power_panner.h"
+#include "third_party/blink/renderer/platform/audio/hrtf_panner.h"
+
+namespace blink {
+
+std::unique_ptr<Panner> Panner::Create(PanningModel model,
+ float sample_rate,
+ HRTFDatabaseLoader* database_loader) {
+ switch (model) {
+ case kPanningModelEqualPower:
+ return std::make_unique<EqualPowerPanner>(sample_rate);
+
+ case kPanningModelHRTF:
+ return std::make_unique<HRTFPanner>(sample_rate, database_loader);
+
+ default:
+ NOTREACHED();
+ return nullptr;
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/panner.h b/chromium/third_party/blink/renderer/platform/audio/panner.h
new file mode 100644
index 00000000000..4fa56c1f8c6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/panner.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_PANNER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_PANNER_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/audio/audio_bus.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class HRTFDatabaseLoader;
+
+// Abstract base class for panning a mono or stereo source.
+
+class PLATFORM_EXPORT Panner {
+ USING_FAST_MALLOC(Panner);
+ WTF_MAKE_NONCOPYABLE(Panner);
+
+ public:
+ // This values are used in histograms and should not be renumbered or deleted.
+ enum { kPanningModelEqualPower = 0, kPanningModelHRTF = 1 };
+
+ typedef unsigned PanningModel;
+
+ static std::unique_ptr<Panner> Create(PanningModel,
+ float sample_rate,
+ HRTFDatabaseLoader*);
+
+ virtual ~Panner() = default;
+ ;
+
+ virtual void Pan(double azimuth,
+ double elevation,
+ const AudioBus* input_bus,
+ AudioBus* output_bus,
+ size_t frames_to_process,
+ AudioBus::ChannelInterpretation) = 0;
+ virtual void PanWithSampleAccurateValues(double* azimuth,
+ double* elevation,
+ const AudioBus* input_bus,
+ AudioBus* output_bus,
+ size_t frames_to_process,
+ AudioBus::ChannelInterpretation) = 0;
+
+ virtual void Reset() = 0;
+
+ virtual double TailTime() const = 0;
+ virtual double LatencyTime() const = 0;
+ virtual bool RequiresTailProcessing() const = 0;
+
+ protected:
+ Panner(PanningModel model) : panning_model_(model) {}
+
+ PanningModel panning_model_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_PANNER_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/push_pull_fifo.cc b/chromium/third_party/blink/renderer/platform/audio/push_pull_fifo.cc
new file mode 100644
index 00000000000..21d6924539d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/push_pull_fifo.cc
@@ -0,0 +1,215 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/audio/push_pull_fifo.h"
+
+#include <memory>
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/audio/audio_utilities.h"
+#include "third_party/blink/renderer/platform/histogram.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+
+namespace blink {
+
+namespace {
+
+// Suppress the warning log if over/underflow happens more than 100 times.
+const unsigned kMaxMessagesToLog = 100;
+}
+
+const size_t PushPullFIFO::kMaxFIFOLength = 65536;
+
+PushPullFIFO::PushPullFIFO(unsigned number_of_channels, size_t fifo_length)
+ : fifo_length_(fifo_length) {
+ CHECK_LE(fifo_length_, kMaxFIFOLength);
+ fifo_bus_ = AudioBus::Create(number_of_channels, fifo_length_);
+}
+
+PushPullFIFO::~PushPullFIFO() {
+ // Capture metrics only after the FIFO is actually pulled.
+ if (pull_count_ == 0)
+ return;
+
+ // TODO(hongchan): The fast-shutdown process prevents the data below from
+ // being collected correctly. Consider using "outside metric collector" that
+ // survives the fast-shutdown.
+
+ // Capture the percentage of underflow happened based on the total pull count.
+ // (100 buckets of size 1) This is equivalent of
+ // "Media.AudioRendererMissedDeadline" metric for WebAudio.
+ DEFINE_STATIC_LOCAL(
+ LinearHistogram,
+ fifo_underflow_percentage_histogram,
+ ("WebAudio.PushPullFIFO.UnderflowPercentage", 1, 100, 101));
+ fifo_underflow_percentage_histogram.Count(
+ static_cast<int32_t>(100.0 * underflow_count_ / pull_count_));
+
+ // We only collect the underflow count because no overflow can happen in the
+ // current implementation. This is similar to
+ // "Media.AudioRendererAudioGlitches" metric for WebAudio, which is a simple
+ // flag indicates any instance of glitches during FIFO's lifetime.
+ DEFINE_STATIC_LOCAL(BooleanHistogram, fifo_underflow_glitches_histogram,
+ ("WebAudio.PushPullFIFO.UnderflowGlitches"));
+ fifo_underflow_glitches_histogram.Count(underflow_count_ > 0);
+}
+
+// Push the data from |input_bus| to FIFO. The size of push is determined by
+// the length of |input_bus|.
+void PushPullFIFO::Push(const AudioBus* input_bus) {
+ TRACE_EVENT1("webaudio", "PushPullFIFO::Push",
+ "input_bus length", input_bus->length());
+
+ MutexLocker locker(lock_);
+
+ CHECK(input_bus);
+ CHECK_EQ(input_bus->length(), AudioUtilities::kRenderQuantumFrames);
+ SECURITY_CHECK(input_bus->length() <= fifo_length_);
+ SECURITY_CHECK(index_write_ < fifo_length_);
+
+ const size_t input_bus_length = input_bus->length();
+ const size_t remainder = fifo_length_ - index_write_;
+
+ for (unsigned i = 0; i < fifo_bus_->NumberOfChannels(); ++i) {
+ float* fifo_bus_channel = fifo_bus_->Channel(i)->MutableData();
+ const float* input_bus_channel = input_bus->Channel(i)->Data();
+ if (remainder >= input_bus_length) {
+ // The remainder is big enough for the input data.
+ memcpy(fifo_bus_channel + index_write_, input_bus_channel,
+ input_bus_length * sizeof(*fifo_bus_channel));
+ } else {
+ // The input data overflows the remainder size. Wrap around the index.
+ memcpy(fifo_bus_channel + index_write_, input_bus_channel,
+ remainder * sizeof(*fifo_bus_channel));
+ memcpy(fifo_bus_channel, input_bus_channel + remainder,
+ (input_bus_length - remainder) * sizeof(*fifo_bus_channel));
+ }
+ }
+
+ // Update the write index; wrap it around if necessary.
+ index_write_ = (index_write_ + input_bus_length) % fifo_length_;
+
+ // In case of overflow, move the |index_read_| to the updated |index_write_|
+ // to avoid reading overwritten frames by the next pull.
+ if (input_bus_length > fifo_length_ - frames_available_) {
+ index_read_ = index_write_;
+ if (++overflow_count_ < kMaxMessagesToLog) {
+ LOG(WARNING) << "PushPullFIFO: overflow while pushing ("
+ << "overflowCount=" << overflow_count_
+ << ", availableFrames=" << frames_available_
+ << ", inputFrames=" << input_bus_length
+ << ", fifoLength=" << fifo_length_ << ")";
+ }
+ }
+
+ // Update the number of frames available in FIFO.
+ frames_available_ =
+ std::min(frames_available_ + input_bus_length, fifo_length_);
+ DCHECK_EQ((index_read_ + frames_available_) % fifo_length_, index_write_);
+}
+
+// Pull the data out of FIFO to |output_bus|. If remaining frame in the FIFO
+// is less than the frames to pull, provides remaining frame plus the silence.
+size_t PushPullFIFO::Pull(AudioBus* output_bus, size_t frames_requested) {
+ TRACE_EVENT2("webaudio", "PushPullFIFO::Pull",
+ "output_bus length", output_bus->length(),
+ "frames_requested", frames_requested);
+
+ MutexLocker locker(lock_);
+
+#if defined(OS_ANDROID)
+ if (!output_bus) {
+ // Log when outputBus or FIFO object is invalid. (crbug.com/692423)
+ LOG(WARNING) << "[WebAudio/PushPullFIFO::pull <" << static_cast<void*>(this)
+ << ">] |outputBus| is invalid.";
+ // Silently return to avoid crash.
+ return 0;
+ }
+
+ // The following checks are in place to catch the inexplicable crash.
+ // (crbug.com/692423)
+ if (frames_requested > output_bus->length()) {
+ LOG(WARNING) << "[WebAudio/PushPullFIFO::pull <" << static_cast<void*>(this)
+ << ">] framesRequested > outputBus->length() ("
+ << frames_requested << " > " << output_bus->length() << ")";
+ }
+ if (frames_requested > fifo_length_) {
+ LOG(WARNING) << "[WebAudio/PushPullFIFO::pull <" << static_cast<void*>(this)
+ << ">] framesRequested > fifo_length_ (" << frames_requested
+ << " > " << fifo_length_ << ")";
+ }
+ if (index_read_ >= fifo_length_) {
+ LOG(WARNING) << "[WebAudio/PushPullFIFO::pull <" << static_cast<void*>(this)
+ << ">] index_read_ >= fifo_length_ (" << index_read_
+ << " >= " << fifo_length_ << ")";
+ }
+#endif
+
+ CHECK(output_bus);
+ SECURITY_CHECK(frames_requested <= output_bus->length());
+ SECURITY_CHECK(frames_requested <= fifo_length_);
+ SECURITY_CHECK(index_read_ < fifo_length_);
+
+ const size_t remainder = fifo_length_ - index_read_;
+ const size_t frames_to_fill = std::min(frames_available_, frames_requested);
+
+ for (unsigned i = 0; i < fifo_bus_->NumberOfChannels(); ++i) {
+ const float* fifo_bus_channel = fifo_bus_->Channel(i)->Data();
+ float* output_bus_channel = output_bus->Channel(i)->MutableData();
+
+ // Fill up the output bus with the available frames first.
+ if (remainder >= frames_to_fill) {
+ // The remainder is big enough for the frames to pull.
+ memcpy(output_bus_channel, fifo_bus_channel + index_read_,
+ frames_to_fill * sizeof(*fifo_bus_channel));
+ } else {
+ // The frames to pull is bigger than the remainder size.
+ // Wrap around the index.
+ memcpy(output_bus_channel, fifo_bus_channel + index_read_,
+ remainder * sizeof(*fifo_bus_channel));
+ memcpy(output_bus_channel + remainder, fifo_bus_channel,
+ (frames_to_fill - remainder) * sizeof(*fifo_bus_channel));
+ }
+
+ // The frames available was not enough to fulfill the requested frames. Fill
+ // the rest of the channel with silence.
+ if (frames_requested > frames_to_fill) {
+ memset(output_bus_channel + frames_to_fill, 0,
+ (frames_requested - frames_to_fill) * sizeof(*output_bus_channel));
+ }
+ }
+
+ // Update the read index; wrap it around if necessary.
+ index_read_ = (index_read_ + frames_to_fill) % fifo_length_;
+
+ // In case of underflow, move the |indexWrite| to the updated |indexRead|.
+ if (frames_requested > frames_to_fill) {
+ index_write_ = index_read_;
+ if (underflow_count_++ < kMaxMessagesToLog) {
+ LOG(WARNING) << "PushPullFIFO: underflow while pulling ("
+ << "underflowCount=" << underflow_count_
+ << ", availableFrames=" << frames_available_
+ << ", requestedFrames=" << frames_requested
+ << ", fifoLength=" << fifo_length_ << ")";
+ }
+ }
+
+ // Update the number of frames in FIFO.
+ frames_available_ -= frames_to_fill;
+ DCHECK_EQ((index_read_ + frames_available_) % fifo_length_, index_write_);
+
+ pull_count_++;
+
+ // |frames_requested > frames_available_| means the frames in FIFO is not
+ // enough to fulfill the requested frames from the audio device.
+ return frames_requested > frames_available_
+ ? frames_requested - frames_available_
+ : 0;
+}
+
+const PushPullFIFOStateForTest PushPullFIFO::GetStateForTest() const {
+ return {length(), NumberOfChannels(), frames_available_, index_read_,
+ index_write_, overflow_count_, underflow_count_};
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/push_pull_fifo.h b/chromium/third_party/blink/renderer/platform/audio/push_pull_fifo.h
new file mode 100644
index 00000000000..3e6335ed5ee
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/push_pull_fifo.h
@@ -0,0 +1,101 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_PUSH_PULL_FIFO_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_PUSH_PULL_FIFO_H_
+
+#include "third_party/blink/public/platform/web_common.h"
+#include "third_party/blink/renderer/platform/audio/audio_bus.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+#include "third_party/blink/renderer/platform/wtf/threading.h"
+#include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
+
+namespace blink {
+
+// A configuration data container for PushPullFIFO unit test.
+struct PushPullFIFOStateForTest {
+ const size_t fifo_length;
+ const unsigned number_of_channels;
+ const size_t frames_available;
+ const size_t index_read;
+ const size_t index_write;
+ const unsigned overflow_count;
+ const unsigned underflow_count;
+};
+
+// PushPullFIFO class is an intermediate audio sample storage between
+// Blink-WebAudio and the renderer. The renderer's hardware callback buffer size
+// varies on the platform, but the WebAudio always renders 128 frames (render
+// quantum, RQ) thus FIFO is needed to handle the general case.
+//
+// Note that this object is concurrently accessed by two threads; WebAudio
+// rendering thread (WebThread) in Blink and the audio device thread
+// (AudioDeviceThread) from the media renderer. The push/pull operations touch
+// most of variables in the class (index_write_, index_read_, frames_available_,
+// and fifo_Bus_) so the thread safety must be handled with care.
+//
+// TODO(hongchan): add a unit test for multi-thread access.
+class BLINK_PLATFORM_EXPORT PushPullFIFO {
+ USING_FAST_MALLOC(PushPullFIFO);
+ WTF_MAKE_NONCOPYABLE(PushPullFIFO);
+
+ public:
+ // Maximum FIFO length. (512 render quanta)
+ static const size_t kMaxFIFOLength;
+
+ // |fifo_length| cannot exceed |kMaxFIFOLength|. Otherwise it crashes.
+ explicit PushPullFIFO(unsigned number_of_channels, size_t fifo_length);
+ ~PushPullFIFO();
+
+ // Pushes the rendered frames by WebAudio engine.
+ // - The |input_bus| length is 128 frames (1 render quantum), fixed.
+ // - In case of overflow (FIFO full while push), the existing frames in FIFO
+ // will be overwritten and |index_read_| will be forcibly moved to
+ // |index_write_| to avoid reading overwritten frames.
+ void Push(const AudioBus* input_bus);
+
+ // Pulls |frames_requested| by the audio device thread and returns the actual
+ // number of frames to be rendered by the source. (i.e. WebAudio graph)
+ // - If |frames_requested| is bigger than the length of |output_bus|, it
+ // violates SECURITY_CHECK().
+ // - If |frames_requested| is bigger than FIFO length, it violates
+ // SECURITY_CHECK().
+ // - In case of underflow (FIFO empty while pull), the remaining space in the
+ // requested output bus will be filled with silence. Thus it will fulfill
+ // the request from the consumer without causing error, but with a glitch.
+ size_t Pull(AudioBus* output_bus, size_t frames_requested);
+
+ size_t length() const { return fifo_length_; }
+ unsigned NumberOfChannels() const { return fifo_bus_->NumberOfChannels(); }
+
+ // TODO(hongchan): For single thread unit test only. Consider refactoring.
+ AudioBus* GetFIFOBusForTest() const { return fifo_bus_.get(); }
+
+ // For single thread unit test only. Get the current configuration that
+ // consists of FIFO length, number of channels, read/write index position and
+ // under/overflow count.
+ const PushPullFIFOStateForTest GetStateForTest() const;
+
+ private:
+ // The size of the FIFO.
+ const size_t fifo_length_ = 0;
+
+ // For UMA reporting purpose.
+ unsigned pull_count_ = 0;
+ unsigned overflow_count_ = 0;
+ unsigned underflow_count_ = 0;
+
+ // This lock protects variables below.
+ Mutex lock_;
+ // The number of frames in the FIFO actually available for pulling.
+ size_t frames_available_ = 0;
+ size_t index_read_ = 0;
+ size_t index_write_ = 0;
+ scoped_refptr<AudioBus> fifo_bus_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_PUSH_PULL_FIFO_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/push_pull_fifo_multithread_test.cc b/chromium/third_party/blink/renderer/platform/audio/push_pull_fifo_multithread_test.cc
new file mode 100644
index 00000000000..96563e12318
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/push_pull_fifo_multithread_test.cc
@@ -0,0 +1,225 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/audio/push_pull_fifo.h"
+
+#include <memory>
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_thread.h"
+#include "third_party/blink/renderer/platform/audio/audio_utilities.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
+#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
+#include "third_party/blink/renderer/platform/waitable_event.h"
+#include "third_party/blink/renderer/platform/web_task_runner.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+
+namespace blink {
+
+namespace {
+
+// Base FIFOClient with an extra thread for looping and jitter control. The
+// child class must define a specific task to run on the thread.
+class FIFOClient {
+ public:
+ FIFOClient(PushPullFIFO* fifo, size_t bus_length, size_t jitter_range_ms)
+ : fifo_(fifo),
+ bus_(AudioBus::Create(fifo->NumberOfChannels(), bus_length)),
+ client_thread_(Platform::Current()->CreateThread(
+ WebThreadCreationParams(WebThreadType::kTestThread)
+ .SetThreadNameForTest("FIFOClientThread"))),
+ done_event_(std::make_unique<WaitableEvent>()),
+ jitter_range_ms_(jitter_range_ms) {}
+
+ WaitableEvent* Start(double duration_ms, double interval_ms) {
+ duration_ms_ = duration_ms;
+ interval_ms_ = interval_ms;
+ PostCrossThreadTask(*client_thread_->GetTaskRunner(), FROM_HERE,
+ CrossThreadBind(&FIFOClient::RunTaskOnOwnThread,
+ CrossThreadUnretained(this)));
+ return done_event_.get();
+ }
+
+ virtual void Stop(int callback_counter) = 0;
+ virtual void RunTask() = 0;
+
+ void Pull(size_t frames_to_pull) { fifo_->Pull(bus_.get(), frames_to_pull); }
+
+ void Push() { fifo_->Push(bus_.get()); }
+
+ private:
+ void RunTaskOnOwnThread() {
+ double interval_with_jitter = interval_ms_
+ + (static_cast<double>(std::rand()) / RAND_MAX) * jitter_range_ms_;
+ elapsed_ms_ += interval_with_jitter;
+ ++counter_;
+ RunTask();
+ if (elapsed_ms_ < duration_ms_) {
+ PostDelayedCrossThreadTask(
+ *client_thread_->GetTaskRunner(), FROM_HERE,
+ CrossThreadBind(&FIFOClient::RunTaskOnOwnThread,
+ CrossThreadUnretained(this)),
+ TimeDelta::FromMillisecondsD(interval_with_jitter));
+ } else {
+ Stop(counter_);
+ done_event_->Signal();
+ }
+ }
+
+ // Should be instantiated before calling Platform::Current()->CreateThread().
+ // Do not place this after the |client_thread_| below.
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform_;
+
+ PushPullFIFO* fifo_;
+ scoped_refptr<AudioBus> bus_;
+ std::unique_ptr<WebThread> client_thread_;
+ std::unique_ptr<WaitableEvent> done_event_;
+
+ // Test duration.
+ double duration_ms_;
+
+ // Interval between each callback.
+ double interval_ms_;
+
+ // Jitter added to the regular pushing/pulling interval.
+ // (where j is 0 < j < jitter_range_ms)
+ double jitter_range_ms_;
+
+ // Elapsed test duration.
+ double elapsed_ms_ = 0;
+
+ // Counter variable for the total number of callbacks invoked.
+ int counter_ = 0;
+};
+
+// FIFO-pulling client (consumer). This mimics the audio device thread.
+// |frames_to_pull| is variable.
+class PullClient final : public FIFOClient {
+ public:
+ PullClient(PushPullFIFO* fifo, size_t frames_to_pull, double jitter_range_ms)
+ : FIFOClient(fifo, frames_to_pull, jitter_range_ms),
+ frames_to_pull_(frames_to_pull) {
+ }
+
+ void RunTask() override {
+ Pull(frames_to_pull_);
+ }
+
+ void Stop(int callback_counter) override {
+ LOG(INFO) << "PullClient stopped. (" << callback_counter << " calls)";
+ }
+
+ private:
+ size_t frames_to_pull_;
+};
+
+// FIFO-pushing client (producer). This mimics the WebAudio rendering thread.
+// The frames to push are static as 128 frames.
+class PushClient final : public FIFOClient {
+ public:
+ PushClient(PushPullFIFO* fifo, size_t frames_to_push, double jitter_range_ms)
+ : FIFOClient(fifo, frames_to_push, jitter_range_ms) {}
+
+ void RunTask() override {
+ Push();
+ }
+
+ void Stop(int callback_counter) override {
+ LOG(INFO) << "PushClient stopped. (" << callback_counter << " calls)";
+ }
+};
+
+struct FIFOSmokeTestParam {
+ const double sample_rate;
+ const unsigned number_of_channels;
+ const size_t fifo_length;
+ const double test_duration_ms;
+ // Buffer size for pulling. Equivalent of |callback_buffer_size|.
+ const size_t pull_buffer_size;
+ // Jitter range for the pulling interval.
+ const double pull_jitter_range_ms;
+ // Buffer size for pushing. Equivalent of WebAudio render quantum.
+ const size_t push_buffer_size;
+ // Jitter range for the pushing interval.
+ const double push_jitter_range_ms;
+};
+
+class PushPullFIFOSmokeTest
+ : public testing::TestWithParam<FIFOSmokeTestParam> {};
+
+TEST_P(PushPullFIFOSmokeTest, SmokeTests) {
+ const FIFOSmokeTestParam param = GetParam();
+ const double sample_rate = param.sample_rate * 4;
+
+ const double pull_interval_ms =
+ param.pull_buffer_size / sample_rate * 1000;
+ const double push_interval_ms =
+ param.push_buffer_size / sample_rate * 1000;
+
+ std::unique_ptr<PushPullFIFO> test_fifo = std::make_unique<PushPullFIFO>(
+ param.number_of_channels, param.fifo_length);
+ std::unique_ptr<PullClient> pull_client = std::make_unique<PullClient>(
+ test_fifo.get(), param.pull_buffer_size, param.pull_jitter_range_ms);
+ std::unique_ptr<PushClient> push_client = std::make_unique<PushClient>(
+ test_fifo.get(), param.push_buffer_size, param.push_jitter_range_ms);
+
+ Vector<WaitableEvent*> done_events;
+ done_events.push_back(
+ pull_client->Start(param.test_duration_ms, pull_interval_ms));
+ done_events.push_back(
+ push_client->Start(param.test_duration_ms, push_interval_ms));
+
+ LOG(INFO) << "PushPullFIFOSmokeTest - Started";
+
+ // We have to wait both of events to be signaled.
+ WaitableEvent::WaitMultiple(done_events);
+ WaitableEvent::WaitMultiple(done_events);
+}
+
+FIFOSmokeTestParam smoke_test_params[] = {
+ // Test case 0 (OSX): 256 Pull, 128 Push, Minimal jitter.
+ // WebThread's priority is lower than the device thread, so its jitter range
+ // is slightly bigger than the other.
+ {48000, 2, 8192, 250, 256, 1, 128, 2},
+
+ // Test case 1 (Windows): 441 Pull, 128 Push. Moderate Jitter.
+ // Windows' audio callback is known to be ~10ms and UMA data shows the
+ // evidence for it. The jitter range was determined speculatively.
+ {44100, 2, 8192, 250, 441, 2, 128, 3},
+
+ // Test case 2 (Ubuntu/Linux): 512 Pull, 128 Push. Unstable callback, but
+ // fast CPU. A typical configuration for Ubuntu + PulseAudio setup.
+ // PulseAudio's callback is known to be rather unstable.
+ {48000, 2, 8192, 250, 512, 8, 128, 1},
+
+ // Test case 3 (Android-Reference): 512 Pull, 128 Push. Similar to Linux, but
+ // low profile CPU.
+ {44100, 2, 8192, 250, 512, 8, 128, 3},
+
+ // Test case 4 (Android-ExternalA): 441 Pull, 128 Push. Extreme jitter with
+ // low profile CPU.
+ {44100, 2, 8192, 250, 441, 24, 128, 8},
+
+ // Test case 5 (Android-ExternalB): 5768 Pull, 128 Push. Huge callback with
+ // large jitter. Low profile CPU.
+ {44100, 2, 8192, 250, 5768, 120, 128, 12},
+
+ // Test case 6 (User-specified buffer size): 960 Pull, 128 Push. Minimal
+ // Jitter. 960 frames = 20ms at 48KHz.
+ {48000, 2, 8192, 250, 960, 1, 128, 1},
+
+ // Test case 7 (Longer test duration): 256 Pull, 128 Push. 2.5 seconds.
+ {48000, 2, 8192, 2500, 256, 0, 128, 1}
+};
+
+INSTANTIATE_TEST_CASE_P(PushPullFIFOSmokeTest,
+ PushPullFIFOSmokeTest,
+ testing::ValuesIn(smoke_test_params));
+
+} // namespace
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/push_pull_fifo_test.cc b/chromium/third_party/blink/renderer/platform/audio/push_pull_fifo_test.cc
new file mode 100644
index 00000000000..1c4558f3ec4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/push_pull_fifo_test.cc
@@ -0,0 +1,367 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/audio/push_pull_fifo.h"
+
+#include <memory>
+#include <vector>
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_thread.h"
+#include "third_party/blink/renderer/platform/audio/audio_utilities.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+
+namespace blink {
+
+namespace {
+
+// Check the basic contract of FIFO. This test only covers the single thread
+// scenario.
+TEST(PushPullFIFOBasicTest, BasicTests) {
+ // This suppresses the multi-thread warning for GTest. Potently it increases
+ // the test execution time, but this specific test is very short and simple.
+ testing::FLAGS_gtest_death_test_style = "threadsafe";
+
+ // FIFO length exceeding the maximum length allowed will cause crash.
+ // i.e.) fifo_length_ <= kMaxFIFOLength
+ EXPECT_DEATH(new PushPullFIFO(2, PushPullFIFO::kMaxFIFOLength + 1), "");
+
+ std::unique_ptr<PushPullFIFO> test_fifo =
+ std::make_unique<PushPullFIFO>(2, 1024);
+
+ // The input bus length must be |AudioUtilities::kRenderQuantumFrames|.
+ // i.e.) input_bus->length() == kRenderQuantumFrames
+ scoped_refptr<AudioBus> input_bus_129_frames =
+ AudioBus::Create(2, AudioUtilities::kRenderQuantumFrames + 1);
+ EXPECT_DEATH(test_fifo->Push(input_bus_129_frames.get()), "");
+ scoped_refptr<AudioBus> input_bus_127_frames =
+ AudioBus::Create(2, AudioUtilities::kRenderQuantumFrames - 1);
+ EXPECT_DEATH(test_fifo->Push(input_bus_127_frames.get()), "");
+
+ // Pull request frames cannot exceed the length of output bus.
+ // i.e.) frames_requested <= output_bus->length()
+ scoped_refptr<AudioBus> output_bus_512_frames = AudioBus::Create(2, 512);
+ EXPECT_DEATH(test_fifo->Pull(output_bus_512_frames.get(), 513), "");
+
+ // Pull request frames cannot exceed the length of FIFO.
+ // i.e.) frames_requested <= fifo_length_
+ scoped_refptr<AudioBus> output_bus_1025_frames = AudioBus::Create(2, 1025);
+ EXPECT_DEATH(test_fifo->Pull(output_bus_1025_frames.get(), 1025), "");
+}
+
+// Fills each AudioChannel in an AudioBus with a series of linearly increasing
+// values starting from |starting_value| and incrementing by 1. Then return
+// value will be |starting_value| + |bus_length|.
+size_t FillBusWithLinearRamp(AudioBus* target_bus, size_t starting_value) {
+ for (unsigned c = 0; c < target_bus->NumberOfChannels(); ++c) {
+ float* bus_channel = target_bus->Channel(c)->MutableData();
+ for (size_t i = 0; i < target_bus->Channel(c)->length(); ++i) {
+ bus_channel[i] = static_cast<float>(starting_value + i);
+ }
+ }
+ return starting_value + target_bus->length();
+}
+
+// Inspect the content of AudioBus with a given set of index and value across
+// channels.
+bool VerifyBusValueAtIndex(AudioBus* target_bus,
+ int index,
+ float expected_value) {
+ for (unsigned c = 0; c < target_bus->NumberOfChannels(); ++c) {
+ float* bus_channel = target_bus->Channel(c)->MutableData();
+ if (bus_channel[index] != expected_value) {
+ LOG(ERROR) << ">> [FAIL] expected " << expected_value << " at index "
+ << index << " but got " << bus_channel[index] << ".";
+ return false;
+ }
+ }
+ return true;
+}
+
+struct FIFOAction {
+ // The type of action; "PUSH" or "PULL".
+ const char* action;
+ // Number of frames for the operation.
+ const size_t number_of_frames;
+};
+
+struct AudioBusSample {
+ // The frame index of a sample in the bus.
+ const size_t index;
+ // The value at the |index| above.
+ const float value;
+};
+
+struct FIFOTestSetup {
+ // Length of FIFO to be created for test case.
+ const size_t fifo_length;
+ // Channel count of FIFO to be created for test case.
+ const unsigned number_of_channels;
+ // A list of |FIFOAction| entries to be performed in test case.
+ const std::vector<FIFOAction> fifo_actions;
+};
+
+struct FIFOTestExpectedState {
+ // Expected read index in FIFO.
+ const size_t index_read;
+ // Expected write index in FIFO.
+ const size_t index_write;
+ // Expected overflow count in FIFO.
+ const unsigned overflow_count;
+ // Expected underflow count in FIFO.
+ const unsigned underflow_count;
+ // A list of expected |AudioBusSample| entries for the FIFO bus.
+ const std::vector<AudioBusSample> fifo_samples;
+ // A list of expected |AudioBusSample| entries for the output bus.
+ const std::vector<AudioBusSample> output_samples;
+};
+
+// The data structure for the parameterized test cases.
+struct FIFOTestParam {
+ FIFOTestSetup setup;
+ FIFOTestExpectedState expected_state;
+};
+
+std::ostream& operator<<(std::ostream& out, const FIFOTestParam& param) {
+ out << "fifoLength=" << param.setup.fifo_length
+ << " numberOfChannels=" << param.setup.number_of_channels;
+ return out;
+}
+
+class PushPullFIFOFeatureTest : public testing::TestWithParam<FIFOTestParam> {};
+
+TEST_P(PushPullFIFOFeatureTest, FeatureTests) {
+ const FIFOTestSetup setup = GetParam().setup;
+ const FIFOTestExpectedState expected_state = GetParam().expected_state;
+
+ // Create a FIFO with a specified configuration.
+ std::unique_ptr<PushPullFIFO> fifo = std::make_unique<PushPullFIFO>(
+ setup.number_of_channels, setup.fifo_length);
+
+ scoped_refptr<AudioBus> output_bus;
+
+ // Iterate all the scheduled push/pull actions.
+ size_t frame_counter = 0;
+ for (const auto& action : setup.fifo_actions) {
+ if (strcmp(action.action, "PUSH") == 0) {
+ scoped_refptr<AudioBus> input_bus =
+ AudioBus::Create(setup.number_of_channels, action.number_of_frames);
+ frame_counter = FillBusWithLinearRamp(input_bus.get(), frame_counter);
+ fifo->Push(input_bus.get());
+ LOG(INFO) << "PUSH " << action.number_of_frames
+ << " frames (frameCounter=" << frame_counter << ")";
+ } else {
+ output_bus =
+ AudioBus::Create(setup.number_of_channels, action.number_of_frames);
+ fifo->Pull(output_bus.get(), action.number_of_frames);
+ LOG(INFO) << "PULL " << action.number_of_frames << " frames";
+ }
+ }
+
+ // Get FIFO config data.
+ const PushPullFIFOStateForTest actual_state = fifo->GetStateForTest();
+
+ // Verify the read/write indexes.
+ EXPECT_EQ(expected_state.index_read, actual_state.index_read);
+ EXPECT_EQ(expected_state.index_write, actual_state.index_write);
+ EXPECT_EQ(expected_state.overflow_count, actual_state.overflow_count);
+ EXPECT_EQ(expected_state.underflow_count, actual_state.underflow_count);
+
+ // Verify in-FIFO samples.
+ for (const auto& sample : expected_state.fifo_samples) {
+ EXPECT_TRUE(VerifyBusValueAtIndex(fifo->GetFIFOBusForTest(),
+ sample.index, sample.value));
+ }
+
+ // Verify samples from the most recent output bus.
+ for (const auto& sample : expected_state.output_samples) {
+ EXPECT_TRUE(
+ VerifyBusValueAtIndex(output_bus.get(), sample.index, sample.value));
+ }
+}
+
+FIFOTestParam g_feature_test_params[] = {
+ // Test cases 0 ~ 3: Regular operation on various channel configuration.
+ // - Mono, Stereo, Quad, 5.1.
+ // - FIFO length and pull size are RQ-aligned.
+ {{512, 1, {{"PUSH", 128}, {"PUSH", 128}, {"PULL", 256}}},
+ {256, 256, 0, 0, {{0, 0}}, {{0, 0}, {255, 255}}}},
+
+ {{512, 2, {{"PUSH", 128}, {"PUSH", 128}, {"PULL", 256}}},
+ {256, 256, 0, 0, {{0, 0}}, {{0, 0}, {255, 255}}}},
+
+ {{512, 4, {{"PUSH", 128}, {"PUSH", 128}, {"PULL", 256}}},
+ {256, 256, 0, 0, {{0, 0}}, {{0, 0}, {255, 255}}}},
+
+ {{512, 6, {{"PUSH", 128}, {"PUSH", 128}, {"PULL", 256}}},
+ {256, 256, 0, 0, {{0, 0}}, {{0, 0}, {255, 255}}}},
+
+ // Test case 4: Pull size less than or equal to 128.
+ {{128, 2, {{"PUSH", 128}, {"PULL", 128}, {"PUSH", 128}, {"PULL", 64}}},
+ {64, 0, 0, 0, {{64, 192}, {0, 128}}, {{0, 128}, {63, 191}}}},
+
+ // Test case 5: Unusual FIFO and Pull length.
+ // - FIFO and pull length that are not aligned to render quantum.
+ // - Check if the indexes are wrapping around correctly.
+ // - Check if the output bus starts and ends with correct values.
+ {{997,
+ 1,
+ {
+ {"PUSH", 128},
+ {"PUSH", 128},
+ {"PUSH", 128},
+ {"PUSH", 128},
+ {"PULL", 449},
+ {"PUSH", 128},
+ {"PUSH", 128},
+ {"PUSH", 128},
+ {"PUSH", 128},
+ {"PULL", 449},
+ }},
+ // - expectedIndexRead = 898, expectedIndexWrite = 27
+ // - overflowCount = 0, underflowCount = 0
+ // - FIFO samples (index, expectedValue) = (898, 898), (27, 27)
+ // - Output bus samples (index, expectedValue) = (0, 499), (448, 897)
+ {898, 27, 0, 0, {{898, 898}, {27, 27}}, {{0, 449}, {448, 897}}}},
+
+ // Test case 6: Overflow
+ // - Check overflow counter.
+ // - After the overflow occurs, the read index must be moved to the write
+ // index. Thus pulled frames must not contain overwritten data.
+ {{512,
+ 3,
+ {
+ {"PUSH", 128},
+ {"PUSH", 128},
+ {"PUSH", 128},
+ {"PUSH", 128},
+ {"PUSH", 128},
+ {"PULL", 256},
+ }},
+ // - expectedIndexRead = 384, expectedIndexWrite = 128
+ // - overflowCount = 1, underflowCount = 0
+ // - FIFO samples (index, expectedValue) = (384, 384), (128, 128)
+ // - Output bus samples (index, expectedValue) = (0, 128), (255, 383)
+ {384, 128, 1, 0, {{384, 384}, {128, 128}}, {{0, 128}, {255, 383}}}},
+
+ // Test case 7: Overflow in unusual FIFO and pull length.
+ // - Check overflow counter.
+ // - After the overflow occurs, the read index must be moved to the write
+ // index. Thus pulled frames must not contain overwritten data.
+ {{577,
+ 5,
+ {
+ {"PUSH", 128},
+ {"PUSH", 128},
+ {"PUSH", 128},
+ {"PUSH", 128},
+ {"PUSH", 128},
+ {"PULL", 227},
+ }},
+ // - expectedIndexRead = 290, expectedIndexWrite = 63
+ // - overflowCount = 1, underflowCount = 0
+ // - FIFO samples (index, expectedValue) = (63, 63), (290, 290)
+ // - Output bus samples (index, expectedValue) = (0, 63), (226, 289)
+ {290, 63, 1, 0, {{63, 63}, {290, 290}}, {{0, 63}, {226, 289}}}},
+
+ // Test case 8: Underflow
+ // - Check underflow counter.
+ // - After the underflow occurs, the write index must be moved to the read
+ // index. Frames pulled after FIFO underflows must be zeroed.
+ {{512,
+ 7,
+ {
+ {"PUSH", 128},
+ {"PUSH", 128},
+ {"PUSH", 128},
+ {"PULL", 384},
+ {"PUSH", 128},
+ {"PUSH", 128},
+ {"PULL", 384},
+ }},
+ // - expectedIndexRead = 128, expectedIndexWrite = 128
+ // - overflowCount = 0, underflowCount = 1
+ // - FIFO samples (index, expectedValue) = (128, 128)
+ // - Output bus samples (index, expectedValue) = (0, 384), (255, 639)
+ // (256, 0), (383, 0)
+ {128,
+ 128,
+ 0,
+ 1,
+ {{128, 128}},
+ {{0, 384}, {255, 639}, {256, 0}, {383, 0}}}},
+
+ // Test case 9: Underflow in unusual FIFO and pull length.
+ // - Check underflow counter.
+ // - After the underflow occurs, the write index must be moved to the read
+ // index. Frames pulled after FIFO underflows must be zeroed.
+ {{523,
+ 11,
+ {
+ {"PUSH", 128},
+ {"PUSH", 128},
+ {"PUSH", 128},
+ {"PULL", 383},
+ {"PUSH", 128},
+ {"PUSH", 128},
+ {"PULL", 383},
+ }},
+ // - expectedIndexRead = 117, expectedIndexWrite = 117
+ // - overflowCount = 0, underflowCount = 1
+ // - FIFO samples (index, expectedValue) = (117, 117)
+ // - Output bus samples (index, expectedValue) = (0, 383), (256, 639)
+ // (257, 0), (382, 0)
+ {117,
+ 117,
+ 0,
+ 1,
+ {{117, 117}},
+ {{0, 383}, {256, 639}, {257, 0}, {382, 0}}}},
+
+ // Test case 10: Multiple pull from an empty FIFO.
+ // - Check underflow counter.
+ // - After the underflow occurs, the write index must be moved to the read
+ // index. Frames pulled after FIFO underflows must be zeroed.
+ {{1024,
+ 11,
+ {
+ {"PUSH", 128},
+ {"PUSH", 128},
+ {"PULL", 440},
+ {"PULL", 440},
+ {"PULL", 440},
+ {"PULL", 440},
+ {"PULL", 440},
+ }},
+ // - expectedIndexRead = 117, expectedIndexWrite = 117
+ // - overflowCount = 0, underflowCount = 1
+ // - FIFO samples (index, expectedValue) = (117, 117)
+ // - Output bus samples (index, expectedValue) = (0, 383), (256, 639)
+ // (257, 0), (382, 0)
+ {256, 256, 0, 5, {{256, 0}}, {{0, 0}, {439, 0}}}},
+
+ // Test case 11: Multiple pull from an empty FIFO. (zero push)
+ {{1024,
+ 11,
+ {
+ {"PULL", 144},
+ {"PULL", 144},
+ {"PULL", 144},
+ {"PULL", 144},
+ }},
+ // - expectedIndexRead = 0, expectedIndexWrite = 0
+ // - overflowCount = 0, underflowCount = 4
+ // - FIFO samples (index, expectedValue) = (0, 0), (1023, 0)
+ // - Output bus samples (index, expectedValue) = (0, 0), (143, 0)
+ {0, 0, 0, 4, {{0, 0}, {1023, 0}}, {{0, 0}, {143, 0}}}}};
+
+INSTANTIATE_TEST_CASE_P(PushPullFIFOFeatureTest,
+ PushPullFIFOFeatureTest,
+ testing::ValuesIn(g_feature_test_params));
+
+} // namespace
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/Composite.flac b/chromium/third_party/blink/renderer/platform/audio/resources/Composite.flac
new file mode 100644
index 00000000000..d67abe48d57
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/Composite.flac
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P000.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P000.wav
new file mode 100644
index 00000000000..57f2ef32eb2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P000.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P015.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P015.wav
new file mode 100644
index 00000000000..3ecea337867
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P015.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P030.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P030.wav
new file mode 100644
index 00000000000..7320802ac8f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P030.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P045.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P045.wav
new file mode 100644
index 00000000000..1a10d9ac44f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P045.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P060.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P060.wav
new file mode 100644
index 00000000000..9b12c228e11
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P060.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P075.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P075.wav
new file mode 100644
index 00000000000..3153bb86af0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P075.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P090.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P090.wav
new file mode 100644
index 00000000000..3282da981f8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P090.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P315.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P315.wav
new file mode 100644
index 00000000000..b9998521a6a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P315.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P330.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P330.wav
new file mode 100644
index 00000000000..53a03b68818
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P330.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P345.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P345.wav
new file mode 100644
index 00000000000..16d57668e30
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T000_P345.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P000.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P000.wav
new file mode 100644
index 00000000000..3788e169e10
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P000.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P015.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P015.wav
new file mode 100644
index 00000000000..ad2efb6d336
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P015.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P030.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P030.wav
new file mode 100644
index 00000000000..ee86702b30e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P030.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P045.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P045.wav
new file mode 100644
index 00000000000..98ff82e045f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P045.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P060.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P060.wav
new file mode 100644
index 00000000000..98ff82e045f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P060.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P075.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P075.wav
new file mode 100644
index 00000000000..98ff82e045f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P075.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P090.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P090.wav
new file mode 100644
index 00000000000..98ff82e045f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P090.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P315.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P315.wav
new file mode 100644
index 00000000000..10bdf78573e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P315.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P330.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P330.wav
new file mode 100644
index 00000000000..c8398a48f69
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P330.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P345.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P345.wav
new file mode 100644
index 00000000000..82b92a81bc4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T015_P345.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P000.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P000.wav
new file mode 100644
index 00000000000..8b8714ce4bc
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P000.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P015.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P015.wav
new file mode 100644
index 00000000000..882efd482a1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P015.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P030.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P030.wav
new file mode 100644
index 00000000000..abd99e64eff
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P030.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P045.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P045.wav
new file mode 100644
index 00000000000..28765be4f13
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P045.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P060.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P060.wav
new file mode 100644
index 00000000000..42c144505a4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P060.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P075.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P075.wav
new file mode 100644
index 00000000000..42c144505a4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P075.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P090.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P090.wav
new file mode 100644
index 00000000000..42c144505a4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P090.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P315.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P315.wav
new file mode 100644
index 00000000000..99b00f748bd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P315.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P330.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P330.wav
new file mode 100644
index 00000000000..f81bee2a940
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P330.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P345.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P345.wav
new file mode 100644
index 00000000000..139d0cb9afc
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T030_P345.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P000.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P000.wav
new file mode 100644
index 00000000000..68b7b4fda79
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P000.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P015.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P015.wav
new file mode 100644
index 00000000000..d6773aec50d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P015.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P030.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P030.wav
new file mode 100644
index 00000000000..9e786bbcf9e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P030.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P045.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P045.wav
new file mode 100644
index 00000000000..fbef2f384f4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P045.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P060.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P060.wav
new file mode 100644
index 00000000000..fbef2f384f4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P060.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P075.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P075.wav
new file mode 100644
index 00000000000..fbef2f384f4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P075.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P090.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P090.wav
new file mode 100644
index 00000000000..fbef2f384f4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P090.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P315.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P315.wav
new file mode 100644
index 00000000000..3c53b767047
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P315.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P330.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P330.wav
new file mode 100644
index 00000000000..e4524c0eb13
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P330.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P345.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P345.wav
new file mode 100644
index 00000000000..ff12535f799
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T045_P345.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P000.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P000.wav
new file mode 100644
index 00000000000..5bb1b175390
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P000.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P015.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P015.wav
new file mode 100644
index 00000000000..47e0209e654
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P015.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P030.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P030.wav
new file mode 100644
index 00000000000..536b4ac9056
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P030.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P045.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P045.wav
new file mode 100644
index 00000000000..05152ad862d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P045.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P060.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P060.wav
new file mode 100644
index 00000000000..221637b4f9c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P060.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P075.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P075.wav
new file mode 100644
index 00000000000..7d6d07f4635
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P075.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P090.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P090.wav
new file mode 100644
index 00000000000..7d6d07f4635
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P090.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P315.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P315.wav
new file mode 100644
index 00000000000..a4eca78f849
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P315.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P330.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P330.wav
new file mode 100644
index 00000000000..37393c21a10
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P330.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P345.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P345.wav
new file mode 100644
index 00000000000..3d56e26c958
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T060_P345.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P000.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P000.wav
new file mode 100644
index 00000000000..2159f3debc7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P000.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P015.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P015.wav
new file mode 100644
index 00000000000..8b689f6c477
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P015.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P030.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P030.wav
new file mode 100644
index 00000000000..9ee8ac5894d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P030.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P045.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P045.wav
new file mode 100644
index 00000000000..88124e91464
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P045.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P060.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P060.wav
new file mode 100644
index 00000000000..88124e91464
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P060.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P075.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P075.wav
new file mode 100644
index 00000000000..88124e91464
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P075.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P090.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P090.wav
new file mode 100644
index 00000000000..88124e91464
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P090.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P315.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P315.wav
new file mode 100644
index 00000000000..59441a617e7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P315.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P330.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P330.wav
new file mode 100644
index 00000000000..7cac0f5bbca
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P330.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P345.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P345.wav
new file mode 100644
index 00000000000..dc28d64c7fb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T075_P345.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P000.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P000.wav
new file mode 100644
index 00000000000..ae7e5833817
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P000.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P015.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P015.wav
new file mode 100644
index 00000000000..509449e984c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P015.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P030.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P030.wav
new file mode 100644
index 00000000000..e23b20ca54a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P030.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P045.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P045.wav
new file mode 100644
index 00000000000..cf247b98b9e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P045.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P060.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P060.wav
new file mode 100644
index 00000000000..f49d52049a8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P060.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P075.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P075.wav
new file mode 100644
index 00000000000..f49d52049a8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P075.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P090.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P090.wav
new file mode 100644
index 00000000000..f49d52049a8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P090.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P315.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P315.wav
new file mode 100644
index 00000000000..e5472f1fc8a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P315.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P330.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P330.wav
new file mode 100644
index 00000000000..8e1af836997
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P330.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P345.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P345.wav
new file mode 100644
index 00000000000..c477193a1cb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T090_P345.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P000.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P000.wav
new file mode 100644
index 00000000000..4236e085f07
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P000.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P015.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P015.wav
new file mode 100644
index 00000000000..2461fb0783e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P015.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P030.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P030.wav
new file mode 100644
index 00000000000..11d549becef
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P030.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P045.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P045.wav
new file mode 100644
index 00000000000..0aa100e3d89
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P045.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P060.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P060.wav
new file mode 100644
index 00000000000..0aa100e3d89
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P060.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P075.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P075.wav
new file mode 100644
index 00000000000..0aa100e3d89
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P075.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P090.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P090.wav
new file mode 100644
index 00000000000..0aa100e3d89
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P090.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P315.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P315.wav
new file mode 100644
index 00000000000..572e602991b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P315.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P330.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P330.wav
new file mode 100644
index 00000000000..7f41da37c32
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P330.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P345.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P345.wav
new file mode 100644
index 00000000000..d0101b81005
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T105_P345.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P000.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P000.wav
new file mode 100644
index 00000000000..800fbd783bb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P000.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P015.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P015.wav
new file mode 100644
index 00000000000..9b35e726464
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P015.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P030.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P030.wav
new file mode 100644
index 00000000000..bb58c4eb749
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P030.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P045.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P045.wav
new file mode 100644
index 00000000000..8963e3e56d4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P045.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P060.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P060.wav
new file mode 100644
index 00000000000..22241ee9a79
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P060.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P075.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P075.wav
new file mode 100644
index 00000000000..9e4fee09e80
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P075.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P090.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P090.wav
new file mode 100644
index 00000000000..9e4fee09e80
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P090.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P315.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P315.wav
new file mode 100644
index 00000000000..95976c6492d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P315.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P330.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P330.wav
new file mode 100644
index 00000000000..8fc55f12bc1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P330.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P345.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P345.wav
new file mode 100644
index 00000000000..eeeb70276c9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T120_P345.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P000.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P000.wav
new file mode 100644
index 00000000000..1847d8d26d6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P000.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P015.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P015.wav
new file mode 100644
index 00000000000..52e812c8b69
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P015.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P030.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P030.wav
new file mode 100644
index 00000000000..32ca3447152
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P030.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P045.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P045.wav
new file mode 100644
index 00000000000..3de60c8737f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P045.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P060.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P060.wav
new file mode 100644
index 00000000000..3de60c8737f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P060.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P075.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P075.wav
new file mode 100644
index 00000000000..3de60c8737f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P075.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P090.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P090.wav
new file mode 100644
index 00000000000..3de60c8737f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P090.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P315.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P315.wav
new file mode 100644
index 00000000000..2668e41d0fe
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P315.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P330.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P330.wav
new file mode 100644
index 00000000000..e69670baacf
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P330.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P345.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P345.wav
new file mode 100644
index 00000000000..1cc48eed3f0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T135_P345.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P000.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P000.wav
new file mode 100644
index 00000000000..252968b67d1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P000.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P015.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P015.wav
new file mode 100644
index 00000000000..50aff3c13de
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P015.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P030.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P030.wav
new file mode 100644
index 00000000000..3abd6e89001
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P030.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P045.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P045.wav
new file mode 100644
index 00000000000..3f0d5ef936b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P045.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P060.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P060.wav
new file mode 100644
index 00000000000..616f760d522
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P060.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P075.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P075.wav
new file mode 100644
index 00000000000..616f760d522
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P075.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P090.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P090.wav
new file mode 100644
index 00000000000..616f760d522
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P090.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P315.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P315.wav
new file mode 100644
index 00000000000..bfb603225d9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P315.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P330.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P330.wav
new file mode 100644
index 00000000000..1983cdbfd3c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P330.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P345.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P345.wav
new file mode 100644
index 00000000000..27c07628a6a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T150_P345.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P000.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P000.wav
new file mode 100644
index 00000000000..b04391b9fb8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P000.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P015.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P015.wav
new file mode 100644
index 00000000000..5955612bbe1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P015.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P030.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P030.wav
new file mode 100644
index 00000000000..af5d83a5da2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P030.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P045.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P045.wav
new file mode 100644
index 00000000000..a592f717ffb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P045.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P060.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P060.wav
new file mode 100644
index 00000000000..a592f717ffb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P060.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P075.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P075.wav
new file mode 100644
index 00000000000..a592f717ffb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P075.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P090.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P090.wav
new file mode 100644
index 00000000000..a592f717ffb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P090.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P315.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P315.wav
new file mode 100644
index 00000000000..a985aa15c4d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P315.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P330.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P330.wav
new file mode 100644
index 00000000000..a8b83d1e336
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P330.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P345.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P345.wav
new file mode 100644
index 00000000000..7e649a3af1a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T165_P345.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P000.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P000.wav
new file mode 100644
index 00000000000..b74985c9446
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P000.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P015.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P015.wav
new file mode 100644
index 00000000000..e112ee07048
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P015.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P030.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P030.wav
new file mode 100644
index 00000000000..ac842ccb070
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P030.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P045.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P045.wav
new file mode 100644
index 00000000000..95c3a6df07e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P045.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P060.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P060.wav
new file mode 100644
index 00000000000..610eedba585
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P060.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P075.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P075.wav
new file mode 100644
index 00000000000..d4a57bfb573
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P075.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P090.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P090.wav
new file mode 100644
index 00000000000..d4a57bfb573
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P090.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P315.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P315.wav
new file mode 100644
index 00000000000..bd6e4f8d85a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P315.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P330.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P330.wav
new file mode 100644
index 00000000000..7d4be6f1e89
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P330.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P345.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P345.wav
new file mode 100644
index 00000000000..b7ef81a9d0a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T180_P345.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P000.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P000.wav
new file mode 100644
index 00000000000..0c4af2a8613
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P000.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P015.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P015.wav
new file mode 100644
index 00000000000..dd7a5056faa
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P015.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P030.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P030.wav
new file mode 100644
index 00000000000..e1690496cae
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P030.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P045.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P045.wav
new file mode 100644
index 00000000000..49008a067ca
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P045.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P060.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P060.wav
new file mode 100644
index 00000000000..49008a067ca
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P060.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P075.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P075.wav
new file mode 100644
index 00000000000..49008a067ca
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P075.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P090.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P090.wav
new file mode 100644
index 00000000000..49008a067ca
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P090.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P315.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P315.wav
new file mode 100644
index 00000000000..1e7d478cd3b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P315.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P330.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P330.wav
new file mode 100644
index 00000000000..2a77d74d887
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P330.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P345.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P345.wav
new file mode 100644
index 00000000000..843b92865f2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T195_P345.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P000.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P000.wav
new file mode 100644
index 00000000000..770af176a34
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P000.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P015.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P015.wav
new file mode 100644
index 00000000000..437a1c6d342
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P015.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P030.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P030.wav
new file mode 100644
index 00000000000..f0d9d8e3f28
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P030.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P045.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P045.wav
new file mode 100644
index 00000000000..cd4ae55a071
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P045.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P060.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P060.wav
new file mode 100644
index 00000000000..7dd5a1a2ec4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P060.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P075.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P075.wav
new file mode 100644
index 00000000000..7dd5a1a2ec4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P075.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P090.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P090.wav
new file mode 100644
index 00000000000..7dd5a1a2ec4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P090.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P315.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P315.wav
new file mode 100644
index 00000000000..bfac19d6615
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P315.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P330.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P330.wav
new file mode 100644
index 00000000000..8b3e086ff7d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P330.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P345.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P345.wav
new file mode 100644
index 00000000000..8b4da465b3a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T210_P345.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P000.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P000.wav
new file mode 100644
index 00000000000..b6d47036a33
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P000.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P015.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P015.wav
new file mode 100644
index 00000000000..0ff35e93a58
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P015.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P030.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P030.wav
new file mode 100644
index 00000000000..e934c7837b6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P030.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P045.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P045.wav
new file mode 100644
index 00000000000..c931e3ca2eb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P045.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P060.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P060.wav
new file mode 100644
index 00000000000..c931e3ca2eb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P060.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P075.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P075.wav
new file mode 100644
index 00000000000..c931e3ca2eb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P075.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P090.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P090.wav
new file mode 100644
index 00000000000..c931e3ca2eb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P090.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P315.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P315.wav
new file mode 100644
index 00000000000..f9999666454
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P315.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P330.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P330.wav
new file mode 100644
index 00000000000..d95859015bb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P330.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P345.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P345.wav
new file mode 100644
index 00000000000..ac062606820
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T225_P345.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P000.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P000.wav
new file mode 100644
index 00000000000..b720ed16b84
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P000.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P015.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P015.wav
new file mode 100644
index 00000000000..b48852a7e80
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P015.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P030.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P030.wav
new file mode 100644
index 00000000000..92c7ef04f09
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P030.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P045.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P045.wav
new file mode 100644
index 00000000000..2d5ff652de0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P045.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P060.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P060.wav
new file mode 100644
index 00000000000..07dcfde6afd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P060.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P075.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P075.wav
new file mode 100644
index 00000000000..283e250334a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P075.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P090.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P090.wav
new file mode 100644
index 00000000000..283e250334a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P090.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P315.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P315.wav
new file mode 100644
index 00000000000..b99ad7de245
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P315.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P330.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P330.wav
new file mode 100644
index 00000000000..48869156a22
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P330.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P345.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P345.wav
new file mode 100644
index 00000000000..c93283343eb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T240_P345.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P000.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P000.wav
new file mode 100644
index 00000000000..b204deff4ed
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P000.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P015.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P015.wav
new file mode 100644
index 00000000000..fa48113c022
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P015.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P030.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P030.wav
new file mode 100644
index 00000000000..2e2de705aeb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P030.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P045.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P045.wav
new file mode 100644
index 00000000000..685f102d379
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P045.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P060.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P060.wav
new file mode 100644
index 00000000000..685f102d379
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P060.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P075.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P075.wav
new file mode 100644
index 00000000000..685f102d379
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P075.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P090.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P090.wav
new file mode 100644
index 00000000000..685f102d379
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P090.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P315.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P315.wav
new file mode 100644
index 00000000000..c7cce6ecce3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P315.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P330.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P330.wav
new file mode 100644
index 00000000000..93a6b8a7808
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P330.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P345.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P345.wav
new file mode 100644
index 00000000000..efc72bc5ee1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T255_P345.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P000.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P000.wav
new file mode 100644
index 00000000000..8f490785cb4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P000.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P015.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P015.wav
new file mode 100644
index 00000000000..96510f7b262
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P015.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P030.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P030.wav
new file mode 100644
index 00000000000..60b84f49072
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P030.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P045.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P045.wav
new file mode 100644
index 00000000000..ec995e6c06b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P045.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P060.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P060.wav
new file mode 100644
index 00000000000..e287d0e4a42
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P060.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P075.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P075.wav
new file mode 100644
index 00000000000..e287d0e4a42
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P075.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P090.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P090.wav
new file mode 100644
index 00000000000..e287d0e4a42
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P090.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P315.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P315.wav
new file mode 100644
index 00000000000..01f0921731e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P315.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P330.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P330.wav
new file mode 100644
index 00000000000..380e7073c2c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P330.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P345.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P345.wav
new file mode 100644
index 00000000000..124a5348a82
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T270_P345.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P000.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P000.wav
new file mode 100644
index 00000000000..1a577f37f8c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P000.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P015.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P015.wav
new file mode 100644
index 00000000000..ce698bbd0aa
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P015.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P030.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P030.wav
new file mode 100644
index 00000000000..8d5134ff88f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P030.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P045.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P045.wav
new file mode 100644
index 00000000000..583c6edb901
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P045.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P060.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P060.wav
new file mode 100644
index 00000000000..583c6edb901
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P060.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P075.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P075.wav
new file mode 100644
index 00000000000..583c6edb901
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P075.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P090.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P090.wav
new file mode 100644
index 00000000000..583c6edb901
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P090.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P315.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P315.wav
new file mode 100644
index 00000000000..7b1b43badb2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P315.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P330.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P330.wav
new file mode 100644
index 00000000000..e9ed7edc879
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P330.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P345.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P345.wav
new file mode 100644
index 00000000000..6ce83edf96d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T285_P345.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P000.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P000.wav
new file mode 100644
index 00000000000..b4ea6bfb238
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P000.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P015.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P015.wav
new file mode 100644
index 00000000000..76d5b7127df
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P015.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P030.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P030.wav
new file mode 100644
index 00000000000..04ee0034f0b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P030.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P045.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P045.wav
new file mode 100644
index 00000000000..22d74137b9d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P045.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P060.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P060.wav
new file mode 100644
index 00000000000..1b35018446d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P060.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P075.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P075.wav
new file mode 100644
index 00000000000..2f55df8a071
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P075.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P090.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P090.wav
new file mode 100644
index 00000000000..2f55df8a071
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P090.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P315.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P315.wav
new file mode 100644
index 00000000000..7bcc8a425ee
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P315.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P330.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P330.wav
new file mode 100644
index 00000000000..a3bacf3a53a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P330.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P345.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P345.wav
new file mode 100644
index 00000000000..bdfba2dfceb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T300_P345.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P000.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P000.wav
new file mode 100644
index 00000000000..719320c0a33
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P000.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P015.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P015.wav
new file mode 100644
index 00000000000..5d366fc3bc7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P015.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P030.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P030.wav
new file mode 100644
index 00000000000..e10e88bcd1f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P030.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P045.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P045.wav
new file mode 100644
index 00000000000..ecb4b50ea2b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P045.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P060.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P060.wav
new file mode 100644
index 00000000000..ecb4b50ea2b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P060.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P075.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P075.wav
new file mode 100644
index 00000000000..ecb4b50ea2b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P075.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P090.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P090.wav
new file mode 100644
index 00000000000..ecb4b50ea2b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P090.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P315.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P315.wav
new file mode 100644
index 00000000000..35c44d4082f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P315.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P330.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P330.wav
new file mode 100644
index 00000000000..8fe859bfd2f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P330.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P345.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P345.wav
new file mode 100644
index 00000000000..3e44b83087e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T315_P345.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P000.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P000.wav
new file mode 100644
index 00000000000..e8782204a18
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P000.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P015.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P015.wav
new file mode 100644
index 00000000000..7628cbc9e7f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P015.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P030.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P030.wav
new file mode 100644
index 00000000000..7c4430c27ea
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P030.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P045.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P045.wav
new file mode 100644
index 00000000000..55e3c5ec8fe
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P045.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P060.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P060.wav
new file mode 100644
index 00000000000..563313e9ce2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P060.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P075.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P075.wav
new file mode 100644
index 00000000000..563313e9ce2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P075.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P090.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P090.wav
new file mode 100644
index 00000000000..563313e9ce2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P090.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P315.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P315.wav
new file mode 100644
index 00000000000..3eccc16d06d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P315.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P330.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P330.wav
new file mode 100644
index 00000000000..fd3f5e1f5bd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P330.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P345.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P345.wav
new file mode 100644
index 00000000000..5937c5930c1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T330_P345.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P000.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P000.wav
new file mode 100644
index 00000000000..99dc851bf70
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P000.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P015.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P015.wav
new file mode 100644
index 00000000000..28994d57c73
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P015.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P030.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P030.wav
new file mode 100644
index 00000000000..beb24a2357a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P030.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P045.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P045.wav
new file mode 100644
index 00000000000..f840c591efa
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P045.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P060.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P060.wav
new file mode 100644
index 00000000000..f840c591efa
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P060.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P075.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P075.wav
new file mode 100644
index 00000000000..f840c591efa
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P075.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P090.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P090.wav
new file mode 100644
index 00000000000..f840c591efa
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P090.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P315.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P315.wav
new file mode 100644
index 00000000000..68baa8ea7f9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P315.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P330.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P330.wav
new file mode 100644
index 00000000000..6cb01b8f463
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P330.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P345.wav b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P345.wav
new file mode 100644
index 00000000000..b2ae88c78af
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/IRC_Composite_C_R0195_T345_P345.wav
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/audio/resources/README b/chromium/third_party/blink/renderer/platform/audio/resources/README
new file mode 100644
index 00000000000..90d3fd6255e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/resources/README
@@ -0,0 +1,20 @@
+See https://bugs.webkit.org/show_bug.cgi?id=98294#c9.
+
+Here is how to create Composite.wav from the indidividual responses:
+
+AUDIO_DIR="WebKit/Source/platform/audio/resources"
+sox $( \
+ for azimuth in {0..23}; do \
+ for elevation in 0 15 30 45 60 75 90 315 330 345; do \
+ printf "$AUDIO_DIR/IRC_Composite_C_R0195_T%03d_P%03d.wav\n" "$(expr $azimuth \* 15)" "$elevation"; \
+ done; \
+ done;) \
+Composite.wav
+
+To create the FLAC-encoded version:
+
+avconv -i Composite.wav Composite.flac
+
+If you change Composite.flac (or Composite.wav), be sure to update
+LayoutTests/webaudio/resources/hrtf with the updated files!
+
diff --git a/chromium/third_party/blink/renderer/platform/audio/reverb.cc b/chromium/third_party/blink/renderer/platform/audio/reverb.cc
new file mode 100644
index 00000000000..0738a6991b8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/reverb.cc
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/audio/reverb.h"
+
+#include <math.h>
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/audio/audio_bus.h"
+#include "third_party/blink/renderer/platform/audio/vector_math.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+namespace blink {
+
+using namespace VectorMath;
+
+// Empirical gain calibration tested across many impulse responses to ensure
+// perceived volume is same as dry (unprocessed) signal
+const float kGainCalibration = -58;
+const float kGainCalibrationSampleRate = 44100;
+
+// A minimum power value to when normalizing a silent (or very quiet) impulse
+// response
+const float kMinPower = 0.000125f;
+
+static float CalculateNormalizationScale(AudioBus* response) {
+ // Normalize by RMS power
+ size_t number_of_channels = response->NumberOfChannels();
+ size_t length = response->length();
+
+ float power = 0;
+
+ for (size_t i = 0; i < number_of_channels; ++i) {
+ float channel_power = 0;
+ Vsvesq(response->Channel(i)->Data(), 1, &channel_power, length);
+ power += channel_power;
+ }
+
+ power = sqrt(power / (number_of_channels * length));
+
+ // Protect against accidental overload
+ if (std::isinf(power) || std::isnan(power) || power < kMinPower)
+ power = kMinPower;
+
+ float scale = 1 / power;
+
+ scale *= powf(
+ 10, kGainCalibration *
+ 0.05f); // calibrate to make perceived volume same as unprocessed
+
+ // Scale depends on sample-rate.
+ if (response->SampleRate())
+ scale *= kGainCalibrationSampleRate / response->SampleRate();
+
+ // True-stereo compensation
+ if (response->NumberOfChannels() == 4)
+ scale *= 0.5f;
+
+ return scale;
+}
+
+Reverb::Reverb(AudioBus* impulse_response,
+ size_t render_slice_size,
+ size_t max_fft_size,
+ bool use_background_threads,
+ bool normalize) {
+ float scale = 1;
+
+ if (normalize) {
+ scale = CalculateNormalizationScale(impulse_response);
+
+ if (scale)
+ impulse_response->Scale(scale);
+ }
+
+ Initialize(impulse_response, render_slice_size, max_fft_size,
+ use_background_threads);
+
+ // Undo scaling since this shouldn't be a destructive operation on
+ // impulseResponse.
+ // FIXME: What about roundoff? Perhaps consider making a temporary scaled copy
+ // instead of scaling and unscaling in place.
+ if (normalize && scale)
+ impulse_response->Scale(1 / scale);
+}
+
+void Reverb::Initialize(AudioBus* impulse_response_buffer,
+ size_t render_slice_size,
+ size_t max_fft_size,
+ bool use_background_threads) {
+ impulse_response_length_ = impulse_response_buffer->length();
+ number_of_response_channels_ = impulse_response_buffer->NumberOfChannels();
+
+ // The reverb can handle a mono impulse response and still do stereo
+ // processing.
+ unsigned num_convolvers = std::max(number_of_response_channels_, 2u);
+ convolvers_.ReserveCapacity(num_convolvers);
+
+ int convolver_render_phase = 0;
+ for (unsigned i = 0; i < num_convolvers; ++i) {
+ AudioChannel* channel = impulse_response_buffer->Channel(
+ std::min(i, number_of_response_channels_ - 1));
+
+ std::unique_ptr<ReverbConvolver> convolver =
+ std::make_unique<ReverbConvolver>(channel, render_slice_size,
+ max_fft_size, convolver_render_phase,
+ use_background_threads);
+ convolvers_.push_back(std::move(convolver));
+
+ convolver_render_phase += render_slice_size;
+ }
+
+ // For "True" stereo processing we allocate a temporary buffer to avoid
+ // repeatedly allocating it in the process() method. It can be bad to
+ // allocate memory in a real-time thread.
+ if (number_of_response_channels_ == 4)
+ temp_buffer_ = AudioBus::Create(2, kMaxFrameSize);
+}
+
+void Reverb::Process(const AudioBus* source_bus,
+ AudioBus* destination_bus,
+ size_t frames_to_process) {
+ // Do a fairly comprehensive sanity check.
+ // If these conditions are satisfied, all of the source and destination
+ // pointers will be valid for the various matrixing cases.
+ bool is_safe_to_process = source_bus && destination_bus &&
+ source_bus->NumberOfChannels() > 0 &&
+ destination_bus->NumberOfChannels() > 0 &&
+ frames_to_process <= kMaxFrameSize &&
+ frames_to_process <= source_bus->length() &&
+ frames_to_process <= destination_bus->length();
+
+ DCHECK(is_safe_to_process);
+ if (!is_safe_to_process)
+ return;
+
+ // For now only handle mono or stereo output
+ if (destination_bus->NumberOfChannels() > 2) {
+ destination_bus->Zero();
+ return;
+ }
+
+ AudioChannel* destination_channel_l = destination_bus->Channel(0);
+ const AudioChannel* source_channel_l = source_bus->Channel(0);
+
+ // Handle input -> output matrixing...
+ size_t num_input_channels = source_bus->NumberOfChannels();
+ size_t num_output_channels = destination_bus->NumberOfChannels();
+ size_t number_of_response_channels = number_of_response_channels_;
+
+ DCHECK_LE(num_input_channels, 2ul);
+ DCHECK_LE(num_output_channels, 2ul);
+ DCHECK(number_of_response_channels == 1 || number_of_response_channels == 2 ||
+ number_of_response_channels == 4);
+
+ // These are the possible combinations of number inputs, response
+ // channels and outputs channels that need to be supported:
+ //
+ // numInputChannels: 1 or 2
+ // numberOfResponseChannels: 1, 2, or 4
+ // numOutputChannels: 1 or 2
+ //
+ // Not all possible combinations are valid. numOutputChannels is
+ // one only if both numInputChannels and numberOfResponseChannels are 1.
+ // Otherwise numOutputChannels MUST be 2.
+ //
+ // The valid combinations are
+ //
+ // Case in -> resp -> out
+ // 1 1 -> 1 -> 1
+ // 2 1 -> 2 -> 2
+ // 3 1 -> 4 -> 2
+ // 4 2 -> 1 -> 2
+ // 5 2 -> 2 -> 2
+ // 6 2 -> 4 -> 2
+
+ if (num_input_channels == 2 &&
+ (number_of_response_channels == 1 || number_of_response_channels == 2) &&
+ num_output_channels == 2) {
+ // Case 4 and 5: 2 -> 2 -> 2 or 2 -> 1 -> 2.
+ //
+ // These can be handled in the same way because in the latter
+ // case, two connvolvers are still created with the second being a
+ // copy of the first.
+ const AudioChannel* source_channel_r = source_bus->Channel(1);
+ AudioChannel* destination_channel_r = destination_bus->Channel(1);
+ convolvers_[0]->Process(source_channel_l, destination_channel_l,
+ frames_to_process);
+ convolvers_[1]->Process(source_channel_r, destination_channel_r,
+ frames_to_process);
+ } else if (num_input_channels == 1 && num_output_channels == 2 &&
+ number_of_response_channels == 2) {
+ // Case 2: 1 -> 2 -> 2
+ for (int i = 0; i < 2; ++i) {
+ AudioChannel* destination_channel = destination_bus->Channel(i);
+ convolvers_[i]->Process(source_channel_l, destination_channel,
+ frames_to_process);
+ }
+ } else if (num_input_channels == 1 && number_of_response_channels == 1) {
+ // Case 1: 1 -> 1 -> 1
+ DCHECK_EQ(num_output_channels, 1ul);
+ convolvers_[0]->Process(source_channel_l, destination_channel_l,
+ frames_to_process);
+ } else if (num_input_channels == 2 && number_of_response_channels == 4 &&
+ num_output_channels == 2) {
+ // Case 6: 2 -> 4 -> 2 ("True" stereo)
+ const AudioChannel* source_channel_r = source_bus->Channel(1);
+ AudioChannel* destination_channel_r = destination_bus->Channel(1);
+
+ AudioChannel* temp_channel_l = temp_buffer_->Channel(0);
+ AudioChannel* temp_channel_r = temp_buffer_->Channel(1);
+
+ // Process left virtual source
+ convolvers_[0]->Process(source_channel_l, destination_channel_l,
+ frames_to_process);
+ convolvers_[1]->Process(source_channel_l, destination_channel_r,
+ frames_to_process);
+
+ // Process right virtual source
+ convolvers_[2]->Process(source_channel_r, temp_channel_l,
+ frames_to_process);
+ convolvers_[3]->Process(source_channel_r, temp_channel_r,
+ frames_to_process);
+
+ destination_bus->SumFrom(*temp_buffer_);
+ } else if (num_input_channels == 1 && number_of_response_channels == 4 &&
+ num_output_channels == 2) {
+ // Case 3: 1 -> 4 -> 2 (Processing mono with "True" stereo impulse
+ // response) This is an inefficient use of a four-channel impulse
+ // response, but we should handle the case.
+ AudioChannel* destination_channel_r = destination_bus->Channel(1);
+
+ AudioChannel* temp_channel_l = temp_buffer_->Channel(0);
+ AudioChannel* temp_channel_r = temp_buffer_->Channel(1);
+
+ // Process left virtual source
+ convolvers_[0]->Process(source_channel_l, destination_channel_l,
+ frames_to_process);
+ convolvers_[1]->Process(source_channel_l, destination_channel_r,
+ frames_to_process);
+
+ // Process right virtual source
+ convolvers_[2]->Process(source_channel_l, temp_channel_l,
+ frames_to_process);
+ convolvers_[3]->Process(source_channel_l, temp_channel_r,
+ frames_to_process);
+
+ destination_bus->SumFrom(*temp_buffer_);
+ } else {
+ NOTREACHED();
+ destination_bus->Zero();
+ }
+}
+
+void Reverb::Reset() {
+ for (size_t i = 0; i < convolvers_.size(); ++i)
+ convolvers_[i]->Reset();
+}
+
+size_t Reverb::LatencyFrames() const {
+ return !convolvers_.IsEmpty() ? convolvers_.front()->LatencyFrames() : 0;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/reverb.h b/chromium/third_party/blink/renderer/platform/audio/reverb.h
new file mode 100644
index 00000000000..3b5b6983175
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/reverb.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_REVERB_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_REVERB_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/audio/reverb_convolver.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class AudioBus;
+
+// Multi-channel convolution reverb with channel matrixing - one or more
+// ReverbConvolver objects are used internally.
+
+class PLATFORM_EXPORT Reverb {
+ USING_FAST_MALLOC(Reverb);
+ WTF_MAKE_NONCOPYABLE(Reverb);
+
+ public:
+ enum { kMaxFrameSize = 256 };
+
+ // renderSliceSize is a rendering hint, so the FFTs can be optimized to not
+ // all occur at the same time (very bad when rendering on a real-time thread).
+ Reverb(AudioBus* impulse_response_buffer,
+ size_t render_slice_size,
+ size_t max_fft_size,
+ bool use_background_threads,
+ bool normalize);
+
+ void Process(const AudioBus* source_bus,
+ AudioBus* destination_bus,
+ size_t frames_to_process);
+ void Reset();
+
+ size_t ImpulseResponseLength() const { return impulse_response_length_; }
+ size_t LatencyFrames() const;
+
+ private:
+ void Initialize(AudioBus* impulse_response_buffer,
+ size_t render_slice_size,
+ size_t max_fft_size,
+ bool use_background_threads);
+
+ size_t impulse_response_length_;
+ // The actual number of channels in the response. This can be less
+ // than the number of ReverbConvolver's in |m_convolvers|.
+ unsigned number_of_response_channels_;
+
+ Vector<std::unique_ptr<ReverbConvolver>> convolvers_;
+
+ // For "True" stereo processing
+ scoped_refptr<AudioBus> temp_buffer_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_REVERB_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/reverb_accumulation_buffer.cc b/chromium/third_party/blink/renderer/platform/audio/reverb_accumulation_buffer.cc
new file mode 100644
index 00000000000..b5b4c859604
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/reverb_accumulation_buffer.cc
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/audio/reverb_accumulation_buffer.h"
+#include "third_party/blink/renderer/platform/audio/vector_math.h"
+
+namespace blink {
+
+using namespace VectorMath;
+
+ReverbAccumulationBuffer::ReverbAccumulationBuffer(size_t length)
+ : buffer_(length), read_index_(0), read_time_frame_(0) {}
+
+void ReverbAccumulationBuffer::ReadAndClear(float* destination,
+ size_t number_of_frames) {
+ size_t buffer_length = buffer_.size();
+ bool is_copy_safe =
+ read_index_ <= buffer_length && number_of_frames <= buffer_length;
+
+ DCHECK(is_copy_safe);
+ if (!is_copy_safe)
+ return;
+
+ size_t frames_available = buffer_length - read_index_;
+ size_t number_of_frames1 = std::min(number_of_frames, frames_available);
+ size_t number_of_frames2 = number_of_frames - number_of_frames1;
+
+ float* source = buffer_.Data();
+ memcpy(destination, source + read_index_, sizeof(float) * number_of_frames1);
+ memset(source + read_index_, 0, sizeof(float) * number_of_frames1);
+
+ // Handle wrap-around if necessary
+ if (number_of_frames2 > 0) {
+ memcpy(destination + number_of_frames1, source,
+ sizeof(float) * number_of_frames2);
+ memset(source, 0, sizeof(float) * number_of_frames2);
+ }
+
+ read_index_ = (read_index_ + number_of_frames) % buffer_length;
+ read_time_frame_ += number_of_frames;
+}
+
+void ReverbAccumulationBuffer::UpdateReadIndex(int* read_index,
+ size_t number_of_frames) const {
+ // Update caller's readIndex
+ *read_index = (*read_index + number_of_frames) % buffer_.size();
+}
+
+int ReverbAccumulationBuffer::Accumulate(float* source,
+ size_t number_of_frames,
+ int* read_index,
+ size_t delay_frames) {
+ size_t buffer_length = buffer_.size();
+
+ size_t write_index = (*read_index + delay_frames) % buffer_length;
+
+ // Update caller's readIndex
+ *read_index = (*read_index + number_of_frames) % buffer_length;
+
+ size_t frames_available = buffer_length - write_index;
+ size_t number_of_frames1 = std::min(number_of_frames, frames_available);
+ size_t number_of_frames2 = number_of_frames - number_of_frames1;
+
+ float* destination = buffer_.Data();
+
+ bool is_safe = write_index <= buffer_length &&
+ number_of_frames1 + write_index <= buffer_length &&
+ number_of_frames2 <= buffer_length;
+ DCHECK(is_safe);
+ if (!is_safe)
+ return 0;
+
+ Vadd(source, 1, destination + write_index, 1, destination + write_index, 1,
+ number_of_frames1);
+
+ // Handle wrap-around if necessary
+ if (number_of_frames2 > 0)
+ Vadd(source + number_of_frames1, 1, destination, 1, destination, 1,
+ number_of_frames2);
+
+ return write_index;
+}
+
+void ReverbAccumulationBuffer::Reset() {
+ buffer_.Zero();
+ read_index_ = 0;
+ read_time_frame_ = 0;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/reverb_accumulation_buffer.h b/chromium/third_party/blink/renderer/platform/audio/reverb_accumulation_buffer.h
new file mode 100644
index 00000000000..9b2389134b5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/reverb_accumulation_buffer.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_REVERB_ACCUMULATION_BUFFER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_REVERB_ACCUMULATION_BUFFER_H_
+
+#include "third_party/blink/renderer/platform/audio/audio_array.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+// ReverbAccumulationBuffer is a circular delay buffer with one client reading
+// from it and multiple clients writing/accumulating to it at different delay
+// offsets from the read position. The read operation will zero the memory
+// just read from the buffer, so it will be ready for accumulation the next
+// time around.
+class PLATFORM_EXPORT ReverbAccumulationBuffer {
+ DISALLOW_NEW();
+ WTF_MAKE_NONCOPYABLE(ReverbAccumulationBuffer);
+
+ public:
+ ReverbAccumulationBuffer(size_t length);
+
+ // This will read from, then clear-out numberOfFrames
+ void ReadAndClear(float* destination, size_t number_of_frames);
+
+ // Each ReverbConvolverStage will accumulate its output at the appropriate
+ // delay from the read position. We need to pass in and update readIndex
+ // here, since each ReverbConvolverStage may be running in a different thread
+ // than the realtime thread calling ReadAndClear() and maintaining
+ // m_readIndex
+ // Returns the writeIndex where the accumulation took place
+ int Accumulate(float* source,
+ size_t number_of_frames,
+ int* read_index,
+ size_t delay_frames);
+
+ size_t ReadIndex() const { return read_index_; }
+ void UpdateReadIndex(int* read_index, size_t number_of_frames) const;
+
+ size_t ReadTimeFrame() const { return read_time_frame_; }
+
+ void Reset();
+
+ private:
+ AudioFloatArray buffer_;
+ size_t read_index_;
+ size_t read_time_frame_; // for debugging (frame on continuous timeline)
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_REVERB_ACCUMULATION_BUFFER_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/reverb_convolver.cc b/chromium/third_party/blink/renderer/platform/audio/reverb_convolver.cc
new file mode 100644
index 00000000000..9cb601ebc22
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/reverb_convolver.cc
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/audio/reverb_convolver.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/location.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_thread.h"
+#include "third_party/blink/renderer/platform/audio/audio_bus.h"
+#include "third_party/blink/renderer/platform/audio/vector_math.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/web_task_runner.h"
+
+namespace blink {
+
+using namespace VectorMath;
+
+const int kInputBufferSize = 8 * 16384;
+
+// We only process the leading portion of the impulse response in the real-time
+// thread. We don't exceed this length. It turns out then, that the
+// background thread has about 278msec of scheduling slop. Empirically, this
+// has been found to be a good compromise between giving enough time for
+// scheduling slop, while still minimizing the amount of processing done in the
+// primary (high-priority) thread. This was found to be a good value on Mac OS
+// X, and may work well on other platforms as well, assuming the very rough
+// scheduling latencies are similar on these time-scales. Of course, this code
+// may need to be tuned for individual platforms if this assumption is found to
+// be incorrect.
+const size_t kRealtimeFrameLimit = 8192 + 4096; // ~278msec @ 44.1KHz
+
+const size_t kMinFFTSize = 128;
+const size_t kMaxRealtimeFFTSize = 2048;
+
+ReverbConvolver::ReverbConvolver(AudioChannel* impulse_response,
+ size_t render_slice_size,
+ size_t max_fft_size,
+ size_t convolver_render_phase,
+ bool use_background_threads)
+ : impulse_response_length_(impulse_response->length()),
+ accumulation_buffer_(impulse_response->length() + render_slice_size),
+ input_buffer_(kInputBufferSize),
+ min_fft_size_(
+ kMinFFTSize), // First stage will have this size - successive
+ // stages will double in size each time
+ max_fft_size_(max_fft_size) // until we hit m_maxFFTSize
+{
+ // If we are using background threads then don't exceed this FFT size for the
+ // stages which run in the real-time thread. This avoids having only one or
+ // two large stages (size 16384 or so) at the end which take a lot of time
+ // every several processing slices. This way we amortize the cost over more
+ // processing slices.
+ max_realtime_fft_size_ = kMaxRealtimeFFTSize;
+
+ const float* response = impulse_response->Data();
+ size_t total_response_length = impulse_response->length();
+
+ // The total latency is zero because the direct-convolution is used in the
+ // leading portion.
+ size_t reverb_total_latency = 0;
+
+ size_t stage_offset = 0;
+ int i = 0;
+ size_t fft_size = min_fft_size_;
+ while (stage_offset < total_response_length) {
+ size_t stage_size = fft_size / 2;
+
+ // For the last stage, it's possible that stageOffset is such that we're
+ // straddling the end of the impulse response buffer (if we use stageSize),
+ // so reduce the last stage's length...
+ if (stage_size + stage_offset > total_response_length)
+ stage_size = total_response_length - stage_offset;
+
+ // This "staggers" the time when each FFT happens so they don't all happen
+ // at the same time
+ int render_phase = convolver_render_phase + i * render_slice_size;
+
+ bool use_direct_convolver = !stage_offset;
+
+ std::unique_ptr<ReverbConvolverStage> stage =
+ std::make_unique<ReverbConvolverStage>(
+ response, total_response_length, reverb_total_latency, stage_offset,
+ stage_size, fft_size, render_phase, render_slice_size,
+ &accumulation_buffer_, use_direct_convolver);
+
+ bool is_background_stage = false;
+
+ if (use_background_threads && stage_offset > kRealtimeFrameLimit) {
+ background_stages_.push_back(std::move(stage));
+ is_background_stage = true;
+ } else {
+ stages_.push_back(std::move(stage));
+ }
+
+ stage_offset += stage_size;
+ ++i;
+
+ if (!use_direct_convolver) {
+ // Figure out next FFT size
+ fft_size *= 2;
+ }
+
+ if (use_background_threads && !is_background_stage &&
+ fft_size > max_realtime_fft_size_)
+ fft_size = max_realtime_fft_size_;
+ if (fft_size > max_fft_size_)
+ fft_size = max_fft_size_;
+ }
+
+ // Start up background thread
+ // FIXME: would be better to up the thread priority here. It doesn't need to
+ // be real-time, but higher than the default...
+ if (use_background_threads && background_stages_.size() > 0) {
+ background_thread_ =
+ Platform::Current()->CreateThread(WebThreadCreationParams(
+ WebThreadType::kReverbConvolutionBackgroundThread));
+ }
+}
+
+ReverbConvolver::~ReverbConvolver() {
+ // Wait for background thread to stop
+ background_thread_.reset();
+}
+
+void ReverbConvolver::ProcessInBackground() {
+ // Process all of the stages until their read indices reach the input buffer's
+ // write index
+ int write_index = input_buffer_.WriteIndex();
+
+ // Even though it doesn't seem like every stage needs to maintain its own
+ // version of readIndex we do this in case we want to run in more than one
+ // background thread.
+ int read_index;
+
+ while ((read_index = background_stages_[0]->InputReadIndex()) !=
+ write_index) { // FIXME: do better to detect buffer overrun...
+ // The ReverbConvolverStages need to process in amounts which evenly divide
+ // half the FFT size
+ const int kSliceSize = kMinFFTSize / 2;
+
+ // Accumulate contributions from each stage
+ for (size_t i = 0; i < background_stages_.size(); ++i)
+ background_stages_[i]->ProcessInBackground(this, kSliceSize);
+ }
+}
+
+void ReverbConvolver::Process(const AudioChannel* source_channel,
+ AudioChannel* destination_channel,
+ size_t frames_to_process) {
+ bool is_safe = source_channel && destination_channel &&
+ source_channel->length() >= frames_to_process &&
+ destination_channel->length() >= frames_to_process;
+ DCHECK(is_safe);
+ if (!is_safe)
+ return;
+
+ const float* source = source_channel->Data();
+ float* destination = destination_channel->MutableData();
+ bool is_data_safe = source && destination;
+ DCHECK(is_data_safe);
+ if (!is_data_safe)
+ return;
+
+ // Feed input buffer (read by all threads)
+ input_buffer_.Write(source, frames_to_process);
+
+ // Accumulate contributions from each stage
+ for (size_t i = 0; i < stages_.size(); ++i)
+ stages_[i]->Process(source, frames_to_process);
+
+ // Finally read from accumulation buffer
+ accumulation_buffer_.ReadAndClear(destination, frames_to_process);
+
+ // Now that we've buffered more input, post another task to the background
+ // thread.
+ if (background_thread_) {
+ PostCrossThreadTask(*background_thread_->GetTaskRunner(), FROM_HERE,
+ CrossThreadBind(&ReverbConvolver::ProcessInBackground,
+ CrossThreadUnretained(this)));
+ }
+}
+
+void ReverbConvolver::Reset() {
+ for (size_t i = 0; i < stages_.size(); ++i)
+ stages_[i]->Reset();
+
+ for (size_t i = 0; i < background_stages_.size(); ++i)
+ background_stages_[i]->Reset();
+
+ accumulation_buffer_.Reset();
+ input_buffer_.Reset();
+}
+
+size_t ReverbConvolver::LatencyFrames() const {
+ return 0;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/reverb_convolver.h b/chromium/third_party/blink/renderer/platform/audio/reverb_convolver.h
new file mode 100644
index 00000000000..4d3490b34a2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/reverb_convolver.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_REVERB_CONVOLVER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_REVERB_CONVOLVER_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/audio/audio_array.h"
+#include "third_party/blink/renderer/platform/audio/direct_convolver.h"
+#include "third_party/blink/renderer/platform/audio/fft_convolver.h"
+#include "third_party/blink/renderer/platform/audio/reverb_accumulation_buffer.h"
+#include "third_party/blink/renderer/platform/audio/reverb_convolver_stage.h"
+#include "third_party/blink/renderer/platform/audio/reverb_input_buffer.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class AudioChannel;
+class WebThread;
+
+class PLATFORM_EXPORT ReverbConvolver {
+ USING_FAST_MALLOC(ReverbConvolver);
+ WTF_MAKE_NONCOPYABLE(ReverbConvolver);
+
+ public:
+ // maxFFTSize can be adjusted (from say 2048 to 32768) depending on how much
+ // precision is necessary. For certain tweaky de-convolving applications the
+ // phase errors add up quickly and lead to non-sensical results with larger
+ // FFT sizes and single-precision floats. In these cases 2048 is a good
+ // size. If not doing multi-threaded convolution, then should not go > 8192.
+ ReverbConvolver(AudioChannel* impulse_response,
+ size_t render_slice_size,
+ size_t max_fft_size,
+ size_t convolver_render_phase,
+ bool use_background_threads);
+ ~ReverbConvolver();
+
+ void Process(const AudioChannel* source_channel,
+ AudioChannel* destination_channel,
+ size_t frames_to_process);
+ void Reset();
+
+ ReverbInputBuffer* InputBuffer() { return &input_buffer_; }
+
+ size_t LatencyFrames() const;
+
+ private:
+ void ProcessInBackground();
+
+ Vector<std::unique_ptr<ReverbConvolverStage>> stages_;
+ Vector<std::unique_ptr<ReverbConvolverStage>> background_stages_;
+ size_t impulse_response_length_;
+
+ ReverbAccumulationBuffer accumulation_buffer_;
+
+ // One or more background threads read from this input buffer which is fed
+ // from the realtime thread.
+ ReverbInputBuffer input_buffer_;
+
+ // First stage will be of size m_minFFTSize. Each next stage will be twice as
+ // big until we hit m_maxFFTSize.
+ size_t min_fft_size_;
+ size_t max_fft_size_;
+
+ // But don't exceed this size in the real-time thread (if we're doing
+ // background processing).
+ size_t max_realtime_fft_size_;
+
+ // Background thread and synchronization
+ std::unique_ptr<WebThread> background_thread_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_REVERB_CONVOLVER_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/reverb_convolver_stage.cc b/chromium/third_party/blink/renderer/platform/audio/reverb_convolver_stage.cc
new file mode 100644
index 00000000000..b9dc0f3bded
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/reverb_convolver_stage.cc
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/audio/reverb_convolver_stage.h"
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+#include "third_party/blink/renderer/platform/audio/reverb_accumulation_buffer.h"
+#include "third_party/blink/renderer/platform/audio/reverb_convolver.h"
+#include "third_party/blink/renderer/platform/audio/reverb_input_buffer.h"
+#include "third_party/blink/renderer/platform/audio/vector_math.h"
+
+namespace blink {
+
+ReverbConvolverStage::ReverbConvolverStage(
+ const float* impulse_response,
+ size_t,
+ size_t reverb_total_latency,
+ size_t stage_offset,
+ size_t stage_length,
+ size_t fft_size,
+ size_t render_phase,
+ size_t render_slice_size,
+ ReverbAccumulationBuffer* accumulation_buffer,
+ bool direct_mode)
+ : accumulation_buffer_(accumulation_buffer),
+ accumulation_read_index_(0),
+ input_read_index_(0),
+ direct_mode_(direct_mode) {
+ DCHECK(impulse_response);
+ DCHECK(accumulation_buffer);
+
+ if (!direct_mode_) {
+ fft_kernel_ = std::make_unique<FFTFrame>(fft_size);
+ fft_kernel_->DoPaddedFFT(impulse_response + stage_offset, stage_length);
+ fft_convolver_ = std::make_unique<FFTConvolver>(fft_size);
+ } else {
+ DCHECK(!stage_offset);
+ DCHECK_LE(stage_length, fft_size / 2);
+
+ auto direct_kernel = std::make_unique<AudioFloatArray>(fft_size / 2);
+ direct_kernel->CopyToRange(impulse_response, 0, stage_length);
+ direct_convolver_ = std::make_unique<DirectConvolver>(
+ render_slice_size, std::move(direct_kernel));
+ }
+ temporary_buffer_.Allocate(render_slice_size);
+
+ // The convolution stage at offset stageOffset needs to have a corresponding
+ // delay to cancel out the offset.
+ size_t total_delay = stage_offset + reverb_total_latency;
+
+ // But, the FFT convolution itself incurs fftSize / 2 latency, so subtract
+ // this out...
+ size_t half_size = fft_size / 2;
+ if (!direct_mode_) {
+ DCHECK_GE(total_delay, half_size);
+ if (total_delay >= half_size)
+ total_delay -= half_size;
+ }
+
+ // We divide up the total delay, into pre and post delay sections so that we
+ // can schedule at exactly the moment when the FFT will happen. This is
+ // coordinated with the other stages, so they don't all do their FFTs at the
+ // same time...
+ int max_pre_delay_length = std::min(half_size, total_delay);
+ pre_delay_length_ = total_delay > 0 ? render_phase % max_pre_delay_length : 0;
+ if (pre_delay_length_ > total_delay)
+ pre_delay_length_ = 0;
+
+ post_delay_length_ = total_delay - pre_delay_length_;
+ pre_read_write_index_ = 0;
+ frames_processed_ = 0; // total frames processed so far
+
+ size_t delay_buffer_size =
+ pre_delay_length_ < fft_size ? fft_size : pre_delay_length_;
+ delay_buffer_size = delay_buffer_size < render_slice_size ? render_slice_size
+ : delay_buffer_size;
+ pre_delay_buffer_.Allocate(delay_buffer_size);
+}
+
+void ReverbConvolverStage::ProcessInBackground(ReverbConvolver* convolver,
+ size_t frames_to_process) {
+ ReverbInputBuffer* input_buffer = convolver->InputBuffer();
+ float* source =
+ input_buffer->DirectReadFrom(&input_read_index_, frames_to_process);
+ Process(source, frames_to_process);
+}
+
+void ReverbConvolverStage::Process(const float* source,
+ size_t frames_to_process) {
+ DCHECK(source);
+ if (!source)
+ return;
+
+ // Deal with pre-delay stream : note special handling of zero delay.
+
+ const float* pre_delayed_source;
+ float* pre_delayed_destination;
+ float* temporary_buffer;
+ bool is_temporary_buffer_safe = false;
+ if (pre_delay_length_ > 0) {
+ // Handles both the read case (call to process() ) and the write case
+ // (memcpy() )
+ bool is_pre_delay_safe =
+ pre_read_write_index_ + frames_to_process <= pre_delay_buffer_.size();
+ DCHECK(is_pre_delay_safe);
+ if (!is_pre_delay_safe)
+ return;
+
+ is_temporary_buffer_safe = frames_to_process <= temporary_buffer_.size();
+
+ pre_delayed_destination = pre_delay_buffer_.Data() + pre_read_write_index_;
+ pre_delayed_source = pre_delayed_destination;
+ temporary_buffer = temporary_buffer_.Data();
+ } else {
+ // Zero delay
+ pre_delayed_destination = nullptr;
+ pre_delayed_source = source;
+ temporary_buffer = pre_delay_buffer_.Data();
+
+ is_temporary_buffer_safe = frames_to_process <= pre_delay_buffer_.size();
+ }
+
+ DCHECK(is_temporary_buffer_safe);
+ if (!is_temporary_buffer_safe)
+ return;
+
+ if (frames_processed_ < pre_delay_length_) {
+ // For the first m_preDelayLength frames don't process the convolver,
+ // instead simply buffer in the pre-delay. But while buffering the
+ // pre-delay, we still need to update our index.
+ accumulation_buffer_->UpdateReadIndex(&accumulation_read_index_,
+ frames_to_process);
+ } else {
+ // Now, run the convolution (into the delay buffer).
+ // An expensive FFT will happen every fftSize / 2 frames.
+ // We process in-place here...
+ if (!direct_mode_)
+ fft_convolver_->Process(fft_kernel_.get(), pre_delayed_source,
+ temporary_buffer, frames_to_process);
+ else
+ direct_convolver_->Process(pre_delayed_source, temporary_buffer,
+ frames_to_process);
+
+ // Now accumulate into reverb's accumulation buffer.
+ accumulation_buffer_->Accumulate(temporary_buffer, frames_to_process,
+ &accumulation_read_index_,
+ post_delay_length_);
+ }
+
+ // Finally copy input to pre-delay.
+ if (pre_delay_length_ > 0) {
+ memcpy(pre_delayed_destination, source, sizeof(float) * frames_to_process);
+ pre_read_write_index_ += frames_to_process;
+
+ DCHECK_LE(pre_read_write_index_, pre_delay_length_);
+ if (pre_read_write_index_ >= pre_delay_length_)
+ pre_read_write_index_ = 0;
+ }
+
+ frames_processed_ += frames_to_process;
+}
+
+void ReverbConvolverStage::Reset() {
+ if (!direct_mode_)
+ fft_convolver_->Reset();
+ else
+ direct_convolver_->Reset();
+ pre_delay_buffer_.Zero();
+ accumulation_read_index_ = 0;
+ input_read_index_ = 0;
+ frames_processed_ = 0;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/reverb_convolver_stage.h b/chromium/third_party/blink/renderer/platform/audio/reverb_convolver_stage.h
new file mode 100644
index 00000000000..d40df064021
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/reverb_convolver_stage.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_REVERB_CONVOLVER_STAGE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_REVERB_CONVOLVER_STAGE_H_
+
+#include <memory>
+
+#include "third_party/blink/renderer/platform/audio/audio_array.h"
+#include "third_party/blink/renderer/platform/audio/fft_frame.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class ReverbAccumulationBuffer;
+class ReverbConvolver;
+class FFTConvolver;
+class DirectConvolver;
+
+// A ReverbConvolverStage represents the convolution associated with a
+// sub-section of a large impulse response. It incorporates a delay line to
+// account for the offset of the sub-section within the larger impulse
+// response.
+class PLATFORM_EXPORT ReverbConvolverStage {
+ USING_FAST_MALLOC(ReverbConvolverStage);
+ WTF_MAKE_NONCOPYABLE(ReverbConvolverStage);
+
+ public:
+ // renderPhase is useful to know so that we can manipulate the pre versus post
+ // delay so that stages will perform their heavy work (FFT processing) on
+ // different slices to balance the load in a real-time thread.
+ ReverbConvolverStage(const float* impulse_response,
+ size_t response_length,
+ size_t reverb_total_latency,
+ size_t stage_offset,
+ size_t stage_length,
+ size_t fft_size,
+ size_t render_phase,
+ size_t render_slice_size,
+ ReverbAccumulationBuffer*,
+ bool direct_mode = false);
+
+ // WARNING: framesToProcess must be such that it evenly divides the delay
+ // buffer size (stage_offset).
+ void Process(const float* source, size_t frames_to_process);
+
+ void ProcessInBackground(ReverbConvolver* convolver,
+ size_t frames_to_process);
+
+ void Reset();
+
+ // Useful for background processing
+ int InputReadIndex() const { return input_read_index_; }
+
+ private:
+ std::unique_ptr<FFTFrame> fft_kernel_;
+ std::unique_ptr<FFTConvolver> fft_convolver_;
+
+ AudioFloatArray pre_delay_buffer_;
+
+ ReverbAccumulationBuffer* accumulation_buffer_;
+ int accumulation_read_index_;
+ int input_read_index_;
+
+ size_t pre_delay_length_;
+ size_t post_delay_length_;
+ size_t pre_read_write_index_;
+ size_t frames_processed_;
+
+ AudioFloatArray temporary_buffer_;
+
+ bool direct_mode_;
+ std::unique_ptr<DirectConvolver> direct_convolver_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_REVERB_CONVOLVER_STAGE_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/reverb_input_buffer.cc b/chromium/third_party/blink/renderer/platform/audio/reverb_input_buffer.cc
new file mode 100644
index 00000000000..e8b6de19a27
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/reverb_input_buffer.cc
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/audio/reverb_input_buffer.h"
+
+namespace blink {
+
+ReverbInputBuffer::ReverbInputBuffer(size_t length)
+ : buffer_(length), write_index_(0) {}
+
+void ReverbInputBuffer::Write(const float* source_p, size_t number_of_frames) {
+ size_t buffer_length = buffer_.size();
+ size_t index = WriteIndex();
+ size_t new_index = index + number_of_frames;
+
+ CHECK_LE(new_index, buffer_length);
+
+ memcpy(buffer_.Data() + index, source_p, sizeof(float) * number_of_frames);
+
+ if (new_index >= buffer_length)
+ new_index = 0;
+
+ SetWriteIndex(new_index);
+}
+
+float* ReverbInputBuffer::DirectReadFrom(int* read_index,
+ size_t number_of_frames) {
+ size_t buffer_length = buffer_.size();
+ bool is_pointer_good = read_index && *read_index >= 0 &&
+ *read_index + number_of_frames <= buffer_length;
+ DCHECK(is_pointer_good);
+ if (!is_pointer_good) {
+ // Should never happen in practice but return pointer to start of buffer
+ // (avoid crash)
+ if (read_index)
+ *read_index = 0;
+ return buffer_.Data();
+ }
+
+ float* source_p = buffer_.Data();
+ float* p = source_p + *read_index;
+
+ // Update readIndex
+ *read_index = (*read_index + number_of_frames) % buffer_length;
+
+ return p;
+}
+
+void ReverbInputBuffer::Reset() {
+ buffer_.Zero();
+ write_index_ = 0;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/reverb_input_buffer.h b/chromium/third_party/blink/renderer/platform/audio/reverb_input_buffer.h
new file mode 100644
index 00000000000..3d86ea226d6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/reverb_input_buffer.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_REVERB_INPUT_BUFFER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_REVERB_INPUT_BUFFER_H_
+
+#include "third_party/blink/renderer/platform/audio/audio_array.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/atomics.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+// ReverbInputBuffer is used to buffer input samples for deferred processing by
+// the background threads.
+class PLATFORM_EXPORT ReverbInputBuffer {
+ DISALLOW_NEW();
+ WTF_MAKE_NONCOPYABLE(ReverbInputBuffer);
+
+ public:
+ ReverbInputBuffer(size_t length);
+
+ // The realtime audio thread keeps writing samples here.
+ // The assumption is that the buffer's length is evenly divisible by
+ // numberOfFrames (for nearly all cases this will be fine).
+ // FIXME: remove numberOfFrames restriction...
+ void Write(const float* source_p, size_t number_of_frames);
+
+ // Background threads can call this to check if there's anything to read...
+ size_t WriteIndex() const { return AcquireLoad(&write_index_); }
+ void SetWriteIndex(size_t new_index) {
+ ReleaseStore(&write_index_, new_index);
+ }
+
+ // The individual background threads read here (and hope that they can keep up
+ // with the buffer writing).
+ // readIndex is updated with the next readIndex to read from...
+ // The assumption is that the buffer's length is evenly divisible by
+ // numberOfFrames.
+ // FIXME: remove numberOfFrames restriction...
+ float* DirectReadFrom(int* read_index, size_t number_of_frames);
+
+ void Reset();
+
+ private:
+ AudioFloatArray buffer_;
+
+ // |write_index_| can be accessed from several threads. Only use
+ // the getter and setter to access it atomically. Don't access
+ // directly!
+ size_t write_index_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_REVERB_INPUT_BUFFER_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/sinc_resampler.cc b/chromium/third_party/blink/renderer/platform/audio/sinc_resampler.cc
new file mode 100644
index 00000000000..79a1681ef2e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/sinc_resampler.cc
@@ -0,0 +1,498 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/audio/sinc_resampler.h"
+
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/audio/audio_bus.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+#if defined(ARCH_CPU_X86_FAMILY)
+#include <emmintrin.h>
+#endif
+
+// Input buffer layout, dividing the total buffer into regions (r0 - r5):
+//
+// |----------------|-----------------------------------------|----------------|
+//
+// blockSize + kernelSize / 2
+// <--------------------------------------------------------->
+// r0
+//
+// kernelSize / 2 kernelSize / 2 kernelSize / 2 kernelSize / 2
+// <---------------> <---------------> <---------------> <--------------->
+// r1 r2 r3 r4
+//
+// blockSize
+// <---------------------------------------->
+// r5
+
+// The Algorithm:
+//
+// 1) Consume input frames into r0 (r1 is zero-initialized).
+// 2) Position kernel centered at start of r0 (r2) and generate output frames
+// until kernel is centered at start of r4, or we've finished generating
+// all the output frames.
+// 3) Copy r3 to r1 and r4 to r2.
+// 4) Consume input frames into r5 (zero-pad if we run out of input).
+// 5) Goto (2) until all of input is consumed.
+//
+// note: we're glossing over how the sub-sample handling works with
+// m_virtualSourceIndex, etc.
+
+namespace blink {
+
+SincResampler::SincResampler(double scale_factor,
+ unsigned kernel_size,
+ unsigned number_of_kernel_offsets)
+ : scale_factor_(scale_factor),
+ kernel_size_(kernel_size),
+ number_of_kernel_offsets_(number_of_kernel_offsets),
+ kernel_storage_(kernel_size_ * (number_of_kernel_offsets_ + 1)),
+ virtual_source_index_(0),
+ block_size_(512),
+ // See input buffer layout above.
+ input_buffer_(block_size_ + kernel_size_),
+ source_(nullptr),
+ source_frames_available_(0),
+ source_provider_(nullptr),
+ is_buffer_primed_(false) {
+ InitializeKernel();
+}
+
+void SincResampler::InitializeKernel() {
+ // Blackman window parameters.
+ double alpha = 0.16;
+ double a0 = 0.5 * (1.0 - alpha);
+ double a1 = 0.5;
+ double a2 = 0.5 * alpha;
+
+ // sincScaleFactor is basically the normalized cutoff frequency of the
+ // low-pass filter.
+ double sinc_scale_factor = scale_factor_ > 1.0 ? 1.0 / scale_factor_ : 1.0;
+
+ // The sinc function is an idealized brick-wall filter, but since we're
+ // windowing it the transition from pass to stop does not happen right away.
+ // So we should adjust the lowpass filter cutoff slightly downward to avoid
+ // some aliasing at the very high-end.
+ // FIXME: this value is empirical and to be more exact should vary depending
+ // on m_kernelSize.
+ sinc_scale_factor *= 0.9;
+
+ int n = kernel_size_;
+ int half_size = n / 2;
+
+ // Generates a set of windowed sinc() kernels.
+ // We generate a range of sub-sample offsets from 0.0 to 1.0.
+ for (unsigned offset_index = 0; offset_index <= number_of_kernel_offsets_;
+ ++offset_index) {
+ double subsample_offset =
+ static_cast<double>(offset_index) / number_of_kernel_offsets_;
+
+ for (int i = 0; i < n; ++i) {
+ // Compute the sinc() with offset.
+ double s =
+ sinc_scale_factor * piDouble * (i - half_size - subsample_offset);
+ double sinc = !s ? 1.0 : std::sin(s) / s;
+ sinc *= sinc_scale_factor;
+
+ // Compute Blackman window, matching the offset of the sinc().
+ double x = (i - subsample_offset) / n;
+ double window = a0 - a1 * std::cos(twoPiDouble * x) +
+ a2 * std::cos(twoPiDouble * 2.0 * x);
+
+ // Window the sinc() function and store at the correct offset.
+ kernel_storage_[i + offset_index * kernel_size_] = sinc * window;
+ }
+ }
+}
+
+void SincResampler::ConsumeSource(float* buffer,
+ unsigned number_of_source_frames) {
+ DCHECK(source_provider_);
+ if (!source_provider_)
+ return;
+
+ // Wrap the provided buffer by an AudioBus for use by the source provider.
+ scoped_refptr<AudioBus> bus =
+ AudioBus::Create(1, number_of_source_frames, false);
+
+ // FIXME: Find a way to make the following const-correct:
+ bus->SetChannelMemory(0, buffer, number_of_source_frames);
+
+ source_provider_->ProvideInput(bus.get(), number_of_source_frames);
+}
+
+namespace {
+
+// BufferSourceProvider is an AudioSourceProvider wrapping an in-memory buffer.
+
+class BufferSourceProvider final : public AudioSourceProvider {
+ public:
+ BufferSourceProvider(const float* source, size_t number_of_source_frames)
+ : source_(source), source_frames_available_(number_of_source_frames) {}
+
+ // Consumes samples from the in-memory buffer.
+ void ProvideInput(AudioBus* bus, size_t frames_to_process) override {
+ DCHECK(source_);
+ DCHECK(bus);
+ if (!source_ || !bus)
+ return;
+
+ float* buffer = bus->Channel(0)->MutableData();
+
+ // Clamp to number of frames available and zero-pad.
+ size_t frames_to_copy =
+ std::min(source_frames_available_, frames_to_process);
+ memcpy(buffer, source_, sizeof(float) * frames_to_copy);
+
+ // Zero-pad if necessary.
+ if (frames_to_copy < frames_to_process)
+ memset(buffer + frames_to_copy, 0,
+ sizeof(float) * (frames_to_process - frames_to_copy));
+
+ source_frames_available_ -= frames_to_copy;
+ source_ += frames_to_copy;
+ }
+
+ private:
+ const float* source_;
+ size_t source_frames_available_;
+};
+
+} // namespace
+
+void SincResampler::Process(const float* source,
+ float* destination,
+ unsigned number_of_source_frames) {
+ // Resample an in-memory buffer using an AudioSourceProvider.
+ BufferSourceProvider source_provider(source, number_of_source_frames);
+
+ unsigned number_of_destination_frames =
+ static_cast<unsigned>(number_of_source_frames / scale_factor_);
+ unsigned remaining = number_of_destination_frames;
+
+ while (remaining) {
+ unsigned frames_this_time = std::min(remaining, block_size_);
+ Process(&source_provider, destination, frames_this_time);
+
+ destination += frames_this_time;
+ remaining -= frames_this_time;
+ }
+}
+
+void SincResampler::Process(AudioSourceProvider* source_provider,
+ float* destination,
+ size_t frames_to_process) {
+ bool is_good = source_provider && block_size_ > kernel_size_ &&
+ input_buffer_.size() >= block_size_ + kernel_size_ &&
+ !(kernel_size_ % 2);
+ DCHECK(is_good);
+ if (!is_good)
+ return;
+
+ source_provider_ = source_provider;
+
+ unsigned number_of_destination_frames = frames_to_process;
+
+ // Setup various region pointers in the buffer (see diagram above).
+ float* r0 = input_buffer_.Data() + kernel_size_ / 2;
+ float* r1 = input_buffer_.Data();
+ float* r2 = r0;
+ float* r3 = r0 + block_size_ - kernel_size_ / 2;
+ float* r4 = r0 + block_size_;
+ float* r5 = r0 + kernel_size_ / 2;
+
+ // Step (1)
+ // Prime the input buffer at the start of the input stream.
+ if (!is_buffer_primed_) {
+ ConsumeSource(r0, block_size_ + kernel_size_ / 2);
+ is_buffer_primed_ = true;
+ }
+
+ // Step (2)
+
+ while (number_of_destination_frames) {
+ while (virtual_source_index_ < block_size_) {
+ // m_virtualSourceIndex lies in between two kernel offsets so figure out
+ // what they are.
+ int source_index_i = static_cast<int>(virtual_source_index_);
+ double subsample_remainder = virtual_source_index_ - source_index_i;
+
+ double virtual_offset_index =
+ subsample_remainder * number_of_kernel_offsets_;
+ int offset_index = static_cast<int>(virtual_offset_index);
+
+ float* k1 = kernel_storage_.Data() + offset_index * kernel_size_;
+ float* k2 = k1 + kernel_size_;
+
+ // Initialize input pointer based on quantized m_virtualSourceIndex.
+ float* input_p = r1 + source_index_i;
+
+ // We'll compute "convolutions" for the two kernels which straddle
+ // m_virtualSourceIndex
+ float sum1 = 0;
+ float sum2 = 0;
+
+ // Figure out how much to weight each kernel's "convolution".
+ double kernel_interpolation_factor = virtual_offset_index - offset_index;
+
+ // Generate a single output sample.
+ int n = kernel_size_;
+
+#define CONVOLVE_ONE_SAMPLE() \
+ do { \
+ input = *input_p++; \
+ sum1 += input * *k1; \
+ sum2 += input * *k2; \
+ ++k1; \
+ ++k2; \
+ } while (0)
+
+ {
+ float input;
+
+#if defined(ARCH_CPU_X86_FAMILY)
+ // If the sourceP address is not 16-byte aligned, the first several
+ // frames (at most three) should be processed seperately.
+ while ((reinterpret_cast<uintptr_t>(input_p) & 0x0F) && n) {
+ CONVOLVE_ONE_SAMPLE();
+ n--;
+ }
+
+ // Now the inputP is aligned and start to apply SSE.
+ float* end_p = input_p + n - n % 4;
+ __m128 m_input;
+ __m128 m_k1;
+ __m128 m_k2;
+ __m128 mul1;
+ __m128 mul2;
+
+ __m128 sums1 = _mm_setzero_ps();
+ __m128 sums2 = _mm_setzero_ps();
+ bool k1_aligned = !(reinterpret_cast<uintptr_t>(k1) & 0x0F);
+ bool k2_aligned = !(reinterpret_cast<uintptr_t>(k2) & 0x0F);
+
+#define LOAD_DATA(l1, l2) \
+ do { \
+ m_input = _mm_load_ps(input_p); \
+ m_k1 = _mm_##l1##_ps(k1); \
+ m_k2 = _mm_##l2##_ps(k2); \
+ } while (0)
+
+#define CONVOLVE_4_SAMPLES() \
+ do { \
+ mul1 = _mm_mul_ps(m_input, m_k1); \
+ mul2 = _mm_mul_ps(m_input, m_k2); \
+ sums1 = _mm_add_ps(sums1, mul1); \
+ sums2 = _mm_add_ps(sums2, mul2); \
+ input_p += 4; \
+ k1 += 4; \
+ k2 += 4; \
+ } while (0)
+
+ if (k1_aligned && k2_aligned) { // both aligned
+ while (input_p < end_p) {
+ LOAD_DATA(load, load);
+ CONVOLVE_4_SAMPLES();
+ }
+ } else if (!k1_aligned && k2_aligned) { // only k2 aligned
+ while (input_p < end_p) {
+ LOAD_DATA(loadu, load);
+ CONVOLVE_4_SAMPLES();
+ }
+ } else if (k1_aligned && !k2_aligned) { // only k1 aligned
+ while (input_p < end_p) {
+ LOAD_DATA(load, loadu);
+ CONVOLVE_4_SAMPLES();
+ }
+ } else { // both non-aligned
+ while (input_p < end_p) {
+ LOAD_DATA(loadu, loadu);
+ CONVOLVE_4_SAMPLES();
+ }
+ }
+
+ // Summarize the SSE results to sum1 and sum2.
+ float* group_sum_p = reinterpret_cast<float*>(&sums1);
+ sum1 +=
+ group_sum_p[0] + group_sum_p[1] + group_sum_p[2] + group_sum_p[3];
+ group_sum_p = reinterpret_cast<float*>(&sums2);
+ sum2 +=
+ group_sum_p[0] + group_sum_p[1] + group_sum_p[2] + group_sum_p[3];
+
+ n %= 4;
+ while (n) {
+ CONVOLVE_ONE_SAMPLE();
+ n--;
+ }
+#else
+ // FIXME: add ARM NEON optimizations for the following. The scalar
+ // code-path can probably also be optimized better.
+
+ // Optimize size 32 and size 64 kernels by unrolling the while loop.
+ // A 20 - 30% speed improvement was measured in some cases by using this
+ // approach.
+
+ if (n == 32) {
+ CONVOLVE_ONE_SAMPLE(); // 1
+ CONVOLVE_ONE_SAMPLE(); // 2
+ CONVOLVE_ONE_SAMPLE(); // 3
+ CONVOLVE_ONE_SAMPLE(); // 4
+ CONVOLVE_ONE_SAMPLE(); // 5
+ CONVOLVE_ONE_SAMPLE(); // 6
+ CONVOLVE_ONE_SAMPLE(); // 7
+ CONVOLVE_ONE_SAMPLE(); // 8
+ CONVOLVE_ONE_SAMPLE(); // 9
+ CONVOLVE_ONE_SAMPLE(); // 10
+ CONVOLVE_ONE_SAMPLE(); // 11
+ CONVOLVE_ONE_SAMPLE(); // 12
+ CONVOLVE_ONE_SAMPLE(); // 13
+ CONVOLVE_ONE_SAMPLE(); // 14
+ CONVOLVE_ONE_SAMPLE(); // 15
+ CONVOLVE_ONE_SAMPLE(); // 16
+ CONVOLVE_ONE_SAMPLE(); // 17
+ CONVOLVE_ONE_SAMPLE(); // 18
+ CONVOLVE_ONE_SAMPLE(); // 19
+ CONVOLVE_ONE_SAMPLE(); // 20
+ CONVOLVE_ONE_SAMPLE(); // 21
+ CONVOLVE_ONE_SAMPLE(); // 22
+ CONVOLVE_ONE_SAMPLE(); // 23
+ CONVOLVE_ONE_SAMPLE(); // 24
+ CONVOLVE_ONE_SAMPLE(); // 25
+ CONVOLVE_ONE_SAMPLE(); // 26
+ CONVOLVE_ONE_SAMPLE(); // 27
+ CONVOLVE_ONE_SAMPLE(); // 28
+ CONVOLVE_ONE_SAMPLE(); // 29
+ CONVOLVE_ONE_SAMPLE(); // 30
+ CONVOLVE_ONE_SAMPLE(); // 31
+ CONVOLVE_ONE_SAMPLE(); // 32
+ } else if (n == 64) {
+ CONVOLVE_ONE_SAMPLE(); // 1
+ CONVOLVE_ONE_SAMPLE(); // 2
+ CONVOLVE_ONE_SAMPLE(); // 3
+ CONVOLVE_ONE_SAMPLE(); // 4
+ CONVOLVE_ONE_SAMPLE(); // 5
+ CONVOLVE_ONE_SAMPLE(); // 6
+ CONVOLVE_ONE_SAMPLE(); // 7
+ CONVOLVE_ONE_SAMPLE(); // 8
+ CONVOLVE_ONE_SAMPLE(); // 9
+ CONVOLVE_ONE_SAMPLE(); // 10
+ CONVOLVE_ONE_SAMPLE(); // 11
+ CONVOLVE_ONE_SAMPLE(); // 12
+ CONVOLVE_ONE_SAMPLE(); // 13
+ CONVOLVE_ONE_SAMPLE(); // 14
+ CONVOLVE_ONE_SAMPLE(); // 15
+ CONVOLVE_ONE_SAMPLE(); // 16
+ CONVOLVE_ONE_SAMPLE(); // 17
+ CONVOLVE_ONE_SAMPLE(); // 18
+ CONVOLVE_ONE_SAMPLE(); // 19
+ CONVOLVE_ONE_SAMPLE(); // 20
+ CONVOLVE_ONE_SAMPLE(); // 21
+ CONVOLVE_ONE_SAMPLE(); // 22
+ CONVOLVE_ONE_SAMPLE(); // 23
+ CONVOLVE_ONE_SAMPLE(); // 24
+ CONVOLVE_ONE_SAMPLE(); // 25
+ CONVOLVE_ONE_SAMPLE(); // 26
+ CONVOLVE_ONE_SAMPLE(); // 27
+ CONVOLVE_ONE_SAMPLE(); // 28
+ CONVOLVE_ONE_SAMPLE(); // 29
+ CONVOLVE_ONE_SAMPLE(); // 30
+ CONVOLVE_ONE_SAMPLE(); // 31
+ CONVOLVE_ONE_SAMPLE(); // 32
+ CONVOLVE_ONE_SAMPLE(); // 33
+ CONVOLVE_ONE_SAMPLE(); // 34
+ CONVOLVE_ONE_SAMPLE(); // 35
+ CONVOLVE_ONE_SAMPLE(); // 36
+ CONVOLVE_ONE_SAMPLE(); // 37
+ CONVOLVE_ONE_SAMPLE(); // 38
+ CONVOLVE_ONE_SAMPLE(); // 39
+ CONVOLVE_ONE_SAMPLE(); // 40
+ CONVOLVE_ONE_SAMPLE(); // 41
+ CONVOLVE_ONE_SAMPLE(); // 42
+ CONVOLVE_ONE_SAMPLE(); // 43
+ CONVOLVE_ONE_SAMPLE(); // 44
+ CONVOLVE_ONE_SAMPLE(); // 45
+ CONVOLVE_ONE_SAMPLE(); // 46
+ CONVOLVE_ONE_SAMPLE(); // 47
+ CONVOLVE_ONE_SAMPLE(); // 48
+ CONVOLVE_ONE_SAMPLE(); // 49
+ CONVOLVE_ONE_SAMPLE(); // 50
+ CONVOLVE_ONE_SAMPLE(); // 51
+ CONVOLVE_ONE_SAMPLE(); // 52
+ CONVOLVE_ONE_SAMPLE(); // 53
+ CONVOLVE_ONE_SAMPLE(); // 54
+ CONVOLVE_ONE_SAMPLE(); // 55
+ CONVOLVE_ONE_SAMPLE(); // 56
+ CONVOLVE_ONE_SAMPLE(); // 57
+ CONVOLVE_ONE_SAMPLE(); // 58
+ CONVOLVE_ONE_SAMPLE(); // 59
+ CONVOLVE_ONE_SAMPLE(); // 60
+ CONVOLVE_ONE_SAMPLE(); // 61
+ CONVOLVE_ONE_SAMPLE(); // 62
+ CONVOLVE_ONE_SAMPLE(); // 63
+ CONVOLVE_ONE_SAMPLE(); // 64
+ } else {
+ while (n--) {
+ // Non-optimized using actual while loop.
+ CONVOLVE_ONE_SAMPLE();
+ }
+ }
+#endif
+ }
+#undef CONVOLVE_ONE_SAMPLE
+
+ // Linearly interpolate the two "convolutions".
+ double result = (1.0 - kernel_interpolation_factor) * sum1 +
+ kernel_interpolation_factor * sum2;
+
+ *destination++ = result;
+
+ // Advance the virtual index.
+ virtual_source_index_ += scale_factor_;
+
+ --number_of_destination_frames;
+ if (!number_of_destination_frames)
+ return;
+ }
+
+ // Wrap back around to the start.
+ virtual_source_index_ -= block_size_;
+
+ // Step (3) Copy r3 to r1 and r4 to r2.
+ // This wraps the last input frames back to the start of the buffer.
+ memcpy(r1, r3, sizeof(float) * (kernel_size_ / 2));
+ memcpy(r2, r4, sizeof(float) * (kernel_size_ / 2));
+
+ // Step (4)
+ // Refresh the buffer with more input.
+ ConsumeSource(r5, block_size_);
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/sinc_resampler.h b/chromium/third_party/blink/renderer/platform/audio/sinc_resampler.h
new file mode 100644
index 00000000000..f0f83c7cd26
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/sinc_resampler.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_SINC_RESAMPLER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_SINC_RESAMPLER_H_
+
+#include "third_party/blink/renderer/platform/audio/audio_array.h"
+#include "third_party/blink/renderer/platform/audio/audio_source_provider.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+// SincResampler is a high-quality sample-rate converter.
+
+class PLATFORM_EXPORT SincResampler {
+ USING_FAST_MALLOC(SincResampler);
+ WTF_MAKE_NONCOPYABLE(SincResampler);
+
+ public:
+ // scaleFactor == sourceSampleRate / destinationSampleRate
+ // kernelSize can be adjusted for quality (higher is better)
+ // numberOfKernelOffsets is used for interpolation and is the number of
+ // sub-sample kernel shifts.
+ SincResampler(double scale_factor,
+ unsigned kernel_size = 32,
+ unsigned number_of_kernel_offsets = 32);
+
+ // Processes numberOfSourceFrames from source to produce numberOfSourceFrames
+ // / scaleFactor frames in destination.
+ void Process(const float* source,
+ float* destination,
+ unsigned number_of_source_frames);
+
+ // Process with input source callback function for streaming applications.
+ void Process(AudioSourceProvider*,
+ float* destination,
+ size_t frames_to_process);
+
+ protected:
+ void InitializeKernel();
+ void ConsumeSource(float* buffer, unsigned number_of_source_frames);
+
+ double scale_factor_;
+ unsigned kernel_size_;
+ unsigned number_of_kernel_offsets_;
+
+ // m_kernelStorage has m_numberOfKernelOffsets kernels back-to-back, each of
+ // size m_kernelSize. The kernel offsets are sub-sample shifts of a windowed
+ // sinc() shifted from 0.0 to 1.0 sample.
+ AudioFloatArray kernel_storage_;
+
+ // m_virtualSourceIndex is an index on the source input buffer with sub-sample
+ // precision. It must be double precision to avoid drift.
+ double virtual_source_index_;
+
+ // This is the number of destination frames we generate per processing pass on
+ // the buffer.
+ unsigned block_size_;
+
+ // Source is copied into this buffer for each processing pass.
+ AudioFloatArray input_buffer_;
+
+ const float* source_;
+ unsigned source_frames_available_;
+
+ // m_sourceProvider is used to provide the audio input stream to the
+ // resampler.
+ AudioSourceProvider* source_provider_;
+
+ // The buffer is primed once at the very beginning of processing.
+ bool is_buffer_primed_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_SINC_RESAMPLER_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/stereo_panner.cc b/chromium/third_party/blink/renderer/platform/audio/stereo_panner.cc
new file mode 100644
index 00000000000..6c911eba328
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/stereo_panner.cc
@@ -0,0 +1,169 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/audio/stereo_panner.h"
+
+#include <algorithm>
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/platform/audio/audio_bus.h"
+#include "third_party/blink/renderer/platform/audio/audio_utilities.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+namespace blink {
+
+// Implement equal-power panning algorithm for mono or stereo input.
+// See: http://webaudio.github.io/web-audio-api/#panning-algorithm
+
+std::unique_ptr<StereoPanner> StereoPanner::Create(float sample_rate) {
+ return base::WrapUnique(new StereoPanner(sample_rate));
+}
+
+StereoPanner::StereoPanner(float sample_rate) {}
+
+void StereoPanner::PanWithSampleAccurateValues(const AudioBus* input_bus,
+ AudioBus* output_bus,
+ const float* pan_values,
+ size_t frames_to_process) {
+ bool is_input_safe = input_bus &&
+ (input_bus->NumberOfChannels() == 1 ||
+ input_bus->NumberOfChannels() == 2) &&
+ frames_to_process <= input_bus->length();
+ DCHECK(is_input_safe);
+ if (!is_input_safe)
+ return;
+
+ unsigned number_of_input_channels = input_bus->NumberOfChannels();
+
+ bool is_output_safe = output_bus && output_bus->NumberOfChannels() == 2 &&
+ frames_to_process <= output_bus->length();
+ DCHECK(is_output_safe);
+ if (!is_output_safe)
+ return;
+
+ const float* source_l = input_bus->Channel(0)->Data();
+ const float* source_r =
+ number_of_input_channels > 1 ? input_bus->Channel(1)->Data() : source_l;
+ float* destination_l =
+ output_bus->ChannelByType(AudioBus::kChannelLeft)->MutableData();
+ float* destination_r =
+ output_bus->ChannelByType(AudioBus::kChannelRight)->MutableData();
+
+ if (!source_l || !source_r || !destination_l || !destination_r)
+ return;
+
+ double gain_l, gain_r, pan_radian;
+
+ int n = frames_to_process;
+
+ if (number_of_input_channels == 1) { // For mono source case.
+ while (n--) {
+ float input_l = *source_l++;
+ double pan = clampTo(*pan_values++, -1.0, 1.0);
+ // Pan from left to right [-1; 1] will be normalized as [0; 1].
+ pan_radian = (pan * 0.5 + 0.5) * piOverTwoDouble;
+ gain_l = std::cos(pan_radian);
+ gain_r = std::sin(pan_radian);
+ *destination_l++ = static_cast<float>(input_l * gain_l);
+ *destination_r++ = static_cast<float>(input_l * gain_r);
+ }
+ } else { // For stereo source case.
+ while (n--) {
+ float input_l = *source_l++;
+ float input_r = *source_r++;
+ double pan = clampTo(*pan_values++, -1.0, 1.0);
+ // Normalize [-1; 0] to [0; 1]. Do nothing when [0; 1].
+ pan_radian = (pan <= 0 ? pan + 1 : pan) * piOverTwoDouble;
+ gain_l = std::cos(pan_radian);
+ gain_r = std::sin(pan_radian);
+ if (pan <= 0) {
+ *destination_l++ = static_cast<float>(input_l + input_r * gain_l);
+ *destination_r++ = static_cast<float>(input_r * gain_r);
+ } else {
+ *destination_l++ = static_cast<float>(input_l * gain_l);
+ *destination_r++ = static_cast<float>(input_r + input_l * gain_r);
+ }
+ }
+ }
+}
+
+void StereoPanner::PanToTargetValue(const AudioBus* input_bus,
+ AudioBus* output_bus,
+ float pan_value,
+ size_t frames_to_process) {
+ bool is_input_safe = input_bus &&
+ (input_bus->NumberOfChannels() == 1 ||
+ input_bus->NumberOfChannels() == 2) &&
+ frames_to_process <= input_bus->length();
+ DCHECK(is_input_safe);
+ if (!is_input_safe)
+ return;
+
+ unsigned number_of_input_channels = input_bus->NumberOfChannels();
+
+ bool is_output_safe = output_bus && output_bus->NumberOfChannels() == 2 &&
+ frames_to_process <= output_bus->length();
+ DCHECK(is_output_safe);
+ if (!is_output_safe)
+ return;
+
+ const float* source_l = input_bus->Channel(0)->Data();
+ const float* source_r =
+ number_of_input_channels > 1 ? input_bus->Channel(1)->Data() : source_l;
+ float* destination_l =
+ output_bus->ChannelByType(AudioBus::kChannelLeft)->MutableData();
+ float* destination_r =
+ output_bus->ChannelByType(AudioBus::kChannelRight)->MutableData();
+
+ if (!source_l || !source_r || !destination_l || !destination_r)
+ return;
+
+ float target_pan = clampTo(pan_value, -1.0, 1.0);
+
+ int n = frames_to_process;
+
+ if (number_of_input_channels == 1) { // For mono source case.
+ // Pan from left to right [-1; 1] will be normalized as [0; 1].
+ double pan_radian = (target_pan * 0.5 + 0.5) * piOverTwoDouble;
+
+ double gain_l = std::cos(pan_radian);
+ double gain_r = std::sin(pan_radian);
+
+ // TODO(rtoy): This can be vectorized using VectorMath::Vsmul
+ while (n--) {
+ float input_l = *source_l++;
+ *destination_l++ = static_cast<float>(input_l * gain_l);
+ *destination_r++ = static_cast<float>(input_l * gain_r);
+ }
+ } else { // For stereo source case.
+ // Normalize [-1; 0] to [0; 1] for the left pan position (<= 0), and
+ // do nothing when [0; 1].
+ double pan_radian =
+ (target_pan <= 0 ? target_pan + 1 : target_pan) * piOverTwoDouble;
+
+ double gain_l = std::cos(pan_radian);
+ double gain_r = std::sin(pan_radian);
+
+ // TODO(rtoy): Consider moving the if statement outside the loop
+ // since |target_pan| is constant inside the loop.
+ while (n--) {
+ float input_l = *source_l++;
+ float input_r = *source_r++;
+ if (target_pan <= 0) {
+ // When [-1; 0], keep left channel intact and equal-power pan the
+ // right channel only.
+ *destination_l++ = static_cast<float>(input_l + input_r * gain_l);
+ *destination_r++ = static_cast<float>(input_r * gain_r);
+ } else {
+ // When [0; 1], keep right channel intact and equal-power pan the
+ // left channel only.
+ *destination_l++ = static_cast<float>(input_l * gain_l);
+ *destination_r++ = static_cast<float>(input_r + input_l * gain_r);
+ }
+ }
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/stereo_panner.h b/chromium/third_party/blink/renderer/platform/audio/stereo_panner.h
new file mode 100644
index 00000000000..cf1b7efce09
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/stereo_panner.h
@@ -0,0 +1,43 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_STEREO_PANNER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_STEREO_PANNER_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class AudioBus;
+
+// Implement the equal-power panning algorithm for mono or stereo input. See:
+// https://webaudio.github.io/web-audio-api/#Spatialzation-equal-power-panning
+
+class PLATFORM_EXPORT StereoPanner {
+ USING_FAST_MALLOC(StereoPanner);
+ WTF_MAKE_NONCOPYABLE(StereoPanner);
+
+ public:
+ static std::unique_ptr<StereoPanner> Create(float sample_rate);
+ ~StereoPanner() = default;
+ ;
+
+ void PanWithSampleAccurateValues(const AudioBus* input_bus,
+ AudioBus* output_bus,
+ const float* pan_values,
+ size_t frames_to_process);
+ void PanToTargetValue(const AudioBus* input_bus,
+ AudioBus* output_bus,
+ float pan_value,
+ size_t frames_to_process);
+
+ private:
+ explicit StereoPanner(float sample_rate);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_STEREO_PANNER_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/up_sampler.cc b/chromium/third_party/blink/renderer/platform/audio/up_sampler.cc
new file mode 100644
index 00000000000..62eae93248d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/up_sampler.cc
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/renderer/platform/audio/up_sampler.h"
+
+#include <memory>
+
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+namespace blink {
+
+namespace {
+
+// Computes ideal band-limited filter coefficients to sample in between each
+// source sample-frame. This filter will be used to compute the odd
+// sample-frames of the output.
+std::unique_ptr<AudioFloatArray> MakeKernel(size_t size) {
+ std::unique_ptr<AudioFloatArray> kernel =
+ std::make_unique<AudioFloatArray>(size);
+
+ // Blackman window parameters.
+ double alpha = 0.16;
+ double a0 = 0.5 * (1.0 - alpha);
+ double a1 = 0.5;
+ double a2 = 0.5 * alpha;
+
+ int n = kernel->size();
+ int half_size = n / 2;
+ double subsample_offset = -0.5;
+
+ for (int i = 0; i < n; ++i) {
+ // Compute the sinc() with offset.
+ double s = piDouble * (i - half_size - subsample_offset);
+ double sinc = !s ? 1.0 : sin(s) / s;
+
+ // Compute Blackman window, matching the offset of the sinc().
+ double x = (i - subsample_offset) / n;
+ double window =
+ a0 - a1 * cos(twoPiDouble * x) + a2 * cos(twoPiDouble * 2.0 * x);
+
+ // Window the sinc() function.
+ (*kernel)[i] = sinc * window;
+ }
+
+ return kernel;
+}
+
+} // namespace
+
+UpSampler::UpSampler(size_t input_block_size)
+ : input_block_size_(input_block_size),
+ convolver_(input_block_size, MakeKernel(kDefaultKernelSize)),
+ temp_buffer_(input_block_size),
+ input_buffer_(input_block_size * 2) {}
+
+void UpSampler::Process(const float* source_p,
+ float* dest_p,
+ size_t source_frames_to_process) {
+ bool is_input_block_size_good = source_frames_to_process == input_block_size_;
+ DCHECK(is_input_block_size_good);
+ if (!is_input_block_size_good)
+ return;
+
+ bool is_temp_buffer_good = source_frames_to_process == temp_buffer_.size();
+ DCHECK(is_temp_buffer_good);
+ if (!is_temp_buffer_good)
+ return;
+
+ bool is_kernel_good =
+ convolver_.ConvolutionKernelSize() == kDefaultKernelSize;
+ DCHECK(is_kernel_good);
+ if (!is_kernel_good)
+ return;
+
+ size_t half_size = convolver_.ConvolutionKernelSize() / 2;
+
+ // Copy source samples to 2nd half of input buffer.
+ bool is_input_buffer_good =
+ input_buffer_.size() == source_frames_to_process * 2 &&
+ half_size <= source_frames_to_process;
+ DCHECK(is_input_buffer_good);
+ if (!is_input_buffer_good)
+ return;
+
+ float* input_p = input_buffer_.Data() + source_frames_to_process;
+ memcpy(input_p, source_p, sizeof(float) * source_frames_to_process);
+
+ // Copy even sample-frames 0,2,4,6... (delayed by the linear phase delay)
+ // directly into destP.
+ for (unsigned i = 0; i < source_frames_to_process; ++i)
+ dest_p[i * 2] = *((input_p - half_size) + i);
+
+ // Compute odd sample-frames 1,3,5,7...
+ float* odd_samples_p = temp_buffer_.Data();
+ convolver_.Process(source_p, odd_samples_p, source_frames_to_process);
+
+ for (unsigned i = 0; i < source_frames_to_process; ++i)
+ dest_p[i * 2 + 1] = odd_samples_p[i];
+
+ // Copy 2nd half of input buffer to 1st half.
+ memcpy(input_buffer_.Data(), input_p,
+ sizeof(float) * source_frames_to_process);
+}
+
+void UpSampler::Reset() {
+ convolver_.Reset();
+ input_buffer_.Zero();
+}
+
+size_t UpSampler::LatencyFrames() const {
+ // Divide by two since this is a linear phase kernel and the delay is at the
+ // center of the kernel.
+ return convolver_.ConvolutionKernelSize() / 2;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/up_sampler.h b/chromium/third_party/blink/renderer/platform/audio/up_sampler.h
new file mode 100644
index 00000000000..69000eeb843
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/up_sampler.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_UP_SAMPLER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_UP_SAMPLER_H_
+
+#include "third_party/blink/renderer/platform/audio/audio_array.h"
+#include "third_party/blink/renderer/platform/audio/direct_convolver.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+// UpSampler up-samples the source stream by a factor of 2x.
+
+class PLATFORM_EXPORT UpSampler {
+ USING_FAST_MALLOC(UpSampler);
+ WTF_MAKE_NONCOPYABLE(UpSampler);
+
+ public:
+ explicit UpSampler(size_t input_block_size);
+
+ // The destination buffer |destP| is of size sourceFramesToProcess * 2.
+ void Process(const float* source_p,
+ float* dest_p,
+ size_t source_frames_to_process);
+
+ void Reset();
+
+ // Latency based on the source sample-rate.
+ size_t LatencyFrames() const;
+
+ private:
+ enum { kDefaultKernelSize = 128 };
+
+ size_t input_block_size_;
+
+ // Computes the odd sample-frames of the output.
+ DirectConvolver convolver_;
+
+ AudioFloatArray temp_buffer_;
+
+ // Delay line for generating the even sample-frames of the output.
+ // The source samples are delayed exactly to match the linear phase delay of
+ // the FIR filter (convolution) used to generate the odd sample-frames of the
+ // output.
+ AudioFloatArray input_buffer_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_UP_SAMPLER_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/vector_math.cc b/chromium/third_party/blink/renderer/platform/audio/vector_math.cc
new file mode 100644
index 00000000000..388fecddd00
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/vector_math.cc
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2010, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/audio/vector_math.h"
+
+#include <cmath>
+
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/cpu.h"
+
+#if defined(OS_MACOSX)
+#include "third_party/blink/renderer/platform/audio/mac/vector_math_mac.h"
+#elif WTF_CPU_ARM_NEON
+#include "third_party/blink/renderer/platform/audio/cpu/arm/vector_math_neon.h"
+#elif HAVE_MIPS_MSA_INTRINSICS
+#include "third_party/blink/renderer/platform/audio/cpu/mips/vector_math_msa.h"
+#elif defined(ARCH_CPU_X86_FAMILY)
+#include "third_party/blink/renderer/platform/audio/cpu/x86/vector_math_x8_6.h"
+#else
+#include "third_party/blink/renderer/platform/audio/vector_math_scalar.h"
+#endif
+
+namespace blink {
+
+namespace VectorMath {
+
+namespace {
+#if defined(OS_MACOSX)
+namespace Impl = Mac;
+#elif WTF_CPU_ARM_NEON
+namespace Impl = NEON;
+#elif HAVE_MIPS_MSA_INTRINSICS
+namespace Impl = MSA;
+#elif defined(ARCH_CPU_X86_FAMILY)
+namespace Impl = X86;
+#else
+namespace Impl = Scalar;
+#endif
+} // namespace
+
+void PrepareFilterForConv(const float* filter_p,
+ int filter_stride,
+ size_t filter_size,
+ AudioFloatArray* prepared_filter) {
+ // Only contiguous convolution is implemented by all implementations.
+ // Correlation (positive |filter_stride|) and support for non-contiguous
+ // vectors are not implemented by all implementations.
+ DCHECK_EQ(-1, filter_stride);
+ DCHECK(prepared_filter);
+#if defined(ARCH_CPU_X86_FAMILY) && !defined(OS_MACOSX)
+ X86::PrepareFilterForConv(filter_p, filter_stride, filter_size,
+ prepared_filter);
+#endif
+}
+
+void Conv(const float* source_p,
+ int source_stride,
+ const float* filter_p,
+ int filter_stride,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process,
+ size_t filter_size,
+ const AudioFloatArray* prepared_filter) {
+ // Only contiguous convolution is implemented by all implementations.
+ // Correlation (positive |filter_stride|) and support for non-contiguous
+ // vectors are not implemented by all implementations.
+ DCHECK_EQ(1, source_stride);
+ DCHECK_EQ(-1, filter_stride);
+ DCHECK_EQ(1, dest_stride);
+ Impl::Conv(source_p, source_stride, filter_p, filter_stride, dest_p,
+ dest_stride, frames_to_process, filter_size, prepared_filter);
+}
+
+void Vadd(const float* source1p,
+ int source_stride1,
+ const float* source2p,
+ int source_stride2,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process) {
+ Impl::Vadd(source1p, source_stride1, source2p, source_stride2, dest_p,
+ dest_stride, frames_to_process);
+}
+
+void Vclip(const float* source_p,
+ int source_stride,
+ const float* low_threshold_p,
+ const float* high_threshold_p,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process) {
+ float low_threshold = *low_threshold_p;
+ float high_threshold = *high_threshold_p;
+
+#if DCHECK_IS_ON()
+ // Do the same DCHECKs that |clampTo| would do so that optimization paths do
+ // not have to do them.
+ for (size_t i = 0u; i < frames_to_process; ++i)
+ DCHECK(!std::isnan(source_p[i]));
+ // This also ensures that thresholds are not NaNs.
+ DCHECK_LE(low_threshold, high_threshold);
+#endif
+
+ Impl::Vclip(source_p, source_stride, &low_threshold, &high_threshold, dest_p,
+ dest_stride, frames_to_process);
+}
+
+void Vmaxmgv(const float* source_p,
+ int source_stride,
+ float* max_p,
+ size_t frames_to_process) {
+ float max = 0;
+
+ Impl::Vmaxmgv(source_p, source_stride, &max, frames_to_process);
+
+ DCHECK(max_p);
+ *max_p = max;
+}
+
+void Vmul(const float* source1p,
+ int source_stride1,
+ const float* source2p,
+ int source_stride2,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process) {
+ Impl::Vmul(source1p, source_stride1, source2p, source_stride2, dest_p,
+ dest_stride, frames_to_process);
+}
+
+void Vsma(const float* source_p,
+ int source_stride,
+ const float* scale,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process) {
+ const float k = *scale;
+
+ Impl::Vsma(source_p, source_stride, &k, dest_p, dest_stride,
+ frames_to_process);
+}
+
+void Vsmul(const float* source_p,
+ int source_stride,
+ const float* scale,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process) {
+ const float k = *scale;
+
+ Impl::Vsmul(source_p, source_stride, &k, dest_p, dest_stride,
+ frames_to_process);
+}
+
+void Vsvesq(const float* source_p,
+ int source_stride,
+ float* sum_p,
+ size_t frames_to_process) {
+ float sum = 0;
+
+ Impl::Vsvesq(source_p, source_stride, &sum, frames_to_process);
+
+ DCHECK(sum_p);
+ *sum_p = sum;
+}
+
+void Zvmul(const float* real1p,
+ const float* imag1p,
+ const float* real2p,
+ const float* imag2p,
+ float* real_dest_p,
+ float* imag_dest_p,
+ size_t frames_to_process) {
+ Impl::Zvmul(real1p, imag1p, real2p, imag2p, real_dest_p, imag_dest_p,
+ frames_to_process);
+}
+
+} // namespace VectorMath
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/audio/vector_math.h b/chromium/third_party/blink/renderer/platform/audio/vector_math.h
new file mode 100644
index 00000000000..1dec0c9acf1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/vector_math.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2010, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_VECTOR_MATH_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_VECTOR_MATH_H_
+
+#include <cstddef>
+
+#include "third_party/blink/renderer/platform/audio/audio_array.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+// Defines the interface for several vector math functions whose implementation
+// will ideally be optimized.
+
+namespace blink {
+namespace VectorMath {
+
+// Direct vector convolution:
+//
+// dest[k*dest_stride] =
+// sum(source[(k+m)*source_stride]*filter[m*filter_stride]) for all m
+PLATFORM_EXPORT void Conv(const float* source_p,
+ int source_stride,
+ const float* filter_p,
+ int filter_stride,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process,
+ size_t filter_size,
+ const AudioFloatArray* prepared_filter);
+
+// Prepare filter for Conv for faster processing.
+PLATFORM_EXPORT void PrepareFilterForConv(const float* filter_p,
+ int filter_stride,
+ size_t filter_size,
+ AudioFloatArray* prepared_filter);
+
+// Vector scalar multiply and then add.
+//
+// dest[k*dest_stride] += scale * source[k*source_stride]
+PLATFORM_EXPORT void Vsma(const float* source_p,
+ int source_stride,
+ const float* scale,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process);
+
+// Vector scalar multiply:
+//
+// dest[k*dest_stride] = scale * source[k*source_stride]
+PLATFORM_EXPORT void Vsmul(const float* source_p,
+ int source_stride,
+ const float* scale,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process);
+
+// Vector add:
+//
+// dest[k*dest_stride] = source1[k*source_stride1] + source2[k*source_stride2]
+PLATFORM_EXPORT void Vadd(const float* source1p,
+ int source_stride1,
+ const float* source2p,
+ int source_stride2,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process);
+
+// Finds the maximum magnitude of a float vector:
+//
+// max = max(abs(source[k*source_stride])) for all k.
+PLATFORM_EXPORT void Vmaxmgv(const float* source_p,
+ int source_stride,
+ float* max_p,
+ size_t frames_to_process);
+
+// Sums the squares of a float vector's elements:
+//
+// sum = sum(source[k*source_stride]^2, k = 0, frames_to_process);
+PLATFORM_EXPORT void Vsvesq(const float* source_p,
+ int source_stride,
+ float* sum_p,
+ size_t frames_to_process);
+
+// For an element-by-element multiply of two float vectors:
+//
+// dest[k*dest_stride] = source1[k*source_stride1] * source2[k*source_stride2]
+PLATFORM_EXPORT void Vmul(const float* source1p,
+ int source_stride1,
+ const float* source2p,
+ int source_stride2,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process);
+
+// Multiplies two complex vectors. Complex version of Vmul where |rea1p| and
+// |imag1p| forms the real and complex components of source1; |real2p| and
+// |imag2p| the components of source2, and |real_dest_p| and |imag_dest_p|, the
+// components of the destination.
+PLATFORM_EXPORT void Zvmul(const float* real1p,
+ const float* imag1p,
+ const float* real2p,
+ const float* imag2p,
+ float* real_dest_p,
+ float* imag_dest_p,
+ size_t frames_to_process);
+
+// Copies elements while clipping values to the threshold inputs.
+//
+// dest[k*dest_stride] = clip(source[k*source_stride], low, high)
+//
+// where y = clip(x, low, high) = max(low, min(x, high)), effectively making
+// low <= y <= high.
+PLATFORM_EXPORT void Vclip(const float* source_p,
+ int source_stride,
+ const float* low_threshold_p,
+ const float* high_threshold_p,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process);
+
+} // namespace VectorMath
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_VECTOR_MATH_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/vector_math_scalar.h b/chromium/third_party/blink/renderer/platform/audio/vector_math_scalar.h
new file mode 100644
index 00000000000..70b0a59d446
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/vector_math_scalar.h
@@ -0,0 +1,433 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_VECTOR_MATH_SCALAR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_VECTOR_MATH_SCALAR_H_
+
+#include <algorithm>
+#include <cmath>
+
+#include "third_party/blink/renderer/platform/audio/audio_array.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+namespace blink {
+namespace VectorMath {
+namespace Scalar {
+
+static ALWAYS_INLINE void Conv(const float* source_p,
+ int source_stride,
+ const float* filter_p,
+ int filter_stride,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process,
+ size_t filter_size,
+ const AudioFloatArray* /*prepared_filter*/) {
+ // Only contiguous convolution is implemented. Correlation (positive
+ // |filter_stride|) and support for non-contiguous vectors are not
+ // implemented.
+ DCHECK_EQ(1, source_stride);
+ DCHECK_EQ(-1, filter_stride);
+ DCHECK_EQ(1, dest_stride);
+
+ size_t i = 0;
+
+// FIXME: The macro can be further optimized to avoid pipeline stalls. One
+// possibility is to maintain 4 separate sums and change the macro to
+// CONVOLVE_FOUR_SAMPLES.
+#define CONVOLVE_ONE_SAMPLE \
+ do { \
+ sum += source_p[i + j] * *(filter_p - j); \
+ j++; \
+ } while (0)
+
+ while (i < frames_to_process) {
+ size_t j = 0;
+ float sum = 0;
+
+ if (filter_size == 32) {
+ CONVOLVE_ONE_SAMPLE; // 1
+ CONVOLVE_ONE_SAMPLE; // 2
+ CONVOLVE_ONE_SAMPLE; // 3
+ CONVOLVE_ONE_SAMPLE; // 4
+ CONVOLVE_ONE_SAMPLE; // 5
+ CONVOLVE_ONE_SAMPLE; // 6
+ CONVOLVE_ONE_SAMPLE; // 7
+ CONVOLVE_ONE_SAMPLE; // 8
+ CONVOLVE_ONE_SAMPLE; // 9
+ CONVOLVE_ONE_SAMPLE; // 10
+
+ CONVOLVE_ONE_SAMPLE; // 11
+ CONVOLVE_ONE_SAMPLE; // 12
+ CONVOLVE_ONE_SAMPLE; // 13
+ CONVOLVE_ONE_SAMPLE; // 14
+ CONVOLVE_ONE_SAMPLE; // 15
+ CONVOLVE_ONE_SAMPLE; // 16
+ CONVOLVE_ONE_SAMPLE; // 17
+ CONVOLVE_ONE_SAMPLE; // 18
+ CONVOLVE_ONE_SAMPLE; // 19
+ CONVOLVE_ONE_SAMPLE; // 20
+
+ CONVOLVE_ONE_SAMPLE; // 21
+ CONVOLVE_ONE_SAMPLE; // 22
+ CONVOLVE_ONE_SAMPLE; // 23
+ CONVOLVE_ONE_SAMPLE; // 24
+ CONVOLVE_ONE_SAMPLE; // 25
+ CONVOLVE_ONE_SAMPLE; // 26
+ CONVOLVE_ONE_SAMPLE; // 27
+ CONVOLVE_ONE_SAMPLE; // 28
+ CONVOLVE_ONE_SAMPLE; // 29
+ CONVOLVE_ONE_SAMPLE; // 30
+
+ CONVOLVE_ONE_SAMPLE; // 31
+ CONVOLVE_ONE_SAMPLE; // 32
+
+ } else if (filter_size == 64) {
+ CONVOLVE_ONE_SAMPLE; // 1
+ CONVOLVE_ONE_SAMPLE; // 2
+ CONVOLVE_ONE_SAMPLE; // 3
+ CONVOLVE_ONE_SAMPLE; // 4
+ CONVOLVE_ONE_SAMPLE; // 5
+ CONVOLVE_ONE_SAMPLE; // 6
+ CONVOLVE_ONE_SAMPLE; // 7
+ CONVOLVE_ONE_SAMPLE; // 8
+ CONVOLVE_ONE_SAMPLE; // 9
+ CONVOLVE_ONE_SAMPLE; // 10
+
+ CONVOLVE_ONE_SAMPLE; // 11
+ CONVOLVE_ONE_SAMPLE; // 12
+ CONVOLVE_ONE_SAMPLE; // 13
+ CONVOLVE_ONE_SAMPLE; // 14
+ CONVOLVE_ONE_SAMPLE; // 15
+ CONVOLVE_ONE_SAMPLE; // 16
+ CONVOLVE_ONE_SAMPLE; // 17
+ CONVOLVE_ONE_SAMPLE; // 18
+ CONVOLVE_ONE_SAMPLE; // 19
+ CONVOLVE_ONE_SAMPLE; // 20
+
+ CONVOLVE_ONE_SAMPLE; // 21
+ CONVOLVE_ONE_SAMPLE; // 22
+ CONVOLVE_ONE_SAMPLE; // 23
+ CONVOLVE_ONE_SAMPLE; // 24
+ CONVOLVE_ONE_SAMPLE; // 25
+ CONVOLVE_ONE_SAMPLE; // 26
+ CONVOLVE_ONE_SAMPLE; // 27
+ CONVOLVE_ONE_SAMPLE; // 28
+ CONVOLVE_ONE_SAMPLE; // 29
+ CONVOLVE_ONE_SAMPLE; // 30
+
+ CONVOLVE_ONE_SAMPLE; // 31
+ CONVOLVE_ONE_SAMPLE; // 32
+ CONVOLVE_ONE_SAMPLE; // 33
+ CONVOLVE_ONE_SAMPLE; // 34
+ CONVOLVE_ONE_SAMPLE; // 35
+ CONVOLVE_ONE_SAMPLE; // 36
+ CONVOLVE_ONE_SAMPLE; // 37
+ CONVOLVE_ONE_SAMPLE; // 38
+ CONVOLVE_ONE_SAMPLE; // 39
+ CONVOLVE_ONE_SAMPLE; // 40
+
+ CONVOLVE_ONE_SAMPLE; // 41
+ CONVOLVE_ONE_SAMPLE; // 42
+ CONVOLVE_ONE_SAMPLE; // 43
+ CONVOLVE_ONE_SAMPLE; // 44
+ CONVOLVE_ONE_SAMPLE; // 45
+ CONVOLVE_ONE_SAMPLE; // 46
+ CONVOLVE_ONE_SAMPLE; // 47
+ CONVOLVE_ONE_SAMPLE; // 48
+ CONVOLVE_ONE_SAMPLE; // 49
+ CONVOLVE_ONE_SAMPLE; // 50
+
+ CONVOLVE_ONE_SAMPLE; // 51
+ CONVOLVE_ONE_SAMPLE; // 52
+ CONVOLVE_ONE_SAMPLE; // 53
+ CONVOLVE_ONE_SAMPLE; // 54
+ CONVOLVE_ONE_SAMPLE; // 55
+ CONVOLVE_ONE_SAMPLE; // 56
+ CONVOLVE_ONE_SAMPLE; // 57
+ CONVOLVE_ONE_SAMPLE; // 58
+ CONVOLVE_ONE_SAMPLE; // 59
+ CONVOLVE_ONE_SAMPLE; // 60
+
+ CONVOLVE_ONE_SAMPLE; // 61
+ CONVOLVE_ONE_SAMPLE; // 62
+ CONVOLVE_ONE_SAMPLE; // 63
+ CONVOLVE_ONE_SAMPLE; // 64
+
+ } else if (filter_size == 128) {
+ CONVOLVE_ONE_SAMPLE; // 1
+ CONVOLVE_ONE_SAMPLE; // 2
+ CONVOLVE_ONE_SAMPLE; // 3
+ CONVOLVE_ONE_SAMPLE; // 4
+ CONVOLVE_ONE_SAMPLE; // 5
+ CONVOLVE_ONE_SAMPLE; // 6
+ CONVOLVE_ONE_SAMPLE; // 7
+ CONVOLVE_ONE_SAMPLE; // 8
+ CONVOLVE_ONE_SAMPLE; // 9
+ CONVOLVE_ONE_SAMPLE; // 10
+
+ CONVOLVE_ONE_SAMPLE; // 11
+ CONVOLVE_ONE_SAMPLE; // 12
+ CONVOLVE_ONE_SAMPLE; // 13
+ CONVOLVE_ONE_SAMPLE; // 14
+ CONVOLVE_ONE_SAMPLE; // 15
+ CONVOLVE_ONE_SAMPLE; // 16
+ CONVOLVE_ONE_SAMPLE; // 17
+ CONVOLVE_ONE_SAMPLE; // 18
+ CONVOLVE_ONE_SAMPLE; // 19
+ CONVOLVE_ONE_SAMPLE; // 20
+
+ CONVOLVE_ONE_SAMPLE; // 21
+ CONVOLVE_ONE_SAMPLE; // 22
+ CONVOLVE_ONE_SAMPLE; // 23
+ CONVOLVE_ONE_SAMPLE; // 24
+ CONVOLVE_ONE_SAMPLE; // 25
+ CONVOLVE_ONE_SAMPLE; // 26
+ CONVOLVE_ONE_SAMPLE; // 27
+ CONVOLVE_ONE_SAMPLE; // 28
+ CONVOLVE_ONE_SAMPLE; // 29
+ CONVOLVE_ONE_SAMPLE; // 30
+
+ CONVOLVE_ONE_SAMPLE; // 31
+ CONVOLVE_ONE_SAMPLE; // 32
+ CONVOLVE_ONE_SAMPLE; // 33
+ CONVOLVE_ONE_SAMPLE; // 34
+ CONVOLVE_ONE_SAMPLE; // 35
+ CONVOLVE_ONE_SAMPLE; // 36
+ CONVOLVE_ONE_SAMPLE; // 37
+ CONVOLVE_ONE_SAMPLE; // 38
+ CONVOLVE_ONE_SAMPLE; // 39
+ CONVOLVE_ONE_SAMPLE; // 40
+
+ CONVOLVE_ONE_SAMPLE; // 41
+ CONVOLVE_ONE_SAMPLE; // 42
+ CONVOLVE_ONE_SAMPLE; // 43
+ CONVOLVE_ONE_SAMPLE; // 44
+ CONVOLVE_ONE_SAMPLE; // 45
+ CONVOLVE_ONE_SAMPLE; // 46
+ CONVOLVE_ONE_SAMPLE; // 47
+ CONVOLVE_ONE_SAMPLE; // 48
+ CONVOLVE_ONE_SAMPLE; // 49
+ CONVOLVE_ONE_SAMPLE; // 50
+
+ CONVOLVE_ONE_SAMPLE; // 51
+ CONVOLVE_ONE_SAMPLE; // 52
+ CONVOLVE_ONE_SAMPLE; // 53
+ CONVOLVE_ONE_SAMPLE; // 54
+ CONVOLVE_ONE_SAMPLE; // 55
+ CONVOLVE_ONE_SAMPLE; // 56
+ CONVOLVE_ONE_SAMPLE; // 57
+ CONVOLVE_ONE_SAMPLE; // 58
+ CONVOLVE_ONE_SAMPLE; // 59
+ CONVOLVE_ONE_SAMPLE; // 60
+
+ CONVOLVE_ONE_SAMPLE; // 61
+ CONVOLVE_ONE_SAMPLE; // 62
+ CONVOLVE_ONE_SAMPLE; // 63
+ CONVOLVE_ONE_SAMPLE; // 64
+ CONVOLVE_ONE_SAMPLE; // 65
+ CONVOLVE_ONE_SAMPLE; // 66
+ CONVOLVE_ONE_SAMPLE; // 67
+ CONVOLVE_ONE_SAMPLE; // 68
+ CONVOLVE_ONE_SAMPLE; // 69
+ CONVOLVE_ONE_SAMPLE; // 70
+
+ CONVOLVE_ONE_SAMPLE; // 71
+ CONVOLVE_ONE_SAMPLE; // 72
+ CONVOLVE_ONE_SAMPLE; // 73
+ CONVOLVE_ONE_SAMPLE; // 74
+ CONVOLVE_ONE_SAMPLE; // 75
+ CONVOLVE_ONE_SAMPLE; // 76
+ CONVOLVE_ONE_SAMPLE; // 77
+ CONVOLVE_ONE_SAMPLE; // 78
+ CONVOLVE_ONE_SAMPLE; // 79
+ CONVOLVE_ONE_SAMPLE; // 80
+
+ CONVOLVE_ONE_SAMPLE; // 81
+ CONVOLVE_ONE_SAMPLE; // 82
+ CONVOLVE_ONE_SAMPLE; // 83
+ CONVOLVE_ONE_SAMPLE; // 84
+ CONVOLVE_ONE_SAMPLE; // 85
+ CONVOLVE_ONE_SAMPLE; // 86
+ CONVOLVE_ONE_SAMPLE; // 87
+ CONVOLVE_ONE_SAMPLE; // 88
+ CONVOLVE_ONE_SAMPLE; // 89
+ CONVOLVE_ONE_SAMPLE; // 90
+
+ CONVOLVE_ONE_SAMPLE; // 91
+ CONVOLVE_ONE_SAMPLE; // 92
+ CONVOLVE_ONE_SAMPLE; // 93
+ CONVOLVE_ONE_SAMPLE; // 94
+ CONVOLVE_ONE_SAMPLE; // 95
+ CONVOLVE_ONE_SAMPLE; // 96
+ CONVOLVE_ONE_SAMPLE; // 97
+ CONVOLVE_ONE_SAMPLE; // 98
+ CONVOLVE_ONE_SAMPLE; // 99
+ CONVOLVE_ONE_SAMPLE; // 100
+
+ CONVOLVE_ONE_SAMPLE; // 101
+ CONVOLVE_ONE_SAMPLE; // 102
+ CONVOLVE_ONE_SAMPLE; // 103
+ CONVOLVE_ONE_SAMPLE; // 104
+ CONVOLVE_ONE_SAMPLE; // 105
+ CONVOLVE_ONE_SAMPLE; // 106
+ CONVOLVE_ONE_SAMPLE; // 107
+ CONVOLVE_ONE_SAMPLE; // 108
+ CONVOLVE_ONE_SAMPLE; // 109
+ CONVOLVE_ONE_SAMPLE; // 110
+
+ CONVOLVE_ONE_SAMPLE; // 111
+ CONVOLVE_ONE_SAMPLE; // 112
+ CONVOLVE_ONE_SAMPLE; // 113
+ CONVOLVE_ONE_SAMPLE; // 114
+ CONVOLVE_ONE_SAMPLE; // 115
+ CONVOLVE_ONE_SAMPLE; // 116
+ CONVOLVE_ONE_SAMPLE; // 117
+ CONVOLVE_ONE_SAMPLE; // 118
+ CONVOLVE_ONE_SAMPLE; // 119
+ CONVOLVE_ONE_SAMPLE; // 120
+
+ CONVOLVE_ONE_SAMPLE; // 121
+ CONVOLVE_ONE_SAMPLE; // 122
+ CONVOLVE_ONE_SAMPLE; // 123
+ CONVOLVE_ONE_SAMPLE; // 124
+ CONVOLVE_ONE_SAMPLE; // 125
+ CONVOLVE_ONE_SAMPLE; // 126
+ CONVOLVE_ONE_SAMPLE; // 127
+ CONVOLVE_ONE_SAMPLE; // 128
+ } else {
+ while (j < filter_size) {
+ // Non-optimized using actual while loop.
+ CONVOLVE_ONE_SAMPLE;
+ }
+ }
+ dest_p[i++] = sum;
+ }
+#undef CONVOLVE_ONE_SAMPLE
+}
+
+static ALWAYS_INLINE void Vadd(const float* source1p,
+ int source_stride1,
+ const float* source2p,
+ int source_stride2,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process) {
+ while (frames_to_process > 0u) {
+ *dest_p = *source1p + *source2p;
+ source1p += source_stride1;
+ source2p += source_stride2;
+ dest_p += dest_stride;
+ --frames_to_process;
+ }
+}
+
+static ALWAYS_INLINE void Vclip(const float* source_p,
+ int source_stride,
+ const float* low_threshold_p,
+ const float* high_threshold_p,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process) {
+ while (frames_to_process > 0u) {
+ *dest_p = clampTo(*source_p, *low_threshold_p, *high_threshold_p);
+ source_p += source_stride;
+ dest_p += dest_stride;
+ --frames_to_process;
+ }
+}
+
+static ALWAYS_INLINE void Vmaxmgv(const float* source_p,
+ int source_stride,
+ float* max_p,
+ size_t frames_to_process) {
+ while (frames_to_process > 0u) {
+ *max_p = std::max(*max_p, std::abs(*source_p));
+ source_p += source_stride;
+ --frames_to_process;
+ }
+}
+
+static ALWAYS_INLINE void Vmul(const float* source1p,
+ int source_stride1,
+ const float* source2p,
+ int source_stride2,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process) {
+ while (frames_to_process > 0u) {
+ *dest_p = *source1p * *source2p;
+ source1p += source_stride1;
+ source2p += source_stride2;
+ dest_p += dest_stride;
+ --frames_to_process;
+ }
+}
+
+static ALWAYS_INLINE void Vsma(const float* source_p,
+ int source_stride,
+ const float* scale,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process) {
+ const float k = *scale;
+ while (frames_to_process > 0u) {
+ *dest_p += k * *source_p;
+ source_p += source_stride;
+ dest_p += dest_stride;
+ --frames_to_process;
+ }
+}
+
+static ALWAYS_INLINE void Vsmul(const float* source_p,
+ int source_stride,
+ const float* scale,
+ float* dest_p,
+ int dest_stride,
+ size_t frames_to_process) {
+ const float k = *scale;
+ while (frames_to_process > 0u) {
+ *dest_p = k * *source_p;
+ source_p += source_stride;
+ dest_p += dest_stride;
+ --frames_to_process;
+ }
+}
+
+static ALWAYS_INLINE void Vsvesq(const float* source_p,
+ int source_stride,
+ float* sum_p,
+ size_t frames_to_process) {
+ while (frames_to_process > 0u) {
+ const float sample = *source_p;
+ *sum_p += sample * sample;
+ source_p += source_stride;
+ --frames_to_process;
+ }
+}
+
+static ALWAYS_INLINE void Zvmul(const float* real1p,
+ const float* imag1p,
+ const float* real2p,
+ const float* imag2p,
+ float* real_dest_p,
+ float* imag_dest_p,
+ size_t frames_to_process) {
+ for (size_t i = 0u; i < frames_to_process; ++i) {
+ // Read and compute result before storing them, in case the
+ // destination is the same as one of the sources.
+ float real_result = real1p[i] * real2p[i] - imag1p[i] * imag2p[i];
+ float imag_result = real1p[i] * imag2p[i] + imag1p[i] * real2p[i];
+
+ real_dest_p[i] = real_result;
+ imag_dest_p[i] = imag_result;
+ }
+}
+
+} // namespace Scalar
+} // namespace VectorMath
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_VECTOR_MATH_SCALAR_H_
diff --git a/chromium/third_party/blink/renderer/platform/audio/vector_math_test.cc b/chromium/third_party/blink/renderer/platform/audio/vector_math_test.cc
new file mode 100644
index 00000000000..7b45ec290d5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/audio/vector_math_test.cc
@@ -0,0 +1,519 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/audio/vector_math.h"
+
+#include <algorithm>
+#include <array>
+#include <cmath>
+#include <limits>
+#include <numeric>
+#include <random>
+#include <vector>
+
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+namespace blink {
+namespace VectorMath {
+namespace {
+
+struct MemoryLayout {
+ size_t byte_alignment;
+ size_t stride;
+};
+
+// This is the minimum aligned needed by AVX on x86 family architectures.
+constexpr size_t kMaxBitAlignment = 256u;
+constexpr size_t kMaxByteAlignment = kMaxBitAlignment / 8u;
+
+constexpr size_t kMaxStride = 2u;
+
+constexpr MemoryLayout kMemoryLayouts[] = {
+ {kMaxByteAlignment / 4u, 1u},
+ {kMaxByteAlignment / 2u, 1u},
+ {kMaxByteAlignment / 2u + kMaxByteAlignment / 4u, 1u},
+ {kMaxByteAlignment, 1u},
+ {0u, kMaxStride}};
+constexpr size_t kMemoryLayoutCount =
+ sizeof(kMemoryLayouts) / sizeof(*kMemoryLayouts);
+
+// This is the minimum vector size in bytes needed for MSA instructions on
+// MIPS.
+constexpr size_t kMaxVectorSizeInBytes = 1024u;
+constexpr size_t kVectorSizesInBytes[] = {
+ kMaxVectorSizeInBytes,
+ // This vector size in bytes is chosen so that the following optimization
+ // paths can be tested on x86 family architectures using different memory
+ // layouts:
+ // * AVX + SSE + scalar
+ // * scalar + SSE + AVX
+ // * SSE + AVX + scalar
+ // * scalar + AVX + SSE
+ // On other architectures, this vector size in bytes results in either
+ // optimization + scalar path or scalar path to be tested.
+ kMaxByteAlignment + kMaxByteAlignment / 2u + kMaxByteAlignment / 4u};
+constexpr size_t kVectorSizeCount =
+ sizeof(kVectorSizesInBytes) / sizeof(*kVectorSizesInBytes);
+
+// Compare two floats and consider all NaNs to be equal.
+bool Equal(float a, float b) {
+ if (std::isnan(a))
+ return std::isnan(b);
+ return a == b;
+}
+
+// This represents a real source or destination vector which is aligned, can be
+// non-contiguous and can be used as a source or destination vector for
+// blink::VectorMath functions.
+template <typename T>
+class TestVector {
+ class Iterator {
+ public:
+ // These types are used by std::iterator_traits used by std::equal used by
+ // TestVector::operator==.
+ using difference_type = ptrdiff_t;
+ using iterator_category = std::bidirectional_iterator_tag;
+ using pointer = T*;
+ using reference = T&;
+ using value_type = T;
+
+ Iterator(T* p, int stride) : p_(p), stride_(stride) {}
+
+ Iterator& operator++() {
+ p_ += stride_;
+ return *this;
+ }
+ Iterator operator++(int) {
+ Iterator iter = *this;
+ ++(*this);
+ return iter;
+ }
+ Iterator& operator--() {
+ p_ -= stride_;
+ return *this;
+ }
+ Iterator operator--(int) {
+ Iterator iter = *this;
+ --(*this);
+ return iter;
+ }
+ bool operator==(const Iterator& other) const { return p_ == other.p_; }
+ bool operator!=(const Iterator& other) const { return !(*this == other); }
+ T& operator*() const { return *p_; }
+
+ private:
+ T* p_;
+ size_t stride_;
+ };
+
+ public:
+ using ReverseIterator = std::reverse_iterator<Iterator>;
+
+ // These types are used internally by Google Test.
+ using const_iterator = Iterator;
+ using iterator = Iterator;
+
+ TestVector() = default;
+ TestVector(T* base, const MemoryLayout* memory_layout, size_t size)
+ : p_(GetAligned(base, memory_layout->byte_alignment)),
+ memory_layout_(memory_layout),
+ size_(size) {}
+ TestVector(T* base, const TestVector<const T>& primary_vector)
+ : TestVector(base,
+ primary_vector.memory_layout(),
+ primary_vector.size()) {}
+
+ Iterator begin() const { return Iterator(p_, stride()); }
+ Iterator end() const { return Iterator(p_ + size() * stride(), stride()); }
+ ReverseIterator rbegin() const { return ReverseIterator(end()); }
+ ReverseIterator rend() const { return ReverseIterator(begin()); }
+ const MemoryLayout* memory_layout() const { return memory_layout_; }
+ T* p() const { return p_; }
+ size_t size() const { return size_; }
+ int stride() const { return static_cast<int>(memory_layout()->stride); }
+
+ bool operator==(const TestVector& other) const {
+ return std::equal(begin(), end(), other.begin(), other.end(), Equal);
+ }
+ T& operator[](size_t i) const { return p_[i * stride()]; }
+
+ private:
+ static T* GetAligned(T* base, size_t byte_alignment) {
+ size_t base_byte_alignment = GetByteAlignment(base);
+ size_t byte_offset =
+ (byte_alignment - base_byte_alignment + kMaxByteAlignment) %
+ kMaxByteAlignment;
+ T* p = base + byte_offset / sizeof(T);
+ size_t p_byte_alignment = GetByteAlignment(p);
+ CHECK_EQ(byte_alignment % kMaxByteAlignment, p_byte_alignment);
+ return p;
+ }
+ static size_t GetByteAlignment(T* p) {
+ return reinterpret_cast<size_t>(p) % kMaxByteAlignment;
+ }
+
+ T* p_;
+ const MemoryLayout* memory_layout_;
+ size_t size_;
+};
+
+// Get primary input vectors with difference memory layout and size
+// combinations.
+template <typename T>
+std::array<TestVector<const T>, kVectorSizeCount * kMemoryLayoutCount>
+GetPrimaryVectors(const T* base) {
+ std::array<TestVector<const T>, kVectorSizeCount * kMemoryLayoutCount>
+ vectors;
+ for (auto& vector : vectors) {
+ ptrdiff_t i = &vector - &vectors[0];
+ ptrdiff_t memory_layout_index = i % kMemoryLayoutCount;
+ ptrdiff_t size_index = i / kMemoryLayoutCount;
+ vector = TestVector<const T>(base, &kMemoryLayouts[memory_layout_index],
+ kVectorSizesInBytes[size_index] / sizeof(T));
+ }
+ return vectors;
+}
+
+// Get secondary input or output vectors. As the size of a secondary vector
+// must always be the same as the size of the primary input vector, there are
+// only two interesting secondary vectors:
+// - A vector with the same memory layout as the primary input vector has and
+// which therefore is aligned whenever the primary input vector is aligned.
+// - A vector with a different memory layout than the primary input vector has
+// and which therefore is not aligned when the primary input vector is
+// aligned.
+template <typename T>
+std::array<TestVector<T>, 2u> GetSecondaryVectors(
+ T* base,
+ const MemoryLayout* primary_memory_layout,
+ size_t size) {
+ std::array<TestVector<T>, 2u> vectors;
+ const MemoryLayout* other_memory_layout =
+ &kMemoryLayouts[primary_memory_layout == &kMemoryLayouts[0]];
+ CHECK_NE(primary_memory_layout, other_memory_layout);
+ CHECK_NE(primary_memory_layout->byte_alignment,
+ other_memory_layout->byte_alignment);
+ vectors[0] = TestVector<T>(base, primary_memory_layout, size);
+ vectors[1] = TestVector<T>(base, other_memory_layout, size);
+ return vectors;
+}
+
+template <typename T>
+std::array<TestVector<T>, 2u> GetSecondaryVectors(
+ T* base,
+ const TestVector<const float>& primary_vector) {
+ return GetSecondaryVectors(base, primary_vector.memory_layout(),
+ primary_vector.size());
+}
+
+class VectorMathTest : public testing::Test {
+ protected:
+ enum {
+ kDestinationCount = 4u,
+ kFloatArraySize =
+ (kMaxStride * kMaxVectorSizeInBytes + kMaxByteAlignment - 1u) /
+ sizeof(float),
+ kFullyFiniteSource = 4u,
+ kFullyFiniteSource2 = 5u,
+ kFullyNonNanSource = 6u,
+ kSourceCount = 7u
+ };
+
+ // Get a destination buffer containing initially uninitialized floats.
+ float* GetDestination(size_t i) {
+ CHECK_LT(i, static_cast<size_t>(kDestinationCount));
+ return destinations_[i];
+ }
+ // Get a source buffer containing random floats.
+ const float* GetSource(size_t i) {
+ CHECK_LT(i, static_cast<size_t>(kSourceCount));
+ return sources_[i];
+ }
+
+ static void SetUpTestCase() {
+ std::minstd_rand generator(3141592653u);
+ // Fill in source buffers with finite random floats.
+ std::uniform_real_distribution<float> float_distribution(-10.0f, 10.0f);
+ std::generate_n(&**sources_, sizeof(sources_) / sizeof(**sources_),
+ [&]() { return float_distribution(generator); });
+ // Add INFINITYs and NANs to most source buffers.
+ std::uniform_int_distribution<size_t> index_distribution(
+ 0u, kFloatArraySize / 2u - 1u);
+ for (size_t i = 0u; i < kSourceCount; ++i) {
+ if (i == kFullyFiniteSource || i == kFullyFiniteSource2)
+ continue;
+ sources_[i][index_distribution(generator)] = INFINITY;
+ sources_[i][index_distribution(generator)] = -INFINITY;
+ if (i != kFullyNonNanSource)
+ sources_[i][index_distribution(generator)] = NAN;
+ }
+ }
+
+ private:
+ static float destinations_[kDestinationCount][kFloatArraySize];
+ static float sources_[kSourceCount][kFloatArraySize];
+};
+
+float VectorMathTest::destinations_[kDestinationCount][kFloatArraySize];
+float VectorMathTest::sources_[kSourceCount][kFloatArraySize];
+
+TEST_F(VectorMathTest, Conv) {
+ for (const auto& source : GetPrimaryVectors(GetSource(kFullyFiniteSource))) {
+ if (source.stride() != 1)
+ continue;
+ for (size_t filter_size : {3u, 32u, 64u, 128u}) {
+ // The maximum number of frames which could be processed here is
+ // |source.size() - filter_size + 1|. However, in order to test
+ // optimization paths, |frames_to_process| should be optimal (divisible
+ // by a power of 2) whenever |filter_size| is optimal. Therefore, let's
+ // process only |source.size() - filter_size| frames here.
+ if (filter_size >= source.size())
+ break;
+ size_t frames_to_process = source.size() - filter_size;
+ // The stride of a convolution filter must be -1. Let's first create
+ // a reversed filter whose stride is 1.
+ TestVector<const float> reversed_filter(
+ GetSource(kFullyFiniteSource2), source.memory_layout(), filter_size);
+ // The filter begins from the reverse beginning of the reversed filter
+ // and grows downwards.
+ const float* filter_p = &*reversed_filter.rbegin();
+ TestVector<float> expected_dest(
+ GetDestination(0u), source.memory_layout(), frames_to_process);
+ for (size_t i = 0u; i < frames_to_process; ++i) {
+ expected_dest[i] = 0u;
+ for (size_t j = 0u; j < filter_size; ++j)
+ expected_dest[i] += source[i + j] * *(filter_p - j);
+ }
+ for (auto& dest : GetSecondaryVectors(
+ GetDestination(1u), source.memory_layout(), frames_to_process)) {
+ AudioFloatArray prepared_filter;
+ PrepareFilterForConv(filter_p, -1, filter_size, &prepared_filter);
+ Conv(source.p(), 1, filter_p, -1, dest.p(), 1, frames_to_process,
+ filter_size, &prepared_filter);
+ for (size_t i = 0u; i < frames_to_process; ++i) {
+ EXPECT_NEAR(expected_dest[i], dest[i],
+ 1e-3 * std::abs(expected_dest[i]));
+ }
+ }
+ }
+ }
+}
+
+TEST_F(VectorMathTest, Vadd) {
+ for (const auto& source1 : GetPrimaryVectors(GetSource(0u))) {
+ for (const auto& source2 : GetSecondaryVectors(GetSource(1u), source1)) {
+ TestVector<float> expected_dest(GetDestination(0u), source1);
+ for (size_t i = 0u; i < source1.size(); ++i)
+ expected_dest[i] = source1[i] + source2[i];
+ for (auto& dest : GetSecondaryVectors(GetDestination(1u), source1)) {
+ Vadd(source1.p(), source1.stride(), source2.p(), source2.stride(),
+ dest.p(), dest.stride(), source1.size());
+ EXPECT_EQ(expected_dest, dest);
+ }
+ }
+ }
+}
+
+TEST_F(VectorMathTest, Vclip) {
+ // Vclip does not accept NaNs thus let's use only sources without NaNs.
+ for (const auto& source : GetPrimaryVectors(GetSource(kFullyNonNanSource))) {
+ const float* thresholds = GetSource(kFullyFiniteSource);
+ const float low_threshold = std::min(thresholds[0], thresholds[1]);
+ const float high_threshold = std::max(thresholds[0], thresholds[1]);
+ TestVector<float> expected_dest(GetDestination(0u), source);
+ for (size_t i = 0u; i < source.size(); ++i)
+ expected_dest[i] = clampTo(source[i], low_threshold, high_threshold);
+ for (auto& dest : GetSecondaryVectors(GetDestination(1u), source)) {
+ Vclip(source.p(), source.stride(), &low_threshold, &high_threshold,
+ dest.p(), dest.stride(), source.size());
+ EXPECT_EQ(expected_dest, dest);
+ }
+ }
+}
+
+TEST_F(VectorMathTest, Vmaxmgv) {
+ const auto maxmg = [](float init, float x) {
+ return std::max(init, std::abs(x));
+ };
+ // Vmaxmgv does not accept NaNs thus let's use only sources without NaNs.
+ for (const float* source_base :
+ {GetSource(kFullyFiniteSource), GetSource(kFullyNonNanSource)}) {
+ for (const auto& source : GetPrimaryVectors(source_base)) {
+ const float expected_max =
+ std::accumulate(source.begin(), source.end(), 0.0f, maxmg);
+ float max;
+ Vmaxmgv(source.p(), source.stride(), &max, source.size());
+ EXPECT_EQ(expected_max, max) << testing::PrintToString(source);
+ }
+ }
+}
+
+TEST_F(VectorMathTest, Vmul) {
+ for (const auto& source1 : GetPrimaryVectors(GetSource(0u))) {
+ for (const auto& source2 : GetSecondaryVectors(GetSource(1u), source1)) {
+ TestVector<float> expected_dest(GetDestination(0u), source1);
+ for (size_t i = 0u; i < source1.size(); ++i)
+ expected_dest[i] = source1[i] * source2[i];
+ for (auto& dest : GetSecondaryVectors(GetDestination(1u), source1)) {
+ Vmul(source1.p(), source1.stride(), source2.p(), source2.stride(),
+ dest.p(), dest.stride(), source1.size());
+ EXPECT_EQ(expected_dest, dest);
+ }
+ }
+ }
+}
+
+TEST_F(VectorMathTest, Vsma) {
+ for (const auto& source : GetPrimaryVectors(GetSource(0u))) {
+ const float scale = *GetSource(1u);
+ const TestVector<const float> dest_source(GetSource(2u), source);
+ TestVector<float> expected_dest(GetDestination(0u), source);
+ for (size_t i = 0u; i < source.size(); ++i)
+ expected_dest[i] = dest_source[i] + scale * source[i];
+ for (auto& dest : GetSecondaryVectors(GetDestination(1u), source)) {
+ std::copy(dest_source.begin(), dest_source.end(), dest.begin());
+ Vsma(source.p(), source.stride(), &scale, dest.p(), dest.stride(),
+ source.size());
+ // Different optimizations may use different precisions for intermediate
+ // results which may result in different rounding errors thus let's
+ // expect only mostly equal floats.
+ for (size_t i = 0u; i < source.size(); ++i) {
+ if (std::isfinite(expected_dest[i])) {
+#if defined(OS_MACOSX)
+ // On Mac, OS provided vectorized functions are used which may result
+ // in bigger rounding errors than functions used on other OSes.
+ EXPECT_NEAR(expected_dest[i], dest[i],
+ 1e-5 * std::abs(expected_dest[i]));
+#else
+ EXPECT_FLOAT_EQ(expected_dest[i], dest[i]);
+#endif
+ } else {
+ EXPECT_PRED2(Equal, expected_dest[i], dest[i]);
+ }
+ }
+ }
+ }
+}
+
+TEST_F(VectorMathTest, Vsmul) {
+ for (const auto& source : GetPrimaryVectors(GetSource(0u))) {
+ const float scale = *GetSource(1u);
+ TestVector<float> expected_dest(GetDestination(0u), source);
+ for (size_t i = 0u; i < source.size(); ++i)
+ expected_dest[i] = scale * source[i];
+ for (auto& dest : GetSecondaryVectors(GetDestination(1u), source)) {
+ Vsmul(source.p(), source.stride(), &scale, dest.p(), dest.stride(),
+ source.size());
+ EXPECT_EQ(expected_dest, dest);
+ }
+ }
+}
+
+TEST_F(VectorMathTest, Vsvesq) {
+ const auto sqsum = [](float init, float x) { return init + x * x; };
+ for (const float* source_base :
+ {GetSource(0u), GetSource(kFullyFiniteSource)}) {
+ for (const auto& source : GetPrimaryVectors(source_base)) {
+ const float expected_sum =
+ std::accumulate(source.begin(), source.end(), 0.0f, sqsum);
+ float sum;
+ Vsvesq(source.p(), source.stride(), &sum, source.size());
+ if (std::isfinite(expected_sum)) {
+ // Optimized paths in Vsvesq use parallel partial sums which may result
+ // in different rounding errors than the non-partial sum algorithm used
+ // here and in non-optimized paths in Vsvesq.
+ EXPECT_FLOAT_EQ(expected_sum, sum);
+ } else {
+ EXPECT_PRED2(Equal, expected_sum, sum);
+ }
+ }
+ }
+}
+
+TEST_F(VectorMathTest, Zvmul) {
+ constexpr float kMax = std::numeric_limits<float>::max();
+ std::vector<std::array<float, kFloatArraySize + 1u>> sources(4u);
+ for (size_t i = 0u; i < sources.size(); ++i) {
+ // Initialize a local source with a randomized test case source.
+ std::copy_n(GetSource(i), kFloatArraySize, sources[i].begin());
+ // Put +FLT_MAX and -FLT_MAX in the middle of the source. Use a different
+ // sequence for each source in order to get 16 different combinations.
+ for (size_t j = 0u; j < 16u; ++j)
+ sources[i][kFloatArraySize / 2u + j] = ((j >> i) & 1) ? -kMax : kMax;
+ }
+ for (const auto& real1 : GetPrimaryVectors(sources[0u].data())) {
+ if (real1.stride() != 1)
+ continue;
+ const TestVector<const float> imag1(sources[1u].data(), real1);
+ const TestVector<const float> real2(sources[2u].data(), real1);
+ const TestVector<const float> imag2(sources[3u].data(), real1);
+ TestVector<float> expected_dest_real(GetDestination(0u), real1);
+ TestVector<float> expected_dest_imag(GetDestination(1u), real1);
+ for (size_t i = 0u; i < real1.size(); ++i) {
+ expected_dest_real[i] = real1[i] * real2[i] - imag1[i] * imag2[i];
+ expected_dest_imag[i] = real1[i] * imag2[i] + imag1[i] * real2[i];
+ if (&real1[i] >= &sources[0u][kFloatArraySize / 2u] &&
+ &real1[i] < &sources[0u][kFloatArraySize / 2u] + 16u) {
+ // FLT_MAX products should have overflowed.
+ EXPECT_TRUE(std::isinf(expected_dest_real[i]) ||
+ std::isinf(expected_dest_imag[i]));
+ EXPECT_TRUE(std::isnan(expected_dest_real[i]) ||
+ std::isnan(expected_dest_imag[i]));
+ }
+ }
+ for (auto& dest_real : GetSecondaryVectors(GetDestination(2u), real1)) {
+ TestVector<float> dest_imag(GetDestination(3u), real1);
+ ASSERT_EQ(1, dest_real.stride());
+ Zvmul(real1.p(), imag1.p(), real2.p(), imag2.p(), dest_real.p(),
+ dest_imag.p(), real1.size());
+ // Different optimizations may use different precisions for intermediate
+ // results which may result in different rounding errors thus let's
+ // expect only mostly equal floats.
+ for (size_t i = 0u; i < real1.size(); ++i) {
+ if (std::isfinite(expected_dest_real[i])) {
+#if defined(OS_MACOSX)
+ // On Mac, OS provided vectorized functions are used which may result
+ // in bigger rounding errors than functions used on other OSes.
+ EXPECT_NEAR(expected_dest_real[i], dest_real[i],
+ 1e-5 * std::abs(expected_dest_real[i]));
+#else
+ EXPECT_FLOAT_EQ(expected_dest_real[i], dest_real[i]);
+#endif
+ } else {
+#if defined(OS_MACOSX)
+ // On Mac, OS provided vectorized functions are used which may result
+ // in different NaN handling than functions used on other OSes.
+ EXPECT_TRUE(!std::isfinite(dest_real[i]));
+#else
+ EXPECT_PRED2(Equal, expected_dest_real[i], dest_real[i]);
+#endif
+ }
+ if (std::isfinite(expected_dest_imag[i])) {
+#if defined(OS_MACOSX)
+ // On Mac, OS provided vectorized functions are used which may result
+ // in bigger rounding errors than functions used on other OSes.
+ EXPECT_NEAR(expected_dest_imag[i], dest_imag[i],
+ 1e-5 * std::abs(expected_dest_imag[i]));
+#else
+ EXPECT_FLOAT_EQ(expected_dest_imag[i], dest_imag[i]);
+#endif
+ } else {
+#if defined(OS_MACOSX)
+ // On Mac, OS provided vectorized functions are used which may result
+ // in different NaN handling than functions used on other OSes.
+ EXPECT_TRUE(!std::isfinite(dest_imag[i]));
+#else
+ EXPECT_PRED2(Equal, expected_dest_imag[i], dest_imag[i]);
+#endif
+ }
+ }
+ }
+ }
+}
+
+} // namespace
+} // namespace VectorMath
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/bindings/DEPS b/chromium/third_party/blink/renderer/platform/bindings/DEPS
new file mode 100644
index 00000000000..8e2d58bf4bf
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+gin/public"
+]
diff --git a/chromium/third_party/blink/renderer/platform/bindings/OWNERS b/chromium/third_party/blink/renderer/platform/bindings/OWNERS
new file mode 100644
index 00000000000..750dcc6a414
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/OWNERS
@@ -0,0 +1,5 @@
+# Bindings OWNERS
+file://third_party/blink/renderer/bindings/OWNERS
+
+# TEAM: blink-reviews-bindings@chromium.org
+# COMPONENT: Blink>Bindings
diff --git a/chromium/third_party/blink/renderer/platform/bindings/README.md b/chromium/third_party/blink/renderer/platform/bindings/README.md
new file mode 100644
index 00000000000..e7d530b5126
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/README.md
@@ -0,0 +1,10 @@
+# Bindings
+
+This directory contains classes and functionality used to implement the V8 bindings layer in Blink. Any reusable bindings components/infrastructure that are independent of `core/` objects (or can be generalized to be independent) should be added to this directory, otherwise they can be kept in `bindings/core/`.
+
+Some of the things you can find here are:
+
+* Functionality to wrap Blink C++ objects with a JavaScript object and maintain wrappers in multiple worlds (see [ScriptWrappable](ScriptWrappable.h), [ActiveScriptWrappable](ActiveScriptWrappable.h))
+* Implementation of wrapper tracing (see [documentation](TraceWrapperReference.md))
+* Important abstractions for script execution (see [ScriptState](ScriptState.h), [V8PerIsolateData](V8PerIsolateData.h), [V8PerContextData](V8PerContextData.h))
+* Utility functions to interface with V8 and convert between V8 and Blink types (see [V8Binding.h](V8Binding.h), [ToV8.h](ToV8.h))
diff --git a/chromium/third_party/blink/renderer/platform/bindings/RuntimeCallStats.md b/chromium/third_party/blink/renderer/platform/bindings/RuntimeCallStats.md
new file mode 100644
index 00000000000..28e54316af7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/RuntimeCallStats.md
@@ -0,0 +1,23 @@
+# Runtime Call Stats
+
+## About
+
+RuntimeCallStats is a group of counters used to track execution times and call counts of functions in Blink during JS Execution. V8 has its own corresponding implementation of RuntimeCallStats, which is closely mirrored by Blink. Blink's implementation can be found in [RuntimeCallStats.h](RuntimeCallStats.h) and [RuntimeCallStats.cpp](RuntimeCallStats.cpp).
+
+## Usage
+
+Counters can be added by adding a name under one of the categories listed under FOR_EACH_COUNTER in RuntimeCallStats.h and by using the RUNTIME_CALL_TIMER_SCOPE, RUNTIME_CALL_STATS_ENTER and RUNTIME_CALL_STATS_LEAVE macros. See documentation in [RuntimeCallStats.h](RuntimeCallStats.h) for more details.
+
+Counters can also be directly added to the bindings layer in method and attribute callbacks by using the `[RuntimeCallStatsCounter]` IDL extended attribute (see [IDLExtendedAttributes.md](../../bindings/IDLExtendedAttributes.md#RuntimeCallStatsCounter_m_a) for more details).
+
+## Viewing Results
+
+Results can be seen through [chrome tracing](https://www.chromium.org/developers/how-tos/trace-event-profiling-tool). Run chrome with `--enable-blink-features=BlinkRuntimeCallStats`, and record a trace. Be sure to enable the `v8` and `v8.runtime_stats` (which is disabled by default) categories under 'Manually select settings'. After recording a trace, select the events recorded (for the website being analyzed) and click on 'Runtime call stats table'. The Blink runtime call stats should be visible below the V8 call stats table.
+
+Alternatively, running chrome as follows `chrome --enable-blink-features=BlinkRuntimeCallStats --dump-blink-runtime-call-stats --single-process` will dump call stats to the logs when the browser is closed. Adding `--enable-logging=stderr` will display log output in stderr.
+
+## RCS_COUNT_EVERYTHING
+
+Adding `rcs_count_everything = true` to a gn args file creates a special build where counters are added in the bindings layer to most Blink callbacks that are called by V8. This gives a more thorough breakdown of where time is spent executing Blink C++ during JS Execution.
+
+It is disabled by default (and behind a compile time flag) as it adds a large number of counters (> 2000) which causes a significant increase in binary size. There is also a performance hit when RCS is enabled with this build due to the large number of counters and counters added to some very trivial getters and setters.
diff --git a/chromium/third_party/blink/renderer/platform/bindings/TraceWrapperReference.md b/chromium/third_party/blink/renderer/platform/bindings/TraceWrapperReference.md
new file mode 100644
index 00000000000..fa32b874f7f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/TraceWrapperReference.md
@@ -0,0 +1,265 @@
+# Wrapper Tracing Reference
+
+This document describes wrapper tracing and how its API is supposed to be used.
+
+[TOC]
+
+## Quickstart guide
+
+Wrapper tracing is used to represent reachability across V8 and Blink. The
+following checklist highlights the modifications needed to make a class
+participate in wrapper tracing.
+
+1. Make sure that objects participating in tracing either inherit from
+`ScriptWrappable` (if they can reference wrappers) or `TraceWrapperBase`
+(transitively holding wrappers alive).
+2. Use `TraceWrapperMember<T>` to annotate fields that need to be followed to
+find other wrappers that this object should keep alive.
+3. Use `TraceWrapperV8Reference<T>` to annotate references to V8 that this
+object should keep alive.
+4. Declare a `virtual void TraceWrappers(const ScriptWrappableVisitor*) const`
+method to trace other wrappers.
+5. Define the method and trace all fields that received a wrapper tracing type
+in (1) and (2) using `visitor->TraceWrappers(<field_>)` in the body.
+
+The following example illustrates these steps:
+
+```c++
+#include "platform/bindings/ScriptWrappable.h"
+#include "platform/bindings/TraceWrapperMember.h"
+#include "platform/bindings/TraceWrapperV8Reference.h"
+
+class SomeDOMObject : public ScriptWrappable { // (1)
+ DEFINE_WRAPPERTYPEINFO();
+
+ public:
+ virtual void TraceWrappers(
+ const ScriptWrappableVisitor*) const; // (4)
+
+ private:
+ TraceWrapperMember<OtherWrappable> other_wrappable_; // (2)
+ Member<NonWrappable> non_wrappable_;
+ TraceWrapperV8Reference<v8::Value> v8object_; // (3)
+ // ...
+};
+
+void SomeDOMObject::TraceWrappers(
+ const ScriptWrappableVisitor* visitor) const { // (5)
+ visitor->TraceWrappers(other_wrappable_); // (5)
+ visitor->TraceWrappers(v8object_); // (5)
+}
+```
+
+For more in-depth information and how to deal with corner cases continue on reading.
+
+## Background
+
+Blink and V8 need to cooperate to collect JavaScript *wrappers*. Each V8
+*wrapper* object (*W*) in JavaScript is assigned a C++ *DOM object* (*D*) in
+Blink. A single C++ *DOM object* can hold onto one or many *wrapper* objects.
+During a garbage collection initiated from JavaScript, a *wrapper* then keeps
+the C++ *DOM object* and all its transitive dependencies, including other
+*wrappers*, alive, effectively tracing paths like
+*W<sub>x</sub> -> D<sub>1</sub> -> ⋯ -> D<sub>n</sub> -> W<sub>y</sub>*.
+
+Previously, this relationship was expressed using so-called object groups,
+effectively assigning all C++ *DOM objects* in a given DOM tree the same group.
+The group would only die if all objects belonging to the same group die. A brief
+introduction on the limitations on this approach can be found in
+[this slide deck][object-grouping-slides].
+
+Wrapper tracing solves this problem by determining liveness based on
+reachability by tracing through the C++ *DOM objects*. The rest of this document
+describes how the API is used to keep JavaScript wrapper objects alive.
+
+Note that *wrappables* have to be *on-heap objects* and thus all
+[Oilpan-related rules][oilpan-docs] apply.
+
+[object-grouping-slides]: https://docs.google.com/presentation/d/1I6leiRm0ysSTqy7QWh33Gfp7_y4ngygyM2tDAqdF0fI/
+[oilpan-docs]: https://chromium.googlesource.com/chromium/src/+/master/third_party/blink/renderer/platform/heap/BlinkGCAPIReference.md
+
+## Basic usage
+
+The annotations that are required can be found in the following header files.
+Pick the header file depending on what types are needed.
+
+```c++
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h"
+```
+
+The following example will guide through the modifications that are needed to
+adjust a given class `SomeDOMObject` to participate in wrapper tracing.
+
+```c++
+class SomeDOMObject : public ScriptWrappable {
+ DEFINE_WRAPPERTYPEINFO();
+
+ // ...
+ private:
+ Member<OtherWrappable /* extending ScriptWrappable */> other_wrappable_;
+ Member<NonWrappable> non_wrappable_;
+};
+```
+
+In this scenario `SomeDOMObject` is the object that is wrapped by an object on
+the JavaScript side. The next step is to identify the paths that lead to other
+wrappables. In this case, only `other_wrappable_` needs to be traced to find
+other *wrappers* in V8.
+
+```c++
+class SomeDOMObject : public ScriptWrappable {
+ DEFINE_WRAPPERTYPEINFO();
+
+ public:
+ virtual void TraceWrappers(const ScriptWrappableVisitor*) const;
+
+ private:
+ Member<OtherWrappable> other_wrappable_;
+ Member<NonWrappable> non_wrappable_;
+};
+
+void SomeDOMObject::TraceWrappers(
+ const ScriptWrappableVisitor* visitor) const {
+ visitor->TraceWrappers(other_wrappable_);
+}
+```
+
+
+Blink and V8 implement *incremental* wrapper tracing, which means that marking
+can be interleaved with JavaScript or even DOM operations. This poses a
+challenge, because already marked objects will not be considered again if they
+are reached through some other path.
+
+For example, consider an object `A` that has already been marked and a write
+to a field `A.x` setting `x` to an unmarked object `Y`. Since `A.x` is
+the only reference keeping `Y`, and `A` has already been marked, the garbage
+collector will not find `Y` and reclaim it.
+
+To overcome this problem we require all writes of interesting objects, i.e.,
+writes to traced fields, to go through a write barrier.
+The write barrier will check for the problem case above and make sure
+`Y` will be marked. In order to automatically issue a write barrier
+`other_wrappable_` needs `TraceWrapperMember` type.
+
+```c++
+class SomeDOMObject : public ScriptWrappable {
+ DEFINE_WRAPPERTYPEINFO();
+
+ public:
+ virtual void TraceWrappers(const ScriptWrappableVisitor*) const;
+
+ private:
+ TraceWrapperMember<OtherWrappable> other_wrappable_;
+ Member<NonWrappable> non_wrappable_;
+};
+
+void SomeDOMObject::TraceWrappers(
+ const ScriptWrappableVisitor* visitor) const {
+ visitor->TraceWrappers(other_wrappable_);
+}
+```
+
+`TraceWrapperMember` makes sure that any write to `other_wrappable_` will
+consider doing a write barrier. Using the proper type, the write barrier is
+correct by construction, i.e., it will never be missed.
+
+## Heap collections
+
+The proper type usage for collections, e.g. `HeapVector` looks like the
+following.
+
+```c++
+class SomeDOMObject : public ScriptWrappable {
+ DEFINE_WRAPPERTYPEINFO();
+
+ public:
+ // ...
+ void AppendNewValue(OtherWrappable* newValue);
+ virtual void TraceWrappers(const ScriptWrappableVisitor*) const;
+
+ private:
+ HeapVector<TraceWrapperMember<OtherWrappable>> other_wrappables_;
+};
+
+void SomeDOMObject::TraceWrappers(
+ const ScriptWrappableVisitor* visitor) const {
+ for (auto other : other_wrappables_)
+ visitor->TraceWrappers(other);
+}
+```
+
+Note that this is different to Oilpan which can just trace the whole collection.
+`TraceWrapperMember` can be constructed in place, so using `append` and
+friends will work out of the box, e.g.
+
+```c++
+void SomeDOMObject::AppendNewValue(OtherWrappable* newValue) {
+ other_wrappables_.append(newValue);
+}
+```
+
+The compiler will throw an error for each omitted `TraceWrapperMember`
+construction.
+
+### Swapping `HeapVector` containing `TraceWrapperMember` and `Member`
+
+It is possible to swap two `HeapVectors` containing `TraceWrapperMember` and
+`Member` by using `blink::swap`. The underlying swap will avoid copies and
+write barriers if possible.
+
+```c++
+// Swap two wrapper traced heap vectors.
+HeapVector<TraceWrapperMember<Wrappable>> a;
+HeapVector<TraceWrapperMember<Wrappable>> b;
+blink::swap(a, b);
+
+// Swap in a non-traced heap vector into a wrapper traced one.
+HeapVector<TraceWrapperMember<Wrappable>> c;
+HeapVector<Member<Wrappable>> temporary;
+blink::swap(c, temporary);
+```
+
+## Tracing through non-`ScriptWrappable` types
+
+Sometimes it is necessary to trace through types that do not inherit from
+`ScriptWrappable`. For example, consider the object graph
+`A -> B -> C` where both `A` and `C` are `ScriptWrappable`s that
+need to be traced.
+
+In this case, the same rules as with `ScriptWrappables` apply, except for the
+difference that these classes need to inherit from `TraceWrapperBase`.
+
+### Memory-footprint critical uses
+
+In the case we cannot afford inheriting from `TraceWrapperBase`, which will
+add a vtable pointer for tracing wrappers, use
+`DEFINE_TRAIT_FOR_TRACE_WRAPPERS(ClassName)` after defining
+`ClassName` to define the proper tracing specializations.
+
+## Explicit write barriers
+
+Sometimes it is necessary to stick with the regular types and issue the write
+barriers explicitly. In this case, tracing needs to be adjusted to tell the
+system that all barriers will be done manually.
+
+```c++
+class ManualWrappable : public ScriptWrappable {
+ DEFINE_WRAPPERTYPEINFO();
+
+ public:
+ void setNew(OtherWrappable* newValue) {
+ other_wrappable_ = newValue;
+ SriptWrappableVisitor::WriteBarrier(other_wrappable_);
+ }
+
+ virtual void TraceWrappers(const ScriptWrappableVisitor*) const;
+ private:
+ Member<OtherWrappable>> other_wrappable_;
+};
+
+void ManualWrappable::TraceWrappers(
+ const ScriptWrappableVisitor* visitor) const {
+ visitor->TraceWrappersWithManualWriteBarrier(other_wrappable_);
+}
+```
diff --git a/chromium/third_party/blink/renderer/platform/bindings/active_script_wrappable_base.cc b/chromium/third_party/blink/renderer/platform/bindings/active_script_wrappable_base.cc
new file mode 100644
index 00000000000..5fa35eb59b3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/active_script_wrappable_base.cc
@@ -0,0 +1,60 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/bindings/active_script_wrappable_base.h"
+
+#include "third_party/blink/renderer/platform/bindings/dom_data_store.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable_visitor.h"
+#include "third_party/blink/renderer/platform/bindings/v8_binding.h"
+#include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
+
+namespace blink {
+
+void ActiveScriptWrappableBase::TraceActiveScriptWrappables(
+ v8::Isolate* isolate,
+ ScriptWrappableVisitor* visitor) {
+ V8PerIsolateData* isolate_data = V8PerIsolateData::From(isolate);
+ const auto* active_script_wrappables = isolate_data->ActiveScriptWrappables();
+ if (!active_script_wrappables)
+ return;
+
+ for (auto active_wrappable : *active_script_wrappables) {
+ if (!active_wrappable->DispatchHasPendingActivity())
+ continue;
+
+ // A wrapper isn't kept alive after its ExecutionContext becomes
+ // detached, even if hasPendingActivity() returns |true|. This measure
+ // avoids memory leaks and has proven not to be too eager wrt
+ // garbage collection of objects belonging to discarded browser contexts
+ // ( https://html.spec.whatwg.org/#a-browsing-context-is-discarded )
+ //
+ // Consequently, an implementation of hasPendingActivity() is
+ // not required to take the detached state of the associated
+ // ExecutionContext into account (i.e., return |false|.) We probe
+ // the detached state of the ExecutionContext via
+ // |isContextDestroyed()|.
+ //
+ // TODO(haraken): Implement correct lifetime using traceWrapper.
+ if (active_wrappable->IsContextDestroyed())
+ continue;
+ ScriptWrappable* script_wrappable = active_wrappable->ToScriptWrappable();
+ // Notify the visitor about this script_wrappable by dispatching to the
+ // corresponding visitor->Visit(script_wrappable) method.
+ // Ideally, we would call visitor->TraceWrappers(script_wrappable) here,
+ // but that method requires TraceWrapperMember<T>. Since we are getting
+ // the script wrappable from ActiveScriptWrappables, we do not have
+ // TraceWrapperMember<T> and have to use TraceWrappersFromGeneratedCode.
+ visitor->TraceWrappersFromGeneratedCode(script_wrappable);
+ }
+}
+
+ActiveScriptWrappableBase::ActiveScriptWrappableBase() {
+ DCHECK(ThreadState::Current());
+ v8::Isolate* isolate = ThreadState::Current()->GetIsolate();
+ V8PerIsolateData* isolate_data = V8PerIsolateData::From(isolate);
+ isolate_data->AddActiveScriptWrappable(this);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/bindings/active_script_wrappable_base.h b/chromium/third_party/blink/renderer/platform/bindings/active_script_wrappable_base.h
new file mode 100644
index 00000000000..7c94f0ad8d4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/active_script_wrappable_base.h
@@ -0,0 +1,42 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_ACTIVE_SCRIPT_WRAPPABLE_BASE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_ACTIVE_SCRIPT_WRAPPABLE_BASE_H_
+
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+class ScriptWrappable;
+class ScriptWrappableVisitor;
+
+/**
+ * Classes deriving from ActiveScriptWrappable will be registered in a
+ * thread-specific list. They keep their wrappers and dependant objects alive
+ * as long as they have pending activity.
+ */
+class PLATFORM_EXPORT ActiveScriptWrappableBase : public GarbageCollectedMixin {
+ WTF_MAKE_NONCOPYABLE(ActiveScriptWrappableBase);
+
+ public:
+ static void TraceActiveScriptWrappables(v8::Isolate*,
+ ScriptWrappableVisitor*);
+
+ virtual ~ActiveScriptWrappableBase() = default;
+
+ protected:
+ ActiveScriptWrappableBase();
+
+ virtual bool IsContextDestroyed() const = 0;
+ virtual bool DispatchHasPendingActivity() const = 0;
+ virtual ScriptWrappable* ToScriptWrappable() = 0;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_ACTIVE_SCRIPT_WRAPPABLE_BASE_H_
diff --git a/chromium/third_party/blink/renderer/platform/bindings/callback_function_base.cc b/chromium/third_party/blink/renderer/platform/bindings/callback_function_base.cc
new file mode 100644
index 00000000000..265dbb5e9f1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/callback_function_base.cc
@@ -0,0 +1,37 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/bindings/callback_function_base.h"
+
+namespace blink {
+
+CallbackFunctionBase::CallbackFunctionBase(
+ v8::Local<v8::Function> callback_function) {
+ DCHECK(!callback_function.IsEmpty());
+
+ callback_relevant_script_state_ =
+ ScriptState::From(callback_function->CreationContext());
+ v8::Isolate* isolate = callback_relevant_script_state_->GetIsolate();
+
+ callback_function_.Set(isolate, callback_function);
+ incumbent_script_state_ = ScriptState::From(isolate->GetIncumbentContext());
+}
+
+void CallbackFunctionBase::TraceWrappers(
+ const ScriptWrappableVisitor* visitor) const {
+ visitor->TraceWrappers(callback_function_);
+}
+
+V8PersistentCallbackFunctionBase::V8PersistentCallbackFunctionBase(
+ CallbackFunctionBase* callback_function)
+ : callback_function_(callback_function) {
+ v8_function_.Reset(callback_function_->GetIsolate(),
+ callback_function_->callback_function_.Get());
+}
+
+void V8PersistentCallbackFunctionBase::Trace(blink::Visitor* visitor) {
+ visitor->Trace(callback_function_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/bindings/callback_function_base.h b/chromium/third_party/blink/renderer/platform/bindings/callback_function_base.h
new file mode 100644
index 00000000000..9d8103aa4ae
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/callback_function_base.h
@@ -0,0 +1,130 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_CALLBACK_FUNCTION_BASE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_CALLBACK_FUNCTION_BASE_H_
+
+#include "third_party/blink/renderer/platform/bindings/script_state.h"
+#include "third_party/blink/renderer/platform/bindings/trace_wrapper_base.h"
+#include "third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+
+namespace blink {
+
+class V8PersistentCallbackFunctionBase;
+
+// CallbackFunctionBase is the common base class of all the callback function
+// classes. Most importantly this class provides a way of type dispatching (e.g.
+// overload resolutions, SFINAE technique, etc.) so that it's possible to
+// distinguish callback functions from anything else. Also it provides a common
+// implementation of callback functions.
+//
+// As the signatures of callback functions vary, this class does not implement
+// |Invoke| member function that performs "invoke" steps. Subclasses will
+// implement it.
+class PLATFORM_EXPORT CallbackFunctionBase
+ : public GarbageCollectedFinalized<CallbackFunctionBase>,
+ public TraceWrapperBase {
+ public:
+ virtual ~CallbackFunctionBase() = default;
+
+ virtual void Trace(blink::Visitor* visitor) {}
+ void TraceWrappers(const ScriptWrappableVisitor*) const override;
+ const char* NameInHeapSnapshot() const override {
+ return "CallbackFunctionBase";
+ }
+
+ v8::Isolate* GetIsolate() const {
+ return callback_relevant_script_state_->GetIsolate();
+ }
+ ScriptState* CallbackRelevantScriptState() {
+ return callback_relevant_script_state_.get();
+ }
+
+ protected:
+ explicit CallbackFunctionBase(v8::Local<v8::Function>);
+
+ v8::Local<v8::Function> CallbackFunction() const {
+ return callback_function_.NewLocal(GetIsolate());
+ }
+ ScriptState* IncumbentScriptState() { return incumbent_script_state_.get(); }
+
+ private:
+ // The "callback function type" value.
+ TraceWrapperV8Reference<v8::Function> callback_function_;
+ // The associated Realm of the callback function type value.
+ scoped_refptr<ScriptState> callback_relevant_script_state_;
+ // The callback context, i.e. the incumbent Realm when an ECMAScript value is
+ // converted to an IDL value.
+ // https://heycam.github.io/webidl/#dfn-callback-context
+ scoped_refptr<ScriptState> incumbent_script_state_;
+
+ friend class V8PersistentCallbackFunctionBase;
+};
+
+// V8PersistentCallbackFunctionBase retains the underlying v8::Function of a
+// CallbackFunctionBase without wrapper-tracing. This class is necessary and
+// useful where wrapper-tracing is not suitable. Remember that, as a nature of
+// v8::Persistent, abuse of V8PersistentCallbackFunctionBase would result in
+// memory leak, so the use of V8PersistentCallbackFunctionBase should be limited
+// to those which are guaranteed to release the persistents in a finite time
+// period.
+class PLATFORM_EXPORT V8PersistentCallbackFunctionBase
+ : public GarbageCollectedFinalized<V8PersistentCallbackFunctionBase> {
+ public:
+ virtual ~V8PersistentCallbackFunctionBase() { v8_function_.Reset(); }
+
+ virtual void Trace(blink::Visitor*);
+
+ protected:
+ explicit V8PersistentCallbackFunctionBase(CallbackFunctionBase*);
+
+ template <typename V8CallbackFunction>
+ V8CallbackFunction* As() {
+ static_assert(
+ std::is_base_of<CallbackFunctionBase, V8CallbackFunction>::value,
+ "V8CallbackFunction must be a subclass of CallbackFunctionBase.");
+ return static_cast<V8CallbackFunction*>(callback_function_.Get());
+ }
+
+ private:
+ Member<CallbackFunctionBase> callback_function_;
+ v8::Persistent<v8::Function> v8_function_;
+};
+
+// V8PersistentCallbackFunction<V8CallbackFunction> is a counter-part of
+// V8CallbackFunction. While V8CallbackFunction uses wrapper-tracing,
+// V8PersistentCallbackFunction<V8CallbackFunction> uses v8::Persistent to make
+// the underlying v8::Function alive.
+//
+// Since the signature of |Invoke| varies depending on the IDL definition,
+// the class definition is specialized and generated by the bindings code
+// generator.
+template <typename V8CallbackFunction>
+class V8PersistentCallbackFunction;
+
+// Converts the wrapper-tracing version of a callback function to the
+// v8::Persistent version of it.
+template <typename V8CallbackFunction>
+inline V8PersistentCallbackFunction<V8CallbackFunction>*
+ToV8PersistentCallbackFunction(V8CallbackFunction* callback_function) {
+ static_assert(
+ std::is_base_of<CallbackFunctionBase, V8CallbackFunction>::value,
+ "V8CallbackFunction must be a subclass of CallbackFunctionBase.");
+ return callback_function
+ ? new V8PersistentCallbackFunction<V8CallbackFunction>(
+ callback_function)
+ : nullptr;
+}
+
+// CallbackFunctionBase is designed to be used with wrapper-tracing. As
+// blink::Persistent does not perform wrapper-tracing, use of |WrapPersistent|
+// for callback functions is likely (if not always) misuse. Thus, this code
+// prohibits such a use case. The call sites should explicitly use
+// WrapPersistent(V8PersistentCallbackFunction<T>*).
+Persistent<CallbackFunctionBase> WrapPersistent(CallbackFunctionBase*) = delete;
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_CALLBACK_FUNCTION_BASE_H_
diff --git a/chromium/third_party/blink/renderer/platform/bindings/callback_interface_base.cc b/chromium/third_party/blink/renderer/platform/bindings/callback_interface_base.cc
new file mode 100644
index 00000000000..c6d306c3a62
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/callback_interface_base.cc
@@ -0,0 +1,40 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/bindings/callback_interface_base.h"
+
+namespace blink {
+
+CallbackInterfaceBase::CallbackInterfaceBase(
+ v8::Local<v8::Object> callback_object,
+ SingleOperationOrNot single_op_or_not) {
+ DCHECK(!callback_object.IsEmpty());
+
+ callback_relevant_script_state_ =
+ ScriptState::From(callback_object->CreationContext());
+ v8::Isolate* isolate = callback_relevant_script_state_->GetIsolate();
+
+ callback_object_.Set(isolate, callback_object);
+ is_callback_object_callable_ =
+ (single_op_or_not == kSingleOperation) && callback_object->IsCallable();
+ incumbent_script_state_ = ScriptState::From(isolate->GetIncumbentContext());
+}
+
+void CallbackInterfaceBase::TraceWrappers(
+ const ScriptWrappableVisitor* visitor) const {
+ visitor->TraceWrappers(callback_object_);
+}
+
+V8PersistentCallbackInterfaceBase::V8PersistentCallbackInterfaceBase(
+ CallbackInterfaceBase* callback_interface)
+ : callback_interface_(callback_interface) {
+ v8_object_.Reset(callback_interface_->GetIsolate(),
+ callback_interface_->callback_object_.Get());
+}
+
+void V8PersistentCallbackInterfaceBase::Trace(blink::Visitor* visitor) {
+ visitor->Trace(callback_interface_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/bindings/callback_interface_base.h b/chromium/third_party/blink/renderer/platform/bindings/callback_interface_base.h
new file mode 100644
index 00000000000..5276381bb47
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/callback_interface_base.h
@@ -0,0 +1,147 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_CALLBACK_INTERFACE_BASE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_CALLBACK_INTERFACE_BASE_H_
+
+#include "third_party/blink/renderer/platform/bindings/script_state.h"
+#include "third_party/blink/renderer/platform/bindings/trace_wrapper_base.h"
+#include "third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+
+namespace blink {
+
+class V8PersistentCallbackInterfaceBase;
+
+// CallbackInterfaceBase is the common base class of all the callback interface
+// classes. Most importantly this class provides a way of type dispatching (e.g.
+// overload resolutions, SFINAE technique, etc.) so that it's possible to
+// distinguish callback interfaces from anything else. Also it provides a common
+// implementation of callback interfaces.
+//
+// As the signatures of callback interface's operations vary, this class does
+// not implement any operation. Subclasses will implement it.
+class PLATFORM_EXPORT CallbackInterfaceBase
+ : public GarbageCollectedFinalized<CallbackInterfaceBase>,
+ public TraceWrapperBase {
+ public:
+ // Whether the callback interface is a "single operation callback interface"
+ // or not.
+ // https://heycam.github.io/webidl/#dfn-single-operation-callback-interface
+ enum SingleOperationOrNot {
+ kNotSingleOperation,
+ kSingleOperation,
+ };
+
+ virtual ~CallbackInterfaceBase() = default;
+
+ virtual void Trace(blink::Visitor*) {}
+ void TraceWrappers(const ScriptWrappableVisitor*) const override;
+ const char* NameInHeapSnapshot() const override {
+ return "CallbackInterfaceBase";
+ }
+
+ v8::Isolate* GetIsolate() {
+ return callback_relevant_script_state_->GetIsolate();
+ }
+ ScriptState* CallbackRelevantScriptState() {
+ return callback_relevant_script_state_.get();
+ }
+
+ protected:
+ CallbackInterfaceBase(v8::Local<v8::Object> callback_object,
+ SingleOperationOrNot);
+
+ v8::Local<v8::Object> CallbackObject() {
+ return callback_object_.NewLocal(GetIsolate());
+ }
+ // Returns true iff the callback interface is a single operation callback
+ // interface and the callback interface type value is callable.
+ bool IsCallbackObjectCallable() const { return is_callback_object_callable_; }
+ ScriptState* IncumbentScriptState() { return incumbent_script_state_.get(); }
+
+ private:
+ // The "callback interface type" value.
+ TraceWrapperV8Reference<v8::Object> callback_object_;
+ bool is_callback_object_callable_ = false;
+ // The associated Realm of the callback interface type value. Note that the
+ // callback interface type value can be different from the function object
+ // to be invoked.
+ scoped_refptr<ScriptState> callback_relevant_script_state_;
+ // The callback context, i.e. the incumbent Realm when an ECMAScript value is
+ // converted to an IDL value.
+ // https://heycam.github.io/webidl/#dfn-callback-context
+ scoped_refptr<ScriptState> incumbent_script_state_;
+
+ friend class V8PersistentCallbackInterfaceBase;
+};
+
+// V8PersistentCallbackInterfaceBase retains the underlying v8::Object of a
+// CallbackInterfaceBase without wrapper-tracing. This class is necessary and
+// useful where wrapper-tracing is not suitable. Remember that, as a nature of
+// v8::Persistent, abuse of V8PersistentCallbackInterfaceBase would result in
+// memory leak, so the use of V8PersistentCallbackInterfaceBase should be
+// limited to those which are guaranteed to release the persistents in a finite
+// time period.
+class PLATFORM_EXPORT V8PersistentCallbackInterfaceBase
+ : public GarbageCollectedFinalized<V8PersistentCallbackInterfaceBase> {
+ public:
+ virtual ~V8PersistentCallbackInterfaceBase() { v8_object_.Reset(); }
+
+ virtual void Trace(blink::Visitor*);
+
+ v8::Isolate* GetIsolate() { return callback_interface_->GetIsolate(); }
+
+ protected:
+ explicit V8PersistentCallbackInterfaceBase(CallbackInterfaceBase*);
+
+ template <typename V8CallbackInterface>
+ V8CallbackInterface* As() {
+ static_assert(
+ std::is_base_of<CallbackInterfaceBase, V8CallbackInterface>::value,
+ "V8CallbackInterface must be a subclass of CallbackInterfaceBase.");
+ return static_cast<V8CallbackInterface*>(callback_interface_.Get());
+ }
+
+ private:
+ Member<CallbackInterfaceBase> callback_interface_;
+ v8::Persistent<v8::Object> v8_object_;
+};
+
+// V8PersistentCallbackInterface<V8CallbackInterface> is a counter-part of
+// V8CallbackInterface. While V8CallbackInterface uses wrapper-tracing,
+// V8PersistentCallbackInterface<V8CallbackInterface> uses v8::Persistent to
+// make the underlying v8::Object alive.
+//
+// Since the signatures of the operations vary depending on the IDL definition,
+// the class definition is specialized and generated by the bindings code
+// generator.
+template <typename V8CallbackInterface>
+class V8PersistentCallbackInterface;
+
+// Converts the wrapper-tracing version of a callback interface to the
+// v8::Persistent version of it.
+template <typename V8CallbackInterface>
+inline V8PersistentCallbackInterface<V8CallbackInterface>*
+ToV8PersistentCallbackInterface(V8CallbackInterface* callback_interface) {
+ static_assert(
+ std::is_base_of<CallbackInterfaceBase, V8CallbackInterface>::value,
+ "V8CallbackInterface must be a subclass of CallbackInterfaceBase.");
+ return callback_interface
+ ? new V8PersistentCallbackInterface<V8CallbackInterface>(
+ callback_interface)
+ : nullptr;
+}
+
+// CallbackInterfaceBase is designed to be used with wrapper-tracing. As
+// blink::Persistent does not perform wrapper-tracing, use of |WrapPersistent|
+// for callback interfaces is likely (if not always) misuse. Thus, this code
+// prohibits such a use case. The call sites should explicitly use
+// WrapPersistent(V8PersistentCallbackInterface<T>*).
+Persistent<CallbackInterfaceBase> WrapPersistent(CallbackInterfaceBase*) =
+ delete;
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_CALLBACK_INTERFACE_BASE_H_
diff --git a/chromium/third_party/blink/renderer/platform/bindings/dom_data_store.h b/chromium/third_party/blink/renderer/platform/bindings/dom_data_store.h
new file mode 100644
index 00000000000..33e30105185
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/dom_data_store.h
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_DOM_DATA_STORE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_DOM_DATA_STORE_H_
+
+#include <memory>
+
+#include "third_party/blink/renderer/platform/bindings/dom_wrapper_map.h"
+#include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable_marking_visitor.h"
+#include "third_party/blink/renderer/platform/bindings/wrapper_type_info.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+#include "third_party/blink/renderer/platform/wtf/stack_util.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+// Holds a DOMWrapperMap specialized to map between ScriptWrappable objects and
+// their wrappers and provides an API to perform common operations with this map
+// and manage wrappers in a single world. Each world (DOMWrapperWorld) holds a
+// single DOMWrapperMap instance to hold wrappers only for that world.
+class DOMDataStore {
+ WTF_MAKE_NONCOPYABLE(DOMDataStore);
+ USING_FAST_MALLOC(DOMDataStore);
+
+ public:
+ DOMDataStore(v8::Isolate* isolate, bool is_main_world)
+ : is_main_world_(is_main_world) {
+ // We never use |m_wrapperMap| when it's the main world.
+ if (!is_main_world)
+ wrapper_map_.emplace(isolate);
+ }
+
+ static DOMDataStore& Current(v8::Isolate* isolate) {
+ return DOMWrapperWorld::Current(isolate).DomDataStore();
+ }
+
+ static bool SetReturnValue(v8::ReturnValue<v8::Value> return_value,
+ ScriptWrappable* object) {
+ if (CanUseMainWorldWrapper())
+ return object->SetReturnValue(return_value);
+ return Current(return_value.GetIsolate())
+ .SetReturnValueFrom(return_value, object);
+ }
+
+ static bool SetReturnValueForMainWorld(
+ v8::ReturnValue<v8::Value> return_value,
+ ScriptWrappable* object) {
+ return object->SetReturnValue(return_value);
+ }
+
+ static bool SetReturnValueFast(v8::ReturnValue<v8::Value> return_value,
+ ScriptWrappable* object,
+ v8::Local<v8::Object> holder,
+ const ScriptWrappable* wrappable) {
+ if (CanUseMainWorldWrapper()
+ // The second fastest way to check if we're in the main world is to
+ // check if the wrappable's wrapper is the same as the holder.
+ || HolderContainsWrapper(holder, wrappable))
+ return object->SetReturnValue(return_value);
+ return Current(return_value.GetIsolate())
+ .SetReturnValueFrom(return_value, object);
+ }
+
+ static v8::Local<v8::Object> GetWrapper(ScriptWrappable* object,
+ v8::Isolate* isolate) {
+ if (CanUseMainWorldWrapper())
+ return object->MainWorldWrapper(isolate);
+ return Current(isolate).Get(object, isolate);
+ }
+
+ // Associates the given |object| with the given |wrapper| if the object is
+ // not yet associated with any wrapper. Returns true if the given wrapper
+ // is associated with the object, or false if the object is already
+ // associated with a wrapper. In the latter case, |wrapper| will be updated
+ // to the existing wrapper.
+ WARN_UNUSED_RESULT static bool SetWrapper(
+ v8::Isolate* isolate,
+ ScriptWrappable* object,
+ const WrapperTypeInfo* wrapper_type_info,
+ v8::Local<v8::Object>& wrapper) {
+ if (CanUseMainWorldWrapper())
+ return object->SetWrapper(isolate, wrapper_type_info, wrapper);
+ return Current(isolate).Set(isolate, object, wrapper_type_info, wrapper);
+ }
+
+ static bool ContainsWrapper(const ScriptWrappable* object,
+ v8::Isolate* isolate) {
+ return Current(isolate).ContainsWrapper(object);
+ }
+
+ v8::Local<v8::Object> Get(ScriptWrappable* object, v8::Isolate* isolate) {
+ if (is_main_world_)
+ return object->MainWorldWrapper(isolate);
+ return wrapper_map_->NewLocal(isolate, object);
+ }
+
+ WARN_UNUSED_RESULT bool Set(v8::Isolate* isolate,
+ ScriptWrappable* object,
+ const WrapperTypeInfo* wrapper_type_info,
+ v8::Local<v8::Object>& wrapper) {
+ DCHECK(object);
+ DCHECK(!wrapper.IsEmpty());
+ if (is_main_world_)
+ return object->SetWrapper(isolate, wrapper_type_info, wrapper);
+ bool updated = wrapper_map_->Set(object, wrapper_type_info, wrapper);
+ if (updated) {
+ ScriptWrappableMarkingVisitor::WriteBarrier(
+ isolate, &wrapper_map_.value(), object);
+ }
+ return updated;
+ }
+
+ void TraceWrappers(const ScriptWrappable* script_wrappable,
+ const ScriptWrappableVisitor* visitor) {
+ visitor->TraceWrappers(&wrapper_map_.value(), script_wrappable);
+ }
+
+ void MarkWrapper(ScriptWrappable* script_wrappable) {
+ wrapper_map_->MarkWrapper(script_wrappable);
+ }
+
+ // Dissociates a wrapper, if any, from |script_wrappable|.
+ void UnsetWrapperIfAny(ScriptWrappable* script_wrappable) {
+ DCHECK(!is_main_world_);
+ wrapper_map_->RemoveIfAny(script_wrappable);
+ }
+
+ bool SetReturnValueFrom(v8::ReturnValue<v8::Value> return_value,
+ ScriptWrappable* object) {
+ if (is_main_world_)
+ return object->SetReturnValue(return_value);
+ return wrapper_map_->SetReturnValueFrom(return_value, object);
+ }
+
+ bool ContainsWrapper(const ScriptWrappable* object) {
+ if (is_main_world_)
+ return object->ContainsWrapper();
+ return wrapper_map_->ContainsKey(object);
+ }
+
+ private:
+ // We can use a wrapper stored in a ScriptWrappable when we're in the main
+ // world. This method does the fast check if we're in the main world. If this
+ // method returns true, it is guaranteed that we're in the main world. On the
+ // other hand, if this method returns false, nothing is guaranteed (we might
+ // be in the main world).
+ static bool CanUseMainWorldWrapper() {
+ return !WTF::MayNotBeMainThread() &&
+ !DOMWrapperWorld::NonMainWorldsExistInMainThread();
+ }
+
+ static bool HolderContainsWrapper(v8::Local<v8::Object> holder,
+ const ScriptWrappable* wrappable) {
+ // Verify our assumptions about the main world.
+ DCHECK(wrappable);
+ DCHECK(!wrappable->ContainsWrapper() || !wrappable->IsEqualTo(holder) ||
+ Current(v8::Isolate::GetCurrent()).is_main_world_);
+ return wrappable->IsEqualTo(holder);
+ }
+
+ bool is_main_world_;
+ WTF::Optional<DOMWrapperMap<ScriptWrappable>> wrapper_map_;
+};
+
+template <>
+inline void DOMWrapperMap<ScriptWrappable>::PersistentValueMapTraits::Dispose(
+ v8::Isolate*,
+ v8::Global<v8::Object>,
+ ScriptWrappable*) {
+ WrapperTypeInfo::WrapperDestroyed();
+}
+
+template <>
+inline void
+DOMWrapperMap<ScriptWrappable>::PersistentValueMapTraits::DisposeWeak(
+ const v8::WeakCallbackInfo<WeakCallbackDataType>&) {
+ WrapperTypeInfo::WrapperDestroyed();
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_DOM_DATA_STORE_H_
diff --git a/chromium/third_party/blink/renderer/platform/bindings/dom_wrapper_map.h b/chromium/third_party/blink/renderer/platform/bindings/dom_wrapper_map.h
new file mode 100644
index 00000000000..6d532f09e45
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/dom_wrapper_map.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2012 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_DOM_WRAPPER_MAP_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_DOM_WRAPPER_MAP_H_
+
+#include <utility>
+
+#include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h"
+#include "third_party/blink/renderer/platform/bindings/wrapper_type_info.h"
+#include "third_party/blink/renderer/platform/wtf/compiler.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "v8/include/v8-util.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+// Maps from C++ objects to their corresponding JavaScript wrappers. See also
+// DOMDataStore.
+template <class KeyType>
+class DOMWrapperMap {
+ USING_FAST_MALLOC(DOMWrapperMap);
+
+ public:
+ explicit DOMWrapperMap(v8::Isolate* isolate)
+ : isolate_(isolate), map_(isolate) {}
+
+ v8::Local<v8::Object> NewLocal(v8::Isolate* isolate, KeyType* key) {
+ return map_.Get(key);
+ }
+
+ bool SetReturnValueFrom(v8::ReturnValue<v8::Value> return_value,
+ KeyType* key) {
+ return map_.SetReturnValue(key, return_value);
+ }
+
+ void SetReference(v8::Isolate* isolate,
+ const v8::Persistent<v8::Object>& parent,
+ KeyType* key) {
+ map_.SetReference(key, parent);
+ }
+
+ bool ContainsKey(const KeyType* key) {
+ return map_.Contains(const_cast<KeyType*>(key));
+ }
+
+ WARN_UNUSED_RESULT bool Set(KeyType* key,
+ const WrapperTypeInfo* wrapper_type_info,
+ v8::Local<v8::Object>& wrapper) {
+ if (UNLIKELY(ContainsKey(key))) {
+ wrapper = NewLocal(isolate_, key);
+ return false;
+ }
+ v8::Global<v8::Object> global(isolate_, wrapper);
+ wrapper_type_info->ConfigureWrapper(&global);
+ map_.Set(key, std::move(global));
+ return true;
+ }
+
+ void RemoveIfAny(KeyType* key) {
+ if (ContainsKey(key))
+ map_.Remove(key);
+ }
+
+ void Clear() { map_.Clear(); }
+
+ void RemoveAndDispose(KeyType* key) {
+ DCHECK(ContainsKey(key));
+ map_.Remove(key);
+ }
+
+ void MarkWrapper(KeyType* object) {
+ map_.RegisterExternallyReferencedObject(object);
+ }
+
+ private:
+ class PersistentValueMapTraits {
+ STATIC_ONLY(PersistentValueMapTraits);
+
+ public:
+ // Map traits:
+ //
+ // DOMWrapperMap is NOT responsible to make |KeyType|s alive, so uses
+ // UntracedMember<KeyType> as the key type of the internal storage.
+ // |KeyType|s will be made alive by V8 wrapper objects.
+ typedef HashMap<UntracedMember<KeyType>, v8::PersistentContainerValue> Impl;
+ typedef typename Impl::iterator Iterator;
+ static size_t Size(const Impl* impl) { return impl->size(); }
+ static bool Empty(Impl* impl) { return impl->IsEmpty(); }
+ static void Swap(Impl& impl, Impl& other) { impl.swap(other); }
+ static Iterator Begin(Impl* impl) { return impl->begin(); }
+ static Iterator End(Impl* impl) { return impl->end(); }
+ static v8::PersistentContainerValue Value(Iterator& iter) {
+ return iter->value;
+ }
+ static KeyType* Key(Iterator& iter) { return iter->key; }
+ static v8::PersistentContainerValue
+ Set(Impl* impl, KeyType* key, v8::PersistentContainerValue value) {
+ v8::PersistentContainerValue old_value = Get(impl, key);
+ impl->Set(key, value);
+ return old_value;
+ }
+ static v8::PersistentContainerValue Get(const Impl* impl, KeyType* key) {
+ return impl->at(key);
+ }
+
+ static v8::PersistentContainerValue Remove(Impl* impl, KeyType* key) {
+ return impl->Take(key);
+ }
+
+ // Weak traits:
+ static const v8::PersistentContainerCallbackType kCallbackType =
+ v8::kWeakWithInternalFields;
+ typedef v8::GlobalValueMap<KeyType*, v8::Object, PersistentValueMapTraits>
+ MapType;
+ typedef MapType WeakCallbackDataType;
+
+ static WeakCallbackDataType* WeakCallbackParameter(
+ MapType* map,
+ KeyType* key,
+ v8::Local<v8::Object>& value) {
+ return map;
+ }
+
+ static void DisposeCallbackData(WeakCallbackDataType* callback_data) {}
+
+ static MapType* MapFromWeakCallbackInfo(
+ const v8::WeakCallbackInfo<WeakCallbackDataType>& data) {
+ return data.GetParameter();
+ }
+
+ static KeyType* KeyFromWeakCallbackInfo(
+ const v8::WeakCallbackInfo<WeakCallbackDataType>& data) {
+ return reinterpret_cast<KeyType*>(
+ data.GetInternalField(kV8DOMWrapperObjectIndex));
+ }
+
+ static void OnWeakCallback(
+ const v8::WeakCallbackInfo<WeakCallbackDataType>&) {}
+
+ static void Dispose(v8::Isolate*, v8::Global<v8::Object>, KeyType*);
+
+ static void DisposeWeak(const v8::WeakCallbackInfo<WeakCallbackDataType>&);
+ };
+
+ v8::Isolate* isolate_;
+ typename PersistentValueMapTraits::MapType map_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_DOM_WRAPPER_MAP_H_
diff --git a/chromium/third_party/blink/renderer/platform/bindings/dom_wrapper_world.cc b/chromium/third_party/blink/renderer/platform/bindings/dom_wrapper_world.cc
new file mode 100644
index 00000000000..0351715bfdb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/dom_wrapper_world.cc
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h"
+
+#include <memory>
+#include <utility>
+
+#include "third_party/blink/renderer/platform/bindings/dom_data_store.h"
+#include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
+#include "third_party/blink/renderer/platform/wtf/hash_traits.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+
+namespace blink {
+
+unsigned DOMWrapperWorld::number_of_non_main_worlds_in_main_thread_ = 0;
+
+// This does not contain the main world because the WorldMap needs
+// non-default hashmap traits (WTF::UnsignedWithZeroKeyHashTraits) to contain
+// it for the main world's id (0), and it may change the performance trends.
+// (see https://crbug.com/704778#c6).
+using WorldMap = HashMap<int, DOMWrapperWorld*>;
+static WorldMap& GetWorldMap() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadSpecific<WorldMap>, map, ());
+ return *map;
+}
+
+#if DCHECK_IS_ON()
+static bool IsIsolatedWorldId(int world_id) {
+ return DOMWrapperWorld::kMainWorldId < world_id &&
+ world_id < DOMWrapperWorld::kIsolatedWorldIdLimit;
+}
+
+static bool IsMainWorldId(int world_id) {
+ return world_id == DOMWrapperWorld::kMainWorldId;
+}
+#endif
+
+scoped_refptr<DOMWrapperWorld> DOMWrapperWorld::Create(v8::Isolate* isolate,
+ WorldType world_type) {
+ DCHECK_NE(WorldType::kIsolated, world_type);
+ int world_id = GenerateWorldIdForType(world_type);
+ if (world_id == kInvalidWorldId)
+ return nullptr;
+ return base::AdoptRef(new DOMWrapperWorld(isolate, world_type, world_id));
+}
+
+DOMWrapperWorld::DOMWrapperWorld(v8::Isolate* isolate,
+ WorldType world_type,
+ int world_id)
+ : world_type_(world_type),
+ world_id_(world_id),
+ dom_data_store_(std::make_unique<DOMDataStore>(isolate, IsMainWorld())) {
+ switch (world_type_) {
+ case WorldType::kMain:
+ // The main world is managed separately from worldMap(). See worldMap().
+ break;
+ case WorldType::kIsolated:
+ case WorldType::kInspectorIsolated:
+ case WorldType::kGarbageCollector:
+ case WorldType::kRegExp:
+ case WorldType::kTesting:
+ case WorldType::kForV8ContextSnapshotNonMain:
+ case WorldType::kWorker: {
+ WorldMap& map = GetWorldMap();
+ DCHECK(!map.Contains(world_id_));
+ map.insert(world_id_, this);
+ if (IsMainThread())
+ number_of_non_main_worlds_in_main_thread_++;
+ break;
+ }
+ }
+}
+
+DOMWrapperWorld& DOMWrapperWorld::MainWorld() {
+ DCHECK(IsMainThread());
+ DEFINE_STATIC_REF(
+ DOMWrapperWorld, cached_main_world,
+ (DOMWrapperWorld::Create(v8::Isolate::GetCurrent(), WorldType::kMain)));
+ return *cached_main_world;
+}
+
+void DOMWrapperWorld::AllWorldsInCurrentThread(
+ Vector<scoped_refptr<DOMWrapperWorld>>& worlds) {
+ if (IsMainThread())
+ worlds.push_back(&MainWorld());
+ for (DOMWrapperWorld* world : GetWorldMap().Values())
+ worlds.push_back(world);
+}
+
+void DOMWrapperWorld::TraceWrappers(const ScriptWrappable* script_wrappable,
+ const ScriptWrappableVisitor* visitor) {
+ // Marking for worlds other than the main world.
+ DCHECK(ThreadState::Current()->GetIsolate());
+ for (DOMWrapperWorld* world : GetWorldMap().Values()) {
+ DOMDataStore& data_store = world->DomDataStore();
+ if (data_store.ContainsWrapper(script_wrappable))
+ data_store.TraceWrappers(script_wrappable, visitor);
+ }
+}
+
+DOMWrapperWorld::~DOMWrapperWorld() {
+ DCHECK(!IsMainWorld());
+ if (IsMainThread())
+ number_of_non_main_worlds_in_main_thread_--;
+
+ // WorkerWorld should be disposed of before the dtor.
+ if (!IsWorkerWorld())
+ Dispose();
+ DCHECK(!GetWorldMap().Contains(world_id_));
+}
+
+void DOMWrapperWorld::Dispose() {
+ dom_object_holders_.clear();
+ dom_data_store_.reset();
+ DCHECK(GetWorldMap().Contains(world_id_));
+ GetWorldMap().erase(world_id_);
+}
+
+scoped_refptr<DOMWrapperWorld> DOMWrapperWorld::EnsureIsolatedWorld(
+ v8::Isolate* isolate,
+ int world_id) {
+#if DCHECK_IS_ON()
+ DCHECK(IsIsolatedWorldId(world_id));
+#endif
+
+ WorldMap& map = GetWorldMap();
+ auto it = map.find(world_id);
+ if (it != map.end()) {
+ scoped_refptr<DOMWrapperWorld> world = it->value;
+ DCHECK(world->IsIsolatedWorld());
+ DCHECK_EQ(world_id, world->GetWorldId());
+ return world;
+ }
+
+ return base::AdoptRef(
+ new DOMWrapperWorld(isolate, WorldType::kIsolated, world_id));
+}
+
+typedef HashMap<int, scoped_refptr<SecurityOrigin>>
+ IsolatedWorldSecurityOriginMap;
+static IsolatedWorldSecurityOriginMap& IsolatedWorldSecurityOrigins() {
+ DCHECK(IsMainThread());
+ DEFINE_STATIC_LOCAL(IsolatedWorldSecurityOriginMap, map, ());
+ return map;
+}
+
+SecurityOrigin* DOMWrapperWorld::IsolatedWorldSecurityOrigin() {
+ DCHECK(this->IsIsolatedWorld());
+ IsolatedWorldSecurityOriginMap& origins = IsolatedWorldSecurityOrigins();
+ IsolatedWorldSecurityOriginMap::iterator it = origins.find(GetWorldId());
+ return it == origins.end() ? nullptr : it->value.get();
+}
+
+void DOMWrapperWorld::SetIsolatedWorldSecurityOrigin(
+ int world_id,
+ scoped_refptr<SecurityOrigin> security_origin) {
+#if DCHECK_IS_ON()
+ DCHECK(IsIsolatedWorldId(world_id));
+#endif
+ if (security_origin)
+ IsolatedWorldSecurityOrigins().Set(world_id, std::move(security_origin));
+ else
+ IsolatedWorldSecurityOrigins().erase(world_id);
+}
+
+typedef HashMap<int, String> IsolatedWorldHumanReadableNameMap;
+static IsolatedWorldHumanReadableNameMap& IsolatedWorldHumanReadableNames() {
+ DCHECK(IsMainThread());
+ DEFINE_STATIC_LOCAL(IsolatedWorldHumanReadableNameMap, map, ());
+ return map;
+}
+
+String DOMWrapperWorld::NonMainWorldHumanReadableName() {
+ DCHECK(!this->IsMainWorld());
+ return IsolatedWorldHumanReadableNames().at(GetWorldId());
+}
+
+void DOMWrapperWorld::SetNonMainWorldHumanReadableName(
+ int world_id,
+ const String& human_readable_name) {
+#if DCHECK_IS_ON()
+ DCHECK(!IsMainWorldId(world_id));
+#endif
+ IsolatedWorldHumanReadableNames().Set(world_id, human_readable_name);
+}
+
+typedef HashMap<int, bool> IsolatedWorldContentSecurityPolicyMap;
+static IsolatedWorldContentSecurityPolicyMap&
+IsolatedWorldContentSecurityPolicies() {
+ DCHECK(IsMainThread());
+ DEFINE_STATIC_LOCAL(IsolatedWorldContentSecurityPolicyMap, map, ());
+ return map;
+}
+
+bool DOMWrapperWorld::IsolatedWorldHasContentSecurityPolicy() {
+ DCHECK(this->IsIsolatedWorld());
+ IsolatedWorldContentSecurityPolicyMap& policies =
+ IsolatedWorldContentSecurityPolicies();
+ IsolatedWorldContentSecurityPolicyMap::iterator it =
+ policies.find(GetWorldId());
+ return it == policies.end() ? false : it->value;
+}
+
+void DOMWrapperWorld::SetIsolatedWorldContentSecurityPolicy(
+ int world_id,
+ const String& policy) {
+#if DCHECK_IS_ON()
+ DCHECK(IsIsolatedWorldId(world_id));
+#endif
+ if (!policy.IsEmpty())
+ IsolatedWorldContentSecurityPolicies().Set(world_id, true);
+ else
+ IsolatedWorldContentSecurityPolicies().erase(world_id);
+}
+
+void DOMWrapperWorld::RegisterDOMObjectHolderInternal(
+ std::unique_ptr<DOMObjectHolderBase> holder_base) {
+ DCHECK(!dom_object_holders_.Contains(holder_base.get()));
+ holder_base->SetWorld(this);
+ holder_base->SetWeak(&DOMWrapperWorld::WeakCallbackForDOMObjectHolder);
+ dom_object_holders_.insert(std::move(holder_base));
+}
+
+void DOMWrapperWorld::UnregisterDOMObjectHolder(
+ DOMObjectHolderBase* holder_base) {
+ DCHECK(dom_object_holders_.Contains(holder_base));
+ dom_object_holders_.erase(holder_base);
+}
+
+void DOMWrapperWorld::WeakCallbackForDOMObjectHolder(
+ const v8::WeakCallbackInfo<DOMObjectHolderBase>& data) {
+ DOMObjectHolderBase* holder_base = data.GetParameter();
+ holder_base->World()->UnregisterDOMObjectHolder(holder_base);
+}
+
+// static
+int DOMWrapperWorld::GenerateWorldIdForType(WorldType world_type) {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadSpecific<int>, next_world_id, ());
+ if (!next_world_id.IsSet())
+ *next_world_id = WorldId::kUnspecifiedWorldIdStart;
+ switch (world_type) {
+ case WorldType::kMain:
+ return kMainWorldId;
+ case WorldType::kIsolated:
+ // This function should not be called for IsolatedWorld because an
+ // identifier for the world is given from out of DOMWrapperWorld.
+ NOTREACHED();
+ return kInvalidWorldId;
+ case WorldType::kInspectorIsolated: {
+ DCHECK(IsMainThread());
+ static int next_devtools_isolated_world_id =
+ IsolatedWorldId::kDevToolsFirstIsolatedWorldId;
+ if (next_devtools_isolated_world_id >
+ IsolatedWorldId::kDevToolsLastIsolatedWorldId)
+ return WorldId::kInvalidWorldId;
+ return next_devtools_isolated_world_id++;
+ }
+ case WorldType::kGarbageCollector:
+ case WorldType::kRegExp:
+ case WorldType::kTesting:
+ case WorldType::kForV8ContextSnapshotNonMain:
+ case WorldType::kWorker:
+ int world_id = *next_world_id;
+ CHECK_GE(world_id, WorldId::kUnspecifiedWorldIdStart);
+ *next_world_id = world_id + 1;
+ return world_id;
+ }
+ NOTREACHED();
+ return kInvalidWorldId;
+}
+
+void DOMWrapperWorld::DissociateDOMWindowWrappersInAllWorlds(
+ ScriptWrappable* script_wrappable) {
+ DCHECK(script_wrappable);
+ DCHECK(IsMainThread());
+
+ script_wrappable->UnsetWrapperIfAny();
+
+ for (auto*& world : GetWorldMap().Values())
+ world->DomDataStore().UnsetWrapperIfAny(script_wrappable);
+}
+
+bool DOMWrapperWorld::HasWrapperInAnyWorldInMainThread(
+ ScriptWrappable* script_wrappable) {
+ DCHECK(IsMainThread());
+
+ Vector<scoped_refptr<DOMWrapperWorld>> worlds;
+ DOMWrapperWorld::AllWorldsInCurrentThread(worlds);
+ for (const auto& world : worlds) {
+ DOMDataStore& dom_data_store = world->DomDataStore();
+ if (dom_data_store.ContainsWrapper(script_wrappable))
+ return true;
+ }
+ return false;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/bindings/dom_wrapper_world.h b/chromium/third_party/blink/renderer/platform/bindings/dom_wrapper_world.h
new file mode 100644
index 00000000000..5e5bc75d862
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/dom_wrapper_world.h
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_DOM_WRAPPER_WORLD_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_DOM_WRAPPER_WORLD_H_
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/public/platform/web_isolated_world_ids.h"
+#include "third_party/blink/renderer/platform/bindings/script_state.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+class DOMDataStore;
+class DOMObjectHolderBase;
+
+// This class represent a collection of DOM wrappers for a specific world. This
+// is identified by a world id that is a per-thread global identifier (see
+// WorldId enum).
+class PLATFORM_EXPORT DOMWrapperWorld : public RefCounted<DOMWrapperWorld> {
+ public:
+ // Per-thread global identifiers for DOMWrapperWorld.
+ enum WorldId {
+ kInvalidWorldId = -1,
+ kMainWorldId = 0,
+
+ kEmbedderWorldIdLimit = IsolatedWorldId::kEmbedderWorldIdLimit,
+ kIsolatedWorldIdLimit = IsolatedWorldId::kIsolatedWorldIdLimit,
+
+ // Other worlds can use IDs after this. Don't manually pick up an ID from
+ // this range. generateWorldIdForType() picks it up on behalf of you.
+ kUnspecifiedWorldIdStart,
+ };
+
+ enum class WorldType {
+ kMain,
+ kIsolated,
+ kInspectorIsolated,
+ kGarbageCollector,
+ kRegExp,
+ kTesting,
+ kForV8ContextSnapshotNonMain,
+ kWorker,
+ };
+
+ // Creates a world other than IsolatedWorld. Note this can return nullptr if
+ // GenerateWorldIdForType fails to allocate a valid id.
+ static scoped_refptr<DOMWrapperWorld> Create(v8::Isolate*, WorldType);
+
+ // Ensures an IsolatedWorld for |worldId|.
+ static scoped_refptr<DOMWrapperWorld> EnsureIsolatedWorld(v8::Isolate*,
+ int world_id);
+ ~DOMWrapperWorld();
+ void Dispose();
+
+ // Called from performance-sensitive functions, so we should keep this simple
+ // and fast as much as possible.
+ static bool NonMainWorldsExistInMainThread() {
+ return number_of_non_main_worlds_in_main_thread_;
+ }
+
+ static void AllWorldsInCurrentThread(
+ Vector<scoped_refptr<DOMWrapperWorld>>& worlds);
+
+ // Traces wrappers corresponding to the ScriptWrappable in DOM data stores.
+ static void TraceWrappers(const ScriptWrappable*,
+ const ScriptWrappableVisitor*);
+
+ static DOMWrapperWorld& World(v8::Local<v8::Context> context) {
+ return ScriptState::From(context)->World();
+ }
+
+ static DOMWrapperWorld& Current(v8::Isolate* isolate) {
+ return World(isolate->GetCurrentContext());
+ }
+
+ static DOMWrapperWorld& MainWorld();
+
+ static void SetNonMainWorldHumanReadableName(int world_id, const String&);
+ String NonMainWorldHumanReadableName();
+
+ // Associates an isolated world (see above for description) with a security
+ // origin. XMLHttpRequest instances used in that world will be considered
+ // to come from that origin, not the frame's.
+ static void SetIsolatedWorldSecurityOrigin(int world_id,
+ scoped_refptr<SecurityOrigin>);
+ SecurityOrigin* IsolatedWorldSecurityOrigin();
+
+ // Associated an isolated world with a Content Security Policy. Resources
+ // embedded into the main world's DOM from script executed in an isolated
+ // world should be restricted based on the isolated world's DOM, not the
+ // main world's.
+ //
+ // FIXME: Right now, resource injection simply bypasses the main world's
+ // DOM. More work is necessary to allow the isolated world's policy to be
+ // applied correctly.
+ static void SetIsolatedWorldContentSecurityPolicy(int world_id,
+ const String& policy);
+ bool IsolatedWorldHasContentSecurityPolicy();
+
+ static bool HasWrapperInAnyWorldInMainThread(ScriptWrappable*);
+
+ bool IsMainWorld() const { return world_type_ == WorldType::kMain; }
+ bool IsWorkerWorld() const { return world_type_ == WorldType::kWorker; }
+ bool IsIsolatedWorld() const {
+ return world_type_ == WorldType::kIsolated ||
+ world_type_ == WorldType::kInspectorIsolated;
+ }
+
+ int GetWorldId() const { return world_id_; }
+ DOMDataStore& DomDataStore() const { return *dom_data_store_; }
+
+ template <typename T>
+ void RegisterDOMObjectHolder(v8::Isolate* isolate,
+ T* object,
+ v8::Local<v8::Value> wrapper) {
+ RegisterDOMObjectHolderInternal(
+ DOMObjectHolder<T>::Create(isolate, object, wrapper));
+ }
+
+ private:
+ class DOMObjectHolderBase {
+ USING_FAST_MALLOC(DOMObjectHolderBase);
+
+ public:
+ DOMObjectHolderBase(v8::Isolate* isolate, v8::Local<v8::Value> wrapper)
+ : wrapper_(isolate, wrapper), world_(nullptr) {}
+ virtual ~DOMObjectHolderBase() = default;
+
+ DOMWrapperWorld* World() const { return world_; }
+ void SetWorld(DOMWrapperWorld* world) { world_ = world; }
+ void SetWeak(v8::WeakCallbackInfo<DOMObjectHolderBase>::Callback callback) {
+ wrapper_.SetWeak(this, callback);
+ }
+
+ private:
+ ScopedPersistent<v8::Value> wrapper_;
+ DOMWrapperWorld* world_;
+ };
+
+ template <typename T>
+ class DOMObjectHolder : public DOMObjectHolderBase {
+ public:
+ static std::unique_ptr<DOMObjectHolder<T>>
+ Create(v8::Isolate* isolate, T* object, v8::Local<v8::Value> wrapper) {
+ return base::WrapUnique(new DOMObjectHolder(isolate, object, wrapper));
+ }
+
+ private:
+ DOMObjectHolder(v8::Isolate* isolate,
+ T* object,
+ v8::Local<v8::Value> wrapper)
+ : DOMObjectHolderBase(isolate, wrapper), object_(object) {}
+
+ Persistent<T> object_;
+ };
+
+ DOMWrapperWorld(v8::Isolate*, WorldType, int world_id);
+
+ static void WeakCallbackForDOMObjectHolder(
+ const v8::WeakCallbackInfo<DOMObjectHolderBase>&);
+ void RegisterDOMObjectHolderInternal(std::unique_ptr<DOMObjectHolderBase>);
+ void UnregisterDOMObjectHolder(DOMObjectHolderBase*);
+
+ static unsigned number_of_non_main_worlds_in_main_thread_;
+
+ // Returns an identifier for a given world type. This must not be called for
+ // WorldType::IsolatedWorld because an identifier for the world is given from
+ // out of DOMWrapperWorld.
+ static int GenerateWorldIdForType(WorldType);
+
+ // Dissociates all wrappers in all worlds associated with |script_wrappable|.
+ //
+ // Do not use this function except for DOMWindow. Only DOMWindow needs to
+ // dissociate wrappers from the ScriptWrappable because of the following two
+ // reasons.
+ //
+ // Reason 1) Case of the main world
+ // A DOMWindow may be collected by Blink GC *before* V8 GC collects the
+ // wrapper because the wrapper object associated with a DOMWindow is a global
+ // proxy, which remains after navigations. We don't want V8 GC to reset the
+ // weak persistent handle to a wrapper within the DOMWindow
+ // (ScriptWrappable::main_world_wrapper_) *after* Blink GC collects the
+ // DOMWindow because it's use-after-free. Thus, we need to dissociate the
+ // wrapper in advance.
+ //
+ // Reason 2) Case of isolated worlds
+ // As same, a DOMWindow may be collected before the wrapper gets collected.
+ // A DOMWrapperMap supports mapping from ScriptWrappable* to v8::Global<T>,
+ // and we don't want to leave an entry of an already-dead DOMWindow* to the
+ // persistent handle for the global proxy object, especially considering that
+ // the address to the already-dead DOMWindow* may be re-used.
+ friend class DOMWindow;
+ static void DissociateDOMWindowWrappersInAllWorlds(ScriptWrappable*);
+
+ const WorldType world_type_;
+ const int world_id_;
+ std::unique_ptr<DOMDataStore> dom_data_store_;
+ HashSet<std::unique_ptr<DOMObjectHolderBase>> dom_object_holders_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_DOM_WRAPPER_WORLD_H_
diff --git a/chromium/third_party/blink/renderer/platform/bindings/microtask.cc b/chromium/third_party/blink/renderer/platform/bindings/microtask.cc
new file mode 100644
index 00000000000..8c2150a1a9f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/microtask.cc
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2013 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:
+ *
+ * 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 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 "third_party/blink/renderer/platform/bindings/microtask.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h"
+
+namespace blink {
+
+void Microtask::PerformCheckpoint(v8::Isolate* isolate) {
+ if (ScriptForbiddenScope::IsScriptForbidden())
+ return;
+ v8::MicrotasksScope::PerformCheckpoint(isolate);
+}
+
+static void MicrotaskFunctionCallback(void* data) {
+ std::unique_ptr<base::OnceClosure> task =
+ base::WrapUnique(static_cast<base::OnceClosure*>(data));
+ std::move(*task).Run();
+}
+
+void Microtask::EnqueueMicrotask(base::OnceClosure callback) {
+ v8::Isolate* isolate = v8::Isolate::GetCurrent();
+ isolate->EnqueueMicrotask(
+ &MicrotaskFunctionCallback,
+ static_cast<void*>(new base::OnceClosure(std::move(callback))));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/bindings/microtask.h b/chromium/third_party/blink/renderer/platform/bindings/microtask.h
new file mode 100644
index 00000000000..be2e69487e6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/microtask.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2013 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:
+ *
+ * 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 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_MICROTASK_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_MICROTASK_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+// C++ calls into script contexts which are "owned" by blink (created in a
+// process where WebKit.cpp initializes v8) must declare their type:
+//
+// 1. Calls into page/author script from a frame
+// 2. Calls into page/author script from a worker
+// 3. Calls into internal script (typically setup/teardown work)
+//
+// Debug-time checking of this is enforced via v8::MicrotasksScope.
+//
+// Calls of type (1) should generally go through ScriptController, as inspector
+// instrumentation is needed. ScriptController allocates V8RecursionScope for
+// you.
+//
+// Calls of type (2) should always stack-allocate a
+// v8::MicrotasksScope(kRunMicrtoasks) in the same block as the call into
+// script.
+//
+// Calls of type (3) should stack allocate a
+// v8::MicrotasksScope(kDoNotRunMicrotasks) -- this skips work that is spec'd to
+// happen at the end of the outer-most script stack frame of calls into page
+// script:
+// http://www.whatwg.org/specs/web-apps/current-work/#perform-a-microtask-checkpoint
+class PLATFORM_EXPORT Microtask {
+ STATIC_ONLY(Microtask);
+
+ public:
+ static void PerformCheckpoint(v8::Isolate*);
+
+ // TODO(jochen): Make all microtasks pass in the ScriptState they want to be
+ // executed in. Until then, all microtasks have to keep track of their
+ // ScriptState themselves.
+ static void EnqueueMicrotask(base::OnceClosure);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_MICROTASK_H_
diff --git a/chromium/third_party/blink/renderer/platform/bindings/origin_trial_features.cc b/chromium/third_party/blink/renderer/platform/bindings/origin_trial_features.cc
new file mode 100644
index 00000000000..169ef8785cc
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/origin_trial_features.cc
@@ -0,0 +1,70 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/bindings/origin_trial_features.h"
+
+#include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h"
+#include "third_party/blink/renderer/platform/bindings/script_state.h"
+
+namespace blink {
+
+void InstallOriginTrialFeaturesDefault(
+ const WrapperTypeInfo* wrapper_type_info,
+ const ScriptState* script_state,
+ v8::Local<v8::Object> prototype_object,
+ v8::Local<v8::Function> interface_object) {}
+
+void InstallPendingOriginTrialFeatureDefault(const String& feature,
+ const ScriptState* script_state) {}
+
+namespace {
+InstallOriginTrialFeaturesFunction g_install_origin_trial_features_function =
+ &InstallOriginTrialFeaturesDefault;
+
+InstallPendingOriginTrialFeatureFunction
+ g_install_pending_origin_trial_feature_function =
+ &InstallPendingOriginTrialFeatureDefault;
+} // namespace
+
+InstallOriginTrialFeaturesFunction SetInstallOriginTrialFeaturesFunction(
+ InstallOriginTrialFeaturesFunction
+ new_install_origin_trial_features_function) {
+ InstallOriginTrialFeaturesFunction original_function =
+ g_install_origin_trial_features_function;
+ g_install_origin_trial_features_function =
+ new_install_origin_trial_features_function;
+ return original_function;
+}
+
+InstallPendingOriginTrialFeatureFunction
+SetInstallPendingOriginTrialFeatureFunction(
+ InstallPendingOriginTrialFeatureFunction
+ new_install_pending_origin_trial_feature_function) {
+ InstallPendingOriginTrialFeatureFunction original_function =
+ g_install_pending_origin_trial_feature_function;
+ g_install_pending_origin_trial_feature_function =
+ new_install_pending_origin_trial_feature_function;
+ return original_function;
+}
+
+void InstallOriginTrialFeatures(const WrapperTypeInfo* type,
+ const ScriptState* script_state,
+ v8::Local<v8::Object> prototype_object,
+ v8::Local<v8::Function> interface_object) {
+ (*g_install_origin_trial_features_function)(
+ type, script_state, prototype_object, interface_object);
+}
+
+void InstallPendingOriginTrialFeature(const String& feature,
+ const ScriptState* script_state) {
+ DCHECK(script_state);
+ DCHECK(script_state->GetContext() ==
+ script_state->GetIsolate()->GetCurrentContext());
+ DCHECK(script_state->PerContextData());
+ DCHECK(script_state->World().IsMainWorld());
+
+ (*g_install_pending_origin_trial_feature_function)(feature, script_state);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/bindings/origin_trial_features.h b/chromium/third_party/blink/renderer/platform/bindings/origin_trial_features.h
new file mode 100644
index 00000000000..46fa1971faf
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/origin_trial_features.h
@@ -0,0 +1,59 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_ORIGIN_TRIAL_FEATURES_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_ORIGIN_TRIAL_FEATURES_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+class ScriptState;
+struct WrapperTypeInfo;
+
+using InstallOriginTrialFeaturesFunction = void (*)(const WrapperTypeInfo*,
+ const ScriptState*,
+ v8::Local<v8::Object>,
+ v8::Local<v8::Function>);
+
+using InstallPendingOriginTrialFeatureFunction = void (*)(const String&,
+ const ScriptState*);
+
+// Sets the function to be called by |InstallOriginTrialFeatures|. The function
+// is initially set to the private |InstallOriginTrialFeaturesDefault| function,
+// but can be overridden by this function. A pointer to the previously set
+// function is returned, so that functions can be chained.
+PLATFORM_EXPORT InstallOriginTrialFeaturesFunction
+ SetInstallOriginTrialFeaturesFunction(InstallOriginTrialFeaturesFunction);
+
+// Sets the function to be called by |InstallPendingOriginTrialFeature|. This
+// is initially set to the private |InstallPendingOriginTrialFeatureDefault|
+// function, but can be overridden by this function. A pointer to the previously
+// set function is returned, so that functions can be chained.
+PLATFORM_EXPORT InstallPendingOriginTrialFeatureFunction
+ SetInstallPendingOriginTrialFeatureFunction(
+ InstallPendingOriginTrialFeatureFunction);
+
+// Installs all of the conditionally enabled V8 bindings for the given type, in
+// a specific context. This is called in V8PerContextData, after the constructor
+// and prototype for the type have been created. It indirectly calls the
+// function set by |SetInstallOriginTrialFeaturesFunction|.
+PLATFORM_EXPORT void InstallOriginTrialFeatures(const WrapperTypeInfo*,
+ const ScriptState*,
+ v8::Local<v8::Object>,
+ v8::Local<v8::Function>);
+
+// Installs all of the conditionally enabled V8 bindings for a feature, if
+// needed. This is called to install a newly-enabled feature on any existing
+// objects. If the target object hasn't been created, nothing is installed. The
+// enabled feature will be instead be installed when the object is created
+// (avoids forcing the creation of objects prematurely).
+PLATFORM_EXPORT void InstallPendingOriginTrialFeature(const String&,
+ const ScriptState*);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_ORIGIN_TRIAL_FEATURES_H_
diff --git a/chromium/third_party/blink/renderer/platform/bindings/runtime_call_stats.cc b/chromium/third_party/blink/renderer/platform/bindings/runtime_call_stats.cc
new file mode 100644
index 00000000000..80d04c72f8c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/runtime_call_stats.cc
@@ -0,0 +1,208 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/bindings/runtime_call_stats.h"
+
+#include <inttypes.h>
+#include <algorithm>
+#include "base/time/tick_clock.h"
+#include "third_party/blink/public/web/blink.h"
+#include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+
+namespace blink {
+
+// Wrapper function defined in WebKit.h
+void LogRuntimeCallStats() {
+ LOG(INFO)
+ << "\n"
+ << RuntimeCallStats::From(MainThreadIsolate())->ToString().Utf8().data();
+}
+
+namespace {
+RuntimeCallStats* g_runtime_call_stats_for_testing = nullptr;
+}
+
+void RuntimeCallCounter::Dump(TracedValue& value) const {
+ value.BeginArray(name_);
+ value.PushDouble(count_);
+ value.PushDouble(time_.InMicroseconds());
+ value.EndArray();
+}
+
+void RuntimeCallTimer::Start(RuntimeCallCounter* counter,
+ RuntimeCallTimer* parent) {
+ DCHECK(!IsRunning());
+ counter_ = counter;
+ parent_ = parent;
+ start_ticks_ = TimeTicks(clock_->NowTicks());
+ if (parent_)
+ parent_->Pause(start_ticks_);
+}
+
+RuntimeCallTimer* RuntimeCallTimer::Stop() {
+ DCHECK(IsRunning());
+ TimeTicks now = TimeTicks(clock_->NowTicks());
+ elapsed_time_ += (now - start_ticks_);
+ start_ticks_ = TimeTicks();
+ counter_->IncrementAndAddTime(elapsed_time_);
+ if (parent_)
+ parent_->Resume(now);
+ return parent_;
+}
+
+RuntimeCallStats::RuntimeCallStats(const base::TickClock* clock)
+ : clock_(clock) {
+ static const char* const names[] = {
+#define BINDINGS_COUNTER_NAME(name) "Blink_Bindings_" #name,
+ BINDINGS_COUNTERS(BINDINGS_COUNTER_NAME) //
+#undef BINDINGS_COUNTER_NAME
+#define GC_COUNTER_NAME(name) "Blink_GC_" #name,
+ GC_COUNTERS(GC_COUNTER_NAME) //
+#undef GC_COUNTER_NAME
+#define PARSING_COUNTER_NAME(name) "Blink_Parsing_" #name,
+ PARSING_COUNTERS(PARSING_COUNTER_NAME) //
+#undef PARSING_COUNTER_NAME
+#define STYLE_COUNTER_NAME(name) "Blink_Style_" #name,
+ STYLE_COUNTERS(STYLE_COUNTER_NAME) //
+#undef STYLE_COUNTER_NAME
+#define LAYOUT_COUNTER_NAME(name) "Blink_Layout_" #name,
+ LAYOUT_COUNTERS(LAYOUT_COUNTER_NAME) //
+#undef STYLE_COUNTER_NAME
+#define COUNTER_NAME(name) "Blink_" #name,
+ CALLBACK_COUNTERS(COUNTER_NAME) //
+ EXTRA_COUNTERS(COUNTER_NAME)
+#undef COUNTER_NAME
+ };
+
+ for (int i = 0; i < number_of_counters_; i++) {
+ counters_[i] = RuntimeCallCounter(names[i]);
+ }
+}
+
+// static
+RuntimeCallStats* RuntimeCallStats::From(v8::Isolate* isolate) {
+ if (g_runtime_call_stats_for_testing)
+ return g_runtime_call_stats_for_testing;
+ return V8PerIsolateData::From(isolate)->GetRuntimeCallStats();
+}
+
+void RuntimeCallStats::Reset() {
+ for (int i = 0; i < number_of_counters_; i++) {
+ counters_[i].Reset();
+ }
+
+#if BUILDFLAG(RCS_COUNT_EVERYTHING)
+ for (const auto& counter : counter_map_.Values()) {
+ counter->Reset();
+ }
+#endif
+}
+
+void RuntimeCallStats::Dump(TracedValue& value) const {
+ for (int i = 0; i < number_of_counters_; i++) {
+ if (counters_[i].GetCount() > 0)
+ counters_[i].Dump(value);
+ }
+
+#if BUILDFLAG(RCS_COUNT_EVERYTHING)
+ for (const auto& counter : counter_map_.Values()) {
+ if (counter->GetCount() > 0)
+ counter->Dump(value);
+ }
+#endif
+}
+
+namespace {
+const char row_format[] = "%-55s %8" PRIu64 " %9.3f\n";
+}
+
+String RuntimeCallStats::ToString() const {
+ StringBuilder builder;
+ builder.Append("Runtime Call Stats for Blink \n");
+ builder.Append(
+ "Name Count Time "
+ "(ms)\n\n");
+ for (int i = 0; i < number_of_counters_; i++) {
+ const RuntimeCallCounter* counter = &counters_[i];
+ builder.Append(String::Format(row_format, counter->GetName(),
+ counter->GetCount(),
+ counter->GetTime().InMillisecondsF()));
+ }
+
+#if BUILDFLAG(RCS_COUNT_EVERYTHING)
+ AddCounterMapStatsToBuilder(builder);
+#endif
+
+ return builder.ToString();
+}
+
+// static
+void RuntimeCallStats::SetRuntimeCallStatsForTesting() {
+ DEFINE_STATIC_LOCAL(RuntimeCallStats, s_rcs_for_testing,
+ (base::DefaultTickClock::GetInstance()));
+ g_runtime_call_stats_for_testing =
+ static_cast<RuntimeCallStats*>(&s_rcs_for_testing);
+}
+
+// static
+void RuntimeCallStats::ClearRuntimeCallStatsForTesting() {
+ g_runtime_call_stats_for_testing = nullptr;
+}
+
+#if BUILDFLAG(RCS_COUNT_EVERYTHING)
+RuntimeCallCounter* RuntimeCallStats::GetCounter(const char* name) {
+ CounterMap::iterator it = counter_map_.find(name);
+ if (it != counter_map_.end())
+ return it->value.get();
+ return counter_map_.insert(name, std::make_unique<RuntimeCallCounter>(name))
+ .stored_value->value.get();
+}
+
+Vector<RuntimeCallCounter*> RuntimeCallStats::CounterMapToSortedArray() const {
+ Vector<RuntimeCallCounter*> counters;
+ for (const auto& counter : counter_map_.Values()) {
+ counters.push_back(counter.get());
+ }
+ auto comparator = [](RuntimeCallCounter* a, RuntimeCallCounter* b) {
+ return a->GetCount() == b->GetCount()
+ ? strcmp(a->GetName(), b->GetName()) < 0
+ : a->GetCount() < b->GetCount();
+ };
+ std::sort(counters.begin(), counters.end(), comparator);
+ return counters;
+}
+
+void RuntimeCallStats::AddCounterMapStatsToBuilder(
+ StringBuilder& builder) const {
+ builder.Append(String::Format("\nNumber of counters in map: %u\n\n",
+ counter_map_.size()));
+ for (RuntimeCallCounter* counter : CounterMapToSortedArray()) {
+ builder.Append(String::Format(row_format, counter->GetName(),
+ counter->GetCount(),
+ counter->GetTime().InMillisecondsF()));
+ }
+}
+#endif
+
+const char* const RuntimeCallStatsScopedTracer::s_category_group_ =
+ TRACE_DISABLED_BY_DEFAULT("v8.runtime_stats");
+const char* const RuntimeCallStatsScopedTracer::s_name_ =
+ "BlinkRuntimeCallStats";
+
+void RuntimeCallStatsScopedTracer::AddBeginTraceEvent() {
+ stats_->Reset();
+ stats_->SetInUse(true);
+ TRACE_EVENT_BEGIN0(s_category_group_, s_name_);
+}
+
+void RuntimeCallStatsScopedTracer::AddEndTraceEvent() {
+ std::unique_ptr<TracedValue> value = TracedValue::Create();
+ stats_->Dump(*value);
+ stats_->SetInUse(false);
+ TRACE_EVENT_END1(s_category_group_, s_name_, "runtime-call-stats",
+ std::move(value));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/bindings/runtime_call_stats.h b/chromium/third_party/blink/renderer/platform/bindings/runtime_call_stats.h
new file mode 100644
index 00000000000..ecc3c01c90d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/runtime_call_stats.h
@@ -0,0 +1,390 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains the Blink version of RuntimeCallStats which is implemented
+// by V8 in //v8/src/counters.h
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_RUNTIME_CALL_STATS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_RUNTIME_CALL_STATS_H_
+
+#include "third_party/blink/renderer/platform/bindings/runtime_call_stats_count_everything_buildflags.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/traced_value.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+#include "v8/include/v8.h"
+
+namespace base {
+class TickClock;
+}
+
+namespace blink {
+
+// A simple counter used to track total execution count & time for a particular
+// function/scope.
+class PLATFORM_EXPORT RuntimeCallCounter {
+ public:
+ explicit RuntimeCallCounter(const char* name) : count_(0), name_(name) {}
+
+ void IncrementAndAddTime(TimeDelta time) {
+ count_++;
+ time_ += time;
+ }
+
+ uint64_t GetCount() const { return count_; }
+ TimeDelta GetTime() const { return time_; }
+ const char* GetName() const { return name_; }
+
+ void Reset() {
+ time_ = TimeDelta();
+ count_ = 0;
+ }
+
+ void Dump(TracedValue&) const;
+
+ private:
+ RuntimeCallCounter() = default;
+
+ uint64_t count_;
+ TimeDelta time_;
+ const char* name_;
+
+ friend class RuntimeCallStats;
+};
+
+// Used to track elapsed time for a counter.
+// NOTE: Do not use this class directly to track execution times, instead use it
+// with the macros below.
+class PLATFORM_EXPORT RuntimeCallTimer {
+ public:
+ explicit RuntimeCallTimer(const base::TickClock* clock) : clock_(clock) {}
+ ~RuntimeCallTimer() { DCHECK(!IsRunning()); };
+
+ // Starts recording time for <counter>, and pauses <parent> (if non-null).
+ void Start(RuntimeCallCounter*, RuntimeCallTimer* parent);
+
+ // Stops recording time for the counter passed in Start(), and also updates
+ // elapsed time and increments the count stored by the counter. It also
+ // resumes the parent timer passed in Start() (if any).
+ RuntimeCallTimer* Stop();
+
+ // Resets the timer. Call this before reusing a timer.
+ void Reset() {
+ start_ticks_ = TimeTicks();
+ elapsed_time_ = TimeDelta();
+ }
+
+ private:
+ void Pause(TimeTicks now) {
+ DCHECK(IsRunning());
+ elapsed_time_ += (now - start_ticks_);
+ start_ticks_ = TimeTicks();
+ }
+
+ void Resume(TimeTicks now) {
+ DCHECK(!IsRunning());
+ start_ticks_ = now;
+ }
+
+ bool IsRunning() { return start_ticks_ != TimeTicks(); }
+
+ RuntimeCallCounter* counter_;
+ RuntimeCallTimer* parent_;
+ TimeTicks start_ticks_;
+ TimeDelta elapsed_time_;
+ const base::TickClock* clock_ = nullptr;
+};
+
+// Macros that take RuntimeCallStats as a parameter; used only in
+// RuntimeCallStatsTest.
+#define RUNTIME_CALL_STATS_ENTER_WITH_RCS(runtime_call_stats, timer, \
+ counterId) \
+ if (UNLIKELY(RuntimeEnabledFeatures::BlinkRuntimeCallStatsEnabled())) { \
+ (runtime_call_stats)->Enter(timer, counterId); \
+ }
+
+#define RUNTIME_CALL_STATS_LEAVE_WITH_RCS(runtime_call_stats, timer) \
+ if (UNLIKELY(RuntimeEnabledFeatures::BlinkRuntimeCallStatsEnabled())) { \
+ (runtime_call_stats)->Leave(timer); \
+ }
+
+#define RUNTIME_CALL_TIMER_SCOPE_WITH_RCS(runtime_call_stats, counterId) \
+ Optional<RuntimeCallTimerScope> rcs_scope; \
+ if (UNLIKELY(RuntimeEnabledFeatures::BlinkRuntimeCallStatsEnabled())) { \
+ rcs_scope.emplace(runtime_call_stats, counterId); \
+ }
+
+#define RUNTIME_CALL_TIMER_SCOPE_WITH_OPTIONAL_RCS( \
+ optional_scope_name, runtime_call_stats, counterId) \
+ if (UNLIKELY(RuntimeEnabledFeatures::BlinkRuntimeCallStatsEnabled())) { \
+ optional_scope_name.emplace(runtime_call_stats, counterId); \
+ }
+
+// Use the macros below instead of directly using RuntimeCallStats::Enter,
+// RuntimeCallStats::Leave and RuntimeCallTimerScope. They force an early
+// exit if Runtime Call Stats is disabled.
+#define RUNTIME_CALL_STATS_ENTER(isolate, timer, counterId) \
+ RUNTIME_CALL_STATS_ENTER_WITH_RCS(RuntimeCallStats::From(isolate), timer, \
+ counterId)
+
+#define RUNTIME_CALL_STATS_LEAVE(isolate, timer) \
+ RUNTIME_CALL_STATS_LEAVE_WITH_RCS(RuntimeCallStats::From(isolate), timer)
+
+#define RUNTIME_CALL_TIMER_SCOPE(isolate, counterId) \
+ RUNTIME_CALL_TIMER_SCOPE_WITH_RCS(RuntimeCallStats::From(isolate), counterId)
+
+#define RUNTIME_CALL_TIMER_SCOPE_IF_ISOLATE_EXISTS(isolate, counterId) \
+ Optional<RuntimeCallTimerScope> rcs_scope; \
+ if (isolate) { \
+ RUNTIME_CALL_TIMER_SCOPE_WITH_OPTIONAL_RCS( \
+ rcs_scope, RuntimeCallStats::From(isolate), counterId) \
+ }
+
+// Used in places which do not have a counter explicitly defined in
+// FOR_EACH_COUNTER. This is a no-op by default (when RCS_COUNT_EVERYTHING is
+// not set).
+#if BUILDFLAG(RCS_COUNT_EVERYTHING)
+#define RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT(isolate, counterName) \
+ RUNTIME_CALL_TIMER_SCOPE(isolate, counterName)
+#else
+#define RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT(isolate, counterName) \
+ do { \
+ } while (false)
+#endif
+
+// Maintains a stack of timers and provides functions to manage recording scopes
+// by pausing and resuming timers in the chain when entering and leaving a
+// scope.
+class PLATFORM_EXPORT RuntimeCallStats {
+ public:
+ explicit RuntimeCallStats(const base::TickClock*);
+ // Get RuntimeCallStats object associated with the given isolate.
+ static RuntimeCallStats* From(v8::Isolate*);
+
+// The following 3 macros are used to define counters that are used in the
+// bindings layer to measure call stats for IDL interface methods and
+// attributes. Also see documentation for [RuntimeCallStatsCounter] in
+// bindings/IDLExtendedAttributes.md.
+
+// Use this to define a counter for IDL interface methods.
+// [RuntimeCallStatsCounter=MethodCounter] void method() =>
+// BINDINGS_METHOD(V, MethodCounter)
+#define BINDINGS_METHOD(V, counter) V(counter)
+
+// Use this to define a counter for IDL readonly attributes.
+// [RuntimeCallStatsCounter=AttributeCounter] readonly attribute boolean attr =>
+// BINDINGS_READ_ONLY_ATTRIBUTE(V, AttributeCounter)
+#define BINDINGS_READ_ONLY_ATTRIBUTE(V, counter) V(counter##_Getter)
+
+// Use this to define counters for IDL attributes (defines a counter each for
+// getter and setter).
+// [RuntimeCallStats=AttributeCounter] attribute long attr
+// => BINDINGS_ATTRIBUTE(V, AttributeCounter)
+#define BINDINGS_ATTRIBUTE(V, counter) \
+ V(counter##_Getter) \
+ V(counter##_Setter)
+
+// Counters
+
+#define BINDINGS_COUNTERS(V) \
+ V(AssociateObjectWithWrapper) \
+ V(CreateWrapper) \
+ V(GetEventListener) \
+ V(HasInstance) \
+ V(ToExecutionContext) \
+ V(ToV8DOMWindow) \
+ V(ToV8SequenceInternal) \
+ V(SetReturnValueFromStringSlow) \
+ V(V8ExternalStringSlow)
+
+#define GC_COUNTERS(V) \
+ V(CollectGarbage) \
+ V(GcEpilogue) \
+ V(GcPrologue) \
+ V(PerformIdleLazySweep)
+
+#define PARSING_COUNTERS(V) \
+ V(DocumentFragmentParseHTML) \
+ V(ParserAppendChild) \
+ V(ReplaceChildrenWithFragment) \
+ V(HTMLTokenizerNextToken) \
+ V(ConstructTree)
+
+#define STYLE_COUNTERS(V) \
+ V(ProcessStyleSheet) \
+ V(UpdateStyle)
+
+#define LAYOUT_COUNTERS(V) \
+ V(UpdateLayout) \
+ V(UpdateLayerPositionsAfterLayout)
+
+#define CALLBACK_COUNTERS(V) \
+ BINDINGS_METHOD(V, ElementGetBoundingClientRect) \
+ BINDINGS_METHOD(V, EventTargetDispatchEvent) \
+ BINDINGS_METHOD(V, HTMLElementClick) \
+ BINDINGS_METHOD(V, NodeAppendChild) \
+ BINDINGS_METHOD(V, NodeRemoveChild) \
+ BINDINGS_METHOD(V, WindowSetTimeout) \
+ BINDINGS_ATTRIBUTE(V, DocumentCookie) \
+ BINDINGS_ATTRIBUTE(V, ElementInnerHTML) \
+ BINDINGS_READ_ONLY_ATTRIBUTE(V, NodeName)
+
+#define EXTRA_COUNTERS(V) \
+ V(V8) \
+ V(TestCounter1) \
+ V(TestCounter2) \
+ BINDINGS_METHOD(V, BindingsMethodTestCounter) \
+ BINDINGS_READ_ONLY_ATTRIBUTE(V, BindingsReadOnlyAttributeTestCounter) \
+ BINDINGS_ATTRIBUTE(V, BindingsAttributeTestCounter)
+
+#define FOR_EACH_COUNTER(V) \
+ BINDINGS_COUNTERS(V) \
+ GC_COUNTERS(V) \
+ PARSING_COUNTERS(V) \
+ STYLE_COUNTERS(V) \
+ LAYOUT_COUNTERS(V) \
+ CALLBACK_COUNTERS(V) \
+ EXTRA_COUNTERS(V)
+
+ enum class CounterId : uint16_t {
+#define ADD_ENUM_VALUE(counter) k##counter,
+ FOR_EACH_COUNTER(ADD_ENUM_VALUE)
+#undef ADD_ENUM_VALUE
+ kNumberOfCounters
+ };
+
+ // Enters a new recording scope by pausing the currently running timer that
+ // was started by the current instance, and starting <timer>.
+ // NOTE: Do not use this function directly, use RUNTIME_CALL_STATS_ENTER.
+ void Enter(RuntimeCallTimer* timer, CounterId id) {
+ timer->Start(GetCounter(id), current_timer_);
+ current_timer_ = timer;
+ }
+
+#if BUILDFLAG(RCS_COUNT_EVERYTHING)
+ void Enter(RuntimeCallTimer* timer, const char* id) {
+ timer->Start(GetCounter(id), current_timer_);
+ current_timer_ = timer;
+ }
+#endif
+
+ // Exits the current recording scope, by stopping <timer> (and updating the
+ // counter associated with <timer>) and resuming the timer that was paused
+ // before entering the current scope.
+ // NOTE: Do not use this function directly, use RUNTIME_CALL_STATS_LEAVE.
+ void Leave(RuntimeCallTimer* timer) {
+ DCHECK_EQ(timer, current_timer_);
+ current_timer_ = timer->Stop();
+ }
+
+ // Reset all the counters.
+ void Reset();
+
+ void Dump(TracedValue&) const;
+
+ bool InUse() const { return in_use_; }
+ void SetInUse(bool in_use) { in_use_ = in_use; }
+
+ RuntimeCallCounter* GetCounter(CounterId id) {
+ return &(counters_[static_cast<uint16_t>(id)]);
+ }
+
+ String ToString() const;
+
+ static void SetRuntimeCallStatsForTesting();
+ static void ClearRuntimeCallStatsForTesting();
+
+#if BUILDFLAG(RCS_COUNT_EVERYTHING)
+ // Look up counter in counter map. If counter doesn't exist, a new counter is
+ // created and inserted into the map.
+ RuntimeCallCounter* GetCounter(const char* name);
+#endif
+
+ const base::TickClock* clock() const { return clock_; }
+
+ private:
+ RuntimeCallTimer* current_timer_ = nullptr;
+ bool in_use_ = false;
+ RuntimeCallCounter counters_[static_cast<int>(CounterId::kNumberOfCounters)];
+ static const int number_of_counters_ =
+ static_cast<int>(CounterId::kNumberOfCounters);
+ const base::TickClock* clock_ = nullptr;
+
+#if BUILDFLAG(RCS_COUNT_EVERYTHING)
+ typedef HashMap<const char*, std::unique_ptr<RuntimeCallCounter>> CounterMap;
+ CounterMap counter_map_;
+
+ Vector<RuntimeCallCounter*> CounterMapToSortedArray() const;
+ void AddCounterMapStatsToBuilder(StringBuilder&) const;
+#endif
+};
+
+// A utility class that creates a RuntimeCallTimer and uses it with
+// RuntimeCallStats to measure execution time of a C++ scope.
+// Do not use this class directly, use RUNTIME_CALL_TIMER_SCOPE instead.
+class PLATFORM_EXPORT RuntimeCallTimerScope {
+ public:
+ RuntimeCallTimerScope(RuntimeCallStats* stats,
+ RuntimeCallStats::CounterId counter)
+ : call_stats_(stats), timer_(stats->clock()) {
+ call_stats_->Enter(&timer_, counter);
+ }
+#if BUILDFLAG(RCS_COUNT_EVERYTHING)
+ RuntimeCallTimerScope(RuntimeCallStats* stats, const char* counterName)
+ : call_stats_(stats), timer_(stats->clock()) {
+ call_stats_->Enter(&timer_, counterName);
+ }
+#endif
+ ~RuntimeCallTimerScope() { call_stats_->Leave(&timer_); }
+
+ private:
+ RuntimeCallStats* call_stats_;
+ RuntimeCallTimer timer_;
+};
+
+// Creates scoped begin and end trace events. The end trace event also contains
+// a dump of RuntimeCallStats collected to that point (and the stats are reset
+// before sending a begin event). Use this to define regions where
+// RuntimeCallStats data is collected and dumped through tracing.
+// NOTE: Nested scoped tracers will not send events of their own, the stats
+// collected in their scopes will be dumped by the root tracer scope.
+class PLATFORM_EXPORT RuntimeCallStatsScopedTracer {
+ public:
+ explicit RuntimeCallStatsScopedTracer(v8::Isolate* isolate) {
+ bool category_group_enabled;
+ TRACE_EVENT_CATEGORY_GROUP_ENABLED(s_category_group_,
+ &category_group_enabled);
+ if (LIKELY(!category_group_enabled ||
+ !RuntimeEnabledFeatures::BlinkRuntimeCallStatsEnabled()))
+ return;
+
+ RuntimeCallStats* stats = RuntimeCallStats::From(isolate);
+ if (!stats->InUse()) {
+ stats_ = stats;
+ AddBeginTraceEvent();
+ }
+ }
+
+ ~RuntimeCallStatsScopedTracer() {
+ if (stats_)
+ AddEndTraceEvent();
+ }
+
+ private:
+ void AddBeginTraceEvent();
+ void AddEndTraceEvent();
+
+ static const char* const s_category_group_;
+ static const char* const s_name_;
+
+ RuntimeCallStats* stats_ = nullptr;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_RUNTIME_CALL_STATS_H_
diff --git a/chromium/third_party/blink/renderer/platform/bindings/runtime_call_stats_test.cc b/chromium/third_party/blink/renderer/platform/bindings/runtime_call_stats_test.cc
new file mode 100644
index 00000000000..fe703d6424c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/runtime_call_stats_test.cc
@@ -0,0 +1,330 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/bindings/runtime_call_stats.h"
+
+#include "base/test/simple_test_tick_clock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+namespace {
+
+RuntimeCallStats::CounterId test_counter_1_id =
+ RuntimeCallStats::CounterId::kTestCounter1;
+RuntimeCallStats::CounterId test_counter_2_id =
+ RuntimeCallStats::CounterId::kTestCounter2;
+
+} // namespace
+
+class RuntimeCallStatsTest : public testing::Test {
+ public:
+ void SetUp() override {
+ // Add one millisecond because RuntimeCallTimer uses |start_ticks_| =
+ // TimeTicks() to represent that the timer is not running.
+ clock_.SetNowTicks(base::TimeTicks() + TimeDelta::FromMilliseconds(1));
+ }
+
+ void TearDown() override {
+ features_backup_.Restore();
+ }
+
+ void AdvanceClock(int milliseconds) {
+ clock_.Advance(TimeDelta::FromMilliseconds(milliseconds));
+ }
+
+ const base::TickClock* clock() { return &clock_; }
+
+ private:
+ RuntimeEnabledFeatures::Backup features_backup_;
+ base::SimpleTestTickClock clock_;
+};
+
+TEST_F(RuntimeCallStatsTest, InitialCountShouldBeZero) {
+ RuntimeCallCounter counter("counter");
+ EXPECT_EQ(0ul, counter.GetCount());
+}
+
+TEST_F(RuntimeCallStatsTest, StatsCounterNameIsCorrect) {
+ RuntimeCallStats stats(clock());
+ EXPECT_STREQ("Blink_TestCounter1",
+ stats.GetCounter(test_counter_1_id)->GetName());
+}
+
+TEST_F(RuntimeCallStatsTest, TestBindingsCountersForMethods) {
+ RuntimeCallStats stats(clock());
+ RuntimeCallCounter* method_counter =
+ stats.GetCounter(RuntimeCallStats::CounterId::kBindingsMethodTestCounter);
+ EXPECT_STREQ("Blink_BindingsMethodTestCounter", method_counter->GetName());
+}
+
+TEST_F(RuntimeCallStatsTest, TestBindingsCountersForReadOnlyAttributes) {
+ RuntimeCallStats stats(clock());
+ RuntimeCallCounter* getter_counter =
+ stats.GetCounter(RuntimeCallStats::CounterId::
+ kBindingsReadOnlyAttributeTestCounter_Getter);
+ EXPECT_STREQ("Blink_BindingsReadOnlyAttributeTestCounter_Getter",
+ getter_counter->GetName());
+}
+
+TEST_F(RuntimeCallStatsTest, TestBindingsCountersForAttributes) {
+ RuntimeCallStats stats(clock());
+ RuntimeCallCounter* getter_counter = stats.GetCounter(
+ RuntimeCallStats::CounterId::kBindingsAttributeTestCounter_Getter);
+ RuntimeCallCounter* setter_counter = stats.GetCounter(
+ RuntimeCallStats::CounterId::kBindingsAttributeTestCounter_Setter);
+ EXPECT_STREQ("Blink_BindingsAttributeTestCounter_Getter",
+ getter_counter->GetName());
+ EXPECT_STREQ("Blink_BindingsAttributeTestCounter_Setter",
+ setter_counter->GetName());
+}
+
+TEST_F(RuntimeCallStatsTest, CountIsUpdatedAfterLeave) {
+ RuntimeCallTimer timer(clock());
+ RuntimeCallStats stats(clock());
+ RuntimeCallCounter* counter = stats.GetCounter(test_counter_1_id);
+
+ stats.Enter(&timer, test_counter_1_id);
+ EXPECT_EQ(0ul, counter->GetCount());
+ stats.Leave(&timer);
+ EXPECT_EQ(1ul, counter->GetCount());
+}
+
+TEST_F(RuntimeCallStatsTest, TimeIsUpdatedAfterLeave) {
+ RuntimeCallStats stats(clock());
+ RuntimeCallTimer timer(clock());
+ RuntimeCallCounter* counter = stats.GetCounter(test_counter_1_id);
+
+ stats.Enter(&timer, test_counter_1_id);
+ AdvanceClock(50);
+ stats.Leave(&timer);
+ EXPECT_EQ(50, counter->GetTime().InMilliseconds());
+}
+
+TEST_F(RuntimeCallStatsTest, CountAndTimeAreUpdatedAfterMultipleExecutions) {
+ RuntimeCallStats stats(clock());
+ RuntimeCallCounter* counter = stats.GetCounter(test_counter_1_id);
+
+ const unsigned func_duration = 20;
+ const unsigned loops = 5;
+
+ RuntimeCallStatsTest* test = this;
+ auto func = [&stats, func_duration, test]() {
+ RuntimeCallTimer timer(test->clock());
+ stats.Enter(&timer, test_counter_1_id);
+ test->AdvanceClock(func_duration);
+ stats.Leave(&timer);
+ };
+
+ for (unsigned i = 0; i < loops; i++)
+ func();
+
+ EXPECT_EQ((uint64_t)loops, counter->GetCount());
+ EXPECT_EQ(loops * func_duration, counter->GetTime().InMilliseconds());
+}
+
+TEST_F(RuntimeCallStatsTest, NestedTimersTest) {
+ RuntimeCallStats stats(clock());
+ RuntimeCallCounter* outer_counter = stats.GetCounter(test_counter_1_id);
+ RuntimeCallCounter* inner_counter = stats.GetCounter(test_counter_2_id);
+
+ const unsigned inner_func_duration = 50;
+ const unsigned outer_func_duration = 20;
+
+ RuntimeCallStatsTest* test = this;
+ auto inner_func = [&stats, inner_func_duration, test]() {
+ RuntimeCallTimer timer(test->clock());
+ stats.Enter(&timer, test_counter_2_id);
+ test->AdvanceClock(inner_func_duration);
+ stats.Leave(&timer);
+ };
+
+ auto outer_func = [&stats, &inner_func, outer_func_duration, test]() {
+ RuntimeCallTimer timer(test->clock());
+ stats.Enter(&timer, test_counter_1_id);
+ inner_func();
+ test->AdvanceClock(outer_func_duration);
+ stats.Leave(&timer);
+ };
+
+ outer_func();
+
+ EXPECT_EQ(1ul, outer_counter->GetCount());
+ EXPECT_EQ(1ul, inner_counter->GetCount());
+ EXPECT_EQ(outer_func_duration, outer_counter->GetTime().InMilliseconds());
+ EXPECT_EQ(inner_func_duration, inner_counter->GetTime().InMilliseconds());
+}
+
+TEST_F(RuntimeCallStatsTest, RuntimeCallTimerScopeTest) {
+ RuntimeCallStats stats(clock());
+ RuntimeCallCounter* counter = stats.GetCounter(test_counter_1_id);
+
+ RuntimeCallStatsTest* test = this;
+ auto func = [&stats, test]() {
+ RuntimeCallTimerScope scope(&stats, test_counter_1_id);
+ test->AdvanceClock(50);
+ };
+
+ func();
+
+ EXPECT_EQ(1ul, counter->GetCount());
+ EXPECT_EQ(50, counter->GetTime().InMilliseconds());
+
+ func();
+
+ EXPECT_EQ(2ul, counter->GetCount());
+ EXPECT_EQ(100, counter->GetTime().InMilliseconds());
+}
+
+TEST_F(RuntimeCallStatsTest, RecursiveFunctionWithScopeTest) {
+ RuntimeCallStats stats(clock());
+ RuntimeCallCounter* counter = stats.GetCounter(test_counter_1_id);
+
+ RuntimeCallStatsTest* test = this;
+ std::function<void(int)> recursive_func;
+ recursive_func = [&stats, &recursive_func, test](int x) {
+ RuntimeCallTimerScope scope(&stats, test_counter_1_id);
+ if (x <= 0)
+ return;
+ test->AdvanceClock(50);
+ recursive_func(x - 1);
+ };
+ recursive_func(5);
+
+ EXPECT_EQ(6ul, counter->GetCount());
+ EXPECT_EQ(250, counter->GetTime().InMilliseconds());
+}
+
+TEST_F(RuntimeCallStatsTest, ReuseTimer) {
+ RuntimeCallStats stats(clock());
+ RuntimeCallTimer timer(clock());
+ RuntimeCallCounter* counter1 = stats.GetCounter(test_counter_1_id);
+ RuntimeCallCounter* counter2 = stats.GetCounter(test_counter_2_id);
+
+ stats.Enter(&timer, test_counter_1_id);
+ AdvanceClock(50);
+ stats.Leave(&timer);
+
+ timer.Reset();
+
+ stats.Enter(&timer, test_counter_2_id);
+ AdvanceClock(25);
+ stats.Leave(&timer);
+
+ EXPECT_EQ(1ul, counter1->GetCount());
+ EXPECT_EQ(1ul, counter2->GetCount());
+ EXPECT_EQ(50, counter1->GetTime().InMilliseconds());
+ EXPECT_EQ(25, counter2->GetTime().InMilliseconds());
+}
+
+TEST_F(RuntimeCallStatsTest, ResetCallStats) {
+ RuntimeCallStats stats(clock());
+ RuntimeCallCounter* counter1 = stats.GetCounter(test_counter_1_id);
+ RuntimeCallCounter* counter2 = stats.GetCounter(test_counter_2_id);
+
+ {
+ RuntimeCallTimerScope scope1(&stats, test_counter_1_id);
+ RuntimeCallTimerScope scope2(&stats, test_counter_2_id);
+ }
+
+ EXPECT_EQ(1ul, counter1->GetCount());
+ EXPECT_EQ(1ul, counter2->GetCount());
+
+ stats.Reset();
+
+ EXPECT_EQ(0ul, counter1->GetCount());
+ EXPECT_EQ(0ul, counter2->GetCount());
+}
+
+TEST_F(RuntimeCallStatsTest, TestEnterAndLeaveMacrosWithCallStatsDisabled) {
+ ScopedBlinkRuntimeCallStatsForTest blink_runtime_call_stats(false);
+ RuntimeCallStats stats(clock());
+ RuntimeCallCounter* counter = stats.GetCounter(test_counter_1_id);
+ RuntimeCallTimer timer(clock());
+
+ RUNTIME_CALL_STATS_ENTER_WITH_RCS(&stats, &timer, test_counter_1_id);
+ AdvanceClock(25);
+ RUNTIME_CALL_STATS_LEAVE_WITH_RCS(&stats, &timer);
+
+ EXPECT_EQ(0ul, counter->GetCount());
+ EXPECT_EQ(0, counter->GetTime().InMilliseconds());
+}
+
+TEST_F(RuntimeCallStatsTest, TestEnterAndLeaveMacrosWithCallStatsEnabled) {
+ ScopedBlinkRuntimeCallStatsForTest blink_runtime_call_stats(true);
+ RuntimeCallStats stats(clock());
+ RuntimeCallCounter* counter = stats.GetCounter(test_counter_1_id);
+ RuntimeCallTimer timer(clock());
+
+ RUNTIME_CALL_STATS_ENTER_WITH_RCS(&stats, &timer, test_counter_1_id);
+ AdvanceClock(25);
+ RUNTIME_CALL_STATS_LEAVE_WITH_RCS(&stats, &timer);
+
+ EXPECT_EQ(1ul, counter->GetCount());
+ EXPECT_EQ(25, counter->GetTime().InMilliseconds());
+}
+
+TEST_F(RuntimeCallStatsTest, TestScopeMacroWithCallStatsDisabled) {
+ ScopedBlinkRuntimeCallStatsForTest blink_runtime_call_stats(false);
+ RuntimeCallStats stats(clock());
+ RuntimeCallCounter* counter = stats.GetCounter(test_counter_1_id);
+
+ {
+ RUNTIME_CALL_TIMER_SCOPE_WITH_RCS(&stats, test_counter_1_id);
+ AdvanceClock(25);
+ }
+
+ EXPECT_EQ(0ul, counter->GetCount());
+ EXPECT_EQ(0, counter->GetTime().InMilliseconds());
+}
+
+TEST_F(RuntimeCallStatsTest, TestScopeMacroWithCallStatsEnabled) {
+ ScopedBlinkRuntimeCallStatsForTest blink_runtime_call_stats(true);
+ RuntimeCallStats stats(clock());
+ RuntimeCallCounter* counter = stats.GetCounter(test_counter_1_id);
+
+ {
+ RUNTIME_CALL_TIMER_SCOPE_WITH_RCS(&stats, test_counter_1_id);
+ AdvanceClock(25);
+ }
+
+ EXPECT_EQ(1ul, counter->GetCount());
+ EXPECT_EQ(25, counter->GetTime().InMilliseconds());
+}
+
+TEST_F(RuntimeCallStatsTest, TestScopeWithOptionalMacroWithCallStatsDisabled) {
+ ScopedBlinkRuntimeCallStatsForTest blink_runtime_call_stats(false);
+ RuntimeCallStats stats(clock());
+ RuntimeCallCounter* counter = stats.GetCounter(test_counter_1_id);
+
+ {
+ Optional<RuntimeCallTimerScope> scope;
+ RUNTIME_CALL_TIMER_SCOPE_WITH_OPTIONAL_RCS(scope, &stats,
+ test_counter_1_id);
+ AdvanceClock(25);
+ }
+
+ EXPECT_EQ(0ul, counter->GetCount());
+ EXPECT_EQ(0, counter->GetTime().InMilliseconds());
+}
+
+TEST_F(RuntimeCallStatsTest, TestScopeWithOptionalMacroWithCallStatsEnabled) {
+ ScopedBlinkRuntimeCallStatsForTest blink_runtime_call_stats(true);
+ RuntimeCallStats stats(clock());
+ RuntimeCallCounter* counter = stats.GetCounter(test_counter_1_id);
+
+ {
+ Optional<RuntimeCallTimerScope> scope;
+ RUNTIME_CALL_TIMER_SCOPE_WITH_OPTIONAL_RCS(scope, &stats,
+ test_counter_1_id);
+ AdvanceClock(25);
+ }
+
+ EXPECT_EQ(1ul, counter->GetCount());
+ EXPECT_EQ(25, counter->GetTime().InMilliseconds());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/bindings/scoped_persistent.h b/chromium/third_party/blink/renderer/platform/bindings/scoped_persistent.h
new file mode 100644
index 00000000000..137745250cd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/scoped_persistent.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCOPED_PERSISTENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCOPED_PERSISTENT_H_
+
+#include <memory>
+
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+// Holds a persistent handle to a V8 object; use ScopedPersistent instead of
+// directly using v8::Persistent. Introducing a (non-weak) ScopedPersistent
+// has a risk of producing memory leaks, ask blink-reviews-bindings@ for a
+// review.
+template <typename T>
+class ScopedPersistent {
+ USING_FAST_MALLOC(ScopedPersistent);
+ WTF_MAKE_NONCOPYABLE(ScopedPersistent);
+
+ public:
+ ScopedPersistent() = default;
+
+ ScopedPersistent(v8::Isolate* isolate, v8::Local<T> handle)
+ : handle_(isolate, handle) {}
+
+ ScopedPersistent(v8::Isolate* isolate, v8::MaybeLocal<T> maybe) {
+ v8::Local<T> local;
+ if (maybe.ToLocal(&local))
+ handle_.Reset(isolate, local);
+ }
+
+ ~ScopedPersistent() { Clear(); }
+
+ ALWAYS_INLINE v8::Local<T> NewLocal(v8::Isolate* isolate) const {
+ return v8::Local<T>::New(isolate, handle_);
+ }
+
+ // If you don't need to get weak callback, use setPhantom instead.
+ // setPhantom is faster than setWeak.
+ template <typename P>
+ void SetWeak(P* parameters,
+ void (*callback)(const v8::WeakCallbackInfo<P>&),
+ v8::WeakCallbackType type = v8::WeakCallbackType::kParameter) {
+ handle_.SetWeak(parameters, callback, type);
+ }
+
+ // Turns this handle into a weak phantom handle without
+ // finalization callback.
+ void SetPhantom() { handle_.SetWeak(); }
+
+ void ClearWeak() { handle_.template ClearWeak<void>(); }
+
+ bool IsEmpty() const { return handle_.IsEmpty(); }
+ bool IsWeak() const { return handle_.IsWeak(); }
+
+ void Set(v8::Isolate* isolate, v8::Local<T> handle) {
+ handle_.Reset(isolate, handle);
+ }
+
+ // Note: This is clear in the std::unique_ptr sense, not the v8::Local sense.
+ void Clear() { handle_.Reset(); }
+
+ bool operator==(const ScopedPersistent<T>& other) {
+ return handle_ == other.handle_;
+ }
+
+ template <class S>
+ bool operator==(const v8::Local<S> other) const {
+ return handle_ == other;
+ }
+
+ ALWAYS_INLINE v8::Persistent<T>& Get() { return handle_; }
+
+ private:
+ v8::Persistent<T> handle_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCOPED_PERSISTENT_H_
diff --git a/chromium/third_party/blink/renderer/platform/bindings/script_forbidden_scope.cc b/chromium/third_party/blink/renderer/platform/bindings/script_forbidden_scope.cc
new file mode 100644
index 00000000000..0b4b882265e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/script_forbidden_scope.cc
@@ -0,0 +1,23 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h"
+
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/thread_specific.h"
+
+namespace blink {
+
+unsigned ScriptForbiddenScope::g_main_thread_counter_ = 0;
+
+unsigned& ScriptForbiddenScope::GetMutableCounter() {
+ if (IsMainThread())
+ return g_main_thread_counter_;
+
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(WTF::ThreadSpecific<unsigned>,
+ script_forbidden_counter_, ());
+ return *script_forbidden_counter_;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/bindings/script_forbidden_scope.h b/chromium/third_party/blink/renderer/platform/bindings/script_forbidden_scope.h
new file mode 100644
index 00000000000..f778551eb20
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/script_forbidden_scope.h
@@ -0,0 +1,68 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCRIPT_FORBIDDEN_SCOPE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCRIPT_FORBIDDEN_SCOPE_H_
+
+#include "base/macros.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/auto_reset.h"
+#include "third_party/blink/renderer/platform/wtf/stack_util.h"
+#include "third_party/blink/renderer/platform/wtf/wtf.h"
+
+namespace blink {
+
+// Scoped disabling of script execution.
+class PLATFORM_EXPORT ScriptForbiddenScope final {
+ STACK_ALLOCATED();
+ DISALLOW_COPY_AND_ASSIGN(ScriptForbiddenScope);
+
+ public:
+ ScriptForbiddenScope() { Enter(); }
+ ~ScriptForbiddenScope() { Exit(); }
+
+ class PLATFORM_EXPORT AllowUserAgentScript final {
+ STACK_ALLOCATED();
+ DISALLOW_COPY_AND_ASSIGN(AllowUserAgentScript);
+
+ public:
+ AllowUserAgentScript() : saved_counter_(&GetMutableCounter(), 0) {}
+ ~AllowUserAgentScript() { DCHECK(!IsScriptForbidden()); }
+
+ private:
+ AutoReset<unsigned> saved_counter_;
+ };
+
+ static bool IsScriptForbidden() {
+ if (LIKELY(!WTF::MayNotBeMainThread()))
+ return g_main_thread_counter_ > 0;
+ return GetMutableCounter() > 0;
+ }
+
+ // DO NOT USE THESE FUNCTIONS FROM OUTSIDE OF THIS CLASS.
+ static void Enter() {
+ if (LIKELY(!WTF::MayNotBeMainThread())) {
+ ++g_main_thread_counter_;
+ } else {
+ ++GetMutableCounter();
+ }
+ }
+ static void Exit() {
+ DCHECK(IsScriptForbidden());
+ if (LIKELY(!WTF::MayNotBeMainThread())) {
+ --g_main_thread_counter_;
+ } else {
+ --GetMutableCounter();
+ }
+ }
+
+ private:
+ static unsigned& GetMutableCounter();
+ static unsigned g_main_thread_counter_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCRIPT_FORBIDDEN_SCOPE_H_
diff --git a/chromium/third_party/blink/renderer/platform/bindings/script_promise_properties.h b/chromium/third_party/blink/renderer/platform/bindings/script_promise_properties.h
new file mode 100644
index 00000000000..1506c3a3e18
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/script_promise_properties.h
@@ -0,0 +1,18 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCRIPT_PROMISE_PROPERTIES_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCRIPT_PROMISE_PROPERTIES_H_
+
+// See ScriptPromiseProperty.h
+#define SCRIPT_PROMISE_PROPERTIES(P, ...) \
+ P(ScriptPromise, kReady##__VA_ARGS__) \
+ P(ScriptPromise, kClosed##__VA_ARGS__) \
+ P(ScriptPromise, kFinished##__VA_ARGS__) \
+ P(ScriptPromise, kLoaded##__VA_ARGS__) \
+ P(ScriptPromise, kReleased##__VA_ARGS__) \
+ P(ScriptPromise, kUserChoice##__VA_ARGS__) \
+ P(ScriptPromise, kPreloadResponse##__VA_ARGS__)
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCRIPT_PROMISE_PROPERTIES_H_
diff --git a/chromium/third_party/blink/renderer/platform/bindings/script_state.cc b/chromium/third_party/blink/renderer/platform/bindings/script_state.cc
new file mode 100644
index 00000000000..e6fc3e248a2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/script_state.cc
@@ -0,0 +1,58 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/bindings/script_state.h"
+
+#include "third_party/blink/renderer/platform/bindings/v8_binding.h"
+
+namespace blink {
+
+scoped_refptr<ScriptState> ScriptState::Create(
+ v8::Local<v8::Context> context,
+ scoped_refptr<DOMWrapperWorld> world) {
+ scoped_refptr<ScriptState> script_state =
+ base::AdoptRef(new ScriptState(context, std::move(world)));
+ // This AddRef() is for keeping this ScriptState alive as long as the
+ // v8::Context is alive. This is Release()d in the weak callback of the
+ // v8::Context.
+ script_state->AddRef();
+ return script_state;
+}
+
+static void DerefCallback(const v8::WeakCallbackInfo<ScriptState>& data) {
+ data.GetParameter()->Release();
+}
+
+static void ContextCollectedCallback(
+ const v8::WeakCallbackInfo<ScriptState>& data) {
+ data.GetParameter()->ClearContext();
+ data.SetSecondPassCallback(DerefCallback);
+}
+
+ScriptState::ScriptState(v8::Local<v8::Context> context,
+ scoped_refptr<DOMWrapperWorld> world)
+ : isolate_(context->GetIsolate()),
+ context_(isolate_, context),
+ world_(std::move(world)),
+ per_context_data_(V8PerContextData::Create(context)) {
+ DCHECK(world_);
+ context_.SetWeak(this, &ContextCollectedCallback);
+ context->SetAlignedPointerInEmbedderData(kV8ContextPerContextDataIndex, this);
+}
+
+ScriptState::~ScriptState() {
+ DCHECK(!per_context_data_);
+ DCHECK(context_.IsEmpty());
+}
+
+void ScriptState::DetachGlobalObject() {
+ DCHECK(!context_.IsEmpty());
+ GetContext()->DetachGlobal();
+}
+
+void ScriptState::DisposePerContextData() {
+ per_context_data_ = nullptr;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/bindings/script_state.h b/chromium/third_party/blink/renderer/platform/bindings/script_state.h
new file mode 100644
index 00000000000..87187ff4296
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/script_state.h
@@ -0,0 +1,199 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCRIPT_STATE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCRIPT_STATE_H_
+
+#include <memory>
+
+#include "third_party/blink/renderer/platform/bindings/scoped_persistent.h"
+#include "third_party/blink/renderer/platform/bindings/v8_per_context_data.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+class DOMWrapperWorld;
+class ScriptValue;
+
+// ScriptState is an abstraction class that holds all information about script
+// exectuion (e.g., v8::Isolate, v8::Context, DOMWrapperWorld, ExecutionContext
+// etc). If you need any info about the script execution, you're expected to
+// pass around ScriptState in the code base. ScriptState is in a 1:1
+// relationship with v8::Context.
+//
+// When you need ScriptState, you can add [CallWith=ScriptState] to IDL files
+// and pass around ScriptState into a place where you need ScriptState.
+//
+// In some cases, you need ScriptState in code that doesn't have any JavaScript
+// on the stack. Then you can store ScriptState on a C++ object using
+// scoped_refptr<ScriptState>.
+//
+// class SomeObject {
+// void someMethod(ScriptState* scriptState) {
+// script_state_ = scriptState; // Record the ScriptState.
+// ...;
+// }
+//
+// void asynchronousMethod() {
+// if (!script_state_->contextIsValid()) {
+// // It's possible that the context is already gone.
+// return;
+// }
+// // Enter the ScriptState.
+// ScriptState::Scope scope(script_state_.get());
+// // Do V8 related things.
+// ToV8(...);
+// }
+// scoped_refptr<ScriptState> script_state_;
+// };
+//
+// You should not store ScriptState on a C++ object that can be accessed
+// by multiple worlds. For example, you can store ScriptState on
+// ScriptPromiseResolver, ScriptValue etc because they can be accessed from one
+// world. However, you cannot store ScriptState on a DOM object that has
+// an IDL interface because the DOM object can be accessed from multiple
+// worlds. If ScriptState of one world "leak"s to another world, you will
+// end up with leaking any JavaScript objects from one Chrome extension
+// to another Chrome extension, which is a severe security bug.
+//
+// Lifetime:
+// ScriptState is created when v8::Context is created.
+// ScriptState is destroyed when v8::Context is garbage-collected and
+// all V8 proxy objects that have references to the ScriptState are destructed.
+class PLATFORM_EXPORT ScriptState : public RefCounted<ScriptState> {
+ WTF_MAKE_NONCOPYABLE(ScriptState);
+
+ public:
+ class Scope {
+ STACK_ALLOCATED();
+
+ public:
+ // You need to make sure that scriptState->context() is not empty before
+ // creating a Scope.
+ explicit Scope(ScriptState* script_state)
+ : handle_scope_(script_state->GetIsolate()),
+ context_(script_state->GetContext()) {
+ DCHECK(script_state->ContextIsValid());
+ context_->Enter();
+ }
+
+ ~Scope() { context_->Exit(); }
+
+ private:
+ v8::HandleScope handle_scope_;
+ v8::Local<v8::Context> context_;
+ };
+
+ static scoped_refptr<ScriptState> Create(v8::Local<v8::Context>,
+ scoped_refptr<DOMWrapperWorld>);
+ virtual ~ScriptState();
+
+ static ScriptState* Current(v8::Isolate* isolate) // DEPRECATED
+ {
+ return From(isolate->GetCurrentContext());
+ }
+
+ static ScriptState* ForCurrentRealm(
+ const v8::FunctionCallbackInfo<v8::Value>& info) {
+ return From(info.GetIsolate()->GetCurrentContext());
+ }
+
+ static ScriptState* ForRelevantRealm(
+ const v8::FunctionCallbackInfo<v8::Value>& info) {
+ return From(info.Holder()->CreationContext());
+ }
+
+ static ScriptState* ForRelevantRealm(
+ const v8::PropertyCallbackInfo<v8::Value>& info) {
+ return From(info.Holder()->CreationContext());
+ }
+
+ static ScriptState* ForRelevantRealm(
+ const v8::PropertyCallbackInfo<void>& info) {
+ return From(info.Holder()->CreationContext());
+ }
+
+ static ScriptState* From(v8::Local<v8::Context> context) {
+ DCHECK(!context.IsEmpty());
+ ScriptState* script_state =
+ static_cast<ScriptState*>(context->GetAlignedPointerFromEmbedderData(
+ kV8ContextPerContextDataIndex));
+ // ScriptState::from() must not be called for a context that does not have
+ // valid embedder data in the embedder field.
+ SECURITY_CHECK(script_state);
+ SECURITY_CHECK(script_state->context_ == context);
+ return script_state;
+ }
+
+ v8::Isolate* GetIsolate() const { return isolate_; }
+ DOMWrapperWorld& World() const { return *world_; }
+
+ // This can return an empty handle if the v8::Context is gone.
+ v8::Local<v8::Context> GetContext() const {
+ return context_.NewLocal(isolate_);
+ }
+ bool ContextIsValid() const {
+ return !context_.IsEmpty() && per_context_data_;
+ }
+ void DetachGlobalObject();
+ void ClearContext() { return context_.Clear(); }
+
+ V8PerContextData* PerContextData() const { return per_context_data_.get(); }
+ void DisposePerContextData();
+
+ protected:
+ ScriptState(v8::Local<v8::Context>, scoped_refptr<DOMWrapperWorld>);
+
+ private:
+ v8::Isolate* isolate_;
+ // This persistent handle is weak.
+ ScopedPersistent<v8::Context> context_;
+
+ // This RefPtr doesn't cause a cycle because all persistent handles that
+ // DOMWrapperWorld holds are weak.
+ scoped_refptr<DOMWrapperWorld> world_;
+
+ // This std::unique_ptr causes a cycle:
+ // V8PerContextData --(Persistent)--> v8::Context --(RefPtr)--> ScriptState
+ // --(std::unique_ptr)--> V8PerContextData
+ // So you must explicitly clear the std::unique_ptr by calling
+ // disposePerContextData() once you no longer need V8PerContextData.
+ // Otherwise, the v8::Context will leak.
+ std::unique_ptr<V8PerContextData> per_context_data_;
+};
+
+// ScriptStateProtectingContext keeps the context associated with the
+// ScriptState alive. You need to call clear() once you no longer need the
+// context. Otherwise, the context will leak.
+class ScriptStateProtectingContext {
+ WTF_MAKE_NONCOPYABLE(ScriptStateProtectingContext);
+ USING_FAST_MALLOC(ScriptStateProtectingContext);
+
+ public:
+ ScriptStateProtectingContext(ScriptState* script_state)
+ : script_state_(script_state) {
+ if (script_state_) {
+ context_.Set(script_state_->GetIsolate(), script_state_->GetContext());
+ context_.Get().AnnotateStrongRetainer(
+ "blink::ScriptStateProtectingContext::context_");
+ }
+ }
+
+ ScriptState* operator->() const { return script_state_.get(); }
+ ScriptState* Get() const { return script_state_.get(); }
+ void Clear() {
+ script_state_ = nullptr;
+ context_.Clear();
+ }
+
+ private:
+ scoped_refptr<ScriptState> script_state_;
+ ScopedPersistent<v8::Context> context_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCRIPT_STATE_H_
diff --git a/chromium/third_party/blink/renderer/platform/bindings/script_wrappable.cc b/chromium/third_party/blink/renderer/platform/bindings/script_wrappable.cc
new file mode 100644
index 00000000000..950c3e88847
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/script_wrappable.cc
@@ -0,0 +1,52 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+
+#include "third_party/blink/renderer/platform/bindings/dom_data_store.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable_visitor.h"
+#include "third_party/blink/renderer/platform/bindings/v8_dom_wrapper.h"
+
+namespace blink {
+
+struct SameSizeAsScriptWrappable {
+ virtual ~SameSizeAsScriptWrappable() = default;
+ v8::Persistent<v8::Object> main_world_wrapper_;
+};
+
+static_assert(sizeof(ScriptWrappable) <= sizeof(SameSizeAsScriptWrappable),
+ "ScriptWrappable should stay small");
+
+v8::Local<v8::Object> ScriptWrappable::Wrap(
+ v8::Isolate* isolate,
+ v8::Local<v8::Object> creation_context) {
+ const WrapperTypeInfo* wrapper_type_info = this->GetWrapperTypeInfo();
+
+ DCHECK(!DOMDataStore::ContainsWrapper(this, isolate));
+
+ v8::Local<v8::Object> wrapper =
+ V8DOMWrapper::CreateWrapper(isolate, creation_context, wrapper_type_info);
+ DCHECK(!wrapper.IsEmpty());
+ return AssociateWithWrapper(isolate, wrapper_type_info, wrapper);
+}
+
+v8::Local<v8::Object> ScriptWrappable::AssociateWithWrapper(
+ v8::Isolate* isolate,
+ const WrapperTypeInfo* wrapper_type_info,
+ v8::Local<v8::Object> wrapper) {
+ return V8DOMWrapper::AssociateObjectWithWrapper(isolate, this,
+ wrapper_type_info, wrapper);
+}
+
+void ScriptWrappable::TraceWrappers(
+ const ScriptWrappableVisitor* visitor) const {
+ visitor->TraceWrappers(main_world_wrapper_);
+ DOMWrapperWorld::TraceWrappers(this, visitor);
+}
+
+const char* ScriptWrappable::NameInHeapSnapshot() const {
+ return GetWrapperTypeInfo()->interface_name;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/bindings/script_wrappable.h b/chromium/third_party/blink/renderer/platform/bindings/script_wrappable.h
new file mode 100644
index 00000000000..7cc10a8be5e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/script_wrappable.h
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2010 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCRIPT_WRAPPABLE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCRIPT_WRAPPABLE_H_
+
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/bindings/trace_wrapper_base.h"
+#include "third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h"
+#include "third_party/blink/renderer/platform/bindings/wrapper_type_info.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/type_traits.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+// ScriptWrappable provides a way to map from/to C++ DOM implementation to/from
+// JavaScript object (platform object). ToV8() converts a ScriptWrappable to
+// a v8::Object and toScriptWrappable() converts a v8::Object back to
+// a ScriptWrappable. v8::Object as platform object is called "wrapper object".
+// The wrapper object for the main world is stored in ScriptWrappable. Wrapper
+// objects for other worlds are stored in DOMWrapperMap.
+class PLATFORM_EXPORT ScriptWrappable
+ : public GarbageCollectedFinalized<ScriptWrappable>,
+ public TraceWrapperBase {
+ WTF_MAKE_NONCOPYABLE(ScriptWrappable);
+
+ public:
+ virtual ~ScriptWrappable() = default;
+
+ virtual void Trace(blink::Visitor*) {}
+
+ // Traces wrapper objects corresponding to this ScriptWrappable in all worlds.
+ void TraceWrappers(const ScriptWrappableVisitor*) const override;
+
+ bool IsScriptWrappable() const override { return true; }
+
+ const char* NameInHeapSnapshot() const override;
+
+ template <typename T>
+ T* ToImpl() {
+ // All ScriptWrappables are managed by the Blink GC heap; check that
+ // |T| is a garbage collected type.
+ static_assert(
+ sizeof(T) && WTF::IsGarbageCollectedType<T>::value,
+ "Classes implementing ScriptWrappable must be garbage collected.");
+
+// Check if T* is castable to ScriptWrappable*, which means T doesn't
+// have two or more ScriptWrappable as superclasses. If T has two
+// ScriptWrappable as superclasses, conversions from T* to
+// ScriptWrappable* are ambiguous.
+#if !defined(COMPILER_MSVC)
+ // MSVC 2013 doesn't support static_assert + constexpr well.
+ static_assert(!static_cast<ScriptWrappable*>(static_cast<T*>(nullptr)),
+ "Class T must not have two or more ScriptWrappable as its "
+ "superclasses.");
+#endif
+ return static_cast<T*>(this);
+ }
+
+ // Returns the WrapperTypeInfo of the instance.
+ //
+ // This method must be overridden by DEFINE_WRAPPERTYPEINFO macro.
+ virtual const WrapperTypeInfo* GetWrapperTypeInfo() const = 0;
+
+ // Creates and returns a new wrapper object.
+ virtual v8::Local<v8::Object> Wrap(v8::Isolate*,
+ v8::Local<v8::Object> creation_context);
+
+ // Associates the instance with the given |wrapper| if this instance is not
+ // yet associated with any wrapper. Returns the wrapper already associated
+ // or |wrapper| if not yet associated.
+ // The caller should always use the returned value rather than |wrapper|.
+ WARN_UNUSED_RESULT virtual v8::Local<v8::Object> AssociateWithWrapper(
+ v8::Isolate*,
+ const WrapperTypeInfo*,
+ v8::Local<v8::Object> wrapper);
+
+ // Returns true if the instance needs to be kept alive even when the
+ // instance is unreachable from JavaScript.
+ virtual bool HasPendingActivity() const { return false; }
+
+ // Associates this instance with the given |wrapper| if this instance is not
+ // yet associated with any wrapper. Returns true if the given wrapper is
+ // associated with this instance, or false if this instance is already
+ // associated with a wrapper. In the latter case, |wrapper| will be updated
+ // to the existing wrapper.
+ WARN_UNUSED_RESULT bool SetWrapper(v8::Isolate* isolate,
+ const WrapperTypeInfo* wrapper_type_info,
+ v8::Local<v8::Object>& wrapper) {
+ DCHECK(!wrapper.IsEmpty());
+ if (UNLIKELY(ContainsWrapper())) {
+ wrapper = MainWorldWrapper(isolate);
+ return false;
+ }
+ main_world_wrapper_.Set(isolate, wrapper);
+ DCHECK(ContainsWrapper());
+ wrapper_type_info->ConfigureWrapper(&main_world_wrapper_.Get());
+ return true;
+ }
+
+ // Dissociates the wrapper, if any, from this instance.
+ void UnsetWrapperIfAny() {
+ if (ContainsWrapper()) {
+ main_world_wrapper_.Get().Reset();
+ WrapperTypeInfo::WrapperDestroyed();
+ }
+ }
+
+ bool IsEqualTo(const v8::Local<v8::Object>& other) const {
+ return main_world_wrapper_.Get() == other;
+ }
+
+ bool SetReturnValue(v8::ReturnValue<v8::Value> return_value) {
+ return_value.Set(main_world_wrapper_.Get());
+ return ContainsWrapper();
+ }
+
+ bool ContainsWrapper() const { return !main_world_wrapper_.IsEmpty(); }
+
+ protected:
+ ScriptWrappable() = default;
+
+ private:
+ // These classes are exceptionally allowed to use MainWorldWrapper().
+ friend class DOMDataStore;
+ friend class HeapSnaphotWrapperVisitor;
+ friend class V8HiddenValue;
+ friend class V8PrivateProperty;
+
+ v8::Local<v8::Object> MainWorldWrapper(v8::Isolate* isolate) const {
+ return main_world_wrapper_.NewLocal(isolate);
+ }
+
+ // Only use when really necessary, i.e., when passing over this
+ // ScriptWrappable's reference to V8. Should only be needed by GC
+ // infrastructure.
+ const v8::Persistent<v8::Object>* RawMainWorldWrapper() const {
+ return &main_world_wrapper_.Get();
+ }
+
+ TraceWrapperV8Reference<v8::Object> main_world_wrapper_;
+};
+
+// Defines |GetWrapperTypeInfo| virtual method which returns the WrapperTypeInfo
+// of the instance. Also declares a static member of type WrapperTypeInfo, of
+// which the definition is given by the IDL code generator.
+//
+// All the derived classes of ScriptWrappable, regardless of directly or
+// indirectly, must write this macro in the class definition as long as the
+// class has a corresponding .idl file.
+#define DEFINE_WRAPPERTYPEINFO() \
+ public: \
+ const WrapperTypeInfo* GetWrapperTypeInfo() const override { \
+ return &wrapper_type_info_; \
+ } \
+ \
+ private: \
+ static const WrapperTypeInfo& wrapper_type_info_
+
+// Declares |GetWrapperTypeInfo| method without definition.
+//
+// This macro is used for template classes. e.g. DOMTypedArray<>.
+// To export such a template class X, we need to instantiate X with EXPORT_API,
+// i.e. "extern template class EXPORT_API X;"
+// However, once we instantiate X, we cannot specialize X after
+// the instantiation. i.e. we will see "error: explicit specialization of ...
+// after instantiation". So we cannot define X's s_wrapperTypeInfo in generated
+// code by using specialization. Instead, we need to implement wrapperTypeInfo
+// in X's cpp code, and instantiate X, i.e. "template class X;".
+#define DECLARE_WRAPPERTYPEINFO() \
+ public: \
+ const WrapperTypeInfo* GetWrapperTypeInfo() const override; \
+ \
+ private: \
+ typedef void end_of_declare_wrappertypeinfo_t
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCRIPT_WRAPPABLE_H_
diff --git a/chromium/third_party/blink/renderer/platform/bindings/script_wrappable_marking_visitor.cc b/chromium/third_party/blink/renderer/platform/bindings/script_wrappable_marking_visitor.cc
new file mode 100644
index 00000000000..d7ddc64762c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/script_wrappable_marking_visitor.cc
@@ -0,0 +1,328 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/bindings/script_wrappable_marking_visitor.h"
+
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/bindings/active_script_wrappable_base.h"
+#include "third_party/blink/renderer/platform/bindings/dom_wrapper_map.h"
+#include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h"
+#include "third_party/blink/renderer/platform/bindings/scoped_persistent.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable_visitor_verifier.h"
+#include "third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h"
+#include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
+#include "third_party/blink/renderer/platform/bindings/wrapper_type_info.h"
+#include "third_party/blink/renderer/platform/heap/heap_compact.h"
+#include "third_party/blink/renderer/platform/heap/heap_page.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/scheduler/child/web_scheduler.h"
+#include "third_party/blink/renderer/platform/supplementable.h"
+#include "third_party/blink/renderer/platform/wtf/auto_reset.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+ScriptWrappableMarkingVisitor::~ScriptWrappableMarkingVisitor() = default;
+
+void ScriptWrappableMarkingVisitor::TracePrologue() {
+ // This CHECK ensures that wrapper tracing is not started from scopes
+ // that forbid GC execution, e.g., constructors.
+ CHECK(ThreadState::Current());
+ CHECK(!ThreadState::Current()->IsWrapperTracingForbidden());
+ PerformCleanup();
+
+ CHECK(!tracing_in_progress_);
+ CHECK(!should_cleanup_);
+ CHECK(headers_to_unmark_.IsEmpty());
+ CHECK(marking_deque_.IsEmpty());
+ CHECK(verifier_deque_.IsEmpty());
+ tracing_in_progress_ = true;
+ ThreadState::Current()->SetWrapperTracingInProgress(true);
+}
+
+void ScriptWrappableMarkingVisitor::EnterFinalPause() {
+ CHECK(ThreadState::Current());
+ CHECK(!ThreadState::Current()->IsWrapperTracingForbidden());
+ ActiveScriptWrappableBase::TraceActiveScriptWrappables(isolate_, this);
+}
+
+void ScriptWrappableMarkingVisitor::TraceEpilogue() {
+ CHECK(ThreadState::Current());
+ CHECK(!ThreadState::Current()->IsWrapperTracingForbidden());
+ DCHECK(marking_deque_.IsEmpty());
+#if DCHECK_IS_ON()
+ ScriptWrappableVisitorVerifier verifier;
+ for (auto& marking_data : verifier_deque_) {
+ // Check that all children of this object are marked.
+ marking_data.TraceWrappers(&verifier);
+ }
+#endif
+
+ should_cleanup_ = true;
+ tracing_in_progress_ = false;
+ ThreadState::Current()->SetWrapperTracingInProgress(false);
+ ScheduleIdleLazyCleanup();
+}
+
+void ScriptWrappableMarkingVisitor::AbortTracing() {
+ CHECK(ThreadState::Current());
+ should_cleanup_ = true;
+ tracing_in_progress_ = false;
+ ThreadState::Current()->SetWrapperTracingInProgress(false);
+ PerformCleanup();
+}
+
+size_t ScriptWrappableMarkingVisitor::NumberOfWrappersToTrace() {
+ CHECK(ThreadState::Current());
+ return marking_deque_.size();
+}
+
+void ScriptWrappableMarkingVisitor::PerformCleanup() {
+ if (!should_cleanup_)
+ return;
+
+ CHECK(!tracing_in_progress_);
+ for (auto* header : headers_to_unmark_) {
+ // Dead objects residing in the marking deque may become invalid due to
+ // minor garbage collections and are therefore set to nullptr. We have
+ // to skip over such objects.
+ if (header)
+ header->UnmarkWrapperHeader();
+ }
+
+ headers_to_unmark_.clear();
+ marking_deque_.clear();
+ verifier_deque_.clear();
+ should_cleanup_ = false;
+}
+
+void ScriptWrappableMarkingVisitor::ScheduleIdleLazyCleanup() {
+ WebThread* const thread = Platform::Current()->CurrentThread();
+ // Thread might already be gone, or some threads (e.g. PPAPI) don't have a
+ // scheduler.
+ if (!thread || !thread->Scheduler())
+ return;
+
+ if (idle_cleanup_task_scheduled_)
+ return;
+
+ Platform::Current()->CurrentThread()->Scheduler()->PostIdleTask(
+ FROM_HERE, WTF::Bind(&ScriptWrappableMarkingVisitor::PerformLazyCleanup,
+ WTF::Unretained(this)));
+ idle_cleanup_task_scheduled_ = true;
+}
+
+void ScriptWrappableMarkingVisitor::PerformLazyCleanup(
+ double deadline_seconds) {
+ idle_cleanup_task_scheduled_ = false;
+
+ if (!should_cleanup_)
+ return;
+
+ TRACE_EVENT1("blink_gc,devtools.timeline",
+ "ScriptWrappableMarkingVisitor::performLazyCleanup",
+ "idleDeltaInSeconds",
+ deadline_seconds - CurrentTimeTicksInSeconds());
+
+ const int kDeadlineCheckInterval = 2500;
+ int processed_wrapper_count = 0;
+ for (auto it = headers_to_unmark_.rbegin();
+ it != headers_to_unmark_.rend();) {
+ auto* header = *it;
+ // Dead objects residing in the marking deque may become invalid due to
+ // minor garbage collections and are therefore set to nullptr. We have
+ // to skip over such objects.
+ if (header)
+ header->UnmarkWrapperHeader();
+
+ ++it;
+ headers_to_unmark_.pop_back();
+
+ processed_wrapper_count++;
+ if (processed_wrapper_count % kDeadlineCheckInterval == 0) {
+ if (deadline_seconds <= CurrentTimeTicksInSeconds()) {
+ ScheduleIdleLazyCleanup();
+ return;
+ }
+ }
+ }
+
+ // Unmarked all headers.
+ CHECK(headers_to_unmark_.IsEmpty());
+ marking_deque_.clear();
+ verifier_deque_.clear();
+ should_cleanup_ = false;
+}
+
+void ScriptWrappableMarkingVisitor::RegisterV8Reference(
+ const std::pair<void*, void*>& internal_fields) {
+ if (!tracing_in_progress_) {
+ return;
+ }
+
+ WrapperTypeInfo* wrapper_type_info =
+ reinterpret_cast<WrapperTypeInfo*>(internal_fields.first);
+ if (wrapper_type_info->gin_embedder != gin::GinEmbedder::kEmbedderBlink) {
+ return;
+ }
+ DCHECK(wrapper_type_info->wrapper_class_id == WrapperTypeInfo::kNodeClassId ||
+ wrapper_type_info->wrapper_class_id ==
+ WrapperTypeInfo::kObjectClassId);
+
+ ScriptWrappable* script_wrappable =
+ reinterpret_cast<ScriptWrappable*>(internal_fields.second);
+ TraceWrappersFromGeneratedCode(script_wrappable);
+}
+
+void ScriptWrappableMarkingVisitor::RegisterV8References(
+ const std::vector<std::pair<void*, void*>>&
+ internal_fields_of_potential_wrappers) {
+ CHECK(ThreadState::Current());
+ // TODO(hlopko): Visit the vector in the V8 instead of passing it over if
+ // there is no performance impact
+ for (auto& pair : internal_fields_of_potential_wrappers) {
+ RegisterV8Reference(pair);
+ }
+}
+
+bool ScriptWrappableMarkingVisitor::AdvanceTracing(
+ double deadline_in_ms,
+ v8::EmbedderHeapTracer::AdvanceTracingActions actions) {
+ // Do not drain the marking deque in a state where we can generally not
+ // perform a GC. This makes sure that TraceTraits and friends find
+ // themselves in a well-defined environment, e.g., properly set up vtables.
+ CHECK(ThreadState::Current());
+ CHECK(!ThreadState::Current()->IsWrapperTracingForbidden());
+ CHECK(tracing_in_progress_);
+ WTF::AutoReset<bool>(&advancing_tracing_, true);
+ while (actions.force_completion ==
+ v8::EmbedderHeapTracer::ForceCompletionAction::FORCE_COMPLETION ||
+ WTF::CurrentTimeTicksInMilliseconds() < deadline_in_ms) {
+ if (marking_deque_.IsEmpty()) {
+ return false;
+ }
+ marking_deque_.TakeFirst().TraceWrappers(this);
+ }
+ return true;
+}
+
+void ScriptWrappableMarkingVisitor::MarkWrapperHeader(
+ HeapObjectHeader* header) const {
+ DCHECK(!header->IsWrapperHeaderMarked());
+ // Verify that no compactable & movable objects are slated for
+ // lazy unmarking.
+ DCHECK(!HeapCompact::IsCompactableArena(
+ PageFromObject(header)->Arena()->ArenaIndex()));
+ header->MarkWrapperHeader();
+ headers_to_unmark_.push_back(header);
+}
+
+void ScriptWrappableMarkingVisitor::WriteBarrier(
+ v8::Isolate* isolate,
+ const TraceWrapperV8Reference<v8::Value>& dst_object) {
+ ScriptWrappableMarkingVisitor* visitor = CurrentVisitor(isolate);
+ if (dst_object.IsEmpty() || !visitor->WrapperTracingInProgress())
+ return;
+
+ // Conservatively assume that the source object containing |dst_object| is
+ // marked.
+ visitor->TraceWrappers(dst_object);
+}
+
+void ScriptWrappableMarkingVisitor::WriteBarrier(
+ v8::Isolate* isolate,
+ DOMWrapperMap<ScriptWrappable>* wrapper_map,
+ ScriptWrappable* key) {
+ ScriptWrappableMarkingVisitor* visitor = CurrentVisitor(isolate);
+ if (!visitor->WrapperTracingInProgress())
+ return;
+ // Conservatively assume that the source object key is marked.
+ visitor->TraceWrappers(wrapper_map, key);
+}
+
+void ScriptWrappableMarkingVisitor::Visit(
+ const TraceWrapperV8Reference<v8::Value>& traced_wrapper) const {
+ // The write barrier may try to mark a wrapper because cleanup is still
+ // delayed. Bail out in this case. We also allow unconditional marking which
+ // requires us to bail out here when tracing is not in progress.
+ if (!tracing_in_progress_ || traced_wrapper.Get().IsEmpty())
+ return;
+ traced_wrapper.Get().RegisterExternalReference(isolate_);
+}
+
+void ScriptWrappableMarkingVisitor::Visit(
+ const TraceWrapperDescriptor& wrapper_descriptor) const {
+ HeapObjectHeader* header =
+ HeapObjectHeader::FromPayload(wrapper_descriptor.base_object_payload);
+ if (header->IsWrapperHeaderMarked())
+ return;
+ MarkWrapperHeader(header);
+ DCHECK(tracing_in_progress_);
+ DCHECK(header->IsWrapperHeaderMarked());
+ marking_deque_.push_back(MarkingDequeItem(wrapper_descriptor));
+#if DCHECK_IS_ON()
+ if (!advancing_tracing_) {
+ verifier_deque_.push_back(MarkingDequeItem(wrapper_descriptor));
+ }
+#endif
+}
+
+void ScriptWrappableMarkingVisitor::Visit(
+ DOMWrapperMap<ScriptWrappable>* wrapper_map,
+ const ScriptWrappable* key) const {
+ wrapper_map->MarkWrapper(const_cast<ScriptWrappable*>(key));
+}
+
+void ScriptWrappableMarkingVisitor::InvalidateDeadObjectsInMarkingDeque() {
+ for (auto it = marking_deque_.begin(); it != marking_deque_.end(); ++it) {
+ auto& marking_data = *it;
+ if (marking_data.ShouldBeInvalidated()) {
+ marking_data.Invalidate();
+ }
+ }
+ for (auto it = verifier_deque_.begin(); it != verifier_deque_.end(); ++it) {
+ auto& marking_data = *it;
+ if (marking_data.ShouldBeInvalidated()) {
+ marking_data.Invalidate();
+ }
+ }
+ for (auto** it = headers_to_unmark_.begin(); it != headers_to_unmark_.end();
+ ++it) {
+ auto* header = *it;
+ if (header && !header->IsMarked()) {
+ *it = nullptr;
+ }
+ }
+}
+
+void ScriptWrappableMarkingVisitor::InvalidateDeadObjectsInMarkingDeque(
+ v8::Isolate* isolate) {
+ ScriptWrappableMarkingVisitor* script_wrappable_visitor =
+ V8PerIsolateData::From(isolate)->GetScriptWrappableMarkingVisitor();
+ if (script_wrappable_visitor)
+ script_wrappable_visitor->InvalidateDeadObjectsInMarkingDeque();
+}
+
+void ScriptWrappableMarkingVisitor::PerformCleanup(v8::Isolate* isolate) {
+ ScriptWrappableMarkingVisitor* script_wrappable_visitor =
+ V8PerIsolateData::From(isolate)->GetScriptWrappableMarkingVisitor();
+ if (script_wrappable_visitor)
+ script_wrappable_visitor->PerformCleanup();
+}
+
+ScriptWrappableMarkingVisitor* ScriptWrappableMarkingVisitor::CurrentVisitor(
+ v8::Isolate* isolate) {
+ return V8PerIsolateData::From(isolate)->GetScriptWrappableMarkingVisitor();
+}
+
+bool ScriptWrappableMarkingVisitor::MarkingDequeContains(void* needle) {
+ for (auto item : marking_deque_) {
+ if (item.RawObjectPointer() == needle)
+ return true;
+ }
+ return false;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/bindings/script_wrappable_marking_visitor.h b/chromium/third_party/blink/renderer/platform/bindings/script_wrappable_marking_visitor.h
new file mode 100644
index 00000000000..f2b1f44d451
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/script_wrappable_marking_visitor.h
@@ -0,0 +1,232 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCRIPT_WRAPPABLE_MARKING_VISITOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCRIPT_WRAPPABLE_MARKING_VISITOR_H_
+
+#include "third_party/blink/renderer/platform/bindings/script_wrappable_visitor.h"
+#include "third_party/blink/renderer/platform/heap/heap_page.h"
+#include "third_party/blink/renderer/platform/heap/threading_traits.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/deque.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+template <typename T>
+class DOMWrapperMap;
+class HeapObjectHeader;
+class ScriptWrappable;
+class ScriptWrappableVisitor;
+template <typename T>
+class TraceWrapperV8Reference;
+
+// ScriptWrappableVisitor is used to trace through Blink's heap to find all
+// reachable wrappers. V8 calls this visitor during its garbage collection,
+// see v8::EmbedderHeapTracer.
+class PLATFORM_EXPORT ScriptWrappableMarkingVisitor
+ : public v8::EmbedderHeapTracer,
+ public ScriptWrappableVisitor {
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ScriptWrappableMarkingVisitor);
+
+ public:
+ static ScriptWrappableMarkingVisitor* CurrentVisitor(v8::Isolate*);
+
+ bool WrapperTracingInProgress() const { return tracing_in_progress_; }
+
+ // Replace all dead objects in the marking deque with nullptr after Oilpan
+ // garbage collection.
+ static void InvalidateDeadObjectsInMarkingDeque(v8::Isolate*);
+
+ // Immediately clean up all wrappers.
+ static void PerformCleanup(v8::Isolate*);
+
+ // Conservative Dijkstra barrier.
+ //
+ // On assignment 'x.a = y' during incremental marking the Dijkstra barrier
+ // suggests checking the color of 'x' and only mark 'y' if 'x' is marked.
+
+ // Since checking 'x' is expensive in the current setting, as it requires
+ // either a back pointer or expensive lookup logic due to large objects and
+ // multiple inheritance, just assume that 'x' is black. We assume here that
+ // since an object 'x' is referenced for a write, it will generally also be
+ // alive in the current GC cycle.
+ template <typename T>
+ static void WriteBarrier(const T* dst_object) {
+ if (!dst_object)
+ return;
+
+ const ThreadState* thread_state =
+ ThreadStateFor<ThreadingTrait<T>::kAffinity>::GetState();
+ DCHECK(thread_state);
+ // Bail out if tracing is not in progress.
+ if (!thread_state->WrapperTracingInProgress())
+ return;
+
+ // If the wrapper is already marked we can bail out here.
+ if (TraceTrait<T>::GetHeapObjectHeader(const_cast<T*>(dst_object))
+ ->IsWrapperHeaderMarked())
+ return;
+
+ CurrentVisitor(thread_state->GetIsolate())
+ ->Visit(WrapperDescriptorFor(dst_object));
+ }
+
+ static void WriteBarrier(v8::Isolate*,
+ const TraceWrapperV8Reference<v8::Value>&);
+
+ static void WriteBarrier(v8::Isolate*,
+ DOMWrapperMap<ScriptWrappable>*,
+ ScriptWrappable* key);
+
+ ScriptWrappableMarkingVisitor(v8::Isolate* isolate) : isolate_(isolate){};
+ ~ScriptWrappableMarkingVisitor() override;
+
+ // v8::EmbedderHeapTracer interface.
+
+ void TracePrologue() override;
+ void RegisterV8References(const std::vector<std::pair<void*, void*>>&
+ internal_fields_of_potential_wrappers) override;
+ void RegisterV8Reference(const std::pair<void*, void*>& internal_fields);
+ bool AdvanceTracing(double deadline_in_ms,
+ v8::EmbedderHeapTracer::AdvanceTracingActions) override;
+ void TraceEpilogue() override;
+ void AbortTracing() override;
+ void EnterFinalPause() override;
+ size_t NumberOfWrappersToTrace() override;
+
+ protected:
+ // ScriptWrappableVisitor interface.
+ void Visit(const TraceWrapperV8Reference<v8::Value>&) const override;
+ void Visit(const TraceWrapperDescriptor&) const override;
+ void Visit(DOMWrapperMap<ScriptWrappable>*,
+ const ScriptWrappable* key) const override;
+
+ v8::Isolate* isolate() const { return isolate_; }
+
+ private:
+ class MarkingDequeItem {
+ public:
+ explicit MarkingDequeItem(const TraceWrapperDescriptor& wrapper_descriptor)
+ : raw_object_pointer_(wrapper_descriptor.base_object_payload),
+ trace_wrappers_callback_(wrapper_descriptor.trace_wrappers_callback) {
+ DCHECK(raw_object_pointer_);
+ DCHECK(trace_wrappers_callback_);
+ }
+
+ // Traces wrappers if the underlying object has not yet been invalidated.
+ inline void TraceWrappers(ScriptWrappableVisitor* visitor) const {
+ if (raw_object_pointer_) {
+ trace_wrappers_callback_(visitor,
+ const_cast<void*>(raw_object_pointer_));
+ }
+ }
+
+ inline const void* RawObjectPointer() { return raw_object_pointer_; }
+
+ // Returns true if the object is currently marked in Oilpan and false
+ // otherwise.
+ inline bool ShouldBeInvalidated() {
+ return raw_object_pointer_ && !GetHeapObjectHeader()->IsMarked();
+ }
+
+ // Invalidates the current wrapper marking data, i.e., calling TraceWrappers
+ // will result in a noop.
+ inline void Invalidate() { raw_object_pointer_ = nullptr; }
+
+ private:
+ inline const HeapObjectHeader* GetHeapObjectHeader() {
+ return HeapObjectHeader::FromPayload(raw_object_pointer_);
+ }
+
+ const void* raw_object_pointer_;
+ TraceWrappersCallback trace_wrappers_callback_;
+ };
+
+ void MarkWrapperHeader(HeapObjectHeader*) const;
+
+ // Schedule an idle task to perform a lazy (incremental) clean up of
+ // wrappers.
+ void ScheduleIdleLazyCleanup();
+ void PerformLazyCleanup(double deadline_seconds);
+
+ void InvalidateDeadObjectsInMarkingDeque();
+
+ // Immediately cleans up all wrappers if necessary.
+ void PerformCleanup();
+
+ WTF::Deque<MarkingDequeItem>* MarkingDeque() const { return &marking_deque_; }
+ WTF::Vector<HeapObjectHeader*>* HeadersToUnmark() const {
+ return &headers_to_unmark_;
+ }
+
+ bool MarkingDequeContains(void* needle);
+
+ // Returns true if wrapper tracing is currently in progress, i.e.,
+ // TracePrologue has been called, and TraceEpilogue has not yet been called.
+ bool tracing_in_progress_ = false;
+
+ // Is AdvanceTracing currently running? If not, we know that all calls of
+ // pushToMarkingDeque are from V8 or new wrapper associations. And this
+ // information is used by the verifier feature.
+ bool advancing_tracing_ = false;
+
+ // Indicates whether an idle task for a lazy cleanup has already been
+ // scheduled. The flag is used to avoid scheduling multiple idle tasks for
+ // cleaning up.
+ bool idle_cleanup_task_scheduled_ = false;
+
+ // Indicates whether cleanup should currently happen. The flag is used to
+ // avoid cleaning up in the next GC cycle.
+ bool should_cleanup_ = false;
+
+ // Collection of objects we need to trace from. We assume it is safe to hold
+ // on to the raw pointers because:
+ // - oilpan object cannot move
+ // - oilpan gc will call invalidateDeadObjectsInMarkingDeque to delete all
+ // obsolete objects
+ mutable WTF::Deque<MarkingDequeItem> marking_deque_;
+
+ // Collection of objects we started tracing from. We assume it is safe to
+ // hold on to the raw pointers because:
+ // - oilpan object cannot move
+ // - oilpan gc will call invalidateDeadObjectsInMarkingDeque to delete
+ // all obsolete objects
+ //
+ // These objects are used when TraceWrappablesVerifier feature is enabled to
+ // verify that all objects reachable in the atomic pause were marked
+ // incrementally. If not, there is one or multiple write barriers missing.
+ mutable WTF::Deque<MarkingDequeItem> verifier_deque_;
+
+ // Collection of headers we need to unmark after the tracing finished. We
+ // assume it is safe to hold on to the headers because:
+ // - oilpan objects cannot move
+ // - objects this headers belong to are invalidated by the oilpan GC in
+ // invalidateDeadObjectsInMarkingDeque.
+ mutable WTF::Vector<HeapObjectHeader*> headers_to_unmark_;
+ v8::Isolate* isolate_;
+
+ FRIEND_TEST_ALL_PREFIXES(ScriptWrappableMarkingVisitorTest, MixinTracing);
+ FRIEND_TEST_ALL_PREFIXES(ScriptWrappableMarkingVisitorTest,
+ OilpanClearsMarkingDequeWhenObjectDied);
+ FRIEND_TEST_ALL_PREFIXES(ScriptWrappableMarkingVisitorTest,
+ ScriptWrappableMarkingVisitorTracesWrappers);
+ FRIEND_TEST_ALL_PREFIXES(ScriptWrappableMarkingVisitorTest,
+ OilpanClearsHeadersWhenObjectDied);
+ FRIEND_TEST_ALL_PREFIXES(
+ ScriptWrappableMarkingVisitorTest,
+ MarkedObjectDoesNothingOnWriteBarrierHitWhenDependencyIsMarkedToo);
+ FRIEND_TEST_ALL_PREFIXES(
+ ScriptWrappableMarkingVisitorTest,
+ MarkedObjectMarksDependencyOnWriteBarrierHitWhenNotMarked);
+ FRIEND_TEST_ALL_PREFIXES(ScriptWrappableMarkingVisitorTest,
+ WriteBarrierOnHeapVectorSwap1);
+ FRIEND_TEST_ALL_PREFIXES(ScriptWrappableMarkingVisitorTest,
+ WriteBarrierOnHeapVectorSwap2);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCRIPT_WRAPPABLE_MARKING_VISITOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/bindings/script_wrappable_visitor.cc b/chromium/third_party/blink/renderer/platform/bindings/script_wrappable_visitor.cc
new file mode 100644
index 00000000000..93fd61983f6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/script_wrappable_visitor.cc
@@ -0,0 +1,28 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/bindings/script_wrappable_visitor.h"
+
+#include "third_party/blink/renderer/platform/bindings/dom_wrapper_map.h"
+#include "third_party/blink/renderer/platform/bindings/trace_wrapper_base.h"
+#include "third_party/blink/renderer/platform/supplementable.h"
+
+namespace blink {
+
+void ScriptWrappableVisitor::TraceWrappers(
+ DOMWrapperMap<ScriptWrappable>* wrapper_map,
+ const ScriptWrappable* key) const {
+ Visit(wrapper_map, key);
+}
+
+void ScriptWrappableVisitor::DispatchTraceWrappers(
+ const TraceWrapperBase* wrapper_base) const {
+ wrapper_base->TraceWrappers(this);
+}
+
+void ScriptWrappableVisitor::DispatchTraceWrappersForSupplement(
+ const TraceWrapperBaseForSupplement* wrapper_base) const {
+ wrapper_base->TraceWrappers(this);
+}
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/bindings/script_wrappable_visitor.h b/chromium/third_party/blink/renderer/platform/bindings/script_wrappable_visitor.h
new file mode 100644
index 00000000000..28063c81d3e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/script_wrappable_visitor.h
@@ -0,0 +1,153 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCRIPT_WRAPPABLE_VISITOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCRIPT_WRAPPABLE_VISITOR_H_
+
+#include "third_party/blink/renderer/platform/bindings/trace_wrapper_base.h"
+#include "third_party/blink/renderer/platform/heap/heap_page.h"
+#include "third_party/blink/renderer/platform/heap/threading_traits.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/deque.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+template <typename T>
+class DOMWrapperMap;
+class ScriptWrappable;
+class ScriptWrappableVisitor;
+template <typename T>
+class Supplement;
+class TraceWrapperBase;
+class TraceWrapperBaseForSupplement;
+template <typename T>
+class TraceWrapperV8Reference;
+
+#define DEFINE_TRAIT_FOR_TRACE_WRAPPERS(ClassName) \
+ template <> \
+ inline void TraceTrait<ClassName>::TraceWrappers( \
+ ScriptWrappableVisitor* visitor, void* t) { \
+ static_assert(sizeof(ClassName), "type needs to be defined"); \
+ static_assert(IsGarbageCollectedType<ClassName>::value, \
+ "only objects deriving from GarbageCollected can be used"); \
+ static_cast<ClassName*>(t)->TraceWrappers(visitor); \
+ }
+
+// Abstract visitor for wrapper references in a ScriptWrappable.
+// Usage:
+// - Define a derived class that overrides Visit(..) methods.
+// - Create an instance of the derived class: visitor.
+// - Call visitor.DispatchTraceWrappers(traceable).
+// DispatchTraceWrappers will invoke Visit() method for all
+// wrapper references in traceable.
+class PLATFORM_EXPORT ScriptWrappableVisitor {
+ public:
+ template <typename T>
+ static NOINLINE void MissedWriteBarrier() {
+ NOTREACHED();
+ }
+
+ template <typename T>
+ static const char* NameCallback(const void* traceable) {
+ // Mixns never inherit from TraceWrapperBase.
+ return NameInHeapSnapshot(static_cast<const T*>(traceable));
+ }
+
+ // Trace all wrappers of |tracable|.
+ //
+ // If you cannot use TraceWrapperMember & the corresponding TraceWrappers()
+ // for some reason (e.g., unions using raw pointers), see
+ // |TraceWrappersWithManualWriteBarrier()| below.
+ template <typename T>
+ void TraceWrappers(const TraceWrapperMember<T>& traceable) const {
+ static_assert(sizeof(T), "T must be fully defined");
+ Visit(traceable.Get());
+ }
+
+ // Enable partial tracing of objects. This is used when tracing interior
+ // objects without their own header.
+ template <typename T>
+ void TraceWrappers(const T& traceable) const {
+ static_assert(sizeof(T), "T must be fully defined");
+ traceable.TraceWrappers(this);
+ }
+
+ // Only called from automatically generated bindings code.
+ template <typename T>
+ void TraceWrappersFromGeneratedCode(const T* traceable) const {
+ Visit(traceable);
+ }
+
+ // Require all users of manual write barriers to make this explicit in their
+ // |TraceWrappers| definition. Be sure to add
+ // |ScriptWrappableMarkingVisitor::WriteBarrier(new_value)| after all
+ // assignments to the field. Otherwise, the objects may be collected
+ // prematurely.
+ template <typename T>
+ void TraceWrappersWithManualWriteBarrier(const T* traceable) const {
+ Visit(traceable);
+ }
+
+ template <typename V8Type>
+ void TraceWrappers(const TraceWrapperV8Reference<V8Type>& v8reference) const {
+ Visit(v8reference.template Cast<v8::Value>());
+ }
+
+ // Trace wrappers in non-main worlds.
+ void TraceWrappers(DOMWrapperMap<ScriptWrappable>*,
+ const ScriptWrappable* key) const;
+
+ virtual void DispatchTraceWrappers(const TraceWrapperBase*) const;
+ template <typename T>
+ void DispatchTraceWrappers(const Supplement<T>* traceable) const {
+ const TraceWrapperBaseForSupplement* base = traceable;
+ DispatchTraceWrappersForSupplement(base);
+ }
+ // Catch all handlers needed because of mixins except for Supplement<T>.
+ void DispatchTraceWrappers(const void*) const { CHECK(false); }
+
+ protected:
+ // The visitor interface. Derived visitors should override this
+ // function to visit V8 references and ScriptWrappables.
+ virtual void Visit(const TraceWrapperV8Reference<v8::Value>&) const = 0;
+ virtual void Visit(const TraceWrapperDescriptor&) const = 0;
+ virtual void Visit(DOMWrapperMap<ScriptWrappable>*,
+ const ScriptWrappable* key) const = 0;
+
+ template <typename T>
+ static TraceWrapperDescriptor WrapperDescriptorFor(const T* traceable) {
+ return TraceTrait<T>::GetTraceWrapperDescriptor(const_cast<T*>(traceable));
+ }
+
+ private:
+ static const char* NameInHeapSnapshot(const TraceWrapperBase* traceable) {
+ return traceable->NameInHeapSnapshot();
+ }
+
+ static const char* NameInHeapSnapshot(...) {
+ // Default case for all non-TraceWrapperBase classes.
+ return "InternalNode";
+ }
+
+ // Helper method to invoke the virtual Visit method with wrapper descriptor.
+ template <typename T>
+ void Visit(const T* traceable) const {
+ static_assert(sizeof(T), "T must be fully defined");
+ if (!traceable)
+ return;
+ Visit(WrapperDescriptorFor(traceable));
+ }
+
+ // Supplement-specific implementation of DispatchTraceWrappers. The suffix of
+ // "ForSupplement" is necessary not to make this member function a candidate
+ // of overload resolutions.
+ void DispatchTraceWrappersForSupplement(
+ const TraceWrapperBaseForSupplement*) const;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCRIPT_WRAPPABLE_VISITOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/bindings/script_wrappable_visitor_verifier.h b/chromium/third_party/blink/renderer/platform/bindings/script_wrappable_visitor_verifier.h
new file mode 100644
index 00000000000..4bd05ffecbd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/script_wrappable_visitor_verifier.h
@@ -0,0 +1,39 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCRIPT_WRAPPABLE_VISITOR_VERIFIER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCRIPT_WRAPPABLE_VISITOR_VERIFIER_H_
+
+#include "third_party/blink/renderer/platform/bindings/script_wrappable_visitor.h"
+
+namespace blink {
+
+// This visitor should be applied on wrapper members of each marked object
+// after marking is complete. The Visit method checks that the given wrapper
+// is also marked.
+class ScriptWrappableVisitorVerifier final : public ScriptWrappableVisitor {
+ protected:
+ void Visit(const TraceWrapperV8Reference<v8::Value>&) const final {}
+ void Visit(const TraceWrapperDescriptor& descriptor) const final {
+ if (!HeapObjectHeader::FromPayload(descriptor.base_object_payload)
+ ->IsWrapperHeaderMarked()) {
+ // If this branch is hit, it means that a white (not discovered by
+ // traceWrappers) object was assigned as a member to a black object
+ // (already processed by traceWrappers). Black object will not be
+ // processed anymore so White object will remain undetected and
+ // therefore its wrapper and all wrappers reachable from it would be
+ // collected.
+
+ // This means there is a write barrier missing somewhere. Check the
+ // backtrace to see which types are causing this and review all the
+ // places where white object is set to a black object.
+ descriptor.missed_write_barrier_callback();
+ NOTREACHED();
+ }
+ }
+ void Visit(DOMWrapperMap<ScriptWrappable>*,
+ const ScriptWrappable* key) const final {}
+};
+}
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/bindings/shared_persistent.h b/chromium/third_party/blink/renderer/platform/bindings/shared_persistent.h
new file mode 100644
index 00000000000..505858b66fe
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/shared_persistent.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SHARED_PERSISTENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SHARED_PERSISTENT_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/bindings/scoped_persistent.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+// A ref counted version of ScopedPersistent. This class is intended for use by
+// ScriptValue and not for normal use. Consider using ScopedPersistent directly
+// instead.
+template <typename T>
+class SharedPersistent : public RefCounted<SharedPersistent<T>> {
+ WTF_MAKE_NONCOPYABLE(SharedPersistent);
+
+ public:
+ static scoped_refptr<SharedPersistent<T>> Create(v8::Local<T> value,
+ v8::Isolate* isolate) {
+ return base::AdoptRef(new SharedPersistent<T>(value, isolate));
+ }
+
+ v8::Local<T> NewLocal(v8::Isolate* isolate) const {
+ return value_.NewLocal(isolate);
+ }
+
+ bool IsEmpty() { return value_.IsEmpty(); }
+
+ bool operator==(const SharedPersistent<T>& other) {
+ return value_ == other.value_;
+ }
+
+ private:
+ explicit SharedPersistent(v8::Local<T> value, v8::Isolate* isolate)
+ : value_(isolate, value) {}
+ ScopedPersistent<T> value_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SHARED_PERSISTENT_H_
diff --git a/chromium/third_party/blink/renderer/platform/bindings/string_resource.cc b/chromium/third_party/blink/renderer/platform/bindings/string_resource.cc
new file mode 100644
index 00000000000..3d973cda4a9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/string_resource.cc
@@ -0,0 +1,166 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/bindings/string_resource.h"
+
+#include "third_party/blink/renderer/platform/bindings/v8_binding.h"
+
+namespace blink {
+
+template <class StringClass>
+struct StringTraits {
+ static const StringClass& FromStringResource(StringResourceBase*);
+ template <typename V8StringTrait>
+ static StringClass FromV8String(v8::Local<v8::String>, int);
+};
+
+template <>
+struct StringTraits<String> {
+ static const String& FromStringResource(StringResourceBase* resource) {
+ return resource->WebcoreString();
+ }
+ template <typename V8StringTrait>
+ static String FromV8String(v8::Local<v8::String>, int);
+};
+
+template <>
+struct StringTraits<AtomicString> {
+ static const AtomicString& FromStringResource(StringResourceBase* resource) {
+ return resource->GetAtomicString();
+ }
+ template <typename V8StringTrait>
+ static AtomicString FromV8String(v8::Local<v8::String>, int);
+};
+
+struct V8StringTwoBytesTrait {
+ typedef UChar CharType;
+ ALWAYS_INLINE static void Write(v8::Local<v8::String> v8_string,
+ CharType* buffer,
+ int length) {
+ v8_string->Write(reinterpret_cast<uint16_t*>(buffer), 0, length);
+ }
+};
+
+struct V8StringOneByteTrait {
+ typedef LChar CharType;
+ ALWAYS_INLINE static void Write(v8::Local<v8::String> v8_string,
+ CharType* buffer,
+ int length) {
+ v8_string->WriteOneByte(buffer, 0, length);
+ }
+};
+
+template <typename V8StringTrait>
+String StringTraits<String>::FromV8String(v8::Local<v8::String> v8_string,
+ int length) {
+ DCHECK(v8_string->Length() == length);
+ typename V8StringTrait::CharType* buffer;
+ String result = String::CreateUninitialized(length, buffer);
+ V8StringTrait::Write(v8_string, buffer, length);
+ return result;
+}
+
+template <typename V8StringTrait>
+AtomicString StringTraits<AtomicString>::FromV8String(
+ v8::Local<v8::String> v8_string,
+ int length) {
+ DCHECK(v8_string->Length() == length);
+ static const int kInlineBufferSize =
+ 32 / sizeof(typename V8StringTrait::CharType);
+ if (length <= kInlineBufferSize) {
+ typename V8StringTrait::CharType inline_buffer[kInlineBufferSize];
+ V8StringTrait::Write(v8_string, inline_buffer, length);
+ return AtomicString(inline_buffer, length);
+ }
+ typename V8StringTrait::CharType* buffer;
+ String string = String::CreateUninitialized(length, buffer);
+ V8StringTrait::Write(v8_string, buffer, length);
+ return AtomicString(string);
+}
+
+template <typename StringType>
+StringType V8StringToWebCoreString(v8::Local<v8::String> v8_string,
+ ExternalMode external) {
+ {
+ // This portion of this function is very hot in certain Dromeao benchmarks.
+ v8::String::Encoding encoding;
+ v8::String::ExternalStringResourceBase* resource =
+ v8_string->GetExternalStringResourceBase(&encoding);
+ if (LIKELY(!!resource)) {
+ StringResourceBase* base;
+ if (encoding == v8::String::ONE_BYTE_ENCODING)
+ base = static_cast<StringResource8*>(resource);
+ else
+ base = static_cast<StringResource16*>(resource);
+ return StringTraits<StringType>::FromStringResource(base);
+ }
+ }
+
+ int length = v8_string->Length();
+ if (UNLIKELY(!length))
+ return StringType("");
+
+ bool one_byte = v8_string->ContainsOnlyOneByte();
+ StringType result(one_byte ? StringTraits<StringType>::template FromV8String<
+ V8StringOneByteTrait>(v8_string, length)
+ : StringTraits<StringType>::template FromV8String<
+ V8StringTwoBytesTrait>(v8_string, length));
+
+ if (external != kExternalize || !v8_string->CanMakeExternal())
+ return result;
+
+ if (result.Is8Bit()) {
+ StringResource8* string_resource = new StringResource8(result);
+ if (UNLIKELY(!v8_string->MakeExternal(string_resource)))
+ delete string_resource;
+ } else {
+ StringResource16* string_resource = new StringResource16(result);
+ if (UNLIKELY(!v8_string->MakeExternal(string_resource)))
+ delete string_resource;
+ }
+ return result;
+}
+
+// Explicitly instantiate the above template with the expected
+// parameterizations, to ensure the compiler generates the code; otherwise link
+// errors can result in GCC 4.4.
+template String V8StringToWebCoreString<String>(v8::Local<v8::String>,
+ ExternalMode);
+template AtomicString V8StringToWebCoreString<AtomicString>(
+ v8::Local<v8::String>,
+ ExternalMode);
+
+// Fast but non thread-safe version.
+String Int32ToWebCoreStringFast(int value) {
+ // Caching of small strings below is not thread safe: newly constructed
+ // AtomicString are not safely published.
+ DCHECK(IsMainThread());
+
+ // Most numbers used are <= 100. Even if they aren't used there's very little
+ // cost in using the space.
+ const int kLowNumbers = 100;
+ DEFINE_STATIC_LOCAL(Vector<AtomicString>, low_numbers, (kLowNumbers + 1));
+ String web_core_string;
+ if (0 <= value && value <= kLowNumbers) {
+ web_core_string = low_numbers[value];
+ if (!web_core_string) {
+ AtomicString value_string = AtomicString::Number(value);
+ low_numbers[value] = value_string;
+ web_core_string = value_string;
+ }
+ } else {
+ web_core_string = String::Number(value);
+ }
+ return web_core_string;
+}
+
+String Int32ToWebCoreString(int value) {
+ // If we are on the main thread (this should always true for non-workers),
+ // call the faster one.
+ if (IsMainThread())
+ return Int32ToWebCoreStringFast(value);
+ return String::Number(value);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/bindings/string_resource.h b/chromium/third_party/blink/renderer/platform/bindings/string_resource.h
new file mode 100644
index 00000000000..9a8a6b6bda7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/string_resource.h
@@ -0,0 +1,140 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_STRING_RESOURCE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_STRING_RESOURCE_H_
+
+#include "base/macros.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#include "third_party/blink/renderer/platform/wtf/threading.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+// StringResource is a helper class for V8ExternalString. It is used
+// to manage the life-cycle of the underlying buffer of the external string.
+class StringResourceBase {
+ USING_FAST_MALLOC(StringResourceBase);
+
+ public:
+ explicit StringResourceBase(const String& string) : plain_string_(string) {
+#if DCHECK_IS_ON()
+ thread_id_ = WTF::CurrentThread();
+#endif
+ DCHECK(!string.IsNull());
+ v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(
+ string.CharactersSizeInBytes());
+ }
+
+ explicit StringResourceBase(const AtomicString& string)
+ : plain_string_(string.GetString()), atomic_string_(string) {
+#if DCHECK_IS_ON()
+ thread_id_ = WTF::CurrentThread();
+#endif
+ DCHECK(!string.IsNull());
+ v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(
+ string.CharactersSizeInBytes());
+ }
+
+ virtual ~StringResourceBase() {
+#if DCHECK_IS_ON()
+ DCHECK(thread_id_ == WTF::CurrentThread());
+#endif
+ int64_t reduced_external_memory = plain_string_.CharactersSizeInBytes();
+ if (plain_string_.Impl() != atomic_string_.Impl() &&
+ !atomic_string_.IsNull())
+ reduced_external_memory += atomic_string_.CharactersSizeInBytes();
+ v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(
+ -reduced_external_memory);
+ }
+
+ const String& WebcoreString() { return plain_string_; }
+
+ const AtomicString& GetAtomicString() {
+#if DCHECK_IS_ON()
+ DCHECK(thread_id_ == WTF::CurrentThread());
+#endif
+ if (atomic_string_.IsNull()) {
+ atomic_string_ = AtomicString(plain_string_);
+ DCHECK(!atomic_string_.IsNull());
+ if (plain_string_.Impl() != atomic_string_.Impl()) {
+ v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(
+ atomic_string_.CharactersSizeInBytes());
+ }
+ }
+ return atomic_string_;
+ }
+
+ protected:
+ // A shallow copy of the string. Keeps the string buffer alive until the V8
+ // engine garbage collects it.
+ String plain_string_;
+ // If this string is atomic or has been made atomic earlier the
+ // atomic string is held here. In the case where the string starts
+ // off non-atomic and becomes atomic later it is necessary to keep
+ // the original string alive because v8 may keep derived pointers
+ // into that string.
+ AtomicString atomic_string_;
+
+ private:
+#if DCHECK_IS_ON()
+ WTF::ThreadIdentifier thread_id_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(StringResourceBase);
+};
+
+class StringResource16 final : public StringResourceBase,
+ public v8::String::ExternalStringResource {
+ public:
+ explicit StringResource16(const String& string) : StringResourceBase(string) {
+ DCHECK(!string.Is8Bit());
+ }
+
+ explicit StringResource16(const AtomicString& string)
+ : StringResourceBase(string) {
+ DCHECK(!string.Is8Bit());
+ }
+
+ size_t length() const override { return plain_string_.Impl()->length(); }
+ const uint16_t* data() const override {
+ return reinterpret_cast<const uint16_t*>(
+ plain_string_.Impl()->Characters16());
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(StringResource16);
+};
+
+class StringResource8 final : public StringResourceBase,
+ public v8::String::ExternalOneByteStringResource {
+ public:
+ explicit StringResource8(const String& string) : StringResourceBase(string) {
+ DCHECK(string.Is8Bit());
+ }
+
+ explicit StringResource8(const AtomicString& string)
+ : StringResourceBase(string) {
+ DCHECK(string.Is8Bit());
+ }
+
+ size_t length() const override { return plain_string_.Impl()->length(); }
+ const char* data() const override {
+ return reinterpret_cast<const char*>(plain_string_.Impl()->Characters8());
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(StringResource8);
+};
+
+enum ExternalMode { kExternalize, kDoNotExternalize };
+
+template <typename StringType>
+PLATFORM_EXPORT StringType V8StringToWebCoreString(v8::Local<v8::String>,
+ ExternalMode);
+PLATFORM_EXPORT String Int32ToWebCoreString(int value);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_STRING_RESOURCE_H_
diff --git a/chromium/third_party/blink/renderer/platform/bindings/to_v8.h b/chromium/third_party/blink/renderer/platform/bindings/to_v8.h
new file mode 100644
index 00000000000..13ecc792188
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/to_v8.h
@@ -0,0 +1,298 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_TO_V8_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_TO_V8_H_
+
+// ToV8() provides C++ -> V8 conversion. Note that ToV8() can return an empty
+// handle. Call sites must check IsEmpty() before using return value.
+
+#include <utility>
+
+#include "third_party/blink/renderer/platform/bindings/dom_data_store.h"
+#include "third_party/blink/renderer/platform/bindings/script_state.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/bindings/v8_binding.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+// ScriptWrappable
+
+inline v8::Local<v8::Value> ToV8(ScriptWrappable* impl,
+ v8::Local<v8::Object> creation_context,
+ v8::Isolate* isolate) {
+ if (UNLIKELY(!impl))
+ return v8::Null(isolate);
+ v8::Local<v8::Value> wrapper = DOMDataStore::GetWrapper(impl, isolate);
+ if (!wrapper.IsEmpty())
+ return wrapper;
+
+ wrapper = impl->Wrap(isolate, creation_context);
+ DCHECK(!wrapper.IsEmpty());
+ return wrapper;
+}
+
+// Primitives
+
+inline v8::Local<v8::Value> ToV8(const String& value,
+ v8::Local<v8::Object> creation_context,
+ v8::Isolate* isolate) {
+ return V8String(isolate, value);
+}
+
+inline v8::Local<v8::Value> ToV8(const char* value,
+ v8::Local<v8::Object> creation_context,
+ v8::Isolate* isolate) {
+ return V8String(isolate, value);
+}
+
+template <size_t sizeOfValue>
+inline v8::Local<v8::Value> ToV8SignedIntegerInternal(int64_t value,
+ v8::Isolate*);
+
+template <>
+inline v8::Local<v8::Value> ToV8SignedIntegerInternal<4>(int64_t value,
+ v8::Isolate* isolate) {
+ return v8::Integer::New(isolate, static_cast<int32_t>(value));
+}
+
+template <>
+inline v8::Local<v8::Value> ToV8SignedIntegerInternal<8>(int64_t value,
+ v8::Isolate* isolate) {
+ int32_t value_in32_bit = static_cast<int32_t>(value);
+ if (value_in32_bit == value)
+ return v8::Integer::New(isolate, value);
+ // V8 doesn't have a 64-bit integer implementation.
+ return v8::Number::New(isolate, value);
+}
+
+template <size_t sizeOfValue>
+inline v8::Local<v8::Value> ToV8UnsignedIntegerInternal(uint64_t value,
+ v8::Isolate*);
+
+template <>
+inline v8::Local<v8::Value> ToV8UnsignedIntegerInternal<4>(
+ uint64_t value,
+ v8::Isolate* isolate) {
+ return v8::Integer::NewFromUnsigned(isolate, static_cast<uint32_t>(value));
+}
+
+template <>
+inline v8::Local<v8::Value> ToV8UnsignedIntegerInternal<8>(
+ uint64_t value,
+ v8::Isolate* isolate) {
+ uint32_t value_in32_bit = static_cast<uint32_t>(value);
+ if (value_in32_bit == value)
+ return v8::Integer::NewFromUnsigned(isolate, value);
+ // V8 doesn't have a 64-bit integer implementation.
+ return v8::Number::New(isolate, value);
+}
+
+inline v8::Local<v8::Value> ToV8(int value,
+ v8::Local<v8::Object> creation_context,
+ v8::Isolate* isolate) {
+ return ToV8SignedIntegerInternal<sizeof value>(value, isolate);
+}
+
+inline v8::Local<v8::Value> ToV8(long value,
+ v8::Local<v8::Object> creation_context,
+ v8::Isolate* isolate) {
+ return ToV8SignedIntegerInternal<sizeof value>(value, isolate);
+}
+
+inline v8::Local<v8::Value> ToV8(long long value,
+ v8::Local<v8::Object> creation_context,
+ v8::Isolate* isolate) {
+ return ToV8SignedIntegerInternal<sizeof value>(value, isolate);
+}
+
+inline v8::Local<v8::Value> ToV8(unsigned value,
+ v8::Local<v8::Object> creation_context,
+ v8::Isolate* isolate) {
+ return ToV8UnsignedIntegerInternal<sizeof value>(value, isolate);
+}
+
+inline v8::Local<v8::Value> ToV8(unsigned long value,
+ v8::Local<v8::Object> creation_context,
+ v8::Isolate* isolate) {
+ return ToV8UnsignedIntegerInternal<sizeof value>(value, isolate);
+}
+
+inline v8::Local<v8::Value> ToV8(unsigned long long value,
+ v8::Local<v8::Object> creation_context,
+ v8::Isolate* isolate) {
+ return ToV8UnsignedIntegerInternal<sizeof value>(value, isolate);
+}
+
+inline v8::Local<v8::Value> ToV8(double value,
+ v8::Local<v8::Object> creation_context,
+ v8::Isolate* isolate) {
+ return v8::Number::New(isolate, value);
+}
+
+inline v8::Local<v8::Value> ToV8(bool value,
+ v8::Local<v8::Object> creation_context,
+ v8::Isolate* isolate) {
+ return v8::Boolean::New(isolate, value);
+}
+
+// Identity operator
+
+inline v8::Local<v8::Value> ToV8(v8::Local<v8::Value> value,
+ v8::Local<v8::Object> creation_context,
+ v8::Isolate*) {
+ return value;
+}
+
+// Undefined
+
+struct ToV8UndefinedGenerator {
+ DISALLOW_NEW();
+}; // Used only for having toV8 return v8::Undefined.
+
+inline v8::Local<v8::Value> ToV8(const ToV8UndefinedGenerator& value,
+ v8::Local<v8::Object> creation_context,
+ v8::Isolate* isolate) {
+ return v8::Undefined(isolate);
+}
+
+// Forward declaration to allow interleaving with sequences.
+template <typename InnerType>
+inline v8::Local<v8::Value> ToV8(const base::Optional<InnerType>& value,
+ v8::Local<v8::Object> creation_context,
+ v8::Isolate*);
+
+// Array
+
+// Declare the function here but define it later so it can call the ToV8()
+// overloads below.
+template <typename Sequence>
+inline v8::Local<v8::Value> ToV8SequenceInternal(
+ const Sequence&,
+ v8::Local<v8::Object> creation_context,
+ v8::Isolate*);
+
+template <typename T, size_t inlineCapacity>
+inline v8::Local<v8::Value> ToV8(const Vector<T, inlineCapacity>& value,
+ v8::Local<v8::Object> creation_context,
+ v8::Isolate* isolate) {
+ return ToV8SequenceInternal(value, creation_context, isolate);
+}
+
+template <typename T, size_t inlineCapacity>
+inline v8::Local<v8::Value> ToV8(const HeapVector<T, inlineCapacity>& value,
+ v8::Local<v8::Object> creation_context,
+ v8::Isolate* isolate) {
+ return ToV8SequenceInternal(value, creation_context, isolate);
+}
+
+// The following two overloads are also used to convert record<K,V> IDL types
+// back into ECMAScript Objects.
+template <typename T>
+inline v8::Local<v8::Value> ToV8(const Vector<std::pair<String, T>>& value,
+ v8::Local<v8::Object> creation_context,
+ v8::Isolate* isolate) {
+ v8::Local<v8::Object> object;
+ {
+ v8::Context::Scope context_scope(creation_context->CreationContext());
+ object = v8::Object::New(isolate);
+ }
+ for (unsigned i = 0; i < value.size(); ++i) {
+ v8::Local<v8::Value> v8_value = ToV8(value[i].second, object, isolate);
+ if (v8_value.IsEmpty())
+ v8_value = v8::Undefined(isolate);
+ if (!V8CallBoolean(object->CreateDataProperty(
+ isolate->GetCurrentContext(), V8String(isolate, value[i].first),
+ v8_value)))
+ return v8::Local<v8::Value>();
+ }
+ return object;
+}
+
+template <typename T>
+inline v8::Local<v8::Value> ToV8(const HeapVector<std::pair<String, T>>& value,
+ v8::Local<v8::Object> creation_context,
+ v8::Isolate* isolate) {
+ v8::Local<v8::Object> object;
+ {
+ v8::Context::Scope context_scope(creation_context->CreationContext());
+ object = v8::Object::New(isolate);
+ }
+ for (unsigned i = 0; i < value.size(); ++i) {
+ v8::Local<v8::Value> v8_value = ToV8(value[i].second, object, isolate);
+ if (v8_value.IsEmpty())
+ v8_value = v8::Undefined(isolate);
+ if (!V8CallBoolean(object->CreateDataProperty(
+ isolate->GetCurrentContext(), V8String(isolate, value[i].first),
+ v8_value)))
+ return v8::Local<v8::Value>();
+ }
+ return object;
+}
+
+template <typename Sequence>
+inline v8::Local<v8::Value> ToV8SequenceInternal(
+ const Sequence& sequence,
+ v8::Local<v8::Object> creation_context,
+ v8::Isolate* isolate) {
+ RUNTIME_CALL_TIMER_SCOPE(isolate,
+ RuntimeCallStats::CounterId::kToV8SequenceInternal);
+ v8::Local<v8::Array> array;
+ {
+ v8::Context::Scope context_scope(creation_context->CreationContext());
+ array = v8::Array::New(isolate, sequence.size());
+ }
+ uint32_t index = 0;
+ typename Sequence::const_iterator end = sequence.end();
+ for (typename Sequence::const_iterator iter = sequence.begin(); iter != end;
+ ++iter) {
+ v8::Local<v8::Value> value = ToV8(*iter, array, isolate);
+ if (value.IsEmpty())
+ value = v8::Undefined(isolate);
+ if (!V8CallBoolean(array->CreateDataProperty(isolate->GetCurrentContext(),
+ index++, value)))
+ return v8::Local<v8::Value>();
+ }
+ return array;
+}
+
+// Nullable
+
+template <typename InnerType>
+inline v8::Local<v8::Value> ToV8(const base::Optional<InnerType>& value,
+ v8::Local<v8::Object> creation_context,
+ v8::Isolate* isolate) {
+ if (!value)
+ return v8::Null(isolate);
+ return ToV8(*value, creation_context, isolate);
+}
+
+// In all cases allow script state instead of creation context + isolate.
+// Use this function only if the call site does not otherwise need the global,
+// since v8::Context::Global is heavy.
+template <typename T>
+inline v8::Local<v8::Value> ToV8(T&& value, ScriptState* script_state) {
+ return ToV8(std::forward<T>(value), script_state->GetContext()->Global(),
+ script_state->GetIsolate());
+}
+
+// Only declare ToV8(void*,...) for checking function overload mismatch.
+// This ToV8(void*,...) should be never used. So we will find mismatch
+// because of "unresolved external symbol".
+// Without ToV8(void*, ...), call to toV8 with T* will match with
+// ToV8(bool, ...) if T is not a subclass of ScriptWrappable or if T is
+// declared but not defined (so it's not clear that T is a subclass of
+// ScriptWrappable).
+// This hack helps detect such unwanted implicit conversions from T* to bool.
+v8::Local<v8::Value> ToV8(void* value,
+ v8::Local<v8::Object> creation_context,
+ v8::Isolate*) = delete;
+
+} // namespace blink
+
+#endif // ToV8ForPlatform_h
diff --git a/chromium/third_party/blink/renderer/platform/bindings/trace_wrapper_base.h b/chromium/third_party/blink/renderer/platform/bindings/trace_wrapper_base.h
new file mode 100644
index 00000000000..1b36e449cc2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/trace_wrapper_base.h
@@ -0,0 +1,32 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_TRACE_WRAPPER_BASE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_TRACE_WRAPPER_BASE_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class ScriptWrappableVisitor;
+
+class PLATFORM_EXPORT TraceWrapperBase {
+ WTF_MAKE_NONCOPYABLE(TraceWrapperBase);
+
+ public:
+ TraceWrapperBase() = default;
+ ~TraceWrapperBase() = default;
+ virtual bool IsScriptWrappable() const { return false; }
+
+ virtual void TraceWrappers(const ScriptWrappableVisitor*) const = 0;
+
+ // Human-readable name of this object. The DevTools heap snapshot uses
+ // this method to show the object.
+ virtual const char* NameInHeapSnapshot() const = 0;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_TRACE_WRAPPER_BASE_H_
diff --git a/chromium/third_party/blink/renderer/platform/bindings/trace_wrapper_member.h b/chromium/third_party/blink/renderer/platform/bindings/trace_wrapper_member.h
new file mode 100644
index 00000000000..e3e21e414b2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/trace_wrapper_member.h
@@ -0,0 +1,120 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_TRACE_WRAPPER_MEMBER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_TRACE_WRAPPER_MEMBER_H_
+
+#include "third_party/blink/renderer/platform/bindings/script_wrappable_marking_visitor.h"
+#include "third_party/blink/renderer/platform/bindings/trace_wrapper_base.h"
+#include "third_party/blink/renderer/platform/heap/heap_allocator.h"
+
+namespace blink {
+
+template <typename T>
+class Member;
+
+// TraceWrapperMember is used for Member fields that should participate in
+// wrapper tracing, i.e., strongly hold a ScriptWrappable alive. All
+// TraceWrapperMember fields must be traced in the class' |TraceWrappers|
+// method.
+template <class T>
+class TraceWrapperMember : public Member<T> {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ // TODO(mlippautz): Enable the following check.
+ // static_assert(std::is_base_of<TraceWrapperBase, T>::value,
+ // "T must inherit from TraceWrapperBase");
+
+ public:
+ TraceWrapperMember() : Member<T>(nullptr) {}
+
+ TraceWrapperMember(T* raw) : Member<T>(raw) {
+ // We have to use a write barrier here because of in-place construction
+ // in containers, such as HeapVector::push_back.
+ ScriptWrappableMarkingVisitor::WriteBarrier(raw);
+ }
+
+ TraceWrapperMember(WTF::HashTableDeletedValueType x) : Member<T>(x) {}
+
+ TraceWrapperMember(const TraceWrapperMember& other) { *this = other; }
+
+ TraceWrapperMember& operator=(const TraceWrapperMember& other) {
+ Member<T>::operator=(other);
+ DCHECK_EQ(other.Get(), this->Get());
+ ScriptWrappableMarkingVisitor::WriteBarrier(this->Get());
+ return *this;
+ }
+
+ TraceWrapperMember& operator=(const Member<T>& other) {
+ Member<T>::operator=(other);
+ DCHECK_EQ(other.Get(), this->Get());
+ ScriptWrappableMarkingVisitor::WriteBarrier(this->Get());
+ return *this;
+ }
+
+ TraceWrapperMember& operator=(T* other) {
+ Member<T>::operator=(other);
+ DCHECK_EQ(other, this->Get());
+ ScriptWrappableMarkingVisitor::WriteBarrier(this->Get());
+ return *this;
+ }
+
+ TraceWrapperMember& operator=(std::nullptr_t) {
+ // No need for a write barrier when assigning nullptr.
+ Member<T>::operator=(nullptr);
+ return *this;
+ }
+};
+
+// Swaps two HeapVectors specialized for TraceWrapperMember. The custom swap
+// function is required as TraceWrapperMember potentially requires emitting a
+// write barrier.
+template <typename T>
+void swap(HeapVector<TraceWrapperMember<T>>& a,
+ HeapVector<TraceWrapperMember<T>>& b) {
+ // HeapVector<Member<T>> and HeapVector<TraceWrapperMember<T>> have the
+ // same size and semantics.
+ HeapVector<Member<T>>& a_ = reinterpret_cast<HeapVector<Member<T>>&>(a);
+ HeapVector<Member<T>>& b_ = reinterpret_cast<HeapVector<Member<T>>&>(b);
+ a_.swap(b_);
+ if (ThreadState::Current()->WrapperTracingInProgress()) {
+ // If incremental marking is enabled we need to emit the write barrier since
+ // the swap was performed on HeapVector<Member<T>>.
+ for (auto item : a) {
+ ScriptWrappableMarkingVisitor::WriteBarrier(item.Get());
+ }
+ for (auto item : b) {
+ ScriptWrappableMarkingVisitor::WriteBarrier(item.Get());
+ }
+ }
+}
+
+// HeapVectorBacking<TraceWrapperMember<T>> need to map to
+// HeapVectorBacking<Member<T>> for performing the swap method below.
+template <typename T, typename Traits>
+struct GCInfoTrait<HeapVectorBacking<TraceWrapperMember<T>, Traits>>
+ : public GCInfoTrait<
+ HeapVectorBacking<Member<T>, WTF::VectorTraits<Member<T>>>> {};
+
+// Swaps two HeapVectors, one containing TraceWrapperMember and one with
+// regular Members. The custom swap function is required as TraceWrapperMember
+// potentially requires emitting a write barrier.
+template <typename T>
+void swap(HeapVector<TraceWrapperMember<T>>& a, HeapVector<Member<T>>& b) {
+ // HeapVector<Member<T>> and HeapVector<TraceWrapperMember<T>> have the
+ // same size and semantics. This cast and swap assumes that GCInfo for both
+ // TraceWrapperMember and Member match in vector backings.
+ HeapVector<Member<T>>& a_ = reinterpret_cast<HeapVector<Member<T>>&>(a);
+ a_.swap(b);
+ if (ThreadState::Current()->WrapperTracingInProgress()) {
+ // If incremental marking is enabled we need to emit the write barrier since
+ // the swap was performed on HeapVector<Member<T>>.
+ for (auto item : a) {
+ ScriptWrappableMarkingVisitor::WriteBarrier(item.Get());
+ }
+ }
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_TRACE_WRAPPER_MEMBER_H_
diff --git a/chromium/third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h b/chromium/third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h
new file mode 100644
index 00000000000..3584abdb153
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h
@@ -0,0 +1,81 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_TRACE_WRAPPER_V8_REFERENCE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_TRACE_WRAPPER_V8_REFERENCE_H_
+
+#include "third_party/blink/renderer/platform/bindings/script_wrappable_marking_visitor.h"
+
+namespace blink {
+
+/**
+ * TraceWrapperV8Reference is used to trace from Blink to V8. If wrapper
+ * tracing is disabled, the reference is a weak v8::Persistent. Otherwise,
+ * the reference is (strongly) traced by wrapper tracing.
+ *
+ * TODO(mlippautz): Use a better handle type than v8::Persistent.
+ */
+template <typename T>
+class TraceWrapperV8Reference {
+ public:
+ TraceWrapperV8Reference() = default;
+
+ TraceWrapperV8Reference(v8::Isolate* isolate, v8::Local<T> handle) {
+ InternalSet(isolate, handle);
+ handle_.SetWeak();
+ }
+
+ ~TraceWrapperV8Reference() { Clear(); }
+
+ void Set(v8::Isolate* isolate, v8::Local<T> handle) {
+ InternalSet(isolate, handle);
+ handle_.SetWeak();
+ }
+
+ template <typename P>
+ void Set(v8::Isolate* isolate,
+ v8::Local<T> handle,
+ P* parameters,
+ void (*callback)(const v8::WeakCallbackInfo<P>&),
+ v8::WeakCallbackType type = v8::WeakCallbackType::kParameter) {
+ InternalSet(isolate, handle);
+ handle_.SetWeak(parameters, callback, type);
+ }
+
+ ALWAYS_INLINE v8::Local<T> NewLocal(v8::Isolate* isolate) const {
+ return v8::Local<T>::New(isolate, handle_);
+ }
+
+ bool IsEmpty() const { return handle_.IsEmpty(); }
+ void Clear() { handle_.Reset(); }
+ ALWAYS_INLINE const v8::Persistent<T>& Get() const { return handle_; }
+ ALWAYS_INLINE v8::Persistent<T>& Get() { return handle_; }
+
+ template <typename S>
+ const TraceWrapperV8Reference<S>& Cast() const {
+ static_assert(std::is_base_of<S, T>::value, "T must inherit from S");
+ return reinterpret_cast<const TraceWrapperV8Reference<S>&>(
+ const_cast<const TraceWrapperV8Reference<T>&>(*this));
+ }
+ // TODO(mlippautz): Support TraceWrappers(const
+ // TraceWrapperV8Reference<v8::Module>&) and remove UnsafeCast.
+ template <typename S>
+ const TraceWrapperV8Reference<S>& UnsafeCast() const {
+ return reinterpret_cast<const TraceWrapperV8Reference<S>&>(
+ const_cast<const TraceWrapperV8Reference<T>&>(*this));
+ }
+
+ private:
+ inline void InternalSet(v8::Isolate* isolate, v8::Local<T> handle) {
+ handle_.Reset(isolate, handle);
+ ScriptWrappableMarkingVisitor::WriteBarrier(isolate,
+ UnsafeCast<v8::Value>());
+ }
+
+ v8::Persistent<T> handle_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_TRACE_WRAPPER_V8_REFERENCE_H_
diff --git a/chromium/third_party/blink/renderer/platform/bindings/trace_wrapper_v8_string.cc b/chromium/third_party/blink/renderer/platform/bindings/trace_wrapper_v8_string.cc
new file mode 100644
index 00000000000..3fd53edc211
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/trace_wrapper_v8_string.cc
@@ -0,0 +1,29 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/bindings/trace_wrapper_v8_string.h"
+#include "third_party/blink/renderer/platform/bindings/v8_binding.h"
+
+namespace blink {
+
+void TraceWrapperV8String::Concat(v8::Isolate* isolate, const String& string) {
+ DCHECK(isolate);
+ v8::HandleScope handle_scope(isolate);
+ v8::Local<v8::String> target_string =
+ (string_.IsEmpty()) ? V8String(isolate, string)
+ : v8::String::Concat(string_.NewLocal(isolate),
+ V8String(isolate, string));
+ string_.Set(isolate, target_string);
+}
+
+String TraceWrapperV8String::Flatten(v8::Isolate* isolate) const {
+ if (IsEmpty())
+ return String();
+ DCHECK(isolate);
+ v8::HandleScope handle_scope(isolate);
+ return V8StringToWebCoreString<String>(string_.NewLocal(isolate),
+ kExternalize);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/bindings/trace_wrapper_v8_string.h b/chromium/third_party/blink/renderer/platform/bindings/trace_wrapper_v8_string.h
new file mode 100644
index 00000000000..1f637e384cb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/trace_wrapper_v8_string.h
@@ -0,0 +1,48 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_TRACE_WRAPPER_V8_STRING_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_TRACE_WRAPPER_V8_STRING_H_
+
+#include "third_party/blink/renderer/platform/bindings/trace_wrapper_base.h"
+#include "third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+// Small shim around TraceWrapperReference<v8::String> with a few
+// utility methods. Internally, v8::String is represented as string
+// rope.
+class PLATFORM_EXPORT TraceWrapperV8String final : public TraceWrapperBase {
+ DISALLOW_COPY_AND_ASSIGN(TraceWrapperV8String);
+ DISALLOW_NEW();
+
+ public:
+ TraceWrapperV8String() = default;
+
+ bool IsEmpty() const { return string_.IsEmpty(); }
+ void Clear() { string_.Clear(); }
+
+ v8::Local<v8::String> V8Value(v8::Isolate* isolate) {
+ return string_.NewLocal(isolate);
+ }
+
+ void Concat(v8::Isolate*, const String&);
+ String Flatten(v8::Isolate*) const;
+
+ void TraceWrappers(const ScriptWrappableVisitor* visitor) const override {
+ visitor->TraceWrappers(string_);
+ }
+ const char* NameInHeapSnapshot() const override {
+ return "TraceWrapperV8String";
+ }
+
+ private:
+ TraceWrapperV8Reference<v8::String> string_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_TRACE_WRAPPER_V8_STRING_H_
diff --git a/chromium/third_party/blink/renderer/platform/bindings/v0_custom_element_binding.cc b/chromium/third_party/blink/renderer/platform/bindings/v0_custom_element_binding.cc
new file mode 100644
index 00000000000..68e534af779
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/v0_custom_element_binding.cc
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/renderer/platform/bindings/v0_custom_element_binding.h"
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+
+namespace blink {
+
+std::unique_ptr<V0CustomElementBinding> V0CustomElementBinding::Create(
+ v8::Isolate* isolate,
+ v8::Local<v8::Object> prototype) {
+ return base::WrapUnique(new V0CustomElementBinding(isolate, prototype));
+}
+
+V0CustomElementBinding::V0CustomElementBinding(v8::Isolate* isolate,
+ v8::Local<v8::Object> prototype)
+ : prototype_(isolate, prototype) {
+ DCHECK(!prototype_.IsEmpty());
+}
+
+V0CustomElementBinding::~V0CustomElementBinding() = default;
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/bindings/v0_custom_element_binding.h b/chromium/third_party/blink/renderer/platform/bindings/v0_custom_element_binding.h
new file mode 100644
index 00000000000..c5a005f7809
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/v0_custom_element_binding.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V0_CUSTOM_ELEMENT_BINDING_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V0_CUSTOM_ELEMENT_BINDING_H_
+
+#include <memory>
+
+#include "third_party/blink/renderer/platform/bindings/scoped_persistent.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT V0CustomElementBinding {
+ USING_FAST_MALLOC(V0CustomElementBinding);
+
+ public:
+ static std::unique_ptr<V0CustomElementBinding> Create(
+ v8::Isolate*,
+ v8::Local<v8::Object> prototype);
+ ~V0CustomElementBinding();
+
+ private:
+ V0CustomElementBinding(v8::Isolate*, v8::Local<v8::Object> prototype);
+ ScopedPersistent<v8::Object> prototype_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V0_CUSTOM_ELEMENT_BINDING_H_
diff --git a/chromium/third_party/blink/renderer/platform/bindings/v8_binding.cc b/chromium/third_party/blink/renderer/platform/bindings/v8_binding.cc
new file mode 100644
index 00000000000..b3be2954a73
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/v8_binding.cc
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2006, 2007, 2008, 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/renderer/platform/bindings/v8_binding.h"
+
+#include "third_party/blink/renderer/platform/bindings/v8_binding_macros.h"
+
+namespace blink {
+
+v8::Local<v8::Function> GetBoundFunction(v8::Local<v8::Function> function) {
+ v8::Local<v8::Value> bound_function = function->GetBoundFunction();
+ return bound_function->IsFunction()
+ ? v8::Local<v8::Function>::Cast(bound_function)
+ : function;
+}
+
+v8::Local<v8::Value> FreezeV8Object(v8::Local<v8::Value> value,
+ v8::Isolate* isolate) {
+ value.As<v8::Object>()
+ ->SetIntegrityLevel(isolate->GetCurrentContext(),
+ v8::IntegrityLevel::kFrozen)
+ .ToChecked();
+ return value;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/bindings/v8_binding.h b/chromium/third_party/blink/renderer/platform/bindings/v8_binding.h
new file mode 100644
index 00000000000..54b6db45c3e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/v8_binding.h
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ * Copyright (C) 2012 Ericsson AB. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_BINDING_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_BINDING_H_
+
+#include "third_party/blink/renderer/platform/bindings/dom_data_store.h"
+#include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/bindings/string_resource.h"
+#include "third_party/blink/renderer/platform/bindings/v8_binding_macros.h"
+#include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
+#include "third_party/blink/renderer/platform/bindings/v8_value_cache.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_view.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+// This file contains bindings helper functions that do not have dependencies
+// to core/ or bindings/core. For core-specific helper functions, see
+// bindings/core/v8/V8BindingForCore.h.
+
+template <typename T>
+struct V8TypeOf {
+ STATIC_ONLY(V8TypeOf);
+ // |Type| provides C++ -> V8 type conversion for DOM wrappers.
+ // The Blink binding code generator will generate specialized version of
+ // V8TypeOf for each wrapper class.
+ typedef void Type;
+};
+
+template <typename CallbackInfo, typename S>
+inline void V8SetReturnValue(const CallbackInfo& info,
+ const v8::Persistent<S>& handle) {
+ info.GetReturnValue().Set(handle);
+}
+
+template <typename CallbackInfo, typename S>
+inline void V8SetReturnValue(const CallbackInfo& info,
+ const v8::Local<S> handle) {
+ info.GetReturnValue().Set(handle);
+}
+
+template <typename CallbackInfo, typename S>
+inline void V8SetReturnValue(const CallbackInfo& info,
+ v8::MaybeLocal<S> maybe) {
+ if (LIKELY(!maybe.IsEmpty()))
+ info.GetReturnValue().Set(maybe.ToLocalChecked());
+}
+
+template <typename CallbackInfo>
+inline void V8SetReturnValue(const CallbackInfo& info, bool value) {
+ info.GetReturnValue().Set(value);
+}
+
+template <typename CallbackInfo>
+inline void V8SetReturnValue(const CallbackInfo& info, double value) {
+ info.GetReturnValue().Set(value);
+}
+
+template <typename CallbackInfo>
+inline void V8SetReturnValue(const CallbackInfo& info, int32_t value) {
+ info.GetReturnValue().Set(value);
+}
+
+template <typename CallbackInfo>
+inline void V8SetReturnValue(const CallbackInfo& info, uint32_t value) {
+ info.GetReturnValue().Set(value);
+}
+
+template <typename CallbackInfo>
+inline void V8SetReturnValueBool(const CallbackInfo& info, bool v) {
+ info.GetReturnValue().Set(v);
+}
+
+template <typename CallbackInfo>
+inline void V8SetReturnValueInt(const CallbackInfo& info, int v) {
+ info.GetReturnValue().Set(v);
+}
+
+template <typename CallbackInfo>
+inline void V8SetReturnValueUnsigned(const CallbackInfo& info, unsigned v) {
+ info.GetReturnValue().Set(v);
+}
+
+template <typename CallbackInfo>
+inline void V8SetReturnValueNull(const CallbackInfo& info) {
+ info.GetReturnValue().SetNull();
+}
+
+template <typename CallbackInfo>
+inline void V8SetReturnValueUndefined(const CallbackInfo& info) {
+ info.GetReturnValue().SetUndefined();
+}
+
+template <typename CallbackInfo>
+inline void V8SetReturnValueEmptyString(const CallbackInfo& info) {
+ info.GetReturnValue().SetEmptyString();
+}
+
+template <typename CallbackInfo>
+inline void V8SetReturnValueString(const CallbackInfo& info,
+ const String& string,
+ v8::Isolate* isolate) {
+ if (string.IsNull()) {
+ V8SetReturnValueEmptyString(info);
+ return;
+ }
+ V8PerIsolateData::From(isolate)->GetStringCache()->SetReturnValueFromString(
+ info.GetReturnValue(), string.Impl());
+}
+
+template <typename CallbackInfo>
+inline void V8SetReturnValueStringOrNull(const CallbackInfo& info,
+ const String& string,
+ v8::Isolate* isolate) {
+ if (string.IsNull()) {
+ V8SetReturnValueNull(info);
+ return;
+ }
+ V8PerIsolateData::From(isolate)->GetStringCache()->SetReturnValueFromString(
+ info.GetReturnValue(), string.Impl());
+}
+
+template <typename CallbackInfo>
+inline void V8SetReturnValue(const CallbackInfo& callback_info,
+ ScriptWrappable* impl,
+ v8::Local<v8::Object> creation_context) {
+ if (UNLIKELY(!impl)) {
+ V8SetReturnValueNull(callback_info);
+ return;
+ }
+ if (DOMDataStore::SetReturnValue(callback_info.GetReturnValue(), impl))
+ return;
+ v8::Local<v8::Object> wrapper =
+ impl->Wrap(callback_info.GetIsolate(), creation_context);
+ V8SetReturnValue(callback_info, wrapper);
+}
+
+template <typename CallbackInfo>
+inline void V8SetReturnValue(const CallbackInfo& callback_info,
+ ScriptWrappable* impl) {
+ V8SetReturnValue(callback_info, impl, callback_info.Holder());
+}
+
+template <typename CallbackInfo>
+inline void V8SetReturnValueForMainWorld(const CallbackInfo& callback_info,
+ ScriptWrappable* impl) {
+ DCHECK(DOMWrapperWorld::Current(callback_info.GetIsolate()).IsMainWorld());
+ if (UNLIKELY(!impl)) {
+ V8SetReturnValueNull(callback_info);
+ return;
+ }
+ if (DOMDataStore::SetReturnValueForMainWorld(callback_info.GetReturnValue(),
+ impl))
+ return;
+ v8::Local<v8::Object> wrapper =
+ impl->Wrap(callback_info.GetIsolate(), callback_info.Holder());
+ V8SetReturnValue(callback_info, wrapper);
+}
+
+template <typename CallbackInfo>
+inline void V8SetReturnValueFast(const CallbackInfo& callback_info,
+ ScriptWrappable* impl,
+ const ScriptWrappable* wrappable) {
+ if (UNLIKELY(!impl)) {
+ V8SetReturnValueNull(callback_info);
+ return;
+ }
+ if (DOMDataStore::SetReturnValueFast(callback_info.GetReturnValue(), impl,
+ callback_info.Holder(), wrappable))
+ return;
+ v8::Local<v8::Object> wrapper =
+ impl->Wrap(callback_info.GetIsolate(), callback_info.Holder());
+ V8SetReturnValue(callback_info, wrapper);
+}
+
+template <typename CallbackInfo, typename T>
+inline void V8SetReturnValueFast(const CallbackInfo& callback_info,
+ const v8::Local<T> handle,
+ const ScriptWrappable*) {
+ V8SetReturnValue(callback_info, handle);
+}
+
+// Convert v8::String to a WTF::String. If the V8 string is not already
+// an external string then it is transformed into an external string at this
+// point to avoid repeated conversions.
+inline String ToCoreString(v8::Local<v8::String> value) {
+ return V8StringToWebCoreString<String>(value, kExternalize);
+}
+
+inline String ToCoreStringWithNullCheck(v8::Local<v8::String> value) {
+ if (value.IsEmpty() || value->IsNull())
+ return String();
+ return ToCoreString(value);
+}
+
+inline String ToCoreStringWithUndefinedOrNullCheck(
+ v8::Local<v8::String> value) {
+ if (value.IsEmpty())
+ return String();
+ return ToCoreString(value);
+}
+
+inline AtomicString ToCoreAtomicString(v8::Local<v8::String> value) {
+ return V8StringToWebCoreString<AtomicString>(value, kExternalize);
+}
+
+// This method will return a null String if the v8::Value does not contain a
+// v8::String. It will not call ToString() on the v8::Value. If you want
+// ToString() to be called, please use the TONATIVE_FOR_V8STRINGRESOURCE_*()
+// macros instead.
+inline String ToCoreStringWithUndefinedOrNullCheck(v8::Local<v8::Value> value) {
+ if (value.IsEmpty() || !value->IsString())
+ return String();
+ return ToCoreString(value.As<v8::String>());
+}
+
+// Convert a string to a V8 string.
+
+inline v8::Local<v8::String> V8String(v8::Isolate* isolate,
+ const StringView& string) {
+ DCHECK(isolate);
+ if (string.IsNull())
+ return v8::String::Empty(isolate);
+ if (StringImpl* impl = string.SharedImpl()) {
+ return V8PerIsolateData::From(isolate)->GetStringCache()->V8ExternalString(
+ isolate, impl);
+ }
+ if (string.Is8Bit()) {
+ return v8::String::NewFromOneByte(
+ isolate, reinterpret_cast<const uint8_t*>(string.Characters8()),
+ v8::NewStringType::kNormal, static_cast<int>(string.length()))
+ .ToLocalChecked();
+ }
+ return v8::String::NewFromTwoByte(
+ isolate, reinterpret_cast<const uint16_t*>(string.Characters16()),
+ v8::NewStringType::kNormal, static_cast<int>(string.length()))
+ .ToLocalChecked();
+}
+
+// As above, for string literals. The compiler doesn't optimize away the is8Bit
+// and sharedImpl checks for string literals in the StringView version.
+inline v8::Local<v8::String> V8String(v8::Isolate* isolate,
+ const char* string) {
+ DCHECK(isolate);
+ if (!string || string[0] == '\0')
+ return v8::String::Empty(isolate);
+ return v8::String::NewFromOneByte(isolate,
+ reinterpret_cast<const uint8_t*>(string),
+ v8::NewStringType::kNormal, strlen(string))
+ .ToLocalChecked();
+}
+
+inline v8::Local<v8::Value> V8StringOrNull(v8::Isolate* isolate,
+ const AtomicString& string) {
+ if (string.IsNull())
+ return v8::Null(isolate);
+ return V8PerIsolateData::From(isolate)->GetStringCache()->V8ExternalString(
+ isolate, string.Impl());
+}
+
+inline v8::Local<v8::String> V8AtomicString(v8::Isolate* isolate,
+ const StringView& string) {
+ DCHECK(isolate);
+ if (string.Is8Bit()) {
+ return v8::String::NewFromOneByte(
+ isolate, reinterpret_cast<const uint8_t*>(string.Characters8()),
+ v8::NewStringType::kInternalized,
+ static_cast<int>(string.length()))
+ .ToLocalChecked();
+ }
+ return v8::String::NewFromTwoByte(
+ isolate, reinterpret_cast<const uint16_t*>(string.Characters16()),
+ v8::NewStringType::kInternalized,
+ static_cast<int>(string.length()))
+ .ToLocalChecked();
+}
+
+// As above, for string literals. The compiler doesn't optimize away the is8Bit
+// check for string literals in the StringView version.
+inline v8::Local<v8::String> V8AtomicString(v8::Isolate* isolate,
+ const char* string) {
+ DCHECK(isolate);
+ if (!string || string[0] == '\0')
+ return v8::String::Empty(isolate);
+ return v8::String::NewFromOneByte(
+ isolate, reinterpret_cast<const uint8_t*>(string),
+ v8::NewStringType::kInternalized, strlen(string))
+ .ToLocalChecked();
+}
+
+inline v8::Local<v8::String> V8StringFromUtf8(v8::Isolate* isolate,
+ const char* bytes,
+ int length) {
+ DCHECK(isolate);
+ return v8::String::NewFromUtf8(isolate, bytes, v8::NewStringType::kNormal,
+ length)
+ .ToLocalChecked();
+}
+
+inline v8::Local<v8::Value> V8Undefined() {
+ return v8::Local<v8::Value>();
+}
+
+inline v8::MaybeLocal<v8::Value> V8DateOrNaN(v8::Isolate* isolate,
+ double value) {
+ DCHECK(isolate);
+ return v8::Date::New(isolate->GetCurrentContext(), value);
+}
+
+inline bool IsUndefinedOrNull(v8::Local<v8::Value> value) {
+ return value.IsEmpty() || value->IsNullOrUndefined();
+}
+PLATFORM_EXPORT v8::Local<v8::Function> GetBoundFunction(
+ v8::Local<v8::Function>);
+
+// FIXME: This will be soon embedded in the generated code.
+template <typename Collection>
+static void IndexedPropertyEnumerator(
+ const v8::PropertyCallbackInfo<v8::Array>& info) {
+ Collection* collection =
+ ToScriptWrappable(info.Holder())->ToImpl<Collection>();
+ int length = collection->length();
+ v8::Local<v8::Array> properties = v8::Array::New(info.GetIsolate(), length);
+ v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
+ for (int i = 0; i < length; ++i) {
+ v8::Local<v8::Integer> integer = v8::Integer::New(info.GetIsolate(), i);
+ if (!V8CallBoolean(properties->CreateDataProperty(context, i, integer)))
+ return;
+ }
+ V8SetReturnValue(info, properties);
+}
+
+// Freeze a V8 object. The type of the first parameter and the return value is
+// intentionally v8::Value so that this function can wrap ToV8().
+// If the argument isn't an object, this will crash.
+PLATFORM_EXPORT v8::Local<v8::Value> FreezeV8Object(v8::Local<v8::Value>,
+ v8::Isolate*);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_BINDING_H_
diff --git a/chromium/third_party/blink/renderer/platform/bindings/v8_binding_macros.h b/chromium/third_party/blink/renderer/platform/bindings/v8_binding_macros.h
new file mode 100644
index 00000000000..83751de1266
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/v8_binding_macros.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2010 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_BINDING_MACROS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_BINDING_MACROS_H_
+
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+// type is an instance of class template V8StringResource<>,
+// but Mode argument varies; using type (not Mode) for consistency
+// with other macros and ease of code generation
+#define TOSTRING_VOID(type, var, value) \
+ type var(value); \
+ if (UNLIKELY(!var.Prepare())) \
+ return;
+
+#define TOSTRING_DEFAULT(type, var, value, retVal) \
+ type var(value); \
+ if (UNLIKELY(!var.Prepare())) \
+ return retVal;
+
+// Checks for a given v8::Value (value) whether it is an ArrayBufferView and
+// below a certain size limit. If below the limit, memory is allocated on the
+// stack to hold the actual payload. Keep the limit in sync with V8's
+// typed_array_max_size.
+#define allocateFlexibleArrayBufferViewStorage(value) \
+ (value->IsArrayBufferView() && \
+ (value.As<v8::ArrayBufferView>()->ByteLength() <= 64) \
+ ? alloca(value.As<v8::ArrayBufferView>()->ByteLength()) \
+ : nullptr)
+
+// DEPRECATED
+inline bool V8CallBoolean(v8::Maybe<bool> maybe) {
+ bool result;
+ return maybe.To(&result) && result;
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_BINDING_MACROS_H_
diff --git a/chromium/third_party/blink/renderer/platform/bindings/v8_dom_activity_logger.cc b/chromium/third_party/blink/renderer/platform/bindings/v8_dom_activity_logger.cc
new file mode 100644
index 00000000000..39c7b4c3800
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/v8_dom_activity_logger.cc
@@ -0,0 +1,134 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/bindings/v8_dom_activity_logger.h"
+
+#include <memory>
+#include "third_party/blink/renderer/platform/bindings/v8_binding.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+
+namespace blink {
+
+typedef HashMap<String, std::unique_ptr<V8DOMActivityLogger>>
+ DOMActivityLoggerMapForMainWorld;
+typedef HashMap<int,
+ std::unique_ptr<V8DOMActivityLogger>,
+ WTF::IntHash<int>,
+ WTF::UnsignedWithZeroKeyHashTraits<int>>
+ DOMActivityLoggerMapForIsolatedWorld;
+
+static DOMActivityLoggerMapForMainWorld& DomActivityLoggersForMainWorld() {
+ DCHECK(IsMainThread());
+ DEFINE_STATIC_LOCAL(DOMActivityLoggerMapForMainWorld, map, ());
+ return map;
+}
+
+static DOMActivityLoggerMapForIsolatedWorld&
+DomActivityLoggersForIsolatedWorld() {
+ DCHECK(IsMainThread());
+ DEFINE_STATIC_LOCAL(DOMActivityLoggerMapForIsolatedWorld, map, ());
+ return map;
+}
+
+void V8DOMActivityLogger::LogMethod(const char* api_name,
+ v8::FunctionCallbackInfo<v8::Value> info) {
+ Vector<v8::Local<v8::Value>> loggerArgs;
+ loggerArgs.ReserveInitialCapacity(info.Length());
+ for (int i = 0; i < info.Length(); ++i) {
+ loggerArgs.UncheckedAppend(info[i]);
+ }
+ LogMethod(api_name, info.Length(), loggerArgs.data());
+}
+
+void V8DOMActivityLogger::SetActivityLogger(
+ int world_id,
+ const String& extension_id,
+ std::unique_ptr<V8DOMActivityLogger> logger) {
+ if (world_id)
+ DomActivityLoggersForIsolatedWorld().Set(world_id, std::move(logger));
+ else
+ DomActivityLoggersForMainWorld().Set(extension_id, std::move(logger));
+}
+
+V8DOMActivityLogger* V8DOMActivityLogger::ActivityLogger(
+ int world_id,
+ const String& extension_id) {
+ if (world_id) {
+ DOMActivityLoggerMapForIsolatedWorld& loggers =
+ DomActivityLoggersForIsolatedWorld();
+ DOMActivityLoggerMapForIsolatedWorld::iterator it = loggers.find(world_id);
+ return it == loggers.end() ? nullptr : it->value.get();
+ }
+
+ if (extension_id.IsEmpty())
+ return nullptr;
+
+ DOMActivityLoggerMapForMainWorld& loggers = DomActivityLoggersForMainWorld();
+ DOMActivityLoggerMapForMainWorld::iterator it = loggers.find(extension_id);
+ return it == loggers.end() ? nullptr : it->value.get();
+}
+
+V8DOMActivityLogger* V8DOMActivityLogger::ActivityLogger(int world_id,
+ const KURL& url) {
+ // extension ID is ignored for worldId != 0.
+ if (world_id)
+ return ActivityLogger(world_id, String());
+
+ // To find an activity logger that corresponds to the main world of an
+ // extension, we need to obtain the extension ID. Extension ID is a hostname
+ // of a background page's URL.
+ if (!url.ProtocolIs("chrome-extension"))
+ return nullptr;
+
+ return ActivityLogger(world_id, url.Host());
+}
+
+V8DOMActivityLogger* V8DOMActivityLogger::CurrentActivityLogger() {
+ v8::Isolate* isolate = v8::Isolate::GetCurrent();
+ if (!isolate->InContext())
+ return nullptr;
+
+ v8::HandleScope handle_scope(isolate);
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
+
+ V8PerContextData* context_data = ScriptState::From(context)->PerContextData();
+ if (!context_data)
+ return nullptr;
+
+ return context_data->ActivityLogger();
+}
+
+V8DOMActivityLogger* V8DOMActivityLogger::CurrentActivityLoggerIfIsolatedWorld(
+ v8::Isolate* isolate) {
+ if (!isolate->InContext())
+ return nullptr;
+
+ ScriptState* script_state = ScriptState::From(isolate->GetCurrentContext());
+ if (!script_state->World().IsIsolatedWorld())
+ return nullptr;
+
+ V8PerContextData* context_data = script_state->PerContextData();
+ if (!context_data)
+ return nullptr;
+
+ return context_data->ActivityLogger();
+}
+
+V8DOMActivityLogger*
+V8DOMActivityLogger::CurrentActivityLoggerIfIsolatedWorld() {
+ return CurrentActivityLoggerIfIsolatedWorld(v8::Isolate::GetCurrent());
+}
+
+V8DOMActivityLogger*
+V8DOMActivityLogger::CurrentActivityLoggerIfIsolatedWorldForMainThread() {
+ DCHECK(IsMainThread());
+ if (DomActivityLoggersForIsolatedWorld().IsEmpty())
+ return nullptr;
+ return CurrentActivityLoggerIfIsolatedWorld(
+ V8PerIsolateData::MainThreadIsolate());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/bindings/v8_dom_activity_logger.h b/chromium/third_party/blink/renderer/platform/bindings/v8_dom_activity_logger.h
new file mode 100644
index 00000000000..44513a05313
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/v8_dom_activity_logger.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_DOM_ACTIVITY_LOGGER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_DOM_ACTIVITY_LOGGER_H_
+
+#include <memory>
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+class KURL;
+
+class PLATFORM_EXPORT V8DOMActivityLogger {
+ USING_FAST_MALLOC(V8DOMActivityLogger);
+
+ public:
+ virtual ~V8DOMActivityLogger() = default;
+
+ virtual void LogGetter(const String& api_name) {}
+ virtual void LogSetter(const String& api_name,
+ const v8::Local<v8::Value>& new_value) {}
+ virtual void LogMethod(const String& api_name,
+ int argc,
+ const v8::Local<v8::Value>* argv) {}
+ virtual void LogEvent(const String& event_name,
+ int argc,
+ const String* argv) {}
+
+ void LogMethod(const char* api_name, v8::FunctionCallbackInfo<v8::Value>);
+
+ // Associates a logger with the world identified by worldId (worlId may be 0
+ // identifying the main world) and extension ID. Extension ID is used to
+ // identify a logger for main world only (worldId == 0). If the world is not
+ // a main world, an extension ID is ignored.
+ //
+ // A renderer process may host multiple extensions when the browser hits the
+ // renderer process limit. In such case, we assign multiple extensions to
+ // the same main world of a renderer process. In order to distinguish the
+ // extensions and their activity loggers in the main world, we require an
+ // extension ID. Otherwise, extension activities may be logged under
+ // a wrong extension ID.
+ static void SetActivityLogger(int world_id,
+ const String&,
+ std::unique_ptr<V8DOMActivityLogger>);
+ static V8DOMActivityLogger* ActivityLogger(int world_id,
+ const String& extension_id);
+ static V8DOMActivityLogger* ActivityLogger(int world_id, const KURL&);
+
+ // Returns activity logger for current V8 context or 0.
+ static V8DOMActivityLogger* CurrentActivityLogger();
+ // Returns activity logger for current V8 context if the context belongs to
+ // an isolated world or 0.
+ static V8DOMActivityLogger* CurrentActivityLoggerIfIsolatedWorld();
+ static V8DOMActivityLogger*
+ CurrentActivityLoggerIfIsolatedWorldForMainThread();
+
+ private:
+ static V8DOMActivityLogger* CurrentActivityLoggerIfIsolatedWorld(
+ v8::Isolate*);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_DOM_ACTIVITY_LOGGER_H_
diff --git a/chromium/third_party/blink/renderer/platform/bindings/v8_dom_wrapper.cc b/chromium/third_party/blink/renderer/platform/bindings/v8_dom_wrapper.cc
new file mode 100644
index 00000000000..12104ab2a26
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/v8_dom_wrapper.cc
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/renderer/platform/bindings/v8_dom_wrapper.h"
+
+#include "third_party/blink/renderer/platform/bindings/v8_binding.h"
+#include "third_party/blink/renderer/platform/bindings/v8_object_constructor.h"
+#include "third_party/blink/renderer/platform/bindings/v8_per_context_data.h"
+#include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
+
+namespace blink {
+
+v8::Local<v8::Object> V8DOMWrapper::CreateWrapper(
+ v8::Isolate* isolate,
+ v8::Local<v8::Object> creation_context,
+ const WrapperTypeInfo* type) {
+ RUNTIME_CALL_TIMER_SCOPE(isolate,
+ RuntimeCallStats::CounterId::kCreateWrapper);
+
+ // TODO(adithyas): We should abort wrapper creation if the context access
+ // check fails and throws an exception.
+ V8WrapperInstantiationScope scope(creation_context, isolate, type);
+ CHECK(!scope.AccessCheckFailed());
+
+ V8PerContextData* per_context_data =
+ V8PerContextData::From(scope.GetContext());
+ v8::Local<v8::Object> wrapper;
+ if (per_context_data) {
+ wrapper = per_context_data->CreateWrapperFromCache(type);
+ CHECK(!wrapper.IsEmpty());
+ } else {
+ // The context is detached, but still accessible.
+ // TODO(yukishiino): This code does not create a wrapper with
+ // the correct settings. Should follow the same way as
+ // V8PerContextData::createWrapperFromCache, though there is no need to
+ // cache resulting objects or their constructors.
+ const DOMWrapperWorld& world = DOMWrapperWorld::World(scope.GetContext());
+ wrapper = type->domTemplate(isolate, world)
+ ->InstanceTemplate()
+ ->NewInstance(scope.GetContext())
+ .ToLocalChecked();
+ }
+ return wrapper;
+}
+
+bool V8DOMWrapper::IsWrapper(v8::Isolate* isolate, v8::Local<v8::Value> value) {
+ if (value.IsEmpty() || !value->IsObject())
+ return false;
+ v8::Local<v8::Object> object = v8::Local<v8::Object>::Cast(value);
+
+ if (object->InternalFieldCount() < kV8DefaultWrapperInternalFieldCount)
+ return false;
+
+ const WrapperTypeInfo* untrusted_wrapper_type_info =
+ ToWrapperTypeInfo(object);
+ V8PerIsolateData* per_isolate_data = V8PerIsolateData::From(isolate);
+ if (!(untrusted_wrapper_type_info && per_isolate_data))
+ return false;
+ return per_isolate_data->HasInstance(untrusted_wrapper_type_info, object);
+}
+
+bool V8DOMWrapper::HasInternalFieldsSet(v8::Local<v8::Value> value) {
+ if (value.IsEmpty() || !value->IsObject())
+ return false;
+ v8::Local<v8::Object> object = v8::Local<v8::Object>::Cast(value);
+
+ if (object->InternalFieldCount() < kV8DefaultWrapperInternalFieldCount)
+ return false;
+
+ const ScriptWrappable* untrusted_script_wrappable = ToScriptWrappable(object);
+ const WrapperTypeInfo* untrusted_wrapper_type_info =
+ ToWrapperTypeInfo(object);
+ return untrusted_script_wrappable && untrusted_wrapper_type_info &&
+ untrusted_wrapper_type_info->gin_embedder == gin::kEmbedderBlink;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/bindings/v8_dom_wrapper.h b/chromium/third_party/blink/renderer/platform/bindings/v8_dom_wrapper.h
new file mode 100644
index 00000000000..bedf60e7099
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/v8_dom_wrapper.h
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_DOM_WRAPPER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_DOM_WRAPPER_H_
+
+#include "third_party/blink/renderer/platform/bindings/dom_data_store.h"
+#include "third_party/blink/renderer/platform/bindings/runtime_call_stats.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/bindings/v8_binding.h"
+#include "third_party/blink/renderer/platform/bindings/wrapper_creation_security_check.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/compiler.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+struct WrapperTypeInfo;
+
+// Contains utility methods to create wrappers, associate ScriptWrappable
+// objects with wrappers and set fields in wrappers.
+class V8DOMWrapper {
+ STATIC_ONLY(V8DOMWrapper);
+
+ public:
+ PLATFORM_EXPORT static v8::Local<v8::Object> CreateWrapper(
+ v8::Isolate*,
+ v8::Local<v8::Object> creation_context,
+ const WrapperTypeInfo*);
+ PLATFORM_EXPORT static bool IsWrapper(v8::Isolate*, v8::Local<v8::Value>);
+
+ // Associates the given ScriptWrappable with the given |wrapper| if the
+ // ScriptWrappable is not yet associated with any wrapper. Returns the
+ // wrapper already associated or |wrapper| if not yet associated.
+ // The caller should always use the returned value rather than |wrapper|.
+ PLATFORM_EXPORT WARN_UNUSED_RESULT static v8::Local<v8::Object>
+ AssociateObjectWithWrapper(v8::Isolate*,
+ ScriptWrappable*,
+ const WrapperTypeInfo*,
+ v8::Local<v8::Object> wrapper);
+ static void SetNativeInfo(v8::Isolate*,
+ v8::Local<v8::Object>,
+ const WrapperTypeInfo*,
+ ScriptWrappable*);
+ static void ClearNativeInfo(v8::Isolate*, v8::Local<v8::Object>);
+
+ // hasInternalFieldsSet only checks if the value has the internal fields for
+ // wrapper obejct and type, and does not check if it's valid or not. The
+ // value may not be a Blink's wrapper object. In order to make sure of it,
+ // Use isWrapper function instead.
+ PLATFORM_EXPORT static bool HasInternalFieldsSet(v8::Local<v8::Value>);
+};
+
+inline void V8DOMWrapper::SetNativeInfo(
+ v8::Isolate* isolate,
+ v8::Local<v8::Object> wrapper,
+ const WrapperTypeInfo* wrapper_type_info,
+ ScriptWrappable* script_wrappable) {
+ DCHECK_GE(wrapper->InternalFieldCount(), 2);
+ DCHECK(script_wrappable);
+ DCHECK(wrapper_type_info);
+ int indices[] = {kV8DOMWrapperObjectIndex, kV8DOMWrapperTypeIndex};
+ void* values[] = {script_wrappable,
+ const_cast<WrapperTypeInfo*>(wrapper_type_info)};
+ wrapper->SetAlignedPointerInInternalFields(WTF_ARRAY_LENGTH(indices), indices,
+ values);
+ auto* per_isolate_data = V8PerIsolateData::From(isolate);
+ // We notify ScriptWrappableVisitor about the new wrapper association,
+ // so the visitor can make sure to trace the association (in case it is
+ // currently tracing). Because of some optimizations, V8 will not
+ // necessarily detect wrappers created during its incremental marking.
+ per_isolate_data->GetScriptWrappableMarkingVisitor()->RegisterV8Reference(
+ std::make_pair(const_cast<WrapperTypeInfo*>(wrapper_type_info),
+ script_wrappable));
+}
+
+inline void V8DOMWrapper::ClearNativeInfo(v8::Isolate* isolate,
+ v8::Local<v8::Object> wrapper) {
+ int indices[] = {kV8DOMWrapperObjectIndex, kV8DOMWrapperTypeIndex};
+ void* values[] = {nullptr, nullptr};
+ wrapper->SetAlignedPointerInInternalFields(WTF_ARRAY_LENGTH(indices), indices,
+ values);
+}
+
+inline v8::Local<v8::Object> V8DOMWrapper::AssociateObjectWithWrapper(
+ v8::Isolate* isolate,
+ ScriptWrappable* impl,
+ const WrapperTypeInfo* wrapper_type_info,
+ v8::Local<v8::Object> wrapper) {
+ RUNTIME_CALL_TIMER_SCOPE(
+ isolate, RuntimeCallStats::CounterId::kAssociateObjectWithWrapper);
+ if (DOMDataStore::SetWrapper(isolate, impl, wrapper_type_info, wrapper)) {
+ WrapperTypeInfo::WrapperCreated();
+ SetNativeInfo(isolate, wrapper, wrapper_type_info, impl);
+ DCHECK(HasInternalFieldsSet(wrapper));
+ }
+ SECURITY_CHECK(ToScriptWrappable(wrapper) == impl);
+ return wrapper;
+}
+
+class V8WrapperInstantiationScope {
+ STACK_ALLOCATED();
+
+ public:
+ V8WrapperInstantiationScope(v8::Local<v8::Object> creation_context,
+ v8::Isolate* isolate,
+ const WrapperTypeInfo* type)
+ : did_enter_context_(false),
+ context_(isolate->GetCurrentContext()),
+ try_catch_(isolate),
+ type_(type),
+ access_check_failed_(false) {
+ // creationContext should not be empty. Because if we have an
+ // empty creationContext, we will end up creating
+ // a new object in the context currently entered. This is wrong.
+ CHECK(!creation_context.IsEmpty());
+ v8::Local<v8::Context> context_for_wrapper =
+ creation_context->CreationContext();
+
+ // For performance, we enter the context only if the currently running
+ // context is different from the context that we are about to enter.
+ if (context_for_wrapper == context_)
+ return;
+
+ context_ = context_for_wrapper;
+
+ if (!WrapperCreationSecurityCheck::VerifyContextAccess(context_, type_)) {
+ DCHECK(try_catch_.HasCaught());
+ try_catch_.ReThrow();
+ access_check_failed_ = true;
+ return;
+ }
+
+ did_enter_context_ = true;
+ context_->Enter();
+ }
+
+ ~V8WrapperInstantiationScope() {
+ if (!did_enter_context_) {
+ try_catch_.ReThrow();
+ return;
+ }
+ context_->Exit();
+
+ if (access_check_failed_ || !try_catch_.HasCaught())
+ return;
+
+ // Any exception caught here is a cross context exception and it may not be
+ // safe to directly rethrow the exception in the current context (without
+ // converting it). rethrowCrossContextException converts the exception in
+ // such a scenario.
+ v8::Local<v8::Value> caught_exception = try_catch_.Exception();
+ try_catch_.Reset();
+ WrapperCreationSecurityCheck::RethrowCrossContextException(
+ context_, type_, caught_exception);
+ try_catch_.ReThrow();
+ }
+
+ v8::Local<v8::Context> GetContext() const { return context_; }
+ bool AccessCheckFailed() const { return access_check_failed_; }
+
+ private:
+ bool did_enter_context_;
+ v8::Local<v8::Context> context_;
+ v8::TryCatch try_catch_;
+ const WrapperTypeInfo* type_;
+ bool access_check_failed_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_DOM_WRAPPER_H_
diff --git a/chromium/third_party/blink/renderer/platform/bindings/v8_global_value_map.h b/chromium/third_party/blink/renderer/platform/bindings/v8_global_value_map.h
new file mode 100644
index 00000000000..df0f8eb117c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/v8_global_value_map.h
@@ -0,0 +1,118 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_GLOBAL_VALUE_MAP_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_GLOBAL_VALUE_MAP_H_
+
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+#include "v8/include/v8-util.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+/**
+ * A Traits class for v8::GlobalValueMap that uses wtf/HashMap as a
+ * backing store.
+ *
+ * The parameter is_weak will determine whether the references are 'weak'.
+ * If so, entries will be removed from the map as the weak references are
+ * collected.
+ */
+template <typename KeyType,
+ typename ValueType,
+ v8::PersistentContainerCallbackType type>
+class V8GlobalValueMapTraits {
+ STATIC_ONLY(V8GlobalValueMapTraits);
+
+ public:
+ // Map traits:
+ typedef HashMap<KeyType, v8::PersistentContainerValue> Impl;
+ typedef typename Impl::iterator Iterator;
+ static size_t Size(const Impl* impl) { return impl->size(); }
+ static bool Empty(Impl* impl) { return impl->IsEmpty(); }
+ static void Swap(Impl& impl, Impl& other) { impl.swap(other); }
+ static Iterator Begin(Impl* impl) { return impl->begin(); }
+ static Iterator End(Impl* impl) { return impl->end(); }
+ static v8::PersistentContainerValue Value(Iterator& iter) {
+ return iter->value;
+ }
+ static KeyType Key(Iterator& iter) { return iter->key; }
+ static v8::PersistentContainerValue Set(Impl* impl,
+ KeyType key,
+ v8::PersistentContainerValue value) {
+ v8::PersistentContainerValue old_value = Get(impl, key);
+ impl->Set(key, value);
+ return old_value;
+ }
+ static v8::PersistentContainerValue Get(const Impl* impl, KeyType key) {
+ return impl->at(key);
+ }
+
+ static v8::PersistentContainerValue Remove(Impl* impl, KeyType key) {
+ return impl->Take(key);
+ }
+
+ // Weak traits:
+ static const v8::PersistentContainerCallbackType kCallbackType = type;
+ typedef v8::GlobalValueMap<KeyType,
+ ValueType,
+ V8GlobalValueMapTraits<KeyType, ValueType, type>>
+ MapType;
+
+ typedef void WeakCallbackDataType;
+
+ static WeakCallbackDataType* WeakCallbackParameter(
+ MapType* map,
+ KeyType key,
+ const v8::Local<ValueType>& value) {
+ return nullptr;
+ }
+
+ static void DisposeCallbackData(WeakCallbackDataType* callback_data) {}
+
+ static MapType* MapFromWeakCallbackInfo(
+ const v8::WeakCallbackInfo<WeakCallbackDataType>& data) {
+ return nullptr;
+ }
+
+ static KeyType KeyFromWeakCallbackInfo(
+ const v8::WeakCallbackInfo<WeakCallbackDataType>& data) {
+ return KeyType();
+ }
+
+ static void OnWeakCallback(
+ const v8::WeakCallbackInfo<WeakCallbackDataType>& data) {}
+
+ // Dispose traits:
+ static void Dispose(v8::Isolate* isolate,
+ v8::Global<ValueType> value,
+ KeyType key) {}
+ static void DisposeWeak(
+ const v8::WeakCallbackInfo<WeakCallbackDataType>& data) {}
+};
+
+/**
+ * A map for safely storing persistent V8 values, based on
+ * v8::GlobalValueMap.
+ */
+template <typename KeyType,
+ typename ValueType,
+ v8::PersistentContainerCallbackType type>
+class V8GlobalValueMap : public v8::GlobalValueMap<
+ KeyType,
+ ValueType,
+ V8GlobalValueMapTraits<KeyType, ValueType, type>> {
+ DISALLOW_NEW();
+
+ public:
+ typedef V8GlobalValueMapTraits<KeyType, ValueType, type> Traits;
+ explicit V8GlobalValueMap(v8::Isolate* isolate)
+ : v8::GlobalValueMap<KeyType, ValueType, Traits>(isolate) {}
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_GLOBAL_VALUE_MAP_H_
diff --git a/chromium/third_party/blink/renderer/platform/bindings/v8_object_constructor.cc b/chromium/third_party/blink/renderer/platform/bindings/v8_object_constructor.cc
new file mode 100644
index 00000000000..9d46e4800b8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/v8_object_constructor.cc
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2012 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/bindings/v8_object_constructor.h"
+
+#include "third_party/blink/renderer/platform/bindings/origin_trial_features.h"
+#include "third_party/blink/renderer/platform/bindings/runtime_call_stats.h"
+#include "third_party/blink/renderer/platform/bindings/v8_binding.h"
+#include "third_party/blink/renderer/platform/bindings/v8_per_context_data.h"
+#include "third_party/blink/renderer/platform/bindings/v8_throw_exception.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+
+namespace blink {
+
+v8::MaybeLocal<v8::Object> V8ObjectConstructor::NewInstance(
+ v8::Isolate* isolate,
+ v8::Local<v8::Function> function,
+ int argc,
+ v8::Local<v8::Value> argv[]) {
+ DCHECK(!function.IsEmpty());
+ TRACE_EVENT0("v8", "v8.newInstance");
+ RUNTIME_CALL_TIMER_SCOPE(isolate, RuntimeCallStats::CounterId::kV8);
+ ConstructorMode constructor_mode(isolate);
+ v8::MicrotasksScope microtasks_scope(
+ isolate, v8::MicrotasksScope::kDoNotRunMicrotasks);
+ v8::MaybeLocal<v8::Object> result =
+ function->NewInstance(isolate->GetCurrentContext(), argc, argv);
+ CHECK(!isolate->IsDead());
+ return result;
+}
+
+void V8ObjectConstructor::IsValidConstructorMode(
+ const v8::FunctionCallbackInfo<v8::Value>& info) {
+ RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT(info.GetIsolate(),
+ "Blink_IsValidConstructorMode");
+ if (ConstructorMode::Current(info.GetIsolate()) ==
+ ConstructorMode::kCreateNewObject) {
+ V8ThrowException::ThrowTypeError(info.GetIsolate(), "Illegal constructor");
+ return;
+ }
+ V8SetReturnValue(info, info.Holder());
+}
+
+v8::Local<v8::Function> V8ObjectConstructor::CreateInterfaceObject(
+ const WrapperTypeInfo* type,
+ v8::Local<v8::Context> context,
+ const DOMWrapperWorld& world,
+ v8::Isolate* isolate,
+ v8::Local<v8::Function> parent_interface,
+ CreationMode creation_mode) {
+ // We shouldn't reach this point for the types that are implemented in v8 such
+ // as typed arrays and hence don't have domTemplateFunction.
+ DCHECK(type->dom_template_function);
+ v8::Local<v8::FunctionTemplate> interface_template =
+ type->domTemplate(isolate, world);
+ // Getting the function might fail if we're running out of stack or memory.
+ v8::Local<v8::Function> interface_object;
+ bool get_interface_object =
+ interface_template->GetFunction(context).ToLocal(&interface_object);
+ CHECK(get_interface_object);
+
+ if (type->parent_class) {
+ DCHECK(!parent_interface.IsEmpty());
+ bool set_parent_interface =
+ interface_object->SetPrototype(context, parent_interface).ToChecked();
+ CHECK(set_parent_interface);
+ }
+
+ v8::Local<v8::Object> prototype_object;
+ if (type->wrapper_type_prototype ==
+ WrapperTypeInfo::kWrapperTypeObjectPrototype) {
+ v8::Local<v8::Value> prototype_value;
+ bool get_prototype_value =
+ interface_object->Get(context, V8AtomicString(isolate, "prototype"))
+ .ToLocal(&prototype_value);
+ CHECK(get_prototype_value);
+ CHECK(prototype_value->IsObject());
+
+ prototype_object = prototype_value.As<v8::Object>();
+ if (prototype_object->InternalFieldCount() ==
+ kV8PrototypeInternalFieldcount) {
+ prototype_object->SetAlignedPointerInInternalField(
+ kV8PrototypeTypeIndex, const_cast<WrapperTypeInfo*>(type));
+ }
+ }
+
+ if (creation_mode == CreationMode::kInstallConditionalFeatures) {
+ type->InstallConditionalFeatures(context, world, v8::Local<v8::Object>(),
+ prototype_object, interface_object,
+ interface_template);
+ InstallOriginTrialFeatures(type, ScriptState::From(context),
+ prototype_object, interface_object);
+ }
+
+ return interface_object;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/bindings/v8_object_constructor.h b/chromium/third_party/blink/renderer/platform/bindings/v8_object_constructor.h
new file mode 100644
index 00000000000..89f99bfb299
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/v8_object_constructor.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_OBJECT_CONSTRUCTOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_OBJECT_CONSTRUCTOR_H_
+
+#include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+class ConstructorMode {
+ STACK_ALLOCATED();
+
+ public:
+ enum Mode { kWrapExistingObject, kCreateNewObject };
+
+ ConstructorMode(v8::Isolate* isolate) : isolate_(isolate) {
+ V8PerIsolateData* data = V8PerIsolateData::From(isolate_);
+ previous_ = data->constructor_mode_;
+ data->constructor_mode_ = kWrapExistingObject;
+ }
+
+ ~ConstructorMode() {
+ V8PerIsolateData* data = V8PerIsolateData::From(isolate_);
+ data->constructor_mode_ = previous_;
+ }
+
+ static bool Current(v8::Isolate* isolate) {
+ return V8PerIsolateData::From(isolate)->constructor_mode_;
+ }
+
+ private:
+ v8::Isolate* isolate_;
+ bool previous_;
+};
+
+class PLATFORM_EXPORT V8ObjectConstructor {
+ STATIC_ONLY(V8ObjectConstructor);
+
+ public:
+ enum class CreationMode {
+ kInstallConditionalFeatures,
+ kDoNotInstallConditionalFeatures,
+ };
+
+ static v8::MaybeLocal<v8::Object> NewInstance(
+ v8::Isolate*,
+ v8::Local<v8::Function>,
+ int argc = 0,
+ v8::Local<v8::Value> argv[] = nullptr);
+
+ static void IsValidConstructorMode(
+ const v8::FunctionCallbackInfo<v8::Value>&);
+
+ // Returns the interface object of the wrapper type in the context. If you
+ // call with CreationMode::kDoNotInstallConditionalFeatures, no conditional
+ // features are installed.
+ static v8::Local<v8::Function> CreateInterfaceObject(
+ const WrapperTypeInfo*,
+ v8::Local<v8::Context>,
+ const DOMWrapperWorld&,
+ v8::Isolate*,
+ v8::Local<v8::Function> parent_interface,
+ CreationMode);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_OBJECT_CONSTRUCTOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/bindings/v8_per_context_data.cc b/chromium/third_party/blink/renderer/platform/bindings/v8_per_context_data.cc
new file mode 100644
index 00000000000..f137b259824
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/v8_per_context_data.cc
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2012 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 "third_party/blink/renderer/platform/bindings/v8_per_context_data.h"
+
+#include <stdlib.h>
+#include <memory>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/platform/bindings/origin_trial_features.h"
+#include "third_party/blink/renderer/platform/bindings/script_state.h"
+#include "third_party/blink/renderer/platform/bindings/v8_binding.h"
+#include "third_party/blink/renderer/platform/bindings/v8_object_constructor.h"
+#include "third_party/blink/renderer/platform/instance_counters.h"
+
+namespace blink {
+
+V8PerContextData::V8PerContextData(v8::Local<v8::Context> context)
+ : isolate_(context->GetIsolate()),
+ wrapper_boilerplates_(isolate_),
+ constructor_map_(isolate_),
+ context_holder_(std::make_unique<gin::ContextHolder>(isolate_)),
+ context_(isolate_, context),
+ activity_logger_(nullptr) {
+ context_holder_->SetContext(context);
+ context_.Get().AnnotateStrongRetainer("blink::V8PerContextData::context_");
+
+ if (IsMainThread()) {
+ InstanceCounters::IncrementCounter(
+ InstanceCounters::kV8PerContextDataCounter);
+ }
+}
+
+V8PerContextData::~V8PerContextData() {
+ if (IsMainThread()) {
+ InstanceCounters::DecrementCounter(
+ InstanceCounters::kV8PerContextDataCounter);
+ }
+}
+
+std::unique_ptr<V8PerContextData> V8PerContextData::Create(
+ v8::Local<v8::Context> context) {
+ return base::WrapUnique(new V8PerContextData(context));
+}
+
+V8PerContextData* V8PerContextData::From(v8::Local<v8::Context> context) {
+ return ScriptState::From(context)->PerContextData();
+}
+
+v8::Local<v8::Object> V8PerContextData::CreateWrapperFromCacheSlowCase(
+ const WrapperTypeInfo* type) {
+ v8::Context::Scope scope(GetContext());
+ v8::Local<v8::Function> interface_object = ConstructorForType(type);
+ CHECK(!interface_object.IsEmpty());
+ v8::Local<v8::Object> instance_template =
+ V8ObjectConstructor::NewInstance(isolate_, interface_object)
+ .ToLocalChecked();
+ wrapper_boilerplates_.Set(type, instance_template);
+ return instance_template->Clone();
+}
+
+v8::Local<v8::Function> V8PerContextData::ConstructorForTypeSlowCase(
+ const WrapperTypeInfo* type) {
+ v8::Local<v8::Context> context = GetContext();
+ v8::Context::Scope scope(context);
+
+ v8::Local<v8::Function> parent_interface_object;
+ if (type->parent_class) {
+ parent_interface_object = ConstructorForType(type->parent_class);
+ }
+
+ const DOMWrapperWorld& world = DOMWrapperWorld::World(context);
+ v8::Local<v8::Function> interface_object =
+ V8ObjectConstructor::CreateInterfaceObject(
+ type, context, world, isolate_, parent_interface_object,
+ V8ObjectConstructor::CreationMode::kInstallConditionalFeatures);
+
+ constructor_map_.Set(type, interface_object);
+ return interface_object;
+}
+
+v8::Local<v8::Object> V8PerContextData::PrototypeForType(
+ const WrapperTypeInfo* type) {
+ v8::Local<v8::Object> constructor = ConstructorForType(type);
+ if (constructor.IsEmpty())
+ return v8::Local<v8::Object>();
+ v8::Local<v8::Value> prototype_value;
+ if (!constructor->Get(GetContext(), V8AtomicString(isolate_, "prototype"))
+ .ToLocal(&prototype_value) ||
+ !prototype_value->IsObject())
+ return v8::Local<v8::Object>();
+ return prototype_value.As<v8::Object>();
+}
+
+bool V8PerContextData::GetExistingConstructorAndPrototypeForType(
+ const WrapperTypeInfo* type,
+ v8::Local<v8::Object>* prototype_object,
+ v8::Local<v8::Function>* interface_object) {
+ *interface_object = constructor_map_.Get(type);
+ if (interface_object->IsEmpty()) {
+ *prototype_object = v8::Local<v8::Object>();
+ return false;
+ }
+ *prototype_object = PrototypeForType(type);
+ DCHECK(!prototype_object->IsEmpty());
+ return true;
+}
+
+void V8PerContextData::AddCustomElementBinding(
+ std::unique_ptr<V0CustomElementBinding> binding) {
+ custom_element_bindings_.push_back(std::move(binding));
+}
+
+void V8PerContextData::AddData(const char* key, Data* data) {
+ data_map_.Set(key, data);
+}
+
+void V8PerContextData::ClearData(const char* key) {
+ data_map_.erase(key);
+}
+
+V8PerContextData::Data* V8PerContextData::GetData(const char* key) {
+ return data_map_.at(key);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/bindings/v8_per_context_data.h b/chromium/third_party/blink/renderer/platform/bindings/v8_per_context_data.h
new file mode 100644
index 00000000000..719ff9841d0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/v8_per_context_data.h
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2012 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_PER_CONTEXT_DATA_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_PER_CONTEXT_DATA_H_
+
+#include <memory>
+
+#include "gin/public/context_holder.h"
+#include "gin/public/gin_embedders.h"
+#include "third_party/blink/renderer/platform/bindings/scoped_persistent.h"
+#include "third_party/blink/renderer/platform/bindings/v0_custom_element_binding.h"
+#include "third_party/blink/renderer/platform/bindings/v8_global_value_map.h"
+#include "third_party/blink/renderer/platform/bindings/wrapper_type_info.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+class V8DOMActivityLogger;
+class V8PerContextData;
+
+enum V8ContextEmbedderDataField {
+ kV8ContextPerContextDataIndex = static_cast<int>(
+ gin::kPerContextDataStartIndex + // NOLINT(readability/enum_casing)
+ gin::kEmbedderBlink), // NOLINT(readability/enum_casing)
+};
+
+// Used to hold data that is associated with a single v8::Context object, and
+// has a 1:1 relationship with v8::Context.
+class PLATFORM_EXPORT V8PerContextData final {
+ USING_FAST_MALLOC(V8PerContextData);
+ WTF_MAKE_NONCOPYABLE(V8PerContextData);
+
+ public:
+ static std::unique_ptr<V8PerContextData> Create(v8::Local<v8::Context>);
+
+ static V8PerContextData* From(v8::Local<v8::Context>);
+
+ ~V8PerContextData();
+
+ v8::Local<v8::Context> GetContext() { return context_.NewLocal(isolate_); }
+
+ // To create JS Wrapper objects, we create a cache of a 'boiler plate'
+ // object, and then simply Clone that object each time we need a new one.
+ // This is faster than going through the full object creation process.
+ v8::Local<v8::Object> CreateWrapperFromCache(const WrapperTypeInfo* type) {
+ v8::Local<v8::Object> boilerplate = wrapper_boilerplates_.Get(type);
+ return !boilerplate.IsEmpty() ? boilerplate->Clone()
+ : CreateWrapperFromCacheSlowCase(type);
+ }
+
+ v8::Local<v8::Function> ConstructorForType(const WrapperTypeInfo* type) {
+ v8::Local<v8::Function> interface_object = constructor_map_.Get(type);
+ return (!interface_object.IsEmpty()) ? interface_object
+ : ConstructorForTypeSlowCase(type);
+ }
+
+ v8::Local<v8::Object> PrototypeForType(const WrapperTypeInfo*);
+
+ // Gets the constructor and prototype for a type, if they have already been
+ // created. Returns true if they exist, and sets the existing values in
+ // |prototypeObject| and |interfaceObject|. Otherwise, returns false, and the
+ // values are set to empty objects (non-null).
+ bool GetExistingConstructorAndPrototypeForType(
+ const WrapperTypeInfo*,
+ v8::Local<v8::Object>* prototype_object,
+ v8::Local<v8::Function>* interface_object);
+
+ void AddCustomElementBinding(std::unique_ptr<V0CustomElementBinding>);
+
+ // Gets a Private to store custom element definition IDs on a
+ // constructor that has been registered as a custom element in this
+ // context. This private has to be per-context because the same
+ // constructor could be simultaneously registered as a custom
+ // element in many contexts and they each need to give it a unique
+ // identifier.
+ v8::Local<v8::Private> GetPrivateCustomElementDefinitionId() {
+ if (UNLIKELY(private_custom_element_definition_id_.IsEmpty())) {
+ private_custom_element_definition_id_.Set(isolate_,
+ v8::Private::New(isolate_));
+ }
+ return private_custom_element_definition_id_.NewLocal(isolate_);
+ }
+
+ V8DOMActivityLogger* ActivityLogger() const { return activity_logger_; }
+ void SetActivityLogger(V8DOMActivityLogger* activity_logger) {
+ activity_logger_ = activity_logger;
+ }
+
+ // Garbage collected classes that use V8PerContextData to hold an instance
+ // should subclass Data, and use addData / clearData / getData to manage the
+ // instance.
+ class PLATFORM_EXPORT Data : public GarbageCollectedMixin {};
+
+ void AddData(const char* key, Data*);
+ void ClearData(const char* key);
+ Data* GetData(const char* key);
+
+ private:
+ V8PerContextData(v8::Local<v8::Context>);
+
+ v8::Local<v8::Object> CreateWrapperFromCacheSlowCase(const WrapperTypeInfo*);
+ v8::Local<v8::Function> ConstructorForTypeSlowCase(const WrapperTypeInfo*);
+
+ v8::Isolate* isolate_;
+
+ // For each possible type of wrapper, we keep a boilerplate object.
+ // The boilerplate is used to create additional wrappers of the same type.
+ typedef V8GlobalValueMap<const WrapperTypeInfo*, v8::Object, v8::kNotWeak>
+ WrapperBoilerplateMap;
+ WrapperBoilerplateMap wrapper_boilerplates_;
+
+ typedef V8GlobalValueMap<const WrapperTypeInfo*, v8::Function, v8::kNotWeak>
+ ConstructorMap;
+ ConstructorMap constructor_map_;
+
+ std::unique_ptr<gin::ContextHolder> context_holder_;
+
+ ScopedPersistent<v8::Context> context_;
+
+ ScopedPersistent<v8::Private> private_custom_element_definition_id_;
+
+ typedef Vector<std::unique_ptr<V0CustomElementBinding>>
+ V0CustomElementBindingList;
+ V0CustomElementBindingList custom_element_bindings_;
+
+ // This is owned by a static hash map in V8DOMActivityLogger.
+ V8DOMActivityLogger* activity_logger_;
+
+ using DataMap = PersistentHeapHashMap<const char*, Member<Data>>;
+ DataMap data_map_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_PER_CONTEXT_DATA_H_
diff --git a/chromium/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.cc b/chromium/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.cc
new file mode 100644
index 00000000000..a05468a6fd0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.cc
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
+
+#include <memory>
+#include <utility>
+
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/web/blink.h"
+#include "third_party/blink/renderer/platform/bindings/dom_data_store.h"
+#include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h"
+#include "third_party/blink/renderer/platform/bindings/v8_binding.h"
+#include "third_party/blink/renderer/platform/bindings/v8_object_constructor.h"
+#include "third_party/blink/renderer/platform/bindings/v8_private_property.h"
+#include "third_party/blink/renderer/platform/bindings/v8_value_cache.h"
+#include "third_party/blink/renderer/platform/wtf/leak_annotations.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+// Wrapper function defined in WebKit.h
+v8::Isolate* MainThreadIsolate() {
+ return V8PerIsolateData::MainThreadIsolate();
+}
+
+static V8PerIsolateData* g_main_thread_per_isolate_data = nullptr;
+
+static void BeforeCallEnteredCallback(v8::Isolate* isolate) {
+ CHECK(!ScriptForbiddenScope::IsScriptForbidden());
+}
+
+static void MicrotasksCompletedCallback(v8::Isolate* isolate) {
+ V8PerIsolateData::From(isolate)->RunEndOfScopeTasks();
+}
+
+V8PerIsolateData::V8PerIsolateData(
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+ V8ContextSnapshotMode v8_context_snapshot_mode)
+ : v8_context_snapshot_mode_(v8_context_snapshot_mode),
+ isolate_holder_(task_runner,
+ gin::IsolateHolder::kSingleThread,
+ IsMainThread() ? gin::IsolateHolder::kDisallowAtomicsWait
+ : gin::IsolateHolder::kAllowAtomicsWait),
+ interface_template_map_for_v8_context_snapshot_(GetIsolate()),
+ string_cache_(std::make_unique<StringCache>(GetIsolate())),
+ private_property_(V8PrivateProperty::Create()),
+ constructor_mode_(ConstructorMode::kCreateNewObject),
+ use_counter_disabled_(false),
+ is_handling_recursion_level_error_(false),
+ is_reporting_exception_(false),
+ runtime_call_stats_(base::DefaultTickClock::GetInstance()) {
+ // FIXME: Remove once all v8::Isolate::GetCurrent() calls are gone.
+ GetIsolate()->Enter();
+ GetIsolate()->AddBeforeCallEnteredCallback(&BeforeCallEnteredCallback);
+ GetIsolate()->AddMicrotasksCompletedCallback(&MicrotasksCompletedCallback);
+ if (IsMainThread())
+ g_main_thread_per_isolate_data = this;
+}
+
+// This constructor is used for creating a V8 context snapshot. It must run on
+// the main thread.
+V8PerIsolateData::V8PerIsolateData()
+ : v8_context_snapshot_mode_(V8ContextSnapshotMode::kTakeSnapshot),
+ isolate_holder_(Platform::Current()->MainThread()->GetTaskRunner(),
+ gin::IsolateHolder::kSingleThread,
+ gin::IsolateHolder::kAllowAtomicsWait,
+ gin::IsolateHolder::IsolateCreationMode::kCreateSnapshot),
+ interface_template_map_for_v8_context_snapshot_(GetIsolate()),
+ string_cache_(std::make_unique<StringCache>(GetIsolate())),
+ private_property_(V8PrivateProperty::Create()),
+ constructor_mode_(ConstructorMode::kCreateNewObject),
+ use_counter_disabled_(false),
+ is_handling_recursion_level_error_(false),
+ is_reporting_exception_(false),
+ runtime_call_stats_(base::DefaultTickClock::GetInstance()) {
+ CHECK(IsMainThread());
+
+ // SnapshotCreator enters the isolate, so we don't call Isolate::Enter() here.
+ g_main_thread_per_isolate_data = this;
+}
+
+V8PerIsolateData::~V8PerIsolateData() = default;
+
+v8::Isolate* V8PerIsolateData::MainThreadIsolate() {
+ DCHECK(g_main_thread_per_isolate_data);
+ return g_main_thread_per_isolate_data->GetIsolate();
+}
+
+v8::Isolate* V8PerIsolateData::Initialize(
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+ V8ContextSnapshotMode context_mode) {
+ V8PerIsolateData* data = nullptr;
+ if (context_mode == V8ContextSnapshotMode::kTakeSnapshot) {
+ data = new V8PerIsolateData();
+ } else {
+ data = new V8PerIsolateData(task_runner, context_mode);
+ }
+ DCHECK(data);
+
+ v8::Isolate* isolate = data->GetIsolate();
+ isolate->SetData(gin::kEmbedderBlink, data);
+ return isolate;
+}
+
+void V8PerIsolateData::EnableIdleTasks(
+ v8::Isolate* isolate,
+ std::unique_ptr<gin::V8IdleTaskRunner> task_runner) {
+ From(isolate)->isolate_holder_.EnableIdleTasks(std::move(task_runner));
+}
+
+// willBeDestroyed() clear things that should be cleared before
+// ThreadState::detach() gets called.
+void V8PerIsolateData::WillBeDestroyed(v8::Isolate* isolate) {
+ V8PerIsolateData* data = From(isolate);
+
+ data->thread_debugger_.reset();
+ // Clear any data that may have handles into the heap,
+ // prior to calling ThreadState::detach().
+ data->ClearEndOfScopeTasks();
+
+ data->active_script_wrappables_.Clear();
+}
+
+// destroy() clear things that should be cleared after ThreadState::detach()
+// gets called but before the Isolate exits.
+void V8PerIsolateData::Destroy(v8::Isolate* isolate) {
+ isolate->RemoveBeforeCallEnteredCallback(&BeforeCallEnteredCallback);
+ isolate->RemoveMicrotasksCompletedCallback(&MicrotasksCompletedCallback);
+ V8PerIsolateData* data = From(isolate);
+
+ // Clear everything before exiting the Isolate.
+ if (data->script_regexp_script_state_)
+ data->script_regexp_script_state_->DisposePerContextData();
+ data->private_property_.reset();
+ data->string_cache_->Dispose();
+ data->string_cache_.reset();
+ data->interface_template_map_for_non_main_world_.clear();
+ data->interface_template_map_for_main_world_.clear();
+ data->operation_template_map_for_non_main_world_.clear();
+ data->operation_template_map_for_main_world_.clear();
+ if (IsMainThread())
+ g_main_thread_per_isolate_data = nullptr;
+
+ // FIXME: Remove once all v8::Isolate::GetCurrent() calls are gone.
+ isolate->Exit();
+ delete data;
+}
+
+V8PerIsolateData::V8FunctionTemplateMap&
+V8PerIsolateData::SelectInterfaceTemplateMap(const DOMWrapperWorld& world) {
+ return world.IsMainWorld() ? interface_template_map_for_main_world_
+ : interface_template_map_for_non_main_world_;
+}
+
+V8PerIsolateData::V8FunctionTemplateMap&
+V8PerIsolateData::SelectOperationTemplateMap(const DOMWrapperWorld& world) {
+ return world.IsMainWorld() ? operation_template_map_for_main_world_
+ : operation_template_map_for_non_main_world_;
+}
+
+v8::Local<v8::FunctionTemplate> V8PerIsolateData::FindOrCreateOperationTemplate(
+ const DOMWrapperWorld& world,
+ const void* key,
+ v8::FunctionCallback callback,
+ v8::Local<v8::Value> data,
+ v8::Local<v8::Signature> signature,
+ int length) {
+ auto& map = SelectOperationTemplateMap(world);
+ auto result = map.find(key);
+ if (result != map.end())
+ return result->value.Get(GetIsolate());
+
+ v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(
+ GetIsolate(), callback, data, signature, length);
+ templ->RemovePrototype();
+ map.insert(key, v8::Eternal<v8::FunctionTemplate>(GetIsolate(), templ));
+ return templ;
+}
+
+v8::Local<v8::FunctionTemplate> V8PerIsolateData::FindInterfaceTemplate(
+ const DOMWrapperWorld& world,
+ const void* key) {
+ if (GetV8ContextSnapshotMode() == V8ContextSnapshotMode::kTakeSnapshot) {
+ const WrapperTypeInfo* type = reinterpret_cast<const WrapperTypeInfo*>(key);
+ return interface_template_map_for_v8_context_snapshot_.Get(type);
+ }
+
+ auto& map = SelectInterfaceTemplateMap(world);
+ auto result = map.find(key);
+ if (result != map.end())
+ return result->value.Get(GetIsolate());
+ return v8::Local<v8::FunctionTemplate>();
+}
+
+void V8PerIsolateData::SetInterfaceTemplate(
+ const DOMWrapperWorld& world,
+ const void* key,
+ v8::Local<v8::FunctionTemplate> value) {
+ if (GetV8ContextSnapshotMode() == V8ContextSnapshotMode::kTakeSnapshot) {
+ auto& map = interface_template_map_for_v8_context_snapshot_;
+ const WrapperTypeInfo* type = reinterpret_cast<const WrapperTypeInfo*>(key);
+ map.Set(type, value);
+ } else {
+ auto& map = SelectInterfaceTemplateMap(world);
+ map.insert(key, v8::Eternal<v8::FunctionTemplate>(GetIsolate(), value));
+ }
+}
+
+void V8PerIsolateData::ClearPersistentsForV8ContextSnapshot() {
+ interface_template_map_for_v8_context_snapshot_.Clear();
+ private_property_.reset();
+}
+
+const v8::Eternal<v8::Name>* V8PerIsolateData::FindOrCreateEternalNameCache(
+ const void* lookup_key,
+ const char* const names[],
+ size_t count) {
+ auto it = eternal_name_cache_.find(lookup_key);
+ const Vector<v8::Eternal<v8::Name>>* vector = nullptr;
+ if (UNLIKELY(it == eternal_name_cache_.end())) {
+ v8::Isolate* isolate = this->GetIsolate();
+ Vector<v8::Eternal<v8::Name>> new_vector(count);
+ std::transform(
+ names, names + count, new_vector.begin(), [isolate](const char* name) {
+ return v8::Eternal<v8::Name>(isolate, V8AtomicString(isolate, name));
+ });
+ vector = &eternal_name_cache_.Set(lookup_key, std::move(new_vector))
+ .stored_value->value;
+ } else {
+ vector = &it->value;
+ }
+ DCHECK_EQ(vector->size(), count);
+ return vector->data();
+}
+
+v8::Local<v8::Context> V8PerIsolateData::EnsureScriptRegexpContext() {
+ if (!script_regexp_script_state_) {
+ LEAK_SANITIZER_DISABLED_SCOPE;
+ v8::Local<v8::Context> context(v8::Context::New(GetIsolate()));
+ script_regexp_script_state_ = ScriptState::Create(
+ context, DOMWrapperWorld::Create(GetIsolate(),
+ DOMWrapperWorld::WorldType::kRegExp));
+ }
+ return script_regexp_script_state_->GetContext();
+}
+
+void V8PerIsolateData::ClearScriptRegexpContext() {
+ if (script_regexp_script_state_)
+ script_regexp_script_state_->DisposePerContextData();
+ script_regexp_script_state_ = nullptr;
+}
+
+bool V8PerIsolateData::HasInstance(
+ const WrapperTypeInfo* untrusted_wrapper_type_info,
+ v8::Local<v8::Value> value) {
+ RUNTIME_CALL_TIMER_SCOPE(GetIsolate(),
+ RuntimeCallStats::CounterId::kHasInstance);
+ return HasInstance(untrusted_wrapper_type_info, value,
+ interface_template_map_for_main_world_) ||
+ HasInstance(untrusted_wrapper_type_info, value,
+ interface_template_map_for_non_main_world_);
+}
+
+bool V8PerIsolateData::HasInstance(
+ const WrapperTypeInfo* untrusted_wrapper_type_info,
+ v8::Local<v8::Value> value,
+ V8FunctionTemplateMap& map) {
+ auto result = map.find(untrusted_wrapper_type_info);
+ if (result == map.end())
+ return false;
+ v8::Local<v8::FunctionTemplate> templ = result->value.Get(GetIsolate());
+ return templ->HasInstance(value);
+}
+
+v8::Local<v8::Object> V8PerIsolateData::FindInstanceInPrototypeChain(
+ const WrapperTypeInfo* info,
+ v8::Local<v8::Value> value) {
+ v8::Local<v8::Object> wrapper = FindInstanceInPrototypeChain(
+ info, value, interface_template_map_for_main_world_);
+ if (!wrapper.IsEmpty())
+ return wrapper;
+ return FindInstanceInPrototypeChain(
+ info, value, interface_template_map_for_non_main_world_);
+}
+
+v8::Local<v8::Object> V8PerIsolateData::FindInstanceInPrototypeChain(
+ const WrapperTypeInfo* info,
+ v8::Local<v8::Value> value,
+ V8FunctionTemplateMap& map) {
+ if (value.IsEmpty() || !value->IsObject())
+ return v8::Local<v8::Object>();
+ auto result = map.find(info);
+ if (result == map.end())
+ return v8::Local<v8::Object>();
+ v8::Local<v8::FunctionTemplate> templ = result->value.Get(GetIsolate());
+ return v8::Local<v8::Object>::Cast(value)->FindInstanceInPrototypeChain(
+ templ);
+}
+
+void V8PerIsolateData::AddEndOfScopeTask(base::OnceClosure task) {
+ end_of_scope_tasks_.push_back(std::move(task));
+}
+
+void V8PerIsolateData::RunEndOfScopeTasks() {
+ Vector<base::OnceClosure> tasks;
+ tasks.swap(end_of_scope_tasks_);
+ for (auto& task : tasks)
+ std::move(task).Run();
+ DCHECK(end_of_scope_tasks_.IsEmpty());
+}
+
+void V8PerIsolateData::ClearEndOfScopeTasks() {
+ end_of_scope_tasks_.clear();
+}
+
+void V8PerIsolateData::SetThreadDebugger(
+ std::unique_ptr<V8PerIsolateData::Data> thread_debugger) {
+ DCHECK(!thread_debugger_);
+ thread_debugger_ = std::move(thread_debugger);
+}
+
+V8PerIsolateData::Data* V8PerIsolateData::ThreadDebugger() {
+ return thread_debugger_.get();
+}
+
+void V8PerIsolateData::AddActiveScriptWrappable(
+ ActiveScriptWrappableBase* wrappable) {
+ if (!active_script_wrappables_)
+ active_script_wrappables_ = new ActiveScriptWrappableSet();
+
+ active_script_wrappables_->insert(wrappable);
+}
+
+void V8PerIsolateData::TemporaryScriptWrappableVisitorScope::
+ SwapWithV8PerIsolateDataVisitor(
+ std::unique_ptr<ScriptWrappableMarkingVisitor>& visitor) {
+ ScriptWrappableMarkingVisitor* current = CurrentVisitor();
+ if (current)
+ ScriptWrappableMarkingVisitor::PerformCleanup(isolate_);
+
+ V8PerIsolateData::From(isolate_)->script_wrappable_visitor_.swap(
+ saved_visitor_);
+ isolate_->SetEmbedderHeapTracer(CurrentVisitor());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h b/chromium/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h
new file mode 100644
index 00000000000..c150c179cee
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_PER_ISOLATE_DATA_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_PER_ISOLATE_DATA_H_
+
+#include <memory>
+
+#include "base/time/default_tick_clock.h"
+#include "gin/public/isolate_holder.h"
+#include "gin/public/v8_idle_task_runner.h"
+#include "third_party/blink/renderer/platform/bindings/runtime_call_stats.h"
+#include "third_party/blink/renderer/platform/bindings/scoped_persistent.h"
+#include "third_party/blink/renderer/platform/bindings/script_state.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable_marking_visitor.h"
+#include "third_party/blink/renderer/platform/bindings/wrapper_type_info.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+class ActiveScriptWrappableBase;
+class DOMDataStore;
+class StringCache;
+class V8PrivateProperty;
+struct WrapperTypeInfo;
+
+typedef WTF::Vector<DOMDataStore*> DOMDataStoreList;
+
+// Used to hold data that is associated with a single v8::Isolate object, and
+// has a 1:1 relationship with v8::Isolate.
+class PLATFORM_EXPORT V8PerIsolateData {
+ USING_FAST_MALLOC(V8PerIsolateData);
+ WTF_MAKE_NONCOPYABLE(V8PerIsolateData);
+
+ public:
+ enum class V8ContextSnapshotMode {
+ kTakeSnapshot,
+ kDontUseSnapshot,
+ kUseSnapshot,
+ };
+
+ // Disables the UseCounter.
+ // UseCounter depends on the current context, but it's not available during
+ // the initialization of v8::Context and the global object. So we need to
+ // disable the UseCounter while the initialization of the context and global
+ // object.
+ // TODO(yukishiino): Come up with an idea to remove this hack.
+ class UseCounterDisabledScope {
+ STACK_ALLOCATED();
+
+ public:
+ explicit UseCounterDisabledScope(V8PerIsolateData* per_isolate_data)
+ : per_isolate_data_(per_isolate_data),
+ original_use_counter_disabled_(
+ per_isolate_data_->use_counter_disabled_) {
+ per_isolate_data_->use_counter_disabled_ = true;
+ }
+ ~UseCounterDisabledScope() {
+ per_isolate_data_->use_counter_disabled_ = original_use_counter_disabled_;
+ }
+
+ private:
+ V8PerIsolateData* per_isolate_data_;
+ const bool original_use_counter_disabled_;
+ };
+
+ // Use this class to abstract away types of members that are pointers to core/
+ // objects, which are simply owned and released by V8PerIsolateData (see
+ // m_threadDebugger for an example).
+ class PLATFORM_EXPORT Data {
+ public:
+ virtual ~Data() = default;
+ };
+
+ static v8::Isolate* Initialize(scoped_refptr<base::SingleThreadTaskRunner>,
+ V8ContextSnapshotMode);
+
+ static V8PerIsolateData* From(v8::Isolate* isolate) {
+ DCHECK(isolate);
+ DCHECK(isolate->GetData(gin::kEmbedderBlink));
+ return static_cast<V8PerIsolateData*>(
+ isolate->GetData(gin::kEmbedderBlink));
+ }
+
+ static void WillBeDestroyed(v8::Isolate*);
+ static void Destroy(v8::Isolate*);
+ static v8::Isolate* MainThreadIsolate();
+
+ static void EnableIdleTasks(v8::Isolate*,
+ std::unique_ptr<gin::V8IdleTaskRunner>);
+
+ v8::Isolate* GetIsolate() { return isolate_holder_.isolate(); }
+
+ StringCache* GetStringCache() { return string_cache_.get(); }
+
+ RuntimeCallStats* GetRuntimeCallStats() { return &runtime_call_stats_; }
+
+ bool IsHandlingRecursionLevelError() const {
+ return is_handling_recursion_level_error_;
+ }
+ void SetIsHandlingRecursionLevelError(bool value) {
+ is_handling_recursion_level_error_ = value;
+ }
+
+ bool IsReportingException() const { return is_reporting_exception_; }
+ void SetReportingException(bool value) { is_reporting_exception_ = value; }
+
+ bool IsUseCounterDisabled() const { return use_counter_disabled_; }
+
+ V8PrivateProperty* PrivateProperty() { return private_property_.get(); }
+
+ // Accessors to the cache of interface templates.
+ v8::Local<v8::FunctionTemplate> FindInterfaceTemplate(const DOMWrapperWorld&,
+ const void* key);
+ void SetInterfaceTemplate(const DOMWrapperWorld&,
+ const void* key,
+ v8::Local<v8::FunctionTemplate>);
+
+ // When v8::SnapshotCreator::CreateBlob() is called, we must not have
+ // persistent handles in Blink. This method clears them.
+ void ClearPersistentsForV8ContextSnapshot();
+
+ v8::SnapshotCreator* GetSnapshotCreator() const {
+ return isolate_holder_.snapshot_creator();
+ }
+ V8ContextSnapshotMode GetV8ContextSnapshotMode() const {
+ return v8_context_snapshot_mode_;
+ }
+
+ // Accessor to the cache of cross-origin accessible operation's templates.
+ // Created templates get automatically cached.
+ v8::Local<v8::FunctionTemplate> FindOrCreateOperationTemplate(
+ const DOMWrapperWorld&,
+ const void* key,
+ v8::FunctionCallback,
+ v8::Local<v8::Value> data,
+ v8::Local<v8::Signature>,
+ int length);
+
+ // Obtains a pointer to an array of names, given a lookup key. If it does not
+ // yet exist, it is created from the given array of strings. Once created,
+ // these live for as long as the isolate, so this is appropriate only for a
+ // compile-time list of related names, such as IDL dictionary keys.
+ const v8::Eternal<v8::Name>* FindOrCreateEternalNameCache(
+ const void* lookup_key,
+ const char* const names[],
+ size_t count);
+
+ bool HasInstance(const WrapperTypeInfo* untrusted, v8::Local<v8::Value>);
+ v8::Local<v8::Object> FindInstanceInPrototypeChain(const WrapperTypeInfo*,
+ v8::Local<v8::Value>);
+
+ v8::Local<v8::Context> EnsureScriptRegexpContext();
+ void ClearScriptRegexpContext();
+
+ // EndOfScopeTasks are run when control is returning
+ // to C++ from script, after executing a script task (e.g. callback,
+ // event) or microtasks (e.g. promise). This is explicitly needed for
+ // Indexed DB transactions per spec, but should in general be avoided.
+ void AddEndOfScopeTask(base::OnceClosure);
+ void RunEndOfScopeTasks();
+ void ClearEndOfScopeTasks();
+
+ void SetThreadDebugger(std::unique_ptr<Data>);
+ Data* ThreadDebugger();
+
+ using ActiveScriptWrappableSet =
+ HeapHashSet<WeakMember<ActiveScriptWrappableBase>>;
+ void AddActiveScriptWrappable(ActiveScriptWrappableBase*);
+ const ActiveScriptWrappableSet* ActiveScriptWrappables() const {
+ return active_script_wrappables_.Get();
+ }
+
+ class PLATFORM_EXPORT TemporaryScriptWrappableVisitorScope {
+ WTF_MAKE_NONCOPYABLE(TemporaryScriptWrappableVisitorScope);
+ STACK_ALLOCATED();
+
+ public:
+ TemporaryScriptWrappableVisitorScope(
+ v8::Isolate* isolate,
+ std::unique_ptr<ScriptWrappableMarkingVisitor> visitor)
+ : isolate_(isolate), saved_visitor_(std::move(visitor)) {
+ SwapWithV8PerIsolateDataVisitor(saved_visitor_);
+ }
+ ~TemporaryScriptWrappableVisitorScope() {
+ SwapWithV8PerIsolateDataVisitor(saved_visitor_);
+ }
+
+ inline ScriptWrappableMarkingVisitor* CurrentVisitor() {
+ return V8PerIsolateData::From(isolate_)
+ ->GetScriptWrappableMarkingVisitor();
+ }
+
+ private:
+ void SwapWithV8PerIsolateDataVisitor(
+ std::unique_ptr<ScriptWrappableMarkingVisitor>&);
+
+ v8::Isolate* isolate_;
+ std::unique_ptr<ScriptWrappableMarkingVisitor> saved_visitor_;
+ };
+
+ void SetScriptWrappableMarkingVisitor(
+ std::unique_ptr<ScriptWrappableMarkingVisitor> visitor) {
+ script_wrappable_visitor_ = std::move(visitor);
+ }
+ ScriptWrappableMarkingVisitor* GetScriptWrappableMarkingVisitor() {
+ return script_wrappable_visitor_.get();
+ }
+
+ private:
+ V8PerIsolateData(scoped_refptr<base::SingleThreadTaskRunner>,
+ V8ContextSnapshotMode);
+ V8PerIsolateData();
+ ~V8PerIsolateData();
+
+ // A really simple hash function, which makes lookups faster. The set of
+ // possible keys for this is relatively small and fixed at compile time, so
+ // collisions are less of a worry than they would otherwise be.
+ struct SimplePtrHash : WTF::PtrHash<const void> {
+ static unsigned GetHash(const void* key) {
+ uintptr_t k = reinterpret_cast<uintptr_t>(key);
+ return static_cast<unsigned>(k ^ (k >> 8));
+ }
+ };
+ using V8FunctionTemplateMap =
+ HashMap<const void*, v8::Eternal<v8::FunctionTemplate>, SimplePtrHash>;
+ V8FunctionTemplateMap& SelectInterfaceTemplateMap(const DOMWrapperWorld&);
+ V8FunctionTemplateMap& SelectOperationTemplateMap(const DOMWrapperWorld&);
+ bool HasInstance(const WrapperTypeInfo* untrusted,
+ v8::Local<v8::Value>,
+ V8FunctionTemplateMap&);
+ v8::Local<v8::Object> FindInstanceInPrototypeChain(const WrapperTypeInfo*,
+ v8::Local<v8::Value>,
+ V8FunctionTemplateMap&);
+
+ V8ContextSnapshotMode v8_context_snapshot_mode_;
+ // This isolate_holder_ must be initialized before initializing some other
+ // members below.
+ gin::IsolateHolder isolate_holder_;
+
+ // interface_template_map_for_{,non_}main_world holds function templates for
+ // the inerface objects.
+ V8FunctionTemplateMap interface_template_map_for_main_world_;
+ V8FunctionTemplateMap interface_template_map_for_non_main_world_;
+
+ // m_operationTemplateMapFor{,Non}MainWorld holds function templates for
+ // the cross-origin accessible DOM operations.
+ V8FunctionTemplateMap operation_template_map_for_main_world_;
+ V8FunctionTemplateMap operation_template_map_for_non_main_world_;
+
+ // Contains lists of eternal names, such as dictionary keys.
+ HashMap<const void*, Vector<v8::Eternal<v8::Name>>> eternal_name_cache_;
+
+ // When taking a V8 context snapshot, we can't keep V8 objects with eternal
+ // handles. So we use a special interface map that doesn't use eternal handles
+ // instead of the default V8FunctionTemplateMap.
+ V8GlobalValueMap<const WrapperTypeInfo*, v8::FunctionTemplate, v8::kNotWeak>
+ interface_template_map_for_v8_context_snapshot_;
+
+ std::unique_ptr<StringCache> string_cache_;
+ std::unique_ptr<V8PrivateProperty> private_property_;
+ scoped_refptr<ScriptState> script_regexp_script_state_;
+
+ bool constructor_mode_;
+ friend class ConstructorMode;
+
+ bool use_counter_disabled_;
+ friend class UseCounterDisabledScope;
+
+ bool is_handling_recursion_level_error_;
+ bool is_reporting_exception_;
+
+ Vector<base::OnceClosure> end_of_scope_tasks_;
+ std::unique_ptr<Data> thread_debugger_;
+
+ Persistent<ActiveScriptWrappableSet> active_script_wrappables_;
+ std::unique_ptr<ScriptWrappableMarkingVisitor> script_wrappable_visitor_;
+
+ RuntimeCallStats runtime_call_stats_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_PER_ISOLATE_DATA_H_
diff --git a/chromium/third_party/blink/renderer/platform/bindings/v8_private_property.cc b/chromium/third_party/blink/renderer/platform/bindings/v8_private_property.cc
new file mode 100644
index 00000000000..2fc7ea7355a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/v8_private_property.cc
@@ -0,0 +1,33 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/bindings/v8_private_property.h"
+
+#include "third_party/blink/renderer/platform/bindings/script_state.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/bindings/v8_binding.h"
+#include "third_party/blink/renderer/platform/bindings/v8_binding_macros.h"
+
+namespace blink {
+
+v8::MaybeLocal<v8::Value> V8PrivateProperty::Symbol::GetFromMainWorld(
+ ScriptWrappable* script_wrappable) {
+ v8::Local<v8::Object> wrapper = script_wrappable->MainWorldWrapper(isolate_);
+ return wrapper.IsEmpty() ? v8::MaybeLocal<v8::Value>()
+ : GetOrUndefined(wrapper);
+}
+
+v8::Local<v8::Private> V8PrivateProperty::CreateV8Private(v8::Isolate* isolate,
+ const char* symbol) {
+ return v8::Private::New(isolate, V8String(isolate, symbol));
+}
+
+v8::Local<v8::Private> V8PrivateProperty::CreateCachedV8Private(
+ v8::Isolate* isolate,
+ const char* symbol) {
+ // Use ForApi() to get the same Private symbol which is not cached in Chrome.
+ return v8::Private::ForApi(isolate, V8String(isolate, symbol));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/bindings/v8_private_property.h b/chromium/third_party/blink/renderer/platform/bindings/v8_private_property.h
new file mode 100644
index 00000000000..128bd766433
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/v8_private_property.h
@@ -0,0 +1,232 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_PRIVATE_PROPERTY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_PRIVATE_PROPERTY_H_
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/platform/bindings/script_promise_properties.h"
+#include "third_party/blink/renderer/platform/bindings/v8_binding_macros.h"
+#include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+class ScriptWrappable;
+
+// TODO(peria): Remove properties just to keep V8 objects alive.
+// e.g. InternalBody.Buffer, InternalBody.Stream, IDBCursor.Request,
+// FetchEvent.Request.
+// Apply |X| for each pair of (InterfaceName, PrivateKeyName).
+#define V8_PRIVATE_PROPERTY_FOR_EACH(X) \
+ X(CustomElement, Document) \
+ X(CustomElement, IsInterfacePrototypeObject) \
+ X(CustomElement, NamespaceURI) \
+ X(CustomElement, TagName) \
+ X(CustomElement, Type) \
+ X(CustomElementLifecycle, AttachedCallback) \
+ X(CustomElementLifecycle, AttributeChangedCallback) \
+ X(CustomElementLifecycle, CreatedCallback) \
+ X(CustomElementLifecycle, DetachedCallback) \
+ X(DOMException, Error) \
+ X(ErrorEvent, Error) \
+ X(FetchEvent, Request) \
+ X(Global, Event) \
+ X(IDBCursor, Request) \
+ X(InternalBody, Buffer) \
+ X(InternalBody, Stream) \
+ X(IntersectionObserver, Callback) \
+ X(MessageChannel, Port1) \
+ X(MessageChannel, Port2) \
+ X(MessageEvent, CachedData) \
+ X(MutationObserver, Callback) \
+ X(NamedConstructor, Initialized) \
+ X(PopStateEvent, State) \
+ X(SameObject, DetectedBarcodeCornerPoints) \
+ X(SameObject, DetectedFaceLandmarks) \
+ X(SameObject, NotificationActions) \
+ X(SameObject, NotificationData) \
+ X(SameObject, NotificationVibrate) \
+ X(SameObject, PerformanceLongTaskTimingAttribution) \
+ X(SameObject, PushManagerSupportedContentEncodings) \
+ X(V8ErrorHandler, ErrorHandler) \
+ X(V8EventListener, AttributeListener) \
+ X(V8EventListener, Listener) \
+ SCRIPT_PROMISE_PROPERTIES(X, Promise) \
+ SCRIPT_PROMISE_PROPERTIES(X, Resolver)
+
+// The getter's name for a private property.
+#define V8_PRIVATE_PROPERTY_GETTER_NAME(InterfaceName, PrivateKeyName) \
+ Get##InterfaceName##PrivateKeyName
+
+// The member variable's name for a private property.
+#define V8_PRIVATE_PROPERTY_MEMBER_NAME(InterfaceName, PrivateKeyName) \
+ m_symbol##InterfaceName##PrivateKeyName
+
+// The string used to create a private symbol. Must be unique per V8 instance.
+#define V8_PRIVATE_PROPERTY_SYMBOL_STRING(InterfaceName, PrivateKeyName) \
+ #InterfaceName "#" #PrivateKeyName // NOLINT(whitespace/indent)
+
+// Provides access to V8's private properties.
+//
+// Usage 1) Fast path to use a pre-registered symbol.
+// auto private = V8PrivateProperty::getMessageEventCachedData(isolate);
+// v8::Local<v8::Object> object = ...;
+// v8::Local<v8::Value> value;
+// if (!private.GetOrUndefined(object).ToLocal(&value)) return;
+// value = ...;
+// private.set(object, value);
+//
+// Usage 2) Slow path to create a global private symbol.
+// const char symbolName[] = "Interface#PrivateKeyName";
+// auto private = V8PrivateProperty::createSymbol(isolate, symbolName);
+// ...
+class PLATFORM_EXPORT V8PrivateProperty {
+ USING_FAST_MALLOC(V8PrivateProperty);
+ WTF_MAKE_NONCOPYABLE(V8PrivateProperty);
+
+ public:
+ enum CachedAccessorSymbol : unsigned {
+ kNoCachedAccessor = 0,
+ kWindowDocumentCachedAccessor,
+ };
+
+ // Provides fast access to V8's private properties.
+ //
+ // Retrieving/creating a global private symbol from a string is very
+ // expensive compared to get or set a private property. This class
+ // provides a way to cache a private symbol and re-use it.
+ class PLATFORM_EXPORT Symbol {
+ STACK_ALLOCATED();
+
+ public:
+ bool HasValue(v8::Local<v8::Object> object) const {
+ return object->HasPrivate(GetContext(), private_symbol_).ToChecked();
+ }
+
+ // Returns the value of the private property if set, or undefined.
+ WARN_UNUSED_RESULT v8::MaybeLocal<v8::Value> GetOrUndefined(
+ v8::Local<v8::Object> object) const {
+ return object->GetPrivate(GetContext(), private_symbol_);
+ }
+
+ bool Set(v8::Local<v8::Object> object, v8::Local<v8::Value> value) const {
+ return object->SetPrivate(GetContext(), private_symbol_, value)
+ .ToChecked();
+ }
+
+ bool DeleteProperty(v8::Local<v8::Object> object) const {
+ return object->DeletePrivate(GetContext(), private_symbol_).ToChecked();
+ }
+
+ v8::Local<v8::Private> GetPrivate() const { return private_symbol_; }
+
+ private:
+ friend class V8PrivateProperty;
+ // The following classes are exceptionally allowed to call to
+ // getFromMainWorld.
+ friend class V8CustomEvent;
+ friend class V8ExtendableMessageEvent;
+
+ Symbol(v8::Isolate* isolate, v8::Local<v8::Private> private_symbol)
+ : private_symbol_(private_symbol), isolate_(isolate) {}
+
+ // To get/set private property, we should use the current context.
+ v8::Local<v8::Context> GetContext() const {
+ return isolate_->GetCurrentContext();
+ }
+
+ // Only friend classes are allowed to use this API.
+ WARN_UNUSED_RESULT v8::MaybeLocal<v8::Value> GetFromMainWorld(
+ ScriptWrappable*);
+
+ v8::Local<v8::Private> private_symbol_;
+ v8::Isolate* isolate_;
+ };
+
+ static std::unique_ptr<V8PrivateProperty> Create() {
+ return base::WrapUnique(new V8PrivateProperty());
+ }
+
+#define V8_PRIVATE_PROPERTY_DEFINE_GETTER(InterfaceName, KeyName) \
+ static Symbol V8_PRIVATE_PROPERTY_GETTER_NAME(/* // NOLINT */ \
+ InterfaceName, KeyName)( \
+ v8::Isolate * isolate) { \
+ V8PrivateProperty* private_prop = \
+ V8PerIsolateData::From(isolate)->PrivateProperty(); \
+ v8::Eternal<v8::Private>& property_handle = \
+ private_prop->V8_PRIVATE_PROPERTY_MEMBER_NAME(InterfaceName, KeyName); \
+ if (UNLIKELY(property_handle.IsEmpty())) { \
+ property_handle.Set( \
+ isolate, CreateV8Private(isolate, V8_PRIVATE_PROPERTY_SYMBOL_STRING( \
+ InterfaceName, KeyName))); \
+ } \
+ return Symbol(isolate, property_handle.Get(isolate)); \
+ }
+
+ V8_PRIVATE_PROPERTY_FOR_EACH(V8_PRIVATE_PROPERTY_DEFINE_GETTER)
+#undef V8_PRIVATE_PROPERTY_DEFINE_GETTER
+
+ // TODO(peria): Do not use this specialized hack. See a TODO comment
+ // on m_symbolWindowDocumentCachedAccessor.
+ static Symbol GetWindowDocumentCachedAccessor(v8::Isolate* isolate) {
+ V8PrivateProperty* private_prop =
+ V8PerIsolateData::From(isolate)->PrivateProperty();
+ if (UNLIKELY(
+ private_prop->symbol_window_document_cached_accessor_.IsEmpty())) {
+ private_prop->symbol_window_document_cached_accessor_.Set(
+ isolate, CreateCachedV8Private(
+ isolate, V8_PRIVATE_PROPERTY_SYMBOL_STRING(
+ "Window", "DocumentCachedAccessor")));
+ }
+ return Symbol(
+ isolate, private_prop->symbol_window_document_cached_accessor_.NewLocal(
+ isolate));
+ }
+
+ static Symbol GetCachedAccessor(v8::Isolate* isolate,
+ CachedAccessorSymbol symbol_id) {
+ switch (symbol_id) {
+ case kWindowDocumentCachedAccessor:
+ return GetWindowDocumentCachedAccessor(isolate);
+ case kNoCachedAccessor:
+ break;
+ };
+ NOTREACHED();
+ return GetSymbol(isolate, "unexpected cached accessor");
+ }
+
+ static Symbol GetSymbol(v8::Isolate* isolate, const char* symbol) {
+ return Symbol(isolate, CreateCachedV8Private(isolate, symbol));
+ }
+
+ private:
+ V8PrivateProperty() = default;
+
+ static v8::Local<v8::Private> CreateV8Private(v8::Isolate*,
+ const char* symbol);
+ // TODO(peria): Remove this method. We should not use v8::Private::ForApi().
+ static v8::Local<v8::Private> CreateCachedV8Private(v8::Isolate*,
+ const char* symbol);
+
+#define V8_PRIVATE_PROPERTY_DECLARE_MEMBER(InterfaceName, KeyName) \
+ v8::Eternal<v8::Private> V8_PRIVATE_PROPERTY_MEMBER_NAME( \
+ InterfaceName, KeyName); // NOLINT(readability/naming/underscores)
+ V8_PRIVATE_PROPERTY_FOR_EACH(V8_PRIVATE_PROPERTY_DECLARE_MEMBER)
+#undef V8_PRIVATE_PROPERTY_DECLARE_MEMBER
+
+ // TODO(peria): Do not use this specialized hack for
+ // Window#DocumentCachedAccessor. This is required to put v8::Private key in
+ // a snapshot, and it cannot be a v8::Eternal<> due to V8 serializer's
+ // requirement.
+ ScopedPersistent<v8::Private> symbol_window_document_cached_accessor_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_PRIVATE_PROPERTY_H_
diff --git a/chromium/third_party/blink/renderer/platform/bindings/v8_throw_exception.cc b/chromium/third_party/blink/renderer/platform/bindings/v8_throw_exception.cc
new file mode 100644
index 00000000000..dca7d040c8c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/v8_throw_exception.cc
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/bindings/v8_throw_exception.h"
+
+#include "third_party/blink/renderer/platform/bindings/v8_binding.h"
+
+namespace blink {
+
+#define DEFINE_CREATE_AND_THROW_ERROR_FUNC(blinkErrorType, v8ErrorType, \
+ defaultMessage) \
+ v8::Local<v8::Value> V8ThrowException::Create##blinkErrorType( \
+ v8::Isolate* isolate, const String& message) { \
+ return v8::Exception::v8ErrorType( \
+ V8String(isolate, message.IsNull() ? defaultMessage : message)); \
+ } \
+ \
+ void V8ThrowException::Throw##blinkErrorType(v8::Isolate* isolate, \
+ const String& message) { \
+ ThrowException(isolate, Create##blinkErrorType(isolate, message)); \
+ }
+
+DEFINE_CREATE_AND_THROW_ERROR_FUNC(Error, Error, "Error")
+DEFINE_CREATE_AND_THROW_ERROR_FUNC(RangeError, RangeError, "Range error")
+DEFINE_CREATE_AND_THROW_ERROR_FUNC(ReferenceError,
+ ReferenceError,
+ "Reference error")
+DEFINE_CREATE_AND_THROW_ERROR_FUNC(SyntaxError, SyntaxError, "Syntax error")
+DEFINE_CREATE_AND_THROW_ERROR_FUNC(TypeError, TypeError, "Type error")
+
+#undef DEFINE_CREATE_AND_THROW_ERROR_FUNC
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/bindings/v8_throw_exception.h b/chromium/third_party/blink/renderer/platform/bindings/v8_throw_exception.h
new file mode 100644
index 00000000000..24431f53bb9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/v8_throw_exception.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_THROW_EXCEPTION_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_THROW_EXCEPTION_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+// Provides utility functions to create and/or throw JS built-in errors.
+class PLATFORM_EXPORT V8ThrowException {
+ STATIC_ONLY(V8ThrowException);
+
+ public:
+ static void ThrowException(v8::Isolate* isolate,
+ v8::Local<v8::Value> exception) {
+ if (!isolate->IsExecutionTerminating())
+ isolate->ThrowException(exception);
+ }
+
+ static v8::Local<v8::Value> CreateError(v8::Isolate*, const String& message);
+ static v8::Local<v8::Value> CreateRangeError(v8::Isolate*,
+ const String& message);
+ static v8::Local<v8::Value> CreateReferenceError(v8::Isolate*,
+ const String& message);
+ static v8::Local<v8::Value> CreateSyntaxError(v8::Isolate*,
+ const String& message);
+ static v8::Local<v8::Value> CreateTypeError(v8::Isolate*,
+ const String& message);
+
+ static void ThrowError(v8::Isolate*, const String& message);
+ static void ThrowRangeError(v8::Isolate*, const String& message);
+ static void ThrowReferenceError(v8::Isolate*, const String& message);
+ static void ThrowSyntaxError(v8::Isolate*, const String& message);
+ static void ThrowTypeError(v8::Isolate*, const String& message);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_THROW_EXCEPTION_H_
diff --git a/chromium/third_party/blink/renderer/platform/bindings/v8_value_cache.cc b/chromium/third_party/blink/renderer/platform/bindings/v8_value_cache.cc
new file mode 100644
index 00000000000..c80e76ab2a4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/v8_value_cache.cc
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/bindings/v8_value_cache.h"
+
+#include <utility>
+#include "third_party/blink/renderer/platform/bindings/runtime_call_stats.h"
+#include "third_party/blink/renderer/platform/bindings/v8_binding.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+
+namespace blink {
+
+StringCacheMapTraits::MapType* StringCacheMapTraits::MapFromWeakCallbackInfo(
+ const v8::WeakCallbackInfo<WeakCallbackDataType>& data) {
+ return &(V8PerIsolateData::From(data.GetIsolate())
+ ->GetStringCache()
+ ->string_cache_);
+}
+
+void StringCacheMapTraits::Dispose(v8::Isolate* isolate,
+ v8::Global<v8::String> value,
+ StringImpl* key) {
+ V8PerIsolateData::From(isolate)->GetStringCache()->InvalidateLastString();
+ key->Release();
+}
+
+void StringCacheMapTraits::DisposeWeak(
+ const v8::WeakCallbackInfo<WeakCallbackDataType>& data) {
+ V8PerIsolateData::From(data.GetIsolate())
+ ->GetStringCache()
+ ->InvalidateLastString();
+ data.GetParameter()->Release();
+}
+
+void StringCacheMapTraits::OnWeakCallback(
+ const v8::WeakCallbackInfo<WeakCallbackDataType>& data) {
+ V8PerIsolateData::From(data.GetIsolate())
+ ->GetStringCache()
+ ->InvalidateLastString();
+}
+
+void StringCache::Dispose() {
+ // The MapType::Dispose callback calls StringCache::InvalidateLastString,
+ // which will only work while the destructor has not yet finished. Thus,
+ // we need to clear the map before the destructor has completed.
+ string_cache_.Clear();
+}
+
+static v8::Local<v8::String> MakeExternalString(v8::Isolate* isolate,
+ const String& string) {
+ if (string.Is8Bit()) {
+ StringResource8* string_resource = new StringResource8(string);
+ v8::Local<v8::String> new_string;
+ if (!v8::String::NewExternalOneByte(isolate, string_resource)
+ .ToLocal(&new_string)) {
+ delete string_resource;
+ return v8::String::Empty(isolate);
+ }
+ return new_string;
+ }
+
+ StringResource16* string_resource = new StringResource16(string);
+ v8::Local<v8::String> new_string;
+ if (!v8::String::NewExternalTwoByte(isolate, string_resource)
+ .ToLocal(&new_string)) {
+ delete string_resource;
+ return v8::String::Empty(isolate);
+ }
+ return new_string;
+}
+
+v8::Local<v8::String> StringCache::V8ExternalStringSlow(
+ v8::Isolate* isolate,
+ StringImpl* string_impl) {
+ RUNTIME_CALL_TIMER_SCOPE(isolate,
+ RuntimeCallStats::CounterId::kV8ExternalStringSlow);
+ if (!string_impl->length())
+ return v8::String::Empty(isolate);
+
+ StringCacheMapTraits::MapType::PersistentValueReference cached_v8_string =
+ string_cache_.GetReference(string_impl);
+ if (!cached_v8_string.IsEmpty()) {
+ last_string_impl_ = string_impl;
+ last_v8_string_ = cached_v8_string;
+ return last_v8_string_.NewLocal(isolate);
+ }
+
+ return CreateStringAndInsertIntoCache(isolate, string_impl);
+}
+
+void StringCache::SetReturnValueFromStringSlow(
+ v8::ReturnValue<v8::Value> return_value,
+ StringImpl* string_impl) {
+ RUNTIME_CALL_TIMER_SCOPE(
+ return_value.GetIsolate(),
+ RuntimeCallStats::CounterId::kSetReturnValueFromStringSlow);
+ if (!string_impl->length()) {
+ return_value.SetEmptyString();
+ return;
+ }
+
+ StringCacheMapTraits::MapType::PersistentValueReference cached_v8_string =
+ string_cache_.GetReference(string_impl);
+ if (!cached_v8_string.IsEmpty()) {
+ last_string_impl_ = string_impl;
+ last_v8_string_ = cached_v8_string;
+ last_v8_string_.SetReturnValue(return_value);
+ return;
+ }
+
+ return_value.Set(
+ CreateStringAndInsertIntoCache(return_value.GetIsolate(), string_impl));
+}
+
+v8::Local<v8::String> StringCache::CreateStringAndInsertIntoCache(
+ v8::Isolate* isolate,
+ StringImpl* string_impl) {
+ DCHECK(!string_cache_.Contains(string_impl));
+ DCHECK(string_impl->length());
+
+ v8::Local<v8::String> new_string =
+ MakeExternalString(isolate, String(string_impl));
+ DCHECK(!new_string.IsEmpty());
+ DCHECK(new_string->Length());
+
+ v8::UniquePersistent<v8::String> wrapper(isolate, new_string);
+
+ string_impl->AddRef();
+ string_cache_.Set(string_impl, std::move(wrapper), &last_v8_string_);
+ last_string_impl_ = string_impl;
+
+ return new_string;
+}
+
+void StringCache::InvalidateLastString() {
+ last_string_impl_ = nullptr;
+ last_v8_string_.Reset();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/bindings/v8_value_cache.h b/chromium/third_party/blink/renderer/platform/bindings/v8_value_cache.h
new file mode 100644
index 00000000000..cbb9b7097ed
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/v8_value_cache.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_VALUE_CACHE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_VALUE_CACHE_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/bindings/v8_global_value_map.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+class StringCacheMapTraits
+ : public V8GlobalValueMapTraits<StringImpl*,
+ v8::String,
+ v8::kWeakWithParameter> {
+ STATIC_ONLY(StringCacheMapTraits);
+
+ public:
+ // Weak traits:
+ typedef StringImpl WeakCallbackDataType;
+ typedef v8::GlobalValueMap<StringImpl*, v8::String, StringCacheMapTraits>
+ MapType;
+
+ static WeakCallbackDataType* WeakCallbackParameter(
+ MapType* map,
+ StringImpl* key,
+ v8::Local<v8::String>& value) {
+ return key;
+ }
+ static void DisposeCallbackData(WeakCallbackDataType* callback_data) {}
+
+ static MapType* MapFromWeakCallbackInfo(
+ const v8::WeakCallbackInfo<WeakCallbackDataType>&);
+
+ static StringImpl* KeyFromWeakCallbackInfo(
+ const v8::WeakCallbackInfo<WeakCallbackDataType>& data) {
+ return data.GetParameter();
+ }
+
+ static void OnWeakCallback(const v8::WeakCallbackInfo<WeakCallbackDataType>&);
+
+ static void Dispose(v8::Isolate*,
+ v8::Global<v8::String> value,
+ StringImpl* key);
+ static void DisposeWeak(const v8::WeakCallbackInfo<WeakCallbackDataType>&);
+};
+
+// String cache helps convert WTF strings (StringImpl*) into v8 strings by
+// only creating a v8::String for a particular StringImpl* once and caching it
+// for future use. It is held by and can be retrieved from V8PerIsolateData, and
+// is cleared when the isolate is destroyed. Entries are removed from the
+// backing global value map when weak references to the values are collected.
+class PLATFORM_EXPORT StringCache {
+ USING_FAST_MALLOC(StringCache);
+ WTF_MAKE_NONCOPYABLE(StringCache);
+
+ public:
+ explicit StringCache(v8::Isolate* isolate) : string_cache_(isolate) {}
+
+ v8::Local<v8::String> V8ExternalString(v8::Isolate* isolate,
+ StringImpl* string_impl) {
+ DCHECK(string_impl);
+ if (last_string_impl_.get() == string_impl)
+ return last_v8_string_.NewLocal(isolate);
+ return V8ExternalStringSlow(isolate, string_impl);
+ }
+
+ void SetReturnValueFromString(v8::ReturnValue<v8::Value> return_value,
+ StringImpl* string_impl) {
+ DCHECK(string_impl);
+ if (last_string_impl_.get() == string_impl)
+ last_v8_string_.SetReturnValue(return_value);
+ else
+ SetReturnValueFromStringSlow(return_value, string_impl);
+ }
+
+ void Dispose();
+
+ friend class StringCacheMapTraits;
+
+ private:
+ v8::Local<v8::String> V8ExternalStringSlow(v8::Isolate*, StringImpl*);
+ void SetReturnValueFromStringSlow(v8::ReturnValue<v8::Value>, StringImpl*);
+ v8::Local<v8::String> CreateStringAndInsertIntoCache(v8::Isolate*,
+ StringImpl*);
+ void InvalidateLastString();
+
+ StringCacheMapTraits::MapType string_cache_;
+ StringCacheMapTraits::MapType::PersistentValueReference last_v8_string_;
+
+ // Note: RefPtr is a must as we cache by StringImpl* equality, not identity
+ // hence lastStringImpl might be not a key of the cache (in sense of identity)
+ // and hence it's not refed on addition.
+ scoped_refptr<StringImpl> last_string_impl_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_VALUE_CACHE_H_
diff --git a/chromium/third_party/blink/renderer/platform/bindings/wrapper_creation_security_check.cc b/chromium/third_party/blink/renderer/platform/bindings/wrapper_creation_security_check.cc
new file mode 100644
index 00000000000..8d9b988583c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/wrapper_creation_security_check.cc
@@ -0,0 +1,41 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/bindings/wrapper_creation_security_check.h"
+
+#include "third_party/blink/renderer/platform/bindings/wrapper_type_info.h"
+
+namespace blink {
+
+WrapperCreationSecurityCheck::SecurityCheckFunction
+ WrapperCreationSecurityCheck::security_check_ = nullptr;
+WrapperCreationSecurityCheck::RethrowExceptionFunction
+ WrapperCreationSecurityCheck::rethrow_exception_ = nullptr;
+
+void WrapperCreationSecurityCheck::SetSecurityCheckFunction(
+ SecurityCheckFunction func) {
+ DCHECK(!security_check_);
+ security_check_ = func;
+}
+
+void WrapperCreationSecurityCheck::SetRethrowExceptionFunction(
+ RethrowExceptionFunction func) {
+ DCHECK(!rethrow_exception_);
+ rethrow_exception_ = func;
+}
+
+bool WrapperCreationSecurityCheck::VerifyContextAccess(
+ v8::Local<v8::Context> creation_context,
+ const WrapperTypeInfo* type) {
+ return (*security_check_)(creation_context, type);
+}
+
+void WrapperCreationSecurityCheck::RethrowCrossContextException(
+ v8::Local<v8::Context> creation_context,
+ const WrapperTypeInfo* type,
+ v8::Local<v8::Value> cross_context_exception) {
+ (*rethrow_exception_)(creation_context, type, cross_context_exception);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/bindings/wrapper_creation_security_check.h b/chromium/third_party/blink/renderer/platform/bindings/wrapper_creation_security_check.h
new file mode 100644
index 00000000000..a5f6ea1bf63
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/wrapper_creation_security_check.h
@@ -0,0 +1,46 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_WRAPPER_CREATION_SECURITY_CHECK_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_WRAPPER_CREATION_SECURITY_CHECK_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+struct WrapperTypeInfo;
+
+// This class holds pointers to functions that implement creation context access
+// and exception rethrowing logic required by V8WrapperInstantiationScope when
+// creating wrappers.
+class PLATFORM_EXPORT WrapperCreationSecurityCheck {
+ STATIC_ONLY(WrapperCreationSecurityCheck);
+
+ public:
+ using SecurityCheckFunction = bool (*)(v8::Local<v8::Context>,
+ const WrapperTypeInfo*);
+ using RethrowExceptionFunction = void (*)(v8::Local<v8::Context>,
+ const WrapperTypeInfo*,
+ v8::Local<v8::Value>);
+
+ static void SetSecurityCheckFunction(SecurityCheckFunction);
+ static void SetRethrowExceptionFunction(RethrowExceptionFunction);
+
+ static bool VerifyContextAccess(v8::Local<v8::Context> creation_context,
+ const WrapperTypeInfo*);
+ static void RethrowCrossContextException(
+ v8::Local<v8::Context> creation_context,
+ const WrapperTypeInfo*,
+ v8::Local<v8::Value> cross_context_exception);
+
+ private:
+ static SecurityCheckFunction security_check_;
+ static RethrowExceptionFunction rethrow_exception_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_WRAPPER_CREATION_SECURITY_CHECK_H_
diff --git a/chromium/third_party/blink/renderer/platform/bindings/wrapper_type_info.cc b/chromium/third_party/blink/renderer/platform/bindings/wrapper_type_info.cc
new file mode 100644
index 00000000000..0b1581ee8d4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/wrapper_type_info.cc
@@ -0,0 +1,14 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/bindings/wrapper_type_info.h"
+
+namespace blink {
+
+static_assert(offsetof(struct WrapperTypeInfo, gin_embedder) ==
+ offsetof(struct gin::WrapperInfo, embedder),
+ "offset of WrapperTypeInfo.ginEmbedder must be the same as "
+ "gin::WrapperInfo.embedder");
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/bindings/wrapper_type_info.h b/chromium/third_party/blink/renderer/platform/bindings/wrapper_type_info.h
new file mode 100644
index 00000000000..201bf7ad516
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/bindings/wrapper_type_info.h
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2010 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_WRAPPER_TYPE_INFO_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_WRAPPER_TYPE_INFO_H_
+
+#include "gin/public/wrapper_info.h"
+#include "third_party/blink/renderer/platform/bindings/active_script_wrappable_base.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+class DOMWrapperWorld;
+class ScriptWrappable;
+
+ScriptWrappable* ToScriptWrappable(
+ const v8::PersistentBase<v8::Object>& wrapper);
+ScriptWrappable* ToScriptWrappable(v8::Local<v8::Object> wrapper);
+
+static const int kV8DOMWrapperTypeIndex =
+ static_cast<int>(gin::kWrapperInfoIndex);
+static const int kV8DOMWrapperObjectIndex =
+ static_cast<int>(gin::kEncodedValueIndex);
+static const int kV8DefaultWrapperInternalFieldCount =
+ static_cast<int>(gin::kNumberOfInternalFields);
+static const int kV8PrototypeTypeIndex = 0;
+static const int kV8PrototypeInternalFieldcount = 1;
+
+typedef v8::Local<v8::FunctionTemplate> (
+ *DomTemplateFunction)(v8::Isolate*, const DOMWrapperWorld&);
+typedef ActiveScriptWrappableBase* (*ToActiveScriptWrappableFunction)(
+ v8::Local<v8::Object>);
+typedef void (*ResolveWrapperReachabilityFunction)(
+ v8::Isolate*,
+ ScriptWrappable*,
+ const v8::Persistent<v8::Object>&);
+typedef void (*InstallConditionalFeaturesFunction)(
+ v8::Local<v8::Context>,
+ const DOMWrapperWorld&,
+ v8::Local<v8::Object>,
+ v8::Local<v8::Object>,
+ v8::Local<v8::Function>,
+ v8::Local<v8::FunctionTemplate>);
+
+// This struct provides a way to store a bunch of information that is helpful
+// when unwrapping v8 objects. Each v8 bindings class has exactly one static
+// WrapperTypeInfo member, so comparing pointers is a safe way to determine if
+// types match.
+struct WrapperTypeInfo {
+ DISALLOW_NEW();
+
+ enum WrapperTypePrototype {
+ kWrapperTypeObjectPrototype,
+ kWrapperTypeNoPrototype, // For legacy callback interface
+ };
+
+ enum WrapperClassId {
+ kNodeClassId = 1, // NodeClassId must be smaller than ObjectClassId.
+ kObjectClassId,
+ };
+
+ enum ActiveScriptWrappableInheritance {
+ kNotInheritFromActiveScriptWrappable,
+ kInheritFromActiveScriptWrappable,
+ };
+
+ static const WrapperTypeInfo* Unwrap(v8::Local<v8::Value> type_info_wrapper) {
+ return reinterpret_cast<const WrapperTypeInfo*>(
+ v8::External::Cast(*type_info_wrapper)->Value());
+ }
+
+ static void WrapperCreated() {
+ ThreadState::Current()->Heap().HeapStats().IncreaseWrapperCount(1);
+ }
+
+ static void WrapperDestroyed() {
+ ThreadHeapStats& heap_stats = ThreadState::Current()->Heap().HeapStats();
+ heap_stats.DecreaseWrapperCount(1);
+ heap_stats.IncreaseCollectedWrapperCount(1);
+ }
+
+ bool Equals(const WrapperTypeInfo* that) const { return this == that; }
+
+ bool IsSubclass(const WrapperTypeInfo* that) const {
+ for (const WrapperTypeInfo* current = this; current;
+ current = current->parent_class) {
+ if (current == that)
+ return true;
+ }
+
+ return false;
+ }
+
+ void ConfigureWrapper(v8::PersistentBase<v8::Object>* wrapper) const {
+ wrapper->SetWrapperClassId(wrapper_class_id);
+ }
+
+ v8::Local<v8::FunctionTemplate> domTemplate(
+ v8::Isolate* isolate,
+ const DOMWrapperWorld& world) const {
+ return dom_template_function(isolate, world);
+ }
+
+ void InstallConditionalFeatures(
+ v8::Local<v8::Context> context,
+ const DOMWrapperWorld& world,
+ v8::Local<v8::Object> instance_object,
+ v8::Local<v8::Object> prototype_object,
+ v8::Local<v8::Function> interface_object,
+ v8::Local<v8::FunctionTemplate> interface_template) const {
+ if (install_conditional_features_function) {
+ install_conditional_features_function(context, world, instance_object,
+ prototype_object, interface_object,
+ interface_template);
+ }
+ }
+
+ bool IsActiveScriptWrappable() const {
+ return active_script_wrappable_inheritance ==
+ kInheritFromActiveScriptWrappable;
+ }
+
+ // This field must be the first member of the struct WrapperTypeInfo.
+ // See also static_assert() in .cpp file.
+ const gin::GinEmbedder gin_embedder;
+
+ DomTemplateFunction dom_template_function;
+ InstallConditionalFeaturesFunction install_conditional_features_function;
+ const char* const interface_name;
+ const WrapperTypeInfo* parent_class;
+ const unsigned wrapper_type_prototype : 2; // WrapperTypePrototype
+ const unsigned wrapper_class_id : 2; // WrapperClassId
+ const unsigned // ActiveScriptWrappableInheritance
+ active_script_wrappable_inheritance : 1;
+};
+
+template <typename T, int offset>
+inline T* GetInternalField(const v8::PersistentBase<v8::Object>& persistent) {
+ DCHECK_LT(offset, v8::Object::InternalFieldCount(persistent));
+ return reinterpret_cast<T*>(
+ v8::Object::GetAlignedPointerFromInternalField(persistent, offset));
+}
+
+template <typename T, int offset>
+inline T* GetInternalField(v8::Local<v8::Object> wrapper) {
+ DCHECK_LT(offset, wrapper->InternalFieldCount());
+ return reinterpret_cast<T*>(
+ wrapper->GetAlignedPointerFromInternalField(offset));
+}
+
+// The return value can be null if |wrapper| is a global proxy, which points to
+// nothing while a navigation.
+inline ScriptWrappable* ToScriptWrappable(
+ const v8::PersistentBase<v8::Object>& wrapper) {
+ return GetInternalField<ScriptWrappable, kV8DOMWrapperObjectIndex>(wrapper);
+}
+
+inline ScriptWrappable* ToScriptWrappable(v8::Local<v8::Object> wrapper) {
+ return GetInternalField<ScriptWrappable, kV8DOMWrapperObjectIndex>(wrapper);
+}
+
+inline const WrapperTypeInfo* ToWrapperTypeInfo(
+ const v8::PersistentBase<v8::Object>& wrapper) {
+ return GetInternalField<WrapperTypeInfo, kV8DOMWrapperTypeIndex>(wrapper);
+}
+
+inline const WrapperTypeInfo* ToWrapperTypeInfo(v8::Local<v8::Object> wrapper) {
+ return GetInternalField<WrapperTypeInfo, kV8DOMWrapperTypeIndex>(wrapper);
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_WRAPPER_TYPE_INFO_H_
diff --git a/chromium/third_party/blink/renderer/platform/blob/BUILD.gn b/chromium/third_party/blink/renderer/platform/blob/BUILD.gn
new file mode 100644
index 00000000000..f6af4da29ae
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/blob/BUILD.gn
@@ -0,0 +1,88 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/jumbo.gni")
+import("//third_party/blink/renderer/platform/platform.gni")
+
+# Intentionally depends on generator targets so to depend only on generated
+# mojo biding header files. This is needed to use mojo interfaces from a static
+# library such as platform:test_support without causing duplicated symbols.
+# There is no tool to detect missing indirect generated header dependency today
+# and this is easy to be broken when mojom files are updated to depend on
+# another.
+jumbo_source_set("generator") {
+ public_deps = [
+ "//third_party/blink/public/mojom:mojom_platform_blink__generator",
+ "//url/mojom:url_mojom_gurl_blink__generator",
+ "//url/mojom:url_mojom_gurl_shared__generator",
+ "//url/mojom:url_mojom_origin_blink__generator",
+ "//url/mojom:url_mojom_origin_shared__generator",
+ ]
+}
+
+blink_platform_sources("blob") {
+ sources = [
+ "blob_bytes_provider.cc",
+ "blob_bytes_provider.h",
+ "blob_data.cc",
+ "blob_data.h",
+ "blob_registry.cc",
+ "blob_registry.h",
+ "blob_url.cc",
+ "blob_url.h",
+ "serialized_blob_struct_traits.cc",
+ "serialized_blob_struct_traits.h",
+ ]
+
+ deps = [
+ "//services/network/public/mojom:mojom_blink",
+ ]
+}
+
+jumbo_source_set("unit_tests") {
+ visibility = [ "//third_party/blink/renderer/platform:*" ]
+ testonly = true
+
+ sources = [
+ "blob_bytes_provider_test.cc",
+ "blob_data_test.cc",
+ ]
+
+ configs += [ "//third_party/blink/renderer/platform:blink_platform_config" ]
+
+ deps = [
+ "//base/test:test_support",
+ "//testing/gmock",
+ "//testing/gtest",
+ "//third_party/blink/renderer/platform:platform",
+ ]
+}
+
+jumbo_source_set("test_support") {
+ # This target defines test files for platform:test_support that
+ # blink_platform_unittests and webkit_unit_tests can use.
+ visibility = [ "//third_party/blink/renderer/platform:test_support" ]
+ testonly = true
+
+ # Source files that can be called from blink_platform_unittests and
+ # webkit_unit_tests.
+ sources = [
+ "testing/fake_blob.cc",
+ "testing/fake_blob.h",
+ "testing/fake_blob_registry.cc",
+ "testing/fake_blob_registry.h",
+ "testing/fake_blob_url_store.cc",
+ "testing/fake_blob_url_store.h",
+ ]
+
+ configs += [
+ "//third_party/blink/renderer:non_test_config",
+ "//third_party/blink/renderer/platform:blink_platform_config",
+ ]
+
+ public_deps = [
+ ":generator",
+ "//third_party/blink/public:blink_headers",
+ ]
+}
diff --git a/chromium/third_party/blink/renderer/platform/blob/OWNERS b/chromium/third_party/blink/renderer/platform/blob/OWNERS
new file mode 100644
index 00000000000..9561ef853b6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/blob/OWNERS
@@ -0,0 +1,10 @@
+dmurph@chromium.org
+mek@chromium.org
+
+per-file *_struct_traits*.*=set noparent
+per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS
+per-file *.typemap=set noparent
+per-file *.typemap=file://ipc/SECURITY_OWNERS
+
+# TEAM: storage-dev@chromium.org
+# COMPONENT: Blink>FileAPI
diff --git a/chromium/third_party/blink/renderer/platform/blob/blob_bytes_provider.cc b/chromium/third_party/blink/renderer/platform/blob/blob_bytes_provider.cc
new file mode 100644
index 00000000000..70a61b5ac9f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/blob/blob_bytes_provider.cc
@@ -0,0 +1,261 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/blob/blob_bytes_provider.h"
+
+#include "base/numerics/safe_conversions.h"
+#include "base/task_scheduler/post_task.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/histogram.h"
+#include "third_party/blink/renderer/platform/web_task_runner.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+
+namespace blink {
+
+namespace {
+
+// Helper class that streams all the bytes from a vector of RawData RefPtrs to
+// a mojo data pipe. Instances will delete themselves when all data has been
+// written, or when the data pipe is disconnected.
+class BlobBytesStreamer {
+ public:
+ BlobBytesStreamer(Vector<scoped_refptr<RawData>> data,
+ mojo::ScopedDataPipeProducerHandle pipe,
+ scoped_refptr<base::SequencedTaskRunner> task_runner)
+ : data_(std::move(data)),
+ pipe_(std::move(pipe)),
+ watcher_(FROM_HERE,
+ mojo::SimpleWatcher::ArmingPolicy::AUTOMATIC,
+ std::move(task_runner)) {
+ watcher_.Watch(pipe_.get(), MOJO_HANDLE_SIGNAL_WRITABLE,
+ WTF::BindRepeating(&BlobBytesStreamer::OnWritable,
+ WTF::Unretained(this)));
+ }
+
+ void OnWritable(MojoResult result) {
+ if (result == MOJO_RESULT_CANCELLED ||
+ result == MOJO_RESULT_FAILED_PRECONDITION) {
+ delete this;
+ return;
+ }
+ DCHECK_EQ(result, MOJO_RESULT_OK);
+
+ while (true) {
+ uint32_t num_bytes = base::saturated_cast<uint32_t>(
+ data_[current_item_]->length() - current_item_offset_);
+ MojoResult write_result =
+ pipe_->WriteData(data_[current_item_]->data() + current_item_offset_,
+ &num_bytes, MOJO_WRITE_DATA_FLAG_NONE);
+ if (write_result == MOJO_RESULT_OK) {
+ current_item_offset_ += num_bytes;
+ if (current_item_offset_ >= data_[current_item_]->length()) {
+ data_[current_item_] = nullptr;
+ current_item_++;
+ current_item_offset_ = 0;
+ if (current_item_ >= data_.size()) {
+ // All items were sent completely.
+ delete this;
+ return;
+ }
+ }
+ } else if (write_result == MOJO_RESULT_SHOULD_WAIT) {
+ break;
+ } else {
+ // Writing failed. This isn't necessarily bad, as this could just mean
+ // the browser no longer needs the data for this blob. So just delete
+ // this as sending data is definitely finished.
+ delete this;
+ return;
+ }
+ }
+ }
+
+ private:
+ // The index of the item currently being written.
+ size_t current_item_ = 0;
+ // The offset into the current item of the first byte not yet written to the
+ // data pipe.
+ size_t current_item_offset_ = 0;
+ // The data being written.
+ Vector<scoped_refptr<RawData>> data_;
+
+ mojo::ScopedDataPipeProducerHandle pipe_;
+ mojo::SimpleWatcher watcher_;
+};
+
+// This keeps the process alive while blobs are being transferred.
+void IncreaseChildProcessRefCount() {
+ if (!Platform::Current()->MainThread()->IsCurrentThread()) {
+ PostCrossThreadTask(*Platform::Current()->MainThread()->GetTaskRunner(),
+ FROM_HERE,
+ CrossThreadBind(&IncreaseChildProcessRefCount));
+ return;
+ }
+ Platform::Current()->SuddenTerminationChanged(false);
+ Platform::Current()->AddRefProcess();
+}
+
+void DecreaseChildProcessRefCount() {
+ if (!Platform::Current()->MainThread()->IsCurrentThread()) {
+ PostCrossThreadTask(*Platform::Current()->MainThread()->GetTaskRunner(),
+ FROM_HERE,
+ CrossThreadBind(&DecreaseChildProcessRefCount));
+ return;
+ }
+ Platform::Current()->SuddenTerminationChanged(true);
+ Platform::Current()->ReleaseRefProcess();
+}
+
+} // namespace
+
+constexpr size_t BlobBytesProvider::kMaxConsolidatedItemSizeInBytes;
+
+// static
+BlobBytesProvider* BlobBytesProvider::CreateAndBind(
+ mojom::blink::BytesProviderRequest request) {
+ auto task_runner = base::CreateSequencedTaskRunnerWithTraits(
+ {base::MayBlock(), base::TaskPriority::USER_VISIBLE});
+ auto provider = base::WrapUnique(new BlobBytesProvider(task_runner));
+ auto* result = provider.get();
+ // TODO(mek): Consider binding BytesProvider on the IPC thread instead, only
+ // using the MayBlock taskrunner for actual file operations.
+ PostCrossThreadTask(
+ *task_runner, FROM_HERE,
+ CrossThreadBind(
+ [](std::unique_ptr<BlobBytesProvider> provider,
+ mojom::blink::BytesProviderRequest request) {
+ mojo::MakeStrongBinding(std::move(provider), std::move(request));
+ },
+ WTF::Passed(std::move(provider)), WTF::Passed(std::move(request))));
+ return result;
+}
+
+// static
+std::unique_ptr<BlobBytesProvider> BlobBytesProvider::CreateForTesting(
+ scoped_refptr<base::SequencedTaskRunner> task_runner) {
+ return base::WrapUnique(new BlobBytesProvider(std::move(task_runner)));
+}
+
+BlobBytesProvider::~BlobBytesProvider() {
+ DecreaseChildProcessRefCount();
+}
+
+void BlobBytesProvider::AppendData(scoped_refptr<RawData> data) {
+ if (!data_.IsEmpty()) {
+ uint64_t last_offset = offsets_.IsEmpty() ? 0 : offsets_.back();
+ offsets_.push_back(last_offset + data_.back()->length());
+ }
+ data_.push_back(std::move(data));
+}
+
+void BlobBytesProvider::AppendData(base::span<const char> data) {
+ if (data_.IsEmpty() ||
+ data_.back()->length() + data.size() > kMaxConsolidatedItemSizeInBytes) {
+ AppendData(RawData::Create());
+ }
+ data_.back()->MutableData()->Append(data.data(), data.size());
+}
+
+void BlobBytesProvider::RequestAsReply(RequestAsReplyCallback callback) {
+ DCHECK(task_runner_->RunsTasksInCurrentSequence());
+ // TODO(mek): Once better metrics are created we could experiment with ways
+ // to reduce the number of copies of data that are made here.
+ Vector<uint8_t> result;
+ for (const auto& d : data_)
+ result.Append(d->data(), d->length());
+ std::move(callback).Run(result);
+}
+
+void BlobBytesProvider::RequestAsStream(
+ mojo::ScopedDataPipeProducerHandle pipe) {
+ DCHECK(task_runner_->RunsTasksInCurrentSequence());
+ // BlobBytesStreamer will self delete when done.
+ new BlobBytesStreamer(std::move(data_), std::move(pipe), task_runner_);
+}
+
+void BlobBytesProvider::RequestAsFile(uint64_t source_offset,
+ uint64_t source_size,
+ base::File file,
+ uint64_t file_offset,
+ RequestAsFileCallback callback) {
+ DCHECK(task_runner_->RunsTasksInCurrentSequence());
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(BooleanHistogram, seek_histogram,
+ ("Storage.Blob.RendererFileSeekFailed"));
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(BooleanHistogram, write_histogram,
+ ("Storage.Blob.RendererFileWriteFailed"));
+
+ if (!file.IsValid()) {
+ std::move(callback).Run(WTF::nullopt);
+ return;
+ }
+
+ int64_t seek_distance =
+ file.Seek(base::File::FROM_BEGIN, SafeCast<int64_t>(file_offset));
+ bool seek_failed = seek_distance < 0;
+ seek_histogram.Count(seek_failed);
+ if (seek_failed) {
+ std::move(callback).Run(WTF::nullopt);
+ return;
+ }
+
+ // Find first data item that should be read from (by finding the first offset
+ // that starts after the offset we want to start reading from).
+ size_t data_index =
+ std::upper_bound(offsets_.begin(), offsets_.end(), source_offset) -
+ offsets_.begin();
+
+ // Offset of the current data chunk in the overall stream provided by this
+ // provider.
+ uint64_t offset = data_index == 0 ? 0 : offsets_[data_index - 1];
+ for (; data_index < data_.size(); ++data_index) {
+ const auto& data = data_[data_index];
+
+ // We're done if the beginning of the current chunk is past the end of the
+ // data to write.
+ if (offset >= source_offset + source_size)
+ break;
+
+ // Offset within this chunk where writing needs to start from.
+ uint64_t data_offset = offset > source_offset ? 0 : source_offset - offset;
+ uint64_t data_size =
+ std::min(data->length() - data_offset,
+ source_offset + source_size - offset - data_offset);
+ size_t written = 0;
+ while (written < data_size) {
+ int writing_size = base::saturated_cast<int>(data_size - written);
+ int actual_written = file.WriteAtCurrentPos(
+ data->data() + data_offset + written, writing_size);
+ bool write_failed = actual_written < 0;
+ write_histogram.Count(write_failed);
+ if (write_failed) {
+ std::move(callback).Run(WTF::nullopt);
+ return;
+ }
+ written += actual_written;
+ }
+
+ offset += data->length();
+ }
+
+ if (!file.Flush()) {
+ std::move(callback).Run(WTF::nullopt);
+ return;
+ }
+ base::File::Info info;
+ if (!file.GetInfo(&info)) {
+ std::move(callback).Run(WTF::nullopt);
+ return;
+ }
+ std::move(callback).Run(info.last_modified);
+}
+
+BlobBytesProvider::BlobBytesProvider(
+ scoped_refptr<base::SequencedTaskRunner> task_runner)
+ : task_runner_(std::move(task_runner)) {
+ IncreaseChildProcessRefCount();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/blob/blob_bytes_provider.h b/chromium/third_party/blink/renderer/platform/blob/blob_bytes_provider.h
new file mode 100644
index 00000000000..9799b034291
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/blob/blob_bytes_provider.h
@@ -0,0 +1,65 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BLOB_BLOB_BYTES_PROVIDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BLOB_BLOB_BYTES_PROVIDER_H_
+
+#include "third_party/blink/public/mojom/blob/blob_registry.mojom-blink.h"
+#include "third_party/blink/renderer/platform/blob/blob_data.h"
+
+namespace blink {
+
+// Implementation of the BytesProvider mojo interface, used to transport bytes
+// making up a blob to the browser process, at the request of the blob service.
+//
+// Typical usage of this class creates and calls AppendData on one thread, and
+// then transfers ownership of the class to a different thread where it will be
+// bound to a mojo pipe, such that the various Request* methods are called on a
+// thread that is allowed to do File IO.
+class PLATFORM_EXPORT BlobBytesProvider : public mojom::blink::BytesProvider {
+ public:
+ // All consecutive items that are accumulate to < this number will have the
+ // data appended to the same item.
+ static constexpr size_t kMaxConsolidatedItemSizeInBytes = 15 * 1024;
+
+ // Creates a new instance, and binds it on a new SequencedTaskRunner. The
+ // returned instance should only be considered valid as long as the request
+ // passed in to this method is still known to be valid.
+ static BlobBytesProvider* CreateAndBind(mojom::blink::BytesProviderRequest);
+ static std::unique_ptr<BlobBytesProvider> CreateForTesting(
+ scoped_refptr<base::SequencedTaskRunner>);
+
+ ~BlobBytesProvider() override;
+
+ void AppendData(scoped_refptr<RawData>);
+ void AppendData(base::span<const char>);
+
+ // BytesProvider implementation:
+ void RequestAsReply(RequestAsReplyCallback) override;
+ void RequestAsStream(mojo::ScopedDataPipeProducerHandle) override;
+ void RequestAsFile(uint64_t source_offset,
+ uint64_t source_size,
+ base::File,
+ uint64_t file_offset,
+ RequestAsFileCallback) override;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(BlobBytesProviderTest, Consolidation);
+
+ BlobBytesProvider(scoped_refptr<base::SequencedTaskRunner>);
+
+ // The task runner this class is bound on, as well as what is used by the
+ // RequestAsStream method to monitor the data pipe.
+ scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
+ Vector<scoped_refptr<RawData>> data_;
+ // |offsets_| always contains exactly one fewer item than |data_| (except when
+ // |data_| itself is empty).
+ // offsets_[x] is equal to the sum of data_[i].length for all i <= x.
+ Vector<uint64_t> offsets_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BLOB_BLOB_BYTES_PROVIDER_H_
diff --git a/chromium/third_party/blink/renderer/platform/blob/blob_bytes_provider_test.cc b/chromium/third_party/blink/renderer/platform/blob/blob_bytes_provider_test.cc
new file mode 100644
index 00000000000..51cddb2949c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/blob/blob_bytes_provider_test.cc
@@ -0,0 +1,361 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/blob/blob_bytes_provider.h"
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+#include "base/files/file.h"
+#include "base/files/file_util.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
+
+namespace blink {
+
+class BlobBytesProviderTest : public testing::Test {
+ public:
+ void SetUp() override {
+ test_bytes1_.resize(128);
+ for (size_t i = 0; i < test_bytes1_.size(); ++i)
+ test_bytes1_[i] = i % 191;
+ test_data1_ = RawData::Create();
+ test_data1_->MutableData()->AppendVector(test_bytes1_);
+ test_bytes2_.resize(64);
+ for (size_t i = 0; i < test_bytes2_.size(); ++i)
+ test_bytes2_[i] = i;
+ test_data2_ = RawData::Create();
+ test_data2_->MutableData()->AppendVector(test_bytes2_);
+ test_bytes3_.resize(32);
+ for (size_t i = 0; i < test_bytes3_.size(); ++i)
+ test_bytes3_[i] = (i + 10) % 137;
+ test_data3_ = RawData::Create();
+ test_data3_->MutableData()->AppendVector(test_bytes3_);
+
+ combined_bytes_.AppendVector(test_bytes1_);
+ combined_bytes_.AppendVector(test_bytes2_);
+ combined_bytes_.AppendVector(test_bytes3_);
+ }
+
+ std::unique_ptr<BlobBytesProvider> CreateProvider(
+ scoped_refptr<RawData> data = nullptr) {
+ auto result = BlobBytesProvider::CreateForTesting(
+ blink::scheduler::GetSequencedTaskRunnerForTesting());
+ if (data)
+ result->AppendData(std::move(data));
+ return result;
+ }
+
+ protected:
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
+
+ scoped_refptr<RawData> test_data1_;
+ Vector<uint8_t> test_bytes1_;
+ scoped_refptr<RawData> test_data2_;
+ Vector<uint8_t> test_bytes2_;
+ scoped_refptr<RawData> test_data3_;
+ Vector<uint8_t> test_bytes3_;
+ Vector<uint8_t> combined_bytes_;
+};
+
+TEST_F(BlobBytesProviderTest, Consolidation) {
+ auto data = CreateProvider();
+ data->AppendData(base::make_span("abc", 3));
+ data->AppendData(base::make_span("def", 3));
+ data->AppendData(base::make_span("ps1", 3));
+ data->AppendData(base::make_span("ps2", 3));
+
+ EXPECT_EQ(1u, data->data_.size());
+ EXPECT_EQ(12u, data->data_[0]->length());
+ EXPECT_EQ(0, memcmp(data->data_[0]->data(), "abcdefps1ps2", 12));
+
+ auto large_data = std::make_unique<char[]>(
+ BlobBytesProvider::kMaxConsolidatedItemSizeInBytes);
+ data->AppendData(base::make_span(
+ large_data.get(), BlobBytesProvider::kMaxConsolidatedItemSizeInBytes));
+
+ EXPECT_EQ(2u, data->data_.size());
+ EXPECT_EQ(12u, data->data_[0]->length());
+ EXPECT_EQ(BlobBytesProvider::kMaxConsolidatedItemSizeInBytes,
+ data->data_[1]->length());
+}
+
+TEST_F(BlobBytesProviderTest, RequestAsReply) {
+ auto provider = CreateProvider(test_data1_);
+ Vector<uint8_t> received_bytes;
+ provider->RequestAsReply(
+ base::BindOnce([](Vector<uint8_t>* bytes_out,
+ const Vector<uint8_t>& bytes) { *bytes_out = bytes; },
+ &received_bytes));
+ EXPECT_EQ(test_bytes1_, received_bytes);
+
+ received_bytes.clear();
+ provider = CreateProvider();
+ provider->AppendData(test_data1_);
+ provider->AppendData(test_data2_);
+ provider->AppendData(test_data3_);
+ provider->RequestAsReply(
+ base::BindOnce([](Vector<uint8_t>* bytes_out,
+ const Vector<uint8_t>& bytes) { *bytes_out = bytes; },
+ &received_bytes));
+ EXPECT_EQ(combined_bytes_, received_bytes);
+}
+
+namespace {
+
+struct FileTestData {
+ uint64_t offset;
+ uint64_t size;
+};
+
+void PrintTo(const FileTestData& test, std::ostream* os) {
+ *os << "offset: " << test.offset << ", size: " << test.size;
+}
+
+class RequestAsFile : public BlobBytesProviderTest,
+ public testing::WithParamInterface<FileTestData> {
+ public:
+ void SetUp() override {
+ BlobBytesProviderTest::SetUp();
+ test_provider_ = CreateProvider();
+ test_provider_->AppendData(test_data1_);
+ test_provider_->AppendData(test_data2_);
+ test_provider_->AppendData(test_data3_);
+
+ sliced_data_.AppendRange(
+ combined_bytes_.begin() + GetParam().offset,
+ combined_bytes_.begin() + GetParam().offset + GetParam().size);
+ }
+
+ base::File DoRequestAsFile(uint64_t source_offset,
+ uint64_t source_length,
+ uint64_t file_offset) {
+ base::FilePath path;
+ base::CreateTemporaryFile(&path);
+ WTF::Optional<WTF::Time> received_modified;
+ test_provider_->RequestAsFile(
+ source_offset, source_length,
+ base::File(path, base::File::FLAG_OPEN | base::File::FLAG_WRITE),
+ file_offset,
+ base::BindOnce(
+ [](WTF::Optional<WTF::Time>* received_modified,
+ WTF::Optional<WTF::Time> modified) {
+ *received_modified = modified;
+ },
+ &received_modified));
+ base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ |
+ base::File::FLAG_DELETE_ON_CLOSE);
+ base::File::Info info;
+ EXPECT_TRUE(file.GetInfo(&info));
+ EXPECT_EQ(info.last_modified, received_modified);
+ return file;
+ }
+
+ protected:
+ std::unique_ptr<BlobBytesProvider> test_provider_;
+ Vector<uint8_t> sliced_data_;
+};
+
+TEST_P(RequestAsFile, AtStartOfEmptyFile) {
+ FileTestData test = GetParam();
+ base::File file = DoRequestAsFile(test.offset, test.size, 0);
+
+ base::File::Info info;
+ EXPECT_TRUE(file.GetInfo(&info));
+ EXPECT_EQ(static_cast<int64_t>(test.size), info.size);
+
+ Vector<uint8_t> read_data(test.size);
+ EXPECT_EQ(static_cast<int>(test.size),
+ file.Read(0, reinterpret_cast<char*>(read_data.data()), test.size));
+ EXPECT_EQ(sliced_data_, read_data);
+}
+
+TEST_P(RequestAsFile, OffsetInEmptyFile) {
+ FileTestData test = GetParam();
+ int file_offset = 32;
+ sliced_data_.InsertVector(0, Vector<uint8_t>(file_offset));
+
+ base::File file = DoRequestAsFile(test.offset, test.size, file_offset);
+
+ base::File::Info info;
+ EXPECT_TRUE(file.GetInfo(&info));
+ if (test.size == 0) {
+ EXPECT_EQ(0, info.size);
+ } else {
+ EXPECT_EQ(static_cast<int64_t>(test.size) + 32, info.size);
+
+ Vector<uint8_t> read_data(sliced_data_.size());
+ EXPECT_EQ(static_cast<int>(sliced_data_.size()),
+ file.Read(0, reinterpret_cast<char*>(read_data.data()),
+ sliced_data_.size()));
+ EXPECT_EQ(sliced_data_, read_data);
+ }
+}
+
+TEST_P(RequestAsFile, OffsetInNonEmptyFile) {
+ FileTestData test = GetParam();
+ int file_offset = 23;
+
+ Vector<uint8_t> expected_data(1024, 42);
+
+ base::FilePath path;
+ base::CreateTemporaryFile(&path);
+ {
+ base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_WRITE);
+ EXPECT_EQ(static_cast<int>(expected_data.size()),
+ file.WriteAtCurrentPos(
+ reinterpret_cast<const char*>(expected_data.data()),
+ expected_data.size()));
+ }
+
+ std::copy(sliced_data_.begin(), sliced_data_.end(),
+ expected_data.begin() + file_offset);
+
+ test_provider_->RequestAsFile(
+ test.offset, test.size,
+ base::File(path, base::File::FLAG_OPEN | base::File::FLAG_WRITE),
+ file_offset, base::BindOnce([](WTF::Optional<WTF::Time> last_modified) {
+ EXPECT_TRUE(last_modified);
+ }));
+
+ base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ |
+ base::File::FLAG_DELETE_ON_CLOSE);
+ base::File::Info info;
+ EXPECT_TRUE(file.GetInfo(&info));
+ EXPECT_EQ(static_cast<int64_t>(expected_data.size()), info.size);
+
+ Vector<uint8_t> read_data(expected_data.size());
+ EXPECT_EQ(static_cast<int>(expected_data.size()),
+ file.Read(0, reinterpret_cast<char*>(read_data.data()),
+ expected_data.size()));
+ EXPECT_EQ(expected_data, read_data);
+}
+
+const FileTestData file_tests[] = {
+ {0, 128 + 64 + 32}, // The full amount of data.
+ {0, 128 + 64}, // First two chunks.
+ {10, 13}, // Just a subset of the first chunk.
+ {10, 128}, // Parts of both the first and second chunk.
+ {128, 64}, // The entire second chunk.
+ {0, 0}, // Zero bytes from the beginning.
+ {130, 10}, // Just a subset of the second chunk.
+ {140, 0}, // Zero bytes from the middle of the second chunk.
+ {10, 128 + 64}, // Parts of all three chunks.
+};
+
+INSTANTIATE_TEST_CASE_P(BlobBytesProviderTest,
+ RequestAsFile,
+ testing::ValuesIn(file_tests));
+
+TEST_F(BlobBytesProviderTest, RequestAsFile_MultipleChunks) {
+ auto provider = CreateProvider();
+ provider->AppendData(test_data1_);
+ provider->AppendData(test_data2_);
+ provider->AppendData(test_data3_);
+
+ base::FilePath path;
+ base::CreateTemporaryFile(&path);
+
+ Vector<uint8_t> expected_data;
+ for (size_t i = 0; i < combined_bytes_.size(); i += 16) {
+ provider->RequestAsFile(
+ i, 16, base::File(path, base::File::FLAG_OPEN | base::File::FLAG_WRITE),
+ combined_bytes_.size() - i - 16,
+ base::BindOnce([](WTF::Optional<WTF::Time> last_modified) {
+ EXPECT_TRUE(last_modified);
+ }));
+ expected_data.insert(0, combined_bytes_.data() + i, 16);
+ }
+
+ base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ |
+ base::File::FLAG_DELETE_ON_CLOSE);
+ base::File::Info info;
+ EXPECT_TRUE(file.GetInfo(&info));
+ EXPECT_EQ(static_cast<int64_t>(combined_bytes_.size()), info.size);
+
+ Vector<uint8_t> read_data(expected_data.size());
+ EXPECT_EQ(static_cast<int>(expected_data.size()),
+ file.Read(0, reinterpret_cast<char*>(read_data.data()),
+ expected_data.size()));
+ EXPECT_EQ(expected_data, read_data);
+}
+
+TEST_F(BlobBytesProviderTest, RequestAsFile_InvaldFile) {
+ auto provider = CreateProvider(test_data1_);
+
+ provider->RequestAsFile(
+ 0, 16, base::File(), 0,
+ base::BindOnce([](WTF::Optional<WTF::Time> last_modified) {
+ EXPECT_FALSE(last_modified);
+ }));
+}
+
+TEST_F(BlobBytesProviderTest, RequestAsFile_UnwritableFile) {
+ auto provider = CreateProvider(test_data1_);
+
+ base::FilePath path;
+ base::CreateTemporaryFile(&path);
+ provider->RequestAsFile(
+ 0, 16, base::File(path, base::File::FLAG_OPEN | base::File::FLAG_READ), 0,
+ base::BindOnce([](WTF::Optional<WTF::Time> last_modified) {
+ EXPECT_FALSE(last_modified);
+ }));
+
+ base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ |
+ base::File::FLAG_DELETE_ON_CLOSE);
+ base::File::Info info;
+ EXPECT_TRUE(file.GetInfo(&info));
+ EXPECT_EQ(0, info.size);
+}
+
+TEST_F(BlobBytesProviderTest, RequestAsStream) {
+ auto provider = CreateProvider();
+ provider->AppendData(test_data1_);
+ provider->AppendData(test_data2_);
+ provider->AppendData(test_data3_);
+
+ mojo::DataPipe pipe(7);
+ provider->RequestAsStream(std::move(pipe.producer_handle));
+
+ Vector<uint8_t> received_data;
+ base::RunLoop loop;
+ mojo::SimpleWatcher watcher(
+ FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::AUTOMATIC,
+ blink::scheduler::GetSequencedTaskRunnerForTesting());
+ watcher.Watch(
+ pipe.consumer_handle.get(), MOJO_HANDLE_SIGNAL_READABLE,
+ base::BindRepeating(
+ [](mojo::DataPipeConsumerHandle pipe, base::Closure quit_closure,
+ Vector<uint8_t>* bytes_out, MojoResult result) {
+ if (result == MOJO_RESULT_CANCELLED ||
+ result == MOJO_RESULT_FAILED_PRECONDITION) {
+ quit_closure.Run();
+ return;
+ }
+
+ uint32_t num_bytes = 0;
+ MojoResult query_result =
+ pipe.ReadData(nullptr, &num_bytes, MOJO_READ_DATA_FLAG_QUERY);
+ if (query_result == MOJO_RESULT_SHOULD_WAIT)
+ return;
+ EXPECT_EQ(MOJO_RESULT_OK, query_result);
+
+ Vector<uint8_t> bytes(num_bytes);
+ EXPECT_EQ(MOJO_RESULT_OK,
+ pipe.ReadData(bytes.data(), &num_bytes,
+ MOJO_READ_DATA_FLAG_ALL_OR_NONE));
+ bytes_out->AppendVector(bytes);
+ },
+ pipe.consumer_handle.get(), loop.QuitClosure(), &received_data));
+ loop.Run();
+
+ EXPECT_EQ(combined_bytes_, received_data);
+}
+
+} // namespace
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/blob/blob_data.cc b/chromium/third_party/blink/renderer/platform/blob/blob_data.cc
new file mode 100644
index 00000000000..2ba38d77230
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/blob/blob_data.cc
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) 2010 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 "third_party/blink/renderer/platform/blob/blob_data.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/single_thread_task_runner.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "third_party/blink/public/mojom/blob/blob_registry.mojom-blink.h"
+#include "third_party/blink/public/platform/file_path_conversion.h"
+#include "third_party/blink/public/platform/interface_provider.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/blob/blob_bytes_provider.h"
+#include "third_party/blink/renderer/platform/blob/blob_registry.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/histogram.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/text/line_ending.h"
+#include "third_party/blink/renderer/platform/uuid.h"
+#include "third_party/blink/renderer/platform/web_task_runner.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+using mojom::blink::BlobPtr;
+using mojom::blink::BlobPtrInfo;
+using mojom::blink::BlobRegistryPtr;
+using mojom::blink::BytesProviderPtr;
+using mojom::blink::BytesProviderPtrInfo;
+using mojom::blink::BytesProviderRequest;
+using mojom::blink::DataElement;
+using mojom::blink::DataElementBlob;
+using mojom::blink::DataElementPtr;
+using mojom::blink::DataElementBytes;
+using mojom::blink::DataElementBytesPtr;
+using mojom::blink::DataElementFile;
+using mojom::blink::DataElementFilesystemURL;
+
+namespace {
+
+// http://dev.w3.org/2006/webapi/FileAPI/#constructorBlob
+bool IsValidBlobType(const String& type) {
+ for (unsigned i = 0; i < type.length(); ++i) {
+ UChar c = type[i];
+ if (c < 0x20 || c > 0x7E)
+ return false;
+ }
+ return true;
+}
+
+mojom::blink::BlobRegistry* g_blob_registry_for_testing = nullptr;
+
+mojom::blink::BlobRegistry* GetThreadSpecificRegistry() {
+ if (UNLIKELY(g_blob_registry_for_testing))
+ return g_blob_registry_for_testing;
+
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadSpecific<BlobRegistryPtr>, registry,
+ ());
+ if (UNLIKELY(!registry.IsSet())) {
+ // TODO(mek): Going through InterfaceProvider to get a BlobRegistryPtr
+ // ends up going through the main thread. Ideally workers wouldn't need
+ // to do that.
+ Platform::Current()->GetInterfaceProvider()->GetInterface(
+ MakeRequest(&*registry));
+ }
+ return registry->get();
+}
+
+} // namespace
+
+constexpr long long BlobData::kToEndOfFile;
+
+RawData::RawData() = default;
+
+std::unique_ptr<BlobData> BlobData::Create() {
+ return base::WrapUnique(
+ new BlobData(FileCompositionStatus::NO_UNKNOWN_SIZE_FILES));
+}
+
+std::unique_ptr<BlobData> BlobData::CreateForFileWithUnknownSize(
+ const String& path) {
+ std::unique_ptr<BlobData> data = base::WrapUnique(
+ new BlobData(FileCompositionStatus::SINGLE_UNKNOWN_SIZE_FILE));
+ data->elements_.push_back(DataElement::NewFile(DataElementFile::New(
+ WebStringToFilePath(path), 0, BlobData::kToEndOfFile, WTF::Time())));
+ return data;
+}
+
+std::unique_ptr<BlobData> BlobData::CreateForFileWithUnknownSize(
+ const String& path,
+ double expected_modification_time) {
+ std::unique_ptr<BlobData> data = base::WrapUnique(
+ new BlobData(FileCompositionStatus::SINGLE_UNKNOWN_SIZE_FILE));
+ data->elements_.push_back(DataElement::NewFile(DataElementFile::New(
+ WebStringToFilePath(path), 0, BlobData::kToEndOfFile,
+ WTF::Time::FromDoubleT(expected_modification_time))));
+ return data;
+}
+
+std::unique_ptr<BlobData> BlobData::CreateForFileSystemURLWithUnknownSize(
+ const KURL& file_system_url,
+ double expected_modification_time) {
+ std::unique_ptr<BlobData> data = base::WrapUnique(
+ new BlobData(FileCompositionStatus::SINGLE_UNKNOWN_SIZE_FILE));
+ data->elements_.push_back(
+ DataElement::NewFileFilesystem(DataElementFilesystemURL::New(
+ file_system_url, 0, BlobData::kToEndOfFile,
+ WTF::Time::FromDoubleT(expected_modification_time))));
+ return data;
+}
+
+void BlobData::DetachFromCurrentThread() {
+ content_type_ = content_type_.IsolatedCopy();
+ for (auto& element : elements_) {
+ if (element->is_file_filesystem()) {
+ auto& file_element = element->get_file_filesystem();
+ file_element->url = file_element->url.Copy();
+ }
+ }
+}
+
+void BlobData::SetContentType(const String& content_type) {
+ if (IsValidBlobType(content_type))
+ content_type_ = content_type;
+ else
+ content_type_ = "";
+}
+
+void BlobData::AppendData(scoped_refptr<RawData> data) {
+ AppendDataInternal(base::make_span(data->data(), data->length()), data);
+}
+
+void BlobData::AppendFile(const String& path,
+ long long offset,
+ long long length,
+ double expected_modification_time) {
+ DCHECK_EQ(file_composition_, FileCompositionStatus::NO_UNKNOWN_SIZE_FILES)
+ << "Blobs with a unknown-size file cannot have other items.";
+ DCHECK_NE(length, BlobData::kToEndOfFile)
+ << "It is illegal to append file items that have an unknown size. To "
+ "create a blob with a single file with unknown size, use "
+ "BlobData::createForFileWithUnknownSize. Otherwise please provide the "
+ "file size.";
+ // Skip zero-byte items, as they don't matter for the contents of the blob.
+ if (length == 0)
+ return;
+ elements_.push_back(DataElement::NewFile(DataElementFile::New(
+ WebStringToFilePath(path), offset, length,
+ WTF::Time::FromDoubleT(expected_modification_time))));
+}
+
+void BlobData::AppendBlob(scoped_refptr<BlobDataHandle> data_handle,
+ long long offset,
+ long long length) {
+ DCHECK_EQ(file_composition_, FileCompositionStatus::NO_UNKNOWN_SIZE_FILES)
+ << "Blobs with a unknown-size file cannot have other items.";
+ DCHECK(!data_handle->IsSingleUnknownSizeFile())
+ << "It is illegal to append an unknown size file blob.";
+ // Skip zero-byte items, as they don't matter for the contents of the blob.
+ if (length == 0)
+ return;
+ elements_.push_back(DataElement::NewBlob(DataElementBlob::New(
+ data_handle->CloneBlobPtr().PassInterface(), offset, length)));
+}
+
+void BlobData::AppendFileSystemURL(const KURL& url,
+ long long offset,
+ long long length,
+ double expected_modification_time) {
+ DCHECK_EQ(file_composition_, FileCompositionStatus::NO_UNKNOWN_SIZE_FILES)
+ << "Blobs with a unknown-size file cannot have other items.";
+ // Skip zero-byte items, as they don't matter for the contents of the blob.
+ if (length == 0)
+ return;
+ elements_.push_back(
+ DataElement::NewFileFilesystem(DataElementFilesystemURL::New(
+ url, offset, length,
+ WTF::Time::FromDoubleT(expected_modification_time))));
+}
+
+void BlobData::AppendText(const String& text,
+ bool do_normalize_line_endings_to_native) {
+ DCHECK_EQ(file_composition_, FileCompositionStatus::NO_UNKNOWN_SIZE_FILES)
+ << "Blobs with a unknown-size file cannot have other items.";
+ CString utf8_text =
+ UTF8Encoding().Encode(text, WTF::kEntitiesForUnencodables);
+
+ if (do_normalize_line_endings_to_native) {
+ if (utf8_text.length() >
+ BlobBytesProvider::kMaxConsolidatedItemSizeInBytes) {
+ auto raw_data = RawData::Create();
+ NormalizeLineEndingsToNative(utf8_text, *raw_data->MutableData());
+ AppendDataInternal(base::make_span(raw_data->data(), raw_data->length()),
+ raw_data);
+ } else {
+ Vector<char> buffer;
+ NormalizeLineEndingsToNative(utf8_text, buffer);
+ AppendDataInternal(base::make_span(buffer));
+ }
+ } else {
+ AppendDataInternal(base::make_span(utf8_text.data(), utf8_text.length()));
+ }
+}
+
+void BlobData::AppendBytes(const void* bytes, size_t length) {
+ AppendDataInternal(
+ base::make_span(reinterpret_cast<const char*>(bytes), length));
+}
+
+uint64_t BlobData::length() const {
+ uint64_t length = 0;
+
+ for (const auto& element : elements_) {
+ switch (element->which()) {
+ case DataElement::Tag::BYTES:
+ length += element->get_bytes()->length;
+ break;
+ case DataElement::Tag::FILE:
+ length += element->get_file()->length;
+ break;
+ case DataElement::Tag::FILE_FILESYSTEM:
+ length += element->get_file_filesystem()->length;
+ break;
+ case DataElement::Tag::BLOB:
+ length += element->get_blob()->length;
+ break;
+ }
+ }
+ return length;
+}
+
+void BlobData::AppendDataInternal(base::span<const char> data,
+ scoped_refptr<RawData> raw_data) {
+ DCHECK_EQ(file_composition_, FileCompositionStatus::NO_UNKNOWN_SIZE_FILES)
+ << "Blobs with a unknown-size file cannot have other items.";
+ // Skip zero-byte items, as they don't matter for the contents of the blob.
+ if (data.size() == 0)
+ return;
+ bool should_embed_bytes = current_memory_population_ + data.size() <=
+ DataElementBytes::kMaximumEmbeddedDataSize;
+ if (!elements_.IsEmpty() && elements_.back()->is_bytes()) {
+ // Append bytes to previous element.
+ DCHECK(last_bytes_provider_);
+ const auto& bytes_element = elements_.back()->get_bytes();
+ bytes_element->length += data.size();
+ if (should_embed_bytes && bytes_element->embedded_data) {
+ bytes_element->embedded_data->Append(data.data(), data.size());
+ current_memory_population_ += data.size();
+ } else if (bytes_element->embedded_data) {
+ current_memory_population_ -= bytes_element->embedded_data->size();
+ bytes_element->embedded_data = WTF::nullopt;
+ }
+ } else {
+ BytesProviderPtrInfo bytes_provider_info;
+ last_bytes_provider_ =
+ BlobBytesProvider::CreateAndBind(MakeRequest(&bytes_provider_info));
+
+ auto bytes_element = DataElementBytes::New(data.size(), WTF::nullopt,
+ std::move(bytes_provider_info));
+ if (should_embed_bytes) {
+ bytes_element->embedded_data = Vector<uint8_t>();
+ bytes_element->embedded_data->Append(data.data(), data.size());
+ current_memory_population_ += data.size();
+ }
+ elements_.push_back(DataElement::NewBytes(std::move(bytes_element)));
+ }
+ if (raw_data)
+ last_bytes_provider_->AppendData(std::move(raw_data));
+ else
+ last_bytes_provider_->AppendData(std::move(data));
+}
+
+BlobDataHandle::BlobDataHandle()
+ : uuid_(CreateCanonicalUUIDString()),
+ size_(0),
+ is_single_unknown_size_file_(false) {
+ GetThreadSpecificRegistry()->Register(MakeRequest(&blob_info_), uuid_, "", "",
+ {});
+}
+
+BlobDataHandle::BlobDataHandle(std::unique_ptr<BlobData> data, long long size)
+ : uuid_(CreateCanonicalUUIDString()),
+ type_(data->ContentType().IsolatedCopy()),
+ size_(size),
+ is_single_unknown_size_file_(data->IsSingleUnknownSizeFile()) {
+ TRACE_EVENT0("Blob", "Registry::RegisterBlob");
+ SCOPED_BLINK_UMA_HISTOGRAM_TIMER_THREAD_SAFE("Storage.Blob.RegisterBlobTime");
+ GetThreadSpecificRegistry()->Register(MakeRequest(&blob_info_), uuid_,
+ type_.IsNull() ? "" : type_, "",
+ data->ReleaseElements());
+}
+
+BlobDataHandle::BlobDataHandle(const String& uuid,
+ const String& type,
+ long long size)
+ : uuid_(uuid.IsolatedCopy()),
+ type_(IsValidBlobType(type) ? type.IsolatedCopy() : ""),
+ size_(size),
+ is_single_unknown_size_file_(false) {
+ SCOPED_BLINK_UMA_HISTOGRAM_TIMER_THREAD_SAFE(
+ "Storage.Blob.GetBlobFromUUIDTime");
+ GetThreadSpecificRegistry()->GetBlobFromUUID(MakeRequest(&blob_info_), uuid_);
+}
+
+BlobDataHandle::BlobDataHandle(const String& uuid,
+ const String& type,
+ long long size,
+ BlobPtrInfo blob_info)
+ : uuid_(uuid.IsolatedCopy()),
+ type_(IsValidBlobType(type) ? type.IsolatedCopy() : ""),
+ size_(size),
+ is_single_unknown_size_file_(false),
+ blob_info_(std::move(blob_info)) {
+ DCHECK(blob_info_.is_valid());
+}
+
+BlobDataHandle::~BlobDataHandle() {
+}
+
+BlobPtr BlobDataHandle::CloneBlobPtr() {
+ MutexLocker locker(blob_info_mutex_);
+ if (!blob_info_.is_valid())
+ return nullptr;
+ BlobPtr blob, blob_clone;
+ blob.Bind(std::move(blob_info_));
+ blob->Clone(MakeRequest(&blob_clone));
+ blob_info_ = blob.PassInterface();
+ return blob_clone;
+}
+
+void BlobDataHandle::ReadAll(mojo::ScopedDataPipeProducerHandle pipe,
+ mojom::blink::BlobReaderClientPtr client) {
+ MutexLocker locker(blob_info_mutex_);
+ BlobPtr blob;
+ blob.Bind(std::move(blob_info_));
+ blob->ReadAll(std::move(pipe), std::move(client));
+ blob_info_ = blob.PassInterface();
+}
+
+void BlobDataHandle::ReadRange(uint64_t offset,
+ uint64_t length,
+ mojo::ScopedDataPipeProducerHandle pipe,
+ mojom::blink::BlobReaderClientPtr client) {
+ MutexLocker locker(blob_info_mutex_);
+ BlobPtr blob;
+ blob.Bind(std::move(blob_info_));
+ blob->ReadRange(offset, length, std::move(pipe), std::move(client));
+ blob_info_ = blob.PassInterface();
+}
+
+// static
+mojom::blink::BlobRegistry* BlobDataHandle::GetBlobRegistry() {
+ return GetThreadSpecificRegistry();
+}
+
+// static
+void BlobDataHandle::SetBlobRegistryForTesting(
+ mojom::blink::BlobRegistry* registry) {
+ g_blob_registry_for_testing = registry;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/blob/blob_data.h b/chromium/third_party/blink/renderer/platform/blob/blob_data.h
new file mode 100644
index 00000000000..773bcf6c177
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/blob/blob_data.h
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2010 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_BLOB_BLOB_DATA_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BLOB_BLOB_DATA_H_
+
+#include <memory>
+#include "base/gtest_prod_util.h"
+#include "base/thread_annotations.h"
+#include "third_party/blink/public/mojom/blob/blob.mojom-blink.h"
+#include "third_party/blink/public/mojom/blob/data_element.mojom-blink.h"
+#include "third_party/blink/renderer/platform/file_metadata.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
+
+namespace blink {
+namespace mojom {
+namespace blink {
+class BlobRegistry;
+}
+} // namespace mojom
+
+class BlobBytesProvider;
+class BlobDataHandle;
+
+class PLATFORM_EXPORT RawData : public ThreadSafeRefCounted<RawData> {
+ public:
+ static scoped_refptr<RawData> Create() {
+ return base::AdoptRef(new RawData());
+ }
+
+ const char* data() const { return data_.data(); }
+ size_t length() const { return data_.size(); }
+ Vector<char>* MutableData() { return &data_; }
+
+ private:
+ RawData();
+
+ Vector<char> data_;
+};
+
+class PLATFORM_EXPORT BlobData {
+ USING_FAST_MALLOC(BlobData);
+
+ public:
+ static constexpr long long kToEndOfFile = -1;
+
+ static std::unique_ptr<BlobData> Create();
+
+ // Calling append* on objects returned by createFor___WithUnknownSize will
+ // check-fail. The caller can only have an unknown-length file if it is the
+ // only item in the blob.
+ static std::unique_ptr<BlobData> CreateForFileWithUnknownSize(
+ const String& path);
+ static std::unique_ptr<BlobData> CreateForFileWithUnknownSize(
+ const String& path,
+ double expected_modification_time);
+ static std::unique_ptr<BlobData> CreateForFileSystemURLWithUnknownSize(
+ const KURL& file_system_url,
+ double expected_modification_time);
+
+ // Detaches from current thread so that it can be passed to another thread.
+ void DetachFromCurrentThread();
+
+ const String& ContentType() const { return content_type_; }
+ void SetContentType(const String&);
+
+ const Vector<mojom::blink::DataElementPtr>& Elements() const {
+ return elements_;
+ }
+ Vector<mojom::blink::DataElementPtr> ReleaseElements() {
+ return std::move(elements_);
+ }
+
+ void AppendBytes(const void*, size_t length);
+ void AppendData(scoped_refptr<RawData>);
+ void AppendFile(const String& path,
+ long long offset,
+ long long length,
+ double expected_modification_time);
+
+ // The given blob must not be a file with unknown size. Please use the
+ // File::appendTo instead.
+ void AppendBlob(scoped_refptr<BlobDataHandle>,
+ long long offset,
+ long long length);
+ void AppendFileSystemURL(const KURL&,
+ long long offset,
+ long long length,
+ double expected_modification_time);
+ void AppendText(const String&, bool normalize_line_endings_to_native);
+
+ // The value of the size property for a Blob who has this data.
+ // BlobDataItem::toEndOfFile if the Blob has a file whose size was not yet
+ // determined.
+ uint64_t length() const;
+
+ bool IsSingleUnknownSizeFile() const {
+ return file_composition_ == FileCompositionStatus::SINGLE_UNKNOWN_SIZE_FILE;
+ }
+
+ private:
+ enum class FileCompositionStatus {
+ SINGLE_UNKNOWN_SIZE_FILE,
+ NO_UNKNOWN_SIZE_FILES
+ };
+
+ explicit BlobData(FileCompositionStatus composition)
+ : file_composition_(composition) {}
+
+ void AppendDataInternal(base::span<const char> data,
+ scoped_refptr<RawData> = nullptr);
+
+ String content_type_;
+ FileCompositionStatus file_composition_;
+
+ Vector<mojom::blink::DataElementPtr> elements_;
+ size_t current_memory_population_ = 0;
+ BlobBytesProvider* last_bytes_provider_ = nullptr;
+
+ DISALLOW_COPY_AND_ASSIGN(BlobData);
+};
+
+class PLATFORM_EXPORT BlobDataHandle
+ : public ThreadSafeRefCounted<BlobDataHandle> {
+ public:
+ // For empty blob construction.
+ static scoped_refptr<BlobDataHandle> Create() {
+ return base::AdoptRef(new BlobDataHandle());
+ }
+
+ // For initial creation.
+ static scoped_refptr<BlobDataHandle> Create(std::unique_ptr<BlobData> data,
+ long long size) {
+ return base::AdoptRef(new BlobDataHandle(std::move(data), size));
+ }
+
+ // For deserialization of script values and ipc messages.
+ static scoped_refptr<BlobDataHandle> Create(const String& uuid,
+ const String& type,
+ long long size) {
+ return base::AdoptRef(new BlobDataHandle(uuid, type, size));
+ }
+
+ static scoped_refptr<BlobDataHandle> Create(const String& uuid,
+ const String& type,
+ long long size,
+ mojom::blink::BlobPtrInfo blob_info) {
+ if (blob_info.is_valid()) {
+ return base::AdoptRef(
+ new BlobDataHandle(uuid, type, size, std::move(blob_info)));
+ }
+ return base::AdoptRef(new BlobDataHandle(uuid, type, size));
+ }
+
+ String Uuid() const { return uuid_.IsolatedCopy(); }
+ String GetType() const { return type_.IsolatedCopy(); }
+ unsigned long long size() const { return size_; }
+
+ bool IsSingleUnknownSizeFile() const { return is_single_unknown_size_file_; }
+
+ ~BlobDataHandle();
+
+ mojom::blink::BlobPtr CloneBlobPtr();
+
+ void ReadAll(mojo::ScopedDataPipeProducerHandle,
+ mojom::blink::BlobReaderClientPtr);
+ void ReadRange(uint64_t offset,
+ uint64_t length,
+ mojo::ScopedDataPipeProducerHandle,
+ mojom::blink::BlobReaderClientPtr);
+
+ static mojom::blink::BlobRegistry* GetBlobRegistry();
+ static void SetBlobRegistryForTesting(mojom::blink::BlobRegistry*);
+
+ private:
+ BlobDataHandle();
+ BlobDataHandle(std::unique_ptr<BlobData>, long long size);
+ BlobDataHandle(const String& uuid, const String& type, long long size);
+ BlobDataHandle(const String& uuid,
+ const String& type,
+ long long size,
+ mojom::blink::BlobPtrInfo);
+
+ const String uuid_;
+ const String type_;
+ const long long size_;
+ const bool is_single_unknown_size_file_;
+ // This class is supposed to be thread safe. So to be able to use the mojo
+ // Blob interface from multiple threads store a InterfacePtrInfo combined with
+ // a mutex, and make sure any access to the mojo interface is done protected
+ // by the mutex.
+ mojom::blink::BlobPtrInfo blob_info_ GUARDED_BY(blob_info_mutex_);
+ Mutex blob_info_mutex_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BLOB_BLOB_DATA_H_
diff --git a/chromium/third_party/blink/renderer/platform/blob/blob_data_test.cc b/chromium/third_party/blink/renderer/platform/blob/blob_data_test.cc
new file mode 100644
index 00000000000..af5cc3c8089
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/blob/blob_data_test.cc
@@ -0,0 +1,456 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/blob/blob_data.h"
+
+#include <memory>
+#include <utility>
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/mojom/blob/blob_registry.mojom-blink.h"
+#include "third_party/blink/public/platform/file_path_conversion.h"
+#include "third_party/blink/public/platform/interface_provider.h"
+#include "third_party/blink/renderer/platform/blob/blob_bytes_provider.h"
+#include "third_party/blink/renderer/platform/blob/testing/fake_blob_registry.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
+#include "third_party/blink/renderer/platform/uuid.h"
+
+namespace blink {
+
+using mojom::blink::Blob;
+using mojom::blink::BlobPtr;
+using mojom::blink::BlobRegistry;
+using mojom::blink::BlobRegistryPtr;
+using mojom::blink::BlobRegistryRequest;
+using mojom::blink::BlobRequest;
+using mojom::blink::DataElement;
+using mojom::blink::DataElementBlob;
+using mojom::blink::DataElementBytes;
+using mojom::blink::DataElementFile;
+using mojom::blink::DataElementFilesystemURL;
+using mojom::blink::DataElementPtr;
+
+namespace {
+
+struct ExpectedElement {
+ DataElementPtr element;
+ String blob_uuid;
+ Vector<uint8_t> large_data;
+
+ static ExpectedElement EmbeddedBytes(Vector<uint8_t> embedded_data) {
+ uint64_t size = embedded_data.size();
+ return ExpectedElement{DataElement::NewBytes(
+ DataElementBytes::New(size, std::move(embedded_data), nullptr))};
+ }
+
+ static ExpectedElement LargeBytes(Vector<uint8_t> data) {
+ uint64_t size = data.size();
+ return ExpectedElement{DataElement::NewBytes(DataElementBytes::New(
+ size, WTF::nullopt, nullptr)),
+ String(), std::move(data)};
+ }
+
+ static ExpectedElement File(const String& path,
+ uint64_t offset,
+ uint64_t length,
+ WTF::Time time) {
+ return ExpectedElement{DataElement::NewFile(
+ DataElementFile::New(WebStringToFilePath(path), offset, length, time))};
+ }
+
+ static ExpectedElement FileFilesystem(const KURL& url,
+ uint64_t offset,
+ uint64_t length,
+ WTF::Time time) {
+ return ExpectedElement{DataElement::NewFileFilesystem(
+ DataElementFilesystemURL::New(url, offset, length, time))};
+ }
+
+ static ExpectedElement Blob(const String& uuid,
+ uint64_t offset,
+ uint64_t length) {
+ return ExpectedElement{
+ DataElement::NewBlob(DataElementBlob::New(nullptr, offset, length)),
+ uuid};
+ }
+};
+
+} // namespace
+
+class BlobDataHandleTest : public testing::Test {
+ public:
+ BlobDataHandleTest() : blob_registry_binding_(&mock_blob_registry_) {
+ blob_registry_binding_.Bind(MakeRequest(&blob_registry_ptr_));
+ BlobDataHandle::SetBlobRegistryForTesting(blob_registry_ptr_.get());
+ }
+
+ ~BlobDataHandleTest() { BlobDataHandle::SetBlobRegistryForTesting(nullptr); }
+
+ void SetUp() override {
+ small_test_data_.resize(1024);
+ medium_test_data_.resize(1024 * 32);
+ large_test_data_.resize(1024 * 512);
+ for (size_t i = 0; i < small_test_data_.size(); ++i)
+ small_test_data_[i] = i;
+ for (size_t i = 0; i < medium_test_data_.size(); ++i)
+ medium_test_data_[i] = i % 191;
+ for (size_t i = 0; i < large_test_data_.size(); ++i)
+ large_test_data_[i] = i % 251;
+
+ ASSERT_LT(small_test_data_.size(),
+ BlobBytesProvider::kMaxConsolidatedItemSizeInBytes);
+ ASSERT_LT(medium_test_data_.size(),
+ DataElementBytes::kMaximumEmbeddedDataSize);
+ ASSERT_GT(medium_test_data_.size(),
+ BlobBytesProvider::kMaxConsolidatedItemSizeInBytes);
+ ASSERT_GT(large_test_data_.size(),
+ DataElementBytes::kMaximumEmbeddedDataSize);
+
+ empty_blob_ = BlobDataHandle::Create();
+
+ std::unique_ptr<BlobData> test_data = BlobData::Create();
+ test_data->AppendBytes(large_test_data_.data(), large_test_data_.size());
+ test_blob_ =
+ BlobDataHandle::Create(std::move(test_data), large_test_data_.size());
+
+ blob_registry_ptr_.FlushForTesting();
+ ASSERT_EQ(2u, mock_blob_registry_.registrations.size());
+ empty_blob_uuid_ = mock_blob_registry_.registrations[0].uuid;
+ test_blob_uuid_ = mock_blob_registry_.registrations[1].uuid;
+ mock_blob_registry_.registrations.clear();
+ }
+
+ void TestCreateBlob(std::unique_ptr<BlobData> data,
+ Vector<ExpectedElement> expected_elements) {
+ size_t blob_size = data->length();
+ String type = data->ContentType();
+ bool is_single_unknown_size_file = data->IsSingleUnknownSizeFile();
+
+ scoped_refptr<BlobDataHandle> handle =
+ BlobDataHandle::Create(std::move(data), blob_size);
+ EXPECT_EQ(blob_size, handle->size());
+ EXPECT_EQ(type, handle->GetType());
+ EXPECT_EQ(is_single_unknown_size_file, handle->IsSingleUnknownSizeFile());
+
+ blob_registry_ptr_.FlushForTesting();
+ EXPECT_EQ(0u, mock_blob_registry_.binding_requests.size());
+ ASSERT_EQ(1u, mock_blob_registry_.registrations.size());
+ auto& reg = mock_blob_registry_.registrations[0];
+ EXPECT_EQ(handle->Uuid(), reg.uuid);
+ EXPECT_EQ(type.IsNull() ? "" : type, reg.content_type);
+ EXPECT_EQ("", reg.content_disposition);
+ ASSERT_EQ(expected_elements.size(), reg.elements.size());
+ for (size_t i = 0; i < expected_elements.size(); ++i) {
+ const auto& expected = expected_elements[i].element;
+ auto& actual = reg.elements[i];
+ if (expected->is_bytes()) {
+ ASSERT_TRUE(actual->is_bytes());
+ EXPECT_EQ(expected->get_bytes()->length, actual->get_bytes()->length);
+ EXPECT_EQ(expected->get_bytes()->embedded_data,
+ actual->get_bytes()->embedded_data);
+
+ base::RunLoop loop;
+ Vector<uint8_t> received_bytes;
+ mojom::blink::BytesProviderPtr data(
+ std::move(actual->get_bytes()->data));
+ data->RequestAsReply(base::BindOnce(
+ [](base::Closure quit_closure, Vector<uint8_t>* bytes_out,
+ const Vector<uint8_t>& bytes) {
+ *bytes_out = bytes;
+ quit_closure.Run();
+ },
+ loop.QuitClosure(), &received_bytes));
+ loop.Run();
+ if (expected->get_bytes()->embedded_data)
+ EXPECT_EQ(expected->get_bytes()->embedded_data, received_bytes);
+ else
+ EXPECT_EQ(expected_elements[i].large_data, received_bytes);
+ } else if (expected->is_file()) {
+ ASSERT_TRUE(actual->is_file());
+ EXPECT_EQ(expected->get_file()->path, actual->get_file()->path);
+ EXPECT_EQ(expected->get_file()->length, actual->get_file()->length);
+ EXPECT_EQ(expected->get_file()->offset, actual->get_file()->offset);
+ EXPECT_EQ(expected->get_file()->expected_modification_time,
+ actual->get_file()->expected_modification_time);
+ } else if (expected->is_file_filesystem()) {
+ ASSERT_TRUE(actual->is_file_filesystem());
+ EXPECT_EQ(expected->get_file_filesystem()->url,
+ actual->get_file_filesystem()->url);
+ EXPECT_EQ(expected->get_file_filesystem()->length,
+ actual->get_file_filesystem()->length);
+ EXPECT_EQ(expected->get_file_filesystem()->offset,
+ actual->get_file_filesystem()->offset);
+ EXPECT_EQ(expected->get_file_filesystem()->expected_modification_time,
+ actual->get_file_filesystem()->expected_modification_time);
+ } else if (expected->is_blob()) {
+ ASSERT_TRUE(actual->is_blob());
+ EXPECT_EQ(expected->get_blob()->length, actual->get_blob()->length);
+ EXPECT_EQ(expected->get_blob()->offset, actual->get_blob()->offset);
+
+ base::RunLoop loop;
+ String received_uuid;
+ mojom::blink::BlobPtr blob(std::move(actual->get_blob()->blob));
+ blob->GetInternalUUID(base::BindOnce(
+ [](base::Closure quit_closure, String* uuid_out,
+ const String& uuid) {
+ *uuid_out = uuid;
+ quit_closure.Run();
+ },
+ loop.QuitClosure(), &received_uuid));
+ loop.Run();
+ EXPECT_EQ(expected_elements[i].blob_uuid, received_uuid);
+ }
+ }
+ mock_blob_registry_.registrations.clear();
+ }
+
+ protected:
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
+ FakeBlobRegistry mock_blob_registry_;
+ BlobRegistryPtr blob_registry_ptr_;
+ mojo::Binding<BlobRegistry> blob_registry_binding_;
+
+ // Significantly less than BlobData's kMaxConsolidatedItemSizeInBytes.
+ Vector<uint8_t> small_test_data_;
+ // Larger than kMaxConsolidatedItemSizeInBytes, but smaller than
+ // max_data_population.
+ Vector<uint8_t> medium_test_data_;
+ // Larger than max_data_population.
+ Vector<uint8_t> large_test_data_;
+ scoped_refptr<BlobDataHandle> empty_blob_;
+ String empty_blob_uuid_;
+ scoped_refptr<BlobDataHandle> test_blob_;
+ String test_blob_uuid_;
+};
+
+TEST_F(BlobDataHandleTest, CreateEmpty) {
+ scoped_refptr<BlobDataHandle> handle = BlobDataHandle::Create();
+ EXPECT_TRUE(handle->GetType().IsNull());
+ EXPECT_EQ(0u, handle->size());
+ EXPECT_FALSE(handle->IsSingleUnknownSizeFile());
+
+ blob_registry_ptr_.FlushForTesting();
+ EXPECT_EQ(0u, mock_blob_registry_.binding_requests.size());
+ ASSERT_EQ(1u, mock_blob_registry_.registrations.size());
+ const auto& reg = mock_blob_registry_.registrations[0];
+ EXPECT_EQ(handle->Uuid(), reg.uuid);
+ EXPECT_EQ("", reg.content_type);
+ EXPECT_EQ("", reg.content_disposition);
+ EXPECT_EQ(0u, reg.elements.size());
+}
+
+TEST_F(BlobDataHandleTest, CreateFromEmptyData) {
+ String kType = "content/type";
+
+ std::unique_ptr<BlobData> data = BlobData::Create();
+ data->SetContentType(kType);
+
+ TestCreateBlob(std::move(data), {});
+}
+
+TEST_F(BlobDataHandleTest, CreateFromUUID) {
+ String kUuid = CreateCanonicalUUIDString();
+ String kType = "content/type";
+ uint64_t kSize = 1234;
+
+ scoped_refptr<BlobDataHandle> handle =
+ BlobDataHandle::Create(kUuid, kType, kSize);
+ EXPECT_EQ(kUuid, handle->Uuid());
+ EXPECT_EQ(kType, handle->GetType());
+ EXPECT_EQ(kSize, handle->size());
+ EXPECT_FALSE(handle->IsSingleUnknownSizeFile());
+
+ blob_registry_ptr_.FlushForTesting();
+ EXPECT_EQ(0u, mock_blob_registry_.registrations.size());
+ ASSERT_EQ(1u, mock_blob_registry_.binding_requests.size());
+ EXPECT_EQ(kUuid, mock_blob_registry_.binding_requests[0].uuid);
+}
+
+TEST_F(BlobDataHandleTest, CreateFromEmptyElements) {
+ std::unique_ptr<BlobData> data = BlobData::Create();
+ data->AppendBytes(small_test_data_.data(), 0);
+ data->AppendBlob(empty_blob_, 0, 0);
+ data->AppendFile("path", 0, 0, 0.0);
+ data->AppendFileSystemURL(NullURL(), 0, 0, 0.0);
+
+ TestCreateBlob(std::move(data), {});
+}
+
+TEST_F(BlobDataHandleTest, CreateFromSmallBytes) {
+ std::unique_ptr<BlobData> data = BlobData::Create();
+ data->AppendBytes(small_test_data_.data(), small_test_data_.size());
+
+ Vector<ExpectedElement> expected_elements;
+ expected_elements.push_back(ExpectedElement::EmbeddedBytes(small_test_data_));
+
+ TestCreateBlob(std::move(data), std::move(expected_elements));
+}
+
+TEST_F(BlobDataHandleTest, CreateFromLargeBytes) {
+ std::unique_ptr<BlobData> data = BlobData::Create();
+ data->AppendBytes(large_test_data_.data(), large_test_data_.size());
+
+ Vector<ExpectedElement> expected_elements;
+ expected_elements.push_back(ExpectedElement::LargeBytes(large_test_data_));
+
+ TestCreateBlob(std::move(data), std::move(expected_elements));
+}
+
+TEST_F(BlobDataHandleTest, CreateFromMergedBytes) {
+ std::unique_ptr<BlobData> data = BlobData::Create();
+ data->AppendBytes(medium_test_data_.data(), medium_test_data_.size());
+ data->AppendBytes(small_test_data_.data(), small_test_data_.size());
+ EXPECT_EQ(1u, data->Elements().size());
+
+ Vector<uint8_t> expected_data = medium_test_data_;
+ expected_data.AppendVector(small_test_data_);
+
+ Vector<ExpectedElement> expected_elements;
+ expected_elements.push_back(
+ ExpectedElement::EmbeddedBytes(std::move(expected_data)));
+
+ TestCreateBlob(std::move(data), std::move(expected_elements));
+}
+
+TEST_F(BlobDataHandleTest, CreateFromMergedLargeAndSmallBytes) {
+ std::unique_ptr<BlobData> data = BlobData::Create();
+ data->AppendBytes(large_test_data_.data(), large_test_data_.size());
+ data->AppendBytes(small_test_data_.data(), small_test_data_.size());
+ EXPECT_EQ(1u, data->Elements().size());
+
+ Vector<uint8_t> expected_data = large_test_data_;
+ expected_data.AppendVector(small_test_data_);
+
+ Vector<ExpectedElement> expected_elements;
+ expected_elements.push_back(
+ ExpectedElement::LargeBytes(std::move(expected_data)));
+
+ TestCreateBlob(std::move(data), std::move(expected_elements));
+}
+
+TEST_F(BlobDataHandleTest, CreateFromMergedSmallAndLargeBytes) {
+ std::unique_ptr<BlobData> data = BlobData::Create();
+ data->AppendBytes(small_test_data_.data(), small_test_data_.size());
+ data->AppendBytes(large_test_data_.data(), large_test_data_.size());
+ EXPECT_EQ(1u, data->Elements().size());
+
+ Vector<uint8_t> expected_data = small_test_data_;
+ expected_data.AppendVector(large_test_data_);
+
+ Vector<ExpectedElement> expected_elements;
+ expected_elements.push_back(
+ ExpectedElement::LargeBytes(std::move(expected_data)));
+
+ TestCreateBlob(std::move(data), std::move(expected_elements));
+}
+
+TEST_F(BlobDataHandleTest, CreateFromFileAndFileSystemURL) {
+ double timestamp1 = CurrentTime();
+ double timestamp2 = timestamp1 + 1;
+ KURL url(NullURL(), "http://example.com/");
+ std::unique_ptr<BlobData> data = BlobData::Create();
+ data->AppendFile("path", 4, 32, timestamp1);
+ data->AppendFileSystemURL(url, 15, 876, timestamp2);
+
+ Vector<ExpectedElement> expected_elements;
+ expected_elements.push_back(
+ ExpectedElement::File("path", 4, 32, WTF::Time::FromDoubleT(timestamp1)));
+ expected_elements.push_back(ExpectedElement::FileFilesystem(
+ url, 15, 876, WTF::Time::FromDoubleT(timestamp2)));
+
+ TestCreateBlob(std::move(data), std::move(expected_elements));
+}
+
+TEST_F(BlobDataHandleTest, CreateFromFileWithUnknownSize) {
+ Vector<ExpectedElement> expected_elements;
+ expected_elements.push_back(
+ ExpectedElement::File("path", 0, uint64_t(-1), WTF::Time()));
+
+ TestCreateBlob(BlobData::CreateForFileWithUnknownSize("path"),
+ std::move(expected_elements));
+}
+
+TEST_F(BlobDataHandleTest, CreateFromFilesystemFileWithUnknownSize) {
+ double timestamp = CurrentTime();
+ KURL url(NullURL(), "http://example.com/");
+ Vector<ExpectedElement> expected_elements;
+ expected_elements.push_back(ExpectedElement::FileFilesystem(
+ url, 0, uint64_t(-1), WTF::Time::FromDoubleT(timestamp)));
+
+ TestCreateBlob(
+ BlobData::CreateForFileSystemURLWithUnknownSize(url, timestamp),
+ std::move(expected_elements));
+}
+
+TEST_F(BlobDataHandleTest, CreateFromBlob) {
+ std::unique_ptr<BlobData> data = BlobData::Create();
+ data->AppendBlob(test_blob_, 13, 765);
+
+ Vector<ExpectedElement> expected_elements;
+ expected_elements.push_back(ExpectedElement::Blob(test_blob_uuid_, 13, 765));
+
+ TestCreateBlob(std::move(data), std::move(expected_elements));
+}
+
+TEST_F(BlobDataHandleTest, CreateFromBlobsAndBytes) {
+ std::unique_ptr<BlobData> data = BlobData::Create();
+ data->AppendBlob(test_blob_, 10, 10);
+ data->AppendBytes(medium_test_data_.data(), medium_test_data_.size());
+ data->AppendBlob(test_blob_, 0, 0);
+ data->AppendBytes(small_test_data_.data(), small_test_data_.size());
+ data->AppendBlob(test_blob_, 0, 10);
+ data->AppendBytes(large_test_data_.data(), large_test_data_.size());
+
+ Vector<uint8_t> expected_data = medium_test_data_;
+ expected_data.AppendVector(small_test_data_);
+
+ Vector<ExpectedElement> expected_elements;
+ expected_elements.push_back(ExpectedElement::Blob(test_blob_uuid_, 10, 10));
+ expected_elements.push_back(
+ ExpectedElement::EmbeddedBytes(std::move(expected_data)));
+ expected_elements.push_back(ExpectedElement::Blob(test_blob_uuid_, 0, 10));
+ expected_elements.push_back(ExpectedElement::LargeBytes(large_test_data_));
+
+ TestCreateBlob(std::move(data), std::move(expected_elements));
+}
+
+TEST_F(BlobDataHandleTest, CreateFromSmallBytesAfterLargeBytes) {
+ std::unique_ptr<BlobData> data = BlobData::Create();
+ data->AppendBytes(large_test_data_.data(), large_test_data_.size());
+ data->AppendBlob(test_blob_, 0, 10);
+ data->AppendBytes(small_test_data_.data(), small_test_data_.size());
+
+ Vector<ExpectedElement> expected_elements;
+ expected_elements.push_back(ExpectedElement::LargeBytes(large_test_data_));
+ expected_elements.push_back(ExpectedElement::Blob(test_blob_uuid_, 0, 10));
+ expected_elements.push_back(ExpectedElement::EmbeddedBytes(small_test_data_));
+
+ TestCreateBlob(std::move(data), std::move(expected_elements));
+}
+
+TEST_F(BlobDataHandleTest, CreateFromManyMergedBytes) {
+ std::unique_ptr<BlobData> data = BlobData::Create();
+ Vector<uint8_t> merged_data;
+ while (merged_data.size() <= DataElementBytes::kMaximumEmbeddedDataSize) {
+ data->AppendBytes(medium_test_data_.data(), medium_test_data_.size());
+ merged_data.AppendVector(medium_test_data_);
+ }
+ data->AppendBlob(test_blob_, 0, 10);
+ data->AppendBytes(medium_test_data_.data(), medium_test_data_.size());
+
+ Vector<ExpectedElement> expected_elements;
+ expected_elements.push_back(
+ ExpectedElement::LargeBytes(std::move(merged_data)));
+ expected_elements.push_back(ExpectedElement::Blob(test_blob_uuid_, 0, 10));
+ expected_elements.push_back(
+ ExpectedElement::EmbeddedBytes(medium_test_data_));
+
+ TestCreateBlob(std::move(data), std::move(expected_elements));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/blob/blob_registry.cc b/chromium/third_party/blink/renderer/platform/blob/blob_registry.cc
new file mode 100644
index 00000000000..c63445d0487
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/blob/blob_registry.cc
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2010 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 "third_party/blink/renderer/platform/blob/blob_registry.h"
+
+#include <memory>
+#include "base/location.h"
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_blob_registry.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/renderer/platform/blob/blob_data.h"
+#include "third_party/blink/renderer/platform/blob/blob_url.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/threading.h"
+
+namespace blink {
+
+static WebBlobRegistry* GetBlobRegistry() {
+ return Platform::Current()->GetBlobRegistry();
+}
+
+void BlobRegistry::RegisterPublicBlobURL(SecurityOrigin* origin,
+ const KURL& url,
+ scoped_refptr<BlobDataHandle> handle) {
+ GetBlobRegistry()->RegisterPublicBlobURL(url, handle->Uuid());
+}
+
+void BlobRegistry::RevokePublicBlobURL(const KURL& url) {
+ GetBlobRegistry()->RevokePublicBlobURL(url);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/blob/blob_registry.h b/chromium/third_party/blink/renderer/platform/blob/blob_registry.h
new file mode 100644
index 00000000000..56d2fcb049c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/blob/blob_registry.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2010 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_BLOB_BLOB_REGISTRY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BLOB_BLOB_REGISTRY_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+class BlobBytesConsumer;
+class BlobDataHandle;
+class BlobURLRegistry;
+class KURL;
+class SecurityOrigin;
+
+// A bridging class for calling blink::WebBlobRegistry methods.
+class PLATFORM_EXPORT BlobRegistry {
+ STATIC_ONLY(BlobRegistry);
+
+ // Calling methods in this class directly won't work when Blob URL management
+ // is switched to mojo. Instead codew should call PublicURLManager methods to
+ // create/revoke blob URLs.
+ // To avoid new usage of these methods, mark all as private with friends for
+ // existing usage.
+ private:
+ friend class BlobBytesConsumer;
+ friend class BlobURLRegistry;
+
+ // Methods for controlling Blob URLs.
+ static void RegisterPublicBlobURL(SecurityOrigin*,
+ const KURL&,
+ scoped_refptr<BlobDataHandle>);
+ static void RevokePublicBlobURL(const KURL&);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BLOB_BLOB_REGISTRY_H_
diff --git a/chromium/third_party/blink/renderer/platform/blob/blob_url.cc b/chromium/third_party/blink/renderer/platform/blob/blob_url.cc
new file mode 100644
index 00000000000..ac38073aba1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/blob/blob_url.cc
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2010 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 "third_party/blink/renderer/platform/blob/blob_url.h"
+
+#include "third_party/blink/renderer/platform/uuid.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+const char BlobURL::kBlobProtocol[] = "blob";
+
+KURL BlobURL::CreatePublicURL(const SecurityOrigin* security_origin) {
+ DCHECK(security_origin);
+ return CreateBlobURL(security_origin->ToString());
+}
+
+String BlobURL::GetOrigin(const KURL& url) {
+ DCHECK(url.ProtocolIs(kBlobProtocol));
+
+ unsigned start_index = url.PathStart();
+ unsigned end_index = url.PathAfterLastSlash();
+ return url.GetString().Substring(start_index, end_index - start_index - 1);
+}
+
+KURL BlobURL::CreateBlobURL(const String& origin_string) {
+ DCHECK(!origin_string.IsEmpty());
+ String url_string =
+ "blob:" + origin_string + '/' + CreateCanonicalUUIDString();
+ return KURL::CreateIsolated(url_string);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/blob/blob_url.h b/chromium/third_party/blink/renderer/platform/blob/blob_url.h
new file mode 100644
index 00000000000..62906732e1e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/blob/blob_url.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2010 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_BLOB_BLOB_URL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BLOB_BLOB_URL_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+class KURL;
+class SecurityOrigin;
+
+// Public blob URLs are of the form
+// blob:%escaped_origin%/%UUID%
+// The origin of the host page is encoded in the URL value to
+// allow easy lookup of the origin when security checks need to be performed.
+// When loading blobs via ResourceHandle or when reading blobs via FileReader
+// the loader conducts security checks that examine the origin of host page
+// encoded in the blob url.
+class PLATFORM_EXPORT BlobURL {
+ STATIC_ONLY(BlobURL);
+
+ public:
+ static KURL CreatePublicURL(const SecurityOrigin*);
+ static String GetOrigin(const KURL&);
+
+ private:
+ static KURL CreateBlobURL(const String& origin_string);
+ static const char kBlobProtocol[];
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BLOB_BLOB_URL_H_
diff --git a/chromium/third_party/blink/renderer/platform/blob/serialized_blob.typemap b/chromium/third_party/blink/renderer/platform/blob/serialized_blob.typemap
new file mode 100644
index 00000000000..251381df46f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/blob/serialized_blob.typemap
@@ -0,0 +1,14 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//third_party/blink/public/mojom/blob/serialized_blob.mojom"
+public_headers = [ "//third_party/blink/renderer/platform/blob/blob_data.h" ]
+traits_headers = [
+ "//third_party/blink/renderer/platform/blob/serialized_blob_struct_traits.h",
+]
+
+deps = [
+ "//mojo/public/cpp/bindings",
+]
+type_mappings = [ "blink.mojom.SerializedBlob=scoped_refptr<::blink::BlobDataHandle>[nullable_is_same_type]" ]
diff --git a/chromium/third_party/blink/renderer/platform/blob/serialized_blob_struct_traits.cc b/chromium/third_party/blink/renderer/platform/blob/serialized_blob_struct_traits.cc
new file mode 100644
index 00000000000..50bdc957a44
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/blob/serialized_blob_struct_traits.cc
@@ -0,0 +1,23 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/blob/serialized_blob_struct_traits.h"
+
+namespace mojo {
+
+bool StructTraits<blink::mojom::blink::SerializedBlob::DataView,
+ scoped_refptr<blink::BlobDataHandle>>::
+ Read(blink::mojom::blink::SerializedBlob::DataView data,
+ scoped_refptr<blink::BlobDataHandle>* out) {
+ WTF::String uuid;
+ WTF::String type;
+ if (!data.ReadUuid(&uuid) || !data.ReadContentType(&type))
+ return false;
+ *out = blink::BlobDataHandle::Create(
+ uuid, type, data.size(),
+ data.TakeBlob<blink::mojom::blink::BlobPtrInfo>());
+ return true;
+}
+
+} // namespace mojo
diff --git a/chromium/third_party/blink/renderer/platform/blob/serialized_blob_struct_traits.h b/chromium/third_party/blink/renderer/platform/blob/serialized_blob_struct_traits.h
new file mode 100644
index 00000000000..f11b8150f75
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/blob/serialized_blob_struct_traits.h
@@ -0,0 +1,52 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BLOB_SERIALIZED_BLOB_STRUCT_TRAITS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BLOB_SERIALIZED_BLOB_STRUCT_TRAITS_H_
+
+#include "base/memory/ref_counted.h"
+#include "mojo/public/cpp/bindings/string_traits_wtf.h"
+#include "third_party/blink/public/mojom/blob/serialized_blob.mojom-blink.h"
+#include "third_party/blink/renderer/platform/blob/blob_data.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace mojo {
+
+template <>
+struct PLATFORM_EXPORT
+ StructTraits<blink::mojom::blink::SerializedBlob::DataView,
+ scoped_refptr<blink::BlobDataHandle>> {
+ static bool IsNull(const scoped_refptr<blink::BlobDataHandle>& input) {
+ return !input;
+ }
+
+ static void SetToNull(scoped_refptr<blink::BlobDataHandle>* output) {
+ *output = nullptr;
+ }
+
+ static WTF::String uuid(const scoped_refptr<blink::BlobDataHandle>& input) {
+ return input->Uuid();
+ }
+
+ static WTF::String content_type(
+ const scoped_refptr<blink::BlobDataHandle>& input) {
+ return input->GetType().IsNull() ? g_empty_string : input->GetType();
+ }
+
+ static uint64_t size(const scoped_refptr<blink::BlobDataHandle>& input) {
+ return input->size();
+ }
+
+ static blink::mojom::blink::BlobPtr blob(
+ const scoped_refptr<blink::BlobDataHandle>& input) {
+ return input->CloneBlobPtr();
+ }
+
+ static bool Read(blink::mojom::blink::SerializedBlob::DataView,
+ scoped_refptr<blink::BlobDataHandle>* out);
+};
+
+} // namespace mojo
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BLOB_SERIALIZED_BLOB_STRUCT_TRAITS_H_
diff --git a/chromium/third_party/blink/renderer/platform/blob/testing/fake_blob.cc b/chromium/third_party/blink/renderer/platform/blob/testing/fake_blob.cc
new file mode 100644
index 00000000000..096cae3fad3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/blob/testing/fake_blob.cc
@@ -0,0 +1,43 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/blob/testing/fake_blob.h"
+
+#include "mojo/public/cpp/bindings/strong_binding.h"
+
+namespace blink {
+
+FakeBlob::FakeBlob(const String& uuid) : uuid_(uuid) {}
+
+void FakeBlob::Clone(mojom::blink::BlobRequest request) {
+ mojo::MakeStrongBinding(std::make_unique<FakeBlob>(uuid_),
+ std::move(request));
+}
+
+void FakeBlob::AsDataPipeGetter(
+ network::mojom::blink::DataPipeGetterRequest request) {
+ NOTREACHED();
+}
+
+void FakeBlob::ReadRange(uint64_t offset,
+ uint64_t length,
+ mojo::ScopedDataPipeProducerHandle,
+ mojom::blink::BlobReaderClientPtr) {
+ NOTREACHED();
+}
+
+void FakeBlob::ReadAll(mojo::ScopedDataPipeProducerHandle,
+ mojom::blink::BlobReaderClientPtr) {
+ NOTREACHED();
+}
+
+void FakeBlob::ReadSideData(ReadSideDataCallback callback) {
+ NOTREACHED();
+}
+
+void FakeBlob::GetInternalUUID(GetInternalUUIDCallback callback) {
+ std::move(callback).Run(uuid_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/blob/testing/fake_blob.h b/chromium/third_party/blink/renderer/platform/blob/testing/fake_blob.h
new file mode 100644
index 00000000000..447645fabe0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/blob/testing/fake_blob.h
@@ -0,0 +1,35 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BLOB_TESTING_FAKE_BLOB_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BLOB_TESTING_FAKE_BLOB_H_
+
+#include "third_party/blink/public/mojom/blob/blob.mojom-blink.h"
+
+namespace blink {
+
+// Mocked Blob implementation for testing. You can't read from a FakeBlob, but
+// it does have a UUID.
+class FakeBlob : public mojom::blink::Blob {
+ public:
+ explicit FakeBlob(const String& uuid);
+
+ void Clone(mojom::blink::BlobRequest) override;
+ void AsDataPipeGetter(network::mojom::blink::DataPipeGetterRequest) override;
+ void ReadRange(uint64_t offset,
+ uint64_t length,
+ mojo::ScopedDataPipeProducerHandle,
+ mojom::blink::BlobReaderClientPtr) override;
+ void ReadAll(mojo::ScopedDataPipeProducerHandle,
+ mojom::blink::BlobReaderClientPtr) override;
+ void ReadSideData(ReadSideDataCallback) override;
+ void GetInternalUUID(GetInternalUUIDCallback) override;
+
+ private:
+ String uuid_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BLOB_TESTING_FAKE_BLOB_H_
diff --git a/chromium/third_party/blink/renderer/platform/blob/testing/fake_blob_registry.cc b/chromium/third_party/blink/renderer/platform/blob/testing/fake_blob_registry.cc
new file mode 100644
index 00000000000..d85a2d6b8e3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/blob/testing/fake_blob_registry.cc
@@ -0,0 +1,48 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/blob/testing/fake_blob_registry.h"
+
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "third_party/blink/renderer/platform/blob/testing/fake_blob.h"
+
+namespace blink {
+
+void FakeBlobRegistry::Register(mojom::blink::BlobRequest blob,
+ const String& uuid,
+ const String& content_type,
+ const String& content_disposition,
+ Vector<mojom::blink::DataElementPtr> elements,
+ RegisterCallback callback) {
+ registrations.push_back(Registration{uuid, content_type, content_disposition,
+ std::move(elements)});
+ mojo::MakeStrongBinding(std::make_unique<FakeBlob>(uuid), std::move(blob));
+ std::move(callback).Run();
+}
+
+void FakeBlobRegistry::RegisterFromStream(
+ const String& content_type,
+ const String& content_disposition,
+ uint64_t expected_length,
+ mojo::ScopedDataPipeConsumerHandle data,
+ mojom::blink::ProgressClientAssociatedPtrInfo,
+ RegisterFromStreamCallback) {
+ NOTREACHED();
+}
+
+void FakeBlobRegistry::GetBlobFromUUID(mojom::blink::BlobRequest blob,
+ const String& uuid,
+ GetBlobFromUUIDCallback callback) {
+ binding_requests.push_back(BindingRequest{uuid});
+ mojo::MakeStrongBinding(std::make_unique<FakeBlob>(uuid), std::move(blob));
+ std::move(callback).Run();
+}
+
+void FakeBlobRegistry::URLStoreForOrigin(
+ const scoped_refptr<const SecurityOrigin>& origin,
+ mojom::blink::BlobURLStoreAssociatedRequest request) {
+ NOTREACHED();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/blob/testing/fake_blob_registry.h b/chromium/third_party/blink/renderer/platform/blob/testing/fake_blob_registry.h
new file mode 100644
index 00000000000..210a4fc5696
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/blob/testing/fake_blob_registry.h
@@ -0,0 +1,54 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BLOB_TESTING_FAKE_BLOB_REGISTRY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BLOB_TESTING_FAKE_BLOB_REGISTRY_H_
+
+#include "third_party/blink/public/mojom/blob/blob_registry.mojom-blink.h"
+
+namespace blink {
+
+// Mocked BlobRegistry implementation for testing. Simply keeps track of all
+// blob registrations and blob lookup requests, binding each blob request to a
+// FakeBlob instance with the correct uuid.
+class FakeBlobRegistry : public mojom::blink::BlobRegistry {
+ public:
+ void Register(mojom::blink::BlobRequest,
+ const String& uuid,
+ const String& content_type,
+ const String& content_disposition,
+ Vector<mojom::blink::DataElementPtr> elements,
+ RegisterCallback) override;
+
+ void RegisterFromStream(const String& content_type,
+ const String& content_disposition,
+ uint64_t expected_length,
+ mojo::ScopedDataPipeConsumerHandle,
+ mojom::blink::ProgressClientAssociatedPtrInfo,
+ RegisterFromStreamCallback) override;
+
+ void GetBlobFromUUID(mojom::blink::BlobRequest,
+ const String& uuid,
+ GetBlobFromUUIDCallback) override;
+
+ void URLStoreForOrigin(const scoped_refptr<const SecurityOrigin>&,
+ mojom::blink::BlobURLStoreAssociatedRequest) override;
+
+ struct Registration {
+ String uuid;
+ String content_type;
+ String content_disposition;
+ Vector<mojom::blink::DataElementPtr> elements;
+ };
+ Vector<Registration> registrations;
+
+ struct BindingRequest {
+ String uuid;
+ };
+ Vector<BindingRequest> binding_requests;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BLOB_TESTING_FAKE_BLOB_REGISTRY_H_
diff --git a/chromium/third_party/blink/renderer/platform/blob/testing/fake_blob_url_store.cc b/chromium/third_party/blink/renderer/platform/blob/testing/fake_blob_url_store.cc
new file mode 100644
index 00000000000..0063695609c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/blob/testing/fake_blob_url_store.cc
@@ -0,0 +1,43 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/blob/testing/fake_blob_url_store.h"
+
+namespace blink {
+
+void FakeBlobURLStore::Register(mojom::blink::BlobPtr blob,
+ const KURL& url,
+ RegisterCallback callback) {
+ registrations.insert(url, std::move(blob));
+ std::move(callback).Run();
+}
+
+void FakeBlobURLStore::Revoke(const KURL& url) {
+ registrations.erase(url);
+ revocations.push_back(url);
+}
+
+void FakeBlobURLStore::Resolve(const KURL& url, ResolveCallback callback) {
+ auto it = registrations.find(url);
+ if (it == registrations.end()) {
+ std::move(callback).Run(nullptr);
+ return;
+ }
+ mojom::blink::BlobPtr blob;
+ it->value->Clone(MakeRequest(&blob));
+ std::move(callback).Run(std::move(blob));
+}
+
+void FakeBlobURLStore::ResolveAsURLLoaderFactory(
+ const KURL&,
+ network::mojom::blink::URLLoaderFactoryRequest) {
+ NOTREACHED();
+}
+
+void FakeBlobURLStore::ResolveForNavigation(const KURL&,
+ mojom::blink::BlobURLTokenRequest) {
+ NOTREACHED();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/blob/testing/fake_blob_url_store.h b/chromium/third_party/blink/renderer/platform/blob/testing/fake_blob_url_store.h
new file mode 100644
index 00000000000..68c1e7c1e01
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/blob/testing/fake_blob_url_store.h
@@ -0,0 +1,35 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BLOB_TESTING_FAKE_BLOB_URL_STORE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BLOB_TESTING_FAKE_BLOB_URL_STORE_H_
+
+#include "third_party/blink/public/mojom/blob/blob_url_store.mojom-blink.h"
+
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl_hash.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+// Mocked BlobURLStore implementation for testing.
+class FakeBlobURLStore : public mojom::blink::BlobURLStore {
+ public:
+ void Register(mojom::blink::BlobPtr, const KURL&, RegisterCallback) override;
+ void Revoke(const KURL&) override;
+ void Resolve(const KURL&, ResolveCallback) override;
+ void ResolveAsURLLoaderFactory(
+ const KURL&,
+ network::mojom::blink::URLLoaderFactoryRequest) override;
+ void ResolveForNavigation(const KURL&,
+ mojom::blink::BlobURLTokenRequest) override;
+
+ HashMap<KURL, mojom::blink::BlobPtr> registrations;
+ Vector<KURL> revocations;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BLOB_TESTING_FAKE_BLOB_URL_STORE_H_
diff --git a/chromium/third_party/blink/renderer/platform/calculation_value.h b/chromium/third_party/blink/renderer/platform/calculation_value.h
new file mode 100644
index 00000000000..060dfe71642
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/calculation_value.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_CALCULATION_VALUE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_CALCULATION_VALUE_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/length.h"
+#include "third_party/blink/renderer/platform/length_functions.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT CalculationValue : public RefCounted<CalculationValue> {
+ public:
+ static scoped_refptr<CalculationValue> Create(PixelsAndPercent value,
+ ValueRange range) {
+ return base::AdoptRef(new CalculationValue(value, range));
+ }
+
+ float Evaluate(float max_value) const {
+ float value = Pixels() + Percent() / 100 * max_value;
+ return (IsNonNegative() && value < 0) ? 0 : value;
+ }
+ bool operator==(const CalculationValue& o) const {
+ return Pixels() == o.Pixels() && Percent() == o.Percent();
+ }
+ bool IsNonNegative() const { return is_non_negative_; }
+ ValueRange GetValueRange() const {
+ return is_non_negative_ ? kValueRangeNonNegative : kValueRangeAll;
+ }
+ float Pixels() const { return value_.pixels; }
+ float Percent() const { return value_.percent; }
+ PixelsAndPercent GetPixelsAndPercent() const { return value_; }
+
+ private:
+ CalculationValue(PixelsAndPercent value, ValueRange range)
+ : value_(value), is_non_negative_(range == kValueRangeNonNegative) {}
+
+ PixelsAndPercent value_;
+ bool is_non_negative_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_CALCULATION_VALUE_H_
diff --git a/chromium/third_party/blink/renderer/platform/clipboard/clipboard_mime_types.cc b/chromium/third_party/blink/renderer/platform/clipboard/clipboard_mime_types.cc
new file mode 100644
index 00000000000..5234818fa0b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/clipboard/clipboard_mime_types.cc
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2010 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 "third_party/blink/renderer/platform/clipboard/clipboard_mime_types.h"
+
+namespace blink {
+
+const char kMimeTypeText[] = "text";
+const char kMimeTypeTextPlain[] = "text/plain";
+const char kMimeTypeTextPlainEtc[] = "text/plain;";
+const char kMimeTypeTextHTML[] = "text/html";
+const char kMimeTypeTextRTF[] = "text/rtf";
+const char kMimeTypeURL[] = "url";
+const char kMimeTypeTextURIList[] = "text/uri-list";
+const char kMimeTypeDownloadURL[] = "downloadurl";
+const char kMimeTypeFiles[] = "Files";
+const char kMimeTypeImagePng[] = "image/png";
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/clipboard/clipboard_mime_types.h b/chromium/third_party/blink/renderer/platform/clipboard/clipboard_mime_types.h
new file mode 100644
index 00000000000..b9e29c94f81
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/clipboard/clipboard_mime_types.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2010 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_CLIPBOARD_CLIPBOARD_MIME_TYPES_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_CLIPBOARD_CLIPBOARD_MIME_TYPES_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+
+PLATFORM_EXPORT extern const char kMimeTypeText[];
+PLATFORM_EXPORT extern const char kMimeTypeTextPlain[];
+PLATFORM_EXPORT extern const char kMimeTypeTextPlainEtc[];
+PLATFORM_EXPORT extern const char kMimeTypeTextHTML[];
+PLATFORM_EXPORT extern const char kMimeTypeTextRTF[];
+PLATFORM_EXPORT extern const char kMimeTypeURL[];
+PLATFORM_EXPORT extern const char kMimeTypeTextURIList[];
+PLATFORM_EXPORT extern const char kMimeTypeDownloadURL[];
+PLATFORM_EXPORT extern const char kMimeTypeFiles[];
+PLATFORM_EXPORT extern const char kMimeTypeImagePng[];
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/clipboard/clipboard_utilities.cc b/chromium/third_party/blink/renderer/platform/clipboard/clipboard_utilities.cc
new file mode 100644
index 00000000000..fab14200560
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/clipboard/clipboard_utilities.cc
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2008, 2009, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/renderer/platform/clipboard/clipboard_utilities.h"
+
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+void ReplaceNBSPWithSpace(String& str) {
+ static const UChar kNonBreakingSpaceCharacter = 0xA0;
+ static const UChar kSpaceCharacter = ' ';
+ str.Replace(kNonBreakingSpaceCharacter, kSpaceCharacter);
+}
+
+String ConvertURIListToURL(const String& uri_list) {
+ Vector<String> items;
+ // Line separator is \r\n per RFC 2483 - however, for compatibility
+ // reasons we allow just \n here.
+ uri_list.Split('\n', items);
+ // Process the input and return the first valid URL. In case no URLs can
+ // be found, return an empty string. This is in line with the HTML5 spec.
+ for (size_t i = 0; i < items.size(); ++i) {
+ String& line = items[i];
+ line = line.StripWhiteSpace();
+ if (line.IsEmpty())
+ continue;
+ if (line[0] == '#')
+ continue;
+ KURL url = KURL(line);
+ if (url.IsValid())
+ return url;
+ }
+ return String();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/clipboard/clipboard_utilities.h b/chromium/third_party/blink/renderer/platform/clipboard/clipboard_utilities.h
new file mode 100644
index 00000000000..fafd6dd44b8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/clipboard/clipboard_utilities.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2008, 2009, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_CLIPBOARD_CLIPBOARD_UTILITIES_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_CLIPBOARD_CLIPBOARD_UTILITIES_H_
+
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+#if defined(OS_WIN)
+PLATFORM_EXPORT void ReplaceNewlinesWithWindowsStyleNewlines(String&);
+#endif
+PLATFORM_EXPORT void ReplaceNBSPWithSpace(String&);
+PLATFORM_EXPORT String ConvertURIListToURL(const String& uri_list);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_CLIPBOARD_CLIPBOARD_UTILITIES_H_
diff --git a/chromium/third_party/blink/renderer/platform/clipboard/clipboard_utilities_win.cc b/chromium/third_party/blink/renderer/platform/clipboard/clipboard_utilities_win.cc
new file mode 100644
index 00000000000..88ae9d6e9e8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/clipboard/clipboard_utilities_win.cc
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2009 Google Inc.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/clipboard/clipboard_utilities.h"
+
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+#include <shlwapi.h>
+
+namespace blink {
+
+void ReplaceNewlinesWithWindowsStyleNewlines(String& str) {
+ DEFINE_STATIC_LOCAL(String, windows_newline, ("\r\n"));
+ StringBuilder result;
+ for (unsigned index = 0; index < str.length(); ++index) {
+ if (str[index] != '\n' || (index > 0 && str[index - 1] == '\r'))
+ result.Append(str[index]);
+ else
+ result.Append(windows_newline);
+ }
+ str = result.ToString();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/color_data.gperf b/chromium/third_party/blink/renderer/platform/color_data.gperf
new file mode 100644
index 00000000000..e47326a7311
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/color_data.gperf
@@ -0,0 +1,177 @@
+%{
+#include "third_party/blink/renderer/platform/graphics/color.h"
+#include <string.h>
+
+namespace blink {
+
+%}
+%struct-type
+struct NamedColor;
+%omit-struct-type
+%language=C++
+%readonly-tables
+%global-table
+%compare-strncmp
+%define class-name ColorDataHash
+%define lookup-function-name findColorImpl
+%define hash-function-name colordata_hash_function
+%enum
+%%
+aliceblue, 0xfff0f8ff
+antiquewhite, 0xfffaebd7
+aqua, 0xff00ffff
+aquamarine, 0xff7fffd4
+azure, 0xfff0ffff
+beige, 0xfff5f5dc
+bisque, 0xffffe4c4
+black, 0xff000000
+blanchedalmond, 0xffffebcd
+blue, 0xff0000ff
+blueviolet, 0xff8a2be2
+brown, 0xffa52a2a
+burlywood, 0xffdeb887
+cadetblue, 0xff5f9ea0
+chartreuse, 0xff7fff00
+chocolate, 0xffd2691e
+coral, 0xffff7f50
+cornflowerblue, 0xff6495ed
+cornsilk, 0xfffff8dc
+crimson, 0xffdc143c
+cyan, 0xff00ffff
+darkblue, 0xff00008b
+darkcyan, 0xff008b8b
+darkgoldenrod, 0xffb8860b
+darkgray, 0xffa9a9a9
+darkgrey, 0xffa9a9a9
+darkgreen, 0xff006400
+darkkhaki, 0xffbdb76b
+darkmagenta, 0xff8b008b
+darkolivegreen, 0xff556b2f
+darkorange, 0xffff8c00
+darkorchid, 0xff9932cc
+darkred, 0xff8b0000
+darksalmon, 0xffe9967a
+darkseagreen, 0xff8fbc8f
+darkslateblue, 0xff483d8b
+darkslategray, 0xff2f4f4f
+darkslategrey, 0xff2f4f4f
+darkturquoise, 0xff00ced1
+darkviolet, 0xff9400d3
+deeppink, 0xffff1493
+deepskyblue, 0xff00bfff
+dimgray, 0xff696969
+dimgrey, 0xff696969
+dodgerblue, 0xff1e90ff
+firebrick, 0xffb22222
+floralwhite, 0xfffffaf0
+forestgreen, 0xff228b22
+fuchsia, 0xffff00ff
+gainsboro, 0xffdcdcdc
+ghostwhite, 0xfff8f8ff
+gold, 0xffffd700
+goldenrod, 0xffdaa520
+gray, 0xff808080
+grey, 0xff808080
+green, 0xff008000
+greenyellow, 0xffadff2f
+honeydew, 0xfff0fff0
+hotpink, 0xffff69b4
+indianred, 0xffcd5c5c
+indigo, 0xff4b0082
+ivory, 0xfffffff0
+khaki, 0xfff0e68c
+lavender, 0xffe6e6fa
+lavenderblush, 0xfffff0f5
+lawngreen, 0xff7cfc00
+lemonchiffon, 0xfffffacd
+lightblue, 0xffadd8e6
+lightcoral, 0xfff08080
+lightcyan, 0xffe0ffff
+lightgoldenrodyellow, 0xfffafad2
+lightgray, 0xffd3d3d3
+lightgrey, 0xffd3d3d3
+lightgreen, 0xff90ee90
+lightpink, 0xffffb6c1
+lightsalmon, 0xffffa07a
+lightseagreen, 0xff20b2aa
+lightskyblue, 0xff87cefa
+lightslateblue, 0xff8470ff
+lightslategray, 0xff778899
+lightslategrey, 0xff778899
+lightsteelblue, 0xffb0c4de
+lightyellow, 0xffffffe0
+lime, 0xff00ff00
+limegreen, 0xff32cd32
+linen, 0xfffaf0e6
+magenta, 0xffff00ff
+maroon, 0xff800000
+mediumaquamarine, 0xff66cdaa
+mediumblue, 0xff0000cd
+mediumorchid, 0xffba55d3
+mediumpurple, 0xff9370db
+mediumseagreen, 0xff3cb371
+mediumslateblue, 0xff7b68ee
+mediumspringgreen, 0xff00fa9a
+mediumturquoise, 0xff48d1cc
+mediumvioletred, 0xffc71585
+midnightblue, 0xff191970
+mintcream, 0xfff5fffa
+mistyrose, 0xffffe4e1
+moccasin, 0xffffe4b5
+navajowhite, 0xffffdead
+navy, 0xff000080
+oldlace, 0xfffdf5e6
+olive, 0xff808000
+olivedrab, 0xff6b8e23
+orange, 0xffffa500
+orangered, 0xffff4500
+orchid, 0xffda70d6
+palegoldenrod, 0xffeee8aa
+palegreen, 0xff98fb98
+paleturquoise, 0xffafeeee
+palevioletred, 0xffdb7093
+papayawhip, 0xffffefd5
+peachpuff, 0xffffdab9
+peru, 0xffcd853f
+pink, 0xffffc0cb
+plum, 0xffdda0dd
+powderblue, 0xffb0e0e6
+purple, 0xff800080
+rebeccapurple, 0xff663399
+red, 0xffff0000
+rosybrown, 0xffbc8f8f
+royalblue, 0xff4169e1
+saddlebrown, 0xff8b4513
+salmon, 0xfffa8072
+sandybrown, 0xfff4a460
+seagreen, 0xff2e8b57
+seashell, 0xfffff5ee
+sienna, 0xffa0522d
+silver, 0xffc0c0c0
+skyblue, 0xff87ceeb
+slateblue, 0xff6a5acd
+slategray, 0xff708090
+slategrey, 0xff708090
+snow, 0xfffffafa
+springgreen, 0xff00ff7f
+steelblue, 0xff4682b4
+tan, 0xffd2b48c
+teal, 0xff008080
+thistle, 0xffd8bfd8
+tomato, 0xffff6347
+transparent, 0x00000000
+turquoise, 0xff40e0d0
+violet, 0xffee82ee
+violetred, 0xffd02090
+wheat, 0xfff5deb3
+white, 0xffffffff
+whitesmoke, 0xfff5f5f5
+yellow, 0xffffff00
+yellowgreen, 0xff9acd32
+%%
+
+const struct NamedColor* FindColor(const char* str, unsigned len) {
+ return ColorDataHash::findColorImpl(str, len);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/content_decryption_module_result.h b/chromium/third_party/blink/renderer/platform/content_decryption_module_result.h
new file mode 100644
index 00000000000..bdab63b84ce
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/content_decryption_module_result.h
@@ -0,0 +1,44 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_CONTENT_DECRYPTION_MODULE_RESULT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_CONTENT_DECRYPTION_MODULE_RESULT_H_
+
+#include "third_party/blink/public/platform/web_content_decryption_module_exception.h"
+#include "third_party/blink/public/platform/web_content_decryption_module_result.h"
+#include "third_party/blink/public/platform/web_encrypted_media_key_information.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+
+namespace blink {
+
+class WebContentDecryptionModule;
+class WebString;
+
+// Used to notify completion of a CDM operation.
+class ContentDecryptionModuleResult
+ : public GarbageCollectedFinalized<ContentDecryptionModuleResult> {
+ public:
+ virtual ~ContentDecryptionModuleResult() = default;
+
+ virtual void Complete() = 0;
+ virtual void CompleteWithContentDecryptionModule(
+ WebContentDecryptionModule*) = 0;
+ virtual void CompleteWithSession(
+ WebContentDecryptionModuleResult::SessionStatus) = 0;
+ virtual void CompleteWithKeyStatus(
+ WebEncryptedMediaKeyInformation::KeyStatus) = 0;
+ virtual void CompleteWithError(WebContentDecryptionModuleException,
+ unsigned long system_code,
+ const WebString&) = 0;
+
+ WebContentDecryptionModuleResult Result() {
+ return WebContentDecryptionModuleResult(this);
+ }
+
+ virtual void Trace(blink::Visitor* visitor) {}
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_CONTENT_DECRYPTION_MODULE_RESULT_H_
diff --git a/chromium/third_party/blink/renderer/platform/content_setting_callbacks.cc b/chromium/third_party/blink/renderer/platform/content_setting_callbacks.cc
new file mode 100644
index 00000000000..5430d8972ca
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/content_setting_callbacks.cc
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014 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 "third_party/blink/renderer/platform/content_setting_callbacks.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+
+namespace blink {
+
+std::unique_ptr<ContentSettingCallbacks> ContentSettingCallbacks::Create(
+ base::OnceClosure allowed,
+ base::OnceClosure denied) {
+ return base::WrapUnique(
+ new ContentSettingCallbacks(std::move(allowed), std::move(denied)));
+}
+
+ContentSettingCallbacks::ContentSettingCallbacks(base::OnceClosure allowed,
+ base::OnceClosure denied)
+ : allowed_(std::move(allowed)), denied_(std::move(denied)) {}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/content_setting_callbacks.h b/chromium/third_party/blink/renderer/platform/content_setting_callbacks.h
new file mode 100644
index 00000000000..05bacc5ab79
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/content_setting_callbacks.h
@@ -0,0 +1,39 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_CONTENT_SETTING_CALLBACKS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_CONTENT_SETTING_CALLBACKS_H_
+
+#include <memory>
+#include "base/callback.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT ContentSettingCallbacks {
+ USING_FAST_MALLOC(ContentSettingCallbacks);
+ WTF_MAKE_NONCOPYABLE(ContentSettingCallbacks);
+
+ public:
+ static std::unique_ptr<ContentSettingCallbacks> Create(
+ base::OnceClosure allowed,
+ base::OnceClosure denied);
+ virtual ~ContentSettingCallbacks() = default;
+
+ void OnAllowed() { std::move(allowed_).Run(); }
+ void OnDenied() { std::move(denied_).Run(); }
+
+ private:
+ ContentSettingCallbacks(base::OnceClosure allowed, base::OnceClosure denied);
+
+ base::OnceClosure allowed_;
+ base::OnceClosure denied_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_CONTENT_SETTING_CALLBACKS_H_
diff --git a/chromium/third_party/blink/renderer/platform/context_menu.cc b/chromium/third_party/blink/renderer/platform/context_menu.cc
new file mode 100644
index 00000000000..689525b9284
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/context_menu.cc
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2010 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/context_menu.h"
+
+namespace blink {
+
+static const ContextMenuItem* FindItemWithAction(
+ unsigned action,
+ const Vector<ContextMenuItem>& items) {
+ for (size_t i = 0; i < items.size(); ++i) {
+ const ContextMenuItem& item = items[i];
+ if (item.Action() == static_cast<ContextMenuAction>(action))
+ return &item;
+ if (item.GetType() != kSubmenuType)
+ continue;
+ if (const ContextMenuItem* sub_menu_item =
+ FindItemWithAction(action, item.SubMenuItems()))
+ return sub_menu_item;
+ }
+
+ return nullptr;
+}
+
+const ContextMenuItem* ContextMenu::ItemWithAction(unsigned action) const {
+ return FindItemWithAction(action, items_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/context_menu.h b/chromium/third_party/blink/renderer/platform/context_menu.h
new file mode 100644
index 00000000000..a591bff7888
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/context_menu.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2006 Apple Computer, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_CONTEXT_MENU_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_CONTEXT_MENU_H_
+
+#include "third_party/blink/renderer/platform/context_menu_item.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT ContextMenu {
+ WTF_MAKE_NONCOPYABLE(ContextMenu);
+ USING_FAST_MALLOC(ContextMenu);
+
+ public:
+ ContextMenu() = default;
+ const ContextMenuItem* ItemWithAction(unsigned) const;
+ const Vector<ContextMenuItem>& Items() const { return items_; }
+ void AppendItem(const ContextMenuItem& item) { items_.push_back(item); }
+ void RemoveLastItem() { items_.pop_back(); }
+
+ private:
+ Vector<ContextMenuItem> items_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_CONTEXT_MENU_H_
diff --git a/chromium/third_party/blink/renderer/platform/context_menu_item.cc b/chromium/third_party/blink/renderer/platform/context_menu_item.cc
new file mode 100644
index 00000000000..9bc41ac2303
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/context_menu_item.cc
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2010 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/context_menu_item.h"
+
+#include "third_party/blink/renderer/platform/context_menu.h"
+
+namespace blink {
+
+ContextMenuItem::ContextMenuItem(ContextMenuItemType type,
+ ContextMenuAction action,
+ const String& title,
+ ContextMenu* sub_menu)
+ : type_(type),
+ action_(action),
+ title_(title),
+ enabled_(true),
+ checked_(false) {
+ if (sub_menu)
+ SetSubMenu(sub_menu);
+}
+
+ContextMenuItem::ContextMenuItem(ContextMenuItemType type,
+ ContextMenuAction action,
+ const String& title,
+ bool enabled,
+ bool checked)
+ : type_(type),
+ action_(action),
+ title_(title),
+ enabled_(enabled),
+ checked_(checked) {}
+
+ContextMenuItem::ContextMenuItem(ContextMenuAction action,
+ const String& title,
+ bool enabled,
+ bool checked,
+ const Vector<ContextMenuItem>& sub_menu_items)
+ : type_(kSubmenuType),
+ action_(action),
+ title_(title),
+ enabled_(enabled),
+ checked_(checked),
+ sub_menu_items_(sub_menu_items) {}
+
+ContextMenuItem::~ContextMenuItem() = default;
+
+void ContextMenuItem::SetSubMenu(ContextMenu* sub_menu) {
+ if (sub_menu) {
+ type_ = kSubmenuType;
+ sub_menu_items_ = sub_menu->Items();
+ } else {
+ type_ = kActionType;
+ sub_menu_items_.clear();
+ }
+}
+
+void ContextMenuItem::SetType(ContextMenuItemType type) {
+ type_ = type;
+}
+
+ContextMenuItemType ContextMenuItem::GetType() const {
+ return type_;
+}
+
+void ContextMenuItem::SetAction(ContextMenuAction action) {
+ action_ = action;
+}
+
+ContextMenuAction ContextMenuItem::Action() const {
+ return action_;
+}
+
+void ContextMenuItem::SetChecked(bool checked) {
+ checked_ = checked;
+}
+
+bool ContextMenuItem::Checked() const {
+ return checked_;
+}
+
+void ContextMenuItem::SetEnabled(bool enabled) {
+ enabled_ = enabled;
+}
+
+bool ContextMenuItem::Enabled() const {
+ return enabled_;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/context_menu_item.h b/chromium/third_party/blink/renderer/platform/context_menu_item.h
new file mode 100644
index 00000000000..9765d45989f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/context_menu_item.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2010 Igalia S.L
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_CONTEXT_MENU_ITEM_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_CONTEXT_MENU_ITEM_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class ContextMenu;
+
+enum ContextMenuAction {
+ kContextMenuItemBaseCustomTag = 5000,
+ kContextMenuItemCustomTagNoAction = 5998,
+ kContextMenuItemLastCustomTag = 5999
+};
+
+enum ContextMenuItemType {
+ kActionType,
+ kCheckableActionType,
+ kSeparatorType,
+ kSubmenuType
+};
+
+class PLATFORM_EXPORT ContextMenuItem {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ ContextMenuItem(ContextMenuItemType,
+ ContextMenuAction,
+ const String& title,
+ ContextMenu* sub_menu = nullptr);
+ ContextMenuItem(ContextMenuItemType,
+ ContextMenuAction,
+ const String& title,
+ bool enabled,
+ bool checked);
+
+ ~ContextMenuItem();
+
+ void SetType(ContextMenuItemType);
+ ContextMenuItemType GetType() const;
+
+ void SetAction(ContextMenuAction);
+ ContextMenuAction Action() const;
+
+ void SetChecked(bool = true);
+ bool Checked() const;
+
+ void SetEnabled(bool = true);
+ bool Enabled() const;
+
+ void SetSubMenu(ContextMenu*);
+
+ ContextMenuItem(ContextMenuAction,
+ const String&,
+ bool enabled,
+ bool checked,
+ const Vector<ContextMenuItem>& sub_menu_items);
+
+ void SetTitle(const String& title) { title_ = title; }
+ const String& Title() const { return title_; }
+
+ const Vector<ContextMenuItem>& SubMenuItems() const {
+ return sub_menu_items_;
+ }
+
+ private:
+ ContextMenuItemType type_;
+ ContextMenuAction action_;
+ String title_;
+ bool enabled_;
+ bool checked_;
+ Vector<ContextMenuItem> sub_menu_items_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_CONTEXT_MENU_ITEM_H_
diff --git a/chromium/third_party/blink/renderer/platform/cpu/mips/common_macros_msa.h b/chromium/third_party/blink/renderer/platform/cpu/mips/common_macros_msa.h
new file mode 100644
index 00000000000..d5ed8db7c06
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/cpu/mips/common_macros_msa.h
@@ -0,0 +1,814 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_CPU_MIPS_COMMON_MACROS_MSA_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_CPU_MIPS_COMMON_MACROS_MSA_H_
+
+#include <msa.h>
+#include <stdint.h>
+
+#if defined(__clang__)
+#define CLANG_BUILD
+#endif
+
+typedef union {
+ int32_t intVal;
+ float floatVal;
+} FloatInt;
+
+#ifdef CLANG_BUILD
+#define SRLI_B(a, b) __msa_srli_b((v16i8)a, b)
+#define SRLI_H(a, b) __msa_srli_h((v8i16)a, b)
+#define SLLI_B(a, b) __msa_slli_b((v16i8)a, b)
+#define SLLI_H(a, b) __msa_slli_h((v8i16)a, b)
+#define CEQI_B(a, b) __msa_ceqi_b((v16i8)a, b)
+#define CEQI_H(a, b) __msa_ceqi_h((v8i16)a, b)
+#define ANDI_B(a, b) __msa_andi_b((v16u8)a, b)
+#else
+#define SRLI_B(a, b) ((v16u8)a >> b)
+#define SRLI_H(a, b) ((v8u16)a >> b)
+#define SLLI_B(a, b) ((v16i8)a << b)
+#define SLLI_H(a, b) ((v8i16)a << b)
+#define CEQI_B(a, b) (a == b)
+#define CEQI_H(a, b) (a == b)
+#define ANDI_B(a, b) ((v16u8)a & b)
+#endif
+
+#define LD_V(RTYPE, psrc) *((RTYPE*)(psrc))
+#define LD_UB(...) LD_V(v16u8, __VA_ARGS__)
+#define LD_UH(...) LD_V(v8u16, __VA_ARGS__)
+#define LD_SP(...) LD_V(v4f32, __VA_ARGS__)
+#define LD_DP(...) LD_V(v2f64, __VA_ARGS__)
+
+#define ST_V(RTYPE, in, pdst) *((RTYPE*)(pdst)) = in
+#define ST_UB(...) ST_V(v16u8, __VA_ARGS__)
+#define ST_UH(...) ST_V(v8u16, __VA_ARGS__)
+#define ST_SP(...) ST_V(v4f32, __VA_ARGS__)
+#define ST_DP(...) ST_V(v2f64, __VA_ARGS__)
+
+#ifdef CLANG_BUILD
+#define COPY_DOUBLE_TO_VECTOR(a) \
+ ({ \
+ v2f64 out; \
+ out = (v2f64)__msa_fill_d(*(int64_t*)(&a)); \
+ out; \
+ })
+#else
+#define COPY_DOUBLE_TO_VECTOR(a) \
+ ({ \
+ v2f64 out; \
+ out = __msa_cast_to_vector_double(a); \
+ out = (v2f64)__msa_splati_d((v2i64)out, 0); \
+ out; \
+ })
+#endif
+
+#define MSA_STORE_FUNC(TYPE, INSTR, FUNCNAME) \
+ static inline void FUNCNAME(TYPE val, void* const pdst) { \
+ uint8_t* const pdstm = (uint8_t*)pdst; \
+ TYPE valm = val; \
+ asm volatile(" " #INSTR " %[valm], %[pdstm] \n\t" \
+ : [pdstm] "=m"(*pdstm) \
+ : [valm] "r"(valm)); \
+ }
+
+#define MSA_STORE(val, pdst, FUNCNAME) FUNCNAME(val, pdst)
+
+#ifdef CLANG_BUILD
+MSA_STORE_FUNC(uint32_t, sw, msa_sw);
+#define SW(val, pdst) MSA_STORE(val, pdst, msa_sw)
+#if (__mips == 64)
+MSA_STORE_FUNC(uint64_t, sd, msa_sd);
+#define SD(val, pdst) MSA_STORE(val, pdst, msa_sd)
+#else
+#define SD(val, pdst) \
+ { \
+ uint8_t* const pdstsd = (uint8_t*)(pdst); \
+ const uint32_t val0m = (uint32_t)(val & 0x00000000FFFFFFFF); \
+ const uint32_t val1m = (uint32_t)((val >> 32) & 0x00000000FFFFFFFF); \
+ SW(val0m, pdstsd); \
+ SW(val1m, pdstsd + 4); \
+ }
+#endif
+#else
+#if (__mips_isa_rev >= 6)
+MSA_STORE_FUNC(uint32_t, sw, msa_sw);
+#define SW(val, pdst) MSA_STORE(val, pdst, msa_sw)
+MSA_STORE_FUNC(uint64_t, sd, msa_sd);
+#define SD(val, pdst) MSA_STORE(val, pdst, msa_sd)
+#else // !(__mips_isa_rev >= 6)
+MSA_STORE_FUNC(uint32_t, usw, msa_usw);
+#define SW(val, pdst) MSA_STORE(val, pdst, msa_usw)
+#define SD(val, pdst) \
+ { \
+ uint8_t* const pdstsd = (uint8_t*)(pdst); \
+ const uint32_t val0m = (uint32_t)(val & 0x00000000FFFFFFFF); \
+ const uint32_t val1m = (uint32_t)((val >> 32) & 0x00000000FFFFFFFF); \
+ SW(val0m, pdstsd); \
+ SW(val1m, pdstsd + 4); \
+ }
+#endif // (__mips_isa_rev >= 6)
+#endif
+
+/* Description : Load vectors with elements with stride
+ * Arguments : Inputs - psrc, stride
+ * Outputs - out0, out1
+ * Return Type - as per RTYPE
+ * Details : Load elements in 'out0' from (psrc)
+ * Load elements in 'out1' from (psrc + stride)
+ */
+#define LD_V2(RTYPE, psrc, stride, out0, out1) \
+ { \
+ out0 = LD_V(RTYPE, psrc); \
+ psrc += stride; \
+ out1 = LD_V(RTYPE, psrc); \
+ psrc += stride; \
+ }
+#define LD_UB2(...) LD_V2(v16u8, __VA_ARGS__)
+#define LD_UH2(...) LD_V2(v8u16, __VA_ARGS__)
+#define LD_SP2(...) LD_V2(v4f32, __VA_ARGS__)
+
+#define LD_V3(RTYPE, psrc, stride, out0, out1, out2) \
+ { \
+ LD_V2(RTYPE, psrc, stride, out0, out1); \
+ out2 = LD_V(RTYPE, psrc); \
+ psrc += stride; \
+ }
+#define LD_UB3(...) LD_V3(v16u8, __VA_ARGS__)
+#define LD_UH3(...) LD_V3(v8u16, __VA_ARGS__)
+
+#define LD_V4(RTYPE, psrc, stride, out0, out1, out2, out3) \
+ { \
+ LD_V2(RTYPE, psrc, stride, out0, out1); \
+ LD_V2(RTYPE, psrc, stride, out2, out3); \
+ }
+#define LD_UB4(...) LD_V4(v16u8, __VA_ARGS__)
+#define LD_UH4(...) LD_V4(v8u16, __VA_ARGS__)
+#define LD_SP4(...) LD_V4(v4f32, __VA_ARGS__)
+
+#define LD_V5(RTYPE, psrc, stride, out0, out1, out2, out3, out4) \
+ { \
+ LD_V4(RTYPE, psrc, stride, out0, out1, out2, out3); \
+ out4 = LD_V(RTYPE, psrc); \
+ psrc += stride; \
+ }
+#define LD_UB5(...) LD_V5(v16u8, __VA_ARGS__)
+
+#define LD_V6(RTYPE, psrc, stride, out0, out1, out2, out3, out4, out5) \
+ { \
+ LD_V4(RTYPE, psrc, stride, out0, out1, out2, out3); \
+ LD_V2(RTYPE, psrc, stride, out4, out5); \
+ }
+#define LD_UB6(...) LD_V6(v16u8, __VA_ARGS__)
+#define LD_UH6(...) LD_V6(v8u16, __VA_ARGS__)
+#define LD_SP6(...) LD_V6(v4f32, __VA_ARGS__)
+
+#define LD_V7(RTYPE, psrc, stride, out0, out1, out2, out3, out4, out5, out6) \
+ { \
+ LD_V5(RTYPE, psrc, stride, out0, out1, out2, out3, out4); \
+ LD_V2(RTYPE, psrc, stride, out5, out6); \
+ }
+#define LD_UB7(...) LD_V7(v16u8, __VA_ARGS__)
+
+#define LD_V8(RTYPE, psrc, stride, out0, out1, out2, out3, out4, out5, out6, \
+ out7) \
+ { \
+ LD_V4(RTYPE, psrc, stride, out0, out1, out2, out3); \
+ LD_V4(RTYPE, psrc, stride, out4, out5, out6, out7); \
+ }
+#define LD_UB8(...) LD_V8(v16u8, __VA_ARGS__)
+#define LD_UH8(...) LD_V8(v8u16, __VA_ARGS__)
+#define LD_SP8(...) LD_V8(v4f32, __VA_ARGS__)
+#define LD_DP8(...) LD_V8(v2f64, __VA_ARGS__)
+
+/* Description : Store vectors of elements with stride
+ * Arguments : Inputs - in0, in1, pdst, stride
+ * Details : Store elements from 'in0' to (pdst)
+ * Store elements from 'in1' to (pdst + stride)
+ */
+#define ST_V2(RTYPE, in0, in1, pdst, stride) \
+ { \
+ ST_V(RTYPE, in0, pdst); \
+ pdst += stride; \
+ ST_V(RTYPE, in1, pdst); \
+ pdst += stride; \
+ }
+#define ST_UB2(...) ST_V2(v16u8, __VA_ARGS__)
+#define ST_UH2(...) ST_V2(v8u16, __VA_ARGS__)
+#define ST_SP2(...) ST_V2(v4f32, __VA_ARGS__)
+
+#define ST_V3(RTYPE, in0, in1, in2, pdst, stride) \
+ { \
+ ST_V2(RTYPE, in0, in1, pdst, stride); \
+ ST_V(RTYPE, in2, pdst); \
+ pdst += stride; \
+ }
+#define ST_UB3(...) ST_V3(v16u8, __VA_ARGS__)
+#define ST_UH3(...) ST_V3(v8u16, __VA_ARGS__)
+
+#define ST_V4(RTYPE, in0, in1, in2, in3, pdst, stride) \
+ { \
+ ST_V2(RTYPE, in0, in1, pdst, stride); \
+ ST_V2(RTYPE, in2, in3, pdst, stride); \
+ }
+#define ST_UB4(...) ST_V4(v16u8, __VA_ARGS__)
+#define ST_UH4(...) ST_V4(v8u16, __VA_ARGS__)
+#define ST_SP4(...) ST_V4(v4f32, __VA_ARGS__)
+
+#define ST_V6(RTYPE, in0, in1, in2, in3, in4, in5, pdst, stride) \
+ { \
+ ST_V3(RTYPE, in0, in1, in2, pdst, stride); \
+ ST_V3(RTYPE, in3, in4, in5, pdst, stride); \
+ }
+#define ST_UB6(...) ST_V6(v16u8, __VA_ARGS__)
+#define ST_SP6(...) ST_V6(v4f32, __VA_ARGS__)
+
+#define ST_V8(RTYPE, in0, in1, in2, in3, in4, in5, in6, in7, pdst, stride) \
+ { \
+ ST_V4(RTYPE, in0, in1, in2, in3, pdst, stride); \
+ ST_V4(RTYPE, in4, in5, in6, in7, pdst, stride); \
+ }
+#define ST_UB8(...) ST_V8(v16u8, __VA_ARGS__)
+#define ST_SP8(...) ST_V8(v4f32, __VA_ARGS__)
+
+/* Description : Store 8x1 byte block to destination memory from input vector
+ Arguments : Inputs - in, pdst
+ Details : Index 0 double word element from 'in' vector is copied to the
+ GP register and stored to (pdst)
+*/
+#define ST8x1_UB(in, pdst) \
+ { \
+ const uint64_t out0m = __msa_copy_s_d((v2i64)in, 0); \
+ SD(out0m, pdst); \
+ }
+
+/* Description : Logical and in0 and in1.
+ Arguments : Inputs - in0, in1, in2, in3,
+ Outputs - out0, out1, out2, out3
+ Return Type - as per RTYPE
+ Details : Each unsigned word element from 'in0' vector is added with
+ each unsigned word element from 'in1' vector. Then the average
+ is calculated and written to 'out0'
+*/
+#define AND_V2(RTYPE, in0, in1, mask, out0, out1) \
+ { \
+ out0 = (RTYPE)__msa_and_v((v16u8)in0, (v16u8)mask); \
+ out1 = (RTYPE)__msa_and_v((v16u8)in1, (v16u8)mask); \
+ }
+#define AND_V2_UB(...) AND_V2(v16u8, __VA_ARGS__)
+
+#define AND_V4(RTYPE, in0, in1, in2, in3, mask, out0, out1, out2, out3) \
+ { \
+ AND_V2(RTYPE, in0, in1, mask, out0, out1); \
+ AND_V2(RTYPE, in2, in3, mask, out2, out3); \
+ }
+#define AND_V4_UB(...) AND_V4(v16u8, __VA_ARGS__)
+
+/* Description : Logical equate of input vectors with immediate value
+ Arguments : Inputs - in0, in1, val
+ Outputs - in place operation
+ Return Type - as per RTYPE
+ Details : Each unsigned byte element from input vector 'in0' & 'in1' is
+ logically and'ed with immediate mask and the result
+ is stored in-place.
+*/
+#define CEQI_B2(RTYPE, in0, in1, val, out0, out1) \
+ { \
+ out0 = CEQI_B(in0, val); \
+ out1 = CEQI_B(in1, val); \
+ }
+#define CEQI_B2_UB(...) CEQI_B2(v16u8, __VA_ARGS__)
+
+#define CEQI_B4(RTYPE, in0, in1, in2, in3, val, out0, out1, out2, out3) \
+ { \
+ CEQI_B2(RTYPE, in0, in1, val, out0, out1); \
+ CEQI_B2(RTYPE, in2, in3, val, out2, out3); \
+ }
+#define CEQI_B4_UB(...) CEQI_B4(v16u8, __VA_ARGS__)
+
+/* Description : Immediate number of elements to slide
+ * Arguments : Inputs - in0, in1, slide_val
+ * Outputs - out
+ * Return Type - as per RTYPE
+ * Details : Byte elements from 'in1' vector are slid into 'in0' by
+ * value specified in the 'slide_val'
+ */
+#define SLDI_B(RTYPE, in0, in1, slide_val) \
+ (RTYPE) __msa_sldi_b((v16i8)in0, (v16i8)in1, slide_val)
+#define SLDI_UB(...) SLDI_B(v16u8, __VA_ARGS__)
+#define SLDI_D(...) SLDI_B(v2f64, __VA_ARGS__)
+
+/* Description : Immediate number of elements to slide
+ Arguments : Inputs - in0_0, in0_1, in1_0, in1_1, slide_val
+ Outputs - out0, out1
+ Return Type - as per RTYPE
+ Details : Byte elements from 'in0_0' vector are slid into 'in1_0' by
+ value specified in the 'slide_val'
+*/
+#define SLDI_B2(RTYPE, in0_0, in0_1, in1_0, in1_1, out0, out1, slide_val) \
+ { \
+ out0 = SLDI_B(RTYPE, in0_0, in1_0, slide_val); \
+ out1 = SLDI_B(RTYPE, in0_1, in1_1, slide_val); \
+ }
+#define SLDI_B2_UB(...) SLDI_B2(v16u8, __VA_ARGS__)
+
+/* Description : Shuffle byte vector elements as per variable
+ Arguments : Inputs - in0, in1, shf_val
+ Outputs - out0, out1
+ Return Type - as per RTYPE
+ Details : Byte elements from 'in0' & 'in1' are copied selectively to
+ 'out0' as per control variable 'shf_val'.
+*/
+#define SHF_B2(RTYPE, in0, in1, shf_val) \
+ { \
+ in0 = (RTYPE)__msa_shf_b((v16i8)in0, shf_val); \
+ in1 = (RTYPE)__msa_shf_b((v16i8)in1, shf_val); \
+ }
+#define SHF_B2_UB(...) SHF_B2(v16u8, __VA_ARGS__)
+#define SHF_B2_UH(...) SHF_B2(v8u16, __VA_ARGS__)
+
+#define SHF_B3(RTYPE, in0, in1, in2, shf_val) \
+ { \
+ SHF_B2(RTYPE, in0, in1, shf_val); \
+ in2 = (RTYPE)__msa_shf_b((v16i8)in2, shf_val); \
+ }
+#define SHF_B3_UB(...) SHF_B3(v16u8, __VA_ARGS__)
+#define SHF_B3_UH(...) SHF_B3(v8u16, __VA_ARGS__)
+
+#define SHF_B4(RTYPE, in0, in1, in2, in3, shf_val) \
+ { \
+ SHF_B2(RTYPE, in0, in1, shf_val); \
+ SHF_B2(RTYPE, in2, in3, shf_val); \
+ }
+#define SHF_B4_UB(...) SHF_B4(v16u8, __VA_ARGS__)
+#define SHF_B4_UH(...) SHF_B4(v8u16, __VA_ARGS__)
+
+/* Description : Shuffle byte vector elements as per mask vector
+ Arguments : Inputs - in0, in1, in2, in3, mask0, mask1
+ Outputs - out0, out1
+ Return Type - as per RTYPE
+ Details : Byte elements from 'in0' & 'in1' are copied selectively to
+ 'out0' as per control vector 'mask0'
+*/
+#define VSHF_B(RTYPE, in0, in1, mask) \
+ (RTYPE) __msa_vshf_b((v16i8)mask, (v16i8)in1, (v16i8)in0);
+#define VSHF_UB(...) VSHF_B(v16u8, __VA_ARGS__)
+
+/* Description : Interleave even byte elements from vectors
+ Arguments : Inputs - in0, in1, in2, in3
+ Outputs - out0, out1
+ Return Type - as per RTYPE
+ Details : Even byte elements of 'in0' and 'in1' are interleaved
+ and written to 'out0'
+*/
+#define ILVEV_B2(RTYPE, in0, in1, in2, in3, out0, out1) \
+ { \
+ out0 = (RTYPE)__msa_ilvev_b((v16i8)in1, (v16i8)in0); \
+ out1 = (RTYPE)__msa_ilvev_b((v16i8)in3, (v16i8)in2); \
+ }
+#define ILVEV_B2_UB(...) ILVEV_B2(v16u8, __VA_ARGS__)
+#define ILVEV_B2_UH(...) ILVEV_B2(v8u16, __VA_ARGS__)
+
+#define ILVEV_B3(RTYPE, in0, in1, in2, in3, in4, in5, out0, out1, out2) \
+ { \
+ ILVEV_B2(RTYPE, in0, in1, in2, in3, out0, out1) \
+ out2 = (RTYPE)__msa_ilvev_b((v16i8)in5, (v16i8)in4); \
+ }
+#define ILVEV_B3_UH(...) ILVEV_B3(v8u16, __VA_ARGS__)
+
+/* Description : Interleave even halfword elements from vectors
+ Arguments : Inputs - in0, in1, in2, in3
+ Outputs - out0, out1
+ Return Type - as per RTYPE
+ Details : Even halfword elements of 'in0' and 'in1' are interleaved
+ and written to 'out0'
+*/
+#define ILVEV_H2(RTYPE, in0, in1, in2, in3, out0, out1) \
+ { \
+ out0 = (RTYPE)__msa_ilvev_h((v8i16)in1, (v8i16)in0); \
+ out1 = (RTYPE)__msa_ilvev_h((v8i16)in3, (v8i16)in2); \
+ }
+#define ILVEV_H2_UB(...) ILVEV_H2(v16u8, __VA_ARGS__)
+
+/* Description : Interleave right half of double word elements from vectors
+ * Arguments : Inputs - in0, in1, in2, in3
+ * Outputs - out0, out1
+ * Return Type - as per RTYPE
+ * Details : Right half of double word elements of 'in0' and 'in1' are
+ * interleaved and written to 'out0'.
+ */
+#define ILVR_D2(RTYPE, in0, in1, in2, in3, out0, out1) \
+ { \
+ out0 = (RTYPE)__msa_ilvr_d((v2i64)in0, (v2i64)in1); \
+ out1 = (RTYPE)__msa_ilvr_d((v2i64)in2, (v2i64)in3); \
+ }
+#define ILVR_D2_UB(...) ILVR_D2(v16u8, __VA_ARGS__)
+
+#define ILVR_D3(RTYPE, in0, in1, in2, in3, in4, in5, out0, out1, out2) \
+ { \
+ ILVR_D2(RTYPE, in0, in1, in2, in3, out0, out1); \
+ out2 = (RTYPE)__msa_ilvr_d((v2i64)in4, (v2i64)in5); \
+ }
+#define ILVR_D3_UB(...) ILVR_D3(v16u8, __VA_ARGS__)
+
+#define ILVR_D4(RTYPE, in0, in1, in2, in3, in4, in5, in6, in7, out0, out1, \
+ out2, out3) \
+ { \
+ ILVR_D2(RTYPE, in0, in1, in2, in3, out0, out1); \
+ ILVR_D2(RTYPE, in4, in5, in6, in7, out2, out3); \
+ }
+#define ILVR_D4_UB(...) ILVR_D4(v16u8, __VA_ARGS__)
+
+/* Description : Interleave both left and right half of input vectors
+ Arguments : Inputs - in0, in1
+ Outputs - out0, out1
+ Return Type - as per RTYPE
+ Details : Right half of byte elements from 'in0' and 'in1' are
+ interleaved and written to 'out0'
+*/
+#define ILVRL_B2(RTYPE, in0, in1, out0, out1) \
+ { \
+ out0 = (RTYPE)__msa_ilvr_b((v16i8)in0, (v16i8)in1); \
+ out1 = (RTYPE)__msa_ilvl_b((v16i8)in0, (v16i8)in1); \
+ }
+#define ILVRL_B2_UB(...) ILVRL_B2(v16u8, __VA_ARGS__)
+
+#define ILVRL_H2(RTYPE, in0, in1, out0, out1) \
+ { \
+ out0 = (RTYPE)__msa_ilvr_h((v8i16)in0, (v8i16)in1); \
+ out1 = (RTYPE)__msa_ilvl_h((v8i16)in0, (v8i16)in1); \
+ }
+#define ILVRL_H2_UB(...) ILVRL_H2(v16u8, __VA_ARGS__)
+
+/* Description : Interleave both odd and even half of input vectors
+ Arguments : Inputs - in0, in1
+ Outputs - out0, out1
+ Return Type - as per RTYPE
+ Details : Odd half of byte elements from 'in0' and 'in1' are
+ interleaved and written to 'out0'
+*/
+#define ILVODEV_B2(RTYPE, in0, in1, out0, out1) \
+ { \
+ out0 = (RTYPE)__msa_ilvod_b((v16i8)in0, (v16i8)in1); \
+ out1 = (RTYPE)__msa_ilvev_b((v16i8)in0, (v16i8)in1); \
+ }
+#define ILVODEV_B2_UB(...) ILVODEV_B2(v16u8, __VA_ARGS__)
+
+/* Description : Pack even byte elements of vector pairs
+ * Arguments : Inputs - in0, in1, in2, in3
+ * Outputs - out0, out1
+ * Return Type - as per RTYPE
+ * Details : Even byte elements of 'in0' are copied to the left half of
+ * 'out0' & even byte elements of 'in1' are copied to the right
+ * half of 'out0'.
+ */
+#define PCKEV_B2(RTYPE, in0, in1, in2, in3, out0, out1) \
+ { \
+ out0 = (RTYPE)__msa_pckev_b((v16i8)in0, (v16i8)in1); \
+ out1 = (RTYPE)__msa_pckev_b((v16i8)in2, (v16i8)in3); \
+ }
+#define PCKEV_B2_UB(...) PCKEV_B2(v16u8, __VA_ARGS__)
+#define PCKEV_B2_UH(...) PCKEV_B2(v8u16, __VA_ARGS__)
+
+#define PCKEV_B3(RTYPE, in0, in1, in2, in3, in4, in5, out0, out1, out2) \
+ { \
+ PCKEV_B2(RTYPE, in0, in1, in2, in3, out0, out1); \
+ out2 = (RTYPE)__msa_pckev_b((v16i8)in4, (v16i8)in5); \
+ }
+#define PCKEV_B3_UH(...) PCKEV_B3(v8u16, __VA_ARGS__)
+
+#define PCKEV_B4(RTYPE, in0, in1, in2, in3, in4, in5, in6, in7, out0, out1, \
+ out2, out3) \
+ { \
+ PCKEV_B2(RTYPE, in0, in1, in2, in3, out0, out1); \
+ PCKEV_B2(RTYPE, in4, in5, in6, in7, out2, out3); \
+ }
+#define PCKEV_B4_UH(...) PCKEV_B4(v8u16, __VA_ARGS__)
+
+/* Description : Pack even halfword elements of vector pairs
+ Arguments : Inputs - in0, in1, in2, in3
+ Outputs - out0, out1
+ Return Type - as per RTYPE
+ Details : Even halfword elements of 'in0' are copied to the left half of
+ 'out0' & even halfword elements of 'in1' are copied to the
+ right half of 'out0'.
+*/
+#define PCKEV_H2(RTYPE, in0, in1, in2, in3, out0, out1) \
+ { \
+ out0 = (RTYPE)__msa_pckev_h((v8i16)in0, (v8i16)in1); \
+ out1 = (RTYPE)__msa_pckev_h((v8i16)in2, (v8i16)in3); \
+ }
+#define PCKEV_H2_UB(...) PCKEV_H2(v16u8, __VA_ARGS__)
+
+#define PCKEV_H3(RTYPE, in0, in1, in2, in3, in4, in5, out0, out1, out2) \
+ { \
+ PCKEV_H2(RTYPE, in0, in1, in2, in3, out0, out1); \
+ out2 = (RTYPE)__msa_pckev_h((v8i16)in4, (v8i16)in5); \
+ }
+#define PCKEV_H3_UB(...) PCKEV_H3(v16u8, __VA_ARGS__)
+
+#define PCKEV_H4(RTYPE, in0, in1, in2, in3, in4, in5, in6, in7, out0, out1, \
+ out2, out3) \
+ { \
+ PCKEV_H2(RTYPE, in0, in1, in2, in3, out0, out1); \
+ PCKEV_H2(RTYPE, in4, in5, in6, in7, out2, out3); \
+ }
+#define PCKEV_H4_UB(...) PCKEV_H4(v16u8, __VA_ARGS__)
+
+/* Description : Pack odd halfword elements of vector pairs
+ Arguments : Inputs - in0, in1, in2, in3
+ Outputs - out0, out1
+ Return Type - as per RTYPE
+ Details : Odd halfword elements of 'in0' are copied to the left half of
+ 'out0' & odd halfword elements of 'in1' are copied to the
+ right half of 'out0'.
+*/
+#define PCKOD_H2(RTYPE, in0, in1, in2, in3, out0, out1) \
+ { \
+ out0 = (RTYPE)__msa_pckod_h((v8i16)in0, (v8i16)in1); \
+ out1 = (RTYPE)__msa_pckod_h((v8i16)in2, (v8i16)in3); \
+ }
+#define PCKOD_H2_UB(...) PCKOD_H2(v16u8, __VA_ARGS__)
+
+#define PCKOD_H3(RTYPE, in0, in1, in2, in3, in4, in5, out0, out1, out2) \
+ { \
+ PCKOD_H2(RTYPE, in0, in1, in2, in3, out0, out1); \
+ out2 = (RTYPE)__msa_pckod_h((v8i16)in4, (v8i16)in5); \
+ }
+#define PCKOD_H3_UB(...) PCKOD_H3(v16u8, __VA_ARGS__)
+
+#define PCKOD_H4(RTYPE, in0, in1, in2, in3, in4, in5, in6, in7, out0, out1, \
+ out2, out3) \
+ { \
+ PCKOD_H2(RTYPE, in0, in1, in2, in3, out0, out1); \
+ PCKOD_H2(RTYPE, in4, in5, in6, in7, out2, out3); \
+ }
+#define PCKOD_H4_UB(...) PCKOD_H4(v16u8, __VA_ARGS__)
+
+/* Description : Logical shift right all elements of half-word vector
+ Arguments : Inputs - in0, in1, shift
+ Outputs - in place operation
+ Return Type - as per input vector RTYPE
+ Details : Each element of vector 'in0' is right shifted by 'shift' and
+ the result is written in-place. 'shift' is a GP variable.
+*/
+#define SRLI_B2(RTYPE, in0, in1, shift_val) \
+ { \
+ in0 = (RTYPE)SRLI_B(in0, shift_val); \
+ in1 = (RTYPE)SRLI_B(in1, shift_val); \
+ }
+#define SRLI_B2_UB(...) SRLI_B2(v16u8, __VA_ARGS__)
+
+#define SRLI_B3(RTYPE, in0, in1, in2, shift_val) \
+ { \
+ SRLI_B2(RTYPE, in0, in1, shift_val); \
+ in2 = (RTYPE)SRLI_B(in2, shift_val); \
+ }
+#define SRLI_B3_UB(...) SRLI_B3(v16u8, __VA_ARGS__)
+
+#define SRLI_B4(RTYPE, in0, in1, in2, in3, shift_val) \
+ { \
+ SRLI_B2(RTYPE, in0, in1, shift_val); \
+ SRLI_B2(RTYPE, in2, in3, shift_val); \
+ }
+#define SRLI_B4_UB(...) SRLI_B4(v16u8, __VA_ARGS__)
+
+/* Description : Logical shift right all elements of vector (immediate)
+ Arguments : Inputs - in0, in1, in2, in3, shift
+ Outputs - out0, out1, out2, out3
+ Return Type - as per RTYPE
+ Details : Each element of vector 'in0' is right shifted by 'shift' and
+ the result is written in 'out0'. 'shift' is an immediate value.
+*/
+#define SRLI_H2(RTYPE, in0, in1, out0, out1, shift) \
+ { \
+ out0 = (RTYPE)SRLI_H((v8i16)in0, shift); \
+ out1 = (RTYPE)SRLI_H((v8i16)in1, shift); \
+ }
+#define SRLI_H2_UB(...) SRLI_H2(v16u8, __VA_ARGS__)
+
+#define SRLI_H4(RTYPE, in0, in1, in2, in3, out0, out1, out2, out3, shift) \
+ { \
+ SRLI_H2(RTYPE, in0, in1, out0, out1, shift); \
+ SRLI_H2(RTYPE, in2, in3, out2, out3, shift); \
+ }
+#define SRLI_H4_UB(...) SRLI_H4(v16u8, __VA_ARGS__)
+
+/* Description : Immediate Bit Insert Left (immediate)
+ Arguments : Inputs - in0, in1, in2, in3, shift
+ Outputs - out0, out1
+ Return Type - as per RTYPE
+ Details : Copy most significant (left) bits in each element of vector
+ 'in1' to elements in vector in0 while preserving the least
+ significant (right) bits. The number of bits to copy is given
+ by the immediate 'shift + 1'.
+*/
+#define BINSLI_B2(RTYPE, in0, in1, in2, in3, out0, out1, shift) \
+ { \
+ out0 = (RTYPE)__msa_binsli_b((v16u8)in0, (v16u8)in1, shift); \
+ out1 = (RTYPE)__msa_binsli_b((v16u8)in2, (v16u8)in3, shift); \
+ }
+#define BINSLI_B2_UB(...) BINSLI_B2(v16u8, __VA_ARGS__)
+
+/* Description : Immediate Bit Insert Right (immediate)
+ Arguments : Inputs - in0, in1, in2, in3, shift
+ Outputs - out0, out1
+ Return Type - as per RTYPE
+ Details : Copy least significant (right) bits in each element of vector
+ 'in1' to elements in vector in0 while preserving the most
+ significant (left) bits. The number of bits to copy is given
+ by the immediate 'shift + 1'.
+*/
+#define BINSRI_B2(RTYPE, in0, in1, in2, in3, out0, out1, shift) \
+ { \
+ out0 = (RTYPE)__msa_binsri_b((v16u8)in0, (v16u8)in1, shift); \
+ out1 = (RTYPE)__msa_binsri_b((v16u8)in2, (v16u8)in3, shift); \
+ }
+#define BINSRI_B2_UB(...) BINSRI_B2(v16u8, __VA_ARGS__)
+
+#define BINSRI_B3(RTYPE, in0, in1, in2, in3, in4, in5, out0, out1, out2, \
+ shift) \
+ { \
+ BINSRI_B2(RTYPE, in0, in1, in2, in3, out0, out1, shift); \
+ out2 = (RTYPE)__msa_binsri_b((v16u8)in4, (v16u8)in5, shift); \
+ }
+#define BINSRI_B3_UB(...) BINSRI_B3(v16u8, __VA_ARGS__)
+
+/* Description : Multiplication of pairs of vectors
+ Arguments : Inputs - in0, in1, in2, in3
+ Outputs - out0, out1
+ Details : Each element from 'in0' is multiplied with elements from 'in1'
+ and the result is written to 'out0'
+*/
+#define MUL2(in0, in1, in2, in3, out0, out1) \
+ { \
+ out0 = in0 * in1; \
+ out1 = in2 * in3; \
+ }
+#define MUL4(in0, in1, in2, in3, in4, in5, in6, in7, out0, out1, out2, out3) \
+ { \
+ MUL2(in0, in1, in2, in3, out0, out1); \
+ MUL2(in4, in5, in6, in7, out2, out3); \
+ }
+
+/* Description : Division of pairs of vectors
+ Arguments : Inputs - in0, in1, in2, in3
+ Outputs - out0, out1
+ Details : Each element from 'in0' is divided by elements from 'in1'
+ and the result is written to 'out0'
+*/
+#define DIV2(in0, in1, in2, in3, out0, out1) \
+ { \
+ out0 = in0 / in1; \
+ out1 = in2 / in3; \
+ }
+#define DIV4(in0, in1, in2, in3, in4, in5, in6, in7, out0, out1, out2, out3) \
+ { \
+ DIV2(in0, in1, in2, in3, out0, out1); \
+ DIV2(in4, in5, in6, in7, out2, out3); \
+ }
+
+/* Description : Logical AND of 4 pairs of vectors with mask
+ Arguments : Inputs - in0, in1, in2, in3, mask
+ Outputs - in0, in1, in2, in3
+ Details : Each element in 'in0' is logically AND'ed with mask
+ Each element in 'in1' is logically AND'ed with mask
+ Each element in 'in2' is logically AND'ed with mask
+ Each element in 'in3' is logically AND'ed with mask
+*/
+#define AND_W4(RTYPE, in0, in1, in2, in3, mask) \
+ { \
+ in0 = (RTYPE)((v16i8)in0 & (v16i8)mask); \
+ in1 = (RTYPE)((v16i8)in1 & (v16i8)mask); \
+ in2 = (RTYPE)((v16i8)in2 & (v16i8)mask); \
+ in3 = (RTYPE)((v16i8)in3 & (v16i8)mask); \
+ }
+#define AND_W4_SP(...) AND_W4(v4f32, __VA_ARGS__)
+
+/* Description : Addition of 2 pairs of vectors
+ Arguments : Inputs - in0, in1, in2, in3
+ Outputs - out0, out1
+ Details : Each element in 'in0' is added to 'in1' and result is written
+ to 'out0'
+ Each element in 'in2' is added to 'in3' and result is written
+ to 'out1'
+*/
+#define ADD2(in0, in1, in2, in3, out0, out1) \
+ { \
+ out0 = in0 + in1; \
+ out1 = in2 + in3; \
+ }
+
+/* Description : Addition of 4 pairs of vectors
+ Arguments : Inputs - in0, in1, in2, in3, in4, in5, in6, in7
+ Outputs - out0, out1
+ Details : Each element in 'in0' is added to 'in1' and result is written
+ to 'out0'
+ Each element in 'in2' is added to 'in3' and result is written
+ to 'out1'
+ Each element in 'in4' is added to 'in5' and result is written
+ to 'out2'
+ Each element in 'in6' is added to 'in7' and result is written
+ to 'out3'
+*/
+#define ADD4(in0, in1, in2, in3, in4, in5, in6, in7, out0, out1, out2, out3) \
+ { \
+ ADD2(in0, in1, in2, in3, out0, out1); \
+ ADD2(in4, in5, in6, in7, out2, out3); \
+ }
+
+/* Description : Vector Floating-Point Convert from Unsigned Integer
+ Arguments : Inputs - in0, in1
+ Outputs - out0, out1
+*/
+#define FFINTU_W2(RTYPE, in0, in1, out0, out1) \
+ { \
+ out0 = (RTYPE)__msa_ffint_u_w((v4u32)in0); \
+ out1 = (RTYPE)__msa_ffint_u_w((v4u32)in1); \
+ }
+#define FFINTU_W2_SP(...) FFINTU_W2(v4f32, __VA_ARGS__)
+
+/* Description : Vector Floating-Point Convert from Unsigned Integer
+ Arguments : Inputs - in0, in1, in2, in3
+ Outputs - out0, out1, out2, out3
+*/
+#define FFINTU_W4(RTYPE, in0, in1, in2, in3, out0, out1, out2, out3) \
+ { \
+ FFINTU_W2(RTYPE, in0, in1, out0, out1); \
+ FFINTU_W2(RTYPE, in2, in3, out2, out3); \
+ }
+#define FFINTU_W4_SP(...) FFINTU_W4(v4f32, __VA_ARGS__)
+
+/* Description : Vector Floating-Point Truncate and Convert to Unsigned Integer
+ Arguments : Inputs - in0, in1
+ Outputs - out0, out1
+*/
+#define FTRUNCU_W2(RTYPE, in0, in1, out0, out1) \
+ { \
+ out0 = (RTYPE)__msa_ftrunc_u_w((v4f32)in0); \
+ out1 = (RTYPE)__msa_ftrunc_u_w((v4f32)in1); \
+ }
+#define FTRUNCU_W2_UB(...) FTRUNCU_W2(v16u8, __VA_ARGS__)
+
+/* Description : Vector Floating-Point Truncate and Convert to Unsigned Integer
+ Arguments : Inputs - in0, in1, in2, in3
+ Outputs - out0, out1, out2, out3
+*/
+#define FTRUNCU_W4(RTYPE, in0, in1, in2, in3, out0, out1, out2, out3) \
+ { \
+ FTRUNCU_W2(RTYPE, in0, in1, out0, out1); \
+ FTRUNCU_W2(RTYPE, in2, in3, out2, out3); \
+ }
+#define FTRUNCU_W4_UB(...) FTRUNCU_W4(v16u8, __VA_ARGS__)
+
+/* Description : Vector Floating-Point multiply with scale and accumulate
+ Arguments : Inputs - in0, in1, in2, in3, out0, out1, out2, out3, scale
+ Outputs - out0, out1, out2, out3
+*/
+#define VSMA4(in0, in1, in2, in3, out0, out1, out2, out3, scale) \
+ { \
+ out0 += in0 * scale; \
+ out1 += in1 * scale; \
+ out2 += in2 * scale; \
+ out3 += in3 * scale; \
+ }
+
+/* Description : Vector Floating-Point multiply with scale
+ Arguments : Inputs - in0, in1, in2, in3, scale
+ Outputs - out0, out1, out2, out3
+*/
+#define VSMUL4(in0, in1, in2, in3, out0, out1, out2, out3, scale) \
+ { \
+ out0 = in0 * scale; \
+ out1 = in1 * scale; \
+ out2 = in2 * scale; \
+ out3 = in3 * scale; \
+ }
+
+/* Description : Vector Floating-Point max value
+ Arguments : Inputs - in0, in1, in2, in3, max
+ Output - max
+*/
+#define VMAX_W4(RTYPE, in0, in1, in2, in3, max) \
+ { \
+ max = (RTYPE)__msa_fmax_w((v4f32)max, (v4f32)in0); \
+ max = (RTYPE)__msa_fmax_w((v4f32)max, (v4f32)in1); \
+ max = (RTYPE)__msa_fmax_w((v4f32)max, (v4f32)in2); \
+ max = (RTYPE)__msa_fmax_w((v4f32)max, (v4f32)in3); \
+ }
+#define VMAX_W4_SP(...) VMAX_W4(v4f32, __VA_ARGS__)
+
+/* Description : Vector Floating-Point clip to min max
+ Arguments : Inputs - in0, in1, in2, in3, min, max
+ Outputs - out0, out1, out2, out3
+*/
+#define VCLIP4(in0, in1, in2, in3, min, max, out0, out1, out2, out3) \
+ { \
+ out0 = __msa_fmax_w(__msa_fmin_w(in0, max), min); \
+ out1 = __msa_fmax_w(__msa_fmin_w(in1, max), min); \
+ out2 = __msa_fmax_w(__msa_fmin_w(in2, max), min); \
+ out3 = __msa_fmax_w(__msa_fmin_w(in3, max), min); \
+ }
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_CPU_MIPS_COMMON_MACROS_MSA_H_
diff --git a/chromium/third_party/blink/renderer/platform/cross_origin_attribute_value.h b/chromium/third_party/blink/renderer/platform/cross_origin_attribute_value.h
new file mode 100644
index 00000000000..0f59a05ce3f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/cross_origin_attribute_value.h
@@ -0,0 +1,18 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_CROSS_ORIGIN_ATTRIBUTE_VALUE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_CROSS_ORIGIN_ATTRIBUTE_VALUE_H_
+
+namespace blink {
+
+enum CrossOriginAttributeValue {
+ kCrossOriginAttributeNotSet,
+ kCrossOriginAttributeAnonymous,
+ kCrossOriginAttributeUseCredentials,
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/cross_thread_copier.cc b/chromium/third_party/blink/renderer/platform/cross_thread_copier.cc
new file mode 100644
index 00000000000..240eb81f6a4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/cross_thread_copier.cc
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/renderer/platform/cross_thread_copier.h"
+
+#include <memory>
+#include "third_party/blink/renderer/platform/loader/fetch/resource_error.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+CrossThreadCopier<KURL>::Type CrossThreadCopier<KURL>::Copy(const KURL& url) {
+ return url.Copy();
+}
+
+CrossThreadCopier<String>::Type CrossThreadCopier<String>::Copy(
+ const String& str) {
+ return str.IsolatedCopy();
+}
+
+CrossThreadCopier<ResourceError>::Type CrossThreadCopier<ResourceError>::Copy(
+ const ResourceError& error) {
+ return error.Copy();
+}
+
+CrossThreadCopier<ResourceRequest>::Type
+CrossThreadCopier<ResourceRequest>::Copy(const ResourceRequest& request) {
+ return WTF::Passed(request.CopyData());
+}
+
+CrossThreadCopier<ResourceResponse>::Type
+CrossThreadCopier<ResourceResponse>::Copy(const ResourceResponse& response) {
+ return WTF::Passed(response.CopyData());
+}
+
+// Test CrossThreadCopier using static_assert.
+
+// Verify that ThreadSafeRefCounted objects get handled correctly.
+class CopierThreadSafeRefCountedTest
+ : public ThreadSafeRefCounted<CopierThreadSafeRefCountedTest> {};
+
+// Add a generic specialization which will let's us verify that no other
+// template matches.
+template <typename T>
+struct CrossThreadCopierBase<T, false> {
+ typedef int Type;
+};
+
+static_assert((std::is_same<scoped_refptr<CopierThreadSafeRefCountedTest>,
+ CrossThreadCopier<scoped_refptr<
+ CopierThreadSafeRefCountedTest>>::Type>::value),
+ "RefPtr + ThreadSafeRefCounted should pass CrossThreadCopier");
+static_assert(
+ (std::is_same<
+ int,
+ CrossThreadCopier<CopierThreadSafeRefCountedTest*>::Type>::value),
+ "Raw pointer + ThreadSafeRefCounted should NOT pass CrossThreadCopier");
+
+// Verify that RefCounted objects only match our generic template which exposes
+// Type as int.
+class CopierRefCountedTest : public RefCounted<CopierRefCountedTest> {};
+
+static_assert(
+ (std::is_same<int, CrossThreadCopier<CopierRefCountedTest*>::Type>::value),
+ "Raw pointer + RefCounted should NOT pass CrossThreadCopier");
+
+// Verify that std::unique_ptr gets passed through.
+static_assert(
+ (std::is_same<std::unique_ptr<float>,
+ CrossThreadCopier<std::unique_ptr<float>>::Type>::value),
+ "std::unique_ptr test");
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/cross_thread_copier.h b/chromium/third_party/blink/renderer/platform/cross_thread_copier.h
new file mode 100644
index 00000000000..1b4880b03da
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/cross_thread_copier.h
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2009, 2010 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_CROSS_THREAD_COPIER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_CROSS_THREAD_COPIER_H_
+
+#include <memory>
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "mojo/public/cpp/bindings/interface_ptr_info.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "third_party/blink/public/common/message_port/message_port_channel.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h" // FunctionThreadAffinity
+#include "third_party/blink/renderer/platform/wtf/type_traits.h"
+
+namespace base {
+template <typename, typename>
+class RefCountedThreadSafe;
+}
+
+class SkRefCnt;
+template <typename T>
+class sk_sp;
+
+namespace WTF {
+
+template <typename T>
+class PassedWrapper;
+}
+
+namespace blink {
+
+class IntRect;
+class IntSize;
+class KURL;
+class ResourceError;
+class ResourceRequest;
+class ResourceResponse;
+struct CrossThreadResourceResponseData;
+struct CrossThreadResourceRequestData;
+template <typename T>
+class CrossThreadPersistent;
+template <typename T>
+class CrossThreadWeakPersistent;
+
+template <typename T>
+struct CrossThreadCopierPassThrough {
+ STATIC_ONLY(CrossThreadCopierPassThrough);
+ typedef T Type;
+ static Type Copy(const T& parameter) { return parameter; }
+};
+
+template <typename T, bool isArithmeticOrEnum>
+struct CrossThreadCopierBase;
+
+// Arithmetic values (integers or floats) and enums can be safely copied.
+template <typename T>
+struct CrossThreadCopierBase<T, true> : public CrossThreadCopierPassThrough<T> {
+ STATIC_ONLY(CrossThreadCopierBase);
+};
+
+template <typename T>
+struct CrossThreadCopier
+ : public CrossThreadCopierBase<T,
+ std::is_arithmetic<T>::value ||
+ std::is_enum<T>::value> {
+ STATIC_ONLY(CrossThreadCopier);
+};
+
+// CrossThreadCopier specializations follow.
+template <typename T>
+struct CrossThreadCopier<WTF::RetainedRefWrapper<T>> {
+ STATIC_ONLY(CrossThreadCopier);
+ static_assert(WTF::IsSubclassOfTemplate<T, base::RefCountedThreadSafe>::value,
+ "scoped_refptr<T> can be passed across threads only if T is "
+ "WTF::ThreadSafeRefCounted or base::RefCountedThreadSafe.");
+ using Type = WTF::RetainedRefWrapper<T>;
+ static Type Copy(Type pointer) { return pointer; }
+};
+template <typename T>
+struct CrossThreadCopier<scoped_refptr<T>> {
+ STATIC_ONLY(CrossThreadCopier);
+ static_assert(WTF::IsSubclassOfTemplate<T, base::RefCountedThreadSafe>::value,
+ "scoped_refptr<T> can be passed across threads only if T is "
+ "WTF::ThreadSafeRefCounted or base::RefCountedThreadSafe.");
+ using Type = scoped_refptr<T>;
+ static scoped_refptr<T> Copy(scoped_refptr<T> pointer) { return pointer; }
+};
+template <typename T>
+struct CrossThreadCopier<sk_sp<T>>
+ : public CrossThreadCopierPassThrough<sk_sp<T>> {
+ STATIC_ONLY(CrossThreadCopier);
+ static_assert(std::is_base_of<SkRefCnt, T>::value,
+ "sk_sp<T> can be passed across threads only if T is SkRefCnt.");
+};
+
+// nullptr_t can be passed through without any changes.
+template <>
+struct CrossThreadCopier<std::nullptr_t>
+ : public CrossThreadCopierPassThrough<std::nullptr_t> {
+ STATIC_ONLY(CrossThreadCopier);
+};
+
+// To allow a type to be passed across threads using its copy constructor, add a
+// forward declaration of the type and provide a specialization of
+// CrossThreadCopier<T> in this file, like IntRect below.
+template <>
+struct CrossThreadCopier<IntRect>
+ : public CrossThreadCopierPassThrough<IntRect> {
+ STATIC_ONLY(CrossThreadCopier);
+};
+
+template <>
+struct CrossThreadCopier<IntSize>
+ : public CrossThreadCopierPassThrough<IntSize> {
+ STATIC_ONLY(CrossThreadCopier);
+};
+
+template <typename T, typename Deleter>
+struct CrossThreadCopier<std::unique_ptr<T, Deleter>> {
+ STATIC_ONLY(CrossThreadCopier);
+ using Type = std::unique_ptr<T, Deleter>;
+ static std::unique_ptr<T, Deleter> Copy(std::unique_ptr<T, Deleter> pointer) {
+ return pointer; // This is in fact a move.
+ }
+};
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+struct CrossThreadCopier<
+ Vector<std::unique_ptr<T>, inlineCapacity, Allocator>> {
+ STATIC_ONLY(CrossThreadCopier);
+ using Type = Vector<std::unique_ptr<T>, inlineCapacity, Allocator>;
+ static Type Copy(Type pointer) {
+ return pointer; // This is in fact a move.
+ }
+};
+
+template <size_t inlineCapacity, typename Allocator>
+struct CrossThreadCopier<Vector<uint64_t, inlineCapacity, Allocator>> {
+ STATIC_ONLY(CrossThreadCopier);
+ using Type = Vector<uint64_t, inlineCapacity, Allocator>;
+ static Type Copy(Type value) { return value; }
+};
+
+template <typename T>
+struct CrossThreadCopier<CrossThreadPersistent<T>>
+ : public CrossThreadCopierPassThrough<CrossThreadPersistent<T>> {
+ STATIC_ONLY(CrossThreadCopier);
+};
+
+template <typename T>
+struct CrossThreadCopier<CrossThreadWeakPersistent<T>>
+ : public CrossThreadCopierPassThrough<CrossThreadWeakPersistent<T>> {
+ STATIC_ONLY(CrossThreadCopier);
+};
+
+template <typename T>
+struct CrossThreadCopier<WTF::CrossThreadUnretainedWrapper<T>>
+ : public CrossThreadCopierPassThrough<
+ WTF::CrossThreadUnretainedWrapper<T>> {
+ STATIC_ONLY(CrossThreadCopier);
+};
+
+template <typename T>
+struct CrossThreadCopier<base::WeakPtr<T>>
+ : public CrossThreadCopierPassThrough<base::WeakPtr<T>> {
+ STATIC_ONLY(CrossThreadCopier);
+};
+
+template <typename T>
+struct CrossThreadCopier<WTF::PassedWrapper<T>> {
+ STATIC_ONLY(CrossThreadCopier);
+ using Type = WTF::PassedWrapper<typename CrossThreadCopier<T>::Type>;
+ static Type Copy(WTF::PassedWrapper<T>&& value) {
+ return WTF::Passed(CrossThreadCopier<T>::Copy(value.MoveOut()));
+ }
+};
+
+template <typename Signature>
+struct CrossThreadCopier<WTF::CrossThreadFunction<Signature>> {
+ STATIC_ONLY(CrossThreadCopier);
+ using Type = WTF::CrossThreadFunction<Signature>;
+ static Type Copy(Type&& value) { return std::move(value); }
+};
+
+template <>
+struct CrossThreadCopier<KURL> {
+ STATIC_ONLY(CrossThreadCopier);
+ typedef KURL Type;
+ PLATFORM_EXPORT static Type Copy(const KURL&);
+};
+
+template <>
+struct CrossThreadCopier<String> {
+ STATIC_ONLY(CrossThreadCopier);
+ typedef String Type;
+ PLATFORM_EXPORT static Type Copy(const String&);
+};
+
+template <>
+struct CrossThreadCopier<ResourceError> {
+ STATIC_ONLY(CrossThreadCopier);
+ typedef ResourceError Type;
+ PLATFORM_EXPORT static Type Copy(const ResourceError&);
+};
+
+template <>
+struct CrossThreadCopier<ResourceRequest> {
+ STATIC_ONLY(CrossThreadCopier);
+ typedef WTF::PassedWrapper<std::unique_ptr<CrossThreadResourceRequestData>>
+ Type;
+ PLATFORM_EXPORT static Type Copy(const ResourceRequest&);
+};
+
+template <>
+struct CrossThreadCopier<ResourceResponse> {
+ STATIC_ONLY(CrossThreadCopier);
+ typedef WTF::PassedWrapper<std::unique_ptr<CrossThreadResourceResponseData>>
+ Type;
+ PLATFORM_EXPORT static Type Copy(const ResourceResponse&);
+};
+
+// mojo::InterfacePtrInfo is a cross-thread safe mojo::InterfacePtr.
+template <typename Interface>
+struct CrossThreadCopier<mojo::InterfacePtrInfo<Interface>> {
+ STATIC_ONLY(CrossThreadCopier);
+ using Type = mojo::InterfacePtrInfo<Interface>;
+ static Type Copy(Type ptr_info) {
+ return ptr_info; // This is in fact a move.
+ }
+};
+
+template <typename Interface>
+struct CrossThreadCopier<mojo::InterfaceRequest<Interface>> {
+ STATIC_ONLY(CrossThreadCopier);
+ using Type = mojo::InterfaceRequest<Interface>;
+ static Type Copy(Type request) {
+ return request; // This is in fact a move.
+ }
+};
+
+template <>
+struct CrossThreadCopier<MessagePortChannel> {
+ STATIC_ONLY(CrossThreadCopier);
+ using Type = MessagePortChannel;
+ static Type Copy(Type pointer) {
+ return pointer; // This is in fact a move.
+ }
+};
+
+template <size_t inlineCapacity, typename Allocator>
+struct CrossThreadCopier<
+ Vector<MessagePortChannel, inlineCapacity, Allocator>> {
+ STATIC_ONLY(CrossThreadCopier);
+ using Type = Vector<MessagePortChannel, inlineCapacity, Allocator>;
+ static Type Copy(Type pointer) {
+ return pointer; // This is in fact a move.
+ }
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_CROSS_THREAD_COPIER_H_
diff --git a/chromium/third_party/blink/renderer/platform/cross_thread_functional.h b/chromium/third_party/blink/renderer/platform/cross_thread_functional.h
new file mode 100644
index 00000000000..4e9db67273b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/cross_thread_functional.h
@@ -0,0 +1,46 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_CROSS_THREAD_FUNCTIONAL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_CROSS_THREAD_FUNCTIONAL_H_
+
+#include <type_traits>
+#include "base/bind.h"
+#include "third_party/blink/renderer/platform/cross_thread_copier.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+
+namespace blink {
+
+// CrossThreadBind() is Bind() for cross-thread task posting.
+// CrossThreadBind() applies CrossThreadCopier to the arguments.
+//
+// Example:
+// void Func1(int, const String&);
+// f = CrossThreadBind(&Func1, 42, str);
+// Func1(42, str2) will be called when |f()| is executed,
+// where |str2| is a deep copy of |str| (created by str.IsolatedCopy()).
+//
+// CrossThreadBind(str) is similar to Bind(str.IsolatedCopy()), but the latter
+// is NOT thread-safe due to temporary objects (https://crbug.com/390851).
+//
+// Don't (if you pass the task across threads):
+// Bind(&Func1, 42, str);
+// Bind(&Func1, 42, str.IsolatedCopy());
+
+template <typename FunctionType, typename... Ps>
+WTF::CrossThreadFunction<base::MakeUnboundRunType<FunctionType, Ps...>>
+CrossThreadBind(FunctionType function, Ps&&... parameters) {
+ static_assert(
+ WTF::internal::CheckGCedTypeRestrictions<std::index_sequence_for<Ps...>,
+ std::decay_t<Ps>...>::ok,
+ "A bound argument uses a bad pattern.");
+ using UnboundRunType = base::MakeUnboundRunType<FunctionType, Ps...>;
+ return WTF::CrossThreadFunction<UnboundRunType>(
+ base::Bind(function, CrossThreadCopier<std::decay_t<Ps>>::Copy(
+ std::forward<Ps>(parameters))...));
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_CROSS_THREAD_FUNCTIONAL_H_
diff --git a/chromium/third_party/blink/renderer/platform/crypto.cc b/chromium/third_party/blink/renderer/platform/crypto.cc
new file mode 100644
index 00000000000..2e522aa1e6f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/crypto.cc
@@ -0,0 +1,70 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/crypto.h"
+
+#include <memory>
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_crypto.h"
+#include "third_party/blink/public/platform/web_crypto_algorithm.h"
+
+namespace blink {
+
+static WebCryptoAlgorithmId ToWebCryptoAlgorithmId(HashAlgorithm algorithm) {
+ switch (algorithm) {
+ case kHashAlgorithmSha1:
+ return kWebCryptoAlgorithmIdSha1;
+ case kHashAlgorithmSha256:
+ return kWebCryptoAlgorithmIdSha256;
+ case kHashAlgorithmSha384:
+ return kWebCryptoAlgorithmIdSha384;
+ case kHashAlgorithmSha512:
+ return kWebCryptoAlgorithmIdSha512;
+ };
+
+ NOTREACHED();
+ return kWebCryptoAlgorithmIdSha256;
+}
+
+bool ComputeDigest(HashAlgorithm algorithm,
+ const char* digestable,
+ size_t length,
+ DigestValue& digest_result) {
+ WebCryptoAlgorithmId algorithm_id = ToWebCryptoAlgorithmId(algorithm);
+ WebCrypto* crypto = Platform::Current()->Crypto();
+ unsigned char* result;
+ unsigned result_size;
+
+ DCHECK(crypto);
+
+ std::unique_ptr<WebCryptoDigestor> digestor =
+ crypto->CreateDigestor(algorithm_id);
+ DCHECK(digestor);
+ if (!digestor->Consume(reinterpret_cast<const unsigned char*>(digestable),
+ length) ||
+ !digestor->Finish(result, result_size))
+ return false;
+
+ digest_result.Append(static_cast<uint8_t*>(result), result_size);
+ return true;
+}
+
+std::unique_ptr<WebCryptoDigestor> CreateDigestor(HashAlgorithm algorithm) {
+ return Platform::Current()->Crypto()->CreateDigestor(
+ ToWebCryptoAlgorithmId(algorithm));
+}
+
+void FinishDigestor(WebCryptoDigestor* digestor, DigestValue& digest_result) {
+ unsigned char* result = nullptr;
+ unsigned result_size = 0;
+
+ if (!digestor->Finish(result, result_size))
+ return;
+
+ DCHECK(result);
+
+ digest_result.Append(static_cast<uint8_t*>(result), result_size);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/crypto.h b/chromium/third_party/blink/renderer/platform/crypto.h
new file mode 100644
index 00000000000..ddc62627b23
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/crypto.h
@@ -0,0 +1,77 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// TODO(jww) The original Blink-style header guard for this file conflicts with
+// the header guard in Source/modules/crypto/Crypto.h, so this is a
+// Chromium-style header guard instead. There is now a bug
+// (https://crbug.com/360121) to track a proposal to change all header guards
+// to a similar style. Thus, whenever that is resolved, this header guard
+// should be changed to whatever style is agreed upon.
+#ifndef SOURCE_PLATFORM_CRYPTO_H_
+#define SOURCE_PLATFORM_CRYPTO_H_
+
+#include <memory>
+#include "third_party/blink/public/platform/web_crypto.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/string_hasher.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+static const size_t kMaxDigestSize = 64;
+typedef Vector<uint8_t, kMaxDigestSize> DigestValue;
+
+const size_t kSha1HashSize = 20;
+enum HashAlgorithm {
+ kHashAlgorithmSha1,
+ kHashAlgorithmSha256,
+ kHashAlgorithmSha384,
+ kHashAlgorithmSha512
+};
+
+PLATFORM_EXPORT bool ComputeDigest(HashAlgorithm,
+ const char* digestable,
+ size_t length,
+ DigestValue& digest_result);
+// Note: this will never return null.
+PLATFORM_EXPORT std::unique_ptr<WebCryptoDigestor> CreateDigestor(
+ HashAlgorithm);
+PLATFORM_EXPORT void FinishDigestor(WebCryptoDigestor*,
+ DigestValue& digest_result);
+
+} // namespace blink
+
+namespace WTF {
+
+struct DigestValueHash {
+ STATIC_ONLY(DigestValueHash);
+ static unsigned GetHash(const blink::DigestValue& v) {
+ return StringHasher::ComputeHash(v.data(), v.size());
+ }
+ static bool Equal(const blink::DigestValue& a, const blink::DigestValue& b) {
+ return a == b;
+ };
+ static const bool safe_to_compare_to_empty_or_deleted = true;
+};
+template <>
+struct DefaultHash<blink::DigestValue> {
+ STATIC_ONLY(DefaultHash);
+ typedef DigestValueHash Hash;
+};
+
+template <>
+struct DefaultHash<blink::HashAlgorithm> {
+ STATIC_ONLY(DefaultHash);
+ typedef IntHash<blink::HashAlgorithm> Hash;
+};
+template <>
+struct HashTraits<blink::HashAlgorithm>
+ : UnsignedWithZeroKeyHashTraits<blink::HashAlgorithm> {
+ STATIC_ONLY(HashTraits);
+};
+
+} // namespace WTF
+#endif // SOURCE_PLATFORM_CRYPTO_H_
diff --git a/chromium/third_party/blink/renderer/platform/crypto_result.h b/chromium/third_party/blink/renderer/platform/crypto_result.h
new file mode 100644
index 00000000000..b206f7bc46b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/crypto_result.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_CRYPTO_RESULT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_CRYPTO_RESULT_H_
+
+#include "third_party/blink/public/platform/web_crypto.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
+
+namespace blink {
+
+// Result cancellation status interface to allow non-Blink webcrypto threads
+// to query for status.
+class CryptoResultCancel : public ThreadSafeRefCounted<CryptoResultCancel> {
+ public:
+ virtual ~CryptoResultCancel() = default;
+
+ virtual bool Cancelled() const = 0;
+};
+
+// Receives notification of completion of the crypto operation.
+class PLATFORM_EXPORT CryptoResult
+ : public GarbageCollectedFinalized<CryptoResult> {
+ public:
+ virtual ~CryptoResult() = default;
+
+ virtual void CompleteWithError(WebCryptoErrorType, const WebString&) = 0;
+ virtual void CompleteWithBuffer(const void* bytes, unsigned bytes_size) = 0;
+ virtual void CompleteWithJson(const char* utf8_data, unsigned length) = 0;
+ virtual void CompleteWithBoolean(bool) = 0;
+ virtual void CompleteWithKey(const WebCryptoKey&) = 0;
+ virtual void CompleteWithKeyPair(const WebCryptoKey& public_key,
+ const WebCryptoKey& private_key) = 0;
+
+ virtual void Trace(blink::Visitor* visitor) {}
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_CRYPTO_RESULT_H_
diff --git a/chromium/third_party/blink/renderer/platform/cursor.cc b/chromium/third_party/blink/renderer/platform/cursor.cc
new file mode 100644
index 00000000000..8e45f8e10de
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/cursor.cc
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) 2010 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 "third_party/blink/public/platform/web_cursor_info.h"
+#include "third_party/blink/renderer/platform/cursor.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+namespace blink {
+
+IntPoint DetermineHotSpot(Image* image,
+ bool hot_spot_specified,
+ const IntPoint& specified_hot_spot) {
+ if (image->IsNull())
+ return IntPoint();
+
+ IntRect image_rect = image->Rect();
+
+ // Hot spot must be inside cursor rectangle.
+ if (hot_spot_specified) {
+ if (image_rect.Contains(specified_hot_spot)) {
+ return specified_hot_spot;
+ }
+
+ return IntPoint(clampTo<int>(specified_hot_spot.X(), image_rect.X(),
+ image_rect.MaxX() - 1),
+ clampTo<int>(specified_hot_spot.Y(), image_rect.Y(),
+ image_rect.MaxY() - 1));
+ }
+
+ // If hot spot is not specified externally, it can be extracted from some
+ // image formats (e.g. .cur).
+ IntPoint intrinsic_hot_spot;
+ bool image_has_intrinsic_hot_spot = image->GetHotSpot(intrinsic_hot_spot);
+ if (image_has_intrinsic_hot_spot && image_rect.Contains(intrinsic_hot_spot))
+ return intrinsic_hot_spot;
+
+ // If neither is provided, use a default value of (0, 0).
+ return IntPoint();
+}
+
+Cursor::Cursor(Image* image, bool hot_spot_specified, const IntPoint& hot_spot)
+ : type_(kCustom),
+ image_(image),
+ hot_spot_(DetermineHotSpot(image, hot_spot_specified, hot_spot)),
+ image_scale_factor_(1) {}
+
+Cursor::Cursor(Image* image,
+ bool hot_spot_specified,
+ const IntPoint& hot_spot,
+ float scale)
+ : type_(kCustom),
+ image_(image),
+ hot_spot_(DetermineHotSpot(image, hot_spot_specified, hot_spot)),
+ image_scale_factor_(scale) {}
+
+Cursor::Cursor(Type type) : type_(type), image_scale_factor_(1) {}
+
+Cursor::Cursor(const Cursor& other) = default;
+
+Cursor& Cursor::operator=(const Cursor& other) {
+ type_ = other.type_;
+ image_ = other.image_;
+ hot_spot_ = other.hot_spot_;
+ image_scale_factor_ = other.image_scale_factor_;
+ return *this;
+}
+
+Cursor::~Cursor() = default;
+
+const Cursor& PointerCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kPointer));
+ return c;
+}
+
+const Cursor& CrossCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kCross));
+ return c;
+}
+
+const Cursor& HandCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kHand));
+ return c;
+}
+
+const Cursor& MoveCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kMove));
+ return c;
+}
+
+const Cursor& VerticalTextCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kVerticalText));
+ return c;
+}
+
+const Cursor& CellCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kCell));
+ return c;
+}
+
+const Cursor& ContextMenuCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kContextMenu));
+ return c;
+}
+
+const Cursor& AliasCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kAlias));
+ return c;
+}
+
+const Cursor& ZoomInCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kZoomIn));
+ return c;
+}
+
+const Cursor& ZoomOutCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kZoomOut));
+ return c;
+}
+
+const Cursor& CopyCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kCopy));
+ return c;
+}
+
+const Cursor& NoneCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kNone));
+ return c;
+}
+
+const Cursor& ProgressCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kProgress));
+ return c;
+}
+
+const Cursor& NoDropCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kNoDrop));
+ return c;
+}
+
+const Cursor& NotAllowedCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kNotAllowed));
+ return c;
+}
+
+const Cursor& IBeamCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kIBeam));
+ return c;
+}
+
+const Cursor& WaitCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kWait));
+ return c;
+}
+
+const Cursor& HelpCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kHelp));
+ return c;
+}
+
+const Cursor& EastResizeCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kEastResize));
+ return c;
+}
+
+const Cursor& NorthResizeCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kNorthResize));
+ return c;
+}
+
+const Cursor& NorthEastResizeCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kNorthEastResize));
+ return c;
+}
+
+const Cursor& NorthWestResizeCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kNorthWestResize));
+ return c;
+}
+
+const Cursor& SouthResizeCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kSouthResize));
+ return c;
+}
+
+const Cursor& SouthEastResizeCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kSouthEastResize));
+ return c;
+}
+
+const Cursor& SouthWestResizeCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kSouthWestResize));
+ return c;
+}
+
+const Cursor& WestResizeCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kWestResize));
+ return c;
+}
+
+const Cursor& NorthSouthResizeCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kNorthSouthResize));
+ return c;
+}
+
+const Cursor& EastWestResizeCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kEastWestResize));
+ return c;
+}
+
+const Cursor& NorthEastSouthWestResizeCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kNorthEastSouthWestResize));
+ return c;
+}
+
+const Cursor& NorthWestSouthEastResizeCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kNorthWestSouthEastResize));
+ return c;
+}
+
+const Cursor& ColumnResizeCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kColumnResize));
+ return c;
+}
+
+const Cursor& RowResizeCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kRowResize));
+ return c;
+}
+
+const Cursor& MiddlePanningCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kMiddlePanning));
+ return c;
+}
+
+const Cursor& EastPanningCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kEastPanning));
+ return c;
+}
+
+const Cursor& NorthPanningCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kNorthPanning));
+ return c;
+}
+
+const Cursor& NorthEastPanningCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kNorthEastPanning));
+ return c;
+}
+
+const Cursor& NorthWestPanningCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kNorthWestPanning));
+ return c;
+}
+
+const Cursor& SouthPanningCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kSouthPanning));
+ return c;
+}
+
+const Cursor& SouthEastPanningCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kSouthEastPanning));
+ return c;
+}
+
+const Cursor& SouthWestPanningCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kSouthWestPanning));
+ return c;
+}
+
+const Cursor& WestPanningCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kWestPanning));
+ return c;
+}
+
+const Cursor& GrabCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kGrab));
+ return c;
+}
+
+const Cursor& GrabbingCursor() {
+ DEFINE_STATIC_LOCAL(Cursor, c, (Cursor::kGrabbing));
+ return c;
+}
+
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypePointer, Cursor::kPointer);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeCross, Cursor::kCross);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeHand, Cursor::kHand);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeIBeam, Cursor::kIBeam);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeWait, Cursor::kWait);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeHelp, Cursor::kHelp);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeEastResize, Cursor::kEastResize);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeNorthResize, Cursor::kNorthResize);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeNorthEastResize,
+ Cursor::kNorthEastResize);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeNorthWestResize,
+ Cursor::kNorthWestResize);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeSouthResize, Cursor::kSouthResize);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeSouthEastResize,
+ Cursor::kSouthEastResize);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeSouthWestResize,
+ Cursor::kSouthWestResize);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeWestResize, Cursor::kWestResize);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeNorthSouthResize,
+ Cursor::kNorthSouthResize);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeEastWestResize, Cursor::kEastWestResize);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeNorthEastSouthWestResize,
+ Cursor::kNorthEastSouthWestResize);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeNorthWestSouthEastResize,
+ Cursor::kNorthWestSouthEastResize);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeColumnResize, Cursor::kColumnResize);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeRowResize, Cursor::kRowResize);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeMiddlePanning, Cursor::kMiddlePanning);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeEastPanning, Cursor::kEastPanning);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeNorthPanning, Cursor::kNorthPanning);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeNorthEastPanning,
+ Cursor::kNorthEastPanning);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeNorthWestPanning,
+ Cursor::kNorthWestPanning);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeSouthPanning, Cursor::kSouthPanning);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeSouthEastPanning,
+ Cursor::kSouthEastPanning);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeSouthWestPanning,
+ Cursor::kSouthWestPanning);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeWestPanning, Cursor::kWestPanning);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeMove, Cursor::kMove);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeVerticalText, Cursor::kVerticalText);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeCell, Cursor::kCell);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeContextMenu, Cursor::kContextMenu);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeAlias, Cursor::kAlias);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeProgress, Cursor::kProgress);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeNoDrop, Cursor::kNoDrop);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeCopy, Cursor::kCopy);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeNone, Cursor::kNone);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeNotAllowed, Cursor::kNotAllowed);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeZoomIn, Cursor::kZoomIn);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeZoomOut, Cursor::kZoomOut);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeGrab, Cursor::kGrab);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeGrabbing, Cursor::kGrabbing);
+STATIC_ASSERT_ENUM(WebCursorInfo::kTypeCustom, Cursor::kCustom);
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/cursor.h b/chromium/third_party/blink/renderer/platform/cursor.h
new file mode 100644
index 00000000000..0646bec8b6d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/cursor.h
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2004, 2006, 2008 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_CURSOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_CURSOR_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/geometry/int_point.h"
+#include "third_party/blink/renderer/platform/graphics/image.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+// To avoid conflicts with the CreateWindow macro from the Windows SDK...
+#undef CopyCursor
+
+namespace blink {
+
+class PLATFORM_EXPORT Cursor {
+ USING_FAST_MALLOC(Cursor);
+
+ public:
+ enum Type {
+ kPointer = 0,
+ kCross,
+ kHand,
+ kIBeam,
+ kWait,
+ kHelp,
+ kEastResize,
+ kNorthResize,
+ kNorthEastResize,
+ kNorthWestResize,
+ kSouthResize,
+ kSouthEastResize,
+ kSouthWestResize,
+ kWestResize,
+ kNorthSouthResize,
+ kEastWestResize,
+ kNorthEastSouthWestResize,
+ kNorthWestSouthEastResize,
+ kColumnResize,
+ kRowResize,
+ kMiddlePanning,
+ kEastPanning,
+ kNorthPanning,
+ kNorthEastPanning,
+ kNorthWestPanning,
+ kSouthPanning,
+ kSouthEastPanning,
+ kSouthWestPanning,
+ kWestPanning,
+ kMove,
+ kVerticalText,
+ kCell,
+ kContextMenu,
+ kAlias,
+ kProgress,
+ kNoDrop,
+ kCopy,
+ kNone,
+ kNotAllowed,
+ kZoomIn,
+ kZoomOut,
+ kGrab,
+ kGrabbing,
+ kCustom
+ };
+
+ Cursor()
+ // This is an invalid Cursor and should never actually get used.
+ : type_(static_cast<Type>(-1)) {}
+
+ Cursor(Image*, bool hot_spot_specified, const IntPoint& hot_spot);
+
+ // Hot spot is in image pixels.
+ Cursor(Image*,
+ bool hot_spot_specified,
+ const IntPoint& hot_spot,
+ float image_scale_factor);
+
+ Cursor(const Cursor&);
+ ~Cursor();
+ Cursor& operator=(const Cursor&);
+
+ explicit Cursor(Type);
+ Type GetType() const {
+ DCHECK_GE(type_, 0);
+ DCHECK_LE(type_, kCustom);
+ return type_;
+ }
+ Image* GetImage() const { return image_.get(); }
+ const IntPoint& HotSpot() const { return hot_spot_; }
+ // Image scale in image pixels per logical (UI) pixel.
+ float ImageScaleFactor() const { return image_scale_factor_; }
+
+ private:
+ Type type_;
+ scoped_refptr<Image> image_;
+ IntPoint hot_spot_;
+ float image_scale_factor_;
+};
+
+PLATFORM_EXPORT IntPoint DetermineHotSpot(Image*,
+ bool hot_spot_specified,
+ const IntPoint& specified_hot_spot);
+
+PLATFORM_EXPORT const Cursor& PointerCursor();
+PLATFORM_EXPORT const Cursor& CrossCursor();
+PLATFORM_EXPORT const Cursor& HandCursor();
+PLATFORM_EXPORT const Cursor& MoveCursor();
+PLATFORM_EXPORT const Cursor& IBeamCursor();
+PLATFORM_EXPORT const Cursor& WaitCursor();
+PLATFORM_EXPORT const Cursor& HelpCursor();
+PLATFORM_EXPORT const Cursor& EastResizeCursor();
+PLATFORM_EXPORT const Cursor& NorthResizeCursor();
+PLATFORM_EXPORT const Cursor& NorthEastResizeCursor();
+PLATFORM_EXPORT const Cursor& NorthWestResizeCursor();
+PLATFORM_EXPORT const Cursor& SouthResizeCursor();
+PLATFORM_EXPORT const Cursor& SouthEastResizeCursor();
+PLATFORM_EXPORT const Cursor& SouthWestResizeCursor();
+PLATFORM_EXPORT const Cursor& WestResizeCursor();
+PLATFORM_EXPORT const Cursor& NorthSouthResizeCursor();
+PLATFORM_EXPORT const Cursor& EastWestResizeCursor();
+PLATFORM_EXPORT const Cursor& NorthEastSouthWestResizeCursor();
+PLATFORM_EXPORT const Cursor& NorthWestSouthEastResizeCursor();
+PLATFORM_EXPORT const Cursor& ColumnResizeCursor();
+PLATFORM_EXPORT const Cursor& RowResizeCursor();
+PLATFORM_EXPORT const Cursor& MiddlePanningCursor();
+PLATFORM_EXPORT const Cursor& EastPanningCursor();
+PLATFORM_EXPORT const Cursor& NorthPanningCursor();
+PLATFORM_EXPORT const Cursor& NorthEastPanningCursor();
+PLATFORM_EXPORT const Cursor& NorthWestPanningCursor();
+PLATFORM_EXPORT const Cursor& SouthPanningCursor();
+PLATFORM_EXPORT const Cursor& SouthEastPanningCursor();
+PLATFORM_EXPORT const Cursor& SouthWestPanningCursor();
+PLATFORM_EXPORT const Cursor& WestPanningCursor();
+PLATFORM_EXPORT const Cursor& VerticalTextCursor();
+PLATFORM_EXPORT const Cursor& CellCursor();
+PLATFORM_EXPORT const Cursor& ContextMenuCursor();
+PLATFORM_EXPORT const Cursor& NoDropCursor();
+PLATFORM_EXPORT const Cursor& NotAllowedCursor();
+PLATFORM_EXPORT const Cursor& ProgressCursor();
+PLATFORM_EXPORT const Cursor& AliasCursor();
+PLATFORM_EXPORT const Cursor& ZoomInCursor();
+PLATFORM_EXPORT const Cursor& ZoomOutCursor();
+PLATFORM_EXPORT const Cursor& CopyCursor();
+PLATFORM_EXPORT const Cursor& NoneCursor();
+PLATFORM_EXPORT const Cursor& GrabCursor();
+PLATFORM_EXPORT const Cursor& GrabbingCursor();
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_CURSOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/data_resource_helper.cc b/chromium/third_party/blink/renderer/platform/data_resource_helper.cc
new file mode 100644
index 00000000000..b0e6ab17f3c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/data_resource_helper.cc
@@ -0,0 +1,30 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/data_resource_helper.h"
+
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_data.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+
+namespace blink {
+
+String GetDataResourceAsASCIIString(const char* resource) {
+ StringBuilder builder;
+ const WebData& resource_data = Platform::Current()->GetDataResource(resource);
+ builder.ReserveCapacity(resource_data.size());
+ resource_data.ForEachSegment([&builder](const char* segment,
+ size_t segment_size,
+ size_t segment_offset) {
+ builder.Append(segment, segment_size);
+ return true;
+ });
+
+ String data_string = builder.ToString();
+ DCHECK(!data_string.IsEmpty());
+ DCHECK(data_string.ContainsOnlyASCII());
+ return data_string;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/data_resource_helper.h b/chromium/third_party/blink/renderer/platform/data_resource_helper.h
new file mode 100644
index 00000000000..f9811bf0056
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/data_resource_helper.h
@@ -0,0 +1,17 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_DATA_RESOURCE_HELPER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_DATA_RESOURCE_HELPER_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+PLATFORM_EXPORT String GetDataResourceAsASCIIString(const char* resource);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_DATA_RESOURCE_HELPER_H_
diff --git a/chromium/third_party/blink/renderer/platform/date_components.cc b/chromium/third_party/blink/renderer/platform/date_components.cc
new file mode 100644
index 00000000000..bc0d7f8c731
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/date_components.cc
@@ -0,0 +1,724 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/renderer/platform/date_components.h"
+
+#include <limits.h>
+#include "third_party/blink/renderer/platform/wtf/ascii_ctype.h"
+#include "third_party/blink/renderer/platform/wtf/date_math.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+// HTML5 specification defines minimum week of year is one.
+const int DateComponents::kMinimumWeekNumber = 1;
+
+// HTML5 specification defines maximum week of year is 53.
+const int DateComponents::kMaximumWeekNumber = 53;
+
+static const int kMaximumMonthInMaximumYear =
+ 8; // This is September, since months are 0 based.
+static const int kMaximumDayInMaximumMonth = 13;
+static const int kMaximumWeekInMaximumYear = 37; // The week of 275760-09-13
+
+static const int kDaysInMonth[12] = {31, 28, 31, 30, 31, 30,
+ 31, 31, 30, 31, 30, 31};
+
+// 'month' is 0-based.
+static int MaxDayOfMonth(int year, int month) {
+ if (month != 1) // February?
+ return kDaysInMonth[month];
+ return IsLeapYear(year) ? 29 : 28;
+}
+
+// 'month' is 0-based.
+static int DayOfWeek(int year, int month, int day) {
+ int shifted_month = month + 2;
+ // 2:January, 3:Feburuary, 4:March, ...
+
+ // Zeller's congruence
+ if (shifted_month <= 3) {
+ shifted_month += 12;
+ year--;
+ }
+ // 4:March, ..., 14:January, 15:February
+
+ int high_year = year / 100;
+ int low_year = year % 100;
+ // We add 6 to make the result Sunday-origin.
+ int result = (day + 13 * shifted_month / 5 + low_year + low_year / 4 +
+ high_year / 4 + 5 * high_year + 6) %
+ 7;
+ return result;
+}
+
+int DateComponents::WeekDay() const {
+ return DayOfWeek(year_, month_, month_day_);
+}
+
+int DateComponents::MaxWeekNumberInYear() const {
+ int day = DayOfWeek(year_, 0, 1); // January 1.
+ return day == kThursday || (day == kWednesday && IsLeapYear(year_))
+ ? kMaximumWeekNumber
+ : kMaximumWeekNumber - 1;
+}
+
+static unsigned CountDigits(const String& src, unsigned start) {
+ unsigned index = start;
+ for (; index < src.length(); ++index) {
+ if (!IsASCIIDigit(src[index]))
+ break;
+ }
+ return index - start;
+}
+
+// Very strict integer parser. Do not allow leading or trailing whitespace
+// unlike charactersToIntStrict().
+static bool ToInt(const String& src,
+ unsigned parse_start,
+ unsigned parse_length,
+ int& out) {
+ if (parse_start + parse_length > src.length() || !parse_length)
+ return false;
+ int value = 0;
+ unsigned current = parse_start;
+ unsigned end = current + parse_length;
+
+ // We don't need to handle negative numbers for ISO 8601.
+ for (; current < end; ++current) {
+ if (!IsASCIIDigit(src[current]))
+ return false;
+ int digit = src[current] - '0';
+ if (value > (INT_MAX - digit) / 10) // Check for overflow.
+ return false;
+ value = value * 10 + digit;
+ }
+ out = value;
+ return true;
+}
+
+bool DateComponents::ParseYear(const String& src,
+ unsigned start,
+ unsigned& end) {
+ unsigned digits_length = CountDigits(src, start);
+ // Needs at least 4 digits according to the standard.
+ if (digits_length < 4)
+ return false;
+ int year;
+ if (!ToInt(src, start, digits_length, year))
+ return false;
+ if (year < MinimumYear() || year > MaximumYear())
+ return false;
+ year_ = year;
+ end = start + digits_length;
+ return true;
+}
+
+static bool WithinHTMLDateLimits(int year, int month) {
+ if (year < DateComponents::MinimumYear())
+ return false;
+ if (year < DateComponents::MaximumYear())
+ return true;
+ return month <= kMaximumMonthInMaximumYear;
+}
+
+static bool WithinHTMLDateLimits(int year, int month, int month_day) {
+ if (year < DateComponents::MinimumYear())
+ return false;
+ if (year < DateComponents::MaximumYear())
+ return true;
+ if (month < kMaximumMonthInMaximumYear)
+ return true;
+ return month_day <= kMaximumDayInMaximumMonth;
+}
+
+static bool WithinHTMLDateLimits(int year,
+ int month,
+ int month_day,
+ int hour,
+ int minute,
+ int second,
+ int millisecond) {
+ if (year < DateComponents::MinimumYear())
+ return false;
+ if (year < DateComponents::MaximumYear())
+ return true;
+ if (month < kMaximumMonthInMaximumYear)
+ return true;
+ if (month_day < kMaximumDayInMaximumMonth)
+ return true;
+ if (month_day > kMaximumDayInMaximumMonth)
+ return false;
+ // (year, month, monthDay) =
+ // (maximumYear, maximumMonthInMaximumYear, maximumDayInMaximumMonth)
+ return !hour && !minute && !second && !millisecond;
+}
+
+bool DateComponents::AddDay(int day_diff) {
+ DCHECK(month_day_);
+
+ int day = month_day_ + day_diff;
+ if (day > MaxDayOfMonth(year_, month_)) {
+ day = month_day_;
+ int year = year_;
+ int month = month_;
+ int max_day = MaxDayOfMonth(year, month);
+ for (; day_diff > 0; --day_diff) {
+ ++day;
+ if (day > max_day) {
+ day = 1;
+ ++month;
+ if (month >= 12) { // month is 0-origin.
+ month = 0;
+ ++year;
+ }
+ max_day = MaxDayOfMonth(year, month);
+ }
+ }
+ if (!WithinHTMLDateLimits(year, month, day))
+ return false;
+ year_ = year;
+ month_ = month;
+ } else if (day < 1) {
+ int month = month_;
+ int year = year_;
+ day = month_day_;
+ for (; day_diff < 0; ++day_diff) {
+ --day;
+ if (day < 1) {
+ --month;
+ if (month < 0) {
+ month = 11;
+ --year;
+ }
+ day = MaxDayOfMonth(year, month);
+ }
+ }
+ if (!WithinHTMLDateLimits(year, month, day))
+ return false;
+ year_ = year;
+ month_ = month;
+ } else {
+ if (!WithinHTMLDateLimits(year_, month_, day))
+ return false;
+ }
+ month_day_ = day;
+ return true;
+}
+
+bool DateComponents::AddMinute(int minute) {
+ // This function is used to adjust timezone offset. So m_year, m_month,
+ // m_monthDay have values between the lower and higher limits.
+ DCHECK(WithinHTMLDateLimits(year_, month_, month_day_));
+
+ int carry;
+ // minute can be negative or greater than 59.
+ minute += minute_;
+ if (minute > 59) {
+ carry = minute / 60;
+ minute = minute % 60;
+ } else if (minute < 0) {
+ carry = (59 - minute) / 60;
+ minute += carry * 60;
+ carry = -carry;
+ DCHECK_GE(minute, 0);
+ DCHECK_LE(minute, 59);
+ } else {
+ if (!WithinHTMLDateLimits(year_, month_, month_day_, hour_, minute, second_,
+ millisecond_))
+ return false;
+ minute_ = minute;
+ return true;
+ }
+
+ int hour = hour_ + carry;
+ if (hour > 23) {
+ carry = hour / 24;
+ hour = hour % 24;
+ } else if (hour < 0) {
+ carry = (23 - hour) / 24;
+ hour += carry * 24;
+ carry = -carry;
+ DCHECK_GE(hour, 0);
+ DCHECK_LE(hour, 23);
+ } else {
+ if (!WithinHTMLDateLimits(year_, month_, month_day_, hour, minute, second_,
+ millisecond_))
+ return false;
+ minute_ = minute;
+ hour_ = hour;
+ return true;
+ }
+ if (!AddDay(carry))
+ return false;
+ if (!WithinHTMLDateLimits(year_, month_, month_day_, hour, minute, second_,
+ millisecond_))
+ return false;
+ minute_ = minute;
+ hour_ = hour;
+ return true;
+}
+
+// Parses a timezone part, and adjust year, month, monthDay, hour, minute,
+// second, millisecond.
+bool DateComponents::ParseTimeZone(const String& src,
+ unsigned start,
+ unsigned& end) {
+ if (start >= src.length())
+ return false;
+ unsigned index = start;
+ if (src[index] == 'Z') {
+ end = index + 1;
+ return true;
+ }
+
+ bool minus;
+ if (src[index] == '+')
+ minus = false;
+ else if (src[index] == '-')
+ minus = true;
+ else
+ return false;
+ ++index;
+
+ int hour;
+ int minute;
+ if (!ToInt(src, index, 2, hour) || hour < 0 || hour > 23)
+ return false;
+ index += 2;
+
+ if (index >= src.length() || src[index] != ':')
+ return false;
+ ++index;
+
+ if (!ToInt(src, index, 2, minute) || minute < 0 || minute > 59)
+ return false;
+ index += 2;
+
+ if (minus) {
+ hour = -hour;
+ minute = -minute;
+ }
+
+ // Subtract the timezone offset.
+ if (!AddMinute(-(hour * 60 + minute)))
+ return false;
+ end = index;
+ return true;
+}
+
+bool DateComponents::ParseMonth(const String& src,
+ unsigned start,
+ unsigned& end) {
+ unsigned index;
+ if (!ParseYear(src, start, index))
+ return false;
+ if (index >= src.length() || src[index] != '-')
+ return false;
+ ++index;
+
+ int month;
+ if (!ToInt(src, index, 2, month) || month < 1 || month > 12)
+ return false;
+ --month;
+ if (!WithinHTMLDateLimits(year_, month))
+ return false;
+ month_ = month;
+ end = index + 2;
+ type_ = kMonth;
+ return true;
+}
+
+bool DateComponents::ParseDate(const String& src,
+ unsigned start,
+ unsigned& end) {
+ unsigned index;
+ if (!ParseMonth(src, start, index))
+ return false;
+ // '-' and 2-digits are needed.
+ if (index + 2 >= src.length())
+ return false;
+ if (src[index] != '-')
+ return false;
+ ++index;
+
+ int day;
+ if (!ToInt(src, index, 2, day) || day < 1 ||
+ day > MaxDayOfMonth(year_, month_))
+ return false;
+ if (!WithinHTMLDateLimits(year_, month_, day))
+ return false;
+ month_day_ = day;
+ end = index + 2;
+ type_ = kDate;
+ return true;
+}
+
+bool DateComponents::ParseWeek(const String& src,
+ unsigned start,
+ unsigned& end) {
+ unsigned index;
+ if (!ParseYear(src, start, index))
+ return false;
+
+ // 4 characters ('-' 'W' digit digit) are needed.
+ if (index + 3 >= src.length())
+ return false;
+ if (src[index] != '-')
+ return false;
+ ++index;
+ if (src[index] != 'W')
+ return false;
+ ++index;
+
+ int week;
+ if (!ToInt(src, index, 2, week) || week < kMinimumWeekNumber ||
+ week > MaxWeekNumberInYear())
+ return false;
+ if (year_ == MaximumYear() && week > kMaximumWeekInMaximumYear)
+ return false;
+ week_ = week;
+ end = index + 2;
+ type_ = kWeek;
+ return true;
+}
+
+bool DateComponents::ParseTime(const String& src,
+ unsigned start,
+ unsigned& end) {
+ int hour;
+ if (!ToInt(src, start, 2, hour) || hour < 0 || hour > 23)
+ return false;
+ unsigned index = start + 2;
+ if (index >= src.length())
+ return false;
+ if (src[index] != ':')
+ return false;
+ ++index;
+
+ int minute;
+ if (!ToInt(src, index, 2, minute) || minute < 0 || minute > 59)
+ return false;
+ index += 2;
+
+ int second = 0;
+ int millisecond = 0;
+ // Optional second part.
+ // Do not return with false because the part is optional.
+ if (index + 2 < src.length() && src[index] == ':') {
+ if (ToInt(src, index + 1, 2, second) && second >= 0 && second <= 59) {
+ index += 3;
+
+ // Optional fractional second part.
+ if (index < src.length() && src[index] == '.') {
+ unsigned digits_length = CountDigits(src, index + 1);
+ if (digits_length > 0) {
+ ++index;
+ bool ok;
+ if (digits_length == 1) {
+ ok = ToInt(src, index, 1, millisecond);
+ millisecond *= 100;
+ } else if (digits_length == 2) {
+ ok = ToInt(src, index, 2, millisecond);
+ millisecond *= 10;
+ } else if (digits_length == 3) {
+ ok = ToInt(src, index, 3, millisecond);
+ } else { // digitsLength >= 4
+ return false;
+ }
+ DCHECK(ok);
+ index += digits_length;
+ }
+ }
+ }
+ }
+ hour_ = hour;
+ minute_ = minute;
+ second_ = second;
+ millisecond_ = millisecond;
+ end = index;
+ type_ = kTime;
+ return true;
+}
+
+bool DateComponents::ParseDateTimeLocal(const String& src,
+ unsigned start,
+ unsigned& end) {
+ unsigned index;
+ if (!ParseDate(src, start, index))
+ return false;
+ if (index >= src.length())
+ return false;
+ if (src[index] != 'T')
+ return false;
+ ++index;
+ if (!ParseTime(src, index, end))
+ return false;
+ if (!WithinHTMLDateLimits(year_, month_, month_day_, hour_, minute_, second_,
+ millisecond_))
+ return false;
+ type_ = kDateTimeLocal;
+ return true;
+}
+
+static inline double PositiveFmod(double value, double divider) {
+ double remainder = fmod(value, divider);
+ return remainder < 0 ? remainder + divider : remainder;
+}
+
+void DateComponents::SetMillisecondsSinceMidnightInternal(double ms_in_day) {
+ DCHECK_GE(ms_in_day, 0);
+ DCHECK_LT(ms_in_day, kMsPerDay);
+ millisecond_ = static_cast<int>(fmod(ms_in_day, kMsPerSecond));
+ double value = std::floor(ms_in_day / kMsPerSecond);
+ second_ = static_cast<int>(fmod(value, kSecondsPerMinute));
+ value = std::floor(value / kSecondsPerMinute);
+ minute_ = static_cast<int>(fmod(value, kMinutesPerHour));
+ hour_ = static_cast<int>(value / kMinutesPerHour);
+}
+
+bool DateComponents::SetMillisecondsSinceEpochForDateInternal(double ms) {
+ year_ = MsToYear(ms);
+ int year_day = DayInYear(ms, year_);
+ month_ = MonthFromDayInYear(year_day, IsLeapYear(year_));
+ month_day_ = DayInMonthFromDayInYear(year_day, IsLeapYear(year_));
+ return true;
+}
+
+bool DateComponents::SetMillisecondsSinceEpochForDate(double ms) {
+ type_ = kInvalid;
+ if (!std::isfinite(ms))
+ return false;
+ if (!SetMillisecondsSinceEpochForDateInternal(round(ms)))
+ return false;
+ if (!WithinHTMLDateLimits(year_, month_, month_day_))
+ return false;
+ type_ = kDate;
+ return true;
+}
+
+bool DateComponents::SetMillisecondsSinceEpochForDateTime(double ms) {
+ type_ = kInvalid;
+ if (!std::isfinite(ms))
+ return false;
+ ms = round(ms);
+ SetMillisecondsSinceMidnightInternal(PositiveFmod(ms, kMsPerDay));
+ if (!SetMillisecondsSinceEpochForDateInternal(ms))
+ return false;
+ if (!WithinHTMLDateLimits(year_, month_, month_day_, hour_, minute_, second_,
+ millisecond_))
+ return false;
+ type_ = kDateTime;
+ return true;
+}
+
+bool DateComponents::SetMillisecondsSinceEpochForDateTimeLocal(double ms) {
+ // Internal representation of DateTimeLocal is the same as DateTime except
+ // m_type.
+ if (!SetMillisecondsSinceEpochForDateTime(ms))
+ return false;
+ type_ = kDateTimeLocal;
+ return true;
+}
+
+bool DateComponents::SetMillisecondsSinceEpochForMonth(double ms) {
+ type_ = kInvalid;
+ if (!std::isfinite(ms))
+ return false;
+ if (!SetMillisecondsSinceEpochForDateInternal(round(ms)))
+ return false;
+ if (!WithinHTMLDateLimits(year_, month_))
+ return false;
+ type_ = kMonth;
+ return true;
+}
+
+bool DateComponents::SetMillisecondsSinceMidnight(double ms) {
+ type_ = kInvalid;
+ if (!std::isfinite(ms))
+ return false;
+ SetMillisecondsSinceMidnightInternal(PositiveFmod(round(ms), kMsPerDay));
+ type_ = kTime;
+ return true;
+}
+
+bool DateComponents::SetMonthsSinceEpoch(double months) {
+ if (!std::isfinite(months))
+ return false;
+ months = round(months);
+ double double_month = PositiveFmod(months, 12);
+ double double_year = 1970 + (months - double_month) / 12;
+ if (double_year < MinimumYear() || MaximumYear() < double_year)
+ return false;
+ int year = static_cast<int>(double_year);
+ int month = static_cast<int>(double_month);
+ if (!WithinHTMLDateLimits(year, month))
+ return false;
+ year_ = year;
+ month_ = month;
+ type_ = kMonth;
+ return true;
+}
+
+// Offset from January 1st to Monday of the ISO 8601's first week.
+// ex. If January 1st is Friday, such Monday is 3 days later. Returns 3.
+static int OffsetTo1stWeekStart(int year) {
+ int offset_to1st_week_start = 1 - DayOfWeek(year, 0, 1);
+ if (offset_to1st_week_start <= -4)
+ offset_to1st_week_start += 7;
+ return offset_to1st_week_start;
+}
+
+bool DateComponents::SetMillisecondsSinceEpochForWeek(double ms) {
+ type_ = kInvalid;
+ if (!std::isfinite(ms))
+ return false;
+ ms = round(ms);
+
+ year_ = MsToYear(ms);
+ if (year_ < MinimumYear() || year_ > MaximumYear())
+ return false;
+
+ int year_day = DayInYear(ms, year_);
+ int offset = OffsetTo1stWeekStart(year_);
+ if (year_day < offset) {
+ // The day belongs to the last week of the previous year.
+ year_--;
+ if (year_ <= MinimumYear())
+ return false;
+ week_ = MaxWeekNumberInYear();
+ } else {
+ week_ = ((year_day - offset) / 7) + 1;
+ if (week_ > MaxWeekNumberInYear()) {
+ year_++;
+ week_ = 1;
+ }
+ if (year_ > MaximumYear() ||
+ (year_ == MaximumYear() && week_ > kMaximumWeekInMaximumYear))
+ return false;
+ }
+ type_ = kWeek;
+ return true;
+}
+
+bool DateComponents::SetWeek(int year, int week_number) {
+ type_ = kInvalid;
+ if (year < MinimumYear() || year > MaximumYear())
+ return false;
+ year_ = year;
+ if (week_number < 1 || week_number > MaxWeekNumberInYear())
+ return false;
+ week_ = week_number;
+ type_ = kWeek;
+ return true;
+}
+
+double DateComponents::MillisecondsSinceEpochForTime() const {
+ DCHECK(type_ == kTime || type_ == kDateTime || type_ == kDateTimeLocal);
+ return ((hour_ * kMinutesPerHour + minute_) * kSecondsPerMinute + second_) *
+ kMsPerSecond +
+ millisecond_;
+}
+
+double DateComponents::MillisecondsSinceEpoch() const {
+ switch (type_) {
+ case kDate:
+ return DateToDaysFrom1970(year_, month_, month_day_) * kMsPerDay;
+ case kDateTime:
+ case kDateTimeLocal:
+ return DateToDaysFrom1970(year_, month_, month_day_) * kMsPerDay +
+ MillisecondsSinceEpochForTime();
+ case kMonth:
+ return DateToDaysFrom1970(year_, month_, 1) * kMsPerDay;
+ case kTime:
+ return MillisecondsSinceEpochForTime();
+ case kWeek:
+ return (DateToDaysFrom1970(year_, 0, 1) + OffsetTo1stWeekStart(year_) +
+ (week_ - 1) * 7) *
+ kMsPerDay;
+ case kInvalid:
+ break;
+ }
+ NOTREACHED();
+ return InvalidMilliseconds();
+}
+
+double DateComponents::MonthsSinceEpoch() const {
+ DCHECK_EQ(type_, kMonth);
+ return (year_ - 1970) * 12 + month_;
+}
+
+String DateComponents::ToStringForTime(SecondFormat format) const {
+ DCHECK(type_ == kDateTime || type_ == kDateTimeLocal || type_ == kTime);
+ SecondFormat effective_format = format;
+ if (millisecond_)
+ effective_format = kMillisecond;
+ else if (format == kNone && second_)
+ effective_format = kSecond;
+
+ switch (effective_format) {
+ default:
+ NOTREACHED();
+ FALLTHROUGH;
+ case kNone:
+ return String::Format("%02d:%02d", hour_, minute_);
+ case kSecond:
+ return String::Format("%02d:%02d:%02d", hour_, minute_, second_);
+ case kMillisecond:
+ return String::Format("%02d:%02d:%02d.%03d", hour_, minute_, second_,
+ millisecond_);
+ }
+}
+
+String DateComponents::ToString(SecondFormat format) const {
+ switch (type_) {
+ case kDate:
+ return String::Format("%04d-%02d-%02d", year_, month_ + 1, month_day_);
+ case kDateTime:
+ return String::Format("%04d-%02d-%02dT", year_, month_ + 1, month_day_) +
+ ToStringForTime(format) + String("Z");
+ case kDateTimeLocal:
+ return String::Format("%04d-%02d-%02dT", year_, month_ + 1, month_day_) +
+ ToStringForTime(format);
+ case kMonth:
+ return String::Format("%04d-%02d", year_, month_ + 1);
+ case kTime:
+ return ToStringForTime(format);
+ case kWeek:
+ return String::Format("%04d-W%02d", year_, week_);
+ case kInvalid:
+ break;
+ }
+ NOTREACHED();
+ return String("(Invalid DateComponents)");
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/date_components.h b/chromium/third_party/blink/renderer/platform/date_components.h
new file mode 100644
index 00000000000..00335ee6965
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/date_components.h
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_DATE_COMPONENTS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_DATE_COMPONENTS_H_
+
+#include <limits>
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
+
+namespace blink {
+
+// A DateComponents instance represents one of the following date and time
+// combinations:
+// * Month type: year-month
+// * Date type: year-month-day
+// * Week type: year-week
+// * Time type: hour-minute-second-millisecond
+// * DateTime or DateTimeLocal type:
+// year-month-day hour-minute-second-millisecond
+class PLATFORM_EXPORT DateComponents {
+ DISALLOW_NEW();
+
+ public:
+ DateComponents()
+ : millisecond_(0),
+ second_(0),
+ minute_(0),
+ hour_(0),
+ month_day_(0),
+ month_(0),
+ year_(0),
+ week_(0),
+ type_(kInvalid) {}
+
+ enum Type {
+ kInvalid,
+ kDate,
+ kDateTime,
+ kDateTimeLocal,
+ kMonth,
+ kTime,
+ kWeek,
+ };
+
+ int Millisecond() const { return millisecond_; }
+ int Second() const { return second_; }
+ int Minute() const { return minute_; }
+ int Hour() const { return hour_; }
+ int MonthDay() const { return month_day_; }
+ int WeekDay() const;
+ int Month() const { return month_; }
+ int FullYear() const { return year_; }
+ int Week() const { return week_; }
+ Type GetType() const { return type_; }
+
+ enum SecondFormat {
+ kNone, // Suppress the second part and the millisecond part if they are 0.
+ kSecond, // Always show the second part, and suppress the millisecond part
+ // if it is 0.
+ kMillisecond // Always show the second part and the millisecond part.
+ };
+
+ // Returns an ISO 8601 representation for this instance.
+ // The format argument is valid for DateTime, DateTimeLocal, and Time types.
+ String ToString(SecondFormat format = kNone) const;
+
+ // parse*() and setMillisecondsSince*() functions are initializers for an
+ // DateComponents instance. If these functions return false, the instance
+ // might be invalid.
+
+ // The following six functions parse the input 'src' whose length is
+ // 'length', and updates some fields of this instance. The parsing starts at
+ // src[start] and examines characters before src[length].
+ // 'src' must be non-null. The 'src' string doesn't need to be
+ // null-terminated.
+ // The functions return true if the parsing succeeds, and set 'end' to the
+ // next index after the last consumed. Extra leading characters cause parse
+ // failures, and the trailing extra characters don't cause parse failures.
+
+ // Sets year and month.
+ bool ParseMonth(const String&, unsigned start, unsigned& end);
+ // Sets year, month and monthDay.
+ bool ParseDate(const String&, unsigned start, unsigned& end);
+ // Sets year and week.
+ bool ParseWeek(const String&, unsigned start, unsigned& end);
+ // Sets hour, minute, second and millisecond.
+ bool ParseTime(const String&, unsigned start, unsigned& end);
+ // Sets year, month, monthDay, hour, minute, second and millisecond.
+ bool ParseDateTimeLocal(const String&, unsigned start, unsigned& end);
+
+ // The following setMillisecondsSinceEpochFor*() functions take
+ // the number of milliseconds since 1970-01-01 00:00:00.000 UTC as
+ // the argument, and update all fields for the corresponding
+ // DateComponents type. The functions return true if it succeeds, and
+ // false if they fail.
+
+ // For Date type. Updates m_year, m_month and m_monthDay.
+ bool SetMillisecondsSinceEpochForDate(double ms);
+ // For DateTime type. Updates m_year, m_month, m_monthDay, m_hour, m_minute,
+ // m_second and m_millisecond.
+ bool SetMillisecondsSinceEpochForDateTime(double ms);
+ // For DateTimeLocal type. Updates m_year, m_month, m_monthDay, m_hour,
+ // m_minute, m_second and m_millisecond.
+ bool SetMillisecondsSinceEpochForDateTimeLocal(double ms);
+ // For Month type. Updates m_year and m_month.
+ bool SetMillisecondsSinceEpochForMonth(double ms);
+ // For Week type. Updates m_year and m_week.
+ bool SetMillisecondsSinceEpochForWeek(double ms);
+
+ // For Time type. Updates m_hour, m_minute, m_second and m_millisecond.
+ bool SetMillisecondsSinceMidnight(double ms);
+
+ // Another initializer for Month type. Updates m_year and m_month.
+ bool SetMonthsSinceEpoch(double months);
+ // Another initializer for Week type. Updates m_year and m_week.
+ bool SetWeek(int year, int week_number);
+
+ // Returns the number of milliseconds from 1970-01-01 00:00:00 UTC.
+ // For a DateComponents initialized with parseDateTimeLocal(),
+ // millisecondsSinceEpoch() returns a value for UTC timezone.
+ double MillisecondsSinceEpoch() const;
+ // Returns the number of months from 1970-01.
+ // Do not call this for types other than Month.
+ double MonthsSinceEpoch() const;
+ static inline double InvalidMilliseconds() {
+ return std::numeric_limits<double>::quiet_NaN();
+ }
+
+ // Minimum and maxmimum limits for setMillisecondsSince*(),
+ // setMonthsSinceEpoch(), millisecondsSinceEpoch(), and monthsSinceEpoch().
+ static inline double MinimumDate() {
+ return -62135596800000.0;
+ } // 0001-01-01T00:00Z
+ static inline double MinimumDateTime() {
+ return -62135596800000.0;
+ } // ditto.
+ static inline double MinimumMonth() {
+ return (1 - 1970) * 12.0 + 1 - 1;
+ } // 0001-01
+ static inline double MinimumTime() { return 0; } // 00:00:00.000
+ static inline double MinimumWeek() {
+ return -62135596800000.0;
+ } // 0001-01-01, the first Monday of 0001.
+ static inline double MaximumDate() {
+ return 8640000000000000.0;
+ } // 275760-09-13T00:00Z
+ static inline double MaximumDateTime() {
+ return 8640000000000000.0;
+ } // ditto.
+ static inline double MaximumMonth() {
+ return (275760 - 1970) * 12.0 + 9 - 1;
+ } // 275760-09
+ static inline double MaximumTime() { return 86399999; } // 23:59:59.999
+ static inline double MaximumWeek() {
+ return 8639999568000000.0;
+ } // 275760-09-08, the Monday of the week including 275760-09-13.
+
+ // HTML5 uses ISO-8601 format with year >= 1. Gregorian calendar started in
+ // 1582. However, we need to support 0001-01-01 in Gregorian calendar rule.
+ static inline int MinimumYear() { return 1; }
+ // Date in ECMAScript can't represent dates later than 275760-09-13T00:00Z.
+ // So, we have the same upper limit in HTML5 date/time types.
+ static inline int MaximumYear() { return 275760; }
+ static const int kMinimumWeekNumber;
+ static const int kMaximumWeekNumber;
+
+ private:
+ // Returns the maximum week number in this DateComponents's year.
+ // The result is either of 52 and 53.
+ int MaxWeekNumberInYear() const;
+ bool ParseYear(const String&, unsigned start, unsigned& end);
+ bool AddDay(int);
+ bool AddMinute(int);
+ bool ParseTimeZone(const String&, unsigned start, unsigned& end);
+ // Helper for millisecondsSinceEpoch().
+ double MillisecondsSinceEpochForTime() const;
+ // Helpers for setMillisecondsSinceEpochFor*().
+ bool SetMillisecondsSinceEpochForDateInternal(double ms);
+ void SetMillisecondsSinceMidnightInternal(double ms);
+ // Helper for toString().
+ String ToStringForTime(SecondFormat) const;
+
+ // m_weekDay values
+ enum {
+ kSunday = 0,
+ kMonday,
+ kTuesday,
+ kWednesday,
+ kThursday,
+ kFriday,
+ kSaturday,
+ };
+
+ int millisecond_; // 0 - 999
+ int second_;
+ int minute_;
+ int hour_;
+ int month_day_; // 1 - 31
+ int month_; // 0:January - 11:December
+ int year_; // 1582 -
+ int week_; // 1 - 53
+
+ Type type_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_DATE_COMPONENTS_H_
diff --git a/chromium/third_party/blink/renderer/platform/decimal.cc b/chromium/third_party/blink/renderer/platform/decimal.cc
new file mode 100644
index 00000000000..10979afd0d6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/decimal.cc
@@ -0,0 +1,1007 @@
+/*
+ * Copyright (C) 2012 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 "third_party/blink/renderer/platform/decimal.h"
+
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+
+#include <algorithm>
+#include <float.h>
+
+namespace blink {
+
+namespace DecimalPrivate {
+
+static int const kExponentMax = 1023;
+static int const kExponentMin = -1023;
+static int const kPrecision = 18;
+
+static const uint64_t kMaxCoefficient =
+ UINT64_C(0xDE0B6B3A763FFFF); // 999999999999999999 == 18 9's
+
+// This class handles Decimal special values.
+class SpecialValueHandler {
+ STACK_ALLOCATED();
+ WTF_MAKE_NONCOPYABLE(SpecialValueHandler);
+
+ public:
+ enum HandleResult {
+ kBothFinite,
+ kBothInfinity,
+ kEitherNaN,
+ kLHSIsInfinity,
+ kRHSIsInfinity,
+ };
+
+ SpecialValueHandler(const Decimal& lhs, const Decimal& rhs);
+ HandleResult Handle();
+ Decimal Value() const;
+
+ private:
+ enum Result {
+ kResultIsLHS,
+ kResultIsRHS,
+ kResultIsUnknown,
+ };
+
+ const Decimal& lhs_;
+ const Decimal& rhs_;
+ Result result_;
+};
+
+SpecialValueHandler::SpecialValueHandler(const Decimal& lhs, const Decimal& rhs)
+ : lhs_(lhs), rhs_(rhs), result_(kResultIsUnknown) {}
+
+SpecialValueHandler::HandleResult SpecialValueHandler::Handle() {
+ if (lhs_.IsFinite() && rhs_.IsFinite())
+ return kBothFinite;
+
+ const Decimal::EncodedData::FormatClass lhs_class =
+ lhs_.Value().GetFormatClass();
+ const Decimal::EncodedData::FormatClass rhs_class =
+ rhs_.Value().GetFormatClass();
+ if (lhs_class == Decimal::EncodedData::kClassNaN) {
+ result_ = kResultIsLHS;
+ return kEitherNaN;
+ }
+
+ if (rhs_class == Decimal::EncodedData::kClassNaN) {
+ result_ = kResultIsRHS;
+ return kEitherNaN;
+ }
+
+ if (lhs_class == Decimal::EncodedData::kClassInfinity)
+ return rhs_class == Decimal::EncodedData::kClassInfinity ? kBothInfinity
+ : kLHSIsInfinity;
+
+ if (rhs_class == Decimal::EncodedData::kClassInfinity)
+ return kRHSIsInfinity;
+
+ NOTREACHED();
+ return kBothFinite;
+}
+
+Decimal SpecialValueHandler::Value() const {
+ switch (result_) {
+ case kResultIsLHS:
+ return lhs_;
+ case kResultIsRHS:
+ return rhs_;
+ case kResultIsUnknown:
+ default:
+ NOTREACHED();
+ return lhs_;
+ }
+}
+
+// This class is used for 128 bit unsigned integer arithmetic.
+class UInt128 {
+ public:
+ UInt128(uint64_t low, uint64_t high) : high_(high), low_(low) {}
+
+ UInt128& operator/=(uint32_t);
+
+ uint64_t High() const { return high_; }
+ uint64_t Low() const { return low_; }
+
+ static UInt128 Multiply(uint64_t u, uint64_t v) {
+ return UInt128(u * v, MultiplyHigh(u, v));
+ }
+
+ private:
+ static uint32_t HighUInt32(uint64_t x) {
+ return static_cast<uint32_t>(x >> 32);
+ }
+ static uint32_t LowUInt32(uint64_t x) {
+ return static_cast<uint32_t>(x & ((static_cast<uint64_t>(1) << 32) - 1));
+ }
+ static uint64_t MakeUInt64(uint32_t low, uint32_t high) {
+ return low | (static_cast<uint64_t>(high) << 32);
+ }
+
+ static uint64_t MultiplyHigh(uint64_t, uint64_t);
+
+ uint64_t high_;
+ uint64_t low_;
+};
+
+UInt128& UInt128::operator/=(const uint32_t divisor) {
+ DCHECK(divisor);
+
+ if (!high_) {
+ low_ /= divisor;
+ return *this;
+ }
+
+ uint32_t dividend[4];
+ dividend[0] = LowUInt32(low_);
+ dividend[1] = HighUInt32(low_);
+ dividend[2] = LowUInt32(high_);
+ dividend[3] = HighUInt32(high_);
+
+ uint32_t quotient[4];
+ uint32_t remainder = 0;
+ for (int i = 3; i >= 0; --i) {
+ const uint64_t work = MakeUInt64(dividend[i], remainder);
+ remainder = static_cast<uint32_t>(work % divisor);
+ quotient[i] = static_cast<uint32_t>(work / divisor);
+ }
+ low_ = MakeUInt64(quotient[0], quotient[1]);
+ high_ = MakeUInt64(quotient[2], quotient[3]);
+ return *this;
+}
+
+// Returns high 64bit of 128bit product.
+uint64_t UInt128::MultiplyHigh(uint64_t u, uint64_t v) {
+ const uint64_t u_low = LowUInt32(u);
+ const uint64_t u_high = HighUInt32(u);
+ const uint64_t v_low = LowUInt32(v);
+ const uint64_t v_high = HighUInt32(v);
+ const uint64_t partial_product = u_high * v_low + HighUInt32(u_low * v_low);
+ return u_high * v_high + HighUInt32(partial_product) +
+ HighUInt32(u_low * v_high + LowUInt32(partial_product));
+}
+
+static int CountDigits(uint64_t x) {
+ int number_of_digits = 0;
+ for (uint64_t power_of_ten = 1; x >= power_of_ten; power_of_ten *= 10) {
+ ++number_of_digits;
+ if (power_of_ten >= std::numeric_limits<uint64_t>::max() / 10)
+ break;
+ }
+ return number_of_digits;
+}
+
+static uint64_t ScaleDown(uint64_t x, int n) {
+ DCHECK_GE(n, 0);
+ while (n > 0 && x) {
+ x /= 10;
+ --n;
+ }
+ return x;
+}
+
+static uint64_t ScaleUp(uint64_t x, int n) {
+ DCHECK_GE(n, 0);
+ DCHECK_LE(n, kPrecision);
+
+ uint64_t y = 1;
+ uint64_t z = 10;
+ for (;;) {
+ if (n & 1)
+ y = y * z;
+
+ n >>= 1;
+ if (!n)
+ return x * y;
+
+ z = z * z;
+ }
+}
+
+} // namespace DecimalPrivate
+
+using namespace DecimalPrivate;
+
+Decimal::EncodedData::EncodedData(Sign sign, FormatClass format_class)
+ : coefficient_(0), exponent_(0), format_class_(format_class), sign_(sign) {}
+
+Decimal::EncodedData::EncodedData(Sign sign, int exponent, uint64_t coefficient)
+ : format_class_(coefficient ? kClassNormal : kClassZero), sign_(sign) {
+ if (exponent >= kExponentMin && exponent <= kExponentMax) {
+ while (coefficient > kMaxCoefficient) {
+ coefficient /= 10;
+ ++exponent;
+ }
+ }
+
+ if (exponent > kExponentMax) {
+ coefficient_ = 0;
+ exponent_ = 0;
+ format_class_ = kClassInfinity;
+ return;
+ }
+
+ if (exponent < kExponentMin) {
+ coefficient_ = 0;
+ exponent_ = 0;
+ format_class_ = kClassZero;
+ return;
+ }
+
+ coefficient_ = coefficient;
+ exponent_ = static_cast<int16_t>(exponent);
+}
+
+bool Decimal::EncodedData::operator==(const EncodedData& another) const {
+ return sign_ == another.sign_ && format_class_ == another.format_class_ &&
+ exponent_ == another.exponent_ && coefficient_ == another.coefficient_;
+}
+
+Decimal::Decimal(int32_t i32)
+ : data_(i32 < 0 ? kNegative : kPositive,
+ 0,
+ i32 < 0 ? static_cast<uint64_t>(-static_cast<int64_t>(i32))
+ : static_cast<uint64_t>(i32)) {}
+
+Decimal::Decimal(Sign sign, int exponent, uint64_t coefficient)
+ : data_(sign, exponent, coefficient) {}
+
+Decimal::Decimal(const EncodedData& data) : data_(data) {}
+
+Decimal::Decimal(const Decimal& other) = default;
+
+Decimal& Decimal::operator=(const Decimal& other) = default;
+
+Decimal& Decimal::operator+=(const Decimal& other) {
+ data_ = (*this + other).data_;
+ return *this;
+}
+
+Decimal& Decimal::operator-=(const Decimal& other) {
+ data_ = (*this - other).data_;
+ return *this;
+}
+
+Decimal& Decimal::operator*=(const Decimal& other) {
+ data_ = (*this * other).data_;
+ return *this;
+}
+
+Decimal& Decimal::operator/=(const Decimal& other) {
+ data_ = (*this / other).data_;
+ return *this;
+}
+
+Decimal Decimal::operator-() const {
+ if (IsNaN())
+ return *this;
+
+ Decimal result(*this);
+ result.data_.SetSign(InvertSign(data_.GetSign()));
+ return result;
+}
+
+Decimal Decimal::operator+(const Decimal& rhs) const {
+ const Decimal& lhs = *this;
+ const Sign lhs_sign = lhs.GetSign();
+ const Sign rhs_sign = rhs.GetSign();
+
+ SpecialValueHandler handler(lhs, rhs);
+ switch (handler.Handle()) {
+ case SpecialValueHandler::kBothFinite:
+ break;
+
+ case SpecialValueHandler::kBothInfinity:
+ return lhs_sign == rhs_sign ? lhs : Nan();
+
+ case SpecialValueHandler::kEitherNaN:
+ return handler.Value();
+
+ case SpecialValueHandler::kLHSIsInfinity:
+ return lhs;
+
+ case SpecialValueHandler::kRHSIsInfinity:
+ return rhs;
+ }
+
+ const AlignedOperands aligned_operands = AlignOperands(lhs, rhs);
+
+ const uint64_t result =
+ lhs_sign == rhs_sign
+ ? aligned_operands.lhs_coefficient + aligned_operands.rhs_coefficient
+ : aligned_operands.lhs_coefficient - aligned_operands.rhs_coefficient;
+
+ if (lhs_sign == kNegative && rhs_sign == kPositive && !result)
+ return Decimal(kPositive, aligned_operands.exponent, 0);
+
+ return static_cast<int64_t>(result) >= 0
+ ? Decimal(lhs_sign, aligned_operands.exponent, result)
+ : Decimal(InvertSign(lhs_sign), aligned_operands.exponent,
+ -static_cast<int64_t>(result));
+}
+
+Decimal Decimal::operator-(const Decimal& rhs) const {
+ const Decimal& lhs = *this;
+ const Sign lhs_sign = lhs.GetSign();
+ const Sign rhs_sign = rhs.GetSign();
+
+ SpecialValueHandler handler(lhs, rhs);
+ switch (handler.Handle()) {
+ case SpecialValueHandler::kBothFinite:
+ break;
+
+ case SpecialValueHandler::kBothInfinity:
+ return lhs_sign == rhs_sign ? Nan() : lhs;
+
+ case SpecialValueHandler::kEitherNaN:
+ return handler.Value();
+
+ case SpecialValueHandler::kLHSIsInfinity:
+ return lhs;
+
+ case SpecialValueHandler::kRHSIsInfinity:
+ return Infinity(InvertSign(rhs_sign));
+ }
+
+ const AlignedOperands aligned_operands = AlignOperands(lhs, rhs);
+
+ const uint64_t result =
+ lhs_sign == rhs_sign
+ ? aligned_operands.lhs_coefficient - aligned_operands.rhs_coefficient
+ : aligned_operands.lhs_coefficient + aligned_operands.rhs_coefficient;
+
+ if (lhs_sign == kNegative && rhs_sign == kNegative && !result)
+ return Decimal(kPositive, aligned_operands.exponent, 0);
+
+ return static_cast<int64_t>(result) >= 0
+ ? Decimal(lhs_sign, aligned_operands.exponent, result)
+ : Decimal(InvertSign(lhs_sign), aligned_operands.exponent,
+ -static_cast<int64_t>(result));
+}
+
+Decimal Decimal::operator*(const Decimal& rhs) const {
+ const Decimal& lhs = *this;
+ const Sign lhs_sign = lhs.GetSign();
+ const Sign rhs_sign = rhs.GetSign();
+ const Sign result_sign = lhs_sign == rhs_sign ? kPositive : kNegative;
+
+ SpecialValueHandler handler(lhs, rhs);
+ switch (handler.Handle()) {
+ case SpecialValueHandler::kBothFinite: {
+ const uint64_t lhs_coefficient = lhs.data_.Coefficient();
+ const uint64_t rhs_coefficient = rhs.data_.Coefficient();
+ int result_exponent = lhs.Exponent() + rhs.Exponent();
+ UInt128 work(UInt128::Multiply(lhs_coefficient, rhs_coefficient));
+ while (work.High()) {
+ work /= 10;
+ ++result_exponent;
+ }
+ return Decimal(result_sign, result_exponent, work.Low());
+ }
+
+ case SpecialValueHandler::kBothInfinity:
+ return Infinity(result_sign);
+
+ case SpecialValueHandler::kEitherNaN:
+ return handler.Value();
+
+ case SpecialValueHandler::kLHSIsInfinity:
+ return rhs.IsZero() ? Nan() : Infinity(result_sign);
+
+ case SpecialValueHandler::kRHSIsInfinity:
+ return lhs.IsZero() ? Nan() : Infinity(result_sign);
+ }
+
+ NOTREACHED();
+ return Nan();
+}
+
+Decimal Decimal::operator/(const Decimal& rhs) const {
+ const Decimal& lhs = *this;
+ const Sign lhs_sign = lhs.GetSign();
+ const Sign rhs_sign = rhs.GetSign();
+ const Sign result_sign = lhs_sign == rhs_sign ? kPositive : kNegative;
+
+ SpecialValueHandler handler(lhs, rhs);
+ switch (handler.Handle()) {
+ case SpecialValueHandler::kBothFinite:
+ break;
+
+ case SpecialValueHandler::kBothInfinity:
+ return Nan();
+
+ case SpecialValueHandler::kEitherNaN:
+ return handler.Value();
+
+ case SpecialValueHandler::kLHSIsInfinity:
+ return Infinity(result_sign);
+
+ case SpecialValueHandler::kRHSIsInfinity:
+ return Zero(result_sign);
+ }
+
+ DCHECK(lhs.IsFinite());
+ DCHECK(rhs.IsFinite());
+
+ if (rhs.IsZero())
+ return lhs.IsZero() ? Nan() : Infinity(result_sign);
+
+ int result_exponent = lhs.Exponent() - rhs.Exponent();
+
+ if (lhs.IsZero())
+ return Decimal(result_sign, result_exponent, 0);
+
+ uint64_t remainder = lhs.data_.Coefficient();
+ const uint64_t divisor = rhs.data_.Coefficient();
+ uint64_t result = 0;
+ for (;;) {
+ while (remainder < divisor && result < kMaxCoefficient / 10) {
+ remainder *= 10;
+ result *= 10;
+ --result_exponent;
+ }
+ if (remainder < divisor)
+ break;
+ uint64_t quotient = remainder / divisor;
+ if (result > kMaxCoefficient - quotient)
+ break;
+ result += quotient;
+ remainder %= divisor;
+ if (!remainder)
+ break;
+ }
+
+ if (remainder > divisor / 2)
+ ++result;
+
+ return Decimal(result_sign, result_exponent, result);
+}
+
+bool Decimal::operator==(const Decimal& rhs) const {
+ return data_ == rhs.data_ || CompareTo(rhs).IsZero();
+}
+
+bool Decimal::operator!=(const Decimal& rhs) const {
+ if (data_ == rhs.data_)
+ return false;
+ const Decimal result = CompareTo(rhs);
+ if (result.IsNaN())
+ return false;
+ return !result.IsZero();
+}
+
+bool Decimal::operator<(const Decimal& rhs) const {
+ const Decimal result = CompareTo(rhs);
+ if (result.IsNaN())
+ return false;
+ return !result.IsZero() && result.IsNegative();
+}
+
+bool Decimal::operator<=(const Decimal& rhs) const {
+ if (data_ == rhs.data_)
+ return true;
+ const Decimal result = CompareTo(rhs);
+ if (result.IsNaN())
+ return false;
+ return result.IsZero() || result.IsNegative();
+}
+
+bool Decimal::operator>(const Decimal& rhs) const {
+ const Decimal result = CompareTo(rhs);
+ if (result.IsNaN())
+ return false;
+ return !result.IsZero() && result.IsPositive();
+}
+
+bool Decimal::operator>=(const Decimal& rhs) const {
+ if (data_ == rhs.data_)
+ return true;
+ const Decimal result = CompareTo(rhs);
+ if (result.IsNaN())
+ return false;
+ return result.IsZero() || !result.IsNegative();
+}
+
+Decimal Decimal::Abs() const {
+ Decimal result(*this);
+ result.data_.SetSign(kPositive);
+ return result;
+}
+
+Decimal::AlignedOperands Decimal::AlignOperands(const Decimal& lhs,
+ const Decimal& rhs) {
+ DCHECK(lhs.IsFinite());
+ DCHECK(rhs.IsFinite());
+
+ const int lhs_exponent = lhs.Exponent();
+ const int rhs_exponent = rhs.Exponent();
+ int exponent = std::min(lhs_exponent, rhs_exponent);
+ uint64_t lhs_coefficient = lhs.data_.Coefficient();
+ uint64_t rhs_coefficient = rhs.data_.Coefficient();
+
+ if (lhs_exponent > rhs_exponent) {
+ const int number_of_lhs_digits = CountDigits(lhs_coefficient);
+ if (number_of_lhs_digits) {
+ const int lhs_shift_amount = lhs_exponent - rhs_exponent;
+ const int overflow = number_of_lhs_digits + lhs_shift_amount - kPrecision;
+ if (overflow <= 0) {
+ lhs_coefficient = ScaleUp(lhs_coefficient, lhs_shift_amount);
+ } else {
+ lhs_coefficient = ScaleUp(lhs_coefficient, lhs_shift_amount - overflow);
+ rhs_coefficient = ScaleDown(rhs_coefficient, overflow);
+ exponent += overflow;
+ }
+ }
+
+ } else if (lhs_exponent < rhs_exponent) {
+ const int number_of_rhs_digits = CountDigits(rhs_coefficient);
+ if (number_of_rhs_digits) {
+ const int rhs_shift_amount = rhs_exponent - lhs_exponent;
+ const int overflow = number_of_rhs_digits + rhs_shift_amount - kPrecision;
+ if (overflow <= 0) {
+ rhs_coefficient = ScaleUp(rhs_coefficient, rhs_shift_amount);
+ } else {
+ rhs_coefficient = ScaleUp(rhs_coefficient, rhs_shift_amount - overflow);
+ lhs_coefficient = ScaleDown(lhs_coefficient, overflow);
+ exponent += overflow;
+ }
+ }
+ }
+
+ AlignedOperands aligned_operands;
+ aligned_operands.exponent = exponent;
+ aligned_operands.lhs_coefficient = lhs_coefficient;
+ aligned_operands.rhs_coefficient = rhs_coefficient;
+ return aligned_operands;
+}
+
+static bool IsMultiplePowersOfTen(uint64_t coefficient, int n) {
+ return !coefficient || !(coefficient % ScaleUp(1, n));
+}
+
+// Round toward positive infinity.
+Decimal Decimal::Ceil() const {
+ if (IsSpecial())
+ return *this;
+
+ if (Exponent() >= 0)
+ return *this;
+
+ uint64_t result = data_.Coefficient();
+ const int number_of_digits = CountDigits(result);
+ const int number_of_drop_digits = -Exponent();
+ if (number_of_digits <= number_of_drop_digits)
+ return IsPositive() ? Decimal(1) : Zero(kPositive);
+
+ result = ScaleDown(result, number_of_drop_digits);
+ if (IsPositive() &&
+ !IsMultiplePowersOfTen(data_.Coefficient(), number_of_drop_digits))
+ ++result;
+ return Decimal(GetSign(), 0, result);
+}
+
+Decimal Decimal::CompareTo(const Decimal& rhs) const {
+ const Decimal result(*this - rhs);
+ switch (result.data_.GetFormatClass()) {
+ case EncodedData::kClassInfinity:
+ return result.IsNegative() ? Decimal(-1) : Decimal(1);
+
+ case EncodedData::kClassNaN:
+ case EncodedData::kClassNormal:
+ return result;
+
+ case EncodedData::kClassZero:
+ return Zero(kPositive);
+
+ default:
+ NOTREACHED();
+ return Nan();
+ }
+}
+
+// Round toward negative infinity.
+Decimal Decimal::Floor() const {
+ if (IsSpecial())
+ return *this;
+
+ if (Exponent() >= 0)
+ return *this;
+
+ uint64_t result = data_.Coefficient();
+ const int number_of_digits = CountDigits(result);
+ const int number_of_drop_digits = -Exponent();
+ if (number_of_digits < number_of_drop_digits)
+ return IsPositive() ? Zero(kPositive) : Decimal(-1);
+
+ result = ScaleDown(result, number_of_drop_digits);
+ if (IsNegative() &&
+ !IsMultiplePowersOfTen(data_.Coefficient(), number_of_drop_digits))
+ ++result;
+ return Decimal(GetSign(), 0, result);
+}
+
+Decimal Decimal::FromDouble(double double_value) {
+ if (std::isfinite(double_value))
+ return FromString(String::NumberToStringECMAScript(double_value));
+
+ if (std::isinf(double_value))
+ return Infinity(double_value < 0 ? kNegative : kPositive);
+
+ return Nan();
+}
+
+Decimal Decimal::FromString(const String& str) {
+ int exponent = 0;
+ Sign exponent_sign = kPositive;
+ int number_of_digits = 0;
+ int number_of_digits_after_dot = 0;
+ int number_of_extra_digits = 0;
+ Sign sign = kPositive;
+
+ enum {
+ kStateDigit,
+ kStateDot,
+ kStateDotDigit,
+ kStateE,
+ kStateEDigit,
+ kStateESign,
+ kStateSign,
+ kStateStart,
+ kStateZero,
+ } state = kStateStart;
+
+#define HandleCharAndBreak(expected, nextState) \
+ if (ch == expected) { \
+ state = nextState; \
+ break; \
+ }
+
+#define HandleTwoCharsAndBreak(expected1, expected2, nextState) \
+ if (ch == expected1 || ch == expected2) { \
+ state = nextState; \
+ break; \
+ }
+
+ uint64_t accumulator = 0;
+ for (unsigned index = 0; index < str.length(); ++index) {
+ const int ch = str[index];
+ switch (state) {
+ case kStateDigit:
+ if (ch >= '0' && ch <= '9') {
+ if (number_of_digits < kPrecision) {
+ ++number_of_digits;
+ accumulator *= 10;
+ accumulator += ch - '0';
+ } else {
+ ++number_of_extra_digits;
+ }
+ break;
+ }
+
+ HandleCharAndBreak('.', kStateDot);
+ HandleTwoCharsAndBreak('E', 'e', kStateE);
+ return Nan();
+
+ case kStateDot:
+ case kStateDotDigit:
+ if (ch >= '0' && ch <= '9') {
+ if (number_of_digits < kPrecision) {
+ ++number_of_digits;
+ ++number_of_digits_after_dot;
+ accumulator *= 10;
+ accumulator += ch - '0';
+ }
+ state = kStateDotDigit;
+ break;
+ }
+
+ HandleTwoCharsAndBreak('E', 'e', kStateE);
+ return Nan();
+
+ case kStateE:
+ if (ch == '+') {
+ exponent_sign = kPositive;
+ state = kStateESign;
+ break;
+ }
+
+ if (ch == '-') {
+ exponent_sign = kNegative;
+ state = kStateESign;
+ break;
+ }
+
+ if (ch >= '0' && ch <= '9') {
+ exponent = ch - '0';
+ state = kStateEDigit;
+ break;
+ }
+
+ return Nan();
+
+ case kStateEDigit:
+ if (ch >= '0' && ch <= '9') {
+ exponent *= 10;
+ exponent += ch - '0';
+ if (exponent > kExponentMax + kPrecision) {
+ if (accumulator)
+ return exponent_sign == kNegative ? Zero(kPositive)
+ : Infinity(sign);
+ return Zero(sign);
+ }
+ state = kStateEDigit;
+ break;
+ }
+
+ return Nan();
+
+ case kStateESign:
+ if (ch >= '0' && ch <= '9') {
+ exponent = ch - '0';
+ state = kStateEDigit;
+ break;
+ }
+
+ return Nan();
+
+ case kStateSign:
+ if (ch >= '1' && ch <= '9') {
+ accumulator = ch - '0';
+ number_of_digits = 1;
+ state = kStateDigit;
+ break;
+ }
+
+ HandleCharAndBreak('0', kStateZero);
+ return Nan();
+
+ case kStateStart:
+ if (ch >= '1' && ch <= '9') {
+ accumulator = ch - '0';
+ number_of_digits = 1;
+ state = kStateDigit;
+ break;
+ }
+
+ if (ch == '-') {
+ sign = kNegative;
+ state = kStateSign;
+ break;
+ }
+
+ if (ch == '+') {
+ sign = kPositive;
+ state = kStateSign;
+ break;
+ }
+
+ HandleCharAndBreak('0', kStateZero);
+ HandleCharAndBreak('.', kStateDot);
+ return Nan();
+
+ case kStateZero:
+ if (ch == '0')
+ break;
+
+ if (ch >= '1' && ch <= '9') {
+ accumulator = ch - '0';
+ number_of_digits = 1;
+ state = kStateDigit;
+ break;
+ }
+
+ HandleCharAndBreak('.', kStateDot);
+ HandleTwoCharsAndBreak('E', 'e', kStateE);
+ return Nan();
+
+ default:
+ NOTREACHED();
+ return Nan();
+ }
+ }
+
+ if (state == kStateZero)
+ return Zero(sign);
+
+ if (state == kStateDigit || state == kStateEDigit ||
+ state == kStateDotDigit) {
+ int result_exponent = exponent * (exponent_sign == kNegative ? -1 : 1) -
+ number_of_digits_after_dot + number_of_extra_digits;
+ if (result_exponent < kExponentMin)
+ return Zero(kPositive);
+
+ const int overflow = result_exponent - kExponentMax + 1;
+ if (overflow > 0) {
+ if (overflow + number_of_digits - number_of_digits_after_dot > kPrecision)
+ return Infinity(sign);
+ accumulator = ScaleUp(accumulator, overflow);
+ result_exponent -= overflow;
+ }
+
+ return Decimal(sign, result_exponent, accumulator);
+ }
+
+ return Nan();
+}
+
+Decimal Decimal::Infinity(const Sign sign) {
+ return Decimal(EncodedData(sign, EncodedData::kClassInfinity));
+}
+
+Decimal Decimal::Nan() {
+ return Decimal(EncodedData(kPositive, EncodedData::kClassNaN));
+}
+
+Decimal Decimal::Remainder(const Decimal& rhs) const {
+ const Decimal quotient = *this / rhs;
+ return quotient.IsSpecial()
+ ? quotient
+ : *this - (quotient.IsNegative() ? quotient.Ceil()
+ : quotient.Floor()) *
+ rhs;
+}
+
+Decimal Decimal::Round() const {
+ if (IsSpecial())
+ return *this;
+
+ if (Exponent() >= 0)
+ return *this;
+
+ uint64_t result = data_.Coefficient();
+ const int number_of_digits = CountDigits(result);
+ const int number_of_drop_digits = -Exponent();
+ if (number_of_digits < number_of_drop_digits)
+ return Zero(kPositive);
+
+ result = ScaleDown(result, number_of_drop_digits - 1);
+ if (result % 10 >= 5)
+ result += 10;
+ result /= 10;
+ return Decimal(GetSign(), 0, result);
+}
+
+double Decimal::ToDouble() const {
+ if (IsFinite()) {
+ bool valid;
+ const double double_value = ToString().ToDouble(&valid);
+ return valid ? double_value : std::numeric_limits<double>::quiet_NaN();
+ }
+
+ if (IsInfinity())
+ return IsNegative() ? -std::numeric_limits<double>::infinity()
+ : std::numeric_limits<double>::infinity();
+
+ return std::numeric_limits<double>::quiet_NaN();
+}
+
+String Decimal::ToString() const {
+ switch (data_.GetFormatClass()) {
+ case EncodedData::kClassInfinity:
+ return GetSign() ? "-Infinity" : "Infinity";
+
+ case EncodedData::kClassNaN:
+ return "NaN";
+
+ case EncodedData::kClassNormal:
+ case EncodedData::kClassZero:
+ break;
+
+ default:
+ NOTREACHED();
+ return "";
+ }
+
+ StringBuilder builder;
+ if (GetSign())
+ builder.Append('-');
+
+ int original_exponent = Exponent();
+ uint64_t coefficient = data_.Coefficient();
+
+ if (original_exponent < 0) {
+ const int kMaxDigits = DBL_DIG;
+ uint64_t last_digit = 0;
+ while (CountDigits(coefficient) > kMaxDigits) {
+ last_digit = coefficient % 10;
+ coefficient /= 10;
+ ++original_exponent;
+ }
+
+ if (last_digit >= 5)
+ ++coefficient;
+
+ while (original_exponent < 0 && coefficient && !(coefficient % 10)) {
+ coefficient /= 10;
+ ++original_exponent;
+ }
+ }
+
+ const String digits = String::Number(coefficient);
+ int coefficient_length = static_cast<int>(digits.length());
+ const int adjusted_exponent = original_exponent + coefficient_length - 1;
+ if (original_exponent <= 0 && adjusted_exponent >= -6) {
+ if (!original_exponent) {
+ builder.Append(digits);
+ return builder.ToString();
+ }
+
+ if (adjusted_exponent >= 0) {
+ for (int i = 0; i < coefficient_length; ++i) {
+ builder.Append(digits[i]);
+ if (i == adjusted_exponent)
+ builder.Append('.');
+ }
+ return builder.ToString();
+ }
+
+ builder.Append("0.");
+ for (int i = adjusted_exponent + 1; i < 0; ++i)
+ builder.Append('0');
+
+ builder.Append(digits);
+
+ } else {
+ builder.Append(digits[0]);
+ while (coefficient_length >= 2 && digits[coefficient_length - 1] == '0')
+ --coefficient_length;
+ if (coefficient_length >= 2) {
+ builder.Append('.');
+ for (int i = 1; i < coefficient_length; ++i)
+ builder.Append(digits[i]);
+ }
+
+ if (adjusted_exponent) {
+ builder.Append(adjusted_exponent < 0 ? "e" : "e+");
+ builder.AppendNumber(adjusted_exponent);
+ }
+ }
+ return builder.ToString();
+}
+
+Decimal Decimal::Zero(Sign sign) {
+ return Decimal(EncodedData(sign, EncodedData::kClassZero));
+}
+
+std::ostream& operator<<(std::ostream& ostream, const Decimal& decimal) {
+ Decimal::EncodedData data = decimal.Value();
+ return ostream << "encode("
+ << String::Number(data.Coefficient()).Ascii().data() << ", "
+ << String::Number(data.Exponent()).Ascii().data() << ", "
+ << (data.GetSign() == Decimal::kNegative ? "Negative"
+ : "Positive")
+ << ")=" << decimal.ToString().Ascii().data();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/decimal.h b/chromium/third_party/blink/renderer/platform/decimal.h
new file mode 100644
index 00000000000..fe971d67e99
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/decimal.h
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2012 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_DECIMAL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_DECIMAL_H_
+
+#include <stdint.h>
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+namespace DecimalPrivate {
+class SpecialValueHandler;
+}
+
+// This class represents decimal base floating point number.
+//
+// FIXME: Once all C++ compiler support decimal type, we should replace this
+// class to compiler supported one. See below URI for current status of decimal
+// type for C++:
+// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1977.html
+class PLATFORM_EXPORT Decimal {
+ USING_FAST_MALLOC(Decimal);
+
+ public:
+ enum Sign {
+ kPositive,
+ kNegative,
+ };
+
+ // You should not use EncodedData other than unit testing.
+ class EncodedData {
+ DISALLOW_NEW();
+ // For accessing FormatClass.
+ friend class Decimal;
+ friend class DecimalPrivate::SpecialValueHandler;
+
+ public:
+ EncodedData(Sign, int exponent, uint64_t coefficient);
+
+ bool operator==(const EncodedData&) const;
+ bool operator!=(const EncodedData& another) const {
+ return !operator==(another);
+ }
+
+ uint64_t Coefficient() const { return coefficient_; }
+ int CountDigits() const;
+ int Exponent() const { return exponent_; }
+ bool IsFinite() const { return !IsSpecial(); }
+ bool IsInfinity() const { return format_class_ == kClassInfinity; }
+ bool IsNaN() const { return format_class_ == kClassNaN; }
+ bool IsSpecial() const {
+ return format_class_ == kClassInfinity || format_class_ == kClassNaN;
+ }
+ bool IsZero() const { return format_class_ == kClassZero; }
+ Sign GetSign() const { return sign_; }
+ void SetSign(Sign sign) { sign_ = sign; }
+
+ private:
+ enum FormatClass {
+ kClassInfinity,
+ kClassNormal,
+ kClassNaN,
+ kClassZero,
+ };
+
+ EncodedData(Sign, FormatClass);
+ FormatClass GetFormatClass() const { return format_class_; }
+
+ uint64_t coefficient_;
+ int16_t exponent_;
+ FormatClass format_class_;
+ Sign sign_;
+ };
+
+ Decimal(int32_t = 0);
+ Decimal(Sign, int exponent, uint64_t coefficient);
+ Decimal(const Decimal&);
+
+ Decimal& operator=(const Decimal&);
+ Decimal& operator+=(const Decimal&);
+ Decimal& operator-=(const Decimal&);
+ Decimal& operator*=(const Decimal&);
+ Decimal& operator/=(const Decimal&);
+
+ Decimal operator-() const;
+
+ bool operator==(const Decimal&) const;
+ bool operator!=(const Decimal&) const;
+ bool operator<(const Decimal&) const;
+ bool operator<=(const Decimal&) const;
+ bool operator>(const Decimal&) const;
+ bool operator>=(const Decimal&) const;
+
+ Decimal operator+(const Decimal&) const;
+ Decimal operator-(const Decimal&) const;
+ Decimal operator*(const Decimal&)const;
+ Decimal operator/(const Decimal&) const;
+
+ int Exponent() const {
+ DCHECK(IsFinite());
+ return data_.Exponent();
+ }
+
+ bool IsFinite() const { return data_.IsFinite(); }
+ bool IsInfinity() const { return data_.IsInfinity(); }
+ bool IsNaN() const { return data_.IsNaN(); }
+ bool IsNegative() const { return GetSign() == kNegative; }
+ bool IsPositive() const { return GetSign() == kPositive; }
+ bool IsSpecial() const { return data_.IsSpecial(); }
+ bool IsZero() const { return data_.IsZero(); }
+
+ Decimal Abs() const;
+ Decimal Ceil() const;
+ Decimal Floor() const;
+ Decimal Remainder(const Decimal&) const;
+ Decimal Round() const;
+
+ double ToDouble() const;
+ // Note: toString method supports infinity and nan but fromString not.
+ String ToString() const;
+
+ static Decimal FromDouble(double);
+ // fromString supports following syntax EBNF:
+ // number ::= sign? digit+ ('.' digit*) (exponent-marker sign? digit+)?
+ // | sign? '.' digit+ (exponent-marker sign? digit+)?
+ // sign ::= '+' | '-'
+ // exponent-marker ::= 'e' | 'E'
+ // digit ::= '0' | '1' | ... | '9'
+ // Note: fromString doesn't support "infinity" and "nan".
+ static Decimal FromString(const String&);
+ static Decimal Infinity(Sign);
+ static Decimal Nan();
+ static Decimal Zero(Sign);
+
+ // You should not use below methods. We expose them for unit testing.
+ explicit Decimal(const EncodedData&);
+ const EncodedData& Value() const { return data_; }
+
+ private:
+ struct AlignedOperands {
+ uint64_t lhs_coefficient;
+ uint64_t rhs_coefficient;
+ int exponent;
+ };
+
+ Decimal(double);
+ Decimal CompareTo(const Decimal&) const;
+
+ static AlignedOperands AlignOperands(const Decimal& lhs, const Decimal& rhs);
+ static inline Sign InvertSign(Sign sign) {
+ return sign == kNegative ? kPositive : kNegative;
+ }
+
+ Sign GetSign() const { return data_.GetSign(); }
+
+ EncodedData data_;
+};
+
+PLATFORM_EXPORT std::ostream& operator<<(std::ostream&, const Decimal&);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_DECIMAL_H_
diff --git a/chromium/third_party/blink/renderer/platform/decimal_test.cc b/chromium/third_party/blink/renderer/platform/decimal_test.cc
new file mode 100644
index 00000000000..8bd321acfe1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/decimal_test.cc
@@ -0,0 +1,1161 @@
+/*
+ * Copyright (C) 2012 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 "third_party/blink/renderer/platform/decimal.h"
+
+#include <float.h>
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+
+namespace blink {
+
+// Simulate core/html/forms/StepRange
+class DecimalStepRange {
+ public:
+ Decimal maximum;
+ Decimal minimum;
+ Decimal step;
+
+ DecimalStepRange(const Decimal& minimum,
+ const Decimal& maximum,
+ const Decimal& step)
+ : maximum(maximum), minimum(minimum), step(step) {}
+
+ Decimal ClampValue(Decimal value) const {
+ const Decimal result = minimum + ((value - minimum) / step).Round() * step;
+ DCHECK(result.IsFinite());
+ return result > maximum ? result - step : result;
+ }
+};
+
+class DecimalTest : public testing::Test {
+ protected:
+ using Sign = Decimal::Sign;
+ static const Sign kPositive = Decimal::kPositive;
+ static const Sign kNegative = Decimal::kNegative;
+
+ Decimal Encode(uint64_t coefficient, int exponent, Sign sign) {
+ return Decimal(sign, exponent, coefficient);
+ }
+
+ Decimal FromString(const String& string) {
+ return Decimal::FromString(string);
+ }
+
+ Decimal StepDown(const String& minimum,
+ const String& maximum,
+ const String& step,
+ const String& value_string,
+ int number_of_step_times) {
+ DecimalStepRange step_range(FromString(minimum), FromString(maximum),
+ FromString(step));
+ Decimal value = FromString(value_string);
+ for (int i = 0; i < number_of_step_times; ++i) {
+ value -= step_range.step;
+ value = step_range.ClampValue(value);
+ }
+ return value;
+ }
+
+ Decimal StepUp(const String& minimum,
+ const String& maximum,
+ const String& step,
+ const String& value_string,
+ int number_of_step_times) {
+ DecimalStepRange step_range(FromString(minimum), FromString(maximum),
+ FromString(step));
+ Decimal value = FromString(value_string);
+ for (int i = 0; i < number_of_step_times; ++i) {
+ value += step_range.step;
+ value = step_range.ClampValue(value);
+ }
+ return value;
+ }
+};
+
+// FIXME: We should use expectedSign without "Decimal::", however, g++ causes
+// undefined references for DecimalTest::Positive and Negative.
+#define EXPECT_DECIMAL_ENCODED_DATA_EQ(expectedCoefficient, expectedExponent, \
+ expectedSign, decimal) \
+ EXPECT_EQ((expectedCoefficient), (decimal).Value().Coefficient()); \
+ EXPECT_EQ((expectedExponent), (decimal).Value().Exponent()); \
+ EXPECT_EQ(Decimal::expectedSign, (decimal).Value().GetSign());
+
+#define EXPECT_DECIMAL_STREQ(expected, decimal) \
+ EXPECT_STREQ((expected), (decimal).ToString().Ascii().data())
+
+TEST_F(DecimalTest, Abs) {
+ EXPECT_EQ(Encode(0, 0, kPositive), Encode(0, 0, kPositive).Abs());
+ EXPECT_EQ(Encode(0, 0, kPositive), Encode(0, 0, kNegative).Abs());
+
+ EXPECT_EQ(Encode(0, 10, kPositive), Encode(0, 10, kPositive).Abs());
+ EXPECT_EQ(Encode(0, 10, kPositive), Encode(0, 10, kNegative).Abs());
+
+ EXPECT_EQ(Encode(0, -10, kPositive), Encode(0, -10, kPositive).Abs());
+ EXPECT_EQ(Encode(0, -10, kPositive), Encode(0, -10, kNegative).Abs());
+
+ EXPECT_EQ(Encode(1, 0, kPositive), Encode(1, 0, kPositive).Abs());
+ EXPECT_EQ(Encode(1, 0, kPositive), Encode(1, 0, kNegative).Abs());
+
+ EXPECT_EQ(Encode(1, 10, kPositive), Encode(1, 10, kPositive).Abs());
+ EXPECT_EQ(Encode(1, 10, kPositive), Encode(1, 10, kNegative).Abs());
+
+ EXPECT_EQ(Encode(1, -10, kPositive), Encode(1, -10, kPositive).Abs());
+ EXPECT_EQ(Encode(1, -10, kPositive), Encode(1, -10, kNegative).Abs());
+}
+
+TEST_F(DecimalTest, AbsBigExponent) {
+ EXPECT_EQ(Encode(1, 1000, kPositive), Encode(1, 1000, kPositive).Abs());
+ EXPECT_EQ(Encode(1, 1000, kPositive), Encode(1, 1000, kNegative).Abs());
+}
+
+TEST_F(DecimalTest, AbsSmallExponent) {
+ EXPECT_EQ(Encode(1, -1000, kPositive), Encode(1, -1000, kPositive).Abs());
+ EXPECT_EQ(Encode(1, -1000, kPositive), Encode(1, -1000, kNegative).Abs());
+}
+
+TEST_F(DecimalTest, AbsSpecialValues) {
+ EXPECT_EQ(Decimal::Infinity(kPositive), Decimal::Infinity(kPositive).Abs());
+ EXPECT_EQ(Decimal::Infinity(kPositive), Decimal::Infinity(kNegative).Abs());
+ EXPECT_EQ(Decimal::Nan(), Decimal::Nan().Abs());
+}
+
+TEST_F(DecimalTest, Add) {
+ EXPECT_EQ(Encode(0, 0, kPositive), Decimal(0) + Decimal(0));
+ EXPECT_EQ(Decimal(1), Decimal(2) + Decimal(-1));
+ EXPECT_EQ(Decimal(1), Decimal(-1) + Decimal(2));
+ EXPECT_EQ(Encode(100, 0, kPositive), Decimal(99) + Decimal(1));
+ EXPECT_EQ(Encode(100, 0, kNegative), Decimal(-50) + Decimal(-50));
+ EXPECT_EQ(Encode(UINT64_C(1000000000000000), 35, kPositive),
+ Encode(1, 50, kPositive) + Decimal(1));
+ EXPECT_EQ(Encode(UINT64_C(1000000000000000), 35, kPositive),
+ Decimal(1) + Encode(1, 50, kPositive));
+ EXPECT_EQ(Encode(UINT64_C(10000000001), 0, kPositive),
+ Encode(1, 10, kPositive) + Decimal(1));
+ EXPECT_EQ(Encode(UINT64_C(10000000001), 0, kPositive),
+ Decimal(1) + Encode(1, 10, kPositive));
+ EXPECT_EQ(Encode(1, 0, kPositive),
+ Encode(1, -1022, kPositive) + Encode(1, 0, kPositive));
+ EXPECT_EQ(Encode(2, -1022, kPositive),
+ Encode(1, -1022, kPositive) + Encode(1, -1022, kPositive));
+}
+
+TEST_F(DecimalTest, AddBigExponent) {
+ EXPECT_EQ(Encode(1, 1022, kPositive),
+ Encode(1, 1022, kPositive) + Encode(1, 0, kPositive));
+ EXPECT_EQ(Encode(2, 1022, kPositive),
+ Encode(1, 1022, kPositive) + Encode(1, 1022, kPositive));
+ EXPECT_EQ(Decimal::Infinity(kPositive),
+ Encode(std::numeric_limits<uint64_t>::max(), 1022, kPositive) +
+ Encode(1, 0, kPositive));
+ EXPECT_EQ(Encode(1, 1022, kPositive),
+ Encode(1, 1022, kPositive) + Encode(1, -1000, kPositive));
+}
+
+TEST_F(DecimalTest, AddSmallExponent) {
+ EXPECT_EQ(Encode(1, 0, kPositive),
+ Encode(1, -1022, kPositive) + Encode(1, 0, kPositive));
+ EXPECT_EQ(Encode(2, -1022, kPositive),
+ Encode(1, -1022, kPositive) + Encode(1, -1022, kPositive));
+}
+
+TEST_F(DecimalTest, AddSpecialValues) {
+ const Decimal infinity(Decimal::Infinity(kPositive));
+ const Decimal minus_infinity(Decimal::Infinity(kNegative));
+ const Decimal na_n(Decimal::Nan());
+ const Decimal ten(10);
+
+ EXPECT_EQ(infinity, infinity + infinity);
+ EXPECT_EQ(na_n, infinity + minus_infinity);
+ EXPECT_EQ(na_n, minus_infinity + infinity);
+ EXPECT_EQ(minus_infinity, minus_infinity + minus_infinity);
+
+ EXPECT_EQ(infinity, infinity + ten);
+ EXPECT_EQ(infinity, ten + infinity);
+ EXPECT_EQ(minus_infinity, minus_infinity + ten);
+ EXPECT_EQ(minus_infinity, ten + minus_infinity);
+
+ EXPECT_EQ(na_n, na_n + na_n);
+ EXPECT_EQ(na_n, na_n + ten);
+ EXPECT_EQ(na_n, ten + na_n);
+
+ EXPECT_EQ(na_n, na_n - infinity);
+ EXPECT_EQ(na_n, na_n - minus_infinity);
+ EXPECT_EQ(na_n, infinity - na_n);
+ EXPECT_EQ(na_n, minus_infinity - na_n);
+}
+
+TEST_F(DecimalTest, Ceil) {
+ EXPECT_EQ(Decimal(1), Decimal(1).Ceil());
+ EXPECT_EQ(Decimal(1), Encode(1, -10, kPositive).Ceil());
+ EXPECT_EQ(Decimal(2), Encode(11, -1, kPositive).Ceil());
+ EXPECT_EQ(Decimal(2), Encode(13, -1, kPositive).Ceil());
+ EXPECT_EQ(Decimal(2), Encode(15, -1, kPositive).Ceil());
+ EXPECT_EQ(Decimal(2), Encode(19, -1, kPositive).Ceil());
+ EXPECT_EQ(Decimal(2), Encode(151, -2, kPositive).Ceil());
+ EXPECT_EQ(Decimal(2), Encode(101, -2, kPositive).Ceil());
+ EXPECT_EQ(Decimal(1), Encode(199, -3, kPositive).Ceil());
+ EXPECT_EQ(Decimal(2), Encode(199, -2, kPositive).Ceil());
+ EXPECT_EQ(Decimal(3), Encode(209, -2, kPositive).Ceil());
+
+ EXPECT_EQ(Decimal(-1), Decimal(-1).Ceil());
+ EXPECT_EQ(Decimal(0), Encode(1, -10, kNegative).Ceil());
+ EXPECT_EQ(Decimal(-1), Encode(11, -1, kNegative).Ceil());
+ EXPECT_EQ(Decimal(-1), Encode(13, -1, kNegative).Ceil());
+ EXPECT_EQ(Decimal(-1), Encode(15, -1, kNegative).Ceil());
+ EXPECT_EQ(Decimal(-1), Encode(19, -1, kNegative).Ceil());
+ EXPECT_EQ(Decimal(-1), Encode(151, -2, kNegative).Ceil());
+ EXPECT_EQ(Decimal(-1), Encode(101, -2, kNegative).Ceil());
+ EXPECT_EQ(Decimal(0), Encode(199, -3, kNegative).Ceil());
+ EXPECT_EQ(Decimal(-1), Encode(199, -2, kNegative).Ceil());
+ EXPECT_EQ(Decimal(-2), Encode(209, -2, kNegative).Ceil());
+ EXPECT_EQ(Decimal(1),
+ Encode(UINT64_C(123456789012345678), -18, kPositive).Ceil());
+}
+
+TEST_F(DecimalTest, CeilingBigExponent) {
+ EXPECT_EQ(Encode(1, 1000, kPositive), Encode(1, 1000, kPositive).Ceil());
+ EXPECT_EQ(Encode(1, 1000, kNegative), Encode(1, 1000, kNegative).Ceil());
+}
+
+TEST_F(DecimalTest, CeilingSmallExponent) {
+ EXPECT_EQ(Encode(1, 0, kPositive), Encode(1, -1000, kPositive).Ceil());
+ EXPECT_EQ(Encode(0, 0, kNegative), Encode(1, -1000, kNegative).Ceil());
+}
+
+TEST_F(DecimalTest, CeilingSpecialValues) {
+ EXPECT_EQ(Decimal::Infinity(kPositive), Decimal::Infinity(kPositive).Ceil());
+ EXPECT_EQ(Decimal::Infinity(kNegative), Decimal::Infinity(kNegative).Ceil());
+ EXPECT_EQ(Decimal::Nan(), Decimal::Nan().Ceil());
+}
+
+TEST_F(DecimalTest, Compare) {
+ EXPECT_TRUE(Decimal(0) == Decimal(0));
+ EXPECT_TRUE(Decimal(0) != Decimal(1));
+ EXPECT_TRUE(Decimal(0) < Decimal(1));
+ EXPECT_TRUE(Decimal(0) <= Decimal(0));
+ EXPECT_TRUE(Decimal(0) > Decimal(-1));
+ EXPECT_TRUE(Decimal(0) >= Decimal(0));
+
+ EXPECT_FALSE(Decimal(1) == Decimal(2));
+ EXPECT_FALSE(Decimal(1) != Decimal(1));
+ EXPECT_FALSE(Decimal(1) < Decimal(0));
+ EXPECT_FALSE(Decimal(1) <= Decimal(0));
+ EXPECT_FALSE(Decimal(1) > Decimal(2));
+ EXPECT_FALSE(Decimal(1) >= Decimal(2));
+}
+
+TEST_F(DecimalTest, CompareBigExponent) {
+ EXPECT_TRUE(Encode(1, 1000, kPositive) == Encode(1, 1000, kPositive));
+ EXPECT_FALSE(Encode(1, 1000, kPositive) != Encode(1, 1000, kPositive));
+ EXPECT_FALSE(Encode(1, 1000, kPositive) < Encode(1, 1000, kPositive));
+ EXPECT_TRUE(Encode(1, 1000, kPositive) <= Encode(1, 1000, kPositive));
+ EXPECT_FALSE(Encode(1, 1000, kPositive) > Encode(1, 1000, kPositive));
+ EXPECT_TRUE(Encode(1, 1000, kPositive) >= Encode(1, 1000, kPositive));
+
+ EXPECT_TRUE(Encode(1, 1000, kNegative) == Encode(1, 1000, kNegative));
+ EXPECT_FALSE(Encode(1, 1000, kNegative) != Encode(1, 1000, kNegative));
+ EXPECT_FALSE(Encode(1, 1000, kNegative) < Encode(1, 1000, kNegative));
+ EXPECT_TRUE(Encode(1, 1000, kNegative) <= Encode(1, 1000, kNegative));
+ EXPECT_FALSE(Encode(1, 1000, kNegative) > Encode(1, 1000, kNegative));
+ EXPECT_TRUE(Encode(1, 1000, kNegative) >= Encode(1, 1000, kNegative));
+
+ EXPECT_FALSE(Encode(2, 1000, kPositive) == Encode(1, 1000, kPositive));
+ EXPECT_TRUE(Encode(2, 1000, kPositive) != Encode(1, 1000, kPositive));
+ EXPECT_FALSE(Encode(2, 1000, kPositive) < Encode(1, 1000, kPositive));
+ EXPECT_FALSE(Encode(2, 1000, kPositive) <= Encode(1, 1000, kPositive));
+ EXPECT_TRUE(Encode(2, 1000, kPositive) > Encode(1, 1000, kPositive));
+ EXPECT_TRUE(Encode(2, 1000, kPositive) >= Encode(1, 1000, kPositive));
+
+ EXPECT_FALSE(Encode(2, 1000, kNegative) == Encode(1, 1000, kNegative));
+ EXPECT_TRUE(Encode(2, 1000, kNegative) != Encode(1, 1000, kNegative));
+ EXPECT_TRUE(Encode(2, 1000, kNegative) < Encode(1, 1000, kNegative));
+ EXPECT_TRUE(Encode(2, 1000, kNegative) <= Encode(1, 1000, kNegative));
+ EXPECT_FALSE(Encode(2, 1000, kNegative) > Encode(1, 1000, kNegative));
+ EXPECT_FALSE(Encode(2, 1000, kNegative) >= Encode(1, 1000, kNegative));
+}
+
+TEST_F(DecimalTest, CompareSmallExponent) {
+ EXPECT_TRUE(Encode(1, -1000, kPositive) == Encode(1, -1000, kPositive));
+ EXPECT_FALSE(Encode(1, -1000, kPositive) != Encode(1, -1000, kPositive));
+ EXPECT_FALSE(Encode(1, -1000, kPositive) < Encode(1, -1000, kPositive));
+ EXPECT_TRUE(Encode(1, -1000, kPositive) <= Encode(1, -1000, kPositive));
+ EXPECT_FALSE(Encode(1, -1000, kPositive) > Encode(1, -1000, kPositive));
+ EXPECT_TRUE(Encode(1, -1000, kPositive) >= Encode(1, -1000, kPositive));
+
+ EXPECT_TRUE(Encode(1, -1000, kNegative) == Encode(1, -1000, kNegative));
+ EXPECT_FALSE(Encode(1, -1000, kNegative) != Encode(1, -1000, kNegative));
+ EXPECT_FALSE(Encode(1, -1000, kNegative) < Encode(1, -1000, kNegative));
+ EXPECT_TRUE(Encode(1, -1000, kNegative) <= Encode(1, -1000, kNegative));
+ EXPECT_FALSE(Encode(1, -1000, kNegative) > Encode(1, -1000, kNegative));
+ EXPECT_TRUE(Encode(1, -1000, kNegative) >= Encode(1, -1000, kNegative));
+
+ EXPECT_FALSE(Encode(2, -1000, kPositive) == Encode(1, -1000, kPositive));
+ EXPECT_TRUE(Encode(2, -1000, kPositive) != Encode(1, -1000, kPositive));
+ EXPECT_FALSE(Encode(2, -1000, kPositive) < Encode(1, -1000, kPositive));
+ EXPECT_FALSE(Encode(2, -1000, kPositive) <= Encode(1, -1000, kPositive));
+ EXPECT_TRUE(Encode(2, -1000, kPositive) > Encode(1, -1000, kPositive));
+ EXPECT_TRUE(Encode(2, -1000, kPositive) >= Encode(1, -1000, kPositive));
+
+ EXPECT_FALSE(Encode(2, -1000, kNegative) == Encode(1, -1000, kNegative));
+ EXPECT_TRUE(Encode(2, -1000, kNegative) != Encode(1, -1000, kNegative));
+ EXPECT_TRUE(Encode(2, -1000, kNegative) < Encode(1, -1000, kNegative));
+ EXPECT_TRUE(Encode(2, -1000, kNegative) <= Encode(1, -1000, kNegative));
+ EXPECT_FALSE(Encode(2, -1000, kNegative) > Encode(1, -1000, kNegative));
+ EXPECT_FALSE(Encode(2, -1000, kNegative) >= Encode(1, -1000, kNegative));
+}
+
+TEST_F(DecimalTest, CompareSpecialValues) {
+ const Decimal infinity(Decimal::Infinity(kPositive));
+ const Decimal minus_infinity(Decimal::Infinity(kNegative));
+ const Decimal na_n(Decimal::Nan());
+ const Decimal zero(Decimal::Zero(kPositive));
+ const Decimal minus_zero(Decimal::Zero(kNegative));
+ const Decimal ten(10);
+
+ EXPECT_TRUE(zero == zero);
+ EXPECT_FALSE(zero != zero);
+ EXPECT_FALSE(zero < zero);
+ EXPECT_TRUE(zero <= zero);
+ EXPECT_FALSE(zero > zero);
+ EXPECT_TRUE(zero >= zero);
+
+ EXPECT_TRUE(zero == minus_zero);
+ EXPECT_FALSE(zero != minus_zero);
+ EXPECT_FALSE(zero < minus_zero);
+ EXPECT_TRUE(zero <= minus_zero);
+ EXPECT_FALSE(zero > minus_zero);
+ EXPECT_TRUE(zero >= minus_zero);
+
+ EXPECT_TRUE(minus_zero == zero);
+ EXPECT_FALSE(minus_zero != zero);
+ EXPECT_FALSE(minus_zero < zero);
+ EXPECT_TRUE(minus_zero <= zero);
+ EXPECT_FALSE(minus_zero > zero);
+ EXPECT_TRUE(minus_zero >= zero);
+
+ EXPECT_TRUE(minus_zero == minus_zero);
+ EXPECT_FALSE(minus_zero != minus_zero);
+ EXPECT_FALSE(minus_zero < minus_zero);
+ EXPECT_TRUE(minus_zero <= minus_zero);
+ EXPECT_FALSE(minus_zero > minus_zero);
+ EXPECT_TRUE(minus_zero >= minus_zero);
+
+ EXPECT_TRUE(infinity == infinity);
+ EXPECT_FALSE(infinity != infinity);
+ EXPECT_FALSE(infinity < infinity);
+ EXPECT_TRUE(infinity <= infinity);
+ EXPECT_FALSE(infinity > infinity);
+ EXPECT_TRUE(infinity >= infinity);
+
+ EXPECT_FALSE(infinity == ten);
+ EXPECT_TRUE(infinity != ten);
+ EXPECT_FALSE(infinity < ten);
+ EXPECT_FALSE(infinity <= ten);
+ EXPECT_TRUE(infinity > ten);
+ EXPECT_TRUE(infinity >= ten);
+
+ EXPECT_FALSE(infinity == minus_infinity);
+ EXPECT_TRUE(infinity != minus_infinity);
+ EXPECT_FALSE(infinity < minus_infinity);
+ EXPECT_FALSE(infinity <= minus_infinity);
+ EXPECT_TRUE(infinity > minus_infinity);
+ EXPECT_TRUE(infinity >= minus_infinity);
+
+ EXPECT_FALSE(infinity == na_n);
+ EXPECT_FALSE(infinity != na_n);
+ EXPECT_FALSE(infinity < na_n);
+ EXPECT_FALSE(infinity <= na_n);
+ EXPECT_FALSE(infinity > na_n);
+ EXPECT_FALSE(infinity >= na_n);
+
+ EXPECT_FALSE(minus_infinity == infinity);
+ EXPECT_TRUE(minus_infinity != infinity);
+ EXPECT_TRUE(minus_infinity < infinity);
+ EXPECT_TRUE(minus_infinity <= infinity);
+ EXPECT_FALSE(minus_infinity > infinity);
+ EXPECT_FALSE(minus_infinity >= infinity);
+
+ EXPECT_FALSE(minus_infinity == ten);
+ EXPECT_TRUE(minus_infinity != ten);
+ EXPECT_TRUE(minus_infinity < ten);
+ EXPECT_TRUE(minus_infinity <= ten);
+ EXPECT_FALSE(minus_infinity > ten);
+ EXPECT_FALSE(minus_infinity >= ten);
+
+ EXPECT_TRUE(minus_infinity == minus_infinity);
+ EXPECT_FALSE(minus_infinity != minus_infinity);
+ EXPECT_FALSE(minus_infinity < minus_infinity);
+ EXPECT_TRUE(minus_infinity <= minus_infinity);
+ EXPECT_FALSE(minus_infinity > minus_infinity);
+ EXPECT_TRUE(minus_infinity >= minus_infinity);
+
+ EXPECT_FALSE(minus_infinity == na_n);
+ EXPECT_FALSE(minus_infinity != na_n);
+ EXPECT_FALSE(minus_infinity < na_n);
+ EXPECT_FALSE(minus_infinity <= na_n);
+ EXPECT_FALSE(minus_infinity > na_n);
+ EXPECT_FALSE(minus_infinity >= na_n);
+
+ EXPECT_FALSE(na_n == infinity);
+ EXPECT_FALSE(na_n != infinity);
+ EXPECT_FALSE(na_n < infinity);
+ EXPECT_FALSE(na_n <= infinity);
+ EXPECT_FALSE(na_n > infinity);
+ EXPECT_FALSE(na_n >= infinity);
+
+ EXPECT_FALSE(na_n == ten);
+ EXPECT_FALSE(na_n != ten);
+ EXPECT_FALSE(na_n < ten);
+ EXPECT_FALSE(na_n <= ten);
+ EXPECT_FALSE(na_n > ten);
+ EXPECT_FALSE(na_n >= ten);
+
+ EXPECT_FALSE(na_n == minus_infinity);
+ EXPECT_FALSE(na_n != minus_infinity);
+ EXPECT_FALSE(na_n < minus_infinity);
+ EXPECT_FALSE(na_n <= minus_infinity);
+ EXPECT_FALSE(na_n > minus_infinity);
+ EXPECT_FALSE(na_n >= minus_infinity);
+
+ EXPECT_TRUE(na_n == na_n);
+ EXPECT_FALSE(na_n != na_n);
+ EXPECT_FALSE(na_n < na_n);
+ EXPECT_TRUE(na_n <= na_n);
+ EXPECT_FALSE(na_n > na_n);
+ EXPECT_TRUE(na_n >= na_n);
+}
+
+TEST_F(DecimalTest, Constructor) {
+ EXPECT_DECIMAL_ENCODED_DATA_EQ(0u, 0, kPositive, Encode(0, 0, kPositive));
+ EXPECT_DECIMAL_ENCODED_DATA_EQ(0u, 0, kNegative, Encode(0, 0, kNegative));
+ EXPECT_DECIMAL_ENCODED_DATA_EQ(1u, 0, kPositive, Encode(1, 0, kPositive));
+ EXPECT_DECIMAL_ENCODED_DATA_EQ(1u, 0, kNegative, Encode(1, 0, kNegative));
+ EXPECT_DECIMAL_ENCODED_DATA_EQ(1u, 1022, kPositive,
+ Encode(1, 1022, kPositive));
+ EXPECT_DECIMAL_ENCODED_DATA_EQ(1u, 1022, kNegative,
+ Encode(1, 1022, kNegative));
+ EXPECT_DECIMAL_ENCODED_DATA_EQ(1u, 1023, kPositive,
+ Encode(1, 1023, kPositive));
+ EXPECT_DECIMAL_ENCODED_DATA_EQ(1u, 1023, kNegative,
+ Encode(1, 1023, kNegative));
+ EXPECT_TRUE(Encode(1, 2000, kPositive).IsInfinity());
+ EXPECT_TRUE(Encode(1, 2000, kNegative).IsInfinity());
+ EXPECT_DECIMAL_ENCODED_DATA_EQ(0u, 0, kPositive, Encode(1, -2000, kPositive));
+ EXPECT_DECIMAL_ENCODED_DATA_EQ(0u, 0, kNegative, Encode(1, -2000, kNegative));
+ EXPECT_DECIMAL_ENCODED_DATA_EQ(
+ UINT64_C(99999999999999998), 0, kPositive,
+ Encode(UINT64_C(99999999999999998), 0, kPositive));
+ EXPECT_DECIMAL_ENCODED_DATA_EQ(
+ UINT64_C(99999999999999998), 0, kNegative,
+ Encode(UINT64_C(99999999999999998), 0, kNegative));
+ EXPECT_DECIMAL_ENCODED_DATA_EQ(
+ UINT64_C(99999999999999999), 0, kPositive,
+ Encode(UINT64_C(99999999999999999), 0, kPositive));
+ EXPECT_DECIMAL_ENCODED_DATA_EQ(
+ UINT64_C(99999999999999999), 0, kNegative,
+ Encode(UINT64_C(99999999999999999), 0, kNegative));
+ EXPECT_DECIMAL_ENCODED_DATA_EQ(
+ UINT64_C(100000000000000000), 0, kPositive,
+ Encode(UINT64_C(100000000000000000), 0, kPositive));
+ EXPECT_DECIMAL_ENCODED_DATA_EQ(
+ UINT64_C(100000000000000000), 0, kNegative,
+ Encode(UINT64_C(100000000000000000), 0, kNegative));
+}
+
+TEST_F(DecimalTest, Division) {
+ EXPECT_EQ(Encode(0, 0, kPositive), Decimal(0) / Decimal(1));
+ EXPECT_EQ(Encode(2, 0, kNegative), Decimal(2) / Decimal(-1));
+ EXPECT_EQ(Encode(5, -1, kNegative), Decimal(-1) / Decimal(2));
+ EXPECT_EQ(Encode(99, 0, kPositive), Decimal(99) / Decimal(1));
+ EXPECT_EQ(Decimal(1), Decimal(-50) / Decimal(-50));
+ EXPECT_EQ(Encode(UINT64_C(333333333333333333), -18, kPositive),
+ Decimal(1) / Decimal(3));
+ EXPECT_EQ(Encode(UINT64_C(12345678901234), -1, kPositive),
+ Encode(UINT64_C(12345678901234), 0, kPositive) / Decimal(10));
+ EXPECT_EQ(Encode(UINT64_C(500005000050000500), -18, kPositive),
+ Decimal(50000) / Decimal(99999));
+}
+
+TEST_F(DecimalTest, DivisionBigExponent) {
+ EXPECT_EQ(Encode(1, 1022, kPositive),
+ Encode(1, 1022, kPositive) / Encode(1, 0, kPositive));
+ EXPECT_EQ(Encode(1, 0, kPositive),
+ Encode(1, 1022, kPositive) / Encode(1, 1022, kPositive));
+ EXPECT_EQ(Decimal::Infinity(kPositive),
+ Encode(1, 1022, kPositive) / Encode(1, -1000, kPositive));
+}
+
+TEST_F(DecimalTest, DivisionSmallExponent) {
+ EXPECT_EQ(Encode(1, -1022, kPositive),
+ Encode(1, -1022, kPositive) / Encode(1, 0, kPositive));
+ EXPECT_EQ(Encode(1, 0, kPositive),
+ Encode(1, -1022, kPositive) / Encode(1, -1022, kPositive));
+}
+
+TEST_F(DecimalTest, DivisionSpecialValues) {
+ const Decimal infinity(Decimal::Infinity(kPositive));
+ const Decimal minus_infinity(Decimal::Infinity(kNegative));
+ const Decimal na_n(Decimal::Nan());
+ const Decimal zero(Decimal::Zero(kPositive));
+ const Decimal minus_zero(Decimal::Zero(kNegative));
+ const Decimal ten(10);
+ const Decimal minus_ten(-10);
+
+ EXPECT_EQ(na_n, zero / zero);
+ EXPECT_EQ(na_n, zero / minus_zero);
+ EXPECT_EQ(na_n, minus_zero / zero);
+ EXPECT_EQ(na_n, minus_zero / minus_zero);
+
+ EXPECT_EQ(infinity, ten / zero);
+ EXPECT_EQ(minus_infinity, ten / minus_zero);
+ EXPECT_EQ(minus_infinity, minus_ten / zero);
+ EXPECT_EQ(infinity, minus_ten / minus_zero);
+
+ EXPECT_EQ(infinity, infinity / zero);
+ EXPECT_EQ(minus_infinity, infinity / minus_zero);
+ EXPECT_EQ(minus_infinity, minus_infinity / zero);
+ EXPECT_EQ(infinity, minus_infinity / minus_zero);
+
+ EXPECT_EQ(na_n, infinity / infinity);
+ EXPECT_EQ(na_n, infinity / minus_infinity);
+ EXPECT_EQ(na_n, minus_infinity / infinity);
+ EXPECT_EQ(na_n, minus_infinity / minus_infinity);
+
+ EXPECT_EQ(zero, ten / infinity);
+ EXPECT_EQ(minus_zero, ten / minus_infinity);
+ EXPECT_EQ(minus_zero, minus_ten / infinity);
+ EXPECT_EQ(zero, minus_ten / minus_infinity);
+
+ EXPECT_EQ(na_n, na_n / na_n);
+ EXPECT_EQ(na_n, na_n / ten);
+ EXPECT_EQ(na_n, ten / na_n);
+
+ EXPECT_EQ(na_n, na_n / infinity);
+ EXPECT_EQ(na_n, na_n / minus_infinity);
+ EXPECT_EQ(na_n, infinity / na_n);
+ EXPECT_EQ(na_n, minus_infinity / na_n);
+}
+
+TEST_F(DecimalTest, EncodedData) {
+ EXPECT_EQ(Encode(0, 0, kPositive), Encode(0, 0, kPositive));
+ EXPECT_EQ(Encode(0, 0, kNegative), Encode(0, 0, kNegative));
+ EXPECT_EQ(Decimal(1), Decimal(1));
+ EXPECT_EQ(Encode(1, 0, kNegative), Encode(1, 0, kNegative));
+ EXPECT_EQ(Decimal::Infinity(kPositive), Encode(1, 2000, kPositive));
+ EXPECT_EQ(Decimal::Zero(kPositive), Encode(1, -2000, kPositive));
+}
+
+TEST_F(DecimalTest, Floor) {
+ EXPECT_EQ(Decimal(1), Decimal(1).Floor());
+ EXPECT_EQ(Decimal(0), Encode(1, -10, kPositive).Floor());
+ EXPECT_EQ(Decimal(1), Encode(11, -1, kPositive).Floor());
+ EXPECT_EQ(Decimal(1), Encode(13, -1, kPositive).Floor());
+ EXPECT_EQ(Decimal(1), Encode(15, -1, kPositive).Floor());
+ EXPECT_EQ(Decimal(1), Encode(19, -1, kPositive).Floor());
+ EXPECT_EQ(Decimal(1), Encode(193332, -5, kPositive).Floor());
+ EXPECT_EQ(Decimal(12), Encode(12002, -3, kPositive).Floor());
+
+ EXPECT_EQ(Decimal(-1), Decimal(-1).Floor());
+ EXPECT_EQ(Decimal(-1), Encode(1, -10, kNegative).Floor());
+ EXPECT_EQ(Decimal(-2), Encode(11, -1, kNegative).Floor());
+ EXPECT_EQ(Decimal(-2), Encode(13, -1, kNegative).Floor());
+ EXPECT_EQ(Decimal(-2), Encode(15, -1, kNegative).Floor());
+ EXPECT_EQ(Decimal(-2), Encode(19, -1, kNegative).Floor());
+ EXPECT_EQ(Decimal(-2), Encode(193332, -5, kNegative).Floor());
+ EXPECT_EQ(Decimal(-13), Encode(12002, -3, kNegative).Floor());
+
+ // crbug.com/572769
+ EXPECT_EQ(Decimal(-1), Encode(992971299197409433, -18, kNegative).Floor());
+}
+
+TEST_F(DecimalTest, FloorBigExponent) {
+ EXPECT_EQ(Encode(1, 1000, kPositive), Encode(1, 1000, kPositive).Floor());
+ EXPECT_EQ(Encode(1, 1000, kNegative), Encode(1, 1000, kNegative).Floor());
+}
+
+TEST_F(DecimalTest, FloorSmallExponent) {
+ EXPECT_EQ(Encode(0, 0, kPositive), Encode(1, -1000, kPositive).Floor());
+ EXPECT_EQ(Encode(1, 0, kNegative), Encode(1, -1000, kNegative).Floor());
+}
+
+TEST_F(DecimalTest, FloorSpecialValues) {
+ EXPECT_EQ(Decimal::Infinity(kPositive), Decimal::Infinity(kPositive).Floor());
+ EXPECT_EQ(Decimal::Infinity(kNegative), Decimal::Infinity(kNegative).Floor());
+ EXPECT_EQ(Decimal::Nan(), Decimal::Nan().Floor());
+}
+
+TEST_F(DecimalTest, FromDouble) {
+ EXPECT_EQ(Encode(0, 0, kPositive), Decimal::FromDouble(0.0));
+ EXPECT_EQ(Encode(0, 0, kNegative), Decimal::FromDouble(-0.0));
+ EXPECT_EQ(Encode(1, 0, kPositive), Decimal::FromDouble(1));
+ EXPECT_EQ(Encode(1, 0, kNegative), Decimal::FromDouble(-1));
+ EXPECT_EQ(Encode(123, 0, kPositive), Decimal::FromDouble(123));
+ EXPECT_EQ(Encode(123, 0, kNegative), Decimal::FromDouble(-123));
+ EXPECT_EQ(Encode(1, -1, kPositive), Decimal::FromDouble(0.1));
+ EXPECT_EQ(Encode(1, -1, kNegative), Decimal::FromDouble(-0.1));
+}
+
+TEST_F(DecimalTest, FromDoubleLimits) {
+ EXPECT_EQ(Encode(UINT64_C(2220446049250313), -31, kPositive),
+ Decimal::FromDouble(std::numeric_limits<double>::epsilon()));
+ EXPECT_EQ(Encode(UINT64_C(2220446049250313), -31, kNegative),
+ Decimal::FromDouble(-std::numeric_limits<double>::epsilon()));
+ EXPECT_EQ(Encode(UINT64_C(17976931348623157), 292, kPositive),
+ Decimal::FromDouble(std::numeric_limits<double>::max()));
+ EXPECT_EQ(Encode(UINT64_C(17976931348623157), 292, kNegative),
+ Decimal::FromDouble(-std::numeric_limits<double>::max()));
+ EXPECT_EQ(Encode(UINT64_C(22250738585072014), -324, kPositive),
+ Decimal::FromDouble(std::numeric_limits<double>::min()));
+ EXPECT_EQ(Encode(UINT64_C(22250738585072014), -324, kNegative),
+ Decimal::FromDouble(-std::numeric_limits<double>::min()));
+ EXPECT_TRUE(Decimal::FromDouble(std::numeric_limits<double>::infinity())
+ .IsInfinity());
+ EXPECT_TRUE(Decimal::FromDouble(-std::numeric_limits<double>::infinity())
+ .IsInfinity());
+ EXPECT_TRUE(
+ Decimal::FromDouble(std::numeric_limits<double>::quiet_NaN()).IsNaN());
+ EXPECT_TRUE(
+ Decimal::FromDouble(-std::numeric_limits<double>::quiet_NaN()).IsNaN());
+}
+
+TEST_F(DecimalTest, FromInt32) {
+ EXPECT_EQ(Encode(0, 0, kPositive), Decimal(0));
+ EXPECT_EQ(Encode(1, 0, kPositive), Decimal(1));
+ EXPECT_EQ(Encode(1, 0, kNegative), Decimal(-1));
+ EXPECT_EQ(Encode(100, 0, kPositive), Decimal(100));
+ EXPECT_EQ(Encode(100, 0, kNegative), Decimal(-100));
+ EXPECT_EQ(Encode(0x7FFFFFFF, 0, kPositive),
+ Decimal(std::numeric_limits<int32_t>::max()));
+ EXPECT_EQ(Encode(0x80000000u, 0, kNegative),
+ Decimal(std::numeric_limits<int32_t>::min()));
+}
+
+TEST_F(DecimalTest, FromString) {
+ EXPECT_EQ(Encode(0, 0, kPositive), FromString("0"));
+ EXPECT_EQ(Encode(0, 0, kNegative), FromString("-0"));
+ EXPECT_EQ(Decimal(1), FromString("1"));
+ EXPECT_EQ(Encode(1, 0, kNegative), FromString("-1"));
+ EXPECT_EQ(Decimal(1), FromString("01"));
+ EXPECT_EQ(Encode(3, 0, kPositive), FromString("+3"));
+ EXPECT_EQ(Encode(0, 3, kPositive), FromString("0E3"));
+ EXPECT_EQ(Encode(5, -1, kPositive), FromString(".5"));
+ EXPECT_EQ(Encode(100, 0, kPositive), FromString("100"));
+ EXPECT_EQ(Encode(100, 0, kNegative), FromString("-100"));
+ EXPECT_EQ(Encode(123, -2, kPositive), FromString("1.23"));
+ EXPECT_EQ(Encode(123, -2, kNegative), FromString("-1.23"));
+ EXPECT_EQ(Encode(123, 8, kPositive), FromString("1.23E10"));
+ EXPECT_EQ(Encode(123, 8, kNegative), FromString("-1.23E10"));
+ EXPECT_EQ(Encode(123, 8, kPositive), FromString("1.23E+10"));
+ EXPECT_EQ(Encode(123, 8, kNegative), FromString("-1.23E+10"));
+ EXPECT_EQ(Encode(123, -12, kPositive), FromString("1.23E-10"));
+ EXPECT_EQ(Encode(123, -12, kNegative), FromString("-1.23E-10"));
+ EXPECT_EQ(Encode(5, -7, kPositive), FromString("0.0000005"));
+ EXPECT_EQ(Encode(0, 0, kPositive), FromString("0e9999"));
+ EXPECT_EQ(Encode(123, -3, kPositive), FromString("0.123"));
+ EXPECT_EQ(Encode(0, -2, kPositive), FromString("00.00"));
+ EXPECT_EQ(Encode(1, 2, kPositive), FromString("1E2"));
+ EXPECT_EQ(Decimal::Infinity(kPositive), FromString("1E20000"));
+ EXPECT_EQ(Decimal::Zero(kPositive), FromString("1E-20000"));
+ EXPECT_EQ(Encode(1000, 1023, kPositive), FromString("1E1026"));
+ EXPECT_EQ(Decimal::Zero(kPositive), FromString("1E-1026"));
+ EXPECT_EQ(Decimal::Infinity(kPositive), FromString("1234567890E1036"));
+
+ // 2^1024
+ const uint64_t kLeadingDigitsOf2PowerOf1024 = UINT64_C(17976931348623159);
+ EXPECT_EQ(Encode(kLeadingDigitsOf2PowerOf1024, 292, kPositive),
+ FromString("1797693134862315907729305190789024733617976978942306572"
+ "7343008115773267580550096313270847732240753602112011387"
+ "9871393357658789768814416622492847430639474124377767893"
+ "4248654852763022196012460941194530829520850057688381506"
+ "8234246288147391311054082723716335051068458629823994724"
+ "5938479716304835356329624224137216"));
+}
+
+// These strings are look like proper number, but we don't accept them.
+TEST_F(DecimalTest, FromStringLikeNumber) {
+ EXPECT_EQ(Decimal::Nan(), FromString(" 123 "));
+ EXPECT_EQ(Decimal::Nan(), FromString("1,234"));
+}
+
+// fromString doesn't support infinity and NaN.
+TEST_F(DecimalTest, FromStringSpecialValues) {
+ EXPECT_EQ(Decimal::Nan(), FromString("INF"));
+ EXPECT_EQ(Decimal::Nan(), FromString("Infinity"));
+ EXPECT_EQ(Decimal::Nan(), FromString("infinity"));
+ EXPECT_EQ(Decimal::Nan(), FromString("+Infinity"));
+ EXPECT_EQ(Decimal::Nan(), FromString("+infinity"));
+ EXPECT_EQ(Decimal::Nan(), FromString("-Infinity"));
+ EXPECT_EQ(Decimal::Nan(), FromString("-infinity"));
+ EXPECT_EQ(Decimal::Nan(), FromString("NaN"));
+ EXPECT_EQ(Decimal::Nan(), FromString("nan"));
+ EXPECT_EQ(Decimal::Nan(), FromString("+NaN"));
+ EXPECT_EQ(Decimal::Nan(), FromString("+nan"));
+ EXPECT_EQ(Decimal::Nan(), FromString("-NaN"));
+ EXPECT_EQ(Decimal::Nan(), FromString("-nan"));
+}
+
+TEST_F(DecimalTest, fromStringTruncated) {
+ EXPECT_EQ(Decimal::Nan(), FromString("x"));
+ EXPECT_EQ(Decimal::Nan(), FromString("0."));
+ EXPECT_EQ(Decimal::Nan(), FromString("1x"));
+
+ EXPECT_EQ(Decimal::Nan(), FromString("1Ex"));
+ EXPECT_EQ(Decimal::Nan(), FromString("1E2x"));
+ EXPECT_EQ(Decimal::Nan(), FromString("1E+x"));
+}
+
+TEST_F(DecimalTest, Multiplication) {
+ EXPECT_EQ(Encode(0, 0, kPositive), Decimal(0) * Decimal(0));
+ EXPECT_EQ(Encode(2, 0, kNegative), Decimal(2) * Decimal(-1));
+ EXPECT_EQ(Encode(2, 0, kNegative), Decimal(-1) * Decimal(2));
+ EXPECT_EQ(Encode(99, 0, kPositive), Decimal(99) * Decimal(1));
+ EXPECT_EQ(Encode(2500, 0, kPositive), Decimal(-50) * Decimal(-50));
+ EXPECT_EQ(Encode(1, 21, kPositive),
+ Encode(UINT64_C(10000000000), 0, kPositive) *
+ Encode(UINT64_C(100000000000), 0, kPositive));
+}
+
+TEST_F(DecimalTest, MultiplicationBigExponent) {
+ EXPECT_EQ(Encode(1, 1022, kPositive),
+ Encode(1, 1022, kPositive) * Encode(1, 0, kPositive));
+ EXPECT_EQ(Decimal::Infinity(kPositive),
+ Encode(1, 1022, kPositive) * Encode(1, 1022, kPositive));
+ EXPECT_EQ(Encode(1, 22, kPositive),
+ Encode(1, 1022, kPositive) * Encode(1, -1000, kPositive));
+}
+
+TEST_F(DecimalTest, MultiplicationSmallExponent) {
+ EXPECT_EQ(Encode(1, -1022, kPositive),
+ Encode(1, -1022, kPositive) * Encode(1, 0, kPositive));
+ EXPECT_EQ(Encode(0, 0, kPositive),
+ Encode(1, -1022, kPositive) * Encode(1, -1022, kPositive));
+}
+
+TEST_F(DecimalTest, MultiplicationSpecialValues) {
+ const Decimal infinity(Decimal::Infinity(kPositive));
+ const Decimal minus_infinity(Decimal::Infinity(kNegative));
+ const Decimal na_n(Decimal::Nan());
+ const Decimal ten(10);
+ const Decimal minus_ten(-10);
+ const Decimal zero(Decimal::Zero(kPositive));
+ const Decimal minus_zero(Decimal::Zero(kNegative));
+
+ EXPECT_EQ(infinity, infinity * infinity);
+ EXPECT_EQ(minus_infinity, infinity * minus_infinity);
+ EXPECT_EQ(minus_infinity, minus_infinity * infinity);
+ EXPECT_EQ(infinity, minus_infinity * minus_infinity);
+
+ EXPECT_EQ(na_n, infinity * zero);
+ EXPECT_EQ(na_n, zero * minus_infinity);
+ EXPECT_EQ(na_n, minus_infinity * zero);
+ EXPECT_EQ(na_n, minus_infinity * zero);
+
+ EXPECT_EQ(na_n, infinity * minus_zero);
+ EXPECT_EQ(na_n, minus_zero * minus_infinity);
+ EXPECT_EQ(na_n, minus_infinity * minus_zero);
+ EXPECT_EQ(na_n, minus_infinity * minus_zero);
+
+ EXPECT_EQ(infinity, infinity * ten);
+ EXPECT_EQ(infinity, ten * infinity);
+ EXPECT_EQ(minus_infinity, minus_infinity * ten);
+ EXPECT_EQ(minus_infinity, ten * minus_infinity);
+
+ EXPECT_EQ(minus_infinity, infinity * minus_ten);
+ EXPECT_EQ(minus_infinity, minus_ten * infinity);
+ EXPECT_EQ(infinity, minus_infinity * minus_ten);
+ EXPECT_EQ(infinity, minus_ten * minus_infinity);
+
+ EXPECT_EQ(na_n, na_n * na_n);
+ EXPECT_EQ(na_n, na_n * ten);
+ EXPECT_EQ(na_n, ten * na_n);
+
+ EXPECT_EQ(na_n, na_n * infinity);
+ EXPECT_EQ(na_n, na_n * minus_infinity);
+ EXPECT_EQ(na_n, infinity * na_n);
+ EXPECT_EQ(na_n, minus_infinity * na_n);
+}
+
+TEST_F(DecimalTest, Negate) {
+ EXPECT_EQ(Encode(0, 0, kNegative), -Encode(0, 0, kPositive));
+ EXPECT_EQ(Encode(0, 0, kPositive), -Encode(0, 0, kNegative));
+
+ EXPECT_EQ(Encode(0, 10, kNegative), -Encode(0, 10, kPositive));
+ EXPECT_EQ(Encode(0, 10, kPositive), -Encode(0, 10, kNegative));
+
+ EXPECT_EQ(Encode(0, -10, kNegative), -Encode(0, -10, kPositive));
+ EXPECT_EQ(Encode(0, -10, kPositive), -Encode(0, -10, kNegative));
+
+ EXPECT_EQ(Encode(1, 0, kNegative), -Encode(1, 0, kPositive));
+ EXPECT_EQ(Encode(1, 0, kPositive), -Encode(1, 0, kNegative));
+
+ EXPECT_EQ(Encode(1, 10, kNegative), -Encode(1, 10, kPositive));
+ EXPECT_EQ(Encode(1, 10, kPositive), -Encode(1, 10, kNegative));
+
+ EXPECT_EQ(Encode(1, -10, kNegative), -Encode(1, -10, kPositive));
+ EXPECT_EQ(Encode(1, -10, kPositive), -Encode(1, -10, kNegative));
+}
+
+TEST_F(DecimalTest, NegateBigExponent) {
+ EXPECT_EQ(Encode(1, 1000, kNegative), -Encode(1, 1000, kPositive));
+ EXPECT_EQ(Encode(1, 1000, kPositive), -Encode(1, 1000, kNegative));
+}
+
+TEST_F(DecimalTest, NegateSmallExponent) {
+ EXPECT_EQ(Encode(1, -1000, kNegative), -Encode(1, -1000, kPositive));
+ EXPECT_EQ(Encode(1, -1000, kPositive), -Encode(1, -1000, kNegative));
+}
+
+TEST_F(DecimalTest, NegateSpecialValues) {
+ EXPECT_EQ(Decimal::Infinity(kNegative), -Decimal::Infinity(kPositive));
+ EXPECT_EQ(Decimal::Infinity(kPositive), -Decimal::Infinity(kNegative));
+ EXPECT_EQ(Decimal::Nan(), -Decimal::Nan());
+}
+
+TEST_F(DecimalTest, Predicates) {
+ EXPECT_TRUE(Decimal::Zero(kPositive).IsFinite());
+ EXPECT_FALSE(Decimal::Zero(kPositive).IsInfinity());
+ EXPECT_FALSE(Decimal::Zero(kPositive).IsNaN());
+ EXPECT_TRUE(Decimal::Zero(kPositive).IsPositive());
+ EXPECT_FALSE(Decimal::Zero(kPositive).IsNegative());
+ EXPECT_FALSE(Decimal::Zero(kPositive).IsSpecial());
+ EXPECT_TRUE(Decimal::Zero(kPositive).IsZero());
+
+ EXPECT_TRUE(Decimal::Zero(kNegative).IsFinite());
+ EXPECT_FALSE(Decimal::Zero(kNegative).IsInfinity());
+ EXPECT_FALSE(Decimal::Zero(kNegative).IsNaN());
+ EXPECT_FALSE(Decimal::Zero(kNegative).IsPositive());
+ EXPECT_TRUE(Decimal::Zero(kNegative).IsNegative());
+ EXPECT_FALSE(Decimal::Zero(kNegative).IsSpecial());
+ EXPECT_TRUE(Decimal::Zero(kNegative).IsZero());
+
+ EXPECT_TRUE(Decimal(123).IsFinite());
+ EXPECT_FALSE(Decimal(123).IsInfinity());
+ EXPECT_FALSE(Decimal(123).IsNaN());
+ EXPECT_TRUE(Decimal(123).IsPositive());
+ EXPECT_FALSE(Decimal(123).IsNegative());
+ EXPECT_FALSE(Decimal(123).IsSpecial());
+ EXPECT_FALSE(Decimal(123).IsZero());
+
+ EXPECT_TRUE(Decimal(-123).IsFinite());
+ EXPECT_FALSE(Decimal(-123).IsInfinity());
+ EXPECT_FALSE(Decimal(-123).IsNaN());
+ EXPECT_FALSE(Decimal(-123).IsPositive());
+ EXPECT_TRUE(Decimal(-123).IsNegative());
+ EXPECT_FALSE(Decimal(-123).IsSpecial());
+ EXPECT_FALSE(Decimal(-123).IsZero());
+}
+
+TEST_F(DecimalTest, PredicatesSpecialValues) {
+ EXPECT_FALSE(Decimal::Infinity(kPositive).IsFinite());
+ EXPECT_TRUE(Decimal::Infinity(kPositive).IsInfinity());
+ EXPECT_FALSE(Decimal::Infinity(kPositive).IsNaN());
+ EXPECT_TRUE(Decimal::Infinity(kPositive).IsPositive());
+ EXPECT_FALSE(Decimal::Infinity(kPositive).IsNegative());
+ EXPECT_TRUE(Decimal::Infinity(kPositive).IsSpecial());
+ EXPECT_FALSE(Decimal::Infinity(kPositive).IsZero());
+
+ EXPECT_FALSE(Decimal::Infinity(kNegative).IsFinite());
+ EXPECT_TRUE(Decimal::Infinity(kNegative).IsInfinity());
+ EXPECT_FALSE(Decimal::Infinity(kNegative).IsNaN());
+ EXPECT_FALSE(Decimal::Infinity(kNegative).IsPositive());
+ EXPECT_TRUE(Decimal::Infinity(kNegative).IsNegative());
+ EXPECT_TRUE(Decimal::Infinity(kNegative).IsSpecial());
+ EXPECT_FALSE(Decimal::Infinity(kNegative).IsZero());
+
+ EXPECT_FALSE(Decimal::Nan().IsFinite());
+ EXPECT_FALSE(Decimal::Nan().IsInfinity());
+ EXPECT_TRUE(Decimal::Nan().IsNaN());
+ EXPECT_TRUE(Decimal::Nan().IsSpecial());
+ EXPECT_FALSE(Decimal::Nan().IsZero());
+}
+
+// LayoutTests/fast/forms/number/number-stepup-stepdown-from-renderer
+TEST_F(DecimalTest, RealWorldExampleNumberStepUpStepDownFromRenderer) {
+ EXPECT_DECIMAL_STREQ("10", StepDown("0", "100", "10", "19", 1));
+ EXPECT_DECIMAL_STREQ("90", StepUp("0", "99", "10", "89", 1));
+ EXPECT_DECIMAL_STREQ(
+ "1", StepUp("0", "1", "0.33333333333333333", "0", 3)); // step=1/3
+ EXPECT_DECIMAL_STREQ("0.01", StepUp("0", "0.01", "0.0033333333333333333", "0",
+ 3)); // step=1/300
+ EXPECT_DECIMAL_STREQ(
+ "1", StepUp("0", "1", "0.003921568627450980", "0", 255)); // step=1/255
+ EXPECT_DECIMAL_STREQ("1", StepUp("0", "1", "0.1", "0", 10));
+}
+
+TEST_F(DecimalTest, RealWorldExampleNumberStepUpStepDownFromRendererRounding) {
+ EXPECT_DECIMAL_STREQ("5.015", StepUp("0", "100", "0.005", "5.005", 2));
+ EXPECT_DECIMAL_STREQ("5.06", StepUp("0", "100", "0.005", "5.005", 11));
+ EXPECT_DECIMAL_STREQ("5.065", StepUp("0", "100", "0.005", "5.005", 12));
+
+ EXPECT_DECIMAL_STREQ("5.015", StepUp("4", "9", "0.005", "5.005", 2));
+ EXPECT_DECIMAL_STREQ("5.06", StepUp("4", "9", "0.005", "5.005", 11));
+ EXPECT_DECIMAL_STREQ("5.065", StepUp("4", "9", "0.005", "5.005", 12));
+}
+
+TEST_F(DecimalTest, RealWorldExampleRangeStepUpStepDown) {
+ EXPECT_DECIMAL_STREQ("1e+38", StepUp("0", "1E38", "1", "1E38", 9));
+ EXPECT_DECIMAL_STREQ("1e+38", StepDown("0", "1E38", "1", "1E38", 9));
+}
+
+TEST_F(DecimalTest, Remainder) {
+ EXPECT_EQ(Encode(21, -1, kPositive), Encode(21, -1, kPositive).Remainder(3));
+ EXPECT_EQ(Decimal(1), Decimal(10).Remainder(3));
+ EXPECT_EQ(Decimal(1), Decimal(10).Remainder(-3));
+ EXPECT_EQ(Encode(1, 0, kNegative), Decimal(-10).Remainder(3));
+ EXPECT_EQ(Decimal(-1), Decimal(-10).Remainder(-3));
+ EXPECT_EQ(Encode(2, -1, kPositive), Encode(102, -1, kPositive).Remainder(1));
+ EXPECT_EQ(Encode(1, -1, kPositive),
+ Decimal(10).Remainder(Encode(3, -1, kPositive)));
+ EXPECT_EQ(Decimal(1),
+ Encode(36, -1, kPositive).Remainder(Encode(13, -1, kPositive)));
+ EXPECT_EQ(Encode(1, 86, kPositive),
+ (Encode(1234, 100, kPositive).Remainder(Decimal(3))));
+ EXPECT_EQ(Decimal(500), (Decimal(500).Remainder(1000)));
+ EXPECT_EQ(Decimal(-500), (Decimal(-500).Remainder(1000)));
+}
+
+TEST_F(DecimalTest, RemainderBigExponent) {
+ EXPECT_EQ(Encode(0, 1022, kPositive),
+ Encode(1, 1022, kPositive).Remainder(Encode(1, 0, kPositive)));
+ EXPECT_EQ(Encode(0, 1022, kPositive),
+ Encode(1, 1022, kPositive).Remainder(Encode(1, 1022, kPositive)));
+ EXPECT_EQ(Decimal::Infinity(kPositive),
+ Encode(1, 1022, kPositive).Remainder(Encode(1, -1000, kPositive)));
+}
+
+TEST_F(DecimalTest, RemainderSmallExponent) {
+ EXPECT_EQ(Encode(1, -1022, kPositive),
+ Encode(1, -1022, kPositive).Remainder(Encode(1, 0, kPositive)));
+ EXPECT_EQ(Encode(0, -1022, kPositive),
+ Encode(1, -1022, kPositive).Remainder(Encode(1, -1022, kPositive)));
+}
+
+TEST_F(DecimalTest, RemainderSpecialValues) {
+ EXPECT_EQ(Decimal::Infinity(kPositive),
+ Decimal::Infinity(kPositive).Remainder(1));
+ EXPECT_EQ(Decimal::Infinity(kNegative),
+ Decimal::Infinity(kNegative).Remainder(1));
+ EXPECT_EQ(Decimal::Nan(), Decimal::Nan().Remainder(1));
+
+ EXPECT_EQ(Decimal::Infinity(kNegative),
+ Decimal::Infinity(kPositive).Remainder(-1));
+ EXPECT_EQ(Decimal::Infinity(kPositive),
+ Decimal::Infinity(kNegative).Remainder(-1));
+ EXPECT_EQ(Decimal::Nan(), Decimal::Nan().Remainder(-1));
+
+ EXPECT_EQ(Decimal::Infinity(kPositive),
+ Decimal::Infinity(kPositive).Remainder(3));
+ EXPECT_EQ(Decimal::Infinity(kNegative),
+ Decimal::Infinity(kNegative).Remainder(3));
+ EXPECT_EQ(Decimal::Nan(), Decimal::Nan().Remainder(3));
+
+ EXPECT_EQ(Decimal::Infinity(kNegative),
+ Decimal::Infinity(kPositive).Remainder(-1));
+ EXPECT_EQ(Decimal::Infinity(kPositive),
+ Decimal::Infinity(kNegative).Remainder(-1));
+ EXPECT_EQ(Decimal::Nan(), Decimal::Nan().Remainder(-1));
+
+ EXPECT_EQ(Decimal::Nan(), Decimal(1).Remainder(Decimal::Infinity(kPositive)));
+ EXPECT_EQ(Decimal::Nan(), Decimal(1).Remainder(Decimal::Infinity(kNegative)));
+ EXPECT_EQ(Decimal::Nan(), Decimal(1).Remainder(Decimal::Nan()));
+}
+
+TEST_F(DecimalTest, Round) {
+ EXPECT_EQ(Decimal(1), (Decimal(9) / Decimal(10)).Round());
+ EXPECT_EQ(Decimal(25), (Decimal(5) / FromString("0.200")).Round());
+ EXPECT_EQ(Decimal(3), (Decimal(5) / Decimal(2)).Round());
+ EXPECT_EQ(Decimal(1), (Decimal(2) / Decimal(3)).Round());
+ EXPECT_EQ(Decimal(3), (Decimal(10) / Decimal(3)).Round());
+ EXPECT_EQ(Decimal(3), (Decimal(1) / FromString("0.3")).Round());
+ EXPECT_EQ(Decimal(10), (Decimal(1) / FromString("0.1")).Round());
+ EXPECT_EQ(Decimal(5), (Decimal(1) / FromString("0.2")).Round());
+ EXPECT_EQ(Decimal(10), (FromString("10.2") / 1).Round());
+ EXPECT_EQ(Encode(1234, 100, kPositive), Encode(1234, 100, kPositive).Round());
+
+ EXPECT_EQ(Decimal(2), Encode(190002, -5, kPositive).Round());
+ EXPECT_EQ(Decimal(2), Encode(150002, -5, kPositive).Round());
+ EXPECT_EQ(Decimal(2), Encode(150000, -5, kPositive).Round());
+ EXPECT_EQ(Decimal(12), Encode(12492, -3, kPositive).Round());
+ EXPECT_EQ(Decimal(13), Encode(12502, -3, kPositive).Round());
+
+ EXPECT_EQ(Decimal(-2), Encode(190002, -5, kNegative).Round());
+ EXPECT_EQ(Decimal(-2), Encode(150002, -5, kNegative).Round());
+ EXPECT_EQ(Decimal(-2), Encode(150000, -5, kNegative).Round());
+ EXPECT_EQ(Decimal(-12), Encode(12492, -3, kNegative).Round());
+ EXPECT_EQ(Decimal(-13), Encode(12502, -3, kNegative).Round());
+}
+
+TEST_F(DecimalTest, RoundSpecialValues) {
+ EXPECT_EQ(Decimal::Infinity(kPositive), Decimal::Infinity(kPositive).Round());
+ EXPECT_EQ(Decimal::Infinity(kNegative), Decimal::Infinity(kNegative).Round());
+ EXPECT_EQ(Decimal::Nan(), Decimal::Nan().Round());
+}
+
+TEST_F(DecimalTest, Subtract) {
+ EXPECT_EQ(Encode(0, 0, kPositive), Decimal(0) - Decimal(0));
+ EXPECT_EQ(Encode(3, 0, kPositive), Decimal(2) - Decimal(-1));
+ EXPECT_EQ(Encode(3, 0, kNegative), Decimal(-1) - Decimal(2));
+ EXPECT_EQ(Encode(98, 0, kPositive), Decimal(99) - Decimal(1));
+ EXPECT_EQ(Encode(0, 0, kPositive), Decimal(-50) - Decimal(-50));
+ EXPECT_EQ(Encode(UINT64_C(1000000000000000), 35, kPositive),
+ Encode(1, 50, kPositive) - Decimal(1));
+ EXPECT_EQ(Encode(UINT64_C(1000000000000000), 35, kNegative),
+ Decimal(1) - Encode(1, 50, kPositive));
+}
+
+TEST_F(DecimalTest, SubtractBigExponent) {
+ EXPECT_EQ(Encode(1, 1022, kPositive),
+ Encode(1, 1022, kPositive) - Encode(1, 0, kPositive));
+ EXPECT_EQ(Encode(0, 0, kPositive),
+ Encode(1, 1022, kPositive) - Encode(1, 1022, kPositive));
+ EXPECT_EQ(Encode(1, 1022, kPositive),
+ Encode(1, 1022, kPositive) + Encode(1, -1000, kPositive));
+}
+
+TEST_F(DecimalTest, SubtractSmallExponent) {
+ EXPECT_EQ(Encode(UINT64_C(10000000000000000), -16, kNegative),
+ Encode(1, -1022, kPositive) - Encode(1, 0, kPositive));
+ EXPECT_EQ(Encode(0, 0, kPositive),
+ Encode(1, -1022, kPositive) - Encode(1, -1022, kPositive));
+}
+
+TEST_F(DecimalTest, SubtractSpecialValues) {
+ const Decimal infinity(Decimal::Infinity(kPositive));
+ const Decimal minus_infinity(Decimal::Infinity(kNegative));
+ const Decimal na_n(Decimal::Nan());
+ const Decimal ten(10);
+
+ EXPECT_EQ(na_n, infinity - infinity);
+ EXPECT_EQ(infinity, infinity - minus_infinity);
+ EXPECT_EQ(minus_infinity, minus_infinity - infinity);
+ EXPECT_EQ(na_n, minus_infinity - minus_infinity);
+
+ EXPECT_EQ(infinity, infinity - ten);
+ EXPECT_EQ(minus_infinity, ten - infinity);
+ EXPECT_EQ(minus_infinity, minus_infinity - ten);
+ EXPECT_EQ(infinity, ten - minus_infinity);
+
+ EXPECT_EQ(na_n, na_n - na_n);
+ EXPECT_EQ(na_n, na_n - ten);
+ EXPECT_EQ(na_n, ten - na_n);
+
+ EXPECT_EQ(na_n, na_n - infinity);
+ EXPECT_EQ(na_n, na_n - minus_infinity);
+ EXPECT_EQ(na_n, infinity - na_n);
+ EXPECT_EQ(na_n, minus_infinity - na_n);
+}
+
+TEST_F(DecimalTest, ToDouble) {
+ EXPECT_EQ(0.0, Encode(0, 0, kPositive).ToDouble());
+ EXPECT_EQ(-0.0, Encode(0, 0, kNegative).ToDouble());
+
+ EXPECT_EQ(1.0, Encode(1, 0, kPositive).ToDouble());
+ EXPECT_EQ(-1.0, Encode(1, 0, kNegative).ToDouble());
+
+ EXPECT_EQ(0.1, Encode(1, -1, kPositive).ToDouble());
+ EXPECT_EQ(-0.1, Encode(1, -1, kNegative).ToDouble());
+ EXPECT_EQ(0.3, Encode(3, -1, kPositive).ToDouble());
+ EXPECT_EQ(-0.3, Encode(3, -1, kNegative).ToDouble());
+ EXPECT_EQ(0.6, Encode(6, -1, kPositive).ToDouble());
+ EXPECT_EQ(-0.6, Encode(6, -1, kNegative).ToDouble());
+ EXPECT_EQ(0.7, Encode(7, -1, kPositive).ToDouble());
+ EXPECT_EQ(-0.7, Encode(7, -1, kNegative).ToDouble());
+
+ EXPECT_EQ(0.01, Encode(1, -2, kPositive).ToDouble());
+ EXPECT_EQ(0.001, Encode(1, -3, kPositive).ToDouble());
+ EXPECT_EQ(0.0001, Encode(1, -4, kPositive).ToDouble());
+ EXPECT_EQ(0.00001, Encode(1, -5, kPositive).ToDouble());
+
+ EXPECT_EQ(1e+308, Encode(1, 308, kPositive).ToDouble());
+ EXPECT_EQ(1e-307, Encode(1, -307, kPositive).ToDouble());
+
+ EXPECT_TRUE(std::isinf(Encode(1, 1000, kPositive).ToDouble()));
+ EXPECT_EQ(0.0, Encode(1, -1000, kPositive).ToDouble());
+}
+
+TEST_F(DecimalTest, ToDoubleSpecialValues) {
+ EXPECT_TRUE(std::isinf(Decimal::Infinity(Decimal::kPositive).ToDouble()));
+ EXPECT_TRUE(std::isinf(Decimal::Infinity(Decimal::kNegative).ToDouble()));
+ EXPECT_TRUE(std::isnan(Decimal::Nan().ToDouble()));
+}
+
+TEST_F(DecimalTest, ToString) {
+ EXPECT_DECIMAL_STREQ("0", Decimal::Zero(kPositive));
+ EXPECT_DECIMAL_STREQ("-0", Decimal::Zero(kNegative));
+ EXPECT_DECIMAL_STREQ("1", Decimal(1));
+ EXPECT_DECIMAL_STREQ("-1", Decimal(-1));
+ EXPECT_DECIMAL_STREQ("1234567", Decimal(1234567));
+ EXPECT_DECIMAL_STREQ("-1234567", Decimal(-1234567));
+ EXPECT_DECIMAL_STREQ("0.5", Encode(5, -1, kPositive));
+ EXPECT_DECIMAL_STREQ("-0.5", Encode(5, -1, kNegative));
+ EXPECT_DECIMAL_STREQ("12.345", Encode(12345, -3, kPositive));
+ EXPECT_DECIMAL_STREQ("-12.345", Encode(12345, -3, kNegative));
+ EXPECT_DECIMAL_STREQ("0.12345", Encode(12345, -5, kPositive));
+ EXPECT_DECIMAL_STREQ("-0.12345", Encode(12345, -5, kNegative));
+ EXPECT_DECIMAL_STREQ("50", Encode(50, 0, kPositive));
+ EXPECT_DECIMAL_STREQ("-50", Encode(50, 0, kNegative));
+ EXPECT_DECIMAL_STREQ("5e+1", Encode(5, 1, kPositive));
+ EXPECT_DECIMAL_STREQ("-5e+1", Encode(5, 1, kNegative));
+ EXPECT_DECIMAL_STREQ("5.678e+103", Encode(5678, 100, kPositive));
+ EXPECT_DECIMAL_STREQ("-5.678e+103", Encode(5678, 100, kNegative));
+ EXPECT_DECIMAL_STREQ("5.678e-97", Encode(5678, -100, kPositive));
+ EXPECT_DECIMAL_STREQ("-5.678e-97", Encode(5678, -100, kNegative));
+ EXPECT_DECIMAL_STREQ("8639999913600001",
+ Encode(UINT64_C(8639999913600001), 0, kPositive));
+ EXPECT_DECIMAL_STREQ(
+ "9007199254740991",
+ Encode((static_cast<uint64_t>(1) << DBL_MANT_DIG) - 1, 0, kPositive));
+ EXPECT_DECIMAL_STREQ("99999999999999999",
+ Encode(UINT64_C(99999999999999999), 0, kPositive));
+ EXPECT_DECIMAL_STREQ("9.9999999999999999e+17",
+ Encode(UINT64_C(99999999999999999), 1, kPositive));
+ EXPECT_DECIMAL_STREQ("9.9999999999999999e+18",
+ Encode(UINT64_C(99999999999999999), 2, kPositive));
+ EXPECT_DECIMAL_STREQ("1e+16",
+ Encode(UINT64_C(99999999999999999), -1, kPositive));
+ EXPECT_DECIMAL_STREQ("1000000000000000",
+ Encode(UINT64_C(99999999999999999), -2, kPositive));
+ EXPECT_DECIMAL_STREQ("1",
+ Encode(UINT64_C(99999999999999999), -17, kPositive));
+ EXPECT_DECIMAL_STREQ("0.001",
+ Encode(UINT64_C(99999999999999999), -20, kPositive));
+ EXPECT_DECIMAL_STREQ("1e-83",
+ Encode(UINT64_C(99999999999999999), -100, kPositive));
+}
+
+TEST_F(DecimalTest, ToStringSpecialValues) {
+ EXPECT_DECIMAL_STREQ("Infinity", Decimal::Infinity(kPositive));
+ EXPECT_DECIMAL_STREQ("-Infinity", Decimal::Infinity(kNegative));
+ EXPECT_DECIMAL_STREQ("NaN", Decimal::Nan());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/drag_image.cc b/chromium/third_party/blink/renderer/platform/drag_image.cc
new file mode 100644
index 00000000000..fb724c6a7b1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/drag_image.cc
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2007 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/drag_image.h"
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "base/memory/scoped_refptr.h"
+#include "skia/ext/image_operations.h"
+#include "third_party/blink/renderer/platform/fonts/font.h"
+#include "third_party/blink/renderer/platform/fonts/font_cache.h"
+#include "third_party/blink/renderer/platform/fonts/font_description.h"
+#include "third_party/blink/renderer/platform/fonts/font_metrics.h"
+#include "third_party/blink/renderer/platform/geometry/float_point.h"
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/geometry/int_point.h"
+#include "third_party/blink/renderer/platform/graphics/bitmap_image.h"
+#include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h"
+#include "third_party/blink/renderer/platform/graphics/color.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
+#include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/text/bidi_text_run.h"
+#include "third_party/blink/renderer/platform/text/string_truncator.h"
+#include "third_party/blink/renderer/platform/text/text_run.h"
+#include "third_party/blink/renderer/platform/transforms/affine_transform.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkColorSpaceXformCanvas.h"
+#include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/core/SkMatrix.h"
+#include "third_party/skia/include/core/SkSurface.h"
+
+namespace blink {
+
+namespace {
+
+const float kDragLabelBorderX = 4;
+// Keep border_y in synch with DragController::LinkDragBorderInset.
+const float kDragLabelBorderY = 2;
+const float kLabelBorderYOffset = 2;
+
+const float kMaxDragLabelWidth = 300;
+const float kMaxDragLabelStringWidth =
+ (kMaxDragLabelWidth - 2 * kDragLabelBorderX);
+
+const float kDragLinkLabelFontSize = 11;
+const float kDragLinkUrlFontSize = 10;
+
+} // anonymous namespace
+
+PaintImage DragImage::ResizeAndOrientImage(
+ const PaintImage& image,
+ ImageOrientation orientation,
+ FloatSize image_scale,
+ float opacity,
+ InterpolationQuality interpolation_quality) {
+ IntSize size(image.width(), image.height());
+ size.Scale(image_scale.Width(), image_scale.Height());
+ AffineTransform transform;
+ if (orientation != kDefaultImageOrientation) {
+ if (orientation.UsesWidthAsHeight())
+ size = size.TransposedSize();
+ transform *= orientation.TransformFromDefault(FloatSize(size));
+ }
+ transform.ScaleNonUniform(image_scale.Width(), image_scale.Height());
+
+ if (size.IsEmpty())
+ return PaintImage();
+
+ if (transform.IsIdentity() && opacity == 1) {
+ // Nothing to adjust, just use the original.
+ DCHECK_EQ(image.width(), size.Width());
+ DCHECK_EQ(image.height(), size.Height());
+ return image;
+ }
+
+ sk_sp<SkSurface> surface =
+ SkSurface::MakeRasterN32Premul(size.Width(), size.Height());
+ if (!surface)
+ return PaintImage();
+
+ SkPaint paint;
+ DCHECK_GE(opacity, 0);
+ DCHECK_LE(opacity, 1);
+ paint.setAlpha(opacity * 255);
+ paint.setFilterQuality(interpolation_quality == kInterpolationNone
+ ? kNone_SkFilterQuality
+ : kHigh_SkFilterQuality);
+
+ SkCanvas* canvas = surface->getCanvas();
+ std::unique_ptr<SkCanvas> color_transform_canvas;
+ color_transform_canvas =
+ SkCreateColorSpaceXformCanvas(canvas, SkColorSpace::MakeSRGB());
+ canvas = color_transform_canvas.get();
+ canvas->concat(AffineTransformToSkMatrix(transform));
+ canvas->drawImage(image.GetSkImage(), 0, 0, &paint);
+
+ return PaintImageBuilder::WithProperties(std::move(image))
+ .set_image(surface->makeImageSnapshot(), PaintImage::GetNextContentId())
+ .TakePaintImage();
+}
+
+FloatSize DragImage::ClampedImageScale(const IntSize& image_size,
+ const IntSize& size,
+ const IntSize& max_size) {
+ // Non-uniform scaling for size mapping.
+ FloatSize image_scale(
+ static_cast<float>(size.Width()) / image_size.Width(),
+ static_cast<float>(size.Height()) / image_size.Height());
+
+ // Uniform scaling for clamping.
+ const float clamp_scale_x =
+ size.Width() > max_size.Width()
+ ? static_cast<float>(max_size.Width()) / size.Width()
+ : 1;
+ const float clamp_scale_y =
+ size.Height() > max_size.Height()
+ ? static_cast<float>(max_size.Height()) / size.Height()
+ : 1;
+ image_scale.Scale(std::min(clamp_scale_x, clamp_scale_y));
+
+ return image_scale;
+}
+
+std::unique_ptr<DragImage> DragImage::Create(
+ Image* image,
+ RespectImageOrientationEnum should_respect_image_orientation,
+ float device_scale_factor,
+ InterpolationQuality interpolation_quality,
+ float opacity,
+ FloatSize image_scale) {
+ if (!image)
+ return nullptr;
+
+ PaintImage paint_image = image->PaintImageForCurrentFrame();
+ if (!paint_image)
+ return nullptr;
+
+ ImageOrientation orientation;
+ if (should_respect_image_orientation == kRespectImageOrientation &&
+ image->IsBitmapImage())
+ orientation = ToBitmapImage(image)->CurrentFrameOrientation();
+
+ SkBitmap bm;
+ paint_image = ResizeAndOrientImage(paint_image, orientation, image_scale,
+ opacity, interpolation_quality);
+ if (!paint_image || !paint_image.GetSkImage()->asLegacyBitmap(&bm))
+ return nullptr;
+
+ return base::WrapUnique(
+ new DragImage(bm, device_scale_factor, interpolation_quality));
+}
+
+static Font DeriveDragLabelFont(int size,
+ FontSelectionValue font_weight,
+ const FontDescription& system_font) {
+ FontDescription description = system_font;
+ description.SetWeight(font_weight);
+ description.SetSpecifiedSize(size);
+ description.SetComputedSize(size);
+ Font result(description);
+ result.Update(nullptr);
+ return result;
+}
+
+std::unique_ptr<DragImage> DragImage::Create(const KURL& url,
+ const String& in_label,
+ const FontDescription& system_font,
+ float device_scale_factor) {
+ const Font label_font = DeriveDragLabelFont(kDragLinkLabelFontSize,
+ BoldWeightValue(), system_font);
+ const SimpleFontData* label_font_data = label_font.PrimaryFont();
+ DCHECK(label_font_data);
+ const Font url_font = DeriveDragLabelFont(kDragLinkUrlFontSize,
+ NormalWeightValue(), system_font);
+ const SimpleFontData* url_font_data = url_font.PrimaryFont();
+ DCHECK(url_font_data);
+
+ if (!label_font_data || !url_font_data)
+ return nullptr;
+
+ FontCachePurgePreventer font_cache_purge_preventer;
+
+ bool draw_url_string = true;
+ bool clip_url_string = false;
+ bool clip_label_string = false;
+ float max_drag_label_string_width_dip =
+ kMaxDragLabelStringWidth / device_scale_factor;
+
+ String url_string = url.GetString();
+ String label = in_label.StripWhiteSpace();
+ if (label.IsEmpty()) {
+ draw_url_string = false;
+ label = url_string;
+ }
+
+ // First step is drawing the link drag image width.
+ TextRun label_run(label.Impl());
+ TextRun url_run(url_string.Impl());
+ IntSize label_size(label_font.Width(label_run),
+ label_font_data->GetFontMetrics().Ascent() +
+ label_font_data->GetFontMetrics().Descent());
+
+ if (label_size.Width() > max_drag_label_string_width_dip) {
+ label_size.SetWidth(max_drag_label_string_width_dip);
+ clip_label_string = true;
+ }
+
+ IntSize url_string_size;
+ IntSize image_size(label_size.Width() + kDragLabelBorderX * 2,
+ label_size.Height() + kDragLabelBorderY * 2);
+
+ if (draw_url_string) {
+ url_string_size.SetWidth(url_font.Width(url_run));
+ url_string_size.SetHeight(url_font_data->GetFontMetrics().Ascent() +
+ url_font_data->GetFontMetrics().Descent());
+ image_size.SetHeight(image_size.Height() + url_string_size.Height());
+ if (url_string_size.Width() > max_drag_label_string_width_dip) {
+ image_size.SetWidth(max_drag_label_string_width_dip);
+ clip_url_string = true;
+ } else {
+ image_size.SetWidth(
+ std::max(label_size.Width(), url_string_size.Width()) +
+ kDragLabelBorderX * 2);
+ }
+ }
+
+ // We now know how big the image needs to be, so we create and
+ // fill the background
+ IntSize scaled_image_size = image_size;
+ scaled_image_size.Scale(device_scale_factor);
+ std::unique_ptr<CanvasResourceProvider> resource_provider(
+ CanvasResourceProvider::Create(
+ scaled_image_size, CanvasResourceProvider::kSoftwareResourceUsage));
+ if (!resource_provider)
+ return nullptr;
+
+ resource_provider->Canvas()->scale(device_scale_factor, device_scale_factor);
+
+ const float kDragLabelRadius = 5;
+
+ IntRect rect(IntPoint(), image_size);
+ PaintFlags background_paint;
+ background_paint.setColor(SkColorSetRGB(140, 140, 140));
+ background_paint.setAntiAlias(true);
+ SkRRect rrect;
+ rrect.setRectXY(SkRect::MakeWH(image_size.Width(), image_size.Height()),
+ kDragLabelRadius, kDragLabelRadius);
+ resource_provider->Canvas()->drawRRect(rrect, background_paint);
+
+ // Draw the text
+ PaintFlags text_paint;
+ if (draw_url_string) {
+ if (clip_url_string)
+ url_string = StringTruncator::CenterTruncate(
+ url_string, image_size.Width() - (kDragLabelBorderX * 2.0f),
+ url_font);
+ IntPoint text_pos(
+ kDragLabelBorderX,
+ image_size.Height() -
+ (kLabelBorderYOffset + url_font_data->GetFontMetrics().Descent()));
+ TextRun text_run(url_string);
+ url_font.DrawText(resource_provider->Canvas(), TextRunPaintInfo(text_run),
+ text_pos, device_scale_factor, text_paint);
+ }
+
+ if (clip_label_string)
+ label = StringTruncator::RightTruncate(
+ label, image_size.Width() - (kDragLabelBorderX * 2.0f), label_font);
+
+ bool has_strong_directionality;
+ TextRun text_run =
+ TextRunWithDirectionality(label, &has_strong_directionality);
+ IntPoint text_pos(
+ kDragLabelBorderX,
+ kDragLabelBorderY + label_font.GetFontDescription().ComputedPixelSize());
+ if (has_strong_directionality &&
+ text_run.Direction() == TextDirection::kRtl) {
+ float text_width = label_font.Width(text_run);
+ int available_width = image_size.Width() - kDragLabelBorderX * 2;
+ text_pos.SetX(available_width - ceilf(text_width));
+ }
+ label_font.DrawBidiText(resource_provider->Canvas(),
+ TextRunPaintInfo(text_run), FloatPoint(text_pos),
+ Font::kDoNotPaintIfFontNotReady, device_scale_factor,
+ text_paint);
+
+ scoped_refptr<StaticBitmapImage> image = resource_provider->Snapshot();
+ return DragImage::Create(image.get(), kDoNotRespectImageOrientation,
+ device_scale_factor);
+}
+
+DragImage::DragImage(const SkBitmap& bitmap,
+ float resolution_scale,
+ InterpolationQuality interpolation_quality)
+ : bitmap_(bitmap),
+ resolution_scale_(resolution_scale),
+ interpolation_quality_(interpolation_quality) {}
+
+DragImage::~DragImage() = default;
+
+void DragImage::Scale(float scale_x, float scale_y) {
+ skia::ImageOperations::ResizeMethod resize_method =
+ interpolation_quality_ == kInterpolationNone
+ ? skia::ImageOperations::RESIZE_BOX
+ : skia::ImageOperations::RESIZE_LANCZOS3;
+ int image_width = scale_x * bitmap_.width();
+ int image_height = scale_y * bitmap_.height();
+ bitmap_ = skia::ImageOperations::Resize(bitmap_, resize_method, image_width,
+ image_height);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/drag_image.h b/chromium/third_party/blink/renderer/platform/drag_image.h
new file mode 100644
index 00000000000..c57ea8fec16
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/drag_image.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2007 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_DRAG_IMAGE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_DRAG_IMAGE_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "third_party/blink/renderer/platform/geometry/float_size.h"
+#include "third_party/blink/renderer/platform/geometry/int_size.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_types.h"
+#include "third_party/blink/renderer/platform/graphics/image_orientation.h"
+#include "third_party/blink/renderer/platform/graphics/paint/display_item_client.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_image.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+namespace blink {
+
+class FontDescription;
+class Image;
+class KURL;
+
+class PLATFORM_EXPORT DragImage {
+ USING_FAST_MALLOC(DragImage);
+
+ public:
+ static std::unique_ptr<DragImage> Create(
+ Image*,
+ RespectImageOrientationEnum = kDoNotRespectImageOrientation,
+ float device_scale_factor = 1,
+ InterpolationQuality = kInterpolationDefault,
+ float opacity = 1,
+ FloatSize image_scale = FloatSize(1, 1));
+
+ static std::unique_ptr<DragImage> Create(const KURL&,
+ const String& label,
+ const FontDescription& system_font,
+ float device_scale_factor);
+ ~DragImage();
+
+ static FloatSize ClampedImageScale(const IntSize&,
+ const IntSize&,
+ const IntSize& max_size);
+
+ const SkBitmap& Bitmap() { return bitmap_; }
+ float ResolutionScale() const { return resolution_scale_; }
+ IntSize Size() const { return IntSize(bitmap_.width(), bitmap_.height()); }
+
+ void Scale(float scale_x, float scale_y);
+
+ static PaintImage ResizeAndOrientImage(
+ const PaintImage&,
+ ImageOrientation,
+ FloatSize image_scale = FloatSize(1, 1),
+ float opacity = 1.0,
+ InterpolationQuality = kInterpolationNone);
+
+ private:
+ DragImage(const SkBitmap&, float resolution_scale, InterpolationQuality);
+
+ SkBitmap bitmap_;
+ float resolution_scale_;
+ InterpolationQuality interpolation_quality_;
+
+ DISALLOW_COPY_AND_ASSIGN(DragImage);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_DRAG_IMAGE_H_
diff --git a/chromium/third_party/blink/renderer/platform/drag_image_test.cc b/chromium/third_party/blink/renderer/platform/drag_image_test.cc
new file mode 100644
index 00000000000..9bbf3a09151
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/drag_image_test.cc
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2010 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 "third_party/blink/renderer/platform/drag_image.h"
+
+#include <memory>
+#include "base/memory/scoped_refptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/fonts/font_description.h"
+#include "third_party/blink/renderer/platform/geometry/int_size.h"
+#include "third_party/blink/renderer/platform/graphics/bitmap_image.h"
+#include "third_party/blink/renderer/platform/graphics/image.h"
+#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/core/SkPixelRef.h"
+#include "third_party/skia/include/core/SkSurface.h"
+
+namespace blink {
+
+class TestImage : public Image {
+ public:
+ static scoped_refptr<TestImage> Create(sk_sp<SkImage> image) {
+ return base::AdoptRef(new TestImage(image));
+ }
+
+ static scoped_refptr<TestImage> Create(const IntSize& size) {
+ return base::AdoptRef(new TestImage(size));
+ }
+
+ IntSize Size() const override {
+ DCHECK(image_);
+
+ return IntSize(image_->width(), image_->height());
+ }
+
+ bool CurrentFrameKnownToBeOpaque() override { return false; }
+
+ void DestroyDecodedData() override {
+ // Image pure virtual stub.
+ }
+
+ void Draw(PaintCanvas*,
+ const PaintFlags&,
+ const FloatRect&,
+ const FloatRect&,
+ RespectImageOrientationEnum,
+ ImageClampingMode,
+ ImageDecodingMode) override {
+ // Image pure virtual stub.
+ }
+
+ PaintImage PaintImageForCurrentFrame() override {
+ return CreatePaintImageBuilder()
+ .set_image(image_, cc::PaintImage::GetNextContentId())
+ .TakePaintImage();
+ }
+
+ private:
+ explicit TestImage(sk_sp<SkImage> image) : image_(image) {}
+
+ explicit TestImage(IntSize size) : image_(nullptr) {
+ sk_sp<SkSurface> surface = CreateSkSurface(size);
+ if (!surface)
+ return;
+
+ surface->getCanvas()->clear(SK_ColorTRANSPARENT);
+ image_ = surface->makeImageSnapshot();
+ }
+
+ static sk_sp<SkSurface> CreateSkSurface(IntSize size) {
+ return SkSurface::MakeRaster(
+ SkImageInfo::MakeN32(size.Width(), size.Height(), kPremul_SkAlphaType));
+ }
+
+ sk_sp<SkImage> image_;
+};
+
+TEST(DragImageTest, NullHandling) {
+ EXPECT_FALSE(DragImage::Create(nullptr));
+
+ scoped_refptr<TestImage> null_test_image(TestImage::Create(IntSize()));
+ EXPECT_FALSE(DragImage::Create(null_test_image.get()));
+}
+
+TEST(DragImageTest, NonNullHandling) {
+ scoped_refptr<TestImage> test_image(TestImage::Create(IntSize(2, 2)));
+ std::unique_ptr<DragImage> drag_image = DragImage::Create(test_image.get());
+ ASSERT_TRUE(drag_image);
+
+ drag_image->Scale(0.5, 0.5);
+ IntSize size = drag_image->Size();
+ EXPECT_EQ(1, size.Width());
+ EXPECT_EQ(1, size.Height());
+}
+
+TEST(DragImageTest, CreateDragImage) {
+ // Tests that the DrageImage implementation doesn't choke on null values
+ // of imageForCurrentFrame().
+ // FIXME: how is this test any different from test NullHandling?
+ scoped_refptr<TestImage> test_image(TestImage::Create(IntSize()));
+ EXPECT_FALSE(DragImage::Create(test_image.get()));
+}
+
+TEST(DragImageTest, TrimWhitespace) {
+ KURL url("http://www.example.com/");
+ String test_label = " Example Example Example \n ";
+ String expected_label = "Example Example Example";
+ float device_scale_factor = 1.0f;
+
+ FontDescription font_description;
+ font_description.FirstFamily().SetFamily("Arial");
+ font_description.SetSpecifiedSize(16);
+ font_description.SetIsAbsoluteSize(true);
+ font_description.SetGenericFamily(FontDescription::kNoFamily);
+ font_description.SetWeight(NormalWeightValue());
+ font_description.SetStyle(NormalSlopeValue());
+
+ std::unique_ptr<DragImage> test_image =
+ DragImage::Create(url, test_label, font_description, device_scale_factor);
+ std::unique_ptr<DragImage> expected_image = DragImage::Create(
+ url, expected_label, font_description, device_scale_factor);
+
+ EXPECT_EQ(test_image->Size().Width(), expected_image->Size().Width());
+}
+
+TEST(DragImageTest, InterpolationNone) {
+ SkBitmap expected_bitmap;
+ expected_bitmap.allocN32Pixels(4, 4);
+ expected_bitmap.eraseArea(SkIRect::MakeXYWH(0, 0, 2, 2), 0xFFFFFFFF);
+ expected_bitmap.eraseArea(SkIRect::MakeXYWH(0, 2, 2, 2), 0xFF000000);
+ expected_bitmap.eraseArea(SkIRect::MakeXYWH(2, 0, 2, 2), 0xFF000000);
+ expected_bitmap.eraseArea(SkIRect::MakeXYWH(2, 2, 2, 2), 0xFFFFFFFF);
+
+ SkBitmap test_bitmap;
+ test_bitmap.allocN32Pixels(2, 2);
+ test_bitmap.eraseArea(SkIRect::MakeXYWH(0, 0, 1, 1), 0xFFFFFFFF);
+ test_bitmap.eraseArea(SkIRect::MakeXYWH(0, 1, 1, 1), 0xFF000000);
+ test_bitmap.eraseArea(SkIRect::MakeXYWH(1, 0, 1, 1), 0xFF000000);
+ test_bitmap.eraseArea(SkIRect::MakeXYWH(1, 1, 1, 1), 0xFFFFFFFF);
+
+ scoped_refptr<TestImage> test_image =
+ TestImage::Create(SkImage::MakeFromBitmap(test_bitmap));
+ std::unique_ptr<DragImage> drag_image = DragImage::Create(
+ test_image.get(), kDoNotRespectImageOrientation, 1, kInterpolationNone);
+ ASSERT_TRUE(drag_image);
+ drag_image->Scale(2, 2);
+ const SkBitmap& drag_bitmap = drag_image->Bitmap();
+ for (int x = 0; x < drag_bitmap.width(); ++x)
+ for (int y = 0; y < drag_bitmap.height(); ++y)
+ EXPECT_EQ(expected_bitmap.getColor(x, y), drag_bitmap.getColor(x, y));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/encrypted_media_request.h b/chromium/third_party/blink/renderer/platform/encrypted_media_request.h
new file mode 100644
index 00000000000..92e73fb181e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/encrypted_media_request.h
@@ -0,0 +1,38 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_ENCRYPTED_MEDIA_REQUEST_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_ENCRYPTED_MEDIA_REQUEST_H_
+
+#include "third_party/blink/renderer/platform/heap/handle.h"
+
+namespace blink {
+
+class SecurityOrigin;
+class WebContentDecryptionModuleAccess;
+struct WebMediaKeySystemConfiguration;
+class WebString;
+template <typename T>
+class WebVector;
+
+class EncryptedMediaRequest
+ : public GarbageCollectedFinalized<EncryptedMediaRequest> {
+ public:
+ virtual ~EncryptedMediaRequest() = default;
+
+ virtual WebString KeySystem() const = 0;
+ virtual const WebVector<WebMediaKeySystemConfiguration>&
+ SupportedConfigurations() const = 0;
+
+ virtual const SecurityOrigin* GetSecurityOrigin() const = 0;
+
+ virtual void RequestSucceeded(WebContentDecryptionModuleAccess*) = 0;
+ virtual void RequestNotSupported(const WebString& error_message) = 0;
+
+ virtual void Trace(blink::Visitor* visitor) {}
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_ENCRYPTED_MEDIA_REQUEST_H_
diff --git a/chromium/third_party/blink/renderer/platform/event_dispatch_forbidden_scope.cc b/chromium/third_party/blink/renderer/platform/event_dispatch_forbidden_scope.cc
new file mode 100644
index 00000000000..9dfab394c7a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/event_dispatch_forbidden_scope.cc
@@ -0,0 +1,13 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/event_dispatch_forbidden_scope.h"
+
+namespace blink {
+
+#if DCHECK_IS_ON()
+unsigned EventDispatchForbiddenScope::count_ = 0;
+#endif // DECHECK_IS_ON()
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/event_dispatch_forbidden_scope.h b/chromium/third_party/blink/renderer/platform/event_dispatch_forbidden_scope.h
new file mode 100644
index 00000000000..7fb24f2ef23
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/event_dispatch_forbidden_scope.h
@@ -0,0 +1,74 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_EVENT_DISPATCH_FORBIDDEN_SCOPE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_EVENT_DISPATCH_FORBIDDEN_SCOPE_H_
+
+#include "base/macros.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/auto_reset.h"
+
+namespace blink {
+
+#if DCHECK_IS_ON()
+
+class EventDispatchForbiddenScope {
+ STACK_ALLOCATED();
+ DISALLOW_COPY_AND_ASSIGN(EventDispatchForbiddenScope);
+
+ public:
+ EventDispatchForbiddenScope() {
+ DCHECK(IsMainThread());
+ ++count_;
+ }
+
+ ~EventDispatchForbiddenScope() {
+ DCHECK(IsMainThread());
+ DCHECK(count_);
+ --count_;
+ }
+
+ static bool IsEventDispatchForbidden() {
+ if (!IsMainThread())
+ return false;
+ return count_;
+ }
+
+ class AllowUserAgentEvents {
+ STACK_ALLOCATED();
+
+ public:
+ AllowUserAgentEvents() : change_(&count_, 0) { DCHECK(IsMainThread()); }
+
+ ~AllowUserAgentEvents() { DCHECK(!count_); }
+
+ AutoReset<unsigned> change_;
+ };
+
+ private:
+ PLATFORM_EXPORT static unsigned count_;
+};
+
+#else
+
+class EventDispatchForbiddenScope {
+ STACK_ALLOCATED();
+ DISALLOW_COPY_AND_ASSIGN(EventDispatchForbiddenScope);
+
+ public:
+ EventDispatchForbiddenScope() {}
+
+ class AllowUserAgentEvents {
+ public:
+ AllowUserAgentEvents() {}
+ };
+};
+
+#endif // DCHECK_IS_ON()
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_EVENT_DISPATCH_FORBIDDEN_SCOPE_H_
diff --git a/chromium/third_party/blink/renderer/platform/exported/file_path_conversion.cc b/chromium/third_party/blink/renderer/platform/exported/file_path_conversion.cc
new file mode 100644
index 00000000000..ad589bdfad6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/file_path_conversion.cc
@@ -0,0 +1,46 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/file_path_conversion.h"
+
+#include "base/files/file_path.h"
+#include "build/build_config.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+base::FilePath WebStringToFilePath(const WebString& web_string) {
+ if (web_string.IsEmpty())
+ return base::FilePath();
+
+ String str = web_string;
+ if (!str.Is8Bit()) {
+ return base::FilePath::FromUTF16Unsafe(
+ base::StringPiece16(str.Characters16(), str.length()));
+ }
+
+#if defined(OS_POSIX)
+ StringUTF8Adaptor utf8(str);
+ return base::FilePath::FromUTF8Unsafe(utf8.AsStringPiece());
+#else
+ const LChar* data8 = str.Characters8();
+ return base::FilePath::FromUTF16Unsafe(
+ base::string16(data8, data8 + str.length()));
+#endif
+}
+
+WebString FilePathToWebString(const base::FilePath& path) {
+ if (path.empty())
+ return WebString();
+
+#if defined(OS_POSIX)
+ return WebString::FromUTF8(path.value());
+#else
+ return WebString::FromUTF16(path.AsUTF16Unsafe());
+#endif
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/file_path_conversion_test.cc b/chromium/third_party/blink/renderer/platform/exported/file_path_conversion_test.cc
new file mode 100644
index 00000000000..151c73ab40f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/file_path_conversion_test.cc
@@ -0,0 +1,54 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/file_path_conversion.h"
+
+#include "base/files/file_path.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+TEST(FilePathConversionTest, convert) {
+ String test8bit_string("path");
+ String test8bit_latin1("a\xC4");
+
+ static const UChar kTest[5] = {0x0070, 0x0061, 0x0074, 0x0068, 0}; // path
+ static const UChar kTestLatin1[3] = {0x0061, 0x00C4, 0}; // a\xC4
+ static const UChar kTestUTF16[3] = {0x6587, 0x5B57, 0}; // \u6587 \u5B57
+ String test16bit_string(kTest);
+ String test16bit_latin1(kTestLatin1);
+ String test16bit_utf16(kTestUTF16);
+
+ // Latin1 a\xC4 == UTF8 a\xC3\x84
+ base::FilePath path_latin1 = base::FilePath::FromUTF8Unsafe("a\xC3\x84");
+ // UTF16 \u6587\u5B57 == \xE6\x96\x87\xE5\xAD\x97
+ base::FilePath path_utf16 =
+ base::FilePath::FromUTF8Unsafe("\xE6\x96\x87\xE5\xAD\x97");
+
+ EXPECT_TRUE(test8bit_string.Is8Bit());
+ EXPECT_TRUE(test8bit_latin1.Is8Bit());
+ EXPECT_FALSE(test16bit_string.Is8Bit());
+ EXPECT_FALSE(test16bit_latin1.Is8Bit());
+
+ EXPECT_EQ(FILE_PATH_LITERAL("path"),
+ WebStringToFilePath(test8bit_string).value());
+ EXPECT_EQ(path_latin1.value(), WebStringToFilePath(test8bit_latin1).value());
+ EXPECT_EQ(FILE_PATH_LITERAL("path"),
+ WebStringToFilePath(test16bit_string).value());
+ EXPECT_EQ(path_latin1.value(), WebStringToFilePath(test16bit_latin1).value());
+ EXPECT_EQ(path_utf16.value(), WebStringToFilePath(test16bit_utf16).value());
+
+ EXPECT_STREQ("path",
+ FilePathToWebString(base::FilePath(FILE_PATH_LITERAL("path")))
+ .Utf8()
+ .data());
+ EXPECT_STREQ(test8bit_latin1.Utf8().data(),
+ FilePathToWebString(path_latin1).Utf8().data());
+ EXPECT_STREQ(test16bit_utf16.Utf8().data(),
+ FilePathToWebString(path_utf16).Utf8().data());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/interface_registry.cc b/chromium/third_party/blink/renderer/platform/exported/interface_registry.cc
new file mode 100644
index 00000000000..002c2a68886
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/interface_registry.cc
@@ -0,0 +1,29 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/interface_registry.h"
+
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+
+namespace blink {
+namespace {
+
+class EmptyInterfaceRegistry : public InterfaceRegistry {
+ void AddInterface(
+ const char* name,
+ const InterfaceFactory& factory,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner) override {}
+ void AddAssociatedInterface(
+ const char* name,
+ const AssociatedInterfaceFactory& factory) override {}
+};
+
+} // namespace
+
+InterfaceRegistry* InterfaceRegistry::GetEmptyInterfaceRegistry() {
+ DEFINE_STATIC_LOCAL(EmptyInterfaceRegistry, empty_interface_registry, ());
+ return &empty_interface_registry;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/platform.cc b/chromium/third_party/blink/renderer/platform/exported/platform.cc
new file mode 100644
index 00000000000..b15478a499f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/platform.cc
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2012 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 "third_party/blink/public/platform/platform.h"
+
+#include <memory>
+
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/trace_event/memory_dump_manager.h"
+#include "services/service_manager/public/cpp/connector.h"
+#include "services/service_manager/public/cpp/interface_provider.h"
+#include "third_party/blink/public/common/origin_trials/trial_policy.h"
+#include "third_party/blink/public/platform/interface_provider.h"
+#include "third_party/blink/public/platform/modules/serviceworker/web_service_worker_cache_storage.h"
+#include "third_party/blink/public/platform/modules/webmidi/web_midi_accessor.h"
+#include "third_party/blink/public/platform/web_canvas_capture_handler.h"
+#include "third_party/blink/public/platform/web_gesture_curve.h"
+#include "third_party/blink/public/platform/web_graphics_context_3d_provider.h"
+#include "third_party/blink/public/platform/web_image_capture_frame_grabber.h"
+#include "third_party/blink/public/platform/web_media_recorder_handler.h"
+#include "third_party/blink/public/platform/web_media_stream_center.h"
+#include "third_party/blink/public/platform/web_prerendering_support.h"
+#include "third_party/blink/public/platform/web_rtc_certificate_generator.h"
+#include "third_party/blink/public/platform/web_rtc_peer_connection_handler.h"
+#include "third_party/blink/public/platform/web_socket_handshake_throttle.h"
+#include "third_party/blink/public/platform/web_storage_namespace.h"
+#include "third_party/blink/public/platform/web_thread.h"
+#include "third_party/blink/public/platform/web_trial_token_validator.h"
+#include "third_party/blink/renderer/platform/exported/web_clipboard_impl.h"
+#include "third_party/blink/renderer/platform/font_family_names.h"
+#include "third_party/blink/renderer/platform/fonts/font_cache_memory_dump_provider.h"
+#include "third_party/blink/renderer/platform/heap/blink_gc_memory_dump_provider.h"
+#include "third_party/blink/renderer/platform/heap/gc_task_runner.h"
+#include "third_party/blink/renderer/platform/histogram.h"
+#include "third_party/blink/renderer/platform/instance_counters_memory_dump_provider.h"
+#include "third_party/blink/renderer/platform/instrumentation/resource_coordinator/blink_resource_coordinator_base.h"
+#include "third_party/blink/renderer/platform/instrumentation/resource_coordinator/renderer_resource_coordinator.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/memory_cache_dump_provider.h"
+#include "third_party/blink/renderer/platform/language.h"
+#include "third_party/blink/renderer/platform/memory_coordinator.h"
+#include "third_party/blink/renderer/platform/partition_alloc_memory_dump_provider.h"
+#include "third_party/blink/renderer/platform/web_task_runner.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+
+namespace blink {
+
+namespace {
+
+class DefaultConnector {
+ public:
+ DefaultConnector() {
+ service_manager::mojom::ConnectorRequest request;
+ connector_ = service_manager::Connector::Create(&request);
+ }
+
+ service_manager::Connector* Get() { return connector_.get(); }
+
+ private:
+ std::unique_ptr<service_manager::Connector> connector_;
+};
+
+} // namespace
+
+static Platform* g_platform = nullptr;
+
+static GCTaskRunner* g_gc_task_runner = nullptr;
+
+static void MaxObservedSizeFunction(size_t size_in_mb) {
+ const size_t kSupportedMaxSizeInMB = 4 * 1024;
+ if (size_in_mb >= kSupportedMaxSizeInMB)
+ size_in_mb = kSupportedMaxSizeInMB - 1;
+
+ // Send a UseCounter only when we see the highest memory usage
+ // we've ever seen.
+ DEFINE_STATIC_LOCAL(EnumerationHistogram, committed_size_histogram,
+ ("PartitionAlloc.CommittedSize", kSupportedMaxSizeInMB));
+ committed_size_histogram.Count(size_in_mb);
+}
+
+static void CallOnMainThreadFunction(WTF::MainThreadFunction function,
+ void* context) {
+ PostCrossThreadTask(
+ *Platform::Current()->MainThread()->GetTaskRunner(), FROM_HERE,
+ CrossThreadBind(function, CrossThreadUnretained(context)));
+}
+
+Platform::Platform() : main_thread_(nullptr) {
+ WTF::Partitions::Initialize(MaxObservedSizeFunction);
+}
+
+Platform::~Platform() = default;
+
+void Platform::Initialize(Platform* platform) {
+ DCHECK(!g_platform);
+ DCHECK(platform);
+ g_platform = platform;
+ g_platform->main_thread_ = platform->CurrentThread();
+
+ WTF::Initialize(CallOnMainThreadFunction);
+
+ ProcessHeap::Init();
+ MemoryCoordinator::Initialize();
+ if (base::ThreadTaskRunnerHandle::IsSet()) {
+ base::trace_event::MemoryDumpProvider::Options options;
+ options.supports_heap_profiling = true;
+ base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
+ BlinkGCMemoryDumpProvider::Instance(), "BlinkGC",
+ base::ThreadTaskRunnerHandle::Get(), options);
+ }
+
+ ThreadState::AttachMainThread();
+
+ // FontFamilyNames are used by platform/fonts and are initialized by core.
+ // In case core is not available (like on PPAPI plugins), we need to init
+ // them here.
+ FontFamilyNames::init();
+ InitializePlatformLanguage();
+
+ // TODO(ssid): remove this check after fixing crbug.com/486782.
+ if (g_platform->main_thread_) {
+ DCHECK(!g_gc_task_runner);
+ g_gc_task_runner = new GCTaskRunner(g_platform->main_thread_);
+ base::trace_event::MemoryDumpProvider::Options heap_profiling_options;
+ heap_profiling_options.supports_heap_profiling = true;
+ base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
+ PartitionAllocMemoryDumpProvider::Instance(), "PartitionAlloc",
+ base::ThreadTaskRunnerHandle::Get(), heap_profiling_options);
+ base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
+ FontCacheMemoryDumpProvider::Instance(), "FontCaches",
+ base::ThreadTaskRunnerHandle::Get());
+ base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
+ MemoryCacheDumpProvider::Instance(), "MemoryCache",
+ base::ThreadTaskRunnerHandle::Get());
+ base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
+ InstanceCountersMemoryDumpProvider::Instance(), "BlinkObjectCounters",
+ base::ThreadTaskRunnerHandle::Get());
+ }
+
+ if (BlinkResourceCoordinatorBase::IsEnabled())
+ RendererResourceCoordinator::Initialize();
+}
+
+void Platform::SetCurrentPlatformForTesting(Platform* platform) {
+ DCHECK(platform);
+ g_platform = platform;
+ g_platform->main_thread_ = platform->CurrentThread();
+}
+
+Platform* Platform::Current() {
+ return g_platform;
+}
+
+WebThread* Platform::MainThread() const {
+ return main_thread_;
+}
+
+service_manager::Connector* Platform::GetConnector() {
+ DEFINE_STATIC_LOCAL(DefaultConnector, connector, ());
+ return connector.Get();
+}
+
+InterfaceProvider* Platform::GetInterfaceProvider() {
+ return InterfaceProvider::GetEmptyInterfaceProvider();
+}
+
+std::unique_ptr<WebMIDIAccessor> Platform::CreateMIDIAccessor(
+ WebMIDIAccessorClient*) {
+ return nullptr;
+}
+
+std::unique_ptr<WebStorageNamespace> Platform::CreateLocalStorageNamespace() {
+ return nullptr;
+}
+
+std::unique_ptr<WebStorageNamespace> Platform::CreateSessionStorageNamespace(
+ base::StringPiece namespace_id) {
+ return nullptr;
+}
+
+std::unique_ptr<WebServiceWorkerCacheStorage> Platform::CreateCacheStorage(
+ service_manager::InterfaceProvider* mojo_provider) {
+ return nullptr;
+}
+
+std::unique_ptr<WebThread> Platform::CreateThread(
+ const WebThreadCreationParams& params) {
+ return nullptr;
+}
+
+std::unique_ptr<WebThread> Platform::CreateWebAudioThread() {
+ return nullptr;
+}
+
+std::unique_ptr<WebGraphicsContext3DProvider>
+Platform::CreateOffscreenGraphicsContext3DProvider(
+ const Platform::ContextAttributes&,
+ const WebURL& top_document_url,
+ WebGraphicsContext3DProvider* share_context,
+ Platform::GraphicsInfo*) {
+ return nullptr;
+};
+
+std::unique_ptr<WebGraphicsContext3DProvider>
+Platform::CreateSharedOffscreenGraphicsContext3DProvider() {
+ return nullptr;
+}
+
+std::unique_ptr<WebGestureCurve> Platform::CreateFlingAnimationCurve(
+ WebGestureDevice device_source,
+ const WebFloatPoint& velocity,
+ const WebSize& cumulative_scroll) {
+ return nullptr;
+}
+
+std::unique_ptr<WebRTCPeerConnectionHandler>
+Platform::CreateRTCPeerConnectionHandler(
+ WebRTCPeerConnectionHandlerClient*,
+ scoped_refptr<base::SingleThreadTaskRunner>) {
+ return nullptr;
+}
+
+std::unique_ptr<WebMediaRecorderHandler> Platform::CreateMediaRecorderHandler(
+ scoped_refptr<base::SingleThreadTaskRunner>) {
+ return nullptr;
+}
+
+std::unique_ptr<WebRTCCertificateGenerator>
+Platform::CreateRTCCertificateGenerator() {
+ return nullptr;
+}
+
+std::unique_ptr<WebMediaStreamCenter> Platform::CreateMediaStreamCenter(
+ WebMediaStreamCenterClient*) {
+ return nullptr;
+}
+
+std::unique_ptr<WebCanvasCaptureHandler> Platform::CreateCanvasCaptureHandler(
+ const WebSize&,
+ double,
+ WebMediaStreamTrack*) {
+ return nullptr;
+}
+
+std::unique_ptr<WebSocketHandshakeThrottle>
+Platform::CreateWebSocketHandshakeThrottle() {
+ return nullptr;
+}
+
+std::unique_ptr<WebImageCaptureFrameGrabber>
+Platform::CreateImageCaptureFrameGrabber() {
+ return nullptr;
+}
+
+std::unique_ptr<WebTrialTokenValidator> Platform::CreateTrialTokenValidator() {
+ return nullptr;
+}
+
+// TODO(slangley): Remove this once we can get pepper to use mojo directly.
+WebClipboard* Platform::Clipboard() {
+ DEFINE_STATIC_LOCAL(WebClipboardImpl, clipboard, ());
+ return &clipboard;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/service_registry.cc b/chromium/third_party/blink/renderer/platform/exported/service_registry.cc
new file mode 100644
index 00000000000..fa7d863a309
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/service_registry.cc
@@ -0,0 +1,21 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/interface_provider.h"
+
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+
+namespace blink {
+namespace {
+
+class EmptyInterfaceProvider : public InterfaceProvider {
+ void GetInterface(const char* name, mojo::ScopedMessagePipeHandle) override {}
+};
+}
+
+InterfaceProvider* InterfaceProvider::GetEmptyInterfaceProvider() {
+ DEFINE_STATIC_LOCAL(EmptyInterfaceProvider, empty_interface_provider, ());
+ return &empty_interface_provider;
+}
+}
diff --git a/chromium/third_party/blink/renderer/platform/exported/url_conversion.cc b/chromium/third_party/blink/renderer/platform/exported/url_conversion.cc
new file mode 100644
index 00000000000..4b149343d90
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/url_conversion.cc
@@ -0,0 +1,29 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/url_conversion.h"
+
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "url/gurl.h"
+
+namespace blink {
+
+GURL WebStringToGURL(const WebString& web_string) {
+ if (web_string.IsEmpty())
+ return GURL();
+
+ String str = web_string;
+ if (str.Is8Bit()) {
+ // Ensure the (possibly Latin-1) 8-bit string is UTF-8 for GURL.
+ StringUTF8Adaptor utf8(str);
+ return GURL(utf8.AsStringPiece());
+ }
+
+ // GURL can consume UTF-16 directly.
+ return GURL(base::StringPiece16(str.Characters16(), str.length()));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_active_gesture_animation.cc b/chromium/third_party/blink/renderer/platform/exported/web_active_gesture_animation.cc
new file mode 100644
index 00000000000..30efc50e56b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_active_gesture_animation.cc
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/exported/web_active_gesture_animation.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/public/platform/web_gesture_curve.h"
+#include "third_party/blink/public/platform/web_gesture_curve_target.h"
+
+namespace blink {
+
+std::unique_ptr<WebActiveGestureAnimation>
+WebActiveGestureAnimation::CreateWithTimeOffset(
+ std::unique_ptr<WebGestureCurve> curve,
+ WebGestureCurveTarget* target,
+ double start_time) {
+ return base::WrapUnique(
+ new WebActiveGestureAnimation(std::move(curve), target, start_time));
+}
+
+WebActiveGestureAnimation::~WebActiveGestureAnimation() = default;
+
+WebActiveGestureAnimation::WebActiveGestureAnimation(
+ std::unique_ptr<WebGestureCurve> curve,
+ WebGestureCurveTarget* target,
+ double start_time)
+ : start_time_(start_time), curve_(std::move(curve)), target_(target) {}
+
+bool WebActiveGestureAnimation::Animate(double time) {
+ // All WebGestureCurves assume zero-based time, so we subtract
+ // the animation start time before passing to the curve.
+ return curve_->AdvanceAndApplyToTarget(time - start_time_, target_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_active_gesture_animation.h b/chromium/third_party/blink/renderer/platform/exported/web_active_gesture_animation.h
new file mode 100644
index 00000000000..00d963602b6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_active_gesture_animation.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_EXPORTED_WEB_ACTIVE_GESTURE_ANIMATION_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_EXPORTED_WEB_ACTIVE_GESTURE_ANIMATION_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class WebGestureCurve;
+class WebGestureCurveTarget;
+
+// Implements a gesture animation (fling scroll, etc.) using a curve with a
+// generic interface to define the animation parameters as a function of time,
+// and applies the animation to a target, again via a generic interface. It is
+// assumed that animate() is called on a more-or-less regular basis by the
+// owner.
+class PLATFORM_EXPORT WebActiveGestureAnimation {
+ USING_FAST_MALLOC(WebActiveGestureAnimation);
+ WTF_MAKE_NONCOPYABLE(WebActiveGestureAnimation);
+
+ public:
+ static std::unique_ptr<WebActiveGestureAnimation> CreateWithTimeOffset(
+ std::unique_ptr<WebGestureCurve>,
+ WebGestureCurveTarget*,
+ double start_time);
+ ~WebActiveGestureAnimation();
+
+ bool Animate(double time);
+
+ private:
+ // Assumes a valid WebGestureCurveTarget that outlives the animation.
+ WebActiveGestureAnimation(std::unique_ptr<WebGestureCurve>,
+ WebGestureCurveTarget*,
+ double start_time);
+
+ double start_time_;
+ std::unique_ptr<WebGestureCurve> curve_;
+ WebGestureCurveTarget* target_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_audio_bus.cc b/chromium/third_party/blink/renderer/platform/exported/web_audio_bus.cc
new file mode 100644
index 00000000000..d01605f0012
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_audio_bus.cc
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2010, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/public/platform/web_audio_bus.h"
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/audio/audio_bus.h"
+
+namespace blink {
+
+void WebAudioBus::Initialize(unsigned number_of_channels,
+ size_t length,
+ double sample_rate) {
+ scoped_refptr<AudioBus> audio_bus =
+ AudioBus::Create(number_of_channels, length);
+ audio_bus->SetSampleRate(sample_rate);
+
+ if (private_)
+ private_->Release();
+
+ audio_bus->AddRef();
+ private_ = audio_bus.get();
+}
+
+void WebAudioBus::ResizeSmaller(size_t new_length) {
+ DCHECK(private_);
+ if (private_) {
+ DCHECK_LE(new_length, length());
+ private_->ResizeSmaller(new_length);
+ }
+}
+
+void WebAudioBus::Reset() {
+ if (private_) {
+ private_->Release();
+ private_ = nullptr;
+ }
+}
+
+unsigned WebAudioBus::NumberOfChannels() const {
+ if (!private_)
+ return 0;
+ return private_->NumberOfChannels();
+}
+
+size_t WebAudioBus::length() const {
+ if (!private_)
+ return 0;
+ return private_->length();
+}
+
+double WebAudioBus::SampleRate() const {
+ if (!private_)
+ return 0;
+ return private_->SampleRate();
+}
+
+float* WebAudioBus::ChannelData(unsigned channel_index) {
+ if (!private_)
+ return nullptr;
+ DCHECK_LT(channel_index, NumberOfChannels());
+ return private_->Channel(channel_index)->MutableData();
+}
+
+scoped_refptr<AudioBus> WebAudioBus::Release() {
+ scoped_refptr<AudioBus> audio_bus(private_);
+ private_->Release();
+ private_ = nullptr;
+ return audio_bus;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_audio_device.cc b/chromium/third_party/blink/renderer/platform/exported/web_audio_device.cc
new file mode 100644
index 00000000000..d6e20a8e703
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_audio_device.cc
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2010, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/public/platform/web_audio_device.h"
+
+namespace blink {
+
+void WebAudioDevice::RenderCallback::Render(
+ const WebVector<float*>& destination_data,
+ size_t number_of_frames,
+ double delay,
+ double delay_timestamp,
+ size_t prior_frames_skipped) {}
+
+WebAudioDevice::RenderCallback::~RenderCallback() = default;
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_blob_info.cc b/chromium/third_party/blink/renderer/platform/exported/web_blob_info.cc
new file mode 100644
index 00000000000..5ba895268ae
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_blob_info.cc
@@ -0,0 +1,114 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_blob_info.h"
+
+#include "third_party/blink/renderer/platform/blob/blob_data.h"
+
+namespace blink {
+
+WebBlobInfo::WebBlobInfo(const WebString& uuid,
+ const WebString& type,
+ long long size,
+ mojo::ScopedMessagePipeHandle handle)
+ : WebBlobInfo(BlobDataHandle::Create(
+ uuid,
+ type,
+ size,
+ mojom::blink::BlobPtrInfo(std::move(handle),
+ mojom::blink::Blob::Version_))) {}
+
+WebBlobInfo::WebBlobInfo(const WebString& uuid,
+ const WebString& file_path,
+ const WebString& file_name,
+ const WebString& type,
+ double last_modified,
+ long long size,
+ mojo::ScopedMessagePipeHandle handle)
+ : WebBlobInfo(BlobDataHandle::Create(
+ uuid,
+ type,
+ size,
+ mojom::blink::BlobPtrInfo(std::move(handle),
+ mojom::blink::Blob::Version_)),
+ file_path,
+ file_name,
+ last_modified) {}
+
+// static
+WebBlobInfo WebBlobInfo::BlobForTesting(const WebString& uuid,
+ const WebString& type,
+ long long size) {
+ return WebBlobInfo(uuid, type, size, mojo::MessagePipe().handle0);
+}
+
+// static
+WebBlobInfo WebBlobInfo::FileForTesting(const WebString& uuid,
+ const WebString& file_path,
+ const WebString& file_name,
+ const WebString& type) {
+ return WebBlobInfo(uuid, file_path, file_name, type, 0, -1,
+ mojo::MessagePipe().handle0);
+}
+
+WebBlobInfo::~WebBlobInfo() {
+ blob_handle_.Reset();
+}
+
+WebBlobInfo::WebBlobInfo(const WebBlobInfo& other) {
+ *this = other;
+}
+
+WebBlobInfo& WebBlobInfo::operator=(const WebBlobInfo& other) = default;
+
+mojo::ScopedMessagePipeHandle WebBlobInfo::CloneBlobHandle() const {
+ if (!blob_handle_)
+ return mojo::ScopedMessagePipeHandle();
+ return blob_handle_->CloneBlobPtr().PassInterface().PassHandle();
+}
+
+WebBlobInfo::WebBlobInfo(scoped_refptr<BlobDataHandle> handle)
+ : WebBlobInfo(handle, handle->GetType(), handle->size()) {}
+
+WebBlobInfo::WebBlobInfo(scoped_refptr<BlobDataHandle> handle,
+ const WebString& file_path,
+ const WebString& file_name,
+ double last_modified)
+ : WebBlobInfo(handle,
+ file_path,
+ file_name,
+ handle->GetType(),
+ last_modified,
+ handle->size()) {}
+
+WebBlobInfo::WebBlobInfo(scoped_refptr<BlobDataHandle> handle,
+ const WebString& type,
+ long long size)
+ : is_file_(false),
+ uuid_(handle->Uuid()),
+ type_(type),
+ size_(size),
+ blob_handle_(std::move(handle)),
+ last_modified_(0) {}
+
+WebBlobInfo::WebBlobInfo(scoped_refptr<BlobDataHandle> handle,
+ const WebString& file_path,
+ const WebString& file_name,
+ const WebString& type,
+ double last_modified,
+ long long size)
+ : is_file_(true),
+ uuid_(handle->Uuid()),
+ type_(type),
+ size_(size),
+ blob_handle_(std::move(handle)),
+ file_path_(file_path),
+ file_name_(file_name),
+ last_modified_(last_modified) {}
+
+scoped_refptr<BlobDataHandle> WebBlobInfo::GetBlobHandle() const {
+ return blob_handle_.Get();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_cache.cc b/chromium/third_party/blink/renderer/platform/exported/web_cache.cc
new file mode 100644
index 00000000000..6cf1ea0f1d5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_cache.cc
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/public/platform/web_cache.h"
+
+#include "third_party/blink/renderer/platform/loader/fetch/memory_cache.h"
+
+namespace blink {
+
+// A helper method for coverting a MemoryCache::TypeStatistic to a
+// WebCache::ResourceTypeStat.
+static void ToResourceTypeStat(const MemoryCache::TypeStatistic& from,
+ WebCache::ResourceTypeStat& to) {
+ to.count = from.count;
+ to.size = from.size;
+ to.decoded_size = from.decoded_size;
+}
+
+void WebCache::SetCapacity(size_t capacity) {
+ MemoryCache* cache = GetMemoryCache();
+ if (cache)
+ cache->SetCapacity(static_cast<unsigned>(capacity));
+}
+
+void WebCache::Clear() {
+ MemoryCache* cache = GetMemoryCache();
+ if (cache)
+ cache->EvictResources();
+}
+
+void WebCache::GetUsageStats(UsageStats* result) {
+ DCHECK(result);
+
+ MemoryCache* cache = GetMemoryCache();
+ if (cache) {
+ result->capacity = cache->Capacity();
+ result->size = cache->size();
+ } else {
+ memset(result, 0, sizeof(UsageStats));
+ }
+}
+
+void WebCache::GetResourceTypeStats(ResourceTypeStats* result) {
+ MemoryCache* cache = GetMemoryCache();
+ if (cache) {
+ MemoryCache::Statistics stats = cache->GetStatistics();
+ ToResourceTypeStat(stats.images, result->images);
+ ToResourceTypeStat(stats.css_style_sheets, result->css_style_sheets);
+ ToResourceTypeStat(stats.scripts, result->scripts);
+ ToResourceTypeStat(stats.xsl_style_sheets, result->xsl_style_sheets);
+ ToResourceTypeStat(stats.fonts, result->fonts);
+ ToResourceTypeStat(stats.other, result->other);
+ } else {
+ memset(result, 0, sizeof(WebCache::ResourceTypeStats));
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_canvas_capture_handler.cc b/chromium/third_party/blink/renderer/platform/exported/web_canvas_capture_handler.cc
new file mode 100644
index 00000000000..d7634e5964b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_canvas_capture_handler.cc
@@ -0,0 +1,9 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_canvas_capture_handler.h"
+
+// This file just includes WebCanvasCaptureHandler.h, to make sure
+// MSVC compiler does not fail linking with LNK2019 due to unresolved
+// constructor/destructor and should be in Source/platform/exported.
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_clipboard_impl.cc b/chromium/third_party/blink/renderer/platform/exported/web_clipboard_impl.cc
new file mode 100644
index 00000000000..fadda48a8aa
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_clipboard_impl.cc
@@ -0,0 +1,284 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/exported/web_clipboard_impl.h"
+
+#include "build/build_config.h"
+#include "mojo/public/cpp/system/platform_handle.h"
+#include "net/base/escape.h"
+#include "services/service_manager/public/cpp/connector.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_drag_data.h"
+#include "third_party/blink/public/platform/web_image.h"
+#include "third_party/blink/public/platform/web_size.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/platform/web_url.h"
+#include "third_party/blink/public/platform/web_vector.h"
+#include "third_party/blink/renderer/platform/blob/blob_data.h"
+#include "third_party/blink/renderer/platform/clipboard/clipboard_mime_types.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
+
+namespace blink {
+
+namespace {
+
+struct DragData {
+ WTF::String text;
+ WTF::String html;
+ WTF::HashMap<WTF::String, WTF::String> custom_data;
+};
+
+// This plagiarizes the logic in DropDataBuilder::Build, but only extracts the
+// data needed for the implementation of WriteDataObject.
+// TODO(slangley): Use a mojo struct to send web_drag_data and allow receiving
+// side to extract the data required.
+DragData BuildDragData(const WebDragData& web_drag_data) {
+ DragData result;
+
+ const WebVector<WebDragData::Item>& item_list = web_drag_data.Items();
+ for (size_t i = 0; i < item_list.size(); ++i) {
+ const WebDragData::Item& item = item_list[i];
+ if (item.storage_type == WebDragData::Item::kStorageTypeString) {
+ if (item.string_type == blink::kMimeTypeTextPlain) {
+ result.text = item.string_data;
+ } else if (item.string_type == blink::kMimeTypeTextHTML) {
+ result.html = item.string_data;
+ } else if (item.string_type != blink::kMimeTypeTextURIList &&
+ item.string_type != blink::kMimeTypeDownloadURL) {
+ result.custom_data.insert(item.string_type, item.string_data);
+ }
+ }
+ }
+
+ return result;
+};
+
+String EscapeForHTML(const String& str) {
+ std::string output =
+ net::EscapeForHTML(StringUTF8Adaptor(str).AsStringPiece());
+ return String(output.c_str());
+}
+// TODO(slangley): crbug.com/775830 Remove the implementation of
+// URLToImageMarkup from clipboard_utils.h once we can delete
+// MockWebClipboardImpl.
+WTF::String URLToImageMarkup(const WebURL& url, const WTF::String& title) {
+ WTF::String markup("<img src=\"");
+ markup.append(EscapeForHTML(url.GetString()));
+ markup.append("\"");
+ if (!title.IsEmpty()) {
+ markup.append(" alt=\"");
+ markup.append(EscapeForHTML(title));
+ markup.append("\"");
+ }
+ markup.append("/>");
+ return markup;
+}
+
+String EnsureNotNullWTFString(const WebString& string) {
+ String result = string;
+ if (result.IsNull()) {
+ return g_empty_string16_bit;
+ }
+ return result;
+}
+
+} // namespace
+
+WebClipboardImpl::WebClipboardImpl() {
+ Platform::Current()->GetConnector()->BindInterface(
+ Platform::Current()->GetBrowserServiceName(), &clipboard_);
+}
+
+WebClipboardImpl::~WebClipboardImpl() = default;
+
+uint64_t WebClipboardImpl::SequenceNumber(mojom::ClipboardBuffer buffer) {
+ uint64_t result = 0;
+ if (!IsValidBufferType(buffer))
+ return 0;
+
+ clipboard_->GetSequenceNumber(buffer, &result);
+ return result;
+}
+
+bool WebClipboardImpl::IsFormatAvailable(mojom::ClipboardFormat format,
+ mojom::ClipboardBuffer buffer) {
+ if (!IsValidBufferType(buffer))
+ return false;
+
+ bool result = false;
+ clipboard_->IsFormatAvailable(format, buffer, &result);
+ return result;
+}
+
+WebVector<WebString> WebClipboardImpl::ReadAvailableTypes(
+ mojom::ClipboardBuffer buffer,
+ bool* contains_filenames) {
+ WTF::Vector<WTF::String> types;
+ if (IsValidBufferType(buffer)) {
+ clipboard_->ReadAvailableTypes(buffer, &types, contains_filenames);
+ }
+ return types;
+}
+
+WebString WebClipboardImpl::ReadPlainText(mojom::ClipboardBuffer buffer) {
+ if (!IsValidBufferType(buffer))
+ return WebString();
+
+ WTF::String text;
+ clipboard_->ReadText(buffer, &text);
+ return text;
+}
+
+WebString WebClipboardImpl::ReadHTML(mojom::ClipboardBuffer buffer,
+ WebURL* source_url,
+ unsigned* fragment_start,
+ unsigned* fragment_end) {
+ if (!IsValidBufferType(buffer))
+ return WebString();
+
+ WTF::String html_stdstr;
+ KURL gurl;
+ clipboard_->ReadHtml(buffer, &html_stdstr, &gurl,
+ static_cast<uint32_t*>(fragment_start),
+ static_cast<uint32_t*>(fragment_end));
+ *source_url = gurl;
+ return html_stdstr;
+}
+
+WebString WebClipboardImpl::ReadRTF(mojom::ClipboardBuffer buffer) {
+ if (!IsValidBufferType(buffer))
+ return WebString();
+
+ WTF::String rtf;
+ clipboard_->ReadRtf(buffer, &rtf);
+ return rtf;
+}
+
+WebBlobInfo WebClipboardImpl::ReadImage(mojom::ClipboardBuffer buffer) {
+ if (!IsValidBufferType(buffer))
+ return WebBlobInfo();
+
+ scoped_refptr<BlobDataHandle> blob;
+ clipboard_->ReadImage(buffer, &blob);
+ if (!blob)
+ return WebBlobInfo();
+ return blob;
+}
+
+WebString WebClipboardImpl::ReadCustomData(mojom::ClipboardBuffer buffer,
+ const WebString& type) {
+ if (!IsValidBufferType(buffer))
+ return WebString();
+
+ WTF::String data;
+ clipboard_->ReadCustomData(buffer, EnsureNotNullWTFString(type), &data);
+ return data;
+}
+
+void WebClipboardImpl::WritePlainText(const WebString& plain_text) {
+ clipboard_->WriteText(mojom::ClipboardBuffer::kStandard, plain_text);
+ clipboard_->CommitWrite(mojom::ClipboardBuffer::kStandard);
+}
+
+void WebClipboardImpl::WriteHTML(const WebString& html_text,
+ const WebURL& source_url,
+ const WebString& plain_text,
+ bool write_smart_paste) {
+ clipboard_->WriteHtml(mojom::ClipboardBuffer::kStandard, html_text,
+ source_url);
+ clipboard_->WriteText(mojom::ClipboardBuffer::kStandard, plain_text);
+
+ if (write_smart_paste)
+ clipboard_->WriteSmartPasteMarker(mojom::ClipboardBuffer::kStandard);
+ clipboard_->CommitWrite(mojom::ClipboardBuffer::kStandard);
+}
+
+void WebClipboardImpl::WriteImage(const WebImage& image,
+ const WebURL& url,
+ const WebString& title) {
+ DCHECK(!image.IsNull());
+ if (!WriteImageToClipboard(mojom::ClipboardBuffer::kStandard,
+ image.GetSkBitmap()))
+ return;
+
+ if (url.IsValid() && !url.IsEmpty()) {
+ clipboard_->WriteBookmark(mojom::ClipboardBuffer::kStandard,
+ url.GetString(), EnsureNotNullWTFString(title));
+
+ // When writing the image, we also write the image markup so that pasting
+ // into rich text editors, such as Gmail, reveals the image. We also don't
+ // want to call writeText(), since some applications (WordPad) don't pick
+ // the image if there is also a text format on the clipboard.
+ clipboard_->WriteHtml(mojom::ClipboardBuffer::kStandard,
+ URLToImageMarkup(url, title), KURL());
+ }
+ clipboard_->CommitWrite(mojom::ClipboardBuffer::kStandard);
+}
+
+void WebClipboardImpl::WriteDataObject(const WebDragData& data) {
+ const DragData& drag_data = BuildDragData(data);
+ // TODO(dcheng): Properly support text/uri-list here.
+ // Avoid calling the WriteFoo functions if there is no data associated with a
+ // type. This prevents stomping on clipboard contents that might have been
+ // written by extension functions such as chrome.bookmarkManagerPrivate.copy.
+ if (!drag_data.text.IsNull()) {
+ clipboard_->WriteText(mojom::ClipboardBuffer::kStandard, drag_data.text);
+ }
+ if (!drag_data.html.IsNull()) {
+ clipboard_->WriteHtml(mojom::ClipboardBuffer::kStandard, drag_data.html,
+ KURL());
+ }
+ if (!drag_data.custom_data.IsEmpty()) {
+ clipboard_->WriteCustomData(mojom::ClipboardBuffer::kStandard,
+ std::move(drag_data.custom_data));
+ }
+ clipboard_->CommitWrite(mojom::ClipboardBuffer::kStandard);
+}
+
+bool WebClipboardImpl::IsValidBufferType(mojom::ClipboardBuffer buffer) {
+ switch (buffer) {
+ case mojom::ClipboardBuffer::kStandard:
+ return true;
+ case mojom::ClipboardBuffer::kSelection:
+#if defined(USE_X11)
+ return true;
+#else
+ // Chrome OS and non-X11 unix builds do not support
+ // the X selection clipboad.
+ // TODO: remove the need for this case, see http://crbug.com/361753
+ return false;
+#endif
+ }
+ return true;
+}
+
+bool WebClipboardImpl::WriteImageToClipboard(mojom::ClipboardBuffer buffer,
+ const SkBitmap& bitmap) {
+ // Only 32-bit bitmaps are supported.
+ DCHECK_EQ(bitmap.colorType(), kN32_SkColorType);
+
+ const WebSize size(bitmap.width(), bitmap.height());
+
+ void* pixels = bitmap.getPixels();
+ // TODO(piman): this should not be NULL, but it is. crbug.com/369621
+ if (!pixels)
+ return false;
+
+ base::CheckedNumeric<uint32_t> checked_buf_size = 4;
+ checked_buf_size *= size.width;
+ checked_buf_size *= size.height;
+ if (!checked_buf_size.IsValid())
+ return false;
+
+ // Allocate a shared memory buffer to hold the bitmap bits.
+ uint32_t buf_size = checked_buf_size.ValueOrDie();
+ auto shared_buffer = mojo::SharedBufferHandle::Create(buf_size);
+ auto mapping = shared_buffer->Map(buf_size);
+ memcpy(mapping.get(), pixels, buf_size);
+
+ clipboard_->WriteImage(buffer, size, std::move(shared_buffer));
+ return true;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_clipboard_impl.h b/chromium/third_party/blink/renderer/platform/exported/web_clipboard_impl.h
new file mode 100644
index 00000000000..8bf6c2e96e3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_clipboard_impl.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_EXPORTED_WEB_CLIPBOARD_IMPL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_EXPORTED_WEB_CLIPBOARD_IMPL_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "third_party/blink/public/mojom/clipboard/clipboard.mojom-blink.h"
+#include "third_party/blink/public/platform/web_clipboard.h"
+
+namespace blink {
+
+class WebClipboardImpl : public blink::WebClipboard {
+ public:
+ WebClipboardImpl();
+ virtual ~WebClipboardImpl();
+
+ // WebClipboard methods:
+ uint64_t SequenceNumber(mojom::ClipboardBuffer) override;
+ bool IsFormatAvailable(mojom::ClipboardFormat,
+ mojom::ClipboardBuffer) override;
+ blink::WebVector<blink::WebString> ReadAvailableTypes(
+ mojom::ClipboardBuffer,
+ bool* contains_filenames) override;
+ blink::WebString ReadPlainText(mojom::ClipboardBuffer) override;
+ blink::WebString ReadHTML(mojom::ClipboardBuffer,
+ blink::WebURL* source_url,
+ unsigned* fragment_start,
+ unsigned* fragment_end) override;
+ blink::WebString ReadRTF(mojom::ClipboardBuffer) override;
+ blink::WebBlobInfo ReadImage(mojom::ClipboardBuffer) override;
+ blink::WebString ReadCustomData(mojom::ClipboardBuffer,
+ const blink::WebString& type) override;
+ void WritePlainText(const blink::WebString& plain_text) override;
+ void WriteHTML(const blink::WebString& html_text,
+ const blink::WebURL& source_url,
+ const blink::WebString& plain_text,
+ bool write_smart_paste) override;
+ void WriteImage(const blink::WebImage&,
+ const blink::WebURL& source_url,
+ const blink::WebString& title) override;
+ void WriteDataObject(const blink::WebDragData&) override;
+
+ private:
+ bool IsValidBufferType(mojom::ClipboardBuffer);
+ bool WriteImageToClipboard(mojom::ClipboardBuffer, const SkBitmap&);
+ mojom::blink::ClipboardHostPtr clipboard_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_EXPORTED_WEB_CLIPBOARD_IMPL_H_
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_coalesced_input_event.cc b/chromium/third_party/blink/renderer/platform/exported/web_coalesced_input_event.cc
new file mode 100644
index 00000000000..75198b964aa
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_coalesced_input_event.cc
@@ -0,0 +1,142 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_coalesced_input_event.h"
+
+#include "third_party/blink/public/platform/web_gesture_event.h"
+#include "third_party/blink/public/platform/web_keyboard_event.h"
+#include "third_party/blink/public/platform/web_mouse_wheel_event.h"
+#include "third_party/blink/public/platform/web_pointer_event.h"
+#include "third_party/blink/public/platform/web_touch_event.h"
+
+namespace blink {
+
+namespace {
+
+struct WebInputEventDelete {
+ template <class EventType>
+ bool Execute(WebInputEvent* event) const {
+ if (!event)
+ return false;
+ DCHECK_EQ(sizeof(EventType), event->size());
+ delete static_cast<EventType*>(event);
+ return true;
+ }
+};
+
+template <typename Operator, typename ArgIn>
+bool Apply(Operator op, WebInputEvent::Type type, const ArgIn& arg_in) {
+ if (WebInputEvent::IsMouseEventType(type))
+ return op.template Execute<WebMouseEvent>(arg_in);
+ if (type == WebInputEvent::kMouseWheel)
+ return op.template Execute<WebMouseWheelEvent>(arg_in);
+ if (WebInputEvent::IsKeyboardEventType(type))
+ return op.template Execute<WebKeyboardEvent>(arg_in);
+ if (WebInputEvent::IsTouchEventType(type))
+ return op.template Execute<WebTouchEvent>(arg_in);
+ if (WebInputEvent::IsGestureEventType(type))
+ return op.template Execute<WebGestureEvent>(arg_in);
+ if (WebInputEvent::IsPointerEventType(type))
+ return op.template Execute<WebPointerEvent>(arg_in);
+
+ NOTREACHED() << "Unknown webkit event type " << type;
+ return false;
+}
+}
+
+void WebCoalescedInputEvent::WebInputEventDeleter::operator()(
+ WebInputEvent* event) const {
+ if (!event)
+ return;
+ Apply(WebInputEventDelete(), event->GetType(), event);
+}
+
+WebInputEvent* WebCoalescedInputEvent::EventPointer() {
+ return event_.get();
+}
+
+void WebCoalescedInputEvent::AddCoalescedEvent(
+ const blink::WebInputEvent& event) {
+ coalesced_events_.push_back(MakeWebScopedInputEvent(event));
+}
+
+const WebInputEvent& WebCoalescedInputEvent::Event() const {
+ return *event_.get();
+}
+
+size_t WebCoalescedInputEvent::CoalescedEventSize() const {
+ return coalesced_events_.size();
+}
+
+const WebInputEvent& WebCoalescedInputEvent::CoalescedEvent(
+ size_t index) const {
+ return *coalesced_events_[index].get();
+}
+
+std::vector<const WebInputEvent*>
+WebCoalescedInputEvent::GetCoalescedEventsPointers() const {
+ std::vector<const WebInputEvent*> events;
+ for (const auto& event : coalesced_events_)
+ events.push_back(event.get());
+ return events;
+}
+
+WebCoalescedInputEvent::WebCoalescedInputEvent(const WebInputEvent& event) {
+ event_ = MakeWebScopedInputEvent(event);
+ coalesced_events_.push_back(MakeWebScopedInputEvent(event));
+}
+
+WebCoalescedInputEvent::WebCoalescedInputEvent(
+ const WebInputEvent& event,
+ const std::vector<const WebInputEvent*>& coalesced_events) {
+ event_ = MakeWebScopedInputEvent(event);
+ for (const auto& coalesced_event : coalesced_events)
+ coalesced_events_.push_back(MakeWebScopedInputEvent(*coalesced_event));
+}
+
+WebCoalescedInputEvent::WebCoalescedInputEvent(
+ const WebPointerEvent& event,
+ const std::vector<WebPointerEvent>& coalesced_events) {
+ event_ = MakeWebScopedInputEvent(event);
+ for (const auto& coalesced_event : coalesced_events)
+ coalesced_events_.push_back(MakeWebScopedInputEvent(coalesced_event));
+}
+
+WebCoalescedInputEvent::WebCoalescedInputEvent(
+ const WebCoalescedInputEvent& event)
+ : WebCoalescedInputEvent(event.Event(),
+ event.GetCoalescedEventsPointers()) {}
+
+WebCoalescedInputEvent::WebScopedInputEvent
+WebCoalescedInputEvent::MakeWebScopedInputEvent(
+ const blink::WebInputEvent& event) {
+ if (blink::WebInputEvent::IsGestureEventType(event.GetType())) {
+ return WebScopedInputEvent(new blink::WebGestureEvent(
+ static_cast<const blink::WebGestureEvent&>(event)));
+ }
+ if (blink::WebInputEvent::IsMouseEventType(event.GetType())) {
+ return WebScopedInputEvent(new blink::WebMouseEvent(
+ static_cast<const blink::WebMouseEvent&>(event)));
+ }
+ if (blink::WebInputEvent::IsTouchEventType(event.GetType())) {
+ return WebScopedInputEvent(new blink::WebTouchEvent(
+ static_cast<const blink::WebTouchEvent&>(event)));
+ }
+ if (event.GetType() == blink::WebInputEvent::kMouseWheel) {
+ return WebScopedInputEvent(new blink::WebMouseWheelEvent(
+ static_cast<const blink::WebMouseWheelEvent&>(event)));
+ }
+ if (blink::WebInputEvent::IsKeyboardEventType(event.GetType())) {
+ return WebScopedInputEvent(new blink::WebKeyboardEvent(
+ static_cast<const blink::WebKeyboardEvent&>(event)));
+ }
+ if (blink::WebInputEvent::IsPointerEventType(event.GetType())) {
+ return WebScopedInputEvent(new blink::WebPointerEvent(
+ static_cast<const blink::WebPointerEvent&>(event)));
+ }
+ NOTREACHED();
+ return WebScopedInputEvent();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_content_decryption_module.cc b/chromium/third_party/blink/renderer/platform/exported/web_content_decryption_module.cc
new file mode 100644
index 00000000000..c23d48251a4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_content_decryption_module.cc
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2013, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/public/platform/web_content_decryption_module.h"
+
+namespace blink {
+
+WebContentDecryptionModule::~WebContentDecryptionModule() = default;
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_content_decryption_module_access.cc b/chromium/third_party/blink/renderer/platform/exported/web_content_decryption_module_access.cc
new file mode 100644
index 00000000000..6c1d5f0600e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_content_decryption_module_access.cc
@@ -0,0 +1,11 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_content_decryption_module_access.h"
+
+namespace blink {
+
+WebContentDecryptionModuleAccess::~WebContentDecryptionModuleAccess() = default;
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_content_decryption_module_result.cc b/chromium/third_party/blink/renderer/platform/exported/web_content_decryption_module_result.cc
new file mode 100644
index 00000000000..35c9b45da91
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_content_decryption_module_result.cc
@@ -0,0 +1,57 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_content_decryption_module_result.h"
+
+#include "third_party/blink/renderer/platform/content_decryption_module_result.h"
+
+namespace blink {
+
+void WebContentDecryptionModuleResult::Complete() {
+ impl_->Complete();
+ Reset();
+}
+
+void WebContentDecryptionModuleResult::CompleteWithContentDecryptionModule(
+ WebContentDecryptionModule* cdm) {
+ impl_->CompleteWithContentDecryptionModule(cdm);
+ Reset();
+}
+
+void WebContentDecryptionModuleResult::CompleteWithSession(
+ SessionStatus status) {
+ impl_->CompleteWithSession(status);
+ Reset();
+}
+
+void WebContentDecryptionModuleResult::CompleteWithKeyStatus(
+ WebEncryptedMediaKeyInformation::KeyStatus key_status) {
+ impl_->CompleteWithKeyStatus(key_status);
+ Reset();
+}
+
+void WebContentDecryptionModuleResult::CompleteWithError(
+ WebContentDecryptionModuleException exception,
+ unsigned long system_code,
+ const WebString& error_message) {
+ impl_->CompleteWithError(exception, system_code, error_message);
+ Reset();
+}
+
+WebContentDecryptionModuleResult::WebContentDecryptionModuleResult(
+ ContentDecryptionModuleResult* impl)
+ : impl_(impl) {
+ DCHECK(impl_.Get());
+}
+
+void WebContentDecryptionModuleResult::Reset() {
+ impl_.Reset();
+}
+
+void WebContentDecryptionModuleResult::Assign(
+ const WebContentDecryptionModuleResult& o) {
+ impl_ = o.impl_;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_content_decryption_module_session.cc b/chromium/third_party/blink/renderer/platform/exported/web_content_decryption_module_session.cc
new file mode 100644
index 00000000000..e1a380da5bd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_content_decryption_module_session.cc
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2013, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/public/platform/web_content_decryption_module_session.h"
+
+#include "third_party/blink/public/platform/web_string.h"
+
+namespace blink {
+
+WebContentDecryptionModuleSession::~WebContentDecryptionModuleSession() =
+ default;
+
+WebContentDecryptionModuleSession::Client::~Client() = default;
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_content_setting_callbacks.cc b/chromium/third_party/blink/renderer/platform/exported/web_content_setting_callbacks.cc
new file mode 100644
index 00000000000..8a547c7ebc3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_content_setting_callbacks.cc
@@ -0,0 +1,57 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_content_setting_callbacks.h"
+
+#include <memory>
+#include "third_party/blink/renderer/platform/content_setting_callbacks.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+
+namespace blink {
+
+class WebContentSettingCallbacksPrivate
+ : public RefCounted<WebContentSettingCallbacksPrivate> {
+ public:
+ static scoped_refptr<WebContentSettingCallbacksPrivate> Create(
+ std::unique_ptr<ContentSettingCallbacks> callbacks) {
+ return base::AdoptRef(
+ new WebContentSettingCallbacksPrivate(std::move(callbacks)));
+ }
+
+ ContentSettingCallbacks* Callbacks() { return callbacks_.get(); }
+
+ private:
+ WebContentSettingCallbacksPrivate(
+ std::unique_ptr<ContentSettingCallbacks> callbacks)
+ : callbacks_(std::move(callbacks)) {}
+ std::unique_ptr<ContentSettingCallbacks> callbacks_;
+};
+
+WebContentSettingCallbacks::WebContentSettingCallbacks(
+ std::unique_ptr<ContentSettingCallbacks>&& callbacks) {
+ private_ = WebContentSettingCallbacksPrivate::Create(std::move(callbacks));
+}
+
+void WebContentSettingCallbacks::Reset() {
+ private_.Reset();
+}
+
+void WebContentSettingCallbacks::Assign(
+ const WebContentSettingCallbacks& other) {
+ private_ = other.private_;
+}
+
+void WebContentSettingCallbacks::DoAllow() {
+ DCHECK(!private_.IsNull());
+ private_->Callbacks()->OnAllowed();
+ private_.Reset();
+}
+
+void WebContentSettingCallbacks::DoDeny() {
+ DCHECK(!private_.IsNull());
+ private_->Callbacks()->OnDenied();
+ private_.Reset();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_cors.cc b/chromium/third_party/blink/renderer/platform/exported/web_cors.cc
new file mode 100644
index 00000000000..bc00038c174
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_cors.cc
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2008 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/public/platform/web_cors.h"
+
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "net/http/http_util.h"
+#include "third_party/blink/public/platform/web_security_origin.h"
+#include "third_party/blink/public/platform/web_url_response.h"
+#include "third_party/blink/renderer/platform/loader/cors/cors.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_utils.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
+#include "third_party/blink/renderer/platform/network/http_names.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/weborigin/scheme_registry.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "url/gurl.h"
+#include "url/url_util.h"
+
+using network::mojom::CORSError;
+
+namespace blink {
+
+namespace WebCORS {
+
+namespace {
+
+// A parser for the value of the Access-Control-Expose-Headers header.
+class HTTPHeaderNameListParser {
+ STACK_ALLOCATED();
+
+ public:
+ explicit HTTPHeaderNameListParser(const String& value)
+ : value_(value), pos_(0) {}
+
+ // Tries parsing |value_| expecting it to be conforming to the #field-name
+ // ABNF rule defined in RFC 7230. Returns with the field-name entries stored
+ // in |output| when successful. Otherwise, returns with |output| kept empty.
+ //
+ // |output| must be empty.
+ void Parse(WebHTTPHeaderSet& output) {
+ DCHECK(output.empty());
+
+ while (true) {
+ ConsumeSpaces();
+
+ size_t token_start = pos_;
+ ConsumeTokenChars();
+ size_t token_size = pos_ - token_start;
+ if (token_size == 0) {
+ output.clear();
+ return;
+ }
+
+ const CString& name = value_.Substring(token_start, token_size).Ascii();
+ output.emplace(name.data(), name.length());
+
+ ConsumeSpaces();
+
+ if (pos_ == value_.length())
+ return;
+
+ if (value_[pos_] == ',') {
+ ++pos_;
+ } else {
+ output.clear();
+ return;
+ }
+ }
+ }
+
+ private:
+ // Consumes zero or more spaces (SP and HTAB) from value_.
+ void ConsumeSpaces() {
+ while (true) {
+ if (pos_ == value_.length())
+ return;
+
+ UChar c = value_[pos_];
+ if (c != ' ' && c != '\t')
+ return;
+ ++pos_;
+ }
+ }
+
+ // Consumes zero or more tchars from value_.
+ void ConsumeTokenChars() {
+ while (true) {
+ if (pos_ == value_.length())
+ return;
+
+ UChar c = value_[pos_];
+ if (c > 0x7F || !net::HttpUtil::IsTokenChar(c))
+ return;
+ ++pos_;
+ }
+ }
+
+ const String value_;
+ size_t pos_;
+};
+
+} // namespace
+
+base::Optional<CORSError> HandleRedirect(
+ WebSecurityOrigin& current_security_origin,
+ WebURLRequest& new_request,
+ const WebURL redirect_response_url,
+ const int redirect_response_status_code,
+ const WebHTTPHeaderMap& redirect_response_header,
+ network::mojom::FetchCredentialsMode credentials_mode,
+ ResourceLoaderOptions& options) {
+ const KURL& last_url = redirect_response_url;
+ const KURL& new_url = new_request.Url();
+
+ WebSecurityOrigin& new_security_origin = current_security_origin;
+
+ // TODO(tyoshino): This should be fixed to check not only the last one but
+ // all redirect responses.
+ if (!current_security_origin.CanRequest(last_url)) {
+ base::Optional<CORSError> redirect_error =
+ CORS::CheckRedirectLocation(new_url);
+ if (redirect_error)
+ return redirect_error;
+
+ KURL redirect_response_kurl = redirect_response_url;
+ base::Optional<CORSError> access_error =
+ CORS::CheckAccess(redirect_response_kurl, redirect_response_status_code,
+ redirect_response_header.GetHTTPHeaderMap(),
+ credentials_mode, *current_security_origin.Get());
+ if (access_error)
+ return access_error;
+
+ scoped_refptr<const SecurityOrigin> last_origin =
+ SecurityOrigin::Create(last_url);
+ // Set request's origin to a globally unique identifier as specified in
+ // the step 10 in https://fetch.spec.whatwg.org/#http-redirect-fetch.
+ if (!last_origin->CanRequest(new_url)) {
+ options.security_origin = SecurityOrigin::CreateUnique();
+ new_security_origin = options.security_origin;
+ }
+ }
+
+ if (!current_security_origin.CanRequest(new_url)) {
+ new_request.SetHTTPHeaderField(WebString(HTTPNames::Origin),
+ new_security_origin.ToString());
+ options.cors_flag = true;
+ }
+ return base::nullopt;
+}
+
+WebHTTPHeaderSet ExtractCorsExposedHeaderNamesList(
+ network::mojom::FetchCredentialsMode credentials_mode,
+ const WebURLResponse& response) {
+ // If a response was fetched via a service worker, it will always have
+ // CorsExposedHeaderNames set from the Access-Control-Expose-Headers header.
+ // For requests that didn't come from a service worker, just parse the CORS
+ // header.
+ if (response.WasFetchedViaServiceWorker()) {
+ WebHTTPHeaderSet header_set;
+ for (const auto& header : response.CorsExposedHeaderNames())
+ header_set.emplace(header.Ascii().data(), header.Ascii().length());
+ return header_set;
+ }
+
+ WebHTTPHeaderSet header_set;
+ HTTPHeaderNameListParser parser(response.HttpHeaderField(
+ WebString(HTTPNames::Access_Control_Expose_Headers)));
+ parser.Parse(header_set);
+
+ if (credentials_mode != network::mojom::FetchCredentialsMode::kInclude &&
+ header_set.find("*") != header_set.end()) {
+ header_set.clear();
+ for (const auto& header :
+ response.ToResourceResponse().HttpHeaderFields()) {
+ CString name = header.key.Ascii();
+ header_set.emplace(name.data(), name.length());
+ }
+ }
+ return header_set;
+}
+
+bool IsOnAccessControlResponseHeaderWhitelist(const WebString& name) {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(
+ WebHTTPHeaderSet, allowed_cross_origin_response_headers,
+ ({
+ "cache-control", "content-language", "content-type", "expires",
+ "last-modified", "pragma",
+ }));
+ return allowed_cross_origin_response_headers.find(name.Ascii().data()) !=
+ allowed_cross_origin_response_headers.end();
+}
+
+WebString ListOfCORSEnabledURLSchemes() {
+ return SchemeRegistry::ListOfCORSEnabledURLSchemes();
+}
+
+bool ContainsOnlyCORSSafelistedOrForbiddenHeaders(const WebHTTPHeaderMap& map) {
+ return FetchUtils::ContainsOnlyCORSSafelistedOrForbiddenHeaders(
+ map.GetHTTPHeaderMap());
+}
+
+// In the spec, https://fetch.spec.whatwg.org/#ref-for-concept-request-mode,
+// No-CORS mode is highly discouraged from using it for new features. Only
+// legacy usages for backward compatibility are allowed except for well-designed
+// usages over the fetch API.
+bool IsNoCORSAllowedContext(WebURLRequest::RequestContext context) {
+ switch (context) {
+ case WebURLRequest::kRequestContextAudio:
+ case WebURLRequest::kRequestContextFavicon:
+ case WebURLRequest::kRequestContextFetch:
+ case WebURLRequest::kRequestContextImage:
+ case WebURLRequest::kRequestContextObject:
+ case WebURLRequest::kRequestContextPlugin:
+ case WebURLRequest::kRequestContextScript:
+ case WebURLRequest::kRequestContextSharedWorker:
+ case WebURLRequest::kRequestContextVideo:
+ case WebURLRequest::kRequestContextWorker:
+ return true;
+ default:
+ return false;
+ }
+}
+
+} // namespace WebCORS
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_cors_test.cc b/chromium/third_party/blink/renderer/platform/exported/web_cors_test.cc
new file mode 100644
index 00000000000..f724d12c7d1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_cors_test.cc
@@ -0,0 +1,109 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_cors.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/web_cors.h"
+#include "third_party/blink/renderer/platform/exported/wrapped_resource_response.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
+
+namespace blink {
+
+namespace {
+
+class CORSExposedHeadersTest : public testing::Test {
+ public:
+ using CredentialsMode = network::mojom::FetchCredentialsMode;
+
+ WebHTTPHeaderSet Parse(CredentialsMode credentials_mode,
+ const AtomicString& header) const {
+ ResourceResponse response;
+ response.AddHTTPHeaderField("access-control-expose-headers", header);
+
+ return WebCORS::ExtractCorsExposedHeaderNamesList(
+ credentials_mode, WrappedResourceResponse(response));
+ }
+};
+
+TEST_F(CORSExposedHeadersTest, ValidInput) {
+ EXPECT_EQ(Parse(CredentialsMode::kOmit, "valid"),
+ WebHTTPHeaderSet({"valid"}));
+
+ EXPECT_EQ(Parse(CredentialsMode::kOmit, "a,b"), WebHTTPHeaderSet({"a", "b"}));
+
+ EXPECT_EQ(Parse(CredentialsMode::kOmit, " a , b "),
+ WebHTTPHeaderSet({"a", "b"}));
+
+ EXPECT_EQ(Parse(CredentialsMode::kOmit, " \t \t\t a"),
+ WebHTTPHeaderSet({"a"}));
+}
+
+TEST_F(CORSExposedHeadersTest, DuplicatedEntries) {
+ EXPECT_EQ(Parse(CredentialsMode::kOmit, "a, a"), WebHTTPHeaderSet{"a"});
+
+ EXPECT_EQ(Parse(CredentialsMode::kOmit, "a, a, b"),
+ WebHTTPHeaderSet({"a", "b"}));
+}
+
+TEST_F(CORSExposedHeadersTest, InvalidInput) {
+ EXPECT_TRUE(Parse(CredentialsMode::kOmit, "not valid").empty());
+
+ EXPECT_TRUE(Parse(CredentialsMode::kOmit, "///").empty());
+
+ EXPECT_TRUE(Parse(CredentialsMode::kOmit, "/a/").empty());
+
+ EXPECT_TRUE(Parse(CredentialsMode::kOmit, ",").empty());
+
+ EXPECT_TRUE(Parse(CredentialsMode::kOmit, " , ").empty());
+
+ EXPECT_TRUE(Parse(CredentialsMode::kOmit, " , a").empty());
+
+ EXPECT_TRUE(Parse(CredentialsMode::kOmit, "a , ").empty());
+
+ EXPECT_TRUE(Parse(CredentialsMode::kOmit, "").empty());
+
+ EXPECT_TRUE(Parse(CredentialsMode::kOmit, " ").empty());
+
+ // U+0141 which is 'A' (0x41) + 0x100.
+ EXPECT_TRUE(
+ Parse(CredentialsMode::kOmit, AtomicString(String::FromUTF8("\xC5\x81")))
+ .empty());
+}
+
+TEST_F(CORSExposedHeadersTest, Wildcard) {
+ ResourceResponse response;
+ response.AddHTTPHeaderField("access-control-expose-headers", "a, b, *");
+ response.AddHTTPHeaderField("b", "-");
+ response.AddHTTPHeaderField("c", "-");
+ response.AddHTTPHeaderField("d", "-");
+ response.AddHTTPHeaderField("*", "-");
+
+ EXPECT_EQ(
+ WebCORS::ExtractCorsExposedHeaderNamesList(
+ CredentialsMode::kOmit, WrappedResourceResponse(response)),
+ WebHTTPHeaderSet({"access-control-expose-headers", "b", "c", "d", "*"}));
+
+ EXPECT_EQ(
+ WebCORS::ExtractCorsExposedHeaderNamesList(
+ CredentialsMode::kSameOrigin, WrappedResourceResponse(response)),
+ WebHTTPHeaderSet({"access-control-expose-headers", "b", "c", "d", "*"}));
+}
+
+TEST_F(CORSExposedHeadersTest, Asterisk) {
+ ResourceResponse response;
+ response.AddHTTPHeaderField("access-control-expose-headers", "a, b, *");
+ response.AddHTTPHeaderField("b", "-");
+ response.AddHTTPHeaderField("c", "-");
+ response.AddHTTPHeaderField("d", "-");
+ response.AddHTTPHeaderField("*", "-");
+
+ EXPECT_EQ(WebCORS::ExtractCorsExposedHeaderNamesList(
+ CredentialsMode::kInclude, WrappedResourceResponse(response)),
+ WebHTTPHeaderSet({"a", "b", "*"}));
+}
+
+} // namespace
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_crypto_algorithm.cc b/chromium/third_party/blink/renderer/platform/exported/web_crypto_algorithm.cc
new file mode 100644
index 00000000000..dc94ef3502e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_crypto_algorithm.cc
@@ -0,0 +1,547 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/public/platform/web_crypto_algorithm.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/public/platform/web_crypto_algorithm_params.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
+
+namespace blink {
+
+namespace {
+
+// A mapping from the algorithm ID to information about the algorithm.
+constexpr WebCryptoAlgorithmInfo kAlgorithmIdToInfo[] = {
+ {// Index 0
+ "AES-CBC",
+ {
+ kWebCryptoAlgorithmParamsTypeAesCbcParams, // Encrypt
+ kWebCryptoAlgorithmParamsTypeAesCbcParams, // Decrypt
+ WebCryptoAlgorithmInfo::kUndefined, // Sign
+ WebCryptoAlgorithmInfo::kUndefined, // Verify
+ WebCryptoAlgorithmInfo::kUndefined, // Digest
+ kWebCryptoAlgorithmParamsTypeAesKeyGenParams, // GenerateKey
+ kWebCryptoAlgorithmParamsTypeNone, // ImportKey
+ kWebCryptoAlgorithmParamsTypeAesDerivedKeyParams, // GetKeyLength
+ WebCryptoAlgorithmInfo::kUndefined, // DeriveBits
+ kWebCryptoAlgorithmParamsTypeAesCbcParams, // WrapKey
+ kWebCryptoAlgorithmParamsTypeAesCbcParams // UnwrapKey
+ }},
+ {// Index 1
+ "HMAC",
+ {
+ WebCryptoAlgorithmInfo::kUndefined, // Encrypt
+ WebCryptoAlgorithmInfo::kUndefined, // Decrypt
+ kWebCryptoAlgorithmParamsTypeNone, // Sign
+ kWebCryptoAlgorithmParamsTypeNone, // Verify
+ WebCryptoAlgorithmInfo::kUndefined, // Digest
+ kWebCryptoAlgorithmParamsTypeHmacKeyGenParams, // GenerateKey
+ kWebCryptoAlgorithmParamsTypeHmacImportParams, // ImportKey
+ kWebCryptoAlgorithmParamsTypeHmacImportParams, // GetKeyLength
+ WebCryptoAlgorithmInfo::kUndefined, // DeriveBits
+ WebCryptoAlgorithmInfo::kUndefined, // WrapKey
+ WebCryptoAlgorithmInfo::kUndefined // UnwrapKey
+ }},
+ {// Index 2
+ "RSASSA-PKCS1-v1_5",
+ {
+ WebCryptoAlgorithmInfo::kUndefined, // Encrypt
+ WebCryptoAlgorithmInfo::kUndefined, // Decrypt
+ kWebCryptoAlgorithmParamsTypeNone, // Sign
+ kWebCryptoAlgorithmParamsTypeNone, // Verify
+ WebCryptoAlgorithmInfo::kUndefined, // Digest
+ kWebCryptoAlgorithmParamsTypeRsaHashedKeyGenParams, // GenerateKey
+ kWebCryptoAlgorithmParamsTypeRsaHashedImportParams, // ImportKey
+ WebCryptoAlgorithmInfo::kUndefined, // GetKeyLength
+ WebCryptoAlgorithmInfo::kUndefined, // DeriveBits
+ WebCryptoAlgorithmInfo::kUndefined, // WrapKey
+ WebCryptoAlgorithmInfo::kUndefined // UnwrapKey
+ }},
+ {// Index 3
+ "SHA-1",
+ {
+ WebCryptoAlgorithmInfo::kUndefined, // Encrypt
+ WebCryptoAlgorithmInfo::kUndefined, // Decrypt
+ WebCryptoAlgorithmInfo::kUndefined, // Sign
+ WebCryptoAlgorithmInfo::kUndefined, // Verify
+ kWebCryptoAlgorithmParamsTypeNone, // Digest
+ WebCryptoAlgorithmInfo::kUndefined, // GenerateKey
+ WebCryptoAlgorithmInfo::kUndefined, // ImportKey
+ WebCryptoAlgorithmInfo::kUndefined, // GetKeyLength
+ WebCryptoAlgorithmInfo::kUndefined, // DeriveBits
+ WebCryptoAlgorithmInfo::kUndefined, // WrapKey
+ WebCryptoAlgorithmInfo::kUndefined // UnwrapKey
+ }},
+ {// Index 4
+ "SHA-256",
+ {
+ WebCryptoAlgorithmInfo::kUndefined, // Encrypt
+ WebCryptoAlgorithmInfo::kUndefined, // Decrypt
+ WebCryptoAlgorithmInfo::kUndefined, // Sign
+ WebCryptoAlgorithmInfo::kUndefined, // Verify
+ kWebCryptoAlgorithmParamsTypeNone, // Digest
+ WebCryptoAlgorithmInfo::kUndefined, // GenerateKey
+ WebCryptoAlgorithmInfo::kUndefined, // ImportKey
+ WebCryptoAlgorithmInfo::kUndefined, // GetKeyLength
+ WebCryptoAlgorithmInfo::kUndefined, // DeriveBits
+ WebCryptoAlgorithmInfo::kUndefined, // WrapKey
+ WebCryptoAlgorithmInfo::kUndefined // UnwrapKey
+ }},
+ {// Index 5
+ "SHA-384",
+ {
+ WebCryptoAlgorithmInfo::kUndefined, // Encrypt
+ WebCryptoAlgorithmInfo::kUndefined, // Decrypt
+ WebCryptoAlgorithmInfo::kUndefined, // Sign
+ WebCryptoAlgorithmInfo::kUndefined, // Verify
+ kWebCryptoAlgorithmParamsTypeNone, // Digest
+ WebCryptoAlgorithmInfo::kUndefined, // GenerateKey
+ WebCryptoAlgorithmInfo::kUndefined, // ImportKey
+ WebCryptoAlgorithmInfo::kUndefined, // GetKeyLength
+ WebCryptoAlgorithmInfo::kUndefined, // DeriveBits
+ WebCryptoAlgorithmInfo::kUndefined, // WrapKey
+ WebCryptoAlgorithmInfo::kUndefined // UnwrapKey
+ }},
+ {// Index 6
+ "SHA-512",
+ {
+ WebCryptoAlgorithmInfo::kUndefined, // Encrypt
+ WebCryptoAlgorithmInfo::kUndefined, // Decrypt
+ WebCryptoAlgorithmInfo::kUndefined, // Sign
+ WebCryptoAlgorithmInfo::kUndefined, // Verify
+ kWebCryptoAlgorithmParamsTypeNone, // Digest
+ WebCryptoAlgorithmInfo::kUndefined, // GenerateKey
+ WebCryptoAlgorithmInfo::kUndefined, // ImportKey
+ WebCryptoAlgorithmInfo::kUndefined, // GetKeyLength
+ WebCryptoAlgorithmInfo::kUndefined, // DeriveBits
+ WebCryptoAlgorithmInfo::kUndefined, // WrapKey
+ WebCryptoAlgorithmInfo::kUndefined // UnwrapKey
+ }},
+ {// Index 7
+ "AES-GCM",
+ {
+ kWebCryptoAlgorithmParamsTypeAesGcmParams, // Encrypt
+ kWebCryptoAlgorithmParamsTypeAesGcmParams, // Decrypt
+ WebCryptoAlgorithmInfo::kUndefined, // Sign
+ WebCryptoAlgorithmInfo::kUndefined, // Verify
+ WebCryptoAlgorithmInfo::kUndefined, // Digest
+ kWebCryptoAlgorithmParamsTypeAesKeyGenParams, // GenerateKey
+ kWebCryptoAlgorithmParamsTypeNone, // ImportKey
+ kWebCryptoAlgorithmParamsTypeAesDerivedKeyParams, // GetKeyLength
+ WebCryptoAlgorithmInfo::kUndefined, // DeriveBits
+ kWebCryptoAlgorithmParamsTypeAesGcmParams, // WrapKey
+ kWebCryptoAlgorithmParamsTypeAesGcmParams // UnwrapKey
+ }},
+ {// Index 8
+ "RSA-OAEP",
+ {
+ kWebCryptoAlgorithmParamsTypeRsaOaepParams, // Encrypt
+ kWebCryptoAlgorithmParamsTypeRsaOaepParams, // Decrypt
+ WebCryptoAlgorithmInfo::kUndefined, // Sign
+ WebCryptoAlgorithmInfo::kUndefined, // Verify
+ WebCryptoAlgorithmInfo::kUndefined, // Digest
+ kWebCryptoAlgorithmParamsTypeRsaHashedKeyGenParams, // GenerateKey
+ kWebCryptoAlgorithmParamsTypeRsaHashedImportParams, // ImportKey
+ WebCryptoAlgorithmInfo::kUndefined, // GetKeyLength
+ WebCryptoAlgorithmInfo::kUndefined, // DeriveBits
+ kWebCryptoAlgorithmParamsTypeRsaOaepParams, // WrapKey
+ kWebCryptoAlgorithmParamsTypeRsaOaepParams // UnwrapKey
+ }},
+ {// Index 9
+ "AES-CTR",
+ {
+ kWebCryptoAlgorithmParamsTypeAesCtrParams, // Encrypt
+ kWebCryptoAlgorithmParamsTypeAesCtrParams, // Decrypt
+ WebCryptoAlgorithmInfo::kUndefined, // Sign
+ WebCryptoAlgorithmInfo::kUndefined, // Verify
+ WebCryptoAlgorithmInfo::kUndefined, // Digest
+ kWebCryptoAlgorithmParamsTypeAesKeyGenParams, // GenerateKey
+ kWebCryptoAlgorithmParamsTypeNone, // ImportKey
+ kWebCryptoAlgorithmParamsTypeAesDerivedKeyParams, // GetKeyLength
+ WebCryptoAlgorithmInfo::kUndefined, // DeriveBits
+ kWebCryptoAlgorithmParamsTypeAesCtrParams, // WrapKey
+ kWebCryptoAlgorithmParamsTypeAesCtrParams // UnwrapKey
+ }},
+ {// Index 10
+ "AES-KW",
+ {
+ WebCryptoAlgorithmInfo::kUndefined, // Encrypt
+ WebCryptoAlgorithmInfo::kUndefined, // Decrypt
+ WebCryptoAlgorithmInfo::kUndefined, // Sign
+ WebCryptoAlgorithmInfo::kUndefined, // Verify
+ WebCryptoAlgorithmInfo::kUndefined, // Digest
+ kWebCryptoAlgorithmParamsTypeAesKeyGenParams, // GenerateKey
+ kWebCryptoAlgorithmParamsTypeNone, // ImportKey
+ kWebCryptoAlgorithmParamsTypeAesDerivedKeyParams, // GetKeyLength
+ WebCryptoAlgorithmInfo::kUndefined, // DeriveBits
+ kWebCryptoAlgorithmParamsTypeNone, // WrapKey
+ kWebCryptoAlgorithmParamsTypeNone // UnwrapKey
+ }},
+ {// Index 11
+ "RSA-PSS",
+ {
+ WebCryptoAlgorithmInfo::kUndefined, // Encrypt
+ WebCryptoAlgorithmInfo::kUndefined, // Decrypt
+ kWebCryptoAlgorithmParamsTypeRsaPssParams, // Sign
+ kWebCryptoAlgorithmParamsTypeRsaPssParams, // Verify
+ WebCryptoAlgorithmInfo::kUndefined, // Digest
+ kWebCryptoAlgorithmParamsTypeRsaHashedKeyGenParams, // GenerateKey
+ kWebCryptoAlgorithmParamsTypeRsaHashedImportParams, // ImportKey
+ WebCryptoAlgorithmInfo::kUndefined, // GetKeyLength
+ WebCryptoAlgorithmInfo::kUndefined, // DeriveBits
+ WebCryptoAlgorithmInfo::kUndefined, // WrapKey
+ WebCryptoAlgorithmInfo::kUndefined // UnwrapKey
+ }},
+ {// Index 12
+ "ECDSA",
+ {
+ WebCryptoAlgorithmInfo::kUndefined, // Encrypt
+ WebCryptoAlgorithmInfo::kUndefined, // Decrypt
+ kWebCryptoAlgorithmParamsTypeEcdsaParams, // Sign
+ kWebCryptoAlgorithmParamsTypeEcdsaParams, // Verify
+ WebCryptoAlgorithmInfo::kUndefined, // Digest
+ kWebCryptoAlgorithmParamsTypeEcKeyGenParams, // GenerateKey
+ kWebCryptoAlgorithmParamsTypeEcKeyImportParams, // ImportKey
+ WebCryptoAlgorithmInfo::kUndefined, // GetKeyLength
+ WebCryptoAlgorithmInfo::kUndefined, // DeriveBits
+ WebCryptoAlgorithmInfo::kUndefined, // WrapKey
+ WebCryptoAlgorithmInfo::kUndefined // UnwrapKey
+ }},
+ {// Index 13
+ "ECDH",
+ {
+ WebCryptoAlgorithmInfo::kUndefined, // Encrypt
+ WebCryptoAlgorithmInfo::kUndefined, // Decrypt
+ WebCryptoAlgorithmInfo::kUndefined, // Sign
+ WebCryptoAlgorithmInfo::kUndefined, // Verify
+ WebCryptoAlgorithmInfo::kUndefined, // Digest
+ kWebCryptoAlgorithmParamsTypeEcKeyGenParams, // GenerateKey
+ kWebCryptoAlgorithmParamsTypeEcKeyImportParams, // ImportKey
+ WebCryptoAlgorithmInfo::kUndefined, // GetKeyLength
+ kWebCryptoAlgorithmParamsTypeEcdhKeyDeriveParams, // DeriveBits
+ WebCryptoAlgorithmInfo::kUndefined, // WrapKey
+ WebCryptoAlgorithmInfo::kUndefined // UnwrapKey
+ }},
+ {// Index 14
+ "HKDF",
+ {
+ WebCryptoAlgorithmInfo::kUndefined, // Encrypt
+ WebCryptoAlgorithmInfo::kUndefined, // Decrypt
+ WebCryptoAlgorithmInfo::kUndefined, // Sign
+ WebCryptoAlgorithmInfo::kUndefined, // Verify
+ WebCryptoAlgorithmInfo::kUndefined, // Digest
+ WebCryptoAlgorithmInfo::kUndefined, // GenerateKey
+ kWebCryptoAlgorithmParamsTypeNone, // ImportKey
+ kWebCryptoAlgorithmParamsTypeNone, // GetKeyLength
+ kWebCryptoAlgorithmParamsTypeHkdfParams, // DeriveBits
+ WebCryptoAlgorithmInfo::kUndefined, // WrapKey
+ WebCryptoAlgorithmInfo::kUndefined // UnwrapKey
+ }},
+ {// Index 15
+ "PBKDF2",
+ {
+ WebCryptoAlgorithmInfo::kUndefined, // Encrypt
+ WebCryptoAlgorithmInfo::kUndefined, // Decrypt
+ WebCryptoAlgorithmInfo::kUndefined, // Sign
+ WebCryptoAlgorithmInfo::kUndefined, // Verify
+ WebCryptoAlgorithmInfo::kUndefined, // Digest
+ WebCryptoAlgorithmInfo::kUndefined, // GenerateKey
+ kWebCryptoAlgorithmParamsTypeNone, // ImportKey
+ kWebCryptoAlgorithmParamsTypeNone, // GetKeyLength
+ kWebCryptoAlgorithmParamsTypePbkdf2Params, // DeriveBits
+ WebCryptoAlgorithmInfo::kUndefined, // WrapKey
+ WebCryptoAlgorithmInfo::kUndefined // UnwrapKey
+ }},
+};
+
+// Initializing the algorithmIdToInfo table above depends on knowing the enum
+// values for algorithm IDs. If those ever change, the table will need to be
+// updated.
+static_assert(kWebCryptoAlgorithmIdAesCbc == 0, "AES CBC id must match");
+static_assert(kWebCryptoAlgorithmIdHmac == 1, "HMAC id must match");
+static_assert(kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5 == 2,
+ "RSASSA-PKCS1-v1_5 id must match");
+static_assert(kWebCryptoAlgorithmIdSha1 == 3, "SHA1 id must match");
+static_assert(kWebCryptoAlgorithmIdSha256 == 4, "SHA256 id must match");
+static_assert(kWebCryptoAlgorithmIdSha384 == 5, "SHA384 id must match");
+static_assert(kWebCryptoAlgorithmIdSha512 == 6, "SHA512 id must match");
+static_assert(kWebCryptoAlgorithmIdAesGcm == 7, "AES GCM id must match");
+static_assert(kWebCryptoAlgorithmIdRsaOaep == 8, "RSA OAEP id must match");
+static_assert(kWebCryptoAlgorithmIdAesCtr == 9, "AES CTR id must match");
+static_assert(kWebCryptoAlgorithmIdAesKw == 10, "AESKW id must match");
+static_assert(kWebCryptoAlgorithmIdRsaPss == 11, "RSA-PSS id must match");
+static_assert(kWebCryptoAlgorithmIdEcdsa == 12, "ECDSA id must match");
+static_assert(kWebCryptoAlgorithmIdEcdh == 13, "ECDH id must match");
+static_assert(kWebCryptoAlgorithmIdHkdf == 14, "HKDF id must match");
+static_assert(kWebCryptoAlgorithmIdPbkdf2 == 15, "Pbkdf2 id must match");
+static_assert(kWebCryptoAlgorithmIdLast == 15, "last id must match");
+static_assert(10 == kWebCryptoOperationLast,
+ "the parameter mapping needs to be updated");
+
+} // namespace
+
+class WebCryptoAlgorithmPrivate
+ : public ThreadSafeRefCounted<WebCryptoAlgorithmPrivate> {
+ public:
+ WebCryptoAlgorithmPrivate(WebCryptoAlgorithmId id,
+ std::unique_ptr<WebCryptoAlgorithmParams> params)
+ : id(id), params(std::move(params)) {}
+
+ WebCryptoAlgorithmId id;
+ std::unique_ptr<WebCryptoAlgorithmParams> params;
+};
+
+WebCryptoAlgorithm::WebCryptoAlgorithm(
+ WebCryptoAlgorithmId id,
+ std::unique_ptr<WebCryptoAlgorithmParams> params)
+ : private_(base::AdoptRef(
+ new WebCryptoAlgorithmPrivate(id, std::move(params)))) {}
+
+WebCryptoAlgorithm WebCryptoAlgorithm::CreateNull() {
+ return WebCryptoAlgorithm();
+}
+
+WebCryptoAlgorithm WebCryptoAlgorithm::AdoptParamsAndCreate(
+ WebCryptoAlgorithmId id,
+ WebCryptoAlgorithmParams* params) {
+ return WebCryptoAlgorithm(id, base::WrapUnique(params));
+}
+
+const WebCryptoAlgorithmInfo* WebCryptoAlgorithm::LookupAlgorithmInfo(
+ WebCryptoAlgorithmId id) {
+ const unsigned id_int = id;
+ if (id_int >= WTF_ARRAY_LENGTH(kAlgorithmIdToInfo))
+ return nullptr;
+ return &kAlgorithmIdToInfo[id];
+}
+
+bool WebCryptoAlgorithm::IsNull() const {
+ return private_.IsNull();
+}
+
+WebCryptoAlgorithmId WebCryptoAlgorithm::Id() const {
+ DCHECK(!IsNull());
+ return private_->id;
+}
+
+WebCryptoAlgorithmParamsType WebCryptoAlgorithm::ParamsType() const {
+ DCHECK(!IsNull());
+ if (!private_->params)
+ return kWebCryptoAlgorithmParamsTypeNone;
+ return private_->params->GetType();
+}
+
+const WebCryptoAesCbcParams* WebCryptoAlgorithm::AesCbcParams() const {
+ DCHECK(!IsNull());
+ if (ParamsType() == kWebCryptoAlgorithmParamsTypeAesCbcParams)
+ return static_cast<WebCryptoAesCbcParams*>(private_->params.get());
+ return nullptr;
+}
+
+const WebCryptoAesCtrParams* WebCryptoAlgorithm::AesCtrParams() const {
+ DCHECK(!IsNull());
+ if (ParamsType() == kWebCryptoAlgorithmParamsTypeAesCtrParams)
+ return static_cast<WebCryptoAesCtrParams*>(private_->params.get());
+ return nullptr;
+}
+
+const WebCryptoAesKeyGenParams* WebCryptoAlgorithm::AesKeyGenParams() const {
+ DCHECK(!IsNull());
+ if (ParamsType() == kWebCryptoAlgorithmParamsTypeAesKeyGenParams)
+ return static_cast<WebCryptoAesKeyGenParams*>(private_->params.get());
+ return nullptr;
+}
+
+const WebCryptoHmacImportParams* WebCryptoAlgorithm::HmacImportParams() const {
+ DCHECK(!IsNull());
+ if (ParamsType() == kWebCryptoAlgorithmParamsTypeHmacImportParams)
+ return static_cast<WebCryptoHmacImportParams*>(private_->params.get());
+ return nullptr;
+}
+
+const WebCryptoHmacKeyGenParams* WebCryptoAlgorithm::HmacKeyGenParams() const {
+ DCHECK(!IsNull());
+ if (ParamsType() == kWebCryptoAlgorithmParamsTypeHmacKeyGenParams)
+ return static_cast<WebCryptoHmacKeyGenParams*>(private_->params.get());
+ return nullptr;
+}
+
+const WebCryptoAesGcmParams* WebCryptoAlgorithm::AesGcmParams() const {
+ DCHECK(!IsNull());
+ if (ParamsType() == kWebCryptoAlgorithmParamsTypeAesGcmParams)
+ return static_cast<WebCryptoAesGcmParams*>(private_->params.get());
+ return nullptr;
+}
+
+const WebCryptoRsaOaepParams* WebCryptoAlgorithm::RsaOaepParams() const {
+ DCHECK(!IsNull());
+ if (ParamsType() == kWebCryptoAlgorithmParamsTypeRsaOaepParams)
+ return static_cast<WebCryptoRsaOaepParams*>(private_->params.get());
+ return nullptr;
+}
+
+const WebCryptoRsaHashedImportParams*
+WebCryptoAlgorithm::RsaHashedImportParams() const {
+ DCHECK(!IsNull());
+ if (ParamsType() == kWebCryptoAlgorithmParamsTypeRsaHashedImportParams)
+ return static_cast<WebCryptoRsaHashedImportParams*>(private_->params.get());
+ return nullptr;
+}
+
+const WebCryptoRsaHashedKeyGenParams*
+WebCryptoAlgorithm::RsaHashedKeyGenParams() const {
+ DCHECK(!IsNull());
+ if (ParamsType() == kWebCryptoAlgorithmParamsTypeRsaHashedKeyGenParams)
+ return static_cast<WebCryptoRsaHashedKeyGenParams*>(private_->params.get());
+ return nullptr;
+}
+
+const WebCryptoRsaPssParams* WebCryptoAlgorithm::RsaPssParams() const {
+ DCHECK(!IsNull());
+ if (ParamsType() == kWebCryptoAlgorithmParamsTypeRsaPssParams)
+ return static_cast<WebCryptoRsaPssParams*>(private_->params.get());
+ return nullptr;
+}
+
+const WebCryptoEcdsaParams* WebCryptoAlgorithm::EcdsaParams() const {
+ DCHECK(!IsNull());
+ if (ParamsType() == kWebCryptoAlgorithmParamsTypeEcdsaParams)
+ return static_cast<WebCryptoEcdsaParams*>(private_->params.get());
+ return nullptr;
+}
+
+const WebCryptoEcKeyGenParams* WebCryptoAlgorithm::EcKeyGenParams() const {
+ DCHECK(!IsNull());
+ if (ParamsType() == kWebCryptoAlgorithmParamsTypeEcKeyGenParams)
+ return static_cast<WebCryptoEcKeyGenParams*>(private_->params.get());
+ return nullptr;
+}
+
+const WebCryptoEcKeyImportParams* WebCryptoAlgorithm::EcKeyImportParams()
+ const {
+ DCHECK(!IsNull());
+ if (ParamsType() == kWebCryptoAlgorithmParamsTypeEcKeyImportParams)
+ return static_cast<WebCryptoEcKeyImportParams*>(private_->params.get());
+ return nullptr;
+}
+
+const WebCryptoEcdhKeyDeriveParams* WebCryptoAlgorithm::EcdhKeyDeriveParams()
+ const {
+ DCHECK(!IsNull());
+ if (ParamsType() == kWebCryptoAlgorithmParamsTypeEcdhKeyDeriveParams)
+ return static_cast<WebCryptoEcdhKeyDeriveParams*>(private_->params.get());
+ return nullptr;
+}
+
+const WebCryptoAesDerivedKeyParams* WebCryptoAlgorithm::AesDerivedKeyParams()
+ const {
+ DCHECK(!IsNull());
+ if (ParamsType() == kWebCryptoAlgorithmParamsTypeAesDerivedKeyParams)
+ return static_cast<WebCryptoAesDerivedKeyParams*>(private_->params.get());
+ return nullptr;
+}
+
+const WebCryptoHkdfParams* WebCryptoAlgorithm::HkdfParams() const {
+ DCHECK(!IsNull());
+ if (ParamsType() == kWebCryptoAlgorithmParamsTypeHkdfParams)
+ return static_cast<WebCryptoHkdfParams*>(private_->params.get());
+ return nullptr;
+}
+
+const WebCryptoPbkdf2Params* WebCryptoAlgorithm::Pbkdf2Params() const {
+ DCHECK(!IsNull());
+ if (ParamsType() == kWebCryptoAlgorithmParamsTypePbkdf2Params)
+ return static_cast<WebCryptoPbkdf2Params*>(private_->params.get());
+ return nullptr;
+}
+
+bool WebCryptoAlgorithm::IsHash(WebCryptoAlgorithmId id) {
+ switch (id) {
+ case kWebCryptoAlgorithmIdSha1:
+ case kWebCryptoAlgorithmIdSha256:
+ case kWebCryptoAlgorithmIdSha384:
+ case kWebCryptoAlgorithmIdSha512:
+ return true;
+ case kWebCryptoAlgorithmIdAesCbc:
+ case kWebCryptoAlgorithmIdHmac:
+ case kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5:
+ case kWebCryptoAlgorithmIdAesGcm:
+ case kWebCryptoAlgorithmIdRsaOaep:
+ case kWebCryptoAlgorithmIdAesCtr:
+ case kWebCryptoAlgorithmIdAesKw:
+ case kWebCryptoAlgorithmIdRsaPss:
+ case kWebCryptoAlgorithmIdEcdsa:
+ case kWebCryptoAlgorithmIdEcdh:
+ case kWebCryptoAlgorithmIdHkdf:
+ case kWebCryptoAlgorithmIdPbkdf2:
+ break;
+ }
+ return false;
+}
+
+bool WebCryptoAlgorithm::IsKdf(WebCryptoAlgorithmId id) {
+ switch (id) {
+ case kWebCryptoAlgorithmIdHkdf:
+ case kWebCryptoAlgorithmIdPbkdf2:
+ return true;
+ case kWebCryptoAlgorithmIdSha1:
+ case kWebCryptoAlgorithmIdSha256:
+ case kWebCryptoAlgorithmIdSha384:
+ case kWebCryptoAlgorithmIdSha512:
+ case kWebCryptoAlgorithmIdAesCbc:
+ case kWebCryptoAlgorithmIdHmac:
+ case kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5:
+ case kWebCryptoAlgorithmIdAesGcm:
+ case kWebCryptoAlgorithmIdRsaOaep:
+ case kWebCryptoAlgorithmIdAesCtr:
+ case kWebCryptoAlgorithmIdAesKw:
+ case kWebCryptoAlgorithmIdRsaPss:
+ case kWebCryptoAlgorithmIdEcdsa:
+ case kWebCryptoAlgorithmIdEcdh:
+ break;
+ }
+ return false;
+}
+
+void WebCryptoAlgorithm::Assign(const WebCryptoAlgorithm& other) {
+ private_ = other.private_;
+}
+
+void WebCryptoAlgorithm::Reset() {
+ private_.Reset();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_crypto_key.cc b/chromium/third_party/blink/renderer/platform/exported/web_crypto_key.cc
new file mode 100644
index 00000000000..a519f39ffd4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_crypto_key.cc
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/public/platform/web_crypto_key.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/public/platform/web_crypto_algorithm.h"
+#include "third_party/blink/public/platform/web_crypto_algorithm_params.h"
+#include "third_party/blink/public/platform/web_crypto_key_algorithm.h"
+#include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
+
+namespace blink {
+
+class WebCryptoKeyPrivate : public ThreadSafeRefCounted<WebCryptoKeyPrivate> {
+ public:
+ WebCryptoKeyPrivate(std::unique_ptr<WebCryptoKeyHandle> handle,
+ WebCryptoKeyType type,
+ bool extractable,
+ const WebCryptoKeyAlgorithm& algorithm,
+ WebCryptoKeyUsageMask usages)
+ : handle(std::move(handle)),
+ type(type),
+ extractable(extractable),
+ algorithm(algorithm),
+ usages(usages) {
+ DCHECK(!algorithm.IsNull());
+ }
+
+ const std::unique_ptr<WebCryptoKeyHandle> handle;
+ const WebCryptoKeyType type;
+ const bool extractable;
+ const WebCryptoKeyAlgorithm algorithm;
+ const WebCryptoKeyUsageMask usages;
+};
+
+WebCryptoKey WebCryptoKey::Create(WebCryptoKeyHandle* handle,
+ WebCryptoKeyType type,
+ bool extractable,
+ const WebCryptoKeyAlgorithm& algorithm,
+ WebCryptoKeyUsageMask usages) {
+ WebCryptoKey key;
+ key.private_ = base::AdoptRef(new WebCryptoKeyPrivate(
+ base::WrapUnique(handle), type, extractable, algorithm, usages));
+ return key;
+}
+
+WebCryptoKey WebCryptoKey::CreateNull() {
+ return WebCryptoKey();
+}
+
+WebCryptoKeyHandle* WebCryptoKey::Handle() const {
+ DCHECK(!IsNull());
+ return private_->handle.get();
+}
+
+WebCryptoKeyType WebCryptoKey::GetType() const {
+ DCHECK(!IsNull());
+ return private_->type;
+}
+
+bool WebCryptoKey::Extractable() const {
+ DCHECK(!IsNull());
+ return private_->extractable;
+}
+
+const WebCryptoKeyAlgorithm& WebCryptoKey::Algorithm() const {
+ DCHECK(!IsNull());
+ return private_->algorithm;
+}
+
+WebCryptoKeyUsageMask WebCryptoKey::Usages() const {
+ DCHECK(!IsNull());
+ return private_->usages;
+}
+
+bool WebCryptoKey::IsNull() const {
+ return private_.IsNull();
+}
+
+bool WebCryptoKey::KeyUsageAllows(const blink::WebCryptoKeyUsage usage) const {
+ return ((private_->usages & usage) != 0);
+}
+
+void WebCryptoKey::Assign(const WebCryptoKey& other) {
+ private_ = other.private_;
+}
+
+void WebCryptoKey::Reset() {
+ private_.Reset();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_crypto_key_algorithm.cc b/chromium/third_party/blink/renderer/platform/exported/web_crypto_key_algorithm.cc
new file mode 100644
index 00000000000..dc70386b11a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_crypto_key_algorithm.cc
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2014 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 "third_party/blink/public/platform/web_crypto_key_algorithm.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
+
+namespace blink {
+
+// FIXME: Remove the need for this.
+WebCryptoAlgorithm CreateHash(WebCryptoAlgorithmId hash) {
+ return WebCryptoAlgorithm::AdoptParamsAndCreate(hash, nullptr);
+}
+
+class WebCryptoKeyAlgorithmPrivate
+ : public ThreadSafeRefCounted<WebCryptoKeyAlgorithmPrivate> {
+ public:
+ WebCryptoKeyAlgorithmPrivate(
+ WebCryptoAlgorithmId id,
+ std::unique_ptr<WebCryptoKeyAlgorithmParams> params)
+ : id(id), params(std::move(params)) {}
+
+ WebCryptoAlgorithmId id;
+ std::unique_ptr<WebCryptoKeyAlgorithmParams> params;
+};
+
+WebCryptoKeyAlgorithm::WebCryptoKeyAlgorithm(
+ WebCryptoAlgorithmId id,
+ std::unique_ptr<WebCryptoKeyAlgorithmParams> params)
+ : private_(base::AdoptRef(
+ new WebCryptoKeyAlgorithmPrivate(id, std::move(params)))) {}
+
+WebCryptoKeyAlgorithm WebCryptoKeyAlgorithm::AdoptParamsAndCreate(
+ WebCryptoAlgorithmId id,
+ WebCryptoKeyAlgorithmParams* params) {
+ return WebCryptoKeyAlgorithm(id, base::WrapUnique(params));
+}
+
+WebCryptoKeyAlgorithm WebCryptoKeyAlgorithm::CreateAes(
+ WebCryptoAlgorithmId id,
+ unsigned short key_length_bits) {
+ // FIXME: Verify that id is an AES algorithm.
+ // FIXME: Move this somewhere more general.
+ if (key_length_bits != 128 && key_length_bits != 192 &&
+ key_length_bits != 256)
+ return WebCryptoKeyAlgorithm();
+ return WebCryptoKeyAlgorithm(
+ id, std::make_unique<WebCryptoAesKeyAlgorithmParams>(key_length_bits));
+}
+
+WebCryptoKeyAlgorithm WebCryptoKeyAlgorithm::CreateHmac(
+ WebCryptoAlgorithmId hash,
+ unsigned key_length_bits) {
+ if (!WebCryptoAlgorithm::IsHash(hash))
+ return WebCryptoKeyAlgorithm();
+ return WebCryptoKeyAlgorithm(
+ kWebCryptoAlgorithmIdHmac,
+ std::make_unique<WebCryptoHmacKeyAlgorithmParams>(CreateHash(hash),
+ key_length_bits));
+}
+
+WebCryptoKeyAlgorithm WebCryptoKeyAlgorithm::CreateRsaHashed(
+ WebCryptoAlgorithmId id,
+ unsigned modulus_length_bits,
+ const unsigned char* public_exponent,
+ unsigned public_exponent_size,
+ WebCryptoAlgorithmId hash) {
+ // FIXME: Verify that id is an RSA algorithm which expects a hash
+ if (!WebCryptoAlgorithm::IsHash(hash))
+ return WebCryptoKeyAlgorithm();
+ return WebCryptoKeyAlgorithm(
+ id, std::make_unique<WebCryptoRsaHashedKeyAlgorithmParams>(
+ modulus_length_bits, public_exponent, public_exponent_size,
+ CreateHash(hash)));
+}
+
+WebCryptoKeyAlgorithm WebCryptoKeyAlgorithm::CreateEc(
+ WebCryptoAlgorithmId id,
+ WebCryptoNamedCurve named_curve) {
+ return WebCryptoKeyAlgorithm(
+ id, std::make_unique<WebCryptoEcKeyAlgorithmParams>(named_curve));
+}
+
+WebCryptoKeyAlgorithm WebCryptoKeyAlgorithm::CreateWithoutParams(
+ WebCryptoAlgorithmId id) {
+ if (!WebCryptoAlgorithm::IsKdf(id))
+ return WebCryptoKeyAlgorithm();
+ return WebCryptoKeyAlgorithm(id, nullptr);
+}
+
+bool WebCryptoKeyAlgorithm::IsNull() const {
+ return private_.IsNull();
+}
+
+WebCryptoAlgorithmId WebCryptoKeyAlgorithm::Id() const {
+ DCHECK(!IsNull());
+ return private_->id;
+}
+
+WebCryptoKeyAlgorithmParamsType WebCryptoKeyAlgorithm::ParamsType() const {
+ DCHECK(!IsNull());
+ if (!private_->params.get())
+ return kWebCryptoKeyAlgorithmParamsTypeNone;
+ return private_->params->GetType();
+}
+
+WebCryptoAesKeyAlgorithmParams* WebCryptoKeyAlgorithm::AesParams() const {
+ DCHECK(!IsNull());
+ if (ParamsType() == kWebCryptoKeyAlgorithmParamsTypeAes)
+ return static_cast<WebCryptoAesKeyAlgorithmParams*>(private_->params.get());
+ return nullptr;
+}
+
+WebCryptoHmacKeyAlgorithmParams* WebCryptoKeyAlgorithm::HmacParams() const {
+ DCHECK(!IsNull());
+ if (ParamsType() == kWebCryptoKeyAlgorithmParamsTypeHmac)
+ return static_cast<WebCryptoHmacKeyAlgorithmParams*>(
+ private_->params.get());
+ return nullptr;
+}
+
+WebCryptoRsaHashedKeyAlgorithmParams* WebCryptoKeyAlgorithm::RsaHashedParams()
+ const {
+ DCHECK(!IsNull());
+ if (ParamsType() == kWebCryptoKeyAlgorithmParamsTypeRsaHashed)
+ return static_cast<WebCryptoRsaHashedKeyAlgorithmParams*>(
+ private_->params.get());
+ return nullptr;
+}
+
+WebCryptoEcKeyAlgorithmParams* WebCryptoKeyAlgorithm::EcParams() const {
+ DCHECK(!IsNull());
+ if (ParamsType() == kWebCryptoKeyAlgorithmParamsTypeEc)
+ return static_cast<WebCryptoEcKeyAlgorithmParams*>(private_->params.get());
+ return nullptr;
+}
+
+void WebCryptoKeyAlgorithm::WriteToDictionary(
+ WebCryptoKeyAlgorithmDictionary* dict) const {
+ DCHECK(!IsNull());
+ dict->SetString("name", WebCryptoAlgorithm::LookupAlgorithmInfo(Id())->name);
+ if (private_->params.get())
+ private_->params.get()->WriteToDictionary(dict);
+}
+
+void WebCryptoKeyAlgorithm::Assign(const WebCryptoKeyAlgorithm& other) {
+ private_ = other.private_;
+}
+
+void WebCryptoKeyAlgorithm::Reset() {
+ private_.Reset();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_crypto_result.cc b/chromium/third_party/blink/renderer/platform/exported/web_crypto_result.cc
new file mode 100644
index 00000000000..2df5c0fded7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_crypto_result.cc
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/public/platform/web_crypto.h"
+
+#include "third_party/blink/renderer/platform/crypto_result.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+
+namespace blink {
+
+void WebCryptoResult::CompleteWithError(WebCryptoErrorType error_type,
+ const WebString& error_details) {
+ if (!Cancelled())
+ impl_->CompleteWithError(error_type, error_details);
+ Reset();
+}
+
+void WebCryptoResult::CompleteWithBuffer(const void* bytes,
+ unsigned bytes_size) {
+ if (!Cancelled())
+ impl_->CompleteWithBuffer(bytes, bytes_size);
+ Reset();
+}
+
+void WebCryptoResult::CompleteWithJson(const char* utf8_data, unsigned length) {
+ if (!Cancelled())
+ impl_->CompleteWithJson(utf8_data, length);
+ Reset();
+}
+
+void WebCryptoResult::CompleteWithBoolean(bool b) {
+ if (!Cancelled())
+ impl_->CompleteWithBoolean(b);
+ Reset();
+}
+
+void WebCryptoResult::CompleteWithKey(const WebCryptoKey& key) {
+ DCHECK(!key.IsNull());
+ if (!Cancelled())
+ impl_->CompleteWithKey(key);
+ Reset();
+}
+
+void WebCryptoResult::CompleteWithKeyPair(const WebCryptoKey& public_key,
+ const WebCryptoKey& private_key) {
+ DCHECK(!public_key.IsNull());
+ DCHECK(!private_key.IsNull());
+ if (!Cancelled())
+ impl_->CompleteWithKeyPair(public_key, private_key);
+ Reset();
+}
+
+bool WebCryptoResult::Cancelled() const {
+ return cancel_->Cancelled();
+}
+
+WebCryptoResult::WebCryptoResult(CryptoResult* impl,
+ scoped_refptr<CryptoResultCancel> cancel)
+ : impl_(impl), cancel_(std::move(cancel)) {
+ DCHECK(impl_.Get());
+ DCHECK(cancel_.Get());
+}
+
+void WebCryptoResult::Reset() {
+ impl_.Reset();
+ cancel_.Reset();
+}
+
+void WebCryptoResult::Assign(const WebCryptoResult& o) {
+ impl_ = o.impl_;
+ cancel_ = o.cancel_;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_cursor_info.cc b/chromium/third_party/blink/renderer/platform/exported/web_cursor_info.cc
new file mode 100644
index 00000000000..1d0d5bb473a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_cursor_info.cc
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/public/platform/web_cursor_info.h"
+
+#include "third_party/blink/renderer/platform/cursor.h"
+
+namespace blink {
+
+WebCursorInfo::WebCursorInfo(const Cursor& cursor)
+ : type(static_cast<Type>(cursor.GetType())),
+ hot_spot(cursor.HotSpot()),
+ image_scale_factor(cursor.ImageScaleFactor()),
+ custom_image(cursor.GetImage())
+#ifdef WIN32
+ ,
+ external_handle(0)
+#endif
+{
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_data.cc b/chromium/third_party/blink/renderer/platform/exported/web_data.cc
new file mode 100644
index 00000000000..41858db01a8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_data.cc
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/public/platform/web_data.h"
+
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+
+namespace blink {
+
+void WebData::Reset() {
+ private_.Reset();
+}
+
+void WebData::Assign(const WebData& other) {
+ private_ = other.private_;
+}
+
+void WebData::Assign(const char* data, size_t size) {
+ private_ = SharedBuffer::Create(data, size);
+}
+
+size_t WebData::size() const {
+ if (private_.IsNull())
+ return 0;
+ return private_->size();
+}
+
+size_t WebData::GetSomeData(const char*& data, size_t position) const {
+ return private_.IsNull() ? 0 : private_->GetSomeData(data, position);
+}
+
+WebData::WebData(scoped_refptr<SharedBuffer> buffer)
+ : private_(std::move(buffer)) {}
+
+WebData& WebData::operator=(scoped_refptr<SharedBuffer> buffer) {
+ private_ = std::move(buffer);
+ return *this;
+}
+
+WebData::operator scoped_refptr<SharedBuffer>() const {
+ return scoped_refptr<SharedBuffer>(private_.Get());
+}
+
+WebData::operator const SharedBuffer&() const {
+ return *private_;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_data_consumer_handle.cc b/chromium/third_party/blink/renderer/platform/exported/web_data_consumer_handle.cc
new file mode 100644
index 00000000000..374e8441edc
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_data_consumer_handle.cc
@@ -0,0 +1,38 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_data_consumer_handle.h"
+
+#include <string.h>
+#include <algorithm>
+#include <memory>
+#include "third_party/blink/renderer/platform/heap/handle.h"
+
+namespace blink {
+
+WebDataConsumerHandle::WebDataConsumerHandle() {
+ DCHECK(ThreadState::Current());
+}
+
+WebDataConsumerHandle::~WebDataConsumerHandle() {
+ DCHECK(ThreadState::Current());
+}
+
+WebDataConsumerHandle::Result WebDataConsumerHandle::Reader::Read(
+ void* data,
+ size_t size,
+ Flags flags,
+ size_t* read_size) {
+ *read_size = 0;
+ const void* src = nullptr;
+ size_t available;
+ Result r = BeginRead(&src, flags, &available);
+ if (r != WebDataConsumerHandle::kOk)
+ return r;
+ *read_size = std::min(available, size);
+ memcpy(data, src, *read_size);
+ return EndRead(*read_size);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_drag_data.cc b/chromium/third_party/blink/renderer/platform/exported/web_drag_data.cc
new file mode 100644
index 00000000000..52c392f4fda
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_drag_data.cc
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/public/platform/web_drag_data.h"
+
+namespace blink {
+
+void WebDragData::SetItems(WebVector<Item> item_list) {
+ DCHECK(!IsNull());
+ item_list_.Swap(item_list);
+}
+
+void WebDragData::AddItem(const Item& item) {
+ DCHECK(!IsNull());
+ WebVector<Item> item_list(item_list_.size() + 1);
+
+ for (unsigned i = 0; i < item_list_.size(); ++i)
+ item_list[i] = item_list_[i];
+ item_list[item_list_.size()] = item;
+ item_list_.Swap(item_list);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_encrypted_media_client.cc b/chromium/third_party/blink/renderer/platform/exported/web_encrypted_media_client.cc
new file mode 100644
index 00000000000..a0904019430
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_encrypted_media_client.cc
@@ -0,0 +1,11 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_encrypted_media_client.h"
+
+namespace blink {
+
+WebEncryptedMediaClient::~WebEncryptedMediaClient() = default;
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_encrypted_media_key_information.cc b/chromium/third_party/blink/renderer/platform/exported/web_encrypted_media_key_information.cc
new file mode 100644
index 00000000000..091d4af3603
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_encrypted_media_key_information.cc
@@ -0,0 +1,38 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_encrypted_media_key_information.h"
+
+namespace blink {
+
+WebEncryptedMediaKeyInformation::WebEncryptedMediaKeyInformation() = default;
+
+WebEncryptedMediaKeyInformation::~WebEncryptedMediaKeyInformation() = default;
+
+WebData WebEncryptedMediaKeyInformation::Id() const {
+ return id_;
+}
+
+void WebEncryptedMediaKeyInformation::SetId(const WebData& id) {
+ id_.Assign(id);
+}
+
+WebEncryptedMediaKeyInformation::KeyStatus
+WebEncryptedMediaKeyInformation::Status() const {
+ return status_;
+}
+
+void WebEncryptedMediaKeyInformation::SetStatus(KeyStatus status) {
+ status_ = status;
+}
+
+uint32_t WebEncryptedMediaKeyInformation::SystemCode() const {
+ return system_code_;
+}
+
+void WebEncryptedMediaKeyInformation::SetSystemCode(uint32_t system_code) {
+ system_code_ = system_code;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_encrypted_media_request.cc b/chromium/third_party/blink/renderer/platform/exported/web_encrypted_media_request.cc
new file mode 100644
index 00000000000..470722c32c7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_encrypted_media_request.cc
@@ -0,0 +1,60 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_encrypted_media_request.h"
+
+#include "third_party/blink/public/platform/web_media_key_system_configuration.h"
+#include "third_party/blink/public/platform/web_security_origin.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/platform/web_vector.h"
+#include "third_party/blink/renderer/platform/encrypted_media_request.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+
+namespace blink {
+
+WebEncryptedMediaRequest::WebEncryptedMediaRequest(
+ const WebEncryptedMediaRequest& request) {
+ Assign(request);
+}
+
+WebEncryptedMediaRequest::WebEncryptedMediaRequest(
+ EncryptedMediaRequest* request)
+ : private_(request) {}
+
+WebEncryptedMediaRequest::~WebEncryptedMediaRequest() {
+ Reset();
+}
+
+WebString WebEncryptedMediaRequest::KeySystem() const {
+ return private_->KeySystem();
+}
+
+const WebVector<WebMediaKeySystemConfiguration>&
+WebEncryptedMediaRequest::SupportedConfigurations() const {
+ return private_->SupportedConfigurations();
+}
+
+WebSecurityOrigin WebEncryptedMediaRequest::GetSecurityOrigin() const {
+ return WebSecurityOrigin(private_->GetSecurityOrigin());
+}
+
+void WebEncryptedMediaRequest::RequestSucceeded(
+ WebContentDecryptionModuleAccess* access) {
+ private_->RequestSucceeded(access);
+}
+
+void WebEncryptedMediaRequest::RequestNotSupported(
+ const WebString& error_message) {
+ private_->RequestNotSupported(error_message);
+}
+
+void WebEncryptedMediaRequest::Assign(const WebEncryptedMediaRequest& other) {
+ private_ = other.private_;
+}
+
+void WebEncryptedMediaRequest::Reset() {
+ private_.Reset();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_file_system_callbacks.cc b/chromium/third_party/blink/renderer/platform/exported/web_file_system_callbacks.cc
new file mode 100644
index 00000000000..e4858aa42c7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_file_system_callbacks.cc
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/public/platform/web_file_system_callbacks.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/public/platform/web_file_info.h"
+#include "third_party/blink/public/platform/web_file_system.h"
+#include "third_party/blink/public/platform/web_file_system_entry.h"
+#include "third_party/blink/public/platform/web_file_writer.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/renderer/platform/async_file_system_callbacks.h"
+#include "third_party/blink/renderer/platform/file_metadata.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+
+namespace blink {
+
+class WebFileSystemCallbacksPrivate
+ : public RefCounted<WebFileSystemCallbacksPrivate> {
+ public:
+ static scoped_refptr<WebFileSystemCallbacksPrivate> Create(
+ std::unique_ptr<AsyncFileSystemCallbacks> callbacks) {
+ return base::AdoptRef(
+ new WebFileSystemCallbacksPrivate(std::move(callbacks)));
+ }
+
+ AsyncFileSystemCallbacks* Callbacks() { return callbacks_.get(); }
+
+ private:
+ WebFileSystemCallbacksPrivate(
+ std::unique_ptr<AsyncFileSystemCallbacks> callbacks)
+ : callbacks_(std::move(callbacks)) {}
+ std::unique_ptr<AsyncFileSystemCallbacks> callbacks_;
+};
+
+WebFileSystemCallbacks::WebFileSystemCallbacks(
+ std::unique_ptr<AsyncFileSystemCallbacks>&& callbacks) {
+ private_ = WebFileSystemCallbacksPrivate::Create(std::move(callbacks));
+}
+
+void WebFileSystemCallbacks::Reset() {
+ private_.Reset();
+}
+
+void WebFileSystemCallbacks::Assign(const WebFileSystemCallbacks& other) {
+ private_ = other.private_;
+}
+
+void WebFileSystemCallbacks::DidSucceed() {
+ DCHECK(!private_.IsNull());
+ private_->Callbacks()->DidSucceed();
+ private_.Reset();
+}
+
+void WebFileSystemCallbacks::DidReadMetadata(const WebFileInfo& web_file_info) {
+ DCHECK(!private_.IsNull());
+ FileMetadata file_metadata;
+ file_metadata.modification_time = web_file_info.modification_time;
+ file_metadata.length = web_file_info.length;
+ file_metadata.type = static_cast<FileMetadata::Type>(web_file_info.type);
+ file_metadata.platform_path = web_file_info.platform_path;
+ private_->Callbacks()->DidReadMetadata(file_metadata);
+ private_.Reset();
+}
+
+void WebFileSystemCallbacks::DidCreateSnapshotFile(
+ const WebFileInfo& web_file_info) {
+ DCHECK(!private_.IsNull());
+ // It's important to create a BlobDataHandle that refers to the platform file
+ // path prior to return from this method so the underlying file will not be
+ // deleted.
+ std::unique_ptr<BlobData> blob_data = BlobData::Create();
+ blob_data->AppendFile(web_file_info.platform_path, 0, web_file_info.length,
+ InvalidFileTime());
+ scoped_refptr<BlobDataHandle> snapshot_blob =
+ BlobDataHandle::Create(std::move(blob_data), web_file_info.length);
+
+ FileMetadata file_metadata;
+ file_metadata.modification_time = web_file_info.modification_time;
+ file_metadata.length = web_file_info.length;
+ file_metadata.type = static_cast<FileMetadata::Type>(web_file_info.type);
+ file_metadata.platform_path = web_file_info.platform_path;
+ private_->Callbacks()->DidCreateSnapshotFile(file_metadata, snapshot_blob);
+ private_.Reset();
+}
+
+void WebFileSystemCallbacks::DidReadDirectory(
+ const WebVector<WebFileSystemEntry>& entries,
+ bool has_more) {
+ DCHECK(!private_.IsNull());
+ for (size_t i = 0; i < entries.size(); ++i)
+ private_->Callbacks()->DidReadDirectoryEntry(entries[i].name,
+ entries[i].is_directory);
+ private_->Callbacks()->DidReadDirectoryEntries(has_more);
+ private_.Reset();
+}
+
+void WebFileSystemCallbacks::DidOpenFileSystem(const WebString& name,
+ const WebURL& root_url) {
+ DCHECK(!private_.IsNull());
+ private_->Callbacks()->DidOpenFileSystem(name, root_url);
+ private_.Reset();
+}
+
+void WebFileSystemCallbacks::DidResolveURL(const WebString& name,
+ const WebURL& root_url,
+ WebFileSystemType type,
+ const WebString& file_path,
+ bool is_directory) {
+ DCHECK(!private_.IsNull());
+ private_->Callbacks()->DidResolveURL(name, root_url,
+ static_cast<FileSystemType>(type),
+ file_path, is_directory);
+ private_.Reset();
+}
+
+void WebFileSystemCallbacks::DidCreateFileWriter(WebFileWriter* web_file_writer,
+ long long length) {
+ DCHECK(!private_.IsNull());
+ private_->Callbacks()->DidCreateFileWriter(base::WrapUnique(web_file_writer),
+ length);
+ private_.Reset();
+}
+
+void WebFileSystemCallbacks::DidFail(WebFileError error) {
+ DCHECK(!private_.IsNull());
+ private_->Callbacks()->DidFail(error);
+ private_.Reset();
+}
+
+bool WebFileSystemCallbacks::ShouldBlockUntilCompletion() const {
+ DCHECK(!private_.IsNull());
+ return private_->Callbacks()->ShouldBlockUntilCompletion();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_font.cc b/chromium/third_party/blink/renderer/platform/exported/web_font.cc
new file mode 100644
index 00000000000..126e82ffdb5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_font.cc
@@ -0,0 +1,122 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_font.h"
+
+#include "third_party/blink/public/platform/web_float_point.h"
+#include "third_party/blink/public/platform/web_float_rect.h"
+#include "third_party/blink/public/platform/web_font_description.h"
+#include "third_party/blink/public/platform/web_rect.h"
+#include "third_party/blink/public/platform/web_text_run.h"
+#include "third_party/blink/renderer/platform/fonts/font.h"
+#include "third_party/blink/renderer/platform/fonts/font_cache.h"
+#include "third_party/blink/renderer/platform/fonts/font_description.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_record_builder.h"
+#include "third_party/blink/renderer/platform/text/text_run.h"
+
+namespace blink {
+
+WebFont* WebFont::Create(const WebFontDescription& description) {
+ return new WebFont(description);
+}
+
+class WebFont::Impl final {
+ public:
+ explicit Impl(const WebFontDescription& description) : font_(description) {
+ font_.Update(nullptr);
+ }
+
+ const Font& GetFont() const { return font_; }
+
+ private:
+ Font font_;
+};
+
+WebFont::WebFont(const WebFontDescription& description)
+ : private_(new Impl(description)) {}
+
+WebFont::~WebFont() = default;
+
+WebFontDescription WebFont::GetFontDescription() const {
+ return WebFontDescription(private_->GetFont().GetFontDescription());
+}
+
+static inline const SimpleFontData* GetFontData(const Font& font) {
+ const SimpleFontData* font_data = font.PrimaryFont();
+ DCHECK(font_data);
+ return font_data;
+}
+
+int WebFont::Ascent() const {
+ const SimpleFontData* font_data = GetFontData(private_->GetFont());
+ return font_data ? font_data->GetFontMetrics().Ascent() : 0;
+}
+
+int WebFont::Descent() const {
+ const SimpleFontData* font_data = GetFontData(private_->GetFont());
+ return font_data ? font_data->GetFontMetrics().Descent() : 0;
+}
+
+int WebFont::Height() const {
+ const SimpleFontData* font_data = GetFontData(private_->GetFont());
+ return font_data ? font_data->GetFontMetrics().Height() : 0;
+}
+
+int WebFont::LineSpacing() const {
+ const SimpleFontData* font_data = GetFontData(private_->GetFont());
+ return font_data ? font_data->GetFontMetrics().LineSpacing() : 0;
+}
+
+float WebFont::XHeight() const {
+ const SimpleFontData* font_data = private_->GetFont().PrimaryFont();
+ DCHECK(font_data);
+ return font_data ? font_data->GetFontMetrics().XHeight() : 0;
+}
+
+void WebFont::DrawText(WebCanvas* canvas,
+ const WebTextRun& run,
+ const WebFloatPoint& left_baseline,
+ WebColor color,
+ const WebRect& clip) const {
+ FontCachePurgePreventer font_cache_purge_preventer;
+ FloatRect text_clip_rect(clip);
+ TextRun text_run(run);
+ TextRunPaintInfo run_info(text_run);
+ run_info.bounds = text_clip_rect;
+
+ PaintRecordBuilder builder;
+ GraphicsContext& context = builder.Context();
+
+ {
+ DrawingRecorder recorder(context, builder, DisplayItem::kWebFont);
+ context.Save();
+ context.SetFillColor(color);
+ context.Clip(text_clip_rect);
+ context.DrawText(private_->GetFont(), run_info, left_baseline);
+ context.Restore();
+ }
+
+ builder.EndRecording(*canvas);
+}
+
+int WebFont::CalculateWidth(const WebTextRun& run) const {
+ return private_->GetFont().Width(run, nullptr);
+}
+
+int WebFont::OffsetForPosition(const WebTextRun& run, float position) const {
+ return private_->GetFont().OffsetForPosition(run, position, true);
+}
+
+WebFloatRect WebFont::SelectionRectForText(const WebTextRun& run,
+ const WebFloatPoint& left_baseline,
+ int height,
+ int from,
+ int to) const {
+ return private_->GetFont().SelectionRectForText(run, left_baseline, height,
+ from, to);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_font_description.cc b/chromium/third_party/blink/renderer/platform/exported/web_font_description.cc
new file mode 100644
index 00000000000..0420dc0c90d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_font_description.cc
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2010 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 "third_party/blink/public/platform/web_font_description.h"
+
+#include "third_party/blink/renderer/platform/fonts/font_description.h"
+
+namespace blink {
+
+WebFontDescription::WebFontDescription(const FontDescription& desc) {
+ family = desc.Family().Family();
+ generic_family = static_cast<GenericFamily>(desc.GenericFamily());
+ size = desc.SpecifiedSize();
+ italic = desc.Style() == ItalicSlopeValue();
+ small_caps = desc.VariantCaps() == FontDescription::kSmallCaps;
+ DCHECK(desc.Weight() >= 100 && desc.Weight() <= 900 &&
+ static_cast<int>(desc.Weight()) % 100 == 0);
+ weight = static_cast<Weight>(static_cast<int>(desc.Weight()) / 100 - 1);
+ smoothing = static_cast<Smoothing>(desc.FontSmoothing());
+ letter_spacing = desc.LetterSpacing();
+ word_spacing = desc.WordSpacing();
+}
+
+WebFontDescription::operator FontDescription() const {
+ FontFamily font_family;
+ font_family.SetFamily(family);
+
+ FontDescription desc;
+ desc.SetFamily(font_family);
+ desc.SetGenericFamily(
+ static_cast<FontDescription::GenericFamilyType>(generic_family));
+ desc.SetSpecifiedSize(size);
+ desc.SetComputedSize(size);
+ desc.SetStyle(italic ? ItalicSlopeValue() : NormalSlopeValue());
+ desc.SetVariantCaps(small_caps ? FontDescription::kSmallCaps
+ : FontDescription::kCapsNormal);
+ static_assert(static_cast<int>(WebFontDescription::kWeight100) == 0,
+ "kWeight100 conversion");
+ static_assert(static_cast<int>(WebFontDescription::kWeight900) == 8,
+ "kWeight900 conversion");
+ desc.SetWeight(FontSelectionValue((weight + 1) * 100));
+ desc.SetFontSmoothing(static_cast<FontSmoothingMode>(smoothing));
+ desc.SetLetterSpacing(letter_spacing);
+ desc.SetWordSpacing(word_spacing);
+ return desc;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_http_body.cc b/chromium/third_party/blink/renderer/platform/exported/web_http_body.cc
new file mode 100644
index 00000000000..2202619e50a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_http_body.cc
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/public/platform/web_http_body.h"
+
+#include "mojo/public/cpp/system/data_pipe.h"
+#include "services/network/public/mojom/data_pipe_getter.mojom-blink.h"
+#include "third_party/blink/renderer/platform/file_metadata.h"
+#include "third_party/blink/renderer/platform/network/encoded_form_data.h"
+#include "third_party/blink/renderer/platform/network/form_data_encoder.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+
+namespace blink {
+
+void WebHTTPBody::Initialize() {
+ private_ = EncodedFormData::Create();
+}
+
+void WebHTTPBody::Reset() {
+ private_ = nullptr;
+}
+
+void WebHTTPBody::Assign(const WebHTTPBody& other) {
+ private_ = other.private_;
+}
+
+size_t WebHTTPBody::ElementCount() const {
+ DCHECK(!IsNull());
+ return private_->Elements().size();
+}
+
+bool WebHTTPBody::ElementAt(size_t index, Element& result) const {
+ DCHECK(!IsNull());
+
+ if (index >= private_->Elements().size())
+ return false;
+
+ const FormDataElement& element = private_->Elements()[index];
+
+ result.data.Reset();
+ result.file_path.Reset();
+ result.file_start = 0;
+ result.file_length = 0;
+ result.modification_time = InvalidFileTime();
+ result.blob_uuid.Reset();
+
+ switch (element.type_) {
+ case FormDataElement::kData:
+ result.type = Element::kTypeData;
+ result.data.Assign(element.data_.data(), element.data_.size());
+ break;
+ case FormDataElement::kEncodedFile:
+ result.type = Element::kTypeFile;
+ result.file_path = element.filename_;
+ result.file_start = element.file_start_;
+ result.file_length = element.file_length_;
+ result.modification_time = element.expected_file_modification_time_;
+ break;
+ case FormDataElement::kEncodedBlob:
+ result.type = Element::kTypeBlob;
+ result.blob_uuid = element.blob_uuid_;
+ if (element.optional_blob_data_handle_) {
+ result.optional_blob_handle =
+ element.optional_blob_data_handle_->CloneBlobPtr()
+ .PassInterface()
+ .PassHandle();
+ }
+ break;
+ case FormDataElement::kDataPipe:
+ result.type = Element::kTypeDataPipe;
+ network::mojom::blink::DataPipeGetterPtr data_pipe_getter;
+ (*element.data_pipe_getter_->GetPtr())
+ ->Clone(mojo::MakeRequest(&data_pipe_getter));
+ result.data_pipe_getter = data_pipe_getter.PassInterface().PassHandle();
+ break;
+ }
+
+ return true;
+}
+
+void WebHTTPBody::AppendData(const WebData& data) {
+ EnsureMutable();
+ // FIXME: FormDataElement::m_data should be a SharedBuffer<char>. Then we
+ // could avoid this buffer copy.
+ data.ForEachSegment(
+ [this](const char* segment, size_t segment_size, size_t segment_offset) {
+ private_->AppendData(segment, segment_size);
+ return true;
+ });
+}
+
+void WebHTTPBody::AppendFile(const WebString& file_path) {
+ EnsureMutable();
+ private_->AppendFile(file_path);
+}
+
+void WebHTTPBody::AppendFileRange(const WebString& file_path,
+ long long file_start,
+ long long file_length,
+ double modification_time) {
+ EnsureMutable();
+ private_->AppendFileRange(file_path, file_start, file_length,
+ modification_time);
+}
+
+void WebHTTPBody::AppendBlob(const WebString& uuid) {
+ EnsureMutable();
+ private_->AppendBlob(uuid, nullptr);
+}
+
+void WebHTTPBody::AppendDataPipe(mojo::ScopedMessagePipeHandle message_pipe) {
+ EnsureMutable();
+
+ // Convert the raw message pipe to network::mojom::blink::DataPipeGetter.
+ network::mojom::blink::DataPipeGetterPtr data_pipe_getter;
+ data_pipe_getter.Bind(network::mojom::blink::DataPipeGetterPtrInfo(
+ std::move(message_pipe), 0u));
+
+ auto wrapped =
+ base::MakeRefCounted<WrappedDataPipeGetter>(std::move(data_pipe_getter));
+ private_->AppendDataPipe(std::move(wrapped));
+}
+
+long long WebHTTPBody::Identifier() const {
+ DCHECK(!IsNull());
+ return private_->Identifier();
+}
+
+void WebHTTPBody::SetIdentifier(long long identifier) {
+ EnsureMutable();
+ return private_->SetIdentifier(identifier);
+}
+
+void WebHTTPBody::SetUniqueBoundary() {
+ EnsureMutable();
+ private_->SetBoundary(FormDataEncoder::GenerateUniqueBoundaryString());
+}
+
+bool WebHTTPBody::ContainsPasswordData() const {
+ return private_->ContainsPasswordData();
+}
+
+void WebHTTPBody::SetContainsPasswordData(bool contains_password_data) {
+ private_->SetContainsPasswordData(contains_password_data);
+}
+
+WebHTTPBody::WebHTTPBody(scoped_refptr<EncodedFormData> data)
+ : private_(std::move(data)) {}
+
+WebHTTPBody& WebHTTPBody::operator=(scoped_refptr<EncodedFormData> data) {
+ private_ = std::move(data);
+ return *this;
+}
+
+WebHTTPBody::operator scoped_refptr<EncodedFormData>() const {
+ return private_.Get();
+}
+
+void WebHTTPBody::EnsureMutable() {
+ DCHECK(!IsNull());
+ if (!private_->HasOneRef())
+ private_ = private_->Copy();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_http_header_map.cc b/chromium/third_party/blink/renderer/platform/exported/web_http_header_map.cc
new file mode 100644
index 00000000000..c869bb3eb63
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_http_header_map.cc
@@ -0,0 +1,76 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_http_header_map.h"
+
+#include <memory>
+#include <string>
+#include "net/http/http_request_headers.h"
+#include "net/http/http_response_headers.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/renderer/platform/network/http_header_map.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+
+namespace blink {
+
+class WebHTTPHeaderMap::WebHTTPHeaderMapImpl {
+ public:
+ explicit WebHTTPHeaderMapImpl(const HTTPHeaderMap& map) : map_(map){};
+
+ explicit WebHTTPHeaderMapImpl(const net::HttpRequestHeaders* headers) {
+ for (net::HttpRequestHeaders::Iterator it(*headers); it.GetNext();) {
+ map_.Add(
+ WTF::AtomicString::FromUTF8(it.name().c_str(), it.name().length()),
+ WTF::AtomicString::FromUTF8(it.value().c_str(), it.value().length()));
+ }
+ };
+
+ explicit WebHTTPHeaderMapImpl(const net::HttpResponseHeaders* headers) {
+ size_t iter = 0;
+ std::string name;
+ std::string value;
+
+ while (headers->EnumerateHeaderLines(&iter, &name, &value)) {
+ WTF::AtomicString atomic_name =
+ WTF::AtomicString::FromUTF8(name.c_str(), name.length());
+ WTF::AtomicString atomic_value =
+ WTF::AtomicString::FromUTF8(value.c_str(), value.length());
+
+ if (map_.Contains(atomic_name))
+ map_.Set(atomic_name, map_.Get(atomic_name) + "," + atomic_value);
+ else
+ map_.Add(atomic_name, atomic_value);
+ }
+ };
+
+ const HTTPHeaderMap& map() const { return map_; };
+
+ private:
+ HTTPHeaderMap map_;
+};
+
+WebHTTPHeaderMap::~WebHTTPHeaderMap() = default;
+;
+
+WebHTTPHeaderMap::WebHTTPHeaderMap(const HTTPHeaderMap& map) {
+ implementation_ = std::make_unique<WebHTTPHeaderMapImpl>(map);
+}
+
+WebHTTPHeaderMap::WebHTTPHeaderMap(const net::HttpResponseHeaders* headers) {
+ implementation_ = std::make_unique<WebHTTPHeaderMapImpl>(headers);
+}
+
+WebHTTPHeaderMap::WebHTTPHeaderMap(const net::HttpRequestHeaders* headers) {
+ implementation_ = std::make_unique<WebHTTPHeaderMapImpl>(headers);
+}
+
+const HTTPHeaderMap& WebHTTPHeaderMap::GetHTTPHeaderMap() const {
+ return implementation_->map();
+}
+
+WebString WebHTTPHeaderMap::Get(const WebString& name) const {
+ return implementation_->map().Get(name).GetString();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_http_load_info.cc b/chromium/third_party/blink/renderer/platform/exported/web_http_load_info.cc
new file mode 100644
index 00000000000..5814fe5002c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_http_load_info.cc
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2010 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 "third_party/blink/public/platform/web_http_load_info.h"
+
+#include "third_party/blink/public/platform/web_http_header_visitor.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_load_info.h"
+
+namespace blink {
+
+void WebHTTPLoadInfo::Initialize() {
+ private_ = base::AdoptRef(new ResourceLoadInfo());
+}
+
+void WebHTTPLoadInfo::Reset() {
+ private_.Reset();
+}
+
+void WebHTTPLoadInfo::Assign(const WebHTTPLoadInfo& r) {
+ private_ = r.private_;
+}
+
+WebHTTPLoadInfo::WebHTTPLoadInfo(scoped_refptr<ResourceLoadInfo> value)
+ : private_(std::move(value)) {}
+
+WebHTTPLoadInfo::operator scoped_refptr<ResourceLoadInfo>() const {
+ return private_.Get();
+}
+
+int WebHTTPLoadInfo::HttpStatusCode() const {
+ DCHECK(!private_.IsNull());
+ return private_->http_status_code;
+}
+
+void WebHTTPLoadInfo::SetHTTPStatusCode(int status_code) {
+ DCHECK(!private_.IsNull());
+ private_->http_status_code = status_code;
+}
+
+WebString WebHTTPLoadInfo::HttpStatusText() const {
+ DCHECK(!private_.IsNull());
+ return private_->http_status_text;
+}
+
+void WebHTTPLoadInfo::SetHTTPStatusText(const WebString& status_text) {
+ DCHECK(!private_.IsNull());
+ private_->http_status_text = status_text;
+}
+
+static void AddHeader(HTTPHeaderMap* map,
+ const WebString& name,
+ const WebString& value) {
+ HTTPHeaderMap::AddResult result = map->Add(name, value);
+ // It is important that values are separated by '\n', not comma, otherwise
+ // Set-Cookie header is not parseable.
+ if (!result.is_new_entry)
+ result.stored_value->value =
+ result.stored_value->value + "\n" + String(value);
+}
+
+void WebHTTPLoadInfo::AddRequestHeader(const WebString& name,
+ const WebString& value) {
+ DCHECK(!private_.IsNull());
+ AddHeader(&private_->request_headers, name, value);
+}
+
+void WebHTTPLoadInfo::AddResponseHeader(const WebString& name,
+ const WebString& value) {
+ DCHECK(!private_.IsNull());
+ AddHeader(&private_->response_headers, name, value);
+}
+
+WebString WebHTTPLoadInfo::RequestHeadersText() const {
+ DCHECK(!private_.IsNull());
+ return private_->request_headers_text;
+}
+
+void WebHTTPLoadInfo::SetRequestHeadersText(const WebString& headers_text) {
+ DCHECK(!private_.IsNull());
+ private_->request_headers_text = headers_text;
+}
+
+WebString WebHTTPLoadInfo::ResponseHeadersText() const {
+ DCHECK(!private_.IsNull());
+ return private_->response_headers_text;
+}
+
+void WebHTTPLoadInfo::SetResponseHeadersText(const WebString& headers_text) {
+ DCHECK(!private_.IsNull());
+ private_->response_headers_text = headers_text;
+}
+
+WebString WebHTTPLoadInfo::NpnNegotiatedProtocol() const {
+ DCHECK(!private_.IsNull());
+ return private_->npn_negotiated_protocol;
+}
+
+void WebHTTPLoadInfo::SetNPNNegotiatedProtocol(
+ const WebString& npn_negotiated_protocol) {
+ DCHECK(!private_.IsNull());
+ private_->npn_negotiated_protocol = npn_negotiated_protocol;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_image.cc b/chromium/third_party/blink/renderer/platform/exported/web_image.cc
new file mode 100644
index 00000000000..628998b47c4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_image.cc
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/public/platform/web_image.h"
+
+#include <algorithm>
+#include <memory>
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/public/platform/web_data.h"
+#include "third_party/blink/public/platform/web_size.h"
+#include "third_party/blink/renderer/platform/drag_image.h"
+#include "third_party/blink/renderer/platform/graphics/bitmap_image.h"
+#include "third_party/blink/renderer/platform/graphics/image.h"
+#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "third_party/skia/include/core/SkImage.h"
+
+namespace blink {
+
+WebImage WebImage::FromData(const WebData& data, const WebSize& desired_size) {
+ std::unique_ptr<ImageDecoder> decoder(ImageDecoder::Create(
+ data, true, ImageDecoder::kAlphaPremultiplied, ColorBehavior::Ignore()));
+ if (!decoder || !decoder->IsSizeAvailable())
+ return WebImage();
+
+ // Frames are arranged by decreasing size, then decreasing bit depth.
+ // Pick the frame closest to |desiredSize|'s area without being smaller,
+ // which has the highest bit depth.
+ const size_t frame_count = decoder->FrameCount();
+ size_t index = 0; // Default to first frame if none are large enough.
+ int frame_area_at_index = 0;
+ for (size_t i = 0; i < frame_count; ++i) {
+ const IntSize frame_size = decoder->FrameSizeAtIndex(i);
+ if (WebSize(frame_size) == desired_size) {
+ index = i;
+ break; // Perfect match.
+ }
+
+ const int frame_area = frame_size.Width() * frame_size.Height();
+ if (frame_area < (desired_size.width * desired_size.height))
+ break; // No more frames that are large enough.
+
+ if (!i || (frame_area < frame_area_at_index)) {
+ index = i; // Closer to desired area than previous best match.
+ frame_area_at_index = frame_area;
+ }
+ }
+
+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(index);
+ return (frame && !decoder->Failed()) ? WebImage(frame->Bitmap()) : WebImage();
+}
+
+WebVector<WebImage> WebImage::FramesFromData(const WebData& data) {
+ // This is to protect from malicious images. It should be big enough that it's
+ // never hit in practice.
+ const size_t kMaxFrameCount = 8;
+
+ std::unique_ptr<ImageDecoder> decoder(ImageDecoder::Create(
+ data, true, ImageDecoder::kAlphaPremultiplied, ColorBehavior::Ignore()));
+ if (!decoder || !decoder->IsSizeAvailable())
+ return WebVector<WebImage>();
+
+ // Frames are arranged by decreasing size, then decreasing bit depth.
+ // Keep the first frame at every size, has the highest bit depth.
+ const size_t frame_count = decoder->FrameCount();
+ IntSize last_size;
+
+ Vector<WebImage> frames;
+ for (size_t i = 0; i < std::min(frame_count, kMaxFrameCount); ++i) {
+ const IntSize frame_size = decoder->FrameSizeAtIndex(i);
+ if (frame_size == last_size)
+ continue;
+ last_size = frame_size;
+
+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(i);
+ if (!frame)
+ continue;
+
+ SkBitmap bitmap = frame->Bitmap();
+ if (!bitmap.isNull() && frame->GetStatus() == ImageFrame::kFrameComplete)
+ frames.push_back(WebImage(bitmap));
+ }
+
+ return frames;
+}
+
+WebVector<WebImage::AnimationFrame> WebImage::AnimationFromData(
+ const WebData& data) {
+ std::unique_ptr<ImageDecoder> decoder(ImageDecoder::Create(
+ data, true, ImageDecoder::kAlphaPremultiplied, ColorBehavior::Ignore()));
+ if (!decoder || !decoder->IsSizeAvailable() || decoder->FrameCount() == 0)
+ return WebVector<WebImage::AnimationFrame>();
+
+ const size_t frame_count = decoder->FrameCount();
+ IntSize last_size = decoder->FrameSizeAtIndex(0);
+
+ Vector<WebImage::AnimationFrame> frames;
+ frames.ReserveCapacity(frame_count);
+ for (size_t i = 0; i < frame_count; ++i) {
+ // If frame size changes, this is most likely not an animation and is
+ // instead an image with multiple versions at different resolutions. If
+ // that's the case, return only the first frame (or no frames if we failed
+ // decoding the first one).
+ if (last_size != decoder->FrameSizeAtIndex(i)) {
+ frames.resize(frames.IsEmpty() ? 0 : 1);
+ return frames;
+ }
+ last_size = decoder->FrameSizeAtIndex(i);
+
+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(i);
+
+ SkBitmap bitmap = frame->Bitmap();
+ if (bitmap.isNull() || frame->GetStatus() != ImageFrame::kFrameComplete)
+ continue;
+
+ // Make the bitmap a deep copy, otherwise the next loop iteration will
+ // replace the contents of the previous frame. DecodeFrameBufferAtIndex
+ // reuses the same underlying pixel buffer.
+ bitmap.setImmutable();
+
+ AnimationFrame output;
+ output.bitmap = bitmap;
+ output.duration = frame->Duration();
+ frames.push_back(output);
+ }
+
+ return frames;
+}
+
+void WebImage::Reset() {
+ bitmap_.reset();
+}
+
+void WebImage::Assign(const WebImage& image) {
+ bitmap_ = image.bitmap_;
+}
+
+bool WebImage::IsNull() const {
+ return bitmap_.isNull();
+}
+
+WebSize WebImage::Size() const {
+ return WebSize(bitmap_.width(), bitmap_.height());
+}
+
+WebImage::WebImage(scoped_refptr<Image> image,
+ RespectImageOrientationEnum should_respect_image_orientation) {
+ if (!image)
+ return;
+
+ PaintImage paint_image = image->PaintImageForCurrentFrame();
+ if (!paint_image)
+ return;
+
+ if (should_respect_image_orientation == kRespectImageOrientation &&
+ image->IsBitmapImage()) {
+ ImageOrientation orientation = ToBitmapImage(image.get())->CurrentFrameOrientation();
+ paint_image = DragImage::ResizeAndOrientImage(paint_image, orientation);
+ if (!paint_image)
+ return;
+ }
+
+ if (sk_sp<SkImage> sk_image = paint_image.GetSkImage())
+ sk_image->asLegacyBitmap(&bitmap_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_image_generator.cc b/chromium/third_party/blink/renderer/platform/exported/web_image_generator.cc
new file mode 100644
index 00000000000..d1668dbcb9f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_image_generator.cc
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 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 "third_party/blink/public/platform/web_image_generator.h"
+
+#include <utility>
+#include "third_party/blink/renderer/platform/graphics/decoding_image_generator.h"
+
+namespace blink {
+
+std::unique_ptr<SkImageGenerator> WebImageGenerator::CreateAsSkImageGenerator(
+ sk_sp<SkData> data) {
+ return DecodingImageGenerator::CreateAsSkImageGenerator(std::move(data));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_image_test.cc b/chromium/third_party/blink/renderer/platform/exported/web_image_test.cc
new file mode 100644
index 00000000000..1c5a40e70d0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_image_test.cc
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2012 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 "third_party/blink/public/platform/web_image.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/web_data.h"
+#include "third_party/blink/public/platform/web_size.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
+
+namespace blink {
+
+static scoped_refptr<SharedBuffer> ReadFile(const char* file_name) {
+ String file_path = test::CoreTestDataPath(file_name);
+
+ return test::ReadFromFile(file_path);
+}
+
+TEST(WebImageTest, PNGImage) {
+ scoped_refptr<SharedBuffer> data = ReadFile("white-1x1.png");
+ ASSERT_TRUE(data.get());
+
+ WebImage image = WebImage::FromData(WebData(data), WebSize());
+ EXPECT_TRUE(image.Size() == WebSize(1, 1));
+ EXPECT_EQ(SkColorSetARGB(255, 255, 255, 255),
+ image.GetSkBitmap().getColor(0, 0));
+}
+
+TEST(WebImageTest, ICOImage) {
+ scoped_refptr<SharedBuffer> data = ReadFile("black-and-white.ico");
+ ASSERT_TRUE(data.get());
+
+ WebVector<WebImage> images = WebImage::FramesFromData(WebData(data));
+ ASSERT_EQ(2u, images.size());
+ EXPECT_TRUE(images[0].Size() == WebSize(2, 2));
+ EXPECT_TRUE(images[1].Size() == WebSize(1, 1));
+ EXPECT_EQ(SkColorSetARGB(255, 255, 255, 255),
+ images[0].GetSkBitmap().getColor(0, 0));
+ EXPECT_EQ(SkColorSetARGB(255, 0, 0, 0),
+ images[1].GetSkBitmap().getColor(0, 0));
+}
+
+TEST(WebImageTest, ICOValidHeaderMissingBitmap) {
+ scoped_refptr<SharedBuffer> data =
+ ReadFile("valid_header_missing_bitmap.ico");
+ ASSERT_TRUE(data.get());
+
+ WebVector<WebImage> images = WebImage::FramesFromData(WebData(data));
+ ASSERT_TRUE(images.IsEmpty());
+}
+
+TEST(WebImageTest, BadImage) {
+ const char kBadImage[] = "hello world";
+ WebVector<WebImage> images = WebImage::FramesFromData(WebData(kBadImage));
+ ASSERT_EQ(0u, images.size());
+
+ WebImage image = WebImage::FromData(WebData(kBadImage), WebSize());
+ EXPECT_TRUE(image.GetSkBitmap().empty());
+ EXPECT_TRUE(image.GetSkBitmap().isNull());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_input_event.cc b/chromium/third_party/blink/renderer/platform/exported/web_input_event.cc
new file mode 100644
index 00000000000..19ccf486029
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_input_event.cc
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/public/platform/web_input_event.h"
+
+#include <ctype.h>
+#include "third_party/blink/public/platform/web_gesture_event.h"
+#include "third_party/blink/public/platform/web_keyboard_event.h"
+#include "third_party/blink/public/platform/web_mouse_wheel_event.h"
+#include "third_party/blink/public/platform/web_touch_event.h"
+#include "third_party/blink/renderer/platform/keyboard_codes.h"
+#include "third_party/blink/renderer/platform/wtf/ascii_ctype.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+namespace blink {
+
+struct SameSizeAsWebInputEvent {
+ int input_data[8];
+};
+
+struct SameSizeAsWebKeyboardEvent : public SameSizeAsWebInputEvent {
+ int keyboard_data[9];
+};
+
+struct SameSizeAsWebMouseEvent : public SameSizeAsWebInputEvent {
+ int mouse_data[16];
+};
+
+struct SameSizeAsWebMouseWheelEvent : public SameSizeAsWebMouseEvent {
+ int mousewheel_data[12];
+};
+
+struct SameSizeAsWebGestureEvent : public SameSizeAsWebInputEvent {
+ int gesture_data[16];
+};
+
+struct SameSizeAsWebTouchEvent : public SameSizeAsWebInputEvent {
+ WebTouchPoint touch_points[WebTouchEvent::kTouchesLengthCap];
+ int touch_data[4];
+};
+
+static_assert(sizeof(WebInputEvent) == sizeof(SameSizeAsWebInputEvent),
+ "WebInputEvent should not have gaps");
+static_assert(sizeof(WebKeyboardEvent) == sizeof(SameSizeAsWebKeyboardEvent),
+ "WebKeyboardEvent should not have gaps");
+static_assert(sizeof(WebMouseEvent) == sizeof(SameSizeAsWebMouseEvent),
+ "WebMouseEvent should not have gaps");
+static_assert(sizeof(WebMouseWheelEvent) ==
+ sizeof(SameSizeAsWebMouseWheelEvent),
+ "WebMouseWheelEvent should not have gaps");
+static_assert(sizeof(WebGestureEvent) == sizeof(SameSizeAsWebGestureEvent),
+ "WebGestureEvent should not have gaps");
+static_assert(sizeof(WebTouchEvent) == sizeof(SameSizeAsWebTouchEvent),
+ "WebTouchEvent should not have gaps");
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_media_constraints.cc b/chromium/third_party/blink/renderer/platform/exported/web_media_constraints.cc
new file mode 100644
index 00000000000..7de9b8cd1b0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_media_constraints.cc
@@ -0,0 +1,558 @@
+/*
+ * Copyright (C) 2012 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 "third_party/blink/public/platform/web_media_constraints.h"
+
+#include <math.h>
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
+
+namespace blink {
+
+namespace {
+
+template <typename T>
+void MaybeEmitNamedValue(StringBuilder& builder,
+ bool emit,
+ const char* name,
+ T value) {
+ if (!emit)
+ return;
+ if (builder.length() > 1)
+ builder.Append(", ");
+ builder.Append(name);
+ builder.Append(": ");
+ builder.AppendNumber(value);
+}
+
+void MaybeEmitNamedBoolean(StringBuilder& builder,
+ bool emit,
+ const char* name,
+ bool value) {
+ if (!emit)
+ return;
+ if (builder.length() > 1)
+ builder.Append(", ");
+ builder.Append(name);
+ builder.Append(": ");
+ if (value)
+ builder.Append("true");
+ else
+ builder.Append("false");
+}
+
+} // namespace
+
+class WebMediaConstraintsPrivate final
+ : public ThreadSafeRefCounted<WebMediaConstraintsPrivate> {
+ public:
+ static scoped_refptr<WebMediaConstraintsPrivate> Create();
+ static scoped_refptr<WebMediaConstraintsPrivate> Create(
+ const WebMediaTrackConstraintSet& basic,
+ const WebVector<WebMediaTrackConstraintSet>& advanced);
+
+ bool IsEmpty() const;
+ const WebMediaTrackConstraintSet& Basic() const;
+ const WebVector<WebMediaTrackConstraintSet>& Advanced() const;
+ const String ToString() const;
+
+ private:
+ WebMediaConstraintsPrivate(
+ const WebMediaTrackConstraintSet& basic,
+ const WebVector<WebMediaTrackConstraintSet>& advanced);
+
+ WebMediaTrackConstraintSet basic_;
+ WebVector<WebMediaTrackConstraintSet> advanced_;
+};
+
+scoped_refptr<WebMediaConstraintsPrivate> WebMediaConstraintsPrivate::Create() {
+ WebMediaTrackConstraintSet basic;
+ WebVector<WebMediaTrackConstraintSet> advanced;
+ return base::AdoptRef(new WebMediaConstraintsPrivate(basic, advanced));
+}
+
+scoped_refptr<WebMediaConstraintsPrivate> WebMediaConstraintsPrivate::Create(
+ const WebMediaTrackConstraintSet& basic,
+ const WebVector<WebMediaTrackConstraintSet>& advanced) {
+ return base::AdoptRef(new WebMediaConstraintsPrivate(basic, advanced));
+}
+
+WebMediaConstraintsPrivate::WebMediaConstraintsPrivate(
+ const WebMediaTrackConstraintSet& basic,
+ const WebVector<WebMediaTrackConstraintSet>& advanced)
+ : basic_(basic), advanced_(advanced) {}
+
+bool WebMediaConstraintsPrivate::IsEmpty() const {
+ // TODO(hta): When generating advanced constraints, make sure no empty
+ // elements can be added to the m_advanced vector.
+ return basic_.IsEmpty() && advanced_.empty();
+}
+
+const WebMediaTrackConstraintSet& WebMediaConstraintsPrivate::Basic() const {
+ return basic_;
+}
+
+const WebVector<WebMediaTrackConstraintSet>&
+WebMediaConstraintsPrivate::Advanced() const {
+ return advanced_;
+}
+
+const String WebMediaConstraintsPrivate::ToString() const {
+ StringBuilder builder;
+ if (!IsEmpty()) {
+ builder.Append('{');
+ builder.Append(Basic().ToString());
+ if (!Advanced().empty()) {
+ if (builder.length() > 1)
+ builder.Append(", ");
+ builder.Append("advanced: [");
+ bool first = true;
+ for (const auto& constraint_set : Advanced()) {
+ if (!first)
+ builder.Append(", ");
+ builder.Append('{');
+ builder.Append(constraint_set.ToString());
+ builder.Append('}');
+ first = false;
+ }
+ builder.Append(']');
+ }
+ builder.Append('}');
+ }
+ return builder.ToString();
+}
+
+// *Constraints
+
+BaseConstraint::BaseConstraint(const char* name) : name_(name) {}
+
+BaseConstraint::~BaseConstraint() = default;
+
+LongConstraint::LongConstraint(const char* name)
+ : BaseConstraint(name),
+ min_(),
+ max_(),
+ exact_(),
+ ideal_(),
+ has_min_(false),
+ has_max_(false),
+ has_exact_(false),
+ has_ideal_(false) {}
+
+bool LongConstraint::Matches(long value) const {
+ if (has_min_ && value < min_) {
+ return false;
+ }
+ if (has_max_ && value > max_) {
+ return false;
+ }
+ if (has_exact_ && value != exact_) {
+ return false;
+ }
+ return true;
+}
+
+bool LongConstraint::IsEmpty() const {
+ return !has_min_ && !has_max_ && !has_exact_ && !has_ideal_;
+}
+
+bool LongConstraint::HasMandatory() const {
+ return has_min_ || has_max_ || has_exact_;
+}
+
+WebString LongConstraint::ToString() const {
+ StringBuilder builder;
+ builder.Append('{');
+ MaybeEmitNamedValue(builder, has_min_, "min", min_);
+ MaybeEmitNamedValue(builder, has_max_, "max", max_);
+ MaybeEmitNamedValue(builder, has_exact_, "exact", exact_);
+ MaybeEmitNamedValue(builder, has_ideal_, "ideal", ideal_);
+ builder.Append('}');
+ return builder.ToString();
+}
+
+const double DoubleConstraint::kConstraintEpsilon = 0.00001;
+
+DoubleConstraint::DoubleConstraint(const char* name)
+ : BaseConstraint(name),
+ min_(),
+ max_(),
+ exact_(),
+ ideal_(),
+ has_min_(false),
+ has_max_(false),
+ has_exact_(false),
+ has_ideal_(false) {}
+
+bool DoubleConstraint::Matches(double value) const {
+ if (has_min_ && value < min_ - kConstraintEpsilon) {
+ return false;
+ }
+ if (has_max_ && value > max_ + kConstraintEpsilon) {
+ return false;
+ }
+ if (has_exact_ &&
+ fabs(static_cast<double>(value) - exact_) > kConstraintEpsilon) {
+ return false;
+ }
+ return true;
+}
+
+bool DoubleConstraint::IsEmpty() const {
+ return !has_min_ && !has_max_ && !has_exact_ && !has_ideal_;
+}
+
+bool DoubleConstraint::HasMandatory() const {
+ return has_min_ || has_max_ || has_exact_;
+}
+
+WebString DoubleConstraint::ToString() const {
+ StringBuilder builder;
+ builder.Append('{');
+ MaybeEmitNamedValue(builder, has_min_, "min", min_);
+ MaybeEmitNamedValue(builder, has_max_, "max", max_);
+ MaybeEmitNamedValue(builder, has_exact_, "exact", exact_);
+ MaybeEmitNamedValue(builder, has_ideal_, "ideal", ideal_);
+ builder.Append('}');
+ return builder.ToString();
+}
+
+StringConstraint::StringConstraint(const char* name)
+ : BaseConstraint(name), exact_(), ideal_() {}
+
+bool StringConstraint::Matches(WebString value) const {
+ if (exact_.empty()) {
+ return true;
+ }
+ for (const auto& choice : exact_) {
+ if (value == choice) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool StringConstraint::IsEmpty() const {
+ return exact_.empty() && ideal_.empty();
+}
+
+bool StringConstraint::HasMandatory() const {
+ return !exact_.empty();
+}
+
+const WebVector<WebString>& StringConstraint::Exact() const {
+ return exact_;
+}
+
+const WebVector<WebString>& StringConstraint::Ideal() const {
+ return ideal_;
+}
+
+WebString StringConstraint::ToString() const {
+ StringBuilder builder;
+ builder.Append('{');
+ if (!ideal_.empty()) {
+ builder.Append("ideal: [");
+ bool first = true;
+ for (const auto& iter : ideal_) {
+ if (!first)
+ builder.Append(", ");
+ builder.Append('"');
+ builder.Append(iter);
+ builder.Append('"');
+ first = false;
+ }
+ builder.Append(']');
+ }
+ if (!exact_.empty()) {
+ if (builder.length() > 1)
+ builder.Append(", ");
+ builder.Append("exact: [");
+ bool first = true;
+ for (const auto& iter : exact_) {
+ if (!first)
+ builder.Append(", ");
+ builder.Append('"');
+ builder.Append(iter);
+ builder.Append('"');
+ }
+ builder.Append(']');
+ }
+ builder.Append('}');
+ return builder.ToString();
+}
+
+BooleanConstraint::BooleanConstraint(const char* name)
+ : BaseConstraint(name),
+ ideal_(false),
+ exact_(false),
+ has_ideal_(false),
+ has_exact_(false) {}
+
+bool BooleanConstraint::Matches(bool value) const {
+ if (has_exact_ && static_cast<bool>(exact_) != value) {
+ return false;
+ }
+ return true;
+}
+
+bool BooleanConstraint::IsEmpty() const {
+ return !has_ideal_ && !has_exact_;
+}
+
+bool BooleanConstraint::HasMandatory() const {
+ return has_exact_;
+}
+
+WebString BooleanConstraint::ToString() const {
+ StringBuilder builder;
+ builder.Append('{');
+ MaybeEmitNamedBoolean(builder, has_exact_, "exact", Exact());
+ MaybeEmitNamedBoolean(builder, has_ideal_, "ideal", Ideal());
+ builder.Append('}');
+ return builder.ToString();
+}
+
+WebMediaTrackConstraintSet::WebMediaTrackConstraintSet()
+ : width("width"),
+ height("height"),
+ aspect_ratio("aspectRatio"),
+ frame_rate("frameRate"),
+ facing_mode("facingMode"),
+ volume("volume"),
+ sample_rate("sampleRate"),
+ sample_size("sampleSize"),
+ echo_cancellation("echoCancellation"),
+ latency("latency"),
+ channel_count("channelCount"),
+ device_id("deviceId"),
+ disable_local_echo("disableLocalEcho"),
+ group_id("groupId"),
+ video_kind("videoKind"),
+ depth_near("depthNear"),
+ depth_far("depthFar"),
+ focal_length_x("focalLengthX"),
+ focal_length_y("focalLengthY"),
+ media_stream_source("mediaStreamSource"),
+ render_to_associated_sink("chromeRenderToAssociatedSink"),
+ hotword_enabled("hotwordEnabled"),
+ goog_echo_cancellation("googEchoCancellation"),
+ goog_experimental_echo_cancellation("googExperimentalEchoCancellation"),
+ goog_auto_gain_control("googAutoGainControl"),
+ goog_experimental_auto_gain_control("googExperimentalAutoGainControl"),
+ goog_noise_suppression("googNoiseSuppression"),
+ goog_highpass_filter("googHighpassFilter"),
+ goog_typing_noise_detection("googTypingNoiseDetection"),
+ goog_experimental_noise_suppression("googExperimentalNoiseSuppression"),
+ goog_beamforming("googBeamforming"),
+ goog_array_geometry("googArrayGeometry"),
+ goog_audio_mirroring("googAudioMirroring"),
+ goog_da_echo_cancellation("googDAEchoCancellation"),
+ goog_noise_reduction("googNoiseReduction"),
+ offer_to_receive_audio("offerToReceiveAudio"),
+ offer_to_receive_video("offerToReceiveVideo"),
+ voice_activity_detection("voiceActivityDetection"),
+ ice_restart("iceRestart"),
+ goog_use_rtp_mux("googUseRtpMux"),
+ enable_dtls_srtp("enableDtlsSrtp"),
+ enable_rtp_data_channels("enableRtpDataChannels"),
+ enable_dscp("enableDscp"),
+ enable_i_pv6("enableIPv6"),
+ goog_enable_video_suspend_below_min_bitrate(
+ "googEnableVideoSuspendBelowMinBitrate"),
+ goog_num_unsignalled_recv_streams("googNumUnsignalledRecvStreams"),
+ goog_combined_audio_video_bwe("googCombinedAudioVideoBwe"),
+ goog_screencast_min_bitrate("googScreencastMinBitrate"),
+ goog_cpu_overuse_detection("googCpuOveruseDetection"),
+ goog_cpu_underuse_threshold("googCpuUnderuseThreshold"),
+ goog_cpu_overuse_threshold("googCpuOveruseThreshold"),
+ goog_cpu_underuse_encode_rsd_threshold(
+ "googCpuUnderuseEncodeRsdThreshold"),
+ goog_cpu_overuse_encode_rsd_threshold("googCpuOveruseEncodeRsdThreshold"),
+ goog_cpu_overuse_encode_usage("googCpuOveruseEncodeUsage"),
+ goog_high_start_bitrate("googHighStartBitrate"),
+ goog_payload_padding("googPayloadPadding"),
+ goog_latency_ms("latencyMs"),
+ goog_power_line_frequency("googPowerLineFrequency") {}
+
+std::vector<const BaseConstraint*> WebMediaTrackConstraintSet::AllConstraints()
+ const {
+ const BaseConstraint* temp[] = {&width,
+ &height,
+ &aspect_ratio,
+ &frame_rate,
+ &facing_mode,
+ &volume,
+ &sample_rate,
+ &sample_size,
+ &echo_cancellation,
+ &latency,
+ &channel_count,
+ &device_id,
+ &group_id,
+ &video_kind,
+ &depth_near,
+ &depth_far,
+ &focal_length_x,
+ &focal_length_y,
+ &media_stream_source,
+ &disable_local_echo,
+ &render_to_associated_sink,
+ &hotword_enabled,
+ &goog_echo_cancellation,
+ &goog_experimental_echo_cancellation,
+ &goog_auto_gain_control,
+ &goog_experimental_auto_gain_control,
+ &goog_noise_suppression,
+ &goog_highpass_filter,
+ &goog_typing_noise_detection,
+ &goog_experimental_noise_suppression,
+ &goog_beamforming,
+ &goog_array_geometry,
+ &goog_audio_mirroring,
+ &goog_da_echo_cancellation,
+ &goog_noise_reduction,
+ &offer_to_receive_audio,
+ &offer_to_receive_video,
+ &voice_activity_detection,
+ &ice_restart,
+ &goog_use_rtp_mux,
+ &enable_dtls_srtp,
+ &enable_rtp_data_channels,
+ &enable_dscp,
+ &enable_i_pv6,
+ &goog_enable_video_suspend_below_min_bitrate,
+ &goog_num_unsignalled_recv_streams,
+ &goog_combined_audio_video_bwe,
+ &goog_screencast_min_bitrate,
+ &goog_cpu_overuse_detection,
+ &goog_cpu_underuse_threshold,
+ &goog_cpu_overuse_threshold,
+ &goog_cpu_underuse_encode_rsd_threshold,
+ &goog_cpu_overuse_encode_rsd_threshold,
+ &goog_cpu_overuse_encode_usage,
+ &goog_high_start_bitrate,
+ &goog_payload_padding,
+ &goog_latency_ms,
+ &goog_power_line_frequency};
+ const int element_count = sizeof(temp) / sizeof(temp[0]);
+ return std::vector<const BaseConstraint*>(&temp[0], &temp[element_count]);
+}
+
+bool WebMediaTrackConstraintSet::IsEmpty() const {
+ for (const auto& constraint : AllConstraints()) {
+ if (!constraint->IsEmpty())
+ return false;
+ }
+ return true;
+}
+
+bool WebMediaTrackConstraintSet::HasMandatoryOutsideSet(
+ const std::vector<std::string>& good_names,
+ std::string& found_name) const {
+ for (const auto& constraint : AllConstraints()) {
+ if (constraint->HasMandatory()) {
+ if (std::find(good_names.begin(), good_names.end(),
+ constraint->GetName()) == good_names.end()) {
+ found_name = constraint->GetName();
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool WebMediaTrackConstraintSet::HasMandatory() const {
+ std::string dummy_string;
+ return HasMandatoryOutsideSet(std::vector<std::string>(), dummy_string);
+}
+
+WebString WebMediaTrackConstraintSet::ToString() const {
+ StringBuilder builder;
+ bool first = true;
+ for (const auto& constraint : AllConstraints()) {
+ if (!constraint->IsEmpty()) {
+ if (!first)
+ builder.Append(", ");
+ builder.Append(constraint->GetName());
+ builder.Append(": ");
+ builder.Append(constraint->ToString());
+ first = false;
+ }
+ }
+ return builder.ToString();
+}
+
+// WebMediaConstraints
+
+void WebMediaConstraints::Assign(const WebMediaConstraints& other) {
+ private_ = other.private_;
+}
+
+void WebMediaConstraints::Reset() {
+ private_.Reset();
+}
+
+bool WebMediaConstraints::IsEmpty() const {
+ return private_.IsNull() || private_->IsEmpty();
+}
+
+void WebMediaConstraints::Initialize() {
+ DCHECK(IsNull());
+ private_ = WebMediaConstraintsPrivate::Create();
+}
+
+void WebMediaConstraints::Initialize(
+ const WebMediaTrackConstraintSet& basic,
+ const WebVector<WebMediaTrackConstraintSet>& advanced) {
+ DCHECK(IsNull());
+ private_ = WebMediaConstraintsPrivate::Create(basic, advanced);
+}
+
+const WebMediaTrackConstraintSet& WebMediaConstraints::Basic() const {
+ DCHECK(!IsNull());
+ return private_->Basic();
+}
+
+const WebVector<WebMediaTrackConstraintSet>& WebMediaConstraints::Advanced()
+ const {
+ DCHECK(!IsNull());
+ return private_->Advanced();
+}
+
+const WebString WebMediaConstraints::ToString() const {
+ if (IsNull())
+ return WebString("");
+ return private_->ToString();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_media_player_client.cc b/chromium/third_party/blink/renderer/platform/exported/web_media_player_client.cc
new file mode 100644
index 00000000000..02edf08708a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_media_player_client.cc
@@ -0,0 +1,12 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_media_player_client.h"
+
+// This WebMediaPlayerClient.cpp, which includes only
+// and WebMediaPlayerClient.h, should be in
+// Source/platform/exported, because WebMediaPlayerClient is not
+// compiled without this cpp.
+// So if we don't have this cpp, we will see unresolved symbol error
+// when constructor/destructor's address is required.
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_media_player_encrypted_media_client.cc b/chromium/third_party/blink/renderer/platform/exported/web_media_player_encrypted_media_client.cc
new file mode 100644
index 00000000000..b918f6e4928
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_media_player_encrypted_media_client.cc
@@ -0,0 +1,12 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_media_player_encrypted_media_client.h"
+
+// This WebMediaPlayerEncryptedMediaClient.cpp, which includes only
+// and WebMediaPlayerEncryptedMediaClient.h, should be in
+// Source/platform/exported, because WebMediaPlayerEncryptedMediaClient is not
+// compiled without this cpp.
+// So if we don't have this cpp, we will see unresolved symbol error
+// when constructor/destructor's address is required.
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_media_player_source.cc b/chromium/third_party/blink/renderer/platform/exported/web_media_player_source.cc
new file mode 100644
index 00000000000..a69588c6ffb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_media_player_source.cc
@@ -0,0 +1,36 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_media_player_source.h"
+
+namespace blink {
+
+WebMediaPlayerSource::WebMediaPlayerSource() = default;
+
+WebMediaPlayerSource::WebMediaPlayerSource(const WebURL& url) : url_(url) {}
+
+WebMediaPlayerSource::WebMediaPlayerSource(const WebMediaStream& media_stream)
+ : media_stream_(media_stream) {}
+
+WebMediaPlayerSource::~WebMediaPlayerSource() {
+ media_stream_.Reset();
+}
+
+bool WebMediaPlayerSource::IsURL() const {
+ return !url_.IsEmpty();
+}
+
+WebURL WebMediaPlayerSource::GetAsURL() const {
+ return url_;
+}
+
+bool WebMediaPlayerSource::IsMediaStream() const {
+ return !media_stream_.IsNull();
+}
+
+WebMediaStream WebMediaPlayerSource::GetAsMediaStream() const {
+ return media_stream_;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_media_recorder_handler.cc b/chromium/third_party/blink/renderer/platform/exported/web_media_recorder_handler.cc
new file mode 100644
index 00000000000..2f0e772bdbb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_media_recorder_handler.cc
@@ -0,0 +1,9 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_media_recorder_handler.h"
+
+// This file just includes WebMediaPlayerClient.h, to make sure
+// MSVC compiler does not fail linking with LNK2019 due to unresolved
+// constructor/destructor and should be in Source/platform/exported.
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_media_stream.cc b/chromium/third_party/blink/renderer/platform/exported/web_media_stream.cc
new file mode 100644
index 00000000000..79491ffcb1a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_media_stream.cc
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2013 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/public/platform/web_media_stream.h"
+
+#include <memory>
+#include "third_party/blink/public/platform/web_media_stream_source.h"
+#include "third_party/blink/public/platform/web_media_stream_track.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/renderer/platform/mediastream/media_stream_component.h"
+#include "third_party/blink/renderer/platform/mediastream/media_stream_descriptor.h"
+#include "third_party/blink/renderer/platform/mediastream/media_stream_source.h"
+#include "third_party/blink/renderer/platform/uuid.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+WebMediaStream::WebMediaStream(MediaStreamDescriptor* media_stream_descriptor)
+ : private_(media_stream_descriptor) {}
+
+void WebMediaStream::Reset() {
+ private_.Reset();
+}
+
+WebString WebMediaStream::Id() const {
+ return private_->Id();
+}
+
+int WebMediaStream::UniqueId() const {
+ return private_->UniqueId();
+}
+
+void WebMediaStream::AudioTracks(
+ WebVector<WebMediaStreamTrack>& web_tracks) const {
+ size_t number_of_tracks = private_->NumberOfAudioComponents();
+ WebVector<WebMediaStreamTrack> result(number_of_tracks);
+ for (size_t i = 0; i < number_of_tracks; ++i)
+ result[i] = private_->AudioComponent(i);
+ web_tracks.Swap(result);
+}
+
+void WebMediaStream::VideoTracks(
+ WebVector<WebMediaStreamTrack>& web_tracks) const {
+ size_t number_of_tracks = private_->NumberOfVideoComponents();
+ WebVector<WebMediaStreamTrack> result(number_of_tracks);
+ for (size_t i = 0; i < number_of_tracks; ++i)
+ result[i] = private_->VideoComponent(i);
+ web_tracks.Swap(result);
+}
+
+WebMediaStreamTrack WebMediaStream::GetAudioTrack(
+ const WebString& track_id) const {
+ size_t number_of_tracks = private_->NumberOfAudioComponents();
+ String id = track_id;
+ for (size_t i = 0; i < number_of_tracks; ++i) {
+ MediaStreamComponent* audio_component = private_->AudioComponent(i);
+ DCHECK(audio_component);
+ if (audio_component->Id() == id)
+ return private_->AudioComponent(i);
+ }
+ return nullptr;
+}
+
+WebMediaStreamTrack WebMediaStream::GetVideoTrack(
+ const WebString& track_id) const {
+ size_t number_of_tracks = private_->NumberOfVideoComponents();
+ String id = track_id;
+ for (size_t i = 0; i < number_of_tracks; ++i) {
+ MediaStreamComponent* video_component = private_->VideoComponent(i);
+ DCHECK(video_component);
+ if (video_component->Id() == id)
+ return private_->VideoComponent(i);
+ }
+ return nullptr;
+}
+
+void WebMediaStream::AddTrack(const WebMediaStreamTrack& track) {
+ DCHECK(!IsNull());
+ private_->AddRemoteTrack(track);
+}
+
+void WebMediaStream::RemoveTrack(const WebMediaStreamTrack& track) {
+ DCHECK(!IsNull());
+ private_->RemoveRemoteTrack(track);
+}
+
+void WebMediaStream::AddObserver(WebMediaStreamObserver* observer) {
+ DCHECK(!IsNull());
+ private_->AddObserver(observer);
+}
+
+void WebMediaStream::RemoveObserver(WebMediaStreamObserver* observer) {
+ DCHECK(!IsNull());
+ private_->RemoveObserver(observer);
+}
+
+WebMediaStream& WebMediaStream::operator=(
+ MediaStreamDescriptor* media_stream_descriptor) {
+ private_ = media_stream_descriptor;
+ return *this;
+}
+
+WebMediaStream::operator MediaStreamDescriptor*() const {
+ return private_.Get();
+}
+
+void WebMediaStream::Initialize(
+ const WebVector<WebMediaStreamTrack>& audio_tracks,
+ const WebVector<WebMediaStreamTrack>& video_tracks) {
+ Initialize(CreateCanonicalUUIDString(), audio_tracks, video_tracks);
+}
+
+void WebMediaStream::Initialize(
+ const WebString& label,
+ const WebVector<WebMediaStreamTrack>& audio_tracks,
+ const WebVector<WebMediaStreamTrack>& video_tracks) {
+ MediaStreamComponentVector audio, video;
+ for (size_t i = 0; i < audio_tracks.size(); ++i) {
+ MediaStreamComponent* component = audio_tracks[i];
+ audio.push_back(component);
+ }
+ for (size_t i = 0; i < video_tracks.size(); ++i) {
+ MediaStreamComponent* component = video_tracks[i];
+ video.push_back(component);
+ }
+ private_ = MediaStreamDescriptor::Create(label, audio, video);
+}
+
+void WebMediaStream::Assign(const WebMediaStream& other) {
+ private_ = other.private_;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_media_stream_source.cc b/chromium/third_party/blink/renderer/platform/exported/web_media_stream_source.cc
new file mode 100644
index 00000000000..514f1de7bed
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_media_stream_source.cc
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/public/platform/web_media_stream_source.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/public/platform/web_audio_destination_consumer.h"
+#include "third_party/blink/public/platform/web_media_constraints.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/renderer/platform/audio/audio_bus.h"
+#include "third_party/blink/renderer/platform/mediastream/media_stream_source.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+namespace {
+
+class MediaStreamSourceExtraDataContainer
+ : public MediaStreamSource::ExtraData {
+ public:
+ MediaStreamSourceExtraDataContainer(
+ std::unique_ptr<WebMediaStreamSource::ExtraData> extra_data)
+ : extra_data_(std::move(extra_data)) {}
+
+ WebMediaStreamSource::ExtraData* GetExtraData() { return extra_data_.get(); }
+
+ private:
+ std::unique_ptr<WebMediaStreamSource::ExtraData> extra_data_;
+};
+
+} // namespace
+
+WebMediaStreamSource WebMediaStreamSource::ExtraData::Owner() {
+ DCHECK(owner_);
+ return WebMediaStreamSource(owner_);
+}
+
+void WebMediaStreamSource::ExtraData::SetOwner(MediaStreamSource* owner) {
+ DCHECK(!owner_);
+ owner_ = owner;
+}
+
+WebMediaStreamSource::WebMediaStreamSource(
+ MediaStreamSource* media_stream_source)
+ : private_(media_stream_source) {}
+
+WebMediaStreamSource& WebMediaStreamSource::operator=(
+ MediaStreamSource* media_stream_source) {
+ private_ = media_stream_source;
+ return *this;
+}
+
+void WebMediaStreamSource::Assign(const WebMediaStreamSource& other) {
+ private_ = other.private_;
+}
+
+void WebMediaStreamSource::Reset() {
+ private_.Reset();
+}
+
+WebMediaStreamSource::operator MediaStreamSource*() const {
+ return private_.Get();
+}
+
+void WebMediaStreamSource::Initialize(const WebString& id,
+ Type type,
+ const WebString& name) {
+ private_ = MediaStreamSource::Create(
+ id, static_cast<MediaStreamSource::StreamType>(type), name, false);
+}
+
+void WebMediaStreamSource::Initialize(const WebString& id,
+ Type type,
+ const WebString& name,
+ bool remote) {
+ private_ = MediaStreamSource::Create(
+ id, static_cast<MediaStreamSource::StreamType>(type), name, remote);
+}
+
+WebString WebMediaStreamSource::Id() const {
+ DCHECK(!private_.IsNull());
+ return private_.Get()->Id();
+}
+
+WebMediaStreamSource::Type WebMediaStreamSource::GetType() const {
+ DCHECK(!private_.IsNull());
+ return static_cast<Type>(private_.Get()->GetType());
+}
+
+WebString WebMediaStreamSource::GetName() const {
+ DCHECK(!private_.IsNull());
+ return private_.Get()->GetName();
+}
+
+bool WebMediaStreamSource::Remote() const {
+ DCHECK(!private_.IsNull());
+ return private_.Get()->Remote();
+}
+
+void WebMediaStreamSource::SetReadyState(ReadyState state) {
+ DCHECK(!private_.IsNull());
+ private_->SetReadyState(static_cast<MediaStreamSource::ReadyState>(state));
+}
+
+WebMediaStreamSource::ReadyState WebMediaStreamSource::GetReadyState() const {
+ DCHECK(!private_.IsNull());
+ return static_cast<ReadyState>(private_->GetReadyState());
+}
+
+WebMediaStreamSource::ExtraData* WebMediaStreamSource::GetExtraData() const {
+ DCHECK(!private_.IsNull());
+ MediaStreamSource::ExtraData* data = private_->GetExtraData();
+ if (!data)
+ return nullptr;
+ return static_cast<MediaStreamSourceExtraDataContainer*>(data)
+ ->GetExtraData();
+}
+
+void WebMediaStreamSource::SetExtraData(ExtraData* extra_data) {
+ DCHECK(!private_.IsNull());
+
+ if (extra_data)
+ extra_data->SetOwner(private_.Get());
+
+ private_->SetExtraData(std::make_unique<MediaStreamSourceExtraDataContainer>(
+ base::WrapUnique(extra_data)));
+}
+
+void WebMediaStreamSource::SetAudioProcessingProperties(bool echo_cancellation,
+ bool auto_gain_control,
+ bool noise_supression) {
+ DCHECK(!private_.IsNull());
+ private_->SetAudioProcessingProperties(echo_cancellation, auto_gain_control,
+ noise_supression);
+}
+
+WebMediaConstraints WebMediaStreamSource::Constraints() {
+ DCHECK(!private_.IsNull());
+ return private_->Constraints();
+}
+
+void WebMediaStreamSource::SetCapabilities(
+ const WebMediaStreamSource::Capabilities& capabilities) {
+ DCHECK(!private_.IsNull());
+ private_->SetCapabilities(capabilities);
+}
+
+bool WebMediaStreamSource::RequiresAudioConsumer() const {
+ DCHECK(!private_.IsNull());
+ return private_->RequiresAudioConsumer();
+}
+
+class ConsumerWrapper final : public AudioDestinationConsumer {
+ USING_FAST_MALLOC(ConsumerWrapper);
+
+ public:
+ static ConsumerWrapper* Create(WebAudioDestinationConsumer* consumer) {
+ return new ConsumerWrapper(consumer);
+ }
+
+ void SetFormat(size_t number_of_channels, float sample_rate) override;
+ void ConsumeAudio(AudioBus*, size_t number_of_frames) override;
+
+ WebAudioDestinationConsumer* Consumer() { return consumer_; }
+
+ private:
+ explicit ConsumerWrapper(WebAudioDestinationConsumer* consumer)
+ : consumer_(consumer) {}
+
+ // m_consumer is not owned by this class.
+ WebAudioDestinationConsumer* consumer_;
+};
+
+void ConsumerWrapper::SetFormat(size_t number_of_channels, float sample_rate) {
+ consumer_->SetFormat(number_of_channels, sample_rate);
+}
+
+void ConsumerWrapper::ConsumeAudio(AudioBus* bus, size_t number_of_frames) {
+ if (!bus)
+ return;
+
+ // Wrap AudioBus.
+ size_t number_of_channels = bus->NumberOfChannels();
+ WebVector<const float*> bus_vector(number_of_channels);
+ for (size_t i = 0; i < number_of_channels; ++i)
+ bus_vector[i] = bus->Channel(i)->Data();
+
+ consumer_->ConsumeAudio(bus_vector, number_of_frames);
+}
+
+void WebMediaStreamSource::AddAudioConsumer(
+ WebAudioDestinationConsumer* consumer) {
+ DCHECK(IsMainThread());
+ DCHECK(!private_.IsNull() && consumer);
+
+ private_->AddAudioConsumer(ConsumerWrapper::Create(consumer));
+}
+
+bool WebMediaStreamSource::RemoveAudioConsumer(
+ WebAudioDestinationConsumer* consumer) {
+ DCHECK(IsMainThread());
+ DCHECK(!private_.IsNull() && consumer);
+
+ const HashSet<AudioDestinationConsumer*>& consumers =
+ private_->AudioConsumers();
+ for (AudioDestinationConsumer* it : consumers) {
+ ConsumerWrapper* wrapper = static_cast<ConsumerWrapper*>(it);
+ if (wrapper->Consumer() == consumer) {
+ private_->RemoveAudioConsumer(wrapper);
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_media_stream_track.cc b/chromium/third_party/blink/renderer/platform/exported/web_media_stream_track.cc
new file mode 100644
index 00000000000..db859bc16fd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_media_stream_track.cc
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2013 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/public/platform/web_media_stream_track.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/public/platform/web_audio_source_provider.h"
+#include "third_party/blink/public/platform/web_media_constraints.h"
+#include "third_party/blink/public/platform/web_media_stream.h"
+#include "third_party/blink/public/platform/web_media_stream_source.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/renderer/platform/mediastream/media_stream_component.h"
+#include "third_party/blink/renderer/platform/mediastream/media_stream_source.h"
+
+namespace blink {
+
+namespace {
+
+class TrackDataContainer : public MediaStreamComponent::TrackData {
+ public:
+ explicit TrackDataContainer(
+ std::unique_ptr<WebMediaStreamTrack::TrackData> extra_data)
+ : extra_data_(std::move(extra_data)) {}
+
+ WebMediaStreamTrack::TrackData* GetTrackData() { return extra_data_.get(); }
+ void GetSettings(WebMediaStreamTrack::Settings& settings) {
+ extra_data_->GetSettings(settings);
+ }
+
+ private:
+ std::unique_ptr<WebMediaStreamTrack::TrackData> extra_data_;
+};
+
+} // namespace
+
+WebMediaStreamTrack::WebMediaStreamTrack(
+ MediaStreamComponent* media_stream_component)
+ : private_(media_stream_component) {}
+
+WebMediaStreamTrack& WebMediaStreamTrack::operator=(
+ MediaStreamComponent* media_stream_component) {
+ private_ = media_stream_component;
+ return *this;
+}
+
+void WebMediaStreamTrack::Initialize(const WebMediaStreamSource& source) {
+ private_ = MediaStreamComponent::Create(source);
+}
+
+void WebMediaStreamTrack::Initialize(const WebString& id,
+ const WebMediaStreamSource& source) {
+ private_ = MediaStreamComponent::Create(id, source);
+}
+
+void WebMediaStreamTrack::Reset() {
+ private_.Reset();
+}
+
+WebMediaStreamTrack::operator MediaStreamComponent*() const {
+ return private_.Get();
+}
+
+bool WebMediaStreamTrack::IsEnabled() const {
+ DCHECK(!private_.IsNull());
+ return private_->Enabled();
+}
+
+bool WebMediaStreamTrack::IsMuted() const {
+ DCHECK(!private_.IsNull());
+ return private_->Muted();
+}
+
+WebMediaStreamTrack::ContentHintType WebMediaStreamTrack::ContentHint() const {
+ DCHECK(!private_.IsNull());
+ return private_->ContentHint();
+}
+
+WebMediaConstraints WebMediaStreamTrack::Constraints() const {
+ DCHECK(!private_.IsNull());
+ return private_->Constraints();
+}
+
+void WebMediaStreamTrack::SetConstraints(
+ const WebMediaConstraints& constraints) {
+ DCHECK(!private_.IsNull());
+ return private_->SetConstraints(constraints);
+}
+
+WebString WebMediaStreamTrack::Id() const {
+ DCHECK(!private_.IsNull());
+ return private_->Id();
+}
+
+int WebMediaStreamTrack::UniqueId() const {
+ DCHECK(!private_.IsNull());
+ return private_->UniqueId();
+}
+
+WebMediaStreamSource WebMediaStreamTrack::Source() const {
+ DCHECK(!private_.IsNull());
+ return WebMediaStreamSource(private_->Source());
+}
+
+WebMediaStreamTrack::TrackData* WebMediaStreamTrack::GetTrackData() const {
+ MediaStreamComponent::TrackData* data = private_->GetTrackData();
+ if (!data)
+ return nullptr;
+ return static_cast<TrackDataContainer*>(data)->GetTrackData();
+}
+
+void WebMediaStreamTrack::SetTrackData(TrackData* extra_data) {
+ DCHECK(!private_.IsNull());
+
+ private_->SetTrackData(
+ std::make_unique<TrackDataContainer>(base::WrapUnique(extra_data)));
+}
+
+void WebMediaStreamTrack::SetSourceProvider(WebAudioSourceProvider* provider) {
+ DCHECK(!private_.IsNull());
+ private_->SetSourceProvider(provider);
+}
+
+void WebMediaStreamTrack::Assign(const WebMediaStreamTrack& other) {
+ private_ = other.private_;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_memory_coordinator.cc b/chromium/third_party/blink/renderer/platform/exported/web_memory_coordinator.cc
new file mode 100644
index 00000000000..b8a5c236d43
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_memory_coordinator.cc
@@ -0,0 +1,24 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_memory_coordinator.h"
+
+#include "third_party/blink/renderer/platform/memory_coordinator.h"
+
+namespace blink {
+
+void WebMemoryCoordinator::OnMemoryPressure(
+ WebMemoryPressureLevel pressure_level) {
+ MemoryCoordinator::Instance().OnMemoryPressure(pressure_level);
+}
+
+void WebMemoryCoordinator::OnMemoryStateChange(MemoryState state) {
+ MemoryCoordinator::Instance().OnMemoryStateChange(state);
+}
+
+void WebMemoryCoordinator::OnPurgeMemory() {
+ MemoryCoordinator::Instance().OnPurgeMemory();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_mixed_content.cc b/chromium/third_party/blink/renderer/platform/exported/web_mixed_content.cc
new file mode 100644
index 00000000000..9631cd779d0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_mixed_content.cc
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2016 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 "third_party/blink/public/platform/web_mixed_content.h"
+
+namespace blink {
+
+// static
+WebMixedContentContextType WebMixedContent::ContextTypeFromRequestContext(
+ WebURLRequest::RequestContext context,
+ bool strict_mixed_content_checking_for_plugin) {
+ switch (context) {
+ // "Optionally-blockable" mixed content
+ case WebURLRequest::kRequestContextAudio:
+ case WebURLRequest::kRequestContextImage:
+ case WebURLRequest::kRequestContextVideo:
+ return WebMixedContentContextType::kOptionallyBlockable;
+
+ // Plugins! Oh how dearly we love plugin-loaded content!
+ case WebURLRequest::kRequestContextPlugin: {
+ return strict_mixed_content_checking_for_plugin
+ ? WebMixedContentContextType::kBlockable
+ : WebMixedContentContextType::kOptionallyBlockable;
+ }
+
+ // "Blockable" mixed content
+ case WebURLRequest::kRequestContextBeacon:
+ case WebURLRequest::kRequestContextCSPReport:
+ case WebURLRequest::kRequestContextEmbed:
+ case WebURLRequest::kRequestContextEventSource:
+ case WebURLRequest::kRequestContextFavicon:
+ case WebURLRequest::kRequestContextFetch:
+ case WebURLRequest::kRequestContextFont:
+ case WebURLRequest::kRequestContextForm:
+ case WebURLRequest::kRequestContextFrame:
+ case WebURLRequest::kRequestContextHyperlink:
+ case WebURLRequest::kRequestContextIframe:
+ case WebURLRequest::kRequestContextImageSet:
+ case WebURLRequest::kRequestContextImport:
+ case WebURLRequest::kRequestContextInternal:
+ case WebURLRequest::kRequestContextLocation:
+ case WebURLRequest::kRequestContextManifest:
+ case WebURLRequest::kRequestContextObject:
+ case WebURLRequest::kRequestContextPing:
+ case WebURLRequest::kRequestContextPrefetch:
+ case WebURLRequest::kRequestContextScript:
+ case WebURLRequest::kRequestContextServiceWorker:
+ case WebURLRequest::kRequestContextSharedWorker:
+ case WebURLRequest::kRequestContextStyle:
+ case WebURLRequest::kRequestContextSubresource:
+ case WebURLRequest::kRequestContextTrack:
+ case WebURLRequest::kRequestContextWorker:
+ case WebURLRequest::kRequestContextXMLHttpRequest:
+ case WebURLRequest::kRequestContextXSLT:
+ return WebMixedContentContextType::kBlockable;
+
+ // FIXME: Contexts that we should block, but don't currently.
+ // https://crbug.com/388650
+ case WebURLRequest::kRequestContextDownload:
+ return WebMixedContentContextType::kShouldBeBlockable;
+
+ case WebURLRequest::kRequestContextUnspecified:
+ NOTREACHED();
+ }
+ NOTREACHED();
+ return WebMixedContentContextType::kBlockable;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_mock_clipboard.cc b/chromium/third_party/blink/renderer/platform/exported/web_mock_clipboard.cc
new file mode 100644
index 00000000000..52d2c5dc75e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_mock_clipboard.cc
@@ -0,0 +1,23 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_mock_clipboard.h"
+
+#include "third_party/blink/renderer/platform/blob/blob_data.h"
+
+namespace blink {
+
+WebBlobInfo WebMockClipboard::CreateBlobFromData(base::span<const uint8_t> data,
+ const WebString& mime_type) {
+ auto blob_data = BlobData::Create();
+ blob_data->SetContentType(mime_type);
+ blob_data->AppendBytes(data.data(), data.size());
+ auto blob_data_handle =
+ BlobDataHandle::Create(std::move(blob_data), data.size());
+ return WebBlobInfo(
+ blob_data_handle->Uuid(), mime_type, data.size(),
+ blob_data_handle->CloneBlobPtr().PassInterface().PassHandle());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_network_state_notifier.cc b/chromium/third_party/blink/renderer/platform/exported/web_network_state_notifier.cc
new file mode 100644
index 00000000000..9c0a8df3833
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_network_state_notifier.cc
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/public/platform/web_network_state_notifier.h"
+
+#include "third_party/blink/renderer/platform/network/network_state_notifier.h"
+
+namespace blink {
+
+void WebNetworkStateNotifier::SetOnLine(bool on_line) {
+ GetNetworkStateNotifier().SetOnLine(on_line);
+}
+
+void WebNetworkStateNotifier::SetWebConnection(WebConnectionType type,
+ double max_bandwidth_mbps) {
+ GetNetworkStateNotifier().SetWebConnection(type, max_bandwidth_mbps);
+}
+
+void WebNetworkStateNotifier::SetNetworkQuality(WebEffectiveConnectionType type,
+ TimeDelta http_rtt,
+ TimeDelta transport_rtt,
+ int downlink_throughput_kbps) {
+ GetNetworkStateNotifier().SetNetworkQuality(type, http_rtt, transport_rtt,
+ downlink_throughput_kbps);
+}
+
+void WebNetworkStateNotifier::SetSaveDataEnabled(bool enabled) {
+ GetNetworkStateNotifier().SetSaveDataEnabled(enabled);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_prerender.cc b/chromium/third_party/blink/renderer/platform/exported/web_prerender.cc
new file mode 100644
index 00000000000..6e123924e8d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_prerender.cc
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2012 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 "third_party/blink/public/platform/web_prerender.h"
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/prerender.h"
+
+namespace blink {
+
+namespace {
+
+class PrerenderExtraDataContainer : public Prerender::ExtraData {
+ public:
+ static scoped_refptr<PrerenderExtraDataContainer> Create(
+ WebPrerender::ExtraData* extra_data) {
+ return base::AdoptRef(new PrerenderExtraDataContainer(extra_data));
+ }
+
+ ~PrerenderExtraDataContainer() override = default;
+
+ WebPrerender::ExtraData* GetExtraData() const { return extra_data_.get(); }
+
+ private:
+ explicit PrerenderExtraDataContainer(WebPrerender::ExtraData* extra_data)
+ : extra_data_(base::WrapUnique(extra_data)) {}
+
+ std::unique_ptr<WebPrerender::ExtraData> extra_data_;
+};
+
+} // namespace
+
+WebPrerender::WebPrerender(Prerender* prerender) : private_(prerender) {}
+
+const Prerender* WebPrerender::ToPrerender() const {
+ return private_.Get();
+}
+
+void WebPrerender::Reset() {
+ private_.Reset();
+}
+
+void WebPrerender::Assign(const WebPrerender& other) {
+ private_ = other.private_;
+}
+
+bool WebPrerender::IsNull() const {
+ return private_.IsNull();
+}
+
+WebURL WebPrerender::Url() const {
+ return WebURL(private_->Url());
+}
+
+unsigned WebPrerender::RelTypes() const {
+ return private_->RelTypes();
+}
+
+WebString WebPrerender::GetReferrer() const {
+ return private_->GetReferrer();
+}
+
+WebReferrerPolicy WebPrerender::GetReferrerPolicy() const {
+ return static_cast<WebReferrerPolicy>(private_->GetReferrerPolicy());
+}
+
+void WebPrerender::SetExtraData(WebPrerender::ExtraData* extra_data) {
+ private_->SetExtraData(PrerenderExtraDataContainer::Create(extra_data));
+}
+
+const WebPrerender::ExtraData* WebPrerender::GetExtraData() const {
+ scoped_refptr<Prerender::ExtraData> webcore_extra_data =
+ private_->GetExtraData();
+ if (!webcore_extra_data)
+ return nullptr;
+ return static_cast<PrerenderExtraDataContainer*>(webcore_extra_data.get())
+ ->GetExtraData();
+}
+
+void WebPrerender::DidStartPrerender() {
+ private_->DidStartPrerender();
+}
+
+void WebPrerender::DidStopPrerender() {
+ private_->DidStopPrerender();
+}
+
+void WebPrerender::DidSendLoadForPrerender() {
+ private_->DidSendLoadForPrerender();
+}
+
+void WebPrerender::DidSendDOMContentLoadedForPrerender() {
+ private_->DidSendDOMContentLoadedForPrerender();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_prerendering_support.cc b/chromium/third_party/blink/renderer/platform/exported/web_prerendering_support.cc
new file mode 100644
index 00000000000..c05a368ef48
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_prerendering_support.cc
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2012 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 "third_party/blink/public/platform/web_prerendering_support.h"
+
+namespace blink {
+
+WebPrerenderingSupport* WebPrerenderingSupport::platform_ = nullptr;
+
+void WebPrerenderingSupport::Initialize(WebPrerenderingSupport* platform) {
+ platform_ = platform;
+}
+
+void WebPrerenderingSupport::Shutdown() {
+ platform_ = nullptr;
+}
+
+WebPrerenderingSupport* WebPrerenderingSupport::Current() {
+ return platform_;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_presentation_receiver.cc b/chromium/third_party/blink/renderer/platform/exported/web_presentation_receiver.cc
new file mode 100644
index 00000000000..0e3d93d402e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_presentation_receiver.cc
@@ -0,0 +1,11 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/modules/presentation/web_presentation_receiver.h"
+
+// This WebPresentationReceiver.cpp, which includes only
+// WebPresentationReceiver.h, should be in Source/platform/exported,
+// because WebPresentationReceiver is not compiled without this cpp.
+// So if we don't have this cpp, we will see unresolved symbol error
+// when constructor/destructor's address is required.
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_rtc_answer_options.cc b/chromium/third_party/blink/renderer/platform/exported/web_rtc_answer_options.cc
new file mode 100644
index 00000000000..11d96529ea7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_rtc_answer_options.cc
@@ -0,0 +1,27 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_rtc_answer_options.h"
+
+#include "third_party/blink/renderer/platform/peerconnection/rtc_answer_options_platform.h"
+
+namespace blink {
+
+WebRTCAnswerOptions::WebRTCAnswerOptions(RTCAnswerOptionsPlatform* options)
+ : private_(options) {}
+
+void WebRTCAnswerOptions::Assign(const WebRTCAnswerOptions& other) {
+ private_ = other.private_;
+}
+
+void WebRTCAnswerOptions::Reset() {
+ private_.Reset();
+}
+
+bool WebRTCAnswerOptions::VoiceActivityDetection() const {
+ DCHECK(!private_.IsNull());
+ return private_->VoiceActivityDetection();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_rtc_offer_options.cc b/chromium/third_party/blink/renderer/platform/exported/web_rtc_offer_options.cc
new file mode 100644
index 00000000000..6340aef85bf
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_rtc_offer_options.cc
@@ -0,0 +1,51 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_rtc_offer_options.h"
+
+#include "third_party/blink/renderer/platform/peerconnection/rtc_offer_options_platform.h"
+
+namespace blink {
+
+WebRTCOfferOptions::WebRTCOfferOptions(RTCOfferOptionsPlatform* options)
+ : private_(options) {}
+
+WebRTCOfferOptions::WebRTCOfferOptions(int32_t offer_to_receive_audio,
+ int32_t offer_to_receive_video,
+ bool voice_activity_detection,
+ bool ice_restart)
+ : private_(RTCOfferOptionsPlatform::Create(offer_to_receive_audio,
+ offer_to_receive_video,
+ voice_activity_detection,
+ ice_restart)) {}
+
+void WebRTCOfferOptions::Assign(const WebRTCOfferOptions& other) {
+ private_ = other.private_;
+}
+
+void WebRTCOfferOptions::Reset() {
+ private_.Reset();
+}
+
+int32_t WebRTCOfferOptions::OfferToReceiveVideo() const {
+ DCHECK(!IsNull());
+ return private_->OfferToReceiveVideo();
+}
+
+int32_t WebRTCOfferOptions::OfferToReceiveAudio() const {
+ DCHECK(!IsNull());
+ return private_->OfferToReceiveAudio();
+}
+
+bool WebRTCOfferOptions::VoiceActivityDetection() const {
+ DCHECK(!IsNull());
+ return private_->VoiceActivityDetection();
+}
+
+bool WebRTCOfferOptions::IceRestart() const {
+ DCHECK(!IsNull());
+ return private_->IceRestart();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_rtc_peer_connection_handler_client.cc b/chromium/third_party/blink/renderer/platform/exported/web_rtc_peer_connection_handler_client.cc
new file mode 100644
index 00000000000..9b3be0525c0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_rtc_peer_connection_handler_client.cc
@@ -0,0 +1,14 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_rtc_peer_connection_handler_client.h"
+
+namespace blink {
+
+WebRTCPeerConnectionHandlerClient::~WebRTCPeerConnectionHandlerClient() =
+ default;
+
+void WebRTCPeerConnectionHandlerClient::ClosePeerConnection() {}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_rtc_rtp_contributing_source.cc b/chromium/third_party/blink/renderer/platform/exported/web_rtc_rtp_contributing_source.cc
new file mode 100644
index 00000000000..0453e96394a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_rtc_rtp_contributing_source.cc
@@ -0,0 +1,11 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_rtc_rtp_contributing_source.h"
+
+namespace blink {
+
+WebRTCRtpContributingSource::~WebRTCRtpContributingSource() = default;
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_rtc_rtp_receiver.cc b/chromium/third_party/blink/renderer/platform/exported/web_rtc_rtp_receiver.cc
new file mode 100644
index 00000000000..a09e4d5da73
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_rtc_rtp_receiver.cc
@@ -0,0 +1,11 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_rtc_rtp_receiver.h"
+
+namespace blink {
+
+WebRTCRtpReceiver::~WebRTCRtpReceiver() = default;
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_rtc_rtp_sender.cc b/chromium/third_party/blink/renderer/platform/exported/web_rtc_rtp_sender.cc
new file mode 100644
index 00000000000..6a88a1ab147
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_rtc_rtp_sender.cc
@@ -0,0 +1,11 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_rtc_rtp_sender.h"
+
+namespace blink {
+
+WebRTCRtpSender::~WebRTCRtpSender() = default;
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_rtc_session_description.cc b/chromium/third_party/blink/renderer/platform/exported/web_rtc_session_description.cc
new file mode 100644
index 00000000000..a55ebff8f61
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_rtc_session_description.cc
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2012 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 "third_party/blink/public/platform/web_rtc_session_description.h"
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+
+namespace blink {
+
+class WebRTCSessionDescriptionPrivate final
+ : public RefCounted<WebRTCSessionDescriptionPrivate> {
+ public:
+ static scoped_refptr<WebRTCSessionDescriptionPrivate> Create(
+ const WebString& type,
+ const WebString& sdp);
+
+ WebString GetType() { return type_; }
+ void SetType(const WebString& type) { type_ = type; }
+
+ WebString Sdp() { return sdp_; }
+ void SetSdp(const WebString& sdp) { sdp_ = sdp; }
+
+ private:
+ WebRTCSessionDescriptionPrivate(const WebString& type, const WebString& sdp);
+
+ WebString type_;
+ WebString sdp_;
+};
+
+scoped_refptr<WebRTCSessionDescriptionPrivate>
+WebRTCSessionDescriptionPrivate::Create(const WebString& type,
+ const WebString& sdp) {
+ return base::AdoptRef(new WebRTCSessionDescriptionPrivate(type, sdp));
+}
+
+WebRTCSessionDescriptionPrivate::WebRTCSessionDescriptionPrivate(
+ const WebString& type,
+ const WebString& sdp)
+ : type_(type), sdp_(sdp) {}
+
+void WebRTCSessionDescription::Assign(const WebRTCSessionDescription& other) {
+ private_ = other.private_;
+}
+
+void WebRTCSessionDescription::Reset() {
+ private_.Reset();
+}
+
+void WebRTCSessionDescription::Initialize(const WebString& type,
+ const WebString& sdp) {
+ private_ = WebRTCSessionDescriptionPrivate::Create(type, sdp);
+}
+
+WebString WebRTCSessionDescription::GetType() const {
+ DCHECK(!private_.IsNull());
+ return private_->GetType();
+}
+
+void WebRTCSessionDescription::SetType(const WebString& type) {
+ DCHECK(!private_.IsNull());
+ return private_->SetType(type);
+}
+
+WebString WebRTCSessionDescription::Sdp() const {
+ DCHECK(!private_.IsNull());
+ return private_->Sdp();
+}
+
+void WebRTCSessionDescription::SetSDP(const WebString& sdp) {
+ DCHECK(!private_.IsNull());
+ return private_->SetSdp(sdp);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_rtc_session_description_request.cc b/chromium/third_party/blink/renderer/platform/exported/web_rtc_session_description_request.cc
new file mode 100644
index 00000000000..91ce74759cd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_rtc_session_description_request.cc
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2012 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 "third_party/blink/public/platform/web_rtc_session_description_request.h"
+
+#include "third_party/blink/public/platform/web_rtc_session_description.h"
+#include "third_party/blink/renderer/platform/peerconnection/rtc_session_description_request.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+WebRTCSessionDescriptionRequest::WebRTCSessionDescriptionRequest(
+ RTCSessionDescriptionRequest* constraints)
+ : private_(constraints) {}
+
+void WebRTCSessionDescriptionRequest::Assign(
+ const WebRTCSessionDescriptionRequest& other) {
+ private_ = other.private_;
+}
+
+void WebRTCSessionDescriptionRequest::Reset() {
+ private_.Reset();
+}
+
+void WebRTCSessionDescriptionRequest::RequestSucceeded(
+ const WebRTCSessionDescription& session_description) const {
+ DCHECK(private_.Get());
+ private_->RequestSucceeded(session_description);
+}
+
+void WebRTCSessionDescriptionRequest::RequestFailed(
+ const WebString& error) const {
+ DCHECK(private_.Get());
+ private_->RequestFailed(error);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_rtc_stats.cc b/chromium/third_party/blink/renderer/platform/exported/web_rtc_stats.cc
new file mode 100644
index 00000000000..032fa7b2521
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_rtc_stats.cc
@@ -0,0 +1,17 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_rtc_stats.h"
+
+namespace blink {
+
+WebRTCStatsReport::~WebRTCStatsReport() = default;
+
+WebRTCStats::~WebRTCStats() = default;
+
+WebRTCStatsMember::~WebRTCStatsMember() = default;
+
+WebRTCStatsReportCallback::~WebRTCStatsReportCallback() = default;
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_rtc_stats_request.cc b/chromium/third_party/blink/renderer/platform/exported/web_rtc_stats_request.cc
new file mode 100644
index 00000000000..809050fa22c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_rtc_stats_request.cc
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2012 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 "third_party/blink/public/platform/web_rtc_stats_request.h"
+
+#include "third_party/blink/public/platform/web_media_stream.h"
+#include "third_party/blink/public/platform/web_media_stream_track.h"
+#include "third_party/blink/public/platform/web_rtc_stats_response.h"
+#include "third_party/blink/renderer/platform/peerconnection/rtc_stats_request.h"
+#include "third_party/blink/renderer/platform/peerconnection/rtc_stats_response_base.h"
+
+namespace blink {
+
+WebRTCStatsRequest::WebRTCStatsRequest(RTCStatsRequest* request)
+ : private_(request) {}
+
+void WebRTCStatsRequest::Assign(const WebRTCStatsRequest& other) {
+ private_ = other.private_;
+}
+
+void WebRTCStatsRequest::Reset() {
+ private_.Reset();
+}
+
+WebRTCStatsResponse WebRTCStatsRequest::CreateResponse() const {
+ return WebRTCStatsResponse(private_->CreateResponse());
+}
+
+bool WebRTCStatsRequest::HasSelector() const {
+ return private_->HasSelector();
+}
+
+const WebMediaStreamTrack WebRTCStatsRequest::Component() const {
+ return WebMediaStreamTrack(private_->Component());
+}
+
+void WebRTCStatsRequest::RequestSucceeded(
+ const WebRTCStatsResponse& response) const {
+ private_->RequestSucceeded(response);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_rtc_stats_response.cc b/chromium/third_party/blink/renderer/platform/exported/web_rtc_stats_response.cc
new file mode 100644
index 00000000000..2c7d842b641
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_rtc_stats_response.cc
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2012 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/public/platform/web_rtc_stats_response.h"
+
+#include "third_party/blink/renderer/platform/peerconnection/rtc_stats_response_base.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+WebRTCStatsResponse::WebRTCStatsResponse(RTCStatsResponseBase* request)
+ : private_(request) {}
+
+void WebRTCStatsResponse::Assign(const WebRTCStatsResponse& other) {
+ private_ = other.private_;
+}
+
+void WebRTCStatsResponse::Reset() {
+ private_.Reset();
+}
+
+WebRTCStatsResponse::operator RTCStatsResponseBase*() const {
+ return private_.Get();
+}
+
+void WebRTCStatsResponse::AddStats(const WebRTCLegacyStats& stats) {
+ DCHECK(!private_.IsNull());
+ private_->AddStats(stats);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_rtc_void_request.cc b/chromium/third_party/blink/renderer/platform/exported/web_rtc_void_request.cc
new file mode 100644
index 00000000000..b771fa7239a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_rtc_void_request.cc
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2012 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 "third_party/blink/public/platform/web_rtc_void_request.h"
+
+#include "third_party/blink/renderer/platform/peerconnection/rtc_void_request.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+WebRTCVoidRequest::WebRTCVoidRequest(RTCVoidRequest* constraints)
+ : private_(constraints) {}
+
+void WebRTCVoidRequest::Assign(const WebRTCVoidRequest& other) {
+ private_ = other.private_;
+}
+
+void WebRTCVoidRequest::Reset() {
+ private_.Reset();
+}
+
+void WebRTCVoidRequest::RequestSucceeded() const {
+ if (private_.Get())
+ private_->RequestSucceeded();
+}
+
+void WebRTCVoidRequest::RequestFailed(WebRTCError error) const {
+ if (private_.Get()) {
+ private_->RequestFailed(error);
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_runtime_features.cc b/chromium/third_party/blink/renderer/platform/exported/web_runtime_features.cc
new file mode 100644
index 00000000000..d65f6e9d6fc
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_runtime_features.cc
@@ -0,0 +1,521 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/public/platform/web_runtime_features.h"
+
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+namespace blink {
+
+void WebRuntimeFeatures::EnableExperimentalFeatures(bool enable) {
+ RuntimeEnabledFeatures::SetExperimentalFeaturesEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableWebBluetooth(bool enable) {
+ RuntimeEnabledFeatures::SetWebBluetoothEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableWebAssemblyStreaming(bool enable) {
+ RuntimeEnabledFeatures::SetWebAssemblyStreamingEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableWebNfc(bool enable) {
+ RuntimeEnabledFeatures::SetWebNFCEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableWebUsb(bool enable) {
+ RuntimeEnabledFeatures::SetWebUSBEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableFeatureFromString(const std::string& name,
+ bool enable) {
+ RuntimeEnabledFeatures::SetFeatureEnabledFromString(name, enable);
+}
+
+void WebRuntimeFeatures::EnableTestOnlyFeatures(bool enable) {
+ RuntimeEnabledFeatures::SetTestFeaturesEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableOriginTrialControlledFeatures(bool enable) {
+ RuntimeEnabledFeatures::SetOriginTrialControlledFeaturesEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableOriginManifest(bool enable) {
+ RuntimeEnabledFeatures::SetOriginManifestEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableOutOfBlinkCORS(bool enable) {
+ RuntimeEnabledFeatures::SetOutOfBlinkCORSEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableAccelerated2dCanvas(bool enable) {
+ RuntimeEnabledFeatures::SetAccelerated2dCanvasEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableAllowActivationDelegationAttr(bool enable) {
+ RuntimeEnabledFeatures::SetAllowActivationDelegationAttrEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableAudioOutputDevices(bool enable) {
+ RuntimeEnabledFeatures::SetAudioOutputDevicesEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableCacheInlineScriptCode(bool enable) {
+ RuntimeEnabledFeatures::SetCacheInlineScriptCodeEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableCanvas2dImageChromium(bool enable) {
+ RuntimeEnabledFeatures::SetCanvas2dImageChromiumEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableCompositedSelectionUpdate(bool enable) {
+ RuntimeEnabledFeatures::SetCompositedSelectionUpdateEnabled(enable);
+}
+
+bool WebRuntimeFeatures::IsCompositedSelectionUpdateEnabled() {
+ return RuntimeEnabledFeatures::CompositedSelectionUpdateEnabled();
+}
+
+void WebRuntimeFeatures::EnableCompositorTouchAction(bool enable) {
+ RuntimeEnabledFeatures::SetCompositorTouchActionEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableCSSHexAlphaColor(bool enable) {
+ RuntimeEnabledFeatures::SetCSSHexAlphaColorEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableScrollTopLeftInterop(bool enable) {
+ RuntimeEnabledFeatures::SetScrollTopLeftInteropEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableDatabase(bool enable) {
+ RuntimeEnabledFeatures::SetDatabaseEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableDecodeToYUV(bool enable) {
+ RuntimeEnabledFeatures::SetDecodeToYUVEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableFastMobileScrolling(bool enable) {
+ RuntimeEnabledFeatures::SetFastMobileScrollingEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableFileSystem(bool enable) {
+ RuntimeEnabledFeatures::SetFileSystemEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableForceTallerSelectPopup(bool enable) {
+ RuntimeEnabledFeatures::SetForceTallerSelectPopupEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableGamepadExtensions(bool enable) {
+ RuntimeEnabledFeatures::SetGamepadExtensionsEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableGenericSensor(bool enable) {
+ RuntimeEnabledFeatures::SetSensorEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableGenericSensorExtraClasses(bool enable) {
+ RuntimeEnabledFeatures::SetSensorExtraClassesEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableHeapCompaction(bool enable) {
+ RuntimeEnabledFeatures::SetHeapCompactionEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableInputMultipleFieldsUI(bool enable) {
+ RuntimeEnabledFeatures::SetInputMultipleFieldsUIEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableLazyFrameLoading(bool enable) {
+ RuntimeEnabledFeatures::SetLazyFrameLoadingEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableLazyParseCSS(bool enable) {
+ RuntimeEnabledFeatures::SetLazyParseCSSEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableMediaCapture(bool enable) {
+ RuntimeEnabledFeatures::SetMediaCaptureEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableMediaSession(bool enable) {
+ RuntimeEnabledFeatures::SetMediaSessionEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableModernMediaControls(bool enable) {
+ RuntimeEnabledFeatures::SetModernMediaControlsEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableModuleScriptsDynamicImport(bool enable) {
+ RuntimeEnabledFeatures::SetModuleScriptsDynamicImportEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableModuleScriptsImportMetaUrl(bool enable) {
+ RuntimeEnabledFeatures::SetModuleScriptsImportMetaUrlEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableNotificationConstructor(bool enable) {
+ RuntimeEnabledFeatures::SetNotificationConstructorEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableNotificationContentImage(bool enable) {
+ RuntimeEnabledFeatures::SetNotificationContentImageEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableNotifications(bool enable) {
+ RuntimeEnabledFeatures::SetNotificationsEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableNotificationsWithMojo(bool enable) {
+ RuntimeEnabledFeatures::SetNotificationsWithMojoEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableNavigatorContentUtils(bool enable) {
+ RuntimeEnabledFeatures::SetNavigatorContentUtilsEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableNetInfoDownlinkMax(bool enable) {
+ RuntimeEnabledFeatures::SetNetInfoDownlinkMaxEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableNetworkService(bool enable) {
+ RuntimeEnabledFeatures::SetNetworkServiceEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableOnDeviceChange(bool enable) {
+ RuntimeEnabledFeatures::SetOnDeviceChangeEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableOrientationEvent(bool enable) {
+ RuntimeEnabledFeatures::SetOrientationEventEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableOriginTrials(bool enable) {
+ RuntimeEnabledFeatures::SetOriginTrialsEnabled(enable);
+}
+
+bool WebRuntimeFeatures::IsOriginTrialsEnabled() {
+ return RuntimeEnabledFeatures::OriginTrialsEnabled();
+}
+
+void WebRuntimeFeatures::EnableOverflowIconsForMediaControls(bool enable) {
+ RuntimeEnabledFeatures::SetOverflowIconsForMediaControlsEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnablePagePopup(bool enable) {
+ RuntimeEnabledFeatures::SetPagePopupEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableMiddleClickAutoscroll(bool enable) {
+ RuntimeEnabledFeatures::SetMiddleClickAutoscrollEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnablePassiveDocumentEventListeners(bool enable) {
+ RuntimeEnabledFeatures::SetPassiveDocumentEventListenersEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnablePaymentApp(bool enable) {
+ RuntimeEnabledFeatures::SetPaymentAppEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnablePaymentRequest(bool enable) {
+ RuntimeEnabledFeatures::SetPaymentRequestEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnablePermissionsAPI(bool enable) {
+ RuntimeEnabledFeatures::SetPermissionsEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnablePictureInPicture(bool enable) {
+ RuntimeEnabledFeatures::SetPictureInPictureEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnablePreloadDefaultIsMetadata(bool enable) {
+ RuntimeEnabledFeatures::SetPreloadDefaultIsMetadataEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnablePreloadImageSrcSetEnabled(bool enable) {
+ RuntimeEnabledFeatures::SetPreloadImageSrcSetEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableRasterInducingScroll(bool enable) {
+ RuntimeEnabledFeatures::SetRasterInducingScrollEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableScriptedSpeech(bool enable) {
+ RuntimeEnabledFeatures::SetScriptedSpeechEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableSlimmingPaintV2(bool enable) {
+ RuntimeEnabledFeatures::SetSlimmingPaintV2Enabled(enable);
+}
+
+void WebRuntimeFeatures::EnableUserActivationV2(bool enable) {
+ RuntimeEnabledFeatures::SetUserActivationV2Enabled(enable);
+}
+
+void WebRuntimeFeatures::EnableTouchEventFeatureDetection(bool enable) {
+ RuntimeEnabledFeatures::SetTouchEventFeatureDetectionEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableTouchpadAndWheelScrollLatching(bool enable) {
+ RuntimeEnabledFeatures::SetTouchpadAndWheelScrollLatchingEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableTurnOff2DAndOpacityCompositorAnimations(
+ bool enable) {
+ RuntimeEnabledFeatures::SetTurnOff2DAndOpacityCompositorAnimationsEnabled(
+ enable);
+}
+
+void WebRuntimeFeatures::EnableWebGLDraftExtensions(bool enable) {
+ RuntimeEnabledFeatures::SetWebGLDraftExtensionsEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableWebGLImageChromium(bool enable) {
+ RuntimeEnabledFeatures::SetWebGLImageChromiumEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableXSLT(bool enable) {
+ RuntimeEnabledFeatures::SetXSLTEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableOverlayScrollbars(bool enable) {
+ RuntimeEnabledFeatures::SetOverlayScrollbarsEnabled(enable);
+}
+
+void WebRuntimeFeatures::ForceOverlayFullscreenVideo(bool enable) {
+ RuntimeEnabledFeatures::SetForceOverlayFullscreenVideoEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableSharedArrayBuffer(bool enable) {
+ RuntimeEnabledFeatures::SetSharedArrayBufferEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableSharedWorker(bool enable) {
+ RuntimeEnabledFeatures::SetSharedWorkerEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnablePreciseMemoryInfo(bool enable) {
+ RuntimeEnabledFeatures::SetPreciseMemoryInfoEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnablePrintBrowser(bool enable) {
+ RuntimeEnabledFeatures::SetPrintBrowserEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableV8IdleTasks(bool enable) {
+ RuntimeEnabledFeatures::SetV8IdleTasksEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableReducedReferrerGranularity(bool enable) {
+ RuntimeEnabledFeatures::SetReducedReferrerGranularityEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnablePushMessaging(bool enable) {
+ RuntimeEnabledFeatures::SetPushMessagingEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableWebShare(bool enable) {
+ RuntimeEnabledFeatures::SetWebShareEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableWebVR(bool enable) {
+ RuntimeEnabledFeatures::SetWebVREnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableWebXR(bool enable) {
+ RuntimeEnabledFeatures::SetWebXREnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableWebXRGamepadSupport(bool enable) {
+ RuntimeEnabledFeatures::SetWebXRGamepadSupportEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnablePresentationAPI(bool enable) {
+ RuntimeEnabledFeatures::SetPresentationEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableRenderingPipelineThrottling(bool enable) {
+ RuntimeEnabledFeatures::SetRenderingPipelineThrottlingEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableRequireCSSExtensionForFile(bool enable) {
+ RuntimeEnabledFeatures::SetRequireCSSExtensionForFileEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableResourceLoadScheduler(bool enable) {
+ RuntimeEnabledFeatures::SetResourceLoadSchedulerEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableExpensiveBackgroundTimerThrottling(bool enable) {
+ RuntimeEnabledFeatures::SetExpensiveBackgroundTimerThrottlingEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableRootLayerScrolling(bool enable) {
+ RuntimeEnabledFeatures::SetRootLayerScrollingEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableScrollAnchoring(bool enable) {
+ RuntimeEnabledFeatures::SetScrollAnchoringEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableScrollAnchorSerialization(bool enable) {
+ RuntimeEnabledFeatures::SetScrollAnchorSerializationEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableServiceWorkerScriptFullCodeCache(bool enable) {
+ RuntimeEnabledFeatures::SetServiceWorkerScriptFullCodeCacheEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableAutoplayMutedVideos(bool enable) {
+ RuntimeEnabledFeatures::SetAutoplayMutedVideosEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableTimerThrottlingForBackgroundTabs(bool enable) {
+ RuntimeEnabledFeatures::SetTimerThrottlingForBackgroundTabsEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableTimerThrottlingForHiddenFrames(bool enable) {
+ RuntimeEnabledFeatures::SetTimerThrottlingForHiddenFramesEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableSendBeaconThrowForBlobWithNonSimpleType(
+ bool enable) {
+ RuntimeEnabledFeatures::SetSendBeaconThrowForBlobWithNonSimpleTypeEnabled(
+ enable);
+}
+
+void WebRuntimeFeatures::EnableBackgroundVideoTrackOptimization(bool enable) {
+ RuntimeEnabledFeatures::SetBackgroundVideoTrackOptimizationEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableNewRemotePlaybackPipeline(bool enable) {
+ RuntimeEnabledFeatures::SetNewRemotePlaybackPipelineEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableRemotePlaybackAPI(bool enable) {
+ RuntimeEnabledFeatures::SetRemotePlaybackEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableVideoFullscreenOrientationLock(bool enable) {
+ RuntimeEnabledFeatures::SetVideoFullscreenOrientationLockEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableVideoRotateToFullscreen(bool enable) {
+ RuntimeEnabledFeatures::SetVideoRotateToFullscreenEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableVideoFullscreenDetection(bool enable) {
+ RuntimeEnabledFeatures::SetVideoFullscreenDetectionEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableMediaControlsOverlayPlayButton(bool enable) {
+ RuntimeEnabledFeatures::SetMediaControlsOverlayPlayButtonEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableRemotePlaybackBackend(bool enable) {
+ RuntimeEnabledFeatures::SetRemotePlaybackBackendEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableMediaCastOverlayButton(bool enable) {
+ RuntimeEnabledFeatures::SetMediaCastOverlayButtonEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableWebAuth(bool enable) {
+ RuntimeEnabledFeatures::SetWebAuthEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableClientPlaceholdersForServerLoFi(bool enable) {
+ RuntimeEnabledFeatures::SetClientPlaceholdersForServerLoFiEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableLazyInitializeMediaControls(bool enable) {
+ RuntimeEnabledFeatures::SetLazyInitializeMediaControlsEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableClientHintsPersistent(bool enable) {
+ RuntimeEnabledFeatures::SetClientHintsPersistentEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableMediaEngagementBypassAutoplayPolicies(
+ bool enable) {
+ RuntimeEnabledFeatures::SetMediaEngagementBypassAutoplayPoliciesEnabled(
+ enable);
+}
+
+void WebRuntimeFeatures::EnableV8ContextSnapshot(bool enable) {
+ RuntimeEnabledFeatures::SetV8ContextSnapshotEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableAutomationControlled(bool enable) {
+ RuntimeEnabledFeatures::SetAutomationControlledEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableWorkStealingInScriptRunner(bool enable) {
+ RuntimeEnabledFeatures::SetWorkStealingInScriptRunnerEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableStopInBackground(bool enable) {
+ RuntimeEnabledFeatures::SetStopInBackgroundEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableStopLoadingInBackground(bool enable) {
+ RuntimeEnabledFeatures::SetStopLoadingInBackgroundEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableStopNonTimersInBackground(bool enable) {
+ RuntimeEnabledFeatures::SetStopNonTimersInBackgroundEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnablePWAFullCodeCache(bool enable) {
+ RuntimeEnabledFeatures::SetPWAFullCodeCacheEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableCodeCacheAfterExecute(bool enable) {
+ RuntimeEnabledFeatures::SetCodeCacheAfterExecuteEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableUnifiedTouchAdjustment(bool enable) {
+ RuntimeEnabledFeatures::SetUnifiedTouchAdjustmentEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableMojoBlobURLs(bool enable) {
+ RuntimeEnabledFeatures::SetMojoBlobURLsEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableOffMainThreadWebSocket(bool enable) {
+ RuntimeEnabledFeatures::SetOffMainThreadWebSocketEnabled(enable);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_scrollbar_impl.cc b/chromium/third_party/blink/renderer/platform/exported/web_scrollbar_impl.cc
new file mode 100644
index 00000000000..825beb9597d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_scrollbar_impl.cc
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2012 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/exported/web_scrollbar_impl.h"
+
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/scroll/scrollbar.h"
+
+namespace blink {
+
+WebScrollbarImpl::WebScrollbarImpl(Scrollbar* scrollbar)
+ : scrollbar_(scrollbar) {}
+
+bool WebScrollbarImpl::IsOverlay() const {
+ return scrollbar_->IsOverlayScrollbar();
+}
+
+int WebScrollbarImpl::Value() const {
+ return scrollbar_->Value();
+}
+
+WebPoint WebScrollbarImpl::Location() const {
+ return scrollbar_->Location();
+}
+
+WebSize WebScrollbarImpl::Size() const {
+ return scrollbar_->Size();
+}
+
+bool WebScrollbarImpl::Enabled() const {
+ return scrollbar_->Enabled();
+}
+
+int WebScrollbarImpl::Maximum() const {
+ return scrollbar_->Maximum();
+}
+
+int WebScrollbarImpl::TotalSize() const {
+ return scrollbar_->TotalSize();
+}
+
+bool WebScrollbarImpl::IsScrollableAreaActive() const {
+ return scrollbar_->IsScrollableAreaActive();
+}
+
+bool WebScrollbarImpl::HasTickmarks() const {
+ Vector<IntRect> tickmarks;
+ scrollbar_->GetTickmarks(tickmarks);
+
+ return !tickmarks.IsEmpty();
+}
+
+void WebScrollbarImpl::GetTickmarks(WebVector<WebRect>& web_tickmarks) const {
+ Vector<IntRect> tickmarks;
+ scrollbar_->GetTickmarks(tickmarks);
+
+ WebVector<WebRect> result(tickmarks.size());
+ for (size_t i = 0; i < tickmarks.size(); ++i)
+ result[i] = tickmarks[i];
+
+ web_tickmarks.Swap(result);
+}
+
+WebScrollbar::ScrollbarControlSize WebScrollbarImpl::GetControlSize() const {
+ return static_cast<WebScrollbar::ScrollbarControlSize>(
+ scrollbar_->GetControlSize());
+}
+
+WebScrollbar::ScrollbarPart WebScrollbarImpl::PressedPart() const {
+ return static_cast<WebScrollbar::ScrollbarPart>(scrollbar_->PressedPart());
+}
+
+WebScrollbar::ScrollbarPart WebScrollbarImpl::HoveredPart() const {
+ return static_cast<WebScrollbar::ScrollbarPart>(scrollbar_->HoveredPart());
+}
+
+WebScrollbarOverlayColorTheme WebScrollbarImpl::ScrollbarOverlayColorTheme()
+ const {
+ return static_cast<WebScrollbarOverlayColorTheme>(
+ scrollbar_->GetScrollbarOverlayColorTheme());
+}
+
+WebScrollbar::Orientation WebScrollbarImpl::GetOrientation() const {
+ return static_cast<WebScrollbar::Orientation>(scrollbar_->Orientation());
+}
+
+bool WebScrollbarImpl::IsLeftSideVerticalScrollbar() const {
+ return scrollbar_->IsLeftSideVerticalScrollbar();
+}
+
+bool WebScrollbarImpl::IsCustomScrollbar() const {
+ return scrollbar_->IsCustomScrollbar();
+}
+
+float WebScrollbarImpl::ElasticOverscroll() const {
+ return scrollbar_->ElasticOverscroll();
+}
+
+void WebScrollbarImpl::SetElasticOverscroll(float elastic_overscroll) {
+ scrollbar_->SetElasticOverscroll(elastic_overscroll);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_scrollbar_impl.h b/chromium/third_party/blink/renderer/platform/exported/web_scrollbar_impl.h
new file mode 100644
index 00000000000..b6938a6168e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_scrollbar_impl.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2012 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_EXPORTED_WEB_SCROLLBAR_IMPL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_EXPORTED_WEB_SCROLLBAR_IMPL_H_
+
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/public/platform/web_scrollbar.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+#include <memory>
+
+namespace blink {
+
+class Scrollbar;
+class PLATFORM_EXPORT WebScrollbarImpl final : public WebScrollbar {
+ USING_FAST_MALLOC(WebScrollbarImpl);
+ WTF_MAKE_NONCOPYABLE(WebScrollbarImpl);
+
+ public:
+ static std::unique_ptr<WebScrollbarImpl> Create(Scrollbar* scrollbar) {
+ return base::WrapUnique(new WebScrollbarImpl(scrollbar));
+ }
+
+ // Implement WebScrollbar methods
+ bool IsOverlay() const override;
+ int Value() const override;
+ WebPoint Location() const override;
+ WebSize Size() const override;
+ bool Enabled() const override;
+ int Maximum() const override;
+ int TotalSize() const override;
+ bool IsScrollableAreaActive() const override;
+ bool HasTickmarks() const override;
+ void GetTickmarks(WebVector<WebRect>& tickmarks) const override;
+ ScrollbarControlSize GetControlSize() const override;
+ ScrollbarPart PressedPart() const override;
+ ScrollbarPart HoveredPart() const override;
+ WebScrollbarOverlayColorTheme ScrollbarOverlayColorTheme() const override;
+ bool IsCustomScrollbar() const override;
+ Orientation GetOrientation() const override;
+ bool IsLeftSideVerticalScrollbar() const override;
+ float ElasticOverscroll() const override;
+ void SetElasticOverscroll(float) override;
+
+ private:
+ explicit WebScrollbarImpl(Scrollbar*);
+
+ // Accessed by main and compositor threads, e.g., the compositor thread
+ // checks |orientation()|.
+ //
+ // TODO: minimize or avoid cross-thread use, if possible.
+ CrossThreadPersistent<Scrollbar> scrollbar_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_scrollbar_theme_client_impl.cc b/chromium/third_party/blink/renderer/platform/exported/web_scrollbar_theme_client_impl.cc
new file mode 100644
index 00000000000..eb441771182
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_scrollbar_theme_client_impl.cc
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/exported/web_scrollbar_theme_client_impl.h"
+
+#include "third_party/blink/renderer/platform/scroll/scrollbar_theme.h"
+
+namespace blink {
+
+WebScrollbarThemeClientImpl::WebScrollbarThemeClientImpl(
+ WebScrollbar& scrollbar)
+ : scrollbar_(scrollbar) {
+ ScrollbarTheme::DeprecatedStaticGetTheme().RegisterScrollbar(*this);
+}
+
+WebScrollbarThemeClientImpl::~WebScrollbarThemeClientImpl() {
+ ScrollbarTheme::DeprecatedStaticGetTheme().UnregisterScrollbar(*this);
+}
+
+int WebScrollbarThemeClientImpl::X() const {
+ return Location().X();
+}
+
+int WebScrollbarThemeClientImpl::Y() const {
+ return Location().Y();
+}
+
+int WebScrollbarThemeClientImpl::Width() const {
+ return Size().Width();
+}
+
+int WebScrollbarThemeClientImpl::Height() const {
+ return Size().Height();
+}
+
+IntSize WebScrollbarThemeClientImpl::Size() const {
+ return scrollbar_.Size();
+}
+
+IntPoint WebScrollbarThemeClientImpl::Location() const {
+ return scrollbar_.Location();
+}
+
+void WebScrollbarThemeClientImpl::SetFrameRect(const IntRect&) {
+ // Unused by Chromium scrollbar themes.
+ NOTREACHED();
+}
+
+IntRect WebScrollbarThemeClientImpl::FrameRect() const {
+ return IntRect(Location(), Size());
+}
+
+void WebScrollbarThemeClientImpl::Invalidate() {
+ // Unused by Chromium scrollbar themes.
+ NOTREACHED();
+}
+
+void WebScrollbarThemeClientImpl::InvalidateRect(const IntRect&) {
+ // Unused by Chromium scrollbar themes.
+ NOTREACHED();
+}
+
+ScrollbarOverlayColorTheme
+WebScrollbarThemeClientImpl::GetScrollbarOverlayColorTheme() const {
+ return static_cast<ScrollbarOverlayColorTheme>(
+ scrollbar_.ScrollbarOverlayColorTheme());
+}
+
+void WebScrollbarThemeClientImpl::GetTickmarks(
+ Vector<IntRect>& tickmarks) const {
+ WebVector<WebRect> web_tickmarks;
+ scrollbar_.GetTickmarks(web_tickmarks);
+ tickmarks.resize(web_tickmarks.size());
+ for (size_t i = 0; i < web_tickmarks.size(); ++i)
+ tickmarks[i] = web_tickmarks[i];
+}
+
+bool WebScrollbarThemeClientImpl::IsScrollableAreaActive() const {
+ return scrollbar_.IsScrollableAreaActive();
+}
+
+IntPoint WebScrollbarThemeClientImpl::ConvertFromRootFrame(
+ const IntPoint& point_in_root_frame) const {
+ // Unused by Chromium scrollbar themes.
+ NOTREACHED();
+ return point_in_root_frame;
+}
+
+bool WebScrollbarThemeClientImpl::IsCustomScrollbar() const {
+ return scrollbar_.IsCustomScrollbar();
+}
+
+ScrollbarOrientation WebScrollbarThemeClientImpl::Orientation() const {
+ return static_cast<ScrollbarOrientation>(scrollbar_.GetOrientation());
+}
+
+bool WebScrollbarThemeClientImpl::IsLeftSideVerticalScrollbar() const {
+ return scrollbar_.IsLeftSideVerticalScrollbar();
+}
+
+int WebScrollbarThemeClientImpl::Value() const {
+ return scrollbar_.Value();
+}
+
+float WebScrollbarThemeClientImpl::CurrentPos() const {
+ return Value();
+}
+
+int WebScrollbarThemeClientImpl::VisibleSize() const {
+ return TotalSize() - Maximum();
+}
+
+int WebScrollbarThemeClientImpl::TotalSize() const {
+ return scrollbar_.TotalSize();
+}
+
+int WebScrollbarThemeClientImpl::Maximum() const {
+ return scrollbar_.Maximum();
+}
+
+ScrollbarControlSize WebScrollbarThemeClientImpl::GetControlSize() const {
+ return static_cast<ScrollbarControlSize>(scrollbar_.GetControlSize());
+}
+
+ScrollbarPart WebScrollbarThemeClientImpl::PressedPart() const {
+ return static_cast<ScrollbarPart>(scrollbar_.PressedPart());
+}
+
+ScrollbarPart WebScrollbarThemeClientImpl::HoveredPart() const {
+ return static_cast<ScrollbarPart>(scrollbar_.HoveredPart());
+}
+
+void WebScrollbarThemeClientImpl::StyleChanged() {
+ NOTREACHED();
+}
+
+void WebScrollbarThemeClientImpl::SetScrollbarsHiddenIfOverlay(bool hidden) {
+ NOTREACHED();
+}
+
+bool WebScrollbarThemeClientImpl::Enabled() const {
+ return scrollbar_.Enabled();
+}
+
+void WebScrollbarThemeClientImpl::SetEnabled(bool) {
+ NOTREACHED();
+}
+
+bool WebScrollbarThemeClientImpl::IsOverlayScrollbar() const {
+ return scrollbar_.IsOverlay();
+}
+
+float WebScrollbarThemeClientImpl::ElasticOverscroll() const {
+ return scrollbar_.ElasticOverscroll();
+}
+
+void WebScrollbarThemeClientImpl::SetElasticOverscroll(
+ float elastic_overscroll) {
+ return scrollbar_.SetElasticOverscroll(elastic_overscroll);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_scrollbar_theme_client_impl.h b/chromium/third_party/blink/renderer/platform/exported/web_scrollbar_theme_client_impl.h
new file mode 100644
index 00000000000..34a7d267f9f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_scrollbar_theme_client_impl.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_EXPORTED_WEB_SCROLLBAR_THEME_CLIENT_IMPL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_EXPORTED_WEB_SCROLLBAR_THEME_CLIENT_IMPL_H_
+
+#include "third_party/blink/public/platform/web_scrollbar.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/scroll/scrollbar_theme_client.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+// Adapts a WebScrollbar to the ScrollbarThemeClient interface
+class PLATFORM_EXPORT WebScrollbarThemeClientImpl
+ : public ScrollbarThemeClient {
+ DISALLOW_NEW();
+ WTF_MAKE_NONCOPYABLE(WebScrollbarThemeClientImpl);
+
+ public:
+ // Caller must retain ownership of this pointer and ensure that its lifetime
+ // exceeds this instance.
+ WebScrollbarThemeClientImpl(WebScrollbar&);
+ ~WebScrollbarThemeClientImpl() override;
+
+ // Implement ScrollbarThemeClient interface
+ int X() const override;
+ int Y() const override;
+ int Width() const override;
+ int Height() const override;
+ IntSize Size() const override;
+ IntPoint Location() const override;
+ void SetFrameRect(const IntRect&) override;
+ IntRect FrameRect() const override;
+ void Invalidate() override;
+ void InvalidateRect(const IntRect&) override;
+ ScrollbarOverlayColorTheme GetScrollbarOverlayColorTheme() const override;
+ void GetTickmarks(Vector<IntRect>&) const override;
+ bool IsScrollableAreaActive() const override;
+ IntPoint ConvertFromRootFrame(const IntPoint&) const override;
+ bool IsCustomScrollbar() const override;
+ ScrollbarOrientation Orientation() const override;
+ bool IsLeftSideVerticalScrollbar() const override;
+ int Value() const override;
+ float CurrentPos() const override;
+ int VisibleSize() const override;
+ int TotalSize() const override;
+ int Maximum() const override;
+ ScrollbarControlSize GetControlSize() const override;
+ ScrollbarPart PressedPart() const override;
+ ScrollbarPart HoveredPart() const override;
+ void StyleChanged() override;
+ void SetScrollbarsHiddenIfOverlay(bool) override;
+ bool Enabled() const override;
+ void SetEnabled(bool) override;
+ bool IsOverlayScrollbar() const override;
+ float ElasticOverscroll() const override;
+ void SetElasticOverscroll(float) override;
+
+ private:
+ WebScrollbar& scrollbar_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_EXPORTED_WEB_SCROLLBAR_THEME_CLIENT_IMPL_H_
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_scrollbar_theme_geometry_native.cc b/chromium/third_party/blink/renderer/platform/exported/web_scrollbar_theme_geometry_native.cc
new file mode 100644
index 00000000000..89c9a73b01b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_scrollbar_theme_geometry_native.cc
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/exported/web_scrollbar_theme_geometry_native.h"
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/public/platform/web_scrollbar.h"
+#include "third_party/blink/renderer/platform/exported/web_scrollbar_theme_client_impl.h"
+#include "third_party/blink/renderer/platform/scroll/scrollbar_theme.h"
+
+namespace blink {
+
+std::unique_ptr<WebScrollbarThemeGeometryNative>
+WebScrollbarThemeGeometryNative::Create(ScrollbarTheme& theme) {
+ return base::WrapUnique(new WebScrollbarThemeGeometryNative(theme));
+}
+
+WebScrollbarThemeGeometryNative::WebScrollbarThemeGeometryNative(
+ ScrollbarTheme& theme)
+ : theme_(theme) {}
+
+bool WebScrollbarThemeGeometryNative::HasButtons(WebScrollbar* scrollbar) {
+ return theme_.HasButtons(WebScrollbarThemeClientImpl(*scrollbar));
+}
+
+bool WebScrollbarThemeGeometryNative::HasThumb(WebScrollbar* scrollbar) {
+ return theme_.HasThumb(WebScrollbarThemeClientImpl(*scrollbar));
+}
+
+WebRect WebScrollbarThemeGeometryNative::TrackRect(WebScrollbar* scrollbar) {
+ return theme_.TrackRect(WebScrollbarThemeClientImpl(*scrollbar));
+}
+
+WebRect WebScrollbarThemeGeometryNative::ThumbRect(WebScrollbar* scrollbar) {
+ return theme_.ThumbRect(WebScrollbarThemeClientImpl(*scrollbar));
+}
+
+WebRect WebScrollbarThemeGeometryNative::BackButtonStartRect(
+ WebScrollbar* scrollbar) {
+ return theme_.BackButtonRect(WebScrollbarThemeClientImpl(*scrollbar),
+ kBackButtonStartPart, false);
+}
+
+WebRect WebScrollbarThemeGeometryNative::BackButtonEndRect(
+ WebScrollbar* scrollbar) {
+ return theme_.BackButtonRect(WebScrollbarThemeClientImpl(*scrollbar),
+ kBackButtonEndPart, false);
+}
+
+WebRect WebScrollbarThemeGeometryNative::ForwardButtonStartRect(
+ WebScrollbar* scrollbar) {
+ return theme_.ForwardButtonRect(WebScrollbarThemeClientImpl(*scrollbar),
+ kForwardButtonStartPart, false);
+}
+
+WebRect WebScrollbarThemeGeometryNative::ForwardButtonEndRect(
+ WebScrollbar* scrollbar) {
+ return theme_.ForwardButtonRect(WebScrollbarThemeClientImpl(*scrollbar),
+ kForwardButtonEndPart, false);
+}
+
+WebSize WebScrollbarThemeGeometryNative::NinePatchThumbCanvasSize(
+ WebScrollbar* scrollbar) {
+ DCHECK(theme_.UsesNinePatchThumbResource());
+ return theme_.NinePatchThumbCanvasSize(
+ WebScrollbarThemeClientImpl(*scrollbar));
+}
+
+WebRect WebScrollbarThemeGeometryNative::NinePatchThumbAperture(
+ WebScrollbar* scrollbar) {
+ DCHECK(theme_.UsesNinePatchThumbResource());
+ return theme_.NinePatchThumbAperture(WebScrollbarThemeClientImpl(*scrollbar));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_scrollbar_theme_geometry_native.h b/chromium/third_party/blink/renderer/platform/exported/web_scrollbar_theme_geometry_native.h
new file mode 100644
index 00000000000..b7606dea6f4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_scrollbar_theme_geometry_native.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_EXPORTED_WEB_SCROLLBAR_THEME_GEOMETRY_NATIVE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_EXPORTED_WEB_SCROLLBAR_THEME_GEOMETRY_NATIVE_H_
+
+#include <memory>
+#include "third_party/blink/public/platform/web_rect.h"
+#include "third_party/blink/public/platform/web_scrollbar_theme_geometry.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class ScrollbarTheme;
+class WebScrollbar;
+
+class PLATFORM_EXPORT WebScrollbarThemeGeometryNative
+ : public WebScrollbarThemeGeometry {
+ USING_FAST_MALLOC(WebScrollbarThemeGeometryNative);
+ WTF_MAKE_NONCOPYABLE(WebScrollbarThemeGeometryNative);
+
+ public:
+ static std::unique_ptr<WebScrollbarThemeGeometryNative> Create(
+ ScrollbarTheme&);
+
+ bool HasButtons(WebScrollbar*) override;
+ bool HasThumb(WebScrollbar*) override;
+ WebRect TrackRect(WebScrollbar*) override;
+ WebRect ThumbRect(WebScrollbar*) override;
+ WebRect BackButtonStartRect(WebScrollbar*) override;
+ WebRect BackButtonEndRect(WebScrollbar*) override;
+ WebRect ForwardButtonStartRect(WebScrollbar*) override;
+ WebRect ForwardButtonEndRect(WebScrollbar*) override;
+ WebSize NinePatchThumbCanvasSize(WebScrollbar*) override;
+ WebRect NinePatchThumbAperture(WebScrollbar*) override;
+
+ private:
+ explicit WebScrollbarThemeGeometryNative(ScrollbarTheme&);
+
+ // The theme is not owned by this class. It is assumed that the theme is a
+ // static pointer and its lifetime is essentially infinite. Only thread-safe
+ // functions on the theme can be called by this theme.
+ ScrollbarTheme& theme_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_scrollbar_theme_painter.cc b/chromium/third_party/blink/renderer/platform/exported/web_scrollbar_theme_painter.cc
new file mode 100644
index 00000000000..6d123b306d1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_scrollbar_theme_painter.cc
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/public/platform/web_scrollbar_theme_painter.h"
+
+#include "third_party/blink/public/platform/web_rect.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_record_builder.h"
+#include "third_party/blink/renderer/platform/scroll/scrollbar.h"
+#include "third_party/blink/renderer/platform/scroll/scrollbar_theme.h"
+
+namespace blink {
+
+// WebScrollbarThemeMac uses GraphicsContextCanvas which doesn't quite support
+// device clip rects not at the origin. This class translates the recording
+// canvas to the origin and then adjusts it back during playback.
+class ScopedScrollbarPainter {
+ public:
+ ScopedScrollbarPainter(WebScrollbarThemePainter* painter,
+ WebCanvas* canvas,
+ const WebRect& rect)
+ : int_rect_(IntRect(IntPoint(), IntSize(rect.width, rect.height))),
+ canvas_(canvas),
+ rect_(rect) {
+ builder_.Context().SetDeviceScaleFactor(painter->DeviceScaleFactor());
+ }
+ GraphicsContext& Context() { return builder_.Context(); }
+ const IntRect& Rect() const { return int_rect_; }
+
+ ~ScopedScrollbarPainter() {
+ canvas_->save();
+ canvas_->translate(rect_.x, rect_.y);
+ canvas_->drawPicture(builder_.EndRecording());
+ canvas_->restore();
+ }
+
+ protected:
+ IntRect int_rect_;
+ PaintRecordBuilder builder_;
+ WebCanvas* canvas_;
+ const WebRect& rect_;
+};
+
+void WebScrollbarThemePainter::Assign(const WebScrollbarThemePainter& painter) {
+ // This is a pointer to a static object, so no ownership transferral.
+ theme_ = painter.theme_;
+ scrollbar_ = painter.scrollbar_;
+ device_scale_factor_ = painter.device_scale_factor_;
+}
+
+void WebScrollbarThemePainter::Reset() {
+ scrollbar_ = nullptr;
+}
+
+void WebScrollbarThemePainter::PaintScrollbarBackground(WebCanvas* canvas,
+ const WebRect& rect) {
+ SkRect clip = SkRect::MakeXYWH(rect.x, rect.y, rect.width, rect.height);
+ canvas->clipRect(clip);
+
+ ScopedScrollbarPainter painter(this, canvas, rect);
+ theme_->PaintScrollbarBackground(painter.Context(), *scrollbar_);
+}
+
+void WebScrollbarThemePainter::PaintTrackBackground(WebCanvas* canvas,
+ const WebRect& rect) {
+ ScopedScrollbarPainter painter(this, canvas, rect);
+ theme_->PaintTrackBackground(painter.Context(), *scrollbar_, painter.Rect());
+ if (!theme_->ShouldRepaintAllPartsOnInvalidation())
+ scrollbar_->ClearTrackNeedsRepaint();
+}
+
+void WebScrollbarThemePainter::PaintBackTrackPart(WebCanvas* canvas,
+ const WebRect& rect) {
+ ScopedScrollbarPainter painter(this, canvas, rect);
+ theme_->PaintTrackPiece(painter.Context(), *scrollbar_, painter.Rect(),
+ kBackTrackPart);
+}
+
+void WebScrollbarThemePainter::PaintForwardTrackPart(WebCanvas* canvas,
+ const WebRect& rect) {
+ ScopedScrollbarPainter painter(this, canvas, rect);
+ theme_->PaintTrackPiece(painter.Context(), *scrollbar_, painter.Rect(),
+ kForwardTrackPart);
+}
+
+void WebScrollbarThemePainter::PaintBackButtonStart(WebCanvas* canvas,
+ const WebRect& rect) {
+ ScopedScrollbarPainter painter(this, canvas, rect);
+ theme_->PaintButton(painter.Context(), *scrollbar_, painter.Rect(),
+ kBackButtonStartPart);
+}
+
+void WebScrollbarThemePainter::PaintBackButtonEnd(WebCanvas* canvas,
+ const WebRect& rect) {
+ ScopedScrollbarPainter painter(this, canvas, rect);
+ theme_->PaintButton(painter.Context(), *scrollbar_, painter.Rect(),
+ kBackButtonEndPart);
+}
+
+void WebScrollbarThemePainter::PaintForwardButtonStart(WebCanvas* canvas,
+ const WebRect& rect) {
+ ScopedScrollbarPainter painter(this, canvas, rect);
+ theme_->PaintButton(painter.Context(), *scrollbar_, painter.Rect(),
+ kForwardButtonStartPart);
+}
+
+void WebScrollbarThemePainter::PaintForwardButtonEnd(WebCanvas* canvas,
+ const WebRect& rect) {
+ ScopedScrollbarPainter painter(this, canvas, rect);
+ theme_->PaintButton(painter.Context(), *scrollbar_, painter.Rect(),
+ kForwardButtonEndPart);
+}
+
+void WebScrollbarThemePainter::PaintTickmarks(WebCanvas* canvas,
+ const WebRect& rect) {
+ ScopedScrollbarPainter painter(this, canvas, rect);
+ theme_->PaintTickmarks(painter.Context(), *scrollbar_, painter.Rect());
+}
+
+void WebScrollbarThemePainter::PaintThumb(WebCanvas* canvas,
+ const WebRect& rect) {
+ ScopedScrollbarPainter painter(this, canvas, rect);
+ theme_->PaintThumb(painter.Context(), *scrollbar_, painter.Rect());
+ if (!theme_->ShouldRepaintAllPartsOnInvalidation())
+ scrollbar_->ClearThumbNeedsRepaint();
+}
+
+WebScrollbarThemePainter::WebScrollbarThemePainter(ScrollbarTheme& theme,
+ Scrollbar& scrollbar,
+ float device_scale_factor)
+ : theme_(&theme),
+ scrollbar_(&scrollbar),
+ device_scale_factor_(device_scale_factor) {}
+
+float WebScrollbarThemePainter::ThumbOpacity() const {
+ return theme_->ThumbOpacity(*scrollbar_);
+}
+
+bool WebScrollbarThemePainter::TrackNeedsRepaint() const {
+ return scrollbar_->TrackNeedsRepaint();
+}
+
+bool WebScrollbarThemePainter::ThumbNeedsRepaint() const {
+ return scrollbar_->ThumbNeedsRepaint();
+}
+
+bool WebScrollbarThemePainter::UsesNinePatchThumbResource() const {
+ return theme_->UsesNinePatchThumbResource();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_security_origin.cc b/chromium/third_party/blink/renderer/platform/exported/web_security_origin.cc
new file mode 100644
index 00000000000..003b6a1e754
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_security_origin.cc
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2010 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 "third_party/blink/public/platform/web_security_origin.h"
+
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/platform/web_url.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+
+namespace blink {
+
+WebSecurityOrigin WebSecurityOrigin::CreateFromString(const WebString& origin) {
+ return WebSecurityOrigin(SecurityOrigin::CreateFromString(origin));
+}
+
+WebSecurityOrigin WebSecurityOrigin::Create(const WebURL& url) {
+ return WebSecurityOrigin(SecurityOrigin::Create(url));
+}
+
+WebSecurityOrigin WebSecurityOrigin::CreateUnique() {
+ return WebSecurityOrigin(SecurityOrigin::CreateUnique());
+}
+
+void WebSecurityOrigin::Reset() {
+ private_ = nullptr;
+}
+
+void WebSecurityOrigin::Assign(const WebSecurityOrigin& other) {
+ private_ = other.private_;
+}
+
+WebString WebSecurityOrigin::Protocol() const {
+ DCHECK(private_);
+ return private_->Protocol();
+}
+
+WebString WebSecurityOrigin::Host() const {
+ DCHECK(private_);
+ return private_->Host();
+}
+
+unsigned short WebSecurityOrigin::Port() const {
+ DCHECK(private_);
+ return private_->Port();
+}
+
+unsigned short WebSecurityOrigin::EffectivePort() const {
+ DCHECK(private_);
+ return private_->EffectivePort();
+}
+
+bool WebSecurityOrigin::IsUnique() const {
+ DCHECK(private_);
+ return private_->IsUnique();
+}
+
+bool WebSecurityOrigin::CanAccess(const WebSecurityOrigin& other) const {
+ DCHECK(private_);
+ DCHECK(other.private_);
+ return private_->CanAccess(other.private_.Get());
+}
+
+bool WebSecurityOrigin::CanRequest(const WebURL& url) const {
+ DCHECK(private_);
+ return private_->CanRequest(url);
+}
+
+bool WebSecurityOrigin::IsPotentiallyTrustworthy() const {
+ DCHECK(private_);
+ return private_->IsPotentiallyTrustworthy();
+}
+
+WebString WebSecurityOrigin::ToString() const {
+ DCHECK(private_);
+ return private_->ToString();
+}
+
+bool WebSecurityOrigin::CanAccessPasswordManager() const {
+ DCHECK(private_);
+ return private_->CanAccessPasswordManager();
+}
+
+WebSecurityOrigin::WebSecurityOrigin(scoped_refptr<const SecurityOrigin> origin)
+ : private_(std::move(origin)) {}
+
+WebSecurityOrigin& WebSecurityOrigin::operator=(
+ scoped_refptr<const SecurityOrigin> origin) {
+ private_ = std::move(origin);
+ return *this;
+}
+
+WebSecurityOrigin::operator scoped_refptr<const SecurityOrigin>() const {
+ return private_.Get();
+}
+
+const SecurityOrigin* WebSecurityOrigin::Get() const {
+ return private_.Get();
+}
+
+WebSecurityOrigin::WebSecurityOrigin(const url::Origin& origin) {
+ *this = SecurityOrigin::CreateFromUrlOrigin(origin);
+}
+
+WebSecurityOrigin::operator url::Origin() const {
+ return Get()->ToUrlOrigin();
+}
+
+#if DCHECK_IS_ON()
+bool WebSecurityOrigin::operator==(const WebSecurityOrigin& other) const {
+ return Get() == other.Get();
+}
+#endif
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_service_worker_installed_scripts_manager.cc b/chromium/third_party/blink/renderer/platform/exported/web_service_worker_installed_scripts_manager.cc
new file mode 100644
index 00000000000..7ed3c333392
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_service_worker_installed_scripts_manager.cc
@@ -0,0 +1,54 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/modules/serviceworker/web_service_worker_installed_scripts_manager.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+
+namespace blink {
+
+// static
+std::unique_ptr<WebServiceWorkerInstalledScriptsManager::RawScriptData>
+WebServiceWorkerInstalledScriptsManager::RawScriptData::Create(
+ WebString encoding,
+ WebVector<BytesChunk> script_text,
+ WebVector<BytesChunk> meta_data) {
+ return base::WrapUnique(
+ new RawScriptData(std::move(encoding), std::move(script_text),
+ std::move(meta_data), true /* is_valid */));
+}
+
+// static
+std::unique_ptr<WebServiceWorkerInstalledScriptsManager::RawScriptData>
+WebServiceWorkerInstalledScriptsManager::RawScriptData::
+ CreateInvalidInstance() {
+ return base::WrapUnique(
+ new RawScriptData(WebString() /* encoding */, WebVector<BytesChunk>(),
+ WebVector<BytesChunk>(), false /* is_valid */));
+}
+
+WebServiceWorkerInstalledScriptsManager::RawScriptData::RawScriptData(
+ WebString encoding,
+ WebVector<BytesChunk> script_text,
+ WebVector<BytesChunk> meta_data,
+ bool is_valid)
+ : is_valid_(is_valid),
+ encoding_(std::move(encoding)),
+ script_text_(std::move(script_text)),
+ meta_data_(std::move(meta_data)),
+ headers_(std::make_unique<CrossThreadHTTPHeaderMapData>()) {}
+
+WebServiceWorkerInstalledScriptsManager::RawScriptData::~RawScriptData() =
+ default;
+
+void WebServiceWorkerInstalledScriptsManager::RawScriptData::AddHeader(
+ const WebString& key,
+ const WebString& value) {
+ headers_->emplace_back(key, value);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_service_worker_request.cc b/chromium/third_party/blink/renderer/platform/exported/web_service_worker_request.cc
new file mode 100644
index 00000000000..4d9a026cacb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_service_worker_request.cc
@@ -0,0 +1,248 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/modules/serviceworker/web_service_worker_request.h"
+
+#include "third_party/blink/public/platform/web_http_header_visitor.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/platform/web_url_request.h"
+#include "third_party/blink/renderer/platform/blob/blob_data.h"
+#include "third_party/blink/renderer/platform/network/encoded_form_data.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+
+namespace blink {
+
+class WebServiceWorkerRequestPrivate
+ : public RefCounted<WebServiceWorkerRequestPrivate> {
+ public:
+ WebURL url_;
+ WebString method_;
+ HTTPHeaderMap headers_;
+ scoped_refptr<EncodedFormData> http_body;
+ scoped_refptr<BlobDataHandle> blob_data_handle;
+ Referrer referrer_;
+ network::mojom::FetchRequestMode mode_ =
+ network::mojom::FetchRequestMode::kNoCORS;
+ bool is_main_resource_load_ = false;
+ network::mojom::FetchCredentialsMode credentials_mode_ =
+ network::mojom::FetchCredentialsMode::kOmit;
+ mojom::FetchCacheMode cache_mode_ = mojom::FetchCacheMode::kDefault;
+ network::mojom::FetchRedirectMode redirect_mode_ =
+ network::mojom::FetchRedirectMode::kFollow;
+ WebURLRequest::RequestContext request_context_ =
+ WebURLRequest::kRequestContextUnspecified;
+ network::mojom::RequestContextFrameType frame_type_ =
+ network::mojom::RequestContextFrameType::kNone;
+ WebString integrity_;
+ bool keepalive_ = false;
+ WebString client_id_;
+ bool is_reload_ = false;
+};
+
+WebServiceWorkerRequest::WebServiceWorkerRequest()
+ : private_(base::AdoptRef(new WebServiceWorkerRequestPrivate)) {}
+
+void WebServiceWorkerRequest::Reset() {
+ private_.Reset();
+}
+
+void WebServiceWorkerRequest::Assign(const WebServiceWorkerRequest& other) {
+ private_ = other.private_;
+}
+
+void WebServiceWorkerRequest::SetURL(const WebURL& url) {
+ private_->url_ = url;
+}
+
+const WebString& WebServiceWorkerRequest::Integrity() const {
+ return private_->integrity_;
+}
+
+bool WebServiceWorkerRequest::Keepalive() const {
+ return private_->keepalive_;
+}
+
+const WebURL& WebServiceWorkerRequest::Url() const {
+ return private_->url_;
+}
+
+void WebServiceWorkerRequest::SetMethod(const WebString& method) {
+ private_->method_ = method;
+}
+
+const WebString& WebServiceWorkerRequest::Method() const {
+ return private_->method_;
+}
+
+void WebServiceWorkerRequest::SetHeader(const WebString& key,
+ const WebString& value) {
+ if (DeprecatedEqualIgnoringCase(key, "referer"))
+ return;
+ private_->headers_.Set(key, value);
+}
+
+void WebServiceWorkerRequest::AppendHeader(const WebString& key,
+ const WebString& value) {
+ if (DeprecatedEqualIgnoringCase(key, "referer"))
+ return;
+ HTTPHeaderMap::AddResult result = private_->headers_.Add(key, value);
+ if (!result.is_new_entry)
+ result.stored_value->value =
+ result.stored_value->value + ", " + String(value);
+}
+
+void WebServiceWorkerRequest::VisitHTTPHeaderFields(
+ WebHTTPHeaderVisitor* header_visitor) const {
+ for (HTTPHeaderMap::const_iterator i = private_->headers_.begin(),
+ end = private_->headers_.end();
+ i != end; ++i)
+ header_visitor->VisitHeader(i->key, i->value);
+}
+
+const HTTPHeaderMap& WebServiceWorkerRequest::Headers() const {
+ return private_->headers_;
+}
+
+void WebServiceWorkerRequest::SetBody(const WebHTTPBody& body) {
+ private_->http_body = body;
+}
+
+WebHTTPBody WebServiceWorkerRequest::Body() const {
+ return private_->http_body;
+}
+
+void WebServiceWorkerRequest::SetBlob(const WebString& uuid,
+ long long size,
+ mojo::ScopedMessagePipeHandle blob_pipe) {
+ SetBlob(uuid, size,
+ mojom::blink::BlobPtrInfo(std::move(blob_pipe),
+ mojom::blink::Blob::Version_));
+}
+
+void WebServiceWorkerRequest::SetBlob(const WebString& uuid,
+ long long size,
+ mojom::blink::BlobPtrInfo blob_info) {
+ private_->blob_data_handle =
+ BlobDataHandle::Create(uuid, String(), size, std::move(blob_info));
+}
+
+scoped_refptr<BlobDataHandle> WebServiceWorkerRequest::GetBlobDataHandle()
+ const {
+ return private_->blob_data_handle;
+}
+
+void WebServiceWorkerRequest::SetReferrer(const WebString& web_referrer,
+ WebReferrerPolicy referrer_policy) {
+ // WebString doesn't have the distinction between empty and null. We use
+ // the null WTFString for referrer.
+ DCHECK_EQ(Referrer::NoReferrer(), String());
+ String referrer =
+ web_referrer.IsEmpty() ? Referrer::NoReferrer() : String(web_referrer);
+ private_->referrer_ =
+ Referrer(referrer, static_cast<ReferrerPolicy>(referrer_policy));
+}
+
+WebURL WebServiceWorkerRequest::ReferrerUrl() const {
+ return KURL(private_->referrer_.referrer);
+}
+
+WebReferrerPolicy WebServiceWorkerRequest::GetReferrerPolicy() const {
+ return static_cast<WebReferrerPolicy>(private_->referrer_.referrer_policy);
+}
+
+const Referrer& WebServiceWorkerRequest::GetReferrer() const {
+ return private_->referrer_;
+}
+
+void WebServiceWorkerRequest::SetMode(network::mojom::FetchRequestMode mode) {
+ private_->mode_ = mode;
+}
+
+network::mojom::FetchRequestMode WebServiceWorkerRequest::Mode() const {
+ return private_->mode_;
+}
+
+void WebServiceWorkerRequest::SetIsMainResourceLoad(
+ bool is_main_resource_load) {
+ private_->is_main_resource_load_ = is_main_resource_load;
+}
+
+bool WebServiceWorkerRequest::IsMainResourceLoad() const {
+ return private_->is_main_resource_load_;
+}
+
+void WebServiceWorkerRequest::SetCredentialsMode(
+ network::mojom::FetchCredentialsMode credentials_mode) {
+ private_->credentials_mode_ = credentials_mode;
+}
+
+void WebServiceWorkerRequest::SetIntegrity(const WebString& integrity) {
+ private_->integrity_ = integrity;
+}
+
+void WebServiceWorkerRequest::SetKeepalive(bool keepalive) {
+ private_->keepalive_ = keepalive;
+}
+
+network::mojom::FetchCredentialsMode WebServiceWorkerRequest::CredentialsMode()
+ const {
+ return private_->credentials_mode_;
+}
+
+void WebServiceWorkerRequest::SetCacheMode(mojom::FetchCacheMode cache_mode) {
+ private_->cache_mode_ = cache_mode;
+}
+
+mojom::FetchCacheMode WebServiceWorkerRequest::CacheMode() const {
+ return private_->cache_mode_;
+}
+
+void WebServiceWorkerRequest::SetRedirectMode(
+ network::mojom::FetchRedirectMode redirect_mode) {
+ private_->redirect_mode_ = redirect_mode;
+}
+
+network::mojom::FetchRedirectMode WebServiceWorkerRequest::RedirectMode()
+ const {
+ return private_->redirect_mode_;
+}
+
+void WebServiceWorkerRequest::SetRequestContext(
+ WebURLRequest::RequestContext request_context) {
+ private_->request_context_ = request_context;
+}
+
+WebURLRequest::RequestContext WebServiceWorkerRequest::GetRequestContext()
+ const {
+ return private_->request_context_;
+}
+
+void WebServiceWorkerRequest::SetFrameType(
+ network::mojom::RequestContextFrameType frame_type) {
+ private_->frame_type_ = frame_type;
+}
+
+network::mojom::RequestContextFrameType WebServiceWorkerRequest::GetFrameType()
+ const {
+ return private_->frame_type_;
+}
+
+void WebServiceWorkerRequest::SetClientId(const WebString& client_id) {
+ private_->client_id_ = client_id;
+}
+
+const WebString& WebServiceWorkerRequest::ClientId() const {
+ return private_->client_id_;
+}
+
+void WebServiceWorkerRequest::SetIsReload(bool is_reload) {
+ private_->is_reload_ = is_reload;
+}
+
+bool WebServiceWorkerRequest::IsReload() const {
+ return private_->is_reload_;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_service_worker_response.cc b/chromium/third_party/blink/renderer/platform/exported/web_service_worker_response.cc
new file mode 100644
index 00000000000..90cf2254a39
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_service_worker_response.cc
@@ -0,0 +1,222 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/modules/serviceworker/web_service_worker_response.h"
+
+#include "third_party/blink/public/platform/web_http_header_visitor.h"
+#include "third_party/blink/renderer/platform/blob/blob_data.h"
+#include "third_party/blink/renderer/platform/network/http_header_map.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+class WebServiceWorkerResponsePrivate
+ : public RefCounted<WebServiceWorkerResponsePrivate> {
+ public:
+ WebServiceWorkerResponsePrivate()
+ : status(0),
+ response_type(network::mojom::FetchResponseType::kDefault),
+ error(mojom::ServiceWorkerResponseError::kUnknown) {}
+ WebVector<WebURL> url_list;
+ unsigned short status;
+ WebString status_text;
+ network::mojom::FetchResponseType response_type;
+ HTTPHeaderMap headers;
+ scoped_refptr<BlobDataHandle> blob_data_handle;
+ mojom::ServiceWorkerResponseError error;
+ Time response_time;
+ WebString cache_storage_cache_name;
+ WebVector<WebString> cors_exposed_header_names;
+ scoped_refptr<BlobDataHandle> side_data_blob_data_handle;
+};
+
+WebServiceWorkerResponse::WebServiceWorkerResponse()
+ : private_(base::AdoptRef(new WebServiceWorkerResponsePrivate)) {}
+
+void WebServiceWorkerResponse::Reset() {
+ private_.Reset();
+}
+
+void WebServiceWorkerResponse::Assign(const WebServiceWorkerResponse& other) {
+ private_ = other.private_;
+}
+
+void WebServiceWorkerResponse::SetURLList(const WebVector<WebURL>& url_list) {
+ private_->url_list = url_list;
+}
+
+const WebVector<WebURL>& WebServiceWorkerResponse::UrlList() const {
+ return private_->url_list;
+}
+
+void WebServiceWorkerResponse::SetStatus(unsigned short status) {
+ private_->status = status;
+}
+
+unsigned short WebServiceWorkerResponse::Status() const {
+ return private_->status;
+}
+
+void WebServiceWorkerResponse::SetStatusText(const WebString& status_text) {
+ private_->status_text = status_text;
+}
+
+const WebString& WebServiceWorkerResponse::StatusText() const {
+ return private_->status_text;
+}
+
+void WebServiceWorkerResponse::SetResponseType(
+ network::mojom::FetchResponseType response_type) {
+ private_->response_type = response_type;
+}
+
+network::mojom::FetchResponseType WebServiceWorkerResponse::ResponseType()
+ const {
+ return private_->response_type;
+}
+
+void WebServiceWorkerResponse::SetHeader(const WebString& key,
+ const WebString& value) {
+ private_->headers.Set(key, value);
+}
+
+void WebServiceWorkerResponse::AppendHeader(const WebString& key,
+ const WebString& value) {
+ HTTPHeaderMap::AddResult add_result = private_->headers.Add(key, value);
+ if (!add_result.is_new_entry)
+ add_result.stored_value->value =
+ add_result.stored_value->value + ", " + String(value);
+}
+
+WebVector<WebString> WebServiceWorkerResponse::GetHeaderKeys() const {
+ Vector<String> keys;
+ for (HTTPHeaderMap::const_iterator it = private_->headers.begin(),
+ end = private_->headers.end();
+ it != end; ++it)
+ keys.push_back(it->key);
+
+ return keys;
+}
+
+WebString WebServiceWorkerResponse::GetHeader(const WebString& key) const {
+ return private_->headers.Get(key);
+}
+
+void WebServiceWorkerResponse::VisitHTTPHeaderFields(
+ WebHTTPHeaderVisitor* header_visitor) const {
+ for (HTTPHeaderMap::const_iterator i = private_->headers.begin(),
+ end = private_->headers.end();
+ i != end; ++i)
+ header_visitor->VisitHeader(i->key, i->value);
+}
+
+void WebServiceWorkerResponse::SetBlob(
+ const WebString& uuid,
+ uint64_t size,
+ mojo::ScopedMessagePipeHandle blob_pipe) {
+ private_->blob_data_handle = BlobDataHandle::Create(
+ uuid, String(), size,
+ mojom::blink::BlobPtrInfo(std::move(blob_pipe),
+ mojom::blink::Blob::Version_));
+}
+
+WebString WebServiceWorkerResponse::BlobUUID() const {
+ if (!private_->blob_data_handle)
+ return WebString();
+ return private_->blob_data_handle->Uuid();
+}
+
+uint64_t WebServiceWorkerResponse::BlobSize() const {
+ if (!private_->blob_data_handle)
+ return 0;
+ return private_->blob_data_handle->size();
+}
+
+mojo::ScopedMessagePipeHandle WebServiceWorkerResponse::CloneBlobPtr() const {
+ if (!private_->blob_data_handle)
+ return mojo::ScopedMessagePipeHandle();
+ return private_->blob_data_handle->CloneBlobPtr()
+ .PassInterface()
+ .PassHandle();
+}
+
+void WebServiceWorkerResponse::SetError(
+ mojom::ServiceWorkerResponseError error) {
+ private_->error = error;
+}
+
+mojom::ServiceWorkerResponseError WebServiceWorkerResponse::GetError() const {
+ return private_->error;
+}
+
+void WebServiceWorkerResponse::SetResponseTime(base::Time time) {
+ private_->response_time = time;
+}
+
+base::Time WebServiceWorkerResponse::ResponseTime() const {
+ return private_->response_time;
+}
+
+void WebServiceWorkerResponse::SetCacheStorageCacheName(
+ const WebString& cache_storage_cache_name) {
+ private_->cache_storage_cache_name = cache_storage_cache_name;
+}
+
+const WebString& WebServiceWorkerResponse::CacheStorageCacheName() const {
+ return private_->cache_storage_cache_name;
+}
+
+void WebServiceWorkerResponse::SetCorsExposedHeaderNames(
+ const WebVector<WebString>& header_names) {
+ private_->cors_exposed_header_names = header_names;
+}
+
+const WebVector<WebString>& WebServiceWorkerResponse::CorsExposedHeaderNames()
+ const {
+ return private_->cors_exposed_header_names;
+}
+
+const HTTPHeaderMap& WebServiceWorkerResponse::Headers() const {
+ return private_->headers;
+}
+
+void WebServiceWorkerResponse::SetBlobDataHandle(
+ scoped_refptr<BlobDataHandle> blob_data_handle) {
+ private_->blob_data_handle = std::move(blob_data_handle);
+}
+
+scoped_refptr<BlobDataHandle> WebServiceWorkerResponse::GetBlobDataHandle()
+ const {
+ return private_->blob_data_handle;
+}
+
+void WebServiceWorkerResponse::SetSideDataBlobDataHandle(
+ scoped_refptr<BlobDataHandle> blob_data_handle) {
+ private_->side_data_blob_data_handle = std::move(blob_data_handle);
+}
+
+WebString WebServiceWorkerResponse::SideDataBlobUUID() const {
+ if (!private_->side_data_blob_data_handle)
+ return WebString();
+ return private_->side_data_blob_data_handle->Uuid();
+}
+
+uint64_t WebServiceWorkerResponse::SideDataBlobSize() const {
+ if (!private_->side_data_blob_data_handle)
+ return 0;
+ return private_->side_data_blob_data_handle->size();
+}
+
+mojo::ScopedMessagePipeHandle WebServiceWorkerResponse::CloneSideDataBlobPtr()
+ const {
+ if (!private_->side_data_blob_data_handle)
+ return mojo::ScopedMessagePipeHandle();
+ return private_->side_data_blob_data_handle->CloneBlobPtr()
+ .PassInterface()
+ .PassHandle();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_service_worker_stream_handle.cc b/chromium/third_party/blink/renderer/platform/exported/web_service_worker_stream_handle.cc
new file mode 100644
index 00000000000..fa0b64f3b8e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_service_worker_stream_handle.cc
@@ -0,0 +1,31 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/modules/serviceworker/web_service_worker_stream_handle.h"
+
+namespace blink {
+
+void WebServiceWorkerStreamHandle::SetListener(
+ std::unique_ptr<Listener> listener) {
+ DCHECK(!listener_);
+ listener_ = std::move(listener);
+}
+
+void WebServiceWorkerStreamHandle::Aborted() {
+ DCHECK(listener_);
+ listener_->OnAborted();
+}
+
+void WebServiceWorkerStreamHandle::Completed() {
+ DCHECK(listener_);
+ listener_->OnCompleted();
+}
+
+mojo::ScopedDataPipeConsumerHandle
+WebServiceWorkerStreamHandle::DrainStreamDataPipe() {
+ DCHECK(stream_.is_valid());
+ return std::move(stream_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_speech_synthesis_utterance.cc b/chromium/third_party/blink/renderer/platform/exported/web_speech_synthesis_utterance.cc
new file mode 100644
index 00000000000..c837477e80b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_speech_synthesis_utterance.cc
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2013 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:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/public/platform/web_speech_synthesis_utterance.h"
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/speech/platform_speech_synthesis_utterance.h"
+
+namespace blink {
+
+WebSpeechSynthesisUtterance::WebSpeechSynthesisUtterance(
+ PlatformSpeechSynthesisUtterance* utterance)
+ : private_(utterance) {}
+
+WebSpeechSynthesisUtterance& WebSpeechSynthesisUtterance::operator=(
+ PlatformSpeechSynthesisUtterance* utterance) {
+ private_ = utterance;
+ return *this;
+}
+
+void WebSpeechSynthesisUtterance::Assign(
+ const WebSpeechSynthesisUtterance& other) {
+ private_ = other.private_;
+}
+
+void WebSpeechSynthesisUtterance::Reset() {
+ private_.Reset();
+}
+
+WebSpeechSynthesisUtterance::operator PlatformSpeechSynthesisUtterance*()
+ const {
+ return private_.Get();
+}
+
+WebString WebSpeechSynthesisUtterance::GetText() const {
+ return private_->GetText();
+}
+
+WebString WebSpeechSynthesisUtterance::Lang() const {
+ return private_->Lang();
+}
+
+WebString WebSpeechSynthesisUtterance::Voice() const {
+ return private_->Voice() ? WebString(private_->Voice()->GetName())
+ : WebString();
+}
+
+float WebSpeechSynthesisUtterance::Volume() const {
+ return private_->Volume();
+}
+
+float WebSpeechSynthesisUtterance::Rate() const {
+ return private_->Rate();
+}
+
+float WebSpeechSynthesisUtterance::Pitch() const {
+ return private_->Pitch();
+}
+
+double WebSpeechSynthesisUtterance::StartTime() const {
+ return private_->StartTime();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_speech_synthesis_voice.cc b/chromium/third_party/blink/renderer/platform/exported/web_speech_synthesis_voice.cc
new file mode 100644
index 00000000000..62031d8b6a2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_speech_synthesis_voice.cc
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2013 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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 "third_party/blink/public/platform/web_speech_synthesis_voice.h"
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/speech/platform_speech_synthesis_voice.h"
+
+namespace blink {
+
+WebSpeechSynthesisVoice::WebSpeechSynthesisVoice()
+ : private_(PlatformSpeechSynthesisVoice::Create()) {}
+
+void WebSpeechSynthesisVoice::Assign(const WebSpeechSynthesisVoice& other) {
+ private_ = other.private_;
+}
+
+void WebSpeechSynthesisVoice::Reset() {
+ private_.Reset();
+}
+
+void WebSpeechSynthesisVoice::SetVoiceURI(const WebString& voice_uri) {
+ private_->SetVoiceURI(voice_uri);
+}
+
+void WebSpeechSynthesisVoice::SetName(const WebString& name) {
+ private_->SetName(name);
+}
+
+void WebSpeechSynthesisVoice::SetLanguage(const WebString& language) {
+ private_->SetLang(language);
+}
+
+void WebSpeechSynthesisVoice::SetIsLocalService(bool is_local_service) {
+ private_->SetLocalService(is_local_service);
+}
+
+void WebSpeechSynthesisVoice::SetIsDefault(bool is_default) {
+ private_->SetIsDefault(is_default);
+}
+
+WebSpeechSynthesisVoice::operator PlatformSpeechSynthesisVoice*() const {
+ return private_.Get();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_speech_synthesizer_client_impl.cc b/chromium/third_party/blink/renderer/platform/exported/web_speech_synthesizer_client_impl.cc
new file mode 100644
index 00000000000..53efa1319b6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_speech_synthesizer_client_impl.cc
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2013 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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 "third_party/blink/renderer/platform/exported/web_speech_synthesizer_client_impl.h"
+
+#include "third_party/blink/renderer/platform/speech/platform_speech_synthesis_utterance.h"
+
+namespace blink {
+
+WebSpeechSynthesizerClientImpl::WebSpeechSynthesizerClientImpl(
+ PlatformSpeechSynthesizer* synthesizer,
+ PlatformSpeechSynthesizerClient* client)
+ : synthesizer_(synthesizer), client_(client) {}
+
+WebSpeechSynthesizerClientImpl::~WebSpeechSynthesizerClientImpl() = default;
+
+void WebSpeechSynthesizerClientImpl::SetVoiceList(
+ const WebVector<WebSpeechSynthesisVoice>& voices) {
+ Vector<scoped_refptr<PlatformSpeechSynthesisVoice>> out_voices;
+ for (size_t i = 0; i < voices.size(); i++)
+ out_voices.push_back(voices[i]);
+ synthesizer_->SetVoiceList(out_voices);
+ client_->VoicesDidChange();
+}
+
+void WebSpeechSynthesizerClientImpl::DidStartSpeaking(
+ const WebSpeechSynthesisUtterance& utterance) {
+ client_->DidStartSpeaking(utterance);
+}
+
+void WebSpeechSynthesizerClientImpl::DidFinishSpeaking(
+ const WebSpeechSynthesisUtterance& utterance) {
+ client_->DidFinishSpeaking(utterance);
+}
+
+void WebSpeechSynthesizerClientImpl::DidPauseSpeaking(
+ const WebSpeechSynthesisUtterance& utterance) {
+ client_->DidPauseSpeaking(utterance);
+}
+
+void WebSpeechSynthesizerClientImpl::DidResumeSpeaking(
+ const WebSpeechSynthesisUtterance& utterance) {
+ client_->DidResumeSpeaking(utterance);
+}
+
+void WebSpeechSynthesizerClientImpl::SpeakingErrorOccurred(
+ const WebSpeechSynthesisUtterance& utterance) {
+ client_->SpeakingErrorOccurred(utterance);
+}
+
+void WebSpeechSynthesizerClientImpl::WordBoundaryEventOccurred(
+ const WebSpeechSynthesisUtterance& utterance,
+ unsigned char_index) {
+ client_->BoundaryEventOccurred(utterance, kSpeechWordBoundary, char_index);
+}
+
+void WebSpeechSynthesizerClientImpl::SentenceBoundaryEventOccurred(
+ const WebSpeechSynthesisUtterance& utterance,
+ unsigned char_index) {
+ client_->BoundaryEventOccurred(utterance, kSpeechSentenceBoundary,
+ char_index);
+}
+
+void WebSpeechSynthesizerClientImpl::Trace(blink::Visitor* visitor) {
+ visitor->Trace(synthesizer_);
+ visitor->Trace(client_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_speech_synthesizer_client_impl.h b/chromium/third_party/blink/renderer/platform/exported/web_speech_synthesizer_client_impl.h
new file mode 100644
index 00000000000..e3ef80ee256
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_speech_synthesizer_client_impl.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2013 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_EXPORTED_WEB_SPEECH_SYNTHESIZER_CLIENT_IMPL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_EXPORTED_WEB_SPEECH_SYNTHESIZER_CLIENT_IMPL_H_
+
+#include "third_party/blink/public/platform/web_speech_synthesis_utterance.h"
+#include "third_party/blink/public/platform/web_speech_synthesis_voice.h"
+#include "third_party/blink/public/platform/web_speech_synthesizer_client.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/speech/platform_speech_synthesizer.h"
+
+namespace blink {
+
+class PlatformSpeechSynthesizer;
+class PlatformSpeechSynthesizerClient;
+
+class WebSpeechSynthesizerClientImpl final
+ : public GarbageCollectedFinalized<WebSpeechSynthesizerClientImpl>,
+ public WebSpeechSynthesizerClient {
+ public:
+ WebSpeechSynthesizerClientImpl(PlatformSpeechSynthesizer*,
+ PlatformSpeechSynthesizerClient*);
+ virtual ~WebSpeechSynthesizerClientImpl();
+
+ virtual void SetVoiceList(const WebVector<WebSpeechSynthesisVoice>& voices);
+ virtual void DidStartSpeaking(const WebSpeechSynthesisUtterance&);
+ virtual void DidFinishSpeaking(const WebSpeechSynthesisUtterance&);
+ virtual void DidPauseSpeaking(const WebSpeechSynthesisUtterance&);
+ virtual void DidResumeSpeaking(const WebSpeechSynthesisUtterance&);
+ virtual void SpeakingErrorOccurred(const WebSpeechSynthesisUtterance&);
+ virtual void WordBoundaryEventOccurred(const WebSpeechSynthesisUtterance&,
+ unsigned char_index);
+ virtual void SentenceBoundaryEventOccurred(const WebSpeechSynthesisUtterance&,
+ unsigned char_index);
+
+ void Trace(blink::Visitor*);
+
+ private:
+ Member<PlatformSpeechSynthesizer> synthesizer_;
+ Member<PlatformSpeechSynthesizerClient> client_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_EXPORTED_WEB_SPEECH_SYNTHESIZER_CLIENT_IMPL_H_
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_string.cc b/chromium/third_party/blink/renderer/platform/exported/web_string.cc
new file mode 100644
index 00000000000..cddf917e560
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_string.cc
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/public/platform/web_string.h"
+
+#include "base/strings/string_util.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/text/ascii_fast_path.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_view.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+STATIC_ASSERT_ENUM(WTF::kLenientUTF8Conversion,
+ blink::WebString::UTF8ConversionMode::kLenient);
+STATIC_ASSERT_ENUM(WTF::kStrictUTF8Conversion,
+ blink::WebString::UTF8ConversionMode::kStrict);
+STATIC_ASSERT_ENUM(
+ WTF::kStrictUTF8ConversionReplacingUnpairedSurrogatesWithFFFD,
+ blink::WebString::UTF8ConversionMode::kStrictReplacingErrorsWithFFFD);
+
+namespace blink {
+
+WebString::~WebString() = default;
+WebString::WebString() = default;
+WebString::WebString(const WebString&) = default;
+WebString::WebString(WebString&&) = default;
+WebString& WebString::operator=(const WebString&) = default;
+WebString& WebString::operator=(WebString&&) = default;
+
+WebString::WebString(const WebUChar* data, size_t len)
+ : impl_(StringImpl::Create8BitIfPossible(data, len)) {}
+
+void WebString::Reset() {
+ impl_ = nullptr;
+}
+
+size_t WebString::length() const {
+ return impl_ ? impl_->length() : 0;
+}
+
+bool WebString::Is8Bit() const {
+ return impl_->Is8Bit();
+}
+
+const WebLChar* WebString::Data8() const {
+ return impl_ && Is8Bit() ? impl_->Characters8() : nullptr;
+}
+
+const WebUChar* WebString::Data16() const {
+ return impl_ && !Is8Bit() ? impl_->Characters16() : nullptr;
+}
+
+std::string WebString::Utf8(UTF8ConversionMode mode) const {
+ StringUTF8Adaptor utf8(impl_.get(),
+ static_cast<WTF::UTF8ConversionMode>(mode));
+ return std::string(utf8.Data(), utf8.length());
+}
+
+WebString WebString::FromUTF8(const char* data, size_t length) {
+ return String::FromUTF8(data, length);
+}
+
+WebString WebString::FromUTF16(const base::string16& s) {
+ return WebString(s.data(), s.length());
+}
+
+WebString WebString::FromUTF16(const base::NullableString16& s) {
+ if (s.is_null())
+ return WebString();
+ return WebString(s.string().data(), s.string().length());
+}
+
+WebString WebString::FromUTF16(const base::Optional<base::string16>& s) {
+ if (!s.has_value())
+ return WebString();
+ return WebString(s->data(), s->length());
+}
+
+std::string WebString::Latin1() const {
+ String string(impl_);
+
+ if (string.IsEmpty())
+ return std::string();
+
+ if (string.Is8Bit())
+ return std::string(reinterpret_cast<const char*>(string.Characters8()),
+ string.length());
+
+ CString latin1 = string.Latin1();
+ return std::string(latin1.data(), latin1.length());
+}
+
+WebString WebString::FromLatin1(const WebLChar* data, size_t length) {
+ return String(data, length);
+}
+
+std::string WebString::Ascii() const {
+ DCHECK(ContainsOnlyASCII());
+
+ if (IsEmpty())
+ return std::string();
+
+ if (impl_->Is8Bit()) {
+ return std::string(reinterpret_cast<const char*>(impl_->Characters8()),
+ impl_->length());
+ }
+
+ return std::string(impl_->Characters16(),
+ impl_->Characters16() + impl_->length());
+}
+
+bool WebString::ContainsOnlyASCII() const {
+ return String(impl_).ContainsOnlyASCII();
+}
+
+WebString WebString::FromASCII(const std::string& s) {
+ DCHECK(base::IsStringASCII(s));
+ return FromLatin1(s);
+}
+
+bool WebString::Equals(const WebString& s) const {
+ return Equal(impl_.get(), s.impl_.get());
+}
+
+bool WebString::Equals(const char* characters, size_t length) const {
+ return Equal(impl_.get(), characters, length);
+}
+
+WebString::WebString(const WTF::String& s) : impl_(s.Impl()) {}
+
+WebString& WebString::operator=(const WTF::String& s) {
+ impl_ = s.Impl();
+ return *this;
+}
+
+WebString::operator WTF::String() const {
+ return impl_.get();
+}
+
+WebString::operator WTF::StringView() const {
+ return StringView(impl_.get());
+}
+
+WebString::WebString(const WTF::AtomicString& s) {
+ impl_ = s.Impl();
+}
+
+WebString& WebString::operator=(const WTF::AtomicString& s) {
+ impl_ = s.Impl();
+ return *this;
+}
+
+WebString::operator WTF::AtomicString() const {
+ return WTF::AtomicString(impl_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_string_test.cc b/chromium/third_party/blink/renderer/platform/exported/web_string_test.cc
new file mode 100644
index 00000000000..6453629b1d4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_string_test.cc
@@ -0,0 +1,50 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_string.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+TEST(WebStringTest, UTF8ConversionRoundTrip) {
+ // Valid characters.
+ for (UChar uchar = 0; uchar <= 0xD7FF; ++uchar) {
+ WebString utf16_string(&uchar, 1);
+ std::string utf8_string(utf16_string.Utf8());
+ WebString utf16_new_string =
+ WebString::FromUTF8(utf8_string.data(), utf8_string.length());
+ EXPECT_FALSE(utf16_new_string.IsNull());
+ EXPECT_TRUE(utf16_string == utf16_new_string);
+ }
+
+ // Unpaired surrogates.
+ for (UChar uchar = 0xD800; uchar <= 0xDFFF; ++uchar) {
+ WebString utf16_string(&uchar, 1);
+
+ // Conversion with Strict mode results in an empty string.
+ std::string utf8_string(
+ utf16_string.Utf8(WebString::UTF8ConversionMode::kStrict));
+ EXPECT_TRUE(utf8_string.empty());
+
+ // Unpaired surrogates can't be converted back in Lenient mode.
+ utf8_string = utf16_string.Utf8(WebString::UTF8ConversionMode::kLenient);
+ EXPECT_FALSE(utf8_string.empty());
+ WebString utf16_new_string =
+ WebString::FromUTF8(utf8_string.data(), utf8_string.length());
+ EXPECT_TRUE(utf16_new_string.IsNull());
+
+ // Round-trip works with StrictReplacingErrorsWithFFFD mode.
+ utf8_string = utf16_string.Utf8(
+ WebString::UTF8ConversionMode::kStrictReplacingErrorsWithFFFD);
+ EXPECT_FALSE(utf8_string.empty());
+ utf16_new_string =
+ WebString::FromUTF8(utf8_string.data(), utf8_string.length());
+ EXPECT_FALSE(utf16_new_string.IsNull());
+ EXPECT_FALSE(utf16_string == utf16_new_string);
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_surface_layer_bridge.cc b/chromium/third_party/blink/renderer/platform/exported/web_surface_layer_bridge.cc
new file mode 100644
index 00000000000..e434b442c5b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_surface_layer_bridge.cc
@@ -0,0 +1,20 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_surface_layer_bridge.h"
+
+#include <memory>
+#include "third_party/blink/renderer/platform/graphics/surface_layer_bridge.h"
+
+namespace blink {
+
+std::unique_ptr<WebSurfaceLayerBridge> WebSurfaceLayerBridge::Create(
+ WebLayerTreeView* layer_tree_view,
+ WebSurfaceLayerBridgeObserver* observer) {
+ return std::make_unique<SurfaceLayerBridge>(layer_tree_view, observer);
+}
+
+WebSurfaceLayerBridge::~WebSurfaceLayerBridge() = default;
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_text_run.cc b/chromium/third_party/blink/renderer/platform/exported/web_text_run.cc
new file mode 100644
index 00000000000..53cddcb9b52
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_text_run.cc
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2010 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 "third_party/blink/public/platform/web_text_run.h"
+
+#include "third_party/blink/renderer/platform/text/text_run.h"
+
+namespace blink {
+
+WebTextRun::operator TextRun() const {
+ return TextRun(text, 0, 0, TextRun::kAllowTrailingExpansion,
+ rtl ? TextDirection::kRtl : TextDirection::kLtr,
+ directional_override);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_thread_safe_data.cc b/chromium/third_party/blink/renderer/platform/exported/web_thread_safe_data.cc
new file mode 100644
index 00000000000..19196e639d8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_thread_safe_data.cc
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2010 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 "third_party/blink/public/platform/web_thread_safe_data.h"
+
+#include "third_party/blink/renderer/platform/blob/blob_data.h"
+
+namespace blink {
+
+WebThreadSafeData::WebThreadSafeData(const char* data, size_t length) {
+ private_ = RawData::Create();
+ private_->MutableData()->Append(data, length);
+}
+
+void WebThreadSafeData::Reset() {
+ private_.Reset();
+}
+
+void WebThreadSafeData::Assign(const WebThreadSafeData& other) {
+ private_ = other.private_;
+}
+
+size_t WebThreadSafeData::size() const {
+ if (private_.IsNull())
+ return 0;
+ return private_->length();
+}
+
+const char* WebThreadSafeData::Data() const {
+ if (private_.IsNull())
+ return nullptr;
+ return private_->data();
+}
+
+WebThreadSafeData::WebThreadSafeData(scoped_refptr<RawData> data)
+ : private_(std::move(data)) {}
+
+WebThreadSafeData::WebThreadSafeData(scoped_refptr<RawData>&& data)
+ : private_(std::move(data)) {}
+
+WebThreadSafeData::WebThreadSafeData(const WebThreadSafeData& other) {
+ private_ = other.private_;
+}
+
+WebThreadSafeData& WebThreadSafeData::operator=(
+ const WebThreadSafeData& other) = default;
+
+WebThreadSafeData& WebThreadSafeData::operator=(scoped_refptr<RawData> data) {
+ private_ = std::move(data);
+ return *this;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_url.cc b/chromium/third_party/blink/renderer/platform/exported/web_url.cc
new file mode 100644
index 00000000000..a3bab053622
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_url.cc
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/public/platform/web_url.h"
+
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_view.h"
+
+namespace blink {
+
+bool WebURL::ProtocolIs(const char* protocol) const {
+ const url::Component& scheme = parsed_.scheme;
+ StringView url_view = string_;
+ // For subtlety why this works in all cases, see KURL::componentString.
+ return is_valid_ &&
+ StringView(url_view, scheme.begin, scheme.len) == protocol;
+}
+
+WebURL::WebURL(const KURL& url)
+ : string_(url.GetString()),
+ parsed_(url.GetParsed()),
+ is_valid_(url.IsValid()) {}
+
+WebURL& WebURL::operator=(const KURL& url) {
+ string_ = url.GetString();
+ parsed_ = url.GetParsed();
+ is_valid_ = url.IsValid();
+ return *this;
+}
+
+WebURL::operator KURL() const {
+ return KURL(string_, parsed_, is_valid_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_url_error.cc b/chromium/third_party/blink/renderer/platform/exported/web_url_error.cc
new file mode 100644
index 00000000000..688e086648f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_url_error.cc
@@ -0,0 +1,39 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_url_error.h"
+
+#include "net/base/net_errors.h"
+
+namespace blink {
+
+WebURLError::WebURLError(int reason, const WebURL& url)
+ : reason_(reason), extended_reason_(0), url_(url) {
+ DCHECK_NE(reason_, 0);
+}
+
+WebURLError::WebURLError(int reason,
+ int extended_reason,
+ HasCopyInCache has_copy_in_cache,
+ IsWebSecurityViolation is_web_security_violation,
+ const WebURL& url)
+ : reason_(reason),
+ extended_reason_(extended_reason),
+ has_copy_in_cache_(has_copy_in_cache == HasCopyInCache::kTrue),
+ is_web_security_violation_(is_web_security_violation ==
+ IsWebSecurityViolation::kTrue),
+ url_(url) {
+ DCHECK_NE(reason_, 0);
+}
+
+WebURLError::WebURLError(const network::CORSErrorStatus& cors_error_status,
+ HasCopyInCache has_copy_in_cache,
+ const WebURL& url)
+ : reason_(net::ERR_FAILED),
+ has_copy_in_cache_(has_copy_in_cache == HasCopyInCache::kTrue),
+ is_web_security_violation_(true),
+ url_(url),
+ cors_error_status_(cors_error_status) {}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_url_load_timing.cc b/chromium/third_party/blink/renderer/platform/exported/web_url_load_timing.cc
new file mode 100644
index 00000000000..43310e1b248
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_url_load_timing.cc
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2010 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 "third_party/blink/public/platform/web_url_load_timing.h"
+
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_load_timing.h"
+
+namespace blink {
+
+void WebURLLoadTiming::Initialize() {
+ private_ = ResourceLoadTiming::Create();
+}
+
+void WebURLLoadTiming::Reset() {
+ private_.Reset();
+}
+
+void WebURLLoadTiming::Assign(const WebURLLoadTiming& other) {
+ private_ = other.private_;
+}
+
+double WebURLLoadTiming::RequestTime() const {
+ return TimeTicksInSeconds(private_->RequestTime());
+}
+
+void WebURLLoadTiming::SetRequestTime(double time) {
+ DCHECK_GE(time, 0.0);
+ private_->SetRequestTime(TimeTicksFromSeconds(time));
+}
+
+double WebURLLoadTiming::ProxyStart() const {
+ return TimeTicksInSeconds(private_->ProxyStart());
+}
+
+void WebURLLoadTiming::SetProxyStart(double start) {
+ DCHECK_GE(start, 0.0);
+ private_->SetProxyStart(TimeTicksFromSeconds(start));
+}
+
+double WebURLLoadTiming::ProxyEnd() const {
+ return TimeTicksInSeconds(private_->ProxyEnd());
+}
+
+void WebURLLoadTiming::SetProxyEnd(double end) {
+ DCHECK_GE(end, 0.0);
+ private_->SetProxyEnd(TimeTicksFromSeconds(end));
+}
+
+double WebURLLoadTiming::DnsStart() const {
+ return TimeTicksInSeconds(private_->DnsStart());
+}
+
+void WebURLLoadTiming::SetDNSStart(double start) {
+ DCHECK_GE(start, 0.0);
+ private_->SetDnsStart(TimeTicksFromSeconds(start));
+}
+
+double WebURLLoadTiming::DnsEnd() const {
+ return TimeTicksInSeconds(private_->DnsEnd());
+}
+
+void WebURLLoadTiming::SetDNSEnd(double end) {
+ DCHECK_GE(end, 0.0);
+ private_->SetDnsEnd(TimeTicksFromSeconds(end));
+}
+
+double WebURLLoadTiming::ConnectStart() const {
+ return TimeTicksInSeconds(private_->ConnectStart());
+}
+
+void WebURLLoadTiming::SetConnectStart(double start) {
+ DCHECK_GE(start, 0.0);
+ private_->SetConnectStart(TimeTicksFromSeconds(start));
+}
+
+double WebURLLoadTiming::ConnectEnd() const {
+ return TimeTicksInSeconds(private_->ConnectEnd());
+}
+
+void WebURLLoadTiming::SetConnectEnd(double end) {
+ DCHECK_GE(end, 0.0);
+ private_->SetConnectEnd(TimeTicksFromSeconds(end));
+}
+
+double WebURLLoadTiming::WorkerStart() const {
+ return TimeTicksInSeconds(private_->WorkerStart());
+}
+
+void WebURLLoadTiming::SetWorkerStart(double start) {
+ DCHECK_GE(start, 0.0);
+ private_->SetWorkerStart(TimeTicksFromSeconds(start));
+}
+
+double WebURLLoadTiming::WorkerReady() const {
+ return TimeTicksInSeconds(private_->WorkerReady());
+}
+
+void WebURLLoadTiming::SetWorkerReady(double ready) {
+ DCHECK_GE(ready, 0.0);
+ private_->SetWorkerReady(TimeTicksFromSeconds(ready));
+}
+
+double WebURLLoadTiming::SendStart() const {
+ return TimeTicksInSeconds(private_->SendStart());
+}
+
+void WebURLLoadTiming::SetSendStart(double start) {
+ DCHECK_GE(start, 0.0);
+ private_->SetSendStart(TimeTicksFromSeconds(start));
+}
+
+double WebURLLoadTiming::SendEnd() const {
+ return TimeTicksInSeconds(private_->SendEnd());
+}
+
+void WebURLLoadTiming::SetSendEnd(double end) {
+ DCHECK_GE(end, 0.0);
+ private_->SetSendEnd(TimeTicksFromSeconds(end));
+}
+
+double WebURLLoadTiming::ReceiveHeadersEnd() const {
+ return TimeTicksInSeconds(private_->ReceiveHeadersEnd());
+}
+
+void WebURLLoadTiming::SetReceiveHeadersEnd(double end) {
+ DCHECK_GE(end, 0.0);
+ private_->SetReceiveHeadersEnd(TimeTicksFromSeconds(end));
+}
+
+double WebURLLoadTiming::SslStart() const {
+ return TimeTicksInSeconds(private_->SslStart());
+}
+
+void WebURLLoadTiming::SetSSLStart(double start) {
+ DCHECK_GE(start, 0.0);
+ private_->SetSslStart(TimeTicksFromSeconds(start));
+}
+
+double WebURLLoadTiming::SslEnd() const {
+ return TimeTicksInSeconds(private_->SslEnd());
+}
+
+void WebURLLoadTiming::SetSSLEnd(double end) {
+ DCHECK_GE(end, 0.0);
+ private_->SetSslEnd(TimeTicksFromSeconds(end));
+}
+
+double WebURLLoadTiming::PushStart() const {
+ return TimeTicksInSeconds(private_->PushStart());
+}
+
+void WebURLLoadTiming::SetPushStart(double start) {
+ DCHECK_GE(start, 0.0);
+ private_->SetPushStart(TimeTicksFromSeconds(start));
+}
+
+double WebURLLoadTiming::PushEnd() const {
+ return TimeTicksInSeconds(private_->PushEnd());
+}
+
+void WebURLLoadTiming::SetPushEnd(double end) {
+ DCHECK_GE(end, 0.0);
+ private_->SetPushEnd(TimeTicksFromSeconds(end));
+}
+
+WebURLLoadTiming::WebURLLoadTiming(scoped_refptr<ResourceLoadTiming> value)
+ : private_(std::move(value)) {}
+
+WebURLLoadTiming& WebURLLoadTiming::operator=(
+ scoped_refptr<ResourceLoadTiming> value) {
+ private_ = std::move(value);
+ return *this;
+}
+
+WebURLLoadTiming::operator scoped_refptr<ResourceLoadTiming>() const {
+ return private_.Get();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_url_loader_client.cc b/chromium/third_party/blink/renderer/platform/exported/web_url_loader_client.cc
new file mode 100644
index 00000000000..b551e886947
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_url_loader_client.cc
@@ -0,0 +1,11 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_url_loader_client.h"
+
+// This WebURLLoaderClient.cpp, which includes only
+// WebURLLoaderClient.h, should be in Source/platform/exported,
+// because WebURLLoaderClient is not compiled without this cpp.
+// So if we don't have this cpp, we will see unresolved symbol error
+// when constructor/destructor's address is required.
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_url_loader_test_delegate.cc b/chromium/third_party/blink/renderer/platform/exported/web_url_loader_test_delegate.cc
new file mode 100644
index 00000000000..08ec94e12a2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_url_loader_test_delegate.cc
@@ -0,0 +1,52 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_url_loader_test_delegate.h"
+
+#include "third_party/blink/public/platform/web_url_error.h"
+#include "third_party/blink/public/platform/web_url_loader.h"
+#include "third_party/blink/public/platform/web_url_loader_client.h"
+#include "third_party/blink/public/platform/web_url_request.h"
+
+namespace blink {
+
+WebURLLoaderTestDelegate::WebURLLoaderTestDelegate() = default;
+
+WebURLLoaderTestDelegate::~WebURLLoaderTestDelegate() = default;
+
+void WebURLLoaderTestDelegate::DidReceiveResponse(
+ WebURLLoaderClient* original_client,
+ const WebURLResponse& response) {
+ original_client->DidReceiveResponse(response);
+}
+
+void WebURLLoaderTestDelegate::DidReceiveData(
+ WebURLLoaderClient* original_client,
+ const char* data,
+ int data_length) {
+ original_client->DidReceiveData(data, data_length);
+}
+
+void WebURLLoaderTestDelegate::DidFail(WebURLLoaderClient* original_client,
+ const WebURLError& error,
+ int64_t total_encoded_data_length,
+ int64_t total_encoded_body_length,
+ int64_t total_decoded_body_length) {
+ original_client->DidFail(error, total_encoded_data_length,
+ total_encoded_body_length,
+ total_decoded_body_length);
+}
+
+void WebURLLoaderTestDelegate::DidFinishLoading(
+ WebURLLoaderClient* original_client,
+ double finish_time,
+ int64_t total_encoded_data_length,
+ int64_t total_encoded_body_length,
+ int64_t total_decoded_body_length) {
+ original_client->DidFinishLoading(finish_time, total_encoded_data_length,
+ total_encoded_body_length,
+ total_decoded_body_length, false);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_url_request.cc b/chromium/third_party/blink/renderer/platform/exported/web_url_request.cc
new file mode 100644
index 00000000000..2420be2f0e8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_url_request.cc
@@ -0,0 +1,433 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/public/platform/web_url_request.h"
+
+#include <memory>
+#include "third_party/blink/public/platform/web_http_body.h"
+#include "third_party/blink/public/platform/web_http_header_visitor.h"
+#include "third_party/blink/public/platform/web_security_origin.h"
+#include "third_party/blink/public/platform/web_url.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+// The purpose of this struct is to permit allocating a ResourceRequest on the
+// heap, which is otherwise disallowed by DISALLOW_NEW_EXCEPT_PLACEMENT_NEW
+// annotation on ResourceRequest.
+struct WebURLRequest::ResourceRequestContainer {
+ ResourceRequestContainer() = default;
+ explicit ResourceRequestContainer(const ResourceRequest& r)
+ : resource_request(r) {}
+
+ ResourceRequest resource_request;
+};
+
+WebURLRequest::~WebURLRequest() = default;
+
+WebURLRequest::WebURLRequest()
+ : owned_resource_request_(new ResourceRequestContainer()),
+ resource_request_(&owned_resource_request_->resource_request) {}
+
+WebURLRequest::WebURLRequest(const WebURLRequest& r)
+ : owned_resource_request_(
+ new ResourceRequestContainer(*r.resource_request_)),
+ resource_request_(&owned_resource_request_->resource_request) {}
+
+WebURLRequest::WebURLRequest(const WebURL& url) : WebURLRequest() {
+ SetURL(url);
+}
+
+WebURLRequest& WebURLRequest::operator=(const WebURLRequest& r) {
+ // Copying subclasses that have different m_resourceRequest ownership
+ // semantics via this operator is just not supported.
+ DCHECK(owned_resource_request_);
+ DCHECK(resource_request_);
+ if (&r != this)
+ *resource_request_ = *r.resource_request_;
+ return *this;
+}
+
+bool WebURLRequest::IsNull() const {
+ return resource_request_->IsNull();
+}
+
+WebURL WebURLRequest::Url() const {
+ return resource_request_->Url();
+}
+
+void WebURLRequest::SetURL(const WebURL& url) {
+ resource_request_->SetURL(url);
+}
+
+WebURL WebURLRequest::SiteForCookies() const {
+ return resource_request_->SiteForCookies();
+}
+
+void WebURLRequest::SetSiteForCookies(const WebURL& site_for_cookies) {
+ resource_request_->SetSiteForCookies(site_for_cookies);
+}
+
+WebSecurityOrigin WebURLRequest::RequestorOrigin() const {
+ return resource_request_->RequestorOrigin();
+}
+
+void WebURLRequest::SetRequestorOrigin(
+ const WebSecurityOrigin& requestor_origin) {
+ resource_request_->SetRequestorOrigin(requestor_origin);
+}
+
+bool WebURLRequest::AllowStoredCredentials() const {
+ return resource_request_->AllowStoredCredentials();
+}
+
+void WebURLRequest::SetAllowStoredCredentials(bool allow_stored_credentials) {
+ resource_request_->SetAllowStoredCredentials(allow_stored_credentials);
+}
+
+mojom::FetchCacheMode WebURLRequest::GetCacheMode() const {
+ return resource_request_->GetCacheMode();
+}
+
+void WebURLRequest::SetCacheMode(mojom::FetchCacheMode cache_mode) {
+ resource_request_->SetCacheMode(cache_mode);
+}
+
+double WebURLRequest::TimeoutInterval() const {
+ return resource_request_->TimeoutInterval();
+}
+
+WebString WebURLRequest::HttpMethod() const {
+ return resource_request_->HttpMethod();
+}
+
+void WebURLRequest::SetHTTPMethod(const WebString& http_method) {
+ resource_request_->SetHTTPMethod(http_method);
+}
+
+WebString WebURLRequest::HttpHeaderField(const WebString& name) const {
+ return resource_request_->HttpHeaderField(name);
+}
+
+void WebURLRequest::SetHTTPHeaderField(const WebString& name,
+ const WebString& value) {
+ CHECK(!DeprecatedEqualIgnoringCase(name, "referer"));
+ resource_request_->SetHTTPHeaderField(name, value);
+}
+
+void WebURLRequest::SetHTTPReferrer(const WebString& web_referrer,
+ WebReferrerPolicy referrer_policy) {
+ // WebString doesn't have the distinction between empty and null. We use
+ // the null WTFString for referrer.
+ DCHECK_EQ(Referrer::NoReferrer(), String());
+ String referrer =
+ web_referrer.IsEmpty() ? Referrer::NoReferrer() : String(web_referrer);
+ resource_request_->SetHTTPReferrer(
+ Referrer(referrer, static_cast<ReferrerPolicy>(referrer_policy)));
+}
+
+void WebURLRequest::AddHTTPHeaderField(const WebString& name,
+ const WebString& value) {
+ resource_request_->AddHTTPHeaderField(name, value);
+}
+
+void WebURLRequest::ClearHTTPHeaderField(const WebString& name) {
+ resource_request_->ClearHTTPHeaderField(name);
+}
+
+void WebURLRequest::VisitHTTPHeaderFields(WebHTTPHeaderVisitor* visitor) const {
+ const HTTPHeaderMap& map = resource_request_->HttpHeaderFields();
+ for (HTTPHeaderMap::const_iterator it = map.begin(); it != map.end(); ++it)
+ visitor->VisitHeader(it->key, it->value);
+}
+
+WebHTTPBody WebURLRequest::HttpBody() const {
+ return WebHTTPBody(resource_request_->HttpBody());
+}
+
+void WebURLRequest::SetHTTPBody(const WebHTTPBody& http_body) {
+ resource_request_->SetHTTPBody(http_body);
+}
+
+bool WebURLRequest::ReportUploadProgress() const {
+ return resource_request_->ReportUploadProgress();
+}
+
+void WebURLRequest::SetReportUploadProgress(bool report_upload_progress) {
+ resource_request_->SetReportUploadProgress(report_upload_progress);
+}
+
+void WebURLRequest::SetReportRawHeaders(bool report_raw_headers) {
+ resource_request_->SetReportRawHeaders(report_raw_headers);
+}
+
+bool WebURLRequest::ReportRawHeaders() const {
+ return resource_request_->ReportRawHeaders();
+}
+
+WebURLRequest::RequestContext WebURLRequest::GetRequestContext() const {
+ return resource_request_->GetRequestContext();
+}
+
+network::mojom::RequestContextFrameType WebURLRequest::GetFrameType() const {
+ return resource_request_->GetFrameType();
+}
+
+WebReferrerPolicy WebURLRequest::GetReferrerPolicy() const {
+ return static_cast<WebReferrerPolicy>(resource_request_->GetReferrerPolicy());
+}
+
+void WebURLRequest::SetHTTPOriginIfNeeded(const WebSecurityOrigin& origin) {
+ resource_request_->SetHTTPOriginIfNeeded(origin.Get());
+}
+
+bool WebURLRequest::HasUserGesture() const {
+ return resource_request_->HasUserGesture();
+}
+
+void WebURLRequest::SetHasUserGesture(bool has_user_gesture) {
+ resource_request_->SetHasUserGesture(has_user_gesture);
+}
+
+void WebURLRequest::SetRequestContext(RequestContext request_context) {
+ resource_request_->SetRequestContext(request_context);
+}
+
+void WebURLRequest::SetFrameType(
+ network::mojom::RequestContextFrameType frame_type) {
+ resource_request_->SetFrameType(frame_type);
+}
+
+int WebURLRequest::RequestorID() const {
+ return resource_request_->RequestorID();
+}
+
+void WebURLRequest::SetRequestorID(int requestor_id) {
+ resource_request_->SetRequestorID(requestor_id);
+}
+
+int WebURLRequest::GetPluginChildID() const {
+ return resource_request_->GetPluginChildID();
+}
+
+void WebURLRequest::SetPluginChildID(int plugin_child_id) {
+ resource_request_->SetPluginChildID(plugin_child_id);
+}
+
+int WebURLRequest::AppCacheHostID() const {
+ return resource_request_->AppCacheHostID();
+}
+
+void WebURLRequest::SetAppCacheHostID(int app_cache_host_id) {
+ resource_request_->SetAppCacheHostID(app_cache_host_id);
+}
+
+bool WebURLRequest::DownloadToFile() const {
+ return resource_request_->DownloadToFile();
+}
+
+void WebURLRequest::SetDownloadToFile(bool download_to_file) {
+ resource_request_->SetDownloadToFile(download_to_file);
+}
+
+bool WebURLRequest::PassResponsePipeToClient() const {
+ return resource_request_->DownloadToBlob();
+}
+
+bool WebURLRequest::UseStreamOnResponse() const {
+ return resource_request_->UseStreamOnResponse();
+}
+
+void WebURLRequest::SetUseStreamOnResponse(bool use_stream_on_response) {
+ resource_request_->SetUseStreamOnResponse(use_stream_on_response);
+}
+
+bool WebURLRequest::GetKeepalive() const {
+ return resource_request_->GetKeepalive();
+}
+
+void WebURLRequest::SetKeepalive(bool keepalive) {
+ resource_request_->SetKeepalive(keepalive);
+}
+
+bool WebURLRequest::GetSkipServiceWorker() const {
+ return resource_request_->GetSkipServiceWorker();
+}
+
+void WebURLRequest::SetSkipServiceWorker(bool skip_service_worker) {
+ resource_request_->SetSkipServiceWorker(skip_service_worker);
+}
+
+bool WebURLRequest::ShouldResetAppCache() const {
+ return resource_request_->ShouldResetAppCache();
+}
+
+void WebURLRequest::SetShouldResetAppCache(bool set_should_reset_app_cache) {
+ resource_request_->SetShouldResetAppCache(set_should_reset_app_cache);
+}
+
+network::mojom::FetchRequestMode WebURLRequest::GetFetchRequestMode() const {
+ return resource_request_->GetFetchRequestMode();
+}
+
+void WebURLRequest::SetFetchRequestMode(network::mojom::FetchRequestMode mode) {
+ return resource_request_->SetFetchRequestMode(mode);
+}
+
+network::mojom::FetchCredentialsMode WebURLRequest::GetFetchCredentialsMode()
+ const {
+ return resource_request_->GetFetchCredentialsMode();
+}
+
+void WebURLRequest::SetFetchCredentialsMode(
+ network::mojom::FetchCredentialsMode mode) {
+ return resource_request_->SetFetchCredentialsMode(mode);
+}
+
+network::mojom::FetchRedirectMode WebURLRequest::GetFetchRedirectMode() const {
+ return resource_request_->GetFetchRedirectMode();
+}
+
+void WebURLRequest::SetFetchRedirectMode(
+ network::mojom::FetchRedirectMode redirect) {
+ return resource_request_->SetFetchRedirectMode(redirect);
+}
+
+WebString WebURLRequest::GetFetchIntegrity() const {
+ return resource_request_->GetFetchIntegrity();
+}
+
+void WebURLRequest::SetFetchIntegrity(const WebString& integrity) {
+ return resource_request_->SetFetchIntegrity(integrity);
+}
+
+WebURLRequest::PreviewsState WebURLRequest::GetPreviewsState() const {
+ return resource_request_->GetPreviewsState();
+}
+
+void WebURLRequest::SetPreviewsState(
+ WebURLRequest::PreviewsState previews_state) {
+ return resource_request_->SetPreviewsState(previews_state);
+}
+
+WebURLRequest::ExtraData* WebURLRequest::GetExtraData() const {
+ return resource_request_->GetExtraData();
+}
+
+void WebURLRequest::SetExtraData(std::unique_ptr<ExtraData> extra_data) {
+ resource_request_->SetExtraData(std::move(extra_data));
+}
+
+ResourceRequest& WebURLRequest::ToMutableResourceRequest() {
+ DCHECK(resource_request_);
+ return *resource_request_;
+}
+
+WebURLRequest::Priority WebURLRequest::GetPriority() const {
+ return static_cast<WebURLRequest::Priority>(resource_request_->Priority());
+}
+
+void WebURLRequest::SetPriority(WebURLRequest::Priority priority) {
+ resource_request_->SetPriority(static_cast<ResourceLoadPriority>(priority));
+}
+
+bool WebURLRequest::CheckForBrowserSideNavigation() const {
+ return resource_request_->CheckForBrowserSideNavigation();
+}
+
+void WebURLRequest::SetCheckForBrowserSideNavigation(bool check) {
+ resource_request_->SetCheckForBrowserSideNavigation(check);
+}
+
+bool WebURLRequest::WasDiscarded() const {
+ return resource_request_->WasDiscarded();
+}
+void WebURLRequest::SetWasDiscarded(bool was_discarded) {
+ resource_request_->SetWasDiscarded(was_discarded);
+}
+
+double WebURLRequest::UiStartTime() const {
+ return resource_request_->UiStartTime();
+}
+
+void WebURLRequest::SetUiStartTime(double time_seconds) {
+ resource_request_->SetUIStartTime(time_seconds);
+}
+
+bool WebURLRequest::IsExternalRequest() const {
+ return resource_request_->IsExternalRequest();
+}
+
+network::mojom::CORSPreflightPolicy WebURLRequest::GetCORSPreflightPolicy()
+ const {
+ return resource_request_->CORSPreflightPolicy();
+}
+
+void WebURLRequest::SetNavigationStartTime(double navigation_start_seconds) {
+ resource_request_->SetNavigationStartTime(navigation_start_seconds);
+}
+
+void WebURLRequest::SetIsSameDocumentNavigation(bool is_same_document) {
+ resource_request_->SetIsSameDocumentNavigation(is_same_document);
+}
+
+WebURLRequest::InputToLoadPerfMetricReportPolicy
+WebURLRequest::InputPerfMetricReportPolicy() const {
+ return static_cast<WebURLRequest::InputToLoadPerfMetricReportPolicy>(
+ resource_request_->InputPerfMetricReportPolicy());
+}
+
+void WebURLRequest::SetInputPerfMetricReportPolicy(
+ WebURLRequest::InputToLoadPerfMetricReportPolicy policy) {
+ resource_request_->SetInputPerfMetricReportPolicy(
+ static_cast<blink::InputToLoadPerfMetricReportPolicy>(policy));
+}
+
+base::Optional<WebString> WebURLRequest::GetSuggestedFilename() const {
+ if (!resource_request_->GetSuggestedFilename().has_value())
+ return base::Optional<WebString>();
+ return static_cast<WebString>(
+ resource_request_->GetSuggestedFilename().value());
+}
+
+bool WebURLRequest::IsAdResource() const {
+ return resource_request_->IsAdResource();
+}
+
+const ResourceRequest& WebURLRequest::ToResourceRequest() const {
+ DCHECK(resource_request_);
+ return *resource_request_;
+}
+
+WebURLRequest::WebURLRequest(ResourceRequest& r) : resource_request_(&r) {}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_url_request_test.cc b/chromium/third_party/blink/renderer/platform/exported/web_url_request_test.cc
new file mode 100644
index 00000000000..bce21fa0f57
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_url_request_test.cc
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/public/platform/web_url_request.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+namespace {
+
+class RequestTestExtraData : public WebURLRequest::ExtraData {
+ public:
+ explicit RequestTestExtraData(bool* alive) : alive_(alive) { *alive = true; }
+
+ ~RequestTestExtraData() override { *alive_ = false; }
+
+ private:
+ bool* alive_;
+};
+
+} // anonymous namespace
+
+TEST(WebURLRequestTest, ExtraData) {
+ bool alive = false;
+ {
+ WebURLRequest url_request;
+ auto extra_data = std::make_unique<RequestTestExtraData>(&alive);
+ EXPECT_TRUE(alive);
+
+ auto* raw_extra_data_pointer = extra_data.get();
+ url_request.SetExtraData(std::move(extra_data));
+ EXPECT_EQ(raw_extra_data_pointer, url_request.GetExtraData());
+ {
+ WebURLRequest other_url_request = url_request;
+ EXPECT_TRUE(alive);
+ EXPECT_EQ(raw_extra_data_pointer, other_url_request.GetExtraData());
+ EXPECT_EQ(raw_extra_data_pointer, url_request.GetExtraData());
+ }
+ EXPECT_TRUE(alive);
+ EXPECT_EQ(raw_extra_data_pointer, url_request.GetExtraData());
+ }
+ EXPECT_FALSE(alive);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_url_response.cc b/chromium/third_party/blink/renderer/platform/exported/web_url_response.cc
new file mode 100644
index 00000000000..26f131d1ea0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_url_response.cc
@@ -0,0 +1,456 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/public/platform/web_url_response.h"
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/public/platform/web_http_header_visitor.h"
+#include "third_party/blink/public/platform/web_http_load_info.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/platform/web_url.h"
+#include "third_party/blink/public/platform/web_url_load_timing.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_load_timing.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+namespace blink {
+
+namespace {
+
+class URLResponseExtraDataContainer : public ResourceResponse::ExtraData {
+ public:
+ static scoped_refptr<URLResponseExtraDataContainer> Create(
+ WebURLResponse::ExtraData* extra_data) {
+ return base::AdoptRef(new URLResponseExtraDataContainer(extra_data));
+ }
+
+ ~URLResponseExtraDataContainer() override = default;
+
+ WebURLResponse::ExtraData* GetExtraData() const { return extra_data_.get(); }
+
+ private:
+ explicit URLResponseExtraDataContainer(WebURLResponse::ExtraData* extra_data)
+ : extra_data_(base::WrapUnique(extra_data)) {}
+
+ std::unique_ptr<WebURLResponse::ExtraData> extra_data_;
+};
+
+} // namespace
+
+// The purpose of this struct is to permit allocating a ResourceResponse on the
+// heap, which is otherwise disallowed by the DISALLOW_NEW_EXCEPT_PLACEMENT_NEW
+// annotation on ResourceResponse.
+struct WebURLResponse::ResourceResponseContainer {
+ ResourceResponseContainer() = default;
+
+ explicit ResourceResponseContainer(const ResourceResponse& r)
+ : resource_response(r) {}
+
+ ResourceResponse resource_response;
+};
+
+WebURLResponse::~WebURLResponse() = default;
+
+WebURLResponse::WebURLResponse()
+ : owned_resource_response_(new ResourceResponseContainer()),
+ resource_response_(&owned_resource_response_->resource_response) {}
+
+WebURLResponse::WebURLResponse(const WebURLResponse& r)
+ : owned_resource_response_(
+ new ResourceResponseContainer(*r.resource_response_)),
+ resource_response_(&owned_resource_response_->resource_response) {}
+
+WebURLResponse::WebURLResponse(const WebURL& url) : WebURLResponse() {
+ SetURL(url);
+}
+
+WebURLResponse& WebURLResponse::operator=(const WebURLResponse& r) {
+ // Copying subclasses that have different m_resourceResponse ownership
+ // semantics via this operator is just not supported.
+ DCHECK(owned_resource_response_);
+ DCHECK(resource_response_);
+ if (&r != this)
+ *resource_response_ = *r.resource_response_;
+ return *this;
+}
+
+bool WebURLResponse::IsNull() const {
+ return resource_response_->IsNull();
+}
+
+WebURL WebURLResponse::Url() const {
+ return resource_response_->Url();
+}
+
+void WebURLResponse::SetURL(const WebURL& url) {
+ resource_response_->SetURL(url);
+}
+
+void WebURLResponse::SetConnectionID(unsigned connection_id) {
+ resource_response_->SetConnectionID(connection_id);
+}
+
+void WebURLResponse::SetConnectionReused(bool connection_reused) {
+ resource_response_->SetConnectionReused(connection_reused);
+}
+
+void WebURLResponse::SetLoadTiming(const WebURLLoadTiming& timing) {
+ scoped_refptr<ResourceLoadTiming> load_timing =
+ scoped_refptr<ResourceLoadTiming>(timing);
+ resource_response_->SetResourceLoadTiming(std::move(load_timing));
+}
+
+void WebURLResponse::SetHTTPLoadInfo(const WebHTTPLoadInfo& value) {
+ resource_response_->SetResourceLoadInfo(value);
+}
+
+void WebURLResponse::SetResponseTime(base::Time response_time) {
+ resource_response_->SetResponseTime(response_time);
+}
+
+WebString WebURLResponse::MimeType() const {
+ return resource_response_->MimeType();
+}
+
+void WebURLResponse::SetMIMEType(const WebString& mime_type) {
+ resource_response_->SetMimeType(mime_type);
+}
+
+long long WebURLResponse::ExpectedContentLength() const {
+ return resource_response_->ExpectedContentLength();
+}
+
+void WebURLResponse::SetExpectedContentLength(
+ long long expected_content_length) {
+ resource_response_->SetExpectedContentLength(expected_content_length);
+}
+
+void WebURLResponse::SetTextEncodingName(const WebString& text_encoding_name) {
+ resource_response_->SetTextEncodingName(text_encoding_name);
+}
+
+WebURLResponse::HTTPVersion WebURLResponse::HttpVersion() const {
+ return static_cast<HTTPVersion>(resource_response_->HttpVersion());
+}
+
+void WebURLResponse::SetHTTPVersion(HTTPVersion version) {
+ resource_response_->SetHTTPVersion(
+ static_cast<ResourceResponse::HTTPVersion>(version));
+}
+
+int WebURLResponse::HttpStatusCode() const {
+ return resource_response_->HttpStatusCode();
+}
+
+void WebURLResponse::SetHTTPStatusCode(int http_status_code) {
+ resource_response_->SetHTTPStatusCode(http_status_code);
+}
+
+WebString WebURLResponse::HttpStatusText() const {
+ return resource_response_->HttpStatusText();
+}
+
+void WebURLResponse::SetHTTPStatusText(const WebString& http_status_text) {
+ resource_response_->SetHTTPStatusText(http_status_text);
+}
+
+WebString WebURLResponse::HttpHeaderField(const WebString& name) const {
+ return resource_response_->HttpHeaderField(name);
+}
+
+void WebURLResponse::SetHTTPHeaderField(const WebString& name,
+ const WebString& value) {
+ resource_response_->SetHTTPHeaderField(name, value);
+}
+
+void WebURLResponse::AddHTTPHeaderField(const WebString& name,
+ const WebString& value) {
+ if (name.IsNull() || value.IsNull())
+ return;
+
+ resource_response_->AddHTTPHeaderField(name, value);
+}
+
+void WebURLResponse::ClearHTTPHeaderField(const WebString& name) {
+ resource_response_->ClearHTTPHeaderField(name);
+}
+
+void WebURLResponse::VisitHTTPHeaderFields(
+ WebHTTPHeaderVisitor* visitor) const {
+ const HTTPHeaderMap& map = resource_response_->HttpHeaderFields();
+ for (HTTPHeaderMap::const_iterator it = map.begin(); it != map.end(); ++it)
+ visitor->VisitHeader(it->key, it->value);
+}
+
+long long WebURLResponse::AppCacheID() const {
+ return resource_response_->AppCacheID();
+}
+
+void WebURLResponse::SetAppCacheID(long long app_cache_id) {
+ resource_response_->SetAppCacheID(app_cache_id);
+}
+
+WebURL WebURLResponse::AppCacheManifestURL() const {
+ return resource_response_->AppCacheManifestURL();
+}
+
+void WebURLResponse::SetAppCacheManifestURL(const WebURL& url) {
+ resource_response_->SetAppCacheManifestURL(url);
+}
+
+void WebURLResponse::SetHasMajorCertificateErrors(bool value) {
+ resource_response_->SetHasMajorCertificateErrors(value);
+}
+
+void WebURLResponse::SetCTPolicyCompliance(
+ net::ct::CTPolicyCompliance compliance) {
+ switch (compliance) {
+ case net::ct::CTPolicyCompliance::
+ CT_POLICY_COMPLIANCE_DETAILS_NOT_AVAILABLE:
+ case net::ct::CTPolicyCompliance::CT_POLICY_BUILD_NOT_TIMELY:
+ resource_response_->SetCTPolicyCompliance(
+ ResourceResponse::kCTPolicyComplianceDetailsNotAvailable);
+ break;
+ case net::ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS:
+ case net::ct::CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS:
+ resource_response_->SetCTPolicyCompliance(
+ ResourceResponse::kCTPolicyDoesNotComply);
+ break;
+ case net::ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS:
+ resource_response_->SetCTPolicyCompliance(
+ ResourceResponse::kCTPolicyComplies);
+ break;
+ case net::ct::CTPolicyCompliance::CT_POLICY_MAX:
+ NOTREACHED();
+ resource_response_->SetCTPolicyCompliance(
+ ResourceResponse::kCTPolicyComplianceDetailsNotAvailable);
+ break;
+ };
+}
+
+void WebURLResponse::SetIsLegacySymantecCert(bool value) {
+ resource_response_->SetIsLegacySymantecCert(value);
+}
+
+void WebURLResponse::SetSecurityStyle(WebSecurityStyle security_style) {
+ resource_response_->SetSecurityStyle(
+ static_cast<ResourceResponse::SecurityStyle>(security_style));
+}
+
+void WebURLResponse::SetSecurityDetails(
+ const WebSecurityDetails& web_security_details) {
+ ResourceResponse::SignedCertificateTimestampList sct_list;
+ for (const auto& iter : web_security_details.sct_list) {
+ sct_list.push_back(
+ static_cast<ResourceResponse::SignedCertificateTimestamp>(iter));
+ }
+ Vector<String> san_list;
+ san_list.Append(web_security_details.san_list.Data(),
+ web_security_details.san_list.size());
+ Vector<AtomicString> certificate;
+ for (const auto& iter : web_security_details.certificate) {
+ AtomicString cert = iter;
+ certificate.push_back(cert);
+ }
+ resource_response_->SetSecurityDetails(
+ web_security_details.protocol, web_security_details.key_exchange,
+ web_security_details.key_exchange_group, web_security_details.cipher,
+ web_security_details.mac, web_security_details.subject_name, san_list,
+ web_security_details.issuer,
+ static_cast<time_t>(web_security_details.valid_from),
+ static_cast<time_t>(web_security_details.valid_to), certificate,
+ sct_list);
+}
+
+WebURLResponse::WebSecurityDetails WebURLResponse::SecurityDetailsForTesting() {
+ const blink::ResourceResponse::SecurityDetails* security_details =
+ resource_response_->GetSecurityDetails();
+ std::vector<SignedCertificateTimestamp> sct_list;
+ for (const auto& iter : security_details->sct_list) {
+ sct_list.push_back(SignedCertificateTimestamp(
+ iter.status_, iter.origin_, iter.log_description_, iter.log_id_,
+ iter.timestamp_, iter.hash_algorithm_, iter.signature_algorithm_,
+ iter.signature_data_));
+ }
+ return WebSecurityDetails(
+ security_details->protocol, security_details->key_exchange,
+ security_details->key_exchange_group, security_details->cipher,
+ security_details->mac, security_details->subject_name,
+ security_details->san_list, security_details->issuer,
+ security_details->valid_from, security_details->valid_to,
+ security_details->certificate, SignedCertificateTimestampList(sct_list));
+}
+
+const ResourceResponse& WebURLResponse::ToResourceResponse() const {
+ return *resource_response_;
+}
+
+void WebURLResponse::SetWasCached(bool value) {
+ resource_response_->SetWasCached(value);
+}
+
+void WebURLResponse::SetWasFetchedViaSPDY(bool value) {
+ resource_response_->SetWasFetchedViaSPDY(value);
+}
+
+bool WebURLResponse::WasFetchedViaServiceWorker() const {
+ return resource_response_->WasFetchedViaServiceWorker();
+}
+
+void WebURLResponse::SetWasFetchedViaServiceWorker(bool value) {
+ resource_response_->SetWasFetchedViaServiceWorker(value);
+}
+
+void WebURLResponse::SetWasFallbackRequiredByServiceWorker(bool value) {
+ resource_response_->SetWasFallbackRequiredByServiceWorker(value);
+}
+
+void WebURLResponse::SetResponseTypeViaServiceWorker(
+ network::mojom::FetchResponseType value) {
+ resource_response_->SetResponseTypeViaServiceWorker(value);
+}
+
+network::mojom::FetchResponseType WebURLResponse::ResponseTypeViaServiceWorker()
+ const {
+ return resource_response_->ResponseTypeViaServiceWorker();
+}
+
+void WebURLResponse::SetURLListViaServiceWorker(
+ const WebVector<WebURL>& url_list_via_service_worker) {
+ Vector<KURL> url_list(url_list_via_service_worker.size());
+ std::transform(url_list_via_service_worker.begin(),
+ url_list_via_service_worker.end(), url_list.begin(),
+ [](const WebURL& url) { return url; });
+ resource_response_->SetURLListViaServiceWorker(url_list);
+}
+
+WebURL WebURLResponse::OriginalURLViaServiceWorker() const {
+ return resource_response_->OriginalURLViaServiceWorker();
+}
+
+void WebURLResponse::SetMultipartBoundary(const char* bytes, size_t size) {
+ resource_response_->SetMultipartBoundary(bytes, size);
+}
+
+void WebURLResponse::SetCacheStorageCacheName(
+ const WebString& cache_storage_cache_name) {
+ resource_response_->SetCacheStorageCacheName(cache_storage_cache_name);
+}
+
+WebVector<WebString> WebURLResponse::CorsExposedHeaderNames() const {
+ return resource_response_->CorsExposedHeaderNames();
+}
+
+void WebURLResponse::SetCorsExposedHeaderNames(
+ const WebVector<WebString>& header_names) {
+ Vector<String> exposed_header_names;
+ exposed_header_names.Append(header_names.Data(), header_names.size());
+ resource_response_->SetCorsExposedHeaderNames(exposed_header_names);
+}
+
+void WebURLResponse::SetDidServiceWorkerNavigationPreload(bool value) {
+ resource_response_->SetDidServiceWorkerNavigationPreload(value);
+}
+
+WebString WebURLResponse::DownloadFilePath() const {
+ return resource_response_->DownloadedFilePath();
+}
+
+void WebURLResponse::SetDownloadFilePath(const WebString& download_file_path) {
+ resource_response_->SetDownloadedFilePath(download_file_path);
+}
+
+WebString WebURLResponse::RemoteIPAddress() const {
+ return resource_response_->RemoteIPAddress();
+}
+
+void WebURLResponse::SetRemoteIPAddress(const WebString& remote_ip_address) {
+ resource_response_->SetRemoteIPAddress(remote_ip_address);
+}
+
+unsigned short WebURLResponse::RemotePort() const {
+ return resource_response_->RemotePort();
+}
+
+void WebURLResponse::SetRemotePort(unsigned short remote_port) {
+ resource_response_->SetRemotePort(remote_port);
+}
+
+void WebURLResponse::SetEncodedDataLength(long long length) {
+ resource_response_->SetEncodedDataLength(length);
+}
+
+WebURLResponse::ExtraData* WebURLResponse::GetExtraData() const {
+ scoped_refptr<ResourceResponse::ExtraData> data =
+ resource_response_->GetExtraData();
+ if (!data)
+ return nullptr;
+ return static_cast<URLResponseExtraDataContainer*>(data.get())
+ ->GetExtraData();
+}
+
+void WebURLResponse::SetExtraData(WebURLResponse::ExtraData* extra_data) {
+ if (extra_data != GetExtraData()) {
+ resource_response_->SetExtraData(
+ URLResponseExtraDataContainer::Create(extra_data));
+ }
+}
+
+void WebURLResponse::AppendRedirectResponse(const WebURLResponse& response) {
+ resource_response_->AppendRedirectResponse(response.ToResourceResponse());
+}
+
+WebString WebURLResponse::AlpnNegotiatedProtocol() const {
+ return resource_response_->AlpnNegotiatedProtocol();
+}
+
+void WebURLResponse::SetAlpnNegotiatedProtocol(
+ const WebString& alpn_negotiated_protocol) {
+ resource_response_->SetAlpnNegotiatedProtocol(alpn_negotiated_protocol);
+}
+
+net::HttpResponseInfo::ConnectionInfo WebURLResponse::ConnectionInfo() const {
+ return resource_response_->ConnectionInfo();
+}
+
+void WebURLResponse::SetConnectionInfo(
+ net::HttpResponseInfo::ConnectionInfo connection_info) {
+ resource_response_->SetConnectionInfo(connection_info);
+}
+
+WebURLResponse::WebURLResponse(ResourceResponse& r) : resource_response_(&r) {}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_url_response_test.cc b/chromium/third_party/blink/renderer/platform/exported/web_url_response_test.cc
new file mode 100644
index 00000000000..d27d0cd47b1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_url_response_test.cc
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2012 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 "third_party/blink/public/platform/web_url_response.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/web_url.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+
+namespace blink {
+
+namespace {
+
+class ResponseTestExtraData : public WebURLResponse::ExtraData {
+ public:
+ explicit ResponseTestExtraData(bool* alive) : alive_(alive) { *alive = true; }
+
+ ~ResponseTestExtraData() override { *alive_ = false; }
+
+ private:
+ bool* alive_;
+};
+
+} // anonymous namespace
+
+TEST(WebURLResponseTest, ExtraData) {
+ bool alive = false;
+ {
+ WebURLResponse url_response;
+ ResponseTestExtraData* extra_data = new ResponseTestExtraData(&alive);
+ EXPECT_TRUE(alive);
+
+ url_response.SetExtraData(extra_data);
+ EXPECT_EQ(extra_data, url_response.GetExtraData());
+ {
+ WebURLResponse other_url_response = url_response;
+ EXPECT_TRUE(alive);
+ EXPECT_EQ(extra_data, other_url_response.GetExtraData());
+ EXPECT_EQ(extra_data, url_response.GetExtraData());
+ }
+ EXPECT_TRUE(alive);
+ EXPECT_EQ(extra_data, url_response.GetExtraData());
+ }
+ EXPECT_FALSE(alive);
+}
+
+TEST(WebURLResponseTest, NewInstanceIsNull) {
+ WebURLResponse instance;
+ EXPECT_TRUE(instance.IsNull());
+}
+
+TEST(WebURLResponseTest, NotNullAfterSetURL) {
+ WebURLResponse instance;
+ instance.SetURL(KURL("http://localhost/"));
+ EXPECT_FALSE(instance.IsNull());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_video_frame_submitter.cc b/chromium/third_party/blink/renderer/platform/exported/web_video_frame_submitter.cc
new file mode 100644
index 00000000000..83dfcf91463
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/web_video_frame_submitter.cc
@@ -0,0 +1,38 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_video_frame_submitter.h"
+
+#include <memory>
+#include <utility>
+
+#include "third_party/blink/renderer/platform/graphics/video_frame_submitter.h"
+
+namespace cc {
+class LayerTreeSettings;
+class VideoFrameProvider;
+} // namespace cc
+
+namespace gpu {
+class GpuMemoryBufferManager;
+}
+
+namespace viz {
+class ContextProvider;
+}
+
+namespace blink {
+
+std::unique_ptr<WebVideoFrameSubmitter> WebVideoFrameSubmitter::Create(
+ WebContextProviderCallback context_provider_callback,
+ viz::SharedBitmapManager* shared_bitmap_manager,
+ gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
+ const cc::LayerTreeSettings& settings) {
+ return std::make_unique<VideoFrameSubmitter>(
+ std::make_unique<VideoFrameResourceProvider>(
+ std::move(context_provider_callback), shared_bitmap_manager,
+ gpu_memory_buffer_manager, settings));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/wrapped_resource_request.h b/chromium/third_party/blink/renderer/platform/exported/wrapped_resource_request.h
new file mode 100644
index 00000000000..4377fcd2181
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/wrapped_resource_request.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_EXPORTED_WRAPPED_RESOURCE_REQUEST_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_EXPORTED_WRAPPED_RESOURCE_REQUEST_H_
+
+#include "third_party/blink/public/platform/web_url_request.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+// WrappedResourceRequest doesn't take ownership of given ResourceRequest,
+// but just holds a pointer to it. It is not copyable.
+class WrappedResourceRequest : public WebURLRequest {
+ WTF_MAKE_NONCOPYABLE(WrappedResourceRequest);
+
+ public:
+ ~WrappedResourceRequest() = default;
+
+ explicit WrappedResourceRequest(ResourceRequest& resource_request)
+ : WebURLRequest(resource_request) {}
+
+ explicit WrappedResourceRequest(const ResourceRequest& resource_request)
+ : WrappedResourceRequest(const_cast<ResourceRequest&>(resource_request)) {
+ }
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/exported/wrapped_resource_response.h b/chromium/third_party/blink/renderer/platform/exported/wrapped_resource_response.h
new file mode 100644
index 00000000000..222b964f690
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/exported/wrapped_resource_response.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_EXPORTED_WRAPPED_RESOURCE_RESPONSE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_EXPORTED_WRAPPED_RESOURCE_RESPONSE_H_
+
+#include "third_party/blink/public/platform/web_url_response.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+// WrappedResourceResponse doesn't take ownership of given ResourceResponse,
+// but just holds a pointer to it. It is not copyable.
+class WrappedResourceResponse : public WebURLResponse {
+ WTF_MAKE_NONCOPYABLE(WrappedResourceResponse);
+
+ public:
+ ~WrappedResourceResponse() = default;
+
+ explicit WrappedResourceResponse(ResourceResponse& resource_response)
+ : WebURLResponse(resource_response) {}
+
+ explicit WrappedResourceResponse(const ResourceResponse& resource_response)
+ : WrappedResourceResponse(
+ const_cast<ResourceResponse&>(resource_response)) {}
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/feature_policy/feature_policy.cc b/chromium/third_party/blink/renderer/platform/feature_policy/feature_policy.cc
new file mode 100644
index 00000000000..bb9a13b1249
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/feature_policy/feature_policy.cc
@@ -0,0 +1,300 @@
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/feature_policy/feature_policy.h"
+
+#include "third_party/blink/renderer/platform/json/json_values.h"
+#include "third_party/blink/renderer/platform/network/http_parsers.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+#include "third_party/blink/renderer/platform/wtf/ascii_ctype.h"
+#include "third_party/blink/renderer/platform/wtf/bit_vector.h"
+#include "third_party/blink/renderer/platform/wtf/text/parsing_utilities.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
+#include "url/gurl.h"
+
+namespace blink {
+
+namespace {
+
+// TODO(loonybear): Deprecate the methods in this namesapce when deprecating old
+// allow syntax.
+bool IsValidOldAllowSyntax(const String& policy,
+ scoped_refptr<const SecurityOrigin> src_origin) {
+ // Old syntax enable all features on src_origin, If src_origin does not exist
+ // (example, http header does not have a src_origin), then the syntax cannot
+ // be valid.
+ if (!src_origin)
+ return false;
+ // allow = "feature" is also supported by new syntax.
+ if (!policy.Contains(' '))
+ return false;
+ // Old syntax only allows whitespace as valid delimiter.
+ if (policy.Contains(';') || policy.Contains(','))
+ return false;
+ // An empty policy is also allowed in the new syntax.
+ if (policy.ContainsOnlyWhitespace())
+ return false;
+ // Old syntax does not support specifying wildcards / origins for any feature.
+ if (policy.Contains("self") || policy.Contains("src") ||
+ policy.Contains("none") || policy.Contains("*")) {
+ return false;
+ }
+
+ // Verify that the policy follows this syntax:
+ // allow = "name1 name2 name3 ...", name* = 1*( ALPHA / DIGIT / "-" )
+ auto IsValidFeatureNameCharacter = [](auto c) {
+ return IsASCIIAlphanumeric(c) || c == '-';
+ };
+
+ for (unsigned i = 0; i < policy.length(); i++) {
+ if (!IsValidFeatureNameCharacter(policy[i]) && !IsASCIISpace(policy[i]))
+ return false;
+ }
+ return true;
+}
+
+ParsedFeaturePolicy ParseOldAllowSyntax(const String& policy,
+ const url::Origin& origin,
+ Vector<String>* messages,
+ const FeatureNameMap& feature_names) {
+ ParsedFeaturePolicy whitelists;
+ if (messages) {
+ messages->push_back(
+ "The old syntax (allow=\"feature1 feature2 feature3 ...\") is "
+ "deprecated and will be removed in Chrome 68. Use semicolons to "
+ "separate features (allow=\"feature1; feature2; feature3; ...\").");
+ }
+ Vector<String> tokens;
+ policy.Split(' ', tokens);
+ for (const String& token : tokens) {
+ if (!feature_names.Contains(token)) {
+ if (messages)
+ messages->push_back("Unrecognized feature: '" + token + "'.");
+ continue;
+ }
+ ParsedFeaturePolicyDeclaration whitelist;
+ whitelist.feature = feature_names.at(token);
+ whitelist.origins = {origin};
+ whitelists.push_back(whitelist);
+ }
+ return whitelists;
+}
+
+} // namespace
+
+ParsedFeaturePolicy ParseFeaturePolicyHeader(
+ const String& policy,
+ scoped_refptr<const SecurityOrigin> origin,
+ Vector<String>* messages) {
+ return ParseFeaturePolicy(policy, origin, nullptr, messages,
+ GetDefaultFeatureNameMap());
+}
+
+ParsedFeaturePolicy ParseFeaturePolicyAttribute(
+ const String& policy,
+ scoped_refptr<const SecurityOrigin> self_origin,
+ scoped_refptr<const SecurityOrigin> src_origin,
+ Vector<String>* messages,
+ bool* old_syntax) {
+ return ParseFeaturePolicy(policy, self_origin, src_origin, messages,
+ GetDefaultFeatureNameMap(), old_syntax);
+}
+
+ParsedFeaturePolicy ParseFeaturePolicy(
+ const String& policy,
+ scoped_refptr<const SecurityOrigin> self_origin,
+ scoped_refptr<const SecurityOrigin> src_origin,
+ Vector<String>* messages,
+ const FeatureNameMap& feature_names,
+ bool* old_syntax) {
+ // Temporarily supporting old allow syntax:
+ // allow = "feature1 feature2 feature3 ... "
+ // TODO(loonybear): depracate this old syntax in the future.
+ if (IsValidOldAllowSyntax(policy, src_origin)) {
+ if (old_syntax)
+ *old_syntax = true;
+ return ParseOldAllowSyntax(policy, src_origin->ToUrlOrigin(), messages,
+ feature_names);
+ }
+
+ ParsedFeaturePolicy whitelists;
+ BitVector features_specified(
+ static_cast<int>(mojom::FeaturePolicyFeature::kMaxValue));
+
+ // RFC2616, section 4.2 specifies that headers appearing multiple times can be
+ // combined with a comma. Walk the header string, and parse each comma
+ // separated chunk as a separate header.
+ Vector<String> policy_items;
+ // policy_items = [ policy *( "," [ policy ] ) ]
+ policy.Split(',', policy_items);
+ for (const String& item : policy_items) {
+ Vector<String> entry_list;
+ // entry_list = [ entry *( ";" [ entry ] ) ]
+ item.Split(';', entry_list);
+ for (const String& entry : entry_list) {
+ // Split removes extra whitespaces by default
+ // "name value1 value2" or "name".
+ Vector<String> tokens;
+ entry.Split(' ', tokens);
+ // Empty policy. Skip.
+ if (tokens.IsEmpty())
+ continue;
+ if (!feature_names.Contains(tokens[0])) {
+ if (messages)
+ messages->push_back("Unrecognized feature: '" + tokens[0] + "'.");
+ continue;
+ }
+
+ mojom::FeaturePolicyFeature feature = feature_names.at(tokens[0]);
+ // If a policy has already been specified for the current feature, drop
+ // the new policy.
+ if (features_specified.QuickGet(static_cast<int>(feature)))
+ continue;
+
+ ParsedFeaturePolicyDeclaration whitelist;
+ whitelist.feature = feature;
+ features_specified.QuickSet(static_cast<int>(feature));
+ std::vector<url::Origin> origins;
+ // If a policy entry has no (optional) values (e,g,
+ // allow="feature_name1; feature_name2 value"), enable the feature for:
+ // a. |self_origin|, if we are parsing a header policy (i.e.,
+ // |src_origin| is null);
+ // b. |src_origin|, if we are parsing an allow attribute (i.e.,
+ // |src_origin| is not null), |src_origin| is not opaque; or
+ // c. the opaque origin of the frame, if |src_origin| is opaque.
+ if (tokens.size() == 1) {
+ if (!src_origin) {
+ origins.push_back(self_origin->ToUrlOrigin());
+ } else if (!src_origin->IsUnique()) {
+ origins.push_back(src_origin->ToUrlOrigin());
+ } else {
+ whitelist.matches_opaque_src = true;
+ }
+ }
+
+ for (size_t i = 1; i < tokens.size(); i++) {
+ if (!tokens[i].ContainsOnlyASCII()) {
+ messages->push_back("Non-ASCII characters in origin.");
+ continue;
+ }
+ if (EqualIgnoringASCIICase(tokens[i], "'self'")) {
+ origins.push_back(self_origin->ToUrlOrigin());
+ } else if (src_origin && EqualIgnoringASCIICase(tokens[i], "'src'")) {
+ // Only the iframe allow attribute can define |src_origin|.
+ // When parsing feature policy header, 'src' is disallowed and
+ // |src_origin| = nullptr.
+ // If the iframe will have an opaque origin (for example, if it is
+ // sandboxed, or has a data: URL), then 'src' needs to refer to the
+ // opaque origin of the frame, which is not known yet. In this case,
+ // the |matches_opaque_src| flag on the declaration is set, rather
+ // than adding an origin to the allowlist.
+ if (src_origin->IsUnique()) {
+ whitelist.matches_opaque_src = true;
+ } else {
+ origins.push_back(src_origin->ToUrlOrigin());
+ }
+ } else if (EqualIgnoringASCIICase(tokens[i], "'none'")) {
+ continue;
+ } else if (tokens[i] == "*") {
+ whitelist.matches_all_origins = true;
+ break;
+ } else {
+ url::Origin target_origin = url::Origin::Create(
+ GURL(StringUTF8Adaptor(tokens[i]).AsStringPiece()));
+ if (!target_origin.unique())
+ origins.push_back(target_origin);
+ else if (messages)
+ messages->push_back("Unrecognized origin: '" + tokens[i] + "'.");
+ }
+ }
+ whitelist.origins = origins;
+ whitelists.push_back(whitelist);
+ }
+ }
+ return whitelists;
+}
+
+bool IsSupportedInFeaturePolicy(mojom::FeaturePolicyFeature feature) {
+ switch (feature) {
+ case mojom::FeaturePolicyFeature::kFullscreen:
+ case mojom::FeaturePolicyFeature::kPayment:
+ case mojom::FeaturePolicyFeature::kUsb:
+ case mojom::FeaturePolicyFeature::kWebVr:
+ case mojom::FeaturePolicyFeature::kAccelerometer:
+ case mojom::FeaturePolicyFeature::kAmbientLightSensor:
+ case mojom::FeaturePolicyFeature::kGyroscope:
+ case mojom::FeaturePolicyFeature::kMagnetometer:
+ return true;
+ case mojom::FeaturePolicyFeature::kPictureInPicture:
+ return RuntimeEnabledFeatures::PictureInPictureAPIEnabled();
+ case mojom::FeaturePolicyFeature::kSyncXHR:
+ return true;
+ case mojom::FeaturePolicyFeature::kUnsizedMedia:
+ return RuntimeEnabledFeatures::FeaturePolicyExperimentalFeaturesEnabled();
+ case mojom::FeaturePolicyFeature::kVerticalScroll:
+ return RuntimeEnabledFeatures::FeaturePolicyExperimentalFeaturesEnabled();
+ default:
+ return false;
+ }
+}
+
+const FeatureNameMap& GetDefaultFeatureNameMap() {
+ DEFINE_STATIC_LOCAL(FeatureNameMap, default_feature_name_map, ());
+ if (default_feature_name_map.IsEmpty()) {
+ default_feature_name_map.Set("fullscreen",
+ mojom::FeaturePolicyFeature::kFullscreen);
+ default_feature_name_map.Set("payment",
+ mojom::FeaturePolicyFeature::kPayment);
+ default_feature_name_map.Set("usb", mojom::FeaturePolicyFeature::kUsb);
+ default_feature_name_map.Set("camera",
+ mojom::FeaturePolicyFeature::kCamera);
+ default_feature_name_map.Set("encrypted-media",
+ mojom::FeaturePolicyFeature::kEncryptedMedia);
+ default_feature_name_map.Set("microphone",
+ mojom::FeaturePolicyFeature::kMicrophone);
+ default_feature_name_map.Set("speaker",
+ mojom::FeaturePolicyFeature::kSpeaker);
+ default_feature_name_map.Set("geolocation",
+ mojom::FeaturePolicyFeature::kGeolocation);
+ default_feature_name_map.Set("midi",
+ mojom::FeaturePolicyFeature::kMidiFeature);
+ default_feature_name_map.Set("sync-xhr",
+ mojom::FeaturePolicyFeature::kSyncXHR);
+ default_feature_name_map.Set("vr", mojom::FeaturePolicyFeature::kWebVr);
+ default_feature_name_map.Set("accelerometer",
+ mojom::FeaturePolicyFeature::kAccelerometer);
+ default_feature_name_map.Set(
+ "ambient-light-sensor",
+ mojom::FeaturePolicyFeature::kAmbientLightSensor);
+ default_feature_name_map.Set("gyroscope",
+ mojom::FeaturePolicyFeature::kGyroscope);
+ default_feature_name_map.Set("magnetometer",
+ mojom::FeaturePolicyFeature::kMagnetometer);
+ if (RuntimeEnabledFeatures::PictureInPictureAPIEnabled()) {
+ default_feature_name_map.Set(
+ "picture-in-picture", mojom::FeaturePolicyFeature::kPictureInPicture);
+ }
+ if (RuntimeEnabledFeatures::FeaturePolicyExperimentalFeaturesEnabled()) {
+ default_feature_name_map.Set(
+ "cookie", mojom::FeaturePolicyFeature::kDocumentCookie);
+ default_feature_name_map.Set(
+ "domain", mojom::FeaturePolicyFeature::kDocumentDomain);
+ default_feature_name_map.Set("docwrite",
+ mojom::FeaturePolicyFeature::kDocumentWrite);
+ default_feature_name_map.Set("sync-script",
+ mojom::FeaturePolicyFeature::kSyncScript);
+ default_feature_name_map.Set("unsized-media",
+ mojom::FeaturePolicyFeature::kUnsizedMedia);
+ default_feature_name_map.Set(
+ "vertical-scroll", mojom::FeaturePolicyFeature::kVerticalScroll);
+ }
+ if (RuntimeEnabledFeatures::FeaturePolicyAutoplayFeatureEnabled()) {
+ default_feature_name_map.Set("autoplay",
+ mojom::FeaturePolicyFeature::kAutoplay);
+ }
+ }
+ return default_feature_name_map;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/feature_policy/feature_policy.h b/chromium/third_party/blink/renderer/platform/feature_policy/feature_policy.h
new file mode 100644
index 00000000000..fa88d3f3032
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/feature_policy/feature_policy.h
@@ -0,0 +1,79 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FEATURE_POLICY_FEATURE_POLICY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FEATURE_POLICY_FEATURE_POLICY_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/public/common/feature_policy/feature_policy.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "url/origin.h"
+
+#include <memory>
+
+namespace blink {
+
+// Returns a map between feature name (string) and mojom::FeaturePolicyFeature
+// (enum).
+typedef HashMap<String, mojom::FeaturePolicyFeature> FeatureNameMap;
+PLATFORM_EXPORT const FeatureNameMap& GetDefaultFeatureNameMap();
+
+// Converts a header policy string into a vector of whitelists, one for each
+// feature specified. Unrecognized features are filtered out. If |messages|
+// is not null, then any message in the input will cause a warning message to be
+// appended to it.
+// Example of a feature policy string:
+// "vibrate a.com b.com; fullscreen 'none'; payment 'self', payment *".
+PLATFORM_EXPORT ParsedFeaturePolicy
+ParseFeaturePolicyHeader(const String& policy,
+ scoped_refptr<const SecurityOrigin>,
+ Vector<String>* messages);
+
+// Converts a container policy string into a vector of whitelists, given self
+// and src origins provided, one for each feature specified. Unrecognized
+// features are filtered out. If |messages| is not null, then any message in the
+// input will cause as warning message to be appended to it.
+// Example of a feature policy string:
+// "vibrate a.com 'src'; fullscreen 'none'; payment 'self', payment *".
+// If |old_syntax| is not null, it will be set true if the deprecated
+// space-deparated feature list syntax is detected.
+// TODO(loonybear): remove the boolean once the space separated feature list
+// syntax is deprecated.
+// https://crbug.com/761009.
+PLATFORM_EXPORT ParsedFeaturePolicy
+ParseFeaturePolicyAttribute(const String& policy,
+ scoped_refptr<const SecurityOrigin> self_origin,
+ scoped_refptr<const SecurityOrigin> src_origin,
+ Vector<String>* messages,
+ bool* old_syntax);
+
+// Converts a feature policy string into a vector of whitelists (see comments
+// above), with an explicit FeatureNameMap. This algorithm is called by both
+// header policy parsing and container policy parsing. |self_origin| and
+// |src_origin| are both nullable.
+// If |old_syntax| is not null, it will be set true if the deprecated
+// space-deparated feature list syntax is detected.
+// TODO(loonybear): remove the boolean once the space separated feature list
+// syntax is deprecated.
+// https://crbug.com/761009.
+PLATFORM_EXPORT ParsedFeaturePolicy
+ParseFeaturePolicy(const String& policy,
+ scoped_refptr<const SecurityOrigin> self_origin,
+ scoped_refptr<const SecurityOrigin> src_origin,
+ Vector<String>* messages,
+ const FeatureNameMap& feature_names,
+ bool* old_syntax = nullptr);
+
+// Verifies whether feature policy is enabled and |feature| is supported in
+// feature policy.
+PLATFORM_EXPORT bool IsSupportedInFeaturePolicy(mojom::FeaturePolicyFeature);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FEATURE_POLICY_FEATURE_POLICY_H_
diff --git a/chromium/third_party/blink/renderer/platform/feature_policy/feature_policy_fuzzer.cc b/chromium/third_party/blink/renderer/platform/feature_policy/feature_policy_fuzzer.cc
new file mode 100644
index 00000000000..384ee711640
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/feature_policy/feature_policy_fuzzer.cc
@@ -0,0 +1,26 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/feature_policy/feature_policy.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <memory>
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ static blink::BlinkFuzzerTestSupport test_support =
+ blink::BlinkFuzzerTestSupport();
+ WTF::Vector<WTF::String> messages;
+ // TODO(csharrison): Be smarter about parsing this origin for performance.
+ scoped_refptr<const blink::SecurityOrigin> origin =
+ blink::SecurityOrigin::CreateFromString("https://example.com/");
+ blink::ParseFeaturePolicyHeader(WTF::String(data, size), origin.get(),
+ &messages);
+ return 0;
+}
diff --git a/chromium/third_party/blink/renderer/platform/feature_policy/feature_policy_test.cc b/chromium/third_party/blink/renderer/platform/feature_policy/feature_policy_test.cc
new file mode 100644
index 00000000000..0d3aafc951e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/feature_policy/feature_policy_test.cc
@@ -0,0 +1,322 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/feature_policy/feature_policy.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+#include "url/origin.h"
+
+// Origin strings used for tests
+#define ORIGIN_A "https://example.com/"
+#define ORIGIN_B "https://example.net/"
+#define ORIGIN_C "https://example.org/"
+
+class GURL;
+
+namespace blink {
+
+namespace {
+
+const char* const kValidPolicies[] = {
+ "", // An empty policy.
+ " ", // An empty policy.
+ ";;", // Empty policies.
+ ",,", // Empty policies.
+ " ; ;", // Empty policies.
+ " , ,", // Empty policies.
+ ",;,", // Empty policies.
+ "geolocation 'none'",
+ "geolocation 'self'",
+ "geolocation 'src'", // Only valid for iframe allow attribute.
+ "geolocation", // Only valid for iframe allow attribute.
+ "geolocation; fullscreen; payment",
+ "geolocation *",
+ "geolocation " ORIGIN_A "",
+ "geolocation " ORIGIN_B "",
+ "geolocation " ORIGIN_A " " ORIGIN_B "",
+ "geolocation 'none' " ORIGIN_A " " ORIGIN_B "",
+ "geolocation " ORIGIN_A " 'none' " ORIGIN_B "",
+ "geolocation 'none' 'none' 'none'",
+ "geolocation " ORIGIN_A " *",
+ "fullscreen " ORIGIN_A "; payment 'self'",
+ "fullscreen " ORIGIN_A "; payment *, geolocation 'self'"};
+
+const char* const kInvalidPolicies[] = {
+ "badfeaturename",
+ "badfeaturename 'self'",
+ "1.0",
+ "geolocation data://badorigin",
+ "geolocation https://bad;origin",
+ "geolocation https:/bad,origin",
+ "geolocation https://example.com, https://a.com",
+ "geolocation *, payment data://badorigin",
+ "geolocation ws://xn--fd\xbcwsw3taaaaaBaa333aBBBBBBJBBJBBBt"};
+
+} // namespace
+
+class FeaturePolicyTest : public testing::Test {
+ protected:
+ FeaturePolicyTest() = default;
+
+ ~FeaturePolicyTest() = default;
+
+ scoped_refptr<const SecurityOrigin> origin_a_ =
+ SecurityOrigin::CreateFromString(ORIGIN_A);
+ scoped_refptr<const SecurityOrigin> origin_b_ =
+ SecurityOrigin::CreateFromString(ORIGIN_B);
+ scoped_refptr<const SecurityOrigin> origin_c_ =
+ SecurityOrigin::CreateFromString(ORIGIN_C);
+
+ url::Origin expected_url_origin_a_ = url::Origin::Create(GURL(ORIGIN_A));
+ url::Origin expected_url_origin_b_ = url::Origin::Create(GURL(ORIGIN_B));
+ url::Origin expected_url_origin_c_ = url::Origin::Create(GURL(ORIGIN_C));
+
+ const FeatureNameMap test_feature_name_map = {
+ {"fullscreen", blink::mojom::FeaturePolicyFeature::kFullscreen},
+ {"payment", blink::mojom::FeaturePolicyFeature::kPayment},
+ {"geolocation", blink::mojom::FeaturePolicyFeature::kGeolocation}};
+};
+
+TEST_F(FeaturePolicyTest, ParseValidPolicy) {
+ Vector<String> messages;
+ for (const char* policy_string : kValidPolicies) {
+ messages.clear();
+ ParseFeaturePolicy(policy_string, origin_a_.get(), origin_b_.get(),
+ &messages, test_feature_name_map);
+ EXPECT_EQ(0UL, messages.size());
+ }
+}
+
+TEST_F(FeaturePolicyTest, ParseInvalidPolicy) {
+ Vector<String> messages;
+ for (const char* policy_string : kInvalidPolicies) {
+ messages.clear();
+ ParseFeaturePolicy(policy_string, origin_a_.get(), origin_b_.get(),
+ &messages, test_feature_name_map);
+ EXPECT_NE(0UL, messages.size());
+ }
+}
+
+TEST_F(FeaturePolicyTest, PolicyParsedCorrectly) {
+ Vector<String> messages;
+
+ // Empty policy.
+ ParsedFeaturePolicy parsed_policy = ParseFeaturePolicy(
+ "", origin_a_.get(), origin_b_.get(), &messages, test_feature_name_map);
+ EXPECT_EQ(0UL, parsed_policy.size());
+
+ // Simple policy with 'self'.
+ parsed_policy =
+ ParseFeaturePolicy("geolocation 'self'", origin_a_.get(), origin_b_.get(),
+ &messages, test_feature_name_map);
+ EXPECT_EQ(1UL, parsed_policy.size());
+
+ EXPECT_EQ(mojom::FeaturePolicyFeature::kGeolocation,
+ parsed_policy[0].feature);
+ EXPECT_FALSE(parsed_policy[0].matches_all_origins);
+ EXPECT_FALSE(parsed_policy[0].matches_opaque_src);
+ EXPECT_EQ(1UL, parsed_policy[0].origins.size());
+ EXPECT_TRUE(
+ parsed_policy[0].origins[0].IsSameOriginWith(expected_url_origin_a_));
+
+ // Simple policy with *.
+ parsed_policy =
+ ParseFeaturePolicy("geolocation *", origin_a_.get(), origin_b_.get(),
+ &messages, test_feature_name_map);
+ EXPECT_EQ(1UL, parsed_policy.size());
+ EXPECT_EQ(mojom::FeaturePolicyFeature::kGeolocation,
+ parsed_policy[0].feature);
+ EXPECT_TRUE(parsed_policy[0].matches_all_origins);
+ EXPECT_FALSE(parsed_policy[0].matches_opaque_src);
+ EXPECT_EQ(0UL, parsed_policy[0].origins.size());
+
+ // Complicated policy.
+ parsed_policy = ParseFeaturePolicy(
+ "geolocation *; "
+ "fullscreen https://example.net https://example.org; "
+ "payment 'self'",
+ origin_a_.get(), origin_b_.get(), &messages, test_feature_name_map);
+ EXPECT_EQ(3UL, parsed_policy.size());
+ EXPECT_EQ(mojom::FeaturePolicyFeature::kGeolocation,
+ parsed_policy[0].feature);
+ EXPECT_TRUE(parsed_policy[0].matches_all_origins);
+ EXPECT_FALSE(parsed_policy[0].matches_opaque_src);
+ EXPECT_EQ(0UL, parsed_policy[0].origins.size());
+ EXPECT_EQ(mojom::FeaturePolicyFeature::kFullscreen, parsed_policy[1].feature);
+ EXPECT_FALSE(parsed_policy[1].matches_all_origins);
+ EXPECT_FALSE(parsed_policy[1].matches_opaque_src);
+ EXPECT_EQ(2UL, parsed_policy[1].origins.size());
+ EXPECT_TRUE(
+ parsed_policy[1].origins[0].IsSameOriginWith(expected_url_origin_b_));
+ EXPECT_TRUE(
+ parsed_policy[1].origins[1].IsSameOriginWith(expected_url_origin_c_));
+ EXPECT_EQ(mojom::FeaturePolicyFeature::kPayment, parsed_policy[2].feature);
+ EXPECT_FALSE(parsed_policy[2].matches_all_origins);
+ EXPECT_FALSE(parsed_policy[2].matches_opaque_src);
+ EXPECT_EQ(1UL, parsed_policy[2].origins.size());
+ EXPECT_TRUE(
+ parsed_policy[2].origins[0].IsSameOriginWith(expected_url_origin_a_));
+
+ // Multiple policies.
+ parsed_policy = ParseFeaturePolicy(
+ "geolocation * https://example.net; "
+ "fullscreen https://example.net none https://example.org,"
+ "payment 'self' badorigin",
+ origin_a_.get(), origin_b_.get(), &messages, test_feature_name_map);
+ EXPECT_EQ(3UL, parsed_policy.size());
+ EXPECT_EQ(mojom::FeaturePolicyFeature::kGeolocation,
+ parsed_policy[0].feature);
+ EXPECT_TRUE(parsed_policy[0].matches_all_origins);
+ EXPECT_FALSE(parsed_policy[0].matches_opaque_src);
+ EXPECT_EQ(0UL, parsed_policy[0].origins.size());
+ EXPECT_EQ(mojom::FeaturePolicyFeature::kFullscreen, parsed_policy[1].feature);
+ EXPECT_FALSE(parsed_policy[1].matches_all_origins);
+ EXPECT_FALSE(parsed_policy[1].matches_opaque_src);
+ EXPECT_EQ(2UL, parsed_policy[1].origins.size());
+ EXPECT_TRUE(
+ parsed_policy[1].origins[0].IsSameOriginWith(expected_url_origin_b_));
+ EXPECT_TRUE(
+ parsed_policy[1].origins[1].IsSameOriginWith(expected_url_origin_c_));
+ EXPECT_EQ(mojom::FeaturePolicyFeature::kPayment, parsed_policy[2].feature);
+ EXPECT_FALSE(parsed_policy[2].matches_all_origins);
+ EXPECT_FALSE(parsed_policy[2].matches_opaque_src);
+ EXPECT_EQ(1UL, parsed_policy[2].origins.size());
+ EXPECT_TRUE(
+ parsed_policy[2].origins[0].IsSameOriginWith(expected_url_origin_a_));
+
+ // Old (to be deprecated) iframe allow syntax.
+ messages.clear();
+ parsed_policy =
+ ParseFeaturePolicy("geolocation badname fullscreen payment", nullptr,
+ origin_a_.get(), &messages, test_feature_name_map);
+ // Expect 2 messages: one about deprecation warning, one about unrecognized
+ // feature name.
+ EXPECT_EQ(2UL, messages.size());
+ EXPECT_EQ(3UL, parsed_policy.size());
+ EXPECT_EQ(mojom::FeaturePolicyFeature::kGeolocation,
+ parsed_policy[0].feature);
+ EXPECT_FALSE(parsed_policy[0].matches_all_origins);
+ EXPECT_FALSE(parsed_policy[0].matches_opaque_src);
+ EXPECT_EQ(1UL, parsed_policy[0].origins.size());
+ EXPECT_TRUE(
+ parsed_policy[0].origins[0].IsSameOriginWith(expected_url_origin_a_));
+ EXPECT_EQ(mojom::FeaturePolicyFeature::kFullscreen, parsed_policy[1].feature);
+ EXPECT_FALSE(parsed_policy[1].matches_all_origins);
+ EXPECT_FALSE(parsed_policy[1].matches_opaque_src);
+ EXPECT_EQ(1UL, parsed_policy[1].origins.size());
+ EXPECT_TRUE(
+ parsed_policy[1].origins[0].IsSameOriginWith(expected_url_origin_a_));
+ EXPECT_EQ(mojom::FeaturePolicyFeature::kPayment, parsed_policy[2].feature);
+ EXPECT_FALSE(parsed_policy[2].matches_all_origins);
+ EXPECT_FALSE(parsed_policy[2].matches_opaque_src);
+ EXPECT_EQ(1UL, parsed_policy[2].origins.size());
+ EXPECT_TRUE(
+ parsed_policy[2].origins[0].IsSameOriginWith(expected_url_origin_a_));
+
+ // Header policies with no optional origin lists.
+ parsed_policy =
+ ParseFeaturePolicy("geolocation;fullscreen;payment", origin_a_.get(),
+ nullptr, &messages, test_feature_name_map);
+ EXPECT_EQ(3UL, parsed_policy.size());
+ EXPECT_EQ(mojom::FeaturePolicyFeature::kGeolocation,
+ parsed_policy[0].feature);
+ EXPECT_FALSE(parsed_policy[0].matches_all_origins);
+ EXPECT_FALSE(parsed_policy[0].matches_opaque_src);
+ EXPECT_EQ(1UL, parsed_policy[0].origins.size());
+ EXPECT_TRUE(
+ parsed_policy[0].origins[0].IsSameOriginWith(expected_url_origin_a_));
+ EXPECT_EQ(mojom::FeaturePolicyFeature::kFullscreen, parsed_policy[1].feature);
+ EXPECT_FALSE(parsed_policy[1].matches_all_origins);
+ EXPECT_FALSE(parsed_policy[1].matches_opaque_src);
+ EXPECT_EQ(1UL, parsed_policy[1].origins.size());
+ EXPECT_TRUE(
+ parsed_policy[1].origins[0].IsSameOriginWith(expected_url_origin_a_));
+ EXPECT_EQ(mojom::FeaturePolicyFeature::kPayment, parsed_policy[2].feature);
+ EXPECT_FALSE(parsed_policy[2].matches_all_origins);
+ EXPECT_FALSE(parsed_policy[2].matches_opaque_src);
+ EXPECT_EQ(1UL, parsed_policy[2].origins.size());
+ EXPECT_TRUE(
+ parsed_policy[2].origins[0].IsSameOriginWith(expected_url_origin_a_));
+}
+
+TEST_F(FeaturePolicyTest, PolicyParsedCorrectlyForOpaqueOrigins) {
+ Vector<String> messages;
+
+ scoped_refptr<SecurityOrigin> opaque_origin = SecurityOrigin::CreateUnique();
+
+ // Empty policy.
+ ParsedFeaturePolicy parsed_policy =
+ ParseFeaturePolicy("", origin_a_.get(), opaque_origin.get(), &messages,
+ test_feature_name_map);
+ EXPECT_EQ(0UL, parsed_policy.size());
+
+ // Simple policy.
+ parsed_policy =
+ ParseFeaturePolicy("geolocation", origin_a_.get(), opaque_origin.get(),
+ &messages, test_feature_name_map);
+ EXPECT_EQ(1UL, parsed_policy.size());
+
+ EXPECT_EQ(mojom::FeaturePolicyFeature::kGeolocation,
+ parsed_policy[0].feature);
+ EXPECT_FALSE(parsed_policy[0].matches_all_origins);
+ EXPECT_TRUE(parsed_policy[0].matches_opaque_src);
+ EXPECT_EQ(0UL, parsed_policy[0].origins.size());
+
+ // Simple policy with 'src'.
+ parsed_policy =
+ ParseFeaturePolicy("geolocation 'src'", origin_a_.get(),
+ opaque_origin.get(), &messages, test_feature_name_map);
+ EXPECT_EQ(1UL, parsed_policy.size());
+
+ EXPECT_EQ(mojom::FeaturePolicyFeature::kGeolocation,
+ parsed_policy[0].feature);
+ EXPECT_FALSE(parsed_policy[0].matches_all_origins);
+ EXPECT_TRUE(parsed_policy[0].matches_opaque_src);
+ EXPECT_EQ(0UL, parsed_policy[0].origins.size());
+
+ // Simple policy with *.
+ parsed_policy =
+ ParseFeaturePolicy("geolocation *", origin_a_.get(), opaque_origin.get(),
+ &messages, test_feature_name_map);
+ EXPECT_EQ(1UL, parsed_policy.size());
+
+ EXPECT_EQ(mojom::FeaturePolicyFeature::kGeolocation,
+ parsed_policy[0].feature);
+ EXPECT_TRUE(parsed_policy[0].matches_all_origins);
+ EXPECT_FALSE(parsed_policy[0].matches_opaque_src);
+ EXPECT_EQ(0UL, parsed_policy[0].origins.size());
+
+ // Policy with explicit origins
+ parsed_policy = ParseFeaturePolicy(
+ "geolocation https://example.net https://example.org", origin_a_.get(),
+ opaque_origin.get(), &messages, test_feature_name_map);
+ EXPECT_EQ(1UL, parsed_policy.size());
+
+ EXPECT_EQ(mojom::FeaturePolicyFeature::kGeolocation,
+ parsed_policy[0].feature);
+ EXPECT_FALSE(parsed_policy[0].matches_all_origins);
+ EXPECT_FALSE(parsed_policy[0].matches_opaque_src);
+ EXPECT_EQ(2UL, parsed_policy[0].origins.size());
+ EXPECT_TRUE(
+ parsed_policy[0].origins[0].IsSameOriginWith(expected_url_origin_b_));
+ EXPECT_TRUE(
+ parsed_policy[0].origins[1].IsSameOriginWith(expected_url_origin_c_));
+
+ // Policy with multiple origins, including 'src'.
+ parsed_policy = ParseFeaturePolicy("geolocation https://example.net 'src'",
+ origin_a_.get(), opaque_origin.get(),
+ &messages, test_feature_name_map);
+ EXPECT_EQ(1UL, parsed_policy.size());
+
+ EXPECT_EQ(mojom::FeaturePolicyFeature::kGeolocation,
+ parsed_policy[0].feature);
+ EXPECT_FALSE(parsed_policy[0].matches_all_origins);
+ EXPECT_TRUE(parsed_policy[0].matches_opaque_src);
+ EXPECT_EQ(1UL, parsed_policy[0].origins.size());
+ EXPECT_TRUE(
+ parsed_policy[0].origins[0].IsSameOriginWith(expected_url_origin_b_));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/file_metadata.cc b/chromium/third_party/blink/renderer/platform/file_metadata.cc
new file mode 100644
index 00000000000..b6929e479b7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/file_metadata.cc
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2008, 2009, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/renderer/platform/file_metadata.h"
+
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_file_info.h"
+#include "third_party/blink/public/platform/web_file_utilities.h"
+
+namespace blink {
+
+bool GetFileSize(const String& path, long long& result) {
+ FileMetadata metadata;
+ if (!GetFileMetadata(path, metadata))
+ return false;
+ result = metadata.length;
+ return true;
+}
+
+bool GetFileModificationTime(const String& path, double& result) {
+ FileMetadata metadata;
+ if (!GetFileMetadata(path, metadata))
+ return false;
+ result = metadata.modification_time;
+ return true;
+}
+
+bool GetFileMetadata(const String& path, FileMetadata& metadata) {
+ WebFileInfo web_file_info;
+ if (!Platform::Current()->GetFileUtilities()->GetFileInfo(path,
+ web_file_info))
+ return false;
+ metadata.modification_time = web_file_info.modification_time;
+ metadata.length = web_file_info.length;
+ metadata.type = static_cast<FileMetadata::Type>(web_file_info.type);
+ return true;
+}
+
+String DirectoryName(const String& path) {
+ return Platform::Current()->GetFileUtilities()->DirectoryName(path);
+}
+
+KURL FilePathToURL(const String& path) {
+ return Platform::Current()->GetFileUtilities()->FilePathToURL(path);
+}
+
+STATIC_ASSERT_ENUM(WebFileInfo::kTypeUnknown, FileMetadata::kTypeUnknown);
+STATIC_ASSERT_ENUM(WebFileInfo::kTypeFile, FileMetadata::kTypeFile);
+STATIC_ASSERT_ENUM(WebFileInfo::kTypeDirectory, FileMetadata::kTypeDirectory);
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/file_metadata.h b/chromium/third_party/blink/renderer/platform/file_metadata.h
new file mode 100644
index 00000000000..e437e1caee2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/file_metadata.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2010 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FILE_METADATA_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FILE_METADATA_H_
+
+#include <time.h>
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+inline double InvalidFileTime() {
+ return std::numeric_limits<double>::quiet_NaN();
+}
+inline bool IsValidFileTime(double time) {
+ return std::isfinite(time);
+}
+
+class FileMetadata {
+ DISALLOW_NEW();
+
+ public:
+ FileMetadata()
+ : modification_time(InvalidFileTime()), length(-1), type(kTypeUnknown) {}
+
+ // The last modification time of the file, in milliseconds.
+ // The value NaN means that the time is not known.
+ double modification_time;
+
+ // The length of the file in bytes.
+ // The value -1 means that the length is not set.
+ long long length;
+
+ enum Type { kTypeUnknown = 0, kTypeFile, kTypeDirectory };
+
+ Type type;
+ String platform_path;
+};
+
+PLATFORM_EXPORT bool GetFileSize(const String&, long long& result);
+PLATFORM_EXPORT bool GetFileModificationTime(const String&, double& result);
+PLATFORM_EXPORT bool GetFileMetadata(const String&, FileMetadata&);
+PLATFORM_EXPORT String DirectoryName(const String&);
+PLATFORM_EXPORT KURL FilePathToURL(const String&);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FILE_METADATA_H_
diff --git a/chromium/third_party/blink/renderer/platform/file_system_type.h b/chromium/third_party/blink/renderer/platform/file_system_type.h
new file mode 100644
index 00000000000..2e545dea41d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/file_system_type.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2012 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FILE_SYSTEM_TYPE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FILE_SYSTEM_TYPE_H_
+
+namespace blink {
+
+// For file system types used in FileSystem API.
+//
+// WARNING: These enumerators can be serialized to disk (with IndexedDB).
+// If you have to update this list, also modify deserialization logic to handle
+// the previous version of this enum.
+enum FileSystemType {
+ kFileSystemTypeTemporary,
+ kFileSystemTypePersistent,
+
+ // Transient isolated non-sandboxed filesystem.
+ kFileSystemTypeIsolated,
+
+ // Non-sandbox filesystem.
+ kFileSystemTypeExternal,
+
+ kFileSystemTypeLast = kFileSystemTypeExternal,
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FILE_SYSTEM_TYPE_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/LocaleInFonts.md b/chromium/third_party/blink/renderer/platform/fonts/LocaleInFonts.md
new file mode 100644
index 00000000000..3696adffad3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/LocaleInFonts.md
@@ -0,0 +1,128 @@
+# Locale uses in Fonts
+
+This document summarizes how locales are used in fonts.
+
+[TOC]
+
+## Parsing the Language Tag
+
+The [lang attribute] spec defines to use [BCP 47] as the language tag.
+Since ICU Locale class is not fully compatbile with [BCP 47],
+Blink uses its own simple parser
+in [platform/text/LocaleToScriptMapping.cpp](../text/LocaleToScriptMapping.cpp).
+
+`LayoutLocale::get()` parses [BCP 47] language tags
+and provides methods related with fonts and layout.
+
+`ComputedStyle::getFontDescription().locale()`
+computes the [language of a node] as defined in the spec.
+Note that this includes not only
+the value of the `lang` attribute of the element and its ancestors,
+but also the language of the document from the [content-language] header.
+Refer to the [language of a node] spec for more details.
+
+The [language of a node] could still be unknown.
+`localeOrDefault()` gives you the default language in such case.
+The default language is what Chrome uses for its UI,
+which is passed to the renderer through `--lang` command line argument.
+This could be the same or different from the language of the platform.
+
+Note that `ComputedStyle::locale()` is an `AtomicString`
+for the style system to work without special casing,
+while `FontDescription::locale()` is a pointer to `LayoutLocale`.
+
+[lang attribute]: https://html.spec.whatwg.org/multipage/dom.html#the-lang-and-xml:lang-attributes
+[BCP 47]: https://tools.ietf.org/html/bcp47
+[language of a node]: https://html.spec.whatwg.org/multipage/dom.html#language
+[content-language]: https://html.spec.whatwg.org/multipage/semantics.html#pragma-set-default-language
+
+## Generic Family
+
+Users can configure their preferred fonts for [generic-family]
+using the [Advanced Font Settings].
+Blink has this settings in `GenericFontFamilySettings`.
+In this class, each [generic-family] has a `ScriptFontFamilyMap`,
+which is a map to fonts with `UScriptCode` as the key.
+
+To look up the font to use for a [generic-family],
+Blink uses the following prioritized list to determine the script.
+
+1. The [language of a node] as defined in HTML, if known.
+2. The default language.
+
+This result is available at `ComputedStyle::getFontDescription().localeOrDefault().script()`.
+
+[generic-family]: https://drafts.csswg.org/css-fonts-3/#generic-family-value
+[Advanced Font Settings]: https://chrome.google.com/webstore/detail/advanced-font-settings/caclkomlalccbpcdllchkeecicepbmbm
+
+## System Font Fallback
+
+[CSS Fonts] defines a concept of [system font fallback],
+though its behavior is UA dependent.
+
+As Blink tries to match the font fallback behavior
+to the one in the platform,
+the logic varies by platforms.
+While the complete logic varies by platforms,
+we try to share parts of the logic where possible.
+
+[CSS Fonts]: https://drafts.csswg.org/css-fonts-3/
+[system font fallback]: https://drafts.csswg.org/css-fonts-3/#system-font-fallback
+
+### Unified Han Ideographs
+
+As seen in [CJK Unified Ideographs code charts] in Unicode,
+glyphs of Han Ideographs vary by locales.
+
+To render correct glyphs,
+the system font fallback uses the following prioritized list of locales.
+
+1. The [language of a node] as defined in HTML, if known.
+2. The list of languages the browser sends in the [Accept-Language] header.
+3. The default language.
+4. The system locale (Windows only.)
+
+The prioritized list alone may not help the Unified Han Ideographs.
+For instance, when the top of the list is "en-US",
+it gives no clue to choose the correct font for the Unified Han Ideographs.
+For this purpose,
+`LayoutLocale::hasScriptForHan()` determines whether
+the locale can choose the correct font for the Unified Han Ideographs or not.
+
+When the system font fallback needs to determine the font
+for a Unified Han Ideograph,
+it uses `scriptForHan()` of the first locale in the prioritized list
+that has `hasScriptForHan()` true.
+
+`scriptForHan()` may be different from `script()`,
+in cases such as "en-JP", which indicates an English user in Japan.
+Such locale is not major but is not rare either.
+Some organizations are known to require to use English versions of the OS,
+but their region is not US.
+
+The `script()` of "en-JP" is Latin for the [generic-family] to work correctly,
+but its `scriptForHan()` can indicate that
+the user prefers Japanese variants of glyphs for the Unified Han Ideographs.
+
+There are multiple cases where such locale can appear in the list:
+
+* A site can use such language tag in HTML or in the [content-language] header
+when its UI is in English,
+but knows that the user is in Japan,
+either by IP address, user preferences of the logged on user,
+or any other methods.
+* The system (e.g., Windows) can produce such language tag
+when its language and region are set differently.
+
+This algorithm is currently used in Windows and Linux.
+Android, before N, does not have the language settings and thus
+unable to provide the [Accept-Language] list for this algorithm to consume,
+but Android N Preview supports multi-locale
+and the work to feed the list from OS to the [Accept-Language] list is going on.
+Mac relies on Core Graphics to do the job.
+
+The prioritized list is not consistent across platforms today
+and this is being addressed.
+
+[CJK Unified Ideographs code charts]: http://unicode.org/charts/PDF/U4E00.pdf
+[Accept-Language]: https://tools.ietf.org/html/rfc7231#section-5.3.5
diff --git a/chromium/third_party/blink/renderer/platform/fonts/README.md b/chromium/third_party/blink/renderer/platform/fonts/README.md
new file mode 100644
index 00000000000..7891fbad18a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/README.md
@@ -0,0 +1,382 @@
+# Blink's Text Stack #
+
+This README serves as an documentation entry point of Blink's text stack.
+
+It can be viewed in formatted form [here](https://chromium.googlesource.com/chromium/src/+/master/third_party/blink/renderer/platform/fonts/README.md).
+
+## Overview ##
+
+Blink's font and text stack covers those functional parts of the layout engine
+that provide measurement, geometric operations and drawing for runs of
+CSS-styled HTML text.
+
+The API methods
+in
+[font.h](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/fonts/shaping/font.h) describe
+the interface between the layout code and the font
+code. [font.h](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/fonts/shaping/font.h) provides
+API for mainly three kinds of requests coming from the layout and paint code:
+
+ - Measuring text
+ - Geometric operations used for text selection:
+ - Computing bounding boxes
+ - Mapping from coordinates to character indices
+ - Mapping from character index to coordinates
+ - Painting text
+
+From source HTML to visual output this roughly comprises the following stages:
+
+* [From CSS Styling to Font Objects](#From-CSS-Styling-to-Font-Objects)
+* [Using this font definition for matching against available web and system fonts](#Font-Matching)
+* [An excursion into prerequisites before text shaping can be performed](#excursion_setting-up-shaping)
+* [Segmenting text into portions suitable for shaping](#Run-Segmentation)
+* [Looking up elements from the previously shaped entries in the word cache](#Word-Cache)
+* [Using the matched font for shaping and mapping from characters to glyphs](#Text-Shaping)
+* [Font fallback](#Font-Fallback)
+
+## From CSS Styling to `Font` Objects ##
+
+During
+the
+[style resolution](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/css/resolver/style_resolver.cc?type=cs&l=1028) stage
+of
+layout
+[`ComputedStyle`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/style/computed_style.h) objects
+are calculated for each element of the DOM tree. Each `ComputedStyle` will own
+a
+[`Font`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/fonts/font.h) and
+a
+[`FontDescription`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/fonts/font_description.cc) object.
+For this to work, after CSS is parsed into the various specialized
+`CSSValue`-derived types, the `ConvertFont…` methods
+in
+[`style_builder_converter.cc`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc) convert
+from `CSSValue`s to `FontDescription` data structures. In the opposite
+direction, the `ValueForFont…` methods
+in
+[`computed_style_css_value_mapping.cc`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/css/computed_style_css_value_mapping.cc) convert
+back from `FontDescription` back to CSS values.
+
+Then, during style resolution, `FontBuilder::CreateFont` is called to update the
+`Font` object based on the properties stored in `FontDescription` and thus link
+those two objects together. The key to linking them together is
+`CSSFontSelector`. `Font` is assigned a `CSSFontSelector`, which serves as a
+looking glass to know about which fonts are available in the document's
+scope. This includes fonts from the realm of web fonts as well as system fonts.
+
+The `Font` object does not yet represent a particular typeface. Similarly,
+`FontDescription` does not point to an individual typeface yet. This is because
+`FontDescription` contains a `FontFamily` list of family names originating from
+the CSS `font-family` property.
+
+`Font` objects represent the API for style and layout operations to retrieve
+geometric information about a font used in the DOM: retrieve metrics such as
+line height, x-height etc. and measure text to retrieve its bounding box. The
+paint stage uses `Font` objects to draw text.
+
+Only when such geometric or painting operations are requested from a `Font`
+object, internally `Font` will perform font matching.
+
+## Font Matching ##
+
+As soon as a `Font` object is requested to perform any operation, it needs to
+find an individual typeface to work with. The information stored in its
+`FontDescription` member needs to be resolved against available web fonts or
+system fonts. This process is called font matching. The detailed algorithm for
+this process is described in the CSS Fonts Module,
+section
+[Matching Font Styles](https://drafts.csswg.org/css-fonts/#font-style-matching).
+
+For this purpose, a `Font` object has an `Update(CSSFontSelector*)` method to
+inform it about an updated set of available fonts. `Font` internally delegates
+the resolution of CSS family names from the `font-family` against available
+fonts to its `FontFallbackList` member, and thus hands the `CSSFontSelector`
+down to `FontFallbackList`.
+
+`FontFallbackList` calls `CSSFontSelector::GetFontData` with a `FontDescription`
+to perform a lookup. `CSSFontSelector` will in turn ask `FontFaceCache` to find
+a FontData object among the available web fonts. If there is already a match for
+`FontDescription` in the cache, this `FontData` object is returned. If not,
+the
+[`FontSelectionAlgorithm::IsBetterMatchForRequest`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/fonts/font_selection_algorithm.h?l=44) comparison
+function is used to find the best match among the available web fonts. This
+comparison function implements
+the
+[CSS font matching algorithm](https://drafts.csswg.org/css-fonts/#font-style-matching).
+
+If `CSSFontSelector` can't find a font using this approach, it will try to find
+a font from the system. To this end, it will query `FontCache`. `FontCache` is
+for system fonts what `FontFaceCache` is for web fonts. `FontCache` has
+OS/system specific implementations
+in
+[font_cache_skia.cc](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/fonts/skia/font_cache_skia.cc),
+[font_cache_mac.mm](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/fonts/mac/font_cache_mac.mm),
+[font_cache_linux.cc](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/fonts/linux/font_cache_linux.cc),
+[font_cache_android.cc](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/fonts/android/font_cache_android.cc) in
+order to perform system font lookups using the respective OS/system API.
+
+## Excursion: Setting up Shaping
+
+Before looking at the next stages, we need to understand what text shaping
+means.
+
+Shaping text is the process of mapping a unicode string to a sequence of glyph
+IDs from a font plus their exact geometrical positions through performing
+OpenType layout operations defined in the font. For Latin script, the output is
+mostly glyph IDs and horizontal advances, but for complex scripts, the output
+also describes positioning in the vertical direction, reordered glyphs, and
+association into grapheme clusters.
+
+In more detail, a unicode string is not the only required input for this
+mapping. Instead, a number of variables are required before shaping can be
+performed. For a single run of text ready for shaping, the following input
+variables need to be isolated and constant:
+
+ - Font
+ - Font Size
+ - Text Direction (LTR, RTL)
+ - Text Orientation (Horizontal, Vertical)
+ - Requested OpenType Features
+ - Unicode Script
+ - Unicode Language
+ - Text (the actual text of this run)
+ - Context (the text surrounding this run of text)
+
+This means that before shaping can be performed, incoming text and
+`FontDescription` information coming from the layout code needs to be segmented
+into sub-runs where the above inputs stay the same. For example, if the incoming
+text contains Unicode sequences of different scripts, the incoming text run
+needs to be broken up into sub-runs of only one script. Similarly, if the
+incoming text contains differing orientations, for example Latin text embedded
+in Japanese vertical layout, then these sub-runs need to be isolated and shaped
+separately.
+
+### Emoji
+
+Emoji place additional requirements in isolating sub-runs for shaping. Emoji
+Unicode code points and code point sequences have different default presentation
+styles, text-default, or emoji-default. This is defined in the
+section
+[Presentation Style of Unicode Technical Report #51](http://unicode.org/draft/reports/tr51/tr51.html#Presentation_Style). So
+in order to select the correct font for emoji presentation — either a color
+font, or a regular contour font — the incoming text needs to be segmented and
+isolated by its emoji properties as well.
+
+## Word Cache
+
+Because text shaping and font fallback are costly operations and geometric
+operations on text runs are performed over and over again during layout, a word
+cache is used to speed up these operations.
+
+### Cacheable units
+
+The basic unit for storing shaping results in a cache is a word, separated by
+spaces. Since CJK text often does not use spaces to separate words, for CJK
+text, each individual CJK character is treated as a word.
+
+### Cache Keying
+
+As the [excursion on text shaping explains](#excursion_setting-up-shaping),
+multiple variables go into the shaping equation, such as a fixed font, a fixed
+font size, script, et cetera. This in turn means that the word cache becomes
+invalid if the set of available font changes changes because `FontFallbackList`
+would change in what it returns when available fonts change. We cannot reduce
+the cache key computation for the word cache to the string/word itself plus the
+requested font as represented by `FontDescription`, but we also need to capture
+the set of available fonts at the time shaping for this word and its
+`FontDescription` was performed. This state is captured by computing a composite
+key off of the `FontFallbackList`
+in
+[`FontFallbackList::CompositeKey`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/fonts/font_fallback_list.cpp?l=186).
+
+### Accessing the Cache
+
+[`cachin_gword_shaper.h`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.h) is
+the entry point for retrieving shaping results through the word cache. It defers
+to
+[`caching_word_shape_iterator.h`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.h) for
+word/space or CJK segmentation and responds to requests for a `TextRun`'s
+`Width()` or returns a `ShapeResultBuffer`containing a list of `ShapeResult`
+objects. If not found in the cache `ShapeResult` objects are produced by
+the [text shaping](#Text-Shaping) stage. So `CachingWordShaper` serves as the
+accelerating caching layer between `Font` operations and `HarfBuzzShaper`.
+
+## Run Segmentation ##
+
+The section [Setting up Shaping](#excursion_setting-up-shaping) described the
+requirements for constant input requirements before shaping can be performed.
+
+[RunSegmenter](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/fonts/shaping/run_segmenter.h) is
+the top level API for segmenting incoming text runs using sub-segmenters. It
+splits text by their Unicode script property
+(via
+[`ScriptRunIterator`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/fonts/script_run_iterator.h)),
+orientation and direction — horizontal LTR/RTL, vertical LTR/RTL
+(via
+[`OrientationIterator`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/fonts/orientation_iterator.h)),
+and emoji presentation attributes
+(via
+[`SymbolsIterator`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/fonts/symbols_iterator.h)).
+
+`RunSegmenter` is constructed from a text run in UTF-16 `UChar` format, then
+functions as an iterator returning sub-runs of constant script, emoji
+presentation, and orientation. These sub-runs are then suitable as units for
+shaping.
+
+## Text Shaping ##
+
+The text shaping implementation is
+in
+[shaping/harf_buzz_shaper.h](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.h) and
+[shaping/harf_buzz_shaper.cc](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.cc)
+
+Shaping text runs is split into several
+stages: [Run segmentation](#Run-Segmentation), shaping the initial segment
+starting with the primary font, identifying shaped and non-shaped sequences of
+the shaping result, and processing unshaped sub-runs by trying to shape using
+the remaining list of fonts, then trying fallback fonts until the last resort
+font is reached.
+
+If small/petite caps formatting is requested, an additional lowercase/uppercase
+segmentation pass is required. In this stage, OpenType features in the font are
+matched against the requested formatting. If the respective caps feature is
+found, the feature is used in shaping and activated from the font. Otherwise
+small-caps glyphs are synthesized as required by the CSS Level 3 Fonts Module.
+
+Below we will go through one example — for simplicity without caps formatting —
+to illustrate the process: The following is a run of vertical text to be
+shaped. After run segmentation in `RunSegmenter` it is split into 4
+segments. The segments indicated by the segementation results showing the
+script, orientation information and font fallback preference (text, emoji
+presentation) of the individual segment. The Japanese text at the beginning has
+script "Hiragana", does not need rotation when laid out vertically and does not
+require an emoji font, as indicated by `FontFallbackPriority::kText`.
+
+```
+0 い
+1 ろ
+2 は USCRIPT_HIRAGANA,
+ OrientationIterator::OrientationKeep,
+ FontFallbackPriority::kText
+
+3 a
+4 ̄ (Combining Macron)
+5 a
+6 A USCRIPT_LATIN,
+ OrientationIterator::OrientationRotateSideways,
+ FontFallbackPriority::kText
+
+7 い
+8 ろ
+9 は USCRIPT_HIRAGANA,
+ OrientationIterator::OrientationKeep,
+ FontFallbackPriority::kText
+```
+
+Let's assume the CSS for this text run is as follows:
+ `font-family: "Heiti SC", Tinos, sans-serif;`
+where *Tinos* is a web font, defined as a composite font (i.e. two separate CSS
+`@font-face` instances with
+different
+[`unicode-range` subsetting ranges](https://developer.mozilla.org/en/docs/Web/CSS/@font-face/unicode-range)),
+one for Latin `U+00-U+FF` and one unrestricted `unicode-range`.
+
+`FontFallbackIterator` provides these fonts to the shaper. It will start with
+*Heiti SC*, then the first part of *Tinos* for the restricted unicode-range,
+then the unrestricted full unicode-range part of *Tinos*, then a system
+*sans-serif*.
+
+The initial segment 0-2 is sent to the shaper, together with the segmentation
+properties and the initial Heiti SC font. Characters 0-2 are shaped successfully
+with Heiti SC.
+
+```
+Glyphpos: 0 1 2
+Cluster: 0 1 2
+Glyph: い ろ は
+```
+The next segment, 3-6 is passed to the shaper. The shaper attempts to shape it
+with Heiti SC, which fails for the Combining Macron. So the shaping result for
+this segment would look similar to this.
+
+```
+Glyphpos: 3 4 5 6
+Cluster: 3 3 5 6
+Glyph: a ☐ a A (where ☐ is .notdef)
+```
+
+Now in the `extractShapeResults()` step we notice that there is more work to do,
+since *Heiti SC* does not have a glyph for the Combining Macron combined with an
+a. So, cluster 3 consisting of the characters a plus ̄ (Combining Macron) is
+placed in the `HolesQueue` for clusters that need to be processed after
+switching to the next fallback font.
+
+After shaping the first segment as whole, the font is cycled to the next font
+coming from `FontFallbackIterator` and the remaining items in the `HolesQueue`
+are processed, picking them from the head of the queue.
+
+In this case, the next font is *Tinos* (for the range `U+00-U+FF`). Shaping
+using this font, assuming it is subsetted, fails again since there is no
+combining mark available in the unicode range `U+00-U+FF`. This triggers
+requesting yet another font. This time, the Tinos font for the full range. With
+this, shaping succeeds with the following HarfBuzz result:
+
+```
+Glyphpos: 3 4 5 6
+Cluster: 3 3 5 6
+Glyph: a ◌̄ a A (with glyph coordinates placing the ◌̄ above the first a)
+```
+
+Now this sub run is successfully processed and can be appended to
+`ShapeResult`. A new `ShapeResult::RunInfo` is created. The logic in
+`ShapeResult::insertRun` then takes care of merging the shape result into the
+right position the vector of `RunInfo`s in `ShapeResult`.
+
+Shaping then continues analogously for the remaining Hiragana Japanese sub-run,
+and the result is inserted into `ShapeResult` as well.
+
+## Font Fallback ##
+
+The section [Text Shaping](#Text-Shaping) illustrates that font selection during
+shaping is part of an iterative process, which first tries to use as many glyphs
+as possible from the primary font, then in subsequent iterations proceeds to
+fill gaps from the secondary font and so on until there are no more so called
+`.notdef` glyphs, i.e. no more boxes of text for which no glyph was found.
+
+`FontFallbackIterator` meets the needs of HarfBuzzShaper to deliver new fonts to
+fill such gaps. `FontFallbackIterator` is an iterator style API, which on
+calling `next()` will deliver the first suitable font to
+try. A
+[`FontFallbackList`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/fonts/font_fallback_list.h) is
+the internal representation of fonts resolved from the CSS `font-family:`
+property. `FontFallbackList` attempts to resolve font family names from the CSS
+`font-family:` property in descending order. It tries to find them among the
+list of available web fonts which were declared by `@font-face` rules or added
+to the document using
+the [`FontFace`](https://developer.mozilla.org/en/docs/Web/API/FontFace)
+JavaScript API. If a requested font family is not found among web fonts, system
+fonts are searched next. This behavior matches the requirements of the font
+style matching algorithm of
+the
+[CSS Fonts specification](https://drafts.csswg.org/css-fonts/#font-style-matching),
+which mandates to prioritize web fonts over system fonts.
+
+`FontFallbackIterator` is intialized with a `FontFallbackList` and starts
+retrieving fonts from this list as its first source for fonts. If during shaping
+a run of text `HarfBuzzShaper` keeps requesting additional fonts after
+`FontFallbackList` is exhausted, this means that `HarfBuzzShaper` still tries to
+fill gaps in the run. In other words, the fonts specified in `font-family` did
+not have sufficient glyph coverage to draw the whole run of text on the
+screen. In this situation, system font fallback is invoked, which means
+attempting to find a surrogate font which contains those glyphs that were
+missing so far. To this end `FontFallbackIterator`
+calls
+[`FontCache::FallbackFontForCharacter()`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/fonts/font_cache.h?type=cs&q=fallbackFontForCharacter) in
+order to retrieve a font that has a glyph for the requested Unicode
+character. This means, beyond what is listed in `font-family` there are
+additional system fonts pulled in to the shaping process.
+
+In summary, `FontFallbackIterator` feeds fonts from the CSS `font-family` list
+as well as system fallback fonts to `HarfBuzzShaper` for use in the shaping
+iterations until ideally all gaps are filled and the full text run can be drawn
+with the correct glyphs.
diff --git a/chromium/third_party/blink/renderer/platform/fonts/accept_languages_resolver.cc b/chromium/third_party/blink/renderer/platform/fonts/accept_languages_resolver.cc
new file mode 100644
index 00000000000..3939b0cfd25
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/accept_languages_resolver.cc
@@ -0,0 +1,42 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/accept_languages_resolver.h"
+
+#include "third_party/blink/renderer/platform/fonts/font_global_context.h"
+#include "third_party/blink/renderer/platform/layout_locale.h"
+
+namespace blink {
+
+void AcceptLanguagesResolver::AcceptLanguagesChanged(
+ const String& accept_languages) {
+ String& current_value = FontGlobalContext::CurrentAcceptLanguages();
+ if (current_value == accept_languages)
+ return;
+
+ current_value = accept_languages;
+ FontGlobalContext::InvalidateLocaleForHan();
+}
+
+const LayoutLocale* AcceptLanguagesResolver::LocaleForHan() {
+ return LocaleForHanFromAcceptLanguages(
+ FontGlobalContext::CurrentAcceptLanguages());
+}
+
+const LayoutLocale* AcceptLanguagesResolver::LocaleForHanFromAcceptLanguages(
+ const String& accept_languages) {
+ // Use the first acceptLanguages that can disambiguate.
+ Vector<String> languages;
+ accept_languages.Split(',', languages);
+ for (String token : languages) {
+ token = token.StripWhiteSpace();
+ const LayoutLocale* locale = LayoutLocale::Get(AtomicString(token));
+ if (locale->HasScriptForHan())
+ return locale;
+ }
+
+ return nullptr;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/accept_languages_resolver.h b/chromium/third_party/blink/renderer/platform/fonts/accept_languages_resolver.h
new file mode 100644
index 00000000000..f664ecbf87e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/accept_languages_resolver.h
@@ -0,0 +1,27 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_ACCEPT_LANGUAGES_RESOLVER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_ACCEPT_LANGUAGES_RESOLVER_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+#include <unicode/uscript.h>
+
+namespace blink {
+
+class LayoutLocale;
+
+class PLATFORM_EXPORT AcceptLanguagesResolver {
+ public:
+ static void AcceptLanguagesChanged(const String&);
+
+ static const LayoutLocale* LocaleForHan();
+ static const LayoutLocale* LocaleForHanFromAcceptLanguages(const String&);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_ACCEPT_LANGUAGES_RESOLVER_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/accept_languages_resolver_test.cc b/chromium/third_party/blink/renderer/platform/fonts/accept_languages_resolver_test.cc
new file mode 100644
index 00000000000..ef0be29cc5e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/accept_languages_resolver_test.cc
@@ -0,0 +1,65 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/accept_languages_resolver.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/layout_locale.h"
+
+namespace blink {
+
+TEST(AcceptLanguagesResolverTest, AcceptLanguagesChanged) {
+ struct {
+ const char* accept_languages;
+ UScriptCode script;
+ const char* locale;
+ } tests[] = {
+ // Non-Han script cases.
+ {nullptr, USCRIPT_COMMON, nullptr},
+ {"", USCRIPT_COMMON, nullptr},
+ {"en-US", USCRIPT_COMMON, nullptr},
+ {",en-US", USCRIPT_COMMON, nullptr},
+
+ // Single value cases.
+ {"ja-JP", USCRIPT_KATAKANA_OR_HIRAGANA, "ja-jp"},
+ {"ko-KR", USCRIPT_HANGUL, "ko-kr"},
+ {"zh-CN", USCRIPT_SIMPLIFIED_HAN, "zh-Hans"},
+ {"zh-HK", USCRIPT_TRADITIONAL_HAN, "zh-Hant"},
+ {"zh-TW", USCRIPT_TRADITIONAL_HAN, "zh-Hant"},
+
+ // Language only.
+ {"ja", USCRIPT_KATAKANA_OR_HIRAGANA, "ja-jp"},
+ {"ko", USCRIPT_HANGUL, "ko-kr"},
+ {"zh", USCRIPT_SIMPLIFIED_HAN, "zh-Hans"},
+
+ // Unusual combinations.
+ {"en-JP", USCRIPT_KATAKANA_OR_HIRAGANA, "ja-jp"},
+
+ // Han scripts not in the first item.
+ {"en-US,ja-JP", USCRIPT_KATAKANA_OR_HIRAGANA, "ja-jp"},
+ {"en-US,en-JP", USCRIPT_KATAKANA_OR_HIRAGANA, "ja-jp"},
+
+ // Multiple Han scripts. The first one wins.
+ {"ja-JP,zh-CN", USCRIPT_KATAKANA_OR_HIRAGANA, "ja-jp"},
+ {"zh-TW,ja-JP", USCRIPT_TRADITIONAL_HAN, "zh-Hant"},
+ };
+
+ for (const auto& test : tests) {
+ const LayoutLocale* locale =
+ AcceptLanguagesResolver::LocaleForHanFromAcceptLanguages(
+ test.accept_languages);
+
+ if (test.script == USCRIPT_COMMON) {
+ EXPECT_EQ(nullptr, locale) << test.accept_languages;
+ continue;
+ }
+
+ ASSERT_NE(nullptr, locale) << test.accept_languages;
+ EXPECT_EQ(test.script, locale->GetScriptForHan()) << test.accept_languages;
+ EXPECT_STRCASEEQ(test.locale, locale->LocaleForHanForSkFontMgr())
+ << test.accept_languages;
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/alternate_font_family.h b/chromium/third_party/blink/renderer/platform/fonts/alternate_font_family.h
new file mode 100644
index 00000000000..62ae63d6b66
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/alternate_font_family.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2007 Nicholas Shanks <webkit@nickshanks.com>
+ * Copyright (C) 2013 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_ALTERNATE_FONT_FAMILY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_ALTERNATE_FONT_FAMILY_H_
+
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/font_family_names.h"
+#include "third_party/blink/renderer/platform/fonts/font_description.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+
+namespace blink {
+
+// We currently do not support bitmap fonts on windows.
+// Instead of trying to construct a bitmap font and then going down the fallback
+// path map certain common bitmap fonts to their truetype equivalent up front.
+inline const AtomicString& AdjustFamilyNameToAvoidUnsupportedFonts(
+ const AtomicString& family_name) {
+#if defined(OS_WIN)
+ // On Windows, 'Courier New' (truetype font) is always present and
+ // 'Courier' is a bitmap font. On Mac on the other hand 'Courier' is
+ // a truetype font. Thus pages asking for Courier are better of
+ // using 'Courier New' on windows.
+ if (EqualIgnoringASCIICase(family_name, FontFamilyNames::Courier))
+ return FontFamilyNames::Courier_New;
+
+ // Alias 'MS Sans Serif' (bitmap font) -> 'Microsoft Sans Serif'
+ // (truetype font).
+ if (EqualIgnoringASCIICase(family_name, FontFamilyNames::MS_Sans_Serif))
+ return FontFamilyNames::Microsoft_Sans_Serif;
+
+ // Alias 'MS Serif' (bitmap) -> 'Times New Roman' (truetype font).
+ // Alias 'Times' -> 'Times New Roman' (truetype font).
+ // There's no 'Microsoft Sans Serif-equivalent' for Serif.
+ if (EqualIgnoringASCIICase(family_name, FontFamilyNames::MS_Serif) ||
+ EqualIgnoringASCIICase(family_name, FontFamilyNames::Times))
+ return FontFamilyNames::Times_New_Roman;
+#endif
+
+ return family_name;
+}
+
+inline const AtomicString& AlternateFamilyName(
+ const AtomicString& family_name) {
+ // Alias Courier <-> Courier New
+ if (EqualIgnoringASCIICase(family_name, FontFamilyNames::Courier))
+ return FontFamilyNames::Courier_New;
+#if !defined(OS_WIN)
+ // On Windows, Courier New (truetype font) is always present and
+ // Courier is a bitmap font. So, we don't want to map Courier New to
+ // Courier.
+ if (EqualIgnoringASCIICase(family_name, FontFamilyNames::Courier_New))
+ return FontFamilyNames::Courier;
+#endif
+
+ // Alias Times and Times New Roman.
+ if (EqualIgnoringASCIICase(family_name, FontFamilyNames::Times))
+ return FontFamilyNames::Times_New_Roman;
+ if (EqualIgnoringASCIICase(family_name, FontFamilyNames::Times_New_Roman))
+ return FontFamilyNames::Times;
+
+ // Alias Arial and Helvetica
+ if (EqualIgnoringASCIICase(family_name, FontFamilyNames::Arial))
+ return FontFamilyNames::Helvetica;
+ if (EqualIgnoringASCIICase(family_name, FontFamilyNames::Helvetica))
+ return FontFamilyNames::Arial;
+
+ return g_empty_atom;
+}
+
+inline const AtomicString& GetFallbackFontFamily(
+ const FontDescription& description) {
+ switch (description.GenericFamily()) {
+ case FontDescription::kSansSerifFamily:
+ return FontFamilyNames::sans_serif;
+ case FontDescription::kSerifFamily:
+ return FontFamilyNames::serif;
+ case FontDescription::kMonospaceFamily:
+ return FontFamilyNames::monospace;
+ case FontDescription::kCursiveFamily:
+ return FontFamilyNames::cursive;
+ case FontDescription::kFantasyFamily:
+ return FontFamilyNames::fantasy;
+ default:
+ // Let the caller use the system default font.
+ return g_empty_atom;
+ }
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_ALTERNATE_FONT_FAMILY_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/android/font_cache_android.cc b/chromium/third_party/blink/renderer/platform/fonts/android/font_cache_android.cc
new file mode 100644
index 00000000000..57966613684
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/android/font_cache_android.cc
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/renderer/platform/fonts/font_cache.h"
+
+#include "third_party/blink/renderer/platform/font_family_names.h"
+#include "third_party/blink/renderer/platform/fonts/font_description.h"
+#include "third_party/blink/renderer/platform/fonts/font_face_creation_params.h"
+#include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
+#include "third_party/blink/renderer/platform/language.h"
+#include "third_party/skia/include/core/SkTypeface.h"
+#include "third_party/skia/include/ports/SkFontMgr.h"
+
+namespace blink {
+
+static AtomicString DefaultFontFamily(sk_sp<SkFontMgr> font_manager) {
+ // Pass nullptr to get the default typeface. The default typeface in Android
+ // is "sans-serif" if exists, or the first entry in fonts.xml.
+ sk_sp<SkTypeface> typeface(
+ font_manager->legacyMakeTypeface(nullptr, SkFontStyle()));
+ if (typeface) {
+ SkString family_name;
+ typeface->getFamilyName(&family_name);
+ if (family_name.size())
+ return ToAtomicString(family_name);
+ }
+
+ // Some devices do not return the default typeface. There's not much we can
+ // do here, use "Arial", the value LayoutTheme uses for CSS system font
+ // keywords such as "menu".
+ NOTREACHED();
+ return FontFamilyNames::Arial;
+}
+
+static AtomicString DefaultFontFamily() {
+ if (sk_sp<SkFontMgr> font_manager = FontCache::GetFontCache()->FontManager())
+ return DefaultFontFamily(font_manager);
+ return DefaultFontFamily(SkFontMgr::RefDefault());
+}
+
+// static
+const AtomicString& FontCache::SystemFontFamily() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(AtomicString, system_font_family,
+ (DefaultFontFamily()));
+ return system_font_family;
+}
+
+// static
+void FontCache::SetSystemFontFamily(const AtomicString&) {}
+
+scoped_refptr<SimpleFontData> FontCache::PlatformFallbackFontForCharacter(
+ const FontDescription& font_description,
+ UChar32 c,
+ const SimpleFontData*,
+ FontFallbackPriority fallback_priority) {
+ sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
+ AtomicString family_name = GetFamilyNameForCharacter(
+ fm.get(), c, font_description, fallback_priority);
+ if (family_name.IsEmpty())
+ return GetLastResortFallbackFont(font_description, kDoNotRetain);
+ return FontDataFromFontPlatformData(
+ GetFontPlatformData(font_description,
+ FontFaceCreationParams(family_name)),
+ kDoNotRetain);
+}
+
+// static
+AtomicString FontCache::GetGenericFamilyNameForScript(
+ const AtomicString& family_name,
+ const FontDescription& font_description) {
+ // If monospace, do not apply CJK hack to find i18n fonts, because
+ // i18n fonts are likely not monospace. Monospace is mostly used
+ // for code, but when i18n characters appear in monospace, system
+ // fallback can still render the characters.
+ if (family_name == FontFamilyNames::webkit_monospace)
+ return family_name;
+
+ // The CJK hack below should be removed, at latest when we have
+ // serif and sans-serif versions of CJK fonts. Until then, limit it
+ // to only when the content locale is available. crbug.com/652146
+ const LayoutLocale* content_locale = font_description.Locale();
+ if (!content_locale)
+ return family_name;
+
+ // This is a hack to use the preferred font for CJK scripts.
+ // TODO(kojii): This logic disregards either generic family name
+ // or locale. We need an API that honors both to find appropriate
+ // fonts. crbug.com/642340
+ UChar32 exampler_char;
+ switch (content_locale->GetScript()) {
+ case USCRIPT_SIMPLIFIED_HAN:
+ case USCRIPT_TRADITIONAL_HAN:
+ case USCRIPT_KATAKANA_OR_HIRAGANA:
+ exampler_char = 0x4E00; // A common character in Japanese and Chinese.
+ break;
+ case USCRIPT_HANGUL:
+ exampler_char = 0xAC00;
+ break;
+ default:
+ // For other scripts, use the default generic family mapping logic.
+ return family_name;
+ }
+
+ sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
+ return GetFamilyNameForCharacter(fm.get(), exampler_char, font_description,
+ FontFallbackPriority::kText);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/android/font_cache_android_test.cc b/chromium/third_party/blink/renderer/platform/fonts/android/font_cache_android_test.cc
new file mode 100644
index 00000000000..0148a6771d7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/android/font_cache_android_test.cc
@@ -0,0 +1,57 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/font_cache.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
+
+namespace blink {
+
+TEST(FontCacheAndroid, fallbackFontForCharacter) {
+ // A Latin character in the common locale system font, but not in the
+ // Chinese locale-preferred font.
+ const UChar32 kTestChar = 228;
+
+ FontDescription font_description;
+ font_description.SetLocale(LayoutLocale::Get("zh"));
+ ASSERT_EQ(USCRIPT_SIMPLIFIED_HAN, font_description.GetScript());
+ font_description.SetGenericFamily(FontDescription::kStandardFamily);
+
+ FontCache* font_cache = FontCache::GetFontCache();
+ ASSERT_TRUE(font_cache);
+ scoped_refptr<SimpleFontData> font_data =
+ font_cache->FallbackFontForCharacter(font_description, kTestChar, 0);
+ EXPECT_TRUE(font_data);
+}
+
+TEST(FontCacheAndroid, genericFamilyNameForScript) {
+ FontDescription english;
+ english.SetLocale(LayoutLocale::Get("en"));
+ FontDescription chinese;
+ chinese.SetLocale(LayoutLocale::Get("zh"));
+
+ if (FontFamilyNames::webkit_standard.IsEmpty())
+ FontFamilyNames::init();
+
+ // For non-CJK, getGenericFamilyNameForScript should return the given
+ // familyName.
+ EXPECT_EQ(FontFamilyNames::webkit_standard,
+ FontCache::GetGenericFamilyNameForScript(
+ FontFamilyNames::webkit_standard, english));
+ EXPECT_EQ(FontFamilyNames::webkit_monospace,
+ FontCache::GetGenericFamilyNameForScript(
+ FontFamilyNames::webkit_monospace, english));
+
+ // For CJK, getGenericFamilyNameForScript should return CJK fonts except
+ // monospace.
+ EXPECT_NE(FontFamilyNames::webkit_standard,
+ FontCache::GetGenericFamilyNameForScript(
+ FontFamilyNames::webkit_standard, chinese));
+ EXPECT_EQ(FontFamilyNames::webkit_monospace,
+ FontCache::GetGenericFamilyNameForScript(
+ FontFamilyNames::webkit_monospace, chinese));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/bitmap_glyphs_blacklist.cc b/chromium/third_party/blink/renderer/platform/fonts/bitmap_glyphs_blacklist.cc
new file mode 100644
index 00000000000..099c3dd9e34
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/bitmap_glyphs_blacklist.cc
@@ -0,0 +1,32 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/bitmap_glyphs_blacklist.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+#include "SkTypeface.h"
+
+namespace blink {
+
+namespace {
+
+// Calibri is the only font we encountered which has embeded bitmaps and
+// vector outlines for Latin glyphs. We avoid using the bitmap glyphs
+// because they cause issues with uneven spacing when combined with
+// subpixel positioning, see
+// https://bugs.chromium.org/p/chromium/issues/detail?id=707713#c5
+constexpr const char* kBitmapGlyphsBlacklist[] = {"Calibri", "Courier New"};
+
+} // namespace
+
+bool BitmapGlyphsBlacklist::AvoidEmbeddedBitmapsForTypeface(
+ SkTypeface* typeface) {
+ CHECK(typeface);
+ SkString font_family_name;
+ typeface->getFamilyName(&font_family_name);
+ return font_family_name.equals(kBitmapGlyphsBlacklist[0]) ||
+ font_family_name.equals(kBitmapGlyphsBlacklist[1]);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/bitmap_glyphs_blacklist.h b/chromium/third_party/blink/renderer/platform/fonts/bitmap_glyphs_blacklist.h
new file mode 100644
index 00000000000..56ef5d6f675
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/bitmap_glyphs_blacklist.h
@@ -0,0 +1,21 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_BITMAP_GLYPHS_BLACKLIST_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_BITMAP_GLYPHS_BLACKLIST_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+class SkTypeface;
+
+namespace blink {
+
+class PLATFORM_EXPORT BitmapGlyphsBlacklist {
+ public:
+ static bool AvoidEmbeddedBitmapsForTypeface(SkTypeface*);
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/fonts/bitmap_glyphs_blacklist_test.cc b/chromium/third_party/blink/renderer/platform/fonts/bitmap_glyphs_blacklist_test.cc
new file mode 100644
index 00000000000..d735092e1da
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/bitmap_glyphs_blacklist_test.cc
@@ -0,0 +1,45 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/bitmap_glyphs_blacklist.h"
+#include "third_party/blink/renderer/platform/fonts/font_cache.h"
+
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+#if defined(OS_WIN)
+
+static void TestBitmapGlyphsBlacklisted(AtomicString windows_family_name,
+ bool blacklisted_expected) {
+ FontCache* font_cache = FontCache::GetFontCache();
+ FontDescription font_description;
+ FontFamily font_family;
+ font_family.SetFamily(windows_family_name);
+ font_description.SetFamily(font_family);
+ scoped_refptr<SimpleFontData> simple_font_data =
+ font_cache->GetFontData(font_description, windows_family_name);
+ ASSERT_TRUE(simple_font_data);
+ const FontPlatformData& font_platform_data = simple_font_data->PlatformData();
+ ASSERT_TRUE(font_platform_data.Typeface());
+ ASSERT_EQ(blacklisted_expected,
+ BitmapGlyphsBlacklist::AvoidEmbeddedBitmapsForTypeface(
+ font_platform_data.Typeface()));
+}
+
+TEST(BlacklistBitmapGlyphsTest, Simsun) {
+ TestBitmapGlyphsBlacklisted("Simsun", false);
+}
+
+TEST(BlacklistBitmapGlyphsTest, Arial) {
+ TestBitmapGlyphsBlacklisted("Arial", false);
+}
+
+TEST(BlacklistBitmapGlyphsTest, Calibri) {
+ TestBitmapGlyphsBlacklisted("Calibri", true);
+}
+
+#endif
+}
diff --git a/chromium/third_party/blink/renderer/platform/fonts/canvas_rotation_in_vertical.h b/chromium/third_party/blink/renderer/platform/fonts/canvas_rotation_in_vertical.h
new file mode 100644
index 00000000000..62d6bb25c3f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/canvas_rotation_in_vertical.h
@@ -0,0 +1,13 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_CANVAS_ROTATION_IN_VERTICAL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_CANVAS_ROTATION_IN_VERTICAL_H_
+
+namespace blink {
+
+enum class CanvasRotationInVertical : char { kRegular, kRotateCanvasUpright };
+}
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/fonts/character_range.h b/chromium/third_party/blink/renderer/platform/fonts/character_range.h
new file mode 100644
index 00000000000..04dedacdb99
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/character_range.h
@@ -0,0 +1,28 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_CHARACTER_RANGE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_CHARACTER_RANGE_H_
+
+namespace blink {
+
+struct CharacterRange {
+ CharacterRange(float from, float to, float ascent, float descent)
+ : start(from), end(to), ascent(ascent), descent(descent) {
+ DCHECK_LE(start, end);
+ }
+
+ float Width() const { return end - start; }
+ float Height() const { return ascent + descent; }
+
+ float start;
+ float end;
+
+ float ascent;
+ float descent;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_CHARACTER_RANGE_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/custom_font_data.h b/chromium/third_party/blink/renderer/platform/fonts/custom_font_data.h
new file mode 100644
index 00000000000..cd2bc766efe
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/custom_font_data.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_CUSTOM_FONT_DATA_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_CUSTOM_FONT_DATA_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT CustomFontData : public RefCounted<CustomFontData> {
+ public:
+ static scoped_refptr<CustomFontData> Create() {
+ return base::AdoptRef(new CustomFontData());
+ }
+
+ virtual ~CustomFontData() = default;
+
+ virtual void BeginLoadIfNeeded() const {}
+ virtual bool IsLoading() const { return false; }
+ virtual bool IsLoadingFallback() const { return false; }
+ virtual bool ShouldSkipDrawing() const { return false; }
+ virtual void ClearFontFaceSource() {}
+
+ protected:
+ CustomFontData() = default;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_CUSTOM_FONT_DATA_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/fallback_list_composite_key.h b/chromium/third_party/blink/renderer/platform/fonts/fallback_list_composite_key.h
new file mode 100644
index 00000000000..06a411740ff
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/fallback_list_composite_key.h
@@ -0,0 +1,101 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FALLBACK_LIST_COMPOSITE_KEY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FALLBACK_LIST_COMPOSITE_KEY_H_
+
+#include "third_party/blink/renderer/platform/fonts/font_cache_key.h"
+#include "third_party/blink/renderer/platform/fonts/font_description.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/hash_table_deleted_value_type.h"
+
+namespace blink {
+
+class FontDescription;
+
+// Cache key representing a font description and font fallback list combination
+// as passed into shaping. Used to look up an applicable ShapeCache instance
+// from the global FontCache.
+// TODO(eae,drott): Ideally this should be replaced by a combination of
+// FontDescription and CSSFontSelector.
+struct FallbackListCompositeKey {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ FallbackListCompositeKey(const FontDescription& font_description)
+ : hash_(font_description.StyleHashWithoutFamilyList() << 1),
+ computed_size_(font_description.ComputedSize()),
+ letter_spacing_(font_description.LetterSpacing()),
+ word_spacing_(font_description.WordSpacing()),
+ bitmap_fields_(font_description.BitmapFields()),
+ auxiliary_bitmap_fields_(font_description.AuxiliaryBitmapFields()) {}
+ FallbackListCompositeKey()
+ : hash_(0),
+ computed_size_(0),
+ letter_spacing_(0),
+ word_spacing_(0),
+ bitmap_fields_(0),
+ auxiliary_bitmap_fields_(0) {}
+ FallbackListCompositeKey(WTF::HashTableDeletedValueType)
+ : hash_(kDeletedValueHash),
+ computed_size_(0),
+ letter_spacing_(0),
+ word_spacing_(0),
+ bitmap_fields_(0),
+ auxiliary_bitmap_fields_(0) {}
+
+ void Add(FontCacheKey key) {
+ font_cache_keys_.push_back(key);
+ // Djb2 with the first bit reserved for deleted.
+ hash_ = (((hash_ << 5) + hash_) + key.GetHash()) << 1;
+ }
+
+ unsigned GetHash() const { return hash_; }
+
+ bool operator==(const FallbackListCompositeKey& other) const {
+ return hash_ == other.hash_ && computed_size_ == other.computed_size_ &&
+ letter_spacing_ == other.letter_spacing_ &&
+ word_spacing_ == other.word_spacing_ &&
+ bitmap_fields_ == other.bitmap_fields_ &&
+ auxiliary_bitmap_fields_ == other.auxiliary_bitmap_fields_ &&
+ font_cache_keys_ == other.font_cache_keys_;
+ }
+
+ bool IsHashTableDeletedValue() const { return hash_ == kDeletedValueHash; }
+
+ private:
+ static const unsigned kDeletedValueHash = 1;
+ Vector<FontCacheKey> font_cache_keys_;
+ unsigned hash_;
+
+ float computed_size_;
+ float letter_spacing_;
+ float word_spacing_;
+ unsigned bitmap_fields_;
+ unsigned auxiliary_bitmap_fields_;
+};
+
+struct FallbackListCompositeKeyHash {
+ STATIC_ONLY(FallbackListCompositeKeyHash);
+ static unsigned GetHash(const FallbackListCompositeKey& key) {
+ return key.GetHash();
+ }
+
+ static bool Equal(const FallbackListCompositeKey& a,
+ const FallbackListCompositeKey& b) {
+ return a == b;
+ }
+
+ static const bool safe_to_compare_to_empty_or_deleted = false;
+};
+
+struct FallbackListCompositeKeyTraits
+ : WTF::SimpleClassHashTraits<FallbackListCompositeKey> {
+ STATIC_ONLY(FallbackListCompositeKeyTraits);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FALLBACK_LIST_COMPOSITE_KEY_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font.cc b/chromium/third_party/blink/renderer/platform/fonts/font.cc
new file mode 100644
index 00000000000..511b523682b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font.cc
@@ -0,0 +1,535 @@
+/*
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2006, 2010, 2011 Apple Inc. All rights reserved.
+ * Copyright (c) 2007, 2008, 2010 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "third_party/blink/renderer/platform/fonts/font.h"
+
+#include "third_party/blink/renderer/platform/fonts/character_range.h"
+#include "third_party/blink/renderer/platform/fonts/font_cache.h"
+#include "third_party/blink/renderer/platform/fonts/font_fallback_iterator.h"
+#include "third_party/blink/renderer/platform/fonts/font_fallback_list.h"
+#include "third_party/blink/renderer/platform/fonts/ng_text_fragment_paint_info.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer.h"
+#include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_text_blob.h"
+#include "third_party/blink/renderer/platform/layout_test_support.h"
+#include "third_party/blink/renderer/platform/layout_unit.h"
+#include "third_party/blink/renderer/platform/text/bidi_resolver.h"
+#include "third_party/blink/renderer/platform/text/character.h"
+#include "third_party/blink/renderer/platform/text/text_run.h"
+#include "third_party/blink/renderer/platform/text/text_run_iterator.h"
+#include "third_party/blink/renderer/platform/transforms/affine_transform.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
+#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
+#include "third_party/skia/include/core/SkTextBlob.h"
+
+namespace blink {
+
+Font::Font() : can_shape_word_by_word_(0), shape_word_by_word_computed_(0) {}
+
+Font::Font(const FontDescription& fd)
+ : font_description_(fd),
+ can_shape_word_by_word_(0),
+ shape_word_by_word_computed_(0) {}
+
+Font::Font(const Font& other)
+ : font_description_(other.font_description_),
+ font_fallback_list_(other.font_fallback_list_),
+ // TODO(yosin): We should have a comment the reason why don't we copy
+ // |m_canShapeWordByWord| and |m_shapeWordByWordComputed| from |other|,
+ // since |operator=()| copies them from |other|.
+ can_shape_word_by_word_(0),
+ shape_word_by_word_computed_(0) {}
+
+Font& Font::operator=(const Font& other) {
+ font_description_ = other.font_description_;
+ font_fallback_list_ = other.font_fallback_list_;
+ can_shape_word_by_word_ = other.can_shape_word_by_word_;
+ shape_word_by_word_computed_ = other.shape_word_by_word_computed_;
+ return *this;
+}
+
+bool Font::operator==(const Font& other) const {
+ FontSelector* first =
+ font_fallback_list_ ? font_fallback_list_->GetFontSelector() : nullptr;
+ FontSelector* second = other.font_fallback_list_
+ ? other.font_fallback_list_->GetFontSelector()
+ : nullptr;
+
+ return first == second && font_description_ == other.font_description_ &&
+ (font_fallback_list_
+ ? font_fallback_list_->FontSelectorVersion()
+ : 0) == (other.font_fallback_list_
+ ? other.font_fallback_list_->FontSelectorVersion()
+ : 0) &&
+ (font_fallback_list_ ? font_fallback_list_->Generation() : 0) ==
+ (other.font_fallback_list_
+ ? other.font_fallback_list_->Generation()
+ : 0);
+}
+
+void Font::Update(FontSelector* font_selector) const {
+ // FIXME: It is pretty crazy that we are willing to just poke into a RefPtr,
+ // but it ends up being reasonably safe (because inherited fonts in the render
+ // tree pick up the new style anyway. Other copies are transient, e.g., the
+ // state in the GraphicsContext, and won't stick around long enough to get you
+ // in trouble). Still, this is pretty disgusting, and could eventually be
+ // rectified by using RefPtrs for Fonts themselves.
+ if (!font_fallback_list_)
+ font_fallback_list_ = FontFallbackList::Create();
+ font_fallback_list_->Invalidate(font_selector);
+}
+
+namespace {
+
+void DrawBlobs(PaintCanvas* canvas,
+ const PaintFlags& flags,
+ const ShapeResultBloberizer::BlobBuffer& blobs,
+ const FloatPoint& point) {
+ for (const auto& blob_info : blobs) {
+ DCHECK(blob_info.blob);
+ PaintCanvasAutoRestore auto_restore(canvas, false);
+ if (blob_info.rotation == CanvasRotationInVertical::kRotateCanvasUpright) {
+ canvas->save();
+
+ SkMatrix m;
+ m.setSinCos(-1, 0, point.X(), point.Y());
+ canvas->concat(m);
+ }
+
+ canvas->drawTextBlob(blob_info.blob, point.X(), point.Y(), flags);
+ }
+}
+
+} // anonymous ns
+
+void Font::DrawText(PaintCanvas* canvas,
+ const TextRunPaintInfo& run_info,
+ const FloatPoint& point,
+ float device_scale_factor,
+ const PaintFlags& flags) const {
+ // Don't draw anything while we are using custom fonts that are in the process
+ // of loading.
+ if (ShouldSkipDrawing())
+ return;
+
+ ShapeResultBloberizer bloberizer(*this, device_scale_factor);
+ CachingWordShaper word_shaper(*this);
+ ShapeResultBuffer buffer;
+ word_shaper.FillResultBuffer(run_info, &buffer);
+ bloberizer.FillGlyphs(run_info, buffer);
+ DrawBlobs(canvas, flags, bloberizer.Blobs(), point);
+}
+
+void Font::DrawText(PaintCanvas* canvas,
+ const NGTextFragmentPaintInfo& text_info,
+ const FloatPoint& point,
+ float device_scale_factor,
+ const PaintFlags& flags) const {
+ // Don't draw anything while we are using custom fonts that are in the process
+ // of loading.
+ if (ShouldSkipDrawing())
+ return;
+
+ ShapeResultBloberizer bloberizer(*this, device_scale_factor);
+ bloberizer.FillGlyphs(text_info.text, text_info.from, text_info.to,
+ text_info.shape_result);
+ DrawBlobs(canvas, flags, bloberizer.Blobs(), point);
+}
+
+bool Font::DrawBidiText(PaintCanvas* canvas,
+ const TextRunPaintInfo& run_info,
+ const FloatPoint& point,
+ CustomFontNotReadyAction custom_font_not_ready_action,
+ float device_scale_factor,
+ const PaintFlags& flags) const {
+ // Don't draw anything while we are using custom fonts that are in the process
+ // of loading, except if the 'force' argument is set to true (in which case it
+ // will use a fallback font).
+ if (ShouldSkipDrawing() &&
+ custom_font_not_ready_action == kDoNotPaintIfFontNotReady)
+ return false;
+
+ // sub-run painting is not supported for Bidi text.
+ const TextRun& run = run_info.run;
+ DCHECK_EQ(run_info.from, 0u);
+ DCHECK_EQ(run_info.to, run.length());
+ BidiResolver<TextRunIterator, BidiCharacterRun> bidi_resolver;
+ bidi_resolver.SetStatus(
+ BidiStatus(run.Direction(), run.DirectionalOverride()));
+ bidi_resolver.SetPositionIgnoringNestedIsolates(TextRunIterator(&run, 0));
+
+ // FIXME: This ownership should be reversed. We should pass BidiRunList
+ // to BidiResolver in createBidiRunsForLine.
+ BidiRunList<BidiCharacterRun>& bidi_runs = bidi_resolver.Runs();
+ bidi_resolver.CreateBidiRunsForLine(TextRunIterator(&run, run.length()));
+ if (!bidi_runs.RunCount())
+ return true;
+
+ FloatPoint curr_point = point;
+ BidiCharacterRun* bidi_run = bidi_runs.FirstRun();
+ CachingWordShaper word_shaper(*this);
+ while (bidi_run) {
+ TextRun subrun =
+ run.SubRun(bidi_run->Start(), bidi_run->Stop() - bidi_run->Start());
+ bool is_rtl = bidi_run->Level() % 2;
+ subrun.SetDirection(is_rtl ? TextDirection::kRtl : TextDirection::kLtr);
+ subrun.SetDirectionalOverride(bidi_run->DirOverride(false));
+
+ TextRunPaintInfo subrun_info(subrun);
+ subrun_info.bounds = run_info.bounds;
+
+ ShapeResultBloberizer bloberizer(*this, device_scale_factor);
+ ShapeResultBuffer buffer;
+ word_shaper.FillResultBuffer(subrun_info, &buffer);
+ float run_width = bloberizer.FillGlyphs(subrun_info, buffer);
+ DrawBlobs(canvas, flags, bloberizer.Blobs(), curr_point);
+
+ bidi_run = bidi_run->Next();
+ curr_point.Move(run_width, 0);
+ }
+
+ bidi_runs.DeleteRuns();
+ return true;
+}
+
+void Font::DrawEmphasisMarks(PaintCanvas* canvas,
+ const TextRunPaintInfo& run_info,
+ const AtomicString& mark,
+ const FloatPoint& point,
+ float device_scale_factor,
+ const PaintFlags& flags) const {
+ if (ShouldSkipDrawing())
+ return;
+
+ FontCachePurgePreventer purge_preventer;
+
+ const auto emphasis_glyph_data = GetEmphasisMarkGlyphData(mark);
+ if (!emphasis_glyph_data.font_data)
+ return;
+
+ ShapeResultBloberizer bloberizer(*this, device_scale_factor);
+ CachingWordShaper word_shaper(*this);
+ ShapeResultBuffer buffer;
+ word_shaper.FillResultBuffer(run_info, &buffer);
+ bloberizer.FillTextEmphasisGlyphs(run_info, emphasis_glyph_data, buffer);
+ DrawBlobs(canvas, flags, bloberizer.Blobs(), point);
+}
+
+void Font::DrawEmphasisMarks(PaintCanvas* canvas,
+ const NGTextFragmentPaintInfo& text_info,
+ const AtomicString& mark,
+ const FloatPoint& point,
+ float device_scale_factor,
+ const PaintFlags& flags) const {
+ if (ShouldSkipDrawing())
+ return;
+
+ FontCachePurgePreventer purge_preventer;
+ const auto emphasis_glyph_data = GetEmphasisMarkGlyphData(mark);
+ if (!emphasis_glyph_data.font_data)
+ return;
+
+ ShapeResultBloberizer bloberizer(*this, device_scale_factor);
+ // TODO(layout-dev): This should either not take a direction argument or we
+ // need to plumb the proper one through. I don't think we need it.
+ bloberizer.FillTextEmphasisGlyphs(
+ text_info.text, TextDirection::kLtr, text_info.from, text_info.to,
+ emphasis_glyph_data, text_info.shape_result);
+ DrawBlobs(canvas, flags, bloberizer.Blobs(), point);
+}
+
+float Font::Width(const TextRun& run,
+ HashSet<const SimpleFontData*>* fallback_fonts,
+ FloatRect* glyph_bounds) const {
+ FontCachePurgePreventer purge_preventer;
+ CachingWordShaper shaper(*this);
+ return shaper.Width(run, fallback_fonts, glyph_bounds);
+}
+
+namespace { // anonymous namespace
+
+unsigned InterceptsFromBlobs(const ShapeResultBloberizer::BlobBuffer& blobs,
+ const SkPaint& paint,
+ const std::tuple<float, float>& bounds,
+ SkScalar* intercepts_buffer) {
+ SkScalar bounds_array[2] = {std::get<0>(bounds), std::get<1>(bounds)};
+
+ unsigned num_intervals = 0;
+ for (const auto& blob_info : blobs) {
+ DCHECK(blob_info.blob);
+
+ // ShapeResultBloberizer splits for a new blob rotation, but does not split
+ // for a change in font. A TextBlob can contain runs with differing fonts
+ // and the getTextBlobIntercepts method handles multiple fonts for us. For
+ // upright in vertical blobs we currently have to bail, see crbug.com/655154
+ if (blob_info.rotation == CanvasRotationInVertical::kRotateCanvasUpright)
+ continue;
+
+ SkScalar* offset_intercepts_buffer = nullptr;
+ if (intercepts_buffer)
+ offset_intercepts_buffer = &intercepts_buffer[num_intervals];
+ num_intervals +=
+ paint.getTextBlobIntercepts(blob_info.blob->ToSkTextBlob().get(),
+ bounds_array, offset_intercepts_buffer);
+ }
+ return num_intervals;
+}
+
+void GetTextInterceptsInternal(const ShapeResultBloberizer::BlobBuffer& blobs,
+ const PaintFlags& flags,
+ const std::tuple<float, float>& bounds,
+ Vector<Font::TextIntercept>& intercepts) {
+ // Get the number of intervals, without copying the actual values by
+ // specifying nullptr for the buffer, following the Skia allocation model for
+ // retrieving text intercepts.
+ SkPaint paint = flags.ToSkPaint();
+ unsigned num_intervals = InterceptsFromBlobs(blobs, paint, bounds, nullptr);
+ if (!num_intervals)
+ return;
+ DCHECK_EQ(num_intervals % 2, 0u);
+ intercepts.resize(num_intervals / 2u);
+
+ InterceptsFromBlobs(blobs, paint, bounds,
+ reinterpret_cast<SkScalar*>(intercepts.data()));
+}
+
+} // anonymous namespace
+
+void Font::GetTextIntercepts(const TextRunPaintInfo& run_info,
+ float device_scale_factor,
+ const PaintFlags& flags,
+ const std::tuple<float, float>& bounds,
+ Vector<TextIntercept>& intercepts) const {
+ if (ShouldSkipDrawing())
+ return;
+
+ ShapeResultBloberizer bloberizer(
+ *this, device_scale_factor, ShapeResultBloberizer::Type::kTextIntercepts);
+ CachingWordShaper word_shaper(*this);
+ ShapeResultBuffer buffer;
+ word_shaper.FillResultBuffer(run_info, &buffer);
+ bloberizer.FillGlyphs(run_info, buffer);
+
+ GetTextInterceptsInternal(bloberizer.Blobs(), flags, bounds, intercepts);
+}
+
+void Font::GetTextIntercepts(const NGTextFragmentPaintInfo& text_info,
+ float device_scale_factor,
+ const PaintFlags& flags,
+ const std::tuple<float, float>& bounds,
+ Vector<TextIntercept>& intercepts) const {
+ if (ShouldSkipDrawing())
+ return;
+
+ ShapeResultBloberizer bloberizer(
+ *this, device_scale_factor, ShapeResultBloberizer::Type::kTextIntercepts);
+ bloberizer.FillGlyphs(text_info.text, text_info.from, text_info.to,
+ text_info.shape_result);
+
+ GetTextInterceptsInternal(bloberizer.Blobs(), flags, bounds, intercepts);
+}
+
+static inline FloatRect PixelSnappedSelectionRect(FloatRect rect) {
+ // Using roundf() rather than ceilf() for the right edge as a compromise to
+ // ensure correct caret positioning.
+ float rounded_x = roundf(rect.X());
+ return FloatRect(rounded_x, rect.Y(), roundf(rect.MaxX() - rounded_x),
+ rect.Height());
+}
+
+FloatRect Font::SelectionRectForText(const TextRun& run,
+ const FloatPoint& point,
+ int height,
+ int from,
+ int to) const {
+ to = (to == -1 ? run.length() : to);
+
+ FontCachePurgePreventer purge_preventer;
+
+ CachingWordShaper shaper(*this);
+ CharacterRange range = shaper.GetCharacterRange(run, from, to);
+
+ return PixelSnappedSelectionRect(
+ FloatRect(point.X() + range.start, point.Y(), range.Width(), height));
+}
+
+FloatRect Font::BoundingBox(const TextRun& run) const {
+ FontCachePurgePreventer purge_preventer;
+ CachingWordShaper shaper(*this);
+ CharacterRange range = shaper.GetCharacterRange(run, 0, run.length());
+ return FloatRect(range.start, -range.ascent, range.Width(), range.Height());
+}
+
+int Font::OffsetForPosition(const TextRun& run,
+ float x_float,
+ bool include_partial_glyphs) const {
+ FontCachePurgePreventer purge_preventer;
+ CachingWordShaper shaper(*this);
+ return shaper.OffsetForPosition(run, x_float, include_partial_glyphs);
+}
+
+ShapeCache* Font::GetShapeCache() const {
+ return font_fallback_list_->GetShapeCache(font_description_);
+}
+
+bool Font::CanShapeWordByWord() const {
+ if (!shape_word_by_word_computed_) {
+ can_shape_word_by_word_ = ComputeCanShapeWordByWord();
+ shape_word_by_word_computed_ = true;
+ }
+ return can_shape_word_by_word_;
+};
+
+bool Font::ComputeCanShapeWordByWord() const {
+ if (!GetFontDescription().GetTypesettingFeatures())
+ return true;
+
+ if (!PrimaryFont())
+ return false;
+
+ const FontPlatformData& platform_data = PrimaryFont()->PlatformData();
+ TypesettingFeatures features = GetFontDescription().GetTypesettingFeatures();
+ return !platform_data.HasSpaceInLigaturesOrKerning(features);
+};
+
+void Font::ReportNotDefGlyph() const {
+ FontSelector* fontSelector = font_fallback_list_->GetFontSelector();
+ // We have a few non-DOM usages of Font code, for example in DragImage::Create
+ // and in EmbeddedObjectPainter::paintReplaced. In those cases, we can't
+ // retrieve a font selector as our connection to a Document object to report
+ // UseCounter metrics, and thus we cannot report notdef glyphs.
+ if (fontSelector)
+ fontSelector->ReportNotDefGlyph();
+}
+
+void Font::WillUseFontData(const String& text) const {
+ const FontFamily& family = GetFontDescription().Family();
+ if (font_fallback_list_ && font_fallback_list_->GetFontSelector() &&
+ !family.FamilyIsEmpty())
+ font_fallback_list_->GetFontSelector()->WillUseFontData(
+ GetFontDescription(), family.Family(), text);
+}
+
+scoped_refptr<FontFallbackIterator> Font::CreateFontFallbackIterator(
+ FontFallbackPriority fallback_priority) const {
+ return FontFallbackIterator::Create(font_description_, font_fallback_list_,
+ fallback_priority);
+}
+
+GlyphData Font::GetEmphasisMarkGlyphData(const AtomicString& mark) const {
+ if (mark.IsEmpty())
+ return GlyphData();
+
+ TextRun emphasis_mark_run(mark, mark.length());
+ return CachingWordShaper(*this).EmphasisMarkGlyphData(emphasis_mark_run);
+}
+
+int Font::EmphasisMarkAscent(const AtomicString& mark) const {
+ FontCachePurgePreventer purge_preventer;
+
+ const auto mark_glyph_data = GetEmphasisMarkGlyphData(mark);
+ const SimpleFontData* mark_font_data = mark_glyph_data.font_data;
+ if (!mark_font_data)
+ return 0;
+
+ return mark_font_data->GetFontMetrics().Ascent();
+}
+
+int Font::EmphasisMarkDescent(const AtomicString& mark) const {
+ FontCachePurgePreventer purge_preventer;
+
+ const auto mark_glyph_data = GetEmphasisMarkGlyphData(mark);
+ const SimpleFontData* mark_font_data = mark_glyph_data.font_data;
+ if (!mark_font_data)
+ return 0;
+
+ return mark_font_data->GetFontMetrics().Descent();
+}
+
+int Font::EmphasisMarkHeight(const AtomicString& mark) const {
+ FontCachePurgePreventer purge_preventer;
+
+ const auto mark_glyph_data = GetEmphasisMarkGlyphData(mark);
+ const SimpleFontData* mark_font_data = mark_glyph_data.font_data;
+ if (!mark_font_data)
+ return 0;
+
+ return mark_font_data->GetFontMetrics().Height();
+}
+
+CharacterRange Font::GetCharacterRange(const TextRun& run,
+ unsigned from,
+ unsigned to) const {
+ FontCachePurgePreventer purge_preventer;
+ CachingWordShaper shaper(*this);
+ return shaper.GetCharacterRange(run, from, to);
+}
+
+Vector<CharacterRange> Font::IndividualCharacterRanges(
+ const TextRun& run) const {
+ FontCachePurgePreventer purge_preventer;
+ CachingWordShaper shaper(*this);
+ auto ranges = shaper.IndividualCharacterRanges(run);
+ // The shaper should return ranges.size == run.length but on some platforms
+ // (OSX10.9.5) we are seeing cases in the upper end of the unicode range
+ // where this is not true (see: crbug.com/620952). To catch these cases on
+ // more popular platforms, and to protect users, we are using a CHECK here.
+ CHECK_EQ(ranges.size(), run.length());
+ return ranges;
+}
+
+LayoutUnit Font::TabWidth(const TabSize& tab_size, LayoutUnit position) const {
+ const SimpleFontData* font_data = PrimaryFont();
+ if (!font_data)
+ return LayoutUnit::FromFloatCeil(GetFontDescription().LetterSpacing());
+ float base_tab_width = tab_size.GetPixelSize(font_data->SpaceWidth());
+ if (!base_tab_width)
+ return LayoutUnit::FromFloatCeil(GetFontDescription().LetterSpacing());
+
+ LayoutUnit distance_to_tab_stop = LayoutUnit::FromFloatFloor(
+ base_tab_width - fmodf(position, base_tab_width));
+
+ // Let the minimum width be the half of the space width so that it's always
+ // recognizable. if the distance to the next tab stop is less than that,
+ // advance an additional tab stop.
+ if (distance_to_tab_stop < font_data->SpaceWidth() / 2)
+ distance_to_tab_stop += base_tab_width;
+
+ return distance_to_tab_stop;
+}
+
+bool Font::LoadingCustomFonts() const {
+ return font_fallback_list_ && font_fallback_list_->LoadingCustomFonts();
+}
+
+bool Font::IsFallbackValid() const {
+ return !font_fallback_list_ || font_fallback_list_->IsValid();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font.h b/chromium/third_party/blink/renderer/platform/fonts/font.h
new file mode 100644
index 00000000000..225a28a2044
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font.h
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2006, 2007, 2010, 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2008 Holger Hans Peter Freyther
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_H_
+
+#include "third_party/blink/renderer/platform/fonts/font_description.h"
+#include "third_party/blink/renderer/platform/fonts/font_fallback_list.h"
+#include "third_party/blink/renderer/platform/fonts/font_fallback_priority.h"
+#include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
+#include "third_party/blink/renderer/platform/layout_unit.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/text/tab_size.h"
+#include "third_party/blink/renderer/platform/text/text_direction.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
+
+// To avoid conflicts with the CreateWindow macro from the Windows SDK...
+#undef DrawText
+
+namespace blink {
+
+struct CharacterRange;
+class FloatPoint;
+class FloatRect;
+class FontFallbackIterator;
+class FontData;
+class FontSelector;
+class ShapeCache;
+class TextRun;
+struct TextRunPaintInfo;
+struct NGTextFragmentPaintInfo;
+
+class PLATFORM_EXPORT Font {
+ DISALLOW_NEW();
+
+ public:
+ Font();
+ Font(const FontDescription&);
+ ~Font();
+
+ Font(const Font&);
+ Font& operator=(const Font&);
+
+ bool operator==(const Font& other) const;
+ bool operator!=(const Font& other) const { return !(*this == other); }
+
+ const FontDescription& GetFontDescription() const {
+ return font_description_;
+ }
+
+ void Update(FontSelector*) const;
+
+ enum CustomFontNotReadyAction {
+ kDoNotPaintIfFontNotReady,
+ kUseFallbackIfFontNotReady
+ };
+ void DrawText(PaintCanvas*,
+ const TextRunPaintInfo&,
+ const FloatPoint&,
+ float device_scale_factor,
+ const PaintFlags&) const;
+ void DrawText(PaintCanvas*,
+ const NGTextFragmentPaintInfo&,
+ const FloatPoint&,
+ float device_scale_factor,
+ const PaintFlags&) const;
+ bool DrawBidiText(PaintCanvas*,
+ const TextRunPaintInfo&,
+ const FloatPoint&,
+ CustomFontNotReadyAction,
+ float device_scale_factor,
+ const PaintFlags&) const;
+ void DrawEmphasisMarks(PaintCanvas*,
+ const TextRunPaintInfo&,
+ const AtomicString& mark,
+ const FloatPoint&,
+ float device_scale_factor,
+ const PaintFlags&) const;
+ void DrawEmphasisMarks(PaintCanvas*,
+ const NGTextFragmentPaintInfo&,
+ const AtomicString& mark,
+ const FloatPoint&,
+ float device_scale_factor,
+ const PaintFlags&) const;
+
+ struct TextIntercept {
+ float begin_, end_;
+ };
+
+ // Compute the text intercepts along the axis of the advance and write them
+ // into the specified Vector of TextIntercepts. The number of those is zero or
+ // a multiple of two, and is at most the number of glyphs * 2 in the TextRun
+ // part of TextRunPaintInfo. Specify bounds for the upper and lower extend of
+ // a line crossing through the text, parallel to the baseline.
+ // TODO(drott): crbug.com/655154 Fix this for
+ // upright in vertical.
+ void GetTextIntercepts(const TextRunPaintInfo&,
+ float device_scale_factor,
+ const PaintFlags&,
+ const std::tuple<float, float>& bounds,
+ Vector<TextIntercept>&) const;
+ void GetTextIntercepts(const NGTextFragmentPaintInfo&,
+ float device_scale_factor,
+ const PaintFlags&,
+ const std::tuple<float, float>& bounds,
+ Vector<TextIntercept>&) const;
+
+ // Glyph bounds will be the minimum rect containing all glyph strokes, in
+ // coordinates using (<text run x position>, <baseline position>) as the
+ // origin.
+ float Width(const TextRun&,
+ HashSet<const SimpleFontData*>* fallback_fonts = nullptr,
+ FloatRect* glyph_bounds = nullptr) const;
+
+ int OffsetForPosition(const TextRun&,
+ float position,
+ bool include_partial_glyphs) const;
+ FloatRect SelectionRectForText(const TextRun&,
+ const FloatPoint&,
+ int h,
+ int from = 0,
+ int to = -1) const;
+ FloatRect BoundingBox(const TextRun&) const;
+ CharacterRange GetCharacterRange(const TextRun&,
+ unsigned from,
+ unsigned to) const;
+ Vector<CharacterRange> IndividualCharacterRanges(const TextRun&) const;
+
+ // Metrics that we query the FontFallbackList for.
+ float SpaceWidth() const {
+ DCHECK(PrimaryFont());
+ return (PrimaryFont() ? PrimaryFont()->SpaceWidth() : 0) +
+ GetFontDescription().LetterSpacing();
+ }
+ float TabWidth(const SimpleFontData*, const TabSize&, float position) const;
+ float TabWidth(const TabSize& tab_size, float position) const {
+ return TabWidth(PrimaryFont(), tab_size, position);
+ }
+ LayoutUnit TabWidth(const TabSize&, LayoutUnit position) const;
+
+ int EmphasisMarkAscent(const AtomicString&) const;
+ int EmphasisMarkDescent(const AtomicString&) const;
+ int EmphasisMarkHeight(const AtomicString&) const;
+
+ // This may fail and return a nullptr in case the last resort font cannot be
+ // loaded. This *should* not happen but in reality it does ever now and then
+ // when, for whatever reason, the last resort font cannot be loaded.
+ const SimpleFontData* PrimaryFont() const;
+ const FontData* FontDataAt(unsigned) const;
+
+ // Access the shape cache associated with this particular font object.
+ // Should *not* be retained across layout calls as it may become invalid.
+ ShapeCache* GetShapeCache() const;
+
+ // Whether the font supports shaping word by word instead of shaping the
+ // full run in one go. Allows better caching for fonts where space cannot
+ // participate in kerning and/or ligatures.
+ bool CanShapeWordByWord() const;
+
+ void SetCanShapeWordByWordForTesting(bool b) {
+ can_shape_word_by_word_ = b;
+ shape_word_by_word_computed_ = true;
+ }
+
+ void ReportNotDefGlyph() const;
+
+ private:
+ enum ForTextEmphasisOrNot { kNotForTextEmphasis, kForTextEmphasis };
+
+ GlyphData GetEmphasisMarkGlyphData(const AtomicString&) const;
+
+ bool ComputeCanShapeWordByWord() const;
+
+ public:
+ FontSelector* GetFontSelector() const;
+ scoped_refptr<FontFallbackIterator> CreateFontFallbackIterator(
+ FontFallbackPriority) const;
+
+ void WillUseFontData(const String& text) const;
+
+ bool LoadingCustomFonts() const;
+ bool IsFallbackValid() const;
+
+ private:
+ bool ShouldSkipDrawing() const {
+ return font_fallback_list_ && font_fallback_list_->ShouldSkipDrawing();
+ }
+
+ FontDescription font_description_;
+ mutable scoped_refptr<FontFallbackList> font_fallback_list_;
+ mutable unsigned can_shape_word_by_word_ : 1;
+ mutable unsigned shape_word_by_word_computed_ : 1;
+
+ // For m_fontDescription & m_fontFallbackList access.
+ friend class CachingWordShaper;
+};
+
+inline Font::~Font() = default;
+
+inline const SimpleFontData* Font::PrimaryFont() const {
+ DCHECK(font_fallback_list_);
+ return font_fallback_list_->PrimarySimpleFontData(font_description_);
+}
+
+inline const FontData* Font::FontDataAt(unsigned index) const {
+ DCHECK(font_fallback_list_);
+ return font_fallback_list_->FontDataAt(font_description_, index);
+}
+
+inline FontSelector* Font::GetFontSelector() const {
+ return font_fallback_list_ ? font_fallback_list_->GetFontSelector() : nullptr;
+}
+
+inline float Font::TabWidth(const SimpleFontData* font_data,
+ const TabSize& tab_size,
+ float position) const {
+ if (!font_data)
+ return GetFontDescription().LetterSpacing();
+ float base_tab_width = tab_size.GetPixelSize(font_data->SpaceWidth());
+ if (!base_tab_width)
+ return GetFontDescription().LetterSpacing();
+ float distance_to_tab_stop = base_tab_width - fmodf(position, base_tab_width);
+
+ // Let the minimum width be the half of the space width so that it's always
+ // recognizable. if the distance to the next tab stop is less than that,
+ // advance an additional tab stop.
+ if (distance_to_tab_stop < font_data->SpaceWidth() / 2)
+ distance_to_tab_stop += base_tab_width;
+
+ return distance_to_tab_stop;
+}
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_baseline.h b/chromium/third_party/blink/renderer/platform/fonts/font_baseline.h
new file mode 100644
index 00000000000..8658a15486b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_baseline.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2010 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_BASELINE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_BASELINE_H_
+
+namespace blink {
+
+enum FontBaseline { kAlphabeticBaseline, kIdeographicBaseline };
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_BASELINE_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_cache.cc b/chromium/third_party/blink/renderer/platform/fonts/font_cache.cc
new file mode 100644
index 00000000000..5d3f4ce4fd5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_cache.cc
@@ -0,0 +1,432 @@
+/*
+ * Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2007 Nicholas Shanks <webkit@nickshanks.com>
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/fonts/font_cache.h"
+
+#include <limits>
+#include <memory>
+
+#include "base/debug/alias.h"
+#include "base/memory/ptr_util.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "build/build_config.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/font_family_names.h"
+#include "third_party/blink/renderer/platform/fonts/accept_languages_resolver.h"
+#include "third_party/blink/renderer/platform/fonts/alternate_font_family.h"
+#include "third_party/blink/renderer/platform/fonts/font_cache_client.h"
+#include "third_party/blink/renderer/platform/fonts/font_cache_key.h"
+#include "third_party/blink/renderer/platform/fonts/font_data_cache.h"
+#include "third_party/blink/renderer/platform/fonts/font_description.h"
+#include "third_party/blink/renderer/platform/fonts/font_global_context.h"
+#include "third_party/blink/renderer/platform/fonts/font_platform_data.h"
+#include "third_party/blink/renderer/platform/fonts/font_smoothing_mode.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_cache.h"
+#include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
+#include "third_party/blink/renderer/platform/fonts/text_rendering_mode.h"
+#include "third_party/blink/renderer/platform/histogram.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/web_memory_allocator_dump.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/web_process_memory_dump.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "ui/gfx/font_list.h"
+
+namespace blink {
+
+SkFontMgr* FontCache::static_font_manager_ = nullptr;
+
+#if defined(OS_WIN)
+bool FontCache::antialiased_text_enabled_ = false;
+bool FontCache::lcd_text_enabled_ = false;
+float FontCache::device_scale_factor_ = 1.0;
+bool FontCache::use_skia_font_fallback_ = false;
+#endif // defined(OS_WIN)
+
+FontCache* FontCache::GetFontCache() {
+ return &FontGlobalContext::GetFontCache();
+}
+
+#if !defined(OS_WIN)
+FontCache::FontCache()
+ : purge_prevent_count_(0), font_manager_(sk_ref_sp(static_font_manager_)) {}
+#endif // !defined(OS_WIN) && !defined(OS_LINUX)
+
+#if !defined(OS_MACOSX)
+FontPlatformData* FontCache::SystemFontPlatformData(
+ const FontDescription& font_description) {
+ const AtomicString& family = FontCache::SystemFontFamily();
+#if defined(OS_LINUX)
+ if (family.IsEmpty() || family == FontFamilyNames::system_ui)
+ return nullptr;
+#else
+ DCHECK(!family.IsEmpty() && family != FontFamilyNames::system_ui);
+#endif
+ return GetFontPlatformData(font_description, FontFaceCreationParams(family),
+ AlternateFontName::kNoAlternate);
+}
+#endif
+
+FontPlatformData* FontCache::GetFontPlatformData(
+ const FontDescription& font_description,
+ const FontFaceCreationParams& creation_params,
+ AlternateFontName alternate_font_name) {
+ if (!platform_init_) {
+ platform_init_ = true;
+ PlatformInit();
+ }
+
+#if !defined(OS_MACOSX)
+ if (creation_params.CreationType() == kCreateFontByFamily &&
+ creation_params.Family() == FontFamilyNames::system_ui) {
+ return SystemFontPlatformData(font_description);
+ }
+#endif
+
+ float size = font_description.EffectiveFontSize();
+ unsigned rounded_size = size * FontCacheKey::PrecisionMultiplier();
+ FontCacheKey key = font_description.CacheKey(creation_params);
+
+ // Remove the font size from the cache key, and handle the font size
+ // separately in the inner HashMap. So that different size of FontPlatformData
+ // can share underlying SkTypeface.
+ if (RuntimeEnabledFeatures::FontCacheScalingEnabled())
+ key.ClearFontSize();
+
+ FontPlatformData* result;
+ bool found_result;
+
+ {
+ // addResult's scope must end before we recurse for alternate family names
+ // below, to avoid trigering its dtor hash-changed asserts.
+ SizedFontPlatformDataSet* sized_fonts =
+ &font_platform_data_cache_.insert(key, SizedFontPlatformDataSet())
+ .stored_value->value;
+ bool was_empty = sized_fonts->IsEmpty();
+
+ // Take a different size instance of the same font before adding an entry to
+ // |sizedFont|.
+ FontPlatformData* another_size =
+ was_empty ? nullptr : sized_fonts->begin()->value.get();
+ auto add_result = sized_fonts->insert(rounded_size, nullptr);
+ std::unique_ptr<FontPlatformData>* found = &add_result.stored_value->value;
+ if (add_result.is_new_entry) {
+ if (was_empty) {
+ *found = CreateFontPlatformData(font_description, creation_params, size,
+ alternate_font_name);
+ } else if (another_size) {
+ *found = ScaleFontPlatformData(*another_size, font_description,
+ creation_params, size);
+ }
+ }
+
+ result = found->get();
+ found_result = result || !add_result.is_new_entry;
+ }
+
+ if (!found_result &&
+ alternate_font_name == AlternateFontName::kAllowAlternate &&
+ creation_params.CreationType() == kCreateFontByFamily) {
+ // We were unable to find a font. We have a small set of fonts that we alias
+ // to other names, e.g., Arial/Helvetica, Courier/Courier New, etc. Try
+ // looking up the font under the aliased name.
+ const AtomicString& alternate_name =
+ AlternateFamilyName(creation_params.Family());
+ if (!alternate_name.IsEmpty()) {
+ FontFaceCreationParams create_by_alternate_family(alternate_name);
+ result = GetFontPlatformData(font_description, create_by_alternate_family,
+ AlternateFontName::kNoAlternate);
+ }
+ if (result) {
+ // Cache the result under the old name.
+ auto adding =
+ &font_platform_data_cache_.insert(key, SizedFontPlatformDataSet())
+ .stored_value->value;
+ adding->Set(rounded_size, std::make_unique<FontPlatformData>(*result));
+ }
+ }
+
+ return result;
+}
+
+std::unique_ptr<FontPlatformData> FontCache::ScaleFontPlatformData(
+ const FontPlatformData& font_platform_data,
+ const FontDescription& font_description,
+ const FontFaceCreationParams& creation_params,
+ float font_size) {
+#if defined(OS_MACOSX)
+ return CreateFontPlatformData(font_description, creation_params, font_size);
+#else
+ return std::make_unique<FontPlatformData>(font_platform_data, font_size);
+#endif
+}
+
+ShapeCache* FontCache::GetShapeCache(const FallbackListCompositeKey& key) {
+ FallbackListShaperCache::iterator it = fallback_list_shaper_cache_.find(key);
+ ShapeCache* result = nullptr;
+ if (it == fallback_list_shaper_cache_.end()) {
+ result = new ShapeCache();
+ fallback_list_shaper_cache_.Set(key, base::WrapUnique(result));
+ } else {
+ result = it->value.get();
+ }
+
+ DCHECK(result);
+ return result;
+}
+
+void FontCache::SetFontManager(sk_sp<SkFontMgr> font_manager) {
+ DCHECK(!static_font_manager_);
+ static_font_manager_ = font_manager.release();
+}
+
+void FontCache::AcceptLanguagesChanged(const String& accept_languages) {
+ AcceptLanguagesResolver::AcceptLanguagesChanged(accept_languages);
+ GetFontCache()->InvalidateShapeCache();
+}
+
+scoped_refptr<SimpleFontData> FontCache::GetFontData(
+ const FontDescription& font_description,
+ const AtomicString& family,
+ AlternateFontName altername_font_name,
+ ShouldRetain should_retain) {
+ if (FontPlatformData* platform_data = GetFontPlatformData(
+ font_description,
+ FontFaceCreationParams(
+ AdjustFamilyNameToAvoidUnsupportedFonts(family)),
+ altername_font_name)) {
+ return FontDataFromFontPlatformData(
+ platform_data, should_retain, font_description.SubpixelAscentDescent());
+ }
+
+ return nullptr;
+}
+
+scoped_refptr<SimpleFontData> FontCache::FontDataFromFontPlatformData(
+ const FontPlatformData* platform_data,
+ ShouldRetain should_retain,
+ bool subpixel_ascent_descent) {
+#if DCHECK_IS_ON()
+ if (should_retain == kDoNotRetain)
+ DCHECK(purge_prevent_count_);
+#endif
+
+ return font_data_cache_.Get(platform_data, should_retain,
+ subpixel_ascent_descent);
+}
+
+bool FontCache::IsPlatformFamilyMatchAvailable(
+ const FontDescription& font_description,
+ const AtomicString& family) {
+ return GetFontPlatformData(
+ font_description,
+ FontFaceCreationParams(AdjustFamilyNameToAvoidUnsupportedFonts(family)),
+ AlternateFontName::kNoAlternate);
+}
+
+bool FontCache::IsPlatformFontUniqueNameMatchAvailable(
+ const FontDescription& font_description,
+ const AtomicString& unique_font_name) {
+ return GetFontPlatformData(font_description,
+ FontFaceCreationParams(unique_font_name),
+ AlternateFontName::kLocalUniqueFace);
+}
+
+String FontCache::FirstAvailableOrFirst(const String& families) {
+ // The conversions involve at least two string copies, and more if non-ASCII.
+ // For now we prefer shared code over the cost because a) inputs are
+ // only from grd/xtb and all ASCII, and b) at most only a few times per
+ // setting change/script.
+ return String::FromUTF8(
+ gfx::FontList::FirstAvailableOrFirst(families.Utf8().data()).c_str());
+}
+
+SimpleFontData* FontCache::GetNonRetainedLastResortFallbackFont(
+ const FontDescription& font_description) {
+ auto font = GetLastResortFallbackFont(font_description, kDoNotRetain);
+ if (font)
+ font->AddRef();
+ return font.get();
+}
+
+scoped_refptr<SimpleFontData> FontCache::FallbackFontForCharacter(
+ const FontDescription& description,
+ UChar32 lookup_char,
+ const SimpleFontData* font_data_to_substitute,
+ FontFallbackPriority fallback_priority) {
+ if (Character::IsUnassignedOrPrivateUse(lookup_char))
+ return nullptr;
+ return PlatformFallbackFontForCharacter(
+ description, lookup_char, font_data_to_substitute, fallback_priority);
+}
+
+void FontCache::ReleaseFontData(const SimpleFontData* font_data) {
+ font_data_cache_.Release(font_data);
+}
+
+void FontCache::PurgePlatformFontDataCache() {
+ Vector<FontCacheKey> keys_to_remove;
+ keys_to_remove.ReserveInitialCapacity(font_platform_data_cache_.size());
+ for (auto& sized_fonts : font_platform_data_cache_) {
+ Vector<unsigned> sizes_to_remove;
+ sizes_to_remove.ReserveInitialCapacity(sized_fonts.value.size());
+ for (const auto& platform_data : sized_fonts.value) {
+ if (platform_data.value &&
+ !font_data_cache_.Contains(platform_data.value.get()))
+ sizes_to_remove.push_back(platform_data.key);
+ }
+ sized_fonts.value.RemoveAll(sizes_to_remove);
+ if (sized_fonts.value.IsEmpty())
+ keys_to_remove.push_back(sized_fonts.key);
+ }
+ font_platform_data_cache_.RemoveAll(keys_to_remove);
+}
+
+void FontCache::PurgeFallbackListShaperCache() {
+ unsigned items = 0;
+ FallbackListShaperCache::iterator iter;
+ for (iter = fallback_list_shaper_cache_.begin();
+ iter != fallback_list_shaper_cache_.end(); ++iter) {
+ items += iter->value->size();
+ }
+ fallback_list_shaper_cache_.clear();
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(CustomCountHistogram, shape_cache_histogram,
+ ("Blink.Fonts.ShapeCache", 1, 1000000, 50));
+ shape_cache_histogram.Count(items);
+}
+
+void FontCache::InvalidateShapeCache() {
+ PurgeFallbackListShaperCache();
+}
+
+void FontCache::Purge(PurgeSeverity purge_severity) {
+ // Ideally we should never be forcing the purge while the
+ // FontCachePurgePreventer is in scope, but we call purge() at any timing
+ // via MemoryCoordinator.
+ if (purge_prevent_count_)
+ return;
+
+ if (!font_data_cache_.Purge(purge_severity))
+ return;
+
+ PurgePlatformFontDataCache();
+ PurgeFallbackListShaperCache();
+}
+
+void FontCache::AddClient(FontCacheClient* client) {
+ CHECK(client);
+ if (!font_cache_clients_) {
+ font_cache_clients_ = new HeapHashSet<WeakMember<FontCacheClient>>();
+ font_cache_clients_.RegisterAsStaticReference();
+ }
+ DCHECK(!font_cache_clients_->Contains(client));
+ font_cache_clients_->insert(client);
+}
+
+unsigned short FontCache::Generation() {
+ return generation_;
+}
+
+void FontCache::Invalidate() {
+ font_platform_data_cache_.clear();
+ generation_++;
+
+ if (font_cache_clients_) {
+ for (const auto& client : *font_cache_clients_)
+ client->FontCacheInvalidated();
+ }
+
+ Purge(kForcePurge);
+}
+
+void FontCache::CrashWithFontInfo(const FontDescription* font_description) {
+ FontCache* font_cache = nullptr;
+ SkFontMgr* font_mgr = nullptr;
+ int num_families = std::numeric_limits<int>::min();
+ bool is_test_font_mgr = false;
+ if (FontGlobalContext::Get(kDoNotCreate)) {
+ font_cache = FontCache::GetFontCache();
+ if (font_cache) {
+#if defined(OS_WIN)
+ is_test_font_mgr = font_cache->is_test_font_mgr_;
+#endif
+ font_mgr = font_cache->font_manager_.get();
+ if (font_mgr)
+ num_families = font_mgr->countFamilies();
+ }
+ }
+
+ // In production, these 3 font managers must match.
+ // They don't match in unit tests or in single process mode.
+ SkFontMgr* static_font_mgr = static_font_manager_;
+ SkFontMgr* skia_default_font_mgr = SkFontMgr::RefDefault().get();
+ base::debug::Alias(&font_mgr);
+ base::debug::Alias(&static_font_mgr);
+ base::debug::Alias(&skia_default_font_mgr);
+
+ FontDescription font_description_copy = *font_description;
+ base::debug::Alias(&font_description_copy);
+ base::debug::Alias(&is_test_font_mgr);
+ base::debug::Alias(&num_families);
+
+ CHECK(false);
+}
+
+void FontCache::DumpFontPlatformDataCache(
+ base::trace_event::ProcessMemoryDump* memory_dump) {
+ DCHECK(IsMainThread());
+ base::trace_event::MemoryAllocatorDump* dump =
+ memory_dump->CreateAllocatorDump("font_caches/font_platform_data_cache");
+ size_t font_platform_data_objects_size =
+ font_platform_data_cache_.size() * sizeof(FontPlatformData);
+ dump->AddScalar("size", "bytes", font_platform_data_objects_size);
+ memory_dump->AddSuballocation(dump->guid(),
+ WTF::Partitions::kAllocatedObjectPoolName);
+}
+
+void FontCache::DumpShapeResultCache(
+ base::trace_event::ProcessMemoryDump* memory_dump) {
+ DCHECK(IsMainThread());
+ base::trace_event::MemoryAllocatorDump* dump =
+ memory_dump->CreateAllocatorDump("font_caches/shape_caches");
+ size_t shape_result_cache_size = 0;
+ FallbackListShaperCache::iterator iter;
+ for (iter = fallback_list_shaper_cache_.begin();
+ iter != fallback_list_shaper_cache_.end(); ++iter) {
+ shape_result_cache_size += iter->value->ByteSize();
+ }
+ dump->AddScalar("size", "bytes", shape_result_cache_size);
+ memory_dump->AddSuballocation(dump->guid(),
+ WTF::Partitions::kAllocatedObjectPoolName);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_cache.h b/chromium/third_party/blink/renderer/platform/fonts/font_cache.h
new file mode 100644
index 00000000000..0b081b4afae
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_cache.h
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2006, 2008 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2007-2008 Torch Mobile, Inc.
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_CACHE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_CACHE_H_
+
+#include <limits.h>
+
+#include <memory>
+
+#include "base/memory/scoped_refptr.h"
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/fonts/fallback_list_composite_key.h"
+#include "third_party/blink/renderer/platform/fonts/font_cache_client.h"
+#include "third_party/blink/renderer/platform/fonts/font_cache_key.h"
+#include "third_party/blink/renderer/platform/fonts/font_data_cache.h"
+#include "third_party/blink/renderer/platform/fonts/font_face_creation_params.h"
+#include "third_party/blink/renderer/platform/fonts/font_fallback_priority.h"
+#include "third_party/blink/renderer/platform/fonts/font_platform_data.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_cache.h"
+#include "third_party/blink/renderer/platform/heap/heap_allocator.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+#include "SkFontMgr.h"
+
+class SkString;
+class SkTypeface;
+
+namespace base {
+namespace trace_event {
+class ProcessMemoryDump;
+}
+}
+
+namespace blink {
+
+class FontFaceCreationParams;
+class FontGlobalContext;
+class FontDescription;
+class SimpleFontData;
+
+enum class AlternateFontName {
+ kAllowAlternate,
+ kNoAlternate,
+ kLocalUniqueFace,
+ kLastResort
+};
+
+typedef HashMap<unsigned,
+ std::unique_ptr<FontPlatformData>,
+ WTF::IntHash<unsigned>,
+ WTF::UnsignedWithZeroKeyHashTraits<unsigned>>
+ SizedFontPlatformDataSet;
+typedef HashMap<FontCacheKey,
+ SizedFontPlatformDataSet,
+ FontCacheKeyHash,
+ FontCacheKeyTraits>
+ FontPlatformDataCache;
+typedef HashMap<FallbackListCompositeKey,
+ std::unique_ptr<ShapeCache>,
+ FallbackListCompositeKeyHash,
+ FallbackListCompositeKeyTraits>
+ FallbackListShaperCache;
+
+class PLATFORM_EXPORT FontCache {
+ friend class FontCachePurgePreventer;
+
+ WTF_MAKE_NONCOPYABLE(FontCache);
+ USING_FAST_MALLOC(FontCache);
+
+ public:
+ static FontCache* GetFontCache();
+
+ void ReleaseFontData(const SimpleFontData*);
+
+ scoped_refptr<SimpleFontData> FallbackFontForCharacter(
+ const FontDescription&,
+ UChar32,
+ const SimpleFontData* font_data_to_substitute,
+ FontFallbackPriority = FontFallbackPriority::kText);
+
+ // Also implemented by the platform.
+ void PlatformInit();
+
+ scoped_refptr<SimpleFontData> GetFontData(
+ const FontDescription&,
+ const AtomicString&,
+ AlternateFontName = AlternateFontName::kAllowAlternate,
+ ShouldRetain = kRetain);
+ scoped_refptr<SimpleFontData> GetLastResortFallbackFont(const FontDescription&,
+ ShouldRetain = kRetain);
+ SimpleFontData* GetNonRetainedLastResortFallbackFont(const FontDescription&);
+
+ // Should be used in determining whether family names listed in font-family:
+ // ... are available locally. Only returns true if family name matches.
+ bool IsPlatformFamilyMatchAvailable(const FontDescription&,
+ const AtomicString& family);
+
+ // Should be used in determining whether the <abc> argument to local in
+ // @font-face { ... src: local(<abc>) } are available locally, which should
+ // match Postscript name or full font name. Compare
+ // https://drafts.csswg.org/css-fonts-3/#src-desc
+ // TODO crbug.com/627143 complete this and actually look at the right
+ // namerecords.
+ bool IsPlatformFontUniqueNameMatchAvailable(
+ const FontDescription&,
+ const AtomicString& unique_font_name);
+
+ static String FirstAvailableOrFirst(const String&);
+
+ // Returns the ShapeCache instance associated with the given cache key.
+ // Creates a new instance as needed and as such is guaranteed not to return
+ // a nullptr. Instances are managed by FontCache and are only guaranteed to
+ // be valid for the duration of the current session, as controlled by
+ // disable/enablePurging.
+ ShapeCache* GetShapeCache(const FallbackListCompositeKey&);
+
+ void AddClient(FontCacheClient*);
+
+ unsigned short Generation();
+ void Invalidate();
+
+ sk_sp<SkFontMgr> FontManager() { return font_manager_; }
+ static void SetFontManager(sk_sp<SkFontMgr>);
+
+#if !defined(OS_MACOSX)
+ static const AtomicString& SystemFontFamily();
+#else
+ static const AtomicString& LegacySystemFontFamily();
+#endif
+
+#if !defined(OS_WIN) && !defined(OS_MACOSX)
+ static void SetSystemFontFamily(const AtomicString&);
+#endif
+
+#if defined(OS_WIN)
+ // TODO(https://crbug.com/808221) System font style configuration is not
+ // related to FontCache. Move it somewhere else, e.g. to WebThemeEngine.
+ static bool AntialiasedTextEnabled() { return antialiased_text_enabled_; }
+ static bool LcdTextEnabled() { return lcd_text_enabled_; }
+ static float DeviceScaleFactor() { return device_scale_factor_; }
+ static void SetAntialiasedTextEnabled(bool enabled) {
+ antialiased_text_enabled_ = enabled;
+ }
+ static void SetLCDTextEnabled(bool enabled) { lcd_text_enabled_ = enabled; }
+ static void SetDeviceScaleFactor(float device_scale_factor) {
+ device_scale_factor_ = device_scale_factor;
+ }
+ static void AddSideloadedFontForTesting(sk_sp<SkTypeface>);
+ // Functions to cache and retrieve the system font metrics.
+ static void SetMenuFontMetrics(const wchar_t* family_name,
+ int32_t font_height);
+ static void SetSmallCaptionFontMetrics(const wchar_t* family_name,
+ int32_t font_height);
+ static void SetStatusFontMetrics(const wchar_t* family_name,
+ int32_t font_height);
+ static int32_t MenuFontHeight() { return menu_font_height_; }
+ static const AtomicString& MenuFontFamily() {
+ return *menu_font_family_name_;
+ }
+ static int32_t SmallCaptionFontHeight() { return small_caption_font_height_; }
+ static const AtomicString& SmallCaptionFontFamily() {
+ return *small_caption_font_family_name_;
+ }
+ static int32_t StatusFontHeight() { return status_font_height_; }
+ static const AtomicString& StatusFontFamily() {
+ return *status_font_family_name_;
+ }
+ static void SetUseSkiaFontFallback(bool use_skia_font_fallback) {
+ use_skia_font_fallback_ = use_skia_font_fallback;
+ }
+#endif // defined(OS_WIN)
+
+ static void AcceptLanguagesChanged(const String&);
+
+#if defined(OS_ANDROID)
+ static AtomicString GetGenericFamilyNameForScript(
+ const AtomicString& family_name,
+ const FontDescription&);
+#endif // defined(OS_ANDROID)
+
+#if defined(OS_LINUX)
+ struct PlatformFallbackFont {
+ String name;
+ CString filename;
+ int fontconfig_interface_id;
+ int ttc_index;
+ bool is_bold;
+ bool is_italic;
+ };
+ static void GetFontForCharacter(UChar32,
+ const char* preferred_locale,
+ PlatformFallbackFont*);
+#endif // defined(OS_LINUX)
+
+ scoped_refptr<SimpleFontData> FontDataFromFontPlatformData(
+ const FontPlatformData*,
+ ShouldRetain = kRetain,
+ bool subpixel_ascent_descent = false);
+
+ void InvalidateShapeCache();
+
+ static void CrashWithFontInfo(const FontDescription*);
+
+ // Memory reporting
+ void DumpFontPlatformDataCache(base::trace_event::ProcessMemoryDump*);
+ void DumpShapeResultCache(base::trace_event::ProcessMemoryDump*);
+
+ ~FontCache() = default;
+
+ private:
+ scoped_refptr<SimpleFontData> PlatformFallbackFontForCharacter(
+ const FontDescription&,
+ UChar32,
+ const SimpleFontData* font_data_to_substitute,
+ FontFallbackPriority = FontFallbackPriority::kText);
+
+ friend class FontGlobalContext;
+ FontCache();
+
+ void Purge(PurgeSeverity = kPurgeIfNeeded);
+
+ void DisablePurging() { purge_prevent_count_++; }
+ void EnablePurging() {
+ DCHECK(purge_prevent_count_);
+ if (!--purge_prevent_count_)
+ Purge(kPurgeIfNeeded);
+ }
+
+ // FIXME: This method should eventually be removed.
+ FontPlatformData* GetFontPlatformData(
+ const FontDescription&,
+ const FontFaceCreationParams&,
+ AlternateFontName = AlternateFontName::kAllowAlternate);
+#if !defined(OS_MACOSX)
+ FontPlatformData* SystemFontPlatformData(const FontDescription&);
+#endif // !defined(OS_MACOSX)
+
+ // These methods are implemented by each platform.
+ std::unique_ptr<FontPlatformData> CreateFontPlatformData(
+ const FontDescription&,
+ const FontFaceCreationParams&,
+ float font_size,
+ AlternateFontName = AlternateFontName::kAllowAlternate);
+ std::unique_ptr<FontPlatformData> ScaleFontPlatformData(
+ const FontPlatformData&,
+ const FontDescription&,
+ const FontFaceCreationParams&,
+ float font_size);
+
+ // Implemented on skia platforms.
+ PaintTypeface CreateTypeface(const FontDescription&,
+ const FontFaceCreationParams&,
+ CString& name);
+
+#if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_FUCHSIA)
+ static AtomicString GetFamilyNameForCharacter(SkFontMgr*,
+ UChar32,
+ const FontDescription&,
+ FontFallbackPriority);
+#endif // defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_FUCHSIA)
+
+ scoped_refptr<SimpleFontData> FallbackOnStandardFontStyle(const FontDescription&,
+ UChar32);
+
+ // Don't purge if this count is > 0;
+ int purge_prevent_count_;
+
+ sk_sp<SkFontMgr> font_manager_;
+
+ // A leaky owning bare pointer.
+ static SkFontMgr* static_font_manager_;
+
+#if defined(OS_WIN)
+ static bool antialiased_text_enabled_;
+ static bool lcd_text_enabled_;
+ static float device_scale_factor_;
+ static HashMap<String, sk_sp<SkTypeface>>* sideloaded_fonts_;
+ // The system font metrics cache.
+ static AtomicString* menu_font_family_name_;
+ static int32_t menu_font_height_;
+ static AtomicString* small_caption_font_family_name_;
+ static int32_t small_caption_font_height_;
+ static AtomicString* status_font_family_name_;
+ static int32_t status_font_height_;
+ static bool use_skia_font_fallback_;
+
+ // Windows creates an SkFontMgr for unit testing automatically. This flag is
+ // to ensure it's not happening in the production from the crash log.
+ bool is_test_font_mgr_ = false;
+#endif // defined(OS_WIN)
+
+ unsigned short generation_ = 0;
+ bool platform_init_ = false;
+ Persistent<HeapHashSet<WeakMember<FontCacheClient>>> font_cache_clients_;
+ FontPlatformDataCache font_platform_data_cache_;
+ FallbackListShaperCache fallback_list_shaper_cache_;
+ FontDataCache font_data_cache_;
+
+ void PurgePlatformFontDataCache();
+ void PurgeFallbackListShaperCache();
+
+ friend class SimpleFontData; // For fontDataFromFontPlatformData
+ friend class FontFallbackList;
+};
+
+class PLATFORM_EXPORT FontCachePurgePreventer {
+ USING_FAST_MALLOC(FontCachePurgePreventer);
+ WTF_MAKE_NONCOPYABLE(FontCachePurgePreventer);
+
+ public:
+ FontCachePurgePreventer() { FontCache::GetFontCache()->DisablePurging(); }
+ ~FontCachePurgePreventer() { FontCache::GetFontCache()->EnablePurging(); }
+};
+
+AtomicString ToAtomicString(const SkString&);
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_cache_client.h b/chromium/third_party/blink/renderer/platform/fonts/font_cache_client.h
new file mode 100644
index 00000000000..d56df8c0c03
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_cache_client.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_CACHE_CLIENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_CACHE_CLIENT_H_
+
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT FontCacheClient
+ : public GarbageCollectedFinalized<FontCacheClient> {
+ public:
+ virtual ~FontCacheClient() = default;
+
+ virtual void FontCacheInvalidated() = 0;
+ virtual void Trace(blink::Visitor* visitor) {}
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_CACHE_CLIENT_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_cache_key.h b/chromium/third_party/blink/renderer/platform/fonts/font_cache_key.h
new file mode 100644
index 00000000000..305c1b0a47f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_cache_key.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2013 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:
+ *
+ * 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 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_CACHE_KEY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_CACHE_KEY_H_
+
+#include "third_party/blink/renderer/platform/fonts/font_face_creation_params.h"
+#include "third_party/blink/renderer/platform/fonts/opentype/font_settings.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/hash_table_deleted_value_type.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+
+namespace blink {
+
+// Multiplying the floating point size by 100 gives two decimal point
+// precision which should be sufficient.
+static const unsigned kFontSizePrecisionMultiplier = 100;
+
+struct FontCacheKey {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ FontCacheKey() : creation_params_(), font_size_(0), options_(0) {}
+ FontCacheKey(FontFaceCreationParams creation_params,
+ float font_size,
+ unsigned options,
+ scoped_refptr<FontVariationSettings> variation_settings)
+ : creation_params_(creation_params),
+ font_size_(font_size * kFontSizePrecisionMultiplier),
+ options_(options),
+ variation_settings_(std::move(variation_settings)) {}
+
+ FontCacheKey(WTF::HashTableDeletedValueType)
+ : font_size_(HashTableDeletedSize()) {}
+
+ unsigned GetHash() const {
+ unsigned hash_codes[4] = {
+ creation_params_.GetHash(), font_size_, options_,
+ variation_settings_ ? variation_settings_->GetHash() : 0};
+ return StringHasher::HashMemory<sizeof(hash_codes)>(hash_codes);
+ }
+
+ bool operator==(const FontCacheKey& other) const {
+ return creation_params_ == other.creation_params_ &&
+ font_size_ == other.font_size_ && options_ == other.options_ &&
+ variation_settings_ == other.variation_settings_;
+ }
+
+ bool IsHashTableDeletedValue() const {
+ return font_size_ == HashTableDeletedSize();
+ }
+
+ static unsigned PrecisionMultiplier() { return kFontSizePrecisionMultiplier; }
+
+ void ClearFontSize() { font_size_ = 0; }
+
+ private:
+ static unsigned HashTableDeletedSize() { return 0xFFFFFFFFU; }
+
+ FontFaceCreationParams creation_params_;
+ unsigned font_size_;
+ unsigned options_;
+ scoped_refptr<FontVariationSettings> variation_settings_;
+};
+
+struct FontCacheKeyHash {
+ STATIC_ONLY(FontCacheKeyHash);
+ static unsigned GetHash(const FontCacheKey& key) { return key.GetHash(); }
+
+ static bool Equal(const FontCacheKey& a, const FontCacheKey& b) {
+ return a == b;
+ }
+
+ static const bool safe_to_compare_to_empty_or_deleted = true;
+};
+
+struct FontCacheKeyTraits : WTF::SimpleClassHashTraits<FontCacheKey> {
+ STATIC_ONLY(FontCacheKeyTraits);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_CACHE_KEY_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_cache_memory_dump_provider.cc b/chromium/third_party/blink/renderer/platform/fonts/font_cache_memory_dump_provider.cc
new file mode 100644
index 00000000000..b079ea3fa81
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_cache_memory_dump_provider.cc
@@ -0,0 +1,26 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/font_cache_memory_dump_provider.h"
+
+#include "third_party/blink/renderer/platform/fonts/font_cache.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+namespace blink {
+
+FontCacheMemoryDumpProvider* FontCacheMemoryDumpProvider::Instance() {
+ DEFINE_STATIC_LOCAL(FontCacheMemoryDumpProvider, instance, ());
+ return &instance;
+}
+
+bool FontCacheMemoryDumpProvider::OnMemoryDump(
+ const base::trace_event::MemoryDumpArgs&,
+ base::trace_event::ProcessMemoryDump* memory_dump) {
+ DCHECK(IsMainThread());
+ FontCache::GetFontCache()->DumpFontPlatformDataCache(memory_dump);
+ FontCache::GetFontCache()->DumpShapeResultCache(memory_dump);
+ return true;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_cache_memory_dump_provider.h b/chromium/third_party/blink/renderer/platform/fonts/font_cache_memory_dump_provider.h
new file mode 100644
index 00000000000..12896385fae
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_cache_memory_dump_provider.h
@@ -0,0 +1,36 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_CACHE_MEMORY_DUMP_PROVIDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_CACHE_MEMORY_DUMP_PROVIDER_H_
+
+#include "base/trace_event/memory_dump_provider.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT FontCacheMemoryDumpProvider final
+ : public base::trace_event::MemoryDumpProvider {
+ USING_FAST_MALLOC(FontCacheMemoryDumpProvider);
+
+ public:
+ static FontCacheMemoryDumpProvider* Instance();
+ ~FontCacheMemoryDumpProvider() override = default;
+
+ // base::trace_event::MemoryDumpProvider implementation.
+ bool OnMemoryDump(const base::trace_event::MemoryDumpArgs&,
+ base::trace_event::ProcessMemoryDump*) override;
+
+ private:
+ FontCacheMemoryDumpProvider() = default;
+
+ WTF_MAKE_NONCOPYABLE(FontCacheMemoryDumpProvider);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_CACHE_MEMORY_DUMP_PROVIDER_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_cache_test.cc b/chromium/third_party/blink/renderer/platform/fonts/font_cache_test.cc
new file mode 100644
index 00000000000..a4b69a38a04
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_cache_test.cc
@@ -0,0 +1,73 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/font_cache.h"
+
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/fonts/font_description.h"
+#include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
+
+namespace blink {
+
+TEST(FontCache, getLastResortFallbackFont) {
+ FontCache* font_cache = FontCache::GetFontCache();
+ ASSERT_TRUE(font_cache);
+
+ FontDescription font_description;
+ font_description.SetGenericFamily(FontDescription::kStandardFamily);
+ scoped_refptr<SimpleFontData> font_data =
+ font_cache->GetLastResortFallbackFont(font_description, kRetain);
+ EXPECT_TRUE(font_data);
+
+ font_description.SetGenericFamily(FontDescription::kSansSerifFamily);
+ font_data = font_cache->GetLastResortFallbackFont(font_description, kRetain);
+ EXPECT_TRUE(font_data);
+}
+
+TEST(FontCache, NoFallbackForPrivateUseArea) {
+ FontCache* font_cache = FontCache::GetFontCache();
+ ASSERT_TRUE(font_cache);
+
+ FontDescription font_description;
+ font_description.SetGenericFamily(FontDescription::kStandardFamily);
+ for (UChar32 character : {0xE000, 0xE401, 0xE402, 0xE403, 0xF8FF, 0xF0000,
+ 0xFAAAA, 0xFFFFF, 0x100000, 0x10AAAA, 0x10FFFF}) {
+ scoped_refptr<SimpleFontData> font_data =
+ font_cache->FallbackFontForCharacter(font_description, character,
+ nullptr);
+ EXPECT_EQ(font_data.get(), nullptr);
+ }
+}
+
+TEST(FontCache, firstAvailableOrFirst) {
+ EXPECT_TRUE(FontCache::FirstAvailableOrFirst("").IsEmpty());
+ EXPECT_TRUE(FontCache::FirstAvailableOrFirst(String()).IsEmpty());
+
+ EXPECT_EQ("Arial", FontCache::FirstAvailableOrFirst("Arial"));
+ EXPECT_EQ("not exist", FontCache::FirstAvailableOrFirst("not exist"));
+
+ EXPECT_EQ("Arial", FontCache::FirstAvailableOrFirst("Arial, not exist"));
+ EXPECT_EQ("Arial", FontCache::FirstAvailableOrFirst("not exist, Arial"));
+ EXPECT_EQ("Arial",
+ FontCache::FirstAvailableOrFirst("not exist, Arial, not exist"));
+
+ EXPECT_EQ("not exist",
+ FontCache::FirstAvailableOrFirst("not exist, not exist 2"));
+
+ EXPECT_EQ("Arial", FontCache::FirstAvailableOrFirst(", not exist, Arial"));
+ EXPECT_EQ("not exist",
+ FontCache::FirstAvailableOrFirst(", not exist, not exist"));
+}
+
+#if !defined(OS_MACOSX)
+TEST(FontCache, systemFont) {
+ FontCache::SystemFontFamily();
+ // Test the function does not crash. Return value varies by system and config.
+}
+#endif
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_custom_platform_data.cc b/chromium/third_party/blink/renderer/platform/fonts/font_custom_platform_data.cc
new file mode 100644
index 00000000000..b02c8ac1075
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_custom_platform_data.cc
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2007 Apple Computer, Inc.
+ * Copyright (c) 2007, 2008, 2009, Google Inc. All rights reserved.
+ * Copyright (C) 2010 Company 100, Inc.
+ *
+ * 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 "third_party/blink/renderer/platform/fonts/font_custom_platform_data.h"
+
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/fonts/font_cache.h"
+#include "third_party/blink/renderer/platform/fonts/font_platform_data.h"
+#include "third_party/blink/renderer/platform/fonts/opentype/font_format_check.h"
+#include "third_party/blink/renderer/platform/fonts/opentype/font_settings.h"
+#include "third_party/blink/renderer/platform/fonts/web_font_decoder.h"
+#include "third_party/blink/renderer/platform/fonts/web_font_typeface_factory.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_typeface.h"
+#include "third_party/blink/renderer/platform/layout_test_support.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/skia/include/core/SkStream.h"
+#include "third_party/skia/include/core/SkTypeface.h"
+
+namespace blink {
+
+namespace {
+sk_sp<SkFontMgr> FontManagerForSubType(
+ FontFormatCheck::VariableFontSubType font_sub_type) {
+ CHECK_NE(font_sub_type, FontFormatCheck::VariableFontSubType::kNotVariable);
+ if (font_sub_type == FontFormatCheck::VariableFontSubType::kVariableCFF2)
+ return WebFontTypefaceFactory::FreeTypeFontManager();
+ return WebFontTypefaceFactory::FontManagerForVariations();
+}
+} // namespace
+
+FontCustomPlatformData::FontCustomPlatformData(sk_sp<SkTypeface> typeface,
+ size_t data_size)
+ : base_typeface_(std::move(typeface)), data_size_(data_size) {}
+
+FontCustomPlatformData::~FontCustomPlatformData() = default;
+
+FontPlatformData FontCustomPlatformData::GetFontPlatformData(
+ float size,
+ bool bold,
+ bool italic,
+ const FontSelectionRequest& selection_request,
+ const FontSelectionCapabilities& selection_capabilities,
+ FontOrientation orientation,
+ const FontVariationSettings* variation_settings) {
+ DCHECK(base_typeface_);
+
+ sk_sp<SkTypeface> return_typeface = base_typeface_;
+
+ // Maximum axis count is maximum value for the OpenType USHORT,
+ // which is a 16bit unsigned.
+ // https://www.microsoft.com/typography/otspec/fvar.htm Variation
+ // settings coming from CSS can have duplicate assignments and the
+ // list can be longer than UINT16_MAX, but ignoring the length for
+ // now, going with a reasonable upper limit. Deduplication is
+ // handled by Skia with priority given to the last occuring
+ // assignment.
+ FontFormatCheck::VariableFontSubType font_sub_type =
+ FontFormatCheck::ProbeVariableFont(base_typeface_);
+ if (font_sub_type ==
+ FontFormatCheck::VariableFontSubType::kVariableTrueType ||
+ font_sub_type == FontFormatCheck::VariableFontSubType::kVariableCFF2) {
+ Vector<SkFontArguments::Axis, 0> axes;
+
+ SkFontArguments::Axis weight_axis = {
+ SkSetFourByteTag('w', 'g', 'h', 't'),
+ SkFloatToScalar(selection_capabilities.weight.clampToRange(
+ selection_request.weight))};
+ SkFontArguments::Axis width_axis = {
+ SkSetFourByteTag('w', 'd', 't', 'h'),
+ SkFloatToScalar(selection_capabilities.width.clampToRange(
+ selection_request.width))};
+ SkFontArguments::Axis slant_axis = {
+ SkSetFourByteTag('s', 'l', 'n', 't'),
+ SkFloatToScalar(selection_capabilities.slope.clampToRange(
+ selection_request.slope))};
+
+ axes.push_back(weight_axis);
+ axes.push_back(width_axis);
+ axes.push_back(slant_axis);
+
+ if (variation_settings && variation_settings->size() < UINT16_MAX) {
+ axes.ReserveCapacity(variation_settings->size() + axes.size());
+ for (size_t i = 0; i < variation_settings->size(); ++i) {
+ SkFontArguments::Axis axis = {
+ AtomicStringToFourByteTag(variation_settings->at(i).Tag()),
+ SkFloatToScalar(variation_settings->at(i).Value())};
+ axes.push_back(axis);
+ }
+ }
+
+ sk_sp<SkTypeface> sk_variation_font(
+ FontManagerForSubType(font_sub_type)
+ ->makeFromStream(
+ base_typeface_->openStream(nullptr)->duplicate(),
+ SkFontArguments().setAxes(axes.data(), axes.size())));
+
+ if (sk_variation_font) {
+ return_typeface = sk_variation_font;
+ } else {
+ SkString family_name;
+ base_typeface_->getFamilyName(&family_name);
+ // TODO: Surface this as a console message?
+ LOG(ERROR) << "Unable for apply variation axis properties for font: "
+ << family_name.c_str();
+ }
+ }
+
+ // TODO(vmpstr): Handle web fonts PaintTypefaces.
+ PaintTypeface paint_tf = PaintTypeface::FromSkTypeface(return_typeface);
+ return FontPlatformData(std::move(paint_tf), CString(), size,
+ bold && !base_typeface_->isBold(),
+ italic && !base_typeface_->isItalic(), orientation);
+}
+
+SkString FontCustomPlatformData::FamilyNameForInspector() const {
+ SkTypeface::LocalizedStrings* font_family_iterator =
+ base_typeface_->createFamilyNameIterator();
+ SkTypeface::LocalizedString localized_string;
+ while (font_family_iterator->next(&localized_string)) {
+ // BCP 47 tags for English take precedent in font matching over other
+ // localizations: https://drafts.csswg.org/css-fonts/#descdef-src.
+ if (localized_string.fLanguage.equals("en") ||
+ localized_string.fLanguage.equals("en-US")) {
+ break;
+ }
+ }
+ font_family_iterator->unref();
+ return localized_string.fString;
+}
+
+scoped_refptr<FontCustomPlatformData> FontCustomPlatformData::Create(
+ SharedBuffer* buffer,
+ String& ots_parse_message) {
+ DCHECK(buffer);
+ WebFontDecoder decoder;
+ sk_sp<SkTypeface> typeface = decoder.Decode(buffer);
+ if (!typeface) {
+ ots_parse_message = decoder.GetErrorString();
+ return nullptr;
+ }
+ return base::AdoptRef(
+ new FontCustomPlatformData(std::move(typeface), decoder.DecodedSize()));
+}
+
+bool FontCustomPlatformData::SupportsFormat(const String& format) {
+ // Support relevant format specifiers from
+ // https://drafts.csswg.org/css-fonts-4/#src-desc
+ return EqualIgnoringASCIICase(format, "woff") ||
+ EqualIgnoringASCIICase(format, "truetype") ||
+ EqualIgnoringASCIICase(format, "opentype") ||
+ EqualIgnoringASCIICase(format, "woff2") ||
+ EqualIgnoringASCIICase(format, "woff-variations") ||
+ EqualIgnoringASCIICase(format, "truetype-variations") ||
+ EqualIgnoringASCIICase(format, "opentype-variations") ||
+ EqualIgnoringASCIICase(format, "woff2-variations");
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_custom_platform_data.h b/chromium/third_party/blink/renderer/platform/fonts/font_custom_platform_data.h
new file mode 100644
index 00000000000..491b1b0b666
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_custom_platform_data.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2007 Apple Computer, Inc.
+ * Copyright (c) 2007, 2008, 2009, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_CUSTOM_PLATFORM_DATA_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_CUSTOM_PLATFORM_DATA_H_
+
+#include "third_party/blink/renderer/platform/fonts/font_orientation.h"
+#include "third_party/blink/renderer/platform/fonts/font_selection_types.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+class SkTypeface;
+
+namespace blink {
+
+class FontPlatformData;
+class SharedBuffer;
+class FontVariationSettings;
+
+class PLATFORM_EXPORT FontCustomPlatformData
+ : public RefCounted<FontCustomPlatformData> {
+ USING_FAST_MALLOC(FontCustomPlatformData);
+ WTF_MAKE_NONCOPYABLE(FontCustomPlatformData);
+
+ public:
+ static scoped_refptr<FontCustomPlatformData> Create(SharedBuffer*,
+ String& ots_parse_message);
+ ~FontCustomPlatformData();
+
+ FontPlatformData GetFontPlatformData(
+ float size,
+ bool bold,
+ bool italic,
+ const FontSelectionRequest&,
+ const FontSelectionCapabilities&,
+ FontOrientation = FontOrientation::kHorizontal,
+ const FontVariationSettings* = nullptr);
+
+ SkString FamilyNameForInspector() const;
+
+ size_t DataSize() const { return data_size_; }
+ static bool SupportsFormat(const String&);
+
+ private:
+ FontCustomPlatformData(sk_sp<SkTypeface>, size_t data_size);
+ sk_sp<SkTypeface> base_typeface_;
+ size_t data_size_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_CUSTOM_PLATFORM_DATA_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_data.cc b/chromium/third_party/blink/renderer/platform/fonts/font_data.cc
new file mode 100644
index 00000000000..a53671147eb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_data.cc
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2008 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/fonts/font_data.h"
+
+namespace blink {
+
+FontData::~FontData() = default;
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_data.h b/chromium/third_party/blink/renderer/platform/fonts/font_data.h
new file mode 100644
index 00000000000..2b770f6a2ef
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_data.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2008 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_DATA_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_DATA_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
+
+namespace blink {
+
+class SimpleFontData;
+
+class PLATFORM_EXPORT FontData : public RefCounted<FontData> {
+ WTF_MAKE_NONCOPYABLE(FontData);
+
+ public:
+ FontData() = default;
+
+ virtual ~FontData();
+
+ virtual const SimpleFontData* FontDataForCharacter(UChar32) const = 0;
+ virtual bool IsCustomFont() const = 0;
+ virtual bool IsLoading() const = 0;
+ // Returns whether this is a temporary font data for a custom font which is
+ // not yet loaded.
+ virtual bool IsLoadingFallback() const = 0;
+ virtual bool IsSegmented() const = 0;
+ virtual bool ShouldSkipDrawing() const = 0;
+};
+
+#define DEFINE_FONT_DATA_TYPE_CASTS(thisType, predicate) \
+ template <typename T> \
+ inline thisType* To##thisType(const scoped_refptr<T>& fontData) { \
+ return To##thisType(fontData.get()); \
+ } \
+ DEFINE_TYPE_CASTS(thisType, FontData, fontData, \
+ fontData->IsSegmented() == predicate, \
+ fontData.IsSegmented() == predicate)
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_DATA_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_data_cache.cc b/chromium/third_party/blink/renderer/platform/fonts/font_data_cache.cc
new file mode 100644
index 00000000000..060207ea5e5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_data_cache.cc
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/renderer/platform/fonts/font_data_cache.h"
+
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
+
+namespace blink {
+
+#if !defined(OS_ANDROID)
+const unsigned kCMaxInactiveFontData = 250;
+const unsigned kCTargetInactiveFontData = 200;
+#else
+const unsigned kCMaxInactiveFontData = 225;
+const unsigned kCTargetInactiveFontData = 200;
+#endif
+
+scoped_refptr<SimpleFontData> FontDataCache::Get(const FontPlatformData* platform_data,
+ ShouldRetain should_retain,
+ bool subpixel_ascent_descent) {
+ if (!platform_data)
+ return nullptr;
+
+ // TODO: crbug.com/446376 - This should not happen, but we currently
+ // do not have a reproduction for the crash that an empty typeface()
+ // causes downstream from here.
+ if (!platform_data->Typeface()) {
+ DLOG(ERROR)
+ << "Empty typeface() in FontPlatformData when accessing FontDataCache.";
+ return nullptr;
+ }
+
+ Cache::iterator result = cache_.find(platform_data);
+ if (result == cache_.end()) {
+ std::pair<scoped_refptr<SimpleFontData>, unsigned> new_value(
+ SimpleFontData::Create(*platform_data, nullptr,
+ subpixel_ascent_descent),
+ should_retain == kRetain ? 1 : 0);
+ // The new SimpleFontData takes a copy of the incoming FontPlatformData
+ // object. The incoming key may be temporary. So, for cache storage, take
+ // the address of the newly created FontPlatformData that is copied an owned
+ // by SimpleFontData.
+ cache_.Set(&new_value.first->PlatformData(), new_value);
+ if (should_retain == kDoNotRetain)
+ inactive_font_data_.insert(new_value.first);
+ return std::move(new_value.first);
+ }
+
+ if (!result.Get()->value.second) {
+ DCHECK(inactive_font_data_.Contains(result.Get()->value.first));
+ inactive_font_data_.erase(result.Get()->value.first);
+ }
+
+ if (should_retain == kRetain) {
+ result.Get()->value.second++;
+ } else if (!result.Get()->value.second) {
+ // If shouldRetain is DoNotRetain and count is 0, we want to remove the
+ // fontData from m_inactiveFontData (above) and re-add here to update LRU
+ // position.
+ inactive_font_data_.insert(result.Get()->value.first);
+ }
+
+ return result.Get()->value.first;
+}
+
+bool FontDataCache::Contains(const FontPlatformData* font_platform_data) const {
+ return cache_.Contains(font_platform_data);
+}
+
+void FontDataCache::Release(const SimpleFontData* font_data) {
+ DCHECK(!font_data->IsCustomFont());
+
+ Cache::iterator it = cache_.find(&(font_data->PlatformData()));
+ DCHECK_NE(it, cache_.end());
+ if (it == cache_.end())
+ return;
+
+ DCHECK(it->value.second);
+ if (!--it->value.second)
+ inactive_font_data_.insert(it->value.first);
+}
+
+bool FontDataCache::Purge(PurgeSeverity purge_severity) {
+ if (purge_severity == kForcePurge)
+ return PurgeLeastRecentlyUsed(INT_MAX);
+
+ if (inactive_font_data_.size() > kCMaxInactiveFontData)
+ return PurgeLeastRecentlyUsed(inactive_font_data_.size() -
+ kCTargetInactiveFontData);
+
+ return false;
+}
+
+bool FontDataCache::PurgeLeastRecentlyUsed(int count) {
+ // Guard against reentry when e.g. a deleted FontData releases its small caps
+ // FontData.
+ static bool is_purging;
+ if (is_purging)
+ return false;
+
+ is_purging = true;
+
+ Vector<scoped_refptr<SimpleFontData>, 20> font_data_to_delete;
+ auto end = inactive_font_data_.end();
+ auto it = inactive_font_data_.begin();
+ for (int i = 0; i < count && it != end; ++it, ++i) {
+ scoped_refptr<SimpleFontData>& font_data = *it.Get();
+ cache_.erase(&(font_data->PlatformData()));
+ // We should not delete SimpleFontData here because deletion can modify
+ // m_inactiveFontData. See http://trac.webkit.org/changeset/44011
+ font_data_to_delete.push_back(font_data);
+ }
+
+ if (it == end) {
+ // Removed everything
+ inactive_font_data_.clear();
+ } else {
+ for (int i = 0; i < count; ++i)
+ inactive_font_data_.erase(inactive_font_data_.begin());
+ }
+
+ bool did_work = font_data_to_delete.size();
+
+ font_data_to_delete.clear();
+
+ is_purging = false;
+
+ return did_work;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_data_cache.h b/chromium/third_party/blink/renderer/platform/fonts/font_data_cache.h
new file mode 100644
index 00000000000..685a26926d6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_data_cache.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_DATA_CACHE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_DATA_CACHE_H_
+
+#include "third_party/blink/renderer/platform/fonts/font_platform_data.h"
+#include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/linked_hash_set.h"
+
+namespace blink {
+
+enum ShouldRetain { kRetain, kDoNotRetain };
+enum PurgeSeverity { kPurgeIfNeeded, kForcePurge };
+
+struct FontDataCacheKeyHash {
+ STATIC_ONLY(FontDataCacheKeyHash);
+ static unsigned GetHash(const FontPlatformData* platform_data) {
+ return platform_data->GetHash();
+ }
+
+ static bool Equal(const FontPlatformData* a, const FontPlatformData* b) {
+ const FontPlatformData* empty_value =
+ reinterpret_cast<FontPlatformData*>(-1);
+
+ if (a == empty_value)
+ return b == empty_value;
+ if (b == empty_value)
+ return a == empty_value;
+
+ if (!a || !b)
+ return a == b;
+
+ CHECK(a && b);
+
+ return *a == *b;
+ }
+
+ static const bool safe_to_compare_to_empty_or_deleted = true;
+};
+
+class FontDataCache {
+ USING_FAST_MALLOC(FontDataCache);
+ WTF_MAKE_NONCOPYABLE(FontDataCache);
+
+ public:
+ FontDataCache() = default;
+
+ scoped_refptr<SimpleFontData> Get(const FontPlatformData*,
+ ShouldRetain = kRetain,
+ bool subpixel_ascent_descent = false);
+ bool Contains(const FontPlatformData*) const;
+ void Release(const SimpleFontData*);
+
+ // Purges items in FontDataCache according to provided severity.
+ // Returns true if any removal of cache items actually occurred.
+ bool Purge(PurgeSeverity);
+
+ private:
+ bool PurgeLeastRecentlyUsed(int count);
+
+ typedef HashMap<const FontPlatformData*,
+ std::pair<scoped_refptr<SimpleFontData>, unsigned>,
+ FontDataCacheKeyHash>
+ Cache;
+ Cache cache_;
+ LinkedHashSet<scoped_refptr<SimpleFontData>> inactive_font_data_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_data_for_range_set.cc b/chromium/third_party/blink/renderer/platform/fonts/font_data_for_range_set.cc
new file mode 100644
index 00000000000..8ee03326121
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_data_for_range_set.cc
@@ -0,0 +1,22 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/font_data_for_range_set.h"
+
+#include "third_party/blink/renderer/platform/fonts/font_cache.h"
+
+namespace blink {
+
+FontDataForRangeSet::FontDataForRangeSet(const FontDataForRangeSet& other) {
+ font_data_ = other.font_data_;
+ range_set_ = other.range_set_;
+}
+
+FontDataForRangeSetFromCache::~FontDataForRangeSetFromCache() {
+ if (font_data_ && !font_data_->IsCustomFont()) {
+ FontCache::GetFontCache()->ReleaseFontData(font_data_.get());
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_data_for_range_set.h b/chromium/third_party/blink/renderer/platform/fonts/font_data_for_range_set.h
new file mode 100644
index 00000000000..461e3bef3e2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_data_for_range_set.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2008, 2009 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_DATA_FOR_RANGE_SET_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_DATA_FOR_RANGE_SET_H_
+
+#include "third_party/blink/renderer/platform/fonts/font_data.h"
+#include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
+#include "third_party/blink/renderer/platform/fonts/unicode_range_set.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
+
+namespace blink {
+
+class SimpleFontData;
+
+class PLATFORM_EXPORT FontDataForRangeSet
+ : public RefCounted<FontDataForRangeSet> {
+ public:
+ explicit FontDataForRangeSet(scoped_refptr<SimpleFontData> font_data = nullptr,
+ scoped_refptr<UnicodeRangeSet> range_set = nullptr)
+ : font_data_(std::move(font_data)), range_set_(std::move(range_set)) {}
+
+ FontDataForRangeSet(const FontDataForRangeSet& other);
+
+ virtual ~FontDataForRangeSet() = default;
+ ;
+
+ bool Contains(UChar32 test_char) const {
+ return !range_set_ || range_set_->Contains(test_char);
+ }
+ bool IsEntireRange() const {
+ return !range_set_ || range_set_->IsEntireRange();
+ }
+ UnicodeRangeSet* Ranges() const { return range_set_.get(); }
+ bool HasFontData() const { return font_data_.get(); }
+ const SimpleFontData* FontData() const { return font_data_.get(); }
+
+ protected:
+ scoped_refptr<SimpleFontData> font_data_;
+ scoped_refptr<UnicodeRangeSet> range_set_;
+};
+
+class PLATFORM_EXPORT FontDataForRangeSetFromCache
+ : public FontDataForRangeSet {
+ public:
+ explicit FontDataForRangeSetFromCache(
+ scoped_refptr<SimpleFontData> font_data,
+ scoped_refptr<UnicodeRangeSet> range_set = nullptr)
+ : FontDataForRangeSet(std::move(font_data), std::move(range_set)) {}
+ virtual ~FontDataForRangeSetFromCache();
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_description.cc b/chromium/third_party/blink/renderer/platform/fonts/font_description.cc
new file mode 100644
index 00000000000..156431f82cd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_description.cc
@@ -0,0 +1,507 @@
+/*
+ * Copyright (C) 2007 Nicholas Shanks <contact@nickshanks.com>
+ * Copyright (C) 2008 Apple 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/fonts/font_description.h"
+
+#include "third_party/blink/public/platform/web_font_description.h"
+#include "third_party/blink/renderer/platform/language.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/hash_functions.h"
+#include "third_party/blink/renderer/platform/wtf/string_hasher.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+
+namespace blink {
+
+struct SameSizeAsFontDescription {
+ DISALLOW_NEW();
+ FontFamily family_list;
+ scoped_refptr<FontFeatureSettings> feature_settings_;
+ scoped_refptr<FontVariationSettings> variation_settings_;
+ AtomicString locale;
+ float sizes[6];
+ FontSelectionRequest selection_request_;
+ FieldsAsUnsignedType bitfields;
+};
+
+static_assert(sizeof(FontDescription) == sizeof(SameSizeAsFontDescription),
+ "FontDescription should stay small");
+
+TypesettingFeatures FontDescription::default_typesetting_features_ = 0;
+
+bool FontDescription::use_subpixel_text_positioning_ = false;
+
+FontDescription::FontDescription()
+ : specified_size_(0),
+ computed_size_(0),
+ adjusted_size_(0),
+ size_adjust_(kFontSizeAdjustNone),
+ letter_spacing_(0),
+ word_spacing_(0),
+ font_selection_request_(NormalWeightValue(),
+ NormalWidthValue(),
+ NormalSlopeValue()) {
+ fields_as_unsigned_.parts[0] = 0;
+ fields_as_unsigned_.parts[1] = 0;
+ fields_.orientation_ = static_cast<unsigned>(FontOrientation::kHorizontal);
+ fields_.width_variant_ = kRegularWidth;
+ fields_.variant_caps_ = kCapsNormal;
+ fields_.is_absolute_size_ = false;
+ fields_.generic_family_ = kNoFamily;
+ fields_.kerning_ = kAutoKerning;
+ fields_.common_ligatures_state_ = kNormalLigaturesState;
+ fields_.discretionary_ligatures_state_ = kNormalLigaturesState;
+ fields_.historical_ligatures_state_ = kNormalLigaturesState;
+ fields_.contextual_ligatures_state_ = kNormalLigaturesState;
+ fields_.keyword_size_ = 0;
+ fields_.font_smoothing_ = kAutoSmoothing;
+ fields_.text_rendering_ = kAutoTextRendering;
+ fields_.synthetic_bold_ = false;
+ fields_.synthetic_italic_ = false;
+ fields_.subpixel_text_position_ = use_subpixel_text_positioning_;
+ fields_.typesetting_features_ = default_typesetting_features_;
+ fields_.variant_numeric_ = FontVariantNumeric().fields_as_unsigned_;
+ fields_.subpixel_ascent_descent_ = false;
+}
+
+FontDescription::FontDescription(const FontDescription&) = default;
+
+FontDescription& FontDescription::operator=(const FontDescription&) = default;
+
+bool FontDescription::operator==(const FontDescription& other) const {
+ return family_list_ == other.family_list_ && locale_ == other.locale_ &&
+ specified_size_ == other.specified_size_ &&
+ computed_size_ == other.computed_size_ &&
+ adjusted_size_ == other.adjusted_size_ &&
+ size_adjust_ == other.size_adjust_ &&
+ letter_spacing_ == other.letter_spacing_ &&
+ word_spacing_ == other.word_spacing_ &&
+ font_selection_request_ == other.font_selection_request_ &&
+ fields_as_unsigned_.parts[0] == other.fields_as_unsigned_.parts[0] &&
+ fields_as_unsigned_.parts[1] == other.fields_as_unsigned_.parts[1] &&
+ (feature_settings_ == other.feature_settings_ ||
+ (feature_settings_ && other.feature_settings_ &&
+ *feature_settings_ == *other.feature_settings_)) &&
+ (variation_settings_ == other.variation_settings_ ||
+ (variation_settings_ && other.variation_settings_ &&
+ *variation_settings_ == *other.variation_settings_));
+}
+
+// Compute a 'lighter' weight per
+// https://drafts.csswg.org/css-fonts-4/#font-weight-prop
+FontSelectionValue FontDescription::LighterWeight(FontSelectionValue weight) {
+ DCHECK(weight >= FontSelectionValue(1) && weight <= FontSelectionValue(1000));
+ // [1, 100) => No change
+ if (weight < FontSelectionValue(100))
+ return weight;
+ // [100, 550) => 100
+ if (weight < FontSelectionValue(550))
+ return FontSelectionValue(100);
+ // [550, 750) => 400
+ if (weight < FontSelectionValue(750))
+ return FontSelectionValue(400);
+ // [750, 1000] => 700
+ return FontSelectionValue(700);
+}
+
+// Compute a 'bolder' weight per
+// https://drafts.csswg.org/css-fonts-4/#font-weight-prop
+FontSelectionValue FontDescription::BolderWeight(FontSelectionValue weight) {
+ DCHECK(weight >= FontSelectionValue(1) && weight <= FontSelectionValue(1000));
+ // [1, 350) => 400
+ if (weight < FontSelectionValue(350))
+ return FontSelectionValue(400);
+ // [350, 550) => 700
+ if (weight < FontSelectionValue(550))
+ return FontSelectionValue(700);
+ // [550, 900) => 900
+ if (weight < FontSelectionValue(900))
+ return FontSelectionValue(900);
+ // [900, 1000] => No change
+ return weight;
+}
+
+FontDescription::Size FontDescription::LargerSize(const Size& size) {
+ return Size(0, size.value * 1.2, size.is_absolute);
+}
+
+FontDescription::Size FontDescription::SmallerSize(const Size& size) {
+ return Size(0, size.value / 1.2, size.is_absolute);
+}
+
+FontSelectionRequest FontDescription::GetFontSelectionRequest() const {
+ return font_selection_request_;
+}
+
+FontDescription::VariantLigatures FontDescription::GetVariantLigatures() const {
+ VariantLigatures ligatures;
+
+ ligatures.common = CommonLigaturesState();
+ ligatures.discretionary = DiscretionaryLigaturesState();
+ ligatures.historical = HistoricalLigaturesState();
+ ligatures.contextual = ContextualLigaturesState();
+
+ return ligatures;
+}
+
+void FontDescription::SetVariantCaps(FontVariantCaps variant_caps) {
+ fields_.variant_caps_ = variant_caps;
+
+ UpdateTypesettingFeatures();
+}
+
+void FontDescription::SetVariantEastAsian(
+ const FontVariantEastAsian variant_east_asian) {
+ fields_.variant_east_asian_ = variant_east_asian.fields_as_unsigned_;
+}
+
+void FontDescription::SetVariantLigatures(const VariantLigatures& ligatures) {
+ fields_.common_ligatures_state_ = ligatures.common;
+ fields_.discretionary_ligatures_state_ = ligatures.discretionary;
+ fields_.historical_ligatures_state_ = ligatures.historical;
+ fields_.contextual_ligatures_state_ = ligatures.contextual;
+
+ UpdateTypesettingFeatures();
+}
+
+void FontDescription::SetVariantNumeric(
+ const FontVariantNumeric& variant_numeric) {
+ fields_.variant_numeric_ = variant_numeric.fields_as_unsigned_;
+
+ UpdateTypesettingFeatures();
+}
+
+float FontDescription::EffectiveFontSize() const {
+ // Ensure that the effective precision matches the font-cache precision.
+ // This guarantees that the same precision is used regardless of cache status.
+ float computed_or_adjusted_size =
+ HasSizeAdjust() ? AdjustedSize() : ComputedSize();
+ return floorf(computed_or_adjusted_size *
+ FontCacheKey::PrecisionMultiplier()) /
+ FontCacheKey::PrecisionMultiplier();
+}
+
+FontCacheKey FontDescription::CacheKey(
+ const FontFaceCreationParams& creation_params,
+ const FontSelectionRequest& font_selection_request) const {
+ unsigned options =
+ static_cast<unsigned>(fields_.synthetic_italic_) << 6 | // bit 7
+ static_cast<unsigned>(fields_.synthetic_bold_) << 5 | // bit 6
+ static_cast<unsigned>(fields_.text_rendering_) << 3 | // bits 4-5
+ static_cast<unsigned>(fields_.orientation_) << 1 | // bit 2-3
+ static_cast<unsigned>(fields_.subpixel_text_position_); // bit 1
+
+ FontCacheKey cache_key(creation_params, EffectiveFontSize(),
+ options | font_selection_request_.GetHash() << 8,
+ variation_settings_);
+ return cache_key;
+}
+
+void FontDescription::SetDefaultTypesettingFeatures(
+ TypesettingFeatures typesetting_features) {
+ default_typesetting_features_ = typesetting_features;
+}
+
+TypesettingFeatures FontDescription::DefaultTypesettingFeatures() {
+ return default_typesetting_features_;
+}
+
+void FontDescription::UpdateTypesettingFeatures() {
+ fields_.typesetting_features_ = default_typesetting_features_;
+
+ switch (TextRendering()) {
+ case kAutoTextRendering:
+ break;
+ case kOptimizeSpeed:
+ fields_.typesetting_features_ &= ~(blink::kKerning | kLigatures);
+ break;
+ case kGeometricPrecision:
+ case kOptimizeLegibility:
+ fields_.typesetting_features_ |= blink::kKerning | kLigatures;
+ break;
+ }
+
+ switch (GetKerning()) {
+ case FontDescription::kNoneKerning:
+ fields_.typesetting_features_ &= ~blink::kKerning;
+ break;
+ case FontDescription::kNormalKerning:
+ fields_.typesetting_features_ |= blink::kKerning;
+ break;
+ case FontDescription::kAutoKerning:
+ break;
+ }
+
+ // As per CSS (https://drafts.csswg.org/css-text/#letter-spacing-property),
+ // When the effective letter-spacing between two characters is not zero (due
+ // to either justification or non-zero computed letter-spacing), user agents
+ // should not apply optional ligatures.
+ if (letter_spacing_ == 0) {
+ switch (CommonLigaturesState()) {
+ case FontDescription::kDisabledLigaturesState:
+ fields_.typesetting_features_ &= ~blink::kLigatures;
+ break;
+ case FontDescription::kEnabledLigaturesState:
+ fields_.typesetting_features_ |= blink::kLigatures;
+ break;
+ case FontDescription::kNormalLigaturesState:
+ break;
+ }
+
+ if (DiscretionaryLigaturesState() ==
+ FontDescription::kEnabledLigaturesState ||
+ HistoricalLigaturesState() == FontDescription::kEnabledLigaturesState ||
+ ContextualLigaturesState() == FontDescription::kEnabledLigaturesState) {
+ fields_.typesetting_features_ |= blink::kLigatures;
+ }
+ }
+
+ if (VariantCaps() != kCapsNormal)
+ fields_.typesetting_features_ |= blink::kCaps;
+}
+
+unsigned FontDescription::StyleHashWithoutFamilyList() const {
+ unsigned hash = 0;
+ StringHasher string_hasher;
+ const FontFeatureSettings* settings = FeatureSettings();
+ if (settings) {
+ unsigned num_features = settings->size();
+ for (unsigned i = 0; i < num_features; ++i) {
+ const AtomicString& tag = settings->at(i).Tag();
+ for (unsigned j = 0; j < tag.length(); j++)
+ string_hasher.AddCharacter(tag[j]);
+ WTF::AddIntToHash(hash, settings->at(i).Value());
+ }
+ }
+
+ if (VariationSettings())
+ WTF::AddIntToHash(hash, VariationSettings()->GetHash());
+
+ if (locale_) {
+ const AtomicString& locale = locale_->LocaleString();
+ for (unsigned i = 0; i < locale.length(); i++)
+ string_hasher.AddCharacter(locale[i]);
+ }
+ WTF::AddIntToHash(hash, string_hasher.GetHash());
+
+ WTF::AddFloatToHash(hash, specified_size_);
+ WTF::AddFloatToHash(hash, computed_size_);
+ WTF::AddFloatToHash(hash, adjusted_size_);
+ WTF::AddFloatToHash(hash, size_adjust_);
+ WTF::AddFloatToHash(hash, letter_spacing_);
+ WTF::AddFloatToHash(hash, word_spacing_);
+ WTF::AddIntToHash(hash, fields_as_unsigned_.parts[0]);
+ WTF::AddIntToHash(hash, fields_as_unsigned_.parts[1]);
+ WTF::AddIntToHash(hash, font_selection_request_.GetHash());
+
+ return hash;
+}
+
+SkFontStyle FontDescription::SkiaFontStyle() const {
+ // FIXME(drott): This is a lossy conversion, compare
+ // https://bugs.chromium.org/p/skia/issues/detail?id=6844
+ int skia_width = SkFontStyle::kNormal_Width;
+ if (Stretch() <= UltraCondensedWidthValue())
+ skia_width = SkFontStyle::kUltraCondensed_Width;
+ if (Stretch() <= ExtraCondensedWidthValue())
+ skia_width = SkFontStyle::kExtraCondensed_Width;
+ if (Stretch() <= CondensedWidthValue())
+ skia_width = SkFontStyle::kCondensed_Width;
+ if (Stretch() <= SemiCondensedWidthValue())
+ skia_width = SkFontStyle::kSemiCondensed_Width;
+ if (Stretch() >= SemiExpandedWidthValue())
+ skia_width = SkFontStyle::kSemiExpanded_Width;
+ if (Stretch() >= ExpandedWidthValue())
+ skia_width = SkFontStyle::kExpanded_Width;
+ if (Stretch() >= ExtraExpandedWidthValue())
+ skia_width = SkFontStyle::kExtraExpanded_Width;
+ if (Stretch() >= UltraExpandedWidthValue())
+ skia_width = SkFontStyle::kUltraExpanded_Width;
+
+ SkFontStyle::Slant slant = SkFontStyle::kUpright_Slant;
+ FontSelectionValue style = Style();
+ if (style > NormalSlopeValue() && style <= ItalicThreshold())
+ slant = SkFontStyle::kItalic_Slant;
+ if (style > ItalicThreshold()) {
+ slant = SkFontStyle::kOblique_Slant;
+ }
+
+ int skia_weight = SkFontStyle::kNormal_Weight;
+ if (Weight() >= 100 && Weight() <= 1000)
+ skia_weight = static_cast<int>(roundf(Weight() / 100) * 100);
+
+ return SkFontStyle(skia_weight, skia_width, slant);
+}
+
+String FontDescription::ToString(GenericFamilyType familyType) {
+ switch (familyType) {
+ case GenericFamilyType::kNoFamily:
+ return "None";
+ case GenericFamilyType::kStandardFamily:
+ return "Standard";
+ case GenericFamilyType::kSerifFamily:
+ return "Serif";
+ case GenericFamilyType::kSansSerifFamily:
+ return "SansSerif";
+ case GenericFamilyType::kMonospaceFamily:
+ return "Monospace";
+ case GenericFamilyType::kCursiveFamily:
+ return "Cursive";
+ case GenericFamilyType::kFantasyFamily:
+ return "Fantasy";
+ case GenericFamilyType::kPictographFamily:
+ return "Pictograph";
+ }
+ return "Unknown";
+}
+
+String FontDescription::ToString(Kerning kerning) {
+ switch (kerning) {
+ case Kerning::kAutoKerning:
+ return "Auto";
+ case Kerning::kNormalKerning:
+ return "Normal";
+ case Kerning::kNoneKerning:
+ return "None";
+ }
+ return "Unknown";
+}
+
+String FontDescription::ToString(LigaturesState state) {
+ switch (state) {
+ case LigaturesState::kNormalLigaturesState:
+ return "Normal";
+ case LigaturesState::kDisabledLigaturesState:
+ return "Disabled";
+ case LigaturesState::kEnabledLigaturesState:
+ return "Enabled";
+ }
+ return "Unknown";
+}
+
+String FontDescription::ToString(FontVariantCaps variant) {
+ switch (variant) {
+ case FontVariantCaps::kCapsNormal:
+ return "Normal";
+ case FontVariantCaps::kSmallCaps:
+ return "SmallCaps";
+ case FontVariantCaps::kAllSmallCaps:
+ return "AllSmallCaps";
+ case FontVariantCaps::kPetiteCaps:
+ return "PetiteCaps";
+ case FontVariantCaps::kAllPetiteCaps:
+ return "AllPetiteCaps";
+ case FontVariantCaps::kUnicase:
+ return "Unicase";
+ case FontVariantCaps::kTitlingCaps:
+ return "TitlingCaps";
+ }
+ return "Unknown";
+}
+
+String FontDescription::VariantLigatures::ToString() const {
+ return String::Format(
+ "common=%s, discretionary=%s, historical=%s, contextual=%s",
+ FontDescription::ToString(static_cast<LigaturesState>(common))
+ .Ascii()
+ .data(),
+ FontDescription::ToString(static_cast<LigaturesState>(discretionary))
+ .Ascii()
+ .data(),
+ FontDescription::ToString(static_cast<LigaturesState>(historical))
+ .Ascii()
+ .data(),
+ FontDescription::ToString(static_cast<LigaturesState>(contextual))
+ .Ascii()
+ .data());
+}
+
+String FontDescription::Size::ToString() const {
+ return String::Format(
+ "keyword_size=%u, specified_size=%f, is_absolute_size=%s", keyword, value,
+ is_absolute ? "true" : "false");
+}
+
+String FontDescription::FamilyDescription::ToString() const {
+ return String::Format(
+ "generic_family=%s, family=[%s]",
+ FontDescription::ToString(generic_family).Ascii().data(),
+ family.ToString().Ascii().data());
+}
+
+static const char* ToBooleanString(bool value) {
+ return value ? "true" : "false";
+}
+
+String FontDescription::ToString() const {
+ return String::Format(
+ "family_list=[%s], feature_settings=[%s], variation_settings=[%s], "
+ "locale=%s, "
+ "specified_size=%f, computed_size=%f, adjusted_size=%f, "
+ "size_adjust=%f, letter_spacing=%f, word_spacing=%f, "
+ "font_selection_request=[%s], "
+ "typesetting_features=[%s], "
+ "orientation=%s, width_variant=%s, variant_caps=%s, "
+ "is_absolute_size=%s, generic_family=%s, kerning=%s, "
+ "variant_ligatures=[%s], "
+ "keyword_size=%u, font_smoothing=%s, text_rendering=%s, "
+ "synthetic_bold=%s, synthetic_italic=%s, subpixel_positioning=%s, "
+ "subpixel_ascent_descent=%s, variant_numeric=[%s], "
+ "variant_east_asian=[%s]",
+ family_list_.ToString().Ascii().data(),
+ (feature_settings_ ? feature_settings_->ToString().Ascii().data() : ""),
+ (variation_settings_ ? variation_settings_->ToString().Ascii().data()
+ : ""),
+ // TODO(wkorman): Locale has additional internal fields such as
+ // hyphenation and script. Consider adding a more detailed
+ // string method.
+ (locale_ ? locale_->LocaleString().Ascii().data() : ""), specified_size_,
+ computed_size_, adjusted_size_, size_adjust_, letter_spacing_,
+ word_spacing_, font_selection_request_.ToString().Ascii().data(),
+ blink::ToString(
+ static_cast<TypesettingFeatures>(fields_.typesetting_features_))
+ .Ascii()
+ .data(),
+ blink::ToString(Orientation()).Ascii().data(),
+ blink::ToString(WidthVariant()).Ascii().data(),
+ FontDescription::ToString(VariantCaps()).Ascii().data(),
+ ToBooleanString(IsAbsoluteSize()),
+ FontDescription::ToString(GenericFamily()).Ascii().data(),
+ FontDescription::ToString(Kerning()).Ascii().data(),
+ GetVariantLigatures().ToString().Ascii().data(), KeywordSize(),
+ blink::ToString(FontSmoothing()).Ascii().data(),
+ blink::ToString(TextRendering()).Ascii().data(),
+ ToBooleanString(IsSyntheticBold()), ToBooleanString(IsSyntheticItalic()),
+ ToBooleanString(UseSubpixelPositioning()),
+ ToBooleanString(SubpixelAscentDescent()),
+ VariantNumeric().ToString().Ascii().data(),
+ VariantEastAsian().ToString().Ascii().data());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_description.h b/chromium/third_party/blink/renderer/platform/fonts/font_description.h
new file mode 100644
index 00000000000..0af519e4154
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_description.h
@@ -0,0 +1,423 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights
+ * reserved.
+ * Copyright (C) 2007 Nicholas Shanks <webkit@nickshanks.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_DESCRIPTION_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_DESCRIPTION_H_
+
+#include "SkFontStyle.h"
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/font_family_names.h"
+#include "third_party/blink/renderer/platform/fonts/font_cache_key.h"
+#include "third_party/blink/renderer/platform/fonts/font_family.h"
+#include "third_party/blink/renderer/platform/fonts/font_orientation.h"
+#include "third_party/blink/renderer/platform/fonts/font_selection_types.h"
+#include "third_party/blink/renderer/platform/fonts/font_smoothing_mode.h"
+#include "third_party/blink/renderer/platform/fonts/font_variant_east_asian.h"
+#include "third_party/blink/renderer/platform/fonts/font_variant_numeric.h"
+#include "third_party/blink/renderer/platform/fonts/font_width_variant.h"
+#include "third_party/blink/renderer/platform/fonts/opentype/font_settings.h"
+#include "third_party/blink/renderer/platform/fonts/text_rendering_mode.h"
+#include "third_party/blink/renderer/platform/fonts/typesetting_features.h"
+#include "third_party/blink/renderer/platform/layout_locale.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+#include <unicode/uscript.h>
+
+namespace blink {
+
+const float kFontSizeAdjustNone = -1;
+typedef struct { uint32_t parts[2]; } FieldsAsUnsignedType;
+
+class PLATFORM_EXPORT FontDescription {
+ USING_FAST_MALLOC(FontDescription);
+
+ public:
+ enum GenericFamilyType {
+ kNoFamily,
+ kStandardFamily,
+ kSerifFamily,
+ kSansSerifFamily,
+ kMonospaceFamily,
+ kCursiveFamily,
+ kFantasyFamily,
+ kPictographFamily
+ };
+ static String ToString(GenericFamilyType);
+
+ enum Kerning { kAutoKerning, kNormalKerning, kNoneKerning };
+ static String ToString(Kerning);
+
+ enum LigaturesState {
+ kNormalLigaturesState,
+ kDisabledLigaturesState,
+ kEnabledLigaturesState
+ };
+ static String ToString(LigaturesState);
+
+ enum FontVariantCaps {
+ kCapsNormal,
+ kSmallCaps,
+ kAllSmallCaps,
+ kPetiteCaps,
+ kAllPetiteCaps,
+ kUnicase,
+ kTitlingCaps
+ };
+ static String ToString(FontVariantCaps);
+
+ FontDescription();
+ FontDescription(const FontDescription&);
+
+ FontDescription& operator=(const FontDescription&);
+
+ bool operator==(const FontDescription&) const;
+ bool operator!=(const FontDescription& other) const {
+ return !(*this == other);
+ }
+
+ struct VariantLigatures {
+ STACK_ALLOCATED();
+ VariantLigatures(LigaturesState state = kNormalLigaturesState)
+ : common(state),
+ discretionary(state),
+ historical(state),
+ contextual(state) {}
+
+ String ToString() const;
+
+ unsigned common : 2;
+ unsigned discretionary : 2;
+ unsigned historical : 2;
+ unsigned contextual : 2;
+ };
+
+ struct Size {
+ STACK_ALLOCATED();
+ Size(unsigned keyword, float value, bool is_absolute)
+ : keyword(keyword), is_absolute(is_absolute), value(value) {}
+
+ String ToString() const;
+
+ unsigned keyword : 4; // FontDescription::KeywordSize
+ unsigned is_absolute : 1; // FontDescription::IsAbsoluteSize
+ float value; // FontDescription::SpecifiedSize
+ };
+
+ struct FamilyDescription {
+ STACK_ALLOCATED();
+ FamilyDescription(GenericFamilyType generic_family)
+ : generic_family(generic_family) {}
+ FamilyDescription(GenericFamilyType generic_family,
+ const FontFamily& family)
+ : generic_family(generic_family), family(family) {}
+
+ String ToString() const;
+
+ GenericFamilyType generic_family;
+ FontFamily family;
+ };
+
+ const FontFamily& Family() const { return family_list_; }
+ FamilyDescription GetFamilyDescription() const {
+ return FamilyDescription(GenericFamily(), Family());
+ }
+ FontFamily& FirstFamily() { return family_list_; }
+ Size GetSize() const {
+ return Size(KeywordSize(), SpecifiedSize(), IsAbsoluteSize());
+ }
+ float SpecifiedSize() const { return specified_size_; }
+ float ComputedSize() const { return computed_size_; }
+ float AdjustedSize() const { return adjusted_size_; }
+ float SizeAdjust() const { return size_adjust_; }
+ bool HasSizeAdjust() const { return size_adjust_ != kFontSizeAdjustNone; }
+ int ComputedPixelSize() const { return int(computed_size_ + 0.5f); }
+ FontVariantCaps VariantCaps() const {
+ return static_cast<FontVariantCaps>(fields_.variant_caps_);
+ }
+ bool IsAbsoluteSize() const { return fields_.is_absolute_size_; }
+ FontSelectionValue Weight() const { return font_selection_request_.weight; }
+ FontSelectionValue Style() const { return font_selection_request_.slope; }
+ FontSelectionValue Stretch() const { return font_selection_request_.width; }
+ static FontSelectionValue LighterWeight(FontSelectionValue);
+ static FontSelectionValue BolderWeight(FontSelectionValue);
+ static Size LargerSize(const Size&);
+ static Size SmallerSize(const Size&);
+ GenericFamilyType GenericFamily() const {
+ return static_cast<GenericFamilyType>(fields_.generic_family_);
+ }
+
+ // only use fixed default size when there is only one font family, and that
+ // family is "monospace"
+ bool IsMonospace() const {
+ return GenericFamily() == kMonospaceFamily && !Family().Next() &&
+ Family().Family() == FontFamilyNames::webkit_monospace;
+ }
+ Kerning GetKerning() const { return static_cast<Kerning>(fields_.kerning_); }
+ FontVariantEastAsian VariantEastAsian() const {
+ return FontVariantEastAsian::InitializeFromUnsigned(
+ fields_.variant_east_asian_);
+ }
+ VariantLigatures GetVariantLigatures() const;
+ FontVariantNumeric VariantNumeric() const {
+ return FontVariantNumeric::InitializeFromUnsigned(fields_.variant_numeric_);
+ };
+ LigaturesState CommonLigaturesState() const {
+ return static_cast<LigaturesState>(fields_.common_ligatures_state_);
+ }
+ LigaturesState DiscretionaryLigaturesState() const {
+ return static_cast<LigaturesState>(fields_.discretionary_ligatures_state_);
+ }
+ LigaturesState HistoricalLigaturesState() const {
+ return static_cast<LigaturesState>(fields_.historical_ligatures_state_);
+ }
+ LigaturesState ContextualLigaturesState() const {
+ return static_cast<LigaturesState>(fields_.contextual_ligatures_state_);
+ }
+ unsigned KeywordSize() const { return fields_.keyword_size_; }
+ FontSmoothingMode FontSmoothing() const {
+ return static_cast<FontSmoothingMode>(fields_.font_smoothing_);
+ }
+ TextRenderingMode TextRendering() const {
+ return static_cast<TextRenderingMode>(fields_.text_rendering_);
+ }
+ const LayoutLocale* Locale() const { return locale_.get(); }
+ const LayoutLocale& LocaleOrDefault() const {
+ return LayoutLocale::ValueOrDefault(locale_.get());
+ }
+ UScriptCode GetScript() const { return LocaleOrDefault().GetScript(); }
+ bool IsSyntheticBold() const { return fields_.synthetic_bold_; }
+ bool IsSyntheticItalic() const { return fields_.synthetic_italic_; }
+ bool UseSubpixelPositioning() const {
+ return fields_.subpixel_text_position_;
+ }
+
+ FontSelectionRequest GetFontSelectionRequest() const;
+ float WordSpacing() const { return word_spacing_; }
+ float LetterSpacing() const { return letter_spacing_; }
+ FontOrientation Orientation() const {
+ return static_cast<FontOrientation>(fields_.orientation_);
+ }
+ bool IsVerticalAnyUpright() const {
+ return blink::IsVerticalAnyUpright(Orientation());
+ }
+ bool IsVerticalNonCJKUpright() const {
+ return blink::IsVerticalNonCJKUpright(Orientation());
+ }
+ bool IsVerticalUpright(UChar32 character) const {
+ return blink::IsVerticalUpright(Orientation(), character);
+ }
+ bool IsVerticalBaseline() const {
+ return blink::IsVerticalBaseline(Orientation());
+ }
+ FontWidthVariant WidthVariant() const {
+ return static_cast<FontWidthVariant>(fields_.width_variant_);
+ }
+ FontFeatureSettings* FeatureSettings() const {
+ return feature_settings_.get();
+ }
+ FontVariationSettings* VariationSettings() const {
+ return variation_settings_.get();
+ }
+
+ float EffectiveFontSize()
+ const; // Returns either the computedSize or the computedPixelSize
+ FontCacheKey CacheKey(
+ const FontFaceCreationParams&,
+ const FontSelectionRequest& = FontSelectionRequest()) const;
+
+ void SetFamily(const FontFamily& family) { family_list_ = family; }
+ void SetComputedSize(float s) { computed_size_ = clampTo<float>(s); }
+ void SetSpecifiedSize(float s) { specified_size_ = clampTo<float>(s); }
+ void SetAdjustedSize(float s) { adjusted_size_ = clampTo<float>(s); }
+ void SetSizeAdjust(float aspect) { size_adjust_ = clampTo<float>(aspect); }
+
+ void SetStyle(FontSelectionValue i) { font_selection_request_.slope = i; }
+ void SetWeight(FontSelectionValue w) { font_selection_request_.weight = w; }
+ void SetStretch(FontSelectionValue s) { font_selection_request_.width = s; }
+
+ void SetVariantCaps(FontVariantCaps);
+ void SetVariantEastAsian(const FontVariantEastAsian);
+ void SetVariantLigatures(const VariantLigatures&);
+ void SetVariantNumeric(const FontVariantNumeric&);
+ void SetIsAbsoluteSize(bool s) { fields_.is_absolute_size_ = s; }
+
+ void SetGenericFamily(GenericFamilyType generic_family) {
+ fields_.generic_family_ = generic_family;
+ }
+ void SetKerning(Kerning kerning) {
+ fields_.kerning_ = kerning;
+ UpdateTypesettingFeatures();
+ }
+ void SetKeywordSize(unsigned s) { fields_.keyword_size_ = s; }
+ void SetFontSmoothing(FontSmoothingMode smoothing) {
+ fields_.font_smoothing_ = smoothing;
+ }
+ void SetTextRendering(TextRenderingMode rendering) {
+ fields_.text_rendering_ = rendering;
+ UpdateTypesettingFeatures();
+ }
+ void SetOrientation(FontOrientation orientation) {
+ fields_.orientation_ = static_cast<unsigned>(orientation);
+ }
+ void SetWidthVariant(FontWidthVariant width_variant) {
+ fields_.width_variant_ = width_variant;
+ }
+ void SetLocale(scoped_refptr<const LayoutLocale> locale) {
+ locale_ = std::move(locale);
+ }
+ void SetSyntheticBold(bool synthetic_bold) {
+ fields_.synthetic_bold_ = synthetic_bold;
+ }
+ void SetSyntheticItalic(bool synthetic_italic) {
+ fields_.synthetic_italic_ = synthetic_italic;
+ }
+ void SetFeatureSettings(scoped_refptr<FontFeatureSettings> settings) {
+ feature_settings_ = std::move(settings);
+ }
+ void SetVariationSettings(scoped_refptr<FontVariationSettings> settings) {
+ variation_settings_ = std::move(settings);
+ }
+ void SetWordSpacing(float s) { word_spacing_ = s; }
+ void SetLetterSpacing(float s) {
+ letter_spacing_ = s;
+ UpdateTypesettingFeatures();
+ }
+
+ TypesettingFeatures GetTypesettingFeatures() const {
+ return static_cast<TypesettingFeatures>(fields_.typesetting_features_);
+ }
+
+ static void SetSubpixelPositioning(bool b) {
+ use_subpixel_text_positioning_ = b;
+ }
+ static bool SubpixelPositioning() { return use_subpixel_text_positioning_; }
+
+ void SetSubpixelAscentDescent(bool sp) const {
+ fields_.subpixel_ascent_descent_ = sp;
+ }
+
+ bool SubpixelAscentDescent() const {
+ return fields_.subpixel_ascent_descent_;
+ }
+
+ static void SetDefaultTypesettingFeatures(TypesettingFeatures);
+ static TypesettingFeatures DefaultTypesettingFeatures();
+
+ unsigned StyleHashWithoutFamilyList() const;
+ // TODO(drott): We should not expose internal structure here, but rather
+ // introduce a hash function here.
+ unsigned BitmapFields() const { return fields_as_unsigned_.parts[0]; }
+ unsigned AuxiliaryBitmapFields() const {
+ return fields_as_unsigned_.parts[1];
+ }
+
+ SkFontStyle SkiaFontStyle() const;
+
+ String ToString() const;
+
+ private:
+ FontFamily family_list_; // The list of font families to be used.
+ scoped_refptr<FontFeatureSettings> feature_settings_;
+ scoped_refptr<FontVariationSettings> variation_settings_;
+ scoped_refptr<const LayoutLocale> locale_;
+
+ void UpdateTypesettingFeatures();
+
+ // Specified CSS value. Independent of rendering issues such as integer
+ // rounding, minimum font sizes, and zooming.
+ float specified_size_;
+ // Computed size adjusted for the minimum font size and the zoom factor.
+ float computed_size_;
+
+ // (Given aspect value / aspect value of a font family) * specifiedSize.
+ // This value is adjusted for the minimum font size and the zoom factor
+ // as well as a computed size is.
+ float adjusted_size_;
+
+ // Given aspect value, i.e. font-size-adjust.
+ float size_adjust_;
+
+ float letter_spacing_;
+ float word_spacing_;
+
+ // Covers stretch, style, weight.
+ FontSelectionRequest font_selection_request_;
+
+ struct BitFields {
+ DISALLOW_NEW();
+
+ String ToString() const;
+
+ unsigned orientation_ : kFontOrientationBitCount;
+
+ unsigned width_variant_ : 2; // FontWidthVariant
+
+ unsigned variant_caps_ : 3; // FontVariantCaps
+ unsigned
+ is_absolute_size_ : 1; // Whether or not CSS specified an explicit size
+ // (logical sizes like "medium" don't count).
+ unsigned generic_family_ : 3; // GenericFamilyType
+
+ unsigned kerning_ : 2; // Kerning
+
+ unsigned common_ligatures_state_ : 2;
+ unsigned discretionary_ligatures_state_ : 2;
+ unsigned historical_ligatures_state_ : 2;
+ unsigned contextual_ligatures_state_ : 2;
+
+ // We cache whether or not a font is currently represented by a CSS keyword
+ // (e.g., medium). If so, then we can accurately translate across different
+ // generic families to adjust for different preference settings (e.g., 13px
+ // monospace vs. 16px everything else). Sizes are 1-8 (like the HTML size
+ // values for <font>).
+ unsigned keyword_size_ : 4;
+
+ unsigned font_smoothing_ : 2; // FontSmoothingMode
+ unsigned text_rendering_ : 2; // TextRenderingMode
+ unsigned synthetic_bold_ : 1;
+ unsigned synthetic_italic_ : 1;
+ unsigned subpixel_text_position_ : 1;
+ unsigned typesetting_features_ : 3;
+ unsigned variant_numeric_ : 8;
+ unsigned variant_east_asian_ : 6;
+ mutable unsigned subpixel_ascent_descent_ : 1;
+ };
+
+ static_assert(sizeof(BitFields) == sizeof(FieldsAsUnsignedType),
+ "Mapped bitfield datatypes must have identical size.");
+ union {
+ BitFields fields_;
+ FieldsAsUnsignedType fields_as_unsigned_;
+ };
+
+ static TypesettingFeatures default_typesetting_features_;
+
+ static bool use_subpixel_text_positioning_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_description_test.cc b/chromium/third_party/blink/renderer/platform/fonts/font_description_test.cc
new file mode 100644
index 00000000000..77fb5e78008
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_description_test.cc
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2014 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:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/fonts/font_description.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+TEST(FontDescriptionTest, TestHashCollision) {
+ FontSelectionValue weights[] = {
+ FontSelectionValue(100), FontSelectionValue(200),
+ FontSelectionValue(300), FontSelectionValue(400),
+ FontSelectionValue(500), FontSelectionValue(600),
+ FontSelectionValue(700), FontSelectionValue(800),
+ FontSelectionValue(900)};
+ FontSelectionValue stretches[]{
+ UltraCondensedWidthValue(), ExtraCondensedWidthValue(),
+ CondensedWidthValue(), SemiCondensedWidthValue(),
+ NormalWidthValue(), SemiExpandedWidthValue(),
+ ExpandedWidthValue(), ExtraExpandedWidthValue(),
+ UltraExpandedWidthValue()};
+
+ FontSelectionValue slopes[] = {NormalSlopeValue(), ItalicSlopeValue()};
+
+ FontDescription source;
+ WTF::Vector<unsigned> hashes;
+ for (size_t i = 0; i < WTF_ARRAY_LENGTH(weights); i++) {
+ source.SetWeight(weights[i]);
+ for (size_t j = 0; j < WTF_ARRAY_LENGTH(stretches); j++) {
+ source.SetStretch(stretches[j]);
+ for (size_t k = 0; k < WTF_ARRAY_LENGTH(slopes); k++) {
+ source.SetStyle(slopes[k]);
+ unsigned hash = source.StyleHashWithoutFamilyList();
+ ASSERT_FALSE(hashes.Contains(hash));
+ hashes.push_back(hash);
+ }
+ }
+ }
+}
+
+TEST(FontDescriptionTest, ToString) {
+ FontDescription description;
+
+ FontFamily family;
+ family.SetFamily("A");
+ scoped_refptr<SharedFontFamily> b_family = SharedFontFamily::Create();
+ b_family->SetFamily("B");
+ family.AppendFamily(b_family);
+ description.SetFamily(family);
+
+ description.SetLocale(LayoutLocale::Get("no"));
+
+ scoped_refptr<FontVariationSettings> variation_settings =
+ FontVariationSettings::Create();
+ variation_settings->Append(FontVariationAxis{"a", 42});
+ variation_settings->Append(FontVariationAxis{"b", 8118});
+ description.SetVariationSettings(variation_settings);
+
+ scoped_refptr<FontFeatureSettings> feature_settings = FontFeatureSettings::Create();
+ feature_settings->Append(FontFeature{"c", 76});
+ feature_settings->Append(FontFeature{"d", 94});
+ description.SetFeatureSettings(feature_settings);
+
+ description.SetSpecifiedSize(1.1f);
+ description.SetComputedSize(2.2f);
+ description.SetAdjustedSize(3.3f);
+ description.SetSizeAdjust(4.4f);
+ description.SetLetterSpacing(5.5f);
+ description.SetWordSpacing(6.6f);
+
+ description.SetStyle(FontSelectionValue(31.5));
+ description.SetWeight(FontSelectionValue(32.6));
+ description.SetStretch(FontSelectionValue(33.7));
+
+ description.SetTextRendering(kOptimizeLegibility);
+
+ EXPECT_EQ(
+ "family_list=[A,B], feature_settings=[c=76,d=94], "
+ "variation_settings=[a=42,b=8118], locale=no, specified_size=1.100000, "
+ "computed_size=2.200000, adjusted_size=3.300000, size_adjust=4.400000, "
+ "letter_spacing=5.500000, word_spacing=6.600000, "
+ "font_selection_request=[weight=32.500000, width=33.500000, "
+ "slope=31.500000], typesetting_features=[Kerning,Ligatures], "
+ "orientation=Horizontal, width_variant=Regular, variant_caps=Normal, "
+ "is_absolute_size=false, generic_family=None, kerning=Auto, "
+ "variant_ligatures=[common=Normal, discretionary=Normal, "
+ "historical=Normal, contextual=Normal], keyword_size=0, "
+ "font_smoothing=Auto, text_rendering=OptimizeLegibility, "
+ "synthetic_bold=false, synthetic_italic=false, "
+ "subpixel_positioning=false, subpixel_ascent_descent=false, "
+ "variant_numeric=[numeric_figure=NormalFigure, "
+ "numeric_spacing=NormalSpacing, numeric_fraction=Normal, ordinal=Off, "
+ "slashed_zero=Off], variant_east_asian=[form=Normal, width=Normal, "
+ "ruby=false]",
+ description.ToString());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_face_creation_params.h b/chromium/third_party/blink/renderer/platform/fonts/font_face_creation_params.h
new file mode 100644
index 00000000000..0fdbf572520
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_face_creation_params.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2009, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_FACE_CREATION_PARAMS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_FACE_CREATION_PARAMS_H_
+
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/string_hasher.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+
+namespace blink {
+
+enum FontFaceCreationType {
+ kCreateFontByFamily,
+ kCreateFontByFciIdAndTtcIndex
+};
+
+class FontFaceCreationParams {
+ USING_FAST_MALLOC(FontFaceCreationParams);
+
+ public:
+ FontFaceCreationParams()
+ : creation_type_(kCreateFontByFamily),
+ family_(AtomicString()),
+ filename_(CString()),
+ fontconfig_interface_id_(0),
+ ttc_index_(0) {}
+
+ explicit FontFaceCreationParams(AtomicString family)
+ : creation_type_(kCreateFontByFamily),
+ family_(family),
+ filename_(CString()),
+ fontconfig_interface_id_(0),
+ ttc_index_(0) {
+#if defined(OS_WIN)
+ // Leading "@" in the font name enables Windows vertical flow flag for the
+ // font. Because we do vertical flow by ourselves, we don't want to use the
+ // Windows feature. IE disregards "@" regardless of the orientation, so we
+ // follow the behavior and normalize the family name.
+ family_ = (family_.IsEmpty() || family_[0] != '@')
+ ? family_
+ : AtomicString(family_.Impl()->Substring(1));
+#endif
+ }
+
+ FontFaceCreationParams(CString filename,
+ int fontconfig_interface_id,
+ int ttc_index = 0)
+ : creation_type_(kCreateFontByFciIdAndTtcIndex),
+ filename_(filename),
+ fontconfig_interface_id_(fontconfig_interface_id),
+ ttc_index_(ttc_index) {}
+
+ FontFaceCreationType CreationType() const { return creation_type_; }
+ AtomicString Family() const {
+ DCHECK_EQ(creation_type_, kCreateFontByFamily);
+ return family_;
+ }
+ CString Filename() const {
+ DCHECK_EQ(creation_type_, kCreateFontByFciIdAndTtcIndex);
+ return filename_;
+ }
+ int FontconfigInterfaceId() const {
+ DCHECK_EQ(creation_type_, kCreateFontByFciIdAndTtcIndex);
+ return fontconfig_interface_id_;
+ }
+ int TtcIndex() const {
+ DCHECK_EQ(creation_type_, kCreateFontByFciIdAndTtcIndex);
+ return ttc_index_;
+ }
+
+ unsigned GetHash() const {
+ if (creation_type_ == kCreateFontByFciIdAndTtcIndex) {
+ StringHasher hasher;
+ // Hashing the filename and ints in this way is sensitive to character
+ // encoding and endianness. However, since the hash is not transferred
+ // over a network or permanently stored and only used for the runtime of
+ // Chromium, this is not a concern.
+ hasher.AddCharacters(reinterpret_cast<const LChar*>(filename_.data()),
+ filename_.length());
+ hasher.AddCharacters(reinterpret_cast<const LChar*>(&ttc_index_),
+ sizeof(ttc_index_));
+ hasher.AddCharacters(
+ reinterpret_cast<const LChar*>(&fontconfig_interface_id_),
+ sizeof(fontconfig_interface_id_));
+ return hasher.GetHash();
+ }
+ return CaseFoldingHash::GetHash(family_.IsEmpty() ? "" : family_);
+ }
+
+ bool operator==(const FontFaceCreationParams& other) const {
+ return creation_type_ == other.creation_type_ &&
+ DeprecatedEqualIgnoringCase(family_, other.family_) &&
+ filename_ == other.filename_ &&
+ fontconfig_interface_id_ == other.fontconfig_interface_id_ &&
+ ttc_index_ == other.ttc_index_;
+ }
+
+ private:
+ FontFaceCreationType creation_type_;
+ AtomicString family_;
+ CString filename_;
+ int fontconfig_interface_id_;
+ int ttc_index_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_FACE_CREATION_PARAMS_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_fallback_iterator.cc b/chromium/third_party/blink/renderer/platform/fonts/font_fallback_iterator.cc
new file mode 100644
index 00000000000..e0c73ed0a07
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_fallback_iterator.cc
@@ -0,0 +1,246 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/font_fallback_iterator.h"
+
+#include "third_party/blink/renderer/platform/fonts/font_cache.h"
+#include "third_party/blink/renderer/platform/fonts/font_description.h"
+#include "third_party/blink/renderer/platform/fonts/font_fallback_list.h"
+#include "third_party/blink/renderer/platform/fonts/segmented_font_data.h"
+#include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
+#include "third_party/blink/renderer/platform/text/icu_error.h"
+
+namespace blink {
+
+scoped_refptr<FontFallbackIterator> FontFallbackIterator::Create(
+ const FontDescription& description,
+ scoped_refptr<FontFallbackList> fallback_list,
+ FontFallbackPriority font_fallback_priority) {
+ return base::AdoptRef(new FontFallbackIterator(
+ description, std::move(fallback_list), font_fallback_priority));
+}
+
+FontFallbackIterator::FontFallbackIterator(
+ const FontDescription& description,
+ scoped_refptr<FontFallbackList> fallback_list,
+ FontFallbackPriority font_fallback_priority)
+ : font_description_(description),
+ font_fallback_list_(std::move(fallback_list)),
+ current_font_data_index_(0),
+ segmented_face_index_(0),
+ fallback_stage_(kFontGroupFonts),
+ font_fallback_priority_(font_fallback_priority) {}
+
+bool FontFallbackIterator::AlreadyLoadingRangeForHintChar(UChar32 hint_char) {
+ for (auto it = tracked_loading_range_sets_.begin();
+ it != tracked_loading_range_sets_.end(); ++it) {
+ if ((*it)->Contains(hint_char))
+ return true;
+ }
+ return false;
+}
+
+bool FontFallbackIterator::RangeSetContributesForHint(
+ const Vector<UChar32> hint_list,
+ const FontDataForRangeSet* segmented_face) {
+ for (auto it = hint_list.begin(); it != hint_list.end(); ++it) {
+ if (segmented_face->Contains(*it)) {
+ if (!AlreadyLoadingRangeForHintChar(*it))
+ return true;
+ }
+ }
+ return false;
+}
+
+void FontFallbackIterator::WillUseRange(const AtomicString& family,
+ const FontDataForRangeSet& range_set) {
+ FontSelector* selector = font_fallback_list_->GetFontSelector();
+ if (!selector)
+ return;
+
+ selector->WillUseRange(font_description_, family, range_set);
+}
+
+scoped_refptr<FontDataForRangeSet> FontFallbackIterator::UniqueOrNext(
+ scoped_refptr<FontDataForRangeSet> candidate,
+ const Vector<UChar32>& hint_list) {
+ SkTypeface* candidate_typeface =
+ candidate->FontData()->PlatformData().Typeface();
+ if (!candidate_typeface)
+ return Next(hint_list);
+
+ uint32_t candidate_id = candidate_typeface->uniqueID();
+ if (unique_font_data_for_range_sets_returned_.Contains(candidate_id)) {
+ return Next(hint_list);
+ }
+
+ // We don't want to skip subsetted ranges because HarfBuzzShaper's behavior
+ // depends on the subsetting.
+ if (candidate->IsEntireRange())
+ unique_font_data_for_range_sets_returned_.insert(candidate_id);
+ return candidate;
+}
+
+scoped_refptr<FontDataForRangeSet> FontFallbackIterator::Next(
+ const Vector<UChar32>& hint_list) {
+ if (fallback_stage_ == kOutOfLuck)
+ return base::AdoptRef(new FontDataForRangeSet());
+
+ if (fallback_stage_ == kFallbackPriorityFonts) {
+ // Only try one fallback priority font,
+ // then proceed to regular system fallback.
+ fallback_stage_ = kSystemFonts;
+ scoped_refptr<FontDataForRangeSet> fallback_priority_font_range =
+ base::AdoptRef(
+ new FontDataForRangeSet(FallbackPriorityFont(hint_list[0])));
+ if (fallback_priority_font_range->HasFontData())
+ return UniqueOrNext(std::move(fallback_priority_font_range), hint_list);
+ return Next(hint_list);
+ }
+
+ if (fallback_stage_ == kSystemFonts) {
+ // We've reached pref + system fallback.
+ scoped_refptr<SimpleFontData> system_font = UniqueSystemFontForHintList(hint_list);
+ if (system_font) {
+ // Fallback fonts are not retained in the FontDataCache.
+ return UniqueOrNext(base::AdoptRef(new FontDataForRangeSet(system_font)),
+ hint_list);
+ }
+
+ // If we don't have options from the system fallback anymore or had
+ // previously returned them, we only have the last resort font left.
+ // TODO: crbug.com/42217 Improve this by doing the last run with a last
+ // resort font that has glyphs for everything, for example the Unicode
+ // LastResort font, not just Times or Arial.
+ FontCache* font_cache = FontCache::GetFontCache();
+ fallback_stage_ = kOutOfLuck;
+ scoped_refptr<SimpleFontData> last_resort =
+ font_cache->GetLastResortFallbackFont(font_description_).get();
+ if (!last_resort)
+ FontCache::CrashWithFontInfo(&font_description_);
+ // Don't skip the LastResort font in uniqueOrNext() since HarfBuzzShaper
+ // needs to use this one to place missing glyph boxes.
+ return base::AdoptRef(new FontDataForRangeSetFromCache(last_resort));
+ }
+
+ DCHECK(fallback_stage_ == kFontGroupFonts ||
+ fallback_stage_ == kSegmentedFace);
+ const FontData* font_data = font_fallback_list_->FontDataAt(
+ font_description_, current_font_data_index_);
+
+ if (!font_data) {
+ // If there is no fontData coming from the fallback list, it means
+ // we are now looking at system fonts, either for prioritized symbol
+ // or emoji fonts or by calling system fallback API.
+ fallback_stage_ = IsNonTextFallbackPriority(font_fallback_priority_)
+ ? kFallbackPriorityFonts
+ : kSystemFonts;
+ return Next(hint_list);
+ }
+
+ // Otherwise we've received a fontData from the font-family: set of fonts,
+ // and a non-segmented one in this case.
+ if (!font_data->IsSegmented()) {
+ // Skip forward to the next font family for the next call to next().
+ current_font_data_index_++;
+ if (!font_data->IsLoading()) {
+ scoped_refptr<SimpleFontData> non_segmented =
+ const_cast<SimpleFontData*>(ToSimpleFontData(font_data));
+ // The fontData object that we have here is tracked in m_fontList of
+ // FontFallbackList and gets released in the font cache when the
+ // FontFallbackList is destroyed.
+ return UniqueOrNext(
+ base::AdoptRef(new FontDataForRangeSet(non_segmented)), hint_list);
+ }
+ return Next(hint_list);
+ }
+
+ // Iterate over ranges of a segmented font below.
+
+ const SegmentedFontData* segmented = ToSegmentedFontData(font_data);
+ if (fallback_stage_ != kSegmentedFace) {
+ segmented_face_index_ = 0;
+ fallback_stage_ = kSegmentedFace;
+ }
+
+ DCHECK_LT(segmented_face_index_, segmented->NumFaces());
+ scoped_refptr<FontDataForRangeSet> current_segmented_face =
+ segmented->FaceAt(segmented_face_index_);
+ segmented_face_index_++;
+
+ if (segmented_face_index_ == segmented->NumFaces()) {
+ // Switch from iterating over a segmented face to the next family from
+ // the font-family: group of fonts.
+ fallback_stage_ = kFontGroupFonts;
+ current_font_data_index_++;
+ }
+
+ if (RangeSetContributesForHint(hint_list, current_segmented_face.get())) {
+ const SimpleFontData* font_data = current_segmented_face->FontData();
+ if (const CustomFontData* custom_font_data = font_data->GetCustomFontData())
+ custom_font_data->BeginLoadIfNeeded();
+ if (!font_data->IsLoading())
+ return UniqueOrNext(current_segmented_face, hint_list);
+ tracked_loading_range_sets_.push_back(current_segmented_face);
+ }
+
+ return Next(hint_list);
+}
+
+scoped_refptr<SimpleFontData> FontFallbackIterator::FallbackPriorityFont(
+ UChar32 hint) {
+ return FontCache::GetFontCache()->FallbackFontForCharacter(
+ font_description_, hint,
+ font_fallback_list_->PrimarySimpleFontData(font_description_),
+ font_fallback_priority_);
+}
+
+static inline unsigned ChooseHintIndex(const Vector<UChar32>& hint_list) {
+ // crbug.com/618178 has a test case where no Myanmar font is ever found,
+ // because the run starts with a punctuation character with a script value of
+ // common. Our current font fallback code does not find a very meaningful
+ // result for this.
+ // TODO crbug.com/668706 - Improve this situation.
+ // So if we have multiple hint characters (which indicates that a
+ // multi-character grapheme or more failed to shape, then we can try to be
+ // smarter and select the first character that has an actual script value.
+ DCHECK(hint_list.size());
+ if (hint_list.size() <= 1)
+ return 0;
+
+ ICUError err;
+ UScriptCode hint_char_script = uscript_getScript(hint_list[0], &err);
+ if (!U_SUCCESS(err) || hint_char_script > USCRIPT_INHERITED)
+ return 0;
+
+ for (size_t i = 1; i < hint_list.size(); ++i) {
+ UScriptCode new_hint_script = uscript_getScript(hint_list[i], &err);
+ if (!U_SUCCESS(err))
+ return 0;
+ if (new_hint_script > USCRIPT_INHERITED)
+ return i;
+ }
+ return 0;
+}
+
+scoped_refptr<SimpleFontData> FontFallbackIterator::UniqueSystemFontForHintList(
+ const Vector<UChar32>& hint_list) {
+ // When we're asked for a fallback for the same characters again, we give up
+ // because the shaper must have previously tried shaping with the font
+ // already.
+ if (!hint_list.size())
+ return nullptr;
+
+ FontCache* font_cache = FontCache::GetFontCache();
+ UChar32 hint = hint_list[ChooseHintIndex(hint_list)];
+
+ if (!hint || previously_asked_for_hint_.Contains(hint))
+ return nullptr;
+ previously_asked_for_hint_.insert(hint);
+ return font_cache->FallbackFontForCharacter(
+ font_description_, hint,
+ font_fallback_list_->PrimarySimpleFontData(font_description_));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_fallback_iterator.h b/chromium/third_party/blink/renderer/platform/fonts/font_fallback_iterator.h
new file mode 100644
index 00000000000..22e1667ce0e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_fallback_iterator.h
@@ -0,0 +1,82 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_FALLBACK_ITERATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_FALLBACK_ITERATOR_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/fonts/font_data_for_range_set.h"
+#include "third_party/blink/renderer/platform/fonts/font_fallback_priority.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class FontDescription;
+class FontFallbackList;
+class SimpleFontData;
+
+class FontFallbackIterator : public RefCounted<FontFallbackIterator> {
+ WTF_MAKE_NONCOPYABLE(FontFallbackIterator);
+
+ public:
+ static scoped_refptr<FontFallbackIterator> Create(const FontDescription&,
+ scoped_refptr<FontFallbackList>,
+ FontFallbackPriority);
+
+ bool HasNext() const { return fallback_stage_ != kOutOfLuck; };
+
+ // Some system fallback APIs (Windows, Android) require a character, or a
+ // portion of the string to be passed. On Mac and Linux, we get a list of
+ // fonts without passing in characters.
+ scoped_refptr<FontDataForRangeSet> Next(const Vector<UChar32>& hint_list);
+
+ private:
+ FontFallbackIterator(const FontDescription&,
+ scoped_refptr<FontFallbackList>,
+ FontFallbackPriority);
+ bool RangeSetContributesForHint(const Vector<UChar32> hint_list,
+ const FontDataForRangeSet*);
+ bool AlreadyLoadingRangeForHintChar(UChar32 hint_char);
+ void WillUseRange(const AtomicString& family, const FontDataForRangeSet&);
+
+ scoped_refptr<FontDataForRangeSet> UniqueOrNext(
+ scoped_refptr<FontDataForRangeSet> candidate,
+ const Vector<UChar32>& hint_list);
+
+ scoped_refptr<SimpleFontData> FallbackPriorityFont(UChar32 hint);
+ scoped_refptr<SimpleFontData> UniqueSystemFontForHintList(
+ const Vector<UChar32>& hint_list);
+
+ const FontDescription& font_description_;
+ scoped_refptr<FontFallbackList> font_fallback_list_;
+ int current_font_data_index_;
+ unsigned segmented_face_index_;
+
+ enum FallbackStage {
+ kFallbackPriorityFonts,
+ kFontGroupFonts,
+ kSegmentedFace,
+ kPreferencesFonts,
+ kSystemFonts,
+ kOutOfLuck
+ };
+
+ FallbackStage fallback_stage_;
+ HashSet<UChar32> previously_asked_for_hint_;
+ // FontFallbackIterator is meant for single use by HarfBuzzShaper,
+ // traversing through the fonts for shaping only once. We must not return
+ // duplicate FontDataForRangeSet objects from the next() iteration functions
+ // as returning a duplicate value causes a shaping run that won't return any
+ // results.
+ HashSet<uint32_t> unique_font_data_for_range_sets_returned_;
+ Vector<scoped_refptr<FontDataForRangeSet>> tracked_loading_range_sets_;
+ FontFallbackPriority font_fallback_priority_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_fallback_list.cc b/chromium/third_party/blink/renderer/platform/fonts/font_fallback_list.cc
new file mode 100644
index 00000000000..a29433a64c5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_fallback_list.cc
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2006 Apple Computer, 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/fonts/font_fallback_list.h"
+
+#include "third_party/blink/renderer/platform/font_family_names.h"
+#include "third_party/blink/renderer/platform/fonts/alternate_font_family.h"
+#include "third_party/blink/renderer/platform/fonts/font_cache.h"
+#include "third_party/blink/renderer/platform/fonts/font_cache_key.h"
+#include "third_party/blink/renderer/platform/fonts/font_description.h"
+#include "third_party/blink/renderer/platform/fonts/font_family.h"
+#include "third_party/blink/renderer/platform/fonts/segmented_font_data.h"
+#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
+
+namespace blink {
+
+FontFallbackList::FontFallbackList()
+ : cached_primary_simple_font_data_(nullptr),
+ font_selector_(nullptr),
+ font_selector_version_(0),
+ family_index_(0),
+ generation_(FontCache::GetFontCache()->Generation()),
+ has_loading_fallback_(false) {}
+
+void FontFallbackList::Invalidate(FontSelector* font_selector) {
+ ReleaseFontData();
+ font_list_.clear();
+ cached_primary_simple_font_data_ = nullptr;
+ family_index_ = 0;
+ has_loading_fallback_ = false;
+ if (font_selector_ != font_selector)
+ font_selector_ = font_selector;
+ font_selector_version_ = font_selector_ ? font_selector_->Version() : 0;
+ generation_ = FontCache::GetFontCache()->Generation();
+}
+
+void FontFallbackList::ReleaseFontData() {
+ unsigned num_fonts = font_list_.size();
+ for (unsigned i = 0; i < num_fonts; ++i) {
+ if (!font_list_[i]->IsCustomFont()) {
+ DCHECK(!font_list_[i]->IsSegmented());
+ FontCache::GetFontCache()->ReleaseFontData(
+ ToSimpleFontData(font_list_[i]));
+ }
+ }
+ shape_cache_.reset(); // Clear the weak pointer to the cache instance.
+}
+
+bool FontFallbackList::LoadingCustomFonts() const {
+ if (!has_loading_fallback_)
+ return false;
+
+ unsigned num_fonts = font_list_.size();
+ for (unsigned i = 0; i < num_fonts; ++i) {
+ if (font_list_[i]->IsLoading())
+ return true;
+ }
+ return false;
+}
+
+bool FontFallbackList::ShouldSkipDrawing() const {
+ if (!has_loading_fallback_)
+ return false;
+
+ unsigned num_fonts = font_list_.size();
+ for (unsigned i = 0; i < num_fonts; ++i) {
+ if (font_list_[i]->ShouldSkipDrawing())
+ return true;
+ }
+ return false;
+}
+
+const SimpleFontData* FontFallbackList::DeterminePrimarySimpleFontData(
+ const FontDescription& font_description) const {
+ bool should_load_custom_font = true;
+
+ for (unsigned font_index = 0;; ++font_index) {
+ const FontData* font_data = FontDataAt(font_description, font_index);
+ if (!font_data) {
+ // All fonts are custom fonts and are loading. Return the first FontData.
+ font_data = FontDataAt(font_description, 0);
+ if (font_data)
+ return font_data->FontDataForCharacter(kSpaceCharacter);
+
+ FontCache* cache = FontCache::GetFontCache();
+ SimpleFontData* last_resort_fallback =
+ cache->GetLastResortFallbackFont(font_description).get();
+ DCHECK(last_resort_fallback);
+ return last_resort_fallback;
+ }
+
+ if (font_data->IsSegmented() &&
+ !ToSegmentedFontData(font_data)->ContainsCharacter(kSpaceCharacter))
+ continue;
+
+ const SimpleFontData* font_data_for_space =
+ font_data->FontDataForCharacter(kSpaceCharacter);
+ DCHECK(font_data_for_space);
+
+ // When a custom font is loading, we should use the correct fallback font to
+ // layout the text. Here skip the temporary font for the loading custom
+ // font which may not act as the correct fallback font.
+ if (!font_data_for_space->IsLoadingFallback())
+ return font_data_for_space;
+
+ if (font_data->IsSegmented()) {
+ const SegmentedFontData* segmented = ToSegmentedFontData(font_data);
+ for (unsigned i = 0; i < segmented->NumFaces(); i++) {
+ const SimpleFontData* range_font_data =
+ segmented->FaceAt(i)->FontData();
+ if (!range_font_data->IsLoadingFallback())
+ return range_font_data;
+ }
+ if (font_data->IsLoading())
+ should_load_custom_font = false;
+ }
+
+ // Begin to load the first custom font if needed.
+ if (should_load_custom_font) {
+ should_load_custom_font = false;
+ font_data_for_space->GetCustomFontData()->BeginLoadIfNeeded();
+ }
+ }
+}
+
+scoped_refptr<FontData> FontFallbackList::GetFontData(
+ const FontDescription& font_description,
+ int& family_index) const {
+ const FontFamily* curr_family = &font_description.Family();
+ for (int i = 0; curr_family && i < family_index; i++)
+ curr_family = curr_family->Next();
+
+ for (; curr_family; curr_family = curr_family->Next()) {
+ family_index++;
+ if (curr_family->Family().length()) {
+ scoped_refptr<FontData> result;
+ if (font_selector_)
+ result = font_selector_->GetFontData(font_description,
+ curr_family->Family());
+ if (!result)
+ result = FontCache::GetFontCache()->GetFontData(font_description,
+ curr_family->Family());
+ if (result)
+ return result;
+ }
+ }
+ family_index = kCAllFamiliesScanned;
+
+ if (font_selector_) {
+ // Try the user's preferred standard font.
+ if (scoped_refptr<FontData> data = font_selector_->GetFontData(
+ font_description, FontFamilyNames::webkit_standard))
+ return data;
+ }
+
+ // Still no result. Hand back our last resort fallback font.
+ return FontCache::GetFontCache()->GetLastResortFallbackFont(font_description);
+}
+
+FallbackListCompositeKey FontFallbackList::CompositeKey(
+ const FontDescription& font_description) const {
+ FallbackListCompositeKey key(font_description);
+ const FontFamily* current_family = &font_description.Family();
+ while (current_family) {
+ if (current_family->Family().length()) {
+ FontFaceCreationParams params(
+ AdjustFamilyNameToAvoidUnsupportedFonts(current_family->Family()));
+ scoped_refptr<FontData> result;
+ if (font_selector_)
+ result = font_selector_->GetFontData(font_description,
+ current_family->Family());
+ if (!result) {
+ if (FontPlatformData* platform_data =
+ FontCache::GetFontCache()->GetFontPlatformData(font_description,
+ params))
+ result = FontCache::GetFontCache()->FontDataFromFontPlatformData(
+ platform_data);
+ }
+ if (result) {
+ key.Add(font_description.CacheKey(params));
+ if (!result->IsSegmented() && !result->IsCustomFont())
+ FontCache::GetFontCache()->ReleaseFontData(ToSimpleFontData(result));
+ }
+ }
+ current_family = current_family->Next();
+ }
+
+ return key;
+}
+
+const FontData* FontFallbackList::FontDataAt(
+ const FontDescription& font_description,
+ unsigned realized_font_index) const {
+ // This fallback font is already in our list.
+ if (realized_font_index < font_list_.size())
+ return font_list_[realized_font_index].get();
+
+ // Make sure we're not passing in some crazy value here.
+ DCHECK_EQ(realized_font_index, font_list_.size());
+
+ if (family_index_ == kCAllFamiliesScanned)
+ return nullptr;
+
+ // Ask the font cache for the font data.
+ // We are obtaining this font for the first time. We keep track of the
+ // families we've looked at before in |m_familyIndex|, so that we never scan
+ // the same spot in the list twice. getFontData will adjust our
+ // |m_familyIndex| as it scans for the right font to make.
+ DCHECK_EQ(FontCache::GetFontCache()->Generation(), generation_);
+ scoped_refptr<FontData> result = GetFontData(font_description, family_index_);
+ if (result) {
+ font_list_.push_back(result);
+ if (result->IsLoadingFallback())
+ has_loading_fallback_ = true;
+ }
+ return result.get();
+}
+
+bool FontFallbackList::IsValid() const {
+ if (!font_selector_)
+ return font_selector_version_ == 0;
+
+ return font_selector_->Version() == font_selector_version_;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_fallback_list.h b/chromium/third_party/blink/renderer/platform/fonts/font_fallback_list.h
new file mode 100644
index 00000000000..05b8b4bb8ab
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_fallback_list.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2006, 2010 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_FALLBACK_LIST_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_FALLBACK_LIST_H_
+
+#include "base/memory/weak_ptr.h"
+#include "third_party/blink/renderer/platform/fonts/fallback_list_composite_key.h"
+#include "third_party/blink/renderer/platform/fonts/font_cache.h"
+#include "third_party/blink/renderer/platform/fonts/font_selector.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_cache.h"
+#include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+
+namespace blink {
+
+class FontDescription;
+
+const int kCAllFamiliesScanned = -1;
+
+class PLATFORM_EXPORT FontFallbackList : public RefCounted<FontFallbackList> {
+ WTF_MAKE_NONCOPYABLE(FontFallbackList);
+
+ public:
+ static scoped_refptr<FontFallbackList> Create() {
+ return base::AdoptRef(new FontFallbackList());
+ }
+
+ ~FontFallbackList() { ReleaseFontData(); }
+ bool IsValid() const;
+ void Invalidate(FontSelector*);
+
+ bool LoadingCustomFonts() const;
+ bool ShouldSkipDrawing() const;
+
+ FontSelector* GetFontSelector() const { return font_selector_.Get(); }
+ // FIXME: It should be possible to combine fontSelectorVersion and generation.
+ unsigned FontSelectorVersion() const { return font_selector_version_; }
+ unsigned Generation() const { return generation_; }
+
+ ShapeCache* GetShapeCache(const FontDescription& font_description) const {
+ if (!shape_cache_) {
+ FallbackListCompositeKey key = CompositeKey(font_description);
+ shape_cache_ =
+ FontCache::GetFontCache()->GetShapeCache(key)->GetWeakPtr();
+ }
+ DCHECK(shape_cache_);
+ if (GetFontSelector())
+ shape_cache_->ClearIfVersionChanged(GetFontSelector()->Version());
+ return shape_cache_.get();
+ }
+
+ const SimpleFontData* PrimarySimpleFontData(
+ const FontDescription& font_description) {
+ if (!cached_primary_simple_font_data_) {
+ cached_primary_simple_font_data_ =
+ DeterminePrimarySimpleFontData(font_description);
+ DCHECK(cached_primary_simple_font_data_);
+ }
+ return cached_primary_simple_font_data_;
+ }
+ const FontData* FontDataAt(const FontDescription&, unsigned index) const;
+
+ FallbackListCompositeKey CompositeKey(const FontDescription&) const;
+
+ private:
+ FontFallbackList();
+
+ scoped_refptr<FontData> GetFontData(const FontDescription&, int& family_index) const;
+
+ const SimpleFontData* DeterminePrimarySimpleFontData(
+ const FontDescription&) const;
+
+ void ReleaseFontData();
+
+ mutable Vector<scoped_refptr<FontData>, 1> font_list_;
+ mutable const SimpleFontData* cached_primary_simple_font_data_;
+ Persistent<FontSelector> font_selector_;
+ unsigned font_selector_version_;
+ mutable int family_index_;
+ unsigned short generation_;
+ mutable bool has_loading_fallback_ : 1;
+ mutable base::WeakPtr<ShapeCache> shape_cache_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_fallback_priority.cc b/chromium/third_party/blink/renderer/platform/fonts/font_fallback_priority.cc
new file mode 100644
index 00000000000..5310cde786b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_fallback_priority.cc
@@ -0,0 +1,14 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/font_fallback_priority.h"
+
+namespace blink {
+
+bool IsNonTextFallbackPriority(FontFallbackPriority fallback_priority) {
+ return fallback_priority == FontFallbackPriority::kEmojiText ||
+ fallback_priority == FontFallbackPriority::kEmojiEmoji;
+};
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_fallback_priority.h b/chromium/third_party/blink/renderer/platform/fonts/font_fallback_priority.h
new file mode 100644
index 00000000000..5626be46fe2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_fallback_priority.h
@@ -0,0 +1,29 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_FALLBACK_PRIORITY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_FALLBACK_PRIORITY_H_
+
+namespace blink {
+
+// http://unicode.org/reports/tr51/#Presentation_Style discusses the differences
+// between emoji in text and the emoji in emoji presentation. In that sense, the
+// EmojiEmoji wording is taken from there. Also compare
+// http://unicode.org/Public/emoji/1.0/emoji-data.txt
+enum class FontFallbackPriority {
+ // For regular non-symbols text,
+ // normal text fallback in FontFallbackIterator
+ kText,
+ // For emoji in text presentaiton
+ kEmojiText,
+ // For emoji in emoji presentation
+ kEmojiEmoji,
+ kInvalid
+};
+
+bool IsNonTextFallbackPriority(FontFallbackPriority);
+
+}; // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_family.cc b/chromium/third_party/blink/renderer/platform/fonts/font_family.cc
new file mode 100644
index 00000000000..4aafd636d98
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_family.cc
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2004, 2008 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/fonts/font_family.h"
+
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+
+namespace blink {
+
+bool operator==(const FontFamily& a, const FontFamily& b) {
+ if (a.Family() != b.Family())
+ return false;
+ const FontFamily* ap;
+ const FontFamily* bp;
+ for (ap = a.Next(), bp = b.Next(); ap != bp;
+ ap = ap->Next(), bp = bp->Next()) {
+ if (!ap || !bp)
+ return false;
+ if (ap->Family() != bp->Family())
+ return false;
+ }
+ return true;
+}
+
+String FontFamily::ToString() const {
+ StringBuilder builder;
+ builder.Append(family_);
+ const FontFamily* current = Next();
+ while (current) {
+ builder.Append(",");
+ builder.Append(current->Family());
+ current = current->Next();
+ }
+ return builder.ToString();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_family.h b/chromium/third_party/blink/renderer/platform/fonts/font_family.h
new file mode 100644
index 00000000000..90b03b7631f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_family.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2003, 2006, 2008 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_FAMILY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_FAMILY_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+
+namespace blink {
+
+class SharedFontFamily;
+
+class PLATFORM_EXPORT FontFamily {
+ DISALLOW_NEW();
+
+ public:
+ FontFamily() = default;
+ ~FontFamily();
+
+ void SetFamily(const AtomicString& family) { family_ = family; }
+ const AtomicString& Family() const { return family_; }
+ bool FamilyIsEmpty() const { return family_.IsEmpty(); }
+
+ const FontFamily* Next() const;
+
+ void AppendFamily(scoped_refptr<SharedFontFamily>);
+ scoped_refptr<SharedFontFamily> ReleaseNext();
+
+ // Returns this font family's name followed by all subsequent linked
+ // families delimited by commas.
+ String ToString() const;
+
+ private:
+ AtomicString family_;
+ scoped_refptr<SharedFontFamily> next_;
+};
+
+class PLATFORM_EXPORT SharedFontFamily : public FontFamily,
+ public RefCounted<SharedFontFamily> {
+ USING_FAST_MALLOC(SharedFontFamily);
+ public:
+ static scoped_refptr<SharedFontFamily> Create() {
+ return base::AdoptRef(new SharedFontFamily);
+ }
+
+ private:
+ SharedFontFamily() = default;
+
+ DISALLOW_COPY_AND_ASSIGN(SharedFontFamily);
+};
+
+PLATFORM_EXPORT bool operator==(const FontFamily&, const FontFamily&);
+inline bool operator!=(const FontFamily& a, const FontFamily& b) {
+ return !(a == b);
+}
+
+inline FontFamily::~FontFamily() {
+ scoped_refptr<SharedFontFamily> reaper = std::move(next_);
+ while (reaper && reaper->HasOneRef()) {
+ // implicitly protects reaper->next, then derefs reaper
+ reaper = reaper->ReleaseNext();
+ }
+}
+
+inline const FontFamily* FontFamily::Next() const {
+ return next_.get();
+}
+
+inline void FontFamily::AppendFamily(scoped_refptr<SharedFontFamily> family) {
+ next_ = std::move(family);
+}
+
+inline scoped_refptr<SharedFontFamily> FontFamily::ReleaseNext() {
+ return std::move(next_);
+}
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_family_names.json5 b/chromium/third_party/blink/renderer/platform/fonts/font_family_names.json5
new file mode 100644
index 00000000000..638bf7995fe
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_family_names.json5
@@ -0,0 +1,40 @@
+{
+ metadata: {
+ namespace: "FontFamily",
+ export: "PLATFORM_EXPORT",
+ },
+
+ data: [
+ "-webkit-cursive",
+ "-webkit-fantasy",
+ "-webkit-monospace",
+ "-webkit-sans-serif",
+ "-webkit-serif",
+ "-webkit-pictograph",
+ "-webkit-standard",
+ "system-ui",
+
+ "Arial",
+ "Calibri",
+ "Courier New",
+ "Courier",
+ "Helvetica",
+ "Lucida Grande",
+ "Microsoft Sans Serif",
+ "MS Sans Serif",
+ "MS Serif",
+ "MS UI Gothic",
+ "Sans",
+ "Segoe UI",
+ "Times New Roman",
+ "Times",
+
+ "cursive",
+ "fantasy",
+ "monospace",
+ "sans-serif",
+ "serif",
+
+ "BlinkMacSystemFont",
+ ],
+}
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_family_test.cc b/chromium/third_party/blink/renderer/platform/fonts/font_family_test.cc
new file mode 100644
index 00000000000..da00ee1e0d4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_family_test.cc
@@ -0,0 +1,42 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/font_family.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+namespace {
+
+FontFamily* CreateAndAppendFamily(FontFamily& parent, const char* name) {
+ scoped_refptr<SharedFontFamily> family = SharedFontFamily::Create();
+ family->SetFamily(name);
+ parent.AppendFamily(family);
+ return family.get();
+}
+
+} // namespace
+
+TEST(FontFamilyTest, ToString) {
+ {
+ FontFamily family;
+ EXPECT_EQ("", family.ToString());
+ }
+ {
+ FontFamily family;
+ family.SetFamily("A");
+ CreateAndAppendFamily(family, "B");
+ EXPECT_EQ("A,B", family.ToString());
+ }
+ {
+ FontFamily family;
+ family.SetFamily("A");
+ FontFamily* b_family = CreateAndAppendFamily(family, "B");
+ CreateAndAppendFamily(*b_family, "C");
+ EXPECT_EQ("A,B,C", family.ToString());
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_global_context.cc b/chromium/third_party/blink/renderer/platform/fonts/font_global_context.cc
new file mode 100644
index 00000000000..e90f6d72a80
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_global_context.cc
@@ -0,0 +1,46 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/font_global_context.h"
+
+#include "third_party/blink/renderer/platform/fonts/font_cache.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/thread_specific.h"
+
+namespace blink {
+
+FontGlobalContext* FontGlobalContext::Get(CreateIfNeeded create_if_needed) {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadSpecific<FontGlobalContext*>,
+ font_persistent, ());
+ if (!*font_persistent && create_if_needed == kCreate) {
+ *font_persistent = new FontGlobalContext();
+ }
+ return *font_persistent;
+}
+
+FontGlobalContext::FontGlobalContext()
+ : harfbuzz_font_funcs_(nullptr),
+ default_locale_(nullptr),
+ system_locale_(nullptr),
+ default_locale_for_han_(nullptr),
+ has_default_locale_for_han_(false) {}
+
+void FontGlobalContext::ClearMemory() {
+ if (!Get(kDoNotCreate))
+ return;
+
+ GetFontCache().Invalidate();
+}
+
+void FontGlobalContext::ClearForTesting() {
+ FontGlobalContext* ctx = Get();
+ ctx->default_locale_ = nullptr;
+ ctx->system_locale_ = nullptr;
+ ctx->default_locale_for_han_ = nullptr;
+ ctx->has_default_locale_for_han_ = false;
+ ctx->layout_locale_map_.clear();
+ ctx->font_cache_.Invalidate();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_global_context.h b/chromium/third_party/blink/renderer/platform/fonts/font_global_context.h
new file mode 100644
index 00000000000..256d435dc8e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_global_context.h
@@ -0,0 +1,118 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_GLOBAL_CONTEXT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_GLOBAL_CONTEXT_H_
+
+#include "third_party/blink/renderer/platform/fonts/font_cache.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_font_cache.h"
+#include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h"
+
+struct hb_font_funcs_t;
+
+namespace blink {
+
+class LayoutLocale;
+class FontCache;
+
+using LayoutLocaleMap =
+ HashMap<AtomicString, scoped_refptr<LayoutLocale>, CaseFoldingHash>;
+
+enum CreateIfNeeded { kDoNotCreate, kCreate };
+
+// FontGlobalContext contains non-thread-safe, thread-specific data used for
+// font formatting.
+class PLATFORM_EXPORT FontGlobalContext {
+ WTF_MAKE_NONCOPYABLE(FontGlobalContext);
+
+ public:
+ static FontGlobalContext* Get(CreateIfNeeded = kCreate);
+
+ static inline FontCache& GetFontCache() { return Get()->font_cache_; }
+
+ static inline HarfBuzzFontCache& GetHarfBuzzFontCache() {
+ return Get()->harf_buzz_font_cache_;
+ }
+
+ static hb_font_funcs_t* GetHarfBuzzFontFuncs() {
+ return Get()->harfbuzz_font_funcs_;
+ }
+
+ static void SetHarfBuzzFontFuncs(hb_font_funcs_t* funcs) {
+ Get()->harfbuzz_font_funcs_ = funcs;
+ }
+
+ static inline LayoutLocaleMap& GetLayoutLocaleMap() {
+ return Get()->layout_locale_map_;
+ }
+
+ static inline const LayoutLocale* GetDefaultLayoutLocale() {
+ return Get()->default_locale_;
+ }
+ static inline void SetDefaultLayoutLocale(const LayoutLocale* locale) {
+ Get()->default_locale_ = locale;
+ }
+ static inline const LayoutLocale* GetSystemLayoutLocale() {
+ return Get()->system_locale_;
+ }
+ static inline void SetSystemLayoutLocale(const LayoutLocale* locale) {
+ Get()->system_locale_ = locale;
+ }
+
+ static inline const LayoutLocale* GetDefaultLocaleForHan() {
+ FontGlobalContext* ctx = Get();
+ DCHECK(ctx->has_default_locale_for_han_);
+ return ctx->default_locale_for_han_;
+ }
+ static inline void SetDefaultLocaleForHan(const LayoutLocale* locale) {
+ FontGlobalContext* ctx = Get();
+ ctx->default_locale_for_han_ = locale;
+ ctx->has_default_locale_for_han_ = true;
+ }
+ static inline void InvalidateLocaleForHan() {
+ FontGlobalContext* ctx = Get();
+ ctx->default_locale_for_han_ = nullptr;
+ ctx->has_default_locale_for_han_ = false;
+ }
+ static inline bool HasDefaultLocaleForHan() {
+ return Get()->has_default_locale_for_han_;
+ }
+
+ static inline String& CurrentAcceptLanguages() {
+ return Get()->current_accept_languages_;
+ }
+
+ // Called by MemoryCoordinator to clear memory.
+ static void ClearMemory();
+
+ static void ClearForTesting();
+
+ private:
+ friend class WTF::ThreadSpecific<FontGlobalContext>;
+
+ FontGlobalContext();
+ ~FontGlobalContext() = default;
+
+ FontCache font_cache_;
+
+ HarfBuzzFontCache harf_buzz_font_cache_;
+
+ hb_font_funcs_t* harfbuzz_font_funcs_;
+
+ LayoutLocaleMap layout_locale_map_;
+ const LayoutLocale* default_locale_;
+ const LayoutLocale* system_locale_;
+ const LayoutLocale* default_locale_for_han_;
+ bool has_default_locale_for_han_;
+
+ String current_accept_languages_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_metrics.cc b/chromium/third_party/blink/renderer/platform/fonts/font_metrics.cc
new file mode 100644
index 00000000000..0d431a2eb65
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_metrics.cc
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2005, 2008, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Alexey Proskuryakov
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/fonts/font_metrics.h"
+
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/fonts/font_platform_data.h"
+#include "third_party/blink/renderer/platform/fonts/vdmx_parser.h"
+
+#include <SkPaint.h>
+#include <SkTypeface.h>
+
+namespace blink {
+
+#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_FUCHSIA)
+// This is the largest VDMX table which we'll try to load and parse.
+static const size_t kMaxVDMXTableSize = 1024 * 1024; // 1 MB
+#endif
+
+void FontMetrics::AscentDescentWithHacks(
+ float& ascent,
+ float& descent,
+ unsigned& visual_overflow_inflation_for_ascent,
+ unsigned& visual_overflow_inflation_for_descent,
+ const FontPlatformData& platform_data,
+ const SkPaint& paint,
+ bool subpixel_ascent_descent) {
+ SkTypeface* face = paint.getTypeface();
+ DCHECK(face);
+
+ SkPaint::FontMetrics metrics;
+ paint.getFontMetrics(&metrics);
+
+ int vdmx_ascent = 0, vdmx_descent = 0;
+ bool is_vdmx_valid = false;
+
+#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_FUCHSIA)
+ // Manually digging up VDMX metrics is only applicable when bytecode hinting
+ // using FreeType. With DirectWrite or CoreText, no bytecode hinting is ever
+ // done. This code should be pushed into FreeType (hinted font metrics).
+ static const uint32_t kVdmxTag = SkSetFourByteTag('V', 'D', 'M', 'X');
+ int pixel_size = platform_data.size() + 0.5;
+ if (!paint.isAutohinted() &&
+ (paint.getHinting() == SkPaint::kFull_Hinting ||
+ paint.getHinting() == SkPaint::kNormal_Hinting)) {
+ size_t vdmx_size = face->getTableSize(kVdmxTag);
+ if (vdmx_size && vdmx_size < kMaxVDMXTableSize) {
+ uint8_t* vdmx_table = (uint8_t*)WTF::Partitions::FastMalloc(
+ vdmx_size, WTF_HEAP_PROFILER_TYPE_NAME(FontMetrics));
+ if (vdmx_table &&
+ face->getTableData(kVdmxTag, 0, vdmx_size, vdmx_table) == vdmx_size &&
+ ParseVDMX(&vdmx_ascent, &vdmx_descent, vdmx_table, vdmx_size,
+ pixel_size))
+ is_vdmx_valid = true;
+ WTF::Partitions::FastFree(vdmx_table);
+ }
+ }
+#endif
+
+ // Beware those who step here: This code is designed to match Win32 font
+ // metrics *exactly* except:
+ // - the adjustment of ascent/descent on Linux/Android
+ // - metrics.fAscent and .fDesscent are not rounded to int for tiny fonts
+ if (is_vdmx_valid) {
+ ascent = vdmx_ascent;
+ descent = -vdmx_descent;
+ } else if (subpixel_ascent_descent &&
+ (-metrics.fAscent < 3 ||
+ -metrics.fAscent + metrics.fDescent < 2)) {
+ // For tiny fonts, the rounding of fAscent and fDescent results in equal
+ // baseline for different types of text baselines (crbug.com/338908).
+ // Please see CanvasRenderingContext2D::getFontBaseline for the heuristic.
+ ascent = -metrics.fAscent;
+ descent = metrics.fDescent;
+ } else {
+ ascent = SkScalarRoundToScalar(-metrics.fAscent);
+ descent = SkScalarRoundToScalar(metrics.fDescent);
+
+ if (ascent < -metrics.fAscent)
+ visual_overflow_inflation_for_ascent = 1;
+ if (descent < metrics.fDescent) {
+ visual_overflow_inflation_for_descent = 1;
+#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_FUCHSIA)
+ // When subpixel positioning is enabled, if the descent is rounded down,
+ // the descent part of the glyph may be truncated when displayed in a
+ // 'overflow: hidden' container. To avoid that, borrow 1 unit from the
+ // ascent when possible.
+ if (platform_data.GetFontRenderStyle().use_subpixel_positioning &&
+ ascent >= 1) {
+ ++descent;
+ --ascent;
+ // We should inflate overflow 1 more pixel for ascent instead.
+ visual_overflow_inflation_for_descent = 0;
+ ++(visual_overflow_inflation_for_ascent);
+ }
+#endif
+ }
+ }
+
+#if defined(OS_MACOSX)
+ // We are preserving this ascent hack to match Safari's ascent adjustment
+ // in their SimpleFontDataMac.mm, for details see crbug.com/445830.
+ // We need to adjust Times, Helvetica, and Courier to closely match the
+ // vertical metrics of their Microsoft counterparts that are the de facto
+ // web standard. The AppKit adjustment of 20% is too big and is
+ // incorrectly added to line spacing, so we use a 15% adjustment instead
+ // and add it to the ascent.
+ String family_name = platform_data.FontFamilyName();
+ if (family_name == FontFamilyNames::Times ||
+ family_name == FontFamilyNames::Helvetica ||
+ family_name == FontFamilyNames::Courier)
+ ascent += floorf(((ascent + descent) * 0.15f) + 0.5f);
+#endif
+}
+}
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_metrics.h b/chromium/third_party/blink/renderer/platform/fonts/font_metrics.h
new file mode 100644
index 00000000000..89381754c8f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_metrics.h
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) Research In Motion Limited 2010-2011. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_METRICS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_METRICS_H_
+
+#include "third_party/blink/renderer/platform/fonts/font_baseline.h"
+#include "third_party/blink/renderer/platform/layout_unit.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+#include <SkPaint.h>
+
+namespace blink {
+
+const unsigned kGDefaultUnitsPerEm = 1000;
+class FontPlatformData;
+
+class FontMetrics {
+ DISALLOW_NEW();
+
+ public:
+ FontMetrics()
+ : units_per_em_(kGDefaultUnitsPerEm),
+ ascent_(0),
+ descent_(0),
+ line_gap_(0),
+ line_spacing_(0),
+ x_height_(0),
+ zero_width_(0),
+ underlinethickness_(0),
+ underline_position_(0),
+ ascent_int_(0),
+ descent_int_(0),
+ has_x_height_(false),
+ has_zero_width_(false) {}
+
+ unsigned UnitsPerEm() const { return units_per_em_; }
+ void SetUnitsPerEm(unsigned units_per_em) { units_per_em_ = units_per_em; }
+
+ float FloatAscent(FontBaseline baseline_type = kAlphabeticBaseline) const {
+ if (baseline_type == kAlphabeticBaseline)
+ return ascent_;
+ return FloatHeight() / 2;
+ }
+
+ void SetAscent(float ascent) {
+ ascent_ = ascent;
+ ascent_int_ = lroundf(ascent);
+ }
+
+ float FloatDescent(FontBaseline baseline_type = kAlphabeticBaseline) const {
+ if (baseline_type == kAlphabeticBaseline)
+ return descent_;
+ return FloatHeight() / 2;
+ }
+
+ void SetDescent(float descent) {
+ descent_ = descent;
+ descent_int_ = lroundf(descent);
+ }
+
+ float FloatHeight(FontBaseline baseline_type = kAlphabeticBaseline) const {
+ return FloatAscent() + FloatDescent();
+ }
+
+ float FloatLineGap() const { return line_gap_; }
+ void SetLineGap(float line_gap) { line_gap_ = line_gap; }
+
+ float FloatLineSpacing() const { return line_spacing_; }
+ void SetLineSpacing(float line_spacing) { line_spacing_ = line_spacing; }
+
+ float XHeight() const { return x_height_; }
+ void SetXHeight(float x_height) {
+ x_height_ = x_height;
+ has_x_height_ = true;
+ }
+
+ bool HasXHeight() const { return has_x_height_ && x_height_ > 0; }
+ void SetHasXHeight(bool has_x_height) { has_x_height_ = has_x_height; }
+
+ // Integer variants of certain metrics, used for HTML rendering.
+ int Ascent(FontBaseline baseline_type = kAlphabeticBaseline) const {
+ if (baseline_type == kAlphabeticBaseline)
+ return ascent_int_;
+ return Height() - Height() / 2;
+ }
+
+ int Descent(FontBaseline baseline_type = kAlphabeticBaseline) const {
+ if (baseline_type == kAlphabeticBaseline)
+ return descent_int_;
+ return Height() / 2;
+ }
+
+ int Height(FontBaseline baseline_type = kAlphabeticBaseline) const {
+ return Ascent() + Descent();
+ }
+
+ int LineGap() const { return lroundf(line_gap_); }
+ int LineSpacing() const { return lroundf(line_spacing_); }
+
+ // LayoutUnit variants of certain metrics.
+ // LayoutNG should use LayoutUnit for the block progression metrics.
+ // TODO(kojii): Consider keeping converted values.
+ LayoutUnit FixedAscent(
+ FontBaseline baseline_type = kAlphabeticBaseline) const {
+ return LayoutUnit::FromFloatRound(FloatAscent(baseline_type));
+ }
+
+ LayoutUnit FixedDescent(
+ FontBaseline baseline_type = kAlphabeticBaseline) const {
+ return LayoutUnit::FromFloatRound(FloatDescent(baseline_type));
+ }
+
+ LayoutUnit FixedLineSpacing() const {
+ return LayoutUnit::FromFloatRound(line_spacing_);
+ }
+
+ bool HasIdenticalAscentDescentAndLineGap(const FontMetrics& other) const {
+ return Ascent() == other.Ascent() && Descent() == other.Descent() &&
+ LineGap() == other.LineGap();
+ }
+
+ float ZeroWidth() const { return zero_width_; }
+ void SetZeroWidth(float zero_width) {
+ zero_width_ = zero_width;
+ has_zero_width_ = true;
+ }
+
+ bool HasZeroWidth() const { return has_zero_width_; }
+ void SetHasZeroWidth(bool has_zero_width) {
+ has_zero_width_ = has_zero_width;
+ }
+
+ float UnderlineThickness() const { return underlinethickness_; }
+ void SetUnderlineThickness(float underline_thickness) {
+ underlinethickness_ = underline_thickness;
+ }
+
+ float UnderlinePosition() const { return underline_position_; }
+ void SetUnderlinePosition(float underline_position) {
+ underline_position_ = underline_position;
+ }
+
+ // Unfortunately we still need to keep metrics adjustments for certain Mac
+ // fonts, see crbug.com/445830. Also, a potentially better solution for the
+ // subpixel_ascent_descent flag would be to move line layout to LayoutUnit
+ // instead of int boundaries, see crbug.com/707807 and crbug.com/706298.
+ static void AscentDescentWithHacks(
+ float& ascent,
+ float& descent,
+ unsigned& visual_overflow_inflation_for_ascent,
+ unsigned& visual_overflow_inflation_for_descent,
+ const FontPlatformData&,
+ const SkPaint&,
+ bool subpixel_ascent_descent = false);
+
+ private:
+ friend class SimpleFontData;
+
+ void Reset() {
+ units_per_em_ = kGDefaultUnitsPerEm;
+ ascent_ = 0;
+ descent_ = 0;
+ ascent_int_ = 0;
+ descent_int_ = 0;
+ line_gap_ = 0;
+ line_spacing_ = 0;
+ x_height_ = 0;
+ has_x_height_ = false;
+ underlinethickness_ = 0;
+ underline_position_ = 0;
+ }
+
+ unsigned units_per_em_;
+ float ascent_;
+ float descent_;
+ float line_gap_;
+ float line_spacing_;
+ float x_height_;
+ float zero_width_;
+ float underlinethickness_;
+ float underline_position_;
+ int ascent_int_;
+ int descent_int_;
+ bool has_x_height_;
+ bool has_zero_width_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_METRICS_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_orientation.cc b/chromium/third_party/blink/renderer/platform/fonts/font_orientation.cc
new file mode 100644
index 00000000000..54f9f7a7cf7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_orientation.cc
@@ -0,0 +1,25 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/font_orientation.h"
+
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+String ToString(FontOrientation orientation) {
+ switch (orientation) {
+ case FontOrientation::kHorizontal:
+ return "Horizontal";
+ case FontOrientation::kVerticalRotated:
+ return "VerticalRotated";
+ case FontOrientation::kVerticalMixed:
+ return "VerticalMixed";
+ case FontOrientation::kVerticalUpright:
+ return "VerticalUpright";
+ }
+ return "Unknown";
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_orientation.h b/chromium/third_party/blink/renderer/platform/fonts/font_orientation.h
new file mode 100644
index 00000000000..4e33e90a5a3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_orientation.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2010 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_ORIENTATION_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_ORIENTATION_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/text/character.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+enum class FontOrientation {
+ // Horizontal; i.e., writing-mode: horizontal-tb
+ kHorizontal = 0,
+ // Baseline is vertical but use rotated horizontal typography;
+ // i.e., writing-mode: vertical-*; text-orientation: sideways-*
+ kVerticalRotated = 1,
+ // Vertical with upright CJK and rotated non-CJK;
+ // i.e., writing-mode: vertical-*, text-orientation: mixed
+ kVerticalMixed = 2,
+ // Vertical with all upright;
+ // i.e., writing-mode: vertical-*, text-orientation: upright
+ kVerticalUpright = 3,
+};
+const unsigned kFontOrientationBitCount = 2;
+const unsigned kFontOrientationAnyUprightMask = 2;
+
+inline bool IsVerticalAnyUpright(FontOrientation orientation) {
+ return static_cast<unsigned>(orientation) & kFontOrientationAnyUprightMask;
+}
+inline bool IsVerticalNonCJKUpright(FontOrientation orientation) {
+ return orientation == FontOrientation::kVerticalUpright;
+}
+inline bool IsVerticalUpright(FontOrientation orientation, UChar32 character) {
+ return orientation == FontOrientation::kVerticalUpright ||
+ (orientation == FontOrientation::kVerticalMixed &&
+ Character::IsUprightInMixedVertical(character));
+}
+inline bool IsVerticalBaseline(FontOrientation orientation) {
+ return orientation != FontOrientation::kHorizontal;
+}
+
+inline FontOrientation AdjustOrientationForCharacterInMixedVertical(
+ FontOrientation orientation,
+ UChar32 character) {
+ if (orientation != FontOrientation::kVerticalMixed)
+ return orientation;
+ return Character::IsUprightInMixedVertical(character)
+ ? FontOrientation::kVerticalUpright
+ : FontOrientation::kVerticalRotated;
+}
+
+PLATFORM_EXPORT String ToString(FontOrientation);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_ORIENTATION_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_platform_data.cc b/chromium/third_party/blink/renderer/platform/fonts/font_platform_data.cc
new file mode 100644
index 00000000000..de02c36d6fe
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_platform_data.cc
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2011 Brent Fulgham
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "third_party/blink/renderer/platform/fonts/font_platform_data.h"
+
+#include "SkTypeface.h"
+#include "build/build_config.h"
+#include "hb-ot.h"
+#include "hb.h"
+#include "third_party/blink/public/platform/linux/web_sandbox_support.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/fonts/font_cache.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_face.h"
+#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
+#include "third_party/blink/renderer/platform/layout_test_support.h"
+#include "third_party/blink/renderer/platform/text/character.h"
+#include "third_party/blink/renderer/platform/wtf/byte_swap.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+#if defined(OS_MACOSX)
+#include "third_party/skia/include/ports/SkTypeface_mac.h"
+#endif
+
+namespace blink {
+
+FontPlatformData::FontPlatformData(WTF::HashTableDeletedValueType)
+ : text_size_(0),
+ synthetic_bold_(false),
+ synthetic_italic_(false),
+ avoid_embedded_bitmaps_(false),
+ orientation_(FontOrientation::kHorizontal),
+ is_hash_table_deleted_value_(true)
+#if defined(OS_WIN)
+ ,
+ paint_text_flags_(0)
+#endif
+{
+}
+
+FontPlatformData::FontPlatformData()
+ : text_size_(0),
+ synthetic_bold_(false),
+ synthetic_italic_(false),
+ avoid_embedded_bitmaps_(false),
+ orientation_(FontOrientation::kHorizontal),
+ is_hash_table_deleted_value_(false)
+#if defined(OS_WIN)
+ ,
+ paint_text_flags_(0)
+#endif
+{
+}
+
+FontPlatformData::FontPlatformData(float size,
+ bool synthetic_bold,
+ bool synthetic_italic,
+ FontOrientation orientation)
+ : text_size_(size),
+ synthetic_bold_(synthetic_bold),
+ synthetic_italic_(synthetic_italic),
+ avoid_embedded_bitmaps_(false),
+ orientation_(orientation),
+ is_hash_table_deleted_value_(false)
+#if defined(OS_WIN)
+ ,
+ paint_text_flags_(0)
+#endif
+{
+}
+
+FontPlatformData::FontPlatformData(const FontPlatformData& source)
+ : paint_typeface_(source.paint_typeface_),
+#if !defined(OS_WIN)
+ family_(source.family_),
+#endif
+ text_size_(source.text_size_),
+ synthetic_bold_(source.synthetic_bold_),
+ synthetic_italic_(source.synthetic_italic_),
+ avoid_embedded_bitmaps_(source.avoid_embedded_bitmaps_),
+ orientation_(source.orientation_),
+#if !defined(OS_WIN) && !defined(OS_MACOSX)
+ style_(source.style_),
+#endif
+ harf_buzz_face_(nullptr),
+ is_hash_table_deleted_value_(false)
+#if defined(OS_WIN)
+ ,
+ paint_text_flags_(source.paint_text_flags_)
+#endif
+{
+}
+
+FontPlatformData::FontPlatformData(const FontPlatformData& src, float text_size)
+ : FontPlatformData(src.paint_typeface_,
+#if !defined(OS_WIN)
+ src.family_.data(),
+#else
+ CString(),
+#endif
+ text_size,
+ src.synthetic_bold_,
+ src.synthetic_italic_,
+ src.orientation_) {
+}
+
+FontPlatformData::FontPlatformData(const PaintTypeface& paint_tf,
+ const CString& family,
+ float text_size,
+ bool synthetic_bold,
+ bool synthetic_italic,
+ FontOrientation orientation)
+ : paint_typeface_(paint_tf),
+#if !defined(OS_WIN)
+ family_(family),
+#endif
+ text_size_(text_size),
+ synthetic_bold_(synthetic_bold),
+ synthetic_italic_(synthetic_italic),
+ avoid_embedded_bitmaps_(false),
+ orientation_(orientation),
+ is_hash_table_deleted_value_(false)
+#if defined(OS_WIN)
+ ,
+ paint_text_flags_(0)
+#endif
+{
+#if !defined(OS_WIN) && !defined(OS_MACOSX)
+ style_ = WebFontRenderStyle::GetDefault();
+ auto system_style = QuerySystemRenderStyle(
+ family_, text_size_, paint_typeface_.ToSkTypeface()->fontStyle());
+
+ // In layout tests, ignore system preference for subpixel positioning,
+ // or explicitly disable if requested.
+ if (LayoutTestSupport::IsRunningLayoutTest()) {
+ system_style.use_subpixel_positioning =
+ LayoutTestSupport::IsTextSubpixelPositioningAllowedForTest()
+ ? WebFontRenderStyle::kNoPreference
+ : 0;
+ }
+
+ style_.OverrideWith(system_style);
+#endif
+
+#if defined(OS_WIN)
+ QuerySystemForRenderStyle();
+#endif
+}
+
+FontPlatformData::~FontPlatformData() = default;
+
+#if defined(OS_MACOSX)
+CTFontRef FontPlatformData::CtFont() const {
+ return SkTypeface_GetCTFontRef(paint_typeface_.ToSkTypeface().get());
+};
+
+CGFontRef FontPlatformData::CgFont() const {
+ if (!CtFont())
+ return nullptr;
+ return CTFontCopyGraphicsFont(CtFont(), 0);
+}
+#endif
+
+const FontPlatformData& FontPlatformData::operator=(
+ const FontPlatformData& other) {
+ // Check for self-assignment.
+ if (this == &other)
+ return *this;
+
+ paint_typeface_ = other.paint_typeface_;
+#if !defined(OS_WIN)
+ family_ = other.family_;
+#endif
+ text_size_ = other.text_size_;
+ synthetic_bold_ = other.synthetic_bold_;
+ synthetic_italic_ = other.synthetic_italic_;
+ avoid_embedded_bitmaps_ = other.avoid_embedded_bitmaps_;
+ harf_buzz_face_ = nullptr;
+ orientation_ = other.orientation_;
+#if !defined(OS_WIN) && !defined(OS_MACOSX)
+ style_ = other.style_;
+#endif
+
+#if defined(OS_WIN)
+ paint_text_flags_ = 0;
+#endif
+
+ return *this;
+}
+
+bool FontPlatformData::operator==(const FontPlatformData& a) const {
+ // If either of the typeface pointers are null then we test for pointer
+ // equality. Otherwise, we call SkTypeface::Equal on the valid pointers.
+ bool typefaces_equal = false;
+ if (!Typeface() || !a.Typeface())
+ typefaces_equal = Typeface() == a.Typeface();
+ else
+ typefaces_equal = SkTypeface::Equal(Typeface(), a.Typeface());
+
+ return typefaces_equal && text_size_ == a.text_size_ &&
+ is_hash_table_deleted_value_ == a.is_hash_table_deleted_value_ &&
+ synthetic_bold_ == a.synthetic_bold_ &&
+ synthetic_italic_ == a.synthetic_italic_ &&
+ avoid_embedded_bitmaps_ == a.avoid_embedded_bitmaps_
+#if !defined(OS_WIN) && !defined(OS_MACOSX)
+ && style_ == a.style_
+#endif
+ && orientation_ == a.orientation_;
+}
+
+SkFontID FontPlatformData::UniqueID() const {
+ return Typeface()->uniqueID();
+}
+
+String FontPlatformData::FontFamilyName() const {
+ DCHECK(this->Typeface());
+ SkTypeface::LocalizedStrings* font_family_iterator =
+ this->Typeface()->createFamilyNameIterator();
+ SkTypeface::LocalizedString localized_string;
+ while (font_family_iterator->next(&localized_string) &&
+ !localized_string.fString.size()) {
+ }
+ font_family_iterator->unref();
+ return String::FromUTF8(localized_string.fString.c_str());
+}
+
+SkTypeface* FontPlatformData::Typeface() const {
+ return paint_typeface_.ToSkTypeface().get();
+}
+
+HarfBuzzFace* FontPlatformData::GetHarfBuzzFace() const {
+ if (!harf_buzz_face_)
+ harf_buzz_face_ =
+ HarfBuzzFace::Create(const_cast<FontPlatformData*>(this), UniqueID());
+
+ return harf_buzz_face_.get();
+}
+
+bool FontPlatformData::HasSpaceInLigaturesOrKerning(
+ TypesettingFeatures features) const {
+ HarfBuzzFace* hb_face = GetHarfBuzzFace();
+ if (!hb_face)
+ return false;
+
+ return hb_face->HasSpaceInLigaturesOrKerning(features);
+}
+
+unsigned FontPlatformData::GetHash() const {
+ unsigned h = SkTypeface::UniqueID(Typeface());
+ h ^= 0x01010101 * ((static_cast<int>(is_hash_table_deleted_value_) << 3) |
+ (static_cast<int>(orientation_) << 2) |
+ (static_cast<int>(synthetic_bold_) << 1) |
+ static_cast<int>(synthetic_italic_));
+
+ // This memcpy is to avoid a reinterpret_cast that breaks strict-aliasing
+ // rules. Memcpy is generally optimized enough so that performance doesn't
+ // matter here.
+ uint32_t text_size_bytes;
+ memcpy(&text_size_bytes, &text_size_, sizeof(uint32_t));
+ h ^= text_size_bytes;
+
+ return h;
+}
+
+#if !defined(OS_MACOSX)
+bool FontPlatformData::FontContainsCharacter(UChar32 character) {
+ PaintFont font;
+ SetupPaintFont(&font);
+ font.SetTextEncoding(SkPaint::kUTF32_TextEncoding);
+
+ uint16_t glyph;
+ font.ToSkPaint().textToGlyphs(&character, sizeof(character), &glyph);
+ return glyph;
+}
+#endif
+
+#if !defined(OS_MACOSX) && !defined(OS_WIN)
+// static
+WebFontRenderStyle FontPlatformData::QuerySystemRenderStyle(
+ const CString& family,
+ float text_size,
+ SkFontStyle font_style) {
+ WebFontRenderStyle result;
+
+#if !defined(OS_ANDROID)
+ // If the font name is missing (i.e. probably a web font) or the sandbox is
+ // disabled, use the system defaults.
+ if (family.length() && Platform::Current()->GetSandboxSupport()) {
+ bool is_bold = font_style.weight() >= SkFontStyle::kSemiBold_Weight;
+ bool is_italic = font_style.slant() != SkFontStyle::kUpright_Slant;
+ const int size_and_style = (((int)text_size) << 2) | (((int)is_bold) << 1) |
+ (((int)is_italic) << 0);
+ Platform::Current()->GetSandboxSupport()->GetWebFontRenderStyleForStrike(
+ family.data(), size_and_style, &result);
+ }
+#endif
+
+ return result;
+}
+
+void FontPlatformData::SetupPaintFont(PaintFont* font,
+ float device_scale_factor,
+ const Font*) const {
+ style_.ApplyToPaintFont(*font, device_scale_factor);
+
+ const float ts = text_size_ >= 0 ? text_size_ : 12;
+ font->SetTextSize(SkFloatToScalar(ts));
+ font->SetTypeface(paint_typeface_);
+ font->SetFakeBoldText(synthetic_bold_);
+ font->SetTextSkewX(synthetic_italic_ ? -SK_Scalar1 / 4 : 0);
+
+ font->SetEmbeddedBitmapText(!avoid_embedded_bitmaps_);
+}
+#endif
+
+const PaintTypeface& FontPlatformData::GetPaintTypeface() const {
+ return paint_typeface_;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_platform_data.h b/chromium/third_party/blink/renderer/platform/fonts/font_platform_data.h
new file mode 100644
index 00000000000..dd5762f82a8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_platform_data.h
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2006, 2007, 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_PLATFORM_DATA_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_PLATFORM_DATA_H_
+
+#include "SkPaint.h"
+#include "SkTypeface.h"
+#include "base/memory/scoped_refptr.h"
+#include "build/build_config.h"
+#include "third_party/blink/public/platform/web_font_render_style.h"
+#include "third_party/blink/renderer/platform/fonts/font_description.h"
+#include "third_party/blink/renderer/platform/fonts/font_orientation.h"
+#include "third_party/blink/renderer/platform/fonts/small_caps_iterator.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_font.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_typeface.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/hash_table_deleted_value_type.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_impl.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+#if defined(OS_MACOSX)
+OBJC_CLASS NSFont;
+
+typedef struct CGFont* CGFontRef;
+typedef const struct __CTFont* CTFontRef;
+
+#include <objc/objc-auto.h>
+
+inline CTFontRef toCTFontRef(NSFont* nsFont) {
+ return reinterpret_cast<CTFontRef>(nsFont);
+}
+inline NSFont* toNSFont(CTFontRef ctFontRef) {
+ return const_cast<NSFont*>(reinterpret_cast<const NSFont*>(ctFontRef));
+}
+#endif // defined(OS_MACOSX)
+
+class SkTypeface;
+typedef uint32_t SkFontID;
+
+namespace blink {
+
+class Font;
+class HarfBuzzFace;
+class FontVariationSettings;
+
+class PLATFORM_EXPORT FontPlatformData {
+ USING_FAST_MALLOC(FontPlatformData);
+
+ public:
+ // Used for deleted values in the font cache's hash tables. The hash table
+ // will create us with this structure, and it will compare other values
+ // to this "Deleted" one. It expects the Deleted one to be differentiable
+ // from the 0 one (created with the empty constructor), so we can't just
+ // set everything to 0.
+ FontPlatformData(WTF::HashTableDeletedValueType);
+ FontPlatformData();
+ FontPlatformData(const FontPlatformData&);
+ FontPlatformData(float size,
+ bool synthetic_bold,
+ bool synthetic_italic,
+ FontOrientation = FontOrientation::kHorizontal);
+ FontPlatformData(const FontPlatformData& src, float text_size);
+#if defined(OS_MACOSX)
+ FontPlatformData(NSFont*,
+ float size,
+ bool synthetic_bold,
+ bool synthetic_italic,
+ FontOrientation,
+ FontVariationSettings*);
+#endif
+ FontPlatformData(const PaintTypeface&,
+ const CString& name,
+ float text_size,
+ bool synthetic_bold,
+ bool synthetic_italic,
+ FontOrientation = FontOrientation::kHorizontal);
+ ~FontPlatformData();
+
+#if defined(OS_MACOSX)
+ // These methods return a nullptr for FreeType backed SkTypefaces, compare
+ // FontCustomPlatformData, which are used for variable fonts on Mac OS <
+ // 10.12. They should not return nullptr otherwise. So they allow
+ // distinguishing which backend the SkTypeface is using.
+ CTFontRef CtFont() const;
+ CGFontRef CgFont() const;
+#endif
+
+ String FontFamilyName() const;
+ float size() const { return text_size_; }
+ bool SyntheticBold() const { return synthetic_bold_; }
+ bool SyntheticItalic() const { return synthetic_italic_; }
+
+ SkTypeface* Typeface() const;
+ HarfBuzzFace* GetHarfBuzzFace() const;
+ bool HasSpaceInLigaturesOrKerning(TypesettingFeatures) const;
+ SkFontID UniqueID() const;
+ unsigned GetHash() const;
+
+ FontOrientation Orientation() const { return orientation_; }
+ bool IsVerticalAnyUpright() const {
+ return blink::IsVerticalAnyUpright(orientation_);
+ }
+ void SetOrientation(FontOrientation orientation) {
+ orientation_ = orientation;
+ }
+ void SetSyntheticBold(bool synthetic_bold) {
+ synthetic_bold_ = synthetic_bold;
+ }
+ void SetSyntheticItalic(bool synthetic_italic) {
+ synthetic_italic_ = synthetic_italic;
+ }
+ void SetAvoidEmbeddedBitmaps(bool embedded_bitmaps) {
+ avoid_embedded_bitmaps_ = embedded_bitmaps;
+ }
+ bool operator==(const FontPlatformData&) const;
+ const FontPlatformData& operator=(const FontPlatformData&);
+
+ bool IsHashTableDeletedValue() const { return is_hash_table_deleted_value_; }
+ bool FontContainsCharacter(UChar32 character);
+
+#if !defined(OS_WIN) && !defined(OS_MACOSX)
+ const WebFontRenderStyle& GetFontRenderStyle() const { return style_; }
+#endif
+
+ void SetupPaintFont(PaintFont*,
+ float device_scale_factor = 1,
+ const Font* = nullptr) const;
+ const PaintTypeface& GetPaintTypeface() const;
+
+#if defined(OS_WIN)
+ int PaintTextFlags() const { return paint_text_flags_; }
+#endif
+
+ private:
+#if !defined(OS_WIN) && !defined(OS_MACOSX)
+ WebFontRenderStyle QuerySystemRenderStyle(const CString& family,
+ float text_size,
+ SkFontStyle);
+#endif
+#if defined(OS_WIN)
+ // TODO(https://crbug.com/808221): Remove and use QuerySystemRenderStyle()
+ // instead.
+ void QuerySystemForRenderStyle();
+#endif
+
+ PaintTypeface paint_typeface_;
+#if !defined(OS_WIN)
+ CString family_;
+#endif
+
+ public:
+ float text_size_;
+ bool synthetic_bold_;
+ bool synthetic_italic_;
+ bool avoid_embedded_bitmaps_;
+ FontOrientation orientation_;
+
+ private:
+#if !defined(OS_WIN) && !defined(OS_MACOSX)
+ WebFontRenderStyle style_;
+#endif
+
+ mutable scoped_refptr<HarfBuzzFace> harf_buzz_face_;
+ bool is_hash_table_deleted_value_;
+#if defined(OS_WIN)
+ // TODO(https://crbug.com/808221): Replace |paint_text_flags_| with |style_|.
+ int paint_text_flags_;
+#endif
+};
+
+} // namespace blink
+
+#endif // ifdef FontPlatformData_h
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_platform_data_test.cc b/chromium/third_party/blink/renderer/platform/fonts/font_platform_data_test.cc
new file mode 100644
index 00000000000..a4ae3ba4914
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_platform_data_test.cc
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2015 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 "third_party/blink/renderer/platform/fonts/font.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/fonts/typesetting_features.h"
+#include "third_party/blink/renderer/platform/testing/font_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
+
+using blink::test::CreateTestFont;
+
+namespace blink {
+
+TEST(FontPlatformDataTest, AhemHasNoSpaceInLigaturesOrKerning) {
+ Font font =
+ CreateTestFont("Ahem", test::PlatformTestDataPath("Ahem.woff"), 16);
+ const FontPlatformData& platform_data = font.PrimaryFont()->PlatformData();
+ TypesettingFeatures features = kKerning | kLigatures;
+
+ EXPECT_FALSE(platform_data.HasSpaceInLigaturesOrKerning(features));
+}
+
+TEST(FontPlatformDataTest, AhemSpaceLigatureHasSpaceInLigaturesOrKerning) {
+ Font font =
+ CreateTestFont("AhemSpaceLigature",
+ test::PlatformTestDataPath("AhemSpaceLigature.woff"), 16);
+ const FontPlatformData& platform_data = font.PrimaryFont()->PlatformData();
+ TypesettingFeatures features = kKerning | kLigatures;
+
+ EXPECT_TRUE(platform_data.HasSpaceInLigaturesOrKerning(features));
+}
+
+TEST(FontPlatformDataTest, AhemSpaceLigatureHasNoSpaceWithoutFontFeatures) {
+ Font font =
+ CreateTestFont("AhemSpaceLigature",
+ test::PlatformTestDataPath("AhemSpaceLigature.woff"), 16);
+ const FontPlatformData& platform_data = font.PrimaryFont()->PlatformData();
+ TypesettingFeatures features = 0;
+
+ EXPECT_FALSE(platform_data.HasSpaceInLigaturesOrKerning(features));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_selection_algorithm.cc b/chromium/third_party/blink/renderer/platform/fonts/font_selection_algorithm.cc
new file mode 100644
index 00000000000..0ac9d52d066
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_selection_algorithm.cc
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2017 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/fonts/font_selection_algorithm.h"
+
+namespace blink {
+
+auto FontSelectionAlgorithm::StretchDistance(
+ FontSelectionCapabilities capabilities) const -> DistanceResult {
+ auto width = capabilities.width;
+ DCHECK(width.IsValid());
+ if (width.Includes(request_.width))
+ return {FontSelectionValue(), request_.width};
+
+ if (request_.width > NormalWidthValue()) {
+ if (width.minimum > request_.width)
+ return {width.minimum - request_.width, width.minimum};
+ DCHECK(width.maximum < request_.width);
+ auto threshold =
+ std::max(request_.width, capabilities_bounds_.width.maximum);
+ return {threshold - width.maximum, width.maximum};
+ }
+
+ if (width.maximum < request_.width)
+ return {request_.width - width.maximum, width.maximum};
+ DCHECK(width.minimum > request_.width);
+ auto threshold = std::min(request_.width, capabilities_bounds_.width.minimum);
+ return {width.minimum - threshold, width.minimum};
+}
+
+auto FontSelectionAlgorithm::StyleDistance(
+ FontSelectionCapabilities capabilities) const -> DistanceResult {
+ auto slope = capabilities.slope;
+ DCHECK(slope.IsValid());
+ if (slope.Includes(request_.slope))
+ return {FontSelectionValue(), request_.slope};
+
+ if (request_.slope >= ItalicThreshold()) {
+ if (slope.minimum > request_.slope)
+ return {slope.minimum - request_.slope, slope.minimum};
+ DCHECK(request_.slope > slope.maximum);
+ auto threshold =
+ std::max(request_.slope, capabilities_bounds_.slope.maximum);
+ return {threshold - slope.maximum, slope.maximum};
+ }
+
+ if (request_.slope >= FontSelectionValue()) {
+ if (slope.maximum >= FontSelectionValue() && slope.maximum < request_.slope)
+ return {request_.slope - slope.maximum, slope.maximum};
+ if (slope.minimum > request_.slope)
+ return {slope.minimum, slope.minimum};
+ DCHECK(slope.maximum < FontSelectionValue());
+ auto threshold =
+ std::max(request_.slope, capabilities_bounds_.slope.maximum);
+ return {threshold - slope.maximum, slope.maximum};
+ }
+
+ if (request_.slope > -ItalicThreshold()) {
+ if (slope.minimum > request_.slope && slope.minimum <= FontSelectionValue())
+ return {slope.minimum - request_.slope, slope.minimum};
+ if (slope.maximum < request_.slope)
+ return {-slope.maximum, slope.maximum};
+ DCHECK(slope.minimum > FontSelectionValue());
+ auto threshold =
+ std::min(request_.slope, capabilities_bounds_.slope.minimum);
+ return {slope.minimum - threshold, slope.minimum};
+ }
+
+ if (slope.maximum < request_.slope)
+ return {request_.slope - slope.maximum, slope.maximum};
+ DCHECK(slope.minimum > request_.slope);
+ auto threshold = std::min(request_.slope, capabilities_bounds_.slope.minimum);
+ return {slope.minimum - threshold, slope.minimum};
+}
+
+auto FontSelectionAlgorithm::WeightDistance(
+ FontSelectionCapabilities capabilities) const -> DistanceResult {
+ auto weight = capabilities.weight;
+ DCHECK(weight.IsValid());
+ if (weight.Includes(request_.weight))
+ return {FontSelectionValue(), request_.weight};
+
+ if (request_.weight >= LowerWeightSearchThreshold() &&
+ request_.weight <= UpperWeightSearchThreshold()) {
+ if (weight.minimum > request_.weight &&
+ weight.minimum <= UpperWeightSearchThreshold())
+ return {weight.minimum - request_.weight, weight.minimum};
+ if (weight.maximum < request_.weight)
+ return {UpperWeightSearchThreshold() - weight.maximum, weight.maximum};
+ DCHECK(weight.minimum > UpperWeightSearchThreshold());
+ auto threshold =
+ std::min(request_.weight, capabilities_bounds_.weight.minimum);
+ return {weight.minimum - threshold, weight.minimum};
+ }
+
+ if (request_.weight < LowerWeightSearchThreshold()) {
+ if (weight.maximum < request_.weight)
+ return {request_.weight - weight.maximum, weight.maximum};
+ DCHECK(weight.minimum > request_.weight);
+ auto threshold =
+ std::min(request_.weight, capabilities_bounds_.weight.minimum);
+ return {weight.minimum - threshold, weight.minimum};
+ }
+
+ DCHECK(request_.weight >= UpperWeightSearchThreshold());
+ if (weight.minimum > request_.weight)
+ return {weight.minimum - request_.weight, weight.minimum};
+ DCHECK(weight.maximum < request_.weight);
+ auto threshold =
+ std::max(request_.weight, capabilities_bounds_.weight.maximum);
+ return {threshold - weight.maximum, weight.maximum};
+}
+
+bool FontSelectionAlgorithm::IsBetterMatchForRequest(
+ const FontSelectionCapabilities& firstCapabilities,
+ const FontSelectionCapabilities& secondCapabilities) {
+ auto stretchDistanceFirst = StretchDistance(firstCapabilities).distance;
+ auto stretchDistanceSecond = StretchDistance(secondCapabilities).distance;
+ if (stretchDistanceFirst < stretchDistanceSecond)
+ return true;
+ if (stretchDistanceFirst > stretchDistanceSecond)
+ return false;
+
+ auto styleDistanceFirst = StyleDistance(firstCapabilities).distance;
+ auto styleDistanceSecond = StyleDistance(secondCapabilities).distance;
+ if (styleDistanceFirst < styleDistanceSecond)
+ return true;
+ if (styleDistanceFirst > styleDistanceSecond)
+ return false;
+
+ auto weightDistanceFirst = WeightDistance(firstCapabilities).distance;
+ auto weightDistanceSecond = WeightDistance(secondCapabilities).distance;
+ if (weightDistanceFirst < weightDistanceSecond)
+ return true;
+ return false;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_selection_algorithm.h b/chromium/third_party/blink/renderer/platform/fonts/font_selection_algorithm.h
new file mode 100644
index 00000000000..83dedcb6bb1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_selection_algorithm.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_SELECTION_ALGORITHM_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_SELECTION_ALGORITHM_H_
+
+#include "third_party/blink/renderer/platform/fonts/font_selection_types.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT FontSelectionAlgorithm {
+ public:
+ FontSelectionAlgorithm() = delete;
+
+ FontSelectionAlgorithm(FontSelectionRequest request,
+ const FontSelectionCapabilities& capabilities_bounds)
+ : request_(request), capabilities_bounds_(capabilities_bounds) {}
+
+ bool IsBetterMatchForRequest(
+ const FontSelectionCapabilities& firstCapabilities,
+ const FontSelectionCapabilities& secondCapabilities);
+
+ struct DistanceResult {
+ FontSelectionValue distance;
+ FontSelectionValue value;
+ };
+
+ DistanceResult StretchDistance(FontSelectionCapabilities) const;
+ DistanceResult StyleDistance(FontSelectionCapabilities) const;
+ DistanceResult WeightDistance(FontSelectionCapabilities) const;
+
+ private:
+ FontSelectionRequest request_;
+ FontSelectionCapabilities capabilities_bounds_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_SELECTION_ALGORITHM_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_selection_types.cc b/chromium/third_party/blink/renderer/platform/fonts/font_selection_types.cc
new file mode 100644
index 00000000000..fc182594396
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_selection_types.cc
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2017 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/fonts/font_selection_types.h"
+
+#include "third_party/blink/renderer/platform/wtf/string_hasher.h"
+
+namespace {
+
+class IntegerHasher {
+ public:
+ void add(unsigned integer) {
+ m_underlyingHasher.AddCharactersAssumingAligned(integer, integer >> 16);
+ }
+
+ unsigned hash() const { return m_underlyingHasher.GetHash(); }
+
+ private:
+ StringHasher m_underlyingHasher;
+};
+
+} // namespace
+
+namespace blink {
+
+unsigned FontSelectionRequest::GetHash() const {
+ IntegerHasher hasher;
+ hasher.add(weight.RawValue());
+ hasher.add(width.RawValue());
+ hasher.add(slope.RawValue());
+ return hasher.hash();
+}
+
+unsigned FontSelectionRequestKeyHash::GetHash(
+ const FontSelectionRequestKey& key) {
+ IntegerHasher hasher;
+ hasher.add(key.request.GetHash());
+ hasher.add(key.isDeletedValue);
+ return hasher.hash();
+}
+
+unsigned FontSelectionCapabilitiesHash::GetHash(
+ const FontSelectionCapabilities& key) {
+ IntegerHasher hasher;
+ hasher.add(key.width.UniqueValue());
+ hasher.add(key.slope.UniqueValue());
+ hasher.add(key.weight.UniqueValue());
+ hasher.add(key.IsHashTableDeletedValue());
+ return hasher.hash();
+}
+
+String FontSelectionValue::ToString() const {
+ return String::Format("%f", (float)*this);
+}
+
+String FontSelectionRequest::ToString() const {
+ return String::Format(
+ "weight=%s, width=%s, slope=%s", weight.ToString().Ascii().data(),
+ width.ToString().Ascii().data(), slope.ToString().Ascii().data());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_selection_types.h b/chromium/third_party/blink/renderer/platform/fonts/font_selection_types.h
new file mode 100644
index 00000000000..5fb1723e2fe
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_selection_types.h
@@ -0,0 +1,484 @@
+/*
+ * Copyright (C) 2017 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_SELECTION_TYPES_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_SELECTION_TYPES_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/hash_table_deleted_value_type.h"
+#include "third_party/blink/renderer/platform/wtf/hash_traits.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+// Unclamped, unchecked, signed fixed-point number representing a value used for
+// font variations. Sixteen bits in total, one sign bit, two fractional bits,
+// means the smallest positive representable value is 0.25, the maximum
+// representable value is 8191.75, and the minimum representable value is -8192.
+class PLATFORM_EXPORT FontSelectionValue {
+ public:
+ FontSelectionValue() = default;
+
+ // Explicit because it is lossy.
+ explicit FontSelectionValue(int x) : backing_(x * fractionalEntropy) {}
+
+ // Explicit because it is lossy.
+ explicit FontSelectionValue(float x) : backing_(x * fractionalEntropy) {}
+
+ // Explicit because it is lossy.
+ explicit FontSelectionValue(double x) : backing_(x * fractionalEntropy) {}
+
+ operator float() const {
+ // floats have 23 fractional bits, but only 14 fractional bits are
+ // necessary, so every value can be represented losslessly.
+ return backing_ / static_cast<float>(fractionalEntropy);
+ }
+
+ FontSelectionValue operator+(const FontSelectionValue other) const;
+ FontSelectionValue operator-(const FontSelectionValue other) const;
+ FontSelectionValue operator*(const FontSelectionValue other) const;
+ FontSelectionValue operator/(const FontSelectionValue other) const;
+ FontSelectionValue operator-() const;
+ bool operator==(const FontSelectionValue other) const;
+ bool operator!=(const FontSelectionValue other) const;
+ bool operator<(const FontSelectionValue other) const;
+ bool operator<=(const FontSelectionValue other) const;
+ bool operator>(const FontSelectionValue other) const;
+ bool operator>=(const FontSelectionValue other) const;
+
+ int16_t RawValue() const { return backing_; }
+
+ String ToString() const;
+
+ static const FontSelectionValue& MaximumValue() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(
+ const FontSelectionValue, maximumValue,
+ (std::numeric_limits<int16_t>::max(), RawTag::RawTag));
+ return maximumValue;
+ }
+
+ static const FontSelectionValue& MinimumValue() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(
+ const FontSelectionValue, minimumValue,
+ (std::numeric_limits<int16_t>::min(), RawTag::RawTag));
+ return minimumValue;
+ }
+
+ protected:
+ enum class RawTag { RawTag };
+
+ FontSelectionValue(int16_t rawValue, RawTag) : backing_(rawValue) {}
+
+ private:
+ static constexpr int fractionalEntropy = 4;
+ // TODO(drott) crbug.com/745910 - Consider making this backed by a checked
+ // arithmetic type.
+ int16_t backing_{0};
+};
+
+inline FontSelectionValue FontSelectionValue::operator+(
+ const FontSelectionValue other) const {
+ return FontSelectionValue(backing_ + other.backing_, RawTag::RawTag);
+}
+
+inline FontSelectionValue FontSelectionValue::operator-(
+ const FontSelectionValue other) const {
+ return FontSelectionValue(backing_ - other.backing_, RawTag::RawTag);
+}
+
+inline FontSelectionValue FontSelectionValue::operator*(
+ const FontSelectionValue other) const {
+ return FontSelectionValue(
+ static_cast<int32_t>(backing_) * other.backing_ / fractionalEntropy,
+ RawTag::RawTag);
+}
+
+inline FontSelectionValue FontSelectionValue::operator/(
+ const FontSelectionValue other) const {
+ return FontSelectionValue(
+ static_cast<int32_t>(backing_) / other.backing_ * fractionalEntropy,
+ RawTag::RawTag);
+}
+
+inline FontSelectionValue FontSelectionValue::operator-() const {
+ return FontSelectionValue(-backing_, RawTag::RawTag);
+}
+
+inline bool FontSelectionValue::operator==(
+ const FontSelectionValue other) const {
+ return backing_ == other.backing_;
+}
+
+inline bool FontSelectionValue::operator!=(
+ const FontSelectionValue other) const {
+ return !operator==(other);
+}
+
+inline bool FontSelectionValue::operator<(
+ const FontSelectionValue other) const {
+ return backing_ < other.backing_;
+}
+
+inline bool FontSelectionValue::operator<=(
+ const FontSelectionValue other) const {
+ return backing_ <= other.backing_;
+}
+
+inline bool FontSelectionValue::operator>(
+ const FontSelectionValue other) const {
+ return backing_ > other.backing_;
+}
+
+inline bool FontSelectionValue::operator>=(
+ const FontSelectionValue other) const {
+ return backing_ >= other.backing_;
+}
+
+static inline const FontSelectionValue& ItalicThreshold() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(const FontSelectionValue, italicThreshold,
+ (20));
+ return italicThreshold;
+}
+
+static inline bool isItalic(FontSelectionValue fontWeight) {
+ return fontWeight >= ItalicThreshold();
+}
+
+static inline const FontSelectionValue& FontSelectionZeroValue() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(const FontSelectionValue,
+ fontSelectionZeroValue, (0));
+ return fontSelectionZeroValue;
+}
+
+static inline const FontSelectionValue& NormalSlopeValue() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(const FontSelectionValue, normalSlopeValue,
+ ());
+ return normalSlopeValue;
+}
+
+static inline const FontSelectionValue& ItalicSlopeValue() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(const FontSelectionValue, italicValue, (20));
+ return italicValue;
+}
+
+static inline const FontSelectionValue& BoldThreshold() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(const FontSelectionValue, boldThreshold,
+ (600));
+ return boldThreshold;
+}
+
+static inline const FontSelectionValue& MinWeightValue() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(const FontSelectionValue, minWeightValue,
+ (1));
+ return minWeightValue;
+}
+
+static inline const FontSelectionValue& MaxWeightValue() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(const FontSelectionValue, maxWeightValue,
+ (1000));
+ return maxWeightValue;
+}
+
+static inline const FontSelectionValue& BoldWeightValue() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(const FontSelectionValue, boldWeightValue,
+ (700));
+ return boldWeightValue;
+}
+
+static inline const FontSelectionValue& NormalWeightValue() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(const FontSelectionValue, normalWeightValue,
+ (400));
+ return normalWeightValue;
+}
+
+static inline const FontSelectionValue& LightWeightValue() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(const FontSelectionValue, lightWeightValue,
+ (200));
+ return lightWeightValue;
+}
+
+static inline bool isFontWeightBold(FontSelectionValue fontWeight) {
+ return fontWeight >= BoldThreshold();
+}
+
+static inline const FontSelectionValue& UpperWeightSearchThreshold() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(const FontSelectionValue,
+ upperWeightSearchThreshold, (500));
+ return upperWeightSearchThreshold;
+}
+
+static inline const FontSelectionValue& LowerWeightSearchThreshold() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(const FontSelectionValue,
+ lowerWeightSearchThreshold, (400));
+ return lowerWeightSearchThreshold;
+}
+
+static inline const FontSelectionValue& UltraCondensedWidthValue() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(const FontSelectionValue,
+ ultraCondensedWidthValue, (50));
+ return ultraCondensedWidthValue;
+}
+
+static inline const FontSelectionValue& ExtraCondensedWidthValue() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(const FontSelectionValue,
+ extraCondensedWidthValue, (62.5f));
+ return extraCondensedWidthValue;
+}
+
+static inline const FontSelectionValue& CondensedWidthValue() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(const FontSelectionValue, condensedWidthValue,
+ (75));
+ return condensedWidthValue;
+}
+
+static inline const FontSelectionValue& SemiCondensedWidthValue() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(const FontSelectionValue,
+ semiCondensedWidthValue, (87.5f));
+ return semiCondensedWidthValue;
+}
+
+static inline const FontSelectionValue& NormalWidthValue() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(const FontSelectionValue, normalWidthValue,
+ (100.0f));
+ return normalWidthValue;
+}
+
+static inline const FontSelectionValue& SemiExpandedWidthValue() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(const FontSelectionValue,
+ semiExpandedWidthValue, (112.5f));
+ return semiExpandedWidthValue;
+}
+
+static inline const FontSelectionValue& ExpandedWidthValue() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(const FontSelectionValue, expandedWidthValue,
+ (125));
+ return expandedWidthValue;
+}
+
+static inline const FontSelectionValue& ExtraExpandedWidthValue() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(const FontSelectionValue,
+ extraExpandedWidthValue, (150));
+ return extraExpandedWidthValue;
+}
+
+static inline const FontSelectionValue& UltraExpandedWidthValue() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(const FontSelectionValue,
+ ultraExpandedWidthValue, (200));
+ return ultraExpandedWidthValue;
+}
+
+struct FontSelectionRange {
+ FontSelectionRange(FontSelectionValue single_value)
+ : minimum(single_value), maximum(single_value) {}
+
+ FontSelectionRange(FontSelectionValue minimum, FontSelectionValue maximum)
+ : minimum(minimum), maximum(maximum) {}
+
+ bool operator==(const FontSelectionRange& other) const {
+ return minimum == other.minimum && maximum == other.maximum;
+ }
+
+ bool IsValid() const { return minimum <= maximum; }
+
+ bool IsRange() const { return maximum > minimum; }
+
+ void Expand(const FontSelectionRange& other) {
+ DCHECK(other.IsValid());
+ if (!IsValid()) {
+ *this = other;
+ } else {
+ minimum = std::min(minimum, other.minimum);
+ maximum = std::max(maximum, other.maximum);
+ }
+ DCHECK(IsValid());
+ }
+
+ bool Includes(FontSelectionValue target) const {
+ return target >= minimum && target <= maximum;
+ }
+
+ uint32_t UniqueValue() const {
+ return minimum.RawValue() << 16 | maximum.RawValue();
+ }
+
+ FontSelectionValue clampToRange(FontSelectionValue selection_value) const {
+ if (selection_value < minimum)
+ return minimum;
+ if (selection_value > maximum)
+ return maximum;
+ return selection_value;
+ }
+
+ FontSelectionValue minimum{FontSelectionValue(1)};
+ FontSelectionValue maximum{FontSelectionValue(0)};
+};
+
+struct PLATFORM_EXPORT FontSelectionRequest {
+ FontSelectionRequest() = default;
+
+ FontSelectionRequest(FontSelectionValue weight,
+ FontSelectionValue width,
+ FontSelectionValue slope)
+ : weight(weight), width(width), slope(slope) {}
+
+ unsigned GetHash() const;
+
+ bool operator==(const FontSelectionRequest& other) const {
+ return weight == other.weight && width == other.width &&
+ slope == other.slope;
+ }
+
+ bool operator!=(const FontSelectionRequest& other) const {
+ return !operator==(other);
+ }
+
+ String ToString() const;
+
+ FontSelectionValue weight;
+ FontSelectionValue width;
+ FontSelectionValue slope;
+};
+
+// Only used for HashMaps. We don't want to put the bool into
+// FontSelectionRequest because FontSelectionRequest needs to be as small as
+// possible because it's inside every FontDescription.
+struct FontSelectionRequestKey {
+ FontSelectionRequestKey() = default;
+
+ FontSelectionRequestKey(FontSelectionRequest request) : request(request) {}
+
+ explicit FontSelectionRequestKey(WTF::HashTableDeletedValueType)
+ : isDeletedValue(true) {}
+
+ bool IsHashTableDeletedValue() const { return isDeletedValue; }
+
+ bool operator==(const FontSelectionRequestKey& other) const {
+ return request == other.request && isDeletedValue == other.isDeletedValue;
+ }
+
+ FontSelectionRequest request;
+ bool isDeletedValue{false};
+};
+
+struct PLATFORM_EXPORT FontSelectionRequestKeyHash {
+ static unsigned GetHash(const FontSelectionRequestKey&);
+
+ static bool Equal(const FontSelectionRequestKey& a,
+ const FontSelectionRequestKey& b) {
+ return a == b;
+ }
+
+ static const bool safe_to_compare_to_empty_or_deleted = true;
+};
+
+struct FontSelectionCapabilities {
+ FontSelectionCapabilities() = default;
+
+ FontSelectionCapabilities(FontSelectionRange width,
+ FontSelectionRange slope,
+ FontSelectionRange weight)
+ : width(width), slope(slope), weight(weight), is_deleted_value_(false) {}
+
+ FontSelectionCapabilities(WTF::HashTableDeletedValueType)
+ : is_deleted_value_(true) {}
+
+ bool IsHashTableDeletedValue() const { return is_deleted_value_; }
+
+ void Expand(const FontSelectionCapabilities& capabilities) {
+ width.Expand(capabilities.width);
+ slope.Expand(capabilities.slope);
+ weight.Expand(capabilities.weight);
+ }
+
+ bool IsValid() const {
+ return width.IsValid() && slope.IsValid() && weight.IsValid() &&
+ !is_deleted_value_;
+ }
+
+ bool HasRange() const {
+ return width.IsRange() || slope.IsRange() || weight.IsRange();
+ }
+
+ bool operator==(const FontSelectionCapabilities& other) const {
+ return width == other.width && slope == other.slope &&
+ weight == other.weight &&
+ is_deleted_value_ == other.is_deleted_value_;
+ }
+
+ bool operator!=(const FontSelectionCapabilities& other) const {
+ return !(*this == other);
+ }
+
+ FontSelectionRange width{FontSelectionZeroValue(), FontSelectionZeroValue()};
+ FontSelectionRange slope{FontSelectionZeroValue(), FontSelectionZeroValue()};
+ FontSelectionRange weight{FontSelectionZeroValue(), FontSelectionZeroValue()};
+ bool is_deleted_value_{false};
+};
+
+struct PLATFORM_EXPORT FontSelectionCapabilitiesHash {
+ static unsigned GetHash(const FontSelectionCapabilities& key);
+
+ static bool Equal(const FontSelectionCapabilities& a,
+ const FontSelectionCapabilities& b) {
+ return a == b;
+ }
+
+ static const bool safe_to_compare_to_empty_or_deleted = true;
+};
+
+} // namespace blink
+
+namespace WTF {
+
+template <>
+struct DefaultHash<blink::FontSelectionCapabilities> {
+ STATIC_ONLY(DefaultHash);
+ typedef blink::FontSelectionCapabilitiesHash Hash;
+};
+
+template <>
+struct HashTraits<blink::FontSelectionCapabilities>
+ : SimpleClassHashTraits<blink::FontSelectionCapabilities> {
+ STATIC_ONLY(HashTraits);
+};
+
+} // namespace WTF
+
+// Used for clampTo for example in StyleBuilderConverter
+template <>
+inline blink::FontSelectionValue
+defaultMinimumForClamp<blink::FontSelectionValue>() {
+ return blink::FontSelectionValue::MinimumValue();
+}
+
+template <>
+inline blink::FontSelectionValue
+defaultMaximumForClamp<blink::FontSelectionValue>() {
+ return blink::FontSelectionValue::MaximumValue();
+}
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_selection_types_test.cc b/chromium/third_party/blink/renderer/platform/fonts/font_selection_types_test.cc
new file mode 100644
index 00000000000..dd41e1fde4b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_selection_types_test.cc
@@ -0,0 +1,55 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/font_selection_types.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+
+namespace blink {
+
+TEST(FontSelectionTypesTest, HashCollisions) {
+ std::vector<int> weights = {100, 200, 300, 400, 500, 600, 700, 800, 900};
+ std::vector<float> slopes = {-90, -67.5, -30, -20, -10, 0,
+ 10, 20, 30, 67.5, 90};
+ std::vector<float> widths = {50, 67.5, 75, 100, 125, 150, 167.5, 175, 200};
+
+ HashSet<unsigned> hashes;
+ for (auto weight : weights) {
+ for (auto slope : slopes) {
+ for (auto width : widths) {
+ FontSelectionRequest request = FontSelectionRequest(
+ FontSelectionValue(weight), FontSelectionValue(width),
+ FontSelectionValue(slope));
+ ASSERT_FALSE(hashes.Contains(request.GetHash()));
+ ASSERT_TRUE(hashes.insert(request.GetHash()).is_new_entry);
+ }
+ }
+ }
+ ASSERT_EQ(hashes.size(), weights.size() * slopes.size() * widths.size());
+}
+
+TEST(FontSelectionTypesTest, ValueToString) {
+ {
+ FontSelectionValue value(42);
+ EXPECT_EQ("42.000000", value.ToString());
+ }
+ {
+ FontSelectionValue value(42.81f);
+ EXPECT_EQ("42.750000", value.ToString());
+ }
+ {
+ FontSelectionValue value(42.923456789123456789);
+ EXPECT_EQ("42.750000", value.ToString());
+ }
+}
+
+TEST(FontSelectionTypesTest, RequestToString) {
+ FontSelectionRequest request(FontSelectionValue(42), FontSelectionValue(43),
+ FontSelectionValue(44));
+ EXPECT_EQ("weight=42.000000, width=43.000000, slope=44.000000",
+ request.ToString());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_selector.cc b/chromium/third_party/blink/renderer/platform/fonts/font_selector.cc
new file mode 100644
index 00000000000..90bb107acbc
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_selector.cc
@@ -0,0 +1,50 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/font_selector.h"
+
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/fonts/font_cache.h"
+#include "third_party/blink/renderer/platform/fonts/font_description.h"
+#include "third_party/blink/renderer/platform/fonts/generic_font_family_settings.h"
+
+namespace blink {
+
+AtomicString FontSelector::FamilyNameFromSettings(
+ const GenericFontFamilySettings& settings,
+ const FontDescription& font_description,
+ const AtomicString& generic_family_name) {
+#if defined(OS_ANDROID)
+ if (font_description.GenericFamily() == FontDescription::kStandardFamily) {
+ return FontCache::GetGenericFamilyNameForScript(
+ FontFamilyNames::webkit_standard, font_description);
+ }
+
+ if (generic_family_name.StartsWith("-webkit-")) {
+ return FontCache::GetGenericFamilyNameForScript(generic_family_name,
+ font_description);
+ }
+#else
+ UScriptCode script = font_description.GetScript();
+ if (font_description.GenericFamily() == FontDescription::kStandardFamily)
+ return settings.Standard(script);
+ if (generic_family_name == FontFamilyNames::webkit_serif)
+ return settings.Serif(script);
+ if (generic_family_name == FontFamilyNames::webkit_sans_serif)
+ return settings.SansSerif(script);
+ if (generic_family_name == FontFamilyNames::webkit_cursive)
+ return settings.Cursive(script);
+ if (generic_family_name == FontFamilyNames::webkit_fantasy)
+ return settings.Fantasy(script);
+ if (generic_family_name == FontFamilyNames::webkit_monospace)
+ return settings.Fixed(script);
+ if (generic_family_name == FontFamilyNames::webkit_pictograph)
+ return settings.Pictograph(script);
+ if (generic_family_name == FontFamilyNames::webkit_standard)
+ return settings.Standard(script);
+#endif
+ return g_empty_atom;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_selector.h b/chromium/third_party/blink/renderer/platform/fonts/font_selector.h
new file mode 100644
index 00000000000..66afc15d79c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_selector.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2007, 2008 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_SELECTOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_SELECTOR_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/fonts/font_cache_client.h"
+#include "third_party/blink/renderer/platform/fonts/segmented_font_data.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+
+namespace blink {
+
+class ExecutionContext;
+class FontData;
+class FontDescription;
+class FontFaceCache;
+class FontSelectorClient;
+class GenericFontFamilySettings;
+
+class PLATFORM_EXPORT FontSelector : public FontCacheClient {
+ public:
+ virtual ~FontSelector() = default;
+ virtual scoped_refptr<FontData> GetFontData(const FontDescription&,
+ const AtomicString& family_name) = 0;
+
+ // TODO crbug.com/542629 - The String variant of this method shouldbe replaced
+ // with a better approach, now that we only have complex text.
+ virtual void WillUseFontData(const FontDescription&,
+ const AtomicString& family_name,
+ const String& text) = 0;
+ virtual void WillUseRange(const FontDescription&,
+ const AtomicString& family_name,
+ const FontDataForRangeSet&) = 0;
+
+ virtual unsigned Version() const = 0;
+
+ virtual void ReportNotDefGlyph() const = 0;
+
+ virtual void RegisterForInvalidationCallbacks(FontSelectorClient*) = 0;
+ virtual void UnregisterForInvalidationCallbacks(FontSelectorClient*) = 0;
+
+ virtual void FontFaceInvalidated(){};
+
+ virtual ExecutionContext* GetExecutionContext() const = 0;
+
+ virtual FontFaceCache* GetFontFaceCache() = 0;
+
+ virtual bool IsPlatformFamilyMatchAvailable(
+ const FontDescription&,
+ const AtomicString& passed_family) = 0;
+
+ protected:
+ static AtomicString FamilyNameFromSettings(
+ const GenericFontFamilySettings&,
+ const FontDescription&,
+ const AtomicString& generic_family_name);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_SELECTOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_selector_client.h b/chromium/third_party/blink/renderer/platform/fonts/font_selector_client.h
new file mode 100644
index 00000000000..259cf82d71f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_selector_client.h
@@ -0,0 +1,25 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_SELECTOR_CLIENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_SELECTOR_CLIENT_H_
+
+#include "third_party/blink/renderer/platform/heap/handle.h"
+
+namespace blink {
+
+class FontSelector;
+
+class FontSelectorClient : public GarbageCollectedMixin {
+ public:
+ virtual ~FontSelectorClient() = default;
+
+ virtual void FontsNeedUpdate(FontSelector*) = 0;
+
+ virtual void Trace(blink::Visitor* visitor) {}
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_SELECTOR_CLIENT_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_smoothing_mode.cc b/chromium/third_party/blink/renderer/platform/fonts/font_smoothing_mode.cc
new file mode 100644
index 00000000000..581814f0e5d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_smoothing_mode.cc
@@ -0,0 +1,25 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/font_smoothing_mode.h"
+
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+String ToString(FontSmoothingMode mode) {
+ switch (mode) {
+ case kAutoSmoothing:
+ return "Auto";
+ case kNoSmoothing:
+ return "None";
+ case kAntialiased:
+ return "Antialiased";
+ case kSubpixelAntialiased:
+ return "SubpixelAntialiased";
+ }
+ return "Unknown";
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_smoothing_mode.h b/chromium/third_party/blink/renderer/platform/fonts/font_smoothing_mode.h
new file mode 100644
index 00000000000..e56d307697c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_smoothing_mode.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2009 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_SMOOTHING_MODE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_SMOOTHING_MODE_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+enum FontSmoothingMode {
+ kAutoSmoothing,
+ kNoSmoothing,
+ kAntialiased,
+ kSubpixelAntialiased
+};
+
+PLATFORM_EXPORT String ToString(FontSmoothingMode);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_SMOOTHING_MODE_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_test.cc b/chromium/third_party/blink/renderer/platform/fonts/font_test.cc
new file mode 100644
index 00000000000..69f39f0dc31
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_test.cc
@@ -0,0 +1,50 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/font.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
+#include "third_party/blink/renderer/platform/testing/font_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
+#include "third_party/blink/renderer/platform/text/text_run.h"
+
+using blink::test::CreateTestFont;
+
+namespace blink {
+
+TEST(FontTest, TextIntercepts) {
+ Font font =
+ CreateTestFont("Ahem", test::PlatformTestDataPath("Ahem.woff"), 16);
+ // A sequence of LATIN CAPITAL LETTER E WITH ACUTE and LATIN SMALL LETTER P
+ // characters. E ACUTES are squares above the baseline in Ahem, while p's
+ // are rectangles below the baseline.
+ UChar ahem_above_below_baseline_string[] = {0xc9, 0x70, 0xc9, 0x70, 0xc9,
+ 0x70, 0xc9, 0x70, 0xc9};
+ TextRun ahem_above_below_baseline(ahem_above_below_baseline_string, 9);
+ TextRunPaintInfo text_run_paint_info(ahem_above_below_baseline);
+ PaintFlags default_paint;
+ float device_scale_factor = 1;
+
+ std::tuple<float, float> below_baseline_bounds = std::make_tuple(2, 4);
+ Vector<Font::TextIntercept> text_intercepts;
+ // 4 intercept ranges for below baseline p glyphs in the test string
+ font.GetTextIntercepts(text_run_paint_info, device_scale_factor,
+ default_paint, below_baseline_bounds, text_intercepts);
+ EXPECT_EQ(text_intercepts.size(), 4u);
+ for (auto text_intercept : text_intercepts) {
+ EXPECT_GT(text_intercept.end_, text_intercept.begin_);
+ }
+
+ std::tuple<float, float> above_baseline_bounds = std::make_tuple(-4, -2);
+ // 5 intercept ranges for the above baseline E ACUTE glyphs
+ font.GetTextIntercepts(text_run_paint_info, device_scale_factor,
+ default_paint, above_baseline_bounds, text_intercepts);
+ EXPECT_EQ(text_intercepts.size(), 5u);
+ for (auto text_intercept : text_intercepts) {
+ EXPECT_GT(text_intercept.end_, text_intercept.begin_);
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_test_utilities.cc b/chromium/third_party/blink/renderer/platform/fonts/font_test_utilities.cc
new file mode 100644
index 00000000000..8d481d06e39
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_test_utilities.cc
@@ -0,0 +1,14 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/font_test_utilities.h"
+
+namespace blink {
+
+String To16Bit(const char* text, unsigned length) {
+ return String::Make16BitFrom8BitSource(reinterpret_cast<const LChar*>(text),
+ length);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_test_utilities.h b/chromium/third_party/blink/renderer/platform/fonts/font_test_utilities.h
new file mode 100644
index 00000000000..3336c3e4a27
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_test_utilities.h
@@ -0,0 +1,14 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_TEST_UTILITIES_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_TEST_UTILITIES_H_
+
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+String To16Bit(const char* text, unsigned length);
+}
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_TEST_UTILITIES_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_variant_east_asian.cc b/chromium/third_party/blink/renderer/platform/fonts/font_variant_east_asian.cc
new file mode 100644
index 00000000000..635cdb6e724
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_variant_east_asian.cc
@@ -0,0 +1,51 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/font_variant_east_asian.h"
+
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+static const char* kUnknownEastAsianString = "Unknown";
+
+String FontVariantEastAsian::ToString(EastAsianForm form) {
+ switch (form) {
+ case EastAsianForm::kNormalForm:
+ return "Normal";
+ case EastAsianForm::kJis78:
+ return "Jis78";
+ case EastAsianForm::kJis83:
+ return "Jis83";
+ case EastAsianForm::kJis90:
+ return "Jis90";
+ case EastAsianForm::kJis04:
+ return "Jis04";
+ case EastAsianForm::kSimplified:
+ return "Simplified";
+ case EastAsianForm::kTraditional:
+ return "Traditional";
+ }
+ return kUnknownEastAsianString;
+}
+
+String FontVariantEastAsian::ToString(EastAsianWidth width) {
+ switch (width) {
+ case FontVariantEastAsian::kNormalWidth:
+ return "Normal";
+ case FontVariantEastAsian::kFullWidth:
+ return "Full";
+ case FontVariantEastAsian::kProportionalWidth:
+ return "Proportional";
+ }
+ return kUnknownEastAsianString;
+}
+
+String FontVariantEastAsian::ToString() const {
+ return String::Format(
+ "form=%s, width=%s, ruby=%s", ToString(Form()).Ascii().data(),
+ ToString(Width()).Ascii().data(), Ruby() ? "true" : "false");
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_variant_east_asian.h b/chromium/third_party/blink/renderer/platform/fonts/font_variant_east_asian.h
new file mode 100644
index 00000000000..0a93008f9ac
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_variant_east_asian.h
@@ -0,0 +1,85 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_VARIANT_EAST_ASIAN_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_VARIANT_EAST_ASIAN_H_
+
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+class FontVariantEastAsian {
+ STACK_ALLOCATED();
+
+ public:
+ enum EastAsianForm {
+ kNormalForm,
+ kJis78,
+ kJis83,
+ kJis90,
+ kJis04,
+ kSimplified,
+ kTraditional
+ // Ensure |BitFields| has enough bits when adding values.
+ };
+ static String ToString(EastAsianForm);
+
+ enum EastAsianWidth {
+ kNormalWidth,
+ kFullWidth,
+ kProportionalWidth
+ // Ensure |BitFields| has enough bits when adding values.
+ };
+ static String ToString(EastAsianWidth);
+
+ FontVariantEastAsian() : fields_as_unsigned_(0) {}
+
+ static FontVariantEastAsian InitializeFromUnsigned(unsigned init_value) {
+ return FontVariantEastAsian(init_value);
+ }
+
+ EastAsianForm Form() const {
+ return static_cast<EastAsianForm>(fields_.form_);
+ }
+ EastAsianWidth Width() const {
+ return static_cast<EastAsianWidth>(fields_.width_);
+ }
+ bool Ruby() const { return fields_.ruby_; }
+
+ void SetForm(EastAsianForm form) { fields_.form_ = form; };
+ void SetWidth(EastAsianWidth width) { fields_.width_ = width; };
+ void SetRuby(bool ruby) { fields_.ruby_ = ruby; };
+
+ bool IsAllNormal() const { return !fields_as_unsigned_; }
+
+ bool operator==(const FontVariantEastAsian& other) const {
+ return fields_as_unsigned_ == other.fields_as_unsigned_;
+ }
+
+ String ToString() const;
+
+ private:
+ FontVariantEastAsian(unsigned init_value) : fields_as_unsigned_(init_value) {}
+
+ struct BitFields {
+ unsigned form_ : 3;
+ unsigned width_ : 2;
+ unsigned ruby_ : 1;
+ // Ensure |FontDescription| has enough bits when adding values.
+ };
+
+ union {
+ BitFields fields_;
+ unsigned fields_as_unsigned_;
+ };
+ static_assert(sizeof(BitFields) == sizeof(unsigned),
+ "Mapped union types must match in size.");
+
+ // Used in setVariant to store the value in m_fields.m_variantNumeric;
+ friend class FontDescription;
+};
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_VARIANT_EAST_ASIAN_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_variant_numeric.cc b/chromium/third_party/blink/renderer/platform/fonts/font_variant_numeric.cc
new file mode 100644
index 00000000000..179d2d2fc29
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_variant_numeric.cc
@@ -0,0 +1,80 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/font_variant_numeric.h"
+
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+static const char* kUnknownNumericString = "Unknown";
+
+String FontVariantNumeric::ToString(NumericFigure figure) {
+ switch (figure) {
+ case kNormalFigure:
+ return "NormalFigure";
+ case kLiningNums:
+ return "LiningNums";
+ case kOldstyleNums:
+ return "OldstyleNums";
+ }
+ return kUnknownNumericString;
+}
+
+String FontVariantNumeric::ToString(NumericSpacing spacing) {
+ switch (spacing) {
+ case kNormalSpacing:
+ return "NormalSpacing";
+ case kProportionalNums:
+ return "ProportionalNums";
+ case kTabularNums:
+ return "TabularNums";
+ }
+ return kUnknownNumericString;
+}
+
+String FontVariantNumeric::ToString(NumericFraction fraction) {
+ switch (fraction) {
+ case kNormalFraction:
+ return "Normal";
+ case kDiagonalFractions:
+ return "Diagonal";
+ case kStackedFractions:
+ return "Stacked";
+ }
+ return kUnknownNumericString;
+}
+
+String FontVariantNumeric::ToString(Ordinal ordinal) {
+ switch (ordinal) {
+ case kOrdinalOff:
+ return "Off";
+ case kOrdinalOn:
+ return "On";
+ }
+ return kUnknownNumericString;
+}
+
+String FontVariantNumeric::ToString(SlashedZero slashed) {
+ switch (slashed) {
+ case kSlashedZeroOff:
+ return "Off";
+ case kSlashedZeroOn:
+ return "On";
+ }
+ return kUnknownNumericString;
+}
+
+String FontVariantNumeric::ToString() const {
+ return String::Format(
+ "numeric_figure=%s, numeric_spacing=%s, numeric_fraction=%s, ordinal=%s, "
+ "slashed_zero=%s",
+ ToString(NumericFigureValue()).Ascii().data(),
+ ToString(NumericSpacingValue()).Ascii().data(),
+ ToString(NumericFractionValue()).Ascii().data(),
+ ToString(OrdinalValue()).Ascii().data(),
+ ToString(SlashedZeroValue()).Ascii().data());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_variant_numeric.h b/chromium/third_party/blink/renderer/platform/fonts/font_variant_numeric.h
new file mode 100644
index 00000000000..c3b3a24664a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_variant_numeric.h
@@ -0,0 +1,103 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_VARIANT_NUMERIC_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_VARIANT_NUMERIC_H_
+
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+class FontVariantNumeric {
+ STACK_ALLOCATED();
+
+ public:
+ enum NumericFigure { kNormalFigure = 0, kLiningNums, kOldstyleNums };
+ static String ToString(NumericFigure);
+
+ enum NumericSpacing { kNormalSpacing = 0, kProportionalNums, kTabularNums };
+ static String ToString(NumericSpacing);
+
+ enum NumericFraction {
+ kNormalFraction = 0,
+ kDiagonalFractions,
+ kStackedFractions
+ };
+ static String ToString(NumericFraction);
+
+ enum Ordinal { kOrdinalOff = 0, kOrdinalOn };
+ static String ToString(Ordinal);
+
+ enum SlashedZero { kSlashedZeroOff = 0, kSlashedZeroOn };
+ static String ToString(SlashedZero);
+
+ FontVariantNumeric() : fields_as_unsigned_(0) {}
+
+ static FontVariantNumeric InitializeFromUnsigned(unsigned init_value) {
+ return FontVariantNumeric(init_value);
+ }
+
+ void SetNumericFigure(NumericFigure figure) {
+ fields_.numeric_figure_ = figure;
+ };
+ void SetNumericSpacing(NumericSpacing spacing) {
+ fields_.numeric_spacing_ = spacing;
+ };
+ void SetNumericFraction(NumericFraction fraction) {
+ fields_.numeric_fraction_ = fraction;
+ };
+ void SetOrdinal(Ordinal ordinal) { fields_.ordinal_ = ordinal; };
+ void SetSlashedZero(SlashedZero slashed_zero) {
+ fields_.slashed_zero_ = slashed_zero;
+ };
+
+ NumericFigure NumericFigureValue() const {
+ return static_cast<NumericFigure>(fields_.numeric_figure_);
+ }
+ NumericSpacing NumericSpacingValue() const {
+ return static_cast<NumericSpacing>(fields_.numeric_spacing_);
+ }
+ NumericFraction NumericFractionValue() const {
+ return static_cast<NumericFraction>(fields_.numeric_fraction_);
+ }
+ Ordinal OrdinalValue() const {
+ return static_cast<Ordinal>(fields_.ordinal_);
+ };
+ SlashedZero SlashedZeroValue() const {
+ return static_cast<SlashedZero>(fields_.slashed_zero_);
+ }
+
+ bool IsAllNormal() { return !fields_as_unsigned_; }
+
+ bool operator==(const FontVariantNumeric& other) const {
+ return fields_as_unsigned_ == other.fields_as_unsigned_;
+ }
+
+ String ToString() const;
+
+ private:
+ FontVariantNumeric(unsigned init_value) : fields_as_unsigned_(init_value) {}
+
+ struct BitFields {
+ unsigned numeric_figure_ : 2;
+ unsigned numeric_spacing_ : 2;
+ unsigned numeric_fraction_ : 2;
+ unsigned ordinal_ : 1;
+ unsigned slashed_zero_ : 1;
+ };
+
+ union {
+ BitFields fields_;
+ unsigned fields_as_unsigned_;
+ };
+ static_assert(sizeof(BitFields) == sizeof(unsigned),
+ "Mapped union types must match in size.");
+
+ // Used in SetVariant to store the value in m_fields.m_variantNumeric;
+ friend class FontDescription;
+};
+}
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_VARIANT_NUMERIC_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_vertical_position_type.h b/chromium/third_party/blink/renderer/platform/fonts/font_vertical_position_type.h
new file mode 100644
index 00000000000..7e05ad49e8c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_vertical_position_type.h
@@ -0,0 +1,25 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_VERTICAL_POSITION_TYPE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_VERTICAL_POSITION_TYPE_H_
+
+namespace blink {
+
+enum class FontVerticalPositionType {
+ // TextTop and TextBottom are the top/bottom of the content area.
+ // This is where 'vertical-align: text-top/text-bottom' aligns to.
+ // This is explicitly undefined in CSS2.
+ // https://drafts.csswg.org/css2/visudet.html#inline-non-replaced
+ TextTop,
+ TextBottom,
+ // Em height as being discussed in Font Metrics API.
+ // https://drafts.css-houdini.org/font-metrics-api-1/#fontmetrics
+ TopOfEmHeight,
+ BottomOfEmHeight
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_VERTICAL_POSITION_TYPE_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_width_variant.cc b/chromium/third_party/blink/renderer/platform/fonts/font_width_variant.cc
new file mode 100644
index 00000000000..eb78885b1ff
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_width_variant.cc
@@ -0,0 +1,25 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/font_width_variant.h"
+
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+String ToString(FontWidthVariant variant) {
+ switch (variant) {
+ case FontWidthVariant::kRegularWidth:
+ return "Regular";
+ case FontWidthVariant::kHalfWidth:
+ return "Half";
+ case FontWidthVariant::kThirdWidth:
+ return "Third";
+ case FontWidthVariant::kQuarterWidth:
+ return "Quarter";
+ }
+ return "Unknown";
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_width_variant.h b/chromium/third_party/blink/renderer/platform/fonts/font_width_variant.h
new file mode 100644
index 00000000000..06e8e50ca52
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_width_variant.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2011 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_WIDTH_VARIANT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_WIDTH_VARIANT_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+enum FontWidthVariant {
+ kRegularWidth,
+ kHalfWidth,
+ kThirdWidth,
+ kQuarterWidth,
+ kLastFontWidthVariant = kQuarterWidth
+};
+
+const unsigned kFontWidthVariantWidth = 2;
+
+static_assert(kLastFontWidthVariant >> kFontWidthVariantWidth == 0,
+ "FontWidthVariantWidth must be correct");
+
+PLATFORM_EXPORT String ToString(FontWidthVariant);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_WIDTH_VARIANT_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/fuchsia/font_cache_fuchsia.cc b/chromium/third_party/blink/renderer/platform/fonts/fuchsia/font_cache_fuchsia.cc
new file mode 100644
index 00000000000..3bd6b1cd061
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/fuchsia/font_cache_fuchsia.cc
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/fonts/font_cache.h"
+
+#include "third_party/blink/renderer/platform/fonts/font_platform_data.h"
+
+namespace blink {
+
+static AtomicString& MutableSystemFontFamily() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(AtomicString, system_font_family, ());
+ return system_font_family;
+}
+
+// static
+const AtomicString& FontCache::SystemFontFamily() {
+ return MutableSystemFontFamily();
+}
+
+// static
+void FontCache::SetSystemFontFamily(const AtomicString& family_name) {
+ DCHECK(!family_name.IsEmpty());
+ MutableSystemFontFamily() = family_name;
+}
+
+scoped_refptr<SimpleFontData> FontCache::PlatformFallbackFontForCharacter(
+ const FontDescription& font_description,
+ UChar32 c,
+ const SimpleFontData* font_data_to_substitute,
+ FontFallbackPriority fallback_priority) {
+ sk_sp<SkFontMgr> font_mgr(SkFontMgr::RefDefault());
+ AtomicString family_name = GetFamilyNameForCharacter(
+ font_mgr.get(), c, font_description, fallback_priority);
+ if (family_name.IsEmpty())
+ return GetLastResortFallbackFont(font_description, kDoNotRetain);
+ return FontDataFromFontPlatformData(
+ GetFontPlatformData(font_description,
+ FontFaceCreationParams(family_name)),
+ kDoNotRetain);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/generic_font_family_settings.cc b/chromium/third_party/blink/renderer/platform/fonts/generic_font_family_settings.cc
new file mode 100644
index 00000000000..3816c98c0b4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/generic_font_family_settings.cc
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/renderer/platform/fonts/generic_font_family_settings.h"
+
+#include <memory>
+#include "third_party/blink/renderer/platform/fonts/font_cache.h"
+
+namespace blink {
+
+GenericFontFamilySettings::GenericFontFamilySettings(
+ const GenericFontFamilySettings& other)
+ : standard_font_family_map_(other.standard_font_family_map_),
+ serif_font_family_map_(other.serif_font_family_map_),
+ fixed_font_family_map_(other.fixed_font_family_map_),
+ sans_serif_font_family_map_(other.sans_serif_font_family_map_),
+ cursive_font_family_map_(other.cursive_font_family_map_),
+ fantasy_font_family_map_(other.fantasy_font_family_map_),
+ pictograph_font_family_map_(other.pictograph_font_family_map_) {}
+
+void GenericFontFamilySettings::IsolatedCopyTo(
+ GenericFontFamilySettings& dest) const {
+ DCHECK(!IsIsolated());
+ auto copy_to_vector = [](const ScriptFontFamilyMap& map,
+ IsolatedCopyVector& vector) {
+ for (const auto& kv : map)
+ vector.emplace_back(kv.key, kv.value.GetString().IsolatedCopy());
+ };
+
+ dest.isolated_copy_ = std::make_unique<IsolatedCopyVector[]>(7);
+ copy_to_vector(standard_font_family_map_, dest.isolated_copy_[0]);
+ copy_to_vector(serif_font_family_map_, dest.isolated_copy_[1]);
+ copy_to_vector(fixed_font_family_map_, dest.isolated_copy_[2]);
+ copy_to_vector(sans_serif_font_family_map_, dest.isolated_copy_[3]);
+ copy_to_vector(cursive_font_family_map_, dest.isolated_copy_[4]);
+ copy_to_vector(fantasy_font_family_map_, dest.isolated_copy_[5]);
+ copy_to_vector(pictograph_font_family_map_, dest.isolated_copy_[6]);
+}
+
+void GenericFontFamilySettings::MakeAtomic() {
+ DCHECK(IsIsolated());
+ auto copy_from_vector = [](ScriptFontFamilyMap& map,
+ const IsolatedCopyVector& vector) {
+ for (const auto& kv : vector)
+ map.insert(kv.first, AtomicString(kv.second));
+ };
+ copy_from_vector(standard_font_family_map_, isolated_copy_[0]);
+ copy_from_vector(serif_font_family_map_, isolated_copy_[1]);
+ copy_from_vector(fixed_font_family_map_, isolated_copy_[2]);
+ copy_from_vector(sans_serif_font_family_map_, isolated_copy_[3]);
+ copy_from_vector(cursive_font_family_map_, isolated_copy_[4]);
+ copy_from_vector(fantasy_font_family_map_, isolated_copy_[5]);
+ copy_from_vector(pictograph_font_family_map_, isolated_copy_[6]);
+ isolated_copy_.reset();
+}
+
+GenericFontFamilySettings& GenericFontFamilySettings::operator=(
+ const GenericFontFamilySettings& other) {
+ standard_font_family_map_ = other.standard_font_family_map_;
+ serif_font_family_map_ = other.serif_font_family_map_;
+ fixed_font_family_map_ = other.fixed_font_family_map_;
+ sans_serif_font_family_map_ = other.sans_serif_font_family_map_;
+ cursive_font_family_map_ = other.cursive_font_family_map_;
+ fantasy_font_family_map_ = other.fantasy_font_family_map_;
+ pictograph_font_family_map_ = other.pictograph_font_family_map_;
+ return *this;
+}
+
+// Sets the entry in the font map for the given script. If family is the empty
+// string, removes the entry instead.
+void GenericFontFamilySettings::SetGenericFontFamilyMap(
+ ScriptFontFamilyMap& font_map,
+ const AtomicString& family,
+ UScriptCode script) {
+ ScriptFontFamilyMap::iterator it = font_map.find(static_cast<int>(script));
+ if (family.IsEmpty()) {
+ if (it == font_map.end())
+ return;
+ font_map.erase(it);
+ } else if (it != font_map.end() && it->value == family) {
+ return;
+ } else {
+ font_map.Set(static_cast<int>(script), family);
+ }
+}
+
+const AtomicString& GenericFontFamilySettings::GenericFontFamilyForScript(
+ const ScriptFontFamilyMap& font_map,
+ UScriptCode script) const {
+ ScriptFontFamilyMap::iterator it =
+ const_cast<ScriptFontFamilyMap&>(font_map).find(static_cast<int>(script));
+ if (it != font_map.end()) {
+ // Replace with the first available font if it starts with ",".
+ if (!it->value.IsEmpty() && it->value[0] == ',')
+ it->value = AtomicString(FontCache::FirstAvailableOrFirst(it->value));
+ return it->value;
+ }
+ if (script != USCRIPT_COMMON)
+ return GenericFontFamilyForScript(font_map, USCRIPT_COMMON);
+ return g_empty_atom;
+}
+
+const AtomicString& GenericFontFamilySettings::Standard(
+ UScriptCode script) const {
+ return GenericFontFamilyForScript(standard_font_family_map_, script);
+}
+
+bool GenericFontFamilySettings::UpdateStandard(const AtomicString& family,
+ UScriptCode script) {
+ if (family == Standard())
+ return false;
+ SetGenericFontFamilyMap(standard_font_family_map_, family, script);
+ return true;
+}
+
+const AtomicString& GenericFontFamilySettings::Fixed(UScriptCode script) const {
+ return GenericFontFamilyForScript(fixed_font_family_map_, script);
+}
+
+bool GenericFontFamilySettings::UpdateFixed(const AtomicString& family,
+ UScriptCode script) {
+ if (family == Fixed())
+ return false;
+ SetGenericFontFamilyMap(fixed_font_family_map_, family, script);
+ return true;
+}
+
+const AtomicString& GenericFontFamilySettings::Serif(UScriptCode script) const {
+ return GenericFontFamilyForScript(serif_font_family_map_, script);
+}
+
+bool GenericFontFamilySettings::UpdateSerif(const AtomicString& family,
+ UScriptCode script) {
+ if (family == Serif())
+ return false;
+ SetGenericFontFamilyMap(serif_font_family_map_, family, script);
+ return true;
+}
+
+const AtomicString& GenericFontFamilySettings::SansSerif(
+ UScriptCode script) const {
+ return GenericFontFamilyForScript(sans_serif_font_family_map_, script);
+}
+
+bool GenericFontFamilySettings::UpdateSansSerif(const AtomicString& family,
+ UScriptCode script) {
+ if (family == SansSerif())
+ return false;
+ SetGenericFontFamilyMap(sans_serif_font_family_map_, family, script);
+ return true;
+}
+
+const AtomicString& GenericFontFamilySettings::Cursive(
+ UScriptCode script) const {
+ return GenericFontFamilyForScript(cursive_font_family_map_, script);
+}
+
+bool GenericFontFamilySettings::UpdateCursive(const AtomicString& family,
+ UScriptCode script) {
+ if (family == Cursive())
+ return false;
+ SetGenericFontFamilyMap(cursive_font_family_map_, family, script);
+ return true;
+}
+
+const AtomicString& GenericFontFamilySettings::Fantasy(
+ UScriptCode script) const {
+ return GenericFontFamilyForScript(fantasy_font_family_map_, script);
+}
+
+bool GenericFontFamilySettings::UpdateFantasy(const AtomicString& family,
+ UScriptCode script) {
+ if (family == Fantasy())
+ return false;
+ SetGenericFontFamilyMap(fantasy_font_family_map_, family, script);
+ return true;
+}
+
+const AtomicString& GenericFontFamilySettings::Pictograph(
+ UScriptCode script) const {
+ return GenericFontFamilyForScript(pictograph_font_family_map_, script);
+}
+
+bool GenericFontFamilySettings::UpdatePictograph(const AtomicString& family,
+ UScriptCode script) {
+ if (family == Pictograph())
+ return false;
+ SetGenericFontFamilyMap(pictograph_font_family_map_, family, script);
+ return true;
+}
+
+void GenericFontFamilySettings::Reset() {
+ standard_font_family_map_.clear();
+ serif_font_family_map_.clear();
+ fixed_font_family_map_.clear();
+ sans_serif_font_family_map_.clear();
+ cursive_font_family_map_.clear();
+ fantasy_font_family_map_.clear();
+ pictograph_font_family_map_.clear();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/generic_font_family_settings.h b/chromium/third_party/blink/renderer/platform/fonts/generic_font_family_settings.h
new file mode 100644
index 00000000000..f5166531429
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/generic_font_family_settings.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_GENERIC_FONT_FAMILY_SETTINGS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_GENERIC_FONT_FAMILY_SETTINGS_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+#include <unicode/uscript.h>
+
+namespace blink {
+
+class PLATFORM_EXPORT GenericFontFamilySettings {
+ DISALLOW_NEW();
+
+ public:
+ GenericFontFamilySettings() = default;
+
+ explicit GenericFontFamilySettings(const GenericFontFamilySettings&);
+
+ bool UpdateStandard(const AtomicString&, UScriptCode = USCRIPT_COMMON);
+ const AtomicString& Standard(UScriptCode = USCRIPT_COMMON) const;
+
+ bool UpdateFixed(const AtomicString&, UScriptCode = USCRIPT_COMMON);
+ const AtomicString& Fixed(UScriptCode = USCRIPT_COMMON) const;
+
+ bool UpdateSerif(const AtomicString&, UScriptCode = USCRIPT_COMMON);
+ const AtomicString& Serif(UScriptCode = USCRIPT_COMMON) const;
+
+ bool UpdateSansSerif(const AtomicString&, UScriptCode = USCRIPT_COMMON);
+ const AtomicString& SansSerif(UScriptCode = USCRIPT_COMMON) const;
+
+ bool UpdateCursive(const AtomicString&, UScriptCode = USCRIPT_COMMON);
+ const AtomicString& Cursive(UScriptCode = USCRIPT_COMMON) const;
+
+ bool UpdateFantasy(const AtomicString&, UScriptCode = USCRIPT_COMMON);
+ const AtomicString& Fantasy(UScriptCode = USCRIPT_COMMON) const;
+
+ bool UpdatePictograph(const AtomicString&, UScriptCode = USCRIPT_COMMON);
+ const AtomicString& Pictograph(UScriptCode = USCRIPT_COMMON) const;
+
+ // Only called by InternalSettings to clear font family maps.
+ void Reset();
+
+ GenericFontFamilySettings& operator=(const GenericFontFamilySettings&);
+
+ // Returns a new instance with String instead of AtomicString objects.
+ // This allows GenericFontFamilySettings to be sent from one thread to
+ // another, since AtomicStrings can't be shared cross-threads.
+ // Before using it, call it MakeAtomic() on the final thread, to bring back
+ // the AtomicStrings.
+ void IsolatedCopyTo(GenericFontFamilySettings& dest) const;
+
+ bool IsIsolated() const { return isolated_copy_.get(); }
+
+ // Transform an IsolatedCopy GenericFontFamilySettings into a regular
+ // GenericFontFamilySettings.
+ void MakeAtomic();
+
+ private:
+ // UScriptCode uses -1 and 0 for UScriptInvalidCode and UScriptCommon.
+ // We need to use -2 and -3 for empty value and deleted value.
+ struct UScriptCodeHashTraits : WTF::GenericHashTraits<int> {
+ STATIC_ONLY(UScriptCodeHashTraits);
+ static const bool kEmptyValueIsZero = false;
+ static int EmptyValue() { return -2; }
+ static void ConstructDeletedValue(int& slot, bool) { slot = -3; }
+ static bool IsDeletedValue(int value) { return value == -3; }
+ };
+
+ typedef HashMap<int,
+ AtomicString,
+ DefaultHash<int>::Hash,
+ UScriptCodeHashTraits>
+ ScriptFontFamilyMap;
+
+ void SetGenericFontFamilyMap(ScriptFontFamilyMap&,
+ const AtomicString&,
+ UScriptCode);
+ const AtomicString& GenericFontFamilyForScript(const ScriptFontFamilyMap&,
+ UScriptCode) const;
+
+ ScriptFontFamilyMap standard_font_family_map_;
+ ScriptFontFamilyMap serif_font_family_map_;
+ ScriptFontFamilyMap fixed_font_family_map_;
+ ScriptFontFamilyMap sans_serif_font_family_map_;
+ ScriptFontFamilyMap cursive_font_family_map_;
+ ScriptFontFamilyMap fantasy_font_family_map_;
+ ScriptFontFamilyMap pictograph_font_family_map_;
+
+ typedef Vector<std::pair<int, String>> IsolatedCopyVector;
+ std::unique_ptr<IsolatedCopyVector[]> isolated_copy_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/fonts/generic_font_family_settings_test.cc b/chromium/third_party/blink/renderer/platform/fonts/generic_font_family_settings_test.cc
new file mode 100644
index 00000000000..796727c3982
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/generic_font_family_settings_test.cc
@@ -0,0 +1,29 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/generic_font_family_settings.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+TEST(GenericFontFamilySettingsTest, FirstAvailableFontFamily) {
+ GenericFontFamilySettings settings;
+ EXPECT_TRUE(settings.Standard().IsEmpty());
+
+ // Returns the first available font if starts with ",".
+ settings.UpdateStandard(",not exist, Arial");
+ EXPECT_EQ("Arial", settings.Standard());
+
+ // Otherwise returns any strings as they were set.
+ AtomicString non_lists[] = {
+ "Arial", "not exist", "not exist, Arial",
+ };
+ for (const AtomicString& name : non_lists) {
+ settings.UpdateStandard(name);
+ EXPECT_EQ(name, settings.Standard());
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/glyph.h b/chromium/third_party/blink/renderer/platform/fonts/glyph.h
new file mode 100644
index 00000000000..54a1bc5a11c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/glyph.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) Research In Motion Limited 2011. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_GLYPH_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_GLYPH_H_
+
+namespace blink {
+
+typedef unsigned short Glyph;
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_GLYPH_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/glyph_metrics_map.h b/chromium/third_party/blink/renderer/platform/fonts/glyph_metrics_map.h
new file mode 100644
index 00000000000..cb98f10921e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/glyph_metrics_map.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2006, 2009 Apple 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_GLYPH_METRICS_MAP_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_GLYPH_METRICS_MAP_H_
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/platform/fonts/glyph.h"
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
+
+namespace blink {
+
+const float kCGlyphSizeUnknown = -1;
+
+template <class T>
+class GlyphMetricsMap {
+ USING_FAST_MALLOC(GlyphMetricsMap);
+ WTF_MAKE_NONCOPYABLE(GlyphMetricsMap);
+
+ public:
+ GlyphMetricsMap() : filled_primary_page_(false) {}
+ T MetricsForGlyph(Glyph glyph) {
+ return LocatePage(glyph / GlyphMetricsPage::kSize)->MetricsForGlyph(glyph);
+ }
+
+ void SetMetricsForGlyph(Glyph glyph, const T& metrics) {
+ LocatePage(glyph / GlyphMetricsPage::kSize)
+ ->SetMetricsForGlyph(glyph, metrics);
+ }
+
+ private:
+ class GlyphMetricsPage {
+ USING_FAST_MALLOC(GlyphMetricsPage);
+ WTF_MAKE_NONCOPYABLE(GlyphMetricsPage);
+
+ public:
+ static const size_t kSize =
+ 256; // Usually covers Latin-1 in a single page.
+ GlyphMetricsPage() {}
+
+ T MetricsForGlyph(Glyph glyph) const { return metrics_[glyph % kSize]; }
+ void SetMetricsForGlyph(Glyph glyph, const T& metrics) {
+ SetMetricsForIndex(glyph % kSize, metrics);
+ }
+ void SetMetricsForIndex(unsigned index, const T& metrics) {
+ SECURITY_DCHECK(index < kSize);
+ metrics_[index] = metrics;
+ }
+
+ private:
+ T metrics_[kSize];
+ };
+
+ GlyphMetricsPage* LocatePage(unsigned page_number) {
+ if (!page_number && filled_primary_page_)
+ return &primary_page_;
+ return LocatePageSlowCase(page_number);
+ }
+
+ GlyphMetricsPage* LocatePageSlowCase(unsigned page_number);
+
+ static T UnknownMetrics();
+
+ bool filled_primary_page_;
+ // We optimize for the page that contains glyph indices 0-255.
+ GlyphMetricsPage primary_page_;
+ std::unique_ptr<HashMap<int, std::unique_ptr<GlyphMetricsPage>>> pages_;
+};
+
+template <>
+inline float GlyphMetricsMap<float>::UnknownMetrics() {
+ return kCGlyphSizeUnknown;
+}
+
+template <>
+inline FloatRect GlyphMetricsMap<FloatRect>::UnknownMetrics() {
+ return FloatRect(0, 0, kCGlyphSizeUnknown, kCGlyphSizeUnknown);
+}
+
+template <class T>
+typename GlyphMetricsMap<T>::GlyphMetricsPage*
+GlyphMetricsMap<T>::LocatePageSlowCase(unsigned page_number) {
+ GlyphMetricsPage* page;
+ if (!page_number) {
+ DCHECK(!filled_primary_page_);
+ page = &primary_page_;
+ filled_primary_page_ = true;
+ } else {
+ if (pages_) {
+ page = pages_->at(page_number);
+ if (page)
+ return page;
+ } else {
+ pages_ =
+ std::make_unique<HashMap<int, std::unique_ptr<GlyphMetricsPage>>>();
+ }
+ page = new GlyphMetricsPage;
+ pages_->Set(page_number, base::WrapUnique(page));
+ }
+
+ // Fill in the whole page with the unknown glyph information.
+ for (unsigned i = 0; i < GlyphMetricsPage::kSize; i++)
+ page->SetMetricsForIndex(i, UnknownMetrics());
+
+ return page;
+}
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/fonts/linux/font_cache_linux.cc b/chromium/third_party/blink/renderer/platform/fonts/linux/font_cache_linux.cc
new file mode 100644
index 00000000000..6d99346ee21
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/linux/font_cache_linux.cc
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2012 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/fonts/font_cache.h"
+
+#include "build/build_config.h"
+#include "third_party/blink/public/platform/linux/web_fallback_font.h"
+#include "third_party/blink/public/platform/linux/web_sandbox_support.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/fonts/font_platform_data.h"
+#include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "ui/gfx/font_fallback_linux.h"
+
+namespace blink {
+
+static AtomicString& MutableSystemFontFamily() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(AtomicString, system_font_family, ());
+ return system_font_family;
+}
+
+// static
+const AtomicString& FontCache::SystemFontFamily() {
+ return MutableSystemFontFamily();
+}
+
+// static
+void FontCache::SetSystemFontFamily(const AtomicString& family_name) {
+ DCHECK(!family_name.IsEmpty());
+ MutableSystemFontFamily() = family_name;
+}
+
+void FontCache::GetFontForCharacter(
+ UChar32 c,
+ const char* preferred_locale,
+ FontCache::PlatformFallbackFont* fallback_font) {
+ if (Platform::Current()->GetSandboxSupport()) {
+ WebFallbackFont web_fallback_font;
+ Platform::Current()->GetSandboxSupport()->GetFallbackFontForCharacter(
+ c, preferred_locale, &web_fallback_font);
+ fallback_font->name = web_fallback_font.name;
+ fallback_font->filename = CString(web_fallback_font.filename.Data(),
+ web_fallback_font.filename.size());
+ fallback_font->fontconfig_interface_id =
+ web_fallback_font.fontconfig_interface_id;
+ fallback_font->ttc_index = web_fallback_font.ttc_index;
+ fallback_font->is_bold = web_fallback_font.is_bold;
+ fallback_font->is_italic = web_fallback_font.is_italic;
+ } else {
+ std::string locale = preferred_locale ? preferred_locale : std::string();
+ gfx::FallbackFontData fallback_data =
+ gfx::GetFallbackFontForChar(c, locale);
+ fallback_font->name = String::FromUTF8(fallback_data.name.data(),
+ fallback_data.name.length());
+ fallback_font->filename =
+ CString(fallback_data.filename.data(), fallback_data.filename.length());
+ fallback_font->fontconfig_interface_id = 0;
+ fallback_font->ttc_index = fallback_data.ttc_index;
+ fallback_font->is_bold = fallback_data.is_bold;
+ fallback_font->is_italic = fallback_data.is_italic;
+ }
+}
+
+scoped_refptr<SimpleFontData> FontCache::PlatformFallbackFontForCharacter(
+ const FontDescription& font_description,
+ UChar32 c,
+ const SimpleFontData*,
+ FontFallbackPriority fallback_priority) {
+ // The m_fontManager is set only if it was provided by the embedder with
+ // WebFontRendering::setSkiaFontManager. This is used to emulate android fonts
+ // on linux so we always request the family from the font manager and if none
+ // is found, we return the LastResort fallback font and avoid using
+ // FontCache::getFontForCharacter which would use sandbox support to query the
+ // underlying system for the font family.
+ if (font_manager_) {
+ AtomicString family_name = GetFamilyNameForCharacter(
+ font_manager_.get(), c, font_description, fallback_priority);
+ if (family_name.IsEmpty())
+ return GetLastResortFallbackFont(font_description, kDoNotRetain);
+ return FontDataFromFontPlatformData(
+ GetFontPlatformData(font_description,
+ FontFaceCreationParams(family_name)),
+ kDoNotRetain);
+ }
+
+ if (fallback_priority == FontFallbackPriority::kEmojiEmoji) {
+ // FIXME crbug.com/591346: We're overriding the fallback character here
+ // with the FAMILY emoji in the hope to find a suitable emoji font.
+ // This should be improved by supporting fallback for character
+ // sequences like DIGIT ONE + COMBINING keycap etc.
+ c = kFamilyCharacter;
+ }
+
+ // First try the specified font with standard style & weight.
+ if (fallback_priority != FontFallbackPriority::kEmojiEmoji &&
+ (font_description.Style() == ItalicSlopeValue() ||
+ font_description.Weight() >= BoldThreshold())) {
+ scoped_refptr<SimpleFontData> font_data =
+ FallbackOnStandardFontStyle(font_description, c);
+ if (font_data)
+ return font_data;
+ }
+
+ FontCache::PlatformFallbackFont fallback_font;
+ FontCache::GetFontForCharacter(
+ c, font_description.LocaleOrDefault().Ascii().data(), &fallback_font);
+ if (fallback_font.name.IsEmpty())
+ return nullptr;
+
+ FontFaceCreationParams creation_params;
+ creation_params = FontFaceCreationParams(
+ fallback_font.filename, fallback_font.fontconfig_interface_id,
+ fallback_font.ttc_index);
+
+ // Changes weight and/or italic of given FontDescription depends on
+ // the result of fontconfig so that keeping the correct font mapping
+ // of the given character. See http://crbug.com/32109 for details.
+ bool should_set_synthetic_bold = false;
+ bool should_set_synthetic_italic = false;
+ FontDescription description(font_description);
+ if (fallback_font.is_bold && description.Weight() < BoldThreshold())
+ description.SetWeight(BoldWeightValue());
+ if (!fallback_font.is_bold && description.Weight() >= BoldThreshold()) {
+ should_set_synthetic_bold = true;
+ description.SetWeight(NormalWeightValue());
+ }
+ if (fallback_font.is_italic && description.Style() == NormalSlopeValue())
+ description.SetStyle(ItalicSlopeValue());
+ if (!fallback_font.is_italic && (description.Style() == ItalicSlopeValue())) {
+ should_set_synthetic_italic = true;
+ description.SetStyle(NormalSlopeValue());
+ }
+
+ FontPlatformData* substitute_platform_data =
+ GetFontPlatformData(description, creation_params);
+ if (!substitute_platform_data)
+ return nullptr;
+
+ std::unique_ptr<FontPlatformData> platform_data(
+ new FontPlatformData(*substitute_platform_data));
+ platform_data->SetSyntheticBold(should_set_synthetic_bold);
+ platform_data->SetSyntheticItalic(should_set_synthetic_italic);
+ return FontDataFromFontPlatformData(platform_data.get(), kDoNotRetain);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/mac/core_text_variations_support.cc b/chromium/third_party/blink/renderer/platform/fonts/mac/core_text_variations_support.cc
new file mode 100644
index 00000000000..5b95205856b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/mac/core_text_variations_support.cc
@@ -0,0 +1,20 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/mac/core_text_variations_support.h"
+
+#include <CoreText/CoreText.h>
+
+namespace blink {
+
+// Compare CoreText.h in an up to date SDK, redefining here since we don't seem
+// to have access to this value when building against the 10.10 SDK in our
+// standard Chrome build configuration.
+static const long kBlinkLocalCTVersionNumber10_12 = 0x00090000;
+
+bool CoreTextVersionSupportsVariations() {
+ return &CTGetCoreTextVersion &&
+ CTGetCoreTextVersion() >= kBlinkLocalCTVersionNumber10_12;
+}
+}
diff --git a/chromium/third_party/blink/renderer/platform/fonts/mac/core_text_variations_support.h b/chromium/third_party/blink/renderer/platform/fonts/mac/core_text_variations_support.h
new file mode 100644
index 00000000000..f8c175a7101
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/mac/core_text_variations_support.h
@@ -0,0 +1,13 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_MAC_CORE_TEXT_VARIATIONS_SUPPORT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_MAC_CORE_TEXT_VARIATIONS_SUPPORT_H_
+
+namespace blink {
+
+bool CoreTextVersionSupportsVariations();
+}
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/fonts/mac/font_cache_mac.mm b/chromium/third_party/blink/renderer/platform/fonts/mac/font_cache_mac.mm
new file mode 100644
index 00000000000..4c06fbbbb3f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/mac/font_cache_mac.mm
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2007 Nicholas Shanks <webkit@nickshanks.com>
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/fonts/font_cache.h"
+
+#import <AppKit/AppKit.h>
+#include <memory>
+#include "base/location.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/font_family_names.h"
+#include "third_party/blink/renderer/platform/fonts/font_description.h"
+#include "third_party/blink/renderer/platform/fonts/font_face_creation_params.h"
+#include "third_party/blink/renderer/platform/fonts/font_platform_data.h"
+#include "third_party/blink/renderer/platform/fonts/mac/font_family_matcher_mac.h"
+#include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
+#include "third_party/blink/renderer/platform/layout_test_support.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+
+// Forward declare Mac SPIs.
+// Request for public API: rdar://13803570
+@interface NSFont (WebKitSPI)
++ (NSFont*)findFontLike:(NSFont*)font
+ forString:(NSString*)string
+ withRange:(NSRange)range
+ inLanguage:(id)useNil;
++ (NSFont*)findFontLike:(NSFont*)font
+ forCharacter:(UniChar)uc
+ inLanguage:(id)useNil;
+@end
+
+namespace blink {
+
+const char kColorEmojiFontMac[] = "Apple Color Emoji";
+
+// static
+const AtomicString& FontCache::LegacySystemFontFamily() {
+ return FontFamilyNames::BlinkMacSystemFont;
+}
+
+static void InvalidateFontCache() {
+ if (!IsMainThread()) {
+ Platform::Current()->MainThread()->GetTaskRunner()->PostTask(
+ FROM_HERE, WTF::Bind(&InvalidateFontCache));
+ return;
+ }
+ FontCache::GetFontCache()->Invalidate();
+}
+
+static void FontCacheRegisteredFontsChangedNotificationCallback(
+ CFNotificationCenterRef,
+ void* observer,
+ CFStringRef name,
+ const void*,
+ CFDictionaryRef) {
+ DCHECK_EQ(observer, FontCache::GetFontCache());
+ DCHECK(CFEqual(name, kCTFontManagerRegisteredFontsChangedNotification));
+ InvalidateFontCache();
+}
+
+static bool UseHinting() {
+ // Enable hinting only when antialiasing is disabled in layout tests.
+ return (LayoutTestSupport::IsRunningLayoutTest() &&
+ !LayoutTestSupport::IsFontAntialiasingEnabledForTest());
+}
+
+void FontCache::PlatformInit() {
+ CFNotificationCenterAddObserver(
+ CFNotificationCenterGetLocalCenter(), this,
+ FontCacheRegisteredFontsChangedNotificationCallback,
+ kCTFontManagerRegisteredFontsChangedNotification, 0,
+ CFNotificationSuspensionBehaviorDeliverImmediately);
+}
+
+static inline bool IsAppKitFontWeightBold(NSInteger app_kit_font_weight) {
+ return app_kit_font_weight >= 7;
+}
+
+scoped_refptr<SimpleFontData> FontCache::PlatformFallbackFontForCharacter(
+ const FontDescription& font_description,
+ UChar32 character,
+ const SimpleFontData* font_data_to_substitute,
+ FontFallbackPriority fallback_priority) {
+ if (fallback_priority == FontFallbackPriority::kEmojiEmoji) {
+ scoped_refptr<SimpleFontData> emoji_font =
+ GetFontData(font_description, AtomicString(kColorEmojiFontMac));
+ if (emoji_font)
+ return emoji_font;
+ }
+
+ // FIXME: We should fix getFallbackFamily to take a UChar32
+ // and remove this split-to-UChar16 code.
+ UChar code_units[2];
+ int code_units_length;
+ if (character <= 0xFFFF) {
+ code_units[0] = character;
+ code_units_length = 1;
+ } else {
+ code_units[0] = U16_LEAD(character);
+ code_units[1] = U16_TRAIL(character);
+ code_units_length = 2;
+ }
+
+ const FontPlatformData& platform_data =
+ font_data_to_substitute->PlatformData();
+ NSFont* ns_font = toNSFont(platform_data.CtFont());
+
+ NSString* string =
+ [[NSString alloc] initWithCharactersNoCopy:code_units
+ length:code_units_length
+ freeWhenDone:NO];
+ NSFont* substitute_font =
+ [NSFont findFontLike:ns_font
+ forString:string
+ withRange:NSMakeRange(0, code_units_length)
+ inLanguage:nil];
+ [string release];
+
+ // FIXME: Remove this SPI usage: http://crbug.com/255122
+ if (!substitute_font && code_units_length == 1)
+ substitute_font =
+ [NSFont findFontLike:ns_font forCharacter:code_units[0] inLanguage:nil];
+ if (!substitute_font)
+ return nullptr;
+
+ // Use the family name from the AppKit-supplied substitute font, requesting
+ // the traits, weight, and size we want. One way this does better than the
+ // original AppKit request is that it takes synthetic bold and oblique into
+ // account. But it does create the possibility that we could end up with a
+ // font that doesn't actually cover the characters we need.
+
+ NSFontManager* font_manager = [NSFontManager sharedFontManager];
+
+ NSFontTraitMask traits;
+ NSInteger weight;
+ CGFloat size;
+
+ if (ns_font) {
+ traits = [font_manager traitsOfFont:ns_font];
+ if (platform_data.synthetic_bold_)
+ traits |= NSBoldFontMask;
+ if (platform_data.synthetic_italic_)
+ traits |= NSFontItalicTrait;
+ weight = [font_manager weightOfFont:ns_font];
+ size = [ns_font pointSize];
+ } else {
+ // For custom fonts nsFont is nil.
+ traits = font_description.Style() ? NSFontItalicTrait : 0;
+ weight = ToAppKitFontWeight(font_description.Weight());
+ size = font_description.ComputedPixelSize();
+ }
+
+ NSFontTraitMask substitute_font_traits =
+ [font_manager traitsOfFont:substitute_font];
+ NSInteger substitute_font_weight =
+ [font_manager weightOfFont:substitute_font];
+
+ if (traits != substitute_font_traits || weight != substitute_font_weight ||
+ !ns_font) {
+ if (NSFont* best_variation =
+ [font_manager fontWithFamily:[substitute_font familyName]
+ traits:traits
+ weight:weight
+ size:size]) {
+ if ((!ns_font ||
+ [font_manager traitsOfFont:best_variation] !=
+ substitute_font_traits ||
+ [font_manager weightOfFont:best_variation] !=
+ substitute_font_weight) &&
+ [[best_variation coveredCharacterSet]
+ longCharacterIsMember:character])
+ substitute_font = best_variation;
+ }
+ }
+
+ substitute_font = UseHinting() ? [substitute_font screenFont]
+ : [substitute_font printerFont];
+
+ substitute_font_traits = [font_manager traitsOfFont:substitute_font];
+ substitute_font_weight = [font_manager weightOfFont:substitute_font];
+
+ // TODO(eae): Remove once skia supports bold emoji. See
+ // https://bugs.chromium.org/p/skia/issues/detail?id=4904
+ // Bold emoji look the same as normal emoji, so syntheticBold isn't needed.
+ bool synthetic_bold =
+ IsAppKitFontWeightBold(weight) &&
+ !IsAppKitFontWeightBold(substitute_font_weight) &&
+ ![substitute_font.familyName isEqual:@"Apple Color Emoji"];
+
+ FontPlatformData alternate_font(
+ substitute_font, platform_data.size(), synthetic_bold,
+ (traits & NSFontItalicTrait) &&
+ !(substitute_font_traits & NSFontItalicTrait),
+ platform_data.Orientation(),
+ nullptr); // No variation paramaters in fallback.
+
+ return FontDataFromFontPlatformData(&alternate_font, kDoNotRetain);
+}
+
+scoped_refptr<SimpleFontData> FontCache::GetLastResortFallbackFont(
+ const FontDescription& font_description,
+ ShouldRetain should_retain) {
+ // FIXME: Would be even better to somehow get the user's default font here.
+ // For now we'll pick the default that the user would get without changing
+ // any prefs.
+ scoped_refptr<SimpleFontData> simple_font_data =
+ GetFontData(font_description, FontFamilyNames::Times,
+ AlternateFontName::kAllowAlternate, should_retain);
+ if (simple_font_data)
+ return simple_font_data;
+
+ // The Times fallback will almost always work, but in the highly unusual case
+ // where the user doesn't have it, we fall back on Lucida Grande because
+ // that's guaranteed to be there, according to Nathan Taylor. This is good
+ // enough to avoid a crash at least.
+ return GetFontData(font_description, FontFamilyNames::Lucida_Grande,
+ AlternateFontName::kAllowAlternate, should_retain);
+}
+
+std::unique_ptr<FontPlatformData> FontCache::CreateFontPlatformData(
+ const FontDescription& font_description,
+ const FontFaceCreationParams& creation_params,
+ float font_size,
+ AlternateFontName) {
+ NSFontTraitMask traits = font_description.Style() ? NSFontItalicTrait : 0;
+ float size = font_size;
+
+ NSFont* ns_font = MatchNSFontFamily(creation_params.Family(), traits,
+ font_description.Weight(), size);
+ if (!ns_font)
+ return nullptr;
+
+ NSFontManager* font_manager = [NSFontManager sharedFontManager];
+ NSFontTraitMask actual_traits = 0;
+ if (font_description.Style())
+ actual_traits = [font_manager traitsOfFont:ns_font];
+ NSInteger actual_weight = [font_manager weightOfFont:ns_font];
+
+ NSFont* platform_font =
+ UseHinting() ? [ns_font screenFont] : [ns_font printerFont];
+ NSInteger app_kit_weight = ToAppKitFontWeight(font_description.Weight());
+
+ // TODO(eae): Remove once skia supports bold emoji. See
+ // https://bugs.chromium.org/p/skia/issues/detail?id=4904
+ // Bold emoji look the same as normal emoji, so syntheticBold isn't needed.
+ bool synthetic_bold = [platform_font.familyName isEqual:@"Apple Color Emoji"]
+ ? false
+ : (IsAppKitFontWeightBold(app_kit_weight) &&
+ !IsAppKitFontWeightBold(actual_weight)) ||
+ font_description.IsSyntheticBold();
+
+ bool synthetic_italic =
+ ((traits & NSFontItalicTrait) && !(actual_traits & NSFontItalicTrait)) ||
+ font_description.IsSyntheticItalic();
+
+ // FontPlatformData::typeface() is null in the case of Chromium out-of-process
+ // font loading failing. Out-of-process loading occurs for registered fonts
+ // stored in non-system locations. When loading fails, we do not want to use
+ // the returned FontPlatformData since it will not have a valid SkTypeface.
+ std::unique_ptr<FontPlatformData> platform_data =
+ std::make_unique<FontPlatformData>(
+ platform_font, size, synthetic_bold, synthetic_italic,
+ font_description.Orientation(), font_description.VariationSettings());
+ if (!platform_data->Typeface()) {
+ return nullptr;
+ }
+ return platform_data;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/mac/font_family_matcher_mac.h b/chromium/third_party/blink/renderer/platform/fonts/mac/font_family_matcher_mac.h
new file mode 100644
index 00000000000..679bc3e0b7b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/mac/font_family_matcher_mac.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2007 Nicholas Shanks <webkit@nickshanks.com>
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_MAC_FONT_FAMILY_MATCHER_MAC_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_MAC_FONT_FAMILY_MATCHER_MAC_H_
+
+#include <AppKit/NSFontManager.h>
+#include "third_party/blink/renderer/platform/fonts/font_selection_types.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+PLATFORM_EXPORT NSFont* MatchNSFontFamily(const AtomicString& desired_family,
+ NSFontTraitMask desired_traits,
+ FontSelectionValue desired_weight,
+ float size);
+
+// Converts ablink::FontSelectionValue to the nearest AppKit font weight if
+// possible, otherwise returns the default font weight.
+int ToAppKitFontWeight(FontSelectionValue);
+}
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_MAC_FONT_FAMILY_MATCHER_MAC_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/mac/font_family_matcher_mac.mm b/chromium/third_party/blink/renderer/platform/fonts/mac/font_family_matcher_mac.mm
new file mode 100644
index 00000000000..1110c0e7f43
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/mac/font_family_matcher_mac.mm
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2007 Nicholas Shanks <webkit@nickshanks.com>
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/fonts/mac/font_family_matcher_mac.h"
+
+#import <AppKit/AppKit.h>
+#import <Foundation/Foundation.h>
+#import <math.h>
+#include "third_party/blink/renderer/platform/fonts/font_cache.h"
+#include "third_party/blink/renderer/platform/layout_test_support.h"
+#include "third_party/blink/renderer/platform/mac/version_util_mac.h"
+#import "third_party/blink/renderer/platform/wtf/hash_set.h"
+#import "third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h"
+
+@interface NSFont (YosemiteAdditions)
++ (NSFont*)systemFontOfSize:(CGFloat)size weight:(CGFloat)weight;
+@end
+
+namespace {
+
+static CGFloat toYosemiteFontWeight(blink::FontSelectionValue font_weight) {
+ static uint64_t ns_font_weights[] = {
+ 0xbfe99999a0000000, // NSFontWeightUltraLight
+ 0xbfe3333340000000, // NSFontWeightThin
+ 0xbfd99999a0000000, // NSFontWeightLight
+ 0x0000000000000000, // NSFontWeightRegular
+ 0x3fcd70a3e0000000, // NSFontWeightMedium
+ 0x3fd3333340000000, // NSFontWeightSemibold
+ 0x3fd99999a0000000, // NSFontWeightBold
+ 0x3fe1eb8520000000, // NSFontWeightHeavy
+ 0x3fe3d70a40000000, // NSFontWeightBlack
+ };
+ if (font_weight <= 50 || font_weight >= 950)
+ return ns_font_weights[3];
+
+ size_t select_weight = roundf(font_weight / 100) - 1;
+ DCHECK_GE(select_weight, 0ul);
+ DCHECK_LE(select_weight, arraysize(ns_font_weights));
+ CGFloat* return_weight =
+ reinterpret_cast<CGFloat*>(&ns_font_weights[select_weight]);
+ return *return_weight;
+}
+}
+
+namespace blink {
+
+const NSFontTraitMask SYNTHESIZED_FONT_TRAITS =
+ (NSBoldFontMask | NSItalicFontMask);
+
+const NSFontTraitMask IMPORTANT_FONT_TRAITS =
+ (NSCompressedFontMask | NSCondensedFontMask | NSExpandedFontMask |
+ NSItalicFontMask |
+ NSNarrowFontMask |
+ NSPosterFontMask |
+ NSSmallCapsFontMask);
+
+static BOOL AcceptableChoice(NSFontTraitMask desired_traits,
+ NSFontTraitMask candidate_traits) {
+ desired_traits &= ~SYNTHESIZED_FONT_TRAITS;
+ return (candidate_traits & desired_traits) == desired_traits;
+}
+
+static BOOL BetterChoice(NSFontTraitMask desired_traits,
+ int desired_weight,
+ NSFontTraitMask chosen_traits,
+ int chosen_weight,
+ NSFontTraitMask candidate_traits,
+ int candidate_weight) {
+ if (!AcceptableChoice(desired_traits, candidate_traits))
+ return NO;
+
+ // A list of the traits we care about.
+ // The top item in the list is the worst trait to mismatch; if a font has this
+ // and we didn't ask for it, we'd prefer any other font in the family.
+ const NSFontTraitMask kMasks[] = {NSPosterFontMask, NSSmallCapsFontMask,
+ NSItalicFontMask, NSCompressedFontMask,
+ NSCondensedFontMask, NSExpandedFontMask,
+ NSNarrowFontMask, 0};
+
+ int i = 0;
+ NSFontTraitMask mask;
+ while ((mask = kMasks[i++])) {
+ BOOL desired = (desired_traits & mask) != 0;
+ BOOL chosen_has_unwanted_trait = desired != ((chosen_traits & mask) != 0);
+ BOOL candidate_has_unwanted_trait =
+ desired != ((candidate_traits & mask) != 0);
+ if (!candidate_has_unwanted_trait && chosen_has_unwanted_trait)
+ return YES;
+ if (!chosen_has_unwanted_trait && candidate_has_unwanted_trait)
+ return NO;
+ }
+
+ int chosen_weight_delta_magnitude = abs(chosen_weight - desired_weight);
+ int candidate_weight_delta_magnitude = abs(candidate_weight - desired_weight);
+
+ // If both are the same distance from the desired weight, prefer the candidate
+ // if it is further from medium.
+ if (chosen_weight_delta_magnitude == candidate_weight_delta_magnitude)
+ return abs(candidate_weight - 6) > abs(chosen_weight - 6);
+
+ // Otherwise, prefer the one closer to the desired weight.
+ return candidate_weight_delta_magnitude < chosen_weight_delta_magnitude;
+}
+
+// Family name is somewhat of a misnomer here. We first attempt to find an
+// exact match comparing the desiredFamily to the PostScript name of the
+// installed fonts. If that fails we then do a search based on the family
+// names of the installed fonts.
+NSFont* MatchNSFontFamily(const AtomicString& desired_family_string,
+ NSFontTraitMask desired_traits,
+ FontSelectionValue desired_weight,
+ float size) {
+ DCHECK_NE(desired_family_string, FontCache::LegacySystemFontFamily());
+ if (desired_family_string == FontFamilyNames::system_ui) {
+ // On OSX 10.9, the default system font depends on the SDK version. When
+ // compiled against the OSX 10.10 SDK, the font is .LucidaGrandeUI. When
+ // compiled against the OSX 10.6 SDK, the font is Lucida Grande. Layout
+ // tests don't support different expectations based on the SDK version,
+ // so force layout tests to use "Lucida Grande". Once the 10.10 SDK
+ // switch is made, this should be changed to return .LucidaGrandeUI and
+ // the Layout Expectations should be updated. http://crbug.com/515836.
+ if (LayoutTestSupport::IsRunningLayoutTest() && IsOS10_9()) {
+ if (desired_weight >= BoldWeightValue())
+ return [NSFont fontWithName:@"Lucida Grande Bold" size:size];
+ else
+ return [NSFont fontWithName:@"Lucida Grande" size:size];
+ }
+
+ NSFont* font = nil;
+ if (IsOS10_9()) {
+ // On older OSX versions, only bold and regular are available.
+ if (desired_weight >= BoldWeightValue())
+ font = [NSFont boldSystemFontOfSize:size];
+ else
+ font = [NSFont systemFontOfSize:size];
+ } else {
+// Normally we'd use an availability macro here, but
+// systemFontOfSize:weight: is available but not visible on macOS 10.10,
+// so it's been forward declared earlier in this file.
+// On OSX 10.10+, the default system font has more weights.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunguarded-availability"
+ font = [NSFont systemFontOfSize:size
+ weight:toYosemiteFontWeight(desired_weight)];
+#pragma clang diagnostic pop
+ }
+
+ if (desired_traits & IMPORTANT_FONT_TRAITS)
+ font = [[NSFontManager sharedFontManager] convertFont:font
+ toHaveTrait:desired_traits];
+ return font;
+ }
+
+ NSString* desired_family = desired_family_string;
+ NSFontManager* font_manager = [NSFontManager sharedFontManager];
+
+ // Do a simple case insensitive search for a matching font family.
+ // NSFontManager requires exact name matches.
+ // This addresses the problem of matching arial to Arial, etc., but perhaps
+ // not all the issues.
+ NSEnumerator* e = [[font_manager availableFontFamilies] objectEnumerator];
+ NSString* available_family;
+ while ((available_family = [e nextObject])) {
+ if ([desired_family caseInsensitiveCompare:available_family] ==
+ NSOrderedSame)
+ break;
+ }
+
+ int app_kit_font_weight = ToAppKitFontWeight(desired_weight);
+ if (!available_family) {
+ // Match by PostScript name.
+ NSEnumerator* available_fonts =
+ [[font_manager availableFonts] objectEnumerator];
+ NSString* available_font;
+ NSFont* name_matched_font = nil;
+ NSFontTraitMask desired_traits_for_name_match =
+ desired_traits | (app_kit_font_weight >= 7 ? NSBoldFontMask : 0);
+ while ((available_font = [available_fonts nextObject])) {
+ if ([desired_family caseInsensitiveCompare:available_font] ==
+ NSOrderedSame) {
+ name_matched_font = [NSFont fontWithName:available_font size:size];
+
+ // Special case Osaka-Mono. According to <rdar://problem/3999467>, we
+ // need to treat Osaka-Mono as fixed pitch.
+ if ([desired_family caseInsensitiveCompare:@"Osaka-Mono"] ==
+ NSOrderedSame &&
+ desired_traits_for_name_match == 0)
+ return name_matched_font;
+
+ NSFontTraitMask traits = [font_manager traitsOfFont:name_matched_font];
+ if ((traits & desired_traits_for_name_match) ==
+ desired_traits_for_name_match)
+ return [font_manager convertFont:name_matched_font
+ toHaveTrait:desired_traits_for_name_match];
+
+ available_family = [name_matched_font familyName];
+ break;
+ }
+ }
+ }
+
+ // Found a family, now figure out what weight and traits to use.
+ BOOL chose_font = false;
+ int chosen_weight = 0;
+ NSFontTraitMask chosen_traits = 0;
+ NSString* chosen_full_name = 0;
+
+ NSArray* fonts = [font_manager availableMembersOfFontFamily:available_family];
+ unsigned n = [fonts count];
+ unsigned i;
+ for (i = 0; i < n; i++) {
+ NSArray* font_info = [fonts objectAtIndex:i];
+
+ // Array indices must be hard coded because of lame AppKit API.
+ NSString* font_full_name = [font_info objectAtIndex:0];
+ NSInteger font_weight = [[font_info objectAtIndex:2] intValue];
+
+ NSFontTraitMask font_traits =
+ [[font_info objectAtIndex:3] unsignedIntValue];
+
+ BOOL new_winner;
+ if (!chose_font)
+ new_winner = AcceptableChoice(desired_traits, font_traits);
+ else
+ new_winner =
+ BetterChoice(desired_traits, app_kit_font_weight, chosen_traits,
+ chosen_weight, font_traits, font_weight);
+
+ if (new_winner) {
+ chose_font = YES;
+ chosen_weight = font_weight;
+ chosen_traits = font_traits;
+ chosen_full_name = font_full_name;
+
+ if (chosen_weight == app_kit_font_weight &&
+ (chosen_traits & IMPORTANT_FONT_TRAITS) ==
+ (desired_traits & IMPORTANT_FONT_TRAITS))
+ break;
+ }
+ }
+
+ if (!chose_font)
+ return nil;
+
+ NSFont* font = [NSFont fontWithName:chosen_full_name size:size];
+
+ if (!font)
+ return nil;
+
+ NSFontTraitMask actual_traits = 0;
+ if (desired_traits & NSFontItalicTrait)
+ actual_traits = [font_manager traitsOfFont:font];
+ int actual_weight = [font_manager weightOfFont:font];
+
+ bool synthetic_bold = app_kit_font_weight >= 7 && actual_weight < 7;
+ bool synthetic_italic = (desired_traits & NSFontItalicTrait) &&
+ !(actual_traits & NSFontItalicTrait);
+
+ // There are some malformed fonts that will be correctly returned by
+ // -fontWithFamily:traits:weight:size: as a match for a particular trait,
+ // though -[NSFontManager traitsOfFont:] incorrectly claims the font does not
+ // have the specified trait. This could result in applying
+ // synthetic bold on top of an already-bold font, as reported in
+ // <http://bugs.webkit.org/show_bug.cgi?id=6146>. To work around this
+ // problem, if we got an apparent exact match, but the requested traits
+ // aren't present in the matched font, we'll try to get a font from the same
+ // family without those traits (to apply the synthetic traits to later).
+ NSFontTraitMask non_synthetic_traits = desired_traits;
+
+ if (synthetic_bold)
+ non_synthetic_traits &= ~NSBoldFontMask;
+
+ if (synthetic_italic)
+ non_synthetic_traits &= ~NSItalicFontMask;
+
+ if (non_synthetic_traits != desired_traits) {
+ NSFont* font_without_synthetic_traits =
+ [font_manager fontWithFamily:available_family
+ traits:non_synthetic_traits
+ weight:chosen_weight
+ size:size];
+ if (font_without_synthetic_traits)
+ font = font_without_synthetic_traits;
+ }
+
+ return font;
+}
+
+int ToAppKitFontWeight(FontSelectionValue font_weight) {
+ float weight = font_weight;
+ if (weight <= 50 || weight >= 950)
+ return 5;
+
+ size_t select_weight = roundf(weight / 100) - 1;
+ static int app_kit_font_weights[] = {
+ 2, // FontWeight100
+ 3, // FontWeight200
+ 4, // FontWeight300
+ 5, // FontWeight400
+ 6, // FontWeight500
+ 8, // FontWeight600
+ 9, // FontWeight700
+ 10, // FontWeight800
+ 12, // FontWeight900
+ };
+ DCHECK_GE(select_weight, 0ul);
+ DCHECK_LE(select_weight, arraysize(app_kit_font_weights));
+ return app_kit_font_weights[select_weight];
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/mac/font_family_matcher_mac_test.mm b/chromium/third_party/blink/renderer/platform/fonts/mac/font_family_matcher_mac_test.mm
new file mode 100644
index 00000000000..6ed8ac7dd5d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/mac/font_family_matcher_mac_test.mm
@@ -0,0 +1,41 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "third_party/blink/renderer/platform/fonts/mac/font_family_matcher_mac.h"
+
+#include <AppKit/AppKit.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/font_family_names.h"
+#include "third_party/blink/renderer/platform/mac/version_util_mac.h"
+
+@interface NSString (YosemiteAdditions)
+- (BOOL)containsString:(NSString*)string;
+@end
+
+namespace blink {
+
+void TestSystemFontContainsString(FontSelectionValue desired_weight,
+ NSString* substring) {
+ NSFont* font =
+ MatchNSFontFamily(FontFamilyNames::system_ui, 0, desired_weight, 11);
+ EXPECT_TRUE([font.description containsString:substring]);
+}
+
+TEST(FontFamilyMatcherMacTest, YosemiteFontWeights) {
+ if (!IsOS10_10())
+ return;
+
+ TestSystemFontContainsString(FontSelectionValue(100), @"-UltraLight");
+ TestSystemFontContainsString(FontSelectionValue(200), @"-Thin");
+ TestSystemFontContainsString(FontSelectionValue(300), @"-Light");
+ TestSystemFontContainsString(FontSelectionValue(400), @"-Regular");
+ TestSystemFontContainsString(FontSelectionValue(500), @"-Medium");
+ TestSystemFontContainsString(FontSelectionValue(600), @"-Bold");
+ TestSystemFontContainsString(FontSelectionValue(700), @"-Bold");
+ TestSystemFontContainsString(FontSelectionValue(800), @"-Heavy");
+ TestSystemFontContainsString(FontSelectionValue(900), @"-Heavy");
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/mac/font_platform_data_mac.mm b/chromium/third_party/blink/renderer/platform/fonts/mac/font_platform_data_mac.mm
new file mode 100644
index 00000000000..4ed30b37a94
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/mac/font_platform_data_mac.mm
@@ -0,0 +1,207 @@
+/*
+ * This file is part of the internal font implementation.
+ *
+ * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
+ * Copyright (c) 2010 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#import "third_party/blink/renderer/platform/fonts/font_platform_data.h"
+
+#import <AppKit/NSFont.h>
+#import <AvailabilityMacros.h>
+#import "third_party/blink/public/platform/mac/web_sandbox_support.h"
+#import "third_party/blink/public/platform/platform.h"
+#import "third_party/blink/renderer/platform/fonts/font.h"
+#import "third_party/blink/renderer/platform/fonts/opentype/font_settings.h"
+#import "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_face.h"
+#import "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
+#import "third_party/blink/renderer/platform/layout_test_support.h"
+#import "third_party/blink/renderer/platform/wtf/retain_ptr.h"
+#import "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#import "third_party/skia/include/ports/SkTypeface_mac.h"
+
+namespace blink {
+
+static bool CanLoadInProcess(NSFont* ns_font) {
+ RetainPtr<CGFontRef> cg_font(kAdoptCF,
+ CTFontCopyGraphicsFont(toCTFontRef(ns_font), 0));
+ // Toll-free bridged types CFStringRef and NSString*.
+ RetainPtr<NSString> font_name(
+ kAdoptNS, const_cast<NSString*>(reinterpret_cast<const NSString*>(
+ CGFontCopyPostScriptName(cg_font.Get()))));
+ return ![font_name.Get() isEqualToString:@"LastResort"];
+}
+
+static CTFontDescriptorRef CascadeToLastResortFontDescriptor() {
+ static CTFontDescriptorRef descriptor;
+ if (descriptor)
+ return descriptor;
+
+ RetainPtr<CTFontDescriptorRef> last_resort(
+ kAdoptCF, CTFontDescriptorCreateWithNameAndSize(CFSTR("LastResort"), 0));
+ const void* descriptors[] = {last_resort.Get()};
+ RetainPtr<CFArrayRef> values_array(
+ kAdoptCF,
+ CFArrayCreate(kCFAllocatorDefault, descriptors,
+ WTF_ARRAY_LENGTH(descriptors), &kCFTypeArrayCallBacks));
+
+ const void* keys[] = {kCTFontCascadeListAttribute};
+ const void* values[] = {values_array.Get()};
+ RetainPtr<CFDictionaryRef> attributes(
+ kAdoptCF,
+ CFDictionaryCreate(kCFAllocatorDefault, keys, values,
+ WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks));
+
+ descriptor = CTFontDescriptorCreateWithAttributes(attributes.Get());
+
+ return descriptor;
+}
+
+static sk_sp<SkTypeface> LoadFromBrowserProcess(NSFont* ns_font,
+ float text_size) {
+ // Send cross-process request to load font.
+ WebSandboxSupport* sandbox_support = Platform::Current()->GetSandboxSupport();
+ if (!sandbox_support) {
+ // This function should only be called in response to an error loading a
+ // font due to being blocked by the sandbox.
+ // This by definition shouldn't happen if there is no sandbox support.
+ NOTREACHED();
+ return nullptr;
+ }
+
+ CGFontRef loaded_cg_font;
+ uint32_t font_id;
+ if (!sandbox_support->LoadFont(toCTFontRef(ns_font), &loaded_cg_font,
+ &font_id)) {
+ // TODO crbug.com/461279: Make this appear in the inspector console?
+ DLOG(ERROR)
+ << "Loading user font \"" << [[ns_font familyName] UTF8String]
+ << "\" from non system location failed. Corrupt or missing font file?";
+ return nullptr;
+ }
+ RetainPtr<CGFontRef> cg_font(kAdoptCF, loaded_cg_font);
+ RetainPtr<CTFontRef> ct_font(
+ kAdoptCF,
+ CTFontCreateWithGraphicsFont(cg_font.Get(), text_size, 0,
+ CascadeToLastResortFontDescriptor()));
+ sk_sp<SkTypeface> return_font(
+ SkCreateTypefaceFromCTFont(ct_font.Get(), cg_font.Get()));
+
+ if (!return_font.get())
+ // TODO crbug.com/461279: Make this appear in the inspector console?
+ DLOG(ERROR)
+ << "Instantiating SkTypeface from user font failed for font family \""
+ << [[ns_font familyName] UTF8String] << "\".";
+ return return_font;
+}
+
+void FontPlatformData::SetupPaintFont(PaintFont* paint_font,
+ float,
+ const Font* font) const {
+ bool should_smooth_fonts = true;
+ bool should_antialias = true;
+ bool should_subpixel_position = true;
+
+ if (font) {
+ switch (font->GetFontDescription().FontSmoothing()) {
+ case kAntialiased:
+ should_smooth_fonts = false;
+ break;
+ case kSubpixelAntialiased:
+ break;
+ case kNoSmoothing:
+ should_antialias = false;
+ should_smooth_fonts = false;
+ break;
+ case kAutoSmoothing:
+ // For the AutoSmooth case, don't do anything! Keep the default
+ // settings.
+ break;
+ }
+ }
+
+ if (LayoutTestSupport::IsRunningLayoutTest()) {
+ should_smooth_fonts = false;
+ should_antialias = should_antialias &&
+ LayoutTestSupport::IsFontAntialiasingEnabledForTest();
+ should_subpixel_position =
+ LayoutTestSupport::IsTextSubpixelPositioningAllowedForTest();
+ }
+
+ paint_font->SetAntiAlias(should_antialias);
+ paint_font->SetEmbeddedBitmapText(false);
+ const float ts = text_size_ >= 0 ? text_size_ : 12;
+ paint_font->SetTextSize(SkFloatToScalar(ts));
+ paint_font->SetTypeface(paint_typeface_);
+ paint_font->SetFakeBoldText(synthetic_bold_);
+ paint_font->SetTextSkewX(synthetic_italic_ ? -SK_Scalar1 / 4 : 0);
+ paint_font->SetLcdRenderText(should_smooth_fonts);
+ paint_font->SetSubpixelText(should_subpixel_position);
+
+ // When rendering using CoreGraphics, disable hinting when
+ // webkit-font-smoothing:antialiased or text-rendering:geometricPrecision is
+ // used. See crbug.com/152304
+ if (font &&
+ (font->GetFontDescription().FontSmoothing() == kAntialiased ||
+ font->GetFontDescription().TextRendering() == kGeometricPrecision))
+ paint_font->SetHinting(SkPaint::kNo_Hinting);
+}
+
+FontPlatformData::FontPlatformData(NSFont* ns_font,
+ float size,
+ bool synthetic_bold,
+ bool synthetic_italic,
+ FontOrientation orientation,
+ FontVariationSettings* variation_settings)
+ : text_size_(size),
+ synthetic_bold_(synthetic_bold),
+ synthetic_italic_(synthetic_italic),
+ orientation_(orientation),
+ is_hash_table_deleted_value_(false) {
+ DCHECK(ns_font);
+ sk_sp<SkTypeface> typeface;
+ if (CanLoadInProcess(ns_font)) {
+ typeface.reset(SkCreateTypefaceFromCTFont(toCTFontRef(ns_font)));
+ } else {
+ // In process loading fails for cases where third party font manager
+ // software registers fonts in non system locations such as /Library/Fonts
+ // and ~/Library Fonts, see crbug.com/72727 or crbug.com/108645.
+ typeface = LoadFromBrowserProcess(ns_font, size);
+ }
+
+ if (variation_settings && variation_settings->size() < UINT16_MAX) {
+ SkFontArguments::Axis axes[variation_settings->size()];
+ for (size_t i = 0; i < variation_settings->size(); ++i) {
+ AtomicString feature_tag = variation_settings->at(i).Tag();
+ axes[i] = {AtomicStringToFourByteTag(feature_tag),
+ SkFloatToScalar(variation_settings->at(i).Value())};
+ }
+ sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
+ // TODO crbug.com/670246: Refactor this to a future Skia API that acccepts
+ // axis parameters on system fonts directly.
+ typeface = fm->makeFromStream(
+ typeface->openStream(nullptr)->duplicate(),
+ SkFontArguments().setAxes(axes, variation_settings->size()));
+ }
+ // TODO(vmpstr): Save the creation parameters in PaintTypeface instead.
+ paint_typeface_ = PaintTypeface::FromSkTypeface(typeface);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/ng_text_fragment_paint_info.h b/chromium/third_party/blink/renderer/platform/fonts/ng_text_fragment_paint_info.h
new file mode 100644
index 00000000000..e4b58a8a90b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/ng_text_fragment_paint_info.h
@@ -0,0 +1,31 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_NG_TEXT_FRAGMENT_PAINT_INFO_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_NG_TEXT_FRAGMENT_PAINT_INFO_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_view.h"
+
+namespace blink {
+
+class ShapeResult;
+
+// Bridge struct for painting text. Encapsulates info needed by the paint code.
+struct PLATFORM_EXPORT NGTextFragmentPaintInfo {
+ // The string to paint. May include surrounding context.
+ const StringView text;
+
+ // The range of the |text| to paint.
+ unsigned from;
+ unsigned to;
+
+ // The |shape_result| may not contain all characters of the |text|, but is
+ // guaranteed to contain |from| to |to|.
+ const ShapeResult* shape_result;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_NG_TEXT_FRAGMENT_PAINT_INFO_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/opentype/font_format_check.cc b/chromium/third_party/blink/renderer/platform/fonts/opentype/font_format_check.cc
new file mode 100644
index 00000000000..1136892fb01
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/opentype/font_format_check.cc
@@ -0,0 +1,63 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/opentype/font_format_check.h"
+
+#include "SkTypeface.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+// Include HarfBuzz to have a cross-platform way to retrieve table tags without
+// having to rely on the platform being able to instantiate this font format.
+#include <hb.h>
+
+namespace blink {
+
+FontFormatCheck::FontFormatCheck(sk_sp<SkData> sk_data) {
+ std::unique_ptr<hb_blob_t, std::function<void(hb_blob_t*)>> font_blob(
+ hb_blob_create(reinterpret_cast<const char*>(sk_data->bytes()),
+ sk_data->size(), HB_MEMORY_MODE_READONLY, nullptr,
+ nullptr),
+ [](hb_blob_t* blob) { hb_blob_destroy(blob); });
+ std::unique_ptr<hb_face_t, std::function<void(hb_face_t*)>> face(
+ hb_face_create(font_blob.get(), 0),
+ [](hb_face_t* face) { hb_face_destroy(face); });
+
+ unsigned table_count = 0;
+ table_count = hb_face_get_table_tags(face.get(), 0, nullptr, nullptr);
+ table_tags_.resize(table_count);
+ if (!hb_face_get_table_tags(face.get(), 0, &table_count, table_tags_.data()))
+ table_tags_.resize(0);
+}
+
+bool FontFormatCheck::IsVariableFont() {
+ return table_tags_.size() && table_tags_.Contains(HB_TAG('f', 'v', 'a', 'r'));
+}
+
+bool FontFormatCheck::IsCbdtCblcColorFont() {
+ return table_tags_.size() &&
+ table_tags_.Contains(HB_TAG('C', 'B', 'D', 'T')) &&
+ table_tags_.Contains(HB_TAG('C', 'B', 'L', 'C'));
+}
+
+bool FontFormatCheck::IsSbixColorFont() {
+ return table_tags_.size() && table_tags_.Contains(HB_TAG('s', 'b', 'i', 'x'));
+}
+
+bool FontFormatCheck::IsCff2OutlineFont() {
+ return table_tags_.size() && table_tags_.Contains(HB_TAG('C', 'F', 'F', '2'));
+}
+
+FontFormatCheck::VariableFontSubType FontFormatCheck::ProbeVariableFont(
+ sk_sp<SkTypeface> typeface) {
+ if (!typeface->getTableSize(
+ SkFontTableTag(SkSetFourByteTag('f', 'v', 'a', 'r'))))
+ return VariableFontSubType::kNotVariable;
+
+ if (typeface->getTableSize(
+ SkFontTableTag(SkSetFourByteTag('C', 'F', 'F', '2'))))
+ return VariableFontSubType::kVariableCFF2;
+ return VariableFontSubType::kVariableTrueType;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/opentype/font_format_check.h b/chromium/third_party/blink/renderer/platform/fonts/opentype/font_format_check.h
new file mode 100644
index 00000000000..88ed07b4552
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/opentype/font_format_check.h
@@ -0,0 +1,39 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_OPENTYPE_FONT_FORMAT_CHECK_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_OPENTYPE_FONT_FORMAT_CHECK_H_
+
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "third_party/skia/include/core/SkData.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+#include "third_party/skia/include/core/SkTypeface.h"
+
+namespace blink {
+
+class FontFormatCheck {
+ public:
+ FontFormatCheck(sk_sp<SkData>);
+ bool IsVariableFont();
+ bool IsCbdtCblcColorFont();
+ bool IsSbixColorFont();
+ bool IsCff2OutlineFont();
+
+ // Still needed in FontCustomPlatformData.
+ enum class VariableFontSubType {
+ kNotVariable,
+ kVariableTrueType,
+ kVariableCFF2
+ };
+
+ static VariableFontSubType ProbeVariableFont(sk_sp<SkTypeface>);
+
+ private:
+ // hb-common.h: typedef uint32_t hb_tag_t;
+ Vector<uint32_t> table_tags_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/fonts/opentype/font_settings.cc b/chromium/third_party/blink/renderer/platform/fonts/opentype/font_settings.cc
new file mode 100644
index 00000000000..d8208acb87b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/opentype/font_settings.cc
@@ -0,0 +1,34 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/opentype/font_settings.h"
+
+#include "third_party/blink/renderer/platform/wtf/hash_functions.h"
+#include "third_party/blink/renderer/platform/wtf/string_hasher.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+
+namespace blink {
+
+uint32_t AtomicStringToFourByteTag(AtomicString tag) {
+ DCHECK_EQ(tag.length(), 4u);
+ return (((tag[0]) << 24) | ((tag[1]) << 16) | ((tag[2]) << 8) | (tag[3]));
+}
+
+unsigned FontVariationSettings::GetHash() const {
+ unsigned computed_hash = size() ? 5381 : 0;
+ unsigned num_features = size();
+ for (unsigned i = 0; i < num_features; ++i) {
+ StringHasher string_hasher;
+ const AtomicString& tag = at(i).Tag();
+ for (unsigned j = 0; j < tag.length(); j++) {
+ string_hasher.AddCharacter(tag[j]);
+ }
+ WTF::AddIntToHash(computed_hash, string_hasher.GetHash());
+ WTF::AddFloatToHash(computed_hash, at(i).Value());
+ }
+ return computed_hash;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/opentype/font_settings.h b/chromium/third_party/blink/renderer/platform/fonts/opentype/font_settings.h
new file mode 100644
index 00000000000..fdf9ff8a498
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/opentype/font_settings.h
@@ -0,0 +1,108 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_OPENTYPE_FONT_SETTINGS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_OPENTYPE_FONT_SETTINGS_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+uint32_t AtomicStringToFourByteTag(AtomicString tag);
+
+template <typename T>
+class FontTagValuePair {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ FontTagValuePair(const AtomicString& tag, T value)
+ : tag_(tag), value_(value){};
+ bool operator==(const FontTagValuePair& other) const {
+ return tag_ == other.tag_ && value_ == other.value_;
+ };
+
+ const AtomicString& Tag() const { return tag_; }
+ T Value() const { return value_; }
+
+ private:
+ AtomicString tag_;
+ const T value_;
+};
+
+template <typename T>
+class FontSettings {
+ public:
+ void Append(const T& feature) { list_.push_back(feature); }
+ size_t size() const { return list_.size(); }
+ const T& operator[](int index) const { return list_[index]; }
+ const T& at(size_t index) const { return list_.at(index); }
+ bool operator==(const FontSettings& other) const {
+ return list_ == other.list_;
+ };
+ String ToString() const {
+ StringBuilder builder;
+ size_t num_features = size();
+ for (size_t i = 0; i < num_features; ++i) {
+ if (i > 0)
+ builder.Append(",");
+ const AtomicString& tag = at(i).Tag();
+ builder.Append(tag);
+ builder.Append("=");
+ builder.AppendNumber(at(i).Value());
+ }
+ return builder.ToString();
+ }
+
+ protected:
+ FontSettings() = default;
+
+ private:
+ Vector<T, 0> list_;
+
+ DISALLOW_COPY_AND_ASSIGN(FontSettings);
+};
+
+using FontFeature = FontTagValuePair<int>;
+using FontVariationAxis = FontTagValuePair<float>;
+
+class PLATFORM_EXPORT FontFeatureSettings
+ : public FontSettings<FontFeature>,
+ public RefCounted<FontFeatureSettings> {
+ DISALLOW_COPY_AND_ASSIGN(FontFeatureSettings);
+
+ public:
+ static scoped_refptr<FontFeatureSettings> Create() {
+ return base::AdoptRef(new FontFeatureSettings());
+ }
+
+ private:
+ FontFeatureSettings() = default;
+};
+
+class PLATFORM_EXPORT FontVariationSettings
+ : public FontSettings<FontVariationAxis>,
+ public RefCounted<FontVariationSettings> {
+ DISALLOW_COPY_AND_ASSIGN(FontVariationSettings);
+
+ public:
+ static scoped_refptr<FontVariationSettings> Create() {
+ return base::AdoptRef(new FontVariationSettings());
+ }
+
+ unsigned GetHash() const;
+
+ private:
+ FontVariationSettings() = default;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_OPENTYPE_FONT_SETTINGS_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/opentype/font_settings_test.cc b/chromium/third_party/blink/renderer/platform/fonts/opentype/font_settings_test.cc
new file mode 100644
index 00000000000..bf748bf1b96
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/opentype/font_settings_test.cc
@@ -0,0 +1,64 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/opentype/font_settings.h"
+
+#include "base/memory/scoped_refptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+namespace {
+
+template <typename T, typename U>
+scoped_refptr<T> MakeSettings(std::initializer_list<U> items) {
+ scoped_refptr<T> settings = T::Create();
+ for (auto item = items.begin(); item != items.end(); ++item) {
+ settings->Append(*item);
+ }
+ return settings;
+}
+
+} // namespace
+
+TEST(FontSettingsTest, HashTest) {
+ scoped_refptr<FontVariationSettings> one_axis_a =
+ MakeSettings<FontVariationSettings, FontVariationAxis>(
+ {FontVariationAxis{"a ", 0}});
+ scoped_refptr<FontVariationSettings> one_axis_b =
+ MakeSettings<FontVariationSettings, FontVariationAxis>(
+ {FontVariationAxis{"b ", 0}});
+ scoped_refptr<FontVariationSettings> two_axes =
+ MakeSettings<FontVariationSettings, FontVariationAxis>(
+ {FontVariationAxis{"a ", 0}, FontVariationAxis{"b ", 0}});
+ scoped_refptr<FontVariationSettings> two_axes_different_value =
+ MakeSettings<FontVariationSettings, FontVariationAxis>(
+ {FontVariationAxis{"a ", 0}, FontVariationAxis{"b ", 1}});
+
+ scoped_refptr<FontVariationSettings> empty_variation_settings =
+ FontVariationSettings::Create();
+
+ CHECK_NE(one_axis_a->GetHash(), one_axis_b->GetHash());
+ CHECK_NE(one_axis_a->GetHash(), two_axes->GetHash());
+ CHECK_NE(one_axis_a->GetHash(), two_axes_different_value->GetHash());
+ CHECK_NE(empty_variation_settings->GetHash(), one_axis_a->GetHash());
+ CHECK_EQ(empty_variation_settings->GetHash(), 0u);
+};
+
+TEST(FontSettingsTest, ToString) {
+ {
+ scoped_refptr<FontVariationSettings> settings =
+ MakeSettings<FontVariationSettings, FontVariationAxis>(
+ {FontVariationAxis{"a", 42}, FontVariationAxis{"b", 8118}});
+ EXPECT_EQ("a=42,b=8118", settings->ToString());
+ }
+ {
+ scoped_refptr<FontFeatureSettings> settings =
+ MakeSettings<FontFeatureSettings, FontFeature>(
+ {FontFeature{"a", 42}, FontFeature{"b", 8118}});
+ EXPECT_EQ("a=42,b=8118", settings->ToString());
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.cc b/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.cc
new file mode 100644
index 00000000000..5b5ec7dedde
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.cc
@@ -0,0 +1,162 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.h"
+
+namespace blink {
+
+OpenTypeCapsSupport::OpenTypeCapsSupport()
+ : harf_buzz_face_(nullptr),
+ requested_caps_(FontDescription::kCapsNormal),
+ font_support_(FontSupport::kFull),
+ caps_synthesis_(CapsSynthesis::kNone) {}
+
+OpenTypeCapsSupport::OpenTypeCapsSupport(
+ const HarfBuzzFace* harf_buzz_face,
+ FontDescription::FontVariantCaps requested_caps,
+ hb_script_t script)
+ : harf_buzz_face_(harf_buzz_face),
+ requested_caps_(requested_caps),
+ font_support_(FontSupport::kFull),
+ caps_synthesis_(CapsSynthesis::kNone) {
+ if (requested_caps != FontDescription::kCapsNormal)
+ DetermineFontSupport(script);
+}
+
+FontDescription::FontVariantCaps OpenTypeCapsSupport::FontFeatureToUse(
+ SmallCapsIterator::SmallCapsBehavior source_text_case) {
+ if (font_support_ == FontSupport::kFull)
+ return requested_caps_;
+
+ if (font_support_ == FontSupport::kFallback) {
+ if (requested_caps_ == FontDescription::FontVariantCaps::kAllPetiteCaps)
+ return FontDescription::FontVariantCaps::kAllSmallCaps;
+
+ if (requested_caps_ == FontDescription::FontVariantCaps::kPetiteCaps ||
+ (requested_caps_ == FontDescription::FontVariantCaps::kUnicase &&
+ source_text_case == SmallCapsIterator::kSmallCapsSameCase))
+ return FontDescription::FontVariantCaps::kSmallCaps;
+ }
+
+ return FontDescription::FontVariantCaps::kCapsNormal;
+}
+
+bool OpenTypeCapsSupport::NeedsRunCaseSplitting() {
+ // Lack of titling case support is ignored, titling case is not synthesized.
+ return font_support_ != FontSupport::kFull &&
+ requested_caps_ != FontDescription::kTitlingCaps;
+}
+
+bool OpenTypeCapsSupport::NeedsSyntheticFont(
+ SmallCapsIterator::SmallCapsBehavior run_case) {
+ if (font_support_ == FontSupport::kFull)
+ return false;
+
+ if (requested_caps_ == FontDescription::kTitlingCaps)
+ return false;
+
+ if (font_support_ == FontSupport::kNone) {
+ if (run_case == SmallCapsIterator::kSmallCapsUppercaseNeeded &&
+ (caps_synthesis_ == CapsSynthesis::kLowerToSmallCaps ||
+ caps_synthesis_ == CapsSynthesis::kBothToSmallCaps))
+ return true;
+
+ if (run_case == SmallCapsIterator::kSmallCapsSameCase &&
+ (caps_synthesis_ == CapsSynthesis::kUpperToSmallCaps ||
+ caps_synthesis_ == CapsSynthesis::kBothToSmallCaps)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+CaseMapIntend OpenTypeCapsSupport::NeedsCaseChange(
+ SmallCapsIterator::SmallCapsBehavior run_case) {
+ CaseMapIntend case_map_intend = CaseMapIntend::kKeepSameCase;
+
+ if (font_support_ == FontSupport::kFull)
+ return case_map_intend;
+
+ switch (run_case) {
+ case SmallCapsIterator::kSmallCapsSameCase:
+ case_map_intend =
+ font_support_ == FontSupport::kFallback &&
+ (caps_synthesis_ == CapsSynthesis::kBothToSmallCaps ||
+ caps_synthesis_ == CapsSynthesis::kUpperToSmallCaps)
+ ? CaseMapIntend::kLowerCase
+ : CaseMapIntend::kKeepSameCase;
+ break;
+ case SmallCapsIterator::kSmallCapsUppercaseNeeded:
+ case_map_intend =
+ font_support_ != FontSupport::kFallback &&
+ (caps_synthesis_ == CapsSynthesis::kLowerToSmallCaps ||
+ caps_synthesis_ == CapsSynthesis::kBothToSmallCaps)
+ ? CaseMapIntend::kUpperCase
+ : CaseMapIntend::kKeepSameCase;
+ break;
+ default:
+ break;
+ }
+ return case_map_intend;
+}
+
+void OpenTypeCapsSupport::DetermineFontSupport(hb_script_t script) {
+ switch (requested_caps_) {
+ case FontDescription::kSmallCaps:
+ if (!SupportsOpenTypeFeature(script, HB_TAG('s', 'm', 'c', 'p'))) {
+ font_support_ = FontSupport::kNone;
+ caps_synthesis_ = CapsSynthesis::kLowerToSmallCaps;
+ }
+ break;
+ case FontDescription::kAllSmallCaps:
+ if (!(SupportsOpenTypeFeature(script, HB_TAG('s', 'm', 'c', 'p')) &&
+ SupportsOpenTypeFeature(script, HB_TAG('c', '2', 's', 'c')))) {
+ font_support_ = FontSupport::kNone;
+ caps_synthesis_ = CapsSynthesis::kBothToSmallCaps;
+ }
+ break;
+ case FontDescription::kPetiteCaps:
+ if (!SupportsOpenTypeFeature(script, HB_TAG('p', 'c', 'a', 'p'))) {
+ if (SupportsOpenTypeFeature(script, HB_TAG('s', 'm', 'c', 'p'))) {
+ font_support_ = FontSupport::kFallback;
+ } else {
+ font_support_ = FontSupport::kNone;
+ caps_synthesis_ = CapsSynthesis::kLowerToSmallCaps;
+ }
+ }
+ break;
+ case FontDescription::kAllPetiteCaps:
+ if (!(SupportsOpenTypeFeature(script, HB_TAG('p', 'c', 'a', 'p')) &&
+ SupportsOpenTypeFeature(script, HB_TAG('c', '2', 'p', 'c')))) {
+ if (SupportsOpenTypeFeature(script, HB_TAG('s', 'm', 'c', 'p')) &&
+ SupportsOpenTypeFeature(script, HB_TAG('c', '2', 's', 'c'))) {
+ font_support_ = FontSupport::kFallback;
+ } else {
+ font_support_ = FontSupport::kNone;
+ caps_synthesis_ = CapsSynthesis::kBothToSmallCaps;
+ }
+ }
+ break;
+ case FontDescription::kUnicase:
+ if (!SupportsOpenTypeFeature(script, HB_TAG('u', 'n', 'i', 'c'))) {
+ caps_synthesis_ = CapsSynthesis::kUpperToSmallCaps;
+ if (SupportsOpenTypeFeature(script, HB_TAG('s', 'm', 'c', 'p'))) {
+ font_support_ = FontSupport::kFallback;
+ } else {
+ font_support_ = FontSupport::kNone;
+ }
+ }
+ break;
+ case FontDescription::kTitlingCaps:
+ if (!SupportsOpenTypeFeature(script, HB_TAG('t', 'i', 't', 'l'))) {
+ font_support_ = FontSupport::kNone;
+ }
+ break;
+ default:
+ NOTREACHED();
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.h b/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.h
new file mode 100644
index 00000000000..16dd277863e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.h
@@ -0,0 +1,57 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_OPENTYPE_OPEN_TYPE_CAPS_SUPPORT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_OPENTYPE_OPEN_TYPE_CAPS_SUPPORT_H_
+
+#include "third_party/blink/renderer/platform/fonts/font_description.h"
+#include "third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/case_mapping_harf_buzz_buffer_filler.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_face.h"
+#include "third_party/blink/renderer/platform/fonts/small_caps_iterator.h"
+
+#include <hb.h>
+
+namespace blink {
+
+class PLATFORM_EXPORT OpenTypeCapsSupport {
+ public:
+ OpenTypeCapsSupport();
+ OpenTypeCapsSupport(const HarfBuzzFace*,
+ FontDescription::FontVariantCaps requested_caps,
+ hb_script_t);
+
+ bool NeedsRunCaseSplitting();
+ bool NeedsSyntheticFont(SmallCapsIterator::SmallCapsBehavior run_case);
+ FontDescription::FontVariantCaps FontFeatureToUse(
+ SmallCapsIterator::SmallCapsBehavior run_case);
+ CaseMapIntend NeedsCaseChange(SmallCapsIterator::SmallCapsBehavior run_case);
+
+ private:
+ void DetermineFontSupport(hb_script_t);
+ bool SupportsOpenTypeFeature(hb_script_t, uint32_t tag) const;
+
+ const HarfBuzzFace* harf_buzz_face_;
+ FontDescription::FontVariantCaps requested_caps_;
+
+ enum class FontSupport {
+ kFull,
+ kFallback, // Fall back to 'smcp' or 'smcp' + 'c2sc'
+ kNone
+ };
+
+ enum class CapsSynthesis {
+ kNone,
+ kLowerToSmallCaps,
+ kUpperToSmallCaps,
+ kBothToSmallCaps
+ };
+
+ FontSupport font_support_;
+ CapsSynthesis caps_synthesis_;
+};
+
+}; // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support_mpl.cc b/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support_mpl.cc
new file mode 100644
index 00000000000..41feeb4d1b3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support_mpl.cc
@@ -0,0 +1,52 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.h"
+
+#include <hb-ot.h>
+
+namespace blink {
+
+bool OpenTypeCapsSupport::SupportsOpenTypeFeature(hb_script_t script,
+ uint32_t tag) const {
+ hb_face_t* face = hb_font_get_face(
+ harf_buzz_face_->GetScaledFont(nullptr, HarfBuzzFace::NoVerticalLayout));
+ DCHECK(face);
+
+ DCHECK(
+ (tag == HB_TAG('s', 'm', 'c', 'p') || tag == HB_TAG('c', '2', 's', 'c') ||
+ tag == HB_TAG('p', 'c', 'a', 'p') || tag == HB_TAG('c', '2', 'p', 'c') ||
+ tag == HB_TAG('s', 'u', 'p', 's') || tag == HB_TAG('s', 'u', 'b', 's') ||
+ tag == HB_TAG('t', 'i', 't', 'l') || tag == HB_TAG('u', 'n', 'i', 'c') ||
+ tag == HB_TAG('v', 'e', 'r', 't')));
+
+ if (!hb_ot_layout_has_substitution(face))
+ return false;
+
+ // Get the OpenType tag(s) that match this script code
+ hb_tag_t script_tags[] = {
+ HB_TAG_NONE, HB_TAG_NONE, HB_TAG_NONE,
+ };
+ hb_ot_tags_from_script(static_cast<hb_script_t>(script), &script_tags[0],
+ &script_tags[1]);
+
+ const hb_tag_t kGSUB = HB_TAG('G', 'S', 'U', 'B');
+ unsigned script_index = 0;
+ // Identify for which script a GSUB table is available.
+ hb_ot_layout_table_choose_script(face, kGSUB, script_tags, &script_index,
+ nullptr);
+
+ if (hb_ot_layout_language_find_feature(face, kGSUB, script_index,
+ HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX,
+ tag, nullptr)) {
+ return true;
+ }
+ return false;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_types.h b/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_types.h
new file mode 100644
index 00000000000..607959e7fbc
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_types.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2012 Koji Ishii <kojiishi@gmail.com>
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_OPENTYPE_OPEN_TYPE_TYPES_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_OPENTYPE_OPEN_TYPE_TYPES_H_
+
+#include "third_party/blink/renderer/platform/wtf/byte_order.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+namespace OpenType {
+
+struct Int16 {
+ DISALLOW_NEW();
+ Int16(int16_t u) : v(htons(static_cast<uint16_t>(u))) {}
+ operator int16_t() const { return static_cast<int16_t>(ntohs(v)); }
+ uint16_t v; // in BigEndian
+};
+
+struct UInt16 {
+ DISALLOW_NEW();
+ UInt16(uint16_t u) : v(htons(u)) {}
+ operator uint16_t() const { return ntohs(v); }
+ uint16_t v; // in BigEndian
+};
+
+struct Int32 {
+ DISALLOW_NEW();
+ Int32(int32_t u) : v(htonl(static_cast<uint32_t>(u))) {}
+ operator int32_t() const { return static_cast<int32_t>(ntohl(v)); }
+ uint32_t v; // in BigEndian
+};
+
+struct UInt32 {
+ DISALLOW_NEW();
+ UInt32(uint32_t u) : v(htonl(u)) {}
+ operator uint32_t() const { return ntohl(v); }
+ uint32_t v; // in BigEndian
+};
+
+typedef UInt32 Fixed;
+typedef UInt16 Offset;
+typedef UInt16 GlyphID;
+
+template <typename T>
+static const T* ValidateTable(const Vector<char>& buffer, size_t count = 1) {
+ if (buffer.size() < sizeof(T) * count)
+ return nullptr;
+ return reinterpret_cast<const T*>(buffer.data());
+}
+
+struct TableBase {
+ DISALLOW_NEW();
+
+ protected:
+ static bool IsValidEnd(const Vector<char>& buffer, const void* position) {
+ if (position < buffer.data())
+ return false;
+ size_t offset = reinterpret_cast<const char*>(position) - buffer.data();
+ return offset <= buffer.size(); // "<=" because end is included as valid
+ }
+
+ template <typename T>
+ static const T* ValidatePtr(const Vector<char>& buffer,
+ const void* position) {
+ const T* casted = reinterpret_cast<const T*>(position);
+ if (!IsValidEnd(buffer, &casted[1]))
+ return nullptr;
+ return casted;
+ }
+
+ template <typename T>
+ const T* ValidateOffset(const Vector<char>& buffer, uint16_t offset) const {
+ return ValidatePtr<T>(buffer,
+ reinterpret_cast<const int8_t*>(this) + offset);
+ }
+};
+
+} // namespace OpenType
+} // namespace blink
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_OPENTYPE_OPEN_TYPE_TYPES_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_vertical_data.cc b/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_vertical_data.cc
new file mode 100644
index 00000000000..6acc43a187d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_vertical_data.cc
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2012 Koji Ishii <kojiishi@gmail.com>
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/fonts/opentype/open_type_vertical_data.h"
+
+#include "SkTypeface.h"
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/fonts/opentype/open_type_types.h"
+#include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
+#include "third_party/blink/renderer/platform/fonts/skia/skia_text_metrics.h"
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+
+namespace blink {
+namespace OpenType {
+
+// The input characters are big-endian (first is most significant).
+#define OT_MAKE_TAG(ch1, ch2, ch3, ch4) \
+ ((((uint32_t)(ch1)) << 24) | (((uint32_t)(ch2)) << 16) | \
+ (((uint32_t)(ch3)) << 8) | ((uint32_t)(ch4)))
+
+const SkFontTableTag kHheaTag = OT_MAKE_TAG('h', 'h', 'e', 'a');
+const SkFontTableTag kHmtxTag = OT_MAKE_TAG('h', 'm', 't', 'x');
+const SkFontTableTag kVheaTag = OT_MAKE_TAG('v', 'h', 'e', 'a');
+const SkFontTableTag kVmtxTag = OT_MAKE_TAG('v', 'm', 't', 'x');
+const SkFontTableTag kVORGTag = OT_MAKE_TAG('V', 'O', 'R', 'G');
+
+#pragma pack(1)
+
+struct HheaTable {
+ DISALLOW_NEW();
+ OpenType::Fixed version;
+ OpenType::Int16 ascender;
+ OpenType::Int16 descender;
+ OpenType::Int16 line_gap;
+ OpenType::Int16 advance_width_max;
+ OpenType::Int16 min_left_side_bearing;
+ OpenType::Int16 min_right_side_bearing;
+ OpenType::Int16 x_max_extent;
+ OpenType::Int16 caret_slope_rise;
+ OpenType::Int16 caret_slope_run;
+ OpenType::Int16 caret_offset;
+ OpenType::Int16 reserved[4];
+ OpenType::Int16 metric_data_format;
+ OpenType::UInt16 number_of_h_metrics;
+};
+
+struct VheaTable {
+ DISALLOW_NEW();
+ OpenType::Fixed version;
+ OpenType::Int16 ascent;
+ OpenType::Int16 descent;
+ OpenType::Int16 line_gap;
+ OpenType::Int16 advance_height_max;
+ OpenType::Int16 min_top_side_bearing;
+ OpenType::Int16 min_bottom_side_bearing;
+ OpenType::Int16 y_max_extent;
+ OpenType::Int16 caret_slope_rise;
+ OpenType::Int16 caret_slope_run;
+ OpenType::Int16 caret_offset;
+ OpenType::Int16 reserved[4];
+ OpenType::Int16 metric_data_format;
+ OpenType::UInt16 num_of_long_ver_metrics;
+};
+
+struct HmtxTable {
+ DISALLOW_NEW();
+ struct Entry {
+ DISALLOW_NEW();
+ OpenType::UInt16 advance_width;
+ OpenType::Int16 lsb;
+ } entries[1];
+};
+
+struct VmtxTable {
+ DISALLOW_NEW();
+ struct Entry {
+ DISALLOW_NEW();
+ OpenType::UInt16 advance_height;
+ OpenType::Int16 top_side_bearing;
+ } entries[1];
+};
+
+struct VORGTable {
+ DISALLOW_NEW();
+ OpenType::UInt16 major_version;
+ OpenType::UInt16 minor_version;
+ OpenType::Int16 default_vert_origin_y;
+ OpenType::UInt16 num_vert_origin_y_metrics;
+ struct VertOriginYMetrics {
+ DISALLOW_NEW();
+ OpenType::UInt16 glyph_index;
+ OpenType::Int16 vert_origin_y;
+ } vert_origin_y_metrics[1];
+
+ size_t RequiredSize() const {
+ return sizeof(*this) +
+ sizeof(VertOriginYMetrics) * (num_vert_origin_y_metrics - 1);
+ }
+};
+
+#pragma pack()
+
+} // namespace OpenType
+
+OpenTypeVerticalData::OpenTypeVerticalData(sk_sp<SkTypeface> typeface)
+ : default_vert_origin_y_(0),
+ size_per_unit_(0),
+ ascent_fallback_(0),
+ height_fallback_(0) {
+ LoadMetrics(typeface);
+}
+
+static void CopyOpenTypeTable(sk_sp<SkTypeface> typeface,
+ SkFontTableTag tag,
+ Vector<char>& destination) {
+ const size_t table_size = typeface->getTableSize(tag);
+ destination.resize(table_size);
+ if (table_size) {
+ typeface->getTableData(tag, 0, table_size, destination.data());
+ }
+}
+
+void OpenTypeVerticalData::LoadMetrics(sk_sp<SkTypeface> typeface) {
+ // Load hhea and hmtx to get x-component of vertical origins.
+ // If these tables are missing, it's not an OpenType font.
+ Vector<char> buffer;
+ CopyOpenTypeTable(typeface, OpenType::kHheaTag, buffer);
+ const OpenType::HheaTable* hhea =
+ OpenType::ValidateTable<OpenType::HheaTable>(buffer);
+ if (!hhea)
+ return;
+ uint16_t count_hmtx_entries = hhea->number_of_h_metrics;
+ if (!count_hmtx_entries) {
+ DLOG(ERROR) << "Invalid numberOfHMetrics";
+ return;
+ }
+
+ CopyOpenTypeTable(typeface, OpenType::kHmtxTag, buffer);
+ const OpenType::HmtxTable* hmtx =
+ OpenType::ValidateTable<OpenType::HmtxTable>(buffer, count_hmtx_entries);
+ if (!hmtx) {
+ DLOG(ERROR) << "hhea exists but hmtx does not (or broken)";
+ return;
+ }
+ advance_widths_.resize(count_hmtx_entries);
+ for (uint16_t i = 0; i < count_hmtx_entries; ++i)
+ advance_widths_[i] = hmtx->entries[i].advance_width;
+
+ // Load vhea first. This table is required for fonts that support vertical
+ // flow.
+ CopyOpenTypeTable(typeface, OpenType::kVheaTag, buffer);
+ const OpenType::VheaTable* vhea =
+ OpenType::ValidateTable<OpenType::VheaTable>(buffer);
+ if (!vhea)
+ return;
+ uint16_t count_vmtx_entries = vhea->num_of_long_ver_metrics;
+ if (!count_vmtx_entries) {
+ DLOG(ERROR) << "Invalid numOfLongVerMetrics";
+ return;
+ }
+
+ // Load VORG. This table is optional.
+ CopyOpenTypeTable(typeface, OpenType::kVORGTag, buffer);
+ const OpenType::VORGTable* vorg =
+ OpenType::ValidateTable<OpenType::VORGTable>(buffer);
+ if (vorg && buffer.size() >= vorg->RequiredSize()) {
+ default_vert_origin_y_ = vorg->default_vert_origin_y;
+ uint16_t count_vert_origin_y_metrics = vorg->num_vert_origin_y_metrics;
+ if (!count_vert_origin_y_metrics) {
+ // Add one entry so that hasVORG() becomes true
+ vert_origin_y_.Set(0, default_vert_origin_y_);
+ } else {
+ for (uint16_t i = 0; i < count_vert_origin_y_metrics; ++i) {
+ const OpenType::VORGTable::VertOriginYMetrics& metrics =
+ vorg->vert_origin_y_metrics[i];
+ vert_origin_y_.Set(metrics.glyph_index, metrics.vert_origin_y);
+ }
+ }
+ }
+
+ // Load vmtx then. This table is required for fonts that support vertical
+ // flow.
+ CopyOpenTypeTable(typeface, OpenType::kVmtxTag, buffer);
+ const OpenType::VmtxTable* vmtx =
+ OpenType::ValidateTable<OpenType::VmtxTable>(buffer, count_vmtx_entries);
+ if (!vmtx) {
+ DLOG(ERROR) << "vhea exists but vmtx does not (or broken)";
+ return;
+ }
+ advance_heights_.resize(count_vmtx_entries);
+ for (uint16_t i = 0; i < count_vmtx_entries; ++i)
+ advance_heights_[i] = vmtx->entries[i].advance_height;
+
+ // VORG is preferred way to calculate vertical origin than vmtx,
+ // so load topSideBearing from vmtx only if VORG is missing.
+ if (HasVORG())
+ return;
+
+ size_t size_extra =
+ buffer.size() - sizeof(OpenType::VmtxTable::Entry) * count_vmtx_entries;
+ if (size_extra % sizeof(OpenType::Int16)) {
+ DLOG(ERROR) << "vmtx has incorrect tsb count";
+ return;
+ }
+ size_t count_top_side_bearings =
+ count_vmtx_entries + size_extra / sizeof(OpenType::Int16);
+ top_side_bearings_.resize(count_top_side_bearings);
+ size_t i;
+ for (i = 0; i < count_vmtx_entries; ++i)
+ top_side_bearings_[i] = vmtx->entries[i].top_side_bearing;
+ if (i < count_top_side_bearings) {
+ const OpenType::Int16* p_top_side_bearings_extra =
+ reinterpret_cast<const OpenType::Int16*>(
+ &vmtx->entries[count_vmtx_entries]);
+ for (; i < count_top_side_bearings; ++i, ++p_top_side_bearings_extra)
+ top_side_bearings_[i] = *p_top_side_bearings_extra;
+ }
+}
+
+void OpenTypeVerticalData::SetScaleAndFallbackMetrics(float size_per_unit,
+ float ascent,
+ int height) {
+ size_per_unit_ = size_per_unit;
+ ascent_fallback_ = ascent;
+ height_fallback_ = height;
+}
+
+float OpenTypeVerticalData::AdvanceHeight(Glyph glyph) const {
+ size_t count_heights = advance_heights_.size();
+ if (count_heights) {
+ uint16_t advance_f_unit =
+ advance_heights_[glyph < count_heights ? glyph : count_heights - 1];
+ float advance = advance_f_unit * size_per_unit_;
+ return advance;
+ }
+
+ // No vertical info in the font file; use height as advance.
+ return height_fallback_;
+}
+
+void OpenTypeVerticalData::GetVerticalTranslationsForGlyphs(
+ const SkPaint& paint,
+ const Glyph* glyphs,
+ size_t count,
+ float* out_xy_array) const {
+ size_t count_widths = advance_widths_.size();
+ DCHECK_GT(count_widths, 0u);
+ bool use_vorg = HasVORG();
+ size_t count_top_side_bearings = top_side_bearings_.size();
+ float default_vert_origin_y = std::numeric_limits<float>::quiet_NaN();
+ for (float *end = &(out_xy_array[count * 2]); out_xy_array != end;
+ ++glyphs, out_xy_array += 2) {
+ Glyph glyph = *glyphs;
+ uint16_t width_f_unit =
+ advance_widths_[glyph < count_widths ? glyph : count_widths - 1];
+ float width = width_f_unit * size_per_unit_;
+ out_xy_array[0] = -width / 2;
+
+ // For Y, try VORG first.
+ if (use_vorg) {
+ if (glyph) {
+ int16_t vert_origin_yf_unit = vert_origin_y_.at(glyph);
+ if (vert_origin_yf_unit) {
+ out_xy_array[1] = -vert_origin_yf_unit * size_per_unit_;
+ continue;
+ }
+ }
+ if (std::isnan(default_vert_origin_y))
+ default_vert_origin_y = -default_vert_origin_y_ * size_per_unit_;
+ out_xy_array[1] = default_vert_origin_y;
+ continue;
+ }
+
+ // If no VORG, try vmtx next.
+ if (count_top_side_bearings) {
+ int16_t top_side_bearing_f_unit =
+ top_side_bearings_[glyph < count_top_side_bearings
+ ? glyph
+ : count_top_side_bearings - 1];
+ float top_side_bearing = top_side_bearing_f_unit * size_per_unit_;
+
+ SkRect skiaBounds;
+ SkiaTextMetrics(&paint).GetSkiaBoundsForGlyph(glyph, &skiaBounds);
+ FloatRect bounds(skiaBounds);
+ out_xy_array[1] = bounds.Y() - top_side_bearing;
+ continue;
+ }
+
+ // No vertical info in the font file; use ascent as vertical origin.
+ out_xy_array[1] = -ascent_fallback_;
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_vertical_data.h b/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_vertical_data.h
new file mode 100644
index 00000000000..109609279df
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_vertical_data.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2012 Koji Ishii <kojiishi@gmail.com>
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_OPENTYPE_OPEN_TYPE_VERTICAL_DATA_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_OPENTYPE_OPEN_TYPE_VERTICAL_DATA_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/fonts/glyph.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+#include <SkPaint.h>
+#include <SkRefCnt.h>
+#include <SkTypeface.h>
+
+namespace blink {
+
+class PLATFORM_EXPORT OpenTypeVerticalData
+ : public RefCounted<OpenTypeVerticalData> {
+ public:
+ static scoped_refptr<OpenTypeVerticalData> CreateUnscaled(
+ sk_sp<SkTypeface> typeface) {
+ return base::AdoptRef(new OpenTypeVerticalData(typeface));
+ }
+
+ void SetScaleAndFallbackMetrics(float size_per_unit,
+ float ascent,
+ int height);
+
+ bool IsOpenType() const { return !advance_widths_.IsEmpty(); }
+ bool HasVerticalMetrics() const { return !advance_heights_.IsEmpty(); }
+ float AdvanceHeight(Glyph) const;
+
+ void GetVerticalTranslationsForGlyphs(const SkPaint&,
+ const Glyph*,
+ size_t,
+ float* out_xy_array) const;
+
+ private:
+ explicit OpenTypeVerticalData(sk_sp<SkTypeface>);
+
+ void LoadMetrics(sk_sp<SkTypeface>);
+ bool HasVORG() const { return !vert_origin_y_.IsEmpty(); }
+
+ HashMap<Glyph, Glyph> vertical_glyph_map_;
+ Vector<uint16_t> advance_widths_;
+ Vector<uint16_t> advance_heights_;
+ Vector<int16_t> top_side_bearings_;
+ int16_t default_vert_origin_y_;
+ HashMap<Glyph, int16_t> vert_origin_y_;
+
+ float size_per_unit_;
+ float ascent_fallback_;
+ int height_fallback_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_OPENTYPE_OPEN_TYPE_VERTICAL_DATA_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_vertical_data_test.cc b/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_vertical_data_test.cc
new file mode 100644
index 00000000000..cd03dc5d349
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_vertical_data_test.cc
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2012 Koji Ishii <kojiishi@gmail.com>
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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/memory/scoped_refptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/fonts/opentype/open_type_types.h"
+
+namespace blink {
+
+struct TestTable : OpenType::TableBase {
+ OpenType::Fixed version;
+ OpenType::Int16 ascender;
+
+ template <typename T>
+ const T* ValidateOffset(const Vector<char>& buffer, uint16_t offset) const {
+ return TableBase::ValidateOffset<T>(buffer, offset);
+ }
+};
+
+TEST(OpenTypeVerticalDataTest, ValidateTableTest) {
+ Vector<char> buffer(sizeof(TestTable));
+ const TestTable* table = OpenType::ValidateTable<TestTable>(buffer);
+ EXPECT_TRUE(table);
+
+ buffer = Vector<char>(sizeof(TestTable) - 1);
+ table = OpenType::ValidateTable<TestTable>(buffer);
+ EXPECT_FALSE(table);
+
+ buffer = Vector<char>(sizeof(TestTable) + 1);
+ table = OpenType::ValidateTable<TestTable>(buffer);
+ EXPECT_TRUE(table);
+}
+
+TEST(OpenTypeVerticalDataTest, ValidateOffsetTest) {
+ Vector<char> buffer(sizeof(TestTable));
+ const TestTable* table = OpenType::ValidateTable<TestTable>(buffer);
+ ASSERT_TRUE(table);
+
+ // Test overflow
+ EXPECT_FALSE(table->ValidateOffset<uint8_t>(buffer, 0xFFFF));
+
+ // uint8_t is valid for all offsets
+ for (uint16_t offset = 0; offset < sizeof(TestTable); offset++)
+ EXPECT_TRUE(table->ValidateOffset<uint8_t>(buffer, offset));
+ EXPECT_FALSE(table->ValidateOffset<uint8_t>(buffer, sizeof(TestTable)));
+ EXPECT_FALSE(table->ValidateOffset<uint8_t>(buffer, sizeof(TestTable) + 1));
+
+ // For uint16_t, the last byte is invalid
+ for (uint16_t offset = 0; offset < sizeof(TestTable) - 1; offset++)
+ EXPECT_TRUE(table->ValidateOffset<uint16_t>(buffer, offset));
+ EXPECT_FALSE(table->ValidateOffset<uint16_t>(buffer, sizeof(TestTable) - 1));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/orientation_iterator.cc b/chromium/third_party/blink/renderer/platform/fonts/orientation_iterator.cc
new file mode 100644
index 00000000000..fe940115cb6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/orientation_iterator.cc
@@ -0,0 +1,53 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/orientation_iterator.h"
+
+#include <memory>
+
+namespace blink {
+
+OrientationIterator::OrientationIterator(const UChar* buffer,
+ unsigned buffer_size,
+ FontOrientation run_orientation)
+ : utf16_iterator_(std::make_unique<UTF16TextIterator>(buffer, buffer_size)),
+ buffer_size_(buffer_size),
+ at_end_(buffer_size == 0) {
+ // There's not much point in segmenting by isUprightInVertical if the text
+ // orientation is not "mixed".
+ DCHECK_EQ(run_orientation, FontOrientation::kVerticalMixed);
+}
+
+bool OrientationIterator::Consume(unsigned* orientation_limit,
+ RenderOrientation* render_orientation) {
+ if (at_end_)
+ return false;
+
+ RenderOrientation current_render_orientation = kOrientationInvalid;
+ UChar32 next_u_char32;
+ while (utf16_iterator_->Consume(next_u_char32)) {
+ if (current_render_orientation == kOrientationInvalid ||
+ !Character::IsGraphemeExtended(next_u_char32)) {
+ RenderOrientation previous_render_orientation =
+ current_render_orientation;
+ current_render_orientation =
+ Character::IsUprightInMixedVertical(next_u_char32)
+ ? kOrientationKeep
+ : kOrientationRotateSideways;
+ if (previous_render_orientation != current_render_orientation &&
+ previous_render_orientation != kOrientationInvalid) {
+ *orientation_limit = utf16_iterator_->Offset();
+ *render_orientation = previous_render_orientation;
+ return true;
+ }
+ }
+ utf16_iterator_->Advance();
+ }
+ *orientation_limit = buffer_size_;
+ *render_orientation = current_render_orientation;
+ at_end_ = true;
+ return true;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/orientation_iterator.h b/chromium/third_party/blink/renderer/platform/fonts/orientation_iterator.h
new file mode 100644
index 00000000000..4ec912cfbc5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/orientation_iterator.h
@@ -0,0 +1,42 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_ORIENTATION_ITERATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_ORIENTATION_ITERATOR_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/fonts/font_orientation.h"
+#include "third_party/blink/renderer/platform/fonts/script_run_iterator.h"
+#include "third_party/blink/renderer/platform/fonts/utf16_text_iterator.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT OrientationIterator {
+ USING_FAST_MALLOC(OrientationIterator);
+ WTF_MAKE_NONCOPYABLE(OrientationIterator);
+
+ public:
+ enum RenderOrientation {
+ kOrientationKeep,
+ kOrientationRotateSideways,
+ kOrientationInvalid
+ };
+
+ OrientationIterator(const UChar* buffer,
+ unsigned buffer_size,
+ FontOrientation run_orientation);
+
+ bool Consume(unsigned* orientation_limit, RenderOrientation*);
+
+ private:
+ std::unique_ptr<UTF16TextIterator> utf16_iterator_;
+ unsigned buffer_size_;
+ bool at_end_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/fonts/orientation_iterator_test.cc b/chromium/third_party/blink/renderer/platform/fonts/orientation_iterator_test.cc
new file mode 100644
index 00000000000..52753c29b4e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/orientation_iterator_test.cc
@@ -0,0 +1,169 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/orientation_iterator.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include <string>
+
+namespace blink {
+
+struct OrientationTestRun {
+ std::string text;
+ OrientationIterator::RenderOrientation code;
+};
+
+struct OrientationExpectedRun {
+ unsigned limit;
+ OrientationIterator::RenderOrientation render_orientation;
+
+ OrientationExpectedRun(
+ unsigned the_limit,
+ OrientationIterator::RenderOrientation the_render_orientation)
+ : limit(the_limit), render_orientation(the_render_orientation) {}
+};
+
+class OrientationIteratorTest : public testing::Test {
+ protected:
+ void CheckRuns(const Vector<OrientationTestRun>& runs) {
+ String text(g_empty_string16_bit);
+ Vector<OrientationExpectedRun> expect;
+ for (auto& run : runs) {
+ text.append(String::FromUTF8(run.text.c_str()));
+ expect.push_back(OrientationExpectedRun(text.length(), run.code));
+ }
+ OrientationIterator orientation_iterator(text.Characters16(), text.length(),
+ FontOrientation::kVerticalMixed);
+ VerifyRuns(&orientation_iterator, expect);
+ }
+
+ void VerifyRuns(OrientationIterator* orientation_iterator,
+ const Vector<OrientationExpectedRun>& expect) {
+ unsigned limit;
+ OrientationIterator::RenderOrientation render_orientation;
+ unsigned long run_count = 0;
+ while (orientation_iterator->Consume(&limit, &render_orientation)) {
+ ASSERT_LT(run_count, expect.size());
+ ASSERT_EQ(expect[run_count].limit, limit);
+ ASSERT_EQ(expect[run_count].render_orientation, render_orientation);
+ ++run_count;
+ }
+ ASSERT_EQ(expect.size(), run_count);
+ }
+};
+
+// TODO(esprehn): WTF::Vector should allow initialization from a literal.
+#define CHECK_ORIENTATION(...) \
+ static const OrientationTestRun kRunsArray[] = __VA_ARGS__; \
+ Vector<OrientationTestRun> runs; \
+ runs.Append(kRunsArray, sizeof(kRunsArray) / sizeof(*kRunsArray)); \
+ CheckRuns(runs);
+
+TEST_F(OrientationIteratorTest, Empty) {
+ String empty(g_empty_string16_bit);
+ OrientationIterator orientation_iterator(empty.Characters16(), empty.length(),
+ FontOrientation::kVerticalMixed);
+ unsigned limit = 0;
+ OrientationIterator::RenderOrientation orientation =
+ OrientationIterator::kOrientationInvalid;
+ DCHECK(!orientation_iterator.Consume(&limit, &orientation));
+ ASSERT_EQ(limit, 0u);
+ ASSERT_EQ(orientation, OrientationIterator::kOrientationInvalid);
+}
+
+TEST_F(OrientationIteratorTest, OneCharLatin) {
+ CHECK_ORIENTATION({{"A", OrientationIterator::kOrientationRotateSideways}});
+}
+
+TEST_F(OrientationIteratorTest, OneAceOfSpades) {
+ CHECK_ORIENTATION({{"🂡", OrientationIterator::kOrientationKeep}});
+}
+
+TEST_F(OrientationIteratorTest, CombiningCircle) {
+ CHECK_ORIENTATION({{"◌́◌̀◌̈◌̂◌̄◌̊", OrientationIterator::kOrientationKeep}});
+}
+
+TEST_F(OrientationIteratorTest, OneEthiopicSyllable) {
+ CHECK_ORIENTATION({{"ጀ", OrientationIterator::kOrientationRotateSideways}});
+}
+
+TEST_F(OrientationIteratorTest, JapaneseLetterlikeEnd) {
+ CHECK_ORIENTATION(
+ {{"いろは", OrientationIterator::kOrientationKeep},
+ {"ℐℒℐℒℐℒℐℒℐℒℐℒℐℒ", OrientationIterator::kOrientationRotateSideways}});
+}
+
+TEST_F(OrientationIteratorTest, LetterlikeJapaneseEnd) {
+ CHECK_ORIENTATION({{"ℐ", OrientationIterator::kOrientationRotateSideways},
+ {"いろは", OrientationIterator::kOrientationKeep}});
+}
+
+TEST_F(OrientationIteratorTest, OneCharJapanese) {
+ CHECK_ORIENTATION({{"い", OrientationIterator::kOrientationKeep}});
+}
+
+TEST_F(OrientationIteratorTest, Japanese) {
+ CHECK_ORIENTATION(
+ {{"いろはにほへと", OrientationIterator::kOrientationKeep}});
+}
+
+TEST_F(OrientationIteratorTest, IVS) {
+ CHECK_ORIENTATION(
+ {{"愉\xF3\xA0\x84\x81", OrientationIterator::kOrientationKeep}});
+}
+
+TEST_F(OrientationIteratorTest, MarkAtFirstCharRotated) {
+ // Unicode General Category M should be combined with the previous base
+ // character, but they have their own orientation if they appear at the
+ // beginning of a run.
+ // http://www.unicode.org/reports/tr50/#grapheme_clusters
+ // https://drafts.csswg.org/css-writing-modes-3/#vertical-orientations
+ // U+0300 COMBINING GRAVE ACCENT is Mn (Mark, Nonspacing) with Rotated.
+ CHECK_ORIENTATION(
+ {{"\xCC\x80", OrientationIterator::kOrientationRotateSideways}});
+}
+
+TEST_F(OrientationIteratorTest, MarkAtFirstCharUpright) {
+ // U+20DD COMBINING ENCLOSING CIRCLE is Me (Mark, Enclosing) with Upright.
+ CHECK_ORIENTATION({{"\xE2\x83\x9D", OrientationIterator::kOrientationKeep}});
+}
+
+TEST_F(OrientationIteratorTest, MarksAtFirstCharUpright) {
+ // U+20DD COMBINING ENCLOSING CIRCLE is Me (Mark, Enclosing) with Upright.
+ // U+0300 COMBINING GRAVE ACCENT is Mn (Mark, Nonspacing) with Rotated.
+ CHECK_ORIENTATION(
+ {{"\xE2\x83\x9D\xCC\x80", OrientationIterator::kOrientationKeep}});
+}
+
+TEST_F(OrientationIteratorTest, MarksAtFirstCharUprightThenBase) {
+ // U+20DD COMBINING ENCLOSING CIRCLE is Me (Mark, Enclosing) with Upright.
+ // U+0300 COMBINING GRAVE ACCENT is Mn (Mark, Nonspacing) with Rotated.
+ CHECK_ORIENTATION(
+ {{"\xE2\x83\x9D\xCC\x80", OrientationIterator::kOrientationKeep},
+ {"ABC\xE2\x83\x9D", OrientationIterator::kOrientationRotateSideways}});
+}
+
+TEST_F(OrientationIteratorTest, JapaneseLatinMixedInside) {
+ CHECK_ORIENTATION({{"いろはに", OrientationIterator::kOrientationKeep},
+ {"Abc", OrientationIterator::kOrientationRotateSideways},
+ {"ほへと", OrientationIterator::kOrientationKeep}});
+}
+
+TEST_F(OrientationIteratorTest, PunctuationJapanese) {
+ CHECK_ORIENTATION({{".…¡", OrientationIterator::kOrientationRotateSideways},
+ {"ほへと", OrientationIterator::kOrientationKeep}});
+}
+
+TEST_F(OrientationIteratorTest, JapaneseLatinMixedOutside) {
+ CHECK_ORIENTATION({{"Abc", OrientationIterator::kOrientationRotateSideways},
+ {"ほへと", OrientationIterator::kOrientationKeep},
+ {"Xyz", OrientationIterator::kOrientationRotateSideways}});
+}
+
+TEST_F(OrientationIteratorTest, JapaneseMahjonggMixed) {
+ CHECK_ORIENTATION(
+ {{"いろはに🀤ほへと", OrientationIterator::kOrientationKeep}});
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/script_run_iterator.cc b/chromium/third_party/blink/renderer/platform/fonts/script_run_iterator.cc
new file mode 100644
index 00000000000..1201203ecdb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/script_run_iterator.cc
@@ -0,0 +1,365 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/script_run_iterator.h"
+
+#include <algorithm>
+#include "third_party/blink/renderer/platform/text/icu_error.h"
+#include "third_party/blink/renderer/platform/wtf/threading.h"
+
+namespace blink {
+
+typedef ScriptData::PairedBracketType PairedBracketType;
+
+const int ScriptData::kMaxScriptCount = 20;
+
+ScriptData::~ScriptData() = default;
+
+void ICUScriptData::GetScripts(UChar32 ch, Vector<UScriptCode>& dst) const {
+ ICUError status;
+ // Leave room to insert primary script. It's not strictly necessary but
+ // it ensures that the result won't ever be greater than kMaxScriptCount,
+ // which some client someday might expect.
+ dst.resize(kMaxScriptCount - 1);
+ // Note, ICU convention is to return the number of available items
+ // regardless of the capacity passed to the call. So count can be greater
+ // than dst->size(), if a later version of the unicode data has more
+ // than kMaxScriptCount items.
+ int count = uscript_getScriptExtensions(ch, &dst[0], dst.size(), &status);
+ if (status == U_BUFFER_OVERFLOW_ERROR) {
+ // Allow this, we'll just use what we have.
+ DLOG(ERROR) << "Exceeded maximum script count of " << kMaxScriptCount
+ << " for 0x" << std::hex << ch;
+ count = dst.size();
+ status = U_ZERO_ERROR;
+ }
+ UScriptCode primary_script = uscript_getScript(ch, &status);
+
+ if (U_FAILURE(status)) {
+ DLOG(ERROR) << "Could not get icu script data: " << status << " for 0x"
+ << std::hex << ch;
+ dst.clear();
+ return;
+ }
+
+ dst.resize(count);
+
+ if (primary_script == dst.at(0)) {
+ // Only one script (might be common or inherited -- these are never in
+ // the extensions unless they're the only script), or extensions are in
+ // priority order already.
+ return;
+ }
+
+ if (primary_script != USCRIPT_INHERITED && primary_script != USCRIPT_COMMON &&
+ primary_script != USCRIPT_INVALID_CODE) {
+ // Not common or primary, with extensions that are not in order. We know
+ // the primary, so we insert it at the front and swap the previous front
+ // to somewhere else in the list.
+ auto it = std::find(dst.begin() + 1, dst.end(), primary_script);
+ if (it == dst.end()) {
+ dst.push_back(primary_script);
+ }
+ std::swap(*dst.begin(), *it);
+ return;
+ }
+
+ if (primary_script == USCRIPT_COMMON) {
+ if (count == 1) {
+ // Common with a preferred script. Keep common at head.
+ dst.push_front(primary_script);
+ return;
+ }
+
+ // Ignore common. Find the preferred script of the multiple scripts that
+ // remain, and ensure it is at the head. Just keep swapping them in,
+ // there aren't likely to be many.
+ for (size_t i = 1; i < dst.size(); ++i) {
+ if (dst.at(0) == USCRIPT_LATIN || dst.at(i) < dst.at(0)) {
+ std::swap(dst.at(0), dst.at(i));
+ }
+ }
+ return;
+ }
+
+ // The primary is inherited, and there are other scripts. Put inherited at
+ // the front, the true primary next, and then the others in random order.
+ // TODO: Take into account the language of a document if available.
+ // Otherwise, use Unicode block as a tie breaker. Comparing
+ // ScriptCodes as integers is not meaningful because 'old' scripts are
+ // just sorted in alphabetic order.
+ dst.push_back(dst.at(0));
+ dst.at(0) = primary_script;
+ for (size_t i = 2; i < dst.size(); ++i) {
+ if (dst.at(1) == USCRIPT_LATIN || dst.at(i) < dst.at(1)) {
+ std::swap(dst.at(1), dst.at(i));
+ }
+ }
+}
+
+UChar32 ICUScriptData::GetPairedBracket(UChar32 ch) const {
+ return u_getBidiPairedBracket(ch);
+}
+
+PairedBracketType ICUScriptData::GetPairedBracketType(UChar32 ch) const {
+ return static_cast<PairedBracketType>(
+ u_getIntPropertyValue(ch, UCHAR_BIDI_PAIRED_BRACKET_TYPE));
+}
+
+const ICUScriptData* ICUScriptData::Instance() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(const ICUScriptData, icu_script_data_instance,
+ ());
+ return &icu_script_data_instance;
+}
+
+ScriptRunIterator::ScriptRunIterator(const UChar* text,
+ size_t length,
+ const ScriptData* data)
+ : text_(text),
+ length_(length),
+ brackets_fixup_depth_(0),
+ // The initial value of m_aheadCharacter is not used.
+ ahead_character_(0),
+ ahead_pos_(0),
+ common_preferred_(USCRIPT_COMMON),
+ script_data_(data) {
+ DCHECK(text);
+ DCHECK(data);
+
+ if (ahead_pos_ < length_) {
+ current_set_.clear();
+ // Priming the m_currentSet with USCRIPT_COMMON here so that the first
+ // resolution between m_currentSet and m_nextSet in mergeSets() leads to
+ // chosing the script of the first consumed character.
+ current_set_.push_back(USCRIPT_COMMON);
+ U16_NEXT(text_, ahead_pos_, length_, ahead_character_);
+ script_data_->GetScripts(ahead_character_, ahead_set_);
+ }
+}
+
+ScriptRunIterator::ScriptRunIterator(const UChar* text, size_t length)
+ : ScriptRunIterator(text, length, ICUScriptData::Instance()) {}
+
+bool ScriptRunIterator::Consume(unsigned& limit, UScriptCode& script) {
+ if (current_set_.IsEmpty()) {
+ return false;
+ }
+
+ size_t pos;
+ UChar32 ch;
+ while (Fetch(&pos, &ch)) {
+ PairedBracketType paired_type = script_data_->GetPairedBracketType(ch);
+ switch (paired_type) {
+ case PairedBracketType::kBracketTypeOpen:
+ OpenBracket(ch);
+ break;
+ case PairedBracketType::kBracketTypeClose:
+ CloseBracket(ch);
+ break;
+ default:
+ break;
+ }
+ if (!MergeSets()) {
+ limit = pos;
+ script = ResolveCurrentScript();
+ FixupStack(script);
+ current_set_ = next_set_;
+ return true;
+ }
+ }
+
+ limit = length_;
+ script = ResolveCurrentScript();
+ current_set_.clear();
+ return true;
+}
+
+void ScriptRunIterator::OpenBracket(UChar32 ch) {
+ if (brackets_.size() == kMaxBrackets) {
+ brackets_.pop_front();
+ if (brackets_fixup_depth_ == kMaxBrackets) {
+ --brackets_fixup_depth_;
+ }
+ }
+ brackets_.push_back(BracketRec({ch, USCRIPT_COMMON}));
+ ++brackets_fixup_depth_;
+}
+
+void ScriptRunIterator::CloseBracket(UChar32 ch) {
+ if (brackets_.size() > 0) {
+ UChar32 target = script_data_->GetPairedBracket(ch);
+ for (auto it = brackets_.rbegin(); it != brackets_.rend(); ++it) {
+ if (it->ch == target) {
+ // Have a match, use open paren's resolved script.
+ UScriptCode script = it->script;
+ next_set_.clear();
+ next_set_.push_back(script);
+
+ // And pop stack to this point.
+ int num_popped = std::distance(brackets_.rbegin(), it);
+ // TODO: No resize operation in WTF::Deque?
+ for (int i = 0; i < num_popped; ++i)
+ brackets_.pop_back();
+ brackets_fixup_depth_ = std::max(static_cast<size_t>(0),
+ brackets_fixup_depth_ - num_popped);
+ return;
+ }
+ }
+ }
+ // leave stack alone, no match
+}
+
+// Keep items in m_currentSet that are in m_nextSet.
+//
+// If the sets are disjoint, return false and leave m_currentSet unchanged. Else
+// return true and make current set the intersection. Make sure to maintain
+// current priority script as priority if it remains, else retain next priority
+// script if it remains.
+//
+// Also maintain a common preferred script. If current and next are both
+// common, and there is no common preferred script and next has a preferred
+// script, set the common preferred script to that of next.
+bool ScriptRunIterator::MergeSets() {
+ if (next_set_.IsEmpty() || current_set_.IsEmpty()) {
+ return false;
+ }
+
+ auto current_set_it = current_set_.begin();
+ auto current_end = current_set_.end();
+ // Most of the time, this is the only one.
+ // Advance the current iterator, we won't need to check it again later.
+ UScriptCode priority_script = *current_set_it++;
+
+ // If next is common or inherited, the only thing that might change
+ // is the common preferred script.
+ if (next_set_.at(0) <= USCRIPT_INHERITED) {
+ if (next_set_.size() == 2 && priority_script <= USCRIPT_INHERITED &&
+ common_preferred_ == USCRIPT_COMMON) {
+ common_preferred_ = next_set_.at(1);
+ }
+ return true;
+ }
+
+ // If current is common or inherited, use the next script set.
+ if (priority_script <= USCRIPT_INHERITED) {
+ current_set_ = next_set_;
+ return true;
+ }
+
+ // Neither is common or inherited. If current is a singleton,
+ // just see if it exists in the next set. This is the common case.
+ auto next_it = next_set_.begin();
+ auto next_end = next_set_.end();
+ if (current_set_it == current_end) {
+ return std::find(next_it, next_end, priority_script) != next_end;
+ }
+
+ // Establish the priority script, if we have one.
+ // First try current priority script.
+ bool have_priority =
+ std::find(next_it, next_end, priority_script) != next_end;
+ if (!have_priority) {
+ // So try next priority script.
+ // Skip the first current script, we already know it's not there.
+ // Advance the next iterator, later we won't need to check it again.
+ priority_script = *next_it++;
+ have_priority =
+ std::find(current_set_it, current_end, priority_script) != current_end;
+ }
+
+ // Note that we can never write more scripts into the current vector than
+ // it already contains, so currentWriteIt won't ever exceed the size/capacity.
+ auto current_write_it = current_set_.begin();
+ if (have_priority) {
+ // keep the priority script.
+ *current_write_it++ = priority_script;
+ }
+
+ if (next_it != next_end) {
+ // Iterate over the remaining current scripts, and keep them if
+ // they occur in the remaining next scripts.
+ while (current_set_it != current_end) {
+ UScriptCode sc = *current_set_it++;
+ if (std::find(next_it, next_end, sc) != next_end) {
+ *current_write_it++ = sc;
+ }
+ }
+ }
+
+ // Only change current if the run continues.
+ int written = std::distance(current_set_.begin(), current_write_it);
+ if (written > 0) {
+ current_set_.resize(written);
+ return true;
+ }
+ return false;
+}
+
+// When we hit the end of the run, and resolve the script, we now know the
+// resolved script of any open bracket that was pushed on the stack since
+// the start of the run. Fixup depth records how many of these there
+// were. We've maintained this count during pushes, and taken care to
+// adjust it if the stack got overfull and open brackets were pushed off
+// the bottom. This sets the script of the fixup_depth topmost entries of the
+// stack to the resolved script.
+void ScriptRunIterator::FixupStack(UScriptCode resolved_script) {
+ if (brackets_fixup_depth_ > 0) {
+ if (brackets_fixup_depth_ > brackets_.size()) {
+ // Should never happen unless someone breaks the code.
+ DLOG(ERROR) << "Brackets fixup depth exceeds size of bracket vector.";
+ brackets_fixup_depth_ = brackets_.size();
+ }
+ auto it = brackets_.rbegin();
+ for (size_t i = 0; i < brackets_fixup_depth_; ++i) {
+ it->script = resolved_script;
+ ++it;
+ }
+ brackets_fixup_depth_ = 0;
+ }
+}
+
+bool ScriptRunIterator::Fetch(size_t* pos, UChar32* ch) {
+ if (ahead_pos_ > length_) {
+ return false;
+ }
+ *pos = ahead_pos_ - (ahead_character_ >= 0x10000 ? 2 : 1);
+ *ch = ahead_character_;
+
+ next_set_.swap(ahead_set_);
+ if (ahead_pos_ == length_) {
+ // No more data to fetch, but last character still needs to be
+ // processed. Advance m_aheadPos so that next time we will know
+ // this has been done.
+ ahead_pos_++;
+ return true;
+ }
+
+ U16_NEXT(text_, ahead_pos_, length_, ahead_character_);
+ script_data_->GetScripts(ahead_character_, ahead_set_);
+ if (ahead_set_.IsEmpty()) {
+ // No scripts for this character. This has already been logged, so
+ // we just terminate processing this text.
+ return false;
+ }
+ if (ahead_set_[0] == USCRIPT_INHERITED && ahead_set_.size() > 1) {
+ if (next_set_[0] == USCRIPT_COMMON) {
+ // Overwrite the next set with the non-inherited portion of the set.
+ next_set_ = ahead_set_;
+ next_set_.EraseAt(0);
+ // Discard the remaining values, we'll inherit.
+ ahead_set_.resize(1);
+ } else {
+ // Else, this applies to anything.
+ ahead_set_.resize(1);
+ }
+ }
+ return true;
+}
+
+UScriptCode ScriptRunIterator::ResolveCurrentScript() const {
+ UScriptCode result = current_set_.at(0);
+ return result == USCRIPT_COMMON ? common_preferred_ : result;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/script_run_iterator.h b/chromium/third_party/blink/renderer/platform/fonts/script_run_iterator.h
new file mode 100644
index 00000000000..7c4ce650762
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/script_run_iterator.h
@@ -0,0 +1,116 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SCRIPT_RUN_ITERATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SCRIPT_RUN_ITERATOR_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/deque.h"
+#include "third_party/blink/renderer/platform/wtf/dtoa/utils.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+#include <unicode/uchar.h>
+#include <unicode/uscript.h>
+
+namespace blink {
+
+class ScriptData;
+
+class PLATFORM_EXPORT ScriptRunIterator {
+ USING_FAST_MALLOC(ScriptRunIterator);
+ WTF_MAKE_NONCOPYABLE(ScriptRunIterator);
+
+ public:
+ ScriptRunIterator(const UChar* text, size_t length);
+
+ // This maintains a reference to data. It must exist for the lifetime of
+ // this object. Typically data is a singleton that exists for the life of
+ // the process.
+ ScriptRunIterator(const UChar* text, size_t length, const ScriptData*);
+
+ bool Consume(unsigned& limit, UScriptCode&);
+
+ private:
+ struct BracketRec {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ UChar32 ch;
+ UScriptCode script;
+ };
+ void OpenBracket(UChar32);
+ void CloseBracket(UChar32);
+ bool MergeSets();
+ void FixupStack(UScriptCode resolved_script);
+ bool Fetch(size_t* pos, UChar32*);
+
+ UScriptCode ResolveCurrentScript() const;
+
+ const UChar* text_;
+ const size_t length_;
+
+ Deque<BracketRec> brackets_;
+ size_t brackets_fixup_depth_;
+ // Limit max brackets so that the bracket tracking buffer does not grow
+ // excessively large when processing long runs of text.
+ static const int kMaxBrackets = 32;
+
+ Vector<UScriptCode> current_set_;
+ Vector<UScriptCode> next_set_;
+ Vector<UScriptCode> ahead_set_;
+
+ UChar32 ahead_character_;
+ size_t ahead_pos_;
+
+ UScriptCode common_preferred_;
+
+ const ScriptData* script_data_;
+};
+
+// ScriptData is a wrapper which returns a set of scripts for a particular
+// character retrieved from the character's primary script and script
+// extensions, as per ICU / Unicode data. ScriptData maintains a certain
+// priority order of the returned values, which are essential for mergeSets
+// method to work correctly.
+class PLATFORM_EXPORT ScriptData {
+ USING_FAST_MALLOC(ScriptData);
+ WTF_MAKE_NONCOPYABLE(ScriptData);
+
+ protected:
+ ScriptData() = default;
+
+ public:
+ virtual ~ScriptData();
+
+ enum PairedBracketType {
+ kBracketTypeNone,
+ kBracketTypeOpen,
+ kBracketTypeClose,
+ kBracketTypeCount
+ };
+
+ static const int kMaxScriptCount;
+
+ virtual void GetScripts(UChar32, Vector<UScriptCode>& dst) const = 0;
+
+ virtual UChar32 GetPairedBracket(UChar32) const = 0;
+
+ virtual PairedBracketType GetPairedBracketType(UChar32) const = 0;
+};
+
+class PLATFORM_EXPORT ICUScriptData : public ScriptData {
+ public:
+ ~ICUScriptData() override = default;
+
+ static const ICUScriptData* Instance();
+
+ void GetScripts(UChar32, Vector<UScriptCode>& dst) const override;
+
+ UChar32 GetPairedBracket(UChar32) const override;
+
+ PairedBracketType GetPairedBracketType(UChar32) const override;
+};
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/fonts/script_run_iterator_test.cc b/chromium/third_party/blink/renderer/platform/fonts/script_run_iterator_test.cc
new file mode 100644
index 00000000000..bf289de297c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/script_run_iterator_test.cc
@@ -0,0 +1,691 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/script_run_iterator.h"
+
+#include <string>
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/threading.h"
+
+namespace blink {
+
+struct ScriptTestRun {
+ std::string text;
+ UScriptCode code;
+};
+
+struct ScriptExpectedRun {
+ unsigned limit;
+ UScriptCode code;
+
+ ScriptExpectedRun(unsigned the_limit, UScriptCode the_code)
+ : limit(the_limit), code(the_code) {}
+};
+
+class MockScriptData : public ScriptData {
+ public:
+ ~MockScriptData() override = default;
+
+ static const MockScriptData* Instance() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(const MockScriptData, mock_script_data, ());
+ return &mock_script_data;
+ }
+
+ void GetScripts(UChar32 ch, Vector<UScriptCode>& dst) const override {
+ DCHECK_GE(ch, kMockCharMin);
+ DCHECK_LT(ch, kMockCharLimit);
+
+ int code = ch - kMockCharMin;
+ dst.clear();
+ switch (code & kCodeSpecialMask) {
+ case kCodeSpecialCommon:
+ dst.push_back(USCRIPT_COMMON);
+ break;
+ case kCodeSpecialInherited:
+ dst.push_back(USCRIPT_INHERITED);
+ break;
+ default:
+ break;
+ }
+ int list_bits = kTable[code & kCodeListIndexMask];
+ if (dst.IsEmpty() && list_bits == 0) {
+ dst.push_back(USCRIPT_UNKNOWN);
+ return;
+ }
+ while (list_bits) {
+ switch (list_bits & kListMask) {
+ case 0:
+ break;
+ case kLatin:
+ dst.push_back(USCRIPT_LATIN);
+ break;
+ case kHan:
+ dst.push_back(USCRIPT_HAN);
+ break;
+ case kGreek:
+ dst.push_back(USCRIPT_GREEK);
+ break;
+ }
+ list_bits >>= kListShift;
+ }
+ }
+
+ UChar32 GetPairedBracket(UChar32 ch) const override {
+ switch (GetPairedBracketType(ch)) {
+ case PairedBracketType::kBracketTypeClose:
+ return ch - kBracketDelta;
+ case PairedBracketType::kBracketTypeOpen:
+ return ch + kBracketDelta;
+ default:
+ return ch;
+ }
+ }
+
+ PairedBracketType GetPairedBracketType(UChar32 ch) const override {
+ DCHECK_GE(ch, kMockCharMin);
+ DCHECK_LT(ch, kMockCharLimit);
+ int code = ch - kMockCharMin;
+ if ((code & kCodeBracketBit) == 0) {
+ return PairedBracketType::kBracketTypeNone;
+ }
+ if (code & kCodeBracketCloseBit) {
+ return PairedBracketType::kBracketTypeClose;
+ }
+ return PairedBracketType::kBracketTypeOpen;
+ }
+
+ static int TableLookup(int value) {
+ for (int i = 0; i < 16; ++i) {
+ if (kTable[i] == value) {
+ return i;
+ }
+ }
+ DLOG(ERROR) << "Table does not contain value 0x" << std::hex << value;
+ return 0;
+ }
+
+ static String ToTestString(const std::string& input) {
+ String result(g_empty_string16_bit);
+ bool in_set = false;
+ int seen = 0;
+ int code = 0;
+ int list = 0;
+ int current_shift = 0;
+ for (char c : input) {
+ if (in_set) {
+ switch (c) {
+ case '(':
+ DCHECK_EQ(seen, 0);
+ seen |= kSawBracket;
+ code |= kCodeBracketBit;
+ break;
+ case '[':
+ DCHECK_EQ(seen, 0);
+ seen |= kSawBracket;
+ code |= kCodeBracketBit | kCodeSquareBracketBit;
+ break;
+ case ')':
+ DCHECK_EQ(seen, 0);
+ seen |= kSawBracket;
+ code |= kCodeBracketBit | kCodeBracketCloseBit;
+ break;
+ case ']':
+ DCHECK_EQ(seen, 0);
+ seen |= kSawBracket;
+ code |=
+ kCodeBracketBit | kCodeSquareBracketBit | kCodeBracketCloseBit;
+ break;
+ case 'i':
+ DCHECK_EQ(seen, 0); // brackets can't be inherited
+ seen |= kSawSpecial;
+ code |= kCodeSpecialInherited;
+ break;
+ case 'c':
+ DCHECK_EQ((seen & ~kSawBracket), 0);
+ seen |= kSawSpecial;
+ code |= kCodeSpecialCommon;
+ break;
+ case 'l':
+ DCHECK_EQ((seen & kSawLatin), 0);
+ DCHECK_LT(current_shift, 3);
+ seen |= kSawLatin;
+ list |= kLatin << (2 * current_shift++);
+ break;
+ case 'h':
+ DCHECK_EQ((seen & kSawHan), 0);
+ DCHECK_LT(current_shift, 3);
+ seen |= kSawHan;
+ list |= kHan << (2 * current_shift++);
+ break;
+ case 'g':
+ DCHECK_EQ((seen & kSawGreek), 0);
+ DCHECK_LT(current_shift, 3);
+ seen |= kSawGreek;
+ list |= kGreek << (2 * current_shift++);
+ break;
+ case '>':
+ DCHECK_NE(seen, 0);
+ code |= TableLookup(list);
+ result.append(static_cast<UChar>(kMockCharMin + code));
+ in_set = false;
+ break;
+ default:
+ DLOG(ERROR) << "Illegal mock string set char: '" << c << "'";
+ break;
+ }
+ continue;
+ }
+ // not in set
+ switch (c) {
+ case '<':
+ seen = 0;
+ code = 0;
+ list = 0;
+ current_shift = 0;
+ in_set = true;
+ break;
+ case '(':
+ code = kCodeBracketBit | kCodeSpecialCommon;
+ break;
+ case '[':
+ code = kCodeBracketBit | kCodeSquareBracketBit | kCodeSpecialCommon;
+ break;
+ case ')':
+ code = kCodeBracketBit | kCodeBracketCloseBit | kCodeSpecialCommon;
+ break;
+ case ']':
+ code = kCodeBracketBit | kCodeSquareBracketBit |
+ kCodeBracketCloseBit | kCodeSpecialCommon;
+ break;
+ case 'i':
+ code = kCodeSpecialInherited;
+ break;
+ case 'c':
+ code = kCodeSpecialCommon;
+ break;
+ case 'l':
+ code = kLatin;
+ break;
+ case 'h':
+ code = kHan;
+ break;
+ case 'g':
+ code = kGreek;
+ break;
+ case '?':
+ code = 0; // unknown
+ break;
+ default:
+ DLOG(ERROR) << "Illegal mock string set char: '" << c << "'";
+ }
+ if (!in_set) {
+ result.append(static_cast<UChar>(kMockCharMin + code));
+ }
+ }
+ return result;
+ }
+
+ // We determine properties based on the offset from kMockCharMin:
+ // bits 0-3 represent the list of l, h, c scripts (index into table)
+ // bit 4-5 means: 0 plain, 1 common, 2 inherited, 3 illegal
+ // bit 6 clear means non-bracket, open means bracket
+ // bit 7 clear means open bracket, set means close bracket
+ // bit 8 clear means paren, set means bracket
+ // if it's a bracket, the matching bracket is 64 code points away
+ static const UChar32 kMockCharMin = 0xe000;
+ static const UChar32 kMockCharLimit = kMockCharMin + 0x200;
+ static const int kLatin = 1;
+ static const int kHan = 2;
+ static const int kGreek = 3;
+ static const int kCodeListIndexMask = 0xf;
+ static const int kCodeSpecialMask = 0x30;
+ static const int kCodeSpecialCommon = 0x10;
+ static const int kCodeSpecialInherited = 0x20;
+ static const int kCodeBracketCloseBit = 0x40;
+ static const int kCodeBracketBit = 0x80;
+ static const int kCodeSquareBracketBit = 0x100;
+ static const int kListShift = 2;
+ static const int kListMask = 0x3;
+ static const int kBracketDelta = kCodeBracketCloseBit;
+ static const int kTable[16];
+
+ static const int kSawBracket = 0x1;
+ static const int kSawSpecial = 0x2;
+ static const int kSawLatin = 0x4;
+ static const int kSawHan = 0x8;
+ static const int kSawGreek = 0x10;
+};
+
+static const int kLatin2 = MockScriptData::kLatin << 2;
+static const int kHan2 = MockScriptData::kHan << 2;
+static const int kGreek2 = MockScriptData::kGreek << 2;
+static const int kLatin3 = MockScriptData::kLatin << 4;
+static const int kHan3 = MockScriptData::kHan << 4;
+static const int kGreek3 = MockScriptData::kGreek << 4;
+const int MockScriptData::kTable[] = {
+ 0,
+ kLatin,
+ kHan,
+ kGreek,
+ kLatin2 + kHan,
+ kLatin2 + kGreek,
+ kHan2 + kLatin,
+ kHan2 + kGreek,
+ kGreek2 + kLatin,
+ kGreek2 + kHan,
+ kLatin3 + kHan2 + kGreek,
+ kLatin3 + kGreek2 + kHan,
+ kHan3 + kLatin2 + kGreek,
+ kHan3 + kGreek2 + kLatin,
+ kGreek3 + kLatin2 + kHan,
+ kGreek3 + kHan2 + kLatin,
+};
+
+class ScriptRunIteratorTest : public testing::Test {
+ protected:
+ void CheckRuns(const Vector<ScriptTestRun>& runs) {
+ String text(g_empty_string16_bit);
+ Vector<ScriptExpectedRun> expect;
+ for (auto& run : runs) {
+ text.append(String::FromUTF8(run.text.c_str()));
+ expect.push_back(ScriptExpectedRun(text.length(), run.code));
+ }
+ ScriptRunIterator script_run_iterator(text.Characters16(), text.length());
+ VerifyRuns(&script_run_iterator, expect);
+ }
+
+ // FIXME crbug.com/527329 - CheckMockRuns should be replaced by finding
+ // suitable equivalent real codepoint sequences instead.
+ void CheckMockRuns(const Vector<ScriptTestRun>& runs) {
+ String text(g_empty_string16_bit);
+ Vector<ScriptExpectedRun> expect;
+ for (const ScriptTestRun& run : runs) {
+ text.append(MockScriptData::ToTestString(run.text));
+ expect.push_back(ScriptExpectedRun(text.length(), run.code));
+ }
+
+ ScriptRunIterator script_run_iterator(text.Characters16(), text.length(),
+ MockScriptData::Instance());
+ VerifyRuns(&script_run_iterator, expect);
+ }
+
+ void VerifyRuns(ScriptRunIterator* script_run_iterator,
+ const Vector<ScriptExpectedRun>& expect) {
+ unsigned limit;
+ UScriptCode code;
+ unsigned long run_count = 0;
+ while (script_run_iterator->Consume(limit, code)) {
+ ASSERT_LT(run_count, expect.size());
+ ASSERT_EQ(expect[run_count].limit, limit);
+ ASSERT_EQ(expect[run_count].code, code);
+ ++run_count;
+ }
+ ASSERT_EQ(expect.size(), run_count);
+ }
+};
+
+TEST_F(ScriptRunIteratorTest, Empty) {
+ String empty(g_empty_string16_bit);
+ ScriptRunIterator script_run_iterator(empty.Characters16(), empty.length());
+ unsigned limit = 0;
+ UScriptCode code = USCRIPT_INVALID_CODE;
+ DCHECK(!script_run_iterator.Consume(limit, code));
+ ASSERT_EQ(limit, 0u);
+ ASSERT_EQ(code, USCRIPT_INVALID_CODE);
+}
+
+// Some of our compilers cannot initialize a vector from an array yet.
+#define DECLARE_SCRIPT_RUNSVECTOR(...) \
+ static const ScriptTestRun kRunsArray[] = __VA_ARGS__; \
+ Vector<ScriptTestRun> runs; \
+ runs.Append(kRunsArray, sizeof(kRunsArray) / sizeof(*kRunsArray));
+
+#define CHECK_SCRIPT_RUNS(...) \
+ DECLARE_SCRIPT_RUNSVECTOR(__VA_ARGS__); \
+ CheckRuns(runs);
+
+#define CHECK_MOCK_SCRIPT_RUNS(...) \
+ DECLARE_SCRIPT_RUNSVECTOR(__VA_ARGS__); \
+ CheckMockRuns(runs);
+
+TEST_F(ScriptRunIteratorTest, Whitespace) {
+ CHECK_SCRIPT_RUNS({{" \t ", USCRIPT_COMMON}});
+}
+
+TEST_F(ScriptRunIteratorTest, Common) {
+ CHECK_SCRIPT_RUNS({{" ... !?", USCRIPT_COMMON}});
+}
+
+TEST_F(ScriptRunIteratorTest, CombiningCircle) {
+ CHECK_SCRIPT_RUNS({{"◌́◌̀◌̈◌̂◌̄◌̊", USCRIPT_COMMON}});
+}
+
+TEST_F(ScriptRunIteratorTest, Latin) {
+ CHECK_SCRIPT_RUNS({{"latin", USCRIPT_LATIN}});
+}
+
+TEST_F(ScriptRunIteratorTest, Chinese) {
+ CHECK_SCRIPT_RUNS({{"萬國碼", USCRIPT_HAN}});
+}
+
+// Close bracket without matching open is ignored
+TEST_F(ScriptRunIteratorTest, UnbalancedParens1) {
+ CHECK_SCRIPT_RUNS(
+ {{"(萬", USCRIPT_HAN}, {"a]", USCRIPT_LATIN}, {")", USCRIPT_HAN}});
+}
+
+// Open bracket without matching close is popped when inside
+// matching close brackets, so doesn't match later close.
+TEST_F(ScriptRunIteratorTest, UnbalancedParens2) {
+ CHECK_SCRIPT_RUNS(
+ {{"(萬", USCRIPT_HAN}, {"a[", USCRIPT_LATIN}, {")]", USCRIPT_HAN}});
+}
+
+// space goes with leading script
+TEST_F(ScriptRunIteratorTest, LatinHan) {
+ CHECK_SCRIPT_RUNS({{"Unicode ", USCRIPT_LATIN}, {"萬國碼", USCRIPT_HAN}});
+}
+
+// space goes with leading script
+TEST_F(ScriptRunIteratorTest, HanLatin) {
+ CHECK_SCRIPT_RUNS({{"萬國碼 ", USCRIPT_HAN}, {"Unicode", USCRIPT_LATIN}});
+}
+
+TEST_F(ScriptRunIteratorTest, ParenEmptyParen) {
+ CHECK_SCRIPT_RUNS({{"()", USCRIPT_COMMON}});
+}
+
+TEST_F(ScriptRunIteratorTest, ParenChineseParen) {
+ CHECK_SCRIPT_RUNS({{"(萬國碼)", USCRIPT_HAN}});
+}
+
+TEST_F(ScriptRunIteratorTest, ParenLatinParen) {
+ CHECK_SCRIPT_RUNS({{"(Unicode)", USCRIPT_LATIN}});
+}
+
+// open paren gets leading script
+TEST_F(ScriptRunIteratorTest, LatinParenChineseParen) {
+ CHECK_SCRIPT_RUNS({{"Unicode (", USCRIPT_LATIN},
+ {"萬國碼", USCRIPT_HAN},
+ {")", USCRIPT_LATIN}});
+}
+
+// open paren gets first trailing script if no leading script
+TEST_F(ScriptRunIteratorTest, ParenChineseParenLatin) {
+ CHECK_SCRIPT_RUNS({{"(萬國碼) ", USCRIPT_HAN}, {"Unicode", USCRIPT_LATIN}});
+}
+
+// leading common and open paren get first trailing script.
+// TODO(dougfelt): we don't do quote matching, but probably should figure out
+// something better then doing nothing.
+TEST_F(ScriptRunIteratorTest, QuoteParenChineseParenLatinQuote) {
+ CHECK_SCRIPT_RUNS(
+ {{"\"(萬國碼) ", USCRIPT_HAN}, {"Unicode\"", USCRIPT_LATIN}});
+}
+
+// Emojies are resolved to the leading script.
+TEST_F(ScriptRunIteratorTest, EmojiCommon) {
+ CHECK_SCRIPT_RUNS({{"百家姓🌱🌲🌳🌴", USCRIPT_HAN}});
+}
+
+// Unmatched close brace gets leading context
+TEST_F(ScriptRunIteratorTest, UnmatchedClose) {
+ CHECK_SCRIPT_RUNS({{"Unicode (", USCRIPT_LATIN},
+ {"萬國碼] ", USCRIPT_HAN},
+ {") Unicode\"", USCRIPT_LATIN}});
+}
+
+// Match up to 32 bracket pairs
+TEST_F(ScriptRunIteratorTest, Match32Brackets) {
+ CHECK_SCRIPT_RUNS({{"[萬國碼 ", USCRIPT_HAN},
+ {"Unicode (((((((((((((((((((((((((((((((!"
+ ")))))))))))))))))))))))))))))))",
+ USCRIPT_LATIN},
+ {"]", USCRIPT_HAN}});
+}
+
+// Matches 32 most recent bracket pairs. More than that, and we revert to
+// surrounding script.
+TEST_F(ScriptRunIteratorTest, Match32MostRecentBrackets) {
+ CHECK_SCRIPT_RUNS({{"((([萬國碼 ", USCRIPT_HAN},
+ {"Unicode (((((((((((((((((((((((((((((((", USCRIPT_LATIN},
+ {"萬國碼!", USCRIPT_HAN},
+ {")))))))))))))))))))))))))))))))", USCRIPT_LATIN},
+ {"]", USCRIPT_HAN},
+ {"But )))", USCRIPT_LATIN}});
+}
+
+// A char with multiple scripts that match both leading and trailing context
+// gets the leading context.
+TEST_F(ScriptRunIteratorTest, ExtensionsPreferLeadingContext) {
+ CHECK_MOCK_SCRIPT_RUNS({{"h<lh>", USCRIPT_HAN}, {"l", USCRIPT_LATIN}});
+}
+
+// A char with multiple scripts that only match trailing context gets the
+// trailing context.
+TEST_F(ScriptRunIteratorTest, ExtensionsMatchTrailingContext) {
+ CHECK_MOCK_SCRIPT_RUNS({{"h", USCRIPT_HAN}, {"<gl>l", USCRIPT_LATIN}});
+}
+
+// Retain first established priority script. <lhg><gh> produce the script <gh>
+// with g as priority, because of the two priority scripts l and g, only g
+// remains. Then <gh><hgl> retains g as priority, because of the two priority
+// scripts g and h that remain, g was encountered first.
+TEST_F(ScriptRunIteratorTest, ExtensionsRetainFirstPriorityScript) {
+ CHECK_MOCK_SCRIPT_RUNS({{"<lhg><gh><hgl>", USCRIPT_GREEK}});
+}
+
+// Parens can have scripts that break script runs.
+TEST_F(ScriptRunIteratorTest, ExtensionsParens) {
+ CHECK_MOCK_SCRIPT_RUNS({{"<gl><(lg>", USCRIPT_GREEK},
+ {"h<[hl>", USCRIPT_HAN},
+ {"l", USCRIPT_LATIN},
+ {"<]hl>", USCRIPT_HAN},
+ {"<)lg>", USCRIPT_GREEK}});
+}
+
+// The close paren might be encountered before we've established the open
+// paren's script, but when this is the case the current set is still valid, so
+// this doesn't affect it nor break the run.
+TEST_F(ScriptRunIteratorTest, ExtensionsParens2) {
+ CHECK_MOCK_SCRIPT_RUNS({{"<(lhg><gh><)lhg>", USCRIPT_GREEK}});
+}
+
+// A common script with a single extension should be treated as common, but
+// with the extended script as a default. If we encounter anything other than
+// common, that takes priority. If we encounter other common scripts with a
+// single extension, the current priority remains.
+TEST_F(ScriptRunIteratorTest, CommonWithPriority) {
+ CHECK_MOCK_SCRIPT_RUNS({{"<ch>", USCRIPT_HAN}});
+}
+
+TEST_F(ScriptRunIteratorTest, CommonWithPriority2) {
+ CHECK_MOCK_SCRIPT_RUNS({{"<ch><lh>", USCRIPT_LATIN}});
+}
+
+TEST_F(ScriptRunIteratorTest, CommonWithPriority3) {
+ CHECK_MOCK_SCRIPT_RUNS({{"<ch><cl><cg>", USCRIPT_HAN}});
+}
+
+// UDatta (\xE0\xA5\x91) is inherited with LATIN, DEVANAGARI, BENGALI and
+// other Indic scripts. Since it has LATIN, and the
+// dotted circle U+25CC (\xE2\x97\x8C) is COMMON and has adopted the
+// preceding LATIN, it gets the LATIN. This is standard.
+TEST_F(ScriptRunIteratorTest, LatinDottedCircleUdatta) {
+ CHECK_SCRIPT_RUNS({{"Latin \xE2\x97\x8C\xE0\xA5\x91", USCRIPT_LATIN}});
+}
+
+// In this situation, UDatta U+0951 (\xE0\xA5\x91) doesn't share a script
+// with the value inherited by the dotted circle U+25CC (\xE2\x97\x8C).
+// It captures the preceding dotted circle and breaks it from the run it would
+// normally have been in. U+0951 is used in multiple scripts (DEVA, BENG, LATN,
+// etc) and has multiple values for Script_Extension property. At the moment,
+// getScripts() treats the script with the lowest script code as 'true' primary,
+// and BENG comes before DEVA in the script enum so that we get BENGALI.
+// Taking into account a Unicode block and returning DEVANAGARI would be
+// slightly better.
+TEST_F(ScriptRunIteratorTest, HanDottedCircleUdatta) {
+ CHECK_SCRIPT_RUNS({{"萬國碼 ", USCRIPT_HAN},
+ {"\xE2\x97\x8C\xE0\xA5\x91", USCRIPT_BENGALI}});
+}
+
+// Tatweel is \xD9\x80 Lm, Fathatan is \xD9\x8B Mn. The script of tatweel is
+// common, that of Fathatan is inherited. The script extensions for Fathatan
+// are Arabic and Syriac. The Syriac script is 34 in ICU, Arabic is 2. So the
+// preferred script for Fathatan is Arabic, according to Behdad's
+// heuristic. This is exactly analogous to the Udatta tests above, except
+// Tatweel is Lm. But we don't take properties into account, only scripts.
+TEST_F(ScriptRunIteratorTest, LatinTatweelFathatan) {
+ CHECK_SCRIPT_RUNS(
+ {{"Latin ", USCRIPT_LATIN}, {"\xD9\x80\xD9\x8B", USCRIPT_ARABIC}});
+}
+
+// Another case where if the mark accepts a script that was inherited by the
+// preceding common-script character, they both continue in that script.
+// SYRIAC LETTER NUN \xDC\xA2
+// ARABIC TATWEEL \xD9\x80
+// ARABIC FATHATAN \xD9\x82
+TEST_F(ScriptRunIteratorTest, SyriacTatweelFathatan) {
+ CHECK_SCRIPT_RUNS({{"\xDC\xA2\xD9\x80\xD9\x8B", USCRIPT_SYRIAC}});
+}
+
+// The Udatta (\xE0\xA5\x91) is inherited, so will share runs with anything that
+// is not common.
+TEST_F(ScriptRunIteratorTest, HanUdatta) {
+ CHECK_SCRIPT_RUNS({{"萬國碼\xE0\xA5\x91", USCRIPT_HAN}});
+}
+
+// The Udatta U+0951 (\xE0\xA5\x91) is inherited, and will capture the space
+// and turn it into Bengali because SCRIPT_BENAGLI is 4 and SCRIPT_DEVANAGARI
+// is 10. See TODO comment for |getScripts| and HanDottedCircleUdatta.
+TEST_F(ScriptRunIteratorTest, HanSpaceUdatta) {
+ CHECK_SCRIPT_RUNS(
+ {{"萬國碼", USCRIPT_HAN}, {" \xE0\xA5\x91", USCRIPT_BENGALI}});
+}
+
+// Corresponds to one test in RunSegmenter, where orientation of the
+// space character is sidesways in vertical.
+TEST_F(ScriptRunIteratorTest, Hangul) {
+ CHECK_SCRIPT_RUNS({{"키스의 고유조건은", USCRIPT_HANGUL}});
+}
+
+// Corresponds to one test in RunSegmenter, which tests that the punctuation
+// characters mixed in are actually sideways in vertical. The ScriptIterator
+// should report one run, but the RunSegmenter should report three, with the
+// middle one rotated sideways.
+TEST_F(ScriptRunIteratorTest, HiraganaMixedPunctuation) {
+ CHECK_SCRIPT_RUNS({{"いろはに.…¡ほへと", USCRIPT_HIRAGANA}});
+}
+
+// Make sure Mock code works too.
+TEST_F(ScriptRunIteratorTest, MockHanInheritedGL) {
+ CHECK_MOCK_SCRIPT_RUNS({{"h<igl>", USCRIPT_HAN}});
+}
+
+TEST_F(ScriptRunIteratorTest, MockHanCommonInheritedGL) {
+ CHECK_MOCK_SCRIPT_RUNS({{"h", USCRIPT_HAN}, {"c<igl>", USCRIPT_GREEK}});
+}
+
+// Leading inherited just act like common, except there's no preferred script.
+TEST_F(ScriptRunIteratorTest, MockLeadingInherited) {
+ CHECK_MOCK_SCRIPT_RUNS({{"<igl>", USCRIPT_COMMON}});
+}
+
+// Leading inherited just act like common, except there's no preferred script.
+TEST_F(ScriptRunIteratorTest, MockLeadingInherited2) {
+ CHECK_MOCK_SCRIPT_RUNS({{"<igl><ih>", USCRIPT_COMMON}});
+}
+
+TEST_F(ScriptRunIteratorTest, LeadingInheritedHan) {
+ // DEVANAGARI STRESS SIGN UDATTA \xE0\xA5\x91
+ CHECK_SCRIPT_RUNS({{"\xE0\xA5\x91萬國碼", USCRIPT_HAN}});
+}
+
+TEST_F(ScriptRunIteratorTest, LeadingInheritedHan2) {
+ // DEVANAGARI STRESS SIGN UDATTA \xE0\xA5\x91
+ // ARABIC FATHATAN \xD9\x8B
+ CHECK_SCRIPT_RUNS({{"\xE0\xA5\x91\xD9\x8B萬國碼", USCRIPT_HAN}});
+}
+
+TEST_F(ScriptRunIteratorTest, OddLatinString) {
+ CHECK_SCRIPT_RUNS({{"ç̈", USCRIPT_LATIN}});
+}
+
+TEST_F(ScriptRunIteratorTest, CommonMalayalam) {
+ CHECK_SCRIPT_RUNS({{"100-ാം", USCRIPT_MALAYALAM}});
+}
+
+class ScriptRunIteratorICUDataTest : public testing::Test {
+ public:
+ ScriptRunIteratorICUDataTest()
+ : max_extensions_(0), max_extensions_codepoint_(0xffff) {
+ int max_extensions = 0;
+ UChar32 max_extensionscp = 0;
+ for (UChar32 cp = 0; cp < 0x11000; ++cp) {
+ UErrorCode status = U_ZERO_ERROR;
+ int count = uscript_getScriptExtensions(cp, nullptr, 0, &status);
+ if (count > max_extensions) {
+ max_extensions = count;
+ max_extensionscp = cp;
+ }
+ }
+ max_extensions_ = max_extensions;
+ max_extensions_codepoint_ = max_extensionscp;
+ }
+
+ protected:
+ UChar32 GetACharWithMaxExtensions(int* num_extensions) {
+ if (num_extensions) {
+ *num_extensions = max_extensions_;
+ }
+ return max_extensions_codepoint_;
+ }
+
+ private:
+ int max_extensions_;
+ UChar32 max_extensions_codepoint_;
+};
+
+// Validate that ICU never returns more than our maximum expected number of
+// script extensions.
+TEST_F(ScriptRunIteratorICUDataTest, ValidateICUMaxScriptExtensions) {
+ int max_extensions;
+ UChar32 cp = GetACharWithMaxExtensions(&max_extensions);
+ ASSERT_LE(max_extensions, ScriptData::kMaxScriptCount)
+ << "char " << std::hex << cp << std::dec;
+}
+
+// Check that ICUScriptData returns all of a character's scripts.
+// This only checks one likely character, but doesn't check all cases.
+TEST_F(ScriptRunIteratorICUDataTest, ICUDataGetScriptsReturnsAllExtensions) {
+ int max_extensions;
+ UChar32 cp = GetACharWithMaxExtensions(&max_extensions);
+ Vector<UScriptCode> extensions;
+ ICUScriptData::Instance()->GetScripts(cp, extensions);
+
+ // It's possible that GetScripts adds the primary script to the list of
+ // extensions, resulting in one more script than the raw extension count.
+ ASSERT_GE(static_cast<int>(extensions.size()), max_extensions)
+ << "char " << std::hex << cp << std::dec;
+}
+
+TEST_F(ScriptRunIteratorICUDataTest, CommonHaveNoMoreThanOneExtension) {
+ Vector<UScriptCode> extensions;
+ for (UChar32 cp = 0; cp < 0x110000; ++cp) {
+ ICUScriptData::Instance()->GetScripts(cp, extensions);
+ UScriptCode primary = extensions.at(0);
+ if (primary == USCRIPT_COMMON) {
+ ASSERT_LE(extensions.size(), 2ul) << "cp: " << std::hex << cp << std::dec;
+ }
+ }
+}
+
+// ZWJ is \u200D Cf (Format, other) and its script is inherited. I'm going to
+// ignore this for now, as I think it shouldn't matter which run it ends up
+// in. HarfBuzz needs to be able to use it as context and shape each
+// neighboring character appropriately no matter what run it got assigned to.
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/segmented_font_data.cc b/chromium/third_party/blink/renderer/platform/fonts/segmented_font_data.cc
new file mode 100644
index 00000000000..9834248df3f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/segmented_font_data.cc
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2008, 2009 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/fonts/segmented_font_data.h"
+
+#include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+const SimpleFontData* SegmentedFontData::FontDataForCharacter(UChar32 c) const {
+ auto end = faces_.end();
+ for (auto it = faces_.begin(); it != end; ++it) {
+ if ((*it)->Contains(c))
+ return (*it)->FontData();
+ }
+ return faces_[0]->FontData();
+}
+
+bool SegmentedFontData::ContainsCharacter(UChar32 c) const {
+ auto end = faces_.end();
+ for (auto it = faces_.begin(); it != end; ++it) {
+ if ((*it)->Contains(c))
+ return true;
+ }
+ return false;
+}
+
+bool SegmentedFontData::IsCustomFont() const {
+ // All segmented fonts are custom fonts.
+ return true;
+}
+
+bool SegmentedFontData::IsLoading() const {
+ auto end = faces_.end();
+ for (auto it = faces_.begin(); it != end; ++it) {
+ if ((*it)->FontData()->IsLoading())
+ return true;
+ }
+ return false;
+}
+
+// Returns true if any of the sub fonts are loadingFallback.
+bool SegmentedFontData::IsLoadingFallback() const {
+ auto end = faces_.end();
+ for (auto it = faces_.begin(); it != end; ++it) {
+ if ((*it)->FontData()->IsLoadingFallback())
+ return true;
+ }
+ return false;
+}
+
+bool SegmentedFontData::IsSegmented() const {
+ return true;
+}
+
+bool SegmentedFontData::ShouldSkipDrawing() const {
+ auto end = faces_.end();
+ for (auto it = faces_.begin(); it != end; ++it) {
+ if ((*it)->FontData()->ShouldSkipDrawing())
+ return true;
+ }
+ return false;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/segmented_font_data.h b/chromium/third_party/blink/renderer/platform/fonts/segmented_font_data.h
new file mode 100644
index 00000000000..61b2872c97b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/segmented_font_data.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2008, 2009 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SEGMENTED_FONT_DATA_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SEGMENTED_FONT_DATA_H_
+
+#include "third_party/blink/renderer/platform/fonts/font_data.h"
+#include "third_party/blink/renderer/platform/fonts/font_data_for_range_set.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+class SimpleFontData;
+
+namespace blink {
+
+class PLATFORM_EXPORT SegmentedFontData : public FontData {
+ public:
+ static scoped_refptr<SegmentedFontData> Create() {
+ return base::AdoptRef(new SegmentedFontData);
+ }
+
+ void AppendFace(scoped_refptr<FontDataForRangeSet> font_data_for_range_set) {
+ faces_.push_back(std::move(font_data_for_range_set));
+ }
+ unsigned NumFaces() const { return faces_.size(); }
+ scoped_refptr<FontDataForRangeSet> FaceAt(unsigned i) const { return faces_[i]; }
+ bool ContainsCharacter(UChar32) const;
+
+ private:
+ SegmentedFontData() = default;
+
+ const SimpleFontData* FontDataForCharacter(UChar32) const override;
+
+ bool IsCustomFont() const override;
+ bool IsLoading() const override;
+ bool IsLoadingFallback() const override;
+ bool IsSegmented() const override;
+ bool ShouldSkipDrawing() const override;
+
+ Vector<scoped_refptr<FontDataForRangeSet>, 1> faces_;
+};
+
+DEFINE_FONT_DATA_TYPE_CASTS(SegmentedFontData, true);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SEGMENTED_FONT_DATA_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.cc b/chromium/third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.cc
new file mode 100644
index 00000000000..0c56ee41fa9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.cc
@@ -0,0 +1,33 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.h"
+
+#include "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.h"
+
+namespace blink {
+
+scoped_refptr<const ShapeResult> CachingWordShapeIterator::ShapeWordWithoutSpacing(
+ const TextRun& word_run,
+ const Font* font) {
+ ShapeCacheEntry* cache_entry = shape_cache_->Add(word_run, ShapeCacheEntry());
+ if (cache_entry && cache_entry->shape_result_)
+ return cache_entry->shape_result_;
+
+ unsigned word_length = 0;
+ std::unique_ptr<UChar[]> word_text = word_run.NormalizedUTF16(&word_length);
+
+ HarfBuzzShaper shaper(word_text.get(), word_length);
+ scoped_refptr<const ShapeResult> shape_result =
+ shaper.Shape(font, word_run.Direction());
+ if (!shape_result)
+ return nullptr;
+
+ if (cache_entry)
+ cache_entry->shape_result_ = shape_result;
+
+ return shape_result;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.h b/chromium/third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.h
new file mode 100644
index 00000000000..1fb7b8d921e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.h
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2015 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_CACHING_WORD_SHAPE_ITERATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_CACHING_WORD_SHAPE_ITERATOR_H_
+
+#include "third_party/blink/renderer/platform/fonts/font.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_cache.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.h"
+#include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT CachingWordShapeIterator final {
+ STACK_ALLOCATED();
+ WTF_MAKE_NONCOPYABLE(CachingWordShapeIterator);
+
+ public:
+ CachingWordShapeIterator(ShapeCache* cache,
+ const TextRun& run,
+ const Font* font)
+ : shape_cache_(cache),
+ text_run_(run),
+ font_(font),
+ spacing_(run),
+ width_so_far_(0),
+ start_index_(0) {
+ DCHECK(font);
+
+ // Shaping word by word is faster as each word is cached. If we cannot
+ // use the cache or if the font doesn't support word by word shaping
+ // fall back on shaping the entire run.
+ shape_by_word_ = font_->CanShapeWordByWord();
+
+ // SVG sets SpacingDisabled because it handles spacing by themselves.
+ if (!run.SpacingDisabled())
+ spacing_.SetSpacingAndExpansion(font->GetFontDescription());
+ }
+
+ bool Next(scoped_refptr<const ShapeResult>* word_result) {
+ if (UNLIKELY(text_run_.AllowTabs()))
+ return NextForAllowTabs(word_result);
+
+ if (!shape_by_word_) {
+ if (start_index_)
+ return false;
+ *word_result = ShapeWord(text_run_, font_);
+ start_index_ = 1;
+ return word_result->get();
+ }
+
+ return NextWord(word_result);
+ }
+
+ private:
+ scoped_refptr<const ShapeResult> ShapeWordWithoutSpacing(const TextRun&,
+ const Font*);
+
+ scoped_refptr<const ShapeResult> ShapeWord(const TextRun& word_run,
+ const Font* font) {
+ if (LIKELY(!spacing_.HasSpacing()))
+ return ShapeWordWithoutSpacing(word_run, font);
+
+ scoped_refptr<const ShapeResult> result = ShapeWordWithoutSpacing(word_run, font);
+ return result->ApplySpacingToCopy(spacing_, word_run);
+ }
+
+ bool NextWord(scoped_refptr<const ShapeResult>* word_result) {
+ return ShapeToEndIndex(word_result, NextWordEndIndex());
+ }
+
+ static bool IsWordDelimiter(UChar ch) {
+ return ch == kSpaceCharacter || ch == kTabulationCharacter;
+ }
+
+ unsigned NextWordEndIndex() const {
+ const unsigned length = text_run_.length();
+ if (start_index_ >= length)
+ return 0;
+
+ if (start_index_ + 1u == length || IsWordDelimiter(text_run_[start_index_]))
+ return start_index_ + 1;
+
+ // 8Bit words end at isWordDelimiter().
+ if (text_run_.Is8Bit()) {
+ for (unsigned i = start_index_ + 1;; i++) {
+ if (i == length || IsWordDelimiter(text_run_[i]))
+ return i;
+ }
+ }
+
+ // Non-CJK/Emoji words end at isWordDelimiter() or CJK/Emoji characters.
+ unsigned end = start_index_;
+ UChar32 ch = text_run_.CodepointAtAndNext(end);
+ if (!Character::IsCJKIdeographOrSymbol(ch)) {
+ for (unsigned next_end = end; end < length; end = next_end) {
+ ch = text_run_.CodepointAtAndNext(next_end);
+ if (IsWordDelimiter(ch) || Character::IsCJKIdeographOrSymbolBase(ch))
+ return end;
+ }
+ return length;
+ }
+
+ // For CJK/Emoji words, delimit every character because these scripts do
+ // not delimit words by spaces, and delimiting only at isWordDelimiter()
+ // worsen the cache efficiency.
+ bool has_any_script = !Character::IsCommonOrInheritedScript(ch);
+ for (unsigned next_end = end; end < length; end = next_end) {
+ ch = text_run_.CodepointAtAndNext(next_end);
+ // ZWJ and modifier check in order not to split those Emoji sequences.
+ if (U_GET_GC_MASK(ch) & (U_GC_M_MASK | U_GC_LM_MASK | U_GC_SK_MASK) ||
+ ch == kZeroWidthJoinerCharacter || Character::IsModifier(ch) ||
+ Character::IsEmojiFlagSequenceTag(ch))
+ continue;
+ // Avoid delimiting COMMON/INHERITED alone, which makes harder to
+ // identify the script.
+ if (Character::IsCJKIdeographOrSymbol(ch)) {
+ if (Character::IsCommonOrInheritedScript(ch))
+ continue;
+ if (!has_any_script) {
+ has_any_script = true;
+ continue;
+ }
+ }
+ return end;
+ }
+ return length;
+ }
+
+ bool ShapeToEndIndex(scoped_refptr<const ShapeResult>* result, unsigned end_index) {
+ if (!end_index || end_index <= start_index_)
+ return false;
+
+ const unsigned length = text_run_.length();
+ if (!start_index_ && end_index == length) {
+ *result = ShapeWord(text_run_, font_);
+ } else {
+ DCHECK_LE(end_index, length);
+ TextRun sub_run =
+ text_run_.SubRun(start_index_, end_index - start_index_);
+ *result = ShapeWord(sub_run, font_);
+ }
+ start_index_ = end_index;
+ return result->get();
+ }
+
+ unsigned EndIndexUntil(UChar ch) const {
+ unsigned length = text_run_.length();
+ DCHECK_LT(start_index_, length);
+ for (unsigned i = start_index_ + 1;; i++) {
+ if (i == length || text_run_[i] == ch)
+ return i;
+ }
+ }
+
+ bool NextForAllowTabs(scoped_refptr<const ShapeResult>* word_result) {
+ unsigned length = text_run_.length();
+ if (start_index_ >= length)
+ return false;
+
+ if (UNLIKELY(text_run_[start_index_] == kTabulationCharacter)) {
+ for (unsigned i = start_index_ + 1;; i++) {
+ if (i == length || text_run_[i] != kTabulationCharacter) {
+ *word_result = ShapeResult::CreateForTabulationCharacters(
+ font_, text_run_, width_so_far_, i - start_index_);
+ start_index_ = i;
+ break;
+ }
+ }
+ } else if (!shape_by_word_) {
+ if (!ShapeToEndIndex(word_result, EndIndexUntil(kTabulationCharacter)))
+ return false;
+ } else {
+ if (!NextWord(word_result))
+ return false;
+ }
+ DCHECK(*word_result);
+ width_so_far_ += (*word_result)->Width();
+ return true;
+ }
+
+ ShapeCache* shape_cache_;
+ const TextRun& text_run_;
+ const Font* font_;
+ ShapeResultSpacing<TextRun> spacing_;
+ float width_so_far_; // Used only when allowTabs()
+ unsigned start_index_ : 31;
+ unsigned shape_by_word_ : 1;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_CACHING_WORD_SHAPE_ITERATOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.cc b/chromium/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.cc
new file mode 100644
index 00000000000..45e40d9d62b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.cc
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2015 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 "third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.h"
+
+#include "third_party/blink/renderer/platform/fonts/character_range.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_cache.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_buffer.h"
+#include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
+#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
+
+namespace blink {
+
+ShapeCache* CachingWordShaper::GetShapeCache() const {
+ return font_.font_fallback_list_->GetShapeCache(font_.font_description_);
+}
+
+float CachingWordShaper::Width(const TextRun& run,
+ HashSet<const SimpleFontData*>* fallback_fonts,
+ FloatRect* glyph_bounds) {
+ float width = 0;
+ scoped_refptr<const ShapeResult> word_result;
+ CachingWordShapeIterator iterator(GetShapeCache(), run, &font_);
+ while (iterator.Next(&word_result)) {
+ if (word_result) {
+ if (glyph_bounds) {
+ FloatRect adjusted_bounds = word_result->Bounds();
+ // Translate glyph bounds to the current glyph position which
+ // is the total width before this glyph.
+ adjusted_bounds.SetX(adjusted_bounds.X() + width);
+ glyph_bounds->Unite(adjusted_bounds);
+ }
+ width += word_result->Width();
+ if (fallback_fonts)
+ word_result->FallbackFonts(fallback_fonts);
+ }
+ }
+
+ return width;
+}
+
+static inline float ShapeResultsForRun(ShapeCache* shape_cache,
+ const Font* font,
+ const TextRun& run,
+ ShapeResultBuffer* results_buffer) {
+ CachingWordShapeIterator iterator(shape_cache, run, font);
+ scoped_refptr<const ShapeResult> word_result;
+ float total_width = 0;
+ while (iterator.Next(&word_result)) {
+ if (word_result) {
+ total_width += word_result->Width();
+ results_buffer->AppendResult(std::move(word_result));
+ }
+ }
+ return total_width;
+}
+
+int CachingWordShaper::OffsetForPosition(const TextRun& run,
+ float target_x,
+ bool include_partial_glyphs) {
+ ShapeResultBuffer buffer;
+ ShapeResultsForRun(GetShapeCache(), &font_, run, &buffer);
+
+ return buffer.OffsetForPosition(run, target_x, include_partial_glyphs);
+}
+
+void CachingWordShaper::FillResultBuffer(const TextRunPaintInfo& run_info,
+ ShapeResultBuffer* buffer) {
+ DCHECK(buffer);
+ ShapeResultsForRun(GetShapeCache(), &font_, run_info.run, buffer);
+}
+
+CharacterRange CachingWordShaper::GetCharacterRange(const TextRun& run,
+ unsigned from,
+ unsigned to) {
+ ShapeResultBuffer buffer;
+ float total_width = ShapeResultsForRun(GetShapeCache(), &font_, run, &buffer);
+
+ return buffer.GetCharacterRange(run.Direction(), total_width, from, to);
+}
+
+Vector<CharacterRange> CachingWordShaper::IndividualCharacterRanges(
+ const TextRun& run) {
+ ShapeResultBuffer buffer;
+ float total_width = ShapeResultsForRun(GetShapeCache(), &font_, run, &buffer);
+
+ auto ranges = buffer.IndividualCharacterRanges(run.Direction(), total_width);
+ // The shaper can fail to return glyph metrics for all characters (see
+ // crbug.com/613915 and crbug.com/615661) so add empty ranges to ensure all
+ // characters have an associated range.
+ while (ranges.size() < static_cast<unsigned>(run.length()))
+ ranges.push_back(CharacterRange(0, 0, 0, 0));
+ return ranges;
+}
+
+Vector<ShapeResult::RunFontData> CachingWordShaper::GetRunFontData(
+ const TextRun& run) const {
+ ShapeResultBuffer buffer;
+ ShapeResultsForRun(GetShapeCache(), &font_, run, &buffer);
+
+ return buffer.GetRunFontData();
+}
+
+GlyphData CachingWordShaper::EmphasisMarkGlyphData(
+ const TextRun& emphasis_mark_run) const {
+ ShapeResultBuffer buffer;
+ ShapeResultsForRun(GetShapeCache(), &font_, emphasis_mark_run, &buffer);
+
+ return buffer.EmphasisMarkGlyphData(font_.font_description_);
+}
+
+}; // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.h b/chromium/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.h
new file mode 100644
index 00000000000..0b6a215fb45
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2015 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_CACHING_WORD_SHAPER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_CACHING_WORD_SHAPER_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_buffer.h"
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/text/text_run.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+struct CharacterRange;
+class Font;
+class ShapeCache;
+class SimpleFontData;
+struct GlyphData;
+
+class PLATFORM_EXPORT CachingWordShaper final {
+ STACK_ALLOCATED();
+ WTF_MAKE_NONCOPYABLE(CachingWordShaper);
+
+ public:
+ explicit CachingWordShaper(const Font& font) : font_(font) {}
+ ~CachingWordShaper() = default;
+
+ float Width(const TextRun&,
+ HashSet<const SimpleFontData*>* fallback_fonts,
+ FloatRect* glyph_bounds);
+ int OffsetForPosition(const TextRun&,
+ float target_x,
+ bool include_partial_glyphs);
+
+ void FillResultBuffer(const TextRunPaintInfo&, ShapeResultBuffer*);
+ CharacterRange GetCharacterRange(const TextRun&, unsigned from, unsigned to);
+ Vector<CharacterRange> IndividualCharacterRanges(const TextRun&);
+
+ Vector<ShapeResult::RunFontData> GetRunFontData(const TextRun&) const;
+
+ GlyphData EmphasisMarkGlyphData(const TextRun&) const;
+
+ private:
+ ShapeCache* GetShapeCache() const;
+
+ const Font& font_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_CACHING_WORD_SHAPER_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper_test.cc b/chromium/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper_test.cc
new file mode 100644
index 00000000000..2183a6ccd12
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper_test.cc
@@ -0,0 +1,422 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.h"
+
+#include <memory>
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/fonts/font_cache.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_test_info.h"
+
+namespace blink {
+
+class CachingWordShaperTest : public testing::Test {
+ protected:
+ void SetUp() override {
+ font_description.SetComputedSize(12.0);
+ font_description.SetLocale(LayoutLocale::Get("en"));
+ ASSERT_EQ(USCRIPT_LATIN, font_description.GetScript());
+ font_description.SetGenericFamily(FontDescription::kStandardFamily);
+
+ font = Font(font_description);
+ font.Update(nullptr);
+ ASSERT_TRUE(font.CanShapeWordByWord());
+ fallback_fonts = nullptr;
+ cache = std::make_unique<ShapeCache>();
+ }
+
+ FontCachePurgePreventer font_cache_purge_preventer;
+ FontDescription font_description;
+ Font font;
+ std::unique_ptr<ShapeCache> cache;
+ HashSet<const SimpleFontData*>* fallback_fonts;
+ unsigned start_index = 0;
+ unsigned num_glyphs = 0;
+ hb_script_t script = HB_SCRIPT_INVALID;
+};
+
+static inline const ShapeResultTestInfo* TestInfo(
+ scoped_refptr<const ShapeResult>& result) {
+ return static_cast<const ShapeResultTestInfo*>(result.get());
+}
+
+TEST_F(CachingWordShaperTest, LatinLeftToRightByWord) {
+ TextRun text_run(reinterpret_cast<const LChar*>("ABC DEF."), 8);
+
+ scoped_refptr<const ShapeResult> result;
+ CachingWordShapeIterator iterator(cache.get(), text_run, &font);
+ ASSERT_TRUE(iterator.Next(&result));
+ ASSERT_TRUE(
+ TestInfo(result)->RunInfoForTesting(0, start_index, num_glyphs, script));
+ EXPECT_EQ(0u, start_index);
+ EXPECT_EQ(3u, num_glyphs);
+ EXPECT_EQ(HB_SCRIPT_LATIN, script);
+
+ ASSERT_TRUE(iterator.Next(&result));
+ ASSERT_TRUE(
+ TestInfo(result)->RunInfoForTesting(0, start_index, num_glyphs, script));
+ EXPECT_EQ(0u, start_index);
+ EXPECT_EQ(1u, num_glyphs);
+ EXPECT_EQ(HB_SCRIPT_COMMON, script);
+
+ ASSERT_TRUE(iterator.Next(&result));
+ ASSERT_TRUE(
+ TestInfo(result)->RunInfoForTesting(0, start_index, num_glyphs, script));
+ EXPECT_EQ(0u, start_index);
+ EXPECT_EQ(4u, num_glyphs);
+ EXPECT_EQ(HB_SCRIPT_LATIN, script);
+
+ ASSERT_FALSE(iterator.Next(&result));
+}
+
+TEST_F(CachingWordShaperTest, CommonAccentLeftToRightByWord) {
+ const UChar kStr[] = {0x2F, 0x301, 0x2E, 0x20, 0x2E, 0x0};
+ TextRun text_run(kStr, 5);
+
+ unsigned offset = 0;
+ scoped_refptr<const ShapeResult> result;
+ CachingWordShapeIterator iterator(cache.get(), text_run, &font);
+ ASSERT_TRUE(iterator.Next(&result));
+ ASSERT_TRUE(
+ TestInfo(result)->RunInfoForTesting(0, start_index, num_glyphs, script));
+ EXPECT_EQ(0u, offset + start_index);
+ EXPECT_EQ(3u, num_glyphs);
+ EXPECT_EQ(HB_SCRIPT_COMMON, script);
+ offset += result->NumCharacters();
+
+ ASSERT_TRUE(iterator.Next(&result));
+ ASSERT_TRUE(
+ TestInfo(result)->RunInfoForTesting(0, start_index, num_glyphs, script));
+ EXPECT_EQ(3u, offset + start_index);
+ EXPECT_EQ(1u, num_glyphs);
+ EXPECT_EQ(HB_SCRIPT_COMMON, script);
+ offset += result->NumCharacters();
+
+ ASSERT_TRUE(iterator.Next(&result));
+ ASSERT_TRUE(
+ TestInfo(result)->RunInfoForTesting(0, start_index, num_glyphs, script));
+ EXPECT_EQ(4u, offset + start_index);
+ EXPECT_EQ(1u, num_glyphs);
+ EXPECT_EQ(HB_SCRIPT_COMMON, script);
+ offset += result->NumCharacters();
+
+ ASSERT_EQ(5u, offset);
+ ASSERT_FALSE(iterator.Next(&result));
+}
+
+TEST_F(CachingWordShaperTest, SegmentCJKByCharacter) {
+ const UChar kStr[] = {0x56FD, 0x56FD, // CJK Unified Ideograph
+ 'a', 'b',
+ 0x56FD, // CJK Unified Ideograph
+ 'x', 'y', 'z',
+ 0x3042, // HIRAGANA LETTER A
+ 0x56FD, // CJK Unified Ideograph
+ 0x0};
+ TextRun text_run(kStr, 10);
+
+ scoped_refptr<const ShapeResult> word_result;
+ CachingWordShapeIterator iterator(cache.get(), text_run, &font);
+
+ ASSERT_TRUE(iterator.Next(&word_result));
+ EXPECT_EQ(1u, word_result->NumCharacters());
+ ASSERT_TRUE(iterator.Next(&word_result));
+ EXPECT_EQ(1u, word_result->NumCharacters());
+
+ ASSERT_TRUE(iterator.Next(&word_result));
+ EXPECT_EQ(2u, word_result->NumCharacters());
+
+ ASSERT_TRUE(iterator.Next(&word_result));
+ EXPECT_EQ(1u, word_result->NumCharacters());
+
+ ASSERT_TRUE(iterator.Next(&word_result));
+ EXPECT_EQ(3u, word_result->NumCharacters());
+
+ ASSERT_TRUE(iterator.Next(&word_result));
+ EXPECT_EQ(1u, word_result->NumCharacters());
+ ASSERT_TRUE(iterator.Next(&word_result));
+ EXPECT_EQ(1u, word_result->NumCharacters());
+
+ ASSERT_FALSE(iterator.Next(&word_result));
+}
+
+TEST_F(CachingWordShaperTest, SegmentCJKAndCommon) {
+ const UChar kStr[] = {'a', 'b',
+ 0xFF08, // FULLWIDTH LEFT PARENTHESIS (script=common)
+ 0x56FD, // CJK Unified Ideograph
+ 0x56FD, // CJK Unified Ideograph
+ 0x56FD, // CJK Unified Ideograph
+ 0x3002, // IDEOGRAPHIC FULL STOP (script=common)
+ 0x0};
+ TextRun text_run(kStr, 7);
+
+ scoped_refptr<const ShapeResult> word_result;
+ CachingWordShapeIterator iterator(cache.get(), text_run, &font);
+
+ ASSERT_TRUE(iterator.Next(&word_result));
+ EXPECT_EQ(2u, word_result->NumCharacters());
+
+ ASSERT_TRUE(iterator.Next(&word_result));
+ EXPECT_EQ(2u, word_result->NumCharacters());
+
+ ASSERT_TRUE(iterator.Next(&word_result));
+ EXPECT_EQ(1u, word_result->NumCharacters());
+
+ ASSERT_TRUE(iterator.Next(&word_result));
+ EXPECT_EQ(2u, word_result->NumCharacters());
+
+ ASSERT_FALSE(iterator.Next(&word_result));
+}
+
+TEST_F(CachingWordShaperTest, SegmentCJKAndInherit) {
+ const UChar kStr[] = {
+ 0x304B, // HIRAGANA LETTER KA
+ 0x304B, // HIRAGANA LETTER KA
+ 0x3009, // COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK
+ 0x304B, // HIRAGANA LETTER KA
+ 0x0};
+ TextRun text_run(kStr, 4);
+
+ scoped_refptr<const ShapeResult> word_result;
+ CachingWordShapeIterator iterator(cache.get(), text_run, &font);
+
+ ASSERT_TRUE(iterator.Next(&word_result));
+ EXPECT_EQ(1u, word_result->NumCharacters());
+
+ ASSERT_TRUE(iterator.Next(&word_result));
+ EXPECT_EQ(2u, word_result->NumCharacters());
+
+ ASSERT_TRUE(iterator.Next(&word_result));
+ EXPECT_EQ(1u, word_result->NumCharacters());
+
+ ASSERT_FALSE(iterator.Next(&word_result));
+}
+
+TEST_F(CachingWordShaperTest, SegmentCJKAndNonCJKCommon) {
+ const UChar kStr[] = {0x56FD, // CJK Unified Ideograph
+ ' ', 0x0};
+ TextRun text_run(kStr, 2);
+
+ scoped_refptr<const ShapeResult> word_result;
+ CachingWordShapeIterator iterator(cache.get(), text_run, &font);
+
+ ASSERT_TRUE(iterator.Next(&word_result));
+ EXPECT_EQ(1u, word_result->NumCharacters());
+
+ ASSERT_TRUE(iterator.Next(&word_result));
+ EXPECT_EQ(1u, word_result->NumCharacters());
+
+ ASSERT_FALSE(iterator.Next(&word_result));
+}
+
+TEST_F(CachingWordShaperTest, SegmentEmojiZWJCommon) {
+ // A family followed by a couple with heart emoji sequence,
+ // the latter including a variation selector.
+ const UChar kStr[] = {0xD83D, 0xDC68, 0x200D, 0xD83D, 0xDC69, 0x200D,
+ 0xD83D, 0xDC67, 0x200D, 0xD83D, 0xDC66, 0xD83D,
+ 0xDC69, 0x200D, 0x2764, 0xFE0F, 0x200D, 0xD83D,
+ 0xDC8B, 0x200D, 0xD83D, 0xDC68, 0x0};
+ TextRun text_run(kStr, 22);
+
+ scoped_refptr<const ShapeResult> word_result;
+ CachingWordShapeIterator iterator(cache.get(), text_run, &font);
+
+ ASSERT_TRUE(iterator.Next(&word_result));
+ EXPECT_EQ(22u, word_result->NumCharacters());
+
+ ASSERT_FALSE(iterator.Next(&word_result));
+}
+
+TEST_F(CachingWordShaperTest, SegmentEmojiPilotJudgeSequence) {
+ // A family followed by a couple with heart emoji sequence,
+ // the latter including a variation selector.
+ const UChar kStr[] = {0xD83D, 0xDC68, 0xD83C, 0xDFFB, 0x200D, 0x2696, 0xFE0F,
+ 0xD83D, 0xDC68, 0xD83C, 0xDFFB, 0x200D, 0x2708, 0xFE0F};
+ TextRun text_run(kStr, ARRAY_SIZE(kStr));
+
+ scoped_refptr<const ShapeResult> word_result;
+ CachingWordShapeIterator iterator(cache.get(), text_run, &font);
+
+ ASSERT_TRUE(iterator.Next(&word_result));
+ EXPECT_EQ(ARRAY_SIZE(kStr), word_result->NumCharacters());
+
+ ASSERT_FALSE(iterator.Next(&word_result));
+}
+
+TEST_F(CachingWordShaperTest, SegmentEmojiHeartZWJSequence) {
+ // A ZWJ, followed by two family ZWJ Sequences.
+ const UChar kStr[] = {0xD83D, 0xDC69, 0x200D, 0x2764, 0xFE0F, 0x200D,
+ 0xD83D, 0xDC8B, 0x200D, 0xD83D, 0xDC68, 0x0};
+ TextRun text_run(kStr, 11);
+
+ scoped_refptr<const ShapeResult> word_result;
+ CachingWordShapeIterator iterator(cache.get(), text_run, &font);
+
+ ASSERT_TRUE(iterator.Next(&word_result));
+ EXPECT_EQ(11u, word_result->NumCharacters());
+
+ ASSERT_FALSE(iterator.Next(&word_result));
+}
+
+TEST_F(CachingWordShaperTest, SegmentEmojiSignsOfHornsModifier) {
+ // A Sign of the Horns emoji, followed by a fitzpatrick modifer
+ const UChar kStr[] = {0xD83E, 0xDD18, 0xD83C, 0xDFFB, 0x0};
+ TextRun text_run(kStr, 4);
+
+ scoped_refptr<const ShapeResult> word_result;
+ CachingWordShapeIterator iterator(cache.get(), text_run, &font);
+
+ ASSERT_TRUE(iterator.Next(&word_result));
+ EXPECT_EQ(4u, word_result->NumCharacters());
+
+ ASSERT_FALSE(iterator.Next(&word_result));
+}
+
+TEST_F(CachingWordShaperTest, SegmentEmojiExtraZWJPrefix) {
+ // A ZWJ, followed by a family and a heart-kiss sequence.
+ const UChar kStr[] = {0x200D, 0xD83D, 0xDC68, 0x200D, 0xD83D, 0xDC69,
+ 0x200D, 0xD83D, 0xDC67, 0x200D, 0xD83D, 0xDC66,
+ 0xD83D, 0xDC69, 0x200D, 0x2764, 0xFE0F, 0x200D,
+ 0xD83D, 0xDC8B, 0x200D, 0xD83D, 0xDC68, 0x0};
+ TextRun text_run(kStr, 23);
+
+ scoped_refptr<const ShapeResult> word_result;
+ CachingWordShapeIterator iterator(cache.get(), text_run, &font);
+
+ ASSERT_TRUE(iterator.Next(&word_result));
+ EXPECT_EQ(1u, word_result->NumCharacters());
+
+ ASSERT_TRUE(iterator.Next(&word_result));
+ EXPECT_EQ(22u, word_result->NumCharacters());
+
+ ASSERT_FALSE(iterator.Next(&word_result));
+}
+
+TEST_F(CachingWordShaperTest, SegmentEmojiSubdivisionFlags) {
+ // Subdivision flags for Wales, Scotland, England.
+ const UChar kStr[] = {0xD83C, 0xDFF4, 0xDB40, 0xDC67, 0xDB40, 0xDC62, 0xDB40,
+ 0xDC77, 0xDB40, 0xDC6C, 0xDB40, 0xDC73, 0xDB40, 0xDC7F,
+ 0xD83C, 0xDFF4, 0xDB40, 0xDC67, 0xDB40, 0xDC62, 0xDB40,
+ 0xDC73, 0xDB40, 0xDC63, 0xDB40, 0xDC74, 0xDB40, 0xDC7F,
+ 0xD83C, 0xDFF4, 0xDB40, 0xDC67, 0xDB40, 0xDC62, 0xDB40,
+ 0xDC65, 0xDB40, 0xDC6E, 0xDB40, 0xDC67, 0xDB40, 0xDC7F};
+ TextRun text_run(kStr, ARRAY_SIZE(kStr));
+
+ scoped_refptr<const ShapeResult> word_result;
+ CachingWordShapeIterator iterator(cache.get(), text_run, &font);
+
+ ASSERT_TRUE(iterator.Next(&word_result));
+ EXPECT_EQ(42u, word_result->NumCharacters());
+
+ ASSERT_FALSE(iterator.Next(&word_result));
+}
+
+TEST_F(CachingWordShaperTest, SegmentCJKCommon) {
+ const UChar kStr[] = {0xFF08, // FULLWIDTH LEFT PARENTHESIS (script=common)
+ 0xFF08, // FULLWIDTH LEFT PARENTHESIS (script=common)
+ 0xFF08, // FULLWIDTH LEFT PARENTHESIS (script=common)
+ 0x0};
+ TextRun text_run(kStr, 3);
+
+ scoped_refptr<const ShapeResult> word_result;
+ CachingWordShapeIterator iterator(cache.get(), text_run, &font);
+
+ ASSERT_TRUE(iterator.Next(&word_result));
+ EXPECT_EQ(3u, word_result->NumCharacters());
+
+ ASSERT_FALSE(iterator.Next(&word_result));
+}
+
+TEST_F(CachingWordShaperTest, SegmentCJKCommonAndNonCJK) {
+ const UChar kStr[] = {0xFF08, // FULLWIDTH LEFT PARENTHESIS (script=common)
+ 'a', 'b', 0x0};
+ TextRun text_run(kStr, 3);
+
+ scoped_refptr<const ShapeResult> word_result;
+ CachingWordShapeIterator iterator(cache.get(), text_run, &font);
+
+ ASSERT_TRUE(iterator.Next(&word_result));
+ EXPECT_EQ(1u, word_result->NumCharacters());
+
+ ASSERT_TRUE(iterator.Next(&word_result));
+ EXPECT_EQ(2u, word_result->NumCharacters());
+
+ ASSERT_FALSE(iterator.Next(&word_result));
+}
+
+TEST_F(CachingWordShaperTest, SegmentCJKSmallFormVariants) {
+ const UChar kStr[] = {0x5916, // CJK UNIFIED IDEOGRPAH
+ 0xFE50, // SMALL COMMA
+ 0x0};
+ TextRun text_run(kStr, 2);
+
+ scoped_refptr<const ShapeResult> word_result;
+ CachingWordShapeIterator iterator(cache.get(), text_run, &font);
+
+ ASSERT_TRUE(iterator.Next(&word_result));
+ EXPECT_EQ(2u, word_result->NumCharacters());
+
+ ASSERT_FALSE(iterator.Next(&word_result));
+}
+
+TEST_F(CachingWordShaperTest, SegmentHangulToneMark) {
+ const UChar kStr[] = {0xC740, // HANGUL SYLLABLE EUN
+ 0x302E, // HANGUL SINGLE DOT TONE MARK
+ 0x0};
+ TextRun text_run(kStr, 2);
+
+ scoped_refptr<const ShapeResult> word_result;
+ CachingWordShapeIterator iterator(cache.get(), text_run, &font);
+
+ ASSERT_TRUE(iterator.Next(&word_result));
+ EXPECT_EQ(2u, word_result->NumCharacters());
+
+ ASSERT_FALSE(iterator.Next(&word_result));
+}
+
+TEST_F(CachingWordShaperTest, TextOrientationFallbackShouldNotInFallbackList) {
+ const UChar kStr[] = {
+ 'A', // code point for verticalRightOrientationFontData()
+ // Ideally we'd like to test uprightOrientationFontData() too
+ // using code point such as U+3042, but it'd fallback to system
+ // fonts as the glyph is missing.
+ 0x0};
+ TextRun text_run(kStr, 1);
+
+ font_description.SetOrientation(FontOrientation::kVerticalMixed);
+ Font vertical_mixed_font = Font(font_description);
+ vertical_mixed_font.Update(nullptr);
+ ASSERT_TRUE(vertical_mixed_font.CanShapeWordByWord());
+
+ CachingWordShaper shaper(vertical_mixed_font);
+ FloatRect glyph_bounds;
+ HashSet<const SimpleFontData*> fallback_fonts;
+ ASSERT_GT(shaper.Width(text_run, &fallback_fonts, &glyph_bounds), 0);
+ EXPECT_EQ(0u, fallback_fonts.size());
+}
+
+TEST_F(CachingWordShaperTest, GlyphBoundsWithSpaces) {
+ CachingWordShaper shaper(font);
+
+ TextRun periods(reinterpret_cast<const LChar*>(".........."), 10);
+ FloatRect periods_glyph_bounds;
+ float periods_width = shaper.Width(periods, nullptr, &periods_glyph_bounds);
+
+ TextRun periods_and_spaces(
+ reinterpret_cast<const LChar*>(". . . . . . . . . ."), 19);
+ FloatRect periods_and_spaces_glyph_bounds;
+ float periods_and_spaces_width = shaper.Width(
+ periods_and_spaces, nullptr, &periods_and_spaces_glyph_bounds);
+
+ // The total width of periods and spaces should be longer than the width of
+ // periods alone.
+ ASSERT_GT(periods_and_spaces_width, periods_width);
+
+ // The glyph bounds of periods and spaces should be longer than the glyph
+ // bounds of periods alone.
+ ASSERT_GT(periods_and_spaces_glyph_bounds.Width(),
+ periods_glyph_bounds.Width());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/case_mapping_harf_buzz_buffer_filler.cc b/chromium/third_party/blink/renderer/platform/fonts/shaping/case_mapping_harf_buzz_buffer_filler.cc
new file mode 100644
index 00000000000..54ce1d432b1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/case_mapping_harf_buzz_buffer_filler.cc
@@ -0,0 +1,94 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/shaping/case_mapping_harf_buzz_buffer_filler.h"
+
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+static const uint16_t* ToUint16(const UChar* src) {
+ // FIXME: This relies on undefined behavior however it works on the
+ // current versions of all compilers we care about and avoids making
+ // a copy of the string.
+ static_assert(sizeof(UChar) == sizeof(uint16_t),
+ "UChar should be the same size as uint16_t");
+ return reinterpret_cast<const uint16_t*>(src);
+}
+
+CaseMappingHarfBuzzBufferFiller::CaseMappingHarfBuzzBufferFiller(
+ CaseMapIntend case_map_intend,
+ AtomicString locale,
+ hb_buffer_t* harf_buzz_buffer,
+ const UChar* buffer,
+ unsigned buffer_length,
+ unsigned start_index,
+ unsigned num_characters)
+ : harf_buzz_buffer_(harf_buzz_buffer) {
+ if (case_map_intend == CaseMapIntend::kKeepSameCase) {
+ hb_buffer_add_utf16(harf_buzz_buffer_, ToUint16(buffer), buffer_length,
+ start_index, num_characters);
+ } else {
+ String case_mapped_text;
+ if (case_map_intend == CaseMapIntend::kUpperCase) {
+ case_mapped_text = String(buffer, buffer_length).UpperUnicode(locale);
+ } else {
+ case_mapped_text = String(buffer, buffer_length).LowerUnicode(locale);
+ }
+
+ if (case_mapped_text.length() != buffer_length) {
+ FillSlowCase(case_map_intend, locale, buffer, buffer_length, start_index,
+ num_characters);
+ return;
+ }
+
+ DCHECK_EQ(case_mapped_text.length(), buffer_length);
+ DCHECK(!case_mapped_text.Is8Bit());
+ hb_buffer_add_utf16(harf_buzz_buffer_,
+ ToUint16(case_mapped_text.Characters16()),
+ buffer_length, start_index, num_characters);
+ }
+}
+
+// TODO(drott): crbug.com/623940 Fix lack of context sensitive case mapping
+// here.
+void CaseMappingHarfBuzzBufferFiller::FillSlowCase(
+ CaseMapIntend case_map_intend,
+ AtomicString locale,
+ const UChar* buffer,
+ unsigned buffer_length,
+ unsigned start_index,
+ unsigned num_characters) {
+ // Record pre-context.
+ hb_buffer_add_utf16(harf_buzz_buffer_, ToUint16(buffer), buffer_length,
+ start_index, 0);
+
+ for (unsigned char_index = start_index;
+ char_index < start_index + num_characters;) {
+ unsigned new_char_index = char_index;
+ U16_FWD_1(buffer, new_char_index, num_characters);
+ String char_by_char(&buffer[char_index], new_char_index - char_index);
+ String case_mapped_char;
+ if (case_map_intend == CaseMapIntend::kUpperCase)
+ case_mapped_char = char_by_char.UpperUnicode(locale);
+ else
+ case_mapped_char = char_by_char.LowerUnicode(locale);
+
+ for (unsigned j = 0; j < case_mapped_char.length();) {
+ UChar32 codepoint = 0;
+ U16_NEXT(case_mapped_char.Characters16(), j, case_mapped_char.length(),
+ codepoint);
+ // Add all characters of the case mapping result at the same cluster
+ // position.
+ hb_buffer_add(harf_buzz_buffer_, codepoint, char_index);
+ }
+ char_index = new_char_index;
+ }
+
+ // Record post-context
+ hb_buffer_add_utf16(harf_buzz_buffer_, ToUint16(buffer), buffer_length,
+ start_index + num_characters, 0);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/case_mapping_harf_buzz_buffer_filler.h b/chromium/third_party/blink/renderer/platform/fonts/shaping/case_mapping_harf_buzz_buffer_filler.h
new file mode 100644
index 00000000000..ef32b1e61ea
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/case_mapping_harf_buzz_buffer_filler.h
@@ -0,0 +1,42 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_CASE_MAPPING_HARF_BUZZ_BUFFER_FILLER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_CASE_MAPPING_HARF_BUZZ_BUFFER_FILLER_H_
+
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
+
+#include <hb.h>
+
+namespace blink {
+
+enum class CaseMapIntend { kKeepSameCase, kUpperCase, kLowerCase };
+
+class CaseMappingHarfBuzzBufferFiller {
+ STACK_ALLOCATED();
+
+ public:
+ CaseMappingHarfBuzzBufferFiller(CaseMapIntend,
+ AtomicString locale,
+ hb_buffer_t* harf_buzz_buffer,
+ const UChar* buffer,
+ unsigned buffer_length,
+ unsigned start_index,
+ unsigned num_characters);
+
+ private:
+ void FillSlowCase(CaseMapIntend,
+ AtomicString locale,
+ const UChar* buffer,
+ unsigned buffer_length,
+ unsigned start_index,
+ unsigned num_characters);
+ hb_buffer_t* harf_buzz_buffer_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_face.cc b/chromium/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_face.cc
new file mode 100644
index 00000000000..74aa4743f32
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_face.cc
@@ -0,0 +1,448 @@
+/*
+ * Copyright (c) 2012 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 "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_face.h"
+
+#include <memory>
+
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/fonts/font_cache.h"
+#include "third_party/blink/renderer/platform/fonts/font_global_context.h"
+#include "third_party/blink/renderer/platform/fonts/font_platform_data.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_font_cache.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.h"
+#include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
+#include "third_party/blink/renderer/platform/fonts/skia/skia_text_metrics.h"
+#include "third_party/blink/renderer/platform/fonts/unicode_range_set.h"
+#include "third_party/blink/renderer/platform/histogram.h"
+#include "third_party/blink/renderer/platform/resolution_units.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+#include <hb-ot.h>
+#include <hb.h>
+#if defined(OS_MACOSX)
+#include <hb-coretext.h>
+#endif
+
+#include <SkPaint.h>
+#include <SkPath.h>
+#include <SkPoint.h>
+#include <SkRect.h>
+#include <SkStream.h>
+#include <SkTypeface.h>
+
+namespace blink {
+
+void HbFontDeleter::operator()(hb_font_t* font) {
+ if (font)
+ hb_font_destroy(font);
+}
+
+void HbFaceDeleter::operator()(hb_face_t* face) {
+ if (face)
+ hb_face_destroy(face);
+}
+
+struct HbSetDeleter {
+ void operator()(hb_set_t* set) {
+ if (set)
+ hb_set_destroy(set);
+ }
+};
+
+using HbSetUniquePtr = std::unique_ptr<hb_set_t, HbSetDeleter>;
+
+static scoped_refptr<HbFontCacheEntry> CreateHbFontCacheEntry(hb_face_t*);
+
+HarfBuzzFace::HarfBuzzFace(FontPlatformData* platform_data, uint64_t unique_id)
+ : platform_data_(platform_data), unique_id_(unique_id) {
+ HarfBuzzFontCache::AddResult result =
+ FontGlobalContext::GetHarfBuzzFontCache().insert(unique_id_, nullptr);
+ if (result.is_new_entry) {
+ HbFaceUniquePtr face(CreateFace());
+ result.stored_value->value = CreateHbFontCacheEntry(face.get());
+ }
+ result.stored_value->value->AddRef();
+ unscaled_font_ = result.stored_value->value->HbFont();
+ harf_buzz_font_data_ = result.stored_value->value->HbFontData();
+}
+
+HarfBuzzFace::~HarfBuzzFace() {
+ HarfBuzzFontCache::iterator result =
+ FontGlobalContext::GetHarfBuzzFontCache().find(unique_id_);
+ SECURITY_DCHECK(result != FontGlobalContext::GetHarfBuzzFontCache().end());
+ DCHECK(!result.Get()->value->HasOneRef());
+ result.Get()->value->Release();
+ if (result.Get()->value->HasOneRef())
+ FontGlobalContext::GetHarfBuzzFontCache().erase(unique_id_);
+}
+
+static hb_bool_t HarfBuzzGetGlyph(hb_font_t* hb_font,
+ void* font_data,
+ hb_codepoint_t unicode,
+ hb_codepoint_t variation_selector,
+ hb_codepoint_t* glyph,
+ void* user_data) {
+ HarfBuzzFontData* hb_font_data =
+ reinterpret_cast<HarfBuzzFontData*>(font_data);
+
+ CHECK(hb_font_data);
+ if (hb_font_data->range_set_ && !hb_font_data->range_set_->Contains(unicode))
+ return false;
+
+ return hb_font_get_glyph(hb_font_get_parent(hb_font), unicode,
+ variation_selector, glyph);
+}
+
+static hb_position_t HarfBuzzGetGlyphHorizontalAdvance(hb_font_t* hb_font,
+ void* font_data,
+ hb_codepoint_t glyph,
+ void* user_data) {
+ HarfBuzzFontData* hb_font_data =
+ reinterpret_cast<HarfBuzzFontData*>(font_data);
+ hb_position_t advance = 0;
+
+ SkiaTextMetrics(&hb_font_data->paint_)
+ .GetGlyphWidthForHarfBuzz(glyph, &advance);
+ return advance;
+}
+
+static hb_bool_t HarfBuzzGetGlyphVerticalOrigin(hb_font_t* hb_font,
+ void* font_data,
+ hb_codepoint_t glyph,
+ hb_position_t* x,
+ hb_position_t* y,
+ void* user_data) {
+ HarfBuzzFontData* hb_font_data =
+ reinterpret_cast<HarfBuzzFontData*>(font_data);
+ scoped_refptr<OpenTypeVerticalData> vertical_data =
+ hb_font_data->VerticalData();
+ if (!vertical_data)
+ return false;
+
+ float result[] = {0, 0};
+ Glyph the_glyph = glyph;
+ vertical_data->GetVerticalTranslationsForGlyphs(hb_font_data->paint_,
+ &the_glyph, 1, result);
+ *x = SkiaTextMetrics::SkiaScalarToHarfBuzzPosition(-result[0]);
+ *y = SkiaTextMetrics::SkiaScalarToHarfBuzzPosition(-result[1]);
+ return true;
+}
+
+static hb_position_t HarfBuzzGetGlyphVerticalAdvance(hb_font_t* hb_font,
+ void* font_data,
+ hb_codepoint_t glyph,
+ void* user_data) {
+ HarfBuzzFontData* hb_font_data =
+ reinterpret_cast<HarfBuzzFontData*>(font_data);
+ scoped_refptr<OpenTypeVerticalData> vertical_data =
+ hb_font_data->VerticalData();
+ if (!vertical_data) {
+ return SkiaTextMetrics::SkiaScalarToHarfBuzzPosition(
+ hb_font_data->height_fallback_);
+ }
+
+ Glyph the_glyph = glyph;
+ float advance_height = -vertical_data->AdvanceHeight(the_glyph);
+ return SkiaTextMetrics::SkiaScalarToHarfBuzzPosition(
+ SkFloatToScalar(advance_height));
+}
+
+static hb_position_t HarfBuzzGetGlyphHorizontalKerning(
+ hb_font_t*,
+ void* font_data,
+ hb_codepoint_t left_glyph,
+ hb_codepoint_t right_glyph,
+ void*) {
+ HarfBuzzFontData* hb_font_data =
+ reinterpret_cast<HarfBuzzFontData*>(font_data);
+ if (hb_font_data->paint_.isVerticalText()) {
+ // We don't support cross-stream kerning
+ return 0;
+ }
+
+ SkTypeface* typeface = hb_font_data->paint_.getTypeface();
+
+ const uint16_t glyphs[2] = {static_cast<uint16_t>(left_glyph),
+ static_cast<uint16_t>(right_glyph)};
+ int32_t kerning_adjustments[1] = {0};
+
+ if (typeface->getKerningPairAdjustments(glyphs, 2, kerning_adjustments)) {
+ SkScalar upm = SkIntToScalar(typeface->getUnitsPerEm());
+ SkScalar size = hb_font_data->paint_.getTextSize();
+ return SkiaTextMetrics::SkiaScalarToHarfBuzzPosition(
+ SkIntToScalar(kerning_adjustments[0]) * size / upm);
+ }
+
+ return 0;
+}
+
+static hb_bool_t HarfBuzzGetGlyphExtents(hb_font_t* hb_font,
+ void* font_data,
+ hb_codepoint_t glyph,
+ hb_glyph_extents_t* extents,
+ void* user_data) {
+ HarfBuzzFontData* hb_font_data =
+ reinterpret_cast<HarfBuzzFontData*>(font_data);
+
+ SkiaTextMetrics(&hb_font_data->paint_)
+ .GetGlyphExtentsForHarfBuzz(glyph, extents);
+ return true;
+}
+
+static inline bool TableHasSpace(hb_face_t* face,
+ hb_set_t* glyphs,
+ hb_tag_t tag,
+ hb_codepoint_t space) {
+ unsigned count = hb_ot_layout_table_get_lookup_count(face, tag);
+ for (unsigned i = 0; i < count; i++) {
+ hb_ot_layout_lookup_collect_glyphs(face, tag, i, glyphs, glyphs, glyphs,
+ nullptr);
+ if (hb_set_has(glyphs, space))
+ return true;
+ }
+ return false;
+}
+
+static bool GetSpaceGlyph(hb_font_t* font, hb_codepoint_t& space) {
+ return hb_font_get_nominal_glyph(font, kSpaceCharacter, &space);
+}
+
+bool HarfBuzzFace::HasSpaceInLigaturesOrKerning(TypesettingFeatures features) {
+ const hb_codepoint_t kInvalidCodepoint = static_cast<hb_codepoint_t>(-1);
+ hb_codepoint_t space = kInvalidCodepoint;
+
+ HbSetUniquePtr glyphs(hb_set_create());
+
+ // Check whether computing is needed and compute for gpos/gsub.
+ if (features & kKerning &&
+ harf_buzz_font_data_->space_in_gpos_ ==
+ HarfBuzzFontData::SpaceGlyphInOpenTypeTables::Unknown) {
+ if (space == kInvalidCodepoint && !GetSpaceGlyph(unscaled_font_, space))
+ return false;
+ // Compute for gpos.
+ hb_face_t* face = hb_font_get_face(unscaled_font_);
+ DCHECK(face);
+ harf_buzz_font_data_->space_in_gpos_ =
+ hb_ot_layout_has_positioning(face) &&
+ TableHasSpace(face, glyphs.get(), HB_OT_TAG_GPOS, space)
+ ? HarfBuzzFontData::SpaceGlyphInOpenTypeTables::Present
+ : HarfBuzzFontData::SpaceGlyphInOpenTypeTables::NotPresent;
+ }
+
+ hb_set_clear(glyphs.get());
+
+ if (features & kLigatures &&
+ harf_buzz_font_data_->space_in_gsub_ ==
+ HarfBuzzFontData::SpaceGlyphInOpenTypeTables::Unknown) {
+ if (space == kInvalidCodepoint && !GetSpaceGlyph(unscaled_font_, space))
+ return false;
+ // Compute for gpos.
+ hb_face_t* face = hb_font_get_face(unscaled_font_);
+ DCHECK(face);
+ harf_buzz_font_data_->space_in_gsub_ =
+ hb_ot_layout_has_substitution(face) &&
+ TableHasSpace(face, glyphs.get(), HB_OT_TAG_GSUB, space)
+ ? HarfBuzzFontData::SpaceGlyphInOpenTypeTables::Present
+ : HarfBuzzFontData::SpaceGlyphInOpenTypeTables::NotPresent;
+ }
+
+ return (features & kKerning &&
+ harf_buzz_font_data_->space_in_gpos_ ==
+ HarfBuzzFontData::SpaceGlyphInOpenTypeTables::Present) ||
+ (features & kLigatures &&
+ harf_buzz_font_data_->space_in_gsub_ ==
+ HarfBuzzFontData::SpaceGlyphInOpenTypeTables::Present);
+}
+
+unsigned HarfBuzzFace::UnitsPerEmFromHeadTable() {
+ hb_face_t* face = hb_font_get_face(unscaled_font_);
+ return hb_face_get_upem(face);
+}
+
+bool HarfBuzzFace::ShouldSubpixelPosition() {
+ return harf_buzz_font_data_->paint_.isSubpixelText();
+}
+
+static hb_font_funcs_t* HarfBuzzSkiaGetFontFuncs() {
+ hb_font_funcs_t* funcs = FontGlobalContext::GetHarfBuzzFontFuncs();
+
+ // We don't set callback functions which we can't support.
+ // HarfBuzz will use the fallback implementation if they aren't set.
+ if (!funcs) {
+ funcs = hb_font_funcs_create();
+ hb_font_funcs_set_glyph_func(funcs, HarfBuzzGetGlyph, nullptr, nullptr);
+ hb_font_funcs_set_glyph_h_advance_func(
+ funcs, HarfBuzzGetGlyphHorizontalAdvance, nullptr, nullptr);
+ hb_font_funcs_set_glyph_h_kerning_func(
+ funcs, HarfBuzzGetGlyphHorizontalKerning, nullptr, nullptr);
+ hb_font_funcs_set_glyph_v_advance_func(
+ funcs, HarfBuzzGetGlyphVerticalAdvance, nullptr, nullptr);
+ hb_font_funcs_set_glyph_v_origin_func(funcs, HarfBuzzGetGlyphVerticalOrigin,
+ nullptr, nullptr);
+ hb_font_funcs_set_glyph_extents_func(funcs, HarfBuzzGetGlyphExtents,
+ nullptr, nullptr);
+ hb_font_funcs_make_immutable(funcs);
+ FontGlobalContext::SetHarfBuzzFontFuncs(funcs);
+ }
+ DCHECK(funcs);
+ return funcs;
+}
+
+static hb_blob_t* HarfBuzzSkiaGetTable(hb_face_t* face,
+ hb_tag_t tag,
+ void* user_data) {
+ SkTypeface* typeface = reinterpret_cast<SkTypeface*>(user_data);
+
+ const size_t table_size = typeface->getTableSize(tag);
+ if (!table_size) {
+ return nullptr;
+ }
+
+ char* buffer = reinterpret_cast<char*>(WTF::Partitions::FastMalloc(
+ table_size, WTF_HEAP_PROFILER_TYPE_NAME(HarfBuzzFontData)));
+ if (!buffer)
+ return nullptr;
+ size_t actual_size = typeface->getTableData(tag, 0, table_size, buffer);
+ if (table_size != actual_size) {
+ WTF::Partitions::FastFree(buffer);
+ return nullptr;
+ }
+ return hb_blob_create(const_cast<char*>(buffer), table_size,
+ HB_MEMORY_MODE_WRITABLE, buffer,
+ WTF::Partitions::FastFree);
+}
+
+static void DeleteTypefaceStream(void* stream_asset_ptr) {
+ SkStreamAsset* stream_asset =
+ reinterpret_cast<SkStreamAsset*>(stream_asset_ptr);
+ delete stream_asset;
+}
+
+hb_face_t* HarfBuzzFace::CreateFace() {
+#if defined(OS_MACOSX)
+ // hb_face_t needs to be instantiated using the CoreText constructor for
+ // compatibility with AAT font, in which case HarfBuzz' CoreText backend is
+ // used. If we encounter a FreeType backed SkTypeface, for variable fonts on
+ // Mac OS < 10.12, follow the regular OpenType-only codepath below.
+ if (platform_data_->CgFont()) {
+ hb_face_t* face = hb_coretext_face_create(platform_data_->CgFont());
+ DCHECK(face);
+ return face;
+ }
+#endif
+ hb_face_t* face = nullptr;
+
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(BooleanHistogram, zero_copy_success_histogram,
+ ("Blink.Fonts.HarfBuzzFaceZeroCopyAccess"));
+ SkTypeface* typeface = platform_data_->Typeface();
+ CHECK(typeface);
+ int ttc_index = 0;
+ SkStreamAsset* typeface_stream = typeface->openStream(&ttc_index);
+ if (typeface_stream && typeface_stream->getMemoryBase()) {
+ std::unique_ptr<hb_blob_t, void (*)(hb_blob_t*)> face_blob(
+ hb_blob_create(
+ reinterpret_cast<const char*>(typeface_stream->getMemoryBase()),
+ typeface_stream->getLength(), HB_MEMORY_MODE_READONLY,
+ typeface_stream, DeleteTypefaceStream),
+ hb_blob_destroy);
+ face = hb_face_create(face_blob.get(), ttc_index);
+ }
+
+ // Fallback to table copies if there is no in-memory access.
+ if (!face) {
+ face = hb_face_create_for_tables(HarfBuzzSkiaGetTable,
+ platform_data_->Typeface(), nullptr);
+ zero_copy_success_histogram.Count(false);
+ } else {
+ zero_copy_success_histogram.Count(true);
+ }
+
+ DCHECK(face);
+ return face;
+}
+
+scoped_refptr<HbFontCacheEntry> CreateHbFontCacheEntry(hb_face_t* face) {
+ HbFontUniquePtr ot_font(hb_font_create(face));
+ hb_ot_font_set_funcs(ot_font.get());
+ // Creating a sub font means that non-available functions
+ // are found from the parent.
+ hb_font_t* unscaled_font = hb_font_create_sub_font(ot_font.get());
+ scoped_refptr<HbFontCacheEntry> cache_entry =
+ HbFontCacheEntry::Create(unscaled_font);
+ hb_font_set_funcs(unscaled_font, HarfBuzzSkiaGetFontFuncs(),
+ cache_entry->HbFontData(), nullptr);
+ return cache_entry;
+}
+
+static_assert(
+ std::is_same<decltype(SkFontArguments::VariationPosition::Coordinate::axis),
+ decltype(hb_variation_t::tag)>::value &&
+ std::is_same<
+ decltype(SkFontArguments::VariationPosition::Coordinate::value),
+ decltype(hb_variation_t::value)>::value &&
+ sizeof(SkFontArguments::VariationPosition::Coordinate) ==
+ sizeof(hb_variation_t),
+ "Skia and HarfBuzz Variation parameter types must match in structure and "
+ "size.");
+
+hb_font_t* HarfBuzzFace::GetScaledFont(
+ scoped_refptr<UnicodeRangeSet> range_set,
+ VerticalLayoutCallbacks vertical_layout) const {
+ PaintFont paint_font;
+ platform_data_->SetupPaintFont(&paint_font);
+ paint_font.SetTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ harf_buzz_font_data_->range_set_ = std::move(range_set);
+ harf_buzz_font_data_->UpdateFallbackMetricsAndScale(
+ *platform_data_, paint_font.ToSkPaint(), vertical_layout);
+
+ int scale =
+ SkiaTextMetrics::SkiaScalarToHarfBuzzPosition(platform_data_->size());
+ hb_font_set_scale(unscaled_font_, scale, scale);
+ hb_font_set_ptem(unscaled_font_, platform_data_->size() / kCssPixelsPerPoint);
+
+ SkTypeface* typeface = harf_buzz_font_data_->paint_.getTypeface();
+ int axis_count = typeface->getVariationDesignPosition(nullptr, 0);
+ if (axis_count > 0) {
+ Vector<SkFontArguments::VariationPosition::Coordinate> axis_values;
+ axis_values.resize(axis_count);
+ if (typeface->getVariationDesignPosition(axis_values.data(),
+ axis_values.size()) > 0) {
+ hb_font_set_variations(
+ unscaled_font_, reinterpret_cast<hb_variation_t*>(axis_values.data()),
+ axis_values.size());
+ }
+ }
+
+ return unscaled_font_;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_face.h b/chromium/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_face.h
new file mode 100644
index 00000000000..35d81feda56
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_face.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2012 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_HARF_BUZZ_FACE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_HARF_BUZZ_FACE_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/fonts/typesetting_features.h"
+#include "third_party/blink/renderer/platform/fonts/unicode_range_set.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
+
+#include <hb.h>
+
+namespace blink {
+
+class FontPlatformData;
+struct HarfBuzzFontData;
+
+class HarfBuzzFace : public RefCounted<HarfBuzzFace> {
+ WTF_MAKE_NONCOPYABLE(HarfBuzzFace);
+
+ public:
+ static scoped_refptr<HarfBuzzFace> Create(FontPlatformData* platform_data,
+ uint64_t unique_id) {
+ return base::AdoptRef(new HarfBuzzFace(platform_data, unique_id));
+ }
+ ~HarfBuzzFace();
+
+ enum VerticalLayoutCallbacks { PrepareForVerticalLayout, NoVerticalLayout };
+
+ // In order to support the restricting effect of unicode-range optionally a
+ // range restriction can be passed in, which will restrict which glyphs we
+ // return in the harfBuzzGetGlyph function.
+ hb_font_t* GetScaledFont(scoped_refptr<UnicodeRangeSet>,
+ VerticalLayoutCallbacks) const;
+
+ bool HasSpaceInLigaturesOrKerning(TypesettingFeatures);
+ unsigned UnitsPerEmFromHeadTable();
+
+ bool ShouldSubpixelPosition();
+
+ private:
+ HarfBuzzFace(FontPlatformData*, uint64_t);
+
+ hb_face_t* CreateFace();
+ void PrepareHarfBuzzFontData();
+
+ FontPlatformData* platform_data_;
+ uint64_t unique_id_;
+ hb_font_t* unscaled_font_;
+ HarfBuzzFontData* harf_buzz_font_data_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_HARF_BUZZ_FACE_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_font_cache.h b/chromium/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_font_cache.h
new file mode 100644
index 00000000000..a25f2532a92
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_font_cache.h
@@ -0,0 +1,151 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_HARF_BUZZ_FONT_CACHE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_HARF_BUZZ_FONT_CACHE_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/fonts/font_metrics.h"
+#include "third_party/blink/renderer/platform/fonts/opentype/open_type_vertical_data.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_face.h"
+#include "third_party/blink/renderer/platform/fonts/unicode_range_set.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+struct hb_font_t;
+struct hb_face_t;
+
+namespace blink {
+
+struct HbFontDeleter {
+ void operator()(hb_font_t* font);
+};
+
+using HbFontUniquePtr = std::unique_ptr<hb_font_t, HbFontDeleter>;
+
+struct HbFaceDeleter {
+ void operator()(hb_face_t* face);
+};
+
+using HbFaceUniquePtr = std::unique_ptr<hb_face_t, HbFaceDeleter>;
+
+const unsigned kInvalidFallbackMetricsValue = static_cast<unsigned>(-1);
+
+// struct to carry user-pointer data for hb_font_t callback
+// functions/operations, that require information related to a font scaled to a
+// particular size.
+struct HarfBuzzFontData {
+ USING_FAST_MALLOC(HarfBuzzFontData);
+ WTF_MAKE_NONCOPYABLE(HarfBuzzFontData);
+
+ public:
+ HarfBuzzFontData()
+ : paint_(),
+ space_in_gpos_(SpaceGlyphInOpenTypeTables::Unknown),
+ space_in_gsub_(SpaceGlyphInOpenTypeTables::Unknown),
+ vertical_data_(nullptr),
+ range_set_(nullptr) {}
+
+ // The vertical origin and vertical advance functions in HarfBuzzFace require
+ // the ascent and height metrics as fallback in case no specific vertical
+ // layout information is found from the font.
+ void UpdateFallbackMetricsAndScale(
+ const FontPlatformData& platform_data,
+ const SkPaint& paint,
+ HarfBuzzFace::VerticalLayoutCallbacks vertical_layout) {
+ float ascent = 0;
+ float descent = 0;
+ unsigned dummy_ascent_inflation = 0;
+ unsigned dummy_descent_inflation = 0;
+
+ paint_ = paint;
+
+ if (UNLIKELY(vertical_layout == HarfBuzzFace::PrepareForVerticalLayout)) {
+ FontMetrics::AscentDescentWithHacks(
+ ascent, descent, dummy_ascent_inflation, dummy_descent_inflation,
+ platform_data, paint);
+ ascent_fallback_ = ascent;
+ // Simulate the rounding that FontMetrics does so far for returning the
+ // integer Height()
+ height_fallback_ = lroundf(ascent) + lroundf(descent);
+
+ int units_per_em =
+ platform_data.GetHarfBuzzFace()->UnitsPerEmFromHeadTable();
+ if (!units_per_em) {
+ DLOG(ERROR)
+ << "Units per EM is 0 for font used in vertical writing mode.";
+ }
+ size_per_unit_ = platform_data.size() / (units_per_em ? units_per_em : 1);
+ } else {
+ ascent_fallback_ = kInvalidFallbackMetricsValue;
+ height_fallback_ = kInvalidFallbackMetricsValue;
+ size_per_unit_ = kInvalidFallbackMetricsValue;
+ }
+ }
+
+ scoped_refptr<OpenTypeVerticalData> VerticalData() {
+ if (!vertical_data_) {
+ DCHECK_NE(ascent_fallback_, kInvalidFallbackMetricsValue);
+ DCHECK_NE(height_fallback_, kInvalidFallbackMetricsValue);
+ DCHECK_NE(size_per_unit_, kInvalidFallbackMetricsValue);
+
+ vertical_data_ =
+ OpenTypeVerticalData::CreateUnscaled(paint_.refTypeface());
+ }
+ vertical_data_->SetScaleAndFallbackMetrics(size_per_unit_, ascent_fallback_,
+ height_fallback_);
+ return vertical_data_;
+ }
+
+ SkPaint paint_;
+
+ // Capture these scaled fallback metrics from FontPlatformData so that a
+ // OpenTypeVerticalData object can be constructed from them when needed.
+ float size_per_unit_;
+ float ascent_fallback_;
+ float height_fallback_;
+
+ enum class SpaceGlyphInOpenTypeTables { Unknown, Present, NotPresent };
+
+ SpaceGlyphInOpenTypeTables space_in_gpos_;
+ SpaceGlyphInOpenTypeTables space_in_gsub_;
+
+ scoped_refptr<OpenTypeVerticalData> vertical_data_;
+ scoped_refptr<UnicodeRangeSet> range_set_;
+};
+
+// Though we have FontCache class, which provides the cache mechanism for
+// WebKit's font objects, we also need additional caching layer for HarfBuzz to
+// reduce the number of hb_font_t objects created. Without it, we would create
+// an hb_font_t object for every FontPlatformData object. But insted, we only
+// need one for each unique SkTypeface.
+// FIXME, crbug.com/609099: We should fix the FontCache to only keep one
+// FontPlatformData object independent of size, then consider using this here.
+class HbFontCacheEntry : public RefCounted<HbFontCacheEntry> {
+ public:
+ static scoped_refptr<HbFontCacheEntry> Create(hb_font_t* hb_font) {
+ DCHECK(hb_font);
+ return base::AdoptRef(new HbFontCacheEntry(hb_font));
+ }
+
+ hb_font_t* HbFont() { return hb_font_.get(); }
+ HarfBuzzFontData* HbFontData() { return hb_font_data_.get(); }
+
+ private:
+ explicit HbFontCacheEntry(hb_font_t* font)
+ : hb_font_(HbFontUniquePtr(font)),
+ hb_font_data_(std::make_unique<HarfBuzzFontData>()){};
+
+ HbFontUniquePtr hb_font_;
+ std::unique_ptr<HarfBuzzFontData> hb_font_data_;
+};
+
+typedef HashMap<uint64_t,
+ scoped_refptr<HbFontCacheEntry>,
+ WTF::IntHash<uint64_t>,
+ WTF::UnsignedWithZeroKeyHashTraits<uint64_t>>
+ HarfBuzzFontCache;
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.cc b/chromium/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.cc
new file mode 100644
index 00000000000..bf0a73e1184
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.cc
@@ -0,0 +1,947 @@
+/*
+ * Copyright (c) 2012 Google Inc. All rights reserved.
+ * Copyright (C) 2013 BlackBerry Limited. 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 "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.h"
+
+#include <hb.h>
+#include <unicode/uchar.h>
+#include <unicode/uscript.h>
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/platform/fonts/font.h"
+#include "third_party/blink/renderer/platform/fonts/font_description.h"
+#include "third_party/blink/renderer/platform/fonts/font_fallback_iterator.h"
+#include "third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/case_mapping_harf_buzz_buffer_filler.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_face.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_inline_headers.h"
+#include "third_party/blink/renderer/platform/fonts/small_caps_iterator.h"
+#include "third_party/blink/renderer/platform/fonts/utf16_text_iterator.h"
+#include "third_party/blink/renderer/platform/wtf/compiler.h"
+#include "third_party/blink/renderer/platform/wtf/deque.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
+
+namespace blink {
+
+namespace {
+
+#if DCHECK_IS_ON()
+// Check if the ShapeResult has the specified range.
+// |text| and |font| are only for logging.
+void CheckShapeResultRange(const ShapeResult* result,
+ unsigned start,
+ unsigned end,
+ const UChar* text,
+ const Font* font) {
+ DCHECK_LE(start, end);
+ unsigned length = end - start;
+ if (length == result->NumCharacters() &&
+ (!length || (start == result->StartIndexForResult() &&
+ end == result->EndIndexForResult())))
+ return;
+
+ // Log font-family/size as specified.
+ StringBuilder log;
+ log.Append("Font='");
+ const FontDescription& font_description = font->GetFontDescription();
+ for (const FontFamily* family = &font_description.Family();;) {
+ log.Append(family->Family());
+ family = family->Next();
+ if (!family)
+ break;
+ log.Append(", ");
+ }
+ log.Append(String::Format("', %f", font_description.ComputedSize()));
+
+ // Log the primary font with its family name in the font file.
+ const SimpleFontData* font_data = font->PrimaryFont();
+ if (font_data) {
+ const SkTypeface* typeface = font_data->PlatformData().Typeface();
+ SkString family_name;
+ typeface->getFamilyName(&family_name);
+ log.Append(", primary=");
+ log.Append(family_name.c_str());
+ }
+
+ // Log the text to shape.
+ log.Append(String::Format(": %u-%u -> %u-%u:", start, end,
+ result->StartIndexForResult(),
+ result->EndIndexForResult()));
+ for (unsigned i = start; i < end; ++i)
+ log.Append(String::Format(" %02X", text[i]));
+
+ log.Append(", result=");
+ result->ToString(&log);
+
+ NOTREACHED() << log.ToString();
+}
+#endif
+
+} // namespace
+
+enum ReshapeQueueItemAction { kReshapeQueueNextFont, kReshapeQueueRange };
+
+struct ReshapeQueueItem {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ ReshapeQueueItemAction action_;
+ unsigned start_index_;
+ unsigned num_characters_;
+ ReshapeQueueItem(ReshapeQueueItemAction action, unsigned start, unsigned num)
+ : action_(action), start_index_(start), num_characters_(num){};
+};
+
+template <typename T>
+class HarfBuzzScopedPtr {
+ STACK_ALLOCATED();
+ WTF_MAKE_NONCOPYABLE(HarfBuzzScopedPtr);
+
+ public:
+ typedef void (*DestroyFunction)(T*);
+
+ HarfBuzzScopedPtr(T* ptr, DestroyFunction destroy)
+ : ptr_(ptr), destroy_(destroy) {
+ DCHECK(destroy_);
+ }
+ ~HarfBuzzScopedPtr() {
+ if (ptr_)
+ (*destroy_)(ptr_);
+ }
+
+ T* Get() { return ptr_; }
+ void Set(T* ptr) { ptr_ = ptr; }
+
+ private:
+ T* ptr_;
+ DestroyFunction destroy_;
+};
+
+HarfBuzzShaper::HarfBuzzShaper(const UChar* text, unsigned length)
+ : text_(text), text_length_(length) {}
+
+using FeaturesVector = Vector<hb_feature_t, 6>;
+struct RangeData {
+ hb_buffer_t* buffer;
+ const Font* font;
+ TextDirection text_direction;
+ unsigned start;
+ unsigned end;
+ FeaturesVector font_features;
+ Deque<ReshapeQueueItem> reshape_queue;
+
+ hb_direction_t HarfBuzzDirection(CanvasRotationInVertical canvas_rotation) {
+ FontOrientation orientation = font->GetFontDescription().Orientation();
+ hb_direction_t direction =
+ IsVerticalAnyUpright(orientation) &&
+ (canvas_rotation ==
+ CanvasRotationInVertical::kRotateCanvasUpright)
+ ? HB_DIRECTION_TTB
+ : HB_DIRECTION_LTR;
+ return text_direction == TextDirection::kRtl
+ ? HB_DIRECTION_REVERSE(direction)
+ : direction;
+ }
+};
+
+struct BufferSlice {
+ unsigned start_character_index;
+ unsigned num_characters;
+ unsigned start_glyph_index;
+ unsigned num_glyphs;
+};
+
+namespace {
+
+// A port of hb_icu_script_to_script because harfbuzz on CrOS is built
+// without hb-icu. See http://crbug.com/356929
+static inline hb_script_t ICUScriptToHBScript(UScriptCode script) {
+ if (UNLIKELY(script == USCRIPT_INVALID_CODE))
+ return HB_SCRIPT_INVALID;
+
+ return hb_script_from_string(uscript_getShortName(script), -1);
+}
+
+void RoundHarfBuzzPosition(hb_position_t* value) {
+ if ((*value) & 0xFFFF) {
+ // There is a non-zero fractional part in the 16.16 value.
+ *value = static_cast<hb_position_t>(
+ round(static_cast<float>(*value) / (1 << 16)))
+ << 16;
+ }
+}
+
+void RoundHarfBuzzBufferPositions(hb_buffer_t* buffer) {
+ unsigned int len;
+ hb_glyph_position_t* glyph_positions =
+ hb_buffer_get_glyph_positions(buffer, &len);
+ for (unsigned int i = 0; i < len; i++) {
+ hb_glyph_position_t* pos = &glyph_positions[i];
+ RoundHarfBuzzPosition(&pos->x_offset);
+ RoundHarfBuzzPosition(&pos->y_offset);
+ RoundHarfBuzzPosition(&pos->x_advance);
+ RoundHarfBuzzPosition(&pos->y_advance);
+ }
+}
+
+inline bool ShapeRange(hb_buffer_t* buffer,
+ hb_feature_t* font_features,
+ unsigned font_features_size,
+ const SimpleFontData* current_font,
+ scoped_refptr<UnicodeRangeSet> current_font_range_set,
+ UScriptCode current_run_script,
+ hb_direction_t direction,
+ hb_language_t language) {
+ const FontPlatformData* platform_data = &(current_font->PlatformData());
+ HarfBuzzFace* face = platform_data->GetHarfBuzzFace();
+ if (!face) {
+ DLOG(ERROR) << "Could not create HarfBuzzFace from FontPlatformData.";
+ return false;
+ }
+
+ hb_buffer_set_language(buffer, language);
+ hb_buffer_set_script(buffer, ICUScriptToHBScript(current_run_script));
+ hb_buffer_set_direction(buffer, direction);
+
+ hb_font_t* hb_font =
+ face->GetScaledFont(std::move(current_font_range_set),
+ HB_DIRECTION_IS_VERTICAL(direction)
+ ? HarfBuzzFace::PrepareForVerticalLayout
+ : HarfBuzzFace::NoVerticalLayout);
+ hb_shape(hb_font, buffer, font_features, font_features_size);
+
+ // We cannot round all glyph positions during hb_shape because the
+ // hb_font_funcs_set_glyph_h_kerning_func only works for legacy kerning.
+ // OpenType uses gpos tables for kerning and harfbuzz does not call
+ // the callback to let us round as we go.
+ // Without this rounding, we get inconsistent spacing between kern points
+ // if subpixel positioning is disabled.
+ // See http://crbug.com/740385.
+ if (!face->ShouldSubpixelPosition())
+ RoundHarfBuzzBufferPositions(buffer);
+
+ return true;
+}
+
+BufferSlice ComputeSlice(RangeData* range_data,
+ const ReshapeQueueItem& current_queue_item,
+ const hb_glyph_info_t* glyph_info,
+ unsigned num_glyphs,
+ unsigned old_glyph_index,
+ unsigned new_glyph_index) {
+ // Compute the range indices of consecutive shaped or .notdef glyphs.
+ // Cluster information for RTL runs becomes reversed, e.g. glyph 0
+ // has cluster index 5 in a run of 6 characters.
+ BufferSlice result;
+ result.start_glyph_index = old_glyph_index;
+ result.num_glyphs = new_glyph_index - old_glyph_index;
+
+ if (HB_DIRECTION_IS_FORWARD(hb_buffer_get_direction(range_data->buffer))) {
+ result.start_character_index = glyph_info[old_glyph_index].cluster;
+ if (new_glyph_index == num_glyphs) {
+ // Clamp the end offsets of the queue item to the offsets representing
+ // the shaping window.
+ unsigned shape_end =
+ std::min(range_data->end, current_queue_item.start_index_ +
+ current_queue_item.num_characters_);
+ result.num_characters = shape_end - result.start_character_index;
+ } else {
+ result.num_characters =
+ glyph_info[new_glyph_index].cluster - result.start_character_index;
+ }
+ } else {
+ // Direction Backwards
+ result.start_character_index = glyph_info[new_glyph_index - 1].cluster;
+ if (old_glyph_index == 0) {
+ // Clamp the end offsets of the queue item to the offsets representing
+ // the shaping window.
+ unsigned shape_end =
+ std::min(range_data->end, current_queue_item.start_index_ +
+ current_queue_item.num_characters_);
+ result.num_characters = shape_end - result.start_character_index;
+ } else {
+ result.num_characters = glyph_info[old_glyph_index - 1].cluster -
+ glyph_info[new_glyph_index - 1].cluster;
+ }
+ }
+
+ return result;
+}
+
+void QueueCharacters(RangeData* range_data,
+ const SimpleFontData* current_font,
+ bool& font_cycle_queued,
+ const BufferSlice& slice) {
+ if (!font_cycle_queued) {
+ range_data->reshape_queue.push_back(
+ ReshapeQueueItem(kReshapeQueueNextFont, 0, 0));
+ font_cycle_queued = true;
+ }
+
+ DCHECK(slice.num_characters);
+ range_data->reshape_queue.push_back(ReshapeQueueItem(
+ kReshapeQueueRange, slice.start_character_index, slice.num_characters));
+}
+
+CanvasRotationInVertical CanvasRotationForRun(
+ FontOrientation font_orientation,
+ OrientationIterator::RenderOrientation render_orientation) {
+ if (font_orientation == FontOrientation::kVerticalUpright ||
+ (font_orientation == FontOrientation::kVerticalMixed &&
+ render_orientation == OrientationIterator::kOrientationKeep))
+ return CanvasRotationInVertical::kRotateCanvasUpright;
+ return CanvasRotationInVertical::kRegular;
+}
+
+} // namespace
+
+void HarfBuzzShaper::CommitGlyphs(RangeData* range_data,
+ const SimpleFontData* current_font,
+ UScriptCode current_run_script,
+ CanvasRotationInVertical canvas_rotation,
+ bool is_last_resort,
+ const BufferSlice& slice,
+ ShapeResult* shape_result) const {
+ hb_direction_t direction = range_data->HarfBuzzDirection(canvas_rotation);
+ // Here we need to specify glyph positions.
+ ShapeResult::RunInfo* run = new ShapeResult::RunInfo(
+ current_font, direction, canvas_rotation,
+ ICUScriptToHBScript(current_run_script), slice.start_character_index,
+ slice.num_glyphs, slice.num_characters);
+ shape_result->InsertRun(base::WrapUnique(run), slice.start_glyph_index,
+ slice.num_glyphs, range_data->buffer);
+ if (is_last_resort)
+ range_data->font->ReportNotDefGlyph();
+}
+
+void HarfBuzzShaper::ExtractShapeResults(
+ RangeData* range_data,
+ bool& font_cycle_queued,
+ const ReshapeQueueItem& current_queue_item,
+ const SimpleFontData* current_font,
+ UScriptCode current_run_script,
+ CanvasRotationInVertical canvas_rotation,
+ bool is_last_resort,
+ ShapeResult* shape_result) const {
+ enum ClusterResult { kShaped, kNotDef, kUnknown };
+ ClusterResult current_cluster_result = kUnknown;
+ ClusterResult previous_cluster_result = kUnknown;
+ unsigned previous_cluster = 0;
+ unsigned current_cluster = 0;
+
+ // Find first notdef glyph in buffer.
+ unsigned num_glyphs = hb_buffer_get_length(range_data->buffer);
+ hb_glyph_info_t* glyph_info =
+ hb_buffer_get_glyph_infos(range_data->buffer, nullptr);
+
+ unsigned last_change_glyph_index = 0;
+ unsigned previous_cluster_start_glyph_index = 0;
+
+ if (!num_glyphs)
+ return;
+
+ for (unsigned glyph_index = 0; glyph_index < num_glyphs; ++glyph_index) {
+ // We proceed by full clusters and determine a shaping result - either
+ // kShaped or kNotDef for each cluster.
+ ClusterResult glyph_result =
+ glyph_info[glyph_index].codepoint == 0 ? kNotDef : kShaped;
+ previous_cluster = current_cluster;
+ current_cluster = glyph_info[glyph_index].cluster;
+
+ if (current_cluster != previous_cluster) {
+ // We are transitioning to a new cluster (whose shaping result state we
+ // have not looked at yet). This means the cluster we just looked at is
+ // completely analysed and we can determine whether it was fully shaped
+ // and whether that means a state change to the cluster before that one.
+ if ((previous_cluster_result != current_cluster_result) &&
+ previous_cluster_result != kUnknown) {
+ BufferSlice slice = ComputeSlice(
+ range_data, current_queue_item, glyph_info, num_glyphs,
+ last_change_glyph_index, previous_cluster_start_glyph_index);
+ // If the most recent cluster is shaped and there is a state change,
+ // it means the previous ones were unshaped, so we queue them, unless
+ // we're using the last resort font.
+ if (current_cluster_result == kShaped && !is_last_resort) {
+ QueueCharacters(range_data, current_font, font_cycle_queued, slice);
+ } else {
+ // If the most recent cluster is unshaped and there is a state
+ // change, it means the previous one(s) were shaped, so we commit
+ // the glyphs. We also commit when we've reached the last resort
+ // font.
+ CommitGlyphs(range_data, current_font, current_run_script,
+ canvas_rotation, is_last_resort, slice, shape_result);
+ }
+ last_change_glyph_index = previous_cluster_start_glyph_index;
+ }
+
+ // No state change happened, continue.
+ previous_cluster_result = current_cluster_result;
+ previous_cluster_start_glyph_index = glyph_index;
+ // Reset current cluster result.
+ current_cluster_result = glyph_result;
+ } else {
+ // Update and merge current cluster result.
+ current_cluster_result =
+ glyph_result == kShaped && (current_cluster_result == kShaped ||
+ current_cluster_result == kUnknown)
+ ? kShaped
+ : kNotDef;
+ }
+ }
+
+ // End of the run.
+ if (current_cluster_result != previous_cluster_result &&
+ previous_cluster_result != kUnknown && !is_last_resort) {
+ // The last cluster in the run still had shaping status different from
+ // the cluster(s) before it, we need to submit one shaped and one
+ // unshaped segment.
+ if (current_cluster_result == kShaped) {
+ BufferSlice slice = ComputeSlice(
+ range_data, current_queue_item, glyph_info, num_glyphs,
+ last_change_glyph_index, previous_cluster_start_glyph_index);
+ QueueCharacters(range_data, current_font, font_cycle_queued, slice);
+ slice =
+ ComputeSlice(range_data, current_queue_item, glyph_info, num_glyphs,
+ previous_cluster_start_glyph_index, num_glyphs);
+ CommitGlyphs(range_data, current_font, current_run_script,
+ canvas_rotation, is_last_resort, slice, shape_result);
+ } else {
+ BufferSlice slice = ComputeSlice(
+ range_data, current_queue_item, glyph_info, num_glyphs,
+ last_change_glyph_index, previous_cluster_start_glyph_index);
+ CommitGlyphs(range_data, current_font, current_run_script,
+ canvas_rotation, is_last_resort, slice, shape_result);
+ slice =
+ ComputeSlice(range_data, current_queue_item, glyph_info, num_glyphs,
+ previous_cluster_start_glyph_index, num_glyphs);
+ QueueCharacters(range_data, current_font, font_cycle_queued, slice);
+ }
+ } else {
+ // There hasn't been a state change for the last cluster, so we can just
+ // either commit or queue what we have up until here.
+ BufferSlice slice =
+ ComputeSlice(range_data, current_queue_item, glyph_info, num_glyphs,
+ last_change_glyph_index, num_glyphs);
+ if (current_cluster_result == kNotDef && !is_last_resort) {
+ QueueCharacters(range_data, current_font, font_cycle_queued, slice);
+ } else {
+ CommitGlyphs(range_data, current_font, current_run_script,
+ canvas_rotation, is_last_resort, slice, shape_result);
+ }
+ }
+}
+
+bool HarfBuzzShaper::CollectFallbackHintChars(
+ const Deque<ReshapeQueueItem>& reshape_queue,
+ Vector<UChar32>& hint) const {
+ if (!reshape_queue.size())
+ return false;
+
+ hint.clear();
+
+ size_t num_chars_added = 0;
+ for (auto it = reshape_queue.begin(); it != reshape_queue.end(); ++it) {
+ if (it->action_ == kReshapeQueueNextFont)
+ break;
+
+ UChar32 hint_char;
+ CHECK_LE((it->start_index_ + it->num_characters_), text_length_);
+ UTF16TextIterator iterator(text_ + it->start_index_, it->num_characters_);
+ while (iterator.Consume(hint_char)) {
+ hint.push_back(hint_char);
+ num_chars_added++;
+ iterator.Advance();
+ }
+ }
+ return num_chars_added > 0;
+}
+
+namespace {
+
+void SplitUntilNextCaseChange(
+ const UChar* normalized_buffer,
+ Deque<blink::ReshapeQueueItem>* queue,
+ blink::ReshapeQueueItem& current_queue_item,
+ SmallCapsIterator::SmallCapsBehavior& small_caps_behavior) {
+ unsigned num_characters_until_case_change = 0;
+ SmallCapsIterator small_caps_iterator(
+ normalized_buffer + current_queue_item.start_index_,
+ current_queue_item.num_characters_);
+ small_caps_iterator.Consume(&num_characters_until_case_change,
+ &small_caps_behavior);
+ if (num_characters_until_case_change > 0 &&
+ num_characters_until_case_change < current_queue_item.num_characters_) {
+ queue->push_front(blink::ReshapeQueueItem(
+ blink::ReshapeQueueItemAction::kReshapeQueueRange,
+ current_queue_item.start_index_ + num_characters_until_case_change,
+ current_queue_item.num_characters_ - num_characters_until_case_change));
+ current_queue_item.num_characters_ = num_characters_until_case_change;
+ }
+}
+
+hb_feature_t CreateFeature(hb_tag_t tag, uint32_t value = 0) {
+ return {tag, value, 0 /* start */, static_cast<unsigned>(-1) /* end */};
+}
+
+// TODO(kojii): crbug.com/762493 This list is getting long enough to extract out
+// of HarfBuzzShaper.cpp.
+void SetFontFeatures(const Font* font, FeaturesVector* features) {
+ const FontDescription& description = font->GetFontDescription();
+
+ static hb_feature_t no_kern = CreateFeature(HB_TAG('k', 'e', 'r', 'n'));
+ static hb_feature_t no_vkrn = CreateFeature(HB_TAG('v', 'k', 'r', 'n'));
+ switch (description.GetKerning()) {
+ case FontDescription::kNormalKerning:
+ // kern/vkrn are enabled by default
+ break;
+ case FontDescription::kNoneKerning:
+ features->push_back(description.IsVerticalAnyUpright() ? no_vkrn
+ : no_kern);
+ break;
+ case FontDescription::kAutoKerning:
+ break;
+ }
+
+ static hb_feature_t no_clig = CreateFeature(HB_TAG('c', 'l', 'i', 'g'));
+ static hb_feature_t no_liga = CreateFeature(HB_TAG('l', 'i', 'g', 'a'));
+ switch (description.CommonLigaturesState()) {
+ case FontDescription::kDisabledLigaturesState:
+ features->push_back(no_liga);
+ features->push_back(no_clig);
+ break;
+ case FontDescription::kEnabledLigaturesState:
+ // liga and clig are on by default
+ break;
+ case FontDescription::kNormalLigaturesState:
+ break;
+ }
+ static hb_feature_t dlig = CreateFeature(HB_TAG('d', 'l', 'i', 'g'), 1);
+ switch (description.DiscretionaryLigaturesState()) {
+ case FontDescription::kDisabledLigaturesState:
+ // dlig is off by default
+ break;
+ case FontDescription::kEnabledLigaturesState:
+ features->push_back(dlig);
+ break;
+ case FontDescription::kNormalLigaturesState:
+ break;
+ }
+ static hb_feature_t hlig = CreateFeature(HB_TAG('h', 'l', 'i', 'g'), 1);
+ switch (description.HistoricalLigaturesState()) {
+ case FontDescription::kDisabledLigaturesState:
+ // hlig is off by default
+ break;
+ case FontDescription::kEnabledLigaturesState:
+ features->push_back(hlig);
+ break;
+ case FontDescription::kNormalLigaturesState:
+ break;
+ }
+ static hb_feature_t no_calt = CreateFeature(HB_TAG('c', 'a', 'l', 't'));
+ switch (description.ContextualLigaturesState()) {
+ case FontDescription::kDisabledLigaturesState:
+ features->push_back(no_calt);
+ break;
+ case FontDescription::kEnabledLigaturesState:
+ // calt is on by default
+ break;
+ case FontDescription::kNormalLigaturesState:
+ break;
+ }
+
+ static hb_feature_t hwid = CreateFeature(HB_TAG('h', 'w', 'i', 'd'), 1);
+ static hb_feature_t twid = CreateFeature(HB_TAG('t', 'w', 'i', 'd'), 1);
+ static hb_feature_t qwid = CreateFeature(HB_TAG('q', 'w', 'i', 'd'), 1);
+ switch (description.WidthVariant()) {
+ case kHalfWidth:
+ features->push_back(hwid);
+ break;
+ case kThirdWidth:
+ features->push_back(twid);
+ break;
+ case kQuarterWidth:
+ features->push_back(qwid);
+ break;
+ case kRegularWidth:
+ break;
+ }
+
+ // font-variant-east-asian:
+ const FontVariantEastAsian east_asian = description.VariantEastAsian();
+ if (UNLIKELY(!east_asian.IsAllNormal())) {
+ static hb_feature_t jp78 = CreateFeature(HB_TAG('j', 'p', '7', '8'), 1);
+ static hb_feature_t jp83 = CreateFeature(HB_TAG('j', 'p', '8', '3'), 1);
+ static hb_feature_t jp90 = CreateFeature(HB_TAG('j', 'p', '9', '0'), 1);
+ static hb_feature_t jp04 = CreateFeature(HB_TAG('j', 'p', '0', '4'), 1);
+ static hb_feature_t smpl = CreateFeature(HB_TAG('s', 'm', 'p', 'l'), 1);
+ static hb_feature_t trad = CreateFeature(HB_TAG('t', 'r', 'a', 'd'), 1);
+ switch (east_asian.Form()) {
+ case FontVariantEastAsian::kNormalForm:
+ break;
+ case FontVariantEastAsian::kJis78:
+ features->push_back(jp78);
+ break;
+ case FontVariantEastAsian::kJis83:
+ features->push_back(jp83);
+ break;
+ case FontVariantEastAsian::kJis90:
+ features->push_back(jp90);
+ break;
+ case FontVariantEastAsian::kJis04:
+ features->push_back(jp04);
+ break;
+ case FontVariantEastAsian::kSimplified:
+ features->push_back(smpl);
+ break;
+ case FontVariantEastAsian::kTraditional:
+ features->push_back(trad);
+ break;
+ default:
+ NOTREACHED();
+ }
+ static hb_feature_t fwid = CreateFeature(HB_TAG('f', 'w', 'i', 'd'), 1);
+ static hb_feature_t pwid = CreateFeature(HB_TAG('p', 'w', 'i', 'd'), 1);
+ switch (east_asian.Width()) {
+ case FontVariantEastAsian::kNormalWidth:
+ break;
+ case FontVariantEastAsian::kFullWidth:
+ features->push_back(fwid);
+ break;
+ case FontVariantEastAsian::kProportionalWidth:
+ features->push_back(pwid);
+ break;
+ default:
+ NOTREACHED();
+ }
+ static hb_feature_t ruby = CreateFeature(HB_TAG('r', 'u', 'b', 'y'), 1);
+ if (east_asian.Ruby())
+ features->push_back(ruby);
+ }
+
+ // font-variant-numeric:
+ static hb_feature_t lnum = CreateFeature(HB_TAG('l', 'n', 'u', 'm'), 1);
+ if (description.VariantNumeric().NumericFigureValue() ==
+ FontVariantNumeric::kLiningNums)
+ features->push_back(lnum);
+
+ static hb_feature_t onum = CreateFeature(HB_TAG('o', 'n', 'u', 'm'), 1);
+ if (description.VariantNumeric().NumericFigureValue() ==
+ FontVariantNumeric::kOldstyleNums)
+ features->push_back(onum);
+
+ static hb_feature_t pnum = CreateFeature(HB_TAG('p', 'n', 'u', 'm'), 1);
+ if (description.VariantNumeric().NumericSpacingValue() ==
+ FontVariantNumeric::kProportionalNums)
+ features->push_back(pnum);
+ static hb_feature_t tnum = CreateFeature(HB_TAG('t', 'n', 'u', 'm'), 1);
+ if (description.VariantNumeric().NumericSpacingValue() ==
+ FontVariantNumeric::kTabularNums)
+ features->push_back(tnum);
+
+ static hb_feature_t afrc = CreateFeature(HB_TAG('a', 'f', 'r', 'c'), 1);
+ if (description.VariantNumeric().NumericFractionValue() ==
+ FontVariantNumeric::kStackedFractions)
+ features->push_back(afrc);
+ static hb_feature_t frac = CreateFeature(HB_TAG('f', 'r', 'a', 'c'), 1);
+ if (description.VariantNumeric().NumericFractionValue() ==
+ FontVariantNumeric::kDiagonalFractions)
+ features->push_back(frac);
+
+ static hb_feature_t ordn = CreateFeature(HB_TAG('o', 'r', 'd', 'n'), 1);
+ if (description.VariantNumeric().OrdinalValue() ==
+ FontVariantNumeric::kOrdinalOn)
+ features->push_back(ordn);
+
+ static hb_feature_t zero = CreateFeature(HB_TAG('z', 'e', 'r', 'o'), 1);
+ if (description.VariantNumeric().SlashedZeroValue() ==
+ FontVariantNumeric::kSlashedZeroOn)
+ features->push_back(zero);
+
+ FontFeatureSettings* settings = description.FeatureSettings();
+ if (!settings)
+ return;
+
+ // TODO(drott): crbug.com/450619 Implement feature resolution instead of
+ // just appending the font-feature-settings.
+ unsigned num_features = settings->size();
+ for (unsigned i = 0; i < num_features; ++i) {
+ hb_feature_t feature;
+ const AtomicString& tag = settings->at(i).Tag();
+ feature.tag = HB_TAG(tag[0], tag[1], tag[2], tag[3]);
+ feature.value = settings->at(i).Value();
+ feature.start = 0;
+ feature.end = static_cast<unsigned>(-1);
+ features->push_back(feature);
+ }
+}
+
+class CapsFeatureSettingsScopedOverlay final {
+ STACK_ALLOCATED();
+
+ public:
+ CapsFeatureSettingsScopedOverlay(FeaturesVector*,
+ FontDescription::FontVariantCaps);
+ CapsFeatureSettingsScopedOverlay() = delete;
+ ~CapsFeatureSettingsScopedOverlay();
+
+ private:
+ void OverlayCapsFeatures(FontDescription::FontVariantCaps);
+ void PrependCounting(const hb_feature_t&);
+ FeaturesVector* features_;
+ size_t count_features_;
+};
+
+CapsFeatureSettingsScopedOverlay::CapsFeatureSettingsScopedOverlay(
+ FeaturesVector* features,
+ FontDescription::FontVariantCaps variant_caps)
+ : features_(features), count_features_(0) {
+ OverlayCapsFeatures(variant_caps);
+}
+
+void CapsFeatureSettingsScopedOverlay::OverlayCapsFeatures(
+ FontDescription::FontVariantCaps variant_caps) {
+ static hb_feature_t smcp = CreateFeature(HB_TAG('s', 'm', 'c', 'p'), 1);
+ static hb_feature_t pcap = CreateFeature(HB_TAG('p', 'c', 'a', 'p'), 1);
+ static hb_feature_t c2sc = CreateFeature(HB_TAG('c', '2', 's', 'c'), 1);
+ static hb_feature_t c2pc = CreateFeature(HB_TAG('c', '2', 'p', 'c'), 1);
+ static hb_feature_t unic = CreateFeature(HB_TAG('u', 'n', 'i', 'c'), 1);
+ static hb_feature_t titl = CreateFeature(HB_TAG('t', 'i', 't', 'l'), 1);
+ if (variant_caps == FontDescription::kSmallCaps ||
+ variant_caps == FontDescription::kAllSmallCaps) {
+ PrependCounting(smcp);
+ if (variant_caps == FontDescription::kAllSmallCaps) {
+ PrependCounting(c2sc);
+ }
+ }
+ if (variant_caps == FontDescription::kPetiteCaps ||
+ variant_caps == FontDescription::kAllPetiteCaps) {
+ PrependCounting(pcap);
+ if (variant_caps == FontDescription::kAllPetiteCaps) {
+ PrependCounting(c2pc);
+ }
+ }
+ if (variant_caps == FontDescription::kUnicase) {
+ PrependCounting(unic);
+ }
+ if (variant_caps == FontDescription::kTitlingCaps) {
+ PrependCounting(titl);
+ }
+}
+
+void CapsFeatureSettingsScopedOverlay::PrependCounting(
+ const hb_feature_t& feature) {
+ features_->push_front(feature);
+ count_features_++;
+}
+
+CapsFeatureSettingsScopedOverlay::~CapsFeatureSettingsScopedOverlay() {
+ features_->EraseAt(0, count_features_);
+}
+
+} // namespace
+
+void HarfBuzzShaper::ShapeSegment(RangeData* range_data,
+ RunSegmenter::RunSegmenterRange segment,
+ ShapeResult* result) const {
+ DCHECK(result);
+ DCHECK(range_data->buffer);
+
+ const Font* font = range_data->font;
+ const FontDescription& font_description = font->GetFontDescription();
+ const hb_language_t language =
+ font_description.LocaleOrDefault().HarfbuzzLanguage();
+ bool needs_caps_handling =
+ font_description.VariantCaps() != FontDescription::kCapsNormal;
+ OpenTypeCapsSupport caps_support;
+
+ scoped_refptr<FontFallbackIterator> fallback_iterator =
+ font->CreateFontFallbackIterator(segment.font_fallback_priority);
+
+ range_data->reshape_queue.push_back(
+ ReshapeQueueItem(kReshapeQueueNextFont, 0, 0));
+ range_data->reshape_queue.push_back(ReshapeQueueItem(
+ kReshapeQueueRange, segment.start, segment.end - segment.start));
+
+ bool font_cycle_queued = false;
+ Vector<UChar32> fallback_chars_hint;
+ scoped_refptr<FontDataForRangeSet> current_font_data_for_range_set;
+ while (range_data->reshape_queue.size()) {
+ ReshapeQueueItem current_queue_item = range_data->reshape_queue.TakeFirst();
+
+ if (current_queue_item.action_ == kReshapeQueueNextFont) {
+ // For now, we're building a character list with which we probe
+ // for needed fonts depending on the declared unicode-range of a
+ // segmented CSS font. Alternatively, we can build a fake font
+ // for the shaper and check whether any glyphs were found, or
+ // define a new API on the shaper which will give us coverage
+ // information?
+ if (!CollectFallbackHintChars(range_data->reshape_queue,
+ fallback_chars_hint)) {
+ // Give up shaping since we cannot retrieve a font fallback
+ // font without a hintlist.
+ range_data->reshape_queue.clear();
+ break;
+ }
+
+ current_font_data_for_range_set =
+ fallback_iterator->Next(fallback_chars_hint);
+ if (!current_font_data_for_range_set->FontData()) {
+ DCHECK(!range_data->reshape_queue.size());
+ break;
+ }
+ font_cycle_queued = false;
+ continue;
+ }
+
+ const SimpleFontData* font_data =
+ current_font_data_for_range_set->FontData();
+ SmallCapsIterator::SmallCapsBehavior small_caps_behavior =
+ SmallCapsIterator::kSmallCapsSameCase;
+ if (needs_caps_handling) {
+ caps_support = OpenTypeCapsSupport(
+ font_data->PlatformData().GetHarfBuzzFace(),
+ font_description.VariantCaps(), ICUScriptToHBScript(segment.script));
+ if (caps_support.NeedsRunCaseSplitting()) {
+ SplitUntilNextCaseChange(text_, &range_data->reshape_queue,
+ current_queue_item, small_caps_behavior);
+ // Skip queue items generated by SplitUntilNextCaseChange that do not
+ // contribute to the shape result if the range_data restricts shaping to
+ // a substring.
+ if (range_data->start >= current_queue_item.start_index_ +
+ current_queue_item.num_characters_ ||
+ range_data->end <= current_queue_item.start_index_)
+ continue;
+ }
+ }
+
+ DCHECK(current_queue_item.num_characters_);
+ const SimpleFontData* small_caps_adjusted_font =
+ needs_caps_handling &&
+ caps_support.NeedsSyntheticFont(small_caps_behavior)
+ ? font_data->SmallCapsFontData(font_description).get()
+ : font_data;
+
+ CaseMapIntend case_map_intend = CaseMapIntend::kKeepSameCase;
+ if (needs_caps_handling)
+ case_map_intend = caps_support.NeedsCaseChange(small_caps_behavior);
+
+ // Clamp the start and end offsets of the queue item to the offsets
+ // representing the shaping window.
+ unsigned shape_start =
+ std::max(range_data->start, current_queue_item.start_index_);
+ unsigned shape_end =
+ std::min(range_data->end, current_queue_item.start_index_ +
+ current_queue_item.num_characters_);
+ DCHECK_GT(shape_end, shape_start);
+
+ CaseMappingHarfBuzzBufferFiller(
+ case_map_intend, font_description.LocaleOrDefault(), range_data->buffer,
+ text_, text_length_, shape_start, shape_end - shape_start);
+
+ CanvasRotationInVertical canvas_rotation = CanvasRotationForRun(
+ small_caps_adjusted_font->PlatformData().Orientation(),
+ segment.render_orientation);
+
+ CapsFeatureSettingsScopedOverlay caps_overlay(
+ &range_data->font_features,
+ caps_support.FontFeatureToUse(small_caps_behavior));
+ hb_direction_t direction = range_data->HarfBuzzDirection(canvas_rotation);
+
+ if (!ShapeRange(range_data->buffer,
+ range_data->font_features.IsEmpty()
+ ? nullptr
+ : range_data->font_features.data(),
+ range_data->font_features.size(), small_caps_adjusted_font,
+ current_font_data_for_range_set->Ranges(), segment.script,
+ direction, language))
+ DLOG(ERROR) << "Shaping range failed.";
+
+ ExtractShapeResults(range_data, font_cycle_queued, current_queue_item,
+ small_caps_adjusted_font, segment.script,
+ canvas_rotation, !fallback_iterator->HasNext(), result);
+
+ hb_buffer_reset(range_data->buffer);
+ }
+}
+
+scoped_refptr<ShapeResult> HarfBuzzShaper::Shape(const Font* font,
+ TextDirection direction,
+ unsigned start,
+ unsigned end) const {
+ DCHECK(end >= start);
+ DCHECK(end <= text_length_);
+
+ unsigned length = end - start;
+ scoped_refptr<ShapeResult> result = ShapeResult::Create(font, length, direction);
+ HarfBuzzScopedPtr<hb_buffer_t> buffer(hb_buffer_create(), hb_buffer_destroy);
+
+ // Run segmentation needs to operate on the entire string, regardless of the
+ // shaping window (defined by the start and end parameters).
+ RunSegmenter::RunSegmenterRange segment_range = RunSegmenter::NullRange();
+ RunSegmenter run_segmenter(text_, text_length_,
+ font->GetFontDescription().Orientation());
+
+ RangeData range_data;
+ range_data.buffer = buffer.Get();
+ range_data.font = font;
+ range_data.text_direction = direction;
+ range_data.start = start;
+ range_data.end = end;
+ SetFontFeatures(font, &range_data.font_features);
+
+ while (run_segmenter.Consume(&segment_range)) {
+ // Only shape segments overlapping with the range indicated by start and
+ // end. Not only those strictly within.
+ if (start < segment_range.end && end > segment_range.start)
+ ShapeSegment(&range_data, segment_range, result.get());
+ }
+
+ // Ensure we have at least one run for StartIndexForResult().
+ if (UNLIKELY(result->runs_.IsEmpty() && start))
+ result->InsertRunForIndex(start);
+
+#if DCHECK_IS_ON()
+ if (result)
+ CheckShapeResultRange(result.get(), start, end, text_, font);
+#endif
+
+ return result;
+}
+
+scoped_refptr<ShapeResult> HarfBuzzShaper::Shape(const Font* font,
+ TextDirection direction) const {
+ return Shape(font, direction, 0, text_length_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.h b/chromium/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.h
new file mode 100644
index 00000000000..5e86364a4ae
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2012 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_HARF_BUZZ_SHAPER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_HARF_BUZZ_SHAPER_H_
+
+#include "third_party/blink/renderer/platform/fonts/shaping/run_segmenter.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_result.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class Font;
+class SimpleFontData;
+class HarfBuzzShaper;
+struct ReshapeQueueItem;
+struct RangeData;
+struct BufferSlice;
+
+class PLATFORM_EXPORT HarfBuzzShaper final {
+ public:
+ HarfBuzzShaper(const UChar*, unsigned length);
+
+ // Shape a range, defined by the start and end parameters, of the string
+ // supplied to the constructor.
+ // The start and end positions should represent boundaries where a break may
+ // occur, such as at the beginning or end of lines or at element boundaries.
+ // If given arbitrary positions the results are not guaranteed to be correct.
+ // May be called multiple times; font and direction may vary between calls.
+ scoped_refptr<ShapeResult> Shape(const Font*,
+ TextDirection,
+ unsigned start,
+ unsigned end) const;
+
+ // Shape the entire string with a single font and direction.
+ // Equivalent to calling the range version with a start offset of zero and an
+ // end offset equal to the length.
+ scoped_refptr<ShapeResult> Shape(const Font*, TextDirection) const;
+
+ const UChar* GetText() const { return text_; }
+ unsigned TextLength() const { return text_length_; }
+
+ ~HarfBuzzShaper() = default;
+
+ private:
+
+ // Shapes a single seqment, as identified by the RunSegmenterRange parameter,
+ // one or more times taking font fallback into account. The start and end
+ // parameters are for the entire text run, not the segment, and are used to
+ // determine pre- and post-context for shaping.
+ void ShapeSegment(RangeData*,
+ RunSegmenter::RunSegmenterRange,
+ ShapeResult*) const;
+
+ void ExtractShapeResults(RangeData*,
+ bool& font_cycle_queued,
+ const ReshapeQueueItem&,
+ const SimpleFontData*,
+ UScriptCode,
+ CanvasRotationInVertical,
+ bool is_last_resort,
+ ShapeResult*) const;
+
+ bool CollectFallbackHintChars(const Deque<ReshapeQueueItem>&,
+ Vector<UChar32>& hint) const;
+
+ void CommitGlyphs(RangeData*,
+ const SimpleFontData* current_font,
+ UScriptCode current_run_script,
+ CanvasRotationInVertical,
+ bool is_last_resort,
+ const BufferSlice&,
+ ShapeResult*) const;
+
+ const UChar* text_;
+ unsigned text_length_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_HARF_BUZZ_SHAPER_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper_fuzzer.cc b/chromium/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper_fuzzer.cc
new file mode 100644
index 00000000000..7778209f5a8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper_fuzzer.cc
@@ -0,0 +1,52 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/font.h"
+#include "third_party/blink/renderer/platform/fonts/font_cache.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.h"
+#include "third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <unicode/ustring.h>
+
+namespace blink {
+
+constexpr size_t kMaxInputLength = 256;
+
+// TODO crbug.com/771901: BlinkFuzzerTestSupport should also initialize the
+// custom fontconfig configuration that we use for content_shell.
+int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ static BlinkFuzzerTestSupport fuzzer_support = BlinkFuzzerTestSupport();
+ constexpr int32_t kDestinationCapacity = 2 * kMaxInputLength;
+ int32_t converted_length = 0;
+ UChar converted_input_buffer[kDestinationCapacity] = {0};
+ UErrorCode error_code = U_ZERO_ERROR;
+
+ // Discard trailing bytes.
+ u_strFromUTF32(converted_input_buffer, kDestinationCapacity,
+ &converted_length, reinterpret_cast<const UChar32*>(data),
+ size / sizeof(UChar32), &error_code);
+ if (U_FAILURE(error_code))
+ return 0;
+
+ FontCachePurgePreventer font_cache_purge_preventer;
+ FontDescription font_description;
+ Font font(font_description);
+ // Set font size to something other than the default 0 size in
+ // FontDescription, 16 matches the default text size in HTML.
+ font_description.SetComputedSize(16.0f);
+ // Only look for system fonts for now.
+ font.Update(nullptr);
+
+ HarfBuzzShaper shaper(converted_input_buffer, converted_length);
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, TextDirection::kLtr);
+ return 0;
+}
+
+} // namespace blink
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ return blink::LLVMFuzzerTestOneInput(data, size);
+}
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper_test.cc b/chromium/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper_test.cc
new file mode 100644
index 00000000000..f038dd7f45a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper_test.cc
@@ -0,0 +1,1258 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.h"
+
+#include <unicode/uscript.h>
+
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/fonts/font.h"
+#include "third_party/blink/renderer/platform/fonts/font_cache.h"
+#include "third_party/blink/renderer/platform/fonts/font_test_utilities.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_inline_headers.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_test_info.h"
+#include "third_party/blink/renderer/platform/layout_test_support.h"
+#include "third_party/blink/renderer/platform/testing/font_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
+#include "third_party/blink/renderer/platform/text/text_break_iterator.h"
+#include "third_party/blink/renderer/platform/text/text_run.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class HarfBuzzShaperTest : public testing::Test {
+ protected:
+ void SetUp() override {
+ font_description.SetComputedSize(12.0);
+ font = Font(font_description);
+ font.Update(nullptr);
+ }
+
+ void TearDown() override {}
+
+ FontCachePurgePreventer font_cache_purge_preventer;
+ FontDescription font_description;
+ Font font;
+ unsigned start_index = 0;
+ unsigned num_characters = 0;
+ unsigned num_glyphs = 0;
+ hb_script_t script = HB_SCRIPT_INVALID;
+};
+
+class ScopedSubpixelOverride {
+ public:
+ ScopedSubpixelOverride(bool b) {
+ prev_layout_test_ = LayoutTestSupport::IsRunningLayoutTest();
+ prev_subpixel_allowed_ =
+ LayoutTestSupport::IsTextSubpixelPositioningAllowedForTest();
+ prev_antialias_ = LayoutTestSupport::IsFontAntialiasingEnabledForTest();
+ prev_fd_subpixel_ = FontDescription::SubpixelPositioning();
+
+ // This is required for all LayoutTestSupport settings to have effects.
+ LayoutTestSupport::SetIsRunningLayoutTest(true);
+
+ if (b) {
+ // Allow subpixel positioning.
+ LayoutTestSupport::SetTextSubpixelPositioningAllowedForTest(true);
+
+ // Now, enable subpixel positioning in platform-specific ways.
+
+ // Mac always enables subpixel positioning.
+
+ // On Windows, subpixel positioning also requires antialiasing.
+ LayoutTestSupport::SetFontAntialiasingEnabledForTest(true);
+
+ // On platforms other than Windows and Mac this needs to be set as
+ // well.
+ FontDescription::SetSubpixelPositioning(true);
+ } else {
+ // Explicitly disallow all subpixel positioning.
+ LayoutTestSupport::SetTextSubpixelPositioningAllowedForTest(false);
+ }
+ }
+ ~ScopedSubpixelOverride() {
+ FontDescription::SetSubpixelPositioning(prev_fd_subpixel_);
+ LayoutTestSupport::SetFontAntialiasingEnabledForTest(prev_antialias_);
+ LayoutTestSupport::SetTextSubpixelPositioningAllowedForTest(
+ prev_subpixel_allowed_);
+ LayoutTestSupport::SetIsRunningLayoutTest(prev_layout_test_);
+
+ // Fonts cached with a different subpixel positioning state are not
+ // automatically invalidated and need to be cleared between test
+ // runs.
+ FontCache::GetFontCache()->Invalidate();
+ }
+
+ private:
+ bool prev_layout_test_;
+ bool prev_subpixel_allowed_;
+ bool prev_antialias_;
+ bool prev_fd_subpixel_;
+};
+
+class ShapeParameterTest : public HarfBuzzShaperTest,
+ public testing::WithParamInterface<TextDirection> {
+ protected:
+ scoped_refptr<ShapeResult> ShapeWithParameter(HarfBuzzShaper* shaper) {
+ TextDirection direction = GetParam();
+ return shaper->Shape(&font, direction);
+ }
+};
+
+INSTANTIATE_TEST_CASE_P(HarfBuzzShaperTest,
+ ShapeParameterTest,
+ testing::Values(TextDirection::kLtr,
+ TextDirection::kRtl));
+
+static inline ShapeResultTestInfo* TestInfo(scoped_refptr<ShapeResult>& result) {
+ return static_cast<ShapeResultTestInfo*>(result.get());
+}
+
+TEST_F(HarfBuzzShaperTest, MutableUnique) {
+ scoped_refptr<ShapeResult> result =
+ ShapeResult::Create(&font, 0, TextDirection::kLtr);
+ EXPECT_TRUE(result->HasOneRef());
+
+ // At this point, |result| has only one ref count.
+ scoped_refptr<ShapeResult> result2 = result->MutableUnique();
+ EXPECT_EQ(result.get(), result2.get());
+ EXPECT_FALSE(result2->HasOneRef());
+
+ // Since |result| has 2 ref counts, it should return a clone.
+ scoped_refptr<ShapeResult> result3 = result->MutableUnique();
+ EXPECT_NE(result.get(), result3.get());
+ EXPECT_TRUE(result3->HasOneRef());
+}
+
+TEST_F(HarfBuzzShaperTest, ResolveCandidateRunsLatin) {
+ String latin_common = To16Bit("ABC DEF.", 8);
+ HarfBuzzShaper shaper(latin_common.Characters16(), 8);
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, TextDirection::kLtr);
+
+ EXPECT_EQ(1u, TestInfo(result)->NumberOfRunsForTesting());
+ ASSERT_TRUE(
+ TestInfo(result)->RunInfoForTesting(0, start_index, num_glyphs, script));
+ EXPECT_EQ(0u, start_index);
+ EXPECT_EQ(8u, num_glyphs);
+ EXPECT_EQ(HB_SCRIPT_LATIN, script);
+}
+
+TEST_F(HarfBuzzShaperTest, ResolveCandidateRunsLeadingCommon) {
+ String leading_common = To16Bit("... test", 8);
+ HarfBuzzShaper shaper(leading_common.Characters16(), 8);
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, TextDirection::kLtr);
+
+ EXPECT_EQ(1u, TestInfo(result)->NumberOfRunsForTesting());
+ ASSERT_TRUE(
+ TestInfo(result)->RunInfoForTesting(0, start_index, num_glyphs, script));
+ EXPECT_EQ(0u, start_index);
+ EXPECT_EQ(8u, num_glyphs);
+ EXPECT_EQ(HB_SCRIPT_LATIN, script);
+}
+
+TEST_F(HarfBuzzShaperTest, ResolveCandidateRunsUnicodeVariants) {
+ struct {
+ const char* name;
+ UChar string[4];
+ unsigned length;
+ hb_script_t script;
+ } testlist[] = {
+ {"Standard Variants text style", {0x30, 0xFE0E}, 2, HB_SCRIPT_COMMON},
+ {"Standard Variants emoji style", {0x203C, 0xFE0F}, 2, HB_SCRIPT_COMMON},
+ {"Standard Variants of Ideograph", {0x4FAE, 0xFE00}, 2, HB_SCRIPT_HAN},
+ {"Ideographic Variants", {0x3402, 0xDB40, 0xDD00}, 3, HB_SCRIPT_HAN},
+ {"Not-defined Variants", {0x41, 0xDB40, 0xDDEF}, 3, HB_SCRIPT_LATIN},
+ };
+ for (auto& test : testlist) {
+ HarfBuzzShaper shaper(test.string, test.length);
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, TextDirection::kLtr);
+
+ EXPECT_EQ(1u, TestInfo(result)->NumberOfRunsForTesting()) << test.name;
+ ASSERT_TRUE(
+ TestInfo(result)->RunInfoForTesting(0, start_index, num_glyphs, script))
+ << test.name;
+ EXPECT_EQ(0u, start_index) << test.name;
+ if (num_glyphs == 2) {
+// If the specified VS is not in the font, it's mapped to .notdef.
+// then hb_ot_hide_default_ignorables() swaps it to a space with zero-advance.
+// http://lists.freedesktop.org/archives/harfbuzz/2015-May/004888.html
+#if !defined(OS_MACOSX)
+ EXPECT_EQ(TestInfo(result)->FontDataForTesting(0)->SpaceGlyph(),
+ TestInfo(result)->GlyphForTesting(0, 1))
+ << test.name;
+#endif
+ EXPECT_EQ(0.f, TestInfo(result)->AdvanceForTesting(0, 1)) << test.name;
+ } else {
+ EXPECT_EQ(1u, num_glyphs) << test.name;
+ }
+ EXPECT_EQ(test.script, script) << test.name;
+ }
+}
+
+TEST_F(HarfBuzzShaperTest, ResolveCandidateRunsDevanagariCommon) {
+ UChar devanagari_common_string[] = {0x915, 0x94d, 0x930, 0x28, 0x20, 0x29};
+ String devanagari_common_latin(devanagari_common_string, 6);
+ HarfBuzzShaper shaper(devanagari_common_latin.Characters16(), 6);
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, TextDirection::kLtr);
+
+ EXPECT_EQ(2u, TestInfo(result)->NumberOfRunsForTesting());
+ ASSERT_TRUE(
+ TestInfo(result)->RunInfoForTesting(0, start_index, num_glyphs, script));
+ EXPECT_EQ(0u, start_index);
+ EXPECT_EQ(1u, num_glyphs);
+ EXPECT_EQ(HB_SCRIPT_DEVANAGARI, script);
+
+ ASSERT_TRUE(
+ TestInfo(result)->RunInfoForTesting(1, start_index, num_glyphs, script));
+ EXPECT_EQ(3u, start_index);
+ EXPECT_EQ(3u, num_glyphs);
+ EXPECT_EQ(HB_SCRIPT_DEVANAGARI, script);
+}
+
+TEST_F(HarfBuzzShaperTest, ResolveCandidateRunsDevanagariCommonLatinCommon) {
+ UChar devanagari_common_latin_string[] = {0x915, 0x94d, 0x930, 0x20,
+ 0x61, 0x62, 0x2E};
+ HarfBuzzShaper shaper(devanagari_common_latin_string, 7);
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, TextDirection::kLtr);
+
+ EXPECT_EQ(3u, TestInfo(result)->NumberOfRunsForTesting());
+ ASSERT_TRUE(
+ TestInfo(result)->RunInfoForTesting(0, start_index, num_glyphs, script));
+ EXPECT_EQ(0u, start_index);
+ EXPECT_EQ(1u, num_glyphs);
+ EXPECT_EQ(HB_SCRIPT_DEVANAGARI, script);
+
+ ASSERT_TRUE(
+ TestInfo(result)->RunInfoForTesting(1, start_index, num_glyphs, script));
+ EXPECT_EQ(3u, start_index);
+ EXPECT_EQ(1u, num_glyphs);
+ EXPECT_EQ(HB_SCRIPT_DEVANAGARI, script);
+
+ ASSERT_TRUE(
+ TestInfo(result)->RunInfoForTesting(2, start_index, num_glyphs, script));
+ EXPECT_EQ(4u, start_index);
+ EXPECT_EQ(3u, num_glyphs);
+ EXPECT_EQ(HB_SCRIPT_LATIN, script);
+}
+
+TEST_F(HarfBuzzShaperTest, ResolveCandidateRunsArabicThaiHanLatin) {
+ UChar mixed_string[] = {0x628, 0x64A, 0x629, 0xE20, 0x65E5, 0x62};
+ HarfBuzzShaper shaper(mixed_string, 6);
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, TextDirection::kLtr);
+
+ EXPECT_EQ(4u, TestInfo(result)->NumberOfRunsForTesting());
+ ASSERT_TRUE(
+ TestInfo(result)->RunInfoForTesting(0, start_index, num_glyphs, script));
+ EXPECT_EQ(0u, start_index);
+ EXPECT_EQ(3u, num_glyphs);
+ EXPECT_EQ(HB_SCRIPT_ARABIC, script);
+
+ ASSERT_TRUE(
+ TestInfo(result)->RunInfoForTesting(1, start_index, num_glyphs, script));
+ EXPECT_EQ(3u, start_index);
+ EXPECT_EQ(1u, num_glyphs);
+ EXPECT_EQ(HB_SCRIPT_THAI, script);
+
+ ASSERT_TRUE(
+ TestInfo(result)->RunInfoForTesting(2, start_index, num_glyphs, script));
+ EXPECT_EQ(4u, start_index);
+ EXPECT_EQ(1u, num_glyphs);
+ EXPECT_EQ(HB_SCRIPT_HAN, script);
+
+ ASSERT_TRUE(
+ TestInfo(result)->RunInfoForTesting(3, start_index, num_glyphs, script));
+ EXPECT_EQ(5u, start_index);
+ EXPECT_EQ(1u, num_glyphs);
+ EXPECT_EQ(HB_SCRIPT_LATIN, script);
+}
+
+TEST_F(HarfBuzzShaperTest, ResolveCandidateRunsArabicThaiHanLatinTwice) {
+ UChar mixed_string[] = {0x628, 0x64A, 0x629, 0xE20, 0x65E5, 0x62};
+ HarfBuzzShaper shaper(mixed_string, 6);
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, TextDirection::kLtr);
+ EXPECT_EQ(4u, TestInfo(result)->NumberOfRunsForTesting());
+
+ // Shape again on the same shape object and check the number of runs.
+ // Should be equal if no state was retained between shape calls.
+ scoped_refptr<ShapeResult> result2 = shaper.Shape(&font, TextDirection::kLtr);
+ EXPECT_EQ(4u, TestInfo(result2)->NumberOfRunsForTesting());
+}
+
+TEST_F(HarfBuzzShaperTest, ResolveCandidateRunsArabic) {
+ UChar arabic_string[] = {0x628, 0x64A, 0x629};
+ HarfBuzzShaper shaper(arabic_string, 3);
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, TextDirection::kRtl);
+
+ EXPECT_EQ(1u, TestInfo(result)->NumberOfRunsForTesting());
+ ASSERT_TRUE(
+ TestInfo(result)->RunInfoForTesting(0, start_index, num_glyphs, script));
+ EXPECT_EQ(0u, start_index);
+ EXPECT_EQ(3u, num_glyphs);
+ EXPECT_EQ(HB_SCRIPT_ARABIC, script);
+}
+
+// This is a simplified test and doesn't accuratly reflect how the shape range
+// is to be used. If you instead of the string you imagine the following HTML:
+// <div>Hello <span>World</span>!</div>
+// It better reflects the intended use where the range given to each shape call
+// corresponds to the text content of a TextNode.
+TEST_F(HarfBuzzShaperTest, ShapeLatinSegment) {
+ String string = To16Bit("Hello World!", 12);
+ TextDirection direction = TextDirection::kLtr;
+
+ HarfBuzzShaper shaper(string.Characters16(), 12);
+ scoped_refptr<ShapeResult> combined = shaper.Shape(&font, direction);
+ scoped_refptr<ShapeResult> first = shaper.Shape(&font, direction, 0, 6);
+ scoped_refptr<ShapeResult> second = shaper.Shape(&font, direction, 6, 11);
+ scoped_refptr<ShapeResult> third = shaper.Shape(&font, direction, 11, 12);
+
+ ASSERT_TRUE(TestInfo(first)->RunInfoForTesting(0, start_index, num_characters,
+ num_glyphs, script));
+ EXPECT_EQ(0u, start_index);
+ EXPECT_EQ(6u, num_characters);
+ ASSERT_TRUE(TestInfo(second)->RunInfoForTesting(
+ 0, start_index, num_characters, num_glyphs, script));
+ EXPECT_EQ(6u, start_index);
+ EXPECT_EQ(5u, num_characters);
+ ASSERT_TRUE(TestInfo(third)->RunInfoForTesting(0, start_index, num_characters,
+ num_glyphs, script));
+ EXPECT_EQ(11u, start_index);
+ EXPECT_EQ(1u, num_characters);
+
+ HarfBuzzShaper shaper2(string.Characters16(), 6);
+ scoped_refptr<ShapeResult> first_reference = shaper2.Shape(&font, direction);
+
+ HarfBuzzShaper shaper3(string.Characters16() + 6, 5);
+ scoped_refptr<ShapeResult> second_reference = shaper3.Shape(&font, direction);
+
+ HarfBuzzShaper shaper4(string.Characters16() + 11, 1);
+ scoped_refptr<ShapeResult> third_reference = shaper4.Shape(&font, direction);
+
+ // Width of each segment should be the same when shaped using start and end
+ // offset as it is when shaping the three segments using separate shaper
+ // instances.
+ // A full pixel is needed for tolerance to account for kerning on some
+ // platforms.
+ ASSERT_NEAR(first_reference->Width(), first->Width(), 1);
+ ASSERT_NEAR(second_reference->Width(), second->Width(), 1);
+ ASSERT_NEAR(third_reference->Width(), third->Width(), 1);
+
+ // Width of shape results for the entire string should match the combined
+ // shape results from the three segments.
+ float total_width = first->Width() + second->Width() + third->Width();
+ ASSERT_NEAR(combined->Width(), total_width, 1);
+}
+
+// Represents the case where a part of a cluster has a different color.
+// <div>0x647<span style="color: red;">0x64A</span></div>
+// This test requires context-aware shaping which hasn't been implemented yet.
+// See crbug.com/689155
+TEST_F(HarfBuzzShaperTest, DISABLED_ShapeArabicWithContext) {
+ UChar arabic_string[] = {0x647, 0x64A};
+ HarfBuzzShaper shaper(arabic_string, 2);
+
+ scoped_refptr<ShapeResult> combined = shaper.Shape(&font, TextDirection::kRtl);
+
+ scoped_refptr<ShapeResult> first = shaper.Shape(&font, TextDirection::kRtl, 0, 1);
+ scoped_refptr<ShapeResult> second = shaper.Shape(&font, TextDirection::kRtl, 1, 2);
+
+ // Combined width should be the same when shaping the two characters
+ // separately as when shaping them combined.
+ ASSERT_NEAR(combined->Width(), first->Width() + second->Width(), 0.1);
+}
+
+TEST_F(HarfBuzzShaperTest, ShapeVerticalUpright) {
+ font_description.SetOrientation(FontOrientation::kVerticalUpright);
+ font = Font(font_description);
+ font.Update(nullptr);
+
+ // This string should create 2 runs, ideographic and Latin, both in upright.
+ String string(u"\u65E5\u65E5\u65E5lllll");
+ TextDirection direction = TextDirection::kLtr;
+ HarfBuzzShaper shaper(string.Characters16(), string.length());
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, direction);
+
+ // Check width and bounds are not too much different. ".1" is heuristic.
+ EXPECT_NEAR(result->Width(), result->Bounds().Width(), result->Width() * .1);
+
+ // Shape each run and merge them using CopyRange. Bounds() should match.
+ scoped_refptr<ShapeResult> result1 = shaper.Shape(&font, direction, 0, 3);
+ scoped_refptr<ShapeResult> result2 =
+ shaper.Shape(&font, direction, 3, string.length());
+
+ scoped_refptr<ShapeResult> composite_result =
+ ShapeResult::Create(&font, 0, direction);
+ result1->CopyRange(0, 3, composite_result.get());
+ result2->CopyRange(3, string.length(), composite_result.get());
+
+ EXPECT_EQ(result->Bounds(), composite_result->Bounds());
+}
+
+TEST_F(HarfBuzzShaperTest, RangeShapeSmallCaps) {
+ // Test passes if no assertion is hit of the ones below, but also the newly
+ // introduced one in HarfBuzzShaper::ShapeSegment: DCHECK_GT(shape_end,
+ // shape_start) is not hit.
+ FontDescription font_description;
+ font_description.SetVariantCaps(FontDescription::kSmallCaps);
+ font_description.SetComputedSize(12.0);
+ Font font(font_description);
+ font.Update(nullptr);
+
+ // Shaping index 2 to 3 means that case splitting for small caps splits before
+ // character index 2 since the initial 'a' needs to be uppercased, but the
+ // space character does not need to be uppercased. This triggered
+ // crbug.com/817271.
+ String string(u"a aa");
+ HarfBuzzShaper shaper(string.Characters16(), string.length());
+ scoped_refptr<ShapeResult> result =
+ shaper.Shape(&font, TextDirection::kLtr, 2, 3);
+ EXPECT_EQ(1u, result->NumCharacters());
+
+ string = u"aa a";
+ HarfBuzzShaper shaper_two(string.Characters16(), string.length());
+ result = shaper_two.Shape(&font, TextDirection::kLtr, 3, 4);
+ EXPECT_EQ(1u, result->NumCharacters());
+
+ string = u"a aa";
+ HarfBuzzShaper shaper_three(string.Characters16(), string.length());
+ result = shaper_three.Shape(&font, TextDirection::kLtr, 1, 2);
+ EXPECT_EQ(1u, result->NumCharacters());
+
+ string = u"aa aa aa aa aa aa aa aa aa aa";
+ HarfBuzzShaper shaper_four(string.Characters16(), string.length());
+ result = shaper_four.Shape(&font, TextDirection::kLtr, 21, 23);
+ EXPECT_EQ(2u, result->NumCharacters());
+
+ string = u"aa aa aa aa aa aa aa aa aa aa";
+ HarfBuzzShaper shaper_five(string.Characters16(), string.length());
+ result = shaper_five.Shape(&font, TextDirection::kLtr, 27, 29);
+ EXPECT_EQ(2u, result->NumCharacters());
+}
+
+TEST_F(HarfBuzzShaperTest, ShapeVerticalMixed) {
+ font_description.SetOrientation(FontOrientation::kVerticalMixed);
+ font = Font(font_description);
+ font.Update(nullptr);
+
+ // This string should create 2 runs, ideographic in upright and Latin in
+ // rotated horizontal.
+ String string(u"\u65E5\u65E5\u65E5lllll");
+ TextDirection direction = TextDirection::kLtr;
+ HarfBuzzShaper shaper(string.Characters16(), string.length());
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, direction);
+
+ // Check width and bounds are not too much different. ".1" is heuristic.
+ EXPECT_NEAR(result->Width(), result->Bounds().Width(), result->Width() * .1);
+
+ // Shape each run and merge them using CopyRange. Bounds() should match.
+ scoped_refptr<ShapeResult> result1 = shaper.Shape(&font, direction, 0, 3);
+ scoped_refptr<ShapeResult> result2 =
+ shaper.Shape(&font, direction, 3, string.length());
+
+ scoped_refptr<ShapeResult> composite_result =
+ ShapeResult::Create(&font, 0, direction);
+ result1->CopyRange(0, 3, composite_result.get());
+ result2->CopyRange(3, string.length(), composite_result.get());
+
+ EXPECT_EQ(result->Bounds(), composite_result->Bounds());
+}
+
+TEST_P(ShapeParameterTest, MissingGlyph) {
+ // U+FFF0 is not assigned as of Unicode 10.0.
+ String string(
+ u"\uFFF0"
+ u"Hello");
+ HarfBuzzShaper shaper(string.Characters16(), string.length());
+ scoped_refptr<ShapeResult> result = ShapeWithParameter(&shaper);
+ EXPECT_EQ(0u, result->StartIndexForResult());
+ EXPECT_EQ(string.length(), result->EndIndexForResult());
+}
+
+TEST_P(ShapeParameterTest, ZeroWidthSpace) {
+ UChar string[] = {kZeroWidthSpaceCharacter,
+ kZeroWidthSpaceCharacter,
+ 0x0627,
+ 0x0631,
+ 0x062F,
+ 0x0648,
+ kZeroWidthSpaceCharacter,
+ kZeroWidthSpaceCharacter};
+ const unsigned length = WTF_ARRAY_LENGTH(string);
+ HarfBuzzShaper shaper(string, length);
+ scoped_refptr<ShapeResult> result = ShapeWithParameter(&shaper);
+ EXPECT_EQ(0u, result->StartIndexForResult());
+ EXPECT_EQ(length, result->EndIndexForResult());
+#if DCHECK_IS_ON()
+ result->CheckConsistency();
+#endif
+}
+
+TEST_F(HarfBuzzShaperTest, NegativeLetterSpacing) {
+ String string(u"Hello");
+ HarfBuzzShaper shaper(string.Characters16(), string.length());
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, TextDirection::kLtr);
+ float width = result->Width();
+ FloatRect bounds = result->Bounds();
+
+ ShapeResultSpacing<String> spacing(string);
+ FontDescription font_description;
+ font_description.SetLetterSpacing(-5);
+ spacing.SetSpacing(font_description);
+ result->ApplySpacing(spacing);
+
+ EXPECT_EQ(5 * 5, width - result->Width());
+ EXPECT_EQ(5 * 4 - 1, bounds.Width() - result->Bounds().Width());
+}
+
+TEST_F(HarfBuzzShaperTest, NegativeLetterSpacingTo0) {
+ String string(u"00000");
+ HarfBuzzShaper shaper(string.Characters16(), string.length());
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, TextDirection::kLtr);
+ float char_width = result->Width() / string.length();
+
+ ShapeResultSpacing<String> spacing(string);
+ FontDescription font_description;
+ font_description.SetLetterSpacing(-char_width);
+ spacing.SetSpacing(font_description);
+ result->ApplySpacing(spacing);
+
+ // EXPECT_EQ(0.0f, result->Width());
+ EXPECT_NEAR(0.0f, result->Bounds().X(), 1);
+ // Because all characters are at 0, the glyph bounds must be the char_width.
+ // Allow being larger because accurate width requires re-measuring each glyph.
+ EXPECT_GE(result->Bounds().MaxX(), char_width);
+ EXPECT_LE(result->Bounds().MaxX(), char_width * 1.2);
+}
+
+TEST_F(HarfBuzzShaperTest, NegativeLetterSpacingToNegative) {
+ String string(u"00000");
+ HarfBuzzShaper shaper(string.Characters16(), string.length());
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, TextDirection::kLtr);
+ float char_width = result->Width() / string.length();
+
+ ShapeResultSpacing<String> spacing(string);
+ FontDescription font_description;
+ font_description.SetLetterSpacing(-2 * char_width);
+ spacing.SetSpacing(font_description);
+ result->ApplySpacing(spacing);
+
+ // CSS does not allow negative width, it should be clampled to 0.
+ // EXPECT_EQ(0.0f, result->Width());
+ // Glyph bounding box should overflow to the left.
+ EXPECT_EQ(-char_width * string.length(), result->Bounds().X());
+ // MaxX() should be char_width. Allow being larger.
+ EXPECT_GE(result->Bounds().MaxX(), char_width);
+}
+
+TEST_F(HarfBuzzShaperTest, PositionForOffsetLatin) {
+ String string = To16Bit("Hello World!", 12);
+ TextDirection direction = TextDirection::kLtr;
+
+ HarfBuzzShaper shaper(string.Characters16(), 12);
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, direction);
+ scoped_refptr<ShapeResult> first = shaper.Shape(&font, direction, 0, 5); // Hello
+ scoped_refptr<ShapeResult> second = shaper.Shape(&font, direction, 6, 11); // World
+
+ EXPECT_EQ(0.0f, result->PositionForOffset(0));
+ ASSERT_NEAR(first->Width(), result->PositionForOffset(5), 1);
+ ASSERT_NEAR(second->Width(),
+ result->PositionForOffset(11) - result->PositionForOffset(6), 1);
+ ASSERT_NEAR(result->Width(), result->PositionForOffset(12), 0.1);
+}
+
+TEST_F(HarfBuzzShaperTest, PositionForOffsetArabic) {
+ UChar arabic_string[] = {0x628, 0x64A, 0x629};
+ TextDirection direction = TextDirection::kRtl;
+
+ HarfBuzzShaper shaper(arabic_string, 3);
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, direction);
+
+ EXPECT_EQ(0.0f, result->PositionForOffset(3));
+ ASSERT_NEAR(result->Width(), result->PositionForOffset(0), 0.1);
+}
+
+TEST_F(HarfBuzzShaperTest, EmojiZWJSequence) {
+ UChar emoji_zwj_sequence[] = {0x270C, 0x200D, 0xD83C, 0xDFFF,
+ 0x270C, 0x200D, 0xD83C, 0xDFFC};
+ TextDirection direction = TextDirection::kLtr;
+
+ HarfBuzzShaper shaper(emoji_zwj_sequence, arraysize(emoji_zwj_sequence));
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, direction);
+}
+
+// A Value-Parameterized Test class to test OffsetForPosition() with
+// |include_partial_glyphs| parameter.
+class IncludePartialGlyphs : public HarfBuzzShaperTest,
+ public testing::WithParamInterface<bool> {};
+
+INSTANTIATE_TEST_CASE_P(OffsetForPositionTest,
+ IncludePartialGlyphs,
+ testing::Bool());
+
+TEST_P(IncludePartialGlyphs, OffsetForPositionMatchesPositionForOffsetLatin) {
+ String string = To16Bit("Hello World!", 12);
+ TextDirection direction = TextDirection::kLtr;
+
+ HarfBuzzShaper shaper(string.Characters16(), 12);
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, direction);
+
+ bool include_partial_glyphs = GetParam();
+ EXPECT_EQ(0u, result->OffsetForPosition(result->PositionForOffset(0),
+ include_partial_glyphs));
+ EXPECT_EQ(1u, result->OffsetForPosition(result->PositionForOffset(1),
+ include_partial_glyphs));
+ EXPECT_EQ(2u, result->OffsetForPosition(result->PositionForOffset(2),
+ include_partial_glyphs));
+ EXPECT_EQ(3u, result->OffsetForPosition(result->PositionForOffset(3),
+ include_partial_glyphs));
+ EXPECT_EQ(4u, result->OffsetForPosition(result->PositionForOffset(4),
+ include_partial_glyphs));
+ EXPECT_EQ(5u, result->OffsetForPosition(result->PositionForOffset(5),
+ include_partial_glyphs));
+ EXPECT_EQ(6u, result->OffsetForPosition(result->PositionForOffset(6),
+ include_partial_glyphs));
+ EXPECT_EQ(7u, result->OffsetForPosition(result->PositionForOffset(7),
+ include_partial_glyphs));
+ EXPECT_EQ(8u, result->OffsetForPosition(result->PositionForOffset(8),
+ include_partial_glyphs));
+ EXPECT_EQ(9u, result->OffsetForPosition(result->PositionForOffset(9),
+ include_partial_glyphs));
+ EXPECT_EQ(10u, result->OffsetForPosition(result->PositionForOffset(10),
+ include_partial_glyphs));
+ EXPECT_EQ(11u, result->OffsetForPosition(result->PositionForOffset(11),
+ include_partial_glyphs));
+ EXPECT_EQ(12u, result->OffsetForPosition(result->PositionForOffset(12),
+ include_partial_glyphs));
+}
+
+TEST_P(IncludePartialGlyphs, OffsetForPositionMatchesPositionForOffsetArabic) {
+ UChar arabic_string[] = {0x628, 0x64A, 0x629};
+ TextDirection direction = TextDirection::kRtl;
+
+ HarfBuzzShaper shaper(arabic_string, 3);
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, direction);
+
+ bool include_partial_glyphs = GetParam();
+ EXPECT_EQ(0u, result->OffsetForPosition(result->PositionForOffset(0),
+ include_partial_glyphs));
+ EXPECT_EQ(1u, result->OffsetForPosition(result->PositionForOffset(1),
+ include_partial_glyphs));
+ EXPECT_EQ(2u, result->OffsetForPosition(result->PositionForOffset(2),
+ include_partial_glyphs));
+ EXPECT_EQ(3u, result->OffsetForPosition(result->PositionForOffset(3),
+ include_partial_glyphs));
+}
+
+TEST_P(IncludePartialGlyphs, OffsetForPositionMatchesPositionForOffsetMixed) {
+ UChar mixed_string[] = {0x628, 0x64A, 0x629, 0xE20, 0x65E5, 0x62};
+ HarfBuzzShaper shaper(mixed_string, 6);
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, TextDirection::kLtr);
+
+ bool include_partial_glyphs = GetParam();
+ EXPECT_EQ(0u, result->OffsetForPosition(result->PositionForOffset(0),
+ include_partial_glyphs));
+ EXPECT_EQ(1u, result->OffsetForPosition(result->PositionForOffset(1),
+ include_partial_glyphs));
+ EXPECT_EQ(2u, result->OffsetForPosition(result->PositionForOffset(2),
+ include_partial_glyphs));
+ EXPECT_EQ(3u, result->OffsetForPosition(result->PositionForOffset(3),
+ include_partial_glyphs));
+ EXPECT_EQ(4u, result->OffsetForPosition(result->PositionForOffset(4),
+ include_partial_glyphs));
+ EXPECT_EQ(5u, result->OffsetForPosition(result->PositionForOffset(5),
+ include_partial_glyphs));
+ EXPECT_EQ(6u, result->OffsetForPosition(result->PositionForOffset(6),
+ include_partial_glyphs));
+}
+
+TEST_F(HarfBuzzShaperTest, PositionForOffsetMissingGlyph) {
+ String string(u"\u0633\u0644\u0627\u0645");
+ HarfBuzzShaper shaper(string.Characters16(), string.length());
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, TextDirection::kRtl);
+ // Because the offset 1 and 2 should form a ligature, SubRange(2, 4) creates a
+ // ShapeResult that does not have its first glyph.
+ result = result->SubRange(2, 4);
+ result->PositionForOffset(0);
+ // Pass if |PositionForOffset| does not crash.
+}
+
+static struct ShapeResultCopyRangeTestData {
+ const char16_t* string;
+ TextDirection direction;
+ unsigned break_point;
+} shape_result_copy_range_test_data[] = {
+ {u"ABC", TextDirection::kLtr, 1},
+ {u"\u0648\u0644\u064A", TextDirection::kRtl, 1},
+ // These strings creates 3 runs. Split it in the middle of 2nd run.
+ {u"\u65E5Hello\u65E5\u65E5", TextDirection::kLtr, 3},
+ {u"\u0648\u0644\u064A AB \u0628\u062A", TextDirection::kRtl, 5}};
+
+std::ostream& operator<<(std::ostream& ostream,
+ const ShapeResultCopyRangeTestData& data) {
+ return ostream << String(data.string) << " @ " << data.break_point << ", "
+ << data.direction;
+}
+
+class ShapeResultCopyRangeTest
+ : public HarfBuzzShaperTest,
+ public testing::WithParamInterface<ShapeResultCopyRangeTestData> {};
+
+INSTANTIATE_TEST_CASE_P(HarfBuzzShaperTest,
+ ShapeResultCopyRangeTest,
+ testing::ValuesIn(shape_result_copy_range_test_data));
+
+// Split a ShapeResult and combine them should match to the original result.
+TEST_P(ShapeResultCopyRangeTest, Split) {
+ const auto& test_data = GetParam();
+ String string(test_data.string);
+ TextDirection direction = test_data.direction;
+
+ HarfBuzzShaper shaper(string.Characters16(), string.length());
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, direction);
+
+ // Split the result.
+ scoped_refptr<ShapeResult> result1 = ShapeResult::Create(&font, 0, direction);
+ result->CopyRange(0, test_data.break_point, result1.get());
+ EXPECT_EQ(test_data.break_point, result1->NumCharacters());
+ EXPECT_EQ(0u, result1->StartIndexForResult());
+ EXPECT_EQ(test_data.break_point, result1->EndIndexForResult());
+
+ scoped_refptr<ShapeResult> result2 = ShapeResult::Create(&font, 0, direction);
+ result->CopyRange(test_data.break_point, string.length(), result2.get());
+ EXPECT_EQ(string.length() - test_data.break_point, result2->NumCharacters());
+ EXPECT_EQ(test_data.break_point, result2->StartIndexForResult());
+ EXPECT_EQ(string.length(), result2->EndIndexForResult());
+
+ // Combine them.
+ scoped_refptr<ShapeResult> composite_result =
+ ShapeResult::Create(&font, 0, direction);
+ result1->CopyRange(0, test_data.break_point, composite_result.get());
+ result2->CopyRange(0, string.length(), composite_result.get());
+ EXPECT_EQ(string.length(), composite_result->NumCharacters());
+
+ // Test character indexes match.
+ Vector<unsigned> expected_character_indexes =
+ TestInfo(result)->CharacterIndexesForTesting();
+ Vector<unsigned> composite_character_indexes =
+ TestInfo(result)->CharacterIndexesForTesting();
+ EXPECT_EQ(expected_character_indexes, composite_character_indexes);
+}
+
+// Shape ranges and combine them shold match to the result of shaping the whole
+// string.
+TEST_P(ShapeResultCopyRangeTest, ShapeRange) {
+ const auto& test_data = GetParam();
+ String string(test_data.string);
+ TextDirection direction = test_data.direction;
+
+ HarfBuzzShaper shaper(string.Characters16(), string.length());
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, direction);
+
+ // Shape each range.
+ scoped_refptr<ShapeResult> result1 =
+ shaper.Shape(&font, direction, 0, test_data.break_point);
+ EXPECT_EQ(test_data.break_point, result1->NumCharacters());
+ scoped_refptr<ShapeResult> result2 =
+ shaper.Shape(&font, direction, test_data.break_point, string.length());
+ EXPECT_EQ(string.length() - test_data.break_point, result2->NumCharacters());
+
+ // Combine them.
+ scoped_refptr<ShapeResult> composite_result =
+ ShapeResult::Create(&font, 0, direction);
+ result1->CopyRange(0, test_data.break_point, composite_result.get());
+ result2->CopyRange(0, string.length(), composite_result.get());
+ EXPECT_EQ(string.length(), composite_result->NumCharacters());
+
+ // Test character indexes match.
+ Vector<unsigned> expected_character_indexes =
+ TestInfo(result)->CharacterIndexesForTesting();
+ Vector<unsigned> composite_character_indexes =
+ TestInfo(result)->CharacterIndexesForTesting();
+ EXPECT_EQ(expected_character_indexes, composite_character_indexes);
+}
+
+TEST_F(HarfBuzzShaperTest, ShapeResultCopyRangeIntoLatin) {
+ String string = To16Bit("Testing ShapeResult::createSubRun", 33);
+ TextDirection direction = TextDirection::kLtr;
+
+ HarfBuzzShaper shaper(string.Characters16(), 33);
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, direction);
+
+ scoped_refptr<ShapeResult> composite_result =
+ ShapeResult::Create(&font, 0, direction);
+ result->CopyRange(0, 10, composite_result.get());
+ result->CopyRange(10, 20, composite_result.get());
+ result->CopyRange(20, 30, composite_result.get());
+ result->CopyRange(30, 33, composite_result.get());
+
+ EXPECT_EQ(result->NumCharacters(), composite_result->NumCharacters());
+ EXPECT_EQ(result->SnappedWidth(), composite_result->SnappedWidth());
+ EXPECT_EQ(result->Bounds(), composite_result->Bounds());
+ EXPECT_EQ(result->SnappedStartPositionForOffset(0),
+ composite_result->SnappedStartPositionForOffset(0));
+ EXPECT_EQ(result->SnappedStartPositionForOffset(15),
+ composite_result->SnappedStartPositionForOffset(15));
+ EXPECT_EQ(result->SnappedStartPositionForOffset(30),
+ composite_result->SnappedStartPositionForOffset(30));
+ EXPECT_EQ(result->SnappedStartPositionForOffset(33),
+ composite_result->SnappedStartPositionForOffset(33));
+}
+
+TEST_F(HarfBuzzShaperTest, ShapeResultCopyRangeIntoArabicThaiHanLatin) {
+ UChar mixed_string[] = {0x628, 0x20, 0x64A, 0x629, 0x20, 0xE20, 0x65E5, 0x62};
+ TextDirection direction = TextDirection::kLtr;
+
+ HarfBuzzShaper shaper(mixed_string, 8);
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, direction);
+
+ // Check width and bounds are not too much different. ".2" is heuristic.
+ EXPECT_NEAR(result->Width(), result->Bounds().Width(), result->Width() * .2);
+
+ scoped_refptr<ShapeResult> composite_result =
+ ShapeResult::Create(&font, 0, direction);
+ result->CopyRange(0, 4, composite_result.get());
+ result->CopyRange(4, 6, composite_result.get());
+ result->CopyRange(6, 8, composite_result.get());
+
+ EXPECT_EQ(result->NumCharacters(), composite_result->NumCharacters());
+ EXPECT_EQ(result->SnappedWidth(), composite_result->SnappedWidth());
+ EXPECT_EQ(result->Bounds(), composite_result->Bounds());
+ EXPECT_EQ(result->SnappedStartPositionForOffset(0),
+ composite_result->SnappedStartPositionForOffset(0));
+ EXPECT_EQ(result->SnappedStartPositionForOffset(1),
+ composite_result->SnappedStartPositionForOffset(1));
+ EXPECT_EQ(result->SnappedStartPositionForOffset(2),
+ composite_result->SnappedStartPositionForOffset(2));
+ EXPECT_EQ(result->SnappedStartPositionForOffset(3),
+ composite_result->SnappedStartPositionForOffset(3));
+ EXPECT_EQ(result->SnappedStartPositionForOffset(4),
+ composite_result->SnappedStartPositionForOffset(4));
+ EXPECT_EQ(result->SnappedStartPositionForOffset(5),
+ composite_result->SnappedStartPositionForOffset(5));
+ EXPECT_EQ(result->SnappedStartPositionForOffset(6),
+ composite_result->SnappedStartPositionForOffset(6));
+ EXPECT_EQ(result->SnappedStartPositionForOffset(7),
+ composite_result->SnappedStartPositionForOffset(7));
+ EXPECT_EQ(result->SnappedStartPositionForOffset(8),
+ composite_result->SnappedStartPositionForOffset(8));
+}
+
+TEST_F(HarfBuzzShaperTest, ShapeResultCopyRangeAcrossRuns) {
+ // Create 3 runs:
+ // [0]: 1 character.
+ // [1]: 5 characters.
+ // [2]: 2 character.
+ String mixed_string(u"\u65E5Hello\u65E5\u65E5");
+ TextDirection direction = TextDirection::kLtr;
+ HarfBuzzShaper shaper(mixed_string.Characters16(), mixed_string.length());
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, direction);
+
+ // Check width and bounds are not too much different. ".1" is heuristic.
+ EXPECT_NEAR(result->Width(), result->Bounds().Width(), result->Width() * .1);
+
+ // CopyRange(5, 7) should copy 1 character from [1] and 1 from [2].
+ scoped_refptr<ShapeResult> target = ShapeResult::Create(&font, 0, direction);
+ result->CopyRange(5, 7, target.get());
+ EXPECT_EQ(2u, target->NumCharacters());
+}
+
+TEST_F(HarfBuzzShaperTest, ShapeResultCopyRangeSegmentGlyphBoundingBox) {
+ String string(u"THello worldL");
+ TextDirection direction = TextDirection::kLtr;
+
+ HarfBuzzShaper shaper(string.Characters16(), string.length());
+ scoped_refptr<ShapeResult> result1 = shaper.Shape(&font, direction, 0, 6);
+ scoped_refptr<ShapeResult> result2 =
+ shaper.Shape(&font, direction, 6, string.length());
+
+ scoped_refptr<ShapeResult> composite_result =
+ ShapeResult::Create(&font, 0, direction);
+ result1->CopyRange(0, 6, composite_result.get());
+ result2->CopyRange(6, string.length(), composite_result.get());
+
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, direction);
+ EXPECT_EQ(result->Bounds(), composite_result->Bounds());
+
+ // Check width and bounds are not too much different. ".1" is heuristic.
+ EXPECT_NEAR(result->Width(), result->Bounds().Width(), result->Width() * .1);
+}
+
+TEST_F(HarfBuzzShaperTest, SubRange) {
+ String string(u"Hello world");
+ TextDirection direction = TextDirection::kRtl;
+ HarfBuzzShaper shaper(string.Characters16(), string.length());
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, direction);
+
+ scoped_refptr<ShapeResult> sub_range = result->SubRange(4, 7);
+ DCHECK_EQ(4u, sub_range->StartIndexForResult());
+ DCHECK_EQ(7u, sub_range->EndIndexForResult());
+ DCHECK_EQ(3u, sub_range->NumCharacters());
+ DCHECK_EQ(result->Direction(), sub_range->Direction());
+}
+
+TEST_F(HarfBuzzShaperTest, SafeToBreakLatinCommonLigatures) {
+ FontDescription::VariantLigatures ligatures;
+ ligatures.common = FontDescription::kEnabledLigaturesState;
+
+ // MEgalopolis Extra has a lot of ligatures which this test relies on.
+ Font testFont = blink::test::CreateTestFont(
+ "MEgalopolis",
+ blink::test::PlatformTestDataPath(
+ "third_party/MEgalopolis/MEgalopolisExtra.woff"),
+ 16, &ligatures);
+
+ String string = To16Bit("ffi ff", 6);
+ HarfBuzzShaper shaper(string.Characters16(), string.length());
+ scoped_refptr<ShapeResult> result = shaper.Shape(&testFont, TextDirection::kLtr);
+
+ EXPECT_EQ(0u, result->NextSafeToBreakOffset(0)); // At start of string.
+ EXPECT_EQ(3u, result->NextSafeToBreakOffset(1)); // At end of "ffi" ligature.
+ EXPECT_EQ(3u, result->NextSafeToBreakOffset(2)); // At end of "ffi" ligature.
+ EXPECT_EQ(3u, result->NextSafeToBreakOffset(3)); // At end of "ffi" ligature.
+ EXPECT_EQ(4u, result->NextSafeToBreakOffset(4)); // After space.
+ EXPECT_EQ(6u, result->NextSafeToBreakOffset(5)); // At end of "ff" ligature.
+ EXPECT_EQ(6u, result->NextSafeToBreakOffset(6)); // At end of "ff" ligature.
+
+ // Verify safe to break information in copied results to ensure that both
+ // copying and multi-run break information works.
+ scoped_refptr<ShapeResult> copied_result =
+ ShapeResult::Create(&testFont, 0, TextDirection::kLtr);
+ result->CopyRange(0, 3, copied_result.get());
+ result->CopyRange(3, string.length(), copied_result.get());
+
+ EXPECT_EQ(0u, copied_result->NextSafeToBreakOffset(0));
+ EXPECT_EQ(3u, copied_result->NextSafeToBreakOffset(1));
+ EXPECT_EQ(3u, copied_result->NextSafeToBreakOffset(2));
+ EXPECT_EQ(3u, copied_result->NextSafeToBreakOffset(3));
+ EXPECT_EQ(4u, copied_result->NextSafeToBreakOffset(4));
+ EXPECT_EQ(6u, copied_result->NextSafeToBreakOffset(5));
+ EXPECT_EQ(6u, copied_result->NextSafeToBreakOffset(6));
+}
+
+TEST_F(HarfBuzzShaperTest, SafeToBreakPreviousLatinCommonLigatures) {
+ FontDescription::VariantLigatures ligatures;
+ ligatures.common = FontDescription::kEnabledLigaturesState;
+
+ // MEgalopolis Extra has a lot of ligatures which this test relies on.
+ Font testFont = blink::test::CreateTestFont(
+ "MEgalopolis",
+ blink::test::PlatformTestDataPath(
+ "third_party/MEgalopolis/MEgalopolisExtra.woff"),
+ 16, &ligatures);
+
+ String string = To16Bit("ffi ff", 6);
+ HarfBuzzShaper shaper(string.Characters16(), string.length());
+ scoped_refptr<ShapeResult> result = shaper.Shape(&testFont, TextDirection::kLtr);
+
+ EXPECT_EQ(6u, result->PreviousSafeToBreakOffset(6)); // At end of "ff" liga.
+ EXPECT_EQ(4u, result->PreviousSafeToBreakOffset(5)); // At end of "ff" liga.
+ EXPECT_EQ(4u, result->PreviousSafeToBreakOffset(4)); // After space.
+ EXPECT_EQ(3u, result->PreviousSafeToBreakOffset(3)); // At end of "ffi" liga.
+ EXPECT_EQ(0u, result->PreviousSafeToBreakOffset(2)); // At start of string.
+ EXPECT_EQ(0u, result->PreviousSafeToBreakOffset(1)); // At start of string.
+ EXPECT_EQ(0u, result->PreviousSafeToBreakOffset(0)); // At start of string.
+
+ // Verify safe to break information in copied results to ensure that both
+ // copying and multi-run break information works.
+ scoped_refptr<ShapeResult> copied_result =
+ ShapeResult::Create(&testFont, 0, TextDirection::kLtr);
+ result->CopyRange(0, 3, copied_result.get());
+ result->CopyRange(3, string.length(), copied_result.get());
+
+ EXPECT_EQ(6u, copied_result->PreviousSafeToBreakOffset(6));
+ EXPECT_EQ(4u, copied_result->PreviousSafeToBreakOffset(5));
+ EXPECT_EQ(4u, copied_result->PreviousSafeToBreakOffset(4));
+ EXPECT_EQ(3u, copied_result->PreviousSafeToBreakOffset(3));
+ EXPECT_EQ(0u, copied_result->PreviousSafeToBreakOffset(2));
+ EXPECT_EQ(0u, copied_result->PreviousSafeToBreakOffset(1));
+ EXPECT_EQ(0u, copied_result->PreviousSafeToBreakOffset(0));
+}
+
+TEST_F(HarfBuzzShaperTest, SafeToBreakLatinDiscretionaryLigatures) {
+ FontDescription::VariantLigatures ligatures;
+ ligatures.common = FontDescription::kEnabledLigaturesState;
+ ligatures.discretionary = FontDescription::kEnabledLigaturesState;
+
+ // MEgalopolis Extra has a lot of ligatures which this test relies on.
+ Font testFont = blink::test::CreateTestFont(
+ "MEgalopolis",
+ blink::test::PlatformTestDataPath(
+ "third_party/MEgalopolis/MEgalopolisExtra.woff"),
+ 16, &ligatures);
+
+ // RA and CA form ligatures, most glyph pairs have kerning.
+ String string(u"ABRACADABRA");
+ HarfBuzzShaper shaper(string.Characters16(), string.length());
+ scoped_refptr<ShapeResult> result = shaper.Shape(&testFont, TextDirection::kLtr);
+ EXPECT_EQ(6u, result->NextSafeToBreakOffset(1)); // After CA ligature.
+ EXPECT_EQ(6u, result->NextSafeToBreakOffset(6)); // After CA ligature.
+ EXPECT_EQ(11u, result->NextSafeToBreakOffset(7)); // At end of string.
+ EXPECT_EQ(11u, result->NextSafeToBreakOffset(9)); // At end of string.
+ EXPECT_EQ(11u, result->NextSafeToBreakOffset(10)); // At end of string.
+
+ // Add zero-width spaces at the safe to break offsets.
+ String refString(u"ABRACA\u200BDAB\u200BRA");
+ HarfBuzzShaper refShaper(refString.Characters16(), refString.length());
+ scoped_refptr<ShapeResult> referenceResult =
+ refShaper.Shape(&testFont, TextDirection::kLtr);
+
+ // Results should be identical if it truly is safe to break at the designated
+ // safe-to-break offsets
+ EXPECT_EQ(result->SnappedWidth(), referenceResult->SnappedWidth());
+ EXPECT_EQ(result->Bounds(), referenceResult->Bounds());
+ EXPECT_EQ(result->SnappedStartPositionForOffset(0),
+ referenceResult->SnappedStartPositionForOffset(0));
+ EXPECT_EQ(result->SnappedStartPositionForOffset(1),
+ referenceResult->SnappedStartPositionForOffset(1));
+ EXPECT_EQ(result->SnappedStartPositionForOffset(2),
+ referenceResult->SnappedStartPositionForOffset(2));
+ EXPECT_EQ(result->SnappedStartPositionForOffset(3),
+ referenceResult->SnappedStartPositionForOffset(3));
+ EXPECT_EQ(result->SnappedStartPositionForOffset(4),
+ referenceResult->SnappedStartPositionForOffset(4));
+ EXPECT_EQ(result->SnappedStartPositionForOffset(5),
+ referenceResult->SnappedStartPositionForOffset(5));
+
+ // First zero-width space is at position 6 so the the matching character in
+ // the reference results is 7.
+ EXPECT_EQ(result->SnappedStartPositionForOffset(6),
+ referenceResult->SnappedStartPositionForOffset(7));
+ EXPECT_EQ(result->SnappedStartPositionForOffset(7),
+ referenceResult->SnappedStartPositionForOffset(8));
+ EXPECT_EQ(result->SnappedStartPositionForOffset(8),
+ referenceResult->SnappedStartPositionForOffset(9));
+
+ // Second zero-width space is at position 9 so the the matching character in
+ // the reference results is 11.
+ EXPECT_EQ(result->SnappedStartPositionForOffset(9),
+ referenceResult->SnappedStartPositionForOffset(11));
+ EXPECT_EQ(result->SnappedStartPositionForOffset(10),
+ referenceResult->SnappedStartPositionForOffset(12));
+}
+
+// TODO(layout-dev): This test fails on Mac due to AAT shaping.
+TEST_F(HarfBuzzShaperTest, DISABLED_SafeToBreakArabicCommonLigatures) {
+ FontDescription::VariantLigatures ligatures;
+ ligatures.common = FontDescription::kEnabledLigaturesState;
+
+ // كسر الاختبار
+ String string(
+ u"\u0643\u0633\u0631\u0020\u0627\u0644\u0627\u062E\u062A\u0628\u0627"
+ u"\u0631");
+ HarfBuzzShaper shaper(string.Characters16(), string.length());
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, TextDirection::kRtl);
+
+ // Safe to break at 0, 3, 4, 5, 7, and 11.
+ EXPECT_EQ(0u, result->NextSafeToBreakOffset(0));
+ EXPECT_EQ(3u, result->NextSafeToBreakOffset(1));
+ EXPECT_EQ(3u, result->NextSafeToBreakOffset(2));
+ EXPECT_EQ(3u, result->NextSafeToBreakOffset(3));
+ EXPECT_EQ(4u, result->NextSafeToBreakOffset(4));
+ EXPECT_EQ(5u, result->NextSafeToBreakOffset(5));
+ EXPECT_EQ(7u, result->NextSafeToBreakOffset(6));
+ EXPECT_EQ(7u, result->NextSafeToBreakOffset(7));
+ EXPECT_EQ(11u, result->NextSafeToBreakOffset(8));
+ EXPECT_EQ(11u, result->NextSafeToBreakOffset(9));
+ EXPECT_EQ(11u, result->NextSafeToBreakOffset(10));
+ EXPECT_EQ(11u, result->NextSafeToBreakOffset(11));
+ EXPECT_EQ(12u, result->NextSafeToBreakOffset(12));
+}
+
+// TODO(layout-dev): Expand RTL test coverage and add tests for mixed
+// directionality strings.
+
+// Test when some characters are missing in |runs_|.
+// RTL on Mac may not have runs for all characters. crbug.com/774034
+TEST_P(ShapeParameterTest, SafeToBreakMissingRun) {
+ TextDirection direction = GetParam();
+ scoped_refptr<ShapeResult> result = ShapeResult::Create(&font, 7, direction);
+ result->InsertRunForTesting(2, 1, direction, {0});
+ result->InsertRunForTesting(3, 3, direction, {0, 1});
+ // The character index 7 is missing.
+ result->InsertRunForTesting(8, 2, direction, {0});
+
+ EXPECT_EQ(2u, result->NextSafeToBreakOffset(2));
+ EXPECT_EQ(3u, result->NextSafeToBreakOffset(3));
+ EXPECT_EQ(4u, result->NextSafeToBreakOffset(4));
+ EXPECT_EQ(6u, result->NextSafeToBreakOffset(5));
+ EXPECT_EQ(6u, result->NextSafeToBreakOffset(6));
+ EXPECT_EQ(8u, result->NextSafeToBreakOffset(7));
+ EXPECT_EQ(8u, result->NextSafeToBreakOffset(8));
+ EXPECT_EQ(10u, result->NextSafeToBreakOffset(9));
+
+ EXPECT_EQ(2u, result->PreviousSafeToBreakOffset(2));
+ EXPECT_EQ(3u, result->PreviousSafeToBreakOffset(3));
+ EXPECT_EQ(4u, result->PreviousSafeToBreakOffset(4));
+ EXPECT_EQ(4u, result->PreviousSafeToBreakOffset(5));
+ EXPECT_EQ(6u, result->PreviousSafeToBreakOffset(6));
+ EXPECT_EQ(6u, result->PreviousSafeToBreakOffset(7));
+ EXPECT_EQ(8u, result->PreviousSafeToBreakOffset(8));
+ EXPECT_EQ(8u, result->PreviousSafeToBreakOffset(9));
+}
+
+// Call this to ensure your test string has some kerning going on.
+static bool KerningIsHappening(const FontDescription& font_description,
+ TextDirection direction,
+ const String& str) {
+ FontDescription no_kern = font_description;
+ no_kern.SetKerning(FontDescription::kNoneKerning);
+
+ FontDescription kern = font_description;
+ kern.SetKerning(FontDescription::kAutoKerning);
+
+ Font font_no_kern(no_kern);
+ font_no_kern.Update(nullptr);
+
+ Font font_kern(kern);
+ font_kern.Update(nullptr);
+
+ HarfBuzzShaper shaper(str.Characters16(), str.length());
+
+ scoped_refptr<ShapeResult> result_no_kern =
+ shaper.Shape(&font_no_kern, direction);
+ scoped_refptr<ShapeResult> result_kern = shaper.Shape(&font_kern, direction);
+
+ for (unsigned i = 0; i < str.length(); i++) {
+ if (result_no_kern->PositionForOffset(i) !=
+ result_kern->PositionForOffset(i))
+ return true;
+ }
+ return false;
+}
+
+TEST_F(HarfBuzzShaperTest, KerningIsHappeningWorks) {
+ EXPECT_TRUE(
+ KerningIsHappening(font_description, TextDirection::kLtr, u"AVOID"));
+ EXPECT_FALSE(
+ KerningIsHappening(font_description, TextDirection::kLtr, u"NOID"));
+
+ // We won't kern vertically with the default font.
+ font_description.SetOrientation(FontOrientation::kVerticalUpright);
+
+ EXPECT_FALSE(
+ KerningIsHappening(font_description, TextDirection::kLtr, u"AVOID"));
+ EXPECT_FALSE(
+ KerningIsHappening(font_description, TextDirection::kLtr, u"NOID"));
+}
+
+TEST_F(HarfBuzzShaperTest,
+ ShapeHorizontalWithoutSubpixelPositionWithoutKerningIsRounded) {
+ ScopedSubpixelOverride subpixel_override(false);
+
+ String string(u"NOID");
+ TextDirection direction = TextDirection::kLtr;
+ ASSERT_FALSE(KerningIsHappening(font_description, direction, string));
+
+ HarfBuzzShaper shaper(string.Characters16(), string.length());
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, direction);
+
+ for (unsigned i = 0; i < string.length(); i++) {
+ float position = result->PositionForOffset(i);
+ EXPECT_EQ(round(position), position)
+ << "Position not rounded at offset " << i;
+ }
+}
+
+TEST_F(HarfBuzzShaperTest,
+ ShapeHorizontalWithSubpixelPositionWithoutKerningIsNotRounded) {
+ ScopedSubpixelOverride subpixel_override(true);
+
+ String string(u"NOID");
+ TextDirection direction = TextDirection::kLtr;
+ ASSERT_FALSE(KerningIsHappening(font_description, direction, string));
+
+ HarfBuzzShaper shaper(string.Characters16(), string.length());
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, direction);
+
+ for (unsigned i = 0; i < string.length(); i++) {
+ float position = result->PositionForOffset(i);
+ if (round(position) != position)
+ return;
+ }
+
+ EXPECT_TRUE(false) << "No unrounded positions found";
+}
+
+TEST_F(HarfBuzzShaperTest,
+ ShapeHorizontalWithoutSubpixelPositionWithKerningIsRounded) {
+ ScopedSubpixelOverride subpixel_override(false);
+
+ String string(u"AVOID");
+ TextDirection direction = TextDirection::kLtr;
+ ASSERT_TRUE(KerningIsHappening(font_description, direction, string));
+
+ HarfBuzzShaper shaper(string.Characters16(), string.length());
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, direction);
+
+ for (unsigned i = 0; i < string.length(); i++) {
+ float position = result->PositionForOffset(i);
+ EXPECT_EQ(round(position), position)
+ << "Position not rounded at offset " << i;
+ }
+}
+
+TEST_F(HarfBuzzShaperTest,
+ ShapeHorizontalWithSubpixelPositionWithKerningIsNotRounded) {
+ ScopedSubpixelOverride subpixel_override(true);
+
+ String string(u"AVOID");
+ TextDirection direction = TextDirection::kLtr;
+ ASSERT_TRUE(KerningIsHappening(font_description, direction, string));
+
+ HarfBuzzShaper shaper(string.Characters16(), string.length());
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, direction);
+
+ for (unsigned i = 0; i < string.length(); i++) {
+ float position = result->PositionForOffset(i);
+ if (round(position) != position)
+ return;
+ }
+
+ EXPECT_TRUE(false) << "No unrounded positions found";
+}
+
+TEST_F(HarfBuzzShaperTest, ShapeVerticalWithoutSubpixelPositionIsRounded) {
+ ScopedSubpixelOverride subpixel_override(false);
+
+ font_description.SetOrientation(FontOrientation::kVerticalUpright);
+ font = Font(font_description);
+ font.Update(nullptr);
+
+ String string(u"\u65E5\u65E5\u65E5");
+ TextDirection direction = TextDirection::kLtr;
+
+ HarfBuzzShaper shaper(string.Characters16(), string.length());
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, direction);
+
+ for (unsigned i = 0; i < string.length(); i++) {
+ float position = result->PositionForOffset(i);
+ EXPECT_EQ(round(position), position)
+ << "Position not rounded at offset " << i;
+ }
+}
+
+TEST_F(HarfBuzzShaperTest, ShapeVerticalWithSubpixelPositionIsRounded) {
+ ScopedSubpixelOverride subpixel_override(true);
+
+ font_description.SetOrientation(FontOrientation::kVerticalUpright);
+ font = Font(font_description);
+ font.Update(nullptr);
+
+ String string(u"\u65E5\u65E5\u65E5");
+ TextDirection direction = TextDirection::kLtr;
+
+ HarfBuzzShaper shaper(string.Characters16(), string.length());
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, direction);
+
+ // Vertical text is never subpixel positioned.
+ for (unsigned i = 0; i < string.length(); i++) {
+ float position = result->PositionForOffset(i);
+ EXPECT_EQ(round(position), position)
+ << "Position not rounded at offset " << i;
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/run_segmenter.cc b/chromium/third_party/blink/renderer/platform/fonts/shaping/run_segmenter.cc
new file mode 100644
index 00000000000..8a81e927d74
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/run_segmenter.cc
@@ -0,0 +1,108 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/shaping/run_segmenter.h"
+
+#include <memory>
+
+#include "third_party/blink/renderer/platform/fonts/script_run_iterator.h"
+#include "third_party/blink/renderer/platform/fonts/small_caps_iterator.h"
+#include "third_party/blink/renderer/platform/fonts/symbols_iterator.h"
+#include "third_party/blink/renderer/platform/fonts/utf16_text_iterator.h"
+#include "third_party/blink/renderer/platform/text/character.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+namespace blink {
+
+RunSegmenter::RunSegmenter(const UChar* buffer,
+ unsigned buffer_size,
+ FontOrientation run_orientation)
+ : buffer_size_(buffer_size),
+ candidate_range_(NullRange()),
+ script_run_iterator_(
+ std::make_unique<ScriptRunIterator>(buffer, buffer_size)),
+ orientation_iterator_(
+ run_orientation == FontOrientation::kVerticalMixed
+ ? std::make_unique<OrientationIterator>(buffer,
+ buffer_size,
+ run_orientation)
+ : nullptr),
+ symbols_iterator_(std::make_unique<SymbolsIterator>(buffer, buffer_size)),
+ last_split_(0),
+ script_run_iterator_position_(0),
+ orientation_iterator_position_(
+ run_orientation == FontOrientation::kVerticalMixed ? 0
+ : buffer_size_),
+ symbols_iterator_position_(0),
+ at_end_(false) {}
+
+void RunSegmenter::ConsumeScriptIteratorPastLastSplit() {
+ DCHECK(script_run_iterator_);
+ if (script_run_iterator_position_ <= last_split_ &&
+ script_run_iterator_position_ < buffer_size_) {
+ while (script_run_iterator_->Consume(script_run_iterator_position_,
+ candidate_range_.script)) {
+ if (script_run_iterator_position_ > last_split_)
+ return;
+ }
+ }
+}
+
+void RunSegmenter::ConsumeOrientationIteratorPastLastSplit() {
+ if (orientation_iterator_ && orientation_iterator_position_ <= last_split_ &&
+ orientation_iterator_position_ < buffer_size_) {
+ while (
+ orientation_iterator_->Consume(&orientation_iterator_position_,
+ &candidate_range_.render_orientation)) {
+ if (orientation_iterator_position_ > last_split_)
+ return;
+ }
+ }
+}
+
+void RunSegmenter::ConsumeSymbolsIteratorPastLastSplit() {
+ DCHECK(symbols_iterator_);
+ if (symbols_iterator_position_ <= last_split_ &&
+ symbols_iterator_position_ < buffer_size_) {
+ while (
+ symbols_iterator_->Consume(&symbols_iterator_position_,
+ &candidate_range_.font_fallback_priority)) {
+ if (symbols_iterator_position_ > last_split_)
+ return;
+ }
+ }
+}
+
+bool RunSegmenter::Consume(RunSegmenterRange* next_range) {
+ if (at_end_ || !buffer_size_)
+ return false;
+
+ ConsumeScriptIteratorPastLastSplit();
+ ConsumeOrientationIteratorPastLastSplit();
+ ConsumeSymbolsIteratorPastLastSplit();
+
+ if (script_run_iterator_position_ <= orientation_iterator_position_ &&
+ script_run_iterator_position_ <= symbols_iterator_position_) {
+ last_split_ = script_run_iterator_position_;
+ }
+
+ if (orientation_iterator_position_ <= script_run_iterator_position_ &&
+ orientation_iterator_position_ <= symbols_iterator_position_) {
+ last_split_ = orientation_iterator_position_;
+ }
+
+ if (symbols_iterator_position_ <= script_run_iterator_position_ &&
+ symbols_iterator_position_ <= orientation_iterator_position_) {
+ last_split_ = symbols_iterator_position_;
+ }
+
+ candidate_range_.start = candidate_range_.end;
+ candidate_range_.end = last_split_;
+ *next_range = candidate_range_;
+
+ at_end_ = last_split_ == buffer_size_;
+ return true;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/run_segmenter.h b/chromium/third_party/blink/renderer/platform/fonts/shaping/run_segmenter.h
new file mode 100644
index 00000000000..e5d8c6059df
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/run_segmenter.h
@@ -0,0 +1,67 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_RUN_SEGMENTER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_RUN_SEGMENTER_H_
+
+#include <unicode/uscript.h>
+#include <memory>
+#include "third_party/blink/renderer/platform/fonts/font_orientation.h"
+#include "third_party/blink/renderer/platform/fonts/orientation_iterator.h"
+#include "third_party/blink/renderer/platform/fonts/script_run_iterator.h"
+#include "third_party/blink/renderer/platform/fonts/small_caps_iterator.h"
+#include "third_party/blink/renderer/platform/fonts/symbols_iterator.h"
+#include "third_party/blink/renderer/platform/fonts/utf16_text_iterator.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+// A tool for segmenting runs prior to shaping, combining ScriptIterator,
+// OrientationIterator and SmallCapsIterator, depending on orientaton and
+// font-variant of the text run.
+class PLATFORM_EXPORT RunSegmenter {
+ STACK_ALLOCATED();
+ WTF_MAKE_NONCOPYABLE(RunSegmenter);
+
+ public:
+ // Indices into the UTF-16 buffer that is passed in
+ struct RunSegmenterRange {
+ DISALLOW_NEW();
+ unsigned start;
+ unsigned end;
+ UScriptCode script;
+ OrientationIterator::RenderOrientation render_orientation;
+ FontFallbackPriority font_fallback_priority;
+ };
+
+ RunSegmenter(const UChar* buffer, unsigned buffer_size, FontOrientation);
+
+ bool Consume(RunSegmenterRange*);
+
+ static RunSegmenterRange NullRange() {
+ return {0, 0, USCRIPT_INVALID_CODE, OrientationIterator::kOrientationKeep,
+ FontFallbackPriority::kText};
+ }
+
+ private:
+ void ConsumeOrientationIteratorPastLastSplit();
+ void ConsumeScriptIteratorPastLastSplit();
+ void ConsumeSymbolsIteratorPastLastSplit();
+
+ unsigned buffer_size_;
+ RunSegmenterRange candidate_range_;
+ std::unique_ptr<ScriptRunIterator> script_run_iterator_;
+ std::unique_ptr<OrientationIterator> orientation_iterator_;
+ std::unique_ptr<SymbolsIterator> symbols_iterator_;
+ unsigned last_split_;
+ unsigned script_run_iterator_position_;
+ unsigned orientation_iterator_position_;
+ unsigned symbols_iterator_position_;
+ bool at_end_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/run_segmenter_test.cc b/chromium/third_party/blink/renderer/platform/fonts/shaping/run_segmenter_test.cc
new file mode 100644
index 00000000000..231a4e9f895
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/run_segmenter_test.cc
@@ -0,0 +1,251 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/shaping/run_segmenter.h"
+
+#include <string>
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/fonts/orientation_iterator.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+struct SegmenterTestRun {
+ std::string text;
+ UScriptCode script;
+ OrientationIterator::RenderOrientation render_orientation;
+ FontFallbackPriority font_fallback_priority;
+};
+
+struct SegmenterExpectedRun {
+ unsigned start;
+ unsigned limit;
+ UScriptCode script;
+ OrientationIterator::RenderOrientation render_orientation;
+ FontFallbackPriority font_fallback_priority;
+
+ SegmenterExpectedRun(
+ unsigned the_start,
+ unsigned the_limit,
+ UScriptCode the_script,
+ OrientationIterator::RenderOrientation the_render_orientation,
+ FontFallbackPriority the_font_fallback_priority)
+ : start(the_start),
+ limit(the_limit),
+ script(the_script),
+ render_orientation(the_render_orientation),
+ font_fallback_priority(the_font_fallback_priority) {}
+};
+
+class RunSegmenterTest : public testing::Test {
+ protected:
+ void CheckRuns(const Vector<SegmenterTestRun>& runs,
+ FontOrientation orientation) {
+ String text(g_empty_string16_bit);
+ Vector<SegmenterExpectedRun> expect;
+ for (auto& run : runs) {
+ unsigned length_before = text.length();
+ text.append(String::FromUTF8(run.text.c_str()));
+ expect.push_back(SegmenterExpectedRun(length_before, text.length(),
+ run.script, run.render_orientation,
+ run.font_fallback_priority));
+ }
+ RunSegmenter run_segmenter(text.Characters16(), text.length(), orientation);
+ VerifyRuns(&run_segmenter, expect);
+ }
+
+ void VerifyRuns(RunSegmenter* run_segmenter,
+ const Vector<SegmenterExpectedRun>& expect) {
+ RunSegmenter::RunSegmenterRange segmenter_range;
+ unsigned long run_count = 0;
+ while (run_segmenter->Consume(&segmenter_range)) {
+ ASSERT_LT(run_count, expect.size());
+ ASSERT_EQ(expect[run_count].start, segmenter_range.start);
+ ASSERT_EQ(expect[run_count].limit, segmenter_range.end);
+ ASSERT_EQ(expect[run_count].script, segmenter_range.script);
+ ASSERT_EQ(expect[run_count].render_orientation,
+ segmenter_range.render_orientation);
+ ASSERT_EQ(expect[run_count].font_fallback_priority,
+ segmenter_range.font_fallback_priority);
+ ++run_count;
+ }
+ ASSERT_EQ(expect.size(), run_count);
+ }
+};
+
+// Some of our compilers cannot initialize a vector from an array yet.
+#define DECLARE_SEGMENTER_RUNSVECTOR(...) \
+ static const SegmenterTestRun kRunsArray[] = __VA_ARGS__; \
+ Vector<SegmenterTestRun> runs; \
+ runs.Append(kRunsArray, sizeof(kRunsArray) / sizeof(*kRunsArray));
+
+#define CHECK_RUNS_MIXED(...) \
+ DECLARE_SEGMENTER_RUNSVECTOR(__VA_ARGS__); \
+ CheckRuns(runs, FontOrientation::kVerticalMixed);
+
+#define CHECK_RUNS_HORIZONTAL(...) \
+ DECLARE_SEGMENTER_RUNSVECTOR(__VA_ARGS__); \
+ CheckRuns(runs, FontOrientation::kHorizontal);
+
+TEST_F(RunSegmenterTest, Empty) {
+ String empty(g_empty_string16_bit);
+ RunSegmenter::RunSegmenterRange segmenter_range = {
+ 0, 0, USCRIPT_INVALID_CODE, OrientationIterator::kOrientationKeep};
+ RunSegmenter run_segmenter(empty.Characters16(), empty.length(),
+ FontOrientation::kVerticalMixed);
+ DCHECK(!run_segmenter.Consume(&segmenter_range));
+ ASSERT_EQ(segmenter_range.start, 0u);
+ ASSERT_EQ(segmenter_range.end, 0u);
+ ASSERT_EQ(segmenter_range.script, USCRIPT_INVALID_CODE);
+ ASSERT_EQ(segmenter_range.render_orientation,
+ OrientationIterator::kOrientationKeep);
+ ASSERT_EQ(segmenter_range.font_fallback_priority,
+ FontFallbackPriority::kText);
+}
+
+TEST_F(RunSegmenterTest, LatinPunctuationSideways) {
+ CHECK_RUNS_MIXED({{"Abc.;?Xyz", USCRIPT_LATIN,
+ OrientationIterator::kOrientationRotateSideways,
+ FontFallbackPriority::kText}});
+}
+
+TEST_F(RunSegmenterTest, OneSpace) {
+ CHECK_RUNS_MIXED(
+ {{" ", USCRIPT_COMMON, OrientationIterator::kOrientationRotateSideways,
+ FontFallbackPriority::kText}});
+}
+
+TEST_F(RunSegmenterTest, ArabicHangul) {
+ CHECK_RUNS_MIXED(
+ {{"نص", USCRIPT_ARABIC, OrientationIterator::kOrientationRotateSideways,
+ FontFallbackPriority::kText},
+ {"키스의", USCRIPT_HANGUL, OrientationIterator::kOrientationKeep,
+ FontFallbackPriority::kText}});
+}
+
+TEST_F(RunSegmenterTest, JapaneseHindiEmojiMix) {
+ CHECK_RUNS_MIXED(
+ {{"百家姓", USCRIPT_HAN, OrientationIterator::kOrientationKeep,
+ FontFallbackPriority::kText},
+ {"ऋषियों", USCRIPT_DEVANAGARI,
+ OrientationIterator::kOrientationRotateSideways,
+ FontFallbackPriority::kText},
+ {"🌱🌲🌳🌴", USCRIPT_DEVANAGARI, OrientationIterator::kOrientationKeep,
+ FontFallbackPriority::kEmojiEmoji},
+ {"百家姓", USCRIPT_HAN, OrientationIterator::kOrientationKeep,
+ FontFallbackPriority::kText},
+ {"🌱🌲", USCRIPT_HAN, OrientationIterator::kOrientationKeep,
+ FontFallbackPriority::kEmojiEmoji}});
+}
+
+TEST_F(RunSegmenterTest, CombiningCirlce) {
+ CHECK_RUNS_HORIZONTAL(
+ {{"◌́◌̀◌̈◌̂◌̄◌̊", USCRIPT_COMMON, OrientationIterator::kOrientationKeep,
+ FontFallbackPriority::kText}});
+}
+
+TEST_F(RunSegmenterTest, HangulSpace) {
+ CHECK_RUNS_MIXED(
+ {{"키스의", USCRIPT_HANGUL, OrientationIterator::kOrientationKeep,
+ FontFallbackPriority::kText},
+ {" ", USCRIPT_HANGUL, OrientationIterator::kOrientationRotateSideways,
+ FontFallbackPriority::kText},
+ {"고유조건은", USCRIPT_HANGUL, OrientationIterator::kOrientationKeep,
+ FontFallbackPriority::kText}});
+}
+
+TEST_F(RunSegmenterTest, TechnicalCommonUpright) {
+ CHECK_RUNS_MIXED(
+ {{"⌀⌁⌂", USCRIPT_COMMON, OrientationIterator::kOrientationKeep,
+ FontFallbackPriority::kText}});
+}
+
+TEST_F(RunSegmenterTest, PunctuationCommonSideways) {
+ CHECK_RUNS_MIXED(
+ {{".…¡", USCRIPT_COMMON, OrientationIterator::kOrientationRotateSideways,
+ FontFallbackPriority::kText}});
+}
+
+TEST_F(RunSegmenterTest, JapanesePunctuationMixedInside) {
+ CHECK_RUNS_MIXED(
+ {{"いろはに", USCRIPT_HIRAGANA, OrientationIterator::kOrientationKeep,
+ FontFallbackPriority::kText},
+ {".…¡", USCRIPT_HIRAGANA,
+ OrientationIterator::kOrientationRotateSideways,
+ FontFallbackPriority::kText},
+ {"ほへと", USCRIPT_HIRAGANA, OrientationIterator::kOrientationKeep,
+ FontFallbackPriority::kText}});
+}
+
+TEST_F(RunSegmenterTest, JapanesePunctuationMixedInsideHorizontal) {
+ CHECK_RUNS_HORIZONTAL(
+ {{"いろはに.…¡ほへと", USCRIPT_HIRAGANA,
+ OrientationIterator::kOrientationKeep, FontFallbackPriority::kText}});
+}
+
+TEST_F(RunSegmenterTest, PunctuationDevanagariCombining) {
+ CHECK_RUNS_HORIZONTAL(
+ {{"क+े", USCRIPT_DEVANAGARI, OrientationIterator::kOrientationKeep,
+ FontFallbackPriority::kText}});
+}
+
+TEST_F(RunSegmenterTest, EmojiZWJSequences) {
+ CHECK_RUNS_HORIZONTAL(
+ {{"👩‍👩‍👧‍👦👩‍❤️‍💋‍👨", USCRIPT_LATIN,
+ OrientationIterator::kOrientationKeep,
+ FontFallbackPriority::kEmojiEmoji},
+ {"abcd", USCRIPT_LATIN, OrientationIterator::kOrientationKeep,
+ FontFallbackPriority::kText},
+ {"👩‍👩‍", USCRIPT_LATIN, OrientationIterator::kOrientationKeep,
+ FontFallbackPriority::kEmojiEmoji},
+ {"efg", USCRIPT_LATIN, OrientationIterator::kOrientationKeep,
+ FontFallbackPriority::kText}});
+}
+
+TEST_F(RunSegmenterTest, JapaneseLetterlikeEnd) {
+ CHECK_RUNS_MIXED(
+ {{"いろは", USCRIPT_HIRAGANA, OrientationIterator::kOrientationKeep,
+ FontFallbackPriority::kText},
+ {"ℐℒℐℒℐℒℐℒℐℒℐℒℐℒ", USCRIPT_HIRAGANA,
+ OrientationIterator::kOrientationRotateSideways,
+ FontFallbackPriority::kText}});
+}
+
+TEST_F(RunSegmenterTest, JapaneseCase) {
+ CHECK_RUNS_MIXED(
+ {{"いろは", USCRIPT_HIRAGANA, OrientationIterator::kOrientationKeep,
+ FontFallbackPriority::kText},
+ {"aaAA", USCRIPT_LATIN, OrientationIterator::kOrientationRotateSideways,
+ FontFallbackPriority::kText},
+ {"いろは", USCRIPT_HIRAGANA, OrientationIterator::kOrientationKeep,
+ FontFallbackPriority::kText}});
+}
+
+TEST_F(RunSegmenterTest, DingbatsMiscSymbolsModifier) {
+ CHECK_RUNS_HORIZONTAL({{"⛹🏻✍🏻✊🏼", USCRIPT_COMMON,
+ OrientationIterator::kOrientationKeep,
+ FontFallbackPriority::kEmojiEmoji}});
+}
+
+TEST_F(RunSegmenterTest, ArmenianCyrillicCase) {
+ CHECK_RUNS_HORIZONTAL(
+ {{"աբգ", USCRIPT_ARMENIAN, OrientationIterator::kOrientationKeep,
+ FontFallbackPriority::kText},
+ {"αβγ", USCRIPT_GREEK, OrientationIterator::kOrientationKeep,
+ FontFallbackPriority::kText},
+ {"ԱԲԳ", USCRIPT_ARMENIAN, OrientationIterator::kOrientationKeep,
+ FontFallbackPriority::kText}});
+}
+
+TEST_F(RunSegmenterTest, EmojiSubdivisionFlags) {
+ CHECK_RUNS_HORIZONTAL(
+ {{"🏴󠁧󠁢󠁷󠁬󠁳󠁿🏴󠁧󠁢󠁳󠁣󠁴󠁿🏴󠁧󠁢"
+ "󠁥󠁮󠁧󠁿",
+ USCRIPT_COMMON, OrientationIterator::kOrientationKeep,
+ FontFallbackPriority::kEmojiEmoji}});
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_cache.cc b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_cache.cc
new file mode 100644
index 00000000000..3fbc1ef3978
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_cache.cc
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2012, 2017 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 "third_party/blink/renderer/platform/fonts/shaping/shape_cache.h"
+#include "third_party/blink/renderer/platform/wtf/string_hasher.h"
+
+namespace blink {
+
+void ShapeCache::SmallStringKey::HashString() {
+ // TODO(cavalcantii): replace this for a better hash function,
+ // see crbug.com/735674.
+ hash_ = StringHasher::ComputeHash(characters_, length_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_cache.h b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_cache.h
new file mode 100644
index 00000000000..0153c4c89a2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_cache.h
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2015 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_SHAPE_CACHE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_SHAPE_CACHE_H_
+
+#include "base/memory/weak_ptr.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_result.h"
+#include "third_party/blink/renderer/platform/text/text_run.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/hash_functions.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/hash_table_deleted_value_type.h"
+
+namespace blink {
+
+struct ShapeCacheEntry {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ ShapeCacheEntry() { shape_result_ = nullptr; }
+ scoped_refptr<const ShapeResult> shape_result_;
+};
+
+class ShapeCache {
+ USING_FAST_MALLOC(ShapeCache);
+ WTF_MAKE_NONCOPYABLE(ShapeCache);
+ // Used to optimize small strings as hash table keys. Avoids malloc'ing an
+ // out-of-line StringImpl.
+ class SmallStringKey {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ void HashString();
+
+ public:
+ static unsigned Capacity() { return kCapacity; }
+
+ SmallStringKey()
+ : length_(kEmptyValueLength),
+ direction_(static_cast<unsigned>(TextDirection::kLtr)) {}
+
+ SmallStringKey(WTF::HashTableDeletedValueType)
+ : length_(kDeletedValueLength),
+ direction_(static_cast<unsigned>(TextDirection::kLtr)) {}
+
+ SmallStringKey(const LChar* characters,
+ unsigned short length,
+ TextDirection direction)
+ : length_(length), direction_(static_cast<unsigned>(direction)) {
+ DCHECK(length <= kCapacity);
+ // Up-convert from LChar to UChar.
+ for (unsigned short i = 0; i < length; ++i) {
+ characters_[i] = characters[i];
+ }
+
+ HashString();
+ }
+
+ SmallStringKey(const UChar* characters,
+ unsigned short length,
+ TextDirection direction)
+ : length_(length), direction_(static_cast<unsigned>(direction)) {
+ DCHECK(length <= kCapacity);
+ memcpy(characters_, characters, length * sizeof(UChar));
+ HashString();
+ }
+
+ const UChar* Characters() const { return characters_; }
+ unsigned short length() const { return length_; }
+ TextDirection Direction() const {
+ return static_cast<TextDirection>(direction_);
+ }
+ unsigned GetHash() const { return hash_; }
+
+ bool IsHashTableDeletedValue() const {
+ return length_ == kDeletedValueLength;
+ }
+ bool IsHashTableEmptyValue() const { return length_ == kEmptyValueLength; }
+
+ private:
+ static const unsigned kCapacity = 15;
+ static const unsigned kEmptyValueLength = kCapacity + 1;
+ static const unsigned kDeletedValueLength = kCapacity + 2;
+
+ unsigned hash_;
+ unsigned length_ : 15;
+ unsigned direction_ : 1;
+ UChar characters_[kCapacity];
+ };
+
+ public:
+ ShapeCache() : weak_factory_(this), version_(0) {
+ // TODO(cavalcantii): Investigate tradeoffs of reserving space
+ // in short_string_map.
+ }
+
+ ShapeCacheEntry* Add(const TextRun& run, ShapeCacheEntry entry) {
+ if (run.length() > SmallStringKey::Capacity())
+ return nullptr;
+
+ return AddSlowCase(run, entry);
+ }
+
+ void ClearIfVersionChanged(unsigned version) {
+ if (version != version_) {
+ Clear();
+ version_ = version;
+ }
+ }
+
+ void Clear() {
+ single_char_map_.clear();
+ short_string_map_.clear();
+ }
+
+ unsigned size() const {
+ return single_char_map_.size() + short_string_map_.size();
+ }
+
+ size_t ByteSize() const {
+ size_t self_byte_size = 0;
+ for (auto cache_entry : single_char_map_) {
+ self_byte_size += cache_entry.value.shape_result_->ByteSize();
+ }
+ for (auto cache_entry : short_string_map_) {
+ self_byte_size += cache_entry.value.shape_result_->ByteSize();
+ }
+ return self_byte_size;
+ }
+
+ base::WeakPtr<ShapeCache> GetWeakPtr() { return weak_factory_.GetWeakPtr(); }
+
+ private:
+ ShapeCacheEntry* AddSlowCase(const TextRun& run, ShapeCacheEntry entry) {
+ unsigned length = run.length();
+ bool is_new_entry;
+ ShapeCacheEntry* value;
+ if (length == 1) {
+ uint32_t key = run[0];
+ // All current codepoints in UTF-32 are bewteen 0x0 and 0x10FFFF,
+ // as such use bit 31 (zero-based) to indicate direction.
+ if (run.Direction() == TextDirection::kRtl)
+ key |= (1u << 31);
+ SingleCharMap::AddResult add_result = single_char_map_.insert(key, entry);
+ is_new_entry = add_result.is_new_entry;
+ value = &add_result.stored_value->value;
+ } else {
+ SmallStringKey small_string_key;
+ if (run.Is8Bit()) {
+ small_string_key =
+ SmallStringKey(run.Characters8(), length, run.Direction());
+ } else {
+ small_string_key =
+ SmallStringKey(run.Characters16(), length, run.Direction());
+ }
+
+ SmallStringMap::AddResult add_result =
+ short_string_map_.insert(small_string_key, entry);
+ is_new_entry = add_result.is_new_entry;
+ value = &add_result.stored_value->value;
+ }
+
+ if ((!is_new_entry) || (size() < kMaxSize)) {
+ return value;
+ }
+
+ // No need to be fancy: we're just trying to avoid pathological growth.
+ single_char_map_.clear();
+ short_string_map_.clear();
+
+ return nullptr;
+ }
+
+ struct SmallStringKeyHash {
+ STATIC_ONLY(SmallStringKeyHash);
+ static unsigned GetHash(const SmallStringKey& key) { return key.GetHash(); }
+ static bool Equal(const SmallStringKey& a, const SmallStringKey& b) {
+ return a == b;
+ }
+ // Empty and deleted values have lengths that are not equal to any valid
+ // length.
+ static const bool safe_to_compare_to_empty_or_deleted = true;
+ };
+
+ struct SmallStringKeyHashTraits : WTF::SimpleClassHashTraits<SmallStringKey> {
+ STATIC_ONLY(SmallStringKeyHashTraits);
+ static const bool kHasIsEmptyValueFunction = true;
+ static bool IsEmptyValue(const SmallStringKey& key) {
+ return key.IsHashTableEmptyValue();
+ }
+ static const unsigned kMinimumTableSize = 16;
+ };
+
+ friend bool operator==(const SmallStringKey&, const SmallStringKey&);
+
+ typedef HashMap<SmallStringKey,
+ ShapeCacheEntry,
+ SmallStringKeyHash,
+ SmallStringKeyHashTraits>
+ SmallStringMap;
+ typedef HashMap<uint32_t,
+ ShapeCacheEntry,
+ DefaultHash<uint32_t>::Hash,
+ WTF::UnsignedWithZeroKeyHashTraits<uint32_t>>
+ SingleCharMap;
+
+ // Hard limit to guard against pathological growth. The expected number of
+ // cache entries is a lot lower given the average word count for a web page
+ // is well below 1,000 and even full length books rarely have over 10,000
+ // unique words [1]. 1: http://www.mine-control.com/zack/guttenberg/
+ // Our definition of a word is somewhat different from the norm in that we
+ // only segment on space. Thus "foo", "foo-", and "foo)" would count as
+ // three separate words. Given that 10,000 seems like a reasonable maximum.
+ static const unsigned kMaxSize = 10000;
+
+ SingleCharMap single_char_map_;
+ SmallStringMap short_string_map_;
+ base::WeakPtrFactory<ShapeCache> weak_factory_;
+ unsigned version_;
+};
+
+inline bool operator==(const ShapeCache::SmallStringKey& a,
+ const ShapeCache::SmallStringKey& b) {
+ if (a.length() != b.length() || a.Direction() != b.Direction())
+ return false;
+ return WTF::Equal(a.Characters(), b.Characters(), a.length());
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_SHAPE_CACHE_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc
new file mode 100644
index 00000000000..f872c8c8451
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc
@@ -0,0 +1,953 @@
+/*
+ * Copyright (c) 2012 Google Inc. All rights reserved.
+ * Copyright (C) 2013 BlackBerry Limited. 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 "third_party/blink/renderer/platform/fonts/shaping/shape_result.h"
+
+#include <hb.h>
+#include <algorithm>
+#include <limits>
+#include <memory>
+#include <utility>
+
+#include "third_party/blink/renderer/platform/fonts/character_range.h"
+#include "third_party/blink/renderer/platform/fonts/font.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_buffer.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_inline_headers.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+
+namespace blink {
+
+unsigned ShapeResult::RunInfo::NextSafeToBreakOffset(unsigned offset) const {
+ DCHECK_LE(offset, num_characters_);
+ for (unsigned i = 0; i < safe_break_offsets_.size(); i++) {
+ if (safe_break_offsets_[i] >= offset)
+ return safe_break_offsets_[i];
+ }
+
+ // Next safe break is at the end of the run.
+ return num_characters_;
+}
+
+unsigned ShapeResult::RunInfo::PreviousSafeToBreakOffset(
+ unsigned offset) const {
+ if (offset >= num_characters_)
+ return num_characters_;
+
+ for (unsigned i = safe_break_offsets_.size(); i > 0; i--) {
+ if (safe_break_offsets_[i - 1] <= offset)
+ return safe_break_offsets_[i - 1];
+ }
+
+ // Next safe break is at the start of the run.
+ return 0;
+}
+
+float ShapeResult::RunInfo::XPositionForVisualOffset(
+ unsigned offset,
+ AdjustMidCluster adjust_mid_cluster) const {
+ DCHECK_LT(offset, num_characters_);
+ if (Rtl())
+ offset = num_characters_ - offset - 1;
+ return XPositionForOffset(offset, adjust_mid_cluster);
+}
+
+float ShapeResult::RunInfo::XPositionForOffset(
+ unsigned offset,
+ AdjustMidCluster adjust_mid_cluster) const {
+ DCHECK_LE(offset, num_characters_);
+ const unsigned num_glyphs = glyph_data_.size();
+ unsigned glyph_index = 0;
+ float position = 0;
+ if (Rtl()) {
+ while (glyph_index < num_glyphs &&
+ glyph_data_[glyph_index].character_index > offset) {
+ position += glyph_data_[glyph_index].advance;
+ ++glyph_index;
+ }
+ // If |glyph_index| is at the end, the glyph for |offset| is missing, along
+ // with all glyphs before it. We can't adjust position to the start
+ // direction.
+ if (glyph_index == num_glyphs)
+ return position;
+ // Adjust offset if it's not on the cluster boundary. In RTL, this means
+ // that the adjusted position is the left side of the character.
+ if (adjust_mid_cluster == AdjustMidCluster::kToEnd &&
+ glyph_data_[glyph_index].character_index < offset) {
+ return position;
+ }
+ // For RTL, we need to return the right side boundary of the character.
+ // Add advance of glyphs which are part of the character.
+ while (glyph_index < num_glyphs - 1 &&
+ glyph_data_[glyph_index].character_index ==
+ glyph_data_[glyph_index + 1].character_index) {
+ position += glyph_data_[glyph_index].advance;
+ ++glyph_index;
+ }
+ position += glyph_data_[glyph_index].advance;
+ } else {
+ while (glyph_index < num_glyphs &&
+ glyph_data_[glyph_index].character_index < offset) {
+ position += glyph_data_[glyph_index].advance;
+ ++glyph_index;
+ }
+ // Adjust offset if it's not on the cluster boundary.
+ if (adjust_mid_cluster == AdjustMidCluster::kToStart && glyph_index &&
+ (glyph_index < num_glyphs ? glyph_data_[glyph_index].character_index
+ : num_characters_) > offset) {
+ offset = glyph_data_[--glyph_index].character_index;
+ for (; glyph_data_[glyph_index].character_index == offset;
+ --glyph_index) {
+ position -= glyph_data_[glyph_index].advance;
+ if (!glyph_index)
+ break;
+ }
+ }
+ }
+ return position;
+}
+
+static bool TargetPastEdge(bool rtl, float target_x, float next_x) {
+ // In LTR, the edge belongs to the character on right.
+ if (!rtl)
+ return target_x < next_x;
+
+ // In RTL, the edge belongs to the character on left.
+ return target_x <= next_x;
+}
+
+int ShapeResult::RunInfo::CharacterIndexForXPosition(
+ float target_x,
+ bool include_partial_glyphs) const {
+ DCHECK(target_x >= 0 && target_x <= width_);
+ if (target_x <= 0)
+ return !Rtl() ? 0 : num_characters_;
+ const unsigned num_glyphs = glyph_data_.size();
+ float current_x = 0;
+ float current_advance = 0;
+ unsigned glyph_index = 0;
+ unsigned prev_character_index = num_characters_; // used only when rtl()
+
+ while (glyph_index < num_glyphs) {
+ float prev_advance = current_advance;
+ unsigned current_character_index = glyph_data_[glyph_index].character_index;
+ current_advance = glyph_data_[glyph_index].advance;
+ while (glyph_index < num_glyphs - 1 &&
+ current_character_index ==
+ glyph_data_[glyph_index + 1].character_index)
+ current_advance += glyph_data_[++glyph_index].advance;
+ float next_x;
+ if (include_partial_glyphs) {
+ // For hit testing, find the closest caret point by incuding
+ // end-half of the previous character and start-half of the current
+ // character.
+ current_advance = current_advance / 2.0;
+ next_x = current_x + prev_advance + current_advance;
+ // When include_partial_glyphs, "<=" or "<" is not a big deal because
+ // |next_x| is not at the character boundary.
+ if (target_x <= next_x)
+ return Rtl() ? prev_character_index : current_character_index;
+ } else {
+ next_x = current_x + current_advance;
+ if (TargetPastEdge(Rtl(), target_x, next_x))
+ return current_character_index;
+ }
+ current_x = next_x;
+ prev_character_index = current_character_index;
+ ++glyph_index;
+ }
+
+ return Rtl() ? 0 : num_characters_;
+}
+
+void ShapeResult::RunInfo::SetGlyphAndPositions(unsigned index,
+ uint16_t glyph_id,
+ float advance,
+ float offset_x,
+ float offset_y) {
+ HarfBuzzRunGlyphData& data = glyph_data_[index];
+ data.glyph = glyph_id;
+ data.advance = advance;
+ data.offset = FloatSize(offset_x, offset_y);
+}
+
+ShapeResult::ShapeResult(const SimpleFontData* font_data,
+ unsigned num_characters,
+ TextDirection direction)
+ : width_(0),
+ primary_font_(font_data),
+ num_characters_(num_characters),
+ num_glyphs_(0),
+ direction_(static_cast<unsigned>(direction)),
+ has_vertical_offsets_(0) {}
+
+ShapeResult::ShapeResult(const Font* font,
+ unsigned num_characters,
+ TextDirection direction)
+ : ShapeResult(font->PrimaryFont(), num_characters, direction) {}
+
+ShapeResult::ShapeResult(const ShapeResult& other)
+ : width_(other.width_),
+ glyph_bounding_box_(other.glyph_bounding_box_),
+ primary_font_(other.primary_font_),
+ num_characters_(other.num_characters_),
+ num_glyphs_(other.num_glyphs_),
+ direction_(other.direction_),
+ has_vertical_offsets_(other.has_vertical_offsets_) {
+ runs_.ReserveCapacity(other.runs_.size());
+ for (const auto& run : other.runs_)
+ runs_.push_back(std::make_unique<RunInfo>(*run));
+}
+
+ShapeResult::~ShapeResult() = default;
+
+size_t ShapeResult::ByteSize() const {
+ size_t self_byte_size = sizeof(this);
+ for (unsigned i = 0; i < runs_.size(); ++i) {
+ self_byte_size += runs_[i]->ByteSize();
+ }
+ return self_byte_size;
+}
+
+CharacterRange ShapeResult::GetCharacterRange(unsigned from,
+ unsigned to) const {
+ return ShapeResultBuffer::GetCharacterRange(this, Direction(), Width(), from,
+ to);
+}
+
+unsigned ShapeResult::StartIndexForResult() const {
+ if (UNLIKELY(runs_.IsEmpty()))
+ return 0;
+ const RunInfo& first_run = *runs_.front();
+ if (!Rtl())
+ return first_run.start_index_;
+ unsigned end = first_run.start_index_ + first_run.num_characters_;
+ DCHECK_GE(end, NumCharacters());
+ return end - NumCharacters();
+}
+
+unsigned ShapeResult::EndIndexForResult() const {
+ if (UNLIKELY(runs_.IsEmpty()))
+ return NumCharacters();
+ const RunInfo& first_run = *runs_.front();
+ if (!Rtl())
+ return first_run.start_index_ + NumCharacters();
+ return first_run.start_index_ + first_run.num_characters_;
+}
+
+scoped_refptr<ShapeResult> ShapeResult::MutableUnique() const {
+ if (HasOneRef())
+ return const_cast<ShapeResult*>(this);
+ return ShapeResult::Create(*this);
+}
+
+unsigned ShapeResult::NextSafeToBreakOffset(unsigned index) const {
+ for (auto it = runs_.begin(); it != runs_.end(); ++it) {
+ const auto& run = *it;
+ if (!run)
+ continue;
+
+ unsigned run_start = run->start_index_;
+ if (index >= run_start) {
+ unsigned offset = index - run_start;
+ if (offset <= run->num_characters_) {
+ return run->NextSafeToBreakOffset(offset) + run_start;
+ }
+ if (Rtl()) {
+ if (it == runs_.begin())
+ return run_start + run->num_characters_;
+ const auto& previous_run = *--it;
+ return previous_run->start_index_;
+ }
+ } else if (!Rtl()) {
+ return run_start;
+ }
+ }
+
+ return EndIndexForResult();
+}
+
+unsigned ShapeResult::PreviousSafeToBreakOffset(unsigned index) const {
+ for (auto it = runs_.rbegin(); it != runs_.rend(); ++it) {
+ const auto& run = *it;
+ if (!run)
+ continue;
+
+ unsigned run_start = run->start_index_;
+ if (index >= run_start) {
+ unsigned offset = index - run_start;
+ if (offset <= run->num_characters_) {
+ return run->PreviousSafeToBreakOffset(offset) + run_start;
+ }
+ if (!Rtl()) {
+ return run_start + run->num_characters_;
+ }
+ } else if (Rtl()) {
+ if (it == runs_.rbegin())
+ return run->start_index_;
+ const auto& previous_run = *--it;
+ return previous_run->start_index_ + previous_run->num_characters_;
+ }
+ }
+
+ return StartIndexForResult();
+}
+
+// If the position is outside of the result, returns the start or the end offset
+// depends on the position.
+unsigned ShapeResult::OffsetForPosition(float target_x,
+ bool include_partial_glyphs) const {
+ unsigned characters_so_far = 0;
+ float current_x = 0;
+
+ if (Rtl()) {
+ if (target_x <= 0)
+ return num_characters_;
+ characters_so_far = num_characters_;
+ for (unsigned i = 0; i < runs_.size(); ++i) {
+ if (!runs_[i])
+ continue;
+ characters_so_far -= runs_[i]->num_characters_;
+ float next_x = current_x + runs_[i]->width_;
+ float offset_for_run = target_x - current_x;
+ if (offset_for_run >= 0 && offset_for_run <= runs_[i]->width_) {
+ // The x value in question is within this script run.
+ const unsigned index = runs_[i]->CharacterIndexForXPosition(
+ offset_for_run, include_partial_glyphs);
+ return characters_so_far + index;
+ }
+ current_x = next_x;
+ }
+ } else {
+ if (target_x <= 0)
+ return 0;
+ for (unsigned i = 0; i < runs_.size(); ++i) {
+ if (!runs_[i])
+ continue;
+ float next_x = current_x + runs_[i]->width_;
+ float offset_for_run = target_x - current_x;
+ if (offset_for_run >= 0 && offset_for_run <= runs_[i]->width_) {
+ const unsigned index = runs_[i]->CharacterIndexForXPosition(
+ offset_for_run, include_partial_glyphs);
+ return characters_so_far + index;
+ }
+ characters_so_far += runs_[i]->num_characters_;
+ current_x = next_x;
+ }
+ }
+
+ return characters_so_far;
+}
+
+float ShapeResult::PositionForOffset(
+ unsigned absolute_offset,
+ AdjustMidCluster adjust_mid_cluster) const {
+ float x = 0;
+ float offset_x = 0;
+
+ // The absolute_offset argument represents the offset for the entire
+ // ShapeResult while offset is continuously updated to be relative to the
+ // current run.
+ unsigned offset = absolute_offset;
+
+ if (Rtl()) {
+ // Convert logical offsets to visual offsets, because results are in
+ // logical order while runs are in visual order.
+ x = width_;
+ if (offset < NumCharacters())
+ offset = NumCharacters() - offset - 1;
+ x -= Width();
+ }
+
+ for (unsigned i = 0; i < runs_.size(); i++) {
+ if (!runs_[i])
+ continue;
+ DCHECK_EQ(Rtl(), runs_[i]->Rtl());
+ unsigned num_characters = runs_[i]->num_characters_;
+
+ if (!offset_x && offset < num_characters) {
+ offset_x =
+ runs_[i]->XPositionForVisualOffset(offset, adjust_mid_cluster) + x;
+ break;
+ }
+
+ offset -= num_characters;
+ x += runs_[i]->width_;
+ }
+
+ // The position in question might be just after the text.
+ if (!offset_x && absolute_offset == NumCharacters())
+ return Rtl() ? 0 : width_;
+
+ return offset_x;
+}
+
+void ShapeResult::FallbackFonts(
+ HashSet<const SimpleFontData*>* fallback) const {
+ DCHECK(fallback);
+ DCHECK(primary_font_);
+ for (unsigned i = 0; i < runs_.size(); ++i) {
+ if (runs_[i] && runs_[i]->font_data_ &&
+ runs_[i]->font_data_ != primary_font_) {
+ fallback->insert(runs_[i]->font_data_.get());
+ }
+ }
+}
+
+void ShapeResult::GetRunFontData(Vector<RunFontData>* font_data) const {
+ for (const auto& run : runs_) {
+ font_data->push_back(
+ RunFontData({run->font_data_.get(), run->glyph_data_.size()}));
+ }
+}
+
+// TODO(kojii): VC2015 fails to explicit instantiation of a member function.
+// Typed functions + this private function are to instantiate instances.
+template <typename TextContainerType>
+void ShapeResult::ApplySpacingImpl(
+ ShapeResultSpacing<TextContainerType>& spacing,
+ int text_start_offset) {
+ float offset = 0;
+ float total_space = 0;
+ float space = 0;
+ for (auto& run : runs_) {
+ if (!run)
+ continue;
+ unsigned run_start_index = run->start_index_ + text_start_offset;
+ float total_space_for_run = 0;
+ for (size_t i = 0; i < run->glyph_data_.size(); i++) {
+ HarfBuzzRunGlyphData& glyph_data = run->glyph_data_[i];
+
+ // Skip if it's not a grapheme cluster boundary.
+ if (i + 1 < run->glyph_data_.size() &&
+ glyph_data.character_index ==
+ run->glyph_data_[i + 1].character_index) {
+ continue;
+ }
+
+ space = spacing.ComputeSpacing(
+ run_start_index + glyph_data.character_index, offset);
+ glyph_data.advance += space;
+ total_space_for_run += space;
+
+ // |offset| is non-zero only when justifying CJK characters that follow
+ // non-CJK characters.
+ if (UNLIKELY(offset)) {
+ if (run->IsHorizontal()) {
+ glyph_data.offset.SetWidth(glyph_data.offset.Width() + offset);
+ } else {
+ glyph_data.offset.SetHeight(glyph_data.offset.Height() + offset);
+ has_vertical_offsets_ = true;
+ }
+ offset = 0;
+ }
+ }
+ run->width_ += total_space_for_run;
+ total_space += total_space_for_run;
+ }
+ width_ += total_space;
+
+ // The spacing on the right of the last glyph does not affect the glyph
+ // bounding box. Thus, the glyph bounding box becomes smaller than the advance
+ // if the letter spacing is positve, or larger if negative.
+ if (space) {
+ total_space -= space;
+
+ // TODO(kojii): crbug.com/768284: There are cases where
+ // InlineTextBox::LogicalWidth() is round down of ShapeResult::Width() in
+ // LayoutUnit. Ceiling the width did not help. Add 1px to avoid cut-off.
+ if (space < 0)
+ total_space += 1;
+ }
+
+ // Set the width because glyph bounding box is in logical space.
+ float glyph_bounding_box_width = glyph_bounding_box_.Width() + total_space;
+ if (width_ >= 0 && glyph_bounding_box_width >= 0) {
+ glyph_bounding_box_.SetWidth(glyph_bounding_box_width);
+ return;
+ }
+
+ // Negative word-spacing and/or letter-spacing may cause some glyphs to
+ // overflow the left boundary and result negative measured width. Adjust glyph
+ // bounds accordingly to cover the overflow.
+ // The negative width should be clamped to 0 in CSS box model, but it's up to
+ // caller's responsibility.
+ float left = std::min(width_, glyph_bounding_box_width);
+ if (left < glyph_bounding_box_.X()) {
+ // The right edge should be the width of the first character in most cases,
+ // but computing it requires re-measuring bounding box of each glyph. Leave
+ // it unchanged, which gives an excessive right edge but assures it covers
+ // all glyphs.
+ glyph_bounding_box_.ShiftXEdgeTo(left);
+ } else {
+ glyph_bounding_box_.SetWidth(glyph_bounding_box_width);
+ }
+}
+
+void ShapeResult::ApplySpacing(ShapeResultSpacing<String>& spacing,
+ int text_start_offset) {
+ ApplySpacingImpl(spacing, text_start_offset);
+}
+
+scoped_refptr<ShapeResult> ShapeResult::ApplySpacingToCopy(
+ ShapeResultSpacing<TextRun>& spacing,
+ const TextRun& run) const {
+ unsigned index_of_sub_run = spacing.Text().IndexOfSubRun(run);
+ DCHECK_NE(std::numeric_limits<unsigned>::max(), index_of_sub_run);
+ scoped_refptr<ShapeResult> result = ShapeResult::Create(*this);
+ if (index_of_sub_run != std::numeric_limits<unsigned>::max())
+ result->ApplySpacingImpl(spacing, index_of_sub_run);
+ return result;
+}
+
+namespace {
+
+float HarfBuzzPositionToFloat(hb_position_t value) {
+ return static_cast<float>(value) / (1 << 16);
+}
+
+// Checks whether it's safe to break without reshaping before the given glyph.
+bool IsSafeToBreakBefore(const hb_glyph_info_t* glyph_infos,
+ unsigned num_glyphs,
+ unsigned i) {
+ // Before the first glyph is safe to break.
+ if (!i)
+ return true;
+
+ // Not at a cluster boundary.
+ if (glyph_infos[i].cluster == glyph_infos[i - 1].cluster)
+ return false;
+
+ // The HB_GLYPH_FLAG_UNSAFE_TO_BREAK flag is set for all glyphs in a
+ // given cluster so we only need to check the last one.
+ hb_glyph_flags_t flags = hb_glyph_info_get_glyph_flags(glyph_infos + i);
+ return (flags & HB_GLYPH_FLAG_UNSAFE_TO_BREAK) == 0;
+}
+
+} // anonymous namespace
+
+// Computes glyph positions, sets advance and offset of each glyph to RunInfo.
+//
+// Also computes glyph bounding box of the run. In this function, glyph bounding
+// box is in physical.
+template <bool is_horizontal_run>
+void ShapeResult::ComputeGlyphPositions(ShapeResult::RunInfo* run,
+ unsigned start_glyph,
+ unsigned num_glyphs,
+ hb_buffer_t* harf_buzz_buffer,
+ FloatRect* glyph_bounding_box) {
+ DCHECK_EQ(is_horizontal_run, run->IsHorizontal());
+ const SimpleFontData* current_font_data = run->font_data_.get();
+ const hb_glyph_info_t* glyph_infos =
+ hb_buffer_get_glyph_infos(harf_buzz_buffer, nullptr);
+ const hb_glyph_position_t* glyph_positions =
+ hb_buffer_get_glyph_positions(harf_buzz_buffer, nullptr);
+ const unsigned start_cluster =
+ HB_DIRECTION_IS_FORWARD(hb_buffer_get_direction(harf_buzz_buffer))
+ ? glyph_infos[start_glyph].cluster
+ : glyph_infos[start_glyph + num_glyphs - 1].cluster;
+
+ // Compute glyph_origin and glyph_bounding_box in physical, since both offsets
+ // and boudning box of glyphs are in physical. It's the caller's
+ // responsibility to convert the united physical bounds to logical.
+ float total_advance = 0.0f;
+ FloatPoint glyph_origin;
+ if (is_horizontal_run)
+ glyph_origin.SetX(width_);
+ else
+ glyph_origin.SetY(width_);
+ bool has_vertical_offsets = !is_horizontal_run;
+
+ // HarfBuzz returns result in visual order, no need to flip for RTL.
+ for (unsigned i = 0; i < num_glyphs; ++i) {
+ uint16_t glyph = glyph_infos[start_glyph + i].codepoint;
+ hb_glyph_position_t pos = glyph_positions[start_glyph + i];
+
+ // Offset is primarily used when painting glyphs. Keep it in physical.
+ float offset_x = HarfBuzzPositionToFloat(pos.x_offset);
+ float offset_y = -HarfBuzzPositionToFloat(pos.y_offset);
+
+ // One out of x_advance and y_advance is zero, depending on
+ // whether the buffer direction is horizontal or vertical.
+ // Convert to float and negate to avoid integer-overflow for ULONG_MAX.
+ float advance;
+ if (is_horizontal_run)
+ advance = HarfBuzzPositionToFloat(pos.x_advance);
+ else
+ advance = -HarfBuzzPositionToFloat(pos.y_advance);
+
+ uint16_t character_index =
+ glyph_infos[start_glyph + i].cluster - start_cluster;
+ run->glyph_data_[i].character_index = character_index;
+
+ run->SetGlyphAndPositions(i, glyph, advance, offset_x, offset_y);
+ total_advance += advance;
+ has_vertical_offsets |= (offset_y != 0);
+
+ // SetGlyphAndPositions() above sets to draw glyphs at |glyph_origin +
+ // offset_{x,y}|. Move glyph_bounds to that point.
+ // Then move the current point by |advance| from |glyph_origin|.
+ // All positions in hb_glyph_position_t are relative to the current point.
+ // https://behdad.github.io/harfbuzz/harfbuzz-Buffers.html#hb-glyph-position-t-struct
+ FloatRect glyph_bounds = current_font_data->BoundsForGlyph(glyph);
+ if (!glyph_bounds.IsEmpty()) {
+ glyph_bounds.Move(glyph_origin.X() + offset_x,
+ glyph_origin.Y() + offset_y);
+ glyph_bounding_box->Unite(glyph_bounds);
+ }
+ if (is_horizontal_run)
+ glyph_origin.SetX(glyph_origin.X() + advance);
+ else
+ glyph_origin.SetY(glyph_origin.Y() + advance);
+
+ // Check if it is safe to break without reshaping before the cluster.
+ if (IsSafeToBreakBefore(glyph_infos + start_glyph, num_glyphs, i)) {
+ if (run->Rtl())
+ run->safe_break_offsets_.push_front(character_index);
+ else
+ run->safe_break_offsets_.push_back(character_index);
+ }
+ }
+
+ run->width_ = std::max(0.0f, total_advance);
+ has_vertical_offsets_ |= has_vertical_offsets;
+}
+
+void ShapeResult::InsertRun(std::unique_ptr<ShapeResult::RunInfo> run_to_insert,
+ unsigned start_glyph,
+ unsigned num_glyphs,
+ hb_buffer_t* harf_buzz_buffer) {
+ DCHECK_GT(num_glyphs, 0u);
+ std::unique_ptr<ShapeResult::RunInfo> run(std::move(run_to_insert));
+ DCHECK_EQ(num_glyphs, run->glyph_data_.size());
+
+ FloatRect glyph_bounding_box;
+ if (run->IsHorizontal()) {
+ // Inserting a horizontal run into a horizontal or vertical result. In both
+ // cases, no adjustments are needed because |glyph_bounding_box_| is in
+ // logical coordinates and uses alphabetic baseline.
+ ComputeGlyphPositions<true>(run.get(), start_glyph, num_glyphs,
+ harf_buzz_buffer, &glyph_bounding_box);
+ } else {
+ // Inserting a vertical run to a vertical result.
+ ComputeGlyphPositions<false>(run.get(), start_glyph, num_glyphs,
+ harf_buzz_buffer, &glyph_bounding_box);
+ // Convert physical glyph_bounding_box to logical.
+ glyph_bounding_box = glyph_bounding_box.TransposedRect();
+ // The glyph bounding box of a vertical run uses ideographic baseline.
+ // Adjust the box Y position because the bounding box of a ShapeResult uses
+ // alphabetic baseline.
+ // See diagrams of base lines at
+ // https://drafts.csswg.org/css-writing-modes-3/#intro-baselines
+ const FontMetrics& font_metrics = run->font_data_->GetFontMetrics();
+ int baseline_adjust = font_metrics.Ascent(kIdeographicBaseline) -
+ font_metrics.Ascent(kAlphabeticBaseline);
+ glyph_bounding_box.SetY(glyph_bounding_box.Y() + baseline_adjust);
+ }
+ glyph_bounding_box_.Unite(glyph_bounding_box);
+ width_ += run->width_;
+ num_glyphs_ += num_glyphs;
+ DCHECK_GE(num_glyphs_, num_glyphs);
+
+ InsertRun(std::move(run));
+}
+
+void ShapeResult::InsertRun(std::unique_ptr<ShapeResult::RunInfo> run) {
+ // The runs are stored in result->m_runs in visual order. For LTR, we place
+ // the run to be inserted before the next run with a bigger character
+ // start index. For RTL, we place the run before the next run with a lower
+ // character index. Otherwise, for both directions, at the end.
+ if (HB_DIRECTION_IS_FORWARD(run->direction_)) {
+ for (size_t pos = 0; pos < runs_.size(); ++pos) {
+ if (runs_.at(pos)->start_index_ > run->start_index_) {
+ runs_.insert(pos, std::move(run));
+ break;
+ }
+ }
+ } else {
+ for (size_t pos = 0; pos < runs_.size(); ++pos) {
+ if (runs_.at(pos)->start_index_ < run->start_index_) {
+ runs_.insert(pos, std::move(run));
+ break;
+ }
+ }
+ }
+ // If we didn't find an existing slot to place it, append.
+ if (run)
+ runs_.push_back(std::move(run));
+}
+
+// Insert a |RunInfo| without glyphs. |StartIndexForResult()| needs a run to
+// compute the start character index. When all glyphs are missing, this function
+// synthesize a run without glyphs.
+void ShapeResult::InsertRunForIndex(unsigned start_character_index) {
+ DCHECK(runs_.IsEmpty());
+ runs_.push_back(std::make_unique<RunInfo>(
+ primary_font_.get(), !Rtl() ? HB_DIRECTION_LTR : HB_DIRECTION_RTL,
+ CanvasRotationInVertical::kRegular, HB_SCRIPT_UNKNOWN,
+ start_character_index, 0, num_characters_));
+}
+
+ShapeResult::RunInfo* ShapeResult::InsertRunForTesting(
+ unsigned start_index,
+ unsigned num_characters,
+ TextDirection direction,
+ Vector<uint16_t> safe_break_offsets) {
+ std::unique_ptr<RunInfo> run = std::make_unique<ShapeResult::RunInfo>(
+ nullptr, IsLtr(direction) ? HB_DIRECTION_LTR : HB_DIRECTION_RTL,
+ CanvasRotationInVertical::kRegular, HB_SCRIPT_COMMON, start_index, 0,
+ num_characters);
+ run->safe_break_offsets_.AppendVector(safe_break_offsets);
+ RunInfo* run_ptr = run.get();
+ InsertRun(std::move(run));
+ return run_ptr;
+}
+
+// Moves runs at (run_size_before, end) to the front of |runs_|.
+//
+// Runs in RTL result are in visual order, and that new runs should be
+// prepended. This function adjusts the run order after runs were appended.
+void ShapeResult::ReorderRtlRuns(unsigned run_size_before) {
+ DCHECK(Rtl());
+ DCHECK_GT(runs_.size(), run_size_before);
+ if (runs_.size() == run_size_before + 1) {
+ if (!run_size_before)
+ return;
+ std::unique_ptr<RunInfo> new_run(std::move(runs_.back()));
+ runs_.Shrink(runs_.size() - 1);
+ runs_.push_front(std::move(new_run));
+ return;
+ }
+
+ // |push_front| is O(n) that we should not call it multiple times.
+ // Create a new list in the correct order and swap it.
+ Vector<std::unique_ptr<RunInfo>> new_runs;
+ new_runs.ReserveInitialCapacity(runs_.size());
+ for (unsigned i = run_size_before; i < runs_.size(); i++)
+ new_runs.push_back(std::move(runs_[i]));
+
+ // Then append existing runs.
+ for (unsigned i = 0; i < run_size_before; i++)
+ new_runs.push_back(std::move(runs_[i]));
+ runs_.swap(new_runs);
+}
+
+void ShapeResult::CopyRange(unsigned start_offset,
+ unsigned end_offset,
+ ShapeResult* target) const {
+ if (!runs_.size())
+ return;
+
+#if DCHECK_IS_ON()
+ unsigned target_num_characters_before = target->num_characters_;
+#endif
+
+ // When |target| is empty, its character indexes are the specified sub range
+ // of |this|. Otherwise the character indexes are renumbered to be continuous.
+ int index_diff = !target->num_characters_
+ ? 0
+ : target->EndIndexForResult() -
+ std::max(start_offset, StartIndexForResult());
+ unsigned target_run_size_before = target->runs_.size();
+ float total_width = 0;
+ for (const auto& run : runs_) {
+ unsigned run_start = run->start_index_;
+ unsigned run_end = run_start + run->num_characters_;
+
+ if (start_offset < run_end && end_offset > run_start) {
+ unsigned start = start_offset > run_start ? start_offset - run_start : 0;
+ unsigned end = std::min(end_offset, run_end) - run_start;
+ DCHECK(end > start);
+
+ auto sub_run = run->CreateSubRun(start, end);
+ sub_run->start_index_ += index_diff;
+ total_width += sub_run->width_;
+ target->num_characters_ += sub_run->num_characters_;
+ target->num_glyphs_ += sub_run->glyph_data_.size();
+ target->runs_.push_back(std::move(sub_run));
+ }
+ }
+
+ if (target->runs_.size() == target_run_size_before)
+ return;
+
+ // Runs in RTL result are in visual order, and that new runs should be
+ // prepended. Reorder appended runs.
+ DCHECK_EQ(Rtl(), target->Rtl());
+ if (target->Rtl())
+ target->ReorderRtlRuns(target_run_size_before);
+
+ // Compute new glyph bounding box.
+ // If |start_offset| or |end_offset| are the start/end of |this|, use
+ // |glyph_bounding_box_| from |this| for the side. Otherwise, we cannot
+ // compute accurate glyph bounding box; approximate by assuming there are no
+ // glyph overflow nor underflow.
+ float left = target->width_;
+ target->width_ += total_width;
+ float right = target->width_;
+ if (start_offset <= StartIndexForResult())
+ left += glyph_bounding_box_.X();
+ if (end_offset >= EndIndexForResult())
+ right += glyph_bounding_box_.MaxX() - width_;
+ FloatRect adjusted_box(left, glyph_bounding_box_.Y(),
+ std::max(right - left, 0.0f),
+ glyph_bounding_box_.Height());
+ target->glyph_bounding_box_.UniteIfNonZero(adjusted_box);
+
+ target->has_vertical_offsets_ |= has_vertical_offsets_;
+
+#if DCHECK_IS_ON()
+ DCHECK_EQ(target->num_characters_ - target_num_characters_before,
+ std::min(end_offset, EndIndexForResult()) -
+ std::max(start_offset, StartIndexForResult()));
+
+ target->CheckConsistency();
+#endif
+}
+
+scoped_refptr<ShapeResult> ShapeResult::SubRange(unsigned start_offset,
+ unsigned end_offset) const {
+ scoped_refptr<ShapeResult> sub_range =
+ Create(primary_font_.get(), 0, Direction());
+ CopyRange(start_offset, end_offset, sub_range.get());
+ return sub_range;
+}
+
+#if DCHECK_IS_ON()
+void ShapeResult::CheckConsistency() const {
+ if (runs_.IsEmpty()) {
+ DCHECK_EQ(0u, num_characters_);
+ DCHECK_EQ(0u, num_glyphs_);
+ return;
+ }
+
+ unsigned index = StartIndexForResult();
+ unsigned num_glyphs = 0;
+ if (!Rtl()) {
+ for (const auto& run : runs_) {
+ DCHECK_EQ(index, run->start_index_);
+ index += run->num_characters_;
+ num_glyphs += run->glyph_data_.size();
+ }
+ } else {
+ // RTL on Mac may not have runs for the all characters. crbug.com/774034
+ index = runs_.back()->start_index_;
+ for (auto it = runs_.rbegin(); it != runs_.rend(); ++it) {
+ const auto& run = *it;
+ DCHECK_EQ(index, run->start_index_);
+ index += run->num_characters_;
+ num_glyphs += run->glyph_data_.size();
+ }
+ }
+ DCHECK_EQ(index, EndIndexForResult());
+ DCHECK_EQ(num_glyphs, num_glyphs_);
+}
+#endif
+
+scoped_refptr<ShapeResult> ShapeResult::CreateForTabulationCharacters(
+ const Font* font,
+ const TextRun& text_run,
+ float position_offset,
+ unsigned count) {
+ const SimpleFontData* font_data = font->PrimaryFont();
+ // Tab characters are always LTR or RTL, not TTB, even when
+ // isVerticalAnyUpright().
+ std::unique_ptr<ShapeResult::RunInfo> run = std::make_unique<RunInfo>(
+ font_data, text_run.Rtl() ? HB_DIRECTION_RTL : HB_DIRECTION_LTR,
+ CanvasRotationInVertical::kRegular, HB_SCRIPT_COMMON, 0, count, count);
+ float position = text_run.XPos() + position_offset;
+ float start_position = position;
+ for (unsigned i = 0; i < count; i++) {
+ float advance = font->TabWidth(font_data, text_run.GetTabSize(), position);
+ run->glyph_data_[i].character_index = i;
+ run->SetGlyphAndPositions(i, font_data->SpaceGlyph(), advance, 0, 0);
+
+ // Assume it's safe to break after a tab character.
+ run->safe_break_offsets_.push_back(run->glyph_data_[i].character_index);
+ position += advance;
+ }
+ run->width_ = position - start_position;
+
+ scoped_refptr<ShapeResult> result =
+ ShapeResult::Create(font, count, text_run.Direction());
+ result->width_ = run->width_;
+ result->num_glyphs_ = count;
+ DCHECK_EQ(result->num_glyphs_, count); // no overflow
+ result->has_vertical_offsets_ =
+ font_data->PlatformData().IsVerticalAnyUpright();
+ result->runs_.push_back(std::move(run));
+ return result;
+}
+
+void ShapeResult::ToString(StringBuilder* output) const {
+ output->Append("#chars=");
+ output->AppendNumber(num_characters_);
+ output->Append(", #glyphs=");
+ output->AppendNumber(num_glyphs_);
+ output->Append(", dir=");
+ output->AppendNumber(direction_);
+ output->Append(", runs[");
+ output->AppendNumber(runs_.size());
+ output->Append("]{");
+ for (unsigned run_index = 0; run_index < runs_.size(); run_index++) {
+ output->AppendNumber(run_index);
+ const auto& run = *runs_[run_index];
+ output->Append(":{start=");
+ output->AppendNumber(run.start_index_);
+ output->Append(", #chars=");
+ output->AppendNumber(run.num_characters_);
+ output->Append(", dir=");
+ output->AppendNumber(run.direction_);
+ output->Append(", glyphs[");
+ output->AppendNumber(run.glyph_data_.size());
+ output->Append("]{");
+ for (unsigned glyph_index = 0; glyph_index < run.glyph_data_.size();
+ glyph_index++) {
+ output->AppendNumber(glyph_index);
+ const auto& glyph_data = run.glyph_data_[glyph_index];
+ output->Append(":{char=");
+ output->AppendNumber(glyph_data.character_index);
+ output->Append(", glyph=");
+ output->AppendNumber(glyph_data.glyph);
+ output->Append("}");
+ }
+ output->Append("}}");
+ }
+ output->Append("}");
+}
+
+String ShapeResult::ToString() const {
+ StringBuilder output;
+ ToString(&output);
+ return output.ToString();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result.h b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result.h
new file mode 100644
index 00000000000..3d842044e48
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result.h
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2012 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_SHAPE_RESULT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_SHAPE_RESULT_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/fonts/canvas_rotation_in_vertical.h"
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/layout_unit.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/text/text_direction.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+struct hb_buffer_t;
+
+namespace blink {
+
+struct CharacterRange;
+class Font;
+template <typename TextContainerType>
+class PLATFORM_EXPORT ShapeResultSpacing;
+class SimpleFontData;
+class TextRun;
+
+enum class AdjustMidCluster {
+ // Adjust the middle of a grapheme cluster to the logical end boundary.
+ kToEnd,
+ // Adjust the middle of a grapheme cluster to the logical start boundary.
+ kToStart
+};
+
+class PLATFORM_EXPORT ShapeResult : public RefCounted<ShapeResult> {
+ public:
+ static scoped_refptr<ShapeResult> Create(const Font* font,
+ unsigned num_characters,
+ TextDirection direction) {
+ return base::AdoptRef(new ShapeResult(font, num_characters, direction));
+ }
+ static scoped_refptr<ShapeResult> CreateForTabulationCharacters(
+ const Font*,
+ const TextRun&,
+ float position_offset,
+ unsigned count);
+ ~ShapeResult();
+
+ // Returns a mutable unique instance. If |this| has more than 1 ref count,
+ // a clone is created.
+ scoped_refptr<ShapeResult> MutableUnique() const;
+
+ // The logical width of this result.
+ float Width() const { return width_; }
+ LayoutUnit SnappedWidth() const { return LayoutUnit::FromFloatCeil(width_); }
+ // The glyph bounding box, in logical coordinates, using alphabetic baseline
+ // even when the result is in vertical flow.
+ const FloatRect& Bounds() const { return glyph_bounding_box_; }
+ unsigned NumCharacters() const { return num_characters_; }
+ CharacterRange GetCharacterRange(unsigned from, unsigned to) const;
+ // The character start/end index of a range shape result.
+ unsigned StartIndexForResult() const;
+ unsigned EndIndexForResult() const;
+ void FallbackFonts(HashSet<const SimpleFontData*>*) const;
+ TextDirection Direction() const {
+ return static_cast<TextDirection>(direction_);
+ }
+ bool Rtl() const { return Direction() == TextDirection::kRtl; }
+
+ // True if at least one glyph in this result has vertical offsets.
+ //
+ // Vertical result always has vertical offsets, but horizontal result may also
+ // have vertical offsets.
+ bool HasVerticalOffsets() const { return has_vertical_offsets_; }
+
+ // For memory reporting.
+ size_t ByteSize() const;
+
+ // Returns the next or previous offsets respectively at which it is safe to
+ // break without reshaping.
+ // The |offset| given and the return value is for the original string, between
+ // |StartIndexForResult| and |EndIndexForResult|.
+ unsigned NextSafeToBreakOffset(unsigned offset) const;
+ unsigned PreviousSafeToBreakOffset(unsigned offset) const;
+
+ unsigned OffsetForPosition(float target_x, bool include_partial_glyphs) const;
+ float PositionForOffset(unsigned offset,
+ AdjustMidCluster = AdjustMidCluster::kToEnd) const;
+ LayoutUnit SnappedStartPositionForOffset(unsigned offset) const {
+ return LayoutUnit::FromFloatFloor(PositionForOffset(offset));
+ }
+ LayoutUnit SnappedEndPositionForOffset(unsigned offset) const {
+ return LayoutUnit::FromFloatCeil(PositionForOffset(offset));
+ }
+
+ // Apply spacings (letter-spacing, word-spacing, and justification) as
+ // configured to |ShapeResultSpacing|.
+ // |text_start_offset| adjusts the character index in the ShapeResult before
+ // giving it to |ShapeResultSpacing|. It can be negative if
+ // |StartIndexForResult()| is larger than the text in |ShapeResultSpacing|.
+ void ApplySpacing(ShapeResultSpacing<String>&, int text_start_offset = 0);
+ scoped_refptr<ShapeResult> ApplySpacingToCopy(ShapeResultSpacing<TextRun>&,
+ const TextRun&) const;
+
+ void CopyRange(unsigned start, unsigned end, ShapeResult*) const;
+ scoped_refptr<ShapeResult> SubRange(unsigned start_offset,
+ unsigned end_offset) const;
+
+ // Computes the list of fonts along with the number of glyphs for each font.
+ struct RunFontData {
+ SimpleFontData* font_data_;
+ size_t glyph_count_;
+ };
+ void GetRunFontData(Vector<RunFontData>* font_data) const;
+
+ String ToString() const;
+ void ToString(StringBuilder*) const;
+
+ struct RunInfo;
+ RunInfo* InsertRunForTesting(unsigned start_index,
+ unsigned num_characters,
+ TextDirection,
+ Vector<uint16_t> safe_break_offsets = {});
+#if DCHECK_IS_ON()
+ void CheckConsistency() const;
+#endif
+
+ protected:
+ ShapeResult(const SimpleFontData*, unsigned num_characters, TextDirection);
+ ShapeResult(const Font*, unsigned num_characters, TextDirection);
+ ShapeResult(const ShapeResult&);
+
+ static scoped_refptr<ShapeResult> Create(const SimpleFontData* font_data,
+ unsigned num_characters,
+ TextDirection direction) {
+ return base::AdoptRef(
+ new ShapeResult(font_data, num_characters, direction));
+ }
+ static scoped_refptr<ShapeResult> Create(const ShapeResult& other) {
+ return base::AdoptRef(new ShapeResult(other));
+ }
+
+ template <typename TextContainerType>
+ void ApplySpacingImpl(ShapeResultSpacing<TextContainerType>&,
+ int text_start_offset = 0);
+ template <bool is_horizontal_run>
+ void ComputeGlyphPositions(ShapeResult::RunInfo*,
+ unsigned start_glyph,
+ unsigned num_glyphs,
+ hb_buffer_t*,
+ FloatRect* glyph_bounding_box);
+ void InsertRun(std::unique_ptr<ShapeResult::RunInfo>,
+ unsigned start_glyph,
+ unsigned num_glyphs,
+ hb_buffer_t*);
+ void InsertRun(std::unique_ptr<ShapeResult::RunInfo>);
+ void InsertRunForIndex(unsigned start_character_index);
+ void ReorderRtlRuns(unsigned run_size_before);
+
+ float width_;
+ FloatRect glyph_bounding_box_;
+ Vector<std::unique_ptr<RunInfo>> runs_;
+ scoped_refptr<const SimpleFontData> primary_font_;
+
+ unsigned num_characters_;
+ unsigned num_glyphs_ : 30;
+
+ // Overall direction for the TextRun, dictates which order each individual
+ // sub run (represented by RunInfo structs in the m_runs vector) can have a
+ // different text direction.
+ unsigned direction_ : 1;
+
+ // Tracks whether any runs contain glyphs with a y-offset != 0.
+ unsigned has_vertical_offsets_ : 1;
+
+ friend class HarfBuzzShaper;
+ friend class ShapeResultBuffer;
+ friend class ShapeResultBloberizer;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_SHAPE_RESULT_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer.cc b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer.cc
new file mode 100644
index 00000000000..43d94a6941b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer.cc
@@ -0,0 +1,420 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer.h"
+
+#include <hb.h>
+#include "third_party/blink/renderer/platform/fonts/font.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_result.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_inline_headers.h"
+#include "third_party/blink/renderer/platform/text/text_break_iterator.h"
+#include "third_party/blink/renderer/platform/text/text_run.h"
+
+namespace blink {
+
+ShapeResultBloberizer::ShapeResultBloberizer(const Font& font,
+ float device_scale_factor,
+ Type type)
+ : font_(font), device_scale_factor_(device_scale_factor), type_(type) {}
+
+bool ShapeResultBloberizer::HasPendingVerticalOffsets() const {
+ // We exclusively store either horizontal/x-only ofssets -- in which case
+ // m_offsets.size == size, or vertical/xy offsets -- in which case
+ // m_offsets.size == size * 2.
+ DCHECK(pending_glyphs_.size() == pending_offsets_.size() ||
+ pending_glyphs_.size() * 2 == pending_offsets_.size());
+ return pending_glyphs_.size() != pending_offsets_.size();
+}
+
+void ShapeResultBloberizer::CommitPendingRun() {
+ if (pending_glyphs_.IsEmpty())
+ return;
+
+ if (pending_canvas_rotation_ != builder_rotation_) {
+ // The pending run rotation doesn't match the current blob; start a new
+ // blob.
+ CommitPendingBlob();
+ builder_rotation_ = pending_canvas_rotation_;
+ }
+
+ PaintFont run_font;
+ run_font.SetTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ pending_font_data_->PlatformData().SetupPaintFont(
+ &run_font, device_scale_factor_, &font_);
+
+ const auto run_size = pending_glyphs_.size();
+ const auto& buffer = HasPendingVerticalOffsets()
+ ? builder_.AllocRunPos(run_font, run_size)
+ : builder_.AllocRunPosH(run_font, run_size, 0);
+
+ std::copy(pending_glyphs_.begin(), pending_glyphs_.end(), buffer.glyphs);
+ std::copy(pending_offsets_.begin(), pending_offsets_.end(), buffer.pos);
+
+ builder_run_count_ += 1;
+ pending_glyphs_.Shrink(0);
+ pending_offsets_.Shrink(0);
+}
+
+void ShapeResultBloberizer::CommitPendingBlob() {
+ if (!builder_run_count_)
+ return;
+
+ blobs_.emplace_back(builder_.TakeTextBlob(), builder_rotation_);
+ builder_run_count_ = 0;
+}
+
+const ShapeResultBloberizer::BlobBuffer& ShapeResultBloberizer::Blobs() {
+ CommitPendingRun();
+ CommitPendingBlob();
+ DCHECK(pending_glyphs_.IsEmpty());
+ DCHECK_EQ(builder_run_count_, 0u);
+
+ return blobs_;
+}
+
+float ShapeResultBloberizer::FillGlyphs(
+ const TextRunPaintInfo& run_info,
+ const ShapeResultBuffer& result_buffer) {
+ if (CanUseFastPath(run_info.from, run_info.to, run_info.run.length(),
+ result_buffer.HasVerticalOffsets())) {
+ return FillFastHorizontalGlyphs(result_buffer, run_info.run.Direction());
+ }
+
+ float advance = 0;
+ auto results = result_buffer.results_;
+
+ if (run_info.run.Rtl()) {
+ unsigned word_offset = run_info.run.length();
+ for (unsigned j = 0; j < results.size(); j++) {
+ unsigned resolved_index = results.size() - 1 - j;
+ const scoped_refptr<const ShapeResult>& word_result = results[resolved_index];
+ word_offset -= word_result->NumCharacters();
+ advance =
+ FillGlyphsForResult(word_result.get(), run_info.run, run_info.from,
+ run_info.to, advance, word_offset);
+ }
+ } else {
+ unsigned word_offset = 0;
+ for (const auto& word_result : results) {
+ advance =
+ FillGlyphsForResult(word_result.get(), run_info.run, run_info.from,
+ run_info.to, advance, word_offset);
+ word_offset += word_result->NumCharacters();
+ }
+ }
+
+ return advance;
+}
+
+float ShapeResultBloberizer::FillGlyphs(const StringView& text,
+ unsigned from,
+ unsigned to,
+ const ShapeResult* result) {
+ DCHECK(result);
+ DCHECK(to <= text.length());
+ if (CanUseFastPath(from, to, result))
+ return FillFastHorizontalGlyphs(result);
+
+ float advance = 0;
+ float word_offset = 0;
+ return FillGlyphsForResult(result, text, from, to, advance, word_offset);
+}
+
+void ShapeResultBloberizer::FillTextEmphasisGlyphs(
+ const TextRunPaintInfo& run_info,
+ const GlyphData& emphasis_data,
+ const ShapeResultBuffer& result_buffer) {
+ float advance = 0;
+ unsigned word_offset = run_info.run.Rtl() ? run_info.run.length() : 0;
+ auto results = result_buffer.results_;
+
+ for (unsigned j = 0; j < results.size(); j++) {
+ unsigned resolved_index = run_info.run.Rtl() ? results.size() - 1 - j : j;
+ const scoped_refptr<const ShapeResult>& word_result = results[resolved_index];
+ for (unsigned i = 0; i < word_result->runs_.size(); i++) {
+ unsigned resolved_offset =
+ word_offset - (run_info.run.Rtl() ? word_result->NumCharacters() : 0);
+ advance += FillTextEmphasisGlyphsForRun(
+ word_result->runs_[i].get(), run_info.run,
+ run_info.run.CharactersLength(), run_info.run.Direction(),
+ run_info.from, run_info.to, emphasis_data, advance, resolved_offset);
+ }
+ word_offset += word_result->NumCharacters() * (run_info.run.Rtl() ? -1 : 1);
+ }
+}
+
+void ShapeResultBloberizer::FillTextEmphasisGlyphs(const StringView& text,
+ TextDirection direction,
+ unsigned from,
+ unsigned to,
+ const GlyphData& emphasis,
+ const ShapeResult* result) {
+ float advance = 0;
+ unsigned offset = 0;
+
+ for (unsigned i = 0; i < result->runs_.size(); i++) {
+ advance += FillTextEmphasisGlyphsForRun(result->runs_[i].get(), text,
+ text.length(), direction, from, to,
+ emphasis, advance, offset);
+ }
+}
+
+namespace {
+
+template <typename TextContainerType>
+inline bool IsSkipInkException(const ShapeResultBloberizer& bloberizer,
+ const TextContainerType& text,
+ unsigned character_index) {
+ // We want to skip descenders in general, but it is undesirable renderings for
+ // CJK characters.
+ return bloberizer.GetType() == ShapeResultBloberizer::Type::kTextIntercepts &&
+ !Character::CanTextDecorationSkipInk(
+ text.CodepointAt(character_index));
+}
+
+template <typename TextContainerType>
+inline void AddGlyphToBloberizer(ShapeResultBloberizer& bloberizer,
+ float advance,
+ hb_direction_t direction,
+ CanvasRotationInVertical canvas_rotation,
+ const SimpleFontData* font_data,
+ const HarfBuzzRunGlyphData& glyph_data,
+ const TextContainerType& text,
+ unsigned character_index) {
+ FloatPoint start_offset = HB_DIRECTION_IS_HORIZONTAL(direction)
+ ? FloatPoint(advance, 0)
+ : FloatPoint(0, advance);
+ if (!IsSkipInkException(bloberizer, text, character_index)) {
+ bloberizer.Add(glyph_data.glyph, font_data, canvas_rotation,
+ start_offset + glyph_data.offset);
+ }
+}
+
+inline void AddEmphasisMark(ShapeResultBloberizer& bloberizer,
+ const GlyphData& emphasis_data,
+ CanvasRotationInVertical canvas_rotation,
+ FloatPoint glyph_center,
+ float mid_glyph_offset) {
+ const SimpleFontData* emphasis_font_data = emphasis_data.font_data;
+ DCHECK(emphasis_font_data);
+
+ bool is_vertical =
+ emphasis_font_data->PlatformData().IsVerticalAnyUpright() &&
+ emphasis_data.canvas_rotation ==
+ CanvasRotationInVertical::kRotateCanvasUpright;
+
+ if (!is_vertical) {
+ bloberizer.Add(emphasis_data.glyph, emphasis_font_data,
+ CanvasRotationInVertical::kRegular,
+ mid_glyph_offset - glyph_center.X());
+ } else {
+ bloberizer.Add(
+ emphasis_data.glyph, emphasis_font_data,
+ CanvasRotationInVertical::kRotateCanvasUpright,
+ FloatPoint(-glyph_center.X(), mid_glyph_offset - glyph_center.Y()));
+ }
+}
+
+inline unsigned CountGraphemesInCluster(const UChar* str,
+ unsigned str_length,
+ uint16_t start_index,
+ uint16_t end_index) {
+ if (start_index > end_index) {
+ uint16_t temp_index = start_index;
+ start_index = end_index;
+ end_index = temp_index;
+ }
+ uint16_t length = end_index - start_index;
+ DCHECK_LE(static_cast<unsigned>(start_index + length), str_length);
+ TextBreakIterator* cursor_pos_iterator =
+ CursorMovementIterator(&str[start_index], length);
+
+ int cursor_pos = cursor_pos_iterator->current();
+ int num_graphemes = -1;
+ while (0 <= cursor_pos) {
+ cursor_pos = cursor_pos_iterator->next();
+ num_graphemes++;
+ }
+ return std::max(0, num_graphemes);
+}
+
+} // namespace
+
+template <typename TextContainerType>
+float ShapeResultBloberizer::FillGlyphsForResult(const ShapeResult* result,
+ const TextContainerType& text,
+ unsigned from,
+ unsigned to,
+ float initial_advance,
+ unsigned run_offset) {
+ auto total_advance = initial_advance;
+
+ for (const auto& run : result->runs_) {
+ total_advance = run->ForEachGlyphInRange(
+ total_advance, from, to, run_offset,
+ [&](const HarfBuzzRunGlyphData& glyph_data, float total_advance,
+ uint16_t character_index) -> bool {
+
+ AddGlyphToBloberizer(*this, total_advance, run->direction_,
+ run->canvas_rotation_, run->font_data_.get(),
+ glyph_data, text, character_index);
+ return true;
+ });
+ }
+
+ return total_advance;
+}
+
+bool ShapeResultBloberizer::CanUseFastPath(unsigned from,
+ unsigned to,
+ unsigned length,
+ bool has_vertical_offsets) {
+ return !from && to == length && !has_vertical_offsets &&
+ GetType() != ShapeResultBloberizer::Type::kTextIntercepts;
+}
+
+bool ShapeResultBloberizer::CanUseFastPath(unsigned from,
+ unsigned to,
+ const ShapeResult* shape_result) {
+ return from <= shape_result->StartIndexForResult() &&
+ to >= shape_result->EndIndexForResult() &&
+ !shape_result->HasVerticalOffsets() &&
+ GetType() != ShapeResultBloberizer::Type::kTextIntercepts;
+}
+
+float ShapeResultBloberizer::FillFastHorizontalGlyphs(
+ const ShapeResultBuffer& result_buffer,
+ TextDirection text_direction) {
+ DCHECK(!result_buffer.HasVerticalOffsets());
+ DCHECK_NE(GetType(), ShapeResultBloberizer::Type::kTextIntercepts);
+
+ float advance = 0;
+ auto results = result_buffer.results_;
+
+ for (unsigned i = 0; i < results.size(); ++i) {
+ const auto& word_result =
+ IsLtr(text_direction) ? results[i] : results[results.size() - 1 - i];
+ advance = FillFastHorizontalGlyphs(word_result.get(), advance);
+ }
+
+ return advance;
+}
+
+float ShapeResultBloberizer::FillFastHorizontalGlyphs(
+ const ShapeResult* shape_result,
+ float advance) {
+ DCHECK(!shape_result->HasVerticalOffsets());
+ DCHECK_NE(GetType(), ShapeResultBloberizer::Type::kTextIntercepts);
+
+ for (const auto& run : shape_result->runs_) {
+ DCHECK(run);
+ DCHECK(HB_DIRECTION_IS_HORIZONTAL(run->direction_));
+
+ advance =
+ run->ForEachGlyph(advance,
+ [&](const HarfBuzzRunGlyphData& glyph_data,
+ float total_advance) -> bool {
+ DCHECK(!glyph_data.offset.Height());
+ Add(glyph_data.glyph, run->font_data_.get(),
+ run->CanvasRotation(),
+ total_advance + glyph_data.offset.Width());
+ return true;
+ });
+ }
+
+ return advance;
+}
+
+template <typename TextContainerType>
+float ShapeResultBloberizer::FillTextEmphasisGlyphsForRun(
+ const ShapeResult::RunInfo* run,
+ const TextContainerType& text,
+ unsigned text_length,
+ TextDirection direction,
+ unsigned from,
+ unsigned to,
+ const GlyphData& emphasis_data,
+ float initial_advance,
+ unsigned run_offset) {
+ if (!run)
+ return 0;
+
+ unsigned graphemes_in_cluster = 1;
+ float cluster_advance = 0;
+
+ FloatPoint glyph_center =
+ emphasis_data.font_data->BoundsForGlyph(emphasis_data.glyph).Center();
+
+ // A "cluster" in this context means a cluster as it is used by HarfBuzz:
+ // The minimal group of characters and corresponding glyphs, that cannot be
+ // broken down further from a text shaping point of view. A cluster can
+ // contain multiple glyphs and grapheme clusters, with mutually overlapping
+ // boundaries. Below we count grapheme clusters per HarfBuzz clusters, then
+ // linearly split the sum of corresponding glyph advances by the number of
+ // grapheme clusters in order to find positions for emphasis mark drawing.
+ uint16_t cluster_start = static_cast<uint16_t>(
+ direction == TextDirection::kRtl
+ ? run->start_index_ + run->num_characters_ + run_offset
+ : run->GlyphToCharacterIndex(0) + run_offset);
+
+ float advance_so_far = initial_advance;
+ const unsigned num_glyphs = run->glyph_data_.size();
+ for (unsigned i = 0; i < num_glyphs; ++i) {
+ const HarfBuzzRunGlyphData& glyph_data = run->glyph_data_[i];
+ uint16_t current_character_index =
+ run->start_index_ + glyph_data.character_index + run_offset;
+ bool is_run_end = (i + 1 == num_glyphs);
+ bool is_cluster_end =
+ is_run_end || (run->GlyphToCharacterIndex(i + 1) + run_offset !=
+ current_character_index);
+
+ if ((direction == TextDirection::kRtl && current_character_index >= to) ||
+ (direction != TextDirection::kRtl && current_character_index < from)) {
+ advance_so_far += glyph_data.advance;
+ direction == TextDirection::kRtl ? --cluster_start : ++cluster_start;
+ continue;
+ }
+
+ cluster_advance += glyph_data.advance;
+
+ if (text.Is8Bit()) {
+ float glyph_advance_x = glyph_data.advance;
+ if (Character::CanReceiveTextEmphasis(text[current_character_index])) {
+ AddEmphasisMark(*this, emphasis_data, run->CanvasRotation(),
+ glyph_center, advance_so_far + glyph_advance_x / 2);
+ }
+ advance_so_far += glyph_advance_x;
+ } else if (is_cluster_end) {
+ uint16_t cluster_end;
+ if (direction == TextDirection::kRtl) {
+ cluster_end = current_character_index;
+ } else {
+ cluster_end = static_cast<uint16_t>(
+ is_run_end ? run->start_index_ + run->num_characters_ + run_offset
+ : run->GlyphToCharacterIndex(i + 1) + run_offset);
+ }
+ graphemes_in_cluster = CountGraphemesInCluster(
+ text.Characters16(), text_length, cluster_start, cluster_end);
+ if (!graphemes_in_cluster || !cluster_advance)
+ continue;
+
+ float glyph_advance_x = cluster_advance / graphemes_in_cluster;
+ for (unsigned j = 0; j < graphemes_in_cluster; ++j) {
+ // Do not put emphasis marks on space, separator, and control
+ // characters.
+ if (Character::CanReceiveTextEmphasis(text[current_character_index])) {
+ AddEmphasisMark(*this, emphasis_data, run->CanvasRotation(),
+ glyph_center, advance_so_far + glyph_advance_x / 2);
+ }
+ advance_so_far += glyph_advance_x;
+ }
+ cluster_start = cluster_end;
+ cluster_advance = 0;
+ }
+ }
+ return advance_so_far - initial_advance;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer.h b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer.h
new file mode 100644
index 00000000000..320e7305dfa
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer.h
@@ -0,0 +1,170 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_SHAPE_RESULT_BLOBERIZER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_SHAPE_RESULT_BLOBERIZER_H_
+
+#include "third_party/blink/renderer/platform/fonts/canvas_rotation_in_vertical.h"
+#include "third_party/blink/renderer/platform/fonts/glyph.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_buffer.h"
+#include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
+#include "third_party/blink/renderer/platform/geometry/float_point.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_text_blob.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_typeface.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "third_party/skia/include/core/SkTextBlob.h"
+
+namespace blink {
+
+class Font;
+
+class PLATFORM_EXPORT ShapeResultBloberizer {
+ WTF_MAKE_NONCOPYABLE(ShapeResultBloberizer);
+ STACK_ALLOCATED();
+
+ public:
+ enum class Type { kNormal, kTextIntercepts };
+
+ ShapeResultBloberizer(const Font&,
+ float device_scale_factor,
+ Type = Type::kNormal);
+
+ Type GetType() const { return type_; }
+
+ float FillGlyphs(const TextRunPaintInfo&, const ShapeResultBuffer&);
+ float FillGlyphs(const StringView&,
+ unsigned from,
+ unsigned to,
+ const ShapeResult*);
+ void FillTextEmphasisGlyphs(const TextRunPaintInfo&,
+ const GlyphData& emphasis_data,
+ const ShapeResultBuffer&);
+ void FillTextEmphasisGlyphs(const StringView&,
+ TextDirection,
+ unsigned from,
+ unsigned to,
+ const GlyphData& emphasis_data,
+ const ShapeResult*);
+
+ void Add(Glyph glyph,
+ const SimpleFontData* font_data,
+ CanvasRotationInVertical canvas_rotation,
+ float h_offset) {
+ // cannot mix x-only/xy offsets
+ DCHECK(!HasPendingVerticalOffsets());
+
+ if (UNLIKELY(font_data != pending_font_data_) ||
+ UNLIKELY(canvas_rotation != pending_canvas_rotation_)) {
+ CommitPendingRun();
+ pending_font_data_ = font_data;
+ pending_canvas_rotation_ = canvas_rotation;
+ DCHECK_EQ(canvas_rotation, CanvasRotationInVertical::kRegular);
+ }
+
+ pending_glyphs_.push_back(glyph);
+ pending_offsets_.push_back(h_offset);
+ }
+
+ void Add(Glyph glyph,
+ const SimpleFontData* font_data,
+ CanvasRotationInVertical canvas_rotation,
+ const FloatPoint& offset) {
+ // cannot mix x-only/xy offsets
+ DCHECK(pending_glyphs_.IsEmpty() || HasPendingVerticalOffsets());
+
+ if (UNLIKELY(font_data != pending_font_data_) ||
+ UNLIKELY(canvas_rotation != pending_canvas_rotation_)) {
+ CommitPendingRun();
+ pending_font_data_ = font_data;
+ pending_canvas_rotation_ = canvas_rotation;
+ pending_vertical_baseline_x_offset_ =
+ canvas_rotation == CanvasRotationInVertical::kRegular
+ ? 0
+ : font_data->GetFontMetrics().FloatAscent() -
+ font_data->GetFontMetrics().FloatAscent(
+ kIdeographicBaseline);
+ }
+
+ pending_glyphs_.push_back(glyph);
+ pending_offsets_.push_back(offset.X() +
+ pending_vertical_baseline_x_offset_);
+ pending_offsets_.push_back(offset.Y());
+ }
+
+ struct BlobInfo {
+ BlobInfo(scoped_refptr<PaintTextBlob> b, CanvasRotationInVertical r)
+ : blob(std::move(b)), rotation(r) {}
+ scoped_refptr<PaintTextBlob> blob;
+ CanvasRotationInVertical rotation;
+ };
+
+ using BlobBuffer = Vector<BlobInfo, 16>;
+ const BlobBuffer& Blobs();
+
+ private:
+ friend class ShapeResultBloberizerTestInfo;
+
+ // Where TextContainerType can be either a TextRun or a StringView.
+ // For legacy layout and LayoutNG respectively.
+ template <typename TextContainerType>
+ float FillGlyphsForResult(const ShapeResult*,
+ const TextContainerType&,
+ unsigned from,
+ unsigned to,
+ float initial_advance,
+ unsigned run_offset);
+
+ // Whether the FillFastHorizontalGlyphs can be used. Only applies for full
+ // runs with no vertical offsets and no text intercepts.
+ bool CanUseFastPath(unsigned from,
+ unsigned to,
+ unsigned length,
+ bool has_vertical_offsets);
+ bool CanUseFastPath(unsigned from, unsigned to, const ShapeResult*);
+ float FillFastHorizontalGlyphs(const ShapeResultBuffer&, TextDirection);
+ float FillFastHorizontalGlyphs(const ShapeResult*, float advance = 0);
+
+ template <typename TextContainerType>
+ float FillTextEmphasisGlyphsForRun(const ShapeResult::RunInfo*,
+ const TextContainerType&,
+ unsigned text_length,
+ TextDirection,
+ unsigned from,
+ unsigned to,
+ const GlyphData& emphasis_data,
+ float initial_advance,
+ unsigned run_offset);
+
+ void CommitPendingRun();
+ void CommitPendingBlob();
+
+ bool HasPendingVerticalOffsets() const;
+
+ const Font& font_;
+ const float device_scale_factor_;
+ const Type type_;
+
+ // Current text blob state.
+ PaintTextBlobBuilder builder_;
+ CanvasRotationInVertical builder_rotation_ =
+ CanvasRotationInVertical::kRegular;
+ size_t builder_run_count_ = 0;
+
+ // Current run state.
+ const SimpleFontData* pending_font_data_ = nullptr;
+ CanvasRotationInVertical pending_canvas_rotation_ =
+ CanvasRotationInVertical::kRegular;
+ Vector<Glyph, 1024> pending_glyphs_;
+ Vector<float, 1024> pending_offsets_;
+ float pending_vertical_baseline_x_offset_ = 0;
+
+ // Constructed blobs.
+ BlobBuffer blobs_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_SHAPE_RESULT_BLOBERIZER_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer_test.cc b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer_test.cc
new file mode 100644
index 00000000000..25c450376ca
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer_test.cc
@@ -0,0 +1,356 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer.h"
+
+#include <memory>
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/fonts/character_range.h"
+#include "third_party/blink/renderer/platform/fonts/font.h"
+#include "third_party/blink/renderer/platform/fonts/opentype/open_type_vertical_data.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_test_info.h"
+#include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_typeface.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+
+namespace blink {
+
+namespace {
+
+// Creating minimal test SimpleFontData objects,
+// the font won't have any glyphs, but that's okay.
+static scoped_refptr<SimpleFontData> CreateTestSimpleFontData(
+ bool force_rotation = false) {
+ FontPlatformData platform_data(
+ PaintTypeface::FromSkTypeface(SkTypeface::MakeDefault()), CString(), 10,
+ false, false,
+ force_rotation ? FontOrientation::kVerticalUpright
+ : FontOrientation::kHorizontal);
+ return SimpleFontData::Create(platform_data, nullptr);
+}
+
+class ShapeResultBloberizerTest : public testing::Test {
+ protected:
+ void SetUp() override {
+ font_description.SetComputedSize(12.0);
+ font_description.SetLocale(LayoutLocale::Get("en"));
+ ASSERT_EQ(USCRIPT_LATIN, font_description.GetScript());
+ font_description.SetGenericFamily(FontDescription::kStandardFamily);
+
+ font = Font(font_description);
+ font.Update(nullptr);
+ ASSERT_TRUE(font.CanShapeWordByWord());
+ fallback_fonts = nullptr;
+ cache = std::make_unique<ShapeCache>();
+ }
+
+ FontCachePurgePreventer font_cache_purge_preventer;
+ FontDescription font_description;
+ Font font;
+ std::unique_ptr<ShapeCache> cache;
+ HashSet<const SimpleFontData*>* fallback_fonts;
+ unsigned start_index = 0;
+ unsigned num_glyphs = 0;
+ hb_script_t script = HB_SCRIPT_INVALID;
+};
+
+} // anonymous namespace
+
+TEST_F(ShapeResultBloberizerTest, StartsEmpty) {
+ Font font;
+ ShapeResultBloberizer bloberizer(font, 1);
+
+ EXPECT_EQ(ShapeResultBloberizerTestInfo::PendingRunFontData(bloberizer),
+ nullptr);
+ EXPECT_EQ(ShapeResultBloberizerTestInfo::PendingRunGlyphs(bloberizer).size(),
+ 0ul);
+ EXPECT_EQ(ShapeResultBloberizerTestInfo::PendingRunOffsets(bloberizer).size(),
+ 0ul);
+ EXPECT_FALSE(
+ ShapeResultBloberizerTestInfo::HasPendingRunVerticalOffsets(bloberizer));
+ EXPECT_EQ(ShapeResultBloberizerTestInfo::PendingBlobRunCount(bloberizer),
+ 0ul);
+ EXPECT_EQ(ShapeResultBloberizerTestInfo::CommittedBlobCount(bloberizer), 0ul);
+
+ EXPECT_TRUE(bloberizer.Blobs().IsEmpty());
+}
+
+TEST_F(ShapeResultBloberizerTest, StoresGlyphsOffsets) {
+ Font font;
+ ShapeResultBloberizer bloberizer(font, 1);
+
+ scoped_refptr<SimpleFontData> font1 = CreateTestSimpleFontData();
+ scoped_refptr<SimpleFontData> font2 = CreateTestSimpleFontData();
+
+ // 2 pending glyphs
+ bloberizer.Add(42, font1.get(), CanvasRotationInVertical::kRegular, 10);
+ bloberizer.Add(43, font1.get(), CanvasRotationInVertical::kRegular, 15);
+
+ EXPECT_EQ(ShapeResultBloberizerTestInfo::PendingRunFontData(bloberizer),
+ font1.get());
+ EXPECT_FALSE(
+ ShapeResultBloberizerTestInfo::HasPendingRunVerticalOffsets(bloberizer));
+ {
+ const auto& glyphs =
+ ShapeResultBloberizerTestInfo::PendingRunGlyphs(bloberizer);
+ EXPECT_EQ(glyphs.size(), 2ul);
+ EXPECT_EQ(42, glyphs[0]);
+ EXPECT_EQ(43, glyphs[1]);
+
+ const auto& offsets =
+ ShapeResultBloberizerTestInfo::PendingRunOffsets(bloberizer);
+ EXPECT_EQ(offsets.size(), 2ul);
+ EXPECT_EQ(10, offsets[0]);
+ EXPECT_EQ(15, offsets[1]);
+ }
+
+ EXPECT_EQ(ShapeResultBloberizerTestInfo::PendingBlobRunCount(bloberizer),
+ 0ul);
+ EXPECT_EQ(ShapeResultBloberizerTestInfo::CommittedBlobCount(bloberizer), 0ul);
+
+ // one more glyph, different font => pending run flush
+ bloberizer.Add(44, font2.get(), CanvasRotationInVertical::kRegular, 12);
+
+ EXPECT_EQ(ShapeResultBloberizerTestInfo::PendingRunFontData(bloberizer),
+ font2.get());
+ EXPECT_FALSE(
+ ShapeResultBloberizerTestInfo::HasPendingRunVerticalOffsets(bloberizer));
+ {
+ const auto& glyphs =
+ ShapeResultBloberizerTestInfo::PendingRunGlyphs(bloberizer);
+ EXPECT_EQ(glyphs.size(), 1ul);
+ EXPECT_EQ(44, glyphs[0]);
+
+ const auto& offsets =
+ ShapeResultBloberizerTestInfo::PendingRunOffsets(bloberizer);
+ EXPECT_EQ(offsets.size(), 1ul);
+ EXPECT_EQ(12, offsets[0]);
+ }
+
+ EXPECT_EQ(ShapeResultBloberizerTestInfo::PendingBlobRunCount(bloberizer),
+ 1ul);
+ EXPECT_EQ(ShapeResultBloberizerTestInfo::CommittedBlobCount(bloberizer), 0ul);
+
+ // flush everything (1 blob w/ 2 runs)
+ EXPECT_EQ(bloberizer.Blobs().size(), 1ul);
+}
+
+TEST_F(ShapeResultBloberizerTest, StoresGlyphsVerticalOffsets) {
+ Font font;
+ ShapeResultBloberizer bloberizer(font, 1);
+
+ scoped_refptr<SimpleFontData> font1 = CreateTestSimpleFontData();
+ scoped_refptr<SimpleFontData> font2 = CreateTestSimpleFontData();
+
+ // 2 pending glyphs
+ bloberizer.Add(42, font1.get(), CanvasRotationInVertical::kRegular,
+ FloatPoint(10, 0));
+ bloberizer.Add(43, font1.get(), CanvasRotationInVertical::kRegular,
+ FloatPoint(15, 0));
+
+ EXPECT_EQ(ShapeResultBloberizerTestInfo::PendingRunFontData(bloberizer),
+ font1.get());
+ EXPECT_TRUE(
+ ShapeResultBloberizerTestInfo::HasPendingRunVerticalOffsets(bloberizer));
+ {
+ const auto& glyphs =
+ ShapeResultBloberizerTestInfo::PendingRunGlyphs(bloberizer);
+ EXPECT_EQ(glyphs.size(), 2ul);
+ EXPECT_EQ(42, glyphs[0]);
+ EXPECT_EQ(43, glyphs[1]);
+
+ const auto& offsets =
+ ShapeResultBloberizerTestInfo::PendingRunOffsets(bloberizer);
+ EXPECT_EQ(offsets.size(), 4ul);
+ EXPECT_EQ(10, offsets[0]);
+ EXPECT_EQ(0, offsets[1]);
+ EXPECT_EQ(15, offsets[2]);
+ EXPECT_EQ(0, offsets[3]);
+ }
+
+ EXPECT_EQ(ShapeResultBloberizerTestInfo::PendingBlobRunCount(bloberizer),
+ 0ul);
+ EXPECT_EQ(ShapeResultBloberizerTestInfo::CommittedBlobCount(bloberizer), 0ul);
+
+ // one more glyph, different font => pending run flush
+ bloberizer.Add(44, font2.get(), CanvasRotationInVertical::kRegular,
+ FloatPoint(12, 2));
+
+ EXPECT_EQ(ShapeResultBloberizerTestInfo::PendingRunFontData(bloberizer),
+ font2.get());
+ EXPECT_TRUE(
+ ShapeResultBloberizerTestInfo::HasPendingRunVerticalOffsets(bloberizer));
+ {
+ const auto& glyphs =
+ ShapeResultBloberizerTestInfo::PendingRunGlyphs(bloberizer);
+ EXPECT_EQ(glyphs.size(), 1ul);
+ EXPECT_EQ(44, glyphs[0]);
+
+ const auto& offsets =
+ ShapeResultBloberizerTestInfo::PendingRunOffsets(bloberizer);
+ EXPECT_EQ(offsets.size(), 2ul);
+ EXPECT_EQ(12, offsets[0]);
+ EXPECT_EQ(2, offsets[1]);
+ }
+
+ EXPECT_EQ(ShapeResultBloberizerTestInfo::PendingBlobRunCount(bloberizer),
+ 1ul);
+ EXPECT_EQ(ShapeResultBloberizerTestInfo::CommittedBlobCount(bloberizer), 0ul);
+
+ // flush everything (1 blob w/ 2 runs)
+ EXPECT_EQ(bloberizer.Blobs().size(), 1ul);
+}
+
+TEST_F(ShapeResultBloberizerTest, MixedBlobRotation) {
+ Font font;
+ ShapeResultBloberizer bloberizer(font, 1);
+
+ scoped_refptr<SimpleFontData> test_font = CreateTestSimpleFontData();
+
+ struct {
+ CanvasRotationInVertical canvas_rotation;
+ size_t expected_pending_glyphs;
+ size_t expected_pending_runs;
+ size_t expected_committed_blobs;
+ } append_ops[] = {
+ // append 2 horizontal glyphs -> these go into the pending glyph buffer
+ {CanvasRotationInVertical::kRegular, 1u, 0u, 0u},
+ {CanvasRotationInVertical::kRegular, 2u, 0u, 0u},
+
+ // append 3 vertical rotated glyphs -> push the prev pending (horizontal)
+ // glyphs into a new run in the current (horizontal) blob
+ {CanvasRotationInVertical::kRotateCanvasUpright, 1u, 1u, 0u},
+ {CanvasRotationInVertical::kRotateCanvasUpright, 2u, 1u, 0u},
+ {CanvasRotationInVertical::kRotateCanvasUpright, 3u, 1u, 0u},
+
+ // append 2 more horizontal glyphs -> flush the current (horizontal) blob,
+ // push prev (vertical) pending glyphs into new vertical blob run
+ {CanvasRotationInVertical::kRegular, 1u, 1u, 1u},
+ {CanvasRotationInVertical::kRegular, 2u, 1u, 1u},
+
+ // append 1 more vertical glyph -> flush current (vertical) blob, push
+ // prev (horizontal) pending glyphs into a new horizontal blob run
+ {CanvasRotationInVertical::kRotateCanvasUpright, 1u, 1u, 2u},
+ };
+
+ for (const auto& op : append_ops) {
+ bloberizer.Add(42, test_font.get(), op.canvas_rotation, FloatPoint());
+ EXPECT_EQ(
+ op.expected_pending_glyphs,
+ ShapeResultBloberizerTestInfo::PendingRunGlyphs(bloberizer).size());
+ EXPECT_EQ(op.canvas_rotation,
+ ShapeResultBloberizerTestInfo::PendingBlobRotation(bloberizer));
+ EXPECT_EQ(op.expected_pending_runs,
+ ShapeResultBloberizerTestInfo::PendingBlobRunCount(bloberizer));
+ EXPECT_EQ(op.expected_committed_blobs,
+ ShapeResultBloberizerTestInfo::CommittedBlobCount(bloberizer));
+ }
+
+ // flush everything -> 4 blobs total
+ EXPECT_EQ(4u, bloberizer.Blobs().size());
+}
+
+// Tests that filling a glyph buffer for a specific range returns the same
+// results when shaping word by word as when shaping the full run in one go.
+TEST_F(ShapeResultBloberizerTest, CommonAccentLeftToRightFillGlyphBuffer) {
+ // "/. ." with an accent mark over the first dot.
+ const UChar kStr[] = {0x2F, 0x301, 0x2E, 0x20, 0x2E, 0x0};
+ TextRun text_run(kStr, 5);
+ TextRunPaintInfo run_info(text_run);
+ run_info.to = 3;
+
+ ShapeResultBloberizer bloberizer(font, 1);
+ CachingWordShaper word_shaper(font);
+ ShapeResultBuffer buffer;
+ word_shaper.FillResultBuffer(run_info, &buffer);
+ bloberizer.FillGlyphs(run_info, buffer);
+
+ Font reference_font(font_description);
+ reference_font.Update(nullptr);
+ reference_font.SetCanShapeWordByWordForTesting(false);
+
+ ShapeResultBloberizer reference_bloberizer(reference_font, 1);
+ CachingWordShaper reference_word_shaper(font);
+ ShapeResultBuffer reference_buffer;
+ reference_word_shaper.FillResultBuffer(run_info, &reference_buffer);
+ reference_bloberizer.FillGlyphs(run_info, reference_buffer);
+
+ const auto& glyphs =
+ ShapeResultBloberizerTestInfo::PendingRunGlyphs(bloberizer);
+ ASSERT_EQ(glyphs.size(), 3ul);
+ const auto reference_glyphs =
+ ShapeResultBloberizerTestInfo::PendingRunGlyphs(reference_bloberizer);
+ ASSERT_EQ(reference_glyphs.size(), 3ul);
+
+ EXPECT_EQ(reference_glyphs[0], glyphs[0]);
+ EXPECT_EQ(reference_glyphs[1], glyphs[1]);
+ EXPECT_EQ(reference_glyphs[2], glyphs[2]);
+}
+
+// Tests that filling a glyph buffer for a specific range returns the same
+// results when shaping word by word as when shaping the full run in one go.
+TEST_F(ShapeResultBloberizerTest, CommonAccentRightToLeftFillGlyphBuffer) {
+ // "[] []" with an accent mark over the last square bracket.
+ const UChar kStr[] = {0x5B, 0x5D, 0x20, 0x5B, 0x301, 0x5D, 0x0};
+ TextRun text_run(kStr, 6);
+ text_run.SetDirection(TextDirection::kRtl);
+ TextRunPaintInfo run_info(text_run);
+ run_info.from = 1;
+
+ ShapeResultBloberizer bloberizer(font, 1);
+ CachingWordShaper word_shaper(font);
+ ShapeResultBuffer buffer;
+ word_shaper.FillResultBuffer(run_info, &buffer);
+ bloberizer.FillGlyphs(run_info, buffer);
+
+ Font reference_font(font_description);
+ reference_font.Update(nullptr);
+ reference_font.SetCanShapeWordByWordForTesting(false);
+
+ ShapeResultBloberizer reference_bloberizer(reference_font, 1);
+ CachingWordShaper reference_word_shaper(font);
+ ShapeResultBuffer reference_buffer;
+ reference_word_shaper.FillResultBuffer(run_info, &reference_buffer);
+ reference_bloberizer.FillGlyphs(run_info, reference_buffer);
+
+ const auto& glyphs =
+ ShapeResultBloberizerTestInfo::PendingRunGlyphs(bloberizer);
+ ASSERT_EQ(5u, glyphs.size());
+ const auto reference_glyphs =
+ ShapeResultBloberizerTestInfo::PendingRunGlyphs(reference_bloberizer);
+ ASSERT_EQ(5u, reference_glyphs.size());
+
+ EXPECT_EQ(reference_glyphs[0], glyphs[0]);
+ EXPECT_EQ(reference_glyphs[1], glyphs[1]);
+ EXPECT_EQ(reference_glyphs[2], glyphs[2]);
+ EXPECT_EQ(reference_glyphs[3], glyphs[3]);
+ EXPECT_EQ(reference_glyphs[4], glyphs[4]);
+}
+
+// Tests that runs with zero glyphs (the ZWJ non-printable character in this
+// case) are handled correctly. This test passes if it does not cause a crash.
+TEST_F(ShapeResultBloberizerTest, SubRunWithZeroGlyphs) {
+ // "Foo &zwnj; bar"
+ const UChar kStr[] = {0x46, 0x6F, 0x6F, 0x20, 0x200C,
+ 0x20, 0x62, 0x61, 0x71, 0x0};
+ TextRun text_run(kStr, 9);
+
+ CachingWordShaper shaper(font);
+ FloatRect glyph_bounds;
+ ASSERT_GT(shaper.Width(text_run, nullptr, &glyph_bounds), 0);
+
+ ShapeResultBloberizer bloberizer(font, 1);
+ TextRunPaintInfo run_info(text_run);
+ run_info.to = 8;
+
+ CachingWordShaper word_shaper(font);
+ ShapeResultBuffer buffer;
+ word_shaper.FillResultBuffer(run_info, &buffer);
+ bloberizer.FillGlyphs(run_info, buffer);
+
+ shaper.GetCharacterRange(text_run, 0, 8);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_buffer.cc b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_buffer.cc
new file mode 100644
index 00000000000..23511eaa6c2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_buffer.cc
@@ -0,0 +1,235 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_buffer.h"
+
+#include "third_party/blink/renderer/platform/fonts/character_range.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_inline_headers.h"
+#include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
+#include "third_party/blink/renderer/platform/geometry/float_point.h"
+#include "third_party/blink/renderer/platform/text/text_direction.h"
+
+namespace blink {
+
+// TODO(eae): This is a bit of a hack to allow reuse of the implementation
+// for both ShapeResultBuffer and single ShapeResult use cases. Ideally the
+// logic should move into ShapeResult itself and then the ShapeResultBuffer
+// implementation may wrap that.
+CharacterRange ShapeResultBuffer::GetCharacterRange(
+ scoped_refptr<const ShapeResult> result,
+ TextDirection direction,
+ float total_width,
+ unsigned from,
+ unsigned to) {
+ Vector<scoped_refptr<const ShapeResult>, 64> results;
+ results.push_back(result);
+ return GetCharacterRangeInternal(results, direction, total_width, from, to);
+}
+
+CharacterRange ShapeResultBuffer::GetCharacterRangeInternal(
+ const Vector<scoped_refptr<const ShapeResult>, 64>& results,
+ TextDirection direction,
+ float total_width,
+ unsigned absolute_from,
+ unsigned absolute_to) {
+ float current_x = 0;
+ float from_x = 0;
+ float to_x = 0;
+ bool found_from_x = false;
+ bool found_to_x = false;
+ float min_y = 0;
+ float max_y = 0;
+
+ if (direction == TextDirection::kRtl)
+ current_x = total_width;
+
+ // The absoluteFrom and absoluteTo arguments represent the start/end offset
+ // for the entire run, from/to are continuously updated to be relative to
+ // the current word (ShapeResult instance).
+ int from = absolute_from;
+ int to = absolute_to;
+
+ unsigned total_num_characters = 0;
+ for (unsigned j = 0; j < results.size(); j++) {
+ const scoped_refptr<const ShapeResult> result = results[j];
+ if (direction == TextDirection::kRtl) {
+ // Convert logical offsets to visual offsets, because results are in
+ // logical order while runs are in visual order.
+ if (!found_from_x && from >= 0 &&
+ static_cast<unsigned>(from) < result->NumCharacters())
+ from = result->NumCharacters() - from - 1;
+ if (!found_to_x && to >= 0 &&
+ static_cast<unsigned>(to) < result->NumCharacters())
+ to = result->NumCharacters() - to - 1;
+ current_x -= result->Width();
+ }
+ for (unsigned i = 0; i < result->runs_.size(); i++) {
+ if (!result->runs_[i])
+ continue;
+ DCHECK_EQ(direction == TextDirection::kRtl, result->runs_[i]->Rtl());
+ int num_characters = result->runs_[i]->num_characters_;
+ if (!found_from_x && from >= 0 && from < num_characters) {
+ from_x = result->runs_[i]->XPositionForVisualOffset(
+ from, AdjustMidCluster::kToStart) +
+ current_x;
+ found_from_x = true;
+ } else {
+ from -= num_characters;
+ }
+
+ if (!found_to_x && to >= 0 && to < num_characters) {
+ to_x = result->runs_[i]->XPositionForVisualOffset(
+ to, AdjustMidCluster::kToEnd) +
+ current_x;
+ found_to_x = true;
+ } else {
+ to -= num_characters;
+ }
+
+ if (found_from_x || found_to_x) {
+ min_y = std::min(min_y, result->Bounds().Y());
+ max_y = std::max(max_y, result->Bounds().MaxY());
+ }
+
+ if (found_from_x && found_to_x)
+ break;
+ current_x += result->runs_[i]->width_;
+ }
+ if (direction == TextDirection::kRtl)
+ current_x -= result->Width();
+ total_num_characters += result->NumCharacters();
+ }
+
+ // The position in question might be just after the text.
+ if (!found_from_x && absolute_from == total_num_characters) {
+ from_x = direction == TextDirection::kRtl ? 0 : total_width;
+ found_from_x = true;
+ }
+ if (!found_to_x && absolute_to == total_num_characters) {
+ to_x = direction == TextDirection::kRtl ? 0 : total_width;
+ found_to_x = true;
+ }
+ if (!found_from_x)
+ from_x = 0;
+ if (!found_to_x)
+ to_x = direction == TextDirection::kRtl ? 0 : total_width;
+
+ // None of our runs is part of the selection, possibly invalid arguments.
+ if (!found_to_x && !found_from_x)
+ from_x = to_x = 0;
+ if (from_x < to_x)
+ return CharacterRange(from_x, to_x, -min_y, max_y);
+ return CharacterRange(to_x, from_x, -min_y, max_y);
+}
+
+CharacterRange ShapeResultBuffer::GetCharacterRange(TextDirection direction,
+ float total_width,
+ unsigned from,
+ unsigned to) const {
+ return GetCharacterRangeInternal(results_, direction, total_width, from, to);
+}
+
+void ShapeResultBuffer::AddRunInfoRanges(const ShapeResult::RunInfo& run_info,
+ float offset,
+ Vector<CharacterRange>& ranges) {
+ Vector<float> character_widths(run_info.num_characters_);
+ for (const auto& glyph : run_info.glyph_data_)
+ character_widths[glyph.character_index] += glyph.advance;
+
+ for (unsigned character_index = 0; character_index < run_info.num_characters_;
+ character_index++) {
+ float start = offset;
+ offset += character_widths[character_index];
+ float end = offset;
+
+ // To match getCharacterRange we flip ranges to ensure start <= end.
+ if (end < start)
+ ranges.push_back(CharacterRange(end, start, 0, 0));
+ else
+ ranges.push_back(CharacterRange(start, end, 0, 0));
+ }
+}
+
+Vector<CharacterRange> ShapeResultBuffer::IndividualCharacterRanges(
+ TextDirection direction,
+ float total_width) const {
+ Vector<CharacterRange> ranges;
+ float current_x = direction == TextDirection::kRtl ? total_width : 0;
+ for (const scoped_refptr<const ShapeResult> result : results_) {
+ if (direction == TextDirection::kRtl)
+ current_x -= result->Width();
+ unsigned run_count = result->runs_.size();
+ for (unsigned index = 0; index < run_count; index++) {
+ unsigned run_index =
+ direction == TextDirection::kRtl ? run_count - 1 - index : index;
+ AddRunInfoRanges(*result->runs_[run_index], current_x, ranges);
+ current_x += result->runs_[run_index]->width_;
+ }
+ if (direction == TextDirection::kRtl)
+ current_x -= result->Width();
+ }
+ return ranges;
+}
+
+int ShapeResultBuffer::OffsetForPosition(const TextRun& run,
+ float target_x,
+ bool include_partial_glyphs) const {
+ unsigned total_offset;
+ if (run.Rtl()) {
+ total_offset = run.length();
+ for (unsigned i = results_.size(); i; --i) {
+ const scoped_refptr<const ShapeResult>& word_result = results_[i - 1];
+ if (!word_result)
+ continue;
+ total_offset -= word_result->NumCharacters();
+ if (target_x >= 0 && target_x <= word_result->Width()) {
+ int offset_for_word =
+ word_result->OffsetForPosition(target_x, include_partial_glyphs);
+ return total_offset + offset_for_word;
+ }
+ target_x -= word_result->Width();
+ }
+ } else {
+ total_offset = 0;
+ for (const auto& word_result : results_) {
+ if (!word_result)
+ continue;
+ int offset_for_word =
+ word_result->OffsetForPosition(target_x, include_partial_glyphs);
+ DCHECK_GE(offset_for_word, 0);
+ total_offset += offset_for_word;
+ if (target_x >= 0 && target_x <= word_result->Width())
+ return total_offset;
+ target_x -= word_result->Width();
+ }
+ }
+ return total_offset;
+}
+
+Vector<ShapeResult::RunFontData> ShapeResultBuffer::GetRunFontData() const {
+ Vector<ShapeResult::RunFontData> font_data;
+ for (const auto& result : results_)
+ result->GetRunFontData(&font_data);
+ return font_data;
+}
+
+GlyphData ShapeResultBuffer::EmphasisMarkGlyphData(
+ const FontDescription& font_description) const {
+ for (const auto& result : results_) {
+ for (const auto& run : result->runs_) {
+ DCHECK(run->font_data_);
+ if (run->glyph_data_.IsEmpty())
+ continue;
+
+ return GlyphData(
+ run->glyph_data_[0].glyph,
+ run->font_data_->EmphasisMarkFontData(font_description).get(),
+ run->CanvasRotation());
+ }
+ }
+
+ return GlyphData();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_buffer.h b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_buffer.h
new file mode 100644
index 00000000000..af1b66e9703
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_buffer.h
@@ -0,0 +1,77 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_SHAPE_RESULT_BUFFER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_SHAPE_RESULT_BUFFER_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_result.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+struct CharacterRange;
+class FontDescription;
+struct GlyphData;
+class ShapeResultBloberizer;
+class TextRun;
+
+class PLATFORM_EXPORT ShapeResultBuffer {
+ WTF_MAKE_NONCOPYABLE(ShapeResultBuffer);
+ STACK_ALLOCATED();
+
+ public:
+ ShapeResultBuffer() : has_vertical_offsets_(false) {}
+
+ void AppendResult(scoped_refptr<const ShapeResult> result) {
+ has_vertical_offsets_ |= result->HasVerticalOffsets();
+ results_.push_back(std::move(result));
+ }
+
+ bool HasVerticalOffsets() const { return has_vertical_offsets_; }
+
+ int OffsetForPosition(const TextRun&,
+ float target_x,
+ bool include_partial_glyphs) const;
+ CharacterRange GetCharacterRange(TextDirection,
+ float total_width,
+ unsigned from,
+ unsigned to) const;
+ Vector<CharacterRange> IndividualCharacterRanges(TextDirection,
+ float total_width) const;
+
+ static CharacterRange GetCharacterRange(scoped_refptr<const ShapeResult>,
+ TextDirection,
+ float total_width,
+ unsigned from,
+ unsigned to);
+
+ Vector<ShapeResult::RunFontData> GetRunFontData() const;
+
+ GlyphData EmphasisMarkGlyphData(const FontDescription&) const;
+
+ private:
+ friend class ShapeResultBloberizer;
+ static CharacterRange GetCharacterRangeInternal(
+ const Vector<scoped_refptr<const ShapeResult>, 64>&,
+ TextDirection,
+ float total_width,
+ unsigned from,
+ unsigned to);
+
+ static void AddRunInfoRanges(const ShapeResult::RunInfo&,
+ float offset,
+ Vector<CharacterRange>&);
+
+ // Empirically, cases where we get more than 50 ShapeResults are extremely
+ // rare.
+ Vector<scoped_refptr<const ShapeResult>, 64> results_;
+ bool has_vertical_offsets_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_SHAPE_RESULT_BUFFER_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_inline_headers.h b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_inline_headers.h
new file mode 100644
index 00000000000..d24ea32cf99
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_inline_headers.h
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2012 Google Inc. All rights reserved.
+ * Copyright (C) 2013 BlackBerry Limited. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_SHAPE_RESULT_INLINE_HEADERS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_SHAPE_RESULT_INLINE_HEADERS_H_
+
+#include <hb.h>
+#include <memory>
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_result.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class SimpleFontData;
+
+struct HarfBuzzRunGlyphData {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ uint16_t glyph;
+ uint16_t character_index;
+ float advance;
+ FloatSize offset;
+};
+
+struct ShapeResult::RunInfo {
+ USING_FAST_MALLOC(RunInfo);
+
+ public:
+ RunInfo(const SimpleFontData* font,
+ hb_direction_t dir,
+ CanvasRotationInVertical canvas_rotation,
+ hb_script_t script,
+ unsigned start_index,
+ unsigned num_glyphs,
+ unsigned num_characters)
+ : font_data_(const_cast<SimpleFontData*>(font)),
+ direction_(dir),
+ canvas_rotation_(canvas_rotation),
+ script_(script),
+ glyph_data_(num_glyphs),
+ start_index_(start_index),
+ num_characters_(num_characters),
+ width_(0.0f) {}
+
+ RunInfo(const RunInfo& other)
+ : font_data_(other.font_data_),
+ direction_(other.direction_),
+ canvas_rotation_(other.canvas_rotation_),
+ script_(other.script_),
+ glyph_data_(other.glyph_data_),
+ start_index_(other.start_index_),
+ num_characters_(other.num_characters_),
+ width_(other.width_) {}
+
+ bool Rtl() const { return HB_DIRECTION_IS_BACKWARD(direction_); }
+ bool IsHorizontal() const { return HB_DIRECTION_IS_HORIZONTAL(direction_); }
+ CanvasRotationInVertical CanvasRotation() const { return canvas_rotation_; }
+ unsigned NextSafeToBreakOffset(unsigned) const;
+ unsigned PreviousSafeToBreakOffset(unsigned) const;
+ float XPositionForVisualOffset(unsigned, AdjustMidCluster) const;
+ float XPositionForOffset(unsigned, AdjustMidCluster) const;
+ int CharacterIndexForXPosition(float, bool include_partial_glyphs) const;
+ void SetGlyphAndPositions(unsigned index,
+ uint16_t glyph_id,
+ float advance,
+ float offset_x,
+ float offset_y);
+
+ size_t GlyphToCharacterIndex(size_t i) const {
+ return start_index_ + glyph_data_[i].character_index;
+ }
+
+ // For memory reporting.
+ size_t ByteSize() const {
+ return sizeof(this) + glyph_data_.size() * sizeof(HarfBuzzRunGlyphData);
+ }
+
+ // Creates a new RunInfo instance representing a subset of the current run.
+ std::unique_ptr<RunInfo> CreateSubRun(unsigned start, unsigned end) {
+ DCHECK(end > start);
+ unsigned number_of_characters = std::min(end - start, num_characters_);
+
+ // This ends up looping over the glyphs twice if we don't know the glyph
+ // count up front. Once to count the number of glyphs and allocate the new
+ // RunInfo object and then a second time to copy the glyphs over.
+ // TODO: Compared to the cost of allocation and copying the extra loop is
+ // probably fine but we might want to try to eliminate it if we can.
+ unsigned number_of_glyphs;
+ if (start == 0 && end == num_characters_) {
+ number_of_glyphs = glyph_data_.size();
+ } else {
+ number_of_glyphs = 0;
+ ForEachGlyphInRange(
+ 0, start_index_ + start, start_index_ + end, 0,
+ [&](const HarfBuzzRunGlyphData&, float, uint16_t) -> bool {
+ number_of_glyphs++;
+ return true;
+ });
+ }
+
+ auto run = std::make_unique<RunInfo>(
+ font_data_.get(), direction_, canvas_rotation_, script_,
+ start_index_ + start, number_of_glyphs, number_of_characters);
+
+ unsigned sub_glyph_index = 0;
+ float total_advance = 0;
+ ForEachGlyphInRange(
+ 0, start_index_ + start, start_index_ + end, 0,
+ [&](const HarfBuzzRunGlyphData& glyph_data, float, uint16_t) -> bool {
+ HarfBuzzRunGlyphData& sub_glyph = run->glyph_data_[sub_glyph_index++];
+ sub_glyph.glyph = glyph_data.glyph;
+ sub_glyph.character_index = glyph_data.character_index - start;
+ sub_glyph.advance = glyph_data.advance;
+ sub_glyph.offset = glyph_data.offset;
+ total_advance += glyph_data.advance;
+ return true;
+ });
+
+ run->width_ = total_advance;
+ run->num_characters_ = number_of_characters;
+
+ for (unsigned i = 0; i < safe_break_offsets_.size(); i++) {
+ if (safe_break_offsets_[i] >= start && safe_break_offsets_[i] <= end)
+ run->safe_break_offsets_.push_back(safe_break_offsets_[i] - start);
+ }
+
+ return run;
+ }
+
+ // Iterates over, and applies the functor to all the glyphs in this run.
+ // Also tracks (and returns) a seeded total advance.
+ //
+ // Functor signature:
+ //
+ // bool func(const HarfBuzzRunGlyphData& glyphData, float totalAdvance)
+ //
+ // where the returned bool signals whether iteration should continue (true)
+ // or stop (false).
+ template <typename Func>
+ float ForEachGlyph(float initial_advance, Func func) const {
+ float total_advance = initial_advance;
+
+ for (const auto& glyph_data : glyph_data_) {
+ if (!func(glyph_data, total_advance))
+ break;
+ total_advance += glyph_data.advance;
+ }
+
+ return total_advance;
+ }
+
+ // Same as the above, except it only applies the functor to glyphs in the
+ // specified range, and stops after the range.
+ template <typename Func>
+ float ForEachGlyphInRange(float initial_advance,
+ unsigned from,
+ unsigned to,
+ unsigned index_offset,
+ Func func) const {
+ return ForEachGlyph(
+ initial_advance,
+ [&](const HarfBuzzRunGlyphData& glyph_data,
+ float total_advance) -> bool {
+ const uint16_t character_index =
+ start_index_ + glyph_data.character_index + index_offset;
+
+ if (character_index < from) {
+ // Glyph out-of-range; before the range (and must continue
+ // accumulating advance) in LTR.
+ return !Rtl();
+ }
+
+ if (character_index >= to) {
+ // Glyph out-of-range; before the range (and must continue
+ // accumulating advance) in RTL.
+ return Rtl();
+ }
+
+ // Glyph in range; apply functor.
+ return func(glyph_data, total_advance, character_index);
+ });
+ }
+
+ scoped_refptr<SimpleFontData> font_data_;
+ hb_direction_t direction_;
+ // For upright-in-vertical we need to tell the ShapeResultBloberizer to rotate
+ // the canvas back 90deg for this RunInfo.
+ CanvasRotationInVertical canvas_rotation_;
+ hb_script_t script_;
+ Vector<HarfBuzzRunGlyphData> glyph_data_;
+ // List of character indecies before which it's safe to break without
+ // reshaping.
+ Vector<uint16_t> safe_break_offsets_;
+ unsigned start_index_;
+ unsigned num_characters_;
+ float width_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_SHAPE_RESULT_INLINE_HEADERS_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.cc b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.cc
new file mode 100644
index 00000000000..8e6a111da18
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.cc
@@ -0,0 +1,195 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.h"
+
+#include "third_party/blink/renderer/platform/fonts/font_description.h"
+#include "third_party/blink/renderer/platform/text/text_run.h"
+
+namespace blink {
+
+template <typename TextContainerType>
+ShapeResultSpacing<TextContainerType>::ShapeResultSpacing(
+ const TextContainerType& text)
+ : text_(text),
+ letter_spacing_(0),
+ word_spacing_(0),
+ expansion_(0),
+ expansion_per_opportunity_(0),
+ expansion_opportunity_count_(0),
+ text_justify_(TextJustify::kAuto),
+ has_spacing_(false),
+ normalize_space_(false),
+ allow_tabs_(false),
+ is_after_expansion_(false) {}
+
+template <typename TextContainerType>
+bool ShapeResultSpacing<TextContainerType>::SetSpacing(
+ const FontDescription& font_description) {
+ if (!font_description.LetterSpacing() && !font_description.WordSpacing()) {
+ has_spacing_ = false;
+ return false;
+ }
+
+ letter_spacing_ = font_description.LetterSpacing();
+ word_spacing_ = font_description.WordSpacing();
+ DCHECK(!normalize_space_);
+ allow_tabs_ = true;
+ has_spacing_ = true;
+ return true;
+}
+
+template <typename TextContainerType>
+void ShapeResultSpacing<TextContainerType>::SetExpansion(
+ float expansion,
+ TextDirection direction,
+ TextJustify text_justify,
+ bool allows_leading_expansion,
+ bool allows_trailing_expansion) {
+ DCHECK_GT(expansion, 0);
+ expansion_ = expansion;
+ ComputeExpansion(allows_leading_expansion, allows_trailing_expansion,
+ direction, text_justify);
+ has_spacing_ |= HasExpansion();
+}
+
+template <typename TextContainerType>
+void ShapeResultSpacing<TextContainerType>::SetSpacingAndExpansion(
+ const FontDescription& font_description) {
+ // Available only for TextRun since it has expansion data.
+ NOTREACHED();
+}
+
+template <>
+void ShapeResultSpacing<TextRun>::SetSpacingAndExpansion(
+ const FontDescription& font_description) {
+ letter_spacing_ = font_description.LetterSpacing();
+ word_spacing_ = font_description.WordSpacing();
+ expansion_ = text_.Expansion();
+ has_spacing_ = letter_spacing_ || word_spacing_ || expansion_;
+ if (!has_spacing_)
+ return;
+
+ normalize_space_ = text_.NormalizeSpace();
+ allow_tabs_ = text_.AllowTabs();
+
+ if (expansion_) {
+ ComputeExpansion(text_.AllowsLeadingExpansion(),
+ text_.AllowsTrailingExpansion(), text_.Direction(),
+ text_.GetTextJustify());
+ }
+}
+
+template <typename TextContainerType>
+void ShapeResultSpacing<TextContainerType>::ComputeExpansion(
+ bool allows_leading_expansion,
+ bool allows_trailing_expansion,
+ TextDirection direction,
+ TextJustify text_justify) {
+ DCHECK_GT(expansion_, 0);
+
+ text_justify_ = text_justify;
+ if (text_justify_ == TextJustify::kNone) {
+ expansion_opportunity_count_ = 0;
+ return;
+ }
+
+ is_after_expansion_ = !allows_leading_expansion;
+ bool is_after_expansion = is_after_expansion_;
+ if (text_.Is8Bit()) {
+ expansion_opportunity_count_ = Character::ExpansionOpportunityCount(
+ text_.Characters8(), text_.length(), direction, is_after_expansion,
+ text_justify_);
+ } else {
+ expansion_opportunity_count_ = Character::ExpansionOpportunityCount(
+ text_.Characters16(), text_.length(), direction, is_after_expansion,
+ text_justify_);
+ }
+ if (is_after_expansion && !allows_trailing_expansion) {
+ DCHECK_GT(expansion_opportunity_count_, 0u);
+ --expansion_opportunity_count_;
+ }
+
+ if (expansion_opportunity_count_)
+ expansion_per_opportunity_ = expansion_ / expansion_opportunity_count_;
+}
+
+template <typename TextContainerType>
+float ShapeResultSpacing<TextContainerType>::NextExpansion() {
+ if (!expansion_opportunity_count_) {
+ NOTREACHED();
+ return 0;
+ }
+
+ is_after_expansion_ = true;
+
+ if (!--expansion_opportunity_count_) {
+ float remaining = expansion_;
+ expansion_ = 0;
+ return remaining;
+ }
+
+ expansion_ -= expansion_per_opportunity_;
+ return expansion_per_opportunity_;
+}
+
+template <typename TextContainerType>
+float ShapeResultSpacing<TextContainerType>::ComputeSpacing(unsigned index,
+ float& offset) {
+ DCHECK(has_spacing_);
+ UChar32 character = text_[index];
+ bool treat_as_space =
+ (Character::TreatAsSpace(character) ||
+ (normalize_space_ &&
+ Character::IsNormalizedCanvasSpaceCharacter(character))) &&
+ (character != '\t' || !allow_tabs_);
+ if (treat_as_space && character != kNoBreakSpaceCharacter)
+ character = kSpaceCharacter;
+
+ float spacing = 0;
+ if (letter_spacing_ && !Character::TreatAsZeroWidthSpace(character))
+ spacing += letter_spacing_;
+
+ if (treat_as_space && (index || character == kNoBreakSpaceCharacter))
+ spacing += word_spacing_;
+
+ if (!HasExpansion())
+ return spacing;
+
+ if (treat_as_space)
+ return spacing + NextExpansion();
+
+ if (text_.Is8Bit() || text_justify_ != TextJustify::kAuto)
+ return spacing;
+
+ // isCJKIdeographOrSymbol() has expansion opportunities both before and
+ // after each character.
+ // http://www.w3.org/TR/jlreq/#line_adjustment
+ if (U16_IS_LEAD(character) && index + 1 < text_.length() &&
+ U16_IS_TRAIL(text_[index + 1]))
+ character = U16_GET_SUPPLEMENTARY(character, text_[index + 1]);
+ if (!Character::IsCJKIdeographOrSymbol(character)) {
+ is_after_expansion_ = false;
+ return spacing;
+ }
+
+ if (!is_after_expansion_) {
+ // Take the expansion opportunity before this ideograph.
+ float expand_before = NextExpansion();
+ if (expand_before) {
+ offset += expand_before;
+ spacing += expand_before;
+ }
+ if (!HasExpansion())
+ return spacing;
+ }
+
+ return spacing + NextExpansion();
+}
+
+// Instantiate the template class.
+template class ShapeResultSpacing<TextRun>;
+template class ShapeResultSpacing<String>;
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.h b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.h
new file mode 100644
index 00000000000..391de4222b8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.h
@@ -0,0 +1,80 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_SHAPE_RESULT_SPACING_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_SHAPE_RESULT_SPACING_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/text/character.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+class FontDescription;
+
+// A context object to apply letter-spacing, word-spacing, and justification to
+// ShapeResult.
+template <typename TextContainerType>
+class PLATFORM_EXPORT ShapeResultSpacing final {
+ STACK_ALLOCATED();
+
+ public:
+ explicit ShapeResultSpacing(const TextContainerType&);
+
+ const TextContainerType& Text() const { return text_; }
+ float LetterSpacing() const { return letter_spacing_; }
+ bool HasSpacing() const { return has_spacing_; }
+ bool HasExpansion() const { return expansion_opportunity_count_; }
+
+ // Set letter-spacing and word-spacing.
+ bool SetSpacing(const FontDescription&);
+
+ // Set the expansion for the justification.
+ void SetExpansion(float expansion,
+ TextDirection,
+ TextJustify,
+ bool allows_leading_expansion = false,
+ bool allows_trailing_expansion = false);
+
+ // Set letter-spacing, word-spacing, and justification.
+ // Available only for TextRun.
+ void SetSpacingAndExpansion(const FontDescription&);
+
+ // Compute the sum of all spacings for the specified |index|.
+ // The |index| is for the |TextContainerType| given in the constructor.
+ // For justification, this function must be called incrementally since it
+ // keeps states and counts consumed justification opportunities.
+ float ComputeSpacing(unsigned index, float& offset);
+
+ private:
+ bool IsAfterExpansion() const { return is_after_expansion_; }
+
+ void ComputeExpansion(bool allows_leading_expansion,
+ bool allows_trailing_expansion,
+ TextDirection,
+ TextJustify);
+
+ float NextExpansion();
+
+ const TextContainerType& text_;
+ float letter_spacing_;
+ float word_spacing_;
+ float expansion_;
+ float expansion_per_opportunity_;
+ unsigned expansion_opportunity_count_;
+ TextJustify text_justify_;
+ bool has_spacing_;
+ bool normalize_space_;
+ bool allow_tabs_;
+ bool is_after_expansion_;
+};
+
+// Forward declare so no implicit instantiations happen before the
+// first explicit instantiation (which would be a C++ violation).
+template <>
+void ShapeResultSpacing<TextRun>::SetSpacingAndExpansion(
+ const FontDescription&);
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_test_info.cc b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_test_info.cc
new file mode 100644
index 00000000000..a0bef42a0bf
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_test_info.cc
@@ -0,0 +1,66 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_test_info.h"
+
+#include "third_party/blink/renderer/platform/fonts/font.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_inline_headers.h"
+
+namespace blink {
+
+unsigned ShapeResultTestInfo::NumberOfRunsForTesting() const {
+ return runs_.size();
+}
+
+bool ShapeResultTestInfo::RunInfoForTesting(unsigned run_index,
+ unsigned& start_index,
+ unsigned& num_characters,
+ unsigned& num_glyphs,
+ hb_script_t& script) const {
+ if (run_index < runs_.size() && runs_[run_index]) {
+ start_index = runs_[run_index]->start_index_;
+ num_characters = runs_[run_index]->num_characters_;
+ num_glyphs = runs_[run_index]->glyph_data_.size();
+ script = runs_[run_index]->script_;
+ return true;
+ }
+ return false;
+}
+
+bool ShapeResultTestInfo::RunInfoForTesting(unsigned run_index,
+ unsigned& start_index,
+ unsigned& num_glyphs,
+ hb_script_t& script) const {
+ unsigned num_characters;
+ return RunInfoForTesting(run_index, start_index, num_characters, num_glyphs,
+ script);
+}
+
+uint16_t ShapeResultTestInfo::GlyphForTesting(unsigned run_index,
+ size_t glyph_index) const {
+ return runs_[run_index]->glyph_data_[glyph_index].glyph;
+}
+
+float ShapeResultTestInfo::AdvanceForTesting(unsigned run_index,
+ size_t glyph_index) const {
+ return runs_[run_index]->glyph_data_[glyph_index].advance;
+}
+
+SimpleFontData* ShapeResultTestInfo::FontDataForTesting(
+ unsigned run_index) const {
+ return runs_[run_index]->font_data_.get();
+}
+
+Vector<unsigned> ShapeResultTestInfo::CharacterIndexesForTesting() const {
+ Vector<unsigned> character_indexes;
+ for (const auto& run : runs_) {
+ for (const auto& glyph_data : run->glyph_data_) {
+ character_indexes.push_back(run->start_index_ +
+ glyph_data.character_index);
+ }
+ }
+ return character_indexes;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_test_info.h b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_test_info.h
new file mode 100644
index 00000000000..4ac24e0755d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_test_info.h
@@ -0,0 +1,71 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_SHAPE_RESULT_TEST_INFO_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_SHAPE_RESULT_TEST_INFO_H_
+
+#include "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer.h"
+
+#include <hb.h>
+
+namespace blink {
+
+class PLATFORM_EXPORT ShapeResultTestInfo : public ShapeResult {
+ public:
+ unsigned NumberOfRunsForTesting() const;
+ bool RunInfoForTesting(unsigned run_index,
+ unsigned& start_index,
+ unsigned& num_glyphs,
+ hb_script_t&) const;
+ bool RunInfoForTesting(unsigned run_index,
+ unsigned& start_index,
+ unsigned& num_characters,
+ unsigned& num_glyphs,
+ hb_script_t&) const;
+ uint16_t GlyphForTesting(unsigned run_index, size_t glyph_index) const;
+ float AdvanceForTesting(unsigned run_index, size_t glyph_index) const;
+ SimpleFontData* FontDataForTesting(unsigned run_index) const;
+ Vector<unsigned> CharacterIndexesForTesting() const;
+};
+
+class PLATFORM_EXPORT ShapeResultBloberizerTestInfo {
+ public:
+ static const SimpleFontData* PendingRunFontData(
+ const ShapeResultBloberizer& bloberizer) {
+ return bloberizer.pending_font_data_;
+ }
+
+ static CanvasRotationInVertical PendingBlobRotation(
+ const ShapeResultBloberizer& bloberizer) {
+ return bloberizer.pending_canvas_rotation_;
+ }
+
+ static const Vector<Glyph, 1024>& PendingRunGlyphs(
+ const ShapeResultBloberizer& bloberizer) {
+ return bloberizer.pending_glyphs_;
+ }
+
+ static const Vector<float, 1024>& PendingRunOffsets(
+ const ShapeResultBloberizer& bloberizer) {
+ return bloberizer.pending_offsets_;
+ }
+
+ static bool HasPendingRunVerticalOffsets(
+ const ShapeResultBloberizer& bloberizer) {
+ return bloberizer.HasPendingVerticalOffsets();
+ }
+
+ static size_t PendingBlobRunCount(const ShapeResultBloberizer& bloberizer) {
+ return bloberizer.builder_run_count_;
+ }
+
+ static size_t CommittedBlobCount(const ShapeResultBloberizer& bloberizer) {
+ return bloberizer.blobs_.size();
+ }
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_SHAPE_RESULT_TEST_INFO_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.cc b/chromium/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.cc
new file mode 100644
index 00000000000..6454534bcc3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.cc
@@ -0,0 +1,390 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.h"
+
+#include "third_party/blink/renderer/platform/fonts/font.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_result.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_inline_headers.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.h"
+#include "third_party/blink/renderer/platform/text/text_break_iterator.h"
+
+namespace blink {
+
+ShapingLineBreaker::ShapingLineBreaker(
+ const HarfBuzzShaper* shaper,
+ const Font* font,
+ const ShapeResult* result,
+ const LazyLineBreakIterator* break_iterator,
+ ShapeResultSpacing<String>* spacing,
+ const Hyphenation* hyphenation)
+ : shaper_(shaper),
+ font_(font),
+ result_(result),
+ break_iterator_(break_iterator),
+ spacing_(spacing),
+ hyphenation_(hyphenation),
+ is_soft_hyphen_enabled_(true) {
+ // ShapeResultSpacing is stateful when it has expansions. We may use it in
+ // arbitrary order that it cannot have expansions.
+ DCHECK(!spacing_ || !spacing_->HasExpansion());
+}
+
+namespace {
+
+// ShapingLineBreaker computes using visual positions. This function flips
+// logical advance to visual, or vice versa.
+LayoutUnit FlipRtl(LayoutUnit value, TextDirection direction) {
+ return IsLtr(direction) ? value : -value;
+}
+
+// Snaps a visual position to the line start direction.
+LayoutUnit SnapStart(float value, TextDirection direction) {
+ return IsLtr(direction) ? LayoutUnit::FromFloatFloor(value)
+ : LayoutUnit::FromFloatCeil(value);
+}
+
+// Snaps a visual position to the line end direction.
+LayoutUnit SnapEnd(float value, TextDirection direction) {
+ return IsLtr(direction) ? LayoutUnit::FromFloatCeil(value)
+ : LayoutUnit::FromFloatFloor(value);
+}
+
+bool IsAllSpaces(const String& text, unsigned start, unsigned end) {
+ return StringView(text, start, end - start)
+ .IsAllSpecialCharacters<LazyLineBreakIterator::IsBreakableSpace>();
+}
+
+bool ShouldHyphenate(const String& text, unsigned start, unsigned end) {
+ // Do not hyphenate the last word in a paragraph, except when it's a single
+ // word paragraph.
+ if (IsAllSpaces(text, end, text.length()))
+ return IsAllSpaces(text, 0, start);
+ return true;
+}
+
+} // namespace
+
+inline const String& ShapingLineBreaker::GetText() const {
+ return break_iterator_->GetString();
+}
+
+unsigned ShapingLineBreaker::Hyphenate(unsigned offset,
+ unsigned word_start,
+ unsigned word_end,
+ bool backwards) const {
+ DCHECK(hyphenation_);
+ DCHECK_GT(word_end, word_start);
+ DCHECK_GE(offset, word_start);
+ DCHECK_LE(offset, word_end);
+ unsigned word_len = word_end - word_start;
+ if (word_len <= Hyphenation::kMinimumSuffixLength)
+ return 0;
+
+ const String& text = GetText();
+ if (backwards) {
+ unsigned before_index = offset - word_start;
+ if (before_index <= Hyphenation::kMinimumPrefixLength)
+ return 0;
+ unsigned prefix_length = hyphenation_->LastHyphenLocation(
+ StringView(text, word_start, word_len), before_index);
+ DCHECK(!prefix_length || prefix_length < before_index);
+ return prefix_length;
+ } else {
+ unsigned after_index = offset - word_start;
+ if (word_len <= after_index + Hyphenation::kMinimumSuffixLength)
+ return 0;
+ unsigned prefix_length = hyphenation_->FirstHyphenLocation(
+ StringView(text, word_start, word_len), after_index);
+ DCHECK(!prefix_length || prefix_length > after_index);
+ return prefix_length;
+ }
+}
+
+unsigned ShapingLineBreaker::Hyphenate(unsigned offset,
+ unsigned start,
+ bool backwards,
+ bool* is_hyphenated) const {
+ DCHECK(is_hyphenated && !*is_hyphenated);
+ const String& text = GetText();
+ unsigned word_end = break_iterator_->NextBreakOpportunity(offset);
+ if (word_end == offset) {
+ DCHECK_EQ(offset, break_iterator_->PreviousBreakOpportunity(offset, start));
+ return word_end;
+ }
+ unsigned previous_break_opportunity =
+ break_iterator_->PreviousBreakOpportunity(offset, start);
+ unsigned word_start = previous_break_opportunity;
+ // Skip the leading spaces of this word because the break iterator breaks
+ // before spaces.
+ while (word_start < text.length() &&
+ LazyLineBreakIterator::IsBreakableSpace(text[word_start]))
+ word_start++;
+ if (offset >= word_start &&
+ ShouldHyphenate(text, previous_break_opportunity, word_end)) {
+ unsigned prefix_length = Hyphenate(offset, word_start, word_end, backwards);
+ if (prefix_length) {
+ *is_hyphenated = true;
+ return word_start + prefix_length;
+ }
+ }
+ return backwards ? previous_break_opportunity : word_end;
+}
+
+unsigned ShapingLineBreaker::PreviousBreakOpportunity(
+ unsigned offset,
+ unsigned start,
+ bool* is_hyphenated) const {
+ DCHECK(is_hyphenated && !*is_hyphenated);
+ if (UNLIKELY(!IsSoftHyphenEnabled())) {
+ const String& text = GetText();
+ for (;; offset--) {
+ offset = break_iterator_->PreviousBreakOpportunity(offset, start);
+ if (offset <= start || offset >= text.length() ||
+ text[offset - 1] != kSoftHyphenCharacter)
+ return offset;
+ }
+ }
+
+ if (UNLIKELY(hyphenation_))
+ return Hyphenate(offset, start, true, is_hyphenated);
+
+ return break_iterator_->PreviousBreakOpportunity(offset, start);
+}
+
+unsigned ShapingLineBreaker::NextBreakOpportunity(unsigned offset,
+ unsigned start,
+ bool* is_hyphenated) const {
+ DCHECK(is_hyphenated && !*is_hyphenated);
+ if (UNLIKELY(!IsSoftHyphenEnabled())) {
+ const String& text = GetText();
+ for (;; offset++) {
+ offset = break_iterator_->NextBreakOpportunity(offset);
+ if (offset >= text.length() || text[offset - 1] != kSoftHyphenCharacter)
+ return offset;
+ }
+ }
+
+ if (UNLIKELY(hyphenation_))
+ return Hyphenate(offset, start, false, is_hyphenated);
+
+ return break_iterator_->NextBreakOpportunity(offset);
+}
+
+inline scoped_refptr<ShapeResult> ShapingLineBreaker::Shape(TextDirection direction,
+ unsigned start,
+ unsigned end) {
+ if (!spacing_ || !spacing_->HasSpacing())
+ return shaper_->Shape(font_, direction, start, end);
+
+ scoped_refptr<ShapeResult> result = shaper_->Shape(font_, direction, start, end);
+ result->ApplySpacing(*spacing_);
+ return result;
+}
+
+// Shapes a line of text by finding a valid and appropriate break opportunity
+// based on the shaping results for the entire paragraph. Re-shapes the start
+// and end of the line as needed.
+//
+// Definitions:
+// Candidate break opportunity: Ideal point to break, disregarding line
+// breaking rules. May be in the middle of a word
+// or inside a ligature.
+// Valid break opportunity: A point where a break is allowed according to
+// the relevant breaking rules.
+// Safe-to-break: A point where a break may occur without
+// affecting the rendering or metrics of the
+// text. Breaking at safe-to-break point does not
+// require reshaping.
+//
+// For example:
+// Given the string "Line breaking example", an available space of 100px and a
+// mono-space font where each glyph is 10px wide.
+//
+// Line breaking example
+// | |
+// 0 100px
+//
+// The candidate (or ideal) break opportunity would be at an offset of 10 as
+// the break would happen at exactly 100px in that case.
+// The previous valid break opportunity though is at an offset of 5.
+// If we further assume that the font kerns with space then even though it's a
+// valid break opportunity reshaping is required as the combined width of the
+// two segments "Line " and "breaking" may be different from "Line breaking".
+scoped_refptr<ShapeResult> ShapingLineBreaker::ShapeLine(
+ unsigned start,
+ LayoutUnit available_space,
+ bool start_should_be_safe,
+ ShapingLineBreaker::Result* result_out) {
+ DCHECK_GE(available_space, LayoutUnit(0));
+ unsigned range_start = result_->StartIndexForResult();
+ unsigned range_end = result_->EndIndexForResult();
+ DCHECK_GE(start, range_start);
+ DCHECK_LT(start, range_end);
+ result_out->is_hyphenated = false;
+ const String& text = GetText();
+
+ // The start position in the original shape results.
+ float start_position_float = result_->PositionForOffset(start - range_start);
+ TextDirection direction = result_->Direction();
+ LayoutUnit start_position = SnapStart(start_position_float, direction);
+
+ // Find a candidate break opportunity by identifying the last offset before
+ // exceeding the available space and the determine the closest valid break
+ // preceding the candidate.
+ LayoutUnit end_position = SnapEnd(start_position_float, direction) +
+ FlipRtl(available_space, direction);
+ DCHECK_GE(FlipRtl(end_position - start_position, direction), LayoutUnit(0));
+ unsigned candidate_break =
+ result_->OffsetForPosition(end_position, false) + range_start;
+
+ unsigned first_safe =
+ start_should_be_safe ? result_->NextSafeToBreakOffset(start) : start;
+ DCHECK_GE(first_safe, start);
+ if (candidate_break >= range_end) {
+ // The |result_| does not have glyphs to fill the available space,
+ // and thus unable to compute. Return the result up to range_end.
+ DCHECK_EQ(candidate_break, range_end);
+ result_out->break_offset = range_end;
+ return ShapeToEnd(start, first_safe, range_end);
+ }
+
+ // candidate_break should be >= start, but rounding errors can chime in when
+ // comparing floats. See ShapeLineZeroAvailableWidth on Linux/Mac.
+ candidate_break = std::max(candidate_break, start);
+
+ unsigned break_opportunity = PreviousBreakOpportunity(
+ candidate_break, start, &result_out->is_hyphenated);
+ if (break_opportunity <= start) {
+ break_opportunity =
+ NextBreakOpportunity(std::max(candidate_break, start + 1), start,
+ &result_out->is_hyphenated);
+ // |range_end| may not be a break opportunity, but this function cannot
+ // measure beyond it.
+ if (break_opportunity >= range_end) {
+ result_out->break_offset = range_end;
+ return ShapeToEnd(start, first_safe, range_end);
+ }
+ }
+ DCHECK_GT(break_opportunity, start);
+
+ // If the start offset is not at a safe-to-break boundary the content between
+ // the start and the next safe-to-break boundary needs to be reshaped and the
+ // available space adjusted to take the reshaping into account.
+ scoped_refptr<ShapeResult> line_start_result;
+ if (first_safe != start) {
+ if (first_safe >= break_opportunity) {
+ // There is no safe-to-break, reshape the whole range.
+ result_out->break_offset = break_opportunity;
+ return Shape(direction, start, break_opportunity);
+ }
+ LayoutUnit original_width =
+ FlipRtl(SnapEnd(result_->PositionForOffset(first_safe - range_start),
+ direction) -
+ start_position,
+ direction);
+ line_start_result = Shape(direction, start, first_safe);
+ available_space += line_start_result->SnappedWidth() - original_width;
+ }
+
+ scoped_refptr<ShapeResult> line_end_result;
+ unsigned last_safe = break_opportunity;
+ while (break_opportunity > start) {
+ // If the previous valid break opportunity is not at a safe-to-break
+ // boundary reshape between the safe-to-break offset and the valid break
+ // offset. If the resulting width exceeds the available space the
+ // preceding boundary is tried until the available space is sufficient.
+ unsigned previous_safe =
+ std::max(result_->PreviousSafeToBreakOffset(break_opportunity), start);
+ DCHECK_LE(previous_safe, break_opportunity);
+ if (previous_safe != break_opportunity) {
+ LayoutUnit safe_position = SnapStart(
+ result_->PositionForOffset(previous_safe - range_start), direction);
+ while (break_opportunity > previous_safe && previous_safe >= start) {
+ DCHECK_LE(break_opportunity, range_end);
+ line_end_result = Shape(direction, previous_safe, break_opportunity);
+ if (line_end_result->SnappedWidth() <=
+ FlipRtl(end_position - safe_position, direction))
+ break;
+ // Doesn't fit after the reshape. Try previous break opportunity, or
+ // overflow if there were none.
+ bool is_previous_break_opportunity_hyphenated = false;
+ unsigned previous_break_opportunity =
+ PreviousBreakOpportunity(break_opportunity - 1, start,
+ &is_previous_break_opportunity_hyphenated);
+ if (previous_break_opportunity <= start)
+ break;
+ break_opportunity = previous_break_opportunity;
+ result_out->is_hyphenated = is_previous_break_opportunity_hyphenated;
+ line_end_result = nullptr;
+ }
+ }
+
+ if (break_opportunity > start) {
+ last_safe = previous_safe;
+ break;
+ }
+
+ // No suitable break opportunity, not exceeding the available space,
+ // found. Choose the next valid one even though it will overflow.
+ break_opportunity = NextBreakOpportunity(candidate_break, start,
+ &result_out->is_hyphenated);
+ // |range_end| may not be a break opportunity, but this function cannot
+ // measure beyond it.
+ break_opportunity = std::min(break_opportunity, range_end);
+ }
+
+ // Create shape results for the line by copying from the re-shaped result (if
+ // reshaping was needed) and the original shape results.
+ scoped_refptr<ShapeResult> line_result = ShapeResult::Create(font_, 0, direction);
+ unsigned max_length = std::numeric_limits<unsigned>::max();
+ if (line_start_result)
+ line_start_result->CopyRange(0, max_length, line_result.get());
+ if (last_safe > first_safe)
+ result_->CopyRange(first_safe, last_safe, line_result.get());
+ if (line_end_result)
+ line_end_result->CopyRange(last_safe, max_length, line_result.get());
+
+ DCHECK_GT(break_opportunity, start);
+ // TODO(layout-dev): This hits on Mac and Mac only for a number of tests in
+ // virtual/layout_ng/external/wpt/css/CSS2/floats-clear/.
+ // DCHECK_EQ(std::min(break_opportunity, range_end) - start,
+ // line_result->NumCharacters());
+
+ result_out->break_offset = break_opportunity;
+ if (!result_out->is_hyphenated &&
+ text[break_opportunity - 1] == kSoftHyphenCharacter)
+ result_out->is_hyphenated = true;
+ return line_result;
+}
+
+// Shape from the specified offset to the end of the ShapeResult.
+// If |start| is safe-to-break, this copies the subset of the result.
+scoped_refptr<ShapeResult> ShapingLineBreaker::ShapeToEnd(unsigned start,
+ unsigned first_safe,
+ unsigned range_end) {
+ DCHECK_GE(start, result_->StartIndexForResult());
+ DCHECK_LT(start, range_end);
+ DCHECK_GE(first_safe, start);
+ DCHECK_EQ(range_end, result_->EndIndexForResult());
+
+ // If |start| is safe-to-break, no reshape is needed.
+ if (first_safe == start) {
+ return result_->SubRange(start, range_end);
+ }
+
+ // If no safe-to-break offset is found in range, reshape the entire range.
+ TextDirection direction = result_->Direction();
+ if (first_safe >= range_end) {
+ return Shape(direction, start, range_end);
+ }
+
+ // Otherwise reshape to |first_safe|, then copy the rest.
+ scoped_refptr<ShapeResult> line_result = Shape(direction, start, first_safe);
+ result_->CopyRange(first_safe, range_end, line_result.get());
+ return line_result;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.h b/chromium/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.h
new file mode 100644
index 00000000000..361527543ed
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.h
@@ -0,0 +1,116 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_SHAPING_LINE_BREAKER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_SHAPING_LINE_BREAKER_H_
+
+#include "third_party/blink/renderer/platform/layout_unit.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/text/text_direction.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+
+namespace blink {
+
+class Font;
+class ShapeResult;
+class HarfBuzzShaper;
+class Hyphenation;
+class LazyLineBreakIterator;
+enum class LineBreakType;
+template <typename TextContainerType>
+class ShapeResultSpacing;
+
+// Shapes a line of text by finding the ideal break position as indicated by the
+// available space and the shape results for the entire paragraph. Once an ideal
+// break position has been found the text is scanned backwards until a valid and
+// and appropriate break opportunity is identified. Unless the break opportunity
+// is at a safe-to-break boundary (as identified by HarfBuzz) the beginning and/
+// or end of the line is reshaped to account for differences caused by breaking.
+//
+// This allows for significantly faster and more efficient line breaking by only
+// reshaping when absolutely necessarily and by only evaluating likely candidate
+// break opportunities instead of measuring and evaluating all possible options.
+class PLATFORM_EXPORT ShapingLineBreaker final {
+ STACK_ALLOCATED();
+
+ public:
+ ShapingLineBreaker(const HarfBuzzShaper*,
+ const Font*,
+ const ShapeResult*,
+ const LazyLineBreakIterator*,
+ ShapeResultSpacing<String>* = nullptr,
+ const Hyphenation* = nullptr);
+ ~ShapingLineBreaker() = default;
+
+ // Represents details of the result of |ShapeLine()|.
+ struct Result {
+ STACK_ALLOCATED();
+
+ // Indicates the resulting break offset.
+ unsigned break_offset;
+
+ // True if the break is hyphenated, either by automatic hyphenation or
+ // soft-hyphen characters.
+ // The hyphen glyph is not included in the |ShapeResult|, and that appending
+ // a hyphen glyph may overflow the specified available space.
+ bool is_hyphenated;
+ };
+
+ // Shapes a line of text by finding a valid and appropriate break opportunity
+ // based on the shaping results for the entire paragraph.
+ // |start_should_be_safe| is true for the beginning of each wrapped line, but
+ // is false for subsequent ShapeResults.
+ scoped_refptr<ShapeResult> ShapeLine(unsigned start_offset,
+ LayoutUnit available_space,
+ bool start_should_be_safe,
+ Result* result_out);
+ scoped_refptr<ShapeResult> ShapeLine(unsigned start_offset,
+ LayoutUnit available_space,
+ Result* result_out) {
+ return ShapeLine(start_offset, available_space, true, result_out);
+ }
+
+ // Disable breaking at soft hyphens (U+00AD).
+ bool IsSoftHyphenEnabled() const { return is_soft_hyphen_enabled_; }
+ void DisableSoftHyphen() { is_soft_hyphen_enabled_ = false; }
+
+ private:
+ const String& GetText() const;
+
+ unsigned PreviousBreakOpportunity(unsigned offset,
+ unsigned start,
+ bool* is_hyphenated) const;
+ unsigned NextBreakOpportunity(unsigned offset,
+ unsigned start,
+ bool* is_hyphenated) const;
+ unsigned Hyphenate(unsigned offset,
+ unsigned start,
+ bool backwards,
+ bool* is_hyphenated) const;
+ unsigned Hyphenate(unsigned offset,
+ unsigned word_start,
+ unsigned word_end,
+ bool backwards) const;
+
+ scoped_refptr<ShapeResult> Shape(TextDirection, unsigned start, unsigned end);
+ scoped_refptr<ShapeResult> ShapeToEnd(unsigned start,
+ unsigned first_safe,
+ unsigned range_end);
+
+ const HarfBuzzShaper* shaper_;
+ const Font* font_;
+ const ShapeResult* result_;
+ const LazyLineBreakIterator* break_iterator_;
+ // TODO(kojii): ShapeResultSpacing is not const because it's stateful when it
+ // has expansions. Split spacing and expansions to make this const.
+ ShapeResultSpacing<String>* spacing_;
+ const Hyphenation* hyphenation_;
+ bool is_soft_hyphen_enabled_;
+
+ friend class ShapingLineBreakerTest;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_SHAPING_LINE_BREAKER_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker_test.cc b/chromium/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker_test.cc
new file mode 100644
index 00000000000..24daffd79dc
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker_test.cc
@@ -0,0 +1,344 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.h"
+
+#include <unicode/uscript.h>
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/fonts/font.h"
+#include "third_party/blink/renderer/platform/fonts/font_cache.h"
+#include "third_party/blink/renderer/platform/fonts/font_test_utilities.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_inline_headers.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_test_info.h"
+#include "third_party/blink/renderer/platform/text/text_break_iterator.h"
+#include "third_party/blink/renderer/platform/text/text_run.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+namespace {
+
+scoped_refptr<ShapeResult> ShapeLine(ShapingLineBreaker* breaker,
+ unsigned start_offset,
+ LayoutUnit available_space,
+ unsigned* break_offset) {
+ ShapingLineBreaker::Result result;
+ scoped_refptr<ShapeResult> shape_result =
+ breaker->ShapeLine(start_offset, available_space, &result);
+ *break_offset = result.break_offset;
+ return shape_result;
+}
+
+} // namespace
+
+class ShapingLineBreakerTest : public testing::Test {
+ protected:
+ void SetUp() override {
+ font_description.SetComputedSize(12.0);
+ font = Font(font_description);
+ font.Update(nullptr);
+ }
+
+ void TearDown() override {}
+
+ // Compute all break positions by |NextBreakOpportunity|.
+ Vector<unsigned> BreakPositionsByNext(const ShapingLineBreaker& breaker,
+ const String& string) {
+ Vector<unsigned> break_positions;
+ for (unsigned i = 0; i <= string.length(); i++) {
+ bool is_hyphenated = false;
+ unsigned next = breaker.NextBreakOpportunity(i, 0, &is_hyphenated);
+ if (break_positions.IsEmpty() || break_positions.back() != next)
+ break_positions.push_back(next);
+ }
+ return break_positions;
+ }
+
+ // Compute all break positions by |PreviousBreakOpportunity|.
+ Vector<unsigned> BreakPositionsByPrevious(const ShapingLineBreaker& breaker,
+ const String& string) {
+ Vector<unsigned> break_positions;
+ for (unsigned i = string.length(); i; i--) {
+ bool is_hyphenated = false;
+ unsigned previous =
+ breaker.PreviousBreakOpportunity(i, 0, &is_hyphenated);
+ if (previous &&
+ (break_positions.IsEmpty() || break_positions.back() != previous))
+ break_positions.push_back(previous);
+ }
+ break_positions.Reverse();
+ return break_positions;
+ }
+
+ FontCachePurgePreventer font_cache_purge_preventer;
+ FontDescription font_description;
+ Font font;
+ unsigned start_index = 0;
+ unsigned num_glyphs = 0;
+ hb_script_t script = HB_SCRIPT_INVALID;
+};
+
+TEST_F(ShapingLineBreakerTest, ShapeLineLatin) {
+ String string = To16Bit(
+ "Test run with multiple words and breaking "
+ "opportunities.",
+ 56);
+ LazyLineBreakIterator break_iterator(string, "en-US", LineBreakType::kNormal);
+ TextDirection direction = TextDirection::kLtr;
+
+ HarfBuzzShaper shaper(string.Characters16(), 56);
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, direction);
+
+ // "Test run with multiple"
+ scoped_refptr<ShapeResult> first4 = shaper.Shape(&font, direction, 0, 22);
+ ASSERT_LT(first4->SnappedWidth(), result->SnappedWidth());
+
+ // "Test run with"
+ scoped_refptr<ShapeResult> first3 = shaper.Shape(&font, direction, 0, 13);
+ ASSERT_LT(first3->SnappedWidth(), first4->SnappedWidth());
+
+ // "Test run"
+ scoped_refptr<ShapeResult> first2 = shaper.Shape(&font, direction, 0, 8);
+ ASSERT_LT(first2->SnappedWidth(), first3->SnappedWidth());
+
+ // "Test"
+ scoped_refptr<ShapeResult> first1 = shaper.Shape(&font, direction, 0, 4);
+ ASSERT_LT(first1->SnappedWidth(), first2->SnappedWidth());
+
+ ShapingLineBreaker breaker(&shaper, &font, result.get(), &break_iterator);
+ scoped_refptr<ShapeResult> line;
+ unsigned break_offset = 0;
+
+ // Test the case where the entire string fits.
+ line = ShapeLine(&breaker, 0, result->SnappedWidth(), &break_offset);
+ EXPECT_EQ(56u, break_offset); // After the end of the string.
+ EXPECT_EQ(result->SnappedWidth(), line->SnappedWidth());
+
+ // Test cases where we break between words.
+ line = ShapeLine(&breaker, 0, first4->SnappedWidth(), &break_offset);
+ EXPECT_EQ(22u, break_offset); // Between "multiple" and " words"
+ EXPECT_EQ(first4->SnappedWidth(), line->SnappedWidth());
+
+ line = ShapeLine(&breaker, 0, first4->SnappedWidth() + 10, &break_offset);
+ EXPECT_EQ(22u, break_offset); // Between "multiple" and " words"
+ EXPECT_EQ(first4->SnappedWidth(), line->SnappedWidth());
+
+ line = ShapeLine(&breaker, 0, first4->SnappedWidth() - 1, &break_offset);
+ EXPECT_EQ(13u, break_offset); // Between "width" and "multiple"
+ EXPECT_EQ(first3->SnappedWidth(), line->SnappedWidth());
+
+ line = ShapeLine(&breaker, 0, first3->SnappedWidth(), &break_offset);
+ EXPECT_EQ(13u, break_offset); // Between "width" and "multiple"
+ EXPECT_EQ(first3->SnappedWidth(), line->SnappedWidth());
+
+ line = ShapeLine(&breaker, 0, first3->SnappedWidth() - 1, &break_offset);
+ EXPECT_EQ(8u, break_offset); // Between "run" and "width"
+ EXPECT_EQ(first2->SnappedWidth(), line->SnappedWidth());
+
+ line = ShapeLine(&breaker, 0, first2->SnappedWidth(), &break_offset);
+ EXPECT_EQ(8u, break_offset); // Between "run" and "width"
+ EXPECT_EQ(first2->SnappedWidth(), line->SnappedWidth());
+
+ line = ShapeLine(&breaker, 0, first2->SnappedWidth() - 1, &break_offset);
+ EXPECT_EQ(4u, break_offset); // Between "Test" and "run"
+ EXPECT_EQ(first1->SnappedWidth(), line->SnappedWidth());
+
+ line = ShapeLine(&breaker, 0, first1->SnappedWidth(), &break_offset);
+ EXPECT_EQ(4u, break_offset); // Between "Test" and "run"
+ EXPECT_EQ(first1->SnappedWidth(), line->SnappedWidth());
+
+ // Test the case where we cannot break earlier.
+ line = ShapeLine(&breaker, 0, first1->SnappedWidth() - 1, &break_offset);
+ EXPECT_EQ(4u, break_offset); // Between "Test" and "run"
+ EXPECT_EQ(first1->SnappedWidth(), line->SnappedWidth());
+}
+
+TEST_F(ShapingLineBreakerTest, ShapeLineLatinMultiLine) {
+ String string = To16Bit("Line breaking test case.", 24);
+ LazyLineBreakIterator break_iterator(string, "en-US", LineBreakType::kNormal);
+ TextDirection direction = TextDirection::kLtr;
+
+ HarfBuzzShaper shaper(string.Characters16(), 24);
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, direction);
+ scoped_refptr<ShapeResult> first = shaper.Shape(&font, direction, 0, 4);
+ scoped_refptr<ShapeResult> mid_third = shaper.Shape(&font, direction, 0, 16);
+
+ ShapingLineBreaker breaker(&shaper, &font, result.get(), &break_iterator);
+ unsigned break_offset = 0;
+
+ ShapeLine(&breaker, 0, result->SnappedWidth() - 1, &break_offset);
+ EXPECT_EQ(18u, break_offset);
+
+ ShapeLine(&breaker, 0, first->SnappedWidth(), &break_offset);
+ EXPECT_EQ(4u, break_offset);
+
+ ShapeLine(&breaker, 0, mid_third->SnappedWidth(), &break_offset);
+ EXPECT_EQ(13u, break_offset);
+
+ ShapeLine(&breaker, 13u, mid_third->SnappedWidth(), &break_offset);
+ EXPECT_EQ(24u, break_offset);
+}
+
+TEST_F(ShapingLineBreakerTest, ShapeLineLatinBreakAll) {
+ String string = To16Bit("Testing break type-break all.", 29);
+ LazyLineBreakIterator break_iterator(string, "en-US",
+ LineBreakType::kBreakAll);
+ TextDirection direction = TextDirection::kLtr;
+
+ HarfBuzzShaper shaper(string.Characters16(), 29);
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, direction);
+ scoped_refptr<ShapeResult> midpoint = shaper.Shape(&font, direction, 0, 16);
+
+ ShapingLineBreaker breaker(&shaper, &font, result.get(), &break_iterator);
+ scoped_refptr<ShapeResult> line;
+ unsigned break_offset = 0;
+
+ line = ShapeLine(&breaker, 0, midpoint->SnappedWidth(), &break_offset);
+ EXPECT_EQ(16u, break_offset);
+ EXPECT_EQ(midpoint->SnappedWidth(), line->SnappedWidth());
+
+ line = ShapeLine(&breaker, 16u, result->SnappedWidth(), &break_offset);
+ EXPECT_EQ(29u, break_offset);
+ EXPECT_GE(midpoint->SnappedWidth(), line->SnappedWidth());
+}
+
+TEST_F(ShapingLineBreakerTest, ShapeLineZeroAvailableWidth) {
+ String string(u"Testing overflow line break.");
+ LazyLineBreakIterator break_iterator(string, "en-US", LineBreakType::kNormal);
+ TextDirection direction = TextDirection::kLtr;
+
+ HarfBuzzShaper shaper(string.Characters16(), string.length());
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, direction);
+
+ ShapingLineBreaker breaker(&shaper, &font, result.get(), &break_iterator);
+ scoped_refptr<ShapeResult> line;
+ unsigned break_offset = 0;
+ LayoutUnit zero(0);
+
+ line = ShapeLine(&breaker, 0, zero, &break_offset);
+ EXPECT_EQ(7u, break_offset);
+
+ line = ShapeLine(&breaker, 7, zero, &break_offset);
+ EXPECT_EQ(16u, break_offset);
+
+ line = ShapeLine(&breaker, 16, zero, &break_offset);
+ EXPECT_EQ(21u, break_offset);
+
+ line = ShapeLine(&breaker, 21, zero, &break_offset);
+ EXPECT_EQ(28u, break_offset);
+}
+
+// Hits DCHECK at end of ShapingLineBreaker::ShapeLine, not clear if the test
+// is correct. Disabling for now.
+TEST_F(ShapingLineBreakerTest, DISABLED_ShapeLineArabicThaiHanLatin) {
+ UChar mixed_string[] = {0x628, 0x20, 0x64A, 0x629, 0x20,
+ 0xE20, 0x65E5, 0x62, 0};
+ LazyLineBreakIterator break_iterator(mixed_string, "ar_AE",
+ LineBreakType::kNormal);
+ TextDirection direction = TextDirection::kRtl;
+
+ HarfBuzzShaper shaper(mixed_string, 8);
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, direction);
+ scoped_refptr<ShapeResult> words[] = {shaper.Shape(&font, direction, 0, 1),
+ shaper.Shape(&font, direction, 2, 4),
+ shaper.Shape(&font, direction, 5, 6),
+ shaper.Shape(&font, direction, 6, 7),
+ shaper.Shape(&font, direction, 7, 8)};
+ const auto& longest_word = std::max_element(
+ std::begin(words), std::end(words),
+ [](const scoped_refptr<ShapeResult>& a, const scoped_refptr<ShapeResult>& b) {
+ return a->SnappedWidth() < b->SnappedWidth();
+ });
+ LayoutUnit longest_word_width = (*longest_word)->SnappedWidth();
+
+ ShapingLineBreaker breaker(&shaper, &font, result.get(), &break_iterator);
+ scoped_refptr<ShapeResult> line;
+ unsigned break_offset = 0;
+
+ ShapeLine(&breaker, 0, longest_word_width, &break_offset);
+ EXPECT_EQ(1u, break_offset);
+
+ ShapeLine(&breaker, 1, longest_word_width, &break_offset);
+ EXPECT_EQ(4u, break_offset);
+
+ ShapeLine(&breaker, 4, longest_word_width, &break_offset);
+ EXPECT_EQ(6u, break_offset);
+
+ ShapeLine(&breaker, 6, longest_word_width, &break_offset);
+ EXPECT_EQ(7u, break_offset);
+
+ ShapeLine(&breaker, 7, longest_word_width, &break_offset);
+ EXPECT_EQ(8u, break_offset);
+}
+
+TEST_F(ShapingLineBreakerTest, ShapeLineRangeEndMidWord) {
+ String string(u"Mid word");
+ LazyLineBreakIterator break_iterator(string, "en-US", LineBreakType::kNormal);
+ TextDirection direction = TextDirection::kLtr;
+
+ HarfBuzzShaper shaper(string.Characters16(), string.length());
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, direction, 0, 2);
+
+ ShapingLineBreaker breaker(&shaper, &font, result.get(), &break_iterator);
+ scoped_refptr<ShapeResult> line;
+ unsigned break_offset = 0;
+
+ line = ShapeLine(&breaker, 0, LayoutUnit::Max(), &break_offset);
+ EXPECT_EQ(2u, break_offset);
+ EXPECT_EQ(result->Width(), line->Width());
+}
+
+struct BreakOpportunityTestData {
+ const char16_t* string;
+ Vector<unsigned> break_positions;
+ Vector<unsigned> break_positions_with_soft_hyphen_disabled;
+};
+
+class BreakOpportunityTest
+ : public ShapingLineBreakerTest,
+ public testing::WithParamInterface<BreakOpportunityTestData> {};
+
+INSTANTIATE_TEST_CASE_P(
+ ShapingLineBreakerTest,
+ BreakOpportunityTest,
+ testing::Values(BreakOpportunityTestData{u"x y z", {1, 3, 5}},
+ BreakOpportunityTestData{u"y\xADz", {2, 3}, {3}},
+ BreakOpportunityTestData{u"\xADz", {1, 2}, {2}},
+ BreakOpportunityTestData{u"y\xAD", {2}, {2}},
+ BreakOpportunityTestData{u"\xAD\xADz", {2, 3}, {3}}));
+
+TEST_P(BreakOpportunityTest, Next) {
+ const BreakOpportunityTestData& data = GetParam();
+ String string(data.string);
+ LazyLineBreakIterator break_iterator(string);
+ ShapingLineBreaker breaker(nullptr, &font, nullptr, &break_iterator);
+ EXPECT_THAT(BreakPositionsByNext(breaker, string),
+ testing::ElementsAreArray(data.break_positions));
+
+ if (!data.break_positions_with_soft_hyphen_disabled.IsEmpty()) {
+ breaker.DisableSoftHyphen();
+ EXPECT_THAT(BreakPositionsByNext(breaker, string),
+ testing::ElementsAreArray(
+ data.break_positions_with_soft_hyphen_disabled));
+ }
+}
+
+TEST_P(BreakOpportunityTest, Previous) {
+ const BreakOpportunityTestData& data = GetParam();
+ String string(data.string);
+ LazyLineBreakIterator break_iterator(string);
+ ShapingLineBreaker breaker(nullptr, &font, nullptr, &break_iterator);
+ EXPECT_THAT(BreakPositionsByPrevious(breaker, string),
+ testing::ElementsAreArray(data.break_positions));
+
+ if (!data.break_positions_with_soft_hyphen_disabled.IsEmpty()) {
+ breaker.DisableSoftHyphen();
+ EXPECT_THAT(BreakPositionsByPrevious(breaker, string),
+ testing::ElementsAreArray(
+ data.break_positions_with_soft_hyphen_disabled));
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/simple_font_data.cc b/chromium/third_party/blink/renderer/platform/fonts/simple_font_data.cc
new file mode 100644
index 00000000000..189a92dd684
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/simple_font_data.cc
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2005, 2008, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Alexey Proskuryakov
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/fonts/simple_font_data.h"
+
+#include <unicode/utf16.h>
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+#include "SkPath.h"
+#include "SkTypeface.h"
+#include "SkTypes.h"
+
+#include "base/memory/ptr_util.h"
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/font_family_names.h"
+#include "third_party/blink/renderer/platform/fonts/font_description.h"
+#include "third_party/blink/renderer/platform/fonts/skia/skia_text_metrics.h"
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
+#include "third_party/blink/renderer/platform/wtf/byte_order.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
+#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
+
+namespace blink {
+
+const float kSmallCapsFontSizeMultiplier = 0.7f;
+const float kEmphasisMarkFontSizeMultiplier = 0.5f;
+
+SimpleFontData::SimpleFontData(const FontPlatformData& platform_data,
+ scoped_refptr<CustomFontData> custom_data,
+ bool subpixel_ascent_descent)
+ : max_char_width_(-1),
+ avg_char_width_(-1),
+ platform_data_(platform_data),
+ custom_font_data_(std::move(custom_data)),
+ visual_overflow_inflation_for_ascent_(0),
+ visual_overflow_inflation_for_descent_(0) {
+ PlatformInit(subpixel_ascent_descent);
+ PlatformGlyphInit();
+}
+
+void SimpleFontData::PlatformInit(bool subpixel_ascent_descent) {
+ if (!platform_data_.size()) {
+ font_metrics_.Reset();
+ avg_char_width_ = 0;
+ max_char_width_ = 0;
+ return;
+ }
+
+ SkPaint::FontMetrics metrics;
+
+ PaintFont font;
+ platform_data_.SetupPaintFont(&font);
+ font.SetTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ paint_ = font.ToSkPaint();
+ paint_.getFontMetrics(&metrics);
+
+ float ascent;
+ float descent;
+
+ FontMetrics::AscentDescentWithHacks(
+ ascent, descent, visual_overflow_inflation_for_ascent_,
+ visual_overflow_inflation_for_descent_, platform_data_, paint_,
+ subpixel_ascent_descent);
+
+ font_metrics_.SetAscent(ascent);
+ font_metrics_.SetDescent(descent);
+
+ float x_height;
+ if (metrics.fXHeight) {
+ x_height = metrics.fXHeight;
+#if defined(OS_MACOSX)
+ // Mac OS CTFontGetXHeight reports the bounding box height of x,
+ // including parts extending below the baseline and apparently no x-height
+ // value from the OS/2 table. However, the CSS ex unit
+ // expects only parts above the baseline, hence measuring the glyph:
+ // http://www.w3.org/TR/css3-values/#ex-unit
+ const Glyph x_glyph = GlyphForCharacter('x');
+ if (x_glyph) {
+ FloatRect glyph_bounds(BoundsForGlyph(x_glyph));
+ // SkGlyph bounds, y down, based on rendering at (0,0).
+ x_height = -glyph_bounds.Y();
+ }
+#endif
+ font_metrics_.SetXHeight(x_height);
+ } else {
+ x_height = ascent * 0.56; // Best guess from Windows font metrics.
+ font_metrics_.SetXHeight(x_height);
+ font_metrics_.SetHasXHeight(false);
+ }
+
+ float line_gap = SkScalarToFloat(metrics.fLeading);
+ font_metrics_.SetLineGap(line_gap);
+ font_metrics_.SetLineSpacing(lroundf(ascent) + lroundf(descent) +
+ lroundf(line_gap));
+
+// In WebKit/WebCore/platform/graphics/SimpleFontData.cpp, m_spaceWidth is
+// calculated for us, but we need to calculate m_maxCharWidth and
+// m_avgCharWidth in order for text entry widgets to be sized correctly.
+#if defined(OS_WIN)
+ max_char_width_ = SkScalarRoundToInt(metrics.fMaxCharWidth);
+
+ // Older version of the DirectWrite API doesn't implement support for max
+ // char width. Fall back on a multiple of the ascent. This is entirely
+ // arbitrary but comes pretty close to the expected value in most cases.
+ if (max_char_width_ < 1)
+ max_char_width_ = ascent * 2;
+#elif defined(OS_MACOSX)
+ // FIXME: The current avg/max character width calculation is not ideal,
+ // it should check either the OS2 table or, better yet, query FontMetrics.
+ // Sadly FontMetrics provides incorrect data on Mac at the moment.
+ // https://crbug.com/420901
+ max_char_width_ = std::max(avg_char_width_, font_metrics_.FloatAscent());
+#else
+ // Better would be to rely on either fMaxCharWidth or fAveCharWidth.
+ // skbug.com/3087
+ max_char_width_ = SkScalarRoundToInt(metrics.fXMax - metrics.fXMin);
+
+#endif
+
+#if !defined(OS_MACOSX)
+ if (metrics.fAvgCharWidth) {
+ avg_char_width_ = SkScalarRoundToInt(metrics.fAvgCharWidth);
+ } else {
+#endif
+ avg_char_width_ = x_height;
+ const Glyph x_glyph = GlyphForCharacter('x');
+ if (x_glyph) {
+ avg_char_width_ = WidthForGlyph(x_glyph);
+ }
+#if !defined(OS_MACOSX)
+ }
+#endif
+
+ SkTypeface* face = paint_.getTypeface();
+ DCHECK(face);
+ if (int units_per_em = face->getUnitsPerEm())
+ font_metrics_.SetUnitsPerEm(units_per_em);
+}
+
+void SimpleFontData::PlatformGlyphInit() {
+ SkTypeface* typeface = PlatformData().Typeface();
+ if (!typeface->countGlyphs()) {
+ space_glyph_ = 0;
+ space_width_ = 0;
+ zero_glyph_ = 0;
+ return;
+ }
+
+ // Nasty hack to determine if we should round or ceil space widths.
+ // If the font is monospace or fake monospace we ceil to ensure that
+ // every character and the space are the same width. Otherwise we round.
+ space_glyph_ = GlyphForCharacter(' ');
+ float width = WidthForGlyph(space_glyph_);
+ space_width_ = width;
+ zero_glyph_ = GlyphForCharacter('0');
+ font_metrics_.SetZeroWidth(WidthForGlyph(zero_glyph_));
+}
+
+const SimpleFontData* SimpleFontData::FontDataForCharacter(UChar32) const {
+ return this;
+}
+
+Glyph SimpleFontData::GlyphForCharacter(UChar32 codepoint) const {
+ uint16_t glyph;
+ SkTypeface* typeface = PlatformData().Typeface();
+ CHECK(typeface);
+ typeface->charsToGlyphs(&codepoint, SkTypeface::kUTF32_Encoding, &glyph, 1);
+ return glyph;
+}
+
+bool SimpleFontData::IsSegmented() const {
+ return false;
+}
+
+scoped_refptr<SimpleFontData> SimpleFontData::SmallCapsFontData(
+ const FontDescription& font_description) const {
+ if (!derived_font_data_)
+ derived_font_data_ = DerivedFontData::Create();
+ if (!derived_font_data_->small_caps)
+ derived_font_data_->small_caps =
+ CreateScaledFontData(font_description, kSmallCapsFontSizeMultiplier);
+
+ return derived_font_data_->small_caps;
+}
+
+scoped_refptr<SimpleFontData> SimpleFontData::EmphasisMarkFontData(
+ const FontDescription& font_description) const {
+ if (!derived_font_data_)
+ derived_font_data_ = DerivedFontData::Create();
+ if (!derived_font_data_->emphasis_mark)
+ derived_font_data_->emphasis_mark =
+ CreateScaledFontData(font_description, kEmphasisMarkFontSizeMultiplier);
+
+ return derived_font_data_->emphasis_mark;
+}
+
+std::unique_ptr<SimpleFontData::DerivedFontData>
+SimpleFontData::DerivedFontData::Create() {
+ return base::WrapUnique(new DerivedFontData);
+}
+
+scoped_refptr<SimpleFontData> SimpleFontData::CreateScaledFontData(
+ const FontDescription& font_description,
+ float scale_factor) const {
+ const float scaled_size =
+ lroundf(font_description.ComputedSize() * scale_factor);
+ return SimpleFontData::Create(
+ FontPlatformData(platform_data_, scaled_size),
+ IsCustomFont() ? CustomFontData::Create() : nullptr);
+}
+
+// Internal leadings can be distributed to ascent and descent.
+// -------------------------------------------
+// | - Internal Leading (in ascent)
+// |--------------------------------
+// Ascent - | |
+// | |
+// | | - Em height
+// ----------|--------------|
+// | |
+// Descent - |--------------------------------
+// | - Internal Leading (in descent)
+// -------------------------------------------
+LayoutUnit SimpleFontData::EmHeightAscent(FontBaseline baseline_type) const {
+ if (baseline_type == kAlphabeticBaseline) {
+ if (!em_height_ascent_)
+ ComputeEmHeightMetrics();
+ return em_height_ascent_;
+ }
+ LayoutUnit em_height = LayoutUnit::FromFloatRound(PlatformData().size());
+ return em_height - em_height / 2;
+}
+
+LayoutUnit SimpleFontData::EmHeightDescent(FontBaseline baseline_type) const {
+ if (baseline_type == kAlphabeticBaseline) {
+ if (!em_height_descent_)
+ ComputeEmHeightMetrics();
+ return em_height_descent_;
+ }
+ LayoutUnit em_height = LayoutUnit::FromFloatRound(PlatformData().size());
+ return em_height / 2;
+}
+
+static std::pair<int16_t, int16_t> TypoAscenderAndDescender(
+ SkTypeface* typeface) {
+ // TODO(kojii): This should move to Skia once finalized. We can then move
+ // EmHeightAscender/Descender to FontMetrics.
+ int16_t buffer[2];
+ size_t size = typeface->getTableData(SkSetFourByteTag('O', 'S', '/', '2'), 68,
+ sizeof(buffer), buffer);
+ if (size == sizeof(buffer)) {
+ return std::make_pair(static_cast<int16_t>(ntohs(buffer[0])),
+ -static_cast<int16_t>(ntohs(buffer[1])));
+ }
+ return std::make_pair(0, 0);
+}
+
+void SimpleFontData::ComputeEmHeightMetrics() const {
+ // Compute em height metrics from OS/2 sTypoAscender and sTypoDescender.
+ SkTypeface* typeface = platform_data_.Typeface();
+ int16_t typo_ascender, typo_descender;
+ std::tie(typo_ascender, typo_descender) = TypoAscenderAndDescender(typeface);
+ if (typo_ascender > 0 &&
+ NormalizeEmHeightMetrics(typo_ascender, typo_ascender + typo_descender)) {
+ return;
+ }
+
+ // As the last resort, compute em height metrics from our ascent/descent.
+ const FontMetrics& font_metrics = GetFontMetrics();
+ if (NormalizeEmHeightMetrics(font_metrics.FloatAscent(),
+ font_metrics.FloatHeight())) {
+ return;
+ }
+ NOTREACHED();
+}
+
+bool SimpleFontData::NormalizeEmHeightMetrics(float ascent,
+ float height) const {
+ if (height <= 0 || ascent < 0 || ascent > height)
+ return false;
+ // While the OpenType specification recommends the sum of sTypoAscender and
+ // sTypoDescender to equal 1em, most fonts do not follow. Most Latin fonts
+ // set to smaller than 1em, and many tall scripts set to larger than 1em.
+ // https://www.microsoft.com/typography/otspec/recom.htm#OS2
+ // To ensure the sum of ascent and descent is the "em height", normalize by
+ // keeping the ratio of sTypoAscender:sTypoDescender.
+ // This matches to how Gecko computes "em height":
+ // https://github.com/whatwg/html/issues/2470#issuecomment-291425136
+ float em_height = PlatformData().size();
+ em_height_ascent_ = LayoutUnit::FromFloatRound(ascent * em_height / height);
+ em_height_descent_ =
+ LayoutUnit::FromFloatRound(em_height) - em_height_ascent_;
+ return true;
+}
+
+LayoutUnit SimpleFontData::VerticalPosition(
+ FontVerticalPositionType position_type,
+ FontBaseline baseline_type) const {
+ switch (position_type) {
+ case FontVerticalPositionType::TextTop:
+ // Use Ascent, not FixedAscent, to match to how painter computes the
+ // baseline position.
+ return LayoutUnit(GetFontMetrics().Ascent(baseline_type));
+ case FontVerticalPositionType::TextBottom:
+ return LayoutUnit(-GetFontMetrics().Descent(baseline_type));
+ case FontVerticalPositionType::TopOfEmHeight:
+ return EmHeightAscent(baseline_type);
+ case FontVerticalPositionType::BottomOfEmHeight:
+ return -EmHeightDescent(baseline_type);
+ }
+ NOTREACHED();
+ return LayoutUnit();
+}
+
+FloatRect SimpleFontData::PlatformBoundsForGlyph(Glyph glyph) const {
+ if (!platform_data_.size())
+ return FloatRect();
+
+ static_assert(sizeof(glyph) == 2, "Glyph id should not be truncated.");
+
+ SkRect bounds;
+ SkiaTextMetrics(&paint_).GetSkiaBoundsForGlyph(glyph, &bounds);
+ return FloatRect(bounds);
+}
+
+float SimpleFontData::PlatformWidthForGlyph(Glyph glyph) const {
+ if (!platform_data_.size())
+ return 0;
+
+ static_assert(sizeof(glyph) == 2, "Glyph id should not be truncated.");
+
+ return SkiaTextMetrics(&paint_).GetSkiaWidthForGlyph(glyph);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/simple_font_data.h b/chromium/third_party/blink/renderer/platform/fonts/simple_font_data.h
new file mode 100644
index 00000000000..f1a8a20d626
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/simple_font_data.h
@@ -0,0 +1,250 @@
+/*
+ * This file is part of the internal font implementation.
+ *
+ * Copyright (C) 2006, 2008, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2007-2008 Torch Mobile, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SIMPLE_FONT_DATA_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SIMPLE_FONT_DATA_H_
+
+#include <SkPaint.h>
+
+#include <memory>
+#include <utility>
+
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/fonts/canvas_rotation_in_vertical.h"
+#include "third_party/blink/renderer/platform/fonts/custom_font_data.h"
+#include "third_party/blink/renderer/platform/fonts/font_baseline.h"
+#include "third_party/blink/renderer/platform/fonts/font_data.h"
+#include "third_party/blink/renderer/platform/fonts/font_metrics.h"
+#include "third_party/blink/renderer/platform/fonts/font_platform_data.h"
+#include "third_party/blink/renderer/platform/fonts/font_vertical_position_type.h"
+#include "third_party/blink/renderer/platform/fonts/typesetting_features.h"
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+
+#if defined(OS_MACOSX)
+#include "third_party/blink/renderer/platform/fonts/glyph_metrics_map.h"
+#endif
+
+namespace blink {
+
+// Holds the glyph index and the corresponding SimpleFontData information for a
+// given
+// character.
+struct GlyphData {
+ STACK_ALLOCATED();
+ GlyphData(
+ Glyph g = 0,
+ const SimpleFontData* f = nullptr,
+ CanvasRotationInVertical rotation = CanvasRotationInVertical::kRegular)
+ : glyph(g), font_data(f), canvas_rotation(rotation) {}
+ Glyph glyph;
+ const SimpleFontData* font_data;
+ CanvasRotationInVertical canvas_rotation;
+};
+
+class FontDescription;
+
+class PLATFORM_EXPORT SimpleFontData : public FontData {
+ public:
+ // Used to create platform fonts.
+ static scoped_refptr<SimpleFontData> Create(
+ const FontPlatformData& platform_data,
+ scoped_refptr<CustomFontData> custom_data = nullptr,
+ bool subpixel_ascent_descent = false) {
+ return base::AdoptRef(new SimpleFontData(
+ platform_data, std::move(custom_data), subpixel_ascent_descent));
+ }
+
+ const FontPlatformData& PlatformData() const { return platform_data_; }
+
+ scoped_refptr<SimpleFontData> SmallCapsFontData(const FontDescription&) const;
+ scoped_refptr<SimpleFontData> EmphasisMarkFontData(const FontDescription&) const;
+
+ FontMetrics& GetFontMetrics() { return font_metrics_; }
+ const FontMetrics& GetFontMetrics() const { return font_metrics_; }
+ float SizePerUnit() const {
+ return PlatformData().size() /
+ (GetFontMetrics().UnitsPerEm() ? GetFontMetrics().UnitsPerEm() : 1);
+ }
+ float InternalLeading() const {
+ return GetFontMetrics().FloatHeight() - PlatformData().size();
+ }
+
+ // "em height" metrics.
+ // https://drafts.css-houdini.org/font-metrics-api-1/#fontmetrics
+ LayoutUnit EmHeightAscent(FontBaseline = kAlphabeticBaseline) const;
+ LayoutUnit EmHeightDescent(FontBaseline = kAlphabeticBaseline) const;
+
+ LayoutUnit VerticalPosition(FontVerticalPositionType, FontBaseline) const;
+
+ float MaxCharWidth() const { return max_char_width_; }
+ void SetMaxCharWidth(float max_char_width) {
+ max_char_width_ = max_char_width;
+ }
+
+ float AvgCharWidth() const { return avg_char_width_; }
+ void SetAvgCharWidth(float avg_char_width) {
+ avg_char_width_ = avg_char_width;
+ }
+
+ FloatRect BoundsForGlyph(Glyph) const;
+ FloatRect PlatformBoundsForGlyph(Glyph) const;
+ float WidthForGlyph(Glyph) const;
+ float PlatformWidthForGlyph(Glyph) const;
+
+ float SpaceWidth() const { return space_width_; }
+ void SetSpaceWidth(float space_width) { space_width_ = space_width; }
+
+ Glyph SpaceGlyph() const { return space_glyph_; }
+ void SetSpaceGlyph(Glyph space_glyph) { space_glyph_ = space_glyph; }
+ Glyph ZeroGlyph() const { return zero_glyph_; }
+ void SetZeroGlyph(Glyph zero_glyph) { zero_glyph_ = zero_glyph; }
+
+ const SimpleFontData* FontDataForCharacter(UChar32) const override;
+
+ Glyph GlyphForCharacter(UChar32) const;
+
+ bool IsCustomFont() const override { return custom_font_data_.get(); }
+ bool IsLoading() const override {
+ return custom_font_data_ ? custom_font_data_->IsLoading() : false;
+ }
+ bool IsLoadingFallback() const override {
+ return custom_font_data_ ? custom_font_data_->IsLoadingFallback() : false;
+ }
+ bool IsSegmented() const override;
+ bool ShouldSkipDrawing() const override {
+ return custom_font_data_ && custom_font_data_->ShouldSkipDrawing();
+ }
+
+ CustomFontData* GetCustomFontData() const { return custom_font_data_.get(); }
+
+ unsigned VisualOverflowInflationForAscent() const {
+ return visual_overflow_inflation_for_ascent_;
+ }
+ unsigned VisualOverflowInflationForDescent() const {
+ return visual_overflow_inflation_for_descent_;
+ }
+
+ protected:
+ SimpleFontData(const FontPlatformData&,
+ scoped_refptr<CustomFontData> custom_data,
+ bool subpixel_ascent_descent = false);
+
+ private:
+ void PlatformInit(bool subpixel_ascent_descent);
+ void PlatformGlyphInit();
+
+ scoped_refptr<SimpleFontData> CreateScaledFontData(const FontDescription&,
+ float scale_factor) const;
+
+ void ComputeEmHeightMetrics() const;
+ bool NormalizeEmHeightMetrics(float, float) const;
+
+ FontMetrics font_metrics_;
+ float max_char_width_;
+ float avg_char_width_;
+
+ FontPlatformData platform_data_;
+ SkPaint paint_;
+
+ Glyph space_glyph_;
+ float space_width_;
+ Glyph zero_glyph_;
+
+ struct DerivedFontData {
+ USING_FAST_MALLOC(DerivedFontData);
+ WTF_MAKE_NONCOPYABLE(DerivedFontData);
+
+ public:
+ static std::unique_ptr<DerivedFontData> Create();
+
+ scoped_refptr<SimpleFontData> small_caps;
+ scoped_refptr<SimpleFontData> emphasis_mark;
+
+ private:
+ DerivedFontData() = default;
+ };
+
+ mutable std::unique_ptr<DerivedFontData> derived_font_data_;
+
+ scoped_refptr<CustomFontData> custom_font_data_;
+
+ // These are set to non-zero when ascent or descent is rounded or shifted
+ // to be smaller than the actual ascent or descent. When calculating visual
+ // overflows, we should add the inflations.
+ unsigned visual_overflow_inflation_for_ascent_;
+ unsigned visual_overflow_inflation_for_descent_;
+
+ mutable LayoutUnit em_height_ascent_;
+ mutable LayoutUnit em_height_descent_;
+
+// See discussion on crbug.com/631032 and Skiaissue
+// https://bugs.chromium.org/p/skia/issues/detail?id=5328 :
+// On Mac we're still using path based glyph metrics, and they seem to be
+// too slow to be able to remove the caching layer we have here.
+#if defined(OS_MACOSX)
+ mutable std::unique_ptr<GlyphMetricsMap<FloatRect>> glyph_to_bounds_map_;
+ mutable GlyphMetricsMap<float> glyph_to_width_map_;
+#endif
+};
+
+ALWAYS_INLINE FloatRect SimpleFontData::BoundsForGlyph(Glyph glyph) const {
+#if !defined(OS_MACOSX)
+ return PlatformBoundsForGlyph(glyph);
+#else
+ FloatRect bounds_result;
+ if (glyph_to_bounds_map_) {
+ bounds_result = glyph_to_bounds_map_->MetricsForGlyph(glyph);
+ if (bounds_result.Width() != kCGlyphSizeUnknown)
+ return bounds_result;
+ }
+
+ bounds_result = PlatformBoundsForGlyph(glyph);
+ if (!glyph_to_bounds_map_)
+ glyph_to_bounds_map_ = std::make_unique<GlyphMetricsMap<FloatRect>>();
+ glyph_to_bounds_map_->SetMetricsForGlyph(glyph, bounds_result);
+
+ return bounds_result;
+#endif
+}
+
+ALWAYS_INLINE float SimpleFontData::WidthForGlyph(Glyph glyph) const {
+#if !defined(OS_MACOSX)
+ return PlatformWidthForGlyph(glyph);
+#else
+ float width = glyph_to_width_map_.MetricsForGlyph(glyph);
+ if (width != kCGlyphSizeUnknown)
+ return width;
+
+ width = PlatformWidthForGlyph(glyph);
+
+ glyph_to_width_map_.SetMetricsForGlyph(glyph, width);
+ return width;
+#endif
+}
+
+DEFINE_FONT_DATA_TYPE_CASTS(SimpleFontData, false);
+
+} // namespace blink
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SIMPLE_FONT_DATA_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/skia/font_cache_skia.cc b/chromium/third_party/blink/renderer/platform/fonts/skia/font_cache_skia.cc
new file mode 100644
index 00000000000..9b6f99ce9e5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/skia/font_cache_skia.cc
@@ -0,0 +1,305 @@
+
+/*
+ * Copyright (c) 2006, 2007, 2008, 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 <unicode/locid.h>
+
+#include <memory>
+#include <utility>
+
+#include "SkFontMgr.h"
+#include "SkStream.h"
+#include "SkTypeface.h"
+
+#include "build/build_config.h"
+#include "third_party/blink/public/platform/linux/web_sandbox_support.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/font_family_names.h"
+#include "third_party/blink/renderer/platform/fonts/alternate_font_family.h"
+#include "third_party/blink/renderer/platform/fonts/bitmap_glyphs_blacklist.h"
+#include "third_party/blink/renderer/platform/fonts/font_cache.h"
+#include "third_party/blink/renderer/platform/fonts/font_description.h"
+#include "third_party/blink/renderer/platform/fonts/font_face_creation_params.h"
+#include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
+#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
+#include "third_party/blink/renderer/platform/language.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+
+namespace blink {
+
+AtomicString ToAtomicString(const SkString& str) {
+ return AtomicString::FromUTF8(str.c_str(), str.size());
+}
+
+#if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_FUCHSIA)
+// Android special locale for retrieving the color emoji font
+// based on the proposed changes in UTR #51 for introducing
+// an Emoji script code:
+// http://www.unicode.org/reports/tr51/proposed.html#Emoji_Script
+static const char kAndroidColorEmojiLocale[] = "und-Zsye";
+
+// This function is called on android or when we are emulating android fonts on
+// linux and the embedder has overriden the default fontManager with
+// WebFontRendering::setSkiaFontMgr.
+// static
+AtomicString FontCache::GetFamilyNameForCharacter(
+ SkFontMgr* fm,
+ UChar32 c,
+ const FontDescription& font_description,
+ FontFallbackPriority fallback_priority) {
+ DCHECK(fm);
+
+ const size_t kMaxLocales = 4;
+ const char* bcp47_locales[kMaxLocales];
+ size_t locale_count = 0;
+
+ // Fill in the list of locales in the reverse priority order.
+ // Skia expects the highest array index to be the first priority.
+ const LayoutLocale* content_locale = font_description.Locale();
+ if (const LayoutLocale* han_locale =
+ LayoutLocale::LocaleForHan(content_locale))
+ bcp47_locales[locale_count++] = han_locale->LocaleForHanForSkFontMgr();
+ bcp47_locales[locale_count++] =
+ LayoutLocale::GetDefault().LocaleForSkFontMgr();
+ if (content_locale)
+ bcp47_locales[locale_count++] = content_locale->LocaleForSkFontMgr();
+ if (fallback_priority == FontFallbackPriority::kEmojiEmoji)
+ bcp47_locales[locale_count++] = kAndroidColorEmojiLocale;
+ SECURITY_DCHECK(locale_count <= kMaxLocales);
+ sk_sp<SkTypeface> typeface(fm->matchFamilyStyleCharacter(
+ nullptr, SkFontStyle(), bcp47_locales, locale_count, c));
+ if (!typeface)
+ return g_empty_atom;
+
+ SkString skia_family_name;
+ typeface->getFamilyName(&skia_family_name);
+ return ToAtomicString(skia_family_name);
+}
+#endif // defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_FUCHSIA)
+
+void FontCache::PlatformInit() {}
+
+scoped_refptr<SimpleFontData> FontCache::FallbackOnStandardFontStyle(
+ const FontDescription& font_description,
+ UChar32 character) {
+ FontDescription substitute_description(font_description);
+ substitute_description.SetStyle(NormalSlopeValue());
+ substitute_description.SetWeight(NormalWeightValue());
+
+ FontFaceCreationParams creation_params(
+ substitute_description.Family().Family());
+ FontPlatformData* substitute_platform_data =
+ GetFontPlatformData(substitute_description, creation_params);
+ if (substitute_platform_data &&
+ substitute_platform_data->FontContainsCharacter(character)) {
+ FontPlatformData platform_data =
+ FontPlatformData(*substitute_platform_data);
+ platform_data.SetSyntheticBold(font_description.Weight() >=
+ BoldThreshold());
+ platform_data.SetSyntheticItalic(font_description.Style() ==
+ ItalicSlopeValue());
+ return FontDataFromFontPlatformData(&platform_data, kDoNotRetain);
+ }
+
+ return nullptr;
+}
+
+scoped_refptr<SimpleFontData> FontCache::GetLastResortFallbackFont(
+ const FontDescription& description,
+ ShouldRetain should_retain) {
+ const FontFaceCreationParams fallback_creation_params(
+ GetFallbackFontFamily(description));
+ const FontPlatformData* font_platform_data = GetFontPlatformData(
+ description, fallback_creation_params, AlternateFontName::kLastResort);
+
+ // We should at least have Sans or Arial which is the last resort fallback of
+ // SkFontHost ports.
+ if (!font_platform_data) {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(const FontFaceCreationParams,
+ sans_creation_params,
+ (FontFamilyNames::Sans));
+ font_platform_data = GetFontPlatformData(description, sans_creation_params,
+ AlternateFontName::kLastResort);
+ }
+ if (!font_platform_data) {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(const FontFaceCreationParams,
+ arial_creation_params,
+ (FontFamilyNames::Arial));
+ font_platform_data = GetFontPlatformData(description, arial_creation_params,
+ AlternateFontName::kLastResort);
+ }
+#if defined(OS_WIN)
+ // Try some more Windows-specific fallbacks.
+ if (!font_platform_data) {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(const FontFaceCreationParams,
+ msuigothic_creation_params,
+ (FontFamilyNames::MS_UI_Gothic));
+ font_platform_data =
+ GetFontPlatformData(description, msuigothic_creation_params,
+ AlternateFontName::kLastResort);
+ }
+ if (!font_platform_data) {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(const FontFaceCreationParams,
+ mssansserif_creation_params,
+ (FontFamilyNames::Microsoft_Sans_Serif));
+ font_platform_data =
+ GetFontPlatformData(description, mssansserif_creation_params,
+ AlternateFontName::kLastResort);
+ }
+ if (!font_platform_data) {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(const FontFaceCreationParams,
+ segoeui_creation_params,
+ (FontFamilyNames::Segoe_UI));
+ font_platform_data = GetFontPlatformData(
+ description, segoeui_creation_params, AlternateFontName::kLastResort);
+ }
+ if (!font_platform_data) {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(const FontFaceCreationParams,
+ calibri_creation_params,
+ (FontFamilyNames::Calibri));
+ font_platform_data = GetFontPlatformData(
+ description, calibri_creation_params, AlternateFontName::kLastResort);
+ }
+ if (!font_platform_data) {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(const FontFaceCreationParams,
+ timesnewroman_creation_params,
+ (FontFamilyNames::Times_New_Roman));
+ font_platform_data =
+ GetFontPlatformData(description, timesnewroman_creation_params,
+ AlternateFontName::kLastResort);
+ }
+ if (!font_platform_data) {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(const FontFaceCreationParams,
+ couriernew_creation_params,
+ (FontFamilyNames::Courier_New));
+ font_platform_data =
+ GetFontPlatformData(description, couriernew_creation_params,
+ AlternateFontName::kLastResort);
+ }
+#endif
+
+ DCHECK(font_platform_data);
+ return FontDataFromFontPlatformData(font_platform_data, should_retain);
+}
+
+PaintTypeface FontCache::CreateTypeface(
+ const FontDescription& font_description,
+ const FontFaceCreationParams& creation_params,
+ CString& name) {
+#if !defined(OS_WIN) && !defined(OS_ANDROID) && !defined(OS_FUCHSIA)
+ // TODO(fuchsia): Revisit this and other font code for Fuchsia.
+
+ if (creation_params.CreationType() == kCreateFontByFciIdAndTtcIndex) {
+ if (Platform::Current()->GetSandboxSupport()) {
+ return PaintTypeface::FromFontConfigInterfaceIdAndTtcIndex(
+ creation_params.FontconfigInterfaceId(), creation_params.TtcIndex());
+ }
+ return PaintTypeface::FromFilenameAndTtcIndex(
+ creation_params.Filename().data(), creation_params.TtcIndex());
+ }
+#endif
+
+ AtomicString family = creation_params.Family();
+ DCHECK_NE(family, FontFamilyNames::system_ui);
+ // If we're creating a fallback font (e.g. "-webkit-monospace"), convert the
+ // name into the fallback name (like "monospace") that fontconfig understands.
+ if (!family.length() || family.StartsWith("-webkit-")) {
+ name = GetFallbackFontFamily(font_description).GetString().Utf8();
+ } else {
+ // convert the name to utf8
+ name = family.Utf8();
+ }
+
+#if defined(OS_WIN)
+ // TODO(vmpstr): Deal with paint typeface here.
+ if (sideloaded_fonts_) {
+ HashMap<String, sk_sp<SkTypeface>>::iterator sideloaded_font =
+ sideloaded_fonts_->find(name.data());
+ if (sideloaded_font != sideloaded_fonts_->end())
+ return PaintTypeface::FromSkTypeface(sideloaded_font->value);
+ }
+#endif
+
+#if defined(OS_LINUX) || defined(OS_WIN)
+ // On linux if the fontManager has been overridden then we should be calling
+ // the embedder provided font Manager rather than calling
+ // SkTypeface::CreateFromName which may redirect the call to the default font
+ // Manager. On Windows the font manager is always present.
+ if (font_manager_) {
+ // TODO(vmpstr): Handle creating paint typefaces here directly. We need to
+ // figure out whether it's safe to give |font_manager_| to PaintTypeface and
+ // what that means on the GPU side.
+ auto tf = sk_sp<SkTypeface>(font_manager_->matchFamilyStyle(
+ name.data(), font_description.SkiaFontStyle()));
+ return PaintTypeface::FromSkTypeface(std::move(tf));
+ }
+#endif
+
+ // FIXME: Use m_fontManager, matchFamilyStyle instead of
+ // legacyCreateTypeface on all platforms.
+ return PaintTypeface::FromFamilyNameAndFontStyle(
+ name.data(), font_description.SkiaFontStyle());
+}
+
+#if !defined(OS_WIN)
+std::unique_ptr<FontPlatformData> FontCache::CreateFontPlatformData(
+ const FontDescription& font_description,
+ const FontFaceCreationParams& creation_params,
+ float font_size,
+ AlternateFontName) {
+ CString name;
+ PaintTypeface paint_tf =
+ CreateTypeface(font_description, creation_params, name);
+ if (!paint_tf)
+ return nullptr;
+
+ const auto& tf = paint_tf.ToSkTypeface();
+ std::unique_ptr<FontPlatformData> font_platform_data =
+ std::make_unique<FontPlatformData>(
+ paint_tf, name, font_size,
+ (font_description.Weight() >
+ FontSelectionValue(200) +
+ FontSelectionValue(tf->fontStyle().weight()) ||
+ font_description.IsSyntheticBold()),
+ ((font_description.Style() == ItalicSlopeValue()) &&
+ !tf->isItalic()) ||
+ font_description.IsSyntheticItalic(),
+ font_description.Orientation());
+
+ font_platform_data->SetAvoidEmbeddedBitmaps(
+ BitmapGlyphsBlacklist::AvoidEmbeddedBitmapsForTypeface(tf.get()));
+
+ return font_platform_data;
+}
+#endif // !defined(OS_WIN)
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/skia/skia_text_metrics.cc b/chromium/third_party/blink/renderer/platform/fonts/skia/skia_text_metrics.cc
new file mode 100644
index 00000000000..47ee36d7fae
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/skia/skia_text_metrics.cc
@@ -0,0 +1,91 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/skia/skia_text_metrics.h"
+
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+#include <SkPath.h>
+
+namespace blink {
+
+SkiaTextMetrics::SkiaTextMetrics(const SkPaint* paint) : paint_(paint) {
+ CHECK(paint_->getTextEncoding() == SkPaint::kGlyphID_TextEncoding);
+}
+
+void SkiaTextMetrics::GetGlyphWidthForHarfBuzz(hb_codepoint_t codepoint,
+ hb_position_t* width) {
+ DCHECK_LE(codepoint, 0xFFFFu);
+ CHECK(width);
+
+ SkScalar sk_width;
+ uint16_t glyph = codepoint;
+
+ paint_->getTextWidths(&glyph, sizeof(glyph), &sk_width, nullptr);
+ if (!paint_->isSubpixelText())
+ sk_width = SkScalarRoundToInt(sk_width);
+ *width = SkiaScalarToHarfBuzzPosition(sk_width);
+}
+
+void SkiaTextMetrics::GetGlyphExtentsForHarfBuzz(hb_codepoint_t codepoint,
+ hb_glyph_extents_t* extents) {
+ DCHECK_LE(codepoint, 0xFFFFu);
+ CHECK(extents);
+
+ SkRect sk_bounds;
+ uint16_t glyph = codepoint;
+
+ paint_->getTextWidths(&glyph, sizeof(glyph), nullptr, &sk_bounds);
+ if (!paint_->isSubpixelText()) {
+ // Use roundOut() rather than round() to avoid rendering glyphs
+ // outside the visual overflow rect. crbug.com/452914.
+ SkIRect ir;
+ sk_bounds.roundOut(&ir);
+ sk_bounds.set(ir);
+ }
+
+ // Invert y-axis because Skia is y-grows-down but we set up HarfBuzz to be
+ // y-grows-up.
+ extents->x_bearing = SkiaScalarToHarfBuzzPosition(sk_bounds.fLeft);
+ extents->y_bearing = SkiaScalarToHarfBuzzPosition(-sk_bounds.fTop);
+ extents->width = SkiaScalarToHarfBuzzPosition(sk_bounds.width());
+ extents->height = SkiaScalarToHarfBuzzPosition(-sk_bounds.height());
+}
+
+void SkiaTextMetrics::GetSkiaBoundsForGlyph(Glyph glyph, SkRect* bounds) {
+#if defined(OS_MACOSX)
+ // TODO(drott): Remove this once we have better metrics bounds
+ // on Mac, https://bugs.chromium.org/p/skia/issues/detail?id=5328
+ SkPath path;
+ paint_->getTextPath(&glyph, sizeof(glyph), 0, 0, &path);
+ *bounds = path.getBounds();
+#else
+ paint_->getTextWidths(&glyph, sizeof(glyph), nullptr, bounds);
+#endif
+
+ if (!paint_->isSubpixelText()) {
+ SkIRect ir;
+ bounds->roundOut(&ir);
+ bounds->set(ir);
+ }
+}
+
+float SkiaTextMetrics::GetSkiaWidthForGlyph(Glyph glyph) {
+ SkScalar sk_width;
+ paint_->getTextWidths(&glyph, sizeof(glyph), &sk_width, nullptr);
+
+ if (!paint_->isSubpixelText())
+ sk_width = SkScalarRoundToInt(sk_width);
+
+ return SkScalarToFloat(sk_width);
+}
+
+hb_position_t SkiaTextMetrics::SkiaScalarToHarfBuzzPosition(SkScalar value) {
+ // We treat HarfBuzz hb_position_t as 16.16 fixed-point.
+ static const int kHbPosition1 = 1 << 16;
+ return clampTo<int>(value * kHbPosition1);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/skia/skia_text_metrics.h b/chromium/third_party/blink/renderer/platform/fonts/skia/skia_text_metrics.h
new file mode 100644
index 00000000000..e8a38f94142
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/skia/skia_text_metrics.h
@@ -0,0 +1,33 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SKIA_SKIA_TEXT_METRICS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SKIA_SKIA_TEXT_METRICS_H_
+
+#include "third_party/blink/renderer/platform/fonts/glyph.h"
+
+#include <SkPaint.h>
+#include <hb.h>
+
+namespace blink {
+
+class SkiaTextMetrics final {
+ public:
+ SkiaTextMetrics(const SkPaint*);
+
+ void GetGlyphWidthForHarfBuzz(hb_codepoint_t, hb_position_t* width);
+ void GetGlyphExtentsForHarfBuzz(hb_codepoint_t, hb_glyph_extents_t*);
+
+ void GetSkiaBoundsForGlyph(Glyph, SkRect* bounds);
+ float GetSkiaWidthForGlyph(Glyph);
+
+ static hb_position_t SkiaScalarToHarfBuzzPosition(SkScalar value);
+
+ private:
+ const SkPaint* paint_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/fonts/small_caps_iterator.cc b/chromium/third_party/blink/renderer/platform/fonts/small_caps_iterator.cc
new file mode 100644
index 00000000000..7e5c27f9f78
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/small_caps_iterator.cc
@@ -0,0 +1,50 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/small_caps_iterator.h"
+
+#include <unicode/utypes.h>
+#include <memory>
+
+namespace blink {
+
+SmallCapsIterator::SmallCapsIterator(const UChar* buffer, unsigned buffer_size)
+ : utf16_iterator_(std::make_unique<UTF16TextIterator>(buffer, buffer_size)),
+ buffer_size_(buffer_size),
+ next_u_char32_(0),
+ at_end_(buffer_size == 0),
+ current_small_caps_behavior_(kSmallCapsInvalid) {}
+
+bool SmallCapsIterator::Consume(unsigned* caps_limit,
+ SmallCapsBehavior* small_caps_behavior) {
+ if (at_end_)
+ return false;
+
+ while (utf16_iterator_->Consume(next_u_char32_)) {
+ previous_small_caps_behavior_ = current_small_caps_behavior_;
+ // Skipping over combining marks, as these combine with the small-caps
+ // uppercased text as well and we do not need to split by their
+ // individual case-ness.
+ if (!u_getCombiningClass(next_u_char32_)) {
+ current_small_caps_behavior_ =
+ u_hasBinaryProperty(next_u_char32_, UCHAR_CHANGES_WHEN_UPPERCASED)
+ ? kSmallCapsUppercaseNeeded
+ : kSmallCapsSameCase;
+ }
+
+ if (previous_small_caps_behavior_ != current_small_caps_behavior_ &&
+ previous_small_caps_behavior_ != kSmallCapsInvalid) {
+ *caps_limit = utf16_iterator_->Offset();
+ *small_caps_behavior = previous_small_caps_behavior_;
+ return true;
+ }
+ utf16_iterator_->Advance();
+ }
+ *caps_limit = buffer_size_;
+ *small_caps_behavior = current_small_caps_behavior_;
+ at_end_ = true;
+ return true;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/small_caps_iterator.h b/chromium/third_party/blink/renderer/platform/fonts/small_caps_iterator.h
new file mode 100644
index 00000000000..6753fcaf6d0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/small_caps_iterator.h
@@ -0,0 +1,44 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SMALL_CAPS_ITERATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SMALL_CAPS_ITERATOR_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/fonts/font_orientation.h"
+#include "third_party/blink/renderer/platform/fonts/script_run_iterator.h"
+#include "third_party/blink/renderer/platform/fonts/utf16_text_iterator.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT SmallCapsIterator {
+ USING_FAST_MALLOC(SmallCapsIterator);
+ WTF_MAKE_NONCOPYABLE(SmallCapsIterator);
+
+ public:
+ enum SmallCapsBehavior {
+ kSmallCapsSameCase,
+ kSmallCapsUppercaseNeeded,
+ kSmallCapsInvalid
+ };
+
+ SmallCapsIterator(const UChar* buffer, unsigned buffer_size);
+
+ bool Consume(unsigned* caps_limit, SmallCapsBehavior*);
+
+ private:
+ std::unique_ptr<UTF16TextIterator> utf16_iterator_;
+ unsigned buffer_size_;
+ UChar32 next_u_char32_;
+ bool at_end_;
+
+ SmallCapsBehavior current_small_caps_behavior_;
+ SmallCapsBehavior previous_small_caps_behavior_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/fonts/small_caps_iterator_test.cc b/chromium/third_party/blink/renderer/platform/fonts/small_caps_iterator_test.cc
new file mode 100644
index 00000000000..be0bbc64bd3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/small_caps_iterator_test.cc
@@ -0,0 +1,117 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/small_caps_iterator.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include <string>
+
+namespace blink {
+
+struct SmallCapsTestRun {
+ std::string text;
+ SmallCapsIterator::SmallCapsBehavior code;
+};
+
+struct SmallCapsExpectedRun {
+ unsigned limit;
+ SmallCapsIterator::SmallCapsBehavior small_caps_behavior;
+
+ SmallCapsExpectedRun(
+ unsigned the_limit,
+ SmallCapsIterator::SmallCapsBehavior the_small_caps_behavior)
+ : limit(the_limit), small_caps_behavior(the_small_caps_behavior) {}
+};
+
+class SmallCapsIteratorTest : public testing::Test {
+ protected:
+ void CheckRuns(const Vector<SmallCapsTestRun>& runs) {
+ String text(g_empty_string16_bit);
+ Vector<SmallCapsExpectedRun> expect;
+ for (auto& run : runs) {
+ text.append(String::FromUTF8(run.text.c_str()));
+ expect.push_back(SmallCapsExpectedRun(text.length(), run.code));
+ }
+ SmallCapsIterator small_caps_iterator(text.Characters16(), text.length());
+ VerifyRuns(&small_caps_iterator, expect);
+ }
+
+ void VerifyRuns(SmallCapsIterator* small_caps_iterator,
+ const Vector<SmallCapsExpectedRun>& expect) {
+ unsigned limit;
+ SmallCapsIterator::SmallCapsBehavior small_caps_behavior;
+ unsigned long run_count = 0;
+ while (small_caps_iterator->Consume(&limit, &small_caps_behavior)) {
+ ASSERT_LT(run_count, expect.size());
+ ASSERT_EQ(expect[run_count].limit, limit);
+ ASSERT_EQ(expect[run_count].small_caps_behavior, small_caps_behavior);
+ ++run_count;
+ }
+ ASSERT_EQ(expect.size(), run_count);
+ }
+};
+
+// Some of our compilers cannot initialize a vector from an array yet.
+#define DECLARE_SMALL_CAPS_RUNSVECTOR(...) \
+ static const SmallCapsTestRun kRunsArray[] = __VA_ARGS__; \
+ Vector<SmallCapsTestRun> runs; \
+ runs.Append(kRunsArray, sizeof(kRunsArray) / sizeof(*kRunsArray));
+
+#define CHECK_SMALL_CAPS_RUN(...) \
+ DECLARE_SMALL_CAPS_RUNSVECTOR(__VA_ARGS__); \
+ CheckRuns(runs);
+
+TEST_F(SmallCapsIteratorTest, Empty) {
+ String empty(g_empty_string16_bit);
+ SmallCapsIterator small_caps_iterator(empty.Characters16(), empty.length());
+ unsigned limit = 0;
+ SmallCapsIterator::SmallCapsBehavior small_caps_behavior =
+ SmallCapsIterator::kSmallCapsInvalid;
+ DCHECK(!small_caps_iterator.Consume(&limit, &small_caps_behavior));
+ ASSERT_EQ(limit, 0u);
+ ASSERT_EQ(small_caps_behavior, SmallCapsIterator::kSmallCapsInvalid);
+}
+
+TEST_F(SmallCapsIteratorTest, UppercaseA) {
+ CHECK_SMALL_CAPS_RUN({{"A", SmallCapsIterator::kSmallCapsSameCase}});
+}
+
+TEST_F(SmallCapsIteratorTest, LowercaseA) {
+ CHECK_SMALL_CAPS_RUN({{"a", SmallCapsIterator::kSmallCapsUppercaseNeeded}});
+}
+
+TEST_F(SmallCapsIteratorTest, UppercaseLowercaseA) {
+ CHECK_SMALL_CAPS_RUN({{"A", SmallCapsIterator::kSmallCapsSameCase},
+ {"a", SmallCapsIterator::kSmallCapsUppercaseNeeded}});
+}
+
+TEST_F(SmallCapsIteratorTest, UppercasePunctuationMixed) {
+ CHECK_SMALL_CAPS_RUN({{"AAA??", SmallCapsIterator::kSmallCapsSameCase}});
+}
+
+TEST_F(SmallCapsIteratorTest, LowercasePunctuationMixed) {
+ CHECK_SMALL_CAPS_RUN({{"aaa", SmallCapsIterator::kSmallCapsUppercaseNeeded},
+ {"===", SmallCapsIterator::kSmallCapsSameCase}});
+}
+
+TEST_F(SmallCapsIteratorTest, LowercasePunctuationInterleaved) {
+ CHECK_SMALL_CAPS_RUN({{"aaa", SmallCapsIterator::kSmallCapsUppercaseNeeded},
+ {"===", SmallCapsIterator::kSmallCapsSameCase},
+ {"bbb", SmallCapsIterator::kSmallCapsUppercaseNeeded}});
+}
+
+TEST_F(SmallCapsIteratorTest, Japanese) {
+ CHECK_SMALL_CAPS_RUN({{"ほへと", SmallCapsIterator::kSmallCapsSameCase}});
+}
+
+TEST_F(SmallCapsIteratorTest, Armenian) {
+ CHECK_SMALL_CAPS_RUN({{"աբգդ", SmallCapsIterator::kSmallCapsUppercaseNeeded},
+ {"ԵԶԷԸ", SmallCapsIterator::kSmallCapsSameCase}});
+}
+
+TEST_F(SmallCapsIteratorTest, CombiningCharacterSequence) {
+ CHECK_SMALL_CAPS_RUN({{"èü", SmallCapsIterator::kSmallCapsUppercaseNeeded}});
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/symbols_iterator.cc b/chromium/third_party/blink/renderer/platform/fonts/symbols_iterator.cc
new file mode 100644
index 00000000000..61d35e0b4c2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/symbols_iterator.cc
@@ -0,0 +1,131 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/symbols_iterator.h"
+
+#include <unicode/uchar.h>
+#include <unicode/uniset.h>
+#include <memory>
+
+namespace blink {
+
+SymbolsIterator::SymbolsIterator(const UChar* buffer, unsigned buffer_size)
+ : utf16_iterator_(std::make_unique<UTF16TextIterator>(buffer, buffer_size)),
+ buffer_size_(buffer_size),
+ next_char_(0),
+ at_end_(buffer_size == 0),
+ current_font_fallback_priority_(FontFallbackPriority::kInvalid) {}
+
+FontFallbackPriority SymbolsIterator::FontFallbackPriorityForCharacter(
+ UChar32 codepoint) {
+ // Those should only be Emoji presentation as combinations of two.
+ if (Character::IsEmojiKeycapBase(codepoint) ||
+ Character::IsRegionalIndicator(codepoint))
+ return FontFallbackPriority::kText;
+
+ if (codepoint == kCombiningEnclosingKeycapCharacter)
+ return FontFallbackPriority::kEmojiEmoji;
+
+ if (Character::IsEmojiEmojiDefault(codepoint) ||
+ Character::IsEmojiModifierBase(codepoint) ||
+ Character::IsModifier(codepoint))
+ return FontFallbackPriority::kEmojiEmoji;
+
+ if (Character::IsEmojiTextDefault(codepoint))
+ return FontFallbackPriority::kEmojiText;
+
+ // Here we could segment into Symbols and Math categories as well, similar
+ // to what the Windows font fallback does. Map the math Unicode and Symbols
+ // blocks to Text for now since we don't have a good cross-platform way to
+ // select suitable math fonts.
+ return FontFallbackPriority::kText;
+}
+
+bool SymbolsIterator::Consume(unsigned* symbols_limit,
+ FontFallbackPriority* font_fallback_priority) {
+ if (at_end_)
+ return false;
+
+ while (utf16_iterator_->Consume(next_char_)) {
+ previous_font_fallback_priority_ = current_font_fallback_priority_;
+ unsigned iterator_offset = utf16_iterator_->Offset();
+ utf16_iterator_->Advance();
+
+ // Except at the beginning, ZWJ just carries over the emoji or neutral
+ // text type, VS15 & VS16 we just carry over as well, since we already
+ // resolved those through lookahead. Also, don't downgrade to text
+ // presentation for emoji that are part of a ZWJ sequence, example
+ // U+1F441 U+200D U+1F5E8, eye (text presentation) + ZWJ + left speech
+ // bubble, see below.
+ if ((!(next_char_ == kZeroWidthJoinerCharacter &&
+ previous_font_fallback_priority_ ==
+ FontFallbackPriority::kEmojiEmoji) &&
+ next_char_ != kVariationSelector15Character &&
+ next_char_ != kVariationSelector16Character &&
+ next_char_ != kCombiningEnclosingCircleBackslashCharacter &&
+ !Character::IsRegionalIndicator(next_char_) &&
+ !((next_char_ == kLeftSpeechBubbleCharacter ||
+ next_char_ == kRainbowCharacter ||
+ next_char_ == kMaleSignCharacter ||
+ next_char_ == kFemaleSignCharacter ||
+ next_char_ == kStaffOfAesculapiusCharacter) &&
+ previous_font_fallback_priority_ ==
+ FontFallbackPriority::kEmojiEmoji) &&
+ !Character::IsEmojiFlagSequenceTag(next_char_)) ||
+ current_font_fallback_priority_ == FontFallbackPriority::kInvalid) {
+ current_font_fallback_priority_ =
+ FontFallbackPriorityForCharacter(next_char_);
+ }
+
+ UChar32 peek_char = 0;
+ if (utf16_iterator_->Consume(peek_char) && peek_char != 0) {
+ // Variation Selectors
+ if (current_font_fallback_priority_ ==
+ FontFallbackPriority::kEmojiEmoji &&
+ peek_char == kVariationSelector15Character) {
+ current_font_fallback_priority_ = FontFallbackPriority::kEmojiText;
+ }
+
+ if ((current_font_fallback_priority_ ==
+ FontFallbackPriority::kEmojiText ||
+ Character::IsEmojiKeycapBase(next_char_)) &&
+ peek_char == kVariationSelector16Character) {
+ current_font_fallback_priority_ = FontFallbackPriority::kEmojiEmoji;
+ }
+
+ // Combining characters Keycap...
+ if (Character::IsEmojiKeycapBase(next_char_) &&
+ peek_char == kCombiningEnclosingKeycapCharacter) {
+ current_font_fallback_priority_ = FontFallbackPriority::kEmojiEmoji;
+ };
+
+ // Regional indicators
+ if (Character::IsRegionalIndicator(next_char_) &&
+ Character::IsRegionalIndicator(peek_char)) {
+ current_font_fallback_priority_ = FontFallbackPriority::kEmojiEmoji;
+ }
+
+ // Upgrade text presentation emoji to emoji presentation when followed by
+ // ZWJ, Example U+1F441 U+200D U+1F5E8, eye + ZWJ + left speech bubble.
+ if ((next_char_ == kEyeCharacter ||
+ next_char_ == kWavingWhiteFlagCharacter) &&
+ peek_char == kZeroWidthJoinerCharacter) {
+ current_font_fallback_priority_ = FontFallbackPriority::kEmojiEmoji;
+ }
+ }
+
+ if (previous_font_fallback_priority_ != current_font_fallback_priority_ &&
+ (previous_font_fallback_priority_ != FontFallbackPriority::kInvalid)) {
+ *symbols_limit = iterator_offset;
+ *font_fallback_priority = previous_font_fallback_priority_;
+ return true;
+ }
+ }
+ *symbols_limit = buffer_size_;
+ *font_fallback_priority = current_font_fallback_priority_;
+ at_end_ = true;
+ return true;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/symbols_iterator.h b/chromium/third_party/blink/renderer/platform/fonts/symbols_iterator.h
new file mode 100644
index 00000000000..7484c12683a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/symbols_iterator.h
@@ -0,0 +1,41 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SYMBOLS_ITERATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SYMBOLS_ITERATOR_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/fonts/font_fallback_priority.h"
+#include "third_party/blink/renderer/platform/fonts/font_orientation.h"
+#include "third_party/blink/renderer/platform/fonts/script_run_iterator.h"
+#include "third_party/blink/renderer/platform/fonts/utf16_text_iterator.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT SymbolsIterator {
+ USING_FAST_MALLOC(SymbolsIterator);
+ WTF_MAKE_NONCOPYABLE(SymbolsIterator);
+
+ public:
+ SymbolsIterator(const UChar* buffer, unsigned buffer_size);
+
+ bool Consume(unsigned* symbols_limit, FontFallbackPriority*);
+
+ private:
+ FontFallbackPriority FontFallbackPriorityForCharacter(UChar32);
+
+ std::unique_ptr<UTF16TextIterator> utf16_iterator_;
+ unsigned buffer_size_;
+ UChar32 next_char_;
+ bool at_end_;
+
+ FontFallbackPriority current_font_fallback_priority_;
+ FontFallbackPriority previous_font_fallback_priority_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/fonts/symbols_iterator_test.cc b/chromium/third_party/blink/renderer/platform/fonts/symbols_iterator_test.cc
new file mode 100644
index 00000000000..19553e6868a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/symbols_iterator_test.cc
@@ -0,0 +1,377 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/symbols_iterator.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include <string>
+
+namespace blink {
+
+struct FallbackTestRun {
+ std::string text;
+ FontFallbackPriority font_fallback_priority;
+};
+
+struct FallbackExpectedRun {
+ unsigned limit;
+ FontFallbackPriority font_fallback_priority;
+
+ FallbackExpectedRun(unsigned the_limit,
+ FontFallbackPriority the_font_fallback_priority)
+ : limit(the_limit), font_fallback_priority(the_font_fallback_priority) {}
+};
+
+class SymbolsIteratorTest : public testing::Test {
+ protected:
+ void CheckRuns(const Vector<FallbackTestRun>& runs) {
+ String text(g_empty_string16_bit);
+ Vector<FallbackExpectedRun> expect;
+ for (auto& run : runs) {
+ text.append(String::FromUTF8(run.text.c_str()));
+ expect.push_back(
+ FallbackExpectedRun(text.length(), run.font_fallback_priority));
+ }
+ SymbolsIterator symbols_iterator(text.Characters16(), text.length());
+ VerifyRuns(&symbols_iterator, expect);
+ }
+
+ void VerifyRuns(SymbolsIterator* symbols_iterator,
+ const Vector<FallbackExpectedRun>& expect) {
+ unsigned limit;
+ FontFallbackPriority font_fallback_priority;
+ unsigned long run_count = 0;
+ while (symbols_iterator->Consume(&limit, &font_fallback_priority)) {
+ ASSERT_LT(run_count, expect.size());
+ ASSERT_EQ(expect[run_count].limit, limit);
+ ASSERT_EQ(expect[run_count].font_fallback_priority,
+ font_fallback_priority);
+ ++run_count;
+ }
+ ASSERT_EQ(expect.size(), run_count);
+ }
+};
+
+// Some of our compilers cannot initialize a vector from an array yet.
+#define DECLARE_FALLBACK_RUNSVECTOR(...) \
+ static const FallbackTestRun kRunsArray[] = __VA_ARGS__; \
+ Vector<FallbackTestRun> runs; \
+ runs.Append(kRunsArray, sizeof(kRunsArray) / sizeof(*kRunsArray));
+
+#define CHECK_RUNS(...) \
+ DECLARE_FALLBACK_RUNSVECTOR(__VA_ARGS__); \
+ CheckRuns(runs);
+
+TEST_F(SymbolsIteratorTest, Empty) {
+ String empty(g_empty_string16_bit);
+ SymbolsIterator symbols_iterator(empty.Characters16(), empty.length());
+ unsigned limit = 0;
+ FontFallbackPriority symbols_font = FontFallbackPriority::kInvalid;
+ DCHECK(!symbols_iterator.Consume(&limit, &symbols_font));
+ ASSERT_EQ(limit, 0u);
+ ASSERT_EQ(symbols_font, FontFallbackPriority::kInvalid);
+}
+
+TEST_F(SymbolsIteratorTest, Space) {
+ CHECK_RUNS({{" ", FontFallbackPriority::kText}});
+}
+
+TEST_F(SymbolsIteratorTest, Latin) {
+ CHECK_RUNS({{"Aa", FontFallbackPriority::kText}});
+}
+
+TEST_F(SymbolsIteratorTest, LatinColorEmojiTextEmoji) {
+ CHECK_RUNS({{"a", FontFallbackPriority::kText},
+ {"⌚", FontFallbackPriority::kEmojiEmoji},
+ {"☎", FontFallbackPriority::kEmojiText}});
+}
+
+TEST_F(SymbolsIteratorTest, IgnoreVSInMath) {
+ CHECK_RUNS({{"⊆⊇⊈\xEF\xB8\x8E⊙⊚⊚", FontFallbackPriority::kText}});
+}
+
+TEST_F(SymbolsIteratorTest, IgnoreVS15InText) {
+ CHECK_RUNS({{"abcdef\xEF\xB8\x8Eghji", FontFallbackPriority::kText}});
+}
+
+TEST_F(SymbolsIteratorTest, IgnoreVS16InText) {
+ CHECK_RUNS({{"abcdef\xEF\xB8\x8Fghji", FontFallbackPriority::kText}});
+}
+
+TEST_F(SymbolsIteratorTest, AllHexValuesText) {
+ // Helps with detecting incorrect emoji pattern definitions which are
+ // missing a \U000... prefix for example.
+ CHECK_RUNS({{"abcdef0123456789ABCDEF", FontFallbackPriority::kText}});
+}
+
+TEST_F(SymbolsIteratorTest, NumbersAndHashNormalAndEmoji) {
+ CHECK_RUNS({{"0123456789#*", FontFallbackPriority::kText},
+ {"0⃣1⃣2⃣3⃣4⃣5⃣6⃣7⃣8⃣9⃣*⃣", FontFallbackPriority::kEmojiEmoji},
+ {"0123456789#*", FontFallbackPriority::kText}});
+}
+
+TEST_F(SymbolsIteratorTest, VS16onDigits) {
+ CHECK_RUNS({{"#", FontFallbackPriority::kText},
+ {"#\uFE0F#\uFE0F\u20E3", FontFallbackPriority::kEmojiEmoji},
+ {"#", FontFallbackPriority::kText}});
+}
+
+TEST_F(SymbolsIteratorTest, SingleFlag) {
+ CHECK_RUNS({{"🇺", FontFallbackPriority::kText}});
+}
+
+TEST_F(SymbolsIteratorTest, CombiningCircle) {
+ CHECK_RUNS({{"◌́◌̀◌̈◌̂◌̄◌̊", FontFallbackPriority::kText}});
+}
+
+TEST_F(SymbolsIteratorTest, CombiningEnclosingCircleBackslash) {
+ CHECK_RUNS({{"A⃠B⃠C⃠", FontFallbackPriority::kText},
+ {"🚷🚯🚱🔞📵🚭🚫", FontFallbackPriority::kEmojiEmoji},
+ {"🎙⃠", FontFallbackPriority::kEmojiText},
+ {"📸⃠🔫⃠", FontFallbackPriority::kEmojiEmoji},
+ {"a⃠b⃠c⃠", FontFallbackPriority::kText}});
+}
+
+// TODO: Perhaps check for invalid country indicator combinations?
+
+TEST_F(SymbolsIteratorTest, FlagsVsNonFlags) {
+ CHECK_RUNS({{"🇺🇸🇸", FontFallbackPriority::kEmojiEmoji}, // "US"
+ {"abc", FontFallbackPriority::kText},
+ {"🇺🇸", FontFallbackPriority::kEmojiEmoji},
+ {"a🇿", FontFallbackPriority::kText}});
+}
+
+TEST_F(SymbolsIteratorTest, EmojiVS15) {
+ // A VS15 after the anchor must trigger text display.
+ CHECK_RUNS({{"⚓\xEF\xB8\x8E", FontFallbackPriority::kEmojiText},
+ {"⛵", FontFallbackPriority::kEmojiEmoji}});
+}
+
+TEST_F(SymbolsIteratorTest, EmojiZWSSequences) {
+ CHECK_RUNS({{"👩‍👩‍👧‍👦👩‍❤️‍💋‍👨",
+ FontFallbackPriority::kEmojiEmoji},
+ {"abcd", FontFallbackPriority::kText},
+ {"👩‍👩‍", FontFallbackPriority::kEmojiEmoji},
+ {"efgh", FontFallbackPriority::kText}});
+}
+
+TEST_F(SymbolsIteratorTest, AllEmojiZWSSequences) {
+ // clang-format gets confused by Emojis, http://llvm.org/PR30530
+ // clang-format off
+ CHECK_RUNS(
+ {{"💏👩‍❤️‍💋‍👨👨‍❤️‍💋‍👨👩‍❤️‍💋‍👩💑👩‍❤️‍👨👨‍❤"
+ "️"
+ "‍👨👩‍❤️"
+ "‍👩👪👨‍👩‍👦👨‍👩‍👧👨‍👩‍👧‍👦👨‍👩‍👦‍👦👨‍👩‍👧‍👧👨‍👨"
+ "‍"
+ "👦👨‍👨‍👧👨‍👨‍👧‍👦👨‍👨‍👦‍👦👨‍👨‍👧"
+ "‍"
+ "👧"
+ "👩‍👩‍👦👩‍👩‍👧👩‍👩‍👧‍👦👩‍👩‍👦‍👦👩‍👩‍👧‍👧👁"
+ "‍"
+ "🗨",
+ FontFallbackPriority::kEmojiEmoji}});
+ // clang-format on
+}
+
+TEST_F(SymbolsIteratorTest, ModifierPlusGender) {
+ CHECK_RUNS({{"⛹🏻‍♂", FontFallbackPriority::kEmojiEmoji}});
+}
+
+TEST_F(SymbolsIteratorTest, TextMemberZwjSequence) {
+ CHECK_RUNS({{"👨‍⚕", FontFallbackPriority::kEmojiEmoji}});
+}
+
+TEST_F(SymbolsIteratorTest, FacepalmCartwheelShrugModifierFemale) {
+ CHECK_RUNS({{"🤦‍♀🤸‍♀🤷‍♀🤷🏾‍♀",
+ FontFallbackPriority::kEmojiEmoji}});
+}
+
+TEST_F(SymbolsIteratorTest, AesculapiusMaleFemalEmoji) {
+ // Emoji Data 4 has upgraded those three characters to Emoji.
+ CHECK_RUNS({{"a", FontFallbackPriority::kText},
+ {"⚕♀♂", FontFallbackPriority::kEmojiText}});
+}
+
+TEST_F(SymbolsIteratorTest, EyeSpeechBubble) {
+ CHECK_RUNS({{"👁‍🗨", FontFallbackPriority::kEmojiEmoji}});
+}
+
+TEST_F(SymbolsIteratorTest, Modifier) {
+ CHECK_RUNS({{"👶🏿", FontFallbackPriority::kEmojiEmoji}});
+}
+
+TEST_F(SymbolsIteratorTest, DingbatsMiscSymbolsModifier) {
+ CHECK_RUNS({{"⛹🏻✍🏻✊🏼", FontFallbackPriority::kEmojiEmoji}});
+}
+
+TEST_F(SymbolsIteratorTest, ExtraZWJPrefix) {
+ CHECK_RUNS({{"\xE2\x80\x8D", FontFallbackPriority::kText},
+ {"\xF0\x9F\x91\xA9\xE2\x80\x8D\xE2"
+ "\x9D\xA4\xEF\xB8\x8F\xE2\x80\x8D"
+ "\xF0\x9F\x92\x8B\xE2\x80\x8D\xF0\x9F\x91\xA8",
+ FontFallbackPriority::kEmojiEmoji}});
+}
+
+TEST_F(SymbolsIteratorTest, Arrows) {
+ CHECK_RUNS({{"x→←x←↑↓→", FontFallbackPriority::kText}});
+}
+
+TEST_F(SymbolsIteratorTest, JudgePilot) {
+ CHECK_RUNS({{"👨‍⚖️👩‍⚖️👨🏼‍⚖️👩🏼‍⚖️",
+ FontFallbackPriority::kEmojiEmoji}});
+}
+
+// Extracted from http://unicode.org/emoji/charts/emoji-released.html for Emoji
+// v5.0, except for the subdivision-flag section.
+// Before ICU 59 new emoji sequences and new single emoji are not detected as
+// emoji type text and sequences get split up in the middle so that shaping
+// cannot form the right glyph from the emoji font. Running this as one run in
+// one test ensures that the new emoji form an unbroken emoji-type sequence.
+TEST_F(SymbolsIteratorTest, Emoji5AdditionsExceptFlags) {
+ CHECK_RUNS(
+ {{"\xF0\x9F\xA7\x94\xF0\x9F\x8F\xBB\xF0\x9F\xA7\x94\xF0\x9F\x8F\xBC\xF0"
+ "\x9F\xA7\x94\xF0\x9F\x8F\xBD\xF0\x9F\xA7\x94\xF0\x9F\x8F\xBE\xF0\x9F"
+ "\xA7\x94\xF0\x9F\x8F\xBF\xF0\x9F\xA4\xB1\xF0\x9F\xA4\xB1\xF0\x9F\x8F"
+ "\xBB\xF0\x9F\xA4\xB1\xF0\x9F\x8F\xBC\xF0\x9F\xA4\xB1\xF0\x9F\x8F\xBD"
+ "\xF0\x9F\xA4\xB1\xF0\x9F\x8F\xBE\xF0\x9F\xA4\xB1\xF0\x9F\x8F\xBF\xF0"
+ "\x9F\xA7\x99\xF0\x9F\xA7\x99\xF0\x9F\x8F\xBB\xF0\x9F\xA7\x99\xF0\x9F"
+ "\x8F\xBC\xF0\x9F\xA7\x99\xF0\x9F\x8F\xBD\xF0\x9F\xA7\x99\xF0\x9F\x8F"
+ "\xBE\xF0\x9F\xA7\x99\xF0\x9F\x8F\xBF\xF0\x9F\xA7\x99\xE2\x80\x8D\xE2"
+ "\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x99\xF0\x9F\x8F\xBB\xE2\x80\x8D\xE2"
+ "\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x99\xF0\x9F\x8F\xBC\xE2\x80\x8D\xE2"
+ "\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x99\xF0\x9F\x8F\xBD\xE2\x80\x8D\xE2"
+ "\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x99\xF0\x9F\x8F\xBE\xE2\x80\x8D\xE2"
+ "\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x99\xF0\x9F\x8F\xBF\xE2\x80\x8D\xE2"
+ "\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x99\xE2\x80\x8D\xE2\x99\x82\xEF\xB8"
+ "\x8F\xF0\x9F\xA7\x99\xF0\x9F\x8F\xBB\xE2\x80\x8D\xE2\x99\x82\xEF\xB8"
+ "\x8F\xF0\x9F\xA7\x99\xF0\x9F\x8F\xBC\xE2\x80\x8D\xE2\x99\x82\xEF\xB8"
+ "\x8F\xF0\x9F\xA7\x99\xF0\x9F\x8F\xBD\xE2\x80\x8D\xE2\x99\x82\xEF\xB8"
+ "\x8F\xF0\x9F\xA7\x99\xF0\x9F\x8F\xBE\xE2\x80\x8D\xE2\x99\x82\xEF\xB8"
+ "\x8F\xF0\x9F\xA7\x99\xF0\x9F\x8F\xBF\xE2\x80\x8D\xE2\x99\x82\xEF\xB8"
+ "\x8F\xF0\x9F\xA7\x9A\xF0\x9F\xA7\x9A\xF0\x9F\x8F\xBB\xF0\x9F\xA7\x9A"
+ "\xF0\x9F\x8F\xBC\xF0\x9F\xA7\x9A\xF0\x9F\x8F\xBD\xF0\x9F\xA7\x9A\xF0"
+ "\x9F\x8F\xBE\xF0\x9F\xA7\x9A\xF0\x9F\x8F\xBF\xF0\x9F\xA7\x9A\xE2\x80"
+ "\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x9A\xF0\x9F\x8F\xBB\xE2\x80"
+ "\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x9A\xF0\x9F\x8F\xBC\xE2\x80"
+ "\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x9A\xF0\x9F\x8F\xBD\xE2\x80"
+ "\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x9A\xF0\x9F\x8F\xBE\xE2\x80"
+ "\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x9A\xF0\x9F\x8F\xBF\xE2\x80"
+ "\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x9A\xE2\x80\x8D\xE2\x99\x82"
+ "\xEF\xB8\x8F\xF0\x9F\xA7\x9A\xF0\x9F\x8F\xBB\xE2\x80\x8D\xE2\x99\x82"
+ "\xEF\xB8\x8F\xF0\x9F\xA7\x9A\xF0\x9F\x8F\xBC\xE2\x80\x8D\xE2\x99\x82"
+ "\xEF\xB8\x8F\xF0\x9F\xA7\x9A\xF0\x9F\x8F\xBD\xE2\x80\x8D\xE2\x99\x82"
+ "\xEF\xB8\x8F\xF0\x9F\xA7\x9A\xF0\x9F\x8F\xBE\xE2\x80\x8D\xE2\x99\x82"
+ "\xEF\xB8\x8F\xF0\x9F\xA7\x9A\xF0\x9F\x8F\xBF\xE2\x80\x8D\xE2\x99\x82"
+ "\xEF\xB8\x8F\xF0\x9F\xA7\x9B\xF0\x9F\xA7\x9B\xF0\x9F\x8F\xBB\xF0\x9F"
+ "\xA7\x9B\xF0\x9F\x8F\xBC\xF0\x9F\xA7\x9B\xF0\x9F\x8F\xBD\xF0\x9F\xA7"
+ "\x9B\xF0\x9F\x8F\xBE\xF0\x9F\xA7\x9B\xF0\x9F\x8F\xBF\xF0\x9F\xA7\x9B"
+ "\xE2\x80\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x9B\xF0\x9F\x8F\xBB"
+ "\xE2\x80\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x9B\xF0\x9F\x8F\xBC"
+ "\xE2\x80\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x9B\xF0\x9F\x8F\xBD"
+ "\xE2\x80\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x9B\xF0\x9F\x8F\xBE"
+ "\xE2\x80\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x9B\xF0\x9F\x8F\xBF"
+ "\xE2\x80\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x9B\xE2\x80\x8D\xE2"
+ "\x99\x82\xEF\xB8\x8F\xF0\x9F\xA7\x9B\xF0\x9F\x8F\xBB\xE2\x80\x8D\xE2"
+ "\x99\x82\xEF\xB8\x8F\xF0\x9F\xA7\x9B\xF0\x9F\x8F\xBC\xE2\x80\x8D\xE2"
+ "\x99\x82\xEF\xB8\x8F\xF0\x9F\xA7\x9B\xF0\x9F\x8F\xBD\xE2\x80\x8D\xE2"
+ "\x99\x82\xEF\xB8\x8F\xF0\x9F\xA7\x9B\xF0\x9F\x8F\xBE\xE2\x80\x8D\xE2"
+ "\x99\x82\xEF\xB8\x8F\xF0\x9F\xA7\x9B\xF0\x9F\x8F\xBF\xE2\x80\x8D\xE2"
+ "\x99\x82\xEF\xB8\x8F\xF0\x9F\xA7\x9C\xF0\x9F\xA7\x9C\xF0\x9F\x8F\xBB"
+ "\xF0\x9F\xA7\x9C\xF0\x9F\x8F\xBC\xF0\x9F\xA7\x9C\xF0\x9F\x8F\xBD\xF0"
+ "\x9F\xA7\x9C\xF0\x9F\x8F\xBE\xF0\x9F\xA7\x9C\xF0\x9F\x8F\xBF\xF0\x9F"
+ "\xA7\x9C\xE2\x80\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x9C\xF0\x9F"
+ "\x8F\xBB\xE2\x80\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x9C\xF0\x9F"
+ "\x8F\xBC\xE2\x80\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x9C\xF0\x9F"
+ "\x8F\xBD\xE2\x80\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x9C\xF0\x9F"
+ "\x8F\xBE\xE2\x80\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x9C\xF0\x9F"
+ "\x8F\xBF\xE2\x80\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x9C\xE2\x80"
+ "\x8D\xE2\x99\x82\xEF\xB8\x8F\xF0\x9F\xA7\x9C\xF0\x9F\x8F\xBB\xE2\x80"
+ "\x8D\xE2\x99\x82\xEF\xB8\x8F\xF0\x9F\xA7\x9C\xF0\x9F\x8F\xBC\xE2\x80"
+ "\x8D\xE2\x99\x82\xEF\xB8\x8F\xF0\x9F\xA7\x9C\xF0\x9F\x8F\xBD\xE2\x80"
+ "\x8D\xE2\x99\x82\xEF\xB8\x8F\xF0\x9F\xA7\x9C\xF0\x9F\x8F\xBE\xE2\x80"
+ "\x8D\xE2\x99\x82\xEF\xB8\x8F\xF0\x9F\xA7\x9C\xF0\x9F\x8F\xBF\xE2\x80"
+ "\x8D\xE2\x99\x82\xEF\xB8\x8F\xF0\x9F\xA7\x9D\xF0\x9F\xA7\x9D\xF0\x9F"
+ "\x8F\xBB\xF0\x9F\xA7\x9D\xF0\x9F\x8F\xBC\xF0\x9F\xA7\x9D\xF0\x9F\x8F"
+ "\xBD\xF0\x9F\xA7\x9D\xF0\x9F\x8F\xBE\xF0\x9F\xA7\x9D\xF0\x9F\x8F\xBF"
+ "\xF0\x9F\xA7\x9D\xE2\x80\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x9D"
+ "\xF0\x9F\x8F\xBB\xE2\x80\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x9D"
+ "\xF0\x9F\x8F\xBC\xE2\x80\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x9D"
+ "\xF0\x9F\x8F\xBD\xE2\x80\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x9D"
+ "\xF0\x9F\x8F\xBE\xE2\x80\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x9D"
+ "\xF0\x9F\x8F\xBF\xE2\x80\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x9D"
+ "\xE2\x80\x8D\xE2\x99\x82\xEF\xB8\x8F\xF0\x9F\xA7\x9D\xF0\x9F\x8F\xBB"
+ "\xE2\x80\x8D\xE2\x99\x82\xEF\xB8\x8F\xF0\x9F\xA7\x9D\xF0\x9F\x8F\xBC"
+ "\xE2\x80\x8D\xE2\x99\x82\xEF\xB8\x8F\xF0\x9F\xA7\x9D\xF0\x9F\x8F\xBD"
+ "\xE2\x80\x8D\xE2\x99\x82\xEF\xB8\x8F\xF0\x9F\xA7\x9D\xF0\x9F\x8F\xBE"
+ "\xE2\x80\x8D\xE2\x99\x82\xEF\xB8\x8F\xF0\x9F\xA7\x9D\xF0\x9F\x8F\xBF"
+ "\xE2\x80\x8D\xE2\x99\x82\xEF\xB8\x8F\xF0\x9F\xA7\x9E\xF0\x9F\xA7\x9E"
+ "\xE2\x80\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x9E\xE2\x80\x8D\xE2"
+ "\x99\x82\xEF\xB8\x8F\xF0\x9F\xA7\x9F\xF0\x9F\xA7\x9F\xE2\x80\x8D\xE2"
+ "\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x9F\xE2\x80\x8D\xE2\x99\x82\xEF\xB8"
+ "\x8F\xF0\x9F\xA7\x96\xF0\x9F\xA7\x96\xF0\x9F\x8F\xBB\xF0\x9F\xA7\x96"
+ "\xF0\x9F\x8F\xBC\xF0\x9F\xA7\x96\xF0\x9F\x8F\xBD\xF0\x9F\xA7\x96\xF0"
+ "\x9F\x8F\xBE\xF0\x9F\xA7\x96\xF0\x9F\x8F\xBF\xF0\x9F\xA7\x96\xE2\x80"
+ "\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x96\xF0\x9F\x8F\xBB\xE2\x80"
+ "\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x96\xF0\x9F\x8F\xBC\xE2\x80"
+ "\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x96\xF0\x9F\x8F\xBD\xE2\x80"
+ "\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x96\xF0\x9F\x8F\xBE\xE2\x80"
+ "\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x96\xF0\x9F\x8F\xBF\xE2\x80"
+ "\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x96\xE2\x80\x8D\xE2\x99\x82"
+ "\xEF\xB8\x8F\xF0\x9F\xA7\x96\xF0\x9F\x8F\xBB\xE2\x80\x8D\xE2\x99\x82"
+ "\xEF\xB8\x8F\xF0\x9F\xA7\x96\xF0\x9F\x8F\xBC\xE2\x80\x8D\xE2\x99\x82"
+ "\xEF\xB8\x8F\xF0\x9F\xA7\x96\xF0\x9F\x8F\xBD\xE2\x80\x8D\xE2\x99\x82"
+ "\xEF\xB8\x8F\xF0\x9F\xA7\x96\xF0\x9F\x8F\xBE\xE2\x80\x8D\xE2\x99\x82"
+ "\xEF\xB8\x8F\xF0\x9F\xA7\x96\xF0\x9F\x8F\xBF\xE2\x80\x8D\xE2\x99\x82"
+ "\xEF\xB8\x8F\xF0\x9F\xA7\x97\xF0\x9F\xA7\x97\xF0\x9F\x8F\xBB\xF0\x9F"
+ "\xA7\x97\xF0\x9F\x8F\xBC\xF0\x9F\xA7\x97\xF0\x9F\x8F\xBD\xF0\x9F\xA7"
+ "\x97\xF0\x9F\x8F\xBE\xF0\x9F\xA7\x97\xF0\x9F\x8F\xBF\xF0\x9F\xA7\x97"
+ "\xE2\x80\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x97\xF0\x9F\x8F\xBB"
+ "\xE2\x80\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x97\xF0\x9F\x8F\xBC"
+ "\xE2\x80\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x97\xF0\x9F\x8F\xBD"
+ "\xE2\x80\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x97\xF0\x9F\x8F\xBE"
+ "\xE2\x80\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x97\xF0\x9F\x8F\xBF"
+ "\xE2\x80\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x97\xE2\x80\x8D\xE2"
+ "\x99\x82\xEF\xB8\x8F\xF0\x9F\xA7\x97\xF0\x9F\x8F\xBB\xE2\x80\x8D\xE2"
+ "\x99\x82\xEF\xB8\x8F\xF0\x9F\xA7\x97\xF0\x9F\x8F\xBC\xE2\x80\x8D\xE2"
+ "\x99\x82\xEF\xB8\x8F\xF0\x9F\xA7\x97\xF0\x9F\x8F\xBD\xE2\x80\x8D\xE2"
+ "\x99\x82\xEF\xB8\x8F\xF0\x9F\xA7\x97\xF0\x9F\x8F\xBE\xE2\x80\x8D\xE2"
+ "\x99\x82\xEF\xB8\x8F\xF0\x9F\xA7\x97\xF0\x9F\x8F\xBF\xE2\x80\x8D\xE2"
+ "\x99\x82\xEF\xB8\x8F\xF0\x9F\xA7\x98\xF0\x9F\xA7\x98\xF0\x9F\x8F\xBB"
+ "\xF0\x9F\xA7\x98\xF0\x9F\x8F\xBC\xF0\x9F\xA7\x98\xF0\x9F\x8F\xBD\xF0"
+ "\x9F\xA7\x98\xF0\x9F\x8F\xBE\xF0\x9F\xA7\x98\xF0\x9F\x8F\xBF\xF0\x9F"
+ "\xA7\x98\xE2\x80\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x98\xF0\x9F"
+ "\x8F\xBB\xE2\x80\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x98\xF0\x9F"
+ "\x8F\xBC\xE2\x80\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x98\xF0\x9F"
+ "\x8F\xBD\xE2\x80\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x98\xF0\x9F"
+ "\x8F\xBE\xE2\x80\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x98\xF0\x9F"
+ "\x8F\xBF\xE2\x80\x8D\xE2\x99\x80\xEF\xB8\x8F\xF0\x9F\xA7\x98\xE2\x80"
+ "\x8D\xE2\x99\x82\xEF\xB8\x8F\xF0\x9F\xA7\x98\xF0\x9F\x8F\xBB\xE2\x80"
+ "\x8D\xE2\x99\x82\xEF\xB8\x8F\xF0\x9F\xA7\x98\xF0\x9F\x8F\xBC\xE2\x80"
+ "\x8D\xE2\x99\x82\xEF\xB8\x8F\xF0\x9F\xA7\x98\xF0\x9F\x8F\xBD\xE2\x80"
+ "\x8D\xE2\x99\x82\xEF\xB8\x8F\xF0\x9F\xA7\x98\xF0\x9F\x8F\xBE\xE2\x80"
+ "\x8D\xE2\x99\x82\xEF\xB8\x8F\xF0\x9F\xA7\x98\xF0\x9F\x8F\xBF\xE2\x80"
+ "\x8D\xE2\x99\x82\xEF\xB8\x8F\xF0\x9F\xA4\x9F\xF0\x9F\xA4\x9F\xF0\x9F"
+ "\x8F\xBB\xF0\x9F\xA4\x9F\xF0\x9F\x8F\xBC\xF0\x9F\xA4\x9F\xF0\x9F\x8F"
+ "\xBD\xF0\x9F\xA4\x9F\xF0\x9F\x8F\xBE\xF0\x9F\xA4\x9F\xF0\x9F\x8F\xBF"
+ "\xF0\x9F\xA4\xB2\xF0\x9F\xA4\xB2\xF0\x9F\x8F\xBB\xF0\x9F\xA4\xB2\xF0"
+ "\x9F\x8F\xBC\xF0\x9F\xA4\xB2\xF0\x9F\x8F\xBD\xF0\x9F\xA4\xB2\xF0\x9F"
+ "\x8F\xBE\xF0\x9F\xA4\xB2\xF0\x9F\x8F\xBF\xF0\x9F\xA7\xA0\xF0\x9F\xA7"
+ "\xA1\xF0\x9F\xA7\xA3\xF0\x9F\xA7\xA4\xF0\x9F\xA7\xA5\xF0\x9F\xA7\xA6"
+ "\xF0\x9F\xA7\xA2\xF0\x9F\xA6\x93\xF0\x9F\xA6\x92\xF0\x9F\xA6\x94\xF0"
+ "\x9F\xA6\x95\xF0\x9F\xA6\x96\xF0\x9F\xA6\x97\xF0\x9F\xA5\xA5\xF0\x9F"
+ "\xA5\xA6\xF0\x9F\xA5\xA8\xF0\x9F\xA5\xA9\xF0\x9F\xA5\xAA\xF0\x9F\xA5"
+ "\xA3\xF0\x9F\xA5\xAB\xF0\x9F\xA5\x9F\xF0\x9F\xA5\xA0\xF0\x9F\xA5\xA1"
+ "\xF0\x9F\xA5\xA7\xF0\x9F\xA5\xA4\xF0\x9F\xA5\xA2\xF0\x9F\x9B\xB8\xF0"
+ "\x9F\x9B\xB7\xF0\x9F\xA5\x8C",
+ FontFallbackPriority::kEmojiEmoji}});
+}
+
+TEST_F(SymbolsIteratorTest, EmojiSubdivisionFlags) {
+ CHECK_RUNS(
+ {{"🏴󠁧󠁢󠁷󠁬󠁳󠁿🏴󠁧󠁢󠁳󠁣󠁴󠁿🏴󠁧󠁢",
+ FontFallbackPriority::kEmojiEmoji}});
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/text_rendering_mode.cc b/chromium/third_party/blink/renderer/platform/fonts/text_rendering_mode.cc
new file mode 100644
index 00000000000..db814e5a8e0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/text_rendering_mode.cc
@@ -0,0 +1,25 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/text_rendering_mode.h"
+
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+String ToString(TextRenderingMode mode) {
+ switch (mode) {
+ case TextRenderingMode::kAutoTextRendering:
+ return "Auto";
+ case TextRenderingMode::kOptimizeSpeed:
+ return "OptimizeSpeed";
+ case TextRenderingMode::kOptimizeLegibility:
+ return "OptimizeLegibility";
+ case TextRenderingMode::kGeometricPrecision:
+ return "GeometricPrecision";
+ }
+ return "Unknown";
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/text_rendering_mode.h b/chromium/third_party/blink/renderer/platform/fonts/text_rendering_mode.h
new file mode 100644
index 00000000000..a4f97a0547a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/text_rendering_mode.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2009 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_TEXT_RENDERING_MODE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_TEXT_RENDERING_MODE_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+enum TextRenderingMode {
+ kAutoTextRendering,
+ kOptimizeSpeed,
+ kOptimizeLegibility,
+ kGeometricPrecision
+};
+
+PLATFORM_EXPORT String ToString(TextRenderingMode);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_TEXT_RENDERING_MODE_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/typesetting_features.cc b/chromium/third_party/blink/renderer/platform/fonts/typesetting_features.cc
new file mode 100644
index 00000000000..ff42778fa4b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/typesetting_features.cc
@@ -0,0 +1,32 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/typesetting_features.h"
+
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+namespace {
+
+const char* kFeatureNames[kMaxTypesettingFeatureIndex + 1] = {
+ "Kerning", "Ligatures", "Caps"};
+
+} // namespace
+
+String ToString(TypesettingFeatures features) {
+ StringBuilder builder;
+ int featureCount = 0;
+ for (int i = 0; i <= kMaxTypesettingFeatureIndex; i++) {
+ if (features & (1 << i)) {
+ if (featureCount++ > 0)
+ builder.Append(",");
+ builder.Append(kFeatureNames[i]);
+ }
+ }
+ return builder.ToString();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/typesetting_features.h b/chromium/third_party/blink/renderer/platform/fonts/typesetting_features.h
new file mode 100644
index 00000000000..8a31e014885
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/typesetting_features.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2010 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_TYPESETTING_FEATURES_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_TYPESETTING_FEATURES_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+constexpr int kMaxTypesettingFeatureIndex = 2;
+enum TypesettingFeature {
+ kKerning = 1 << 0,
+ kLigatures = 1 << 1,
+ kCaps = 1 << kMaxTypesettingFeatureIndex
+ // When adding new features be sure to increment kMaxFeatureIndex
+ // and update kFeatureNames.
+};
+
+typedef unsigned TypesettingFeatures;
+
+PLATFORM_EXPORT String ToString(TypesettingFeatures);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_TYPESETTING_FEATURES_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/typesetting_features_test.cc b/chromium/third_party/blink/renderer/platform/fonts/typesetting_features_test.cc
new file mode 100644
index 00000000000..e57c4adcf4c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/typesetting_features_test.cc
@@ -0,0 +1,27 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/typesetting_features.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+TEST(TypesettingFeaturesTest, ToString) {
+ {
+ TypesettingFeatures features = 0;
+ EXPECT_EQ("", ToString(features));
+ }
+ {
+ TypesettingFeatures features = kKerning | kLigatures;
+ EXPECT_EQ("Kerning,Ligatures", ToString(features));
+ }
+ {
+ TypesettingFeatures features = kKerning | kLigatures | kCaps;
+ EXPECT_EQ("Kerning,Ligatures,Caps", ToString(features));
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/unicode_range_set.cc b/chromium/third_party/blink/renderer/platform/fonts/unicode_range_set.cc
new file mode 100644
index 00000000000..1721b041ac7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/unicode_range_set.cc
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2007, 2008, 2011 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/fonts/unicode_range_set.h"
+
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+UnicodeRangeSet::UnicodeRangeSet(const Vector<UnicodeRange>& ranges)
+ : ranges_(ranges) {
+ if (ranges_.IsEmpty())
+ return;
+
+ std::sort(ranges_.begin(), ranges_.end());
+
+ // Unify overlapping ranges.
+ UChar32 from = ranges_[0].From();
+ UChar32 to = ranges_[0].To();
+ size_t target_index = 0;
+ for (size_t i = 1; i < ranges_.size(); i++) {
+ if (to + 1 >= ranges_[i].From()) {
+ to = std::max(to, ranges_[i].To());
+ } else {
+ ranges_[target_index++] = UnicodeRange(from, to);
+ from = ranges_[i].From();
+ to = ranges_[i].To();
+ }
+ }
+ ranges_[target_index++] = UnicodeRange(from, to);
+ ranges_.Shrink(target_index);
+}
+
+bool UnicodeRangeSet::Contains(UChar32 c) const {
+ if (IsEntireRange())
+ return true;
+ Vector<UnicodeRange>::const_iterator it =
+ std::lower_bound(ranges_.begin(), ranges_.end(), c);
+ return it != ranges_.end() && it->Contains(c);
+}
+
+bool UnicodeRangeSet::IntersectsWith(const String& text) const {
+ if (text.IsEmpty())
+ return false;
+ if (IsEntireRange())
+ return true;
+ if (text.Is8Bit() && ranges_[0].From() >= 0x100)
+ return false;
+
+ unsigned index = 0;
+ while (index < text.length()) {
+ UChar32 c = text.CharacterStartingAt(index);
+ index += U16_LENGTH(c);
+ if (Contains(c))
+ return true;
+ }
+ return false;
+}
+
+bool UnicodeRangeSet::operator==(const UnicodeRangeSet& other) const {
+ if (ranges_.size() == 0 && other.size() == 0)
+ return true;
+ if (ranges_.size() != other.size()) {
+ return false;
+ }
+ bool equal = true;
+ for (size_t i = 0; i < ranges_.size(); ++i) {
+ equal = equal && ranges_[i] == other.ranges_[i];
+ }
+ return equal;
+}
+}
diff --git a/chromium/third_party/blink/renderer/platform/fonts/unicode_range_set.h b/chromium/third_party/blink/renderer/platform/fonts/unicode_range_set.h
new file mode 100644
index 00000000000..2336355441d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/unicode_range_set.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2007, 2008 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_UNICODE_RANGE_SET_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_UNICODE_RANGE_SET_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
+#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+struct PLATFORM_EXPORT UnicodeRange final {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ UnicodeRange(UChar32 from, UChar32 to) : from_(from), to_(to) {}
+
+ UChar32 From() const { return from_; }
+ UChar32 To() const { return to_; }
+ bool Contains(UChar32 c) const { return from_ <= c && c <= to_; }
+ bool operator<(const UnicodeRange& other) const {
+ return from_ < other.from_;
+ }
+ bool operator<(UChar32 c) const { return to_ < c; }
+ bool operator==(const UnicodeRange& other) const {
+ return other.from_ == from_ && other.to_ == to_;
+ };
+
+ private:
+ UChar32 from_;
+ UChar32 to_;
+};
+
+class PLATFORM_EXPORT UnicodeRangeSet : public RefCounted<UnicodeRangeSet> {
+ public:
+ explicit UnicodeRangeSet(const Vector<UnicodeRange>&);
+ UnicodeRangeSet() = default;
+ ;
+ bool Contains(UChar32) const;
+ bool IntersectsWith(const String&) const;
+ bool IsEntireRange() const { return ranges_.IsEmpty(); }
+ size_t size() const { return ranges_.size(); }
+ const UnicodeRange& RangeAt(size_t i) const { return ranges_[i]; }
+ bool operator==(const UnicodeRangeSet& other) const;
+
+ private:
+ Vector<UnicodeRange> ranges_; // If empty, represents the whole code space.
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/fonts/unicode_range_set_test.cc b/chromium/third_party/blink/renderer/platform/fonts/unicode_range_set_test.cc
new file mode 100644
index 00000000000..d2cd424bdec
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/unicode_range_set_test.cc
@@ -0,0 +1,89 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/unicode_range_set.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+static const UChar kHiraganaA[2] = {0x3042, 0};
+
+TEST(UnicodeRangeSet, Empty) {
+ Vector<UnicodeRange> ranges;
+ scoped_refptr<UnicodeRangeSet> set =
+ base::AdoptRef(new UnicodeRangeSet(ranges));
+ EXPECT_TRUE(set->IsEntireRange());
+ EXPECT_EQ(0u, set->size());
+ EXPECT_FALSE(set->IntersectsWith(String()));
+ EXPECT_TRUE(set->IntersectsWith(String("a")));
+ EXPECT_TRUE(set->IntersectsWith(String(kHiraganaA)));
+}
+
+TEST(UnicodeRangeSet, SingleCharacter) {
+ Vector<UnicodeRange> ranges;
+ ranges.push_back(UnicodeRange('b', 'b'));
+ scoped_refptr<UnicodeRangeSet> set =
+ base::AdoptRef(new UnicodeRangeSet(ranges));
+ EXPECT_FALSE(set->IsEntireRange());
+ EXPECT_FALSE(set->IntersectsWith(String()));
+ EXPECT_FALSE(set->IntersectsWith(String("a")));
+ EXPECT_TRUE(set->IntersectsWith(String("b")));
+ EXPECT_FALSE(set->IntersectsWith(String("c")));
+ EXPECT_TRUE(set->IntersectsWith(String("abc")));
+ EXPECT_FALSE(set->IntersectsWith(String(kHiraganaA)));
+ ASSERT_EQ(1u, set->size());
+ EXPECT_EQ('b', set->RangeAt(0).From());
+ EXPECT_EQ('b', set->RangeAt(0).To());
+}
+
+TEST(UnicodeRangeSet, TwoRanges) {
+ Vector<UnicodeRange> ranges;
+ ranges.push_back(UnicodeRange('6', '7'));
+ ranges.push_back(UnicodeRange('2', '4'));
+ scoped_refptr<UnicodeRangeSet> set =
+ base::AdoptRef(new UnicodeRangeSet(ranges));
+ EXPECT_FALSE(set->IsEntireRange());
+ EXPECT_FALSE(set->IntersectsWith(String()));
+ EXPECT_FALSE(set->IntersectsWith(String("1")));
+ EXPECT_TRUE(set->IntersectsWith(String("2")));
+ EXPECT_TRUE(set->IntersectsWith(String("3")));
+ EXPECT_TRUE(set->IntersectsWith(String("4")));
+ EXPECT_FALSE(set->IntersectsWith(String("5")));
+ EXPECT_TRUE(set->IntersectsWith(String("6")));
+ EXPECT_TRUE(set->IntersectsWith(String("7")));
+ EXPECT_FALSE(set->IntersectsWith(String("8")));
+ ASSERT_EQ(2u, set->size());
+ EXPECT_EQ('2', set->RangeAt(0).From());
+ EXPECT_EQ('4', set->RangeAt(0).To());
+ EXPECT_EQ('6', set->RangeAt(1).From());
+ EXPECT_EQ('7', set->RangeAt(1).To());
+}
+
+TEST(UnicodeRangeSet, Overlap) {
+ Vector<UnicodeRange> ranges;
+ ranges.push_back(UnicodeRange('0', '2'));
+ ranges.push_back(UnicodeRange('1', '1'));
+ ranges.push_back(UnicodeRange('3', '5'));
+ ranges.push_back(UnicodeRange('4', '6'));
+ scoped_refptr<UnicodeRangeSet> set =
+ base::AdoptRef(new UnicodeRangeSet(ranges));
+ ASSERT_EQ(1u, set->size());
+ EXPECT_EQ('0', set->RangeAt(0).From());
+ EXPECT_EQ('6', set->RangeAt(0).To());
+}
+
+TEST(UnicodeRangeSet, Non8Bit) {
+ Vector<UnicodeRange> ranges;
+ ranges.push_back(UnicodeRange(0x3042, 0x3042));
+ scoped_refptr<UnicodeRangeSet> set =
+ base::AdoptRef(new UnicodeRangeSet(ranges));
+ ASSERT_EQ(1u, set->size());
+ EXPECT_EQ(0x3042, set->RangeAt(0).From());
+ EXPECT_EQ(0x3042, set->RangeAt(0).To());
+ EXPECT_FALSE(set->IntersectsWith(String("a")));
+ EXPECT_TRUE(set->IntersectsWith(String(kHiraganaA)));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/utf16_text_iterator.cc b/chromium/third_party/blink/renderer/platform/fonts/utf16_text_iterator.cc
new file mode 100644
index 00000000000..3f175552aea
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/utf16_text_iterator.cc
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2003, 2006, 2008, 2009, 2010, 2011 Apple Inc. All rights
+ * reserved.
+ * Copyright (C) 2008 Holger Hans Peter Freyther
+ * Copyright (C) Research In Motion Limited 2011. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "third_party/blink/renderer/platform/fonts/utf16_text_iterator.h"
+
+namespace blink {
+
+UTF16TextIterator::UTF16TextIterator(const UChar* characters, int length)
+ : characters_(characters),
+ characters_end_(characters + length),
+ offset_(0),
+ length_(length),
+ current_glyph_length_(0) {}
+
+bool UTF16TextIterator::IsValidSurrogatePair(UChar32& character) {
+ // If we have a surrogate pair, make sure it starts with the high part.
+ if (!U16_IS_SURROGATE_LEAD(character))
+ return false;
+
+ // Do we have a surrogate pair? If so, determine the full Unicode (32 bit)
+ // code point before glyph lookup.
+ // Make sure we have another character and it's a low surrogate.
+ if (characters_ + 1 >= characters_end_)
+ return false;
+
+ UChar low = characters_[1];
+ if (!U16_IS_TRAIL(low))
+ return false;
+ return true;
+}
+
+bool UTF16TextIterator::ConsumeSurrogatePair(UChar32& character) {
+ DCHECK(U16_IS_SURROGATE(character));
+
+ if (!IsValidSurrogatePair(character)) {
+ character = WTF::Unicode::kReplacementCharacter;
+ return true;
+ }
+
+ UChar low = characters_[1];
+ character = U16_GET_SUPPLEMENTARY(character, low);
+ current_glyph_length_ = 2;
+ return true;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/utf16_text_iterator.h b/chromium/third_party/blink/renderer/platform/fonts/utf16_text_iterator.h
new file mode 100644
index 00000000000..f5e8ecabfde
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/utf16_text_iterator.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) Research In Motion Limited 2011. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_UTF16_TEXT_ITERATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_UTF16_TEXT_ITERATOR_H_
+
+#include "base/macros.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT UTF16TextIterator {
+ USING_FAST_MALLOC(UTF16TextIterator);
+
+ public:
+ // The passed in UChar pointer starts at 'offset'. The iterator operates on
+ // the range [offset, endOffset].
+ // 'length' denotes the maximum length of the UChar array, which might exceed
+ // 'endOffset'.
+ UTF16TextIterator(const UChar*, int length);
+
+ inline bool Consume(UChar32& character) {
+ if (offset_ >= length_)
+ return false;
+
+ character = *characters_;
+ current_glyph_length_ = 1;
+ if (!U16_IS_SURROGATE(character))
+ return true;
+
+ return ConsumeSurrogatePair(character);
+ }
+
+ void Advance() {
+ characters_ += current_glyph_length_;
+ offset_ += current_glyph_length_;
+ }
+
+ int Offset() const { return offset_; }
+ const UChar* Characters() const { return characters_; }
+ const UChar* GlyphEnd() const { return characters_ + current_glyph_length_; }
+
+ private:
+ bool IsValidSurrogatePair(UChar32&);
+ bool ConsumeSurrogatePair(UChar32&);
+ void ConsumeMultipleUChar();
+
+ const UChar* characters_;
+ const UChar* characters_end_;
+ int offset_;
+ int length_;
+ unsigned current_glyph_length_;
+
+ DISALLOW_COPY_AND_ASSIGN(UTF16TextIterator);
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/fonts/vdmx_parser.cc b/chromium/third_party/blink/renderer/platform/fonts/vdmx_parser.cc
new file mode 100644
index 00000000000..2df7be8d424
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/vdmx_parser.cc
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2008, 2009, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/renderer/platform/fonts/vdmx_parser.h"
+
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/byte_order.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+namespace blink {
+namespace {
+
+// Buffer helper class
+//
+// This class perform some trival buffer operations while checking for
+// out-of-bounds errors. As a family they return false if anything is amiss,
+// updating the current offset otherwise.
+class Buffer {
+ STACK_ALLOCATED();
+ WTF_MAKE_NONCOPYABLE(Buffer);
+
+ public:
+ Buffer(const uint8_t* buffer, size_t length)
+ : buffer_(buffer), length_(length), offset_(0) {}
+
+ bool skip(size_t numBytes) {
+ if (offset_ + numBytes > length_)
+ return false;
+ offset_ += numBytes;
+ return true;
+ }
+
+ bool ReadU8(uint8_t* value) {
+ if (offset_ + sizeof(uint8_t) > length_)
+ return false;
+ *value = buffer_[offset_];
+ offset_ += sizeof(uint8_t);
+ return true;
+ }
+
+ bool ReadU16(uint16_t* value) {
+ if (offset_ + sizeof(uint16_t) > length_)
+ return false;
+ memcpy(value, buffer_ + offset_, sizeof(uint16_t));
+ *value = ntohs(*value);
+ offset_ += sizeof(uint16_t);
+ return true;
+ }
+
+ bool ReadS16(int16_t* value) {
+ return ReadU16(reinterpret_cast<uint16_t*>(value));
+ }
+
+ size_t Offset() const { return offset_; }
+
+ void SetOffset(size_t newoffset) { offset_ = newoffset; }
+
+ private:
+ const uint8_t* const buffer_;
+ const size_t length_;
+ size_t offset_;
+};
+
+} // namespace
+
+// VDMX parsing code.
+//
+// VDMX tables are found in some TrueType/OpenType fonts and contain
+// ascender/descender overrides for certain (usually small) sizes. This is
+// needed in order to match font metrics on Windows.
+//
+// Freetype does not parse these tables so we do so here.
+
+// Parse a TrueType VDMX table.
+// yMax: (output) the ascender value from the table
+// yMin: (output) the descender value from the table (negative!)
+// vdmx: the table bytes
+// vdmxLength: length of @vdmx, in bytes
+// targetPixelSize: the pixel size of the font (e.g. 16)
+//
+// Returns true iff a suitable match are found. Otherwise, *yMax and *yMin are
+// untouched. size_t must be 32-bits to avoid overflow.
+//
+// See http://www.microsoft.com/opentype/otspec/vdmx.htm
+bool ParseVDMX(int* y_max,
+ int* y_min,
+ const uint8_t* vdmx,
+ size_t vdmx_length,
+ unsigned target_pixel_size) {
+ Buffer buf(vdmx, vdmx_length);
+
+ // We ignore the version. Future tables should be backwards compatible with
+ // this layout.
+ uint16_t num_ratios;
+ if (!buf.skip(4) || !buf.ReadU16(&num_ratios))
+ return false;
+
+ // Now we have two tables. Firstly we have @numRatios Ratio records, then a
+ // matching array of @numRatios offsets. We save the offset of the beginning
+ // of this second table.
+ //
+ // Range 6 <= x <= 262146
+ unsigned long offset_table_offset =
+ buf.Offset() + 4 /* sizeof struct ratio */ * num_ratios;
+
+ unsigned desired_ratio = 0xffffffff;
+ // We read 4 bytes per record, so the offset range is
+ // 6 <= x <= 524286
+ for (unsigned i = 0; i < num_ratios; ++i) {
+ uint8_t x_ratio, y_ratio1, y_ratio2;
+
+ if (!buf.skip(1) || !buf.ReadU8(&x_ratio) || !buf.ReadU8(&y_ratio1) ||
+ !buf.ReadU8(&y_ratio2))
+ return false;
+
+ // This either covers 1:1, or this is the default entry (0, 0, 0)
+ if ((x_ratio == 1 && y_ratio1 <= 1 && y_ratio2 >= 1) ||
+ (x_ratio == 0 && y_ratio1 == 0 && y_ratio2 == 0)) {
+ desired_ratio = i;
+ break;
+ }
+ }
+
+ if (desired_ratio == 0xffffffff) // no ratio found
+ return false;
+
+ // Range 10 <= x <= 393216
+ buf.SetOffset(offset_table_offset + sizeof(uint16_t) * desired_ratio);
+
+ // Now we read from the offset table to get the offset of another array
+ uint16_t group_offset;
+ if (!buf.ReadU16(&group_offset))
+ return false;
+ // Range 0 <= x <= 65535
+ buf.SetOffset(group_offset);
+
+ uint16_t num_records;
+ if (!buf.ReadU16(&num_records) || !buf.skip(sizeof(uint16_t)))
+ return false;
+
+ // We read 6 bytes per record, so the offset range is
+ // 4 <= x <= 458749
+ for (unsigned i = 0; i < num_records; ++i) {
+ uint16_t pixel_size;
+ if (!buf.ReadU16(&pixel_size))
+ return false;
+ // the entries are sorted, so we can abort early if need be
+ if (pixel_size > target_pixel_size)
+ return false;
+
+ if (pixel_size == target_pixel_size) {
+ int16_t temp_y_max, temp_y_min;
+ if (!buf.ReadS16(&temp_y_max) || !buf.ReadS16(&temp_y_min))
+ return false;
+ *y_min = temp_y_min;
+ *y_max = temp_y_max;
+ return true;
+ }
+ if (!buf.skip(2 * sizeof(int16_t)))
+ return false;
+ }
+
+ return false;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/vdmx_parser.h b/chromium/third_party/blink/renderer/platform/fonts/vdmx_parser.h
new file mode 100644
index 00000000000..8951dd8f3cb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/vdmx_parser.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2009, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_VDMX_PARSER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_VDMX_PARSER_H_
+
+#include <cstddef>
+#include <stdint.h>
+
+namespace blink {
+bool ParseVDMX(int* ymax,
+ int* ymin,
+ const uint8_t* vdmx,
+ size_t vdmx_length,
+ unsigned target_pixel_size);
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/fonts/web_font_decoder.cc b/chromium/third_party/blink/renderer/platform/fonts/web_font_decoder.cc
new file mode 100644
index 00000000000..e99687127a4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/web_font_decoder.cc
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/renderer/platform/fonts/web_font_decoder.h"
+
+#include "build/build_config.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/fonts/font_cache.h"
+#include "third_party/blink/renderer/platform/fonts/web_font_typeface_factory.h"
+#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
+#include "third_party/blink/renderer/platform/histogram.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+#include "third_party/ots/include/ots-memory-stream.h"
+#include "third_party/skia/include/core/SkStream.h"
+
+#include <hb.h>
+#include <stdarg.h>
+
+namespace blink {
+
+namespace {
+
+class BlinkOTSContext final : public ots::OTSContext {
+ DISALLOW_NEW();
+
+ public:
+ void Message(int level, const char* format, ...) override;
+ ots::TableAction GetTableAction(uint32_t tag) override;
+ const String& GetErrorString() { return error_string_; }
+
+ private:
+ String error_string_;
+};
+
+void BlinkOTSContext::Message(int level, const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+
+#if defined(COMPILER_MSVC)
+ int result = _vscprintf(format, args);
+#else
+ char ch;
+ int result = vsnprintf(&ch, 1, format, args);
+#endif
+ va_end(args);
+
+ if (result <= 0) {
+ error_string_ = String("OTS Error");
+ } else {
+ Vector<char, 256> buffer;
+ unsigned len = result;
+ buffer.Grow(len + 1);
+
+ va_start(args, format);
+ vsnprintf(buffer.data(), buffer.size(), format, args);
+ va_end(args);
+ error_string_ =
+ StringImpl::Create(reinterpret_cast<const LChar*>(buffer.data()), len);
+ }
+}
+
+#if !defined(HB_VERSION_ATLEAST)
+#define HB_VERSION_ATLEAST(major, minor, micro) 0
+#endif
+
+ots::TableAction BlinkOTSContext::GetTableAction(uint32_t tag) {
+ const uint32_t kCbdtTag = OTS_TAG('C', 'B', 'D', 'T');
+ const uint32_t kCblcTag = OTS_TAG('C', 'B', 'L', 'C');
+ const uint32_t kColrTag = OTS_TAG('C', 'O', 'L', 'R');
+ const uint32_t kCpalTag = OTS_TAG('C', 'P', 'A', 'L');
+ const uint32_t kCff2Tag = OTS_TAG('C', 'F', 'F', '2');
+ const uint32_t kSbixTag = OTS_TAG('s', 'b', 'i', 'x');
+#if HB_VERSION_ATLEAST(1, 0, 0)
+ const uint32_t kGdefTag = OTS_TAG('G', 'D', 'E', 'F');
+ const uint32_t kGposTag = OTS_TAG('G', 'P', 'O', 'S');
+ const uint32_t kGsubTag = OTS_TAG('G', 'S', 'U', 'B');
+
+ // Font Variations related tables
+ // See "Variation Tables" in Terminology section of
+ // https://www.microsoft.com/typography/otspec/otvaroverview.htm
+ const uint32_t kAvarTag = OTS_TAG('a', 'v', 'a', 'r');
+ const uint32_t kCvarTag = OTS_TAG('c', 'v', 'a', 'r');
+ const uint32_t kFvarTag = OTS_TAG('f', 'v', 'a', 'r');
+ const uint32_t kGvarTag = OTS_TAG('g', 'v', 'a', 'r');
+ const uint32_t kHvarTag = OTS_TAG('H', 'V', 'A', 'R');
+ const uint32_t kMvarTag = OTS_TAG('M', 'V', 'A', 'R');
+ const uint32_t kVvarTag = OTS_TAG('V', 'V', 'A', 'R');
+#endif
+
+ switch (tag) {
+ // Google Color Emoji Tables
+ case kCbdtTag:
+ case kCblcTag:
+ // Windows Color Emoji Tables
+ case kColrTag:
+ case kCpalTag:
+ case kCff2Tag:
+ case kSbixTag:
+#if HB_VERSION_ATLEAST(1, 0, 0)
+ // Let HarfBuzz handle how to deal with broken tables.
+ case kAvarTag:
+ case kCvarTag:
+ case kFvarTag:
+ case kGvarTag:
+ case kHvarTag:
+ case kMvarTag:
+ case kVvarTag:
+ case kGdefTag:
+ case kGposTag:
+ case kGsubTag:
+#endif
+ return ots::TABLE_ACTION_PASSTHRU;
+ default:
+ return ots::TABLE_ACTION_DEFAULT;
+ }
+}
+
+void RecordDecodeSpeedHistogram(const char* data,
+ size_t length,
+ double decode_time,
+ size_t decoded_size) {
+ if (decode_time <= 0)
+ return;
+
+ double kb_per_second = decoded_size / (1000 * decode_time);
+ if (length >= 4) {
+ if (data[0] == 'w' && data[1] == 'O' && data[2] == 'F' && data[3] == 'F') {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(
+ CustomCountHistogram, woff_histogram,
+ ("WebFont.DecodeSpeed.WOFF", 1000, 300000, 50));
+ woff_histogram.Count(kb_per_second);
+ return;
+ }
+
+ if (data[0] == 'w' && data[1] == 'O' && data[2] == 'F' && data[3] == '2') {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(
+ CustomCountHistogram, woff2_histogram,
+ ("WebFont.DecodeSpeed.WOFF2", 1000, 300000, 50));
+ woff2_histogram.Count(kb_per_second);
+ return;
+ }
+ }
+
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(
+ CustomCountHistogram, sfnt_histogram,
+ ("WebFont.DecodeSpeed.SFNT", 1000, 300000, 50));
+ sfnt_histogram.Count(kb_per_second);
+}
+
+} // namespace
+
+sk_sp<SkTypeface> WebFontDecoder::Decode(SharedBuffer* buffer) {
+ if (!buffer) {
+ SetErrorString("Empty Buffer");
+ return nullptr;
+ }
+
+ // This is the largest web font size which we'll try to transcode.
+ // TODO(bashi): 30MB seems low. Update the limit if necessary.
+ static const size_t kMaxWebFontSize = 30 * 1024 * 1024; // 30 MB
+ if (buffer->size() > kMaxWebFontSize) {
+ SetErrorString("Web font size more than 30MB");
+ return nullptr;
+ }
+
+ // Most web fonts are compressed, so the result can be much larger than
+ // the original.
+ ots::ExpandingMemoryStream output(buffer->size(), kMaxWebFontSize);
+ double start = CurrentTime();
+ BlinkOTSContext ots_context;
+ SharedBuffer::DeprecatedFlatData flattened_buffer(buffer);
+ const char* data = flattened_buffer.Data();
+
+ TRACE_EVENT_BEGIN0("blink", "DecodeFont");
+ bool ok = ots_context.Process(&output, reinterpret_cast<const uint8_t*>(data),
+ buffer->size());
+ TRACE_EVENT_END0("blink", "DecodeFont");
+
+ if (!ok) {
+ SetErrorString(ots_context.GetErrorString());
+ return nullptr;
+ }
+
+ const size_t decoded_length = output.Tell();
+ RecordDecodeSpeedHistogram(data, buffer->size(), CurrentTime() - start,
+ decoded_length);
+
+ sk_sp<SkData> sk_data = SkData::MakeWithCopy(output.get(), decoded_length);
+
+ sk_sp<SkTypeface> new_typeface;
+
+ if (!WebFontTypefaceFactory::CreateTypeface(sk_data, new_typeface)) {
+ SetErrorString("Unable to instantiate font face from font data.");
+ return nullptr;
+ }
+
+ decoded_size_ = decoded_length;
+
+ return new_typeface;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/web_font_decoder.h b/chromium/third_party/blink/renderer/platform/fonts/web_font_decoder.h
new file mode 100644
index 00000000000..4d5578ae899
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/web_font_decoder.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_WEB_FONT_DECODER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_WEB_FONT_DECODER_H_
+
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+class SkTypeface;
+
+namespace blink {
+
+class SharedBuffer;
+
+class WebFontDecoder final {
+ STACK_ALLOCATED();
+
+ public:
+ WebFontDecoder() = default;
+
+ sk_sp<SkTypeface> Decode(SharedBuffer*);
+ size_t DecodedSize() const { return decoded_size_; }
+
+ String GetErrorString() const { return ots_error_string_; }
+
+ private:
+ void SetErrorString(const String& error_string) {
+ ots_error_string_ = error_string;
+ }
+
+ String ots_error_string_;
+ size_t decoded_size_ = 0;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_WEB_FONT_DECODER_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/web_font_render_style.cc b/chromium/third_party/blink/renderer/platform/fonts/web_font_render_style.cc
new file mode 100644
index 00000000000..86636e7e360
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/web_font_render_style.cc
@@ -0,0 +1,119 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_font_render_style.h"
+
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/fonts/font_cache.h"
+#include "third_party/blink/renderer/platform/fonts/font_description.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_font.h"
+#include "third_party/blink/renderer/platform/layout_test_support.h"
+
+namespace blink {
+
+namespace {
+
+SkPaint::Hinting g_skia_hinting = SkPaint::kNormal_Hinting;
+bool g_use_skia_auto_hint = true;
+bool g_use_skia_bitmaps = true;
+bool g_use_skia_anti_alias = true;
+bool g_use_skia_subpixel_rendering = false;
+
+} // namespace
+
+// static
+void WebFontRenderStyle::SetSkiaFontManager(sk_sp<SkFontMgr> font_mgr) {
+ FontCache::SetFontManager(std::move(font_mgr));
+}
+
+// static
+void WebFontRenderStyle::SetHinting(SkPaint::Hinting hinting) {
+ g_skia_hinting = hinting;
+}
+
+// static
+void WebFontRenderStyle::SetAutoHint(bool use_auto_hint) {
+ g_use_skia_auto_hint = use_auto_hint;
+}
+
+// static
+void WebFontRenderStyle::SetUseBitmaps(bool use_bitmaps) {
+ g_use_skia_bitmaps = use_bitmaps;
+}
+
+// static
+void WebFontRenderStyle::SetAntiAlias(bool use_anti_alias) {
+ g_use_skia_anti_alias = use_anti_alias;
+}
+
+// static
+void WebFontRenderStyle::SetSubpixelRendering(bool use_subpixel_rendering) {
+ g_use_skia_subpixel_rendering = use_subpixel_rendering;
+}
+
+// static
+void WebFontRenderStyle::SetSubpixelPositioning(bool use_subpixel_positioning) {
+ FontDescription::SetSubpixelPositioning(use_subpixel_positioning);
+}
+
+// static
+void WebFontRenderStyle::SetSystemFontFamily(const WebString& name) {
+ FontCache::SetSystemFontFamily(name);
+}
+
+// static
+WebFontRenderStyle WebFontRenderStyle::GetDefault() {
+ WebFontRenderStyle result;
+ result.use_anti_alias = g_use_skia_anti_alias;
+ result.hint_style = SkPaint::kNo_Hinting;
+ result.hint_style = g_skia_hinting;
+ result.use_bitmaps = g_use_skia_bitmaps;
+ result.use_auto_hint = g_use_skia_auto_hint;
+ result.use_anti_alias = g_use_skia_anti_alias;
+ result.use_subpixel_rendering = g_use_skia_subpixel_rendering;
+ result.use_subpixel_positioning = FontDescription::SubpixelPositioning();
+ return result;
+}
+
+void WebFontRenderStyle::OverrideWith(const WebFontRenderStyle& other) {
+ if (other.use_anti_alias != WebFontRenderStyle::kNoPreference)
+ use_anti_alias = other.use_anti_alias;
+
+ if (other.use_hinting != WebFontRenderStyle::kNoPreference) {
+ use_hinting = other.use_hinting;
+ hint_style = other.hint_style;
+ }
+
+ if (other.use_bitmaps != WebFontRenderStyle::kNoPreference)
+ use_bitmaps = other.use_bitmaps;
+ if (other.use_auto_hint != WebFontRenderStyle::kNoPreference)
+ use_auto_hint = other.use_auto_hint;
+ if (other.use_anti_alias != WebFontRenderStyle::kNoPreference)
+ use_anti_alias = other.use_anti_alias;
+ if (other.use_subpixel_rendering != WebFontRenderStyle::kNoPreference)
+ use_subpixel_rendering = other.use_subpixel_rendering;
+ if (other.use_subpixel_positioning != WebFontRenderStyle::kNoPreference)
+ use_subpixel_positioning = other.use_subpixel_positioning;
+}
+
+void WebFontRenderStyle::ApplyToPaintFont(PaintFont& font,
+ float device_scale_factor) const {
+ auto sk_hint_style = static_cast<SkPaint::Hinting>(hint_style);
+ font.SetAntiAlias(use_anti_alias);
+ font.SetHinting(sk_hint_style);
+ font.SetEmbeddedBitmapText(use_bitmaps);
+ font.SetAutohinted(use_auto_hint);
+ if (use_anti_alias)
+ font.SetLcdRenderText(use_subpixel_rendering);
+
+ // Force-enable subpixel positioning, except when full hinting is requested on
+ // low-dpi screen or when running layout tests.
+ bool force_subpixel_positioning =
+ !LayoutTestSupport::IsRunningLayoutTest() &&
+ (sk_hint_style != SkPaint::kFull_Hinting || device_scale_factor > 1.0f);
+
+ font.SetSubpixelText(force_subpixel_positioning || use_subpixel_positioning);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/web_font_typeface_factory.cc b/chromium/third_party/blink/renderer/platform/fonts/web_font_typeface_factory.cc
new file mode 100644
index 00000000000..1cefc86a44e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/web_font_typeface_factory.cc
@@ -0,0 +1,116 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/web_font_typeface_factory.h"
+
+#include "third_party/blink/renderer/platform/fonts/font_cache.h"
+#include "third_party/blink/renderer/platform/fonts/opentype/font_format_check.h"
+#include "third_party/blink/renderer/platform/histogram.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/skia/include/core/SkStream.h"
+#include "third_party/skia/include/core/SkTypeface.h"
+#if defined(OS_WIN) || defined(OS_MACOSX)
+#include "third_party/skia/include/ports/SkFontMgr_empty.h"
+#endif
+#if defined(OS_MACOSX)
+#include "third_party/blink/renderer/platform/fonts/mac/core_text_variations_support.h"
+#endif
+
+namespace blink {
+
+bool WebFontTypefaceFactory::CreateTypeface(sk_sp<SkData> sk_data,
+ sk_sp<SkTypeface>& typeface) {
+ CHECK(!typeface);
+
+ FontFormatCheck format_check(sk_data);
+
+ std::unique_ptr<SkStreamAsset> stream(new SkMemoryStream(sk_data));
+
+ if (!format_check.IsVariableFont() && !format_check.IsCbdtCblcColorFont() &&
+ !format_check.IsCff2OutlineFont() && !format_check.IsSbixColorFont()) {
+ typeface = DefaultFontManager()->makeFromStream(std::move(stream));
+ if (typeface) {
+ ReportWebFontInstantiationResult(kSuccessConventionalWebFont);
+ return true;
+ }
+ // Not UMA reporting general decoding errors as these are already recorded
+ // as kPackageFormatUnknown in FontResource.cpp.
+ return false;
+ }
+
+ if (format_check.IsCbdtCblcColorFont()) {
+ typeface = FreeTypeFontManager()->makeFromStream(std::move(stream));
+ if (typeface)
+ ReportWebFontInstantiationResult(kSuccessCbdtCblcColorFont);
+ }
+
+ if (format_check.IsSbixColorFont()) {
+ typeface = FontManagerForSbix()->makeFromStream(std::move(stream));
+ if (typeface) {
+ ReportWebFontInstantiationResult(kSuccessSbixFont);
+ }
+ }
+
+ if (format_check.IsCff2OutlineFont()) {
+ typeface = FreeTypeFontManager()->makeFromStream(std::move(stream));
+ if (typeface)
+ ReportWebFontInstantiationResult(kSuccessCff2Font);
+ }
+
+ // Variable CFF2 fonts must go through FreeType.
+ if (format_check.IsVariableFont() && !format_check.IsCff2OutlineFont()) {
+ typeface = FontManagerForVariations()->makeFromStream(std::move(stream));
+ if (typeface)
+ ReportWebFontInstantiationResult(kSuccessVariableWebFont);
+ else
+ ReportWebFontInstantiationResult(kErrorInstantiatingVariableFont);
+ }
+
+ return true;
+}
+
+sk_sp<SkFontMgr> WebFontTypefaceFactory::FontManagerForVariations() {
+#if defined(OS_WIN)
+ return FreeTypeFontManager();
+#else
+#if defined(OS_MACOSX)
+ if (!CoreTextVersionSupportsVariations())
+ return FreeTypeFontManager();
+#endif
+ return DefaultFontManager();
+#endif
+}
+
+sk_sp<SkFontMgr> WebFontTypefaceFactory::FontManagerForSbix() {
+#if defined(OS_MACOSX)
+ return DefaultFontManager();
+#endif
+ return FreeTypeFontManager();
+}
+
+sk_sp<SkFontMgr> WebFontTypefaceFactory::DefaultFontManager() {
+#if defined(OS_WIN)
+ return FontCache::GetFontCache()->FontManager();
+#else
+ return sk_sp<SkFontMgr>(SkFontMgr::RefDefault());
+#endif
+}
+
+sk_sp<SkFontMgr> WebFontTypefaceFactory::FreeTypeFontManager() {
+#if defined(OS_WIN) || defined(OS_MACOSX)
+ return sk_sp<SkFontMgr>(SkFontMgr_New_Custom_Empty());
+#else
+ return DefaultFontManager();
+#endif
+}
+
+void WebFontTypefaceFactory::ReportWebFontInstantiationResult(
+ WebFontInstantiationResult result) {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(
+ EnumerationHistogram, web_font_variable_fonts_ratio,
+ ("Blink.Fonts.VariableFontsRatio", kMaxWebFontInstantiationResult));
+ web_font_variable_fonts_ratio.Count(result);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/web_font_typeface_factory.h b/chromium/third_party/blink/renderer/platform/fonts/web_font_typeface_factory.h
new file mode 100644
index 00000000000..bd2854a279f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/web_font_typeface_factory.h
@@ -0,0 +1,51 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_WEB_FONT_TYPEFACE_FACTORY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_WEB_FONT_TYPEFACE_FACTORY_H_
+
+#include "third_party/skia/include/ports/SkFontMgr.h"
+
+#include "build/build_config.h"
+#if defined(OS_WIN) || defined(OS_MACOSX)
+#include "third_party/skia/include/ports/SkFontMgr_empty.h"
+#endif
+
+namespace blink {
+
+// Decides which Skia Fontmanager to use for instantiating a web font. In the
+// regular case, this would be default font manager used for the platform.
+// However, for variable fonts, color bitmap font formats and CFF2 fonts we want
+// to use FreeType on Windows and Mac.
+class WebFontTypefaceFactory {
+ public:
+ static bool CreateTypeface(const sk_sp<SkData>, sk_sp<SkTypeface>&);
+
+ // TODO(drott): This should be going away in favor of a new API on SkTypeface:
+ // https://bugs.chromium.org/p/skia/issues/detail?id=7121
+ static sk_sp<SkFontMgr> FontManagerForVariations();
+ static sk_sp<SkFontMgr> FontManagerForSbix();
+ static sk_sp<SkFontMgr> FreeTypeFontManager();
+
+ private:
+ // These values are written to logs. New enum values can be added, but
+ // existing enums must never be renumbered or deleted and reused.
+ enum WebFontInstantiationResult {
+ kErrorInstantiatingVariableFont = 0,
+ kSuccessConventionalWebFont = 1,
+ kSuccessVariableWebFont = 2,
+ kSuccessCbdtCblcColorFont = 3,
+ kSuccessCff2Font = 4,
+ kSuccessSbixFont = 5,
+ kMaxWebFontInstantiationResult = 6
+ };
+
+ static sk_sp<SkFontMgr> DefaultFontManager();
+
+ static void ReportWebFontInstantiationResult(WebFontInstantiationResult);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_WEB_FONT_TYPEFACE_FACTORY_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/win/font_cache_skia_win.cc b/chromium/third_party/blink/renderer/platform/fonts/win/font_cache_skia_win.cc
new file mode 100644
index 00000000000..6c6e206f803
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/win/font_cache_skia_win.cc
@@ -0,0 +1,426 @@
+/*
+ * Copyright (C) 2006, 2007 Apple Computer, Inc.
+ * Copyright (c) 2006, 2007, 2008, 2009, 2012 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 "third_party/blink/renderer/platform/fonts/font_cache.h"
+
+#include <memory>
+#include <utility>
+
+#include "SkFontMgr.h"
+#include "SkTypeface_win.h"
+#include "base/debug/alias.h"
+#include "third_party/blink/renderer/platform/fonts/bitmap_glyphs_blacklist.h"
+#include "third_party/blink/renderer/platform/fonts/font_description.h"
+#include "third_party/blink/renderer/platform/fonts/font_face_creation_params.h"
+#include "third_party/blink/renderer/platform/fonts/font_platform_data.h"
+#include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
+#include "third_party/blink/renderer/platform/fonts/win/font_fallback_win.h"
+#include "third_party/blink/renderer/platform/language.h"
+
+namespace blink {
+
+HashMap<String, sk_sp<SkTypeface>>* FontCache::sideloaded_fonts_ = nullptr;
+
+// Cached system font metrics.
+AtomicString* FontCache::menu_font_family_name_ = nullptr;
+int32_t FontCache::menu_font_height_ = 0;
+AtomicString* FontCache::small_caption_font_family_name_ = nullptr;
+int32_t FontCache::small_caption_font_height_ = 0;
+AtomicString* FontCache::status_font_family_name_ = nullptr;
+int32_t FontCache::status_font_height_ = 0;
+
+namespace {
+
+int32_t EnsureMinimumFontHeightIfNeeded(int32_t font_height) {
+ // Adjustment for codepage 936 to make the fonts more legible in Simplified
+ // Chinese. Please refer to LayoutThemeFontProviderWin.cpp for more
+ // information.
+ return (font_height < 12.0f) && (GetACP() == 936) ? 12.0f : font_height;
+}
+
+} // namespace
+
+// static
+void FontCache::AddSideloadedFontForTesting(sk_sp<SkTypeface> typeface) {
+ if (!sideloaded_fonts_)
+ sideloaded_fonts_ = new HashMap<String, sk_sp<SkTypeface>>;
+ SkString name;
+ typeface->getFamilyName(&name);
+ sideloaded_fonts_->Set(name.c_str(), std::move(typeface));
+}
+
+// static
+const AtomicString& FontCache::SystemFontFamily() {
+ return MenuFontFamily();
+}
+
+// static
+void FontCache::SetMenuFontMetrics(const wchar_t* family_name,
+ int32_t font_height) {
+ menu_font_family_name_ = new AtomicString(family_name);
+ menu_font_height_ = EnsureMinimumFontHeightIfNeeded(font_height);
+}
+
+// static
+void FontCache::SetSmallCaptionFontMetrics(const wchar_t* family_name,
+ int32_t font_height) {
+ small_caption_font_family_name_ = new AtomicString(family_name);
+ small_caption_font_height_ = EnsureMinimumFontHeightIfNeeded(font_height);
+}
+
+// static
+void FontCache::SetStatusFontMetrics(const wchar_t* family_name,
+ int32_t font_height) {
+ status_font_family_name_ = new AtomicString(family_name);
+ status_font_height_ = EnsureMinimumFontHeightIfNeeded(font_height);
+}
+
+FontCache::FontCache() : purge_prevent_count_(0) {
+ font_manager_ = sk_ref_sp(static_font_manager_);
+ if (!font_manager_) {
+ // This code path is only for unit tests. This SkFontMgr does not work in
+ // sandboxed environments, but injecting this initialization code to all
+ // unit tests isn't easy.
+ font_manager_ = SkFontMgr_New_DirectWrite();
+ // Set |is_test_font_mgr_| to capture if this is not happening in the
+ // production code. crbug.com/561873
+ is_test_font_mgr_ = true;
+ }
+ DCHECK(font_manager_.get());
+}
+
+// Given the desired base font, this will create a SimpleFontData for a specific
+// font that can be used to render the given range of characters.
+scoped_refptr<SimpleFontData> FontCache::PlatformFallbackFontForCharacter(
+ const FontDescription& font_description,
+ UChar32 character,
+ const SimpleFontData* original_font_data,
+ FontFallbackPriority fallback_priority) {
+ // First try the specified font with standard style & weight.
+ if (fallback_priority != FontFallbackPriority::kEmojiEmoji &&
+ (font_description.Style() == ItalicSlopeValue() ||
+ font_description.Weight() >= BoldWeightValue())) {
+ scoped_refptr<SimpleFontData> font_data =
+ FallbackOnStandardFontStyle(font_description, character);
+ if (font_data)
+ return font_data;
+ }
+
+ UScriptCode script;
+ const UChar* family = GetFallbackFamily(
+ character, font_description.GenericFamily(), font_description.Locale(),
+ &script, fallback_priority, font_manager_.get());
+ if (family) {
+ FontFaceCreationParams create_by_family(family);
+ FontPlatformData* data =
+ GetFontPlatformData(font_description, create_by_family);
+ if (data && data->FontContainsCharacter(character))
+ return FontDataFromFontPlatformData(data, kDoNotRetain);
+ }
+
+ if (use_skia_font_fallback_) {
+ const char* bcp47_locale = nullptr;
+ int locale_count = 0;
+ // If the font description has a locale, use that. Otherwise, Skia will
+ // fall back on the user's default locale.
+ // TODO(kulshin): extract locale fallback logic from
+ // FontCacheAndroid.cpp and share that code
+ if (font_description.Locale()) {
+ bcp47_locale = font_description.Locale()->LocaleForSkFontMgr();
+ locale_count = 1;
+ }
+
+ CString family_name = font_description.Family().Family().Utf8();
+
+ SkTypeface* typeface = font_manager_->matchFamilyStyleCharacter(
+ family_name.data(), font_description.SkiaFontStyle(), &bcp47_locale,
+ locale_count, character);
+ if (typeface) {
+ SkString skia_family;
+ typeface->getFamilyName(&skia_family);
+ FontFaceCreationParams create_by_family(ToAtomicString(skia_family));
+ FontPlatformData* data =
+ GetFontPlatformData(font_description, create_by_family);
+ if (data && data->FontContainsCharacter(character))
+ return FontDataFromFontPlatformData(data, kDoNotRetain);
+ }
+ }
+
+ // In production, these 3 font managers must match.
+ // They don't match in unit tests or in single process mode.
+ // Capture them in minidump for crbug.com/409784
+ SkFontMgr* font_mgr = font_manager_.get();
+ SkFontMgr* static_font_mgr = static_font_manager_;
+ SkFontMgr* skia_default_font_mgr = SkFontMgr::RefDefault().get();
+ base::debug::Alias(&font_mgr);
+ base::debug::Alias(&static_font_mgr);
+ base::debug::Alias(&skia_default_font_mgr);
+
+ // Last resort font list : PanUnicode. CJK fonts have a pretty
+ // large repertoire. Eventually, we need to scan all the fonts
+ // on the system to have a Firefox-like coverage.
+ // Make sure that all of them are lowercased.
+ const static wchar_t* const kCjkFonts[] = {
+ L"arial unicode ms", L"ms pgothic", L"simsun", L"gulim", L"pmingliu",
+ L"wenquanyi zen hei", // Partial CJK Ext. A coverage but more widely
+ // known to Chinese users.
+ L"ar pl shanheisun uni", L"ar pl zenkai uni",
+ L"han nom a", // Complete CJK Ext. A coverage.
+ L"code2000" // Complete CJK Ext. A coverage.
+ // CJK Ext. B fonts are not listed here because it's of no use
+ // with our current non-BMP character handling because we use
+ // Uniscribe for it and that code path does not go through here.
+ };
+
+ const static wchar_t* const kCommonFonts[] = {
+ L"tahoma", L"arial unicode ms", L"lucida sans unicode",
+ L"microsoft sans serif", L"palatino linotype",
+ // Six fonts below (and code2000 at the end) are not from MS, but
+ // once installed, cover a very wide range of characters.
+ L"dejavu serif", L"dejavu sasns", L"freeserif", L"freesans", L"gentium",
+ L"gentiumalt", L"ms pgothic", L"simsun", L"gulim", L"pmingliu",
+ L"code2000"};
+
+ const wchar_t* const* pan_uni_fonts = nullptr;
+ int num_fonts = 0;
+ if (script == USCRIPT_HAN) {
+ pan_uni_fonts = kCjkFonts;
+ num_fonts = WTF_ARRAY_LENGTH(kCjkFonts);
+ } else {
+ pan_uni_fonts = kCommonFonts;
+ num_fonts = WTF_ARRAY_LENGTH(kCommonFonts);
+ }
+ // Font returned from getFallbackFamily may not cover |character|
+ // because it's based on script to font mapping. This problem is
+ // critical enough for non-Latin scripts (especially Han) to
+ // warrant an additional (real coverage) check with fontCotainsCharacter.
+ for (int i = 0; i < num_fonts; ++i) {
+ family = pan_uni_fonts[i];
+ FontFaceCreationParams create_by_family(family);
+ FontPlatformData* data =
+ GetFontPlatformData(font_description, create_by_family);
+ if (data && data->FontContainsCharacter(character))
+ return FontDataFromFontPlatformData(data, kDoNotRetain);
+ }
+
+ return nullptr;
+}
+
+static inline bool DeprecatedEqualIgnoringCase(const AtomicString& a,
+ const SkString& b) {
+ return DeprecatedEqualIgnoringCase(a, ToAtomicString(b));
+}
+
+static bool TypefacesMatchesFamily(const SkTypeface* tf,
+ const AtomicString& family) {
+ SkTypeface::LocalizedStrings* actual_families =
+ tf->createFamilyNameIterator();
+ bool matches_requested_family = false;
+ SkTypeface::LocalizedString actual_family;
+
+ while (actual_families->next(&actual_family)) {
+ if (DeprecatedEqualIgnoringCase(family, actual_family.fString)) {
+ matches_requested_family = true;
+ break;
+ }
+ }
+ actual_families->unref();
+
+ // getFamilyName may return a name not returned by the
+ // createFamilyNameIterator.
+ // Specifically in cases where Windows substitutes the font based on the
+ // HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes registry
+ // entries.
+ if (!matches_requested_family) {
+ SkString family_name;
+ tf->getFamilyName(&family_name);
+ if (DeprecatedEqualIgnoringCase(family, family_name))
+ matches_requested_family = true;
+ }
+
+ return matches_requested_family;
+}
+
+static bool TypefacesHasWeightSuffix(const AtomicString& family,
+ AtomicString& adjusted_name,
+ FontSelectionValue& variant_weight) {
+ struct FamilyWeightSuffix {
+ const wchar_t* suffix;
+ size_t length;
+ FontSelectionValue weight;
+ };
+ // Mapping from suffix to weight from the DirectWrite documentation.
+ // http://msdn.microsoft.com/en-us/library/windows/desktop/dd368082.aspx
+ const static FamilyWeightSuffix kVariantForSuffix[] = {
+ {L" thin", 5, FontSelectionValue(100)},
+ {L" extralight", 11, FontSelectionValue(200)},
+ {L" ultralight", 11, FontSelectionValue(200)},
+ {L" light", 6, FontSelectionValue(300)},
+ {L" regular", 8, FontSelectionValue(400)},
+ {L" medium", 7, FontSelectionValue(500)},
+ {L" demibold", 9, FontSelectionValue(600)},
+ {L" semibold", 9, FontSelectionValue(600)},
+ {L" extrabold", 10, FontSelectionValue(800)},
+ {L" ultrabold", 10, FontSelectionValue(800)},
+ {L" black", 6, FontSelectionValue(900)},
+ {L" heavy", 6, FontSelectionValue(900)}};
+ size_t num_variants = WTF_ARRAY_LENGTH(kVariantForSuffix);
+ for (size_t i = 0; i < num_variants; i++) {
+ const FamilyWeightSuffix& entry = kVariantForSuffix[i];
+ if (family.EndsWith(entry.suffix, kTextCaseUnicodeInsensitive)) {
+ String family_name = family.GetString();
+ family_name.Truncate(family.length() - entry.length);
+ adjusted_name = AtomicString(family_name);
+ variant_weight = entry.weight;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool TypefacesHasStretchSuffix(const AtomicString& family,
+ AtomicString& adjusted_name,
+ FontSelectionValue& variant_stretch) {
+ struct FamilyStretchSuffix {
+ const wchar_t* suffix;
+ size_t length;
+ FontSelectionValue stretch;
+ };
+ // Mapping from suffix to stretch value from the DirectWrite documentation.
+ // http://msdn.microsoft.com/en-us/library/windows/desktop/dd368078.aspx
+ // Also includes Narrow as a synonym for Condensed to to support Arial
+ // Narrow and other fonts following the same naming scheme.
+ const static FamilyStretchSuffix kVariantForSuffix[] = {
+ {L" ultracondensed", 15, UltraCondensedWidthValue()},
+ {L" extracondensed", 15, ExtraCondensedWidthValue()},
+ {L" condensed", 10, CondensedWidthValue()},
+ {L" narrow", 7, CondensedWidthValue()},
+ {L" semicondensed", 14, SemiCondensedWidthValue()},
+ {L" semiexpanded", 13, SemiExpandedWidthValue()},
+ {L" expanded", 9, ExpandedWidthValue()},
+ {L" extraexpanded", 14, ExtraExpandedWidthValue()},
+ {L" ultraexpanded", 14, UltraExpandedWidthValue()}};
+ size_t num_variants = WTF_ARRAY_LENGTH(kVariantForSuffix);
+ for (size_t i = 0; i < num_variants; i++) {
+ const FamilyStretchSuffix& entry = kVariantForSuffix[i];
+ if (family.EndsWith(entry.suffix, kTextCaseUnicodeInsensitive)) {
+ String family_name = family.GetString();
+ family_name.Truncate(family.length() - entry.length);
+ adjusted_name = AtomicString(family_name);
+ variant_stretch = entry.stretch;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+std::unique_ptr<FontPlatformData> FontCache::CreateFontPlatformData(
+ const FontDescription& font_description,
+ const FontFaceCreationParams& creation_params,
+ float font_size,
+ AlternateFontName alternate_font_name) {
+ DCHECK_EQ(creation_params.CreationType(), kCreateFontByFamily);
+
+ CString name;
+ PaintTypeface paint_tf =
+ CreateTypeface(font_description, creation_params, name);
+ // Windows will always give us a valid pointer here, even if the face name
+ // is non-existent. We have to double-check and see if the family name was
+ // really used.
+ if (!paint_tf || !TypefacesMatchesFamily(paint_tf.ToSkTypeface().get(),
+ creation_params.Family())) {
+ AtomicString adjusted_name;
+ FontSelectionValue variant_weight;
+ FontSelectionValue variant_stretch;
+
+ // TODO: crbug.com/627143 LocalFontFaceSource.cpp, which implements
+ // retrieving src: local() font data uses getFontData, which in turn comes
+ // here, to retrieve fonts from the cache and specifies the argument to
+ // local() as family name. So we do not match by full font name or
+ // postscript name as the spec says:
+ // https://drafts.csswg.org/css-fonts-3/#src-desc
+
+ // Prevent one side effect of the suffix translation below where when
+ // matching local("Roboto Regular") it tries to find the closest match even
+ // though that can be a bold font in case of Roboto Bold.
+ if (alternate_font_name == AlternateFontName::kLocalUniqueFace) {
+ return nullptr;
+ }
+
+ if (alternate_font_name == AlternateFontName::kLastResort) {
+ if (!paint_tf)
+ return nullptr;
+ } else if (TypefacesHasWeightSuffix(creation_params.Family(), adjusted_name,
+ variant_weight)) {
+ FontFaceCreationParams adjusted_params(adjusted_name);
+ FontDescription adjusted_font_description = font_description;
+ adjusted_font_description.SetWeight(variant_weight);
+ paint_tf =
+ CreateTypeface(adjusted_font_description, adjusted_params, name);
+ if (!paint_tf || !TypefacesMatchesFamily(paint_tf.ToSkTypeface().get(),
+ adjusted_name)) {
+ return nullptr;
+ }
+
+ } else if (TypefacesHasStretchSuffix(creation_params.Family(),
+ adjusted_name, variant_stretch)) {
+ FontFaceCreationParams adjusted_params(adjusted_name);
+ FontDescription adjusted_font_description = font_description;
+ adjusted_font_description.SetStretch(variant_stretch);
+ paint_tf =
+ CreateTypeface(adjusted_font_description, adjusted_params, name);
+ if (!paint_tf || !TypefacesMatchesFamily(paint_tf.ToSkTypeface().get(),
+ adjusted_name)) {
+ return nullptr;
+ }
+ } else {
+ return nullptr;
+ }
+ }
+
+ const auto& tf = paint_tf.ToSkTypeface();
+ std::unique_ptr<FontPlatformData> result = std::make_unique<FontPlatformData>(
+ paint_tf, name.data(), font_size,
+ (font_description.Weight() >= BoldThreshold() && !tf->isBold()) ||
+ font_description.IsSyntheticBold(),
+ ((font_description.Style() == ItalicSlopeValue()) && !tf->isItalic()) ||
+ font_description.IsSyntheticItalic(),
+ font_description.Orientation());
+
+ result->SetAvoidEmbeddedBitmaps(
+ BitmapGlyphsBlacklist::AvoidEmbeddedBitmapsForTypeface(tf.get()));
+
+ return result;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/win/font_fallback_win.cc b/chromium/third_party/blink/renderer/platform/fonts/win/font_fallback_win.cc
new file mode 100644
index 00000000000..aaf5982c76c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/win/font_fallback_win.cc
@@ -0,0 +1,544 @@
+/*
+ * Copyright (c) 2006, 2007, 2008, 2009, 2010, 2012 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 "third_party/blink/renderer/platform/fonts/win/font_fallback_win.h"
+
+#include <unicode/uchar.h>
+#include <limits>
+#include "SkFontMgr.h"
+#include "SkTypeface.h"
+#include "third_party/blink/renderer/platform/fonts/font_cache.h"
+#include "third_party/blink/renderer/platform/text/icu_error.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+namespace {
+
+static inline bool IsFontPresent(const UChar* font_name,
+ SkFontMgr* font_manager) {
+ String family = font_name;
+ sk_sp<SkTypeface> tf(
+ font_manager->matchFamilyStyle(family.Utf8().data(), SkFontStyle()));
+ if (!tf)
+ return false;
+
+ SkTypeface::LocalizedStrings* actual_families =
+ tf->createFamilyNameIterator();
+ bool matches_requested_family = false;
+ SkTypeface::LocalizedString actual_family;
+ while (actual_families->next(&actual_family)) {
+ if (DeprecatedEqualIgnoringCase(
+ family, AtomicString::FromUTF8(actual_family.fString.c_str()))) {
+ matches_requested_family = true;
+ break;
+ }
+ }
+ actual_families->unref();
+
+ return matches_requested_family;
+}
+
+struct FontMapping {
+ const UChar* family_name;
+ const UChar* const* candidate_family_names;
+};
+// A simple mapping from UScriptCode to family name. This is a sparse array,
+// which works well since the range of UScriptCode values is small.
+typedef FontMapping ScriptToFontMap[USCRIPT_CODE_LIMIT];
+
+const UChar* FindMonospaceFontForScript(UScriptCode script) {
+ struct FontMap {
+ UScriptCode script;
+ const UChar* family;
+ };
+
+ static const FontMap kFontMap[] = {
+ {USCRIPT_HEBREW, L"courier new"}, {USCRIPT_ARABIC, L"courier new"},
+ };
+
+ for (const auto& font_family : kFontMap) {
+ if (font_family.script == script)
+ return font_family.family;
+ }
+ return nullptr;
+}
+
+void InitializeScriptFontMap(ScriptToFontMap& script_font_map) {
+ struct ScriptToFontFamilies {
+ UScriptCode script;
+ const UChar* const* families;
+ };
+
+ // For the following scripts, multiple fonts may be listed. They are tried
+ // in order. The first slot is preferred but the font may not be available,
+ // if so the remaining slots are tried in order.
+ // In general the order is the Windows 10 font follow by the 8.1, 8.0 and
+ // finally the font for Windows 7.
+ // For scripts where an optional or region specific font may be available
+ // that should be listed before the generic one.
+ // Based on the "Script and Font Support in Windows" MSDN documentation [1]
+ // with overrides and additional fallbacks as needed.
+ // 1: https://msdn.microsoft.com/en-us/goglobal/bb688099.aspx
+ static const UChar* const kArabicFonts[] = {L"Tahoma", L"Segoe UI", 0};
+ static const UChar* const kArmenianFonts[] = {L"Segoe UI", L"Sylfaen", 0};
+ static const UChar* const kBengaliFonts[] = {L"Nirmala UI", L"Vrinda", 0};
+ static const UChar* const kBrahmiFonts[] = {L"Segoe UI Historic", 0};
+ static const UChar* const kBrailleFonts[] = {L"Segoe UI Symbol", 0};
+ static const UChar* const kBugineseFonts[] = {L"Leelawadee UI", 0};
+ static const UChar* const kCanadianAaboriginalFonts[] = {L"Gadugi",
+ L"Euphemia", 0};
+ static const UChar* const kCarianFonts[] = {L"Segoe UI Historic", 0};
+ static const UChar* const kCherokeeFonts[] = {L"Gadugi", L"Plantagenet", 0};
+ static const UChar* const kCopticFonts[] = {L"Segoe UI Symbol", 0};
+ static const UChar* const kCuneiformFonts[] = {L"Segoe UI Historic", 0};
+ static const UChar* const kCypriotFonts[] = {L"Segoe UI Historic", 0};
+ static const UChar* const kCyrillicFonts[] = {L"Times New Roman", 0};
+ static const UChar* const kDeseretFonts[] = {L"Segoe UI Symbol", 0};
+ static const UChar* const kDevanagariFonts[] = {L"Nirmala UI", L"Mangal", 0};
+ static const UChar* const kEgyptianHieroglyphsFonts[] = {L"Segoe UI Historic",
+ 0};
+ static const UChar* const kEthiopicFonts[] = {L"Nyala",
+ L"Abyssinica SIL",
+ L"Ethiopia Jiret",
+ L"Visual Geez Unicode",
+ L"GF Zemen Unicode",
+ L"Ebrima",
+ 0};
+ static const UChar* const kGeorgianFonts[] = {L"Sylfaen", L"Segoe UI", 0};
+ static const UChar* const kGlagoliticFonts[] = {L"Segoe UI Historic",
+ L"Segoe UI Symbol", 0};
+ static const UChar* const kGothicFonts[] = {L"Segoe UI Historic",
+ L"Segoe UI Symbol", 0};
+ static const UChar* const kGreekFonts[] = {L"Times New Roman", 0};
+ static const UChar* const kGujaratiFonts[] = {L"Nirmala UI", L"Shruti", 0};
+ static const UChar* const kGurmukhiFonts[] = {L"Nirmala UI", L"Raavi", 0};
+ static const UChar* const kHangulFonts[] = {L"Malgun Gothic", L"Gulim", 0};
+ static const UChar* const kHebrewFonts[] = {L"David", L"Segoe UI", 0};
+ static const UChar* const kImperialAramaicFonts[] = {L"Segoe UI Historic", 0};
+ static const UChar* const kInscriptionalPahlaviFonts[] = {
+ L"Segoe UI Historic", 0};
+ static const UChar* const kInscriptionalParthianFonts[] = {
+ L"Segoe UI Historic", 0};
+ static const UChar* const kJavaneseFonts[] = {L"Javanese Text", 0};
+ static const UChar* const kKannadaFonts[] = {L"Tunga", L"Nirmala UI", 0};
+ static const UChar* const kKatakanaOrHiraganaFonts[] = {
+ L"Meiryo", L"Yu Gothic", L"MS PGothic", L"Microsoft YaHei", 0};
+ static const UChar* const kKharoshthiFonts[] = {L"Segoe UI Historic", 0};
+ // Try Khmer OS before Vista fonts as it goes along better with Latin
+ // and looks better/larger for the same size.
+ static const UChar* const kKhmerFonts[] = {
+ L"Leelawadee UI", L"Khmer UI", L"Khmer OS", L"MoolBoran", L"DaunPenh", 0};
+ static const UChar* const kLaoFonts[] = {L"Leelawadee UI",
+ L"Lao UI",
+ L"DokChampa",
+ L"Saysettha OT",
+ L"Phetsarath OT",
+ L"Code2000",
+ 0};
+ static const UChar* const kLatinFonts[] = {L"Times New Roman", 0};
+ static const UChar* const kLisuFonts[] = {L"Segoe UI", 0};
+ static const UChar* const kLycianFonts[] = {L"Segoe UI Historic", 0};
+ static const UChar* const kLydianFonts[] = {L"Segoe UI Historic", 0};
+ static const UChar* const kMalayalamFonts[] = {L"Nirmala UI", L"Kartika", 0};
+ static const UChar* const kMeroiticCursiveFonts[] = {L"Segoe UI Historic",
+ L"Segoe UI Symbol", 0};
+ static const UChar* const kMongolianFonts[] = {L"Mongolian Baiti", 0};
+ static const UChar* const kMyanmarFonts[] = {
+ L"Myanmar Text", L"Padauk", L"Parabaik", L"Myanmar3", L"Code2000", 0};
+ static const UChar* const kNewTaiLueFonts[] = {L"Microsoft New Tai Lue", 0};
+ static const UChar* const kNkoFonts[] = {L"Ebrima", 0};
+ static const UChar* const kOghamFonts[] = {L"Segoe UI Historic",
+ L"Segoe UI Symbol", 0};
+ static const UChar* const kOlChikiFonts[] = {L"Nirmala UI", 0};
+ static const UChar* const kOldItalicFonts[] = {L"Segoe UI Historic",
+ L"Segoe UI Symbol", 0};
+ static const UChar* const kOldPersianFonts[] = {L"Segoe UI Historic", 0};
+ static const UChar* const kOldSouthArabianFonts[] = {L"Segoe UI Historic", 0};
+ static const UChar* const kOriyaFonts[] = {L"Kalinga", L"ori1Uni",
+ L"Lohit Oriya", L"Nirmala UI", 0};
+ static const UChar* const kOrkhonFonts[] = {L"Segoe UI Historic",
+ L"Segoe UI Symbol", 0};
+ static const UChar* const kOsmanyaFonts[] = {L"Ebrima", 0};
+ static const UChar* const kPhagsPaFonts[] = {L"Microsoft PhagsPa", 0};
+ static const UChar* const kRunicFonts[] = {L"Segoe UI Historic",
+ L"Segoe UI Symbol", 0};
+ static const UChar* const kShavianFonts[] = {L"Segoe UI Historic", 0};
+ static const UChar* const kSimplifiedHanFonts[] = {L"simsun",
+ L"Microsoft YaHei", 0};
+ static const UChar* const kSinhalaFonts[] = {
+ L"Iskoola Pota", L"AksharUnicode", L"Nirmala UI", 0};
+ static const UChar* const kSoraSompengFonts[] = {L"Nirmala UI", 0};
+ static const UChar* const kSymbolsFonts[] = {L"Segoe UI Symbol", 0};
+ static const UChar* const kSyriacFonts[] = {
+ L"Estrangelo Edessa", L"Estrangelo Nisibin", L"Code2000", 0};
+ static const UChar* const kTaiLeFonts[] = {L"Microsoft Tai Le", 0};
+ static const UChar* const kTamilFonts[] = {L"Nirmala UI", L"Latha", 0};
+ static const UChar* const kTeluguFonts[] = {L"Nirmala UI", L"Gautami", 0};
+ static const UChar* const kThaanaFonts[] = {L"MV Boli", 0};
+ static const UChar* const kThaiFonts[] = {L"Tahoma", L"Leelawadee UI",
+ L"Leelawadee", 0};
+ static const UChar* const kTibetanFonts[] = {
+ L"Microsoft Himalaya", L"Jomolhari", L"Tibetan Machine Uni", 0};
+ static const UChar* const kTifinaghFonts[] = {L"Ebrima", 0};
+ static const UChar* const kTraditionalHanFonts[] = {L"pmingliu",
+ L"Microsoft JhengHei", 0};
+ static const UChar* const kVaiFonts[] = {L"Ebrima", 0};
+ static const UChar* const kYiFonts[] = {L"Microsoft Yi Baiti", L"Nuosu SIL",
+ L"Code2000", 0};
+
+ static const ScriptToFontFamilies kScriptToFontFamilies[] = {
+ {USCRIPT_ARABIC, kArabicFonts},
+ {USCRIPT_ARMENIAN, kArmenianFonts},
+ {USCRIPT_BENGALI, kBengaliFonts},
+ {USCRIPT_BRAHMI, kBrahmiFonts},
+ {USCRIPT_BRAILLE, kBrailleFonts},
+ {USCRIPT_BUGINESE, kBugineseFonts},
+ {USCRIPT_CANADIAN_ABORIGINAL, kCanadianAaboriginalFonts},
+ {USCRIPT_CARIAN, kCarianFonts},
+ {USCRIPT_CHEROKEE, kCherokeeFonts},
+ {USCRIPT_COPTIC, kCopticFonts},
+ {USCRIPT_CUNEIFORM, kCuneiformFonts},
+ {USCRIPT_CYPRIOT, kCypriotFonts},
+ {USCRIPT_CYRILLIC, kCyrillicFonts},
+ {USCRIPT_DESERET, kDeseretFonts},
+ {USCRIPT_DEVANAGARI, kDevanagariFonts},
+ {USCRIPT_EGYPTIAN_HIEROGLYPHS, kEgyptianHieroglyphsFonts},
+ {USCRIPT_ETHIOPIC, kEthiopicFonts},
+ {USCRIPT_GEORGIAN, kGeorgianFonts},
+ {USCRIPT_GLAGOLITIC, kGlagoliticFonts},
+ {USCRIPT_GOTHIC, kGothicFonts},
+ {USCRIPT_GREEK, kGreekFonts},
+ {USCRIPT_GUJARATI, kGujaratiFonts},
+ {USCRIPT_GURMUKHI, kGurmukhiFonts},
+ {USCRIPT_HANGUL, kHangulFonts},
+ {USCRIPT_HEBREW, kHebrewFonts},
+ {USCRIPT_HIRAGANA, kKatakanaOrHiraganaFonts},
+ {USCRIPT_IMPERIAL_ARAMAIC, kImperialAramaicFonts},
+ {USCRIPT_INSCRIPTIONAL_PAHLAVI, kInscriptionalPahlaviFonts},
+ {USCRIPT_INSCRIPTIONAL_PARTHIAN, kInscriptionalParthianFonts},
+ {USCRIPT_JAVANESE, kJavaneseFonts},
+ {USCRIPT_KANNADA, kKannadaFonts},
+ {USCRIPT_KATAKANA, kKatakanaOrHiraganaFonts},
+ {USCRIPT_KATAKANA_OR_HIRAGANA, kKatakanaOrHiraganaFonts},
+ {USCRIPT_KHAROSHTHI, kKharoshthiFonts},
+ {USCRIPT_KHMER, kKhmerFonts},
+ {USCRIPT_LAO, kLaoFonts},
+ {USCRIPT_LATIN, kLatinFonts},
+ {USCRIPT_LISU, kLisuFonts},
+ {USCRIPT_LYCIAN, kLycianFonts},
+ {USCRIPT_LYDIAN, kLydianFonts},
+ {USCRIPT_MALAYALAM, kMalayalamFonts},
+ {USCRIPT_MEROITIC_CURSIVE, kMeroiticCursiveFonts},
+ {USCRIPT_MONGOLIAN, kMongolianFonts},
+ {USCRIPT_MYANMAR, kMyanmarFonts},
+ {USCRIPT_NEW_TAI_LUE, kNewTaiLueFonts},
+ {USCRIPT_NKO, kNkoFonts},
+ {USCRIPT_OGHAM, kOghamFonts},
+ {USCRIPT_OL_CHIKI, kOlChikiFonts},
+ {USCRIPT_OLD_ITALIC, kOldItalicFonts},
+ {USCRIPT_OLD_PERSIAN, kOldPersianFonts},
+ {USCRIPT_OLD_SOUTH_ARABIAN, kOldSouthArabianFonts},
+ {USCRIPT_ORIYA, kOriyaFonts},
+ {USCRIPT_ORKHON, kOrkhonFonts},
+ {USCRIPT_OSMANYA, kOsmanyaFonts},
+ {USCRIPT_PHAGS_PA, kPhagsPaFonts},
+ {USCRIPT_RUNIC, kRunicFonts},
+ {USCRIPT_SHAVIAN, kShavianFonts},
+ {USCRIPT_SIMPLIFIED_HAN, kSimplifiedHanFonts},
+ {USCRIPT_SINHALA, kSinhalaFonts},
+ {USCRIPT_SORA_SOMPENG, kSoraSompengFonts},
+ {USCRIPT_SYMBOLS, kSymbolsFonts},
+ {USCRIPT_SYRIAC, kSyriacFonts},
+ {USCRIPT_TAI_LE, kTaiLeFonts},
+ {USCRIPT_TAMIL, kTamilFonts},
+ {USCRIPT_TELUGU, kTeluguFonts},
+ {USCRIPT_THAANA, kThaanaFonts},
+ {USCRIPT_THAI, kThaiFonts},
+ {USCRIPT_TIBETAN, kTibetanFonts},
+ {USCRIPT_TIFINAGH, kTifinaghFonts},
+ {USCRIPT_TRADITIONAL_HAN, kTraditionalHanFonts},
+ {USCRIPT_VAI, kVaiFonts},
+ {USCRIPT_YI, kYiFonts}};
+
+ for (const auto& font_family : kScriptToFontFamilies) {
+ script_font_map[font_family.script].candidate_family_names =
+ font_family.families;
+ }
+
+ // Initialize the locale-dependent mapping from system locale.
+ UScriptCode han_script = LayoutLocale::GetSystem().GetScriptForHan();
+ DCHECK(han_script != USCRIPT_HAN);
+ if (script_font_map[han_script].candidate_family_names) {
+ script_font_map[USCRIPT_HAN].candidate_family_names =
+ script_font_map[han_script].candidate_family_names;
+ }
+}
+
+void FindFirstExistingCandidateFont(FontMapping& script_font_mapping,
+ SkFontMgr* font_manager) {
+ for (const UChar* const* family_ptr =
+ script_font_mapping.candidate_family_names;
+ *family_ptr; family_ptr++) {
+ if (IsFontPresent(*family_ptr, font_manager)) {
+ script_font_mapping.family_name = *family_ptr;
+ break;
+ }
+ }
+ script_font_mapping.candidate_family_names = nullptr;
+}
+
+// There are a lot of characters in USCRIPT_COMMON that can be covered
+// by fonts for scripts closely related to them. See
+// http://unicode.org/cldr/utility/list-unicodeset.jsp?a=[:Script=Common:]
+// FIXME: make this more efficient with a wider coverage
+UScriptCode GetScriptBasedOnUnicodeBlock(int ucs4) {
+ UBlockCode block = ublock_getCode(ucs4);
+ switch (block) {
+ case UBLOCK_CJK_SYMBOLS_AND_PUNCTUATION:
+ return USCRIPT_HAN;
+ case UBLOCK_HIRAGANA:
+ case UBLOCK_KATAKANA:
+ return USCRIPT_KATAKANA_OR_HIRAGANA;
+ case UBLOCK_ARABIC:
+ return USCRIPT_ARABIC;
+ case UBLOCK_THAI:
+ return USCRIPT_THAI;
+ case UBLOCK_GREEK:
+ return USCRIPT_GREEK;
+ case UBLOCK_DEVANAGARI:
+ // For Danda and Double Danda (U+0964, U+0965), use a Devanagari
+ // font for now although they're used by other scripts as well.
+ // Without a context, we can't do any better.
+ return USCRIPT_DEVANAGARI;
+ case UBLOCK_ARMENIAN:
+ return USCRIPT_ARMENIAN;
+ case UBLOCK_GEORGIAN:
+ return USCRIPT_GEORGIAN;
+ case UBLOCK_KANNADA:
+ return USCRIPT_KANNADA;
+ case UBLOCK_GOTHIC:
+ return USCRIPT_GOTHIC;
+ default:
+ return USCRIPT_COMMON;
+ }
+}
+
+UScriptCode GetScript(int ucs4) {
+ ICUError err;
+ UScriptCode script = uscript_getScript(ucs4, &err);
+ // If script is invalid, common or inherited or there's an error,
+ // infer a script based on the unicode block of a character.
+ if (script <= USCRIPT_INHERITED || U_FAILURE(err))
+ script = GetScriptBasedOnUnicodeBlock(ucs4);
+ return script;
+}
+
+const UChar* GetFontBasedOnUnicodeBlock(UBlockCode block_code,
+ SkFontMgr* font_manager) {
+ static const UChar* const kEmojiFonts[] = {L"Segoe UI Emoji",
+ L"Segoe UI Symbol"};
+ static const UChar* const kMathFonts[] = {L"Cambria Math", L"Segoe UI Symbol",
+ L"Code2000"};
+ static const UChar* const kSymbolFont = L"Segoe UI Symbol";
+ static const UChar* emoji_font = 0;
+ static const UChar* math_font = 0;
+ static bool initialized = false;
+ if (!initialized) {
+ for (size_t i = 0; i < WTF_ARRAY_LENGTH(kEmojiFonts); i++) {
+ if (IsFontPresent(kEmojiFonts[i], font_manager)) {
+ emoji_font = kEmojiFonts[i];
+ break;
+ }
+ }
+ for (size_t i = 0; i < WTF_ARRAY_LENGTH(kMathFonts); i++) {
+ if (IsFontPresent(kMathFonts[i], font_manager)) {
+ math_font = kMathFonts[i];
+ break;
+ }
+ }
+ initialized = true;
+ }
+
+ switch (block_code) {
+ case UBLOCK_EMOTICONS:
+ case UBLOCK_ENCLOSED_ALPHANUMERIC_SUPPLEMENT:
+ return emoji_font;
+ case UBLOCK_PLAYING_CARDS:
+ case UBLOCK_MISCELLANEOUS_SYMBOLS:
+ case UBLOCK_MISCELLANEOUS_SYMBOLS_AND_ARROWS:
+ case UBLOCK_MISCELLANEOUS_SYMBOLS_AND_PICTOGRAPHS:
+ case UBLOCK_TRANSPORT_AND_MAP_SYMBOLS:
+ case UBLOCK_ALCHEMICAL_SYMBOLS:
+ case UBLOCK_DINGBATS:
+ case UBLOCK_GOTHIC:
+ return kSymbolFont;
+ case UBLOCK_ARROWS:
+ case UBLOCK_MATHEMATICAL_OPERATORS:
+ case UBLOCK_MISCELLANEOUS_TECHNICAL:
+ case UBLOCK_GEOMETRIC_SHAPES:
+ case UBLOCK_MISCELLANEOUS_MATHEMATICAL_SYMBOLS_A:
+ case UBLOCK_SUPPLEMENTAL_ARROWS_A:
+ case UBLOCK_SUPPLEMENTAL_ARROWS_B:
+ case UBLOCK_MISCELLANEOUS_MATHEMATICAL_SYMBOLS_B:
+ case UBLOCK_SUPPLEMENTAL_MATHEMATICAL_OPERATORS:
+ case UBLOCK_MATHEMATICAL_ALPHANUMERIC_SYMBOLS:
+ case UBLOCK_ARABIC_MATHEMATICAL_ALPHABETIC_SYMBOLS:
+ case UBLOCK_GEOMETRIC_SHAPES_EXTENDED:
+ return math_font;
+ default:
+ return 0;
+ };
+}
+
+} // namespace
+
+// FIXME: 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.
+// (at least, read values from the registry for IE font settings).
+// - Support generic families (from FontDescription)
+// - If the default font for a script is not available,
+// try some more fonts known to support it. Finally, we can
+// 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) converted from
+// GLYPHSET (BMP) or directly read from truetype cmap tables to
+// keep track of which character is supported by which font
+// - Update script_font_cache in response to WM_FONTCHANGE
+
+const UChar* GetFontFamilyForScript(UScriptCode script,
+ FontDescription::GenericFamilyType generic,
+ SkFontMgr* font_manager) {
+ static ScriptToFontMap script_font_map;
+ static bool initialized = false;
+ if (!initialized) {
+ InitializeScriptFontMap(script_font_map);
+ initialized = true;
+ }
+
+ if (script == USCRIPT_INVALID_CODE)
+ return 0;
+ DCHECK_LT(script, USCRIPT_CODE_LIMIT);
+ if (generic == FontDescription::kMonospaceFamily) {
+ const UChar* monospace_family = FindMonospaceFontForScript(script);
+ if (monospace_family)
+ return monospace_family;
+ }
+ if (script_font_map[script].candidate_family_names)
+ FindFirstExistingCandidateFont(script_font_map[script], font_manager);
+ return script_font_map[script].family_name;
+}
+
+// FIXME:
+// - 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 UChar* GetFallbackFamily(UChar32 character,
+ FontDescription::GenericFamilyType generic,
+ const LayoutLocale* content_locale,
+ UScriptCode* script_checked,
+ FontFallbackPriority fallback_priority,
+ SkFontMgr* font_manager) {
+ DCHECK(character);
+ DCHECK(font_manager);
+ UBlockCode block = fallback_priority == FontFallbackPriority::kEmojiEmoji
+ ? UBLOCK_EMOTICONS
+ : ublock_getCode(character);
+ const UChar* family = GetFontBasedOnUnicodeBlock(block, font_manager);
+ if (family) {
+ if (script_checked)
+ *script_checked = USCRIPT_INVALID_CODE;
+ return family;
+ }
+
+ UScriptCode script = GetScript(character);
+
+ // For the full-width ASCII characters (U+FF00 - U+FF5E), use the font for
+ // Han (determined in a locale-dependent way above). Full-width ASCII
+ // characters are rather widely used in Japanese and Chinese documents and
+ // they're fully covered by Chinese, Japanese and Korean fonts.
+ if (0xFF00 < character && character < 0xFF5F)
+ script = USCRIPT_HAN;
+
+ if (script == USCRIPT_COMMON)
+ script = GetScriptBasedOnUnicodeBlock(character);
+
+ // For unified-Han scripts, try the lang attribute, system, or
+ // accept-languages.
+ if (script == USCRIPT_HAN) {
+ if (const LayoutLocale* locale_for_han =
+ LayoutLocale::LocaleForHan(content_locale))
+ script = locale_for_han->GetScriptForHan();
+ // If still unknown, USCRIPT_HAN uses UI locale.
+ // See initializeScriptFontMap().
+ }
+
+ family = GetFontFamilyForScript(script, generic, font_manager);
+ // Another lame work-around to cover non-BMP characters.
+ // If the font family for script is not found or the character is
+ // not in BMP (> U+FFFF), we resort to the hard-coded list of
+ // fallback fonts for now.
+ if (!family || character > 0xFFFF) {
+ int plane = character >> 16;
+ switch (plane) {
+ case 1:
+ family = L"code2001";
+ break;
+ case 2:
+ // Use a Traditional Chinese ExtB font if in Traditional Chinese locale.
+ // Otherwise, use a Simplified Chinese ExtB font. Windows Japanese
+ // fonts do support a small subset of ExtB (that are included in JIS X
+ // 0213), but its coverage is rather sparse.
+ // Eventually, this should be controlled by lang/xml:lang.
+ if (icu::Locale::getDefault() == icu::Locale::getTraditionalChinese())
+ family = L"pmingliu-extb";
+ else
+ family = L"simsun-extb";
+ break;
+ default:
+ family = L"lucida sans unicode";
+ }
+ }
+
+ if (script_checked)
+ *script_checked = script;
+ return family;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/win/font_fallback_win.h b/chromium/third_party/blink/renderer/platform/fonts/win/font_fallback_win.h
new file mode 100644
index 00000000000..55681fae264
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/win/font_fallback_win.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2006, 2007, 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_WIN_FONT_FALLBACK_WIN_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_WIN_FONT_FALLBACK_WIN_H_
+
+#include <unicode/locid.h>
+#include <unicode/uscript.h>
+#include <usp10.h>
+#include <wchar.h>
+#include <windows.h>
+#include "third_party/blink/renderer/platform/fonts/font_description.h"
+#include "third_party/blink/renderer/platform/fonts/font_fallback_priority.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+class SkFontMgr;
+
+namespace blink {
+
+// Return a font family that can render |character| based on what script
+// that characters belong to.
+// When scriptChecked is non-zero, the script used to determine
+// the family is returned.
+PLATFORM_EXPORT const UChar* GetFallbackFamily(
+ UChar32 character,
+ FontDescription::GenericFamilyType,
+ const LayoutLocale* content_locale,
+ UScriptCode* script_checked,
+ FontFallbackPriority,
+ SkFontMgr* font_manager);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_WIN_FONT_FALLBACK_WIN_H_
diff --git a/chromium/third_party/blink/renderer/platform/fonts/win/font_platform_data_win.cc b/chromium/third_party/blink/renderer/platform/fonts/win/font_platform_data_win.cc
new file mode 100644
index 00000000000..c655c41c2b3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/fonts/win/font_platform_data_win.cc
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2006, 2007 Apple Computer, Inc.
+ * Copyright (c) 2006, 2007, 2008, 2009, 2012 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 "third_party/blink/renderer/platform/fonts/font_platform_data.h"
+
+#include <windows.h>
+#include "SkTypeface.h"
+#include "third_party/blink/renderer/platform/fonts/font_cache.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
+#include "third_party/blink/renderer/platform/layout_test_support.h"
+
+namespace blink {
+
+// Maximum font size, in pixels, at which embedded bitmaps will be used
+// if available.
+const float kMaxSizeForEmbeddedBitmap = 24.0f;
+
+void FontPlatformData::SetupPaintFont(PaintFont* font,
+ float,
+ const Font*) const {
+ const float ts = text_size_ >= 0 ? text_size_ : 12;
+ font->SetTextSize(SkFloatToScalar(text_size_));
+ font->SetTypeface(paint_typeface_);
+ font->SetFakeBoldText(synthetic_bold_);
+ font->SetTextSkewX(synthetic_italic_ ? -SK_Scalar1 / 4 : 0);
+
+ uint32_t text_flags = PaintTextFlags();
+ uint32_t flags = font->flags();
+ static const uint32_t kTextFlagsMask =
+ SkPaint::kAntiAlias_Flag | SkPaint::kLCDRenderText_Flag |
+ SkPaint::kEmbeddedBitmapText_Flag | SkPaint::kSubpixelText_Flag;
+ flags &= ~kTextFlagsMask;
+
+ if (ts <= kMaxSizeForEmbeddedBitmap)
+ flags |= SkPaint::kEmbeddedBitmapText_Flag;
+
+ // Only use sub-pixel positioning if anti aliasing is enabled. Otherwise,
+ // without font smoothing, subpixel text positioning leads to uneven spacing
+ // since subpixel test placement coordinates would be passed to Skia, which
+ // only has non-antialiased glyphs to draw, so they necessarily get clamped at
+ // pixel positions, which leads to uneven spacing, either too close or too far
+ // away from adjacent glyphs. We avoid this by linking the two flags.
+ if (text_flags & SkPaint::kAntiAlias_Flag)
+ flags |= SkPaint::kSubpixelText_Flag;
+
+ if (LayoutTestSupport::IsRunningLayoutTest() &&
+ !LayoutTestSupport::IsTextSubpixelPositioningAllowedForTest())
+ flags &= ~SkPaint::kSubpixelText_Flag;
+
+ SkASSERT(!(text_flags & ~kTextFlagsMask));
+ flags |= text_flags;
+
+ font->SetFlags(flags);
+
+ font->SetEmbeddedBitmapText(!avoid_embedded_bitmaps_);
+}
+
+static bool IsWebFont(const String& family_name) {
+ // Web-fonts have artifical names constructed to always be:
+ // 1. 24 characters, followed by a '\0'
+ // 2. the last two characters are '=='
+ return family_name.length() == 24 && '=' == family_name[22] &&
+ '=' == family_name[23];
+}
+
+static int ComputePaintTextFlags(String font_family_name) {
+ if (LayoutTestSupport::IsRunningLayoutTest())
+ return LayoutTestSupport::IsFontAntialiasingEnabledForTest()
+ ? SkPaint::kAntiAlias_Flag
+ : 0;
+
+ int text_flags = 0;
+ if (FontCache::GetFontCache()->AntialiasedTextEnabled()) {
+ int lcd_flag = FontCache::GetFontCache()->LcdTextEnabled()
+ ? SkPaint::kLCDRenderText_Flag
+ : 0;
+ text_flags = SkPaint::kAntiAlias_Flag | lcd_flag;
+ }
+
+ // Many web-fonts are so poorly hinted that they are terrible to read when
+ // drawn in BW. In these cases, we have decided to FORCE these fonts to be
+ // drawn with at least grayscale AA, even when the System (getSystemTextFlags)
+ // tells us to draw only in BW.
+ if (IsWebFont(font_family_name))
+ text_flags |= SkPaint::kAntiAlias_Flag;
+
+ return text_flags;
+}
+
+void FontPlatformData::QuerySystemForRenderStyle() {
+ paint_text_flags_ = ComputePaintTextFlags(FontFamilyName());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/DEPS b/chromium/third_party/blink/renderer/platform/geometry/DEPS
new file mode 100644
index 00000000000..504247153f6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+ # To whitelist base/ stuff Blink is allowed to include, we list up all
+ # directories and files instead of writing 'base/'.
+ "+ui/gfx/geometry",
+]
diff --git a/chromium/third_party/blink/renderer/platform/geometry/cg/float_point_cg.cc b/chromium/third_party/blink/renderer/platform/geometry/cg/float_point_cg.cc
new file mode 100644
index 00000000000..ddc4b9bdc6f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/cg/float_point_cg.cc
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2004, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2005 Nokia. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/geometry/float_point.h"
+
+#include <ApplicationServices/ApplicationServices.h>
+
+namespace blink {
+
+FloatPoint::FloatPoint(const CGPoint& p) : x_(p.x), y_(p.y) {}
+
+FloatPoint::operator CGPoint() const {
+ return CGPointMake(x_, y_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/cg/float_rect_cg.cc b/chromium/third_party/blink/renderer/platform/geometry/cg/float_rect_cg.cc
new file mode 100644
index 00000000000..b7023895e16
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/cg/float_rect_cg.cc
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2005 Nokia. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/geometry/float_rect.h"
+
+#include <ApplicationServices/ApplicationServices.h>
+
+namespace blink {
+
+FloatRect::FloatRect(const CGRect& r) : location_(r.origin), size_(r.size) {}
+
+FloatRect::operator CGRect() const {
+ return CGRectMake(X(), Y(), Width(), Height());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/cg/float_size_cg.cc b/chromium/third_party/blink/renderer/platform/geometry/cg/float_size_cg.cc
new file mode 100644
index 00000000000..0ab9acace91
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/cg/float_size_cg.cc
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2005 Nokia. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/geometry/float_size.h"
+
+#include <ApplicationServices/ApplicationServices.h>
+
+namespace blink {
+
+FloatSize::FloatSize(const CGSize& s) : width_(s.width), height_(s.height) {}
+
+FloatSize::operator CGSize() const {
+ return CGSizeMake(width_, height_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/cg/int_point_cg.cc b/chromium/third_party/blink/renderer/platform/geometry/cg/int_point_cg.cc
new file mode 100644
index 00000000000..fc8ddbc0c98
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/cg/int_point_cg.cc
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2004, 2005, 2006 Apple Computer, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/geometry/int_point.h"
+
+#include <ApplicationServices/ApplicationServices.h>
+
+namespace blink {
+
+IntPoint::IntPoint(const CGPoint& p)
+ : x_(static_cast<int>(p.x)), y_(static_cast<int>(p.y)) {}
+
+IntPoint::operator CGPoint() const {
+ return CGPointMake(x_, y_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/cg/int_rect_cg.cc b/chromium/third_party/blink/renderer/platform/geometry/cg/int_rect_cg.cc
new file mode 100644
index 00000000000..39b91b130ae
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/cg/int_rect_cg.cc
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2003, 2006 Apple Computer, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/geometry/int_rect.h"
+
+#include <ApplicationServices/ApplicationServices.h>
+
+namespace blink {
+
+IntRect::operator CGRect() const {
+ return CGRectMake(X(), Y(), Width(), Height());
+}
+
+IntRect EnclosingIntRect(const CGRect& rect) {
+ int l = static_cast<int>(floorf(rect.origin.x));
+ int t = static_cast<int>(floorf(rect.origin.y));
+ int r = static_cast<int>(ceilf(CGRectGetMaxX(rect)));
+ int b = static_cast<int>(ceilf(CGRectGetMaxY(rect)));
+ return IntRect(l, t, r - l, b - t);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/cg/int_size_cg.cc b/chromium/third_party/blink/renderer/platform/geometry/cg/int_size_cg.cc
new file mode 100644
index 00000000000..72ef202a810
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/cg/int_size_cg.cc
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/geometry/int_size.h"
+
+#include <ApplicationServices/ApplicationServices.h>
+
+namespace blink {
+
+IntSize::IntSize(const CGSize& s)
+ : width_(static_cast<int>(s.width)), height_(static_cast<int>(s.height)) {}
+
+IntSize::operator CGSize() const {
+ return CGSizeMake(width_, height_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/double_point.cc b/chromium/third_party/blink/renderer/platform/geometry/double_point.cc
new file mode 100644
index 00000000000..5e5baca9f7e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/double_point.cc
@@ -0,0 +1,36 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/geometry/double_point.h"
+
+#include <algorithm>
+#include "third_party/blink/renderer/platform/geometry/float_size.h"
+#include "third_party/blink/renderer/platform/geometry/layout_point.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+DoublePoint::DoublePoint(const LayoutPoint& p)
+ : x_(p.X().ToDouble()), y_(p.Y().ToDouble()) {}
+
+DoublePoint::DoublePoint(const FloatSize& size)
+ : x_(size.Width()), y_(size.Height()) {}
+
+DoublePoint DoublePoint::ExpandedTo(const DoublePoint& other) const {
+ return DoublePoint(std::max(x_, other.x_), std::max(y_, other.y_));
+}
+
+DoublePoint DoublePoint::ShrunkTo(const DoublePoint& other) const {
+ return DoublePoint(std::min(x_, other.x_), std::min(y_, other.y_));
+}
+
+std::ostream& operator<<(std::ostream& ostream, const DoublePoint& point) {
+ return ostream << point.ToString();
+}
+
+String DoublePoint::ToString() const {
+ return String::Format("%lg,%lg", X(), Y());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/double_point.h b/chromium/third_party/blink/renderer/platform/geometry/double_point.h
new file mode 100644
index 00000000000..e9e5f3f30ff
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/double_point.h
@@ -0,0 +1,137 @@
+
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_DOUBLE_POINT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_DOUBLE_POINT_H_
+
+#include <iosfwd>
+#include "third_party/blink/renderer/platform/geometry/double_size.h"
+#include "third_party/blink/renderer/platform/geometry/float_point.h"
+#include "third_party/blink/renderer/platform/geometry/int_point.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+class LayoutPoint;
+
+class PLATFORM_EXPORT DoublePoint {
+ DISALLOW_NEW();
+
+ public:
+ DoublePoint() : x_(0), y_(0) {}
+ DoublePoint(double x, double y) : x_(x), y_(y) {}
+ DoublePoint(const IntPoint& p) : x_(p.X()), y_(p.Y()) {}
+ DoublePoint(const FloatPoint& p) : x_(p.X()), y_(p.Y()) {}
+ explicit DoublePoint(const LayoutPoint&);
+
+ explicit DoublePoint(const IntSize& size)
+ : x_(size.Width()), y_(size.Height()) {}
+
+ explicit DoublePoint(const FloatSize&);
+
+ explicit DoublePoint(const DoubleSize& size)
+ : x_(size.Width()), y_(size.Height()) {}
+
+ static DoublePoint Zero() { return DoublePoint(); }
+
+ DoublePoint ExpandedTo(const DoublePoint&) const;
+ DoublePoint ShrunkTo(const DoublePoint&) const;
+
+ double X() const { return x_; }
+ double Y() const { return y_; }
+ void SetX(double x) { x_ = x; }
+ void SetY(double y) { y_ = y; }
+
+ void Move(const DoubleSize& s) {
+ x_ += s.Width();
+ y_ += s.Height();
+ }
+
+ void Move(double x, double y) {
+ x_ += x;
+ y_ += y;
+ }
+
+ void MoveBy(const DoublePoint& p) {
+ x_ += p.X();
+ y_ += p.Y();
+ }
+
+ void Scale(float sx, float sy) {
+ x_ *= sx;
+ y_ *= sy;
+ }
+
+ DoublePoint ScaledBy(float scale) const {
+ return DoublePoint(x_ * scale, y_ * scale);
+ }
+
+ String ToString() const;
+
+ private:
+ double x_, y_;
+};
+
+inline bool operator==(const DoublePoint& a, const DoublePoint& b) {
+ return a.X() == b.X() && a.Y() == b.Y();
+}
+
+inline bool operator!=(const DoublePoint& a, const DoublePoint& b) {
+ return a.X() != b.X() || a.Y() != b.Y();
+}
+
+inline DoublePoint& operator+=(DoublePoint& a, const DoubleSize& b) {
+ a.SetX(a.X() + b.Width());
+ a.SetY(a.Y() + b.Height());
+ return a;
+}
+
+inline DoublePoint& operator-=(DoublePoint& a, const DoubleSize& b) {
+ a.SetX(a.X() - b.Width());
+ a.SetY(a.Y() - b.Height());
+ return a;
+}
+
+inline DoublePoint operator+(const DoublePoint& a, const DoubleSize& b) {
+ return DoublePoint(a.X() + b.Width(), a.Y() + b.Height());
+}
+
+inline DoubleSize operator-(const DoublePoint& a, const DoublePoint& b) {
+ return DoubleSize(a.X() - b.X(), a.Y() - b.Y());
+}
+
+inline DoublePoint operator-(const DoublePoint& a) {
+ return DoublePoint(-a.X(), -a.Y());
+}
+
+inline DoublePoint operator-(const DoublePoint& a, const DoubleSize& b) {
+ return DoublePoint(a.X() - b.Width(), a.Y() - b.Height());
+}
+
+inline IntPoint RoundedIntPoint(const DoublePoint& p) {
+ return IntPoint(clampTo<int>(roundf(p.X())), clampTo<int>(roundf(p.Y())));
+}
+
+inline IntPoint CeiledIntPoint(const DoublePoint& p) {
+ return IntPoint(clampTo<int>(ceil(p.X())), clampTo<int>(ceil(p.Y())));
+}
+
+inline IntPoint FlooredIntPoint(const DoublePoint& p) {
+ return IntPoint(clampTo<int>(floor(p.X())), clampTo<int>(floor(p.Y())));
+}
+
+inline FloatPoint ToFloatPoint(const DoublePoint& a) {
+ return FloatPoint(a.X(), a.Y());
+}
+
+inline DoubleSize ToDoubleSize(const DoublePoint& a) {
+ return DoubleSize(a.X(), a.Y());
+}
+
+PLATFORM_EXPORT std::ostream& operator<<(std::ostream&, const DoublePoint&);
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/geometry/double_rect.cc b/chromium/third_party/blink/renderer/platform/geometry/double_rect.cc
new file mode 100644
index 00000000000..4af26661947
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/double_rect.cc
@@ -0,0 +1,59 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/geometry/double_rect.h"
+
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+DoubleRect::DoubleRect(const IntRect& r)
+ : location_(r.Location()), size_(r.Size()) {}
+
+DoubleRect::DoubleRect(const FloatRect& r)
+ : location_(r.Location()), size_(r.Size()) {}
+
+DoubleRect::DoubleRect(const LayoutRect& r)
+ : location_(r.Location()), size_(r.Size()) {}
+
+IntRect EnclosingIntRect(const DoubleRect& rect) {
+ IntPoint location = FlooredIntPoint(rect.MinXMinYCorner());
+ IntPoint max_point = CeiledIntPoint(rect.MaxXMaxYCorner());
+
+ return IntRect(location, max_point - location);
+}
+
+IntRect EnclosedIntRect(const DoubleRect& rect) {
+ IntPoint location = CeiledIntPoint(rect.MinXMinYCorner());
+ IntPoint max_point = FlooredIntPoint(rect.MaxXMaxYCorner());
+ IntSize size = max_point - location;
+ size.ClampNegativeToZero();
+
+ return IntRect(location, size);
+}
+
+IntRect RoundedIntRect(const DoubleRect& rect) {
+ return IntRect(RoundedIntPoint(rect.Location()), RoundedIntSize(rect.Size()));
+}
+
+void DoubleRect::Scale(float sx, float sy) {
+ location_.SetX(X() * sx);
+ location_.SetY(Y() * sy);
+ size_.SetWidth(Width() * sx);
+ size_.SetHeight(Height() * sy);
+}
+
+std::ostream& operator<<(std::ostream& ostream, const DoubleRect& rect) {
+ return ostream << rect.ToString();
+}
+
+String DoubleRect::ToString() const {
+ return String::Format("%s %s", Location().ToString().Ascii().data(),
+ Size().ToString().Ascii().data());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/double_rect.h b/chromium/third_party/blink/renderer/platform/geometry/double_rect.h
new file mode 100644
index 00000000000..c8f8b92e986
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/double_rect.h
@@ -0,0 +1,92 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_DOUBLE_RECT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_DOUBLE_RECT_H_
+
+#include "third_party/blink/renderer/platform/geometry/double_point.h"
+#include "third_party/blink/renderer/platform/geometry/double_size.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+class FloatRect;
+class IntRect;
+class LayoutRect;
+
+class PLATFORM_EXPORT DoubleRect {
+ STACK_ALLOCATED();
+
+ public:
+ DoubleRect() = default;
+ DoubleRect(const DoublePoint& location, const DoubleSize& size)
+ : location_(location), size_(size) {}
+ DoubleRect(double x, double y, double width, double height)
+ : location_(DoublePoint(x, y)), size_(DoubleSize(width, height)) {}
+ DoubleRect(const IntRect&);
+ DoubleRect(const FloatRect&);
+ DoubleRect(const LayoutRect&);
+
+ DoublePoint Location() const { return location_; }
+ DoubleSize Size() const { return size_; }
+
+ void SetLocation(const DoublePoint& location) { location_ = location; }
+ void SetSize(const DoubleSize& size) { size_ = size; }
+
+ double X() const { return location_.X(); }
+ double Y() const { return location_.Y(); }
+ double MaxX() const { return X() + Width(); }
+ double MaxY() const { return Y() + Height(); }
+ double Width() const { return size_.Width(); }
+ double Height() const { return size_.Height(); }
+
+ void SetX(double x) { location_.SetX(x); }
+ void SetY(double y) { location_.SetY(y); }
+ void SetWidth(double width) { size_.SetWidth(width); }
+ void SetHeight(double height) { size_.SetHeight(height); }
+
+ bool IsEmpty() const { return size_.IsEmpty(); }
+ bool IsZero() const { return size_.IsZero(); }
+
+ void Move(const DoubleSize& delta) { location_ += delta; }
+ void Move(double dx, double dy) { location_.Move(dx, dy); }
+ void MoveBy(const DoublePoint& delta) {
+ location_.Move(delta.X(), delta.Y());
+ }
+
+ DoublePoint MinXMinYCorner() const { return location_; } // typically topLeft
+ DoublePoint MaxXMinYCorner() const {
+ return DoublePoint(location_.X() + size_.Width(), location_.Y());
+ } // typically topRight
+ DoublePoint MinXMaxYCorner() const {
+ return DoublePoint(location_.X(), location_.Y() + size_.Height());
+ } // typically bottomLeft
+ DoublePoint MaxXMaxYCorner() const {
+ return DoublePoint(location_.X() + size_.Width(),
+ location_.Y() + size_.Height());
+ } // typically bottomRight
+
+ void Scale(float s) { Scale(s, s); }
+ void Scale(float sx, float sy);
+
+ String ToString() const;
+
+ private:
+ DoublePoint location_;
+ DoubleSize size_;
+};
+
+PLATFORM_EXPORT IntRect EnclosingIntRect(const DoubleRect&);
+
+// Returns a valid IntRect contained within the given DoubleRect.
+PLATFORM_EXPORT IntRect EnclosedIntRect(const DoubleRect&);
+
+PLATFORM_EXPORT IntRect RoundedIntRect(const DoubleRect&);
+
+PLATFORM_EXPORT std::ostream& operator<<(std::ostream&, const DoubleRect&);
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/geometry/double_rect_test.cc b/chromium/third_party/blink/renderer/platform/geometry/double_rect_test.cc
new file mode 100644
index 00000000000..55d6627a17c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/double_rect_test.cc
@@ -0,0 +1,23 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/geometry/double_rect.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+TEST(DoubleRectTest, ToString) {
+ DoubleRect empty_rect = DoubleRect();
+ EXPECT_EQ("0,0 0x0", empty_rect.ToString());
+
+ DoubleRect rect(1, 2, 3, 4);
+ EXPECT_EQ("1,2 3x4", rect.ToString());
+
+ DoubleRect granular_rect(1.6, 2.7, 3.8, 4.9);
+ EXPECT_EQ("1.6,2.7 3.8x4.9", granular_rect.ToString());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/double_size.cc b/chromium/third_party/blink/renderer/platform/geometry/double_size.cc
new file mode 100644
index 00000000000..45efb50ed67
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/double_size.cc
@@ -0,0 +1,30 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/geometry/double_size.h"
+#include "third_party/blink/renderer/platform/geometry/layout_size.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+#include <limits>
+#include <math.h>
+
+namespace blink {
+
+DoubleSize::DoubleSize(const LayoutSize& size)
+ : width_(size.Width().ToDouble()), height_(size.Height().ToDouble()) {}
+
+bool DoubleSize::IsZero() const {
+ return fabs(width_) < std::numeric_limits<double>::epsilon() &&
+ fabs(height_) < std::numeric_limits<double>::epsilon();
+}
+
+std::ostream& operator<<(std::ostream& ostream, const DoubleSize& size) {
+ return ostream << size.ToString();
+}
+
+String DoubleSize::ToString() const {
+ return String::Format("%lgx%lg", Width(), Height());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/double_size.h b/chromium/third_party/blink/renderer/platform/geometry/double_size.h
new file mode 100644
index 00000000000..273c50e55aa
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/double_size.h
@@ -0,0 +1,107 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_DOUBLE_SIZE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_DOUBLE_SIZE_H_
+
+#include <iosfwd>
+#include "third_party/blink/renderer/platform/geometry/float_size.h"
+#include "third_party/blink/renderer/platform/geometry/int_size.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+class LayoutSize;
+
+class PLATFORM_EXPORT DoubleSize {
+ DISALLOW_NEW();
+
+ public:
+ DoubleSize() : width_(0), height_(0) {}
+ DoubleSize(double width, double height) : width_(width), height_(height) {}
+ DoubleSize(const IntSize& p) : width_(p.Width()), height_(p.Height()) {}
+ DoubleSize(const FloatSize& s) : width_(s.Width()), height_(s.Height()) {}
+ explicit DoubleSize(const LayoutSize&);
+
+ double Width() const { return width_; }
+ double Height() const { return height_; }
+
+ void SetWidth(double width) { width_ = width; }
+ void SetHeight(double height) { height_ = height; }
+
+ bool IsEmpty() const { return width_ <= 0 || height_ <= 0; }
+
+ bool IsZero() const;
+
+ void Expand(float width, float height) {
+ width_ += width;
+ height_ += height;
+ }
+
+ void Scale(float width_scale, float height_scale) {
+ width_ = width_ * width_scale;
+ height_ = height_ * height_scale;
+ }
+
+ void Scale(float scale) { this->Scale(scale, scale); }
+
+ String ToString() const;
+
+ private:
+ double width_, height_;
+};
+
+inline DoubleSize& operator+=(DoubleSize& a, const DoubleSize& b) {
+ a.SetWidth(a.Width() + b.Width());
+ a.SetHeight(a.Height() + b.Height());
+ return a;
+}
+
+inline DoubleSize& operator-=(DoubleSize& a, const DoubleSize& b) {
+ a.SetWidth(a.Width() - b.Width());
+ a.SetHeight(a.Height() - b.Height());
+ return a;
+}
+
+inline DoubleSize operator+(const DoubleSize& a, const DoubleSize& b) {
+ return DoubleSize(a.Width() + b.Width(), a.Height() + b.Height());
+}
+
+inline DoubleSize operator-(const DoubleSize& a, const DoubleSize& b) {
+ return DoubleSize(a.Width() - b.Width(), a.Height() - b.Height());
+}
+
+inline bool operator==(const DoubleSize& a, const DoubleSize& b) {
+ return a.Width() == b.Width() && a.Height() == b.Height();
+}
+
+inline bool operator!=(const DoubleSize& a, const DoubleSize& b) {
+ return a.Width() != b.Width() || a.Height() != b.Height();
+}
+
+inline IntSize FlooredIntSize(const DoubleSize& p) {
+ return IntSize(clampTo<int>(floor(p.Width())),
+ clampTo<int>(floor(p.Height())));
+}
+
+inline IntSize RoundedIntSize(const DoubleSize& p) {
+ return IntSize(clampTo<int>(roundf(p.Width())),
+ clampTo<int>(roundf(p.Height())));
+}
+
+inline IntSize ExpandedIntSize(const DoubleSize& p) {
+ return IntSize(clampTo<int>(ceilf(p.Width())),
+ clampTo<int>(ceilf(p.Height())));
+}
+
+inline FloatSize ToFloatSize(const DoubleSize& p) {
+ return FloatSize(p.Width(), p.Height());
+}
+
+PLATFORM_EXPORT std::ostream& operator<<(std::ostream&, const DoubleSize&);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_DOUBLE_SIZE_H_
diff --git a/chromium/third_party/blink/renderer/platform/geometry/float_box.cc b/chromium/third_party/blink/renderer/platform/geometry/float_box.cc
new file mode 100644
index 00000000000..2c5b3328a03
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/float_box.cc
@@ -0,0 +1,43 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/geometry/float_box.h"
+
+#include <algorithm>
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+void FloatBox::ExpandTo(const FloatPoint3D& low, const FloatPoint3D& high) {
+ DCHECK_LE(low.X(), high.X());
+ DCHECK_LE(low.Y(), high.Y());
+ DCHECK_LE(low.Z(), high.Z());
+
+ float min_x = std::min(x_, low.X());
+ float min_y = std::min(y_, low.Y());
+ float min_z = std::min(z_, low.Z());
+
+ float max_x = std::max(Right(), high.X());
+ float max_y = std::max(Bottom(), high.Y());
+ float max_z = std::max(front(), high.Z());
+
+ x_ = min_x;
+ y_ = min_y;
+ z_ = min_z;
+
+ width_ = max_x - min_x;
+ height_ = max_y - min_y;
+ depth_ = max_z - min_z;
+}
+
+std::ostream& operator<<(std::ostream& ostream, const FloatBox& box) {
+ return ostream << box.ToString();
+}
+
+String FloatBox::ToString() const {
+ return String::Format("%lg,%lg,%lg %lgx%lgx%lg", X(), Y(), Z(), Width(),
+ Height(), Depth());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/float_box.h b/chromium/third_party/blink/renderer/platform/geometry/float_box.h
new file mode 100644
index 00000000000..8365088b3c1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/float_box.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2014, 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_FLOAT_BOX_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_FLOAT_BOX_H_
+
+#include <cmath>
+#include <iosfwd>
+#include "third_party/blink/renderer/platform/geometry/float_point_3d.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT FloatBox {
+ DISALLOW_NEW();
+
+ public:
+ FloatBox() : x_(0), y_(0), z_(0), width_(0), height_(0), depth_(0) {}
+
+ FloatBox(float x, float y, float z, float width, float height, float depth)
+ : x_(x), y_(y), z_(z), width_(width), height_(height), depth_(depth) {}
+
+ FloatBox(const FloatBox& box)
+ : x_(box.X()),
+ y_(box.Y()),
+ z_(box.Z()),
+ width_(box.Width()),
+ height_(box.Height()),
+ depth_(box.Depth()) {}
+
+ void SetOrigin(const FloatPoint3D& origin) {
+ x_ = origin.X();
+ y_ = origin.Y();
+ z_ = origin.Z();
+ }
+
+ void SetSize(const FloatPoint3D& origin) {
+ DCHECK_GE(origin.X(), 0);
+ DCHECK_GE(origin.Y(), 0);
+ DCHECK_GE(origin.Z(), 0);
+
+ width_ = origin.X();
+ height_ = origin.Y();
+ depth_ = origin.Z();
+ }
+
+ void Move(const FloatPoint3D& location) {
+ x_ += location.X();
+ y_ += location.Y();
+ z_ += location.Z();
+ }
+
+ void Flatten() {
+ z_ = 0;
+ depth_ = 0;
+ }
+
+ void ExpandTo(const FloatPoint3D& low, const FloatPoint3D& high);
+ void ExpandTo(const FloatPoint3D& point) { ExpandTo(point, point); }
+
+ void ExpandTo(const FloatBox& box) {
+ ExpandTo(FloatPoint3D(box.X(), box.Y(), box.Z()),
+ FloatPoint3D(box.Right(), box.Bottom(), box.front()));
+ }
+
+ void UnionBounds(const FloatBox& box) {
+ if (box.IsEmpty())
+ return;
+
+ if (IsEmpty()) {
+ *this = box;
+ return;
+ }
+
+ ExpandTo(box);
+ }
+
+ bool IsEmpty() const {
+ return (width_ <= 0 && height_ <= 0) || (width_ <= 0 && depth_ <= 0) ||
+ (height_ <= 0 && depth_ <= 0);
+ }
+
+ float Right() const { return x_ + width_; }
+ float Bottom() const { return y_ + height_; }
+ float front() const { return z_ + depth_; }
+ float X() const { return x_; }
+ float Y() const { return y_; }
+ float Z() const { return z_; }
+ float Width() const { return width_; }
+ float Height() const { return height_; }
+ float Depth() const { return depth_; }
+
+ String ToString() const;
+
+ private:
+ float x_;
+ float y_;
+ float z_;
+ float width_;
+ float height_;
+ float depth_;
+};
+
+inline bool operator==(const FloatBox& a, const FloatBox& b) {
+ return a.X() == b.X() && a.Y() == b.Y() && a.Z() == b.Z() &&
+ a.Width() == b.Width() && a.Height() == b.Height() &&
+ a.Depth() == b.Depth();
+}
+
+inline bool operator!=(const FloatBox& a, const FloatBox& b) {
+ return !(a == b);
+}
+
+PLATFORM_EXPORT std::ostream& operator<<(std::ostream&, const FloatBox&);
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/geometry/float_box_test.cc b/chromium/third_party/blink/renderer/platform/geometry/float_box_test.cc
new file mode 100644
index 00000000000..09f1ef2dbc6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/float_box_test.cc
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2014 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES,:tabnew INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/geometry/float_box.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/geometry/float_box_test_helpers.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+TEST(FloatBoxTest, SimpleCreationTest) {
+ FloatBox box(1, 2, 3, 4, 5, 6);
+ EXPECT_EQ(1, box.X());
+ EXPECT_EQ(2, box.Y());
+ EXPECT_EQ(3, box.Z());
+ EXPECT_EQ(4, box.Width());
+ EXPECT_EQ(5, box.Height());
+ EXPECT_EQ(6, box.Depth());
+ EXPECT_EQ(5, box.Right());
+ EXPECT_EQ(7, box.Bottom());
+ EXPECT_EQ(9, box.front());
+}
+
+TEST(FloatBoxTest, PositionTest) {
+ FloatBox box(0, 0, 0, 4, 4, 4);
+ box.Move(FloatPoint3D(1, 2, 3));
+ EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual,
+ FloatBox(1, 2, 3, 4, 4, 4), box);
+ box.SetOrigin(FloatPoint3D(-1, -2, -3));
+ box.Move(FloatPoint3D(-1, -2, -3));
+ EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual,
+ FloatBox(-2, -4, -6, 4, 4, 4), box);
+}
+
+TEST(FloatBoxTest, CopyTest) {
+ FloatBox box(1, 2, 3, 4, 4, 4);
+ FloatBox box2(box);
+ EXPECT_EQ(box, box2);
+ box.SetSize(FloatPoint3D(3, 3, 3));
+ EXPECT_NE(box, box2);
+ EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual,
+ FloatBox(1, 2, 3, 3, 3, 3), box);
+}
+
+TEST(FloatBoxTest, FlattenTest) {
+ FloatBox box(1, 2, 3, 4, 4, 4);
+ box.Flatten();
+ EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual,
+ FloatBox(1, 2, 0, 4, 4, 0), box);
+}
+
+TEST(FloatBoxTest, ExpandTests) {
+ FloatBox box;
+ box.ExpandTo(FloatPoint3D(10, -3, 2));
+ EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual,
+ FloatBox(0, -3, 0, 10, 3, 2), box);
+
+ box.ExpandTo(FloatPoint3D(-15, 6, 8));
+ EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual,
+ FloatBox(-15, -3, 0, 25, 9, 8), box);
+
+ box = FloatBox();
+ box.ExpandTo(FloatPoint3D(-3, 6, 9), FloatPoint3D(-2, 10, 11));
+ EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual,
+ FloatBox(-3, 0, 0, 3, 10, 11), box);
+
+ box = FloatBox();
+ box.ExpandTo(FloatBox(-10, -10, -10, 3, 30, 40));
+ box.ExpandTo(FloatBox(-11, 3, 50, 10, 15, 1));
+ EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual,
+ FloatBox(-11, -10, -10, 11, 30, 61), box);
+}
+
+TEST(FloatBoxTest, UnionTest) {
+ FloatBox box;
+ EXPECT_TRUE(box.IsEmpty());
+ FloatBox unioned_box(3, 5, 6, 5, 3, 9);
+ box.UnionBounds(unioned_box);
+ EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, unioned_box, box);
+
+ box.UnionBounds(FloatBox());
+ EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, unioned_box, box);
+
+ box.UnionBounds(FloatBox(0, 0, 0, 1, 1, 1));
+ EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual,
+ FloatBox(0, 0, 0, 8, 8, 15), box);
+}
+
+TEST(FloatBoxTest, EmptyBoxTest) {
+ FloatBox box;
+ EXPECT_TRUE(box.IsEmpty());
+ box.ExpandTo(FloatPoint3D(1, 0, 0));
+ EXPECT_TRUE(box.IsEmpty());
+ box.ExpandTo(FloatPoint3D(0, 1, 0));
+ EXPECT_FALSE(box.IsEmpty());
+}
+
+TEST(FloatBoxTest, ToString) {
+ FloatBox box(2, 3, 5, 7, 11, 13);
+ EXPECT_EQ("2,3,5 7x11x13", box.ToString());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/float_box_test_helpers.cc b/chromium/third_party/blink/renderer/platform/geometry/float_box_test_helpers.cc
new file mode 100644
index 00000000000..0f8ec3833a1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/float_box_test_helpers.cc
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2014 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/geometry/float_box_test_helpers.h"
+
+#include "third_party/blink/renderer/platform/geometry/float_box.h"
+#include "third_party/blink/renderer/platform/geometry/geometry_test_helpers.h"
+
+const static float kTestEpsilon = 1e-6;
+
+namespace blink {
+namespace FloatBoxTest {
+
+bool ApproximatelyEqual(const float& a, const float& b) {
+ return GeometryTest::ApproximatelyEqual(a, b, kTestEpsilon);
+}
+
+bool ApproximatelyEqual(const FloatBox& a, const FloatBox& b) {
+ if (!ApproximatelyEqual(a.X(), b.X()) || !ApproximatelyEqual(a.Y(), b.Y()) ||
+ !ApproximatelyEqual(a.Z(), b.Z()) ||
+ !ApproximatelyEqual(a.Width(), b.Width()) ||
+ !ApproximatelyEqual(a.Height(), b.Height()) ||
+ !ApproximatelyEqual(a.Depth(), b.Depth())) {
+ return false;
+ }
+ return true;
+}
+
+testing::AssertionResult AssertAlmostEqual(const char* expr,
+ const char* n_expr,
+ const FloatBox& m,
+ const FloatBox& n) {
+ if (!ApproximatelyEqual(m, n)) {
+ return testing::AssertionFailure()
+ << " Value of:" << n_expr << std::endl
+ << " Actual:" << testing::PrintToString(n) << std::endl
+ << "Expected Approx:" << expr << std::endl
+ << " Which is:" << testing::PrintToString(m);
+ }
+ return testing::AssertionSuccess();
+}
+
+testing::AssertionResult AssertContains(const char* expr,
+ const char* n_expr,
+ const FloatBox& m,
+ const FloatBox& n) {
+ FloatBox new_m = m;
+ new_m.ExpandTo(n);
+ if (!ApproximatelyEqual(m, new_m)) {
+ return testing::AssertionFailure()
+ << " Value of:" << n_expr << std::endl
+ << " Actual:" << testing::PrintToString(n) << std::endl
+ << "Not Contained in:" << expr << std::endl
+ << " Which is:" << testing::PrintToString(m);
+ }
+ return testing::AssertionSuccess();
+}
+
+} // namespace FloatBoxTest
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/float_box_test_helpers.h b/chromium/third_party/blink/renderer/platform/geometry/float_box_test_helpers.h
new file mode 100644
index 00000000000..c155aa4aa45
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/float_box_test_helpers.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_FLOAT_BOX_TEST_HELPERS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_FLOAT_BOX_TEST_HELPERS_H_
+
+#include <gtest/gtest.h>
+namespace blink {
+class FloatBox;
+
+bool ApproximatelyEqual(const float&, const float&);
+
+namespace FloatBoxTest {
+bool ApproximatelyEqual(const float&, const float&);
+bool ApproximatelyEqual(const FloatBox&, const FloatBox&);
+testing::AssertionResult AssertAlmostEqual(const char*,
+ const char*,
+ const FloatBox&,
+ const FloatBox&);
+testing::AssertionResult AssertContains(const char*,
+ const char*,
+ const FloatBox&,
+ const FloatBox&);
+} // namespace FloatBoxTest
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_FLOAT_BOX_TEST_HELPERS_H_
diff --git a/chromium/third_party/blink/renderer/platform/geometry/float_point.cc b/chromium/third_party/blink/renderer/platform/geometry/float_point.cc
new file mode 100644
index 00000000000..63095fa498f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/float_point.cc
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2005 Nokia. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/geometry/float_point.h"
+
+#include <math.h>
+#include <algorithm>
+#include <limits>
+#include "SkPoint.h"
+#include "third_party/blink/renderer/platform/geometry/double_point.h"
+#include "third_party/blink/renderer/platform/geometry/layout_point.h"
+#include "third_party/blink/renderer/platform/geometry/layout_size.h"
+#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/skia/include/core/SkPoint.h"
+
+namespace blink {
+
+FloatPoint::FloatPoint(const IntPoint& p) : x_(p.X()), y_(p.Y()) {}
+
+FloatPoint::FloatPoint(const SkPoint& point) : x_(point.x()), y_(point.y()) {}
+
+FloatPoint::FloatPoint(const DoublePoint& p) : x_(p.X()), y_(p.Y()) {}
+
+FloatPoint::FloatPoint(const LayoutPoint& p)
+ : x_(p.X().ToFloat()), y_(p.Y().ToFloat()) {}
+
+FloatPoint::FloatPoint(const LayoutSize& size)
+ : x_(size.Width().ToFloat()), y_(size.Height().ToFloat()) {}
+
+float FloatPoint::SlopeAngleRadians() const {
+ return atan2f(y_, x_);
+}
+
+float FloatPoint::length() const {
+ return hypotf(x_, y_);
+}
+
+FloatPoint FloatPoint::ExpandedTo(const FloatPoint& other) const {
+ return FloatPoint(std::max(x_, other.x_), std::max(y_, other.y_));
+}
+
+FloatPoint FloatPoint::ShrunkTo(const FloatPoint& other) const {
+ return FloatPoint(std::min(x_, other.x_), std::min(y_, other.y_));
+}
+
+void FloatPoint::Move(const LayoutSize& size) {
+ x_ += size.Width();
+ y_ += size.Height();
+}
+
+void FloatPoint::MoveBy(const LayoutPoint& point) {
+ x_ += point.X();
+ y_ += point.Y();
+}
+
+SkPoint FloatPoint::Data() const {
+ SkPoint p = {WebCoreFloatToSkScalar(x_), WebCoreFloatToSkScalar(y_)};
+ return p;
+}
+
+FloatPoint FloatPoint::NarrowPrecision(double x, double y) {
+ return FloatPoint(clampTo<float>(x), clampTo<float>(y));
+}
+
+bool FindIntersection(const FloatPoint& p1,
+ const FloatPoint& p2,
+ const FloatPoint& d1,
+ const FloatPoint& d2,
+ FloatPoint& intersection) {
+ float px_length = p2.X() - p1.X();
+ float py_length = p2.Y() - p1.Y();
+
+ float dx_length = d2.X() - d1.X();
+ float dy_length = d2.Y() - d1.Y();
+
+ float denom = px_length * dy_length - py_length * dx_length;
+ if (!denom)
+ return false;
+
+ float param =
+ ((d1.X() - p1.X()) * dy_length - (d1.Y() - p1.Y()) * dx_length) / denom;
+
+ intersection.SetX(p1.X() + param * px_length);
+ intersection.SetY(p1.Y() + param * py_length);
+ return true;
+}
+
+FloatPoint::operator SkPoint() const {
+ return SkPoint::Make(x_, y_);
+}
+
+std::ostream& operator<<(std::ostream& ostream, const FloatPoint& point) {
+ return ostream << point.ToString();
+}
+
+String FloatPoint::ToString() const {
+ return String::Format("%lg,%lg", X(), Y());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/float_point.h b/chromium/third_party/blink/renderer/platform/geometry/float_point.h
new file mode 100644
index 00000000000..6b3593ab8d1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/float_point.h
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2005 Nokia. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_FLOAT_POINT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_FLOAT_POINT_H_
+
+#include <iosfwd>
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/geometry/float_size.h"
+#include "third_party/blink/renderer/platform/geometry/int_point.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+#if defined(OS_MACOSX)
+typedef struct CGPoint CGPoint;
+
+#ifdef __OBJC__
+#import <Foundation/Foundation.h>
+#endif
+#endif
+
+struct SkPoint;
+
+namespace blink {
+
+class DoublePoint;
+class IntPoint;
+class IntSize;
+class LayoutPoint;
+class LayoutSize;
+
+class PLATFORM_EXPORT FloatPoint {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ FloatPoint() : x_(0), y_(0) {}
+ FloatPoint(float x, float y) : x_(x), y_(y) {}
+ FloatPoint(const IntPoint&);
+ explicit FloatPoint(const SkPoint&);
+ explicit FloatPoint(const DoublePoint&);
+ explicit FloatPoint(const LayoutPoint&);
+ explicit FloatPoint(const FloatSize& size)
+ : x_(size.Width()), y_(size.Height()) {}
+ explicit FloatPoint(const LayoutSize&);
+ explicit FloatPoint(const IntSize& size)
+ : x_(size.Width()), y_(size.Height()) {}
+
+ static FloatPoint Zero() { return FloatPoint(); }
+
+ static FloatPoint NarrowPrecision(double x, double y);
+
+ float X() const { return x_; }
+ float Y() const { return y_; }
+
+ void SetX(float x) { x_ = x; }
+ void SetY(float y) { y_ = y; }
+ void Set(float x, float y) {
+ x_ = x;
+ y_ = y;
+ }
+ void Move(float dx, float dy) {
+ x_ += dx;
+ y_ += dy;
+ }
+ void Move(const IntSize& a) {
+ x_ += a.Width();
+ y_ += a.Height();
+ }
+ void Move(const LayoutSize&);
+ void Move(const FloatSize& a) {
+ x_ += a.Width();
+ y_ += a.Height();
+ }
+ void MoveBy(const IntPoint& a) {
+ x_ += a.X();
+ y_ += a.Y();
+ }
+ void MoveBy(const LayoutPoint&);
+ void MoveBy(const FloatPoint& a) {
+ x_ += a.X();
+ y_ += a.Y();
+ }
+ void Scale(float sx, float sy) {
+ x_ *= sx;
+ y_ *= sy;
+ }
+
+ float Dot(const FloatPoint& a) const { return x_ * a.X() + y_ * a.Y(); }
+
+ float SlopeAngleRadians() const;
+ float length() const;
+ float LengthSquared() const { return x_ * x_ + y_ * y_; }
+
+ FloatPoint ExpandedTo(const FloatPoint& other) const;
+ FloatPoint ShrunkTo(const FloatPoint& other) const;
+
+ FloatPoint TransposedPoint() const { return FloatPoint(y_, x_); }
+
+ FloatPoint ScaledBy(float scale) const {
+ return FloatPoint(x_ * scale, y_ * scale);
+ }
+
+#if defined(OS_MACOSX)
+ FloatPoint(const CGPoint&);
+ operator CGPoint() const;
+#endif
+
+ // Can we remove this one?
+ SkPoint Data() const;
+
+ operator SkPoint() const;
+
+ String ToString() const;
+
+ private:
+ float x_, y_;
+};
+
+inline FloatPoint& operator+=(FloatPoint& a, const FloatSize& b) {
+ a.Move(b.Width(), b.Height());
+ return a;
+}
+
+inline FloatPoint& operator+=(FloatPoint& a, const FloatPoint& b) {
+ a.Move(b.X(), b.Y());
+ return a;
+}
+
+inline FloatPoint& operator-=(FloatPoint& a, const FloatSize& b) {
+ a.Move(-b.Width(), -b.Height());
+ return a;
+}
+
+inline FloatPoint operator+(const FloatPoint& a, const FloatSize& b) {
+ return FloatPoint(a.X() + b.Width(), a.Y() + b.Height());
+}
+
+inline FloatPoint operator+(const FloatPoint& a, const IntSize& b) {
+ return FloatPoint(a.X() + b.Width(), a.Y() + b.Height());
+}
+
+inline FloatPoint operator+(const IntPoint& a, const FloatSize& b) {
+ return FloatPoint(a.X() + b.Width(), a.Y() + b.Height());
+}
+
+inline FloatPoint operator+(const FloatPoint& a, const FloatPoint& b) {
+ return FloatPoint(a.X() + b.X(), a.Y() + b.Y());
+}
+
+inline FloatPoint operator+(const FloatPoint& a, const IntPoint& b) {
+ return FloatPoint(a.X() + b.X(), a.Y() + b.Y());
+}
+
+inline FloatSize operator-(const FloatPoint& a, const FloatPoint& b) {
+ return FloatSize(a.X() - b.X(), a.Y() - b.Y());
+}
+
+inline FloatSize operator-(const FloatPoint& a, const IntPoint& b) {
+ return FloatSize(a.X() - b.X(), a.Y() - b.Y());
+}
+
+inline FloatPoint operator-(const FloatPoint& a, const FloatSize& b) {
+ return FloatPoint(a.X() - b.Width(), a.Y() - b.Height());
+}
+
+inline FloatPoint operator-(const FloatPoint& a) {
+ return FloatPoint(-a.X(), -a.Y());
+}
+
+inline bool operator==(const FloatPoint& a, const FloatPoint& b) {
+ return a.X() == b.X() && a.Y() == b.Y();
+}
+
+inline bool operator!=(const FloatPoint& a, const FloatPoint& b) {
+ return a.X() != b.X() || a.Y() != b.Y();
+}
+
+inline float operator*(const FloatPoint& a, const FloatPoint& b) {
+ // dot product
+ return a.Dot(b);
+}
+
+inline IntPoint RoundedIntPoint(const FloatPoint& p) {
+ return IntPoint(clampTo<int>(roundf(p.X())), clampTo<int>(roundf(p.Y())));
+}
+
+inline IntSize RoundedIntSize(const FloatPoint& p) {
+ return IntSize(clampTo<int>(roundf(p.X())), clampTo<int>(roundf(p.Y())));
+}
+
+inline IntPoint FlooredIntPoint(const FloatPoint& p) {
+ return IntPoint(clampTo<int>(floorf(p.X())), clampTo<int>(floorf(p.Y())));
+}
+
+inline IntPoint CeiledIntPoint(const FloatPoint& p) {
+ return IntPoint(clampTo<int>(ceilf(p.X())), clampTo<int>(ceilf(p.Y())));
+}
+
+inline IntSize FlooredIntSize(const FloatPoint& p) {
+ return IntSize(clampTo<int>(floorf(p.X())), clampTo<int>(floorf(p.Y())));
+}
+
+inline FloatSize ToFloatSize(const FloatPoint& a) {
+ return FloatSize(a.X(), a.Y());
+}
+
+// Find point where lines through the two pairs of points intersect.
+// Returns false if the lines don't intersect.
+PLATFORM_EXPORT bool FindIntersection(const FloatPoint& p1,
+ const FloatPoint& p2,
+ const FloatPoint& d1,
+ const FloatPoint& d2,
+ FloatPoint& intersection);
+
+PLATFORM_EXPORT std::ostream& operator<<(std::ostream&, const FloatPoint&);
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/geometry/float_point_3d.cc b/chromium/third_party/blink/renderer/platform/geometry/float_point_3d.cc
new file mode 100644
index 00000000000..79ac416facd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/float_point_3d.cc
@@ -0,0 +1,67 @@
+/*
+ Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann <wildfox@kde.org>
+ 2004, 2005 Rob Buis <buis@kde.org>
+ 2005 Eric Seidel <eric@webkit.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ aint with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "third_party/blink/renderer/platform/geometry/float_point_3d.h"
+
+#include <math.h>
+#include "third_party/blink/renderer/platform/text/text_stream.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+void FloatPoint3D::Normalize() {
+ float temp_length = length();
+
+ if (temp_length) {
+ x_ /= temp_length;
+ y_ /= temp_length;
+ z_ /= temp_length;
+ }
+}
+
+float FloatPoint3D::AngleBetween(const FloatPoint3D& y) const {
+ float x_length = this->length();
+ float y_length = y.length();
+
+ if (x_length && y_length) {
+ float cos_angle = this->Dot(y) / (x_length * y_length);
+ // Due to round-off |cosAngle| can have a magnitude greater than 1. Clamp
+ // the value to [-1, 1] before computing the angle.
+ return acos(clampTo(cos_angle, -1.0, 1.0));
+ }
+ return 0;
+}
+
+std::ostream& operator<<(std::ostream& ostream, const FloatPoint3D& point) {
+ return ostream << point.ToString();
+}
+
+String FloatPoint3D::ToString() const {
+ return String::Format("%lg,%lg,%lg", X(), Y(), Z());
+}
+
+TextStream& operator<<(TextStream& ts, const FloatPoint3D& p) {
+ ts << "x=" << p.X() << " y=" << p.Y() << " z=" << p.Z();
+ return ts;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/float_point_3d.h b/chromium/third_party/blink/renderer/platform/geometry/float_point_3d.h
new file mode 100644
index 00000000000..1f49a063af8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/float_point_3d.h
@@ -0,0 +1,165 @@
+/*
+ Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann <wildfox@kde.org>
+ 2004, 2005 Rob Buis <buis@kde.org>
+ 2005 Eric Seidel <eric@webkit.org>
+ 2010 Zoltan Herczeg <zherczeg@webkit.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ aint with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_FLOAT_POINT_3D_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_FLOAT_POINT_3D_H_
+
+#include "third_party/blink/renderer/platform/geometry/float_point.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/skia/include/core/SkPoint3.h"
+
+namespace blink {
+
+class TextStream;
+
+class PLATFORM_EXPORT FloatPoint3D {
+ DISALLOW_NEW();
+
+ public:
+ FloatPoint3D() : x_(0), y_(0), z_(0) {}
+
+ FloatPoint3D(float x, float y, float z) : x_(x), y_(y), z_(z) {}
+
+ FloatPoint3D(const FloatPoint& p) : x_(p.X()), y_(p.Y()), z_(0) {}
+
+ FloatPoint3D(const FloatPoint3D& p) : x_(p.X()), y_(p.Y()), z_(p.Z()) {}
+
+ float X() const { return x_; }
+ void SetX(float x) { x_ = x; }
+
+ float Y() const { return y_; }
+ void SetY(float y) { y_ = y; }
+
+ float Z() const { return z_; }
+ void SetZ(float z) { z_ = z; }
+ void Set(float x, float y, float z) {
+ x_ = x;
+ y_ = y;
+ z_ = z;
+ }
+ void Move(float dx, float dy, float dz) {
+ x_ += dx;
+ y_ += dy;
+ z_ += dz;
+ }
+ void Scale(float sx, float sy, float sz) {
+ x_ *= sx;
+ y_ *= sy;
+ z_ *= sz;
+ }
+
+ bool IsZero() const { return !x_ && !y_ && !z_; }
+
+ void Normalize();
+
+ float Dot(const FloatPoint3D& a) const {
+ return x_ * a.X() + y_ * a.Y() + z_ * a.Z();
+ }
+
+ // Compute the angle (in radians) between this and y. If either vector is the
+ // zero vector, return an angle of 0.
+ float AngleBetween(const FloatPoint3D& y) const;
+
+ // Sets this FloatPoint3D to the cross product of the passed two.
+ // It is safe for "this" to be the same as either or both of the
+ // arguments.
+ void Cross(const FloatPoint3D& a, const FloatPoint3D& b) {
+ float x = a.Y() * b.Z() - a.Z() * b.Y();
+ float y = a.Z() * b.X() - a.X() * b.Z();
+ float z = a.X() * b.Y() - a.Y() * b.X();
+ x_ = x;
+ y_ = y;
+ z_ = z;
+ }
+
+ // Convenience function returning "this cross point" as a
+ // stack-allocated result.
+ FloatPoint3D Cross(const FloatPoint3D& point) const {
+ FloatPoint3D result;
+ result.Cross(*this, point);
+ return result;
+ }
+
+ float LengthSquared() const { return this->Dot(*this); }
+ float length() const { return sqrtf(LengthSquared()); }
+
+ float DistanceTo(const FloatPoint3D& a) const;
+
+ operator SkPoint3() const { return SkPoint3::Make(x_, y_, z_); }
+
+ String ToString() const;
+
+ private:
+ float x_;
+ float y_;
+ float z_;
+};
+
+inline FloatPoint3D& operator+=(FloatPoint3D& a, const FloatPoint3D& b) {
+ a.Move(b.X(), b.Y(), b.Z());
+ return a;
+}
+
+inline FloatPoint3D& operator-=(FloatPoint3D& a, const FloatPoint3D& b) {
+ a.Move(-b.X(), -b.Y(), -b.Z());
+ return a;
+}
+
+inline FloatPoint3D operator+(const FloatPoint3D& a, const FloatPoint3D& b) {
+ return FloatPoint3D(a.X() + b.X(), a.Y() + b.Y(), a.Z() + b.Z());
+}
+
+inline FloatPoint3D operator-(const FloatPoint3D& a, const FloatPoint3D& b) {
+ return FloatPoint3D(a.X() - b.X(), a.Y() - b.Y(), a.Z() - b.Z());
+}
+
+inline bool operator==(const FloatPoint3D& a, const FloatPoint3D& b) {
+ return a.X() == b.X() && a.Y() == b.Y() && a.Z() == b.Z();
+}
+
+inline bool operator!=(const FloatPoint3D& a, const FloatPoint3D& b) {
+ return a.X() != b.X() || a.Y() != b.Y() || a.Z() != b.Z();
+}
+
+inline float operator*(const FloatPoint3D& a, const FloatPoint3D& b) {
+ // dot product
+ return a.Dot(b);
+}
+
+inline FloatPoint3D operator*(float k, const FloatPoint3D& v) {
+ return FloatPoint3D(k * v.X(), k * v.Y(), k * v.Z());
+}
+
+inline FloatPoint3D operator*(const FloatPoint3D& v, float k) {
+ return FloatPoint3D(k * v.X(), k * v.Y(), k * v.Z());
+}
+
+inline float FloatPoint3D::DistanceTo(const FloatPoint3D& a) const {
+ return (*this - a).length();
+}
+
+PLATFORM_EXPORT std::ostream& operator<<(std::ostream&, const FloatPoint3D&);
+TextStream& operator<<(TextStream&, const FloatPoint3D&);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_FLOAT_POINT_3D_H_
diff --git a/chromium/third_party/blink/renderer/platform/geometry/float_point_test.cc b/chromium/third_party/blink/renderer/platform/geometry/float_point_test.cc
new file mode 100644
index 00000000000..d9489e20178
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/float_point_test.cc
@@ -0,0 +1,31 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/geometry/float_point.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/geometry/geometry_test_helpers.h"
+
+namespace blink {
+
+TEST(FloatPointTest, LengthTest) {
+ // Sanity check the Pythagorean triples 3-4-5 and 5-12-13
+ FloatPoint p1 = FloatPoint(3.f, 4.f);
+ EXPECT_PRED_FORMAT2(GeometryTest::AssertAlmostEqual, p1.length(), 5.f);
+ FloatPoint p2 = FloatPoint(5.f, 12.f);
+ EXPECT_PRED_FORMAT2(GeometryTest::AssertAlmostEqual, p2.length(), 13.f);
+
+ // Test very small numbers. This fails under the old implementation of
+ // FloatPoint::length(): `return sqrtf(lengthSquared());'
+ FloatPoint p3 = FloatPoint(.5e-20f, .5e-20f);
+ EXPECT_PRED_FORMAT2(GeometryTest::AssertAlmostEqual, p3.length(),
+ .707106781186548e-20f);
+
+ // Test very large numbers.
+ FloatPoint p4 = FloatPoint(.5e20f, .5e20f);
+ EXPECT_PRED_FORMAT2(GeometryTest::AssertAlmostEqual, p4.length(),
+ .707106781186548e20f);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/float_polygon.cc b/chromium/third_party/blink/renderer/platform/geometry/float_polygon.cc
new file mode 100644
index 00000000000..ef20d12e884
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/float_polygon.cc
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. 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.
+ *
+ * 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 HOLDER 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 "third_party/blink/renderer/platform/geometry/float_polygon.h"
+#include "third_party/blink/renderer/platform/geometry/float_shape_helpers.h"
+
+#include <memory>
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+namespace blink {
+
+static inline bool AreCollinearPoints(const FloatPoint& p0,
+ const FloatPoint& p1,
+ const FloatPoint& p2) {
+ return !Determinant(p1 - p0, p2 - p0);
+}
+
+static inline bool AreCoincidentPoints(const FloatPoint& p0,
+ const FloatPoint& p1) {
+ return p0.X() == p1.X() && p0.Y() == p1.Y();
+}
+
+static inline bool IsPointOnLineSegment(const FloatPoint& vertex1,
+ const FloatPoint& vertex2,
+ const FloatPoint& point) {
+ return point.X() >= std::min(vertex1.X(), vertex2.X()) &&
+ point.X() <= std::max(vertex1.X(), vertex2.X()) &&
+ AreCollinearPoints(vertex1, vertex2, point);
+}
+
+static inline unsigned NextVertexIndex(unsigned vertex_index,
+ unsigned n_vertices,
+ bool clockwise) {
+ return ((clockwise) ? vertex_index + 1 : vertex_index - 1 + n_vertices) %
+ n_vertices;
+}
+
+static unsigned FindNextEdgeVertexIndex(const FloatPolygon& polygon,
+ unsigned vertex_index1,
+ bool clockwise) {
+ unsigned n_vertices = polygon.NumberOfVertices();
+ unsigned vertex_index2 =
+ NextVertexIndex(vertex_index1, n_vertices, clockwise);
+
+ while (vertex_index2 && AreCoincidentPoints(polygon.VertexAt(vertex_index1),
+ polygon.VertexAt(vertex_index2)))
+ vertex_index2 = NextVertexIndex(vertex_index2, n_vertices, clockwise);
+
+ while (vertex_index2) {
+ unsigned vertex_index3 =
+ NextVertexIndex(vertex_index2, n_vertices, clockwise);
+ if (!AreCollinearPoints(polygon.VertexAt(vertex_index1),
+ polygon.VertexAt(vertex_index2),
+ polygon.VertexAt(vertex_index3)))
+ break;
+ vertex_index2 = vertex_index3;
+ }
+
+ return vertex_index2;
+}
+
+FloatPolygon::FloatPolygon(std::unique_ptr<Vector<FloatPoint>> vertices,
+ WindRule fill_rule)
+ : vertices_(std::move(vertices)), fill_rule_(fill_rule) {
+ unsigned n_vertices = NumberOfVertices();
+ edges_.resize(n_vertices);
+ empty_ = n_vertices < 3;
+
+ if (n_vertices)
+ bounding_box_.SetLocation(VertexAt(0));
+
+ if (empty_)
+ return;
+
+ unsigned min_vertex_index = 0;
+ for (unsigned i = 1; i < n_vertices; ++i) {
+ const FloatPoint& vertex = VertexAt(i);
+ if (vertex.Y() < VertexAt(min_vertex_index).Y() ||
+ (vertex.Y() == VertexAt(min_vertex_index).Y() &&
+ vertex.X() < VertexAt(min_vertex_index).X()))
+ min_vertex_index = i;
+ }
+ FloatPoint next_vertex = VertexAt((min_vertex_index + 1) % n_vertices);
+ FloatPoint prev_vertex =
+ VertexAt((min_vertex_index + n_vertices - 1) % n_vertices);
+ bool clockwise = Determinant(VertexAt(min_vertex_index) - prev_vertex,
+ next_vertex - prev_vertex) > 0;
+
+ unsigned edge_index = 0;
+ unsigned vertex_index1 = 0;
+ do {
+ bounding_box_.Extend(VertexAt(vertex_index1));
+ unsigned vertex_index2 =
+ FindNextEdgeVertexIndex(*this, vertex_index1, clockwise);
+ edges_[edge_index].polygon_ = this;
+ edges_[edge_index].vertex_index1_ = vertex_index1;
+ edges_[edge_index].vertex_index2_ = vertex_index2;
+ edges_[edge_index].edge_index_ = edge_index;
+ ++edge_index;
+ vertex_index1 = vertex_index2;
+ } while (vertex_index1);
+
+ if (edge_index > 3) {
+ const FloatPolygonEdge& first_edge = edges_[0];
+ const FloatPolygonEdge& last_edge = edges_[edge_index - 1];
+ if (AreCollinearPoints(last_edge.Vertex1(), last_edge.Vertex2(),
+ first_edge.Vertex2())) {
+ edges_[0].vertex_index1_ = last_edge.vertex_index1_;
+ edge_index--;
+ }
+ }
+
+ edges_.resize(edge_index);
+ empty_ = edges_.size() < 3;
+
+ if (empty_)
+ return;
+
+ for (unsigned i = 0; i < edges_.size(); ++i) {
+ FloatPolygonEdge* edge = &edges_[i];
+ edge_tree_.Add(EdgeInterval(edge->MinY(), edge->MaxY(), edge));
+ }
+}
+
+bool FloatPolygon::OverlappingEdges(
+ float min_y,
+ float max_y,
+ Vector<const FloatPolygonEdge*>& result) const {
+ Vector<FloatPolygon::EdgeInterval> overlapping_edge_intervals;
+ edge_tree_.AllOverlaps(FloatPolygon::EdgeInterval(min_y, max_y, 0),
+ overlapping_edge_intervals);
+ unsigned overlapping_edge_intervals_size = overlapping_edge_intervals.size();
+ result.resize(overlapping_edge_intervals_size);
+ for (unsigned i = 0; i < overlapping_edge_intervals_size; ++i) {
+ const FloatPolygonEdge* edge = static_cast<const FloatPolygonEdge*>(
+ overlapping_edge_intervals[i].Data());
+ DCHECK(edge);
+ result[i] = edge;
+ }
+ return overlapping_edge_intervals_size > 0;
+}
+
+static inline float LeftSide(const FloatPoint& vertex1,
+ const FloatPoint& vertex2,
+ const FloatPoint& point) {
+ return ((point.X() - vertex1.X()) * (vertex2.Y() - vertex1.Y())) -
+ ((vertex2.X() - vertex1.X()) * (point.Y() - vertex1.Y()));
+}
+
+bool FloatPolygon::ContainsEvenOdd(const FloatPoint& point) const {
+ unsigned crossing_count = 0;
+ for (unsigned i = 0; i < NumberOfEdges(); ++i) {
+ const FloatPoint& vertex1 = EdgeAt(i).Vertex1();
+ const FloatPoint& vertex2 = EdgeAt(i).Vertex2();
+ if (IsPointOnLineSegment(vertex1, vertex2, point))
+ return true;
+ if ((vertex1.Y() <= point.Y() && vertex2.Y() > point.Y()) ||
+ (vertex1.Y() > point.Y() && vertex2.Y() <= point.Y())) {
+ float vt = (point.Y() - vertex1.Y()) / (vertex2.Y() - vertex1.Y());
+ if (point.X() < vertex1.X() + vt * (vertex2.X() - vertex1.X()))
+ ++crossing_count;
+ }
+ }
+ return crossing_count & 1;
+}
+
+bool FloatPolygon::ContainsNonZero(const FloatPoint& point) const {
+ int winding_number = 0;
+ for (unsigned i = 0; i < NumberOfEdges(); ++i) {
+ const FloatPoint& vertex1 = EdgeAt(i).Vertex1();
+ const FloatPoint& vertex2 = EdgeAt(i).Vertex2();
+ if (IsPointOnLineSegment(vertex1, vertex2, point))
+ return true;
+ if (vertex2.Y() <= point.Y()) {
+ if ((vertex1.Y() > point.Y()) && (LeftSide(vertex1, vertex2, point) > 0))
+ ++winding_number;
+ } else if (vertex2.Y() >= point.Y()) {
+ if ((vertex1.Y() <= point.Y()) && (LeftSide(vertex1, vertex2, point) < 0))
+ --winding_number;
+ }
+ }
+ return winding_number;
+}
+
+bool FloatPolygon::Contains(const FloatPoint& point) const {
+ if (!bounding_box_.Contains(point))
+ return false;
+ return (FillRule() == RULE_NONZERO) ? ContainsNonZero(point)
+ : ContainsEvenOdd(point);
+}
+
+bool VertexPair::Intersection(const VertexPair& other,
+ FloatPoint& point) const {
+ // See: http://paulbourke.net/geometry/pointlineplane/,
+ // "Intersection point of two lines in 2 dimensions"
+
+ const FloatSize& this_delta = Vertex2() - Vertex1();
+ const FloatSize& other_delta = other.Vertex2() - other.Vertex1();
+ float denominator = Determinant(this_delta, other_delta);
+ if (!denominator)
+ return false;
+
+ // The two line segments: "this" vertex1,vertex2 and "other" vertex1,vertex2,
+ // have been defined in parametric form. Each point on the line segment is:
+ // vertex1 + u * (vertex2 - vertex1), when 0 <= u <= 1. We're computing the
+ // values of u for each line at their intersection point.
+
+ const FloatSize& vertex1_delta = Vertex1() - other.Vertex1();
+ float u_this_line = Determinant(other_delta, vertex1_delta) / denominator;
+ float u_other_line = Determinant(this_delta, vertex1_delta) / denominator;
+
+ if (u_this_line < 0 || u_other_line < 0 || u_this_line > 1 ||
+ u_other_line > 1)
+ return false;
+
+ point = Vertex1() + u_this_line * this_delta;
+ return true;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/float_polygon.h b/chromium/third_party/blink/renderer/platform/geometry/float_polygon.h
new file mode 100644
index 00000000000..4ee37987c2c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/float_polygon.h
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. 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.
+ *
+ * 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 HOLDER 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_FLOAT_POLYGON_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_FLOAT_POLYGON_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/geometry/float_point.h"
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_types.h"
+#include "third_party/blink/renderer/platform/pod_interval_tree.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class FloatPolygonEdge;
+
+// This class is used by PODIntervalTree for debugging.
+#ifndef NDEBUG
+template <class>
+struct ValueToString;
+#endif
+
+class PLATFORM_EXPORT FloatPolygon {
+ USING_FAST_MALLOC(FloatPolygon);
+ WTF_MAKE_NONCOPYABLE(FloatPolygon);
+
+ public:
+ FloatPolygon(std::unique_ptr<Vector<FloatPoint>> vertices,
+ WindRule fill_rule);
+
+ const FloatPoint& VertexAt(unsigned index) const {
+ return (*vertices_)[index];
+ }
+ unsigned NumberOfVertices() const { return vertices_->size(); }
+
+ WindRule FillRule() const { return fill_rule_; }
+
+ const FloatPolygonEdge& EdgeAt(unsigned index) const { return edges_[index]; }
+ unsigned NumberOfEdges() const { return edges_.size(); }
+
+ FloatRect BoundingBox() const { return bounding_box_; }
+ bool OverlappingEdges(float min_y,
+ float max_y,
+ Vector<const FloatPolygonEdge*>& result) const;
+ bool Contains(const FloatPoint&) const;
+ bool IsEmpty() const { return empty_; }
+
+ private:
+ typedef PODInterval<float, FloatPolygonEdge*> EdgeInterval;
+ typedef PODIntervalTree<float, FloatPolygonEdge*> EdgeIntervalTree;
+
+ bool ContainsNonZero(const FloatPoint&) const;
+ bool ContainsEvenOdd(const FloatPoint&) const;
+
+ std::unique_ptr<Vector<FloatPoint>> vertices_;
+ WindRule fill_rule_;
+ FloatRect bounding_box_;
+ bool empty_;
+ Vector<FloatPolygonEdge> edges_;
+ EdgeIntervalTree edge_tree_; // Each EdgeIntervalTree node stores minY, maxY,
+ // and a ("UserData") pointer to a
+ // FloatPolygonEdge.
+};
+
+class PLATFORM_EXPORT VertexPair {
+ public:
+ virtual ~VertexPair() = default;
+
+ virtual const FloatPoint& Vertex1() const = 0;
+ virtual const FloatPoint& Vertex2() const = 0;
+
+ float MinX() const { return std::min(Vertex1().X(), Vertex2().X()); }
+ float MinY() const { return std::min(Vertex1().Y(), Vertex2().Y()); }
+ float MaxX() const { return std::max(Vertex1().X(), Vertex2().X()); }
+ float MaxY() const { return std::max(Vertex1().Y(), Vertex2().Y()); }
+
+ bool Intersection(const VertexPair&, FloatPoint&) const;
+};
+
+class PLATFORM_EXPORT FloatPolygonEdge final : public VertexPair {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ friend class FloatPolygon;
+
+ public:
+ const FloatPoint& Vertex1() const override {
+ DCHECK(polygon_);
+ return polygon_->VertexAt(vertex_index1_);
+ }
+
+ const FloatPoint& Vertex2() const override {
+ DCHECK(polygon_);
+ return polygon_->VertexAt(vertex_index2_);
+ }
+
+ const FloatPolygonEdge& PreviousEdge() const {
+ DCHECK(polygon_);
+ DCHECK_GT(polygon_->NumberOfEdges(), 1UL);
+ return polygon_->EdgeAt((edge_index_ + polygon_->NumberOfEdges() - 1) %
+ polygon_->NumberOfEdges());
+ }
+
+ const FloatPolygonEdge& NextEdge() const {
+ DCHECK(polygon_);
+ DCHECK_GT(polygon_->NumberOfEdges(), 1UL);
+ return polygon_->EdgeAt((edge_index_ + 1) % polygon_->NumberOfEdges());
+ }
+
+ const FloatPolygon* Polygon() const { return polygon_; }
+ unsigned VertexIndex1() const { return vertex_index1_; }
+ unsigned VertexIndex2() const { return vertex_index2_; }
+ unsigned EdgeIndex() const { return edge_index_; }
+
+ private:
+ // Edge vertex index1 is less than index2, except the last edge, where index2
+ // is 0. When a polygon edge is defined by 3 or more colinear vertices, index2
+ // can be the the index of the last colinear vertex.
+ unsigned vertex_index1_;
+ unsigned vertex_index2_;
+ unsigned edge_index_;
+ const FloatPolygon* polygon_;
+};
+
+// These structures are used by PODIntervalTree for debugging.
+#ifndef NDEBUG
+template <>
+struct ValueToString<float> {
+ STATIC_ONLY(ValueToString);
+ static String ToString(const float value) { return String::Number(value); }
+};
+
+template <>
+struct ValueToString<FloatPolygonEdge*> {
+ STATIC_ONLY(ValueToString);
+ static String ToString(const FloatPolygonEdge* edge) {
+ return String::Format("%p (%f,%f %f,%f)", edge, edge->Vertex1().X(),
+ edge->Vertex1().Y(), edge->Vertex2().X(),
+ edge->Vertex2().Y());
+ }
+};
+#endif
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_FLOAT_POLYGON_H_
diff --git a/chromium/third_party/blink/renderer/platform/geometry/float_polygon_test.cc b/chromium/third_party/blink/renderer/platform/geometry/float_polygon_test.cc
new file mode 100644
index 00000000000..49b8fde7db7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/float_polygon_test.cc
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. 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.
+ *
+ * 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 HOLDER 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 "third_party/blink/renderer/platform/geometry/float_polygon.h"
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+class FloatPolygonTestValue {
+ public:
+ FloatPolygonTestValue(const float* coordinates,
+ unsigned coordinates_length,
+ WindRule fill_rule) {
+ DCHECK(!(coordinates_length % 2));
+ std::unique_ptr<Vector<FloatPoint>> vertices =
+ std::make_unique<Vector<FloatPoint>>(coordinates_length / 2);
+ for (unsigned i = 0; i < coordinates_length; i += 2)
+ (*vertices)[i / 2] = FloatPoint(coordinates[i], coordinates[i + 1]);
+ polygon_ = std::make_unique<FloatPolygon>(std::move(vertices), fill_rule);
+ }
+
+ const FloatPolygon& Polygon() const { return *polygon_; }
+
+ private:
+ std::unique_ptr<FloatPolygon> polygon_;
+};
+
+namespace {
+
+bool CompareEdgeIndex(const FloatPolygonEdge* edge1,
+ const FloatPolygonEdge* edge2) {
+ return edge1->EdgeIndex() < edge2->EdgeIndex();
+}
+
+Vector<const FloatPolygonEdge*>
+SortedOverlappingEdges(const FloatPolygon& polygon, float min_y, float max_y) {
+ Vector<const FloatPolygonEdge*> result;
+ polygon.OverlappingEdges(min_y, max_y, result);
+ std::sort(result.begin(), result.end(), CompareEdgeIndex);
+ return result;
+}
+
+} // anonymous namespace
+
+#define SIZEOF_ARRAY(p) (sizeof(p) / sizeof(p[0]))
+
+/**
+ * Checks a right triangle. This test covers all of the trivial FloatPolygon
+ * accessors.
+ *
+ * 200,100
+ * /|
+ * / |
+ * / |
+ * -----
+ * 100,200 200,200
+ */
+TEST(FloatPolygonTest, basics) {
+ const float kTriangleCoordinates[] = {200, 100, 200, 200, 100, 200};
+ FloatPolygonTestValue triangle_test_value(
+ kTriangleCoordinates, SIZEOF_ARRAY(kTriangleCoordinates), RULE_NONZERO);
+ const FloatPolygon& triangle = triangle_test_value.Polygon();
+
+ EXPECT_EQ(RULE_NONZERO, triangle.FillRule());
+ EXPECT_FALSE(triangle.IsEmpty());
+
+ EXPECT_EQ(3u, triangle.NumberOfVertices());
+ EXPECT_EQ(FloatPoint(200, 100), triangle.VertexAt(0));
+ EXPECT_EQ(FloatPoint(200, 200), triangle.VertexAt(1));
+ EXPECT_EQ(FloatPoint(100, 200), triangle.VertexAt(2));
+
+ EXPECT_EQ(3u, triangle.NumberOfEdges());
+ EXPECT_EQ(FloatPoint(200, 100), triangle.EdgeAt(0).Vertex1());
+ EXPECT_EQ(FloatPoint(200, 200), triangle.EdgeAt(0).Vertex2());
+ EXPECT_EQ(FloatPoint(200, 200), triangle.EdgeAt(1).Vertex1());
+ EXPECT_EQ(FloatPoint(100, 200), triangle.EdgeAt(1).Vertex2());
+ EXPECT_EQ(FloatPoint(100, 200), triangle.EdgeAt(2).Vertex1());
+ EXPECT_EQ(FloatPoint(200, 100), triangle.EdgeAt(2).Vertex2());
+
+ EXPECT_EQ(0u, triangle.EdgeAt(0).VertexIndex1());
+ EXPECT_EQ(1u, triangle.EdgeAt(0).VertexIndex2());
+ EXPECT_EQ(1u, triangle.EdgeAt(1).VertexIndex1());
+ EXPECT_EQ(2u, triangle.EdgeAt(1).VertexIndex2());
+ EXPECT_EQ(2u, triangle.EdgeAt(2).VertexIndex1());
+ EXPECT_EQ(0u, triangle.EdgeAt(2).VertexIndex2());
+
+ EXPECT_EQ(200, triangle.EdgeAt(0).MinX());
+ EXPECT_EQ(200, triangle.EdgeAt(0).MaxX());
+ EXPECT_EQ(100, triangle.EdgeAt(1).MinX());
+ EXPECT_EQ(200, triangle.EdgeAt(1).MaxX());
+ EXPECT_EQ(100, triangle.EdgeAt(2).MinX());
+ EXPECT_EQ(200, triangle.EdgeAt(2).MaxX());
+
+ EXPECT_EQ(100, triangle.EdgeAt(0).MinY());
+ EXPECT_EQ(200, triangle.EdgeAt(0).MaxY());
+ EXPECT_EQ(200, triangle.EdgeAt(1).MinY());
+ EXPECT_EQ(200, triangle.EdgeAt(1).MaxY());
+ EXPECT_EQ(100, triangle.EdgeAt(2).MinY());
+ EXPECT_EQ(200, triangle.EdgeAt(2).MaxY());
+
+ EXPECT_EQ(0u, triangle.EdgeAt(0).EdgeIndex());
+ EXPECT_EQ(1u, triangle.EdgeAt(1).EdgeIndex());
+ EXPECT_EQ(2u, triangle.EdgeAt(2).EdgeIndex());
+
+ EXPECT_EQ(2u, triangle.EdgeAt(0).PreviousEdge().EdgeIndex());
+ EXPECT_EQ(1u, triangle.EdgeAt(0).NextEdge().EdgeIndex());
+ EXPECT_EQ(0u, triangle.EdgeAt(1).PreviousEdge().EdgeIndex());
+ EXPECT_EQ(2u, triangle.EdgeAt(1).NextEdge().EdgeIndex());
+ EXPECT_EQ(1u, triangle.EdgeAt(2).PreviousEdge().EdgeIndex());
+ EXPECT_EQ(0u, triangle.EdgeAt(2).NextEdge().EdgeIndex());
+
+ EXPECT_EQ(FloatRect(100, 100, 100, 100), triangle.BoundingBox());
+
+ Vector<const FloatPolygonEdge*> result_a =
+ SortedOverlappingEdges(triangle, 100, 200);
+ EXPECT_EQ(3u, result_a.size());
+ if (result_a.size() == 3) {
+ EXPECT_EQ(0u, result_a[0]->EdgeIndex());
+ EXPECT_EQ(1u, result_a[1]->EdgeIndex());
+ EXPECT_EQ(2u, result_a[2]->EdgeIndex());
+ }
+
+ Vector<const FloatPolygonEdge*> result_b =
+ SortedOverlappingEdges(triangle, 200, 200);
+ EXPECT_EQ(3u, result_b.size());
+ if (result_b.size() == 3) {
+ EXPECT_EQ(0u, result_b[0]->EdgeIndex());
+ EXPECT_EQ(1u, result_b[1]->EdgeIndex());
+ EXPECT_EQ(2u, result_b[2]->EdgeIndex());
+ }
+
+ Vector<const FloatPolygonEdge*> result_c =
+ SortedOverlappingEdges(triangle, 100, 150);
+ EXPECT_EQ(2u, result_c.size());
+ if (result_c.size() == 2) {
+ EXPECT_EQ(0u, result_c[0]->EdgeIndex());
+ EXPECT_EQ(2u, result_c[1]->EdgeIndex());
+ }
+
+ Vector<const FloatPolygonEdge*> result_d =
+ SortedOverlappingEdges(triangle, 201, 300);
+ EXPECT_EQ(0u, result_d.size());
+
+ Vector<const FloatPolygonEdge*> result_e =
+ SortedOverlappingEdges(triangle, 98, 99);
+ EXPECT_EQ(0u, result_e.size());
+}
+
+/**
+ * Tests FloatPolygon::contains() with a right triangle, and fillRule = nonzero.
+ *
+ * 200,100
+ * /|
+ * / |
+ * / |
+ * -----
+ * 100,200 200,200
+ */
+TEST(FloatPolygonTest, triangle_nonzero) {
+ const float kTriangleCoordinates[] = {200, 100, 200, 200, 100, 200};
+ FloatPolygonTestValue triangle_test_value(
+ kTriangleCoordinates, SIZEOF_ARRAY(kTriangleCoordinates), RULE_NONZERO);
+ const FloatPolygon& triangle = triangle_test_value.Polygon();
+
+ EXPECT_EQ(RULE_NONZERO, triangle.FillRule());
+ EXPECT_TRUE(triangle.Contains(FloatPoint(200, 100)));
+ EXPECT_TRUE(triangle.Contains(FloatPoint(200, 200)));
+ EXPECT_TRUE(triangle.Contains(FloatPoint(100, 200)));
+ EXPECT_TRUE(triangle.Contains(FloatPoint(150, 150)));
+ EXPECT_FALSE(triangle.Contains(FloatPoint(100, 100)));
+ EXPECT_FALSE(triangle.Contains(FloatPoint(149, 149)));
+ EXPECT_FALSE(triangle.Contains(FloatPoint(150, 200.5)));
+ EXPECT_FALSE(triangle.Contains(FloatPoint(201, 200.5)));
+}
+
+/**
+ * Tests FloatPolygon::contains() with a right triangle, and fillRule = evenodd;
+ *
+ * 200,100
+ * /|
+ * / |
+ * / |
+ * -----
+ * 100,200 200,200
+ */
+TEST(FloatPolygonTest, triangle_evenodd) {
+ const float kTriangleCoordinates[] = {200, 100, 200, 200, 100, 200};
+ FloatPolygonTestValue triangle_test_value(
+ kTriangleCoordinates, SIZEOF_ARRAY(kTriangleCoordinates), RULE_EVENODD);
+ const FloatPolygon& triangle = triangle_test_value.Polygon();
+
+ EXPECT_EQ(RULE_EVENODD, triangle.FillRule());
+ EXPECT_TRUE(triangle.Contains(FloatPoint(200, 100)));
+ EXPECT_TRUE(triangle.Contains(FloatPoint(200, 200)));
+ EXPECT_TRUE(triangle.Contains(FloatPoint(100, 200)));
+ EXPECT_TRUE(triangle.Contains(FloatPoint(150, 150)));
+ EXPECT_FALSE(triangle.Contains(FloatPoint(100, 100)));
+ EXPECT_FALSE(triangle.Contains(FloatPoint(149, 149)));
+ EXPECT_FALSE(triangle.Contains(FloatPoint(150, 200.5)));
+ EXPECT_FALSE(triangle.Contains(FloatPoint(201, 200.5)));
+}
+
+#define TEST_EMPTY(coordinates) \
+ { \
+ FloatPolygonTestValue empty_polygon_test_value( \
+ coordinates, SIZEOF_ARRAY(coordinates), RULE_NONZERO); \
+ const FloatPolygon& empty_polygon = empty_polygon_test_value.Polygon(); \
+ EXPECT_TRUE(empty_polygon.IsEmpty()); \
+ }
+
+TEST(FloatPolygonTest, emptyPolygons) {
+ const float kEmptyCoordinates1[] = {0, 0};
+ TEST_EMPTY(kEmptyCoordinates1);
+
+ const float kEmptyCoordinates2[] = {0, 0, 1, 1};
+ TEST_EMPTY(kEmptyCoordinates2);
+
+ const float kEmptyCoordinates3[] = {0, 0, 1, 1, 2, 2, 3, 3};
+ TEST_EMPTY(kEmptyCoordinates3);
+
+ const float kEmptyCoordinates4[] = {0, 0, 1, 1, 2, 2, 3, 3, 1, 1};
+ TEST_EMPTY(kEmptyCoordinates4);
+
+ const float kEmptyCoordinates5[] = {0, 0, 0, 1, 0, 2, 0, 3, 0, 1};
+ TEST_EMPTY(kEmptyCoordinates5);
+
+ const float kEmptyCoordinates6[] = {0, 0, 1, 0, 2, 0, 3, 0, 1, 0};
+ TEST_EMPTY(kEmptyCoordinates6);
+}
+
+/*
+ * Test FloatPolygon::contains() with a trapezoid. The vertices are listed in
+ * counter-clockwise order.
+ *
+ * 150,100 250,100
+ * +----------+
+ * / \
+ * / \
+ * +----------------+
+ * 100,150 300,150
+ */
+TEST(FloatPolygonTest, trapezoid) {
+ const float kTrapezoidCoordinates[] = {100, 150, 300, 150,
+ 250, 100, 150, 100};
+ FloatPolygonTestValue trapezoid_test_value(
+ kTrapezoidCoordinates, SIZEOF_ARRAY(kTrapezoidCoordinates), RULE_EVENODD);
+ const FloatPolygon& trapezoid = trapezoid_test_value.Polygon();
+
+ EXPECT_FALSE(trapezoid.IsEmpty());
+ EXPECT_EQ(4u, trapezoid.NumberOfVertices());
+ EXPECT_EQ(FloatRect(100, 100, 200, 50), trapezoid.BoundingBox());
+
+ EXPECT_TRUE(trapezoid.Contains(FloatPoint(150, 100)));
+ EXPECT_TRUE(trapezoid.Contains(FloatPoint(150, 101)));
+ EXPECT_TRUE(trapezoid.Contains(FloatPoint(200, 125)));
+ EXPECT_FALSE(trapezoid.Contains(FloatPoint(149, 100)));
+ EXPECT_FALSE(trapezoid.Contains(FloatPoint(301, 150)));
+}
+
+/*
+ * Test FloatPolygon::contains() with a non-convex rectilinear polygon. The
+ * polygon has the same shape as the letter "H":
+ *
+ * 100,100 150,100 200,100 250,100
+ * +--------+ +--------+
+ * | | | |
+ * | | | |
+ * | +--------+ |
+ * | 150,150 200,150 |
+ * | |
+ * | 150,200 200,200 |
+ * | +--------+ |
+ * | | | |
+ * | | | |
+ * +--------+ +--------+
+ * 100,250 150,250 200,250 250,250
+ */
+TEST(FloatPolygonTest, rectilinear) {
+ const float kHCoordinates[] = {100, 100, 150, 100, 150, 150, 200, 150,
+ 200, 100, 250, 100, 250, 250, 200, 250,
+ 200, 200, 150, 200, 150, 250, 100, 250};
+ FloatPolygonTestValue h_test_value(kHCoordinates, SIZEOF_ARRAY(kHCoordinates),
+ RULE_NONZERO);
+ const FloatPolygon& h = h_test_value.Polygon();
+
+ EXPECT_FALSE(h.IsEmpty());
+ EXPECT_EQ(12u, h.NumberOfVertices());
+ EXPECT_EQ(FloatRect(100, 100, 150, 150), h.BoundingBox());
+
+ EXPECT_TRUE(h.Contains(FloatPoint(100, 100)));
+ EXPECT_TRUE(h.Contains(FloatPoint(125, 100)));
+ EXPECT_TRUE(h.Contains(FloatPoint(125, 125)));
+ EXPECT_TRUE(h.Contains(FloatPoint(150, 100)));
+ EXPECT_TRUE(h.Contains(FloatPoint(200, 200)));
+ EXPECT_TRUE(h.Contains(FloatPoint(225, 225)));
+ EXPECT_TRUE(h.Contains(FloatPoint(250, 250)));
+ EXPECT_TRUE(h.Contains(FloatPoint(100, 250)));
+ EXPECT_TRUE(h.Contains(FloatPoint(125, 250)));
+
+ EXPECT_FALSE(h.Contains(FloatPoint(99, 100)));
+ EXPECT_FALSE(h.Contains(FloatPoint(251, 100)));
+ EXPECT_FALSE(h.Contains(FloatPoint(151, 100)));
+ EXPECT_FALSE(h.Contains(FloatPoint(199, 100)));
+ EXPECT_FALSE(h.Contains(FloatPoint(175, 125)));
+ EXPECT_FALSE(h.Contains(FloatPoint(151, 250)));
+ EXPECT_FALSE(h.Contains(FloatPoint(199, 250)));
+ EXPECT_FALSE(h.Contains(FloatPoint(199, 250)));
+ EXPECT_FALSE(h.Contains(FloatPoint(175, 225)));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/float_quad.cc b/chromium/third_party/blink/renderer/platform/geometry/float_quad.cc
new file mode 100644
index 00000000000..4e05609d84e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/float_quad.cc
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
+ * Copyright (C) 2013 Xidorn Quan (quanxunzhen@gmail.com)
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/geometry/float_quad.h"
+
+#include <algorithm>
+#include <cmath>
+#include <limits>
+#include "third_party/blink/renderer/platform/geometry/float_shape_helpers.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/skia/include/core/SkPoint.h"
+
+namespace blink {
+
+static inline float Min4(float a, float b, float c, float d) {
+ return std::min(std::min(a, b), std::min(c, d));
+}
+
+static inline float Max4(float a, float b, float c, float d) {
+ return std::max(std::max(a, b), std::max(c, d));
+}
+
+inline float Dot(const FloatSize& a, const FloatSize& b) {
+ return a.Width() * b.Width() + a.Height() * b.Height();
+}
+
+inline bool IsPointInTriangle(const FloatPoint& p,
+ const FloatPoint& t1,
+ const FloatPoint& t2,
+ const FloatPoint& t3) {
+ // Compute vectors
+ FloatSize v0 = t3 - t1;
+ FloatSize v1 = t2 - t1;
+ FloatSize v2 = p - t1;
+
+ // Compute dot products
+ float dot00 = Dot(v0, v0);
+ float dot01 = Dot(v0, v1);
+ float dot02 = Dot(v0, v2);
+ float dot11 = Dot(v1, v1);
+ float dot12 = Dot(v1, v2);
+
+ // Compute barycentric coordinates
+ float inv_denom = 1.0f / (dot00 * dot11 - dot01 * dot01);
+ float u = (dot11 * dot02 - dot01 * dot12) * inv_denom;
+ float v = (dot00 * dot12 - dot01 * dot02) * inv_denom;
+
+ // Check if point is in triangle
+ return (u >= 0) && (v >= 0) && (u + v <= 1);
+}
+
+static inline float SaturateInf(float value) {
+ if (UNLIKELY(std::isinf(value))) {
+ return std::signbit(value) ? std::numeric_limits<int>::min()
+ : std::numeric_limits<int>::max();
+ }
+ return value;
+}
+
+FloatRect FloatQuad::BoundingBox() const {
+ float left = SaturateInf(Min4(p1_.X(), p2_.X(), p3_.X(), p4_.X()));
+ float top = SaturateInf(Min4(p1_.Y(), p2_.Y(), p3_.Y(), p4_.Y()));
+
+ float right = SaturateInf(Max4(p1_.X(), p2_.X(), p3_.X(), p4_.X()));
+ float bottom = SaturateInf(Max4(p1_.Y(), p2_.Y(), p3_.Y(), p4_.Y()));
+
+ return FloatRect(left, top, right - left, bottom - top);
+}
+
+static inline bool WithinEpsilon(float a, float b) {
+ return fabs(a - b) < std::numeric_limits<float>::epsilon();
+}
+
+FloatQuad::FloatQuad(const SkPoint (&quad)[4])
+ : FloatQuad(FloatPoint(quad[0]),
+ FloatPoint(quad[1]),
+ FloatPoint(quad[2]),
+ FloatPoint(quad[3])) {}
+
+bool FloatQuad::IsRectilinear() const {
+ return (WithinEpsilon(p1_.X(), p2_.X()) && WithinEpsilon(p2_.Y(), p3_.Y()) &&
+ WithinEpsilon(p3_.X(), p4_.X()) && WithinEpsilon(p4_.Y(), p1_.Y())) ||
+ (WithinEpsilon(p1_.Y(), p2_.Y()) && WithinEpsilon(p2_.X(), p3_.X()) &&
+ WithinEpsilon(p3_.Y(), p4_.Y()) && WithinEpsilon(p4_.X(), p1_.X()));
+}
+
+bool FloatQuad::ContainsPoint(const FloatPoint& p) const {
+ return IsPointInTriangle(p, p1_, p2_, p3_) ||
+ IsPointInTriangle(p, p1_, p3_, p4_);
+}
+
+// Note that we only handle convex quads here.
+bool FloatQuad::ContainsQuad(const FloatQuad& other) const {
+ return ContainsPoint(other.P1()) && ContainsPoint(other.P2()) &&
+ ContainsPoint(other.P3()) && ContainsPoint(other.P4());
+}
+
+static inline FloatPoint RightMostCornerToVector(const FloatRect& rect,
+ const FloatSize& vector) {
+ // Return the corner of the rectangle that if it is to the left of the vector
+ // would mean all of the rectangle is to the left of the vector.
+ // The vector here represents the side between two points in a clockwise
+ // convex polygon.
+ //
+ // Q XXX
+ // QQQ XXX If the lower left corner of X is left of the vector that goes
+ // QQQ from the top corner of Q to the right corner of Q, then all of X
+ // Q is left of the vector, and intersection impossible.
+ //
+ FloatPoint point;
+ if (vector.Width() >= 0)
+ point.SetY(rect.MaxY());
+ else
+ point.SetY(rect.Y());
+ if (vector.Height() >= 0)
+ point.SetX(rect.X());
+ else
+ point.SetX(rect.MaxX());
+ return point;
+}
+
+bool FloatQuad::IntersectsRect(const FloatRect& rect) const {
+ // IntersectsRect is only valid on convex quads which an empty quad is not.
+ DCHECK(!IsEmpty());
+
+ // For each side of the quad clockwise we check if the rectangle is to the
+ // left of it since only content on the right can onlap with the quad. This
+ // only works if the quad is convex.
+ FloatSize v1, v2, v3, v4;
+
+ // Ensure we use clockwise vectors.
+ if (!IsCounterclockwise()) {
+ v1 = p2_ - p1_;
+ v2 = p3_ - p2_;
+ v3 = p4_ - p3_;
+ v4 = p1_ - p4_;
+ } else {
+ v1 = p4_ - p1_;
+ v2 = p1_ - p2_;
+ v3 = p2_ - p3_;
+ v4 = p3_ - p4_;
+ }
+
+ FloatPoint p = RightMostCornerToVector(rect, v1);
+ if (Determinant(v1, p - p1_) < 0)
+ return false;
+
+ p = RightMostCornerToVector(rect, v2);
+ if (Determinant(v2, p - p2_) < 0)
+ return false;
+
+ p = RightMostCornerToVector(rect, v3);
+ if (Determinant(v3, p - p3_) < 0)
+ return false;
+
+ p = RightMostCornerToVector(rect, v4);
+ if (Determinant(v4, p - p4_) < 0)
+ return false;
+
+ // If not all of the rectangle is outside one of the quad's four sides, then
+ // that means at least a part of the rectangle is overlapping the quad.
+ return true;
+}
+
+// Tests whether the line is contained by or intersected with the circle.
+static inline bool LineIntersectsCircle(const FloatPoint& center,
+ float radius,
+ const FloatPoint& p0,
+ const FloatPoint& p1) {
+ float x0 = p0.X() - center.X(), y0 = p0.Y() - center.Y();
+ float x1 = p1.X() - center.X(), y1 = p1.Y() - center.Y();
+ float radius2 = radius * radius;
+ if ((x0 * x0 + y0 * y0) <= radius2 || (x1 * x1 + y1 * y1) <= radius2)
+ return true;
+ if (p0 == p1)
+ return false;
+
+ float a = y0 - y1;
+ float b = x1 - x0;
+ float c = x0 * y1 - x1 * y0;
+ float distance2 = c * c / (a * a + b * b);
+ // If distance between the center point and the line > the radius,
+ // the line doesn't cross (or is contained by) the ellipse.
+ if (distance2 > radius2)
+ return false;
+
+ // The nearest point on the line is between p0 and p1?
+ float x = -a * c / (a * a + b * b);
+ float y = -b * c / (a * a + b * b);
+
+ return (((x0 <= x && x <= x1) || (x0 >= x && x >= x1)) &&
+ ((y0 <= y && y <= y1) || (y1 <= y && y <= y0)));
+}
+
+bool FloatQuad::IntersectsCircle(const FloatPoint& center, float radius) const {
+ return ContainsPoint(
+ center) // The circle may be totally contained by the quad.
+ || LineIntersectsCircle(center, radius, p1_, p2_) ||
+ LineIntersectsCircle(center, radius, p2_, p3_) ||
+ LineIntersectsCircle(center, radius, p3_, p4_) ||
+ LineIntersectsCircle(center, radius, p4_, p1_);
+}
+
+bool FloatQuad::IntersectsEllipse(const FloatPoint& center,
+ const FloatSize& radii) const {
+ // Transform the ellipse to an origin-centered circle whose radius is the
+ // product of major radius and minor radius. Here we apply the same
+ // transformation to the quad.
+ FloatQuad transformed_quad(*this);
+ transformed_quad.Move(-center.X(), -center.Y());
+ transformed_quad.Scale(radii.Height(), radii.Width());
+
+ FloatPoint origin_point;
+ return transformed_quad.IntersectsCircle(origin_point,
+ radii.Height() * radii.Width());
+}
+
+bool FloatQuad::IsCounterclockwise() const {
+ // Return if the two first vectors are turning clockwise. If the quad is
+ // convex then all following vectors will turn the same way.
+ return Determinant(p2_ - p1_, p3_ - p2_) < 0;
+}
+
+std::ostream& operator<<(std::ostream& ostream, const FloatQuad& quad) {
+ return ostream << quad.ToString();
+}
+
+String FloatQuad::ToString() const {
+ return String::Format("%s; %s; %s; %s", p1_.ToString().Ascii().data(),
+ p2_.ToString().Ascii().data(),
+ p3_.ToString().Ascii().data(),
+ p4_.ToString().Ascii().data());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/float_quad.h b/chromium/third_party/blink/renderer/platform/geometry/float_quad.h
new file mode 100644
index 00000000000..b392aa72297
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/float_quad.h
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2008 Apple 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_FLOAT_QUAD_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_FLOAT_QUAD_H_
+
+#include <iosfwd>
+#include "third_party/blink/renderer/platform/geometry/float_point.h"
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/geometry/layout_size.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+struct SkPoint;
+
+namespace blink {
+
+// A FloatQuad is a collection of 4 points, often representing the result of
+// mapping a rectangle through transforms. When initialized from a rect, the
+// points are in clockwise order from top left.
+class PLATFORM_EXPORT FloatQuad {
+ USING_FAST_MALLOC(FloatQuad);
+
+ public:
+ FloatQuad() = default;
+
+ FloatQuad(const FloatPoint& p1,
+ const FloatPoint& p2,
+ const FloatPoint& p3,
+ const FloatPoint& p4)
+ : p1_(p1), p2_(p2), p3_(p3), p4_(p4) {}
+
+ FloatQuad(const FloatRect& in_rect)
+ : p1_(in_rect.Location()),
+ p2_(in_rect.MaxX(), in_rect.Y()),
+ p3_(in_rect.MaxX(), in_rect.MaxY()),
+ p4_(in_rect.X(), in_rect.MaxY()) {}
+
+ // Converts from an array of four SkPoints, as from SkMatrix::mapRectToQuad.
+ explicit FloatQuad(const SkPoint (&)[4]);
+
+ FloatPoint P1() const { return p1_; }
+ FloatPoint P2() const { return p2_; }
+ FloatPoint P3() const { return p3_; }
+ FloatPoint P4() const { return p4_; }
+
+ void SetP1(const FloatPoint& p) { p1_ = p; }
+ void SetP2(const FloatPoint& p) { p2_ = p; }
+ void SetP3(const FloatPoint& p) { p3_ = p; }
+ void SetP4(const FloatPoint& p) { p4_ = p; }
+
+ // isEmpty tests that the bounding box is empty. This will not identify
+ // "slanted" empty quads.
+ bool IsEmpty() const { return BoundingBox().IsEmpty(); }
+
+ // Tests whether this quad can be losslessly represented by a FloatRect,
+ // that is, if two edges are parallel to the x-axis and the other two
+ // are parallel to the y-axis. If this method returns true, the
+ // corresponding FloatRect can be retrieved with boundingBox().
+ bool IsRectilinear() const;
+
+ // Tests whether the given point is inside, or on an edge or corner of this
+ // quad.
+ bool ContainsPoint(const FloatPoint&) const;
+
+ // Tests whether the four corners of other are inside, or coincident with the
+ // sides of this quad. Note that this only works for convex quads, but that
+ // includes all quads that originate
+ // from transformed rects.
+ bool ContainsQuad(const FloatQuad&) const;
+
+ // Tests whether any part of the rectangle intersects with this quad.
+ // This only works for convex quads.
+ // This intersection is edge-inclusive and will return true even if the
+ // intersecting area is empty (i.e., the intersection is a line or a point).
+ bool IntersectsRect(const FloatRect&) const;
+
+ // Test whether any part of the circle/ellipse intersects with this quad.
+ // Note that these two functions only work for convex quads.
+ // These intersections are edge-inclusive and will return true even if the
+ // intersecting area is empty (i.e., the intersection is a line or a point).
+ bool IntersectsCircle(const FloatPoint& center, float radius) const;
+ bool IntersectsEllipse(const FloatPoint& center,
+ const FloatSize& radii) const;
+
+ // The center of the quad. If the quad is the result of a affine-transformed
+ // rectangle this is the same as the original center transformed.
+ FloatPoint Center() const {
+ return FloatPoint((p1_.X() + p2_.X() + p3_.X() + p4_.X()) / 4.0,
+ (p1_.Y() + p2_.Y() + p3_.Y() + p4_.Y()) / 4.0);
+ }
+
+ FloatRect BoundingBox() const;
+ IntRect EnclosingBoundingBox() const {
+ return EnclosingIntRect(BoundingBox());
+ }
+
+ void Move(const FloatSize& offset) {
+ p1_ += offset;
+ p2_ += offset;
+ p3_ += offset;
+ p4_ += offset;
+ }
+
+ void Move(const LayoutSize& offset) {
+ Move(offset.Width().ToFloat(), offset.Height().ToFloat());
+ }
+
+ void Move(float dx, float dy) {
+ p1_.Move(dx, dy);
+ p2_.Move(dx, dy);
+ p3_.Move(dx, dy);
+ p4_.Move(dx, dy);
+ }
+
+ void Scale(float dx, float dy) {
+ p1_.Scale(dx, dy);
+ p2_.Scale(dx, dy);
+ p3_.Scale(dx, dy);
+ p4_.Scale(dx, dy);
+ }
+
+ // Tests whether points are in clock-wise, or counter clock-wise order.
+ // Note that output is undefined when all points are colinear.
+ bool IsCounterclockwise() const;
+
+ String ToString() const;
+
+ private:
+ FloatPoint p1_;
+ FloatPoint p2_;
+ FloatPoint p3_;
+ FloatPoint p4_;
+};
+
+inline FloatQuad& operator+=(FloatQuad& a, const FloatSize& b) {
+ a.Move(b);
+ return a;
+}
+
+inline FloatQuad& operator-=(FloatQuad& a, const FloatSize& b) {
+ a.Move(-b.Width(), -b.Height());
+ return a;
+}
+
+inline bool operator==(const FloatQuad& a, const FloatQuad& b) {
+ return a.P1() == b.P1() && a.P2() == b.P2() && a.P3() == b.P3() &&
+ a.P4() == b.P4();
+}
+
+inline bool operator!=(const FloatQuad& a, const FloatQuad& b) {
+ return a.P1() != b.P1() || a.P2() != b.P2() || a.P3() != b.P3() ||
+ a.P4() != b.P4();
+}
+
+PLATFORM_EXPORT std::ostream& operator<<(std::ostream&, const FloatQuad&);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_FLOAT_QUAD_H_
diff --git a/chromium/third_party/blink/renderer/platform/geometry/float_quad_test.cc b/chromium/third_party/blink/renderer/platform/geometry/float_quad_test.cc
new file mode 100644
index 00000000000..4827a82f62a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/float_quad_test.cc
@@ -0,0 +1,113 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/geometry/float_quad.h"
+
+#include <limits>
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+TEST(FloatQuadTest, ToString) {
+ FloatQuad quad(FloatPoint(2, 3), FloatPoint(5, 7), FloatPoint(11, 13),
+ FloatPoint(17, 19));
+ EXPECT_EQ("2,3; 5,7; 11,13; 17,19", quad.ToString());
+}
+
+TEST(FloatQuadTest, BoundingBox) {
+ FloatQuad quad(FloatPoint(2, 3), FloatPoint(5, 7), FloatPoint(11, 13),
+ FloatPoint(17, 19));
+ FloatRect rect = quad.BoundingBox();
+ EXPECT_EQ(rect.X(), 2);
+ EXPECT_EQ(rect.Y(), 3);
+ EXPECT_EQ(rect.Width(), 17 - 2);
+ EXPECT_EQ(rect.Height(), 19 - 3);
+}
+
+TEST(FloatQuadTest, BoundingBoxSaturateInf) {
+ double inf = std::numeric_limits<double>::infinity();
+ FloatQuad quad(FloatPoint(-inf, 3), FloatPoint(5, inf), FloatPoint(11, 13),
+ FloatPoint(17, 19));
+ FloatRect rect = quad.BoundingBox();
+ EXPECT_EQ(rect.X(), std::numeric_limits<int>::min());
+ EXPECT_EQ(rect.Y(), 3.0f);
+ EXPECT_EQ(rect.Width(), 17.0f - std::numeric_limits<int>::min());
+ EXPECT_EQ(rect.Height(), std::numeric_limits<int>::max() - 3.0f);
+}
+
+TEST(FloatQuadTest, RectIntersectionIsInclusive) {
+ // A rectilinear quad at (10, 10) with dimensions 10x10.
+ FloatQuad quad(FloatRect(10, 10, 10, 10));
+
+ // A rect fully contained in the quad should intersect.
+ EXPECT_TRUE(quad.IntersectsRect(FloatRect(11, 11, 8, 8)));
+
+ // A point fully contained in the quad should intersect.
+ EXPECT_TRUE(quad.IntersectsRect(FloatRect(11, 11, 0, 0)));
+
+ // A rect that touches the quad only at the point (10, 10) should intersect.
+ EXPECT_TRUE(quad.IntersectsRect(FloatRect(9, 9, 1, 1)));
+
+ // A rect that touches the quad only on the left edge should intersect.
+ EXPECT_TRUE(quad.IntersectsRect(FloatRect(9, 11, 1, 1)));
+
+ // A rect that touches the quad only on the top edge should intersect.
+ EXPECT_TRUE(quad.IntersectsRect(FloatRect(11, 9, 1, 1)));
+
+ // A rect that touches the quad only on the right edge should intersect.
+ EXPECT_TRUE(quad.IntersectsRect(FloatRect(20, 11, 1, 1)));
+
+ // A rect that touches the quad only on the bottom edge should intersect.
+ EXPECT_TRUE(quad.IntersectsRect(FloatRect(11, 20, 1, 1)));
+
+ // A rect that is fully outside the quad should not intersect.
+ EXPECT_FALSE(quad.IntersectsRect(FloatRect(8, 8, 1, 1)));
+
+ // A point that is fully outside the quad should not intersect.
+ EXPECT_FALSE(quad.IntersectsRect(FloatRect(9, 9, 0, 0)));
+}
+
+TEST(FloatQuadTest, CircleIntersectionIsInclusive) {
+ // A rectilinear quad at (10, 10) with dimensions 10x10.
+ FloatQuad quad(FloatRect(10, 10, 10, 10));
+
+ // A circle fully contained in the top-left of the quad should intersect.
+ EXPECT_TRUE(quad.IntersectsCircle(FloatPoint(12, 12), 1));
+
+ // A point fully contained in the top-left of the quad should intersect.
+ EXPECT_TRUE(quad.IntersectsCircle(FloatPoint(12, 12), 0));
+
+ // A circle that touches the left edge should intersect.
+ EXPECT_TRUE(quad.IntersectsCircle(FloatPoint(9, 11), 1));
+
+ // A circle that touches the top edge should intersect.
+ EXPECT_TRUE(quad.IntersectsCircle(FloatPoint(11, 9), 1));
+
+ // A circle that touches the right edge should intersect.
+ EXPECT_TRUE(quad.IntersectsCircle(FloatPoint(21, 11), 1));
+
+ // A circle that touches the bottom edge should intersect.
+ EXPECT_TRUE(quad.IntersectsCircle(FloatPoint(11, 21), 1));
+
+ // A point that touches the left edge should intersect.
+ EXPECT_TRUE(quad.IntersectsCircle(FloatPoint(10, 11), 0));
+
+ // A point that touches the top edge should intersect.
+ EXPECT_TRUE(quad.IntersectsCircle(FloatPoint(11, 10), 0));
+
+ // A point that touches the right edge should intersect.
+ EXPECT_TRUE(quad.IntersectsCircle(FloatPoint(20, 11), 0));
+
+ // A point that touches the bottom edge should intersect.
+ EXPECT_TRUE(quad.IntersectsCircle(FloatPoint(11, 20), 0));
+
+ // A circle that is fully outside the quad should not intersect.
+ EXPECT_FALSE(quad.IntersectsCircle(FloatPoint(9, 9), 1));
+
+ // A point that is fully outside the quad should not intersect.
+ EXPECT_FALSE(quad.IntersectsCircle(FloatPoint(9, 9), 0));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/float_rect.cc b/chromium/third_party/blink/renderer/platform/geometry/float_rect.cc
new file mode 100644
index 00000000000..b5c2e4d9535
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/float_rect.cc
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2003, 2006, 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2005 Nokia. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/geometry/float_rect.h"
+
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/skia/include/core/SkRect.h"
+#include "ui/gfx/geometry/rect_f.h"
+
+namespace blink {
+
+FloatRect::FloatRect(const IntRect& r)
+ : location_(r.Location()), size_(r.Size()) {}
+
+FloatRect::FloatRect(const LayoutRect& r)
+ : location_(r.Location()), size_(r.Size()) {}
+
+FloatRect::FloatRect(const SkRect& r)
+ : location_(r.fLeft, r.fTop), size_(r.width(), r.height()) {}
+
+void FloatRect::Move(const LayoutSize& delta) {
+ location_.Move(delta.Width().ToFloat(), delta.Height().ToFloat());
+}
+
+void FloatRect::Move(const IntSize& delta) {
+ location_.Move(delta.Width(), delta.Height());
+}
+
+FloatRect FloatRect::NarrowPrecision(double x,
+ double y,
+ double width,
+ double height) {
+ return FloatRect(clampTo<float>(x), clampTo<float>(y), clampTo<float>(width),
+ clampTo<float>(height));
+}
+
+#if DCHECK_IS_ON()
+bool FloatRect::MayNotHaveExactIntRectRepresentation() const {
+ static const float kMaxExactlyExpressible = 1 << FLT_MANT_DIG;
+ return fabs(X()) > kMaxExactlyExpressible ||
+ fabs(Y()) > kMaxExactlyExpressible ||
+ fabs(Width()) > kMaxExactlyExpressible ||
+ fabs(Height()) > kMaxExactlyExpressible ||
+ fabs(MaxX()) > kMaxExactlyExpressible ||
+ fabs(MaxY()) > kMaxExactlyExpressible;
+}
+
+bool FloatRect::EqualWithinEpsilon(const FloatRect& other,
+ float epsilon) const {
+ return std::abs(other.X() - X()) <= epsilon &&
+ std::abs(other.Y() - Y()) <= epsilon &&
+ std::abs(other.Width() - Width()) <= epsilon &&
+ std::abs(other.Height() - Height()) <= epsilon;
+}
+
+#endif
+
+bool FloatRect::IsExpressibleAsIntRect() const {
+ return isWithinIntRange(X()) && isWithinIntRange(Y()) &&
+ isWithinIntRange(Width()) && isWithinIntRange(Height()) &&
+ isWithinIntRange(MaxX()) && isWithinIntRange(MaxY());
+}
+
+void FloatRect::ShiftXEdgeTo(float edge) {
+ float delta = edge - X();
+ SetX(edge);
+ SetWidth(std::max(0.0f, Width() - delta));
+}
+
+void FloatRect::ShiftMaxXEdgeTo(float edge) {
+ float delta = edge - MaxX();
+ SetWidth(std::max(0.0f, Width() + delta));
+}
+
+void FloatRect::ShiftYEdgeTo(float edge) {
+ float delta = edge - Y();
+ SetY(edge);
+ SetHeight(std::max(0.0f, Height() - delta));
+}
+
+void FloatRect::ShiftMaxYEdgeTo(float edge) {
+ float delta = edge - MaxY();
+ SetHeight(std::max(0.0f, Height() + delta));
+}
+
+bool FloatRect::Intersects(const FloatRect& other) const {
+ // Checking emptiness handles negative widths as well as zero.
+ return !IsEmpty() && !other.IsEmpty() && X() < other.MaxX() &&
+ other.X() < MaxX() && Y() < other.MaxY() && other.Y() < MaxY();
+}
+
+bool FloatRect::Contains(const FloatRect& other) const {
+ return X() <= other.X() && MaxX() >= other.MaxX() && Y() <= other.Y() &&
+ MaxY() >= other.MaxY();
+}
+
+bool FloatRect::Contains(const FloatPoint& point,
+ ContainsMode contains_mode) const {
+ if (contains_mode == kInsideOrOnStroke)
+ return Contains(point.X(), point.Y());
+ return X() < point.X() && MaxX() > point.X() && Y() < point.Y() &&
+ MaxY() > point.Y();
+}
+
+void FloatRect::Intersect(const FloatRect& other) {
+ float left = std::max(X(), other.X());
+ float top = std::max(Y(), other.Y());
+ float right = std::min(MaxX(), other.MaxX());
+ float bottom = std::min(MaxY(), other.MaxY());
+
+ // Return a clean empty rectangle for non-intersecting cases.
+ if (left >= right || top >= bottom) {
+ left = 0;
+ top = 0;
+ right = 0;
+ bottom = 0;
+ }
+
+ SetLocationAndSizeFromEdges(left, top, right, bottom);
+}
+
+void FloatRect::Unite(const FloatRect& other) {
+ // Handle empty special cases first.
+ if (other.IsEmpty())
+ return;
+ if (IsEmpty()) {
+ *this = other;
+ return;
+ }
+
+ UniteEvenIfEmpty(other);
+}
+
+void FloatRect::UniteEvenIfEmpty(const FloatRect& other) {
+ float min_x = std::min(X(), other.X());
+ float min_y = std::min(Y(), other.Y());
+ float max_x = std::max(this->MaxX(), other.MaxX());
+ float max_y = std::max(this->MaxY(), other.MaxY());
+
+ SetLocationAndSizeFromEdges(min_x, min_y, max_x, max_y);
+}
+
+void FloatRect::UniteIfNonZero(const FloatRect& other) {
+ // Handle empty special cases first.
+ if (other.IsZero())
+ return;
+ if (IsZero()) {
+ *this = other;
+ return;
+ }
+
+ UniteEvenIfEmpty(other);
+}
+
+void FloatRect::Extend(const FloatPoint& p) {
+ float min_x = std::min(X(), p.X());
+ float min_y = std::min(Y(), p.Y());
+ float max_x = std::max(this->MaxX(), p.X());
+ float max_y = std::max(this->MaxY(), p.Y());
+
+ SetLocationAndSizeFromEdges(min_x, min_y, max_x, max_y);
+}
+
+void FloatRect::Scale(float sx, float sy) {
+ location_.SetX(X() * sx);
+ location_.SetY(Y() * sy);
+ size_.SetWidth(Width() * sx);
+ size_.SetHeight(Height() * sy);
+}
+
+float FloatRect::SquaredDistanceTo(const FloatPoint& point) const {
+ FloatPoint closest_point;
+ closest_point.SetX(clampTo<float>(point.X(), X(), MaxX()));
+ closest_point.SetY(clampTo<float>(point.Y(), Y(), MaxY()));
+ return (point - closest_point).DiagonalLengthSquared();
+}
+
+FloatRect::operator SkRect() const {
+ return SkRect::MakeXYWH(X(), Y(), Width(), Height());
+}
+
+FloatRect::operator gfx::RectF() const {
+ return gfx::RectF(X(), Y(), Width(), Height());
+}
+
+FloatRect UnionRect(const Vector<FloatRect>& rects) {
+ FloatRect result;
+
+ size_t count = rects.size();
+ for (size_t i = 0; i < count; ++i)
+ result.Unite(rects[i]);
+
+ return result;
+}
+
+IntRect EnclosedIntRect(const FloatRect& rect) {
+ IntPoint location = CeiledIntPoint(rect.Location());
+ IntPoint max_point = FlooredIntPoint(rect.MaxXMaxYCorner());
+ IntSize size(ClampSub(max_point.X(), location.X()),
+ ClampSub(max_point.Y(), location.Y()));
+ size.ClampNegativeToZero();
+ return IntRect(location, size);
+}
+
+IntRect RoundedIntRect(const FloatRect& rect) {
+ return IntRect(RoundedIntPoint(rect.Location()), RoundedIntSize(rect.Size()));
+}
+
+FloatRect MapRect(const FloatRect& r,
+ const FloatRect& src_rect,
+ const FloatRect& dest_rect) {
+ if (!src_rect.Width() || !src_rect.Height())
+ return FloatRect();
+
+ float width_scale = dest_rect.Width() / src_rect.Width();
+ float height_scale = dest_rect.Height() / src_rect.Height();
+ return FloatRect(dest_rect.X() + (r.X() - src_rect.X()) * width_scale,
+ dest_rect.Y() + (r.Y() - src_rect.Y()) * height_scale,
+ r.Width() * width_scale, r.Height() * height_scale);
+}
+
+std::ostream& operator<<(std::ostream& ostream, const FloatRect& rect) {
+ return ostream << rect.ToString();
+}
+
+String FloatRect::ToString() const {
+ return String::Format("%s %s", Location().ToString().Ascii().data(),
+ Size().ToString().Ascii().data());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/float_rect.h b/chromium/third_party/blink/renderer/platform/geometry/float_rect.h
new file mode 100644
index 00000000000..f0405c2ef40
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/float_rect.h
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2003, 2006, 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2005 Nokia. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_FLOAT_RECT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_FLOAT_RECT_H_
+
+#include <iosfwd>
+
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/geometry/float_point.h"
+#include "third_party/blink/renderer/platform/geometry/float_rect_outsets.h"
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/saturated_arithmetic.h"
+
+#if defined(OS_MACOSX)
+typedef struct CGRect CGRect;
+
+#ifdef __OBJC__
+#import <Foundation/Foundation.h>
+#endif
+#endif
+
+struct SkRect;
+
+namespace gfx {
+class RectF;
+}
+
+namespace blink {
+
+class LayoutRect;
+class LayoutSize;
+
+class PLATFORM_EXPORT FloatRect {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ enum ContainsMode { kInsideOrOnStroke, kInsideButNotOnStroke };
+
+ FloatRect() = default;
+ FloatRect(const FloatPoint& location, const FloatSize& size)
+ : location_(location), size_(size) {}
+ FloatRect(float x, float y, float width, float height)
+ : location_(FloatPoint(x, y)), size_(FloatSize(width, height)) {}
+ FloatRect(const IntRect&);
+ explicit FloatRect(const LayoutRect&);
+ FloatRect(const SkRect&);
+
+ static FloatRect NarrowPrecision(double x,
+ double y,
+ double width,
+ double height);
+
+ FloatPoint Location() const { return location_; }
+ FloatSize Size() const { return size_; }
+
+ void SetLocation(const FloatPoint& location) { location_ = location; }
+ void SetSize(const FloatSize& size) { size_ = size; }
+
+ float X() const { return location_.X(); }
+ float Y() const { return location_.Y(); }
+ float MaxX() const { return X() + Width(); }
+ float MaxY() const { return Y() + Height(); }
+ float Width() const { return size_.Width(); }
+ float Height() const { return size_.Height(); }
+
+ void SetX(float x) { location_.SetX(x); }
+ void SetY(float y) { location_.SetY(y); }
+ void SetWidth(float width) { size_.SetWidth(width); }
+ void SetHeight(float height) { size_.SetHeight(height); }
+
+ bool IsEmpty() const { return size_.IsEmpty(); }
+ bool IsZero() const { return size_.IsZero(); }
+ bool IsExpressibleAsIntRect() const;
+
+ FloatPoint Center() const {
+ return FloatPoint(X() + Width() / 2, Y() + Height() / 2);
+ }
+
+ void Move(const FloatSize& delta) { location_ += delta; }
+ void Move(const LayoutSize&);
+ void Move(const IntSize&);
+ void MoveBy(const FloatPoint& delta) { location_.Move(delta.X(), delta.Y()); }
+ void Move(float dx, float dy) { location_.Move(dx, dy); }
+
+ void Expand(const FloatSize& size) { size_ += size; }
+ void Expand(float dw, float dh) { size_.Expand(dw, dh); }
+ void Expand(const FloatRectOutsets& outsets) {
+ location_.Move(-outsets.Left(), -outsets.Top());
+ size_.Expand(outsets.Left() + outsets.Right(),
+ outsets.Top() + outsets.Bottom());
+ }
+
+ void Contract(const FloatSize& size) { size_ -= size; }
+ void Contract(float dw, float dh) { size_.Expand(-dw, -dh); }
+
+ void ShiftXEdgeTo(float);
+ void ShiftMaxXEdgeTo(float);
+ void ShiftYEdgeTo(float);
+ void ShiftMaxYEdgeTo(float);
+
+ FloatPoint MinXMinYCorner() const { return location_; } // typically topLeft
+ FloatPoint MaxXMinYCorner() const {
+ return FloatPoint(location_.X() + size_.Width(), location_.Y());
+ } // typically topRight
+ FloatPoint MinXMaxYCorner() const {
+ return FloatPoint(location_.X(), location_.Y() + size_.Height());
+ } // typically bottomLeft
+ FloatPoint MaxXMaxYCorner() const {
+ return FloatPoint(location_.X() + size_.Width(),
+ location_.Y() + size_.Height());
+ } // typically bottomRight
+
+ bool Intersects(const FloatRect&) const;
+ bool Contains(const FloatRect&) const;
+ bool Contains(const FloatPoint&, ContainsMode = kInsideOrOnStroke) const;
+
+ void Intersect(const FloatRect&);
+ void Unite(const FloatRect&);
+ void UniteEvenIfEmpty(const FloatRect&);
+ void UniteIfNonZero(const FloatRect&);
+ void Extend(const FloatPoint&);
+
+ // Note, this doesn't match what IntRect::contains(IntPoint&) does; the int
+ // version is really checking for containment of 1x1 rect, but that doesn't
+ // make sense with floats.
+ bool Contains(float px, float py) const {
+ return px >= X() && px <= MaxX() && py >= Y() && py <= MaxY();
+ }
+
+ void InflateX(float dx) {
+ location_.SetX(location_.X() - dx);
+ size_.SetWidth(size_.Width() + dx + dx);
+ }
+ void InflateY(float dy) {
+ location_.SetY(location_.Y() - dy);
+ size_.SetHeight(size_.Height() + dy + dy);
+ }
+ void Inflate(float d) {
+ InflateX(d);
+ InflateY(d);
+ }
+ void Scale(float s) { Scale(s, s); }
+ void Scale(float sx, float sy);
+
+ FloatRect TransposedRect() const {
+ return FloatRect(location_.TransposedPoint(), size_.TransposedSize());
+ }
+
+ float SquaredDistanceTo(const FloatPoint&) const;
+
+#if defined(OS_MACOSX)
+ FloatRect(const CGRect&);
+ operator CGRect() const;
+#endif
+
+ operator SkRect() const;
+ operator gfx::RectF() const;
+
+#if DCHECK_IS_ON()
+ bool MayNotHaveExactIntRectRepresentation() const;
+ bool EqualWithinEpsilon(const FloatRect& other, float epsilon) const;
+#endif
+
+ String ToString() const;
+
+ private:
+ FloatPoint location_;
+ FloatSize size_;
+
+ void SetLocationAndSizeFromEdges(float left,
+ float top,
+ float right,
+ float bottom) {
+ location_.Set(left, top);
+ size_.SetWidth(right - left);
+ size_.SetHeight(bottom - top);
+ }
+};
+
+inline FloatRect Intersection(const FloatRect& a, const FloatRect& b) {
+ FloatRect c = a;
+ c.Intersect(b);
+ return c;
+}
+
+inline FloatRect UnionRect(const FloatRect& a, const FloatRect& b) {
+ FloatRect c = a;
+ c.Unite(b);
+ return c;
+}
+
+PLATFORM_EXPORT FloatRect UnionRect(const Vector<FloatRect>&);
+
+inline FloatRect& operator+=(FloatRect& a, const FloatRect& b) {
+ a.Move(b.X(), b.Y());
+ a.SetWidth(a.Width() + b.Width());
+ a.SetHeight(a.Height() + b.Height());
+ return a;
+}
+
+inline FloatRect operator+(const FloatRect& a, const FloatRect& b) {
+ FloatRect c = a;
+ c += b;
+ return c;
+}
+
+inline bool operator==(const FloatRect& a, const FloatRect& b) {
+ return a.Location() == b.Location() && a.Size() == b.Size();
+}
+
+inline bool operator!=(const FloatRect& a, const FloatRect& b) {
+ return a.Location() != b.Location() || a.Size() != b.Size();
+}
+
+// Returns a IntRect containing the given FloatRect.
+inline IntRect EnclosingIntRect(const FloatRect& rect) {
+ IntPoint location = FlooredIntPoint(rect.Location());
+ IntPoint max_point = CeiledIntPoint(rect.MaxXMaxYCorner());
+ return IntRect(location, IntSize(ClampSub(max_point.X(), location.X()),
+ ClampSub(max_point.Y(), location.Y())));
+}
+
+// Returns a valid IntRect contained within the given FloatRect.
+PLATFORM_EXPORT IntRect EnclosedIntRect(const FloatRect&);
+
+PLATFORM_EXPORT IntRect RoundedIntRect(const FloatRect&);
+
+// Map supplied rect from srcRect to an equivalent rect in destRect.
+PLATFORM_EXPORT FloatRect MapRect(const FloatRect&,
+ const FloatRect& src_rect,
+ const FloatRect& dest_rect);
+
+PLATFORM_EXPORT std::ostream& operator<<(std::ostream&, const FloatRect&);
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/geometry/float_rect_outsets.cc b/chromium/third_party/blink/renderer/platform/geometry/float_rect_outsets.cc
new file mode 100644
index 00000000000..9d61d0913e5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/float_rect_outsets.cc
@@ -0,0 +1,19 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/geometry/float_rect_outsets.h"
+
+#include <algorithm>
+
+namespace blink {
+
+// Change outsets to be at least as large as |other|.
+void FloatRectOutsets::Unite(const FloatRectOutsets& other) {
+ top_ = std::max(top_, other.top_);
+ right_ = std::max(right_, other.right_);
+ bottom_ = std::max(bottom_, other.bottom_);
+ left_ = std::max(left_, other.left_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/float_rect_outsets.h b/chromium/third_party/blink/renderer/platform/geometry/float_rect_outsets.h
new file mode 100644
index 00000000000..6d809ad6478
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/float_rect_outsets.h
@@ -0,0 +1,49 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_FLOAT_RECT_OUTSETS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_FLOAT_RECT_OUTSETS_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+// Specifies floating-point lengths to be used to expand a rectangle.
+// For example, |top()| returns the distance the top edge should be moved
+// upward.
+//
+// Negative lengths can be used to express insets.
+class PLATFORM_EXPORT FloatRectOutsets {
+ STACK_ALLOCATED();
+
+ public:
+ FloatRectOutsets() : top_(0), right_(0), bottom_(0), left_(0) {}
+
+ FloatRectOutsets(float top, float right, float bottom, float left)
+ : top_(top), right_(right), bottom_(bottom), left_(left) {}
+
+ float Top() const { return top_; }
+ float Right() const { return right_; }
+ float Bottom() const { return bottom_; }
+ float Left() const { return left_; }
+
+ void SetTop(float top) { top_ = top; }
+ void SetRight(float right) { right_ = right; }
+ void SetBottom(float bottom) { bottom_ = bottom; }
+ void SetLeft(float left) { left_ = left; }
+
+ // Change outsets to be at least as large as |other|.
+ void Unite(const FloatRectOutsets& other);
+
+ private:
+ float top_;
+ float right_;
+ float bottom_;
+ float left_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_FLOAT_RECT_OUTSETS_H_
diff --git a/chromium/third_party/blink/renderer/platform/geometry/float_rect_test.cc b/chromium/third_party/blink/renderer/platform/geometry/float_rect_test.cc
new file mode 100644
index 00000000000..787d1a7d78b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/float_rect_test.cc
@@ -0,0 +1,260 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/geometry/float_point.h"
+#include "third_party/blink/renderer/platform/geometry/geometry_test_helpers.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+TEST(FloatRectTest, SquaredDistanceToTest) {
+ //
+ // O--x
+ // |
+ // y
+ //
+ // FloatRect.x() FloatRect.maxX()
+ // | |
+ // 1 | 2 | 3
+ // ======+==========+====== --FloatRect.y()
+ // 4 | 5(in) | 6
+ // ======+==========+====== --FloatRect.maxY()
+ // 7 | 8 | 9
+ //
+
+ FloatRect r1(100, 100, 250, 150);
+
+ // `1` case
+ FloatPoint p1(80, 80);
+ EXPECT_PRED_FORMAT2(GeometryTest::AssertAlmostEqual, r1.SquaredDistanceTo(p1),
+ 800.f);
+
+ FloatPoint p2(-10, -10);
+ EXPECT_PRED_FORMAT2(GeometryTest::AssertAlmostEqual, r1.SquaredDistanceTo(p2),
+ 24200.f);
+
+ FloatPoint p3(80, -10);
+ EXPECT_PRED_FORMAT2(GeometryTest::AssertAlmostEqual, r1.SquaredDistanceTo(p3),
+ 12500.f);
+
+ // `2` case
+ FloatPoint p4(110, 80);
+ EXPECT_PRED_FORMAT2(GeometryTest::AssertAlmostEqual, r1.SquaredDistanceTo(p4),
+ 400.f);
+
+ FloatPoint p5(150, 0);
+ EXPECT_PRED_FORMAT2(GeometryTest::AssertAlmostEqual, r1.SquaredDistanceTo(p5),
+ 10000.f);
+
+ FloatPoint p6(180, -10);
+ EXPECT_PRED_FORMAT2(GeometryTest::AssertAlmostEqual, r1.SquaredDistanceTo(p6),
+ 12100.f);
+
+ // `3` case
+ FloatPoint p7(400, 80);
+ EXPECT_PRED_FORMAT2(GeometryTest::AssertAlmostEqual, r1.SquaredDistanceTo(p7),
+ 2900.f);
+
+ FloatPoint p8(360, -10);
+ EXPECT_PRED_FORMAT2(GeometryTest::AssertAlmostEqual, r1.SquaredDistanceTo(p8),
+ 12200.f);
+
+ // `4` case
+ FloatPoint p9(80, 110);
+ EXPECT_PRED_FORMAT2(GeometryTest::AssertAlmostEqual, r1.SquaredDistanceTo(p9),
+ 400.f);
+
+ FloatPoint p10(-10, 180);
+ EXPECT_PRED_FORMAT2(GeometryTest::AssertAlmostEqual,
+ r1.SquaredDistanceTo(p10), 12100.f);
+
+ // `5`(& In) case
+ FloatPoint p11(100, 100);
+ EXPECT_PRED_FORMAT2(GeometryTest::AssertAlmostEqual,
+ r1.SquaredDistanceTo(p11), 0.f);
+
+ FloatPoint p12(150, 100);
+ EXPECT_PRED_FORMAT2(GeometryTest::AssertAlmostEqual,
+ r1.SquaredDistanceTo(p12), 0.f);
+
+ FloatPoint p13(350, 100);
+ EXPECT_PRED_FORMAT2(GeometryTest::AssertAlmostEqual,
+ r1.SquaredDistanceTo(p13), 0.f);
+
+ FloatPoint p14(350, 150);
+ EXPECT_PRED_FORMAT2(GeometryTest::AssertAlmostEqual,
+ r1.SquaredDistanceTo(p14), 0.f);
+
+ FloatPoint p15(350, 250);
+ EXPECT_PRED_FORMAT2(GeometryTest::AssertAlmostEqual,
+ r1.SquaredDistanceTo(p15), 0.f);
+
+ FloatPoint p16(150, 250);
+ EXPECT_PRED_FORMAT2(GeometryTest::AssertAlmostEqual,
+ r1.SquaredDistanceTo(p16), 0.f);
+
+ FloatPoint p17(100, 250);
+ EXPECT_PRED_FORMAT2(GeometryTest::AssertAlmostEqual,
+ r1.SquaredDistanceTo(p17), 0.f);
+
+ FloatPoint p18(100, 150);
+ EXPECT_PRED_FORMAT2(GeometryTest::AssertAlmostEqual,
+ r1.SquaredDistanceTo(p18), 0.f);
+
+ FloatPoint p19(150, 150);
+ EXPECT_PRED_FORMAT2(GeometryTest::AssertAlmostEqual,
+ r1.SquaredDistanceTo(p19), 0.f);
+
+ // `6` case
+ FloatPoint p20(380, 150);
+ EXPECT_PRED_FORMAT2(GeometryTest::AssertAlmostEqual,
+ r1.SquaredDistanceTo(p20), 900.f);
+
+ // `7` case
+ FloatPoint p21(80, 280);
+ EXPECT_PRED_FORMAT2(GeometryTest::AssertAlmostEqual,
+ r1.SquaredDistanceTo(p21), 1300.f);
+
+ FloatPoint p22(-10, 300);
+ EXPECT_PRED_FORMAT2(GeometryTest::AssertAlmostEqual,
+ r1.SquaredDistanceTo(p22), 14600.f);
+
+ // `8` case
+ FloatPoint p23(180, 300);
+ EXPECT_PRED_FORMAT2(GeometryTest::AssertAlmostEqual,
+ r1.SquaredDistanceTo(p23), 2500.f);
+
+ // `9` case
+ FloatPoint p24(450, 450);
+ EXPECT_PRED_FORMAT2(GeometryTest::AssertAlmostEqual,
+ r1.SquaredDistanceTo(p24), 50000.f);
+}
+
+TEST(FloatRectTest, ToString) {
+ FloatRect empty_rect = FloatRect();
+ EXPECT_EQ("0,0 0x0", empty_rect.ToString());
+
+ FloatRect rect(1, 2, 3, 4);
+ EXPECT_EQ("1,2 3x4", rect.ToString());
+
+ FloatRect granular_rect(1.6f, 2.7f, 3.8f, 4.9f);
+ EXPECT_EQ("1.6,2.7 3.8x4.9", granular_rect.ToString());
+
+ FloatRect min_max_rect(
+ std::numeric_limits<float>::min(), std::numeric_limits<float>::max(),
+ std::numeric_limits<float>::min(), std::numeric_limits<float>::max());
+ EXPECT_EQ("1.17549e-38,3.40282e+38 1.17549e-38x3.40282e+38",
+ min_max_rect.ToString());
+
+ FloatRect infinite_rect(-std::numeric_limits<float>::infinity(),
+ -std::numeric_limits<float>::infinity(),
+ std::numeric_limits<float>::infinity(),
+ std::numeric_limits<float>::infinity());
+ EXPECT_EQ("-inf,-inf infxinf", infinite_rect.ToString());
+
+ FloatRect nan_rect(0, 0, std::numeric_limits<float>::signaling_NaN(),
+ std::numeric_limits<float>::signaling_NaN());
+ EXPECT_EQ("0,0 nanxnan", nan_rect.ToString());
+}
+
+TEST(FloatRectTest, EnclosingIntRect) {
+ FloatRect small_dimensions_rect(42.5f, 84.5f,
+ std::numeric_limits<float>::epsilon(),
+ std::numeric_limits<float>::epsilon());
+ EXPECT_EQ(IntRect(42, 84, 1, 1), EnclosingIntRect(small_dimensions_rect));
+
+ FloatRect integral_rect(100, 150, 200, 350);
+ EXPECT_EQ(IntRect(100, 150, 200, 350), EnclosingIntRect(integral_rect));
+
+ FloatRect fractional_pos_rect(100.6f, 150.8f, 200, 350);
+ EXPECT_EQ(IntRect(100, 150, 201, 351), EnclosingIntRect(fractional_pos_rect));
+
+ FloatRect fractional_dimensions_rect(100, 150, 200.6f, 350.4f);
+ EXPECT_EQ(IntRect(100, 150, 201, 351),
+ EnclosingIntRect(fractional_dimensions_rect));
+
+ FloatRect fractional_both_rect1(100.6f, 150.8f, 200.4f, 350.2f);
+ EXPECT_EQ(IntRect(100, 150, 201, 351),
+ EnclosingIntRect(fractional_both_rect1));
+
+ FloatRect fractional_both_rect2(100.6f, 150.8f, 200.3f, 350.3f);
+ EXPECT_EQ(IntRect(100, 150, 201, 352),
+ EnclosingIntRect(fractional_both_rect2));
+
+ FloatRect fractional_both_rect3(100.6f, 150.8f, 200.5f, 350.3f);
+ EXPECT_EQ(IntRect(100, 150, 202, 352),
+ EnclosingIntRect(fractional_both_rect3));
+
+ FloatRect fractional_negpos_rect1(-100.4f, -150.8f, 200, 350);
+ EXPECT_EQ(IntRect(-101, -151, 201, 351),
+ EnclosingIntRect(fractional_negpos_rect1));
+
+ FloatRect fractional_negpos_rect2(-100.4f, -150.8f, 199.4f, 350.3f);
+ EXPECT_EQ(IntRect(-101, -151, 200, 351),
+ EnclosingIntRect(fractional_negpos_rect2));
+
+ FloatRect fractional_negpos_rect3(-100.6f, -150.8f, 199.6f, 350.3f);
+ EXPECT_EQ(IntRect(-101, -151, 201, 351),
+ EnclosingIntRect(fractional_negpos_rect3));
+
+ FloatRect max_rect(-std::numeric_limits<float>::max() / 2,
+ -std::numeric_limits<float>::max() / 2,
+ std::numeric_limits<float>::max(),
+ std::numeric_limits<float>::max());
+ EXPECT_EQ(IntRect(INT_MIN, INT_MIN, INT_MAX, INT_MAX),
+ EnclosingIntRect(max_rect));
+}
+
+TEST(FloatRectTest, EnclosedIntRect) {
+ FloatRect small_dimensions_rect(42.5f, 84.5f,
+ std::numeric_limits<float>::epsilon(),
+ std::numeric_limits<float>::epsilon());
+ EXPECT_EQ(IntRect(43, 85, 0, 0), EnclosedIntRect(small_dimensions_rect));
+
+ FloatRect integral_rect(100, 150, 200, 350);
+ EXPECT_EQ(IntRect(100, 150, 200, 350), EnclosedIntRect(integral_rect));
+
+ FloatRect fractional_pos_rect(100.6f, 150.8f, 200, 350);
+ EXPECT_EQ(IntRect(101, 151, 199, 349), EnclosedIntRect(fractional_pos_rect));
+
+ FloatRect fractional_dimensions_rect(100, 150, 200.6f, 350.4f);
+ EXPECT_EQ(IntRect(100, 150, 200, 350),
+ EnclosedIntRect(fractional_dimensions_rect));
+
+ FloatRect fractional_both_rect1(100.6f, 150.8f, 200.4f, 350.2f);
+ EXPECT_EQ(IntRect(101, 151, 200, 350),
+ EnclosedIntRect(fractional_both_rect1));
+
+ FloatRect fractional_both_rect2(100.6f, 150.8f, 200.3f, 350.3f);
+ EXPECT_EQ(IntRect(101, 151, 199, 350),
+ EnclosedIntRect(fractional_both_rect2));
+
+ FloatRect fractional_both_rect3(100.6f, 150.8f, 200.5f, 350.3f);
+ EXPECT_EQ(IntRect(101, 151, 200, 350),
+ EnclosedIntRect(fractional_both_rect3));
+
+ FloatRect fractional_negpos_rect1(-100.4f, -150.8f, 200, 350);
+ EXPECT_EQ(IntRect(-100, -150, 199, 349),
+ EnclosedIntRect(fractional_negpos_rect1));
+
+ FloatRect fractional_negpos_rect2(-100.4f, -150.8f, 199.5f, 350.3f);
+ EXPECT_EQ(IntRect(-100, -150, 199, 349),
+ EnclosedIntRect(fractional_negpos_rect2));
+
+ FloatRect fractional_negpos_rect3(-100.6f, -150.8f, 199.6f, 350.3f);
+ EXPECT_EQ(IntRect(-100, -150, 199, 349),
+ EnclosedIntRect(fractional_negpos_rect3));
+
+ FloatRect max_rect(-std::numeric_limits<float>::max() / 2,
+ -std::numeric_limits<float>::max() / 2,
+ std::numeric_limits<float>::max(),
+ std::numeric_limits<float>::max());
+ EXPECT_EQ(IntRect(INT_MIN, INT_MIN, INT_MAX, INT_MAX),
+ EnclosedIntRect(max_rect));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/float_rounded_rect.cc b/chromium/third_party/blink/renderer/platform/geometry/float_rounded_rect.cc
new file mode 100644
index 00000000000..59bcb6bd797
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/float_rounded_rect.cc
@@ -0,0 +1,394 @@
+/*
+ * Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. 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.
+ *
+ * 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 HOLDER 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 "third_party/blink/renderer/platform/geometry/float_rounded_rect.h"
+
+#include <algorithm>
+#include "third_party/blink/renderer/platform/geometry/float_quad.h"
+#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+FloatRoundedRect::FloatRoundedRect(float x, float y, float width, float height)
+ : rect_(x, y, width, height) {}
+
+FloatRoundedRect::FloatRoundedRect(const FloatRect& rect, const Radii& radii)
+ : rect_(rect), radii_(radii) {}
+
+FloatRoundedRect::FloatRoundedRect(const FloatRect& rect,
+ const FloatSize& top_left,
+ const FloatSize& top_right,
+ const FloatSize& bottom_left,
+ const FloatSize& bottom_right)
+ : rect_(rect), radii_(top_left, top_right, bottom_left, bottom_right) {}
+
+void FloatRoundedRect::Radii::Scale(float factor) {
+ if (factor == 1)
+ return;
+
+ // If either radius on a corner becomes zero, reset both radii on that corner.
+ top_left_.Scale(factor);
+ if (!top_left_.Width() || !top_left_.Height())
+ top_left_ = FloatSize();
+ top_right_.Scale(factor);
+ if (!top_right_.Width() || !top_right_.Height())
+ top_right_ = FloatSize();
+ bottom_left_.Scale(factor);
+ if (!bottom_left_.Width() || !bottom_left_.Height())
+ bottom_left_ = FloatSize();
+ bottom_right_.Scale(factor);
+ if (!bottom_right_.Width() || !bottom_right_.Height())
+ bottom_right_ = FloatSize();
+}
+
+void FloatRoundedRect::Radii::ScaleAndFloor(float factor) {
+ if (factor == 1)
+ return;
+
+ // If either radius on a corner becomes zero, reset both radii on that corner.
+ top_left_.ScaleAndFloor(factor);
+ if (!top_left_.Width() || !top_left_.Height())
+ top_left_ = FloatSize();
+ top_right_.ScaleAndFloor(factor);
+ if (!top_right_.Width() || !top_right_.Height())
+ top_right_ = FloatSize();
+ bottom_left_.ScaleAndFloor(factor);
+ if (!bottom_left_.Width() || !bottom_left_.Height())
+ bottom_left_ = FloatSize();
+ bottom_right_.ScaleAndFloor(factor);
+ if (!bottom_right_.Width() || !bottom_right_.Height())
+ bottom_right_ = FloatSize();
+}
+
+void FloatRoundedRect::Radii::Shrink(float top_width,
+ float bottom_width,
+ float left_width,
+ float right_width) {
+ DCHECK_GE(top_width, 0);
+ DCHECK_GE(bottom_width, 0);
+ DCHECK_GE(left_width, 0);
+ DCHECK_GE(right_width, 0);
+
+ top_left_.SetWidth(std::max<float>(0, top_left_.Width() - left_width));
+ top_left_.SetHeight(std::max<float>(0, top_left_.Height() - top_width));
+
+ top_right_.SetWidth(std::max<float>(0, top_right_.Width() - right_width));
+ top_right_.SetHeight(std::max<float>(0, top_right_.Height() - top_width));
+
+ bottom_left_.SetWidth(std::max<float>(0, bottom_left_.Width() - left_width));
+ bottom_left_.SetHeight(
+ std::max<float>(0, bottom_left_.Height() - bottom_width));
+
+ bottom_right_.SetWidth(
+ std::max<float>(0, bottom_right_.Width() - right_width));
+ bottom_right_.SetHeight(
+ std::max<float>(0, bottom_right_.Height() - bottom_width));
+}
+
+void FloatRoundedRect::Radii::Expand(float top_width,
+ float bottom_width,
+ float left_width,
+ float right_width) {
+ DCHECK_GE(top_width, 0);
+ DCHECK_GE(bottom_width, 0);
+ DCHECK_GE(left_width, 0);
+ DCHECK_GE(right_width, 0);
+ if (top_left_.Width() > 0 && top_left_.Height() > 0) {
+ top_left_.SetWidth(top_left_.Width() + left_width);
+ top_left_.SetHeight(top_left_.Height() + top_width);
+ }
+ if (top_right_.Width() > 0 && top_right_.Height() > 0) {
+ top_right_.SetWidth(top_right_.Width() + right_width);
+ top_right_.SetHeight(top_right_.Height() + top_width);
+ }
+ if (bottom_left_.Width() > 0 && bottom_left_.Height() > 0) {
+ bottom_left_.SetWidth(bottom_left_.Width() + left_width);
+ bottom_left_.SetHeight(bottom_left_.Height() + bottom_width);
+ }
+ if (bottom_right_.Width() > 0 && bottom_right_.Height() > 0) {
+ bottom_right_.SetWidth(bottom_right_.Width() + right_width);
+ bottom_right_.SetHeight(bottom_right_.Height() + bottom_width);
+ }
+}
+
+static inline float CornerRectIntercept(float y, const FloatRect& corner_rect) {
+ DCHECK_GT(corner_rect.Height(), 0);
+ return corner_rect.Width() *
+ sqrt(1 - (y * y) / (corner_rect.Height() * corner_rect.Height()));
+}
+
+FloatRect FloatRoundedRect::RadiusCenterRect() const {
+ FloatRectOutsets maximum_radius_insets(
+ -std::max(radii_.TopLeft().Height(), radii_.TopRight().Height()),
+ -std::max(radii_.TopRight().Width(), radii_.BottomRight().Width()),
+ -std::max(radii_.BottomLeft().Height(), radii_.BottomRight().Height()),
+ -std::max(radii_.TopLeft().Width(), radii_.BottomLeft().Width()));
+ FloatRect center_rect(rect_);
+ center_rect.Expand(maximum_radius_insets);
+ return center_rect;
+}
+
+bool FloatRoundedRect::XInterceptsAtY(float y,
+ float& min_x_intercept,
+ float& max_x_intercept) const {
+ if (y < Rect().Y() || y > Rect().MaxY())
+ return false;
+
+ if (!IsRounded()) {
+ min_x_intercept = Rect().X();
+ max_x_intercept = Rect().MaxX();
+ return true;
+ }
+
+ const FloatRect& top_left_rect = TopLeftCorner();
+ const FloatRect& bottom_left_rect = BottomLeftCorner();
+
+ if (!top_left_rect.IsEmpty() && y >= top_left_rect.Y() &&
+ y < top_left_rect.MaxY())
+ min_x_intercept =
+ top_left_rect.MaxX() -
+ CornerRectIntercept(top_left_rect.MaxY() - y, top_left_rect);
+ else if (!bottom_left_rect.IsEmpty() && y >= bottom_left_rect.Y() &&
+ y <= bottom_left_rect.MaxY())
+ min_x_intercept =
+ bottom_left_rect.MaxX() -
+ CornerRectIntercept(y - bottom_left_rect.Y(), bottom_left_rect);
+ else
+ min_x_intercept = rect_.X();
+
+ const FloatRect& top_right_rect = TopRightCorner();
+ const FloatRect& bottom_right_rect = BottomRightCorner();
+
+ if (!top_right_rect.IsEmpty() && y >= top_right_rect.Y() &&
+ y <= top_right_rect.MaxY())
+ max_x_intercept =
+ top_right_rect.X() +
+ CornerRectIntercept(top_right_rect.MaxY() - y, top_right_rect);
+ else if (!bottom_right_rect.IsEmpty() && y >= bottom_right_rect.Y() &&
+ y <= bottom_right_rect.MaxY())
+ max_x_intercept =
+ bottom_right_rect.X() +
+ CornerRectIntercept(y - bottom_right_rect.Y(), bottom_right_rect);
+ else
+ max_x_intercept = rect_.MaxX();
+
+ return true;
+}
+
+void FloatRoundedRect::InflateWithRadii(int size) {
+ FloatRect old = rect_;
+
+ rect_.Inflate(size);
+ // Considering the inflation factor of shorter size to scale the radii seems
+ // appropriate here
+ float factor;
+ if (rect_.Width() < rect_.Height())
+ factor = old.Width() ? (float)rect_.Width() / old.Width() : int(0);
+ else
+ factor = old.Height() ? (float)rect_.Height() / old.Height() : int(0);
+
+ radii_.Scale(factor);
+}
+
+bool FloatRoundedRect::IntersectsQuad(const FloatQuad& quad) const {
+ if (!quad.IntersectsRect(rect_))
+ return false;
+
+ const FloatSize& top_left = radii_.TopLeft();
+ if (!top_left.IsEmpty()) {
+ FloatRect rect(rect_.X(), rect_.Y(), top_left.Width(), top_left.Height());
+ if (quad.IntersectsRect(rect)) {
+ FloatPoint center(rect_.X() + top_left.Width(),
+ rect_.Y() + top_left.Height());
+ FloatSize size(top_left.Width(), top_left.Height());
+ if (!quad.IntersectsEllipse(center, size))
+ return false;
+ }
+ }
+
+ const FloatSize& top_right = radii_.TopRight();
+ if (!top_right.IsEmpty()) {
+ FloatRect rect(rect_.MaxX() - top_right.Width(), rect_.Y(),
+ top_right.Width(), top_right.Height());
+ if (quad.IntersectsRect(rect)) {
+ FloatPoint center(rect_.MaxX() - top_right.Width(),
+ rect_.Y() + top_right.Height());
+ FloatSize size(top_right.Width(), top_right.Height());
+ if (!quad.IntersectsEllipse(center, size))
+ return false;
+ }
+ }
+
+ const FloatSize& bottom_left = radii_.BottomLeft();
+ if (!bottom_left.IsEmpty()) {
+ FloatRect rect(rect_.X(), rect_.MaxY() - bottom_left.Height(),
+ bottom_left.Width(), bottom_left.Height());
+ if (quad.IntersectsRect(rect)) {
+ FloatPoint center(rect_.X() + bottom_left.Width(),
+ rect_.MaxY() - bottom_left.Height());
+ FloatSize size(bottom_left.Width(), bottom_left.Height());
+ if (!quad.IntersectsEllipse(center, size))
+ return false;
+ }
+ }
+
+ const FloatSize& bottom_right = radii_.BottomRight();
+ if (!bottom_right.IsEmpty()) {
+ FloatRect rect(rect_.MaxX() - bottom_right.Width(),
+ rect_.MaxY() - bottom_right.Height(), bottom_right.Width(),
+ bottom_right.Height());
+ if (quad.IntersectsRect(rect)) {
+ FloatPoint center(rect_.MaxX() - bottom_right.Width(),
+ rect_.MaxY() - bottom_right.Height());
+ FloatSize size(bottom_right.Width(), bottom_right.Height());
+ if (!quad.IntersectsEllipse(center, size))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void FloatRoundedRect::Radii::IncludeLogicalEdges(
+ const FloatRoundedRect::Radii& edges,
+ bool is_horizontal,
+ bool include_logical_left_edge,
+ bool include_logical_right_edge) {
+ if (include_logical_left_edge) {
+ if (is_horizontal)
+ bottom_left_ = edges.BottomLeft();
+ else
+ top_right_ = edges.TopRight();
+ top_left_ = edges.TopLeft();
+ }
+
+ if (include_logical_right_edge) {
+ if (is_horizontal)
+ top_right_ = edges.TopRight();
+ else
+ bottom_left_ = edges.BottomLeft();
+ bottom_right_ = edges.BottomRight();
+ }
+}
+
+float CalcBorderRadiiConstraintScaleFor(const FloatRect& rect,
+ const FloatRoundedRect::Radii& radii) {
+ float factor = 1;
+ float radii_sum;
+
+ // top
+ radii_sum = radii.TopLeft().Width() +
+ radii.TopRight().Width(); // Casts to avoid integer overflow.
+ if (radii_sum > rect.Width())
+ factor = std::min(rect.Width() / radii_sum, factor);
+
+ // bottom
+ radii_sum = radii.BottomLeft().Width() + radii.BottomRight().Width();
+ if (radii_sum > rect.Width())
+ factor = std::min(rect.Width() / radii_sum, factor);
+
+ // left
+ radii_sum = radii.TopLeft().Height() + radii.BottomLeft().Height();
+ if (radii_sum > rect.Height())
+ factor = std::min(rect.Height() / radii_sum, factor);
+
+ // right
+ radii_sum = radii.TopRight().Height() + radii.BottomRight().Height();
+ if (radii_sum > rect.Height())
+ factor = std::min(rect.Height() / radii_sum, factor);
+
+ DCHECK_LE(factor, 1);
+ return factor;
+}
+
+void FloatRoundedRect::ConstrainRadii() {
+ radii_.ScaleAndFloor(CalcBorderRadiiConstraintScaleFor(Rect(), GetRadii()));
+}
+
+void FloatRoundedRect::IncludeLogicalEdges(const Radii& edges,
+ bool is_horizontal,
+ bool include_logical_left_edge,
+ bool include_logical_right_edge) {
+ radii_.IncludeLogicalEdges(edges, is_horizontal, include_logical_left_edge,
+ include_logical_right_edge);
+}
+
+bool FloatRoundedRect::IsRenderable() const {
+ // FIXME: remove the 0.0001 slop once this class is converted to layout units.
+ return radii_.TopLeft().Width() + radii_.TopRight().Width() <=
+ rect_.Width() + 0.0001 &&
+ radii_.BottomLeft().Width() + radii_.BottomRight().Width() <=
+ rect_.Width() + 0.0001 &&
+ radii_.TopLeft().Height() + radii_.BottomLeft().Height() <=
+ rect_.Height() + 0.0001 &&
+ radii_.TopRight().Height() + radii_.BottomRight().Height() <=
+ rect_.Height() + 0.0001;
+}
+
+void FloatRoundedRect::AdjustRadii() {
+ float max_radius_width =
+ std::max(radii_.TopLeft().Width() + radii_.TopRight().Width(),
+ radii_.BottomLeft().Width() + radii_.BottomRight().Width());
+ float max_radius_height =
+ std::max(radii_.TopLeft().Height() + radii_.BottomLeft().Height(),
+ radii_.TopRight().Height() + radii_.BottomRight().Height());
+
+ if (max_radius_width <= 0 || max_radius_height <= 0) {
+ radii_.Scale(0.0f);
+ return;
+ }
+ float width_ratio = static_cast<float>(rect_.Width()) / max_radius_width;
+ float height_ratio = static_cast<float>(rect_.Height()) / max_radius_height;
+ radii_.Scale(width_ratio < height_ratio ? width_ratio : height_ratio);
+}
+
+std::ostream& operator<<(std::ostream& ostream, const FloatRoundedRect& rect) {
+ return ostream << rect.ToString();
+}
+
+std::ostream& operator<<(std::ostream& ostream,
+ const FloatRoundedRect::Radii& radii) {
+ return ostream << radii.ToString();
+}
+
+String FloatRoundedRect::Radii::ToString() const {
+ return "tl:" + TopLeft().ToString() + "; tr:" + TopRight().ToString() +
+ "; bl:" + BottomLeft().ToString() + "; br:" + BottomRight().ToString();
+}
+
+String FloatRoundedRect::ToString() const {
+ if (Rect() == FloatRect(LayoutRect::InfiniteIntRect()))
+ return "InfiniteIntRect";
+ if (GetRadii().IsZero())
+ return Rect().ToString();
+ return Rect().ToString() + " radii:(" + GetRadii().ToString() + ")";
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/float_rounded_rect.h b/chromium/third_party/blink/renderer/platform/geometry/float_rounded_rect.h
new file mode 100644
index 00000000000..13aef593cca
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/float_rounded_rect.h
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. 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.
+ *
+ * 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 HOLDER 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_FLOAT_ROUNDED_RECT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_FLOAT_ROUNDED_RECT_H_
+
+#include <iosfwd>
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/geometry/float_size.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/skia/include/core/SkRRect.h"
+
+namespace blink {
+
+class FloatQuad;
+
+class PLATFORM_EXPORT FloatRoundedRect {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ class PLATFORM_EXPORT Radii {
+ DISALLOW_NEW();
+
+ public:
+ Radii() = default;
+ Radii(const FloatSize& top_left,
+ const FloatSize& top_right,
+ const FloatSize& bottom_left,
+ const FloatSize& bottom_right)
+ : top_left_(top_left),
+ top_right_(top_right),
+ bottom_left_(bottom_left),
+ bottom_right_(bottom_right) {}
+
+ Radii(const FloatRoundedRect::Radii& int_radii)
+ : top_left_(int_radii.TopLeft()),
+ top_right_(int_radii.TopRight()),
+ bottom_left_(int_radii.BottomLeft()),
+ bottom_right_(int_radii.BottomRight()) {}
+
+ void SetTopLeft(const FloatSize& size) { top_left_ = size; }
+ void SetTopRight(const FloatSize& size) { top_right_ = size; }
+ void SetBottomLeft(const FloatSize& size) { bottom_left_ = size; }
+ void SetBottomRight(const FloatSize& size) { bottom_right_ = size; }
+ const FloatSize& TopLeft() const { return top_left_; }
+ const FloatSize& TopRight() const { return top_right_; }
+ const FloatSize& BottomLeft() const { return bottom_left_; }
+ const FloatSize& BottomRight() const { return bottom_right_; }
+
+ bool IsZero() const {
+ return top_left_.IsZero() && top_right_.IsZero() &&
+ bottom_left_.IsZero() && bottom_right_.IsZero();
+ }
+
+ void Scale(float factor);
+ // Multiply all radii by |factor| and floor the result to the nearest
+ // integer.
+ void ScaleAndFloor(float factor);
+
+ void Expand(float top_width,
+ float bottom_width,
+ float left_width,
+ float right_width);
+ void Expand(float size) { Expand(size, size, size, size); }
+
+ void Shrink(float top_width,
+ float bottom_width,
+ float left_width,
+ float right_width);
+ void Shrink(float size) { Shrink(size, size, size, size); }
+
+ void IncludeLogicalEdges(const Radii& edges,
+ bool is_horizontal,
+ bool include_logical_left_edge,
+ bool include_logical_right_edge);
+
+ String ToString() const;
+
+ private:
+ FloatSize top_left_;
+ FloatSize top_right_;
+ FloatSize bottom_left_;
+ FloatSize bottom_right_;
+ };
+
+ FloatRoundedRect() = default;
+ explicit FloatRoundedRect(const FloatRect&, const Radii& = Radii());
+ FloatRoundedRect(float x, float y, float width, float height);
+ FloatRoundedRect(const FloatRect&,
+ const FloatSize& top_left,
+ const FloatSize& top_right,
+ const FloatSize& bottom_left,
+ const FloatSize& bottom_right);
+
+ const FloatRect& Rect() const { return rect_; }
+ const Radii& GetRadii() const { return radii_; }
+ bool IsRounded() const { return !radii_.IsZero(); }
+ bool IsEmpty() const { return rect_.IsEmpty(); }
+
+ void SetRect(const FloatRect& rect) { rect_ = rect; }
+ void SetRadii(const Radii& radii) { radii_ = radii; }
+
+ void Move(const FloatSize& size) { rect_.Move(size); }
+ void InflateWithRadii(int size);
+ void Inflate(float size) { rect_.Inflate(size); }
+
+ // expandRadii() does not have any effect on corner radii which have zero
+ // width or height. This is because the process of expanding the radius of a
+ // corner is not allowed to make sharp corners non-sharp. This applies when
+ // "spreading" a shadow or a box shape.
+ void ExpandRadii(float size) { radii_.Expand(size); }
+ void ShrinkRadii(float size) { radii_.Shrink(size); }
+
+ // Returns a quickly computed rect enclosed by the rounded rect.
+ FloatRect RadiusCenterRect() const;
+
+ FloatRect TopLeftCorner() const {
+ return FloatRect(rect_.X(), rect_.Y(), radii_.TopLeft().Width(),
+ radii_.TopLeft().Height());
+ }
+ FloatRect TopRightCorner() const {
+ return FloatRect(rect_.MaxX() - radii_.TopRight().Width(), rect_.Y(),
+ radii_.TopRight().Width(), radii_.TopRight().Height());
+ }
+ FloatRect BottomLeftCorner() const {
+ return FloatRect(rect_.X(), rect_.MaxY() - radii_.BottomLeft().Height(),
+ radii_.BottomLeft().Width(), radii_.BottomLeft().Height());
+ }
+ FloatRect BottomRightCorner() const {
+ return FloatRect(rect_.MaxX() - radii_.BottomRight().Width(),
+ rect_.MaxY() - radii_.BottomRight().Height(),
+ radii_.BottomRight().Width(),
+ radii_.BottomRight().Height());
+ }
+
+ bool XInterceptsAtY(float y,
+ float& min_x_intercept,
+ float& max_x_intercept) const;
+
+ void IncludeLogicalEdges(const Radii& edges,
+ bool is_horizontal,
+ bool include_logical_left_edge,
+ bool include_logical_right_edge);
+
+ // Tests whether the quad intersects any part of this rounded rectangle.
+ // This only works for convex quads.
+ // This intersection is edge-inclusive and will return true even if the
+ // intersecting area is empty (i.e., the intersection is a line or a point).
+ bool IntersectsQuad(const FloatQuad&) const;
+
+ void AdjustRadii();
+ bool IsRenderable() const;
+
+ // Constrains the radii to be no more than the size of rect(); radii outside
+ // of this range are not defined. In addition, the radii of the corners are
+ // floored to the nearest integer.
+ // FIXME: the flooring should not be necessary. At the moment it causes
+ // background bleed in some cases.
+ // FIXME: this code is almost the same as adjustRadii()/isRenderable(). Get
+ // rid of one of them.
+ void ConstrainRadii();
+
+ operator SkRRect() const;
+
+ String ToString() const;
+
+ private:
+ FloatRect rect_;
+ Radii radii_;
+};
+
+inline FloatRoundedRect::operator SkRRect() const {
+ SkRRect rrect;
+
+ if (IsRounded()) {
+ SkVector radii[4];
+ radii[SkRRect::kUpperLeft_Corner].set(TopLeftCorner().Width(),
+ TopLeftCorner().Height());
+ radii[SkRRect::kUpperRight_Corner].set(TopRightCorner().Width(),
+ TopRightCorner().Height());
+ radii[SkRRect::kLowerRight_Corner].set(BottomRightCorner().Width(),
+ BottomRightCorner().Height());
+ radii[SkRRect::kLowerLeft_Corner].set(BottomLeftCorner().Width(),
+ BottomLeftCorner().Height());
+
+ rrect.setRectRadii(Rect(), radii);
+ } else {
+ rrect.setRect(Rect());
+ }
+
+ return rrect;
+}
+
+inline bool operator==(const FloatRoundedRect::Radii& a,
+ const FloatRoundedRect::Radii& b) {
+ return a.TopLeft() == b.TopLeft() && a.TopRight() == b.TopRight() &&
+ a.BottomLeft() == b.BottomLeft() && a.BottomRight() == b.BottomRight();
+}
+
+inline bool operator!=(const FloatRoundedRect::Radii& a,
+ const FloatRoundedRect::Radii& b) {
+ return !(a == b);
+}
+
+inline bool operator==(const FloatRoundedRect& a, const FloatRoundedRect& b) {
+ return a.Rect() == b.Rect() && a.GetRadii() == b.GetRadii();
+}
+
+inline bool operator!=(const FloatRoundedRect& a, const FloatRoundedRect& b) {
+ return !(a == b);
+}
+
+PLATFORM_EXPORT std::ostream& operator<<(std::ostream&,
+ const FloatRoundedRect&);
+PLATFORM_EXPORT std::ostream& operator<<(std::ostream&,
+ const FloatRoundedRect::Radii&);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_FLOAT_ROUNDED_RECT_H_
diff --git a/chromium/third_party/blink/renderer/platform/geometry/float_rounded_rect_test.cc b/chromium/third_party/blink/renderer/platform/geometry/float_rounded_rect_test.cc
new file mode 100644
index 00000000000..606e1679ed4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/float_rounded_rect_test.cc
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. 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.
+ *
+ * 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 HOLDER 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 "third_party/blink/renderer/platform/geometry/float_rounded_rect.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/geometry/float_quad.h"
+#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+#define TEST_INTERCEPTS(roundedRect, yCoordinate, expectedMinXIntercept, \
+ expectedMaxXIntercept) \
+ { \
+ float min_x_intercept; \
+ float max_x_intercept; \
+ EXPECT_TRUE(roundedRect.XInterceptsAtY(yCoordinate, min_x_intercept, \
+ max_x_intercept)); \
+ EXPECT_FLOAT_EQ(expectedMinXIntercept, min_x_intercept); \
+ EXPECT_FLOAT_EQ(expectedMaxXIntercept, max_x_intercept); \
+ }
+
+TEST(FloatRoundedRectTest, zeroRadii) {
+ FloatRoundedRect r = FloatRoundedRect(1, 2, 3, 4);
+
+ EXPECT_EQ(FloatRect(1, 2, 3, 4), r.Rect());
+ EXPECT_EQ(FloatSize(), r.GetRadii().TopLeft());
+ EXPECT_EQ(FloatSize(), r.GetRadii().TopRight());
+ EXPECT_EQ(FloatSize(), r.GetRadii().BottomLeft());
+ EXPECT_EQ(FloatSize(), r.GetRadii().BottomRight());
+ EXPECT_TRUE(r.GetRadii().IsZero());
+ EXPECT_FALSE(r.IsRounded());
+ EXPECT_FALSE(r.IsEmpty());
+
+ EXPECT_EQ(FloatRect(1, 2, 0, 0), r.TopLeftCorner());
+ EXPECT_EQ(FloatRect(4, 2, 0, 0), r.TopRightCorner());
+ EXPECT_EQ(FloatRect(4, 6, 0, 0), r.BottomRightCorner());
+ EXPECT_EQ(FloatRect(1, 6, 0, 0), r.BottomLeftCorner());
+
+ TEST_INTERCEPTS(r, 2, r.Rect().X(), r.Rect().MaxX());
+ TEST_INTERCEPTS(r, 4, r.Rect().X(), r.Rect().MaxX());
+ TEST_INTERCEPTS(r, 6, r.Rect().X(), r.Rect().MaxX());
+
+ float min_x_intercept;
+ float max_x_intercept;
+
+ EXPECT_FALSE(r.XInterceptsAtY(1, min_x_intercept, max_x_intercept));
+ EXPECT_FALSE(r.XInterceptsAtY(7, min_x_intercept, max_x_intercept));
+
+ // The FloatRoundedRect::expandRadii() function doesn't change radii
+ // FloatSizes that are <= zero. Same as RoundedRect::expandRadii().
+ r.ExpandRadii(20);
+ r.ShrinkRadii(10);
+ EXPECT_TRUE(r.GetRadii().IsZero());
+}
+
+TEST(FloatRoundedRectTest, circle) {
+ FloatSize corner_radii(50, 50);
+ FloatRoundedRect r(FloatRect(0, 0, 100, 100), corner_radii, corner_radii,
+ corner_radii, corner_radii);
+
+ EXPECT_EQ(FloatRect(0, 0, 100, 100), r.Rect());
+ EXPECT_EQ(corner_radii, r.GetRadii().TopLeft());
+ EXPECT_EQ(corner_radii, r.GetRadii().TopRight());
+ EXPECT_EQ(corner_radii, r.GetRadii().BottomLeft());
+ EXPECT_EQ(corner_radii, r.GetRadii().BottomRight());
+ EXPECT_FALSE(r.GetRadii().IsZero());
+ EXPECT_TRUE(r.IsRounded());
+ EXPECT_FALSE(r.IsEmpty());
+
+ EXPECT_EQ(FloatRect(0, 0, 50, 50), r.TopLeftCorner());
+ EXPECT_EQ(FloatRect(50, 0, 50, 50), r.TopRightCorner());
+ EXPECT_EQ(FloatRect(0, 50, 50, 50), r.BottomLeftCorner());
+ EXPECT_EQ(FloatRect(50, 50, 50, 50), r.BottomRightCorner());
+
+ TEST_INTERCEPTS(r, 0, 50, 50);
+ TEST_INTERCEPTS(r, 25, 6.69873, 93.3013);
+ TEST_INTERCEPTS(r, 50, 0, 100);
+ TEST_INTERCEPTS(r, 75, 6.69873, 93.3013);
+ TEST_INTERCEPTS(r, 100, 50, 50);
+
+ float min_x_intercept;
+ float max_x_intercept;
+
+ EXPECT_FALSE(r.XInterceptsAtY(-1, min_x_intercept, max_x_intercept));
+ EXPECT_FALSE(r.XInterceptsAtY(101, min_x_intercept, max_x_intercept));
+}
+
+/*
+ * FloatRoundedRect geometry for this test. Corner radii are in parens, x and y
+ * intercepts for the elliptical corners are noted. The rectangle itself is at
+ * 0,0 with width and height 100.
+ *
+ * (10, 15) x=10 x=90 (10, 20)
+ * (--+---------+--)
+ * y=15 +--| |-+ y=20
+ * | |
+ * | |
+ * y=85 + -| |- + y=70
+ * (--+---------+--)
+ * (25, 15) x=25 x=80 (20, 30)
+ */
+TEST(FloatRoundedRectTest, ellipticalCorners) {
+ FloatRoundedRect::Radii corner_radii;
+ corner_radii.SetTopLeft(FloatSize(10, 15));
+ corner_radii.SetTopRight(FloatSize(10, 20));
+ corner_radii.SetBottomLeft(FloatSize(25, 15));
+ corner_radii.SetBottomRight(FloatSize(20, 30));
+
+ FloatRoundedRect r(FloatRect(0, 0, 100, 100), corner_radii);
+
+ EXPECT_EQ(r.GetRadii(),
+ FloatRoundedRect::Radii(FloatSize(10, 15), FloatSize(10, 20),
+ FloatSize(25, 15), FloatSize(20, 30)));
+ EXPECT_EQ(r, FloatRoundedRect(FloatRect(0, 0, 100, 100), corner_radii));
+
+ EXPECT_EQ(FloatRect(0, 0, 10, 15), r.TopLeftCorner());
+ EXPECT_EQ(FloatRect(90, 0, 10, 20), r.TopRightCorner());
+ EXPECT_EQ(FloatRect(0, 85, 25, 15), r.BottomLeftCorner());
+ EXPECT_EQ(FloatRect(80, 70, 20, 30), r.BottomRightCorner());
+
+ TEST_INTERCEPTS(r, 5, 2.5464401, 96.61438);
+ TEST_INTERCEPTS(r, 15, 0, 99.682457);
+ TEST_INTERCEPTS(r, 20, 0, 100);
+ TEST_INTERCEPTS(r, 50, 0, 100);
+ TEST_INTERCEPTS(r, 70, 0, 100);
+ TEST_INTERCEPTS(r, 85, 0, 97.320511);
+ TEST_INTERCEPTS(r, 95, 6.3661003, 91.05542);
+
+ float min_x_intercept;
+ float max_x_intercept;
+
+ EXPECT_FALSE(r.XInterceptsAtY(-1, min_x_intercept, max_x_intercept));
+ EXPECT_FALSE(r.XInterceptsAtY(101, min_x_intercept, max_x_intercept));
+}
+
+TEST(FloatRoundedRectTest, radiusCenterRect) {
+ FloatSize corner_rect(10, 10);
+ FloatRoundedRect r0(FloatRect(0, 0, 100, 50),
+ FloatRoundedRect::Radii(corner_rect, corner_rect,
+ corner_rect, corner_rect));
+ EXPECT_EQ(FloatRect(10, 10, 80, 30), r0.RadiusCenterRect());
+
+ // "Degenerate" cases all return an empty rectangle.
+ FloatRect collapsed_rect(0, 0, 100, 50);
+ collapsed_rect.Expand(FloatRectOutsets(-200, -200, -200, -200));
+ FloatRoundedRect r1(collapsed_rect);
+ EXPECT_TRUE(r1.RadiusCenterRect().IsEmpty());
+
+ FloatRoundedRect::Radii radii_with_too_large_corner(
+ FloatSize(55, 55), FloatSize(), FloatSize(), FloatSize());
+ FloatRoundedRect r2(FloatRect(0, 0, 100, 50), radii_with_too_large_corner);
+ EXPECT_TRUE(r2.RadiusCenterRect().IsEmpty());
+}
+
+TEST(FloatRoundedRectTest, IntersectsQuadIsInclusive) {
+ FloatRoundedRect::Radii corner_radii;
+ corner_radii.SetTopLeft(FloatSize(5, 5));
+ corner_radii.SetTopRight(FloatSize(5, 5));
+ corner_radii.SetBottomLeft(FloatSize(5, 5));
+ corner_radii.SetBottomRight(FloatSize(5, 5));
+ // A rect at (10, 10) with dimensions 20x20 and radii of size 5x5.
+ FloatRoundedRect r(FloatRect(10, 10, 20, 20), corner_radii);
+
+ // A quad fully inside the rounded rect should intersect.
+ EXPECT_TRUE(r.IntersectsQuad(FloatQuad(FloatRect(11, 11, 8, 8))));
+
+ // A quad fully outside the rounded rect should not intersect.
+ EXPECT_FALSE(r.IntersectsQuad(FloatQuad(FloatRect(0, 0, 1, 1))));
+
+ // A quad touching the top edge of the rounded rect should intersect.
+ EXPECT_TRUE(r.IntersectsQuad(FloatQuad(FloatRect(15, 9, 5, 1))));
+
+ // A quad touching the right edge of the rounded rect should intersect.
+ EXPECT_TRUE(r.IntersectsQuad(FloatQuad(FloatRect(30, 15, 1, 1))));
+
+ // A quad touching the bottom edge of the rounded rect should intersect.
+ EXPECT_TRUE(r.IntersectsQuad(FloatQuad(FloatRect(15, 30, 1, 1))));
+
+ // A quad touching the left edge of the rounded rect should intersect.
+ EXPECT_TRUE(r.IntersectsQuad(FloatQuad(FloatRect(9, 15, 1, 1))));
+
+ // A quad outside the top-left arc should not intersect.
+ EXPECT_FALSE(r.IntersectsQuad(FloatQuad(FloatRect(10, 10, 1, 1))));
+
+ // A quad inside the top-left arc should intersect.
+ EXPECT_TRUE(r.IntersectsQuad(FloatQuad(FloatRect(13, 13, 1, 1))));
+
+ // A quad outside the top-right arc should not intersect.
+ EXPECT_FALSE(r.IntersectsQuad(FloatQuad(FloatRect(29, 10, 1, 1))));
+
+ // A quad inside the top-right arc should intersect.
+ EXPECT_TRUE(r.IntersectsQuad(FloatQuad(FloatRect(26, 13, 1, 1))));
+
+ // A quad outside the bottom-right arc should not intersect.
+ EXPECT_FALSE(r.IntersectsQuad(FloatQuad(FloatRect(29, 29, 1, 1))));
+
+ // A quad inside the bottom-right arc should intersect.
+ EXPECT_TRUE(r.IntersectsQuad(FloatQuad(FloatRect(26, 26, 1, 1))));
+
+ // A quad outside the bottom-left arc should not intersect.
+ EXPECT_FALSE(r.IntersectsQuad(FloatQuad(FloatRect(10, 29, 1, 1))));
+
+ // A quad inside the bottom-left arc should intersect.
+ EXPECT_TRUE(r.IntersectsQuad(FloatQuad(FloatRect(13, 26, 1, 1))));
+}
+
+TEST(FloatRoundedRectTest, ToString) {
+ FloatSize corner_rect(1, 2);
+ FloatRoundedRect rounded_rect(
+ FloatRect(3, 5, 7, 11),
+ FloatRoundedRect::Radii(corner_rect, corner_rect, corner_rect,
+ corner_rect));
+ EXPECT_EQ("3,5 7x11 radii:(tl:1x2; tr:1x2; bl:1x2; br:1x2)",
+ rounded_rect.ToString());
+
+ FloatRoundedRect infinite((FloatRect(LayoutRect::InfiniteIntRect())));
+ EXPECT_EQ("InfiniteIntRect", infinite.ToString());
+
+ FloatRoundedRect rect_without_radii(FloatRect(1, 3, 5, 7));
+ EXPECT_EQ("1,3 5x7", rect_without_radii.ToString());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/float_shape_helpers.h b/chromium/third_party/blink/renderer/platform/geometry/float_shape_helpers.h
new file mode 100644
index 00000000000..ceb200227f5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/float_shape_helpers.h
@@ -0,0 +1,18 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_FLOAT_SHAPE_HELPERS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_FLOAT_SHAPE_HELPERS_H_
+
+// Code that is useful for both FloatPolygon and FloatQuad.
+
+#include "third_party/blink/renderer/platform/geometry/float_size.h"
+
+namespace blink {
+inline float Determinant(const FloatSize& a, const FloatSize& b) {
+ return a.Width() * b.Height() - a.Height() * b.Width();
+}
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_FLOAT_SHAPE_HELPERS_H_
diff --git a/chromium/third_party/blink/renderer/platform/geometry/float_size.cc b/chromium/third_party/blink/renderer/platform/geometry/float_size.cc
new file mode 100644
index 00000000000..10fd7eeaacd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/float_size.cc
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2005 Nokia. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/geometry/float_size.h"
+
+#include <math.h>
+#include <limits>
+#include "third_party/blink/renderer/platform/geometry/int_size.h"
+#include "third_party/blink/renderer/platform/geometry/layout_size.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/skia/include/core/SkSize.h"
+
+namespace blink {
+
+FloatSize::FloatSize(const SkSize& size)
+ : width_(size.width()), height_(size.height()) {}
+
+FloatSize::FloatSize(const LayoutSize& size)
+ : width_(size.Width().ToFloat()), height_(size.Height().ToFloat()) {}
+
+float FloatSize::DiagonalLength() const {
+ return hypotf(width_, height_);
+}
+
+bool FloatSize::IsExpressibleAsIntSize() const {
+ return isWithinIntRange(width_) && isWithinIntRange(height_);
+}
+
+FloatSize FloatSize::NarrowPrecision(double width, double height) {
+ return FloatSize(clampTo<float>(width), clampTo<float>(height));
+}
+
+FloatSize::operator SkSize() const {
+ return SkSize::Make(width_, height_);
+}
+
+std::ostream& operator<<(std::ostream& ostream, const FloatSize& size) {
+ return ostream << size.ToString();
+}
+
+String FloatSize::ToString() const {
+ return String::Format("%lgx%lg", Width(), Height());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/float_size.h b/chromium/third_party/blink/renderer/platform/geometry/float_size.h
new file mode 100644
index 00000000000..0f1403c58ec
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/float_size.h
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2005 Nokia. All rights reserved.
+ * 2008 Eric Seidel <eric@webkit.org>
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_FLOAT_SIZE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_FLOAT_SIZE_H_
+
+#include <iosfwd>
+
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/geometry/int_point.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+#if defined(OS_MACOSX)
+typedef struct CGSize CGSize;
+
+#ifdef __OBJC__
+#import <Foundation/Foundation.h>
+#endif
+#endif
+
+struct SkSize;
+
+namespace blink {
+
+class IntSize;
+class LayoutSize;
+
+class PLATFORM_EXPORT FloatSize {
+ DISALLOW_NEW();
+
+ public:
+ FloatSize() : width_(0), height_(0) {}
+ FloatSize(float width, float height) : width_(width), height_(height) {}
+ explicit FloatSize(const IntSize& size)
+ : width_(size.Width()), height_(size.Height()) {}
+ FloatSize(const SkSize&);
+ explicit FloatSize(const LayoutSize&);
+
+ static FloatSize NarrowPrecision(double width, double height);
+
+ float Width() const { return width_; }
+ float Height() const { return height_; }
+
+ void SetWidth(float width) { width_ = width; }
+ void SetHeight(float height) { height_ = height; }
+
+ bool IsEmpty() const { return width_ <= 0 || height_ <= 0; }
+ bool IsZero() const {
+ return fabs(width_) < std::numeric_limits<float>::epsilon() &&
+ fabs(height_) < std::numeric_limits<float>::epsilon();
+ }
+ bool IsExpressibleAsIntSize() const;
+
+ float AspectRatio() const { return width_ / height_; }
+
+ float Area() const { return width_ * height_; }
+
+ void Expand(float width, float height) {
+ width_ += width;
+ height_ += height;
+ }
+
+ void Scale(float s) { Scale(s, s); }
+
+ void Scale(float scale_x, float scale_y) {
+ width_ *= scale_x;
+ height_ *= scale_y;
+ }
+
+ void ScaleAndFloor(float scale) {
+ width_ = floorf(width_ * scale);
+ height_ = floorf(height_ * scale);
+ }
+
+ FloatSize ExpandedTo(const FloatSize& other) const {
+ return FloatSize(width_ > other.width_ ? width_ : other.width_,
+ height_ > other.height_ ? height_ : other.height_);
+ }
+
+ FloatSize ShrunkTo(const FloatSize& other) const {
+ return FloatSize(width_ < other.width_ ? width_ : other.width_,
+ height_ < other.height_ ? height_ : other.height_);
+ }
+
+ void ClampNegativeToZero() { *this = ExpandedTo(FloatSize()); }
+
+ float DiagonalLength() const;
+ float DiagonalLengthSquared() const {
+ return width_ * width_ + height_ * height_;
+ }
+
+ FloatSize TransposedSize() const { return FloatSize(height_, width_); }
+
+ FloatSize ScaledBy(float scale) const { return ScaledBy(scale, scale); }
+
+ FloatSize ScaledBy(float scale_x, float scale_y) const {
+ return FloatSize(width_ * scale_x, height_ * scale_y);
+ }
+
+#if defined(OS_MACOSX)
+ explicit FloatSize(
+ const CGSize&); // don't do this implicitly since it's lossy
+ operator CGSize() const;
+#endif
+
+ operator SkSize() const;
+
+ String ToString() const;
+
+ private:
+ float width_, height_;
+};
+
+inline FloatSize& operator+=(FloatSize& a, const FloatSize& b) {
+ a.SetWidth(a.Width() + b.Width());
+ a.SetHeight(a.Height() + b.Height());
+ return a;
+}
+
+inline FloatSize& operator-=(FloatSize& a, const FloatSize& b) {
+ a.SetWidth(a.Width() - b.Width());
+ a.SetHeight(a.Height() - b.Height());
+ return a;
+}
+
+inline FloatSize operator+(const FloatSize& a, const FloatSize& b) {
+ return FloatSize(a.Width() + b.Width(), a.Height() + b.Height());
+}
+
+inline FloatSize operator-(const FloatSize& a, const FloatSize& b) {
+ return FloatSize(a.Width() - b.Width(), a.Height() - b.Height());
+}
+
+inline FloatSize operator-(const FloatSize& size) {
+ return FloatSize(-size.Width(), -size.Height());
+}
+
+inline FloatSize operator*(const FloatSize& a, const float b) {
+ return FloatSize(a.Width() * b, a.Height() * b);
+}
+
+inline FloatSize operator*(const float a, const FloatSize& b) {
+ return FloatSize(a * b.Width(), a * b.Height());
+}
+
+inline bool operator==(const FloatSize& a, const FloatSize& b) {
+ return a.Width() == b.Width() && a.Height() == b.Height();
+}
+
+inline bool operator!=(const FloatSize& a, const FloatSize& b) {
+ return a.Width() != b.Width() || a.Height() != b.Height();
+}
+
+inline IntSize RoundedIntSize(const FloatSize& p) {
+ return IntSize(clampTo<int>(roundf(p.Width())),
+ clampTo<int>(roundf(p.Height())));
+}
+
+inline IntSize FlooredIntSize(const FloatSize& p) {
+ return IntSize(clampTo<int>(floorf(p.Width())),
+ clampTo<int>(floorf(p.Height())));
+}
+
+inline IntSize ExpandedIntSize(const FloatSize& p) {
+ return IntSize(clampTo<int>(ceilf(p.Width())),
+ clampTo<int>(ceilf(p.Height())));
+}
+
+inline IntPoint FlooredIntPoint(const FloatSize& p) {
+ return IntPoint(clampTo<int>(floorf(p.Width())),
+ clampTo<int>(floorf(p.Height())));
+}
+
+PLATFORM_EXPORT std::ostream& operator<<(std::ostream&, const FloatSize&);
+
+} // namespace blink
+
+// Allows this class to be stored in a HeapVector.
+WTF_ALLOW_CLEAR_UNUSED_SLOTS_WITH_MEM_FUNCTIONS(blink::FloatSize);
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_FLOAT_SIZE_H_
diff --git a/chromium/third_party/blink/renderer/platform/geometry/float_size_test.cc b/chromium/third_party/blink/renderer/platform/geometry/float_size_test.cc
new file mode 100644
index 00000000000..c9e185b7bb4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/float_size_test.cc
@@ -0,0 +1,32 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/geometry/float_size.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/geometry/geometry_test_helpers.h"
+
+namespace blink {
+
+TEST(FloatSizeTest, DiagonalLengthTest) {
+ // Sanity check the Pythagorean triples 3-4-5 and 5-12-13
+ FloatSize s1 = FloatSize(3.f, 4.f);
+ EXPECT_PRED_FORMAT2(GeometryTest::AssertAlmostEqual, s1.DiagonalLength(),
+ 5.f);
+ FloatSize s2 = FloatSize(5.f, 12.f);
+ EXPECT_PRED_FORMAT2(GeometryTest::AssertAlmostEqual, s2.DiagonalLength(),
+ 13.f);
+
+ // Test very small numbers.
+ FloatSize s3 = FloatSize(.5e-20f, .5e-20f);
+ EXPECT_PRED_FORMAT2(GeometryTest::AssertAlmostEqual, s3.DiagonalLength(),
+ .707106781186548e-20f);
+
+ // Test very large numbers.
+ FloatSize s4 = FloatSize(.5e20f, .5e20f);
+ EXPECT_PRED_FORMAT2(GeometryTest::AssertAlmostEqual, s4.DiagonalLength(),
+ .707106781186548e20f);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/geometry_as_json.cc b/chromium/third_party/blink/renderer/platform/geometry/geometry_as_json.cc
new file mode 100644
index 00000000000..a48ec97a386
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/geometry_as_json.cc
@@ -0,0 +1,52 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/geometry/geometry_as_json.h"
+
+#include "third_party/blink/renderer/platform/transforms/transformation_matrix.h"
+
+namespace blink {
+
+static double RoundCloseToZero(double number) {
+ return std::abs(number) < 1e-7 ? 0 : number;
+}
+
+std::unique_ptr<JSONArray> TransformAsJSONArray(const TransformationMatrix& t) {
+ std::unique_ptr<JSONArray> array = JSONArray::Create();
+ {
+ std::unique_ptr<JSONArray> row = JSONArray::Create();
+ row->PushDouble(RoundCloseToZero(t.M11()));
+ row->PushDouble(RoundCloseToZero(t.M12()));
+ row->PushDouble(RoundCloseToZero(t.M13()));
+ row->PushDouble(RoundCloseToZero(t.M14()));
+ array->PushArray(std::move(row));
+ }
+ {
+ std::unique_ptr<JSONArray> row = JSONArray::Create();
+ row->PushDouble(RoundCloseToZero(t.M21()));
+ row->PushDouble(RoundCloseToZero(t.M22()));
+ row->PushDouble(RoundCloseToZero(t.M23()));
+ row->PushDouble(RoundCloseToZero(t.M24()));
+ array->PushArray(std::move(row));
+ }
+ {
+ std::unique_ptr<JSONArray> row = JSONArray::Create();
+ row->PushDouble(RoundCloseToZero(t.M31()));
+ row->PushDouble(RoundCloseToZero(t.M32()));
+ row->PushDouble(RoundCloseToZero(t.M33()));
+ row->PushDouble(RoundCloseToZero(t.M34()));
+ array->PushArray(std::move(row));
+ }
+ {
+ std::unique_ptr<JSONArray> row = JSONArray::Create();
+ row->PushDouble(RoundCloseToZero(t.M41()));
+ row->PushDouble(RoundCloseToZero(t.M42()));
+ row->PushDouble(RoundCloseToZero(t.M43()));
+ row->PushDouble(RoundCloseToZero(t.M44()));
+ array->PushArray(std::move(row));
+ }
+ return array;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/geometry_as_json.h b/chromium/third_party/blink/renderer/platform/geometry/geometry_as_json.h
new file mode 100644
index 00000000000..1437b20bb6c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/geometry_as_json.h
@@ -0,0 +1,46 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_GEOMETRY_AS_JSON_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_GEOMETRY_AS_JSON_H_
+
+#include "third_party/blink/renderer/platform/json/json_values.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+
+class TransformationMatrix;
+
+template <typename T>
+static std::unique_ptr<JSONArray> RectAsJSONArray(const T& rect) {
+ std::unique_ptr<JSONArray> array = JSONArray::Create();
+ array->PushDouble(rect.X());
+ array->PushDouble(rect.Y());
+ array->PushDouble(rect.Width());
+ array->PushDouble(rect.Height());
+ return array;
+}
+
+template <typename T>
+std::unique_ptr<JSONArray> PointAsJSONArray(const T& point) {
+ std::unique_ptr<JSONArray> array = JSONArray::Create();
+ array->PushDouble(point.X());
+ array->PushDouble(point.Y());
+ return array;
+}
+
+template <typename T>
+std::unique_ptr<JSONArray> SizeAsJSONArray(const T& size) {
+ std::unique_ptr<JSONArray> array = JSONArray::Create();
+ array->PushDouble(size.Width());
+ array->PushDouble(size.Height());
+ return array;
+}
+
+PLATFORM_EXPORT std::unique_ptr<JSONArray> TransformAsJSONArray(
+ const TransformationMatrix&);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_GEOMETRY_AS_JSON_H_
diff --git a/chromium/third_party/blink/renderer/platform/geometry/geometry_test_helpers.cc b/chromium/third_party/blink/renderer/platform/geometry/geometry_test_helpers.cc
new file mode 100644
index 00000000000..77d0e7625a0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/geometry_test_helpers.cc
@@ -0,0 +1,43 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/geometry/geometry_test_helpers.h"
+
+#include <limits>
+#include <math.h>
+
+namespace blink {
+namespace GeometryTest {
+
+bool ApproximatelyEqual(float a, float b, float test_epsilon) {
+ float abs_a = ::fabs(a);
+ float abs_b = ::fabs(b);
+ float abs_err = ::fabs(a - b);
+ if (a == b)
+ return true;
+
+ if (a == 0 || b == 0 || abs_err < std::numeric_limits<float>::min())
+ return abs_err < (test_epsilon * std::numeric_limits<float>::min());
+
+ return ((abs_err / (abs_a + abs_b)) < test_epsilon);
+}
+
+testing::AssertionResult AssertAlmostEqual(const char* actual_expr,
+ const char* expected_expr,
+ float actual,
+ float expected,
+ float test_epsilon) {
+ if (!ApproximatelyEqual(actual, expected, test_epsilon)) {
+ return testing::AssertionFailure()
+ << " Value of:" << actual_expr << std::endl
+ << " Actual:" << testing::PrintToString(actual) << std::endl
+ << "Expected Approx:" << expected_expr << std::endl
+ << " Which is:" << testing::PrintToString(expected);
+ }
+
+ return testing::AssertionSuccess();
+}
+
+} // namespace GeometryTest
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/geometry_test_helpers.h b/chromium/third_party/blink/renderer/platform/geometry/geometry_test_helpers.h
new file mode 100644
index 00000000000..9ebef7d8c4b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/geometry_test_helpers.h
@@ -0,0 +1,23 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_GEOMETRY_TEST_HELPERS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_GEOMETRY_TEST_HELPERS_H_
+
+#include <gtest/gtest.h>
+
+namespace blink {
+namespace GeometryTest {
+
+bool ApproximatelyEqual(float, float, float test_epsilon);
+testing::AssertionResult AssertAlmostEqual(const char* actual_expr,
+ const char* expected_expr,
+ float actual,
+ float expected,
+ float test_epsilon = 1e-6);
+
+} // namespace GeometryTest
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/geometry/int_point.cc b/chromium/third_party/blink/renderer/platform/geometry/int_point.cc
new file mode 100644
index 00000000000..2bfd9fd9cdb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/int_point.cc
@@ -0,0 +1,19 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/geometry/int_point.h"
+
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+std::ostream& operator<<(std::ostream& ostream, const IntPoint& point) {
+ return ostream << point.ToString();
+}
+
+String IntPoint::ToString() const {
+ return String::Format("%d,%d", X(), Y());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/int_point.h b/chromium/third_party/blink/renderer/platform/geometry/int_point.h
new file mode 100644
index 00000000000..6e0072ecd50
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/int_point.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2013 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_INT_POINT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_INT_POINT_H_
+
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/geometry/int_size.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/saturated_arithmetic.h"
+#include "third_party/blink/renderer/platform/wtf/vector_traits.h"
+
+#if defined(OS_MACOSX)
+typedef struct CGPoint CGPoint;
+
+#ifdef __OBJC__
+#import <Foundation/Foundation.h>
+#endif
+#endif
+
+namespace blink {
+
+class PLATFORM_EXPORT IntPoint {
+ USING_FAST_MALLOC(IntPoint);
+
+ public:
+ IntPoint() : x_(0), y_(0) {}
+ IntPoint(int x, int y) : x_(x), y_(y) {}
+ explicit IntPoint(const IntSize& size)
+ : x_(size.Width()), y_(size.Height()) {}
+
+ static IntPoint Zero() { return IntPoint(); }
+
+ int X() const { return x_; }
+ int Y() const { return y_; }
+
+ void SetX(int x) { x_ = x; }
+ void SetY(int y) { y_ = y; }
+
+ void Move(const IntSize& s) { Move(s.Width(), s.Height()); }
+ void MoveBy(const IntPoint& offset) { Move(offset.X(), offset.Y()); }
+ void Move(int dx, int dy) {
+ x_ += dx;
+ y_ += dy;
+ }
+ void SaturatedMove(int dx, int dy) {
+ x_ = ClampAdd(x_, dx);
+ y_ = ClampAdd(y_, dy);
+ }
+
+ void Scale(float sx, float sy) {
+ x_ = lroundf(static_cast<float>(x_ * sx));
+ y_ = lroundf(static_cast<float>(y_ * sy));
+ }
+
+ IntPoint ExpandedTo(const IntPoint& other) const {
+ return IntPoint(x_ > other.x_ ? x_ : other.x_,
+ y_ > other.y_ ? y_ : other.y_);
+ }
+
+ IntPoint ShrunkTo(const IntPoint& other) const {
+ return IntPoint(x_ < other.x_ ? x_ : other.x_,
+ y_ < other.y_ ? y_ : other.y_);
+ }
+
+ int DistanceSquaredToPoint(const IntPoint&) const;
+
+ void ClampNegativeToZero() { *this = ExpandedTo(Zero()); }
+
+ IntPoint TransposedPoint() const { return IntPoint(y_, x_); }
+
+#if defined(OS_MACOSX)
+ explicit IntPoint(
+ const CGPoint&); // don't do this implicitly since it's lossy
+ operator CGPoint() const;
+#endif
+
+ String ToString() const;
+
+ private:
+ int x_, y_;
+};
+
+inline IntPoint& operator+=(IntPoint& a, const IntSize& b) {
+ a.Move(b.Width(), b.Height());
+ return a;
+}
+
+inline IntPoint& operator-=(IntPoint& a, const IntSize& b) {
+ a.Move(-b.Width(), -b.Height());
+ return a;
+}
+
+inline IntPoint operator+(const IntPoint& a, const IntSize& b) {
+ return IntPoint(a.X() + b.Width(), a.Y() + b.Height());
+}
+
+inline IntPoint operator+(const IntPoint& a, const IntPoint& b) {
+ return IntPoint(a.X() + b.X(), a.Y() + b.Y());
+}
+
+inline IntSize operator-(const IntPoint& a, const IntPoint& b) {
+ return IntSize(a.X() - b.X(), a.Y() - b.Y());
+}
+
+inline IntPoint operator-(const IntPoint& a, const IntSize& b) {
+ return IntPoint(a.X() - b.Width(), a.Y() - b.Height());
+}
+
+inline IntPoint operator-(const IntPoint& point) {
+ return IntPoint(-point.X(), -point.Y());
+}
+
+inline bool operator==(const IntPoint& a, const IntPoint& b) {
+ return a.X() == b.X() && a.Y() == b.Y();
+}
+
+inline bool operator!=(const IntPoint& a, const IntPoint& b) {
+ return a.X() != b.X() || a.Y() != b.Y();
+}
+
+inline IntSize ToIntSize(const IntPoint& a) {
+ return IntSize(a.X(), a.Y());
+}
+
+inline int IntPoint::DistanceSquaredToPoint(const IntPoint& point) const {
+ return ((*this) - point).DiagonalLengthSquared();
+}
+
+PLATFORM_EXPORT std::ostream& operator<<(std::ostream&, const IntPoint&);
+
+} // namespace blink
+
+WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(blink::IntPoint);
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_INT_POINT_H_
diff --git a/chromium/third_party/blink/renderer/platform/geometry/int_rect.cc b/chromium/third_party/blink/renderer/platform/geometry/int_rect.cc
new file mode 100644
index 00000000000..5aeedea84df
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/int_rect.cc
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2003, 2006, 2009 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/geometry/int_rect.h"
+
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
+#include "third_party/blink/renderer/platform/wtf/checked_numeric.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "third_party/skia/include/core/SkRect.h"
+#include "ui/gfx/geometry/rect.h"
+
+#include <algorithm>
+
+namespace blink {
+
+void IntRect::ShiftXEdgeTo(int edge) {
+ int delta = edge - X();
+ SetX(edge);
+ SetWidth(std::max(0, Width() - delta));
+}
+
+void IntRect::ShiftMaxXEdgeTo(int edge) {
+ int delta = edge - MaxX();
+ SetWidth(std::max(0, Width() + delta));
+}
+
+void IntRect::ShiftYEdgeTo(int edge) {
+ int delta = edge - Y();
+ SetY(edge);
+ SetHeight(std::max(0, Height() - delta));
+}
+
+void IntRect::ShiftMaxYEdgeTo(int edge) {
+ int delta = edge - MaxY();
+ SetHeight(std::max(0, Height() + delta));
+}
+
+bool IntRect::Intersects(const IntRect& other) const {
+ // Checking emptiness handles negative widths as well as zero.
+ return !IsEmpty() && !other.IsEmpty() && X() < other.MaxX() &&
+ other.X() < MaxX() && Y() < other.MaxY() && other.Y() < MaxY();
+}
+
+bool IntRect::Contains(const IntRect& other) const {
+ return X() <= other.X() && MaxX() >= other.MaxX() && Y() <= other.Y() &&
+ MaxY() >= other.MaxY();
+}
+
+void IntRect::Intersect(const IntRect& other) {
+ int left = std::max(X(), other.X());
+ int top = std::max(Y(), other.Y());
+ int right = std::min(MaxX(), other.MaxX());
+ int bottom = std::min(MaxY(), other.MaxY());
+
+ // Return a clean empty rectangle for non-intersecting cases.
+ if (left >= right || top >= bottom) {
+ left = 0;
+ top = 0;
+ right = 0;
+ bottom = 0;
+ }
+
+ location_.SetX(left);
+ location_.SetY(top);
+ size_.SetWidth(right - left);
+ size_.SetHeight(bottom - top);
+}
+
+void IntRect::Unite(const IntRect& other) {
+ // Handle empty special cases first.
+ if (other.IsEmpty())
+ return;
+ if (IsEmpty()) {
+ *this = other;
+ return;
+ }
+
+ UniteEvenIfEmpty(other);
+}
+
+void IntRect::UniteIfNonZero(const IntRect& other) {
+ // Handle empty special cases first.
+ if (!other.Width() && !other.Height())
+ return;
+ if (!Width() && !Height()) {
+ *this = other;
+ return;
+ }
+
+ UniteEvenIfEmpty(other);
+}
+
+void IntRect::UniteEvenIfEmpty(const IntRect& other) {
+ int left = std::min(X(), other.X());
+ int top = std::min(Y(), other.Y());
+ int right = std::max(MaxX(), other.MaxX());
+ int bottom = std::max(MaxY(), other.MaxY());
+
+ location_.SetX(left);
+ location_.SetY(top);
+ size_.SetWidth(right - left);
+ size_.SetHeight(bottom - top);
+}
+
+void IntRect::Scale(float s) {
+ location_.SetX((int)(X() * s));
+ location_.SetY((int)(Y() * s));
+ size_.SetWidth((int)(Width() * s));
+ size_.SetHeight((int)(Height() * s));
+}
+
+static inline int DistanceToInterval(int pos, int start, int end) {
+ if (pos < start)
+ return start - pos;
+ if (pos > end)
+ return end - pos;
+ return 0;
+}
+
+IntSize IntRect::DifferenceToPoint(const IntPoint& point) const {
+ int xdistance = DistanceToInterval(point.X(), X(), MaxX());
+ int ydistance = DistanceToInterval(point.Y(), Y(), MaxY());
+ return IntSize(xdistance, ydistance);
+}
+
+IntRect::operator SkIRect() const {
+ SkIRect rect = {X(), Y(), MaxX(), MaxY()};
+ return rect;
+}
+
+IntRect::operator SkRect() const {
+ SkRect rect;
+ rect.set(SkIntToScalar(X()), SkIntToScalar(Y()), SkIntToScalar(MaxX()),
+ SkIntToScalar(MaxY()));
+ return rect;
+}
+
+IntRect::operator gfx::Rect() const {
+ return gfx::Rect(X(), Y(), Width(), Height());
+}
+
+IntRect UnionRect(const Vector<IntRect>& rects) {
+ IntRect result;
+
+ size_t count = rects.size();
+ for (size_t i = 0; i < count; ++i)
+ result.Unite(rects[i]);
+
+ return result;
+}
+
+IntRect UnionRectEvenIfEmpty(const Vector<IntRect>& rects) {
+ size_t count = rects.size();
+ if (!count)
+ return IntRect();
+
+ IntRect result = rects[0];
+ for (size_t i = 1; i < count; ++i)
+ result.UniteEvenIfEmpty(rects[i]);
+
+ return result;
+}
+
+std::ostream& operator<<(std::ostream& ostream, const IntRect& rect) {
+ return ostream << rect.ToString();
+}
+
+String IntRect::ToString() const {
+ return String::Format("%s %s", Location().ToString().Ascii().data(),
+ Size().ToString().Ascii().data());
+}
+
+bool IntRect::IsValid() const {
+ CheckedNumeric<int> max = location_.X();
+ max += size_.Width();
+ if (!max.IsValid())
+ return false;
+ max = location_.Y();
+ max += size_.Height();
+ return max.IsValid();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/int_rect.h b/chromium/third_party/blink/renderer/platform/geometry/int_rect.h
new file mode 100644
index 00000000000..a1909aa4a41
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/int_rect.h
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2003, 2006, 2009 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_INT_RECT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_INT_RECT_H_
+
+#include <iosfwd>
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/geometry/int_point.h"
+#include "third_party/blink/renderer/platform/geometry/int_rect_outsets.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+#if defined(OS_MACOSX)
+typedef struct CGRect CGRect;
+
+#ifdef __OBJC__
+#import <Foundation/Foundation.h>
+#endif
+#endif
+
+struct SkRect;
+struct SkIRect;
+
+namespace gfx {
+class Rect;
+}
+
+namespace blink {
+
+class FloatRect;
+class LayoutRect;
+
+class PLATFORM_EXPORT IntRect {
+ USING_FAST_MALLOC(IntRect);
+
+ public:
+ IntRect() = default;
+ IntRect(const IntPoint& location, const IntSize& size)
+ : location_(location), size_(size) {}
+ IntRect(int x, int y, int width, int height)
+ : location_(IntPoint(x, y)), size_(IntSize(width, height)) {}
+
+ // Use EnclosingIntRect(), EnclosedIntRect(), RoundedIntRect(),
+ // PixelSnappedIntRect(), etc. instead.
+ explicit IntRect(const FloatRect&) = delete;
+ explicit IntRect(const LayoutRect&) = delete;
+
+ IntPoint Location() const { return location_; }
+ IntSize Size() const { return size_; }
+
+ void SetLocation(const IntPoint& location) { location_ = location; }
+ void SetSize(const IntSize& size) { size_ = size; }
+
+ int X() const { return location_.X(); }
+ int Y() const { return location_.Y(); }
+ int MaxX() const { return X() + Width(); }
+ int MaxY() const { return Y() + Height(); }
+ int Width() const { return size_.Width(); }
+ int Height() const { return size_.Height(); }
+
+ void SetX(int x) { location_.SetX(x); }
+ void SetY(int y) { location_.SetY(y); }
+ void SetWidth(int width) { size_.SetWidth(width); }
+ void SetHeight(int height) { size_.SetHeight(height); }
+
+ bool IsEmpty() const { return size_.IsEmpty(); }
+
+ // NOTE: The result is rounded to integer values, and thus may be not the
+ // exact center point.
+ IntPoint Center() const {
+ return IntPoint(X() + Width() / 2, Y() + Height() / 2);
+ }
+
+ void Move(const IntSize& size) { location_ += size; }
+ void MoveBy(const IntPoint& offset) {
+ location_.Move(offset.X(), offset.Y());
+ }
+ void Move(int dx, int dy) { location_.Move(dx, dy); }
+ void SaturatedMove(int dx, int dy) { location_.SaturatedMove(dx, dy); }
+
+ void Expand(const IntSize& size) { size_ += size; }
+ void Expand(int dw, int dh) { size_.Expand(dw, dh); }
+ void Expand(const IntRectOutsets& outsets) {
+ location_.Move(-outsets.Left(), -outsets.Top());
+ size_.Expand(outsets.Left() + outsets.Right(),
+ outsets.Top() + outsets.Bottom());
+ }
+
+ void Contract(const IntSize& size) { size_ -= size; }
+ void Contract(int dw, int dh) { size_.Expand(-dw, -dh); }
+
+ void ShiftXEdgeTo(int);
+ void ShiftMaxXEdgeTo(int);
+ void ShiftYEdgeTo(int);
+ void ShiftMaxYEdgeTo(int);
+
+ IntPoint MinXMinYCorner() const { return location_; } // typically topLeft
+ IntPoint MaxXMinYCorner() const {
+ return IntPoint(location_.X() + size_.Width(), location_.Y());
+ } // typically topRight
+ IntPoint MinXMaxYCorner() const {
+ return IntPoint(location_.X(), location_.Y() + size_.Height());
+ } // typically bottomLeft
+ IntPoint MaxXMaxYCorner() const {
+ return IntPoint(location_.X() + size_.Width(),
+ location_.Y() + size_.Height());
+ } // typically bottomRight
+
+ bool Intersects(const IntRect&) const;
+ bool Contains(const IntRect&) const;
+
+ // This checks to see if the rect contains x,y in the traditional sense.
+ // Equivalent to checking if the rect contains a 1x1 rect below and to the
+ // right of (px,py).
+ bool Contains(int px, int py) const {
+ return px >= X() && px < MaxX() && py >= Y() && py < MaxY();
+ }
+ bool Contains(const IntPoint& point) const {
+ return Contains(point.X(), point.Y());
+ }
+
+ void Intersect(const IntRect&);
+ void Unite(const IntRect&);
+ void UniteIfNonZero(const IntRect&);
+
+ // Besides non-empty rects, this method also unites empty rects (as points or
+ // line segments). For example, union of (100, 100, 0x0) and (200, 200, 50x0)
+ // is (100, 100, 150x100).
+ void UniteEvenIfEmpty(const IntRect&);
+
+ void InflateX(int dx) {
+ location_.SetX(location_.X() - dx);
+ size_.SetWidth(size_.Width() + dx + dx);
+ }
+ void InflateY(int dy) {
+ location_.SetY(location_.Y() - dy);
+ size_.SetHeight(size_.Height() + dy + dy);
+ }
+ void Inflate(int d) {
+ InflateX(d);
+ InflateY(d);
+ }
+ void Scale(float s);
+
+ IntSize DifferenceToPoint(const IntPoint&) const;
+ int DistanceSquaredToPoint(const IntPoint& p) const {
+ return DifferenceToPoint(p).DiagonalLengthSquared();
+ }
+
+ IntRect TransposedRect() const {
+ return IntRect(location_.TransposedPoint(), size_.TransposedSize());
+ }
+
+#if defined(OS_MACOSX)
+ explicit operator CGRect() const;
+#endif
+
+ operator SkRect() const;
+ operator SkIRect() const;
+
+ operator gfx::Rect() const;
+
+ String ToString() const;
+
+ // Return false if x + width or y + height overflows.
+ bool IsValid() const;
+
+ private:
+ IntPoint location_;
+ IntSize size_;
+};
+
+inline IntRect Intersection(const IntRect& a, const IntRect& b) {
+ IntRect c = a;
+ c.Intersect(b);
+ return c;
+}
+
+inline IntRect UnionRect(const IntRect& a, const IntRect& b) {
+ IntRect c = a;
+ c.Unite(b);
+ return c;
+}
+
+PLATFORM_EXPORT IntRect UnionRect(const Vector<IntRect>&);
+
+inline IntRect UnionRectEvenIfEmpty(const IntRect& a, const IntRect& b) {
+ IntRect c = a;
+ c.UniteEvenIfEmpty(b);
+ return c;
+}
+
+PLATFORM_EXPORT IntRect UnionRectEvenIfEmpty(const Vector<IntRect>&);
+
+inline bool operator==(const IntRect& a, const IntRect& b) {
+ return a.Location() == b.Location() && a.Size() == b.Size();
+}
+
+inline bool operator!=(const IntRect& a, const IntRect& b) {
+ return a.Location() != b.Location() || a.Size() != b.Size();
+}
+
+PLATFORM_EXPORT std::ostream& operator<<(std::ostream&, const IntRect&);
+
+} // namespace blink
+
+WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(blink::IntRect);
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_INT_RECT_H_
diff --git a/chromium/third_party/blink/renderer/platform/geometry/int_rect_outsets.h b/chromium/third_party/blink/renderer/platform/geometry/int_rect_outsets.h
new file mode 100644
index 00000000000..a5e4689b560
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/int_rect_outsets.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
+ * EXPRESS OR IMPLIED 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 HOLDER BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_INT_RECT_OUTSETS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_INT_RECT_OUTSETS_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+// Specifies integral lengths to be used to expand a rectangle.
+// For example, |top()| returns the distance the top edge should be moved
+// upward.
+//
+// Negative lengths can be used to express insets.
+class PLATFORM_EXPORT IntRectOutsets {
+ DISALLOW_NEW();
+
+ public:
+ IntRectOutsets() : top_(0), right_(0), bottom_(0), left_(0) {}
+
+ IntRectOutsets(int top, int right, int bottom, int left)
+ : top_(top), right_(right), bottom_(bottom), left_(left) {}
+
+ int Top() const { return top_; }
+ int Right() const { return right_; }
+ int Bottom() const { return bottom_; }
+ int Left() const { return left_; }
+
+ void SetTop(int top) { top_ = top; }
+ void SetRight(int right) { right_ = right; }
+ void SetBottom(int bottom) { bottom_ = bottom; }
+ void SetLeft(int left) { left_ = left; }
+
+ bool IsZero() const { return !Left() && !Right() && !Top() && !Bottom(); }
+
+ private:
+ int top_;
+ int right_;
+ int bottom_;
+ int left_;
+};
+
+inline void operator+=(IntRectOutsets& a, const IntRectOutsets& b) {
+ a.SetTop(a.Top() + b.Top());
+ a.SetRight(a.Right() + b.Right());
+ a.SetBottom(a.Bottom() + b.Bottom());
+ a.SetLeft(a.Left() + b.Left());
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_INT_RECT_OUTSETS_H_
diff --git a/chromium/third_party/blink/renderer/platform/geometry/int_rect_test.cc b/chromium/third_party/blink/renderer/platform/geometry/int_rect_test.cc
new file mode 100644
index 00000000000..fc3de582bdd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/int_rect_test.cc
@@ -0,0 +1,20 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+TEST(IntRectTest, ToString) {
+ IntRect empty_rect = IntRect();
+ EXPECT_EQ("0,0 0x0", empty_rect.ToString());
+
+ IntRect rect(1, 2, 3, 4);
+ EXPECT_EQ("1,2 3x4", rect.ToString());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/int_size.cc b/chromium/third_party/blink/renderer/platform/geometry/int_size.cc
new file mode 100644
index 00000000000..c9d57090496
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/int_size.cc
@@ -0,0 +1,24 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/geometry/int_size.h"
+
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace blink {
+
+IntSize::operator gfx::Size() const {
+ return gfx::Size(Width(), Height());
+}
+
+std::ostream& operator<<(std::ostream& ostream, const IntSize& size) {
+ return ostream << size.ToString();
+}
+
+String IntSize::ToString() const {
+ return String::Format("%dx%d", Width(), Height());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/int_size.h b/chromium/third_party/blink/renderer/platform/geometry/int_size.h
new file mode 100644
index 00000000000..d1eb7ed159f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/int_size.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. All rights
+ * reserved.
+ * Copyright (C) 2013 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_INT_SIZE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_INT_SIZE_H_
+
+#include "build/build_config.h"
+#include "third_party/blink/public/platform/web_common.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+#if defined(OS_MACOSX)
+typedef struct CGSize CGSize;
+
+#ifdef __OBJC__
+#import <Foundation/Foundation.h>
+#endif
+#endif
+
+namespace gfx {
+class Size;
+}
+
+namespace blink {
+
+class PLATFORM_EXPORT IntSize {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ IntSize() : width_(0), height_(0) {}
+ IntSize(int width, int height) : width_(width), height_(height) {}
+
+ int Width() const { return width_; }
+ int Height() const { return height_; }
+
+ void SetWidth(int width) { width_ = width; }
+ void SetHeight(int height) { height_ = height; }
+
+ bool IsEmpty() const { return width_ <= 0 || height_ <= 0; }
+ bool IsZero() const { return !width_ && !height_; }
+
+ float AspectRatio() const {
+ return static_cast<float>(width_) / static_cast<float>(height_);
+ }
+
+ void Expand(int width, int height) {
+ width_ += width;
+ height_ += height;
+ }
+
+ void Scale(float width_scale, float height_scale) {
+ width_ = static_cast<int>(static_cast<float>(width_) * width_scale);
+ height_ = static_cast<int>(static_cast<float>(height_) * height_scale);
+ }
+
+ void Scale(float scale) { this->Scale(scale, scale); }
+
+ IntSize ExpandedTo(const IntSize& other) const {
+ return IntSize(width_ > other.width_ ? width_ : other.width_,
+ height_ > other.height_ ? height_ : other.height_);
+ }
+
+ IntSize ShrunkTo(const IntSize& other) const {
+ return IntSize(width_ < other.width_ ? width_ : other.width_,
+ height_ < other.height_ ? height_ : other.height_);
+ }
+
+ void ClampNegativeToZero() { *this = ExpandedTo(IntSize()); }
+
+ void ClampToMinimumSize(const IntSize& minimum_size) {
+ if (width_ < minimum_size.Width())
+ width_ = minimum_size.Width();
+ if (height_ < minimum_size.Height())
+ height_ = minimum_size.Height();
+ }
+
+ // Return area in a uint64_t to avoid overflow.
+ uint64_t Area() const { return static_cast<uint64_t>(Width()) * Height(); }
+
+ int DiagonalLengthSquared() const {
+ return width_ * width_ + height_ * height_;
+ }
+
+ IntSize TransposedSize() const { return IntSize(height_, width_); }
+
+#if defined(OS_MACOSX)
+ explicit IntSize(const CGSize&);
+ explicit operator CGSize() const;
+#endif
+
+ operator gfx::Size() const;
+
+ String ToString() const;
+
+ private:
+ int width_, height_;
+};
+
+inline IntSize& operator+=(IntSize& a, const IntSize& b) {
+ a.SetWidth(a.Width() + b.Width());
+ a.SetHeight(a.Height() + b.Height());
+ return a;
+}
+
+inline IntSize& operator-=(IntSize& a, const IntSize& b) {
+ a.SetWidth(a.Width() - b.Width());
+ a.SetHeight(a.Height() - b.Height());
+ return a;
+}
+
+inline IntSize operator+(const IntSize& a, const IntSize& b) {
+ return IntSize(a.Width() + b.Width(), a.Height() + b.Height());
+}
+
+inline IntSize operator-(const IntSize& a, const IntSize& b) {
+ return IntSize(a.Width() - b.Width(), a.Height() - b.Height());
+}
+
+inline IntSize operator-(const IntSize& size) {
+ return IntSize(-size.Width(), -size.Height());
+}
+
+inline bool operator==(const IntSize& a, const IntSize& b) {
+ return a.Width() == b.Width() && a.Height() == b.Height();
+}
+
+inline bool operator!=(const IntSize& a, const IntSize& b) {
+ return a.Width() != b.Width() || a.Height() != b.Height();
+}
+
+PLATFORM_EXPORT std::ostream& operator<<(std::ostream&, const IntSize&);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_INT_SIZE_H_
diff --git a/chromium/third_party/blink/renderer/platform/geometry/layout_point.cc b/chromium/third_party/blink/renderer/platform/geometry/layout_point.cc
new file mode 100644
index 00000000000..5ef657bacaa
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/layout_point.cc
@@ -0,0 +1,29 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/geometry/layout_point.h"
+
+#include <algorithm>
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+LayoutPoint LayoutPoint::ExpandedTo(const LayoutPoint& other) const {
+ return LayoutPoint(std::max(x_, other.x_), std::max(y_, other.y_));
+}
+
+LayoutPoint LayoutPoint::ShrunkTo(const LayoutPoint& other) const {
+ return LayoutPoint(std::min(x_, other.x_), std::min(y_, other.y_));
+}
+
+std::ostream& operator<<(std::ostream& ostream, const LayoutPoint& point) {
+ return ostream << point.ToString();
+}
+
+String LayoutPoint::ToString() const {
+ return String::Format("%s,%s", X().ToString().Ascii().data(),
+ Y().ToString().Ascii().data());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/layout_point.h b/chromium/third_party/blink/renderer/platform/geometry/layout_point.h
new file mode 100644
index 00000000000..2d16732abde
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/layout_point.h
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2012, 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_LAYOUT_POINT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_LAYOUT_POINT_H_
+
+#include <iosfwd>
+#include "third_party/blink/renderer/platform/geometry/double_point.h"
+#include "third_party/blink/renderer/platform/geometry/float_point.h"
+#include "third_party/blink/renderer/platform/geometry/layout_size.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT LayoutPoint {
+ DISALLOW_NEW();
+
+ public:
+ LayoutPoint() = default;
+ LayoutPoint(LayoutUnit x, LayoutUnit y) : x_(x), y_(y) {}
+ LayoutPoint(int x, int y) : x_(LayoutUnit(x)), y_(LayoutUnit(y)) {}
+ LayoutPoint(const IntPoint& point) : x_(point.X()), y_(point.Y()) {}
+ explicit LayoutPoint(const FloatPoint& point)
+ : x_(point.X()), y_(point.Y()) {}
+ explicit LayoutPoint(const DoublePoint& point)
+ : x_(point.X()), y_(point.Y()) {}
+ explicit LayoutPoint(const LayoutSize& size)
+ : x_(size.Width()), y_(size.Height()) {}
+
+ static LayoutPoint Zero() { return LayoutPoint(); }
+
+ LayoutUnit X() const { return x_; }
+ LayoutUnit Y() const { return y_; }
+
+ void SetX(LayoutUnit x) { x_ = x; }
+ void SetY(LayoutUnit y) { y_ = y; }
+
+ void Move(const LayoutSize& s) { Move(s.Width(), s.Height()); }
+ void Move(const IntSize& s) { Move(s.Width(), s.Height()); }
+ void MoveBy(const LayoutPoint& offset) { Move(offset.X(), offset.Y()); }
+ void Move(int dx, int dy) { Move(LayoutUnit(dx), LayoutUnit(dy)); }
+ void Move(LayoutUnit dx, LayoutUnit dy) {
+ x_ += dx;
+ y_ += dy;
+ }
+ void Scale(float sx, float sy) {
+ x_ *= sx;
+ y_ *= sy;
+ }
+
+ LayoutPoint ExpandedTo(const LayoutPoint&) const;
+ LayoutPoint ShrunkTo(const LayoutPoint&) const;
+
+ void ClampNegativeToZero() { *this = ExpandedTo(Zero()); }
+
+ LayoutPoint TransposedPoint() const { return LayoutPoint(y_, x_); }
+
+ String ToString() const;
+
+ private:
+ LayoutUnit x_, y_;
+};
+
+ALWAYS_INLINE LayoutPoint& operator+=(LayoutPoint& a, const LayoutSize& b) {
+ a.Move(b.Width(), b.Height());
+ return a;
+}
+
+ALWAYS_INLINE LayoutPoint& operator+=(LayoutPoint& a, const LayoutPoint& b) {
+ a.Move(b.X(), b.Y());
+ return a;
+}
+
+inline LayoutPoint& operator+=(LayoutPoint& a, const IntSize& b) {
+ a.Move(b.Width(), b.Height());
+ return a;
+}
+
+ALWAYS_INLINE LayoutPoint& operator-=(LayoutPoint& a, const LayoutPoint& b) {
+ a.Move(-b.X(), -b.Y());
+ return a;
+}
+
+ALWAYS_INLINE LayoutPoint& operator-=(LayoutPoint& a, const LayoutSize& b) {
+ a.Move(-b.Width(), -b.Height());
+ return a;
+}
+
+inline LayoutPoint& operator-=(LayoutPoint& a, const IntSize& b) {
+ a.Move(-b.Width(), -b.Height());
+ return a;
+}
+
+inline LayoutPoint operator+(const LayoutPoint& a, const LayoutSize& b) {
+ return LayoutPoint(a.X() + b.Width(), a.Y() + b.Height());
+}
+
+ALWAYS_INLINE LayoutPoint operator+(const LayoutPoint& a,
+ const LayoutPoint& b) {
+ return LayoutPoint(a.X() + b.X(), a.Y() + b.Y());
+}
+
+ALWAYS_INLINE LayoutSize operator-(const LayoutPoint& a, const LayoutPoint& b) {
+ return LayoutSize(a.X() - b.X(), a.Y() - b.Y());
+}
+
+ALWAYS_INLINE LayoutSize operator-(const LayoutPoint& a, const IntPoint& b) {
+ return LayoutSize(a.X() - b.X(), a.Y() - b.Y());
+}
+
+inline LayoutPoint operator-(const LayoutPoint& a, const LayoutSize& b) {
+ return LayoutPoint(a.X() - b.Width(), a.Y() - b.Height());
+}
+
+inline LayoutPoint operator-(const LayoutPoint& a, const IntSize& b) {
+ return LayoutPoint(a.X() - b.Width(), a.Y() - b.Height());
+}
+
+inline LayoutPoint operator-(const LayoutPoint& point) {
+ return LayoutPoint(-point.X(), -point.Y());
+}
+
+ALWAYS_INLINE bool operator==(const LayoutPoint& a, const LayoutPoint& b) {
+ return a.X() == b.X() && a.Y() == b.Y();
+}
+
+inline bool operator!=(const LayoutPoint& a, const LayoutPoint& b) {
+ return a.X() != b.X() || a.Y() != b.Y();
+}
+
+inline LayoutPoint ToPoint(const LayoutSize& size) {
+ return LayoutPoint(size.Width(), size.Height());
+}
+
+inline LayoutPoint ToLayoutPoint(const LayoutSize& p) {
+ return LayoutPoint(p.Width(), p.Height());
+}
+
+inline LayoutSize ToSize(const LayoutPoint& a) {
+ return LayoutSize(a.X(), a.Y());
+}
+
+inline IntPoint FlooredIntPoint(const LayoutPoint& point) {
+ return IntPoint(point.X().Floor(), point.Y().Floor());
+}
+
+inline IntPoint RoundedIntPoint(const LayoutPoint& point) {
+ return IntPoint(point.X().Round(), point.Y().Round());
+}
+
+inline IntPoint RoundedIntPoint(const LayoutSize& size) {
+ return IntPoint(size.Width().Round(), size.Height().Round());
+}
+
+inline IntPoint CeiledIntPoint(const LayoutPoint& point) {
+ return IntPoint(point.X().Ceil(), point.Y().Ceil());
+}
+
+inline LayoutPoint FlooredLayoutPoint(const FloatPoint& p) {
+ return LayoutPoint(LayoutUnit::FromFloatFloor(p.X()),
+ LayoutUnit::FromFloatFloor(p.Y()));
+}
+
+inline LayoutPoint CeiledLayoutPoint(const FloatPoint& p) {
+ return LayoutPoint(LayoutUnit::FromFloatCeil(p.X()),
+ LayoutUnit::FromFloatCeil(p.Y()));
+}
+
+inline IntSize PixelSnappedIntSize(const LayoutSize& s, const LayoutPoint& p) {
+ return IntSize(SnapSizeToPixel(s.Width(), p.X()),
+ SnapSizeToPixel(s.Height(), p.Y()));
+}
+
+inline IntSize RoundedIntSize(const LayoutPoint& p) {
+ return IntSize(p.X().Round(), p.Y().Round());
+}
+
+inline LayoutSize ToLayoutSize(const LayoutPoint& p) {
+ return LayoutSize(p.X(), p.Y());
+}
+
+inline LayoutPoint FlooredLayoutPoint(const FloatSize& s) {
+ return FlooredLayoutPoint(FloatPoint(s));
+}
+
+PLATFORM_EXPORT std::ostream& operator<<(std::ostream&, const LayoutPoint&);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_LAYOUT_POINT_H_
diff --git a/chromium/third_party/blink/renderer/platform/geometry/layout_rect.cc b/chromium/third_party/blink/renderer/platform/geometry/layout_rect.cc
new file mode 100644
index 00000000000..19af6e9b8dd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/layout_rect.cc
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2012, 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 "third_party/blink/renderer/platform/geometry/layout_rect.h"
+
+#include <stdio.h>
+#include <algorithm>
+#include "third_party/blink/renderer/platform/geometry/double_rect.h"
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/layout_unit.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+LayoutRect::LayoutRect(const FloatRect& r)
+ : location_(LayoutPoint(r.Location())), size_(LayoutSize(r.Size())) {}
+
+LayoutRect::LayoutRect(const DoubleRect& r)
+ : location_(LayoutPoint(r.Location())), size_(LayoutSize(r.Size())) {}
+
+bool LayoutRect::Intersects(const LayoutRect& other) const {
+ // Checking emptiness handles negative widths as well as zero.
+ return !IsEmpty() && !other.IsEmpty() && X() < other.MaxX() &&
+ other.X() < MaxX() && Y() < other.MaxY() && other.Y() < MaxY();
+}
+
+bool LayoutRect::Contains(const LayoutRect& other) const {
+ return X() <= other.X() && MaxX() >= other.MaxX() && Y() <= other.Y() &&
+ MaxY() >= other.MaxY();
+}
+
+void LayoutRect::Intersect(const LayoutRect& other) {
+ LayoutPoint new_location(std::max(X(), other.X()), std::max(Y(), other.Y()));
+ LayoutPoint new_max_point(std::min(MaxX(), other.MaxX()),
+ std::min(MaxY(), other.MaxY()));
+
+ // Return a clean empty rectangle for non-intersecting cases.
+ if (new_location.X() >= new_max_point.X() ||
+ new_location.Y() >= new_max_point.Y()) {
+ new_location = LayoutPoint();
+ new_max_point = LayoutPoint();
+ }
+
+ location_ = new_location;
+ size_ = new_max_point - new_location;
+}
+
+bool LayoutRect::InclusiveIntersect(const LayoutRect& other) {
+ LayoutPoint new_location(std::max(X(), other.X()), std::max(Y(), other.Y()));
+ LayoutPoint new_max_point(std::min(MaxX(), other.MaxX()),
+ std::min(MaxY(), other.MaxY()));
+
+ if (new_location.X() > new_max_point.X() ||
+ new_location.Y() > new_max_point.Y()) {
+ *this = LayoutRect();
+ return false;
+ }
+
+ location_ = new_location;
+ size_ = new_max_point - new_location;
+ return true;
+}
+
+bool LayoutRect::IntersectsInclusively(const LayoutRect& other) {
+ // TODO(pdr): How should negative widths or heights be handled?
+ return X() <= other.MaxX() && other.X() <= MaxX() && Y() <= other.MaxY() &&
+ other.Y() <= MaxY();
+}
+
+void LayoutRect::Unite(const LayoutRect& other) {
+ // Handle empty special cases first.
+ if (other.IsEmpty())
+ return;
+ if (IsEmpty()) {
+ *this = other;
+ return;
+ }
+
+ UniteEvenIfEmpty(other);
+}
+
+void LayoutRect::UniteIfNonZero(const LayoutRect& other) {
+ // Handle empty special cases first.
+ if (!other.Width() && !other.Height())
+ return;
+ if (!Width() && !Height()) {
+ *this = other;
+ return;
+ }
+
+ UniteEvenIfEmpty(other);
+}
+
+void LayoutRect::UniteEvenIfEmpty(const LayoutRect& other) {
+ LayoutPoint new_location(std::min(X(), other.X()), std::min(Y(), other.Y()));
+ LayoutPoint new_max_point(std::max(MaxX(), other.MaxX()),
+ std::max(MaxY(), other.MaxY()));
+
+ location_ = new_location;
+ size_ = new_max_point - new_location;
+}
+
+void LayoutRect::Scale(float s) {
+ location_.Scale(s, s);
+ size_.Scale(s);
+}
+
+void LayoutRect::Scale(float x_axis_scale, float y_axis_scale) {
+ location_.Scale(x_axis_scale, y_axis_scale);
+ size_.Scale(x_axis_scale, y_axis_scale);
+}
+
+LayoutRect UnionRect(const Vector<LayoutRect>& rects) {
+ LayoutRect result;
+
+ size_t count = rects.size();
+ for (size_t i = 0; i < count; ++i)
+ result.Unite(rects[i]);
+
+ return result;
+}
+
+LayoutRect UnionRectEvenIfEmpty(const Vector<LayoutRect>& rects) {
+ size_t count = rects.size();
+ if (!count)
+ return LayoutRect();
+
+ LayoutRect result = rects[0];
+ for (size_t i = 1; i < count; ++i)
+ result.UniteEvenIfEmpty(rects[i]);
+
+ return result;
+}
+
+LayoutRect EnclosingLayoutRect(const FloatRect& rect) {
+ LayoutPoint location = FlooredLayoutPoint(rect.MinXMinYCorner());
+ LayoutPoint max_point = CeiledLayoutPoint(rect.MaxXMaxYCorner());
+ return LayoutRect(location, max_point - location);
+}
+
+std::ostream& operator<<(std::ostream& ostream, const LayoutRect& rect) {
+ return ostream << rect.ToString();
+}
+
+String LayoutRect::ToString() const {
+ return String::Format("%s %s", Location().ToString().Ascii().data(),
+ Size().ToString().Ascii().data());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/layout_rect.h b/chromium/third_party/blink/renderer/platform/geometry/layout_rect.h
new file mode 100644
index 00000000000..0c4022695a4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/layout_rect.h
@@ -0,0 +1,333 @@
+/*
+ * Copyright (c) 2012, 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_LAYOUT_RECT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_LAYOUT_RECT_H_
+
+#include <iosfwd>
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/geometry/layout_point.h"
+#include "third_party/blink/renderer/platform/geometry/layout_rect_outsets.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class FloatRect;
+class DoubleRect;
+
+class PLATFORM_EXPORT LayoutRect {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ LayoutRect() = default;
+ LayoutRect(const LayoutPoint& location, const LayoutSize& size)
+ : location_(location), size_(size) {}
+ LayoutRect(LayoutUnit x, LayoutUnit y, LayoutUnit width, LayoutUnit height)
+ : location_(LayoutPoint(x, y)), size_(LayoutSize(width, height)) {}
+ LayoutRect(int x, int y, int width, int height)
+ : location_(LayoutPoint(x, y)), size_(LayoutSize(width, height)) {}
+ LayoutRect(const FloatPoint& location, const FloatSize& size)
+ : location_(location), size_(size) {}
+ LayoutRect(const DoublePoint& location, const DoubleSize& size)
+ : location_(location), size_(size) {}
+ LayoutRect(const IntPoint& location, const IntSize& size)
+ : location_(location), size_(size) {}
+ explicit LayoutRect(const IntRect& rect)
+ : location_(rect.Location()), size_(rect.Size()) {}
+
+ // Don't do these implicitly since they are lossy.
+ explicit LayoutRect(const FloatRect&);
+ explicit LayoutRect(const DoubleRect&);
+
+ LayoutPoint Location() const { return location_; }
+ LayoutSize Size() const { return size_; }
+
+ IntPoint PixelSnappedLocation() const { return RoundedIntPoint(location_); }
+ IntSize PixelSnappedSize() const {
+ return IntSize(SnapSizeToPixel(size_.Width(), location_.X()),
+ SnapSizeToPixel(size_.Height(), location_.Y()));
+ }
+
+ void SetLocation(const LayoutPoint& location) { location_ = location; }
+ void SetSize(const LayoutSize& size) { size_ = size; }
+
+ ALWAYS_INLINE LayoutUnit X() const { return location_.X(); }
+ ALWAYS_INLINE LayoutUnit Y() const { return location_.Y(); }
+ ALWAYS_INLINE LayoutUnit MaxX() const { return X() + Width(); }
+ ALWAYS_INLINE LayoutUnit MaxY() const { return Y() + Height(); }
+ LayoutUnit Width() const { return size_.Width(); }
+ LayoutUnit Height() const { return size_.Height(); }
+
+ int PixelSnappedWidth() const { return SnapSizeToPixel(Width(), X()); }
+ int PixelSnappedHeight() const { return SnapSizeToPixel(Height(), Y()); }
+
+ void SetX(LayoutUnit x) { location_.SetX(x); }
+ void SetY(LayoutUnit y) { location_.SetY(y); }
+ void SetWidth(LayoutUnit width) { size_.SetWidth(width); }
+ void SetHeight(LayoutUnit height) { size_.SetHeight(height); }
+
+ ALWAYS_INLINE bool IsEmpty() const { return size_.IsEmpty(); }
+
+ // NOTE: The result is rounded to integer values, and thus may be not the
+ // exact center point.
+ LayoutPoint Center() const {
+ return LayoutPoint(X() + Width() / 2, Y() + Height() / 2);
+ }
+
+ void Move(const LayoutSize& size) { location_ += size; }
+ void Move(const IntSize& size) {
+ location_.Move(LayoutUnit(size.Width()), LayoutUnit(size.Height()));
+ }
+ void MoveBy(const LayoutPoint& offset) {
+ location_.Move(offset.X(), offset.Y());
+ }
+ void MoveBy(const IntPoint& offset) {
+ location_.Move(LayoutUnit(offset.X()), LayoutUnit(offset.Y()));
+ }
+ void Move(LayoutUnit dx, LayoutUnit dy) { location_.Move(dx, dy); }
+ void Move(int dx, int dy) { location_.Move(LayoutUnit(dx), LayoutUnit(dy)); }
+
+ void Expand(const LayoutSize& size) { size_ += size; }
+ void Expand(const LayoutRectOutsets& box) {
+ location_.Move(-box.Left(), -box.Top());
+ size_.Expand(box.Left() + box.Right(), box.Top() + box.Bottom());
+ }
+ void Expand(LayoutUnit dw, LayoutUnit dh) { size_.Expand(dw, dh); }
+ void ExpandEdges(LayoutUnit top,
+ LayoutUnit right,
+ LayoutUnit bottom,
+ LayoutUnit left) {
+ location_.Move(-left, -top);
+ size_.Expand(left + right, top + bottom);
+ }
+ void Contract(const LayoutSize& size) { size_ -= size; }
+ void Contract(LayoutUnit dw, LayoutUnit dh) { size_.Expand(-dw, -dh); }
+ void Contract(int dw, int dh) { size_.Expand(-dw, -dh); }
+ void Contract(const LayoutRectOutsets& box) {
+ location_.Move(box.Left(), box.Top());
+ size_.Shrink(box.Left() + box.Right(), box.Top() + box.Bottom());
+ }
+ void ContractEdges(LayoutUnit top,
+ LayoutUnit right,
+ LayoutUnit bottom,
+ LayoutUnit left) {
+ location_.Move(left, top);
+ size_.Shrink(left + right, top + bottom);
+ }
+
+ // Convert to an outsets for {{0, 0}, size} rect.
+ LayoutRectOutsets ToOutsets(const LayoutSize& size) const {
+ return LayoutRectOutsets(-Y(), MaxX() - size.Width(),
+ MaxY() - size.Height(), -X());
+ }
+
+ void ShiftXEdgeTo(LayoutUnit edge) {
+ LayoutUnit delta = edge - X();
+ SetX(edge);
+ SetWidth((Width() - delta).ClampNegativeToZero());
+ }
+ void ShiftMaxXEdgeTo(LayoutUnit edge) {
+ LayoutUnit delta = edge - MaxX();
+ SetWidth((Width() + delta).ClampNegativeToZero());
+ }
+ void ShiftYEdgeTo(LayoutUnit edge) {
+ LayoutUnit delta = edge - Y();
+ SetY(edge);
+ SetHeight((Height() - delta).ClampNegativeToZero());
+ }
+ void ShiftMaxYEdgeTo(LayoutUnit edge) {
+ LayoutUnit delta = edge - MaxY();
+ SetHeight((Height() + delta).ClampNegativeToZero());
+ }
+
+ LayoutPoint MinXMinYCorner() const { return location_; } // typically topLeft
+ LayoutPoint MaxXMinYCorner() const {
+ return LayoutPoint(location_.X() + size_.Width(), location_.Y());
+ } // typically topRight
+ LayoutPoint MinXMaxYCorner() const {
+ return LayoutPoint(location_.X(), location_.Y() + size_.Height());
+ } // typically bottomLeft
+ LayoutPoint MaxXMaxYCorner() const {
+ return LayoutPoint(location_.X() + size_.Width(),
+ location_.Y() + size_.Height());
+ } // typically bottomRight
+
+ bool Intersects(const LayoutRect&) const;
+ bool Contains(const LayoutRect&) const;
+
+ // This checks to see if the rect contains x,y in the traditional sense.
+ // Equivalent to checking if the rect contains a 1x1 rect below and to the
+ // right of (px,py).
+ bool Contains(LayoutUnit px, LayoutUnit py) const {
+ return px >= X() && px < MaxX() && py >= Y() && py < MaxY();
+ }
+ bool Contains(const LayoutPoint& point) const {
+ return Contains(point.X(), point.Y());
+ }
+
+ void Intersect(const LayoutRect&);
+ void Unite(const LayoutRect&);
+ void UniteIfNonZero(const LayoutRect&);
+
+ // Set this rect to be the intersection of itself and the argument rect
+ // using edge-inclusive geometry. If the two rectangles overlap but the
+ // overlap region is zero-area (either because one of the two rectangles
+ // is zero-area, or because the rectangles overlap at an edge or a corner),
+ // the result is the zero-area intersection. The return value indicates
+ // whether the two rectangle actually have an intersection, since checking
+ // the result for isEmpty() is not conclusive.
+ bool InclusiveIntersect(const LayoutRect&);
+
+ // Similar to |Intersects| but inclusive (see also: |InclusiveIntersect|).
+ // For example, (0,0 10x10) would inclusively intersect (10,10 0x0) even
+ // though the intersection has zero area and |Intersects| would be false.
+ bool IntersectsInclusively(const LayoutRect&);
+
+ // Besides non-empty rects, this method also unites empty rects (as points or
+ // line segments). For example, union of (100, 100, 0x0) and (200, 200, 50x0)
+ // is (100, 100, 150x100).
+ void UniteEvenIfEmpty(const LayoutRect&);
+
+ void InflateX(LayoutUnit dx) {
+ location_.SetX(location_.X() - dx);
+ size_.SetWidth(size_.Width() + dx + dx);
+ }
+ void InflateY(LayoutUnit dy) {
+ location_.SetY(location_.Y() - dy);
+ size_.SetHeight(size_.Height() + dy + dy);
+ }
+ void Inflate(LayoutUnit d) {
+ InflateX(d);
+ InflateY(d);
+ }
+ void Inflate(int d) { Inflate(LayoutUnit(d)); }
+ void Scale(float s);
+ void Scale(float x_axis_scale, float y_axis_scale);
+
+ LayoutRect TransposedRect() const {
+ return LayoutRect(location_.TransposedPoint(), size_.TransposedSize());
+ }
+
+ static IntRect InfiniteIntRect() {
+ // Due to saturated arithemetic this value is not the same as
+ // LayoutRect(IntRect(INT_MIN/2, INT_MIN/2, INT_MAX, INT_MAX)).
+ static IntRect infinite_int_rect(LayoutUnit::NearlyMin().ToInt() / 2,
+ LayoutUnit::NearlyMin().ToInt() / 2,
+ LayoutUnit::NearlyMax().ToInt(),
+ LayoutUnit::NearlyMax().ToInt());
+ return infinite_int_rect;
+ }
+
+ String ToString() const;
+
+ private:
+ LayoutPoint location_;
+ LayoutSize size_;
+};
+
+inline LayoutRect Intersection(const LayoutRect& a, const LayoutRect& b) {
+ LayoutRect c = a;
+ c.Intersect(b);
+ return c;
+}
+
+inline LayoutRect UnionRect(const LayoutRect& a, const LayoutRect& b) {
+ LayoutRect c = a;
+ c.Unite(b);
+ return c;
+}
+
+PLATFORM_EXPORT LayoutRect UnionRect(const Vector<LayoutRect>&);
+
+inline LayoutRect UnionRectEvenIfEmpty(const LayoutRect& a,
+ const LayoutRect& b) {
+ LayoutRect c = a;
+ c.UniteEvenIfEmpty(b);
+ return c;
+}
+
+PLATFORM_EXPORT LayoutRect UnionRectEvenIfEmpty(const Vector<LayoutRect>&);
+
+ALWAYS_INLINE bool operator==(const LayoutRect& a, const LayoutRect& b) {
+ return a.Location() == b.Location() && a.Size() == b.Size();
+}
+
+inline bool operator!=(const LayoutRect& a, const LayoutRect& b) {
+ return a.Location() != b.Location() || a.Size() != b.Size();
+}
+
+inline IntRect PixelSnappedIntRect(const LayoutRect& rect) {
+ return IntRect(RoundedIntPoint(rect.Location()),
+ IntSize(SnapSizeToPixel(rect.Width(), rect.X()),
+ SnapSizeToPixel(rect.Height(), rect.Y())));
+}
+
+inline IntRect EnclosingIntRect(const LayoutRect& rect) {
+ IntPoint location = FlooredIntPoint(rect.MinXMinYCorner());
+ IntPoint max_point = CeiledIntPoint(rect.MaxXMaxYCorner());
+ return IntRect(location, max_point - location);
+}
+
+inline IntRect EnclosedIntRect(const LayoutRect& rect) {
+ IntPoint location = CeiledIntPoint(rect.MinXMinYCorner());
+ IntPoint max_point = FlooredIntPoint(rect.MaxXMaxYCorner());
+ return IntRect(location, max_point - location);
+}
+
+PLATFORM_EXPORT LayoutRect EnclosingLayoutRect(const FloatRect&);
+
+inline IntRect PixelSnappedIntRect(LayoutUnit left,
+ LayoutUnit top,
+ LayoutUnit width,
+ LayoutUnit height) {
+ return IntRect(left.Round(), top.Round(), SnapSizeToPixel(width, left),
+ SnapSizeToPixel(height, top));
+}
+
+inline IntRect PixelSnappedIntRectFromEdges(LayoutUnit left,
+ LayoutUnit top,
+ LayoutUnit right,
+ LayoutUnit bottom) {
+ return IntRect(left.Round(), top.Round(), SnapSizeToPixel(right - left, left),
+ SnapSizeToPixel(bottom - top, top));
+}
+
+inline IntRect PixelSnappedIntRect(LayoutPoint location, LayoutSize size) {
+ return IntRect(RoundedIntPoint(location),
+ PixelSnappedIntSize(size, location));
+}
+
+PLATFORM_EXPORT std::ostream& operator<<(std::ostream&, const LayoutRect&);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_LAYOUT_RECT_H_
diff --git a/chromium/third_party/blink/renderer/platform/geometry/layout_rect_outsets.cc b/chromium/third_party/blink/renderer/platform/geometry/layout_rect_outsets.cc
new file mode 100644
index 00000000000..ceca3822dcc
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/layout_rect_outsets.cc
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2012, 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 "third_party/blink/renderer/platform/geometry/layout_rect_outsets.h"
+
+#include <algorithm>
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+namespace blink {
+
+void LayoutRectOutsets::ClampNegativeToZero() {
+ top_ = top_.ClampNegativeToZero();
+ right_ = right_.ClampNegativeToZero();
+ bottom_ = bottom_.ClampNegativeToZero();
+ left_ = left_.ClampNegativeToZero();
+}
+
+void LayoutRectOutsets::Unite(const LayoutRectOutsets& other) {
+ top_ = std::max(top_, other.top_);
+ right_ = std::max(right_, other.right_);
+ bottom_ = std::max(bottom_, other.bottom_);
+ left_ = std::max(left_, other.left_);
+}
+
+LayoutRectOutsets LayoutRectOutsets::LineOrientationOutsets(
+ WritingMode writing_mode) const {
+ if (!IsHorizontalWritingMode(writing_mode))
+ return LayoutRectOutsets(left_, bottom_, right_, top_);
+ return *this;
+}
+
+LayoutRectOutsets LayoutRectOutsets::LineOrientationOutsetsWithFlippedLines(
+ WritingMode writing_mode) const {
+ LayoutRectOutsets outsets = LineOrientationOutsets(writing_mode);
+ if (IsFlippedLinesWritingMode(writing_mode))
+ std::swap(outsets.top_, outsets.bottom_);
+ return outsets;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/layout_rect_outsets.h b/chromium/third_party/blink/renderer/platform/geometry/layout_rect_outsets.h
new file mode 100644
index 00000000000..c8e75106300
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/layout_rect_outsets.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2012, 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_LAYOUT_RECT_OUTSETS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_LAYOUT_RECT_OUTSETS_H_
+
+#include "third_party/blink/renderer/platform/geometry/float_rect_outsets.h"
+#include "third_party/blink/renderer/platform/geometry/int_rect_outsets.h"
+#include "third_party/blink/renderer/platform/layout_unit.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/text/writing_mode.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+// Specifies LayoutUnit lengths to be used to expand a rectangle.
+// For example, |top()| returns the distance the top edge should be moved
+// upward.
+//
+// Negative lengths can be used to express insets.
+class PLATFORM_EXPORT LayoutRectOutsets {
+ DISALLOW_NEW();
+
+ public:
+ LayoutRectOutsets() = default;
+ LayoutRectOutsets(LayoutUnit top,
+ LayoutUnit right,
+ LayoutUnit bottom,
+ LayoutUnit left)
+ : top_(top), right_(right), bottom_(bottom), left_(left) {}
+ LayoutRectOutsets(int top, int right, int bottom, int left)
+ : top_(LayoutUnit(top)),
+ right_(LayoutUnit(right)),
+ bottom_(LayoutUnit(bottom)),
+ left_(LayoutUnit(left)) {}
+
+ LayoutRectOutsets(const IntRectOutsets& outsets)
+ : top_(LayoutUnit(outsets.Top())),
+ right_(LayoutUnit(outsets.Right())),
+ bottom_(LayoutUnit(outsets.Bottom())),
+ left_(LayoutUnit(outsets.Left())) {}
+
+ LayoutRectOutsets(const FloatRectOutsets& outsets)
+ : top_(LayoutUnit(outsets.Top())),
+ right_(LayoutUnit(outsets.Right())),
+ bottom_(LayoutUnit(outsets.Bottom())),
+ left_(LayoutUnit(outsets.Left())) {}
+
+ LayoutUnit Top() const { return top_; }
+ LayoutUnit Right() const { return right_; }
+ LayoutUnit Bottom() const { return bottom_; }
+ LayoutUnit Left() const { return left_; }
+
+ void SetTop(LayoutUnit value) { top_ = value; }
+ void SetRight(LayoutUnit value) { right_ = value; }
+ void SetBottom(LayoutUnit value) { bottom_ = value; }
+ void SetLeft(LayoutUnit value) { left_ = value; }
+
+ bool IsZero() const { return !top_ && !right_ && !bottom_ && !left_; }
+
+ void ClampNegativeToZero();
+
+ void Unite(const LayoutRectOutsets&);
+
+ void FlipHorizontally() { std::swap(left_, right_); }
+
+ // Produces a new LayoutRectOutsets in line orientation
+ // (https://www.w3.org/TR/css-writing-modes-3/#line-orientation), whose
+ // - |top| is the logical 'over',
+ // - |right| is the logical 'line right',
+ // - |bottom| is the logical 'under',
+ // - |left| is the logical 'line left'.
+ LayoutRectOutsets LineOrientationOutsets(WritingMode) const;
+
+ // The same as |logicalOutsets|, but also adjusting for flipped lines.
+ LayoutRectOutsets LineOrientationOutsetsWithFlippedLines(WritingMode) const;
+
+ bool operator==(const LayoutRectOutsets other) const {
+ return Top() == other.Top() && Right() == other.Right() &&
+ Bottom() == other.Bottom() && Left() == other.Left();
+ }
+
+ private:
+ LayoutUnit top_;
+ LayoutUnit right_;
+ LayoutUnit bottom_;
+ LayoutUnit left_;
+};
+
+inline LayoutRectOutsets& operator+=(LayoutRectOutsets& a,
+ const LayoutRectOutsets& b) {
+ a.SetTop(a.Top() + b.Top());
+ a.SetRight(a.Right() + b.Right());
+ a.SetBottom(a.Bottom() + b.Bottom());
+ a.SetLeft(a.Left() + b.Left());
+ return a;
+}
+
+inline LayoutRectOutsets& operator+=(LayoutRectOutsets& a, LayoutUnit b) {
+ a.SetTop(a.Top() + b);
+ a.SetRight(a.Right() + b);
+ a.SetBottom(a.Bottom() + b);
+ a.SetLeft(a.Left() + b);
+ return a;
+}
+
+inline LayoutRectOutsets operator+(const LayoutRectOutsets& a,
+ const LayoutRectOutsets& b) {
+ return LayoutRectOutsets(a.Top() + b.Top(), a.Right() + b.Right(),
+ a.Bottom() + b.Bottom(), a.Left() + b.Left());
+}
+
+inline LayoutRectOutsets operator-(const LayoutRectOutsets& a) {
+ return LayoutRectOutsets(-a.Top(), -a.Right(), -a.Bottom(), -a.Left());
+}
+
+inline LayoutRectOutsets& operator-=(LayoutRectOutsets& a,
+ const LayoutRectOutsets& b) {
+ a += -b;
+ return a;
+}
+
+inline LayoutRectOutsets EnclosingLayoutRectOutsets(
+ const FloatRectOutsets& rect) {
+ return LayoutRectOutsets(LayoutUnit::FromFloatCeil(rect.Top()),
+ LayoutUnit::FromFloatCeil(rect.Right()),
+ LayoutUnit::FromFloatCeil(rect.Bottom()),
+ LayoutUnit::FromFloatCeil(rect.Left()));
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_LAYOUT_RECT_OUTSETS_H_
diff --git a/chromium/third_party/blink/renderer/platform/geometry/layout_rect_outsets_test.cc b/chromium/third_party/blink/renderer/platform/geometry/layout_rect_outsets_test.cc
new file mode 100644
index 00000000000..1c4e54c979e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/layout_rect_outsets_test.cc
@@ -0,0 +1,40 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/geometry/layout_rect_outsets.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+namespace {
+
+TEST(LayoutRectOutsetsTest, LineOrientationOutsets_Horizontal) {
+ LayoutRectOutsets outsets(1, 2, 3, 4);
+ EXPECT_EQ(LayoutRectOutsets(1, 2, 3, 4),
+ outsets.LineOrientationOutsets(WritingMode::kHorizontalTb));
+}
+
+TEST(LayoutRectOutsetsTest, LineOrientationOutsets_Vertical) {
+ LayoutRectOutsets outsets(1, 2, 3, 4);
+ EXPECT_EQ(LayoutRectOutsets(4, 3, 2, 1),
+ outsets.LineOrientationOutsets(WritingMode::kVerticalLr));
+ EXPECT_EQ(LayoutRectOutsets(4, 3, 2, 1),
+ outsets.LineOrientationOutsets(WritingMode::kVerticalRl));
+}
+
+TEST(LayoutRectOutsetsTest, LineOrientationOutsetsWithFlippedLines) {
+ LayoutRectOutsets outsets(1, 2, 3, 4);
+ EXPECT_EQ(LayoutRectOutsets(1, 2, 3, 4),
+ outsets.LineOrientationOutsetsWithFlippedLines(
+ WritingMode::kHorizontalTb));
+ EXPECT_EQ(
+ LayoutRectOutsets(2, 3, 4, 1),
+ outsets.LineOrientationOutsetsWithFlippedLines(WritingMode::kVerticalLr));
+ EXPECT_EQ(
+ LayoutRectOutsets(4, 3, 2, 1),
+ outsets.LineOrientationOutsetsWithFlippedLines(WritingMode::kVerticalRl));
+}
+
+} // namespace
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/layout_rect_test.cc b/chromium/third_party/blink/renderer/platform/geometry/layout_rect_test.cc
new file mode 100644
index 00000000000..9c73468b17e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/layout_rect_test.cc
@@ -0,0 +1,185 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+TEST(LayoutRectTest, ToString) {
+ LayoutRect empty_rect = LayoutRect();
+ EXPECT_EQ("0,0 0x0", empty_rect.ToString());
+
+ LayoutRect rect(1, 2, 3, 4);
+ EXPECT_EQ("1,2 3x4", rect.ToString());
+
+ LayoutRect granular_rect(LayoutUnit(1.6f), LayoutUnit(2.7f), LayoutUnit(3.8f),
+ LayoutUnit(4.9f));
+ EXPECT_EQ("1.59375,2.6875 3.79688x4.89063", granular_rect.ToString());
+}
+
+TEST(LayoutRectTest, InclusiveIntersect) {
+ LayoutRect rect(11, 12, 0, 0);
+ EXPECT_TRUE(rect.InclusiveIntersect(LayoutRect(11, 12, 13, 14)));
+ EXPECT_EQ(rect, LayoutRect(11, 12, 0, 0));
+
+ rect = LayoutRect(11, 12, 13, 14);
+ EXPECT_TRUE(rect.InclusiveIntersect(LayoutRect(24, 8, 0, 7)));
+ EXPECT_EQ(rect, LayoutRect(24, 12, 0, 3));
+
+ rect = LayoutRect(11, 12, 13, 14);
+ EXPECT_TRUE(rect.InclusiveIntersect(LayoutRect(9, 15, 4, 0)));
+ EXPECT_EQ(rect, LayoutRect(11, 15, 2, 0));
+
+ rect = LayoutRect(11, 12, 0, 14);
+ EXPECT_FALSE(rect.InclusiveIntersect(LayoutRect(12, 13, 15, 16)));
+ EXPECT_EQ(rect, LayoutRect());
+}
+
+TEST(LayoutRectTest, IntersectsInclusively) {
+ LayoutRect a(11, 12, 0, 0);
+ LayoutRect b(11, 12, 13, 14);
+ // An empty rect can have inclusive intersection.
+ EXPECT_TRUE(a.IntersectsInclusively(b));
+ EXPECT_TRUE(b.IntersectsInclusively(a));
+
+ a = LayoutRect(11, 12, 13, 14);
+ b = LayoutRect(24, 8, 0, 7);
+ // Intersecting left side is sufficient for inclusive intersection.
+ EXPECT_TRUE(a.IntersectsInclusively(b));
+ EXPECT_TRUE(b.IntersectsInclusively(a));
+
+ a = LayoutRect(11, 12, 13, 14);
+ b = LayoutRect(0, 26, 13, 8);
+ // Intersecting bottom side is sufficient for inclusive intersection.
+ EXPECT_TRUE(a.IntersectsInclusively(b));
+ EXPECT_TRUE(b.IntersectsInclusively(a));
+
+ a = LayoutRect(11, 12, 0, 0);
+ b = LayoutRect(11, 12, 0, 0);
+ // Two empty rects can intersect inclusively.
+ EXPECT_TRUE(a.IntersectsInclusively(b));
+ EXPECT_TRUE(b.IntersectsInclusively(a));
+
+ a = LayoutRect(10, 10, 10, 10);
+ b = LayoutRect(20, 20, 10, 10);
+ // Two rects can intersect inclusively at a single point.
+ EXPECT_TRUE(a.IntersectsInclusively(b));
+ EXPECT_TRUE(b.IntersectsInclusively(a));
+
+ a = LayoutRect(11, 12, 0, 0);
+ b = LayoutRect(20, 21, 0, 0);
+ // Two empty rects that do not touch do not intersect.
+ EXPECT_FALSE(a.IntersectsInclusively(b));
+ EXPECT_FALSE(b.IntersectsInclusively(a));
+
+ a = LayoutRect(11, 12, 5, 5);
+ b = LayoutRect(20, 21, 0, 0);
+ // A rect that does not touch a point does not intersect.
+ EXPECT_FALSE(a.IntersectsInclusively(b));
+ EXPECT_FALSE(b.IntersectsInclusively(a));
+}
+
+TEST(LayoutRectTest, EnclosingIntRect) {
+ LayoutUnit small;
+ small.SetRawValue(1);
+ LayoutRect small_dimensions_rect(LayoutUnit(42.5f), LayoutUnit(84.5f), small,
+ small);
+ EXPECT_EQ(IntRect(42, 84, 1, 1), EnclosingIntRect(small_dimensions_rect));
+
+ LayoutRect integral_rect(IntRect(100, 150, 200, 350));
+ EXPECT_EQ(IntRect(100, 150, 200, 350), EnclosingIntRect(integral_rect));
+
+ LayoutRect fractional_pos_rect(LayoutUnit(100.6f), LayoutUnit(150.8f),
+ LayoutUnit(200), LayoutUnit(350));
+ EXPECT_EQ(IntRect(100, 150, 201, 351), EnclosingIntRect(fractional_pos_rect));
+
+ LayoutRect fractional_dimensions_rect(LayoutUnit(100), LayoutUnit(150),
+ LayoutUnit(200.6f), LayoutUnit(350.4f));
+ EXPECT_EQ(IntRect(100, 150, 201, 351),
+ EnclosingIntRect(fractional_dimensions_rect));
+
+ LayoutRect fractional_both_rect1(LayoutUnit(100.6f), LayoutUnit(150.8f),
+ LayoutUnit(200.4f), LayoutUnit(350.2f));
+ EXPECT_EQ(IntRect(100, 150, 201, 351),
+ EnclosingIntRect(fractional_both_rect1));
+
+ LayoutRect fractional_both_rect2(LayoutUnit(100.5f), LayoutUnit(150.7f),
+ LayoutUnit(200.3f), LayoutUnit(350.3f));
+ EXPECT_EQ(IntRect(100, 150, 201, 351),
+ EnclosingIntRect(fractional_both_rect2));
+
+ LayoutRect fractional_both_rect3(LayoutUnit(100.3f), LayoutUnit(150.2f),
+ LayoutUnit(200.8f), LayoutUnit(350.9f));
+ EXPECT_EQ(IntRect(100, 150, 202, 352),
+ EnclosingIntRect(fractional_both_rect3));
+
+ LayoutRect fractional_negpos_rect1(LayoutUnit(-100.4f), LayoutUnit(-150.8f),
+ LayoutUnit(200), LayoutUnit(350));
+ EXPECT_EQ(IntRect(-101, -151, 201, 351),
+ EnclosingIntRect(fractional_negpos_rect1));
+
+ LayoutRect fractional_negpos_rect2(LayoutUnit(-100.5f), LayoutUnit(-150.7f),
+ LayoutUnit(199.4f), LayoutUnit(350.3f));
+ EXPECT_EQ(IntRect(-101, -151, 200, 351),
+ EnclosingIntRect(fractional_negpos_rect2));
+
+ LayoutRect fractional_negpos_rect3(LayoutUnit(-100.3f), LayoutUnit(-150.2f),
+ LayoutUnit(199.6f), LayoutUnit(350.3f));
+ EXPECT_EQ(IntRect(-101, -151, 201, 352),
+ EnclosingIntRect(fractional_negpos_rect3));
+}
+
+TEST(LayoutRectTest, EnclosedIntRect) {
+ LayoutUnit small;
+ small.SetRawValue(1);
+ LayoutRect small_dimensions_rect(LayoutUnit(42.5f), LayoutUnit(84.5f), small,
+ small);
+
+ LayoutRect integral_rect(IntRect(100, 150, 200, 350));
+ EXPECT_EQ(IntRect(100, 150, 200, 350), EnclosedIntRect(integral_rect));
+
+ LayoutRect fractional_pos_rect(LayoutUnit(100.6f), LayoutUnit(150.8f),
+ LayoutUnit(200), LayoutUnit(350));
+ EXPECT_EQ(IntRect(101, 151, 199, 349), EnclosedIntRect(fractional_pos_rect));
+
+ LayoutRect fractional_dimensions_rect(LayoutUnit(100), LayoutUnit(150),
+ LayoutUnit(200.6f), LayoutUnit(350.4f));
+ EXPECT_EQ(IntRect(100, 150, 200, 350),
+ EnclosedIntRect(fractional_dimensions_rect));
+
+ LayoutRect fractional_both_rect1(LayoutUnit(100.6f), LayoutUnit(150.8f),
+ LayoutUnit(200.4f), LayoutUnit(350.2f));
+ EXPECT_EQ(IntRect(101, 151, 199, 349),
+ EnclosedIntRect(fractional_both_rect1));
+
+ LayoutRect fractional_both_rect2(LayoutUnit(100.5f), LayoutUnit(150.7f),
+ LayoutUnit(200.3f), LayoutUnit(350.3f));
+ EXPECT_EQ(IntRect(101, 151, 199, 349),
+ EnclosedIntRect(fractional_both_rect2));
+
+ LayoutRect fractional_both_rect3(LayoutUnit(100.3f), LayoutUnit(150.2f),
+ LayoutUnit(200.6f), LayoutUnit(350.3f));
+ EXPECT_EQ(IntRect(101, 151, 199, 349),
+ EnclosedIntRect(fractional_both_rect3));
+
+ LayoutRect fractional_negpos_rect1(LayoutUnit(-100.4f), LayoutUnit(-150.8f),
+ LayoutUnit(200), LayoutUnit(350));
+ EXPECT_EQ(IntRect(-100, -150, 199, 349),
+ EnclosedIntRect(fractional_negpos_rect1));
+
+ LayoutRect fractional_negpos_rect2(LayoutUnit(-100.5f), LayoutUnit(-150.7f),
+ LayoutUnit(199.4f), LayoutUnit(350.3f));
+ EXPECT_EQ(IntRect(-100, -150, 198, 349),
+ EnclosedIntRect(fractional_negpos_rect2));
+
+ LayoutRect fractional_negpos_rect3(LayoutUnit(-100.3f), LayoutUnit(-150.2f),
+ LayoutUnit(199.6f), LayoutUnit(350.3f));
+ EXPECT_EQ(IntRect(-100, -150, 199, 350),
+ EnclosedIntRect(fractional_negpos_rect3));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/layout_size.cc b/chromium/third_party/blink/renderer/platform/geometry/layout_size.cc
new file mode 100644
index 00000000000..23d797a7b2c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/layout_size.cc
@@ -0,0 +1,20 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/geometry/layout_size.h"
+
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+std::ostream& operator<<(std::ostream& ostream, const LayoutSize& size) {
+ return ostream << size.ToString();
+}
+
+String LayoutSize::ToString() const {
+ return String::Format("%sx%s", Width().ToString().Ascii().data(),
+ Height().ToString().Ascii().data());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/layout_size.h b/chromium/third_party/blink/renderer/platform/geometry/layout_size.h
new file mode 100644
index 00000000000..8da95a7027f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/layout_size.h
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2012, 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_LAYOUT_SIZE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_LAYOUT_SIZE_H_
+
+#include <iosfwd>
+#include "third_party/blink/renderer/platform/geometry/double_size.h"
+#include "third_party/blink/renderer/platform/geometry/float_point.h"
+#include "third_party/blink/renderer/platform/geometry/float_size.h"
+#include "third_party/blink/renderer/platform/geometry/int_size.h"
+#include "third_party/blink/renderer/platform/layout_unit.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+enum AspectRatioFit { kAspectRatioFitShrink, kAspectRatioFitGrow };
+
+class PLATFORM_EXPORT LayoutSize {
+ DISALLOW_NEW();
+
+ public:
+ LayoutSize() = default;
+ explicit LayoutSize(const IntSize& size)
+ : width_(size.Width()), height_(size.Height()) {}
+ LayoutSize(LayoutUnit width, LayoutUnit height)
+ : width_(width), height_(height) {}
+ LayoutSize(int width, int height)
+ : width_(LayoutUnit(width)), height_(LayoutUnit(height)) {}
+ LayoutSize(float width, float height)
+ : width_(LayoutUnit(width)), height_(LayoutUnit(height)) {}
+
+ explicit LayoutSize(const FloatSize& size)
+ : width_(size.Width()), height_(size.Height()) {}
+ explicit LayoutSize(const DoubleSize& size)
+ : width_(size.Width()), height_(size.Height()) {}
+
+ LayoutUnit Width() const { return width_; }
+ LayoutUnit Height() const { return height_; }
+
+ void SetWidth(LayoutUnit width) { width_ = width; }
+ void SetHeight(LayoutUnit height) { height_ = height; }
+
+ bool IsEmpty() const {
+ return width_.RawValue() <= 0 || height_.RawValue() <= 0;
+ }
+ bool IsZero() const { return !width_ && !height_; }
+
+ float AspectRatio() const { return width_.ToFloat() / height_.ToFloat(); }
+
+ void Expand(LayoutUnit width, LayoutUnit height) {
+ width_ += width;
+ height_ += height;
+ }
+
+ void Expand(int width, int height) {
+ Expand(LayoutUnit(width), LayoutUnit(height));
+ }
+
+ void Shrink(int width, int height) {
+ Shrink(LayoutUnit(width), LayoutUnit(height));
+ }
+
+ void Shrink(LayoutUnit width, LayoutUnit height) {
+ width_ -= width;
+ height_ -= height;
+ }
+
+ void Scale(float scale) {
+ width_ *= scale;
+ height_ *= scale;
+ }
+
+ void Scale(float width_scale, float height_scale) {
+ width_ *= width_scale;
+ height_ *= height_scale;
+ }
+
+ LayoutSize ExpandedTo(const LayoutSize& other) const {
+ return LayoutSize(width_ > other.width_ ? width_ : other.width_,
+ height_ > other.height_ ? height_ : other.height_);
+ }
+
+ LayoutSize ExpandedTo(const IntSize& other) const {
+ return LayoutSize(
+ width_ > other.Width() ? width_ : LayoutUnit(other.Width()),
+ height_ > other.Height() ? height_ : LayoutUnit(other.Height()));
+ }
+
+ LayoutSize ShrunkTo(const LayoutSize& other) const {
+ return LayoutSize(width_ < other.width_ ? width_ : other.width_,
+ height_ < other.height_ ? height_ : other.height_);
+ }
+
+ void ClampNegativeToZero() { *this = ExpandedTo(LayoutSize()); }
+
+ void ClampToMinimumSize(const LayoutSize& minimum_size) {
+ if (width_ < minimum_size.Width())
+ width_ = minimum_size.Width();
+ if (height_ < minimum_size.Height())
+ height_ = minimum_size.Height();
+ }
+
+ LayoutSize TransposedSize() const { return LayoutSize(height_, width_); }
+
+ LayoutSize FitToAspectRatio(const LayoutSize& aspect_ratio,
+ AspectRatioFit fit) const {
+ const float height_float = Height().ToFloat();
+ const float width_float = Width().ToFloat();
+ float height_scale = height_float / aspect_ratio.Height().ToFloat();
+ float width_scale = width_float / aspect_ratio.Width().ToFloat();
+ if ((width_scale > height_scale) != (fit == kAspectRatioFitGrow)) {
+ return LayoutSize(
+ height_float * aspect_ratio.Width() / aspect_ratio.Height(),
+ Height());
+ }
+ return LayoutSize(
+ Width(), width_float * aspect_ratio.Height() / aspect_ratio.Width());
+ }
+
+ LayoutSize Fraction() const {
+ return LayoutSize(width_.Fraction(), height_.Fraction());
+ }
+
+ String ToString() const;
+
+ private:
+ LayoutUnit width_, height_;
+};
+
+inline LayoutSize& operator+=(LayoutSize& a, const LayoutSize& b) {
+ a.SetWidth(a.Width() + b.Width());
+ a.SetHeight(a.Height() + b.Height());
+ return a;
+}
+
+inline LayoutSize& operator-=(LayoutSize& a, const LayoutSize& b) {
+ a.SetWidth(a.Width() - b.Width());
+ a.SetHeight(a.Height() - b.Height());
+ return a;
+}
+
+inline LayoutSize& operator-=(LayoutSize& a, const IntSize& b) {
+ a.SetWidth(a.Width() - b.Width());
+ a.SetHeight(a.Height() - b.Height());
+ return a;
+}
+
+inline LayoutSize operator+(const LayoutSize& a, const LayoutSize& b) {
+ return LayoutSize(a.Width() + b.Width(), a.Height() + b.Height());
+}
+
+inline LayoutSize operator+(const LayoutSize& a, const IntSize& b) {
+ return LayoutSize(a.Width() + b.Width(), a.Height() + b.Height());
+}
+
+inline LayoutSize operator-(const LayoutSize& a, const LayoutSize& b) {
+ return LayoutSize(a.Width() - b.Width(), a.Height() - b.Height());
+}
+
+inline LayoutSize operator-(const LayoutSize& size) {
+ return LayoutSize(-size.Width(), -size.Height());
+}
+
+inline bool operator==(const LayoutSize& a, const LayoutSize& b) {
+ return a.Width() == b.Width() && a.Height() == b.Height();
+}
+
+inline bool operator==(const LayoutSize& a, const IntSize& b) {
+ return a.Width() == b.Width() && a.Height() == b.Height();
+}
+
+inline bool operator!=(const LayoutSize& a, const LayoutSize& b) {
+ return a.Width() != b.Width() || a.Height() != b.Height();
+}
+
+inline FloatPoint operator+(const FloatPoint& a, const LayoutSize& b) {
+ return FloatPoint(a.X() + b.Width(), a.Y() + b.Height());
+}
+
+inline IntSize FlooredIntSize(const LayoutSize& s) {
+ return IntSize(s.Width().Floor(), s.Height().Floor());
+}
+
+inline IntSize RoundedIntSize(const LayoutSize& s) {
+ return IntSize(s.Width().Round(), s.Height().Round());
+}
+
+inline LayoutSize RoundedLayoutSize(const FloatSize& s) {
+ return LayoutSize(s);
+}
+
+PLATFORM_EXPORT std::ostream& operator<<(std::ostream&, const LayoutSize&);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_LAYOUT_SIZE_H_
diff --git a/chromium/third_party/blink/renderer/platform/geometry/layout_size_test.cc b/chromium/third_party/blink/renderer/platform/geometry/layout_size_test.cc
new file mode 100644
index 00000000000..2eb9e1ff244
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/layout_size_test.cc
@@ -0,0 +1,28 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/geometry/layout_size.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+TEST(LayoutSizeTest, FitToAspectRatioDoesntOverflow) {
+ // FitToAspectRatio() shouldn't overflow due to intermediate calculations,
+ // for both the "constrained by width" and "constrained by height" cases.
+ LayoutSize aspect_ratio(50000, 50000);
+ EXPECT_EQ("1000x1000",
+ LayoutSize(2000, 1000)
+ .FitToAspectRatio(aspect_ratio, kAspectRatioFitShrink)
+ .ToString());
+
+ LayoutSize size(1000, 2000);
+ EXPECT_EQ("1000x1000",
+ LayoutSize(1000, 2000)
+ .FitToAspectRatio(aspect_ratio, kAspectRatioFitShrink)
+ .ToString());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/region.cc b/chromium/third_party/blink/renderer/platform/geometry/region.cc
new file mode 100644
index 00000000000..14db9d1b994
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/region.cc
@@ -0,0 +1,626 @@
+/*
+ * Copyright (C) 2010, 2011 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/geometry/region.h"
+
+#include <stdio.h>
+
+// A region class based on the paper "Scanline Coherent Shape Algebra"
+// by Jonathan E. Steinhart from the book "Graphics Gems II".
+//
+// This implementation uses two vectors instead of linked list, and
+// also compresses regions when possible.
+
+namespace blink {
+
+Region::Region() = default;
+
+Region::Region(const IntRect& rect) : bounds_(rect), shape_(rect) {}
+
+Vector<IntRect> Region::Rects() const {
+ Vector<IntRect> rects;
+
+ for (Shape::SpanIterator span = shape_.SpansBegin(), end = shape_.SpansEnd();
+ span != end && span + 1 != end; ++span) {
+ int y = span->y;
+ int height = (span + 1)->y - y;
+
+ for (Shape::SegmentIterator segment = shape_.SegmentsBegin(span),
+ end = shape_.SegmentsEnd(span);
+ segment != end && segment + 1 != end; segment += 2) {
+ int x = *segment;
+ int width = *(segment + 1) - x;
+
+ rects.push_back(IntRect(x, y, width, height));
+ }
+ }
+
+ return rects;
+}
+
+bool Region::Contains(const Region& region) const {
+ if (!bounds_.Contains(region.bounds_))
+ return false;
+
+ return Shape::CompareShapes<Shape::CompareContainsOperation>(shape_,
+ region.shape_);
+}
+
+bool Region::Contains(const IntPoint& point) const {
+ if (!bounds_.Contains(point))
+ return false;
+
+ for (Shape::SpanIterator span = shape_.SpansBegin(), end = shape_.SpansEnd();
+ span != end && span + 1 != end; ++span) {
+ int y = span->y;
+ int max_y = (span + 1)->y;
+
+ if (y > point.Y())
+ break;
+ if (max_y <= point.Y())
+ continue;
+
+ for (Shape::SegmentIterator segment = shape_.SegmentsBegin(span),
+ end = shape_.SegmentsEnd(span);
+ segment != end && segment + 1 != end; segment += 2) {
+ int x = *segment;
+ int max_x = *(segment + 1);
+
+ if (x > point.X())
+ break;
+ if (max_x > point.X())
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool Region::Intersects(const Region& region) const {
+ if (!bounds_.Intersects(region.bounds_))
+ return false;
+
+ return Shape::CompareShapes<Shape::CompareIntersectsOperation>(shape_,
+ region.shape_);
+}
+
+template <typename CompareOperation>
+bool Region::Shape::CompareShapes(const Shape& a_shape, const Shape& b_shape) {
+ bool result = CompareOperation::kDefaultResult;
+
+ Shape::SpanIterator a_span = a_shape.SpansBegin();
+ Shape::SpanIterator a_span_end = a_shape.SpansEnd();
+ Shape::SpanIterator b_span = b_shape.SpansBegin();
+ Shape::SpanIterator b_span_end = b_shape.SpansEnd();
+
+ bool a_had_segment_in_previous_span = false;
+ bool b_had_segment_in_previous_span = false;
+ while (a_span != a_span_end && a_span + 1 != a_span_end &&
+ b_span != b_span_end && b_span + 1 != b_span_end) {
+ int a_y = a_span->y;
+ int a_max_y = (a_span + 1)->y;
+ int b_y = b_span->y;
+ int b_max_y = (b_span + 1)->y;
+
+ Shape::SegmentIterator a_segment = a_shape.SegmentsBegin(a_span);
+ Shape::SegmentIterator a_segment_end = a_shape.SegmentsEnd(a_span);
+ Shape::SegmentIterator b_segment = b_shape.SegmentsBegin(b_span);
+ Shape::SegmentIterator b_segment_end = b_shape.SegmentsEnd(b_span);
+
+ // Look for a non-overlapping part of the spans. If B had a segment in its
+ // previous span, then we already tested A against B within that span.
+ bool a_has_segment_in_span = a_segment != a_segment_end;
+ bool b_has_segment_in_span = b_segment != b_segment_end;
+ if (a_y < b_y && !b_had_segment_in_previous_span && a_has_segment_in_span &&
+ CompareOperation::AOutsideB(result))
+ return result;
+ if (b_y < a_y && !a_had_segment_in_previous_span && b_has_segment_in_span &&
+ CompareOperation::BOutsideA(result))
+ return result;
+
+ a_had_segment_in_previous_span = a_has_segment_in_span;
+ b_had_segment_in_previous_span = b_has_segment_in_span;
+
+ bool spans_overlap = b_max_y > a_y && b_y < a_max_y;
+ if (spans_overlap) {
+ while (a_segment != a_segment_end && b_segment != b_segment_end) {
+ int a_x = *a_segment;
+ int a_max_x = *(a_segment + 1);
+ int b_x = *b_segment;
+ int b_max_x = *(b_segment + 1);
+
+ bool segments_overlap = b_max_x > a_x && b_x < a_max_x;
+ if (segments_overlap && CompareOperation::AOverlapsB(result))
+ return result;
+ if (a_x < b_x && CompareOperation::AOutsideB(result))
+ return result;
+ if (b_x < a_x && CompareOperation::BOutsideA(result))
+ return result;
+
+ if (a_max_x < b_max_x) {
+ a_segment += 2;
+ } else if (b_max_x < a_max_x) {
+ b_segment += 2;
+ } else {
+ a_segment += 2;
+ b_segment += 2;
+ }
+ }
+
+ if (a_segment != a_segment_end && CompareOperation::AOutsideB(result))
+ return result;
+ if (b_segment != b_segment_end && CompareOperation::BOutsideA(result))
+ return result;
+ }
+
+ if (a_max_y < b_max_y) {
+ a_span += 1;
+ } else if (b_max_y < a_max_y) {
+ b_span += 1;
+ } else {
+ a_span += 1;
+ b_span += 1;
+ }
+ }
+
+ if (a_span != a_span_end && a_span + 1 != a_span_end &&
+ CompareOperation::AOutsideB(result))
+ return result;
+ if (b_span != b_span_end && b_span + 1 != b_span_end &&
+ CompareOperation::BOutsideA(result))
+ return result;
+
+ return result;
+}
+
+void Region::Shape::TrimCapacities() {
+ segments_.ShrinkToReasonableCapacity();
+ spans_.ShrinkToReasonableCapacity();
+}
+
+struct Region::Shape::CompareContainsOperation {
+ STATIC_ONLY(CompareContainsOperation);
+ const static bool kDefaultResult = true;
+ inline static bool AOutsideB(bool& /* result */) { return false; }
+ inline static bool BOutsideA(bool& result) {
+ result = false;
+ return true;
+ }
+ inline static bool AOverlapsB(bool& /* result */) { return false; }
+};
+
+struct Region::Shape::CompareIntersectsOperation {
+ STATIC_ONLY(CompareIntersectsOperation);
+ const static bool kDefaultResult = false;
+ inline static bool AOutsideB(bool& /* result */) { return false; }
+ inline static bool BOutsideA(bool& /* result */) { return false; }
+ inline static bool AOverlapsB(bool& result) {
+ result = true;
+ return true;
+ }
+};
+
+Region::Shape::Shape() = default;
+
+Region::Shape::Shape(const IntRect& rect) {
+ AppendSpan(rect.Y());
+ AppendSegment(rect.X());
+ AppendSegment(rect.MaxX());
+ AppendSpan(rect.MaxY());
+}
+
+Region::Shape::Shape(size_t segments_capacity, size_t spans_capacity) {
+ segments_.ReserveCapacity(segments_capacity);
+ spans_.ReserveCapacity(spans_capacity);
+}
+
+void Region::Shape::AppendSpan(int y) {
+ spans_.push_back(Span(y, segments_.size()));
+}
+
+bool Region::Shape::CanCoalesce(SegmentIterator begin, SegmentIterator end) {
+ if (spans_.IsEmpty())
+ return false;
+
+ SegmentIterator last_span_begin =
+ segments_.data() + spans_.back().segment_index;
+ SegmentIterator last_span_end = segments_.data() + segments_.size();
+
+ // Check if both spans have an equal number of segments.
+ if (last_span_end - last_span_begin != end - begin)
+ return false;
+
+ // Check if both spans are equal.
+ if (!std::equal(begin, end, last_span_begin))
+ return false;
+
+ // Since the segments are equal the second segment can just be ignored.
+ return true;
+}
+
+void Region::Shape::AppendSpan(int y,
+ SegmentIterator begin,
+ SegmentIterator end) {
+ if (CanCoalesce(begin, end))
+ return;
+
+ AppendSpan(y);
+ segments_.AppendRange(begin, end);
+}
+
+void Region::Shape::AppendSpans(const Shape& shape,
+ SpanIterator begin,
+ SpanIterator end) {
+ for (SpanIterator it = begin; it != end; ++it)
+ AppendSpan(it->y, shape.SegmentsBegin(it), shape.SegmentsEnd(it));
+}
+
+void Region::Shape::AppendSegment(int x) {
+ segments_.push_back(x);
+}
+
+Region::Shape::SpanIterator Region::Shape::SpansBegin() const {
+ return spans_.data();
+}
+
+Region::Shape::SpanIterator Region::Shape::SpansEnd() const {
+ return spans_.data() + spans_.size();
+}
+
+Region::Shape::SegmentIterator Region::Shape::SegmentsBegin(
+ SpanIterator it) const {
+ DCHECK_GE(it, spans_.data());
+ DCHECK_LT(it, spans_.data() + spans_.size());
+
+ // Check if this span has any segments.
+ if (it->segment_index == segments_.size())
+ return nullptr;
+
+ return &segments_[it->segment_index];
+}
+
+Region::Shape::SegmentIterator Region::Shape::SegmentsEnd(
+ SpanIterator it) const {
+ DCHECK_GE(it, spans_.data());
+ DCHECK_LT(it, spans_.data() + spans_.size());
+
+ // Check if this span has any segments.
+ if (it->segment_index == segments_.size())
+ return nullptr;
+
+ DCHECK_LT(it + 1, spans_.data() + spans_.size());
+ size_t segment_index = (it + 1)->segment_index;
+
+ SECURITY_DCHECK(segment_index <= segments_.size());
+ return segments_.data() + segment_index;
+}
+
+#ifndef NDEBUG
+void Region::Shape::Dump() const {
+ for (Shape::SpanIterator span = SpansBegin(), end = SpansEnd(); span != end;
+ ++span) {
+ printf("%6d: (", span->y);
+
+ for (Shape::SegmentIterator segment = SegmentsBegin(span),
+ end = SegmentsEnd(span);
+ segment != end; ++segment)
+ printf("%d ", *segment);
+ printf(")\n");
+ }
+
+ printf("\n");
+}
+#endif
+
+IntRect Region::Shape::Bounds() const {
+ if (IsEmpty())
+ return IntRect();
+
+ SpanIterator span = SpansBegin();
+ int min_y = span->y;
+
+ SpanIterator last_span = SpansEnd() - 1;
+ int max_y = last_span->y;
+
+ int min_x = std::numeric_limits<int>::max();
+ int max_x = std::numeric_limits<int>::min();
+
+ while (span != last_span) {
+ SegmentIterator first_segment = SegmentsBegin(span);
+ SegmentIterator last_segment = SegmentsEnd(span) - 1;
+
+ if (first_segment && last_segment) {
+ DCHECK_NE(first_segment, last_segment);
+
+ if (*first_segment < min_x)
+ min_x = *first_segment;
+
+ if (*last_segment > max_x)
+ max_x = *last_segment;
+ }
+
+ ++span;
+ }
+
+ DCHECK_LE(min_x, max_x);
+ DCHECK_LE(min_y, max_y);
+
+ return IntRect(min_x, min_y, max_x - min_x, max_y - min_y);
+}
+
+void Region::Shape::Translate(const IntSize& offset) {
+ for (size_t i = 0; i < segments_.size(); ++i)
+ segments_[i] += offset.Width();
+ for (size_t i = 0; i < spans_.size(); ++i)
+ spans_[i].y += offset.Height();
+}
+
+void Region::Shape::Swap(Shape& other) {
+ segments_.swap(other.segments_);
+ spans_.swap(other.spans_);
+}
+
+enum {
+ kShape1,
+ kShape2,
+};
+
+template <typename Operation>
+Region::Shape Region::Shape::ShapeOperation(const Shape& shape1,
+ const Shape& shape2) {
+ static_assert(!(!Operation::kShouldAddRemainingSegmentsFromSpan1 &&
+ Operation::kShouldAddRemainingSegmentsFromSpan2),
+ "invalid segment combination");
+ static_assert(!(!Operation::kShouldAddRemainingSpansFromShape1 &&
+ Operation::kShouldAddRemainingSpansFromShape2),
+ "invalid span combination");
+
+ size_t segments_capacity = shape1.SegmentsSize() + shape2.SegmentsSize();
+ size_t spans_capacity = shape1.SpansSize() + shape2.SpansSize();
+ Shape result(segments_capacity, spans_capacity);
+ if (Operation::TrySimpleOperation(shape1, shape2, result))
+ return result;
+
+ SpanIterator spans1 = shape1.SpansBegin();
+ SpanIterator spans1_end = shape1.SpansEnd();
+
+ SpanIterator spans2 = shape2.SpansBegin();
+ SpanIterator spans2_end = shape2.SpansEnd();
+
+ SegmentIterator segments1 = nullptr;
+ SegmentIterator segments1_end = nullptr;
+
+ SegmentIterator segments2 = nullptr;
+ SegmentIterator segments2_end = nullptr;
+
+ Vector<int, 32> segments;
+ segments.ReserveCapacity(
+ std::max(shape1.SegmentsSize(), shape2.SegmentsSize()));
+
+ // Iterate over all spans.
+ while (spans1 != spans1_end && spans2 != spans2_end) {
+ int y = 0;
+ int test = spans1->y - spans2->y;
+
+ if (test <= 0) {
+ y = spans1->y;
+
+ segments1 = shape1.SegmentsBegin(spans1);
+ segments1_end = shape1.SegmentsEnd(spans1);
+ ++spans1;
+ }
+ if (test >= 0) {
+ y = spans2->y;
+
+ segments2 = shape2.SegmentsBegin(spans2);
+ segments2_end = shape2.SegmentsEnd(spans2);
+ ++spans2;
+ }
+
+ int flag = 0;
+ int old_flag = 0;
+
+ SegmentIterator s1 = segments1;
+ SegmentIterator s2 = segments2;
+
+ // Clear vector without dropping capacity.
+ segments.resize(0);
+ DCHECK(segments.capacity());
+
+ // Now iterate over the segments in each span and construct a new vector of
+ // segments.
+ while (s1 != segments1_end && s2 != segments2_end) {
+ int test = *s1 - *s2;
+ int x;
+
+ if (test <= 0) {
+ x = *s1;
+ flag = flag ^ 1;
+ ++s1;
+ }
+ if (test >= 0) {
+ x = *s2;
+ flag = flag ^ 2;
+ ++s2;
+ }
+
+ if (flag == Operation::kOpCode || old_flag == Operation::kOpCode)
+ segments.push_back(x);
+
+ old_flag = flag;
+ }
+
+ // Add any remaining segments.
+ if (Operation::kShouldAddRemainingSegmentsFromSpan1 && s1 != segments1_end)
+ segments.AppendRange(s1, segments1_end);
+ else if (Operation::kShouldAddRemainingSegmentsFromSpan2 &&
+ s2 != segments2_end)
+ segments.AppendRange(s2, segments2_end);
+
+ // Add the span.
+ if (!segments.IsEmpty() || !result.IsEmpty())
+ result.AppendSpan(y, segments.data(), segments.data() + segments.size());
+ }
+
+ // Add any remaining spans.
+ if (Operation::kShouldAddRemainingSpansFromShape1 && spans1 != spans1_end)
+ result.AppendSpans(shape1, spans1, spans1_end);
+ else if (Operation::kShouldAddRemainingSpansFromShape2 &&
+ spans2 != spans2_end)
+ result.AppendSpans(shape2, spans2, spans2_end);
+
+ result.TrimCapacities();
+
+ return result;
+}
+
+struct Region::Shape::UnionOperation {
+ STATIC_ONLY(UnionOperation);
+ static bool TrySimpleOperation(const Shape& shape1,
+ const Shape& shape2,
+ Shape& result) {
+ if (shape1.IsEmpty()) {
+ result = shape2;
+ return true;
+ }
+
+ return false;
+ }
+
+ static const int kOpCode = 0;
+
+ static const bool kShouldAddRemainingSegmentsFromSpan1 = true;
+ static const bool kShouldAddRemainingSegmentsFromSpan2 = true;
+ static const bool kShouldAddRemainingSpansFromShape1 = true;
+ static const bool kShouldAddRemainingSpansFromShape2 = true;
+};
+
+Region::Shape Region::Shape::UnionShapes(const Shape& shape1,
+ const Shape& shape2) {
+ return ShapeOperation<UnionOperation>(shape1, shape2);
+}
+
+struct Region::Shape::IntersectOperation {
+ STATIC_ONLY(IntersectOperation);
+ static bool TrySimpleOperation(const Shape&, const Shape&, Shape&) {
+ return false;
+ }
+
+ static const int kOpCode = 3;
+
+ static const bool kShouldAddRemainingSegmentsFromSpan1 = false;
+ static const bool kShouldAddRemainingSegmentsFromSpan2 = false;
+ static const bool kShouldAddRemainingSpansFromShape1 = false;
+ static const bool kShouldAddRemainingSpansFromShape2 = false;
+};
+
+Region::Shape Region::Shape::IntersectShapes(const Shape& shape1,
+ const Shape& shape2) {
+ return ShapeOperation<IntersectOperation>(shape1, shape2);
+}
+
+struct Region::Shape::SubtractOperation {
+ STATIC_ONLY(SubtractOperation);
+ static bool TrySimpleOperation(const Shape&, const Shape&, Region::Shape&) {
+ return false;
+ }
+
+ static const int kOpCode = 1;
+
+ static const bool kShouldAddRemainingSegmentsFromSpan1 = true;
+ static const bool kShouldAddRemainingSegmentsFromSpan2 = false;
+ static const bool kShouldAddRemainingSpansFromShape1 = true;
+ static const bool kShouldAddRemainingSpansFromShape2 = false;
+};
+
+Region::Shape Region::Shape::SubtractShapes(const Shape& shape1,
+ const Shape& shape2) {
+ return ShapeOperation<SubtractOperation>(shape1, shape2);
+}
+
+#ifndef NDEBUG
+void Region::Dump() const {
+ printf("Bounds: (%d, %d, %d, %d)\n", bounds_.X(), bounds_.Y(),
+ bounds_.Width(), bounds_.Height());
+ shape_.Dump();
+}
+#endif
+
+void Region::Intersect(const Region& region) {
+ if (bounds_.IsEmpty())
+ return;
+ if (!bounds_.Intersects(region.bounds_)) {
+ shape_ = Shape();
+ bounds_ = IntRect();
+ return;
+ }
+
+ Shape intersected_shape = Shape::IntersectShapes(shape_, region.shape_);
+
+ shape_.Swap(intersected_shape);
+ bounds_ = shape_.Bounds();
+}
+
+void Region::Unite(const Region& region) {
+ if (region.IsEmpty())
+ return;
+ if (IsRect() && bounds_.Contains(region.bounds_))
+ return;
+ if (region.IsRect() && region.bounds_.Contains(bounds_)) {
+ shape_ = region.shape_;
+ bounds_ = region.bounds_;
+ return;
+ }
+ // FIXME: We may want another way to construct a Region without doing this
+ // test when we expect it to be false.
+ if (!IsRect() && Contains(region))
+ return;
+
+ Shape united_shape = Shape::UnionShapes(shape_, region.shape_);
+
+ shape_.Swap(united_shape);
+ bounds_.Unite(region.bounds_);
+}
+
+void Region::Subtract(const Region& region) {
+ if (bounds_.IsEmpty())
+ return;
+ if (region.IsEmpty())
+ return;
+ if (!bounds_.Intersects(region.bounds_))
+ return;
+
+ Shape subtracted_shape = Shape::SubtractShapes(shape_, region.shape_);
+
+ shape_.Swap(subtracted_shape);
+ bounds_ = shape_.Bounds();
+}
+
+void Region::Translate(const IntSize& offset) {
+ bounds_.Move(offset);
+ shape_.Translate(offset);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/region.h b/chromium/third_party/blink/renderer/platform/geometry/region.h
new file mode 100644
index 00000000000..e27ff5be910
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/region.h
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2010, 2011 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_REGION_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_REGION_H_
+
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT Region {
+ DISALLOW_NEW();
+
+ public:
+ Region();
+ Region(const IntRect&);
+
+ IntRect Bounds() const { return bounds_; }
+ bool IsEmpty() const { return bounds_.IsEmpty(); }
+ bool IsRect() const { return shape_.IsRect(); }
+
+ Vector<IntRect> Rects() const;
+
+ void Unite(const Region&);
+ void Intersect(const Region&);
+ void Subtract(const Region&);
+
+ void Translate(const IntSize&);
+
+ // Returns true if the query region is a subset of this region.
+ bool Contains(const Region&) const;
+
+ bool Contains(const IntPoint&) const;
+
+ // Returns true if the query region intersects any part of this region.
+ bool Intersects(const Region&) const;
+
+#ifndef NDEBUG
+ void Dump() const;
+#endif
+
+ private:
+ struct Span {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ Span(int y, size_t segment_index) : y(y), segment_index(segment_index) {}
+
+ int y;
+ size_t segment_index;
+ };
+
+ class Shape {
+ DISALLOW_NEW();
+
+ public:
+ Shape();
+ Shape(const IntRect&);
+ Shape(size_t segments_capacity, size_t spans_capacity);
+
+ IntRect Bounds() const;
+ bool IsEmpty() const { return spans_.IsEmpty(); }
+ bool IsRect() const { return spans_.size() <= 2 && segments_.size() <= 2; }
+
+ typedef const Span* SpanIterator;
+ SpanIterator SpansBegin() const;
+ SpanIterator SpansEnd() const;
+ size_t SpansSize() const { return spans_.size(); }
+
+ typedef const int* SegmentIterator;
+ SegmentIterator SegmentsBegin(SpanIterator) const;
+ SegmentIterator SegmentsEnd(SpanIterator) const;
+ size_t SegmentsSize() const { return segments_.size(); }
+
+ static Shape UnionShapes(const Shape& shape1, const Shape& shape2);
+ static Shape IntersectShapes(const Shape& shape1, const Shape& shape2);
+ static Shape SubtractShapes(const Shape& shape1, const Shape& shape2);
+
+ void Translate(const IntSize&);
+ void Swap(Shape&);
+
+ struct CompareContainsOperation;
+ struct CompareIntersectsOperation;
+
+ template <typename CompareOperation>
+ static bool CompareShapes(const Shape& shape1, const Shape& shape2);
+ void TrimCapacities();
+
+#ifndef NDEBUG
+ void Dump() const;
+#endif
+
+ private:
+ struct UnionOperation;
+ struct IntersectOperation;
+ struct SubtractOperation;
+
+ template <typename Operation>
+ static Shape ShapeOperation(const Shape& shape1, const Shape& shape2);
+
+ void AppendSegment(int x);
+ void AppendSpan(int y);
+ void AppendSpan(int y, SegmentIterator begin, SegmentIterator end);
+ void AppendSpans(const Shape&, SpanIterator begin, SpanIterator end);
+
+ bool CanCoalesce(SegmentIterator begin, SegmentIterator end);
+
+ Vector<int, 32> segments_;
+ Vector<Span, 16> spans_;
+
+ friend bool operator==(const Shape&, const Shape&);
+ };
+
+ IntRect bounds_;
+ Shape shape_;
+
+ friend bool operator==(const Region&, const Region&);
+ friend bool operator==(const Shape&, const Shape&);
+ friend bool operator==(const Span&, const Span&);
+};
+
+static inline Region Intersect(const Region& a, const Region& b) {
+ Region result(a);
+ result.Intersect(b);
+
+ return result;
+}
+
+static inline Region Subtract(const Region& a, const Region& b) {
+ Region result(a);
+ result.Subtract(b);
+
+ return result;
+}
+
+static inline Region Translate(const Region& region, const IntSize& offset) {
+ Region result(region);
+ result.Translate(offset);
+
+ return result;
+}
+
+inline bool operator==(const Region& a, const Region& b) {
+ return a.bounds_ == b.bounds_ && a.shape_ == b.shape_;
+}
+
+inline bool operator==(const Region::Shape& a, const Region::Shape& b) {
+ return a.spans_ == b.spans_ && a.segments_ == b.segments_;
+}
+
+inline bool operator==(const Region::Span& a, const Region::Span& b) {
+ return a.y == b.y && a.segment_index == b.segment_index;
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_REGION_H_
diff --git a/chromium/third_party/blink/renderer/platform/geometry/region_test.cc b/chromium/third_party/blink/renderer/platform/geometry/region_test.cc
new file mode 100644
index 00000000000..2510b9d5e5a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/region_test.cc
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2012 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/geometry/region.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+#define TEST_INSIDE_RECT(r, x, y, w, h) \
+ EXPECT_TRUE(r.Contains(IntPoint(x, y))); \
+ EXPECT_TRUE(r.Contains(IntPoint(x + w - 1, y))); \
+ EXPECT_TRUE(r.Contains(IntPoint(x, y + h - 1))); \
+ EXPECT_TRUE(r.Contains(IntPoint(x + w - 1, y + h - 1))); \
+ EXPECT_TRUE(r.Contains(IntPoint(x, y + h / 2))); \
+ EXPECT_TRUE(r.Contains(IntPoint(x + w - 1, y + h / 2))); \
+ EXPECT_TRUE(r.Contains(IntPoint(x + w / 2, y))); \
+ EXPECT_TRUE(r.Contains(IntPoint(x + w / 2, y + h - 1))); \
+ EXPECT_TRUE(r.Contains(IntPoint(x + w / 2, y + h / 2)));
+
+#define TEST_LEFT_OF_RECT(r, x, y, w, h) \
+ EXPECT_FALSE(r.Contains(IntPoint(x - 1, y))); \
+ EXPECT_FALSE(r.Contains(IntPoint(x - 1, y + h - 1)));
+
+#define TEST_RIGHT_OF_RECT(r, x, y, w, h) \
+ EXPECT_FALSE(r.Contains(IntPoint(x + w, y))); \
+ EXPECT_FALSE(r.Contains(IntPoint(x + w, y + h - 1)));
+
+#define TEST_TOP_OF_RECT(r, x, y, w, h) \
+ EXPECT_FALSE(r.Contains(IntPoint(x, y - 1))); \
+ EXPECT_FALSE(r.Contains(IntPoint(x + w - 1, y - 1)));
+
+#define TEST_BOTTOM_OF_RECT(r, x, y, w, h) \
+ EXPECT_FALSE(r.Contains(IntPoint(x, y + h))); \
+ EXPECT_FALSE(r.Contains(IntPoint(x + w - 1, y + h)));
+
+TEST(RegionTest, containsPoint) {
+ Region r;
+
+ EXPECT_FALSE(r.Contains(IntPoint(0, 0)));
+
+ r.Unite(IntRect(35, 35, 1, 1));
+ TEST_INSIDE_RECT(r, 35, 35, 1, 1);
+ TEST_LEFT_OF_RECT(r, 35, 35, 1, 1);
+ TEST_RIGHT_OF_RECT(r, 35, 35, 1, 1);
+ TEST_TOP_OF_RECT(r, 35, 35, 1, 1);
+ TEST_BOTTOM_OF_RECT(r, 35, 35, 1, 1);
+
+ r.Unite(IntRect(30, 30, 10, 10));
+ TEST_INSIDE_RECT(r, 30, 30, 10, 10);
+ TEST_LEFT_OF_RECT(r, 30, 30, 10, 10);
+ TEST_RIGHT_OF_RECT(r, 30, 30, 10, 10);
+ TEST_TOP_OF_RECT(r, 30, 30, 10, 10);
+ TEST_BOTTOM_OF_RECT(r, 30, 30, 10, 10);
+
+ r.Unite(IntRect(31, 40, 10, 10));
+ EXPECT_FALSE(r.Contains(IntPoint(30, 40)));
+ EXPECT_TRUE(r.Contains(IntPoint(31, 40)));
+ EXPECT_FALSE(r.Contains(IntPoint(40, 39)));
+ EXPECT_TRUE(r.Contains(IntPoint(40, 40)));
+
+ TEST_INSIDE_RECT(r, 30, 30, 10, 10);
+ TEST_LEFT_OF_RECT(r, 30, 30, 10, 10);
+ TEST_RIGHT_OF_RECT(r, 30, 30, 10, 10);
+ TEST_TOP_OF_RECT(r, 30, 30, 10, 10);
+ TEST_INSIDE_RECT(r, 31, 40, 10, 10);
+ TEST_LEFT_OF_RECT(r, 31, 40, 10, 10);
+ TEST_RIGHT_OF_RECT(r, 31, 40, 10, 10);
+ TEST_BOTTOM_OF_RECT(r, 31, 40, 10, 10);
+
+ r.Unite(IntRect(42, 40, 10, 10));
+
+ TEST_INSIDE_RECT(r, 42, 40, 10, 10);
+ TEST_LEFT_OF_RECT(r, 42, 40, 10, 10);
+ TEST_RIGHT_OF_RECT(r, 42, 40, 10, 10);
+ TEST_TOP_OF_RECT(r, 42, 40, 10, 10);
+ TEST_BOTTOM_OF_RECT(r, 42, 40, 10, 10);
+
+ TEST_INSIDE_RECT(r, 30, 30, 10, 10);
+ TEST_LEFT_OF_RECT(r, 30, 30, 10, 10);
+ TEST_RIGHT_OF_RECT(r, 30, 30, 10, 10);
+ TEST_TOP_OF_RECT(r, 30, 30, 10, 10);
+ TEST_INSIDE_RECT(r, 31, 40, 10, 10);
+ TEST_LEFT_OF_RECT(r, 31, 40, 10, 10);
+ TEST_RIGHT_OF_RECT(r, 31, 40, 10, 10);
+ TEST_BOTTOM_OF_RECT(r, 31, 40, 10, 10);
+}
+
+TEST(RegionTest, emptySpan) {
+ Region r;
+ r.Unite(IntRect(5, 0, 10, 10));
+ r.Unite(IntRect(0, 5, 10, 10));
+ r.Subtract(IntRect(7, 7, 10, 0));
+
+ Vector<IntRect> rects = r.Rects();
+ for (size_t i = 0; i < rects.size(); ++i)
+ EXPECT_FALSE(rects[i].IsEmpty());
+}
+
+#define TEST_NO_INTERSECT(a, b) \
+ { \
+ Region ar = a; \
+ Region br = b; \
+ EXPECT_FALSE(ar.Intersects(br)); \
+ EXPECT_FALSE(br.Intersects(ar)); \
+ }
+
+#define TEST_INTERSECT(a, b) \
+ { \
+ Region ar = a; \
+ Region br = b; \
+ EXPECT_TRUE(ar.Intersects(br)); \
+ EXPECT_TRUE(br.Intersects(ar)); \
+ }
+
+TEST(RegionTest, intersectsRegion) {
+ Region r;
+
+ TEST_NO_INTERSECT(IntRect(), IntRect());
+ TEST_NO_INTERSECT(IntRect(), IntRect(0, 0, 1, 1));
+ TEST_NO_INTERSECT(IntRect(), IntRect(1, 1, 1, 1));
+
+ r.Unite(IntRect(0, 0, 1, 1));
+ TEST_NO_INTERSECT(r, IntRect());
+ TEST_INTERSECT(r, IntRect(0, 0, 1, 1));
+ TEST_INTERSECT(r, IntRect(0, 0, 2, 2));
+ TEST_INTERSECT(r, IntRect(-1, 0, 2, 2));
+ TEST_INTERSECT(r, IntRect(-1, -1, 2, 2));
+ TEST_INTERSECT(r, IntRect(0, -1, 2, 2));
+ TEST_INTERSECT(r, IntRect(-1, -1, 3, 3));
+
+ r.Unite(IntRect(0, 0, 3, 3));
+ r.Unite(IntRect(10, 0, 3, 3));
+ r.Unite(IntRect(0, 10, 13, 3));
+ TEST_NO_INTERSECT(r, IntRect());
+ TEST_INTERSECT(r, IntRect(1, 1, 1, 1));
+ TEST_INTERSECT(r, IntRect(0, 0, 2, 2));
+ TEST_INTERSECT(r, IntRect(1, 0, 2, 2));
+ TEST_INTERSECT(r, IntRect(1, 1, 2, 2));
+ TEST_INTERSECT(r, IntRect(0, 1, 2, 2));
+ TEST_INTERSECT(r, IntRect(0, 0, 3, 3));
+ TEST_INTERSECT(r, IntRect(-1, -1, 2, 2));
+ TEST_INTERSECT(r, IntRect(2, -1, 2, 2));
+ TEST_INTERSECT(r, IntRect(2, 2, 2, 2));
+ TEST_INTERSECT(r, IntRect(-1, 2, 2, 2));
+
+ TEST_INTERSECT(r, IntRect(11, 1, 1, 1));
+ TEST_INTERSECT(r, IntRect(10, 0, 2, 2));
+ TEST_INTERSECT(r, IntRect(11, 0, 2, 2));
+ TEST_INTERSECT(r, IntRect(11, 1, 2, 2));
+ TEST_INTERSECT(r, IntRect(10, 1, 2, 2));
+ TEST_INTERSECT(r, IntRect(10, 0, 3, 3));
+ TEST_INTERSECT(r, IntRect(9, -1, 2, 2));
+ TEST_INTERSECT(r, IntRect(12, -1, 2, 2));
+ TEST_INTERSECT(r, IntRect(12, 2, 2, 2));
+ TEST_INTERSECT(r, IntRect(9, 2, 2, 2));
+
+ TEST_INTERSECT(r, IntRect(0, -1, 13, 5));
+ TEST_INTERSECT(r, IntRect(1, -1, 11, 5));
+ TEST_INTERSECT(r, IntRect(2, -1, 9, 5));
+ TEST_INTERSECT(r, IntRect(2, -1, 8, 5));
+ TEST_INTERSECT(r, IntRect(3, -1, 8, 5));
+ TEST_NO_INTERSECT(r, IntRect(3, -1, 7, 5));
+
+ TEST_INTERSECT(r, IntRect(0, 1, 13, 1));
+ TEST_INTERSECT(r, IntRect(1, 1, 11, 1));
+ TEST_INTERSECT(r, IntRect(2, 1, 9, 1));
+ TEST_INTERSECT(r, IntRect(2, 1, 8, 1));
+ TEST_INTERSECT(r, IntRect(3, 1, 8, 1));
+ TEST_NO_INTERSECT(r, IntRect(3, 1, 7, 1));
+
+ TEST_INTERSECT(r, IntRect(0, 0, 13, 13));
+ TEST_INTERSECT(r, IntRect(0, 1, 13, 11));
+ TEST_INTERSECT(r, IntRect(0, 2, 13, 9));
+ TEST_INTERSECT(r, IntRect(0, 2, 13, 8));
+ TEST_INTERSECT(r, IntRect(0, 3, 13, 8));
+ TEST_NO_INTERSECT(r, IntRect(0, 3, 13, 7));
+}
+
+TEST(RegionTest, ReadPastFullSpanVectorInIntersectsTest) {
+ Region r;
+
+ // This region has enough spans to fill its allocated Vector exactly.
+ r.Unite(IntRect(400, 300, 1, 800));
+ r.Unite(IntRect(785, 585, 1, 1));
+ r.Unite(IntRect(787, 585, 1, 1));
+ r.Unite(IntRect(0, 587, 16, 162));
+ r.Unite(IntRect(26, 590, 300, 150));
+ r.Unite(IntRect(196, 750, 1, 1));
+ r.Unite(IntRect(0, 766, 1, 1));
+ r.Unite(IntRect(0, 782, 1, 1));
+ r.Unite(IntRect(745, 798, 1, 1));
+ r.Unite(IntRect(795, 882, 10, 585));
+ r.Unite(IntRect(100, 1499, 586, 1));
+ r.Unite(IntRect(100, 1500, 585, 784));
+ // This query rect goes past the bottom of the Region, causing the
+ // test to reach the last span and try go past it. It should not read
+ // memory off the end of the span Vector.
+ TEST_NO_INTERSECT(r, IntRect(0, 2184, 1, 150));
+}
+
+#define TEST_NO_CONTAINS(a, b) \
+ { \
+ Region ar = a; \
+ Region br = b; \
+ EXPECT_FALSE(ar.Contains(br)); \
+ }
+
+#define TEST_CONTAINS(a, b) \
+ { \
+ Region ar = a; \
+ Region br = b; \
+ EXPECT_TRUE(ar.Contains(br)); \
+ }
+
+TEST(RegionTest, containsRegion) {
+ TEST_CONTAINS(IntRect(), IntRect());
+ TEST_NO_CONTAINS(IntRect(), IntRect(0, 0, 1, 1));
+ TEST_NO_CONTAINS(IntRect(), IntRect(1, 1, 1, 1));
+
+ TEST_NO_CONTAINS(IntRect(10, 10, 1, 1), IntRect(11, 10, 1, 1));
+ TEST_NO_CONTAINS(IntRect(10, 10, 1, 1), IntRect(10, 11, 1, 1));
+ TEST_NO_CONTAINS(IntRect(10, 10, 1, 1), IntRect(9, 10, 1, 1));
+ TEST_NO_CONTAINS(IntRect(10, 10, 1, 1), IntRect(10, 9, 1, 1));
+ TEST_NO_CONTAINS(IntRect(10, 10, 1, 1), IntRect(9, 9, 2, 2));
+ TEST_NO_CONTAINS(IntRect(10, 10, 1, 1), IntRect(10, 9, 2, 2));
+ TEST_NO_CONTAINS(IntRect(10, 10, 1, 1), IntRect(9, 10, 2, 2));
+ TEST_NO_CONTAINS(IntRect(10, 10, 1, 1), IntRect(10, 10, 2, 2));
+ TEST_NO_CONTAINS(IntRect(10, 10, 1, 1), IntRect(9, 9, 3, 3));
+
+ Region h_lines;
+ for (int i = 10; i < 20; i += 2)
+ h_lines.Unite(IntRect(i, 10, 1, 10));
+
+ TEST_CONTAINS(IntRect(10, 10, 9, 10), h_lines);
+ TEST_NO_CONTAINS(IntRect(10, 10, 9, 9), h_lines);
+ TEST_NO_CONTAINS(IntRect(10, 11, 9, 9), h_lines);
+ TEST_NO_CONTAINS(IntRect(10, 10, 8, 10), h_lines);
+ TEST_NO_CONTAINS(IntRect(11, 10, 8, 10), h_lines);
+
+ Region v_lines;
+ for (int i = 10; i < 20; i += 2)
+ v_lines.Unite(IntRect(10, i, 10, 1));
+
+ TEST_CONTAINS(IntRect(10, 10, 10, 9), v_lines);
+ TEST_NO_CONTAINS(IntRect(10, 10, 9, 9), v_lines);
+ TEST_NO_CONTAINS(IntRect(11, 10, 9, 9), v_lines);
+ TEST_NO_CONTAINS(IntRect(10, 10, 10, 8), v_lines);
+ TEST_NO_CONTAINS(IntRect(10, 11, 10, 8), v_lines);
+
+ Region grid;
+ for (int i = 10; i < 20; i += 2)
+ for (int j = 10; j < 20; j += 2)
+ grid.Unite(IntRect(i, j, 1, 1));
+
+ TEST_CONTAINS(IntRect(10, 10, 9, 9), grid);
+ TEST_NO_CONTAINS(IntRect(10, 10, 9, 8), grid);
+ TEST_NO_CONTAINS(IntRect(10, 11, 9, 8), grid);
+ TEST_NO_CONTAINS(IntRect(10, 10, 8, 9), grid);
+ TEST_NO_CONTAINS(IntRect(11, 10, 8, 9), grid);
+
+ TEST_CONTAINS(h_lines, h_lines);
+ TEST_CONTAINS(v_lines, v_lines);
+ TEST_NO_CONTAINS(v_lines, h_lines);
+ TEST_NO_CONTAINS(h_lines, v_lines);
+ TEST_CONTAINS(grid, grid);
+ TEST_CONTAINS(h_lines, grid);
+ TEST_CONTAINS(v_lines, grid);
+ TEST_NO_CONTAINS(grid, h_lines);
+ TEST_NO_CONTAINS(grid, v_lines);
+
+ for (int i = 10; i < 20; i += 2)
+ TEST_CONTAINS(h_lines, IntRect(i, 10, 1, 10));
+
+ for (int i = 10; i < 20; i += 2)
+ TEST_CONTAINS(v_lines, IntRect(10, i, 10, 1));
+
+ for (int i = 10; i < 20; i += 2)
+ for (int j = 10; j < 20; j += 2)
+ TEST_CONTAINS(grid, IntRect(i, j, 1, 1));
+
+ Region container;
+ container.Unite(IntRect(0, 0, 40, 20));
+ container.Unite(IntRect(0, 20, 41, 20));
+ TEST_CONTAINS(container, IntRect(5, 5, 30, 30));
+
+ container = Region();
+ container.Unite(IntRect(0, 0, 10, 10));
+ container.Unite(IntRect(0, 30, 10, 10));
+ container.Unite(IntRect(30, 30, 10, 10));
+ container.Unite(IntRect(30, 0, 10, 10));
+ TEST_NO_CONTAINS(container, IntRect(5, 5, 30, 30));
+
+ container = Region();
+ container.Unite(IntRect(0, 0, 10, 10));
+ container.Unite(IntRect(0, 30, 10, 10));
+ container.Unite(IntRect(30, 0, 10, 40));
+ TEST_NO_CONTAINS(container, IntRect(5, 5, 30, 30));
+
+ container = Region();
+ container.Unite(IntRect(30, 0, 10, 10));
+ container.Unite(IntRect(30, 30, 10, 10));
+ container.Unite(IntRect(0, 0, 10, 40));
+ TEST_NO_CONTAINS(container, IntRect(5, 5, 30, 30));
+
+ container = Region();
+ container.Unite(IntRect(0, 0, 10, 40));
+ container.Unite(IntRect(30, 0, 10, 40));
+ TEST_NO_CONTAINS(container, IntRect(5, 5, 30, 30));
+
+ container = Region();
+ container.Unite(IntRect(0, 0, 40, 40));
+ TEST_NO_CONTAINS(container, IntRect(10, -1, 20, 10));
+
+ container = Region();
+ container.Unite(IntRect(0, 0, 40, 40));
+ TEST_NO_CONTAINS(container, IntRect(10, 31, 20, 10));
+
+ container = Region();
+ container.Unite(IntRect(0, 0, 40, 20));
+ container.Unite(IntRect(0, 20, 41, 20));
+ TEST_NO_CONTAINS(container, IntRect(-1, 10, 10, 20));
+
+ container = Region();
+ container.Unite(IntRect(0, 0, 40, 20));
+ container.Unite(IntRect(0, 20, 41, 20));
+ TEST_NO_CONTAINS(container, IntRect(31, 10, 10, 20));
+
+ container = Region();
+ container.Unite(IntRect(0, 0, 40, 40));
+ container.Subtract(IntRect(0, 20, 60, 0));
+ TEST_NO_CONTAINS(container, IntRect(31, 10, 10, 20));
+}
+
+TEST(RegionTest, unite) {
+ Region r;
+ Region r2;
+
+ // A rect uniting a contained rect does not change the region.
+ r2 = r = IntRect(0, 0, 50, 50);
+ r2.Unite(IntRect(20, 20, 10, 10));
+ EXPECT_EQ(r, r2);
+
+ // A rect uniting a containing rect gives back the containing rect.
+ r = IntRect(0, 0, 50, 50);
+ r.Unite(IntRect(0, 0, 100, 100));
+ EXPECT_EQ(Region(IntRect(0, 0, 100, 100)), r);
+
+ // A complex region uniting a contained rect does not change the region.
+ r = IntRect(0, 0, 50, 50);
+ r.Unite(IntRect(100, 0, 50, 50));
+ r2 = r;
+ r2.Unite(IntRect(20, 20, 10, 10));
+ EXPECT_EQ(r, r2);
+
+ // A complex region uniting a containing rect gives back the containing rect.
+ r = IntRect(0, 0, 50, 50);
+ r.Unite(IntRect(100, 0, 50, 50));
+ r.Unite(IntRect(0, 0, 500, 500));
+ EXPECT_EQ(Region(IntRect(0, 0, 500, 500)), r);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/transform_state.cc b/chromium/third_party/blink/renderer/platform/geometry/transform_state.cc
new file mode 100644
index 00000000000..305b9f7ba7c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/transform_state.cc
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2011 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/geometry/transform_state.h"
+
+namespace blink {
+
+TransformState& TransformState::operator=(const TransformState& other) {
+ accumulated_offset_ = other.accumulated_offset_;
+ map_point_ = other.map_point_;
+ map_quad_ = other.map_quad_;
+ if (map_point_)
+ last_planar_point_ = other.last_planar_point_;
+ if (map_quad_)
+ last_planar_quad_ = other.last_planar_quad_;
+ accumulating_transform_ = other.accumulating_transform_;
+ force_accumulating_transform_ = other.force_accumulating_transform_;
+ direction_ = other.direction_;
+
+ accumulated_transform_.reset();
+
+ if (other.accumulated_transform_)
+ accumulated_transform_ =
+ TransformationMatrix::Create(*other.accumulated_transform_);
+
+ return *this;
+}
+
+void TransformState::TranslateTransform(const LayoutSize& offset) {
+ if (direction_ == kApplyTransformDirection) {
+ accumulated_transform_->PostTranslate(offset.Width().ToDouble(),
+ offset.Height().ToDouble());
+ } else {
+ accumulated_transform_->Translate(offset.Width().ToDouble(),
+ offset.Height().ToDouble());
+ }
+}
+
+void TransformState::TranslateMappedCoordinates(const LayoutSize& offset) {
+ FloatSize adjusted_offset((direction_ == kApplyTransformDirection) ? offset
+ : -offset);
+ if (map_point_)
+ last_planar_point_.Move(adjusted_offset);
+ if (map_quad_)
+ last_planar_quad_.Move(adjusted_offset);
+}
+
+void TransformState::Move(const LayoutSize& offset,
+ TransformAccumulation accumulate) {
+ if (force_accumulating_transform_)
+ accumulate = kAccumulateTransform;
+
+ if (accumulate == kFlattenTransform || !accumulated_transform_) {
+ accumulated_offset_ += offset;
+ } else {
+ ApplyAccumulatedOffset();
+ if (accumulating_transform_ && accumulated_transform_) {
+ // If we're accumulating into an existing transform, apply the
+ // translation.
+ TranslateTransform(offset);
+ } else {
+ // Just move the point and/or quad.
+ TranslateMappedCoordinates(offset);
+ }
+ }
+ accumulating_transform_ = accumulate == kAccumulateTransform;
+}
+
+void TransformState::ApplyAccumulatedOffset() {
+ LayoutSize offset = accumulated_offset_;
+ accumulated_offset_ = LayoutSize();
+ if (!offset.IsZero()) {
+ if (accumulated_transform_) {
+ TranslateTransform(offset);
+ Flatten();
+ } else {
+ TranslateMappedCoordinates(offset);
+ }
+ }
+}
+
+// FIXME: We transform AffineTransform to TransformationMatrix. This is rather
+// inefficient.
+void TransformState::ApplyTransform(
+ const AffineTransform& transform_from_container,
+ TransformAccumulation accumulate,
+ bool* was_clamped) {
+ ApplyTransform(transform_from_container.ToTransformationMatrix(), accumulate,
+ was_clamped);
+}
+
+void TransformState::ApplyTransform(
+ const TransformationMatrix& transform_from_container,
+ TransformAccumulation accumulate,
+ bool* was_clamped) {
+ if (was_clamped)
+ *was_clamped = false;
+
+ if (transform_from_container.IsIntegerTranslation()) {
+ Move(LayoutSize(LayoutUnit(transform_from_container.E()),
+ LayoutUnit(transform_from_container.F())),
+ accumulate);
+ return;
+ }
+
+ ApplyAccumulatedOffset();
+
+ // If we have an accumulated transform from last time, multiply in this
+ // transform
+ if (accumulated_transform_) {
+ if (direction_ == kApplyTransformDirection)
+ accumulated_transform_ = TransformationMatrix::Create(
+ transform_from_container * *accumulated_transform_);
+ else
+ accumulated_transform_->Multiply(transform_from_container);
+ } else if (accumulate == kAccumulateTransform) {
+ // Make one if we started to accumulate
+ accumulated_transform_ =
+ TransformationMatrix::Create(transform_from_container);
+ }
+
+ if (accumulate == kFlattenTransform) {
+ if (force_accumulating_transform_) {
+ accumulated_transform_->FlattenTo2d();
+ } else {
+ const TransformationMatrix* final_transform =
+ accumulated_transform_ ? accumulated_transform_.get()
+ : &transform_from_container;
+ FlattenWithTransform(*final_transform, was_clamped);
+ }
+ }
+ accumulating_transform_ =
+ accumulate == kAccumulateTransform || force_accumulating_transform_;
+}
+
+void TransformState::Flatten(bool* was_clamped) {
+ DCHECK(!force_accumulating_transform_);
+ if (was_clamped)
+ *was_clamped = false;
+
+ ApplyAccumulatedOffset();
+
+ if (!accumulated_transform_) {
+ accumulating_transform_ = false;
+ return;
+ }
+
+ FlattenWithTransform(*accumulated_transform_, was_clamped);
+}
+
+FloatPoint TransformState::MappedPoint(bool* was_clamped) const {
+ if (was_clamped)
+ *was_clamped = false;
+
+ FloatPoint point = last_planar_point_;
+ point.Move((direction_ == kApplyTransformDirection) ? accumulated_offset_
+ : -accumulated_offset_);
+ if (!accumulated_transform_)
+ return point;
+
+ if (direction_ == kApplyTransformDirection)
+ return accumulated_transform_->MapPoint(point);
+
+ return accumulated_transform_->Inverse().ProjectPoint(point, was_clamped);
+}
+
+FloatQuad TransformState::MappedQuad(bool* was_clamped) const {
+ if (was_clamped)
+ *was_clamped = false;
+
+ FloatQuad quad = last_planar_quad_;
+ quad.Move(FloatSize((direction_ == kApplyTransformDirection)
+ ? accumulated_offset_
+ : -accumulated_offset_));
+ if (!accumulated_transform_)
+ return quad;
+
+ if (direction_ == kApplyTransformDirection)
+ return accumulated_transform_->MapQuad(quad);
+
+ return accumulated_transform_->Inverse().ProjectQuad(quad, was_clamped);
+}
+
+const TransformationMatrix& TransformState::AccumulatedTransform() const {
+ DCHECK(force_accumulating_transform_);
+ DCHECK(accumulating_transform_);
+ return *accumulated_transform_;
+}
+
+void TransformState::FlattenWithTransform(const TransformationMatrix& t,
+ bool* was_clamped) {
+ if (direction_ == kApplyTransformDirection) {
+ if (map_point_)
+ last_planar_point_ = t.MapPoint(last_planar_point_);
+ if (map_quad_)
+ last_planar_quad_ = t.MapQuad(last_planar_quad_);
+ } else {
+ TransformationMatrix inverse_transform = t.Inverse();
+ if (map_point_)
+ last_planar_point_ = inverse_transform.ProjectPoint(last_planar_point_);
+ if (map_quad_)
+ last_planar_quad_ =
+ inverse_transform.ProjectQuad(last_planar_quad_, was_clamped);
+ }
+
+ // We could throw away m_accumulatedTransform if we wanted to here, but that
+ // would cause thrash when traversing hierarchies with alternating
+ // preserve-3d and flat elements.
+ if (accumulated_transform_)
+ accumulated_transform_->MakeIdentity();
+ accumulating_transform_ = false;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/geometry/transform_state.h b/chromium/third_party/blink/renderer/platform/geometry/transform_state.h
new file mode 100644
index 00000000000..034ffeb36c2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/geometry/transform_state.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2011 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_TRANSFORM_STATE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_TRANSFORM_STATE_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/geometry/float_point.h"
+#include "third_party/blink/renderer/platform/geometry/float_quad.h"
+#include "third_party/blink/renderer/platform/geometry/int_size.h"
+#include "third_party/blink/renderer/platform/geometry/layout_point.h"
+#include "third_party/blink/renderer/platform/geometry/layout_size.h"
+#include "third_party/blink/renderer/platform/transforms/affine_transform.h"
+#include "third_party/blink/renderer/platform/transforms/transformation_matrix.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT TransformState {
+ STACK_ALLOCATED();
+
+ public:
+ enum TransformDirection {
+ kApplyTransformDirection,
+ kUnapplyInverseTransformDirection
+ };
+ enum TransformAccumulation { kFlattenTransform, kAccumulateTransform };
+
+ TransformState(TransformDirection mapping_direction,
+ const FloatPoint& p,
+ const FloatQuad& quad)
+ : last_planar_point_(p),
+ last_planar_quad_(quad),
+ accumulating_transform_(false),
+ force_accumulating_transform_(false),
+ map_point_(true),
+ map_quad_(true),
+ direction_(mapping_direction) {}
+
+ TransformState(TransformDirection mapping_direction, const FloatPoint& p)
+ : last_planar_point_(p),
+ accumulating_transform_(false),
+ force_accumulating_transform_(false),
+ map_point_(true),
+ map_quad_(false),
+ direction_(mapping_direction) {}
+
+ TransformState(TransformDirection mapping_direction, const FloatQuad& quad)
+ : last_planar_quad_(quad),
+ accumulating_transform_(false),
+ force_accumulating_transform_(false),
+ map_point_(false),
+ map_quad_(true),
+ direction_(mapping_direction) {}
+
+ // Accumulate a transform but don't map any points directly.
+ TransformState(TransformDirection mapping_direction)
+ : accumulated_transform_(TransformationMatrix::Create()),
+ accumulating_transform_(true),
+ force_accumulating_transform_(true),
+ map_point_(false),
+ map_quad_(false),
+ direction_(mapping_direction) {}
+
+ TransformState(const TransformState& other) { *this = other; }
+
+ TransformState& operator=(const TransformState&);
+
+ // Note: this overrides the quad and ignores any accumulatedOffset.
+ // If it's desired to include the offset, call flatten() first.
+ void SetQuad(const FloatQuad& quad) {
+ DCHECK(!accumulating_transform_);
+ // FIXME: this assumes that the quad being added is in the coordinate system
+ // of the current state. This breaks if we're simultaneously mapping a
+ // point. https://bugs.webkit.org/show_bug.cgi?id=106680
+ DCHECK(!map_point_);
+ accumulated_offset_ = LayoutSize();
+ last_planar_quad_ = quad;
+ }
+
+ void Move(LayoutUnit x,
+ LayoutUnit y,
+ TransformAccumulation accumulate = kFlattenTransform) {
+ Move(LayoutSize(x, y), accumulate);
+ }
+
+ void Move(const LayoutSize&, TransformAccumulation = kFlattenTransform);
+ void Move(const IntSize& size,
+ TransformAccumulation accumulate = kFlattenTransform) {
+ Move(LayoutSize(size), accumulate);
+ }
+ void MoveBy(const LayoutPoint& point,
+ TransformAccumulation accumulate = kFlattenTransform) {
+ Move(LayoutSize(point.X(), point.Y()), accumulate);
+ }
+ void ApplyTransform(const AffineTransform& transform_from_container,
+ TransformAccumulation = kFlattenTransform,
+ bool* was_clamped = nullptr);
+ void ApplyTransform(const TransformationMatrix& transform_from_container,
+ TransformAccumulation = kFlattenTransform,
+ bool* was_clamped = nullptr);
+ void Flatten(bool* was_clamped = nullptr);
+
+ // Return the coords of the point or quad in the last flattened layer
+ FloatPoint LastPlanarPoint() const { return last_planar_point_; }
+ FloatQuad LastPlanarQuad() const { return last_planar_quad_; }
+
+ // Return the point or quad mapped through the current transform
+ FloatPoint MappedPoint(bool* was_clamped = nullptr) const;
+ FloatQuad MappedQuad(bool* was_clamped = nullptr) const;
+
+ // Return the accumulated transform.
+ const TransformationMatrix& AccumulatedTransform() const;
+
+ private:
+ void TranslateTransform(const LayoutSize&);
+ void TranslateMappedCoordinates(const LayoutSize&);
+ void FlattenWithTransform(const TransformationMatrix&, bool* was_clamped);
+ void ApplyAccumulatedOffset();
+
+ FloatPoint last_planar_point_;
+ FloatQuad last_planar_quad_;
+
+ // We only allocate the transform if we need to
+ std::unique_ptr<TransformationMatrix> accumulated_transform_;
+ LayoutSize accumulated_offset_;
+ bool accumulating_transform_;
+ bool force_accumulating_transform_;
+ bool map_point_, map_quad_;
+ TransformDirection direction_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_TRANSFORM_STATE_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/DEPS b/chromium/third_party/blink/renderer/platform/graphics/DEPS
new file mode 100644
index 00000000000..865fc0fdd3a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/DEPS
@@ -0,0 +1,32 @@
+include_rules = [
+ # To whitelist base/ stuff Blink is allowed to include, we list up all
+ # directories and files instead of writing 'base/'.
+ "+base/bind.h",
+ "+base/callback.h",
+ "+base/compiler_specific.h",
+ "+base/message_loop",
+ "+base/task_runner_util.h",
+ "+base/threading/sequenced_task_runner_handle.h",
+ "+base/threading/thread.h",
+ "+base/threading/thread_checker.h",
+ "+cc",
+ "-cc/blink",
+ "+components/viz/common",
+ "+components/viz/test/fake_external_begin_frame_source.h",
+ "+gpu/command_buffer/client/gles2_interface.h",
+ "+gpu/command_buffer/client/gpu_memory_buffer_manager.h",
+ "+gpu/command_buffer/common/gpu_memory_buffer_support.h",
+ "+gpu/command_buffer/common/capabilities.h",
+ "+gpu/command_buffer/common/mailbox.h",
+ "+gpu/command_buffer/common/sync_token.h",
+ "+media/base/media_switches.h",
+ "+media/base/video_frame.h",
+ "+services/viz/public/interfaces",
+ "+third_party/blink/public/web/web_settings.h",
+]
+
+specific_include_rules = {
+ ".*_test.cc": [
+ "+components/viz/test",
+ ],
+}
diff --git a/chromium/third_party/blink/renderer/platform/graphics/OWNERS b/chromium/third_party/blink/renderer/platform/graphics/OWNERS
new file mode 100644
index 00000000000..7d080e35715
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/OWNERS
@@ -0,0 +1,15 @@
+# OWNERS specializing in graphics code.
+
+chrishtr@chromium.org
+flackr@chromium.org
+fmalita@chromium.org
+jbroman@chromium.org
+junov@chromium.org
+kbr@chromium.org
+pdr@chromium.org
+schenney@chromium.org
+senorblanco@chromium.org
+vollick@chromium.org
+
+# TEAM: paint-dev@chromium.org
+# COMPONENT: Blink>Paint
diff --git a/chromium/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.cc b/chromium/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.cc
new file mode 100644
index 00000000000..3598106da0b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.cc
@@ -0,0 +1,300 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h"
+
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "gpu/command_buffer/common/sync_token.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_graphics_context_3d_provider.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
+#include "third_party/blink/renderer/platform/graphics/mailbox_texture_holder.h"
+#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
+#include "third_party/blink/renderer/platform/graphics/skia_texture_holder.h"
+#include "third_party/blink/renderer/platform/web_task_runner.h"
+#include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/gpu/GrTexture.h"
+
+#include <memory>
+#include <utility>
+
+namespace blink {
+
+scoped_refptr<AcceleratedStaticBitmapImage>
+AcceleratedStaticBitmapImage::CreateFromSkImage(
+ sk_sp<SkImage> image,
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper>
+ context_provider_wrapper) {
+ CHECK(image && image->isTextureBacked());
+ return base::AdoptRef(new AcceleratedStaticBitmapImage(
+ std::move(image), std::move(context_provider_wrapper)));
+}
+
+scoped_refptr<AcceleratedStaticBitmapImage>
+AcceleratedStaticBitmapImage::CreateFromWebGLContextImage(
+ const gpu::Mailbox& mailbox,
+ const gpu::SyncToken& sync_token,
+ unsigned texture_id,
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&
+ context_provider_wrapper,
+ IntSize mailbox_size) {
+ return base::AdoptRef(new AcceleratedStaticBitmapImage(
+ mailbox, sync_token, texture_id, std::move(context_provider_wrapper),
+ mailbox_size));
+}
+
+AcceleratedStaticBitmapImage::AcceleratedStaticBitmapImage(
+ sk_sp<SkImage> image,
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&
+ context_provider_wrapper)
+ : paint_image_content_id_(cc::PaintImage::GetNextContentId()) {
+ CHECK(image && image->isTextureBacked());
+ texture_holder_ = std::make_unique<SkiaTextureHolder>(
+ std::move(image), std::move(context_provider_wrapper));
+ thread_checker_.DetachFromThread();
+}
+
+AcceleratedStaticBitmapImage::AcceleratedStaticBitmapImage(
+ const gpu::Mailbox& mailbox,
+ const gpu::SyncToken& sync_token,
+ unsigned texture_id,
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&
+ context_provider_wrapper,
+ IntSize mailbox_size)
+ : paint_image_content_id_(cc::PaintImage::GetNextContentId()) {
+ texture_holder_ = std::make_unique<MailboxTextureHolder>(
+ mailbox, sync_token, texture_id, std::move(context_provider_wrapper),
+ mailbox_size);
+ thread_checker_.DetachFromThread();
+}
+
+namespace {
+
+void DestroySkImageOnOriginalThread(
+ sk_sp<SkImage> image,
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,
+ std::unique_ptr<gpu::SyncToken> sync_token) {
+ if (context_provider_wrapper &&
+ image->isValid(
+ context_provider_wrapper->ContextProvider()->GetGrContext())) {
+ if (sync_token->HasData()) {
+ // To make sure skia does not recycle the texture while it is still in use
+ // by another context.
+ context_provider_wrapper->ContextProvider()
+ ->ContextGL()
+ ->WaitSyncTokenCHROMIUM(sync_token->GetData());
+ }
+ // In case texture was used by compositor, which may have changed params.
+ image->getTexture()->textureParamsModified();
+ }
+ // destroy by letting |image| go out of scope
+}
+
+} // unnamed namespace
+
+AcceleratedStaticBitmapImage::~AcceleratedStaticBitmapImage() {
+ // If the original SkImage was retained, it must be destroyed on the thread
+ // where it came from. In the same thread case, there is nothing to do because
+ // the regular destruction flow is fine.
+ if (original_skia_image_) {
+ std::unique_ptr<gpu::SyncToken> sync_token =
+ base::WrapUnique(new gpu::SyncToken(texture_holder_->GetSyncToken()));
+ if (original_skia_image_thread_id_ !=
+ Platform::Current()->CurrentThread()->ThreadId()) {
+ PostCrossThreadTask(
+ *original_skia_image_task_runner_, FROM_HERE,
+ CrossThreadBind(
+ &DestroySkImageOnOriginalThread, std::move(original_skia_image_),
+ std::move(original_skia_image_context_provider_wrapper_),
+ WTF::Passed(std::move(sync_token))));
+ } else {
+ DestroySkImageOnOriginalThread(
+ std::move(original_skia_image_),
+ std::move(original_skia_image_context_provider_wrapper_),
+ std::move(sync_token));
+ }
+ }
+}
+
+void AcceleratedStaticBitmapImage::RetainOriginalSkImage() {
+ DCHECK(texture_holder_->IsSkiaTextureHolder());
+ original_skia_image_ = texture_holder_->GetSkImage();
+ original_skia_image_context_provider_wrapper_ = ContextProviderWrapper();
+ DCHECK(original_skia_image_);
+ WebThread* thread = Platform::Current()->CurrentThread();
+ original_skia_image_thread_id_ = thread->ThreadId();
+ original_skia_image_task_runner_ = thread->GetTaskRunner();
+}
+
+IntSize AcceleratedStaticBitmapImage::Size() const {
+ return texture_holder_->Size();
+}
+
+scoped_refptr<StaticBitmapImage>
+AcceleratedStaticBitmapImage::MakeUnaccelerated() {
+ CreateImageFromMailboxIfNeeded();
+ return StaticBitmapImage::Create(
+ texture_holder_->GetSkImage()->makeNonTextureImage());
+}
+
+void AcceleratedStaticBitmapImage::UpdateSyncToken(gpu::SyncToken sync_token) {
+ texture_holder_->UpdateSyncToken(sync_token);
+}
+
+bool AcceleratedStaticBitmapImage::CopyToTexture(
+ gpu::gles2::GLES2Interface* dest_gl,
+ GLenum dest_target,
+ GLuint dest_texture_id,
+ bool unpack_premultiply_alpha,
+ bool unpack_flip_y,
+ const IntPoint& dest_point,
+ const IntRect& source_sub_rectangle) {
+ CheckThread();
+ if (!IsValid())
+ return false;
+ // This method should only be used for cross-context copying, otherwise it's
+ // wasting overhead.
+ DCHECK(texture_holder_->IsCrossThread() ||
+ dest_gl != ContextProviderWrapper()->ContextProvider()->ContextGL());
+
+ // TODO(junov) : could reduce overhead by using kOrderingBarrier when we know
+ // that the source and destination context or on the same stream.
+ EnsureMailbox(kUnverifiedSyncToken, GL_NEAREST);
+
+ // Get a texture id that |destProvider| knows about and copy from it.
+ dest_gl->WaitSyncTokenCHROMIUM(
+ texture_holder_->GetSyncToken().GetConstData());
+ GLuint source_texture_id = dest_gl->CreateAndConsumeTextureCHROMIUM(
+ texture_holder_->GetMailbox().name);
+ dest_gl->CopySubTextureCHROMIUM(
+ source_texture_id, 0, dest_target, dest_texture_id, 0, dest_point.X(),
+ dest_point.Y(), source_sub_rectangle.X(), source_sub_rectangle.Y(),
+ source_sub_rectangle.Width(), source_sub_rectangle.Height(),
+ unpack_flip_y ? GL_FALSE : GL_TRUE, GL_FALSE,
+ unpack_premultiply_alpha ? GL_FALSE : GL_TRUE);
+ // This drops the |destGL| context's reference on our |m_mailbox|, but it's
+ // still held alive by our SkImage.
+ dest_gl->DeleteTextures(1, &source_texture_id);
+
+ // We need to update the texture holder's sync token to ensure that when this
+ // image is deleted, the texture resource will not be recycled by skia before
+ // the above texture copy has completed.
+ gpu::SyncToken sync_token;
+ dest_gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
+ texture_holder_->UpdateSyncToken(sync_token);
+
+ return true;
+}
+
+PaintImage AcceleratedStaticBitmapImage::PaintImageForCurrentFrame() {
+ // TODO(ccameron): This function should not ignore |colorBehavior|.
+ // https://crbug.com/672306
+ CheckThread();
+ if (!IsValid())
+ return PaintImage();
+
+ sk_sp<SkImage> image;
+ if (original_skia_image_ &&
+ original_skia_image_thread_id_ ==
+ Platform::Current()->CurrentThread()->ThreadId()) {
+ // We need to avoid consuming the mailbox in the context where it
+ // originated. This avoids swapping back and forth between TextureHolder
+ // types.
+ image = original_skia_image_;
+ } else {
+ CreateImageFromMailboxIfNeeded();
+ image = texture_holder_->GetSkImage();
+ }
+
+ return CreatePaintImageBuilder()
+ .set_image(image, paint_image_content_id_)
+ .set_completion_state(PaintImage::CompletionState::DONE)
+ .TakePaintImage();
+}
+
+void AcceleratedStaticBitmapImage::Draw(PaintCanvas* canvas,
+ const PaintFlags& flags,
+ const FloatRect& dst_rect,
+ const FloatRect& src_rect,
+ RespectImageOrientationEnum,
+ ImageClampingMode image_clamping_mode,
+ ImageDecodingMode decode_mode) {
+ auto paint_image = PaintImageForCurrentFrame();
+ if (!paint_image)
+ return;
+ auto paint_image_decoding_mode = ToPaintImageDecodingMode(decode_mode);
+ if (paint_image.decoding_mode() != paint_image_decoding_mode) {
+ paint_image = PaintImageBuilder::WithCopy(std::move(paint_image))
+ .set_decoding_mode(paint_image_decoding_mode)
+ .TakePaintImage();
+ }
+ StaticBitmapImage::DrawHelper(canvas, flags, dst_rect, src_rect,
+ image_clamping_mode, paint_image);
+}
+
+bool AcceleratedStaticBitmapImage::IsValid() const {
+ return texture_holder_ && texture_holder_->IsValid();
+}
+
+WebGraphicsContext3DProvider* AcceleratedStaticBitmapImage::ContextProvider()
+ const {
+ if (!IsValid())
+ return nullptr;
+ return texture_holder_->ContextProvider();
+}
+
+base::WeakPtr<WebGraphicsContext3DProviderWrapper>
+AcceleratedStaticBitmapImage::ContextProviderWrapper() const {
+ if (!IsValid())
+ return nullptr;
+ return texture_holder_->ContextProviderWrapper();
+}
+
+void AcceleratedStaticBitmapImage::CreateImageFromMailboxIfNeeded() {
+ if (texture_holder_->IsSkiaTextureHolder())
+ return;
+ texture_holder_ =
+ std::make_unique<SkiaTextureHolder>(std::move(texture_holder_));
+}
+
+void AcceleratedStaticBitmapImage::EnsureMailbox(MailboxSyncMode mode,
+ GLenum filter) {
+ if (!texture_holder_->IsMailboxTextureHolder()) {
+ if (!original_skia_image_) {
+ // To ensure that the texture resource stays alive we only really need
+ // to retain the source SkImage until the mailbox is consumed, but this
+ // works too.
+ RetainOriginalSkImage();
+ }
+
+ texture_holder_ = std::make_unique<MailboxTextureHolder>(
+ std::move(texture_holder_), filter);
+ }
+ texture_holder_->Sync(mode);
+}
+
+void AcceleratedStaticBitmapImage::Transfer() {
+ CheckThread();
+ EnsureMailbox(kUnverifiedSyncToken, GL_NEAREST);
+ detach_thread_at_next_check_ = true;
+}
+
+bool AcceleratedStaticBitmapImage::CurrentFrameKnownToBeOpaque() {
+ return texture_holder_->CurrentFrameKnownToBeOpaque();
+}
+
+void AcceleratedStaticBitmapImage::CheckThread() {
+ if (detach_thread_at_next_check_) {
+ thread_checker_.DetachFromThread();
+ detach_thread_at_next_check_ = false;
+ }
+ CHECK(thread_checker_.CalledOnValidThread());
+}
+
+void AcceleratedStaticBitmapImage::Abandon() {
+ texture_holder_->Abandon();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h b/chromium/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h
new file mode 100644
index 00000000000..422c2683f55
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h
@@ -0,0 +1,127 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_ACCELERATED_STATIC_BITMAP_IMAGE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_ACCELERATED_STATIC_BITMAP_IMAGE_H_
+
+#include <memory>
+
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_checker.h"
+#include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
+#include "third_party/blink/renderer/platform/graphics/texture_holder.h"
+
+class GrContext;
+
+namespace blink {
+class WebGraphicsContext3DProviderWrapper;
+class TextureHolder;
+
+class PLATFORM_EXPORT AcceleratedStaticBitmapImage final
+ : public StaticBitmapImage {
+ public:
+ ~AcceleratedStaticBitmapImage() override;
+ // SkImage with a texture backing.
+ static scoped_refptr<AcceleratedStaticBitmapImage> CreateFromSkImage(
+ sk_sp<SkImage>,
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper>);
+ // Can specify the GrContext that created the texture backing. Ideally all
+ // callers would use this option. The |mailbox| is a name for the texture
+ // backing, allowing other contexts to use the same backing.
+ static scoped_refptr<AcceleratedStaticBitmapImage>
+ CreateFromWebGLContextImage(
+ const gpu::Mailbox&,
+ const gpu::SyncToken&,
+ unsigned texture_id,
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&,
+ IntSize mailbox_size);
+
+ bool CurrentFrameKnownToBeOpaque() override;
+ IntSize Size() const override;
+ bool IsTextureBacked() const override { return true; }
+ scoped_refptr<StaticBitmapImage> MakeAccelerated(
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_wrapper)
+ override {
+ NOTREACHED(); // IsTextureBacked() is already true.
+ return nullptr;
+ }
+
+ void Draw(PaintCanvas*,
+ const PaintFlags&,
+ const FloatRect& dst_rect,
+ const FloatRect& src_rect,
+ RespectImageOrientationEnum,
+ ImageClampingMode,
+ ImageDecodingMode) override;
+
+ bool IsValid() const final;
+ WebGraphicsContext3DProvider* ContextProvider() const final;
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper> ContextProviderWrapper()
+ const final;
+ scoped_refptr<StaticBitmapImage> MakeUnaccelerated() final;
+
+ bool CopyToTexture(gpu::gles2::GLES2Interface* dest_gl,
+ GLenum dest_target,
+ GLuint dest_texture_id,
+ bool unpack_premultiply_alpha,
+ bool unpack_flip_y,
+ const IntPoint& dest_point,
+ const IntRect& source_sub_rectangle) override;
+
+ bool HasMailbox() const final {
+ return texture_holder_->IsMailboxTextureHolder();
+ }
+ // To be called on sender thread before performing a transfer
+ void Transfer() final;
+
+ void EnsureMailbox(MailboxSyncMode, GLenum filter) final;
+
+ const gpu::Mailbox& GetMailbox() const final {
+ return texture_holder_->GetMailbox();
+ }
+ const gpu::SyncToken& GetSyncToken() const final {
+ return texture_holder_->GetSyncToken();
+ }
+ void UpdateSyncToken(gpu::SyncToken) final;
+
+ PaintImage PaintImageForCurrentFrame() override;
+
+ void Abandon() final;
+
+ TextureHolder* TextureHolderForTesting() { return texture_holder_.get(); }
+
+ private:
+ AcceleratedStaticBitmapImage(
+ sk_sp<SkImage>,
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&);
+ AcceleratedStaticBitmapImage(
+ const gpu::Mailbox&,
+ const gpu::SyncToken&,
+ unsigned texture_id,
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&,
+ IntSize mailbox_size);
+
+ void CreateImageFromMailboxIfNeeded();
+ void CheckThread();
+ void WaitSyncTokenIfNeeded();
+ void RetainOriginalSkImage();
+
+ std::unique_ptr<TextureHolder> texture_holder_;
+
+ base::ThreadChecker thread_checker_;
+ bool detach_thread_at_next_check_ = false;
+ PaintImage::ContentId paint_image_content_id_;
+
+ // For RetainOriginalSkImageForCopyOnWrite()
+ sk_sp<SkImage> original_skia_image_;
+ scoped_refptr<base::SingleThreadTaskRunner> original_skia_image_task_runner_;
+ PlatformThreadId original_skia_image_thread_id_;
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper>
+ original_skia_image_context_provider_wrapper_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image_test.cc b/chromium/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image_test.cc
new file mode 100644
index 00000000000..091c28f633d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image_test.cc
@@ -0,0 +1,157 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "skia/ext/texture_handle.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
+#include "third_party/blink/renderer/platform/graphics/test/fake_gles2_interface.h"
+#include "third_party/blink/renderer/platform/graphics/test/fake_web_graphics_context_3d_provider.h"
+#include "third_party/skia/include/core/SkSurface.h"
+
+using testing::ElementsAreArray;
+using testing::InSequence;
+using testing::MatcherCast;
+using testing::Pointee;
+using testing::SetArrayArgument;
+using testing::SetArgPointee;
+using testing::Test;
+using testing::_;
+
+namespace {
+
+class MockGLES2InterfaceWithSyncTokenSupport : public FakeGLES2Interface {
+ public:
+ MOCK_METHOD1(GenUnverifiedSyncTokenCHROMIUM, void(GLbyte*));
+ MOCK_METHOD1(WaitSyncTokenCHROMIUM, void(const GLbyte*));
+};
+
+gpu::SyncToken GenTestSyncToken(GLbyte id) {
+ gpu::SyncToken token;
+ // Store id in the first byte
+ reinterpret_cast<GLbyte*>(&token)[0] = id;
+ return token;
+}
+
+GLbyte SyncTokenMatcher(const gpu::SyncToken& token) {
+ return reinterpret_cast<const GLbyte*>(&token)[0];
+}
+
+} // unnamed namespace
+
+namespace blink {
+
+class AcceleratedStaticBitmapImageTest : public Test {
+ public:
+ void SetUp() override {
+ auto factory = [](MockGLES2InterfaceWithSyncTokenSupport* gl,
+ bool* gpu_compositing_disabled)
+ -> std::unique_ptr<WebGraphicsContext3DProvider> {
+ *gpu_compositing_disabled = false;
+ return std::make_unique<FakeWebGraphicsContext3DProvider>(gl, nullptr);
+ };
+ SharedGpuContext::SetContextProviderFactoryForTesting(
+ WTF::BindRepeating(factory, WTF::Unretained(&gl_)));
+ }
+ void TearDown() override { SharedGpuContext::ResetForTesting(); }
+
+ protected:
+ MockGLES2InterfaceWithSyncTokenSupport gl_;
+};
+
+TEST_F(AcceleratedStaticBitmapImageTest, NoTextureHolderThrashing) {
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper =
+ SharedGpuContext::ContextProviderWrapper();
+ GrContext* gr = context_provider_wrapper->ContextProvider()->GetGrContext();
+ SkImageInfo imageInfo = SkImageInfo::MakeN32Premul(100, 100);
+
+ sk_sp<SkSurface> surface =
+ SkSurface::MakeRenderTarget(gr, SkBudgeted::kNo, imageInfo);
+
+ SkPaint paint;
+ surface->getCanvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), paint);
+
+ sk_sp<SkImage> image = surface->makeImageSnapshot();
+ scoped_refptr<AcceleratedStaticBitmapImage> bitmap =
+ AcceleratedStaticBitmapImage::CreateFromSkImage(image,
+ context_provider_wrapper);
+ EXPECT_TRUE(bitmap->TextureHolderForTesting()->IsSkiaTextureHolder());
+
+ sk_sp<SkImage> stored_image =
+ bitmap->PaintImageForCurrentFrame().GetSkImage();
+ EXPECT_EQ(stored_image.get(), image.get());
+
+ bitmap->EnsureMailbox(kUnverifiedSyncToken, GL_LINEAR);
+ EXPECT_TRUE(bitmap->TextureHolderForTesting()->IsMailboxTextureHolder());
+
+ // Verify that calling PaintImageForCurrentFrame does not swap out of mailbox
+ // mode. It should use the cached original image instead.
+ stored_image = bitmap->PaintImageForCurrentFrame().GetSkImage();
+
+ EXPECT_EQ(stored_image.get(), image.get());
+ EXPECT_TRUE(bitmap->TextureHolderForTesting()->IsMailboxTextureHolder());
+}
+
+TEST_F(AcceleratedStaticBitmapImageTest, CopyToTextureSynchronization) {
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper =
+ SharedGpuContext::ContextProviderWrapper();
+ GrContext* gr = context_provider_wrapper->ContextProvider()->GetGrContext();
+ SkImageInfo imageInfo = SkImageInfo::MakeN32Premul(100, 100);
+ sk_sp<SkSurface> surface =
+ SkSurface::MakeRenderTarget(gr, SkBudgeted::kNo, imageInfo);
+
+ sk_sp<SkImage> image = surface->makeImageSnapshot();
+ scoped_refptr<AcceleratedStaticBitmapImage> bitmap =
+ AcceleratedStaticBitmapImage::CreateFromSkImage(image,
+ context_provider_wrapper);
+ EXPECT_TRUE(bitmap->TextureHolderForTesting()->IsSkiaTextureHolder());
+
+ MockGLES2InterfaceWithSyncTokenSupport destination_gl;
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+ testing::Mock::VerifyAndClearExpectations(&destination_gl);
+
+ InSequence s; // Indicate to gmock that order of EXPECT_CALLs is important
+
+ // Anterior synchronization
+ const gpu::SyncToken sync_token1 = GenTestSyncToken(1);
+ EXPECT_CALL(gl_, GenUnverifiedSyncTokenCHROMIUM(_))
+ .WillOnce(SetArrayArgument<0>(
+ sync_token1.GetConstData(),
+ sync_token1.GetConstData() + sizeof(gpu::SyncToken)));
+ EXPECT_CALL(destination_gl,
+ WaitSyncTokenCHROMIUM(Pointee(SyncTokenMatcher(sync_token1))));
+
+ // Posterior synchronization
+ const gpu::SyncToken sync_token2 = GenTestSyncToken(2);
+ EXPECT_CALL(destination_gl, GenUnverifiedSyncTokenCHROMIUM(_))
+ .WillOnce(SetArrayArgument<0>(
+ sync_token2.GetConstData(),
+ sync_token2.GetConstData() + sizeof(gpu::SyncToken)));
+
+ IntPoint dest_point(0, 0);
+ IntRect source_sub_rectangle(0, 0, 10, 10);
+ bitmap->CopyToTexture(&destination_gl, GL_TEXTURE_2D, 1 /*dest_texture_id*/,
+ false /*unpack_premultiply_alpha*/,
+ false /*unpack_flip_y*/, dest_point,
+ source_sub_rectangle);
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+ testing::Mock::VerifyAndClearExpectations(&destination_gl);
+
+ // Note the following expectation is commented-out because the
+ // MailboxTextureHolder destructor skips it when the texture ID is 0.
+ // The ID is zero because skia detected that it is being used with a fake
+ // context, so this problem can't be solved by just mocking GenTextures to
+ // make it produce non-zero IDs.
+ // TODO(junov): fix this!
+
+ // Final wait is postponed until destruction.
+ // EXPECT_CALL(gl_,
+ // WaitSyncTokenCHROMIUM(Pointee(SyncTokenMatcher(sync_token2)))); bitmap =
+ // nullptr;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/bitmap_image.cc b/chromium/third_party/blink/renderer/platform/graphics/bitmap_image.cc
new file mode 100644
index 00000000000..60bfede7bf7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/bitmap_image.cc
@@ -0,0 +1,424 @@
+/*
+ * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
+ * Copyright (C) 2004, 2005, 2006, 2008 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/graphics/bitmap_image.h"
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+#include "base/memory/scoped_refptr.h"
+#include "base/metrics/histogram_macros.h"
+#include "third_party/blink/public/web/web_settings.h"
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h"
+#include "third_party/blink/renderer/platform/graphics/deferred_image_decoder.h"
+#include "third_party/blink/renderer/platform/graphics/image_observer.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_image.h"
+#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
+#include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
+#include "third_party/blink/renderer/platform/instrumentation/platform_instrumentation.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/scheduler/child/web_scheduler.h"
+#include "third_party/blink/renderer/platform/timer.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+int GetRepetitionCountWithPolicyOverride(int actual_count,
+ ImageAnimationPolicy policy) {
+ switch (policy) {
+ case kImageAnimationPolicyAllowed:
+ // Default policy, no count override.
+ return actual_count;
+ case kImageAnimationPolicyAnimateOnce:
+ // Only a single loop allowed.
+ return kAnimationLoopOnce;
+ case kImageAnimationPolicyNoAnimation:
+ // Dont animate.
+ return kAnimationNone;
+ }
+
+ NOTREACHED();
+ return actual_count;
+}
+
+BitmapImage::BitmapImage(ImageObserver* observer, bool is_multipart)
+ : Image(observer, is_multipart),
+ animation_policy_(kImageAnimationPolicyAllowed),
+ all_data_received_(false),
+ have_size_(false),
+ size_available_(false),
+ have_frame_count_(false),
+ repetition_count_status_(kUnknown),
+ repetition_count_(kAnimationNone),
+ frame_count_(0) {}
+
+BitmapImage::~BitmapImage() {
+}
+
+bool BitmapImage::CurrentFrameHasSingleSecurityOrigin() const {
+ return true;
+}
+
+void BitmapImage::DestroyDecodedData() {
+ cached_frame_ = PaintImage();
+ if (decoder_)
+ decoder_->ClearCacheExceptFrame(kNotFound);
+ NotifyMemoryChanged();
+}
+
+scoped_refptr<SharedBuffer> BitmapImage::Data() {
+ return decoder_ ? decoder_->Data() : nullptr;
+}
+
+void BitmapImage::NotifyMemoryChanged() {
+ if (GetImageObserver())
+ GetImageObserver()->DecodedSizeChangedTo(this, TotalFrameBytes());
+}
+
+size_t BitmapImage::TotalFrameBytes() {
+ if (cached_frame_)
+ return Size().Area() * sizeof(ImageFrame::PixelData);
+ return 0u;
+}
+
+PaintImage BitmapImage::PaintImageForTesting(size_t frame_index) {
+ return CreatePaintImage(frame_index);
+}
+
+PaintImage BitmapImage::CreatePaintImage(size_t index) {
+ sk_sp<PaintImageGenerator> generator =
+ decoder_ ? decoder_->CreateGenerator(index) : nullptr;
+ if (!generator)
+ return PaintImage();
+
+ auto completion_state = all_data_received_
+ ? PaintImage::CompletionState::DONE
+ : PaintImage::CompletionState::PARTIALLY_DONE;
+ auto builder =
+ CreatePaintImageBuilder()
+ .set_paint_image_generator(std::move(generator))
+ .set_frame_index(index)
+ .set_repetition_count(GetRepetitionCountWithPolicyOverride(
+ RepetitionCount(), animation_policy_))
+ .set_completion_state(completion_state)
+ .set_reset_animation_sequence_id(reset_animation_sequence_id_);
+
+ return builder.TakePaintImage();
+}
+
+void BitmapImage::UpdateSize() const {
+ if (!size_available_ || have_size_ || !decoder_)
+ return;
+
+ size_ = decoder_->FrameSizeAtIndex(0);
+ if (decoder_->OrientationAtIndex(0).UsesWidthAsHeight())
+ size_respecting_orientation_ = size_.TransposedSize();
+ else
+ size_respecting_orientation_ = size_;
+ have_size_ = true;
+}
+
+IntSize BitmapImage::Size() const {
+ UpdateSize();
+ return size_;
+}
+
+IntSize BitmapImage::SizeRespectingOrientation() const {
+ UpdateSize();
+ return size_respecting_orientation_;
+}
+
+bool BitmapImage::GetHotSpot(IntPoint& hot_spot) const {
+ return decoder_ && decoder_->HotSpot(hot_spot);
+}
+
+Image::SizeAvailability BitmapImage::SetData(scoped_refptr<SharedBuffer> data,
+ bool all_data_received) {
+ if (!data)
+ return kSizeAvailable;
+
+ int length = data->size();
+ if (!length)
+ return kSizeAvailable;
+
+ if (decoder_) {
+ decoder_->SetData(std::move(data), all_data_received);
+ return DataChanged(all_data_received);
+ }
+
+ bool has_enough_data = ImageDecoder::HasSufficientDataToSniffImageType(*data);
+ decoder_ = DeferredImageDecoder::Create(std::move(data), all_data_received,
+ ImageDecoder::kAlphaPremultiplied,
+ ColorBehavior::Tag());
+ // If we had enough data but couldn't create a decoder, it implies a decode
+ // failure.
+ if (has_enough_data && !decoder_)
+ return kSizeAvailable;
+ return DataChanged(all_data_received);
+}
+
+Image::SizeAvailability BitmapImage::DataChanged(bool all_data_received) {
+ TRACE_EVENT0("blink", "BitmapImage::dataChanged");
+
+ // If the data was updated, clear the |cached_frame_| to push it to the
+ // compositor thread. Its necessary to clear the frame since more data
+ // requires a new PaintImageGenerator instance.
+ cached_frame_ = PaintImage();
+
+ // Feed all the data we've seen so far to the image decoder.
+ all_data_received_ = all_data_received;
+ have_frame_count_ = false;
+
+ return IsSizeAvailable() ? kSizeAvailable : kSizeUnavailable;
+}
+
+bool BitmapImage::HasColorProfile() const {
+ return decoder_ && decoder_->HasEmbeddedColorSpace();
+}
+
+String BitmapImage::FilenameExtension() const {
+ return decoder_ ? decoder_->FilenameExtension() : String();
+}
+
+void BitmapImage::Draw(
+ PaintCanvas* canvas,
+ const PaintFlags& flags,
+ const FloatRect& dst_rect,
+ const FloatRect& src_rect,
+ RespectImageOrientationEnum should_respect_image_orientation,
+ ImageClampingMode clamp_mode,
+ ImageDecodingMode decode_mode) {
+ TRACE_EVENT0("skia", "BitmapImage::draw");
+
+ PaintImage image = PaintImageForCurrentFrame();
+ if (!image)
+ return; // It's too early and we don't have an image yet.
+
+ auto paint_image_decoding_mode = ToPaintImageDecodingMode(decode_mode);
+ if (image.decoding_mode() != paint_image_decoding_mode) {
+ image = PaintImageBuilder::WithCopy(std::move(image))
+ .set_decoding_mode(paint_image_decoding_mode)
+ .TakePaintImage();
+ }
+
+ FloatRect adjusted_src_rect = src_rect;
+ adjusted_src_rect.Intersect(SkRect::MakeWH(image.width(), image.height()));
+
+ if (adjusted_src_rect.IsEmpty() || dst_rect.IsEmpty())
+ return; // Nothing to draw.
+
+ ImageOrientation orientation = kDefaultImageOrientation;
+ if (should_respect_image_orientation == kRespectImageOrientation)
+ orientation = CurrentFrameOrientation();
+
+ PaintCanvasAutoRestore auto_restore(canvas, false);
+ FloatRect adjusted_dst_rect = dst_rect;
+ if (orientation != kDefaultImageOrientation) {
+ canvas->save();
+
+ // ImageOrientation expects the origin to be at (0, 0)
+ canvas->translate(adjusted_dst_rect.X(), adjusted_dst_rect.Y());
+ adjusted_dst_rect.SetLocation(FloatPoint());
+
+ canvas->concat(AffineTransformToSkMatrix(
+ orientation.TransformFromDefault(adjusted_dst_rect.Size())));
+
+ if (orientation.UsesWidthAsHeight()) {
+ // The destination rect will have its width and height already reversed
+ // for the orientation of the image, as it was needed for page layout, so
+ // we need to reverse it back here.
+ adjusted_dst_rect =
+ FloatRect(adjusted_dst_rect.X(), adjusted_dst_rect.Y(),
+ adjusted_dst_rect.Height(), adjusted_dst_rect.Width());
+ }
+ }
+
+ uint32_t unique_id = image.GetSkImage()->uniqueID();
+ bool is_lazy_generated = image.IsLazyGenerated();
+ canvas->drawImageRect(std::move(image), adjusted_src_rect, adjusted_dst_rect,
+ &flags,
+ WebCoreClampingModeToSkiaRectConstraint(clamp_mode));
+
+ if (is_lazy_generated)
+ PlatformInstrumentation::DidDrawLazyPixelRef(unique_id);
+
+ StartAnimation();
+}
+
+size_t BitmapImage::FrameCount() {
+ if (!have_frame_count_) {
+ frame_count_ = decoder_ ? decoder_->FrameCount() : 0;
+ have_frame_count_ = frame_count_ > 0;
+ }
+ return frame_count_;
+}
+
+static inline bool HasVisibleImageSize(IntSize size) {
+ return (size.Width() > 1 || size.Height() > 1);
+}
+
+bool BitmapImage::IsSizeAvailable() {
+ if (size_available_)
+ return true;
+
+ size_available_ = decoder_ && decoder_->IsSizeAvailable();
+ if (size_available_ && HasVisibleImageSize(Size())) {
+ BitmapImageMetrics::CountDecodedImageType(decoder_->FilenameExtension());
+ if (decoder_->FilenameExtension() == "jpg") {
+ BitmapImageMetrics::CountImageOrientation(
+ decoder_->OrientationAtIndex(0).Orientation());
+ }
+ }
+
+ return size_available_;
+}
+
+PaintImage BitmapImage::PaintImageForCurrentFrame() {
+ if (cached_frame_)
+ return cached_frame_;
+
+ cached_frame_ = CreatePaintImage(PaintImage::kDefaultFrameIndex);
+
+ // Create the SkImage backing for this PaintImage here to ensure that copies
+ // of the PaintImage share the same SkImage. Skia's caching of the decoded
+ // output of this image is tied to the lifetime of the SkImage. So we create
+ // the SkImage here and cache the PaintImage to keep the decode alive in
+ // skia's cache.
+ cached_frame_.GetSkImage();
+ NotifyMemoryChanged();
+
+ return cached_frame_;
+}
+
+scoped_refptr<Image> BitmapImage::ImageForDefaultFrame() {
+ if (FrameCount() > 1) {
+ PaintImage paint_image = PaintImageForCurrentFrame();
+ if (!paint_image)
+ return nullptr;
+
+ if (paint_image.ShouldAnimate()) {
+ // To prevent the compositor from animating this image, we set the
+ // animation count to kAnimationNone. This makes the image essentially
+ // static.
+ paint_image = PaintImageBuilder::WithCopy(std::move(paint_image))
+ .set_repetition_count(kAnimationNone)
+ .TakePaintImage();
+ }
+ return StaticBitmapImage::Create(std::move(paint_image));
+ }
+
+ return Image::ImageForDefaultFrame();
+}
+
+bool BitmapImage::CurrentFrameKnownToBeOpaque() {
+ // If the image is animated, it is being animated by the compositor and we
+ // don't know what the current frame is.
+ // TODO(khushalsagar): We could say the image is opaque if none of the frames
+ // have alpha.
+ if (MaybeAnimated())
+ return false;
+
+ // We ask the decoder whether the image has alpha because in some cases the
+ // the correct value is known after decoding. The DeferredImageDecoder caches
+ // the accurate value from the decoded result.
+ const bool frame_has_alpha =
+ decoder_ ? decoder_->FrameHasAlphaAtIndex(PaintImage::kDefaultFrameIndex)
+ : true;
+ return !frame_has_alpha;
+}
+
+bool BitmapImage::CurrentFrameIsComplete() {
+ return decoder_
+ ? decoder_->FrameIsReceivedAtIndex(PaintImage::kDefaultFrameIndex)
+ : false;
+}
+
+bool BitmapImage::CurrentFrameIsLazyDecoded() {
+ // BitmapImage supports only lazy generated images.
+ return true;
+}
+
+ImageOrientation BitmapImage::CurrentFrameOrientation() {
+ return decoder_ ? decoder_->OrientationAtIndex(PaintImage::kDefaultFrameIndex)
+ : kDefaultImageOrientation;
+}
+
+int BitmapImage::RepetitionCount() {
+ if ((repetition_count_status_ == kUnknown) ||
+ ((repetition_count_status_ == kUncertain) && all_data_received_)) {
+ // Snag the repetition count. If |imageKnownToBeComplete| is false, the
+ // repetition count may not be accurate yet for GIFs; in this case the
+ // decoder will default to cAnimationLoopOnce, and we'll try and read
+ // the count again once the whole image is decoded.
+ repetition_count_ = decoder_ ? decoder_->RepetitionCount() : kAnimationNone;
+
+ // When requesting more than a single loop, repetition count is one less
+ // than the actual number of loops.
+ if (repetition_count_ > 0)
+ repetition_count_++;
+
+ repetition_count_status_ =
+ (all_data_received_ || repetition_count_ == kAnimationNone)
+ ? kCertain
+ : kUncertain;
+ }
+ return repetition_count_;
+}
+
+void BitmapImage::ResetAnimation() {
+ cached_frame_ = PaintImage();
+ reset_animation_sequence_id_++;
+}
+
+bool BitmapImage::MaybeAnimated() {
+ if (FrameCount() > 1)
+ return true;
+
+ return decoder_ && decoder_->RepetitionCount() != kAnimationNone;
+}
+
+void BitmapImage::SetAnimationPolicy(ImageAnimationPolicy policy) {
+ if (animation_policy_ == policy)
+ return;
+
+ animation_policy_ = policy;
+ ResetAnimation();
+}
+
+STATIC_ASSERT_ENUM(WebSettings::kImageAnimationPolicyAllowed,
+ kImageAnimationPolicyAllowed);
+STATIC_ASSERT_ENUM(WebSettings::kImageAnimationPolicyAnimateOnce,
+ kImageAnimationPolicyAnimateOnce);
+STATIC_ASSERT_ENUM(WebSettings::kImageAnimationPolicyNoAnimation,
+ kImageAnimationPolicyNoAnimation);
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/bitmap_image.h b/chromium/third_party/blink/renderer/platform/graphics/bitmap_image.h
new file mode 100644
index 00000000000..37534e485bf
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/bitmap_image.h
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
+ * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2008-2009 Torch Mobile, Inc.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_BITMAP_IMAGE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_BITMAP_IMAGE_H_
+
+#include <memory>
+#include "base/memory/weak_ptr.h"
+#include "third_party/blink/renderer/platform/geometry/int_size.h"
+#include "third_party/blink/renderer/platform/graphics/color.h"
+#include "third_party/blink/renderer/platform/graphics/deferred_image_decoder.h"
+#include "third_party/blink/renderer/platform/graphics/image.h"
+#include "third_party/blink/renderer/platform/graphics/image_animation_policy.h"
+#include "third_party/blink/renderer/platform/graphics/image_orientation.h"
+#include "third_party/blink/renderer/platform/image-decoders/image_animation.h"
+#include "third_party/blink/renderer/platform/timer.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT BitmapImage final : public Image {
+ friend class BitmapImageTest;
+ friend class CrossfadeGeneratedImage;
+ friend class GeneratedImage;
+ friend class GradientGeneratedImage;
+ friend class GraphicsContext;
+
+ public:
+ static scoped_refptr<BitmapImage> Create(ImageObserver* observer = nullptr,
+ bool is_multipart = false) {
+ return base::AdoptRef(new BitmapImage(observer, is_multipart));
+ }
+
+ ~BitmapImage() override;
+
+ bool IsBitmapImage() const override { return true; }
+
+ bool CurrentFrameHasSingleSecurityOrigin() const override;
+
+ IntSize Size() const override;
+ IntSize SizeRespectingOrientation() const;
+ bool GetHotSpot(IntPoint&) const override;
+ String FilenameExtension() const override;
+
+ SizeAvailability SetData(scoped_refptr<SharedBuffer> data,
+ bool all_data_received) override;
+ SizeAvailability DataChanged(bool all_data_received) override;
+
+ bool IsAllDataReceived() const { return all_data_received_; }
+ bool HasColorProfile() const;
+
+ void ResetAnimation() override;
+ bool MaybeAnimated() override;
+
+ void SetAnimationPolicy(ImageAnimationPolicy) override;
+ ImageAnimationPolicy AnimationPolicy() override { return animation_policy_; }
+
+ scoped_refptr<Image> ImageForDefaultFrame() override;
+
+ // TODO(khushalsagar): These names are bogus, we don't know what the current
+ // frame is.
+ bool CurrentFrameKnownToBeOpaque() override;
+ bool CurrentFrameIsComplete() override;
+ bool CurrentFrameIsLazyDecoded() override;
+ size_t FrameCount() override;
+ PaintImage PaintImageForCurrentFrame() override;
+ ImageOrientation CurrentFrameOrientation();
+
+ PaintImage PaintImageForTesting(size_t frame_index);
+ void AdvanceAnimationForTesting() override {
+ NOTREACHED() << "Supported only with svgs";
+ }
+ void SetDecoderForTesting(std::unique_ptr<DeferredImageDecoder> decoder) {
+ decoder_ = std::move(decoder);
+ }
+
+ protected:
+ bool IsSizeAvailable() override;
+
+ // TODO(khushalsagar): This is only used by MemoryCache to evict images based
+ // on whether they are caching decoded data. Since the decodes are already
+ // in unlocked discardable in cc/skia, this is unnecessary.
+ size_t TotalFrameBytes();
+
+ private:
+ enum RepetitionCountStatus : uint8_t {
+ kUnknown, // We haven't checked the source's repetition count.
+ kUncertain, // We have a repetition count, but it might be wrong (some GIFs
+ // have a count after the image data, and will report "loop
+ // once" until all data has been decoded).
+ kCertain // The repetition count is known to be correct.
+ };
+
+ BitmapImage(const SkBitmap&, ImageObserver* = nullptr);
+ BitmapImage(ImageObserver* = nullptr, bool is_multi_part = false);
+
+ void Draw(PaintCanvas*,
+ const PaintFlags&,
+ const FloatRect& dst_rect,
+ const FloatRect& src_rect,
+ RespectImageOrientationEnum,
+ ImageClampingMode,
+ ImageDecodingMode) override;
+
+ PaintImage CreatePaintImage(size_t index);
+ void UpdateSize() const;
+
+ // Called to wipe out the entire frame buffer cache and tell the image
+ // source to destroy everything; this is used when e.g. we want to free
+ // some room in the image cache.
+ void DestroyDecodedData() override;
+
+ scoped_refptr<SharedBuffer> Data() override;
+
+ // Notifies observers that the memory footprint has changed.
+ void NotifyMemoryChanged();
+
+ int RepetitionCount();
+
+ std::unique_ptr<DeferredImageDecoder> decoder_;
+ mutable IntSize size_; // The size to use for the overall image (will just
+ // be the size of the first image).
+ mutable IntSize size_respecting_orientation_;
+
+ // This caches the PaintImage created with the last updated encoded data to
+ // ensure re-use of generated decodes. This is cleared each time the encoded
+ // data is updated in DataChanged.
+ PaintImage cached_frame_;
+
+ ImageAnimationPolicy
+ animation_policy_; // Whether or not we can play animation.
+
+ bool all_data_received_ : 1; // Whether we've received all our data.
+ mutable bool have_size_ : 1; // Whether our |m_size| member variable has the
+ // final overall image size yet.
+ bool size_available_ : 1; // Whether we can obtain the size of the first
+ // image frame from ImageIO yet.
+ bool have_frame_count_ : 1;
+
+ bool default_frame_has_alpha_ : 1;
+
+ RepetitionCountStatus repetition_count_status_;
+ int repetition_count_; // How many total animation loops we should do. This
+ // will be cAnimationNone if this image type is
+ // incapable of animation.
+
+ size_t frame_count_;
+
+ PaintImage::AnimationSequenceId reset_animation_sequence_id_ = 0;
+};
+
+DEFINE_IMAGE_TYPE_CASTS(BitmapImage);
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.cc b/chromium/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.cc
new file mode 100644
index 00000000000..692fc31e657
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.cc
@@ -0,0 +1,83 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h"
+
+#include "third_party/blink/renderer/platform/graphics/color_space_gamut.h"
+#include "third_party/blink/renderer/platform/histogram.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/threading.h"
+
+namespace blink {
+
+void BitmapImageMetrics::CountDecodedImageType(const String& type) {
+ DecodedImageType decoded_image_type =
+ type == "jpg"
+ ? kImageJPEG
+ : type == "png"
+ ? kImagePNG
+ : type == "gif"
+ ? kImageGIF
+ : type == "webp"
+ ? kImageWebP
+ : type == "ico"
+ ? kImageICO
+ : type == "bmp"
+ ? kImageBMP
+ : DecodedImageType::kImageUnknown;
+
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(
+ EnumerationHistogram, decoded_image_type_histogram,
+ ("Blink.DecodedImageType", kDecodedImageTypeEnumEnd));
+ decoded_image_type_histogram.Count(decoded_image_type);
+}
+
+void BitmapImageMetrics::CountImageOrientation(
+ const ImageOrientationEnum orientation) {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(
+ EnumerationHistogram, orientation_histogram,
+ ("Blink.DecodedImage.Orientation", kImageOrientationEnumEnd));
+ orientation_histogram.Count(orientation);
+}
+
+void BitmapImageMetrics::CountImageGammaAndGamut(SkColorSpace* color_space) {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(EnumerationHistogram, gamma_named_histogram,
+ ("Blink.ColorSpace.Source", kGammaEnd));
+ gamma_named_histogram.Count(GetColorSpaceGamma(color_space));
+
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(
+ EnumerationHistogram, gamut_named_histogram,
+ ("Blink.ColorGamut.Source", static_cast<int>(ColorSpaceGamut::kEnd)));
+ gamut_named_histogram.Count(
+ static_cast<int>(ColorSpaceUtilities::GetColorSpaceGamut(color_space)));
+}
+
+void BitmapImageMetrics::CountOutputGammaAndGamut(SkColorSpace* color_space) {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(EnumerationHistogram, gamma_named_histogram,
+ ("Blink.ColorSpace.Destination", kGammaEnd));
+ gamma_named_histogram.Count(GetColorSpaceGamma(color_space));
+
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(EnumerationHistogram, gamut_named_histogram,
+ ("Blink.ColorGamut.Destination",
+ static_cast<int>(ColorSpaceGamut::kEnd)));
+ gamut_named_histogram.Count(
+ static_cast<int>(ColorSpaceUtilities::GetColorSpaceGamut(color_space)));
+}
+
+BitmapImageMetrics::Gamma BitmapImageMetrics::GetColorSpaceGamma(
+ SkColorSpace* color_space) {
+ Gamma gamma = kGammaNull;
+ if (color_space) {
+ if (color_space->gammaCloseToSRGB()) {
+ gamma = kGammaSRGB;
+ } else if (color_space->gammaIsLinear()) {
+ gamma = kGammaLinear;
+ } else {
+ gamma = kGammaNonStandard;
+ }
+ }
+ return gamma;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h b/chromium/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h
new file mode 100644
index 00000000000..fd1dfe070fd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h
@@ -0,0 +1,60 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_BITMAP_IMAGE_METRICS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_BITMAP_IMAGE_METRICS_H_
+
+#include "third_party/blink/renderer/platform/graphics/image_orientation.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/skia/include/core/SkColorSpace.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT BitmapImageMetrics {
+ STATIC_ONLY(BitmapImageMetrics);
+
+ public:
+ // Values synced with 'DecodedImageType' in
+ // src/tools/metrics/histograms/histograms.xml
+ enum DecodedImageType {
+ kImageUnknown = 0,
+ kImageJPEG = 1,
+ kImagePNG = 2,
+ kImageGIF = 3,
+ kImageWebP = 4,
+ kImageICO = 5,
+ kImageBMP = 6,
+ kDecodedImageTypeEnumEnd = kImageBMP + 1
+ };
+
+ enum Gamma {
+ // Values synced with 'Gamma' in src/tools/metrics/histograms/histograms.xml
+ kGammaLinear = 0,
+ kGammaSRGB = 1,
+ kGamma2Dot2 = 2,
+ kGammaNonStandard = 3,
+ kGammaNull = 4,
+ kGammaFail = 5,
+ kGammaInvalid = 6,
+ kGammaExponent = 7,
+ kGammaTable = 8,
+ kGammaParametric = 9,
+ kGammaNamed = 10,
+ kGammaEnd = kGammaNamed + 1,
+ };
+
+ static void CountDecodedImageType(const String& type);
+ static void CountImageOrientation(const ImageOrientationEnum);
+ static void CountImageGammaAndGamut(SkColorSpace*);
+ static void CountOutputGammaAndGamut(SkColorSpace*);
+
+ private:
+ static Gamma GetColorSpaceGamma(SkColorSpace*);
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/graphics/bitmap_image_test.cc b/chromium/third_party/blink/renderer/platform/graphics/bitmap_image_test.cc
new file mode 100644
index 00000000000..c7ccc1a2766
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/bitmap_image_test.cc
@@ -0,0 +1,694 @@
+/*
+ * Copyright (c) 2013, 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 "third_party/blink/renderer/platform/graphics/bitmap_image.h"
+
+#include "base/test/simple_test_tick_clock.h"
+#include "cc/paint/skia_paint_canvas.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h"
+#include "third_party/blink/renderer/platform/graphics/deferred_image_decoder.h"
+#include "third_party/blink/renderer/platform/graphics/image_observer.h"
+#include "third_party/blink/renderer/platform/graphics/test/mock_image_decoder.h"
+#include "third_party/blink/renderer/platform/scheduler/test/fake_task_runner.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/testing/histogram_tester.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
+#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkImage.h"
+
+namespace blink {
+
+class BitmapImageTest : public testing::Test {
+ public:
+ class FakeImageObserver : public GarbageCollectedFinalized<FakeImageObserver>,
+ public ImageObserver {
+ USING_GARBAGE_COLLECTED_MIXIN(FakeImageObserver);
+
+ public:
+ FakeImageObserver()
+ : last_decoded_size_(0), last_decoded_size_changed_delta_(0) {}
+
+ virtual void DecodedSizeChangedTo(const Image*, size_t new_size) {
+ last_decoded_size_changed_delta_ =
+ SafeCast<int>(new_size) - SafeCast<int>(last_decoded_size_);
+ last_decoded_size_ = new_size;
+ }
+ bool ShouldPauseAnimation(const Image*) override { return false; }
+ void AnimationAdvanced(const Image*) override {
+ animation_advanced_ = true;
+ }
+ void AsyncLoadCompleted(const Image*) override { NOTREACHED(); }
+
+ virtual void ChangedInRect(const Image*, const IntRect&) {}
+
+ size_t last_decoded_size_;
+ int last_decoded_size_changed_delta_;
+ bool animation_advanced_ = false;
+ };
+
+ static scoped_refptr<SharedBuffer> ReadFile(const char* file_name) {
+ String file_path = test::BlinkLayoutTestsDir();
+ file_path.append(file_name);
+ return test::ReadFromFile(file_path);
+ }
+
+ // Accessors to BitmapImage's protected methods.
+ void DestroyDecodedData() { image_->DestroyDecodedData(); }
+ size_t FrameCount() { return image_->FrameCount(); }
+
+ void LoadImage(const char* file_name) {
+ scoped_refptr<SharedBuffer> image_data = ReadFile(file_name);
+ ASSERT_TRUE(image_data.get());
+
+ image_->SetData(image_data, true);
+ }
+
+ SkBitmap GenerateBitmap(size_t frame_index) {
+ CHECK_GE(image_->FrameCount(), frame_index);
+ auto paint_image = image_->PaintImageForTesting(frame_index);
+ CHECK(paint_image);
+ CHECK_EQ(paint_image.frame_index(), frame_index);
+
+ SkBitmap bitmap;
+ SkImageInfo info = SkImageInfo::MakeN32Premul(image_->Size().Width(),
+ image_->Size().Height());
+ bitmap.allocPixels(info, image_->Size().Width() * 4);
+ bitmap.eraseColor(SK_AlphaTRANSPARENT);
+ cc::SkiaPaintCanvas canvas(bitmap);
+ canvas.drawImage(paint_image, 0u, 0u, nullptr);
+ return bitmap;
+ }
+
+ SkBitmap GenerateBitmapForImage(const char* file_name) {
+ scoped_refptr<SharedBuffer> image_data = ReadFile(file_name);
+ EXPECT_TRUE(image_data.get());
+ if (!image_data)
+ return SkBitmap();
+
+ auto image = BitmapImage::Create();
+ image->SetData(image_data, true);
+ auto paint_image = image->PaintImageForCurrentFrame();
+ CHECK(paint_image);
+ CHECK_EQ(paint_image.frame_index(), 0u);
+
+ SkBitmap bitmap;
+ SkImageInfo info = SkImageInfo::MakeN32Premul(image->Size().Width(),
+ image->Size().Height());
+ bitmap.allocPixels(info, image->Size().Width() * 4);
+ bitmap.eraseColor(SK_AlphaTRANSPARENT);
+ cc::SkiaPaintCanvas canvas(bitmap);
+ canvas.drawImage(paint_image, 0u, 0u, nullptr);
+ return bitmap;
+ }
+
+ void VerifyBitmap(const SkBitmap& bitmap, SkColor color) {
+ ASSERT_GT(bitmap.width(), 0);
+ ASSERT_GT(bitmap.height(), 0);
+
+ for (int i = 0; i < bitmap.width(); ++i) {
+ for (int j = 0; j < bitmap.height(); ++j) {
+ auto bitmap_color = bitmap.getColor(i, j);
+ EXPECT_EQ(bitmap_color, color)
+ << "Bitmap: " << SkColorGetA(bitmap_color) << ","
+ << SkColorGetR(bitmap_color) << "," << SkColorGetG(bitmap_color)
+ << "," << SkColorGetB(bitmap_color)
+ << "Expected: " << SkColorGetA(color) << "," << SkColorGetR(color)
+ << "," << SkColorGetG(color) << "," << SkColorGetB(color);
+ }
+ }
+ }
+
+ void VerifyBitmap(const SkBitmap& bitmap, const SkBitmap& expected) {
+ ASSERT_GT(bitmap.width(), 0);
+ ASSERT_GT(bitmap.height(), 0);
+ ASSERT_EQ(bitmap.info(), expected.info());
+
+ for (int i = 0; i < bitmap.width(); ++i) {
+ for (int j = 0; j < bitmap.height(); ++j) {
+ auto bitmap_color = bitmap.getColor(i, j);
+ auto expected_color = expected.getColor(i, j);
+ EXPECT_EQ(bitmap_color, expected_color)
+ << "Bitmap: " << SkColorGetA(bitmap_color) << ","
+ << SkColorGetR(bitmap_color) << "," << SkColorGetG(bitmap_color)
+ << "," << SkColorGetB(bitmap_color)
+ << "Expected: " << SkColorGetA(expected_color) << ","
+ << SkColorGetR(expected_color) << "," << SkColorGetG(expected_color)
+ << "," << SkColorGetB(expected_color);
+ }
+ }
+ }
+
+ size_t DecodedSize() { return image_->TotalFrameBytes(); }
+
+ int RepetitionCount() { return image_->RepetitionCount(); }
+
+ scoped_refptr<Image> ImageForDefaultFrame() {
+ return image_->ImageForDefaultFrame();
+ }
+
+ int LastDecodedSizeChange() {
+ return image_observer_->last_decoded_size_changed_delta_;
+ }
+
+ scoped_refptr<SharedBuffer> Data() { return image_->Data(); }
+
+ protected:
+ void SetUp() override {
+ image_observer_ = new FakeImageObserver;
+ image_ = BitmapImage::Create(image_observer_.Get());
+ }
+
+ Persistent<FakeImageObserver> image_observer_;
+ scoped_refptr<BitmapImage> image_;
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform_;
+};
+
+TEST_F(BitmapImageTest, destroyDecodedData) {
+ LoadImage("/images/resources/animated-10color.gif");
+ image_->PaintImageForCurrentFrame();
+ size_t total_size = DecodedSize();
+ EXPECT_GT(total_size, 0u);
+ DestroyDecodedData();
+ EXPECT_EQ(-static_cast<int>(total_size), LastDecodedSizeChange());
+ EXPECT_EQ(0u, DecodedSize());
+}
+
+TEST_F(BitmapImageTest, maybeAnimated) {
+ LoadImage("/images/resources/gif-loop-count.gif");
+ EXPECT_TRUE(image_->MaybeAnimated());
+}
+
+TEST_F(BitmapImageTest, isAllDataReceived) {
+ scoped_refptr<SharedBuffer> image_data =
+ ReadFile("/images/resources/green.jpg");
+ ASSERT_TRUE(image_data.get());
+
+ scoped_refptr<BitmapImage> image = BitmapImage::Create();
+ EXPECT_FALSE(image->IsAllDataReceived());
+
+ image->SetData(image_data, false);
+ EXPECT_FALSE(image->IsAllDataReceived());
+
+ image->SetData(image_data, true);
+ EXPECT_TRUE(image->IsAllDataReceived());
+
+ image->SetData(SharedBuffer::Create("data", sizeof("data")), false);
+ EXPECT_FALSE(image->IsAllDataReceived());
+
+ image->SetData(image_data, true);
+ EXPECT_TRUE(image->IsAllDataReceived());
+}
+
+TEST_F(BitmapImageTest, noColorProfile) {
+ LoadImage("/images/resources/green.jpg");
+ image_->PaintImageForCurrentFrame();
+ EXPECT_EQ(1024u, DecodedSize());
+ EXPECT_FALSE(image_->HasColorProfile());
+}
+
+TEST_F(BitmapImageTest, jpegHasColorProfile) {
+ LoadImage("/images/resources/icc-v2-gbr.jpg");
+ image_->PaintImageForCurrentFrame();
+ EXPECT_EQ(227700u, DecodedSize());
+ EXPECT_TRUE(image_->HasColorProfile());
+}
+
+TEST_F(BitmapImageTest, pngHasColorProfile) {
+ LoadImage(
+ "/images/resources/"
+ "palatted-color-png-gamma-one-color-profile.png");
+ image_->PaintImageForCurrentFrame();
+ EXPECT_EQ(65536u, DecodedSize());
+ EXPECT_TRUE(image_->HasColorProfile());
+}
+
+TEST_F(BitmapImageTest, webpHasColorProfile) {
+ LoadImage("/images/resources/webp-color-profile-lossy.webp");
+ image_->PaintImageForCurrentFrame();
+ EXPECT_EQ(2560000u, DecodedSize());
+ EXPECT_TRUE(image_->HasColorProfile());
+}
+
+TEST_F(BitmapImageTest, icoHasWrongFrameDimensions) {
+ LoadImage("/images/resources/wrong-frame-dimensions.ico");
+ // This call would cause crash without fix for 408026
+ ImageForDefaultFrame();
+}
+
+TEST_F(BitmapImageTest, correctDecodedDataSize) {
+ // Requesting any one frame shouldn't result in decoding any other frames.
+ LoadImage("/images/resources/anim_none.gif");
+ image_->PaintImageForCurrentFrame();
+ int frame_size =
+ static_cast<int>(image_->Size().Area() * sizeof(ImageFrame::PixelData));
+ EXPECT_EQ(frame_size, LastDecodedSizeChange());
+}
+
+TEST_F(BitmapImageTest, recachingFrameAfterDataChanged) {
+ LoadImage("/images/resources/green.jpg");
+ image_->PaintImageForCurrentFrame();
+ EXPECT_GT(LastDecodedSizeChange(), 0);
+ image_observer_->last_decoded_size_changed_delta_ = 0;
+
+ // Calling dataChanged causes the cache to flush, but doesn't affect the
+ // source's decoded frames. It shouldn't affect decoded size.
+ image_->DataChanged(true);
+ EXPECT_EQ(0, LastDecodedSizeChange());
+ // Recaching the first frame also shouldn't affect decoded size.
+ image_->PaintImageForCurrentFrame();
+ EXPECT_EQ(0, LastDecodedSizeChange());
+}
+
+TEST_F(BitmapImageTest, ConstantImageIdForPartiallyLoadedImages) {
+ scoped_refptr<SharedBuffer> image_data =
+ ReadFile("/images/resources/green.jpg");
+ ASSERT_TRUE(image_data.get());
+
+ // Create a new buffer to partially supply the data.
+ scoped_refptr<SharedBuffer> partial_buffer = SharedBuffer::Create();
+ partial_buffer->Append(image_data->Data(), image_data->size() - 4);
+
+ // First partial load. Repeated calls for a PaintImage should have the same
+ // image until the data changes or the decoded data is destroyed.
+ ASSERT_EQ(image_->SetData(partial_buffer, false), Image::kSizeAvailable);
+ auto image1 = image_->PaintImageForCurrentFrame();
+ auto image2 = image_->PaintImageForCurrentFrame();
+ EXPECT_EQ(image1, image2);
+ auto sk_image1 = image1.GetSkImage();
+ auto sk_image2 = image2.GetSkImage();
+ EXPECT_EQ(sk_image1->uniqueID(), sk_image2->uniqueID());
+
+ // Frame keys should be the same for these PaintImages.
+ EXPECT_EQ(image1.GetKeyForFrame(image1.frame_index()),
+ image2.GetKeyForFrame(image2.frame_index()));
+ EXPECT_EQ(image1.frame_index(), 0u);
+ EXPECT_EQ(image2.frame_index(), 0u);
+
+ // Destroy the decoded data. This generates a new id since we don't cache
+ // image ids for partial decodes.
+ DestroyDecodedData();
+ auto image3 = image_->PaintImageForCurrentFrame();
+ auto sk_image3 = image3.GetSkImage();
+ EXPECT_NE(sk_image1, sk_image3);
+ EXPECT_NE(sk_image1->uniqueID(), sk_image3->uniqueID());
+
+ // Since the cached generator is discarded on destroying the cached decode,
+ // the new content id is generated resulting in an updated frame key.
+ EXPECT_NE(image1.GetKeyForFrame(image1.frame_index()),
+ image3.GetKeyForFrame(image3.frame_index()));
+ EXPECT_EQ(image3.frame_index(), 0u);
+
+ // Load complete. This should generate a new image id.
+ image_->SetData(image_data, true);
+ auto complete_image = image_->PaintImageForCurrentFrame();
+ auto complete_sk_image = complete_image.GetSkImage();
+ EXPECT_NE(sk_image3, complete_sk_image);
+ EXPECT_NE(sk_image3->uniqueID(), complete_sk_image->uniqueID());
+ EXPECT_NE(complete_image.GetKeyForFrame(complete_image.frame_index()),
+ image3.GetKeyForFrame(image3.frame_index()));
+ EXPECT_EQ(complete_image.frame_index(), 0u);
+
+ // Destroy the decoded data and re-create the PaintImage. The frame key
+ // remains constant but the SkImage id will change since we don't cache skia
+ // uniqueIDs.
+ DestroyDecodedData();
+ auto new_complete_image = image_->PaintImageForCurrentFrame();
+ auto new_complete_sk_image = new_complete_image.GetSkImage();
+ EXPECT_NE(new_complete_sk_image, complete_sk_image);
+ EXPECT_EQ(new_complete_image.GetKeyForFrame(new_complete_image.frame_index()),
+ complete_image.GetKeyForFrame(complete_image.frame_index()));
+ EXPECT_EQ(new_complete_image.frame_index(), 0u);
+}
+
+TEST_F(BitmapImageTest, ImageForDefaultFrame_MultiFrame) {
+ LoadImage("/images/resources/anim_none.gif");
+
+ // Multi-frame images create new StaticBitmapImages for each call.
+ auto default_image1 = image_->ImageForDefaultFrame();
+ auto default_image2 = image_->ImageForDefaultFrame();
+ EXPECT_NE(default_image1, default_image2);
+
+ // But the PaintImage should be the same.
+ auto paint_image1 = default_image1->PaintImageForCurrentFrame();
+ auto paint_image2 = default_image2->PaintImageForCurrentFrame();
+ EXPECT_EQ(paint_image1, paint_image2);
+ EXPECT_EQ(paint_image1.GetSkImage()->uniqueID(),
+ paint_image2.GetSkImage()->uniqueID());
+}
+
+TEST_F(BitmapImageTest, ImageForDefaultFrame_SingleFrame) {
+ LoadImage("/images/resources/green.jpg");
+
+ // Default frame images for single-frame cases is the image itself.
+ EXPECT_EQ(image_->ImageForDefaultFrame(), image_);
+}
+
+TEST_F(BitmapImageTest, GifDecoderFrame0) {
+ LoadImage("/images/resources/green-red-blue-yellow-animated.gif");
+ auto bitmap = GenerateBitmap(0u);
+ SkColor color = SkColorSetARGB(255, 0, 128, 0);
+ VerifyBitmap(bitmap, color);
+}
+
+TEST_F(BitmapImageTest, GifDecoderFrame1) {
+ LoadImage("/images/resources/green-red-blue-yellow-animated.gif");
+ auto bitmap = GenerateBitmap(1u);
+ VerifyBitmap(bitmap, SK_ColorRED);
+}
+
+TEST_F(BitmapImageTest, GifDecoderFrame2) {
+ LoadImage("/images/resources/green-red-blue-yellow-animated.gif");
+ auto bitmap = GenerateBitmap(2u);
+ VerifyBitmap(bitmap, SK_ColorBLUE);
+}
+
+TEST_F(BitmapImageTest, GifDecoderFrame3) {
+ LoadImage("/images/resources/green-red-blue-yellow-animated.gif");
+ auto bitmap = GenerateBitmap(3u);
+ VerifyBitmap(bitmap, SK_ColorYELLOW);
+}
+
+TEST_F(BitmapImageTest, APNGDecoder00) {
+ LoadImage("/images/resources/apng00.png");
+ auto actual_bitmap = GenerateBitmap(0u);
+ auto expected_bitmap =
+ GenerateBitmapForImage("/images/resources/apng00-ref.png");
+ VerifyBitmap(actual_bitmap, expected_bitmap);
+}
+
+// Jump to the final frame of each image.
+TEST_F(BitmapImageTest, APNGDecoder01) {
+ LoadImage("/images/resources/apng01.png");
+ auto actual_bitmap = GenerateBitmap(9u);
+ auto expected_bitmap =
+ GenerateBitmapForImage("/images/resources/apng01-ref.png");
+ VerifyBitmap(actual_bitmap, expected_bitmap);
+}
+
+TEST_F(BitmapImageTest, APNGDecoder02) {
+ LoadImage("/images/resources/apng02.png");
+ auto actual_bitmap = GenerateBitmap(9u);
+ auto expected_bitmap =
+ GenerateBitmapForImage("/images/resources/apng02-ref.png");
+ VerifyBitmap(actual_bitmap, expected_bitmap);
+}
+
+TEST_F(BitmapImageTest, APNGDecoder04) {
+ LoadImage("/images/resources/apng04.png");
+ auto actual_bitmap = GenerateBitmap(12u);
+ auto expected_bitmap =
+ GenerateBitmapForImage("/images/resources/apng04-ref.png");
+ VerifyBitmap(actual_bitmap, expected_bitmap);
+}
+
+TEST_F(BitmapImageTest, APNGDecoder08) {
+ LoadImage("/images/resources/apng08.png");
+ auto actual_bitmap = GenerateBitmap(12u);
+ auto expected_bitmap =
+ GenerateBitmapForImage("/images/resources/apng08-ref.png");
+ VerifyBitmap(actual_bitmap, expected_bitmap);
+}
+
+TEST_F(BitmapImageTest, APNGDecoder10) {
+ LoadImage("/images/resources/apng10.png");
+ auto actual_bitmap = GenerateBitmap(3u);
+ auto expected_bitmap =
+ GenerateBitmapForImage("/images/resources/apng10-ref.png");
+ VerifyBitmap(actual_bitmap, expected_bitmap);
+}
+
+TEST_F(BitmapImageTest, APNGDecoder11) {
+ LoadImage("/images/resources/apng11.png");
+ auto actual_bitmap = GenerateBitmap(9u);
+ auto expected_bitmap =
+ GenerateBitmapForImage("/images/resources/apng11-ref.png");
+ VerifyBitmap(actual_bitmap, expected_bitmap);
+}
+
+TEST_F(BitmapImageTest, APNGDecoder12) {
+ LoadImage("/images/resources/apng12.png");
+ auto actual_bitmap = GenerateBitmap(9u);
+ auto expected_bitmap =
+ GenerateBitmapForImage("/images/resources/apng12-ref.png");
+ VerifyBitmap(actual_bitmap, expected_bitmap);
+}
+
+TEST_F(BitmapImageTest, APNGDecoder14) {
+ LoadImage("/images/resources/apng14.png");
+ auto actual_bitmap = GenerateBitmap(12u);
+ auto expected_bitmap =
+ GenerateBitmapForImage("/images/resources/apng14-ref.png");
+ VerifyBitmap(actual_bitmap, expected_bitmap);
+}
+
+TEST_F(BitmapImageTest, APNGDecoder18) {
+ LoadImage("/images/resources/apng18.png");
+ auto actual_bitmap = GenerateBitmap(12u);
+ auto expected_bitmap =
+ GenerateBitmapForImage("/images/resources/apng18-ref.png");
+ VerifyBitmap(actual_bitmap, expected_bitmap);
+}
+
+TEST_F(BitmapImageTest, APNGDecoderDisposePrevious) {
+ LoadImage("/images/resources/crbug722072.png");
+ auto actual_bitmap = GenerateBitmap(3u);
+ auto expected_bitmap = GenerateBitmapForImage("/images/resources/green.png");
+ VerifyBitmap(actual_bitmap, expected_bitmap);
+}
+
+TEST_F(BitmapImageTest, GIFRepetitionCount) {
+ LoadImage("/images/resources/three-frames_loop-three-times.gif");
+ auto paint_image = image_->PaintImageForCurrentFrame();
+ EXPECT_EQ(paint_image.repetition_count(), 3);
+ EXPECT_EQ(paint_image.FrameCount(), 3u);
+}
+
+class BitmapImageTestWithMockDecoder : public BitmapImageTest,
+ public MockImageDecoderClient {
+ public:
+ void SetUp() override {
+ BitmapImageTest::SetUp();
+
+ auto decoder = MockImageDecoder::Create(this);
+ decoder->SetSize(10, 10);
+ image_->SetDecoderForTesting(
+ DeferredImageDecoder::CreateForTesting(std::move(decoder)));
+ }
+
+ void DecoderBeingDestroyed() override {}
+ void DecodeRequested() override {}
+ ImageFrame::Status GetStatus(size_t index) override {
+ if (index < frame_count_ - 1 || last_frame_complete_)
+ return ImageFrame::Status::kFrameComplete;
+ return ImageFrame::Status::kFramePartial;
+ }
+ size_t FrameCount() override { return frame_count_; }
+ int RepetitionCount() const override { return repetition_count_; }
+ TimeDelta FrameDuration() const override { return duration_; }
+
+ protected:
+ TimeDelta duration_;
+ int repetition_count_;
+ size_t frame_count_;
+ bool last_frame_complete_;
+};
+
+TEST_F(BitmapImageTestWithMockDecoder, ImageMetadataTracking) {
+ // For a zero duration, we should make it non-zero when creating a PaintImage.
+ repetition_count_ = kAnimationLoopOnce;
+ frame_count_ = 4u;
+ last_frame_complete_ = false;
+ image_->SetData(SharedBuffer::Create("data", sizeof("data")), false);
+
+ PaintImage image = image_->PaintImageForCurrentFrame();
+ ASSERT_TRUE(image);
+ EXPECT_EQ(image.FrameCount(), frame_count_);
+ EXPECT_EQ(image.completion_state(),
+ PaintImage::CompletionState::PARTIALLY_DONE);
+ EXPECT_EQ(image.repetition_count(), repetition_count_);
+ for (size_t i = 0; i < image.GetFrameMetadata().size(); ++i) {
+ const auto& data = image.GetFrameMetadata()[i];
+ EXPECT_EQ(data.duration, base::TimeDelta::FromMilliseconds(100));
+ if (i == frame_count_ - 1 && !last_frame_complete_)
+ EXPECT_FALSE(data.complete);
+ else
+ EXPECT_TRUE(data.complete);
+ }
+
+ // Now the load is finished.
+ duration_ = TimeDelta::FromSeconds(1);
+ repetition_count_ = kAnimationLoopInfinite;
+ frame_count_ = 6u;
+ last_frame_complete_ = true;
+ image_->SetData(SharedBuffer::Create("data", sizeof("data")), true);
+
+ image = image_->PaintImageForCurrentFrame();
+ ASSERT_TRUE(image);
+ EXPECT_EQ(image.FrameCount(), frame_count_);
+ EXPECT_EQ(image.completion_state(), PaintImage::CompletionState::DONE);
+ EXPECT_EQ(image.repetition_count(), repetition_count_);
+ for (size_t i = 0; i < image.GetFrameMetadata().size(); ++i) {
+ const auto& data = image.GetFrameMetadata()[i];
+ if (i < 4u)
+ EXPECT_EQ(data.duration, base::TimeDelta::FromMilliseconds(100));
+ else
+ EXPECT_EQ(data.duration, base::TimeDelta::FromSeconds(1));
+ EXPECT_TRUE(data.complete);
+ }
+};
+
+TEST_F(BitmapImageTestWithMockDecoder, AnimationPolicyOverride) {
+ repetition_count_ = kAnimationLoopInfinite;
+ frame_count_ = 4u;
+ last_frame_complete_ = true;
+ image_->SetData(SharedBuffer::Create("data", sizeof("data")), false);
+
+ PaintImage image = image_->PaintImageForCurrentFrame();
+ EXPECT_EQ(image.repetition_count(), repetition_count_);
+
+ // Only one loop allowed.
+ image_->SetAnimationPolicy(kImageAnimationPolicyAnimateOnce);
+ image = image_->PaintImageForCurrentFrame();
+ EXPECT_EQ(image.repetition_count(), kAnimationLoopOnce);
+
+ // No animation allowed.
+ image_->SetAnimationPolicy(kImageAnimationPolicyNoAnimation);
+ image = image_->PaintImageForCurrentFrame();
+ EXPECT_EQ(image.repetition_count(), kAnimationNone);
+
+ // Default policy.
+ image_->SetAnimationPolicy(kImageAnimationPolicyAllowed);
+ image = image_->PaintImageForCurrentFrame();
+ EXPECT_EQ(image.repetition_count(), repetition_count_);
+}
+
+TEST_F(BitmapImageTestWithMockDecoder, ResetAnimation) {
+ repetition_count_ = kAnimationLoopInfinite;
+ frame_count_ = 4u;
+ last_frame_complete_ = true;
+ image_->SetData(SharedBuffer::Create("data", sizeof("data")), false);
+
+ PaintImage image = image_->PaintImageForCurrentFrame();
+ image_->ResetAnimation();
+ PaintImage image2 = image_->PaintImageForCurrentFrame();
+ EXPECT_GT(image2.reset_animation_sequence_id(),
+ image.reset_animation_sequence_id());
+}
+
+TEST_F(BitmapImageTestWithMockDecoder, PaintImageForStaticBitmapImage) {
+ repetition_count_ = kAnimationLoopInfinite;
+ frame_count_ = 5;
+ last_frame_complete_ = true;
+ image_->SetData(SharedBuffer::Create("data", sizeof("data")), false);
+
+ // PaintImage for the original image is animated.
+ EXPECT_TRUE(image_->PaintImageForCurrentFrame().ShouldAnimate());
+
+ // But the StaticBitmapImage is not.
+ EXPECT_FALSE(image_->ImageForDefaultFrame()
+ ->PaintImageForCurrentFrame()
+ .ShouldAnimate());
+}
+
+template <typename HistogramEnumType>
+struct HistogramTestParams {
+ const char* filename;
+ HistogramEnumType type;
+};
+
+template <typename HistogramEnumType>
+class BitmapHistogramTest : public BitmapImageTest,
+ public testing::WithParamInterface<
+ HistogramTestParams<HistogramEnumType>> {
+ protected:
+ void RunTest(const char* histogram_name) {
+ HistogramTester histogram_tester;
+ LoadImage(this->GetParam().filename);
+ histogram_tester.ExpectUniqueSample(histogram_name, this->GetParam().type,
+ 1);
+ }
+};
+
+using DecodedImageTypeHistogramTest =
+ BitmapHistogramTest<BitmapImageMetrics::DecodedImageType>;
+
+TEST_P(DecodedImageTypeHistogramTest, ImageType) {
+ RunTest("Blink.DecodedImageType");
+}
+
+const DecodedImageTypeHistogramTest::ParamType
+ kDecodedImageTypeHistogramTestparams[] = {
+ {"/images/resources/green.jpg", BitmapImageMetrics::kImageJPEG},
+ {"/images/resources/"
+ "palatted-color-png-gamma-one-color-profile.png",
+ BitmapImageMetrics::kImagePNG},
+ {"/images/resources/animated-10color.gif",
+ BitmapImageMetrics::kImageGIF},
+ {"/images/resources/webp-color-profile-lossy.webp",
+ BitmapImageMetrics::kImageWebP},
+ {"/images/resources/wrong-frame-dimensions.ico",
+ BitmapImageMetrics::kImageICO},
+ {"/images/resources/lenna.bmp", BitmapImageMetrics::kImageBMP}};
+
+INSTANTIATE_TEST_CASE_P(
+ DecodedImageTypeHistogramTest,
+ DecodedImageTypeHistogramTest,
+ testing::ValuesIn(kDecodedImageTypeHistogramTestparams));
+
+using DecodedImageOrientationHistogramTest =
+ BitmapHistogramTest<ImageOrientationEnum>;
+
+TEST_P(DecodedImageOrientationHistogramTest, ImageOrientation) {
+ RunTest("Blink.DecodedImage.Orientation");
+}
+
+const DecodedImageOrientationHistogramTest::ParamType
+ kDecodedImageOrientationHistogramTestParams[] = {
+ {"/images/resources/exif-orientation-1-ul.jpg", kOriginTopLeft},
+ {"/images/resources/exif-orientation-2-ur.jpg", kOriginTopRight},
+ {"/images/resources/exif-orientation-3-lr.jpg", kOriginBottomRight},
+ {"/images/resources/exif-orientation-4-lol.jpg", kOriginBottomLeft},
+ {"/images/resources/exif-orientation-5-lu.jpg", kOriginLeftTop},
+ {"/images/resources/exif-orientation-6-ru.jpg", kOriginRightTop},
+ {"/images/resources/exif-orientation-7-rl.jpg", kOriginRightBottom},
+ {"/images/resources/exif-orientation-8-llo.jpg", kOriginLeftBottom}};
+
+INSTANTIATE_TEST_CASE_P(
+ DecodedImageOrientationHistogramTest,
+ DecodedImageOrientationHistogramTest,
+ testing::ValuesIn(kDecodedImageOrientationHistogramTestParams));
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/box_reflection.cc b/chromium/third_party/blink/renderer/platform/graphics/box_reflection.cc
new file mode 100644
index 00000000000..56d3336fceb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/box_reflection.cc
@@ -0,0 +1,43 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/box_reflection.h"
+
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
+#include "third_party/skia/include/core/SkMatrix.h"
+
+#include <utility>
+
+namespace blink {
+
+SkMatrix BoxReflection::ReflectionMatrix() const {
+ SkMatrix flip_matrix;
+ switch (direction_) {
+ case kVerticalReflection:
+ flip_matrix.setScale(1, -1);
+ flip_matrix.postTranslate(0, offset_);
+ break;
+ case kHorizontalReflection:
+ flip_matrix.setScale(-1, 1);
+ flip_matrix.postTranslate(offset_, 0);
+ break;
+ default:
+ // MSVC requires that SkMatrix be initialized in this unreachable case.
+ NOTREACHED();
+ flip_matrix.reset();
+ break;
+ }
+ return flip_matrix;
+}
+
+FloatRect BoxReflection::MapRect(const FloatRect& rect) const {
+ SkRect reflection(rect);
+ ReflectionMatrix().mapRect(&reflection);
+ FloatRect result = rect;
+ result.Unite(reflection);
+ return result;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/box_reflection.h b/chromium/third_party/blink/renderer/platform/graphics/box_reflection.h
new file mode 100644
index 00000000000..f0b351fcdca
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/box_reflection.h
@@ -0,0 +1,78 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_BOX_REFLECTION_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_BOX_REFLECTION_H_
+
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_record.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+class SkMatrix;
+
+namespace blink {
+
+// A reflection, as created by -webkit-box-reflect. Consists of:
+// * a direction (either vertical or horizontal)
+// * an offset to be applied to the reflection after flipping about the
+// x- or y-axis, according to the direction
+// * a mask image, which will be applied to the reflection before the
+// reflection matrix is applied
+class PLATFORM_EXPORT BoxReflection {
+ public:
+ enum ReflectionDirection {
+ // Vertically flipped (to appear above or below).
+ kVerticalReflection,
+ // Horizontally flipped (to appear to the left or right).
+ kHorizontalReflection,
+ };
+
+ BoxReflection(ReflectionDirection direction, float offset)
+ : BoxReflection(direction, offset, nullptr, FloatRect()) {}
+
+ BoxReflection(ReflectionDirection direction,
+ float offset,
+ sk_sp<PaintRecord> mask,
+ const FloatRect& mask_bounds)
+ : direction_(direction),
+ offset_(offset),
+ mask_(std::move(mask)),
+ mask_bounds_(mask_bounds) {}
+
+ ReflectionDirection Direction() const { return direction_; }
+ float Offset() const { return offset_; }
+ const sk_sp<PaintRecord>& Mask() const { return mask_; }
+ const FloatRect& MaskBounds() const { return mask_bounds_; }
+
+ // Returns a matrix which maps points between the original content and its
+ // reflection. Reflections are self-inverse, so this matrix can be used to
+ // map in either direction.
+ SkMatrix ReflectionMatrix() const;
+
+ // Maps a source rectangle to the destination rectangle it can affect,
+ // including this reflection. Due to the symmetry of reflections, this can
+ // also be used to map from a destination rectangle to the source rectangle
+ // which contributes to it.
+ FloatRect MapRect(const FloatRect&) const;
+
+ private:
+ ReflectionDirection direction_;
+ float offset_;
+ sk_sp<PaintRecord> mask_;
+ FloatRect mask_bounds_;
+};
+
+inline bool operator==(const BoxReflection& a, const BoxReflection& b) {
+ return a.Direction() == b.Direction() && a.Offset() == b.Offset() &&
+ a.Mask() == b.Mask() && a.MaskBounds() == b.MaskBounds();
+}
+
+inline bool operator!=(const BoxReflection& a, const BoxReflection& b) {
+ return !(a == b);
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_BOX_REFLECTION_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc b/chromium/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc
new file mode 100644
index 00000000000..0122380a437
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc
@@ -0,0 +1,743 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/location.h"
+#include "base/single_thread_task_runner.h"
+#include "components/viz/common/resources/transferable_resource.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_compositor_support.h"
+#include "third_party/blink/renderer/platform/graphics/canvas_heuristic_parameters.h"
+#include "third_party/blink/renderer/platform/graphics/canvas_metrics.h"
+#include "third_party/blink/renderer/platform/graphics/canvas_resource.h"
+#include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/shared_context_rate_limiter.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
+#include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
+#include "third_party/blink/renderer/platform/graphics/web_graphics_context_3d_provider_wrapper.h"
+#include "third_party/blink/renderer/platform/histogram.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/scheduler/child/web_scheduler.h"
+#include "third_party/skia/include/core/SkData.h"
+#include "third_party/skia/include/core/SkSurface.h"
+
+namespace {
+enum {
+ InvalidMailboxIndex = -1,
+ MaxCanvasAnimationBacklog = 2, // Make sure the the GPU is never more than
+ // two animation frames behind.
+};
+} // namespace
+
+namespace blink {
+
+Canvas2DLayerBridge::Canvas2DLayerBridge(const IntSize& size,
+ int msaa_sample_count,
+ AccelerationMode acceleration_mode,
+ const CanvasColorParams& color_params)
+ : logger_(std::make_unique<Logger>()),
+ weak_ptr_factory_(this),
+ msaa_sample_count_(msaa_sample_count),
+ bytes_allocated_(0),
+ have_recorded_draw_commands_(false),
+ destruction_in_progress_(false),
+ filter_quality_(kLow_SkFilterQuality),
+ is_hidden_(false),
+ is_deferral_enabled_(true),
+ software_rendering_while_hidden_(false),
+ acceleration_mode_(acceleration_mode),
+ color_params_(color_params),
+ size_(size),
+ snapshot_state_(kInitialSnapshotState),
+ resource_host_(nullptr) {
+ // Used by browser tests to detect the use of a Canvas2DLayerBridge.
+ TRACE_EVENT_INSTANT0("test_gpu", "Canvas2DLayerBridgeCreation",
+ TRACE_EVENT_SCOPE_GLOBAL);
+ StartRecording();
+ // Clear the background transparent or opaque. Similar code at
+ // CanvasResourceProvider::Clear().
+ if (IsValid()) {
+ DCHECK(!resource_provider_);
+ DCHECK(recorder_);
+ recorder_->getRecordingCanvas()->clear(
+ color_params_.GetOpacityMode() == kOpaque ? SK_ColorBLACK
+ : SK_ColorTRANSPARENT);
+ }
+ DidDraw(FloatRect(FloatPoint(0, 0), FloatSize(size_)));
+}
+
+Canvas2DLayerBridge::~Canvas2DLayerBridge() {
+ BeginDestruction();
+ DCHECK(destruction_in_progress_);
+ layer_.reset();
+}
+
+void Canvas2DLayerBridge::StartRecording() {
+ DCHECK(is_deferral_enabled_);
+ recorder_ = std::make_unique<PaintRecorder>();
+ PaintCanvas* canvas =
+ recorder_->beginRecording(size_.Width(), size_.Height());
+ // Always save an initial frame, to support resetting the top level matrix
+ // and clip.
+ canvas->save();
+
+ if (resource_host_) {
+ resource_host_->RestoreCanvasMatrixClipStack(canvas);
+ }
+
+ recording_pixel_count_ = 0;
+}
+
+void Canvas2DLayerBridge::SetLoggerForTesting(std::unique_ptr<Logger> logger) {
+ logger_ = std::move(logger);
+}
+
+void Canvas2DLayerBridge::ResetResourceProvider() {
+ resource_provider_.reset();
+}
+
+bool Canvas2DLayerBridge::ShouldAccelerate(AccelerationHint hint) const {
+ bool accelerate;
+ if (software_rendering_while_hidden_) {
+ accelerate = false;
+ } else if (acceleration_mode_ == kForceAccelerationForTesting) {
+ accelerate = true;
+ } else if (acceleration_mode_ == kDisableAcceleration) {
+ accelerate = false;
+ } else {
+ accelerate = hint == kPreferAcceleration ||
+ hint == kPreferAccelerationAfterVisibilityChange;
+ }
+
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper =
+ SharedGpuContext::ContextProviderWrapper();
+ if (accelerate && (!context_provider_wrapper ||
+ context_provider_wrapper->ContextProvider()
+ ->ContextGL()
+ ->GetGraphicsResetStatusKHR() != GL_NO_ERROR))
+ accelerate = false;
+ return accelerate;
+}
+
+bool Canvas2DLayerBridge::IsAccelerated() const {
+ if (acceleration_mode_ == kDisableAcceleration)
+ return false;
+ if (IsHibernating())
+ return false;
+ if (software_rendering_while_hidden_)
+ return false;
+ if (resource_provider_)
+ return resource_provider_->IsAccelerated();
+
+ // Whether or not to accelerate is not yet resolved. Determine whether
+ // immediate presentation of the canvas would result in the canvas being
+ // accelerated. Presentation is assumed to be a 'PreferAcceleration'
+ // operation.
+ return ShouldAccelerate(kPreferAcceleration);
+}
+
+static void HibernateWrapper(base::WeakPtr<Canvas2DLayerBridge> bridge,
+ double /*idleDeadline*/) {
+ if (bridge) {
+ bridge->Hibernate();
+ } else {
+ Canvas2DLayerBridge::Logger local_logger;
+ local_logger.ReportHibernationEvent(
+ Canvas2DLayerBridge::
+ kHibernationAbortedDueToDestructionWhileHibernatePending);
+ }
+}
+
+static void HibernateWrapperForTesting(
+ base::WeakPtr<Canvas2DLayerBridge> bridge) {
+ HibernateWrapper(bridge, 0);
+}
+
+void Canvas2DLayerBridge::Hibernate() {
+ DCHECK(!IsHibernating());
+ DCHECK(hibernation_scheduled_);
+
+ hibernation_scheduled_ = false;
+
+ if (destruction_in_progress_) {
+ logger_->ReportHibernationEvent(kHibernationAbortedDueToPendingDestruction);
+ return;
+ }
+
+ if (!resource_provider_) {
+ logger_->ReportHibernationEvent(kHibernationAbortedBecauseNoSurface);
+ return;
+ }
+
+ if (!IsHidden()) {
+ logger_->ReportHibernationEvent(kHibernationAbortedDueToVisibilityChange);
+ return;
+ }
+
+ if (!IsValid()) {
+ logger_->ReportHibernationEvent(kHibernationAbortedDueGpuContextLoss);
+ return;
+ }
+
+ if (!IsAccelerated()) {
+ logger_->ReportHibernationEvent(
+ kHibernationAbortedDueToSwitchToUnacceleratedRendering);
+ return;
+ }
+
+ TRACE_EVENT0("blink", "Canvas2DLayerBridge::hibernate");
+ sk_sp<SkSurface> temp_hibernation_surface =
+ SkSurface::MakeRasterN32Premul(size_.Width(), size_.Height());
+ if (!temp_hibernation_surface) {
+ logger_->ReportHibernationEvent(kHibernationAbortedDueToAllocationFailure);
+ return;
+ }
+ // No HibernationEvent reported on success. This is on purppose to avoid
+ // non-complementary stats. Each HibernationScheduled event is paired with
+ // exactly one failure or exit event.
+ FlushRecording();
+ // The following checks that the flush succeeded, which should always be the
+ // case because flushRecording should only fail it it fails to allocate
+ // a surface, and we have an early exit at the top of this function for when
+ // 'this' does not already have a surface.
+ DCHECK(!have_recorded_draw_commands_);
+ SkPaint copy_paint;
+ copy_paint.setBlendMode(SkBlendMode::kSrc);
+ scoped_refptr<StaticBitmapImage> snapshot = resource_provider_->Snapshot();
+ temp_hibernation_surface->getCanvas()->drawImage(
+ snapshot->PaintImageForCurrentFrame().GetSkImage(), 0, 0, &copy_paint);
+ hibernation_image_ = temp_hibernation_surface->makeImageSnapshot();
+ ResetResourceProvider();
+ layer_->ClearTexture();
+
+ // shouldBeDirectComposited() may have changed.
+ if (resource_host_)
+ resource_host_->SetNeedsCompositingUpdate();
+ logger_->DidStartHibernating();
+}
+
+void Canvas2DLayerBridge::ReportResourceProviderCreationFailure() {
+ if (!resource_provider_creation_failed_at_least_once_) {
+ // Only count the failure once per instance so that the histogram may
+ // reflect the proportion of Canvas2DLayerBridge instances with surface
+ // allocation failures.
+ CanvasMetrics::CountCanvasContextUsage(
+ CanvasMetrics::kGPUAccelerated2DCanvasSurfaceCreationFailed);
+ resource_provider_creation_failed_at_least_once_ = true;
+ }
+}
+
+CanvasResourceProvider* Canvas2DLayerBridge::GetOrCreateResourceProvider(
+ AccelerationHint hint) {
+ if (context_lost_) {
+ DCHECK(!resource_provider_);
+ return nullptr;
+ }
+
+ if (resource_provider_)
+ return resource_provider_.get();
+
+ if (layer_ && !IsHibernating() && hint == kPreferAcceleration &&
+ acceleration_mode_ != kDisableAcceleration) {
+ return nullptr; // re-creation will happen through restore()
+ }
+
+ bool want_acceleration = ShouldAccelerate(hint);
+ if (CANVAS2D_BACKGROUND_RENDER_SWITCH_TO_CPU && IsHidden() &&
+ want_acceleration) {
+ want_acceleration = false;
+ software_rendering_while_hidden_ = true;
+ }
+
+ CanvasResourceProvider::ResourceUsage usage =
+ want_acceleration
+ ? CanvasResourceProvider::kAcceleratedCompositedResourceUsage
+ : CanvasResourceProvider::kSoftwareCompositedResourceUsage;
+
+ resource_provider_ = CanvasResourceProvider::Create(
+ size_, usage, SharedGpuContext::ContextProviderWrapper(),
+ msaa_sample_count_, color_params_);
+
+ if (resource_provider_) {
+ // Always save an initial frame, to support resetting the top level matrix
+ // and clip.
+ resource_provider_->Canvas()->save();
+ resource_provider_->SetFilterQuality(filter_quality_);
+ resource_provider_->SetResourceRecyclingEnabled(!IsHidden());
+ } else {
+ ReportResourceProviderCreationFailure();
+ }
+
+ if (resource_provider_ && resource_provider_->IsAccelerated() && !layer_) {
+ layer_ =
+ Platform::Current()->CompositorSupport()->CreateExternalTextureLayer(
+ this);
+ layer_->SetOpaque(ColorParams().GetOpacityMode() == kOpaque);
+ layer_->SetBlendBackgroundColor(ColorParams().GetOpacityMode() != kOpaque);
+ GraphicsLayer::RegisterContentsLayer(layer_->Layer());
+ layer_->SetNearestNeighbor(filter_quality_ == kNone_SkFilterQuality);
+ }
+
+ if (resource_provider_ && IsHibernating()) {
+ if (resource_provider_->IsAccelerated()) {
+ logger_->ReportHibernationEvent(kHibernationEndedNormally);
+ } else {
+ if (IsHidden()) {
+ logger_->ReportHibernationEvent(
+ kHibernationEndedWithSwitchToBackgroundRendering);
+ } else {
+ logger_->ReportHibernationEvent(kHibernationEndedWithFallbackToSW);
+ }
+ }
+
+ cc::PaintFlags copy_paint;
+ copy_paint.setBlendMode(SkBlendMode::kSrc);
+ PaintImageBuilder builder = PaintImageBuilder::WithDefault();
+ builder.set_image(hibernation_image_, PaintImage::GetNextContentId());
+ builder.set_id(PaintImage::GetNextId());
+ resource_provider_->Canvas()->drawImage(builder.TakePaintImage(), 0, 0,
+ &copy_paint);
+ hibernation_image_.reset();
+
+ if (resource_host_) {
+ resource_host_->UpdateMemoryUsage();
+
+ if (!is_deferral_enabled_) {
+ resource_host_->RestoreCanvasMatrixClipStack(
+ resource_provider_->Canvas());
+ }
+
+ // shouldBeDirectComposited() may have changed.
+ resource_host_->SetNeedsCompositingUpdate();
+ }
+ }
+
+ return resource_provider_.get();
+}
+
+PaintCanvas* Canvas2DLayerBridge::Canvas() {
+ if (!is_deferral_enabled_) {
+ GetOrCreateResourceProvider();
+ return resource_provider_ ? resource_provider_->Canvas() : nullptr;
+ }
+ return recorder_->getRecordingCanvas();
+}
+
+void Canvas2DLayerBridge::DisableDeferral(DisableDeferralReason reason) {
+ // Disabling deferral is permanent: once triggered by disableDeferral()
+ // we stay in immediate mode indefinitely. This is a performance heuristic
+ // that significantly helps a number of use cases. The rationale is that if
+ // immediate rendering was needed once, it is likely to be needed at least
+ // once per frame, which eliminates the possibility for inter-frame
+ // overdraw optimization. Furthermore, in cases where immediate mode is
+ // required multiple times per frame, the repeated flushing of deferred
+ // commands would cause significant overhead, so it is better to just stop
+ // trying to defer altogether.
+ if (!is_deferral_enabled_)
+ return;
+
+ DEFINE_STATIC_LOCAL(EnumerationHistogram, gpu_disabled_histogram,
+ ("Canvas.GPUAccelerated2DCanvasDisableDeferralReason",
+ kDisableDeferralReasonCount));
+ gpu_disabled_histogram.Count(reason);
+ CanvasMetrics::CountCanvasContextUsage(
+ CanvasMetrics::kGPUAccelerated2DCanvasDeferralDisabled);
+ FlushRecording();
+ // Because we will be discarding the recorder, if the flush failed
+ // content will be lost -> force m_haveRecordedDrawCommands to false
+ have_recorded_draw_commands_ = false;
+
+ is_deferral_enabled_ = false;
+ recorder_.reset();
+ // install the current matrix/clip stack onto the immediate canvas
+ GetOrCreateResourceProvider();
+ if (resource_host_ && resource_provider_)
+ resource_host_->RestoreCanvasMatrixClipStack(resource_provider_->Canvas());
+}
+
+void Canvas2DLayerBridge::BeginDestruction() {
+ if (destruction_in_progress_)
+ return;
+ if (IsHibernating())
+ logger_->ReportHibernationEvent(kHibernationEndedWithTeardown);
+ hibernation_image_.reset();
+ recorder_.reset();
+ destruction_in_progress_ = true;
+ SetIsHidden(true);
+ ResetResourceProvider();
+
+ if (layer_ && acceleration_mode_ != kDisableAcceleration) {
+ GraphicsLayer::UnregisterContentsLayer(layer_->Layer());
+ layer_->ClearTexture();
+ // Orphaning the layer is required to trigger the recration of a new layer
+ // in the case where destruction is caused by a canvas resize. Test:
+ // virtual/gpu/fast/canvas/canvas-resize-after-paint-without-layout.html
+ layer_->Layer()->RemoveFromParent();
+ }
+
+ DCHECK(!bytes_allocated_);
+}
+
+void Canvas2DLayerBridge::SetFilterQuality(SkFilterQuality filter_quality) {
+ DCHECK(!destruction_in_progress_);
+ filter_quality_ = filter_quality;
+ if (resource_provider_)
+ resource_provider_->SetFilterQuality(filter_quality);
+ if (layer_)
+ layer_->SetNearestNeighbor(filter_quality == kNone_SkFilterQuality);
+}
+
+void Canvas2DLayerBridge::SetIsHidden(bool hidden) {
+ bool new_hidden_value = hidden || destruction_in_progress_;
+ if (is_hidden_ == new_hidden_value)
+ return;
+
+ is_hidden_ = new_hidden_value;
+ if (resource_provider_)
+ resource_provider_->SetResourceRecyclingEnabled(!IsHidden());
+
+ if (CANVAS2D_HIBERNATION_ENABLED && resource_provider_ && IsHidden() &&
+ !destruction_in_progress_ && !hibernation_scheduled_) {
+ if (layer_)
+ layer_->ClearTexture();
+ logger_->ReportHibernationEvent(kHibernationScheduled);
+ hibernation_scheduled_ = true;
+ if (dont_use_idle_scheduling_for_testing_) {
+ Platform::Current()->CurrentThread()->GetTaskRunner()->PostTask(
+ FROM_HERE, WTF::Bind(&HibernateWrapperForTesting,
+ weak_ptr_factory_.GetWeakPtr()));
+ } else {
+ Platform::Current()->CurrentThread()->Scheduler()->PostIdleTask(
+ FROM_HERE,
+ WTF::Bind(&HibernateWrapper, weak_ptr_factory_.GetWeakPtr()));
+ }
+ }
+ if (!IsHidden() && software_rendering_while_hidden_) {
+ FlushRecording();
+ cc::PaintFlags copy_paint;
+ copy_paint.setBlendMode(SkBlendMode::kSrc);
+
+ std::unique_ptr<CanvasResourceProvider> old_resource_provider =
+ std::move(resource_provider_);
+ ResetResourceProvider();
+
+ software_rendering_while_hidden_ = false;
+ GetOrCreateResourceProvider(kPreferAccelerationAfterVisibilityChange);
+
+ if (resource_provider_) {
+ if (old_resource_provider) {
+ cc::PaintImage snapshot =
+ old_resource_provider->Snapshot()->PaintImageForCurrentFrame();
+ resource_provider_->Canvas()->drawImage(snapshot, 0, 0, &copy_paint);
+ }
+ if (resource_host_ && !is_deferral_enabled_) {
+ resource_host_->RestoreCanvasMatrixClipStack(
+ resource_provider_->Canvas());
+ }
+ } else {
+ // New resource provider could not be created. Stay with old one.
+ resource_provider_ = std::move(old_resource_provider);
+ }
+ }
+ if (!IsHidden() && IsHibernating()) {
+ GetOrCreateResourceProvider(); // Rude awakening
+ }
+}
+
+void Canvas2DLayerBridge::DrawFullImage(const cc::PaintImage& image) {
+ Canvas()->drawImage(image, 0, 0);
+}
+
+bool Canvas2DLayerBridge::WritePixels(const SkImageInfo& orig_info,
+ const void* pixels,
+ size_t row_bytes,
+ int x,
+ int y) {
+ if (!GetOrCreateResourceProvider())
+ return false;
+ if (x <= 0 && y <= 0 && x + orig_info.width() >= size_.Width() &&
+ y + orig_info.height() >= size_.Height()) {
+ SkipQueuedDrawCommands();
+ } else {
+ FlushRecording();
+ }
+
+ GetOrCreateResourceProvider()->WritePixels(orig_info, pixels, row_bytes, x,
+ y);
+ DidDraw(FloatRect(x, y, orig_info.width(), orig_info.height()));
+
+ return true;
+}
+
+void Canvas2DLayerBridge::SkipQueuedDrawCommands() {
+ if (have_recorded_draw_commands_) {
+ recorder_->finishRecordingAsPicture();
+ StartRecording();
+ have_recorded_draw_commands_ = false;
+ }
+
+ if (is_deferral_enabled_) {
+ if (rate_limiter_)
+ rate_limiter_->Reset();
+ }
+}
+
+void Canvas2DLayerBridge::FlushRecording() {
+ DCHECK(!destruction_in_progress_);
+
+ if (have_recorded_draw_commands_ && GetOrCreateResourceProvider()) {
+ TRACE_EVENT0("cc", "Canvas2DLayerBridge::flushRecording");
+
+ PaintCanvas* canvas = GetOrCreateResourceProvider()->Canvas();
+ {
+ sk_sp<PaintRecord> recording = recorder_->finishRecordingAsPicture();
+ canvas->drawPicture(recording);
+ }
+
+ // Rastering the recording would have locked images, since we've flushed
+ // all recorded ops, we should relase all locked images as well.
+ GetOrCreateResourceProvider()->ReleaseLockedImages();
+
+ if (is_deferral_enabled_)
+ StartRecording();
+ have_recorded_draw_commands_ = false;
+ }
+}
+
+bool Canvas2DLayerBridge::IsValid() const {
+ return const_cast<Canvas2DLayerBridge*>(this)->CheckResourceProviderValid();
+}
+
+bool Canvas2DLayerBridge::CheckResourceProviderValid() {
+ DCHECK(!destruction_in_progress_);
+ if (destruction_in_progress_)
+ return false;
+ if (IsHibernating())
+ return true;
+ if (!layer_ || acceleration_mode_ == kDisableAcceleration)
+ return true;
+ if (context_lost_)
+ return false;
+ if (resource_provider_ && resource_provider_->IsAccelerated() &&
+ resource_provider_->IsGpuContextLost()) {
+ context_lost_ = true;
+ ResetResourceProvider();
+ if (resource_host_)
+ resource_host_->NotifySurfaceInvalid();
+ CanvasMetrics::CountCanvasContextUsage(
+ CanvasMetrics::kAccelerated2DCanvasGPUContextLost);
+ return false;
+ }
+ if (!GetOrCreateResourceProvider())
+ return false;
+ return resource_provider_.get();
+}
+
+bool Canvas2DLayerBridge::Restore() {
+ DCHECK(!destruction_in_progress_);
+ DCHECK(context_lost_);
+ if (destruction_in_progress_ || !IsAccelerated())
+ return false;
+ DCHECK(!resource_provider_);
+
+ gpu::gles2::GLES2Interface* shared_gl = nullptr;
+ layer_->ClearTexture();
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper =
+ SharedGpuContext::ContextProviderWrapper();
+ if (context_provider_wrapper)
+ shared_gl = context_provider_wrapper->ContextProvider()->ContextGL();
+
+ if (shared_gl && shared_gl->GetGraphicsResetStatusKHR() == GL_NO_ERROR) {
+ std::unique_ptr<CanvasResourceProvider> resource_provider =
+ CanvasResourceProvider::Create(
+ size_, CanvasResourceProvider::kAcceleratedCompositedResourceUsage,
+ std::move(context_provider_wrapper), msaa_sample_count_,
+ color_params_);
+
+ if (!resource_provider)
+ ReportResourceProviderCreationFailure();
+
+ // The current paradigm does not support switching from accelerated to
+ // non-accelerated, which would be tricky due to changes to the layer tree,
+ // which can only happen at specific times during the document lifecycle.
+ // Therefore, we can only accept the restored surface if it is accelerated.
+ if (resource_provider && resource_provider->IsAccelerated()) {
+ resource_provider_ = std::move(resource_provider);
+ // FIXME: draw sad canvas picture into new buffer crbug.com/243842
+ }
+ context_lost_ = false;
+ }
+
+ if (resource_host_)
+ resource_host_->UpdateMemoryUsage();
+
+ return resource_provider_.get();
+}
+
+bool Canvas2DLayerBridge::PrepareTransferableResource(
+ cc::SharedBitmapIdRegistrar* bitmap_registrar,
+ viz::TransferableResource* out_resource,
+ std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback) {
+ if (destruction_in_progress_) {
+ // It can be hit in the following sequence.
+ // 1. Canvas draws something.
+ // 2. The compositor begins the frame.
+ // 3. Javascript makes a context be lost.
+ // 4. Here.
+ return false;
+ }
+
+ DCHECK(layer_); // This explodes if FinalizeFrame() was not called.
+
+ frames_since_last_commit_ = 0;
+ if (rate_limiter_) {
+ rate_limiter_->Reset();
+ }
+
+ // if hibernating but not hidden, we want to wake up from
+ // hibernation
+ if ((IsHibernating() || software_rendering_while_hidden_) && IsHidden())
+ return false;
+
+ if (!IsValid())
+ return false;
+
+ // If the context is lost, we don't know if we should be producing GPU or
+ // software frames, until we get a new context, since the compositor will
+ // be trying to get a new context and may change modes.
+ if (!GetOrCreateResourceProvider())
+ return false;
+
+ FlushRecording();
+ scoped_refptr<CanvasResource> frame = resource_provider_->ProduceFrame();
+ if (frame && frame->IsValid()) {
+ // Note frame is kept alive via a reference kept in out_release_callback.
+ bool success =
+ frame->PrepareTransferableResource(out_resource, out_release_callback);
+ if (success)
+ out_resource->color_space = color_params_.GetSamplerGfxColorSpace();
+ return success;
+ }
+ return false;
+}
+
+WebLayer* Canvas2DLayerBridge::Layer() {
+ DCHECK(!destruction_in_progress_);
+ // Trigger lazy layer creation
+ GetOrCreateResourceProvider(kPreferAcceleration);
+ return layer_ ? layer_->Layer() : nullptr;
+}
+
+void Canvas2DLayerBridge::DidDraw(const FloatRect& rect) {
+ if (snapshot_state_ == kDidAcquireSnapshot)
+ snapshot_state_ = kDrawnToAfterSnapshot;
+ if (is_deferral_enabled_) {
+ have_recorded_draw_commands_ = true;
+ IntRect pixel_bounds = EnclosingIntRect(rect);
+ CheckedNumeric<int> pixel_bounds_size = pixel_bounds.Width();
+ pixel_bounds_size *= pixel_bounds.Height();
+ recording_pixel_count_ += pixel_bounds_size;
+ if (!recording_pixel_count_.IsValid()) {
+ DisableDeferral(kDisableDeferralReasonExpensiveOverdrawHeuristic);
+ return;
+ }
+ CheckedNumeric<int> threshold_size = size_.Width();
+ threshold_size *= size_.Height();
+ threshold_size *= CanvasHeuristicParameters::kExpensiveOverdrawThreshold;
+ if (!threshold_size.IsValid()) {
+ DisableDeferral(kDisableDeferralReasonExpensiveOverdrawHeuristic);
+ return;
+ }
+ if (recording_pixel_count_.ValueOrDie() >= threshold_size.ValueOrDie()) {
+ DisableDeferral(kDisableDeferralReasonExpensiveOverdrawHeuristic);
+ }
+ }
+}
+
+void Canvas2DLayerBridge::FinalizeFrame() {
+ TRACE_EVENT0("blink", "Canvas2DLayerBridge::FinalizeFrame");
+ DCHECK(!destruction_in_progress_);
+
+ // Make sure surface is ready for painting: fix the rendering mode now
+ // because it will be too late during the paint invalidation phase.
+ if (!GetOrCreateResourceProvider(kPreferAcceleration))
+ return;
+
+ ++frames_since_last_commit_;
+
+ if (frames_since_last_commit_ >= 2) {
+ GetOrCreateResourceProvider()->FlushSkia();
+ if (IsAccelerated()) {
+ if (!rate_limiter_) {
+ rate_limiter_ =
+ SharedContextRateLimiter::Create(MaxCanvasAnimationBacklog);
+ }
+ }
+ }
+
+ if (rate_limiter_) {
+ rate_limiter_->Tick();
+ }
+}
+
+void Canvas2DLayerBridge::DoPaintInvalidation(const FloatRect& dirty_rect) {
+ DCHECK(!destruction_in_progress_);
+ if (layer_ && acceleration_mode_ != kDisableAcceleration)
+ layer_->Layer()->InvalidateRect(EnclosingIntRect(dirty_rect));
+}
+
+scoped_refptr<StaticBitmapImage> Canvas2DLayerBridge::NewImageSnapshot(
+ AccelerationHint hint) {
+ if (snapshot_state_ == kInitialSnapshotState)
+ snapshot_state_ = kDidAcquireSnapshot;
+ if (IsHibernating())
+ return StaticBitmapImage::Create(hibernation_image_);
+ if (!IsValid())
+ return nullptr;
+ if (!GetOrCreateResourceProvider(hint))
+ return nullptr;
+ FlushRecording();
+ return GetOrCreateResourceProvider()->Snapshot();
+}
+
+void Canvas2DLayerBridge::WillOverwriteCanvas() {
+ SkipQueuedDrawCommands();
+}
+
+void Canvas2DLayerBridge::Logger::ReportHibernationEvent(
+ HibernationEvent event) {
+ DEFINE_STATIC_LOCAL(EnumerationHistogram, hibernation_histogram,
+ ("Canvas.HibernationEvents", kHibernationEventCount));
+ hibernation_histogram.Count(event);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h b/chromium/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h
new file mode 100644
index 00000000000..7f062491ff6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CANVAS_2D_LAYER_BRIDGE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CANVAS_2D_LAYER_BRIDGE_H_
+
+#include <memory>
+
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "build/build_config.h"
+#include "cc/layers/texture_layer_client.h"
+#include "third_party/blink/public/platform/web_external_texture_layer.h"
+#include "third_party/blink/renderer/platform/geometry/int_size.h"
+#include "third_party/blink/renderer/platform/graphics/canvas_color_params.h"
+#include "third_party/blink/renderer/platform/graphics/canvas_resource_host.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_types.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_recorder.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/checked_numeric.h"
+#include "third_party/blink/renderer/platform/wtf/deque.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/khronos/GLES2/gl2.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+#include "ui/gfx/color_space.h"
+
+struct SkImageInfo;
+
+namespace blink {
+
+class Canvas2DLayerBridgeTest;
+class CanvasResourceProvider;
+class SharedContextRateLimiter;
+class StaticBitmapImage;
+
+#if defined(OS_MACOSX)
+// Canvas hibernation is currently disabled on MacOS X due to a bug that causes
+// content loss. TODO: Find a better fix for crbug.com/588434
+#define CANVAS2D_HIBERNATION_ENABLED 0
+#else
+#define CANVAS2D_HIBERNATION_ENABLED 1
+#endif
+
+// TODO: Fix background rendering and remove this workaround. crbug.com/600386
+#define CANVAS2D_BACKGROUND_RENDER_SWITCH_TO_CPU 0
+
+class PLATFORM_EXPORT Canvas2DLayerBridge : public cc::TextureLayerClient {
+ WTF_MAKE_NONCOPYABLE(Canvas2DLayerBridge);
+
+ public:
+ enum AccelerationMode {
+ kDisableAcceleration,
+ kEnableAcceleration,
+ kForceAccelerationForTesting,
+ };
+
+ Canvas2DLayerBridge(const IntSize&,
+ int msaa_sample_count,
+ AccelerationMode,
+ const CanvasColorParams&);
+
+ ~Canvas2DLayerBridge() override;
+
+ // cc::TextureLayerClient implementation.
+ bool PrepareTransferableResource(
+ cc::SharedBitmapIdRegistrar* bitmap_registrar,
+ viz::TransferableResource* out_resource,
+ std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback)
+ override;
+
+ void FinalizeFrame();
+ void SetIsHidden(bool);
+ void DidDraw(const FloatRect&);
+ void DoPaintInvalidation(const FloatRect& dirty_rect);
+ WebLayer* Layer();
+ bool Restore();
+ void DisableDeferral(DisableDeferralReason);
+ void SetFilterQuality(SkFilterQuality);
+
+ // virtual for unit testing
+ virtual void WillOverwriteCanvas();
+ virtual void DrawFullImage(const cc::PaintImage&);
+ virtual void DidRestoreCanvasMatrixClipStack(cc::PaintCanvas*) {}
+ virtual bool IsAccelerated() const;
+
+ PaintCanvas* Canvas();
+ bool IsValid() const;
+ bool WritePixels(const SkImageInfo&,
+ const void* pixels,
+ size_t row_bytes,
+ int x,
+ int y);
+ void DontUseIdleSchedulingForTesting() {
+ dont_use_idle_scheduling_for_testing_ = true;
+ }
+ void SetCanvasResourceHost(CanvasResourceHost* host) {
+ resource_host_ = host;
+ }
+
+ void BeginDestruction();
+ void Hibernate();
+ bool IsHibernating() const { return hibernation_image_; }
+ const CanvasColorParams& ColorParams() const { return color_params_; }
+
+ bool HasRecordedDrawCommands() { return have_recorded_draw_commands_; }
+
+ scoped_refptr<StaticBitmapImage> NewImageSnapshot(AccelerationHint);
+ bool WasDrawnToAfterSnapshot() const {
+ return snapshot_state_ == kDrawnToAfterSnapshot;
+ }
+
+ // The values of the enum entries must not change because they are used for
+ // usage metrics histograms. New values can be added to the end.
+ enum HibernationEvent {
+ kHibernationScheduled = 0,
+ kHibernationAbortedDueToDestructionWhileHibernatePending = 1,
+ kHibernationAbortedDueToPendingDestruction = 2,
+ kHibernationAbortedDueToVisibilityChange = 3,
+ kHibernationAbortedDueGpuContextLoss = 4,
+ kHibernationAbortedDueToSwitchToUnacceleratedRendering = 5,
+ kHibernationAbortedDueToAllocationFailure = 6,
+ kHibernationEndedNormally = 7,
+ kHibernationEndedWithSwitchToBackgroundRendering = 8,
+ kHibernationEndedWithFallbackToSW = 9,
+ kHibernationEndedWithTeardown = 10,
+ kHibernationAbortedBecauseNoSurface = 11,
+
+ kHibernationEventCount = 12,
+ };
+
+ class PLATFORM_EXPORT Logger {
+ public:
+ virtual void ReportHibernationEvent(HibernationEvent);
+ virtual void DidStartHibernating() {}
+ virtual ~Logger() = default;
+ };
+
+ void SetLoggerForTesting(std::unique_ptr<Logger>);
+ CanvasResourceProvider* GetResourceProvider() const {
+ return resource_provider_.get();
+ }
+ CanvasResourceProvider* GetOrCreateResourceProvider(
+ AccelerationHint = kPreferAcceleration);
+ void ResetResourceProvider();
+
+ private:
+ bool IsHidden() { return is_hidden_; }
+ bool CheckResourceProviderValid();
+
+ void StartRecording();
+ void SkipQueuedDrawCommands();
+ void FlushRecording();
+ void ReportResourceProviderCreationFailure();
+
+ bool ShouldAccelerate(AccelerationHint) const;
+
+ std::unique_ptr<CanvasResourceProvider> resource_provider_;
+ std::unique_ptr<PaintRecorder> recorder_;
+ sk_sp<SkImage> hibernation_image_;
+ std::unique_ptr<WebExternalTextureLayer> layer_;
+ std::unique_ptr<SharedContextRateLimiter> rate_limiter_;
+ std::unique_ptr<Logger> logger_;
+ base::WeakPtrFactory<Canvas2DLayerBridge> weak_ptr_factory_;
+ int msaa_sample_count_;
+ int frames_since_last_commit_ = 0;
+ size_t bytes_allocated_;
+ bool have_recorded_draw_commands_;
+ bool destruction_in_progress_;
+ SkFilterQuality filter_quality_;
+ bool is_hidden_;
+ bool is_deferral_enabled_;
+ bool software_rendering_while_hidden_;
+ bool resource_provider_creation_failed_at_least_once_ = false;
+ bool hibernation_scheduled_ = false;
+ bool dont_use_idle_scheduling_for_testing_ = false;
+ bool context_lost_ = false;
+
+ friend class Canvas2DLayerBridgeTest;
+ friend class CanvasRenderingContext2DTest;
+ friend class HTMLCanvasPainterTestForSPv2;
+
+ AccelerationMode acceleration_mode_;
+ CanvasColorParams color_params_;
+ IntSize size_;
+ CheckedNumeric<int> recording_pixel_count_;
+
+ enum SnapshotState {
+ kInitialSnapshotState,
+ kDidAcquireSnapshot,
+ kDrawnToAfterSnapshot,
+ };
+ mutable SnapshotState snapshot_state_;
+
+ CanvasResourceHost* resource_host_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc b/chromium/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc
new file mode 100644
index 00000000000..b19e9ae1528
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc
@@ -0,0 +1,1407 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h"
+
+#include <utility>
+
+#include "base/location.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "build/build_config.h"
+#include "cc/test/skia_common.h"
+#include "cc/test/stub_decode_cache.h"
+#include "components/viz/common/resources/single_release_callback.h"
+#include "components/viz/common/resources/transferable_resource.h"
+#include "components/viz/test/test_gpu_memory_buffer_manager.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "gpu/command_buffer/common/capabilities.h"
+#include "skia/ext/texture_handle.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_thread.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/graphics/canvas_resource_host.h"
+#include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
+#include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
+#include "third_party/blink/renderer/platform/graphics/test/fake_gles2_interface.h"
+#include "third_party/blink/renderer/platform/graphics/test/fake_web_graphics_context_3d_provider.h"
+#include "third_party/blink/renderer/platform/graphics/web_graphics_context_3d_provider_wrapper.h"
+#include "third_party/blink/renderer/platform/scheduler/child/web_scheduler.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
+#include "third_party/blink/renderer/platform/waitable_event.h"
+#include "third_party/skia/include/core/SkSurface.h"
+#include "third_party/skia/include/gpu/gl/GrGLTypes.h"
+
+#include <memory>
+
+using testing::AnyNumber;
+using testing::AtLeast;
+using testing::InSequence;
+using testing::Pointee;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::Test;
+using testing::_;
+
+namespace blink {
+
+namespace {
+
+class Canvas2DLayerBridgePtr {
+ public:
+ Canvas2DLayerBridgePtr() = default;
+ Canvas2DLayerBridgePtr(std::unique_ptr<Canvas2DLayerBridge> layer_bridge)
+ : layer_bridge_(std::move(layer_bridge)) {}
+
+ ~Canvas2DLayerBridgePtr() { Clear(); }
+
+ void Clear() {
+ if (layer_bridge_) {
+ layer_bridge_->BeginDestruction();
+ layer_bridge_.reset();
+ }
+ }
+
+ void operator=(std::unique_ptr<Canvas2DLayerBridge> layer_bridge) {
+ DCHECK(!layer_bridge_); // Existing ref must be removed with Clear()
+ layer_bridge_ = std::move(layer_bridge);
+ }
+
+ Canvas2DLayerBridge* operator->() { return layer_bridge_.get(); }
+ Canvas2DLayerBridge* Get() { return layer_bridge_.get(); }
+
+ private:
+ std::unique_ptr<Canvas2DLayerBridge> layer_bridge_;
+};
+
+class MockGLES2InterfaceWithImageSupport : public FakeGLES2Interface {
+ public:
+ MOCK_METHOD0(Flush, void());
+ MOCK_METHOD4(CreateImageCHROMIUM,
+ GLuint(ClientBuffer, GLsizei, GLsizei, GLenum));
+ MOCK_METHOD1(DestroyImageCHROMIUM, void(GLuint));
+ MOCK_METHOD2(GenTextures, void(GLsizei, GLuint*));
+ MOCK_METHOD2(DeleteTextures, void(GLsizei, const GLuint*));
+ // Fake
+ void GenMailboxCHROMIUM(GLbyte* name) {
+ name[0] = 1; // Make non-zero mailbox names
+ }
+};
+
+class FakePlatformSupport : public TestingPlatformSupport {
+ public:
+ void RunUntilStop() const { base::RunLoop().Run(); }
+
+ void StopRunning() const { base::RunLoop().Quit(); }
+
+ private:
+ gpu::GpuMemoryBufferManager* GetGpuMemoryBufferManager() override {
+ return &test_gpu_memory_buffer_manager_;
+ }
+
+ viz::TestGpuMemoryBufferManager test_gpu_memory_buffer_manager_;
+};
+
+class ImageTrackingDecodeCache : public cc::StubDecodeCache {
+ public:
+ ImageTrackingDecodeCache() = default;
+ ~ImageTrackingDecodeCache() override { EXPECT_EQ(num_locked_images_, 0); }
+
+ cc::DecodedDrawImage GetDecodedImageForDraw(
+ const cc::DrawImage& image) override {
+ EXPECT_FALSE(disallow_cache_use_);
+
+ num_locked_images_++;
+ decoded_images_.push_back(image);
+ SkBitmap bitmap;
+ bitmap.allocPixelsFlags(SkImageInfo::MakeN32Premul(10, 10),
+ SkBitmap::kZeroPixels_AllocFlag);
+ sk_sp<SkImage> sk_image = SkImage::MakeFromBitmap(bitmap);
+ return cc::DecodedDrawImage(sk_image, SkSize::Make(0, 0),
+ SkSize::Make(1, 1), kLow_SkFilterQuality,
+ !budget_exceeded_);
+ }
+
+ void set_budget_exceeded(bool exceeded) { budget_exceeded_ = exceeded; }
+ void set_disallow_cache_use(bool disallow) { disallow_cache_use_ = disallow; }
+
+ void DrawWithImageFinished(
+ const cc::DrawImage& image,
+ const cc::DecodedDrawImage& decoded_image) override {
+ EXPECT_FALSE(disallow_cache_use_);
+ num_locked_images_--;
+ }
+
+ const std::vector<cc::DrawImage>& decoded_images() const {
+ return decoded_images_;
+ }
+ int num_locked_images() const { return num_locked_images_; }
+
+ private:
+ std::vector<cc::DrawImage> decoded_images_;
+ int num_locked_images_ = 0;
+ bool budget_exceeded_ = false;
+ bool disallow_cache_use_ = false;
+};
+
+} // anonymous namespace
+
+class Canvas2DLayerBridgeTest : public Test {
+ public:
+ std::unique_ptr<Canvas2DLayerBridge> MakeBridge(
+ const IntSize& size,
+ Canvas2DLayerBridge::AccelerationMode acceleration_mode) {
+ std::unique_ptr<Canvas2DLayerBridge> bridge =
+ std::make_unique<Canvas2DLayerBridge>(size, 0, acceleration_mode,
+ CanvasColorParams());
+ bridge->DontUseIdleSchedulingForTesting();
+ return bridge;
+ }
+ void SetUp() override {
+ auto factory = [](FakeGLES2Interface* gl, ImageTrackingDecodeCache* cache,
+ bool* gpu_compositing_disabled)
+ -> std::unique_ptr<WebGraphicsContext3DProvider> {
+ *gpu_compositing_disabled = false;
+ return std::make_unique<FakeWebGraphicsContext3DProvider>(gl, cache);
+ };
+ SharedGpuContext::SetContextProviderFactoryForTesting(WTF::BindRepeating(
+ factory, WTF::Unretained(&gl_), WTF::Unretained(&image_decode_cache_)));
+ }
+ void TearDown() override { SharedGpuContext::ResetForTesting(); }
+
+ protected:
+ MockGLES2InterfaceWithImageSupport gl_;
+ ImageTrackingDecodeCache image_decode_cache_;
+
+ void FullLifecycleTest() {
+ Canvas2DLayerBridgePtr bridge(std::make_unique<Canvas2DLayerBridge>(
+ IntSize(300, 150), 0, Canvas2DLayerBridge::kDisableAcceleration,
+ CanvasColorParams()));
+
+ const GrGLTextureInfo* texture_info =
+ skia::GrBackendObjectToGrGLTextureInfo(
+ bridge->NewImageSnapshot(kPreferAcceleration)
+ ->PaintImageForCurrentFrame()
+ .GetSkImage()
+ ->getTextureHandle(true));
+ EXPECT_EQ(texture_info, nullptr);
+ bridge.Clear();
+ }
+
+ void FallbackToSoftwareIfContextLost() {
+ gl_.SetIsContextLost(true);
+ Canvas2DLayerBridgePtr bridge(std::make_unique<Canvas2DLayerBridge>(
+ IntSize(300, 150), 0, Canvas2DLayerBridge::kEnableAcceleration,
+ CanvasColorParams()));
+ EXPECT_TRUE(bridge->IsValid());
+ EXPECT_FALSE(bridge->IsAccelerated());
+ }
+
+ void FallbackToSoftwareOnFailedTextureAlloc() {
+ {
+ // No fallback case.
+ Canvas2DLayerBridgePtr bridge(std::make_unique<Canvas2DLayerBridge>(
+ IntSize(300, 150), 0, Canvas2DLayerBridge::kEnableAcceleration,
+ CanvasColorParams()));
+ EXPECT_TRUE(bridge->IsValid());
+ EXPECT_TRUE(bridge->IsAccelerated());
+ scoped_refptr<StaticBitmapImage> snapshot =
+ bridge->NewImageSnapshot(kPreferAcceleration);
+ EXPECT_TRUE(bridge->IsAccelerated());
+ EXPECT_TRUE(snapshot->IsTextureBacked());
+ }
+
+ {
+ // Fallback case.
+ GrContext* gr = SharedGpuContext::ContextProviderWrapper()
+ ->ContextProvider()
+ ->GetGrContext();
+ Canvas2DLayerBridgePtr bridge(std::make_unique<Canvas2DLayerBridge>(
+ IntSize(300, 150), 0, Canvas2DLayerBridge::kEnableAcceleration,
+ CanvasColorParams()));
+ EXPECT_TRUE(bridge->IsValid());
+ EXPECT_TRUE(bridge->IsAccelerated()); // We don't yet know that
+ // allocation will fail.
+ // This will cause SkSurface_Gpu creation to fail without
+ // Canvas2DLayerBridge otherwise detecting that anything was disabled.
+ gr->abandonContext();
+ scoped_refptr<StaticBitmapImage> snapshot =
+ bridge->NewImageSnapshot(kPreferAcceleration);
+ EXPECT_FALSE(bridge->IsAccelerated());
+ EXPECT_FALSE(snapshot->IsTextureBacked());
+ }
+ }
+
+ void NoDrawOnContextLostTest() {
+ Canvas2DLayerBridgePtr bridge(std::make_unique<Canvas2DLayerBridge>(
+ IntSize(300, 150), 0, Canvas2DLayerBridge::kForceAccelerationForTesting,
+ CanvasColorParams()));
+ EXPECT_TRUE(bridge->IsValid());
+ PaintFlags flags;
+ uint32_t gen_id = bridge->GetOrCreateResourceProvider()->ContentUniqueID();
+ bridge->Canvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), flags);
+ EXPECT_EQ(gen_id, bridge->GetOrCreateResourceProvider()->ContentUniqueID());
+ gl_.SetIsContextLost(true);
+ EXPECT_EQ(gen_id, bridge->GetOrCreateResourceProvider()->ContentUniqueID());
+ bridge->Canvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), flags);
+ EXPECT_EQ(gen_id, bridge->GetOrCreateResourceProvider()->ContentUniqueID());
+ // This results in the internal surface being torn down in response to the
+ // context loss.
+ EXPECT_FALSE(bridge->IsValid());
+ EXPECT_EQ(nullptr, bridge->GetOrCreateResourceProvider());
+ // The following passes by not crashing
+ bridge->Canvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), flags);
+ bridge->NewImageSnapshot(kPreferAcceleration);
+ }
+
+ void PrepareMailboxWhenContextIsLost() {
+ Canvas2DLayerBridgePtr bridge(std::make_unique<Canvas2DLayerBridge>(
+ IntSize(300, 150), 0, Canvas2DLayerBridge::kForceAccelerationForTesting,
+ CanvasColorParams()));
+
+ EXPECT_TRUE(bridge->IsAccelerated());
+ bridge->FinalizeFrame(); // Trigger the creation of a backing store
+ // When the context is lost we are not sure if we should still be producing
+ // GL frames for the compositor or not, so fail to generate frames.
+ gl_.SetIsContextLost(true);
+
+ viz::TransferableResource resource;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback;
+ EXPECT_FALSE(bridge->PrepareTransferableResource(nullptr, &resource,
+ &release_callback));
+ }
+
+ void PrepareMailboxWhenContextIsLostWithFailedRestore() {
+ Canvas2DLayerBridgePtr bridge(std::make_unique<Canvas2DLayerBridge>(
+ IntSize(300, 150), 0, Canvas2DLayerBridge::kForceAccelerationForTesting,
+ CanvasColorParams()));
+
+ bridge->GetOrCreateResourceProvider();
+ EXPECT_TRUE(bridge->IsValid());
+ // When the context is lost we are not sure if we should still be producing
+ // GL frames for the compositor or not, so fail to generate frames.
+ gl_.SetIsContextLost(true);
+ EXPECT_FALSE(bridge->IsValid());
+
+ // Restoration will fail because
+ // Platform::createSharedOffscreenGraphicsContext3DProvider() is stubbed
+ // in unit tests. This simulates what would happen when attempting to
+ // restore while the GPU process is down.
+ bridge->Restore();
+
+ viz::TransferableResource resource;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback;
+ EXPECT_FALSE(bridge->PrepareTransferableResource(nullptr, &resource,
+ &release_callback));
+ }
+
+ void ReleaseCallbackWithNullContextProviderWrapperTest() {
+ viz::TransferableResource resource;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback;
+
+ {
+ Canvas2DLayerBridgePtr bridge(std::make_unique<Canvas2DLayerBridge>(
+ IntSize(300, 150), 0,
+ Canvas2DLayerBridge::kForceAccelerationForTesting,
+ CanvasColorParams()));
+ bridge->FinalizeFrame();
+ EXPECT_TRUE(bridge->PrepareTransferableResource(nullptr, &resource,
+ &release_callback));
+ }
+
+ bool lost_resource = true;
+ gl_.SetIsContextLost(true);
+ // Get a new context provider so that the WeakPtr to the old one is null.
+ // This is the test to make sure that ReleaseMailboxImageResource() handles
+ // null context_provider_wrapper properly.
+ SharedGpuContext::ContextProviderWrapper();
+ release_callback->Run(gpu::SyncToken(), lost_resource);
+ }
+
+ void PrepareMailboxAndLoseResourceTest() {
+ // Prepare a mailbox, then report the resource as lost.
+ // This test passes by not crashing and not triggering assertions.
+ {
+ Canvas2DLayerBridgePtr bridge(std::make_unique<Canvas2DLayerBridge>(
+ IntSize(300, 150), 0,
+ Canvas2DLayerBridge::kForceAccelerationForTesting,
+ CanvasColorParams()));
+ bridge->FinalizeFrame();
+ viz::TransferableResource resource;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback;
+ EXPECT_TRUE(bridge->PrepareTransferableResource(nullptr, &resource,
+ &release_callback));
+
+ bool lost_resource = true;
+ release_callback->Run(gpu::SyncToken(), lost_resource);
+ }
+
+ // Retry with mailbox released while bridge destruction is in progress.
+ {
+ viz::TransferableResource resource;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback;
+
+ {
+ Canvas2DLayerBridgePtr bridge(std::make_unique<Canvas2DLayerBridge>(
+ IntSize(300, 150), 0,
+ Canvas2DLayerBridge::kForceAccelerationForTesting,
+ CanvasColorParams()));
+ bridge->FinalizeFrame();
+ bridge->PrepareTransferableResource(nullptr, &resource,
+ &release_callback);
+ // |bridge| goes out of scope and would normally be destroyed, but
+ // object is kept alive by self references.
+ }
+
+ // This should cause the bridge to be destroyed.
+ bool lost_resource = true;
+ // Before fixing crbug.com/411864, the following line would cause a memory
+ // use after free that sometimes caused a crash in normal builds and
+ // crashed consistently with ASAN.
+ release_callback->Run(gpu::SyncToken(), lost_resource);
+ }
+ }
+
+ void AccelerationHintTest() {
+ {
+ Canvas2DLayerBridgePtr bridge(std::make_unique<Canvas2DLayerBridge>(
+ IntSize(300, 300), 0, Canvas2DLayerBridge::kEnableAcceleration,
+ CanvasColorParams()));
+ PaintFlags flags;
+ bridge->Canvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), flags);
+ scoped_refptr<StaticBitmapImage> image =
+ bridge->NewImageSnapshot(kPreferAcceleration);
+ EXPECT_TRUE(bridge->IsValid());
+ EXPECT_TRUE(bridge->IsAccelerated());
+ }
+
+ {
+ Canvas2DLayerBridgePtr bridge(std::make_unique<Canvas2DLayerBridge>(
+ IntSize(300, 300), 0, Canvas2DLayerBridge::kEnableAcceleration,
+ CanvasColorParams()));
+ PaintFlags flags;
+ bridge->Canvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), flags);
+ scoped_refptr<StaticBitmapImage> image =
+ bridge->NewImageSnapshot(kPreferNoAcceleration);
+ EXPECT_TRUE(bridge->IsValid());
+ EXPECT_FALSE(bridge->IsAccelerated());
+ }
+ }
+};
+
+TEST_F(Canvas2DLayerBridgeTest, FullLifecycleSingleThreaded) {
+ FullLifecycleTest();
+}
+
+TEST_F(Canvas2DLayerBridgeTest, NoDrawOnContextLost) {
+ NoDrawOnContextLostTest();
+}
+
+TEST_F(Canvas2DLayerBridgeTest, PrepareMailboxWhenContextIsLost) {
+ PrepareMailboxWhenContextIsLost();
+}
+
+TEST_F(Canvas2DLayerBridgeTest,
+ PrepareMailboxWhenContextIsLostWithFailedRestore) {
+ PrepareMailboxWhenContextIsLostWithFailedRestore();
+}
+
+TEST_F(Canvas2DLayerBridgeTest, PrepareMailboxAndLoseResource) {
+ PrepareMailboxAndLoseResourceTest();
+}
+
+TEST_F(Canvas2DLayerBridgeTest, ReleaseCallbackWithNullContextProviderWrapper) {
+ ReleaseCallbackWithNullContextProviderWrapperTest();
+}
+
+TEST_F(Canvas2DLayerBridgeTest, AccelerationHint) {
+ AccelerationHintTest();
+}
+
+TEST_F(Canvas2DLayerBridgeTest, FallbackToSoftwareIfContextLost) {
+ FallbackToSoftwareIfContextLost();
+}
+
+TEST_F(Canvas2DLayerBridgeTest, FallbackToSoftwareOnFailedTextureAlloc) {
+ FallbackToSoftwareOnFailedTextureAlloc();
+}
+
+class MockLogger : public Canvas2DLayerBridge::Logger {
+ public:
+ MOCK_METHOD1(ReportHibernationEvent,
+ void(Canvas2DLayerBridge::HibernationEvent));
+ MOCK_METHOD0(DidStartHibernating, void());
+ virtual ~MockLogger() = default;
+};
+
+class MockCanvasResourceHost : public CanvasResourceHost {
+ public:
+ void NotifySurfaceInvalid() {}
+ void SetNeedsCompositingUpdate() {}
+ void UpdateMemoryUsage() {}
+ MOCK_CONST_METHOD1(RestoreCanvasMatrixClipStack, void(PaintCanvas*));
+};
+
+void DrawSomething(Canvas2DLayerBridgePtr& bridge) {
+ bridge->DidDraw(FloatRect(0, 0, 1, 1));
+ bridge->FinalizeFrame();
+ // Grabbing an image forces a flush
+ bridge->NewImageSnapshot(kPreferAcceleration);
+}
+
+#if CANVAS2D_HIBERNATION_ENABLED
+TEST_F(Canvas2DLayerBridgeTest, HibernationLifeCycle)
+#else
+TEST_F(Canvas2DLayerBridgeTest, DISABLED_HibernationLifeCycle)
+#endif
+{
+ ScopedTestingPlatformSupport<FakePlatformSupport> platform;
+ Canvas2DLayerBridgePtr bridge(std::make_unique<Canvas2DLayerBridge>(
+ IntSize(300, 300), 0, Canvas2DLayerBridge::kEnableAcceleration,
+ CanvasColorParams()));
+ bridge->DontUseIdleSchedulingForTesting();
+ DrawSomething(bridge);
+
+ // Register an alternate Logger for tracking hibernation events
+ std::unique_ptr<MockLogger> mock_logger = std::make_unique<MockLogger>();
+ MockLogger* mock_logger_ptr = mock_logger.get();
+ bridge->SetLoggerForTesting(std::move(mock_logger));
+
+ // Test entering hibernation
+ EXPECT_CALL(
+ *mock_logger_ptr,
+ ReportHibernationEvent(Canvas2DLayerBridge::kHibernationScheduled));
+ EXPECT_CALL(*mock_logger_ptr, DidStartHibernating()).Times(1);
+
+ bridge->SetIsHidden(true);
+ platform->RunUntilIdle();
+
+ testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
+ EXPECT_FALSE(bridge->IsAccelerated());
+ EXPECT_TRUE(bridge->IsHibernating());
+ EXPECT_TRUE(bridge->IsValid());
+
+ // Test exiting hibernation
+ EXPECT_CALL(
+ *mock_logger_ptr,
+ ReportHibernationEvent(Canvas2DLayerBridge::kHibernationEndedNormally));
+
+ bridge->SetIsHidden(false);
+
+ testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
+ EXPECT_TRUE(bridge->IsAccelerated());
+ EXPECT_FALSE(bridge->IsHibernating());
+ EXPECT_TRUE(bridge->IsValid());
+}
+
+#if CANVAS2D_HIBERNATION_ENABLED
+TEST_F(Canvas2DLayerBridgeTest, HibernationReEntry)
+#else
+TEST_F(Canvas2DLayerBridgeTest, DISABLED_HibernationReEntry)
+#endif
+{
+ ScopedTestingPlatformSupport<FakePlatformSupport> platform;
+ Canvas2DLayerBridgePtr bridge(std::make_unique<Canvas2DLayerBridge>(
+ IntSize(300, 300), 0, Canvas2DLayerBridge::kEnableAcceleration,
+ CanvasColorParams()));
+ bridge->DontUseIdleSchedulingForTesting();
+ DrawSomething(bridge);
+
+ // Register an alternate Logger for tracking hibernation events
+ std::unique_ptr<MockLogger> mock_logger = std::make_unique<MockLogger>();
+ MockLogger* mock_logger_ptr = mock_logger.get();
+ bridge->SetLoggerForTesting(std::move(mock_logger));
+
+ // Test entering hibernation
+ EXPECT_CALL(
+ *mock_logger_ptr,
+ ReportHibernationEvent(Canvas2DLayerBridge::kHibernationScheduled));
+ EXPECT_CALL(*mock_logger_ptr, DidStartHibernating()).Times(1);
+ bridge->SetIsHidden(true);
+ // Toggle visibility before the task that enters hibernation gets a
+ // chance to run.
+ bridge->SetIsHidden(false);
+ bridge->SetIsHidden(true);
+ platform->RunUntilIdle();
+
+ testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
+ EXPECT_FALSE(bridge->IsAccelerated());
+ EXPECT_TRUE(bridge->IsHibernating());
+ EXPECT_TRUE(bridge->IsValid());
+
+ // Test exiting hibernation
+ EXPECT_CALL(
+ *mock_logger_ptr,
+ ReportHibernationEvent(Canvas2DLayerBridge::kHibernationEndedNormally));
+
+ bridge->SetIsHidden(false);
+
+ testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
+ EXPECT_TRUE(bridge->IsAccelerated());
+ EXPECT_FALSE(bridge->IsHibernating());
+ EXPECT_TRUE(bridge->IsValid());
+}
+
+#if CANVAS2D_HIBERNATION_ENABLED
+TEST_F(Canvas2DLayerBridgeTest,
+ HibernationLifeCycleWithDeferredRenderingDisabled)
+#else
+TEST_F(Canvas2DLayerBridgeTest,
+ DISABLED_HibernationLifeCycleWithDeferredRenderingDisabled)
+#endif
+{
+ ScopedTestingPlatformSupport<FakePlatformSupport> platform;
+ MockCanvasResourceHost mock_host;
+ EXPECT_CALL(mock_host, RestoreCanvasMatrixClipStack(_)).Times(AnyNumber());
+
+ Canvas2DLayerBridgePtr bridge(std::make_unique<Canvas2DLayerBridge>(
+ IntSize(300, 300), 0, Canvas2DLayerBridge::kEnableAcceleration,
+ CanvasColorParams()));
+
+ bridge->SetCanvasResourceHost(&mock_host);
+ bridge->DontUseIdleSchedulingForTesting();
+ DrawSomething(bridge);
+ bridge->DisableDeferral(kDisableDeferralReasonUnknown);
+
+ // Register an alternate Logger for tracking hibernation events
+ std::unique_ptr<MockLogger> mock_logger = std::make_unique<MockLogger>();
+ MockLogger* mock_logger_ptr = mock_logger.get();
+ bridge->SetLoggerForTesting(std::move(mock_logger));
+
+ // Test entering hibernation
+ EXPECT_CALL(
+ *mock_logger_ptr,
+ ReportHibernationEvent(Canvas2DLayerBridge::kHibernationScheduled));
+ EXPECT_CALL(*mock_logger_ptr, DidStartHibernating()).Times(1);
+ bridge->SetIsHidden(true);
+ platform->RunUntilIdle();
+ testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
+ testing::Mock::VerifyAndClearExpectations(&mock_host);
+ EXPECT_FALSE(bridge->IsAccelerated());
+ EXPECT_TRUE(bridge->IsHibernating());
+ EXPECT_TRUE(bridge->IsValid());
+
+ // Test exiting hibernation
+ EXPECT_CALL(
+ *mock_logger_ptr,
+ ReportHibernationEvent(Canvas2DLayerBridge::kHibernationEndedNormally));
+ EXPECT_CALL(mock_host, RestoreCanvasMatrixClipStack(_))
+ .Times(AtLeast(1)); // Because deferred rendering is disabled
+ bridge->SetIsHidden(false);
+ testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
+ testing::Mock::VerifyAndClearExpectations(&mock_host);
+ EXPECT_TRUE(bridge->IsAccelerated());
+ EXPECT_FALSE(bridge->IsHibernating());
+ EXPECT_TRUE(bridge->IsValid());
+}
+
+#if CANVAS2D_HIBERNATION_ENABLED && CANVAS2D_BACKGROUND_RENDER_SWITCH_TO_CPU
+TEST_F(Canvas2DLayerBridgeTest, BackgroundRenderingWhileHibernating)
+#else
+TEST_F(Canvas2DLayerBridgeTest, DISABLED_BackgroundRenderingWhileHibernating)
+#endif
+{
+ ScopedTestingPlatformSupport<FakePlatformSupport> platform;
+ Canvas2DLayerBridgePtr bridge(std::make_unique<Canvas2DLayerBridge>(
+ IntSize(300, 300), 0, Canvas2DLayerBridge::kEnableAcceleration,
+ CanvasColorParams()));
+ bridge->DontUseIdleSchedulingForTesting();
+ DrawSomething(bridge);
+
+ // Register an alternate Logger for tracking hibernation events
+ std::unique_ptr<MockLogger> mock_logger = std::make_unique<MockLogger>();
+ MockLogger* mock_logger_ptr = mock_logger.get();
+ bridge->SetLoggerForTesting(std::move(mock_logger));
+
+ // Test entering hibernation
+ EXPECT_CALL(
+ *mock_logger_ptr,
+ ReportHibernationEvent(Canvas2DLayerBridge::kHibernationScheduled));
+ EXPECT_CALL(*mock_logger_ptr, DidStartHibernating()).Times(1);
+ bridge->SetIsHidden(true);
+ platform->RunUntilIdle();
+ testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
+ EXPECT_FALSE(bridge->IsAccelerated());
+ EXPECT_TRUE(bridge->IsHibernating());
+ EXPECT_TRUE(bridge->IsValid());
+
+ // Rendering in the background -> temp switch to SW
+ EXPECT_CALL(*mock_logger_ptr,
+ ReportHibernationEvent(
+ Canvas2DLayerBridge::
+ kHibernationEndedWithSwitchToBackgroundRendering));
+ DrawSomething(bridge);
+ testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
+ EXPECT_FALSE(bridge->IsAccelerated());
+ EXPECT_FALSE(bridge->IsHibernating());
+ EXPECT_TRUE(bridge->IsValid());
+
+ // Unhide
+ bridge->SetIsHidden(false);
+ testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
+ EXPECT_TRUE(
+ bridge->IsAccelerated()); // Becoming visible causes switch back to GPU
+ EXPECT_FALSE(bridge->IsHibernating());
+ EXPECT_TRUE(bridge->IsValid());
+}
+
+#if CANVAS2D_HIBERNATION_ENABLED && CANVAS2D_BACKGROUND_RENDER_SWITCH_TO_CPU
+TEST_F(Canvas2DLayerBridgeTest,
+ BackgroundRenderingWhileHibernatingWithDeferredRenderingDisabled)
+#else
+TEST_F(
+ Canvas2DLayerBridgeTest,
+ DISABLED_BackgroundRenderingWhileHibernatingWithDeferredRenderingDisabled)
+#endif
+{
+ ScopedTestingPlatformSupport<FakePlatformSupport> platform;
+ Canvas2DLayerBridgePtr bridge(std::make_unique<Canvas2DLayerBridge>(
+ IntSize(300, 300), 0, Canvas2DLayerBridge::kEnableAcceleration,
+ CanvasColorParams()));
+ bridge->DontUseIdleSchedulingForTesting();
+ DrawSomething(bridge);
+ MockCanvasResourceHost mock_canvas_resource_host;
+ EXPECT_CALL(mock_canvas_resource_host, RestoreCanvasMatrixClipStack(_))
+ .Times(AnyNumber());
+ bridge->SetCanvasResourceHost(&mock_canvas_resource_host);
+ bridge->DisableDeferral(kDisableDeferralReasonUnknown);
+
+ // Register an alternate Logger for tracking hibernation events
+ std::unique_ptr<MockLogger> mock_logger = std::make_unique<MockLogger>();
+ MockLogger* mock_logger_ptr = mock_logger.get();
+ bridge->SetLoggerForTesting(std::move(mock_logger));
+
+ // Test entering hibernation
+ EXPECT_CALL(
+ *mock_logger_ptr,
+ ReportHibernationEvent(Canvas2DLayerBridge::kHibernationScheduled));
+ EXPECT_CALL(*mock_logger_ptr, DidStartHibernating()).Times(1);
+ bridge->SetIsHidden(true);
+ platform->RunUntilIdle();
+ testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
+ testing::Mock::VerifyAndClearExpectations(&mock_canvas_resource_host);
+ EXPECT_FALSE(bridge->IsAccelerated());
+ EXPECT_TRUE(bridge->IsHibernating());
+ EXPECT_TRUE(bridge->IsValid());
+
+ // Rendering in the background -> temp switch to SW
+ EXPECT_CALL(*mock_logger_ptr,
+ ReportHibernationEvent(
+ Canvas2DLayerBridge::
+ kHibernationEndedWithSwitchToBackgroundRendering));
+ EXPECT_CALL(mock_canvas_resource_host, RestoreCanvasMatrixClipStack(_))
+ .Times(AtLeast(1));
+ DrawSomething(bridge);
+ testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
+ testing::Mock::VerifyAndClearExpectations(&mock_canvas_resource_host);
+ EXPECT_FALSE(bridge->IsAccelerated());
+ EXPECT_FALSE(bridge->IsHibernating());
+ EXPECT_TRUE(bridge->IsValid());
+
+ // Unhide
+ EXPECT_CALL(mock_canvas_resource_host, RestoreCanvasMatrixClipStack(_))
+ .Times(AtLeast(1));
+ bridge->SetIsHidden(false);
+ testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
+ testing::Mock::VerifyAndClearExpectations(&mock_canvas_resource_host);
+ EXPECT_TRUE(
+ bridge->IsAccelerated()); // Becoming visible causes switch back to GPU
+ EXPECT_FALSE(bridge->IsHibernating());
+ EXPECT_TRUE(bridge->IsValid());
+}
+
+#if CANVAS2D_HIBERNATION_ENABLED && CANVAS2D_BACKGROUND_RENDER_SWITCH_TO_CPU
+TEST_F(Canvas2DLayerBridgeTest, DisableDeferredRenderingWhileHibernating)
+#else
+TEST_F(Canvas2DLayerBridgeTest,
+ DISABLED_DisableDeferredRenderingWhileHibernating)
+#endif
+{
+ ScopedTestingPlatformSupport<FakePlatformSupport> platform;
+ Canvas2DLayerBridgePtr bridge(std::make_unique<Canvas2DLayerBridge>(
+ IntSize(300, 300), 0, Canvas2DLayerBridge::kEnableAcceleration,
+ CanvasColorParams()));
+ bridge->DontUseIdleSchedulingForTesting();
+ DrawSomething(bridge);
+
+ MockCanvasResourceHost mock_canvas_resource_host;
+ EXPECT_CALL(mock_canvas_resource_host, RestoreCanvasMatrixClipStack(_))
+ .Times(AnyNumber());
+ bridge->SetCanvasResourceHost(&mock_canvas_resource_host);
+
+ // Register an alternate Logger for tracking hibernation events
+ std::unique_ptr<MockLogger> mock_logger = std::make_unique<MockLogger>();
+ MockLogger* mock_logger_ptr = mock_logger.get();
+ bridge->SetLoggerForTesting(std::move(mock_logger));
+
+ // Test entering hibernation
+ EXPECT_CALL(
+ *mock_logger_ptr,
+ ReportHibernationEvent(Canvas2DLayerBridge::kHibernationScheduled));
+ EXPECT_CALL(*mock_logger_ptr, DidStartHibernating()).Times(1);
+ bridge->SetIsHidden(true);
+ platform->RunUntilIdle();
+ testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
+ testing::Mock::VerifyAndClearExpectations(&mock_canvas_resource_host);
+ EXPECT_FALSE(bridge->IsAccelerated());
+ EXPECT_TRUE(bridge->IsHibernating());
+ EXPECT_TRUE(bridge->IsValid());
+
+ // Disable deferral while background rendering
+ EXPECT_CALL(*mock_logger_ptr,
+ ReportHibernationEvent(
+ Canvas2DLayerBridge::
+ kHibernationEndedWithSwitchToBackgroundRendering));
+ EXPECT_CALL(mock_canvas_resource_host, RestoreCanvasMatrixClipStack(_))
+ .Times(AtLeast(1));
+ bridge->DisableDeferral(kDisableDeferralReasonUnknown);
+ testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
+ testing::Mock::VerifyAndClearExpectations(&mock_canvas_resource_host);
+ EXPECT_FALSE(bridge->IsAccelerated());
+ EXPECT_FALSE(bridge->IsHibernating());
+ EXPECT_TRUE(bridge->IsValid());
+
+ // Unhide
+ EXPECT_CALL(mock_canvas_resource_host, RestoreCanvasMatrixClipStack(_))
+ .Times(AtLeast(1));
+ bridge->SetIsHidden(false);
+ testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
+ testing::Mock::VerifyAndClearExpectations(&mock_canvas_resource_host);
+ EXPECT_TRUE(
+ bridge->IsAccelerated()); // Becoming visible causes switch back to GPU
+ EXPECT_FALSE(bridge->IsHibernating());
+ EXPECT_TRUE(bridge->IsValid());
+
+ EXPECT_CALL(mock_canvas_resource_host, RestoreCanvasMatrixClipStack(_))
+ .Times(AnyNumber());
+ bridge.Clear();
+ testing::Mock::VerifyAndClearExpectations(&mock_canvas_resource_host);
+}
+
+#if CANVAS2D_HIBERNATION_ENABLED
+TEST_F(Canvas2DLayerBridgeTest, TeardownWhileHibernating)
+#else
+TEST_F(Canvas2DLayerBridgeTest, DISABLED_TeardownWhileHibernating)
+#endif
+{
+ ScopedTestingPlatformSupport<FakePlatformSupport> platform;
+ Canvas2DLayerBridgePtr bridge(std::make_unique<Canvas2DLayerBridge>(
+ IntSize(300, 300), 0, Canvas2DLayerBridge::kEnableAcceleration,
+ CanvasColorParams()));
+ bridge->DontUseIdleSchedulingForTesting();
+ DrawSomething(bridge);
+
+ // Register an alternate Logger for tracking hibernation events
+ std::unique_ptr<MockLogger> mock_logger = std::make_unique<MockLogger>();
+ MockLogger* mock_logger_ptr = mock_logger.get();
+ bridge->SetLoggerForTesting(std::move(mock_logger));
+
+ // Test entering hibernation
+ EXPECT_CALL(
+ *mock_logger_ptr,
+ ReportHibernationEvent(Canvas2DLayerBridge::kHibernationScheduled));
+ EXPECT_CALL(*mock_logger_ptr, DidStartHibernating()).Times(1);
+ bridge->SetIsHidden(true);
+ platform->RunUntilIdle();
+ testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
+ EXPECT_FALSE(bridge->IsAccelerated());
+ EXPECT_TRUE(bridge->IsHibernating());
+ EXPECT_TRUE(bridge->IsValid());
+
+ // Tear down the bridge while hibernating
+ EXPECT_CALL(*mock_logger_ptr,
+ ReportHibernationEvent(
+ Canvas2DLayerBridge::kHibernationEndedWithTeardown));
+ bridge.Clear();
+ testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
+}
+
+#if CANVAS2D_HIBERNATION_ENABLED
+TEST_F(Canvas2DLayerBridgeTest, SnapshotWhileHibernating)
+#else
+TEST_F(Canvas2DLayerBridgeTest, DISABLED_SnapshotWhileHibernating)
+#endif
+{
+ ScopedTestingPlatformSupport<FakePlatformSupport> platform;
+ Canvas2DLayerBridgePtr bridge(std::make_unique<Canvas2DLayerBridge>(
+ IntSize(300, 300), 0, Canvas2DLayerBridge::kEnableAcceleration,
+ CanvasColorParams()));
+ bridge->DontUseIdleSchedulingForTesting();
+ DrawSomething(bridge);
+
+ // Register an alternate Logger for tracking hibernation events
+ std::unique_ptr<MockLogger> mock_logger = std::make_unique<MockLogger>();
+ MockLogger* mock_logger_ptr = mock_logger.get();
+ bridge->SetLoggerForTesting(std::move(mock_logger));
+
+ // Test entering hibernation
+ EXPECT_CALL(
+ *mock_logger_ptr,
+ ReportHibernationEvent(Canvas2DLayerBridge::kHibernationScheduled));
+ EXPECT_CALL(*mock_logger_ptr, DidStartHibernating()).Times(1);
+ bridge->SetIsHidden(true);
+ platform->RunUntilIdle();
+ testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
+ EXPECT_FALSE(bridge->IsAccelerated());
+ EXPECT_TRUE(bridge->IsHibernating());
+ EXPECT_TRUE(bridge->IsValid());
+
+ // Take a snapshot and verify that it is not accelerated due to hibernation
+ scoped_refptr<StaticBitmapImage> image =
+ bridge->NewImageSnapshot(kPreferAcceleration);
+ EXPECT_FALSE(image->IsTextureBacked());
+ image = nullptr;
+
+ // Verify that taking a snapshot did not affect the state of bridge
+ EXPECT_FALSE(bridge->IsAccelerated());
+ EXPECT_TRUE(bridge->IsHibernating());
+ EXPECT_TRUE(bridge->IsValid());
+
+ // End hibernation normally
+ EXPECT_CALL(
+ *mock_logger_ptr,
+ ReportHibernationEvent(Canvas2DLayerBridge::kHibernationEndedNormally))
+ .Times(1);
+ bridge->SetIsHidden(false);
+}
+
+#if CANVAS2D_HIBERNATION_ENABLED
+TEST_F(Canvas2DLayerBridgeTest, TeardownWhileHibernationIsPending)
+#else
+TEST_F(Canvas2DLayerBridgeTest, DISABLED_TeardownWhileHibernationIsPending)
+#endif
+{
+ ScopedTestingPlatformSupport<FakePlatformSupport> platform;
+ Canvas2DLayerBridgePtr bridge(std::make_unique<Canvas2DLayerBridge>(
+ IntSize(300, 300), 0, Canvas2DLayerBridge::kEnableAcceleration,
+ CanvasColorParams()));
+ bridge->DontUseIdleSchedulingForTesting();
+ DrawSomething(bridge);
+
+ // Register an alternate Logger for tracking hibernation events
+ std::unique_ptr<MockLogger> mock_logger = std::make_unique<MockLogger>();
+ MockLogger* mock_logger_ptr = mock_logger.get();
+ bridge->SetLoggerForTesting(std::move(mock_logger));
+
+ // Test entering hibernation
+ EXPECT_CALL(
+ *mock_logger_ptr,
+ ReportHibernationEvent(Canvas2DLayerBridge::kHibernationScheduled));
+ bridge->SetIsHidden(true);
+ bridge.Clear();
+ // In production, we would expect a
+ // HibernationAbortedDueToDestructionWhileHibernatePending event to be
+ // fired, but that signal is lost in the unit test due to no longer having
+ // a bridge to hold the mockLogger.
+ platform->RunUntilIdle();
+ // This test passes by not crashing, which proves that the WeakPtr logic
+ // is sound.
+}
+
+#if CANVAS2D_HIBERNATION_ENABLED
+TEST_F(Canvas2DLayerBridgeTest, HibernationAbortedDueToPendingTeardown)
+#else
+TEST_F(Canvas2DLayerBridgeTest, DISABLED_HibernationAbortedDueToPendingTeardown)
+#endif
+{
+ ScopedTestingPlatformSupport<FakePlatformSupport> platform;
+ Canvas2DLayerBridgePtr bridge(std::make_unique<Canvas2DLayerBridge>(
+ IntSize(300, 300), 0, Canvas2DLayerBridge::kEnableAcceleration,
+ CanvasColorParams()));
+ bridge->DontUseIdleSchedulingForTesting();
+ DrawSomething(bridge);
+
+ // Register an alternate Logger for tracking hibernation events
+ std::unique_ptr<MockLogger> mock_logger = std::make_unique<MockLogger>();
+ MockLogger* mock_logger_ptr = mock_logger.get();
+ bridge->SetLoggerForTesting(std::move(mock_logger));
+
+ // Test entering hibernation
+ EXPECT_CALL(
+ *mock_logger_ptr,
+ ReportHibernationEvent(Canvas2DLayerBridge::kHibernationScheduled));
+ EXPECT_CALL(
+ *mock_logger_ptr,
+ ReportHibernationEvent(
+ Canvas2DLayerBridge::kHibernationAbortedDueToPendingDestruction))
+ .Times(1);
+ bridge->SetIsHidden(true);
+ bridge->BeginDestruction();
+ platform->RunUntilIdle();
+
+ testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
+}
+
+#if CANVAS2D_HIBERNATION_ENABLED
+TEST_F(Canvas2DLayerBridgeTest, HibernationAbortedDueToVisibilityChange)
+#else
+TEST_F(Canvas2DLayerBridgeTest,
+ DISABLED_HibernationAbortedDueToVisibilityChange)
+#endif
+{
+ ScopedTestingPlatformSupport<FakePlatformSupport> platform;
+ Canvas2DLayerBridgePtr bridge(std::make_unique<Canvas2DLayerBridge>(
+ IntSize(300, 300), 0, Canvas2DLayerBridge::kEnableAcceleration,
+ CanvasColorParams()));
+ bridge->DontUseIdleSchedulingForTesting();
+ DrawSomething(bridge);
+
+ // Register an alternate Logger for tracking hibernation events
+ std::unique_ptr<MockLogger> mock_logger = std::make_unique<MockLogger>();
+ MockLogger* mock_logger_ptr = mock_logger.get();
+ bridge->SetLoggerForTesting(std::move(mock_logger));
+
+ // Test entering hibernation
+ EXPECT_CALL(
+ *mock_logger_ptr,
+ ReportHibernationEvent(Canvas2DLayerBridge::kHibernationScheduled));
+ EXPECT_CALL(
+ *mock_logger_ptr,
+ ReportHibernationEvent(
+ Canvas2DLayerBridge::kHibernationAbortedDueToVisibilityChange))
+ .Times(1);
+ bridge->SetIsHidden(true);
+ bridge->SetIsHidden(false);
+ platform->RunUntilIdle();
+ testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
+ EXPECT_TRUE(bridge->IsAccelerated());
+ EXPECT_FALSE(bridge->IsHibernating());
+ EXPECT_TRUE(bridge->IsValid());
+}
+
+#if CANVAS2D_HIBERNATION_ENABLED
+TEST_F(Canvas2DLayerBridgeTest, HibernationAbortedDueToLostContext)
+#else
+TEST_F(Canvas2DLayerBridgeTest, DISABLED_HibernationAbortedDueToLostContext)
+#endif
+{
+ ScopedTestingPlatformSupport<FakePlatformSupport> platform;
+ Canvas2DLayerBridgePtr bridge(std::make_unique<Canvas2DLayerBridge>(
+ IntSize(300, 300), 0, Canvas2DLayerBridge::kEnableAcceleration,
+ CanvasColorParams()));
+ bridge->DontUseIdleSchedulingForTesting();
+ DrawSomething(bridge);
+
+ // Register an alternate Logger for tracking hibernation events
+ std::unique_ptr<MockLogger> mock_logger = std::make_unique<MockLogger>();
+ MockLogger* mock_logger_ptr = mock_logger.get();
+ bridge->SetLoggerForTesting(std::move(mock_logger));
+
+ gl_.SetIsContextLost(true);
+
+ // Test entering hibernation
+ EXPECT_CALL(
+ *mock_logger_ptr,
+ ReportHibernationEvent(Canvas2DLayerBridge::kHibernationScheduled));
+ EXPECT_CALL(*mock_logger_ptr,
+ ReportHibernationEvent(
+ Canvas2DLayerBridge::kHibernationAbortedDueGpuContextLoss))
+ .Times(1);
+
+ bridge->SetIsHidden(true);
+ platform->RunUntilIdle();
+ testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
+ EXPECT_FALSE(bridge->IsHibernating());
+}
+
+#if CANVAS2D_HIBERNATION_ENABLED
+TEST_F(Canvas2DLayerBridgeTest, PrepareMailboxWhileHibernating)
+#else
+TEST_F(Canvas2DLayerBridgeTest, DISABLED_PrepareMailboxWhileHibernating)
+#endif
+{
+ ScopedTestingPlatformSupport<FakePlatformSupport> platform;
+ Canvas2DLayerBridgePtr bridge(std::make_unique<Canvas2DLayerBridge>(
+ IntSize(300, 300), 0, Canvas2DLayerBridge::kEnableAcceleration,
+ CanvasColorParams()));
+ bridge->DontUseIdleSchedulingForTesting();
+ DrawSomething(bridge);
+
+ // Register an alternate Logger for tracking hibernation events
+ std::unique_ptr<MockLogger> mock_logger = std::make_unique<MockLogger>();
+ MockLogger* mock_logger_ptr = mock_logger.get();
+ bridge->SetLoggerForTesting(std::move(mock_logger));
+
+ // Test entering hibernation
+ EXPECT_CALL(
+ *mock_logger_ptr,
+ ReportHibernationEvent(Canvas2DLayerBridge::kHibernationScheduled));
+ EXPECT_CALL(*mock_logger_ptr, DidStartHibernating()).Times(1);
+ bridge->SetIsHidden(true);
+ platform->RunUntilIdle();
+ testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
+
+ // Test PrepareTransferableResource() while hibernating
+ viz::TransferableResource resource;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback;
+ EXPECT_FALSE(bridge->PrepareTransferableResource(nullptr, &resource,
+ &release_callback));
+ EXPECT_TRUE(bridge->IsValid());
+
+ // Tear down the bridge on the thread so that 'bridge' can go out of scope
+ // without crashing due to thread checks
+ EXPECT_CALL(*mock_logger_ptr,
+ ReportHibernationEvent(
+ Canvas2DLayerBridge::kHibernationEndedWithTeardown));
+ bridge.Clear();
+ testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
+}
+
+#if CANVAS2D_HIBERNATION_ENABLED && CANVAS2D_BACKGROUND_RENDER_SWITCH_TO_CPU
+TEST_F(Canvas2DLayerBridgeTest, PrepareMailboxWhileBackgroundRendering)
+#else
+TEST_F(Canvas2DLayerBridgeTest, DISABLED_PrepareMailboxWhileBackgroundRendering)
+#endif
+{
+ ScopedTestingPlatformSupport<FakePlatformSupport> platform;
+ Canvas2DLayerBridgePtr bridge(std::make_unique<Canvas2DLayerBridge>(
+ IntSize(300, 300), 0, Canvas2DLayerBridge::kEnableAcceleration,
+ CanvasColorParams()));
+ DrawSomething(bridge);
+
+ // Register an alternate Logger for tracking hibernation events
+ std::unique_ptr<MockLogger> mock_logger = std::make_unique<MockLogger>();
+ MockLogger* mock_logger_ptr = mock_logger.get();
+ bridge->SetLoggerForTesting(std::move(mock_logger));
+
+ // Test entering hibernation
+ std::unique_ptr<WaitableEvent> hibernation_started_event =
+ std::make_unique<WaitableEvent>();
+ EXPECT_CALL(
+ *mock_logger_ptr,
+ ReportHibernationEvent(Canvas2DLayerBridge::kHibernationScheduled));
+ EXPECT_CALL(*mock_logger_ptr, DidStartHibernating()).Times(1);
+ bridge->SetIsHidden(true);
+ platform->RunUntilIdle();
+ testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
+
+ // Rendering in the background -> temp switch to SW
+ EXPECT_CALL(*mock_logger_ptr,
+ ReportHibernationEvent(
+ Canvas2DLayerBridge::
+ kHibernationEndedWithSwitchToBackgroundRendering));
+ DrawSomething(bridge);
+ testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
+ EXPECT_FALSE(bridge->IsAccelerated());
+ EXPECT_FALSE(bridge->IsHibernating());
+ EXPECT_TRUE(bridge->IsValid());
+
+ // Test prepareMailbox while background rendering
+ viz::TransferableResource resource;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback;
+ EXPECT_FALSE(bridge->PrepareTransferableResource(nullptr, &resource,
+ &release_callback));
+ EXPECT_TRUE(bridge->IsValid());
+}
+
+TEST_F(Canvas2DLayerBridgeTest, GpuMemoryBufferRecycling) {
+ ScopedCanvas2dImageChromiumForTest canvas_2d_image_chromium(true);
+ ScopedTestingPlatformSupport<FakePlatformSupport> platform;
+ const_cast<gpu::Capabilities&>(SharedGpuContext::ContextProviderWrapper()
+ ->ContextProvider()
+ ->GetCapabilities())
+ .texture_format_bgra8888 = true;
+
+ viz::TransferableResource resource1;
+ viz::TransferableResource resource2;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback1;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback2;
+ const GLuint texture_id1 = 1;
+ const GLuint texture_id2 = 2;
+ const GLuint image_id1 = 3;
+ const GLuint image_id2 = 4;
+
+ Canvas2DLayerBridgePtr bridge(std::make_unique<Canvas2DLayerBridge>(
+ IntSize(300, 150), 0, Canvas2DLayerBridge::kForceAccelerationForTesting,
+ CanvasColorParams()));
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+
+ EXPECT_CALL(gl_, GenTextures(1, _)).WillOnce(SetArgPointee<1>(texture_id1));
+ EXPECT_CALL(gl_, CreateImageCHROMIUM(_, _, _, _)).WillOnce(Return(image_id1));
+ DrawSomething(bridge);
+ bridge->PrepareTransferableResource(nullptr, &resource1, &release_callback1);
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+
+ EXPECT_CALL(gl_, GenTextures(1, _)).WillOnce(SetArgPointee<1>(texture_id2));
+ EXPECT_CALL(gl_, CreateImageCHROMIUM(_, _, _, _)).WillOnce(Return(image_id2));
+ DrawSomething(bridge);
+ bridge->PrepareTransferableResource(nullptr, &resource2, &release_callback2);
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+
+ // Check that release resources does not result in destruction due
+ // to recycling.
+ EXPECT_CALL(gl_, DestroyImageCHROMIUM(_)).Times(0);
+ EXPECT_CALL(gl_, DeleteTextures(_, _)).Times(0);
+ bool lost_resource = false;
+ release_callback1->Run(gpu::SyncToken(), lost_resource);
+ release_callback1 = nullptr;
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+
+ EXPECT_CALL(gl_, DestroyImageCHROMIUM(_)).Times(0);
+ EXPECT_CALL(gl_, DeleteTextures(_, _)).Times(0);
+ release_callback2->Run(gpu::SyncToken(), lost_resource);
+ release_callback2 = nullptr;
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+
+ // Destroying the bridge results in destruction of cached resources.
+ EXPECT_CALL(gl_, DestroyImageCHROMIUM(image_id1)).Times(1);
+ EXPECT_CALL(gl_, DeleteTextures(1, Pointee(texture_id1))).Times(1);
+ EXPECT_CALL(gl_, DestroyImageCHROMIUM(image_id2)).Times(1);
+ EXPECT_CALL(gl_, DeleteTextures(1, Pointee(texture_id2))).Times(1);
+ bridge.Clear();
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+}
+
+TEST_F(Canvas2DLayerBridgeTest, NoGpuMemoryBufferRecyclingWhenPageHidden) {
+ ScopedCanvas2dImageChromiumForTest canvas_2d_image_chromium(true);
+ ScopedTestingPlatformSupport<FakePlatformSupport> platform;
+ const_cast<gpu::Capabilities&>(SharedGpuContext::ContextProviderWrapper()
+ ->ContextProvider()
+ ->GetCapabilities())
+ .texture_format_bgra8888 = true;
+
+ viz::TransferableResource resource1;
+ viz::TransferableResource resource2;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback1;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback2;
+ const GLuint texture_id1 = 1;
+ const GLuint texture_id2 = 2;
+ const GLuint image_id1 = 3;
+ const GLuint image_id2 = 4;
+
+ Canvas2DLayerBridgePtr bridge(std::make_unique<Canvas2DLayerBridge>(
+ IntSize(300, 150), 0, Canvas2DLayerBridge::kForceAccelerationForTesting,
+ CanvasColorParams()));
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+
+ EXPECT_CALL(gl_, GenTextures(1, _)).WillOnce(SetArgPointee<1>(texture_id1));
+ EXPECT_CALL(gl_, CreateImageCHROMIUM(_, _, _, _)).WillOnce(Return(image_id1));
+ DrawSomething(bridge);
+ bridge->PrepareTransferableResource(nullptr, &resource1, &release_callback1);
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+
+ EXPECT_CALL(gl_, GenTextures(1, _)).WillOnce(SetArgPointee<1>(texture_id2));
+ EXPECT_CALL(gl_, CreateImageCHROMIUM(_, _, _, _)).WillOnce(Return(image_id2));
+ DrawSomething(bridge);
+ bridge->PrepareTransferableResource(nullptr, &resource2, &release_callback2);
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+
+ // Release first frame to cache
+ EXPECT_CALL(gl_, DestroyImageCHROMIUM(_)).Times(0);
+ EXPECT_CALL(gl_, DeleteTextures(_, _)).Times(0);
+ bool lost_resource = false;
+ release_callback1->Run(gpu::SyncToken(), lost_resource);
+ release_callback1 = nullptr;
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+
+ // Switching to Hidden frees cached resources immediately
+ EXPECT_CALL(gl_, DestroyImageCHROMIUM(image_id1)).Times(1);
+ EXPECT_CALL(gl_, DeleteTextures(1, Pointee(texture_id1))).Times(1);
+ bridge->SetIsHidden(true);
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+
+ // Release second frame and verify that its resource is destroyed immediately
+ // due to the layer bridge being hidden
+ EXPECT_CALL(gl_, DestroyImageCHROMIUM(image_id2)).Times(1);
+ EXPECT_CALL(gl_, DeleteTextures(1, Pointee(texture_id2))).Times(1);
+ release_callback2->Run(gpu::SyncToken(), lost_resource);
+ release_callback2 = nullptr;
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+}
+
+TEST_F(Canvas2DLayerBridgeTest, ReleaseGpuMemoryBufferAfterBridgeDestroyed) {
+ ScopedCanvas2dImageChromiumForTest canvas_2d_image_chromium(true);
+ ScopedTestingPlatformSupport<FakePlatformSupport> platform;
+ const_cast<gpu::Capabilities&>(SharedGpuContext::ContextProviderWrapper()
+ ->ContextProvider()
+ ->GetCapabilities())
+ .texture_format_bgra8888 = true;
+
+ viz::TransferableResource resource;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback;
+ const GLuint texture_id = 1;
+ const GLuint image_id = 2;
+
+ Canvas2DLayerBridgePtr bridge(std::make_unique<Canvas2DLayerBridge>(
+ IntSize(300, 150), 0, Canvas2DLayerBridge::kForceAccelerationForTesting,
+ CanvasColorParams()));
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+
+ EXPECT_CALL(gl_, GenTextures(1, _)).WillOnce(SetArgPointee<1>(texture_id));
+ EXPECT_CALL(gl_, CreateImageCHROMIUM(_, _, _, _)).WillOnce(Return(image_id));
+ DrawSomething(bridge);
+ bridge->PrepareTransferableResource(nullptr, &resource, &release_callback);
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+
+ // Tearing down the bridge does not destroy unreleased resources.
+ EXPECT_CALL(gl_, DestroyImageCHROMIUM(_)).Times(0);
+ EXPECT_CALL(gl_, DeleteTextures(_, _)).Times(0);
+ bridge.Clear();
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+
+ EXPECT_CALL(gl_, DestroyImageCHROMIUM(image_id)).Times(1);
+ EXPECT_CALL(gl_, DeleteTextures(1, Pointee(texture_id))).Times(1);
+ bool lost_resource = false;
+ release_callback->Run(gpu::SyncToken(), lost_resource);
+ release_callback = nullptr;
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+}
+
+TEST_F(Canvas2DLayerBridgeTest, EnsureCCImageCacheUse) {
+ auto color_params =
+ CanvasColorParams(kSRGBCanvasColorSpace, kF16CanvasPixelFormat, kOpaque);
+ ASSERT_FALSE(color_params.NeedsSkColorSpaceXformCanvas());
+
+ Canvas2DLayerBridgePtr bridge(std::make_unique<Canvas2DLayerBridge>(
+ IntSize(300, 300), 0, Canvas2DLayerBridge::kEnableAcceleration,
+ color_params));
+ std::vector<cc::DrawImage> images = {
+ cc::DrawImage(cc::CreateDiscardablePaintImage(gfx::Size(10, 10)),
+ SkIRect::MakeWH(10, 10), kNone_SkFilterQuality,
+ SkMatrix::I(), 0u, color_params.GetStorageGfxColorSpace()),
+ cc::DrawImage(cc::CreateDiscardablePaintImage(gfx::Size(20, 20)),
+ SkIRect::MakeWH(5, 5), kNone_SkFilterQuality, SkMatrix::I(),
+ 0u, color_params.GetStorageGfxColorSpace())};
+
+ bridge->Canvas()->drawImage(images[0].paint_image(), 0u, 0u, nullptr);
+ bridge->Canvas()->drawImageRect(
+ images[1].paint_image(), SkRect::MakeWH(5u, 5u), SkRect::MakeWH(5u, 5u),
+ nullptr, cc::PaintCanvas::kFast_SrcRectConstraint);
+ bridge->NewImageSnapshot(kPreferAcceleration);
+
+ EXPECT_EQ(image_decode_cache_.decoded_images(), images);
+}
+
+TEST_F(Canvas2DLayerBridgeTest, EnsureCCImageCacheUseWithColorConversion) {
+ auto color_params = CanvasColorParams(kSRGBCanvasColorSpace,
+ kRGBA8CanvasPixelFormat, kOpaque);
+ ASSERT_TRUE(color_params.NeedsSkColorSpaceXformCanvas());
+
+ Canvas2DLayerBridgePtr bridge(std::make_unique<Canvas2DLayerBridge>(
+ IntSize(300, 300), 0, Canvas2DLayerBridge::kEnableAcceleration,
+ color_params));
+ std::vector<cc::DrawImage> images = {
+ cc::DrawImage(cc::CreateDiscardablePaintImage(gfx::Size(10, 10)),
+ SkIRect::MakeWH(10, 10), kNone_SkFilterQuality,
+ SkMatrix::I(), 0u, color_params.GetStorageGfxColorSpace()),
+ cc::DrawImage(cc::CreateDiscardablePaintImage(gfx::Size(20, 20)),
+ SkIRect::MakeWH(5, 5), kNone_SkFilterQuality, SkMatrix::I(),
+ 0u, color_params.GetStorageGfxColorSpace())};
+
+ bridge->Canvas()->drawImage(images[0].paint_image(), 0u, 0u, nullptr);
+ bridge->Canvas()->drawImageRect(
+ images[1].paint_image(), SkRect::MakeWH(5u, 5u), SkRect::MakeWH(5u, 5u),
+ nullptr, cc::PaintCanvas::kFast_SrcRectConstraint);
+ bridge->NewImageSnapshot(kPreferAcceleration);
+
+ EXPECT_EQ(image_decode_cache_.decoded_images(), images);
+}
+
+TEST_F(Canvas2DLayerBridgeTest, ImagesLockedUntilCacheLimit) {
+ // Disable deferral so we can inspect the cache state as we use the canvas.
+ auto color_params =
+ CanvasColorParams(kSRGBCanvasColorSpace, kF16CanvasPixelFormat, kOpaque);
+ Canvas2DLayerBridgePtr bridge(std::make_unique<Canvas2DLayerBridge>(
+ IntSize(300, 300), 0, Canvas2DLayerBridge::kEnableAcceleration,
+ color_params));
+ bridge->DisableDeferral(DisableDeferralReason::kDisableDeferralReasonUnknown);
+
+ std::vector<cc::DrawImage> images = {
+ cc::DrawImage(cc::CreateDiscardablePaintImage(gfx::Size(10, 10)),
+ SkIRect::MakeWH(10, 10), kNone_SkFilterQuality,
+ SkMatrix::I(), 0u, color_params.GetStorageGfxColorSpace()),
+ cc::DrawImage(cc::CreateDiscardablePaintImage(gfx::Size(20, 20)),
+ SkIRect::MakeWH(5, 5), kNone_SkFilterQuality, SkMatrix::I(),
+ 0u, color_params.GetStorageGfxColorSpace()),
+ cc::DrawImage(cc::CreateDiscardablePaintImage(gfx::Size(20, 20)),
+ SkIRect::MakeWH(5, 5), kNone_SkFilterQuality, SkMatrix::I(),
+ 0u, color_params.GetStorageGfxColorSpace())};
+
+ // First 2 images are budgeted, they should remain locked after the op.
+ bridge->Canvas()->drawImage(images[0].paint_image(), 0u, 0u, nullptr);
+ bridge->Canvas()->drawImage(images[1].paint_image(), 0u, 0u, nullptr);
+ EXPECT_EQ(image_decode_cache_.num_locked_images(), 2);
+
+ // Next image is not budgeted, we should unlock all images other than the last
+ // image.
+ image_decode_cache_.set_budget_exceeded(true);
+ bridge->Canvas()->drawImage(images[2].paint_image(), 0u, 0u, nullptr);
+ EXPECT_EQ(image_decode_cache_.num_locked_images(), 1);
+
+ // Ask the provider to release everything, no locked images should remain.
+ bridge->GetOrCreateResourceProvider()->ReleaseLockedImages();
+ EXPECT_EQ(image_decode_cache_.num_locked_images(), 0);
+}
+
+TEST_F(Canvas2DLayerBridgeTest, ImageCacheOnContextLost) {
+ // Disable deferral so we use the raster canvas directly.
+ auto color_params =
+ CanvasColorParams(kSRGBCanvasColorSpace, kF16CanvasPixelFormat, kOpaque);
+ Canvas2DLayerBridgePtr bridge(std::make_unique<Canvas2DLayerBridge>(
+ IntSize(300, 300), 0, Canvas2DLayerBridge::kEnableAcceleration,
+ color_params));
+ bridge->DisableDeferral(DisableDeferralReason::kDisableDeferralReasonUnknown);
+
+ cc::PaintFlags flags;
+ std::vector<cc::DrawImage> images = {
+ cc::DrawImage(cc::CreateDiscardablePaintImage(gfx::Size(10, 10)),
+ SkIRect::MakeWH(10, 10), kNone_SkFilterQuality,
+ SkMatrix::I(), 0u, color_params.GetStorageGfxColorSpace()),
+ cc::DrawImage(cc::CreateDiscardablePaintImage(gfx::Size(20, 20)),
+ SkIRect::MakeWH(5, 5), kNone_SkFilterQuality, SkMatrix::I(),
+ 0u, color_params.GetStorageGfxColorSpace())};
+ bridge->Canvas()->drawImage(images[0].paint_image(), 0u, 0u, nullptr);
+
+ // Lose the context and ensure that the image provider is not used.
+ bridge->GetResourceProvider()->OnContextDestroyed();
+ // We should unref all images on the cache when the context is destroyed.
+ EXPECT_EQ(image_decode_cache_.num_locked_images(), 0);
+ image_decode_cache_.set_disallow_cache_use(true);
+ bridge->Canvas()->drawImage(images[1].paint_image(), 0u, 0u, &flags);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/canvas_color_params.cc b/chromium/third_party/blink/renderer/platform/graphics/canvas_color_params.cc
new file mode 100644
index 00000000000..8d0601a44fe
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/canvas_color_params.cc
@@ -0,0 +1,221 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/canvas_color_params.h"
+
+#include "cc/paint/skia_paint_canvas.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/khronos/GLES2/gl2.h"
+#include "third_party/khronos/GLES2/gl2ext.h"
+#include "third_party/khronos/GLES3/gl3.h"
+#include "third_party/skia/include/core/SkSurfaceProps.h"
+#include "ui/gfx/color_space.h"
+
+namespace blink {
+
+namespace {
+
+gfx::ColorSpace::PrimaryID GetPrimaryID(CanvasColorSpace color_space) {
+ gfx::ColorSpace::PrimaryID primary_id = gfx::ColorSpace::PrimaryID::BT709;
+ switch (color_space) {
+ case kSRGBCanvasColorSpace:
+ primary_id = gfx::ColorSpace::PrimaryID::BT709;
+ break;
+ case kRec2020CanvasColorSpace:
+ primary_id = gfx::ColorSpace::PrimaryID::BT2020;
+ break;
+ case kP3CanvasColorSpace:
+ primary_id = gfx::ColorSpace::PrimaryID::SMPTEST432_1;
+ break;
+ }
+ return primary_id;
+}
+
+} // namespace
+
+CanvasColorParams::CanvasColorParams() = default;
+
+CanvasColorParams::CanvasColorParams(CanvasColorSpace color_space,
+ CanvasPixelFormat pixel_format,
+ OpacityMode opacity_mode)
+ : color_space_(color_space),
+ pixel_format_(pixel_format),
+ opacity_mode_(opacity_mode) {}
+
+CanvasColorParams::CanvasColorParams(const SkImageInfo& info) {
+ color_space_ = kSRGBCanvasColorSpace;
+ pixel_format_ = kRGBA8CanvasPixelFormat;
+ // When there is no color space information, the SkImage is in legacy mode and
+ // the color type is kN32_SkColorType (which translates to kRGBA8 canvas pixel
+ // format).
+ if (!info.colorSpace())
+ return;
+ if (SkColorSpace::Equals(info.colorSpace(), SkColorSpace::MakeSRGB().get()))
+ color_space_ = kSRGBCanvasColorSpace;
+ else if (SkColorSpace::Equals(
+ info.colorSpace(),
+ SkColorSpace::MakeRGB(SkColorSpace::kLinear_RenderTargetGamma,
+ SkColorSpace::kRec2020_Gamut)
+ .get()))
+ color_space_ = kRec2020CanvasColorSpace;
+ else if (SkColorSpace::Equals(
+ info.colorSpace(),
+ SkColorSpace::MakeRGB(SkColorSpace::kLinear_RenderTargetGamma,
+ SkColorSpace::kDCIP3_D65_Gamut)
+ .get()))
+ color_space_ = kP3CanvasColorSpace;
+ if (info.colorType() == kRGBA_F16_SkColorType)
+ pixel_format_ = kF16CanvasPixelFormat;
+}
+
+void CanvasColorParams::SetCanvasColorSpace(CanvasColorSpace color_space) {
+ color_space_ = color_space;
+}
+
+void CanvasColorParams::SetCanvasPixelFormat(CanvasPixelFormat pixel_format) {
+ pixel_format_ = pixel_format;
+}
+
+void CanvasColorParams::SetOpacityMode(OpacityMode opacity_mode) {
+ opacity_mode_ = opacity_mode;
+}
+
+bool CanvasColorParams::NeedsSkColorSpaceXformCanvas() const {
+ return color_space_ == kSRGBCanvasColorSpace &&
+ pixel_format_ == kRGBA8CanvasPixelFormat;
+}
+
+std::unique_ptr<cc::PaintCanvas> CanvasColorParams::WrapCanvas(
+ SkCanvas* canvas) const {
+ if (NeedsSkColorSpaceXformCanvas()) {
+ return std::make_unique<cc::SkiaPaintCanvas>(canvas, GetSkColorSpace());
+ }
+ // |canvas| already does its own color correction.
+ return std::make_unique<cc::SkiaPaintCanvas>(canvas);
+}
+
+sk_sp<SkColorSpace> CanvasColorParams::GetSkColorSpaceForSkSurfaces() const {
+ if (NeedsSkColorSpaceXformCanvas())
+ return nullptr;
+ return GetSkColorSpace();
+}
+
+bool CanvasColorParams::NeedsColorConversion(
+ const CanvasColorParams& dest_color_params) const {
+ if ((color_space_ == dest_color_params.ColorSpace() &&
+ pixel_format_ == dest_color_params.PixelFormat()) ||
+ (NeedsSkColorSpaceXformCanvas() &&
+ dest_color_params.NeedsSkColorSpaceXformCanvas()))
+ return false;
+ return true;
+}
+
+SkColorType CanvasColorParams::GetSkColorType() const {
+ if (pixel_format_ == kF16CanvasPixelFormat)
+ return kRGBA_F16_SkColorType;
+ return kN32_SkColorType;
+}
+
+SkAlphaType CanvasColorParams::GetSkAlphaType() const {
+ if (opacity_mode_ == kOpaque)
+ return kOpaque_SkAlphaType;
+ return kPremul_SkAlphaType;
+}
+
+const SkSurfaceProps* CanvasColorParams::GetSkSurfaceProps() const {
+ static const SkSurfaceProps disable_lcd_props(0, kUnknown_SkPixelGeometry);
+ if (opacity_mode_ == kOpaque)
+ return nullptr;
+ return &disable_lcd_props;
+}
+
+uint8_t CanvasColorParams::BytesPerPixel() const {
+ return SkColorTypeBytesPerPixel(GetSkColorType());
+}
+
+gfx::ColorSpace CanvasColorParams::GetSamplerGfxColorSpace() const {
+ gfx::ColorSpace::PrimaryID primary_id = GetPrimaryID(color_space_);
+
+ // TODO(ccameron): This needs to take into account whether or not this texture
+ // will be sampled in linear or nonlinear space.
+ gfx::ColorSpace::TransferID transfer_id =
+ gfx::ColorSpace::TransferID::IEC61966_2_1;
+ if (pixel_format_ == kF16CanvasPixelFormat)
+ transfer_id = gfx::ColorSpace::TransferID::LINEAR_HDR;
+
+ return gfx::ColorSpace(primary_id, transfer_id);
+}
+
+gfx::ColorSpace CanvasColorParams::GetStorageGfxColorSpace() const {
+ gfx::ColorSpace::PrimaryID primary_id = GetPrimaryID(color_space_);
+
+ gfx::ColorSpace::TransferID transfer_id =
+ gfx::ColorSpace::TransferID::IEC61966_2_1;
+ if (pixel_format_ == kF16CanvasPixelFormat)
+ transfer_id = gfx::ColorSpace::TransferID::LINEAR_HDR;
+
+ return gfx::ColorSpace(primary_id, transfer_id);
+}
+
+sk_sp<SkColorSpace> CanvasColorParams::GetSkColorSpace() const {
+ SkColorSpace::Gamut gamut = SkColorSpace::kSRGB_Gamut;
+ SkColorSpace::RenderTargetGamma gamma = SkColorSpace::kSRGB_RenderTargetGamma;
+ switch (color_space_) {
+ case kSRGBCanvasColorSpace:
+ if (pixel_format_ == kF16CanvasPixelFormat)
+ gamma = SkColorSpace::kLinear_RenderTargetGamma;
+ break;
+ case kRec2020CanvasColorSpace:
+ gamut = SkColorSpace::kRec2020_Gamut;
+ gamma = SkColorSpace::kLinear_RenderTargetGamma;
+ break;
+ case kP3CanvasColorSpace:
+ gamut = SkColorSpace::kDCIP3_D65_Gamut;
+ gamma = SkColorSpace::kLinear_RenderTargetGamma;
+ break;
+ }
+ return SkColorSpace::MakeRGB(gamma, gamut);
+}
+
+gfx::BufferFormat CanvasColorParams::GetBufferFormat() const {
+ static_assert(kN32_SkColorType == kRGBA_8888_SkColorType ||
+ kN32_SkColorType == kBGRA_8888_SkColorType,
+ "Unexpected kN32_SkColorType value.");
+ constexpr gfx::BufferFormat kN32BufferFormat =
+ kN32_SkColorType == kRGBA_8888_SkColorType ? gfx::BufferFormat::RGBA_8888
+ : gfx::BufferFormat::BGRA_8888;
+
+ if (pixel_format_ == kF16CanvasPixelFormat)
+ return gfx::BufferFormat::RGBA_F16;
+
+ return kN32BufferFormat;
+}
+
+GLenum CanvasColorParams::GLInternalFormat() const {
+ // TODO(junov): try GL_RGB when opacity_mode_ == kOpaque
+ static_assert(kN32_SkColorType == kRGBA_8888_SkColorType ||
+ kN32_SkColorType == kBGRA_8888_SkColorType,
+ "Unexpected kN32_SkColorType value.");
+ constexpr GLenum kN32GLInternalBufferFormat =
+ kN32_SkColorType == kRGBA_8888_SkColorType ? GL_RGBA : GL_BGRA_EXT;
+ if (pixel_format_ == kF16CanvasPixelFormat)
+ return GL_RGBA;
+
+ return kN32GLInternalBufferFormat;
+}
+
+GLenum CanvasColorParams::GLType() const {
+ switch (pixel_format_) {
+ case kRGBA8CanvasPixelFormat:
+ return GL_UNSIGNED_BYTE;
+ case kF16CanvasPixelFormat:
+ return GL_HALF_FLOAT;
+ default:
+ break;
+ }
+ NOTREACHED();
+ return GL_UNSIGNED_BYTE;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/canvas_color_params.h b/chromium/third_party/blink/renderer/platform/graphics/canvas_color_params.h
new file mode 100644
index 00000000000..f3d91d44f93
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/canvas_color_params.h
@@ -0,0 +1,99 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CANVAS_COLOR_PARAMS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CANVAS_COLOR_PARAMS_H_
+
+#include "third_party/blink/renderer/platform/graphics/graphics_types.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/skia/include/core/SkColorSpace.h"
+#include "third_party/skia/include/core/SkImageInfo.h"
+#include "ui/gfx/buffer_types.h"
+
+class SkCanvas;
+
+namespace cc {
+class PaintCanvas;
+}
+
+namespace gfx {
+class ColorSpace;
+}
+
+namespace blink {
+
+enum CanvasColorSpace {
+ kSRGBCanvasColorSpace,
+ kRec2020CanvasColorSpace,
+ kP3CanvasColorSpace,
+};
+
+enum CanvasPixelFormat {
+ kRGBA8CanvasPixelFormat,
+ kRGB10A2CanvasPixelFormat,
+ kRGBA12CanvasPixelFormat,
+ kF16CanvasPixelFormat,
+};
+
+class PLATFORM_EXPORT CanvasColorParams {
+ public:
+ // The default constructor will create an output-blended 8-bit surface.
+ CanvasColorParams();
+ CanvasColorParams(CanvasColorSpace, CanvasPixelFormat, OpacityMode);
+ explicit CanvasColorParams(const SkImageInfo&);
+ CanvasColorSpace ColorSpace() const { return color_space_; }
+ CanvasPixelFormat PixelFormat() const { return pixel_format_; }
+ OpacityMode GetOpacityMode() const { return opacity_mode_; }
+
+ void SetCanvasColorSpace(CanvasColorSpace);
+ void SetCanvasPixelFormat(CanvasPixelFormat);
+ void SetOpacityMode(OpacityMode);
+
+ // Indicates whether rendering needs to go through an SkColorSpaceXformCanvas
+ // in order to enforce non-gamma-aware pixel math behaviour.
+ bool NeedsSkColorSpaceXformCanvas() const;
+
+ // Indicates if pixels in this canvas color settings require any color
+ // conversion to be used in the passed canvas color settings.
+ bool NeedsColorConversion(const CanvasColorParams&) const;
+
+ // The SkColorSpace to use in the SkImageInfo for allocated SkSurfaces. This
+ // is nullptr in legacy rendering mode and when the surface is supposed to be
+ // in sRGB (for which we wrap the canvas into a PaintCanvas along with an
+ // SkColorSpaceXformCanvas).
+ sk_sp<SkColorSpace> GetSkColorSpaceForSkSurfaces() const;
+
+ // Wraps an SkCanvas into a PaintCanvas, along with an SkColorSpaceXformCanvas
+ // if necessary.
+ std::unique_ptr<cc::PaintCanvas> WrapCanvas(SkCanvas*) const;
+
+ // The pixel format to use for allocating SkSurfaces.
+ SkColorType GetSkColorType() const;
+ uint8_t BytesPerPixel() const;
+
+ // The color space in which pixels read from the canvas via a shader will be
+ // returned. Note that for canvases with linear pixel math, these will be
+ // converted from their storage space into a linear space.
+ gfx::ColorSpace GetSamplerGfxColorSpace() const;
+
+ // Return the color space of the underlying data for the canvas.
+ gfx::ColorSpace GetStorageGfxColorSpace() const;
+ sk_sp<SkColorSpace> GetSkColorSpace() const;
+ SkAlphaType GetSkAlphaType() const;
+ const SkSurfaceProps* GetSkSurfaceProps() const;
+
+ // Gpu memory buffer parameters
+ gfx::BufferFormat GetBufferFormat() const;
+ uint32_t GLInternalFormat() const;
+ uint32_t GLType() const;
+
+ private:
+ CanvasColorSpace color_space_ = kSRGBCanvasColorSpace;
+ CanvasPixelFormat pixel_format_ = kRGBA8CanvasPixelFormat;
+ OpacityMode opacity_mode_ = kNonOpaque;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CANVAS_COLOR_PARAMS_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/canvas_color_params_test.cc b/chromium/third_party/blink/renderer/platform/graphics/canvas_color_params_test.cc
new file mode 100644
index 00000000000..e6596a5065f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/canvas_color_params_test.cc
@@ -0,0 +1,73 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/canvas_color_params.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/graphics/color_correction_test_utils.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+#include "third_party/skia/include/core/SkColorSpaceXform.h"
+#include "ui/gfx/color_space.h"
+
+namespace blink {
+
+// When drawing a color managed canvas, the target SkColorSpace is obtained by
+// calling CanvasColorParams::GetSkColorSpaceForSkSurfaces(). When drawing media
+// to the canvas, the target gfx::ColorSpace is returned by CanvasColorParams::
+// GetStorageGfxColorSpace(). This test verifies that the two different color
+// spaces are approximately the same for different CanvasColorParam objects.
+// This test does not use SkColorSpace::Equals() since it is sensitive to
+// rounding issues (floats don't round-trip perfectly through ICC fixed point).
+// Instead, it color converts a pixel and compares the result. Furthermore, this
+// test does not include sRGB as target color space since we use
+// SkColorSpaceXformCanvas for sRGB targets and GetSkColorSpaceForSkSurfaces()
+// returns nullptr for the surface.
+TEST(CanvasColorParamsTest, MatchSkColorSpaceWithGfxColorSpace) {
+ sk_sp<SkColorSpace> src_rgb_color_space = SkColorSpace::MakeSRGB();
+ std::unique_ptr<uint8_t[]> src_pixel(new uint8_t[4]{32, 96, 160, 255});
+
+ CanvasColorSpace canvas_color_spaces[] = {
+ kSRGBCanvasColorSpace, kRec2020CanvasColorSpace, kP3CanvasColorSpace,
+ };
+
+ for (int iter_color_space = 0; iter_color_space < 3; iter_color_space++) {
+ CanvasColorParams color_params(canvas_color_spaces[iter_color_space],
+ kF16CanvasPixelFormat, kNonOpaque);
+
+ std::unique_ptr<SkColorSpaceXform> color_space_xform_canvas =
+ SkColorSpaceXform::New(src_rgb_color_space.get(),
+ color_params.GetSkColorSpace().get());
+ std::unique_ptr<SkColorSpaceXform> color_space_xform_media =
+ SkColorSpaceXform::New(
+ src_rgb_color_space.get(),
+ color_params.GetStorageGfxColorSpace().ToSkColorSpace().get());
+
+ std::unique_ptr<uint8_t[]> transformed_pixel_canvas(
+ new uint8_t[color_params.BytesPerPixel()]());
+ std::unique_ptr<uint8_t[]> transformed_pixel_media(
+ new uint8_t[color_params.BytesPerPixel()]());
+
+ SkColorSpaceXform::ColorFormat transformed_color_format =
+ color_params.BytesPerPixel() == 4
+ ? SkColorSpaceXform::ColorFormat::kRGBA_8888_ColorFormat
+ : SkColorSpaceXform::ColorFormat::kRGBA_F16_ColorFormat;
+
+ EXPECT_TRUE(color_space_xform_canvas->apply(
+ transformed_color_format, transformed_pixel_canvas.get(),
+ SkColorSpaceXform::ColorFormat::kRGBA_8888_ColorFormat, src_pixel.get(),
+ 1, SkAlphaType::kPremul_SkAlphaType));
+
+ EXPECT_TRUE(color_space_xform_media->apply(
+ transformed_color_format, transformed_pixel_media.get(),
+ SkColorSpaceXform::ColorFormat::kRGBA_8888_ColorFormat, src_pixel.get(),
+ 1, SkAlphaType::kPremul_SkAlphaType));
+
+ ColorCorrectionTestUtils::CompareColorCorrectedPixels(
+ transformed_pixel_canvas.get(), transformed_pixel_media.get(), 1,
+ kUint16ArrayStorageFormat, kAlphaMultiplied,
+ kUnpremulRoundTripTolerance);
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/canvas_heuristic_parameters.h b/chromium/third_party/blink/renderer/platform/graphics/canvas_heuristic_parameters.h
new file mode 100644
index 00000000000..d95bad3ef20
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/canvas_heuristic_parameters.h
@@ -0,0 +1,87 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CANVAS_HEURISTIC_PARAMETERS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CANVAS_HEURISTIC_PARAMETERS_H_
+
+#include "build/build_config.h"
+
+namespace blink {
+
+namespace CanvasHeuristicParameters {
+
+enum {
+ // Disable Deferral overdraw parameters
+ //======================================
+
+ // Heuristic: Canvases that are overdrawn beyond this factor in a
+ // single frame will be disabled deferral.
+ kExpensiveOverdrawThreshold = 10,
+
+// Disable Acceleration heuristic parameters
+//===========================================
+
+// When drawing very large images to canvases, there is a point where
+// GPU acceleration becomes inefficient due to texture upload overhead,
+// especially when the image is large enough that it is likely to
+// monopolize the texture cache, and when it is being downsized to the
+// point that few of the upload texels are actually sampled. When both
+// of these conditions are met, we disable acceleration.
+#if defined(OS_ANDROID)
+ // The limits in mobile platforms are halved.
+ kDrawImageTextureUploadSoftSizeLimit = 4096 * 4096 / 2,
+ kDrawImageTextureUploadHardSizeLimit = 8192 * 8192 / 2,
+#else
+ kDrawImageTextureUploadSoftSizeLimit = 4096 * 4096,
+ kDrawImageTextureUploadHardSizeLimit = 8192 * 8192,
+#endif // defined(OS_ANDROID)
+ kDrawImageTextureUploadSoftSizeLimitScaleThreshold = 4,
+
+ // GPU readback prevention heuristics
+ //====================================
+
+ kGPUReadbackForcesNoAcceleration = 1,
+
+ // When gpu readback is successively invoked in following number of frames,
+ // we disable gpu acceleration to avoid the high cost of gpu readback.
+ kGPUReadbackMinSuccessiveFrames = 3,
+
+ // When a canvas is used as a source image, if its destination is
+ // non-accelerated and the source canvas is accelerated, a readback
+ // from the gpu is necessary. This option causes the source canvas to
+ // switch to non-accelerated when this situation is encountered to
+ // prevent future canvas-to-canvas draws from requiring a readback.
+ kDisableAccelerationToAvoidReadbacks = 0,
+
+ // See description of DisableAccelerationToAvoidReadbacks. This is the
+ // opposite strategy : accelerate the destination canvas. If both
+ // EnableAccelerationToAvoidReadbacks and
+ // DisableAccelerationToAvoidReadbacks are specified, we try to enable
+ // acceleration on the destination first. If that does not succeed,
+ // we disable acceleration on the source canvas. Either way, future
+ // readbacks are prevented.
+ kEnableAccelerationToAvoidReadbacks = 1,
+
+ // Accelerated rendering heuristics
+ // =================================
+
+ // Enables frequent flushing of the GrContext for accelerated canvas. Since
+ // skia internally batches the GrOp list when flushing the recording onto the
+ // SkCanvasand may only flush it the command buffer at the end of the frame,
+ // it can lead to inefficient parallelization with the GPU. This enables
+ // triggering context flushes at regular intervals, after a fixed number of
+ // draws.
+ kEnableGrContextFlushes = 1,
+
+ // The maximum number of draw ops executed on the canvas, after which the
+ // underlying GrContext is flushed.
+ kMaxDrawsBeforeContextFlush = 50,
+
+}; // enum
+
+} // namespace CanvasHeuristicParameters
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/graphics/canvas_metrics.cc b/chromium/third_party/blink/renderer/platform/graphics/canvas_metrics.cc
new file mode 100644
index 00000000000..d57c6173e09
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/canvas_metrics.cc
@@ -0,0 +1,21 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/canvas_metrics.h"
+
+#include "third_party/blink/renderer/platform/histogram.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/threading.h"
+
+namespace blink {
+
+void CanvasMetrics::CountCanvasContextUsage(
+ const CanvasContextUsage canvas_context_usage) {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(
+ EnumerationHistogram, usage_histogram,
+ ("WebCore.CanvasContextUsage", CanvasContextUsage::kNumberOfUsages));
+ usage_histogram.Count(canvas_context_usage);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/canvas_metrics.h b/chromium/third_party/blink/renderer/platform/graphics/canvas_metrics.h
new file mode 100644
index 00000000000..0958ba51ade
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/canvas_metrics.h
@@ -0,0 +1,34 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CANVAS_METRICS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CANVAS_METRICS_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT CanvasMetrics {
+ STATIC_ONLY(CanvasMetrics);
+
+ public:
+ enum CanvasContextUsage {
+ kCanvasCreated = 0,
+ kGPUAccelerated2DCanvasImageBufferCreated = 1,
+ kUnaccelerated2DCanvasImageBufferCreated = 3,
+ kAccelerated2DCanvasGPUContextLost = 4,
+ kUnaccelerated2DCanvasImageBufferCreationFailed = 5,
+ kGPUAccelerated2DCanvasImageBufferCreationFailed = 6,
+ kGPUAccelerated2DCanvasDeferralDisabled = 8,
+ kGPUAccelerated2DCanvasSurfaceCreationFailed = 9,
+ kNumberOfUsages
+ };
+
+ static void CountCanvasContextUsage(const CanvasContextUsage);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CANVAS_METRICS_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/canvas_resource.cc b/chromium/third_party/blink/renderer/platform/graphics/canvas_resource.cc
new file mode 100644
index 00000000000..727c44dcc99
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/canvas_resource.cc
@@ -0,0 +1,303 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/canvas_resource.h"
+
+#include "components/viz/common/resources/single_release_callback.h"
+#include "components/viz/common/resources/transferable_resource.h"
+#include "gpu/GLES2/gl2extchromium.h"
+#include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
+#include "skia/ext/texture_handle.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
+#include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
+#include "third_party/skia/include/gpu/GrContext.h"
+#include "ui/gfx/color_space.h"
+
+namespace blink {
+
+CanvasResource::CanvasResource(base::WeakPtr<CanvasResourceProvider> provider,
+ SkFilterQuality filter_quality)
+ : provider_(std::move(provider)), filter_quality_(filter_quality) {}
+
+CanvasResource::~CanvasResource() {
+ // Sync token should have been waited on in sub-class implementation of
+ // Abandon().
+ DCHECK(!sync_token_for_release_.HasData());
+}
+
+gpu::gles2::GLES2Interface* CanvasResource::ContextGL() const {
+ if (!ContextProviderWrapper())
+ return nullptr;
+ return ContextProviderWrapper()->ContextProvider()->ContextGL();
+}
+
+void CanvasResource::SetSyncTokenForRelease(const gpu::SyncToken& token) {
+ sync_token_for_release_ = token;
+}
+
+void CanvasResource::WaitSyncTokenBeforeRelease() {
+ auto gl = ContextGL();
+ if (sync_token_for_release_.HasData() && gl) {
+ gl->WaitSyncTokenCHROMIUM(sync_token_for_release_.GetData());
+ }
+ sync_token_for_release_.Clear();
+}
+
+static void ReleaseFrameResources(
+ base::WeakPtr<CanvasResourceProvider> resource_provider,
+ scoped_refptr<CanvasResource> resource,
+ const gpu::SyncToken& sync_token,
+ bool lost_resource) {
+ resource->SetSyncTokenForRelease(sync_token);
+ if (lost_resource) {
+ resource->Abandon();
+ }
+ if (resource_provider && !lost_resource && resource->IsRecycleable()) {
+ resource_provider->RecycleResource(std::move(resource));
+ }
+}
+
+bool CanvasResource::PrepareTransferableResource(
+ viz::TransferableResource* out_resource,
+ std::unique_ptr<viz::SingleReleaseCallback>* out_callback) {
+ DCHECK(IsValid());
+
+ // This should never be called on unaccelerated canvases (for now).
+
+ // Gpu compositing is a prerequisite for accelerated 2D canvas
+ // TODO: For WebGL to use this, we must add software composing support.
+ DCHECK(SharedGpuContext::IsGpuCompositingEnabled());
+ auto gl = ContextGL();
+ DCHECK(gl);
+
+ const gpu::Mailbox& mailbox = GetOrCreateGpuMailbox();
+ if (mailbox.IsZero())
+ return false;
+
+ *out_resource = viz::TransferableResource::MakeGLOverlay(
+ mailbox, GLFilter(), TextureTarget(), GetSyncToken(), gfx::Size(Size()),
+ IsOverlayCandidate());
+
+ scoped_refptr<CanvasResource> this_ref(this);
+ auto func = WTF::Bind(&ReleaseFrameResources, provider_,
+ WTF::Passed(std::move(this_ref)));
+ *out_callback = viz::SingleReleaseCallback::Create(std::move(func));
+ return true;
+}
+
+GrContext* CanvasResource::GetGrContext() const {
+ if (!ContextProviderWrapper())
+ return nullptr;
+ return ContextProviderWrapper()->ContextProvider()->GetGrContext();
+}
+
+GLenum CanvasResource::GLFilter() const {
+ return filter_quality_ == kNone_SkFilterQuality ? GL_NEAREST : GL_LINEAR;
+}
+
+// CanvasResource_Bitmap
+//==============================================================================
+
+CanvasResource_Bitmap::CanvasResource_Bitmap(
+ scoped_refptr<StaticBitmapImage> image,
+ base::WeakPtr<CanvasResourceProvider> provider,
+ SkFilterQuality filter_quality)
+ : CanvasResource(std::move(provider), filter_quality),
+ image_(std::move(image)) {}
+
+scoped_refptr<CanvasResource_Bitmap> CanvasResource_Bitmap::Create(
+ scoped_refptr<StaticBitmapImage> image,
+ base::WeakPtr<CanvasResourceProvider> provider,
+ SkFilterQuality filter_quality) {
+ scoped_refptr<CanvasResource_Bitmap> resource =
+ AdoptRef(new CanvasResource_Bitmap(std::move(image), std::move(provider),
+ filter_quality));
+ if (resource->IsValid())
+ return resource;
+ return nullptr;
+}
+
+bool CanvasResource_Bitmap::IsValid() const {
+ if (!image_)
+ return false;
+ return image_->IsValid();
+}
+
+void CanvasResource_Bitmap::TearDown() {
+ WaitSyncTokenBeforeRelease();
+ // We must not disassociate the mailbox from the texture object here because
+ // the texture may be recycled by skia and the associated cached mailbox
+ // stored by GraphicsContext3DUtils.cpp must remain valid.
+ image_ = nullptr;
+}
+
+IntSize CanvasResource_Bitmap::Size() const {
+ if (!image_)
+ return IntSize(0, 0);
+ return IntSize(image_->width(), image_->height());
+}
+
+GLenum CanvasResource_Bitmap::TextureTarget() const {
+ return GL_TEXTURE_2D;
+}
+
+const gpu::Mailbox& CanvasResource_Bitmap::GetOrCreateGpuMailbox() {
+ DCHECK(image_); // Calling code should check IsValid() before calling this.
+ image_->EnsureMailbox(kUnverifiedSyncToken, GLFilter());
+ return image_->GetMailbox();
+}
+
+bool CanvasResource_Bitmap::HasGpuMailbox() const {
+ return image_ && image_->HasMailbox();
+}
+
+const gpu::SyncToken& CanvasResource_Bitmap::GetSyncToken() {
+ DCHECK(image_); // Calling code should check IsValid() before calling this.
+ return image_->GetSyncToken();
+}
+
+base::WeakPtr<WebGraphicsContext3DProviderWrapper>
+CanvasResource_Bitmap::ContextProviderWrapper() const {
+ if (!image_)
+ return nullptr;
+ return image_->ContextProviderWrapper();
+}
+
+// CanvasResource_GpuMemoryBuffer
+//==============================================================================
+
+CanvasResource_GpuMemoryBuffer::CanvasResource_GpuMemoryBuffer(
+ const IntSize& size,
+ const CanvasColorParams& color_params,
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,
+ base::WeakPtr<CanvasResourceProvider> provider,
+ SkFilterQuality filter_quality)
+ : CanvasResource(provider, filter_quality),
+ context_provider_wrapper_(std::move(context_provider_wrapper)),
+ color_params_(color_params) {
+ if (!context_provider_wrapper_)
+ return;
+ auto gl = context_provider_wrapper_->ContextProvider()->ContextGL();
+ auto gr = context_provider_wrapper_->ContextProvider()->GetGrContext();
+ if (!gl || !gr)
+ return;
+ gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager =
+ Platform::Current()->GetGpuMemoryBufferManager();
+ if (!gpu_memory_buffer_manager)
+ return;
+ gpu_memory_buffer_ = gpu_memory_buffer_manager->CreateGpuMemoryBuffer(
+ gfx::Size(size.Width(), size.Height()),
+ color_params_.GetBufferFormat(), // Use format
+ gfx::BufferUsage::SCANOUT, gpu::kNullSurfaceHandle);
+ if (!gpu_memory_buffer_) {
+ return;
+ }
+ image_id_ = gl->CreateImageCHROMIUM(gpu_memory_buffer_->AsClientBuffer(),
+ size.Width(), size.Height(),
+ color_params_.GLInternalFormat());
+ if (!image_id_) {
+ gpu_memory_buffer_ = nullptr;
+ return;
+ }
+
+ gpu_memory_buffer_->SetColorSpace(color_params.GetStorageGfxColorSpace());
+ gl->GenTextures(1, &texture_id_);
+ const GLenum target = GL_TEXTURE_RECTANGLE_ARB;
+ gl->BindTexture(target, texture_id_);
+ gl->BindTexImage2DCHROMIUM(target, image_id_);
+}
+
+CanvasResource_GpuMemoryBuffer::~CanvasResource_GpuMemoryBuffer() {
+ TearDown();
+}
+
+GLenum CanvasResource_GpuMemoryBuffer::TextureTarget() const {
+ return GL_TEXTURE_RECTANGLE_ARB;
+}
+
+IntSize CanvasResource_GpuMemoryBuffer::Size() const {
+ return IntSize(gpu_memory_buffer_->GetSize().width(),
+ gpu_memory_buffer_->GetSize().height());
+}
+
+scoped_refptr<CanvasResource_GpuMemoryBuffer>
+CanvasResource_GpuMemoryBuffer::Create(
+ const IntSize& size,
+ const CanvasColorParams& color_params,
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,
+ base::WeakPtr<CanvasResourceProvider> provider,
+ SkFilterQuality filter_quality) {
+ scoped_refptr<CanvasResource_GpuMemoryBuffer> resource =
+ AdoptRef(new CanvasResource_GpuMemoryBuffer(
+ size, color_params, std::move(context_provider_wrapper), provider,
+ filter_quality));
+ if (resource->IsValid())
+ return resource;
+ return nullptr;
+}
+
+void CanvasResource_GpuMemoryBuffer::TearDown() {
+ WaitSyncTokenBeforeRelease();
+ if (!context_provider_wrapper_ || !image_id_)
+ return;
+ auto gl = context_provider_wrapper_->ContextProvider()->ContextGL();
+ if (gl && texture_id_) {
+ GLenum target = TextureTarget();
+ gl->BindTexture(target, texture_id_);
+ gl->ReleaseTexImage2DCHROMIUM(target, image_id_);
+ gl->DestroyImageCHROMIUM(image_id_);
+ gl->DeleteTextures(1, &texture_id_);
+ gl->BindTexture(target, 0);
+ }
+ image_id_ = 0;
+ texture_id_ = 0;
+ gpu_memory_buffer_ = nullptr;
+}
+
+const gpu::Mailbox& CanvasResource_GpuMemoryBuffer::GetOrCreateGpuMailbox() {
+ auto gl = ContextGL();
+ DCHECK(gl); // caller should already have early exited if !gl.
+ if (gpu_mailbox_.IsZero() && gl) {
+ gl->GenMailboxCHROMIUM(gpu_mailbox_.name);
+ gl->ProduceTextureDirectCHROMIUM(texture_id_, gpu_mailbox_.name);
+ mailbox_needs_new_sync_token_ = true;
+ }
+ return gpu_mailbox_;
+}
+
+bool CanvasResource_GpuMemoryBuffer::HasGpuMailbox() const {
+ return !gpu_mailbox_.IsZero();
+}
+
+const gpu::SyncToken& CanvasResource_GpuMemoryBuffer::GetSyncToken() {
+ if (mailbox_needs_new_sync_token_) {
+ auto gl = ContextGL();
+ DCHECK(gl); // caller should already have early exited if !gl.
+ mailbox_needs_new_sync_token_ = false;
+ gl->GenUnverifiedSyncTokenCHROMIUM(sync_token_.GetData());
+ }
+ return sync_token_;
+}
+
+void CanvasResource_GpuMemoryBuffer::CopyFromTexture(GLuint source_texture,
+ GLenum format,
+ GLenum type) {
+ if (!IsValid())
+ return;
+
+ ContextGL()->CopyTextureCHROMIUM(
+ source_texture, 0 /*sourceLevel*/, TextureTarget(), texture_id_,
+ 0 /*destLevel*/, format, type, false /*unpackFlipY*/,
+ false /*unpackPremultiplyAlpha*/, false /*unpackUnmultiplyAlpha*/);
+ mailbox_needs_new_sync_token_ = true;
+}
+
+base::WeakPtr<WebGraphicsContext3DProviderWrapper>
+CanvasResource_GpuMemoryBuffer::ContextProviderWrapper() const {
+ return context_provider_wrapper_;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/canvas_resource.h b/chromium/third_party/blink/renderer/platform/graphics/canvas_resource.h
new file mode 100644
index 00000000000..e9efde9bdc2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/canvas_resource.h
@@ -0,0 +1,158 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/weak_ptr.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "gpu/command_buffer/common/mailbox.h"
+#include "gpu/command_buffer/common/sync_token.h"
+#include "third_party/blink/renderer/platform/geometry/int_size.h"
+#include "third_party/blink/renderer/platform/graphics/canvas_color_params.h"
+#include "third_party/blink/renderer/platform/graphics/web_graphics_context_3d_provider_wrapper.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CANVAS_RESOURCE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CANVAS_RESOURCE_H_
+
+namespace gfx {
+
+class GpuMemoryBuffer;
+
+} // namespace gfx
+
+namespace viz {
+
+class SingleReleaseCallback;
+struct TransferableResource;
+
+} // namespace viz
+
+namespace blink {
+
+class CanvasResourceProvider;
+class StaticBitmapImage;
+
+// Generic resource interface, used for locking (RAII) and recycling pixel
+// buffers of any type.
+class PLATFORM_EXPORT CanvasResource : public WTF::RefCounted<CanvasResource> {
+ public:
+ virtual ~CanvasResource();
+ virtual void Abandon() = 0;
+ virtual bool IsRecycleable() const = 0;
+ virtual bool IsValid() const = 0;
+ virtual IntSize Size() const = 0;
+ virtual const gpu::Mailbox& GetOrCreateGpuMailbox() = 0;
+ virtual const gpu::SyncToken& GetSyncToken() = 0;
+ bool PrepareTransferableResource(
+ viz::TransferableResource* out_resource,
+ std::unique_ptr<viz::SingleReleaseCallback>* out_callback);
+ void SetSyncTokenForRelease(const gpu::SyncToken&);
+ void WaitSyncTokenBeforeRelease();
+ virtual void CopyFromTexture(GLuint source_texture,
+ GLenum format,
+ GLenum type) {
+ NOTREACHED();
+ }
+
+ protected:
+ CanvasResource(base::WeakPtr<CanvasResourceProvider>, SkFilterQuality);
+ virtual GLenum TextureTarget() const = 0;
+ virtual bool IsOverlayCandidate() const { return false; }
+ virtual bool HasGpuMailbox() const = 0;
+ gpu::gles2::GLES2Interface* ContextGL() const;
+ GLenum GLFilter() const;
+ GrContext* GetGrContext() const;
+ virtual base::WeakPtr<WebGraphicsContext3DProviderWrapper>
+ ContextProviderWrapper() const = 0;
+ void PrepareTransferableResourceCommon(
+ viz::TransferableResource* out_resource,
+ std::unique_ptr<viz::SingleReleaseCallback>* out_callback);
+
+ private:
+ // Sync token that was provided when resource was released
+ gpu::SyncToken sync_token_for_release_;
+ base::WeakPtr<CanvasResourceProvider> provider_;
+ SkFilterQuality filter_quality_;
+};
+
+// Resource type for skia Bitmaps (RAM and texture backed)
+class PLATFORM_EXPORT CanvasResource_Bitmap final : public CanvasResource {
+ public:
+ static scoped_refptr<CanvasResource_Bitmap> Create(
+ scoped_refptr<StaticBitmapImage>,
+ base::WeakPtr<CanvasResourceProvider>,
+ SkFilterQuality);
+ virtual ~CanvasResource_Bitmap() { Abandon(); }
+
+ // Not recyclable: Skia handles texture recycling internally and bitmaps are
+ // cheap to allocate.
+ bool IsRecycleable() const final { return false; }
+ bool IsValid() const final;
+ void Abandon() final { TearDown(); }
+ IntSize Size() const final;
+
+ private:
+ void TearDown();
+ GLenum TextureTarget() const final;
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper> ContextProviderWrapper()
+ const override;
+ const gpu::Mailbox& GetOrCreateGpuMailbox() override;
+ bool HasGpuMailbox() const override;
+ const gpu::SyncToken& GetSyncToken() override;
+
+ CanvasResource_Bitmap(scoped_refptr<StaticBitmapImage>,
+ base::WeakPtr<CanvasResourceProvider>,
+ SkFilterQuality);
+
+ scoped_refptr<StaticBitmapImage> image_;
+};
+
+// Resource type for GpuMemoryBuffers
+class PLATFORM_EXPORT CanvasResource_GpuMemoryBuffer final
+ : public CanvasResource {
+ public:
+ static scoped_refptr<CanvasResource_GpuMemoryBuffer> Create(
+ const IntSize&,
+ const CanvasColorParams&,
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper>,
+ base::WeakPtr<CanvasResourceProvider>,
+ SkFilterQuality);
+ virtual ~CanvasResource_GpuMemoryBuffer();
+ bool IsRecycleable() const final { return IsValid(); }
+ bool IsValid() const { return context_provider_wrapper_ && image_id_; }
+ void Abandon() final { TearDown(); }
+ IntSize Size() const final;
+
+ private:
+ void TearDown();
+ GLenum TextureTarget() const final;
+ bool IsOverlayCandidate() const final { return true; }
+ const gpu::Mailbox& GetOrCreateGpuMailbox() override;
+ bool HasGpuMailbox() const override;
+ const gpu::SyncToken& GetSyncToken() override;
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper> ContextProviderWrapper()
+ const override;
+ void CopyFromTexture(GLuint source_texture,
+ GLenum format,
+ GLenum type) override;
+
+ CanvasResource_GpuMemoryBuffer(
+ const IntSize&,
+ const CanvasColorParams&,
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper>,
+ base::WeakPtr<CanvasResourceProvider>,
+ SkFilterQuality);
+
+ gpu::Mailbox gpu_mailbox_;
+ gpu::SyncToken sync_token_;
+ bool mailbox_needs_new_sync_token_ = false;
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper_;
+ std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer_;
+ GLuint image_id_ = 0;
+ GLuint texture_id_ = 0;
+ CanvasColorParams color_params_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CANVAS_RESOURCE_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_host.h b/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_host.h
new file mode 100644
index 00000000000..d3bf9280122
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_host.h
@@ -0,0 +1,24 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CANVAS_RESOURCE_HOST_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CANVAS_RESOURCE_HOST_H_
+
+#include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT CanvasResourceHost {
+ public:
+ virtual ~CanvasResourceHost() = default;
+ virtual void NotifySurfaceInvalid() = 0;
+ virtual void SetNeedsCompositingUpdate() = 0;
+ virtual void RestoreCanvasMatrixClipStack(PaintCanvas*) const = 0;
+ virtual void UpdateMemoryUsage() = 0;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc b/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
new file mode 100644
index 00000000000..9c2c36925f7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
@@ -0,0 +1,542 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h"
+
+#include "cc/paint/decode_stashing_image_provider.h"
+#include "cc/paint/skia_paint_canvas.h"
+#include "gpu/GLES2/gl2extchromium.h"
+#include "gpu/command_buffer/common/capabilities.h"
+#include "gpu/command_buffer/common/gpu_memory_buffer_support.h"
+#include "skia/ext/texture_handle.h"
+#include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h"
+#include "third_party/blink/renderer/platform/graphics/canvas_heuristic_parameters.h"
+#include "third_party/blink/renderer/platform/graphics/canvas_resource.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
+#include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/skia/include/core/SkColorSpaceXformCanvas.h"
+#include "third_party/skia/include/gpu/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/GrContext.h"
+#include "ui/gfx/gpu_memory_buffer.h"
+
+namespace blink {
+
+// CanvasResourceProvider_Texture
+//==============================================================================
+//
+// * Renders to a texture managed by skia. Mailboxes are straight GL textures.
+// * Layers are not overlay candidates
+
+class CanvasResourceProvider_Texture : public CanvasResourceProvider {
+ public:
+ CanvasResourceProvider_Texture(
+ const IntSize& size,
+ unsigned msaa_sample_count,
+ const CanvasColorParams color_params,
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper>
+ context_provider_wrapper)
+ : CanvasResourceProvider(size,
+ color_params,
+ std::move(context_provider_wrapper)),
+ msaa_sample_count_(msaa_sample_count) {}
+
+ virtual ~CanvasResourceProvider_Texture() = default;
+
+ bool IsValid() const final { return GetSkSurface() && !IsGpuContextLost(); }
+ bool IsAccelerated() const final { return true; }
+
+ GLuint GetBackingTextureHandleForOverwrite() override {
+ return skia::GrBackendObjectToGrGLTextureInfo(
+ GetSkSurface()->getTextureHandle(
+ SkSurface::kDiscardWrite_TextureHandleAccess))
+ ->fID;
+ }
+
+ protected:
+ scoped_refptr<CanvasResource> ProduceFrame() override {
+ DCHECK(GetSkSurface());
+
+ if (IsGpuContextLost())
+ return nullptr;
+
+ auto gl = ContextGL();
+ DCHECK(gl);
+
+ if (ContextProviderWrapper()
+ ->ContextProvider()
+ ->GetCapabilities()
+ .disable_2d_canvas_copy_on_write) {
+ // A readback operation may alter the texture parameters, which may affect
+ // the compositor's behavior. Therefore, we must trigger copy-on-write
+ // even though we are not technically writing to the texture, only to its
+ // parameters.
+ // If this issue with readback affecting state is ever fixed, then we'll
+ // have to do this instead of triggering a copy-on-write:
+ // static_cast<AcceleratedStaticBitmapImage*>(image.get())
+ // ->RetainOriginalSkImageForCopyOnWrite();
+ GetSkSurface()->notifyContentWillChange(
+ SkSurface::kRetain_ContentChangeMode);
+ }
+
+ sk_sp<SkImage> skia_image = GetSkSurface()->makeImageSnapshot();
+ if (!skia_image)
+ return nullptr;
+ DCHECK(skia_image->isTextureBacked());
+
+ scoped_refptr<StaticBitmapImage> image =
+ StaticBitmapImage::Create(skia_image, ContextProviderWrapper());
+
+ scoped_refptr<CanvasResource> resource =
+ CanvasResource_Bitmap::Create(image, CreateWeakPtr(), FilterQuality());
+ if (!resource)
+ return nullptr;
+
+ return resource;
+ }
+
+ virtual sk_sp<SkSurface> CreateSkSurface() const {
+ if (IsGpuContextLost())
+ return nullptr;
+ auto gr = GetGrContext();
+ DCHECK(gr);
+
+ SkImageInfo info = SkImageInfo::Make(
+ Size().Width(), Size().Height(), ColorParams().GetSkColorType(),
+ kPremul_SkAlphaType, ColorParams().GetSkColorSpaceForSkSurfaces());
+ return SkSurface::MakeRenderTarget(gr, SkBudgeted::kNo, info,
+ msaa_sample_count_,
+ ColorParams().GetSkSurfaceProps());
+ }
+
+ unsigned msaa_sample_count_;
+};
+
+// CanvasResourceProvider_Texture_GpuMemoryBuffer
+//==============================================================================
+//
+// * Renders to a texture managed by skia. Mailboxes are
+// gpu-accelerated platform native surfaces.
+// * Layers are overlay candidates
+
+class CanvasResourceProvider_Texture_GpuMemoryBuffer final
+ : public CanvasResourceProvider_Texture {
+ public:
+ CanvasResourceProvider_Texture_GpuMemoryBuffer(
+ const IntSize& size,
+ unsigned msaa_sample_count,
+ const CanvasColorParams color_params,
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper>
+ context_provider_wrapper)
+ : CanvasResourceProvider_Texture(size,
+ msaa_sample_count,
+ color_params,
+ std::move(context_provider_wrapper)) {}
+
+ virtual ~CanvasResourceProvider_Texture_GpuMemoryBuffer() = default;
+
+ protected:
+ scoped_refptr<CanvasResource> CreateResource() final {
+ return CanvasResource_GpuMemoryBuffer::Create(
+ Size(), ColorParams(), ContextProviderWrapper(), CreateWeakPtr(),
+ FilterQuality());
+ }
+
+ scoped_refptr<CanvasResource> ProduceFrame() final {
+ DCHECK(GetSkSurface());
+
+ if (IsGpuContextLost())
+ return nullptr;
+
+ scoped_refptr<CanvasResource> output_resource = NewOrRecycledResource();
+ if (!output_resource) {
+ // GpuMemoryBuffer creation failed, fallback to Texture resource
+ return CanvasResourceProvider_Texture::ProduceFrame();
+ }
+
+ sk_sp<SkImage> image = GetSkSurface()->makeImageSnapshot();
+ if (!image)
+ return nullptr;
+ DCHECK(image->isTextureBacked());
+
+ GLuint skia_texture_id =
+ skia::GrBackendObjectToGrGLTextureInfo(image->getTextureHandle(true))
+ ->fID;
+
+ output_resource->CopyFromTexture(skia_texture_id,
+ ColorParams().GLInternalFormat(),
+ ColorParams().GLType());
+
+ return output_resource;
+ }
+};
+
+// CanvasResourceProvider_Bitmap
+//==============================================================================
+//
+// * Renders to a skia RAM-backed bitmap
+// * Mailboxing is not supported : cannot be directly composited
+
+class CanvasResourceProvider_Bitmap final : public CanvasResourceProvider {
+ public:
+ CanvasResourceProvider_Bitmap(const IntSize& size,
+ const CanvasColorParams color_params)
+ : CanvasResourceProvider(size,
+ color_params,
+ nullptr /*context_provider_wrapper*/) {}
+
+ ~CanvasResourceProvider_Bitmap() = default;
+
+ bool IsValid() const final { return GetSkSurface(); }
+ bool IsAccelerated() const final { return false; }
+
+ private:
+ scoped_refptr<CanvasResource> ProduceFrame() final {
+ NOTREACHED(); // Not directly compositable.
+ return nullptr;
+ }
+
+ sk_sp<SkSurface> CreateSkSurface() const override {
+ SkImageInfo info = SkImageInfo::Make(
+ Size().Width(), Size().Height(), ColorParams().GetSkColorType(),
+ kPremul_SkAlphaType, ColorParams().GetSkColorSpaceForSkSurfaces());
+ return SkSurface::MakeRaster(info, ColorParams().GetSkSurfaceProps());
+ }
+
+ sk_sp<SkSurface> surface_;
+};
+
+// CanvasResourceProvider base class implementation
+//==============================================================================
+
+enum ResourceType {
+ kTextureGpuMemoryBufferResourceType,
+ kTextureResourceType,
+ kBitmapResourceType,
+};
+
+constexpr ResourceType kSoftwareCompositedFallbackList[] = {
+ kBitmapResourceType,
+};
+
+constexpr ResourceType kSoftwareFallbackList[] = {
+ kBitmapResourceType,
+};
+
+constexpr ResourceType kAcceleratedFallbackList[] = {
+ kTextureResourceType, kBitmapResourceType,
+};
+
+constexpr ResourceType kAcceleratedCompositedFallbackList[] = {
+ kTextureGpuMemoryBufferResourceType, kTextureResourceType,
+ kBitmapResourceType,
+};
+
+std::unique_ptr<CanvasResourceProvider> CanvasResourceProvider::Create(
+ const IntSize& size,
+ ResourceUsage usage,
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,
+ unsigned msaa_sample_count,
+ const CanvasColorParams& colorParams) {
+ const ResourceType* resource_type_fallback_list = nullptr;
+ size_t list_length = 0;
+
+ switch (usage) {
+ case kSoftwareResourceUsage:
+ resource_type_fallback_list = kSoftwareFallbackList;
+ list_length = arraysize(kSoftwareFallbackList);
+ break;
+ case kSoftwareCompositedResourceUsage:
+ resource_type_fallback_list = kSoftwareCompositedFallbackList;
+ list_length = arraysize(kSoftwareCompositedFallbackList);
+ break;
+ case kAcceleratedResourceUsage:
+ resource_type_fallback_list = kAcceleratedFallbackList;
+ list_length = arraysize(kAcceleratedFallbackList);
+ break;
+ case kAcceleratedCompositedResourceUsage:
+ resource_type_fallback_list = kAcceleratedCompositedFallbackList;
+ list_length = arraysize(kAcceleratedCompositedFallbackList);
+ break;
+ }
+
+ std::unique_ptr<CanvasResourceProvider> provider;
+ for (size_t i = 0; i < list_length; ++i) {
+ switch (resource_type_fallback_list[i]) {
+ case kTextureGpuMemoryBufferResourceType:
+ DCHECK(SharedGpuContext::IsGpuCompositingEnabled());
+ if (RuntimeEnabledFeatures::Canvas2dImageChromiumEnabled()) {
+ if (!gpu::IsImageFromGpuMemoryBufferFormatSupported(
+ colorParams.GetBufferFormat(),
+ context_provider_wrapper->ContextProvider()
+ ->GetCapabilities()))
+ continue;
+ if (!gpu::IsImageSizeValidForGpuMemoryBufferFormat(
+ gfx::Size(size), colorParams.GetBufferFormat()))
+ continue;
+ DCHECK(gpu::IsImageFormatCompatibleWithGpuMemoryBufferFormat(
+ colorParams.GLInternalFormat(), colorParams.GetBufferFormat()));
+
+ provider =
+ std::make_unique<CanvasResourceProvider_Texture_GpuMemoryBuffer>(
+ size, msaa_sample_count, colorParams,
+ context_provider_wrapper);
+ }
+ break;
+ case kTextureResourceType:
+ // TODO(xlai): Check gpu acclereration mode before using this Resource
+ // Type of CanvasResourceProvider and then Add
+ // "DCHECK(SharedGpuContext::IsGpuCompositingEnabled());" here.
+ // See crbug.com/802053.
+ provider = std::make_unique<CanvasResourceProvider_Texture>(
+ size, msaa_sample_count, colorParams, context_provider_wrapper);
+ break;
+ case kBitmapResourceType:
+ provider =
+ std::make_unique<CanvasResourceProvider_Bitmap>(size, colorParams);
+ break;
+ }
+ if (provider && provider->IsValid())
+ return provider;
+ }
+
+ return nullptr;
+}
+
+CanvasResourceProvider::CanvasImageProvider::CanvasImageProvider(
+ cc::ImageDecodeCache* cache,
+ const gfx::ColorSpace& target_color_space)
+ : playback_image_provider_(cache,
+ target_color_space,
+ cc::PlaybackImageProvider::Settings()) {}
+
+CanvasResourceProvider::CanvasImageProvider::~CanvasImageProvider() = default;
+
+cc::ImageProvider::ScopedDecodedDrawImage
+CanvasResourceProvider::CanvasImageProvider::GetDecodedDrawImage(
+ const cc::DrawImage& draw_image) {
+ auto scoped_decoded_image =
+ playback_image_provider_.GetDecodedDrawImage(draw_image);
+ if (!scoped_decoded_image.needs_unlock())
+ return scoped_decoded_image;
+
+ if (!scoped_decoded_image.decoded_image().is_budgeted()) {
+ // If we have exceeded the budget, ReleaseLockedImages any locked decodes.
+ ReleaseLockedImages();
+ }
+
+ // It is safe to use base::Unretained, since decodes acquired from a provider
+ // must not exceed the provider's lifetime.
+ auto decoded_draw_image = scoped_decoded_image.decoded_image();
+ return ScopedDecodedDrawImage(
+ decoded_draw_image,
+ base::BindOnce(&CanvasImageProvider::CanUnlockImage,
+ base::Unretained(this), std::move(scoped_decoded_image)));
+}
+
+void CanvasResourceProvider::CanvasImageProvider::ReleaseLockedImages() {
+ locked_images_.clear();
+}
+
+void CanvasResourceProvider::CanvasImageProvider::CanUnlockImage(
+ ScopedDecodedDrawImage image) {
+ locked_images_.push_back(std::move(image));
+}
+
+CanvasResourceProvider::CanvasResourceProvider(
+ const IntSize& size,
+ const CanvasColorParams& color_params,
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper)
+ : weak_ptr_factory_(this),
+ context_provider_wrapper_(std::move(context_provider_wrapper)),
+ size_(size),
+ color_params_(color_params),
+ snapshot_paint_image_id_(cc::PaintImage::GetNextId()) {
+ if (context_provider_wrapper_)
+ context_provider_wrapper_->AddObserver(this);
+}
+
+CanvasResourceProvider::~CanvasResourceProvider() {
+ if (context_provider_wrapper_)
+ context_provider_wrapper_->RemoveObserver(this);
+}
+
+SkSurface* CanvasResourceProvider::GetSkSurface() const {
+ if (!surface_) {
+ surface_ = CreateSkSurface();
+ }
+ return surface_.get();
+}
+
+PaintCanvas* CanvasResourceProvider::Canvas() {
+ if (!canvas_) {
+ DCHECK(!canvas_image_provider_);
+
+ cc::ImageProvider* image_provider = nullptr;
+ if (ImageDecodeCache()) {
+ canvas_image_provider_.emplace(ImageDecodeCache(),
+ ColorParams().GetStorageGfxColorSpace());
+ image_provider = &*canvas_image_provider_;
+ }
+
+ cc::SkiaPaintCanvas::ContextFlushes context_flushes;
+ if (IsAccelerated()) {
+ context_flushes.enable =
+ CanvasHeuristicParameters::kEnableGrContextFlushes;
+ context_flushes.max_draws_before_flush =
+ CanvasHeuristicParameters::kMaxDrawsBeforeContextFlush;
+ }
+ if (ColorParams().NeedsSkColorSpaceXformCanvas()) {
+ canvas_ = std::make_unique<cc::SkiaPaintCanvas>(
+ GetSkSurface()->getCanvas(), ColorParams().GetSkColorSpace(),
+ std::move(image_provider), context_flushes);
+ } else {
+ canvas_ = std::make_unique<cc::SkiaPaintCanvas>(
+ GetSkSurface()->getCanvas(), std::move(image_provider),
+ context_flushes);
+ }
+ }
+
+ return canvas_.get();
+}
+
+void CanvasResourceProvider::OnContextDestroyed() {
+ if (canvas_image_provider_) {
+ DCHECK(canvas_);
+ canvas_->reset_image_provider();
+ canvas_image_provider_.reset();
+ }
+}
+
+void CanvasResourceProvider::ReleaseLockedImages() {
+ if (canvas_image_provider_)
+ canvas_image_provider_->ReleaseLockedImages();
+}
+
+scoped_refptr<StaticBitmapImage> CanvasResourceProvider::Snapshot() {
+ if (!IsValid())
+ return nullptr;
+
+ auto sk_image = GetSkSurface()->makeImageSnapshot();
+ auto last_snapshot_sk_image_id = snapshot_sk_image_id_;
+ snapshot_sk_image_id_ = sk_image->uniqueID();
+
+ if (ContextProviderWrapper()) {
+ return StaticBitmapImage::Create(std::move(sk_image),
+ ContextProviderWrapper());
+ }
+
+ // Ensure that a new PaintImage::ContentId is used only when the underlying
+ // SkImage changes. This is necessary to ensure that the same image results
+ // in a cache hit in cc's ImageDecodeCache.
+ if (snapshot_paint_image_content_id_ == PaintImage::kInvalidContentId ||
+ last_snapshot_sk_image_id != snapshot_sk_image_id_) {
+ snapshot_paint_image_content_id_ = PaintImage::GetNextContentId();
+ }
+
+ auto paint_image =
+ PaintImageBuilder::WithDefault()
+ .set_id(snapshot_paint_image_id_)
+ .set_image(std::move(sk_image), snapshot_paint_image_content_id_)
+ .TakePaintImage();
+ return StaticBitmapImage::Create(std::move(paint_image));
+}
+
+gpu::gles2::GLES2Interface* CanvasResourceProvider::ContextGL() const {
+ if (!context_provider_wrapper_)
+ return nullptr;
+ return context_provider_wrapper_->ContextProvider()->ContextGL();
+}
+
+GrContext* CanvasResourceProvider::GetGrContext() const {
+ if (!context_provider_wrapper_)
+ return nullptr;
+ return context_provider_wrapper_->ContextProvider()->GetGrContext();
+}
+
+void CanvasResourceProvider::FlushSkia() const {
+ GetSkSurface()->flush();
+}
+
+void CanvasResourceProvider::RecycleResource(
+ scoped_refptr<CanvasResource> resource) {
+ DCHECK(resource->HasOneRef());
+ if (resource_recycling_enabled_)
+ recycled_resources_.push_back(std::move(resource));
+}
+
+void CanvasResourceProvider::SetResourceRecyclingEnabled(bool value) {
+ resource_recycling_enabled_ = value;
+ if (!resource_recycling_enabled_)
+ ClearRecycledResources();
+}
+
+scoped_refptr<CanvasResource> CanvasResourceProvider::NewOrRecycledResource() {
+ if (recycled_resources_.size()) {
+ scoped_refptr<CanvasResource> resource =
+ std::move(recycled_resources_.back());
+ recycled_resources_.pop_back();
+ // Recycling implies releasing the old content
+ resource->WaitSyncTokenBeforeRelease();
+ return resource;
+ }
+ return CreateResource();
+}
+
+bool CanvasResourceProvider::IsGpuContextLost() const {
+ auto gl = ContextGL();
+ return !gl || gl->GetGraphicsResetStatusKHR() != GL_NO_ERROR;
+}
+
+bool CanvasResourceProvider::WritePixels(const SkImageInfo& orig_info,
+ const void* pixels,
+ size_t row_bytes,
+ int x,
+ int y) {
+ DCHECK(IsValid());
+ return GetSkSurface()->getCanvas()->writePixels(orig_info, pixels, row_bytes,
+ x, y);
+}
+
+void CanvasResourceProvider::Clear() {
+ // Clear the background transparent or opaque, as required. It would be nice
+ // if this wasn't required, but the canvas is currently filled with the magic
+ // transparency color. Can we have another way to manage this?
+ DCHECK(IsValid());
+ if (color_params_.GetOpacityMode() == kOpaque) {
+ Canvas()->clear(SK_ColorBLACK);
+ } else {
+ Canvas()->clear(SK_ColorTRANSPARENT);
+ }
+}
+
+void CanvasResourceProvider::ClearRecycledResources() {
+ recycled_resources_.clear();
+}
+
+void CanvasResourceProvider::InvalidateSurface() {
+ canvas_ = nullptr;
+ canvas_image_provider_.reset();
+ xform_canvas_ = nullptr;
+ surface_ = nullptr;
+}
+
+uint32_t CanvasResourceProvider::ContentUniqueID() const {
+ return GetSkSurface()->generationID();
+}
+
+scoped_refptr<CanvasResource> CanvasResourceProvider::CreateResource() {
+ // Needs to be implemented in subclasses that use resource recycling.
+ NOTREACHED();
+ return nullptr;
+}
+
+cc::ImageDecodeCache* CanvasResourceProvider::ImageDecodeCache() {
+ // TODO(khushalsagar): Hook up a software cache.
+ if (!context_provider_wrapper_)
+ return nullptr;
+ return context_provider_wrapper_->ContextProvider()->ImageDecodeCache();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h b/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h
new file mode 100644
index 00000000000..24c56c7c690
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h
@@ -0,0 +1,176 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CANVAS_RESOURCE_PROVIDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CANVAS_RESOURCE_PROVIDER_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "cc/paint/skia_paint_canvas.h"
+#include "cc/raster/playback_image_provider.h"
+#include "third_party/blink/renderer/platform/geometry/int_size.h"
+#include "third_party/blink/renderer/platform/graphics/canvas_color_params.h"
+#include "third_party/blink/renderer/platform/graphics/web_graphics_context_3d_provider_wrapper.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "third_party/khronos/GLES2/gl2.h"
+#include "third_party/skia/include/core/SkSurface.h"
+
+class GrContext;
+class SkCanvas;
+
+namespace cc {
+class ImageDecodeCache;
+class PaintCanvas;
+}
+
+namespace gpu {
+namespace gles2 {
+
+class GLES2Interface;
+
+} // namespace gles2
+} // namespace gpu
+
+namespace blink {
+
+class CanvasResource;
+class StaticBitmapImage;
+class WebGraphicsContext3DProviderWrapper;
+
+// CanvasResourceProvider
+//==============================================================================
+//
+// This is an abstract base class that encapsulates a drawable graphics
+// resource. Subclasses manage specific resource types (Gpu Textures,
+// GpuMemoryBuffer, Bitmap in RAM). CanvasResourceProvider serves as an
+// abstraction layer for these resource types. It is designed to serve
+// the needs of Canvas2DLayerBridge, but can also be used as a general purpose
+// provider of drawable surfaces for 2D rendering with skia.
+//
+// General usage:
+// 1) Use the Create() static method to create an instance
+// 2) use Canvas() to get a drawing interface
+// 3) Call Snapshot() to acquire a bitmap with the rendered image in it.
+
+class PLATFORM_EXPORT CanvasResourceProvider
+ : public WebGraphicsContext3DProviderWrapper::DestructionObserver {
+ WTF_MAKE_NONCOPYABLE(CanvasResourceProvider);
+
+ public:
+ enum ResourceUsage {
+ kSoftwareResourceUsage,
+ kSoftwareCompositedResourceUsage,
+ kAcceleratedResourceUsage,
+ kAcceleratedCompositedResourceUsage,
+ };
+
+ static std::unique_ptr<CanvasResourceProvider> Create(
+ const IntSize&,
+ ResourceUsage,
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper> = nullptr,
+ unsigned msaa_sample_count = 0,
+ const CanvasColorParams& = CanvasColorParams());
+
+ // Use this method for capturing a frame that is intended to be displayed via
+ // the compositor. Cases that need to acquire a snaptshot that is not destined
+ // to be transfered via TransferableResource should call Snapshot() instead.
+ virtual scoped_refptr<CanvasResource> ProduceFrame() = 0;
+ scoped_refptr<StaticBitmapImage> Snapshot();
+
+ // WebGraphicsContext3DProvider::DestructionObserver implementation.
+ void OnContextDestroyed() override;
+
+ cc::PaintCanvas* Canvas();
+ void ReleaseLockedImages();
+ void FlushSkia() const;
+ const CanvasColorParams& ColorParams() const { return color_params_; }
+ void SetFilterQuality(SkFilterQuality quality) { filter_quality_ = quality; }
+ const IntSize& Size() const { return size_; }
+ virtual bool IsValid() const = 0;
+ virtual bool IsAccelerated() const = 0;
+ uint32_t ContentUniqueID() const;
+ void ClearRecycledResources();
+ void RecycleResource(scoped_refptr<CanvasResource>);
+ void SetResourceRecyclingEnabled(bool);
+ SkSurface* GetSkSurface() const;
+ bool IsGpuContextLost() const;
+ bool WritePixels(const SkImageInfo& orig_info,
+ const void* pixels,
+ size_t row_bytes,
+ int x,
+ int y);
+ virtual GLuint GetBackingTextureHandleForOverwrite() {
+ NOTREACHED();
+ return 0;
+ }
+ void Clear();
+ ~CanvasResourceProvider() override;
+
+ protected:
+ gpu::gles2::GLES2Interface* ContextGL() const;
+ GrContext* GetGrContext() const;
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper> ContextProviderWrapper() {
+ return context_provider_wrapper_;
+ }
+ scoped_refptr<CanvasResource> NewOrRecycledResource();
+ SkFilterQuality FilterQuality() const { return filter_quality_; }
+ base::WeakPtr<CanvasResourceProvider> CreateWeakPtr() {
+ return weak_ptr_factory_.GetWeakPtr();
+ }
+
+ // Called by subclasses when the backing resource has changed and resources
+ // are not managed by skia, signaling that a new surface needs to be created.
+ void InvalidateSurface();
+
+ CanvasResourceProvider(const IntSize&,
+ const CanvasColorParams&,
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper>);
+
+ private:
+ class CanvasImageProvider : public cc::ImageProvider {
+ public:
+ CanvasImageProvider(cc::ImageDecodeCache*,
+ const gfx::ColorSpace& target_color_space);
+ ~CanvasImageProvider() override;
+
+ // cc::ImageProvider implementation.
+ ScopedDecodedDrawImage GetDecodedDrawImage(const cc::DrawImage&) override;
+
+ void ReleaseLockedImages();
+
+ private:
+ void CanUnlockImage(ScopedDecodedDrawImage);
+
+ std::vector<ScopedDecodedDrawImage> locked_images_;
+ cc::PlaybackImageProvider playback_image_provider_;
+ };
+
+ virtual sk_sp<SkSurface> CreateSkSurface() const = 0;
+ virtual scoped_refptr<CanvasResource> CreateResource();
+ cc::ImageDecodeCache* ImageDecodeCache();
+
+ base::WeakPtrFactory<CanvasResourceProvider> weak_ptr_factory_;
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper_;
+ IntSize size_;
+ CanvasColorParams color_params_;
+ Optional<CanvasImageProvider> canvas_image_provider_;
+ std::unique_ptr<cc::SkiaPaintCanvas> canvas_;
+ mutable sk_sp<SkSurface> surface_; // mutable for lazy init
+ std::unique_ptr<SkCanvas> xform_canvas_;
+ WTF::Vector<scoped_refptr<CanvasResource>> recycled_resources_;
+ SkFilterQuality filter_quality_;
+ bool resource_recycling_enabled_ = true;
+
+ const cc::PaintImage::Id snapshot_paint_image_id_;
+ cc::PaintImage::ContentId snapshot_paint_image_content_id_ =
+ cc::PaintImage::kInvalidContentId;
+ uint32_t snapshot_sk_image_id_ = 0u;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_test.cc b/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_test.cc
new file mode 100644
index 00000000000..7c1a70c0db5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_test.cc
@@ -0,0 +1,188 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/canvas_resource.h"
+
+#include "base/run_loop.h"
+#include "components/viz/test/test_gpu_memory_buffer_manager.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
+#include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
+#include "third_party/blink/renderer/platform/graphics/test/fake_gles2_interface.h"
+#include "third_party/blink/renderer/platform/graphics/test/fake_web_graphics_context_3d_provider.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+#include "third_party/skia/include/core/SkSurface.h"
+
+using testing::_;
+using testing::Pointee;
+using testing::Return;
+using testing::SetArrayArgument;
+using testing::Test;
+
+namespace blink {
+
+class MockGLES2InterfaceWithMailboxSupport : public FakeGLES2Interface {
+ public:
+ MOCK_METHOD2(ProduceTextureDirectCHROMIUM, void(GLuint, const GLbyte*));
+ MOCK_METHOD1(GenMailboxCHROMIUM, void(GLbyte*));
+ MOCK_METHOD1(GenUnverifiedSyncTokenCHROMIUM, void(GLbyte*));
+ MOCK_METHOD4(CreateImageCHROMIUM,
+ GLuint(ClientBuffer, GLsizei, GLsizei, GLenum));
+};
+
+class FakeCanvasResourcePlatformSupport : public TestingPlatformSupport {
+ public:
+ void RunUntilStop() const { base::RunLoop().Run(); }
+
+ void StopRunning() const { base::RunLoop().Quit(); }
+
+ private:
+ gpu::GpuMemoryBufferManager* GetGpuMemoryBufferManager() override {
+ return &test_gpu_memory_buffer_manager_;
+ }
+
+ viz::TestGpuMemoryBufferManager test_gpu_memory_buffer_manager_;
+};
+
+class CanvasResourceTest : public Test {
+ public:
+ void SetUp() override {
+ // Install our mock GL context so that it gets served by SharedGpuContext.
+ auto factory = [](FakeGLES2Interface* gl, bool* gpu_compositing_disabled)
+ -> std::unique_ptr<WebGraphicsContext3DProvider> {
+ *gpu_compositing_disabled = false;
+ return std::make_unique<FakeWebGraphicsContext3DProvider>(gl);
+ };
+ SharedGpuContext::SetContextProviderFactoryForTesting(
+ WTF::BindRepeating(factory, WTF::Unretained(&gl_)));
+ context_provider_wrapper_ = SharedGpuContext::ContextProviderWrapper();
+ }
+
+ void TearDown() override { SharedGpuContext::ResetForTesting(); }
+
+ GrContext* GetGrContext() {
+ return context_provider_wrapper_->ContextProvider()->GetGrContext();
+ }
+
+ protected:
+ MockGLES2InterfaceWithMailboxSupport gl_;
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper_;
+};
+
+gpu::SyncToken GenTestSyncToken(int id) {
+ gpu::SyncToken token;
+ token.Set(gpu::CommandBufferNamespace::GPU_IO,
+ gpu::CommandBufferId::FromUnsafeValue(id), 1);
+ return token;
+}
+
+TEST_F(CanvasResourceTest, SkiaResourceNoMailboxLeak) {
+ SkImageInfo image_info =
+ SkImageInfo::MakeN32(10, 10, kPremul_SkAlphaType, nullptr);
+ sk_sp<SkSurface> surface =
+ SkSurface::MakeRenderTarget(GetGrContext(), SkBudgeted::kYes, image_info);
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+
+ EXPECT_TRUE(!!context_provider_wrapper_);
+ scoped_refptr<CanvasResource> resource = CanvasResource_Bitmap::Create(
+ StaticBitmapImage::Create(surface->makeImageSnapshot(),
+ context_provider_wrapper_),
+ nullptr, kLow_SkFilterQuality);
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+
+ gpu::Mailbox test_mailbox;
+ test_mailbox.name[0] = 1;
+ EXPECT_CALL(gl_, GenMailboxCHROMIUM(_))
+ .WillOnce(SetArrayArgument<0>(
+ test_mailbox.name, test_mailbox.name + GL_MAILBOX_SIZE_CHROMIUM));
+ resource->GetOrCreateGpuMailbox();
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+
+ // No expected call to DeleteTextures becaus skia recycles
+ // No expected call to ProduceTextureDirectCHROMIUM(0, *) because
+ // mailbox is cached by GraphicsContext3DUtils and therefore does not need to
+ // be orphaned.
+ EXPECT_CALL(gl_, ProduceTextureDirectCHROMIUM(0, _)).Times(0);
+ resource = nullptr;
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+
+ // Purging skia's resource cache will finally delete the GrTexture, resulting
+ // in the mailbox being orphaned via ProduceTextureDirectCHROMIUM.
+ EXPECT_CALL(gl_,
+ ProduceTextureDirectCHROMIUM(0, Pointee(test_mailbox.name[0])))
+ .Times(0);
+ GetGrContext()->performDeferredCleanup(std::chrono::milliseconds(0));
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+}
+
+TEST_F(CanvasResourceTest, GpuMemoryBufferSyncTokenRefresh) {
+ ScopedTestingPlatformSupport<FakeCanvasResourcePlatformSupport> platform;
+
+ constexpr GLuint image_id = 1;
+ EXPECT_CALL(gl_, CreateImageCHROMIUM(_, _, _, _)).WillOnce(Return(image_id));
+ scoped_refptr<CanvasResource> resource =
+ CanvasResource_GpuMemoryBuffer::Create(
+ IntSize(10, 10), CanvasColorParams(),
+ SharedGpuContext::ContextProviderWrapper(),
+ nullptr, // Resource provider
+ kLow_SkFilterQuality);
+
+ EXPECT_TRUE(bool(resource));
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+
+ gpu::Mailbox test_mailbox;
+ test_mailbox.name[0] = 1;
+ EXPECT_CALL(gl_, GenMailboxCHROMIUM(_))
+ .WillOnce(SetArrayArgument<0>(
+ test_mailbox.name, test_mailbox.name + GL_MAILBOX_SIZE_CHROMIUM));
+ resource->GetOrCreateGpuMailbox();
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+
+ const gpu::SyncToken reference_token1 = GenTestSyncToken(1);
+ EXPECT_CALL(gl_, GenUnverifiedSyncTokenCHROMIUM(_))
+ .WillOnce(SetArrayArgument<0>(
+ reinterpret_cast<const GLbyte*>(&reference_token1),
+ reinterpret_cast<const GLbyte*>(&reference_token1 + 1)));
+ gpu::SyncToken actual_token = resource->GetSyncToken();
+ EXPECT_EQ(actual_token, reference_token1);
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+
+ // Grabbing the mailbox again without making any changes must not result in
+ // a sync token refresh
+ EXPECT_CALL(gl_, GenMailboxCHROMIUM(_)).Times(0);
+ EXPECT_CALL(gl_, GenUnverifiedSyncTokenCHROMIUM(_)).Times(0);
+ resource->GetOrCreateGpuMailbox();
+ actual_token = resource->GetSyncToken();
+ EXPECT_EQ(actual_token, reference_token1);
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+
+ // Grabbing the mailbox again after a content change must result in
+ // a sync token refresh, but the mailbox gets re-used.
+ EXPECT_CALL(gl_, GenMailboxCHROMIUM(_)).Times(0);
+ const gpu::SyncToken reference_token2 = GenTestSyncToken(2);
+ EXPECT_CALL(gl_, GenUnverifiedSyncTokenCHROMIUM(_))
+ .WillOnce(SetArrayArgument<0>(
+ reinterpret_cast<const GLbyte*>(&reference_token2),
+ reinterpret_cast<const GLbyte*>(&reference_token2 + 1)));
+ resource->CopyFromTexture(1, // source texture id
+ GL_RGBA, GL_UNSIGNED_BYTE);
+ resource->GetOrCreateGpuMailbox();
+ actual_token = resource->GetSyncToken();
+ EXPECT_EQ(actual_token, reference_token2);
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/color.cc b/chromium/third_party/blink/renderer/platform/graphics/color.cc
new file mode 100644
index 00000000000..ee2e59ab0e5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/color.cc
@@ -0,0 +1,468 @@
+/*
+ * Copyright (C) 2003, 2004, 2005, 2006, 2008 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/graphics/color.h"
+
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/decimal.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/dtoa.h"
+#include "third_party/blink/renderer/platform/wtf/hex_number.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_view.h"
+
+namespace blink {
+
+// VS 2015 and above allow these definitions and in this case require them
+#if !defined(COMPILER_MSVC) || _MSC_VER >= 1900
+// FIXME: Use C++11 enum classes to avoid static data member initializer
+// definition problems.
+const RGBA32 Color::kBlack;
+const RGBA32 Color::kWhite;
+const RGBA32 Color::kDarkGray;
+const RGBA32 Color::kGray;
+const RGBA32 Color::kLightGray;
+const RGBA32 Color::kTransparent;
+#endif
+
+static const RGBA32 kLightenedBlack = 0xFF545454;
+static const RGBA32 kDarkenedWhite = 0xFFABABAB;
+
+RGBA32 MakeRGB(int r, int g, int b) {
+ return 0xFF000000 | clampTo(r, 0, 255) << 16 | clampTo(g, 0, 255) << 8 |
+ clampTo(b, 0, 255);
+}
+
+RGBA32 MakeRGBA(int r, int g, int b, int a) {
+ return clampTo(a, 0, 255) << 24 | clampTo(r, 0, 255) << 16 |
+ clampTo(g, 0, 255) << 8 | clampTo(b, 0, 255);
+}
+
+static int ColorFloatToRGBAByte(float f) {
+ return clampTo(static_cast<int>(lroundf(255.0f * f)), 0, 255);
+}
+
+RGBA32 MakeRGBA32FromFloats(float r, float g, float b, float a) {
+ return ColorFloatToRGBAByte(a) << 24 | ColorFloatToRGBAByte(r) << 16 |
+ ColorFloatToRGBAByte(g) << 8 | ColorFloatToRGBAByte(b);
+}
+
+static double CalcHue(double temp1, double temp2, double hue_val) {
+ if (hue_val < 0.0)
+ hue_val += 6.0;
+ else if (hue_val >= 6.0)
+ hue_val -= 6.0;
+ if (hue_val < 1.0)
+ return temp1 + (temp2 - temp1) * hue_val;
+ if (hue_val < 3.0)
+ return temp2;
+ if (hue_val < 4.0)
+ return temp1 + (temp2 - temp1) * (4.0 - hue_val);
+ return temp1;
+}
+
+// Explanation of this algorithm can be found in the CSS Color 4 Module
+// specification at https://drafts.csswg.org/css-color-4/#hsl-to-rgb with
+// further explanation available at http://en.wikipedia.org/wiki/HSL_color_space
+
+// Hue is in the range of 0 to 6.0, the remainder are in the range 0 to 1.0
+RGBA32 MakeRGBAFromHSLA(double hue,
+ double saturation,
+ double lightness,
+ double alpha) {
+ const double scale_factor = 255.0;
+
+ if (!saturation) {
+ int grey_value = static_cast<int>(round(lightness * scale_factor));
+ return MakeRGBA(grey_value, grey_value, grey_value,
+ static_cast<int>(round(alpha * scale_factor)));
+ }
+
+ double temp2 = lightness <= 0.5
+ ? lightness * (1.0 + saturation)
+ : lightness + saturation - lightness * saturation;
+ double temp1 = 2.0 * lightness - temp2;
+
+ return MakeRGBA(
+ static_cast<int>(round(CalcHue(temp1, temp2, hue + 2.0) * scale_factor)),
+ static_cast<int>(round(CalcHue(temp1, temp2, hue) * scale_factor)),
+ static_cast<int>(round(CalcHue(temp1, temp2, hue - 2.0) * scale_factor)),
+ static_cast<int>(round(alpha * scale_factor)));
+}
+
+RGBA32 MakeRGBAFromCMYKA(float c, float m, float y, float k, float a) {
+ double colors = 1 - k;
+ int r = static_cast<int>(nextafter(256, 0) * (colors * (1 - c)));
+ int g = static_cast<int>(nextafter(256, 0) * (colors * (1 - m)));
+ int b = static_cast<int>(nextafter(256, 0) * (colors * (1 - y)));
+ return MakeRGBA(r, g, b, static_cast<float>(nextafter(256, 0) * a));
+}
+
+// originally moved here from the CSS parser
+template <typename CharacterType>
+static inline bool ParseHexColorInternal(const CharacterType* name,
+ unsigned length,
+ RGBA32& rgb) {
+ if (length != 3 && length != 4 && length != 6 && length != 8)
+ return false;
+ if ((length == 8 || length == 4) &&
+ !RuntimeEnabledFeatures::CSSHexAlphaColorEnabled())
+ return false;
+ unsigned value = 0;
+ for (unsigned i = 0; i < length; ++i) {
+ if (!IsASCIIHexDigit(name[i]))
+ return false;
+ value <<= 4;
+ value |= ToASCIIHexValue(name[i]);
+ }
+ if (length == 6) {
+ rgb = 0xFF000000 | value;
+ return true;
+ }
+ if (length == 8) {
+ // We parsed the values into RGBA order, but the RGBA32 type
+ // expects them to be in ARGB order, so we right rotate eight bits.
+ rgb = value << 24 | value >> 8;
+ return true;
+ }
+ if (length == 4) {
+ // #abcd converts to ddaabbcc in RGBA32.
+ rgb = (value & 0xF) << 28 | (value & 0xF) << 24 | (value & 0xF000) << 8 |
+ (value & 0xF000) << 4 | (value & 0xF00) << 4 | (value & 0xF00) |
+ (value & 0xF0) | (value & 0xF0) >> 4;
+ return true;
+ }
+ // #abc converts to #aabbcc
+ rgb = 0xFF000000 | (value & 0xF00) << 12 | (value & 0xF00) << 8 |
+ (value & 0xF0) << 8 | (value & 0xF0) << 4 | (value & 0xF) << 4 |
+ (value & 0xF);
+ return true;
+}
+
+bool Color::ParseHexColor(const LChar* name, unsigned length, RGBA32& rgb) {
+ return ParseHexColorInternal(name, length, rgb);
+}
+
+bool Color::ParseHexColor(const UChar* name, unsigned length, RGBA32& rgb) {
+ return ParseHexColorInternal(name, length, rgb);
+}
+
+bool Color::ParseHexColor(const StringView& name, RGBA32& rgb) {
+ if (name.IsEmpty())
+ return false;
+ if (name.Is8Bit())
+ return ParseHexColor(name.Characters8(), name.length(), rgb);
+ return ParseHexColor(name.Characters16(), name.length(), rgb);
+}
+
+int DifferenceSquared(const Color& c1, const Color& c2) {
+ int d_r = c1.Red() - c2.Red();
+ int d_g = c1.Green() - c2.Green();
+ int d_b = c1.Blue() - c2.Blue();
+ return d_r * d_r + d_g * d_g + d_b * d_b;
+}
+
+bool Color::SetFromString(const String& name) {
+ if (name[0] != '#')
+ return SetNamedColor(name);
+ if (name.Is8Bit())
+ return ParseHexColor(name.Characters8() + 1, name.length() - 1, color_);
+ return ParseHexColor(name.Characters16() + 1, name.length() - 1, color_);
+}
+
+String Color::SerializedAsCSSComponentValue() const {
+ StringBuilder result;
+ result.ReserveCapacity(32);
+ bool color_has_alpha = HasAlpha();
+ if (color_has_alpha)
+ result.Append("rgba(");
+ else
+ result.Append("rgb(");
+
+ result.AppendNumber(static_cast<unsigned char>(Red()));
+ result.Append(", ");
+
+ result.AppendNumber(static_cast<unsigned char>(Green()));
+ result.Append(", ");
+
+ result.AppendNumber(static_cast<unsigned char>(Blue()));
+ if (color_has_alpha) {
+ result.Append(", ");
+ // See <alphavalue> section in
+ // https://drafts.csswg.org/cssom/#serializing-css-values
+ int alphavalue = Alpha();
+ float rounded = round(alphavalue * 100 / 255.0f) / 100;
+ if (round(rounded * 255) == alphavalue) {
+ result.AppendNumber(rounded, 2);
+ } else {
+ rounded = round(alphavalue * 1000 / 255.0f) / 1000;
+ result.AppendNumber(rounded, 3);
+ }
+ }
+
+ result.Append(')');
+ return result.ToString();
+}
+
+String Color::Serialized() const {
+ if (!HasAlpha()) {
+ StringBuilder builder;
+ builder.ReserveCapacity(7);
+ builder.Append('#');
+ HexNumber::AppendByteAsHex(Red(), builder, HexNumber::kLowercase);
+ HexNumber::AppendByteAsHex(Green(), builder, HexNumber::kLowercase);
+ HexNumber::AppendByteAsHex(Blue(), builder, HexNumber::kLowercase);
+ return builder.ToString();
+ }
+
+ StringBuilder result;
+ result.ReserveCapacity(28);
+
+ result.Append("rgba(");
+ result.AppendNumber(Red());
+ result.Append(", ");
+ result.AppendNumber(Green());
+ result.Append(", ");
+ result.AppendNumber(Blue());
+ result.Append(", ");
+
+ if (!Alpha())
+ result.Append('0');
+ else {
+ result.Append(Decimal::FromDouble(Alpha() / 255.0).ToString());
+ }
+
+ result.Append(')');
+ return result.ToString();
+}
+
+String Color::NameForLayoutTreeAsText() const {
+ if (Alpha() < 0xFF)
+ return String::Format("#%02X%02X%02X%02X", Red(), Green(), Blue(), Alpha());
+ return String::Format("#%02X%02X%02X", Red(), Green(), Blue());
+}
+
+static inline const NamedColor* FindNamedColor(const String& name) {
+ char buffer[64]; // easily big enough for the longest color name
+ unsigned length = name.length();
+ if (length > sizeof(buffer) - 1)
+ return nullptr;
+ for (unsigned i = 0; i < length; ++i) {
+ UChar c = name[i];
+ if (!c || c > 0x7F)
+ return nullptr;
+ buffer[i] = ToASCIILower(static_cast<char>(c));
+ }
+ buffer[length] = '\0';
+ return FindColor(buffer, length);
+}
+
+bool Color::SetNamedColor(const String& name) {
+ const NamedColor* found_color = FindNamedColor(name);
+ color_ = found_color ? found_color->argb_value : 0;
+ return found_color;
+}
+
+Color Color::Light() const {
+ // Hardcode this common case for speed.
+ if (color_ == kBlack)
+ return kLightenedBlack;
+
+ const float scale_factor = nextafterf(256.0f, 0.0f);
+
+ float r, g, b, a;
+ GetRGBA(r, g, b, a);
+
+ float v = std::max(r, std::max(g, b));
+
+ if (v == 0.0f)
+ // Lightened black with alpha.
+ return Color(0x54, 0x54, 0x54, Alpha());
+
+ float multiplier = std::min(1.0f, v + 0.33f) / v;
+
+ return Color(static_cast<int>(multiplier * r * scale_factor),
+ static_cast<int>(multiplier * g * scale_factor),
+ static_cast<int>(multiplier * b * scale_factor), Alpha());
+}
+
+Color Color::Dark() const {
+ // Hardcode this common case for speed.
+ if (color_ == kWhite)
+ return kDarkenedWhite;
+
+ const float scale_factor = nextafterf(256.0f, 0.0f);
+
+ float r, g, b, a;
+ GetRGBA(r, g, b, a);
+
+ float v = std::max(r, std::max(g, b));
+ float multiplier = (v == 0.0f) ? 0.0f : std::max(0.0f, (v - 0.33f) / v);
+
+ return Color(static_cast<int>(multiplier * r * scale_factor),
+ static_cast<int>(multiplier * g * scale_factor),
+ static_cast<int>(multiplier * b * scale_factor), Alpha());
+}
+
+Color Color::CombineWithAlpha(float other_alpha) const {
+ RGBA32 rgb_only = Rgb() & 0x00FFFFFF;
+ float override_alpha = (Alpha() / 255.f) * other_alpha;
+ return rgb_only | ColorFloatToRGBAByte(override_alpha) << 24;
+}
+
+static int BlendComponent(int c, int a) {
+ // We use white.
+ float alpha = a / 255.0f;
+ int white_blend = 255 - a;
+ c -= white_blend;
+ return static_cast<int>(c / alpha);
+}
+
+const int kCStartAlpha = 153; // 60%
+const int kCEndAlpha = 204; // 80%;
+const int kCAlphaIncrement = 17; // Increments in between.
+
+Color Color::Blend(const Color& source) const {
+ if (!Alpha() || !source.HasAlpha())
+ return source;
+
+ if (!source.Alpha())
+ return *this;
+
+ int d = 255 * (Alpha() + source.Alpha()) - Alpha() * source.Alpha();
+ int a = d / 255;
+ int r = (Red() * Alpha() * (255 - source.Alpha()) +
+ 255 * source.Alpha() * source.Red()) /
+ d;
+ int g = (Green() * Alpha() * (255 - source.Alpha()) +
+ 255 * source.Alpha() * source.Green()) /
+ d;
+ int b = (Blue() * Alpha() * (255 - source.Alpha()) +
+ 255 * source.Alpha() * source.Blue()) /
+ d;
+ return Color(r, g, b, a);
+}
+
+Color Color::BlendWithWhite() const {
+ // If the color contains alpha already, we leave it alone.
+ if (HasAlpha())
+ return *this;
+
+ Color new_color;
+ for (int alpha = kCStartAlpha; alpha <= kCEndAlpha;
+ alpha += kCAlphaIncrement) {
+ // We have a solid color. Convert to an equivalent color that looks the
+ // same when blended with white at the current alpha. Try using less
+ // transparency if the numbers end up being negative.
+ int r = BlendComponent(Red(), alpha);
+ int g = BlendComponent(Green(), alpha);
+ int b = BlendComponent(Blue(), alpha);
+
+ new_color = Color(r, g, b, alpha);
+
+ if (r >= 0 && g >= 0 && b >= 0)
+ break;
+ }
+ return new_color;
+}
+
+void Color::GetRGBA(float& r, float& g, float& b, float& a) const {
+ r = Red() / 255.0f;
+ g = Green() / 255.0f;
+ b = Blue() / 255.0f;
+ a = Alpha() / 255.0f;
+}
+
+void Color::GetRGBA(double& r, double& g, double& b, double& a) const {
+ r = Red() / 255.0;
+ g = Green() / 255.0;
+ b = Blue() / 255.0;
+ a = Alpha() / 255.0;
+}
+
+void Color::GetHSL(double& hue, double& saturation, double& lightness) const {
+ // http://en.wikipedia.org/wiki/HSL_color_space. This is a direct copy of
+ // the algorithm therein, although it's 360^o based and we end up wanting
+ // [0...1) based. It's clearer if we stick to 360^o until the end.
+ double r = static_cast<double>(Red()) / 255.0;
+ double g = static_cast<double>(Green()) / 255.0;
+ double b = static_cast<double>(Blue()) / 255.0;
+ double max = std::max(std::max(r, g), b);
+ double min = std::min(std::min(r, g), b);
+
+ if (max == min)
+ hue = 0.0;
+ else if (max == r)
+ hue = (60.0 * ((g - b) / (max - min))) + 360.0;
+ else if (max == g)
+ hue = (60.0 * ((b - r) / (max - min))) + 120.0;
+ else
+ hue = (60.0 * ((r - g) / (max - min))) + 240.0;
+
+ if (hue >= 360.0)
+ hue -= 360.0;
+
+ // makeRGBAFromHSLA assumes that hue is in [0...1).
+ hue /= 360.0;
+
+ lightness = 0.5 * (max + min);
+ if (max == min)
+ saturation = 0.0;
+ else if (lightness <= 0.5)
+ saturation = ((max - min) / (max + min));
+ else
+ saturation = ((max - min) / (2.0 - (max + min)));
+}
+
+Color ColorFromPremultipliedARGB(RGBA32 pixel_color) {
+ int alpha = AlphaChannel(pixel_color);
+ if (alpha && alpha < 255) {
+ return Color::CreateUnchecked(RedChannel(pixel_color) * 255 / alpha,
+ GreenChannel(pixel_color) * 255 / alpha,
+ BlueChannel(pixel_color) * 255 / alpha,
+ alpha);
+ } else
+ return Color(pixel_color);
+}
+
+RGBA32 PremultipliedARGBFromColor(const Color& color) {
+ unsigned pixel_color;
+
+ unsigned alpha = color.Alpha();
+ if (alpha < 255) {
+ pixel_color =
+ Color::CreateUnchecked((color.Red() * alpha + 254) / 255,
+ (color.Green() * alpha + 254) / 255,
+ (color.Blue() * alpha + 254) / 255, alpha)
+ .Rgb();
+ } else
+ pixel_color = color.Rgb();
+
+ return pixel_color;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/color.h b/chromium/third_party/blink/renderer/platform/graphics/color.h
new file mode 100644
index 00000000000..97cacca234e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/color.h
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2003, 2004, 2005, 2006, 2010 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COLOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COLOR_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
+
+namespace blink {
+
+class Color;
+
+typedef unsigned RGBA32; // RGBA quadruplet
+
+PLATFORM_EXPORT RGBA32 MakeRGB(int r, int g, int b);
+PLATFORM_EXPORT RGBA32 MakeRGBA(int r, int g, int b, int a);
+
+PLATFORM_EXPORT RGBA32 MakeRGBA32FromFloats(float r, float g, float b, float a);
+PLATFORM_EXPORT RGBA32 MakeRGBAFromHSLA(double h, double s, double l, double a);
+PLATFORM_EXPORT RGBA32
+MakeRGBAFromCMYKA(float c, float m, float y, float k, float a);
+
+PLATFORM_EXPORT int DifferenceSquared(const Color&, const Color&);
+
+inline int RedChannel(RGBA32 color) {
+ return (color >> 16) & 0xFF;
+}
+inline int GreenChannel(RGBA32 color) {
+ return (color >> 8) & 0xFF;
+}
+inline int BlueChannel(RGBA32 color) {
+ return color & 0xFF;
+}
+inline int AlphaChannel(RGBA32 color) {
+ return (color >> 24) & 0xFF;
+}
+
+struct NamedColor {
+ DISALLOW_NEW();
+ const char* name;
+ unsigned argb_value;
+};
+
+PLATFORM_EXPORT const NamedColor* FindColor(const char* str, unsigned len);
+
+class PLATFORM_EXPORT Color {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ Color() : color_(Color::kTransparent) {}
+ Color(RGBA32 color) : color_(color) {}
+ Color(int r, int g, int b) : color_(MakeRGB(r, g, b)) {}
+ Color(int r, int g, int b, int a) : color_(MakeRGBA(r, g, b, a)) {}
+ // Color is currently limited to 32bit RGBA. Perhaps some day we'll support
+ // better colors.
+ Color(float r, float g, float b, float a)
+ : color_(MakeRGBA32FromFloats(r, g, b, a)) {}
+ // Creates a new color from the specific CMYK and alpha values.
+ Color(float c, float m, float y, float k, float a)
+ : color_(MakeRGBAFromCMYKA(c, m, y, k, a)) {}
+
+ static Color CreateUnchecked(int r, int g, int b) {
+ RGBA32 color = 0xFF000000 | r << 16 | g << 8 | b;
+ return Color(color);
+ }
+ static Color CreateUnchecked(int r, int g, int b, int a) {
+ RGBA32 color = a << 24 | r << 16 | g << 8 | b;
+ return Color(color);
+ }
+
+ // Returns the color serialized according to HTML5:
+ // http://www.whatwg.org/specs/web-apps/current-work/#serialization-of-a-color
+ String Serialized() const;
+
+ // Returns the color serialized according to CSSOM:
+ // https://drafts.csswg.org/cssom/#serialize-a-css-component-value
+ String SerializedAsCSSComponentValue() const;
+
+ // Returns the color serialized as either #RRGGBB or #RRGGBBAA. The latter
+ // format is not a valid CSS color, and should only be seen in DRT dumps.
+ String NameForLayoutTreeAsText() const;
+
+ // Returns whether parsing succeeded. The resulting Color is arbitrary
+ // if parsing fails.
+ bool SetFromString(const String&);
+ bool SetNamedColor(const String&);
+
+ bool HasAlpha() const { return Alpha() < 255; }
+
+ int Red() const { return RedChannel(color_); }
+ int Green() const { return GreenChannel(color_); }
+ int Blue() const { return BlueChannel(color_); }
+ int Alpha() const { return AlphaChannel(color_); }
+
+ RGBA32 Rgb() const { return color_; } // Preserve the alpha.
+ void SetRGB(int r, int g, int b) { color_ = MakeRGB(r, g, b); }
+ void SetRGB(RGBA32 rgb) { color_ = rgb; }
+ void GetRGBA(float& r, float& g, float& b, float& a) const;
+ void GetRGBA(double& r, double& g, double& b, double& a) const;
+ void GetHSL(double& h, double& s, double& l) const;
+
+ Color Light() const;
+ Color Dark() const;
+
+ Color CombineWithAlpha(float other_alpha) const;
+
+ // This is an implementation of Porter-Duff's "source-over" equation
+ Color Blend(const Color&) const;
+ Color BlendWithWhite() const;
+
+ static bool ParseHexColor(const StringView&, RGBA32&);
+ static bool ParseHexColor(const LChar*, unsigned, RGBA32&);
+ static bool ParseHexColor(const UChar*, unsigned, RGBA32&);
+
+ static const RGBA32 kBlack = 0xFF000000;
+ static const RGBA32 kWhite = 0xFFFFFFFF;
+ static const RGBA32 kDarkGray = 0xFF808080;
+ static const RGBA32 kGray = 0xFFA0A0A0;
+ static const RGBA32 kLightGray = 0xFFC0C0C0;
+ static const RGBA32 kTransparent = 0x00000000;
+
+ private:
+ RGBA32 color_;
+};
+
+inline bool operator==(const Color& a, const Color& b) {
+ return a.Rgb() == b.Rgb();
+}
+
+inline bool operator!=(const Color& a, const Color& b) {
+ return !(a == b);
+}
+
+PLATFORM_EXPORT Color ColorFromPremultipliedARGB(RGBA32);
+PLATFORM_EXPORT RGBA32 PremultipliedARGBFromColor(const Color&);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COLOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/color_behavior.cc b/chromium/third_party/blink/renderer/platform/graphics/color_behavior.cc
new file mode 100644
index 00000000000..070324a4d2f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/color_behavior.cc
@@ -0,0 +1,25 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/color_behavior.h"
+
+#include "base/metrics/histogram_macros.h"
+#include "third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h"
+#include "third_party/skia/include/core/SkICC.h"
+#include "ui/gfx/icc_profile.h"
+#include "ui/gfx/skia_color_space_util.h"
+
+namespace blink {
+
+bool ColorBehavior::operator==(const ColorBehavior& other) const {
+ if (type_ != other.type_)
+ return false;
+ return true;
+}
+
+bool ColorBehavior::operator!=(const ColorBehavior& other) const {
+ return !(*this == other);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/color_behavior.h b/chromium/third_party/blink/renderer/platform/graphics/color_behavior.h
new file mode 100644
index 00000000000..021c70fe24a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/color_behavior.h
@@ -0,0 +1,49 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COLOR_BEHAVIOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COLOR_BEHAVIOR_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "ui/gfx/color_space.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT ColorBehavior {
+ public:
+ // This specifies to ignore color profiles embedded in images entirely. No
+ // transformations will be applied to any pixel data, and no SkImages will be
+ // tagged with an SkColorSpace.
+ static ColorBehavior Ignore() { return ColorBehavior(Type::kIgnore); }
+ bool IsIgnore() const { return type_ == Type::kIgnore; }
+
+ // This specifies that images will not be transformed (to the extent
+ // possible), but that SkImages will be tagged with the embedded SkColorSpace
+ // (or sRGB if there was no embedded color profile).
+ static ColorBehavior Tag() { return ColorBehavior(Type::kTag); }
+ bool IsTag() const { return type_ == Type::kTag; }
+
+ // This specifies that images will be transformed to sRGB, and that SkImages
+ // will not be tagged with any SkColorSpace.
+ static ColorBehavior TransformToSRGB() {
+ return ColorBehavior(Type::kTransformToSRGB);
+ }
+ bool IsTransformToSRGB() const { return type_ == Type::kTransformToSRGB; }
+
+ bool operator==(const ColorBehavior&) const;
+ bool operator!=(const ColorBehavior&) const;
+
+ private:
+ enum class Type {
+ kIgnore,
+ kTag,
+ kTransformToSRGB,
+ };
+ ColorBehavior(Type type) : type_(type) {}
+ Type type_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COLOR_BEHAVIOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/color_blend.h b/chromium/third_party/blink/renderer/platform/graphics/color_blend.h
new file mode 100644
index 00000000000..dc03dea5d42
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/color_blend.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2003, 2004, 2005, 2006, 2010 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COLOR_BLEND_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COLOR_BLEND_H_
+
+#include "third_party/blink/renderer/platform/animation/animation_utilities.h"
+#include "third_party/blink/renderer/platform/graphics/color.h"
+
+namespace blink {
+
+inline Color Blend(const Color& from,
+ const Color& to,
+ double progress,
+ bool blend_premultiplied = true) {
+ if (blend_premultiplied) {
+ // Contrary to the name, RGBA32 actually stores ARGB, so we can initialize
+ // Color directly from premultipliedARGBFromColor(). Also,
+ // premultipliedARGBFromColor() bails on zero alpha, so special-case that.
+ Color premult_from = from.Alpha() ? PremultipliedARGBFromColor(from) : 0;
+ Color premult_to = to.Alpha() ? PremultipliedARGBFromColor(to) : 0;
+
+ Color premult_blended(
+ Blend(premult_from.Red(), premult_to.Red(), progress),
+ Blend(premult_from.Green(), premult_to.Green(), progress),
+ Blend(premult_from.Blue(), premult_to.Blue(), progress),
+ Blend(premult_from.Alpha(), premult_to.Alpha(), progress));
+
+ return Color(ColorFromPremultipliedARGB(premult_blended.Rgb()));
+ }
+
+ return Color(Blend(from.Red(), to.Red(), progress),
+ Blend(from.Green(), to.Green(), progress),
+ Blend(from.Blue(), to.Blue(), progress),
+ Blend(from.Alpha(), to.Alpha(), progress));
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COLOR_BLEND_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/color_correction_test_utils.cc b/chromium/third_party/blink/renderer/platform/graphics/color_correction_test_utils.cc
new file mode 100644
index 00000000000..bd0d4c5fc2d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/color_correction_test_utils.cc
@@ -0,0 +1,194 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/color_correction_test_utils.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/byte_swap.h"
+
+namespace blink {
+
+// This function is a compact version of SkHalfToFloat from Skia. If many
+// color correction tests fail at the same time, please check if SkHalf format
+// has changed.
+static float Float16ToFloat(const uint16_t& f16) {
+ union FloatUIntUnion {
+ uint32_t fUInt;
+ float fFloat;
+ };
+ FloatUIntUnion magic = {126 << 23};
+ FloatUIntUnion o;
+ if (((f16 >> 10) & 0x001f) == 0) {
+ o.fUInt = magic.fUInt + (f16 & 0x03ff);
+ o.fFloat -= magic.fFloat;
+ } else {
+ o.fUInt = (f16 & 0x03ff) << 13;
+ if (((f16 >> 10) & 0x001f) == 0x1f)
+ o.fUInt |= (255 << 23);
+ else
+ o.fUInt |= ((127 - 15 + ((f16 >> 10) & 0x001f)) << 23);
+ }
+ o.fUInt |= ((f16 >> 15) << 31);
+ return o.fFloat;
+}
+
+bool ColorCorrectionTestUtils::IsNearlyTheSame(float expected,
+ float actual,
+ float tolerance) {
+ EXPECT_LE(actual, expected + tolerance);
+ EXPECT_GE(actual, expected - tolerance);
+ return true;
+}
+
+void ColorCorrectionTestUtils::CompareColorCorrectedPixels(
+ const void* actual_pixels,
+ const void* expected_pixels,
+ int num_pixels,
+ ImageDataStorageFormat src_storage_format,
+ PixelsAlphaMultiply alpha_multiplied,
+ UnpremulRoundTripTolerance premul_unpremul_tolerance) {
+ bool test_passed = true;
+ int srgb_color_correction_tolerance = 3;
+ float wide_gamut_color_correction_tolerance = 0.01;
+ if (premul_unpremul_tolerance == kNoUnpremulRoundTripTolerance)
+ wide_gamut_color_correction_tolerance = 0;
+
+ switch (src_storage_format) {
+ case kUint8ClampedArrayStorageFormat: {
+ if (premul_unpremul_tolerance == kUnpremulRoundTripTolerance) {
+ // Premul->unpremul->premul round trip does not introduce any error when
+ // rounding intermediate results. However, we still might see some error
+ // introduced in consecutive color correction operations (error <= 3).
+ // For unpremul->premul->unpremul round trip, we do premul and compare
+ // the result.
+ const uint8_t* actual_pixels_u8 =
+ static_cast<const uint8_t*>(actual_pixels);
+ const uint8_t* expected_pixels_u8 =
+ static_cast<const uint8_t*>(expected_pixels);
+ for (int i = 0; test_passed && i < num_pixels; i++) {
+ test_passed &=
+ (actual_pixels_u8[i * 4 + 3] == expected_pixels_u8[i * 4 + 3]);
+ int alpha_multiplier =
+ alpha_multiplied ? 1 : expected_pixels_u8[i * 4 + 3];
+ for (int j = 0; j < 3; j++) {
+ test_passed &= IsNearlyTheSame(
+ actual_pixels_u8[i * 4 + j] * alpha_multiplier,
+ expected_pixels_u8[i * 4 + j] * alpha_multiplier,
+ srgb_color_correction_tolerance);
+ }
+ }
+ } else {
+ EXPECT_EQ(std::memcmp(actual_pixels, expected_pixels, num_pixels * 4),
+ 0);
+ }
+ break;
+ }
+
+ case kUint16ArrayStorageFormat: {
+ const uint16_t* actual_pixels_u16 =
+ static_cast<const uint16_t*>(actual_pixels);
+ const uint16_t* expected_pixels_u16 =
+ static_cast<const uint16_t*>(expected_pixels);
+ for (int i = 0; test_passed && i < num_pixels; i++) {
+ for (int j = 0; j < 4; j++) {
+ test_passed &=
+ IsNearlyTheSame(Float16ToFloat(actual_pixels_u16[i * 4 + j]),
+ Float16ToFloat(expected_pixels_u16[i * 4 + j]),
+ wide_gamut_color_correction_tolerance);
+ }
+ }
+ break;
+ }
+
+ case kFloat32ArrayStorageFormat: {
+ const float* actual_pixels_f32 = static_cast<const float*>(actual_pixels);
+ const float* expected_pixels_f32 =
+ static_cast<const float*>(expected_pixels);
+ for (int i = 0; test_passed && i < num_pixels; i++) {
+ for (int j = 0; j < 4; j++) {
+ test_passed &= IsNearlyTheSame(actual_pixels_f32[i * 4 + j],
+ expected_pixels_f32[i * 4 + j],
+ wide_gamut_color_correction_tolerance);
+ }
+ }
+ break;
+ }
+
+ default:
+ NOTREACHED();
+ }
+ EXPECT_EQ(test_passed, true);
+}
+
+bool ColorCorrectionTestUtils::ConvertPixelsToColorSpaceAndPixelFormatForTest(
+ void* src_data,
+ int num_elements,
+ CanvasColorSpace src_color_space,
+ ImageDataStorageFormat src_storage_format,
+ CanvasColorSpace dst_color_space,
+ CanvasPixelFormat dst_pixel_format,
+ std::unique_ptr<uint8_t[]>& converted_pixels,
+ SkColorSpaceXform::ColorFormat color_format_for_f16_canvas) {
+ unsigned num_pixels = num_elements / 4;
+ // Setting SkColorSpaceXform::apply parameters
+ SkColorSpaceXform::ColorFormat src_color_format =
+ SkColorSpaceXform::kRGBA_8888_ColorFormat;
+
+ uint16_t* u16_buffer = static_cast<uint16_t*>(src_data);
+ switch (src_storage_format) {
+ case kUint8ClampedArrayStorageFormat:
+ break;
+
+ case kUint16ArrayStorageFormat:
+ src_color_format =
+ SkColorSpaceXform::ColorFormat::kRGBA_U16_BE_ColorFormat;
+ for (int i = 0; i < num_elements; i++)
+ *(u16_buffer + i) = WTF::Bswap16(*(u16_buffer + i));
+ break;
+
+ case kFloat32ArrayStorageFormat:
+ src_color_format = SkColorSpaceXform::kRGBA_F32_ColorFormat;
+ break;
+
+ default:
+ NOTREACHED();
+ return false;
+ }
+
+ SkColorSpaceXform::ColorFormat dst_color_format =
+ SkColorSpaceXform::ColorFormat::kRGBA_8888_ColorFormat;
+ if (dst_pixel_format == kF16CanvasPixelFormat)
+ dst_color_format = color_format_for_f16_canvas;
+
+ sk_sp<SkColorSpace> src_sk_color_space = nullptr;
+ src_sk_color_space =
+ CanvasColorParams(src_color_space,
+ (src_storage_format == kUint8ClampedArrayStorageFormat)
+ ? kRGBA8CanvasPixelFormat
+ : kF16CanvasPixelFormat,
+ kNonOpaque)
+ .GetSkColorSpaceForSkSurfaces();
+ if (!src_sk_color_space.get())
+ src_sk_color_space = SkColorSpace::MakeSRGB();
+
+ sk_sp<SkColorSpace> dst_sk_color_space =
+ CanvasColorParams(dst_color_space, dst_pixel_format, kNonOpaque)
+ .GetSkColorSpaceForSkSurfaces();
+ if (!dst_sk_color_space.get())
+ dst_sk_color_space = SkColorSpace::MakeSRGB();
+
+ std::unique_ptr<SkColorSpaceXform> xform = SkColorSpaceXform::New(
+ src_sk_color_space.get(), dst_sk_color_space.get());
+ bool conversion_result =
+ xform->apply(dst_color_format, converted_pixels.get(), src_color_format,
+ src_data, num_pixels, kUnpremul_SkAlphaType);
+
+ if (src_storage_format == kUint16ArrayStorageFormat) {
+ for (int i = 0; i < num_elements; i++)
+ *(u16_buffer + i) = WTF::Bswap16(*(u16_buffer + i));
+ }
+ return conversion_result;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/color_correction_test_utils.h b/chromium/third_party/blink/renderer/platform/graphics/color_correction_test_utils.h
new file mode 100644
index 00000000000..294b15964aa
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/color_correction_test_utils.h
@@ -0,0 +1,52 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COLOR_CORRECTION_TEST_UTILS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COLOR_CORRECTION_TEST_UTILS_H_
+
+#include "third_party/blink/renderer/platform/graphics/canvas_color_params.h"
+
+#include "third_party/blink/renderer/platform/graphics/graphics_types.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/skia/include/core/SkColorSpaceXform.h"
+
+namespace blink {
+
+enum PixelsAlphaMultiply {
+ kAlphaMultiplied,
+ kAlphaUnmultiplied,
+};
+
+enum UnpremulRoundTripTolerance {
+ kNoUnpremulRoundTripTolerance,
+ kUnpremulRoundTripTolerance,
+};
+
+class ColorCorrectionTestUtils {
+ public:
+ static void CompareColorCorrectedPixels(
+ const void* actual_pixels,
+ const void* expected_pixels,
+ int num_pixels,
+ ImageDataStorageFormat src_storage_format,
+ PixelsAlphaMultiply alpha_multiplied,
+ UnpremulRoundTripTolerance premul_unpremul_tolerance);
+
+ static bool ConvertPixelsToColorSpaceAndPixelFormatForTest(
+ void* src_data,
+ int num_elements,
+ CanvasColorSpace src_color_space,
+ ImageDataStorageFormat src_storage_format,
+ CanvasColorSpace dst_color_space,
+ CanvasPixelFormat dst_pixel_format,
+ std::unique_ptr<uint8_t[]>& converted_pixels,
+ SkColorSpaceXform::ColorFormat color_format_for_f16_canvas);
+
+ private:
+ static bool IsNearlyTheSame(float expected, float actual, float tolerance);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COLOR_CORRECTION_TEST_UTILS_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/color_space_gamut.cc b/chromium/third_party/blink/renderer/platform/graphics/color_space_gamut.cc
new file mode 100644
index 00000000000..a38fc0d3a53
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/color_space_gamut.cc
@@ -0,0 +1,70 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/color_space_gamut.h"
+
+#include "third_party/blink/public/platform/web_screen_info.h"
+#include "third_party/skia/include/core/SkColorSpaceXform.h"
+
+namespace blink {
+
+namespace ColorSpaceUtilities {
+
+ColorSpaceGamut GetColorSpaceGamut(const WebScreenInfo& screen_info) {
+ const gfx::ColorSpace& color_space = screen_info.color_space;
+ if (!color_space.IsValid())
+ return ColorSpaceGamut::kUnknown;
+ // Return the gamut of the color space used for raster (this will return a
+ // wide gamut for HDR profiles).
+ return ColorSpaceUtilities::GetColorSpaceGamut(
+ color_space.GetRasterColorSpace().ToSkColorSpace().get());
+}
+
+ColorSpaceGamut GetColorSpaceGamut(SkColorSpace* color_space) {
+ sk_sp<SkColorSpace> sc_rgb(SkColorSpace::MakeSRGBLinear());
+ std::unique_ptr<SkColorSpaceXform> transform(
+ SkColorSpaceXform::New(color_space, sc_rgb.get()));
+
+ if (!transform)
+ return ColorSpaceGamut::kUnknown;
+
+ unsigned char in[3][4];
+ float out[3][4];
+ memset(in, 0, sizeof(in));
+ in[0][0] = 255;
+ in[1][1] = 255;
+ in[2][2] = 255;
+ in[0][3] = 255;
+ in[1][3] = 255;
+ in[2][3] = 255;
+ bool color_converison_successful = transform->apply(
+ SkColorSpaceXform::kRGBA_F32_ColorFormat, out,
+ SkColorSpaceXform::kRGBA_8888_ColorFormat, in, 3, kOpaque_SkAlphaType);
+ DCHECK(color_converison_successful);
+ float score = out[0][0] * out[1][1] * out[2][2];
+
+ if (score < 0.9)
+ return ColorSpaceGamut::kLessThanNTSC;
+ if (score < 0.95)
+ return ColorSpaceGamut::NTSC; // actual score 0.912839
+ if (score < 1.1)
+ return ColorSpaceGamut::SRGB; // actual score 1.0
+ if (score < 1.3)
+ return ColorSpaceGamut::kAlmostP3;
+ if (score < 1.425)
+ return ColorSpaceGamut::P3; // actual score 1.401899
+ if (score < 1.5)
+ return ColorSpaceGamut::kAdobeRGB; // actual score 1.458385
+ if (score < 2.0)
+ return ColorSpaceGamut::kWide;
+ if (score < 2.2)
+ return ColorSpaceGamut::BT2020; // actual score 2.104520
+ if (score < 2.7)
+ return ColorSpaceGamut::kProPhoto; // actual score 2.913247
+ return ColorSpaceGamut::kUltraWide;
+}
+
+} // namespace ColorSpaceUtilities
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/color_space_gamut.h b/chromium/third_party/blink/renderer/platform/graphics/color_space_gamut.h
new file mode 100644
index 00000000000..c935be809f1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/color_space_gamut.h
@@ -0,0 +1,41 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COLOR_SPACE_GAMUT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COLOR_SPACE_GAMUT_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+class SkColorSpace;
+
+namespace blink {
+
+struct WebScreenInfo;
+
+enum class ColorSpaceGamut {
+ // Values synced with 'Gamut' in src/tools/metrics/histograms/histograms.xml
+ kUnknown = 0,
+ kLessThanNTSC = 1,
+ NTSC = 2,
+ SRGB = 3,
+ kAlmostP3 = 4,
+ P3 = 5,
+ kAdobeRGB = 6,
+ kWide = 7,
+ BT2020 = 8,
+ kProPhoto = 9,
+ kUltraWide = 10,
+ kEnd
+};
+
+namespace ColorSpaceUtilities {
+
+PLATFORM_EXPORT ColorSpaceGamut GetColorSpaceGamut(const WebScreenInfo&);
+ColorSpaceGamut GetColorSpaceGamut(SkColorSpace*);
+
+} // namespace ColorSpaceUtilities
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COLOR_SPACE_GAMUT_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/color_space_profile_data.cc b/chromium/third_party/blink/renderer/platform/graphics/color_space_profile_data.cc
new file mode 100644
index 00000000000..1a1c6f5f65c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/color_space_profile_data.cc
@@ -0,0 +1,111 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/color_space_profile_data.h"
+
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+struct bt709ColorProfile {
+ static char* data() // BT.709 HDTV ITU
+ {
+ static unsigned char colorProfile[] = {
+ 0x00, 0x00, 0x01, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x6d, 0x6e, 0x74, 0x72, 0x52, 0x47, 0x42, 0x20, 0x58, 0x59, 0x5a, 0x20,
+ 0x07, 0xdf, 0x00, 0x0c, 0x00, 0x1f, 0x00, 0x17, 0x00, 0x3b, 0x00, 0x3b,
+ 0x61, 0x63, 0x73, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xd6,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x2d, 0x67, 0x6f, 0x6f, 0x67,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+ 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, 0x25,
+ 0x77, 0x74, 0x70, 0x74, 0x00, 0x00, 0x01, 0x0c, 0x00, 0x00, 0x00, 0x14,
+ 0x72, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x14,
+ 0x67, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x01, 0x34, 0x00, 0x00, 0x00, 0x14,
+ 0x62, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x01, 0x48, 0x00, 0x00, 0x00, 0x14,
+ 0x72, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0x5c, 0x00, 0x00, 0x00, 0x20,
+ 0x62, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0x5c, 0x00, 0x00, 0x00, 0x20,
+ 0x67, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0x5c, 0x00, 0x00, 0x00, 0x20,
+ 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11,
+ 0x42, 0x54, 0x2e, 0x37, 0x30, 0x39, 0x20, 0x48, 0x44, 0x54, 0x56, 0x20,
+ 0x49, 0x54, 0x55, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xf3, 0x51, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x16, 0xcc,
+ 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xa2,
+ 0x00, 0x00, 0x38, 0xf5, 0x00, 0x00, 0x03, 0x90, 0x58, 0x59, 0x5a, 0x20,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x99, 0x00, 0x00, 0xb7, 0x85,
+ 0x00, 0x00, 0x18, 0xda, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x24, 0xa0, 0x00, 0x00, 0x0f, 0x84, 0x00, 0x00, 0xb6, 0xcf,
+ 0x70, 0x61, 0x72, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
+ 0x00, 0x02, 0x38, 0xe4, 0x00, 0x00, 0xe8, 0xf0, 0x00, 0x00, 0x17, 0x10,
+ 0x00, 0x00, 0x38, 0xe3, 0x00, 0x00, 0x14, 0xbc};
+
+ return reinterpret_cast<char*>(colorProfile);
+ }
+
+ static size_t size() { return 380u; }
+};
+
+struct bt601ColorProfile {
+ static char* data() // BT.601 SMPTE-C
+ {
+ static unsigned char colorProfile[] = {
+ 0x00, 0x00, 0x01, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x6d, 0x6e, 0x74, 0x72, 0x52, 0x47, 0x42, 0x20, 0x58, 0x59, 0x5a, 0x20,
+ 0x07, 0xdf, 0x00, 0x0c, 0x00, 0x1f, 0x00, 0x17, 0x00, 0x3b, 0x00, 0x3b,
+ 0x61, 0x63, 0x73, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xd6,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x2d, 0x67, 0x6f, 0x6f, 0x67,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+ 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, 0x25,
+ 0x77, 0x74, 0x70, 0x74, 0x00, 0x00, 0x01, 0x0c, 0x00, 0x00, 0x00, 0x14,
+ 0x72, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x14,
+ 0x67, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x01, 0x34, 0x00, 0x00, 0x00, 0x14,
+ 0x62, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x01, 0x48, 0x00, 0x00, 0x00, 0x14,
+ 0x72, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0x5c, 0x00, 0x00, 0x00, 0x20,
+ 0x62, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0x5c, 0x00, 0x00, 0x00, 0x20,
+ 0x67, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0x5c, 0x00, 0x00, 0x00, 0x20,
+ 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f,
+ 0x42, 0x54, 0x2e, 0x36, 0x30, 0x31, 0x20, 0x53, 0x4d, 0x50, 0x54, 0x45,
+ 0x2d, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xf3, 0x51, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x16, 0xcc,
+ 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x95,
+ 0x00, 0x00, 0x38, 0xc1, 0x00, 0x00, 0x03, 0x7f, 0x58, 0x59, 0x5a, 0x20,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0xa5, 0x00, 0x00, 0xb4, 0x09,
+ 0x00, 0x00, 0x17, 0x63, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x27, 0x9d, 0x00, 0x00, 0x13, 0x36, 0x00, 0x00, 0xb8, 0x5f,
+ 0x70, 0x61, 0x72, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
+ 0x00, 0x02, 0x38, 0xe4, 0x00, 0x00, 0xe8, 0xf0, 0x00, 0x00, 0x17, 0x10,
+ 0x00, 0x00, 0x38, 0xe3, 0x00, 0x00, 0x14, 0xbc};
+
+ return reinterpret_cast<char*>(colorProfile);
+ }
+
+ static size_t size() { return 380u; }
+};
+
+namespace blink {
+
+void Bt709ColorProfileData(Vector<char>& data) {
+ DCHECK(WTF::IsMainThread());
+ DCHECK(data.IsEmpty());
+
+ data.Append(bt709ColorProfile::data(), bt709ColorProfile::size());
+}
+
+void Bt601ColorProfileData(Vector<char>& data) {
+ DCHECK(WTF::IsMainThread());
+ DCHECK(data.IsEmpty());
+
+ data.Append(bt601ColorProfile::data(), bt601ColorProfile::size());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/color_space_profile_data.h b/chromium/third_party/blink/renderer/platform/graphics/color_space_profile_data.h
new file mode 100644
index 00000000000..83489fbcb9e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/color_space_profile_data.h
@@ -0,0 +1,18 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COLOR_SPACE_PROFILE_DATA_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COLOR_SPACE_PROFILE_DATA_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+PLATFORM_EXPORT void Bt709ColorProfileData(Vector<char>& data);
+PLATFORM_EXPORT void Bt601ColorProfileData(Vector<char>& data);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COLOR_SPACE_PROFILE_DATA_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositing/README.md b/chromium/third_party/blink/renderer/platform/graphics/compositing/README.md
new file mode 100644
index 00000000000..274707ffec5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/compositing/README.md
@@ -0,0 +1,57 @@
+# `Source/platform/graphics/compositing`
+
+This directory contains the implementation of the "Blink compositing algorithm".
+
+This code is owned by the [paint team][paint-team-site].
+[paint-team-site]: https://www.chromium.org/teams/paint-team
+
+This document explains the SPv2 world as it develops, not the SPv1 world it
+replaces.
+
+## Blink compositing algorithm
+
+Design document: goo.gl/6xP8Oe
+
+Inputs: `PaintArtifact`
+Outputs: List of `cc::Layer` objects and `cc::PropertyTree`'s.
+
+The algorithm walks through the list of `PaintChunks` in the `PaintArtifact`,
+allocating new `c::Layers` if the `PaintChunk` cannot merge into an existing
+`cc::Layer`. The reasons why it would not be able to do so are:
+
+1. The `PaintChunk` requires a foreign layer (see below)
+
+2. The `PaintChunk` cannot merge with any existing layer, due incompatible
+direct compositing reasons on its `PropertyTreeState`.
+
+3. The `PaintChunk` overlaps with an earlier `cc::Layer` that it can't merge with
+due to reason 2, and there is no later-drawn `cc::Layer` for which reasons 1 and
+2 do not apply.
+
+In the worst case, this algorithm has an O(n^2) running time, where n is the
+number of `PaintChunks`.
+
+All property tree nodes referred to by any `PaintChunk` are currently copied
+into their equivalent `cc::PropertyTree` node, regardless of whether they are
+required by the above.
+
+### Flattening property tree nodes
+
+When `PaintChunks` can merge into an existing `cc::Layer`, they may have
+different `PropertyTreeState`s than the `PropertyTreeState` of the `cc::Layer`.
+If so, we need to *flatten* down the nodes that are different between the
+`PropertyTreeState` of the `PaintChunk` and the `cc::Layer`. This is done by
+emitting paired display items to adjust the `PaintChunk`s property tree state
+to the current state when the `PaintChunk` is consumed. See:
+[`ConversionContext::Convert`](compositing/PaintChunksToCcLayer.cpp).
+
+### Foreign layers
+
+Some `PaintChunk` content requires a foreign layer, meaning a layer that is
+managed outside of the scope of this code. Examples are composited video, a
+and 2D/3D (WebGL) canvas.
+
+### Raster invalidations
+
+Any raster invalidates on a `PaintChunk` are also mapped to the space of the
+backing `cc::Layer`.
diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositing/chunk_to_layer_mapper.cc b/chromium/third_party/blink/renderer/platform/graphics/compositing/chunk_to_layer_mapper.cc
new file mode 100644
index 00000000000..92f49af1a99
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/compositing/chunk_to_layer_mapper.cc
@@ -0,0 +1,120 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/compositing/chunk_to_layer_mapper.h"
+
+#include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_chunk.h"
+
+namespace blink {
+
+void ChunkToLayerMapper::SwitchToChunk(const PaintChunk& chunk) {
+ outset_for_raster_effects_ = chunk.outset_for_raster_effects;
+
+ const auto& new_chunk_state =
+ chunk.properties.property_tree_state.GetPropertyTreeState();
+ if (new_chunk_state == chunk_state_)
+ return;
+
+ if (new_chunk_state == layer_state_) {
+ has_filter_that_moves_pixels_ = false;
+ transform_ = TransformationMatrix().Translate(-layer_offset_.x(),
+ -layer_offset_.y());
+ clip_rect_ = FloatClipRect();
+ chunk_state_ = new_chunk_state;
+ return;
+ }
+
+ if (new_chunk_state.Transform() != chunk_state_.Transform()) {
+ transform_ = GeometryMapper::SourceToDestinationProjection(
+ new_chunk_state.Transform(), layer_state_.Transform());
+ transform_.PostTranslate(-layer_offset_.x(), -layer_offset_.y());
+ }
+
+ bool new_has_filter_that_moves_pixels = has_filter_that_moves_pixels_;
+ if (new_chunk_state.Effect() != chunk_state_.Effect()) {
+ new_has_filter_that_moves_pixels = false;
+ for (const auto* effect = new_chunk_state.Effect();
+ effect && effect != layer_state_.Effect(); effect = effect->Parent()) {
+ if (effect->HasFilterThatMovesPixels()) {
+ new_has_filter_that_moves_pixels = true;
+ break;
+ }
+ }
+ }
+
+ bool needs_clip_recalculation =
+ new_has_filter_that_moves_pixels != has_filter_that_moves_pixels_ ||
+ new_chunk_state.Clip() != chunk_state_.Clip();
+ if (needs_clip_recalculation) {
+ clip_rect_ =
+ GeometryMapper::LocalToAncestorClipRect(new_chunk_state, layer_state_);
+ if (!clip_rect_.IsInfinite())
+ clip_rect_.MoveBy(FloatPoint(-layer_offset_.x(), -layer_offset_.y()));
+ }
+
+ chunk_state_ = new_chunk_state;
+ has_filter_that_moves_pixels_ = new_has_filter_that_moves_pixels;
+}
+
+IntRect ChunkToLayerMapper::MapVisualRect(const FloatRect& rect) const {
+ if (rect.IsEmpty())
+ return IntRect();
+
+ if (UNLIKELY(has_filter_that_moves_pixels_))
+ return MapUsingGeometryMapper(rect);
+
+ FloatRect mapped_rect = transform_.MapRect(rect);
+ if (!mapped_rect.IsEmpty() && !clip_rect_.IsInfinite())
+ mapped_rect.Intersect(clip_rect_.Rect());
+
+ if (mapped_rect.IsEmpty()) {
+ DCHECK(MapUsingGeometryMapper(rect).IsEmpty());
+ return IntRect();
+ }
+
+ mapped_rect.Inflate(outset_for_raster_effects_);
+ AdjustVisualRectBySubpixelOffset(mapped_rect);
+
+ auto result = EnclosingIntRect(mapped_rect);
+#if DCHECK_IS_ON()
+ auto slow_result = MapUsingGeometryMapper(rect);
+ if (result != slow_result) {
+ // Not a DCHECK because this may result from a floating point error.
+ LOG(WARNING) << "ChunkToLayerMapper::MapVisualRect: Different results from"
+ << "fast path (" << result << ") and slow path ("
+ << slow_result << ")";
+ }
+#endif
+ return result;
+}
+
+// This is called when the fast path doesn't apply if there is any filter that
+// moves pixels. GeometryMapper::LocalToAncestorVisualRect() will apply the
+// visual effects of the filters, though slowly.
+IntRect ChunkToLayerMapper::MapUsingGeometryMapper(
+ const FloatRect& rect) const {
+ FloatClipRect visual_rect(rect);
+ GeometryMapper::LocalToAncestorVisualRect(chunk_state_, layer_state_,
+ visual_rect);
+ if (visual_rect.Rect().IsEmpty())
+ return IntRect();
+
+ visual_rect.Rect().Move(-layer_offset_.x(), -layer_offset_.y());
+ visual_rect.Rect().Inflate(outset_for_raster_effects_);
+ AdjustVisualRectBySubpixelOffset(visual_rect.Rect());
+ return EnclosingIntRect(visual_rect.Rect());
+}
+
+void ChunkToLayerMapper::AdjustVisualRectBySubpixelOffset(
+ FloatRect& rect) const {
+ // Add back the layer's subpixel accumulation that was excluded from the
+ // visual rect by
+ // PaintInvalidator::ExcludeCompositedLayerSubpixelAccumulation().
+ // The condition below should be kept consistent with that function.
+ if (chunk_state_.Transform() == layer_state_.Transform())
+ rect.Move(visual_rect_subpixel_offset_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositing/chunk_to_layer_mapper.h b/chromium/third_party/blink/renderer/platform/graphics/compositing/chunk_to_layer_mapper.h
new file mode 100644
index 00000000000..dc83ce0fd81
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/compositing/chunk_to_layer_mapper.h
@@ -0,0 +1,68 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITING_CHUNK_TO_LAYER_MAPPER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITING_CHUNK_TO_LAYER_MAPPER_H_
+
+#include "third_party/blink/renderer/platform/graphics/paint/float_clip_rect.h"
+#include "third_party/blink/renderer/platform/graphics/paint/property_tree_state.h"
+
+namespace blink {
+
+struct PaintChunk;
+
+// Maps geometry from PaintChunks to the containing composited layer.
+// It provides higher performance than GeometryMapper by reusing computed
+// transforms and clips for unchanged states within or across paint chunks.
+class PLATFORM_EXPORT ChunkToLayerMapper {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ ChunkToLayerMapper(const PropertyTreeState& layer_state,
+ const gfx::Vector2dF& layer_offset,
+ const FloatSize& visual_rect_subpixel_offset = FloatSize())
+ : layer_state_(layer_state),
+ layer_offset_(layer_offset),
+ visual_rect_subpixel_offset_(visual_rect_subpixel_offset),
+ chunk_state_(nullptr, nullptr, nullptr) {}
+
+ // This class can map from multiple chunks. Before mapping from a chunk, this
+ // method must be called to prepare for the chunk.
+ void SwitchToChunk(const PaintChunk&);
+
+ // Maps a visual rectangle in the current chunk space into the layer space.
+ IntRect MapVisualRect(const FloatRect&) const;
+
+ // Returns the combined transform from the current chunk to the layer.
+ const TransformationMatrix& Transform() const { return transform_; }
+
+ // Returns the combined clip from the current chunk to the layer if it can
+ // be calculated (there is no filter that moves pixels), or infinite loose
+ // clip rect otherwise.
+ const FloatClipRect& ClipRect() const { return clip_rect_; }
+
+ private:
+ friend class ChunkToLayerMapperTest;
+
+ IntRect MapUsingGeometryMapper(const FloatRect&) const;
+ void AdjustVisualRectBySubpixelOffset(FloatRect&) const;
+
+ const PropertyTreeState layer_state_;
+ const gfx::Vector2dF layer_offset_;
+ const FloatSize visual_rect_subpixel_offset_;
+
+ // The following fields are chunk-specific which are updated in
+ // SwitchToChunk().
+ PropertyTreeState chunk_state_;
+ float outset_for_raster_effects_ = 0.f;
+ TransformationMatrix transform_;
+ FloatClipRect clip_rect_;
+ // True if there is any pixel-moving filter between chunk state and layer
+ // state, and we will call GeometryMapper for each mapping.
+ bool has_filter_that_moves_pixels_ = false;
+};
+
+} // namespace blink
+
+#endif // PaintArtifactCompositor_h
diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositing/chunk_to_layer_mapper_test.cc b/chromium/third_party/blink/renderer/platform/graphics/compositing/chunk_to_layer_mapper_test.cc
new file mode 100644
index 00000000000..4e92ff1b554
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/compositing/chunk_to_layer_mapper_test.cc
@@ -0,0 +1,234 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/compositing/chunk_to_layer_mapper.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/graphics/paint/display_item.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_chunk.h"
+#include "third_party/blink/renderer/platform/testing/fake_display_item_client.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+
+namespace blink {
+
+class ChunkToLayerMapperTest : public testing::Test {
+ protected:
+ static PaintChunk Chunk(const PropertyTreeState& state) {
+ DEFINE_STATIC_LOCAL(FakeDisplayItemClient, fake_client, ());
+ DEFINE_STATIC_LOCAL(
+ Optional<PaintChunk::Id>, id,
+ (PaintChunk::Id(fake_client, DisplayItem::kDrawingFirst)));
+ PaintChunk chunk(0, 0, *id, PaintChunkProperties(state));
+ return chunk;
+ }
+
+ // A state containing arbitrary values which should not affect test results
+ // if the state is used as a layer state.
+ PropertyTreeState LayerState() {
+ DEFINE_STATIC_REF(
+ TransformPaintPropertyNode, transform,
+ TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(),
+ TransformationMatrix().Translate(123, 456), FloatPoint3D(1, 2, 3)));
+ DEFINE_STATIC_REF(
+ ClipPaintPropertyNode, clip,
+ ClipPaintPropertyNode::Create(ClipPaintPropertyNode::Root(), transform,
+ FloatRoundedRect(12, 34, 56, 78)));
+ DEFINE_STATIC_REF(
+ EffectPaintPropertyNode, effect,
+ EffectPaintPropertyNode::Create(
+ EffectPaintPropertyNode::Root(), transform, clip,
+ kColorFilterLuminanceToAlpha, CompositorFilterOperations(), 0.789f,
+ SkBlendMode::kSrcIn));
+ return PropertyTreeState(transform, clip, effect);
+ }
+
+ bool HasFilterThatMovesPixels(const ChunkToLayerMapper& mapper) {
+ return mapper.has_filter_that_moves_pixels_;
+ }
+};
+
+TEST_F(ChunkToLayerMapperTest, OneChunkUsingLayerState) {
+ ChunkToLayerMapper mapper(LayerState(), gfx::Vector2dF(10, 20));
+ auto chunk = Chunk(LayerState());
+ mapper.SwitchToChunk(chunk);
+ EXPECT_FALSE(HasFilterThatMovesPixels(mapper));
+ EXPECT_EQ(TransformationMatrix().Translate(-10, -20), mapper.Transform());
+ EXPECT_EQ(FloatClipRect(), mapper.ClipRect());
+ EXPECT_EQ(IntRect(20, 10, 88, 99),
+ mapper.MapVisualRect(FloatRect(30, 30, 88, 99)));
+ EXPECT_EQ(IntRect(20, 10, 88, 99),
+ mapper.MapVisualRect(FloatRect(30.2f, 30.7f, 87.3f, 98.1f)));
+ EXPECT_EQ(IntRect(), mapper.MapVisualRect(FloatRect()));
+}
+
+TEST_F(ChunkToLayerMapperTest, TwoChunkUsingLayerState) {
+ ChunkToLayerMapper mapper(LayerState(), gfx::Vector2dF(10, 20));
+ auto chunk1 = Chunk(LayerState());
+ auto chunk2 = Chunk(LayerState());
+
+ mapper.SwitchToChunk(chunk1);
+ EXPECT_FALSE(HasFilterThatMovesPixels(mapper));
+ EXPECT_EQ(TransformationMatrix().Translate(-10, -20), mapper.Transform());
+ EXPECT_EQ(FloatClipRect(), mapper.ClipRect());
+ EXPECT_EQ(IntRect(20, 10, 88, 99),
+ mapper.MapVisualRect(FloatRect(30, 30, 88, 99)));
+ EXPECT_EQ(IntRect(20, 10, 88, 99),
+ mapper.MapVisualRect(FloatRect(30.2f, 30.7f, 87.3f, 98.1f)));
+ EXPECT_EQ(IntRect(), mapper.MapVisualRect(FloatRect()));
+
+ mapper.SwitchToChunk(chunk2);
+ EXPECT_FALSE(HasFilterThatMovesPixels(mapper));
+ EXPECT_EQ(TransformationMatrix().Translate(-10, -20), mapper.Transform());
+ EXPECT_EQ(FloatClipRect(), mapper.ClipRect());
+ EXPECT_EQ(IntRect(20, 10, 88, 99),
+ mapper.MapVisualRect(FloatRect(30, 30, 88, 99)));
+ EXPECT_EQ(IntRect(20, 10, 88, 99),
+ mapper.MapVisualRect(FloatRect(30.2f, 30.7f, 87.3f, 98.1f)));
+ EXPECT_EQ(IntRect(), mapper.MapVisualRect(FloatRect()));
+}
+
+TEST_F(ChunkToLayerMapperTest, TwoChunkSameState) {
+ ChunkToLayerMapper mapper(LayerState(), gfx::Vector2dF(10, 20));
+ auto transform = TransformPaintPropertyNode::Create(
+ LayerState().Transform(), TransformationMatrix().Scale(2),
+ FloatPoint3D());
+ auto clip = ClipPaintPropertyNode::Create(LayerState().Clip(),
+ LayerState().Transform(),
+ FloatRoundedRect(10, 10, 100, 100));
+ auto effect = LayerState().Effect();
+ auto chunk1 = Chunk(PropertyTreeState(transform.get(), clip.get(), effect));
+ auto chunk2 = Chunk(PropertyTreeState(transform.get(), clip.get(), effect));
+
+ mapper.SwitchToChunk(chunk1);
+ EXPECT_FALSE(HasFilterThatMovesPixels(mapper));
+ EXPECT_EQ(TransformationMatrix().Translate(-10, -20).Scale(2),
+ mapper.Transform());
+ EXPECT_EQ(FloatRect(0, -10, 100, 100), mapper.ClipRect().Rect());
+ EXPECT_TRUE(mapper.ClipRect().IsTight());
+ EXPECT_EQ(IntRect(50, 40, 50, 50),
+ mapper.MapVisualRect(FloatRect(30, 30, 88, 99)));
+ EXPECT_EQ(IntRect(), mapper.MapVisualRect(FloatRect()));
+
+ mapper.SwitchToChunk(chunk2);
+ EXPECT_FALSE(HasFilterThatMovesPixels(mapper));
+ EXPECT_EQ(TransformationMatrix().Translate(-10, -20).Scale(2),
+ mapper.Transform());
+ EXPECT_EQ(FloatRect(0, -10, 100, 100), mapper.ClipRect().Rect());
+ EXPECT_TRUE(mapper.ClipRect().IsTight());
+ EXPECT_EQ(IntRect(50, 40, 50, 50),
+ mapper.MapVisualRect(FloatRect(30, 30, 88, 99)));
+ EXPECT_EQ(IntRect(), mapper.MapVisualRect(FloatRect()));
+}
+
+TEST_F(ChunkToLayerMapperTest, TwoChunkDifferentState) {
+ ChunkToLayerMapper mapper(LayerState(), gfx::Vector2dF(10, 20));
+ auto transform1 = TransformPaintPropertyNode::Create(
+ LayerState().Transform(), TransformationMatrix().Scale(2),
+ FloatPoint3D());
+ auto clip1 = ClipPaintPropertyNode::Create(
+ LayerState().Clip(), LayerState().Transform(),
+ FloatRoundedRect(10, 10, 100, 100));
+ auto effect = LayerState().Effect();
+ auto chunk1 = Chunk(PropertyTreeState(transform1.get(), clip1.get(), effect));
+
+ auto transform2 = TransformPaintPropertyNode::Create(
+ transform1, TransformationMatrix().Translate(20, 30), FloatPoint3D());
+ auto clip2 = ClipPaintPropertyNode::Create(LayerState().Clip(), transform2,
+ FloatRoundedRect(0, 0, 20, 20));
+ auto chunk2 = Chunk(PropertyTreeState(transform2.get(), clip2.get(), effect));
+
+ mapper.SwitchToChunk(chunk1);
+ EXPECT_FALSE(HasFilterThatMovesPixels(mapper));
+ EXPECT_EQ(TransformationMatrix().Translate(-10, -20).Scale(2),
+ mapper.Transform());
+ EXPECT_EQ(FloatRect(0, -10, 100, 100), mapper.ClipRect().Rect());
+ EXPECT_TRUE(mapper.ClipRect().IsTight());
+ EXPECT_EQ(IntRect(50, 40, 50, 50),
+ mapper.MapVisualRect(FloatRect(30, 30, 88, 99)));
+ EXPECT_EQ(IntRect(), mapper.MapVisualRect(FloatRect()));
+
+ mapper.SwitchToChunk(chunk2);
+ EXPECT_FALSE(HasFilterThatMovesPixels(mapper));
+ EXPECT_EQ(
+ TransformationMatrix().Translate(-10, -20).Scale(2).Translate(20, 30),
+ mapper.Transform());
+ EXPECT_EQ(FloatRect(30, 40, 40, 40), mapper.ClipRect().Rect());
+ EXPECT_FALSE(mapper.ClipRect().IsTight());
+ EXPECT_EQ(IntRect(30, 40, 40, 40),
+ mapper.MapVisualRect(FloatRect(0, 0, 200, 200)));
+ EXPECT_EQ(IntRect(), mapper.MapVisualRect(FloatRect()));
+}
+
+TEST_F(ChunkToLayerMapperTest, SlowPath) {
+ ChunkToLayerMapper mapper(LayerState(), gfx::Vector2dF(10, 20));
+ auto chunk1 = Chunk(LayerState());
+
+ // Chunk2 has a blur filter. Should use the slow path.
+ CompositorFilterOperations filter2;
+ filter2.AppendBlurFilter(20);
+ auto effect2 = EffectPaintPropertyNode::Create(
+ EffectPaintPropertyNode::Root(), LayerState().Transform(),
+ LayerState().Clip(), kColorFilterNone, filter2, 1.f, SkBlendMode::kDstIn);
+ auto chunk2 = Chunk(PropertyTreeState(LayerState().Transform(),
+ LayerState().Clip(), effect2.get()));
+
+ // Chunk3 has a different effect which inherits from chunk2's effect.
+ // Should use the slow path.
+ auto effect3 = EffectPaintPropertyNode::Create(
+ effect2.get(), LayerState().Transform(), LayerState().Clip(),
+ kColorFilterNone, CompositorFilterOperations(), 1.f, SkBlendMode::kDstIn);
+ auto chunk3 = Chunk(PropertyTreeState(LayerState().Transform(),
+ LayerState().Clip(), effect3.get()));
+
+ // Chunk4 has an opacity filter effect which inherits from the layer's effect.
+ // Should use the fast path.
+ CompositorFilterOperations filter4;
+ filter4.AppendOpacityFilter(0.5);
+ auto effect4 = EffectPaintPropertyNode::Create(
+ LayerState().Effect(), LayerState().Transform(), LayerState().Clip(),
+ kColorFilterNone, CompositorFilterOperations(), 1.f, SkBlendMode::kDstIn);
+ auto chunk4 = Chunk(PropertyTreeState(LayerState().Transform(),
+ LayerState().Clip(), effect4.get()));
+
+ // Chunk5 goes back to the layer state.
+ auto chunk5 = Chunk(LayerState());
+
+ mapper.SwitchToChunk(chunk1);
+ EXPECT_FALSE(HasFilterThatMovesPixels(mapper));
+ EXPECT_EQ(TransformationMatrix().Translate(-10, -20), mapper.Transform());
+ EXPECT_EQ(FloatClipRect(), mapper.ClipRect());
+
+ mapper.SwitchToChunk(chunk2);
+ EXPECT_TRUE(HasFilterThatMovesPixels(mapper));
+ EXPECT_EQ(TransformationMatrix().Translate(-10, -20), mapper.Transform());
+ EXPECT_TRUE(mapper.ClipRect().IsInfinite());
+ EXPECT_EQ(IntRect(-40, -50, 208, 219),
+ mapper.MapVisualRect(FloatRect(30, 30, 88, 99)));
+ EXPECT_EQ(IntRect(-40, -50, 208, 219),
+ mapper.MapVisualRect(FloatRect(30.2f, 30.7f, 87.3f, 98.1f)));
+ EXPECT_EQ(IntRect(), mapper.MapVisualRect(FloatRect()));
+
+ mapper.SwitchToChunk(chunk3);
+ EXPECT_TRUE(HasFilterThatMovesPixels(mapper));
+ EXPECT_EQ(TransformationMatrix().Translate(-10, -20), mapper.Transform());
+ EXPECT_TRUE(mapper.ClipRect().IsInfinite());
+ EXPECT_EQ(IntRect(-40, -50, 208, 219),
+ mapper.MapVisualRect(FloatRect(30, 30, 88, 99)));
+ EXPECT_EQ(IntRect(-40, -50, 208, 219),
+ mapper.MapVisualRect(FloatRect(30.2f, 30.7f, 87.3f, 98.1f)));
+ EXPECT_EQ(IntRect(), mapper.MapVisualRect(FloatRect()));
+
+ mapper.SwitchToChunk(chunk4);
+ EXPECT_FALSE(HasFilterThatMovesPixels(mapper));
+ EXPECT_EQ(TransformationMatrix().Translate(-10, -20), mapper.Transform());
+ EXPECT_EQ(FloatClipRect(), mapper.ClipRect());
+
+ mapper.SwitchToChunk(chunk5);
+ EXPECT_FALSE(HasFilterThatMovesPixels(mapper));
+ EXPECT_EQ(TransformationMatrix().Translate(-10, -20), mapper.Transform());
+ EXPECT_EQ(FloatClipRect(), mapper.ClipRect());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositing/composited_layer_raster_invalidator.cc b/chromium/third_party/blink/renderer/platform/graphics/compositing/composited_layer_raster_invalidator.cc
new file mode 100644
index 00000000000..33942364fc2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/compositing/composited_layer_raster_invalidator.cc
@@ -0,0 +1,337 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/compositing/composited_layer_raster_invalidator.h"
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+#include "third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.h"
+#include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_chunk_subset.h"
+
+namespace blink {
+
+void CompositedLayerRasterInvalidator::SetTracksRasterInvalidations(
+ bool should_track) {
+ if (should_track) {
+ if (!tracking_info_)
+ tracking_info_ = std::make_unique<RasterInvalidationTrackingInfo>();
+ tracking_info_->tracking.ClearInvalidations();
+ for (const auto& info : paint_chunks_info_) {
+ tracking_info_->old_client_debug_names.Set(&info.id.client,
+ info.id.client.DebugName());
+ }
+ } else if (!RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) {
+ tracking_info_ = nullptr;
+ } else if (tracking_info_) {
+ tracking_info_->tracking.ClearInvalidations();
+ }
+}
+
+size_t CompositedLayerRasterInvalidator::MatchNewChunkToOldChunk(
+ const PaintChunk& new_chunk,
+ size_t old_index) {
+ for (size_t i = old_index; i < paint_chunks_info_.size(); i++) {
+ if (paint_chunks_info_[i].Matches(new_chunk))
+ return i;
+ }
+ for (size_t i = 0; i < old_index; i++) {
+ if (paint_chunks_info_[i].Matches(new_chunk))
+ return i;
+ }
+ return kNotFound;
+}
+
+static bool ApproximatelyEqual(const SkMatrix& a, const SkMatrix& b) {
+ static constexpr float kTolerance = 1e-5f;
+ for (int i = 0; i < 9; i++) {
+ if (std::abs(a[i] - b[i]) > kTolerance)
+ return false;
+ }
+ return true;
+}
+
+PaintInvalidationReason
+CompositedLayerRasterInvalidator::ChunkPropertiesChanged(
+ const PaintChunkInfo& new_chunk,
+ const PaintChunkInfo& old_chunk,
+ const PropertyTreeState& layer_state) const {
+ if (new_chunk.properties.backface_hidden !=
+ old_chunk.properties.backface_hidden)
+ return PaintInvalidationReason::kPaintProperty;
+
+ // Special case for transform changes because we may create or delete some
+ // transform nodes when no raster invalidation is needed. For example, when
+ // a composited layer previously not transformed now gets transformed.
+ // Check for real accumulated transform change instead.
+ if (!ApproximatelyEqual(new_chunk.chunk_to_layer_transform,
+ old_chunk.chunk_to_layer_transform))
+ return PaintInvalidationReason::kPaintProperty;
+
+ // Treat the chunk property as changed if the effect node pointer is
+ // different, or the effect node's value changed between the layer state and
+ // the chunk state.
+ const auto& new_chunk_state = new_chunk.properties.property_tree_state;
+ const auto& old_chunk_state = old_chunk.properties.property_tree_state;
+ if (new_chunk_state.Effect() != old_chunk_state.Effect() ||
+ new_chunk_state.Effect()->Changed(*layer_state.Effect()))
+ return PaintInvalidationReason::kPaintProperty;
+
+ // Check for accumulated clip rect change, if the clip rects are tight.
+ if (new_chunk.chunk_to_layer_clip.IsTight() &&
+ old_chunk.chunk_to_layer_clip.IsTight()) {
+ if (new_chunk.chunk_to_layer_clip == old_chunk.chunk_to_layer_clip)
+ return PaintInvalidationReason::kNone;
+ // Ignore differences out of the current layer bounds.
+ if (ClipByLayerBounds(new_chunk.chunk_to_layer_clip.Rect()) ==
+ ClipByLayerBounds(old_chunk.chunk_to_layer_clip.Rect()))
+ return PaintInvalidationReason::kNone;
+ return PaintInvalidationReason::kIncremental;
+ }
+
+ // Otherwise treat the chunk property as changed if the clip node pointer is
+ // different, or the clip node's value changed between the layer state and the
+ // chunk state.
+ if (new_chunk_state.Clip() != old_chunk_state.Clip() ||
+ new_chunk_state.Clip()->Changed(*layer_state.Clip()))
+ return PaintInvalidationReason::kPaintProperty;
+
+ return PaintInvalidationReason::kNone;
+}
+
+// Generates raster invalidations by checking changes (appearing, disappearing,
+// reordering, property changes) of chunks. The logic is similar to
+// PaintController::GenerateRasterInvalidations(). The complexity is between
+// O(n) and O(m*n) where m and n are the numbers of old and new chunks,
+// respectively. Normally both m and n are small numbers. The best caseis that
+// all old chunks have matching new chunks in the same order. The worst case is
+// that no matching chunks except the first one (which always matches otherwise
+// we won't reuse the CompositedLayerRasterInvalidator), which is rare. In
+// common cases that most of the chunks can be matched in-order, the complexity
+// is slightly larger than O(n).
+void CompositedLayerRasterInvalidator::GenerateRasterInvalidations(
+ const PaintChunkSubset& new_chunks,
+ const PropertyTreeState& layer_state,
+ const FloatSize& visual_rect_subpixel_offset,
+ Vector<PaintChunkInfo>& new_chunks_info) {
+ ChunkToLayerMapper mapper(layer_state, layer_bounds_.OffsetFromOrigin(),
+ visual_rect_subpixel_offset);
+ Vector<bool> old_chunks_matched;
+ old_chunks_matched.resize(paint_chunks_info_.size());
+ size_t old_index = 0;
+ size_t max_matched_old_index = 0;
+ for (const auto& new_chunk : new_chunks) {
+ mapper.SwitchToChunk(new_chunk);
+ auto& new_chunk_info =
+ new_chunks_info.emplace_back(*this, mapper, new_chunk);
+
+ if (!new_chunk.is_cacheable) {
+ FullyInvalidateNewChunk(new_chunk_info,
+ PaintInvalidationReason::kChunkUncacheable);
+ continue;
+ }
+
+ size_t matched_old_index = MatchNewChunkToOldChunk(new_chunk, old_index);
+ if (matched_old_index == kNotFound) {
+ // The new chunk doesn't match any old chunk.
+ FullyInvalidateNewChunk(new_chunk_info,
+ PaintInvalidationReason::kAppeared);
+ continue;
+ }
+
+ DCHECK(!old_chunks_matched[matched_old_index]);
+ old_chunks_matched[matched_old_index] = true;
+
+ auto& old_chunk_info = paint_chunks_info_[matched_old_index];
+ // Clip the old chunk bounds by the new layer bounds.
+ old_chunk_info.bounds_in_layer =
+ ClipByLayerBounds(old_chunk_info.bounds_in_layer);
+
+ PaintInvalidationReason reason =
+ matched_old_index < max_matched_old_index
+ ? PaintInvalidationReason::kChunkReordered
+ : ChunkPropertiesChanged(new_chunk_info, old_chunk_info,
+ layer_state);
+
+ if (IsFullPaintInvalidationReason(reason)) {
+ // Invalidate both old and new bounds of the chunk if the chunk's paint
+ // properties changed, or is moved backward and may expose area that was
+ // previously covered by it.
+ FullyInvalidateChunk(old_chunk_info, new_chunk_info, reason);
+ // Ignore the display item raster invalidations because we have fully
+ // invalidated the chunk.
+ } else {
+ // We may have ignored tiny changes of transform, in which case we should
+ // use the old chunk_to_layer_transform for later comparison to correctly
+ // invalidate animating transform in tiny increments when the accumulated
+ // change exceeds the tolerance.
+ new_chunk_info.chunk_to_layer_transform =
+ old_chunk_info.chunk_to_layer_transform;
+
+ if (reason == PaintInvalidationReason::kIncremental)
+ IncrementallyInvalidateChunk(old_chunk_info, new_chunk_info);
+
+ // Add the raster invalidations found by PaintController within the chunk.
+ AddDisplayItemRasterInvalidations(new_chunk, mapper);
+ }
+
+ old_index = matched_old_index + 1;
+ if (old_index == paint_chunks_info_.size())
+ old_index = 0;
+ max_matched_old_index = std::max(max_matched_old_index, matched_old_index);
+ }
+
+ // Invalidate remaining unmatched (disappeared or uncacheable) old chunks.
+ for (size_t i = 0; i < paint_chunks_info_.size(); ++i) {
+ if (old_chunks_matched[i])
+ continue;
+ FullyInvalidateOldChunk(paint_chunks_info_[i],
+ paint_chunks_info_[i].is_cacheable
+ ? PaintInvalidationReason::kDisappeared
+ : PaintInvalidationReason::kChunkUncacheable);
+ }
+}
+
+void CompositedLayerRasterInvalidator::AddDisplayItemRasterInvalidations(
+ const PaintChunk& chunk,
+ const ChunkToLayerMapper& mapper) {
+ DCHECK(chunk.raster_invalidation_tracking.IsEmpty() ||
+ chunk.raster_invalidation_rects.size() ==
+ chunk.raster_invalidation_tracking.size());
+
+ if (chunk.raster_invalidation_rects.IsEmpty())
+ return;
+
+ for (size_t i = 0; i < chunk.raster_invalidation_rects.size(); ++i) {
+ auto rect = ClipByLayerBounds(
+ mapper.MapVisualRect(chunk.raster_invalidation_rects[i]));
+ if (rect.IsEmpty())
+ continue;
+ raster_invalidation_function_(rect);
+
+ if (!chunk.raster_invalidation_tracking.IsEmpty()) {
+ const auto& info = chunk.raster_invalidation_tracking[i];
+ tracking_info_->tracking.AddInvalidation(
+ info.client, info.client_debug_name, rect, info.reason);
+ }
+ }
+}
+
+void CompositedLayerRasterInvalidator::IncrementallyInvalidateChunk(
+ const PaintChunkInfo& old_chunk,
+ const PaintChunkInfo& new_chunk) {
+ SkRegion diff(old_chunk.bounds_in_layer);
+ diff.op(new_chunk.bounds_in_layer, SkRegion::kXOR_Op);
+ for (SkRegion::Iterator it(diff); !it.done(); it.next()) {
+ const SkIRect& r = it.rect();
+ AddRasterInvalidation(IntRect(r.x(), r.y(), r.width(), r.height()),
+ &new_chunk.id.client,
+ PaintInvalidationReason::kIncremental);
+ }
+}
+
+void CompositedLayerRasterInvalidator::FullyInvalidateChunk(
+ const PaintChunkInfo& old_chunk,
+ const PaintChunkInfo& new_chunk,
+ PaintInvalidationReason reason) {
+ FullyInvalidateOldChunk(old_chunk, reason);
+ if (old_chunk.bounds_in_layer != new_chunk.bounds_in_layer)
+ FullyInvalidateNewChunk(new_chunk, reason);
+}
+
+void CompositedLayerRasterInvalidator::FullyInvalidateNewChunk(
+ const PaintChunkInfo& info,
+ PaintInvalidationReason reason) {
+ AddRasterInvalidation(info.bounds_in_layer, &info.id.client, reason);
+}
+
+void CompositedLayerRasterInvalidator::FullyInvalidateOldChunk(
+ const PaintChunkInfo& info,
+ PaintInvalidationReason reason) {
+ String debug_name;
+ if (tracking_info_)
+ debug_name = tracking_info_->old_client_debug_names.at(&info.id.client);
+ AddRasterInvalidation(info.bounds_in_layer, &info.id.client, reason,
+ &debug_name);
+}
+
+void CompositedLayerRasterInvalidator::AddRasterInvalidation(
+ const IntRect& rect,
+ const DisplayItemClient* client,
+ PaintInvalidationReason reason,
+ const String* debug_name) {
+ raster_invalidation_function_(rect);
+ if (tracking_info_) {
+ tracking_info_->tracking.AddInvalidation(
+ client, debug_name ? *debug_name : client->DebugName(), rect, reason);
+ }
+}
+
+RasterInvalidationTracking& CompositedLayerRasterInvalidator::EnsureTracking() {
+ if (!tracking_info_)
+ tracking_info_ = std::make_unique<RasterInvalidationTrackingInfo>();
+ return tracking_info_->tracking;
+}
+
+void CompositedLayerRasterInvalidator::Generate(
+ const gfx::Rect& layer_bounds,
+ const PaintChunkSubset& paint_chunks,
+ const PropertyTreeState& layer_state,
+ const FloatSize& visual_rect_subpixel_offset) {
+ if (RuntimeEnabledFeatures::DisableRasterInvalidationEnabled())
+ return;
+
+ if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled())
+ EnsureTracking();
+
+ if (tracking_info_) {
+ for (const auto& chunk : paint_chunks) {
+ tracking_info_->new_client_debug_names.insert(
+ &chunk.id.client, chunk.id.client.DebugName());
+ }
+ }
+
+ bool layer_bounds_was_empty = layer_bounds_.IsEmpty();
+ layer_bounds_ = layer_bounds;
+
+ Vector<PaintChunkInfo> new_chunks_info;
+ new_chunks_info.ReserveCapacity(paint_chunks.size());
+
+ if (layer_bounds_was_empty || layer_bounds_.IsEmpty()) {
+ // No raster invalidation is needed if either the old bounds or the new
+ // bounds is empty, but we still need to update new_chunks_info for the
+ // next cycle.
+ ChunkToLayerMapper mapper(layer_state, layer_bounds.OffsetFromOrigin(),
+ visual_rect_subpixel_offset);
+ for (const auto& chunk : paint_chunks) {
+ mapper.SwitchToChunk(chunk);
+ new_chunks_info.emplace_back(*this, mapper, chunk);
+ }
+ } else {
+ GenerateRasterInvalidations(paint_chunks, layer_state,
+ visual_rect_subpixel_offset, new_chunks_info);
+ }
+
+ paint_chunks_info_ = std::move(new_chunks_info);
+
+ if (tracking_info_) {
+ tracking_info_->old_client_debug_names =
+ std::move(tracking_info_->new_client_debug_names);
+ }
+
+ for (const auto& chunk : paint_chunks) {
+ chunk.client_is_just_created = false;
+ chunk.raster_invalidation_rects.clear();
+ chunk.raster_invalidation_tracking.clear();
+ }
+}
+
+size_t CompositedLayerRasterInvalidator::ApproximateUnsharedMemoryUsage()
+ const {
+ return sizeof(*this) + paint_chunks_info_.capacity() * sizeof(PaintChunkInfo);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositing/composited_layer_raster_invalidator.h b/chromium/third_party/blink/renderer/platform/graphics/compositing/composited_layer_raster_invalidator.h
new file mode 100644
index 00000000000..29f8f6ae698
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/compositing/composited_layer_raster_invalidator.h
@@ -0,0 +1,124 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITING_COMPOSITED_LAYER_RASTER_INVALIDATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITING_COMPOSITED_LAYER_RASTER_INVALIDATOR_H_
+
+#include "third_party/blink/renderer/platform/graphics/compositing/chunk_to_layer_mapper.h"
+#include "third_party/blink/renderer/platform/graphics/paint/float_clip_rect.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_chunk.h"
+#include "third_party/blink/renderer/platform/graphics/paint/raster_invalidation_tracking.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class PaintChunkSubset;
+class IntRect;
+
+class PLATFORM_EXPORT CompositedLayerRasterInvalidator {
+ public:
+ using RasterInvalidationFunction = std::function<void(const IntRect&)>;
+
+ CompositedLayerRasterInvalidator(
+ RasterInvalidationFunction raster_invalidation_function)
+ : raster_invalidation_function_(raster_invalidation_function) {}
+
+ void SetTracksRasterInvalidations(bool);
+ RasterInvalidationTracking* GetTracking() const {
+ return tracking_info_ ? &tracking_info_->tracking : nullptr;
+ }
+
+ RasterInvalidationTracking& EnsureTracking();
+
+ void Generate(const gfx::Rect& layer_bounds,
+ const PaintChunkSubset&,
+ const PropertyTreeState&,
+ const FloatSize& visual_rect_subpixel_offset = FloatSize());
+
+ bool Matches(const PaintChunk& paint_chunk) const {
+ return paint_chunks_info_.size() && paint_chunks_info_[0].is_cacheable &&
+ paint_chunk.Matches(paint_chunks_info_[0].id);
+ }
+
+ const gfx::Rect& LayerBounds() const { return layer_bounds_; }
+
+ size_t ApproximateUnsharedMemoryUsage() const;
+
+ private:
+ friend class CompositedLayerRasterInvalidatorTest;
+
+ struct PaintChunkInfo {
+ PaintChunkInfo(const CompositedLayerRasterInvalidator& invalidator,
+ const ChunkToLayerMapper& mapper,
+ const PaintChunk& chunk)
+ : id(chunk.id),
+ properties(chunk.properties),
+ is_cacheable(chunk.is_cacheable),
+ bounds_in_layer(invalidator.ClipByLayerBounds(
+ mapper.MapVisualRect(chunk.bounds))),
+ chunk_to_layer_clip(mapper.ClipRect()),
+ chunk_to_layer_transform(
+ TransformationMatrix::ToSkMatrix44(mapper.Transform())) {}
+
+ bool Matches(const PaintChunk& new_chunk) const {
+ return is_cacheable && new_chunk.Matches(id);
+ }
+
+ PaintChunk::Id id;
+ PaintChunkProperties properties;
+ bool is_cacheable;
+ IntRect bounds_in_layer;
+ FloatClipRect chunk_to_layer_clip;
+ SkMatrix chunk_to_layer_transform;
+ };
+
+ void GenerateRasterInvalidations(const PaintChunkSubset& new_chunks,
+ const PropertyTreeState& layer_state,
+ const FloatSize& visual_rect_subpixel_offset,
+ Vector<PaintChunkInfo>& new_chunks_info);
+ size_t MatchNewChunkToOldChunk(const PaintChunk& new_chunk, size_t old_index);
+ void AddDisplayItemRasterInvalidations(const PaintChunk&,
+ const ChunkToLayerMapper&);
+ void IncrementallyInvalidateChunk(const PaintChunkInfo& old_chunk,
+ const PaintChunkInfo& new_chunk);
+ void FullyInvalidateChunk(const PaintChunkInfo& old_chunk,
+ const PaintChunkInfo& new_chunk,
+ PaintInvalidationReason);
+ ALWAYS_INLINE void FullyInvalidateNewChunk(const PaintChunkInfo&,
+ PaintInvalidationReason);
+ ALWAYS_INLINE void FullyInvalidateOldChunk(const PaintChunkInfo&,
+ PaintInvalidationReason);
+ ALWAYS_INLINE void AddRasterInvalidation(const IntRect&,
+ const DisplayItemClient*,
+ PaintInvalidationReason,
+ const String* debug_name = nullptr);
+ PaintInvalidationReason ChunkPropertiesChanged(
+ const PaintChunkInfo& new_chunk,
+ const PaintChunkInfo& old_chunk,
+ const PropertyTreeState& layer_state) const;
+
+ // Clip a rect in the layer space by the layer bounds.
+ template <typename Rect>
+ Rect ClipByLayerBounds(const Rect& r) const {
+ return Intersection(
+ r, Rect(0, 0, layer_bounds_.width(), layer_bounds_.height()));
+ }
+
+ RasterInvalidationFunction raster_invalidation_function_;
+ gfx::Rect layer_bounds_;
+ Vector<PaintChunkInfo> paint_chunks_info_;
+
+ struct RasterInvalidationTrackingInfo {
+ using ClientDebugNamesMap = HashMap<const DisplayItemClient*, String>;
+ ClientDebugNamesMap new_client_debug_names;
+ ClientDebugNamesMap old_client_debug_names;
+ RasterInvalidationTracking tracking;
+ };
+ std::unique_ptr<RasterInvalidationTrackingInfo> tracking_info_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITING_COMPOSITED_LAYER_RASTER_INVALIDATOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositing/composited_layer_raster_invalidator_test.cc b/chromium/third_party/blink/renderer/platform/graphics/compositing/composited_layer_raster_invalidator_test.cc
new file mode 100644
index 00000000000..36d6203febf
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/compositing/composited_layer_raster_invalidator_test.cc
@@ -0,0 +1,565 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/compositing/composited_layer_raster_invalidator.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_chunk_subset.h"
+#include "third_party/blink/renderer/platform/testing/fake_display_item_client.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+#include "third_party/blink/renderer/platform/wtf/dtoa/utils.h"
+
+namespace blink {
+
+static const IntRect kDefaultLayerBounds(-9999, -7777, 18888, 16666);
+
+class CompositedLayerRasterInvalidatorTest
+ : public testing::Test,
+ private ScopedSlimmingPaintV2ForTest {
+ protected:
+ CompositedLayerRasterInvalidatorTest() : ScopedSlimmingPaintV2ForTest(true) {}
+
+ static PropertyTreeState DefaultPropertyTreeState() {
+ return PropertyTreeState(TransformPaintPropertyNode::Root(),
+ ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root());
+ }
+
+ static PaintChunk Chunk(
+ int type,
+ int raster_invalidation_count = 0,
+ PaintChunk::Cacheable cacheable = PaintChunk::kCacheable) {
+ DEFINE_STATIC_LOCAL(FakeDisplayItemClient, fake_client, ());
+ fake_client.ClearIsJustCreated();
+ // The enum arithmetics and magic numbers are to produce different values
+ // of paint chunk and raster invalidation properties.
+ PaintChunk::Id id(fake_client, static_cast<DisplayItem::Type>(
+ DisplayItem::kDrawingFirst + type));
+ PaintChunk chunk(0, 0, id, PaintChunkProperties(DefaultPropertyTreeState()),
+ cacheable);
+ chunk.bounds =
+ FloatRect(type * 110, type * 220, type * 220 + 200, type * 110 + 200);
+ for (int i = 0; i < raster_invalidation_count; ++i) {
+ chunk.raster_invalidation_rects.push_back(FloatRect(
+ type * 11, type * 22, type * 22 + 100 + i, type * 11 + 100 + i));
+ RasterInvalidationInfo info;
+ info.client = &id.client;
+ info.reason = static_cast<PaintInvalidationReason>(
+ static_cast<int>(PaintInvalidationReason::kFull) + type + i);
+ chunk.raster_invalidation_tracking.push_back(info);
+ }
+ return chunk;
+ }
+
+ static const Vector<RasterInvalidationInfo> TrackedRasterInvalidations(
+ CompositedLayerRasterInvalidator& invalidator) {
+ DCHECK(invalidator.GetTracking());
+ return invalidator.GetTracking()->Invalidations();
+ }
+
+ static IntRect ChunkRectToLayer(const FloatRect& rect,
+ const IntPoint& chunk_offset_from_layer) {
+ FloatRect r = rect;
+ r.MoveBy(chunk_offset_from_layer);
+ return EnclosingIntRect(r);
+ }
+
+ CompositedLayerRasterInvalidator::RasterInvalidationFunction
+ kNoopRasterInvalidation = [this](const IntRect& rect) {};
+
+ Vector<IntRect> raster_invalidations_;
+};
+
+#define EXPECT_DISPLAY_ITEM_INVALIDATIONS(invalidations, index, chunk) \
+ do { \
+ for (size_t i = 0; i < (chunk).raster_invalidation_rects.size(); ++i) { \
+ SCOPED_TRACE(index + i); \
+ const auto& info = (invalidations)[index + i]; \
+ EXPECT_EQ(ChunkRectToLayer((chunk).raster_invalidation_rects[i], \
+ -kDefaultLayerBounds.Location()), \
+ info.rect); \
+ EXPECT_EQ(&(chunk).id.client, info.client); \
+ EXPECT_EQ((chunk).raster_invalidation_tracking[i].reason, info.reason); \
+ } \
+ } while (false)
+
+#define EXPECT_CHUNK_INVALIDATION_WITH_LAYER_OFFSET( \
+ invalidations, index, chunk, expected_reason, layer_offset) \
+ do { \
+ const auto& info = (invalidations)[index]; \
+ EXPECT_EQ(ChunkRectToLayer((chunk).bounds, layer_offset), info.rect); \
+ EXPECT_EQ(&(chunk).id.client, info.client); \
+ EXPECT_EQ(expected_reason, info.reason); \
+ } while (false)
+
+#define EXPECT_CHUNK_INVALIDATION(invalidations, index, chunk, reason) \
+ EXPECT_CHUNK_INVALIDATION_WITH_LAYER_OFFSET( \
+ invalidations, index, chunk, reason, -kDefaultLayerBounds.Location())
+
+#define EXPECT_INCREMENTAL_INVALIDATION(invalidations, index, chunk, \
+ chunk_rect) \
+ do { \
+ const auto& info = (invalidations)[index]; \
+ EXPECT_EQ(ChunkRectToLayer(chunk_rect, -kDefaultLayerBounds.Location()), \
+ info.rect); \
+ EXPECT_EQ(&(chunk).id.client, info.client); \
+ EXPECT_EQ(PaintInvalidationReason::kIncremental, info.reason); \
+ } while (false)
+
+TEST_F(CompositedLayerRasterInvalidatorTest, LayerBounds) {
+ CompositedLayerRasterInvalidator invalidator(kNoopRasterInvalidation);
+ invalidator.SetTracksRasterInvalidations(true);
+ Vector<PaintChunk> chunks = {Chunk(0)};
+
+ invalidator.Generate(kDefaultLayerBounds, chunks, DefaultPropertyTreeState());
+ // No raster invalidations needed for a new layer.
+ EXPECT_TRUE(TrackedRasterInvalidations(invalidator).IsEmpty());
+
+ invalidator.Generate(kDefaultLayerBounds, chunks, DefaultPropertyTreeState());
+ // No raster invalidations needed if layer origin doesn't change.
+ EXPECT_TRUE(TrackedRasterInvalidations(invalidator).IsEmpty());
+
+ auto new_layer_bounds = kDefaultLayerBounds;
+ new_layer_bounds.Move(66, 77);
+ invalidator.Generate(new_layer_bounds, chunks, DefaultPropertyTreeState());
+ // Change of layer origin causes change of chunk0's transform to layer.
+ const auto& invalidations = TrackedRasterInvalidations(invalidator);
+ ASSERT_EQ(2u, invalidations.size());
+ EXPECT_CHUNK_INVALIDATION(invalidations, 0, chunks[0],
+ PaintInvalidationReason::kPaintProperty);
+ EXPECT_CHUNK_INVALIDATION_WITH_LAYER_OFFSET(
+ invalidations, 1, chunks[0], PaintInvalidationReason::kPaintProperty,
+ -new_layer_bounds.Location());
+}
+
+TEST_F(CompositedLayerRasterInvalidatorTest, ReorderChunks) {
+ CompositedLayerRasterInvalidator invalidator(kNoopRasterInvalidation);
+ Vector<PaintChunk> chunks = {Chunk(0), Chunk(1), Chunk(2)};
+ invalidator.SetTracksRasterInvalidations(true);
+ invalidator.Generate(kDefaultLayerBounds, chunks, DefaultPropertyTreeState());
+ EXPECT_TRUE(TrackedRasterInvalidations(invalidator).IsEmpty());
+
+ // Swap chunk 1 and 2. All chunks have their own local raster invalidations.
+ Vector<PaintChunk> new_chunks = {Chunk(0, 2), Chunk(2, 4), Chunk(1, 3)};
+ new_chunks[2].bounds = FloatRect(11, 22, 33, 44);
+ invalidator.Generate(kDefaultLayerBounds, new_chunks,
+ DefaultPropertyTreeState());
+ const auto& invalidations = TrackedRasterInvalidations(invalidator);
+ ASSERT_EQ(8u, invalidations.size());
+ // The first chunk should always match because otherwise we won't reuse the
+ // CompositedLayerRasterInvalidator (which is according to the first chunk's
+ // id). For matched chunk, we issue raster invalidations if any found by
+ // PaintController.
+ EXPECT_DISPLAY_ITEM_INVALIDATIONS(invalidations, 0, new_chunks[0]);
+ EXPECT_DISPLAY_ITEM_INVALIDATIONS(invalidations, 2, new_chunks[1]);
+ // Invalidated new chunk 2's old (as chunks[1]) and new (as new_chunks[2])
+ // bounds.
+ EXPECT_CHUNK_INVALIDATION(invalidations, 6, chunks[1],
+ PaintInvalidationReason::kChunkReordered);
+ EXPECT_CHUNK_INVALIDATION(invalidations, 7, new_chunks[2],
+ PaintInvalidationReason::kChunkReordered);
+}
+
+TEST_F(CompositedLayerRasterInvalidatorTest, ReorderChunkSubsequences) {
+ CompositedLayerRasterInvalidator invalidator(kNoopRasterInvalidation);
+ Vector<PaintChunk> chunks = {Chunk(0), Chunk(1), Chunk(2), Chunk(3),
+ Chunk(4)};
+ invalidator.SetTracksRasterInvalidations(true);
+ invalidator.Generate(kDefaultLayerBounds, chunks, DefaultPropertyTreeState());
+ EXPECT_TRUE(TrackedRasterInvalidations(invalidator).IsEmpty());
+
+ // Swap chunk (1,2) and (3,4). All chunks have their own local raster
+ // invalidations.
+ Vector<PaintChunk> new_chunks = {Chunk(0, 2), Chunk(3, 3), Chunk(4, 4),
+ Chunk(1, 1), Chunk(2, 2)};
+ new_chunks[3].bounds = FloatRect(11, 22, 33, 44);
+ invalidator.Generate(kDefaultLayerBounds, new_chunks,
+ DefaultPropertyTreeState());
+ const auto& invalidations = TrackedRasterInvalidations(invalidator);
+ ASSERT_EQ(12u, invalidations.size());
+ // The first chunk should always match because otherwise we won't reuse the
+ // CompositedLayerRasterInvalidator (which is according to the first chunk's
+ // id). For matched chunk, we issue raster invalidations if any found by
+ // PaintController.
+ EXPECT_DISPLAY_ITEM_INVALIDATIONS(invalidations, 0, new_chunks[0]);
+ EXPECT_DISPLAY_ITEM_INVALIDATIONS(invalidations, 2, new_chunks[1]);
+ EXPECT_DISPLAY_ITEM_INVALIDATIONS(invalidations, 5, new_chunks[2]);
+ // Invalidated new chunk 3's old (as chunks[1]) and new (as new_chunks[3])
+ // bounds.
+ EXPECT_CHUNK_INVALIDATION(invalidations, 9, chunks[1],
+ PaintInvalidationReason::kChunkReordered);
+ EXPECT_CHUNK_INVALIDATION(invalidations, 10, new_chunks[3],
+ PaintInvalidationReason::kChunkReordered);
+ // Invalidated new chunk 4's new bounds. Didn't invalidate old bounds because
+ // it's the same as the new bounds.
+ EXPECT_CHUNK_INVALIDATION(invalidations, 11, new_chunks[4],
+ PaintInvalidationReason::kChunkReordered);
+}
+
+TEST_F(CompositedLayerRasterInvalidatorTest, AppearAndDisappear) {
+ CompositedLayerRasterInvalidator invalidator(kNoopRasterInvalidation);
+ Vector<PaintChunk> chunks = {Chunk(0), Chunk(1), Chunk(2)};
+ invalidator.SetTracksRasterInvalidations(true);
+ invalidator.Generate(kDefaultLayerBounds, chunks, DefaultPropertyTreeState());
+ EXPECT_TRUE(TrackedRasterInvalidations(invalidator).IsEmpty());
+
+ // Chunk 1 and 2 disappeared, 3 and 4 appeared. All chunks have their own
+ // local raster invalidations.
+ Vector<PaintChunk> new_chunks = {Chunk(0, 2), Chunk(3, 3), Chunk(4, 3)};
+ invalidator.Generate(kDefaultLayerBounds, new_chunks,
+ DefaultPropertyTreeState());
+ const auto& invalidations = TrackedRasterInvalidations(invalidator);
+ ASSERT_EQ(6u, invalidations.size());
+ EXPECT_DISPLAY_ITEM_INVALIDATIONS(invalidations, 0, new_chunks[0]);
+ EXPECT_CHUNK_INVALIDATION(invalidations, 2, new_chunks[1],
+ PaintInvalidationReason::kAppeared);
+ EXPECT_CHUNK_INVALIDATION(invalidations, 3, new_chunks[2],
+ PaintInvalidationReason::kAppeared);
+ EXPECT_CHUNK_INVALIDATION(invalidations, 4, chunks[1],
+ PaintInvalidationReason::kDisappeared);
+ EXPECT_CHUNK_INVALIDATION(invalidations, 5, chunks[2],
+ PaintInvalidationReason::kDisappeared);
+}
+
+TEST_F(CompositedLayerRasterInvalidatorTest, AppearAtEnd) {
+ CompositedLayerRasterInvalidator invalidator(kNoopRasterInvalidation);
+ Vector<PaintChunk> chunks = {Chunk(0)};
+ invalidator.SetTracksRasterInvalidations(true);
+ invalidator.Generate(kDefaultLayerBounds, chunks, DefaultPropertyTreeState());
+ EXPECT_TRUE(TrackedRasterInvalidations(invalidator).IsEmpty());
+
+ Vector<PaintChunk> new_chunks = {Chunk(0, 2), Chunk(1, 3), Chunk(2, 3)};
+ invalidator.Generate(kDefaultLayerBounds, new_chunks,
+ DefaultPropertyTreeState());
+ const auto& invalidations = TrackedRasterInvalidations(invalidator);
+ ASSERT_EQ(4u, invalidations.size());
+ EXPECT_DISPLAY_ITEM_INVALIDATIONS(invalidations, 0, new_chunks[0]);
+ EXPECT_CHUNK_INVALIDATION(invalidations, 2, new_chunks[1],
+ PaintInvalidationReason::kAppeared);
+ EXPECT_CHUNK_INVALIDATION(invalidations, 3, new_chunks[2],
+ PaintInvalidationReason::kAppeared);
+}
+
+TEST_F(CompositedLayerRasterInvalidatorTest, UncacheableChunks) {
+ CompositedLayerRasterInvalidator invalidator(kNoopRasterInvalidation);
+ Vector<PaintChunk> chunks = {Chunk(0), Chunk(1, 0, PaintChunk::kUncacheable),
+ Chunk(2)};
+
+ invalidator.SetTracksRasterInvalidations(true);
+ invalidator.Generate(kDefaultLayerBounds, chunks, DefaultPropertyTreeState());
+ EXPECT_TRUE(TrackedRasterInvalidations(invalidator).IsEmpty());
+
+ Vector<PaintChunk> new_chunks = {Chunk(0, 2), Chunk(2, 3),
+ Chunk(1, 3, PaintChunk::kUncacheable)};
+ invalidator.Generate(kDefaultLayerBounds, new_chunks,
+ DefaultPropertyTreeState());
+ const auto& invalidations = TrackedRasterInvalidations(invalidator);
+ ASSERT_EQ(7u, invalidations.size());
+ EXPECT_DISPLAY_ITEM_INVALIDATIONS(invalidations, 0, new_chunks[0]);
+ EXPECT_DISPLAY_ITEM_INVALIDATIONS(invalidations, 2, new_chunks[1]);
+ EXPECT_CHUNK_INVALIDATION(invalidations, 5, new_chunks[2],
+ PaintInvalidationReason::kChunkUncacheable);
+ EXPECT_CHUNK_INVALIDATION(invalidations, 6, chunks[1],
+ PaintInvalidationReason::kChunkUncacheable);
+}
+
+// Tests the path based on ClipPaintPropertyNode::Changed().
+TEST_F(CompositedLayerRasterInvalidatorTest, ClipPropertyChangeRounded) {
+ CompositedLayerRasterInvalidator invalidator(kNoopRasterInvalidation);
+ Vector<PaintChunk> chunks = {Chunk(0), Chunk(1), Chunk(2)};
+ FloatRoundedRect::Radii radii(FloatSize(1, 2), FloatSize(2, 3),
+ FloatSize(3, 4), FloatSize(4, 5));
+ FloatRoundedRect clip_rect(FloatRect(-1000, -1000, 2000, 2000), radii);
+ scoped_refptr<ClipPaintPropertyNode> clip0 = ClipPaintPropertyNode::Create(
+ ClipPaintPropertyNode::Root(), TransformPaintPropertyNode::Root(),
+ clip_rect);
+ scoped_refptr<ClipPaintPropertyNode> clip2 = ClipPaintPropertyNode::Create(
+ clip0, TransformPaintPropertyNode::Root(), clip_rect);
+
+ PropertyTreeState layer_state(TransformPaintPropertyNode::Root(), clip0.get(),
+ EffectPaintPropertyNode::Root());
+ chunks[0].properties = PaintChunkProperties(layer_state);
+ chunks[1].properties = PaintChunkProperties(layer_state);
+ chunks[2].properties = PaintChunkProperties(
+ PropertyTreeState(TransformPaintPropertyNode::Root(), clip2.get(),
+ EffectPaintPropertyNode::Root()));
+
+ GeometryMapperClipCache::ClearCache();
+ invalidator.SetTracksRasterInvalidations(true);
+ invalidator.Generate(kDefaultLayerBounds, chunks, layer_state);
+ EXPECT_TRUE(TrackedRasterInvalidations(invalidator).IsEmpty());
+
+ // Change both clip0 and clip2.
+ Vector<PaintChunk> new_chunks = {Chunk(0), Chunk(1), Chunk(2)};
+ FloatRoundedRect new_clip_rect(FloatRect(-2000, -2000, 4000, 4000), radii);
+ clip0->Update(clip0->Parent(), clip0->LocalTransformSpace(), new_clip_rect);
+ clip2->Update(clip2->Parent(), clip2->LocalTransformSpace(), new_clip_rect);
+ new_chunks[0].properties = chunks[0].properties;
+ new_chunks[1].properties = chunks[1].properties;
+ new_chunks[2].properties = chunks[2].properties;
+
+ GeometryMapperClipCache::ClearCache();
+ invalidator.Generate(kDefaultLayerBounds, new_chunks, layer_state);
+ const auto& invalidations = TrackedRasterInvalidations(invalidator);
+ ASSERT_EQ(1u, invalidations.size());
+ // Property change in the layer state should not trigger raster invalidation.
+ // |clip2| change should trigger raster invalidation.
+ EXPECT_CHUNK_INVALIDATION(invalidations, 0, new_chunks[2],
+ PaintInvalidationReason::kPaintProperty);
+ invalidator.SetTracksRasterInvalidations(false);
+ clip2->ClearChangedToRoot();
+
+ // Change chunk1's properties to use a different property tree state.
+ Vector<PaintChunk> new_chunks1 = {Chunk(0), Chunk(1), Chunk(2)};
+ new_chunks1[0].properties = chunks[0].properties;
+ new_chunks1[1].properties = chunks[2].properties;
+ new_chunks1[2].properties = chunks[2].properties;
+
+ GeometryMapperClipCache::ClearCache();
+ invalidator.SetTracksRasterInvalidations(true);
+ invalidator.Generate(kDefaultLayerBounds, new_chunks1, layer_state);
+ const auto& invalidations1 = TrackedRasterInvalidations(invalidator);
+ ASSERT_EQ(1u, invalidations1.size());
+ EXPECT_CHUNK_INVALIDATION(invalidations1, 0, new_chunks1[1],
+ PaintInvalidationReason::kPaintProperty);
+ invalidator.SetTracksRasterInvalidations(false);
+}
+
+// Tests the path detecting change of PaintChunkInfo::chunk_to_layer_clip.
+TEST_F(CompositedLayerRasterInvalidatorTest, ClipPropertyChangeSimple) {
+ CompositedLayerRasterInvalidator invalidator(kNoopRasterInvalidation);
+ Vector<PaintChunk> chunks = {Chunk(0), Chunk(1)};
+ FloatRoundedRect clip_rect(-1000, -1000, 2000, 2000);
+ scoped_refptr<ClipPaintPropertyNode> clip0 = ClipPaintPropertyNode::Create(
+ ClipPaintPropertyNode::Root(), TransformPaintPropertyNode::Root(),
+ clip_rect);
+ scoped_refptr<ClipPaintPropertyNode> clip1 = ClipPaintPropertyNode::Create(
+ clip0, TransformPaintPropertyNode::Root(), clip_rect);
+
+ PropertyTreeState layer_state = PropertyTreeState::Root();
+ chunks[0].properties = PaintChunkProperties(
+ PropertyTreeState(TransformPaintPropertyNode::Root(), clip0.get(),
+ EffectPaintPropertyNode::Root()));
+ chunks[0].bounds = clip_rect.Rect();
+ chunks[1].properties = PaintChunkProperties(
+ PropertyTreeState(TransformPaintPropertyNode::Root(), clip1.get(),
+ EffectPaintPropertyNode::Root()));
+ chunks[1].bounds = clip_rect.Rect();
+
+ GeometryMapperClipCache::ClearCache();
+ invalidator.SetTracksRasterInvalidations(true);
+ invalidator.Generate(kDefaultLayerBounds, chunks, layer_state);
+ EXPECT_TRUE(TrackedRasterInvalidations(invalidator).IsEmpty());
+
+ // Change clip1 to bigger, which is still bound by clip0, resulting no actual
+ // visual change.
+ Vector<PaintChunk> new_chunks1 = {Chunk(0), Chunk(1)};
+ FloatRoundedRect new_clip_rect1(-2000, -2000, 4000, 4000);
+ clip1->Update(clip1->Parent(), clip1->LocalTransformSpace(), new_clip_rect1);
+ new_chunks1[0].properties = chunks[0].properties;
+ new_chunks1[0].bounds = chunks[0].bounds;
+ new_chunks1[1].properties = chunks[1].properties;
+ new_chunks1[1].bounds = chunks[1].bounds;
+
+ GeometryMapperClipCache::ClearCache();
+ invalidator.Generate(kDefaultLayerBounds, new_chunks1, layer_state);
+ EXPECT_TRUE(TrackedRasterInvalidations(invalidator).IsEmpty());
+ clip1->ClearChangedToRoot();
+
+ // Change clip1 to smaller.
+ Vector<PaintChunk> new_chunks2 = {Chunk(0), Chunk(1)};
+ FloatRoundedRect new_clip_rect2(-500, -500, 1000, 1000);
+ clip1->Update(clip1->Parent(), clip1->LocalTransformSpace(), new_clip_rect2);
+ new_chunks2[0].properties = chunks[0].properties;
+ new_chunks2[0].bounds = chunks[0].bounds;
+ new_chunks2[1].properties = chunks[1].properties;
+ new_chunks2[1].bounds = new_clip_rect2.Rect();
+
+ GeometryMapperClipCache::ClearCache();
+ invalidator.Generate(kDefaultLayerBounds, new_chunks2, layer_state);
+ const auto& invalidations = TrackedRasterInvalidations(invalidator);
+ ASSERT_EQ(4u, invalidations.size());
+ // |clip1| change should trigger incremental raster invalidation.
+ EXPECT_INCREMENTAL_INVALIDATION(invalidations, 0, new_chunks2[1],
+ IntRect(-1000, -1000, 2000, 500));
+ EXPECT_INCREMENTAL_INVALIDATION(invalidations, 1, new_chunks2[1],
+ IntRect(-1000, -500, 500, 1000));
+ EXPECT_INCREMENTAL_INVALIDATION(invalidations, 2, new_chunks2[1],
+ IntRect(500, -500, 500, 1000));
+ EXPECT_INCREMENTAL_INVALIDATION(invalidations, 3, new_chunks2[1],
+ IntRect(-1000, 500, 2000, 500));
+ invalidator.SetTracksRasterInvalidations(false);
+ clip1->ClearChangedToRoot();
+
+ // Change clip1 bigger at one side.
+ Vector<PaintChunk> new_chunks3 = {Chunk(0), Chunk(1)};
+ FloatRoundedRect new_clip_rect3(-500, -500, 2000, 1000);
+ clip1->Update(clip1->Parent(), clip1->LocalTransformSpace(), new_clip_rect3);
+ new_chunks3[0].properties = chunks[0].properties;
+ new_chunks3[0].bounds = chunks[0].bounds;
+ new_chunks3[1].properties = chunks[1].properties;
+ new_chunks3[1].bounds = new_clip_rect3.Rect();
+
+ GeometryMapperClipCache::ClearCache();
+ invalidator.SetTracksRasterInvalidations(true);
+ invalidator.Generate(kDefaultLayerBounds, new_chunks3, layer_state);
+ const auto& invalidations1 = TrackedRasterInvalidations(invalidator);
+ ASSERT_EQ(1u, invalidations1.size());
+ // |clip1| change should trigger incremental raster invalidation.
+ EXPECT_INCREMENTAL_INVALIDATION(invalidations1, 0, new_chunks3[1],
+ IntRect(500, -500, 500, 1000));
+ invalidator.SetTracksRasterInvalidations(false);
+ clip1->ClearChangedToRoot();
+}
+
+TEST_F(CompositedLayerRasterInvalidatorTest, TransformPropertyChange) {
+ CompositedLayerRasterInvalidator invalidator(kNoopRasterInvalidation);
+ Vector<PaintChunk> chunks = {Chunk(0), Chunk(1)};
+
+ auto layer_transform = TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(), TransformationMatrix().Scale(5),
+ FloatPoint3D());
+ auto transform0 = TransformPaintPropertyNode::Create(
+ layer_transform, TransformationMatrix().Translate(10, 20),
+ FloatPoint3D());
+ auto transform1 = TransformPaintPropertyNode::Create(
+ transform0, TransformationMatrix().Translate(-50, -60), FloatPoint3D());
+
+ PropertyTreeState layer_state(layer_transform.get(),
+ ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root());
+ chunks[0].properties = PaintChunkProperties(
+ PropertyTreeState(transform0.get(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root()));
+ chunks[1].properties = PaintChunkProperties(
+ PropertyTreeState(transform1.get(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root()));
+
+ GeometryMapperTransformCache::ClearCache();
+ invalidator.SetTracksRasterInvalidations(true);
+ invalidator.Generate(kDefaultLayerBounds, chunks, layer_state);
+ EXPECT_TRUE(TrackedRasterInvalidations(invalidator).IsEmpty());
+
+ // Change layer_transform should not cause raster invalidation in the layer.
+ Vector<PaintChunk> new_chunks = {Chunk(0), Chunk(1)};
+ layer_transform->Update(layer_transform->Parent(),
+ TransformationMatrix().Scale(10), FloatPoint3D());
+ new_chunks[0].properties = chunks[0].properties;
+ new_chunks[1].properties = chunks[1].properties;
+
+ GeometryMapperTransformCache::ClearCache();
+ invalidator.Generate(kDefaultLayerBounds, new_chunks, layer_state);
+ EXPECT_TRUE(TrackedRasterInvalidations(invalidator).IsEmpty());
+
+ // Inserting another node between layer_transform and transform0 and letting
+ // the new node become the transform of the layer state should not cause
+ // raster invalidation in the layer. This simulates a composited layer is
+ // scrolled from its original location.
+ Vector<PaintChunk> new_chunks1 = {Chunk(0), Chunk(1)};
+ auto new_layer_transform = TransformPaintPropertyNode::Create(
+ layer_transform, TransformationMatrix().Translate(-100, -200),
+ FloatPoint3D());
+ layer_state = PropertyTreeState(new_layer_transform.get(),
+ ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root());
+ transform0->Update(new_layer_transform, transform0->Matrix(), FloatPoint3D());
+ new_chunks1[0].properties = chunks[0].properties;
+ new_chunks1[1].properties = chunks[1].properties;
+
+ GeometryMapperTransformCache::ClearCache();
+ invalidator.Generate(kDefaultLayerBounds, new_chunks1, layer_state);
+ EXPECT_TRUE(TrackedRasterInvalidations(invalidator).IsEmpty());
+
+ // Removing transform nodes above the layer state should not cause raster
+ // invalidation in the layer.
+ Vector<PaintChunk> new_chunks2 = {Chunk(0), Chunk(1)};
+ layer_state = DefaultPropertyTreeState();
+ transform0->Update(layer_state.Transform(), transform0->Matrix(),
+ FloatPoint3D());
+ new_chunks2[0].properties = chunks[0].properties;
+ new_chunks2[1].properties = chunks[1].properties;
+
+ GeometryMapperTransformCache::ClearCache();
+ invalidator.Generate(kDefaultLayerBounds, new_chunks2, layer_state);
+ EXPECT_TRUE(TrackedRasterInvalidations(invalidator).IsEmpty());
+
+ // Change transform0 and transform1, while keeping the combined transform0
+ // and transform1 unchanged for chunk 2. We should invalidate only chunk 0
+ // for changed paint property.
+ Vector<PaintChunk> new_chunks3 = {Chunk(0), Chunk(1)};
+ transform0->Update(
+ layer_state.Transform(),
+ TransformationMatrix(transform0->Matrix()).Translate(20, 30),
+ FloatPoint3D());
+ transform1->Update(
+ transform0,
+ TransformationMatrix(transform1->Matrix()).Translate(-20, -30),
+ FloatPoint3D());
+ new_chunks3[0].properties = new_chunks2[0].properties;
+ new_chunks3[1].properties = new_chunks2[1].properties;
+
+ GeometryMapperTransformCache::ClearCache();
+ invalidator.Generate(kDefaultLayerBounds, new_chunks3, layer_state);
+ const auto& invalidations = TrackedRasterInvalidations(invalidator);
+ ASSERT_EQ(2u, invalidations.size());
+ EXPECT_CHUNK_INVALIDATION_WITH_LAYER_OFFSET(
+ invalidations, 0, new_chunks3[0], PaintInvalidationReason::kPaintProperty,
+ -kDefaultLayerBounds.Location() + IntSize(10, 20));
+ EXPECT_CHUNK_INVALIDATION_WITH_LAYER_OFFSET(
+ invalidations, 1, new_chunks3[0], PaintInvalidationReason::kPaintProperty,
+ -kDefaultLayerBounds.Location() + IntSize(30, 50));
+ invalidator.SetTracksRasterInvalidations(false);
+}
+
+TEST_F(CompositedLayerRasterInvalidatorTest, TransformPropertyTinyChange) {
+ CompositedLayerRasterInvalidator invalidator(kNoopRasterInvalidation);
+
+ auto layer_transform = CreateTransform(TransformPaintPropertyNode::Root(),
+ TransformationMatrix().Scale(5));
+ auto chunk_transform = CreateTransform(
+ layer_transform, TransformationMatrix().Translate(10, 20));
+
+ PropertyTreeState layer_state(layer_transform.get(),
+ ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root());
+ auto artifact = Chunk(0).Properties(chunk_transform.get()).Build();
+
+ GeometryMapperTransformCache::ClearCache();
+ invalidator.SetTracksRasterInvalidations(true);
+ invalidator.Generate(artifact, kDefaultLayerBounds, layer_state);
+ EXPECT_TRUE(TrackedRasterInvalidations(invalidator).IsEmpty());
+
+ // Change chunk_transform by tiny difference, which should be ignored.
+ chunk_transform->Update(layer_state.Transform(),
+ TransformPaintPropertyNode::State{
+ TransformationMatrix(chunk_transform->Matrix())
+ .Translate(0.0000001, -0.0000001)
+ .Scale(1.0000001)
+ .Rotate(0.0000001)});
+ auto new_artifact = Chunk(0).Properties(chunk_transform.get()).Build();
+
+ GeometryMapperTransformCache::ClearCache();
+ invalidator.Generate(new_artifact, kDefaultLayerBounds, layer_state);
+ EXPECT_TRUE(TrackedRasterInvalidations(invalidator).IsEmpty());
+
+ // Tiny differences should accumulate and cause invalidation when the
+ // accumulation is large enough.
+ bool invalidated = false;
+ for (int i = 0; i < 100 && !invalidated; i++) {
+ chunk_transform->Update(layer_state.Transform(),
+ TransformPaintPropertyNode::State{
+ TransformationMatrix(chunk_transform->Matrix())
+ .Translate(0.0000001, -0.0000001)
+ .Scale(1.0000001)
+ .Rotate(0.0000001)});
+ auto new_artifact = Chunk(0).Properties(chunk_transform.get()).Build();
+
+ GeometryMapperTransformCache::ClearCache();
+ invalidator.Generate(new_artifact, kDefaultLayerBounds, layer_state);
+ invalidated = !TrackedRasterInvalidations(invalidator).IsEmpty();
+ }
+ EXPECT_TRUE(invalidated);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc b/chromium/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc
new file mode 100644
index 00000000000..8a4467b8a42
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc
@@ -0,0 +1,201 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h"
+
+#include <memory>
+#include "cc/paint/paint_op_buffer.h"
+#include "third_party/blink/renderer/platform/geometry/geometry_as_json.h"
+#include "third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.h"
+#include "third_party/blink/renderer/platform/graphics/logging_canvas.h"
+#include "third_party/blink/renderer/platform/graphics/paint/display_item_list.h"
+#include "third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_chunk.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_chunk_subset.h"
+#include "third_party/blink/renderer/platform/graphics/paint/raster_invalidation_tracking.h"
+#include "third_party/blink/renderer/platform/json/json_values.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+
+namespace blink {
+
+static int GetTransformId(const TransformPaintPropertyNode* transform,
+ ContentLayerClientImpl::LayerAsJSONContext& context) {
+ if (!transform)
+ return 0;
+
+ auto it = context.transform_id_map.find(transform);
+ if (it != context.transform_id_map.end())
+ return it->value;
+
+ int parent_id = GetTransformId(transform->Parent(), context);
+ if (transform->Matrix().IsIdentity() && !transform->RenderingContextId()) {
+ context.transform_id_map.Set(transform, parent_id);
+ return parent_id;
+ }
+
+ int transform_id = context.next_transform_id++;
+ context.transform_id_map.Set(transform, transform_id);
+
+ auto json = JSONObject::Create();
+ json->SetInteger("id", transform_id);
+ if (parent_id)
+ json->SetInteger("parent", parent_id);
+
+ if (!transform->Matrix().IsIdentity())
+ json->SetArray("transform", TransformAsJSONArray(transform->Matrix()));
+
+ if (!transform->Matrix().IsIdentityOrTranslation())
+ json->SetArray("origin", PointAsJSONArray(transform->Origin()));
+
+ if (!transform->FlattensInheritedTransform())
+ json->SetBoolean("flattenInheritedTransform", false);
+
+ if (auto rendering_context = transform->RenderingContextId()) {
+ auto it = context.rendering_context_map.find(rendering_context);
+ int rendering_id = context.rendering_context_map.size() + 1;
+ if (it == context.rendering_context_map.end())
+ context.rendering_context_map.Set(rendering_context, rendering_id);
+ else
+ rendering_id = it->value;
+
+ json->SetInteger("renderingContext", rendering_id);
+ }
+
+ if (!context.transforms_json)
+ context.transforms_json = JSONArray::Create();
+ context.transforms_json->PushObject(std::move(json));
+
+ return transform_id;
+}
+
+// This is the SPv2 version of GraphicsLayer::LayerAsJSONInternal().
+std::unique_ptr<JSONObject> ContentLayerClientImpl::LayerAsJSON(
+ LayerAsJSONContext& context) const {
+ std::unique_ptr<JSONObject> json = JSONObject::Create();
+ json->SetString("name", debug_name_);
+
+ if (context.flags & kLayerTreeIncludesDebugInfo)
+ json->SetString("this", String::Format("%p", cc_picture_layer_.get()));
+
+ FloatPoint position(cc_picture_layer_->offset_to_transform_parent().x(),
+ cc_picture_layer_->offset_to_transform_parent().y());
+ if (position != FloatPoint())
+ json->SetArray("position", PointAsJSONArray(position));
+
+ IntSize bounds(cc_picture_layer_->bounds().width(),
+ cc_picture_layer_->bounds().height());
+ if (!bounds.IsEmpty())
+ json->SetArray("bounds", SizeAsJSONArray(bounds));
+
+ if (cc_picture_layer_->contents_opaque())
+ json->SetBoolean("contentsOpaque", true);
+
+ if (!cc_picture_layer_->DrawsContent())
+ json->SetBoolean("drawsContent", false);
+
+ if (!cc_picture_layer_->double_sided())
+ json->SetString("backfaceVisibility", "hidden");
+
+ Color background_color(cc_picture_layer_->background_color());
+ if (background_color.Alpha()) {
+ json->SetString("backgroundColor",
+ background_color.NameForLayoutTreeAsText());
+ }
+
+#if DCHECK_IS_ON()
+ if (context.flags & kLayerTreeIncludesDebugInfo)
+ json->SetValue("paintChunkContents", paint_chunk_debug_data_->Clone());
+#endif
+
+ if ((context.flags & kLayerTreeIncludesPaintInvalidations) &&
+ raster_invalidator_.GetTracking())
+ raster_invalidator_.GetTracking()->AsJSON(json.get());
+
+ if (int transform_id = GetTransformId(layer_state_.Transform(), context))
+ json->SetInteger("transform", transform_id);
+
+#if DCHECK_IS_ON()
+ if (context.flags & kLayerTreeIncludesPaintRecords) {
+ LoggingCanvas canvas;
+ cc_display_item_list_->Raster(&canvas);
+ json->SetValue("paintRecord", canvas.Log());
+ }
+#endif
+
+ return json;
+}
+
+static SkColor DisplayItemBackgroundColor(const DisplayItem& item) {
+ if (item.GetType() != DisplayItem::kBoxDecorationBackground &&
+ item.GetType() != DisplayItem::kDocumentBackground)
+ return SK_ColorTRANSPARENT;
+
+ const auto& drawing_item = static_cast<const DrawingDisplayItem&>(item);
+ const auto record = drawing_item.GetPaintRecord();
+ if (!record)
+ return SK_ColorTRANSPARENT;
+
+ for (cc::PaintOpBuffer::Iterator it(record.get()); it; ++it) {
+ const auto* op = *it;
+ if (op->GetType() == cc::PaintOpType::DrawRect ||
+ op->GetType() == cc::PaintOpType::DrawRRect) {
+ const auto& flags = static_cast<const cc::PaintOpWithFlags*>(op)->flags;
+ // Skip op with looper which may modify the color.
+ if (!flags.getLooper() && flags.getStyle() == cc::PaintFlags::kFill_Style)
+ return flags.getColor();
+ }
+ }
+ return SK_ColorTRANSPARENT;
+}
+
+scoped_refptr<cc::PictureLayer> ContentLayerClientImpl::UpdateCcPictureLayer(
+ const DisplayItemList& display_item_list,
+ const gfx::Rect& layer_bounds,
+ const PaintChunkSubset& paint_chunks,
+ const PropertyTreeState& layer_state) {
+ // TODO(wangxianzhu): Avoid calling DebugName() in official release build.
+ debug_name_ = paint_chunks[0].id.client.DebugName();
+
+#if DCHECK_IS_ON()
+ paint_chunk_debug_data_ = JSONArray::Create();
+ for (const auto& chunk : paint_chunks) {
+ auto json = JSONObject::Create();
+ json->SetString("data", chunk.ToString());
+ json->SetArray("displayItems",
+ display_item_list.SubsequenceAsJSON(
+ chunk.begin_index, chunk.end_index,
+ DisplayItemList::kSkipNonDrawings |
+ DisplayItemList::kShownOnlyDisplayItemTypes));
+ json->SetString("propertyTreeState",
+ chunk.properties.property_tree_state.ToTreeString());
+ paint_chunk_debug_data_->PushObject(std::move(json));
+ }
+#endif
+
+ raster_invalidator_.Generate(layer_bounds, paint_chunks, layer_state);
+ layer_state_ = layer_state;
+
+ cc_picture_layer_->SetBounds(layer_bounds.size());
+ cc_picture_layer_->SetIsDrawable(true);
+
+ Optional<RasterUnderInvalidationCheckingParams> params;
+ if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) {
+ params.emplace(*raster_invalidator_.GetTracking(),
+ IntRect(0, 0, layer_bounds.width(), layer_bounds.height()),
+ debug_name_);
+ }
+ cc_display_item_list_ = PaintChunksToCcLayer::Convert(
+ paint_chunks, layer_state, layer_bounds.OffsetFromOrigin(),
+ display_item_list, cc::DisplayItemList::kTopLevelDisplayItemList,
+ WTF::OptionalOrNullptr(params));
+
+ if (paint_chunks[0].size()) {
+ cc_picture_layer_->SetBackgroundColor(DisplayItemBackgroundColor(
+ display_item_list[paint_chunks[0].begin_index]));
+ }
+
+ return cc_picture_layer_;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h b/chromium/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h
new file mode 100644
index 00000000000..c404df60d1a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h
@@ -0,0 +1,90 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITING_CONTENT_LAYER_CLIENT_IMPL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITING_CONTENT_LAYER_CLIENT_IMPL_H_
+
+#include "cc/layers/content_layer_client.h"
+#include "cc/layers/picture_layer.h"
+#include "third_party/blink/renderer/platform/graphics/compositing/composited_layer_raster_invalidator.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_layer_client.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class DisplayItemList;
+class JSONArray;
+class JSONObject;
+class PaintChunkSubset;
+
+class PLATFORM_EXPORT ContentLayerClientImpl : public cc::ContentLayerClient {
+ WTF_MAKE_NONCOPYABLE(ContentLayerClientImpl);
+ USING_FAST_MALLOC(ContentLayerClientImpl);
+
+ public:
+ ContentLayerClientImpl()
+ : cc_picture_layer_(cc::PictureLayer::Create(this)),
+ raster_invalidator_([this](const IntRect& rect) {
+ cc_picture_layer_->SetNeedsDisplayRect(rect);
+ }),
+ layer_state_(nullptr, nullptr, nullptr) {}
+ ~ContentLayerClientImpl() override = default;
+
+ // cc::ContentLayerClient
+ gfx::Rect PaintableRegion() override {
+ return gfx::Rect(raster_invalidator_.LayerBounds().size());
+ }
+ scoped_refptr<cc::DisplayItemList> PaintContentsToDisplayList(
+ PaintingControlSetting) override {
+ return cc_display_item_list_;
+ }
+ bool FillsBoundsCompletely() const override { return false; }
+ size_t GetApproximateUnsharedMemoryUsage() const override {
+ // TODO(jbroman): Actually calculate memory usage.
+ return 0;
+ }
+
+ void SetTracksRasterInvalidations(bool should_track) {
+ raster_invalidator_.SetTracksRasterInvalidations(should_track);
+ }
+
+ bool Matches(const PaintChunk& paint_chunk) const {
+ return raster_invalidator_.Matches(paint_chunk);
+ }
+
+ struct LayerAsJSONContext {
+ LayerAsJSONContext(LayerTreeFlags flags) : flags(flags) {}
+
+ const LayerTreeFlags flags;
+ int next_transform_id = 1;
+ std::unique_ptr<JSONArray> transforms_json;
+ HashMap<const TransformPaintPropertyNode*, int> transform_id_map;
+ HashMap<int, int> rendering_context_map;
+ };
+ std::unique_ptr<JSONObject> LayerAsJSON(LayerAsJSONContext&) const;
+
+ scoped_refptr<cc::PictureLayer> UpdateCcPictureLayer(
+ const DisplayItemList&,
+ const gfx::Rect& layer_bounds,
+ const PaintChunkSubset&,
+ const PropertyTreeState&);
+
+ private:
+ scoped_refptr<cc::PictureLayer> cc_picture_layer_;
+ scoped_refptr<cc::DisplayItemList> cc_display_item_list_;
+ CompositedLayerRasterInvalidator raster_invalidator_;
+ PropertyTreeState layer_state_;
+
+ String debug_name_;
+#if DCHECK_IS_ON()
+ std::unique_ptr<JSONArray> paint_chunk_debug_data_;
+#endif
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITING_CONTENT_LAYER_CLIENT_IMPL_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc b/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
new file mode 100644
index 00000000000..39060a8bbab
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
@@ -0,0 +1,844 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h"
+
+#include <memory>
+#include <utility>
+
+#include "cc/layers/layer.h"
+#include "cc/layers/picture_layer.h"
+#include "cc/paint/display_item_list.h"
+#include "cc/trees/layer_tree_host.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_compositor_support.h"
+#include "third_party/blink/public/platform/web_layer.h"
+#include "third_party/blink/public/platform/web_layer_scroll_client.h"
+#include "third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.h"
+#include "third_party/blink/renderer/platform/graphics/paint/display_item.h"
+#include "third_party/blink/renderer/platform/graphics/paint/foreign_layer_display_item.h"
+#include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_artifact.h"
+#include "third_party/blink/renderer/platform/graphics/paint/property_tree_state.h"
+#include "third_party/blink/renderer/platform/graphics/paint/raster_invalidation_tracking.h"
+#include "third_party/blink/renderer/platform/graphics/paint/scroll_hit_test_display_item.h"
+#include "third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h"
+#include "third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace blink {
+
+// cc property trees make use of a sequence number to identify when tree
+// topology changes. For now we naively increment the sequence number each time
+// we update the property trees. We should explore optimizing our management of
+// the sequence number through the use of a dirty bit or similar. See
+// http://crbug.com/692842#c4.
+static int g_s_property_tree_sequence_number = 1;
+
+PaintArtifactCompositor::PaintArtifactCompositor(WebLayerScrollClient& client)
+ : scroll_client_(client), tracks_raster_invalidations_(false) {
+ if (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled())
+ return;
+ root_layer_ = cc::Layer::Create();
+ web_layer_ = Platform::Current()->CompositorSupport()->CreateLayerFromCCLayer(
+ root_layer_.get());
+}
+
+PaintArtifactCompositor::~PaintArtifactCompositor() {
+ for (auto child : root_layer_->children())
+ DCHECK(!child->element_id());
+}
+
+void PaintArtifactCompositor::EnableExtraDataForTesting() {
+ extra_data_for_testing_enabled_ = true;
+ extra_data_for_testing_ = std::make_unique<ExtraDataForTesting>();
+}
+
+void PaintArtifactCompositor::SetTracksRasterInvalidations(bool should_track) {
+ for (auto& client : content_layer_clients_)
+ client->SetTracksRasterInvalidations(should_track);
+}
+
+void PaintArtifactCompositor::WillBeRemovedFromFrame() {
+ RemoveChildLayers();
+}
+
+void PaintArtifactCompositor::RemoveChildLayers() {
+ // Unregister element ids for all layers. For now we rely on the
+ // element id being set on the layer, but we'll be removing that for
+ // SPv2 soon. We may also shift to having multiple element ids per
+ // layer. When we do either of these, we'll need to keep around the
+ // element ids for unregistering in some other manner.
+ cc::LayerTreeHost* host = root_layer_->layer_tree_host();
+ if (!host)
+ return;
+ for (auto child : root_layer_->children()) {
+ host->UnregisterElement(child->element_id(), cc::ElementListType::ACTIVE);
+ }
+ root_layer_->RemoveAllChildren();
+ if (extra_data_for_testing_enabled_) {
+ extra_data_for_testing_->content_layers.clear();
+ extra_data_for_testing_->synthesized_clip_layers.clear();
+ extra_data_for_testing_->scroll_hit_test_layers.clear();
+ }
+}
+
+std::unique_ptr<JSONObject> PaintArtifactCompositor::LayersAsJSON(
+ LayerTreeFlags flags) const {
+ ContentLayerClientImpl::LayerAsJSONContext context(flags);
+ std::unique_ptr<JSONArray> layers_json = JSONArray::Create();
+ for (const auto& client : content_layer_clients_) {
+ layers_json->PushObject(client->LayerAsJSON(context));
+ }
+ std::unique_ptr<JSONObject> json = JSONObject::Create();
+ json->SetArray("layers", std::move(layers_json));
+ if (context.transforms_json)
+ json->SetArray("transforms", std::move(context.transforms_json));
+ return json;
+}
+
+static scoped_refptr<cc::Layer> ForeignLayerForPaintChunk(
+ const PaintArtifact& paint_artifact,
+ const PaintChunk& paint_chunk,
+ gfx::Vector2dF& layer_offset) {
+ if (paint_chunk.size() != 1)
+ return nullptr;
+
+ const auto& display_item =
+ paint_artifact.GetDisplayItemList()[paint_chunk.begin_index];
+ if (!display_item.IsForeignLayer())
+ return nullptr;
+
+ const auto& foreign_layer_display_item =
+ static_cast<const ForeignLayerDisplayItem&>(display_item);
+ layer_offset = gfx::Vector2dF(foreign_layer_display_item.Location().X(),
+ foreign_layer_display_item.Location().Y());
+ scoped_refptr<cc::Layer> layer = foreign_layer_display_item.GetLayer();
+ layer->SetBounds(foreign_layer_display_item.Bounds());
+ layer->SetIsDrawable(true);
+ return layer;
+}
+
+const TransformPaintPropertyNode&
+PaintArtifactCompositor::ScrollTranslationForPendingLayer(
+ const PaintArtifact& paint_artifact,
+ const PendingLayer& pending_layer) {
+ if (const auto* scroll_translation = ScrollTranslationForScrollHitTestLayer(
+ paint_artifact, pending_layer)) {
+ return *scroll_translation;
+ }
+ const auto* transform = pending_layer.property_tree_state.Transform();
+ // TODO(pdr): This could be a performance issue because it crawls up the
+ // transform tree for each pending layer. If this is on profiles, we should
+ // cache a lookup of transform node to scroll translation transform node.
+ return transform->NearestScrollTranslationNode();
+}
+
+const TransformPaintPropertyNode*
+PaintArtifactCompositor::ScrollTranslationForScrollHitTestLayer(
+ const PaintArtifact& paint_artifact,
+ const PendingLayer& pending_layer) {
+ auto paint_chunks =
+ paint_artifact.GetPaintChunkSubset(pending_layer.paint_chunk_indices);
+ DCHECK(paint_chunks.size());
+ const auto& first_paint_chunk = paint_chunks[0];
+ if (first_paint_chunk.size() != 1)
+ return nullptr;
+
+ const auto& display_item =
+ paint_artifact.GetDisplayItemList()[first_paint_chunk.begin_index];
+ if (!display_item.IsScrollHitTest())
+ return nullptr;
+
+ const auto& scroll_hit_test_display_item =
+ static_cast<const ScrollHitTestDisplayItem&>(display_item);
+ return &scroll_hit_test_display_item.scroll_offset_node();
+}
+
+scoped_refptr<cc::Layer>
+PaintArtifactCompositor::ScrollHitTestLayerForPendingLayer(
+ const PaintArtifact& paint_artifact,
+ const PendingLayer& pending_layer,
+ gfx::Vector2dF& layer_offset) {
+ const auto* scroll_offset_node =
+ ScrollTranslationForScrollHitTestLayer(paint_artifact, pending_layer);
+ if (!scroll_offset_node)
+ return nullptr;
+
+ const auto& scroll_node = *scroll_offset_node->ScrollNode();
+ auto scroll_element_id = scroll_node.GetCompositorElementId();
+
+ scoped_refptr<cc::Layer> scroll_layer;
+ for (auto& existing_layer : scroll_hit_test_layers_) {
+ if (existing_layer && existing_layer->element_id() == scroll_element_id)
+ scroll_layer = existing_layer;
+ }
+ if (!scroll_layer) {
+ scroll_layer = cc::Layer::Create();
+ scroll_layer->SetElementId(scroll_element_id);
+ }
+
+ // TODO(pdr): Add a helper for blink::FloatPoint to gfx::Vector2dF.
+ auto offset = scroll_node.ContainerRect().Location();
+ layer_offset = gfx::Vector2dF(offset.X(), offset.Y());
+ // TODO(pdr): The scroll layer's bounds are currently set to the clipped
+ // container bounds but this does not include the border. We may want to
+ // change this behavior to make non-composited and composited hit testing
+ // match (see: crbug.com/753124).
+ auto bounds = scroll_node.ContainerRect().Size();
+ // Mark the layer as scrollable.
+ // TODO(pdr): When SPV2 launches this parameter for bounds will not be needed.
+ scroll_layer->SetScrollable(bounds);
+ // Set the layer's bounds equal to the container because the scroll layer
+ // does not scroll.
+ scroll_layer->SetBounds(bounds);
+ scroll_layer->set_did_scroll_callback(
+ base::BindRepeating(&blink::WebLayerScrollClient::DidScroll,
+ base::Unretained(&scroll_client_)));
+ return scroll_layer;
+}
+
+std::unique_ptr<ContentLayerClientImpl>
+PaintArtifactCompositor::ClientForPaintChunk(const PaintChunk& paint_chunk) {
+ // TODO(chrishtr): for now, just using a linear walk. In the future we can
+ // optimize this by using the same techniques used in PaintController for
+ // display lists.
+ for (auto& client : content_layer_clients_) {
+ if (client && client->Matches(paint_chunk))
+ return std::move(client);
+ }
+
+ auto client = std::make_unique<ContentLayerClientImpl>();
+ client->SetTracksRasterInvalidations(tracks_raster_invalidations_);
+ return client;
+}
+
+scoped_refptr<cc::Layer>
+PaintArtifactCompositor::CompositedLayerForPendingLayer(
+ const PaintArtifact& paint_artifact,
+ const PendingLayer& pending_layer,
+ gfx::Vector2dF& layer_offset,
+ Vector<std::unique_ptr<ContentLayerClientImpl>>& new_content_layer_clients,
+ Vector<scoped_refptr<cc::Layer>>& new_scroll_hit_test_layers) {
+ auto paint_chunks =
+ paint_artifact.GetPaintChunkSubset(pending_layer.paint_chunk_indices);
+ DCHECK(paint_chunks.size());
+ const PaintChunk& first_paint_chunk = paint_chunks[0];
+ DCHECK(first_paint_chunk.size());
+
+ // If the paint chunk is a foreign layer, just return that layer.
+ if (scoped_refptr<cc::Layer> foreign_layer = ForeignLayerForPaintChunk(
+ paint_artifact, first_paint_chunk, layer_offset)) {
+ DCHECK_EQ(paint_chunks.size(), 1u);
+ if (extra_data_for_testing_enabled_)
+ extra_data_for_testing_->content_layers.push_back(foreign_layer);
+ return foreign_layer;
+ }
+
+ // If the paint chunk is a scroll hit test layer, lookup/create the layer.
+ if (scoped_refptr<cc::Layer> scroll_layer = ScrollHitTestLayerForPendingLayer(
+ paint_artifact, pending_layer, layer_offset)) {
+ new_scroll_hit_test_layers.push_back(scroll_layer);
+ if (extra_data_for_testing_enabled_)
+ extra_data_for_testing_->scroll_hit_test_layers.push_back(scroll_layer);
+ return scroll_layer;
+ }
+
+ // The common case: create or reuse a PictureLayer for painted content.
+ std::unique_ptr<ContentLayerClientImpl> content_layer_client =
+ ClientForPaintChunk(first_paint_chunk);
+
+ gfx::Rect cc_combined_bounds(EnclosingIntRect(pending_layer.bounds));
+ layer_offset = cc_combined_bounds.OffsetFromOrigin();
+
+ auto cc_layer = content_layer_client->UpdateCcPictureLayer(
+ paint_artifact.GetDisplayItemList(), cc_combined_bounds, paint_chunks,
+ pending_layer.property_tree_state);
+ new_content_layer_clients.push_back(std::move(content_layer_client));
+ if (extra_data_for_testing_enabled_)
+ extra_data_for_testing_->content_layers.push_back(cc_layer);
+ return cc_layer;
+}
+
+PaintArtifactCompositor::PendingLayer::PendingLayer(
+ const PaintChunk& first_paint_chunk,
+ size_t chunk_index,
+ bool chunk_requires_own_layer)
+ : bounds(first_paint_chunk.bounds),
+ rect_known_to_be_opaque(
+ first_paint_chunk.known_to_be_opaque ? bounds : FloatRect()),
+ backface_hidden(first_paint_chunk.properties.backface_hidden),
+ property_tree_state(first_paint_chunk.properties.property_tree_state
+ .GetPropertyTreeState()),
+ requires_own_layer(chunk_requires_own_layer) {
+ paint_chunk_indices.push_back(chunk_index);
+}
+
+void PaintArtifactCompositor::PendingLayer::Merge(const PendingLayer& guest) {
+ DCHECK(!requires_own_layer && !guest.requires_own_layer);
+ DCHECK_EQ(backface_hidden, guest.backface_hidden);
+
+ paint_chunk_indices.AppendVector(guest.paint_chunk_indices);
+ FloatClipRect guest_bounds_in_home(guest.bounds);
+ GeometryMapper::LocalToAncestorVisualRect(
+ guest.property_tree_state, property_tree_state, guest_bounds_in_home);
+ bounds.Unite(guest_bounds_in_home.Rect());
+ // TODO(crbug.com/701991): Upgrade GeometryMapper.
+ // If we knew the new bounds is enclosed by the mapped opaque region of
+ // the guest layer, we can deduce the merged layer being opaque too, and
+ // update rect_known_to_be_opaque accordingly.
+}
+
+static bool CanUpcastTo(const PropertyTreeState& guest,
+ const PropertyTreeState& home);
+bool PaintArtifactCompositor::PendingLayer::CanMerge(
+ const PendingLayer& guest) const {
+ if (requires_own_layer || guest.requires_own_layer)
+ return false;
+ if (backface_hidden != guest.backface_hidden)
+ return false;
+ if (property_tree_state.Effect() != guest.property_tree_state.Effect())
+ return false;
+ return CanUpcastTo(guest.property_tree_state, property_tree_state);
+}
+
+void PaintArtifactCompositor::PendingLayer::Upcast(
+ const PropertyTreeState& new_state) {
+ DCHECK(!requires_own_layer);
+ FloatClipRect float_clip_rect(bounds);
+ GeometryMapper::LocalToAncestorVisualRect(property_tree_state, new_state,
+ float_clip_rect);
+ bounds = float_clip_rect.Rect();
+
+ property_tree_state = new_state;
+ // TODO(crbug.com/701991): Upgrade GeometryMapper.
+ // A local visual rect mapped to an ancestor space may become a polygon
+ // (e.g. consider transformed clip), also effects may affect the opaque
+ // region. To determine whether the layer is still opaque, we need to
+ // query conservative opaque rect after mapping to an ancestor space,
+ // which is not supported by GeometryMapper yet.
+ rect_known_to_be_opaque = FloatRect();
+}
+
+static bool IsNonCompositingAncestorOf(
+ const TransformPaintPropertyNode* ancestor,
+ const TransformPaintPropertyNode* node) {
+ for (; node != ancestor; node = node->Parent()) {
+ if (!node || node->HasDirectCompositingReasons())
+ return false;
+ }
+ return true;
+}
+
+// Determines whether drawings based on the 'guest' state can be painted into
+// a layer with the 'home' state. A number of criteria need to be met:
+// 1. The guest effect must be a descendant of the home effect. However this
+// check is enforced by the layerization recursion. Here we assume the guest
+// has already been upcasted to the same effect.
+// 2. The guest clip must be a descendant of the home clip.
+// 3. The local space of each clip and effect node on the ancestor chain must
+// be within compositing boundary of the home transform space.
+// 4. The guest transform space must be within compositing boundary of the home
+// transform space.
+static bool CanUpcastTo(const PropertyTreeState& guest,
+ const PropertyTreeState& home) {
+ DCHECK_EQ(home.Effect(), guest.Effect());
+
+ for (const ClipPaintPropertyNode* current_clip = guest.Clip();
+ current_clip != home.Clip(); current_clip = current_clip->Parent()) {
+ if (!current_clip || current_clip->HasDirectCompositingReasons())
+ return false;
+ if (!IsNonCompositingAncestorOf(home.Transform(),
+ current_clip->LocalTransformSpace()))
+ return false;
+ }
+
+ return IsNonCompositingAncestorOf(home.Transform(), guest.Transform());
+}
+
+// Returns nullptr if 'ancestor' is not a strict ancestor of 'node'.
+// Otherwise, return the child of 'ancestor' that is an ancestor of 'node' or
+// 'node' itself.
+static const EffectPaintPropertyNode* StrictChildOfAlongPath(
+ const EffectPaintPropertyNode* ancestor,
+ const EffectPaintPropertyNode* node) {
+ for (; node; node = node->Parent()) {
+ if (node->Parent() == ancestor)
+ return node;
+ }
+ return nullptr;
+}
+
+bool PaintArtifactCompositor::MightOverlap(const PendingLayer& layer_a,
+ const PendingLayer& layer_b) {
+ PropertyTreeState root_property_tree_state(TransformPaintPropertyNode::Root(),
+ ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root());
+
+ FloatClipRect bounds_a(layer_a.bounds);
+ GeometryMapper::LocalToAncestorVisualRect(layer_a.property_tree_state,
+ root_property_tree_state, bounds_a);
+ FloatClipRect bounds_b(layer_b.bounds);
+ GeometryMapper::LocalToAncestorVisualRect(layer_b.property_tree_state,
+ root_property_tree_state, bounds_b);
+
+ return bounds_a.Rect().Intersects(bounds_b.Rect());
+}
+
+bool PaintArtifactCompositor::CanDecompositeEffect(
+ const EffectPaintPropertyNode* effect,
+ const PendingLayer& layer) {
+ // If the effect associated with the layer is deeper than than the effect
+ // we are attempting to decomposite, than implies some previous decision
+ // did not allow to decomposite intermediate effects.
+ if (layer.property_tree_state.Effect() != effect)
+ return false;
+ if (layer.requires_own_layer)
+ return false;
+ // TODO(trchen): Exotic blending layer may be decomposited only if it could
+ // be merged into the first layer of the current group.
+ if (effect->BlendMode() != SkBlendMode::kSrcOver)
+ return false;
+ if (effect->HasDirectCompositingReasons())
+ return false;
+ if (!CanUpcastTo(layer.property_tree_state,
+ PropertyTreeState(effect->LocalTransformSpace(),
+ effect->OutputClip()
+ ? effect->OutputClip()
+ : layer.property_tree_state.Clip(),
+ effect)))
+ return false;
+ return true;
+}
+
+static bool EffectGroupContainsChunk(
+ const EffectPaintPropertyNode& group_effect,
+ const PaintChunk& chunk) {
+ const EffectPaintPropertyNode* effect =
+ chunk.properties.property_tree_state.Effect();
+ return effect == &group_effect ||
+ StrictChildOfAlongPath(&group_effect, effect);
+}
+
+static bool SkipGroupIfEffectivelyInvisible(
+ const PaintArtifact& paint_artifact,
+ const EffectPaintPropertyNode& current_group,
+ Vector<PaintChunk>::const_iterator& chunk_it) {
+ // The lower bound of visibility is considered to be 0.0004f < 1/2048. With
+ // 10-bit color channels (only available on the newest Macs as of 2016;
+ // otherwise it's 8-bit), we see that an alpha of 1/2048 or less leads to a
+ // color output of less than 0.5 in all channels, hence not visible.
+ static const float kMinimumVisibleOpacity = 0.0004f;
+ if (current_group.Opacity() >= kMinimumVisibleOpacity ||
+ current_group.HasDirectCompositingReasons()) {
+ return false;
+ }
+
+ // Fast-forward to just past the end of the chunk sequence within this
+ // effect group.
+ DCHECK(EffectGroupContainsChunk(current_group, *chunk_it));
+ while (++chunk_it != paint_artifact.PaintChunks().end()) {
+ if (!EffectGroupContainsChunk(current_group, *chunk_it))
+ break;
+ }
+ return true;
+}
+
+void PaintArtifactCompositor::LayerizeGroup(
+ const PaintArtifact& paint_artifact,
+ Vector<PendingLayer>& pending_layers,
+ const EffectPaintPropertyNode& current_group,
+ Vector<PaintChunk>::const_iterator& chunk_it) {
+ // Skip paint chunks that are effectively invisible due to opacity and don't
+ // have a direct compositing reason.
+ if (SkipGroupIfEffectivelyInvisible(paint_artifact, current_group, chunk_it))
+ return;
+
+ size_t first_layer_in_current_group = pending_layers.size();
+ // The worst case time complexity of the algorithm is O(pqd), where
+ // p = the number of paint chunks.
+ // q = average number of trials to find a squash layer or rejected
+ // for overlapping.
+ // d = (sum of) the depth of property trees.
+ // The analysis as follows:
+ // Every paint chunk will be visited by the main loop below for exactly once,
+ // except for chunks that enter or exit groups (case B & C below).
+ // For normal chunk visit (case A), the only cost is determining squash,
+ // which costs O(qd), where d came from "canUpcastTo" and geometry mapping.
+ // Subtotal: O(pqd)
+ // For group entering and exiting, it could cost O(d) for each group, for
+ // searching the shallowest subgroup (strictChildOfAlongPath), thus O(d^2)
+ // in total.
+ // Also when exiting group, the group may be decomposited and squashed to a
+ // previous layer. Again finding the host costs O(qd). Merging would cost O(p)
+ // due to copying the chunk list. Subtotal: O((qd + p)d) = O(qd^2 + pd)
+ // Assuming p > d, the total complexity would be O(pqd + qd^2 + pd) = O(pqd)
+ while (chunk_it != paint_artifact.PaintChunks().end()) {
+ // Look at the effect node of the next chunk. There are 3 possible cases:
+ // A. The next chunk belongs to the current group but no subgroup.
+ // B. The next chunk does not belong to the current group.
+ // C. The next chunk belongs to some subgroup of the current group.
+ const EffectPaintPropertyNode* chunk_effect =
+ chunk_it->properties.property_tree_state.Effect();
+ if (chunk_effect == &current_group) {
+ // Case A: The next chunk belongs to the current group but no subgroup.
+ const auto& last_display_item =
+ paint_artifact.GetDisplayItemList()[chunk_it->begin_index];
+ bool requires_own_layer = last_display_item.IsForeignLayer() ||
+ // TODO(pdr): This should require a direct
+ // compositing reason.
+ last_display_item.IsScrollHitTest();
+ pending_layers.push_back(PendingLayer(
+ *chunk_it, chunk_it - paint_artifact.PaintChunks().begin(),
+ requires_own_layer));
+ chunk_it++;
+ if (requires_own_layer)
+ continue;
+ } else {
+ const EffectPaintPropertyNode* subgroup =
+ StrictChildOfAlongPath(&current_group, chunk_effect);
+ // Case B: This means we need to close the current group without
+ // processing the next chunk.
+ if (!subgroup)
+ break;
+ // Case C: The following chunks belong to a subgroup. Process them by
+ // a recursion call.
+ size_t first_layer_in_subgroup = pending_layers.size();
+ LayerizeGroup(paint_artifact, pending_layers, *subgroup, chunk_it);
+ // Now the chunk iterator stepped over the subgroup we just saw.
+ // If the subgroup generated 2 or more layers then the subgroup must be
+ // composited to satisfy grouping requirement.
+ // i.e. Grouping effects generally must be applied atomically,
+ // for example, Opacity(A+B) != Opacity(A) + Opacity(B), thus an effect
+ // either applied 100% within a layer, or not at all applied within layer
+ // (i.e. applied by compositor render surface instead).
+ if (pending_layers.size() != first_layer_in_subgroup + 1)
+ continue;
+ // Now attempt to "decomposite" subgroup.
+ PendingLayer& subgroup_layer = pending_layers[first_layer_in_subgroup];
+ if (!CanDecompositeEffect(subgroup, subgroup_layer))
+ continue;
+ subgroup_layer.Upcast(PropertyTreeState(
+ subgroup->LocalTransformSpace(),
+ subgroup->OutputClip() ? subgroup->OutputClip()
+ : subgroup_layer.property_tree_state.Clip(),
+ &current_group));
+ }
+ // At this point pendingLayers.back() is the either a layer from a
+ // "decomposited" subgroup or a layer created from a chunk we just
+ // processed. Now determine whether it could be merged into a previous
+ // layer.
+ const PendingLayer& new_layer = pending_layers.back();
+ DCHECK(!new_layer.requires_own_layer);
+ DCHECK_EQ(&current_group, new_layer.property_tree_state.Effect());
+ // This iterates pendingLayers[firstLayerInCurrentGroup:-1] in reverse.
+ for (size_t candidate_index = pending_layers.size() - 1;
+ candidate_index-- > first_layer_in_current_group;) {
+ PendingLayer& candidate_layer = pending_layers[candidate_index];
+ if (candidate_layer.CanMerge(new_layer)) {
+ candidate_layer.Merge(new_layer);
+ pending_layers.pop_back();
+ break;
+ }
+ if (MightOverlap(new_layer, candidate_layer))
+ break;
+ }
+ }
+}
+
+void PaintArtifactCompositor::CollectPendingLayers(
+ const PaintArtifact& paint_artifact,
+ Vector<PendingLayer>& pending_layers) {
+ Vector<PaintChunk>::const_iterator cursor =
+ paint_artifact.PaintChunks().begin();
+ LayerizeGroup(paint_artifact, pending_layers,
+ *EffectPaintPropertyNode::Root(), cursor);
+ DCHECK_EQ(paint_artifact.PaintChunks().end(), cursor);
+}
+
+// This class maintains a persistent mask layer and unique stable cc effect IDs
+// for reuse across compositing cycles. The mask layer paints a rounded rect,
+// which is an updatable parameter of the class. The caller is responsible for
+// inserting the layer into layer list and associating with property nodes.
+//
+// The typical application of the mask layer is to create an isolating effect
+// node to paint the clipped contents, and at the end draw the mask layer with
+// a kDstIn blend effect. This is why two stable cc effect IDs are provided for
+// the convenience of the caller, although they are not directly related to the
+// class functionality.
+class SynthesizedClip : private cc::ContentLayerClient {
+ public:
+ SynthesizedClip() : layer_(cc::PictureLayer::Create(this)) {
+ mask_isolation_id_ =
+ CompositorElementIdFromUniqueObjectId(NewUniqueObjectId());
+ mask_effect_id_ =
+ CompositorElementIdFromUniqueObjectId(NewUniqueObjectId());
+ layer_->SetIsDrawable(true);
+ }
+
+ void Update(const FloatRoundedRect& rrect,
+ scoped_refptr<const RefCountedPath> path) {
+ IntRect layer_bounds = EnclosingIntRect(rrect.Rect());
+ gfx::Vector2dF new_layer_origin(layer_bounds.X(), layer_bounds.Y());
+
+ SkRRect new_local_rrect = rrect;
+ new_local_rrect.offset(-new_layer_origin.x(), -new_layer_origin.y());
+
+ bool path_in_layer_changed = false;
+ if (path_ == path) {
+ path_in_layer_changed = path && layer_origin_ != new_layer_origin;
+ } else if (!path_ || !path) {
+ path_in_layer_changed = true;
+ } else {
+ SkPath new_path = path->GetSkPath();
+ new_path.offset(layer_origin_.x() - new_layer_origin.x(),
+ layer_origin_.y() - new_layer_origin.y());
+ path_in_layer_changed = path_->GetSkPath() != new_path;
+ }
+
+ if (local_rrect_ != new_local_rrect || path_in_layer_changed) {
+ layer_->SetNeedsDisplay();
+ }
+ layer_->set_offset_to_transform_parent(new_layer_origin);
+ layer_->SetBounds(gfx::Size(layer_bounds.Width(), layer_bounds.Height()));
+
+ layer_origin_ = new_layer_origin;
+ local_rrect_ = new_local_rrect;
+ path_ = std::move(path);
+ }
+
+ cc::Layer* GetLayer() const { return layer_.get(); }
+ CompositorElementId GetMaskIsolationId() const { return mask_isolation_id_; }
+ CompositorElementId GetMaskEffectId() const { return mask_effect_id_; }
+
+ private:
+ // ContentLayerClient implementation.
+ gfx::Rect PaintableRegion() final { return gfx::Rect(layer_->bounds()); }
+ bool FillsBoundsCompletely() const final { return false; }
+ size_t GetApproximateUnsharedMemoryUsage() const final { return 0; }
+
+ scoped_refptr<cc::DisplayItemList> PaintContentsToDisplayList(
+ PaintingControlSetting) final {
+ auto cc_list = base::MakeRefCounted<cc::DisplayItemList>(
+ cc::DisplayItemList::kTopLevelDisplayItemList);
+ cc::PaintFlags flags;
+ flags.setAntiAlias(true);
+ cc_list->StartPaint();
+ if (!path_) {
+ cc_list->push<cc::DrawRRectOp>(local_rrect_, flags);
+ } else {
+ cc_list->push<cc::SaveOp>();
+ cc_list->push<cc::TranslateOp>(-layer_origin_.x(), -layer_origin_.x());
+ cc_list->push<cc::ClipPathOp>(path_->GetSkPath(), SkClipOp::kIntersect,
+ true);
+ SkRRect rrect = local_rrect_;
+ rrect.offset(layer_origin_.x(), layer_origin_.y());
+ cc_list->push<cc::DrawRRectOp>(rrect, flags);
+ cc_list->push<cc::RestoreOp>();
+ }
+ cc_list->EndPaintOfUnpaired(gfx::Rect(layer_->bounds()));
+ cc_list->Finalize();
+ return cc_list;
+ }
+
+ private:
+ scoped_refptr<cc::PictureLayer> layer_;
+ gfx::Vector2dF layer_origin_;
+ SkRRect local_rrect_ = SkRRect::MakeEmpty();
+ scoped_refptr<const RefCountedPath> path_;
+ CompositorElementId mask_isolation_id_;
+ CompositorElementId mask_effect_id_;
+};
+
+cc::Layer* PaintArtifactCompositor::CreateOrReuseSynthesizedClipLayer(
+ const ClipPaintPropertyNode* node,
+ CompositorElementId& mask_isolation_id,
+ CompositorElementId& mask_effect_id) {
+ auto entry = std::find_if(
+ synthesized_clip_cache_.begin(), synthesized_clip_cache_.end(),
+ [node](const auto& entry) { return entry.key == node && !entry.in_use; });
+ if (entry == synthesized_clip_cache_.end()) {
+ entry = synthesized_clip_cache_.insert(
+ entry,
+ SynthesizedClipEntry{node, std::make_unique<SynthesizedClip>(), false});
+ }
+
+ entry->in_use = true;
+ SynthesizedClip& synthesized_clip = *entry->synthesized_clip;
+ synthesized_clip.Update(node->ClipRect(), node->ClipPath());
+ mask_isolation_id = synthesized_clip.GetMaskIsolationId();
+ mask_effect_id = synthesized_clip.GetMaskEffectId();
+ return synthesized_clip.GetLayer();
+}
+
+void PaintArtifactCompositor::Update(
+ const PaintArtifact& paint_artifact,
+ CompositorElementIdSet& composited_element_ids) {
+ DCHECK(root_layer_);
+
+ // The tree will be null after detaching and this update can be ignored.
+ // See: WebViewImpl::detachPaintArtifactCompositor().
+ cc::LayerTreeHost* host = root_layer_->layer_tree_host();
+ if (!host)
+ return;
+
+ if (extra_data_for_testing_enabled_)
+ extra_data_for_testing_.reset(new ExtraDataForTesting);
+
+ RemoveChildLayers();
+ root_layer_->set_property_tree_sequence_number(
+ g_s_property_tree_sequence_number);
+
+ PropertyTreeManager property_tree_manager(*this, *host->property_trees(),
+ root_layer_.get(),
+ g_s_property_tree_sequence_number);
+ Vector<PendingLayer, 0> pending_layers;
+ CollectPendingLayers(paint_artifact, pending_layers);
+
+ Vector<std::unique_ptr<ContentLayerClientImpl>> new_content_layer_clients;
+ new_content_layer_clients.ReserveCapacity(pending_layers.size());
+ Vector<scoped_refptr<cc::Layer>> new_scroll_hit_test_layers;
+
+ for (auto& entry : synthesized_clip_cache_)
+ entry.in_use = false;
+
+ for (auto& pending_layer : pending_layers) {
+ const auto& property_state = pending_layer.property_tree_state;
+ const auto* transform = property_state.Transform();
+ const auto* clip = property_state.Clip();
+
+ if (clip->LocalTransformSpace() == transform) {
+ // Limit layer bounds to hide the areas that will be never visible because
+ // of the clip.
+ pending_layer.bounds.Intersect(clip->ClipRect().Rect());
+ } else if (const auto* scroll = transform->ScrollNode()) {
+ // Limit layer bounds to the scroll range to hide the areas that will
+ // never be scrolled into the visible area.
+ pending_layer.bounds.Intersect(FloatRect(scroll->ContentsRect()));
+ }
+
+ gfx::Vector2dF layer_offset;
+ scoped_refptr<cc::Layer> layer = CompositedLayerForPendingLayer(
+ paint_artifact, pending_layer, layer_offset, new_content_layer_clients,
+ new_scroll_hit_test_layers);
+
+ int transform_id =
+ property_tree_manager.EnsureCompositorTransformNode(transform);
+ int clip_id = property_tree_manager.EnsureCompositorClipNode(clip);
+ int effect_id = property_tree_manager.SwitchToEffectNodeWithSynthesizedClip(
+ *property_state.Effect(), *clip);
+ // The compositor scroll node is not directly stored in the property tree
+ // state but can be created via the scroll offset translation node.
+ const auto& scroll_translation =
+ ScrollTranslationForPendingLayer(paint_artifact, pending_layer);
+ int scroll_id =
+ property_tree_manager.EnsureCompositorScrollNode(&scroll_translation);
+
+ layer->set_offset_to_transform_parent(layer_offset);
+
+ // Get the compositor element id for the layer. Scrollable layers are only
+ // associated with scroll element ids which are set in
+ // ScrollHitTestLayerForPendingLayer.
+ CompositorElementId element_id =
+ layer->scrollable()
+ ? layer->element_id()
+ : property_state.GetCompositorElementId(composited_element_ids);
+ if (element_id) {
+ // TODO(wkorman): Cease setting element id on layer once
+ // animation subsystem no longer requires element id to layer
+ // map. http://crbug.com/709137
+ // TODO(pdr): Element ids will still need to be set on scroll layers.
+ layer->SetElementId(element_id);
+ composited_element_ids.insert(element_id);
+ }
+
+ root_layer_->AddChild(layer);
+ // TODO(wkorman): Once we've removed all uses of
+ // LayerTreeHost::{LayerByElementId,element_layers_map} we can
+ // revise element register/unregister to cease passing layer and
+ // only register/unregister element id with the mutator host.
+ if (element_id) {
+ host->RegisterElement(element_id, cc::ElementListType::ACTIVE,
+ layer.get());
+ }
+
+ layer->set_property_tree_sequence_number(g_s_property_tree_sequence_number);
+ layer->SetTransformTreeIndex(transform_id);
+ layer->SetScrollTreeIndex(scroll_id);
+ layer->SetClipTreeIndex(clip_id);
+ layer->SetEffectTreeIndex(effect_id);
+ layer->SetContentsOpaque(pending_layer.rect_known_to_be_opaque.Contains(
+ FloatRect(EnclosingIntRect(pending_layer.bounds))));
+ layer->SetDoubleSided(!pending_layer.backface_hidden);
+ layer->SetShouldCheckBackfaceVisibility(pending_layer.backface_hidden);
+ }
+ property_tree_manager.Finalize();
+ content_layer_clients_.swap(new_content_layer_clients);
+ scroll_hit_test_layers_.swap(new_scroll_hit_test_layers);
+
+ synthesized_clip_cache_.erase(
+ std::remove_if(synthesized_clip_cache_.begin(),
+ synthesized_clip_cache_.end(),
+ [](const auto& entry) { return !entry.in_use; }),
+ synthesized_clip_cache_.end());
+ if (extra_data_for_testing_enabled_) {
+ for (const auto& entry : synthesized_clip_cache_) {
+ extra_data_for_testing_->synthesized_clip_layers.push_back(
+ entry.synthesized_clip->GetLayer());
+ }
+ }
+
+ // Mark the property trees as having been rebuilt.
+ host->property_trees()->sequence_number = g_s_property_tree_sequence_number;
+ host->property_trees()->needs_rebuild = false;
+ host->property_trees()->ResetCachedData();
+
+ g_s_property_tree_sequence_number++;
+
+ // Clear paint property change flags that are for this update only.
+ for (const auto& chunk : paint_artifact.PaintChunks())
+ chunk.properties.property_tree_state.ClearChangedToRoot();
+
+#if DCHECK_IS_ON()
+ if (VLOG_IS_ON(2)) {
+ static String s_previous_output;
+ LayerTreeFlags flags = VLOG_IS_ON(3) ? 0xffffffff : 0;
+ String new_output = LayersAsJSON(flags)->ToPrettyJSONString();
+ if (new_output != s_previous_output) {
+ LOG(ERROR) << "PaintArtifactCompositor::Update() done\n"
+ << "Composited layers:\n"
+ << new_output.Utf8().data();
+ s_previous_output = new_output;
+ }
+ }
+#endif
+}
+
+std::unique_ptr<WebLayer>
+PaintArtifactCompositor::ExtraDataForTesting::ContentWebLayerAt(
+ unsigned index) {
+ return Platform::Current()->CompositorSupport()->CreateLayerFromCCLayer(
+ content_layers[index].get());
+}
+std::unique_ptr<WebLayer>
+PaintArtifactCompositor::ExtraDataForTesting::ScrollHitTestWebLayerAt(
+ unsigned index) {
+ return Platform::Current()->CompositorSupport()->CreateLayerFromCCLayer(
+ scroll_hit_test_layers[index].get());
+}
+
+#if DCHECK_IS_ON()
+void PaintArtifactCompositor::ShowDebugData() {
+ LOG(ERROR) << LayersAsJSON(kLayerTreeIncludesDebugInfo |
+ kLayerTreeIncludesPaintInvalidations)
+ ->ToPrettyJSONString()
+ .Utf8()
+ .data();
+}
+#endif
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h b/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h
new file mode 100644
index 00000000000..d8751ef40c4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h
@@ -0,0 +1,228 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITING_PAINT_ARTIFACT_COMPOSITOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITING_PAINT_ARTIFACT_COMPOSITOR_H_
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_layer_client.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace cc {
+class Layer;
+}
+
+namespace gfx {
+class Vector2dF;
+}
+
+namespace blink {
+
+class ContentLayerClientImpl;
+class JSONObject;
+class PaintArtifact;
+class SynthesizedClip;
+class WebLayer;
+class WebLayerScrollClient;
+struct PaintChunk;
+
+// Responsible for managing compositing in terms of a PaintArtifact.
+//
+// Owns a subtree of the compositor layer tree, and updates it in response to
+// changes in the paint artifact.
+//
+// PaintArtifactCompositor is the successor to PaintLayerCompositor, reflecting
+// the new home of compositing decisions after paint in Slimming Paint v2.
+class PLATFORM_EXPORT PaintArtifactCompositor final
+ : private PropertyTreeManagerClient {
+ USING_FAST_MALLOC(PaintArtifactCompositor);
+ WTF_MAKE_NONCOPYABLE(PaintArtifactCompositor);
+
+ public:
+ ~PaintArtifactCompositor();
+
+ static std::unique_ptr<PaintArtifactCompositor> Create(
+ WebLayerScrollClient& client) {
+ return base::WrapUnique(new PaintArtifactCompositor(client));
+ }
+
+ // Updates the layer tree to match the provided paint artifact.
+ //
+ // Populates |composited_element_ids| with the CompositorElementId of all
+ // animations for which we saw a paint chunk and created a layer.
+ void Update(const PaintArtifact&,
+ CompositorElementIdSet& composited_element_ids);
+
+ // The root layer of the tree managed by this object.
+ cc::Layer* RootLayer() const { return root_layer_.get(); }
+
+ // Wraps rootLayer(), so that it can be attached as a child of another
+ // WebLayer.
+ WebLayer* GetWebLayer() const { return web_layer_.get(); }
+
+ // Returns extra information recorded during unit tests.
+ // While not part of the normal output of this class, this provides a simple
+ // way of locating the layers of interest, since there are still a slew of
+ // placeholder layers required.
+ struct PLATFORM_EXPORT ExtraDataForTesting {
+ std::unique_ptr<WebLayer> ContentWebLayerAt(unsigned index);
+ std::unique_ptr<WebLayer> ScrollHitTestWebLayerAt(unsigned index);
+
+ Vector<scoped_refptr<cc::Layer>> content_layers;
+ Vector<scoped_refptr<cc::Layer>> synthesized_clip_layers;
+ Vector<scoped_refptr<cc::Layer>> scroll_hit_test_layers;
+ };
+ void EnableExtraDataForTesting();
+ ExtraDataForTesting* GetExtraDataForTesting() const {
+ return extra_data_for_testing_.get();
+ }
+
+ void SetTracksRasterInvalidations(bool);
+
+ // Called when the local frame view that owns this compositor is
+ // going to be removed from its frame.
+ void WillBeRemovedFromFrame();
+
+ std::unique_ptr<JSONObject> LayersAsJSON(LayerTreeFlags) const;
+
+#if DCHECK_IS_ON()
+ void ShowDebugData();
+#endif
+
+ private:
+ // A pending layer is a collection of paint chunks that will end up in
+ // the same cc::Layer.
+ struct PLATFORM_EXPORT PendingLayer {
+ PendingLayer(const PaintChunk& first_paint_chunk,
+ size_t first_chunk_index,
+ bool requires_own_layer);
+ // Merge another pending layer after this one, appending all its paint
+ // chunks after chunks in this layer, with appropriate space conversion
+ // applied. The merged layer must have a property tree state that's deeper
+ // than this layer, i.e. can "upcast" to this layer's state.
+ void Merge(const PendingLayer& guest);
+ bool CanMerge(const PendingLayer& guest) const;
+ // Mutate this layer's property tree state to a more general (shallower)
+ // state, thus the name "upcast". The concrete effect of this is to
+ // "decomposite" some of the properties, so that fewer properties will be
+ // applied by the compositor, and more properties will be applied internally
+ // to the chunks as Skia commands.
+ void Upcast(const PropertyTreeState&);
+
+ FloatRect bounds;
+ Vector<size_t> paint_chunk_indices;
+ FloatRect rect_known_to_be_opaque;
+ bool backface_hidden;
+ PropertyTreeState property_tree_state;
+ bool requires_own_layer;
+ };
+
+ PaintArtifactCompositor(WebLayerScrollClient&);
+
+ void RemoveChildLayers();
+
+ // Collects the PaintChunks into groups which will end up in the same
+ // cc layer. This is the entry point of the layerization algorithm.
+ void CollectPendingLayers(const PaintArtifact&,
+ Vector<PendingLayer>& pending_layers);
+ // This is the internal recursion of collectPendingLayers. This function
+ // loops over the list of paint chunks, scoped by an isolated group
+ // (i.e. effect node). Inside of the loop, chunks are tested for overlap
+ // and merge compatibility. Subgroups are handled by recursion, and will
+ // be tested for "decompositing" upon return.
+ // Merge compatibility means consecutive chunks may be layerized into the
+ // same backing (i.e. merged) if their property states don't cross
+ // direct-compositing boundary.
+ // Non-consecutive chunks that are nevertheless compatible may still be
+ // merged, if reordering of the chunks won't affect the ultimate result.
+ // This is determined by overlap testing such that chunks can be safely
+ // reordered if their effective bounds in screen space can't overlap.
+ // The recursion only tests merge & overlap for chunks scoped by the same
+ // group. This is where "decompositing" came in. Upon returning from a
+ // recursion, the layerization of the subgroup may be tested for merge &
+ // overlap with other chunks in the parent group, if grouping requirement
+ // can be satisfied (and the effect node has no direct reason).
+ static void LayerizeGroup(const PaintArtifact&,
+ Vector<PendingLayer>& pending_layers,
+ const EffectPaintPropertyNode&,
+ Vector<PaintChunk>::const_iterator& chunk_cursor);
+ static bool MightOverlap(const PendingLayer&, const PendingLayer&);
+ static bool CanDecompositeEffect(const EffectPaintPropertyNode*,
+ const PendingLayer&);
+
+ // Builds a leaf layer that represents a single paint chunk.
+ // Note: cc::Layer API assumes the layer bounds start at (0, 0), but the
+ // bounding box of a paint chunk does not necessarily start at (0, 0) (and
+ // could even be negative). Internally the generated layer translates the
+ // paint chunk to align the bounding box to (0, 0) and return the actual
+ // origin of the paint chunk in the |layerOffset| outparam.
+ scoped_refptr<cc::Layer> CompositedLayerForPendingLayer(
+ const PaintArtifact&,
+ const PendingLayer&,
+ gfx::Vector2dF& layer_offset,
+ Vector<std::unique_ptr<ContentLayerClientImpl>>&
+ new_content_layer_clients,
+ Vector<scoped_refptr<cc::Layer>>& new_scroll_hit_test_layers);
+
+ const TransformPaintPropertyNode& ScrollTranslationForPendingLayer(
+ const PaintArtifact&,
+ const PendingLayer&);
+
+ // If the pending layer is a special scroll hit test layer, return the
+ // associated scroll offset translation node.
+ const TransformPaintPropertyNode* ScrollTranslationForScrollHitTestLayer(
+ const PaintArtifact&,
+ const PendingLayer&);
+
+ // Finds an existing or creates a new scroll hit test layer for the pending
+ // layer, returning nullptr if the layer is not a scroll hit test layer.
+ scoped_refptr<cc::Layer> ScrollHitTestLayerForPendingLayer(
+ const PaintArtifact&,
+ const PendingLayer&,
+ gfx::Vector2dF& layer_offset);
+
+ // Finds a client among the current vector of clients that matches the paint
+ // chunk's id, or otherwise allocates a new one.
+ std::unique_ptr<ContentLayerClientImpl> ClientForPaintChunk(
+ const PaintChunk&);
+
+ cc::Layer* CreateOrReuseSynthesizedClipLayer(
+ const ClipPaintPropertyNode*,
+ CompositorElementId& mask_isolation_id,
+ CompositorElementId& mask_effect_id) final;
+
+ // Provides a callback for notifying blink of composited scrolling.
+ WebLayerScrollClient& scroll_client_;
+
+ bool tracks_raster_invalidations_;
+
+ scoped_refptr<cc::Layer> root_layer_;
+ std::unique_ptr<WebLayer> web_layer_;
+ Vector<std::unique_ptr<ContentLayerClientImpl>> content_layer_clients_;
+ struct SynthesizedClipEntry {
+ const ClipPaintPropertyNode* key;
+ std::unique_ptr<SynthesizedClip> synthesized_clip;
+ bool in_use;
+ };
+ std::vector<SynthesizedClipEntry> synthesized_clip_cache_;
+
+ Vector<scoped_refptr<cc::Layer>> scroll_hit_test_layers_;
+
+ bool extra_data_for_testing_enabled_ = false;
+ std::unique_ptr<ExtraDataForTesting> extra_data_for_testing_;
+
+ friend class StubChromeClientForSPv2;
+ friend class PaintArtifactCompositorTest;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITING_PAINT_ARTIFACT_COMPOSITOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc b/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
new file mode 100644
index 00000000000..f6e3259974d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
@@ -0,0 +1,3361 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h"
+
+#include <memory>
+
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "cc/layers/layer.h"
+#include "cc/test/fake_layer_tree_frame_sink.h"
+#include "cc/test/geometry_test_utils.h"
+#include "cc/trees/clip_node.h"
+#include "cc/trees/effect_node.h"
+#include "cc/trees/layer_tree_host.h"
+#include "cc/trees/layer_tree_settings.h"
+#include "cc/trees/scroll_node.h"
+#include "cc/trees/transform_node.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/web_layer_scroll_client.h"
+#include "third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_artifact.h"
+#include "third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h"
+#include "third_party/blink/renderer/platform/testing/fake_display_item_client.h"
+#include "third_party/blink/renderer/platform/testing/paint_property_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/picture_matchers.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/test_paint_artifact.h"
+#include "third_party/blink/renderer/platform/testing/web_layer_tree_view_impl_for_testing.h"
+
+namespace blink {
+
+using ::blink::test::CreateOpacityOnlyEffect;
+using testing::Pointee;
+
+PaintChunk::Id DefaultId() {
+ DEFINE_STATIC_LOCAL(FakeDisplayItemClient, fake_client, ());
+ return PaintChunk::Id(fake_client, DisplayItem::kDrawingFirst);
+}
+
+PaintChunkProperties DefaultPaintChunkProperties() {
+ PropertyTreeState property_tree_state(TransformPaintPropertyNode::Root(),
+ ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root());
+ return PaintChunkProperties(property_tree_state);
+}
+
+PaintChunk DefaultChunk() {
+ return PaintChunk(0, 1, DefaultId(), DefaultPaintChunkProperties());
+}
+
+gfx::Transform Translation(SkMScalar x, SkMScalar y) {
+ gfx::Transform transform;
+ transform.Translate(x, y);
+ return transform;
+}
+
+class WebLayerTreeViewWithLayerTreeFrameSink
+ : public WebLayerTreeViewImplForTesting {
+ public:
+ WebLayerTreeViewWithLayerTreeFrameSink(const cc::LayerTreeSettings& settings)
+ : WebLayerTreeViewImplForTesting(settings) {}
+
+ // cc::LayerTreeHostClient
+ void RequestNewLayerTreeFrameSink() override {
+ GetLayerTreeHost()->SetLayerTreeFrameSink(
+ cc::FakeLayerTreeFrameSink::Create3d());
+ }
+};
+
+class FakeScrollClient : public WebLayerScrollClient {
+ public:
+ FakeScrollClient() : did_scroll_count(0) {}
+
+ void DidScroll(const gfx::ScrollOffset& offset,
+ const CompositorElementId&) final {
+ did_scroll_count++;
+ last_scroll_offset = offset;
+ };
+
+ gfx::ScrollOffset last_scroll_offset;
+ unsigned did_scroll_count;
+};
+
+class PaintArtifactCompositorTest : public testing::Test,
+ private ScopedSlimmingPaintV2ForTest {
+ protected:
+ PaintArtifactCompositorTest()
+ : ScopedSlimmingPaintV2ForTest(true),
+ task_runner_(new base::TestSimpleTaskRunner),
+ task_runner_handle_(task_runner_) {}
+
+ void SetUp() override {
+ // Delay constructing the compositor until after the feature is set.
+ paint_artifact_compositor_ =
+ PaintArtifactCompositor::Create(scroll_client_);
+ paint_artifact_compositor_->EnableExtraDataForTesting();
+
+ cc::LayerTreeSettings settings =
+ WebLayerTreeViewImplForTesting::DefaultLayerTreeSettings();
+ settings.single_thread_proxy_scheduler = false;
+ settings.use_layer_lists = true;
+ web_layer_tree_view_ =
+ std::make_unique<WebLayerTreeViewWithLayerTreeFrameSink>(settings);
+ web_layer_tree_view_->SetRootLayer(
+ *paint_artifact_compositor_->GetWebLayer());
+ }
+
+ void TearDown() override {
+ // Make sure we remove all child layers to satisfy destructor
+ // child layer element id DCHECK.
+ WillBeRemovedFromFrame();
+ }
+
+ const cc::PropertyTrees& GetPropertyTrees() {
+ return *web_layer_tree_view_->GetLayerTreeHost()->property_trees();
+ }
+
+ const cc::LayerTreeHost& GetLayerTreeHost() {
+ return *web_layer_tree_view_->GetLayerTreeHost();
+ }
+
+ int ElementIdToEffectNodeIndex(CompositorElementId element_id) {
+ return web_layer_tree_view_->GetLayerTreeHost()
+ ->property_trees()
+ ->element_id_to_effect_node_index[element_id];
+ }
+
+ int ElementIdToTransformNodeIndex(CompositorElementId element_id) {
+ return web_layer_tree_view_->GetLayerTreeHost()
+ ->property_trees()
+ ->element_id_to_transform_node_index[element_id];
+ }
+
+ int ElementIdToScrollNodeIndex(CompositorElementId element_id) {
+ return web_layer_tree_view_->GetLayerTreeHost()
+ ->property_trees()
+ ->element_id_to_scroll_node_index[element_id];
+ }
+
+ const cc::TransformNode& GetTransformNode(const cc::Layer* layer) {
+ return *GetPropertyTrees().transform_tree.Node(
+ layer->transform_tree_index());
+ }
+
+ void Update(const PaintArtifact& artifact) {
+ CompositorElementIdSet element_ids;
+ Update(artifact, element_ids);
+ }
+
+ void Update(const PaintArtifact& artifact,
+ CompositorElementIdSet& element_ids) {
+ paint_artifact_compositor_->Update(artifact, element_ids);
+ web_layer_tree_view_->GetLayerTreeHost()->LayoutAndUpdateLayers();
+ }
+
+ void WillBeRemovedFromFrame() {
+ paint_artifact_compositor_->WillBeRemovedFromFrame();
+ }
+
+ cc::Layer* RootLayer() { return paint_artifact_compositor_->RootLayer(); }
+
+ size_t ContentLayerCount() {
+ return paint_artifact_compositor_->GetExtraDataForTesting()
+ ->content_layers.size();
+ }
+
+ cc::Layer* ContentLayerAt(unsigned index) {
+ return paint_artifact_compositor_->GetExtraDataForTesting()
+ ->content_layers[index]
+ .get();
+ }
+
+ CompositorElementId ScrollElementId(unsigned id) {
+ return CompositorElementIdFromUniqueObjectId(
+ id, CompositorElementIdNamespace::kScroll);
+ }
+
+ size_t SynthesizedClipLayerCount() {
+ return paint_artifact_compositor_->GetExtraDataForTesting()
+ ->synthesized_clip_layers.size();
+ }
+
+ cc::Layer* SynthesizedClipLayerAt(unsigned index) {
+ return paint_artifact_compositor_->GetExtraDataForTesting()
+ ->synthesized_clip_layers[index]
+ .get();
+ }
+
+ cc::Layer* ScrollHitTestLayerAt(unsigned index) {
+ return paint_artifact_compositor_->GetExtraDataForTesting()
+ ->scroll_hit_test_layers[index]
+ .get();
+ }
+
+ // Return the index of |layer| in the root layer list, or -1 if not found.
+ int LayerIndex(const cc::Layer* layer) {
+ for (size_t i = 0; i < RootLayer()->children().size(); ++i) {
+ if (RootLayer()->children()[i] == layer)
+ return i;
+ }
+ return -1;
+ }
+
+ void AddSimpleRectChunk(TestPaintArtifact& artifact) {
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(),
+ ClipPaintPropertyNode::Root(), EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(100, 100, 200, 100), Color::kBlack);
+ }
+
+ void CreateSimpleArtifactWithOpacity(TestPaintArtifact& artifact,
+ float opacity,
+ bool include_preceding_chunk,
+ bool include_subsequent_chunk) {
+ if (include_preceding_chunk)
+ AddSimpleRectChunk(artifact);
+ scoped_refptr<EffectPaintPropertyNode> effect =
+ CreateOpacityOnlyEffect(EffectPaintPropertyNode::Root(), opacity);
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(),
+ ClipPaintPropertyNode::Root(), effect)
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack);
+ if (include_subsequent_chunk)
+ AddSimpleRectChunk(artifact);
+ Update(artifact.Build());
+ }
+
+ using PendingLayer = PaintArtifactCompositor::PendingLayer;
+
+ bool MightOverlap(const PendingLayer& a, const PendingLayer& b) {
+ return PaintArtifactCompositor::MightOverlap(a, b);
+ }
+
+ FakeScrollClient& ScrollClient() { return scroll_client_; }
+
+ private:
+ FakeScrollClient scroll_client_;
+ std::unique_ptr<PaintArtifactCompositor> paint_artifact_compositor_;
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+ base::ThreadTaskRunnerHandle task_runner_handle_;
+ std::unique_ptr<WebLayerTreeViewWithLayerTreeFrameSink> web_layer_tree_view_;
+};
+
+const auto kNotScrollingOnMain = MainThreadScrollingReason::kNotScrollingOnMain;
+
+// Convenient shorthands.
+const TransformPaintPropertyNode* t0() {
+ return TransformPaintPropertyNode::Root();
+}
+const ClipPaintPropertyNode* c0() {
+ return ClipPaintPropertyNode::Root();
+}
+const EffectPaintPropertyNode* e0() {
+ return EffectPaintPropertyNode::Root();
+}
+
+TEST_F(PaintArtifactCompositorTest, EmptyPaintArtifact) {
+ PaintArtifact empty_artifact;
+ Update(empty_artifact);
+ EXPECT_TRUE(RootLayer()->children().empty());
+}
+
+TEST_F(PaintArtifactCompositorTest, OneChunkWithAnOffset) {
+ TestPaintArtifact artifact;
+ artifact.Chunk(DefaultPaintChunkProperties())
+ .RectDrawing(FloatRect(50, -50, 100, 100), Color::kWhite);
+ Update(artifact.Build());
+
+ ASSERT_EQ(1u, ContentLayerCount());
+ const cc::Layer* child = ContentLayerAt(0);
+ EXPECT_THAT(
+ child->GetPicture(),
+ Pointee(DrawsRectangle(FloatRect(0, 0, 100, 100), Color::kWhite)));
+ EXPECT_EQ(Translation(50, -50), child->ScreenSpaceTransform());
+ EXPECT_EQ(gfx::Size(100, 100), child->bounds());
+}
+
+TEST_F(PaintArtifactCompositorTest, OneTransform) {
+ // A 90 degree clockwise rotation about (100, 100).
+ scoped_refptr<TransformPaintPropertyNode> transform =
+ TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(), TransformationMatrix().Rotate(90),
+ FloatPoint3D(100, 100, 0), false, 0, CompositingReason::k3DTransform);
+
+ TestPaintArtifact artifact;
+ artifact
+ .Chunk(transform, ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite);
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kGray);
+ artifact
+ .Chunk(transform, ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(100, 100, 200, 100), Color::kBlack);
+ Update(artifact.Build());
+
+ ASSERT_EQ(2u, ContentLayerCount());
+ {
+ const cc::Layer* layer = ContentLayerAt(0);
+
+ Vector<RectWithColor> rects_with_color;
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(0, 0, 100, 100), Color::kWhite));
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(100, 100, 200, 100), Color::kBlack));
+
+ EXPECT_THAT(layer->GetPicture(),
+ Pointee(DrawsRectangles(rects_with_color)));
+ gfx::RectF mapped_rect(0, 0, 100, 100);
+ layer->ScreenSpaceTransform().TransformRect(&mapped_rect);
+ EXPECT_EQ(gfx::RectF(100, 0, 100, 100), mapped_rect);
+ }
+ {
+ const cc::Layer* layer = ContentLayerAt(1);
+ EXPECT_THAT(
+ layer->GetPicture(),
+ Pointee(DrawsRectangle(FloatRect(0, 0, 100, 100), Color::kGray)));
+ EXPECT_EQ(gfx::Transform(), layer->ScreenSpaceTransform());
+ }
+}
+
+TEST_F(PaintArtifactCompositorTest, TransformCombining) {
+ // A translation by (5, 5) within a 2x scale about (10, 10).
+ scoped_refptr<TransformPaintPropertyNode> transform1 =
+ TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(), TransformationMatrix().Scale(2),
+ FloatPoint3D(10, 10, 0), false, 0, CompositingReason::k3DTransform);
+ scoped_refptr<TransformPaintPropertyNode> transform2 =
+ TransformPaintPropertyNode::Create(
+ transform1, TransformationMatrix().Translate(5, 5), FloatPoint3D(),
+ false, 0, CompositingReason::k3DTransform);
+
+ TestPaintArtifact artifact;
+ artifact
+ .Chunk(transform1, ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 300, 200), Color::kWhite);
+ artifact
+ .Chunk(transform2, ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 300, 200), Color::kBlack);
+ Update(artifact.Build());
+
+ ASSERT_EQ(2u, ContentLayerCount());
+ {
+ const cc::Layer* layer = ContentLayerAt(0);
+ EXPECT_THAT(
+ layer->GetPicture(),
+ Pointee(DrawsRectangle(FloatRect(0, 0, 300, 200), Color::kWhite)));
+ gfx::RectF mapped_rect(0, 0, 300, 200);
+ layer->ScreenSpaceTransform().TransformRect(&mapped_rect);
+ EXPECT_EQ(gfx::RectF(-10, -10, 600, 400), mapped_rect);
+ }
+ {
+ const cc::Layer* layer = ContentLayerAt(1);
+ EXPECT_THAT(
+ layer->GetPicture(),
+ Pointee(DrawsRectangle(FloatRect(0, 0, 300, 200), Color::kBlack)));
+ gfx::RectF mapped_rect(0, 0, 300, 200);
+ layer->ScreenSpaceTransform().TransformRect(&mapped_rect);
+ EXPECT_EQ(gfx::RectF(0, 0, 600, 400), mapped_rect);
+ }
+ EXPECT_NE(ContentLayerAt(0)->transform_tree_index(),
+ ContentLayerAt(1)->transform_tree_index());
+}
+
+TEST_F(PaintArtifactCompositorTest, FlattensInheritedTransform) {
+ for (bool transform_is_flattened : {true, false}) {
+ SCOPED_TRACE(transform_is_flattened);
+
+ // The flattens_inherited_transform bit corresponds to whether the _parent_
+ // transform node flattens the transform. This is because Blink's notion of
+ // flattening determines whether content within the node's local transform
+ // is flattened, while cc's notion applies in the parent's coordinate space.
+ scoped_refptr<TransformPaintPropertyNode> transform1 =
+ TransformPaintPropertyNode::Create(TransformPaintPropertyNode::Root(),
+ TransformationMatrix(),
+ FloatPoint3D());
+ scoped_refptr<TransformPaintPropertyNode> transform2 =
+ TransformPaintPropertyNode::Create(
+ transform1, TransformationMatrix().Rotate3d(0, 45, 0),
+ FloatPoint3D());
+ scoped_refptr<TransformPaintPropertyNode> transform3 =
+ TransformPaintPropertyNode::Create(
+ transform2, TransformationMatrix().Rotate3d(0, 45, 0),
+ FloatPoint3D(), transform_is_flattened);
+
+ TestPaintArtifact artifact;
+ artifact
+ .Chunk(transform3, ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 300, 200), Color::kWhite);
+ Update(artifact.Build());
+
+ ASSERT_EQ(1u, ContentLayerCount());
+ const cc::Layer* layer = ContentLayerAt(0);
+ EXPECT_THAT(
+ layer->GetPicture(),
+ Pointee(DrawsRectangle(FloatRect(0, 0, 300, 200), Color::kWhite)));
+
+ // The leaf transform node should flatten its inherited transform node
+ // if and only if the intermediate rotation transform in the Blink tree
+ // flattens.
+ const cc::TransformNode* transform_node3 =
+ GetPropertyTrees().transform_tree.Node(layer->transform_tree_index());
+ EXPECT_EQ(transform_is_flattened,
+ transform_node3->flattens_inherited_transform);
+
+ // Given this, we should expect the correct screen space transform for
+ // each case. If the transform was flattened, we should see it getting
+ // an effective horizontal scale of 1/sqrt(2) each time, thus it gets
+ // half as wide. If the transform was not flattened, we should see an
+ // empty rectangle (as the total 90 degree rotation makes it
+ // perpendicular to the viewport).
+ gfx::RectF rect(0, 0, 100, 100);
+ layer->ScreenSpaceTransform().TransformRect(&rect);
+ if (transform_is_flattened)
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(0, 0, 50, 100), rect);
+ else
+ EXPECT_TRUE(rect.IsEmpty());
+ }
+}
+
+TEST_F(PaintArtifactCompositorTest, SortingContextID) {
+ // Has no 3D rendering context.
+ scoped_refptr<TransformPaintPropertyNode> transform1 =
+ TransformPaintPropertyNode::Create(TransformPaintPropertyNode::Root(),
+ TransformationMatrix(),
+ FloatPoint3D());
+ // Establishes a 3D rendering context.
+ scoped_refptr<TransformPaintPropertyNode> transform2 =
+ TransformPaintPropertyNode::Create(transform1, TransformationMatrix(),
+ FloatPoint3D(), false, 1,
+ CompositingReason::k3DTransform);
+ // Extends the 3D rendering context of transform2.
+ scoped_refptr<TransformPaintPropertyNode> transform3 =
+ TransformPaintPropertyNode::Create(transform2, TransformationMatrix(),
+ FloatPoint3D(), false, 1,
+ CompositingReason::k3DTransform);
+ // Establishes a 3D rendering context distinct from transform2.
+ scoped_refptr<TransformPaintPropertyNode> transform4 =
+ TransformPaintPropertyNode::Create(transform2, TransformationMatrix(),
+ FloatPoint3D(), false, 2,
+ CompositingReason::k3DTransform);
+
+ TestPaintArtifact artifact;
+ artifact
+ .Chunk(transform1, ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 300, 200), Color::kWhite);
+ artifact
+ .Chunk(transform2, ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 300, 200), Color::kLightGray);
+ artifact
+ .Chunk(transform3, ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 300, 200), Color::kDarkGray);
+ artifact
+ .Chunk(transform4, ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 300, 200), Color::kBlack);
+ Update(artifact.Build());
+
+ ASSERT_EQ(4u, ContentLayerCount());
+
+ // The white layer is not 3D sorted.
+ const cc::Layer* white_layer = ContentLayerAt(0);
+ EXPECT_THAT(
+ white_layer->GetPicture(),
+ Pointee(DrawsRectangle(FloatRect(0, 0, 300, 200), Color::kWhite)));
+ int white_sorting_context_id =
+ GetTransformNode(white_layer).sorting_context_id;
+ EXPECT_EQ(white_layer->sorting_context_id(), white_sorting_context_id);
+ EXPECT_EQ(0, white_sorting_context_id);
+
+ // The light gray layer is 3D sorted.
+ const cc::Layer* light_gray_layer = ContentLayerAt(1);
+ EXPECT_THAT(
+ light_gray_layer->GetPicture(),
+ Pointee(DrawsRectangle(FloatRect(0, 0, 300, 200), Color::kLightGray)));
+ int light_gray_sorting_context_id =
+ GetTransformNode(light_gray_layer).sorting_context_id;
+ EXPECT_NE(0, light_gray_sorting_context_id);
+
+ // The dark gray layer is 3D sorted with the light gray layer, but has a
+ // separate transform node.
+ const cc::Layer* dark_gray_layer = ContentLayerAt(2);
+ EXPECT_THAT(
+ dark_gray_layer->GetPicture(),
+ Pointee(DrawsRectangle(FloatRect(0, 0, 300, 200), Color::kDarkGray)));
+ int dark_gray_sorting_context_id =
+ GetTransformNode(dark_gray_layer).sorting_context_id;
+ EXPECT_EQ(light_gray_sorting_context_id, dark_gray_sorting_context_id);
+ EXPECT_NE(light_gray_layer->transform_tree_index(),
+ dark_gray_layer->transform_tree_index());
+
+ // The black layer is 3D sorted, but in a separate context from the previous
+ // layers.
+ const cc::Layer* black_layer = ContentLayerAt(3);
+ EXPECT_THAT(
+ black_layer->GetPicture(),
+ Pointee(DrawsRectangle(FloatRect(0, 0, 300, 200), Color::kBlack)));
+ int black_sorting_context_id =
+ GetTransformNode(black_layer).sorting_context_id;
+ EXPECT_NE(0, black_sorting_context_id);
+ EXPECT_NE(light_gray_sorting_context_id, black_sorting_context_id);
+}
+
+TEST_F(PaintArtifactCompositorTest, OneClip) {
+ scoped_refptr<ClipPaintPropertyNode> clip = ClipPaintPropertyNode::Create(
+ ClipPaintPropertyNode::Root(), TransformPaintPropertyNode::Root(),
+ FloatRoundedRect(100, 100, 300, 200));
+
+ TestPaintArtifact artifact;
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), clip,
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(220, 80, 300, 200), Color::kBlack);
+ Update(artifact.Build());
+
+ ASSERT_EQ(1u, ContentLayerCount());
+ const cc::Layer* layer = ContentLayerAt(0);
+ // The layer is clipped.
+ EXPECT_EQ(gfx::Size(180, 180), layer->bounds());
+ EXPECT_EQ(gfx::Vector2dF(220, 100), layer->offset_to_transform_parent());
+ EXPECT_THAT(
+ layer->GetPicture(),
+ Pointee(DrawsRectangle(FloatRect(0, 0, 300, 180), Color::kBlack)));
+ EXPECT_EQ(Translation(220, 100), layer->ScreenSpaceTransform());
+
+ const cc::ClipNode* clip_node =
+ GetPropertyTrees().clip_tree.Node(layer->clip_tree_index());
+ EXPECT_EQ(cc::ClipNode::ClipType::APPLIES_LOCAL_CLIP, clip_node->clip_type);
+ EXPECT_EQ(gfx::RectF(100, 100, 300, 200), clip_node->clip);
+}
+
+TEST_F(PaintArtifactCompositorTest, NestedClips) {
+ scoped_refptr<ClipPaintPropertyNode> clip1 = ClipPaintPropertyNode::Create(
+ ClipPaintPropertyNode::Root(), TransformPaintPropertyNode::Root(),
+ FloatRoundedRect(100, 100, 700, 700), nullptr, nullptr,
+ CompositingReason::kOverflowScrollingTouch);
+ scoped_refptr<ClipPaintPropertyNode> clip2 = ClipPaintPropertyNode::Create(
+ clip1, TransformPaintPropertyNode::Root(),
+ FloatRoundedRect(200, 200, 700, 700), nullptr, nullptr,
+ CompositingReason::kOverflowScrollingTouch);
+
+ TestPaintArtifact artifact;
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), clip1,
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(300, 350, 100, 100), Color::kWhite);
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), clip2,
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(300, 350, 100, 100), Color::kLightGray);
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), clip1,
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(300, 350, 100, 100), Color::kDarkGray);
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), clip2,
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(300, 350, 100, 100), Color::kBlack);
+ Update(artifact.Build());
+
+ ASSERT_EQ(4u, ContentLayerCount());
+
+ const cc::Layer* white_layer = ContentLayerAt(0);
+ EXPECT_THAT(
+ white_layer->GetPicture(),
+ Pointee(DrawsRectangle(FloatRect(0, 0, 100, 100), Color::kWhite)));
+ EXPECT_EQ(Translation(300, 350), white_layer->ScreenSpaceTransform());
+
+ const cc::Layer* light_gray_layer = ContentLayerAt(1);
+ EXPECT_THAT(
+ light_gray_layer->GetPicture(),
+ Pointee(DrawsRectangle(FloatRect(0, 0, 100, 100), Color::kLightGray)));
+ EXPECT_EQ(Translation(300, 350), light_gray_layer->ScreenSpaceTransform());
+
+ const cc::Layer* dark_gray_layer = ContentLayerAt(2);
+ EXPECT_THAT(
+ dark_gray_layer->GetPicture(),
+ Pointee(DrawsRectangle(FloatRect(0, 0, 100, 100), Color::kDarkGray)));
+ EXPECT_EQ(Translation(300, 350), dark_gray_layer->ScreenSpaceTransform());
+
+ const cc::Layer* black_layer = ContentLayerAt(3);
+ EXPECT_THAT(
+ black_layer->GetPicture(),
+ Pointee(DrawsRectangle(FloatRect(0, 0, 100, 100), Color::kBlack)));
+ EXPECT_EQ(Translation(300, 350), black_layer->ScreenSpaceTransform());
+
+ EXPECT_EQ(white_layer->clip_tree_index(), dark_gray_layer->clip_tree_index());
+ const cc::ClipNode* outer_clip =
+ GetPropertyTrees().clip_tree.Node(white_layer->clip_tree_index());
+ EXPECT_EQ(cc::ClipNode::ClipType::APPLIES_LOCAL_CLIP, outer_clip->clip_type);
+ EXPECT_EQ(gfx::RectF(100, 100, 700, 700), outer_clip->clip);
+
+ EXPECT_EQ(light_gray_layer->clip_tree_index(),
+ black_layer->clip_tree_index());
+ const cc::ClipNode* inner_clip =
+ GetPropertyTrees().clip_tree.Node(black_layer->clip_tree_index());
+ EXPECT_EQ(cc::ClipNode::ClipType::APPLIES_LOCAL_CLIP, inner_clip->clip_type);
+ EXPECT_EQ(gfx::RectF(200, 200, 700, 700), inner_clip->clip);
+ EXPECT_EQ(outer_clip->id, inner_clip->parent_id);
+}
+
+TEST_F(PaintArtifactCompositorTest, DeeplyNestedClips) {
+ Vector<scoped_refptr<ClipPaintPropertyNode>> clips;
+ for (unsigned i = 1; i <= 10; i++) {
+ clips.push_back(ClipPaintPropertyNode::Create(
+ clips.IsEmpty() ? ClipPaintPropertyNode::Root() : clips.back(),
+ TransformPaintPropertyNode::Root(),
+ FloatRoundedRect(5 * i, 0, 100, 200 - 10 * i)));
+ }
+
+ TestPaintArtifact artifact;
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), clips.back(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 200, 200), Color::kWhite);
+ Update(artifact.Build());
+
+ // Check the drawing layer. It's clipped.
+ ASSERT_EQ(1u, ContentLayerCount());
+ const cc::Layer* drawing_layer = ContentLayerAt(0);
+ EXPECT_EQ(gfx::Size(100, 100), drawing_layer->bounds());
+ EXPECT_EQ(gfx::Vector2dF(50, 0), drawing_layer->offset_to_transform_parent());
+ EXPECT_THAT(
+ drawing_layer->GetPicture(),
+ Pointee(DrawsRectangle(FloatRect(0, 0, 150, 200), Color::kWhite)));
+ EXPECT_EQ(Translation(50, 0), drawing_layer->ScreenSpaceTransform());
+
+ // Check the clip nodes.
+ const cc::ClipNode* clip_node =
+ GetPropertyTrees().clip_tree.Node(drawing_layer->clip_tree_index());
+ for (auto it = clips.rbegin(); it != clips.rend(); ++it) {
+ const ClipPaintPropertyNode* paint_clip_node = it->get();
+ EXPECT_EQ(cc::ClipNode::ClipType::APPLIES_LOCAL_CLIP, clip_node->clip_type);
+ EXPECT_EQ(paint_clip_node->ClipRect().Rect(), clip_node->clip);
+ clip_node = GetPropertyTrees().clip_tree.Node(clip_node->parent_id);
+ }
+}
+
+TEST_F(PaintArtifactCompositorTest, SiblingClips) {
+ scoped_refptr<ClipPaintPropertyNode> common_clip =
+ ClipPaintPropertyNode::Create(ClipPaintPropertyNode::Root(),
+ TransformPaintPropertyNode::Root(),
+ FloatRoundedRect(0, 0, 800, 600));
+ scoped_refptr<ClipPaintPropertyNode> clip1 = ClipPaintPropertyNode::Create(
+ common_clip, TransformPaintPropertyNode::Root(),
+ FloatRoundedRect(0, 0, 400, 600));
+ scoped_refptr<ClipPaintPropertyNode> clip2 = ClipPaintPropertyNode::Create(
+ common_clip, TransformPaintPropertyNode::Root(),
+ FloatRoundedRect(400, 0, 400, 600));
+
+ TestPaintArtifact artifact;
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), clip1,
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 640, 480), Color::kWhite);
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), clip2,
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 640, 480), Color::kBlack);
+ Update(artifact.Build());
+ ASSERT_EQ(2u, ContentLayerCount());
+
+ const cc::Layer* white_layer = ContentLayerAt(0);
+ EXPECT_THAT(
+ white_layer->GetPicture(),
+ Pointee(DrawsRectangle(FloatRect(0, 0, 640, 480), Color::kWhite)));
+ EXPECT_EQ(gfx::Transform(), white_layer->ScreenSpaceTransform());
+ const cc::ClipNode* white_clip =
+ GetPropertyTrees().clip_tree.Node(white_layer->clip_tree_index());
+ EXPECT_EQ(cc::ClipNode::ClipType::APPLIES_LOCAL_CLIP, white_clip->clip_type);
+ ASSERT_EQ(gfx::RectF(0, 0, 400, 600), white_clip->clip);
+
+ const cc::Layer* black_layer = ContentLayerAt(1);
+ // The layer is clipped.
+ EXPECT_EQ(gfx::Size(240, 480), black_layer->bounds());
+ EXPECT_EQ(gfx::Vector2dF(400, 0), black_layer->offset_to_transform_parent());
+ EXPECT_THAT(
+ black_layer->GetPicture(),
+ Pointee(DrawsRectangle(FloatRect(0, 0, 240, 480), Color::kBlack)));
+ EXPECT_EQ(Translation(400, 0), black_layer->ScreenSpaceTransform());
+ const cc::ClipNode* black_clip =
+ GetPropertyTrees().clip_tree.Node(black_layer->clip_tree_index());
+ EXPECT_EQ(cc::ClipNode::ClipType::APPLIES_LOCAL_CLIP, black_clip->clip_type);
+ ASSERT_EQ(gfx::RectF(400, 0, 400, 600), black_clip->clip);
+
+ EXPECT_EQ(white_clip->parent_id, black_clip->parent_id);
+ const cc::ClipNode* common_clip_node =
+ GetPropertyTrees().clip_tree.Node(white_clip->parent_id);
+ EXPECT_EQ(cc::ClipNode::ClipType::APPLIES_LOCAL_CLIP,
+ common_clip_node->clip_type);
+ ASSERT_EQ(gfx::RectF(0, 0, 800, 600), common_clip_node->clip);
+}
+
+TEST_F(PaintArtifactCompositorTest, ForeignLayerPassesThrough) {
+ scoped_refptr<cc::Layer> layer = cc::Layer::Create();
+
+ TestPaintArtifact test_artifact;
+ test_artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite);
+ test_artifact.Chunk(DefaultPaintChunkProperties())
+ .ForeignLayer(FloatPoint(50, 60), IntSize(400, 300), layer);
+ test_artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kGray);
+
+ const PaintArtifact& artifact = test_artifact.Build();
+ ASSERT_EQ(3u, artifact.PaintChunks().size());
+ Update(artifact);
+
+ ASSERT_EQ(3u, ContentLayerCount());
+ EXPECT_EQ(layer, ContentLayerAt(1));
+ EXPECT_EQ(gfx::Size(400, 300), layer->bounds());
+ EXPECT_EQ(Translation(50, 60), layer->ScreenSpaceTransform());
+}
+
+TEST_F(PaintArtifactCompositorTest, EffectTreeConversion) {
+ scoped_refptr<EffectPaintPropertyNode> effect1 =
+ EffectPaintPropertyNode::Create(
+ EffectPaintPropertyNode::Root(), TransformPaintPropertyNode::Root(),
+ ClipPaintPropertyNode::Root(), kColorFilterNone,
+ CompositorFilterOperations(), 0.5, SkBlendMode::kSrcOver,
+ CompositingReason::kAll, CompositorElementId(2));
+ scoped_refptr<EffectPaintPropertyNode> effect2 =
+ EffectPaintPropertyNode::Create(
+ effect1, TransformPaintPropertyNode::Root(),
+ ClipPaintPropertyNode::Root(), kColorFilterNone,
+ CompositorFilterOperations(), 0.3, SkBlendMode::kSrcOver,
+ CompositingReason::kAll);
+ scoped_refptr<EffectPaintPropertyNode> effect3 =
+ EffectPaintPropertyNode::Create(
+ EffectPaintPropertyNode::Root(), TransformPaintPropertyNode::Root(),
+ ClipPaintPropertyNode::Root(), kColorFilterNone,
+ CompositorFilterOperations(), 0.2, SkBlendMode::kSrcOver,
+ CompositingReason::kAll);
+
+ TestPaintArtifact artifact;
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ effect2.get())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite);
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ effect1.get())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite);
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ effect3.get())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite);
+ Update(artifact.Build());
+
+ ASSERT_EQ(3u, ContentLayerCount());
+
+ const cc::EffectTree& effect_tree = GetPropertyTrees().effect_tree;
+ // Node #0 reserved for null; #1 for root render surface; #2 for
+ // EffectPaintPropertyNode::root(), plus 3 nodes for those created by
+ // this test.
+ ASSERT_EQ(5u, effect_tree.size());
+
+ const cc::EffectNode& converted_root_effect = *effect_tree.Node(1);
+ EXPECT_EQ(-1, converted_root_effect.parent_id);
+ EXPECT_EQ(CompositorElementIdFromUniqueObjectId(1).ToInternalValue(),
+ converted_root_effect.stable_id);
+
+ const cc::EffectNode& converted_effect1 = *effect_tree.Node(2);
+ EXPECT_EQ(converted_root_effect.id, converted_effect1.parent_id);
+ EXPECT_FLOAT_EQ(0.5, converted_effect1.opacity);
+ EXPECT_EQ(2u, converted_effect1.stable_id);
+
+ const cc::EffectNode& converted_effect2 = *effect_tree.Node(3);
+ EXPECT_EQ(converted_effect1.id, converted_effect2.parent_id);
+ EXPECT_FLOAT_EQ(0.3, converted_effect2.opacity);
+
+ const cc::EffectNode& converted_effect3 = *effect_tree.Node(4);
+ EXPECT_EQ(converted_root_effect.id, converted_effect3.parent_id);
+ EXPECT_FLOAT_EQ(0.2, converted_effect3.opacity);
+
+ EXPECT_EQ(converted_effect2.id, ContentLayerAt(0)->effect_tree_index());
+ EXPECT_EQ(converted_effect1.id, ContentLayerAt(1)->effect_tree_index());
+ EXPECT_EQ(converted_effect3.id, ContentLayerAt(2)->effect_tree_index());
+}
+
+TEST_F(PaintArtifactCompositorTest, OneScrollNode) {
+ CompositorElementId scroll_element_id = ScrollElementId(2);
+ scoped_refptr<ScrollPaintPropertyNode> scroll =
+ ScrollPaintPropertyNode::Create(ScrollPaintPropertyNode::Root(),
+ IntRect(3, 5, 11, 13),
+ IntRect(-3, -5, 27, 31), true, false,
+ kNotScrollingOnMain, scroll_element_id);
+ scoped_refptr<TransformPaintPropertyNode> scroll_translation =
+ TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(),
+ TransformationMatrix().Translate(7, 9), FloatPoint3D(), false, 0,
+ CompositingReason::kNone, CompositorElementId(), scroll);
+
+ TestPaintArtifact artifact;
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .ScrollHitTest(scroll_translation);
+ artifact
+ .Chunk(scroll_translation, ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(-110, 12, 170, 19), Color::kWhite);
+ Update(artifact.Build());
+
+ const cc::ScrollTree& scroll_tree = GetPropertyTrees().scroll_tree;
+ // Node #0 reserved for null; #1 for root render surface.
+ ASSERT_EQ(3u, scroll_tree.size());
+ const cc::ScrollNode& scroll_node = *scroll_tree.Node(2);
+ EXPECT_EQ(gfx::Size(11, 13), scroll_node.container_bounds);
+ EXPECT_EQ(gfx::Size(27, 31), scroll_node.bounds);
+ EXPECT_TRUE(scroll_node.user_scrollable_horizontal);
+ EXPECT_FALSE(scroll_node.user_scrollable_vertical);
+ EXPECT_EQ(1, scroll_node.parent_id);
+ EXPECT_EQ(scroll_element_id, scroll_node.element_id);
+ EXPECT_EQ(scroll_node.id, ElementIdToScrollNodeIndex(scroll_element_id));
+ EXPECT_EQ(scroll_element_id, ScrollHitTestLayerAt(0)->element_id());
+
+ const cc::TransformTree& transform_tree = GetPropertyTrees().transform_tree;
+ const cc::TransformNode& transform_node =
+ *transform_tree.Node(scroll_node.transform_id);
+ EXPECT_TRUE(transform_node.local.IsIdentity());
+ EXPECT_EQ(gfx::ScrollOffset(-7, -9), transform_node.scroll_offset);
+ EXPECT_EQ(kNotScrollingOnMain, scroll_node.main_thread_scrolling_reasons);
+
+ auto* layer = ContentLayerAt(0);
+ auto transform_node_index = layer->transform_tree_index();
+ EXPECT_EQ(transform_node_index, transform_node.id);
+ auto scroll_node_index = layer->scroll_tree_index();
+ EXPECT_EQ(scroll_node_index, scroll_node.id);
+
+ // The scrolling contents layer is clipped to the scrolling range.
+ EXPECT_EQ(gfx::Size(27, 14), layer->bounds());
+ EXPECT_EQ(gfx::Vector2dF(-3, 12), layer->offset_to_transform_parent());
+ EXPECT_THAT(layer->GetPicture(),
+ Pointee(DrawsRectangle(FloatRect(0, 0, 63, 19), Color::kWhite)));
+
+ auto* scroll_layer = ScrollHitTestLayerAt(0);
+ EXPECT_TRUE(scroll_layer->scrollable());
+ // The scroll layer should be sized to the container bounds.
+ // TODO(pdr): The container bounds will not include scrollbars but the scroll
+ // layer should extend below scrollbars.
+ EXPECT_EQ(gfx::Size(11, 13), scroll_layer->bounds());
+ EXPECT_EQ(gfx::Vector2dF(3, 5), scroll_layer->offset_to_transform_parent());
+ EXPECT_EQ(scroll_layer->scroll_tree_index(), scroll_node.id);
+ EXPECT_EQ(scroll_layer->transform_tree_index(), transform_node.parent_id);
+
+ EXPECT_EQ(0u, ScrollClient().did_scroll_count);
+ scroll_layer->SetScrollOffsetFromImplSide(gfx::ScrollOffset(1, 2));
+ EXPECT_EQ(1u, ScrollClient().did_scroll_count);
+ EXPECT_EQ(gfx::ScrollOffset(1, 2), ScrollClient().last_scroll_offset);
+}
+
+TEST_F(PaintArtifactCompositorTest, TransformUnderScrollNode) {
+ scoped_refptr<ScrollPaintPropertyNode> scroll =
+ ScrollPaintPropertyNode::Create(
+ ScrollPaintPropertyNode::Root(), IntRect(0, 0, 11, 13),
+ IntRect(-3, -5, 27, 31), true, false, kNotScrollingOnMain,
+ CompositorElementId());
+ scoped_refptr<TransformPaintPropertyNode> scroll_translation =
+ TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(),
+ TransformationMatrix().Translate(7, 9), FloatPoint3D(), false, 0,
+ CompositingReason::kNone, CompositorElementId(), scroll);
+
+ scoped_refptr<TransformPaintPropertyNode> transform =
+ TransformPaintPropertyNode::Create(
+ scroll_translation, TransformationMatrix(), FloatPoint3D(), false, 0,
+ CompositingReason::k3DTransform);
+
+ TestPaintArtifact artifact;
+ artifact
+ .Chunk(scroll_translation, ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(-20, 4, 60, 8), Color::kBlack)
+ .Chunk(transform, ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(1, -30, 5, 70), Color::kWhite);
+ Update(artifact.Build());
+
+ const cc::ScrollTree& scroll_tree = GetPropertyTrees().scroll_tree;
+ // Node #0 reserved for null; #1 for root render surface.
+ ASSERT_EQ(3u, scroll_tree.size());
+ const cc::ScrollNode& scroll_node = *scroll_tree.Node(2);
+
+ // Both layers should refer to the same scroll tree node.
+ const auto* layer0 = ContentLayerAt(0);
+ const auto* layer1 = ContentLayerAt(1);
+ EXPECT_EQ(scroll_node.id, layer0->scroll_tree_index());
+ EXPECT_EQ(scroll_node.id, layer1->scroll_tree_index());
+
+ // The scrolling layer is clipped to the scrollable range.
+ EXPECT_EQ(gfx::Vector2dF(-3, 4), layer0->offset_to_transform_parent());
+ EXPECT_EQ(gfx::Size(27, 8), layer0->bounds());
+ EXPECT_THAT(layer0->GetPicture(),
+ Pointee(DrawsRectangle(FloatRect(0, 0, 43, 8), Color::kBlack)));
+
+ // The layer under the transform without a scroll node is not clipped.
+ EXPECT_EQ(gfx::Vector2dF(1, -30), layer1->offset_to_transform_parent());
+ EXPECT_EQ(gfx::Size(5, 70), layer1->bounds());
+ EXPECT_THAT(layer1->GetPicture(),
+ Pointee(DrawsRectangle(FloatRect(0, 0, 5, 70), Color::kWhite)));
+
+ const cc::TransformTree& transform_tree = GetPropertyTrees().transform_tree;
+ const cc::TransformNode& scroll_transform_node =
+ *transform_tree.Node(scroll_node.transform_id);
+ // The layers have different transform nodes.
+ EXPECT_EQ(scroll_transform_node.id, layer0->transform_tree_index());
+ EXPECT_NE(scroll_transform_node.id, layer1->transform_tree_index());
+}
+
+TEST_F(PaintArtifactCompositorTest, NestedScrollNodes) {
+ scoped_refptr<EffectPaintPropertyNode> effect =
+ CreateOpacityOnlyEffect(EffectPaintPropertyNode::Root(), 0.5);
+
+ CompositorElementId scroll_element_id_a = ScrollElementId(2);
+ scoped_refptr<ScrollPaintPropertyNode> scroll_a =
+ ScrollPaintPropertyNode::Create(
+ ScrollPaintPropertyNode::Root(), IntRect(0, 0, 2, 3),
+ IntRect(0, 0, 5, 7), false, true,
+ MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects,
+ scroll_element_id_a);
+ scoped_refptr<TransformPaintPropertyNode> scroll_translation_a =
+ TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(),
+ TransformationMatrix().Translate(11, 13), FloatPoint3D(), false, 0,
+ CompositingReason::kLayerForScrollingContents, CompositorElementId(),
+ scroll_a);
+
+ CompositorElementId scroll_element_id_b = ScrollElementId(3);
+ scoped_refptr<ScrollPaintPropertyNode> scroll_b =
+ ScrollPaintPropertyNode::Create(scroll_translation_a->ScrollNode(),
+ IntRect(0, 0, 19, 23),
+ IntRect(0, 0, 29, 31), true, false,
+ kNotScrollingOnMain, scroll_element_id_b);
+ scoped_refptr<TransformPaintPropertyNode> scroll_translation_b =
+ TransformPaintPropertyNode::Create(
+ scroll_translation_a, TransformationMatrix().Translate(37, 41),
+ FloatPoint3D(), false, 0, CompositingReason::kNone,
+ CompositorElementId(), scroll_b);
+ TestPaintArtifact artifact;
+ artifact.Chunk(scroll_translation_a, ClipPaintPropertyNode::Root(), effect)
+ .RectDrawing(FloatRect(7, 11, 13, 17), Color::kWhite);
+ artifact
+ .Chunk(scroll_translation_a->Parent(), ClipPaintPropertyNode::Root(),
+ effect)
+ .ScrollHitTest(scroll_translation_a);
+ artifact.Chunk(scroll_translation_b, ClipPaintPropertyNode::Root(), effect)
+ .RectDrawing(FloatRect(1, 2, 3, 5), Color::kWhite);
+ artifact
+ .Chunk(scroll_translation_b->Parent(), ClipPaintPropertyNode::Root(),
+ effect)
+ .ScrollHitTest(scroll_translation_b);
+ Update(artifact.Build());
+
+ const cc::ScrollTree& scroll_tree = GetPropertyTrees().scroll_tree;
+ // Node #0 reserved for null; #1 for root render surface.
+ ASSERT_EQ(4u, scroll_tree.size());
+ const cc::ScrollNode& scroll_node_a = *scroll_tree.Node(2);
+ EXPECT_EQ(gfx::Size(2, 3), scroll_node_a.container_bounds);
+ EXPECT_EQ(gfx::Size(5, 7), scroll_node_a.bounds);
+ EXPECT_FALSE(scroll_node_a.user_scrollable_horizontal);
+ EXPECT_TRUE(scroll_node_a.user_scrollable_vertical);
+ EXPECT_EQ(1, scroll_node_a.parent_id);
+ EXPECT_EQ(scroll_element_id_a, scroll_node_a.element_id);
+ EXPECT_EQ(scroll_node_a.id, ElementIdToScrollNodeIndex(scroll_element_id_a));
+ EXPECT_TRUE(scroll_node_a.main_thread_scrolling_reasons &
+ MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects);
+ EXPECT_EQ(scroll_element_id_a, ScrollHitTestLayerAt(0)->element_id());
+
+ const cc::TransformTree& transform_tree = GetPropertyTrees().transform_tree;
+ const cc::TransformNode& transform_node_a =
+ *transform_tree.Node(scroll_node_a.transform_id);
+ EXPECT_TRUE(transform_node_a.local.IsIdentity());
+ EXPECT_EQ(gfx::ScrollOffset(-11, -13), transform_node_a.scroll_offset);
+
+ const cc::ScrollNode& scroll_node_b = *scroll_tree.Node(3);
+ EXPECT_EQ(gfx::Size(19, 23), scroll_node_b.container_bounds);
+ EXPECT_EQ(gfx::Size(29, 31), scroll_node_b.bounds);
+ EXPECT_EQ(scroll_node_a.id, scroll_node_b.parent_id);
+ EXPECT_EQ(scroll_element_id_b, scroll_node_b.element_id);
+ EXPECT_EQ(scroll_node_b.id, ElementIdToScrollNodeIndex(scroll_element_id_b));
+ EXPECT_EQ(scroll_element_id_b, ScrollHitTestLayerAt(1)->element_id());
+
+ const cc::TransformNode& transform_node_b =
+ *transform_tree.Node(scroll_node_b.transform_id);
+ EXPECT_TRUE(transform_node_b.local.IsIdentity());
+ EXPECT_EQ(gfx::ScrollOffset(-37, -41), transform_node_b.scroll_offset);
+}
+
+TEST_F(PaintArtifactCompositorTest, ScrollHitTestLayerOrder) {
+ scoped_refptr<ClipPaintPropertyNode> clip = ClipPaintPropertyNode::Create(
+ ClipPaintPropertyNode::Root(), TransformPaintPropertyNode::Root(),
+ FloatRoundedRect(0, 0, 100, 100));
+
+ CompositorElementId scroll_element_id = ScrollElementId(2);
+ scoped_refptr<ScrollPaintPropertyNode> scroll =
+ ScrollPaintPropertyNode::Create(ScrollPaintPropertyNode::Root(),
+ IntRect(0, 0, 100, 100),
+ IntRect(0, 0, 100, 100), true, false,
+ kNotScrollingOnMain, scroll_element_id);
+ scoped_refptr<TransformPaintPropertyNode> scroll_translation =
+ TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(),
+ TransformationMatrix().Translate(7, 9), FloatPoint3D(), false, 0,
+ CompositingReason::kWillChangeCompositingHint, CompositorElementId(),
+ scroll);
+
+ scoped_refptr<TransformPaintPropertyNode> transform =
+ TransformPaintPropertyNode::Create(
+ scroll_translation, TransformationMatrix().Translate(5, 5),
+ FloatPoint3D(), false, 0, CompositingReason::k3DTransform);
+
+ TestPaintArtifact artifact;
+ artifact.Chunk(scroll_translation, clip, EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite);
+ artifact
+ .Chunk(scroll_translation->Parent(), clip,
+ EffectPaintPropertyNode::Root())
+ .ScrollHitTest(scroll_translation);
+ artifact.Chunk(transform, clip, EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 50, 50), Color::kBlack);
+ Update(artifact.Build());
+
+ // The first content layer (background) should not have the scrolling element
+ // id set.
+ EXPECT_EQ(CompositorElementId(), ContentLayerAt(0)->element_id());
+
+ // The scroll layer should be after the first content layer (background).
+ EXPECT_LT(LayerIndex(ContentLayerAt(0)), LayerIndex(ScrollHitTestLayerAt(0)));
+ const cc::ScrollTree& scroll_tree = GetPropertyTrees().scroll_tree;
+ auto* scroll_node =
+ scroll_tree.Node(ScrollHitTestLayerAt(0)->scroll_tree_index());
+ ASSERT_EQ(scroll_element_id, scroll_node->element_id);
+ EXPECT_EQ(scroll_element_id, ScrollHitTestLayerAt(0)->element_id());
+
+ // The second content layer should appear after the first.
+ EXPECT_LT(LayerIndex(ScrollHitTestLayerAt(0)), LayerIndex(ContentLayerAt(1)));
+ EXPECT_EQ(CompositorElementId(), ContentLayerAt(1)->element_id());
+}
+
+TEST_F(PaintArtifactCompositorTest, NestedScrollHitTestLayerOrder) {
+ scoped_refptr<ClipPaintPropertyNode> clip_1 = ClipPaintPropertyNode::Create(
+ ClipPaintPropertyNode::Root(), TransformPaintPropertyNode::Root(),
+ FloatRoundedRect(0, 0, 100, 100));
+ CompositorElementId scroll_1_element_id = ScrollElementId(1);
+ scoped_refptr<ScrollPaintPropertyNode> scroll_1 =
+ ScrollPaintPropertyNode::Create(ScrollPaintPropertyNode::Root(),
+ IntRect(0, 0, 100, 100),
+ IntRect(0, 0, 100, 100), true, false,
+ kNotScrollingOnMain, scroll_1_element_id);
+ scoped_refptr<TransformPaintPropertyNode> scroll_translation_1 =
+ TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(),
+ TransformationMatrix().Translate(7, 9), FloatPoint3D(), false, 0,
+ CompositingReason::kWillChangeCompositingHint, CompositorElementId(),
+ scroll_1);
+
+ scoped_refptr<ClipPaintPropertyNode> clip_2 = ClipPaintPropertyNode::Create(
+ clip_1, scroll_translation_1, FloatRoundedRect(0, 0, 50, 50));
+ CompositorElementId scroll_2_element_id = ScrollElementId(2);
+ scoped_refptr<ScrollPaintPropertyNode> scroll_2 =
+ ScrollPaintPropertyNode::Create(ScrollPaintPropertyNode::Root(),
+ IntRect(0, 0, 50, 50),
+ IntRect(0, 0, 50, 50), true, false,
+ kNotScrollingOnMain, scroll_2_element_id);
+ scoped_refptr<TransformPaintPropertyNode> scroll_translation_2 =
+ TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(),
+ TransformationMatrix().Translate(0, 0), FloatPoint3D(), false, 0,
+ CompositingReason::kWillChangeCompositingHint, CompositorElementId(),
+ scroll_2);
+
+ TestPaintArtifact artifact;
+ artifact
+ .Chunk(scroll_translation_1->Parent(), clip_1->Parent(),
+ EffectPaintPropertyNode::Root())
+ .ScrollHitTest(scroll_translation_1);
+ artifact
+ .Chunk(scroll_translation_2->Parent(), clip_2->Parent(),
+ EffectPaintPropertyNode::Root())
+ .ScrollHitTest(scroll_translation_2);
+ artifact.Chunk(scroll_translation_2, clip_2, EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 50, 50), Color::kWhite);
+ Update(artifact.Build());
+
+ // Two scroll layers should be created for each scroll translation node.
+ const cc::ScrollTree& scroll_tree = GetPropertyTrees().scroll_tree;
+ const cc::ClipTree& clip_tree = GetPropertyTrees().clip_tree;
+ auto* scroll_1_node =
+ scroll_tree.Node(ScrollHitTestLayerAt(0)->scroll_tree_index());
+ ASSERT_EQ(scroll_1_element_id, scroll_1_node->element_id);
+ auto* scroll_1_clip_node =
+ clip_tree.Node(ScrollHitTestLayerAt(0)->clip_tree_index());
+ // The scroll is not under clip_1.
+ EXPECT_EQ(gfx::RectF(0, 0, 0, 0), scroll_1_clip_node->clip);
+
+ auto* scroll_2_node =
+ scroll_tree.Node(ScrollHitTestLayerAt(1)->scroll_tree_index());
+ ASSERT_EQ(scroll_2_element_id, scroll_2_node->element_id);
+ auto* scroll_2_clip_node =
+ clip_tree.Node(ScrollHitTestLayerAt(1)->clip_tree_index());
+ // The scroll is not under clip_2 but is under the parent clip, clip_1.
+ EXPECT_EQ(gfx::RectF(0, 0, 100, 100), scroll_2_clip_node->clip);
+
+ // The first layer should be before the second scroll layer.
+ EXPECT_LT(LayerIndex(ScrollHitTestLayerAt(0)),
+ LayerIndex(ScrollHitTestLayerAt(1)));
+
+ // The content layer should be after the second scroll layer.
+ EXPECT_LT(LayerIndex(ScrollHitTestLayerAt(1)), LayerIndex(ContentLayerAt(0)));
+}
+
+// If a scroll node is encountered before its parent, ensure the parent scroll
+// node is correctly created.
+TEST_F(PaintArtifactCompositorTest, AncestorScrollNodes) {
+ CompositorElementId scroll_element_id_a = ScrollElementId(2);
+ scoped_refptr<ScrollPaintPropertyNode> scroll_a =
+ ScrollPaintPropertyNode::Create(ScrollPaintPropertyNode::Root(),
+ IntRect(0, 0, 2, 3), IntRect(0, 0, 5, 7),
+ false, true, kNotScrollingOnMain,
+ scroll_element_id_a);
+ scoped_refptr<TransformPaintPropertyNode> scroll_translation_a =
+ TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(),
+ TransformationMatrix().Translate(11, 13), FloatPoint3D(), false, 0,
+ CompositingReason::kLayerForScrollingContents, CompositorElementId(),
+ scroll_a);
+
+ CompositorElementId scroll_element_id_b = ScrollElementId(3);
+ scoped_refptr<ScrollPaintPropertyNode> scroll_b =
+ ScrollPaintPropertyNode::Create(scroll_translation_a->ScrollNode(),
+ IntRect(0, 0, 19, 23),
+ IntRect(0, 0, 29, 31), true, false,
+ kNotScrollingOnMain, scroll_element_id_b);
+ scoped_refptr<TransformPaintPropertyNode> scroll_translation_b =
+ TransformPaintPropertyNode::Create(
+ scroll_translation_a, TransformationMatrix().Translate(37, 41),
+ FloatPoint3D(), false, 0, CompositingReason::kNone,
+ CompositorElementId(), scroll_b);
+
+ TestPaintArtifact artifact;
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .ScrollHitTest(scroll_translation_b);
+ artifact
+ .Chunk(scroll_translation_b, ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .ScrollHitTest(scroll_translation_a);
+ Update(artifact.Build());
+
+ const cc::ScrollTree& scroll_tree = GetPropertyTrees().scroll_tree;
+ // Node #0 reserved for null; #1 for root render surface.
+ ASSERT_EQ(4u, scroll_tree.size());
+
+ const cc::ScrollNode& scroll_node_a = *scroll_tree.Node(2);
+ EXPECT_EQ(1, scroll_node_a.parent_id);
+ EXPECT_EQ(scroll_element_id_a, scroll_node_a.element_id);
+ EXPECT_EQ(scroll_node_a.id, ElementIdToScrollNodeIndex(scroll_element_id_a));
+ // The second scroll hit test layer should be associated with the first
+ // scroll node (a).
+ EXPECT_EQ(scroll_element_id_a, ScrollHitTestLayerAt(1)->element_id());
+
+ const cc::TransformTree& transform_tree = GetPropertyTrees().transform_tree;
+ const cc::TransformNode& transform_node_a =
+ *transform_tree.Node(scroll_node_a.transform_id);
+ EXPECT_TRUE(transform_node_a.local.IsIdentity());
+ EXPECT_EQ(gfx::ScrollOffset(-11, -13), transform_node_a.scroll_offset);
+
+ const cc::ScrollNode& scroll_node_b = *scroll_tree.Node(3);
+ EXPECT_EQ(scroll_node_a.id, scroll_node_b.parent_id);
+ EXPECT_EQ(scroll_element_id_b, scroll_node_b.element_id);
+ EXPECT_EQ(scroll_node_b.id, ElementIdToScrollNodeIndex(scroll_element_id_b));
+ // The first scroll hit test layer should be associated with the second scroll
+ // node (b).
+ EXPECT_EQ(scroll_element_id_b, ScrollHitTestLayerAt(0)->element_id());
+
+ const cc::TransformNode& transform_node_b =
+ *transform_tree.Node(scroll_node_b.transform_id);
+ EXPECT_TRUE(transform_node_b.local.IsIdentity());
+ EXPECT_EQ(gfx::ScrollOffset(-37, -41), transform_node_b.scroll_offset);
+}
+
+TEST_F(PaintArtifactCompositorTest, MergeSimpleChunks) {
+ TestPaintArtifact test_artifact;
+ test_artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite);
+ test_artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 200, 300), Color::kGray);
+
+ const PaintArtifact& artifact = test_artifact.Build();
+ ASSERT_EQ(2u, artifact.PaintChunks().size());
+ Update(artifact);
+
+ ASSERT_EQ(1u, ContentLayerCount());
+ {
+ Vector<RectWithColor> rects_with_color;
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(0, 0, 100, 100), Color::kWhite));
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(0, 0, 200, 300), Color::kGray));
+
+ const cc::Layer* layer = ContentLayerAt(0);
+ EXPECT_THAT(layer->GetPicture(),
+ Pointee(DrawsRectangles(rects_with_color)));
+ }
+}
+
+TEST_F(PaintArtifactCompositorTest, MergeClip) {
+ scoped_refptr<ClipPaintPropertyNode> clip = ClipPaintPropertyNode::Create(
+ ClipPaintPropertyNode::Root(), TransformPaintPropertyNode::Root(),
+ FloatRoundedRect(10, 20, 50, 60));
+
+ TestPaintArtifact test_artifact;
+ test_artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite);
+ test_artifact
+ .Chunk(TransformPaintPropertyNode::Root(), clip.get(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 200, 300), Color::kBlack);
+ test_artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 300, 400), Color::kGray);
+
+ const PaintArtifact& artifact = test_artifact.Build();
+
+ ASSERT_EQ(3u, artifact.PaintChunks().size());
+ Update(artifact);
+ ASSERT_EQ(1u, ContentLayerCount());
+ {
+ Vector<RectWithColor> rects_with_color;
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(0, 0, 100, 100), Color::kWhite));
+ // Clip is applied to this PaintChunk.
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(10, 20, 50, 60), Color::kBlack));
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(0, 0, 300, 400), Color::kGray));
+
+ const cc::Layer* layer = ContentLayerAt(0);
+ EXPECT_THAT(layer->GetPicture(),
+ Pointee(DrawsRectangles(rects_with_color)));
+ }
+}
+
+TEST_F(PaintArtifactCompositorTest, Merge2DTransform) {
+ scoped_refptr<TransformPaintPropertyNode> transform =
+ TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(),
+ TransformationMatrix().Translate(50, 50), FloatPoint3D(100, 100, 0),
+ false, 0);
+
+ TestPaintArtifact test_artifact;
+ test_artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite);
+ test_artifact
+ .Chunk(transform.get(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack);
+ test_artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 200, 300), Color::kGray);
+
+ const PaintArtifact& artifact = test_artifact.Build();
+ ASSERT_EQ(3u, artifact.PaintChunks().size());
+ Update(artifact);
+
+ ASSERT_EQ(1u, ContentLayerCount());
+ {
+ Vector<RectWithColor> rects_with_color;
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(0, 0, 100, 100), Color::kWhite));
+ // Transform is applied to this PaintChunk.
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(50, 50, 100, 100), Color::kBlack));
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(0, 0, 200, 300), Color::kGray));
+
+ const cc::Layer* layer = ContentLayerAt(0);
+ EXPECT_THAT(layer->GetPicture(),
+ Pointee(DrawsRectangles(rects_with_color)));
+ }
+}
+
+TEST_F(PaintArtifactCompositorTest, Merge2DTransformDirectAncestor) {
+ scoped_refptr<TransformPaintPropertyNode> transform =
+ TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(), TransformationMatrix(),
+ FloatPoint3D(), false, 0, CompositingReason::k3DTransform);
+
+ scoped_refptr<TransformPaintPropertyNode> transform2 =
+ TransformPaintPropertyNode::Create(
+ transform.get(), TransformationMatrix().Translate(50, 50),
+ FloatPoint3D(100, 100, 0), false, 0);
+
+ TestPaintArtifact test_artifact;
+ test_artifact
+ .Chunk(transform.get(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite);
+ // The second chunk can merge into the first because it has a descendant
+ // state of the first's transform and no direct compositing reason.
+ test_artifact
+ .Chunk(transform2.get(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack);
+
+ const PaintArtifact& artifact = test_artifact.Build();
+ ASSERT_EQ(2u, artifact.PaintChunks().size());
+ Update(artifact);
+ ASSERT_EQ(1u, ContentLayerCount());
+ {
+ Vector<RectWithColor> rects_with_color;
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(0, 0, 100, 100), Color::kWhite));
+ // Transform is applied to this PaintChunk.
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(50, 50, 100, 100), Color::kBlack));
+
+ const cc::Layer* layer = ContentLayerAt(0);
+ EXPECT_THAT(layer->GetPicture(),
+ Pointee(DrawsRectangles(rects_with_color)));
+ }
+}
+
+TEST_F(PaintArtifactCompositorTest, MergeTransformOrigin) {
+ scoped_refptr<TransformPaintPropertyNode> transform =
+ TransformPaintPropertyNode::Create(TransformPaintPropertyNode::Root(),
+ TransformationMatrix().Rotate(45),
+ FloatPoint3D(100, 100, 0), false, 0);
+
+ TestPaintArtifact test_artifact;
+ test_artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite);
+ test_artifact
+ .Chunk(transform.get(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack);
+ test_artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 200, 300), Color::kGray);
+
+ const PaintArtifact& artifact = test_artifact.Build();
+ ASSERT_EQ(3u, artifact.PaintChunks().size());
+ Update(artifact);
+ ASSERT_EQ(1u, ContentLayerCount());
+ {
+ Vector<RectWithColor> rects_with_color;
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(0, 42, 100, 100), Color::kWhite));
+ // Transform is applied to this PaintChunk.
+ rects_with_color.push_back(RectWithColor(
+ FloatRect(29.2893, 0.578644, 141.421, 141.421), Color::kBlack));
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(0, 42, 200, 300), Color::kGray));
+
+ const cc::Layer* layer = ContentLayerAt(0);
+ EXPECT_THAT(layer->GetPicture(),
+ Pointee(DrawsRectangles(rects_with_color)));
+ }
+}
+
+TEST_F(PaintArtifactCompositorTest, MergeOpacity) {
+ float opacity = 2.0 / 255.0;
+ scoped_refptr<EffectPaintPropertyNode> effect =
+ CreateOpacityOnlyEffect(EffectPaintPropertyNode::Root(), opacity);
+
+ TestPaintArtifact test_artifact;
+ test_artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite);
+ test_artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ effect.get())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack);
+ test_artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 200, 300), Color::kGray);
+
+ const PaintArtifact& artifact = test_artifact.Build();
+ ASSERT_EQ(3u, artifact.PaintChunks().size());
+ Update(artifact);
+ ASSERT_EQ(1u, ContentLayerCount());
+ {
+ Vector<RectWithColor> rects_with_color;
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(0, 0, 100, 100), Color::kWhite));
+ // Transform is applied to this PaintChunk.
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(0, 0, 100, 100),
+ Color(Color::kBlack).CombineWithAlpha(opacity).Rgb()));
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(0, 0, 200, 300), Color::kGray));
+
+ const cc::Layer* layer = ContentLayerAt(0);
+ EXPECT_THAT(layer->GetPicture(),
+ Pointee(DrawsRectangles(rects_with_color)));
+ }
+}
+
+TEST_F(PaintArtifactCompositorTest, MergeNested) {
+ // Tests merging of an opacity effect, inside of a clip, inside of a
+ // transform.
+
+ scoped_refptr<TransformPaintPropertyNode> transform =
+ TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(),
+ TransformationMatrix().Translate(50, 50), FloatPoint3D(100, 100, 0),
+ false, 0);
+
+ scoped_refptr<ClipPaintPropertyNode> clip = ClipPaintPropertyNode::Create(
+ ClipPaintPropertyNode::Root(), transform.get(),
+ FloatRoundedRect(10, 20, 50, 60));
+
+ float opacity = 2.0 / 255.0;
+ scoped_refptr<EffectPaintPropertyNode> effect =
+ EffectPaintPropertyNode::Create(
+ EffectPaintPropertyNode::Root(), transform.get(), clip.get(),
+ kColorFilterNone, CompositorFilterOperations(), opacity,
+ SkBlendMode::kSrcOver);
+
+ TestPaintArtifact test_artifact;
+ test_artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite);
+ test_artifact.Chunk(transform.get(), clip.get(), effect.get())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack);
+ test_artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 200, 300), Color::kGray);
+
+ const PaintArtifact& artifact = test_artifact.Build();
+ ASSERT_EQ(3u, artifact.PaintChunks().size());
+ Update(artifact);
+ ASSERT_EQ(1u, ContentLayerCount());
+ {
+ Vector<RectWithColor> rects_with_color;
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(0, 0, 100, 100), Color::kWhite));
+ // Transform is applied to this PaintChunk.
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(60, 70, 50, 60),
+ Color(Color::kBlack).CombineWithAlpha(opacity).Rgb()));
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(0, 0, 200, 300), Color::kGray));
+
+ const cc::Layer* layer = ContentLayerAt(0);
+ EXPECT_THAT(layer->GetPicture(),
+ Pointee(DrawsRectangles(rects_with_color)));
+ }
+}
+
+TEST_F(PaintArtifactCompositorTest, ClipPushedUp) {
+ // Tests merging of an element which has a clipapplied to it,
+ // but has an ancestor transform of them. This can happen for fixed-
+ // or absolute-position elements which escape scroll transforms.
+
+ scoped_refptr<TransformPaintPropertyNode> transform =
+ TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(),
+ TransformationMatrix().Translate(20, 25), FloatPoint3D(100, 100, 0),
+ false, 0);
+
+ scoped_refptr<TransformPaintPropertyNode> transform2 =
+ TransformPaintPropertyNode::Create(
+ transform.get(), TransformationMatrix().Translate(20, 25),
+ FloatPoint3D(100, 100, 0), false, 0);
+
+ scoped_refptr<ClipPaintPropertyNode> clip = ClipPaintPropertyNode::Create(
+ ClipPaintPropertyNode::Root(), transform2.get(),
+ FloatRoundedRect(10, 20, 50, 60));
+
+ TestPaintArtifact test_artifact;
+ test_artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite);
+ test_artifact
+ .Chunk(TransformPaintPropertyNode::Root(), clip.get(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 300, 400), Color::kBlack);
+ test_artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 200, 300), Color::kGray);
+
+ const PaintArtifact& artifact = test_artifact.Build();
+ ASSERT_EQ(3u, artifact.PaintChunks().size());
+ Update(artifact);
+ ASSERT_EQ(1u, ContentLayerCount());
+ {
+ Vector<RectWithColor> rects_with_color;
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(0, 0, 100, 100), Color::kWhite));
+ // The two transforms (combined translation of (40, 50)) are applied here,
+ // before clipping.
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(50, 70, 50, 60), Color(Color::kBlack)));
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(0, 0, 200, 300), Color::kGray));
+
+ const cc::Layer* layer = ContentLayerAt(0);
+ EXPECT_THAT(layer->GetPicture(),
+ Pointee(DrawsRectangles(rects_with_color)));
+ }
+}
+
+// TODO(crbug.com/696842): The effect refuses to "decomposite" because it's in
+// a deeper transform space than its chunk. We should allow decomposite if
+// the two transform nodes share the same direct compositing ancestor.
+TEST_F(PaintArtifactCompositorTest, EffectPushedUp_DISABLED) {
+ // Tests merging of an element which has an effect applied to it,
+ // but has an ancestor transform of them. This can happen for fixed-
+ // or absolute-position elements which escape scroll transforms.
+
+ scoped_refptr<TransformPaintPropertyNode> transform =
+ TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(),
+ TransformationMatrix().Translate(20, 25), FloatPoint3D(100, 100, 0),
+ false, 0);
+
+ scoped_refptr<TransformPaintPropertyNode> transform2 =
+ TransformPaintPropertyNode::Create(
+ transform.get(), TransformationMatrix().Translate(20, 25),
+ FloatPoint3D(100, 100, 0), false, 0);
+
+ float opacity = 2.0 / 255.0;
+ scoped_refptr<EffectPaintPropertyNode> effect =
+ EffectPaintPropertyNode::Create(
+ EffectPaintPropertyNode::Root(), transform2.get(),
+ ClipPaintPropertyNode::Root(), kColorFilterNone,
+ CompositorFilterOperations(), opacity, SkBlendMode::kSrcOver);
+
+ TestPaintArtifact test_artifact;
+ test_artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite);
+ test_artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ effect.get())
+ .RectDrawing(FloatRect(0, 0, 300, 400), Color::kBlack);
+ test_artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 200, 300), Color::kGray);
+
+ const PaintArtifact& artifact = test_artifact.Build();
+ ASSERT_EQ(3u, artifact.PaintChunks().size());
+ Update(artifact);
+ ASSERT_EQ(1u, ContentLayerCount());
+ {
+ Vector<RectWithColor> rects_with_color;
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(0, 0, 100, 100), Color::kWhite));
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(0, 0, 300, 400),
+ Color(Color::kBlack).CombineWithAlpha(opacity).Rgb()));
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(0, 0, 200, 300), Color::kGray));
+
+ const cc::Layer* layer = ContentLayerAt(0);
+ EXPECT_THAT(layer->GetPicture(),
+ Pointee(DrawsRectangles(rects_with_color)));
+ }
+}
+
+// TODO(crbug.com/696842): The effect refuses to "decomposite" because it's in
+// a deeper transform space than its chunk. We should allow decomposite if
+// the two transform nodes share the same direct compositing ancestor.
+TEST_F(PaintArtifactCompositorTest, EffectAndClipPushedUp_DISABLED) {
+ // Tests merging of an element which has an effect applied to it,
+ // but has an ancestor transform of them. This can happen for fixed-
+ // or absolute-position elements which escape scroll transforms.
+
+ scoped_refptr<TransformPaintPropertyNode> transform =
+ TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(),
+ TransformationMatrix().Translate(20, 25), FloatPoint3D(100, 100, 0),
+ false, 0);
+
+ scoped_refptr<TransformPaintPropertyNode> transform2 =
+ TransformPaintPropertyNode::Create(
+ transform.get(), TransformationMatrix().Translate(20, 25),
+ FloatPoint3D(100, 100, 0), false, 0);
+
+ scoped_refptr<ClipPaintPropertyNode> clip = ClipPaintPropertyNode::Create(
+ ClipPaintPropertyNode::Root(), transform.get(),
+ FloatRoundedRect(10, 20, 50, 60));
+
+ float opacity = 2.0 / 255.0;
+ scoped_refptr<EffectPaintPropertyNode> effect =
+ EffectPaintPropertyNode::Create(
+ EffectPaintPropertyNode::Root(), transform2.get(), clip.get(),
+ kColorFilterNone, CompositorFilterOperations(), opacity,
+ SkBlendMode::kSrcOver);
+
+ TestPaintArtifact test_artifact;
+ test_artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite);
+ test_artifact
+ .Chunk(TransformPaintPropertyNode::Root(), clip.get(), effect.get())
+ .RectDrawing(FloatRect(0, 0, 300, 400), Color::kBlack);
+ test_artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 200, 300), Color::kGray);
+
+ const PaintArtifact& artifact = test_artifact.Build();
+ ASSERT_EQ(3u, artifact.PaintChunks().size());
+ Update(artifact);
+ ASSERT_EQ(1u, ContentLayerCount());
+ {
+ Vector<RectWithColor> rects_with_color;
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(0, 0, 100, 100), Color::kWhite));
+ // The clip is under |transform| but not |transform2|, so only an adjustment
+ // of (20, 25) occurs.
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(30, 45, 50, 60),
+ Color(Color::kBlack).CombineWithAlpha(opacity).Rgb()));
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(0, 0, 200, 300), Color::kGray));
+
+ const cc::Layer* layer = ContentLayerAt(0);
+ EXPECT_THAT(layer->GetPicture(),
+ Pointee(DrawsRectangles(rects_with_color)));
+ }
+}
+
+TEST_F(PaintArtifactCompositorTest, ClipAndEffectNoTransform) {
+ // Tests merging of an element which has a clip and effect in the root
+ // transform space.
+
+ scoped_refptr<ClipPaintPropertyNode> clip = ClipPaintPropertyNode::Create(
+ ClipPaintPropertyNode::Root(), TransformPaintPropertyNode::Root(),
+ FloatRoundedRect(10, 20, 50, 60));
+
+ float opacity = 2.0 / 255.0;
+ scoped_refptr<EffectPaintPropertyNode> effect =
+ EffectPaintPropertyNode::Create(
+ EffectPaintPropertyNode::Root(), TransformPaintPropertyNode::Root(),
+ clip.get(), kColorFilterNone, CompositorFilterOperations(), opacity,
+ SkBlendMode::kSrcOver);
+
+ TestPaintArtifact test_artifact;
+ test_artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite);
+ test_artifact
+ .Chunk(TransformPaintPropertyNode::Root(), clip.get(), effect.get())
+ .RectDrawing(FloatRect(0, 0, 300, 400), Color::kBlack);
+ test_artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 200, 300), Color::kGray);
+
+ const PaintArtifact& artifact = test_artifact.Build();
+ ASSERT_EQ(3u, artifact.PaintChunks().size());
+ Update(artifact);
+ ASSERT_EQ(1u, ContentLayerCount());
+ {
+ Vector<RectWithColor> rects_with_color;
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(0, 0, 100, 100), Color::kWhite));
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(10, 20, 50, 60),
+ Color(Color::kBlack).CombineWithAlpha(opacity).Rgb()));
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(0, 0, 200, 300), Color::kGray));
+
+ const cc::Layer* layer = ContentLayerAt(0);
+ EXPECT_THAT(layer->GetPicture(),
+ Pointee(DrawsRectangles(rects_with_color)));
+ }
+}
+
+TEST_F(PaintArtifactCompositorTest, TwoClips) {
+ // Tests merging of an element which has two clips in the root
+ // transform space.
+
+ scoped_refptr<ClipPaintPropertyNode> clip = ClipPaintPropertyNode::Create(
+ ClipPaintPropertyNode::Root(), TransformPaintPropertyNode::Root(),
+ FloatRoundedRect(20, 30, 10, 20));
+
+ scoped_refptr<ClipPaintPropertyNode> clip2 = ClipPaintPropertyNode::Create(
+ clip.get(), TransformPaintPropertyNode::Root(),
+ FloatRoundedRect(10, 20, 50, 60));
+
+ TestPaintArtifact test_artifact;
+ test_artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite);
+ test_artifact
+ .Chunk(TransformPaintPropertyNode::Root(), clip2.get(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 300, 400), Color::kBlack);
+ test_artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 200, 300), Color::kGray);
+
+ const PaintArtifact& artifact = test_artifact.Build();
+ ASSERT_EQ(3u, artifact.PaintChunks().size());
+ Update(artifact);
+ ASSERT_EQ(1u, ContentLayerCount());
+ {
+ Vector<RectWithColor> rects_with_color;
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(0, 0, 100, 100), Color::kWhite));
+ // The interesction of the two clips is (20, 30, 10, 20).
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(20, 30, 10, 20), Color(Color::kBlack)));
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(0, 0, 200, 300), Color::kGray));
+
+ const cc::Layer* layer = ContentLayerAt(0);
+ EXPECT_THAT(layer->GetPicture(),
+ Pointee(DrawsRectangles(rects_with_color)));
+ }
+}
+
+TEST_F(PaintArtifactCompositorTest, TwoTransformsClipBetween) {
+ scoped_refptr<TransformPaintPropertyNode> transform =
+ TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(),
+ TransformationMatrix().Translate(20, 25), FloatPoint3D(100, 100, 0),
+ false, 0);
+ scoped_refptr<ClipPaintPropertyNode> clip = ClipPaintPropertyNode::Create(
+ ClipPaintPropertyNode::Root(), TransformPaintPropertyNode::Root(),
+ FloatRoundedRect(0, 0, 50, 60));
+ scoped_refptr<TransformPaintPropertyNode> transform2 =
+ TransformPaintPropertyNode::Create(
+ transform.get(), TransformationMatrix().Translate(20, 25),
+ FloatPoint3D(100, 100, 0), false, 0);
+ TestPaintArtifact test_artifact;
+ test_artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite);
+ test_artifact
+ .Chunk(transform2.get(), clip.get(), EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 300, 400), Color::kBlack);
+ test_artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 200, 300), Color::kGray);
+
+ const PaintArtifact& artifact = test_artifact.Build();
+ ASSERT_EQ(3u, artifact.PaintChunks().size());
+ Update(artifact);
+ ASSERT_EQ(1u, ContentLayerCount());
+ {
+ Vector<RectWithColor> rects_with_color;
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(0, 0, 100, 100), Color::kWhite));
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(40, 50, 10, 10), Color(Color::kBlack)));
+ rects_with_color.push_back(
+ RectWithColor(FloatRect(0, 0, 200, 300), Color::kGray));
+ const cc::Layer* layer = ContentLayerAt(0);
+ EXPECT_THAT(layer->GetPicture(),
+ Pointee(DrawsRectangles(rects_with_color)));
+ }
+}
+
+TEST_F(PaintArtifactCompositorTest, OverlapTransform) {
+ scoped_refptr<TransformPaintPropertyNode> transform =
+ TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(),
+ TransformationMatrix().Translate(50, 50), FloatPoint3D(100, 100, 0),
+ false, 0, CompositingReason::k3DTransform);
+
+ TestPaintArtifact test_artifact;
+ test_artifact.Chunk(DefaultPaintChunkProperties())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite);
+ test_artifact
+ .Chunk(transform.get(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack);
+ test_artifact.Chunk(DefaultPaintChunkProperties())
+ .RectDrawing(FloatRect(0, 0, 200, 300), Color::kGray);
+
+ const PaintArtifact& artifact = test_artifact.Build();
+ ASSERT_EQ(3u, artifact.PaintChunks().size());
+ Update(artifact);
+ // The third paint chunk overlaps the second but can't merge due to
+ // incompatible transform. The second paint chunk can't merge into the first
+ // due to a direct compositing reason.
+ ASSERT_EQ(3u, ContentLayerCount());
+}
+
+TEST_F(PaintArtifactCompositorTest, MightOverlap) {
+ PaintChunk paint_chunk = DefaultChunk();
+ paint_chunk.bounds = FloatRect(0, 0, 100, 100);
+ PendingLayer pending_layer(paint_chunk, 0, false);
+
+ PaintChunk paint_chunk2 = DefaultChunk();
+ paint_chunk2.bounds = FloatRect(0, 0, 100, 100);
+
+ {
+ PendingLayer pending_layer2(paint_chunk2, 1, false);
+ EXPECT_TRUE(MightOverlap(pending_layer, pending_layer2));
+ }
+
+ scoped_refptr<TransformPaintPropertyNode> transform =
+ TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(),
+ TransformationMatrix().Translate(99, 0), FloatPoint3D(100, 100, 0),
+ false);
+ {
+ paint_chunk2.properties.property_tree_state.SetTransform(transform.get());
+ PendingLayer pending_layer2(paint_chunk2, 1, false);
+ EXPECT_TRUE(MightOverlap(pending_layer, pending_layer2));
+ }
+
+ scoped_refptr<TransformPaintPropertyNode> transform2 =
+ TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(),
+ TransformationMatrix().Translate(100, 0), FloatPoint3D(100, 100, 0),
+ false);
+ {
+ paint_chunk2.properties.property_tree_state.SetTransform(transform2.get());
+ PendingLayer pending_layer2(paint_chunk2, 1, false);
+ EXPECT_FALSE(MightOverlap(pending_layer, pending_layer2));
+ }
+}
+
+TEST_F(PaintArtifactCompositorTest, PendingLayer) {
+ PaintChunk chunk1 = DefaultChunk();
+ chunk1.properties.property_tree_state = PropertyTreeState(
+ TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root());
+ chunk1.properties.backface_hidden = true;
+ chunk1.known_to_be_opaque = true;
+ chunk1.bounds = FloatRect(0, 0, 30, 40);
+
+ PendingLayer pending_layer(chunk1, 0, false);
+
+ EXPECT_TRUE(pending_layer.backface_hidden);
+ EXPECT_EQ(FloatRect(0, 0, 30, 40), pending_layer.bounds);
+ EXPECT_EQ((Vector<size_t>{0}), pending_layer.paint_chunk_indices);
+ EXPECT_EQ(pending_layer.bounds, pending_layer.rect_known_to_be_opaque);
+
+ PaintChunk chunk2 = DefaultChunk();
+ chunk2.properties.property_tree_state = chunk1.properties.property_tree_state;
+ chunk2.properties.backface_hidden = true;
+ chunk2.known_to_be_opaque = true;
+ chunk2.bounds = FloatRect(10, 20, 30, 40);
+ pending_layer.Merge(PendingLayer(chunk2, 1, false));
+
+ EXPECT_TRUE(pending_layer.backface_hidden);
+ // Bounds not equal to one PaintChunk.
+ EXPECT_EQ(FloatRect(0, 0, 40, 60), pending_layer.bounds);
+ EXPECT_EQ((Vector<size_t>{0, 1}), pending_layer.paint_chunk_indices);
+ EXPECT_NE(pending_layer.bounds, pending_layer.rect_known_to_be_opaque);
+
+ PaintChunk chunk3 = DefaultChunk();
+ chunk3.properties.property_tree_state = chunk1.properties.property_tree_state;
+ chunk3.properties.backface_hidden = true;
+ chunk3.known_to_be_opaque = true;
+ chunk3.bounds = FloatRect(-5, -25, 20, 20);
+ pending_layer.Merge(PendingLayer(chunk3, 2, false));
+
+ EXPECT_TRUE(pending_layer.backface_hidden);
+ EXPECT_EQ(FloatRect(-5, -25, 45, 85), pending_layer.bounds);
+ EXPECT_EQ((Vector<size_t>{0, 1, 2}), pending_layer.paint_chunk_indices);
+ EXPECT_NE(pending_layer.bounds, pending_layer.rect_known_to_be_opaque);
+}
+
+TEST_F(PaintArtifactCompositorTest, PendingLayerWithGeometry) {
+ scoped_refptr<TransformPaintPropertyNode> transform =
+ TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(),
+ TransformationMatrix().Translate(20, 25), FloatPoint3D(100, 100, 0),
+ false, 0);
+
+ PaintChunk chunk1 = DefaultChunk();
+ chunk1.properties.property_tree_state = PropertyTreeState(
+ TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root());
+ chunk1.bounds = FloatRect(0, 0, 30, 40);
+
+ PendingLayer pending_layer(chunk1, 0, false);
+
+ EXPECT_EQ(FloatRect(0, 0, 30, 40), pending_layer.bounds);
+
+ PaintChunk chunk2 = DefaultChunk();
+ chunk2.properties.property_tree_state = chunk1.properties.property_tree_state;
+ chunk2.properties.property_tree_state.SetTransform(transform);
+ chunk2.bounds = FloatRect(0, 0, 50, 60);
+ pending_layer.Merge(PendingLayer(chunk2, 1, false));
+
+ EXPECT_EQ(FloatRect(0, 0, 70, 85), pending_layer.bounds);
+}
+
+// TODO(crbug.com/701991):
+// The test is disabled because opaque rect mapping is not implemented yet.
+TEST_F(PaintArtifactCompositorTest, PendingLayerKnownOpaque_DISABLED) {
+ PaintChunk chunk1 = DefaultChunk();
+ chunk1.properties.property_tree_state = PropertyTreeState(
+ TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root());
+ chunk1.bounds = FloatRect(0, 0, 30, 40);
+ chunk1.known_to_be_opaque = false;
+ PendingLayer pending_layer(chunk1, 0, false);
+
+ EXPECT_TRUE(pending_layer.rect_known_to_be_opaque.IsEmpty());
+
+ PaintChunk chunk2 = DefaultChunk();
+ chunk2.properties.property_tree_state = chunk1.properties.property_tree_state;
+ chunk2.bounds = FloatRect(0, 0, 25, 35);
+ chunk2.known_to_be_opaque = true;
+ pending_layer.Merge(PendingLayer(chunk2, 1, false));
+
+ // Chunk 2 doesn't cover the entire layer, so not opaque.
+ EXPECT_EQ(chunk2.bounds, pending_layer.rect_known_to_be_opaque);
+ EXPECT_NE(pending_layer.bounds, pending_layer.rect_known_to_be_opaque);
+
+ PaintChunk chunk3 = DefaultChunk();
+ chunk3.properties.property_tree_state = chunk1.properties.property_tree_state;
+ chunk3.bounds = FloatRect(0, 0, 50, 60);
+ chunk3.known_to_be_opaque = true;
+ pending_layer.Merge(PendingLayer(chunk3, 2, false));
+
+ // Chunk 3 covers the entire layer, so now it's opaque.
+ EXPECT_EQ(chunk3.bounds, pending_layer.bounds);
+ EXPECT_EQ(pending_layer.bounds, pending_layer.rect_known_to_be_opaque);
+}
+
+scoped_refptr<EffectPaintPropertyNode> CreateSampleEffectNodeWithElementId() {
+ CompositorElementId expected_compositor_element_id(2);
+ float opacity = 2.0 / 255.0;
+ return EffectPaintPropertyNode::Create(
+ EffectPaintPropertyNode::Root(), TransformPaintPropertyNode::Root(),
+ ClipPaintPropertyNode::Root(), kColorFilterNone,
+ CompositorFilterOperations(), opacity, SkBlendMode::kSrcOver,
+ CompositingReason::kActiveOpacityAnimation,
+ expected_compositor_element_id);
+}
+
+scoped_refptr<TransformPaintPropertyNode>
+CreateSampleTransformNodeWithElementId() {
+ CompositorElementId expected_compositor_element_id(3);
+ return TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(), TransformationMatrix().Rotate(90),
+ FloatPoint3D(100, 100, 0), false, 0, CompositingReason::k3DTransform,
+ expected_compositor_element_id);
+}
+
+TEST_F(PaintArtifactCompositorTest, TransformWithElementId) {
+ scoped_refptr<TransformPaintPropertyNode> transform =
+ CreateSampleTransformNodeWithElementId();
+ TestPaintArtifact artifact;
+ artifact
+ .Chunk(transform, ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(100, 100, 200, 100), Color::kBlack);
+ Update(artifact.Build());
+
+ EXPECT_EQ(2,
+ ElementIdToTransformNodeIndex(transform->GetCompositorElementId()));
+}
+
+TEST_F(PaintArtifactCompositorTest, EffectWithElementId) {
+ scoped_refptr<EffectPaintPropertyNode> effect =
+ CreateSampleEffectNodeWithElementId();
+ TestPaintArtifact artifact;
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ effect.get())
+ .RectDrawing(FloatRect(100, 100, 200, 100), Color::kBlack);
+ Update(artifact.Build());
+
+ EXPECT_EQ(2, ElementIdToEffectNodeIndex(effect->GetCompositorElementId()));
+}
+
+TEST_F(PaintArtifactCompositorTest, CompositedLuminanceMask) {
+ scoped_refptr<EffectPaintPropertyNode> masked =
+ EffectPaintPropertyNode::Create(
+ EffectPaintPropertyNode::Root(), TransformPaintPropertyNode::Root(),
+ ClipPaintPropertyNode::Root(), kColorFilterNone,
+ CompositorFilterOperations(), 1.0, SkBlendMode::kSrcOver,
+ CompositingReason::kIsolateCompositedDescendants);
+ scoped_refptr<EffectPaintPropertyNode> masking =
+ EffectPaintPropertyNode::Create(
+ masked, TransformPaintPropertyNode::Root(),
+ ClipPaintPropertyNode::Root(), kColorFilterLuminanceToAlpha,
+ CompositorFilterOperations(), 1.0, SkBlendMode::kDstIn,
+ CompositingReason::kSquashingDisallowed);
+
+ TestPaintArtifact artifact;
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ masked.get())
+ .RectDrawing(FloatRect(100, 100, 200, 200), Color::kGray);
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ masking.get())
+ .RectDrawing(FloatRect(150, 150, 100, 100), Color::kWhite);
+ Update(artifact.Build());
+ ASSERT_EQ(2u, ContentLayerCount());
+
+ const cc::Layer* masked_layer = ContentLayerAt(0);
+ EXPECT_THAT(masked_layer->GetPicture(),
+ Pointee(DrawsRectangle(FloatRect(0, 0, 200, 200), Color::kGray)));
+ EXPECT_EQ(Translation(100, 100), masked_layer->ScreenSpaceTransform());
+ EXPECT_EQ(gfx::Size(200, 200), masked_layer->bounds());
+ const cc::EffectNode* masked_group =
+ GetPropertyTrees().effect_tree.Node(masked_layer->effect_tree_index());
+ EXPECT_TRUE(masked_group->has_render_surface);
+
+ const cc::Layer* masking_layer = ContentLayerAt(1);
+ EXPECT_THAT(
+ masking_layer->GetPicture(),
+ Pointee(DrawsRectangle(FloatRect(0, 0, 100, 100), Color::kWhite)));
+ EXPECT_EQ(Translation(150, 150), masking_layer->ScreenSpaceTransform());
+ EXPECT_EQ(gfx::Size(100, 100), masking_layer->bounds());
+ const cc::EffectNode* masking_group =
+ GetPropertyTrees().effect_tree.Node(masking_layer->effect_tree_index());
+ EXPECT_TRUE(masking_group->has_render_surface);
+ EXPECT_EQ(masked_group->id, masking_group->parent_id);
+ ASSERT_EQ(1u, masking_group->filters.size());
+ EXPECT_EQ(cc::FilterOperation::REFERENCE,
+ masking_group->filters.at(0).type());
+}
+
+TEST_F(PaintArtifactCompositorTest, UpdateProducesNewSequenceNumber) {
+ // A 90 degree clockwise rotation about (100, 100).
+ scoped_refptr<TransformPaintPropertyNode> transform =
+ TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(), TransformationMatrix().Rotate(90),
+ FloatPoint3D(100, 100, 0), false, 0, CompositingReason::k3DTransform);
+
+ scoped_refptr<ClipPaintPropertyNode> clip = ClipPaintPropertyNode::Create(
+ ClipPaintPropertyNode::Root(), TransformPaintPropertyNode::Root(),
+ FloatRoundedRect(100, 100, 300, 200));
+
+ scoped_refptr<EffectPaintPropertyNode> effect =
+ CreateOpacityOnlyEffect(EffectPaintPropertyNode::Root(), 0.5);
+
+ TestPaintArtifact artifact;
+ artifact.Chunk(transform, clip, effect)
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite);
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kGray);
+ Update(artifact.Build());
+
+ // Two content layers for the differentiated rect drawings and three dummy
+ // layers for each of the transform, clip and effect nodes.
+ EXPECT_EQ(2u, RootLayer()->children().size());
+ int sequence_number = GetPropertyTrees().sequence_number;
+ EXPECT_GT(sequence_number, 0);
+ for (auto layer : RootLayer()->children()) {
+ EXPECT_EQ(sequence_number, layer->property_tree_sequence_number());
+ }
+
+ Update(artifact.Build());
+
+ EXPECT_EQ(2u, RootLayer()->children().size());
+ sequence_number++;
+ EXPECT_EQ(sequence_number, GetPropertyTrees().sequence_number);
+ for (auto layer : RootLayer()->children()) {
+ EXPECT_EQ(sequence_number, layer->property_tree_sequence_number());
+ }
+
+ Update(artifact.Build());
+
+ EXPECT_EQ(2u, RootLayer()->children().size());
+ sequence_number++;
+ EXPECT_EQ(sequence_number, GetPropertyTrees().sequence_number);
+ for (auto layer : RootLayer()->children()) {
+ EXPECT_EQ(sequence_number, layer->property_tree_sequence_number());
+ }
+}
+
+TEST_F(PaintArtifactCompositorTest, DecompositeClip) {
+ // A clipped paint chunk that gets merged into a previous layer should
+ // only contribute clipped bounds to the layer bound.
+
+ scoped_refptr<ClipPaintPropertyNode> clip = ClipPaintPropertyNode::Create(
+ ClipPaintPropertyNode::Root(), TransformPaintPropertyNode::Root(),
+ FloatRoundedRect(75, 75, 100, 100));
+
+ TestPaintArtifact artifact;
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(50, 50, 100, 100), Color::kGray);
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), clip.get(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(100, 100, 100, 100), Color::kGray);
+ Update(artifact.Build());
+ ASSERT_EQ(1u, ContentLayerCount());
+
+ const cc::Layer* layer = ContentLayerAt(0);
+ EXPECT_EQ(gfx::Vector2dF(50.f, 50.f), layer->offset_to_transform_parent());
+ EXPECT_EQ(gfx::Size(125, 125), layer->bounds());
+}
+
+TEST_F(PaintArtifactCompositorTest, DecompositeEffect) {
+ // An effect node without direct compositing reason and does not need to
+ // group compositing descendants should not be composited and can merge
+ // with other chunks.
+
+ scoped_refptr<EffectPaintPropertyNode> effect =
+ CreateOpacityOnlyEffect(EffectPaintPropertyNode::Root(), 0.5);
+
+ TestPaintArtifact artifact;
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(50, 25, 100, 100), Color::kGray);
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ effect.get())
+ .RectDrawing(FloatRect(25, 75, 100, 100), Color::kGray);
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(75, 75, 100, 100), Color::kGray);
+ Update(artifact.Build());
+ ASSERT_EQ(1u, ContentLayerCount());
+
+ const cc::Layer* layer = ContentLayerAt(0);
+ EXPECT_EQ(gfx::Vector2dF(25.f, 25.f), layer->offset_to_transform_parent());
+ EXPECT_EQ(gfx::Size(150, 150), layer->bounds());
+ EXPECT_EQ(1, layer->effect_tree_index());
+}
+
+TEST_F(PaintArtifactCompositorTest, DirectlyCompositedEffect) {
+ // An effect node with direct compositing shall be composited.
+ scoped_refptr<EffectPaintPropertyNode> effect =
+ EffectPaintPropertyNode::Create(
+ EffectPaintPropertyNode::Root(), TransformPaintPropertyNode::Root(),
+ ClipPaintPropertyNode::Root(), kColorFilterNone,
+ CompositorFilterOperations(), 0.5f, SkBlendMode::kSrcOver,
+ CompositingReason::kAll);
+
+ TestPaintArtifact artifact;
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(50, 25, 100, 100), Color::kGray);
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ effect.get())
+ .RectDrawing(FloatRect(25, 75, 100, 100), Color::kGray);
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(75, 75, 100, 100), Color::kGray);
+ Update(artifact.Build());
+ ASSERT_EQ(3u, ContentLayerCount());
+
+ const cc::Layer* layer1 = ContentLayerAt(0);
+ EXPECT_EQ(gfx::Vector2dF(50.f, 25.f), layer1->offset_to_transform_parent());
+ EXPECT_EQ(gfx::Size(100, 100), layer1->bounds());
+ EXPECT_EQ(1, layer1->effect_tree_index());
+
+ const cc::Layer* layer2 = ContentLayerAt(1);
+ EXPECT_EQ(gfx::Vector2dF(25.f, 75.f), layer2->offset_to_transform_parent());
+ EXPECT_EQ(gfx::Size(100, 100), layer2->bounds());
+ const cc::EffectNode* effect_node =
+ GetPropertyTrees().effect_tree.Node(layer2->effect_tree_index());
+ EXPECT_EQ(1, effect_node->parent_id);
+ EXPECT_EQ(0.5f, effect_node->opacity);
+
+ const cc::Layer* layer3 = ContentLayerAt(2);
+ EXPECT_EQ(gfx::Vector2dF(75.f, 75.f), layer3->offset_to_transform_parent());
+ EXPECT_EQ(gfx::Size(100, 100), layer3->bounds());
+ EXPECT_EQ(1, layer3->effect_tree_index());
+}
+
+TEST_F(PaintArtifactCompositorTest, DecompositeDeepEffect) {
+ // A paint chunk may enter multiple level effects with or without compositing
+ // reasons. This test verifies we still decomposite effects without a direct
+ // reason, but stop at a directly composited effect.
+ scoped_refptr<EffectPaintPropertyNode> effect1 =
+ CreateOpacityOnlyEffect(EffectPaintPropertyNode::Root(), 0.1f);
+ scoped_refptr<EffectPaintPropertyNode> effect2 =
+ EffectPaintPropertyNode::Create(
+ effect1, TransformPaintPropertyNode::Root(),
+ ClipPaintPropertyNode::Root(), kColorFilterNone,
+ CompositorFilterOperations(), 0.2f, SkBlendMode::kSrcOver,
+ CompositingReason::kAll);
+ scoped_refptr<EffectPaintPropertyNode> effect3 =
+ CreateOpacityOnlyEffect(effect2, 0.3f);
+
+ TestPaintArtifact artifact;
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(50, 25, 100, 100), Color::kGray);
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ effect3.get())
+ .RectDrawing(FloatRect(25, 75, 100, 100), Color::kGray);
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(75, 75, 100, 100), Color::kGray);
+ Update(artifact.Build());
+ ASSERT_EQ(3u, ContentLayerCount());
+
+ const cc::Layer* layer1 = ContentLayerAt(0);
+ EXPECT_EQ(gfx::Vector2dF(50.f, 25.f), layer1->offset_to_transform_parent());
+ EXPECT_EQ(gfx::Size(100, 100), layer1->bounds());
+ EXPECT_EQ(1, layer1->effect_tree_index());
+
+ const cc::Layer* layer2 = ContentLayerAt(1);
+ EXPECT_EQ(gfx::Vector2dF(25.f, 75.f), layer2->offset_to_transform_parent());
+ EXPECT_EQ(gfx::Size(100, 100), layer2->bounds());
+ const cc::EffectNode* effect_node2 =
+ GetPropertyTrees().effect_tree.Node(layer2->effect_tree_index());
+ EXPECT_EQ(0.2f, effect_node2->opacity);
+ const cc::EffectNode* effect_node1 =
+ GetPropertyTrees().effect_tree.Node(effect_node2->parent_id);
+ EXPECT_EQ(1, effect_node1->parent_id);
+ EXPECT_EQ(0.1f, effect_node1->opacity);
+
+ const cc::Layer* layer3 = ContentLayerAt(2);
+ EXPECT_EQ(gfx::Vector2dF(75.f, 75.f), layer3->offset_to_transform_parent());
+ EXPECT_EQ(gfx::Size(100, 100), layer3->bounds());
+ EXPECT_EQ(1, layer3->effect_tree_index());
+}
+
+TEST_F(PaintArtifactCompositorTest, IndirectlyCompositedEffect) {
+ // An effect node without direct compositing still needs to be composited
+ // for grouping, if some chunks need to be composited.
+ scoped_refptr<EffectPaintPropertyNode> effect =
+ CreateOpacityOnlyEffect(EffectPaintPropertyNode::Root(), 0.5f);
+ scoped_refptr<TransformPaintPropertyNode> transform =
+ TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(), TransformationMatrix(),
+ FloatPoint3D(), false, 0, CompositingReason::k3DTransform);
+
+ TestPaintArtifact artifact;
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(50, 25, 100, 100), Color::kGray);
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ effect.get())
+ .RectDrawing(FloatRect(25, 75, 100, 100), Color::kGray);
+ artifact.Chunk(transform.get(), ClipPaintPropertyNode::Root(), effect.get())
+ .RectDrawing(FloatRect(75, 75, 100, 100), Color::kGray);
+ Update(artifact.Build());
+ ASSERT_EQ(3u, ContentLayerCount());
+
+ const cc::Layer* layer1 = ContentLayerAt(0);
+ EXPECT_EQ(gfx::Vector2dF(50.f, 25.f), layer1->offset_to_transform_parent());
+ EXPECT_EQ(gfx::Size(100, 100), layer1->bounds());
+ EXPECT_EQ(1, layer1->effect_tree_index());
+
+ const cc::Layer* layer2 = ContentLayerAt(1);
+ EXPECT_EQ(gfx::Vector2dF(25.f, 75.f), layer2->offset_to_transform_parent());
+ EXPECT_EQ(gfx::Size(100, 100), layer2->bounds());
+ const cc::EffectNode* effect_node =
+ GetPropertyTrees().effect_tree.Node(layer2->effect_tree_index());
+ EXPECT_EQ(1, effect_node->parent_id);
+ EXPECT_EQ(0.5f, effect_node->opacity);
+
+ const cc::Layer* layer3 = ContentLayerAt(2);
+ EXPECT_EQ(gfx::Vector2dF(75.f, 75.f), layer3->offset_to_transform_parent());
+ EXPECT_EQ(gfx::Size(100, 100), layer3->bounds());
+ EXPECT_EQ(effect_node->id, layer3->effect_tree_index());
+}
+
+TEST_F(PaintArtifactCompositorTest, DecompositedEffectNotMergingDueToOverlap) {
+ // This tests an effect that doesn't need to be composited, but needs
+ // separate backing due to overlap with a previous composited effect.
+ scoped_refptr<EffectPaintPropertyNode> effect1 =
+ CreateOpacityOnlyEffect(EffectPaintPropertyNode::Root(), 0.1f);
+ scoped_refptr<EffectPaintPropertyNode> effect2 =
+ CreateOpacityOnlyEffect(EffectPaintPropertyNode::Root(), 0.2f);
+ scoped_refptr<TransformPaintPropertyNode> transform =
+ TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(), TransformationMatrix(),
+ FloatPoint3D(), false, 0, CompositingReason::k3DTransform);
+ TestPaintArtifact artifact;
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 50, 50), Color::kGray);
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ effect1.get())
+ .RectDrawing(FloatRect(100, 0, 50, 50), Color::kGray);
+ // This chunk has a transform that must be composited, thus causing effect1
+ // to be composited too.
+ artifact.Chunk(transform.get(), ClipPaintPropertyNode::Root(), effect1.get())
+ .RectDrawing(FloatRect(200, 0, 50, 50), Color::kGray);
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ effect2.get())
+ .RectDrawing(FloatRect(200, 100, 50, 50), Color::kGray);
+ // This chunk overlaps with the 2nd chunk, but is seemingly safe to merge.
+ // However because effect1 gets composited due to a composited transform,
+ // we can't merge with effect1 nor skip it to merge with the first chunk.
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ effect2.get())
+ .RectDrawing(FloatRect(100, 0, 50, 50), Color::kGray);
+
+ Update(artifact.Build());
+ ASSERT_EQ(4u, ContentLayerCount());
+
+ const cc::Layer* layer1 = ContentLayerAt(0);
+ EXPECT_EQ(gfx::Vector2dF(0.f, 0.f), layer1->offset_to_transform_parent());
+ EXPECT_EQ(gfx::Size(50, 50), layer1->bounds());
+ EXPECT_EQ(1, layer1->effect_tree_index());
+
+ const cc::Layer* layer2 = ContentLayerAt(1);
+ EXPECT_EQ(gfx::Vector2dF(100.f, 0.f), layer2->offset_to_transform_parent());
+ EXPECT_EQ(gfx::Size(50, 50), layer2->bounds());
+ const cc::EffectNode* effect_node =
+ GetPropertyTrees().effect_tree.Node(layer2->effect_tree_index());
+ EXPECT_EQ(1, effect_node->parent_id);
+ EXPECT_EQ(0.1f, effect_node->opacity);
+
+ const cc::Layer* layer3 = ContentLayerAt(2);
+ EXPECT_EQ(gfx::Vector2dF(200.f, 0.f), layer3->offset_to_transform_parent());
+ EXPECT_EQ(gfx::Size(50, 50), layer3->bounds());
+ EXPECT_EQ(effect_node->id, layer3->effect_tree_index());
+
+ const cc::Layer* layer4 = ContentLayerAt(3);
+ EXPECT_EQ(gfx::Vector2dF(100.f, 0.f), layer4->offset_to_transform_parent());
+ EXPECT_EQ(gfx::Size(150, 150), layer4->bounds());
+ EXPECT_EQ(1, layer4->effect_tree_index());
+}
+
+TEST_F(PaintArtifactCompositorTest, UpdatePopulatesCompositedElementIds) {
+ scoped_refptr<TransformPaintPropertyNode> transform =
+ CreateSampleTransformNodeWithElementId();
+ scoped_refptr<EffectPaintPropertyNode> effect =
+ CreateSampleEffectNodeWithElementId();
+ TestPaintArtifact artifact;
+ artifact
+ .Chunk(transform, ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack)
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ effect.get())
+ .RectDrawing(FloatRect(100, 100, 200, 100), Color::kBlack);
+
+ CompositorElementIdSet composited_element_ids;
+ Update(artifact.Build(), composited_element_ids);
+
+ EXPECT_EQ(2u, composited_element_ids.size());
+ EXPECT_TRUE(
+ composited_element_ids.Contains(transform->GetCompositorElementId()));
+ EXPECT_TRUE(
+ composited_element_ids.Contains(effect->GetCompositorElementId()));
+}
+
+TEST_F(PaintArtifactCompositorTest, SkipChunkWithOpacityZero) {
+ {
+ TestPaintArtifact artifact;
+ CreateSimpleArtifactWithOpacity(artifact, 0, false, false);
+ ASSERT_EQ(0u, ContentLayerCount());
+ }
+ {
+ TestPaintArtifact artifact;
+ CreateSimpleArtifactWithOpacity(artifact, 0, true, false);
+ ASSERT_EQ(1u, ContentLayerCount());
+ }
+ {
+ TestPaintArtifact artifact;
+ CreateSimpleArtifactWithOpacity(artifact, 0, true, true);
+ ASSERT_EQ(1u, ContentLayerCount());
+ }
+ {
+ TestPaintArtifact artifact;
+ CreateSimpleArtifactWithOpacity(artifact, 0, false, true);
+ ASSERT_EQ(1u, ContentLayerCount());
+ }
+}
+
+TEST_F(PaintArtifactCompositorTest, SkipChunkWithTinyOpacity) {
+ {
+ TestPaintArtifact artifact;
+ CreateSimpleArtifactWithOpacity(artifact, 0.0003f, false, false);
+ ASSERT_EQ(0u, ContentLayerCount());
+ }
+ {
+ TestPaintArtifact artifact;
+ CreateSimpleArtifactWithOpacity(artifact, 0.0003f, true, false);
+ ASSERT_EQ(1u, ContentLayerCount());
+ }
+ {
+ TestPaintArtifact artifact;
+ CreateSimpleArtifactWithOpacity(artifact, 0.0003f, true, true);
+ ASSERT_EQ(1u, ContentLayerCount());
+ }
+ {
+ TestPaintArtifact artifact;
+ CreateSimpleArtifactWithOpacity(artifact, 0.0003f, false, true);
+ ASSERT_EQ(1u, ContentLayerCount());
+ }
+}
+
+TEST_F(PaintArtifactCompositorTest, DontSkipChunkWithMinimumOpacity) {
+ {
+ TestPaintArtifact artifact;
+ CreateSimpleArtifactWithOpacity(artifact, 0.0004f, false, false);
+ ASSERT_EQ(1u, ContentLayerCount());
+ }
+ {
+ TestPaintArtifact artifact;
+ CreateSimpleArtifactWithOpacity(artifact, 0.0004f, true, false);
+ ASSERT_EQ(1u, ContentLayerCount());
+ }
+ {
+ TestPaintArtifact artifact;
+ CreateSimpleArtifactWithOpacity(artifact, 0.0004f, true, true);
+ ASSERT_EQ(1u, ContentLayerCount());
+ }
+ {
+ TestPaintArtifact artifact;
+ CreateSimpleArtifactWithOpacity(artifact, 0.0004f, false, true);
+ ASSERT_EQ(1u, ContentLayerCount());
+ }
+}
+
+TEST_F(PaintArtifactCompositorTest, DontSkipChunkWithAboveMinimumOpacity) {
+ {
+ TestPaintArtifact artifact;
+ CreateSimpleArtifactWithOpacity(artifact, 0.3f, false, false);
+ ASSERT_EQ(1u, ContentLayerCount());
+ }
+ {
+ TestPaintArtifact artifact;
+ CreateSimpleArtifactWithOpacity(artifact, 0.3f, true, false);
+ ASSERT_EQ(1u, ContentLayerCount());
+ }
+ {
+ TestPaintArtifact artifact;
+ CreateSimpleArtifactWithOpacity(artifact, 0.3f, true, true);
+ ASSERT_EQ(1u, ContentLayerCount());
+ }
+ {
+ TestPaintArtifact artifact;
+ CreateSimpleArtifactWithOpacity(artifact, 0.3f, false, true);
+ ASSERT_EQ(1u, ContentLayerCount());
+ }
+}
+
+scoped_refptr<EffectPaintPropertyNode> CreateEffectWithOpacityAndReason(
+ float opacity,
+ CompositingReasons reason,
+ scoped_refptr<EffectPaintPropertyNode> parent = nullptr) {
+ return EffectPaintPropertyNode::Create(
+ parent ? parent : EffectPaintPropertyNode::Root(),
+ TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ kColorFilterNone, CompositorFilterOperations(), opacity,
+ SkBlendMode::kSrcOver, reason);
+}
+
+TEST_F(PaintArtifactCompositorTest,
+ DontSkipChunkWithTinyOpacityAndDirectCompositingReason) {
+ scoped_refptr<EffectPaintPropertyNode> effect =
+ CreateEffectWithOpacityAndReason(0.0001f, CompositingReason::kCanvas);
+ TestPaintArtifact artifact;
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ effect)
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack);
+ Update(artifact.Build());
+ ASSERT_EQ(1u, ContentLayerCount());
+}
+
+TEST_F(PaintArtifactCompositorTest,
+ SkipChunkWithTinyOpacityAndVisibleChildEffectNode) {
+ scoped_refptr<EffectPaintPropertyNode> tinyEffect =
+ CreateEffectWithOpacityAndReason(0.0001f, CompositingReason::kNone);
+ scoped_refptr<EffectPaintPropertyNode> visibleEffect =
+ CreateEffectWithOpacityAndReason(0.5f, CompositingReason::kNone,
+ tinyEffect);
+ TestPaintArtifact artifact;
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ visibleEffect)
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack);
+ Update(artifact.Build());
+ ASSERT_EQ(0u, ContentLayerCount());
+}
+
+TEST_F(
+ PaintArtifactCompositorTest,
+ DontSkipChunkWithTinyOpacityAndVisibleChildEffectNodeWithCompositingParent) {
+ scoped_refptr<EffectPaintPropertyNode> tinyEffect =
+ CreateEffectWithOpacityAndReason(0.0001f, CompositingReason::kCanvas);
+ scoped_refptr<EffectPaintPropertyNode> visibleEffect =
+ CreateEffectWithOpacityAndReason(0.5f, CompositingReason::kNone,
+ tinyEffect);
+ TestPaintArtifact artifact;
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ visibleEffect)
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack);
+ Update(artifact.Build());
+ ASSERT_EQ(1u, ContentLayerCount());
+}
+
+TEST_F(PaintArtifactCompositorTest,
+ SkipChunkWithTinyOpacityAndVisibleChildEffectNodeWithCompositingChild) {
+ scoped_refptr<EffectPaintPropertyNode> tinyEffect =
+ CreateEffectWithOpacityAndReason(0.0001f, CompositingReason::kNone);
+ scoped_refptr<EffectPaintPropertyNode> visibleEffect =
+ CreateEffectWithOpacityAndReason(0.5f, CompositingReason::kCanvas,
+ tinyEffect);
+ TestPaintArtifact artifact;
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ visibleEffect)
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack);
+ Update(artifact.Build());
+ ASSERT_EQ(0u, ContentLayerCount());
+}
+
+TEST_F(PaintArtifactCompositorTest, UpdateManagesLayerElementIds) {
+ scoped_refptr<TransformPaintPropertyNode> transform =
+ CreateSampleTransformNodeWithElementId();
+ CompositorElementId element_id = transform->GetCompositorElementId();
+
+ {
+ TestPaintArtifact artifact;
+ artifact
+ .Chunk(transform, ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack);
+
+ Update(artifact.Build());
+ ASSERT_EQ(1u, ContentLayerCount());
+ ASSERT_TRUE(GetLayerTreeHost().LayerByElementId(element_id));
+ }
+
+ {
+ TestPaintArtifact artifact;
+ ASSERT_TRUE(GetLayerTreeHost().LayerByElementId(element_id));
+ Update(artifact.Build());
+ ASSERT_EQ(0u, ContentLayerCount());
+ ASSERT_FALSE(GetLayerTreeHost().LayerByElementId(element_id));
+ }
+}
+
+TEST_F(PaintArtifactCompositorTest, SynthesizedClipSimple) {
+ // This tests the simplist case that a single layer needs to be clipped
+ // by a single composited rounded clip.
+ FloatSize corner(5, 5);
+ FloatRoundedRect rrect(FloatRect(50, 50, 300, 200), corner, corner, corner,
+ corner);
+ scoped_refptr<ClipPaintPropertyNode> c1 = ClipPaintPropertyNode::Create(
+ c0(), t0(), rrect, nullptr, nullptr,
+ CompositingReason::kWillChangeCompositingHint);
+
+ TestPaintArtifact artifact;
+ artifact.Chunk(t0(), c1, e0())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack);
+ Update(artifact.Build());
+
+ // Expectation in effect stack diagram:
+ // l1
+ // l0 [ mask_effect_0 ]
+ // [ mask_isolation_0 ]
+ // [ e0 ]
+ // One content layer, one clip mask.
+ ASSERT_EQ(2u, RootLayer()->children().size());
+ ASSERT_EQ(1u, ContentLayerCount());
+ ASSERT_EQ(1u, SynthesizedClipLayerCount());
+
+ const cc::Layer* content0 = RootLayer()->children()[0].get();
+ const cc::Layer* clip_mask0 = RootLayer()->children()[1].get();
+
+ constexpr int c0_id = 1;
+ constexpr int e0_id = 1;
+
+ EXPECT_EQ(ContentLayerAt(0), content0);
+ int c1_id = content0->clip_tree_index();
+ const cc::ClipNode& cc_c1 = *GetPropertyTrees().clip_tree.Node(c1_id);
+ EXPECT_EQ(gfx::RectF(50, 50, 300, 200), cc_c1.clip);
+ ASSERT_EQ(c0_id, cc_c1.parent_id);
+ int mask_isolation_0_id = content0->effect_tree_index();
+ const cc::EffectNode& mask_isolation_0 =
+ *GetPropertyTrees().effect_tree.Node(mask_isolation_0_id);
+ ASSERT_EQ(e0_id, mask_isolation_0.parent_id);
+ EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_0.blend_mode);
+
+ EXPECT_EQ(SynthesizedClipLayerAt(0), clip_mask0);
+ EXPECT_EQ(gfx::Size(300, 200), clip_mask0->bounds());
+ EXPECT_EQ(c1_id, clip_mask0->clip_tree_index());
+ int mask_effect_0_id = clip_mask0->effect_tree_index();
+ const cc::EffectNode& mask_effect_0 =
+ *GetPropertyTrees().effect_tree.Node(mask_effect_0_id);
+ ASSERT_EQ(mask_isolation_0_id, mask_effect_0.parent_id);
+ EXPECT_EQ(SkBlendMode::kDstIn, mask_effect_0.blend_mode);
+}
+
+TEST_F(PaintArtifactCompositorTest,
+ SynthesizedClipIndirectlyCompositedClipPath) {
+ // This tests the case that a clip node needs to be synthesized due to
+ // applying clip path to a composited effect.
+ FloatRoundedRect clip_rect(50, 50, 300, 200);
+ scoped_refptr<RefCountedPath> clip_path = base::AdoptRef(new RefCountedPath);
+ scoped_refptr<ClipPaintPropertyNode> c1 =
+ ClipPaintPropertyNode::Create(c0(), t0(), clip_rect, nullptr, clip_path);
+ scoped_refptr<EffectPaintPropertyNode> e1 = EffectPaintPropertyNode::Create(
+ e0(), t0(), c1, ColorFilter(), CompositorFilterOperations(), 1,
+ SkBlendMode::kSrcOver, CompositingReason::kWillChangeCompositingHint);
+
+ TestPaintArtifact artifact;
+ artifact.Chunk(t0(), c1, e1)
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack);
+ Update(artifact.Build());
+
+ // Expectation in effect stack diagram:
+ // l0 l1
+ // [ e1 ][ mask_effect_0 ]
+ // [ mask_isolation_0 ]
+ // [ e0 ]
+ // One content layer, one clip mask.
+ ASSERT_EQ(2u, RootLayer()->children().size());
+ ASSERT_EQ(1u, ContentLayerCount());
+ ASSERT_EQ(1u, SynthesizedClipLayerCount());
+
+ const cc::Layer* content0 = RootLayer()->children()[0].get();
+ const cc::Layer* clip_mask0 = RootLayer()->children()[1].get();
+
+ constexpr int c0_id = 1;
+ constexpr int e0_id = 1;
+
+ EXPECT_EQ(ContentLayerAt(0), content0);
+ int c1_id = content0->clip_tree_index();
+ const cc::ClipNode& cc_c1 = *GetPropertyTrees().clip_tree.Node(c1_id);
+ EXPECT_EQ(gfx::RectF(50, 50, 300, 200), cc_c1.clip);
+ ASSERT_EQ(c0_id, cc_c1.parent_id);
+ int e1_id = content0->effect_tree_index();
+ const cc::EffectNode& cc_e1 = *GetPropertyTrees().effect_tree.Node(e1_id);
+ EXPECT_EQ(c1_id, cc_e1.clip_id);
+ int mask_isolation_0_id = cc_e1.parent_id;
+ const cc::EffectNode& mask_isolation_0 =
+ *GetPropertyTrees().effect_tree.Node(mask_isolation_0_id);
+ ASSERT_EQ(e0_id, mask_isolation_0.parent_id);
+ EXPECT_EQ(c1_id, mask_isolation_0.clip_id);
+ EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_0.blend_mode);
+
+ EXPECT_EQ(SynthesizedClipLayerAt(0), clip_mask0);
+ EXPECT_EQ(gfx::Size(300, 200), clip_mask0->bounds());
+ EXPECT_EQ(c1_id, clip_mask0->clip_tree_index());
+ int mask_effect_0_id = clip_mask0->effect_tree_index();
+ const cc::EffectNode& mask_effect_0 =
+ *GetPropertyTrees().effect_tree.Node(mask_effect_0_id);
+ ASSERT_EQ(mask_isolation_0_id, mask_effect_0.parent_id);
+ EXPECT_EQ(c1_id, mask_effect_0.clip_id);
+ EXPECT_EQ(SkBlendMode::kDstIn, mask_effect_0.blend_mode);
+}
+
+TEST_F(PaintArtifactCompositorTest, SynthesizedClipContiguous) {
+ // This tests the case that a two back-to-back composited layers having
+ // the same composited rounded clip can share the synthesized mask.
+ scoped_refptr<TransformPaintPropertyNode> t1 =
+ TransformPaintPropertyNode::Create(
+ t0(), TransformationMatrix(), FloatPoint3D(), false, 0,
+ CompositingReason::kWillChangeCompositingHint);
+
+ FloatSize corner(5, 5);
+ FloatRoundedRect rrect(FloatRect(50, 50, 300, 200), corner, corner, corner,
+ corner);
+ scoped_refptr<ClipPaintPropertyNode> c1 = ClipPaintPropertyNode::Create(
+ c0(), t0(), rrect, nullptr, nullptr,
+ CompositingReason::kWillChangeCompositingHint);
+
+ TestPaintArtifact artifact;
+ artifact.Chunk(t0(), c1, e0())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack);
+ artifact.Chunk(t1, c1, e0())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack);
+ Update(artifact.Build());
+
+ // Expectation in effect stack diagram:
+ // l2
+ // l0 l1 [ mask_effect_0 ]
+ // [ mask_isolation_0 ]
+ // [ e0 ]
+ // Two content layers, one clip mask.
+ ASSERT_EQ(3u, RootLayer()->children().size());
+ ASSERT_EQ(2u, ContentLayerCount());
+ ASSERT_EQ(1u, SynthesizedClipLayerCount());
+
+ const cc::Layer* content0 = RootLayer()->children()[0].get();
+ const cc::Layer* content1 = RootLayer()->children()[1].get();
+ const cc::Layer* clip_mask0 = RootLayer()->children()[2].get();
+
+ constexpr int t0_id = 1;
+ constexpr int c0_id = 1;
+ constexpr int e0_id = 1;
+
+ EXPECT_EQ(ContentLayerAt(0), content0);
+ EXPECT_EQ(t0_id, content0->transform_tree_index());
+ int c1_id = content0->clip_tree_index();
+ const cc::ClipNode& cc_c1 = *GetPropertyTrees().clip_tree.Node(c1_id);
+ EXPECT_EQ(gfx::RectF(50, 50, 300, 200), cc_c1.clip);
+ ASSERT_EQ(c0_id, cc_c1.parent_id);
+ int mask_isolation_0_id = content0->effect_tree_index();
+ const cc::EffectNode& mask_isolation_0 =
+ *GetPropertyTrees().effect_tree.Node(mask_isolation_0_id);
+ ASSERT_EQ(e0_id, mask_isolation_0.parent_id);
+ EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_0.blend_mode);
+
+ EXPECT_EQ(ContentLayerAt(1), content1);
+ int t1_id = content1->transform_tree_index();
+ const cc::TransformNode& cc_t1 =
+ *GetPropertyTrees().transform_tree.Node(t1_id);
+ ASSERT_EQ(t0_id, cc_t1.parent_id);
+ EXPECT_EQ(c1_id, content1->clip_tree_index());
+ EXPECT_EQ(mask_isolation_0_id, content1->effect_tree_index());
+
+ EXPECT_EQ(SynthesizedClipLayerAt(0), clip_mask0);
+ EXPECT_EQ(gfx::Size(300, 200), clip_mask0->bounds());
+ EXPECT_EQ(t0_id, clip_mask0->transform_tree_index());
+ EXPECT_EQ(c1_id, clip_mask0->clip_tree_index());
+ int mask_effect_0_id = clip_mask0->effect_tree_index();
+ const cc::EffectNode& mask_effect_0 =
+ *GetPropertyTrees().effect_tree.Node(mask_effect_0_id);
+ ASSERT_EQ(mask_isolation_0_id, mask_effect_0.parent_id);
+ EXPECT_EQ(SkBlendMode::kDstIn, mask_effect_0.blend_mode);
+}
+
+TEST_F(PaintArtifactCompositorTest, SynthesizedClipDiscontiguous) {
+ // This tests the case that a two composited layers having the same
+ // composited rounded clip cannot share the synthesized mask if there is
+ // another layer in the middle.
+ scoped_refptr<TransformPaintPropertyNode> t1 =
+ TransformPaintPropertyNode::Create(
+ t0(), TransformationMatrix(), FloatPoint3D(), false, 0,
+ CompositingReason::kWillChangeCompositingHint);
+
+ FloatSize corner(5, 5);
+ FloatRoundedRect rrect(FloatRect(50, 50, 300, 200), corner, corner, corner,
+ corner);
+ scoped_refptr<ClipPaintPropertyNode> c1 = ClipPaintPropertyNode::Create(
+ c0(), t0(), rrect, nullptr, nullptr,
+ CompositingReason::kWillChangeCompositingHint);
+
+ TestPaintArtifact artifact;
+ artifact.Chunk(t0(), c1, e0())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack);
+ artifact.Chunk(t1, c0(), e0())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack);
+ artifact.Chunk(t0(), c1, e0())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack);
+ Update(artifact.Build());
+
+ // Expectation in effect stack diagram:
+ // l1 l4
+ // l0 [ mask_effect_0 ] l3 [ mask_effect_1 ]
+ // [ mask_isolation_0 ] l2 [ mask_isolation_1 ]
+ // [ e0 ]
+ // Three content layers, two clip mask.
+ ASSERT_EQ(5u, RootLayer()->children().size());
+ ASSERT_EQ(3u, ContentLayerCount());
+ ASSERT_EQ(2u, SynthesizedClipLayerCount());
+
+ const cc::Layer* content0 = RootLayer()->children()[0].get();
+ const cc::Layer* clip_mask0 = RootLayer()->children()[1].get();
+ const cc::Layer* content1 = RootLayer()->children()[2].get();
+ const cc::Layer* content2 = RootLayer()->children()[3].get();
+ const cc::Layer* clip_mask1 = RootLayer()->children()[4].get();
+
+ constexpr int t0_id = 1;
+ constexpr int c0_id = 1;
+ constexpr int e0_id = 1;
+
+ EXPECT_EQ(ContentLayerAt(0), content0);
+ EXPECT_EQ(t0_id, content0->transform_tree_index());
+ int c1_id = content0->clip_tree_index();
+ const cc::ClipNode& cc_c1 = *GetPropertyTrees().clip_tree.Node(c1_id);
+ EXPECT_EQ(gfx::RectF(50, 50, 300, 200), cc_c1.clip);
+ ASSERT_EQ(c0_id, cc_c1.parent_id);
+ int mask_isolation_0_id = content0->effect_tree_index();
+ const cc::EffectNode& mask_isolation_0 =
+ *GetPropertyTrees().effect_tree.Node(mask_isolation_0_id);
+ ASSERT_EQ(e0_id, mask_isolation_0.parent_id);
+ EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_0.blend_mode);
+
+ EXPECT_EQ(SynthesizedClipLayerAt(0), clip_mask0);
+ EXPECT_EQ(gfx::Size(300, 200), clip_mask0->bounds());
+ EXPECT_EQ(t0_id, clip_mask0->transform_tree_index());
+ EXPECT_EQ(c1_id, clip_mask0->clip_tree_index());
+ int mask_effect_0_id = clip_mask0->effect_tree_index();
+ const cc::EffectNode& mask_effect_0 =
+ *GetPropertyTrees().effect_tree.Node(mask_effect_0_id);
+ ASSERT_EQ(mask_isolation_0_id, mask_effect_0.parent_id);
+ EXPECT_EQ(SkBlendMode::kDstIn, mask_effect_0.blend_mode);
+
+ EXPECT_EQ(ContentLayerAt(1), content1);
+ int t1_id = content1->transform_tree_index();
+ const cc::TransformNode& cc_t1 =
+ *GetPropertyTrees().transform_tree.Node(t1_id);
+ ASSERT_EQ(t0_id, cc_t1.parent_id);
+ EXPECT_EQ(c0_id, content1->clip_tree_index());
+ EXPECT_EQ(e0_id, content1->effect_tree_index());
+
+ EXPECT_EQ(ContentLayerAt(2), content2);
+ EXPECT_EQ(t0_id, content2->transform_tree_index());
+ EXPECT_EQ(c1_id, content2->clip_tree_index());
+ int mask_isolation_1_id = content2->effect_tree_index();
+ const cc::EffectNode& mask_isolation_1 =
+ *GetPropertyTrees().effect_tree.Node(mask_isolation_1_id);
+ EXPECT_NE(mask_isolation_0_id, mask_isolation_1_id);
+ ASSERT_EQ(e0_id, mask_isolation_1.parent_id);
+ EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_1.blend_mode);
+
+ EXPECT_EQ(SynthesizedClipLayerAt(1), clip_mask1);
+ EXPECT_EQ(gfx::Size(300, 200), clip_mask1->bounds());
+ EXPECT_EQ(t0_id, clip_mask1->transform_tree_index());
+ EXPECT_EQ(c1_id, clip_mask1->clip_tree_index());
+ int mask_effect_1_id = clip_mask1->effect_tree_index();
+ const cc::EffectNode& mask_effect_1 =
+ *GetPropertyTrees().effect_tree.Node(mask_effect_1_id);
+ ASSERT_EQ(mask_isolation_1_id, mask_effect_1.parent_id);
+ EXPECT_EQ(SkBlendMode::kDstIn, mask_effect_1.blend_mode);
+}
+
+TEST_F(PaintArtifactCompositorTest, SynthesizedClipAcrossChildEffect) {
+ // This tests the case that an effect having the same output clip as the
+ // layers before and after it can share the synthesized mask.
+ FloatSize corner(5, 5);
+ FloatRoundedRect rrect(FloatRect(50, 50, 300, 200), corner, corner, corner,
+ corner);
+ scoped_refptr<ClipPaintPropertyNode> c1 = ClipPaintPropertyNode::Create(
+ c0(), t0(), rrect, nullptr, nullptr,
+ CompositingReason::kWillChangeCompositingHint);
+
+ scoped_refptr<EffectPaintPropertyNode> e1 = EffectPaintPropertyNode::Create(
+ e0(), t0(), c1, ColorFilter(), CompositorFilterOperations(), 1,
+ SkBlendMode::kSrcOver, CompositingReason::kWillChangeCompositingHint);
+
+ TestPaintArtifact artifact;
+ artifact.Chunk(t0(), c1, e0())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack);
+ artifact.Chunk(t0(), c1, e1)
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack);
+ artifact.Chunk(t0(), c1, e0())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack);
+ Update(artifact.Build());
+
+ // Expectation in effect stack diagram:
+ // l1 l3
+ // l0 [ e1 ] l2 [ mask_effect_0 ]
+ // [ mask_isolation_0 ]
+ // [ e0 ]
+ // Three content layers, one clip mask.
+ ASSERT_EQ(4u, RootLayer()->children().size());
+ ASSERT_EQ(3u, ContentLayerCount());
+ ASSERT_EQ(1u, SynthesizedClipLayerCount());
+
+ const cc::Layer* content0 = RootLayer()->children()[0].get();
+ const cc::Layer* content1 = RootLayer()->children()[1].get();
+ const cc::Layer* content2 = RootLayer()->children()[2].get();
+ const cc::Layer* clip_mask0 = RootLayer()->children()[3].get();
+
+ constexpr int c0_id = 1;
+ constexpr int e0_id = 1;
+
+ EXPECT_EQ(ContentLayerAt(0), content0);
+ int c1_id = content0->clip_tree_index();
+ const cc::ClipNode& cc_c1 = *GetPropertyTrees().clip_tree.Node(c1_id);
+ EXPECT_EQ(gfx::RectF(50, 50, 300, 200), cc_c1.clip);
+ ASSERT_EQ(c0_id, cc_c1.parent_id);
+ int mask_isolation_0_id = content0->effect_tree_index();
+ const cc::EffectNode& mask_isolation_0 =
+ *GetPropertyTrees().effect_tree.Node(mask_isolation_0_id);
+ ASSERT_EQ(e0_id, mask_isolation_0.parent_id);
+ EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_0.blend_mode);
+
+ EXPECT_EQ(ContentLayerAt(1), content1);
+ EXPECT_EQ(c1_id, content1->clip_tree_index());
+ int e1_id = content1->effect_tree_index();
+ const cc::EffectNode& cc_e1 = *GetPropertyTrees().effect_tree.Node(e1_id);
+ ASSERT_EQ(mask_isolation_0_id, cc_e1.parent_id);
+
+ EXPECT_EQ(ContentLayerAt(2), content2);
+ EXPECT_EQ(c1_id, content2->clip_tree_index());
+ EXPECT_EQ(mask_isolation_0_id, content2->effect_tree_index());
+
+ EXPECT_EQ(SynthesizedClipLayerAt(0), clip_mask0);
+ EXPECT_EQ(gfx::Size(300, 200), clip_mask0->bounds());
+ EXPECT_EQ(c1_id, clip_mask0->clip_tree_index());
+ int mask_effect_0_id = clip_mask0->effect_tree_index();
+ const cc::EffectNode& mask_effect_0 =
+ *GetPropertyTrees().effect_tree.Node(mask_effect_0_id);
+ ASSERT_EQ(mask_isolation_0_id, mask_effect_0.parent_id);
+ EXPECT_EQ(SkBlendMode::kDstIn, mask_effect_0.blend_mode);
+}
+
+TEST_F(PaintArtifactCompositorTest, SynthesizedClipRespectOutputClip) {
+ // This tests the case that a layer cannot share the synthesized mask despite
+ // having the same composited rounded clip if it's enclosed by an effect not
+ // clipped by the common clip.
+ FloatSize corner(5, 5);
+ FloatRoundedRect rrect(FloatRect(50, 50, 300, 200), corner, corner, corner,
+ corner);
+ scoped_refptr<ClipPaintPropertyNode> c1 = ClipPaintPropertyNode::Create(
+ c0(), t0(), rrect, nullptr, nullptr,
+ CompositingReason::kWillChangeCompositingHint);
+
+ CompositorFilterOperations non_trivial_filter;
+ non_trivial_filter.AppendBlurFilter(5);
+ scoped_refptr<EffectPaintPropertyNode> e1 = EffectPaintPropertyNode::Create(
+ e0(), t0(), c0(), ColorFilter(), non_trivial_filter, 1,
+ SkBlendMode::kSrcOver, CompositingReason::kWillChangeCompositingHint);
+
+ TestPaintArtifact artifact;
+ artifact.Chunk(t0(), c1, e0())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack);
+ artifact.Chunk(t0(), c1, e1)
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack);
+ artifact.Chunk(t0(), c1, e0())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack);
+ Update(artifact.Build());
+
+ // Expectation in effect stack diagram:
+ // l3
+ // l1 l2 [ mask_effect_1 ] l5
+ // l0 [ mask_effect_0 ][ mask_isolation_1 ] l4 [ mask_effect_2 ]
+ // [ mask_isolation_0 ][ e1 ][ mask_isolation_2 ]
+ // [ e0 ]
+ // Three content layers, three clip mask.
+ ASSERT_EQ(6u, RootLayer()->children().size());
+ ASSERT_EQ(3u, ContentLayerCount());
+ ASSERT_EQ(3u, SynthesizedClipLayerCount());
+
+ const cc::Layer* content0 = RootLayer()->children()[0].get();
+ const cc::Layer* clip_mask0 = RootLayer()->children()[1].get();
+ const cc::Layer* content1 = RootLayer()->children()[2].get();
+ const cc::Layer* clip_mask1 = RootLayer()->children()[3].get();
+ const cc::Layer* content2 = RootLayer()->children()[4].get();
+ const cc::Layer* clip_mask2 = RootLayer()->children()[5].get();
+
+ constexpr int c0_id = 1;
+ constexpr int e0_id = 1;
+
+ EXPECT_EQ(ContentLayerAt(0), content0);
+ int c1_id = content0->clip_tree_index();
+ const cc::ClipNode& cc_c1 = *GetPropertyTrees().clip_tree.Node(c1_id);
+ EXPECT_EQ(gfx::RectF(50, 50, 300, 200), cc_c1.clip);
+ ASSERT_EQ(c0_id, cc_c1.parent_id);
+ int mask_isolation_0_id = content0->effect_tree_index();
+ const cc::EffectNode& mask_isolation_0 =
+ *GetPropertyTrees().effect_tree.Node(mask_isolation_0_id);
+ ASSERT_EQ(e0_id, mask_isolation_0.parent_id);
+ EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_0.blend_mode);
+
+ EXPECT_EQ(SynthesizedClipLayerAt(0), clip_mask0);
+ EXPECT_EQ(gfx::Size(300, 200), clip_mask0->bounds());
+ EXPECT_EQ(c1_id, clip_mask0->clip_tree_index());
+ int mask_effect_0_id = clip_mask0->effect_tree_index();
+ const cc::EffectNode& mask_effect_0 =
+ *GetPropertyTrees().effect_tree.Node(mask_effect_0_id);
+ ASSERT_EQ(mask_isolation_0_id, mask_effect_0.parent_id);
+ EXPECT_EQ(SkBlendMode::kDstIn, mask_effect_0.blend_mode);
+
+ EXPECT_EQ(ContentLayerAt(1), content1);
+ EXPECT_EQ(c1_id, content1->clip_tree_index());
+ int mask_isolation_1_id = content1->effect_tree_index();
+ const cc::EffectNode& mask_isolation_1 =
+ *GetPropertyTrees().effect_tree.Node(mask_isolation_1_id);
+ EXPECT_NE(mask_isolation_0_id, mask_isolation_1_id);
+ EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_1.blend_mode);
+ int e1_id = mask_isolation_1.parent_id;
+ const cc::EffectNode& cc_e1 = *GetPropertyTrees().effect_tree.Node(e1_id);
+ ASSERT_EQ(e0_id, cc_e1.parent_id);
+
+ EXPECT_EQ(SynthesizedClipLayerAt(1), clip_mask1);
+ EXPECT_EQ(gfx::Size(300, 200), clip_mask1->bounds());
+ EXPECT_EQ(c1_id, clip_mask1->clip_tree_index());
+ int mask_effect_1_id = clip_mask1->effect_tree_index();
+ const cc::EffectNode& mask_effect_1 =
+ *GetPropertyTrees().effect_tree.Node(mask_effect_1_id);
+ ASSERT_EQ(mask_isolation_1_id, mask_effect_1.parent_id);
+ EXPECT_EQ(SkBlendMode::kDstIn, mask_effect_1.blend_mode);
+
+ EXPECT_EQ(ContentLayerAt(2), content2);
+ EXPECT_EQ(c1_id, content2->clip_tree_index());
+ int mask_isolation_2_id = content2->effect_tree_index();
+ const cc::EffectNode& mask_isolation_2 =
+ *GetPropertyTrees().effect_tree.Node(mask_isolation_2_id);
+ EXPECT_NE(mask_isolation_0_id, mask_isolation_2_id);
+ EXPECT_NE(mask_isolation_1_id, mask_isolation_2_id);
+ ASSERT_EQ(e0_id, mask_isolation_2.parent_id);
+ EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_2.blend_mode);
+
+ EXPECT_EQ(SynthesizedClipLayerAt(2), clip_mask2);
+ EXPECT_EQ(gfx::Size(300, 200), clip_mask2->bounds());
+ EXPECT_EQ(c1_id, clip_mask2->clip_tree_index());
+ int mask_effect_2_id = clip_mask2->effect_tree_index();
+ const cc::EffectNode& mask_effect_2 =
+ *GetPropertyTrees().effect_tree.Node(mask_effect_2_id);
+ ASSERT_EQ(mask_isolation_2_id, mask_effect_2.parent_id);
+ EXPECT_EQ(SkBlendMode::kDstIn, mask_effect_2.blend_mode);
+}
+
+TEST_F(PaintArtifactCompositorTest, SynthesizedClipDelegateBlending) {
+ // This tests the case that an effect with exotic blending cannot share
+ // the synthesized mask with its siblings because its blending has to be
+ // applied by the outermost mask.
+ FloatSize corner(5, 5);
+ FloatRoundedRect rrect(FloatRect(50, 50, 300, 200), corner, corner, corner,
+ corner);
+ scoped_refptr<ClipPaintPropertyNode> c1 = ClipPaintPropertyNode::Create(
+ c0(), t0(), rrect, nullptr, nullptr,
+ CompositingReason::kWillChangeCompositingHint);
+
+ scoped_refptr<EffectPaintPropertyNode> e1 = EffectPaintPropertyNode::Create(
+ e0(), t0(), c1, ColorFilter(), CompositorFilterOperations(), 1,
+ SkBlendMode::kMultiply, CompositingReason::kWillChangeCompositingHint);
+
+ TestPaintArtifact artifact;
+ artifact.Chunk(t0(), c1, e0())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack);
+ artifact.Chunk(t0(), c1, e1)
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack);
+ artifact.Chunk(t0(), c1, e0())
+ .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack);
+ Update(artifact.Build());
+
+ // Expectation in effect stack diagram:
+ // l1 l2 l3 l5
+ // l0 [ mask_effect_0 ][ e1 ][ mask_effect_1 ] l4 [ mask_effect_2 ]
+ // [ mask_isolation_0 ][ mask_isolation_1 ][ mask_isolation_2 ]
+ // [ e0 ]
+ // Three content layers, three clip mask.
+ ASSERT_EQ(6u, RootLayer()->children().size());
+ ASSERT_EQ(3u, ContentLayerCount());
+ ASSERT_EQ(3u, SynthesizedClipLayerCount());
+
+ const cc::Layer* content0 = RootLayer()->children()[0].get();
+ const cc::Layer* clip_mask0 = RootLayer()->children()[1].get();
+ const cc::Layer* content1 = RootLayer()->children()[2].get();
+ const cc::Layer* clip_mask1 = RootLayer()->children()[3].get();
+ const cc::Layer* content2 = RootLayer()->children()[4].get();
+ const cc::Layer* clip_mask2 = RootLayer()->children()[5].get();
+
+ constexpr int c0_id = 1;
+ constexpr int e0_id = 1;
+
+ EXPECT_EQ(ContentLayerAt(0), content0);
+ int c1_id = content0->clip_tree_index();
+ const cc::ClipNode& cc_c1 = *GetPropertyTrees().clip_tree.Node(c1_id);
+ EXPECT_EQ(gfx::RectF(50, 50, 300, 200), cc_c1.clip);
+ ASSERT_EQ(c0_id, cc_c1.parent_id);
+ int mask_isolation_0_id = content0->effect_tree_index();
+ const cc::EffectNode& mask_isolation_0 =
+ *GetPropertyTrees().effect_tree.Node(mask_isolation_0_id);
+ ASSERT_EQ(e0_id, mask_isolation_0.parent_id);
+ EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_0.blend_mode);
+
+ EXPECT_EQ(SynthesizedClipLayerAt(0), clip_mask0);
+ EXPECT_EQ(gfx::Size(300, 200), clip_mask0->bounds());
+ EXPECT_EQ(c1_id, clip_mask0->clip_tree_index());
+ int mask_effect_0_id = clip_mask0->effect_tree_index();
+ const cc::EffectNode& mask_effect_0 =
+ *GetPropertyTrees().effect_tree.Node(mask_effect_0_id);
+ ASSERT_EQ(mask_isolation_0_id, mask_effect_0.parent_id);
+ EXPECT_EQ(SkBlendMode::kDstIn, mask_effect_0.blend_mode);
+
+ EXPECT_EQ(ContentLayerAt(1), content1);
+ EXPECT_EQ(c1_id, content1->clip_tree_index());
+ int e1_id = content1->effect_tree_index();
+ const cc::EffectNode& cc_e1 = *GetPropertyTrees().effect_tree.Node(e1_id);
+ EXPECT_EQ(SkBlendMode::kSrcOver, cc_e1.blend_mode);
+ int mask_isolation_1_id = cc_e1.parent_id;
+ const cc::EffectNode& mask_isolation_1 =
+ *GetPropertyTrees().effect_tree.Node(mask_isolation_1_id);
+ EXPECT_NE(mask_isolation_0_id, mask_isolation_1_id);
+ ASSERT_EQ(e0_id, mask_isolation_1.parent_id);
+ EXPECT_EQ(SkBlendMode::kMultiply, mask_isolation_1.blend_mode);
+
+ EXPECT_EQ(SynthesizedClipLayerAt(1), clip_mask1);
+ EXPECT_EQ(gfx::Size(300, 200), clip_mask1->bounds());
+ EXPECT_EQ(c1_id, clip_mask1->clip_tree_index());
+ int mask_effect_1_id = clip_mask1->effect_tree_index();
+ const cc::EffectNode& mask_effect_1 =
+ *GetPropertyTrees().effect_tree.Node(mask_effect_1_id);
+ ASSERT_EQ(mask_isolation_1_id, mask_effect_1.parent_id);
+ EXPECT_EQ(SkBlendMode::kDstIn, mask_effect_1.blend_mode);
+
+ EXPECT_EQ(ContentLayerAt(2), content2);
+ EXPECT_EQ(c1_id, content2->clip_tree_index());
+ int mask_isolation_2_id = content2->effect_tree_index();
+ const cc::EffectNode& mask_isolation_2 =
+ *GetPropertyTrees().effect_tree.Node(mask_isolation_2_id);
+ EXPECT_NE(mask_isolation_0_id, mask_isolation_2_id);
+ EXPECT_NE(mask_isolation_1_id, mask_isolation_2_id);
+ ASSERT_EQ(e0_id, mask_isolation_2.parent_id);
+ EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_0.blend_mode);
+
+ EXPECT_EQ(SynthesizedClipLayerAt(2), clip_mask2);
+ EXPECT_EQ(gfx::Size(300, 200), clip_mask2->bounds());
+ EXPECT_EQ(c1_id, clip_mask2->clip_tree_index());
+ int mask_effect_2_id = clip_mask2->effect_tree_index();
+ const cc::EffectNode& mask_effect_2 =
+ *GetPropertyTrees().effect_tree.Node(mask_effect_2_id);
+ ASSERT_EQ(mask_isolation_2_id, mask_effect_2.parent_id);
+ EXPECT_EQ(SkBlendMode::kDstIn, mask_effect_2.blend_mode);
+}
+
+TEST_F(PaintArtifactCompositorTest, WillBeRemovedFromFrame) {
+ scoped_refptr<EffectPaintPropertyNode> effect =
+ CreateSampleEffectNodeWithElementId();
+ TestPaintArtifact artifact;
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ effect.get())
+ .RectDrawing(FloatRect(100, 100, 200, 100), Color::kBlack);
+ Update(artifact.Build());
+
+ ASSERT_EQ(1u, ContentLayerCount());
+ WillBeRemovedFromFrame();
+ // We would need a fake or mock LayerTreeHost to validate that we
+ // unregister all element ids, so just check layer count for now.
+ EXPECT_EQ(0u, ContentLayerCount());
+}
+
+TEST_F(PaintArtifactCompositorTest, ContentsNonOpaque) {
+ TestPaintArtifact artifact;
+ artifact.Chunk(DefaultPaintChunkProperties())
+ .RectDrawing(FloatRect(100, 100, 200, 200), Color::kBlack);
+ Update(artifact.Build());
+ ASSERT_EQ(1u, ContentLayerCount());
+ EXPECT_FALSE(ContentLayerAt(0)->contents_opaque());
+}
+
+TEST_F(PaintArtifactCompositorTest, ContentsOpaque) {
+ TestPaintArtifact artifact;
+ artifact.Chunk(DefaultPaintChunkProperties())
+ .RectDrawing(FloatRect(100, 100, 200, 200), Color::kBlack)
+ .KnownToBeOpaque();
+ Update(artifact.Build());
+ ASSERT_EQ(1u, ContentLayerCount());
+ EXPECT_TRUE(ContentLayerAt(0)->contents_opaque());
+}
+
+TEST_F(PaintArtifactCompositorTest, ContentsOpaqueSubpixel) {
+ TestPaintArtifact artifact;
+ artifact.Chunk(DefaultPaintChunkProperties())
+ .RectDrawing(FloatRect(100.5, 100.5, 200, 200), Color::kBlack)
+ .KnownToBeOpaque();
+ Update(artifact.Build());
+ ASSERT_EQ(1u, ContentLayerCount());
+ EXPECT_EQ(gfx::Size(201, 201), ContentLayerAt(0)->bounds());
+ EXPECT_FALSE(ContentLayerAt(0)->contents_opaque());
+}
+
+TEST_F(PaintArtifactCompositorTest, ContentsOpaqueUnitedNonOpaque) {
+ TestPaintArtifact artifact;
+ artifact.Chunk(DefaultPaintChunkProperties())
+ .RectDrawing(FloatRect(100, 100, 200, 200), Color::kBlack)
+ .KnownToBeOpaque()
+ .Chunk(DefaultPaintChunkProperties())
+ .RectDrawing(FloatRect(200, 200, 200, 200), Color::kBlack)
+ .KnownToBeOpaque();
+ Update(artifact.Build());
+ ASSERT_EQ(1u, ContentLayerCount());
+ EXPECT_EQ(gfx::Size(300, 300), ContentLayerAt(0)->bounds());
+ EXPECT_FALSE(ContentLayerAt(0)->contents_opaque());
+}
+
+TEST_F(PaintArtifactCompositorTest, ContentsOpaqueUnitedOpaque1) {
+ TestPaintArtifact artifact;
+ artifact.Chunk(DefaultPaintChunkProperties())
+ .RectDrawing(FloatRect(100, 100, 300, 300), Color::kBlack)
+ .KnownToBeOpaque()
+ .Chunk(DefaultPaintChunkProperties())
+ .RectDrawing(FloatRect(200, 200, 200, 200), Color::kBlack)
+ .KnownToBeOpaque();
+ Update(artifact.Build());
+ ASSERT_EQ(1u, ContentLayerCount());
+ EXPECT_EQ(gfx::Size(300, 300), ContentLayerAt(0)->bounds());
+ EXPECT_TRUE(ContentLayerAt(0)->contents_opaque());
+}
+
+TEST_F(PaintArtifactCompositorTest, ContentsOpaqueUnitedOpaque2) {
+ TestPaintArtifact artifact;
+ artifact.Chunk(DefaultPaintChunkProperties())
+ .RectDrawing(FloatRect(100, 100, 200, 200), Color::kBlack)
+ .KnownToBeOpaque()
+ .Chunk(DefaultPaintChunkProperties())
+ .RectDrawing(FloatRect(100, 100, 300, 300), Color::kBlack)
+ .KnownToBeOpaque();
+ Update(artifact.Build());
+ ASSERT_EQ(1u, ContentLayerCount());
+ EXPECT_EQ(gfx::Size(300, 300), ContentLayerAt(0)->bounds());
+ // TODO(crbug.com/701991): Upgrade GeometryMapper to make this test pass with
+ // the following EXPECT_FALSE changed to EXPECT_TRUE.
+ EXPECT_FALSE(ContentLayerAt(0)->contents_opaque());
+}
+
+TEST_F(PaintArtifactCompositorTest, DecompositeEffectWithNoOutputClip) {
+ // This test verifies effect nodes with no output clip correctly decomposites
+ // if there is no compositing reasons.
+ scoped_refptr<ClipPaintPropertyNode> clip1 = ClipPaintPropertyNode::Create(
+ ClipPaintPropertyNode::Root(), TransformPaintPropertyNode::Root(),
+ FloatRoundedRect(75, 75, 100, 100));
+
+ scoped_refptr<EffectPaintPropertyNode> effect1 =
+ EffectPaintPropertyNode::Create(
+ EffectPaintPropertyNode::Root(), TransformPaintPropertyNode::Root(),
+ nullptr, kColorFilterNone, CompositorFilterOperations(), 0.5,
+ SkBlendMode::kSrcOver);
+
+ TestPaintArtifact artifact;
+ artifact.Chunk(DefaultPaintChunkProperties())
+ .RectDrawing(FloatRect(50, 50, 100, 100), Color::kGray);
+ artifact.Chunk(TransformPaintPropertyNode::Root(), clip1.get(), effect1.get())
+ .RectDrawing(FloatRect(100, 100, 100, 100), Color::kGray);
+ Update(artifact.Build());
+ ASSERT_EQ(1u, ContentLayerCount());
+
+ const cc::Layer* layer = ContentLayerAt(0);
+ EXPECT_EQ(gfx::Vector2dF(50.f, 50.f), layer->offset_to_transform_parent());
+ EXPECT_EQ(gfx::Size(125, 125), layer->bounds());
+ EXPECT_EQ(1, layer->effect_tree_index());
+}
+
+TEST_F(PaintArtifactCompositorTest, CompositedEffectWithNoOutputClip) {
+ // This test verifies effect nodes with no output clip but has compositing
+ // reason correctly squash children chunks and assign clip node.
+ scoped_refptr<ClipPaintPropertyNode> clip1 = ClipPaintPropertyNode::Create(
+ ClipPaintPropertyNode::Root(), TransformPaintPropertyNode::Root(),
+ FloatRoundedRect(75, 75, 100, 100));
+
+ scoped_refptr<EffectPaintPropertyNode> effect1 =
+ EffectPaintPropertyNode::Create(
+ EffectPaintPropertyNode::Root(), TransformPaintPropertyNode::Root(),
+ nullptr, kColorFilterNone, CompositorFilterOperations(), 0.5,
+ SkBlendMode::kSrcOver, CompositingReason::kAll);
+
+ TestPaintArtifact artifact;
+ artifact
+ .Chunk(TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ effect1.get())
+ .RectDrawing(FloatRect(50, 50, 100, 100), Color::kGray);
+ artifact.Chunk(TransformPaintPropertyNode::Root(), clip1.get(), effect1.get())
+ .RectDrawing(FloatRect(100, 100, 100, 100), Color::kGray);
+ Update(artifact.Build());
+ ASSERT_EQ(1u, ContentLayerCount());
+
+ const cc::Layer* layer = ContentLayerAt(0);
+ EXPECT_EQ(gfx::Vector2dF(50.f, 50.f), layer->offset_to_transform_parent());
+ EXPECT_EQ(gfx::Size(125, 125), layer->bounds());
+ EXPECT_EQ(1, layer->clip_tree_index());
+ EXPECT_EQ(2, layer->effect_tree_index());
+}
+
+TEST_F(PaintArtifactCompositorTest, LayerRasterInvalidationWithClip) {
+ // The layer's painting is initially not clipped.
+ auto clip = ClipPaintPropertyNode::Create(ClipPaintPropertyNode::Root(),
+ TransformPaintPropertyNode::Root(),
+ FloatRoundedRect(10, 20, 300, 400));
+ TestPaintArtifact artifact1;
+ artifact1
+ .Chunk(TransformPaintPropertyNode::Root(), clip,
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(FloatRect(50, 50, 200, 200), Color::kBlack);
+ Update(artifact1.Build());
+ ASSERT_EQ(1u, ContentLayerCount());
+
+ auto* layer = ContentLayerAt(0);
+ EXPECT_EQ(gfx::Vector2dF(50, 50), layer->offset_to_transform_parent());
+ EXPECT_EQ(gfx::Size(200, 200), layer->bounds());
+ EXPECT_THAT(
+ layer->GetPicture(),
+ Pointee(DrawsRectangle(FloatRect(0, 0, 200, 200), Color::kBlack)));
+
+ // The layer's painting overflows the left, top, right edges of the clip .
+ TestPaintArtifact artifact2;
+ artifact2
+ .Chunk(artifact1.Client(0), TransformPaintPropertyNode::Root(), clip,
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(artifact1.Client(1), FloatRect(0, 0, 400, 200),
+ Color::kBlack);
+ layer->ResetNeedsDisplayForTesting();
+ Update(artifact2.Build());
+ ASSERT_EQ(1u, ContentLayerCount());
+ ASSERT_EQ(layer, ContentLayerAt(0));
+
+ // Invalidate the first chunk because its transform in layer changed.
+ EXPECT_EQ(gfx::Rect(0, 0, 300, 180), layer->update_rect());
+ EXPECT_EQ(gfx::Vector2dF(10, 20), layer->offset_to_transform_parent());
+ EXPECT_EQ(gfx::Size(300, 180), layer->bounds());
+ EXPECT_THAT(
+ layer->GetPicture(),
+ Pointee(DrawsRectangle(FloatRect(0, 0, 390, 180), Color::kBlack)));
+
+ // The layer's painting overflows all edges of the clip.
+ TestPaintArtifact artifact3;
+ artifact3
+ .Chunk(artifact1.Client(0), TransformPaintPropertyNode::Root(), clip,
+ EffectPaintPropertyNode::Root())
+ .RectDrawing(artifact1.Client(1), FloatRect(-100, -200, 500, 800),
+ Color::kBlack);
+ layer->ResetNeedsDisplayForTesting();
+ Update(artifact3.Build());
+ ASSERT_EQ(1u, ContentLayerCount());
+ ASSERT_EQ(layer, ContentLayerAt(0));
+
+ // We should not invalidate the layer because the origin didn't change
+ // because of the clip.
+ EXPECT_EQ(gfx::Rect(), layer->update_rect());
+ EXPECT_EQ(gfx::Vector2dF(10, 20), layer->offset_to_transform_parent());
+ EXPECT_EQ(gfx::Size(300, 400), layer->bounds());
+ EXPECT_THAT(
+ layer->GetPicture(),
+ Pointee(DrawsRectangle(FloatRect(0, 0, 390, 580), Color::kBlack)));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.cc b/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.cc
new file mode 100644
index 00000000000..ce6373cede5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.cc
@@ -0,0 +1,731 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.h"
+
+#include "cc/paint/display_item_list.h"
+#include "cc/paint/paint_op_buffer.h"
+#include "cc/paint/render_surface_filters.h"
+#include "third_party/blink/renderer/platform/graphics/compositing/chunk_to_layer_mapper.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/paint/display_item_list.h"
+#include "third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h"
+#include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_chunk.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_chunk_subset.h"
+#include "third_party/blink/renderer/platform/graphics/paint/property_tree_state.h"
+#include "third_party/blink/renderer/platform/graphics/paint/raster_invalidation_tracking.h"
+
+namespace blink {
+
+namespace {
+
+class ConversionContext {
+ public:
+ ConversionContext(const PropertyTreeState& layer_state,
+ const gfx::Vector2dF& layer_offset,
+ const FloatSize& visual_rect_subpixel_offset,
+ cc::DisplayItemList& cc_list)
+ : layer_state_(layer_state),
+ layer_offset_(layer_offset),
+ current_transform_(layer_state.Transform()),
+ current_clip_(layer_state.Clip()),
+ current_effect_(layer_state.Effect()),
+ chunk_to_layer_mapper_(layer_state_,
+ layer_offset_,
+ visual_rect_subpixel_offset),
+ cc_list_(cc_list) {}
+ ~ConversionContext();
+
+ // The main function of this class. It converts a list of paint chunks into
+ // non-pair display items, and paint properties associated with them are
+ // implemented by paired display items.
+ // This is done by closing and opening paired items to adjust the current
+ // property state to the chunk's state when each chunk is consumed.
+ // Note that the clip/effect state is "lazy" in the sense that it stays
+ // in whatever state the last chunk left with, and only adjusted when
+ // a new chunk is consumed. The class implemented a few helpers to manage
+ // state switching so that paired display items are nested properly.
+ //
+ // State management example (transform tree omitted).
+ // Corresponds to unit test PaintChunksToCcLayerTest.InterleavedClipEffect:
+ // Clip tree: C0 <-- C1 <-- C2 <-- C3 <-- C4
+ // Effect tree: E0(clip=C0) <-- E1(clip=C2) <-- E2(clip=C4)
+ // Layer state: C0, E0
+ // Paint chunks: P0(C3, E0), P1(C4, E2), P2(C3, E1), P3(C4, E0)
+ // Initialization:
+ // The current state is initalized with the layer state, and starts with
+ // an empty state stack.
+ // current_clip = C0
+ // current_effect = E0
+ // state_stack = []
+ // When P0 is consumed, C1, C2 and C3 need to be applied to the state:
+ // Output: Begin_C1 Begin_C2 Begin_C3 Draw_P0
+ // current_clip = C3
+ // state_stack = [C0, C1, C2]
+ // When P1 is consumed, C3 needs to be closed before E1 can be entered,
+ // then C3 and C4 need to be entered before E2 can be entered:
+ // Output: End_C3 Begin_E1 Begin_C3 Begin_C4 Begin_E2 Draw_P1
+ // current_clip = C4
+ // current_effect = E2
+ // state_stack = [C0, C1, E0, C2, C3, E1]
+ // When P2 is consumed, E2 then C4 need to be exited:
+ // Output: End_E2 End_C4 Draw_P2
+ // current_clip = C3
+ // current_effect = E1
+ // state_stack = [C0, C1, E0, C2]
+ // When P3 is consumed, C3 must exit before E1 can be exited, then we can
+ // enter C3 and C4:
+ // Output: End_C3 End_E1 Enter_C3 Enter_C4 Draw_P3
+ // current_clip = C4
+ // current_effect = E0
+ // state_stack = [C0, C1, C2, C3]
+ // At last, close all pushed states to balance pairs (this happens when the
+ // context object is destructed):
+ // Output: End_C4 End_C3 End_C2 End_C1
+ void Convert(const PaintChunkSubset&, const DisplayItemList&);
+
+ private:
+ // Adjust the translation of the whole display list relative to layer offset.
+ // It's only called if we actually paint anything.
+ void TranslateForLayerOffsetOnce();
+
+ // Switch the current property tree state to the chunk's state. It's only
+ // called if we actually paint anything, and should execute for a chunk
+ // only once.
+ void SwitchToChunkState(const PaintChunk&);
+
+ // Switch the current clip to the target state, staying in the same effect.
+ // It is no-op if the context is already in the target state.
+ // Otherwise zero or more clips will be popped from or pushed onto the
+ // current state stack.
+ // INPUT:
+ // The target clip must be a descendant of the input clip of current effect.
+ // OUTPUT:
+ // The current transform may be changed.
+ // The current clip will change to the target clip.
+ // The current effect will not change.
+ void SwitchToClip(const ClipPaintPropertyNode*);
+
+ // Switch the current effect to the target state.
+ // It is no-op if the context is already in the target state.
+ // Otherwise zero or more effect effects will be popped from or pushed onto
+ // the state stack. As effects getting popped from the stack, clips applied
+ // on top of them will be popped as well. Also clips will be pushed at
+ // appropriate steps to apply output clip to newly pushed effects.
+ // INPUT:
+ // The target effect must be a descendant of the layer's effect.
+ // OUTPUT:
+ // The current transform may be changed.
+ // The current clip may be changed, and is guaranteed to be a descendant of
+ // the output clip of the target effect.
+ // The current effect will change to the target effect.
+ void SwitchToEffect(const EffectPaintPropertyNode*);
+
+ // Switch the current transform to the target state.
+ void SwitchToTransform(const TransformPaintPropertyNode*);
+ // End the transform state that is estalished by SwitchToTransform().
+ // Called when the next chunk has different property tree state and when we
+ // have processed all chunks.
+ void EndTransform();
+
+ // Applies combined transform from |current_transform_| to |target_transform|
+ // This function doesn't change |current_transform_|.
+ void ApplyTransform(const TransformPaintPropertyNode* target_transform) {
+ if (target_transform == current_transform_)
+ return;
+ auto sk_matrix = GetSkMatrix(target_transform);
+ if (!sk_matrix.isIdentity())
+ cc_list_.push<cc::ConcatOp>(sk_matrix);
+ }
+
+ SkMatrix GetSkMatrix(
+ const TransformPaintPropertyNode* target_transform) const {
+ return SkMatrix(TransformationMatrix::ToSkMatrix44(
+ GeometryMapper::SourceToDestinationProjection(target_transform,
+ current_transform_)));
+ }
+
+ void AppendRestore(size_t n) {
+ cc_list_.StartPaint();
+ while (n--)
+ cc_list_.push<cc::RestoreOp>();
+ cc_list_.EndPaintOfPairedEnd();
+ }
+
+ // Starts an effect state by adjusting clip and transform state, applying
+ // the effect as a SaveLayer[Alpha]Op (whose bounds will be updated in
+ // EndEffect()), and updating the current state.
+ void StartEffect(const EffectPaintPropertyNode*);
+ // Ends the effect on the top of the state stack if the stack is not empty,
+ // and update the bounds of the SaveLayer[Alpha]Op of the effect.
+ void EndEffect();
+ void UpdateEffectBounds(const FloatRect&, const TransformPaintPropertyNode*);
+
+ // Starts a clip state by adjusting the transform state, applying
+ // |combined_clip_rect| which is combined from multiple rectangular
+ // consecutive clips, and updating the current state.
+ // |lowest_combined_clip_node| is the lowest node of the combined clips.
+ void StartCombinedClip(
+ const FloatRect& combined_clip_rect,
+ const ClipPaintPropertyNode* lowest_combined_clip_node);
+ // Starts a clip state by adjusting the transform state, applying the single
+ // clip node which can't be combined with other clips, and updating the
+ // current state.
+ void StartSingleClip(const ClipPaintPropertyNode*);
+ // Pop one clip state from the top of the stack.
+ void EndClip();
+ // Pop clip states from the top of the stack until the top is an effect state
+ // or the stack is empty.
+ void EndClips();
+
+ // State stack.
+ // The size of the stack is the number of nested paired items that are
+ // currently nested. Note that this is a "restore stack", i.e. the top
+ // element does not represent the current state, but the state prior to
+ // applying the last paired begin.
+ struct StateEntry {
+ // Remembers the type of paired begin that caused a state to be saved.
+ // This is for checking integrity of the algorithm.
+ enum PairedType { kClip, kEffect } type;
+ int saved_count;
+
+ const TransformPaintPropertyNode* transform;
+ const ClipPaintPropertyNode* clip;
+ const EffectPaintPropertyNode* effect;
+ // See ConversionContext::previous_transform_.
+ const TransformPaintPropertyNode* previous_transform;
+ };
+ void PushState(StateEntry::PairedType, int saved_count);
+ void PopState();
+ Vector<StateEntry> state_stack_;
+
+ const PropertyTreeState& layer_state_;
+ gfx::Vector2dF layer_offset_;
+ bool translated_for_layer_offset_ = false;
+
+ const TransformPaintPropertyNode* current_transform_;
+ const ClipPaintPropertyNode* current_clip_;
+ const EffectPaintPropertyNode* current_effect_;
+
+ // The previous transform state before SwitchToTransform() within the current
+ // clip/effect state. When the next chunk's transform is different from the
+ // current transform we should restore to this transform using EndTransform()
+ // which will set this field to nullptr. When a new clip/effect state starts,
+ // the value of this field will be saved into the state stack and set to
+ // nullptr. When the clip/effect state ends, this field will be restored to
+ // the saved value.
+ const TransformPaintPropertyNode* previous_transform_ = nullptr;
+
+ // This structure accumulates bounds of all chunks under an effect. When an
+ // effect starts, we emit a SaveLayer[Alpha]Op with null bounds starts, and
+ // push a new |EffectBoundsInfo| onto |effect_bounds_stack_|. When the effect
+ // ends, we update the bounds of the op.
+ struct EffectBoundsInfo {
+ // The id of the SaveLayer[Alpha]Op for this effect. It's recorded when we
+ // push the op for this effect, and used when this effect ends in
+ // UpdateSaveLayerBounds().
+ size_t save_layer_id;
+ // The transform space when the SaveLayer[Alpha]Op was emitted.
+ const TransformPaintPropertyNode* transform;
+ // Records the bounds of the effect which initiated the entry. Note that
+ // the effect is not |this->effect| (which is the previous effect), but the
+ // |current_effect_| when this entry is the top of the stack.
+ FloatRect bounds;
+ };
+ Vector<EffectBoundsInfo> effect_bounds_stack_;
+
+ ChunkToLayerMapper chunk_to_layer_mapper_;
+
+ cc::DisplayItemList& cc_list_;
+};
+
+ConversionContext::~ConversionContext() {
+ // End all states.
+ while (state_stack_.size()) {
+ if (state_stack_.back().type == StateEntry::kEffect)
+ EndEffect();
+ else
+ EndClip();
+ }
+ EndTransform();
+ if (translated_for_layer_offset_)
+ AppendRestore(1);
+}
+
+void ConversionContext::TranslateForLayerOffsetOnce() {
+ if (translated_for_layer_offset_ || layer_offset_.IsZero())
+ return;
+
+ cc_list_.StartPaint();
+ cc_list_.push<cc::SaveOp>();
+ cc_list_.push<cc::TranslateOp>(-layer_offset_.x(), -layer_offset_.y());
+ cc_list_.EndPaintOfPairedBegin();
+ translated_for_layer_offset_ = true;
+}
+
+void ConversionContext::SwitchToChunkState(const PaintChunk& chunk) {
+ chunk_to_layer_mapper_.SwitchToChunk(chunk);
+
+ const auto& chunk_state = chunk.properties.property_tree_state;
+ SwitchToEffect(chunk_state.Effect());
+ SwitchToClip(chunk_state.Clip());
+ SwitchToTransform(chunk_state.Transform());
+}
+
+void ConversionContext::SwitchToClip(const ClipPaintPropertyNode* target_clip) {
+ if (target_clip == current_clip_)
+ return;
+
+ // Step 1: Exit all clips until the lowest common ancestor is found.
+ const ClipPaintPropertyNode* lca_clip =
+ &LowestCommonAncestor(*target_clip, *current_clip_);
+ while (current_clip_ != lca_clip) {
+#if DCHECK_IS_ON()
+ DCHECK(state_stack_.size() && state_stack_.back().type == StateEntry::kClip)
+ << "Error: Chunk has a clip that escaped its layer's or effect's clip."
+ << "\ntarget_clip:\n"
+ << target_clip->ToTreeString().Utf8().data() << "current_clip_:\n"
+ << current_clip_->ToTreeString().Utf8().data();
+#endif
+ if (!state_stack_.size() || state_stack_.back().type != StateEntry::kClip)
+ break;
+ current_clip_ = current_clip_->Parent();
+ StateEntry& previous_state = state_stack_.back();
+ if (current_clip_ == lca_clip) {
+ // |lca_clip| is an intermediate clip in a series of combined clips.
+ // Jump to the first of the combined clips.
+ current_clip_ = lca_clip = previous_state.clip;
+ }
+ if (current_clip_ == previous_state.clip)
+ EndClip();
+ }
+
+ if (target_clip == current_clip_)
+ return;
+
+ // Step 2: Collect all clips between the target clip and the current clip.
+ // At this point the current clip must be an ancestor of the target.
+ Vector<const ClipPaintPropertyNode*, 1u> pending_clips;
+ for (const ClipPaintPropertyNode* clip = target_clip; clip != current_clip_;
+ clip = clip->Parent()) {
+ // This should never happen unless the DCHECK in step 1 failed.
+ if (!clip)
+ break;
+ pending_clips.push_back(clip);
+ }
+
+ // Step 3: Now apply the list of clips in top-down order.
+ Optional<FloatRect> pending_combined_clip_rect;
+ const ClipPaintPropertyNode* lowest_combined_clip_node = nullptr;
+ for (size_t i = pending_clips.size(); i--;) {
+ const auto* sub_clip = pending_clips[i];
+ bool has_rounded_clip_or_clip_path =
+ sub_clip->ClipRect().IsRounded() || sub_clip->ClipPath();
+ if (!has_rounded_clip_or_clip_path && pending_combined_clip_rect &&
+ (sub_clip->Parent()->LocalTransformSpace() ==
+ sub_clip->LocalTransformSpace() ||
+ GeometryMapper::SourceToDestinationProjection(
+ sub_clip->Parent()->LocalTransformSpace(),
+ sub_clip->LocalTransformSpace())
+ .IsIdentity())) {
+ // Continue to combine rectangular clips in the same transform space.
+ pending_combined_clip_rect->Intersect(sub_clip->ClipRect().Rect());
+ lowest_combined_clip_node = sub_clip;
+ continue;
+ }
+
+ if (pending_combined_clip_rect) {
+ StartCombinedClip(*pending_combined_clip_rect, lowest_combined_clip_node);
+ lowest_combined_clip_node = nullptr;
+ pending_combined_clip_rect.reset();
+ }
+
+ if (has_rounded_clip_or_clip_path) {
+ // The clip can't be combined with others.
+ StartSingleClip(sub_clip);
+ } else {
+ // Start to combine clips.
+ pending_combined_clip_rect = sub_clip->ClipRect().Rect();
+ lowest_combined_clip_node = sub_clip;
+ }
+ }
+
+ if (pending_combined_clip_rect)
+ StartCombinedClip(*pending_combined_clip_rect, lowest_combined_clip_node);
+
+ DCHECK_EQ(current_clip_, target_clip);
+}
+
+void ConversionContext::StartCombinedClip(
+ const FloatRect& combined_clip_rect,
+ const ClipPaintPropertyNode* lowest_combined_clip_node) {
+ if (lowest_combined_clip_node->LocalTransformSpace() != current_transform_)
+ EndTransform();
+ cc_list_.StartPaint();
+ cc_list_.push<cc::SaveOp>();
+ ApplyTransform(lowest_combined_clip_node->LocalTransformSpace());
+ const bool antialias = true;
+ cc_list_.push<cc::ClipRectOp>(combined_clip_rect, SkClipOp::kIntersect,
+ antialias);
+ cc_list_.EndPaintOfPairedBegin();
+
+ PushState(StateEntry::kClip, 1);
+ current_clip_ = lowest_combined_clip_node;
+ current_transform_ = lowest_combined_clip_node->LocalTransformSpace();
+}
+
+void ConversionContext::StartSingleClip(const ClipPaintPropertyNode* clip) {
+ if (clip->LocalTransformSpace() != current_transform_)
+ EndTransform();
+ cc_list_.StartPaint();
+ cc_list_.push<cc::SaveOp>();
+ ApplyTransform(clip->LocalTransformSpace());
+ const bool antialias = true;
+ cc_list_.push<cc::ClipRectOp>(static_cast<SkRect>(clip->ClipRect().Rect()),
+ SkClipOp::kIntersect, antialias);
+ if (clip->ClipRect().IsRounded()) {
+ cc_list_.push<cc::ClipRRectOp>(static_cast<SkRRect>(clip->ClipRect()),
+ SkClipOp::kIntersect, antialias);
+ }
+ if (clip->ClipPath()) {
+ cc_list_.push<cc::ClipPathOp>(clip->ClipPath()->GetSkPath(),
+ SkClipOp::kIntersect, antialias);
+ }
+ cc_list_.EndPaintOfPairedBegin();
+
+ PushState(StateEntry::kClip, 1);
+ current_clip_ = clip;
+ current_transform_ = clip->LocalTransformSpace();
+}
+
+void ConversionContext::SwitchToEffect(
+ const EffectPaintPropertyNode* target_effect) {
+ if (target_effect == current_effect_)
+ return;
+
+ // Step 1: Exit all effects until the lowest common ancestor is found.
+ const EffectPaintPropertyNode* lca_effect =
+ &LowestCommonAncestor(*target_effect, *current_effect_);
+ while (current_effect_ != lca_effect) {
+ // This EndClips() and the later EndEffect() pop to the parent effect.
+ EndClips();
+#if DCHECK_IS_ON()
+ DCHECK(state_stack_.size())
+ << "Error: Chunk has an effect that escapes layer's effect.\n"
+ << "target_effect:\n"
+ << target_effect->ToTreeString().Utf8().data() << "current_effect_:\n"
+ << current_effect_->ToTreeString().Utf8().data();
+#endif
+ if (!state_stack_.size())
+ break;
+ EndEffect();
+ }
+
+ // Step 2: Collect all effects between the target effect and the current
+ // effect. At this point the current effect must be an ancestor of the target.
+ Vector<const EffectPaintPropertyNode*, 1u> pending_effects;
+ for (const EffectPaintPropertyNode* effect = target_effect;
+ effect != current_effect_; effect = effect->Parent()) {
+ // This should never happen unless the DCHECK in step 1 failed.
+ if (!effect)
+ break;
+ pending_effects.push_back(effect);
+ }
+
+ // Step 3: Now apply the list of effects in top-down order.
+ for (size_t i = pending_effects.size(); i--;) {
+ const EffectPaintPropertyNode* sub_effect = pending_effects[i];
+ DCHECK_EQ(current_effect_, sub_effect->Parent());
+ StartEffect(sub_effect);
+ }
+}
+
+void ConversionContext::StartEffect(const EffectPaintPropertyNode* effect) {
+ // Before each effect can be applied, we must enter its output clip first,
+ // or exit all clips if it doesn't have one.
+ if (effect->OutputClip())
+ SwitchToClip(effect->OutputClip());
+ else
+ EndClips();
+
+ // Apply effects.
+ int saved_count = 0;
+ size_t save_layer_id = kNotFound;
+ const auto* target_transform = current_transform_;
+
+ // We always create separate effect nodes for normal effects and filter
+ // effects, so we can handle them separately.
+ bool has_filter = !effect->Filter().IsEmpty();
+ bool has_opacity = effect->Opacity() != 1.f;
+ bool has_other_effects = effect->BlendMode() != SkBlendMode::kSrcOver ||
+ effect->GetColorFilter() != kColorFilterNone;
+ DCHECK(!has_filter || !(has_opacity || has_other_effects));
+
+ if (!has_filter) {
+ cc_list_.StartPaint();
+ // No need to adjust transform for non-filter effects because transform
+ // doesn't matter.
+ // TODO(ajuma): This should really be rounding instead of flooring the
+ // alpha value, but that breaks slimming paint reftests.
+ auto alpha =
+ static_cast<uint8_t>(gfx::ToFlooredInt(255 * effect->Opacity()));
+ if (has_other_effects) {
+ cc::PaintFlags flags;
+ flags.setBlendMode(effect->BlendMode());
+ flags.setAlpha(alpha);
+ flags.setColorFilter(GraphicsContext::WebCoreColorFilterToSkiaColorFilter(
+ effect->GetColorFilter()));
+ save_layer_id = cc_list_.push<cc::SaveLayerOp>(nullptr, &flags);
+ } else {
+ constexpr bool preserve_lcd_text_requests = false;
+ save_layer_id = cc_list_.push<cc::SaveLayerAlphaOp>(
+ nullptr, alpha, preserve_lcd_text_requests);
+ }
+ saved_count++;
+ cc_list_.EndPaintOfPairedBegin();
+ } else {
+ // Handle filter effect. Adjust transform first.
+ target_transform = effect->LocalTransformSpace();
+ FloatPoint filter_origin = effect->PaintOffset();
+ if (current_transform_ != target_transform ||
+ filter_origin != FloatPoint()) {
+ EndTransform();
+ auto matrix = GetSkMatrix(target_transform);
+ matrix.preTranslate(filter_origin.X(), filter_origin.Y());
+ cc_list_.StartPaint();
+ cc_list_.push<cc::SaveOp>();
+ cc_list_.push<cc::ConcatOp>(matrix);
+ saved_count++;
+ } else {
+ cc_list_.StartPaint();
+ }
+
+ // The size parameter is only used to computed the origin of zoom
+ // operation, which we never generate.
+ gfx::SizeF empty;
+ cc::PaintFlags filter_flags;
+ filter_flags.setImageFilter(cc::RenderSurfaceFilters::BuildImageFilter(
+ effect->Filter().AsCcFilterOperations(), empty));
+ save_layer_id = cc_list_.push<cc::SaveLayerOp>(nullptr, &filter_flags);
+
+ if (filter_origin != FloatPoint())
+ cc_list_.push<cc::TranslateOp>(-filter_origin.X(), -filter_origin.Y());
+ cc_list_.EndPaintOfPairedBegin();
+ saved_count++;
+ }
+
+ DCHECK_GT(saved_count, 0);
+ DCHECK_LE(saved_count, 2);
+ DCHECK_NE(save_layer_id, kNotFound);
+
+ // Adjust state and push previous state onto effect stack.
+ // TODO(trchen): Change input clip to expansion hint once implemented.
+ const ClipPaintPropertyNode* input_clip = current_clip_;
+ PushState(StateEntry::kEffect, saved_count);
+ effect_bounds_stack_.emplace_back(
+ EffectBoundsInfo{save_layer_id, target_transform});
+ current_transform_ = target_transform;
+ current_clip_ = input_clip;
+ current_effect_ = effect;
+}
+
+void ConversionContext::UpdateEffectBounds(
+ const FloatRect& bounds,
+ const TransformPaintPropertyNode* transform) {
+ if (effect_bounds_stack_.IsEmpty() || bounds.IsEmpty())
+ return;
+
+ auto& effect_bounds_info = effect_bounds_stack_.back();
+ FloatRect mapped_bounds = bounds;
+ GeometryMapper::SourceToDestinationRect(
+ transform, effect_bounds_info.transform, mapped_bounds);
+ effect_bounds_info.bounds.Unite(mapped_bounds);
+}
+
+void ConversionContext::EndEffect() {
+ const auto& previous_state = state_stack_.back();
+ DCHECK_EQ(previous_state.type, StateEntry::kEffect);
+ DCHECK_EQ(current_effect_->Parent(), previous_state.effect);
+ DCHECK_EQ(current_clip_, previous_state.clip);
+
+ DCHECK(effect_bounds_stack_.size());
+ const auto& bounds_info = effect_bounds_stack_.back();
+ FloatRect bounds = bounds_info.bounds;
+ if (!bounds.IsEmpty()) {
+ if (current_effect_->Filter().IsEmpty()) {
+ cc_list_.UpdateSaveLayerBounds(bounds_info.save_layer_id, bounds);
+ } else {
+ // The bounds for the SaveLayer[Alpha]Op should be the source bounds
+ // before the filter is applied, in the space of the TranslateOp which was
+ // emitted before the SaveLayer[Alpha]Op.
+ auto save_layer_bounds = bounds;
+ save_layer_bounds.MoveBy(-current_effect_->PaintOffset());
+ cc_list_.UpdateSaveLayerBounds(bounds_info.save_layer_id,
+ save_layer_bounds);
+ // We need to propagate the filtered bounds to the parent.
+ bounds = current_effect_->MapRect(bounds);
+ }
+ }
+
+ effect_bounds_stack_.pop_back();
+ EndTransform();
+ // Propagate the bounds to the parent effect.
+ UpdateEffectBounds(bounds, current_transform_);
+ PopState();
+}
+
+void ConversionContext::EndClips() {
+ while (state_stack_.size() && state_stack_.back().type == StateEntry::kClip)
+ EndClip();
+}
+
+void ConversionContext::EndClip() {
+ DCHECK_EQ(state_stack_.back().type, StateEntry::kClip);
+ DCHECK_EQ(state_stack_.back().effect, current_effect_);
+ EndTransform();
+ PopState();
+}
+
+void ConversionContext::PushState(StateEntry::PairedType type,
+ int saved_count) {
+ state_stack_.emplace_back(StateEntry{type, saved_count, current_transform_,
+ current_clip_, current_effect_,
+ previous_transform_});
+ previous_transform_ = nullptr;
+}
+
+void ConversionContext::PopState() {
+ DCHECK_EQ(nullptr, previous_transform_);
+
+ const auto& previous_state = state_stack_.back();
+ AppendRestore(previous_state.saved_count);
+ current_transform_ = previous_state.transform;
+ previous_transform_ = previous_state.previous_transform;
+ current_clip_ = previous_state.clip;
+ current_effect_ = previous_state.effect;
+ state_stack_.pop_back();
+}
+
+void ConversionContext::SwitchToTransform(
+ const TransformPaintPropertyNode* target_transform) {
+ if (target_transform == current_transform_)
+ return;
+
+ EndTransform();
+ if (target_transform == current_transform_)
+ return;
+
+ auto sk_matrix = GetSkMatrix(target_transform);
+ if (sk_matrix.isIdentity())
+ return;
+
+ cc_list_.StartPaint();
+ cc_list_.push<cc::SaveOp>();
+ cc_list_.push<cc::ConcatOp>(sk_matrix);
+ cc_list_.EndPaintOfPairedBegin();
+ previous_transform_ = current_transform_;
+ current_transform_ = target_transform;
+}
+
+void ConversionContext::EndTransform() {
+ if (!previous_transform_)
+ return;
+
+ cc_list_.StartPaint();
+ cc_list_.push<cc::RestoreOp>();
+ cc_list_.EndPaintOfPairedEnd();
+ current_transform_ = previous_transform_;
+ previous_transform_ = nullptr;
+}
+
+void ConversionContext::Convert(const PaintChunkSubset& paint_chunks,
+ const DisplayItemList& display_items) {
+ for (const auto& chunk : paint_chunks) {
+ const auto& chunk_state = chunk.properties.property_tree_state;
+ bool switched_to_chunk_state = false;
+
+ for (const auto& item : display_items.ItemsInPaintChunk(chunk)) {
+ DCHECK(item.IsDrawing());
+ auto record =
+ static_cast<const DrawingDisplayItem&>(item).GetPaintRecord();
+ // If we have an empty paint record, then we would prefer not to draw it.
+ // However, if we also have a non-root effect, it means that the filter
+ // applied might draw something even if the record is empty. We need to
+ // "draw" this record in order to ensure that the effect has correct
+ // visual rects.
+ if ((!record || record->size() == 0) &&
+ chunk_state.Effect() == EffectPaintPropertyNode::Root()) {
+ continue;
+ }
+
+ TranslateForLayerOffsetOnce();
+ if (!switched_to_chunk_state) {
+ SwitchToChunkState(chunk);
+ switched_to_chunk_state = true;
+ }
+
+ cc_list_.StartPaint();
+ if (record && record->size() != 0)
+ cc_list_.push<cc::DrawRecordOp>(std::move(record));
+ cc_list_.EndPaintOfUnpaired(
+ chunk_to_layer_mapper_.MapVisualRect(item.VisualRect()));
+ }
+ UpdateEffectBounds(chunk.bounds, chunk_state.Transform());
+ }
+}
+
+} // unnamed namespace
+
+void PaintChunksToCcLayer::ConvertInto(
+ const PaintChunkSubset& paint_chunks,
+ const PropertyTreeState& layer_state,
+ const gfx::Vector2dF& layer_offset,
+ const FloatSize& visual_rect_subpixel_offset,
+ const DisplayItemList& display_items,
+ cc::DisplayItemList& cc_list) {
+ if (RuntimeEnabledFeatures::DisablePaintChunksToCcLayerEnabled())
+ return;
+ ConversionContext(layer_state, layer_offset, visual_rect_subpixel_offset,
+ cc_list)
+ .Convert(paint_chunks, display_items);
+}
+
+scoped_refptr<cc::DisplayItemList> PaintChunksToCcLayer::Convert(
+ const PaintChunkSubset& paint_chunks,
+ const PropertyTreeState& layer_state,
+ const gfx::Vector2dF& layer_offset,
+ const DisplayItemList& display_items,
+ cc::DisplayItemList::UsageHint hint,
+ RasterUnderInvalidationCheckingParams* under_invalidation_checking_params) {
+ auto cc_list = base::MakeRefCounted<cc::DisplayItemList>(hint);
+ ConvertInto(paint_chunks, layer_state, layer_offset, FloatSize(),
+ display_items, *cc_list);
+
+ if (under_invalidation_checking_params) {
+ auto& params = *under_invalidation_checking_params;
+ PaintRecorder recorder;
+ recorder.beginRecording(params.interest_rect);
+ // Create a complete cloned list for under-invalidation checking. We can't
+ // use cc_list because it is not finalized yet.
+ auto list_clone = base::MakeRefCounted<cc::DisplayItemList>(
+ cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer);
+ ConvertInto(paint_chunks, layer_state, layer_offset, FloatSize(),
+ display_items, *list_clone);
+ recorder.getRecordingCanvas()->drawPicture(list_clone->ReleaseAsRecord());
+ params.tracking.CheckUnderInvalidations(params.debug_name,
+ recorder.finishRecordingAsPicture(),
+ params.interest_rect);
+ if (auto record = params.tracking.UnderInvalidationRecord()) {
+ cc_list->StartPaint();
+ cc_list->push<cc::DrawRecordOp>(std::move(record));
+ cc_list->EndPaintOfUnpaired(params.interest_rect);
+ }
+ }
+
+ cc_list->Finalize();
+ return cc_list;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.h b/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.h
new file mode 100644
index 00000000000..654977d86de
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.h
@@ -0,0 +1,78 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITING_PAINT_CHUNKS_TO_CC_LAYER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITING_PAINT_CHUNKS_TO_CC_LAYER_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "cc/paint/display_item_list.h"
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace cc {
+class DisplayItemList;
+} // namespace cc
+
+namespace gfx {
+class Vector2dF;
+} // namespace gfx
+
+namespace blink {
+
+class DisplayItemList;
+class PaintChunkSubset;
+class FloatSize;
+class PropertyTreeState;
+class RasterInvalidationTracking;
+
+struct RasterUnderInvalidationCheckingParams {
+ RasterUnderInvalidationCheckingParams(RasterInvalidationTracking& tracking,
+ const IntRect& interest_rect,
+ const String& debug_name)
+ : tracking(tracking),
+ interest_rect(interest_rect),
+ debug_name(debug_name) {}
+
+ RasterInvalidationTracking& tracking;
+ IntRect interest_rect;
+ String debug_name;
+};
+
+class PLATFORM_EXPORT PaintChunksToCcLayer {
+ public:
+ // Converts a list of Blink paint chunks and display items into cc display
+ // items, inserting appropriate begin/end items with respect to property
+ // tree state. The converted items are appended into a unfinalized cc display
+ // item list.
+ // |layer_state| is the target property tree state of the output. This method
+ // generates begin/end items for the relative state differences between the
+ // layer state and the chunk state.
+ // |layer_offset| is an extra translation on top of layer_state.Transform(),
+ // in other word, point (x, y) in the output list maps to
+ // layer_state.Transform() * (layer_offset + (x, y)) on the screen. It is
+ // equivalent to say that |layer_offset| is the layer origin in the space
+ // of layer_state.Transform().
+ static void ConvertInto(const PaintChunkSubset&,
+ const PropertyTreeState& layer_state,
+ const gfx::Vector2dF& layer_offset,
+ const FloatSize& visual_rect_subpixel_offset,
+ const DisplayItemList&,
+ cc::DisplayItemList&);
+
+ // Similar to ConvertInto(), but returns a finalized new list instead of
+ // appending converted items to an existing list.
+ static scoped_refptr<cc::DisplayItemList> Convert(
+ const PaintChunkSubset&,
+ const PropertyTreeState& layer_state,
+ const gfx::Vector2dF& layer_offset,
+ const DisplayItemList&,
+ cc::DisplayItemList::UsageHint,
+ RasterUnderInvalidationCheckingParams* = nullptr);
+};
+
+} // namespace blink
+
+#endif // PaintArtifactCompositor_h
diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer_test.cc b/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer_test.cc
new file mode 100644
index 00000000000..75f6a0b42de
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer_test.cc
@@ -0,0 +1,890 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.h"
+
+#include <initializer_list>
+
+#include "cc/paint/display_item_list.h"
+#include "cc/paint/paint_op_buffer.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/graphics/logging_canvas.h"
+#include "third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.h"
+#include "third_party/blink/renderer/platform/graphics/paint/display_item_list.h"
+#include "third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h"
+#include "third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_chunk.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_chunk_subset.h"
+#include "third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h"
+#include "third_party/blink/renderer/platform/testing/fake_display_item_client.h"
+#include "third_party/blink/renderer/platform/testing/paint_property_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+
+namespace cc {
+std::ostream& operator<<(std::ostream& os, const PaintRecord& record) {
+ return os << "PaintRecord("
+ << blink::RecordAsDebugString(record).Utf8().data() << ")";
+}
+} // namespace cc
+
+std::ostream& operator<<(std::ostream& os, const SkRect& rect) {
+ if (cc::PaintOp::IsUnsetRect(rect))
+ return os << "(unset)";
+ return os << blink::FloatRect(rect).ToString();
+}
+
+namespace blink {
+namespace {
+
+using test::CreateOpacityOnlyEffect;
+
+class PaintChunksToCcLayerTest : public testing::Test,
+ private ScopedSlimmingPaintV2ForTest {
+ protected:
+ PaintChunksToCcLayerTest() : ScopedSlimmingPaintV2ForTest(true) {}
+};
+
+// Matches PaintOpTypes in a PaintRecord.
+class PaintRecordMatcher
+ : public testing::MatcherInterface<const cc::PaintOpBuffer&> {
+ public:
+ static testing::Matcher<const cc::PaintOpBuffer&> Make(
+ std::initializer_list<cc::PaintOpType> args) {
+ return testing::MakeMatcher(new PaintRecordMatcher(args));
+ }
+ PaintRecordMatcher(std::initializer_list<cc::PaintOpType> args)
+ : expected_ops_(args) {}
+
+ bool MatchAndExplain(const cc::PaintOpBuffer& buffer,
+ testing::MatchResultListener* listener) const override {
+ size_t index = 0;
+ for (cc::PaintOpBuffer::Iterator it(&buffer); it; ++it, ++index) {
+ auto op = (*it)->GetType();
+ if (index < expected_ops_.size() && expected_ops_[index] == op)
+ continue;
+
+ if (listener->IsInterested()) {
+ *listener << "unexpected op " << op << " at index " << index << ",";
+ if (index < expected_ops_.size())
+ *listener << " expecting " << expected_ops_[index] << ".";
+ else
+ *listener << " expecting end of list.";
+ }
+ return false;
+ }
+ if (index == expected_ops_.size())
+ return true;
+ if (listener->IsInterested()) {
+ *listener << "unexpected end of list, expecting " << expected_ops_[index]
+ << " at index " << index << ".";
+ }
+ return false;
+ }
+
+ void DescribeTo(::std::ostream* os) const override {
+ *os << "[";
+ bool first = true;
+ for (auto op : expected_ops_) {
+ if (first)
+ first = false;
+ else
+ *os << ", ";
+ *os << op;
+ }
+ *os << "]";
+ }
+
+ private:
+ Vector<cc::PaintOpType> expected_ops_;
+};
+
+#define EXPECT_EFFECT_BOUNDS(x, y, width, height, op_buffer, index) \
+ do { \
+ FloatRect bounds; \
+ if (const auto* save_layer_alpha = \
+ (op_buffer).GetOpAtForTesting<cc::SaveLayerAlphaOp>(index)) { \
+ bounds = save_layer_alpha->bounds; \
+ } else if (const auto* save_layer = \
+ (op_buffer).GetOpAtForTesting<cc::SaveLayerOp>(index)) { \
+ bounds = save_layer->bounds; \
+ } else { \
+ FAIL() << "No SaveLayer[Alpha]Op at " << index; \
+ } \
+ EXPECT_EQ(FloatRect(x, y, width, height), bounds); \
+ } while (false)
+
+#define EXPECT_TRANSFORM_MATRIX(transform, op_buffer, index) \
+ do { \
+ const auto* concat = (op_buffer).GetOpAtForTesting<cc::ConcatOp>(index); \
+ ASSERT_NE(nullptr, concat); \
+ EXPECT_EQ(transform, TransformationMatrix(concat->matrix)); \
+ } while (false)
+
+#define EXPECT_TRANSLATE(x, y, op_buffer, index) \
+ do { \
+ const auto* translate = \
+ (op_buffer).GetOpAtForTesting<cc::TranslateOp>(index); \
+ ASSERT_NE(nullptr, translate); \
+ EXPECT_EQ(x, translate->dx); \
+ EXPECT_EQ(y, translate->dy); \
+ } while (false)
+
+// Convenient shorthands.
+const TransformPaintPropertyNode* t0() {
+ return TransformPaintPropertyNode::Root();
+}
+const ClipPaintPropertyNode* c0() {
+ return ClipPaintPropertyNode::Root();
+}
+const EffectPaintPropertyNode* e0() {
+ return EffectPaintPropertyNode::Root();
+}
+
+PaintChunk::Id DefaultId() {
+ DEFINE_STATIC_LOCAL(FakeDisplayItemClient, fake_client,
+ ("FakeDisplayItemClient", LayoutRect(0, 0, 100, 100)));
+ return PaintChunk::Id(fake_client, DisplayItem::kDrawingFirst);
+}
+
+struct TestChunks {
+ Vector<PaintChunk> chunks;
+ DisplayItemList items = DisplayItemList(0);
+
+ // Add a paint chunk with a non-empty paint record and given property nodes.
+ void AddChunk(const TransformPaintPropertyNode* t,
+ const ClipPaintPropertyNode* c,
+ const EffectPaintPropertyNode* e,
+ const FloatRect& bounds = FloatRect(0, 0, 100, 100)) {
+ auto record = sk_make_sp<PaintRecord>();
+ record->push<cc::DrawRectOp>(bounds, cc::PaintFlags());
+ AddChunk(std::move(record), t, c, e, bounds);
+ }
+
+ // Add a paint chunk with a given paint record and property nodes.
+ void AddChunk(sk_sp<PaintRecord> record,
+ const TransformPaintPropertyNode* t,
+ const ClipPaintPropertyNode* c,
+ const EffectPaintPropertyNode* e,
+ const FloatRect& bounds = FloatRect(0, 0, 100, 100)) {
+ size_t i = items.size();
+ items.AllocateAndConstruct<DrawingDisplayItem>(
+ DefaultId().client, DefaultId().type, std::move(record));
+ chunks.emplace_back(i, i + 1, DefaultId(),
+ PaintChunkProperties(PropertyTreeState(t, c, e)));
+ chunks.back().bounds = bounds;
+ }
+};
+
+TEST_F(PaintChunksToCcLayerTest, EffectGroupingSimple) {
+ // This test verifies effects are applied as a group.
+ scoped_refptr<EffectPaintPropertyNode> e1 =
+ CreateOpacityOnlyEffect(e0(), 0.5f);
+ TestChunks chunks;
+ chunks.AddChunk(t0(), c0(), e1.get(), FloatRect(0, 0, 50, 50));
+ chunks.AddChunk(t0(), c0(), e1.get(), FloatRect(20, 20, 70, 70));
+
+ sk_sp<PaintRecord> output =
+ PaintChunksToCcLayer::Convert(
+ chunks.chunks, PropertyTreeState(t0(), c0(), e0()), gfx::Vector2dF(),
+ chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
+ ->ReleaseAsRecord();
+ EXPECT_THAT(
+ *output,
+ PaintRecordMatcher::Make({cc::PaintOpType::SaveLayerAlpha, // <e1>
+ cc::PaintOpType::DrawRecord, // <p0/>
+ cc::PaintOpType::DrawRecord, // <p1/>
+ cc::PaintOpType::Restore})); // </e1>
+ EXPECT_EFFECT_BOUNDS(0, 0, 90, 90, *output, 0);
+}
+
+TEST_F(PaintChunksToCcLayerTest, EffectGroupingNested) {
+ // This test verifies nested effects are grouped properly.
+ scoped_refptr<EffectPaintPropertyNode> e1 =
+ CreateOpacityOnlyEffect(e0(), 0.5f);
+ scoped_refptr<EffectPaintPropertyNode> e2 =
+ CreateOpacityOnlyEffect(e1.get(), 0.5f);
+ scoped_refptr<EffectPaintPropertyNode> e3 =
+ CreateOpacityOnlyEffect(e1.get(), 0.5f);
+ TestChunks chunks;
+ chunks.AddChunk(t0(), c0(), e2.get());
+ chunks.AddChunk(t0(), c0(), e3.get(), FloatRect(111, 222, 333, 444));
+
+ sk_sp<PaintRecord> output =
+ PaintChunksToCcLayer::Convert(
+ chunks.chunks, PropertyTreeState(t0(), c0(), e0()), gfx::Vector2dF(),
+ chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
+ ->ReleaseAsRecord();
+ EXPECT_THAT(
+ *output,
+ PaintRecordMatcher::Make({cc::PaintOpType::SaveLayerAlpha, // <e1>
+ cc::PaintOpType::SaveLayerAlpha, // <e2>
+ cc::PaintOpType::DrawRecord, // <p0/>
+ cc::PaintOpType::Restore, // </e2>
+ cc::PaintOpType::SaveLayerAlpha, // <e3>
+ cc::PaintOpType::DrawRecord, // <p1/>
+ cc::PaintOpType::Restore, // </e3>
+ cc::PaintOpType::Restore})); // </e1>
+ EXPECT_EFFECT_BOUNDS(0, 0, 444, 666, *output, 0);
+ EXPECT_EFFECT_BOUNDS(0, 0, 100, 100, *output, 1);
+ EXPECT_EFFECT_BOUNDS(111, 222, 333, 444, *output, 4);
+}
+
+TEST_F(PaintChunksToCcLayerTest, EffectFilterGroupingNestedWithTransforms) {
+ // This test verifies nested effects with transforms are grouped properly.
+ auto t1 = TransformPaintPropertyNode::Create(
+ t0(), TransformationMatrix().Scale(2.f), FloatPoint3D());
+ auto t2 = TransformPaintPropertyNode::Create(
+ t1.get(), TransformationMatrix().Translate(-50, -50), FloatPoint3D());
+ auto e1 = EffectPaintPropertyNode::Create(e0(), t2.get(), c0(), ColorFilter(),
+ CompositorFilterOperations(), .5f,
+ SkBlendMode::kSrcOver);
+ CompositorFilterOperations filter;
+ filter.AppendBlurFilter(5);
+ auto e2 = EffectPaintPropertyNode::Create(
+ e1.get(), t2.get(), c0(), ColorFilter(), filter, 1.f,
+ SkBlendMode::kSrcOver, CompositingReason::kNone, CompositorElementId(),
+ FloatPoint(60, 60));
+ CreateOpacityOnlyEffect(e1.get(), 0.5f);
+ TestChunks chunks;
+ chunks.AddChunk(t2.get(), c0(), e1.get(), FloatRect(0, 0, 50, 50));
+ chunks.AddChunk(t1.get(), c0(), e2.get(), FloatRect(20, 20, 70, 70));
+
+ sk_sp<PaintRecord> output =
+ PaintChunksToCcLayer::Convert(
+ chunks.chunks, PropertyTreeState(t0(), c0(), e0()), gfx::Vector2dF(),
+ chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
+ ->ReleaseAsRecord();
+ EXPECT_THAT(
+ *output,
+ PaintRecordMatcher::Make(
+ {cc::PaintOpType::SaveLayerAlpha, // <e1>
+ cc::PaintOpType::Save, cc::PaintOpType::Concat, // <t1*t2>
+ cc::PaintOpType::DrawRecord, // <p1/>
+ cc::PaintOpType::Restore, // </t1*t2>
+ cc::PaintOpType::Save, cc::PaintOpType::Concat, // <t1*t2+e2_offset>
+ cc::PaintOpType::SaveLayer, // <e2>
+ cc::PaintOpType::Translate, // </e2_offset>
+ cc::PaintOpType::Save, cc::PaintOpType::Concat, // <t2^-1>
+ cc::PaintOpType::DrawRecord, // <p2/>
+ cc::PaintOpType::Restore, // </t2^-1>
+ cc::PaintOpType::Restore, // </e2>
+ cc::PaintOpType::Restore, // </t1*t2>
+ cc::PaintOpType::Restore})); // </e1>
+ // t1(t2(chunk1.bounds + e2(t2^-1(chunk2.bounds))))
+ EXPECT_EFFECT_BOUNDS(-100, -100, 310, 310, *output, 0);
+ EXPECT_TRANSFORM_MATRIX(t1->Matrix() * t2->Matrix(), *output, 2);
+ EXPECT_TRANSFORM_MATRIX((t1->Matrix() * t2->Matrix()).Translate(60, 60),
+ *output, 6);
+ // t2^-1(chunk2.bounds) - e2_offset
+ EXPECT_EFFECT_BOUNDS(10, 10, 70, 70, *output, 7);
+ EXPECT_TRANSLATE(-60, -60, *output, 8);
+ EXPECT_TRANSFORM_MATRIX(t2->Matrix().Inverse(), *output, 10);
+}
+
+TEST_F(PaintChunksToCcLayerTest, InterleavedClipEffect) {
+ // This test verifies effects are enclosed by their output clips.
+ // It is the same as the example made in the class comments of
+ // ConversionContext.
+ // Refer to PaintChunksToCcLayer.cpp for detailed explanation.
+ // (Search "State management example".)
+ scoped_refptr<ClipPaintPropertyNode> c1 = ClipPaintPropertyNode::Create(
+ c0(), t0(), FloatRoundedRect(0.f, 0.f, 1.f, 1.f));
+ scoped_refptr<ClipPaintPropertyNode> c2 = ClipPaintPropertyNode::Create(
+ c1.get(), t0(), FloatRoundedRect(0.f, 0.f, 1.f, 1.f));
+ scoped_refptr<ClipPaintPropertyNode> c3 = ClipPaintPropertyNode::Create(
+ c2.get(), t0(), FloatRoundedRect(0.f, 0.f, 1.f, 1.f));
+ scoped_refptr<ClipPaintPropertyNode> c4 = ClipPaintPropertyNode::Create(
+ c3.get(), t0(), FloatRoundedRect(0.f, 0.f, 1.f, 1.f));
+ scoped_refptr<EffectPaintPropertyNode> e1 = EffectPaintPropertyNode::Create(
+ e0(), t0(), c2.get(), ColorFilter(), CompositorFilterOperations(), 0.5f,
+ SkBlendMode::kSrcOver);
+ scoped_refptr<EffectPaintPropertyNode> e2 = EffectPaintPropertyNode::Create(
+ e1.get(), t0(), c4.get(), ColorFilter(), CompositorFilterOperations(),
+ 0.5f, SkBlendMode::kSrcOver);
+ TestChunks chunks;
+ chunks.AddChunk(t0(), c2.get(), e0());
+ chunks.AddChunk(t0(), c3.get(), e0());
+ chunks.AddChunk(t0(), c4.get(), e2.get(), FloatRect(0, 0, 50, 50));
+ chunks.AddChunk(t0(), c3.get(), e1.get(), FloatRect(20, 20, 70, 70));
+ chunks.AddChunk(t0(), c4.get(), e0());
+
+ sk_sp<PaintRecord> output =
+ PaintChunksToCcLayer::Convert(
+ chunks.chunks, PropertyTreeState(t0(), c0(), e0()), gfx::Vector2dF(),
+ chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
+ ->ReleaseAsRecord();
+ EXPECT_THAT(*output, PaintRecordMatcher::Make(
+ {cc::PaintOpType::Save,
+ cc::PaintOpType::ClipRect, // <c1+c2>
+ cc::PaintOpType::DrawRecord, // <p0/>
+ cc::PaintOpType::Save,
+ cc::PaintOpType::ClipRect, // <c3>
+ cc::PaintOpType::DrawRecord, // <p1/>
+ cc::PaintOpType::Restore, // </c3>
+ cc::PaintOpType::SaveLayerAlpha, // <e1>
+ cc::PaintOpType::Save,
+ cc::PaintOpType::ClipRect, // <c3+c4>
+ cc::PaintOpType::SaveLayerAlpha, // <e2>
+ cc::PaintOpType::DrawRecord, // <p2/>
+ cc::PaintOpType::Restore, // </e2>
+ cc::PaintOpType::Restore, // </c3+c4>
+ cc::PaintOpType::Save,
+ cc::PaintOpType::ClipRect, // <c3>
+ cc::PaintOpType::DrawRecord, // <p3/>
+ cc::PaintOpType::Restore, // </c3>
+ cc::PaintOpType::Restore, // </e1>
+ cc::PaintOpType::Save,
+ cc::PaintOpType::ClipRect, // <c3+c4>
+ cc::PaintOpType::DrawRecord, // <p4/>
+ cc::PaintOpType::Restore, // </c3+c4>
+ cc::PaintOpType::Restore})); // </c1+c2>
+ EXPECT_EFFECT_BOUNDS(0, 0, 90, 90, *output, 7);
+ EXPECT_EFFECT_BOUNDS(0, 0, 50, 50, *output, 10);
+}
+
+TEST_F(PaintChunksToCcLayerTest, ClipSpaceInversion) {
+ // This test verifies chunks that have a shallower transform state than
+ // its clip can still be painted. The infamous CSS corner case:
+ // <div style="position:absolute; clip:rect(...)">
+ // <div style="position:fixed;">Clipped but not scroll along.</div>
+ // </div>
+ scoped_refptr<TransformPaintPropertyNode> t1 =
+ TransformPaintPropertyNode::Create(
+ t0(), TransformationMatrix().Scale(2.f), FloatPoint3D());
+ scoped_refptr<ClipPaintPropertyNode> c1 = ClipPaintPropertyNode::Create(
+ c0(), t1.get(), FloatRoundedRect(0.f, 0.f, 1.f, 1.f));
+ TestChunks chunks;
+ chunks.AddChunk(t0(), c1.get(), e0());
+
+ sk_sp<PaintRecord> output =
+ PaintChunksToCcLayer::Convert(
+ chunks.chunks, PropertyTreeState(t0(), c0(), e0()), gfx::Vector2dF(),
+ chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
+ ->ReleaseAsRecord();
+ EXPECT_THAT(*output,
+ PaintRecordMatcher::Make(
+ {cc::PaintOpType::Save, cc::PaintOpType::Concat, // <t1
+ cc::PaintOpType::ClipRect, // c1>
+ cc::PaintOpType::Save, cc::PaintOpType::Concat, // <t1^-1>
+ cc::PaintOpType::DrawRecord, // <p0/>
+ cc::PaintOpType::Restore, // </t1^-1>
+ cc::PaintOpType::Restore})); // </c1 t1>
+}
+
+TEST_F(PaintChunksToCcLayerTest, OpacityEffectSpaceInversion) {
+ // This test verifies chunks that have a shallower transform state than
+ // its effect can still be painted. The infamous CSS corner case:
+ // <div style="overflow:scroll">
+ // <div style="opacity:0.5">
+ // <div style="position:absolute;">Transparent but not scroll along.</div>
+ // </div>
+ // </div>
+ scoped_refptr<TransformPaintPropertyNode> t1 =
+ TransformPaintPropertyNode::Create(
+ t0(), TransformationMatrix().Scale(2.f), FloatPoint3D());
+ scoped_refptr<EffectPaintPropertyNode> e1 = EffectPaintPropertyNode::Create(
+ e0(), t1.get(), c0(), ColorFilter(), CompositorFilterOperations(), 0.5f,
+ SkBlendMode::kSrcOver);
+ TestChunks chunks;
+ chunks.AddChunk(t0(), c0(), e1.get());
+ chunks.AddChunk(t1.get(), c0(), e1.get());
+
+ sk_sp<PaintRecord> output =
+ PaintChunksToCcLayer::Convert(
+ chunks.chunks, PropertyTreeState(t0(), c0(), e0()), gfx::Vector2dF(),
+ chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
+ ->ReleaseAsRecord();
+ EXPECT_THAT(
+ *output,
+ PaintRecordMatcher::Make({cc::PaintOpType::SaveLayerAlpha, // <e1>
+ cc::PaintOpType::DrawRecord, // <p0/>
+ cc::PaintOpType::Save,
+ cc::PaintOpType::Concat, // <t1>
+ cc::PaintOpType::DrawRecord, // <p1/>
+ cc::PaintOpType::Restore, // </t1>
+ cc::PaintOpType::Restore})); // </e1>
+ EXPECT_EFFECT_BOUNDS(0, 0, 200, 200, *output, 0);
+ EXPECT_TRANSFORM_MATRIX(t1->Matrix(), *output, 3);
+}
+
+TEST_F(PaintChunksToCcLayerTest, FilterEffectSpaceInversion) {
+ // This test verifies chunks that have a shallower transform state than
+ // its effect can still be painted. The infamous CSS corner case:
+ // <div style="overflow:scroll">
+ // <div style="filter:blur(1px)">
+ // <div style="position:absolute;">Filtered but not scroll along.</div>
+ // </div>
+ // </div>
+ auto t1 = TransformPaintPropertyNode::Create(
+ t0(), TransformationMatrix().Scale(2.f), FloatPoint3D());
+ CompositorFilterOperations filter;
+ filter.AppendBlurFilter(5);
+ auto e1 = EffectPaintPropertyNode::Create(
+ e0(), t1.get(), c0(), ColorFilter(), filter, 1.f, SkBlendMode::kSrcOver,
+ CompositingReason::kNone, CompositorElementId(), FloatPoint(66, 88));
+ TestChunks chunks;
+ chunks.AddChunk(t0(), c0(), e1.get());
+
+ auto output =
+ PaintChunksToCcLayer::Convert(
+ chunks.chunks, PropertyTreeState(t0(), c0(), e0()), gfx::Vector2dF(),
+ chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
+ ->ReleaseAsRecord();
+ EXPECT_THAT(
+ *output,
+ PaintRecordMatcher::Make(
+ {cc::PaintOpType::Save, cc::PaintOpType::Concat, // <t1*e1_offset>
+ cc::PaintOpType::SaveLayer, // <e1>
+ cc::PaintOpType::Translate, // </e1_offset>
+ cc::PaintOpType::Save, cc::PaintOpType::Concat, // <t1^-1>
+ cc::PaintOpType::DrawRecord, // <p0/>
+ cc::PaintOpType::Restore, // </t1^-1>
+ cc::PaintOpType::Restore, // </e1>
+ cc::PaintOpType::Restore})); // </t1>
+ EXPECT_TRANSFORM_MATRIX(TransformationMatrix(t1->Matrix()).Translate(66, 88),
+ *output, 1);
+ EXPECT_EFFECT_BOUNDS(-66, -88, 50, 50, *output, 2);
+ EXPECT_TRANSLATE(-66, -88, *output, 3);
+ EXPECT_TRANSFORM_MATRIX(t1->Matrix().Inverse(), *output, 5);
+}
+
+TEST_F(PaintChunksToCcLayerTest, NonRootLayerSimple) {
+ // This test verifies a layer with composited property state does not
+ // apply properties again internally.
+ scoped_refptr<TransformPaintPropertyNode> t1 =
+ TransformPaintPropertyNode::Create(
+ t0(), TransformationMatrix().Scale(2.f), FloatPoint3D());
+ scoped_refptr<ClipPaintPropertyNode> c1 = ClipPaintPropertyNode::Create(
+ c0(), t0(), FloatRoundedRect(0.f, 0.f, 1.f, 1.f));
+ scoped_refptr<EffectPaintPropertyNode> e1 =
+ CreateOpacityOnlyEffect(e0(), 0.5f);
+ TestChunks chunks;
+ chunks.AddChunk(t1.get(), c1.get(), e1.get());
+
+ sk_sp<PaintRecord> output =
+ PaintChunksToCcLayer::Convert(
+ chunks.chunks, PropertyTreeState(t1.get(), c1.get(), e1.get()),
+ gfx::Vector2dF(), chunks.items,
+ cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
+ ->ReleaseAsRecord();
+ EXPECT_THAT(*output, PaintRecordMatcher::Make({cc::PaintOpType::DrawRecord}));
+}
+
+TEST_F(PaintChunksToCcLayerTest, NonRootLayerTransformEscape) {
+ // This test verifies chunks that have a shallower transform state than the
+ // layer can still be painted.
+ scoped_refptr<TransformPaintPropertyNode> t1 =
+ TransformPaintPropertyNode::Create(
+ t0(), TransformationMatrix().Scale(2.f), FloatPoint3D());
+ scoped_refptr<ClipPaintPropertyNode> c1 = ClipPaintPropertyNode::Create(
+ c0(), t0(), FloatRoundedRect(0.f, 0.f, 1.f, 1.f));
+ scoped_refptr<EffectPaintPropertyNode> e1 =
+ CreateOpacityOnlyEffect(e0(), 0.5f);
+ TestChunks chunks;
+ chunks.AddChunk(t0(), c1.get(), e1.get());
+
+ sk_sp<PaintRecord> output =
+ PaintChunksToCcLayer::Convert(
+ chunks.chunks, PropertyTreeState(t1.get(), c1.get(), e1.get()),
+ gfx::Vector2dF(), chunks.items,
+ cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
+ ->ReleaseAsRecord();
+ EXPECT_THAT(*output,
+ PaintRecordMatcher::Make(
+ {cc::PaintOpType::Save, cc::PaintOpType::Concat, // <t1^-1>
+ cc::PaintOpType::DrawRecord, // <p0/>
+ cc::PaintOpType::Restore})); // </t1^-1>
+}
+
+TEST_F(PaintChunksToCcLayerTest, EffectWithNoOutputClip) {
+ // This test verifies effect with no output clip can be correctly processed.
+ scoped_refptr<ClipPaintPropertyNode> c1 = ClipPaintPropertyNode::Create(
+ c0(), t0(), FloatRoundedRect(0.f, 0.f, 1.f, 1.f));
+ scoped_refptr<ClipPaintPropertyNode> c2 = ClipPaintPropertyNode::Create(
+ c1.get(), t0(), FloatRoundedRect(0.f, 0.f, 1.f, 1.f));
+ scoped_refptr<EffectPaintPropertyNode> e1 = EffectPaintPropertyNode::Create(
+ e0(), t0(), nullptr, kColorFilterNone, CompositorFilterOperations(), 0.5,
+ SkBlendMode::kSrcOver);
+
+ TestChunks chunks;
+ chunks.AddChunk(t0(), c2.get(), e1.get());
+
+ sk_sp<PaintRecord> output =
+ PaintChunksToCcLayer::Convert(
+ chunks.chunks, PropertyTreeState(t0(), c1.get(), e0()),
+ gfx::Vector2dF(), chunks.items,
+ cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
+ ->ReleaseAsRecord();
+ EXPECT_THAT(
+ *output,
+ PaintRecordMatcher::Make({cc::PaintOpType::SaveLayerAlpha, // <e1>
+ cc::PaintOpType::Save,
+ cc::PaintOpType::ClipRect, // <c2>
+ cc::PaintOpType::DrawRecord, // <p0/>
+ cc::PaintOpType::Restore, // </c2>
+ cc::PaintOpType::Restore})); // </e1>
+ EXPECT_EFFECT_BOUNDS(0, 0, 100, 100, *output, 0);
+}
+
+TEST_F(PaintChunksToCcLayerTest,
+ EffectWithNoOutputClipNestedInDecompositedEffect) {
+ scoped_refptr<ClipPaintPropertyNode> c1 = ClipPaintPropertyNode::Create(
+ c0(), t0(), FloatRoundedRect(0.f, 0.f, 1.f, 1.f));
+ scoped_refptr<EffectPaintPropertyNode> e1 = EffectPaintPropertyNode::Create(
+ e0(), t0(), c0(), kColorFilterNone, CompositorFilterOperations(), 0.5,
+ SkBlendMode::kSrcOver);
+ scoped_refptr<EffectPaintPropertyNode> e2 = EffectPaintPropertyNode::Create(
+ e1.get(), t0(), nullptr, kColorFilterNone, CompositorFilterOperations(),
+ 0.5, SkBlendMode::kSrcOver);
+
+ TestChunks chunks;
+ chunks.AddChunk(t0(), c1.get(), e2.get());
+
+ sk_sp<PaintRecord> output =
+ PaintChunksToCcLayer::Convert(
+ chunks.chunks, PropertyTreeState(t0(), c0(), e0()), gfx::Vector2dF(),
+ chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
+ ->ReleaseAsRecord();
+ EXPECT_THAT(
+ *output,
+ PaintRecordMatcher::Make({cc::PaintOpType::SaveLayerAlpha, // <e1>
+ cc::PaintOpType::SaveLayerAlpha, // <e2>
+ cc::PaintOpType::Save,
+ cc::PaintOpType::ClipRect, // <c1>
+ cc::PaintOpType::DrawRecord, // <p0/>
+ cc::PaintOpType::Restore, // </c1>
+ cc::PaintOpType::Restore, // </e2>
+ cc::PaintOpType::Restore})); // </e1>
+ EXPECT_EFFECT_BOUNDS(0, 0, 100, 100, *output, 0);
+ EXPECT_EFFECT_BOUNDS(0, 0, 100, 100, *output, 1);
+}
+
+TEST_F(PaintChunksToCcLayerTest,
+ EffectWithNoOutputClipNestedInCompositedEffect) {
+ scoped_refptr<ClipPaintPropertyNode> c1 = ClipPaintPropertyNode::Create(
+ c0(), t0(), FloatRoundedRect(0.f, 0.f, 1.f, 1.f));
+ scoped_refptr<EffectPaintPropertyNode> e1 = EffectPaintPropertyNode::Create(
+ e0(), t0(), c0(), kColorFilterNone, CompositorFilterOperations(), 0.5,
+ SkBlendMode::kSrcOver);
+ scoped_refptr<EffectPaintPropertyNode> e2 = EffectPaintPropertyNode::Create(
+ e1.get(), t0(), nullptr, kColorFilterNone, CompositorFilterOperations(),
+ 0.5, SkBlendMode::kSrcOver);
+
+ TestChunks chunks;
+ chunks.AddChunk(t0(), c1.get(), e2.get());
+
+ sk_sp<PaintRecord> output =
+ PaintChunksToCcLayer::Convert(
+ chunks.chunks, PropertyTreeState(t0(), c0(), e1.get()),
+ gfx::Vector2dF(), chunks.items,
+ cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
+ ->ReleaseAsRecord();
+ EXPECT_THAT(
+ *output,
+ PaintRecordMatcher::Make({cc::PaintOpType::SaveLayerAlpha, // <e2>
+ cc::PaintOpType::Save,
+ cc::PaintOpType::ClipRect, // <c1>
+ cc::PaintOpType::DrawRecord, // <p0/>
+ cc::PaintOpType::Restore, // </c1>
+ cc::PaintOpType::Restore})); // </e2>
+ EXPECT_EFFECT_BOUNDS(0, 0, 100, 100, *output, 0);
+}
+
+TEST_F(PaintChunksToCcLayerTest,
+ EffectWithNoOutputClipNestedInCompositedEffectAndClip) {
+ scoped_refptr<ClipPaintPropertyNode> c1 = ClipPaintPropertyNode::Create(
+ c0(), t0(), FloatRoundedRect(0.f, 0.f, 1.f, 1.f));
+ scoped_refptr<EffectPaintPropertyNode> e1 = EffectPaintPropertyNode::Create(
+ e0(), t0(), c0(), kColorFilterNone, CompositorFilterOperations(), 0.5,
+ SkBlendMode::kSrcOver);
+ scoped_refptr<EffectPaintPropertyNode> e2 = EffectPaintPropertyNode::Create(
+ e1.get(), t0(), nullptr, kColorFilterNone, CompositorFilterOperations(),
+ 0.5, SkBlendMode::kSrcOver);
+
+ TestChunks chunks;
+ chunks.AddChunk(t0(), c1.get(), e2.get());
+
+ sk_sp<PaintRecord> output =
+ PaintChunksToCcLayer::Convert(
+ chunks.chunks, PropertyTreeState(t0(), c1.get(), e1.get()),
+ gfx::Vector2dF(), chunks.items,
+ cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
+ ->ReleaseAsRecord();
+ EXPECT_THAT(
+ *output,
+ PaintRecordMatcher::Make({cc::PaintOpType::SaveLayerAlpha, // <e2>
+ cc::PaintOpType::DrawRecord, // <p0/>
+ cc::PaintOpType::Restore})); // </e2>
+ EXPECT_EFFECT_BOUNDS(0, 0, 100, 100, *output, 0);
+}
+
+TEST_F(PaintChunksToCcLayerTest, VisualRect) {
+ auto layer_transform = TransformPaintPropertyNode::Create(
+ t0(), TransformationMatrix().Scale(20), FloatPoint3D());
+ auto chunk_transform = TransformPaintPropertyNode::Create(
+ layer_transform.get(), TransformationMatrix().Translate(50, 100),
+ FloatPoint3D());
+
+ TestChunks chunks;
+ chunks.AddChunk(chunk_transform.get(), c0(), e0());
+
+ auto cc_list = base::MakeRefCounted<cc::DisplayItemList>(
+ cc::DisplayItemList::kTopLevelDisplayItemList);
+ PaintChunksToCcLayer::ConvertInto(
+ chunks.chunks, PropertyTreeState(layer_transform.get(), c0(), e0()),
+ gfx::Vector2dF(100, 200), FloatSize(), chunks.items, *cc_list);
+ EXPECT_EQ(gfx::Rect(-50, -100, 100, 100), cc_list->VisualRectForTesting(4));
+
+ EXPECT_THAT(
+ *cc_list->ReleaseAsRecord(),
+ PaintRecordMatcher::Make({cc::PaintOpType::Save, //
+ cc::PaintOpType::Translate, // <layer_offset>
+ cc::PaintOpType::Save, //
+ cc::PaintOpType::Concat, // <layer_transform>
+ cc::PaintOpType::DrawRecord, // <p0/>
+ cc::PaintOpType::Restore, // </layer_transform>
+ cc::PaintOpType::Restore})); // </layer_offset>
+}
+
+TEST_F(PaintChunksToCcLayerTest, NoncompositedClipPath) {
+ scoped_refptr<RefCountedPath> clip_path = base::AdoptRef(new RefCountedPath);
+ auto c1 = ClipPaintPropertyNode::Create(
+ c0(), t0(), FloatRoundedRect(25.f, 25.f, 100.f, 100.f), nullptr,
+ clip_path);
+
+ TestChunks chunks;
+ chunks.AddChunk(t0(), c1.get(), e0());
+
+ auto cc_list = base::MakeRefCounted<cc::DisplayItemList>(
+ cc::DisplayItemList::kTopLevelDisplayItemList);
+ PaintChunksToCcLayer::ConvertInto(
+ chunks.chunks, PropertyTreeState(t0(), c0(), e0()), gfx::Vector2dF(),
+ FloatSize(), chunks.items, *cc_list);
+
+ EXPECT_THAT(
+ *cc_list->ReleaseAsRecord(),
+ PaintRecordMatcher::Make({cc::PaintOpType::Save, //
+ cc::PaintOpType::ClipRect, //
+ cc::PaintOpType::ClipPath, // <clip_path>
+ cc::PaintOpType::DrawRecord, // <p0/>
+ cc::PaintOpType::Restore})); // </clip_path>
+}
+
+TEST_F(PaintChunksToCcLayerTest, EmptyClipsAreElided) {
+ scoped_refptr<ClipPaintPropertyNode> c1 = ClipPaintPropertyNode::Create(
+ c0(), t0(), FloatRoundedRect(0.f, 0.f, 1.f, 1.f));
+ scoped_refptr<ClipPaintPropertyNode> c1c2 = ClipPaintPropertyNode::Create(
+ c1.get(), t0(), FloatRoundedRect(0.f, 0.f, 1.f, 1.f));
+ scoped_refptr<ClipPaintPropertyNode> c2 = ClipPaintPropertyNode::Create(
+ c0(), t0(), FloatRoundedRect(0.f, 0.f, 1.f, 1.f));
+
+ TestChunks chunks;
+ chunks.AddChunk(nullptr, t0(), c1.get(), e0());
+ chunks.AddChunk(nullptr, t0(), c1c2.get(), e0());
+ chunks.AddChunk(nullptr, t0(), c1c2.get(), e0());
+ chunks.AddChunk(nullptr, t0(), c1c2.get(), e0());
+ chunks.AddChunk(nullptr, t0(), c1.get(), e0());
+ // D1
+ chunks.AddChunk(t0(), c2.get(), e0());
+
+ sk_sp<PaintRecord> output =
+ PaintChunksToCcLayer::Convert(
+ chunks.chunks, PropertyTreeState(t0(), c0(), e0()), gfx::Vector2dF(),
+ chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
+ ->ReleaseAsRecord();
+
+ // Note that c1 and c1c2 are elided.
+ EXPECT_THAT(*output, PaintRecordMatcher::Make({
+ cc::PaintOpType::Save, //
+ cc::PaintOpType::ClipRect, // <c2>
+ cc::PaintOpType::DrawRecord, // D1
+ cc::PaintOpType::Restore, // </c2>
+ }));
+}
+
+TEST_F(PaintChunksToCcLayerTest, NonEmptyClipsAreStored) {
+ scoped_refptr<ClipPaintPropertyNode> c1 = ClipPaintPropertyNode::Create(
+ c0(), t0(), FloatRoundedRect(0.f, 0.f, 1.f, 1.f));
+ scoped_refptr<ClipPaintPropertyNode> c1c2 = ClipPaintPropertyNode::Create(
+ c1.get(), t0(), FloatRoundedRect(0.f, 0.f, 1.f, 1.f));
+ scoped_refptr<ClipPaintPropertyNode> c2 = ClipPaintPropertyNode::Create(
+ c0(), t0(), FloatRoundedRect(0.f, 0.f, 1.f, 1.f));
+
+ TestChunks chunks;
+ chunks.AddChunk(nullptr, t0(), c1.get(), e0());
+ chunks.AddChunk(nullptr, t0(), c1c2.get(), e0());
+ chunks.AddChunk(nullptr, t0(), c1c2.get(), e0());
+ // D1
+ chunks.AddChunk(t0(), c1c2.get(), e0());
+ chunks.AddChunk(nullptr, t0(), c1.get(), e0());
+ // D2
+ chunks.AddChunk(t0(), c2.get(), e0());
+
+ sk_sp<PaintRecord> output =
+ PaintChunksToCcLayer::Convert(
+ chunks.chunks, PropertyTreeState(t0(), c0(), e0()), gfx::Vector2dF(),
+ chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
+ ->ReleaseAsRecord();
+
+ EXPECT_THAT(*output,
+ PaintRecordMatcher::Make({
+ cc::PaintOpType::Save, cc::PaintOpType::ClipRect, // <c1+c2>
+ cc::PaintOpType::DrawRecord, // D1
+ cc::PaintOpType::Restore, // </c1+c2>
+ cc::PaintOpType::Save, cc::PaintOpType::ClipRect, // <c2>
+ cc::PaintOpType::DrawRecord, // D2
+ cc::PaintOpType::Restore, // </c2>
+ }));
+}
+
+TEST_F(PaintChunksToCcLayerTest, EmptyEffectsAreStored) {
+ scoped_refptr<EffectPaintPropertyNode> e1 = EffectPaintPropertyNode::Create(
+ e0(), t0(), c0(), kColorFilterNone, CompositorFilterOperations(), 0.5,
+ SkBlendMode::kSrcOver);
+
+ TestChunks chunks;
+ chunks.AddChunk(nullptr, t0(), c0(), e0());
+ chunks.AddChunk(nullptr, t0(), c0(), e1.get());
+
+ sk_sp<PaintRecord> output =
+ PaintChunksToCcLayer::Convert(
+ chunks.chunks, PropertyTreeState(t0(), c0(), e0()), gfx::Vector2dF(),
+ chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
+ ->ReleaseAsRecord();
+
+ EXPECT_THAT(*output, PaintRecordMatcher::Make({
+ cc::PaintOpType::SaveLayerAlpha, // <e1>
+ cc::PaintOpType::Restore, // </e1>
+ }));
+ EXPECT_EFFECT_BOUNDS(0, 0, 100, 100, *output, 0);
+}
+
+TEST_F(PaintChunksToCcLayerTest, CombineClips) {
+ FloatRoundedRect clip_rect(0, 0, 100, 100);
+ FloatSize corner(5, 5);
+ FloatRoundedRect rounded_clip_rect(clip_rect.Rect(), corner, corner, corner,
+ corner);
+ auto t1 = TransformPaintPropertyNode::Create(
+ t0(), TransformationMatrix().Scale(2.f), FloatPoint3D());
+ auto c1 = ClipPaintPropertyNode::Create(c0(), t0(), clip_rect);
+ auto c2 = ClipPaintPropertyNode::Create(c1.get(), t0(), clip_rect);
+ auto c3 = ClipPaintPropertyNode::Create(c2.get(), t1.get(), clip_rect);
+ auto c4 = ClipPaintPropertyNode::Create(c3.get(), t1.get(), clip_rect);
+ auto c5 =
+ ClipPaintPropertyNode::Create(c4.get(), t1.get(), rounded_clip_rect);
+ auto c6 = ClipPaintPropertyNode::Create(c5.get(), t1.get(), clip_rect);
+
+ TestChunks chunks;
+ chunks.AddChunk(t1.get(), c6.get(), e0());
+ chunks.AddChunk(t1.get(), c3.get(), e0());
+
+ sk_sp<PaintRecord> output =
+ PaintChunksToCcLayer::Convert(
+ chunks.chunks, PropertyTreeState(t0(), c0(), e0()), gfx::Vector2dF(),
+ chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
+ ->ReleaseAsRecord();
+
+ EXPECT_THAT(
+ *output,
+ PaintRecordMatcher::Make(
+ {cc::PaintOpType::Save, cc::PaintOpType::ClipRect, // <c1+c2>
+ cc::PaintOpType::Save, cc::PaintOpType::Concat, // <t1
+ cc::PaintOpType::ClipRect, // c3+c4>
+ cc::PaintOpType::Save, cc::PaintOpType::ClipRect,
+ cc::PaintOpType::ClipRRect, // <c5>
+ cc::PaintOpType::Save, cc::PaintOpType::ClipRect, // <c6>
+ cc::PaintOpType::DrawRecord, // <p0/>
+ cc::PaintOpType::Restore, // </c6>
+ cc::PaintOpType::Restore, // </c5>
+ cc::PaintOpType::Restore, // </c3+c4 t1>
+ cc::PaintOpType::Save, cc::PaintOpType::Concat, // <t1
+ cc::PaintOpType::ClipRect, // c3>
+ cc::PaintOpType::DrawRecord, // <p1/>
+ cc::PaintOpType::Restore, // </c3 t1>
+ cc::PaintOpType::Restore})); // </c1+c2>
+}
+
+TEST_F(PaintChunksToCcLayerTest, ChunksSamePropertyTreeState) {
+ auto t1 = TransformPaintPropertyNode::Create(
+ t0(), TransformationMatrix().Scale(2.f), FloatPoint3D());
+ auto t2 = TransformPaintPropertyNode::Create(
+ t1.get(), TransformationMatrix().Scale(3.f), FloatPoint3D());
+ auto c1 = ClipPaintPropertyNode::Create(c0(), t1.get(),
+ FloatRoundedRect(0, 0, 100, 100));
+
+ TestChunks chunks;
+ chunks.AddChunk(t0(), c0(), e0());
+ chunks.AddChunk(t1.get(), c0(), e0());
+ chunks.AddChunk(t1.get(), c0(), e0());
+ chunks.AddChunk(t1.get(), c1.get(), e0());
+ chunks.AddChunk(t1.get(), c1.get(), e0());
+ chunks.AddChunk(t2.get(), c1.get(), e0());
+ chunks.AddChunk(t2.get(), c1.get(), e0());
+
+ sk_sp<PaintRecord> output =
+ PaintChunksToCcLayer::Convert(
+ chunks.chunks, PropertyTreeState(t0(), c0(), e0()), gfx::Vector2dF(),
+ chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
+ ->ReleaseAsRecord();
+
+ EXPECT_THAT(*output,
+ PaintRecordMatcher::Make(
+ {cc::PaintOpType::DrawRecord, // <p0/>
+ cc::PaintOpType::Save, cc::PaintOpType::Concat, // <t1>
+ cc::PaintOpType::DrawRecord, // <p1/>
+ cc::PaintOpType::DrawRecord, // <p2/>
+ cc::PaintOpType::Save, cc::PaintOpType::ClipRect, // <c1>
+ cc::PaintOpType::DrawRecord, // <p3/>
+ cc::PaintOpType::DrawRecord, // <p4/>
+ cc::PaintOpType::Save, cc::PaintOpType::Concat, // <t2>
+ cc::PaintOpType::DrawRecord, // <p5/>
+ cc::PaintOpType::DrawRecord, // <p6/>
+ cc::PaintOpType::Restore, // </t2>
+ cc::PaintOpType::Restore, // </c1>
+ cc::PaintOpType::Restore})); // </c1></t1>
+}
+
+TEST_F(PaintChunksToCcLayerTest, NoOpForIdentityTransforms) {
+ auto t1 = TransformPaintPropertyNode::Create(t0(), TransformationMatrix(),
+ FloatPoint3D());
+ auto t2 = TransformPaintPropertyNode::Create(t1.get(), TransformationMatrix(),
+ FloatPoint3D());
+ auto t3 = TransformPaintPropertyNode::Create(t2.get(), TransformationMatrix(),
+ FloatPoint3D());
+ auto c1 =
+ ClipPaintPropertyNode::Create(c0(), t2, FloatRoundedRect(0, 0, 100, 100));
+ auto c2 =
+ ClipPaintPropertyNode::Create(c1, t3, FloatRoundedRect(0, 0, 200, 50));
+
+ TestChunks chunks;
+ chunks.AddChunk(t0(), c0(), e0());
+ chunks.AddChunk(t1.get(), c0(), e0());
+ chunks.AddChunk(t0(), c0(), e0());
+ chunks.AddChunk(t1.get(), c0(), e0());
+ chunks.AddChunk(t2.get(), c0(), e0());
+ chunks.AddChunk(t1.get(), c0(), e0());
+ chunks.AddChunk(t1.get(), c2.get(), e0());
+
+ auto output =
+ PaintChunksToCcLayer::Convert(
+ chunks.chunks, PropertyTreeState(t0(), c0(), e0()), gfx::Vector2dF(),
+ chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
+ ->ReleaseAsRecord();
+
+ EXPECT_THAT(*output,
+ PaintRecordMatcher::Make(
+ {cc::PaintOpType::DrawRecord, // <p0/>
+ cc::PaintOpType::DrawRecord, // <p1/>
+ cc::PaintOpType::DrawRecord, // <p2/>
+ cc::PaintOpType::DrawRecord, // <p3/>
+ cc::PaintOpType::DrawRecord, // <p4/>
+ cc::PaintOpType::DrawRecord, // <p5/>
+ cc::PaintOpType::Save, cc::PaintOpType::ClipRect, // <c1+c2>
+ cc::PaintOpType::DrawRecord, // <p6/>
+ cc::PaintOpType::Restore})); // </c1+c2>
+}
+
+} // namespace
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc b/chromium/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
new file mode 100644
index 00000000000..3015d23785c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
@@ -0,0 +1,610 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.h"
+
+#include "cc/layers/layer.h"
+#include "cc/trees/clip_node.h"
+#include "cc/trees/effect_node.h"
+#include "cc/trees/layer_tree_host.h"
+#include "cc/trees/property_tree.h"
+#include "cc/trees/scroll_node.h"
+#include "cc/trees/transform_node.h"
+#include "third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.h"
+#include "third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h"
+#include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h"
+#include "third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h"
+#include "third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h"
+#include "third_party/skia/include/effects/SkColorFilterImageFilter.h"
+#include "third_party/skia/include/effects/SkLumaColorFilter.h"
+
+namespace blink {
+
+namespace {
+
+static constexpr int kInvalidNodeId = -1;
+// cc's property trees use 0 for the root node (always non-null).
+static constexpr int kRealRootNodeId = 0;
+// cc allocates special nodes for root effects such as the device scale.
+static constexpr int kSecondaryRootNodeId = 1;
+
+} // namespace
+
+PropertyTreeManager::PropertyTreeManager(PropertyTreeManagerClient& client,
+ cc::PropertyTrees& property_trees,
+ cc::Layer* root_layer,
+ int sequence_number)
+ : client_(client),
+ property_trees_(property_trees),
+ root_layer_(root_layer),
+ sequence_number_(sequence_number) {
+ SetupRootTransformNode();
+ SetupRootClipNode();
+ SetupRootEffectNode();
+ SetupRootScrollNode();
+}
+
+void PropertyTreeManager::Finalize() {
+ while (effect_stack_.size())
+ CloseCcEffect();
+}
+
+cc::TransformTree& PropertyTreeManager::GetTransformTree() {
+ return property_trees_.transform_tree;
+}
+
+cc::ClipTree& PropertyTreeManager::GetClipTree() {
+ return property_trees_.clip_tree;
+}
+
+cc::EffectTree& PropertyTreeManager::GetEffectTree() {
+ return property_trees_.effect_tree;
+}
+
+cc::ScrollTree& PropertyTreeManager::GetScrollTree() {
+ return property_trees_.scroll_tree;
+}
+
+void PropertyTreeManager::SetupRootTransformNode() {
+ // cc is hardcoded to use transform node index 1 for device scale and
+ // transform.
+ cc::TransformTree& transform_tree = property_trees_.transform_tree;
+ transform_tree.clear();
+ property_trees_.element_id_to_transform_node_index.clear();
+ cc::TransformNode& transform_node = *transform_tree.Node(
+ transform_tree.Insert(cc::TransformNode(), kRealRootNodeId));
+ DCHECK_EQ(transform_node.id, kSecondaryRootNodeId);
+ transform_node.source_node_id = transform_node.parent_id;
+
+ // TODO(jaydasika): We shouldn't set ToScreen and FromScreen of root
+ // transform node here. They should be set while updating transform tree in
+ // cc.
+ float device_scale_factor =
+ root_layer_->layer_tree_host()->device_scale_factor();
+ gfx::Transform to_screen;
+ to_screen.Scale(device_scale_factor, device_scale_factor);
+ transform_tree.SetToScreen(kRealRootNodeId, to_screen);
+ gfx::Transform from_screen;
+ bool invertible = to_screen.GetInverse(&from_screen);
+ DCHECK(invertible);
+ transform_tree.SetFromScreen(kRealRootNodeId, from_screen);
+ transform_tree.set_needs_update(true);
+
+ transform_node_map_.Set(TransformPaintPropertyNode::Root(),
+ transform_node.id);
+ root_layer_->SetTransformTreeIndex(transform_node.id);
+}
+
+void PropertyTreeManager::SetupRootClipNode() {
+ // cc is hardcoded to use clip node index 1 for viewport clip.
+ cc::ClipTree& clip_tree = property_trees_.clip_tree;
+ clip_tree.clear();
+ cc::ClipNode& clip_node =
+ *clip_tree.Node(clip_tree.Insert(cc::ClipNode(), kRealRootNodeId));
+ DCHECK_EQ(clip_node.id, kSecondaryRootNodeId);
+
+ clip_node.clip_type = cc::ClipNode::ClipType::APPLIES_LOCAL_CLIP;
+ clip_node.clip = gfx::RectF(
+ gfx::SizeF(root_layer_->layer_tree_host()->device_viewport_size()));
+ clip_node.transform_id = kRealRootNodeId;
+
+ clip_node_map_.Set(ClipPaintPropertyNode::Root(), clip_node.id);
+ root_layer_->SetClipTreeIndex(clip_node.id);
+}
+
+void PropertyTreeManager::SetupRootEffectNode() {
+ // cc is hardcoded to use effect node index 1 for root render surface.
+ cc::EffectTree& effect_tree = property_trees_.effect_tree;
+ effect_tree.clear();
+ property_trees_.element_id_to_effect_node_index.clear();
+ cc::EffectNode& effect_node =
+ *effect_tree.Node(effect_tree.Insert(cc::EffectNode(), kInvalidNodeId));
+ DCHECK_EQ(effect_node.id, kSecondaryRootNodeId);
+
+ static UniqueObjectId unique_id = NewUniqueObjectId();
+
+ effect_node.stable_id =
+ CompositorElementIdFromUniqueObjectId(unique_id).ToInternalValue();
+ effect_node.transform_id = kRealRootNodeId;
+ effect_node.clip_id = kSecondaryRootNodeId;
+ effect_node.has_render_surface = true;
+ root_layer_->SetEffectTreeIndex(effect_node.id);
+
+ current_effect_id_ = effect_node.id;
+ current_effect_type_ = CcEffectType::kEffect;
+ current_effect_ = EffectPaintPropertyNode::Root();
+ current_clip_ = current_effect_->OutputClip();
+}
+
+void PropertyTreeManager::SetupRootScrollNode() {
+ cc::ScrollTree& scroll_tree = property_trees_.scroll_tree;
+ scroll_tree.clear();
+ property_trees_.element_id_to_scroll_node_index.clear();
+ cc::ScrollNode& scroll_node =
+ *scroll_tree.Node(scroll_tree.Insert(cc::ScrollNode(), kRealRootNodeId));
+ DCHECK_EQ(scroll_node.id, kSecondaryRootNodeId);
+ scroll_node.transform_id = kSecondaryRootNodeId;
+
+ scroll_node_map_.Set(ScrollPaintPropertyNode::Root(), scroll_node.id);
+ root_layer_->SetScrollTreeIndex(scroll_node.id);
+}
+
+int PropertyTreeManager::EnsureCompositorTransformNode(
+ const TransformPaintPropertyNode* transform_node) {
+ DCHECK(transform_node);
+ // TODO(crbug.com/645615): Remove the failsafe here.
+ if (!transform_node)
+ return kSecondaryRootNodeId;
+
+ auto it = transform_node_map_.find(transform_node);
+ if (it != transform_node_map_.end())
+ return it->value;
+
+ int parent_id = EnsureCompositorTransformNode(transform_node->Parent());
+ int id = GetTransformTree().Insert(cc::TransformNode(), parent_id);
+
+ cc::TransformNode& compositor_node = *GetTransformTree().Node(id);
+ compositor_node.source_node_id = parent_id;
+
+ FloatPoint3D origin = transform_node->Origin();
+ compositor_node.pre_local.matrix().setTranslate(-origin.X(), -origin.Y(),
+ -origin.Z());
+ compositor_node.local.matrix() =
+ TransformationMatrix::ToSkMatrix44(transform_node->Matrix());
+ compositor_node.post_local.matrix().setTranslate(origin.X(), origin.Y(),
+ origin.Z());
+ compositor_node.needs_local_transform_update = true;
+ compositor_node.flattens_inherited_transform =
+ transform_node->FlattensInheritedTransform();
+ compositor_node.sorting_context_id = transform_node->RenderingContextId();
+
+ CompositorElementId compositor_element_id =
+ transform_node->GetCompositorElementId();
+ if (compositor_element_id) {
+ property_trees_.element_id_to_transform_node_index[compositor_element_id] =
+ id;
+ }
+
+ // If this transform is a scroll offset translation, create the associated
+ // compositor scroll property node and adjust the compositor transform node's
+ // scroll offset.
+ if (auto* scroll_node = transform_node->ScrollNode()) {
+ // Blink creates a 2d transform node just for scroll offset whereas cc's
+ // transform node has a special scroll offset field. To handle this we
+ // adjust cc's transform node to remove the 2d scroll translation and
+ // instead set the scroll_offset field.
+ auto scroll_offset_size = transform_node->Matrix().To2DTranslation();
+ auto scroll_offset = gfx::ScrollOffset(-scroll_offset_size.Width(),
+ -scroll_offset_size.Height());
+ DCHECK(compositor_node.local.IsIdentityOr2DTranslation());
+ compositor_node.scroll_offset = scroll_offset;
+ compositor_node.local.MakeIdentity();
+ compositor_node.scrolls = true;
+
+ CreateCompositorScrollNode(scroll_node, compositor_node);
+ }
+
+ auto result = transform_node_map_.Set(transform_node, id);
+ DCHECK(result.is_new_entry);
+ GetTransformTree().set_needs_update(true);
+
+ return id;
+}
+
+int PropertyTreeManager::EnsureCompositorClipNode(
+ const ClipPaintPropertyNode* clip_node) {
+ DCHECK(clip_node);
+ // TODO(crbug.com/645615): Remove the failsafe here.
+ if (!clip_node)
+ return kSecondaryRootNodeId;
+
+ auto it = clip_node_map_.find(clip_node);
+ if (it != clip_node_map_.end())
+ return it->value;
+
+ int parent_id = EnsureCompositorClipNode(clip_node->Parent());
+ int id = GetClipTree().Insert(cc::ClipNode(), parent_id);
+
+ cc::ClipNode& compositor_node = *GetClipTree().Node(id);
+
+ compositor_node.clip = clip_node->ClipRect().Rect();
+ compositor_node.transform_id =
+ EnsureCompositorTransformNode(clip_node->LocalTransformSpace());
+ compositor_node.clip_type = cc::ClipNode::ClipType::APPLIES_LOCAL_CLIP;
+
+ auto result = clip_node_map_.Set(clip_node, id);
+ DCHECK(result.is_new_entry);
+ GetClipTree().set_needs_update(true);
+ return id;
+}
+
+void PropertyTreeManager::CreateCompositorScrollNode(
+ const ScrollPaintPropertyNode* scroll_node,
+ const cc::TransformNode& scroll_offset_translation) {
+ DCHECK(!scroll_node_map_.Contains(scroll_node));
+
+ auto parent_it = scroll_node_map_.find(scroll_node->Parent());
+ // Compositor transform nodes up to scroll_offset_translation must exist.
+ // Scrolling uses the transform tree for scroll offsets so this means all
+ // ancestor scroll nodes must also exist.
+ DCHECK(parent_it != scroll_node_map_.end());
+ int parent_id = parent_it->value;
+ int id = GetScrollTree().Insert(cc::ScrollNode(), parent_id);
+
+ cc::ScrollNode& compositor_node = *GetScrollTree().Node(id);
+ compositor_node.scrollable = true;
+
+ compositor_node.container_bounds = scroll_node->ContainerRect().Size();
+ compositor_node.bounds = scroll_node->ContentsRect().Size();
+ compositor_node.user_scrollable_horizontal =
+ scroll_node->UserScrollableHorizontal();
+ compositor_node.user_scrollable_vertical =
+ scroll_node->UserScrollableVertical();
+ compositor_node.main_thread_scrolling_reasons =
+ scroll_node->GetMainThreadScrollingReasons();
+
+ auto compositor_element_id = scroll_node->GetCompositorElementId();
+ if (compositor_element_id) {
+ compositor_node.element_id = compositor_element_id;
+ property_trees_.element_id_to_scroll_node_index[compositor_element_id] = id;
+ }
+
+ compositor_node.transform_id = scroll_offset_translation.id;
+
+ // TODO(pdr): Set the scroll node's non_fast_scrolling_region value.
+
+ auto result = scroll_node_map_.Set(scroll_node, id);
+ DCHECK(result.is_new_entry);
+
+ GetScrollTree().SetScrollOffset(compositor_element_id,
+ scroll_offset_translation.scroll_offset);
+ GetScrollTree().set_needs_update(true);
+}
+
+int PropertyTreeManager::EnsureCompositorScrollNode(
+ const TransformPaintPropertyNode* scroll_offset_translation) {
+ const auto* scroll_node = scroll_offset_translation->ScrollNode();
+ DCHECK(scroll_node);
+ EnsureCompositorTransformNode(scroll_offset_translation);
+ auto it = scroll_node_map_.find(scroll_node);
+ DCHECK(it != scroll_node_map_.end());
+ return it->value;
+}
+
+void PropertyTreeManager::EmitClipMaskLayer() {
+ int clip_id = EnsureCompositorClipNode(current_clip_);
+ CompositorElementId mask_isolation_id, mask_effect_id;
+ cc::Layer* mask_layer = client_.CreateOrReuseSynthesizedClipLayer(
+ current_clip_, mask_isolation_id, mask_effect_id);
+
+ cc::EffectNode& mask_isolation = *GetEffectTree().Node(current_effect_id_);
+ // Assignment of mask_isolation.stable_id was delayed until now.
+ // See PropertyTreeManager::SynthesizeCcEffectsForClipsIfNeeded().
+ DCHECK_EQ(static_cast<uint64_t>(cc::EffectNode::INVALID_STABLE_ID),
+ mask_isolation.stable_id);
+ mask_isolation.stable_id = mask_isolation_id.ToInternalValue();
+
+ cc::EffectNode& mask_effect = *GetEffectTree().Node(
+ GetEffectTree().Insert(cc::EffectNode(), current_effect_id_));
+ mask_effect.stable_id = mask_effect_id.ToInternalValue();
+ mask_effect.clip_id = clip_id;
+ mask_effect.has_render_surface = true;
+ mask_effect.blend_mode = SkBlendMode::kDstIn;
+
+ const TransformPaintPropertyNode* clip_space =
+ current_clip_->LocalTransformSpace();
+ root_layer_->AddChild(mask_layer);
+ mask_layer->set_property_tree_sequence_number(sequence_number_);
+ mask_layer->SetTransformTreeIndex(EnsureCompositorTransformNode(clip_space));
+ // TODO(pdr): This could be a performance issue because it crawls up the
+ // transform tree for each pending layer. If this is on profiles, we should
+ // cache a lookup of transform node to scroll translation transform node.
+ int scroll_id =
+ EnsureCompositorScrollNode(&clip_space->NearestScrollTranslationNode());
+ mask_layer->SetScrollTreeIndex(scroll_id);
+ mask_layer->SetClipTreeIndex(clip_id);
+ mask_layer->SetEffectTreeIndex(mask_effect.id);
+}
+
+void PropertyTreeManager::CloseCcEffect() {
+ DCHECK(effect_stack_.size());
+ const EffectStackEntry& previous_state = effect_stack_.back();
+
+ // An effect with exotic blending that is masked by a synthesized clip must
+ // have its blending to the outermost synthesized clip. It is because
+ // blending needs access to the backdrop of the enclosing effect. With
+ // the isolation for a synthesized clip, a blank backdrop will be seen.
+ // Therefore the blending is delegated to the outermost synthesized clip,
+ // thus the clip can't be shared with sibling layers, and must be closed now.
+ bool clear_synthetic_effects =
+ !IsCurrentCcEffectSynthetic() &&
+ current_effect_->BlendMode() != SkBlendMode::kSrcOver;
+
+ // We are about to close an effect that was synthesized for isolating
+ // a clip mask. Now emit the actual clip mask that will be composited on
+ // top of masked contents with SkBlendMode::kDstIn.
+ if (IsCurrentCcEffectSynthetic())
+ EmitClipMaskLayer();
+
+ current_effect_id_ = previous_state.effect_id;
+ current_effect_type_ = previous_state.effect_type;
+ current_effect_ = previous_state.effect;
+ current_clip_ = previous_state.clip;
+ effect_stack_.pop_back();
+
+ if (clear_synthetic_effects) {
+ while (IsCurrentCcEffectSynthetic())
+ CloseCcEffect();
+ }
+}
+
+int PropertyTreeManager::SwitchToEffectNodeWithSynthesizedClip(
+ const EffectPaintPropertyNode& next_effect,
+ const ClipPaintPropertyNode& next_clip) {
+ // This function is expected to be invoked right before emitting each layer.
+ // It keeps track of the nesting of clip and effects, output a composited
+ // effect node whenever an effect is entered, or a non-trivial clip is
+ // entered. In the latter case, the generated composited effect node is
+ // called a "synthetic effect", and the corresponding clip a "synthesized
+ // clip". Upon exiting a synthesized clip, a mask layer will be appended,
+ // which will be kDstIn blended on top of contents enclosed by the synthetic
+ // effect, i.e. applying the clip as a mask.
+ //
+ // For example with the following clip and effect tree and pending layers:
+ // E0 <-- E1
+ // C0 <-- C1(rounded)
+ // [P0(E1,C0), P1(E1,C1), P2(E0, C1)]
+ // In effect stack diagram:
+ // P0(C0) P1(C1)
+ // [ E1 ] P2(C1)
+ // [ E0 ]
+ //
+ // The following cc property trees and layers will be generated:
+ // E0 <+- E1 <-- E_C1_1 <-- E_C1_1M
+ // +- E_C1_2 <-- E_C1_2M
+ // C0 <-- C1
+ // [L0(E1,C0), L1(E_C1_1, C1), L_C1_1(E_C1_1M, C1), L2(E0, C1),
+ // L_C1_2(E_C1_2M, C1)]
+ // In effect stack diagram:
+ // L_C1_1
+ // L1(C1) [ E_C1_1M ] L_C2_2
+ // L0(C0) [ E_C1_1 ] L2(C1) [ E_C1_2M ]
+ // [ E1 ][ E_C1_2 ]
+ // [ E0 ]
+ //
+ // As the caller iterates the layer list, the sequence of events happen in
+ // the following order:
+ // Prior to emitting P0, this method is invoked with (E1, C0). A compositor
+ // effect node for E1 is generated as we are entering it. The caller emits P0.
+ // Prior to emitting P1, this method is invoked with (E1, C1). A synthetic
+ // compositor effect for C1 is generated as we are entering it. The caller
+ // emits P1.
+ // Prior to emitting P2, this method is invoked with (E0, C1). Both previously
+ // entered effects must be closed, because synthetic effect for C1 is enclosed
+ // by E1, thus must be closed before E1 can be closed. A mask layer L_C1_1
+ // is generated along with an internal effect node for blending. After closing
+ // both effects, C1 has to be entered again, thus generates another synthetic
+ // compositor effect. The caller emits P2.
+ // At last, the caller invokes Finalize() to close the unclosed synthetic
+ // effect. Another mask layer L_C1_2 is generated, along with its internal
+ // effect node for blending.
+ const auto& ancestor = LowestCommonAncestor(*current_effect_, next_effect);
+ while (current_effect_ != &ancestor)
+ CloseCcEffect();
+
+ bool newly_built = BuildEffectNodesRecursively(&next_effect);
+ SynthesizeCcEffectsForClipsIfNeeded(&next_clip, SkBlendMode::kSrcOver,
+ newly_built);
+
+ return current_effect_id_;
+}
+
+static bool IsNodeOnAncestorChain(const ClipPaintPropertyNode& find,
+ const ClipPaintPropertyNode& current,
+ const ClipPaintPropertyNode& ancestor) {
+ // Precondition: |ancestor| must be an (inclusive) ancestor of |current|
+ // otherwise the behavior is undefined.
+ // Returns true if node |find| is one of the node on the ancestor chain
+ // [current, ancestor). Returns false otherwise.
+ DCHECK(ancestor.IsAncestorOf(current));
+
+ for (const auto* node = &current; node != &ancestor; node = node->Parent()) {
+ if (node == &find)
+ return true;
+ }
+ return false;
+}
+
+SkBlendMode PropertyTreeManager::SynthesizeCcEffectsForClipsIfNeeded(
+ const ClipPaintPropertyNode* target_clip,
+ SkBlendMode delegated_blend,
+ bool effect_is_newly_built) {
+ if (delegated_blend != SkBlendMode::kSrcOver) {
+ // Exit all synthetic effect node for rounded clip if the next child has
+ // exotic blending mode because it has to access the backdrop of enclosing
+ // effect.
+ while (IsCurrentCcEffectSynthetic())
+ CloseCcEffect();
+
+ // An effect node can't omit render surface if it has child with exotic
+ // blending mode. See comments below for more detail.
+ // TODO(crbug.com/504464): Remove premature optimization here.
+ GetEffectTree().Node(current_effect_id_)->has_render_surface = true;
+ } else {
+ // Exit synthetic effects until there are no more synthesized clips below
+ // our lowest common ancestor.
+ const auto& lca = LowestCommonAncestor(*current_clip_, *target_clip);
+ while (current_clip_ != &lca) {
+ DCHECK(IsCurrentCcEffectSynthetic());
+ const auto* pre_exit_clip = current_clip_;
+ CloseCcEffect();
+ // We may run past the lowest common ancestor because it may not have
+ // been synthesized.
+ if (IsNodeOnAncestorChain(lca, *pre_exit_clip, *current_clip_))
+ break;
+ }
+
+ // If the effect is an existing node, i.e. already has at least one paint
+ // chunk or child effect, and by reaching here it implies we are going to
+ // attach either another paint chunk or child effect to it. We can no longer
+ // omit render surface for it even for opacity-only node.
+ // See comments in PropertyTreeManager::BuildEffectNodesRecursively().
+ // TODO(crbug.com/504464): Remove premature optimization here.
+ if (!effect_is_newly_built && !IsCurrentCcEffectSynthetic() &&
+ current_effect_->Opacity() != 1.f)
+ GetEffectTree().Node(current_effect_id_)->has_render_surface = true;
+ }
+
+ DCHECK(current_clip_->IsAncestorOf(*target_clip));
+
+ Vector<const ClipPaintPropertyNode*> pending_clips;
+ for (; target_clip != current_clip_; target_clip = target_clip->Parent()) {
+ DCHECK(target_clip);
+ bool should_synthesize =
+ target_clip->ClipRect().IsRounded() || target_clip->ClipPath();
+ if (should_synthesize)
+ pending_clips.push_back(target_clip);
+ }
+
+ for (size_t i = pending_clips.size(); i--;) {
+ const ClipPaintPropertyNode* next_clip = pending_clips[i];
+
+ // For each of clip synthesized, an isolation effect node needs to be
+ // created to enclose only the layers that should be masked by the clip.
+ cc::EffectNode& mask_isolation = *GetEffectTree().Node(
+ GetEffectTree().Insert(cc::EffectNode(), current_effect_id_));
+ // mask_isolation.stable_id will be assigned later when the effect is
+ // closed. For now the default value of INVALID_STABLE_ID is used.
+ // See PropertyTreeManager::EmitClipMaskLayer().
+ mask_isolation.clip_id = EnsureCompositorClipNode(next_clip);
+ mask_isolation.has_render_surface = true;
+ // Clip and kDstIn do not commute. This shall never be reached because
+ // kDstIn is only used internally to implement CSS clip-path and mask,
+ // and there is never a difference between the output clip of the effect
+ // and the mask content.
+ DCHECK(delegated_blend != SkBlendMode::kDstIn);
+ mask_isolation.blend_mode = delegated_blend;
+ delegated_blend = SkBlendMode::kSrcOver;
+
+ effect_stack_.emplace_back(
+ EffectStackEntry{current_effect_id_, current_effect_type_,
+ current_effect_, current_clip_});
+ current_effect_id_ = mask_isolation.id;
+ current_effect_type_ = CcEffectType::kSynthesizedClip;
+ current_clip_ = next_clip;
+ }
+
+ return delegated_blend;
+}
+
+bool PropertyTreeManager::BuildEffectNodesRecursively(
+ const EffectPaintPropertyNode* next_effect) {
+ if (next_effect == current_effect_)
+ return false;
+ DCHECK(next_effect);
+
+ bool newly_built = BuildEffectNodesRecursively(next_effect->Parent());
+ DCHECK_EQ(next_effect->Parent(), current_effect_);
+
+#if DCHECK_IS_ON()
+ DCHECK(!effect_nodes_converted_.Contains(next_effect))
+ << "Malformed paint artifact. Paint chunks under the same effect should "
+ "be contiguous.";
+ effect_nodes_converted_.insert(next_effect);
+#endif
+
+ SkBlendMode used_blend_mode;
+ int output_clip_id;
+ if (next_effect->OutputClip()) {
+ used_blend_mode = SynthesizeCcEffectsForClipsIfNeeded(
+ next_effect->OutputClip(), next_effect->BlendMode(), newly_built);
+ output_clip_id = EnsureCompositorClipNode(next_effect->OutputClip());
+ } else {
+ while (IsCurrentCcEffectSynthetic())
+ CloseCcEffect();
+ // An effect node can't omit render surface if it has child with exotic
+ // blending mode, nor being opacity-only node with more than one child.
+ // TODO(crbug.com/504464): Remove premature optimization here.
+ if (next_effect->BlendMode() != SkBlendMode::kSrcOver ||
+ (!newly_built && current_effect_->Opacity() != 1.f))
+ GetEffectTree().Node(current_effect_id_)->has_render_surface = true;
+
+ used_blend_mode = next_effect->BlendMode();
+ output_clip_id = GetEffectTree().Node(current_effect_id_)->clip_id;
+ }
+
+ cc::EffectNode& effect_node = *GetEffectTree().Node(
+ GetEffectTree().Insert(cc::EffectNode(), current_effect_id_));
+ effect_node.stable_id =
+ next_effect->GetCompositorElementId().ToInternalValue();
+ effect_node.clip_id = output_clip_id;
+ // Every effect is supposed to have render surface enabled for grouping,
+ // but we can get away without one if the effect is opacity-only and has only
+ // one compositing child with kSrcOver blend mode. This is both for
+ // optimization and not introducing sub-pixel differences in layout tests.
+ // See PropertyTreeManager::switchToEffectNode() and above where we
+ // retrospectively enable render surface when more than one compositing child
+ // or a child with exotic blend mode is detected.
+ // TODO(crbug.com/504464): There is ongoing work in cc to delay render surface
+ // decision until later phase of the pipeline. Remove premature optimization
+ // here once the work is ready.
+ if (!next_effect->Filter().IsEmpty() ||
+ used_blend_mode != SkBlendMode::kSrcOver)
+ effect_node.has_render_surface = true;
+ effect_node.opacity = next_effect->Opacity();
+ if (next_effect->GetColorFilter() != kColorFilterNone) {
+ // Currently color filter is only used by SVG masks.
+ // We are cutting corner here by support only specific configuration.
+ DCHECK(next_effect->GetColorFilter() == kColorFilterLuminanceToAlpha);
+ DCHECK(used_blend_mode == SkBlendMode::kDstIn);
+ DCHECK(next_effect->Filter().IsEmpty());
+ effect_node.filters.Append(cc::FilterOperation::CreateReferenceFilter(
+ sk_make_sp<ColorFilterPaintFilter>(SkLumaColorFilter::Make(),
+ nullptr)));
+ } else {
+ effect_node.filters = next_effect->Filter().AsCcFilterOperations();
+ }
+ effect_node.blend_mode = used_blend_mode;
+ CompositorElementId compositor_element_id =
+ next_effect->GetCompositorElementId();
+ if (compositor_element_id) {
+ DCHECK(property_trees_.element_id_to_effect_node_index.find(
+ compositor_element_id) ==
+ property_trees_.element_id_to_effect_node_index.end());
+ property_trees_.element_id_to_effect_node_index[compositor_element_id] =
+ effect_node.id;
+ }
+ effect_stack_.emplace_back(EffectStackEntry{current_effect_id_,
+ current_effect_type_,
+ current_effect_, current_clip_});
+ current_effect_id_ = effect_node.id;
+ current_effect_type_ = CcEffectType::kEffect;
+ current_effect_ = next_effect;
+ if (next_effect->OutputClip())
+ current_clip_ = next_effect->OutputClip();
+
+ return true;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.h b/chromium/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.h
new file mode 100644
index 00000000000..50c8ba8792c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.h
@@ -0,0 +1,192 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITING_PROPERTY_TREE_MANAGER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITING_PROPERTY_TREE_MANAGER_H_
+
+#include "third_party/blink/renderer/platform/graphics/compositor_element_id.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "third_party/skia/include/core/SkBlendMode.h"
+
+namespace cc {
+class ClipTree;
+class EffectTree;
+class Layer;
+class PropertyTrees;
+class ScrollTree;
+class TransformTree;
+struct TransformNode;
+}
+
+namespace blink {
+
+class ClipPaintPropertyNode;
+class EffectPaintPropertyNode;
+class ScrollPaintPropertyNode;
+class TransformPaintPropertyNode;
+
+class PropertyTreeManagerClient {
+ public:
+ virtual cc::Layer* CreateOrReuseSynthesizedClipLayer(
+ const ClipPaintPropertyNode*,
+ CompositorElementId& mask_isolation_id,
+ CompositorElementId& mask_effect_id) = 0;
+};
+
+// Mutates a cc property tree to reflect Blink paint property tree
+// state. Intended for use by PaintArtifactCompositor.
+class PropertyTreeManager {
+ WTF_MAKE_NONCOPYABLE(PropertyTreeManager);
+
+ public:
+ PropertyTreeManager(PropertyTreeManagerClient&,
+ cc::PropertyTrees&,
+ cc::Layer* root_layer,
+ int sequence_number);
+ ~PropertyTreeManager() {
+ DCHECK(!effect_stack_.size()) << "PropertyTreeManager::Finalize() must be "
+ "called at the end of tree conversion.";
+ }
+
+ void SetupRootTransformNode();
+ void SetupRootClipNode();
+ void SetupRootEffectNode();
+ void SetupRootScrollNode();
+
+ // A brief discourse on cc property tree nodes, identifiers, and current and
+ // future design evolution envisioned:
+ //
+ // cc property trees identify nodes by their |id|, which implementation-wise
+ // is actually its index in the property tree's vector of its node type. More
+ // recent cc code now refers to these as 'node indices', or 'property tree
+ // indices'. |parent_id| is the same sort of 'node index' of that node's
+ // parent.
+ //
+ // Note there are two other primary types of 'ids' referenced in cc property
+ // tree related logic: (1) ElementId, also known Blink-side as
+ // CompositorElementId, used by the animation system to allow tying an element
+ // to its respective layer, and (2) layer ids. There are other ancillary ids
+ // not relevant to any of the above, such as
+ // cc::TransformNode::sorting_context_id
+ // (a.k.a. blink::TransformPaintPropertyNode::renderingContextId()).
+ //
+ // There is a vision to move toward a world where cc property nodes have no
+ // association with layers and instead have a |stable_id|. The id could come
+ // from an ElementId in turn derived from the layout object responsible for
+ // creating the property node.
+ //
+ // We would also like to explore moving to use a single shared property tree
+ // representation across both cc and Blink. See
+ // platform/graphics/paint/README.md for more.
+ //
+ // With the above as background, we can now state more clearly a description
+ // of the below set of compositor node methods: they take Blink paint property
+ // tree nodes as input, create a corresponding compositor property tree node
+ // if none yet exists, and return the compositor node's 'node id', a.k.a.,
+ // 'node index'.
+
+ // Returns the compositor transform node id. If a compositor transform node
+ // does not exist, it is created. Any transforms that are for scroll offset
+ // translation will ensure the associated scroll node exists.
+ int EnsureCompositorTransformNode(const TransformPaintPropertyNode*);
+ int EnsureCompositorClipNode(const ClipPaintPropertyNode*);
+ // Ensure the compositor scroll node using the associated scroll offset
+ // translation.
+ int EnsureCompositorScrollNode(
+ const TransformPaintPropertyNode* scroll_offset_translation);
+
+ // This function is expected to be invoked right before emitting each layer.
+ // It keeps track of the nesting of clip and effects, output a composited
+ // effect node whenever an effect is entered, or a non-trivial clip is
+ // entered. In the latter case, the generated composited effect node is
+ // called a "synthetic effect", and the corresponding clip a "synthesized
+ // clip". Upon exiting a synthesized clip, a mask layer will be appended,
+ // which will be kDstIn blended on top of contents enclosed by the synthetic
+ // effect, i.e. applying the clip as a mask.
+ int SwitchToEffectNodeWithSynthesizedClip(
+ const EffectPaintPropertyNode& next_effect,
+ const ClipPaintPropertyNode& next_clip);
+ // Expected to be invoked after emitting the last layer. This will exit all
+ // effects on the effect stack, generating clip mask layers for all the
+ // unclosed synthesized clips.
+ void Finalize();
+
+ private:
+ bool BuildEffectNodesRecursively(const EffectPaintPropertyNode* next_effect);
+ SkBlendMode SynthesizeCcEffectsForClipsIfNeeded(
+ const ClipPaintPropertyNode* target_clip,
+ SkBlendMode delegated_blend,
+ bool effect_is_newly_built);
+ void EmitClipMaskLayer();
+ void CloseCcEffect();
+ bool IsCurrentCcEffectSynthetic() const {
+ return current_effect_type_ != CcEffectType::kEffect;
+ }
+
+ cc::TransformTree& GetTransformTree();
+ cc::ClipTree& GetClipTree();
+ cc::EffectTree& GetEffectTree();
+ cc::ScrollTree& GetScrollTree();
+
+ // Should only be called from EnsureCompositorTransformNode as part of
+ // creating the associated scroll offset transform node.
+ void CreateCompositorScrollNode(
+ const ScrollPaintPropertyNode*,
+ const cc::TransformNode& scroll_offset_translation);
+
+ PropertyTreeManagerClient& client_;
+
+ // Property trees which should be updated by the manager.
+ cc::PropertyTrees& property_trees_;
+
+ // The special layer which is the parent of every other layers.
+ // This is where clip mask layers we generated for synthesized clips are
+ // appended into.
+ cc::Layer* root_layer_;
+
+ // Maps from Blink-side property tree nodes to cc property node indices.
+ HashMap<const TransformPaintPropertyNode*, int> transform_node_map_;
+ HashMap<const ClipPaintPropertyNode*, int> clip_node_map_;
+ HashMap<const ScrollPaintPropertyNode*, int> scroll_node_map_;
+
+ // The cc effect node that has the corresponding drawing state to the
+ // effect and clip state from the last SwitchToEffectNodeWithSynthesizedClip.
+ int current_effect_id_;
+ // The type of operation the current cc effect node applies. kEffect means
+ // it corresponds to a Blink effect node. kSynthesizedClip means it implements
+ // a Blink clip node that has to be rasterized.
+ enum class CcEffectType { kEffect, kSynthesizedClip } current_effect_type_;
+ // The effect state of the current cc effect node.
+ const EffectPaintPropertyNode* current_effect_;
+ // The clip state of the current cc effect node. This value may be shallower
+ // than the one passed into SwitchToEffectNodeWithSynthesizedClip because not
+ // every clip needs to be synthesized as cc effect.
+ // Is set to output clip of the effect if the type is kEffect, or set to the
+ // synthesized clip node if the type is kSynthesizedClip.
+ const ClipPaintPropertyNode* current_clip_;
+ // This keep track of cc effect stack. Whenever a new cc effect is nested,
+ // a new entry is pushed, and the entry will be popped when the effect closed.
+ // Note: This is a "restore stack", i.e. the top element does not represent
+ // the current state, but the state prior to most recent push.
+ struct EffectStackEntry {
+ int effect_id;
+ CcEffectType effect_type;
+ const EffectPaintPropertyNode* effect;
+ const ClipPaintPropertyNode* clip;
+ };
+ Vector<EffectStackEntry> effect_stack_;
+
+ int sequence_number_;
+
+#if DCHECK_IS_ON()
+ HashSet<const EffectPaintPropertyNode*> effect_nodes_converted_;
+#endif
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITING_PROPERTY_TREE_MANAGER_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositing_reasons.cc b/chromium/third_party/blink/renderer/platform/graphics/compositing_reasons.cc
new file mode 100644
index 00000000000..843a84a4fe7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/compositing_reasons.cc
@@ -0,0 +1,209 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/compositing_reasons.h"
+
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+
+namespace blink {
+
+namespace {
+
+struct CompositingReasonStringMap {
+ CompositingReasons reason;
+ const char* short_name;
+ const char* description;
+};
+
+constexpr CompositingReasonStringMap kCompositingReasonsStringMap[] = {
+ {CompositingReason::k3DTransform, "transform3D", "Has a 3d transform"},
+ {CompositingReason::kVideo, "video", "Is an accelerated video"},
+ {CompositingReason::kCanvas, "canvas",
+ "Is an accelerated canvas, or is a display list backed canvas that was "
+ "promoted to a layer based on a performance heuristic."},
+ {CompositingReason::kPlugin, "plugin", "Is an accelerated plugin"},
+ {CompositingReason::kIFrame, "iFrame", "Is an accelerated iFrame"},
+ {CompositingReason::kBackfaceVisibilityHidden, "backfaceVisibilityHidden",
+ "Has backface-visibility: hidden"},
+ {CompositingReason::kActiveTransformAnimation, "activeTransformAnimation",
+ "Has an active accelerated transform animation or transition"},
+ {CompositingReason::kActiveOpacityAnimation, "activeOpacityAnimation",
+ "Has an active accelerated opacity animation or transition"},
+ {CompositingReason::kActiveFilterAnimation, "activeFilterAnimation",
+ "Has an active accelerated filter animation or transition"},
+ {CompositingReason::kActiveBackdropFilterAnimation,
+ "activeBackdropFilterAnimation",
+ "Has an active accelerated backdrop filter animation or transition"},
+ {CompositingReason::kTransitionProperty, "transitionProperty",
+ "Has an acceleratable transition property (active or inactive)"},
+ {CompositingReason::kScrollDependentPosition, "scrollDependentPosition",
+ "Is fixed or sticky position"},
+ {CompositingReason::kOverflowScrollingTouch, "overflowScrollingTouch",
+ "Is a scrollable overflow element"},
+ {CompositingReason::kOverflowScrollingParent, "overflowScrollingParent",
+ "Scroll parent is not an ancestor"},
+ {CompositingReason::kOutOfFlowClipping, "outOfFlowClipping",
+ "Has clipping ancestor"},
+ {CompositingReason::kVideoOverlay, "videoOverlay",
+ "Is overlay controls for video"},
+ {CompositingReason::kWillChangeCompositingHint, "willChange",
+ "Has a will-change compositing hint"},
+ {CompositingReason::kBackdropFilter, "backdropFilter",
+ "Has a backdrop filter"},
+ {CompositingReason::kRootScroller, "rootScroller",
+ "Is the document.rootScroller"},
+ {CompositingReason::kAssumedOverlap, "assumedOverlap",
+ "Might overlap other composited content"},
+ {CompositingReason::kOverlap, "overlap",
+ "Overlaps other composited content"},
+ {CompositingReason::kNegativeZIndexChildren, "negativeZIndexChildren",
+ "Parent with composited negative z-index content"},
+ {CompositingReason::kSquashingDisallowed, "squashingDisallowed",
+ "Layer was separately composited because it could not be squashed."},
+ {CompositingReason::kTransformWithCompositedDescendants,
+ "transformWithCompositedDescendants",
+ "Has a transform that needs to be known by compositor because of "
+ "composited descendants"},
+ {CompositingReason::kOpacityWithCompositedDescendants,
+ "opacityWithCompositedDescendants",
+ "Has opacity that needs to be applied by compositor because of composited "
+ "descendants"},
+ {CompositingReason::kMaskWithCompositedDescendants,
+ "maskWithCompositedDescendants",
+ "Has a mask that needs to be known by compositor because of composited "
+ "descendants"},
+ {CompositingReason::kReflectionWithCompositedDescendants,
+ "reflectionWithCompositedDescendants",
+ "Has a reflection that needs to be known by compositor because of "
+ "composited descendants"},
+ {CompositingReason::kFilterWithCompositedDescendants,
+ "filterWithCompositedDescendants",
+ "Has a filter effect that needs to be known by compositor because of "
+ "composited descendants"},
+ {CompositingReason::kBlendingWithCompositedDescendants,
+ "blendingWithCompositedDescendants",
+ "Has a blending effect that needs to be known by compositor because of "
+ "composited descendants"},
+ {CompositingReason::kClipsCompositingDescendants,
+ "clipsCompositingDescendants",
+ "Has a clip that needs to be known by compositor because of composited "
+ "descendants"},
+ {CompositingReason::kPerspectiveWith3DDescendants,
+ "perspectiveWith3DDescendants",
+ "Has a perspective transform that needs to be known by compositor because "
+ "of 3d descendants"},
+ {CompositingReason::kPreserve3DWith3DDescendants,
+ "preserve3DWith3DDescendants",
+ "Has a preserves-3d property that needs to be known by compositor because "
+ "of 3d descendants"},
+ {CompositingReason::kReflectionOfCompositedParent,
+ "reflectionOfCompositedParent", "Is a reflection of a composited layer"},
+ {CompositingReason::kIsolateCompositedDescendants,
+ "isolateCompositedDescendants",
+ "Should isolate descendants to apply a blend effect"},
+ {CompositingReason::kPositionFixedOrStickyWithCompositedDescendants,
+ "positionFixedOrStickyWithCompositedDescendants"
+ "Is a position:fixed or position:sticky element with composited "
+ "descendants"},
+ {CompositingReason::kRoot, "root", "Is the root layer"},
+ {CompositingReason::kLayerForAncestorClip, "layerForAncestorClip",
+ "Secondary layer, applies a clip due to a sibling in the compositing "
+ "tree"},
+ {CompositingReason::kLayerForDescendantClip, "layerForDescendantClip",
+ "Secondary layer, to clip descendants of the owning layer"},
+ {CompositingReason::kLayerForPerspective, "layerForPerspective",
+ "Secondary layer, to house the perspective transform for all descendants"},
+ {CompositingReason::kLayerForHorizontalScrollbar,
+ "layerForHorizontalScrollbar",
+ "Secondary layer, the horizontal scrollbar layer"},
+ {CompositingReason::kLayerForVerticalScrollbar, "layerForVerticalScrollbar",
+ "Secondary layer, the vertical scrollbar layer"},
+ {CompositingReason::kLayerForOverflowControlsHost,
+ "layerForOverflowControlsHost",
+ "Secondary layer, the overflow controls host layer"},
+ {CompositingReason::kLayerForScrollCorner, "layerForScrollCorner",
+ "Secondary layer, the scroll corner layer"},
+ {CompositingReason::kLayerForScrollingContents, "layerForScrollingContents",
+ "Secondary layer, to house contents that can be scrolled"},
+ {CompositingReason::kLayerForScrollingContainer,
+ "layerForScrollingContainer",
+ "Secondary layer, used to position the scrolling contents while "
+ "scrolling"},
+ {CompositingReason::kLayerForSquashingContents, "layerForSquashingContents",
+ "Secondary layer, home for a group of squashable content"},
+ {CompositingReason::kLayerForSquashingContainer,
+ "layerForSquashingContainer",
+ "Secondary layer, no-op layer to place the squashing layer correctly in "
+ "the composited layer tree"},
+ {CompositingReason::kLayerForForeground, "layerForForeground",
+ "Secondary layer, to contain any normal flow and positive z-index "
+ "contents on top of a negative z-index layer"},
+ {CompositingReason::kLayerForBackground, "layerForBackground",
+ "Secondary layer, to contain acceleratable background content"},
+ {CompositingReason::kLayerForMask, "layerForMask",
+ "Secondary layer, to contain the mask contents"},
+ {CompositingReason::kLayerForClippingMask, "layerForClippingMask",
+ "Secondary layer, for clipping mask"},
+ {CompositingReason::kLayerForAncestorClippingMask,
+ "layerForAncestorClippingMask",
+ "Secondary layer, applies a clipping mask due to a sibling in the "
+ "composited layer tree"},
+ {CompositingReason::kLayerForScrollingBlockSelection,
+ "layerForScrollingBlockSelection",
+ "Secondary layer, to house block selection gaps for composited scrolling "
+ "with no scrolling contents"},
+ {CompositingReason::kLayerForDecoration, "layerForDecoration",
+ "Layer painted on top of other layers as decoration"},
+ {CompositingReason::kInlineTransform, "inlineTransform",
+ "Has an inline transform, which causes subsequent layers to assume "
+ "overlap"},
+
+};
+
+} // anonymous namespace
+
+Vector<const char*> CompositingReason::ShortNames(CompositingReasons reasons) {
+#define V(name) \
+ static_assert( \
+ CompositingReason::k##name == \
+ kCompositingReasonsStringMap[CompositingReason::kE##name].reason, \
+ "kCompositingReasonsStringMap needs update for " \
+ "CompositingReason::k" #name); \
+ FOR_EACH_COMPOSITING_REASON(V)
+#undef V
+
+ Vector<const char*> result;
+ if (reasons == kNone)
+ return result;
+ for (auto& map : kCompositingReasonsStringMap) {
+ if (reasons & map.reason)
+ result.push_back(map.short_name);
+ }
+ return result;
+}
+
+Vector<const char*> CompositingReason::Descriptions(
+ CompositingReasons reasons) {
+ Vector<const char*> result;
+ if (reasons == kNone)
+ return result;
+ for (auto& map : kCompositingReasonsStringMap) {
+ if (reasons & map.reason)
+ result.push_back(map.description);
+ }
+ return result;
+}
+
+String CompositingReason::ToString(CompositingReasons reasons) {
+ StringBuilder builder;
+ for (const char* name : ShortNames(reasons)) {
+ if (builder.length())
+ builder.Append(',');
+ builder.Append(name);
+ }
+ return builder.ToString();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositing_reasons.h b/chromium/third_party/blink/renderer/platform/graphics/compositing_reasons.h
new file mode 100644
index 00000000000..d5f9b73cad6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/compositing_reasons.h
@@ -0,0 +1,174 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITING_REASONS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITING_REASONS_H_
+
+#include <stdint.h>
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+using CompositingReasons = uint64_t;
+
+#define FOR_EACH_COMPOSITING_REASON(V) \
+ /* Intrinsic reasons that can be known right away by the layer. */ \
+ V(3DTransform) \
+ V(Video) \
+ V(Canvas) \
+ V(Plugin) \
+ V(IFrame) \
+ V(BackfaceVisibilityHidden) \
+ V(ActiveTransformAnimation) \
+ V(ActiveOpacityAnimation) \
+ V(ActiveFilterAnimation) \
+ V(ActiveBackdropFilterAnimation) \
+ V(TransitionProperty) \
+ V(ScrollDependentPosition) \
+ V(OverflowScrollingTouch) \
+ V(OverflowScrollingParent) \
+ V(OutOfFlowClipping) \
+ V(VideoOverlay) \
+ V(WillChangeCompositingHint) \
+ V(BackdropFilter) \
+ V(RootScroller) \
+ \
+ /* Overlap reasons that require knowing what's behind you in paint-order \
+ before knowing the answer. */ \
+ V(AssumedOverlap) \
+ V(Overlap) \
+ V(NegativeZIndexChildren) \
+ V(SquashingDisallowed) \
+ \
+ /* Subtree reasons that require knowing what the status of your subtree is \
+ before knowing the answer. */ \
+ V(TransformWithCompositedDescendants) \
+ V(OpacityWithCompositedDescendants) \
+ V(MaskWithCompositedDescendants) \
+ V(ReflectionWithCompositedDescendants) \
+ V(FilterWithCompositedDescendants) \
+ V(BlendingWithCompositedDescendants) \
+ V(ClipsCompositingDescendants) \
+ V(PerspectiveWith3DDescendants) \
+ V(Preserve3DWith3DDescendants) \
+ V(ReflectionOfCompositedParent) \
+ V(IsolateCompositedDescendants) \
+ V(PositionFixedOrStickyWithCompositedDescendants) \
+ \
+ /* The root layer is a special case. It may be forced to be a layer, but it \
+ also needs to be a layer if anything else in the subtree is composited. */ \
+ V(Root) \
+ \
+ /* CompositedLayerMapping internal hierarchy reasons. */ \
+ V(LayerForAncestorClip) \
+ V(LayerForDescendantClip) \
+ V(LayerForPerspective) \
+ V(LayerForHorizontalScrollbar) \
+ V(LayerForVerticalScrollbar) \
+ V(LayerForOverflowControlsHost) \
+ V(LayerForScrollCorner) \
+ V(LayerForScrollingContents) \
+ V(LayerForScrollingContainer) \
+ V(LayerForSquashingContents) \
+ V(LayerForSquashingContainer) \
+ V(LayerForForeground) \
+ V(LayerForBackground) \
+ V(LayerForMask) \
+ V(LayerForClippingMask) \
+ V(LayerForAncestorClippingMask) \
+ V(LayerForScrollingBlockSelection) \
+ /* Composited layer painted on top of all other layers as decoration. */ \
+ V(LayerForDecoration) \
+ \
+ /* Composited elements with inline transforms trigger assumed overlap so \
+ that we can update their transforms quickly. */ \
+ V(InlineTransform)
+
+class PLATFORM_EXPORT CompositingReason {
+ private:
+ // This contains ordinal values for compositing reasons and will be used to
+ // generate the compositing reason bits.
+ enum {
+#define V(name) kE##name,
+ FOR_EACH_COMPOSITING_REASON(V)
+#undef V
+ };
+
+#define V(name) static_assert(kE##name < 64, "Should fit in 64 bits");
+ FOR_EACH_COMPOSITING_REASON(V)
+#undef V
+
+ public:
+ static Vector<const char*> ShortNames(CompositingReasons);
+ static Vector<const char*> Descriptions(CompositingReasons);
+ static String ToString(CompositingReasons);
+
+ enum : CompositingReasons {
+ kNone = 0,
+ kAll = ~static_cast<CompositingReasons>(0),
+#define V(name) k##name = UINT64_C(1) << kE##name,
+ FOR_EACH_COMPOSITING_REASON(V)
+#undef V
+
+ // Various combinations of compositing reasons are defined here also, for
+ // more intuitive and faster bitwise logic.
+ kComboActiveAnimation =
+ kActiveTransformAnimation | kActiveOpacityAnimation |
+ kActiveFilterAnimation | kActiveBackdropFilterAnimation,
+
+ kComboAllDirectStyleDeterminedReasons =
+ k3DTransform | kBackfaceVisibilityHidden | kComboActiveAnimation |
+ kTransitionProperty | kWillChangeCompositingHint | kBackdropFilter,
+
+ kComboAllDirectReasons =
+ kComboAllDirectStyleDeterminedReasons | kVideo | kCanvas | kPlugin |
+ kIFrame | kScrollDependentPosition | kOverflowScrollingTouch |
+ kOverflowScrollingParent | kOutOfFlowClipping | kVideoOverlay |
+ kRootScroller,
+
+ kComboCompositedDescendants =
+ kTransformWithCompositedDescendants | kIsolateCompositedDescendants |
+ kOpacityWithCompositedDescendants | kMaskWithCompositedDescendants |
+ kFilterWithCompositedDescendants | kBlendingWithCompositedDescendants |
+ kReflectionWithCompositedDescendants | kClipsCompositingDescendants |
+ kPositionFixedOrStickyWithCompositedDescendants,
+
+ kCombo3DDescendants =
+ kPreserve3DWith3DDescendants | kPerspectiveWith3DDescendants,
+
+ kComboAllStyleDeterminedReasons = kComboAllDirectStyleDeterminedReasons |
+ kComboCompositedDescendants |
+ kCombo3DDescendants | kInlineTransform,
+
+ kComboSquashableReasons =
+ kOverlap | kAssumedOverlap | kOverflowScrollingParent,
+ };
+};
+
+// Any reasons other than overlap or assumed overlap will require the layer to
+// be separately compositing.
+inline bool RequiresCompositing(CompositingReasons reasons) {
+ return reasons & ~CompositingReason::kComboSquashableReasons;
+}
+
+// If the layer has overlap or assumed overlap, but no other reasons, then it
+// should be squashed.
+inline bool RequiresSquashing(CompositingReasons reasons) {
+ return !RequiresCompositing(reasons) &&
+ (reasons & CompositingReason::kComboSquashableReasons);
+}
+
+struct CompositingReasonsStats {
+ size_t overlap_layers = 0;
+ size_t active_animation_layers = 0;
+ size_t assumed_overlap_layers = 0;
+ size_t indirect_composited_layers = 0;
+ size_t total_composited_layers = 0;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITING_REASONS_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositor_animator.h b/chromium/third_party/blink/renderer/platform/graphics/compositor_animator.h
new file mode 100644
index 00000000000..0346e123251
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/compositor_animator.h
@@ -0,0 +1,24 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITOR_ANIMATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITOR_ANIMATOR_H_
+
+#include "third_party/blink/renderer/platform/graphics/compositor_animators_state.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT CompositorAnimator : public GarbageCollectedMixin {
+ public:
+ // Runs the animation frame callback.
+ virtual std::unique_ptr<CompositorMutatorOutputState> Mutate(
+ const CompositorMutatorInputState&) = 0;
+ virtual void Trace(blink::Visitor* visitor) {}
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITOR_ANIMATOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositor_animators_state.h b/chromium/third_party/blink/renderer/platform/graphics/compositor_animators_state.h
new file mode 100644
index 00000000000..549a1aefbe3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/compositor_animators_state.h
@@ -0,0 +1,18 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITOR_ANIMATORS_STATE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITOR_ANIMATORS_STATE_H_
+
+#include "cc/trees/layer_tree_mutator.h"
+
+namespace blink {
+
+using CompositorMutatorInputState = cc::MutatorInputState;
+
+using CompositorMutatorOutputState = cc::MutatorOutputState;
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITOR_ANIMATORS_STATE_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositor_element_id.cc b/chromium/third_party/blink/renderer/platform/graphics/compositor_element_id.cc
new file mode 100644
index 00000000000..271bf44f49c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/compositor_element_id.cc
@@ -0,0 +1,60 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/compositor_element_id.h"
+
+namespace blink {
+
+UniqueObjectId NewUniqueObjectId() {
+ static UniqueObjectId counter = 0;
+ return ++counter;
+}
+
+static CompositorElementId CreateCompositorElementId(
+ uint64_t blink_id,
+ CompositorElementIdNamespace namespace_id) {
+ DCHECK(
+ blink_id > 0 &&
+ blink_id <
+ std::numeric_limits<uint64_t>::max() /
+ static_cast<unsigned>(
+ CompositorElementIdNamespace::kMaxRepresentableNamespaceId));
+ // Shift to make room for namespace_id enum bits.
+ cc::ElementIdType id = blink_id << kCompositorNamespaceBitCount;
+ id += static_cast<uint64_t>(namespace_id);
+ return CompositorElementId(id);
+}
+
+CompositorElementId PLATFORM_EXPORT CompositorElementIdFromUniqueObjectId(
+ UniqueObjectId id,
+ CompositorElementIdNamespace namespace_id) {
+ DCHECK(namespace_id == CompositorElementIdNamespace::kPrimary ||
+ namespace_id == CompositorElementIdNamespace::kScroll ||
+ namespace_id == CompositorElementIdNamespace::kEffectFilter ||
+ namespace_id == CompositorElementIdNamespace::kEffectMask ||
+ namespace_id == CompositorElementIdNamespace::kEffectClipPath);
+ return CreateCompositorElementId(id, namespace_id);
+}
+
+CompositorElementId PLATFORM_EXPORT
+CompositorElementIdFromDOMNodeId(DOMNodeId id) {
+ return CreateCompositorElementId(
+ id, CompositorElementIdNamespace::kUniqueObjectId);
+}
+
+CompositorElementId PLATFORM_EXPORT
+CompositorElementIdFromUniqueObjectId(UniqueObjectId id) {
+ return CreateCompositorElementId(
+ id, CompositorElementIdNamespace::kUniqueObjectId);
+}
+
+CompositorElementIdNamespace NamespaceFromCompositorElementId(
+ CompositorElementId element_id) {
+ return static_cast<CompositorElementIdNamespace>(
+ element_id.ToInternalValue() %
+ static_cast<uint64_t>(
+ CompositorElementIdNamespace::kMaxRepresentableNamespaceId));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositor_element_id.h b/chromium/third_party/blink/renderer/platform/graphics/compositor_element_id.h
new file mode 100644
index 00000000000..d3c2d94c15b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/compositor_element_id.h
@@ -0,0 +1,87 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITOR_ELEMENT_ID_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITOR_ELEMENT_ID_H_
+
+#include "cc/trees/element_id.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/hash_functions.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/hash_traits.h"
+
+namespace blink {
+
+const int kCompositorNamespaceBitCount = 3;
+
+enum class CompositorElementIdNamespace {
+ kPrimary,
+ kUniqueObjectId,
+ kScroll,
+ // The following are SPv2-only.
+ kEffectFilter,
+ kEffectMask,
+ kEffectClipPath,
+ // A sentinel to indicate the maximum representable namespace id
+ // (the maximum is one less than this value).
+ kMaxRepresentableNamespaceId = 1 << kCompositorNamespaceBitCount
+};
+
+using CompositorElementId = cc::ElementId;
+using ScrollbarId = uint64_t;
+using UniqueObjectId = uint64_t;
+using DOMNodeId = uint64_t;
+using SyntheticEffectId = uint64_t;
+
+// Call this to get a globally unique object id for a newly allocated object.
+UniqueObjectId PLATFORM_EXPORT NewUniqueObjectId();
+
+// Call this with an appropriate namespace if more than one CompositorElementId
+// is required for the given UniqueObjectId.
+CompositorElementId PLATFORM_EXPORT
+ CompositorElementIdFromUniqueObjectId(UniqueObjectId,
+ CompositorElementIdNamespace);
+// ...and otherwise call this method if there is only one CompositorElementId
+// required for the given UniqueObjectId.
+CompositorElementId PLATFORM_EXPORT
+ CompositorElementIdFromUniqueObjectId(UniqueObjectId);
+
+// TODO(chrishtr): refactor ScrollState to remove this dependency.
+CompositorElementId PLATFORM_EXPORT CompositorElementIdFromDOMNodeId(DOMNodeId);
+
+// Note cc::ElementId has a hash function already implemented via
+// ElementIdHash::operator(). However for consistency's sake we choose to use
+// Blink's hash functions with Blink specific data structures.
+struct CompositorElementIdHash {
+ static unsigned GetHash(const CompositorElementId& key) {
+ return WTF::HashInt(static_cast<cc::ElementIdType>(key.ToInternalValue()));
+ }
+ static bool Equal(const CompositorElementId& a,
+ const CompositorElementId& b) {
+ return a == b;
+ }
+ static const bool safe_to_compare_to_empty_or_deleted = true;
+};
+
+CompositorElementIdNamespace PLATFORM_EXPORT
+ NamespaceFromCompositorElementId(CompositorElementId);
+
+struct CompositorElementIdHashTraits
+ : WTF::GenericHashTraits<CompositorElementId> {
+ static CompositorElementId EmptyValue() { return CompositorElementId(1); }
+ static void ConstructDeletedValue(CompositorElementId& slot, bool) {
+ slot = CompositorElementId();
+ }
+ static bool IsDeletedValue(CompositorElementId value) {
+ return value == CompositorElementId();
+ }
+};
+
+using CompositorElementIdSet = HashSet<CompositorElementId,
+ CompositorElementIdHash,
+ CompositorElementIdHashTraits>;
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITOR_ELEMENT_ID_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositor_element_id_test.cc b/chromium/third_party/blink/renderer/platform/graphics/compositor_element_id_test.cc
new file mode 100644
index 00000000000..7cd8ca3c557
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/compositor_element_id_test.cc
@@ -0,0 +1,26 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/compositor_element_id.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+class CompositorElementIdTest : public testing::Test {};
+
+uint64_t IdFromCompositorElementId(CompositorElementId element_id) {
+ return element_id.ToInternalValue() >> kCompositorNamespaceBitCount;
+}
+
+TEST_F(CompositorElementIdTest, EncodeDecode) {
+ CompositorElementId element_id = CompositorElementIdFromUniqueObjectId(1);
+ EXPECT_EQ(1u, IdFromCompositorElementId(element_id));
+
+ element_id = CompositorElementIdFromUniqueObjectId(
+ 1, CompositorElementIdNamespace::kPrimary);
+ EXPECT_EQ(1u, IdFromCompositorElementId(element_id));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositor_filter_operations.cc b/chromium/third_party/blink/renderer/platform/graphics/compositor_filter_operations.cc
new file mode 100644
index 00000000000..3689a83b474
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/compositor_filter_operations.cc
@@ -0,0 +1,139 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/compositor_filter_operations.h"
+
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/skia/include/core/SkImageFilter.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace blink {
+
+const cc::FilterOperations& CompositorFilterOperations::AsCcFilterOperations()
+ const {
+ return filter_operations_;
+}
+
+cc::FilterOperations CompositorFilterOperations::ReleaseCcFilterOperations() {
+ return std::move(filter_operations_);
+}
+
+void CompositorFilterOperations::AppendGrayscaleFilter(float amount) {
+ filter_operations_.Append(cc::FilterOperation::CreateGrayscaleFilter(amount));
+}
+
+void CompositorFilterOperations::AppendSepiaFilter(float amount) {
+ filter_operations_.Append(cc::FilterOperation::CreateSepiaFilter(amount));
+}
+
+void CompositorFilterOperations::AppendSaturateFilter(float amount) {
+ filter_operations_.Append(cc::FilterOperation::CreateSaturateFilter(amount));
+}
+
+void CompositorFilterOperations::AppendHueRotateFilter(float amount) {
+ filter_operations_.Append(cc::FilterOperation::CreateHueRotateFilter(amount));
+}
+
+void CompositorFilterOperations::AppendInvertFilter(float amount) {
+ filter_operations_.Append(cc::FilterOperation::CreateInvertFilter(amount));
+}
+
+void CompositorFilterOperations::AppendBrightnessFilter(float amount) {
+ filter_operations_.Append(
+ cc::FilterOperation::CreateBrightnessFilter(amount));
+}
+
+void CompositorFilterOperations::AppendContrastFilter(float amount) {
+ filter_operations_.Append(cc::FilterOperation::CreateContrastFilter(amount));
+}
+
+void CompositorFilterOperations::AppendOpacityFilter(float amount) {
+ filter_operations_.Append(cc::FilterOperation::CreateOpacityFilter(amount));
+}
+
+void CompositorFilterOperations::AppendBlurFilter(float amount) {
+ filter_operations_.Append(cc::FilterOperation::CreateBlurFilter(amount));
+}
+
+void CompositorFilterOperations::AppendDropShadowFilter(IntPoint offset,
+ float std_deviation,
+ Color color) {
+ gfx::Point gfx_offset(offset.X(), offset.Y());
+ filter_operations_.Append(cc::FilterOperation::CreateDropShadowFilter(
+ gfx_offset, std_deviation, color.Rgb()));
+}
+
+void CompositorFilterOperations::AppendColorMatrixFilter(
+ const cc::FilterOperation::Matrix& matrix) {
+ filter_operations_.Append(
+ cc::FilterOperation::CreateColorMatrixFilter(matrix));
+}
+
+void CompositorFilterOperations::AppendZoomFilter(float amount, int inset) {
+ filter_operations_.Append(
+ cc::FilterOperation::CreateZoomFilter(amount, inset));
+}
+
+void CompositorFilterOperations::AppendSaturatingBrightnessFilter(
+ float amount) {
+ filter_operations_.Append(
+ cc::FilterOperation::CreateSaturatingBrightnessFilter(amount));
+}
+
+void CompositorFilterOperations::AppendReferenceFilter(
+ sk_sp<PaintFilter> image_filter) {
+ filter_operations_.Append(
+ cc::FilterOperation::CreateReferenceFilter(std::move(image_filter)));
+}
+
+void CompositorFilterOperations::Clear() {
+ filter_operations_.Clear();
+}
+
+bool CompositorFilterOperations::IsEmpty() const {
+ return filter_operations_.IsEmpty();
+}
+
+FloatRect CompositorFilterOperations::MapRect(
+ const FloatRect& input_rect) const {
+ gfx::Rect result =
+ filter_operations_.MapRect(EnclosingIntRect(input_rect), SkMatrix::I());
+ return FloatRect(result.x(), result.y(), result.width(), result.height());
+}
+
+bool CompositorFilterOperations::HasFilterThatMovesPixels() const {
+ return filter_operations_.HasFilterThatMovesPixels();
+}
+
+bool CompositorFilterOperations::operator==(
+ const CompositorFilterOperations& o) const {
+ return reference_box_ == o.reference_box_ &&
+ filter_operations_ == o.filter_operations_;
+}
+
+bool CompositorFilterOperations::EqualsIgnoringReferenceFilters(
+ const CompositorFilterOperations& o) const {
+ DCHECK(!RuntimeEnabledFeatures::SlimmingPaintV175Enabled());
+ size_t size = filter_operations_.size();
+ if (size != o.filter_operations_.size() || reference_box_ != o.reference_box_)
+ return false;
+ for (size_t i = 0; i < size; ++i) {
+ const auto& operation = filter_operations_.at(i);
+ if (operation.type() == cc::FilterOperation::REFERENCE) {
+ if (o.filter_operations_.at(i).type() != cc::FilterOperation::REFERENCE)
+ return false;
+ continue;
+ }
+ if (operation != o.filter_operations_.at(i))
+ return false;
+ }
+ return true;
+}
+
+String CompositorFilterOperations::ToString() const {
+ return filter_operations_.ToString().c_str();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositor_filter_operations.h b/chromium/third_party/blink/renderer/platform/graphics/compositor_filter_operations.h
new file mode 100644
index 00000000000..e26aff40aaf
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/compositor_filter_operations.h
@@ -0,0 +1,71 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITOR_FILTER_OPERATIONS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITOR_FILTER_OPERATIONS_H_
+
+#include "cc/paint/filter_operations.h"
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/geometry/int_point.h"
+#include "third_party/blink/renderer/platform/graphics/color.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_filter.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/skia/include/core/SkScalar.h"
+
+namespace blink {
+
+// An ordered list of filter operations.
+class PLATFORM_EXPORT CompositorFilterOperations {
+ public:
+ const cc::FilterOperations& AsCcFilterOperations() const;
+ cc::FilterOperations ReleaseCcFilterOperations();
+
+ void AppendGrayscaleFilter(float amount);
+ void AppendSepiaFilter(float amount);
+ void AppendSaturateFilter(float amount);
+ void AppendHueRotateFilter(float amount);
+ void AppendInvertFilter(float amount);
+ void AppendBrightnessFilter(float amount);
+ void AppendContrastFilter(float amount);
+ void AppendOpacityFilter(float amount);
+ void AppendBlurFilter(float amount);
+ void AppendDropShadowFilter(IntPoint offset, float std_deviation, Color);
+ void AppendColorMatrixFilter(const cc::FilterOperation::Matrix&);
+ void AppendZoomFilter(float amount, int inset);
+ void AppendSaturatingBrightnessFilter(float amount);
+
+ void AppendReferenceFilter(sk_sp<PaintFilter>);
+
+ void Clear();
+ bool IsEmpty() const;
+
+ // Returns a rect covering the destination pixels that can be affected by
+ // source pixels in |inputRect|.
+ FloatRect MapRect(const FloatRect& input_rect) const;
+
+ bool HasFilterThatMovesPixels() const;
+
+ void SetReferenceBox(const FloatRect& r) { reference_box_ = r; }
+ FloatRect ReferenceBox() const { return reference_box_; }
+
+ // For reference filters, this equality operator compares pointers of the
+ // image_filter fields instead of their values.
+ bool operator==(const CompositorFilterOperations&) const;
+ bool operator!=(const CompositorFilterOperations& o) const {
+ return !(*this == o);
+ }
+
+ bool EqualsIgnoringReferenceFilters(const CompositorFilterOperations&) const;
+
+ String ToString() const;
+
+ private:
+ cc::FilterOperations filter_operations_;
+ FloatRect reference_box_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITOR_FILTER_OPERATIONS_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositor_mutator.h b/chromium/third_party/blink/renderer/platform/graphics/compositor_mutator.h
new file mode 100644
index 00000000000..5f5b3cb7c9e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/compositor_mutator.h
@@ -0,0 +1,26 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITOR_MUTATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITOR_MUTATOR_H_
+
+#include "third_party/blink/renderer/platform/graphics/compositor_animators_state.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT CompositorMutator {
+ public:
+ virtual ~CompositorMutator() = default;
+
+ // Called from compositor thread to run the animation frame callbacks from all
+ // connected AnimationWorklets.
+ virtual void Mutate(std::unique_ptr<CompositorMutatorInputState>) = 0;
+ // Returns true if Mutate may do something if called 'now'.
+ virtual bool HasAnimators() = 0;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITOR_MUTATOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositor_mutator_client.cc b/chromium/third_party/blink/renderer/platform/graphics/compositor_mutator_client.cc
new file mode 100644
index 00000000000..290f8363ded
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/compositor_mutator_client.cc
@@ -0,0 +1,47 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/compositor_mutator_client.h"
+
+#include <memory>
+#include "base/trace_event/trace_event.h"
+#include "third_party/blink/renderer/platform/graphics/compositor_mutator_impl.h"
+
+namespace blink {
+
+CompositorMutatorClient::CompositorMutatorClient(
+ std::unique_ptr<CompositorMutatorImpl> mutator)
+ : mutator_(std::move(mutator)) {
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc"),
+ "CompositorMutatorClient::CompositorMutatorClient");
+ mutator_->SetClient(this);
+}
+
+CompositorMutatorClient::~CompositorMutatorClient() {
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc"),
+ "CompositorMutatorClient::~CompositorMutatorClient");
+}
+
+void CompositorMutatorClient::Mutate(
+ std::unique_ptr<cc::MutatorInputState> input_state) {
+ TRACE_EVENT0("cc", "CompositorMutatorClient::Mutate");
+ mutator_->Mutate(std::move(input_state));
+}
+
+void CompositorMutatorClient::SetMutationUpdate(
+ std::unique_ptr<cc::MutatorOutputState> output_state) {
+ TRACE_EVENT0("cc", "CompositorMutatorClient::SetMutationUpdate");
+ client_->SetMutationUpdate(std::move(output_state));
+}
+
+void CompositorMutatorClient::SetClient(cc::LayerTreeMutatorClient* client) {
+ TRACE_EVENT0("cc", "CompositorMutatorClient::SetClient");
+ client_ = client;
+}
+
+bool CompositorMutatorClient::HasAnimators() {
+ return mutator_->HasAnimators();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositor_mutator_client.h b/chromium/third_party/blink/renderer/platform/graphics/compositor_mutator_client.h
new file mode 100644
index 00000000000..48c36549b4d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/compositor_mutator_client.h
@@ -0,0 +1,35 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITOR_MUTATOR_CLIENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITOR_MUTATOR_CLIENT_H_
+
+#include <memory>
+#include "cc/trees/layer_tree_mutator.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+
+class CompositorMutatorImpl;
+
+class PLATFORM_EXPORT CompositorMutatorClient : public cc::LayerTreeMutator {
+ public:
+ explicit CompositorMutatorClient(std::unique_ptr<CompositorMutatorImpl>);
+ virtual ~CompositorMutatorClient();
+
+ void SetMutationUpdate(std::unique_ptr<cc::MutatorOutputState>);
+
+ // cc::LayerTreeMutator
+ void SetClient(cc::LayerTreeMutatorClient*);
+ void Mutate(std::unique_ptr<cc::MutatorInputState>) override;
+ bool HasAnimators() override;
+
+ private:
+ std::unique_ptr<CompositorMutatorImpl> mutator_;
+ cc::LayerTreeMutatorClient* client_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITOR_MUTATOR_CLIENT_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositor_mutator_impl.cc b/chromium/third_party/blink/renderer/platform/graphics/compositor_mutator_impl.cc
new file mode 100644
index 00000000000..1e52422195c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/compositor_mutator_impl.cc
@@ -0,0 +1,114 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/compositor_mutator_impl.h"
+
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/graphics/compositor_animator.h"
+#include "third_party/blink/renderer/platform/graphics/compositor_mutator_client.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/waitable_event.h"
+#include "third_party/blink/renderer/platform/web_task_runner.h"
+
+namespace blink {
+
+CompositorMutatorImpl::CompositorMutatorImpl()
+ : client_(nullptr), weak_factory_(this) {
+ // By default layout tests run without threaded compositing. See
+ // https://crbug.com/770028 For these situations we run on the Main thread.
+ if (Platform::Current()->CompositorThread())
+ mutator_queue_ = Platform::Current()->CompositorThread()->GetTaskRunner();
+ else
+ mutator_queue_ = Platform::Current()->MainThread()->GetTaskRunner();
+}
+
+CompositorMutatorImpl::~CompositorMutatorImpl() {}
+
+// static
+std::unique_ptr<CompositorMutatorClient> CompositorMutatorImpl::CreateClient(
+ base::WeakPtr<CompositorMutatorImpl>* weak_interface,
+ scoped_refptr<base::SingleThreadTaskRunner>* queue) {
+ DCHECK(IsMainThread());
+ auto mutator = std::make_unique<CompositorMutatorImpl>();
+ // This is allowed since we own the class for the duration of creation.
+ *weak_interface = mutator->weak_factory_.GetWeakPtr();
+ *queue = mutator->GetTaskRunner();
+ return std::make_unique<CompositorMutatorClient>(std::move(mutator));
+}
+
+void CompositorMutatorImpl::Mutate(
+ std::unique_ptr<CompositorMutatorInputState> state) {
+ TRACE_EVENT0("cc", "CompositorMutatorImpl::mutate");
+ DCHECK(mutator_queue_->BelongsToCurrentThread());
+ if (animators_.IsEmpty())
+ return;
+ DCHECK(!animator_queue_->BelongsToCurrentThread());
+ DCHECK(client_);
+
+ using Outputs = Vector<std::unique_ptr<CompositorMutatorOutputState>>;
+ Outputs outputs;
+ outputs.ReserveInitialCapacity(animators_.size());
+
+ WaitableEvent done;
+ PostCrossThreadTask(
+ *animator_queue_, FROM_HERE,
+ CrossThreadBind(
+ [](const CompositorAnimators* animators,
+ std::unique_ptr<CompositorMutatorInputState> state,
+ std::unique_ptr<AutoSignal> completion, Outputs* output) {
+ for (CompositorAnimator* animator : *animators)
+ output->push_back(animator->Mutate(*state));
+ },
+ CrossThreadUnretained(&animators_), WTF::Passed(std::move(state)),
+ WTF::Passed(std::make_unique<AutoSignal>(&done)),
+ CrossThreadUnretained(&outputs)));
+ // At some point the AutoSignal(&done) gets destroyed and unblocks us.
+ done.Wait();
+
+ for (auto& output : outputs) {
+ client_->SetMutationUpdate(std::move(output));
+ }
+}
+
+void CompositorMutatorImpl::RegisterCompositorAnimator(
+ CrossThreadPersistent<CompositorAnimator> animator,
+ scoped_refptr<base::SingleThreadTaskRunner> queue) {
+ TRACE_EVENT0("cc", "CompositorMutatorImpl::RegisterCompositorAnimator");
+
+ DCHECK(animator);
+ DCHECK(mutator_queue_->BelongsToCurrentThread());
+ if (animators_.IsEmpty()) {
+ animator_queue_ = std::move(queue);
+ } else {
+ DCHECK_EQ(queue, animator_queue_);
+ }
+
+ animators_.insert(animator);
+}
+
+void CompositorMutatorImpl::UnregisterCompositorAnimator(
+ CrossThreadPersistent<CompositorAnimator> animator) {
+ TRACE_EVENT0("cc", "CompositorMutatorImpl::UnregisterCompositorAnimator");
+
+ DCHECK(animator);
+ DCHECK(mutator_queue_->BelongsToCurrentThread());
+
+ animators_.erase(animator);
+}
+
+bool CompositorMutatorImpl::HasAnimators() {
+ return !animators_.IsEmpty();
+}
+
+CompositorMutatorImpl::AutoSignal::AutoSignal(WaitableEvent* event)
+ : event_(event) {
+ DCHECK(event);
+}
+
+CompositorMutatorImpl::AutoSignal::~AutoSignal() {
+ event_->Signal();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositor_mutator_impl.h b/chromium/third_party/blink/renderer/platform/graphics/compositor_mutator_impl.h
new file mode 100644
index 00000000000..1e8116c43fd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/compositor_mutator_impl.h
@@ -0,0 +1,101 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITOR_MUTATOR_IMPL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITOR_MUTATOR_IMPL_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
+#include "third_party/blink/renderer/platform/graphics/compositor_animator.h"
+#include "third_party/blink/renderer/platform/graphics/compositor_mutator.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/heap/persistent.h"
+#include "third_party/blink/renderer/platform/heap/visitor.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+
+namespace blink {
+
+class CompositorMutatorClient;
+class WaitableEvent;
+
+// Fans out requests from the compositor to all of the registered
+// CompositorAnimators which can then mutate layers through their respective
+// mutate interface. Requests for animation frames are received from
+// CompositorAnimators and sent to the compositor to generate a new compositor
+// frame.
+//
+// Unless otherwise noted, this should be accessed only on the compositor
+// thread.
+class PLATFORM_EXPORT CompositorMutatorImpl final : public CompositorMutator {
+ public:
+ // There are three outputs for the two interface surfaces of the created
+ // class blob. The returned owning pointer to the Client, which
+ // also owns the rest of the structure. |mutatee| and |mutatee_runner| form a
+ // pair for referencing the CompositorMutatorImpl. i.e. Put tasks on the
+ // TaskRunner using the WeakPtr to get to the methods.
+ static std::unique_ptr<CompositorMutatorClient> CreateClient(
+ base::WeakPtr<CompositorMutatorImpl>* mutatee,
+ scoped_refptr<base::SingleThreadTaskRunner>* mutatee_runner);
+
+ CompositorMutatorImpl();
+ ~CompositorMutatorImpl();
+
+ // CompositorMutator implementation.
+ void Mutate(std::unique_ptr<CompositorMutatorInputState>) override;
+ // TODO(majidvp): Remove when timeline inputs are known.
+ bool HasAnimators() override;
+
+ // Interface for use by the AnimationWorklet Thread(s) to request calls.
+ // (To the given Animator on the given TaskRunner.)
+ void RegisterCompositorAnimator(
+ CrossThreadPersistent<CompositorAnimator>,
+ scoped_refptr<base::SingleThreadTaskRunner> animator_runner);
+
+ void UnregisterCompositorAnimator(CrossThreadPersistent<CompositorAnimator>);
+
+ void SetClient(CompositorMutatorClient* client) { client_ = client; }
+
+ private:
+ using CompositorAnimators =
+ HashSet<CrossThreadPersistent<CompositorAnimator>>;
+
+ class AutoSignal {
+ WTF_MAKE_NONCOPYABLE(AutoSignal);
+
+ public:
+ explicit AutoSignal(WaitableEvent*);
+ ~AutoSignal();
+
+ private:
+ WaitableEvent* event_;
+ };
+
+ // The AnimationWorkletProxyClientImpls are also owned by the WorkerClients
+ // dictionary.
+ CompositorAnimators animators_;
+
+ scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() {
+ return mutator_queue_;
+ }
+
+ scoped_refptr<base::SingleThreadTaskRunner> mutator_queue_;
+
+ // Currently we only support a single queue for animators.
+ scoped_refptr<base::SingleThreadTaskRunner> animator_queue_;
+
+ // The CompositorMutatorClient owns (std::unique_ptr) us, so this pointer is
+ // valid as long as this class exists.
+ CompositorMutatorClient* client_;
+
+ base::WeakPtrFactory<CompositorMutatorImpl> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(CompositorMutatorImpl);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_COMPOSITOR_MUTATOR_IMPL_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/contiguous_container.cc b/chromium/third_party/blink/renderer/platform/graphics/contiguous_container.cc
new file mode 100644
index 00000000000..ef305ab3554
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/contiguous_container.cc
@@ -0,0 +1,180 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/contiguous_container.h"
+
+#include <algorithm>
+#include <memory>
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
+#include "third_party/blink/renderer/platform/wtf/container_annotations.h"
+
+namespace blink {
+
+// Default number of max-sized elements to allocate space for, if there is no
+// initial buffer.
+static const unsigned kDefaultInitialBufferSize = 32;
+
+class ContiguousContainerBase::Buffer {
+ WTF_MAKE_NONCOPYABLE(Buffer);
+ USING_FAST_MALLOC(Buffer);
+
+ public:
+ Buffer(size_t buffer_size, const char* type_name) {
+ capacity_ = WTF::Partitions::BufferActualSize(buffer_size);
+ begin_ = end_ =
+ static_cast<char*>(WTF::Partitions::BufferMalloc(capacity_, type_name));
+ ANNOTATE_NEW_BUFFER(begin_, capacity_, 0);
+ }
+
+ ~Buffer() {
+ ANNOTATE_DELETE_BUFFER(begin_, capacity_, UsedCapacity());
+ WTF::Partitions::BufferFree(begin_);
+ }
+
+ size_t Capacity() const { return capacity_; }
+ size_t UsedCapacity() const { return end_ - begin_; }
+ size_t UnusedCapacity() const { return Capacity() - UsedCapacity(); }
+ bool IsEmpty() const { return UsedCapacity() == 0; }
+
+ void* Allocate(size_t object_size) {
+ DCHECK_GE(UnusedCapacity(), object_size);
+ ANNOTATE_CHANGE_SIZE(begin_, capacity_, UsedCapacity(),
+ UsedCapacity() + object_size);
+ void* result = end_;
+ end_ += object_size;
+ return result;
+ }
+
+ void DeallocateLastObject(void* object) {
+ CHECK_LE(begin_, object);
+ CHECK_LT(object, end_);
+ ANNOTATE_CHANGE_SIZE(begin_, capacity_, UsedCapacity(),
+ static_cast<char*>(object) - begin_);
+ end_ = static_cast<char*>(object);
+ }
+
+ private:
+ // m_begin <= m_end <= m_begin + m_capacity
+ char* begin_;
+ char* end_;
+ size_t capacity_;
+};
+
+ContiguousContainerBase::ContiguousContainerBase(size_t max_object_size)
+ : end_index_(0), max_object_size_(max_object_size) {}
+
+ContiguousContainerBase::ContiguousContainerBase(
+ ContiguousContainerBase&& source)
+ : ContiguousContainerBase(source.max_object_size_) {
+ Swap(source);
+}
+
+ContiguousContainerBase::~ContiguousContainerBase() = default;
+
+ContiguousContainerBase& ContiguousContainerBase::operator=(
+ ContiguousContainerBase&& source) {
+ Swap(source);
+ return *this;
+}
+
+size_t ContiguousContainerBase::CapacityInBytes() const {
+ size_t capacity = 0;
+ for (const auto& buffer : buffers_)
+ capacity += buffer->Capacity();
+ return capacity;
+}
+
+size_t ContiguousContainerBase::UsedCapacityInBytes() const {
+ size_t used_capacity = 0;
+ for (const auto& buffer : buffers_)
+ used_capacity += buffer->UsedCapacity();
+ return used_capacity;
+}
+
+size_t ContiguousContainerBase::MemoryUsageInBytes() const {
+ return sizeof(*this) + CapacityInBytes() +
+ elements_.capacity() * sizeof(elements_[0]);
+}
+
+void ContiguousContainerBase::ReserveInitialCapacity(size_t buffer_size,
+ const char* type_name) {
+ AllocateNewBufferForNextAllocation(buffer_size, type_name);
+}
+
+void* ContiguousContainerBase::Allocate(size_t object_size,
+ const char* type_name) {
+ DCHECK_LE(object_size, max_object_size_);
+
+ Buffer* buffer_for_alloc = nullptr;
+ if (!buffers_.IsEmpty()) {
+ Buffer* end_buffer = buffers_[end_index_].get();
+ if (end_buffer->UnusedCapacity() >= object_size)
+ buffer_for_alloc = end_buffer;
+ else if (end_index_ + 1 < buffers_.size())
+ buffer_for_alloc = buffers_[++end_index_].get();
+ }
+
+ if (!buffer_for_alloc) {
+ size_t new_buffer_size = buffers_.IsEmpty()
+ ? kDefaultInitialBufferSize * max_object_size_
+ : 2 * buffers_.back()->Capacity();
+ buffer_for_alloc =
+ AllocateNewBufferForNextAllocation(new_buffer_size, type_name);
+ }
+
+ void* element = buffer_for_alloc->Allocate(object_size);
+ elements_.push_back(element);
+ return element;
+}
+
+void ContiguousContainerBase::RemoveLast() {
+ void* object = elements_.back();
+ elements_.pop_back();
+
+ Buffer* end_buffer = buffers_[end_index_].get();
+ end_buffer->DeallocateLastObject(object);
+
+ if (end_buffer->IsEmpty()) {
+ if (end_index_ > 0)
+ end_index_--;
+ if (end_index_ + 2 < buffers_.size())
+ buffers_.pop_back();
+ }
+}
+
+void ContiguousContainerBase::Clear() {
+ elements_.clear();
+ buffers_.clear();
+ end_index_ = 0;
+}
+
+void ContiguousContainerBase::Swap(ContiguousContainerBase& other) {
+ elements_.swap(other.elements_);
+ buffers_.swap(other.buffers_);
+ std::swap(end_index_, other.end_index_);
+ std::swap(max_object_size_, other.max_object_size_);
+}
+
+void ContiguousContainerBase::ShrinkToFit() {
+ while (end_index_ < buffers_.size() - 1) {
+ DCHECK(buffers_.back()->IsEmpty());
+ buffers_.pop_back();
+ }
+}
+
+ContiguousContainerBase::Buffer*
+ContiguousContainerBase::AllocateNewBufferForNextAllocation(
+ size_t buffer_size,
+ const char* type_name) {
+ DCHECK(buffers_.IsEmpty() || end_index_ == buffers_.size() - 1);
+ std::unique_ptr<Buffer> new_buffer =
+ std::make_unique<Buffer>(buffer_size, type_name);
+ Buffer* buffer_to_return = new_buffer.get();
+ buffers_.push_back(std::move(new_buffer));
+ end_index_ = buffers_.size() - 1;
+ return buffer_to_return;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/contiguous_container.h b/chromium/third_party/blink/renderer/platform/graphics/contiguous_container.h
new file mode 100644
index 00000000000..6ad356600ac
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/contiguous_container.h
@@ -0,0 +1,254 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CONTIGUOUS_CONTAINER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CONTIGUOUS_CONTAINER_H_
+
+#include <cstddef>
+#include <iterator>
+#include <memory>
+#include <utility>
+#include "base/compiler_specific.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/alignment.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/compiler.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/type_traits.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+// ContiguousContainer is a container which stores a list of heterogeneous
+// objects (in particular, of varying sizes), packed next to one another in
+// memory. Objects are never relocated, so it is safe to store pointers to them
+// for the lifetime of the container (unless the object is removed).
+//
+// Memory is allocated in a series of buffers (with exponential growth). When an
+// object is allocated, it is given only the space it requires (possibly with
+// enough padding to preserve alignment), rather than the maximum possible size.
+// This allows small and large objects to coexist without wasting much space.
+//
+// Since it stores pointers to all of the objects it allocates in a vector, it
+// supports efficient iteration and indexing. However, for mutation the
+// supported operations are limited to appending to, and removing from, the end
+// of the list.
+//
+// Clients should instantiate ContiguousContainer; ContiguousContainerBase is an
+// artifact of the implementation.
+
+class PLATFORM_EXPORT ContiguousContainerBase {
+ DISALLOW_NEW();
+ WTF_MAKE_NONCOPYABLE(ContiguousContainerBase);
+
+ protected:
+ explicit ContiguousContainerBase(size_t max_object_size);
+ ContiguousContainerBase(ContiguousContainerBase&&);
+ ~ContiguousContainerBase();
+
+ ContiguousContainerBase& operator=(ContiguousContainerBase&&);
+
+ size_t size() const { return elements_.size(); }
+ bool IsEmpty() const { return !size(); }
+ size_t CapacityInBytes() const;
+ size_t UsedCapacityInBytes() const;
+ size_t MemoryUsageInBytes() const;
+
+ // These do not invoke constructors or destructors.
+ void ReserveInitialCapacity(size_t, const char* type_name);
+ void* Allocate(size_t object_size, const char* type_name);
+ void RemoveLast();
+ void Clear();
+ void Swap(ContiguousContainerBase&);
+
+ // Discards excess buffer capacity. Intended for use when no more appending
+ // is anticipated.
+ void ShrinkToFit();
+
+ Vector<void*> elements_;
+
+ private:
+ class Buffer;
+
+ Buffer* AllocateNewBufferForNextAllocation(size_t, const char* type_name);
+
+ Vector<std::unique_ptr<Buffer>> buffers_;
+ unsigned end_index_;
+ size_t max_object_size_;
+};
+
+// For most cases, no alignment stricter than pointer alignment is required. If
+// one of the derived classes has stronger alignment requirements (and the
+// static_assert fires), set alignment to the LCM of the derived class
+// alignments. For small structs without pointers, it may be possible to reduce
+// alignment for tighter packing.
+
+template <class BaseElementType, unsigned alignment = sizeof(void*)>
+class ContiguousContainer : public ContiguousContainerBase {
+ private:
+ // Declares itself as a forward iterator, but also supports a few more
+ // things. The whole random access iterator interface is a bit much.
+ template <typename BaseIterator, typename ValueType>
+ class IteratorWrapper
+ : public std::iterator<std::forward_iterator_tag, ValueType> {
+ DISALLOW_NEW();
+
+ public:
+ IteratorWrapper() = default;
+ bool operator==(const IteratorWrapper& other) const {
+ return it_ == other.it_;
+ }
+ bool operator!=(const IteratorWrapper& other) const {
+ return it_ != other.it_;
+ }
+ ValueType& operator*() const { return *static_cast<ValueType*>(*it_); }
+ ValueType* operator->() const { return &operator*(); }
+ IteratorWrapper operator+(std::ptrdiff_t n) const {
+ return IteratorWrapper(it_ + n);
+ }
+ IteratorWrapper operator++(int) {
+ IteratorWrapper tmp = *this;
+ ++it_;
+ return tmp;
+ }
+ std::ptrdiff_t operator-(const IteratorWrapper& other) const {
+ return it_ - other.it_;
+ }
+ IteratorWrapper& operator++() {
+ ++it_;
+ return *this;
+ }
+
+ private:
+ explicit IteratorWrapper(const BaseIterator& it) : it_(it) {}
+ BaseIterator it_;
+ friend class ContiguousContainer;
+ };
+
+ public:
+ using iterator = IteratorWrapper<Vector<void*>::iterator, BaseElementType>;
+ using const_iterator =
+ IteratorWrapper<Vector<void*>::const_iterator, const BaseElementType>;
+ using reverse_iterator =
+ IteratorWrapper<Vector<void*>::reverse_iterator, BaseElementType>;
+ using const_reverse_iterator =
+ IteratorWrapper<Vector<void*>::const_reverse_iterator,
+ const BaseElementType>;
+
+ explicit ContiguousContainer(size_t max_object_size)
+ : ContiguousContainerBase(Align(max_object_size)) {}
+
+ ContiguousContainer(size_t max_object_size, size_t initial_size_bytes)
+ : ContiguousContainer(max_object_size) {
+ ReserveInitialCapacity(std::max(max_object_size, initial_size_bytes),
+ WTF_HEAP_PROFILER_TYPE_NAME(BaseElementType));
+ }
+
+ ContiguousContainer(ContiguousContainer&& source)
+ : ContiguousContainerBase(std::move(source)) {}
+
+ ~ContiguousContainer() {
+ for (auto& element : *this) {
+ (void)element; // MSVC incorrectly reports this variable as unused.
+ element.~BaseElementType();
+ }
+ }
+
+ ContiguousContainer& operator=(ContiguousContainer&& source) {
+ // Must clear in the derived class to ensure that element destructors
+ // care called.
+ Clear();
+
+ ContiguousContainerBase::operator=(std::move(source));
+ return *this;
+ }
+
+ using ContiguousContainerBase::size;
+ using ContiguousContainerBase::IsEmpty;
+ using ContiguousContainerBase::CapacityInBytes;
+ using ContiguousContainerBase::UsedCapacityInBytes;
+ using ContiguousContainerBase::MemoryUsageInBytes;
+ using ContiguousContainerBase::ShrinkToFit;
+
+ iterator begin() { return iterator(elements_.begin()); }
+ iterator end() { return iterator(elements_.end()); }
+ const_iterator begin() const { return const_iterator(elements_.begin()); }
+ const_iterator end() const { return const_iterator(elements_.end()); }
+ reverse_iterator rbegin() { return reverse_iterator(elements_.rbegin()); }
+ reverse_iterator rend() { return reverse_iterator(elements_.rend()); }
+ const_reverse_iterator rbegin() const {
+ return const_reverse_iterator(elements_.rbegin());
+ }
+ const_reverse_iterator rend() const {
+ return const_reverse_iterator(elements_.rend());
+ }
+
+ BaseElementType& First() { return *begin(); }
+ const BaseElementType& First() const { return *begin(); }
+ BaseElementType& Last() { return *rbegin(); }
+ const BaseElementType& Last() const { return *rbegin(); }
+ BaseElementType& operator[](size_t index) { return *(begin() + index); }
+ const BaseElementType& operator[](size_t index) const {
+ return *(begin() + index);
+ }
+
+ template <class DerivedElementType, typename... Args>
+ DerivedElementType& AllocateAndConstruct(Args&&... args) {
+ static_assert(WTF::IsSubclass<DerivedElementType, BaseElementType>::value,
+ "Must use subclass of BaseElementType.");
+ static_assert(alignment % WTF_ALIGN_OF(DerivedElementType) == 0,
+ "Derived type requires stronger alignment.");
+ return *new (AlignedAllocate(sizeof(DerivedElementType)))
+ DerivedElementType(std::forward<Args>(args)...);
+ }
+
+ void RemoveLast() {
+ DCHECK(!IsEmpty());
+ Last().~BaseElementType();
+ ContiguousContainerBase::RemoveLast();
+ }
+
+ DISABLE_CFI_PERF
+ void Clear() {
+ for (auto& element : *this) {
+ (void)element; // MSVC incorrectly reports this variable as unused.
+ element.~BaseElementType();
+ }
+ ContiguousContainerBase::Clear();
+ }
+
+ void Swap(ContiguousContainer& other) {
+ ContiguousContainerBase::Swap(other);
+ }
+
+ // Appends a new element using memcpy, then default-constructs a base
+ // element in its place. Use with care.
+ BaseElementType& AppendByMoving(BaseElementType& item, size_t size) {
+ DCHECK_GE(size, sizeof(BaseElementType));
+ void* new_item = AlignedAllocate(size);
+ memcpy(new_item, static_cast<void*>(&item), size);
+ new (&item) BaseElementType;
+ return *static_cast<BaseElementType*>(new_item);
+ }
+
+ private:
+ void* AlignedAllocate(size_t size) {
+ void* result = ContiguousContainerBase::Allocate(
+ Align(size), WTF_HEAP_PROFILER_TYPE_NAME(BaseElementType));
+ DCHECK_EQ(reinterpret_cast<intptr_t>(result) & (alignment - 1), 0u);
+ return result;
+ }
+
+ static size_t Align(size_t size) {
+ size_t aligned_size = alignment * ((size + alignment - 1) / alignment);
+ DCHECK_EQ(aligned_size % alignment, 0u);
+ DCHECK_GE(aligned_size, size);
+ DCHECK_LT(aligned_size, size + alignment);
+ return aligned_size;
+ }
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CONTIGUOUS_CONTAINER_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/contiguous_container_test.cc b/chromium/third_party/blink/renderer/platform/graphics/contiguous_container_test.cc
new file mode 100644
index 00000000000..8530f5c26ae
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/contiguous_container_test.cc
@@ -0,0 +1,542 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/contiguous_container.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/type_traits.h"
+
+namespace blink {
+namespace {
+
+struct Point2D {
+ Point2D() : Point2D(0, 0) {}
+ Point2D(int x, int y) : x(x), y(y) {}
+ int x, y;
+};
+
+struct Point3D : public Point2D {
+ Point3D() : Point3D(0, 0, 0) {}
+ Point3D(int x, int y, int z) : Point2D(x, y), z(z) {}
+ int z;
+};
+
+// Maximum size of a subclass of Point2D.
+static const size_t kMaxPointSize = sizeof(Point3D);
+
+// Alignment for Point2D and its subclasses.
+static const size_t kPointAlignment = sizeof(int);
+
+// How many elements to use for tests with "plenty" of elements.
+static const unsigned kNumElements = 150;
+
+TEST(ContiguousContainerTest, SimpleStructs) {
+ ContiguousContainer<Point2D, kPointAlignment> list(kMaxPointSize);
+ list.AllocateAndConstruct<Point2D>(1, 2);
+ list.AllocateAndConstruct<Point3D>(3, 4, 5);
+ list.AllocateAndConstruct<Point2D>(6, 7);
+
+ ASSERT_EQ(3u, list.size());
+ EXPECT_EQ(1, list[0].x);
+ EXPECT_EQ(2, list[0].y);
+ EXPECT_EQ(3, list[1].x);
+ EXPECT_EQ(4, list[1].y);
+ EXPECT_EQ(5, static_cast<Point3D&>(list[1]).z);
+ EXPECT_EQ(6, list[2].x);
+ EXPECT_EQ(7, list[2].y);
+}
+
+TEST(ContiguousContainerTest, AllocateLots) {
+ ContiguousContainer<Point2D, kPointAlignment> list(kMaxPointSize);
+ for (int i = 0; i < (int)kNumElements; i++) {
+ list.AllocateAndConstruct<Point2D>(i, i);
+ list.AllocateAndConstruct<Point2D>(i, i);
+ list.RemoveLast();
+ }
+ ASSERT_EQ(kNumElements, list.size());
+ for (int i = 0; i < (int)kNumElements; i++) {
+ ASSERT_EQ(i, list[i].x);
+ ASSERT_EQ(i, list[i].y);
+ }
+}
+
+class MockDestructible {
+ public:
+ ~MockDestructible() { Destruct(); }
+ MOCK_METHOD0(Destruct, void());
+};
+
+TEST(ContiguousContainerTest, DestructorCalled) {
+ ContiguousContainer<MockDestructible> list(sizeof(MockDestructible));
+ auto& destructible = list.AllocateAndConstruct<MockDestructible>();
+ EXPECT_EQ(&destructible, &list.First());
+ EXPECT_CALL(destructible, Destruct());
+}
+
+TEST(ContiguousContainerTest, DestructorCalledOnceWhenClear) {
+ ContiguousContainer<MockDestructible> list(sizeof(MockDestructible));
+ auto& destructible = list.AllocateAndConstruct<MockDestructible>();
+ EXPECT_EQ(&destructible, &list.First());
+
+ testing::MockFunction<void()> separator;
+ {
+ testing::InSequence s;
+ EXPECT_CALL(destructible, Destruct());
+ EXPECT_CALL(separator, Call());
+ EXPECT_CALL(destructible, Destruct()).Times(0);
+ }
+
+ list.Clear();
+ separator.Call();
+}
+
+TEST(ContiguousContainerTest, DestructorCalledOnceWhenRemoveLast) {
+ ContiguousContainer<MockDestructible> list(sizeof(MockDestructible));
+ auto& destructible = list.AllocateAndConstruct<MockDestructible>();
+ EXPECT_EQ(&destructible, &list.First());
+
+ testing::MockFunction<void()> separator;
+ {
+ testing::InSequence s;
+ EXPECT_CALL(destructible, Destruct());
+ EXPECT_CALL(separator, Call());
+ EXPECT_CALL(destructible, Destruct()).Times(0);
+ }
+
+ list.RemoveLast();
+ separator.Call();
+}
+
+TEST(ContiguousContainerTest, DestructorCalledWithMultipleRemoveLastCalls) {
+ // This container only requests space for one, but the implementation is
+ // free to use more space if the allocator provides it.
+ ContiguousContainer<MockDestructible> list(sizeof(MockDestructible),
+ 1 * sizeof(MockDestructible));
+ testing::MockFunction<void()> separator;
+
+ // We should be okay to allocate and remove a single one, like before.
+ list.AllocateAndConstruct<MockDestructible>();
+ EXPECT_EQ(1u, list.size());
+ {
+ testing::InSequence s;
+ EXPECT_CALL(list[0], Destruct());
+ EXPECT_CALL(separator, Call());
+ EXPECT_CALL(list[0], Destruct()).Times(0);
+ }
+ list.RemoveLast();
+ separator.Call();
+ EXPECT_EQ(0u, list.size());
+
+ testing::Mock::VerifyAndClearExpectations(&separator);
+
+ // We should also be okay to allocate and remove multiple.
+ list.AllocateAndConstruct<MockDestructible>();
+ list.AllocateAndConstruct<MockDestructible>();
+ list.AllocateAndConstruct<MockDestructible>();
+ list.AllocateAndConstruct<MockDestructible>();
+ list.AllocateAndConstruct<MockDestructible>();
+ list.AllocateAndConstruct<MockDestructible>();
+ EXPECT_EQ(6u, list.size());
+ {
+ // The last three should be destroyed by removeLast.
+ testing::InSequence s;
+ EXPECT_CALL(list[5], Destruct());
+ EXPECT_CALL(separator, Call());
+ EXPECT_CALL(list[5], Destruct()).Times(0);
+ EXPECT_CALL(list[4], Destruct());
+ EXPECT_CALL(separator, Call());
+ EXPECT_CALL(list[4], Destruct()).Times(0);
+ EXPECT_CALL(list[3], Destruct());
+ EXPECT_CALL(separator, Call());
+ EXPECT_CALL(list[3], Destruct()).Times(0);
+ }
+ list.RemoveLast();
+ separator.Call();
+ list.RemoveLast();
+ separator.Call();
+ list.RemoveLast();
+ separator.Call();
+ EXPECT_EQ(3u, list.size());
+
+ // The remaining ones are destroyed when the test finishes.
+ EXPECT_CALL(list[2], Destruct());
+ EXPECT_CALL(list[1], Destruct());
+ EXPECT_CALL(list[0], Destruct());
+}
+
+TEST(ContiguousContainerTest, InsertionAndIndexedAccess) {
+ ContiguousContainer<Point2D, kPointAlignment> list(kMaxPointSize);
+
+ auto& point1 = list.AllocateAndConstruct<Point2D>();
+ auto& point2 = list.AllocateAndConstruct<Point2D>();
+ auto& point3 = list.AllocateAndConstruct<Point2D>();
+
+ EXPECT_EQ(3u, list.size());
+ EXPECT_EQ(&point1, &list.First());
+ EXPECT_EQ(&point3, &list.Last());
+ EXPECT_EQ(&point1, &list[0]);
+ EXPECT_EQ(&point2, &list[1]);
+ EXPECT_EQ(&point3, &list[2]);
+}
+
+TEST(ContiguousContainerTest, InsertionAndClear) {
+ ContiguousContainer<Point2D, kPointAlignment> list(kMaxPointSize);
+ EXPECT_TRUE(list.IsEmpty());
+ EXPECT_EQ(0u, list.size());
+
+ list.AllocateAndConstruct<Point2D>();
+ EXPECT_FALSE(list.IsEmpty());
+ EXPECT_EQ(1u, list.size());
+
+ list.Clear();
+ EXPECT_TRUE(list.IsEmpty());
+ EXPECT_EQ(0u, list.size());
+
+ list.AllocateAndConstruct<Point2D>();
+ EXPECT_FALSE(list.IsEmpty());
+ EXPECT_EQ(1u, list.size());
+}
+
+TEST(ContiguousContainerTest, ElementAddressesAreStable) {
+ ContiguousContainer<Point2D, kPointAlignment> list(kMaxPointSize);
+ Vector<Point2D*> pointers;
+ for (int i = 0; i < (int)kNumElements; i++)
+ pointers.push_back(&list.AllocateAndConstruct<Point2D>());
+ EXPECT_EQ(kNumElements, list.size());
+ EXPECT_EQ(kNumElements, pointers.size());
+
+ auto list_it = list.begin();
+ auto vector_it = pointers.begin();
+ for (; list_it != list.end(); ++list_it, ++vector_it)
+ EXPECT_EQ(&*list_it, *vector_it);
+}
+
+TEST(ContiguousContainerTest, ForwardIteration) {
+ ContiguousContainer<Point2D, kPointAlignment> list(kMaxPointSize);
+ for (int i = 0; i < (int)kNumElements; i++)
+ list.AllocateAndConstruct<Point2D>(i, i);
+ unsigned count = 0;
+ for (Point2D& point : list) {
+ EXPECT_EQ((int)count, point.x);
+ count++;
+ }
+ EXPECT_EQ(kNumElements, count);
+
+ static_assert(std::is_same<decltype(*list.begin()), Point2D&>::value,
+ "Non-const iteration should produce non-const references.");
+}
+
+TEST(ContiguousContainerTest, ConstForwardIteration) {
+ ContiguousContainer<Point2D, kPointAlignment> list(kMaxPointSize);
+ for (int i = 0; i < (int)kNumElements; i++)
+ list.AllocateAndConstruct<Point2D>(i, i);
+
+ const auto& const_list = list;
+ unsigned count = 0;
+ for (const Point2D& point : const_list) {
+ EXPECT_EQ((int)count, point.x);
+ count++;
+ }
+ EXPECT_EQ(kNumElements, count);
+
+ static_assert(
+ std::is_same<decltype(*const_list.begin()), const Point2D&>::value,
+ "Const iteration should produce const references.");
+}
+
+TEST(ContiguousContainerTest, ReverseIteration) {
+ ContiguousContainer<Point2D, kPointAlignment> list(kMaxPointSize);
+ for (int i = 0; i < (int)kNumElements; i++)
+ list.AllocateAndConstruct<Point2D>(i, i);
+
+ unsigned count = 0;
+ for (auto it = list.rbegin(); it != list.rend(); ++it) {
+ EXPECT_EQ((int)(kNumElements - 1 - count), it->x);
+ count++;
+ }
+ EXPECT_EQ(kNumElements, count);
+
+ static_assert(std::is_same<decltype(*list.rbegin()), Point2D&>::value,
+ "Non-const iteration should produce non-const references.");
+}
+
+// Checks that the latter list has pointers to the elements of the former.
+template <typename It1, typename It2>
+bool EqualPointers(It1 it1, const It1& end1, It2 it2) {
+ for (; it1 != end1; ++it1, ++it2) {
+ if (&*it1 != *it2)
+ return false;
+ }
+ return true;
+}
+
+TEST(ContiguousContainerTest, IterationAfterRemoveLast) {
+ struct SmallStruct {
+ char dummy[16];
+ };
+ ContiguousContainer<SmallStruct> list(sizeof(SmallStruct),
+ 1 * sizeof(SmallStruct));
+ Vector<SmallStruct*> pointers;
+
+ // Utilities which keep these two lists in sync and check that their
+ // iteration order matches.
+ auto push = [&list, &pointers]() {
+ pointers.push_back(&list.AllocateAndConstruct<SmallStruct>());
+ };
+ auto pop = [&list, &pointers]() {
+ pointers.pop_back();
+ list.RemoveLast();
+ };
+ auto check_equal = [&list, &pointers]() {
+ // They should be of the same size, and compare equal with all four
+ // kinds of iteration.
+ const auto& const_list = list;
+ const auto& const_pointers = pointers;
+ ASSERT_EQ(list.size(), pointers.size());
+ ASSERT_TRUE(EqualPointers(list.begin(), list.end(), pointers.begin()));
+ ASSERT_TRUE(EqualPointers(const_list.begin(), const_list.end(),
+ const_pointers.begin()));
+ ASSERT_TRUE(EqualPointers(list.rbegin(), list.rend(), pointers.rbegin()));
+ ASSERT_TRUE(EqualPointers(const_list.rbegin(), const_list.rend(),
+ const_pointers.rbegin()));
+ };
+
+ // Note that the allocations that actually happen may not match the
+ // idealized descriptions here, since the implementation takes advantage of
+ // space available in the underlying allocator.
+ check_equal(); // Initially empty.
+ push();
+ check_equal(); // One full inner list.
+ push();
+ check_equal(); // One full, one partially full.
+ push();
+ push();
+ check_equal(); // Two full, one partially full.
+ pop();
+ check_equal(); // Two full, one empty.
+ pop();
+ check_equal(); // One full, one partially full, one empty.
+ pop();
+ check_equal(); // One full, one empty.
+ push();
+ pop();
+ pop();
+ ASSERT_TRUE(list.IsEmpty());
+ check_equal(); // Empty.
+}
+
+TEST(ContiguousContainerTest, AppendByMovingSameList) {
+ ContiguousContainer<Point2D, kPointAlignment> list(kMaxPointSize);
+ list.AllocateAndConstruct<Point3D>(1, 2, 3);
+
+ // Moves the Point3D to the end, and default-constructs a Point2D in its
+ // place.
+ list.AppendByMoving(list.First(), sizeof(Point3D));
+ EXPECT_EQ(1, list.Last().x);
+ EXPECT_EQ(2, list.Last().y);
+ EXPECT_EQ(3, static_cast<const Point3D&>(list.Last()).z);
+ EXPECT_EQ(2u, list.size());
+
+ // Moves that Point2D to the end, and default-constructs another in its
+ // place.
+ list.First().x = 4;
+ list.AppendByMoving(list.First(), sizeof(Point2D));
+ EXPECT_EQ(4, list.Last().x);
+ EXPECT_EQ(3u, list.size());
+}
+
+TEST(ContiguousContainerTest, AppendByMovingDoesNotDestruct) {
+ // GMock mock objects (e.g. MockDestructible) aren't guaranteed to be safe
+ // to memcpy (which is required for appendByMoving).
+ class DestructionNotifier {
+ public:
+ DestructionNotifier(bool* flag = nullptr) : flag_(flag) {}
+ ~DestructionNotifier() {
+ if (flag_)
+ *flag_ = true;
+ }
+
+ private:
+ bool* flag_;
+ };
+
+ bool destroyed = false;
+ ContiguousContainer<DestructionNotifier> list1(sizeof(DestructionNotifier));
+ list1.AllocateAndConstruct<DestructionNotifier>(&destroyed);
+ {
+ // Make sure destructor isn't called during appendByMoving.
+ ContiguousContainer<DestructionNotifier> list2(sizeof(DestructionNotifier));
+ list2.AppendByMoving(list1.Last(), sizeof(DestructionNotifier));
+ EXPECT_FALSE(destroyed);
+ }
+ // But it should be destroyed when list2 is.
+ EXPECT_TRUE(destroyed);
+}
+
+TEST(ContiguousContainerTest, AppendByMovingReturnsMovedPointer) {
+ ContiguousContainer<Point2D, kPointAlignment> list1(kMaxPointSize);
+ ContiguousContainer<Point2D, kPointAlignment> list2(kMaxPointSize);
+
+ Point2D& point = list1.AllocateAndConstruct<Point2D>();
+ Point2D& moved_point1 = list2.AppendByMoving(point, sizeof(Point2D));
+ EXPECT_EQ(&moved_point1, &list2.Last());
+
+ Point2D& moved_point2 = list1.AppendByMoving(moved_point1, sizeof(Point2D));
+ EXPECT_EQ(&moved_point2, &list1.Last());
+ EXPECT_NE(&moved_point1, &moved_point2);
+}
+
+TEST(ContiguousContainerTest, AppendByMovingReplacesSourceWithNewElement) {
+ ContiguousContainer<Point2D, kPointAlignment> list1(kMaxPointSize);
+ ContiguousContainer<Point2D, kPointAlignment> list2(kMaxPointSize);
+
+ list1.AllocateAndConstruct<Point2D>(1, 2);
+ EXPECT_EQ(1, list1.First().x);
+ EXPECT_EQ(2, list1.First().y);
+
+ list2.AppendByMoving(list1.First(), sizeof(Point2D));
+ EXPECT_EQ(0, list1.First().x);
+ EXPECT_EQ(0, list1.First().y);
+ EXPECT_EQ(1, list2.First().x);
+ EXPECT_EQ(2, list2.First().y);
+
+ EXPECT_EQ(1u, list1.size());
+ EXPECT_EQ(1u, list2.size());
+}
+
+TEST(ContiguousContainerTest, AppendByMovingElementsOfDifferentSizes) {
+ ContiguousContainer<Point2D, kPointAlignment> list(kMaxPointSize);
+ list.AllocateAndConstruct<Point3D>(1, 2, 3);
+ list.AllocateAndConstruct<Point2D>(4, 5);
+
+ EXPECT_EQ(1, list[0].x);
+ EXPECT_EQ(2, list[0].y);
+ EXPECT_EQ(3, static_cast<const Point3D&>(list[0]).z);
+ EXPECT_EQ(4, list[1].x);
+ EXPECT_EQ(5, list[1].y);
+
+ // Test that moving the first element actually moves the entire object, not
+ // just the base element.
+ list.AppendByMoving(list[0], sizeof(Point3D));
+ EXPECT_EQ(1, list[2].x);
+ EXPECT_EQ(2, list[2].y);
+ EXPECT_EQ(3, static_cast<const Point3D&>(list[2]).z);
+ EXPECT_EQ(4, list[1].x);
+ EXPECT_EQ(5, list[1].y);
+
+ list.AppendByMoving(list[1], sizeof(Point2D));
+ EXPECT_EQ(1, list[2].x);
+ EXPECT_EQ(2, list[2].y);
+ EXPECT_EQ(3, static_cast<const Point3D&>(list[2]).z);
+ EXPECT_EQ(4, list[3].x);
+ EXPECT_EQ(5, list[3].y);
+}
+
+TEST(ContiguousContainerTest, Swap) {
+ ContiguousContainer<Point2D, kPointAlignment> list1(kMaxPointSize);
+ list1.AllocateAndConstruct<Point2D>(1, 2);
+ ContiguousContainer<Point2D, kPointAlignment> list2(kMaxPointSize);
+ list2.AllocateAndConstruct<Point2D>(3, 4);
+ list2.AllocateAndConstruct<Point2D>(5, 6);
+
+ EXPECT_EQ(1u, list1.size());
+ EXPECT_EQ(1, list1[0].x);
+ EXPECT_EQ(2, list1[0].y);
+ EXPECT_EQ(2u, list2.size());
+ EXPECT_EQ(3, list2[0].x);
+ EXPECT_EQ(4, list2[0].y);
+ EXPECT_EQ(5, list2[1].x);
+ EXPECT_EQ(6, list2[1].y);
+
+ list2.Swap(list1);
+
+ EXPECT_EQ(1u, list2.size());
+ EXPECT_EQ(1, list2[0].x);
+ EXPECT_EQ(2, list2[0].y);
+ EXPECT_EQ(2u, list1.size());
+ EXPECT_EQ(3, list1[0].x);
+ EXPECT_EQ(4, list1[0].y);
+ EXPECT_EQ(5, list1[1].x);
+ EXPECT_EQ(6, list1[1].y);
+}
+
+TEST(ContiguousContainerTest, CapacityInBytes) {
+ const int kIterations = 500;
+ const size_t kInitialCapacity = 10 * kMaxPointSize;
+ const size_t kUpperBoundOnMinCapacity = kInitialCapacity;
+
+ // At time of writing, removing elements from the end can cause up to 7x the
+ // memory required to be consumed, in the worst case, since we can have up to
+ // two trailing inner lists that are empty (for 2*size + 4*size in unused
+ // memory, due to the exponential growth strategy).
+ // Unfortunately, this captures behaviour of the underlying allocator as
+ // well as this container, so we're pretty loose here. This constant may
+ // need to be adjusted.
+ const size_t kMaxWasteFactor = 8;
+
+ ContiguousContainer<Point2D, kPointAlignment> list(kMaxPointSize,
+ kInitialCapacity);
+
+ // The capacity should grow with the list.
+ for (int i = 0; i < kIterations; i++) {
+ size_t capacity = list.CapacityInBytes();
+ ASSERT_GE(capacity, list.size() * sizeof(Point2D));
+ ASSERT_LE(capacity, std::max(list.size() * sizeof(Point2D),
+ kUpperBoundOnMinCapacity) *
+ kMaxWasteFactor);
+ list.AllocateAndConstruct<Point2D>();
+ }
+
+ // The capacity should shrink with the list.
+ for (int i = 0; i < kIterations; i++) {
+ size_t capacity = list.CapacityInBytes();
+ ASSERT_GE(capacity, list.size() * sizeof(Point2D));
+ ASSERT_LE(capacity, std::max(list.size() * sizeof(Point2D),
+ kUpperBoundOnMinCapacity) *
+ kMaxWasteFactor);
+ list.RemoveLast();
+ }
+}
+
+TEST(ContiguousContainerTest, CapacityInBytesAfterClear) {
+ // Clearing should restore the capacity of the container to the same as a
+ // newly allocated one (without reserved capacity requested).
+ ContiguousContainer<Point2D, kPointAlignment> list(kMaxPointSize);
+ size_t empty_capacity = list.CapacityInBytes();
+ list.AllocateAndConstruct<Point2D>();
+ list.AllocateAndConstruct<Point2D>();
+ list.Clear();
+ EXPECT_EQ(empty_capacity, list.CapacityInBytes());
+}
+
+TEST(ContiguousContainerTest, Alignment) {
+ const size_t kMaxAlign = WTF_ALIGN_OF(long double);
+ ContiguousContainer<Point2D, kMaxAlign> list(kMaxPointSize);
+
+ list.AllocateAndConstruct<Point2D>();
+ EXPECT_EQ(0u, reinterpret_cast<intptr_t>(&list.Last()) & (kMaxAlign - 1));
+ list.AllocateAndConstruct<Point2D>();
+ EXPECT_EQ(0u, reinterpret_cast<intptr_t>(&list.Last()) & (kMaxAlign - 1));
+ list.AllocateAndConstruct<Point3D>();
+ EXPECT_EQ(0u, reinterpret_cast<intptr_t>(&list.Last()) & (kMaxAlign - 1));
+ list.AllocateAndConstruct<Point3D>();
+ EXPECT_EQ(0u, reinterpret_cast<intptr_t>(&list.Last()) & (kMaxAlign - 1));
+ list.AllocateAndConstruct<Point2D>();
+ EXPECT_EQ(0u, reinterpret_cast<intptr_t>(&list.Last()) & (kMaxAlign - 1));
+
+ list.AppendByMoving(list[0], sizeof(Point2D));
+ EXPECT_EQ(0u, reinterpret_cast<intptr_t>(&list.Last()) & (kMaxAlign - 1));
+ list.AppendByMoving(list[1], sizeof(Point2D));
+ EXPECT_EQ(0u, reinterpret_cast<intptr_t>(&list.Last()) & (kMaxAlign - 1));
+ list.AppendByMoving(list[2], sizeof(Point3D));
+ EXPECT_EQ(0u, reinterpret_cast<intptr_t>(&list.Last()) & (kMaxAlign - 1));
+ list.AppendByMoving(list[3], sizeof(Point3D));
+ EXPECT_EQ(0u, reinterpret_cast<intptr_t>(&list.Last()) & (kMaxAlign - 1));
+ list.AppendByMoving(list[4], sizeof(Point2D));
+ EXPECT_EQ(0u, reinterpret_cast<intptr_t>(&list.Last()) & (kMaxAlign - 1));
+}
+
+} // namespace
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/cpu/arm/webgl_image_conversion_neon.h b/chromium/third_party/blink/renderer/platform/graphics/cpu/arm/webgl_image_conversion_neon.h
new file mode 100644
index 00000000000..1e1d1834bdd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/cpu/arm/webgl_image_conversion_neon.h
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2012 Gabor Rapcsanyi (rgabor@inf.u-szeged.hu), University of
+ * Szeged
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CPU_ARM_WEBGL_IMAGE_CONVERSION_NEON_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CPU_ARM_WEBGL_IMAGE_CONVERSION_NEON_H_
+
+#if WTF_CPU_ARM_NEON
+
+#include <arm_neon.h>
+
+namespace blink {
+
+namespace SIMD {
+
+ALWAYS_INLINE void UnpackOneRowOfRGBA16LittleToRGBA8(const uint16_t*& source,
+ uint8_t*& destination,
+ unsigned& pixels_per_row) {
+ unsigned components_per_row = pixels_per_row * 4;
+ unsigned tail_components = components_per_row % 16;
+ unsigned components_size = components_per_row - tail_components;
+ const uint8_t* src = reinterpret_cast<const uint8_t*>(source);
+
+ for (unsigned i = 0; i < components_size; i += 16) {
+ uint8x16x2_t components = vld2q_u8(src + i * 2);
+ vst1q_u8(destination + i, components.val[1]);
+ }
+
+ source += components_size;
+ destination += components_size;
+ pixels_per_row = tail_components / 4;
+}
+
+ALWAYS_INLINE void UnpackOneRowOfRGB16LittleToRGBA8(const uint16_t*& source,
+ uint8_t*& destination,
+ unsigned& pixels_per_row) {
+ unsigned components_per_row = pixels_per_row * 3;
+ unsigned tail_components = components_per_row % 24;
+ unsigned components_size = components_per_row - tail_components;
+
+ uint8x8_t component_a = vdup_n_u8(0xFF);
+ for (unsigned i = 0; i < components_size; i += 24) {
+ uint16x8x3_t rgb16 = vld3q_u16(source + i);
+ uint8x8_t component_r = vqmovn_u16(vshrq_n_u16(rgb16.val[0], 8));
+ uint8x8_t component_g = vqmovn_u16(vshrq_n_u16(rgb16.val[1], 8));
+ uint8x8_t component_b = vqmovn_u16(vshrq_n_u16(rgb16.val[2], 8));
+ uint8x8x4_t rgba8 = {{component_r, component_g, component_b, component_a}};
+ vst4_u8(destination, rgba8);
+ destination += 32;
+ }
+
+ source += components_size;
+ pixels_per_row = tail_components / 3;
+}
+
+ALWAYS_INLINE void UnpackOneRowOfARGB16LittleToRGBA8(const uint16_t*& source,
+ uint8_t*& destination,
+ unsigned& pixels_per_row) {
+ unsigned components_per_row = pixels_per_row * 4;
+ unsigned tail_components = components_per_row % 32;
+ unsigned components_size = components_per_row - tail_components;
+
+ for (unsigned i = 0; i < components_size; i += 32) {
+ uint16x8x4_t argb16 = vld4q_u16(source + i);
+ uint8x8_t component_a = vqmovn_u16(vshrq_n_u16(argb16.val[0], 8));
+ uint8x8_t component_r = vqmovn_u16(vshrq_n_u16(argb16.val[1], 8));
+ uint8x8_t component_g = vqmovn_u16(vshrq_n_u16(argb16.val[2], 8));
+ uint8x8_t component_b = vqmovn_u16(vshrq_n_u16(argb16.val[3], 8));
+ uint8x8x4_t rgba8 = {{component_r, component_g, component_b, component_a}};
+ vst4_u8(destination + i, rgba8);
+ }
+
+ source += components_size;
+ destination += components_size;
+ pixels_per_row = tail_components / 4;
+}
+
+ALWAYS_INLINE void UnpackOneRowOfBGRA16LittleToRGBA8(const uint16_t*& source,
+ uint8_t*& destination,
+ unsigned& pixels_per_row) {
+ unsigned components_per_row = pixels_per_row * 4;
+ unsigned tail_components = components_per_row % 32;
+ unsigned components_size = components_per_row - tail_components;
+
+ for (unsigned i = 0; i < components_size; i += 32) {
+ uint16x8x4_t argb16 = vld4q_u16(source + i);
+ uint8x8_t component_b = vqmovn_u16(vshrq_n_u16(argb16.val[0], 8));
+ uint8x8_t component_g = vqmovn_u16(vshrq_n_u16(argb16.val[1], 8));
+ uint8x8_t component_r = vqmovn_u16(vshrq_n_u16(argb16.val[2], 8));
+ uint8x8_t component_a = vqmovn_u16(vshrq_n_u16(argb16.val[3], 8));
+ uint8x8x4_t rgba8 = {{component_r, component_g, component_b, component_a}};
+ vst4_u8(destination + i, rgba8);
+ }
+
+ source += components_size;
+ destination += components_size;
+ pixels_per_row = tail_components / 4;
+}
+
+ALWAYS_INLINE void UnpackOneRowOfRGBA4444ToRGBA8(const uint16_t*& source,
+ uint8_t*& destination,
+ unsigned& pixels_per_row) {
+ unsigned tail_pixels = pixels_per_row % 8;
+ unsigned pixel_size = pixels_per_row - tail_pixels;
+
+ uint16x8_t immediate0x0f = vdupq_n_u16(0x0F);
+ for (unsigned i = 0; i < pixel_size; i += 8) {
+ uint16x8_t eight_pixels = vld1q_u16(source + i);
+
+ uint8x8_t component_r = vqmovn_u16(vshrq_n_u16(eight_pixels, 12));
+ uint8x8_t component_g =
+ vqmovn_u16(vandq_u16(vshrq_n_u16(eight_pixels, 8), immediate0x0f));
+ uint8x8_t component_b =
+ vqmovn_u16(vandq_u16(vshrq_n_u16(eight_pixels, 4), immediate0x0f));
+ uint8x8_t component_a = vqmovn_u16(vandq_u16(eight_pixels, immediate0x0f));
+
+ component_r = vorr_u8(vshl_n_u8(component_r, 4), component_r);
+ component_g = vorr_u8(vshl_n_u8(component_g, 4), component_g);
+ component_b = vorr_u8(vshl_n_u8(component_b, 4), component_b);
+ component_a = vorr_u8(vshl_n_u8(component_a, 4), component_a);
+
+ uint8x8x4_t dest_components = {
+ {component_r, component_g, component_b, component_a}};
+ vst4_u8(destination, dest_components);
+ destination += 32;
+ }
+
+ source += pixel_size;
+ pixels_per_row = tail_pixels;
+}
+
+ALWAYS_INLINE void PackOneRowOfRGBA8ToUnsignedShort4444(
+ const uint8_t*& source,
+ uint16_t*& destination,
+ unsigned& pixels_per_row) {
+ unsigned components_per_row = pixels_per_row * 4;
+ unsigned tail_components = components_per_row % 32;
+ unsigned components_size = components_per_row - tail_components;
+
+ uint8_t* dst = reinterpret_cast<uint8_t*>(destination);
+ uint8x8_t immediate0xf0 = vdup_n_u8(0xF0);
+ for (unsigned i = 0; i < components_size; i += 32) {
+ uint8x8x4_t rgba8 = vld4_u8(source + i);
+
+ uint8x8_t component_r = vand_u8(rgba8.val[0], immediate0xf0);
+ uint8x8_t component_g = vshr_n_u8(vand_u8(rgba8.val[1], immediate0xf0), 4);
+ uint8x8_t component_b = vand_u8(rgba8.val[2], immediate0xf0);
+ uint8x8_t component_a = vshr_n_u8(vand_u8(rgba8.val[3], immediate0xf0), 4);
+
+ uint8x8x2_t rgba4;
+ rgba4.val[0] = vorr_u8(component_b, component_a);
+ rgba4.val[1] = vorr_u8(component_r, component_g);
+ vst2_u8(dst, rgba4);
+ dst += 16;
+ }
+
+ source += components_size;
+ destination += components_size / 4;
+ pixels_per_row = tail_components / 4;
+}
+
+ALWAYS_INLINE void UnpackOneRowOfRGBA5551ToRGBA8(const uint16_t*& source,
+ uint8_t*& destination,
+ unsigned& pixels_per_row) {
+ unsigned tail_pixels = pixels_per_row % 8;
+ unsigned pixel_size = pixels_per_row - tail_pixels;
+
+ uint8x8_t immediate0x7 = vdup_n_u8(0x7);
+ uint8x8_t immediate0xff = vdup_n_u8(0xFF);
+ uint16x8_t immediate0x1f = vdupq_n_u16(0x1F);
+ uint16x8_t immediate0x1 = vdupq_n_u16(0x1);
+
+ for (unsigned i = 0; i < pixel_size; i += 8) {
+ uint16x8_t eight_pixels = vld1q_u16(source + i);
+
+ uint8x8_t component_r = vqmovn_u16(vshrq_n_u16(eight_pixels, 11));
+ uint8x8_t component_g =
+ vqmovn_u16(vandq_u16(vshrq_n_u16(eight_pixels, 6), immediate0x1f));
+ uint8x8_t component_b =
+ vqmovn_u16(vandq_u16(vshrq_n_u16(eight_pixels, 1), immediate0x1f));
+ uint8x8_t component_a = vqmovn_u16(vandq_u16(eight_pixels, immediate0x1));
+
+ component_r =
+ vorr_u8(vshl_n_u8(component_r, 3), vand_u8(component_r, immediate0x7));
+ component_g =
+ vorr_u8(vshl_n_u8(component_g, 3), vand_u8(component_g, immediate0x7));
+ component_b =
+ vorr_u8(vshl_n_u8(component_b, 3), vand_u8(component_b, immediate0x7));
+ component_a = vmul_u8(component_a, immediate0xff);
+
+ uint8x8x4_t dest_components = {
+ {component_r, component_g, component_b, component_a}};
+ vst4_u8(destination, dest_components);
+ destination += 32;
+ }
+
+ source += pixel_size;
+ pixels_per_row = tail_pixels;
+}
+
+ALWAYS_INLINE void PackOneRowOfRGBA8ToUnsignedShort5551(
+ const uint8_t*& source,
+ uint16_t*& destination,
+ unsigned& pixels_per_row) {
+ unsigned components_per_row = pixels_per_row * 4;
+ unsigned tail_components = components_per_row % 32;
+ unsigned components_size = components_per_row - tail_components;
+
+ uint8_t* dst = reinterpret_cast<uint8_t*>(destination);
+
+ uint8x8_t immediate0xf8 = vdup_n_u8(0xF8);
+ uint8x8_t immediate0x18 = vdup_n_u8(0x18);
+ for (unsigned i = 0; i < components_size; i += 32) {
+ uint8x8x4_t rgba8 = vld4_u8(source + i);
+
+ uint8x8_t component_r = vand_u8(rgba8.val[0], immediate0xf8);
+ uint8x8_t component_g3bit = vshr_n_u8(rgba8.val[1], 5);
+
+ uint8x8_t component_g2bit =
+ vshl_n_u8(vand_u8(rgba8.val[1], immediate0x18), 3);
+ uint8x8_t component_b = vshr_n_u8(vand_u8(rgba8.val[2], immediate0xf8), 2);
+ uint8x8_t component_a = vshr_n_u8(rgba8.val[3], 7);
+
+ uint8x8x2_t rgba5551;
+ rgba5551.val[0] =
+ vorr_u8(vorr_u8(component_g2bit, component_b), component_a);
+ rgba5551.val[1] = vorr_u8(component_r, component_g3bit);
+ vst2_u8(dst, rgba5551);
+ dst += 16;
+ }
+
+ source += components_size;
+ destination += components_size / 4;
+ pixels_per_row = tail_components / 4;
+}
+
+ALWAYS_INLINE void PackOneRowOfRGBA8ToUnsignedShort565(
+ const uint8_t*& source,
+ uint16_t*& destination,
+ unsigned& pixels_per_row) {
+ unsigned components_per_row = pixels_per_row * 4;
+ unsigned tail_components = components_per_row % 32;
+ unsigned components_size = components_per_row - tail_components;
+ uint8_t* dst = reinterpret_cast<uint8_t*>(destination);
+
+ uint8x8_t immediate0xf8 = vdup_n_u8(0xF8);
+ uint8x8_t immediate0x1c = vdup_n_u8(0x1C);
+ for (unsigned i = 0; i < components_size; i += 32) {
+ uint8x8x4_t rgba8 = vld4_u8(source + i);
+
+ uint8x8_t component_r = vand_u8(rgba8.val[0], immediate0xf8);
+ uint8x8_t component_g_left = vshr_n_u8(rgba8.val[1], 5);
+ uint8x8_t component_g_right =
+ vshl_n_u8(vand_u8(rgba8.val[1], immediate0x1c), 3);
+ uint8x8_t component_b = vshr_n_u8(vand_u8(rgba8.val[2], immediate0xf8), 3);
+
+ uint8x8x2_t rgb565;
+ rgb565.val[0] = vorr_u8(component_g_right, component_b);
+ rgb565.val[1] = vorr_u8(component_r, component_g_left);
+ vst2_u8(dst, rgb565);
+ dst += 16;
+ }
+
+ source += components_size;
+ destination += components_size / 4;
+ pixels_per_row = tail_components / 4;
+}
+
+} // namespace SIMD
+
+} // namespace blink
+
+#endif // WTF_CPU_ARM_NEON
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CPU_ARM_WEBGL_IMAGE_CONVERSION_NEON_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/cpu/mips/webgl_image_conversion_msa.h b/chromium/third_party/blink/renderer/platform/graphics/cpu/mips/webgl_image_conversion_msa.h
new file mode 100644
index 00000000000..03c75706ff5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/cpu/mips/webgl_image_conversion_msa.h
@@ -0,0 +1,1154 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CPU_MIPS_WEBGL_IMAGE_CONVERSION_MSA_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CPU_MIPS_WEBGL_IMAGE_CONVERSION_MSA_H_
+
+#if HAVE_MIPS_MSA_INTRINSICS
+
+#include "third_party/blink/renderer/platform/cpu/mips/common_macros_msa.h"
+
+namespace blink {
+
+namespace SIMD {
+
+#define SEPERATE_RGBA_FRM_16BIT_5551INPUT(in, out_r, out_g, out_b, out_a) \
+ cnst31 = (v8u16)__msa_ldi_h(0x1F); \
+ cnst7 = (v8u16)__msa_ldi_h(0x7); \
+ cnst1 = (v8u16)__msa_ldi_h(0x1); \
+ out_r = (v8u16)SRLI_H(in, 11); \
+ out_g = ((v8u16)SRLI_H(in, 6)) & cnst31; \
+ out_b = ((v8u16)SRLI_H(in, 1)) & cnst31; \
+ out_a = in & cnst1; \
+ out_r = ((v8u16)SLLI_H(out_r, 3)) | (out_r & cnst7); \
+ out_g = ((v8u16)SLLI_H(out_g, 3)) | (out_g & cnst7); \
+ out_b = ((v8u16)SLLI_H(out_b, 3)) | (out_b & cnst7); \
+ out_a = (v8u16)CEQI_H((v8i16)out_a, 1);
+
+#define SEPERATE_RGBA_FRM_16BIT_4444INPUT(in, out_rb, out_ga) \
+ out_rb = (v16u8)SRLI_B((v16u8)in, 4); \
+ out_ga = ANDI_B((v16u8)in, 15); \
+ out_rb = ((v16u8)SLLI_B(out_rb, 4)) | out_rb; \
+ out_ga = ((v16u8)SLLI_B(out_ga, 4)) | out_ga;
+
+ALWAYS_INLINE void unpackOneRowOfRGBA5551ToRGBA8MSA(const uint16_t*& source,
+ uint8_t*& destination,
+ unsigned& pixelsPerRow) {
+ unsigned i;
+ v8u16 src0, src1, src2, src3;
+ v8u16 src0r, src0g, src0b, src0a, src1r, src1g, src1b, src1a;
+ v8u16 src2r, src2g, src2b, src2a, src3r, src3g, src3b, src3a;
+ v8u16 cnst31, cnst7, cnst1;
+ v16u8 dst0, dst1, dst2, dst3, dst4, dst5, dst6, dst7;
+ v16u8 dst8, dst9, dst10, dst11, dst12, dst13, dst14, dst15;
+ v16u8 out0, out1, out2, out3, out4, out5, out6, out7;
+
+ for (i = (pixelsPerRow >> 5); i--;) {
+ LD_UH4(source, 8, src0, src1, src2, src3);
+ SEPERATE_RGBA_FRM_16BIT_5551INPUT(src0, src0r, src0g, src0b, src0a);
+ SEPERATE_RGBA_FRM_16BIT_5551INPUT(src1, src1r, src1g, src1b, src1a);
+ SEPERATE_RGBA_FRM_16BIT_5551INPUT(src2, src2r, src2g, src2b, src2a);
+ SEPERATE_RGBA_FRM_16BIT_5551INPUT(src3, src3r, src3g, src3b, src3a);
+ ILVRL_B2_UB(src0g, src0r, dst0, dst1);
+ ILVRL_B2_UB(src0a, src0b, dst2, dst3);
+ ILVRL_B2_UB(src1g, src1r, dst4, dst5);
+ ILVRL_B2_UB(src1a, src1b, dst6, dst7);
+ ILVRL_B2_UB(src2g, src2r, dst8, dst9);
+ ILVRL_B2_UB(src2a, src2b, dst10, dst11);
+ ILVRL_B2_UB(src3g, src3r, dst12, dst13);
+ ILVRL_B2_UB(src3a, src3b, dst14, dst15);
+ ILVEV_H2_UB(dst0, dst2, dst1, dst3, out0, out1);
+ ILVEV_H2_UB(dst4, dst6, dst5, dst7, out2, out3);
+ ILVEV_H2_UB(dst8, dst10, dst9, dst11, out4, out5);
+ ILVEV_H2_UB(dst12, dst14, dst13, dst15, out6, out7);
+ ST_UB8(out0, out1, out2, out3, out4, out5, out6, out7, destination, 16);
+ }
+
+ if (pixelsPerRow & 31) {
+ if ((pixelsPerRow & 16) && (pixelsPerRow & 8)) {
+ LD_UH3(source, 8, src0, src1, src2);
+ SEPERATE_RGBA_FRM_16BIT_5551INPUT(src0, src0r, src0g, src0b, src0a);
+ SEPERATE_RGBA_FRM_16BIT_5551INPUT(src1, src1r, src1g, src1b, src1a);
+ SEPERATE_RGBA_FRM_16BIT_5551INPUT(src2, src2r, src2g, src2b, src2a);
+ ILVRL_B2_UB(src0g, src0r, dst0, dst1);
+ ILVRL_B2_UB(src0a, src0b, dst2, dst3);
+ ILVRL_B2_UB(src1g, src1r, dst4, dst5);
+ ILVRL_B2_UB(src1a, src1b, dst6, dst7);
+ ILVRL_B2_UB(src2g, src2r, dst8, dst9);
+ ILVRL_B2_UB(src2a, src2b, dst10, dst11);
+ ILVEV_H2_UB(dst0, dst2, dst1, dst3, out0, out1);
+ ILVEV_H2_UB(dst4, dst6, dst5, dst7, out2, out3);
+ ILVEV_H2_UB(dst8, dst10, dst9, dst11, out4, out5);
+ ST_UB6(out0, out1, out2, out3, out4, out5, destination, 16);
+ } else if (pixelsPerRow & 16) {
+ LD_UH2(source, 8, src0, src1);
+ SEPERATE_RGBA_FRM_16BIT_5551INPUT(src0, src0r, src0g, src0b, src0a);
+ SEPERATE_RGBA_FRM_16BIT_5551INPUT(src1, src1r, src1g, src1b, src1a);
+ ILVRL_B2_UB(src0g, src0r, dst0, dst1);
+ ILVRL_B2_UB(src0a, src0b, dst2, dst3);
+ ILVRL_B2_UB(src1g, src1r, dst4, dst5);
+ ILVRL_B2_UB(src1a, src1b, dst6, dst7);
+ ILVEV_H2_UB(dst0, dst2, dst1, dst3, out0, out1);
+ ILVEV_H2_UB(dst4, dst6, dst5, dst7, out2, out3);
+ ST_UB4(out0, out1, out2, out3, destination, 16);
+ } else if (pixelsPerRow & 8) {
+ src0 = LD_UH(source);
+ source += 8;
+ SEPERATE_RGBA_FRM_16BIT_5551INPUT(src0, src0r, src0g, src0b, src0a);
+ ILVRL_B2_UB(src0g, src0r, dst0, dst1);
+ ILVRL_B2_UB(src0a, src0b, dst2, dst3);
+ ILVEV_H2_UB(dst0, dst2, dst1, dst3, out0, out1);
+ ST_UB2(out0, out1, destination, 16);
+ }
+ }
+
+ pixelsPerRow &= 7;
+}
+
+ALWAYS_INLINE void unpackOneRowOfBGRA8LittleToRGBA8MSA(const uint32_t*& source,
+ uint32_t*& destination,
+ unsigned& pixelsPerRow) {
+ unsigned i;
+ v16u8 src0, src1, src2, src3, src4, src5, src6, src7;
+ v16u8 src8, src9, src10, src11, src12, src13, src14, src15;
+
+ for (i = (pixelsPerRow >> 6); i--;) {
+ LD_UB8(source, 4, src0, src1, src2, src3, src4, src5, src6, src7);
+ LD_UB8(source, 4, src8, src9, src10, src11, src12, src13, src14, src15);
+ SHF_B4_UB(src0, src1, src2, src3, 198);
+ SHF_B4_UB(src4, src5, src6, src7, 198);
+ SHF_B4_UB(src8, src9, src10, src11, 198);
+ SHF_B4_UB(src12, src13, src14, src15, 198);
+ ST_UB8(src0, src1, src2, src3, src4, src5, src6, src7, destination, 4);
+ ST_UB8(src8, src9, src10, src11, src12, src13, src14, src15, destination,
+ 4);
+ }
+
+ if (pixelsPerRow & 63) {
+ if (pixelsPerRow & 32) {
+ if ((pixelsPerRow & 16) && (pixelsPerRow & 8)) {
+ LD_UB8(source, 4, src0, src1, src2, src3, src4, src5, src6, src7);
+ LD_UB6(source, 4, src8, src9, src10, src11, src12, src13);
+ SHF_B4_UB(src0, src1, src2, src3, 198);
+ SHF_B4_UB(src4, src5, src6, src7, 198);
+ SHF_B4_UB(src8, src9, src10, src11, 198);
+ SHF_B2_UB(src12, src13, 198);
+ ST_UB8(src0, src1, src2, src3, src4, src5, src6, src7, destination, 4);
+ ST_UB6(src8, src9, src10, src11, src12, src13, destination, 4);
+ } else if (pixelsPerRow & 16) {
+ LD_UB8(source, 4, src0, src1, src2, src3, src4, src5, src6, src7);
+ LD_UB4(source, 4, src8, src9, src10, src11);
+ SHF_B4_UB(src0, src1, src2, src3, 198);
+ SHF_B4_UB(src4, src5, src6, src7, 198);
+ SHF_B4_UB(src8, src9, src10, src11, 198);
+ ST_UB8(src0, src1, src2, src3, src4, src5, src6, src7, destination, 4);
+ ST_UB4(src8, src9, src10, src11, destination, 4);
+ } else if (pixelsPerRow & 8) {
+ LD_UB8(source, 4, src0, src1, src2, src3, src4, src5, src6, src7);
+ LD_UB2(source, 4, src8, src9);
+ SHF_B4_UB(src0, src1, src2, src3, 198);
+ SHF_B4_UB(src4, src5, src6, src7, 198);
+ SHF_B2_UB(src8, src9, 198);
+ ST_UB8(src0, src1, src2, src3, src4, src5, src6, src7, destination, 4);
+ ST_UB2(src8, src9, destination, 4);
+ } else {
+ LD_UB8(source, 4, src0, src1, src2, src3, src4, src5, src6, src7);
+ SHF_B4_UB(src0, src1, src2, src3, 198);
+ SHF_B4_UB(src4, src5, src6, src7, 198);
+ ST_UB8(src0, src1, src2, src3, src4, src5, src6, src7, destination, 4);
+ }
+ } else if ((pixelsPerRow & 16) && (pixelsPerRow & 8)) {
+ LD_UB6(source, 4, src0, src1, src2, src3, src4, src5);
+ SHF_B4_UB(src0, src1, src2, src3, 198);
+ SHF_B2_UB(src4, src5, 198);
+ ST_UB6(src0, src1, src2, src3, src4, src5, destination, 4);
+ } else if (pixelsPerRow & 16) {
+ LD_UB4(source, 4, src0, src1, src2, src3);
+ SHF_B4_UB(src0, src1, src2, src3, 198);
+ ST_UB4(src0, src1, src2, src3, destination, 4);
+ } else if (pixelsPerRow & 8) {
+ LD_UB2(source, 4, src0, src1);
+ SHF_B2_UB(src0, src1, 198);
+ ST_UB2(src0, src1, destination, 4);
+ }
+
+ if (pixelsPerRow & 4) {
+ src0 = LD_UB(source);
+ source += 4;
+ src0 = (v16u8)__msa_shf_b((v16i8)src0, 198);
+ ST_UB(src0, destination);
+ destination += 4;
+ }
+ }
+
+ pixelsPerRow &= 3;
+}
+
+ALWAYS_INLINE void unpackOneRowOfRGBA4444ToRGBA8MSA(const uint16_t*& source,
+ uint8_t*& destination,
+ unsigned& pixelsPerRow) {
+ unsigned i;
+ v8u16 src0, src1, src2, src3;
+ v16u8 src0rb, src0ga, src1rb, src1ga, src2rb, src2ga, src3rb, src3ga;
+ v16u8 dst0, dst1, dst2, dst3, dst4, dst5, dst6, dst7;
+ v16u8 out0, out1, out2, out3, out4, out5, out6, out7;
+
+ for (i = (pixelsPerRow >> 5); i--;) {
+ LD_UH4(source, 8, src0, src1, src2, src3);
+ SEPERATE_RGBA_FRM_16BIT_4444INPUT(src0, src0rb, src0ga);
+ SEPERATE_RGBA_FRM_16BIT_4444INPUT(src1, src1rb, src1ga);
+ SEPERATE_RGBA_FRM_16BIT_4444INPUT(src2, src2rb, src2ga);
+ SEPERATE_RGBA_FRM_16BIT_4444INPUT(src3, src3rb, src3ga);
+ ILVODEV_B2_UB(src0ga, src0rb, dst0, dst1);
+ ILVODEV_B2_UB(src1ga, src1rb, dst2, dst3);
+ ILVODEV_B2_UB(src2ga, src2rb, dst4, dst5);
+ ILVODEV_B2_UB(src3ga, src3rb, dst6, dst7);
+ ILVRL_H2_UB(dst1, dst0, out0, out1);
+ ILVRL_H2_UB(dst3, dst2, out2, out3);
+ ILVRL_H2_UB(dst5, dst4, out4, out5);
+ ILVRL_H2_UB(dst7, dst6, out6, out7);
+ ST_UB8(out0, out1, out2, out3, out4, out5, out6, out7, destination, 16);
+ }
+
+ if (pixelsPerRow & 31) {
+ if ((pixelsPerRow & 16) && (pixelsPerRow & 8)) {
+ LD_UH3(source, 8, src0, src1, src2);
+ SEPERATE_RGBA_FRM_16BIT_4444INPUT(src0, src0rb, src0ga);
+ SEPERATE_RGBA_FRM_16BIT_4444INPUT(src1, src1rb, src1ga);
+ SEPERATE_RGBA_FRM_16BIT_4444INPUT(src2, src2rb, src2ga);
+ ILVODEV_B2_UB(src0ga, src0rb, dst0, dst1);
+ ILVODEV_B2_UB(src1ga, src1rb, dst2, dst3);
+ ILVODEV_B2_UB(src2ga, src2rb, dst4, dst5);
+ ILVRL_H2_UB(dst1, dst0, out0, out1);
+ ILVRL_H2_UB(dst3, dst2, out2, out3);
+ ILVRL_H2_UB(dst5, dst4, out4, out5);
+ ST_UB6(out0, out1, out2, out3, out4, out5, destination, 16);
+ } else if (pixelsPerRow & 16) {
+ LD_UH2(source, 8, src0, src1);
+ SEPERATE_RGBA_FRM_16BIT_4444INPUT(src0, src0rb, src0ga);
+ SEPERATE_RGBA_FRM_16BIT_4444INPUT(src1, src1rb, src1ga);
+ ILVODEV_B2_UB(src0ga, src0rb, dst0, dst1);
+ ILVODEV_B2_UB(src1ga, src1rb, dst2, dst3);
+ ILVRL_H2_UB(dst1, dst0, out0, out1);
+ ILVRL_H2_UB(dst3, dst2, out2, out3);
+ ST_UB4(out0, out1, out2, out3, destination, 16);
+ } else if (pixelsPerRow & 8) {
+ src0 = LD_UH(source);
+ source += 8;
+ SEPERATE_RGBA_FRM_16BIT_4444INPUT(src0, src0rb, src0ga);
+ ILVODEV_B2_UB(src0ga, src0rb, dst0, dst1);
+ ILVRL_H2_UB(dst1, dst0, out0, out1);
+ ST_UB2(out0, out1, destination, 16);
+ }
+ }
+
+ pixelsPerRow &= 7;
+}
+
+ALWAYS_INLINE void packOneRowOfRGBA8LittleToRGBA8MSA(const uint8_t*& source,
+ uint8_t*& destination,
+ unsigned& pixelsPerRow) {
+ unsigned i;
+ v16u8 src0, src1, src2, src3, out0, out1, out2, out3;
+ v16u8 src0R, src1R, src2R, src3R, src0G, src1G, src2G, src3G;
+ v16u8 src0B, src1B, src2B, src3B, src0A, src1A, src2A, src3A;
+ v16u8 dst0R, dst1R, dst2R, dst3R, dst0G, dst1G, dst2G, dst3G;
+ v16u8 dst0B, dst1B, dst2B, dst3B, dst0A, dst1A, dst2A, dst3A;
+ v16u8 dst0RG, dst1RG, dst2RG, dst3RG, dst0BA, dst1BA, dst2BA, dst3BA;
+ v4f32 fsrc0R, fsrc1R, fsrc2R, fsrc3R, fsrc0G, fsrc1G, fsrc2G, fsrc3G;
+ v4f32 fsrc0B, fsrc1B, fsrc2B, fsrc3B, fsrc0A, fsrc1A, fsrc2A, fsrc3A;
+ v4u32 vCnst255 = (v4u32)__msa_ldi_w(255);
+ v16u8 alphaMask = {0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255};
+ v4f32 vfCnst255 = __msa_ffint_u_w(vCnst255);
+
+ for (i = (pixelsPerRow >> 4); i--;) {
+ LD_UB4(source, 16, src0, src1, src2, src3);
+ CEQI_B4_UB(src0, src1, src2, src3, 0, src0A, src1A, src2A, src3A);
+ src0A = __msa_bmnz_v(src0, alphaMask, src0A);
+ src1A = __msa_bmnz_v(src1, alphaMask, src1A);
+ src2A = __msa_bmnz_v(src2, alphaMask, src2A);
+ src3A = __msa_bmnz_v(src3, alphaMask, src3A);
+ AND_V4_UB(src0A, src1A, src2A, src3A, alphaMask, src0A, src1A, src2A,
+ src3A);
+ src0A = SLDI_UB(src0A, src0A, 3);
+ src1A = SLDI_UB(src1A, src1A, 3);
+ src2A = SLDI_UB(src2A, src2A, 3);
+ src3A = SLDI_UB(src3A, src3A, 3);
+ FFINTU_W4_SP(src0A, src1A, src2A, src3A, fsrc0A, fsrc1A, fsrc2A, fsrc3A);
+ DIV4(vfCnst255, fsrc0A, vfCnst255, fsrc1A, vfCnst255, fsrc2A, vfCnst255,
+ fsrc3A, fsrc0A, fsrc1A, fsrc2A, fsrc3A);
+ AND_V4_UB(src0, src1, src2, src3, vCnst255, src0R, src1R, src2R, src3R);
+ FFINTU_W4_SP(src0R, src1R, src2R, src3R, fsrc0R, fsrc1R, fsrc2R, fsrc3R);
+ MUL4(fsrc0R, fsrc0A, fsrc1R, fsrc1A, fsrc2R, fsrc2A, fsrc3R, fsrc3A, fsrc0R,
+ fsrc1R, fsrc2R, fsrc3R);
+ src0G = SLDI_UB(src0, src0, 1);
+ src1G = SLDI_UB(src1, src1, 1);
+ src2G = SLDI_UB(src2, src2, 1);
+ src3G = SLDI_UB(src3, src3, 1);
+ AND_V4_UB(src0G, src1G, src2G, src3G, vCnst255, src0G, src1G, src2G, src3G);
+ FFINTU_W4_SP(src0G, src1G, src2G, src3G, fsrc0G, fsrc1G, fsrc2G, fsrc3G);
+ MUL4(fsrc0G, fsrc0A, fsrc1G, fsrc1A, fsrc2G, fsrc2A, fsrc3G, fsrc3A, fsrc0G,
+ fsrc1G, fsrc2G, fsrc3G);
+ src0B = SLDI_UB(src0, src0, 2);
+ src1B = SLDI_UB(src1, src1, 2);
+ src2B = SLDI_UB(src2, src2, 2);
+ src3B = SLDI_UB(src3, src3, 2);
+ AND_V4_UB(src0B, src1B, src2B, src3B, vCnst255, src0B, src1B, src2B, src3B);
+ FFINTU_W4_SP(src0B, src1B, src2B, src3B, fsrc0B, fsrc1B, fsrc2B, fsrc3B);
+ MUL4(fsrc0B, fsrc0A, fsrc1B, fsrc1A, fsrc2B, fsrc2A, fsrc3B, fsrc3A, fsrc0B,
+ fsrc1B, fsrc2B, fsrc3B);
+ FTRUNCU_W4_UB(fsrc0R, fsrc1R, fsrc2R, fsrc3R, dst0R, dst1R, dst2R, dst3R);
+ FTRUNCU_W4_UB(fsrc0G, fsrc1G, fsrc2G, fsrc3G, dst0G, dst1G, dst2G, dst3G);
+ FTRUNCU_W4_UB(fsrc0B, fsrc1B, fsrc2B, fsrc3B, dst0B, dst1B, dst2B, dst3B);
+ dst0A = SLDI_UB(src0, src0, 3);
+ dst1A = SLDI_UB(src1, src1, 3);
+ dst2A = SLDI_UB(src2, src2, 3);
+ dst3A = SLDI_UB(src3, src3, 3);
+ ILVEV_B2_UB(dst0R, dst0G, dst1R, dst1G, dst0RG, dst1RG);
+ ILVEV_B2_UB(dst2R, dst2G, dst3R, dst3G, dst2RG, dst3RG);
+ ILVEV_B2_UB(dst0B, dst0A, dst1B, dst1A, dst0BA, dst1BA);
+ ILVEV_B2_UB(dst2B, dst2A, dst3B, dst3A, dst2BA, dst3BA);
+ ILVEV_H2_UB(dst0RG, dst0BA, dst1RG, dst1BA, out0, out1);
+ ILVEV_H2_UB(dst2RG, dst2BA, dst3RG, dst3BA, out2, out3);
+ ST_UB4(out0, out1, out2, out3, destination, 16);
+ }
+
+ if (pixelsPerRow & 15) {
+ if (pixelsPerRow & 8) {
+ LD_UB2(source, 16, src0, src1);
+ CEQI_B2_UB(src0, src1, 0, src0A, src1A);
+ src0A = __msa_bmnz_v(src0, alphaMask, src0A);
+ src1A = __msa_bmnz_v(src1, alphaMask, src1A);
+ AND_V2_UB(src0A, src1A, alphaMask, src0A, src1A);
+ src0A = SLDI_UB(src0A, src0A, 3);
+ src1A = SLDI_UB(src1A, src1A, 3);
+ FFINTU_W2_SP(src0A, src1A, fsrc0A, fsrc1A);
+ DIV2(vfCnst255, fsrc0A, vfCnst255, fsrc1A, fsrc0A, fsrc1A);
+ AND_V2_UB(src0, src1, vCnst255, src0R, src1R);
+ FFINTU_W2_SP(src0R, src1R, fsrc0R, fsrc1R);
+ MUL2(fsrc0R, fsrc0A, fsrc1R, fsrc1A, fsrc0R, fsrc1R);
+ src0G = SLDI_UB(src0, src0, 1);
+ src1G = SLDI_UB(src1, src1, 1);
+ AND_V2_UB(src0G, src1G, vCnst255, src0G, src1G);
+ FFINTU_W2_SP(src0G, src1G, fsrc0G, fsrc1G);
+ MUL2(fsrc0G, fsrc0A, fsrc1G, fsrc1A, fsrc0G, fsrc1G);
+ src0B = SLDI_UB(src0, src0, 2);
+ src1B = SLDI_UB(src1, src1, 2);
+ AND_V2_UB(src0B, src1B, vCnst255, src0B, src1B);
+ FFINTU_W2_SP(src0B, src1B, fsrc0B, fsrc1B);
+ MUL2(fsrc0B, fsrc0A, fsrc1B, fsrc1A, fsrc0B, fsrc1B);
+ FTRUNCU_W2_UB(fsrc0R, fsrc1R, dst0R, dst1R);
+ FTRUNCU_W2_UB(fsrc0G, fsrc1G, dst0G, dst1G);
+ FTRUNCU_W2_UB(fsrc0B, fsrc1B, dst0B, dst1B);
+ dst0A = SLDI_UB(src0, src0, 3);
+ dst1A = SLDI_UB(src1, src1, 3);
+ ILVEV_B2_UB(dst0R, dst0G, dst1R, dst1G, dst0RG, dst1RG);
+ ILVEV_B2_UB(dst0B, dst0A, dst1B, dst1A, dst0BA, dst1BA);
+ ILVEV_H2_UB(dst0RG, dst0BA, dst1RG, dst1BA, out0, out1);
+ ST_UB2(out0, out1, destination, 16);
+ }
+
+ if (pixelsPerRow & 4) {
+ src0 = LD_UB(source);
+ source += 16;
+ src0A = CEQI_B(src0, 0);
+ src0A = __msa_bmnz_v(src0, alphaMask, src0A);
+ src0A = src0A & alphaMask;
+ src0A = SLDI_UB(src0A, src0A, 3);
+ fsrc0A = __msa_ffint_u_w((v4u32)src0A);
+ fsrc0A = vfCnst255 / fsrc0A;
+ src0R = src0 & (v16u8)vCnst255;
+ fsrc0R = __msa_ffint_u_w((v4u32)src0R);
+ fsrc0R *= fsrc0A;
+ src0G = SLDI_UB(src0, src0, 1);
+ src0G &= (v16u8)vCnst255;
+ fsrc0G = __msa_ffint_u_w((v4u32)src0G);
+ fsrc0G *= fsrc0A;
+ src0B = SLDI_UB(src0, src0, 2);
+ src0B &= (v16u8)vCnst255;
+ fsrc0B = __msa_ffint_u_w((v4u32)src0B);
+ fsrc0B *= fsrc0A;
+ dst0R = (v16u8)__msa_ftrunc_u_w(fsrc0R);
+ dst0G = (v16u8)__msa_ftrunc_u_w(fsrc0G);
+ dst0B = (v16u8)__msa_ftrunc_u_w(fsrc0B);
+ dst0A = SLDI_UB(src0, src0, 3);
+ dst0RG = (v16u8)__msa_ilvev_b((v16i8)dst0G, (v16i8)dst0R);
+ dst0BA = (v16u8)__msa_ilvev_b((v16i8)dst0A, (v16i8)dst0B);
+ out0 = (v16u8)__msa_ilvev_h((v8i16)dst0BA, (v8i16)dst0RG);
+ ST_UB(out0, destination);
+ destination += 16;
+ }
+ }
+
+ pixelsPerRow &= 3;
+}
+
+ALWAYS_INLINE void packOneRowOfRGBA8ToUnsignedShort5551MSA(
+ const uint8_t*& source,
+ uint16_t*& destination,
+ unsigned& pixelsPerRow) {
+ unsigned i;
+ v16u8 src0, src1, src2, src3, src4, src5, src6, src7;
+ v16u8 src0r, src0b, src1r, src1b, src2r, src2b, src3r, src3b;
+ v16u8 src0g = {0}, src0a = {0}, src1g = {0}, src1a = {0};
+ v16u8 src2g = {0}, src2a = {0}, src3g = {0}, src3a = {0};
+ v16u8 src0gt, src1gt, src2gt, src3gt;
+ v8u16 dst0, dst1, dst2, dst3;
+
+ for (i = (pixelsPerRow >> 5); i--;) {
+ LD_UB8(source, 16, src0, src1, src2, src3, src4, src5, src6, src7);
+ PCKEV_H4_UB(src1, src0, src3, src2, src5, src4, src7, src6, src0r, src1r,
+ src2r, src3r);
+ PCKOD_H4_UB(src1, src0, src3, src2, src5, src4, src7, src6, src0b, src1b,
+ src2b, src3b);
+ SLDI_B2_UB(src0g, src1g, src0r, src1r, src0g, src1g, 1);
+ SLDI_B2_UB(src2g, src3g, src2r, src3r, src2g, src3g, 1);
+ SLDI_B2_UB(src0a, src1a, src0b, src1b, src0a, src1a, 1);
+ SLDI_B2_UB(src2a, src3a, src2b, src3b, src2a, src3a, 1);
+ src0gt = (v16u8)SLLI_B(src0g, 3);
+ src1gt = (v16u8)SLLI_B(src1g, 3);
+ src2gt = (v16u8)SLLI_B(src2g, 3);
+ src3gt = (v16u8)SLLI_B(src3g, 3);
+ SRLI_B4_UB(src0g, src1g, src2g, src3g, 5);
+ SRLI_B4_UB(src0b, src1b, src2b, src3b, 2);
+ SRLI_B4_UB(src0a, src1a, src2a, src3a, 7);
+ BINSRI_B2_UB(src0r, src0g, src1r, src1g, src0r, src1r, 2);
+ BINSRI_B2_UB(src2r, src2g, src3r, src3g, src2r, src3r, 2);
+ BINSRI_B2_UB(src0gt, src0b, src1gt, src1b, src0b, src1b, 5);
+ BINSRI_B2_UB(src2gt, src2b, src3gt, src3b, src2b, src3b, 5);
+ BINSRI_B2_UB(src0b, src0a, src1b, src1a, src0b, src1b, 0);
+ BINSRI_B2_UB(src2b, src2a, src3b, src3a, src2b, src3b, 0);
+ ILVEV_B2_UH(src0b, src0r, src1b, src1r, dst0, dst1);
+ ILVEV_B2_UH(src2b, src2r, src3b, src3r, dst2, dst3);
+ ST_UH4(dst0, dst1, dst2, dst3, destination, 8);
+ }
+
+ if (pixelsPerRow & 31) {
+ if ((pixelsPerRow & 16) && (pixelsPerRow & 8)) {
+ LD_UB6(source, 16, src0, src1, src2, src3, src4, src5);
+ PCKEV_H3_UB(src1, src0, src3, src2, src5, src4, src0r, src1r, src2r);
+ PCKOD_H3_UB(src1, src0, src3, src2, src5, src4, src0b, src1b, src2b);
+ SLDI_B2_UB(src0g, src1g, src0r, src1r, src0g, src1g, 1);
+ SLDI_B2_UB(src2g, src0a, src2r, src0b, src2g, src0a, 1);
+ SLDI_B2_UB(src1a, src2a, src1b, src2b, src1a, src2a, 1);
+ src0gt = (v16u8)SLLI_B(src0g, 3);
+ src1gt = (v16u8)SLLI_B(src1g, 3);
+ src2gt = (v16u8)SLLI_B(src2g, 3);
+ SRLI_B3_UB(src0g, src1g, src2g, 5);
+ SRLI_B3_UB(src0b, src1b, src2b, 2);
+ SRLI_B3_UB(src0a, src1a, src2a, 7);
+ BINSRI_B3_UB(src0r, src0g, src1r, src1g, src2r, src2g, src0r, src1r,
+ src2r, 2);
+ BINSRI_B3_UB(src0gt, src0b, src1gt, src1b, src2gt, src2b, src0b, src1b,
+ src2b, 5);
+ BINSRI_B3_UB(src0b, src0a, src1b, src1a, src2b, src2a, src0b, src1b,
+ src2b, 0);
+ ILVEV_B3_UH(src0b, src0r, src1b, src1r, src2b, src2r, dst0, dst1, dst2);
+ ST_UH3(dst0, dst1, dst2, destination, 8);
+ } else if (pixelsPerRow & 16) {
+ LD_UB4(source, 16, src0, src1, src2, src3);
+ PCKEV_H2_UB(src1, src0, src3, src2, src0r, src1r);
+ PCKOD_H2_UB(src1, src0, src3, src2, src0b, src1b);
+ SLDI_B2_UB(src0g, src1g, src0r, src1r, src0g, src1g, 1);
+ SLDI_B2_UB(src0a, src1a, src0b, src1b, src0a, src1a, 1);
+ src0gt = (v16u8)SLLI_B(src0g, 3);
+ src1gt = (v16u8)SLLI_B(src1g, 3);
+ SRLI_B2_UB(src0g, src1g, 5);
+ SRLI_B2_UB(src0b, src1b, 2);
+ SRLI_B2_UB(src0a, src1a, 7);
+ BINSRI_B2_UB(src0r, src0g, src1r, src1g, src0r, src1r, 2);
+ BINSRI_B2_UB(src0gt, src0b, src1gt, src1b, src0b, src1b, 5);
+ BINSRI_B2_UB(src0b, src0a, src1b, src1a, src0b, src1b, 0);
+ ILVEV_B2_UH(src0b, src0r, src1b, src1r, dst0, dst1);
+ ST_UH2(dst0, dst1, destination, 8);
+ } else if (pixelsPerRow & 8) {
+ LD_UB2(source, 16, src0, src1);
+ src0r = (v16u8)__msa_pckev_h((v8i16)src1, (v8i16)src0);
+ src0b = (v16u8)__msa_pckod_h((v8i16)src1, (v8i16)src0);
+ SLDI_B2_UB(src0g, src0a, src0r, src0b, src0g, src0a, 1);
+ src0gt = (v16u8)SLLI_B(src0g, 3);
+ src0g = (v16u8)SRLI_B(src0g, 5);
+ src0b = (v16u8)SRLI_B(src0b, 2);
+ src0a = (v16u8)SRLI_B(src0a, 7);
+ src0r = (v16u8)__msa_binsri_b((v16u8)src0r, (v16u8)src0g, 2);
+ src0b = (v16u8)__msa_binsri_b((v16u8)src0gt, (v16u8)src0b, 5);
+ src0b = (v16u8)__msa_binsri_b((v16u8)src0b, (v16u8)src0a, 0);
+ dst0 = (v8u16)__msa_ilvev_b((v16i8)src0r, (v16i8)src0b);
+ ST_UH(dst0, destination);
+ destination += 8;
+ }
+ }
+
+ pixelsPerRow &= 7;
+}
+
+ALWAYS_INLINE void packOneRowOfRGBA8ToUnsignedShort565MSA(
+ const uint8_t*& source,
+ uint16_t*& destination,
+ unsigned& pixelsPerRow) {
+ unsigned i;
+ v16u8 src0, src1, src2, src3, src4, src5, src6, src7;
+ v16u8 src0r, src0b, src1r, src1b, src2r, src2b, src3r, src3b;
+ v16u8 src0g = {0}, src1g = {0}, src2g = {0}, src3g = {0};
+ v16u8 src0gt, src1gt, src2gt, src3gt;
+ v8u16 dst0, dst1, dst2, dst3;
+
+ for (i = (pixelsPerRow >> 6); i--;) {
+ LD_UB8(source, 16, src0, src1, src2, src3, src4, src5, src6, src7);
+ PCKEV_H4_UB(src1, src0, src3, src2, src5, src4, src7, src6, src0r, src1r,
+ src2r, src3r);
+ PCKOD_H4_UB(src1, src0, src3, src2, src5, src4, src7, src6, src0b, src1b,
+ src2b, src3b);
+ SLDI_B2_UB(src0g, src1g, src0r, src1r, src0g, src1g, 1);
+ SLDI_B2_UB(src2g, src3g, src2r, src3r, src2g, src3g, 1);
+ src0gt = (v16u8)SLLI_B(src0g, 3);
+ src1gt = (v16u8)SLLI_B(src1g, 3);
+ src2gt = (v16u8)SLLI_B(src2g, 3);
+ src3gt = (v16u8)SLLI_B(src3g, 3);
+ SRLI_B4_UB(src0g, src1g, src2g, src3g, 5);
+ SRLI_B4_UB(src0b, src1b, src2b, src3b, 3);
+ BINSRI_B2_UB(src0r, src0g, src1r, src1g, src0r, src1r, 2);
+ BINSRI_B2_UB(src2r, src2g, src3r, src3g, src2r, src3r, 2);
+ BINSRI_B2_UB(src0gt, src0b, src1gt, src1b, src0b, src1b, 4);
+ BINSRI_B2_UB(src2gt, src2b, src3gt, src3b, src2b, src3b, 4);
+ ILVEV_B2_UH(src0b, src0r, src1b, src1r, dst0, dst1);
+ ILVEV_B2_UH(src2b, src2r, src3b, src3r, dst2, dst3);
+ LD_UB4(source, 16, src0, src1, src2, src3);
+ ST_UH4(dst0, dst1, dst2, dst3, destination, 8);
+ LD_UB4(source, 16, src4, src5, src6, src7);
+ PCKEV_H4_UB(src1, src0, src3, src2, src5, src4, src7, src6, src0r, src1r,
+ src2r, src3r);
+ PCKOD_H4_UB(src1, src0, src3, src2, src5, src4, src7, src6, src0b, src1b,
+ src2b, src3b);
+ SLDI_B2_UB(src0g, src1g, src0r, src1r, src0g, src1g, 1);
+ SLDI_B2_UB(src2g, src3g, src2r, src3r, src2g, src3g, 1);
+ src0gt = (v16u8)SLLI_B(src0g, 3);
+ src1gt = (v16u8)SLLI_B(src1g, 3);
+ src2gt = (v16u8)SLLI_B(src2g, 3);
+ src3gt = (v16u8)SLLI_B(src3g, 3);
+ SRLI_B4_UB(src0g, src1g, src2g, src3g, 5);
+ SRLI_B4_UB(src0b, src1b, src2b, src3b, 3);
+ BINSRI_B2_UB(src0r, src0g, src1r, src1g, src0r, src1r, 2);
+ BINSRI_B2_UB(src2r, src2g, src3r, src3g, src2r, src3r, 2);
+ BINSRI_B2_UB(src0gt, src0b, src1gt, src1b, src0b, src1b, 4);
+ BINSRI_B2_UB(src2gt, src2b, src3gt, src3b, src2b, src3b, 4);
+ ILVEV_B2_UH(src0b, src0r, src1b, src1r, dst0, dst1);
+ ILVEV_B2_UH(src2b, src2r, src3b, src3r, dst2, dst3);
+ ST_UH4(dst0, dst1, dst2, dst3, destination, 8);
+ }
+
+ if (pixelsPerRow & 63) {
+ if (pixelsPerRow & 32) {
+ if ((pixelsPerRow & 16) && (pixelsPerRow & 8)) {
+ LD_UB8(source, 16, src0, src1, src2, src3, src4, src5, src6, src7);
+ PCKEV_H4_UB(src1, src0, src3, src2, src5, src4, src7, src6, src0r,
+ src1r, src2r, src3r);
+ PCKOD_H4_UB(src1, src0, src3, src2, src5, src4, src7, src6, src0b,
+ src1b, src2b, src3b);
+ SLDI_B2_UB(src0g, src1g, src0r, src1r, src0g, src1g, 1);
+ SLDI_B2_UB(src2g, src3g, src2r, src3r, src2g, src3g, 1);
+ src0gt = (v16u8)SLLI_B(src0g, 3);
+ src1gt = (v16u8)SLLI_B(src1g, 3);
+ src2gt = (v16u8)SLLI_B(src2g, 3);
+ src3gt = (v16u8)SLLI_B(src3g, 3);
+ SRLI_B4_UB(src0g, src1g, src2g, src3g, 5);
+ SRLI_B4_UB(src0b, src1b, src2b, src3b, 3);
+ BINSRI_B2_UB(src0r, src0g, src1r, src1g, src0r, src1r, 2);
+ BINSRI_B2_UB(src2r, src2g, src3r, src3g, src2r, src3r, 2);
+ BINSRI_B2_UB(src0gt, src0b, src1gt, src1b, src0b, src1b, 4);
+ BINSRI_B2_UB(src2gt, src2b, src3gt, src3b, src2b, src3b, 4);
+ ILVEV_B2_UH(src0b, src0r, src1b, src1r, dst0, dst1);
+ ILVEV_B2_UH(src2b, src2r, src3b, src3r, dst2, dst3);
+ LD_UB6(source, 16, src0, src1, src2, src3, src4, src5);
+ ST_UH4(dst0, dst1, dst2, dst3, destination, 8);
+ PCKEV_H3_UB(src1, src0, src3, src2, src5, src4, src0r, src1r, src2r);
+ PCKOD_H3_UB(src1, src0, src3, src2, src5, src4, src0b, src1b, src2b);
+ src0g = SLDI_UB(src0g, src0r, 1);
+ src1g = SLDI_UB(src1g, src1r, 1);
+ src2g = SLDI_UB(src2g, src2r, 1);
+ src0gt = (v16u8)SLLI_B(src0g, 3);
+ src1gt = (v16u8)SLLI_B(src1g, 3);
+ src2gt = (v16u8)SLLI_B(src2g, 3);
+ SRLI_B3_UB(src0g, src1g, src2g, 5);
+ SRLI_B3_UB(src0b, src1b, src2b, 3);
+ BINSRI_B3_UB(src0r, src0g, src1r, src1g, src2r, src2g, src0r, src1r,
+ src2r, 2);
+ BINSRI_B3_UB(src0gt, src0b, src1gt, src1b, src2gt, src2b, src0b, src1b,
+ src2b, 4);
+ ILVEV_B3_UH(src0b, src0r, src1b, src1r, src2b, src2r, dst0, dst1, dst2);
+ ST_UH3(dst0, dst1, dst2, destination, 8);
+ } else if (pixelsPerRow & 16) {
+ LD_UB8(source, 16, src0, src1, src2, src3, src4, src5, src6, src7);
+ PCKEV_H4_UB(src1, src0, src3, src2, src5, src4, src7, src6, src0r,
+ src1r, src2r, src3r);
+ PCKOD_H4_UB(src1, src0, src3, src2, src5, src4, src7, src6, src0b,
+ src1b, src2b, src3b);
+ SLDI_B2_UB(src0g, src1g, src0r, src1r, src0g, src1g, 1);
+ SLDI_B2_UB(src2g, src3g, src2r, src3r, src2g, src3g, 1);
+ src0gt = (v16u8)SLLI_B(src0g, 3);
+ src1gt = (v16u8)SLLI_B(src1g, 3);
+ src2gt = (v16u8)SLLI_B(src2g, 3);
+ src3gt = (v16u8)SLLI_B(src3g, 3);
+ SRLI_B4_UB(src0g, src1g, src2g, src3g, 5);
+ SRLI_B4_UB(src0b, src1b, src2b, src3b, 3);
+ BINSRI_B2_UB(src0r, src0g, src1r, src1g, src0r, src1r, 2);
+ BINSRI_B2_UB(src2r, src2g, src3r, src3g, src2r, src3r, 2);
+ BINSRI_B2_UB(src0gt, src0b, src1gt, src1b, src0b, src1b, 4);
+ BINSRI_B2_UB(src2gt, src2b, src3gt, src3b, src2b, src3b, 4);
+ ILVEV_B2_UH(src0b, src0r, src1b, src1r, dst0, dst1);
+ ILVEV_B2_UH(src2b, src2r, src3b, src3r, dst2, dst3);
+ LD_UB4(source, 16, src0, src1, src2, src3);
+ ST_UH4(dst0, dst1, dst2, dst3, destination, 8);
+ PCKEV_H2_UB(src1, src0, src3, src2, src0r, src1r);
+ PCKOD_H2_UB(src1, src0, src3, src2, src0b, src1b);
+ SLDI_B2_UB(src0g, src1g, src0r, src1r, src0g, src1g, 1);
+ src0gt = (v16u8)SLLI_B(src0g, 3);
+ src1gt = (v16u8)SLLI_B(src1g, 3);
+ SRLI_B2_UB(src0g, src1g, 5);
+ SRLI_B2_UB(src0b, src1b, 3);
+ BINSRI_B2_UB(src0r, src0g, src1r, src1g, src0r, src1r, 2);
+ BINSRI_B2_UB(src0gt, src0b, src1gt, src1b, src0b, src1b, 4);
+ ILVEV_B2_UH(src0b, src0r, src1b, src1r, dst0, dst1);
+ ST_UH2(dst0, dst1, destination, 8);
+ } else if (pixelsPerRow & 8) {
+ LD_UB8(source, 16, src0, src1, src2, src3, src4, src5, src6, src7);
+ PCKEV_H4_UB(src1, src0, src3, src2, src5, src4, src7, src6, src0r,
+ src1r, src2r, src3r);
+ PCKOD_H4_UB(src1, src0, src3, src2, src5, src4, src7, src6, src0b,
+ src1b, src2b, src3b);
+ SLDI_B2_UB(src0g, src1g, src0r, src1r, src0g, src1g, 1);
+ SLDI_B2_UB(src2g, src3g, src2r, src3r, src2g, src3g, 1);
+ src0gt = (v16u8)SLLI_B(src0g, 3);
+ src1gt = (v16u8)SLLI_B(src1g, 3);
+ src2gt = (v16u8)SLLI_B(src2g, 3);
+ src3gt = (v16u8)SLLI_B(src3g, 3);
+ SRLI_B4_UB(src0g, src1g, src2g, src3g, 5);
+ SRLI_B4_UB(src0b, src1b, src2b, src3b, 3);
+ BINSRI_B2_UB(src0r, src0g, src1r, src1g, src0r, src1r, 2);
+ BINSRI_B2_UB(src2r, src2g, src3r, src3g, src2r, src3r, 2);
+ BINSRI_B2_UB(src0gt, src0b, src1gt, src1b, src0b, src1b, 4);
+ BINSRI_B2_UB(src2gt, src2b, src3gt, src3b, src2b, src3b, 4);
+ ILVEV_B2_UH(src0b, src0r, src1b, src1r, dst0, dst1);
+ ILVEV_B2_UH(src2b, src2r, src3b, src3r, dst2, dst3);
+ LD_UB2(source, 16, src0, src1);
+ ST_UH4(dst0, dst1, dst2, dst3, destination, 8);
+ src0r = (v16u8)__msa_pckev_h((v8i16)src1, (v8i16)src0);
+ src0b = (v16u8)__msa_pckod_h((v8i16)src1, (v8i16)src0);
+ src0g = SLDI_UB(src0g, src0r, 1);
+ src0gt = (v16u8)SLLI_B(src0g, 3);
+ src0g = (v16u8)SRLI_B(src0g, 5);
+ src0b = (v16u8)SRLI_B(src0b, 3);
+ src0r = (v16u8)__msa_binsri_b((v16u8)src0r, (v16u8)src0g, 2);
+ src0b = (v16u8)__msa_binsri_b((v16u8)src0gt, (v16u8)src0b, 4);
+ dst0 = (v8u16)__msa_ilvev_b((v16i8)src0r, (v16i8)src0b);
+ ST_UH(dst0, destination);
+ destination += 8;
+ } else {
+ LD_UB8(source, 16, src0, src1, src2, src3, src4, src5, src6, src7);
+ PCKEV_H4_UB(src1, src0, src3, src2, src5, src4, src7, src6, src0r,
+ src1r, src2r, src3r);
+ PCKOD_H4_UB(src1, src0, src3, src2, src5, src4, src7, src6, src0b,
+ src1b, src2b, src3b);
+ SLDI_B2_UB(src0g, src1g, src0r, src1r, src0g, src1g, 1);
+ SLDI_B2_UB(src2g, src3g, src2r, src3r, src2g, src3g, 1);
+ src0gt = (v16u8)SLLI_B(src0g, 3);
+ src1gt = (v16u8)SLLI_B(src1g, 3);
+ src2gt = (v16u8)SLLI_B(src2g, 3);
+ src3gt = (v16u8)SLLI_B(src3g, 3);
+ SRLI_B4_UB(src0g, src1g, src2g, src3g, 5);
+ SRLI_B4_UB(src0b, src1b, src2b, src3b, 3);
+ BINSRI_B2_UB(src0r, src0g, src1r, src1g, src0r, src1r, 2);
+ BINSRI_B2_UB(src2r, src2g, src3r, src3g, src2r, src3r, 2);
+ BINSRI_B2_UB(src0gt, src0b, src1gt, src1b, src0b, src1b, 4);
+ BINSRI_B2_UB(src2gt, src2b, src3gt, src3b, src2b, src3b, 4);
+ ILVEV_B2_UH(src0b, src0r, src1b, src1r, dst0, dst1);
+ ILVEV_B2_UH(src2b, src2r, src3b, src3r, dst2, dst3);
+ ST_UH4(dst0, dst1, dst2, dst3, destination, 8);
+ }
+ } else if ((pixelsPerRow & 16) && (pixelsPerRow & 8)) {
+ LD_UB6(source, 16, src0, src1, src2, src3, src4, src5);
+ PCKEV_H3_UB(src1, src0, src3, src2, src5, src4, src0r, src1r, src2r);
+ PCKOD_H3_UB(src1, src0, src3, src2, src5, src4, src0b, src1b, src2b);
+ src0g = SLDI_UB(src0g, src0r, 1);
+ src1g = SLDI_UB(src1g, src1r, 1);
+ src2g = SLDI_UB(src2g, src2r, 1);
+ src0gt = (v16u8)SLLI_B(src0g, 3);
+ src1gt = (v16u8)SLLI_B(src1g, 3);
+ src2gt = (v16u8)SLLI_B(src2g, 3);
+ SRLI_B3_UB(src0g, src1g, src2g, 5);
+ SRLI_B3_UB(src0b, src1b, src2b, 3);
+ BINSRI_B3_UB(src0r, src0g, src1r, src1g, src2r, src2g, src0r, src1r,
+ src2r, 2);
+ BINSRI_B3_UB(src0gt, src0b, src1gt, src1b, src2gt, src2b, src0b, src1b,
+ src2b, 4);
+ ILVEV_B3_UH(src0b, src0r, src1b, src1r, src2b, src2r, dst0, dst1, dst2);
+ ST_UH3(dst0, dst1, dst2, destination, 8);
+ } else if (pixelsPerRow & 16) {
+ LD_UB4(source, 16, src0, src1, src2, src3);
+ PCKEV_H2_UB(src1, src0, src3, src2, src0r, src1r);
+ PCKOD_H2_UB(src1, src0, src3, src2, src0b, src1b);
+ SLDI_B2_UB(src0g, src1g, src0r, src1r, src0g, src1g, 1);
+ src0gt = (v16u8)SLLI_B(src0g, 3);
+ src1gt = (v16u8)SLLI_B(src1g, 3);
+ SRLI_B2_UB(src0g, src1g, 5);
+ SRLI_B2_UB(src0b, src1b, 3);
+ BINSRI_B2_UB(src0r, src0g, src1r, src1g, src0r, src1r, 2);
+ BINSRI_B2_UB(src0gt, src0b, src1gt, src1b, src0b, src1b, 4);
+ ILVEV_B2_UH(src0b, src0r, src1b, src1r, dst0, dst1);
+ ST_UH2(dst0, dst1, destination, 8);
+ } else if (pixelsPerRow & 8) {
+ LD_UB2(source, 16, src0, src1);
+ src0r = (v16u8)__msa_pckev_h((v8i16)src1, (v8i16)src0);
+ src0b = (v16u8)__msa_pckod_h((v8i16)src1, (v8i16)src0);
+ src0g = SLDI_UB(src0g, src0r, 1);
+ src0gt = (v16u8)SLLI_B(src0g, 3);
+ src0g = (v16u8)SRLI_B(src0g, 5);
+ src0b = (v16u8)SRLI_B(src0b, 3);
+ src0r = (v16u8)__msa_binsri_b((v16u8)src0r, (v16u8)src0g, 2);
+ src0b = (v16u8)__msa_binsri_b((v16u8)src0gt, (v16u8)src0b, 4);
+ dst0 = (v8u16)__msa_ilvev_b((v16i8)src0r, (v16i8)src0b);
+ ST_UH(dst0, destination);
+ destination += 8;
+ }
+ }
+
+ pixelsPerRow &= 7;
+}
+
+ALWAYS_INLINE void packOneRowOfRGBA8ToUnsignedShort4444MSA(
+ const uint8_t*& source,
+ uint16_t*& destination,
+ unsigned& pixelsPerRow) {
+ unsigned i;
+ v16u8 src0, src1, src2, src3, src4, src5, src6, src7;
+ v16u8 vec0, vec1, vec2, vec3, vec4, vec5, vec6, vec7;
+ v8u16 dst0, dst1, dst2, dst3;
+
+ for (i = (pixelsPerRow >> 5); i--;) {
+ LD_UB8(source, 16, src0, src1, src2, src3, src4, src5, src6, src7);
+ SRLI_H4_UB(src0, src1, src2, src3, vec0, vec1, vec2, vec3, 12);
+ SRLI_H4_UB(src4, src5, src6, src7, vec4, vec5, vec6, vec7, 12);
+ BINSLI_B2_UB(vec0, src0, vec1, src1, vec0, vec1, 3);
+ BINSLI_B2_UB(vec2, src2, vec3, src3, vec2, vec3, 3);
+ BINSLI_B2_UB(vec4, src4, vec5, src5, vec4, vec5, 3);
+ BINSLI_B2_UB(vec6, src6, vec7, src7, vec6, vec7, 3);
+ PCKEV_B4_UH(vec1, vec0, vec3, vec2, vec5, vec4, vec7, vec6, dst0, dst1,
+ dst2, dst3);
+ SHF_B4_UH(dst0, dst1, dst2, dst3, 177);
+ ST_UH4(dst0, dst1, dst2, dst3, destination, 8);
+ }
+
+ if (pixelsPerRow & 31) {
+ if (pixelsPerRow & 16) {
+ if ((pixelsPerRow & 8) && (pixelsPerRow & 4)) {
+ LD_UB7(source, 16, src0, src1, src2, src3, src4, src5, src6);
+ SRLI_H4_UB(src0, src1, src2, src3, vec0, vec1, vec2, vec3, 12);
+ SRLI_H2_UB(src4, src5, vec4, vec5, 12);
+ vec6 = (v16u8)SRLI_H(src6, 12);
+ BINSLI_B2_UB(vec0, src0, vec1, src1, vec0, vec1, 3);
+ BINSLI_B2_UB(vec2, src2, vec3, src3, vec2, vec3, 3);
+ BINSLI_B2_UB(vec4, src4, vec5, src5, vec4, vec5, 3);
+ vec6 = (v16u8)__msa_binsli_b((v16u8)vec6, (v16u8)src6, 3);
+ PCKEV_B2_UH(vec1, vec0, vec3, vec2, dst0, dst1);
+ PCKEV_B2_UH(vec5, vec4, vec6, vec6, dst2, dst3);
+ SHF_B4_UH(dst0, dst1, dst2, dst3, 177);
+ ST_UH3(dst0, dst1, dst2, destination, 8);
+ ST8x1_UB(dst3, destination);
+ destination += 4;
+ } else if (pixelsPerRow & 8) {
+ LD_UB6(source, 16, src0, src1, src2, src3, src4, src5);
+ SRLI_H4_UB(src0, src1, src2, src3, vec0, vec1, vec2, vec3, 12);
+ SRLI_H2_UB(src4, src5, vec4, vec5, 12);
+ BINSLI_B2_UB(vec0, src0, vec1, src1, vec0, vec1, 3);
+ BINSLI_B2_UB(vec2, src2, vec3, src3, vec2, vec3, 3);
+ BINSLI_B2_UB(vec4, src4, vec5, src5, vec4, vec5, 3);
+ PCKEV_B3_UH(vec1, vec0, vec3, vec2, vec5, vec4, dst0, dst1, dst2);
+ SHF_B3_UH(dst0, dst1, dst2, 177);
+ ST_UH3(dst0, dst1, dst2, destination, 8);
+ } else if (pixelsPerRow & 4) {
+ LD_UB5(source, 16, src0, src1, src2, src3, src4);
+ SRLI_H4_UB(src0, src1, src2, src3, vec0, vec1, vec2, vec3, 12);
+ vec4 = (v16u8)SRLI_H(src4, 12);
+ BINSLI_B2_UB(vec0, src0, vec1, src1, vec0, vec1, 3);
+ BINSLI_B2_UB(vec2, src2, vec3, src3, vec2, vec3, 3);
+ vec4 = (v16u8)__msa_binsli_b((v16u8)vec4, (v16u8)src4, 3);
+ PCKEV_B3_UH(vec1, vec0, vec3, vec2, vec4, vec4, dst0, dst1, dst2);
+ SHF_B3_UH(dst0, dst1, dst2, 177);
+ ST_UH2(dst0, dst1, destination, 8);
+ ST8x1_UB(dst2, destination);
+ destination += 4;
+ } else {
+ LD_UB4(source, 16, src0, src1, src2, src3);
+ SRLI_H4_UB(src0, src1, src2, src3, vec0, vec1, vec2, vec3, 12);
+ BINSLI_B2_UB(vec0, src0, vec1, src1, vec0, vec1, 3);
+ BINSLI_B2_UB(vec2, src2, vec3, src3, vec2, vec3, 3);
+ PCKEV_B2_UH(vec1, vec0, vec3, vec2, dst0, dst1);
+ SHF_B2_UH(dst0, dst1, 177);
+ ST_UH2(dst0, dst1, destination, 8);
+ }
+ } else if ((pixelsPerRow & 8) && (pixelsPerRow & 4)) {
+ LD_UB3(source, 16, src0, src1, src2);
+ SRLI_H2_UB(src0, src1, vec0, vec1, 12);
+ vec2 = (v16u8)SRLI_H(src2, 12);
+ BINSLI_B2_UB(vec0, src0, vec1, src1, vec0, vec1, 3);
+ vec2 = (v16u8)__msa_binsli_b((v16u8)vec2, (v16u8)src2, 3);
+ PCKEV_B2_UH(vec1, vec0, vec2, vec2, dst0, dst1);
+ SHF_B2_UH(dst0, dst1, 177);
+ ST_UH(dst0, destination);
+ destination += 8;
+ ST8x1_UB(dst1, destination);
+ destination += 4;
+ } else if (pixelsPerRow & 16) {
+ LD_UB4(source, 16, src0, src1, src2, src3);
+ SRLI_H4_UB(src0, src1, src2, src3, vec0, vec1, vec2, vec3, 12);
+ BINSLI_B2_UB(vec0, src0, vec1, src1, vec0, vec1, 3);
+ BINSLI_B2_UB(vec2, src2, vec3, src3, vec2, vec3, 3);
+ PCKEV_B2_UH(vec1, vec0, vec3, vec2, dst0, dst1);
+ SHF_B2_UH(dst0, dst1, 177);
+ ST_UH2(dst0, dst1, destination, 8);
+ } else if (pixelsPerRow & 8) {
+ LD_UB2(source, 16, src0, src1);
+ SRLI_H2_UB(src0, src1, vec0, vec1, 12);
+ BINSLI_B2_UB(vec0, src0, vec1, src1, vec0, vec1, 3);
+ dst0 = (v8u16)__msa_pckev_b((v16i8)vec1, (v16i8)vec0);
+ dst0 = (v8u16)__msa_shf_b((v16i8)dst0, 177);
+ ST_UH(dst0, destination);
+ destination += 8;
+ } else if (pixelsPerRow & 4) {
+ src0 = LD_UB(source);
+ source += 16;
+ vec0 = (v16u8)SRLI_H(src0, 12);
+ vec0 = (v16u8)__msa_binsli_b((v16u8)vec0, (v16u8)src0, 3);
+ dst0 = (v8u16)__msa_pckev_b((v16i8)vec0, (v16i8)vec0);
+ dst0 = (v8u16)__msa_shf_b((v16i8)dst0, 177);
+ ST8x1_UB(dst0, destination);
+ destination += 4;
+ }
+ }
+
+ pixelsPerRow &= 3;
+}
+
+ALWAYS_INLINE void packOneRowOfRGBA8LittleToR8MSA(const uint8_t*& source,
+ uint8_t*& destination,
+ unsigned& pixelsPerRow) {
+ unsigned i;
+ v16u8 src0, src1, src2, src3, src4, src5, src6, src7;
+ v16u8 src0A, src1A, src2A, src3A, src4A, src5A, src6A, src7A;
+ v16u8 src0R, src1R, src2R, src3R, src4R, src5R, src6R, src7R;
+ v16u8 dst0, dst1, dst2, dst3, dst4, dst5, dst6, dst7;
+ v4f32 fsrc0A, fsrc1A, fsrc2A, fsrc3A, fsrc4A, fsrc5A, fsrc6A, fsrc7A;
+ v4f32 fsrc0R, fsrc1R, fsrc2R, fsrc3R, fsrc4R, fsrc5R, fsrc6R, fsrc7R;
+ v4f32 fdst0R, fdst1R, fdst2R, fdst3R, fdst4R, fdst5R, fdst6R, fdst7R;
+ const v16u8 alphaMask = {0, 0, 0, 255, 0, 0, 0, 255,
+ 0, 0, 0, 255, 0, 0, 0, 255};
+ const v4u32 vCnst255 = (v4u32)__msa_ldi_w(255);
+ const v4f32 vfCnst255 = __msa_ffint_u_w(vCnst255);
+
+ for (i = (pixelsPerRow >> 5); i--;) {
+ LD_UB8(source, 16, src0, src1, src2, src3, src4, src5, src6, src7);
+ CEQI_B4_UB(src0, src1, src2, src3, 0, src0A, src1A, src2A, src3A);
+ CEQI_B4_UB(src4, src5, src6, src7, 0, src4A, src5A, src6A, src7A);
+ src0A = __msa_bmnz_v(src0, alphaMask, src0A);
+ src1A = __msa_bmnz_v(src1, alphaMask, src1A);
+ src2A = __msa_bmnz_v(src2, alphaMask, src2A);
+ src3A = __msa_bmnz_v(src3, alphaMask, src3A);
+ src4A = __msa_bmnz_v(src4, alphaMask, src4A);
+ src5A = __msa_bmnz_v(src5, alphaMask, src5A);
+ src6A = __msa_bmnz_v(src6, alphaMask, src6A);
+ src7A = __msa_bmnz_v(src7, alphaMask, src7A);
+ AND_V4_UB(src0A, src1A, src2A, src3A, alphaMask, src0A, src1A, src2A,
+ src3A);
+ AND_V4_UB(src4A, src5A, src6A, src7A, alphaMask, src4A, src5A, src6A,
+ src7A);
+ src0A = SLDI_UB(src0A, src0A, 3);
+ src1A = SLDI_UB(src1A, src1A, 3);
+ src2A = SLDI_UB(src2A, src2A, 3);
+ src3A = SLDI_UB(src3A, src3A, 3);
+ src4A = SLDI_UB(src4A, src4A, 3);
+ src5A = SLDI_UB(src5A, src5A, 3);
+ src6A = SLDI_UB(src6A, src6A, 3);
+ src7A = SLDI_UB(src7A, src7A, 3);
+ AND_V4_UB(src0, src1, src2, src3, vCnst255, src0R, src1R, src2R, src3R);
+ AND_V4_UB(src4, src5, src6, src7, vCnst255, src4R, src5R, src6R, src7R);
+ FFINTU_W4_SP(src0A, src1A, src2A, src3A, fsrc0A, fsrc1A, fsrc2A, fsrc3A);
+ FFINTU_W4_SP(src4A, src5A, src6A, src7A, fsrc4A, fsrc5A, fsrc6A, fsrc7A);
+ FFINTU_W4_SP(src0R, src1R, src2R, src3R, fsrc0R, fsrc1R, fsrc2R, fsrc3R);
+ FFINTU_W4_SP(src4R, src5R, src6R, src7R, fsrc4R, fsrc5R, fsrc6R, fsrc7R);
+ DIV4(vfCnst255, fsrc0A, vfCnst255, fsrc1A, vfCnst255, fsrc2A, vfCnst255,
+ fsrc3A, fsrc0A, fsrc1A, fsrc2A, fsrc3A);
+ DIV4(vfCnst255, fsrc4A, vfCnst255, fsrc5A, vfCnst255, fsrc6A, vfCnst255,
+ fsrc7A, fsrc4A, fsrc5A, fsrc6A, fsrc7A);
+ MUL4(fsrc0R, fsrc0A, fsrc1R, fsrc1A, fsrc2R, fsrc2A, fsrc3R, fsrc3A, fdst0R,
+ fdst1R, fdst2R, fdst3R);
+ MUL4(fsrc4R, fsrc4A, fsrc5R, fsrc5A, fsrc6R, fsrc6A, fsrc7R, fsrc7A, fdst4R,
+ fdst5R, fdst6R, fdst7R);
+ FTRUNCU_W4_UB(fdst0R, fdst1R, fdst2R, fdst3R, dst0, dst1, dst2, dst3);
+ FTRUNCU_W4_UB(fdst4R, fdst5R, fdst6R, fdst7R, dst4, dst5, dst6, dst7);
+ PCKEV_H4_UB(dst1, dst0, dst3, dst2, dst5, dst4, dst7, dst6, dst0, dst2,
+ dst4, dst6);
+ PCKEV_B2_UB(dst2, dst0, dst6, dst4, dst0, dst1);
+ ST_UB2(dst0, dst1, destination, 16);
+ }
+
+ if (pixelsPerRow & 31) {
+ if ((pixelsPerRow & 16) && (pixelsPerRow & 8)) {
+ LD_UB6(source, 16, src0, src1, src2, src3, src4, src5);
+ CEQI_B4_UB(src0, src1, src2, src3, 0, src0A, src1A, src2A, src3A);
+ CEQI_B2_UB(src4, src5, 0, src4A, src5A);
+ src0A = __msa_bmnz_v(src0, alphaMask, src0A);
+ src1A = __msa_bmnz_v(src1, alphaMask, src1A);
+ src2A = __msa_bmnz_v(src2, alphaMask, src2A);
+ src3A = __msa_bmnz_v(src3, alphaMask, src3A);
+ src4A = __msa_bmnz_v(src4, alphaMask, src4A);
+ src5A = __msa_bmnz_v(src5, alphaMask, src5A);
+ AND_V4_UB(src0A, src1A, src2A, src3A, alphaMask, src0A, src1A, src2A,
+ src3A);
+ AND_V2_UB(src4A, src5A, alphaMask, src4A, src5A);
+ src0A = SLDI_UB(src0A, src0A, 3);
+ src1A = SLDI_UB(src1A, src1A, 3);
+ src2A = SLDI_UB(src2A, src2A, 3);
+ src3A = SLDI_UB(src3A, src3A, 3);
+ src4A = SLDI_UB(src4A, src4A, 3);
+ src5A = SLDI_UB(src5A, src5A, 3);
+ AND_V4_UB(src0, src1, src2, src3, vCnst255, src0R, src1R, src2R, src3R);
+ AND_V2_UB(src4, src5, vCnst255, src4R, src5R);
+ FFINTU_W4_SP(src0A, src1A, src2A, src3A, fsrc0A, fsrc1A, fsrc2A, fsrc3A);
+ FFINTU_W2_SP(src4A, src5A, fsrc4A, fsrc5A);
+ FFINTU_W4_SP(src0R, src1R, src2R, src3R, fsrc0R, fsrc1R, fsrc2R, fsrc3R);
+ FFINTU_W2_SP(src4R, src5R, fsrc4R, fsrc5R);
+ DIV4(vfCnst255, fsrc0A, vfCnst255, fsrc1A, vfCnst255, fsrc2A, vfCnst255,
+ fsrc3A, fsrc0A, fsrc1A, fsrc2A, fsrc3A);
+ DIV2(vfCnst255, fsrc4A, vfCnst255, fsrc5A, fsrc4A, fsrc5A);
+ MUL4(fsrc0R, fsrc0A, fsrc1R, fsrc1A, fsrc2R, fsrc2A, fsrc3R, fsrc3A,
+ fdst0R, fdst1R, fdst2R, fdst3R);
+ MUL2(fsrc4R, fsrc4A, fsrc5R, fsrc5A, fdst4R, fdst5R);
+ FTRUNCU_W4_UB(fdst0R, fdst1R, fdst2R, fdst3R, dst0, dst1, dst2, dst3);
+ FTRUNCU_W2_UB(fdst4R, fdst5R, dst4, dst5);
+ PCKEV_H3_UB(dst1, dst0, dst3, dst2, dst5, dst4, dst0, dst2, dst4);
+ PCKEV_B2_UB(dst2, dst0, dst4, dst4, dst0, dst1);
+ ST_UB(dst0, destination);
+ destination += 16;
+ ST8x1_UB(dst1, destination);
+ destination += 8;
+ } else if (pixelsPerRow & 16) {
+ LD_UB4(source, 16, src0, src1, src2, src3);
+ CEQI_B4_UB(src0, src1, src2, src3, 0, src0A, src1A, src2A, src3A);
+ src0A = __msa_bmnz_v(src0, alphaMask, src0A);
+ src1A = __msa_bmnz_v(src1, alphaMask, src1A);
+ src2A = __msa_bmnz_v(src2, alphaMask, src2A);
+ src3A = __msa_bmnz_v(src3, alphaMask, src3A);
+ AND_V4_UB(src0A, src1A, src2A, src3A, alphaMask, src0A, src1A, src2A,
+ src3A);
+ src0A = SLDI_UB(src0A, src0A, 3);
+ src1A = SLDI_UB(src1A, src1A, 3);
+ src2A = SLDI_UB(src2A, src2A, 3);
+ src3A = SLDI_UB(src3A, src3A, 3);
+ AND_V4_UB(src0, src1, src2, src3, vCnst255, src0R, src1R, src2R, src3R);
+ FFINTU_W4_SP(src0A, src1A, src2A, src3A, fsrc0A, fsrc1A, fsrc2A, fsrc3A);
+ FFINTU_W4_SP(src0R, src1R, src2R, src3R, fsrc0R, fsrc1R, fsrc2R, fsrc3R);
+ DIV4(vfCnst255, fsrc0A, vfCnst255, fsrc1A, vfCnst255, fsrc2A, vfCnst255,
+ fsrc3A, fsrc0A, fsrc1A, fsrc2A, fsrc3A);
+ MUL4(fsrc0R, fsrc0A, fsrc1R, fsrc1A, fsrc2R, fsrc2A, fsrc3R, fsrc3A,
+ fdst0R, fdst1R, fdst2R, fdst3R);
+ FTRUNCU_W4_UB(fdst0R, fdst1R, fdst2R, fdst3R, dst0, dst1, dst2, dst3);
+ PCKEV_H2_UB(dst1, dst0, dst3, dst2, dst0, dst2);
+ dst0 = (v16u8)__msa_pckev_b((v16i8)dst2, (v16i8)dst0);
+ ST_UB(dst0, destination);
+ destination += 16;
+ } else if (pixelsPerRow & 8) {
+ LD_UB2(source, 16, src0, src1);
+ CEQI_B2_UB(src0, src1, 0, src0A, src1A);
+ src0A = __msa_bmnz_v(src0, alphaMask, src0A);
+ src1A = __msa_bmnz_v(src1, alphaMask, src1A);
+ AND_V2_UB(src0A, src1A, alphaMask, src0A, src1A);
+ src0A = SLDI_UB(src0A, src0A, 3);
+ src1A = SLDI_UB(src1A, src1A, 3);
+ AND_V2_UB(src0, src1, vCnst255, src0R, src1R);
+ FFINTU_W2_SP(src0A, src1A, fsrc0A, fsrc1A);
+ FFINTU_W2_SP(src0R, src1R, fsrc0R, fsrc1R);
+ DIV2(vfCnst255, fsrc0A, vfCnst255, fsrc1A, fsrc0A, fsrc1A);
+ MUL2(fsrc0R, fsrc0A, fsrc1R, fsrc1A, fdst0R, fdst1R);
+ FTRUNCU_W2_UB(fdst0R, fdst1R, dst0, dst1);
+ dst0 = (v16u8)__msa_pckev_h((v8i16)dst1, (v8i16)dst0);
+ dst0 = (v16u8)__msa_pckev_b((v16i8)dst0, (v16i8)dst0);
+ ST8x1_UB(dst0, destination);
+ destination += 8;
+ }
+ }
+
+ pixelsPerRow &= 7;
+}
+
+ALWAYS_INLINE void packOneRowOfRGBA8LittleToRA8MSA(const uint8_t*& source,
+ uint8_t*& destination,
+ unsigned& pixelsPerRow) {
+ unsigned i;
+ v16u8 src0, src1, src2, src3, src4, src5, src6, src7;
+ v16u8 src0A, src1A, src2A, src3A, src4A, src5A, src6A, src7A;
+ v16u8 src0R, src1R, src2R, src3R, src4R, src5R, src6R, src7R;
+ v16u8 dst0, dst1, dst2, dst3, dst4, dst5, dst6, dst7;
+ v4f32 fsrc0A, fsrc1A, fsrc2A, fsrc3A, fsrc4A, fsrc5A, fsrc6A, fsrc7A;
+ v4f32 fsrc0R, fsrc1R, fsrc2R, fsrc3R, fsrc4R, fsrc5R, fsrc6R, fsrc7R;
+ v4f32 fdst0R, fdst1R, fdst2R, fdst3R, fdst4R, fdst5R, fdst6R, fdst7R;
+ const v16u8 alphaMask = {0, 0, 0, 255, 0, 0, 0, 255,
+ 0, 0, 0, 255, 0, 0, 0, 255};
+ const v16i8 vshfm = {0, 19, 4, 23, 8, 27, 12, 31, 0, 0, 0, 0, 0, 0, 0, 0};
+ const v4u32 vCnst255 = (v4u32)__msa_ldi_w(255);
+ const v4f32 vfCnst255 = __msa_ffint_u_w(vCnst255);
+
+ for (i = (pixelsPerRow >> 5); i--;) {
+ LD_UB8(source, 16, src0, src1, src2, src3, src4, src5, src6, src7);
+ CEQI_B4_UB(src0, src1, src2, src3, 0, src0A, src1A, src2A, src3A);
+ CEQI_B4_UB(src4, src5, src6, src7, 0, src4A, src5A, src6A, src7A);
+ src0A = __msa_bmnz_v(src0, alphaMask, src0A);
+ src1A = __msa_bmnz_v(src1, alphaMask, src1A);
+ src2A = __msa_bmnz_v(src2, alphaMask, src2A);
+ src3A = __msa_bmnz_v(src3, alphaMask, src3A);
+ src4A = __msa_bmnz_v(src4, alphaMask, src4A);
+ src5A = __msa_bmnz_v(src5, alphaMask, src5A);
+ src6A = __msa_bmnz_v(src6, alphaMask, src6A);
+ src7A = __msa_bmnz_v(src7, alphaMask, src7A);
+ AND_V4_UB(src0A, src1A, src2A, src3A, alphaMask, src0A, src1A, src2A,
+ src3A);
+ AND_V4_UB(src4A, src5A, src6A, src7A, alphaMask, src4A, src5A, src6A,
+ src7A);
+ src0A = SLDI_UB(src0A, src0A, 3);
+ src1A = SLDI_UB(src1A, src1A, 3);
+ src2A = SLDI_UB(src2A, src2A, 3);
+ src3A = SLDI_UB(src3A, src3A, 3);
+ src4A = SLDI_UB(src4A, src4A, 3);
+ src5A = SLDI_UB(src5A, src5A, 3);
+ src6A = SLDI_UB(src6A, src6A, 3);
+ src7A = SLDI_UB(src7A, src7A, 3);
+ AND_V4_UB(src0, src1, src2, src3, vCnst255, src0R, src1R, src2R, src3R);
+ AND_V4_UB(src4, src5, src6, src7, vCnst255, src4R, src5R, src6R, src7R);
+ FFINTU_W4_SP(src0A, src1A, src2A, src3A, fsrc0A, fsrc1A, fsrc2A, fsrc3A);
+ FFINTU_W4_SP(src4A, src5A, src6A, src7A, fsrc4A, fsrc5A, fsrc6A, fsrc7A);
+ FFINTU_W4_SP(src0R, src1R, src2R, src3R, fsrc0R, fsrc1R, fsrc2R, fsrc3R);
+ FFINTU_W4_SP(src4R, src5R, src6R, src7R, fsrc4R, fsrc5R, fsrc6R, fsrc7R);
+ DIV4(vfCnst255, fsrc0A, vfCnst255, fsrc1A, vfCnst255, fsrc2A, vfCnst255,
+ fsrc3A, fsrc0A, fsrc1A, fsrc2A, fsrc3A);
+ DIV4(vfCnst255, fsrc4A, vfCnst255, fsrc5A, vfCnst255, fsrc6A, vfCnst255,
+ fsrc7A, fsrc4A, fsrc5A, fsrc6A, fsrc7A);
+ MUL4(fsrc0R, fsrc0A, fsrc1R, fsrc1A, fsrc2R, fsrc2A, fsrc3R, fsrc3A, fdst0R,
+ fdst1R, fdst2R, fdst3R);
+ MUL4(fsrc4R, fsrc4A, fsrc5R, fsrc5A, fsrc6R, fsrc6A, fsrc7R, fsrc7A, fdst4R,
+ fdst5R, fdst6R, fdst7R);
+ FTRUNCU_W4_UB(fdst0R, fdst1R, fdst2R, fdst3R, dst0, dst1, dst2, dst3);
+ FTRUNCU_W4_UB(fdst4R, fdst5R, fdst6R, fdst7R, dst4, dst5, dst6, dst7);
+ dst0 = VSHF_UB(dst0, src0, vshfm);
+ dst1 = VSHF_UB(dst1, src1, vshfm);
+ dst2 = VSHF_UB(dst2, src2, vshfm);
+ dst3 = VSHF_UB(dst3, src3, vshfm);
+ dst4 = VSHF_UB(dst4, src4, vshfm);
+ dst5 = VSHF_UB(dst5, src5, vshfm);
+ dst6 = VSHF_UB(dst6, src6, vshfm);
+ dst7 = VSHF_UB(dst7, src7, vshfm);
+ ILVR_D4_UB(dst1, dst0, dst3, dst2, dst5, dst4, dst7, dst6, dst0, dst1, dst2,
+ dst3);
+ ST_UB4(dst0, dst1, dst2, dst3, destination, 16);
+ }
+
+ if (pixelsPerRow & 31) {
+ if ((pixelsPerRow & 16) && (pixelsPerRow & 8)) {
+ LD_UB6(source, 16, src0, src1, src2, src3, src4, src5);
+ CEQI_B4_UB(src0, src1, src2, src3, 0, src0A, src1A, src2A, src3A);
+ CEQI_B2_UB(src4, src5, 0, src4A, src5A);
+ src0A = __msa_bmnz_v(src0, alphaMask, src0A);
+ src1A = __msa_bmnz_v(src1, alphaMask, src1A);
+ src2A = __msa_bmnz_v(src2, alphaMask, src2A);
+ src3A = __msa_bmnz_v(src3, alphaMask, src3A);
+ src4A = __msa_bmnz_v(src4, alphaMask, src4A);
+ src5A = __msa_bmnz_v(src5, alphaMask, src5A);
+ AND_V4_UB(src0A, src1A, src2A, src3A, alphaMask, src0A, src1A, src2A,
+ src3A);
+ AND_V2_UB(src4A, src5A, alphaMask, src4A, src5A);
+ src0A = SLDI_UB(src0A, src0A, 3);
+ src1A = SLDI_UB(src1A, src1A, 3);
+ src2A = SLDI_UB(src2A, src2A, 3);
+ src3A = SLDI_UB(src3A, src3A, 3);
+ src4A = SLDI_UB(src4A, src4A, 3);
+ src5A = SLDI_UB(src5A, src5A, 3);
+ AND_V4_UB(src0, src1, src2, src3, vCnst255, src0R, src1R, src2R, src3R);
+ AND_V2_UB(src4, src5, vCnst255, src4R, src5R);
+ FFINTU_W4_SP(src0A, src1A, src2A, src3A, fsrc0A, fsrc1A, fsrc2A, fsrc3A);
+ FFINTU_W2_SP(src4A, src5A, fsrc4A, fsrc5A);
+ FFINTU_W4_SP(src0R, src1R, src2R, src3R, fsrc0R, fsrc1R, fsrc2R, fsrc3R);
+ FFINTU_W2_SP(src4R, src5R, fsrc4R, fsrc5R);
+ DIV4(vfCnst255, fsrc0A, vfCnst255, fsrc1A, vfCnst255, fsrc2A, vfCnst255,
+ fsrc3A, fsrc0A, fsrc1A, fsrc2A, fsrc3A);
+ DIV2(vfCnst255, fsrc4A, vfCnst255, fsrc5A, fsrc4A, fsrc5A);
+ MUL4(fsrc0R, fsrc0A, fsrc1R, fsrc1A, fsrc2R, fsrc2A, fsrc3R, fsrc3A,
+ fdst0R, fdst1R, fdst2R, fdst3R);
+ MUL2(fsrc4R, fsrc4A, fsrc5R, fsrc5A, fdst4R, fdst5R);
+ FTRUNCU_W4_UB(fdst0R, fdst1R, fdst2R, fdst3R, dst0, dst1, dst2, dst3);
+ FTRUNCU_W2_UB(fdst4R, fdst5R, dst4, dst5);
+ dst0 = VSHF_UB(dst0, src0, vshfm);
+ dst1 = VSHF_UB(dst1, src1, vshfm);
+ dst2 = VSHF_UB(dst2, src2, vshfm);
+ dst3 = VSHF_UB(dst3, src3, vshfm);
+ dst4 = VSHF_UB(dst4, src4, vshfm);
+ dst5 = VSHF_UB(dst5, src5, vshfm);
+ ILVR_D3_UB(dst1, dst0, dst3, dst2, dst5, dst4, dst0, dst1, dst2);
+ ST_UB3(dst0, dst1, dst2, destination, 16);
+ } else if (pixelsPerRow & 16) {
+ LD_UB4(source, 16, src0, src1, src2, src3);
+ CEQI_B4_UB(src0, src1, src2, src3, 0, src0A, src1A, src2A, src3A);
+ src0A = __msa_bmnz_v(src0, alphaMask, src0A);
+ src1A = __msa_bmnz_v(src1, alphaMask, src1A);
+ src2A = __msa_bmnz_v(src2, alphaMask, src2A);
+ src3A = __msa_bmnz_v(src3, alphaMask, src3A);
+ AND_V4_UB(src0A, src1A, src2A, src3A, alphaMask, src0A, src1A, src2A,
+ src3A);
+ src0A = SLDI_UB(src0A, src0A, 3);
+ src1A = SLDI_UB(src1A, src1A, 3);
+ src2A = SLDI_UB(src2A, src2A, 3);
+ src3A = SLDI_UB(src3A, src3A, 3);
+ AND_V4_UB(src0, src1, src2, src3, vCnst255, src0R, src1R, src2R, src3R);
+ FFINTU_W4_SP(src0A, src1A, src2A, src3A, fsrc0A, fsrc1A, fsrc2A, fsrc3A);
+ FFINTU_W4_SP(src0R, src1R, src2R, src3R, fsrc0R, fsrc1R, fsrc2R, fsrc3R);
+ DIV4(vfCnst255, fsrc0A, vfCnst255, fsrc1A, vfCnst255, fsrc2A, vfCnst255,
+ fsrc3A, fsrc0A, fsrc1A, fsrc2A, fsrc3A);
+ MUL4(fsrc0R, fsrc0A, fsrc1R, fsrc1A, fsrc2R, fsrc2A, fsrc3R, fsrc3A,
+ fdst0R, fdst1R, fdst2R, fdst3R);
+ FTRUNCU_W4_UB(fdst0R, fdst1R, fdst2R, fdst3R, dst0, dst1, dst2, dst3);
+ dst0 = VSHF_UB(dst0, src0, vshfm);
+ dst1 = VSHF_UB(dst1, src1, vshfm);
+ dst2 = VSHF_UB(dst2, src2, vshfm);
+ dst3 = VSHF_UB(dst3, src3, vshfm);
+ ILVR_D2_UB(dst1, dst0, dst3, dst2, dst0, dst1);
+ ST_UB2(dst0, dst1, destination, 16);
+ } else if (pixelsPerRow & 8) {
+ LD_UB2(source, 16, src0, src1);
+ CEQI_B2_UB(src0, src1, 0, src0A, src1A);
+ src0A = __msa_bmnz_v(src0, alphaMask, src0A);
+ src1A = __msa_bmnz_v(src1, alphaMask, src1A);
+ AND_V2_UB(src0A, src1A, alphaMask, src0A, src1A);
+ src0A = SLDI_UB(src0A, src0A, 3);
+ src1A = SLDI_UB(src1A, src1A, 3);
+ AND_V2_UB(src0, src1, vCnst255, src0R, src1R);
+ FFINTU_W2_SP(src0A, src1A, fsrc0A, fsrc1A);
+ FFINTU_W2_SP(src0R, src1R, fsrc0R, fsrc1R);
+ DIV2(vfCnst255, fsrc0A, vfCnst255, fsrc1A, fsrc0A, fsrc1A);
+ MUL2(fsrc0R, fsrc0A, fsrc1R, fsrc1A, fdst0R, fdst1R);
+ FTRUNCU_W2_UB(fdst0R, fdst1R, dst0, dst1);
+ dst0 = VSHF_UB(dst0, src0, vshfm);
+ dst1 = VSHF_UB(dst1, src1, vshfm);
+ dst0 = (v16u8)__msa_ilvr_d((v2i64)dst1, (v2i64)dst0);
+ ST_UB(dst0, destination);
+ destination += 16;
+ }
+ }
+
+ pixelsPerRow &= 7;
+}
+
+} // namespace SIMD
+
+} // namespace blink
+
+#endif // HAVE_MIPS_MSA_INTRINSICS
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CPU_MIPS_WEBGL_IMAGE_CONVERSION_MSA_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/cpu/x86/webgl_image_conversion_sse.h b/chromium/third_party/blink/renderer/platform/graphics/cpu/x86/webgl_image_conversion_sse.h
new file mode 100644
index 00000000000..6438ee77835
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/cpu/x86/webgl_image_conversion_sse.h
@@ -0,0 +1,200 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CPU_X86_WEBGL_IMAGE_CONVERSION_SSE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CPU_X86_WEBGL_IMAGE_CONVERSION_SSE_H_
+
+#include "build/build_config.h"
+
+#if defined(ARCH_CPU_X86_FAMILY)
+#include <emmintrin.h>
+
+namespace blink {
+
+namespace SIMD {
+
+ALWAYS_INLINE void UnpackOneRowOfRGBA4444LittleToRGBA8(
+ const uint16_t*& source,
+ uint8_t*& destination,
+ unsigned& pixels_per_row) {
+ __m128i immediate0x0f = _mm_set1_epi16(0x0F);
+ unsigned pixels_per_row_trunc = (pixels_per_row / 8) * 8;
+ for (unsigned i = 0; i < pixels_per_row_trunc; i += 8) {
+ __m128i packed_value = _mm_loadu_si128((__m128i*)source);
+ __m128i r = _mm_srli_epi16(packed_value, 12);
+ __m128i g = _mm_and_si128(_mm_srli_epi16(packed_value, 8), immediate0x0f);
+ __m128i b = _mm_and_si128(_mm_srli_epi16(packed_value, 4), immediate0x0f);
+ __m128i a = _mm_and_si128(packed_value, immediate0x0f);
+ __m128i component_r = _mm_or_si128(_mm_slli_epi16(r, 4), r);
+ __m128i component_g = _mm_or_si128(_mm_slli_epi16(g, 4), g);
+ __m128i component_b = _mm_or_si128(_mm_slli_epi16(b, 4), b);
+ __m128i component_a = _mm_or_si128(_mm_slli_epi16(a, 4), a);
+
+ __m128i component_rg =
+ _mm_or_si128(_mm_slli_epi16(component_g, 8), component_r);
+ __m128i component_ba =
+ _mm_or_si128(_mm_slli_epi16(component_a, 8), component_b);
+ __m128i component_rgba1 = _mm_unpackhi_epi16(component_rg, component_ba);
+ __m128i component_rgba2 = _mm_unpacklo_epi16(component_rg, component_ba);
+
+ _mm_storeu_si128((__m128i*)destination, component_rgba2);
+ _mm_storeu_si128((__m128i*)(destination + 16), component_rgba1);
+
+ source += 8;
+ destination += 32;
+ }
+ pixels_per_row -= pixels_per_row_trunc;
+}
+
+ALWAYS_INLINE void UnpackOneRowOfRGBA5551LittleToRGBA8(
+ const uint16_t*& source,
+ uint8_t*& destination,
+ unsigned& pixels_per_row) {
+ __m128i immediate0x1f = _mm_set1_epi16(0x1F);
+ __m128i immediate0x7 = _mm_set1_epi16(0x7);
+ __m128i immediate0x1 = _mm_set1_epi16(0x1);
+ unsigned pixels_per_row_trunc = (pixels_per_row / 8) * 8;
+ for (unsigned i = 0; i < pixels_per_row_trunc; i += 8) {
+ __m128i packed_value = _mm_loadu_si128((__m128i*)source);
+ __m128i r = _mm_srli_epi16(packed_value, 11);
+ __m128i g = _mm_and_si128(_mm_srli_epi16(packed_value, 6), immediate0x1f);
+ __m128i b = _mm_and_si128(_mm_srli_epi16(packed_value, 1), immediate0x1f);
+ __m128i component_r =
+ _mm_or_si128(_mm_slli_epi16(r, 3), _mm_and_si128(r, immediate0x7));
+ __m128i component_g =
+ _mm_or_si128(_mm_slli_epi16(g, 3), _mm_and_si128(g, immediate0x7));
+ __m128i component_b =
+ _mm_or_si128(_mm_slli_epi16(b, 3), _mm_and_si128(b, immediate0x7));
+ __m128i component_a = _mm_cmpeq_epi16(
+ _mm_and_si128(packed_value, immediate0x1), immediate0x1);
+
+ __m128i component_rg =
+ _mm_or_si128(_mm_slli_epi16(component_g, 8), component_r);
+ __m128i component_ba =
+ _mm_or_si128(_mm_slli_epi16(component_a, 8), component_b);
+ __m128i component_rgba1 = _mm_unpackhi_epi16(component_rg, component_ba);
+ __m128i component_rgba2 = _mm_unpacklo_epi16(component_rg, component_ba);
+
+ _mm_storeu_si128((__m128i*)destination, component_rgba2);
+ _mm_storeu_si128((__m128i*)(destination + 16), component_rgba1);
+
+ source += 8;
+ destination += 32;
+ }
+ pixels_per_row -= pixels_per_row_trunc;
+}
+
+ALWAYS_INLINE void PackOneRowOfRGBA8LittleToRA8(const uint8_t*& source,
+ uint8_t*& destination,
+ unsigned& pixels_per_row) {
+ float tmp[4];
+ unsigned pixels_per_row_trunc = (pixels_per_row / 4) * 4;
+ const __m128 max_pixel_value = _mm_set1_ps(255.0);
+ for (unsigned i = 0; i < pixels_per_row_trunc; i += 4) {
+ __m128 scale_factor = _mm_set_ps(
+ source[15] ? source[15] : 255.0, source[11] ? source[11] : 255.0,
+ source[7] ? source[7] : 255.0, source[3] ? source[3] : 255.0);
+ __m128 source_r = _mm_set_ps(source[12], source[8], source[4], source[0]);
+
+ source_r = _mm_mul_ps(source_r, _mm_div_ps(max_pixel_value, scale_factor));
+
+ _mm_storeu_ps(tmp, source_r);
+
+ destination[0] = static_cast<uint8_t>(tmp[0]);
+ destination[1] = static_cast<uint8_t>(source[3]);
+ destination[2] = static_cast<uint8_t>(tmp[1]);
+ destination[3] = static_cast<uint8_t>(source[7]);
+ destination[4] = static_cast<uint8_t>(tmp[2]);
+ destination[5] = static_cast<uint8_t>(source[11]);
+ destination[6] = static_cast<uint8_t>(tmp[3]);
+ destination[7] = static_cast<uint8_t>(source[15]);
+
+ source += 16;
+ destination += 8;
+ }
+ pixels_per_row -= pixels_per_row_trunc;
+}
+
+ALWAYS_INLINE void UnpackOneRowOfBGRA8LittleToRGBA8(const uint32_t*& source,
+ uint32_t*& destination,
+ unsigned& pixels_per_row) {
+ __m128i bgra, rgba;
+ __m128i br_mask = _mm_set1_epi32(0x00ff00ff);
+ __m128i ga_mask = _mm_set1_epi32(0xff00ff00);
+ unsigned pixels_per_row_trunc = (pixels_per_row / 4) * 4;
+
+ for (unsigned i = 0; i < pixels_per_row_trunc; i += 4) {
+ bgra = _mm_loadu_si128((const __m128i*)(source));
+ rgba = _mm_shufflehi_epi16(_mm_shufflelo_epi16(bgra, 0xB1), 0xB1);
+
+ rgba = _mm_or_si128(_mm_and_si128(rgba, br_mask),
+ _mm_and_si128(bgra, ga_mask));
+ _mm_storeu_si128((__m128i*)(destination), rgba);
+
+ source += 4;
+ destination += 4;
+ }
+
+ pixels_per_row -= pixels_per_row_trunc;
+}
+
+ALWAYS_INLINE void PackOneRowOfRGBA8LittleToR8(const uint8_t*& source,
+ uint8_t*& destination,
+ unsigned& pixels_per_row) {
+ float tmp[4];
+ unsigned pixels_per_row_trunc = (pixels_per_row / 4) * 4;
+
+ for (unsigned i = 0; i < pixels_per_row_trunc; i += 4) {
+ __m128 scale =
+ _mm_set_ps(source[15] ? source[15] : 255, source[11] ? source[11] : 255,
+ source[7] ? source[7] : 255, source[3] ? source[3] : 255);
+
+ __m128 source_r = _mm_set_ps(source[12], source[8], source[4], source[0]);
+ source_r = _mm_mul_ps(source_r, _mm_div_ps(_mm_set1_ps(255), scale));
+
+ _mm_storeu_ps(tmp, source_r);
+ destination[0] = static_cast<uint8_t>(tmp[0]);
+ destination[1] = static_cast<uint8_t>(tmp[1]);
+ destination[2] = static_cast<uint8_t>(tmp[2]);
+ destination[3] = static_cast<uint8_t>(tmp[3]);
+
+ source += 16;
+ destination += 4;
+ }
+
+ pixels_per_row -= pixels_per_row_trunc;
+}
+
+// This function deliberately doesn't mutate the incoming source, destination,
+// or pixelsPerRow arguments, since it always handles the full row.
+ALWAYS_INLINE void PackOneRowOfRGBA8LittleToRGBA8(const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ float tmp[4];
+ float scale;
+
+ for (unsigned i = 0; i < pixels_per_row; i++) {
+ scale = source[3] ? source[3] : 255;
+ __m128 source_r = _mm_set_ps(0, source[2], source[1], source[0]);
+
+ source_r =
+ _mm_mul_ps(source_r, _mm_div_ps(_mm_set1_ps(255), _mm_set1_ps(scale)));
+
+ _mm_storeu_ps(tmp, source_r);
+ destination[0] = static_cast<uint8_t>(tmp[0]);
+ destination[1] = static_cast<uint8_t>(tmp[1]);
+ destination[2] = static_cast<uint8_t>(tmp[2]);
+ destination[3] = source[3];
+
+ source += 4;
+ destination += 4;
+ }
+}
+
+} // namespace SIMD
+} // namespace blink
+
+#endif // ARCH_CPU_X86_FAMILY
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CPU_X86_WEBGL_IMAGE_CONVERSION_SSE_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/crossfade_generated_image.cc b/chromium/third_party/blink/renderer/platform/graphics/crossfade_generated_image.cc
new file mode 100644
index 00000000000..59dc7d42520
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/crossfade_generated_image.cc
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2011 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/graphics/crossfade_generated_image.h"
+
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+
+namespace blink {
+
+CrossfadeGeneratedImage::CrossfadeGeneratedImage(
+ scoped_refptr<Image> from_image,
+ scoped_refptr<Image> to_image,
+ float percentage,
+ FloatSize crossfade_size,
+ const FloatSize& size)
+ : GeneratedImage(size),
+ from_image_(std::move(from_image)),
+ to_image_(std::move(to_image)),
+ percentage_(percentage),
+ crossfade_size_(crossfade_size) {}
+
+void CrossfadeGeneratedImage::DrawCrossfade(PaintCanvas* canvas,
+ const PaintFlags& flags,
+ ImageClampingMode clamp_mode,
+ ImageDecodingMode decode_mode) {
+ FloatRect from_image_rect(FloatPoint(), FloatSize(from_image_->Size()));
+ FloatRect to_image_rect(FloatPoint(), FloatSize(to_image_->Size()));
+ FloatRect dest_rect((FloatPoint()), crossfade_size_);
+
+ // TODO(junov): The various effects encoded into paint should probably be
+ // applied here instead of inside the layer. This probably faulty behavior
+ // was maintained in order to preserve pre-existing behavior while refactoring
+ // this code. This should be investigated further. crbug.com/472634
+ PaintFlags layer_flags;
+ layer_flags.setBlendMode(flags.getBlendMode());
+ PaintCanvasAutoRestore ar(canvas, false);
+ canvas->saveLayer(nullptr, &layer_flags);
+
+ PaintFlags image_flags(flags);
+ image_flags.setBlendMode(SkBlendMode::kSrcOver);
+ int image_alpha = ClampedAlphaForBlending(1 - percentage_);
+ image_flags.setAlpha(image_alpha > 255 ? 255 : image_alpha);
+ image_flags.setAntiAlias(flags.isAntiAlias());
+ // TODO(junov): This code should probably be propagating the
+ // RespectImageOrientationEnum from CrossfadeGeneratedImage::draw(). Code was
+ // written this way during refactoring to avoid modifying existing behavior,
+ // but this warrants further investigation. crbug.com/472634
+ from_image_->Draw(canvas, image_flags, dest_rect, from_image_rect,
+ kDoNotRespectImageOrientation, clamp_mode, decode_mode);
+ image_flags.setBlendMode(SkBlendMode::kPlus);
+ image_alpha = ClampedAlphaForBlending(percentage_);
+ image_flags.setAlpha(image_alpha > 255 ? 255 : image_alpha);
+ to_image_->Draw(canvas, image_flags, dest_rect, to_image_rect,
+ kDoNotRespectImageOrientation, clamp_mode, decode_mode);
+}
+
+void CrossfadeGeneratedImage::Draw(PaintCanvas* canvas,
+ const PaintFlags& flags,
+ const FloatRect& dst_rect,
+ const FloatRect& src_rect,
+ RespectImageOrientationEnum,
+ ImageClampingMode clamp_mode,
+ ImageDecodingMode decode_mode) {
+ // Draw nothing if either of the images hasn't loaded yet.
+ if (from_image_ == Image::NullImage() || to_image_ == Image::NullImage())
+ return;
+
+ PaintCanvasAutoRestore ar(canvas, true);
+ canvas->clipRect(dst_rect);
+ canvas->translate(dst_rect.X(), dst_rect.Y());
+ if (dst_rect.Size() != src_rect.Size())
+ canvas->scale(dst_rect.Width() / src_rect.Width(),
+ dst_rect.Height() / src_rect.Height());
+ canvas->translate(-src_rect.X(), -src_rect.Y());
+
+ DrawCrossfade(canvas, flags, clamp_mode, decode_mode);
+}
+
+void CrossfadeGeneratedImage::DrawTile(GraphicsContext& context,
+ const FloatRect& src_rect) {
+ // Draw nothing if either of the images hasn't loaded yet.
+ if (from_image_ == Image::NullImage() || to_image_ == Image::NullImage())
+ return;
+
+ PaintFlags flags = context.FillFlags();
+ flags.setBlendMode(SkBlendMode::kSrcOver);
+ flags.setAntiAlias(context.ShouldAntialias());
+ FloatRect dest_rect((FloatPoint()), crossfade_size_);
+ flags.setFilterQuality(
+ context.ComputeFilterQuality(this, dest_rect, src_rect));
+ DrawCrossfade(context.Canvas(), flags, kClampImageToSourceRect, kSyncDecode);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/crossfade_generated_image.h b/chromium/third_party/blink/renderer/platform/graphics/crossfade_generated_image.h
new file mode 100644
index 00000000000..4fc129564ab
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/crossfade_generated_image.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2011 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CROSSFADE_GENERATED_IMAGE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CROSSFADE_GENERATED_IMAGE_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/geometry/int_size.h"
+#include "third_party/blink/renderer/platform/graphics/generated_image.h"
+#include "third_party/blink/renderer/platform/graphics/image.h"
+#include "third_party/blink/renderer/platform/graphics/image_observer.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT CrossfadeGeneratedImage final : public GeneratedImage {
+ public:
+ static scoped_refptr<CrossfadeGeneratedImage> Create(
+ scoped_refptr<Image> from_image,
+ scoped_refptr<Image> to_image,
+ float percentage,
+ FloatSize crossfade_size,
+ const FloatSize& size) {
+ return base::AdoptRef(
+ new CrossfadeGeneratedImage(std::move(from_image), std::move(to_image),
+ percentage, crossfade_size, size));
+ }
+
+ bool UsesContainerSize() const override { return false; }
+ bool HasRelativeSize() const override { return false; }
+
+ IntSize Size() const override { return FlooredIntSize(crossfade_size_); }
+
+ protected:
+ void Draw(PaintCanvas*,
+ const PaintFlags&,
+ const FloatRect&,
+ const FloatRect&,
+ RespectImageOrientationEnum,
+ ImageClampingMode,
+ ImageDecodingMode) override;
+ void DrawTile(GraphicsContext&, const FloatRect&) final;
+
+ CrossfadeGeneratedImage(scoped_refptr<Image> from_image,
+ scoped_refptr<Image> to_image,
+ float percentage,
+ FloatSize crossfade_size,
+ const FloatSize&);
+
+ private:
+ void DrawCrossfade(PaintCanvas*,
+ const PaintFlags&,
+ ImageClampingMode,
+ ImageDecodingMode);
+
+ scoped_refptr<Image> from_image_;
+ scoped_refptr<Image> to_image_;
+
+ float percentage_;
+ FloatSize crossfade_size_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/graphics/dash_array.h b/chromium/third_party/blink/renderer/platform/graphics/dash_array.h
new file mode 100644
index 00000000000..78571dcd5a2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/dash_array.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2008 Dirk Schulze <vbs85@gmx.de>
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DASH_ARRAY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DASH_ARRAY_H_
+
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+typedef float DashArrayElement;
+
+typedef Vector<DashArrayElement> DashArray;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DASH_ARRAY_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/decoding_image_generator.cc b/chromium/third_party/blink/renderer/platform/graphics/decoding_image_generator.cc
new file mode 100644
index 00000000000..3a8baa8ce3f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/decoding_image_generator.cc
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/graphics/decoding_image_generator.h"
+
+#include <utility>
+
+#include <memory>
+#include "third_party/blink/renderer/platform/graphics/image_frame_generator.h"
+#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h"
+#include "third_party/blink/renderer/platform/image-decoders/segment_reader.h"
+#include "third_party/blink/renderer/platform/instrumentation/platform_instrumentation.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/skia/include/core/SkData.h"
+
+namespace blink {
+
+// static
+std::unique_ptr<SkImageGenerator>
+DecodingImageGenerator::CreateAsSkImageGenerator(sk_sp<SkData> data) {
+ scoped_refptr<SegmentReader> segment_reader =
+ SegmentReader::CreateFromSkData(std::move(data));
+ // We just need the size of the image, so we have to temporarily create an
+ // ImageDecoder. Since we only need the size, the premul and gamma settings
+ // don't really matter.
+ std::unique_ptr<ImageDecoder> decoder = ImageDecoder::Create(
+ segment_reader, true, ImageDecoder::kAlphaPremultiplied,
+ ColorBehavior::TransformToSRGB());
+ if (!decoder || !decoder->IsSizeAvailable())
+ return nullptr;
+
+ const IntSize size = decoder->Size();
+ const SkImageInfo info =
+ SkImageInfo::MakeN32(size.Width(), size.Height(), kPremul_SkAlphaType,
+ decoder->ColorSpaceForSkImages());
+
+ scoped_refptr<ImageFrameGenerator> frame = ImageFrameGenerator::Create(
+ SkISize::Make(size.Width(), size.Height()), false,
+ decoder->GetColorBehavior(), decoder->GetSupportedDecodeSizes());
+ if (!frame)
+ return nullptr;
+
+ std::vector<FrameMetadata> frames = {FrameMetadata()};
+ sk_sp<DecodingImageGenerator> generator = DecodingImageGenerator::Create(
+ std::move(frame), info, std::move(segment_reader), std::move(frames),
+ PaintImage::GetNextContentId(), true);
+ return std::make_unique<SkiaPaintImageGenerator>(
+ std::move(generator), PaintImage::kDefaultFrameIndex);
+}
+
+// static
+sk_sp<DecodingImageGenerator> DecodingImageGenerator::Create(
+ scoped_refptr<ImageFrameGenerator> frame_generator,
+ const SkImageInfo& info,
+ scoped_refptr<SegmentReader> data,
+ std::vector<FrameMetadata> frames,
+ PaintImage::ContentId content_id,
+ bool all_data_received) {
+ return sk_sp<DecodingImageGenerator>(new DecodingImageGenerator(
+ std::move(frame_generator), info, std::move(data), std::move(frames),
+ content_id, all_data_received));
+}
+
+DecodingImageGenerator::DecodingImageGenerator(
+ scoped_refptr<ImageFrameGenerator> frame_generator,
+ const SkImageInfo& info,
+ scoped_refptr<SegmentReader> data,
+ std::vector<FrameMetadata> frames,
+ PaintImage::ContentId complete_frame_content_id,
+ bool all_data_received)
+ : PaintImageGenerator(info, std::move(frames)),
+ frame_generator_(std::move(frame_generator)),
+ data_(std::move(data)),
+ all_data_received_(all_data_received),
+ can_yuv_decode_(false),
+ complete_frame_content_id_(complete_frame_content_id) {}
+
+DecodingImageGenerator::~DecodingImageGenerator() = default;
+
+sk_sp<SkData> DecodingImageGenerator::GetEncodedData() const {
+ TRACE_EVENT0("blink", "DecodingImageGenerator::refEncodedData");
+
+ // getAsSkData() may require copying, but the clients of this function are
+ // serializers, which want the data even if it requires copying, and even
+ // if the data is incomplete. (Otherwise they would potentially need to
+ // decode the partial image in order to re-encode it.)
+ return data_->GetAsSkData();
+}
+
+bool DecodingImageGenerator::GetPixels(const SkImageInfo& dst_info,
+ void* pixels,
+ size_t row_bytes,
+ size_t frame_index,
+ uint32_t lazy_pixel_ref) {
+ TRACE_EVENT1("blink", "DecodingImageGenerator::getPixels", "frame index",
+ static_cast<int>(frame_index));
+
+ // Implementation only supports decoding to a supported size.
+ if (dst_info.dimensions() != GetSupportedDecodeSize(dst_info.dimensions())) {
+ return false;
+ }
+
+ // TODO(vmpstr): We could do the color type conversion here by getting N32
+ // colortype decode first, and then converting to whatever was requested.
+ if (dst_info.colorType() != kN32_SkColorType) {
+ return false;
+ }
+
+ // Skip the check for alphaType. blink::ImageFrame may have changed the
+ // owning SkBitmap to kOpaque_SkAlphaType after fully decoding the image
+ // frame, so if we see a request for opaque, that is ok even if our initial
+ // alpha type was not opaque.
+
+ // Pass decodeColorSpace to the decoder. That is what we can expect the
+ // output to be.
+ SkColorSpace* decode_color_space = GetSkImageInfo().colorSpace();
+ SkImageInfo decode_info =
+ dst_info.makeColorSpace(sk_ref_sp(decode_color_space));
+
+ const bool needs_color_xform =
+ decode_color_space && dst_info.colorSpace() &&
+ !SkColorSpace::Equals(decode_color_space, dst_info.colorSpace());
+ ImageDecoder::AlphaOption alpha_option = ImageDecoder::kAlphaPremultiplied;
+ if (needs_color_xform && !decode_info.isOpaque()) {
+ alpha_option = ImageDecoder::kAlphaNotPremultiplied;
+ decode_info = decode_info.makeAlphaType(kUnpremul_SkAlphaType);
+ }
+
+ PlatformInstrumentation::WillDecodeLazyPixelRef(lazy_pixel_ref);
+ const bool decoded = frame_generator_->DecodeAndScale(
+ data_.get(), all_data_received_, frame_index, decode_info, pixels,
+ row_bytes, alpha_option);
+ PlatformInstrumentation::DidDecodeLazyPixelRef();
+
+ if (decoded && needs_color_xform) {
+ TRACE_EVENT0("blink", "DecodingImageGenerator::getPixels - apply xform");
+ SkPixmap src(decode_info, pixels, row_bytes);
+
+ // kIgnore ensures that we perform the premultiply (if necessary) in the dst
+ // space.
+ const bool converted = src.readPixels(dst_info, pixels, row_bytes, 0, 0,
+ SkTransferFunctionBehavior::kIgnore);
+ DCHECK(converted);
+ }
+
+ return decoded;
+}
+
+bool DecodingImageGenerator::QueryYUV8(SkYUVSizeInfo* size_info,
+ SkYUVColorSpace* color_space) const {
+ // YUV decoding does not currently support progressive decoding. See comment
+ // in ImageFrameGenerator.h.
+ if (!can_yuv_decode_ || !all_data_received_)
+ return false;
+
+ TRACE_EVENT0("blink", "DecodingImageGenerator::queryYUV8");
+
+ if (color_space)
+ *color_space = kJPEG_SkYUVColorSpace;
+
+ return frame_generator_->GetYUVComponentSizes(data_.get(), size_info);
+}
+
+bool DecodingImageGenerator::GetYUV8Planes(const SkYUVSizeInfo& size_info,
+ void* planes[3],
+ size_t frame_index,
+ uint32_t lazy_pixel_ref) {
+ // YUV decoding does not currently support progressive decoding. See comment
+ // in ImageFrameGenerator.h.
+ DCHECK(can_yuv_decode_);
+ DCHECK(all_data_received_);
+
+ TRACE_EVENT0("blink", "DecodingImageGenerator::getYUV8Planes");
+
+ PlatformInstrumentation::WillDecodeLazyPixelRef(lazy_pixel_ref);
+ bool decoded =
+ frame_generator_->DecodeToYUV(data_.get(), frame_index, size_info.fSizes,
+ planes, size_info.fWidthBytes);
+ PlatformInstrumentation::DidDecodeLazyPixelRef();
+
+ return decoded;
+}
+
+SkISize DecodingImageGenerator::GetSupportedDecodeSize(
+ const SkISize& requested_size) const {
+ return frame_generator_->GetSupportedDecodeSize(requested_size);
+}
+
+PaintImage::ContentId DecodingImageGenerator::GetContentIdForFrame(
+ size_t frame_index) const {
+ DCHECK_LT(frame_index, GetFrameMetadata().size());
+
+ // If we have all the data for the image, or this particular frame, we can
+ // consider the decoded frame constant.
+ if (all_data_received_ || GetFrameMetadata().at(frame_index).complete)
+ return complete_frame_content_id_;
+
+ return PaintImageGenerator::GetContentIdForFrame(frame_index);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/decoding_image_generator.h b/chromium/third_party/blink/renderer/platform/graphics/decoding_image_generator.h
new file mode 100644
index 00000000000..1058d2c61d1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/decoding_image_generator.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2013 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:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DECODING_IMAGE_GENERATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DECODING_IMAGE_GENERATOR_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_image.h"
+#include "third_party/blink/renderer/platform/image-decoders/segment_reader.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/skia/include/core/SkImageInfo.h"
+
+class SkData;
+
+namespace blink {
+
+class ImageFrameGenerator;
+
+// Implements SkImageGenerator, used by SkPixelRef to populate a discardable
+// memory with a decoded image frame. ImageFrameGenerator does the actual
+// decoding.
+class PLATFORM_EXPORT DecodingImageGenerator final
+ : public PaintImageGenerator {
+ USING_FAST_MALLOC(DecodingImageGenerator);
+ WTF_MAKE_NONCOPYABLE(DecodingImageGenerator);
+
+ public:
+ // Aside from tests, this is used to create a decoder from SkData in Skia
+ // (exported via WebImageGenerator and set via
+ // SkGraphics::SetImageGeneratorFromEncodedDataFactory)
+ static std::unique_ptr<SkImageGenerator> CreateAsSkImageGenerator(
+ sk_sp<SkData>);
+
+ static sk_sp<DecodingImageGenerator> Create(
+ scoped_refptr<ImageFrameGenerator>,
+ const SkImageInfo&,
+ scoped_refptr<SegmentReader>,
+ std::vector<FrameMetadata>,
+ PaintImage::ContentId,
+ bool all_data_received);
+
+ ~DecodingImageGenerator() override;
+
+ void SetCanYUVDecode(bool yes) { can_yuv_decode_ = yes; }
+
+ // PaintImageGenerator implementation.
+ sk_sp<SkData> GetEncodedData() const override;
+ bool GetPixels(const SkImageInfo&,
+ void* pixels,
+ size_t row_bytes,
+ size_t frame_index,
+ uint32_t lazy_pixel_ref) override;
+ bool QueryYUV8(SkYUVSizeInfo*, SkYUVColorSpace*) const override;
+ bool GetYUV8Planes(const SkYUVSizeInfo&,
+ void* planes[3],
+ size_t frame_index,
+ uint32_t lazy_pixel_ref) override;
+ SkISize GetSupportedDecodeSize(const SkISize& requested_size) const override;
+ PaintImage::ContentId GetContentIdForFrame(size_t frame_index) const override;
+
+ private:
+ DecodingImageGenerator(scoped_refptr<ImageFrameGenerator>,
+ const SkImageInfo&,
+ scoped_refptr<SegmentReader>,
+ std::vector<FrameMetadata>,
+ PaintImage::ContentId,
+ bool all_data_received);
+
+ scoped_refptr<ImageFrameGenerator> frame_generator_;
+ const scoped_refptr<SegmentReader> data_; // Data source.
+ const bool all_data_received_;
+ bool can_yuv_decode_;
+ const PaintImage::ContentId complete_frame_content_id_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DECODING_IMAGE_GENERATOR_H__
diff --git a/chromium/third_party/blink/renderer/platform/graphics/decoding_image_generator_test.cc b/chromium/third_party/blink/renderer/platform/graphics/decoding_image_generator_test.cc
new file mode 100644
index 00000000000..550823fcc58
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/decoding_image_generator_test.cc
@@ -0,0 +1,68 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/decoding_image_generator.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/image-decoders/image_decoder_test_helpers.h"
+#include "third_party/blink/renderer/platform/image-decoders/segment_reader.h"
+
+namespace blink {
+
+namespace {
+
+constexpr unsigned kTooShortForSignature = 5;
+
+scoped_refptr<SegmentReader> CreateSegmentReader(char* reference_data,
+ size_t data_length) {
+ PrepareReferenceData(reference_data, data_length);
+ scoped_refptr<SharedBuffer> data = SharedBuffer::Create();
+ data->Append(reference_data, data_length);
+ return SegmentReader::CreateFromSharedBuffer(std::move(data));
+}
+
+} // namespace
+
+class DecodingImageGeneratorTest : public testing::Test {};
+
+TEST_F(DecodingImageGeneratorTest, Create) {
+ scoped_refptr<SharedBuffer> reference_data =
+ ReadFile(kDecodersTestingDir, "radient.gif");
+ scoped_refptr<SegmentReader> reader =
+ SegmentReader::CreateFromSharedBuffer(std::move(reference_data));
+ std::unique_ptr<SkImageGenerator> generator =
+ DecodingImageGenerator::CreateAsSkImageGenerator(reader->GetAsSkData());
+ // Sanity-check the image to make sure it was loaded.
+ EXPECT_EQ(generator->getInfo().width(), 32);
+ EXPECT_EQ(generator->getInfo().height(), 32);
+}
+
+TEST_F(DecodingImageGeneratorTest, CreateWithNoSize) {
+ // Construct dummy image data that produces no valid size from the
+ // ImageDecoder.
+ char reference_data[kDefaultTestSize];
+ EXPECT_EQ(nullptr, DecodingImageGenerator::CreateAsSkImageGenerator(
+ CreateSegmentReader(reference_data, kDefaultTestSize)
+ ->GetAsSkData()));
+}
+
+TEST_F(DecodingImageGeneratorTest, CreateWithNullImageDecoder) {
+ // Construct dummy image data that will produce a null image decoder
+ // due to data being too short for a signature.
+ char reference_data[kTooShortForSignature];
+ EXPECT_EQ(nullptr,
+ DecodingImageGenerator::CreateAsSkImageGenerator(
+ CreateSegmentReader(reference_data, kTooShortForSignature)
+ ->GetAsSkData()));
+}
+
+// TODO(wkorman): Test Create with a null ImageFrameGenerator. We'd
+// need a way to intercept construction of the instance (and could do
+// same for ImageDecoder above to reduce fragility of knowing a short
+// signature will produce a null ImageDecoder). Note that it's not
+// clear that it's possible to end up with a null ImageFrameGenerator,
+// so maybe we can just remove that check from
+// DecodingImageGenerator::Create.
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/deferred_image_decoder.cc b/chromium/third_party/blink/renderer/platform/graphics/deferred_image_decoder.cc
new file mode 100644
index 00000000000..29a8629c141
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/deferred_image_decoder.cc
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2012 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/graphics/deferred_image_decoder.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/platform/graphics/decoding_image_generator.h"
+#include "third_party/blink/renderer/platform/graphics/image_decoding_store.h"
+#include "third_party/blink/renderer/platform/graphics/image_frame_generator.h"
+#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
+#include "third_party/blink/renderer/platform/image-decoders/segment_reader.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/skia/include/core/SkImage.h"
+
+namespace blink {
+
+struct DeferredFrameData {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ WTF_MAKE_NONCOPYABLE(DeferredFrameData);
+
+ public:
+ DeferredFrameData()
+ : orientation_(kDefaultImageOrientation),
+ is_received_(false) {}
+
+ ImageOrientation orientation_;
+ TimeDelta duration_;
+ bool is_received_;
+};
+
+std::unique_ptr<DeferredImageDecoder> DeferredImageDecoder::Create(
+ scoped_refptr<SharedBuffer> data,
+ bool data_complete,
+ ImageDecoder::AlphaOption alpha_option,
+ const ColorBehavior& color_behavior) {
+ std::unique_ptr<ImageDecoder> metadata_decoder =
+ ImageDecoder::Create(data, data_complete, alpha_option, color_behavior);
+ if (!metadata_decoder)
+ return nullptr;
+
+ std::unique_ptr<DeferredImageDecoder> decoder(
+ new DeferredImageDecoder(std::move(metadata_decoder)));
+
+ // Since we've just instantiated a fresh decoder, there's no need to reset its
+ // data.
+ decoder->SetDataInternal(std::move(data), data_complete, false);
+
+ return decoder;
+}
+
+std::unique_ptr<DeferredImageDecoder> DeferredImageDecoder::CreateForTesting(
+ std::unique_ptr<ImageDecoder> metadata_decoder) {
+ return base::WrapUnique(
+ new DeferredImageDecoder(std::move(metadata_decoder)));
+}
+
+DeferredImageDecoder::DeferredImageDecoder(
+ std::unique_ptr<ImageDecoder> metadata_decoder)
+ : metadata_decoder_(std::move(metadata_decoder)),
+ repetition_count_(kAnimationNone),
+ all_data_received_(false),
+ can_yuv_decode_(false),
+ has_hot_spot_(false),
+ complete_frame_content_id_(PaintImage::GetNextContentId()) {}
+
+DeferredImageDecoder::~DeferredImageDecoder() = default;
+
+String DeferredImageDecoder::FilenameExtension() const {
+ return metadata_decoder_ ? metadata_decoder_->FilenameExtension()
+ : filename_extension_;
+}
+
+sk_sp<PaintImageGenerator> DeferredImageDecoder::CreateGenerator(size_t index) {
+ if (frame_generator_ && frame_generator_->DecodeFailed())
+ return nullptr;
+
+ PrepareLazyDecodedFrames();
+
+ // PrepareLazyDecodedFrames should populate the metadata for each frame in
+ // this image and create the |frame_generator_|, if enough data is available.
+ if (index >= frame_data_.size())
+ return nullptr;
+
+ DCHECK(frame_generator_);
+ const SkISize& decoded_size = frame_generator_->GetFullSize();
+ DCHECK_GT(decoded_size.width(), 0);
+ DCHECK_GT(decoded_size.height(), 0);
+
+ sk_sp<SkROBuffer> ro_buffer(rw_buffer_->makeROBufferSnapshot());
+ scoped_refptr<SegmentReader> segment_reader =
+ SegmentReader::CreateFromSkROBuffer(std::move(ro_buffer));
+
+ // ImageFrameGenerator has the latest known alpha state. There will be a
+ // performance boost if this frame is opaque.
+ SkAlphaType alpha_type = frame_generator_->HasAlpha(index)
+ ? kPremul_SkAlphaType
+ : kOpaque_SkAlphaType;
+ SkImageInfo info =
+ SkImageInfo::MakeN32(decoded_size.width(), decoded_size.height(),
+ alpha_type, color_space_for_sk_images_);
+
+ std::vector<FrameMetadata> frames(frame_data_.size());
+ for (size_t i = 0; i < frame_data_.size(); ++i) {
+ frames[i].complete = frame_data_[i].is_received_;
+ frames[i].duration = FrameDurationAtIndex(i);
+ }
+
+ auto generator = DecodingImageGenerator::Create(
+ frame_generator_, info, std::move(segment_reader), std::move(frames),
+ complete_frame_content_id_, all_data_received_);
+ generator->SetCanYUVDecode(can_yuv_decode_);
+
+ return generator;
+}
+
+scoped_refptr<SharedBuffer> DeferredImageDecoder::Data() {
+ if (!rw_buffer_)
+ return nullptr;
+ sk_sp<SkROBuffer> ro_buffer(rw_buffer_->makeROBufferSnapshot());
+ scoped_refptr<SharedBuffer> shared_buffer = SharedBuffer::Create();
+ SkROBuffer::Iter it(ro_buffer.get());
+ do {
+ shared_buffer->Append(static_cast<const char*>(it.data()), it.size());
+ } while (it.next());
+ return shared_buffer;
+}
+
+void DeferredImageDecoder::SetData(scoped_refptr<SharedBuffer> data,
+ bool all_data_received) {
+ SetDataInternal(std::move(data), all_data_received, true);
+}
+
+void DeferredImageDecoder::SetDataInternal(scoped_refptr<SharedBuffer> data,
+ bool all_data_received,
+ bool push_data_to_decoder) {
+ if (metadata_decoder_) {
+ all_data_received_ = all_data_received;
+ if (push_data_to_decoder)
+ metadata_decoder_->SetData(data, all_data_received);
+ PrepareLazyDecodedFrames();
+ }
+
+ if (frame_generator_) {
+ if (!rw_buffer_)
+ rw_buffer_ = std::make_unique<SkRWBuffer>(data->size());
+
+ const char* segment = nullptr;
+ for (size_t length = data->GetSomeData(segment, rw_buffer_->size()); length;
+ length = data->GetSomeData(segment, rw_buffer_->size())) {
+ DCHECK_GE(data->size(), rw_buffer_->size() + length);
+ const size_t remaining = data->size() - rw_buffer_->size() - length;
+ rw_buffer_->append(segment, length, remaining);
+ }
+ }
+}
+
+bool DeferredImageDecoder::IsSizeAvailable() {
+ // m_actualDecoder is 0 only if image decoding is deferred and that means
+ // the image header decoded successfully and the size is available.
+ return metadata_decoder_ ? metadata_decoder_->IsSizeAvailable() : true;
+}
+
+bool DeferredImageDecoder::HasEmbeddedColorSpace() const {
+ return metadata_decoder_ ? metadata_decoder_->HasEmbeddedColorSpace()
+ : has_embedded_color_space_;
+}
+
+IntSize DeferredImageDecoder::Size() const {
+ return metadata_decoder_ ? metadata_decoder_->Size() : size_;
+}
+
+IntSize DeferredImageDecoder::FrameSizeAtIndex(size_t index) const {
+ // FIXME: LocalFrame size is assumed to be uniform. This might not be true for
+ // future supported codecs.
+ return metadata_decoder_ ? metadata_decoder_->FrameSizeAtIndex(index) : size_;
+}
+
+size_t DeferredImageDecoder::FrameCount() {
+ return metadata_decoder_ ? metadata_decoder_->FrameCount()
+ : frame_data_.size();
+}
+
+int DeferredImageDecoder::RepetitionCount() const {
+ return metadata_decoder_ ? metadata_decoder_->RepetitionCount()
+ : repetition_count_;
+}
+
+void DeferredImageDecoder::ClearCacheExceptFrame(size_t clear_except_frame) {
+ if (metadata_decoder_)
+ metadata_decoder_->ClearCacheExceptFrame(clear_except_frame);
+}
+
+bool DeferredImageDecoder::FrameHasAlphaAtIndex(size_t index) const {
+ if (metadata_decoder_)
+ return metadata_decoder_->FrameHasAlphaAtIndex(index);
+ if (!frame_generator_->IsMultiFrame())
+ return frame_generator_->HasAlpha(index);
+ return true;
+}
+
+bool DeferredImageDecoder::FrameIsReceivedAtIndex(size_t index) const {
+ if (metadata_decoder_)
+ return metadata_decoder_->FrameIsReceivedAtIndex(index);
+ if (index < frame_data_.size())
+ return frame_data_[index].is_received_;
+ return false;
+}
+
+TimeDelta DeferredImageDecoder::FrameDurationAtIndex(size_t index) const {
+ TimeDelta duration;
+ if (metadata_decoder_)
+ duration = metadata_decoder_->FrameDurationAtIndex(index);
+ if (index < frame_data_.size())
+ duration = frame_data_[index].duration_;
+
+ // Many annoying ads specify a 0 duration to make an image flash as quickly as
+ // possible. We follow Firefox's behavior and use a duration of 100 ms for any
+ // frames that specify a duration of <= 10 ms. See <rdar://problem/7689300>
+ // and <http://webkit.org/b/36082> for more information.
+ if (duration <= TimeDelta::FromMilliseconds(10))
+ duration = TimeDelta::FromMilliseconds(100);
+
+ return duration;
+}
+
+ImageOrientation DeferredImageDecoder::OrientationAtIndex(size_t index) const {
+ if (metadata_decoder_)
+ return metadata_decoder_->Orientation();
+ if (index < frame_data_.size())
+ return frame_data_[index].orientation_;
+ return kDefaultImageOrientation;
+}
+
+void DeferredImageDecoder::ActivateLazyDecoding() {
+ if (frame_generator_)
+ return;
+
+ size_ = metadata_decoder_->Size();
+ has_hot_spot_ = metadata_decoder_->HotSpot(hot_spot_);
+ filename_extension_ = metadata_decoder_->FilenameExtension();
+ // JPEG images support YUV decoding; other decoders do not. (WebP could in the
+ // future.)
+ can_yuv_decode_ = RuntimeEnabledFeatures::DecodeToYUVEnabled() &&
+ (filename_extension_ == "jpg");
+ has_embedded_color_space_ = metadata_decoder_->HasEmbeddedColorSpace();
+ color_space_for_sk_images_ = metadata_decoder_->ColorSpaceForSkImages();
+
+ const bool is_single_frame =
+ metadata_decoder_->RepetitionCount() == kAnimationNone ||
+ (all_data_received_ && metadata_decoder_->FrameCount() == 1u);
+ const SkISize decoded_size =
+ SkISize::Make(metadata_decoder_->DecodedSize().Width(),
+ metadata_decoder_->DecodedSize().Height());
+ frame_generator_ = ImageFrameGenerator::Create(
+ decoded_size, !is_single_frame, metadata_decoder_->GetColorBehavior(),
+ metadata_decoder_->GetSupportedDecodeSizes());
+}
+
+void DeferredImageDecoder::PrepareLazyDecodedFrames() {
+ if (!metadata_decoder_ || !metadata_decoder_->IsSizeAvailable())
+ return;
+
+ ActivateLazyDecoding();
+
+ const size_t previous_size = frame_data_.size();
+ frame_data_.resize(metadata_decoder_->FrameCount());
+
+ // We have encountered a broken image file. Simply bail.
+ if (frame_data_.size() < previous_size)
+ return;
+
+ for (size_t i = previous_size; i < frame_data_.size(); ++i) {
+ frame_data_[i].duration_ = metadata_decoder_->FrameDurationAtIndex(i);
+ frame_data_[i].orientation_ = metadata_decoder_->Orientation();
+ frame_data_[i].is_received_ = metadata_decoder_->FrameIsReceivedAtIndex(i);
+ }
+
+ // The last lazy decoded frame created from previous call might be
+ // incomplete so update its state.
+ if (previous_size) {
+ const size_t last_frame = previous_size - 1;
+ frame_data_[last_frame].is_received_ =
+ metadata_decoder_->FrameIsReceivedAtIndex(last_frame);
+ }
+
+ // If we've received all of the data, then we can reset the metadata decoder,
+ // since everything we care about should now be stored in |frame_data_|.
+ if (all_data_received_) {
+ repetition_count_ = metadata_decoder_->RepetitionCount();
+ metadata_decoder_.reset();
+ // Hold on to m_rwBuffer, which is still needed by createFrameAtIndex.
+ }
+}
+
+bool DeferredImageDecoder::HotSpot(IntPoint& hot_spot) const {
+ if (metadata_decoder_)
+ return metadata_decoder_->HotSpot(hot_spot);
+ if (has_hot_spot_)
+ hot_spot = hot_spot_;
+ return has_hot_spot_;
+}
+
+} // namespace blink
+
+namespace WTF {
+template <>
+struct VectorTraits<blink::DeferredFrameData>
+ : public SimpleClassVectorTraits<blink::DeferredFrameData> {
+ STATIC_ONLY(VectorTraits);
+ static const bool kCanInitializeWithMemset =
+ false; // Not all DeferredFrameData members initialize to 0.
+};
+}
diff --git a/chromium/third_party/blink/renderer/platform/graphics/deferred_image_decoder.h b/chromium/third_party/blink/renderer/platform/graphics/deferred_image_decoder.h
new file mode 100644
index 00000000000..82c05c43f0a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/deferred_image_decoder.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2012 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DEFERRED_IMAGE_DECODER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DEFERRED_IMAGE_DECODER_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/geometry/int_size.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_image.h"
+#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "third_party/skia/include/core/SkRWBuffer.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+namespace blink {
+
+class ImageFrameGenerator;
+class SharedBuffer;
+struct DeferredFrameData;
+
+class PLATFORM_EXPORT DeferredImageDecoder final {
+ WTF_MAKE_NONCOPYABLE(DeferredImageDecoder);
+ USING_FAST_MALLOC(DeferredImageDecoder);
+
+ public:
+ static std::unique_ptr<DeferredImageDecoder> Create(
+ scoped_refptr<SharedBuffer> data,
+ bool data_complete,
+ ImageDecoder::AlphaOption,
+ const ColorBehavior&);
+
+ static std::unique_ptr<DeferredImageDecoder> CreateForTesting(
+ std::unique_ptr<ImageDecoder>);
+
+ ~DeferredImageDecoder();
+
+ String FilenameExtension() const;
+
+ sk_sp<PaintImageGenerator> CreateGenerator(size_t index);
+
+ scoped_refptr<SharedBuffer> Data();
+ void SetData(scoped_refptr<SharedBuffer> data, bool all_data_received);
+
+ bool IsSizeAvailable();
+ bool HasEmbeddedColorSpace() const;
+ IntSize Size() const;
+ IntSize FrameSizeAtIndex(size_t index) const;
+ size_t FrameCount();
+ int RepetitionCount() const;
+ void ClearCacheExceptFrame(size_t index);
+ bool FrameHasAlphaAtIndex(size_t index) const;
+ bool FrameIsReceivedAtIndex(size_t index) const;
+ TimeDelta FrameDurationAtIndex(size_t index) const;
+ ImageOrientation OrientationAtIndex(size_t index) const;
+ bool HotSpot(IntPoint&) const;
+
+ private:
+ explicit DeferredImageDecoder(std::unique_ptr<ImageDecoder> metadata_decoder);
+
+ friend class DeferredImageDecoderTest;
+ ImageFrameGenerator* FrameGenerator() { return frame_generator_.get(); }
+
+ void ActivateLazyDecoding();
+ void PrepareLazyDecodedFrames();
+
+ void SetDataInternal(scoped_refptr<SharedBuffer> data,
+ bool all_data_received,
+ bool push_data_to_decoder);
+
+ // Copy of the data that is passed in, used by deferred decoding.
+ // Allows creating readonly snapshots that may be read in another thread.
+ std::unique_ptr<SkRWBuffer> rw_buffer_;
+ std::unique_ptr<ImageDecoder> metadata_decoder_;
+
+ String filename_extension_;
+ IntSize size_;
+ int repetition_count_;
+ bool has_embedded_color_space_ = false;
+ bool all_data_received_;
+ bool can_yuv_decode_;
+ bool has_hot_spot_;
+ sk_sp<SkColorSpace> color_space_for_sk_images_;
+ IntPoint hot_spot_;
+ const PaintImage::ContentId complete_frame_content_id_;
+
+ // Caches frame state information.
+ Vector<DeferredFrameData> frame_data_;
+ scoped_refptr<ImageFrameGenerator> frame_generator_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/graphics/deferred_image_decoder_test.cc b/chromium/third_party/blink/renderer/platform/graphics/deferred_image_decoder_test.cc
new file mode 100644
index 00000000000..6cde411d473
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/deferred_image_decoder_test.cc
@@ -0,0 +1,465 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/graphics/deferred_image_decoder.h"
+
+#include <memory>
+#include "base/location.h"
+#include "base/memory/scoped_refptr.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_thread.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/graphics/image_decoding_store.h"
+#include "third_party/blink/renderer/platform/graphics/image_frame_generator.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_image.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_record.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_recorder.h"
+#include "third_party/blink/renderer/platform/graphics/test/mock_image_decoder.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/web_task_runner.h"
+#include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/core/SkPixmap.h"
+#include "third_party/skia/include/core/SkSurface.h"
+
+namespace blink {
+
+namespace {
+
+// Raw data for a PNG file with 1x1 white pixels.
+const unsigned char kWhitePNG[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
+ 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
+ 0x08, 0x02, 0x00, 0x00, 0x00, 0x90, 0x77, 0x53, 0xde, 0x00, 0x00, 0x00,
+ 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00,
+ 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00,
+ 0x0b, 0x13, 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x00, 0x0c, 0x49,
+ 0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 0xf8, 0xff, 0xff, 0x3f, 0x00, 0x05,
+ 0xfe, 0x02, 0xfe, 0xdc, 0xcc, 0x59, 0xe7, 0x00, 0x00, 0x00, 0x00, 0x49,
+ 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
+};
+
+// Raw data for a GIF file with 1x1 white pixels. Modified from animatedGIF.
+const unsigned char kWhiteGIF[] = {
+ 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00, 0x01, 0x00, 0xf0, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x21, 0xff, 0x0b, 0x4e, 0x45,
+ 0x54, 0x53, 0x43, 0x41, 0x50, 0x45, 0x32, 0x2e, 0x30, 0x03, 0x01, 0x00,
+ 0x00, 0x00, 0x21, 0xff, 0x0b, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x4d, 0x61,
+ 0x67, 0x69, 0x63, 0x6b, 0x0d, 0x67, 0x61, 0x6d, 0x6d, 0x61, 0x3d, 0x30,
+ 0x2e, 0x34, 0x35, 0x34, 0x35, 0x35, 0x00, 0x21, 0xff, 0x0b, 0x49, 0x6d,
+ 0x61, 0x67, 0x65, 0x4d, 0x61, 0x67, 0x69, 0x63, 0x6b, 0x0d, 0x67, 0x61,
+ 0x6d, 0x6d, 0x61, 0x3d, 0x30, 0x2e, 0x34, 0x35, 0x34, 0x35, 0x35, 0x00,
+ 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0xff, 0x00, 0x2c, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x4c, 0x01, 0x00, 0x3b};
+
+} // namespace
+
+class DeferredImageDecoderTest : public testing::Test,
+ public MockImageDecoderClient {
+ public:
+ void SetUp() override {
+ paint_image_id_ = PaintImage::GetNextId();
+ ImageDecodingStore::Instance().SetCacheLimitInBytes(1024 * 1024);
+ data_ = SharedBuffer::Create(kWhitePNG, sizeof(kWhitePNG));
+ frame_count_ = 1;
+ std::unique_ptr<MockImageDecoder> decoder = MockImageDecoder::Create(this);
+ actual_decoder_ = decoder.get();
+ actual_decoder_->SetSize(1, 1);
+ lazy_decoder_ = DeferredImageDecoder::CreateForTesting(std::move(decoder));
+ bitmap_.allocPixels(SkImageInfo::MakeN32Premul(100, 100));
+ canvas_ = std::make_unique<cc::SkiaPaintCanvas>(bitmap_);
+ decode_request_count_ = 0;
+ repetition_count_ = kAnimationNone;
+ status_ = ImageFrame::kFrameComplete;
+ frame_duration_ = TimeDelta();
+ decoded_size_ = actual_decoder_->Size();
+ }
+
+ void TearDown() override { ImageDecodingStore::Instance().Clear(); }
+
+ void DecoderBeingDestroyed() override { actual_decoder_ = nullptr; }
+
+ void DecodeRequested() override { ++decode_request_count_; }
+
+ size_t FrameCount() override { return frame_count_; }
+
+ int RepetitionCount() const override { return repetition_count_; }
+
+ ImageFrame::Status GetStatus(size_t index) override { return status_; }
+
+ TimeDelta FrameDuration() const override { return frame_duration_; }
+
+ IntSize DecodedSize() const override { return decoded_size_; }
+
+ PaintImage CreatePaintImageAtIndex(
+ size_t index,
+ PaintImage::CompletionState state = PaintImage::CompletionState::DONE) {
+ return CreatePaintImageAtIndex(lazy_decoder_.get(), index, state);
+ }
+
+ PaintImage CreatePaintImageAtIndex(
+ DeferredImageDecoder* decoder,
+ size_t index,
+ PaintImage::CompletionState state = PaintImage::CompletionState::DONE) {
+ PaintImage::AnimationType type = FrameCount() > 1
+ ? PaintImage::AnimationType::ANIMATED
+ : PaintImage::AnimationType::STATIC;
+
+ return PaintImageBuilder::WithDefault()
+ .set_id(paint_image_id_)
+ .set_animation_type(type)
+ .set_completion_state(state)
+ .set_paint_image_generator(decoder->CreateGenerator(index))
+ .set_frame_index(index)
+ .TakePaintImage();
+ }
+
+ protected:
+ void UseMockImageDecoderFactory() {
+ lazy_decoder_->FrameGenerator()->SetImageDecoderFactory(
+ MockImageDecoderFactory::Create(this, decoded_size_));
+ }
+
+ // Don't own this but saves the pointer to query states.
+ PaintImage::Id paint_image_id_;
+ MockImageDecoder* actual_decoder_;
+ std::unique_ptr<DeferredImageDecoder> lazy_decoder_;
+ SkBitmap bitmap_;
+ std::unique_ptr<cc::PaintCanvas> canvas_;
+ int decode_request_count_;
+ scoped_refptr<SharedBuffer> data_;
+ size_t frame_count_;
+ int repetition_count_;
+ ImageFrame::Status status_;
+ TimeDelta frame_duration_;
+ IntSize decoded_size_;
+};
+
+TEST_F(DeferredImageDecoderTest, drawIntoPaintRecord) {
+ lazy_decoder_->SetData(data_, true);
+ PaintImage image = CreatePaintImageAtIndex(0);
+ ASSERT_TRUE(image);
+ EXPECT_EQ(1, image.width());
+ EXPECT_EQ(1, image.height());
+
+ PaintRecorder recorder;
+ PaintCanvas* temp_canvas = recorder.beginRecording(100, 100);
+ temp_canvas->drawImage(image, 0, 0);
+ sk_sp<PaintRecord> record = recorder.finishRecordingAsPicture();
+ EXPECT_EQ(0, decode_request_count_);
+
+ canvas_->drawPicture(record);
+ EXPECT_EQ(0, decode_request_count_);
+ EXPECT_EQ(SkColorSetARGB(255, 255, 255, 255), bitmap_.getColor(0, 0));
+}
+
+TEST_F(DeferredImageDecoderTest, drawIntoPaintRecordProgressive) {
+ scoped_refptr<SharedBuffer> partial_data =
+ SharedBuffer::Create(data_->Data(), data_->size() - 10);
+
+ // Received only half the file.
+ lazy_decoder_->SetData(partial_data, false);
+ PaintRecorder recorder;
+ PaintCanvas* temp_canvas = recorder.beginRecording(100, 100);
+ PaintImage image =
+ CreatePaintImageAtIndex(0, PaintImage::CompletionState::PARTIALLY_DONE);
+ temp_canvas->drawImage(image, 0, 0);
+ canvas_->drawPicture(recorder.finishRecordingAsPicture());
+
+ // Fully received the file and draw the PaintRecord again.
+ lazy_decoder_->SetData(data_, true);
+ image = CreatePaintImageAtIndex(0);
+ ASSERT_TRUE(image);
+ temp_canvas = recorder.beginRecording(100, 100);
+ temp_canvas->drawImage(image, 0, 0);
+ canvas_->drawPicture(recorder.finishRecordingAsPicture());
+ EXPECT_EQ(SkColorSetARGB(255, 255, 255, 255), bitmap_.getColor(0, 0));
+}
+
+static void RasterizeMain(PaintCanvas* canvas, sk_sp<PaintRecord> record) {
+ canvas->drawPicture(record);
+}
+
+// Flaky on Mac. crbug.com/792540.
+#if defined(OS_MACOSX)
+#define MAYBE_decodeOnOtherThread DISABLED_decodeOnOtherThread
+#else
+#define MAYBE_decodeOnOtherThread decodeOnOtherThread
+#endif
+TEST_F(DeferredImageDecoderTest, MAYBE_decodeOnOtherThread) {
+ lazy_decoder_->SetData(data_, true);
+ PaintImage image = CreatePaintImageAtIndex(0);
+ ASSERT_TRUE(image);
+ EXPECT_EQ(1, image.width());
+ EXPECT_EQ(1, image.height());
+
+ PaintRecorder recorder;
+ PaintCanvas* temp_canvas = recorder.beginRecording(100, 100);
+ temp_canvas->drawImage(image, 0, 0);
+ sk_sp<PaintRecord> record = recorder.finishRecordingAsPicture();
+ EXPECT_EQ(0, decode_request_count_);
+
+ // Create a thread to rasterize PaintRecord.
+ std::unique_ptr<WebThread> thread = Platform::Current()->CreateThread(
+ WebThreadCreationParams(WebThreadType::kTestThread)
+ .SetThreadNameForTest("RasterThread"));
+ PostCrossThreadTask(
+ *thread->GetTaskRunner(), FROM_HERE,
+ CrossThreadBind(&RasterizeMain, CrossThreadUnretained(canvas_.get()),
+ record));
+ thread.reset();
+ EXPECT_EQ(0, decode_request_count_);
+ EXPECT_EQ(SkColorSetARGB(255, 255, 255, 255), bitmap_.getColor(0, 0));
+}
+
+TEST_F(DeferredImageDecoderTest, singleFrameImageLoading) {
+ status_ = ImageFrame::kFramePartial;
+ lazy_decoder_->SetData(data_, false);
+ EXPECT_FALSE(lazy_decoder_->FrameIsReceivedAtIndex(0));
+ PaintImage image = CreatePaintImageAtIndex(0);
+ ASSERT_TRUE(image);
+ EXPECT_FALSE(lazy_decoder_->FrameIsReceivedAtIndex(0));
+ EXPECT_TRUE(actual_decoder_);
+
+ status_ = ImageFrame::kFrameComplete;
+ data_->Append(" ", 1u);
+ lazy_decoder_->SetData(data_, true);
+ EXPECT_FALSE(actual_decoder_);
+ EXPECT_TRUE(lazy_decoder_->FrameIsReceivedAtIndex(0));
+
+ image = CreatePaintImageAtIndex(0);
+ ASSERT_TRUE(image);
+ EXPECT_FALSE(decode_request_count_);
+}
+
+TEST_F(DeferredImageDecoderTest, multiFrameImageLoading) {
+ repetition_count_ = 10;
+ frame_count_ = 1;
+ frame_duration_ = TimeDelta::FromMilliseconds(10);
+ status_ = ImageFrame::kFramePartial;
+ lazy_decoder_->SetData(data_, false);
+
+ PaintImage image = CreatePaintImageAtIndex(0);
+ ASSERT_TRUE(image);
+ EXPECT_FALSE(lazy_decoder_->FrameIsReceivedAtIndex(0));
+ // Anything <= 10ms is clamped to 100ms. See the implementaiton for details.
+ EXPECT_EQ(TimeDelta::FromMilliseconds(100),
+ lazy_decoder_->FrameDurationAtIndex(0));
+
+ frame_count_ = 2;
+ frame_duration_ = TimeDelta::FromMilliseconds(20);
+ status_ = ImageFrame::kFrameComplete;
+ data_->Append(" ", 1u);
+ lazy_decoder_->SetData(data_, false);
+
+ image = CreatePaintImageAtIndex(0);
+ ASSERT_TRUE(image);
+ EXPECT_TRUE(lazy_decoder_->FrameIsReceivedAtIndex(0));
+ EXPECT_TRUE(lazy_decoder_->FrameIsReceivedAtIndex(1));
+ EXPECT_EQ(TimeDelta::FromMilliseconds(20),
+ lazy_decoder_->FrameDurationAtIndex(1));
+ EXPECT_TRUE(actual_decoder_);
+
+ frame_count_ = 3;
+ frame_duration_ = TimeDelta::FromMilliseconds(30);
+ status_ = ImageFrame::kFrameComplete;
+ lazy_decoder_->SetData(data_, true);
+ EXPECT_FALSE(actual_decoder_);
+ EXPECT_TRUE(lazy_decoder_->FrameIsReceivedAtIndex(0));
+ EXPECT_TRUE(lazy_decoder_->FrameIsReceivedAtIndex(1));
+ EXPECT_TRUE(lazy_decoder_->FrameIsReceivedAtIndex(2));
+ EXPECT_EQ(TimeDelta::FromMilliseconds(100),
+ lazy_decoder_->FrameDurationAtIndex(0));
+ EXPECT_EQ(TimeDelta::FromMilliseconds(20),
+ lazy_decoder_->FrameDurationAtIndex(1));
+ EXPECT_EQ(TimeDelta::FromMilliseconds(30),
+ lazy_decoder_->FrameDurationAtIndex(2));
+ EXPECT_EQ(10, lazy_decoder_->RepetitionCount());
+}
+
+TEST_F(DeferredImageDecoderTest, decodedSize) {
+ decoded_size_ = IntSize(22, 33);
+ lazy_decoder_->SetData(data_, true);
+ PaintImage image = CreatePaintImageAtIndex(0);
+ ASSERT_TRUE(image);
+ EXPECT_EQ(decoded_size_.Width(), image.width());
+ EXPECT_EQ(decoded_size_.Height(), image.height());
+
+ UseMockImageDecoderFactory();
+
+ // The following code should not fail any assert.
+ PaintRecorder recorder;
+ PaintCanvas* temp_canvas = recorder.beginRecording(100, 100);
+ temp_canvas->drawImage(image, 0, 0);
+ sk_sp<PaintRecord> record = recorder.finishRecordingAsPicture();
+ EXPECT_EQ(0, decode_request_count_);
+ canvas_->drawPicture(record);
+ EXPECT_EQ(1, decode_request_count_);
+}
+
+TEST_F(DeferredImageDecoderTest, smallerFrameCount) {
+ frame_count_ = 1;
+ lazy_decoder_->SetData(data_, false);
+ EXPECT_EQ(frame_count_, lazy_decoder_->FrameCount());
+ frame_count_ = 2;
+ lazy_decoder_->SetData(data_, false);
+ EXPECT_EQ(frame_count_, lazy_decoder_->FrameCount());
+ frame_count_ = 0;
+ lazy_decoder_->SetData(data_, true);
+ EXPECT_EQ(frame_count_, lazy_decoder_->FrameCount());
+}
+
+TEST_F(DeferredImageDecoderTest, frameOpacity) {
+ for (bool test_gif : {false, true}) {
+ if (test_gif)
+ data_ = SharedBuffer::Create(kWhiteGIF, sizeof(kWhiteGIF));
+
+ std::unique_ptr<DeferredImageDecoder> decoder =
+ DeferredImageDecoder::Create(data_, true,
+ ImageDecoder::kAlphaPremultiplied,
+ ColorBehavior::TransformToSRGB());
+
+ SkImageInfo pix_info = SkImageInfo::MakeN32Premul(1, 1);
+
+ size_t row_bytes = pix_info.minRowBytes();
+ size_t size = pix_info.computeByteSize(row_bytes);
+
+ Vector<char> storage(size);
+ SkPixmap pixmap(pix_info, storage.data(), row_bytes);
+
+ // Before decoding, the frame is not known to be opaque.
+ sk_sp<SkImage> frame =
+ CreatePaintImageAtIndex(decoder.get(), 0).GetSkImage();
+ ASSERT_TRUE(frame);
+ EXPECT_FALSE(frame->isOpaque());
+ EXPECT_TRUE(decoder->FrameHasAlphaAtIndex(0));
+
+ // Force a lazy decode by reading pixels.
+ EXPECT_TRUE(frame->readPixels(pixmap, 0, 0));
+
+ // After decoding, the frame is known to be opaque.
+ EXPECT_FALSE(decoder->FrameHasAlphaAtIndex(0));
+ frame = CreatePaintImageAtIndex(decoder.get(), 0).GetSkImage();
+ ASSERT_TRUE(frame);
+ EXPECT_TRUE(frame->isOpaque());
+
+ // Re-generating the opaque-marked frame should not fail.
+ EXPECT_TRUE(frame->readPixels(pixmap, 0, 0));
+ }
+}
+
+TEST_F(DeferredImageDecoderTest, data) {
+ scoped_refptr<SharedBuffer> original_buffer =
+ SharedBuffer::Create(data_->Data(), data_->size());
+ EXPECT_EQ(original_buffer->size(), data_->size());
+ lazy_decoder_->SetData(original_buffer, false);
+ scoped_refptr<SharedBuffer> new_buffer = lazy_decoder_->Data();
+ EXPECT_EQ(original_buffer->size(), new_buffer->size());
+ const Vector<char> original_data = original_buffer->Copy();
+ const Vector<char> new_data = new_buffer->Copy();
+ EXPECT_EQ(0, std::memcmp(original_data.data(), new_data.data(),
+ new_buffer->size()));
+}
+
+class MultiFrameDeferredImageDecoderTest : public DeferredImageDecoderTest {
+ public:
+ ImageFrame::Status GetStatus(size_t index) override {
+ return index > last_complete_frame_ ? ImageFrame::Status::kFramePartial
+ : ImageFrame::Status::kFrameComplete;
+ }
+
+ size_t last_complete_frame_ = 0u;
+};
+
+TEST_F(MultiFrameDeferredImageDecoderTest, PaintImage) {
+ frame_count_ = 2;
+ frame_duration_ = TimeDelta::FromMilliseconds(20);
+ last_complete_frame_ = 0u;
+ lazy_decoder_->SetData(data_, false);
+
+ // Only the first frame is complete.
+ PaintImage image = CreatePaintImageAtIndex(0);
+ ASSERT_TRUE(image);
+ EXPECT_EQ(image.GetFrameMetadata().size(), 2u);
+ EXPECT_TRUE(image.GetFrameMetadata()[0].complete);
+ EXPECT_FALSE(image.GetFrameMetadata()[1].complete);
+ EXPECT_EQ(image.GetFrameMetadata()[0].duration, frame_duration_);
+ EXPECT_EQ(image.GetFrameMetadata()[1].duration, frame_duration_);
+
+ auto frame0_key = image.GetKeyForFrame(0);
+ auto frame1_key = image.GetKeyForFrame(1);
+ EXPECT_NE(frame0_key, frame1_key);
+
+ // Send some more data but the frame status remains the same.
+ last_complete_frame_ = 0u;
+ lazy_decoder_->SetData(data_, false);
+ PaintImage updated_image = CreatePaintImageAtIndex(0);
+ ASSERT_TRUE(updated_image);
+ EXPECT_EQ(updated_image.GetFrameMetadata().size(), 2u);
+ EXPECT_TRUE(updated_image.GetFrameMetadata()[0].complete);
+ EXPECT_FALSE(updated_image.GetFrameMetadata()[1].complete);
+
+ // Since the first frame was complete, the key remains constant. While the
+ // second frame generates a new key after it is updated.
+ auto updated_frame0_key = updated_image.GetKeyForFrame(0);
+ auto updated_frame1_key = updated_image.GetKeyForFrame(1);
+ EXPECT_NE(updated_frame0_key, updated_frame1_key);
+ EXPECT_EQ(updated_frame0_key, frame0_key);
+ EXPECT_NE(updated_frame1_key, frame1_key);
+
+ // Mark all frames complete.
+ last_complete_frame_ = 1u;
+ lazy_decoder_->SetData(data_, true);
+ PaintImage complete_image = CreatePaintImageAtIndex(0);
+ ASSERT_TRUE(complete_image);
+ EXPECT_EQ(complete_image.GetFrameMetadata().size(), 2u);
+ EXPECT_TRUE(complete_image.GetFrameMetadata()[0].complete);
+ EXPECT_TRUE(complete_image.GetFrameMetadata()[1].complete);
+
+ auto complete_frame0_key = complete_image.GetKeyForFrame(0);
+ auto complete_frame1_key = complete_image.GetKeyForFrame(1);
+ EXPECT_NE(complete_frame0_key, complete_frame1_key);
+ EXPECT_EQ(updated_frame0_key, complete_frame0_key);
+ EXPECT_NE(updated_frame1_key, complete_frame1_key);
+}
+
+TEST_F(MultiFrameDeferredImageDecoderTest, FrameDurationOverride) {
+ frame_count_ = 2;
+ frame_duration_ = TimeDelta::FromMilliseconds(5);
+ last_complete_frame_ = 1u;
+ lazy_decoder_->SetData(data_, true);
+
+ // If the frame duration is below a threshold, we override it to a constant
+ // value of 100 ms.
+ PaintImage image = CreatePaintImageAtIndex(0);
+ EXPECT_EQ(image.GetFrameMetadata()[0].duration,
+ base::TimeDelta::FromMilliseconds(100));
+ EXPECT_EQ(image.GetFrameMetadata()[1].duration,
+ base::TimeDelta::FromMilliseconds(100));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/deferred_image_decoder_test_wo_platform.cc b/chromium/third_party/blink/renderer/platform/graphics/deferred_image_decoder_test_wo_platform.cc
new file mode 100644
index 00000000000..b649be2fcbe
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/deferred_image_decoder_test_wo_platform.cc
@@ -0,0 +1,131 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/deferred_image_decoder.h"
+
+#include <memory>
+#include "base/memory/scoped_refptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/image-decoders/image_decoder_test_helpers.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/core/SkSurface.h"
+
+namespace blink {
+namespace {
+
+sk_sp<SkImage> CreateFrameAtIndex(DeferredImageDecoder* decoder, size_t index) {
+ return SkImage::MakeFromGenerator(std::make_unique<SkiaPaintImageGenerator>(
+ decoder->CreateGenerator(index), index));
+}
+
+} // namespace
+
+/**
+ * Used to test decoding SkImages out of order.
+ * e.g.
+ * SkImage* imageA = decoder.createFrameAtIndex(0);
+ * // supply more (but not all) data to the decoder
+ * SkImage* imageB = decoder.createFrameAtIndex(laterFrame);
+ * draw(imageB);
+ * draw(imageA);
+ *
+ * This results in using the same ImageDecoder (in the ImageDecodingStore) to
+ * decode less data the second time. This test ensures that it is safe to do
+ * so.
+ *
+ * @param fileName File to decode
+ * @param bytesForFirstFrame Number of bytes needed to return an SkImage
+ * @param laterFrame Frame to decode with almost complete data. Can be 0.
+ */
+static void MixImages(const char* file_name,
+ size_t bytes_for_first_frame,
+ size_t later_frame) {
+ const Vector<char> file = ReadFile(file_name)->Copy();
+
+ scoped_refptr<SharedBuffer> partial_file =
+ SharedBuffer::Create(file.data(), bytes_for_first_frame);
+ std::unique_ptr<DeferredImageDecoder> decoder = DeferredImageDecoder::Create(
+ partial_file, false, ImageDecoder::kAlphaPremultiplied,
+ ColorBehavior::Ignore());
+ ASSERT_NE(decoder, nullptr);
+ sk_sp<SkImage> partial_image = CreateFrameAtIndex(decoder.get(), 0);
+
+ scoped_refptr<SharedBuffer> almost_complete_file =
+ SharedBuffer::Create(file.data(), file.size() - 1);
+ decoder->SetData(almost_complete_file, false);
+ sk_sp<SkImage> image_with_more_data =
+ CreateFrameAtIndex(decoder.get(), later_frame);
+
+ // we now want to ensure we don't crash if we access these in this order
+ SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10);
+ sk_sp<SkSurface> surf = SkSurface::MakeRaster(info);
+ surf->getCanvas()->drawImage(image_with_more_data, 0, 0);
+ surf->getCanvas()->drawImage(partial_image, 0, 0);
+}
+
+TEST(DeferredImageDecoderTestWoPlatform, mixImagesGif) {
+ MixImages("/images/resources/animated.gif", 818u, 1u);
+}
+
+TEST(DeferredImageDecoderTestWoPlatform, mixImagesPng) {
+ MixImages("/images/resources/mu.png", 910u, 0u);
+}
+
+TEST(DeferredImageDecoderTestWoPlatform, mixImagesJpg) {
+ MixImages("/images/resources/2-dht.jpg", 177u, 0u);
+}
+
+TEST(DeferredImageDecoderTestWoPlatform, mixImagesWebp) {
+ MixImages("/images/resources/webp-animated.webp", 142u, 1u);
+}
+
+TEST(DeferredImageDecoderTestWoPlatform, mixImagesBmp) {
+ MixImages("/images/resources/lenna.bmp", 122u, 0u);
+}
+
+TEST(DeferredImageDecoderTestWoPlatform, mixImagesIco) {
+ MixImages("/images/resources/wrong-frame-dimensions.ico", 1376u, 1u);
+}
+
+TEST(DeferredImageDecoderTestWoPlatform, fragmentedSignature) {
+ const char* test_files[] = {
+ "/images/resources/animated.gif",
+ "/images/resources/mu.png",
+ "/images/resources/2-dht.jpg",
+ "/images/resources/webp-animated.webp",
+ "/images/resources/lenna.bmp",
+ "/images/resources/wrong-frame-dimensions.ico",
+ };
+
+ for (size_t i = 0; i < SK_ARRAY_COUNT(test_files); ++i) {
+ scoped_refptr<SharedBuffer> file_buffer = ReadFile(test_files[i]);
+ ASSERT_NE(file_buffer, nullptr);
+ // We need contiguous data, which SharedBuffer doesn't guarantee.
+ sk_sp<SkData> sk_data = file_buffer->GetAsSkData();
+ EXPECT_EQ(sk_data->size(), file_buffer->size());
+ const char* data = reinterpret_cast<const char*>(sk_data->bytes());
+
+ // Truncated signature (only 1 byte). Decoder instantiation should fail.
+ scoped_refptr<SharedBuffer> buffer = SharedBuffer::Create<size_t>(data, 1u);
+ EXPECT_FALSE(ImageDecoder::HasSufficientDataToSniffImageType(*buffer));
+ EXPECT_EQ(nullptr, DeferredImageDecoder::Create(
+ buffer, false, ImageDecoder::kAlphaPremultiplied,
+ ColorBehavior::Ignore()));
+
+ // Append the rest of the data. We should be able to sniff the signature
+ // now, even if segmented.
+ buffer->Append<size_t>(data + 1, sk_data->size() - 1);
+ EXPECT_TRUE(ImageDecoder::HasSufficientDataToSniffImageType(*buffer));
+ std::unique_ptr<DeferredImageDecoder> decoder =
+ DeferredImageDecoder::Create(buffer, false,
+ ImageDecoder::kAlphaPremultiplied,
+ ColorBehavior::Ignore());
+ ASSERT_NE(decoder, nullptr);
+ EXPECT_TRUE(String(test_files[i]).EndsWith(decoder->FilenameExtension()));
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/draw_looper_builder.cc b/chromium/third_party/blink/renderer/platform/graphics/draw_looper_builder.cc
new file mode 100644
index 00000000000..ca469f07a27
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/draw_looper_builder.cc
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/renderer/platform/graphics/draw_looper_builder.h"
+
+#include <memory>
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/geometry/float_size.h"
+#include "third_party/blink/renderer/platform/graphics/color.h"
+#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkColorFilter.h"
+#include "third_party/skia/include/core/SkDrawLooper.h"
+#include "third_party/skia/include/core/SkMaskFilter.h"
+#include "third_party/skia/include/core/SkPaint.h"
+
+namespace blink {
+
+DrawLooperBuilder::DrawLooperBuilder() = default;
+
+DrawLooperBuilder::~DrawLooperBuilder() = default;
+
+sk_sp<SkDrawLooper> DrawLooperBuilder::DetachDrawLooper() {
+ return sk_draw_looper_builder_.detach();
+}
+
+void DrawLooperBuilder::AddUnmodifiedContent() {
+ SkLayerDrawLooper::LayerInfo info;
+ sk_draw_looper_builder_.addLayerOnTop(info);
+}
+
+void DrawLooperBuilder::AddShadow(const FloatSize& offset,
+ float blur,
+ const Color& color,
+ ShadowTransformMode shadow_transform_mode,
+ ShadowAlphaMode shadow_alpha_mode) {
+ DCHECK_GE(blur, 0);
+
+ // Detect when there's no effective shadow.
+ if (!color.Alpha())
+ return;
+
+ SkColor sk_color = color.Rgb();
+
+ SkLayerDrawLooper::LayerInfo info;
+
+ switch (shadow_alpha_mode) {
+ case kShadowRespectsAlpha:
+ info.fColorMode = SkBlendMode::kDst;
+ break;
+ case kShadowIgnoresAlpha:
+ info.fColorMode = SkBlendMode::kSrc;
+ break;
+ default:
+ NOTREACHED();
+ }
+
+ if (blur)
+ info.fPaintBits |= SkLayerDrawLooper::kMaskFilter_Bit; // our blur
+ info.fPaintBits |= SkLayerDrawLooper::kColorFilter_Bit;
+ info.fOffset.set(offset.Width(), offset.Height());
+ info.fPostTranslate = (shadow_transform_mode == kShadowIgnoresTransforms);
+
+ SkPaint* paint = sk_draw_looper_builder_.addLayerOnTop(info);
+
+ if (blur) {
+ const SkScalar sigma = SkBlurRadiusToSigma(blur);
+ const bool respectCTM = shadow_transform_mode != kShadowIgnoresTransforms;
+ paint->setMaskFilter(
+ SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, sigma, respectCTM));
+ }
+
+ paint->setColorFilter(
+ SkColorFilter::MakeModeFilter(sk_color, SkBlendMode::kSrcIn));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/draw_looper_builder.h b/chromium/third_party/blink/renderer/platform/graphics/draw_looper_builder.h
new file mode 100644
index 00000000000..7d532552348
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/draw_looper_builder.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DRAW_LOOPER_BUILDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DRAW_LOOPER_BUILDER_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+#include "third_party/skia/include/effects/SkLayerDrawLooper.h"
+
+class SkDrawLooper;
+
+namespace blink {
+
+class Color;
+class FloatSize;
+
+class PLATFORM_EXPORT DrawLooperBuilder final {
+ // Implementing the copy constructor properly would require writing code to
+ // copy the underlying SkLayerDrawLooper::Builder.
+ WTF_MAKE_NONCOPYABLE(DrawLooperBuilder);
+ STACK_ALLOCATED();
+
+ public:
+ enum ShadowTransformMode {
+ kShadowRespectsTransforms,
+ kShadowIgnoresTransforms
+ };
+ enum ShadowAlphaMode { kShadowRespectsAlpha, kShadowIgnoresAlpha };
+
+ DrawLooperBuilder();
+ ~DrawLooperBuilder();
+
+ // Creates the SkDrawLooper and passes ownership to the caller. The builder
+ // should not be used any more after calling this method.
+ sk_sp<SkDrawLooper> DetachDrawLooper();
+
+ void AddUnmodifiedContent();
+ void AddShadow(const FloatSize& offset,
+ float blur,
+ const Color&,
+ ShadowTransformMode = kShadowRespectsTransforms,
+ ShadowAlphaMode = kShadowRespectsAlpha);
+
+ private:
+ SkLayerDrawLooper::Builder sk_draw_looper_builder_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DRAW_LOOPER_BUILDER_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/distant_light_source.cc b/chromium/third_party/blink/renderer/platform/graphics/filters/distant_light_source.cc
new file mode 100644
index 00000000000..eb2937a6799
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/distant_light_source.cc
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
+ * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2010 Zoltan Herczeg <zherczeg@webkit.org>
+ * Copyright (C) 2011 University of Szeged
+ * Copyright (C) 2011 Renata Hodovan <reni@webkit.org>
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED 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 "third_party/blink/renderer/platform/graphics/filters/distant_light_source.h"
+
+#include "third_party/blink/renderer/platform/text/text_stream.h"
+
+namespace blink {
+
+bool DistantLightSource::SetAzimuth(float azimuth) {
+ if (azimuth_ == azimuth)
+ return false;
+ azimuth_ = azimuth;
+ return true;
+}
+
+bool DistantLightSource::SetElevation(float elevation) {
+ if (elevation_ == elevation)
+ return false;
+ elevation_ = elevation;
+ return true;
+}
+
+TextStream& DistantLightSource::ExternalRepresentation(TextStream& ts) const {
+ ts << "[type=DISTANT-LIGHT] ";
+ ts << "[azimuth=\"" << Azimuth() << "\"]";
+ ts << "[elevation=\"" << Elevation() << "\"]";
+ return ts;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/distant_light_source.h b/chromium/third_party/blink/renderer/platform/graphics/filters/distant_light_source.h
new file mode 100644
index 00000000000..ca75b46c991
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/distant_light_source.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2008 Alex Mathews <possessedpenguinbob@gmail.com>
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
+ * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_DISTANT_LIGHT_SOURCE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_DISTANT_LIGHT_SOURCE_H_
+
+#include "third_party/blink/renderer/platform/graphics/filters/light_source.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT DistantLightSource final : public LightSource {
+ public:
+ static scoped_refptr<DistantLightSource> Create(float azimuth,
+ float elevation) {
+ return base::AdoptRef(new DistantLightSource(azimuth, elevation));
+ }
+
+ float Azimuth() const { return azimuth_; }
+ float Elevation() const { return elevation_; }
+
+ bool SetAzimuth(float) override;
+ bool SetElevation(float) override;
+
+ TextStream& ExternalRepresentation(TextStream&) const override;
+
+ private:
+ DistantLightSource(float azimuth, float elevation)
+ : LightSource(LS_DISTANT), azimuth_(azimuth), elevation_(elevation) {}
+
+ float azimuth_;
+ float elevation_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_DISTANT_LIGHT_SOURCE_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_blend.cc b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_blend.cc
new file mode 100644
index 00000000000..f8dd282537b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_blend.cc
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
+ * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
+ * Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "third_party/blink/renderer/platform/graphics/filters/fe_blend.h"
+
+#include "SkXfermodeImageFilter.h"
+#include "third_party/blink/renderer/platform/graphics/filters/paint_filter_builder.h"
+#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
+#include "third_party/blink/renderer/platform/text/text_stream.h"
+
+namespace blink {
+
+FEBlend::FEBlend(Filter* filter, WebBlendMode mode)
+ : FilterEffect(filter), mode_(mode) {}
+
+FEBlend* FEBlend::Create(Filter* filter, WebBlendMode mode) {
+ return new FEBlend(filter, mode);
+}
+
+WebBlendMode FEBlend::BlendMode() const {
+ return mode_;
+}
+
+bool FEBlend::SetBlendMode(WebBlendMode mode) {
+ if (mode_ == mode)
+ return false;
+ mode_ = mode;
+ return true;
+}
+
+sk_sp<PaintFilter> FEBlend::CreateImageFilter() {
+ sk_sp<PaintFilter> foreground(
+ PaintFilterBuilder::Build(InputEffect(0), OperatingInterpolationSpace()));
+ sk_sp<PaintFilter> background(
+ PaintFilterBuilder::Build(InputEffect(1), OperatingInterpolationSpace()));
+ SkBlendMode mode =
+ WebCoreCompositeToSkiaComposite(kCompositeSourceOver, mode_);
+ PaintFilter::CropRect crop_rect = GetCropRect();
+ return sk_make_sp<XfermodePaintFilter>(mode, std::move(background),
+ std::move(foreground), &crop_rect);
+}
+
+TextStream& FEBlend::ExternalRepresentation(TextStream& ts, int indent) const {
+ WriteIndent(ts, indent);
+ ts << "[feBlend";
+ FilterEffect::ExternalRepresentation(ts);
+ ts << " mode=\""
+ << (mode_ == WebBlendMode::kNormal
+ ? "normal"
+ : CompositeOperatorName(kCompositeSourceOver, mode_))
+ << "\"]\n";
+ InputEffect(0)->ExternalRepresentation(ts, indent + 1);
+ InputEffect(1)->ExternalRepresentation(ts, indent + 1);
+ return ts;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_blend.h b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_blend.h
new file mode 100644
index 00000000000..696605e998a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_blend.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
+ * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_BLEND_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_BLEND_H_
+
+#include "third_party/blink/public/platform/web_blend_mode.h"
+#include "third_party/blink/renderer/platform/graphics/filters/filter_effect.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT FEBlend final : public FilterEffect {
+ public:
+ static FEBlend* Create(Filter*, WebBlendMode);
+
+ WebBlendMode BlendMode() const;
+ bool SetBlendMode(WebBlendMode);
+
+ TextStream& ExternalRepresentation(TextStream&, int indention) const override;
+
+ private:
+ FEBlend(Filter*, WebBlendMode);
+
+ sk_sp<PaintFilter> CreateImageFilter() override;
+
+ WebBlendMode mode_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_BLEND_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_box_reflect.cc b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_box_reflect.cc
new file mode 100644
index 00000000000..bdab56eb758
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_box_reflect.cc
@@ -0,0 +1,35 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/filters/fe_box_reflect.h"
+
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/graphics/filters/paint_filter_builder.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+namespace blink {
+
+FEBoxReflect::FEBoxReflect(Filter* filter, const BoxReflection& reflection)
+ : FilterEffect(filter), reflection_(reflection) {}
+
+FEBoxReflect::~FEBoxReflect() = default;
+
+FloatRect FEBoxReflect::MapEffect(const FloatRect& rect) const {
+ return reflection_.MapRect(rect);
+}
+
+TextStream& FEBoxReflect::ExternalRepresentation(TextStream& ts,
+ int indent) const {
+ // Only called for SVG layout tree printing.
+ NOTREACHED();
+ return ts;
+}
+
+sk_sp<PaintFilter> FEBoxReflect::CreateImageFilter() {
+ return PaintFilterBuilder::BuildBoxReflectFilter(
+ reflection_,
+ PaintFilterBuilder::Build(InputEffect(0), OperatingInterpolationSpace()));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_box_reflect.h b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_box_reflect.h
new file mode 100644
index 00000000000..9127df947d4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_box_reflect.h
@@ -0,0 +1,37 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_BOX_REFLECT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_BOX_REFLECT_H_
+
+#include "third_party/blink/renderer/platform/graphics/box_reflection.h"
+#include "third_party/blink/renderer/platform/graphics/filters/filter_effect.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+
+// Used to implement the -webkit-box-reflect property as a filter.
+class PLATFORM_EXPORT FEBoxReflect final : public FilterEffect {
+ public:
+ static FEBoxReflect* Create(Filter* filter, const BoxReflection& reflection) {
+ return new FEBoxReflect(filter, reflection);
+ }
+
+ // FilterEffect implementation
+ TextStream& ExternalRepresentation(TextStream&, int indentation) const final;
+
+ private:
+ FEBoxReflect(Filter*, const BoxReflection&);
+ ~FEBoxReflect() final;
+
+ FloatRect MapEffect(const FloatRect&) const final;
+
+ sk_sp<PaintFilter> CreateImageFilter() final;
+
+ BoxReflection reflection_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_BOX_REFLECT_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_color_matrix.cc b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_color_matrix.cc
new file mode 100644
index 00000000000..ee82ceb2dc9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_color_matrix.cc
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
+ * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "third_party/blink/renderer/platform/graphics/filters/fe_color_matrix.h"
+
+#include "SkColorFilterImageFilter.h"
+#include "SkColorMatrixFilter.h"
+#include "third_party/blink/renderer/platform/graphics/filters/paint_filter_builder.h"
+#include "third_party/blink/renderer/platform/text/text_stream.h"
+
+namespace blink {
+
+static const unsigned kColorMatrixSize = 20;
+
+FEColorMatrix::FEColorMatrix(Filter* filter,
+ ColorMatrixType type,
+ const Vector<float>& values)
+ : FilterEffect(filter), type_(type), values_(values) {}
+
+FEColorMatrix* FEColorMatrix::Create(Filter* filter,
+ ColorMatrixType type,
+ const Vector<float>& values) {
+ return new FEColorMatrix(filter, type, values);
+}
+
+ColorMatrixType FEColorMatrix::GetType() const {
+ return type_;
+}
+
+bool FEColorMatrix::SetType(ColorMatrixType type) {
+ if (type_ == type)
+ return false;
+ type_ = type;
+ return true;
+}
+
+const Vector<float>& FEColorMatrix::Values() const {
+ return values_;
+}
+
+bool FEColorMatrix::SetValues(const Vector<float>& values) {
+ if (values_ == values)
+ return false;
+ values_ = values;
+ return true;
+}
+
+static void SaturateMatrix(float s, SkScalar matrix[kColorMatrixSize]) {
+ matrix[0] = 0.213f + 0.787f * s;
+ matrix[1] = 0.715f - 0.715f * s;
+ matrix[2] = 0.072f - 0.072f * s;
+ matrix[3] = matrix[4] = 0;
+ matrix[5] = 0.213f - 0.213f * s;
+ matrix[6] = 0.715f + 0.285f * s;
+ matrix[7] = 0.072f - 0.072f * s;
+ matrix[8] = matrix[9] = 0;
+ matrix[10] = 0.213f - 0.213f * s;
+ matrix[11] = 0.715f - 0.715f * s;
+ matrix[12] = 0.072f + 0.928f * s;
+ matrix[13] = matrix[14] = 0;
+ matrix[15] = matrix[16] = matrix[17] = 0;
+ matrix[18] = 1;
+ matrix[19] = 0;
+}
+
+static void HueRotateMatrix(float hue, SkScalar matrix[kColorMatrixSize]) {
+ float cos_hue = cosf(hue * piFloat / 180);
+ float sin_hue = sinf(hue * piFloat / 180);
+ matrix[0] = 0.213f + cos_hue * 0.787f - sin_hue * 0.213f;
+ matrix[1] = 0.715f - cos_hue * 0.715f - sin_hue * 0.715f;
+ matrix[2] = 0.072f - cos_hue * 0.072f + sin_hue * 0.928f;
+ matrix[3] = matrix[4] = 0;
+ matrix[5] = 0.213f - cos_hue * 0.213f + sin_hue * 0.143f;
+ matrix[6] = 0.715f + cos_hue * 0.285f + sin_hue * 0.140f;
+ matrix[7] = 0.072f - cos_hue * 0.072f - sin_hue * 0.283f;
+ matrix[8] = matrix[9] = 0;
+ matrix[10] = 0.213f - cos_hue * 0.213f - sin_hue * 0.787f;
+ matrix[11] = 0.715f - cos_hue * 0.715f + sin_hue * 0.715f;
+ matrix[12] = 0.072f + cos_hue * 0.928f + sin_hue * 0.072f;
+ matrix[13] = matrix[14] = 0;
+ matrix[15] = matrix[16] = matrix[17] = 0;
+ matrix[18] = 1;
+ matrix[19] = 0;
+}
+
+static void LuminanceToAlphaMatrix(SkScalar matrix[kColorMatrixSize]) {
+ memset(matrix, 0, kColorMatrixSize * sizeof(SkScalar));
+ matrix[15] = 0.2125f;
+ matrix[16] = 0.7154f;
+ matrix[17] = 0.0721f;
+}
+
+static sk_sp<SkColorFilter> CreateColorFilter(ColorMatrixType type,
+ const Vector<float>& values) {
+ // Use defaults if values contains too few/many values.
+ SkScalar matrix[kColorMatrixSize];
+ memset(matrix, 0, kColorMatrixSize * sizeof(SkScalar));
+ matrix[0] = matrix[6] = matrix[12] = matrix[18] = 1;
+
+ switch (type) {
+ case FECOLORMATRIX_TYPE_UNKNOWN:
+ break;
+ case FECOLORMATRIX_TYPE_MATRIX:
+ if (values.size() == kColorMatrixSize) {
+ for (unsigned i = 0; i < kColorMatrixSize; ++i)
+ matrix[i] = values[i];
+ }
+ matrix[4] *= SkScalar(255);
+ matrix[9] *= SkScalar(255);
+ matrix[14] *= SkScalar(255);
+ matrix[19] *= SkScalar(255);
+ break;
+ case FECOLORMATRIX_TYPE_SATURATE:
+ if (values.size() == 1)
+ SaturateMatrix(values[0], matrix);
+ break;
+ case FECOLORMATRIX_TYPE_HUEROTATE:
+ if (values.size() == 1)
+ HueRotateMatrix(values[0], matrix);
+ break;
+ case FECOLORMATRIX_TYPE_LUMINANCETOALPHA:
+ LuminanceToAlphaMatrix(matrix);
+ break;
+ }
+ return SkColorFilter::MakeMatrixFilterRowMajor255(matrix);
+}
+
+bool FEColorMatrix::AffectsTransparentPixels() const {
+ // Because the input pixels are premultiplied, the only way clear pixels can
+ // be painted is if the additive component for the alpha is not 0.
+ return type_ == FECOLORMATRIX_TYPE_MATRIX &&
+ values_.size() >= kColorMatrixSize && values_[19] > 0;
+}
+
+sk_sp<PaintFilter> FEColorMatrix::CreateImageFilter() {
+ sk_sp<PaintFilter> input(
+ PaintFilterBuilder::Build(InputEffect(0), OperatingInterpolationSpace()));
+ sk_sp<SkColorFilter> filter = CreateColorFilter(type_, values_);
+ PaintFilter::CropRect rect = GetCropRect();
+ return sk_make_sp<ColorFilterPaintFilter>(std::move(filter), std::move(input),
+ &rect);
+}
+
+static TextStream& operator<<(TextStream& ts, const ColorMatrixType& type) {
+ switch (type) {
+ case FECOLORMATRIX_TYPE_UNKNOWN:
+ ts << "UNKNOWN";
+ break;
+ case FECOLORMATRIX_TYPE_MATRIX:
+ ts << "MATRIX";
+ break;
+ case FECOLORMATRIX_TYPE_SATURATE:
+ ts << "SATURATE";
+ break;
+ case FECOLORMATRIX_TYPE_HUEROTATE:
+ ts << "HUEROTATE";
+ break;
+ case FECOLORMATRIX_TYPE_LUMINANCETOALPHA:
+ ts << "LUMINANCETOALPHA";
+ break;
+ }
+ return ts;
+}
+
+static bool ValuesIsValidForType(ColorMatrixType type,
+ const Vector<float>& values) {
+ switch (type) {
+ case FECOLORMATRIX_TYPE_MATRIX:
+ return values.size() == kColorMatrixSize;
+ case FECOLORMATRIX_TYPE_SATURATE:
+ case FECOLORMATRIX_TYPE_HUEROTATE:
+ return values.size() == 1;
+ case FECOLORMATRIX_TYPE_LUMINANCETOALPHA:
+ return values.size() == 0;
+ case FECOLORMATRIX_TYPE_UNKNOWN:
+ break;
+ }
+ NOTREACHED();
+ return false;
+}
+
+TextStream& FEColorMatrix::ExternalRepresentation(TextStream& ts,
+ int indent) const {
+ WriteIndent(ts, indent);
+ ts << "[feColorMatrix";
+ FilterEffect::ExternalRepresentation(ts);
+ ts << " type=\"" << type_ << "\"";
+ if (!values_.IsEmpty() && ValuesIsValidForType(type_, values_)) {
+ ts << " values=\"";
+ Vector<float>::const_iterator ptr = values_.begin();
+ const Vector<float>::const_iterator end = values_.end();
+ while (ptr < end) {
+ ts << *ptr;
+ ++ptr;
+ if (ptr < end)
+ ts << " ";
+ }
+ ts << "\"";
+ }
+ ts << "]\n";
+ InputEffect(0)->ExternalRepresentation(ts, indent + 1);
+ return ts;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_color_matrix.h b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_color_matrix.h
new file mode 100644
index 00000000000..a267d3988b5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_color_matrix.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
+ * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_COLOR_MATRIX_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_COLOR_MATRIX_H_
+
+#include "third_party/blink/renderer/platform/graphics/filters/filter_effect.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+enum ColorMatrixType {
+ FECOLORMATRIX_TYPE_UNKNOWN = 0,
+ FECOLORMATRIX_TYPE_MATRIX = 1,
+ FECOLORMATRIX_TYPE_SATURATE = 2,
+ FECOLORMATRIX_TYPE_HUEROTATE = 3,
+ FECOLORMATRIX_TYPE_LUMINANCETOALPHA = 4
+};
+
+class PLATFORM_EXPORT FEColorMatrix final : public FilterEffect {
+ public:
+ static FEColorMatrix* Create(Filter*, ColorMatrixType, const Vector<float>&);
+
+ ColorMatrixType GetType() const;
+ bool SetType(ColorMatrixType);
+
+ const Vector<float>& Values() const;
+ bool SetValues(const Vector<float>&);
+
+ TextStream& ExternalRepresentation(TextStream&, int indention) const override;
+
+ static inline void CalculateSaturateComponents(float* components,
+ float value);
+ static inline void CalculateHueRotateComponents(float* components,
+ float value);
+
+ private:
+ FEColorMatrix(Filter*, ColorMatrixType, const Vector<float>&);
+
+ sk_sp<PaintFilter> CreateImageFilter() override;
+
+ bool AffectsTransparentPixels() const override;
+
+ ColorMatrixType type_;
+
+ // The m_values vector may not contain the right number of values. Always
+ // check before accessing contents.
+ Vector<float> values_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_COLOR_MATRIX_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_component_transfer.cc b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_component_transfer.cc
new file mode 100644
index 00000000000..bfc9b3dfc4c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_component_transfer.cc
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
+ * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
+ * Copyright (C) Research In Motion Limited 2010. All rights reserved.
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "third_party/blink/renderer/platform/graphics/filters/fe_component_transfer.h"
+
+#include <algorithm>
+#include "SkTableColorFilter.h"
+#include "third_party/blink/renderer/platform/graphics/filters/paint_filter_builder.h"
+#include "third_party/blink/renderer/platform/text/text_stream.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+namespace blink {
+
+typedef void (*TransferType)(unsigned char*, const ComponentTransferFunction&);
+
+FEComponentTransfer::FEComponentTransfer(
+ Filter* filter,
+ const ComponentTransferFunction& red_func,
+ const ComponentTransferFunction& green_func,
+ const ComponentTransferFunction& blue_func,
+ const ComponentTransferFunction& alpha_func)
+ : FilterEffect(filter),
+ red_func_(red_func),
+ green_func_(green_func),
+ blue_func_(blue_func),
+ alpha_func_(alpha_func) {}
+
+FEComponentTransfer* FEComponentTransfer::Create(
+ Filter* filter,
+ const ComponentTransferFunction& red_func,
+ const ComponentTransferFunction& green_func,
+ const ComponentTransferFunction& blue_func,
+ const ComponentTransferFunction& alpha_func) {
+ return new FEComponentTransfer(filter, red_func, green_func, blue_func,
+ alpha_func);
+}
+
+static void Identity(unsigned char*, const ComponentTransferFunction&) {}
+
+static void Table(unsigned char* values,
+ const ComponentTransferFunction& transfer_function) {
+ const Vector<float>& table_values = transfer_function.table_values;
+ unsigned n = table_values.size();
+ if (n < 1)
+ return;
+ for (unsigned i = 0; i < 256; ++i) {
+ double c = i / 255.0;
+ unsigned k = static_cast<unsigned>(c * (n - 1));
+ double v1 = table_values[k];
+ double v2 = table_values[std::min((k + 1), (n - 1))];
+ double val = 255.0 * (v1 + (c * (n - 1) - k) * (v2 - v1));
+ val = clampTo(val, 0.0, 255.0);
+ values[i] = static_cast<unsigned char>(val);
+ }
+}
+
+static void Discrete(unsigned char* values,
+ const ComponentTransferFunction& transfer_function) {
+ const Vector<float>& table_values = transfer_function.table_values;
+ unsigned n = table_values.size();
+ if (n < 1)
+ return;
+ for (unsigned i = 0; i < 256; ++i) {
+ unsigned k = static_cast<unsigned>((i * n) / 255.0);
+ k = std::min(k, n - 1);
+ double val = 255 * table_values[k];
+ val = clampTo(val, 0.0, 255.0);
+ values[i] = static_cast<unsigned char>(val);
+ }
+}
+
+static void Linear(unsigned char* values,
+ const ComponentTransferFunction& transfer_function) {
+ for (unsigned i = 0; i < 256; ++i) {
+ double val =
+ transfer_function.slope * i + 255 * transfer_function.intercept;
+ val = clampTo(val, 0.0, 255.0);
+ values[i] = static_cast<unsigned char>(val);
+ }
+}
+
+static void Gamma(unsigned char* values,
+ const ComponentTransferFunction& transfer_function) {
+ for (unsigned i = 0; i < 256; ++i) {
+ double exponent = transfer_function.exponent;
+ double val =
+ 255.0 * (transfer_function.amplitude * pow((i / 255.0), exponent) +
+ transfer_function.offset);
+ val = clampTo(val, 0.0, 255.0);
+ values[i] = static_cast<unsigned char>(val);
+ }
+}
+
+bool FEComponentTransfer::AffectsTransparentPixels() const {
+ double intercept = 0;
+ switch (alpha_func_.type) {
+ case FECOMPONENTTRANSFER_TYPE_UNKNOWN:
+ case FECOMPONENTTRANSFER_TYPE_IDENTITY:
+ break;
+ case FECOMPONENTTRANSFER_TYPE_TABLE:
+ case FECOMPONENTTRANSFER_TYPE_DISCRETE:
+ if (alpha_func_.table_values.size() > 0)
+ intercept = alpha_func_.table_values[0];
+ break;
+ case FECOMPONENTTRANSFER_TYPE_LINEAR:
+ intercept = alpha_func_.intercept;
+ break;
+ case FECOMPONENTTRANSFER_TYPE_GAMMA:
+ intercept = alpha_func_.offset;
+ break;
+ }
+ return 255 * intercept >= 1;
+}
+
+sk_sp<PaintFilter> FEComponentTransfer::CreateImageFilter() {
+ sk_sp<PaintFilter> input(
+ PaintFilterBuilder::Build(InputEffect(0), OperatingInterpolationSpace()));
+
+ unsigned char r_values[256], g_values[256], b_values[256], a_values[256];
+ GetValues(r_values, g_values, b_values, a_values);
+
+ PaintFilter::CropRect crop_rect = GetCropRect();
+ sk_sp<SkColorFilter> color_filter =
+ SkTableColorFilter::MakeARGB(a_values, r_values, g_values, b_values);
+ return sk_make_sp<ColorFilterPaintFilter>(std::move(color_filter),
+ std::move(input), &crop_rect);
+}
+
+void FEComponentTransfer::GetValues(unsigned char r_values[256],
+ unsigned char g_values[256],
+ unsigned char b_values[256],
+ unsigned char a_values[256]) {
+ for (unsigned i = 0; i < 256; ++i)
+ r_values[i] = g_values[i] = b_values[i] = a_values[i] = i;
+ unsigned char* tables[] = {r_values, g_values, b_values, a_values};
+ ComponentTransferFunction transfer_function[] = {red_func_, green_func_,
+ blue_func_, alpha_func_};
+ TransferType call_effect[] = {Identity, Identity, Table,
+ Discrete, Linear, Gamma};
+
+ for (unsigned channel = 0; channel < 4; channel++) {
+ SECURITY_DCHECK(static_cast<size_t>(transfer_function[channel].type) <
+ WTF_ARRAY_LENGTH(call_effect));
+ (*call_effect[transfer_function[channel].type])(tables[channel],
+ transfer_function[channel]);
+ }
+}
+
+static TextStream& operator<<(TextStream& ts,
+ const ComponentTransferType& type) {
+ switch (type) {
+ case FECOMPONENTTRANSFER_TYPE_UNKNOWN:
+ ts << "UNKNOWN";
+ break;
+ case FECOMPONENTTRANSFER_TYPE_IDENTITY:
+ ts << "IDENTITY";
+ break;
+ case FECOMPONENTTRANSFER_TYPE_TABLE:
+ ts << "TABLE";
+ break;
+ case FECOMPONENTTRANSFER_TYPE_DISCRETE:
+ ts << "DISCRETE";
+ break;
+ case FECOMPONENTTRANSFER_TYPE_LINEAR:
+ ts << "LINEAR";
+ break;
+ case FECOMPONENTTRANSFER_TYPE_GAMMA:
+ ts << "GAMMA";
+ break;
+ }
+ return ts;
+}
+
+static TextStream& operator<<(TextStream& ts,
+ const ComponentTransferFunction& function) {
+ ts << "type=\"" << function.type << "\" slope=\"" << function.slope
+ << "\" intercept=\"" << function.intercept << "\" amplitude=\""
+ << function.amplitude << "\" exponent=\"" << function.exponent
+ << "\" offset=\"" << function.offset << "\"";
+ return ts;
+}
+
+TextStream& FEComponentTransfer::ExternalRepresentation(TextStream& ts,
+ int indent) const {
+ WriteIndent(ts, indent);
+ ts << "[feComponentTransfer";
+ FilterEffect::ExternalRepresentation(ts);
+ ts << " \n";
+ WriteIndent(ts, indent + 2);
+ ts << "{red: " << red_func_ << "}\n";
+ WriteIndent(ts, indent + 2);
+ ts << "{green: " << green_func_ << "}\n";
+ WriteIndent(ts, indent + 2);
+ ts << "{blue: " << blue_func_ << "}\n";
+ WriteIndent(ts, indent + 2);
+ ts << "{alpha: " << alpha_func_ << "}]\n";
+ InputEffect(0)->ExternalRepresentation(ts, indent + 1);
+ return ts;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_component_transfer.h b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_component_transfer.h
new file mode 100644
index 00000000000..278fe470ce3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_component_transfer.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
+ * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_COMPONENT_TRANSFER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_COMPONENT_TRANSFER_H_
+
+#include "third_party/blink/renderer/platform/graphics/filters/filter_effect.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+enum ComponentTransferType {
+ FECOMPONENTTRANSFER_TYPE_UNKNOWN = 0,
+ FECOMPONENTTRANSFER_TYPE_IDENTITY = 1,
+ FECOMPONENTTRANSFER_TYPE_TABLE = 2,
+ FECOMPONENTTRANSFER_TYPE_DISCRETE = 3,
+ FECOMPONENTTRANSFER_TYPE_LINEAR = 4,
+ FECOMPONENTTRANSFER_TYPE_GAMMA = 5
+};
+
+struct ComponentTransferFunction {
+ DISALLOW_NEW();
+ ComponentTransferFunction()
+ : type(FECOMPONENTTRANSFER_TYPE_UNKNOWN),
+ slope(0),
+ intercept(0),
+ amplitude(0),
+ exponent(0),
+ offset(0) {}
+
+ ComponentTransferType type;
+
+ float slope;
+ float intercept;
+ float amplitude;
+ float exponent;
+ float offset;
+
+ Vector<float> table_values;
+};
+
+class PLATFORM_EXPORT FEComponentTransfer final : public FilterEffect {
+ public:
+ static FEComponentTransfer* Create(
+ Filter*,
+ const ComponentTransferFunction& red_func,
+ const ComponentTransferFunction& green_func,
+ const ComponentTransferFunction& blue_func,
+ const ComponentTransferFunction& alpha_func);
+
+ TextStream& ExternalRepresentation(TextStream&, int indention) const override;
+
+ private:
+ FEComponentTransfer(Filter*,
+ const ComponentTransferFunction& red_func,
+ const ComponentTransferFunction& green_func,
+ const ComponentTransferFunction& blue_func,
+ const ComponentTransferFunction& alpha_func);
+
+ sk_sp<PaintFilter> CreateImageFilter() override;
+
+ bool AffectsTransparentPixels() const override;
+
+ void GetValues(unsigned char r_values[256],
+ unsigned char g_values[256],
+ unsigned char b_values[256],
+ unsigned char a_values[256]);
+
+ ComponentTransferFunction red_func_;
+ ComponentTransferFunction green_func_;
+ ComponentTransferFunction blue_func_;
+ ComponentTransferFunction alpha_func_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_COMPONENT_TRANSFER_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_composite.cc b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_composite.cc
new file mode 100644
index 00000000000..047e93f73d5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_composite.cc
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
+ * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
+ * Copyright (C) Research In Motion Limited 2010. All rights reserved.
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "third_party/blink/renderer/platform/graphics/filters/fe_composite.h"
+
+#include "SkArithmeticImageFilter.h"
+#include "SkXfermodeImageFilter.h"
+
+#include "third_party/blink/renderer/platform/graphics/filters/paint_filter_builder.h"
+#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
+#include "third_party/blink/renderer/platform/text/text_stream.h"
+
+namespace blink {
+
+FEComposite::FEComposite(Filter* filter,
+ const CompositeOperationType& type,
+ float k1,
+ float k2,
+ float k3,
+ float k4)
+ : FilterEffect(filter), type_(type), k1_(k1), k2_(k2), k3_(k3), k4_(k4) {}
+
+FEComposite* FEComposite::Create(Filter* filter,
+ const CompositeOperationType& type,
+ float k1,
+ float k2,
+ float k3,
+ float k4) {
+ return new FEComposite(filter, type, k1, k2, k3, k4);
+}
+
+CompositeOperationType FEComposite::Operation() const {
+ return type_;
+}
+
+bool FEComposite::SetOperation(CompositeOperationType type) {
+ if (type_ == type)
+ return false;
+ type_ = type;
+ return true;
+}
+
+float FEComposite::K1() const {
+ return k1_;
+}
+
+bool FEComposite::SetK1(float k1) {
+ if (k1_ == k1)
+ return false;
+ k1_ = k1;
+ return true;
+}
+
+float FEComposite::K2() const {
+ return k2_;
+}
+
+bool FEComposite::SetK2(float k2) {
+ if (k2_ == k2)
+ return false;
+ k2_ = k2;
+ return true;
+}
+
+float FEComposite::K3() const {
+ return k3_;
+}
+
+bool FEComposite::SetK3(float k3) {
+ if (k3_ == k3)
+ return false;
+ k3_ = k3;
+ return true;
+}
+
+float FEComposite::K4() const {
+ return k4_;
+}
+
+bool FEComposite::SetK4(float k4) {
+ if (k4_ == k4)
+ return false;
+ k4_ = k4;
+ return true;
+}
+
+bool FEComposite::AffectsTransparentPixels() const {
+ // When k4 is non-zero (greater than zero with clamping factored in), the
+ // arithmetic operation will produce non-transparent output for transparent
+ // output.
+ return type_ == FECOMPOSITE_OPERATOR_ARITHMETIC && K4() > 0;
+}
+
+FloatRect FEComposite::MapInputs(const FloatRect& rect) const {
+ FloatRect i1 = InputEffect(0)->MapRect(rect);
+ FloatRect i2 = InputEffect(1)->MapRect(rect);
+ switch (type_) {
+ case FECOMPOSITE_OPERATOR_IN:
+ // 'in' has output only in the intersection of both inputs.
+ return Intersection(i1, i2);
+ case FECOMPOSITE_OPERATOR_ATOP:
+ // 'atop' has output only in the extents of the second input.
+ return i2;
+ case FECOMPOSITE_OPERATOR_ARITHMETIC:
+ // result(i1,i2) = k1*i1*i2 + k2*i1 + k3*i2 + k4
+ //
+ // (The below is not a complete breakdown of cases.)
+ //
+ // Arithmetic with positive k4 may influence the complete filter primitive
+ // region. [k4 > 0 => result(0,0) = k4 => result(i1,i2) >= k4]
+ // Fall through to use union. If this effect clips to bounds,
+ // ApplyBounds() will return AbsoluteBounds() regardless of the return
+ // value of this function because AffectsTransparentPixels() is true.
+ if (K4() > 0)
+ break;
+ // If both K2 or K3 are positive, both i1 and i2 appear. Fall through to
+ // use union.
+ if (K2() > 0 && K3() > 0)
+ break;
+ // If k2 > 0, output can be produced whenever i1 is non-transparent.
+ // [k3 = k4 = 0 => result(i1,i2) = k1*i1*i2 + k2*i1 = (k1*i2 + k2)*i1]
+ if (K2() > 0)
+ return i1;
+ // If k3 > 0, output can be produced whenever i2 is non-transparent.
+ // [k2 = k4 = 0 => result(i1,i2) = k1*i1*i2 + k3*i2 = (k1*i1 + k3)*i2]
+ if (K3() > 0)
+ return i2;
+ // If just k1 is positive, output will only be produce where both inputs
+ // are non-transparent. Use intersection.
+ // [k1 > 0 and k2 = k3 = k4 = 0 => result(i1,i2) = k1*i1*i2]
+ if (K1() > 0)
+ return Intersection(i1, i2);
+ // [k1 = k2 = k3 = k4 = 0 => result(i1,i2) = 0]
+ return FloatRect();
+ default:
+ break;
+ }
+ // Take the union of both input effects.
+ return UnionRect(i1, i2);
+}
+
+SkBlendMode ToBlendMode(CompositeOperationType mode) {
+ switch (mode) {
+ case FECOMPOSITE_OPERATOR_OVER:
+ return SkBlendMode::kSrcOver;
+ case FECOMPOSITE_OPERATOR_IN:
+ return SkBlendMode::kSrcIn;
+ case FECOMPOSITE_OPERATOR_OUT:
+ return SkBlendMode::kSrcOut;
+ case FECOMPOSITE_OPERATOR_ATOP:
+ return SkBlendMode::kSrcATop;
+ case FECOMPOSITE_OPERATOR_XOR:
+ return SkBlendMode::kXor;
+ case FECOMPOSITE_OPERATOR_LIGHTER:
+ return SkBlendMode::kPlus;
+ default:
+ NOTREACHED();
+ return SkBlendMode::kSrcOver;
+ }
+}
+
+sk_sp<PaintFilter> FEComposite::CreateImageFilter() {
+ return CreateImageFilterInternal(true);
+}
+
+sk_sp<PaintFilter> FEComposite::CreateImageFilterWithoutValidation() {
+ return CreateImageFilterInternal(false);
+}
+
+sk_sp<PaintFilter> FEComposite::CreateImageFilterInternal(
+ bool requires_pm_color_validation) {
+ sk_sp<PaintFilter> foreground(
+ PaintFilterBuilder::Build(InputEffect(0), OperatingInterpolationSpace(),
+ !MayProduceInvalidPreMultipliedPixels()));
+ sk_sp<PaintFilter> background(
+ PaintFilterBuilder::Build(InputEffect(1), OperatingInterpolationSpace(),
+ !MayProduceInvalidPreMultipliedPixels()));
+ PaintFilter::CropRect crop_rect = GetCropRect();
+
+ if (type_ == FECOMPOSITE_OPERATOR_ARITHMETIC) {
+ return sk_make_sp<ArithmeticPaintFilter>(
+ SkFloatToScalar(k1_), SkFloatToScalar(k2_), SkFloatToScalar(k3_),
+ SkFloatToScalar(k4_), requires_pm_color_validation,
+ std::move(background), std::move(foreground), &crop_rect);
+ }
+
+ return sk_make_sp<XfermodePaintFilter>(ToBlendMode(type_),
+ std::move(background),
+ std::move(foreground), &crop_rect);
+}
+
+static TextStream& operator<<(TextStream& ts,
+ const CompositeOperationType& type) {
+ switch (type) {
+ case FECOMPOSITE_OPERATOR_UNKNOWN:
+ ts << "UNKNOWN";
+ break;
+ case FECOMPOSITE_OPERATOR_OVER:
+ ts << "OVER";
+ break;
+ case FECOMPOSITE_OPERATOR_IN:
+ ts << "IN";
+ break;
+ case FECOMPOSITE_OPERATOR_OUT:
+ ts << "OUT";
+ break;
+ case FECOMPOSITE_OPERATOR_ATOP:
+ ts << "ATOP";
+ break;
+ case FECOMPOSITE_OPERATOR_XOR:
+ ts << "XOR";
+ break;
+ case FECOMPOSITE_OPERATOR_ARITHMETIC:
+ ts << "ARITHMETIC";
+ break;
+ case FECOMPOSITE_OPERATOR_LIGHTER:
+ ts << "LIGHTER";
+ break;
+ }
+ return ts;
+}
+
+TextStream& FEComposite::ExternalRepresentation(TextStream& ts,
+ int indent) const {
+ WriteIndent(ts, indent);
+ ts << "[feComposite";
+ FilterEffect::ExternalRepresentation(ts);
+ ts << " operation=\"" << type_ << "\"";
+ if (type_ == FECOMPOSITE_OPERATOR_ARITHMETIC)
+ ts << " k1=\"" << k1_ << "\" k2=\"" << k2_ << "\" k3=\"" << k3_
+ << "\" k4=\"" << k4_ << "\"";
+ ts << "]\n";
+ InputEffect(0)->ExternalRepresentation(ts, indent + 1);
+ InputEffect(1)->ExternalRepresentation(ts, indent + 1);
+ return ts;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_composite.h b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_composite.h
new file mode 100644
index 00000000000..5a469c4bb50
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_composite.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
+ * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_COMPOSITE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_COMPOSITE_H_
+
+#include "SkBlendMode.h"
+#include "third_party/blink/renderer/platform/graphics/filters/filter_effect.h"
+
+namespace blink {
+
+enum CompositeOperationType {
+ FECOMPOSITE_OPERATOR_UNKNOWN = 0,
+ FECOMPOSITE_OPERATOR_OVER = 1,
+ FECOMPOSITE_OPERATOR_IN = 2,
+ FECOMPOSITE_OPERATOR_OUT = 3,
+ FECOMPOSITE_OPERATOR_ATOP = 4,
+ FECOMPOSITE_OPERATOR_XOR = 5,
+ FECOMPOSITE_OPERATOR_ARITHMETIC = 6,
+ FECOMPOSITE_OPERATOR_LIGHTER = 7
+};
+
+class PLATFORM_EXPORT FEComposite final : public FilterEffect {
+ public:
+ static FEComposite* Create(Filter*,
+ const CompositeOperationType&,
+ float,
+ float,
+ float,
+ float);
+
+ CompositeOperationType Operation() const;
+ bool SetOperation(CompositeOperationType);
+
+ float K1() const;
+ bool SetK1(float);
+
+ float K2() const;
+ bool SetK2(float);
+
+ float K3() const;
+ bool SetK3(float);
+
+ float K4() const;
+ bool SetK4(float);
+
+ TextStream& ExternalRepresentation(TextStream&, int indention) const override;
+
+ protected:
+ bool MayProduceInvalidPreMultipliedPixels() override {
+ return type_ == FECOMPOSITE_OPERATOR_ARITHMETIC;
+ }
+
+ private:
+ FEComposite(Filter*,
+ const CompositeOperationType&,
+ float,
+ float,
+ float,
+ float);
+
+ FloatRect MapInputs(const FloatRect&) const override;
+
+ bool AffectsTransparentPixels() const override;
+
+ sk_sp<PaintFilter> CreateImageFilter() override;
+ sk_sp<PaintFilter> CreateImageFilterWithoutValidation() override;
+ sk_sp<PaintFilter> CreateImageFilterInternal(
+ bool requires_pm_color_validation);
+
+ CompositeOperationType type_;
+ float k1_;
+ float k2_;
+ float k3_;
+ float k4_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_COMPOSITE_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_composite_test.cc b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_composite_test.cc
new file mode 100644
index 00000000000..74972a9d425
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_composite_test.cc
@@ -0,0 +1,133 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/filters/fe_composite.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/graphics/filters/fe_offset.h"
+#include "third_party/blink/renderer/platform/graphics/filters/filter.h"
+#include "third_party/blink/renderer/platform/graphics/filters/source_graphic.h"
+
+namespace blink {
+
+class FECompositeTest : public testing::Test {
+ protected:
+ FEComposite* CreateComposite(CompositeOperationType type,
+ float k1 = 0,
+ float k2 = 0,
+ float k3 = 0,
+ float k4 = 0) {
+ // Use big filter region to avoid it from affecting FEComposite's MapRect
+ // results.
+ FloatRect filter_region(-10000, -10000, 20000, 20000);
+ auto* filter =
+ Filter::Create(FloatRect(), filter_region, 1, Filter::kUserSpace);
+
+ // Input 1 of composite has a fixed output rect.
+ auto* source_graphic1 = SourceGraphic::Create(filter);
+ source_graphic1->SetClipsToBounds(false);
+ source_graphic1->SetSourceRect(kInput1Rect);
+
+ // Input 2 of composite will pass composite->MapRect()'s parameter as its
+ // output.
+ auto* source_graphic2 = SourceGraphic::Create(filter);
+ source_graphic2->SetClipsToBounds(false);
+
+ // Composite input 1 and input 2.
+ auto* composite = FEComposite::Create(filter, type, k1, k2, k3, k4);
+ composite->SetClipsToBounds(false);
+ composite->InputEffects().push_back(source_graphic1);
+ composite->InputEffects().push_back(source_graphic2);
+ return composite;
+ }
+
+ const IntRect kInput1Rect = {50, -50, 100, 100};
+};
+
+#define EXPECT_INTERSECTION(c) \
+ do { \
+ EXPECT_TRUE(c->MapRect(FloatRect()).IsEmpty()); \
+ EXPECT_TRUE(c->MapRect(FloatRect(0, 0, 50, 50)).IsEmpty()); \
+ EXPECT_EQ(FloatRect(50, 0, 100, 50), \
+ c->MapRect(FloatRect(0, 0, 200, 200))); \
+ } while (false)
+
+#define EXPECT_INPUT1(c) \
+ do { \
+ EXPECT_EQ(FloatRect(kInput1Rect), c->MapRect(FloatRect())); \
+ EXPECT_EQ(FloatRect(kInput1Rect), c->MapRect(FloatRect(0, 0, 50, 50))); \
+ EXPECT_EQ(FloatRect(kInput1Rect), c->MapRect(FloatRect(0, 0, 200, 200))); \
+ } while (false)
+
+#define EXPECT_INPUT2(c) \
+ do { \
+ EXPECT_TRUE(c->MapRect(FloatRect()).IsEmpty()); \
+ EXPECT_EQ(FloatRect(0, 0, 50, 50), c->MapRect(FloatRect(0, 0, 50, 50))); \
+ EXPECT_EQ(FloatRect(0, 0, 200, 200), \
+ c->MapRect(FloatRect(0, 0, 200, 200))); \
+ } while (false)
+
+#define EXPECT_UNION(c) \
+ do { \
+ EXPECT_EQ(FloatRect(kInput1Rect), c->MapRect(FloatRect())); \
+ EXPECT_EQ(FloatRect(0, -50, 150, 100), \
+ c->MapRect(FloatRect(0, 0, 50, 50))); \
+ EXPECT_EQ(FloatRect(0, -50, 200, 250), \
+ c->MapRect(FloatRect(0, 0, 200, 200))); \
+ } while (false)
+
+#define EXPECT_EMPTY(c) \
+ do { \
+ EXPECT_TRUE(c->MapRect(FloatRect()).IsEmpty()); \
+ EXPECT_TRUE(c->MapRect(FloatRect(0, 0, 50, 50)).IsEmpty()); \
+ EXPECT_TRUE(c->MapRect(FloatRect(0, 0, 200, 200)).IsEmpty()); \
+ } while (false)
+
+TEST_F(FECompositeTest, MapRectIn) {
+ EXPECT_INTERSECTION(CreateComposite(FECOMPOSITE_OPERATOR_IN));
+}
+
+TEST_F(FECompositeTest, MapRectATop) {
+ EXPECT_INPUT2(CreateComposite(FECOMPOSITE_OPERATOR_ATOP));
+}
+
+TEST_F(FECompositeTest, MapRectOtherOperators) {
+ EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_OVER));
+ EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_OUT));
+ EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_XOR));
+ EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_LIGHTER));
+}
+
+TEST_F(FECompositeTest, MapRectArithmetic) {
+ EXPECT_EMPTY(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 0, 0, 0, 0));
+ EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 0, 0, 0, 1));
+ EXPECT_INPUT2(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 0, 0, 1, 0));
+ EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 0, 0, 1, 1));
+ EXPECT_INPUT1(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 0, 1, 0, 0));
+ EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 0, 1, 0, 1));
+ EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 0, 1, 1, 0));
+ EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 0, 1, 1, 1));
+ EXPECT_INTERSECTION(
+ CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 1, 0, 0, 0));
+ EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 1, 0, 0, 1));
+ EXPECT_INPUT2(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 1, 0, 1, 0));
+ EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 1, 0, 1, 1));
+ EXPECT_INPUT1(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 1, 1, 0, 0));
+ EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 1, 1, 0, 1));
+ EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 1, 1, 1, 0));
+ EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 1, 1, 1, 1));
+}
+
+TEST_F(FECompositeTest, MapRectArithmeticK4Clipped) {
+ // Arithmetic operator with positive K4 will always affect the whole primitive
+ // subregion.
+ auto* c = CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 1, 1, 1, 1);
+ c->SetClipsToBounds(true);
+ FloatRect bounds(222, 333, 444, 555);
+ c->SetFilterPrimitiveSubregion(bounds);
+ EXPECT_EQ(bounds, c->MapRect(FloatRect()));
+ EXPECT_EQ(bounds, c->MapRect(FloatRect(100, 200, 300, 400)));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_convolve_matrix.cc b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_convolve_matrix.cc
new file mode 100644
index 00000000000..d9ac47ac002
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_convolve_matrix.cc
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
+ * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
+ * Copyright (C) 2010 Zoltan Herczeg <zherczeg@webkit.org>
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "third_party/blink/renderer/platform/graphics/filters/fe_convolve_matrix.h"
+
+#include <memory>
+#include "SkMatrixConvolutionImageFilter.h"
+#include "third_party/blink/renderer/platform/graphics/filters/paint_filter_builder.h"
+#include "third_party/blink/renderer/platform/text/text_stream.h"
+#include "third_party/blink/renderer/platform/wtf/checked_numeric.h"
+
+namespace blink {
+
+FEConvolveMatrix::FEConvolveMatrix(Filter* filter,
+ const IntSize& kernel_size,
+ float divisor,
+ float bias,
+ const IntPoint& target_offset,
+ EdgeModeType edge_mode,
+ bool preserve_alpha,
+ const Vector<float>& kernel_matrix)
+ : FilterEffect(filter),
+ kernel_size_(kernel_size),
+ divisor_(divisor),
+ bias_(bias),
+ target_offset_(target_offset),
+ edge_mode_(edge_mode),
+ preserve_alpha_(preserve_alpha),
+ kernel_matrix_(kernel_matrix) {}
+
+FEConvolveMatrix* FEConvolveMatrix::Create(Filter* filter,
+ const IntSize& kernel_size,
+ float divisor,
+ float bias,
+ const IntPoint& target_offset,
+ EdgeModeType edge_mode,
+ bool preserve_alpha,
+ const Vector<float>& kernel_matrix) {
+ return new FEConvolveMatrix(filter, kernel_size, divisor, bias, target_offset,
+ edge_mode, preserve_alpha, kernel_matrix);
+}
+
+FloatRect FEConvolveMatrix::MapEffect(const FloatRect& rect) const {
+ if (!ParametersValid())
+ return rect;
+ FloatRect result = rect;
+ result.MoveBy(-target_offset_);
+ result.Expand(FloatSize(kernel_size_));
+ return result;
+}
+
+bool FEConvolveMatrix::SetDivisor(float divisor) {
+ if (divisor_ == divisor)
+ return false;
+ divisor_ = divisor;
+ return true;
+}
+
+bool FEConvolveMatrix::SetBias(float bias) {
+ if (bias_ == bias)
+ return false;
+ bias_ = bias;
+ return true;
+}
+
+bool FEConvolveMatrix::SetTargetOffset(const IntPoint& target_offset) {
+ if (target_offset_ == target_offset)
+ return false;
+ target_offset_ = target_offset;
+ return true;
+}
+
+bool FEConvolveMatrix::SetEdgeMode(EdgeModeType edge_mode) {
+ if (edge_mode_ == edge_mode)
+ return false;
+ edge_mode_ = edge_mode;
+ return true;
+}
+
+bool FEConvolveMatrix::SetPreserveAlpha(bool preserve_alpha) {
+ if (preserve_alpha_ == preserve_alpha)
+ return false;
+ preserve_alpha_ = preserve_alpha;
+ return true;
+}
+
+SkMatrixConvolutionImageFilter::TileMode ToSkiaTileMode(
+ EdgeModeType edge_mode) {
+ switch (edge_mode) {
+ case EDGEMODE_DUPLICATE:
+ return SkMatrixConvolutionImageFilter::kClamp_TileMode;
+ case EDGEMODE_WRAP:
+ return SkMatrixConvolutionImageFilter::kRepeat_TileMode;
+ case EDGEMODE_NONE:
+ return SkMatrixConvolutionImageFilter::kClampToBlack_TileMode;
+ default:
+ return SkMatrixConvolutionImageFilter::kClamp_TileMode;
+ }
+}
+
+bool FEConvolveMatrix::ParametersValid() const {
+ if (kernel_size_.IsEmpty())
+ return false;
+ uint64_t kernel_area = kernel_size_.Area();
+ if (!CheckedNumeric<int>(kernel_area).IsValid())
+ return false;
+ if (SafeCast<size_t>(kernel_area) != kernel_matrix_.size())
+ return false;
+ if (target_offset_.X() < 0 || target_offset_.X() >= kernel_size_.Width())
+ return false;
+ if (target_offset_.Y() < 0 || target_offset_.Y() >= kernel_size_.Height())
+ return false;
+ if (!divisor_)
+ return false;
+ return true;
+}
+
+sk_sp<PaintFilter> FEConvolveMatrix::CreateImageFilter() {
+ if (!ParametersValid())
+ return CreateTransparentBlack();
+
+ sk_sp<PaintFilter> input(
+ PaintFilterBuilder::Build(InputEffect(0), OperatingInterpolationSpace()));
+ SkISize kernel_size(
+ SkISize::Make(kernel_size_.Width(), kernel_size_.Height()));
+ // parametersValid() above checks that the kernel area fits in int.
+ int num_elements = SafeCast<int>(kernel_size_.Area());
+ SkScalar gain = SkFloatToScalar(1.0f / divisor_);
+ SkScalar bias = SkFloatToScalar(bias_ * 255);
+ SkIPoint target = SkIPoint::Make(target_offset_.X(), target_offset_.Y());
+ MatrixConvolutionPaintFilter::TileMode tile_mode = ToSkiaTileMode(edge_mode_);
+ bool convolve_alpha = !preserve_alpha_;
+ auto kernel = std::make_unique<SkScalar[]>(num_elements);
+ for (int i = 0; i < num_elements; ++i)
+ kernel[i] = SkFloatToScalar(kernel_matrix_[num_elements - 1 - i]);
+ PaintFilter::CropRect crop_rect = GetCropRect();
+ return sk_make_sp<MatrixConvolutionPaintFilter>(
+ kernel_size, kernel.get(), gain, bias, target, tile_mode, convolve_alpha,
+ std::move(input), &crop_rect);
+}
+
+static TextStream& operator<<(TextStream& ts, const EdgeModeType& type) {
+ switch (type) {
+ case EDGEMODE_UNKNOWN:
+ ts << "UNKNOWN";
+ break;
+ case EDGEMODE_DUPLICATE:
+ ts << "DUPLICATE";
+ break;
+ case EDGEMODE_WRAP:
+ ts << "WRAP";
+ break;
+ case EDGEMODE_NONE:
+ ts << "NONE";
+ break;
+ }
+ return ts;
+}
+
+TextStream& FEConvolveMatrix::ExternalRepresentation(TextStream& ts,
+ int indent) const {
+ WriteIndent(ts, indent);
+ ts << "[feConvolveMatrix";
+ FilterEffect::ExternalRepresentation(ts);
+ ts << " order=\"" << FloatSize(kernel_size_) << "\" "
+ << "kernelMatrix=\"" << kernel_matrix_ << "\" "
+ << "divisor=\"" << divisor_ << "\" "
+ << "bias=\"" << bias_ << "\" "
+ << "target=\"" << target_offset_ << "\" "
+ << "edgeMode=\"" << edge_mode_ << "\" "
+ << "preserveAlpha=\"" << preserve_alpha_ << "\"]\n";
+ InputEffect(0)->ExternalRepresentation(ts, indent + 1);
+ return ts;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_convolve_matrix.h b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_convolve_matrix.h
new file mode 100644
index 00000000000..f53f6d9f3ea
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_convolve_matrix.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
+ * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2010 Zoltan Herczeg <zherczeg@webkit.org>
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_CONVOLVE_MATRIX_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_CONVOLVE_MATRIX_H_
+
+#include "third_party/blink/renderer/platform/geometry/int_point.h"
+#include "third_party/blink/renderer/platform/geometry/int_size.h"
+#include "third_party/blink/renderer/platform/graphics/filters/filter.h"
+#include "third_party/blink/renderer/platform/graphics/filters/filter_effect.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+enum EdgeModeType {
+ EDGEMODE_UNKNOWN = 0,
+ EDGEMODE_DUPLICATE = 1,
+ EDGEMODE_WRAP = 2,
+ EDGEMODE_NONE = 3
+};
+
+class PLATFORM_EXPORT FEConvolveMatrix final : public FilterEffect {
+ public:
+ static FEConvolveMatrix* Create(Filter*,
+ const IntSize&,
+ float,
+ float,
+ const IntPoint&,
+ EdgeModeType,
+ bool,
+ const Vector<float>&);
+
+ bool SetDivisor(float);
+ bool SetBias(float);
+ bool SetTargetOffset(const IntPoint&);
+ bool SetEdgeMode(EdgeModeType);
+ bool SetPreserveAlpha(bool);
+
+ TextStream& ExternalRepresentation(TextStream&, int indention) const override;
+
+ private:
+ FEConvolveMatrix(Filter*,
+ const IntSize&,
+ float,
+ float,
+ const IntPoint&,
+ EdgeModeType,
+ bool,
+ const Vector<float>&);
+
+ FloatRect MapEffect(const FloatRect&) const final;
+
+ sk_sp<PaintFilter> CreateImageFilter() override;
+
+ bool ParametersValid() const;
+
+ IntSize kernel_size_;
+ float divisor_;
+ float bias_;
+ IntPoint target_offset_;
+ EdgeModeType edge_mode_;
+ bool preserve_alpha_;
+ Vector<float> kernel_matrix_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_CONVOLVE_MATRIX_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_diffuse_lighting.cc b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_diffuse_lighting.cc
new file mode 100644
index 00000000000..69459d5b7a4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_diffuse_lighting.cc
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
+ * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "third_party/blink/renderer/platform/graphics/filters/fe_diffuse_lighting.h"
+
+#include "third_party/blink/renderer/platform/graphics/filters/light_source.h"
+#include "third_party/blink/renderer/platform/text/text_stream.h"
+
+namespace blink {
+
+FEDiffuseLighting::FEDiffuseLighting(Filter* filter,
+ const Color& lighting_color,
+ float surface_scale,
+ float diffuse_constant,
+ scoped_refptr<LightSource> light_source)
+ : FELighting(filter,
+ kDiffuseLighting,
+ lighting_color,
+ surface_scale,
+ diffuse_constant,
+ 0,
+ 0,
+ std::move(light_source)) {}
+
+FEDiffuseLighting* FEDiffuseLighting::Create(
+ Filter* filter,
+ const Color& lighting_color,
+ float surface_scale,
+ float diffuse_constant,
+ scoped_refptr<LightSource> light_source) {
+ return new FEDiffuseLighting(filter, lighting_color, surface_scale,
+ diffuse_constant, std::move(light_source));
+}
+
+FEDiffuseLighting::~FEDiffuseLighting() = default;
+
+Color FEDiffuseLighting::LightingColor() const {
+ return lighting_color_;
+}
+
+bool FEDiffuseLighting::SetLightingColor(const Color& lighting_color) {
+ if (lighting_color_ == lighting_color)
+ return false;
+ lighting_color_ = lighting_color;
+ return true;
+}
+
+float FEDiffuseLighting::SurfaceScale() const {
+ return surface_scale_;
+}
+
+bool FEDiffuseLighting::SetSurfaceScale(float surface_scale) {
+ if (surface_scale_ == surface_scale)
+ return false;
+ surface_scale_ = surface_scale;
+ return true;
+}
+
+float FEDiffuseLighting::DiffuseConstant() const {
+ return diffuse_constant_;
+}
+
+bool FEDiffuseLighting::SetDiffuseConstant(float diffuse_constant) {
+ diffuse_constant = std::max(diffuse_constant, 0.0f);
+ if (diffuse_constant_ == diffuse_constant)
+ return false;
+ diffuse_constant_ = diffuse_constant;
+ return true;
+}
+
+const LightSource* FEDiffuseLighting::GetLightSource() const {
+ return light_source_.get();
+}
+
+void FEDiffuseLighting::SetLightSource(
+ scoped_refptr<LightSource> light_source) {
+ light_source_ = std::move(light_source);
+}
+
+TextStream& FEDiffuseLighting::ExternalRepresentation(TextStream& ts,
+ int indent) const {
+ WriteIndent(ts, indent);
+ ts << "[feDiffuseLighting";
+ FilterEffect::ExternalRepresentation(ts);
+ ts << " surfaceScale=\"" << surface_scale_ << "\" "
+ << "diffuseConstant=\"" << diffuse_constant_ << "\"]\n";
+ InputEffect(0)->ExternalRepresentation(ts, indent + 1);
+ return ts;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_diffuse_lighting.h b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_diffuse_lighting.h
new file mode 100644
index 00000000000..a29cdf617c7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_diffuse_lighting.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
+ * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_DIFFUSE_LIGHTING_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_DIFFUSE_LIGHTING_H_
+
+#include "third_party/blink/renderer/platform/graphics/filters/fe_lighting.h"
+
+namespace blink {
+
+class LightSource;
+
+class PLATFORM_EXPORT FEDiffuseLighting final : public FELighting {
+ public:
+ static FEDiffuseLighting* Create(Filter*,
+ const Color&,
+ float,
+ float,
+ scoped_refptr<LightSource>);
+ ~FEDiffuseLighting() override;
+
+ Color LightingColor() const;
+ bool SetLightingColor(const Color&);
+
+ float SurfaceScale() const;
+ bool SetSurfaceScale(float);
+
+ float DiffuseConstant() const;
+ bool SetDiffuseConstant(float);
+
+ const LightSource* GetLightSource() const;
+ void SetLightSource(scoped_refptr<LightSource>);
+
+ TextStream& ExternalRepresentation(TextStream&, int indention) const override;
+
+ private:
+ FEDiffuseLighting(Filter*,
+ const Color&,
+ float,
+ float,
+ scoped_refptr<LightSource>);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_DIFFUSE_LIGHTING_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_displacement_map.cc b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_displacement_map.cc
new file mode 100644
index 00000000000..3515d88a43e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_displacement_map.cc
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
+ * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
+ * Copyright (C) Research In Motion Limited 2010. All rights reserved.
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "third_party/blink/renderer/platform/graphics/filters/fe_displacement_map.h"
+
+#include "SkDisplacementMapEffect.h"
+#include "third_party/blink/renderer/platform/graphics/filters/filter.h"
+#include "third_party/blink/renderer/platform/graphics/filters/paint_filter_builder.h"
+#include "third_party/blink/renderer/platform/text/text_stream.h"
+
+namespace blink {
+
+FEDisplacementMap::FEDisplacementMap(Filter* filter,
+ ChannelSelectorType x_channel_selector,
+ ChannelSelectorType y_channel_selector,
+ float scale)
+ : FilterEffect(filter),
+ x_channel_selector_(x_channel_selector),
+ y_channel_selector_(y_channel_selector),
+ scale_(scale) {}
+
+FEDisplacementMap* FEDisplacementMap::Create(
+ Filter* filter,
+ ChannelSelectorType x_channel_selector,
+ ChannelSelectorType y_channel_selector,
+ float scale) {
+ return new FEDisplacementMap(filter, x_channel_selector, y_channel_selector,
+ scale);
+}
+
+FloatRect FEDisplacementMap::MapEffect(const FloatRect& rect) const {
+ FloatRect result = rect;
+ result.InflateX(GetFilter()->ApplyHorizontalScale(std::abs(scale_) / 2));
+ result.InflateY(GetFilter()->ApplyVerticalScale(std::abs(scale_) / 2));
+ return result;
+}
+
+FloatRect FEDisplacementMap::MapInputs(const FloatRect& rect) const {
+ return InputEffect(0)->MapRect(rect);
+}
+
+ChannelSelectorType FEDisplacementMap::XChannelSelector() const {
+ return x_channel_selector_;
+}
+
+bool FEDisplacementMap::SetXChannelSelector(
+ const ChannelSelectorType x_channel_selector) {
+ if (x_channel_selector_ == x_channel_selector)
+ return false;
+ x_channel_selector_ = x_channel_selector;
+ return true;
+}
+
+ChannelSelectorType FEDisplacementMap::YChannelSelector() const {
+ return y_channel_selector_;
+}
+
+bool FEDisplacementMap::SetYChannelSelector(
+ const ChannelSelectorType y_channel_selector) {
+ if (y_channel_selector_ == y_channel_selector)
+ return false;
+ y_channel_selector_ = y_channel_selector;
+ return true;
+}
+
+float FEDisplacementMap::Scale() const {
+ return scale_;
+}
+
+bool FEDisplacementMap::SetScale(float scale) {
+ if (scale_ == scale)
+ return false;
+ scale_ = scale;
+ return true;
+}
+
+static SkDisplacementMapEffect::ChannelSelectorType ToSkiaMode(
+ ChannelSelectorType type) {
+ switch (type) {
+ case CHANNEL_R:
+ return SkDisplacementMapEffect::kR_ChannelSelectorType;
+ case CHANNEL_G:
+ return SkDisplacementMapEffect::kG_ChannelSelectorType;
+ case CHANNEL_B:
+ return SkDisplacementMapEffect::kB_ChannelSelectorType;
+ case CHANNEL_A:
+ return SkDisplacementMapEffect::kA_ChannelSelectorType;
+ case CHANNEL_UNKNOWN:
+ default:
+ return SkDisplacementMapEffect::kUnknown_ChannelSelectorType;
+ }
+}
+
+sk_sp<PaintFilter> FEDisplacementMap::CreateImageFilter() {
+ sk_sp<PaintFilter> color =
+ PaintFilterBuilder::Build(InputEffect(0), OperatingInterpolationSpace());
+ // FEDisplacementMap must be a pass-through filter if
+ // the origin is tainted. See:
+ // https://drafts.fxtf.org/filter-effects/#fedisplacemnentmap-restrictions.
+ if (InputEffect(1)->OriginTainted())
+ return color;
+
+ sk_sp<PaintFilter> displ =
+ PaintFilterBuilder::Build(InputEffect(1), OperatingInterpolationSpace());
+ SkDisplacementMapEffect::ChannelSelectorType type_x =
+ ToSkiaMode(x_channel_selector_);
+ SkDisplacementMapEffect::ChannelSelectorType type_y =
+ ToSkiaMode(y_channel_selector_);
+ PaintFilter::CropRect crop_rect = GetCropRect();
+ // FIXME : Only applyHorizontalScale is used and applyVerticalScale is ignored
+ // This can be fixed by adding a 2nd scale parameter to
+ // DisplacementMapEffectPaintFilter.
+ return sk_make_sp<DisplacementMapEffectPaintFilter>(
+ type_x, type_y,
+ SkFloatToScalar(GetFilter()->ApplyHorizontalScale(scale_)),
+ std::move(displ), std::move(color), &crop_rect);
+}
+
+static TextStream& operator<<(TextStream& ts, const ChannelSelectorType& type) {
+ switch (type) {
+ case CHANNEL_UNKNOWN:
+ ts << "UNKNOWN";
+ break;
+ case CHANNEL_R:
+ ts << "RED";
+ break;
+ case CHANNEL_G:
+ ts << "GREEN";
+ break;
+ case CHANNEL_B:
+ ts << "BLUE";
+ break;
+ case CHANNEL_A:
+ ts << "ALPHA";
+ break;
+ }
+ return ts;
+}
+
+TextStream& FEDisplacementMap::ExternalRepresentation(TextStream& ts,
+ int indent) const {
+ WriteIndent(ts, indent);
+ ts << "[feDisplacementMap";
+ FilterEffect::ExternalRepresentation(ts);
+ ts << " scale=\"" << scale_ << "\" "
+ << "xChannelSelector=\"" << x_channel_selector_ << "\" "
+ << "yChannelSelector=\"" << y_channel_selector_ << "\"]\n";
+ InputEffect(0)->ExternalRepresentation(ts, indent + 1);
+ InputEffect(1)->ExternalRepresentation(ts, indent + 1);
+ return ts;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_displacement_map.h b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_displacement_map.h
new file mode 100644
index 00000000000..cd99d19e32d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_displacement_map.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
+ * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_DISPLACEMENT_MAP_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_DISPLACEMENT_MAP_H_
+
+#include "third_party/blink/renderer/platform/graphics/filters/filter_effect.h"
+
+namespace blink {
+
+enum ChannelSelectorType {
+ CHANNEL_UNKNOWN = 0,
+ CHANNEL_R = 1,
+ CHANNEL_G = 2,
+ CHANNEL_B = 3,
+ CHANNEL_A = 4
+};
+
+class PLATFORM_EXPORT FEDisplacementMap final : public FilterEffect {
+ public:
+ static FEDisplacementMap* Create(Filter*,
+ ChannelSelectorType x_channel_selector,
+ ChannelSelectorType y_channel_selector,
+ float);
+
+ ChannelSelectorType XChannelSelector() const;
+ bool SetXChannelSelector(const ChannelSelectorType);
+
+ ChannelSelectorType YChannelSelector() const;
+ bool SetYChannelSelector(const ChannelSelectorType);
+
+ float Scale() const;
+ bool SetScale(float);
+
+ TextStream& ExternalRepresentation(TextStream&, int indention) const override;
+
+ private:
+ FEDisplacementMap(Filter*,
+ ChannelSelectorType x_channel_selector,
+ ChannelSelectorType y_channel_selector,
+ float);
+
+ FloatRect MapInputs(const FloatRect&) const override;
+ FloatRect MapEffect(const FloatRect&) const override;
+
+ sk_sp<PaintFilter> CreateImageFilter() override;
+
+ ChannelSelectorType x_channel_selector_;
+ ChannelSelectorType y_channel_selector_;
+ float scale_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_DISPLACEMENT_MAP_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_drop_shadow.cc b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_drop_shadow.cc
new file mode 100644
index 00000000000..cad25647e74
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_drop_shadow.cc
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) Research In Motion Limited 2011. All rights reserved.
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "third_party/blink/renderer/platform/graphics/filters/fe_drop_shadow.h"
+
+#include "third_party/blink/renderer/platform/graphics/filters/fe_gaussian_blur.h"
+#include "third_party/blink/renderer/platform/graphics/filters/filter.h"
+#include "third_party/blink/renderer/platform/graphics/filters/paint_filter_builder.h"
+#include "third_party/blink/renderer/platform/text/text_stream.h"
+#include "third_party/skia/include/effects/SkDropShadowImageFilter.h"
+
+namespace blink {
+
+FEDropShadow::FEDropShadow(Filter* filter,
+ float std_x,
+ float std_y,
+ float dx,
+ float dy,
+ const Color& shadow_color,
+ float shadow_opacity)
+ : FilterEffect(filter),
+ std_x_(std_x),
+ std_y_(std_y),
+ dx_(dx),
+ dy_(dy),
+ shadow_color_(shadow_color),
+ shadow_opacity_(shadow_opacity) {}
+
+FEDropShadow* FEDropShadow::Create(Filter* filter,
+ float std_x,
+ float std_y,
+ float dx,
+ float dy,
+ const Color& shadow_color,
+ float shadow_opacity) {
+ return new FEDropShadow(filter, std_x, std_y, dx, dy, shadow_color,
+ shadow_opacity);
+}
+
+FloatRect FEDropShadow::MapEffect(const FloatSize& std_deviation,
+ const FloatPoint& offset,
+ const FloatRect& rect) {
+ FloatRect offset_rect = rect;
+ offset_rect.MoveBy(offset);
+ FloatRect blurred_rect =
+ FEGaussianBlur::MapEffect(std_deviation, offset_rect);
+ return UnionRect(blurred_rect, rect);
+}
+
+FloatRect FEDropShadow::MapEffect(const FloatRect& rect) const {
+ const Filter* filter = this->GetFilter();
+ DCHECK(filter);
+ FloatPoint offset(filter->ApplyHorizontalScale(dx_),
+ filter->ApplyVerticalScale(dy_));
+ FloatSize std_error(filter->ApplyHorizontalScale(std_x_),
+ filter->ApplyVerticalScale(std_y_));
+ return MapEffect(std_error, offset, rect);
+}
+
+sk_sp<PaintFilter> FEDropShadow::CreateImageFilter() {
+ sk_sp<PaintFilter> input(
+ PaintFilterBuilder::Build(InputEffect(0), OperatingInterpolationSpace()));
+ float dx = GetFilter()->ApplyHorizontalScale(dx_);
+ float dy = GetFilter()->ApplyVerticalScale(dy_);
+ float std_x = GetFilter()->ApplyHorizontalScale(std_x_);
+ float std_y = GetFilter()->ApplyVerticalScale(std_y_);
+ Color color = AdaptColorToOperatingInterpolationSpace(
+ shadow_color_.CombineWithAlpha(shadow_opacity_));
+ PaintFilter::CropRect crop_rect = GetCropRect();
+ return sk_make_sp<DropShadowPaintFilter>(
+ SkFloatToScalar(dx), SkFloatToScalar(dy), SkFloatToScalar(std_x),
+ SkFloatToScalar(std_y), color.Rgb(),
+ SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode,
+ std::move(input), &crop_rect);
+}
+
+TextStream& FEDropShadow::ExternalRepresentation(TextStream& ts,
+ int indent) const {
+ WriteIndent(ts, indent);
+ ts << "[feDropShadow";
+ FilterEffect::ExternalRepresentation(ts);
+ ts << " stdDeviation=\"" << std_x_ << ", " << std_y_ << "\" dx=\"" << dx_
+ << "\" dy=\"" << dy_ << "\" flood-color=\""
+ << shadow_color_.NameForLayoutTreeAsText() << "\" flood-opacity=\""
+ << shadow_opacity_ << "]\n";
+ InputEffect(0)->ExternalRepresentation(ts, indent + 1);
+ return ts;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_drop_shadow.h b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_drop_shadow.h
new file mode 100644
index 00000000000..6b1690268ea
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_drop_shadow.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) Research In Motion Limited 2011. All rights reserved.
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_DROP_SHADOW_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_DROP_SHADOW_H_
+
+#include "third_party/blink/renderer/platform/graphics/color.h"
+#include "third_party/blink/renderer/platform/graphics/filters/filter_effect.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT FEDropShadow final : public FilterEffect {
+ public:
+ static FEDropShadow*
+ Create(Filter*, float, float, float, float, const Color&, float);
+
+ // Compute which destination area will be affected when applying a drop
+ // shadow effect with |stdDeviation| and |offset| to an area |rect|.
+ static FloatRect MapEffect(const FloatSize& std_deviation,
+ const FloatPoint& offset,
+ const FloatRect&);
+
+ void SetShadowColor(const Color& color) { shadow_color_ = color; }
+ void SetShadowOpacity(float opacity) { shadow_opacity_ = opacity; }
+
+ TextStream& ExternalRepresentation(TextStream&, int indention) const override;
+
+ private:
+ FEDropShadow(Filter*, float, float, float, float, const Color&, float);
+
+ FloatRect MapEffect(const FloatRect&) const override;
+
+ sk_sp<PaintFilter> CreateImageFilter() override;
+
+ float std_x_;
+ float std_y_;
+ float dx_;
+ float dy_;
+ Color shadow_color_;
+ float shadow_opacity_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_DROP_SHADOW_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_flood.cc b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_flood.cc
new file mode 100644
index 00000000000..e3b09cce37d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_flood.cc
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
+ * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "third_party/blink/renderer/platform/graphics/filters/fe_flood.h"
+
+#include "SkColorFilter.h"
+#include "SkColorFilterImageFilter.h"
+#include "third_party/blink/renderer/platform/text/text_stream.h"
+
+namespace blink {
+
+FEFlood::FEFlood(Filter* filter, const Color& flood_color, float flood_opacity)
+ : FilterEffect(filter),
+ flood_color_(flood_color),
+ flood_opacity_(flood_opacity) {
+ FilterEffect::SetOperatingInterpolationSpace(kInterpolationSpaceSRGB);
+}
+
+FEFlood* FEFlood::Create(Filter* filter,
+ const Color& flood_color,
+ float flood_opacity) {
+ return new FEFlood(filter, flood_color, flood_opacity);
+}
+
+Color FEFlood::FloodColor() const {
+ return flood_color_;
+}
+
+bool FEFlood::SetFloodColor(const Color& color) {
+ if (flood_color_ == color)
+ return false;
+ flood_color_ = color;
+ return true;
+}
+
+float FEFlood::FloodOpacity() const {
+ return flood_opacity_;
+}
+
+bool FEFlood::SetFloodOpacity(float flood_opacity) {
+ if (flood_opacity_ == flood_opacity)
+ return false;
+ flood_opacity_ = flood_opacity;
+ return true;
+}
+
+sk_sp<PaintFilter> FEFlood::CreateImageFilter() {
+ Color color = FloodColor().CombineWithAlpha(FloodOpacity());
+ PaintFilter::CropRect rect = GetCropRect();
+ return sk_make_sp<ColorFilterPaintFilter>(
+ SkColorFilter::MakeModeFilter(color.Rgb(), SkBlendMode::kSrc), nullptr,
+ &rect);
+}
+
+TextStream& FEFlood::ExternalRepresentation(TextStream& ts, int indent) const {
+ WriteIndent(ts, indent);
+ ts << "[feFlood";
+ FilterEffect::ExternalRepresentation(ts);
+ ts << " flood-color=\"" << FloodColor().NameForLayoutTreeAsText() << "\" "
+ << "flood-opacity=\"" << FloodOpacity() << "\"]\n";
+ return ts;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_flood.h b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_flood.h
new file mode 100644
index 00000000000..8f049fc1179
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_flood.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
+ * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_FLOOD_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_FLOOD_H_
+
+#include "third_party/blink/renderer/platform/graphics/color.h"
+#include "third_party/blink/renderer/platform/graphics/filters/filter_effect.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT FEFlood final : public FilterEffect {
+ public:
+ static FEFlood* Create(Filter*, const Color&, float);
+
+ Color FloodColor() const;
+ bool SetFloodColor(const Color&);
+
+ float FloodOpacity() const;
+ bool SetFloodOpacity(float);
+
+ // feFlood does not perform color interpolation of any kind, so the result is
+ // always in the current color space regardless of the value of
+ // color-interpolation-filters.
+ void SetOperatingInterpolationSpace(InterpolationSpace) override {}
+
+ TextStream& ExternalRepresentation(TextStream&, int indention) const override;
+
+ private:
+ FEFlood(Filter*, const Color&, float);
+
+ sk_sp<PaintFilter> CreateImageFilter() override;
+
+ Color flood_color_;
+ float flood_opacity_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_FLOOD_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_gaussian_blur.cc b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_gaussian_blur.cc
new file mode 100644
index 00000000000..7f6eb5e12bb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_gaussian_blur.cc
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
+ * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
+ * Copyright (C) 2010 Igalia, S.L.
+ * Copyright (C) Research In Motion Limited 2010. All rights reserved.
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "third_party/blink/renderer/platform/graphics/filters/fe_gaussian_blur.h"
+
+#include "third_party/blink/renderer/platform/graphics/filters/filter.h"
+#include "third_party/blink/renderer/platform/graphics/filters/paint_filter_builder.h"
+#include "third_party/blink/renderer/platform/text/text_stream.h"
+
+#include "SkBlurImageFilter.h"
+
+namespace blink {
+
+namespace {
+
+inline unsigned ApproximateBoxWidth(float s) {
+ return static_cast<unsigned>(
+ floorf(s * (3 / 4.f * sqrtf(twoPiFloat)) + 0.5f));
+}
+
+IntSize CalculateKernelSize(const FloatSize& std) {
+ DCHECK(std.Width() >= 0 && std.Height() >= 0);
+ IntSize kernel_size;
+ if (std.Width()) {
+ int size = std::max<unsigned>(2, ApproximateBoxWidth(std.Width()));
+ kernel_size.SetWidth(size);
+ }
+ if (std.Height()) {
+ int size = std::max<unsigned>(2, ApproximateBoxWidth(std.Height()));
+ kernel_size.SetHeight(size);
+ }
+ return kernel_size;
+}
+
+}
+
+FEGaussianBlur::FEGaussianBlur(Filter* filter, float x, float y)
+ : FilterEffect(filter), std_x_(x), std_y_(y) {}
+
+FEGaussianBlur* FEGaussianBlur::Create(Filter* filter, float x, float y) {
+ return new FEGaussianBlur(filter, x, y);
+}
+
+FloatRect FEGaussianBlur::MapEffect(const FloatSize& std_deviation,
+ const FloatRect& rect) {
+ IntSize kernel_size = CalculateKernelSize(std_deviation);
+ // We take the half kernel size and multiply it by three, because we run box
+ // blur three times.
+ FloatRect result = rect;
+ result.InflateX(3.0f * kernel_size.Width() * 0.5f);
+ result.InflateY(3.0f * kernel_size.Height() * 0.5f);
+ return result;
+}
+
+FloatRect FEGaussianBlur::MapEffect(const FloatRect& rect) const {
+ FloatSize std_error(GetFilter()->ApplyHorizontalScale(std_x_),
+ GetFilter()->ApplyVerticalScale(std_y_));
+ return MapEffect(std_error, rect);
+}
+
+sk_sp<PaintFilter> FEGaussianBlur::CreateImageFilter() {
+ sk_sp<PaintFilter> input(
+ PaintFilterBuilder::Build(InputEffect(0), OperatingInterpolationSpace()));
+ float std_x = GetFilter()->ApplyHorizontalScale(std_x_);
+ float std_y = GetFilter()->ApplyVerticalScale(std_y_);
+ PaintFilter::CropRect rect = GetCropRect();
+ return sk_make_sp<BlurPaintFilter>(
+ SkFloatToScalar(std_x), SkFloatToScalar(std_y),
+ BlurPaintFilter::TileMode::kClampToBlack_TileMode, std::move(input),
+ &rect);
+}
+
+TextStream& FEGaussianBlur::ExternalRepresentation(TextStream& ts,
+ int indent) const {
+ WriteIndent(ts, indent);
+ ts << "[feGaussianBlur";
+ FilterEffect::ExternalRepresentation(ts);
+ ts << " stdDeviation=\"" << std_x_ << ", " << std_y_ << "\"]\n";
+ InputEffect(0)->ExternalRepresentation(ts, indent + 1);
+ return ts;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_gaussian_blur.h b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_gaussian_blur.h
new file mode 100644
index 00000000000..55ea8f34cf0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_gaussian_blur.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
+ * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_GAUSSIAN_BLUR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_GAUSSIAN_BLUR_H_
+
+#include "third_party/blink/renderer/platform/graphics/filters/filter_effect.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT FEGaussianBlur final : public FilterEffect {
+ public:
+ static FEGaussianBlur* Create(Filter*, float, float);
+
+ // Compute which destination area will be affected when applying a gaussian
+ // blur effect with |stdDeviation| to an area |rect|.
+ static FloatRect MapEffect(const FloatSize& std_deviation, const FloatRect&);
+
+ TextStream& ExternalRepresentation(TextStream&, int indention) const override;
+
+ private:
+ FEGaussianBlur(Filter*, float, float);
+
+ FloatRect MapEffect(const FloatRect&) const override;
+
+ sk_sp<PaintFilter> CreateImageFilter() override;
+
+ float std_x_;
+ float std_y_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_GAUSSIAN_BLUR_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_lighting.cc b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_lighting.cc
new file mode 100644
index 00000000000..4ec708206e2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_lighting.cc
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2010 University of Szeged
+ * Copyright (C) 2010 Zoltan Herczeg
+ * Copyright (C) 2013 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED 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 "third_party/blink/renderer/platform/graphics/filters/fe_lighting.h"
+
+#include "SkLightingImageFilter.h"
+#include "SkPoint3.h"
+#include "third_party/blink/renderer/platform/graphics/filters/distant_light_source.h"
+#include "third_party/blink/renderer/platform/graphics/filters/paint_filter_builder.h"
+#include "third_party/blink/renderer/platform/graphics/filters/point_light_source.h"
+#include "third_party/blink/renderer/platform/graphics/filters/spot_light_source.h"
+
+namespace blink {
+
+FELighting::FELighting(Filter* filter,
+ LightingType lighting_type,
+ const Color& lighting_color,
+ float surface_scale,
+ float diffuse_constant,
+ float specular_constant,
+ float specular_exponent,
+ scoped_refptr<LightSource> light_source)
+ : FilterEffect(filter),
+ lighting_type_(lighting_type),
+ light_source_(std::move(light_source)),
+ lighting_color_(lighting_color),
+ surface_scale_(surface_scale),
+ diffuse_constant_(std::max(diffuse_constant, 0.0f)),
+ specular_constant_(std::max(specular_constant, 0.0f)),
+ specular_exponent_(clampTo(specular_exponent, 1.0f, 128.0f)) {}
+
+sk_sp<PaintFilter> FELighting::CreateImageFilter() {
+ if (!light_source_)
+ return CreateTransparentBlack();
+ PaintFilter::CropRect rect = GetCropRect();
+ Color light_color = AdaptColorToOperatingInterpolationSpace(lighting_color_);
+ sk_sp<PaintFilter> input(
+ PaintFilterBuilder::Build(InputEffect(0), OperatingInterpolationSpace()));
+ switch (light_source_->GetType()) {
+ case LS_DISTANT: {
+ DistantLightSource* distant_light_source =
+ static_cast<DistantLightSource*>(light_source_.get());
+ float azimuth_rad = deg2rad(distant_light_source->Azimuth());
+ float elevation_rad = deg2rad(distant_light_source->Elevation());
+ const SkPoint3 direction = SkPoint3::Make(
+ cosf(azimuth_rad) * cosf(elevation_rad),
+ sinf(azimuth_rad) * cosf(elevation_rad), sinf(elevation_rad));
+ return sk_make_sp<LightingDistantPaintFilter>(
+ GetLightingType(), direction, light_color.Rgb(), surface_scale_,
+ GetFilterConstant(), specular_exponent_, std::move(input), &rect);
+ }
+ case LS_POINT: {
+ PointLightSource* point_light_source =
+ static_cast<PointLightSource*>(light_source_.get());
+ const FloatPoint3D position = point_light_source->GetPosition();
+ const SkPoint3 sk_position =
+ SkPoint3::Make(position.X(), position.Y(), position.Z());
+ return sk_make_sp<LightingPointPaintFilter>(
+ GetLightingType(), sk_position, light_color.Rgb(), surface_scale_,
+ GetFilterConstant(), specular_exponent_, std::move(input), &rect);
+ }
+ case LS_SPOT: {
+ SpotLightSource* spot_light_source =
+ static_cast<SpotLightSource*>(light_source_.get());
+ const SkPoint3 location =
+ SkPoint3::Make(spot_light_source->GetPosition().X(),
+ spot_light_source->GetPosition().Y(),
+ spot_light_source->GetPosition().Z());
+ const SkPoint3 target =
+ SkPoint3::Make(spot_light_source->Direction().X(),
+ spot_light_source->Direction().Y(),
+ spot_light_source->Direction().Z());
+ float specular_exponent = spot_light_source->SpecularExponent();
+ float limiting_cone_angle = spot_light_source->LimitingConeAngle();
+ if (!limiting_cone_angle || limiting_cone_angle > 90 ||
+ limiting_cone_angle < -90)
+ limiting_cone_angle = 90;
+ return sk_make_sp<LightingSpotPaintFilter>(
+ GetLightingType(), location, target, specular_exponent,
+ limiting_cone_angle, light_color.Rgb(), surface_scale_,
+ GetFilterConstant(), specular_exponent_, std::move(input), &rect);
+ }
+ default:
+ NOTREACHED();
+ return nullptr;
+ }
+}
+
+PaintFilter::LightingType FELighting::GetLightingType() {
+ return specular_constant_ > 0 ? PaintFilter::LightingType::kSpecular
+ : PaintFilter::LightingType::kDiffuse;
+}
+
+float FELighting::GetFilterConstant() {
+ return GetLightingType() == PaintFilter::LightingType::kSpecular
+ ? specular_constant_
+ : diffuse_constant_;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_lighting.h b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_lighting.h
new file mode 100644
index 00000000000..6d6197fe07e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_lighting.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2010 University of Szeged
+ * Copyright (C) 2010 Zoltan Herczeg
+ * Copyright (C) 2013 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_LIGHTING_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_LIGHTING_H_
+
+#include "third_party/blink/renderer/platform/graphics/color.h"
+#include "third_party/blink/renderer/platform/graphics/filters/filter_effect.h"
+
+// Common base class for FEDiffuseLighting and FESpecularLighting
+
+namespace blink {
+
+class LightSource;
+
+class PLATFORM_EXPORT FELighting : public FilterEffect {
+ protected:
+ enum LightingType { kDiffuseLighting, kSpecularLighting };
+
+ sk_sp<PaintFilter> CreateImageFilter() override;
+
+ bool AffectsTransparentPixels() const override { return true; }
+
+ FELighting(Filter*,
+ LightingType,
+ const Color&,
+ float,
+ float,
+ float,
+ float,
+ scoped_refptr<LightSource>);
+
+ PaintFilter::LightingType GetLightingType();
+ float GetFilterConstant();
+
+ LightingType lighting_type_;
+ scoped_refptr<LightSource> light_source_;
+
+ Color lighting_color_;
+ float surface_scale_;
+ float diffuse_constant_;
+ float specular_constant_;
+ float specular_exponent_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_LIGHTING_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_merge.cc b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_merge.cc
new file mode 100644
index 00000000000..d587315b248
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_merge.cc
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
+ * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "third_party/blink/renderer/platform/graphics/filters/fe_merge.h"
+
+#include <memory>
+#include "SkMergeImageFilter.h"
+#include "third_party/blink/renderer/platform/graphics/filters/paint_filter_builder.h"
+#include "third_party/blink/renderer/platform/text/text_stream.h"
+
+namespace blink {
+
+FEMerge::FEMerge(Filter* filter) : FilterEffect(filter) {}
+
+FEMerge* FEMerge::Create(Filter* filter) {
+ return new FEMerge(filter);
+}
+
+sk_sp<PaintFilter> FEMerge::CreateImageFilter() {
+ unsigned size = NumberOfEffectInputs();
+
+ auto input_refs = std::make_unique<sk_sp<PaintFilter>[]>(size);
+ for (unsigned i = 0; i < size; ++i) {
+ input_refs[i] = PaintFilterBuilder::Build(InputEffect(i),
+ OperatingInterpolationSpace());
+ }
+ PaintFilter::CropRect rect = GetCropRect();
+ return sk_make_sp<MergePaintFilter>(input_refs.get(), size, &rect);
+}
+
+TextStream& FEMerge::ExternalRepresentation(TextStream& ts, int indent) const {
+ WriteIndent(ts, indent);
+ ts << "[feMerge";
+ FilterEffect::ExternalRepresentation(ts);
+ unsigned size = NumberOfEffectInputs();
+ ts << " mergeNodes=\"" << size << "\"]\n";
+ for (unsigned i = 0; i < size; ++i)
+ InputEffect(i)->ExternalRepresentation(ts, indent + 1);
+ return ts;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_merge.h b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_merge.h
new file mode 100644
index 00000000000..a5ffee7ee56
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_merge.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
+ * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_MERGE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_MERGE_H_
+
+#include "third_party/blink/renderer/platform/graphics/filters/filter_effect.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT FEMerge final : public FilterEffect {
+ public:
+ static FEMerge* Create(Filter*);
+
+ TextStream& ExternalRepresentation(TextStream&, int indention) const override;
+
+ private:
+ explicit FEMerge(Filter*);
+
+ sk_sp<PaintFilter> CreateImageFilter() override;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_MERGE_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_morphology.cc b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_morphology.cc
new file mode 100644
index 00000000000..b95c0dd8b2f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_morphology.cc
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
+ * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
+ * Copyright (C) Research In Motion Limited 2010. All rights reserved.
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "third_party/blink/renderer/platform/graphics/filters/fe_morphology.h"
+
+#include "SkMorphologyImageFilter.h"
+#include "third_party/blink/renderer/platform/graphics/filters/filter.h"
+#include "third_party/blink/renderer/platform/graphics/filters/paint_filter_builder.h"
+#include "third_party/blink/renderer/platform/text/text_stream.h"
+
+namespace blink {
+
+FEMorphology::FEMorphology(Filter* filter,
+ MorphologyOperatorType type,
+ float radius_x,
+ float radius_y)
+ : FilterEffect(filter),
+ type_(type),
+ radius_x_(std::max(0.0f, radius_x)),
+ radius_y_(std::max(0.0f, radius_y)) {}
+
+FEMorphology* FEMorphology::Create(Filter* filter,
+ MorphologyOperatorType type,
+ float radius_x,
+ float radius_y) {
+ return new FEMorphology(filter, type, radius_x, radius_y);
+}
+
+MorphologyOperatorType FEMorphology::MorphologyOperator() const {
+ return type_;
+}
+
+bool FEMorphology::SetMorphologyOperator(MorphologyOperatorType type) {
+ if (type_ == type)
+ return false;
+ type_ = type;
+ return true;
+}
+
+float FEMorphology::RadiusX() const {
+ return radius_x_;
+}
+
+bool FEMorphology::SetRadiusX(float radius_x) {
+ radius_x = std::max(0.0f, radius_x);
+ if (radius_x_ == radius_x)
+ return false;
+ radius_x_ = radius_x;
+ return true;
+}
+
+float FEMorphology::RadiusY() const {
+ return radius_y_;
+}
+
+bool FEMorphology::SetRadiusY(float radius_y) {
+ radius_y = std::max(0.0f, radius_y);
+ if (radius_y_ == radius_y)
+ return false;
+ radius_y_ = radius_y;
+ return true;
+}
+
+FloatRect FEMorphology::MapEffect(const FloatRect& rect) const {
+ FloatRect result = rect;
+ result.InflateX(GetFilter()->ApplyHorizontalScale(radius_x_));
+ result.InflateY(GetFilter()->ApplyVerticalScale(radius_y_));
+ return result;
+}
+
+sk_sp<PaintFilter> FEMorphology::CreateImageFilter() {
+ sk_sp<PaintFilter> input(
+ PaintFilterBuilder::Build(InputEffect(0), OperatingInterpolationSpace()));
+ int radius_x = clampTo<int>(GetFilter()->ApplyHorizontalScale(radius_x_));
+ int radius_y = clampTo<int>(GetFilter()->ApplyVerticalScale(radius_y_));
+ PaintFilter::CropRect rect = GetCropRect();
+ MorphologyPaintFilter::MorphType morph_type =
+ type_ == FEMORPHOLOGY_OPERATOR_DILATE
+ ? MorphologyPaintFilter::MorphType::kDilate
+ : MorphologyPaintFilter::MorphType::kErode;
+ return sk_make_sp<MorphologyPaintFilter>(morph_type, radius_x, radius_y,
+ std::move(input), &rect);
+}
+
+static TextStream& operator<<(TextStream& ts,
+ const MorphologyOperatorType& type) {
+ switch (type) {
+ case FEMORPHOLOGY_OPERATOR_UNKNOWN:
+ ts << "UNKNOWN";
+ break;
+ case FEMORPHOLOGY_OPERATOR_ERODE:
+ ts << "ERODE";
+ break;
+ case FEMORPHOLOGY_OPERATOR_DILATE:
+ ts << "DILATE";
+ break;
+ }
+ return ts;
+}
+
+TextStream& FEMorphology::ExternalRepresentation(TextStream& ts,
+ int indent) const {
+ WriteIndent(ts, indent);
+ ts << "[feMorphology";
+ FilterEffect::ExternalRepresentation(ts);
+ ts << " operator=\"" << MorphologyOperator() << "\" "
+ << "radius=\"" << RadiusX() << ", " << RadiusY() << "\"]\n";
+ InputEffect(0)->ExternalRepresentation(ts, indent + 1);
+ return ts;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_morphology.h b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_morphology.h
new file mode 100644
index 00000000000..a711006f039
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_morphology.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
+ * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_MORPHOLOGY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_MORPHOLOGY_H_
+
+#include "third_party/blink/renderer/platform/graphics/filters/filter_effect.h"
+
+namespace blink {
+
+enum MorphologyOperatorType {
+ FEMORPHOLOGY_OPERATOR_UNKNOWN = 0,
+ FEMORPHOLOGY_OPERATOR_ERODE = 1,
+ FEMORPHOLOGY_OPERATOR_DILATE = 2
+};
+
+class PLATFORM_EXPORT FEMorphology final : public FilterEffect {
+ public:
+ static FEMorphology* Create(Filter*,
+ MorphologyOperatorType,
+ float radius_x,
+ float radius_y);
+ MorphologyOperatorType MorphologyOperator() const;
+ bool SetMorphologyOperator(MorphologyOperatorType);
+
+ float RadiusX() const;
+ bool SetRadiusX(float);
+
+ float RadiusY() const;
+ bool SetRadiusY(float);
+
+ TextStream& ExternalRepresentation(TextStream&, int indention) const override;
+
+ private:
+ FEMorphology(Filter*, MorphologyOperatorType, float radius_x, float radius_y);
+
+ FloatRect MapEffect(const FloatRect&) const override;
+
+ sk_sp<PaintFilter> CreateImageFilter() override;
+
+ MorphologyOperatorType type_;
+ float radius_x_;
+ float radius_y_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_MORPHOLOGY_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_offset.cc b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_offset.cc
new file mode 100644
index 00000000000..9910549dfd5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_offset.cc
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
+ * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
+ * Copyright (C) Research In Motion Limited 2010. All rights reserved.
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "third_party/blink/renderer/platform/graphics/filters/fe_offset.h"
+
+#include "SkOffsetImageFilter.h"
+#include "third_party/blink/renderer/platform/graphics/filters/filter.h"
+#include "third_party/blink/renderer/platform/graphics/filters/paint_filter_builder.h"
+#include "third_party/blink/renderer/platform/text/text_stream.h"
+
+namespace blink {
+
+FEOffset::FEOffset(Filter* filter, float dx, float dy)
+ : FilterEffect(filter), dx_(dx), dy_(dy) {}
+
+FEOffset* FEOffset::Create(Filter* filter, float dx, float dy) {
+ return new FEOffset(filter, dx, dy);
+}
+
+float FEOffset::Dx() const {
+ return dx_;
+}
+
+void FEOffset::SetDx(float dx) {
+ dx_ = dx;
+}
+
+float FEOffset::Dy() const {
+ return dy_;
+}
+
+void FEOffset::SetDy(float dy) {
+ dy_ = dy;
+}
+
+FloatRect FEOffset::MapEffect(const FloatRect& rect) const {
+ FloatRect result = rect;
+ result.Move(GetFilter()->ApplyHorizontalScale(dx_),
+ GetFilter()->ApplyVerticalScale(dy_));
+ return result;
+}
+
+sk_sp<PaintFilter> FEOffset::CreateImageFilter() {
+ Filter* filter = this->GetFilter();
+ PaintFilter::CropRect crop_rect = GetCropRect();
+ return sk_make_sp<OffsetPaintFilter>(
+ SkFloatToScalar(filter->ApplyHorizontalScale(dx_)),
+ SkFloatToScalar(filter->ApplyVerticalScale(dy_)),
+ PaintFilterBuilder::Build(InputEffect(0), OperatingInterpolationSpace()),
+ &crop_rect);
+}
+
+TextStream& FEOffset::ExternalRepresentation(TextStream& ts, int indent) const {
+ WriteIndent(ts, indent);
+ ts << "[feOffset";
+ FilterEffect::ExternalRepresentation(ts);
+ ts << " dx=\"" << Dx() << "\" dy=\"" << Dy() << "\"]\n";
+ InputEffect(0)->ExternalRepresentation(ts, indent + 1);
+ return ts;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_offset.h b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_offset.h
new file mode 100644
index 00000000000..3a571149f4d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_offset.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
+ * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_OFFSET_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_OFFSET_H_
+
+#include "third_party/blink/renderer/platform/graphics/filters/filter_effect.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT FEOffset final : public FilterEffect {
+ public:
+ static FEOffset* Create(Filter*, float dx, float dy);
+
+ float Dx() const;
+ void SetDx(float);
+
+ float Dy() const;
+ void SetDy(float);
+
+ TextStream& ExternalRepresentation(TextStream&, int indention) const override;
+
+ private:
+ FEOffset(Filter*, float dx, float dy);
+
+ FloatRect MapEffect(const FloatRect&) const override;
+
+ sk_sp<PaintFilter> CreateImageFilter() override;
+
+ float dx_;
+ float dy_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_OFFSET_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_specular_lighting.cc b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_specular_lighting.cc
new file mode 100644
index 00000000000..9eab482ef6a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_specular_lighting.cc
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
+ * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "third_party/blink/renderer/platform/graphics/filters/fe_specular_lighting.h"
+
+#include <algorithm>
+#include "third_party/blink/renderer/platform/graphics/filters/light_source.h"
+#include "third_party/blink/renderer/platform/text/text_stream.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+namespace blink {
+
+FESpecularLighting::FESpecularLighting(Filter* filter,
+ const Color& lighting_color,
+ float surface_scale,
+ float specular_constant,
+ float specular_exponent,
+ scoped_refptr<LightSource> light_source)
+ : FELighting(filter,
+ kSpecularLighting,
+ lighting_color,
+ surface_scale,
+ 0,
+ specular_constant,
+ specular_exponent,
+ std::move(light_source)) {}
+
+FESpecularLighting* FESpecularLighting::Create(
+ Filter* filter,
+ const Color& lighting_color,
+ float surface_scale,
+ float specular_constant,
+ float specular_exponent,
+ scoped_refptr<LightSource> light_source) {
+ return new FESpecularLighting(filter, lighting_color, surface_scale,
+ specular_constant, specular_exponent,
+ std::move(light_source));
+}
+
+FESpecularLighting::~FESpecularLighting() = default;
+
+Color FESpecularLighting::LightingColor() const {
+ return lighting_color_;
+}
+
+bool FESpecularLighting::SetLightingColor(const Color& lighting_color) {
+ if (lighting_color_ == lighting_color)
+ return false;
+ lighting_color_ = lighting_color;
+ return true;
+}
+
+float FESpecularLighting::SurfaceScale() const {
+ return surface_scale_;
+}
+
+bool FESpecularLighting::SetSurfaceScale(float surface_scale) {
+ if (surface_scale_ == surface_scale)
+ return false;
+ surface_scale_ = surface_scale;
+ return true;
+}
+
+float FESpecularLighting::SpecularConstant() const {
+ return specular_constant_;
+}
+
+bool FESpecularLighting::SetSpecularConstant(float specular_constant) {
+ specular_constant = std::max(specular_constant, 0.0f);
+ if (specular_constant_ == specular_constant)
+ return false;
+ specular_constant_ = specular_constant;
+ return true;
+}
+
+float FESpecularLighting::SpecularExponent() const {
+ return specular_exponent_;
+}
+
+bool FESpecularLighting::SetSpecularExponent(float specular_exponent) {
+ specular_exponent = clampTo(specular_exponent, 1.0f, 128.0f);
+ if (specular_exponent_ == specular_exponent)
+ return false;
+ specular_exponent_ = specular_exponent;
+ return true;
+}
+
+const LightSource* FESpecularLighting::GetLightSource() const {
+ return light_source_.get();
+}
+
+void FESpecularLighting::SetLightSource(
+ scoped_refptr<LightSource> light_source) {
+ light_source_ = std::move(light_source);
+}
+
+TextStream& FESpecularLighting::ExternalRepresentation(TextStream& ts,
+ int indent) const {
+ WriteIndent(ts, indent);
+ ts << "[feSpecularLighting";
+ FilterEffect::ExternalRepresentation(ts);
+ ts << " surfaceScale=\"" << surface_scale_ << "\" "
+ << "specualConstant=\"" << specular_constant_ << "\" "
+ << "specularExponent=\"" << specular_exponent_ << "\"]\n";
+ InputEffect(0)->ExternalRepresentation(ts, indent + 1);
+ return ts;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_specular_lighting.h b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_specular_lighting.h
new file mode 100644
index 00000000000..2a0ea3775e9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_specular_lighting.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
+ * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_SPECULAR_LIGHTING_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_SPECULAR_LIGHTING_H_
+
+#include "third_party/blink/renderer/platform/graphics/filters/fe_lighting.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT FESpecularLighting final : public FELighting {
+ public:
+ static FESpecularLighting* Create(Filter*,
+ const Color&,
+ float,
+ float,
+ float,
+ scoped_refptr<LightSource>);
+ ~FESpecularLighting() override;
+
+ Color LightingColor() const;
+ bool SetLightingColor(const Color&);
+
+ float SurfaceScale() const;
+ bool SetSurfaceScale(float);
+
+ float SpecularConstant() const;
+ bool SetSpecularConstant(float);
+
+ float SpecularExponent() const;
+ bool SetSpecularExponent(float);
+
+ const LightSource* GetLightSource() const;
+ void SetLightSource(scoped_refptr<LightSource>);
+
+ TextStream& ExternalRepresentation(TextStream&, int indention) const override;
+
+ private:
+ FESpecularLighting(Filter*,
+ const Color&,
+ float,
+ float,
+ float,
+ scoped_refptr<LightSource>);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_SPECULAR_LIGHTING_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_tile.cc b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_tile.cc
new file mode 100644
index 00000000000..3b4d8bb93c5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_tile.cc
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2008 Alex Mathews <possessedpenguinbob@gmail.com>
+ * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "third_party/blink/renderer/platform/graphics/filters/fe_tile.h"
+
+#include "SkTileImageFilter.h"
+#include "third_party/blink/renderer/platform/graphics/filters/filter.h"
+#include "third_party/blink/renderer/platform/graphics/filters/paint_filter_builder.h"
+#include "third_party/blink/renderer/platform/text/text_stream.h"
+
+namespace blink {
+
+FETile::FETile(Filter* filter) : FilterEffect(filter) {}
+
+FETile* FETile::Create(Filter* filter) {
+ return new FETile(filter);
+}
+
+FloatRect FETile::MapInputs(const FloatRect& rect) const {
+ return AbsoluteBounds();
+}
+
+sk_sp<PaintFilter> FETile::CreateImageFilter() {
+ sk_sp<PaintFilter> input(
+ PaintFilterBuilder::Build(InputEffect(0), OperatingInterpolationSpace()));
+ if (!input)
+ return nullptr;
+
+ FloatRect src_rect;
+ if (InputEffect(0)->GetFilterEffectType() == kFilterEffectTypeSourceInput)
+ src_rect = GetFilter()->FilterRegion();
+ else
+ src_rect = InputEffect(0)->FilterPrimitiveSubregion();
+ FloatRect dst_rect = FilterPrimitiveSubregion();
+ return sk_make_sp<TilePaintFilter>(src_rect, dst_rect, std::move(input));
+}
+
+TextStream& FETile::ExternalRepresentation(TextStream& ts, int indent) const {
+ WriteIndent(ts, indent);
+ ts << "[feTile";
+ FilterEffect::ExternalRepresentation(ts);
+ ts << "]\n";
+ InputEffect(0)->ExternalRepresentation(ts, indent + 1);
+
+ return ts;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_tile.h b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_tile.h
new file mode 100644
index 00000000000..7a40b038a21
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_tile.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
+ * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_TILE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_TILE_H_
+
+#include "third_party/blink/renderer/platform/graphics/filters/filter_effect.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT FETile final : public FilterEffect {
+ public:
+ static FETile* Create(Filter*);
+
+ TextStream& ExternalRepresentation(TextStream&, int indention) const override;
+
+ private:
+ FETile(Filter*);
+
+ FilterEffectType GetFilterEffectType() const override {
+ return kFilterEffectTypeTile;
+ }
+
+ FloatRect MapInputs(const FloatRect&) const final;
+
+ sk_sp<PaintFilter> CreateImageFilter() override;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_TILE_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_turbulence.cc b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_turbulence.cc
new file mode 100644
index 00000000000..94d60f6380e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_turbulence.cc
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
+ * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
+ * Copyright (C) 2010 Renata Hodovan <reni@inf.u-szeged.hu>
+ * Copyright (C) 2011 Gabor Loki <loki@webkit.org>
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "third_party/blink/renderer/platform/graphics/filters/fe_turbulence.h"
+
+#include "SkPaintImageFilter.h"
+#include "SkPerlinNoiseShader.h"
+#include "third_party/blink/renderer/platform/graphics/filters/filter.h"
+#include "third_party/blink/renderer/platform/text/text_stream.h"
+
+namespace blink {
+
+FETurbulence::FETurbulence(Filter* filter,
+ TurbulenceType type,
+ float base_frequency_x,
+ float base_frequency_y,
+ int num_octaves,
+ float seed,
+ bool stitch_tiles)
+ : FilterEffect(filter),
+ type_(type),
+ base_frequency_x_(base_frequency_x),
+ base_frequency_y_(base_frequency_y),
+ num_octaves_(num_octaves),
+ seed_(seed),
+ stitch_tiles_(stitch_tiles) {}
+
+FETurbulence* FETurbulence::Create(Filter* filter,
+ TurbulenceType type,
+ float base_frequency_x,
+ float base_frequency_y,
+ int num_octaves,
+ float seed,
+ bool stitch_tiles) {
+ return new FETurbulence(filter, type, base_frequency_x, base_frequency_y,
+ num_octaves, seed, stitch_tiles);
+}
+
+TurbulenceType FETurbulence::GetType() const {
+ return type_;
+}
+
+bool FETurbulence::SetType(TurbulenceType type) {
+ if (type_ == type)
+ return false;
+ type_ = type;
+ return true;
+}
+
+float FETurbulence::BaseFrequencyY() const {
+ return base_frequency_y_;
+}
+
+bool FETurbulence::SetBaseFrequencyY(float base_frequency_y) {
+ if (base_frequency_y_ == base_frequency_y)
+ return false;
+ base_frequency_y_ = base_frequency_y;
+ return true;
+}
+
+float FETurbulence::BaseFrequencyX() const {
+ return base_frequency_x_;
+}
+
+bool FETurbulence::SetBaseFrequencyX(float base_frequency_x) {
+ if (base_frequency_x_ == base_frequency_x)
+ return false;
+ base_frequency_x_ = base_frequency_x;
+ return true;
+}
+
+float FETurbulence::Seed() const {
+ return seed_;
+}
+
+bool FETurbulence::SetSeed(float seed) {
+ if (seed_ == seed)
+ return false;
+ seed_ = seed;
+ return true;
+}
+
+int FETurbulence::NumOctaves() const {
+ return num_octaves_;
+}
+
+bool FETurbulence::SetNumOctaves(int num_octaves) {
+ if (num_octaves_ == num_octaves)
+ return false;
+ num_octaves_ = num_octaves;
+ return true;
+}
+
+bool FETurbulence::StitchTiles() const {
+ return stitch_tiles_;
+}
+
+bool FETurbulence::SetStitchTiles(bool stitch) {
+ if (stitch_tiles_ == stitch)
+ return false;
+ stitch_tiles_ = stitch;
+ return true;
+}
+
+sk_sp<PaintFilter> FETurbulence::CreateImageFilter() {
+ if (base_frequency_x_ < 0 || base_frequency_y_ < 0)
+ return CreateTransparentBlack();
+
+ PaintFilter::CropRect rect = GetCropRect();
+ TurbulencePaintFilter::TurbulenceType type =
+ GetType() == FETURBULENCE_TYPE_FRACTALNOISE
+ ? TurbulencePaintFilter::TurbulenceType::kFractalNoise
+ : TurbulencePaintFilter::TurbulenceType::kTurbulence;
+ const SkISize size = SkISize::Make(FilterPrimitiveSubregion().Width(),
+ FilterPrimitiveSubregion().Height());
+ // Frequency should be scaled by page zoom, but not by primitiveUnits.
+ // So we apply only the transform scale (as Filter::apply*Scale() do)
+ // and not the target bounding box scale (as SVGFilter::apply*Scale()
+ // would do). Note also that we divide by the scale since this is
+ // a frequency, not a period.
+ float base_frequency_x = base_frequency_x_ / GetFilter()->Scale();
+ float base_frequency_y = base_frequency_y_ / GetFilter()->Scale();
+ return sk_make_sp<TurbulencePaintFilter>(
+ type, SkFloatToScalar(base_frequency_x),
+ SkFloatToScalar(base_frequency_y), NumOctaves(), SkFloatToScalar(Seed()),
+ StitchTiles() ? &size : nullptr, &rect);
+}
+
+static TextStream& operator<<(TextStream& ts, const TurbulenceType& type) {
+ switch (type) {
+ case FETURBULENCE_TYPE_UNKNOWN:
+ ts << "UNKNOWN";
+ break;
+ case FETURBULENCE_TYPE_TURBULENCE:
+ ts << "TURBULENCE";
+ break;
+ case FETURBULENCE_TYPE_FRACTALNOISE:
+ ts << "NOISE";
+ break;
+ }
+ return ts;
+}
+
+TextStream& FETurbulence::ExternalRepresentation(TextStream& ts,
+ int indent) const {
+ WriteIndent(ts, indent);
+ ts << "[feTurbulence";
+ FilterEffect::ExternalRepresentation(ts);
+ ts << " type=\"" << GetType() << "\" "
+ << "baseFrequency=\"" << BaseFrequencyX() << ", " << BaseFrequencyY()
+ << "\" "
+ << "seed=\"" << Seed() << "\" "
+ << "numOctaves=\"" << NumOctaves() << "\" "
+ << "stitchTiles=\"" << StitchTiles() << "\"]\n";
+ return ts;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_turbulence.h b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_turbulence.h
new file mode 100644
index 00000000000..8edb1a7b9f7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_turbulence.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
+ * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
+ * Copyright (C) 2010 Renata Hodovan <reni@inf.u-szeged.hu>
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_TURBULENCE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_TURBULENCE_H_
+
+#include "third_party/blink/renderer/platform/graphics/filters/filter_effect.h"
+
+namespace blink {
+
+enum TurbulenceType {
+ FETURBULENCE_TYPE_UNKNOWN = 0,
+ FETURBULENCE_TYPE_FRACTALNOISE = 1,
+ FETURBULENCE_TYPE_TURBULENCE = 2
+};
+
+class PLATFORM_EXPORT FETurbulence final : public FilterEffect {
+ public:
+ static FETurbulence*
+ Create(Filter*, TurbulenceType, float, float, int, float, bool);
+
+ TurbulenceType GetType() const;
+ bool SetType(TurbulenceType);
+
+ float BaseFrequencyY() const;
+ bool SetBaseFrequencyY(float);
+
+ float BaseFrequencyX() const;
+ bool SetBaseFrequencyX(float);
+
+ float Seed() const;
+ bool SetSeed(float);
+
+ int NumOctaves() const;
+ bool SetNumOctaves(int);
+
+ bool StitchTiles() const;
+ bool SetStitchTiles(bool);
+
+ TextStream& ExternalRepresentation(TextStream&, int indention) const override;
+
+ private:
+ FETurbulence(Filter*, TurbulenceType, float, float, int, float, bool);
+
+ sk_sp<PaintFilter> CreateImageFilter() override;
+
+ TurbulenceType type_;
+ float base_frequency_x_;
+ float base_frequency_y_;
+ int num_octaves_;
+ float seed_;
+ bool stitch_tiles_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FE_TURBULENCE_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/filter.cc b/chromium/third_party/blink/renderer/platform/graphics/filters/filter.cc
new file mode 100644
index 00000000000..3cc292d28f8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/filter.cc
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved.
+ * Copyright (C) 2013 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:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
+ * EXPRESS OR IMPLIED 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 HOLDER BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (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 "third_party/blink/renderer/platform/graphics/filters/filter.h"
+
+#include "third_party/blink/renderer/platform/graphics/filters/filter_effect.h"
+#include "third_party/blink/renderer/platform/graphics/filters/source_graphic.h"
+
+namespace blink {
+
+Filter::Filter(const FloatRect& reference_box,
+ const FloatRect& filter_region,
+ float scale,
+ UnitScaling unit_scaling)
+ : reference_box_(reference_box),
+ filter_region_(filter_region),
+ scale_(scale),
+ unit_scaling_(unit_scaling),
+ source_graphic_(SourceGraphic::Create(this)) {}
+
+Filter* Filter::Create(const FloatRect& reference_box,
+ const FloatRect& filter_region,
+ float scale,
+ UnitScaling unit_scaling) {
+ return new Filter(reference_box, filter_region, scale, unit_scaling);
+}
+
+Filter* Filter::Create(float scale) {
+ return new Filter(FloatRect(), FloatRect(), scale, kUserSpace);
+}
+
+void Filter::Trace(blink::Visitor* visitor) {
+ visitor->Trace(source_graphic_);
+ visitor->Trace(last_effect_);
+}
+
+FloatRect Filter::MapLocalRectToAbsoluteRect(const FloatRect& rect) const {
+ FloatRect result(rect);
+ result.Scale(scale_);
+ return result;
+}
+
+FloatRect Filter::MapAbsoluteRectToLocalRect(const FloatRect& rect) const {
+ FloatRect result(rect);
+ result.Scale(1.0f / scale_);
+ return result;
+}
+
+float Filter::ApplyHorizontalScale(float value) const {
+ if (unit_scaling_ == kBoundingBox)
+ value *= ReferenceBox().Width();
+ return scale_ * value;
+}
+
+float Filter::ApplyVerticalScale(float value) const {
+ if (unit_scaling_ == kBoundingBox)
+ value *= ReferenceBox().Height();
+ return scale_ * value;
+}
+
+FloatPoint3D Filter::Resolve3dPoint(const FloatPoint3D& point) const {
+ if (unit_scaling_ != kBoundingBox)
+ return point;
+ return FloatPoint3D(
+ point.X() * ReferenceBox().Width() + ReferenceBox().X(),
+ point.Y() * ReferenceBox().Height() + ReferenceBox().Y(),
+ point.Z() * sqrtf(ReferenceBox().Size().DiagonalLengthSquared() / 2));
+}
+
+void Filter::SetLastEffect(FilterEffect* effect) {
+ last_effect_ = effect;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/filter.h b/chromium/third_party/blink/renderer/platform/graphics/filters/filter.h
new file mode 100644
index 00000000000..cb5ae4db283
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/filter.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FILTER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FILTER_H_
+
+#include "third_party/blink/renderer/platform/geometry/float_point_3d.h"
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class SourceGraphic;
+class FilterEffect;
+
+class PLATFORM_EXPORT Filter final : public GarbageCollected<Filter> {
+ WTF_MAKE_NONCOPYABLE(Filter);
+
+ public:
+ enum UnitScaling { kUserSpace, kBoundingBox };
+
+ static Filter* Create(const FloatRect& reference_box,
+ const FloatRect& filter_region,
+ float scale,
+ UnitScaling);
+ static Filter* Create(float scale);
+
+ void Trace(blink::Visitor*);
+
+ float Scale() const { return scale_; }
+ FloatRect MapLocalRectToAbsoluteRect(const FloatRect&) const;
+ FloatRect MapAbsoluteRectToLocalRect(const FloatRect&) const;
+
+ float ApplyHorizontalScale(float value) const;
+ float ApplyVerticalScale(float value) const;
+
+ FloatPoint3D Resolve3dPoint(const FloatPoint3D&) const;
+
+ FloatRect AbsoluteFilterRegion() const {
+ return MapLocalRectToAbsoluteRect(filter_region_);
+ }
+
+ const FloatRect& FilterRegion() const { return filter_region_; }
+ const FloatRect& ReferenceBox() const { return reference_box_; }
+
+ void SetLastEffect(FilterEffect*);
+ FilterEffect* LastEffect() const { return last_effect_.Get(); }
+
+ SourceGraphic* GetSourceGraphic() const { return source_graphic_.Get(); }
+
+ private:
+ Filter(const FloatRect& reference_box,
+ const FloatRect& filter_region,
+ float scale,
+ UnitScaling);
+
+ FloatRect reference_box_;
+ FloatRect filter_region_;
+ float scale_;
+ UnitScaling unit_scaling_;
+
+ Member<SourceGraphic> source_graphic_;
+ Member<FilterEffect> last_effect_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FILTER_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/filter_effect.cc b/chromium/third_party/blink/renderer/platform/graphics/filters/filter_effect.cc
new file mode 100644
index 00000000000..0af548ed950
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/filter_effect.cc
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2008 Alex Mathews <possessedpenguinbob@gmail.com>
+ * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
+ * Copyright (C) Research In Motion Limited 2010. All rights reserved.
+ * Copyright (C) 2012 University of Szeged
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "third_party/blink/renderer/platform/graphics/filters/filter_effect.h"
+
+#include "third_party/blink/renderer/platform/graphics/filters/filter.h"
+#include "third_party/skia/include/core/SkColorFilter.h"
+#include "third_party/skia/include/effects/SkColorFilterImageFilter.h"
+
+namespace blink {
+
+FilterEffect::FilterEffect(Filter* filter)
+ : filter_(filter),
+ clips_to_bounds_(true),
+ origin_tainted_(false),
+ operating_interpolation_space_(kInterpolationSpaceLinear) {
+ DCHECK(filter_);
+}
+
+FilterEffect::~FilterEffect() = default;
+
+void FilterEffect::Trace(blink::Visitor* visitor) {
+ visitor->Trace(input_effects_);
+ visitor->Trace(filter_);
+}
+
+FloatRect FilterEffect::AbsoluteBounds() const {
+ FloatRect computed_bounds = GetFilter()->FilterRegion();
+ if (!FilterPrimitiveSubregion().IsEmpty())
+ computed_bounds.Intersect(FilterPrimitiveSubregion());
+ return GetFilter()->MapLocalRectToAbsoluteRect(computed_bounds);
+}
+
+FloatRect FilterEffect::MapInputs(const FloatRect& rect) const {
+ if (!input_effects_.size()) {
+ if (ClipsToBounds())
+ return AbsoluteBounds();
+ return rect;
+ }
+ FloatRect input_union;
+ for (const auto& effect : input_effects_)
+ input_union.Unite(effect->MapRect(rect));
+ return input_union;
+}
+
+FloatRect FilterEffect::MapEffect(const FloatRect& rect) const {
+ return rect;
+}
+
+FloatRect FilterEffect::ApplyBounds(const FloatRect& rect) const {
+ // Filters in SVG clip to primitive subregion, while CSS doesn't.
+ if (!ClipsToBounds())
+ return rect;
+ FloatRect bounds = AbsoluteBounds();
+ if (AffectsTransparentPixels())
+ return bounds;
+ return Intersection(rect, bounds);
+}
+
+FloatRect FilterEffect::MapRect(const FloatRect& rect) const {
+ FloatRect result = MapInputs(rect);
+ result = MapEffect(result);
+ return ApplyBounds(result);
+}
+
+FilterEffect* FilterEffect::InputEffect(unsigned number) const {
+ SECURITY_DCHECK(number < input_effects_.size());
+ return input_effects_.at(number).Get();
+}
+
+void FilterEffect::DisposeImageFilters() {
+ for (int i = 0; i < 4; i++)
+ image_filters_[i] = nullptr;
+}
+
+void FilterEffect::DisposeImageFiltersRecursive() {
+ if (!HasImageFilter())
+ return;
+ DisposeImageFilters();
+ for (auto& effect : input_effects_)
+ effect->DisposeImageFiltersRecursive();
+}
+
+Color FilterEffect::AdaptColorToOperatingInterpolationSpace(
+ const Color& device_color) {
+ // |deviceColor| is assumed to be DeviceRGB.
+ return InterpolationSpaceUtilities::ConvertColor(
+ device_color, OperatingInterpolationSpace());
+}
+
+TextStream& FilterEffect::ExternalRepresentation(TextStream& ts, int) const {
+ // FIXME: We should dump the subRegions of the filter primitives here later.
+ // This isn't possible at the moment, because we need more detailed
+ // information from the target object.
+ return ts;
+}
+
+sk_sp<PaintFilter> FilterEffect::CreateImageFilter() {
+ return nullptr;
+}
+
+sk_sp<PaintFilter> FilterEffect::CreateImageFilterWithoutValidation() {
+ return CreateImageFilter();
+}
+
+bool FilterEffect::InputsTaintOrigin() const {
+ for (const Member<FilterEffect>& effect : input_effects_) {
+ if (effect->OriginTainted())
+ return true;
+ }
+ return false;
+}
+
+sk_sp<PaintFilter> FilterEffect::CreateTransparentBlack() const {
+ PaintFilter::CropRect rect = GetCropRect();
+ sk_sp<SkColorFilter> color_filter =
+ SkColorFilter::MakeModeFilter(0, SkBlendMode::kClear);
+ return sk_make_sp<ColorFilterPaintFilter>(std::move(color_filter), nullptr,
+ &rect);
+}
+
+PaintFilter::CropRect FilterEffect::GetCropRect() const {
+ if (!FilterPrimitiveSubregion().IsEmpty()) {
+ FloatRect rect =
+ GetFilter()->MapLocalRectToAbsoluteRect(FilterPrimitiveSubregion());
+ return PaintFilter::CropRect(rect);
+ } else {
+ return PaintFilter::CropRect(SkRect::MakeEmpty(), 0);
+ }
+}
+
+static int GetImageFilterIndex(InterpolationSpace interpolation_space,
+ bool requires_pm_color_validation) {
+ // Map the (colorspace, bool) tuple to an integer index as follows:
+ // 0 == linear colorspace, no PM validation
+ // 1 == device colorspace, no PM validation
+ // 2 == linear colorspace, PM validation
+ // 3 == device colorspace, PM validation
+ return (interpolation_space == kInterpolationSpaceLinear ? 0x1 : 0x0) |
+ (requires_pm_color_validation ? 0x2 : 0x0);
+}
+
+PaintFilter* FilterEffect::GetImageFilter(
+ InterpolationSpace interpolation_space,
+ bool requires_pm_color_validation) const {
+ int index =
+ GetImageFilterIndex(interpolation_space, requires_pm_color_validation);
+ return image_filters_[index].get();
+}
+
+void FilterEffect::SetImageFilter(InterpolationSpace interpolation_space,
+ bool requires_pm_color_validation,
+ sk_sp<PaintFilter> image_filter) {
+ int index =
+ GetImageFilterIndex(interpolation_space, requires_pm_color_validation);
+ image_filters_[index] = std::move(image_filter);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/filter_effect.h b/chromium/third_party/blink/renderer/platform/graphics/filters/filter_effect.h
new file mode 100644
index 00000000000..7bb18ff967a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/filter_effect.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2008 Alex Mathews <possessedpenguinbob@gmail.com>
+ * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
+ * Copyright (C) Research In Motion Limited 2010. All rights reserved.
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FILTER_EFFECT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FILTER_EFFECT_H_
+
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/graphics/color.h"
+#include "third_party/blink/renderer/platform/graphics/interpolation_space.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_filter.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class Filter;
+class FilterEffect;
+class TextStream;
+
+typedef HeapVector<Member<FilterEffect>> FilterEffectVector;
+
+enum FilterEffectType {
+ kFilterEffectTypeUnknown,
+ kFilterEffectTypeImage,
+ kFilterEffectTypeTile,
+ kFilterEffectTypeSourceInput
+};
+
+class PLATFORM_EXPORT FilterEffect
+ : public GarbageCollectedFinalized<FilterEffect> {
+ WTF_MAKE_NONCOPYABLE(FilterEffect);
+
+ public:
+ virtual ~FilterEffect();
+ virtual void Trace(blink::Visitor*);
+
+ void DisposeImageFilters();
+ void DisposeImageFiltersRecursive();
+
+ FilterEffectVector& InputEffects() { return input_effects_; }
+ FilterEffect* InputEffect(unsigned) const;
+ unsigned NumberOfEffectInputs() const { return input_effects_.size(); }
+
+ inline bool HasImageFilter() const {
+ return image_filters_[0] || image_filters_[1] || image_filters_[2] ||
+ image_filters_[3];
+ }
+
+ // Clipped primitive subregion in the coordinate space of the target.
+ FloatRect AbsoluteBounds() const;
+
+ // Mapping a rect forwards to determine which which destination pixels a
+ // given source rect would affect.
+ FloatRect MapRect(const FloatRect&) const;
+
+ virtual sk_sp<PaintFilter> CreateImageFilter();
+ virtual sk_sp<PaintFilter> CreateImageFilterWithoutValidation();
+
+ virtual FilterEffectType GetFilterEffectType() const {
+ return kFilterEffectTypeUnknown;
+ }
+
+ virtual TextStream& ExternalRepresentation(TextStream&,
+ int indention = 0) const;
+
+ FloatRect FilterPrimitiveSubregion() const {
+ return filter_primitive_subregion_;
+ }
+ void SetFilterPrimitiveSubregion(
+ const FloatRect& filter_primitive_subregion) {
+ filter_primitive_subregion_ = filter_primitive_subregion;
+ }
+
+ Filter* GetFilter() { return filter_; }
+ const Filter* GetFilter() const { return filter_; }
+
+ bool ClipsToBounds() const { return clips_to_bounds_; }
+ void SetClipsToBounds(bool value) { clips_to_bounds_ = value; }
+
+ InterpolationSpace OperatingInterpolationSpace() const {
+ return operating_interpolation_space_;
+ }
+ virtual void SetOperatingInterpolationSpace(
+ InterpolationSpace interpolation_space) {
+ operating_interpolation_space_ = interpolation_space;
+ }
+
+ virtual bool AffectsTransparentPixels() const { return false; }
+
+ // Return false if the filter will only operate correctly on valid RGBA
+ // values, with alpha in [0,255] and each color component in [0, alpha].
+ virtual bool MayProduceInvalidPreMultipliedPixels() { return false; }
+
+ PaintFilter* GetImageFilter(InterpolationSpace,
+ bool requires_pm_color_validation) const;
+ void SetImageFilter(InterpolationSpace,
+ bool requires_pm_color_validation,
+ sk_sp<PaintFilter>);
+
+ bool OriginTainted() const { return origin_tainted_; }
+ void SetOriginTainted() { origin_tainted_ = true; }
+
+ bool InputsTaintOrigin() const;
+
+ protected:
+ FilterEffect(Filter*);
+
+ // Determine the contribution from the filter effect's inputs.
+ virtual FloatRect MapInputs(const FloatRect&) const;
+
+ // Apply the contribution from the filter effect's itself. (Like
+ // expanding with the blur radius etc.)
+ virtual FloatRect MapEffect(const FloatRect&) const;
+
+ // Apply the clip bounds and factor in the effect of
+ // affectsTransparentPixels().
+ FloatRect ApplyBounds(const FloatRect&) const;
+
+ sk_sp<PaintFilter> CreateTransparentBlack() const;
+
+ Color AdaptColorToOperatingInterpolationSpace(const Color& device_color);
+
+ PaintFilter::CropRect GetCropRect() const;
+
+ private:
+ FilterEffectVector input_effects_;
+
+ Member<Filter> filter_;
+
+ // The following member variables are SVG specific and will move to
+ // LayoutSVGResourceFilterPrimitive.
+ // See bug https://bugs.webkit.org/show_bug.cgi?id=45614.
+
+ // The subregion of a filter primitive according to the SVG Filter
+ // specification in local coordinates.
+ FloatRect filter_primitive_subregion_;
+
+ // Whether the effect should clip to its primitive region, or expand to use
+ // the combined region of its inputs.
+ bool clips_to_bounds_;
+
+ bool origin_tainted_;
+
+ InterpolationSpace operating_interpolation_space_;
+
+ sk_sp<PaintFilter> image_filters_[4];
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_FILTER_EFFECT_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/image_filter_builder_test.cc b/chromium/third_party/blink/renderer/platform/graphics/filters/image_filter_builder_test.cc
new file mode 100644
index 00000000000..a226148ad9e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/image_filter_builder_test.cc
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2013 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/graphics/filters/fe_blend.h"
+#include "third_party/blink/renderer/platform/graphics/filters/fe_gaussian_blur.h"
+#include "third_party/blink/renderer/platform/graphics/filters/fe_merge.h"
+#include "third_party/blink/renderer/platform/graphics/filters/filter.h"
+#include "third_party/blink/renderer/platform/graphics/filters/paint_filter_builder.h"
+#include "third_party/blink/renderer/platform/graphics/filters/source_graphic.h"
+
+using testing::Test;
+
+namespace blink {
+
+class ImageFilterBuilderTest : public Test {
+ protected:
+ void InterpolationSpaceTest() {
+ // Build filter tree
+ Filter* reference_filter = Filter::Create(1.0f);
+
+ // Add a dummy source graphic input
+ FilterEffect* source_effect = reference_filter->GetSourceGraphic();
+ source_effect->SetOperatingInterpolationSpace(kInterpolationSpaceSRGB);
+
+ // Add a blur effect (with input : source)
+ FilterEffect* blur_effect =
+ FEGaussianBlur::Create(reference_filter, 3.0f, 3.0f);
+ blur_effect->SetOperatingInterpolationSpace(kInterpolationSpaceLinear);
+ blur_effect->InputEffects().push_back(source_effect);
+
+ // Add a blend effect (with inputs : blur, source)
+ FilterEffect* blend_effect =
+ FEBlend::Create(reference_filter, WebBlendMode::kNormal);
+ blend_effect->SetOperatingInterpolationSpace(kInterpolationSpaceSRGB);
+ FilterEffectVector& blend_inputs = blend_effect->InputEffects();
+ blend_inputs.ReserveCapacity(2);
+ blend_inputs.push_back(source_effect);
+ blend_inputs.push_back(blur_effect);
+
+ // Add a merge effect (with inputs : blur, blend)
+ FilterEffect* merge_effect = FEMerge::Create(reference_filter);
+ merge_effect->SetOperatingInterpolationSpace(kInterpolationSpaceLinear);
+ FilterEffectVector& merge_inputs = merge_effect->InputEffects();
+ merge_inputs.ReserveCapacity(2);
+ merge_inputs.push_back(blur_effect);
+ merge_inputs.push_back(blend_effect);
+ reference_filter->SetLastEffect(merge_effect);
+
+ // Get PaintFilter resulting tree
+ sk_sp<PaintFilter> filter = PaintFilterBuilder::Build(
+ reference_filter->LastEffect(), kInterpolationSpaceSRGB);
+
+ // Let's check that the resulting tree looks like this :
+ // InterpolationSpace (Linear->Device) : CS (L->D)
+ // |
+ // Merge (L)
+ // | |
+ // | CS (D->L)
+ // | |
+ // | Blend (D)
+ // | / |
+ // | CS (L->D) |
+ // | / |
+ // Blur (L) |
+ // \ |
+ // CS (D->L) |
+ // \ |
+ // Source Graphic (D)
+
+ // Should be CS : InterpolationSpace (Linear->Device)
+ EXPECT_EQ(filter->type(), PaintFilter::Type::kColorFilter);
+
+ // Should be Merge.
+ const auto* merge_effect_pf =
+ static_cast<const ColorFilterPaintFilter*>(filter.get())->input().get();
+ ASSERT_EQ(merge_effect_pf->type(), PaintFilter::Type::kMerge);
+ const auto* merge = static_cast<const MergePaintFilter*>(merge_effect_pf);
+ EXPECT_EQ(merge->input_count(), 2u);
+
+ // Should be CS (D->L)
+ const auto* color_filter_pf = merge->input_at(1u);
+ ASSERT_EQ(color_filter_pf->type(), PaintFilter::Type::kColorFilter);
+
+ // Should be Blend
+ const auto* xfermode_filter_pf =
+ static_cast<const ColorFilterPaintFilter*>(color_filter_pf)
+ ->input()
+ .get();
+ ASSERT_TRUE(xfermode_filter_pf);
+ EXPECT_EQ(xfermode_filter_pf->type(), PaintFilter::Type::kXfermode);
+ const auto* xfermode =
+ static_cast<const XfermodePaintFilter*>(xfermode_filter_pf);
+ ASSERT_TRUE(xfermode->background());
+
+ // Should be CS (L->D)
+ color_filter_pf = xfermode->background().get();
+ ASSERT_EQ(color_filter_pf->type(), PaintFilter::Type::kColorFilter);
+
+ // Should be Blur
+ const auto* blur_filter_pf =
+ static_cast<const ColorFilterPaintFilter*>(color_filter_pf)
+ ->input()
+ .get();
+ ASSERT_TRUE(blur_filter_pf);
+ EXPECT_EQ(blur_filter_pf->type(), PaintFilter::Type::kBlur);
+
+ // Should be CS (D->L)
+ color_filter_pf =
+ static_cast<const BlurPaintFilter*>(blur_filter_pf)->input().get();
+ ASSERT_TRUE(color_filter_pf);
+ EXPECT_EQ(color_filter_pf->type(), PaintFilter::Type::kColorFilter);
+ }
+};
+
+TEST_F(ImageFilterBuilderTest, testInterpolationSpace) {
+ InterpolationSpaceTest();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/light_source.cc b/chromium/third_party/blink/renderer/platform/graphics/filters/light_source.cc
new file mode 100644
index 00000000000..3bdecf6361e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/light_source.cc
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2013 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:
+ *
+ * 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. ("Google") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/graphics/filters/light_source.h"
+
+namespace blink {
+
+LightSource::~LightSource() = default;
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/light_source.h b/chromium/third_party/blink/renderer/platform/graphics/filters/light_source.h
new file mode 100644
index 00000000000..37ab76da294
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/light_source.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2008 Alex Mathews <possessedpenguinbob@gmail.com>
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
+ * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2010 Zoltan Herczeg <zherczeg@webkit.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_LIGHT_SOURCE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_LIGHT_SOURCE_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/geometry/float_point_3d.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+
+namespace blink {
+
+enum LightType { LS_DISTANT, LS_POINT, LS_SPOT };
+
+class TextStream;
+
+class PLATFORM_EXPORT LightSource : public RefCounted<LightSource> {
+ WTF_MAKE_NONCOPYABLE(LightSource);
+
+ public:
+ LightSource(LightType type) : type_(type) {}
+
+ virtual ~LightSource();
+
+ LightType GetType() const { return type_; }
+ virtual TextStream& ExternalRepresentation(TextStream&) const = 0;
+
+ virtual bool SetAzimuth(float) { return false; }
+ virtual bool SetElevation(float) { return false; }
+ virtual bool SetPosition(const FloatPoint3D&) { return false; }
+ virtual bool SetPointsAt(const FloatPoint3D&) { return false; }
+ virtual bool SetSpecularExponent(float) { return false; }
+ virtual bool SetLimitingConeAngle(float) { return false; }
+
+ private:
+ LightType type_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_LIGHT_SOURCE_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/paint_filter_builder.cc b/chromium/third_party/blink/renderer/platform/graphics/filters/paint_filter_builder.cc
new file mode 100644
index 00000000000..bf0cb7aa1d2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/paint_filter_builder.cc
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/graphics/filters/paint_filter_builder.h"
+
+#include "SkBlurImageFilter.h"
+#include "SkColorFilterImageFilter.h"
+#include "SkColorMatrixFilter.h"
+#include "SkTableColorFilter.h"
+#include "third_party/blink/renderer/platform/graphics/box_reflection.h"
+#include "third_party/blink/renderer/platform/graphics/filters/filter_effect.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_record.h"
+#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
+#include "third_party/skia/include/effects/SkImageSource.h"
+#include "third_party/skia/include/effects/SkOffsetImageFilter.h"
+#include "third_party/skia/include/effects/SkPictureImageFilter.h"
+#include "third_party/skia/include/effects/SkXfermodeImageFilter.h"
+
+namespace blink {
+namespace PaintFilterBuilder {
+
+void PopulateSourceGraphicImageFilters(
+ FilterEffect* source_graphic,
+ sk_sp<PaintFilter> input,
+ InterpolationSpace input_interpolation_space) {
+ // Prepopulate SourceGraphic with two image filters: one with a null image
+ // filter, and the other with a colorspace conversion filter.
+ // We don't know what color space the interior nodes will request, so we
+ // have to initialize SourceGraphic with both options.
+ // Since we know SourceGraphic is always PM-valid, we also use these for
+ // the PM-validated options.
+ sk_sp<PaintFilter> device_filter = TransformInterpolationSpace(
+ input, input_interpolation_space, kInterpolationSpaceSRGB);
+ sk_sp<PaintFilter> linear_filter = TransformInterpolationSpace(
+ input, input_interpolation_space, kInterpolationSpaceLinear);
+ source_graphic->SetImageFilter(kInterpolationSpaceSRGB, false, device_filter);
+ source_graphic->SetImageFilter(kInterpolationSpaceLinear, false,
+ linear_filter);
+ source_graphic->SetImageFilter(kInterpolationSpaceSRGB, true, device_filter);
+ source_graphic->SetImageFilter(kInterpolationSpaceLinear, true,
+ linear_filter);
+}
+
+sk_sp<PaintFilter> Build(
+ FilterEffect* effect,
+ InterpolationSpace interpolation_space,
+ bool destination_requires_valid_pre_multiplied_pixels) {
+ if (!effect)
+ return nullptr;
+
+ bool requires_pm_color_validation =
+ effect->MayProduceInvalidPreMultipliedPixels() &&
+ destination_requires_valid_pre_multiplied_pixels;
+
+ if (PaintFilter* filter = effect->GetImageFilter(
+ interpolation_space, requires_pm_color_validation))
+ return sk_ref_sp(filter);
+
+ // Note that we may still need the color transform even if the filter is null
+ sk_sp<PaintFilter> orig_filter =
+ requires_pm_color_validation
+ ? effect->CreateImageFilter()
+ : effect->CreateImageFilterWithoutValidation();
+
+ sk_sp<PaintFilter> filter = TransformInterpolationSpace(
+ orig_filter, effect->OperatingInterpolationSpace(), interpolation_space);
+ effect->SetImageFilter(interpolation_space, requires_pm_color_validation,
+ filter);
+ if (filter.get() != orig_filter.get()) {
+ effect->SetImageFilter(effect->OperatingInterpolationSpace(),
+ requires_pm_color_validation,
+ std::move(orig_filter));
+ }
+ return filter;
+}
+
+sk_sp<PaintFilter> TransformInterpolationSpace(
+ sk_sp<PaintFilter> input,
+ InterpolationSpace src_interpolation_space,
+ InterpolationSpace dst_interpolation_space) {
+ sk_sp<SkColorFilter> color_filter =
+ InterpolationSpaceUtilities::CreateInterpolationSpaceFilter(
+ src_interpolation_space, dst_interpolation_space);
+ if (!color_filter)
+ return input;
+
+ return sk_make_sp<ColorFilterPaintFilter>(std::move(color_filter),
+ std::move(input));
+}
+
+void BuildSourceGraphic(FilterEffect* source_graphic,
+ sk_sp<PaintRecord> record,
+ const FloatRect& record_bounds) {
+ DCHECK(record);
+ sk_sp<PaintFilter> filter =
+ sk_make_sp<RecordPaintFilter>(record, record_bounds);
+ PopulateSourceGraphicImageFilters(
+ source_graphic, std::move(filter),
+ source_graphic->OperatingInterpolationSpace());
+}
+
+static const float kMaxMaskBufferSize =
+ 50.f * 1024.f * 1024.f / 4.f; // 50MB / 4 bytes per pixel
+
+sk_sp<PaintFilter> BuildBoxReflectFilter(const BoxReflection& reflection,
+ sk_sp<PaintFilter> input) {
+ sk_sp<PaintFilter> masked_input;
+ if (sk_sp<PaintRecord> mask_record = reflection.Mask()) {
+ // Since PaintRecords can't be serialized to the browser process, first
+ // raster the mask to a bitmap, then encode it in an SkImageSource, which
+ // can be serialized.
+ SkBitmap bitmap;
+ const SkRect mask_record_bounds = reflection.MaskBounds();
+ SkRect mask_bounds_rounded;
+ mask_record_bounds.roundOut(&mask_bounds_rounded);
+ SkScalar mask_buffer_size =
+ mask_bounds_rounded.width() * mask_bounds_rounded.height();
+ if (mask_buffer_size < kMaxMaskBufferSize && mask_buffer_size > 0.0f) {
+ bitmap.allocPixels(SkImageInfo::MakeN32Premul(
+ mask_bounds_rounded.width(), mask_bounds_rounded.height()));
+ SkiaPaintCanvas canvas(bitmap);
+ canvas.clear(SK_ColorTRANSPARENT);
+ canvas.translate(-mask_record_bounds.x(), -mask_record_bounds.y());
+ canvas.drawPicture(mask_record);
+ PaintImage image = PaintImageBuilder::WithDefault()
+ .set_id(PaintImage::GetNextId())
+ .set_image(SkImage::MakeFromBitmap(bitmap),
+ PaintImage::GetNextContentId())
+ .TakePaintImage();
+
+ // SkXfermodeImageFilter can choose an excessively large size if the
+ // mask is smaller than the filtered contents (due to overflow).
+ // http://skbug.com/5210
+ PaintFilter::CropRect crop_rect(mask_record_bounds);
+ SkRect image_rect = SkRect::MakeWH(image.width(), image.height());
+ masked_input = sk_make_sp<XfermodePaintFilter>(
+ SkBlendMode::kSrcIn,
+ sk_make_sp<OffsetPaintFilter>(
+ mask_record_bounds.x(), mask_record_bounds.y(),
+ sk_make_sp<ImagePaintFilter>(std::move(image), image_rect,
+ image_rect, kHigh_SkFilterQuality)),
+ input, &crop_rect);
+ } else {
+ // If the buffer is excessively big, give up and make an
+ // SkPictureImageFilter anyway, even if it might not render.
+ PaintFilter::CropRect crop_rect(mask_record_bounds);
+ masked_input = sk_make_sp<XfermodePaintFilter>(
+ SkBlendMode::kSrcOver,
+ sk_make_sp<RecordPaintFilter>(std::move(mask_record),
+ mask_record_bounds),
+ input, &crop_rect);
+ }
+ } else {
+ masked_input = input;
+ }
+ sk_sp<PaintFilter> flip_image_filter = sk_make_sp<MatrixPaintFilter>(
+ reflection.ReflectionMatrix(), kLow_SkFilterQuality,
+ std::move(masked_input));
+ return sk_make_sp<XfermodePaintFilter>(SkBlendMode::kSrcOver,
+ std::move(flip_image_filter),
+ std::move(input), nullptr);
+}
+
+} // namespace PaintFilterBuilder
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/paint_filter_builder.h b/chromium/third_party/blink/renderer/platform/graphics/filters/paint_filter_builder.h
new file mode 100644
index 00000000000..bff2da32762
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/paint_filter_builder.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_PAINT_FILTER_BUILDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_PAINT_FILTER_BUILDER_H_
+
+#include "third_party/blink/renderer/platform/graphics/interpolation_space.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_filter.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_record.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+
+class BoxReflection;
+class FilterEffect;
+class FloatRect;
+
+namespace PaintFilterBuilder {
+
+PLATFORM_EXPORT sk_sp<PaintFilter> Build(
+ FilterEffect*,
+ InterpolationSpace,
+ bool requires_pm_color_validation = true);
+
+PLATFORM_EXPORT sk_sp<PaintFilter> TransformInterpolationSpace(
+ sk_sp<PaintFilter> input,
+ InterpolationSpace src_interpolation_space,
+ InterpolationSpace dst_interpolation_space);
+
+PLATFORM_EXPORT void PopulateSourceGraphicImageFilters(
+ FilterEffect* source_graphic,
+ sk_sp<PaintFilter> input,
+ InterpolationSpace input_interpolation_space);
+PLATFORM_EXPORT void BuildSourceGraphic(FilterEffect*,
+ sk_sp<PaintRecord>,
+ const FloatRect& record_bounds);
+
+PLATFORM_EXPORT sk_sp<PaintFilter> BuildBoxReflectFilter(
+ const BoxReflection&,
+ sk_sp<PaintFilter> input);
+
+} // namespace PaintFilterBuilder
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/paint_filter_effect.cc b/chromium/third_party/blink/renderer/platform/graphics/filters/paint_filter_effect.cc
new file mode 100644
index 00000000000..588d27e4f66
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/paint_filter_effect.cc
@@ -0,0 +1,36 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/filters/paint_filter_effect.h"
+
+#include "third_party/blink/renderer/platform/graphics/filters/filter.h"
+#include "third_party/blink/renderer/platform/text/text_stream.h"
+#include "third_party/skia/include/effects/SkPaintImageFilter.h"
+
+namespace blink {
+
+PaintFilterEffect::PaintFilterEffect(Filter* filter, const PaintFlags& flags)
+ : FilterEffect(filter), flags_(flags) {
+ SetOperatingInterpolationSpace(kInterpolationSpaceSRGB);
+}
+
+PaintFilterEffect::~PaintFilterEffect() = default;
+
+PaintFilterEffect* PaintFilterEffect::Create(Filter* filter,
+ const PaintFlags& flags) {
+ return new PaintFilterEffect(filter, flags);
+}
+
+sk_sp<PaintFilter> PaintFilterEffect::CreateImageFilter() {
+ return sk_make_sp<PaintFlagsPaintFilter>(flags_);
+}
+
+TextStream& PaintFilterEffect::ExternalRepresentation(TextStream& ts,
+ int indent) const {
+ WriteIndent(ts, indent);
+ ts << "[PaintFilterEffect]\n";
+ return ts;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/paint_filter_effect.h b/chromium/third_party/blink/renderer/platform/graphics/filters/paint_filter_effect.h
new file mode 100644
index 00000000000..f320efec706
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/paint_filter_effect.h
@@ -0,0 +1,33 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_PAINT_FILTER_EFFECT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_PAINT_FILTER_EFFECT_H_
+
+#include "third_party/blink/renderer/platform/graphics/filters/filter_effect.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT PaintFilterEffect : public FilterEffect {
+ public:
+ static PaintFilterEffect* Create(Filter*, const PaintFlags&);
+ ~PaintFilterEffect() override;
+
+ FilterEffectType GetFilterEffectType() const override {
+ return kFilterEffectTypeSourceInput;
+ }
+
+ TextStream& ExternalRepresentation(TextStream&, int indention) const override;
+ sk_sp<PaintFilter> CreateImageFilter() override;
+
+ private:
+ PaintFilterEffect(Filter*, const PaintFlags&);
+
+ PaintFlags flags_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_PAINT_FILTER_EFFECT_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/point_light_source.cc b/chromium/third_party/blink/renderer/platform/graphics/filters/point_light_source.cc
new file mode 100644
index 00000000000..2c6f4f115bd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/point_light_source.cc
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
+ * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2010 Zoltan Herczeg <zherczeg@webkit.org>
+ * Copyright (C) 2011 University of Szeged
+ * Copyright (C) 2011 Renata Hodovan <reni@webkit.org>
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED 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 "third_party/blink/renderer/platform/graphics/filters/point_light_source.h"
+
+#include "third_party/blink/renderer/platform/text/text_stream.h"
+
+namespace blink {
+
+bool PointLightSource::SetPosition(const FloatPoint3D& position) {
+ if (position_ == position)
+ return false;
+ position_ = position;
+ return true;
+}
+
+TextStream& PointLightSource::ExternalRepresentation(TextStream& ts) const {
+ ts << "[type=POINT-LIGHT] ";
+ ts << "[position=\"" << GetPosition() << "\"]";
+ return ts;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/point_light_source.h b/chromium/third_party/blink/renderer/platform/graphics/filters/point_light_source.h
new file mode 100644
index 00000000000..5e4d625631c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/point_light_source.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2008 Alex Mathews <possessedpenguinbob@gmail.com>
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
+ * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_POINT_LIGHT_SOURCE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_POINT_LIGHT_SOURCE_H_
+
+#include "third_party/blink/renderer/platform/graphics/filters/light_source.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT PointLightSource final : public LightSource {
+ public:
+ static scoped_refptr<PointLightSource> Create(const FloatPoint3D& position) {
+ return base::AdoptRef(new PointLightSource(position));
+ }
+
+ const FloatPoint3D& GetPosition() const { return position_; }
+ bool SetPosition(const FloatPoint3D&) override;
+
+ TextStream& ExternalRepresentation(TextStream&) const override;
+
+ private:
+ PointLightSource(const FloatPoint3D& position)
+ : LightSource(LS_POINT), position_(position) {}
+
+ FloatPoint3D position_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_POINT_LIGHT_SOURCE_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/source_alpha.cc b/chromium/third_party/blink/renderer/platform/graphics/filters/source_alpha.cc
new file mode 100644
index 00000000000..5ea8cdecc69
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/source_alpha.cc
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "third_party/blink/renderer/platform/graphics/filters/source_alpha.h"
+
+#include "third_party/blink/renderer/platform/graphics/filters/filter.h"
+#include "third_party/blink/renderer/platform/graphics/filters/paint_filter_builder.h"
+#include "third_party/blink/renderer/platform/text/text_stream.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/skia/include/effects/SkColorFilterImageFilter.h"
+#include "third_party/skia/include/effects/SkColorMatrixFilter.h"
+
+namespace blink {
+
+SourceAlpha* SourceAlpha::Create(FilterEffect* source_effect) {
+ return new SourceAlpha(source_effect);
+}
+
+SourceAlpha::SourceAlpha(FilterEffect* source_effect)
+ : FilterEffect(source_effect->GetFilter()) {
+ SetOperatingInterpolationSpace(source_effect->OperatingInterpolationSpace());
+ InputEffects().push_back(source_effect);
+}
+
+sk_sp<PaintFilter> SourceAlpha::CreateImageFilter() {
+ sk_sp<PaintFilter> source_graphic(
+ PaintFilterBuilder::Build(InputEffect(0), OperatingInterpolationSpace()));
+ SkScalar matrix[20] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, SK_Scalar1, 0};
+ sk_sp<SkColorFilter> color_filter =
+ SkColorFilter::MakeMatrixFilterRowMajor255(matrix);
+ return sk_make_sp<ColorFilterPaintFilter>(std::move(color_filter),
+ std::move(source_graphic));
+}
+
+TextStream& SourceAlpha::ExternalRepresentation(TextStream& ts,
+ int indent) const {
+ WriteIndent(ts, indent);
+ ts << "[SourceAlpha]\n";
+ return ts;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/source_alpha.h b/chromium/third_party/blink/renderer/platform/graphics/filters/source_alpha.h
new file mode 100644
index 00000000000..fedadb144b0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/source_alpha.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_SOURCE_ALPHA_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_SOURCE_ALPHA_H_
+
+#include "third_party/blink/renderer/platform/graphics/filters/filter_effect.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT SourceAlpha final : public FilterEffect {
+ public:
+ static SourceAlpha* Create(FilterEffect*);
+
+ TextStream& ExternalRepresentation(TextStream&, int indention) const override;
+
+ private:
+ explicit SourceAlpha(FilterEffect*);
+
+ FilterEffectType GetFilterEffectType() const override {
+ return kFilterEffectTypeSourceInput;
+ }
+
+ sk_sp<PaintFilter> CreateImageFilter() override;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_SOURCE_ALPHA_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/source_graphic.cc b/chromium/third_party/blink/renderer/platform/graphics/filters/source_graphic.cc
new file mode 100644
index 00000000000..02b0bf8293a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/source_graphic.cc
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "third_party/blink/renderer/platform/graphics/filters/source_graphic.h"
+
+#include "third_party/blink/renderer/platform/graphics/filters/filter.h"
+#include "third_party/blink/renderer/platform/text/text_stream.h"
+
+namespace blink {
+
+SourceGraphic::SourceGraphic(Filter* filter) : FilterEffect(filter) {
+ SetOperatingInterpolationSpace(kInterpolationSpaceSRGB);
+}
+
+SourceGraphic::~SourceGraphic() = default;
+
+SourceGraphic* SourceGraphic::Create(Filter* filter) {
+ return new SourceGraphic(filter);
+}
+
+FloatRect SourceGraphic::MapInputs(const FloatRect& rect) const {
+ return !source_rect_.IsEmpty() ? source_rect_ : rect;
+}
+
+void SourceGraphic::SetSourceRect(const IntRect& source_rect) {
+ source_rect_ = source_rect;
+}
+
+TextStream& SourceGraphic::ExternalRepresentation(TextStream& ts,
+ int indent) const {
+ WriteIndent(ts, indent);
+ ts << "[SourceGraphic]\n";
+ return ts;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/source_graphic.h b/chromium/third_party/blink/renderer/platform/graphics/filters/source_graphic.h
new file mode 100644
index 00000000000..0e06362b759
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/source_graphic.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2008 Alex Mathews <possessedpenguinbob@gmail.com>
+ * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_SOURCE_GRAPHIC_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_SOURCE_GRAPHIC_H_
+
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/graphics/filters/filter_effect.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT SourceGraphic final : public FilterEffect {
+ public:
+ static SourceGraphic* Create(Filter*);
+ ~SourceGraphic() override;
+
+ TextStream& ExternalRepresentation(TextStream&, int indention) const override;
+
+ void SetSourceRect(const IntRect&);
+
+ private:
+ explicit SourceGraphic(Filter*);
+
+ FilterEffectType GetFilterEffectType() const override {
+ return kFilterEffectTypeSourceInput;
+ }
+
+ FloatRect MapInputs(const FloatRect&) const override;
+
+ IntRect source_rect_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_SOURCE_GRAPHIC_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/spot_light_source.cc b/chromium/third_party/blink/renderer/platform/graphics/filters/spot_light_source.cc
new file mode 100644
index 00000000000..a47abedb89e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/spot_light_source.cc
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2008 Alex Mathews <possessedpenguinbob@gmail.com>
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
+ * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2010 Zoltan Herczeg <zherczeg@webkit.org>
+ * Copyright (C) 2011 University of Szeged
+ * Copyright (C) 2011 Renata Hodovan <reni@webkit.org>
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED 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 "third_party/blink/renderer/platform/graphics/filters/spot_light_source.h"
+
+#include "third_party/blink/renderer/platform/text/text_stream.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+namespace blink {
+
+bool SpotLightSource::SetPosition(const FloatPoint3D& position) {
+ if (position_ == position)
+ return false;
+ position_ = position;
+ return true;
+}
+
+bool SpotLightSource::SetPointsAt(const FloatPoint3D& direction) {
+ if (direction_ == direction)
+ return false;
+ direction_ = direction;
+ return true;
+}
+
+bool SpotLightSource::SetSpecularExponent(float specular_exponent) {
+ specular_exponent = clampTo(specular_exponent, 1.0f, 128.0f);
+ if (specular_exponent_ == specular_exponent)
+ return false;
+ specular_exponent_ = specular_exponent;
+ return true;
+}
+
+bool SpotLightSource::SetLimitingConeAngle(float limiting_cone_angle) {
+ if (limiting_cone_angle_ == limiting_cone_angle)
+ return false;
+ limiting_cone_angle_ = limiting_cone_angle;
+ return true;
+}
+
+TextStream& SpotLightSource::ExternalRepresentation(TextStream& ts) const {
+ ts << "[type=SPOT-LIGHT] ";
+ ts << "[position=\"" << GetPosition() << "\"]";
+ ts << "[direction=\"" << Direction() << "\"]";
+ ts << "[specularExponent=\"" << SpecularExponent() << "\"]";
+ ts << "[limitingConeAngle=\"" << LimitingConeAngle() << "\"]";
+ return ts;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/spot_light_source.h b/chromium/third_party/blink/renderer/platform/graphics/filters/spot_light_source.h
new file mode 100644
index 00000000000..463b0c0d356
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/filters/spot_light_source.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2008 Alex Mathews <possessedpenguinbob@gmail.com>
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
+ * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_SPOT_LIGHT_SOURCE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_SPOT_LIGHT_SOURCE_H_
+
+#include "third_party/blink/renderer/platform/graphics/filters/light_source.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT SpotLightSource final : public LightSource {
+ public:
+ static scoped_refptr<SpotLightSource> Create(const FloatPoint3D& position,
+ const FloatPoint3D& direction,
+ float specular_exponent,
+ float limiting_cone_angle) {
+ return base::AdoptRef(new SpotLightSource(
+ position, direction, specular_exponent, limiting_cone_angle));
+ }
+
+ const FloatPoint3D& GetPosition() const { return position_; }
+ const FloatPoint3D& Direction() const { return direction_; }
+ float SpecularExponent() const { return specular_exponent_; }
+ float LimitingConeAngle() const { return limiting_cone_angle_; }
+
+ bool SetPosition(const FloatPoint3D&) override;
+ bool SetPointsAt(const FloatPoint3D&) override;
+
+ bool SetSpecularExponent(float) override;
+ bool SetLimitingConeAngle(float) override;
+
+ TextStream& ExternalRepresentation(TextStream&) const override;
+
+ private:
+ SpotLightSource(const FloatPoint3D& position,
+ const FloatPoint3D& direction,
+ float specular_exponent,
+ float limiting_cone_angle)
+ : LightSource(LS_SPOT),
+ position_(position),
+ direction_(direction),
+ specular_exponent_(clampTo(specular_exponent, 1.0f, 128.0f)),
+ limiting_cone_angle_(limiting_cone_angle) {}
+
+ FloatPoint3D position_;
+ FloatPoint3D direction_;
+
+ float specular_exponent_;
+ float limiting_cone_angle_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_SPOT_LIGHT_SOURCE_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/first_paint_invalidation_tracking.cc b/chromium/third_party/blink/renderer/platform/graphics/first_paint_invalidation_tracking.cc
new file mode 100644
index 00000000000..e3d723e9133
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/first_paint_invalidation_tracking.cc
@@ -0,0 +1,11 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/first_paint_invalidation_tracking.h"
+
+namespace blink {
+
+bool FirstPaintInvalidationTracking::enabled_for_show_paint_rects_ = false;
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/first_paint_invalidation_tracking.h b/chromium/third_party/blink/renderer/platform/graphics/first_paint_invalidation_tracking.h
new file mode 100644
index 00000000000..a338c3798e7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/first_paint_invalidation_tracking.h
@@ -0,0 +1,35 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FIRST_PAINT_INVALIDATION_TRACKING_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FIRST_PAINT_INVALIDATION_TRACKING_H_
+
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT FirstPaintInvalidationTracking {
+ public:
+ static bool IsEnabled() {
+ if (enabled_for_show_paint_rects_)
+ return true;
+
+ bool is_tracing_enabled;
+ TRACE_EVENT_CATEGORY_GROUP_ENABLED(
+ TRACE_DISABLED_BY_DEFAULT("blink.invalidation"), &is_tracing_enabled);
+ return is_tracing_enabled;
+ }
+
+ static void SetEnabledForShowPaintRects(bool enabled) {
+ enabled_for_show_paint_rects_ = enabled;
+ }
+
+ private:
+ static bool enabled_for_show_paint_rects_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FIRST_PAINT_INVALIDATION_TRACKING_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/generated_image.cc b/chromium/third_party/blink/renderer/platform/graphics/generated_image.cc
new file mode 100644
index 00000000000..2db7d3e5f15
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/generated_image.cc
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2012 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 "third_party/blink/renderer/platform/graphics/generated_image.h"
+
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_record.h"
+#include "third_party/skia/include/core/SkImage.h"
+
+namespace blink {
+
+void GeneratedImage::DrawPattern(GraphicsContext& dest_context,
+ const FloatRect& src_rect,
+ const FloatSize& scale,
+ const FloatPoint& phase,
+ SkBlendMode composite_op,
+ const FloatRect& dest_rect,
+ const FloatSize& repeat_spacing) {
+ FloatRect tile_rect = src_rect;
+ tile_rect.Expand(FloatSize(repeat_spacing));
+
+ std::unique_ptr<PaintController> paint_controller = PaintController::Create();
+ GraphicsContext context(*paint_controller);
+ context.BeginRecording(tile_rect);
+ DrawTile(context, src_rect);
+ sk_sp<PaintRecord> record = context.EndRecording();
+
+ SkMatrix pattern_matrix = SkMatrix::MakeTrans(phase.X(), phase.Y());
+ pattern_matrix.preScale(scale.Width(), scale.Height());
+ pattern_matrix.preTranslate(tile_rect.X(), tile_rect.Y());
+
+ scoped_refptr<Pattern> pattern =
+ Pattern::CreatePaintRecordPattern(std::move(record), tile_rect);
+
+ PaintFlags fill_flags = dest_context.FillFlags();
+ pattern->ApplyToFlags(fill_flags, pattern_matrix);
+ fill_flags.setColor(SK_ColorBLACK);
+ fill_flags.setBlendMode(composite_op);
+
+ dest_context.DrawRect(dest_rect, fill_flags);
+}
+
+PaintImage GeneratedImage::PaintImageForCurrentFrame() {
+ return PaintImage();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/generated_image.h b/chromium/third_party/blink/renderer/platform/graphics/generated_image.h
new file mode 100644
index 00000000000..905b930cb88
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/generated_image.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2008 Apple Computer, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GENERATED_IMAGE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GENERATED_IMAGE_H_
+
+#include "third_party/blink/renderer/platform/geometry/layout_size.h"
+#include "third_party/blink/renderer/platform/graphics/image.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT GeneratedImage : public Image {
+ public:
+ bool CurrentFrameHasSingleSecurityOrigin() const override { return true; }
+
+ bool UsesContainerSize() const override { return true; }
+ bool HasRelativeSize() const override { return true; }
+
+ IntSize Size() const override { return RoundedIntSize(size_); }
+
+ // Assume that generated content has no decoded data we need to worry about
+ void DestroyDecodedData() override {}
+
+ PaintImage PaintImageForCurrentFrame() override;
+
+ protected:
+ void DrawPattern(GraphicsContext&,
+ const FloatRect&,
+ const FloatSize&,
+ const FloatPoint&,
+ SkBlendMode,
+ const FloatRect&,
+ const FloatSize& repeat_spacing) final;
+
+ // FIXME: Implement this to be less conservative.
+ bool CurrentFrameKnownToBeOpaque() override { return false; }
+
+ GeneratedImage(const FloatSize& size) : size_(size) {}
+
+ virtual void DrawTile(GraphicsContext&, const FloatRect&) = 0;
+
+ FloatSize size_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/DEPS b/chromium/third_party/blink/renderer/platform/graphics/gpu/DEPS
new file mode 100644
index 00000000000..adce483a8d2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/DEPS
@@ -0,0 +1,13 @@
+include_rules = [
+ "+mojo/public/cpp/bindings/binding.h",
+ "+mojo/public/cpp/system/platform_handle.h",
+ "+device/vr/public/mojom/vr_service.mojom-blink.h",
+ "+gpu/command_buffer/client/gles2_interface.h",
+ "+gpu/command_buffer/client/gles2_interface_stub.h",
+ "+gpu/command_buffer/common/capabilities.h",
+ "+gpu/command_buffer/common/gpu_memory_buffer_support.h",
+ "+gpu/command_buffer/common/mailbox_holder.h",
+ "+gpu/config/gpu_driver_bug_workaround_type.h",
+ "+gpu/config/gpu_feature_info.h",
+ "+ui/gfx/gpu_fence.h",
+]
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/OWNERS b/chromium/third_party/blink/renderer/platform/graphics/gpu/OWNERS
new file mode 100644
index 00000000000..45ab8c73e8c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/OWNERS
@@ -0,0 +1,4 @@
+# The bulk of the files in this directory are related to WebGL, though
+# there are a couple used by accelerated 2D canvas as well.
+
+# COMPONENT: Blink>WebGL
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
new file mode 100644
index 00000000000..dded64ab9ff
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
@@ -0,0 +1,1603 @@
+/*
+ * Copyright (c) 2010, 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 "third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h"
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+#include "build/build_config.h"
+#include "cc/layers/texture_layer.h"
+#include "components/viz/common/quads/shared_bitmap.h"
+#include "components/viz/common/resources/bitmap_allocation.h"
+#include "components/viz/common/resources/transferable_resource.h"
+#include "gpu/GLES2/gl2extchromium.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
+#include "gpu/command_buffer/common/capabilities.h"
+#include "gpu/command_buffer/common/gpu_memory_buffer_support.h"
+#include "gpu/config/gpu_driver_bug_workaround_type.h"
+#include "gpu/config/gpu_feature_info.h"
+#include "skia/ext/texture_handle.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_compositor_support.h"
+#include "third_party/blink/public/platform/web_external_texture_layer.h"
+#include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
+#include "third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h"
+#include "third_party/blink/renderer/platform/graphics/web_graphics_context_3d_provider_wrapper.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/wtf/checked_numeric.h"
+#include "third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_contents.h"
+#include "third_party/skia/include/core/SkColorSpaceXform.h"
+#include "third_party/skia/include/core/SkSurface.h"
+#include "third_party/skia/include/gpu/GrContext.h"
+#include "third_party/skia/include/gpu/gl/GrGLTypes.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+namespace {
+
+const float kResourceAdjustedRatio = 0.5;
+
+static bool g_should_fail_drawing_buffer_creation_for_testing = false;
+
+} // namespace
+
+scoped_refptr<DrawingBuffer> DrawingBuffer::Create(
+ std::unique_ptr<WebGraphicsContext3DProvider> context_provider,
+ bool using_gpu_compositing,
+ Client* client,
+ const IntSize& size,
+ bool premultiplied_alpha,
+ bool want_alpha_channel,
+ bool want_depth_buffer,
+ bool want_stencil_buffer,
+ bool want_antialiasing,
+ PreserveDrawingBuffer preserve,
+ WebGLVersion webgl_version,
+ ChromiumImageUsage chromium_image_usage,
+ const CanvasColorParams& color_params) {
+ if (g_should_fail_drawing_buffer_creation_for_testing) {
+ g_should_fail_drawing_buffer_creation_for_testing = false;
+ return nullptr;
+ }
+
+ CheckedNumeric<int> data_size = color_params.BytesPerPixel();
+ data_size *= size.Width();
+ data_size *= size.Height();
+ if (!data_size.IsValid() ||
+ data_size.ValueOrDie() > v8::TypedArray::kMaxLength)
+ return nullptr;
+
+ DCHECK(context_provider);
+ std::unique_ptr<Extensions3DUtil> extensions_util =
+ Extensions3DUtil::Create(context_provider->ContextGL());
+ if (!extensions_util->IsValid()) {
+ // This might be the first time we notice that the GL context is lost.
+ return nullptr;
+ }
+ DCHECK(extensions_util->SupportsExtension("GL_OES_packed_depth_stencil"));
+ extensions_util->EnsureExtensionEnabled("GL_OES_packed_depth_stencil");
+ bool multisample_supported =
+ want_antialiasing &&
+ (extensions_util->SupportsExtension(
+ "GL_CHROMIUM_framebuffer_multisample") ||
+ extensions_util->SupportsExtension(
+ "GL_EXT_multisampled_render_to_texture")) &&
+ extensions_util->SupportsExtension("GL_OES_rgb8_rgba8");
+ if (multisample_supported) {
+ extensions_util->EnsureExtensionEnabled("GL_OES_rgb8_rgba8");
+ if (extensions_util->SupportsExtension(
+ "GL_CHROMIUM_framebuffer_multisample")) {
+ extensions_util->EnsureExtensionEnabled(
+ "GL_CHROMIUM_framebuffer_multisample");
+ } else {
+ extensions_util->EnsureExtensionEnabled(
+ "GL_EXT_multisampled_render_to_texture");
+ }
+ }
+ bool discard_framebuffer_supported =
+ extensions_util->SupportsExtension("GL_EXT_discard_framebuffer");
+ if (discard_framebuffer_supported)
+ extensions_util->EnsureExtensionEnabled("GL_EXT_discard_framebuffer");
+
+ scoped_refptr<DrawingBuffer> drawing_buffer =
+ base::AdoptRef(new DrawingBuffer(
+ std::move(context_provider), using_gpu_compositing,
+ std::move(extensions_util), client, discard_framebuffer_supported,
+ want_alpha_channel, premultiplied_alpha, preserve, webgl_version,
+ want_depth_buffer, want_stencil_buffer, chromium_image_usage,
+ color_params));
+ if (!drawing_buffer->Initialize(size, multisample_supported)) {
+ drawing_buffer->BeginDestruction();
+ return scoped_refptr<DrawingBuffer>();
+ }
+ return drawing_buffer;
+}
+
+void DrawingBuffer::ForceNextDrawingBufferCreationToFail() {
+ g_should_fail_drawing_buffer_creation_for_testing = true;
+}
+
+DrawingBuffer::DrawingBuffer(
+ std::unique_ptr<WebGraphicsContext3DProvider> context_provider,
+ bool using_gpu_compositing,
+ std::unique_ptr<Extensions3DUtil> extensions_util,
+ Client* client,
+ bool discard_framebuffer_supported,
+ bool want_alpha_channel,
+ bool premultiplied_alpha,
+ PreserveDrawingBuffer preserve,
+ WebGLVersion webgl_version,
+ bool want_depth,
+ bool want_stencil,
+ ChromiumImageUsage chromium_image_usage,
+ const CanvasColorParams& color_params)
+ : client_(client),
+ preserve_drawing_buffer_(preserve),
+ webgl_version_(webgl_version),
+ context_provider_(std::make_unique<WebGraphicsContext3DProviderWrapper>(
+ std::move(context_provider))),
+ gl_(this->ContextProvider()->ContextGL()),
+ extensions_util_(std::move(extensions_util)),
+ discard_framebuffer_supported_(discard_framebuffer_supported),
+ want_alpha_channel_(want_alpha_channel),
+ premultiplied_alpha_(premultiplied_alpha),
+ using_gpu_compositing_(using_gpu_compositing),
+ want_depth_(want_depth),
+ want_stencil_(want_stencil),
+ storage_color_space_(color_params.GetStorageGfxColorSpace()),
+ sampler_color_space_(color_params.GetSamplerGfxColorSpace()),
+ use_half_float_storage_(color_params.PixelFormat() ==
+ kF16CanvasPixelFormat),
+ chromium_image_usage_(chromium_image_usage) {
+ // Used by browser tests to detect the use of a DrawingBuffer.
+ TRACE_EVENT_INSTANT0("test_gpu", "DrawingBufferCreation",
+ TRACE_EVENT_SCOPE_GLOBAL);
+}
+
+DrawingBuffer::~DrawingBuffer() {
+ DCHECK(destruction_in_progress_);
+ layer_.reset();
+ context_provider_.reset();
+}
+
+bool DrawingBuffer::MarkContentsChanged() {
+ if (contents_change_resolved_ || !contents_changed_) {
+ contents_change_resolved_ = false;
+ contents_changed_ = true;
+ return true;
+ }
+ return false;
+}
+
+void DrawingBuffer::ResetBuffersToAutoClear() {
+ GLuint buffers = GL_COLOR_BUFFER_BIT;
+ if (want_depth_)
+ buffers |= GL_DEPTH_BUFFER_BIT;
+ if (want_stencil_ || has_implicit_stencil_buffer_)
+ buffers |= GL_STENCIL_BUFFER_BIT;
+ SetBuffersToAutoClear(buffers);
+}
+
+void DrawingBuffer::SetBuffersToAutoClear(GLbitfield buffers) {
+ if (preserve_drawing_buffer_ == kDiscard) {
+ buffers_to_auto_clear_ = buffers;
+ } else {
+ DCHECK_EQ(0u, buffers_to_auto_clear_);
+ }
+}
+
+GLbitfield DrawingBuffer::GetBuffersToAutoClear() const {
+ return buffers_to_auto_clear_;
+}
+
+gpu::gles2::GLES2Interface* DrawingBuffer::ContextGL() {
+ return gl_;
+}
+
+WebGraphicsContext3DProvider* DrawingBuffer::ContextProvider() {
+ return context_provider_->ContextProvider();
+}
+
+base::WeakPtr<WebGraphicsContext3DProviderWrapper>
+DrawingBuffer::ContextProviderWeakPtr() {
+ return context_provider_->GetWeakPtr();
+}
+
+void DrawingBuffer::SetIsHidden(bool hidden) {
+ if (is_hidden_ == hidden)
+ return;
+ is_hidden_ = hidden;
+ if (is_hidden_)
+ recycled_color_buffer_queue_.clear();
+}
+
+void DrawingBuffer::SetFilterQuality(SkFilterQuality filter_quality) {
+ if (filter_quality_ != filter_quality) {
+ filter_quality_ = filter_quality;
+ if (layer_)
+ layer_->SetNearestNeighbor(filter_quality == kNone_SkFilterQuality);
+ }
+}
+
+bool DrawingBuffer::RequiresAlphaChannelToBePreserved() {
+ return client_->DrawingBufferClientIsBoundForDraw() &&
+ DefaultBufferRequiresAlphaChannelToBePreserved();
+}
+
+bool DrawingBuffer::DefaultBufferRequiresAlphaChannelToBePreserved() {
+ return !want_alpha_channel_ && have_alpha_channel_;
+}
+
+DrawingBuffer::RegisteredBitmap DrawingBuffer::CreateOrRecycleBitmap(
+ cc::SharedBitmapIdRegistrar* bitmap_registrar) {
+ auto it = std::remove_if(recycled_bitmaps_.begin(), recycled_bitmaps_.end(),
+ [this](const RegisteredBitmap& registered) {
+ return registered.bitmap->size() != size_;
+ });
+ recycled_bitmaps_.Shrink(it - recycled_bitmaps_.begin());
+
+ if (!recycled_bitmaps_.IsEmpty()) {
+ RegisteredBitmap recycled = std::move(recycled_bitmaps_.back());
+ recycled_bitmaps_.pop_back();
+ DCHECK(recycled.bitmap->size() == size_);
+ return recycled;
+ }
+
+ viz::SharedBitmapId id = viz::SharedBitmap::GenerateId();
+ std::unique_ptr<base::SharedMemory> shm =
+ viz::bitmap_allocation::AllocateMappedBitmap(size_, viz::RGBA_8888);
+ auto bitmap = base::MakeRefCounted<cc::CrossThreadSharedBitmap>(
+ id, std::move(shm), size_, viz::RGBA_8888);
+ RegisteredBitmap registered = {
+ bitmap, bitmap_registrar->RegisterSharedBitmapId(id, bitmap)};
+ return registered;
+}
+
+bool DrawingBuffer::PrepareTransferableResource(
+ cc::SharedBitmapIdRegistrar* bitmap_registrar,
+ viz::TransferableResource* out_resource,
+ std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback) {
+ ScopedStateRestorer scoped_state_restorer(this);
+ bool force_gpu_result = false;
+ return PrepareTransferableResourceInternal(
+ bitmap_registrar, out_resource, out_release_callback, force_gpu_result);
+}
+
+bool DrawingBuffer::PrepareTransferableResourceInternal(
+ cc::SharedBitmapIdRegistrar* bitmap_registrar,
+ viz::TransferableResource* out_resource,
+ std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback,
+ bool force_gpu_result) {
+ DCHECK(state_restorer_);
+ if (destruction_in_progress_) {
+ // It can be hit in the following sequence.
+ // 1. WebGL draws something.
+ // 2. The compositor begins the frame.
+ // 3. Javascript makes a context lost using WEBGL_lose_context extension.
+ // 4. Here.
+ return false;
+ }
+ DCHECK(!is_hidden_);
+ if (!contents_changed_)
+ return false;
+
+ // If the context is lost, we don't know if we should be producing GPU or
+ // software frames, until we get a new context, since the compositor will
+ // be trying to get a new context and may change modes.
+ if (gl_->GetGraphicsResetStatusKHR() != GL_NO_ERROR)
+ return false;
+
+ TRACE_EVENT0("blink,rail", "DrawingBuffer::prepareMailbox");
+
+ // Resolve the multisampled buffer into the texture attached to fbo_.
+ ResolveIfNeeded();
+
+ if (!using_gpu_compositing_ && !force_gpu_result) {
+ FinishPrepareTransferableResourceSoftware(bitmap_registrar, out_resource,
+ out_release_callback);
+ } else {
+ FinishPrepareTransferableResourceGpu(out_resource, out_release_callback);
+ }
+ return true;
+}
+
+void DrawingBuffer::FinishPrepareTransferableResourceSoftware(
+ cc::SharedBitmapIdRegistrar* bitmap_registrar,
+ viz::TransferableResource* out_resource,
+ std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback) {
+ DCHECK(state_restorer_);
+ RegisteredBitmap registered = CreateOrRecycleBitmap(bitmap_registrar);
+
+ // Read the framebuffer into |bitmap|.
+ {
+ unsigned char* pixels = static_cast<unsigned char*>(
+ registered.bitmap->shared_memory()->memory());
+ DCHECK(pixels);
+ bool need_premultiply = want_alpha_channel_ && !premultiplied_alpha_;
+ WebGLImageConversion::AlphaOp op =
+ need_premultiply ? WebGLImageConversion::kAlphaDoPremultiply
+ : WebGLImageConversion::kAlphaDoNothing;
+ state_restorer_->SetFramebufferBindingDirty();
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
+ ReadBackFramebuffer(pixels, Size().Width(), Size().Height(), kReadbackSkia,
+ op);
+ }
+
+ *out_resource = viz::TransferableResource::MakeSoftware(
+ registered.bitmap->id(), /*sequence_number=*/0, size_, viz::RGBA_8888);
+ out_resource->color_space = storage_color_space_;
+
+ // This holds a ref on the DrawingBuffer that will keep it alive until the
+ // mailbox is released (and while the release callback is running). It also
+ // owns the SharedBitmap.
+ auto func = WTF::Bind(&DrawingBuffer::MailboxReleasedSoftware,
+ scoped_refptr<DrawingBuffer>(this),
+ WTF::Passed(std::move(registered)));
+ *out_release_callback = viz::SingleReleaseCallback::Create(std::move(func));
+
+ ResetBuffersToAutoClear();
+}
+
+void DrawingBuffer::FinishPrepareTransferableResourceGpu(
+ viz::TransferableResource* out_resource,
+ std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback) {
+ DCHECK(state_restorer_);
+ if (webgl_version_ > kWebGL1) {
+ state_restorer_->SetPixelUnpackBufferBindingDirty();
+ gl_->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+ }
+
+ if (premultiplied_alpha_false_texture_) {
+ // The rendering results are in this texture rather than the
+ // back_color_buffer_'s texture. Copy them in, multiplying the alpha channel
+ // into the color channels.
+ gl_->CopySubTextureCHROMIUM(premultiplied_alpha_false_texture_, 0,
+ texture_target_, back_color_buffer_->texture_id,
+ 0, 0, 0, 0, 0, size_.Width(), size_.Height(),
+ GL_FALSE, GL_TRUE, GL_FALSE);
+ }
+
+ // Specify the buffer that we will put in the mailbox.
+ scoped_refptr<ColorBuffer> color_buffer_for_mailbox;
+ if (preserve_drawing_buffer_ == kDiscard) {
+ // If we can discard the backbuffer, send the old backbuffer directly
+ // into the mailbox, and allocate (or recycle) a new backbuffer.
+ color_buffer_for_mailbox = back_color_buffer_;
+ back_color_buffer_ = CreateOrRecycleColorBuffer();
+ AttachColorBufferToReadFramebuffer();
+
+ // Explicitly specify that m_fbo (which is now bound to the just-allocated
+ // m_backColorBuffer) is not initialized, to save GPU memory bandwidth for
+ // tile-based GPU architectures.
+ if (discard_framebuffer_supported_) {
+ const GLenum kAttachments[3] = {GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT,
+ GL_STENCIL_ATTACHMENT};
+ state_restorer_->SetFramebufferBindingDirty();
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
+ gl_->DiscardFramebufferEXT(GL_FRAMEBUFFER, 3, kAttachments);
+ }
+ } else {
+ // If we can't discard the backbuffer, create (or recycle) a buffer to put
+ // in the mailbox, and copy backbuffer's contents there.
+ color_buffer_for_mailbox = CreateOrRecycleColorBuffer();
+ gl_->CopySubTextureCHROMIUM(
+ back_color_buffer_->texture_id, 0, texture_target_,
+ color_buffer_for_mailbox->texture_id, 0, 0, 0, 0, 0, size_.Width(),
+ size_.Height(), GL_FALSE, GL_FALSE, GL_FALSE);
+ }
+
+ // Put colorBufferForMailbox into its mailbox, and populate its
+ // produceSyncToken with that point.
+ {
+ gl_->ProduceTextureDirectCHROMIUM(color_buffer_for_mailbox->texture_id,
+ color_buffer_for_mailbox->mailbox.name);
+ // It's critical to order the execution of this context's work relative
+ // to other contexts, in particular the compositor. Previously this
+ // used to be a Flush, and there was a bug that we didn't flush before
+ // synchronizing with the composition, and on some platforms this caused
+ // incorrect rendering with complex WebGL content that wasn't always
+ // properly flushed to the driver. There is now a basic assumption that
+ // there are implicit flushes between contexts at the lowest level.
+ gl_->GenUnverifiedSyncTokenCHROMIUM(
+ color_buffer_for_mailbox->produce_sync_token.GetData());
+#if defined(OS_MACOSX)
+ // Needed for GPU back-pressure on macOS. Used to be in the middle
+ // of the commands above; try to move it to the bottom to allow
+ // them to be treated atomically.
+ gl_->DescheduleUntilFinishedCHROMIUM();
+#endif
+ }
+
+ // Populate the output mailbox and callback.
+ {
+ bool is_overlay_candidate = color_buffer_for_mailbox->image_id != 0;
+ *out_resource = viz::TransferableResource::MakeGLOverlay(
+ color_buffer_for_mailbox->mailbox, GL_LINEAR, texture_target_,
+ color_buffer_for_mailbox->produce_sync_token, gfx::Size(size_),
+ is_overlay_candidate);
+ out_resource->color_space = sampler_color_space_;
+ out_resource->format = viz::RGBA_8888;
+
+ // This holds a ref on the DrawingBuffer that will keep it alive until the
+ // mailbox is released (and while the release callback is running).
+ auto func =
+ WTF::Bind(&DrawingBuffer::MailboxReleasedGpu,
+ scoped_refptr<DrawingBuffer>(this), color_buffer_for_mailbox);
+ *out_release_callback = viz::SingleReleaseCallback::Create(std::move(func));
+ }
+
+ // Point |m_frontColorBuffer| to the buffer that we are now presenting.
+ front_color_buffer_ = color_buffer_for_mailbox;
+
+ contents_changed_ = false;
+ ResetBuffersToAutoClear();
+}
+
+void DrawingBuffer::MailboxReleasedGpu(scoped_refptr<ColorBuffer> color_buffer,
+ const gpu::SyncToken& sync_token,
+ bool lost_resource) {
+ // If the mailbox has been returned by the compositor then it is no
+ // longer being presented, and so is no longer the front buffer.
+ if (color_buffer == front_color_buffer_)
+ front_color_buffer_ = nullptr;
+
+ // Update the SyncToken to ensure that we will wait for it even if we
+ // immediately destroy this buffer.
+ color_buffer->receive_sync_token = sync_token;
+
+ if (destruction_in_progress_ || color_buffer->size != size_ ||
+ gl_->GetGraphicsResetStatusKHR() != GL_NO_ERROR || lost_resource ||
+ is_hidden_) {
+ return;
+ }
+
+ // Creation of image backed mailboxes is very expensive, so be less
+ // aggressive about pruning them. Pruning is done in FIFO order.
+ size_t cache_limit = 1;
+ if (ShouldUseChromiumImage())
+ cache_limit = 4;
+ while (recycled_color_buffer_queue_.size() >= cache_limit)
+ recycled_color_buffer_queue_.TakeLast();
+
+ recycled_color_buffer_queue_.push_front(color_buffer);
+}
+
+void DrawingBuffer::MailboxReleasedSoftware(RegisteredBitmap registered,
+ const gpu::SyncToken& sync_token,
+ bool lost_resource) {
+ DCHECK(!sync_token.HasData()); // No sync tokens for software resources.
+ if (destruction_in_progress_ || lost_resource || is_hidden_ ||
+ registered.bitmap->size() != size_) {
+ // Just delete the RegisteredBitmap, which will free the memory and
+ // unregister it with the compositor.
+ return;
+ }
+
+ recycled_bitmaps_.push_back(std::move(registered));
+}
+
+scoped_refptr<StaticBitmapImage> DrawingBuffer::TransferToStaticBitmapImage(
+ std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback) {
+ ScopedStateRestorer scoped_state_restorer(this);
+
+ viz::TransferableResource transferable_resource;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback;
+ constexpr bool force_gpu_result = true;
+ if (!PrepareTransferableResourceInternal(nullptr, &transferable_resource,
+ &release_callback,
+ force_gpu_result)) {
+ // If we can't get a mailbox, return an transparent black ImageBitmap.
+ // The only situation in which this could happen is when two or more calls
+ // to transferToImageBitmap are made back-to-back, or when the context gets
+ // lost. We intentionally leave the transparent black image in legacy color
+ // space.
+ sk_sp<SkSurface> surface =
+ SkSurface::MakeRasterN32Premul(size_.Width(), size_.Height());
+ return StaticBitmapImage::Create(surface->makeImageSnapshot());
+ }
+
+ DCHECK_EQ(size_.Width(), transferable_resource.size.width());
+ DCHECK_EQ(size_.Height(), transferable_resource.size.height());
+
+ // Make our own textureId that is a reference on the same texture backing
+ // being used as the front buffer (which was returned from
+ // PrepareTransferableResourceInternal()). We do not need to wait on the sync
+ // token in |transferable_resource| since the mailbox was produced on the same
+ // |m_gl| context that we are using here. Similarly, the |release_callback|
+ // will run on the same context so we don't need to send a sync token for this
+ // consume action back to it.
+ // TODO(danakj): Instead of using PrepareTransferableResourceInternal(), we
+ // could just use the actual texture id and avoid needing to produce/consume a
+ // mailbox.
+ GLuint texture_id = gl_->CreateAndConsumeTextureCHROMIUM(
+ transferable_resource.mailbox_holder.mailbox.name);
+
+ if (out_release_callback) {
+ // Allow the consumer to release the resource when done using it, so it can
+ // be recycled.
+ *out_release_callback = std::move(release_callback);
+ } else {
+ // Return the mailbox but report that the resource is lost to prevent trying
+ // to use the backing for future frames. We keep it alive with our own
+ // reference to the backing via our |textureId|.
+ release_callback->Run(gpu::SyncToken(), true /* lost_resource */);
+ }
+
+ // We reuse the same mailbox name from above since our texture id was consumed
+ // from it.
+ const auto& sk_image_mailbox = transferable_resource.mailbox_holder.mailbox;
+ // Use the sync token generated after producing the mailbox. Waiting for this
+ // before trying to use the mailbox with some other context will ensure it is
+ // valid. We wouldn't need to wait for the consume done in this function
+ // because the texture id it generated would only be valid for the
+ // DrawingBuffer's context anyways.
+ const auto& sk_image_sync_token =
+ transferable_resource.mailbox_holder.sync_token;
+
+ // TODO(xidachen): Create a small pool of recycled textures from
+ // ImageBitmapRenderingContext's transferFromImageBitmap, and try to use them
+ // in DrawingBuffer.
+ return AcceleratedStaticBitmapImage::CreateFromWebGLContextImage(
+ sk_image_mailbox, sk_image_sync_token, texture_id,
+ context_provider_->GetWeakPtr(), size_);
+}
+
+scoped_refptr<DrawingBuffer::ColorBuffer> DrawingBuffer::CreateOrRecycleColorBuffer() {
+ DCHECK(state_restorer_);
+ if (!recycled_color_buffer_queue_.IsEmpty()) {
+ scoped_refptr<ColorBuffer> recycled =
+ recycled_color_buffer_queue_.TakeLast();
+ if (recycled->receive_sync_token.HasData())
+ gl_->WaitSyncTokenCHROMIUM(recycled->receive_sync_token.GetData());
+ DCHECK(recycled->size == size_);
+ return recycled;
+ }
+ return CreateColorBuffer(size_);
+}
+
+DrawingBuffer::ScopedRGBEmulationForBlitFramebuffer::
+ ScopedRGBEmulationForBlitFramebuffer(DrawingBuffer* drawing_buffer,
+ bool is_user_draw_framebuffer_bound)
+ : drawing_buffer_(drawing_buffer) {
+ doing_work_ = drawing_buffer->SetupRGBEmulationForBlitFramebuffer(
+ is_user_draw_framebuffer_bound);
+}
+
+DrawingBuffer::ScopedRGBEmulationForBlitFramebuffer::
+ ~ScopedRGBEmulationForBlitFramebuffer() {
+ if (doing_work_) {
+ drawing_buffer_->CleanupRGBEmulationForBlitFramebuffer();
+ }
+}
+
+DrawingBuffer::ColorBuffer::ColorBuffer(
+ DrawingBuffer* drawing_buffer,
+ const IntSize& size,
+ GLuint texture_id,
+ GLuint image_id,
+ std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer)
+ : drawing_buffer(drawing_buffer),
+ size(size),
+ texture_id(texture_id),
+ image_id(image_id),
+ gpu_memory_buffer(std::move(gpu_memory_buffer)) {
+ drawing_buffer->ContextGL()->GenMailboxCHROMIUM(mailbox.name);
+}
+
+DrawingBuffer::ColorBuffer::~ColorBuffer() {
+ gpu::gles2::GLES2Interface* gl = drawing_buffer->gl_;
+ GLenum texture_target = drawing_buffer->texture_target_;
+ if (receive_sync_token.HasData())
+ gl->WaitSyncTokenCHROMIUM(receive_sync_token.GetConstData());
+ if (image_id) {
+ gl->BindTexture(texture_target, texture_id);
+ gl->ReleaseTexImage2DCHROMIUM(texture_target, image_id);
+ if (rgb_workaround_texture_id) {
+ gl->BindTexture(texture_target, rgb_workaround_texture_id);
+ gl->ReleaseTexImage2DCHROMIUM(texture_target, image_id);
+ }
+ gl->DestroyImageCHROMIUM(image_id);
+ switch (texture_target) {
+ case GL_TEXTURE_2D:
+ // Restore the texture binding for GL_TEXTURE_2D, since the client will
+ // expect the previous state.
+ if (drawing_buffer->client_)
+ drawing_buffer->client_->DrawingBufferClientRestoreTexture2DBinding();
+ break;
+ case GC3D_TEXTURE_RECTANGLE_ARB:
+ // Rectangle textures aren't exposed to WebGL, so don't bother
+ // restoring this state (there is no meaningful way to restore it).
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+ gpu_memory_buffer.reset();
+ }
+ gl->DeleteTextures(1, &texture_id);
+ if (rgb_workaround_texture_id) {
+ // Avoid deleting this texture if it was unused.
+ gl->DeleteTextures(1, &rgb_workaround_texture_id);
+ }
+}
+
+bool DrawingBuffer::Initialize(const IntSize& size, bool use_multisampling) {
+ ScopedStateRestorer scoped_state_restorer(this);
+
+ if (gl_->GetGraphicsResetStatusKHR() != GL_NO_ERROR) {
+ // Need to try to restore the context again later.
+ DLOG(ERROR) << "Cannot initialize with lost context.";
+ return false;
+ }
+
+ // Specifying a half-float backbuffer requires and implicitly enables
+ // half-float backbuffer extensions.
+ if (use_half_float_storage_) {
+ const char* color_buffer_extension = webgl_version_ > kWebGL1
+ ? "GL_EXT_color_buffer_float"
+ : "GL_EXT_color_buffer_half_float";
+ if (!extensions_util_->EnsureExtensionEnabled(color_buffer_extension)) {
+ DLOG(ERROR) << "Half-float color buffer support is absent.";
+ return false;
+ }
+ // Support for RGB half-float renderbuffers is absent from ES3. Do not
+ // attempt to expose them.
+ if (!want_alpha_channel_) {
+ DLOG(ERROR) << "RGB half-float renderbuffers are not supported.";
+ return false;
+ }
+ }
+
+ gl_->GetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size_);
+
+ int max_sample_count = 0;
+ anti_aliasing_mode_ = kNone;
+ if (use_multisampling) {
+ gl_->GetIntegerv(GL_MAX_SAMPLES_ANGLE, &max_sample_count);
+ anti_aliasing_mode_ = kMSAAExplicitResolve;
+ if (extensions_util_->SupportsExtension(
+ "GL_EXT_multisampled_render_to_texture")) {
+ anti_aliasing_mode_ = kMSAAImplicitResolve;
+ } else if (extensions_util_->SupportsExtension(
+ "GL_CHROMIUM_screen_space_antialiasing")) {
+ anti_aliasing_mode_ = kScreenSpaceAntialiasing;
+ }
+ }
+ // TODO(dshwang): Enable storage textures on all platforms. crbug.com/557848
+ // The Linux ATI bot fails
+ // WebglConformance.conformance_textures_misc_tex_image_webgl, so use storage
+ // textures only if ScreenSpaceAntialiasing is enabled, because
+ // ScreenSpaceAntialiasing is much faster with storage textures.
+ storage_texture_supported_ =
+ (webgl_version_ > kWebGL1 ||
+ extensions_util_->SupportsExtension("GL_EXT_texture_storage")) &&
+ anti_aliasing_mode_ == kScreenSpaceAntialiasing;
+ // Performance regreses by 30% in WebGL apps for AMD Stoney
+ // if sample count is 8x
+ if (ContextProvider()->GetGpuFeatureInfo().IsWorkaroundEnabled(
+ gpu::MAX_MSAA_SAMPLE_COUNT_4))
+ sample_count_ = std::min(4, max_sample_count);
+ else
+ sample_count_ = std::min(8, max_sample_count);
+
+ texture_target_ = GL_TEXTURE_2D;
+#if defined(OS_MACOSX)
+ if (ShouldUseChromiumImage()) {
+ // A CHROMIUM_image backed texture requires a specialized set of parameters
+ // on OSX.
+ texture_target_ = GC3D_TEXTURE_RECTANGLE_ARB;
+ }
+#endif
+
+ // Initialize the alpha allocation settings based on the features and
+ // workarounds in use.
+ if (want_alpha_channel_) {
+ allocate_alpha_channel_ = true;
+ have_alpha_channel_ = true;
+ } else {
+ allocate_alpha_channel_ = false;
+ have_alpha_channel_ = false;
+ if (ContextProvider()->GetGpuFeatureInfo().IsWorkaroundEnabled(
+ gpu::DISABLE_GL_RGB_FORMAT)) {
+ // This configuration will
+ // - allow invalid CopyTexImage to RGBA targets
+ // - fail valid FramebufferBlit from RGB targets
+ // https://crbug.com/776269
+ allocate_alpha_channel_ = true;
+ have_alpha_channel_ = true;
+ }
+ if (WantExplicitResolve() &&
+ ContextProvider()->GetGpuFeatureInfo().IsWorkaroundEnabled(
+ gpu::DISABLE_WEBGL_RGB_MULTISAMPLING_USAGE)) {
+ // This configuration avoids the above issues because
+ // - CopyTexImage is invalid from multisample renderbuffers
+ // - FramebufferBlit is invalid to multisample renderbuffers
+ allocate_alpha_channel_ = true;
+ have_alpha_channel_ = true;
+ }
+ if (ShouldUseChromiumImage() &&
+ ContextProvider()->GetCapabilities().chromium_image_rgb_emulation) {
+ // This configuration avoids the above issues by
+ // - extra command buffer validation for CopyTexImage
+ // - explicity re-binding as RGB for FramebufferBlit
+ allocate_alpha_channel_ = false;
+ have_alpha_channel_ = true;
+ }
+ }
+
+ state_restorer_->SetFramebufferBindingDirty();
+ gl_->GenFramebuffers(1, &fbo_);
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
+ if (WantExplicitResolve()) {
+ gl_->GenFramebuffers(1, &multisample_fbo_);
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, multisample_fbo_);
+ gl_->GenRenderbuffers(1, &multisample_renderbuffer_);
+ }
+ if (!ResizeFramebufferInternal(size)) {
+ DLOG(ERROR) << "Initialization failed to allocate backbuffer.";
+ return false;
+ }
+
+ if (depth_stencil_buffer_) {
+ DCHECK(WantDepthOrStencil());
+ has_implicit_stencil_buffer_ = !want_stencil_;
+ }
+
+ if (gl_->GetGraphicsResetStatusKHR() != GL_NO_ERROR) {
+ // It's possible that the drawing buffer allocation provokes a context loss,
+ // so check again just in case. http://crbug.com/512302
+ DLOG(ERROR) << "Context lost during initialization.";
+ return false;
+ }
+
+ return true;
+}
+
+bool DrawingBuffer::CopyToPlatformTexture(gpu::gles2::GLES2Interface* dst_gl,
+ GLenum dst_texture_target,
+ GLuint dst_texture,
+ bool premultiply_alpha,
+ bool flip_y,
+ const IntPoint& dst_texture_offset,
+ const IntRect& src_sub_rectangle,
+ SourceDrawingBuffer src_buffer) {
+ ScopedStateRestorer scoped_state_restorer(this);
+
+ gpu::gles2::GLES2Interface* src_gl = gl_;
+
+ if (contents_changed_) {
+ ResolveIfNeeded();
+ src_gl->Flush();
+ }
+
+ if (!Extensions3DUtil::CanUseCopyTextureCHROMIUM(dst_texture_target))
+ return false;
+
+ // Contexts may be in a different share group. We must transfer the texture
+ // through a mailbox first.
+ gpu::Mailbox mailbox;
+ gpu::SyncToken produce_sync_token;
+ if (src_buffer == kFrontBuffer && front_color_buffer_) {
+ mailbox = front_color_buffer_->mailbox;
+ produce_sync_token = front_color_buffer_->produce_sync_token;
+ } else {
+ src_gl->GenMailboxCHROMIUM(mailbox.name);
+ if (premultiplied_alpha_false_texture_) {
+ // If this texture exists, then it holds the rendering results at this
+ // point, rather than back_color_buffer_. back_color_buffer_ receives the
+ // contents of this texture later, premultiplying alpha into the color
+ // channels.
+ src_gl->ProduceTextureDirectCHROMIUM(premultiplied_alpha_false_texture_,
+ mailbox.name);
+ } else {
+ src_gl->ProduceTextureDirectCHROMIUM(back_color_buffer_->texture_id,
+ mailbox.name);
+ }
+ src_gl->GenUnverifiedSyncTokenCHROMIUM(produce_sync_token.GetData());
+ }
+
+ if (!produce_sync_token.HasData()) {
+ // This should only happen if the context has been lost.
+ return false;
+ }
+
+ dst_gl->WaitSyncTokenCHROMIUM(produce_sync_token.GetConstData());
+ GLuint src_texture = dst_gl->CreateAndConsumeTextureCHROMIUM(mailbox.name);
+
+ GLboolean unpack_premultiply_alpha_needed = GL_FALSE;
+ GLboolean unpack_unpremultiply_alpha_needed = GL_FALSE;
+ if (want_alpha_channel_ && premultiplied_alpha_ && !premultiply_alpha)
+ unpack_unpremultiply_alpha_needed = GL_TRUE;
+ else if (want_alpha_channel_ && !premultiplied_alpha_ && premultiply_alpha)
+ unpack_premultiply_alpha_needed = GL_TRUE;
+
+ dst_gl->CopySubTextureCHROMIUM(
+ src_texture, 0, dst_texture_target, dst_texture, 0,
+ dst_texture_offset.X(), dst_texture_offset.Y(), src_sub_rectangle.X(),
+ src_sub_rectangle.Y(), src_sub_rectangle.Width(),
+ src_sub_rectangle.Height(), flip_y, unpack_premultiply_alpha_needed,
+ unpack_unpremultiply_alpha_needed);
+
+ dst_gl->DeleteTextures(1, &src_texture);
+
+ gpu::SyncToken sync_token;
+ dst_gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
+ src_gl->WaitSyncTokenCHROMIUM(sync_token.GetData());
+
+ return true;
+}
+
+WebLayer* DrawingBuffer::PlatformLayer() {
+ if (!layer_) {
+ layer_ =
+ Platform::Current()->CompositorSupport()->CreateExternalTextureLayer(
+ this);
+
+ layer_->SetOpaque(!want_alpha_channel_);
+ layer_->SetBlendBackgroundColor(want_alpha_channel_);
+ // If premultiplied_alpha_false_texture_ exists, then premultiplied_alpha_
+ // has already been handled via CopySubTextureCHROMIUM -- the alpha channel
+ // has been multiplied into the color channels. In this case, or if
+ // premultiplied_alpha_ is true, then the layer should consider its contents
+ // to be premultiplied.
+ //
+ // The only situation where the layer should consider its contents
+ // un-premultiplied is when premultiplied_alpha_ is false, and
+ // premultiplied_alpha_false_texture_ does not exist.
+ DCHECK(!(premultiplied_alpha_ && premultiplied_alpha_false_texture_));
+ layer_->SetPremultipliedAlpha(premultiplied_alpha_ ||
+ premultiplied_alpha_false_texture_);
+ layer_->SetNearestNeighbor(filter_quality_ == kNone_SkFilterQuality);
+ GraphicsLayer::RegisterContentsLayer(layer_->Layer());
+ }
+
+ return layer_->Layer();
+}
+
+void DrawingBuffer::ClearPlatformLayer() {
+ if (layer_)
+ layer_->ClearTexture();
+
+ gl_->Flush();
+}
+
+void DrawingBuffer::BeginDestruction() {
+ DCHECK(!destruction_in_progress_);
+ destruction_in_progress_ = true;
+
+ ClearPlatformLayer();
+ recycled_color_buffer_queue_.clear();
+
+ // If the drawing buffer is being destroyed due to a real context loss these
+ // calls will be ineffective, but won't be harmful.
+ if (multisample_fbo_)
+ gl_->DeleteFramebuffers(1, &multisample_fbo_);
+
+ if (fbo_)
+ gl_->DeleteFramebuffers(1, &fbo_);
+
+ if (multisample_renderbuffer_)
+ gl_->DeleteRenderbuffers(1, &multisample_renderbuffer_);
+
+ if (depth_stencil_buffer_)
+ gl_->DeleteRenderbuffers(1, &depth_stencil_buffer_);
+
+ if (premultiplied_alpha_false_texture_)
+ gl_->DeleteTextures(1, &premultiplied_alpha_false_texture_);
+
+ size_ = IntSize();
+
+ back_color_buffer_ = nullptr;
+ front_color_buffer_ = nullptr;
+ multisample_renderbuffer_ = 0;
+ depth_stencil_buffer_ = 0;
+ premultiplied_alpha_false_texture_ = 0;
+ multisample_fbo_ = 0;
+ fbo_ = 0;
+
+ if (layer_)
+ GraphicsLayer::UnregisterContentsLayer(layer_->Layer());
+
+ client_ = nullptr;
+}
+
+bool DrawingBuffer::ResizeDefaultFramebuffer(const IntSize& size) {
+ DCHECK(state_restorer_);
+ // Recreate m_backColorBuffer.
+ back_color_buffer_ = CreateColorBuffer(size);
+
+ // Most OS compositors assume GpuMemoryBuffers contain premultiplied-alpha
+ // content. If the user created the context with premultipliedAlpha:false and
+ // GpuMemoryBuffers are being used, allocate a non-GMB texture which will hold
+ // the non-premultiplied rendering results. These will be copied into the GMB
+ // via CopySubTextureCHROMIUM, performing the premultiplication step then.
+ if (ShouldUseChromiumImage() && allocate_alpha_channel_ &&
+ !premultiplied_alpha_) {
+ state_restorer_->SetTextureBindingDirty();
+ // TODO(kbr): unify with code in CreateColorBuffer.
+ if (premultiplied_alpha_false_texture_) {
+ gl_->DeleteTextures(1, &premultiplied_alpha_false_texture_);
+ premultiplied_alpha_false_texture_ = 0;
+ }
+ gl_->GenTextures(1, &premultiplied_alpha_false_texture_);
+ // The command decoder forbids allocating "real" OpenGL textures with the
+ // GL_TEXTURE_RECTANGLE_ARB target. Allocate this temporary texture with
+ // type GL_TEXTURE_2D all the time. CopySubTextureCHROMIUM can handle
+ // copying between 2D and rectangular textures.
+ gl_->BindTexture(GL_TEXTURE_2D, premultiplied_alpha_false_texture_);
+ if (storage_texture_supported_) {
+ GLenum internal_storage_format = GL_RGBA8;
+ if (use_half_float_storage_) {
+ internal_storage_format = GL_RGBA16F_EXT;
+ }
+ gl_->TexStorage2DEXT(GL_TEXTURE_2D, 1, internal_storage_format,
+ size.Width(), size.Height());
+ } else {
+ GLenum internal_format = GL_RGBA;
+ GLenum format = internal_format;
+ GLenum data_type = GL_UNSIGNED_BYTE;
+ if (use_half_float_storage_) {
+ if (webgl_version_ > kWebGL1) {
+ internal_format = GL_RGBA16F;
+ data_type = GL_HALF_FLOAT;
+ } else {
+ internal_format = GL_RGBA;
+ data_type = GL_HALF_FLOAT_OES;
+ }
+ }
+ gl_->TexImage2D(GL_TEXTURE_2D, 0, internal_format, size.Width(),
+ size.Height(), 0, format, data_type, nullptr);
+ }
+ }
+
+ AttachColorBufferToReadFramebuffer();
+
+ if (WantExplicitResolve()) {
+ state_restorer_->SetFramebufferBindingDirty();
+ state_restorer_->SetRenderbufferBindingDirty();
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, multisample_fbo_);
+ gl_->BindRenderbuffer(GL_RENDERBUFFER, multisample_renderbuffer_);
+ // Note that the multisample rendertarget will allocate an alpha channel
+ // based on |have_alpha_channel_|, not |allocate_alpha_channel_|, since it
+ // will resolve into the ColorBuffer.
+ GLenum internal_format = have_alpha_channel_ ? GL_RGBA8_OES : GL_RGB8_OES;
+ if (use_half_float_storage_) {
+ DCHECK(want_alpha_channel_);
+ internal_format = GL_RGBA16F_EXT;
+ }
+ gl_->RenderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER, sample_count_,
+ internal_format, size.Width(),
+ size.Height());
+
+ if (gl_->GetError() == GL_OUT_OF_MEMORY)
+ return false;
+
+ gl_->FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_RENDERBUFFER, multisample_renderbuffer_);
+ }
+
+ if (WantDepthOrStencil()) {
+ state_restorer_->SetFramebufferBindingDirty();
+ state_restorer_->SetRenderbufferBindingDirty();
+ gl_->BindFramebuffer(GL_FRAMEBUFFER,
+ multisample_fbo_ ? multisample_fbo_ : fbo_);
+ if (!depth_stencil_buffer_)
+ gl_->GenRenderbuffers(1, &depth_stencil_buffer_);
+ gl_->BindRenderbuffer(GL_RENDERBUFFER, depth_stencil_buffer_);
+ if (anti_aliasing_mode_ == kMSAAImplicitResolve) {
+ gl_->RenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, sample_count_,
+ GL_DEPTH24_STENCIL8_OES,
+ size.Width(), size.Height());
+ } else if (anti_aliasing_mode_ == kMSAAExplicitResolve) {
+ gl_->RenderbufferStorageMultisampleCHROMIUM(
+ GL_RENDERBUFFER, sample_count_, GL_DEPTH24_STENCIL8_OES, size.Width(),
+ size.Height());
+ } else {
+ gl_->RenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES,
+ size.Width(), size.Height());
+ }
+ // For ES 2.0 contexts DEPTH_STENCIL is not available natively, so we
+ // emulate
+ // it at the command buffer level for WebGL contexts.
+ gl_->FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
+ GL_RENDERBUFFER, depth_stencil_buffer_);
+ gl_->BindRenderbuffer(GL_RENDERBUFFER, 0);
+ }
+
+ if (WantExplicitResolve()) {
+ state_restorer_->SetFramebufferBindingDirty();
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, multisample_fbo_);
+ if (gl_->CheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
+ return false;
+ }
+
+ state_restorer_->SetFramebufferBindingDirty();
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
+ return gl_->CheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE;
+}
+
+void DrawingBuffer::ClearFramebuffers(GLbitfield clear_mask) {
+ ScopedStateRestorer scoped_state_restorer(this);
+ ClearFramebuffersInternal(clear_mask);
+}
+
+void DrawingBuffer::ClearFramebuffersInternal(GLbitfield clear_mask) {
+ DCHECK(state_restorer_);
+ state_restorer_->SetFramebufferBindingDirty();
+ // We will clear the multisample FBO, but we also need to clear the
+ // non-multisampled buffer.
+ if (multisample_fbo_) {
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
+ gl_->Clear(GL_COLOR_BUFFER_BIT);
+ }
+
+ gl_->BindFramebuffer(GL_FRAMEBUFFER,
+ multisample_fbo_ ? multisample_fbo_ : fbo_);
+ gl_->Clear(clear_mask);
+}
+
+IntSize DrawingBuffer::AdjustSize(const IntSize& desired_size,
+ const IntSize& cur_size,
+ int max_texture_size) {
+ IntSize adjusted_size = desired_size;
+
+ // Clamp if the desired size is greater than the maximum texture size for the
+ // device.
+ if (adjusted_size.Height() > max_texture_size)
+ adjusted_size.SetHeight(max_texture_size);
+
+ if (adjusted_size.Width() > max_texture_size)
+ adjusted_size.SetWidth(max_texture_size);
+
+ return adjusted_size;
+}
+
+bool DrawingBuffer::Resize(const IntSize& new_size) {
+ ScopedStateRestorer scoped_state_restorer(this);
+ return ResizeFramebufferInternal(new_size);
+}
+
+bool DrawingBuffer::ResizeFramebufferInternal(const IntSize& new_size) {
+ DCHECK(state_restorer_);
+ DCHECK(!new_size.IsEmpty());
+ IntSize adjusted_size = AdjustSize(new_size, size_, max_texture_size_);
+ if (adjusted_size.IsEmpty())
+ return false;
+
+ if (adjusted_size != size_) {
+ do {
+ if (!ResizeDefaultFramebuffer(adjusted_size)) {
+ adjusted_size.Scale(kResourceAdjustedRatio);
+ continue;
+ }
+ break;
+ } while (!adjusted_size.IsEmpty());
+
+ size_ = adjusted_size;
+ // Free all mailboxes, because they are now of the wrong size. Only the
+ // first call in this loop has any effect.
+ recycled_color_buffer_queue_.clear();
+ recycled_bitmaps_.clear();
+
+ if (adjusted_size.IsEmpty())
+ return false;
+ }
+
+ state_restorer_->SetClearStateDirty();
+ gl_->Disable(GL_SCISSOR_TEST);
+ gl_->ClearColor(0, 0, 0,
+ DefaultBufferRequiresAlphaChannelToBePreserved() ? 1 : 0);
+ gl_->ColorMask(true, true, true, true);
+
+ GLbitfield clear_mask = GL_COLOR_BUFFER_BIT;
+ if (!!depth_stencil_buffer_) {
+ gl_->ClearDepthf(1.0f);
+ clear_mask |= GL_DEPTH_BUFFER_BIT;
+ gl_->DepthMask(true);
+ }
+ if (!!depth_stencil_buffer_) {
+ gl_->ClearStencil(0);
+ clear_mask |= GL_STENCIL_BUFFER_BIT;
+ gl_->StencilMaskSeparate(GL_FRONT, 0xFFFFFFFF);
+ }
+
+ ClearFramebuffersInternal(clear_mask);
+ return true;
+}
+
+void DrawingBuffer::ResolveAndBindForReadAndDraw() {
+ {
+ ScopedStateRestorer scoped_state_restorer(this);
+ ResolveIfNeeded();
+ }
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
+}
+
+void DrawingBuffer::ResolveMultisampleFramebufferInternal() {
+ DCHECK(state_restorer_);
+ state_restorer_->SetFramebufferBindingDirty();
+ if (WantExplicitResolve()) {
+ state_restorer_->SetClearStateDirty();
+ gl_->BindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, multisample_fbo_);
+ gl_->BindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, fbo_);
+ gl_->Disable(GL_SCISSOR_TEST);
+
+ int width = size_.Width();
+ int height = size_.Height();
+ // Use NEAREST, because there is no scale performed during the blit.
+ GLuint filter = GL_NEAREST;
+
+ gl_->BlitFramebufferCHROMIUM(0, 0, width, height, 0, 0, width, height,
+ GL_COLOR_BUFFER_BIT, filter);
+
+ // On old AMD GPUs on OS X, glColorMask doesn't work correctly for
+ // multisampled renderbuffers and the alpha channel can be overwritten.
+ // Clear the alpha channel of |m_fbo|.
+ if (DefaultBufferRequiresAlphaChannelToBePreserved() &&
+ ContextProvider()->GetGpuFeatureInfo().IsWorkaroundEnabled(
+ gpu::DISABLE_MULTISAMPLING_COLOR_MASK_USAGE)) {
+ gl_->ClearColor(0, 0, 0, 1);
+ gl_->ColorMask(false, false, false, true);
+ gl_->Clear(GL_COLOR_BUFFER_BIT);
+ }
+ }
+
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
+ if (anti_aliasing_mode_ == kScreenSpaceAntialiasing)
+ gl_->ApplyScreenSpaceAntialiasingCHROMIUM();
+}
+
+void DrawingBuffer::ResolveIfNeeded() {
+ if (anti_aliasing_mode_ != kNone && !contents_change_resolved_)
+ ResolveMultisampleFramebufferInternal();
+ contents_change_resolved_ = true;
+}
+
+void DrawingBuffer::RestoreFramebufferBindings() {
+ client_->DrawingBufferClientRestoreFramebufferBinding();
+}
+
+void DrawingBuffer::RestoreAllState() {
+ client_->DrawingBufferClientRestoreScissorTest();
+ client_->DrawingBufferClientRestoreMaskAndClearValues();
+ client_->DrawingBufferClientRestorePixelPackParameters();
+ client_->DrawingBufferClientRestoreTexture2DBinding();
+ client_->DrawingBufferClientRestoreRenderbufferBinding();
+ client_->DrawingBufferClientRestoreFramebufferBinding();
+ client_->DrawingBufferClientRestorePixelUnpackBufferBinding();
+ client_->DrawingBufferClientRestorePixelPackBufferBinding();
+}
+
+bool DrawingBuffer::Multisample() const {
+ return anti_aliasing_mode_ != kNone;
+}
+
+void DrawingBuffer::Bind(GLenum target) {
+ gl_->BindFramebuffer(target, WantExplicitResolve() ? multisample_fbo_ : fbo_);
+}
+
+scoped_refptr<Uint8Array> DrawingBuffer::PaintRenderingResultsToDataArray(
+ SourceDrawingBuffer source_buffer) {
+ ScopedStateRestorer scoped_state_restorer(this);
+
+ int width = Size().Width();
+ int height = Size().Height();
+
+ CheckedNumeric<int> data_size = 4;
+ data_size *= width;
+ data_size *= height;
+ if (RuntimeEnabledFeatures::CanvasColorManagementEnabled() &&
+ use_half_float_storage_) {
+ data_size *= 2;
+ }
+ if (!data_size.IsValid())
+ return nullptr;
+
+ unsigned byte_length = width * height * 4;
+ if (RuntimeEnabledFeatures::CanvasColorManagementEnabled() &&
+ use_half_float_storage_) {
+ byte_length *= 2;
+ }
+ scoped_refptr<ArrayBuffer> dst_buffer =
+ ArrayBuffer::CreateOrNull(byte_length, 1);
+ if (!dst_buffer)
+ return nullptr;
+ scoped_refptr<Uint8Array> data_array =
+ Uint8Array::Create(std::move(dst_buffer), 0, byte_length);
+ if (!data_array)
+ return nullptr;
+
+ GLuint fbo = 0;
+ state_restorer_->SetFramebufferBindingDirty();
+ if (source_buffer == kFrontBuffer && front_color_buffer_) {
+ gl_->GenFramebuffers(1, &fbo);
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo);
+ gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ texture_target_, front_color_buffer_->texture_id,
+ 0);
+ } else {
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
+ }
+
+ ReadBackFramebuffer(static_cast<unsigned char*>(data_array->Data()), width,
+ height, kReadbackRGBA,
+ WebGLImageConversion::kAlphaDoNothing);
+ FlipVertically(static_cast<uint8_t*>(data_array->Data()), width, height);
+
+ if (fbo) {
+ gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ texture_target_, 0, 0);
+ gl_->DeleteFramebuffers(1, &fbo);
+ }
+
+ return data_array;
+}
+
+void DrawingBuffer::ReadBackFramebuffer(unsigned char* pixels,
+ int width,
+ int height,
+ ReadbackOrder readback_order,
+ WebGLImageConversion::AlphaOp op) {
+ DCHECK(state_restorer_);
+ state_restorer_->SetPixelPackParametersDirty();
+ gl_->PixelStorei(GL_PACK_ALIGNMENT, 1);
+ if (webgl_version_ > kWebGL1) {
+ gl_->PixelStorei(GL_PACK_SKIP_ROWS, 0);
+ gl_->PixelStorei(GL_PACK_SKIP_PIXELS, 0);
+ gl_->PixelStorei(GL_PACK_ROW_LENGTH, 0);
+
+ state_restorer_->SetPixelPackBufferBindingDirty();
+ gl_->BindBuffer(GL_PIXEL_PACK_BUFFER, 0);
+ }
+
+ GLenum data_type = GL_UNSIGNED_BYTE;
+ if (RuntimeEnabledFeatures::CanvasColorManagementEnabled() &&
+ use_half_float_storage_) {
+ if (webgl_version_ > kWebGL1)
+ data_type = GL_HALF_FLOAT;
+ else
+ data_type = GL_HALF_FLOAT_OES;
+ }
+ gl_->ReadPixels(0, 0, width, height, GL_RGBA, data_type, pixels);
+
+ size_t buffer_size = 4 * width * height;
+ if (data_type != GL_UNSIGNED_BYTE)
+ buffer_size *= 2;
+ // For half float storage Skia order is RGBA, hence no swizzling is needed.
+ if (readback_order == kReadbackSkia && data_type == GL_UNSIGNED_BYTE) {
+#if (SK_R32_SHIFT == 16) && !SK_B32_SHIFT
+ // Swizzle red and blue channels to match SkBitmap's byte ordering.
+ // TODO(kbr): expose GL_BGRA as extension.
+ for (size_t i = 0; i < buffer_size; i += 4) {
+ std::swap(pixels[i], pixels[i + 2]);
+ }
+#endif
+ }
+
+ if (op == WebGLImageConversion::kAlphaDoPremultiply) {
+ std::unique_ptr<SkColorSpaceXform> xform =
+ SkColorSpaceXform::New(SkColorSpace::MakeSRGBLinear().get(),
+ SkColorSpace::MakeSRGBLinear().get());
+ SkColorSpaceXform::ColorFormat color_format =
+ SkColorSpaceXform::ColorFormat::kRGBA_8888_ColorFormat;
+ if (data_type != GL_UNSIGNED_BYTE)
+ color_format = SkColorSpaceXform::ColorFormat::kRGBA_F16_ColorFormat;
+ xform->apply(color_format, pixels, color_format, pixels, width * height,
+ kPremul_SkAlphaType);
+ } else if (op != WebGLImageConversion::kAlphaDoNothing) {
+ NOTREACHED();
+ }
+}
+
+void DrawingBuffer::FlipVertically(uint8_t* framebuffer,
+ int width,
+ int height) {
+ unsigned row_bytes = width * 4;
+ if (RuntimeEnabledFeatures::CanvasColorManagementEnabled() &&
+ use_half_float_storage_) {
+ row_bytes *= 2;
+ }
+ std::vector<uint8_t> scanline(row_bytes);
+ unsigned count = height / 2;
+ for (unsigned i = 0; i < count; i++) {
+ uint8_t* row_a = framebuffer + i * row_bytes;
+ uint8_t* row_b = framebuffer + (height - i - 1) * row_bytes;
+ memcpy(scanline.data(), row_b, row_bytes);
+ memcpy(row_b, row_a, row_bytes);
+ memcpy(row_a, scanline.data(), row_bytes);
+ }
+}
+
+scoped_refptr<DrawingBuffer::ColorBuffer> DrawingBuffer::CreateColorBuffer(
+ const IntSize& size) {
+ DCHECK(state_restorer_);
+ state_restorer_->SetFramebufferBindingDirty();
+ state_restorer_->SetTextureBindingDirty();
+
+ // Select the parameters for the texture object. Allocate the backing
+ // GpuMemoryBuffer and GLImage, if one is going to be used.
+ GLuint image_id = 0;
+ std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer;
+ gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager =
+ Platform::Current()->GetGpuMemoryBufferManager();
+ if (ShouldUseChromiumImage()) {
+ gfx::BufferFormat buffer_format;
+ GLenum gl_format = GL_NONE;
+ if (allocate_alpha_channel_) {
+ buffer_format = use_half_float_storage_ ? gfx::BufferFormat::RGBA_F16
+ : gfx::BufferFormat::RGBA_8888;
+ gl_format = GL_RGBA;
+ } else {
+ DCHECK(!use_half_float_storage_);
+ buffer_format = gfx::BufferFormat::RGBX_8888;
+ if (gpu::IsImageFromGpuMemoryBufferFormatSupported(
+ gfx::BufferFormat::BGRX_8888,
+ ContextProvider()->GetCapabilities()))
+ buffer_format = gfx::BufferFormat::BGRX_8888;
+ gl_format = GL_RGB;
+ }
+ gpu_memory_buffer = gpu_memory_buffer_manager->CreateGpuMemoryBuffer(
+ gfx::Size(size), buffer_format, gfx::BufferUsage::SCANOUT,
+ gpu::kNullSurfaceHandle);
+ if (gpu_memory_buffer) {
+ gpu_memory_buffer->SetColorSpace(storage_color_space_);
+ image_id =
+ gl_->CreateImageCHROMIUM(gpu_memory_buffer->AsClientBuffer(),
+ size.Width(), size.Height(), gl_format);
+ if (!image_id)
+ gpu_memory_buffer.reset();
+ }
+ }
+
+ // Allocate the texture for this object.
+ GLuint texture_id = 0;
+ {
+ gl_->GenTextures(1, &texture_id);
+ gl_->BindTexture(texture_target_, texture_id);
+ gl_->TexParameteri(texture_target_, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ gl_->TexParameteri(texture_target_, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ gl_->TexParameteri(texture_target_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ gl_->TexParameteri(texture_target_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ }
+
+ // If this is GpuMemoryBuffer-backed, then bind the texture to the
+ // GpuMemoryBuffer's GLImage. Otherwise, allocate ordinary texture storage.
+ if (image_id) {
+ gl_->BindTexImage2DCHROMIUM(texture_target_, image_id);
+ } else {
+ if (storage_texture_supported_) {
+ GLenum internal_storage_format =
+ allocate_alpha_channel_ ? GL_RGBA8 : GL_RGB8;
+ if (use_half_float_storage_) {
+ DCHECK(want_alpha_channel_);
+ internal_storage_format = GL_RGBA16F_EXT;
+ }
+ gl_->TexStorage2DEXT(GL_TEXTURE_2D, 1, internal_storage_format,
+ size.Width(), size.Height());
+ } else {
+ GLenum internal_format = allocate_alpha_channel_ ? GL_RGBA : GL_RGB;
+ GLenum format = internal_format;
+ GLenum data_type = GL_UNSIGNED_BYTE;
+ if (use_half_float_storage_) {
+ DCHECK(want_alpha_channel_);
+ if (webgl_version_ > kWebGL1) {
+ internal_format = GL_RGBA16F;
+ data_type = GL_HALF_FLOAT;
+ } else {
+ internal_format = GL_RGBA;
+ data_type = GL_HALF_FLOAT_OES;
+ }
+ }
+ gl_->TexImage2D(texture_target_, 0, internal_format, size.Width(),
+ size.Height(), 0, format, data_type, nullptr);
+ }
+ }
+
+ // Clear the alpha channel if this is RGB emulated.
+ if (image_id && !want_alpha_channel_ && have_alpha_channel_) {
+ GLuint fbo = 0;
+
+ state_restorer_->SetClearStateDirty();
+ gl_->GenFramebuffers(1, &fbo);
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo);
+ gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ texture_target_, texture_id, 0);
+ gl_->ClearColor(0, 0, 0, 1);
+ gl_->ColorMask(false, false, false, true);
+ gl_->Disable(GL_SCISSOR_TEST);
+ gl_->Clear(GL_COLOR_BUFFER_BIT);
+ gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ texture_target_, 0, 0);
+ gl_->DeleteFramebuffers(1, &fbo);
+ }
+
+ return base::AdoptRef(new ColorBuffer(this, size, texture_id, image_id,
+ std::move(gpu_memory_buffer)));
+}
+
+void DrawingBuffer::AttachColorBufferToReadFramebuffer() {
+ DCHECK(state_restorer_);
+ state_restorer_->SetFramebufferBindingDirty();
+ state_restorer_->SetTextureBindingDirty();
+
+ gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
+
+ GLenum id = 0;
+ GLenum texture_target = 0;
+
+ if (premultiplied_alpha_false_texture_) {
+ id = premultiplied_alpha_false_texture_;
+ texture_target = GL_TEXTURE_2D;
+ } else {
+ id = back_color_buffer_->texture_id;
+ texture_target = texture_target_;
+ }
+
+ gl_->BindTexture(texture_target, id);
+
+ if (anti_aliasing_mode_ == kMSAAImplicitResolve) {
+ gl_->FramebufferTexture2DMultisampleEXT(
+ GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture_target, id, 0,
+ sample_count_);
+ } else {
+ gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ texture_target, id, 0);
+ }
+}
+
+bool DrawingBuffer::WantExplicitResolve() {
+ return anti_aliasing_mode_ == kMSAAExplicitResolve;
+}
+
+bool DrawingBuffer::WantDepthOrStencil() {
+ return want_depth_ || want_stencil_;
+}
+
+bool DrawingBuffer::SetupRGBEmulationForBlitFramebuffer(
+ bool is_user_draw_framebuffer_bound) {
+ // We only need to do this work if:
+ // - We are blitting to the default framebuffer
+ // - The user has selected alpha:false and antialias:false
+ // - We are using CHROMIUM_image with RGB emulation
+ // macOS is the only platform on which this is necessary.
+
+ if (is_user_draw_framebuffer_bound) {
+ return false;
+ }
+
+ if (anti_aliasing_mode_ != kNone)
+ return false;
+
+ bool has_emulated_rgb = !allocate_alpha_channel_ && have_alpha_channel_;
+ if (!has_emulated_rgb)
+ return false;
+
+ // If for some reason the back buffer doesn't exist or doesn't have a
+ // CHROMIUM_image, don't proceed with this workaround.
+ if (!back_color_buffer_ || !back_color_buffer_->image_id)
+ return false;
+
+ // Before allowing the BlitFramebuffer call to go through, it's necessary
+ // to swap out the RGBA texture that's bound to the CHROMIUM_image
+ // instance with an RGB texture. BlitFramebuffer requires the internal
+ // formats of the source and destination to match when doing a
+ // multisample resolve, and the best way to achieve this without adding
+ // more full-screen blits is to hook up a true RGB texture to the
+ // underlying IOSurface. Unfortunately, on macOS, this rendering path
+ // destroys the alpha channel and requires a fixup afterward, which is
+ // why it isn't used all the time.
+
+ GLuint rgb_texture = back_color_buffer_->rgb_workaround_texture_id;
+ DCHECK_EQ(texture_target_, GC3D_TEXTURE_RECTANGLE_ARB);
+ if (!rgb_texture) {
+ gl_->GenTextures(1, &rgb_texture);
+ gl_->BindTexture(texture_target_, rgb_texture);
+ gl_->TexParameteri(texture_target_, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ gl_->TexParameteri(texture_target_, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ gl_->TexParameteri(texture_target_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ gl_->TexParameteri(texture_target_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ // Bind this texture to the CHROMIUM_image instance that the color
+ // buffer owns. This is an expensive operation, so it's important that
+ // the result be cached.
+ gl_->BindTexImage2DWithInternalformatCHROMIUM(texture_target_, GL_RGB,
+ back_color_buffer_->image_id);
+ back_color_buffer_->rgb_workaround_texture_id = rgb_texture;
+ }
+
+ gl_->FramebufferTexture2D(GL_DRAW_FRAMEBUFFER_ANGLE, GL_COLOR_ATTACHMENT0,
+ texture_target_, rgb_texture, 0);
+ return true;
+}
+
+void DrawingBuffer::CleanupRGBEmulationForBlitFramebuffer() {
+ // This will only be called if SetupRGBEmulationForBlitFramebuffer was.
+ // Put the framebuffer back the way it was, and clear the alpha channel.
+ DCHECK(back_color_buffer_);
+ DCHECK(back_color_buffer_->image_id);
+ gl_->FramebufferTexture2D(GL_DRAW_FRAMEBUFFER_ANGLE, GL_COLOR_ATTACHMENT0,
+ texture_target_, back_color_buffer_->texture_id, 0);
+ // Clear the alpha channel.
+ gl_->ColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
+ gl_->Disable(GL_SCISSOR_TEST);
+ gl_->ClearColor(0, 0, 0, 1);
+ gl_->Clear(GL_COLOR_BUFFER_BIT);
+ DCHECK(client_);
+ client_->DrawingBufferClientRestoreScissorTest();
+ client_->DrawingBufferClientRestoreMaskAndClearValues();
+}
+
+DrawingBuffer::ScopedStateRestorer::ScopedStateRestorer(
+ DrawingBuffer* drawing_buffer)
+ : drawing_buffer_(drawing_buffer) {
+ // If this is a nested restorer, save the previous restorer.
+ previous_state_restorer_ = drawing_buffer->state_restorer_;
+ drawing_buffer_->state_restorer_ = this;
+}
+
+DrawingBuffer::ScopedStateRestorer::~ScopedStateRestorer() {
+ DCHECK_EQ(drawing_buffer_->state_restorer_, this);
+ drawing_buffer_->state_restorer_ = previous_state_restorer_;
+ Client* client = drawing_buffer_->client_;
+ if (!client)
+ return;
+
+ if (clear_state_dirty_) {
+ client->DrawingBufferClientRestoreScissorTest();
+ client->DrawingBufferClientRestoreMaskAndClearValues();
+ }
+ if (pixel_pack_parameters_dirty_)
+ client->DrawingBufferClientRestorePixelPackParameters();
+ if (texture_binding_dirty_)
+ client->DrawingBufferClientRestoreTexture2DBinding();
+ if (renderbuffer_binding_dirty_)
+ client->DrawingBufferClientRestoreRenderbufferBinding();
+ if (framebuffer_binding_dirty_)
+ client->DrawingBufferClientRestoreFramebufferBinding();
+ if (pixel_unpack_buffer_binding_dirty_)
+ client->DrawingBufferClientRestorePixelUnpackBufferBinding();
+ if (pixel_pack_buffer_binding_dirty_)
+ client->DrawingBufferClientRestorePixelPackBufferBinding();
+}
+
+bool DrawingBuffer::ShouldUseChromiumImage() {
+ return RuntimeEnabledFeatures::WebGLImageChromiumEnabled() &&
+ chromium_image_usage_ == kAllowChromiumImage &&
+ Platform::Current()->GetGpuMemoryBufferManager();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h b/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h
new file mode 100644
index 00000000000..e19db7bcc26
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h
@@ -0,0 +1,585 @@
+/*
+ * Copyright (c) 2010, 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_DRAWING_BUFFER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_DRAWING_BUFFER_H_
+
+#include <memory>
+
+#include "cc/layers/texture_layer_client.h"
+#include "cc/resources/cross_thread_shared_bitmap.h"
+#include "cc/resources/shared_bitmap_id_registrar.h"
+#include "gpu/command_buffer/common/mailbox.h"
+#include "gpu/command_buffer/common/sync_token.h"
+#include "third_party/blink/renderer/platform/geometry/int_size.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_types_3d.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/deque.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "third_party/khronos/GLES2/gl2.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/color_space.h"
+
+namespace gfx {
+class GpuMemoryBuffer;
+}
+
+namespace gpu {
+namespace gles2 {
+class GLES2Interface;
+}
+}
+
+namespace blink {
+class CanvasColorParams;
+class Extensions3DUtil;
+class StaticBitmapImage;
+class WebExternalTextureLayer;
+class WebGraphicsContext3DProvider;
+class WebGraphicsContext3DProviderWrapper;
+class WebLayer;
+
+// Manages a rendering target (framebuffer + attachment) for a canvas. Can
+// publish its rendering results to a WebLayer for compositing.
+class PLATFORM_EXPORT DrawingBuffer : public cc::TextureLayerClient,
+ public RefCounted<DrawingBuffer> {
+ WTF_MAKE_NONCOPYABLE(DrawingBuffer);
+
+ public:
+ class Client {
+ public:
+ // Returns true if the DrawingBuffer is currently bound for draw.
+ virtual bool DrawingBufferClientIsBoundForDraw() = 0;
+ virtual void DrawingBufferClientRestoreScissorTest() = 0;
+ // Restores the mask and clear value for color, depth, and stencil buffers.
+ virtual void DrawingBufferClientRestoreMaskAndClearValues() = 0;
+ // Assume client knows the GL/WebGL version and restore necessary params
+ // accordingly.
+ virtual void DrawingBufferClientRestorePixelPackParameters() = 0;
+ // Restores the GL_TEXTURE_2D binding for the active texture unit only.
+ virtual void DrawingBufferClientRestoreTexture2DBinding() = 0;
+ virtual void DrawingBufferClientRestoreRenderbufferBinding() = 0;
+ virtual void DrawingBufferClientRestoreFramebufferBinding() = 0;
+ virtual void DrawingBufferClientRestorePixelUnpackBufferBinding() = 0;
+ virtual void DrawingBufferClientRestorePixelPackBufferBinding() = 0;
+ };
+
+ enum PreserveDrawingBuffer {
+ kPreserve,
+ kDiscard,
+ };
+ enum WebGLVersion {
+ kWebGL1,
+ kWebGL2,
+ };
+
+ enum ChromiumImageUsage {
+ kAllowChromiumImage,
+ kDisallowChromiumImage,
+ };
+
+ static scoped_refptr<DrawingBuffer> Create(
+ std::unique_ptr<WebGraphicsContext3DProvider>,
+ bool using_gpu_compositing,
+ Client*,
+ const IntSize&,
+ bool premultiplied_alpha,
+ bool want_alpha_channel,
+ bool want_depth_buffer,
+ bool want_stencil_buffer,
+ bool want_antialiasing,
+ PreserveDrawingBuffer,
+ WebGLVersion,
+ ChromiumImageUsage,
+ const CanvasColorParams&);
+ static void ForceNextDrawingBufferCreationToFail();
+
+ ~DrawingBuffer() override;
+
+ // Destruction will be completed after all mailboxes are released.
+ void BeginDestruction();
+
+ // Issues a glClear() on all framebuffers associated with this DrawingBuffer.
+ void ClearFramebuffers(GLbitfield clear_mask);
+
+ // Indicates whether the DrawingBuffer internally allocated a packed
+ // depth-stencil renderbuffer in the situation where the end user only asked
+ // for a depth buffer. In this case, we need to upgrade clears of the depth
+ // buffer to clears of the depth and stencil buffers in order to avoid
+ // performance problems on some GPUs.
+ bool HasImplicitStencilBuffer() const { return has_implicit_stencil_buffer_; }
+ bool HasDepthBuffer() const { return !!depth_stencil_buffer_; }
+ bool HasStencilBuffer() const { return !!depth_stencil_buffer_; }
+
+ // Given the desired buffer size, provides the largest dimensions that will
+ // fit in the pixel budget.
+ static IntSize AdjustSize(const IntSize& desired_size,
+ const IntSize& cur_size,
+ int max_texture_size);
+
+ // Resizes (or allocates if necessary) all buffers attached to the default
+ // framebuffer. Returns whether the operation was successful.
+ bool Resize(const IntSize&);
+
+ // Bind the default framebuffer to |target|. |target| must be
+ // GL_FRAMEBUFFER, GL_READ_FRAMEBUFFER, or GL_DRAW_FRAMEBUFFER.
+ void Bind(GLenum target);
+ IntSize Size() const { return size_; }
+
+ // Resolves the multisample color buffer to the normal color buffer and leaves
+ // the resolved color buffer bound to GL_READ_FRAMEBUFFER and
+ // GL_DRAW_FRAMEBUFFER.
+ void ResolveAndBindForReadAndDraw();
+
+ bool Multisample() const;
+
+ bool DiscardFramebufferSupported() const {
+ return discard_framebuffer_supported_;
+ }
+
+ // Returns false if the contents had previously been marked as changed and
+ // have not yet been resolved.
+ bool MarkContentsChanged();
+
+ // Maintenance of auto-clearing of color/depth/stencil buffers. The
+ // Reset method is present to keep calling code simpler, so it
+ // doesn't have to know which buffers were allocated.
+ void ResetBuffersToAutoClear();
+ void SetBuffersToAutoClear(GLbitfield bitmask);
+ GLbitfield GetBuffersToAutoClear() const;
+
+ void SetIsHidden(bool);
+ void SetFilterQuality(SkFilterQuality);
+
+ // Whether the target for draw operations has format GL_RGBA, but is
+ // emulating format GL_RGB. When the target's storage is first
+ // allocated, its alpha channel must be cleared to 1. All future drawing
+ // operations must use a color mask with alpha=GL_FALSE.
+ bool RequiresAlphaChannelToBePreserved();
+
+ // Similar to requiresAlphaChannelToBePreserved(), but always targets the
+ // default framebuffer.
+ bool DefaultBufferRequiresAlphaChannelToBePreserved();
+
+ WebLayer* PlatformLayer();
+
+ gpu::gles2::GLES2Interface* ContextGL();
+ WebGraphicsContext3DProvider* ContextProvider();
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper> ContextProviderWeakPtr();
+ Client* client() { return client_; }
+ WebGLVersion webgl_version() const { return webgl_version_; }
+ bool destroyed() const { return destruction_in_progress_; }
+
+ // cc::TextureLayerClient implementation.
+ bool PrepareTransferableResource(
+ cc::SharedBitmapIdRegistrar* bitmap_registrar,
+ viz::TransferableResource* out_resource,
+ std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback)
+ override;
+
+ // Returns a StaticBitmapImage backed by a texture containing the current
+ // contents of the front buffer. This is done without any pixel copies. The
+ // texture in the ImageBitmap is from the active ContextProvider on the
+ // DrawingBuffer.
+ // If out_release_callback is null, the image is discarded. If it is non-null
+ // the image must be recycled or discarded by calling *out_release_callback.
+ scoped_refptr<StaticBitmapImage> TransferToStaticBitmapImage(
+ std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback);
+
+ bool CopyToPlatformTexture(gpu::gles2::GLES2Interface*,
+ GLenum dst_target,
+ GLuint dst_texture,
+ bool premultiply_alpha,
+ bool flip_y,
+ const IntPoint& dst_texture_offset,
+ const IntRect& src_sub_rectangle,
+ SourceDrawingBuffer);
+
+ scoped_refptr<Uint8Array> PaintRenderingResultsToDataArray(
+ SourceDrawingBuffer);
+
+ int SampleCount() const { return sample_count_; }
+ bool ExplicitResolveOfMultisampleData() const {
+ return anti_aliasing_mode_ == kMSAAExplicitResolve;
+ }
+
+ // Rebind the read and draw framebuffers that WebGL is expecting.
+ void RestoreFramebufferBindings();
+
+ // Restore all state that may have been dirtied by any call.
+ void RestoreAllState();
+
+ // This class helps implement correct semantics for BlitFramebuffer
+ // when the DrawingBuffer is using a CHROMIUM image for its backing
+ // store and RGB emulation is in use (basically, macOS only).
+ class PLATFORM_EXPORT ScopedRGBEmulationForBlitFramebuffer {
+ public:
+ ScopedRGBEmulationForBlitFramebuffer(DrawingBuffer*,
+ bool is_user_draw_framebuffer_bound);
+ ~ScopedRGBEmulationForBlitFramebuffer();
+
+ private:
+ scoped_refptr<DrawingBuffer> drawing_buffer_;
+ bool doing_work_ = false;
+ };
+
+ protected: // For unittests
+ DrawingBuffer(std::unique_ptr<WebGraphicsContext3DProvider>,
+ bool using_gpu_compositing,
+ std::unique_ptr<Extensions3DUtil>,
+ Client*,
+ bool discard_framebuffer_supported,
+ bool want_alpha_channel,
+ bool premultiplied_alpha,
+ PreserveDrawingBuffer,
+ WebGLVersion,
+ bool wants_depth,
+ bool wants_stencil,
+ ChromiumImageUsage,
+ const CanvasColorParams&);
+
+ bool Initialize(const IntSize&, bool use_multisampling);
+
+ struct RegisteredBitmap {
+ scoped_refptr<cc::CrossThreadSharedBitmap> bitmap;
+ cc::SharedBitmapIdRegistration registration;
+
+ // Explicitly move-only.
+ RegisteredBitmap(RegisteredBitmap&&) = default;
+ RegisteredBitmap& operator=(RegisteredBitmap&&) = default;
+ };
+ // Shared memory bitmaps that were released by the compositor and can be used
+ // again by this DrawingBuffer.
+ Vector<RegisteredBitmap> recycled_bitmaps_;
+
+ private:
+ friend class ScopedRGBEmulationForBlitFramebuffer;
+ friend class ScopedStateRestorer;
+ friend class ColorBuffer;
+
+ // This structure should wrap all public entrypoints that may modify GL state.
+ // It will restore all state when it drops out of scope.
+ class ScopedStateRestorer {
+ public:
+ ScopedStateRestorer(DrawingBuffer*);
+ ~ScopedStateRestorer();
+
+ // Mark parts of the state that are dirty and need to be restored.
+ void SetClearStateDirty() { clear_state_dirty_ = true; }
+ void SetPixelPackParametersDirty() { pixel_pack_parameters_dirty_ = true; }
+ void SetTextureBindingDirty() { texture_binding_dirty_ = true; }
+ void SetRenderbufferBindingDirty() { renderbuffer_binding_dirty_ = true; }
+ void SetFramebufferBindingDirty() { framebuffer_binding_dirty_ = true; }
+ void SetPixelUnpackBufferBindingDirty() {
+ pixel_unpack_buffer_binding_dirty_ = true;
+ }
+ void SetPixelPackBufferBindingDirty() {
+ pixel_pack_buffer_binding_dirty_ = true;
+ }
+
+ private:
+ scoped_refptr<DrawingBuffer> drawing_buffer_;
+ // The previous state restorer, in case restorers are nested.
+ ScopedStateRestorer* previous_state_restorer_ = nullptr;
+ bool clear_state_dirty_ = false;
+ bool pixel_pack_parameters_dirty_ = false;
+ bool texture_binding_dirty_ = false;
+ bool renderbuffer_binding_dirty_ = false;
+ bool framebuffer_binding_dirty_ = false;
+ bool pixel_unpack_buffer_binding_dirty_ = false;
+ bool pixel_pack_buffer_binding_dirty_ = false;
+ };
+
+ struct ColorBuffer : public RefCounted<ColorBuffer> {
+ ColorBuffer(DrawingBuffer*,
+ const IntSize&,
+ GLuint texture_id,
+ GLuint image_id,
+ std::unique_ptr<gfx::GpuMemoryBuffer>);
+ ~ColorBuffer();
+
+ // The owning DrawingBuffer. Note that DrawingBuffer is explicitly destroyed
+ // by the beginDestruction method, which will eventually drain all of its
+ // ColorBuffers.
+ scoped_refptr<DrawingBuffer> drawing_buffer;
+ const IntSize size;
+ const GLuint texture_id = 0;
+ const GLuint image_id = 0;
+ std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer;
+
+ // If we're emulating an RGB back buffer using an RGBA Chromium
+ // image (essentially macOS only), then when performing
+ // BlitFramebuffer calls, we have to swap in an RGB texture in
+ // place of the RGBA texture bound to the image. The reason is
+ // that BlitFramebuffer requires the internal formats of the
+ // source and destination to match (e.g. RGB8 on both sides).
+ // There are bugs in the semantics of RGB8 textures in this
+ // situation (the alpha channel is zeroed), requiring more fixups.
+ GLuint rgb_workaround_texture_id = 0;
+
+ // The mailbox used to send this buffer to the compositor.
+ gpu::Mailbox mailbox;
+
+ // The sync token for when this buffer was sent to the compositor.
+ gpu::SyncToken produce_sync_token;
+
+ // The sync token for when this buffer was received back from the
+ // compositor.
+ gpu::SyncToken receive_sync_token;
+
+ private:
+ WTF_MAKE_NONCOPYABLE(ColorBuffer);
+ };
+
+ // The same as clearFramebuffers(), but leaves GL state dirty.
+ void ClearFramebuffersInternal(GLbitfield clear_mask);
+
+ // The same as reset(), but leaves GL state dirty.
+ bool ResizeFramebufferInternal(const IntSize&);
+
+ // The same as resolveAndBindForReadAndDraw(), but leaves GL state dirty.
+ void ResolveMultisampleFramebufferInternal();
+
+ // Resolves m_multisampleFBO into m_fbo, if multisampling.
+ void ResolveIfNeeded();
+
+ bool PrepareTransferableResourceInternal(
+ cc::SharedBitmapIdRegistrar* bitmap_registrar,
+ viz::TransferableResource* out_resource,
+ std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback,
+ bool force_gpu_result);
+
+ // Helper functions to be called only by PrepareTransferableResourceInternal.
+ void FinishPrepareTransferableResourceGpu(
+ viz::TransferableResource* out_resource,
+ std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback);
+ void FinishPrepareTransferableResourceSoftware(
+ cc::SharedBitmapIdRegistrar* bitmap_registrar,
+ viz::TransferableResource* out_resource,
+ std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback);
+
+ // Callbacks for mailboxes given to the compositor from
+ // FinishPrepareTransferableResource{Gpu,Software}.
+ void MailboxReleasedGpu(scoped_refptr<ColorBuffer>,
+ const gpu::SyncToken&,
+ bool lost_resource);
+ void MailboxReleasedSoftware(RegisteredBitmap,
+ const gpu::SyncToken&,
+ bool lost_resource);
+
+ // Attempts to allocator storage for, or resize all buffers. Returns whether
+ // the operation was successful.
+ bool ResizeDefaultFramebuffer(const IntSize&);
+
+ void ClearPlatformLayer();
+
+ RegisteredBitmap CreateOrRecycleBitmap(
+ cc::SharedBitmapIdRegistrar* bitmap_registrar);
+
+ // Updates the current size of the buffer, ensuring that
+ // s_currentResourceUsePixels is updated.
+ void SetSize(const IntSize&);
+
+ // This is the order of bytes to use when doing a readback.
+ enum ReadbackOrder { kReadbackRGBA, kReadbackSkia };
+
+ // Helper function which does a readback from the currently-bound
+ // framebuffer into a buffer of a certain size with 4-byte pixels.
+ void ReadBackFramebuffer(unsigned char* pixels,
+ int width,
+ int height,
+ ReadbackOrder,
+ WebGLImageConversion::AlphaOp);
+
+ // Helper function to flip a bitmap vertically.
+ void FlipVertically(uint8_t* data, int width, int height);
+
+ // If RGB emulation is required, then the CHROMIUM image's alpha channel
+ // must be immediately cleared after it is bound to a texture. Nothing
+ // should be allowed to change the alpha channel after this.
+ void ClearChromiumImageAlpha(const ColorBuffer&);
+
+ // Tries to create a CHROMIUM_image backed texture if
+ // RuntimeEnabledFeatures::WebGLImageChromiumEnabled() is true. On failure,
+ // or if the flag is false, creates a default texture. Always returns a valid
+ // ColorBuffer.
+ scoped_refptr<ColorBuffer> CreateColorBuffer(const IntSize&);
+
+ // Creates or recycles a ColorBuffer of size |m_size|.
+ scoped_refptr<ColorBuffer> CreateOrRecycleColorBuffer();
+
+ // Attaches |m_backColorBuffer| to |m_fbo|, which is always the source for
+ // read operations.
+ void AttachColorBufferToReadFramebuffer();
+
+ // Whether the WebGL client desires an explicit resolve. This is
+ // implemented by forwarding all draw operations to a multisample
+ // renderbuffer, which is resolved before any read operations or swaps.
+ bool WantExplicitResolve();
+
+ // Whether the WebGL client wants a depth or stencil buffer.
+ bool WantDepthOrStencil();
+
+ // Helpers to ensure correct behavior of BlitFramebuffer when using
+ // an emulated RGB CHROMIUM_image back buffer.
+ bool SetupRGBEmulationForBlitFramebuffer(bool is_user_draw_framebuffer_bound);
+ void CleanupRGBEmulationForBlitFramebuffer();
+
+ // Weak, reset by beginDestruction.
+ Client* client_ = nullptr;
+
+ const PreserveDrawingBuffer preserve_drawing_buffer_;
+ const WebGLVersion webgl_version_;
+
+ std::unique_ptr<WebGraphicsContext3DProviderWrapper> context_provider_;
+ // Lifetime is tied to the m_contextProvider.
+ gpu::gles2::GLES2Interface* gl_;
+ std::unique_ptr<Extensions3DUtil> extensions_util_;
+ IntSize size_ = {-1, -1};
+ const bool discard_framebuffer_supported_;
+ // Did the user request an alpha channel be allocated.
+ const bool want_alpha_channel_;
+ // Do we explicitly allocate an alpha channel in our ColorBuffer allocations.
+ // Note that this does not apply to |multisample_renderbuffer_|.
+ bool allocate_alpha_channel_ = false;
+ // Does our allocation have an alpha channel (potentially implicitly created).
+ // Note that this determines if |multisample_renderbuffer_| allocates an alpha
+ // channel.
+ bool have_alpha_channel_ = false;
+ const bool premultiplied_alpha_;
+ const bool using_gpu_compositing_;
+ bool has_implicit_stencil_buffer_ = false;
+ bool storage_texture_supported_ = false;
+
+ // The texture target (2D or RECTANGLE) for our allocations.
+ GLenum texture_target_ = 0;
+
+ // The current state restorer, which is used to track state dirtying. It is an
+ // error to dirty state shared with WebGL while there is no existing state
+ // restorer.
+ ScopedStateRestorer* state_restorer_ = nullptr;
+
+ // This is used when the user requests either a depth or stencil buffer.
+ GLuint depth_stencil_buffer_ = 0;
+
+ // When wantExplicitResolve() returns true, the target of all draw
+ // operations.
+ GLuint multisample_fbo_ = 0;
+
+ // The id of the renderbuffer storage for |m_multisampleFBO|.
+ GLuint multisample_renderbuffer_ = 0;
+
+ // If premultipliedAlpha:false is set during context creation, and a
+ // GpuMemoryBuffer is used for the DrawingBuffer's storage, then a separate,
+ // regular, OpenGL texture is allocated to hold either the rendering results
+ // (if antialias:false) or resolve results (if antialias:true). Then
+ // CopyTextureCHROMIUM is used to multiply the alpha channel into the color
+ // channels when copying into the GMB.
+ GLuint premultiplied_alpha_false_texture_ = 0;
+
+ // When wantExplicitResolve() returns false, the target of all draw and
+ // read operations. When wantExplicitResolve() returns true, the target of
+ // all read operations.
+ GLuint fbo_ = 0;
+
+ // The ColorBuffer that backs |m_fbo|.
+ scoped_refptr<ColorBuffer> back_color_buffer_;
+
+ // The ColorBuffer that was most recently presented to the compositor by
+ // PrepareTransferableResourceInternal.
+ scoped_refptr<ColorBuffer> front_color_buffer_;
+
+ // True if our contents have been modified since the last presentation of this
+ // buffer.
+ bool contents_changed_ = true;
+
+ // True if resolveIfNeeded() has been called since the last time
+ // markContentsChanged() had been called.
+ bool contents_change_resolved_ = false;
+
+ // A bitmask of GL buffer bits (GL_COLOR_BUFFER_BIT,
+ // GL_DEPTH_BUFFER_BIT, GL_STENCIL_BUFFER_BIT) which need to be
+ // auto-cleared.
+ GLbitfield buffers_to_auto_clear_ = 0;
+
+ // Whether the client wants a depth or stencil buffer.
+ const bool want_depth_;
+ const bool want_stencil_;
+
+ // The color space of this buffer's storage, and the color space in which
+ // shader samplers will read this buffer.
+ const gfx::ColorSpace storage_color_space_;
+ const gfx::ColorSpace sampler_color_space_;
+
+ enum AntialiasingMode {
+ kNone,
+ kMSAAImplicitResolve,
+ kMSAAExplicitResolve,
+ kScreenSpaceAntialiasing,
+ };
+
+ AntialiasingMode anti_aliasing_mode_ = kNone;
+
+ bool use_half_float_storage_ = false;
+
+ int max_texture_size_ = 0;
+ int sample_count_ = 0;
+ bool destruction_in_progress_ = false;
+ bool is_hidden_ = false;
+ SkFilterQuality filter_quality_ = kLow_SkFilterQuality;
+
+ std::unique_ptr<WebExternalTextureLayer> layer_;
+
+ // Mailboxes that were released by the compositor can be used again by this
+ // DrawingBuffer.
+ Deque<scoped_refptr<ColorBuffer>> recycled_color_buffer_queue_;
+
+ // If the width and height of the Canvas's backing store don't
+ // match those that we were given in the most recent call to
+ // reshape(), then we need an intermediate bitmap to read back the
+ // frame buffer into. This seems to happen when CSS styles are
+ // used to resize the Canvas.
+ SkBitmap resizing_bitmap_;
+
+ // In the case of OffscreenCanvas, we do not want to enable the
+ // WebGLImageChromium flag, so we replace all the
+ // RuntimeEnabledFeatures::WebGLImageChromiumEnabled() call with
+ // shouldUseChromiumImage() calls, and set m_chromiumImageUsage to
+ // DisallowChromiumImage in the case of OffscreenCanvas.
+ ChromiumImageUsage chromium_image_usage_;
+ bool ShouldUseChromiumImage();
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_DRAWING_BUFFER_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_software_rendering_test.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_software_rendering_test.cc
new file mode 100644
index 00000000000..c8e07ba0c7a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_software_rendering_test.cc
@@ -0,0 +1,117 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h"
+
+#include "cc/resources/shared_bitmap_id_registrar.h"
+#include "components/viz/common/resources/single_release_callback.h"
+#include "components/viz/common/resources/transferable_resource.h"
+#include "gpu/command_buffer/client/gles2_interface_stub.h"
+#include "gpu/config/gpu_feature_info.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test_helpers.h"
+
+// These unit tests are separate from DrawingBufferTests.cpp because they are
+// built as a part of webkit_unittests instead blink_platform_unittests. This is
+// because the software rendering mode has a dependency on the blink::Platform
+// interface for buffer allocations.
+
+namespace blink {
+namespace {
+
+class TestSharedBitmapIdRegistar : public cc::SharedBitmapIdRegistrar {
+ virtual cc::SharedBitmapIdRegistration RegisterSharedBitmapId(
+ const viz::SharedBitmapId& id,
+ scoped_refptr<cc::CrossThreadSharedBitmap> bitmap) {
+ return {};
+ }
+};
+
+class DrawingBufferSoftwareCompositingTest : public testing::Test {
+ protected:
+ void SetUp() override {
+ IntSize initial_size(kInitialWidth, kInitialHeight);
+ auto gl = std::make_unique<GLES2InterfaceForTests>();
+ auto provider =
+ std::make_unique<WebGraphicsContext3DProviderForTests>(std::move(gl));
+ GLES2InterfaceForTests* gl_ =
+ static_cast<GLES2InterfaceForTests*>(provider->ContextGL());
+ bool gpu_compositing = false;
+ drawing_buffer_ = DrawingBufferForTests::Create(
+ std::move(provider), gpu_compositing, gl_, initial_size,
+ DrawingBuffer::kPreserve, kDisableMultisampling);
+ CHECK(drawing_buffer_);
+ }
+
+ scoped_refptr<DrawingBufferForTests> drawing_buffer_;
+ TestSharedBitmapIdRegistar test_shared_bitmap_id_registrar_;
+};
+
+TEST_F(DrawingBufferSoftwareCompositingTest, BitmapRecycling) {
+ viz::TransferableResource resource;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback1;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback2;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback3;
+ IntSize initial_size(kInitialWidth, kInitialHeight);
+ IntSize alternate_size(kInitialWidth, kAlternateHeight);
+
+ drawing_buffer_->Resize(initial_size);
+ drawing_buffer_->MarkContentsChanged();
+ drawing_buffer_->PrepareTransferableResource(
+ &test_shared_bitmap_id_registrar_, &resource,
+ &release_callback1); // create a bitmap.
+ EXPECT_EQ(0, drawing_buffer_->RecycledBitmapCount());
+ release_callback1->Run(
+ gpu::SyncToken(),
+ false /* lostResource */); // release bitmap to the recycling queue
+ EXPECT_EQ(1, drawing_buffer_->RecycledBitmapCount());
+ drawing_buffer_->MarkContentsChanged();
+ drawing_buffer_->PrepareTransferableResource(
+ &test_shared_bitmap_id_registrar_, &resource,
+ &release_callback2); // recycle a bitmap.
+ EXPECT_EQ(0, drawing_buffer_->RecycledBitmapCount());
+ release_callback2->Run(
+ gpu::SyncToken(),
+ false /* lostResource */); // release bitmap to the recycling queue
+ EXPECT_EQ(1, drawing_buffer_->RecycledBitmapCount());
+ drawing_buffer_->Resize(alternate_size);
+ drawing_buffer_->MarkContentsChanged();
+ // Regression test for crbug.com/647896 - Next line must not crash
+ drawing_buffer_->PrepareTransferableResource(
+ &test_shared_bitmap_id_registrar_, &resource,
+ &release_callback3); // cause recycling queue to be purged due to resize
+ EXPECT_EQ(0, drawing_buffer_->RecycledBitmapCount());
+ release_callback3->Run(gpu::SyncToken(), false /* lostResource */);
+ EXPECT_EQ(1, drawing_buffer_->RecycledBitmapCount());
+
+ drawing_buffer_->BeginDestruction();
+}
+
+TEST_F(DrawingBufferSoftwareCompositingTest, FramebufferBinding) {
+ GLES2InterfaceForTests* gl_ = drawing_buffer_->ContextGLForTests();
+ viz::TransferableResource resource;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback;
+ IntSize initial_size(kInitialWidth, kInitialHeight);
+ GLint drawBinding = 0, readBinding = 0;
+
+ GLuint draw_framebuffer_binding = 0xbeef3;
+ GLuint read_framebuffer_binding = 0xbeef4;
+ gl_->BindFramebuffer(GL_DRAW_FRAMEBUFFER, draw_framebuffer_binding);
+ gl_->BindFramebuffer(GL_READ_FRAMEBUFFER, read_framebuffer_binding);
+ gl_->SaveState();
+ drawing_buffer_->Resize(initial_size);
+ drawing_buffer_->MarkContentsChanged();
+ drawing_buffer_->PrepareTransferableResource(
+ &test_shared_bitmap_id_registrar_, &resource, &release_callback);
+ gl_->GetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &drawBinding);
+ gl_->GetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &readBinding);
+ EXPECT_EQ(static_cast<GLint>(draw_framebuffer_binding), drawBinding);
+ EXPECT_EQ(static_cast<GLint>(read_framebuffer_binding), readBinding);
+ release_callback->Run(gpu::SyncToken(), false /* lostResource */);
+
+ drawing_buffer_->BeginDestruction();
+}
+
+} // unnamed namespace
+} // blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test.cc
new file mode 100644
index 00000000000..e4171a1f293
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test.cc
@@ -0,0 +1,744 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h"
+
+#include <memory>
+#include "base/memory/scoped_refptr.h"
+#include "components/viz/common/resources/single_release_callback.h"
+#include "components/viz/common/resources/transferable_resource.h"
+#include "components/viz/test/test_gpu_memory_buffer_manager.h"
+#include "gpu/command_buffer/client/gles2_interface_stub.h"
+#include "gpu/command_buffer/common/mailbox.h"
+#include "gpu/command_buffer/common/sync_token.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/graphics/canvas_color_params.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
+#include "v8/include/v8.h"
+
+using testing::Test;
+using testing::_;
+
+namespace blink {
+
+namespace {
+
+class FakePlatformSupport : public TestingPlatformSupport {
+ gpu::GpuMemoryBufferManager* GetGpuMemoryBufferManager() override {
+ return &test_gpu_memory_buffer_manager_;
+ }
+
+ private:
+ viz::TestGpuMemoryBufferManager test_gpu_memory_buffer_manager_;
+};
+
+} // anonymous namespace
+
+class DrawingBufferTest : public Test {
+ protected:
+ void SetUp() override { Init(kDisableMultisampling); }
+
+ void Init(UseMultisampling use_multisampling) {
+ IntSize initial_size(kInitialWidth, kInitialHeight);
+ auto gl = std::make_unique<GLES2InterfaceForTests>();
+ auto provider =
+ std::make_unique<WebGraphicsContext3DProviderForTests>(std::move(gl));
+ GLES2InterfaceForTests* gl_ =
+ static_cast<GLES2InterfaceForTests*>(provider->ContextGL());
+ bool gpu_compositing = true;
+ drawing_buffer_ = DrawingBufferForTests::Create(
+ std::move(provider), gpu_compositing, gl_, initial_size,
+ DrawingBuffer::kPreserve, use_multisampling);
+ CHECK(drawing_buffer_);
+ SetAndSaveRestoreState(false);
+ }
+
+ // Initialize GL state with unusual values, to verify that they are restored.
+ // The |invert| parameter will reverse all boolean parameters, so that all
+ // values are tested.
+ void SetAndSaveRestoreState(bool invert) {
+ GLES2InterfaceForTests* gl_ = drawing_buffer_->ContextGLForTests();
+ GLboolean scissor_enabled = !invert;
+ GLfloat clear_color[4] = {0.1, 0.2, 0.3, 0.4};
+ GLfloat clear_depth = 0.8;
+ GLint clear_stencil = 37;
+ GLboolean color_mask[4] = {invert, !invert, !invert, invert};
+ GLboolean depth_mask = invert;
+ GLboolean stencil_mask = invert;
+ GLint pack_alignment = 7;
+ GLuint active_texture2d_binding = 0xbeef1;
+ GLuint renderbuffer_binding = 0xbeef2;
+ GLuint draw_framebuffer_binding = 0xbeef3;
+ GLuint read_framebuffer_binding = 0xbeef4;
+ GLuint pixel_unpack_buffer_binding = 0xbeef5;
+
+ if (scissor_enabled)
+ gl_->Enable(GL_SCISSOR_TEST);
+ else
+ gl_->Disable(GL_SCISSOR_TEST);
+
+ gl_->ClearColor(clear_color[0], clear_color[1], clear_color[2],
+ clear_color[3]);
+ gl_->ClearDepthf(clear_depth);
+ gl_->ClearStencil(clear_stencil);
+ gl_->ColorMask(color_mask[0], color_mask[1], color_mask[2], color_mask[3]);
+ gl_->DepthMask(depth_mask);
+ gl_->StencilMask(stencil_mask);
+ gl_->PixelStorei(GL_PACK_ALIGNMENT, pack_alignment);
+ gl_->BindTexture(GL_TEXTURE_2D, active_texture2d_binding);
+ gl_->BindRenderbuffer(GL_RENDERBUFFER, renderbuffer_binding);
+ gl_->BindFramebuffer(GL_DRAW_FRAMEBUFFER, draw_framebuffer_binding);
+ gl_->BindFramebuffer(GL_READ_FRAMEBUFFER, read_framebuffer_binding);
+ gl_->BindBuffer(GL_PIXEL_UNPACK_BUFFER, pixel_unpack_buffer_binding);
+
+ gl_->SaveState();
+ }
+
+ void VerifyStateWasRestored() {
+ GLES2InterfaceForTests* gl_ = drawing_buffer_->ContextGLForTests();
+ gl_->VerifyStateHasNotChangedSinceSave();
+ }
+
+ scoped_refptr<DrawingBufferForTests> drawing_buffer_;
+};
+
+class DrawingBufferTestMultisample : public DrawingBufferTest {
+ protected:
+ void SetUp() override { Init(kEnableMultisampling); }
+};
+
+TEST_F(DrawingBufferTestMultisample, verifyMultisampleResolve) {
+ // Initial state: already marked changed, multisampled
+ EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->ExplicitResolveOfMultisampleData());
+
+ // Resolve the multisample buffer
+ drawing_buffer_->ResolveAndBindForReadAndDraw();
+
+ // After resolve, acknowledge new content
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ // No new content
+ EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
+
+ drawing_buffer_->BeginDestruction();
+}
+
+TEST_F(DrawingBufferTest, VerifyResizingProperlyAffectsResources) {
+ GLES2InterfaceForTests* gl_ = drawing_buffer_->ContextGLForTests();
+ VerifyStateWasRestored();
+ viz::TransferableResource resource;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback;
+
+ IntSize initial_size(kInitialWidth, kInitialHeight);
+ IntSize alternate_size(kInitialWidth, kAlternateHeight);
+
+ // Produce one resource at size 100x100.
+ EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
+ &release_callback));
+ VerifyStateWasRestored();
+ EXPECT_EQ(initial_size, gl_->MostRecentlyProducedSize());
+
+ // Resize to 100x50.
+ drawing_buffer_->Resize(alternate_size);
+ VerifyStateWasRestored();
+ release_callback->Run(gpu::SyncToken(), false /* lostResource */);
+ VerifyStateWasRestored();
+
+ // Produce a resource at this size.
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
+ &release_callback));
+ EXPECT_EQ(alternate_size, gl_->MostRecentlyProducedSize());
+ VerifyStateWasRestored();
+
+ // Reset to initial size.
+ drawing_buffer_->Resize(initial_size);
+ VerifyStateWasRestored();
+ SetAndSaveRestoreState(true);
+ release_callback->Run(gpu::SyncToken(), false /* lostResource */);
+ VerifyStateWasRestored();
+
+ // Prepare another resource and verify that it's the correct size.
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
+ &release_callback));
+ EXPECT_EQ(initial_size, gl_->MostRecentlyProducedSize());
+ VerifyStateWasRestored();
+
+ // Prepare one final resource and verify that it's the correct size.
+ release_callback->Run(gpu::SyncToken(), false /* lostResource */);
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
+ &release_callback));
+ VerifyStateWasRestored();
+ EXPECT_EQ(initial_size, gl_->MostRecentlyProducedSize());
+ release_callback->Run(gpu::SyncToken(), false /* lostResource */);
+ drawing_buffer_->BeginDestruction();
+}
+
+TEST_F(DrawingBufferTest, VerifyDestructionCompleteAfterAllResourceReleased) {
+ bool live = true;
+ drawing_buffer_->live_ = &live;
+
+ viz::TransferableResource resource1;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback1;
+ viz::TransferableResource resource2;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback2;
+ viz::TransferableResource resource3;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback3;
+
+ IntSize initial_size(kInitialWidth, kInitialHeight);
+
+ // Produce resources.
+ EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
+ drawing_buffer_->ClearFramebuffers(GL_STENCIL_BUFFER_BIT);
+ VerifyStateWasRestored();
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource1,
+ &release_callback1));
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ drawing_buffer_->ClearFramebuffers(GL_DEPTH_BUFFER_BIT);
+ VerifyStateWasRestored();
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource2,
+ &release_callback2));
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ drawing_buffer_->ClearFramebuffers(GL_COLOR_BUFFER_BIT);
+ VerifyStateWasRestored();
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource3,
+ &release_callback3));
+
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ release_callback1->Run(gpu::SyncToken(), false /* lostResource */);
+
+ drawing_buffer_->BeginDestruction();
+ ASSERT_EQ(live, true);
+
+ DrawingBufferForTests* raw_pointer = drawing_buffer_.get();
+ drawing_buffer_ = nullptr;
+ ASSERT_EQ(live, true);
+
+ EXPECT_FALSE(raw_pointer->MarkContentsChanged());
+ release_callback2->Run(gpu::SyncToken(), false /* lostResource */);
+ ASSERT_EQ(live, true);
+
+ EXPECT_FALSE(raw_pointer->MarkContentsChanged());
+ release_callback3->Run(gpu::SyncToken(), false /* lostResource */);
+ ASSERT_EQ(live, false);
+}
+
+TEST_F(DrawingBufferTest, verifyDrawingBufferStaysAliveIfResourcesAreLost) {
+ bool live = true;
+ drawing_buffer_->live_ = &live;
+
+ viz::TransferableResource resource1;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback1;
+ viz::TransferableResource resource2;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback2;
+ viz::TransferableResource resource3;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback3;
+
+ EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource1,
+ &release_callback1));
+ VerifyStateWasRestored();
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource2,
+ &release_callback2));
+ VerifyStateWasRestored();
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource3,
+ &release_callback3));
+ VerifyStateWasRestored();
+
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ release_callback1->Run(gpu::SyncToken(), true /* lostResource */);
+ EXPECT_EQ(live, true);
+
+ drawing_buffer_->BeginDestruction();
+ EXPECT_EQ(live, true);
+
+ EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
+ release_callback2->Run(gpu::SyncToken(), false /* lostResource */);
+ EXPECT_EQ(live, true);
+
+ DrawingBufferForTests* raw_ptr = drawing_buffer_.get();
+ drawing_buffer_ = nullptr;
+ EXPECT_EQ(live, true);
+
+ EXPECT_FALSE(raw_ptr->MarkContentsChanged());
+ release_callback3->Run(gpu::SyncToken(), true /* lostResource */);
+ EXPECT_EQ(live, false);
+}
+
+TEST_F(DrawingBufferTest, VerifyOnlyOneRecycledResourceMustBeKept) {
+ viz::TransferableResource resource1;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback1;
+ viz::TransferableResource resource2;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback2;
+ viz::TransferableResource resource3;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback3;
+
+ // Produce resources.
+ EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource1,
+ &release_callback1));
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource2,
+ &release_callback2));
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource3,
+ &release_callback3));
+
+ // Release resources by specific order; 1, 3, 2.
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ release_callback1->Run(gpu::SyncToken(), false /* lostResource */);
+ EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
+ release_callback3->Run(gpu::SyncToken(), false /* lostResource */);
+ EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
+ release_callback2->Run(gpu::SyncToken(), false /* lostResource */);
+
+ // The first recycled resource must be 2. 1 and 3 were deleted by FIFO order
+ // because DrawingBuffer never keeps more than one resource.
+ viz::TransferableResource recycled_resource1;
+ std::unique_ptr<viz::SingleReleaseCallback> recycled_release_callback1;
+ EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(
+ nullptr, &recycled_resource1, &recycled_release_callback1));
+ EXPECT_EQ(resource2.mailbox_holder.mailbox,
+ recycled_resource1.mailbox_holder.mailbox);
+
+ // The second recycled resource must be a new resource.
+ viz::TransferableResource recycled_resource2;
+ std::unique_ptr<viz::SingleReleaseCallback> recycled_release_callback2;
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(
+ nullptr, &recycled_resource2, &recycled_release_callback2));
+ EXPECT_NE(resource1.mailbox_holder.mailbox,
+ recycled_resource2.mailbox_holder.mailbox);
+ EXPECT_NE(resource2.mailbox_holder.mailbox,
+ recycled_resource2.mailbox_holder.mailbox);
+ EXPECT_NE(resource3.mailbox_holder.mailbox,
+ recycled_resource2.mailbox_holder.mailbox);
+
+ recycled_release_callback1->Run(gpu::SyncToken(), false /* lostResource */);
+ recycled_release_callback2->Run(gpu::SyncToken(), false /* lostResource */);
+ drawing_buffer_->BeginDestruction();
+}
+
+TEST_F(DrawingBufferTest, verifyInsertAndWaitSyncTokenCorrectly) {
+ GLES2InterfaceForTests* gl_ = drawing_buffer_->ContextGLForTests();
+ viz::TransferableResource resource;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback;
+
+ // Produce resources.
+ EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_EQ(gpu::SyncToken(), gl_->MostRecentlyWaitedSyncToken());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
+ &release_callback));
+ // PrepareTransferableResource() does not wait for any sync point.
+ EXPECT_EQ(gpu::SyncToken(), gl_->MostRecentlyWaitedSyncToken());
+
+ gpu::SyncToken wait_sync_token;
+ gl_->GenSyncTokenCHROMIUM(wait_sync_token.GetData());
+ release_callback->Run(wait_sync_token, false /* lostResource */);
+ // m_drawingBuffer will wait for the sync point when recycling.
+ EXPECT_EQ(gpu::SyncToken(), gl_->MostRecentlyWaitedSyncToken());
+
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
+ &release_callback));
+ // m_drawingBuffer waits for the sync point when recycling in
+ // PrepareTransferableResource().
+ EXPECT_EQ(wait_sync_token, gl_->MostRecentlyWaitedSyncToken());
+
+ drawing_buffer_->BeginDestruction();
+ gl_->GenSyncTokenCHROMIUM(wait_sync_token.GetData());
+ release_callback->Run(wait_sync_token, false /* lostResource */);
+ // m_drawingBuffer waits for the sync point because the destruction is in
+ // progress.
+ EXPECT_EQ(wait_sync_token, gl_->MostRecentlyWaitedSyncToken());
+}
+
+class DrawingBufferImageChromiumTest : public DrawingBufferTest,
+ private ScopedWebGLImageChromiumForTest {
+ public:
+ DrawingBufferImageChromiumTest() : ScopedWebGLImageChromiumForTest(true) {}
+
+ protected:
+ void SetUp() override {
+ platform_.reset(new ScopedTestingPlatformSupport<FakePlatformSupport>);
+
+ IntSize initial_size(kInitialWidth, kInitialHeight);
+ auto gl = std::make_unique<GLES2InterfaceForTests>();
+ auto provider =
+ std::make_unique<WebGraphicsContext3DProviderForTests>(std::move(gl));
+ GLES2InterfaceForTests* gl_ =
+ static_cast<GLES2InterfaceForTests*>(provider->ContextGL());
+ image_id0_ = gl_->NextImageIdToBeCreated();
+ EXPECT_CALL(*gl_, BindTexImage2DMock(image_id0_)).Times(1);
+ bool gpu_compositing = true;
+ drawing_buffer_ = DrawingBufferForTests::Create(
+ std::move(provider), gpu_compositing, gl_, initial_size,
+ DrawingBuffer::kPreserve, kDisableMultisampling);
+ CHECK(drawing_buffer_);
+ SetAndSaveRestoreState(true);
+ testing::Mock::VerifyAndClearExpectations(gl_);
+ }
+
+ void TearDown() override {
+ platform_.reset();
+ }
+
+ GLuint image_id0_;
+ std::unique_ptr<ScopedTestingPlatformSupport<FakePlatformSupport>> platform_;
+};
+
+TEST_F(DrawingBufferImageChromiumTest, VerifyResizingReallocatesImages) {
+ GLES2InterfaceForTests* gl_ = drawing_buffer_->ContextGLForTests();
+ viz::TransferableResource resource;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback;
+
+ IntSize initial_size(kInitialWidth, kInitialHeight);
+ IntSize alternate_size(kInitialWidth, kAlternateHeight);
+
+ GLuint image_id1 = gl_->NextImageIdToBeCreated();
+ EXPECT_CALL(*gl_, BindTexImage2DMock(image_id1)).Times(1);
+ // Produce one resource at size 100x100.
+ EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
+ &release_callback));
+ EXPECT_EQ(initial_size, gl_->MostRecentlyProducedSize());
+ EXPECT_TRUE(resource.is_overlay_candidate);
+ EXPECT_EQ(initial_size, resource.size);
+ testing::Mock::VerifyAndClearExpectations(gl_);
+ VerifyStateWasRestored();
+
+ GLuint image_id2 = gl_->NextImageIdToBeCreated();
+ EXPECT_CALL(*gl_, BindTexImage2DMock(image_id2)).Times(1);
+ EXPECT_CALL(*gl_, DestroyImageMock(image_id0_)).Times(1);
+ EXPECT_CALL(*gl_, ReleaseTexImage2DMock(image_id0_)).Times(1);
+ EXPECT_CALL(*gl_, DestroyImageMock(image_id1)).Times(1);
+ EXPECT_CALL(*gl_, ReleaseTexImage2DMock(image_id1)).Times(1);
+ // Resize to 100x50.
+ drawing_buffer_->Resize(alternate_size);
+ VerifyStateWasRestored();
+ release_callback->Run(gpu::SyncToken(), false /* lostResource */);
+ VerifyStateWasRestored();
+ testing::Mock::VerifyAndClearExpectations(gl_);
+
+ GLuint image_id3 = gl_->NextImageIdToBeCreated();
+ EXPECT_CALL(*gl_, BindTexImage2DMock(image_id3)).Times(1);
+ // Produce a resource at this size.
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
+ &release_callback));
+ EXPECT_EQ(alternate_size, gl_->MostRecentlyProducedSize());
+ EXPECT_TRUE(resource.is_overlay_candidate);
+ EXPECT_EQ(alternate_size, resource.size);
+ testing::Mock::VerifyAndClearExpectations(gl_);
+
+ GLuint image_id4 = gl_->NextImageIdToBeCreated();
+ EXPECT_CALL(*gl_, BindTexImage2DMock(image_id4)).Times(1);
+ EXPECT_CALL(*gl_, DestroyImageMock(image_id2)).Times(1);
+ EXPECT_CALL(*gl_, ReleaseTexImage2DMock(image_id2)).Times(1);
+ EXPECT_CALL(*gl_, DestroyImageMock(image_id3)).Times(1);
+ EXPECT_CALL(*gl_, ReleaseTexImage2DMock(image_id3)).Times(1);
+ // Reset to initial size.
+ drawing_buffer_->Resize(initial_size);
+ VerifyStateWasRestored();
+ release_callback->Run(gpu::SyncToken(), false /* lostResource */);
+ VerifyStateWasRestored();
+ testing::Mock::VerifyAndClearExpectations(gl_);
+
+ GLuint image_id5 = gl_->NextImageIdToBeCreated();
+ EXPECT_CALL(*gl_, BindTexImage2DMock(image_id5)).Times(1);
+ // Prepare another resource and verify that it's the correct size.
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
+ &release_callback));
+ EXPECT_EQ(initial_size, gl_->MostRecentlyProducedSize());
+ EXPECT_TRUE(resource.is_overlay_candidate);
+ EXPECT_EQ(initial_size, resource.size);
+ testing::Mock::VerifyAndClearExpectations(gl_);
+
+ // Prepare one final resource and verify that it's the correct size.
+ release_callback->Run(gpu::SyncToken(), false /* lostResource */);
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
+ &release_callback));
+ EXPECT_EQ(initial_size, gl_->MostRecentlyProducedSize());
+ EXPECT_TRUE(resource.is_overlay_candidate);
+ EXPECT_EQ(initial_size, resource.size);
+ release_callback->Run(gpu::SyncToken(), false /* lostResource */);
+
+ EXPECT_CALL(*gl_, DestroyImageMock(image_id5)).Times(1);
+ EXPECT_CALL(*gl_, ReleaseTexImage2DMock(image_id5)).Times(1);
+ EXPECT_CALL(*gl_, DestroyImageMock(image_id4)).Times(1);
+ EXPECT_CALL(*gl_, ReleaseTexImage2DMock(image_id4)).Times(1);
+ drawing_buffer_->BeginDestruction();
+ testing::Mock::VerifyAndClearExpectations(gl_);
+}
+
+TEST_F(DrawingBufferImageChromiumTest, AllocationFailure) {
+ GLES2InterfaceForTests* gl_ = drawing_buffer_->ContextGLForTests();
+ viz::TransferableResource resource1;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback1;
+ viz::TransferableResource resource2;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback2;
+ viz::TransferableResource resource3;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback3;
+
+ // Request a resource. An image should already be created. Everything works
+ // as expected.
+ EXPECT_CALL(*gl_, BindTexImage2DMock(_)).Times(1);
+ IntSize initial_size(kInitialWidth, kInitialHeight);
+ EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource1,
+ &release_callback1));
+ EXPECT_TRUE(resource1.is_overlay_candidate);
+ testing::Mock::VerifyAndClearExpectations(gl_);
+ VerifyStateWasRestored();
+
+ // Force image CHROMIUM creation failure. Request another resource. It should
+ // still be provided, but this time with allowOverlay = false.
+ gl_->SetCreateImageChromiumFail(true);
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource2,
+ &release_callback2));
+ EXPECT_FALSE(resource2.is_overlay_candidate);
+ VerifyStateWasRestored();
+
+ // Check that if image CHROMIUM starts working again, resources are
+ // correctly created with allowOverlay = true.
+ EXPECT_CALL(*gl_, BindTexImage2DMock(_)).Times(1);
+ gl_->SetCreateImageChromiumFail(false);
+ EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource3,
+ &release_callback3));
+ EXPECT_TRUE(resource3.is_overlay_candidate);
+ testing::Mock::VerifyAndClearExpectations(gl_);
+ VerifyStateWasRestored();
+
+ release_callback1->Run(gpu::SyncToken(), false /* lostResource */);
+ release_callback2->Run(gpu::SyncToken(), false /* lostResource */);
+ release_callback3->Run(gpu::SyncToken(), false /* lostResource */);
+
+ EXPECT_CALL(*gl_, DestroyImageMock(_)).Times(3);
+ EXPECT_CALL(*gl_, ReleaseTexImage2DMock(_)).Times(3);
+ drawing_buffer_->BeginDestruction();
+ testing::Mock::VerifyAndClearExpectations(gl_);
+}
+
+class DepthStencilTrackingGLES2Interface
+ : public gpu::gles2::GLES2InterfaceStub {
+ public:
+ void FramebufferRenderbuffer(GLenum target,
+ GLenum attachment,
+ GLenum renderbuffertarget,
+ GLuint renderbuffer) override {
+ switch (attachment) {
+ case GL_DEPTH_ATTACHMENT:
+ depth_attachment_ = renderbuffer;
+ break;
+ case GL_STENCIL_ATTACHMENT:
+ stencil_attachment_ = renderbuffer;
+ break;
+ case GL_DEPTH_STENCIL_ATTACHMENT:
+ depth_stencil_attachment_ = renderbuffer;
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+ }
+
+ GLenum CheckFramebufferStatus(GLenum target) override {
+ return GL_FRAMEBUFFER_COMPLETE;
+ }
+
+ void GetIntegerv(GLenum ptype, GLint* value) override {
+ switch (ptype) {
+ case GL_MAX_TEXTURE_SIZE:
+ *value = 1024;
+ return;
+ }
+ }
+
+ const GLubyte* GetString(GLenum type) override {
+ if (type == GL_EXTENSIONS)
+ return reinterpret_cast<const GLubyte*>("GL_OES_packed_depth_stencil");
+ return reinterpret_cast<const GLubyte*>("");
+ }
+
+ void GenRenderbuffers(GLsizei n, GLuint* renderbuffers) override {
+ for (GLsizei i = 0; i < n; ++i)
+ renderbuffers[i] = next_gen_renderbuffer_id_++;
+ }
+
+ GLuint StencilAttachment() const { return stencil_attachment_; }
+ GLuint DepthAttachment() const { return depth_attachment_; }
+ GLuint DepthStencilAttachment() const { return depth_stencil_attachment_; }
+ size_t NumAllocatedRenderBuffer() const {
+ return next_gen_renderbuffer_id_ - 1;
+ }
+
+ private:
+ GLuint next_gen_renderbuffer_id_ = 1;
+ GLuint depth_attachment_ = 0;
+ GLuint stencil_attachment_ = 0;
+ GLuint depth_stencil_attachment_ = 0;
+};
+
+struct DepthStencilTestCase {
+ DepthStencilTestCase(bool request_stencil,
+ bool request_depth,
+ int expected_render_buffers,
+ const char* const test_case_name)
+ : request_stencil(request_stencil),
+ request_depth(request_depth),
+ expected_render_buffers(expected_render_buffers),
+ test_case_name(test_case_name) {}
+
+ bool request_stencil;
+ bool request_depth;
+ size_t expected_render_buffers;
+ const char* const test_case_name;
+};
+
+// This tests that, when the packed depth+stencil extension is supported, and
+// either depth or stencil is requested, DrawingBuffer always allocates a single
+// packed renderbuffer and properly computes the actual context attributes as
+// defined by WebGL. We always allocate a packed buffer in this case since many
+// desktop OpenGL drivers that support this extension do not consider a
+// framebuffer with only a depth or a stencil buffer attached to be complete.
+TEST(DrawingBufferDepthStencilTest, packedDepthStencilSupported) {
+ DepthStencilTestCase cases[] = {
+ DepthStencilTestCase(false, false, 0, "neither"),
+ DepthStencilTestCase(true, false, 1, "stencil only"),
+ DepthStencilTestCase(false, true, 1, "depth only"),
+ DepthStencilTestCase(true, true, 1, "both"),
+ };
+
+ for (size_t i = 0; i < WTF_ARRAY_LENGTH(cases); i++) {
+ SCOPED_TRACE(cases[i].test_case_name);
+ auto gl = std::make_unique<DepthStencilTrackingGLES2Interface>();
+ DepthStencilTrackingGLES2Interface* tracking_gl = gl.get();
+ auto provider =
+ std::make_unique<WebGraphicsContext3DProviderForTests>(std::move(gl));
+ DrawingBuffer::PreserveDrawingBuffer preserve = DrawingBuffer::kPreserve;
+
+ bool premultiplied_alpha = false;
+ bool want_alpha_channel = true;
+ bool want_depth_buffer = cases[i].request_depth;
+ bool want_stencil_buffer = cases[i].request_stencil;
+ bool want_antialiasing = false;
+ bool gpu_compositing = true;
+ scoped_refptr<DrawingBuffer> drawing_buffer = DrawingBuffer::Create(
+ std::move(provider), gpu_compositing, nullptr, IntSize(10, 10),
+ premultiplied_alpha, want_alpha_channel, want_depth_buffer,
+ want_stencil_buffer, want_antialiasing, preserve,
+ DrawingBuffer::kWebGL1, DrawingBuffer::kAllowChromiumImage,
+ CanvasColorParams());
+
+ // When we request a depth or a stencil buffer, we will get both.
+ EXPECT_EQ(cases[i].request_depth || cases[i].request_stencil,
+ drawing_buffer->HasDepthBuffer());
+ EXPECT_EQ(cases[i].request_depth || cases[i].request_stencil,
+ drawing_buffer->HasStencilBuffer());
+ EXPECT_EQ(cases[i].expected_render_buffers,
+ tracking_gl->NumAllocatedRenderBuffer());
+ if (cases[i].request_depth || cases[i].request_stencil) {
+ EXPECT_NE(0u, tracking_gl->DepthStencilAttachment());
+ EXPECT_EQ(0u, tracking_gl->DepthAttachment());
+ EXPECT_EQ(0u, tracking_gl->StencilAttachment());
+ } else {
+ EXPECT_EQ(0u, tracking_gl->DepthStencilAttachment());
+ EXPECT_EQ(0u, tracking_gl->DepthAttachment());
+ EXPECT_EQ(0u, tracking_gl->StencilAttachment());
+ }
+
+ drawing_buffer->Resize(IntSize(10, 20));
+ EXPECT_EQ(cases[i].request_depth || cases[i].request_stencil,
+ drawing_buffer->HasDepthBuffer());
+ EXPECT_EQ(cases[i].request_depth || cases[i].request_stencil,
+ drawing_buffer->HasStencilBuffer());
+ EXPECT_EQ(cases[i].expected_render_buffers,
+ tracking_gl->NumAllocatedRenderBuffer());
+ if (cases[i].request_depth || cases[i].request_stencil) {
+ EXPECT_NE(0u, tracking_gl->DepthStencilAttachment());
+ EXPECT_EQ(0u, tracking_gl->DepthAttachment());
+ EXPECT_EQ(0u, tracking_gl->StencilAttachment());
+ } else {
+ EXPECT_EQ(0u, tracking_gl->DepthStencilAttachment());
+ EXPECT_EQ(0u, tracking_gl->DepthAttachment());
+ EXPECT_EQ(0u, tracking_gl->StencilAttachment());
+ }
+
+ drawing_buffer->BeginDestruction();
+ }
+}
+
+TEST_F(DrawingBufferTest, VerifySetIsHiddenProperlyAffectsMailboxes) {
+ GLES2InterfaceForTests* gl_ = drawing_buffer_->ContextGLForTests();
+ viz::TransferableResource resource;
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback;
+
+ // Produce resources.
+ EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
+ &release_callback));
+
+ gpu::SyncToken wait_sync_token;
+ gl_->GenSyncTokenCHROMIUM(wait_sync_token.GetData());
+ drawing_buffer_->SetIsHidden(true);
+ release_callback->Run(wait_sync_token, false /* lostResource */);
+ // m_drawingBuffer deletes resource immediately when hidden.
+
+ EXPECT_EQ(wait_sync_token, gl_->MostRecentlyWaitedSyncToken());
+
+ drawing_buffer_->BeginDestruction();
+}
+
+TEST_F(DrawingBufferTest,
+ VerifyTooBigDrawingBufferExceedingV8MaxSizeFailsToCreate) {
+ IntSize too_big_size(1, (v8::TypedArray::kMaxLength / 4) + 1);
+ bool gpu_compositing = true;
+ scoped_refptr<DrawingBuffer> too_big_drawing_buffer = DrawingBuffer::Create(
+ nullptr, gpu_compositing, nullptr, too_big_size, false, false, false,
+ false, false, DrawingBuffer::kDiscard, DrawingBuffer::kWebGL1,
+ DrawingBuffer::kAllowChromiumImage, CanvasColorParams());
+ EXPECT_EQ(too_big_drawing_buffer, nullptr);
+ drawing_buffer_->BeginDestruction();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test_helpers.h b/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test_helpers.h
new file mode 100644
index 00000000000..308e30ab5a2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test_helpers.h
@@ -0,0 +1,452 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_DRAWING_BUFFER_TEST_HELPERS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_DRAWING_BUFFER_TEST_HELPERS_H_
+
+#include "build/build_config.h"
+#include "gpu/command_buffer/common/capabilities.h"
+#include "gpu/config/gpu_feature_info.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/web_graphics_context_3d_provider.h"
+#include "third_party/blink/renderer/platform/graphics/canvas_color_params.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+
+namespace blink {
+
+enum {
+ kInitialWidth = 100,
+ kInitialHeight = 100,
+ kAlternateHeight = 50,
+};
+
+enum UseMultisampling {
+ kDisableMultisampling,
+ kEnableMultisampling,
+};
+
+class WebGraphicsContext3DProviderForTests
+ : public WebGraphicsContext3DProvider {
+ public:
+ WebGraphicsContext3DProviderForTests(
+ std::unique_ptr<gpu::gles2::GLES2Interface> gl)
+ : gl_(std::move(gl)) {}
+
+ gpu::gles2::GLES2Interface* ContextGL() override { return gl_.get(); }
+ bool IsSoftwareRendering() const override { return false; }
+
+ // Not used by WebGL code.
+ GrContext* GetGrContext() override { return nullptr; }
+ bool BindToCurrentThread() override { return false; }
+ const gpu::Capabilities& GetCapabilities() const override {
+ return capabilities_;
+ }
+ const gpu::GpuFeatureInfo& GetGpuFeatureInfo() const override {
+ return gpu_feature_info_;
+ }
+ viz::GLHelper* GetGLHelper() override { return nullptr; }
+ void SetLostContextCallback(base::Closure) override {}
+ void SetErrorMessageCallback(
+ base::RepeatingCallback<void(const char*, int32_t id)>) {}
+ void SignalQuery(uint32_t, base::OnceClosure) override {}
+ cc::ImageDecodeCache* ImageDecodeCache() override { return nullptr; }
+
+ private:
+ std::unique_ptr<gpu::gles2::GLES2Interface> gl_;
+ gpu::Capabilities capabilities_;
+ gpu::GpuFeatureInfo gpu_feature_info_;
+};
+
+// The target to use when binding a texture to a Chromium image.
+GLenum ImageCHROMIUMTextureTarget() {
+#if defined(OS_MACOSX)
+ return GC3D_TEXTURE_RECTANGLE_ARB;
+#else
+ return GL_TEXTURE_2D;
+#endif
+}
+
+// The target to use when preparing a mailbox texture.
+GLenum DrawingBufferTextureTarget() {
+ if (RuntimeEnabledFeatures::WebGLImageChromiumEnabled())
+ return ImageCHROMIUMTextureTarget();
+ return GL_TEXTURE_2D;
+}
+
+class GLES2InterfaceForTests : public gpu::gles2::GLES2InterfaceStub,
+ public DrawingBuffer::Client {
+ public:
+ // GLES2InterfaceStub implementation:
+ void BindTexture(GLenum target, GLuint texture) override {
+ if (target == GL_TEXTURE_2D)
+ state_.active_texture2d_binding = texture;
+ bound_textures_[target] = texture;
+ }
+
+ void BindFramebuffer(GLenum target, GLuint framebuffer) override {
+ switch (target) {
+ case GL_FRAMEBUFFER:
+ state_.draw_framebuffer_binding = framebuffer;
+ state_.read_framebuffer_binding = framebuffer;
+ break;
+ case GL_DRAW_FRAMEBUFFER:
+ state_.draw_framebuffer_binding = framebuffer;
+ break;
+ case GL_READ_FRAMEBUFFER:
+ state_.read_framebuffer_binding = framebuffer;
+ break;
+ default:
+ break;
+ }
+ }
+
+ void BindRenderbuffer(GLenum target, GLuint renderbuffer) override {
+ state_.renderbuffer_binding = renderbuffer;
+ }
+
+ void Enable(GLenum cap) {
+ if (cap == GL_SCISSOR_TEST)
+ state_.scissor_enabled = true;
+ }
+
+ void Disable(GLenum cap) {
+ if (cap == GL_SCISSOR_TEST)
+ state_.scissor_enabled = false;
+ }
+
+ void ClearColor(GLfloat red,
+ GLfloat green,
+ GLfloat blue,
+ GLfloat alpha) override {
+ state_.clear_color[0] = red;
+ state_.clear_color[1] = green;
+ state_.clear_color[2] = blue;
+ state_.clear_color[3] = alpha;
+ }
+
+ void ClearDepthf(GLfloat depth) override { state_.clear_depth = depth; }
+
+ void ClearStencil(GLint s) override { state_.clear_stencil = s; }
+
+ void ColorMask(GLboolean red,
+ GLboolean green,
+ GLboolean blue,
+ GLboolean alpha) override {
+ state_.color_mask[0] = red;
+ state_.color_mask[1] = green;
+ state_.color_mask[2] = blue;
+ state_.color_mask[3] = alpha;
+ }
+
+ void DepthMask(GLboolean flag) override { state_.depth_mask = flag; }
+
+ void StencilMask(GLuint mask) override { state_.stencil_mask = mask; }
+
+ void StencilMaskSeparate(GLenum face, GLuint mask) override {
+ if (face == GL_FRONT)
+ state_.stencil_mask = mask;
+ }
+
+ void PixelStorei(GLenum pname, GLint param) override {
+ if (pname == GL_PACK_ALIGNMENT)
+ state_.pack_alignment = param;
+ }
+
+ void BindBuffer(GLenum target, GLuint buffer) override {
+ switch (target) {
+ case GL_PIXEL_UNPACK_BUFFER:
+ state_.pixel_unpack_buffer_binding = buffer;
+ break;
+ case GL_PIXEL_PACK_BUFFER:
+ state_.pixel_pack_buffer_binding = buffer;
+ break;
+ default:
+ break;
+ }
+ }
+
+ void WaitSyncTokenCHROMIUM(const GLbyte* sync_token) override {
+ memcpy(&most_recently_waited_sync_token_, sync_token,
+ sizeof(most_recently_waited_sync_token_));
+ }
+
+ GLenum CheckFramebufferStatus(GLenum target) override {
+ return GL_FRAMEBUFFER_COMPLETE;
+ }
+
+ void GetIntegerv(GLenum pname, GLint* value) override {
+ switch (pname) {
+ case GL_DRAW_FRAMEBUFFER_BINDING:
+ *value = state_.draw_framebuffer_binding;
+ break;
+ case GL_READ_FRAMEBUFFER_BINDING:
+ *value = state_.read_framebuffer_binding;
+ break;
+ case GL_MAX_TEXTURE_SIZE:
+ *value = 1024;
+ break;
+ default:
+ break;
+ }
+ }
+
+ void GenMailboxCHROMIUM(GLbyte* mailbox) override {
+ ++current_mailbox_byte_;
+ memset(mailbox, current_mailbox_byte_, GL_MAILBOX_SIZE_CHROMIUM);
+ }
+
+ void ProduceTextureDirectCHROMIUM(GLuint texture,
+ const GLbyte* mailbox) override {
+ if (!create_image_chromium_fail_) {
+ ASSERT_TRUE(texture_sizes_.Contains(texture));
+ most_recently_produced_size_ = texture_sizes_.at(texture);
+ }
+ }
+
+ void TexImage2D(GLenum target,
+ GLint level,
+ GLint internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLint border,
+ GLenum format,
+ GLenum type,
+ const void* pixels) override {
+ if (target == GL_TEXTURE_2D && !level) {
+ texture_sizes_.Set(bound_textures_[target], IntSize(width, height));
+ }
+ }
+
+ GLuint CreateImageCHROMIUM(ClientBuffer buffer,
+ GLsizei width,
+ GLsizei height,
+ GLenum internalformat) override {
+ if (create_image_chromium_fail_)
+ return 0;
+ image_sizes_.Set(current_image_id_, IntSize(width, height));
+ return current_image_id_++;
+ }
+
+ MOCK_METHOD1(DestroyImageMock, void(GLuint imageId));
+ void DestroyImageCHROMIUM(GLuint image_id) {
+ image_sizes_.erase(image_id);
+ // No textures should be bound to this.
+ CHECK(image_to_texture_map_.find(image_id) == image_to_texture_map_.end());
+ image_sizes_.erase(image_id);
+ DestroyImageMock(image_id);
+ }
+
+ MOCK_METHOD1(BindTexImage2DMock, void(GLint imageId));
+ void BindTexImage2DCHROMIUM(GLenum target, GLint image_id) {
+ if (target == ImageCHROMIUMTextureTarget()) {
+ texture_sizes_.Set(bound_textures_[target],
+ image_sizes_.find(image_id)->value);
+ image_to_texture_map_.Set(image_id, bound_textures_[target]);
+ BindTexImage2DMock(image_id);
+ }
+ }
+
+ MOCK_METHOD1(ReleaseTexImage2DMock, void(GLint imageId));
+ void ReleaseTexImage2DCHROMIUM(GLenum target, GLint image_id) {
+ if (target == ImageCHROMIUMTextureTarget()) {
+ image_sizes_.Set(current_image_id_, IntSize());
+ image_to_texture_map_.erase(image_id);
+ ReleaseTexImage2DMock(image_id);
+ }
+ }
+
+ void GenSyncTokenCHROMIUM(GLbyte* sync_token) override {
+ static uint64_t unique_id = 1;
+ gpu::SyncToken source(
+ gpu::GPU_IO, gpu::CommandBufferId::FromUnsafeValue(unique_id++), 2);
+ memcpy(sync_token, &source, sizeof(source));
+ }
+
+ void GenTextures(GLsizei n, GLuint* textures) override {
+ static GLuint id = 1;
+ for (GLsizei i = 0; i < n; ++i)
+ textures[i] = id++;
+ }
+
+ // DrawingBuffer::Client implementation.
+ bool DrawingBufferClientIsBoundForDraw() override {
+ return !state_.draw_framebuffer_binding;
+ }
+ void DrawingBufferClientRestoreScissorTest() override {
+ state_.scissor_enabled = saved_state_.scissor_enabled;
+ }
+ void DrawingBufferClientRestoreMaskAndClearValues() override {
+ memcpy(state_.color_mask, saved_state_.color_mask,
+ sizeof(state_.color_mask));
+ state_.clear_depth = saved_state_.clear_depth;
+ state_.clear_stencil = saved_state_.clear_stencil;
+
+ memcpy(state_.clear_color, saved_state_.clear_color,
+ sizeof(state_.clear_color));
+ state_.depth_mask = saved_state_.depth_mask;
+ state_.stencil_mask = saved_state_.stencil_mask;
+ }
+ void DrawingBufferClientRestorePixelPackParameters() override {
+ // TODO(zmo): restore ES3 pack parameters?
+ state_.pack_alignment = saved_state_.pack_alignment;
+ }
+ void DrawingBufferClientRestoreTexture2DBinding() override {
+ state_.active_texture2d_binding = saved_state_.active_texture2d_binding;
+ }
+ void DrawingBufferClientRestoreRenderbufferBinding() override {
+ state_.renderbuffer_binding = saved_state_.renderbuffer_binding;
+ }
+ void DrawingBufferClientRestoreFramebufferBinding() override {
+ state_.draw_framebuffer_binding = saved_state_.draw_framebuffer_binding;
+ state_.read_framebuffer_binding = saved_state_.read_framebuffer_binding;
+ }
+ void DrawingBufferClientRestorePixelUnpackBufferBinding() override {
+ state_.pixel_unpack_buffer_binding =
+ saved_state_.pixel_unpack_buffer_binding;
+ }
+ void DrawingBufferClientRestorePixelPackBufferBinding() override {
+ state_.pixel_pack_buffer_binding = saved_state_.pixel_pack_buffer_binding;
+ }
+
+ // Testing methods.
+ gpu::SyncToken MostRecentlyWaitedSyncToken() const {
+ return most_recently_waited_sync_token_;
+ }
+ GLuint NextImageIdToBeCreated() const { return current_image_id_; }
+ IntSize MostRecentlyProducedSize() const {
+ return most_recently_produced_size_;
+ }
+
+ void SetCreateImageChromiumFail(bool fail) {
+ create_image_chromium_fail_ = fail;
+ }
+
+ // Saves current GL state for later verification.
+ void SaveState() { saved_state_ = state_; }
+ void VerifyStateHasNotChangedSinceSave() const {
+ for (size_t i = 0; i < 4; ++i) {
+ EXPECT_EQ(state_.clear_color[0], saved_state_.clear_color[0]);
+ EXPECT_EQ(state_.color_mask[0], saved_state_.color_mask[0]);
+ }
+ EXPECT_EQ(state_.clear_depth, saved_state_.clear_depth);
+ EXPECT_EQ(state_.clear_stencil, saved_state_.clear_stencil);
+ EXPECT_EQ(state_.depth_mask, saved_state_.depth_mask);
+ EXPECT_EQ(state_.stencil_mask, saved_state_.stencil_mask);
+ EXPECT_EQ(state_.pack_alignment, saved_state_.pack_alignment);
+ EXPECT_EQ(state_.active_texture2d_binding,
+ saved_state_.active_texture2d_binding);
+ EXPECT_EQ(state_.renderbuffer_binding, saved_state_.renderbuffer_binding);
+ EXPECT_EQ(state_.draw_framebuffer_binding,
+ saved_state_.draw_framebuffer_binding);
+ EXPECT_EQ(state_.read_framebuffer_binding,
+ saved_state_.read_framebuffer_binding);
+ EXPECT_EQ(state_.pixel_unpack_buffer_binding,
+ saved_state_.pixel_unpack_buffer_binding);
+ EXPECT_EQ(state_.pixel_pack_buffer_binding,
+ saved_state_.pixel_pack_buffer_binding);
+ }
+
+ private:
+ std::map<GLenum, GLuint> bound_textures_;
+
+ // State tracked to verify that it is restored correctly.
+ struct State {
+ bool scissor_enabled = false;
+
+ GLfloat clear_color[4] = {0, 0, 0, 0};
+ GLfloat clear_depth = 0;
+ GLint clear_stencil = 0;
+
+ GLboolean color_mask[4] = {0, 0, 0, 0};
+ GLboolean depth_mask = 0;
+ GLuint stencil_mask = 0;
+
+ GLint pack_alignment = 4;
+
+ // The bound 2D texture for the active texture unit.
+ GLuint active_texture2d_binding = 0;
+ GLuint renderbuffer_binding = 0;
+ GLuint draw_framebuffer_binding = 0;
+ GLuint read_framebuffer_binding = 0;
+ GLuint pixel_unpack_buffer_binding = 0;
+ GLuint pixel_pack_buffer_binding = 0;
+ };
+ State state_;
+ State saved_state_;
+
+ gpu::SyncToken most_recently_waited_sync_token_;
+ GLbyte current_mailbox_byte_ = 0;
+ IntSize most_recently_produced_size_;
+ bool create_image_chromium_fail_ = false;
+ GLuint current_image_id_ = 1;
+ HashMap<GLuint, IntSize> texture_sizes_;
+ HashMap<GLuint, IntSize> image_sizes_;
+ HashMap<GLuint, GLuint> image_to_texture_map_;
+};
+
+class DrawingBufferForTests : public DrawingBuffer {
+ public:
+ static scoped_refptr<DrawingBufferForTests> Create(
+ std::unique_ptr<WebGraphicsContext3DProvider> context_provider,
+ bool using_gpu_compositing,
+ DrawingBuffer::Client* client,
+ const IntSize& size,
+ PreserveDrawingBuffer preserve,
+ UseMultisampling use_multisampling) {
+ std::unique_ptr<Extensions3DUtil> extensions_util =
+ Extensions3DUtil::Create(context_provider->ContextGL());
+ scoped_refptr<DrawingBufferForTests> drawing_buffer =
+ base::AdoptRef(new DrawingBufferForTests(
+ std::move(context_provider), using_gpu_compositing,
+ std::move(extensions_util), client, preserve));
+ if (!drawing_buffer->Initialize(
+ size, use_multisampling != kDisableMultisampling)) {
+ drawing_buffer->BeginDestruction();
+ return nullptr;
+ }
+ return drawing_buffer;
+ }
+
+ DrawingBufferForTests(
+ std::unique_ptr<WebGraphicsContext3DProvider> context_provider,
+ bool using_gpu_compositing,
+ std::unique_ptr<Extensions3DUtil> extensions_util,
+ DrawingBuffer::Client* client,
+ PreserveDrawingBuffer preserve)
+ : DrawingBuffer(
+ std::move(context_provider),
+ using_gpu_compositing,
+ std::move(extensions_util),
+ client,
+ false /* discardFramebufferSupported */,
+ true /* wantAlphaChannel */,
+ true /* premultipliedAlpha */,
+ preserve,
+ kWebGL1,
+ false /* wantDepth */,
+ false /* wantStencil */,
+ DrawingBuffer::kAllowChromiumImage /* ChromiumImageUsage */,
+ CanvasColorParams()),
+ live_(nullptr) {}
+
+ ~DrawingBufferForTests() override {
+ if (live_)
+ *live_ = false;
+ }
+
+ GLES2InterfaceForTests* ContextGLForTests() {
+ return static_cast<GLES2InterfaceForTests*>(ContextGL());
+ }
+
+ bool* live_;
+
+ int RecycledBitmapCount() { return recycled_bitmaps_.size(); }
+};
+
+} // blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_DRAWING_BUFFER_TEST_HELPERS_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.cc
new file mode 100644
index 00000000000..760ffa1f4a7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.cc
@@ -0,0 +1,93 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.h"
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+
+namespace blink {
+
+namespace {
+
+void SplitStringHelper(const String& str, HashSet<String>& set) {
+ Vector<String> substrings;
+ str.Split(' ', substrings);
+ for (size_t i = 0; i < substrings.size(); ++i)
+ set.insert(substrings[i]);
+}
+
+} // anonymous namespace
+
+std::unique_ptr<Extensions3DUtil> Extensions3DUtil::Create(
+ gpu::gles2::GLES2Interface* gl) {
+ std::unique_ptr<Extensions3DUtil> out =
+ base::WrapUnique(new Extensions3DUtil(gl));
+ out->InitializeExtensions();
+ return out;
+}
+
+Extensions3DUtil::Extensions3DUtil(gpu::gles2::GLES2Interface* gl)
+ : gl_(gl), is_valid_(true) {}
+
+Extensions3DUtil::~Extensions3DUtil() = default;
+
+void Extensions3DUtil::InitializeExtensions() {
+ if (gl_->GetGraphicsResetStatusKHR() != GL_NO_ERROR) {
+ // If the context is lost don't initialize the extension strings.
+ // This will cause supportsExtension, ensureExtensionEnabled, and
+ // isExtensionEnabled to always return false.
+ is_valid_ = false;
+ return;
+ }
+
+ String extensions_string(gl_->GetString(GL_EXTENSIONS));
+ SplitStringHelper(extensions_string, enabled_extensions_);
+
+ String requestable_extensions_string(gl_->GetRequestableExtensionsCHROMIUM());
+ SplitStringHelper(requestable_extensions_string, requestable_extensions_);
+}
+
+bool Extensions3DUtil::SupportsExtension(const String& name) {
+ return enabled_extensions_.Contains(name) ||
+ requestable_extensions_.Contains(name);
+}
+
+bool Extensions3DUtil::EnsureExtensionEnabled(const String& name) {
+ if (enabled_extensions_.Contains(name))
+ return true;
+
+ if (requestable_extensions_.Contains(name)) {
+ gl_->RequestExtensionCHROMIUM(name.Ascii().data());
+ enabled_extensions_.clear();
+ requestable_extensions_.clear();
+ InitializeExtensions();
+ }
+ return enabled_extensions_.Contains(name);
+}
+
+bool Extensions3DUtil::IsExtensionEnabled(const String& name) {
+ return enabled_extensions_.Contains(name);
+}
+
+bool Extensions3DUtil::CanUseCopyTextureCHROMIUM(GLenum dest_target) {
+ switch (dest_target) {
+ case GL_TEXTURE_2D:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
+ return true;
+ default:
+ return false;
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.h b/chromium/third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.h
new file mode 100644
index 00000000000..f220a21eb4e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.h
@@ -0,0 +1,55 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_EXTENSIONS_3D_UTIL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_EXTENSIONS_3D_UTIL_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/khronos/GLES2/gl2.h"
+
+namespace gpu {
+namespace gles2 {
+class GLES2Interface;
+}
+}
+
+namespace blink {
+
+class PLATFORM_EXPORT Extensions3DUtil final {
+ USING_FAST_MALLOC(Extensions3DUtil);
+ WTF_MAKE_NONCOPYABLE(Extensions3DUtil);
+
+ public:
+ // Creates a new Extensions3DUtil. If the passed GLES2Interface has been
+ // spontaneously lost, returns null.
+ static std::unique_ptr<Extensions3DUtil> Create(gpu::gles2::GLES2Interface*);
+ ~Extensions3DUtil();
+
+ bool IsValid() { return is_valid_; }
+
+ bool SupportsExtension(const String& name);
+ bool EnsureExtensionEnabled(const String& name);
+ bool IsExtensionEnabled(const String& name);
+
+ static bool CanUseCopyTextureCHROMIUM(GLenum dest_target);
+
+ private:
+ Extensions3DUtil(gpu::gles2::GLES2Interface*);
+ void InitializeExtensions();
+
+ gpu::gles2::GLES2Interface* gl_;
+ HashSet<String> enabled_extensions_;
+ HashSet<String> requestable_extensions_;
+ bool is_valid_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_EXTENSIONS_3D_UTIL_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/graphics_context_3d_utils.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/graphics_context_3d_utils.cc
new file mode 100644
index 00000000000..0adc80a4a48
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/graphics_context_3d_utils.cc
@@ -0,0 +1,83 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/gpu/graphics_context_3d_utils.h"
+
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "skia/ext/texture_handle.h"
+#include "third_party/blink/renderer/platform/graphics/web_graphics_context_3d_provider_wrapper.h"
+#include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/gpu/GrContext.h"
+
+namespace {
+
+struct GrTextureMailboxReleaseProcData {
+ GrTexture* gr_texture_;
+ base::WeakPtr<blink::WebGraphicsContext3DProviderWrapper>
+ context_provider_wrapper_;
+};
+
+void GrTextureMailboxReleaseProc(void* data) {
+ GrTextureMailboxReleaseProcData* release_proc_data =
+ static_cast<GrTextureMailboxReleaseProcData*>(data);
+
+ if (release_proc_data->context_provider_wrapper_) {
+ release_proc_data->context_provider_wrapper_->Utils()->RemoveCachedMailbox(
+ release_proc_data->gr_texture_);
+ }
+
+ delete release_proc_data;
+}
+
+} // unnamed namespace
+
+namespace blink {
+
+void GraphicsContext3DUtils::GetMailboxForSkImage(gpu::Mailbox& out_mailbox,
+ const sk_sp<SkImage>& image,
+ GLenum filter) {
+ // This object is owned by context_provider_wrapper_, so that weak ref
+ // should never be null.
+ DCHECK(context_provider_wrapper_);
+ DCHECK(image->isTextureBacked());
+ GrContext* gr = context_provider_wrapper_->ContextProvider()->GetGrContext();
+ gpu::gles2::GLES2Interface* gl =
+ context_provider_wrapper_->ContextProvider()->ContextGL();
+
+ DCHECK(gr);
+ DCHECK(gl);
+ GrTexture* gr_texture = image->getTexture();
+ DCHECK(gr == gr_texture->getContext());
+ auto it = cached_mailboxes_.find(gr_texture);
+ if (it != cached_mailboxes_.end()) {
+ out_mailbox = it->value;
+ } else {
+ gl->GenMailboxCHROMIUM(out_mailbox.name);
+
+ GrTextureMailboxReleaseProcData* release_proc_data =
+ new GrTextureMailboxReleaseProcData();
+ release_proc_data->gr_texture_ = gr_texture;
+ release_proc_data->context_provider_wrapper_ = context_provider_wrapper_;
+ gr_texture->setRelease(GrTextureMailboxReleaseProc, release_proc_data);
+ cached_mailboxes_.insert(gr_texture, out_mailbox);
+ }
+
+ GLuint texture_id =
+ skia::GrBackendObjectToGrGLTextureInfo(image->getTextureHandle(true))
+ ->fID;
+ gl->BindTexture(GL_TEXTURE_2D, texture_id);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ gl->BindTexture(GL_TEXTURE_2D, 0);
+ gl->ProduceTextureDirectCHROMIUM(texture_id, out_mailbox.name);
+ image->getTexture()->textureParamsModified();
+}
+
+void GraphicsContext3DUtils::RemoveCachedMailbox(GrTexture* gr_texture) {
+ cached_mailboxes_.erase(gr_texture);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/graphics_context_3d_utils.h b/chromium/third_party/blink/renderer/platform/graphics/gpu/graphics_context_3d_utils.h
new file mode 100644
index 00000000000..d88bef383ab
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/graphics_context_3d_utils.h
@@ -0,0 +1,46 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_GRAPHICS_CONTEXT_3D_UTILS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_GRAPHICS_CONTEXT_3D_UTILS_H_
+
+#include "base/memory/weak_ptr.h"
+#include "gpu/command_buffer/common/mailbox.h"
+#include "gpu/command_buffer/common/sync_token.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/gpu/GrTexture.h"
+
+typedef unsigned int GLenum;
+
+namespace blink {
+
+class WebGraphicsContext3DProviderWrapper;
+
+class PLATFORM_EXPORT GraphicsContext3DUtils {
+ public:
+ // The constructor takes a weak ref to the wrapper because it internally
+ // it generates callbacks that may outlive the wrapper.
+ GraphicsContext3DUtils(base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&
+ context_provider_wrapper)
+ : context_provider_wrapper_(std::move(context_provider_wrapper)) {}
+
+ // Use this service to create a new mailbox or possibly obtain a pre-existing
+ // mailbox for a given texture. The caching of pre-existing mailboxes survives
+ // when the texture gets recycled by skia for creating a new SkSurface or
+ // SkImage with a pre-existing GrTexture backing.
+ void GetMailboxForSkImage(gpu::Mailbox&,
+ const sk_sp<SkImage>&,
+ GLenum filter);
+ void RemoveCachedMailbox(GrTexture*);
+
+ private:
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper_;
+ WTF::HashMap<GrTexture*, gpu::Mailbox> cached_mailboxes_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.cc
new file mode 100644
index 00000000000..10f2d7faeda
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.cc
@@ -0,0 +1,217 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.h"
+
+#include "components/viz/common/quads/shared_bitmap.h"
+#include "components/viz/common/resources/transferable_resource.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_compositor_support.h"
+#include "third_party/blink/public/platform/web_external_texture_layer.h"
+#include "third_party/blink/public/platform/web_graphics_context_3d_provider.h"
+#include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h"
+#include "third_party/blink/renderer/platform/graphics/color_behavior.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
+
+namespace blink {
+
+ImageLayerBridge::ImageLayerBridge(OpacityMode opacity_mode)
+ : opacity_mode_(opacity_mode) {
+ layer_ = Platform::Current()->CompositorSupport()->CreateExternalTextureLayer(
+ this);
+ GraphicsLayer::RegisterContentsLayer(layer_->Layer());
+ layer_->SetNearestNeighbor(filter_quality_ == kNone_SkFilterQuality);
+ if (opacity_mode_ == kOpaque) {
+ layer_->SetOpaque(true);
+ layer_->SetBlendBackgroundColor(false);
+ }
+}
+
+ImageLayerBridge::~ImageLayerBridge() {
+ if (!disposed_)
+ Dispose();
+}
+
+void ImageLayerBridge::SetImage(scoped_refptr<StaticBitmapImage> image) {
+ if (disposed_)
+ return;
+
+ image_ = std::move(image);
+ if (image_) {
+ if (opacity_mode_ == kNonOpaque) {
+ layer_->SetOpaque(image_->CurrentFrameKnownToBeOpaque());
+ layer_->SetBlendBackgroundColor(!image_->CurrentFrameKnownToBeOpaque());
+ }
+ }
+ if (!has_presented_since_last_set_image_ && image_ &&
+ image_->IsTextureBacked()) {
+ // If the layer bridge is not presenting, the GrContext may not be getting
+ // flushed regularly. The flush is normally triggered inside the
+ // m_image->EnsureMailbox() call of
+ // ImageLayerBridge::PrepareTransferableResource. To prevent a potential
+ // memory leak we must flush the GrContext here.
+ image_->PaintImageForCurrentFrame().GetSkImage()->getTextureHandle(
+ true); // GrContext flush.
+ }
+ has_presented_since_last_set_image_ = false;
+}
+
+void ImageLayerBridge::SetUV(const FloatPoint left_top,
+ const FloatPoint right_bottom) {
+ if (disposed_)
+ return;
+
+ layer_->SetUV(WebFloatPoint(left_top.X(), left_top.Y()),
+ WebFloatPoint(right_bottom.X(), right_bottom.Y()));
+}
+
+void ImageLayerBridge::Dispose() {
+ if (layer_) {
+ GraphicsLayer::UnregisterContentsLayer(layer_->Layer());
+ layer_->ClearTexture();
+ layer_.reset();
+ }
+ image_ = nullptr;
+ disposed_ = true;
+}
+
+bool ImageLayerBridge::PrepareTransferableResource(
+ cc::SharedBitmapIdRegistrar* bitmap_registrar,
+ viz::TransferableResource* out_resource,
+ std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback) {
+ if (disposed_)
+ return false;
+
+ if (!image_)
+ return false;
+
+ if (has_presented_since_last_set_image_)
+ return false;
+
+ has_presented_since_last_set_image_ = true;
+
+ bool gpu_compositing = SharedGpuContext::IsGpuCompositingEnabled();
+ bool gpu_image = image_->IsTextureBacked();
+
+ // Expect software images for software compositing.
+ if (!gpu_compositing && gpu_image)
+ return false;
+
+ // If the texture comes from a software image then it does not need to be
+ // flipped.
+ layer_->SetFlipped(gpu_image);
+
+ scoped_refptr<StaticBitmapImage> image_for_compositor;
+
+ // Upload to a texture if the compositor is expecting one.
+ if (gpu_compositing && !image_->IsTextureBacked()) {
+ image_for_compositor =
+ image_->MakeAccelerated(SharedGpuContext::ContextProviderWrapper());
+ } else if (!gpu_compositing && image_->IsTextureBacked()) {
+ image_for_compositor = image_->MakeUnaccelerated();
+ } else {
+ image_for_compositor = image_;
+ }
+ DCHECK_EQ(image_for_compositor->IsTextureBacked(), gpu_compositing);
+
+ if (gpu_compositing) {
+ uint32_t filter =
+ filter_quality_ == kNone_SkFilterQuality ? GL_NEAREST : GL_LINEAR;
+ image_for_compositor->EnsureMailbox(kUnverifiedSyncToken, filter);
+ *out_resource = viz::TransferableResource::MakeGL(
+ image_for_compositor->GetMailbox(), filter, GL_TEXTURE_2D,
+ image_for_compositor->GetSyncToken());
+ auto func =
+ WTF::Bind(&ImageLayerBridge::ResourceReleasedGpu,
+ WrapWeakPersistent(this), std::move(image_for_compositor));
+ *out_release_callback = viz::SingleReleaseCallback::Create(std::move(func));
+ } else {
+ std::unique_ptr<viz::SharedBitmap> bitmap =
+ CreateOrRecycleBitmap(image_for_compositor->Size());
+ if (!bitmap)
+ return false;
+
+ sk_sp<SkImage> sk_image =
+ image_for_compositor->PaintImageForCurrentFrame().GetSkImage();
+ if (!sk_image)
+ return false;
+
+ SkImageInfo dst_info =
+ SkImageInfo::MakeN32Premul(image_for_compositor->width(), 1);
+ dst_info = dst_info.makeColorSpace(sk_image->refColorSpace());
+ size_t row_bytes = image_for_compositor->width() * 4;
+
+ // Copy from SkImage into |bitmap|, while flipping the Y axis.
+ for (int row = 0; row < image_for_compositor->height(); row++) {
+ if (!sk_image->readPixels(dst_info, bitmap->pixels(), row_bytes, 0, 0))
+ return false;
+ }
+
+ *out_resource = viz::TransferableResource::MakeSoftware(
+ bitmap->id(), bitmap->sequence_number(),
+ gfx::Size(image_for_compositor->width(),
+ image_for_compositor->height()),
+ viz::RGBA_8888);
+ auto func = WTF::Bind(&ImageLayerBridge::ResourceReleasedSoftware,
+ WrapWeakPersistent(this), std::move(bitmap),
+ image_for_compositor->Size());
+ *out_release_callback = viz::SingleReleaseCallback::Create(std::move(func));
+ }
+
+ // TODO(junov): Figure out how to get the color space info.
+ // out_resource->color_space = ...;
+
+ return true;
+}
+
+std::unique_ptr<viz::SharedBitmap> ImageLayerBridge::CreateOrRecycleBitmap(
+ const IntSize& size) {
+ auto it = std::remove_if(
+ recycled_bitmaps_.begin(), recycled_bitmaps_.end(),
+ [&size](const RecycledBitmap& bitmap) { return bitmap.size != size; });
+ recycled_bitmaps_.Shrink(it - recycled_bitmaps_.begin());
+
+ if (!recycled_bitmaps_.IsEmpty()) {
+ RecycledBitmap recycled = std::move(recycled_bitmaps_.back());
+ recycled_bitmaps_.pop_back();
+ DCHECK(recycled.size == size);
+ return std::move(recycled.bitmap);
+ }
+ return Platform::Current()->AllocateSharedBitmap(size, viz::RGBA_8888);
+}
+
+void ImageLayerBridge::ResourceReleasedGpu(
+ scoped_refptr<StaticBitmapImage> image,
+ const gpu::SyncToken& token,
+ bool lost_resource) {
+ if (image && image->IsValid()) {
+ DCHECK(image->IsTextureBacked());
+ if (token.HasData() && image->ContextProvider() &&
+ image->ContextProvider()->ContextGL()) {
+ image->ContextProvider()->ContextGL()->WaitSyncTokenCHROMIUM(
+ token.GetConstData());
+ }
+ }
+ // let 'image' go out of scope to release gpu resources.
+}
+
+void ImageLayerBridge::ResourceReleasedSoftware(
+ std::unique_ptr<viz::SharedBitmap> bitmap,
+ const IntSize& size,
+ const gpu::SyncToken& sync_token,
+ bool lost_resource) {
+ DCHECK(!sync_token.HasData()); // No sync tokens for software resources.
+ if (!disposed_ && !lost_resource) {
+ RecycledBitmap recycled = {std::move(bitmap), size};
+ recycled_bitmaps_.push_back(std::move(recycled));
+ }
+}
+
+WebLayer* ImageLayerBridge::PlatformLayer() const {
+ return layer_->Layer();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.h b/chromium/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.h
new file mode 100644
index 00000000000..9dfaae76c1c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.h
@@ -0,0 +1,87 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_IMAGE_LAYER_BRIDGE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_IMAGE_LAYER_BRIDGE_H_
+
+#include "cc/layers/texture_layer_client.h"
+#include "third_party/blink/renderer/platform/geometry/float_point.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_types.h"
+#include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace viz {
+class SharedBitmap;
+}
+
+namespace blink {
+
+class WebLayer;
+class WebExternalTextureLayer;
+
+class PLATFORM_EXPORT ImageLayerBridge
+ : public GarbageCollectedFinalized<ImageLayerBridge>,
+ public cc::TextureLayerClient {
+ WTF_MAKE_NONCOPYABLE(ImageLayerBridge);
+
+ public:
+ ImageLayerBridge(OpacityMode);
+ ~ImageLayerBridge();
+
+ void SetImage(scoped_refptr<StaticBitmapImage>);
+ void Dispose();
+
+ // cc::TextureLayerClient implementation.
+ bool PrepareTransferableResource(
+ cc::SharedBitmapIdRegistrar* bitmap_registrar,
+ viz::TransferableResource* out_resource,
+ std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback)
+ override;
+
+ void ResourceReleasedGpu(scoped_refptr<StaticBitmapImage>,
+ const gpu::SyncToken&,
+ bool lost_resource);
+
+ void ResourceReleasedSoftware(std::unique_ptr<viz::SharedBitmap>,
+ const IntSize&,
+ const gpu::SyncToken&,
+ bool lost_resource);
+
+ scoped_refptr<StaticBitmapImage> GetImage() { return image_; }
+
+ WebLayer* PlatformLayer() const;
+
+ void SetFilterQuality(SkFilterQuality filter_quality) {
+ filter_quality_ = filter_quality;
+ }
+ void SetUV(const FloatPoint left_top, const FloatPoint right_bottom);
+
+ bool IsAccelerated() { return image_->IsTextureBacked(); }
+
+ void Trace(blink::Visitor* visitor) {}
+
+ private:
+ std::unique_ptr<viz::SharedBitmap> CreateOrRecycleBitmap(const IntSize& size);
+
+ scoped_refptr<StaticBitmapImage> image_;
+ std::unique_ptr<WebExternalTextureLayer> layer_;
+ SkFilterQuality filter_quality_ = kLow_SkFilterQuality;
+
+ // Shared memory bitmaps that were released by the compositor and can be used
+ // again by this ImageLayerBridge.
+ struct RecycledBitmap {
+ std::unique_ptr<viz::SharedBitmap> bitmap;
+ IntSize size;
+ };
+ Vector<RecycledBitmap> recycled_bitmaps_;
+
+ bool disposed_ = false;
+ bool has_presented_since_last_set_image_ = false;
+ OpacityMode opacity_mode_ = kNonOpaque;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/shared_context_rate_limiter.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/shared_context_rate_limiter.cc
new file mode 100644
index 00000000000..c31f06e8e9e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/shared_context_rate_limiter.cc
@@ -0,0 +1,84 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/gpu/shared_context_rate_limiter.h"
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "gpu/GLES2/gl2extchromium.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_graphics_context_3d_provider.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.h"
+#include "third_party/khronos/GLES2/gl2.h"
+
+namespace blink {
+
+std::unique_ptr<SharedContextRateLimiter> SharedContextRateLimiter::Create(
+ unsigned max_pending_ticks) {
+ return base::WrapUnique(new SharedContextRateLimiter(max_pending_ticks));
+}
+
+SharedContextRateLimiter::SharedContextRateLimiter(unsigned max_pending_ticks)
+ : max_pending_ticks_(max_pending_ticks), can_use_sync_queries_(false) {
+ context_provider_ =
+ Platform::Current()->CreateSharedOffscreenGraphicsContext3DProvider();
+ if (!context_provider_)
+ return;
+
+ gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
+ if (gl && gl->GetGraphicsResetStatusKHR() == GL_NO_ERROR) {
+ std::unique_ptr<Extensions3DUtil> extensions_util =
+ Extensions3DUtil::Create(gl);
+ // TODO(junov): when the GLES 3.0 command buffer is ready, we could use
+ // fenceSync instead.
+ can_use_sync_queries_ =
+ extensions_util->SupportsExtension("GL_CHROMIUM_sync_query");
+ }
+}
+
+void SharedContextRateLimiter::Tick() {
+ if (!context_provider_)
+ return;
+
+ gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
+ if (!gl || gl->GetGraphicsResetStatusKHR() != GL_NO_ERROR)
+ return;
+
+ queries_.push_back(0);
+ if (can_use_sync_queries_) {
+ gl->GenQueriesEXT(1, &queries_.back());
+ gl->BeginQueryEXT(GL_COMMANDS_COMPLETED_CHROMIUM, queries_.back());
+ gl->EndQueryEXT(GL_COMMANDS_COMPLETED_CHROMIUM);
+ }
+ if (queries_.size() > max_pending_ticks_) {
+ if (can_use_sync_queries_) {
+ GLuint result;
+ gl->GetQueryObjectuivEXT(queries_.front(), GL_QUERY_RESULT_EXT, &result);
+ gl->DeleteQueriesEXT(1, &queries_.front());
+ queries_.pop_front();
+ } else {
+ gl->Finish();
+ Reset();
+ }
+ }
+}
+
+void SharedContextRateLimiter::Reset() {
+ if (!context_provider_)
+ return;
+
+ gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
+ if (can_use_sync_queries_ && gl &&
+ gl->GetGraphicsResetStatusKHR() == GL_NO_ERROR) {
+ while (queries_.size() > 0) {
+ gl->DeleteQueriesEXT(1, &queries_.front());
+ queries_.pop_front();
+ }
+ } else {
+ queries_.clear();
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/shared_context_rate_limiter.h b/chromium/third_party/blink/renderer/platform/graphics/gpu/shared_context_rate_limiter.h
new file mode 100644
index 00000000000..86b0a6c0209
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/shared_context_rate_limiter.h
@@ -0,0 +1,59 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_SHARED_CONTEXT_RATE_LIMITER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_SHARED_CONTEXT_RATE_LIMITER_H_
+
+#include <memory>
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/deque.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class WebGraphicsContext3DProvider;
+
+// Purpose: to limit the amount of worked queued for execution
+// (backlog) on the GPU by blocking the main thread to allow the GPU
+// to catch up. The Prevents unsynchronized tight animation loops
+// from cause a GPU denial of service.
+//
+// How it works: The rate limiter uses GPU fences to mark each tick
+// and makes sure there are never more that 'maxPendingTicks' fences
+// that are awaiting completion. On platforms that do not support
+// fences, we use glFinish instead. glFinish will only be called in
+// unsynchronized cases that submit more than maxPendingTicks animation
+// tick per compositor frame, which should be quite rare.
+//
+// How to use it: Each unit of work that constitutes a complete animation
+// frame must call tick(). reset() must be called when the animation
+// is consumed by committing to the compositor. Several rate limiters can
+// be used concurrently: they will each use their own sequences of
+// fences which may be interleaved. When the graphics context is lost
+// and later restored, the existing rate limiter must be destroyed and
+// a new one created.
+
+class SharedContextRateLimiter final {
+ USING_FAST_MALLOC(SharedContextRateLimiter);
+ WTF_MAKE_NONCOPYABLE(SharedContextRateLimiter);
+
+ public:
+ static std::unique_ptr<SharedContextRateLimiter> Create(
+ unsigned max_pending_ticks);
+ void Tick();
+ void Reset();
+
+ private:
+ SharedContextRateLimiter(unsigned max_pending_ticks);
+
+ std::unique_ptr<WebGraphicsContext3DProvider> context_provider_;
+ Deque<GLuint> queries_;
+ unsigned max_pending_ticks_;
+ bool can_use_sync_queries_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.cc
new file mode 100644
index 00000000000..cfddf674cfc
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.cc
@@ -0,0 +1,178 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
+
+#include "base/single_thread_task_runner.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "gpu/config/gpu_driver_bug_workaround_type.h"
+#include "gpu/config/gpu_feature_info.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_graphics_context_3d_provider.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/waitable_event.h"
+#include "third_party/blink/renderer/platform/web_task_runner.h"
+
+namespace blink {
+
+SharedGpuContext* SharedGpuContext::GetInstanceForCurrentThread() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadSpecific<SharedGpuContext>,
+ thread_specific_instance, ());
+ return thread_specific_instance;
+}
+
+SharedGpuContext::SharedGpuContext() = default;
+
+// static
+bool SharedGpuContext::IsGpuCompositingEnabled() {
+ SharedGpuContext* this_ptr = GetInstanceForCurrentThread();
+ // The check for gpu compositing enabled implies a context will
+ // desired, so we combine them into a single trip to the main thread.
+ // This also ensures that the compositing mode does not change before
+ // the context is created, so if it does change the context will be lost
+ // and this class will know to check the compositing mode again.
+ bool only_if_gpu_compositing = true;
+ this_ptr->CreateContextProviderIfNeeded(only_if_gpu_compositing);
+ return !this_ptr->is_gpu_compositing_disabled_;
+}
+
+base::WeakPtr<WebGraphicsContext3DProviderWrapper>
+SharedGpuContext::ContextProviderWrapper() {
+ SharedGpuContext* this_ptr = GetInstanceForCurrentThread();
+ bool only_if_gpu_compositing = false;
+ this_ptr->CreateContextProviderIfNeeded(only_if_gpu_compositing);
+ if (!this_ptr->context_provider_wrapper_)
+ return nullptr;
+ return this_ptr->context_provider_wrapper_->GetWeakPtr();
+}
+
+static void CreateContextProviderOnMainThread(
+ bool only_if_gpu_compositing,
+ bool* gpu_compositing_disabled,
+ std::unique_ptr<WebGraphicsContext3DProviderWrapper>* wrapper,
+ WaitableEvent* waitable_event) {
+ DCHECK(IsMainThread());
+
+ Platform::ContextAttributes context_attributes;
+ context_attributes.enable_raster_interface = true;
+ context_attributes.support_grcontext = true;
+
+ *gpu_compositing_disabled = Platform::Current()->IsGpuCompositingDisabled();
+ if (*gpu_compositing_disabled && only_if_gpu_compositing) {
+ waitable_event->Signal();
+ return;
+ }
+
+ Platform::GraphicsInfo graphics_info;
+ auto context_provider =
+ Platform::Current()->CreateOffscreenGraphicsContext3DProvider(
+ context_attributes, WebURL(), nullptr, &graphics_info);
+ if (context_provider) {
+ *wrapper = std::make_unique<WebGraphicsContext3DProviderWrapper>(
+ std::move(context_provider));
+ }
+ waitable_event->Signal();
+}
+
+void SharedGpuContext::CreateContextProviderIfNeeded(
+ bool only_if_gpu_compositing) {
+ // Once true, |is_gpu_compositing_disabled_| will always stay true.
+ if (is_gpu_compositing_disabled_ && only_if_gpu_compositing)
+ return;
+
+ // TODO(danakj): This needs to check that the context is being used on the
+ // thread it was made on, or else lock it.
+ if (context_provider_wrapper_ &&
+ context_provider_wrapper_->ContextProvider()
+ ->ContextGL()
+ ->GetGraphicsResetStatusKHR() == GL_NO_ERROR) {
+ // If the context isn't lost then |is_gpu_compositing_disabled_| state
+ // hasn't changed yet. RenderThreadImpl::CompositingModeFallbackToSoftware()
+ // will lose the context to let us know if it changes.
+ return;
+ }
+
+ is_gpu_compositing_disabled_ = false;
+ context_provider_wrapper_ = nullptr;
+
+ if (context_provider_factory_) {
+ // This path should only be used in unit tests.
+ auto context_provider =
+ context_provider_factory_.Run(&is_gpu_compositing_disabled_);
+ if (context_provider) {
+ context_provider_wrapper_ =
+ std::make_unique<WebGraphicsContext3DProviderWrapper>(
+ std::move(context_provider));
+ }
+ } else if (IsMainThread()) {
+ is_gpu_compositing_disabled_ =
+ Platform::Current()->IsGpuCompositingDisabled();
+ if (is_gpu_compositing_disabled_ && only_if_gpu_compositing)
+ return;
+ auto context_provider =
+ Platform::Current()->CreateSharedOffscreenGraphicsContext3DProvider();
+ if (context_provider) {
+ context_provider_wrapper_ =
+ std::make_unique<WebGraphicsContext3DProviderWrapper>(
+ std::move(context_provider));
+ }
+ } else {
+ // This synchronous round-trip to the main thread is the reason why
+ // SharedGpuContext encasulates the context provider: so we only have to do
+ // this once per thread.
+ WaitableEvent waitable_event;
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner =
+ Platform::Current()->MainThread()->GetTaskRunner();
+ PostCrossThreadTask(
+ *task_runner, FROM_HERE,
+ CrossThreadBind(&CreateContextProviderOnMainThread,
+ only_if_gpu_compositing,
+ CrossThreadUnretained(&is_gpu_compositing_disabled_),
+ CrossThreadUnretained(&context_provider_wrapper_),
+ CrossThreadUnretained(&waitable_event)));
+ waitable_event.Wait();
+ if (context_provider_wrapper_ &&
+ !context_provider_wrapper_->ContextProvider()->BindToCurrentThread())
+ context_provider_wrapper_ = nullptr;
+ }
+}
+
+// static
+void SharedGpuContext::SetContextProviderFactoryForTesting(
+ ContextProviderFactory factory) {
+ SharedGpuContext* this_ptr = GetInstanceForCurrentThread();
+ DCHECK(!this_ptr->context_provider_wrapper_);
+ this_ptr->context_provider_factory_ = std::move(factory);
+}
+
+// static
+void SharedGpuContext::ResetForTesting() {
+ SharedGpuContext* this_ptr = GetInstanceForCurrentThread();
+ this_ptr->is_gpu_compositing_disabled_ = false;
+ this_ptr->context_provider_wrapper_.reset();
+ this_ptr->context_provider_factory_.Reset();
+}
+
+bool SharedGpuContext::IsValidWithoutRestoring() {
+ SharedGpuContext* this_ptr = GetInstanceForCurrentThread();
+ if (!this_ptr->context_provider_wrapper_)
+ return false;
+ return this_ptr->context_provider_wrapper_->ContextProvider()
+ ->ContextGL()
+ ->GetGraphicsResetStatusKHR() == GL_NO_ERROR;
+}
+
+bool SharedGpuContext::AllowSoftwareToAcceleratedCanvasUpgrade() {
+ SharedGpuContext* this_ptr = GetInstanceForCurrentThread();
+ bool only_if_gpu_compositing = false;
+ this_ptr->CreateContextProviderIfNeeded(only_if_gpu_compositing);
+ if (!this_ptr->context_provider_wrapper_)
+ return false;
+ return !this_ptr->context_provider_wrapper_->ContextProvider()
+ ->GetGpuFeatureInfo()
+ .IsWorkaroundEnabled(
+ gpu::DISABLE_SOFTWARE_TO_ACCELERATED_CANVAS_UPGRADE);
+}
+
+} // blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h b/chromium/third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h
new file mode 100644
index 00000000000..57535d41863
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h
@@ -0,0 +1,66 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_SHARED_GPU_CONTEXT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_SHARED_GPU_CONTEXT_H_
+
+#include <memory>
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "third_party/blink/renderer/platform/graphics/web_graphics_context_3d_provider_wrapper.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/thread_specific.h"
+
+namespace blink {
+
+class WebGraphicsContext3DProvider;
+
+// SharedGpuContext provides access to a thread-specific GPU context
+// that is shared by many callsites throughout the thread.
+// When on the main thread, provides access to the same context as
+// Platform::CreateSharedOffscreenGraphicsContext3DProvider, and the
+// same query as Platform::IsGPUCompositingEnabled().
+class PLATFORM_EXPORT SharedGpuContext {
+ public:
+ // Thread-safe query if gpu compositing is enabled. This should be done before
+ // calling ContextProviderWrapper() if the context will be used to make
+ // resources meant for the compositor. When it is false, no context will be
+ // needed and software-based resources should be given to the compositor
+ // instead.
+ static bool IsGpuCompositingEnabled();
+ // May re-create context if context was lost
+ static base::WeakPtr<WebGraphicsContext3DProviderWrapper>
+ ContextProviderWrapper();
+ static bool AllowSoftwareToAcceleratedCanvasUpgrade();
+ static bool IsValidWithoutRestoring();
+
+ using ContextProviderFactory =
+ base::RepeatingCallback<std::unique_ptr<WebGraphicsContext3DProvider>(
+ bool* is_gpu_compositing_disabled)>;
+ static void SetContextProviderFactoryForTesting(ContextProviderFactory);
+ // Resets the global instance including the |context_provider_factory_| and
+ // dropping the context. Should be called at the end of a test that uses this
+ // to not interfere with the next test.
+ static void ResetForTesting();
+
+ private:
+ friend class WTF::ThreadSpecific<SharedGpuContext>;
+
+ static SharedGpuContext* GetInstanceForCurrentThread();
+
+ SharedGpuContext();
+ void CreateContextProviderIfNeeded(bool only_if_gpu_compositing);
+
+ // Can be overridden for tests.
+ ContextProviderFactory context_provider_factory_;
+
+ // This is sticky once true, we never need to ask again.
+ bool is_gpu_compositing_disabled_ = false;
+ std::unique_ptr<WebGraphicsContext3DProviderWrapper>
+ context_provider_wrapper_;
+};
+
+} // blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_SHARED_GPU_CONTEXT_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context_test.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context_test.cc
new file mode 100644
index 00000000000..368d8cb03db
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context_test.cc
@@ -0,0 +1,272 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
+
+#include <memory>
+
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h"
+#include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h"
+#include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
+#include "third_party/blink/renderer/platform/graphics/test/fake_gles2_interface.h"
+#include "third_party/blink/renderer/platform/graphics/test/fake_web_graphics_context_3d_provider.h"
+#include "third_party/khronos/GLES2/gl2ext.h"
+
+using testing::Test;
+
+namespace blink {
+
+namespace {
+
+template <class GLES2InterfaceType>
+class SharedGpuContextTestBase : public Test {
+ public:
+ void SetUp() override {
+ auto factory = [](GLES2InterfaceType* gl, bool* gpu_compositing_disabled)
+ -> std::unique_ptr<WebGraphicsContext3DProvider> {
+ *gpu_compositing_disabled = false;
+ gl->SetIsContextLost(false);
+ return std::make_unique<FakeWebGraphicsContext3DProvider>(gl);
+ };
+ SharedGpuContext::SetContextProviderFactoryForTesting(
+ WTF::BindRepeating(factory, WTF::Unretained(&gl_)));
+ }
+
+ void TearDown() override { SharedGpuContext::ResetForTesting(); }
+
+ GLES2InterfaceType gl_;
+};
+
+class SharedGpuContextTest
+ : public SharedGpuContextTestBase<FakeGLES2Interface> {};
+
+class MailboxMockGLES2Interface : public FakeGLES2Interface {
+ public:
+ MOCK_METHOD1(GenMailboxCHROMIUM, void(GLbyte*));
+ MOCK_METHOD1(GenSyncTokenCHROMIUM, void(GLbyte*));
+ MOCK_METHOD1(GenUnverifiedSyncTokenCHROMIUM, void(GLbyte*));
+};
+
+class MailboxSharedGpuContextTest
+ : public SharedGpuContextTestBase<MailboxMockGLES2Interface> {};
+
+// Test fixure that simulate a graphics context creation failure, when using gpu
+// compositing.
+class BadSharedGpuContextTest : public Test {
+ public:
+ void SetUp() override {
+ auto factory = [](bool* gpu_compositing_disabled)
+ -> std::unique_ptr<WebGraphicsContext3DProvider> {
+ *gpu_compositing_disabled = false;
+ return nullptr;
+ };
+ SharedGpuContext::SetContextProviderFactoryForTesting(
+ WTF::BindRepeating(factory));
+ }
+
+ void TearDown() override { SharedGpuContext::ResetForTesting(); }
+};
+
+// Test fixure that simulate not using gpu compositing.
+class SoftwareCompositingTest : public Test {
+ public:
+ void SetUp() override {
+ auto factory = [](FakeGLES2Interface* gl, bool* gpu_compositing_disabled)
+ -> std::unique_ptr<WebGraphicsContext3DProvider> {
+ *gpu_compositing_disabled = true;
+ // Return a context anyway, to ensure that's not what the class checks
+ // to determine compositing mode.
+ gl->SetIsContextLost(false);
+ return std::make_unique<FakeWebGraphicsContext3DProvider>(gl);
+ };
+ SharedGpuContext::SetContextProviderFactoryForTesting(
+ WTF::BindRepeating(factory, WTF::Unretained(&gl_)));
+ }
+
+ void TearDown() override { SharedGpuContext::ResetForTesting(); }
+
+ FakeGLES2Interface gl_;
+};
+
+TEST_F(SharedGpuContextTest, contextLossAutoRecovery) {
+ EXPECT_NE(SharedGpuContext::ContextProviderWrapper(), nullptr);
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper> context =
+ SharedGpuContext::ContextProviderWrapper();
+ gl_.SetIsContextLost(true);
+ EXPECT_FALSE(SharedGpuContext::IsValidWithoutRestoring());
+ EXPECT_TRUE(!!context);
+
+ // Context recreation results in old provider being discarded.
+ EXPECT_TRUE(!!SharedGpuContext::ContextProviderWrapper());
+ EXPECT_FALSE(!!context);
+}
+
+TEST_F(SharedGpuContextTest, AccelerateImageBufferSurfaceAutoRecovery) {
+ // Verifies that after a context loss, attempting to allocate an
+ // AcceleratedImageBufferSurface will restore the context and succeed
+ gl_.SetIsContextLost(true);
+ EXPECT_FALSE(SharedGpuContext::IsValidWithoutRestoring());
+ IntSize size(10, 10);
+ std::unique_ptr<CanvasResourceProvider> resource_provider =
+ CanvasResourceProvider::Create(
+ size, CanvasResourceProvider::kAcceleratedResourceUsage,
+ SharedGpuContext::ContextProviderWrapper());
+ EXPECT_TRUE(resource_provider && resource_provider->IsValid());
+ EXPECT_TRUE(SharedGpuContext::IsValidWithoutRestoring());
+}
+
+TEST_F(SharedGpuContextTest, Canvas2DLayerBridgeAutoRecovery) {
+ // Verifies that after a context loss, attempting to allocate a
+ // Canvas2DLayerBridge will restore the context and succeed.
+ gl_.SetIsContextLost(true);
+ EXPECT_FALSE(SharedGpuContext::IsValidWithoutRestoring());
+ IntSize size(10, 10);
+ CanvasColorParams color_params;
+ std::unique_ptr<Canvas2DLayerBridge> bridge =
+ std::make_unique<Canvas2DLayerBridge>(
+ size, 0,
+ /*msaa sample count*/ Canvas2DLayerBridge::kEnableAcceleration,
+ color_params);
+ EXPECT_TRUE(bridge->IsAccelerated());
+ EXPECT_TRUE(SharedGpuContext::IsValidWithoutRestoring());
+ bridge->BeginDestruction();
+}
+
+TEST_F(SharedGpuContextTest, IsValidWithoutRestoring) {
+ EXPECT_NE(SharedGpuContext::ContextProviderWrapper(), nullptr);
+ EXPECT_TRUE(SharedGpuContext::IsValidWithoutRestoring());
+}
+
+TEST_F(BadSharedGpuContextTest, IsValidWithoutRestoring) {
+ EXPECT_FALSE(SharedGpuContext::IsValidWithoutRestoring());
+}
+
+TEST_F(BadSharedGpuContextTest, AllowSoftwareToAcceleratedCanvasUpgrade) {
+ EXPECT_FALSE(SharedGpuContext::AllowSoftwareToAcceleratedCanvasUpgrade());
+}
+
+TEST_F(BadSharedGpuContextTest, AccelerateImageBufferSurfaceCreationFails) {
+ // With a bad shared context, AccelerateImageBufferSurface creation should
+ // fail gracefully
+ IntSize size(10, 10);
+ std::unique_ptr<CanvasResourceProvider> resource_provider =
+ CanvasResourceProvider::Create(
+ size, CanvasResourceProvider::kAcceleratedResourceUsage,
+ SharedGpuContext::ContextProviderWrapper());
+ EXPECT_FALSE(!resource_provider);
+}
+
+TEST_F(SharedGpuContextTest, CompositingMode) {
+ EXPECT_TRUE(SharedGpuContext::IsGpuCompositingEnabled());
+}
+
+TEST_F(BadSharedGpuContextTest, CompositingMode) {
+ EXPECT_TRUE(SharedGpuContext::IsGpuCompositingEnabled());
+}
+
+TEST_F(SoftwareCompositingTest, CompositingMode) {
+ EXPECT_FALSE(SharedGpuContext::IsGpuCompositingEnabled());
+}
+
+class FakeMailboxGenerator {
+ public:
+ void GenMailbox(GLbyte* name) { *name = counter_++; }
+
+ GLbyte counter_ = 1;
+};
+
+TEST_F(MailboxSharedGpuContextTest, MailboxCaching) {
+ IntSize size(10, 10);
+ std::unique_ptr<CanvasResourceProvider> resource_provider =
+ CanvasResourceProvider::Create(
+ size, CanvasResourceProvider::kAcceleratedResourceUsage,
+ SharedGpuContext::ContextProviderWrapper());
+ EXPECT_TRUE(resource_provider && resource_provider->IsValid());
+ scoped_refptr<StaticBitmapImage> image = resource_provider->Snapshot();
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+
+ FakeMailboxGenerator mailboxGenerator;
+ gpu::Mailbox mailbox;
+ mailbox.name[0] = 0;
+
+ EXPECT_CALL(gl_, GenMailboxCHROMIUM(mailbox.name))
+ .Times(1)
+ .WillOnce(testing::Invoke(&mailboxGenerator,
+ &FakeMailboxGenerator::GenMailbox));
+
+ SharedGpuContext::ContextProviderWrapper()->Utils()->GetMailboxForSkImage(
+ mailbox, image->PaintImageForCurrentFrame().GetSkImage(), GL_NEAREST);
+
+ EXPECT_EQ(mailbox.name[0], 1);
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+
+ EXPECT_CALL(gl_, GenMailboxCHROMIUM(mailbox.name))
+ .Times(0); // GenMailboxCHROMIUM must not be called!
+
+ mailbox.name[0] = 0;
+ SharedGpuContext::ContextProviderWrapper()->Utils()->GetMailboxForSkImage(
+ mailbox, image->PaintImageForCurrentFrame().GetSkImage(), GL_NEAREST);
+ EXPECT_EQ(mailbox.name[0], 1);
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+}
+
+TEST_F(MailboxSharedGpuContextTest, MailboxCacheSurvivesSkiaRecycling) {
+ IntSize size(10, 10);
+ std::unique_ptr<CanvasResourceProvider> resource_provider =
+ CanvasResourceProvider::Create(
+ size, CanvasResourceProvider::kAcceleratedResourceUsage,
+ SharedGpuContext::ContextProviderWrapper());
+ EXPECT_TRUE(resource_provider && resource_provider->IsValid());
+ scoped_refptr<StaticBitmapImage> image = resource_provider->Snapshot();
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+
+ FakeMailboxGenerator mailboxGenerator;
+ gpu::Mailbox mailbox;
+ mailbox.name[0] = 0;
+
+ EXPECT_CALL(gl_, GenMailboxCHROMIUM(mailbox.name))
+ .Times(1)
+ .WillOnce(testing::Invoke(&mailboxGenerator,
+ &FakeMailboxGenerator::GenMailbox));
+
+ SharedGpuContext::ContextProviderWrapper()->Utils()->GetMailboxForSkImage(
+ mailbox, image->PaintImageForCurrentFrame().GetSkImage(), GL_NEAREST);
+
+ EXPECT_EQ(mailbox.name[0], 1);
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+
+ // Destroy image and surface to return texture to recleable resource pool
+ image = nullptr;
+ resource_provider = nullptr;
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+
+ // Re-creating surface should recycle the old GrTexture inside skia
+ resource_provider = CanvasResourceProvider::Create(
+ size, CanvasResourceProvider::kAcceleratedResourceUsage,
+ SharedGpuContext::ContextProviderWrapper());
+ EXPECT_TRUE(resource_provider && resource_provider->IsValid());
+ image = resource_provider->Snapshot();
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+
+ EXPECT_CALL(gl_, GenMailboxCHROMIUM(mailbox.name))
+ .Times(0); // GenMailboxCHROMIUM must not be called!
+
+ mailbox.name[0] = 0;
+ SharedGpuContext::ContextProviderWrapper()->Utils()->GetMailboxForSkImage(
+ mailbox, image->PaintImageForCurrentFrame().GetSkImage(), GL_NEAREST);
+ EXPECT_EQ(mailbox.name[0], 1);
+
+ testing::Mock::VerifyAndClearExpectations(&gl_);
+}
+
+} // unnamed namespace
+
+} // blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion.cc
new file mode 100644
index 00000000000..bdaa943e701
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion.cc
@@ -0,0 +1,3260 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion.h"
+
+#include <memory>
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/graphics/cpu/arm/webgl_image_conversion_neon.h"
+#include "third_party/blink/renderer/platform/graphics/cpu/mips/webgl_image_conversion_msa.h"
+#include "third_party/blink/renderer/platform/graphics/cpu/x86/webgl_image_conversion_sse.h"
+#include "third_party/blink/renderer/platform/graphics/image_observer.h"
+#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
+#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h"
+#include "third_party/blink/renderer/platform/wtf/checked_numeric.h"
+#include "third_party/skia/include/core/SkImage.h"
+
+namespace blink {
+
+namespace {
+
+const float kMaxInt8Value = INT8_MAX;
+const float kMaxUInt8Value = UINT8_MAX;
+const float kMaxInt16Value = INT16_MAX;
+const float kMaxUInt16Value = UINT16_MAX;
+const double kMaxInt32Value = INT32_MAX;
+const double kMaxUInt32Value = UINT32_MAX;
+
+int8_t ClampMin(int8_t value) {
+ const static int8_t kMinInt8Value = INT8_MIN + 1;
+ return value < kMinInt8Value ? kMinInt8Value : value;
+}
+
+int16_t ClampMin(int16_t value) {
+ const static int16_t kMinInt16Value = INT16_MIN + 1;
+ return value < kMinInt16Value ? kMinInt16Value : value;
+}
+
+int32_t ClampMin(int32_t value) {
+ const static int32_t kMinInt32Value = INT32_MIN + 1;
+ return value < kMinInt32Value ? kMinInt32Value : value;
+}
+
+// Return kDataFormatNumFormats if format/type combination is invalid.
+WebGLImageConversion::DataFormat GetDataFormat(GLenum destination_format,
+ GLenum destination_type) {
+ WebGLImageConversion::DataFormat dst_format =
+ WebGLImageConversion::kDataFormatRGBA8;
+ switch (destination_type) {
+ case GL_BYTE:
+ switch (destination_format) {
+ case GL_RED:
+ case GL_RED_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatR8_S;
+ break;
+ case GL_RG:
+ case GL_RG_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatRG8_S;
+ break;
+ case GL_RGB:
+ case GL_RGB_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatRGB8_S;
+ break;
+ case GL_RGBA:
+ case GL_RGBA_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatRGBA8_S;
+ break;
+ default:
+ return WebGLImageConversion::kDataFormatNumFormats;
+ }
+ break;
+ case GL_UNSIGNED_BYTE:
+ switch (destination_format) {
+ case GL_RGB:
+ case GL_RGB_INTEGER:
+ case GL_SRGB_EXT:
+ dst_format = WebGLImageConversion::kDataFormatRGB8;
+ break;
+ case GL_RGBA:
+ case GL_RGBA_INTEGER:
+ case GL_SRGB_ALPHA_EXT:
+ dst_format = WebGLImageConversion::kDataFormatRGBA8;
+ break;
+ case GL_ALPHA:
+ dst_format = WebGLImageConversion::kDataFormatA8;
+ break;
+ case GL_LUMINANCE:
+ case GL_RED:
+ case GL_RED_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatR8;
+ break;
+ case GL_RG:
+ case GL_RG_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatRG8;
+ break;
+ case GL_LUMINANCE_ALPHA:
+ dst_format = WebGLImageConversion::kDataFormatRA8;
+ break;
+ default:
+ return WebGLImageConversion::kDataFormatNumFormats;
+ }
+ break;
+ case GL_SHORT:
+ switch (destination_format) {
+ case GL_RED_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatR16_S;
+ break;
+ case GL_RG_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatRG16_S;
+ break;
+ case GL_RGB_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatRGB16_S;
+ break;
+ case GL_RGBA_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatRGBA16_S;
+ break;
+ default:
+ return WebGLImageConversion::kDataFormatNumFormats;
+ }
+ break;
+ case GL_UNSIGNED_SHORT:
+ switch (destination_format) {
+ case GL_RED_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatR16;
+ break;
+ case GL_DEPTH_COMPONENT:
+ dst_format = WebGLImageConversion::kDataFormatD16;
+ break;
+ case GL_RG_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatRG16;
+ break;
+ case GL_RGB_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatRGB16;
+ break;
+ case GL_RGBA_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatRGBA16;
+ break;
+ default:
+ return WebGLImageConversion::kDataFormatNumFormats;
+ }
+ break;
+ case GL_INT:
+ switch (destination_format) {
+ case GL_RED_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatR32_S;
+ break;
+ case GL_RG_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatRG32_S;
+ break;
+ case GL_RGB_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatRGB32_S;
+ break;
+ case GL_RGBA_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatRGBA32_S;
+ break;
+ default:
+ return WebGLImageConversion::kDataFormatNumFormats;
+ }
+ break;
+ case GL_UNSIGNED_INT:
+ switch (destination_format) {
+ case GL_RED_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatR32;
+ break;
+ case GL_DEPTH_COMPONENT:
+ dst_format = WebGLImageConversion::kDataFormatD32;
+ break;
+ case GL_RG_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatRG32;
+ break;
+ case GL_RGB_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatRGB32;
+ break;
+ case GL_RGBA_INTEGER:
+ dst_format = WebGLImageConversion::kDataFormatRGBA32;
+ break;
+ default:
+ return WebGLImageConversion::kDataFormatNumFormats;
+ }
+ break;
+ case GL_HALF_FLOAT_OES: // OES_texture_half_float
+ case GL_HALF_FLOAT:
+ switch (destination_format) {
+ case GL_RGBA:
+ dst_format = WebGLImageConversion::kDataFormatRGBA16F;
+ break;
+ case GL_RGB:
+ dst_format = WebGLImageConversion::kDataFormatRGB16F;
+ break;
+ case GL_RG:
+ dst_format = WebGLImageConversion::kDataFormatRG16F;
+ break;
+ case GL_ALPHA:
+ dst_format = WebGLImageConversion::kDataFormatA16F;
+ break;
+ case GL_LUMINANCE:
+ case GL_RED:
+ dst_format = WebGLImageConversion::kDataFormatR16F;
+ break;
+ case GL_LUMINANCE_ALPHA:
+ dst_format = WebGLImageConversion::kDataFormatRA16F;
+ break;
+ default:
+ return WebGLImageConversion::kDataFormatNumFormats;
+ }
+ break;
+ case GL_FLOAT: // OES_texture_float
+ switch (destination_format) {
+ case GL_RGBA:
+ dst_format = WebGLImageConversion::kDataFormatRGBA32F;
+ break;
+ case GL_RGB:
+ dst_format = WebGLImageConversion::kDataFormatRGB32F;
+ break;
+ case GL_RG:
+ dst_format = WebGLImageConversion::kDataFormatRG32F;
+ break;
+ case GL_ALPHA:
+ dst_format = WebGLImageConversion::kDataFormatA32F;
+ break;
+ case GL_LUMINANCE:
+ case GL_RED:
+ dst_format = WebGLImageConversion::kDataFormatR32F;
+ break;
+ case GL_DEPTH_COMPONENT:
+ dst_format = WebGLImageConversion::kDataFormatD32F;
+ break;
+ case GL_LUMINANCE_ALPHA:
+ dst_format = WebGLImageConversion::kDataFormatRA32F;
+ break;
+ default:
+ return WebGLImageConversion::kDataFormatNumFormats;
+ }
+ break;
+ case GL_UNSIGNED_SHORT_4_4_4_4:
+ dst_format = WebGLImageConversion::kDataFormatRGBA4444;
+ break;
+ case GL_UNSIGNED_SHORT_5_5_5_1:
+ dst_format = WebGLImageConversion::kDataFormatRGBA5551;
+ break;
+ case GL_UNSIGNED_SHORT_5_6_5:
+ dst_format = WebGLImageConversion::kDataFormatRGB565;
+ break;
+ case GL_UNSIGNED_INT_5_9_9_9_REV:
+ dst_format = WebGLImageConversion::kDataFormatRGB5999;
+ break;
+ case GL_UNSIGNED_INT_24_8:
+ dst_format = WebGLImageConversion::kDataFormatDS24_8;
+ break;
+ case GL_UNSIGNED_INT_10F_11F_11F_REV:
+ dst_format = WebGLImageConversion::kDataFormatRGB10F11F11F;
+ break;
+ case GL_UNSIGNED_INT_2_10_10_10_REV:
+ dst_format = WebGLImageConversion::kDataFormatRGBA2_10_10_10;
+ break;
+ default:
+ return WebGLImageConversion::kDataFormatNumFormats;
+ }
+ return dst_format;
+}
+
+// The following Float to Half-Float conversion code is from the implementation
+// of ftp://www.fox-toolkit.org/pub/fasthalffloatconversion.pdf, "Fast Half
+// Float Conversions" by Jeroen van der Zijp, November 2008 (Revised September
+// 2010). Specially, the basetable[512] and shifttable[512] are generated as
+// follows:
+/*
+unsigned short basetable[512];
+unsigned char shifttable[512];
+
+void generatetables(){
+ unsigned int i;
+ int e;
+ for (i = 0; i < 256; ++i){
+ e = i - 127;
+ if (e < -24){ // Very small numbers map to zero
+ basetable[i | 0x000] = 0x0000;
+ basetable[i | 0x100] = 0x8000;
+ shifttable[i | 0x000] = 24;
+ shifttable[i | 0x100] = 24;
+ }
+ else if (e < -14) { // Small numbers map to denorms
+ basetable[i | 0x000] = (0x0400>>(-e-14));
+ basetable[i | 0x100] = (0x0400>>(-e-14)) | 0x8000;
+ shifttable[i | 0x000] = -e-1;
+ shifttable[i | 0x100] = -e-1;
+ }
+ else if (e <= 15){ // Normal numbers just lose precision
+ basetable[i | 0x000] = ((e+15)<<10);
+ basetable[i| 0x100] = ((e+15)<<10) | 0x8000;
+ shifttable[i|0x000] = 13;
+ shifttable[i|0x100] = 13;
+ }
+ else if (e<128){ // Large numbers map to Infinity
+ basetable[i|0x000] = 0x7C00;
+ basetable[i|0x100] = 0xFC00;
+ shifttable[i|0x000] = 24;
+ shifttable[i|0x100] = 24;
+ }
+ else { // Infinity and NaN's stay Infinity and NaN's
+ basetable[i|0x000] = 0x7C00;
+ basetable[i|0x100] = 0xFC00;
+ shifttable[i|0x000] = 13;
+ shifttable[i|0x100] = 13;
+ }
+ }
+}
+*/
+
+unsigned short g_base_table[512] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 2, 4, 8, 16, 32, 64,
+ 128, 256, 512, 1024, 2048, 3072, 4096, 5120, 6144, 7168, 8192,
+ 9216, 10240, 11264, 12288, 13312, 14336, 15360, 16384, 17408, 18432, 19456,
+ 20480, 21504, 22528, 23552, 24576, 25600, 26624, 27648, 28672, 29696, 30720,
+ 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
+ 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
+ 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
+ 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
+ 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
+ 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
+ 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
+ 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
+ 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
+ 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
+ 31744, 31744, 31744, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
+ 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
+ 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
+ 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
+ 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
+ 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
+ 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
+ 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
+ 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
+ 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32769, 32770, 32772, 32776,
+ 32784, 32800, 32832, 32896, 33024, 33280, 33792, 34816, 35840, 36864, 37888,
+ 38912, 39936, 40960, 41984, 43008, 44032, 45056, 46080, 47104, 48128, 49152,
+ 50176, 51200, 52224, 53248, 54272, 55296, 56320, 57344, 58368, 59392, 60416,
+ 61440, 62464, 63488, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
+ 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
+ 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
+ 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
+ 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
+ 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
+ 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
+ 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
+ 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
+ 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
+ 64512, 64512, 64512, 64512, 64512, 64512};
+
+unsigned char g_shift_table[512] = {
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 13, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 23, 22,
+ 21, 20, 19, 18, 17, 16, 15, 14, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 13};
+
+unsigned short ConvertFloatToHalfFloat(float f) {
+ unsigned temp = *(reinterpret_cast<unsigned*>(&f));
+ unsigned signexp = (temp >> 23) & 0x1ff;
+ return g_base_table[signexp] +
+ ((temp & 0x007fffff) >> g_shift_table[signexp]);
+}
+
+/* BEGIN CODE SHARED WITH MOZILLA FIREFOX */
+
+// The following packing and unpacking routines are expressed in terms of
+// function templates and inline functions to achieve generality and speedup.
+// Explicit template specializations correspond to the cases that would occur.
+// Some code are merged back from Mozilla code in
+// http://mxr.mozilla.org/mozilla-central/source/content/canvas/src/WebGLTexelConversions.h
+
+//----------------------------------------------------------------------
+// Pixel unpacking routines.
+template <int format, typename SourceType, typename DstType>
+void Unpack(const SourceType*, DstType*, unsigned) {
+ NOTREACHED();
+}
+
+template <>
+void Unpack<WebGLImageConversion::kDataFormatARGB8, uint8_t, uint8_t>(
+ const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[1];
+ destination[1] = source[2];
+ destination[2] = source[3];
+ destination[3] = source[0];
+ source += 4;
+ destination += 4;
+ }
+}
+
+template <>
+void Unpack<WebGLImageConversion::kDataFormatABGR8, uint8_t, uint8_t>(
+ const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[3];
+ destination[1] = source[2];
+ destination[2] = source[1];
+ destination[3] = source[0];
+ source += 4;
+ destination += 4;
+ }
+}
+
+template <>
+void Unpack<WebGLImageConversion::kDataFormatBGRA8, uint8_t, uint8_t>(
+ const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ const uint32_t* source32 = reinterpret_cast_ptr<const uint32_t*>(source);
+ uint32_t* destination32 = reinterpret_cast_ptr<uint32_t*>(destination);
+
+#if defined(ARCH_CPU_X86_FAMILY)
+ SIMD::UnpackOneRowOfBGRA8LittleToRGBA8(source32, destination32,
+ pixels_per_row);
+#endif
+#if HAVE_MIPS_MSA_INTRINSICS
+ SIMD::unpackOneRowOfBGRA8LittleToRGBA8MSA(source32, destination32,
+ pixels_per_row);
+#endif
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ uint32_t bgra = source32[i];
+#if defined(ARCH_CPU_BIG_ENDIAN)
+ uint32_t brMask = 0xff00ff00;
+ uint32_t gaMask = 0x00ff00ff;
+#else
+ uint32_t br_mask = 0x00ff00ff;
+ uint32_t ga_mask = 0xff00ff00;
+#endif
+ uint32_t rgba =
+ (((bgra >> 16) | (bgra << 16)) & br_mask) | (bgra & ga_mask);
+ destination32[i] = rgba;
+ }
+}
+
+template <>
+void Unpack<WebGLImageConversion::kDataFormatRGBA5551, uint16_t, uint8_t>(
+ const uint16_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+#if defined(ARCH_CPU_X86_FAMILY)
+ SIMD::UnpackOneRowOfRGBA5551LittleToRGBA8(source, destination,
+ pixels_per_row);
+#endif
+#if WTF_CPU_ARM_NEON
+ SIMD::UnpackOneRowOfRGBA5551ToRGBA8(source, destination, pixels_per_row);
+#endif
+#if HAVE_MIPS_MSA_INTRINSICS
+ SIMD::unpackOneRowOfRGBA5551ToRGBA8MSA(source, destination, pixels_per_row);
+#endif
+
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ uint16_t packed_value = source[0];
+ uint8_t r = packed_value >> 11;
+ uint8_t g = (packed_value >> 6) & 0x1F;
+ uint8_t b = (packed_value >> 1) & 0x1F;
+ destination[0] = (r << 3) | (r & 0x7);
+ destination[1] = (g << 3) | (g & 0x7);
+ destination[2] = (b << 3) | (b & 0x7);
+ destination[3] = (packed_value & 0x1) ? 0xFF : 0x0;
+ source += 1;
+ destination += 4;
+ }
+}
+
+template <>
+void Unpack<WebGLImageConversion::kDataFormatRGBA4444, uint16_t, uint8_t>(
+ const uint16_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+#if defined(ARCH_CPU_X86_FAMILY)
+ SIMD::UnpackOneRowOfRGBA4444LittleToRGBA8(source, destination,
+ pixels_per_row);
+#endif
+#if WTF_CPU_ARM_NEON
+ SIMD::UnpackOneRowOfRGBA4444ToRGBA8(source, destination, pixels_per_row);
+#endif
+#if HAVE_MIPS_MSA_INTRINSICS
+ SIMD::unpackOneRowOfRGBA4444ToRGBA8MSA(source, destination, pixels_per_row);
+#endif
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ uint16_t packed_value = source[0];
+ uint8_t r = packed_value >> 12;
+ uint8_t g = (packed_value >> 8) & 0x0F;
+ uint8_t b = (packed_value >> 4) & 0x0F;
+ uint8_t a = packed_value & 0x0F;
+ destination[0] = r << 4 | r;
+ destination[1] = g << 4 | g;
+ destination[2] = b << 4 | b;
+ destination[3] = a << 4 | a;
+ source += 1;
+ destination += 4;
+ }
+}
+
+template <>
+void Unpack<WebGLImageConversion::kDataFormatRA8, uint8_t, uint8_t>(
+ const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[0];
+ destination[1] = source[0];
+ destination[2] = source[0];
+ destination[3] = source[1];
+ source += 2;
+ destination += 4;
+ }
+}
+
+template <>
+void Unpack<WebGLImageConversion::kDataFormatAR8, uint8_t, uint8_t>(
+ const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[1];
+ destination[1] = source[1];
+ destination[2] = source[1];
+ destination[3] = source[0];
+ source += 2;
+ destination += 4;
+ }
+}
+
+template <>
+void Unpack<WebGLImageConversion::kDataFormatRGBA8, uint8_t, float>(
+ const uint8_t* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ const float kScaleFactor = 1.0f / 255.0f;
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[0] * kScaleFactor;
+ destination[1] = source[1] * kScaleFactor;
+ destination[2] = source[2] * kScaleFactor;
+ destination[3] = source[3] * kScaleFactor;
+ source += 4;
+ destination += 4;
+ }
+}
+
+template <>
+void Unpack<WebGLImageConversion::kDataFormatBGRA8, uint8_t, float>(
+ const uint8_t* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ const float kScaleFactor = 1.0f / 255.0f;
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[2] * kScaleFactor;
+ destination[1] = source[1] * kScaleFactor;
+ destination[2] = source[0] * kScaleFactor;
+ destination[3] = source[3] * kScaleFactor;
+ source += 4;
+ destination += 4;
+ }
+}
+
+template <>
+void Unpack<WebGLImageConversion::kDataFormatABGR8, uint8_t, float>(
+ const uint8_t* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ const float kScaleFactor = 1.0f / 255.0f;
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[3] * kScaleFactor;
+ destination[1] = source[2] * kScaleFactor;
+ destination[2] = source[1] * kScaleFactor;
+ destination[3] = source[0] * kScaleFactor;
+ source += 4;
+ destination += 4;
+ }
+}
+
+template <>
+void Unpack<WebGLImageConversion::kDataFormatARGB8, uint8_t, float>(
+ const uint8_t* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ const float kScaleFactor = 1.0f / 255.0f;
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[1] * kScaleFactor;
+ destination[1] = source[2] * kScaleFactor;
+ destination[2] = source[3] * kScaleFactor;
+ destination[3] = source[0] * kScaleFactor;
+ source += 4;
+ destination += 4;
+ }
+}
+
+template <>
+void Unpack<WebGLImageConversion::kDataFormatRA32F, float, float>(
+ const float* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[0];
+ destination[1] = source[0];
+ destination[2] = source[0];
+ destination[3] = source[1];
+ source += 2;
+ destination += 4;
+ }
+}
+
+template <>
+void Unpack<WebGLImageConversion::kDataFormatRGBA2_10_10_10, uint32_t, float>(
+ const uint32_t* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ static const float kRgbScaleFactor = 1.0f / 1023.0f;
+ static const float kAlphaScaleFactor = 1.0f / 3.0f;
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ uint32_t packed_value = source[0];
+ destination[0] = static_cast<float>(packed_value & 0x3FF) * kRgbScaleFactor;
+ destination[1] =
+ static_cast<float>((packed_value >> 10) & 0x3FF) * kRgbScaleFactor;
+ destination[2] =
+ static_cast<float>((packed_value >> 20) & 0x3FF) * kRgbScaleFactor;
+ destination[3] = static_cast<float>(packed_value >> 30) * kAlphaScaleFactor;
+ source += 1;
+ destination += 4;
+ }
+}
+
+//----------------------------------------------------------------------
+// Pixel packing routines.
+//
+
+template <int format, int alphaOp, typename SourceType, typename DstType>
+void Pack(const SourceType*, DstType*, unsigned) {
+ NOTREACHED();
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatA8,
+ WebGLImageConversion::kAlphaDoNothing,
+ uint8_t,
+ uint8_t>(const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[3];
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatR8,
+ WebGLImageConversion::kAlphaDoNothing,
+ uint8_t,
+ uint8_t>(const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[0];
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatR8,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ uint8_t,
+ uint8_t>(const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] / 255.0f;
+ uint8_t source_r =
+ static_cast<uint8_t>(static_cast<float>(source[0]) * scale_factor);
+ destination[0] = source_r;
+ source += 4;
+ destination += 1;
+ }
+}
+
+// FIXME: this routine is lossy and must be removed.
+template <>
+void Pack<WebGLImageConversion::kDataFormatR8,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ uint8_t,
+ uint8_t>(const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+#if defined(ARCH_CPU_X86_FAMILY)
+ SIMD::PackOneRowOfRGBA8LittleToR8(source, destination, pixels_per_row);
+#endif
+#if HAVE_MIPS_MSA_INTRINSICS
+ SIMD::packOneRowOfRGBA8LittleToR8MSA(source, destination, pixels_per_row);
+#endif
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 255.0f / source[3] : 1.0f;
+ uint8_t source_r =
+ static_cast<uint8_t>(static_cast<float>(source[0]) * scale_factor);
+ destination[0] = source_r;
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRA8,
+ WebGLImageConversion::kAlphaDoNothing,
+ uint8_t,
+ uint8_t>(const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[0];
+ destination[1] = source[3];
+ source += 4;
+ destination += 2;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRA8,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ uint8_t,
+ uint8_t>(const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] / 255.0f;
+ uint8_t source_r =
+ static_cast<uint8_t>(static_cast<float>(source[0]) * scale_factor);
+ destination[0] = source_r;
+ destination[1] = source[3];
+ source += 4;
+ destination += 2;
+ }
+}
+
+// FIXME: this routine is lossy and must be removed.
+template <>
+void Pack<WebGLImageConversion::kDataFormatRA8,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ uint8_t,
+ uint8_t>(const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+#if defined(ARCH_CPU_X86_FAMILY)
+ SIMD::PackOneRowOfRGBA8LittleToRA8(source, destination, pixels_per_row);
+#endif
+#if HAVE_MIPS_MSA_INTRINSICS
+ SIMD::packOneRowOfRGBA8LittleToRA8MSA(source, destination, pixels_per_row);
+#endif
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 255.0f / source[3] : 1.0f;
+ uint8_t source_r =
+ static_cast<uint8_t>(static_cast<float>(source[0]) * scale_factor);
+ destination[0] = source_r;
+ destination[1] = source[3];
+ source += 4;
+ destination += 2;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGB8,
+ WebGLImageConversion::kAlphaDoNothing,
+ uint8_t,
+ uint8_t>(const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[0];
+ destination[1] = source[1];
+ destination[2] = source[2];
+ source += 4;
+ destination += 3;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGB8,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ uint8_t,
+ uint8_t>(const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] / 255.0f;
+ uint8_t source_r =
+ static_cast<uint8_t>(static_cast<float>(source[0]) * scale_factor);
+ uint8_t source_g =
+ static_cast<uint8_t>(static_cast<float>(source[1]) * scale_factor);
+ uint8_t source_b =
+ static_cast<uint8_t>(static_cast<float>(source[2]) * scale_factor);
+ destination[0] = source_r;
+ destination[1] = source_g;
+ destination[2] = source_b;
+ source += 4;
+ destination += 3;
+ }
+}
+
+// FIXME: this routine is lossy and must be removed.
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGB8,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ uint8_t,
+ uint8_t>(const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 255.0f / source[3] : 1.0f;
+ uint8_t source_r =
+ static_cast<uint8_t>(static_cast<float>(source[0]) * scale_factor);
+ uint8_t source_g =
+ static_cast<uint8_t>(static_cast<float>(source[1]) * scale_factor);
+ uint8_t source_b =
+ static_cast<uint8_t>(static_cast<float>(source[2]) * scale_factor);
+ destination[0] = source_r;
+ destination[1] = source_g;
+ destination[2] = source_b;
+ source += 4;
+ destination += 3;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA8,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ uint8_t,
+ uint8_t>(const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] / 255.0f;
+ uint8_t source_r =
+ static_cast<uint8_t>(static_cast<float>(source[0]) * scale_factor);
+ uint8_t source_g =
+ static_cast<uint8_t>(static_cast<float>(source[1]) * scale_factor);
+ uint8_t source_b =
+ static_cast<uint8_t>(static_cast<float>(source[2]) * scale_factor);
+ destination[0] = source_r;
+ destination[1] = source_g;
+ destination[2] = source_b;
+ destination[3] = source[3];
+ source += 4;
+ destination += 4;
+ }
+}
+
+// FIXME: this routine is lossy and must be removed.
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA8,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ uint8_t,
+ uint8_t>(const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+#if defined(ARCH_CPU_X86_FAMILY)
+ SIMD::PackOneRowOfRGBA8LittleToRGBA8(source, destination, pixels_per_row);
+#endif
+#if HAVE_MIPS_MSA_INTRINSICS
+ SIMD::packOneRowOfRGBA8LittleToRGBA8MSA(source, destination, pixels_per_row);
+#endif
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 255.0f / source[3] : 1.0f;
+ uint8_t source_r =
+ static_cast<uint8_t>(static_cast<float>(source[0]) * scale_factor);
+ uint8_t source_g =
+ static_cast<uint8_t>(static_cast<float>(source[1]) * scale_factor);
+ uint8_t source_b =
+ static_cast<uint8_t>(static_cast<float>(source[2]) * scale_factor);
+ destination[0] = source_r;
+ destination[1] = source_g;
+ destination[2] = source_b;
+ destination[3] = source[3];
+ source += 4;
+ destination += 4;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA4444,
+ WebGLImageConversion::kAlphaDoNothing,
+ uint8_t,
+ uint16_t>(const uint8_t* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+#if WTF_CPU_ARM_NEON
+ SIMD::PackOneRowOfRGBA8ToUnsignedShort4444(source, destination,
+ pixels_per_row);
+#endif
+#if HAVE_MIPS_MSA_INTRINSICS
+ SIMD::packOneRowOfRGBA8ToUnsignedShort4444MSA(source, destination,
+ pixels_per_row);
+#endif
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ *destination = (((source[0] & 0xF0) << 8) | ((source[1] & 0xF0) << 4) |
+ (source[2] & 0xF0) | (source[3] >> 4));
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA4444,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ uint8_t,
+ uint16_t>(const uint8_t* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] / 255.0f;
+ uint8_t source_r =
+ static_cast<uint8_t>(static_cast<float>(source[0]) * scale_factor);
+ uint8_t source_g =
+ static_cast<uint8_t>(static_cast<float>(source[1]) * scale_factor);
+ uint8_t source_b =
+ static_cast<uint8_t>(static_cast<float>(source[2]) * scale_factor);
+ *destination = (((source_r & 0xF0) << 8) | ((source_g & 0xF0) << 4) |
+ (source_b & 0xF0) | (source[3] >> 4));
+ source += 4;
+ destination += 1;
+ }
+}
+
+// FIXME: this routine is lossy and must be removed.
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA4444,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ uint8_t,
+ uint16_t>(const uint8_t* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 255.0f / source[3] : 1.0f;
+ uint8_t source_r =
+ static_cast<uint8_t>(static_cast<float>(source[0]) * scale_factor);
+ uint8_t source_g =
+ static_cast<uint8_t>(static_cast<float>(source[1]) * scale_factor);
+ uint8_t source_b =
+ static_cast<uint8_t>(static_cast<float>(source[2]) * scale_factor);
+ *destination = (((source_r & 0xF0) << 8) | ((source_g & 0xF0) << 4) |
+ (source_b & 0xF0) | (source[3] >> 4));
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA5551,
+ WebGLImageConversion::kAlphaDoNothing,
+ uint8_t,
+ uint16_t>(const uint8_t* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+#if WTF_CPU_ARM_NEON
+ SIMD::PackOneRowOfRGBA8ToUnsignedShort5551(source, destination,
+ pixels_per_row);
+#endif
+#if HAVE_MIPS_MSA_INTRINSICS
+ SIMD::packOneRowOfRGBA8ToUnsignedShort5551MSA(source, destination,
+ pixels_per_row);
+#endif
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ *destination = (((source[0] & 0xF8) << 8) | ((source[1] & 0xF8) << 3) |
+ ((source[2] & 0xF8) >> 2) | (source[3] >> 7));
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA5551,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ uint8_t,
+ uint16_t>(const uint8_t* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] / 255.0f;
+ uint8_t source_r =
+ static_cast<uint8_t>(static_cast<float>(source[0]) * scale_factor);
+ uint8_t source_g =
+ static_cast<uint8_t>(static_cast<float>(source[1]) * scale_factor);
+ uint8_t source_b =
+ static_cast<uint8_t>(static_cast<float>(source[2]) * scale_factor);
+ *destination = (((source_r & 0xF8) << 8) | ((source_g & 0xF8) << 3) |
+ ((source_b & 0xF8) >> 2) | (source[3] >> 7));
+ source += 4;
+ destination += 1;
+ }
+}
+
+// FIXME: this routine is lossy and must be removed.
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA5551,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ uint8_t,
+ uint16_t>(const uint8_t* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 255.0f / source[3] : 1.0f;
+ uint8_t source_r =
+ static_cast<uint8_t>(static_cast<float>(source[0]) * scale_factor);
+ uint8_t source_g =
+ static_cast<uint8_t>(static_cast<float>(source[1]) * scale_factor);
+ uint8_t source_b =
+ static_cast<uint8_t>(static_cast<float>(source[2]) * scale_factor);
+ *destination = (((source_r & 0xF8) << 8) | ((source_g & 0xF8) << 3) |
+ ((source_b & 0xF8) >> 2) | (source[3] >> 7));
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGB565,
+ WebGLImageConversion::kAlphaDoNothing,
+ uint8_t,
+ uint16_t>(const uint8_t* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+#if WTF_CPU_ARM_NEON
+ SIMD::PackOneRowOfRGBA8ToUnsignedShort565(source, destination,
+ pixels_per_row);
+#endif
+#if HAVE_MIPS_MSA_INTRINSICS
+ SIMD::packOneRowOfRGBA8ToUnsignedShort565MSA(source, destination,
+ pixels_per_row);
+#endif
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ *destination = (((source[0] & 0xF8) << 8) | ((source[1] & 0xFC) << 3) |
+ ((source[2] & 0xF8) >> 3));
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGB565,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ uint8_t,
+ uint16_t>(const uint8_t* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] / 255.0f;
+ uint8_t source_r =
+ static_cast<uint8_t>(static_cast<float>(source[0]) * scale_factor);
+ uint8_t source_g =
+ static_cast<uint8_t>(static_cast<float>(source[1]) * scale_factor);
+ uint8_t source_b =
+ static_cast<uint8_t>(static_cast<float>(source[2]) * scale_factor);
+ *destination = (((source_r & 0xF8) << 8) | ((source_g & 0xFC) << 3) |
+ ((source_b & 0xF8) >> 3));
+ source += 4;
+ destination += 1;
+ }
+}
+
+// FIXME: this routine is lossy and must be removed.
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGB565,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ uint8_t,
+ uint16_t>(const uint8_t* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 255.0f / source[3] : 1.0f;
+ uint8_t source_r =
+ static_cast<uint8_t>(static_cast<float>(source[0]) * scale_factor);
+ uint8_t source_g =
+ static_cast<uint8_t>(static_cast<float>(source[1]) * scale_factor);
+ uint8_t source_b =
+ static_cast<uint8_t>(static_cast<float>(source[2]) * scale_factor);
+ *destination = (((source_r & 0xF8) << 8) | ((source_g & 0xFC) << 3) |
+ ((source_b & 0xF8) >> 3));
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGB32F,
+ WebGLImageConversion::kAlphaDoNothing,
+ float,
+ float>(const float* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[0];
+ destination[1] = source[1];
+ destination[2] = source[2];
+ source += 4;
+ destination += 3;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGB32F,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ float,
+ float>(const float* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3];
+ destination[0] = source[0] * scale_factor;
+ destination[1] = source[1] * scale_factor;
+ destination[2] = source[2] * scale_factor;
+ source += 4;
+ destination += 3;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGB32F,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ float,
+ float>(const float* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 1.0f / source[3] : 1.0f;
+ destination[0] = source[0] * scale_factor;
+ destination[1] = source[1] * scale_factor;
+ destination[2] = source[2] * scale_factor;
+ source += 4;
+ destination += 3;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA32F,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ float,
+ float>(const float* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3];
+ destination[0] = source[0] * scale_factor;
+ destination[1] = source[1] * scale_factor;
+ destination[2] = source[2] * scale_factor;
+ destination[3] = source[3];
+ source += 4;
+ destination += 4;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA32F,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ float,
+ float>(const float* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 1.0f / source[3] : 1.0f;
+ destination[0] = source[0] * scale_factor;
+ destination[1] = source[1] * scale_factor;
+ destination[2] = source[2] * scale_factor;
+ destination[3] = source[3];
+ source += 4;
+ destination += 4;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatA32F,
+ WebGLImageConversion::kAlphaDoNothing,
+ float,
+ float>(const float* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[3];
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatR32F,
+ WebGLImageConversion::kAlphaDoNothing,
+ float,
+ float>(const float* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[0];
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatR32F,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ float,
+ float>(const float* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3];
+ destination[0] = source[0] * scale_factor;
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatR32F,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ float,
+ float>(const float* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 1.0f / source[3] : 1.0f;
+ destination[0] = source[0] * scale_factor;
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRA32F,
+ WebGLImageConversion::kAlphaDoNothing,
+ float,
+ float>(const float* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[0];
+ destination[1] = source[3];
+ source += 4;
+ destination += 2;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRA32F,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ float,
+ float>(const float* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3];
+ destination[0] = source[0] * scale_factor;
+ destination[1] = source[3];
+ source += 4;
+ destination += 2;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRA32F,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ float,
+ float>(const float* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 1.0f / source[3] : 1.0f;
+ destination[0] = source[0] * scale_factor;
+ destination[1] = source[3];
+ source += 4;
+ destination += 2;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA16F,
+ WebGLImageConversion::kAlphaDoNothing,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = ConvertFloatToHalfFloat(source[0]);
+ destination[1] = ConvertFloatToHalfFloat(source[1]);
+ destination[2] = ConvertFloatToHalfFloat(source[2]);
+ destination[3] = ConvertFloatToHalfFloat(source[3]);
+ source += 4;
+ destination += 4;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA16F,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3];
+ destination[0] = ConvertFloatToHalfFloat(source[0] * scale_factor);
+ destination[1] = ConvertFloatToHalfFloat(source[1] * scale_factor);
+ destination[2] = ConvertFloatToHalfFloat(source[2] * scale_factor);
+ destination[3] = ConvertFloatToHalfFloat(source[3]);
+ source += 4;
+ destination += 4;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA16F,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 1.0f / source[3] : 1.0f;
+ destination[0] = ConvertFloatToHalfFloat(source[0] * scale_factor);
+ destination[1] = ConvertFloatToHalfFloat(source[1] * scale_factor);
+ destination[2] = ConvertFloatToHalfFloat(source[2] * scale_factor);
+ destination[3] = ConvertFloatToHalfFloat(source[3]);
+ source += 4;
+ destination += 4;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGB16F,
+ WebGLImageConversion::kAlphaDoNothing,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = ConvertFloatToHalfFloat(source[0]);
+ destination[1] = ConvertFloatToHalfFloat(source[1]);
+ destination[2] = ConvertFloatToHalfFloat(source[2]);
+ source += 4;
+ destination += 3;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGB16F,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3];
+ destination[0] = ConvertFloatToHalfFloat(source[0] * scale_factor);
+ destination[1] = ConvertFloatToHalfFloat(source[1] * scale_factor);
+ destination[2] = ConvertFloatToHalfFloat(source[2] * scale_factor);
+ source += 4;
+ destination += 3;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGB16F,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 1.0f / source[3] : 1.0f;
+ destination[0] = ConvertFloatToHalfFloat(source[0] * scale_factor);
+ destination[1] = ConvertFloatToHalfFloat(source[1] * scale_factor);
+ destination[2] = ConvertFloatToHalfFloat(source[2] * scale_factor);
+ source += 4;
+ destination += 3;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRA16F,
+ WebGLImageConversion::kAlphaDoNothing,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = ConvertFloatToHalfFloat(source[0]);
+ destination[1] = ConvertFloatToHalfFloat(source[3]);
+ source += 4;
+ destination += 2;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRA16F,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3];
+ destination[0] = ConvertFloatToHalfFloat(source[0] * scale_factor);
+ destination[1] = ConvertFloatToHalfFloat(source[3]);
+ source += 4;
+ destination += 2;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRA16F,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 1.0f / source[3] : 1.0f;
+ destination[0] = ConvertFloatToHalfFloat(source[0] * scale_factor);
+ destination[1] = ConvertFloatToHalfFloat(source[3]);
+ source += 4;
+ destination += 2;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatR16F,
+ WebGLImageConversion::kAlphaDoNothing,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = ConvertFloatToHalfFloat(source[0]);
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatR16F,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3];
+ destination[0] = ConvertFloatToHalfFloat(source[0] * scale_factor);
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatR16F,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 1.0f / source[3] : 1.0f;
+ destination[0] = ConvertFloatToHalfFloat(source[0] * scale_factor);
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatA16F,
+ WebGLImageConversion::kAlphaDoNothing,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = ConvertFloatToHalfFloat(source[3]);
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA8_S,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ int8_t,
+ int8_t>(const int8_t* source,
+ int8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[3] = ClampMin(source[3]);
+ float scale_factor = static_cast<float>(destination[3]) / kMaxInt8Value;
+ destination[0] = static_cast<int8_t>(
+ static_cast<float>(ClampMin(source[0])) * scale_factor);
+ destination[1] = static_cast<int8_t>(
+ static_cast<float>(ClampMin(source[1])) * scale_factor);
+ destination[2] = static_cast<int8_t>(
+ static_cast<float>(ClampMin(source[2])) * scale_factor);
+ source += 4;
+ destination += 4;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA16,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ uint16_t,
+ uint16_t>(const uint16_t* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = static_cast<float>(source[3]) / kMaxUInt16Value;
+ destination[0] =
+ static_cast<uint16_t>(static_cast<float>(source[0]) * scale_factor);
+ destination[1] =
+ static_cast<uint16_t>(static_cast<float>(source[1]) * scale_factor);
+ destination[2] =
+ static_cast<uint16_t>(static_cast<float>(source[2]) * scale_factor);
+ destination[3] = source[3];
+ source += 4;
+ destination += 4;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA16_S,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ int16_t,
+ int16_t>(const int16_t* source,
+ int16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[3] = ClampMin(source[3]);
+ float scale_factor = static_cast<float>(destination[3]) / kMaxInt16Value;
+ destination[0] = static_cast<int16_t>(
+ static_cast<float>(ClampMin(source[0])) * scale_factor);
+ destination[1] = static_cast<int16_t>(
+ static_cast<float>(ClampMin(source[1])) * scale_factor);
+ destination[2] = static_cast<int16_t>(
+ static_cast<float>(ClampMin(source[2])) * scale_factor);
+ source += 4;
+ destination += 4;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA32,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ uint32_t,
+ uint32_t>(const uint32_t* source,
+ uint32_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ double scale_factor = static_cast<double>(source[3]) / kMaxUInt32Value;
+ destination[0] =
+ static_cast<uint32_t>(static_cast<double>(source[0]) * scale_factor);
+ destination[1] =
+ static_cast<uint32_t>(static_cast<double>(source[1]) * scale_factor);
+ destination[2] =
+ static_cast<uint32_t>(static_cast<double>(source[2]) * scale_factor);
+ destination[3] = source[3];
+ source += 4;
+ destination += 4;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA32_S,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ int32_t,
+ int32_t>(const int32_t* source,
+ int32_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[3] = ClampMin(source[3]);
+ double scale_factor = static_cast<double>(destination[3]) / kMaxInt32Value;
+ destination[0] = static_cast<int32_t>(
+ static_cast<double>(ClampMin(source[0])) * scale_factor);
+ destination[1] = static_cast<int32_t>(
+ static_cast<double>(ClampMin(source[1])) * scale_factor);
+ destination[2] = static_cast<int32_t>(
+ static_cast<double>(ClampMin(source[2])) * scale_factor);
+ source += 4;
+ destination += 4;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA2_10_10_10,
+ WebGLImageConversion::kAlphaDoNothing,
+ float,
+ uint32_t>(const float* source,
+ uint32_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ uint32_t r = static_cast<uint32_t>(source[0] * 1023.0f);
+ uint32_t g = static_cast<uint32_t>(source[1] * 1023.0f);
+ uint32_t b = static_cast<uint32_t>(source[2] * 1023.0f);
+ uint32_t a = static_cast<uint32_t>(source[3] * 3.0f);
+ destination[0] = (a << 30) | (b << 20) | (g << 10) | r;
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA2_10_10_10,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ float,
+ uint32_t>(const float* source,
+ uint32_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ uint32_t r = static_cast<uint32_t>(source[0] * source[3] * 1023.0f);
+ uint32_t g = static_cast<uint32_t>(source[1] * source[3] * 1023.0f);
+ uint32_t b = static_cast<uint32_t>(source[2] * source[3] * 1023.0f);
+ uint32_t a = static_cast<uint32_t>(source[3] * 3.0f);
+ destination[0] = (a << 30) | (b << 20) | (g << 10) | r;
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA2_10_10_10,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ float,
+ uint32_t>(const float* source,
+ uint32_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 1023.0f / source[3] : 1023.0f;
+ uint32_t r = static_cast<uint32_t>(source[0] * scale_factor);
+ uint32_t g = static_cast<uint32_t>(source[1] * scale_factor);
+ uint32_t b = static_cast<uint32_t>(source[2] * scale_factor);
+ uint32_t a = static_cast<uint32_t>(source[3] * 3.0f);
+ destination[0] = (a << 30) | (b << 20) | (g << 10) | r;
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRG8,
+ WebGLImageConversion::kAlphaDoNothing,
+ uint8_t,
+ uint8_t>(const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[0];
+ destination[1] = source[1];
+ source += 4;
+ destination += 2;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRG8,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ uint8_t,
+ uint8_t>(const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = static_cast<float>(source[3]) / kMaxUInt8Value;
+ destination[0] =
+ static_cast<uint8_t>(static_cast<float>(source[0]) * scale_factor);
+ destination[1] =
+ static_cast<uint8_t>(static_cast<float>(source[1]) * scale_factor);
+ source += 4;
+ destination += 2;
+ }
+}
+
+// FIXME: this routine is lossy and must be removed.
+template <>
+void Pack<WebGLImageConversion::kDataFormatRG8,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ uint8_t,
+ uint8_t>(const uint8_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor =
+ source[3] ? kMaxUInt8Value / static_cast<float>(source[3]) : 1.0f;
+ destination[0] =
+ static_cast<uint8_t>(static_cast<float>(source[0]) * scale_factor);
+ destination[1] =
+ static_cast<uint8_t>(static_cast<float>(source[1]) * scale_factor);
+ source += 4;
+ destination += 2;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRG16F,
+ WebGLImageConversion::kAlphaDoNothing,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = ConvertFloatToHalfFloat(source[0]);
+ destination[1] = ConvertFloatToHalfFloat(source[1]);
+ source += 4;
+ destination += 2;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRG16F,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3];
+ destination[0] = ConvertFloatToHalfFloat(source[0] * scale_factor);
+ destination[1] = ConvertFloatToHalfFloat(source[1] * scale_factor);
+ source += 4;
+ destination += 2;
+ }
+}
+
+// FIXME: this routine is lossy and must be removed.
+template <>
+void Pack<WebGLImageConversion::kDataFormatRG16F,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 1.0f / source[3] : 1.0f;
+ destination[0] = ConvertFloatToHalfFloat(source[0] * scale_factor);
+ destination[1] = ConvertFloatToHalfFloat(source[1] * scale_factor);
+ source += 4;
+ destination += 2;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRG32F,
+ WebGLImageConversion::kAlphaDoNothing,
+ float,
+ float>(const float* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = source[0];
+ destination[1] = source[1];
+ source += 4;
+ destination += 2;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRG32F,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ float,
+ float>(const float* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3];
+ destination[0] = source[0] * scale_factor;
+ destination[1] = source[1] * scale_factor;
+ source += 4;
+ destination += 2;
+ }
+}
+
+// FIXME: this routine is lossy and must be removed.
+template <>
+void Pack<WebGLImageConversion::kDataFormatRG32F,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ float,
+ float>(const float* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 1.0f / source[3] : 1.0f;
+ destination[0] = source[0] * scale_factor;
+ destination[1] = source[1] * scale_factor;
+ source += 4;
+ destination += 2;
+ }
+}
+
+bool HasAlpha(int format) {
+ return format == WebGLImageConversion::kDataFormatA8 ||
+ format == WebGLImageConversion::kDataFormatA16F ||
+ format == WebGLImageConversion::kDataFormatA32F ||
+ format == WebGLImageConversion::kDataFormatRA8 ||
+ format == WebGLImageConversion::kDataFormatAR8 ||
+ format == WebGLImageConversion::kDataFormatRA16F ||
+ format == WebGLImageConversion::kDataFormatRA32F ||
+ format == WebGLImageConversion::kDataFormatRGBA8 ||
+ format == WebGLImageConversion::kDataFormatBGRA8 ||
+ format == WebGLImageConversion::kDataFormatARGB8 ||
+ format == WebGLImageConversion::kDataFormatABGR8 ||
+ format == WebGLImageConversion::kDataFormatRGBA16F ||
+ format == WebGLImageConversion::kDataFormatRGBA32F ||
+ format == WebGLImageConversion::kDataFormatRGBA4444 ||
+ format == WebGLImageConversion::kDataFormatRGBA5551 ||
+ format == WebGLImageConversion::kDataFormatRGBA8_S ||
+ format == WebGLImageConversion::kDataFormatRGBA16 ||
+ format == WebGLImageConversion::kDataFormatRGBA16_S ||
+ format == WebGLImageConversion::kDataFormatRGBA32 ||
+ format == WebGLImageConversion::kDataFormatRGBA32_S ||
+ format == WebGLImageConversion::kDataFormatRGBA2_10_10_10;
+}
+
+bool HasColor(int format) {
+ return format == WebGLImageConversion::kDataFormatRGBA8 ||
+ format == WebGLImageConversion::kDataFormatRGBA16F ||
+ format == WebGLImageConversion::kDataFormatRGBA32F ||
+ format == WebGLImageConversion::kDataFormatRGB8 ||
+ format == WebGLImageConversion::kDataFormatRGB16F ||
+ format == WebGLImageConversion::kDataFormatRGB32F ||
+ format == WebGLImageConversion::kDataFormatBGR8 ||
+ format == WebGLImageConversion::kDataFormatBGRA8 ||
+ format == WebGLImageConversion::kDataFormatARGB8 ||
+ format == WebGLImageConversion::kDataFormatABGR8 ||
+ format == WebGLImageConversion::kDataFormatRGBA5551 ||
+ format == WebGLImageConversion::kDataFormatRGBA4444 ||
+ format == WebGLImageConversion::kDataFormatRGB565 ||
+ format == WebGLImageConversion::kDataFormatR8 ||
+ format == WebGLImageConversion::kDataFormatR16F ||
+ format == WebGLImageConversion::kDataFormatR32F ||
+ format == WebGLImageConversion::kDataFormatRA8 ||
+ format == WebGLImageConversion::kDataFormatRA16F ||
+ format == WebGLImageConversion::kDataFormatRA32F ||
+ format == WebGLImageConversion::kDataFormatAR8 ||
+ format == WebGLImageConversion::kDataFormatRGBA8_S ||
+ format == WebGLImageConversion::kDataFormatRGBA16 ||
+ format == WebGLImageConversion::kDataFormatRGBA16_S ||
+ format == WebGLImageConversion::kDataFormatRGBA32 ||
+ format == WebGLImageConversion::kDataFormatRGBA32_S ||
+ format == WebGLImageConversion::kDataFormatRGBA2_10_10_10 ||
+ format == WebGLImageConversion::kDataFormatRGB8_S ||
+ format == WebGLImageConversion::kDataFormatRGB16 ||
+ format == WebGLImageConversion::kDataFormatRGB16_S ||
+ format == WebGLImageConversion::kDataFormatRGB32 ||
+ format == WebGLImageConversion::kDataFormatRGB32_S ||
+ format == WebGLImageConversion::kDataFormatRGB10F11F11F ||
+ format == WebGLImageConversion::kDataFormatRGB5999 ||
+ format == WebGLImageConversion::kDataFormatRG8 ||
+ format == WebGLImageConversion::kDataFormatRG8_S ||
+ format == WebGLImageConversion::kDataFormatRG16 ||
+ format == WebGLImageConversion::kDataFormatRG16_S ||
+ format == WebGLImageConversion::kDataFormatRG32 ||
+ format == WebGLImageConversion::kDataFormatRG32_S ||
+ format == WebGLImageConversion::kDataFormatRG16F ||
+ format == WebGLImageConversion::kDataFormatRG32F ||
+ format == WebGLImageConversion::kDataFormatR8_S ||
+ format == WebGLImageConversion::kDataFormatR16 ||
+ format == WebGLImageConversion::kDataFormatR16_S ||
+ format == WebGLImageConversion::kDataFormatR32 ||
+ format == WebGLImageConversion::kDataFormatR32_S;
+}
+
+template <int Format>
+struct IsInt8Format {
+ STATIC_ONLY(IsInt8Format);
+ static const bool value =
+ Format == WebGLImageConversion::kDataFormatRGBA8_S ||
+ Format == WebGLImageConversion::kDataFormatRGB8_S ||
+ Format == WebGLImageConversion::kDataFormatRG8_S ||
+ Format == WebGLImageConversion::kDataFormatR8_S;
+};
+
+template <int Format>
+struct IsInt16Format {
+ STATIC_ONLY(IsInt16Format);
+ static const bool value =
+ Format == WebGLImageConversion::kDataFormatRGBA16_S ||
+ Format == WebGLImageConversion::kDataFormatRGB16_S ||
+ Format == WebGLImageConversion::kDataFormatRG16_S ||
+ Format == WebGLImageConversion::kDataFormatR16_S;
+};
+
+template <int Format>
+struct IsInt32Format {
+ STATIC_ONLY(IsInt32Format);
+ static const bool value =
+ Format == WebGLImageConversion::kDataFormatRGBA32_S ||
+ Format == WebGLImageConversion::kDataFormatRGB32_S ||
+ Format == WebGLImageConversion::kDataFormatRG32_S ||
+ Format == WebGLImageConversion::kDataFormatR32_S;
+};
+
+template <int Format>
+struct IsUInt8Format {
+ STATIC_ONLY(IsUInt8Format);
+ static const bool value = Format == WebGLImageConversion::kDataFormatRGBA8 ||
+ Format == WebGLImageConversion::kDataFormatRGB8 ||
+ Format == WebGLImageConversion::kDataFormatRG8 ||
+ Format == WebGLImageConversion::kDataFormatR8 ||
+ Format == WebGLImageConversion::kDataFormatBGRA8 ||
+ Format == WebGLImageConversion::kDataFormatBGR8 ||
+ Format == WebGLImageConversion::kDataFormatARGB8 ||
+ Format == WebGLImageConversion::kDataFormatABGR8 ||
+ Format == WebGLImageConversion::kDataFormatRA8 ||
+ Format == WebGLImageConversion::kDataFormatAR8 ||
+ Format == WebGLImageConversion::kDataFormatA8;
+};
+
+template <int Format>
+struct IsUInt16Format {
+ STATIC_ONLY(IsUInt16Format);
+ static const bool value = Format == WebGLImageConversion::kDataFormatRGBA16 ||
+ Format == WebGLImageConversion::kDataFormatRGB16 ||
+ Format == WebGLImageConversion::kDataFormatRG16 ||
+ Format == WebGLImageConversion::kDataFormatR16;
+};
+
+template <int Format>
+struct IsUInt32Format {
+ STATIC_ONLY(IsUInt32Format);
+ static const bool value = Format == WebGLImageConversion::kDataFormatRGBA32 ||
+ Format == WebGLImageConversion::kDataFormatRGB32 ||
+ Format == WebGLImageConversion::kDataFormatRG32 ||
+ Format == WebGLImageConversion::kDataFormatR32;
+};
+
+template <int Format>
+struct IsFloatFormat {
+ STATIC_ONLY(IsFloatFormat);
+ static const bool value =
+ Format == WebGLImageConversion::kDataFormatRGBA32F ||
+ Format == WebGLImageConversion::kDataFormatRGB32F ||
+ Format == WebGLImageConversion::kDataFormatRA32F ||
+ Format == WebGLImageConversion::kDataFormatR32F ||
+ Format == WebGLImageConversion::kDataFormatA32F ||
+ Format == WebGLImageConversion::kDataFormatRG32F;
+};
+
+template <int Format>
+struct IsHalfFloatFormat {
+ STATIC_ONLY(IsHalfFloatFormat);
+ static const bool value =
+ Format == WebGLImageConversion::kDataFormatRGBA16F ||
+ Format == WebGLImageConversion::kDataFormatRGB16F ||
+ Format == WebGLImageConversion::kDataFormatRA16F ||
+ Format == WebGLImageConversion::kDataFormatR16F ||
+ Format == WebGLImageConversion::kDataFormatA16F ||
+ Format == WebGLImageConversion::kDataFormatRG16F;
+};
+
+template <int Format>
+struct Is32bppFormat {
+ STATIC_ONLY(Is32bppFormat);
+ static const bool value =
+ Format == WebGLImageConversion::kDataFormatRGBA2_10_10_10 ||
+ Format == WebGLImageConversion::kDataFormatRGB5999 ||
+ Format == WebGLImageConversion::kDataFormatRGB10F11F11F;
+};
+
+template <int Format>
+struct Is16bppFormat {
+ STATIC_ONLY(Is16bppFormat);
+ static const bool value =
+ Format == WebGLImageConversion::kDataFormatRGBA5551 ||
+ Format == WebGLImageConversion::kDataFormatRGBA4444 ||
+ Format == WebGLImageConversion::kDataFormatRGB565;
+};
+
+template <int Format,
+ bool IsInt8Format = IsInt8Format<Format>::value,
+ bool IsUInt8Format = IsUInt8Format<Format>::value,
+ bool IsInt16Format = IsInt16Format<Format>::value,
+ bool IsUInt16Format = IsUInt16Format<Format>::value,
+ bool IsInt32Format = IsInt32Format<Format>::value,
+ bool IsUInt32Format = IsUInt32Format<Format>::value,
+ bool IsFloat = IsFloatFormat<Format>::value,
+ bool IsHalfFloat = IsHalfFloatFormat<Format>::value,
+ bool Is16bpp = Is16bppFormat<Format>::value,
+ bool Is32bpp = Is32bppFormat<Format>::value>
+struct DataTypeForFormat {
+ STATIC_ONLY(DataTypeForFormat);
+ typedef double Type; // Use a type that's not used in unpack/pack.
+};
+
+template <int Format>
+struct DataTypeForFormat<Format,
+ true,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false> {
+ STATIC_ONLY(DataTypeForFormat);
+ typedef int8_t Type;
+};
+
+template <int Format>
+struct DataTypeForFormat<Format,
+ false,
+ true,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false> {
+ STATIC_ONLY(DataTypeForFormat);
+ typedef uint8_t Type;
+};
+
+template <int Format>
+struct DataTypeForFormat<Format,
+ false,
+ false,
+ true,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false> {
+ STATIC_ONLY(DataTypeForFormat);
+ typedef int16_t Type;
+};
+
+template <int Format>
+struct DataTypeForFormat<Format,
+ false,
+ false,
+ false,
+ true,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false> {
+ STATIC_ONLY(DataTypeForFormat);
+ typedef uint16_t Type;
+};
+
+template <int Format>
+struct DataTypeForFormat<Format,
+ false,
+ false,
+ false,
+ false,
+ true,
+ false,
+ false,
+ false,
+ false,
+ false> {
+ STATIC_ONLY(DataTypeForFormat);
+ typedef int32_t Type;
+};
+
+template <int Format>
+struct DataTypeForFormat<Format,
+ false,
+ false,
+ false,
+ false,
+ false,
+ true,
+ false,
+ false,
+ false,
+ false> {
+ STATIC_ONLY(DataTypeForFormat);
+ typedef uint32_t Type;
+};
+
+template <int Format>
+struct DataTypeForFormat<Format,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ true,
+ false,
+ false,
+ false> {
+ STATIC_ONLY(DataTypeForFormat);
+ typedef float Type;
+};
+
+template <int Format>
+struct DataTypeForFormat<Format,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ true,
+ false,
+ false> {
+ STATIC_ONLY(DataTypeForFormat);
+ typedef uint16_t Type;
+};
+
+template <int Format>
+struct DataTypeForFormat<Format,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ true,
+ false> {
+ STATIC_ONLY(DataTypeForFormat);
+ typedef uint16_t Type;
+};
+
+template <int Format>
+struct DataTypeForFormat<Format,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ true> {
+ STATIC_ONLY(DataTypeForFormat);
+ typedef uint32_t Type;
+};
+
+template <int Format>
+struct UsesFloatIntermediateFormat {
+ STATIC_ONLY(UsesFloatIntermediateFormat);
+ static const bool value =
+ IsFloatFormat<Format>::value || IsHalfFloatFormat<Format>::value ||
+ Format == WebGLImageConversion::kDataFormatRGBA2_10_10_10 ||
+ Format == WebGLImageConversion::kDataFormatRGB10F11F11F ||
+ Format == WebGLImageConversion::kDataFormatRGB5999;
+};
+
+template <int Format>
+struct IntermediateFormat {
+ STATIC_ONLY(IntermediateFormat);
+ static const int value =
+ UsesFloatIntermediateFormat<Format>::value
+ ? WebGLImageConversion::kDataFormatRGBA32F
+ : IsInt32Format<Format>::value
+ ? WebGLImageConversion::kDataFormatRGBA32_S
+ : IsUInt32Format<Format>::value
+ ? WebGLImageConversion::kDataFormatRGBA32
+ : IsInt16Format<Format>::value
+ ? WebGLImageConversion::kDataFormatRGBA16_S
+ : (IsUInt16Format<Format>::value ||
+ Is32bppFormat<Format>::value)
+ ? WebGLImageConversion::kDataFormatRGBA16
+ : IsInt8Format<Format>::value
+ ? WebGLImageConversion::
+ kDataFormatRGBA8_S
+ : WebGLImageConversion::
+ kDataFormatRGBA8;
+};
+
+unsigned TexelBytesForFormat(WebGLImageConversion::DataFormat format) {
+ switch (format) {
+ case WebGLImageConversion::kDataFormatR8:
+ case WebGLImageConversion::kDataFormatR8_S:
+ case WebGLImageConversion::kDataFormatA8:
+ return 1;
+ case WebGLImageConversion::kDataFormatRG8:
+ case WebGLImageConversion::kDataFormatRG8_S:
+ case WebGLImageConversion::kDataFormatRA8:
+ case WebGLImageConversion::kDataFormatAR8:
+ case WebGLImageConversion::kDataFormatRGBA5551:
+ case WebGLImageConversion::kDataFormatRGBA4444:
+ case WebGLImageConversion::kDataFormatRGB565:
+ case WebGLImageConversion::kDataFormatA16F:
+ case WebGLImageConversion::kDataFormatR16:
+ case WebGLImageConversion::kDataFormatR16_S:
+ case WebGLImageConversion::kDataFormatR16F:
+ case WebGLImageConversion::kDataFormatD16:
+ return 2;
+ case WebGLImageConversion::kDataFormatRGB8:
+ case WebGLImageConversion::kDataFormatRGB8_S:
+ case WebGLImageConversion::kDataFormatBGR8:
+ return 3;
+ case WebGLImageConversion::kDataFormatRGBA8:
+ case WebGLImageConversion::kDataFormatRGBA8_S:
+ case WebGLImageConversion::kDataFormatARGB8:
+ case WebGLImageConversion::kDataFormatABGR8:
+ case WebGLImageConversion::kDataFormatBGRA8:
+ case WebGLImageConversion::kDataFormatR32:
+ case WebGLImageConversion::kDataFormatR32_S:
+ case WebGLImageConversion::kDataFormatR32F:
+ case WebGLImageConversion::kDataFormatA32F:
+ case WebGLImageConversion::kDataFormatRA16F:
+ case WebGLImageConversion::kDataFormatRGBA2_10_10_10:
+ case WebGLImageConversion::kDataFormatRGB10F11F11F:
+ case WebGLImageConversion::kDataFormatRGB5999:
+ case WebGLImageConversion::kDataFormatRG16:
+ case WebGLImageConversion::kDataFormatRG16_S:
+ case WebGLImageConversion::kDataFormatRG16F:
+ case WebGLImageConversion::kDataFormatD32:
+ case WebGLImageConversion::kDataFormatD32F:
+ case WebGLImageConversion::kDataFormatDS24_8:
+ return 4;
+ case WebGLImageConversion::kDataFormatRGB16:
+ case WebGLImageConversion::kDataFormatRGB16_S:
+ case WebGLImageConversion::kDataFormatRGB16F:
+ return 6;
+ case WebGLImageConversion::kDataFormatRGBA16:
+ case WebGLImageConversion::kDataFormatRGBA16_S:
+ case WebGLImageConversion::kDataFormatRA32F:
+ case WebGLImageConversion::kDataFormatRGBA16F:
+ case WebGLImageConversion::kDataFormatRG32:
+ case WebGLImageConversion::kDataFormatRG32_S:
+ case WebGLImageConversion::kDataFormatRG32F:
+ return 8;
+ case WebGLImageConversion::kDataFormatRGB32:
+ case WebGLImageConversion::kDataFormatRGB32_S:
+ case WebGLImageConversion::kDataFormatRGB32F:
+ return 12;
+ case WebGLImageConversion::kDataFormatRGBA32:
+ case WebGLImageConversion::kDataFormatRGBA32_S:
+ case WebGLImageConversion::kDataFormatRGBA32F:
+ return 16;
+ default:
+ return 0;
+ }
+}
+
+/* END CODE SHARED WITH MOZILLA FIREFOX */
+
+class FormatConverter {
+ STACK_ALLOCATED();
+
+ public:
+ FormatConverter(const IntRect& source_data_sub_rectangle,
+ int depth,
+ int unpack_image_height,
+ const void* src_start,
+ void* dst_start,
+ int src_stride,
+ int src_row_offset,
+ int dst_stride)
+ : src_sub_rectangle_(source_data_sub_rectangle),
+ depth_(depth),
+ unpack_image_height_(unpack_image_height),
+ src_start_(src_start),
+ dst_start_(dst_start),
+ src_stride_(src_stride),
+ src_row_offset_(src_row_offset),
+ dst_stride_(dst_stride),
+ success_(false) {
+ const unsigned kMaxNumberOfComponents = 4;
+ const unsigned kMaxBytesPerComponent = 4;
+ unpacked_intermediate_src_data_ = std::make_unique<uint8_t[]>(
+ src_sub_rectangle_.Width() * kMaxNumberOfComponents *
+ kMaxBytesPerComponent);
+ DCHECK(unpacked_intermediate_src_data_.get());
+ }
+
+ void Convert(WebGLImageConversion::DataFormat src_format,
+ WebGLImageConversion::DataFormat dst_format,
+ WebGLImageConversion::AlphaOp);
+ bool Success() const { return success_; }
+
+ private:
+ template <WebGLImageConversion::DataFormat SrcFormat>
+ void Convert(WebGLImageConversion::DataFormat dst_format,
+ WebGLImageConversion::AlphaOp);
+
+ template <WebGLImageConversion::DataFormat SrcFormat,
+ WebGLImageConversion::DataFormat DstFormat>
+ void Convert(WebGLImageConversion::AlphaOp);
+
+ template <WebGLImageConversion::DataFormat SrcFormat,
+ WebGLImageConversion::DataFormat DstFormat,
+ WebGLImageConversion::AlphaOp alphaOp>
+ void Convert();
+
+ const IntRect& src_sub_rectangle_;
+ const int depth_;
+ const int unpack_image_height_;
+ const void* const src_start_;
+ void* const dst_start_;
+ const int src_stride_, src_row_offset_, dst_stride_;
+ bool success_;
+ std::unique_ptr<uint8_t[]> unpacked_intermediate_src_data_;
+};
+
+void FormatConverter::Convert(WebGLImageConversion::DataFormat src_format,
+ WebGLImageConversion::DataFormat dst_format,
+ WebGLImageConversion::AlphaOp alpha_op) {
+#define FORMATCONVERTER_CASE_SRCFORMAT(SrcFormat) \
+ case SrcFormat: \
+ return Convert<SrcFormat>(dst_format, alpha_op);
+
+ switch (src_format) {
+ FORMATCONVERTER_CASE_SRCFORMAT(WebGLImageConversion::kDataFormatRA8)
+ FORMATCONVERTER_CASE_SRCFORMAT(WebGLImageConversion::kDataFormatRA32F)
+ FORMATCONVERTER_CASE_SRCFORMAT(WebGLImageConversion::kDataFormatRGBA8)
+ FORMATCONVERTER_CASE_SRCFORMAT(WebGLImageConversion::kDataFormatARGB8)
+ FORMATCONVERTER_CASE_SRCFORMAT(WebGLImageConversion::kDataFormatABGR8)
+ FORMATCONVERTER_CASE_SRCFORMAT(WebGLImageConversion::kDataFormatAR8)
+ FORMATCONVERTER_CASE_SRCFORMAT(WebGLImageConversion::kDataFormatBGRA8)
+ FORMATCONVERTER_CASE_SRCFORMAT(WebGLImageConversion::kDataFormatRGBA5551)
+ FORMATCONVERTER_CASE_SRCFORMAT(WebGLImageConversion::kDataFormatRGBA4444)
+ FORMATCONVERTER_CASE_SRCFORMAT(WebGLImageConversion::kDataFormatRGBA32F)
+ FORMATCONVERTER_CASE_SRCFORMAT(
+ WebGLImageConversion::kDataFormatRGBA2_10_10_10)
+ default:
+ NOTREACHED();
+ }
+#undef FORMATCONVERTER_CASE_SRCFORMAT
+}
+
+template <WebGLImageConversion::DataFormat SrcFormat>
+void FormatConverter::Convert(WebGLImageConversion::DataFormat dst_format,
+ WebGLImageConversion::AlphaOp alpha_op) {
+#define FORMATCONVERTER_CASE_DSTFORMAT(DstFormat) \
+ case DstFormat: \
+ return Convert<SrcFormat, DstFormat>(alpha_op);
+
+ switch (dst_format) {
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatR8)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatR16F)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatR32F)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatA8)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatA16F)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatA32F)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRA8)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRA16F)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRA32F)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRGB8)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRGB565)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRGB16F)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRGB32F)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRGBA8)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRGBA5551)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRGBA4444)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRGBA16F)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRGBA32F)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRGBA8_S)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRGBA16)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRGBA16_S)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRGBA32)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRGBA32_S)
+ FORMATCONVERTER_CASE_DSTFORMAT(
+ WebGLImageConversion::kDataFormatRGBA2_10_10_10)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRG8)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRG16F)
+ FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::kDataFormatRG32F)
+ default:
+ NOTREACHED();
+ }
+
+#undef FORMATCONVERTER_CASE_DSTFORMAT
+}
+
+template <WebGLImageConversion::DataFormat SrcFormat,
+ WebGLImageConversion::DataFormat DstFormat>
+void FormatConverter::Convert(WebGLImageConversion::AlphaOp alpha_op) {
+#define FORMATCONVERTER_CASE_ALPHAOP(alphaOp) \
+ case alphaOp: \
+ return Convert<SrcFormat, DstFormat, alphaOp>();
+
+ switch (alpha_op) {
+ FORMATCONVERTER_CASE_ALPHAOP(WebGLImageConversion::kAlphaDoNothing)
+ FORMATCONVERTER_CASE_ALPHAOP(WebGLImageConversion::kAlphaDoPremultiply)
+ FORMATCONVERTER_CASE_ALPHAOP(WebGLImageConversion::kAlphaDoUnmultiply)
+ default:
+ NOTREACHED();
+ }
+#undef FORMATCONVERTER_CASE_ALPHAOP
+}
+
+template <int Format>
+struct SupportsConversionFromDomElements {
+ STATIC_ONLY(SupportsConversionFromDomElements);
+ static const bool value =
+ Format == WebGLImageConversion::kDataFormatRGBA8 ||
+ Format == WebGLImageConversion::kDataFormatRGB8 ||
+ Format == WebGLImageConversion::kDataFormatRG8 ||
+ Format == WebGLImageConversion::kDataFormatRA8 ||
+ Format == WebGLImageConversion::kDataFormatR8 ||
+ Format == WebGLImageConversion::kDataFormatRGBA32F ||
+ Format == WebGLImageConversion::kDataFormatRGB32F ||
+ Format == WebGLImageConversion::kDataFormatRG32F ||
+ Format == WebGLImageConversion::kDataFormatRA32F ||
+ Format == WebGLImageConversion::kDataFormatR32F ||
+ Format == WebGLImageConversion::kDataFormatRGBA16F ||
+ Format == WebGLImageConversion::kDataFormatRGB16F ||
+ Format == WebGLImageConversion::kDataFormatRG16F ||
+ Format == WebGLImageConversion::kDataFormatRA16F ||
+ Format == WebGLImageConversion::kDataFormatR16F ||
+ Format == WebGLImageConversion::kDataFormatRGBA5551 ||
+ Format == WebGLImageConversion::kDataFormatRGBA4444 ||
+ Format == WebGLImageConversion::kDataFormatRGB565 ||
+ Format == WebGLImageConversion::kDataFormatRGBA2_10_10_10;
+};
+
+template <WebGLImageConversion::DataFormat SrcFormat,
+ WebGLImageConversion::DataFormat DstFormat,
+ WebGLImageConversion::AlphaOp alphaOp>
+void FormatConverter::Convert() {
+ // Many instantiations of this template function will never be entered, so we
+ // try to return immediately in these cases to avoid generating useless code.
+ if (SrcFormat == DstFormat &&
+ alphaOp == WebGLImageConversion::kAlphaDoNothing) {
+ NOTREACHED();
+ return;
+ }
+ if (!IsFloatFormat<DstFormat>::value && IsFloatFormat<SrcFormat>::value) {
+ NOTREACHED();
+ return;
+ }
+
+ // Only textures uploaded from DOM elements or ImageData can allow DstFormat
+ // != SrcFormat.
+ const bool src_format_comes_from_dom_element_or_image_data =
+ WebGLImageConversion::SrcFormatComeFromDOMElementOrImageData(SrcFormat);
+ if (!src_format_comes_from_dom_element_or_image_data &&
+ SrcFormat != DstFormat) {
+ NOTREACHED();
+ return;
+ }
+ // Likewise, only textures uploaded from DOM elements or ImageData can
+ // possibly need to be unpremultiplied.
+ if (!src_format_comes_from_dom_element_or_image_data &&
+ alphaOp == WebGLImageConversion::kAlphaDoUnmultiply) {
+ NOTREACHED();
+ return;
+ }
+ if (src_format_comes_from_dom_element_or_image_data &&
+ alphaOp == WebGLImageConversion::kAlphaDoUnmultiply &&
+ !SupportsConversionFromDomElements<DstFormat>::value) {
+ NOTREACHED();
+ return;
+ }
+ if ((!HasAlpha(SrcFormat) || !HasColor(SrcFormat) || !HasColor(DstFormat)) &&
+ alphaOp != WebGLImageConversion::kAlphaDoNothing) {
+ NOTREACHED();
+ return;
+ }
+ // If converting DOM element data to UNSIGNED_INT_5_9_9_9_REV or
+ // UNSIGNED_INT_10F_11F_11F_REV, we should always switch to FLOAT instead to
+ // avoid unpacking/packing these two types.
+ if (src_format_comes_from_dom_element_or_image_data &&
+ SrcFormat != DstFormat &&
+ (DstFormat == WebGLImageConversion::kDataFormatRGB5999 ||
+ DstFormat == WebGLImageConversion::kDataFormatRGB10F11F11F)) {
+ NOTREACHED();
+ return;
+ }
+
+ typedef typename DataTypeForFormat<SrcFormat>::Type SrcType;
+ typedef typename DataTypeForFormat<DstFormat>::Type DstType;
+ const int kIntermFormat = IntermediateFormat<DstFormat>::value;
+ typedef typename DataTypeForFormat<kIntermFormat>::Type IntermType;
+ const ptrdiff_t src_stride_in_elements = src_stride_ / sizeof(SrcType);
+ const ptrdiff_t dst_stride_in_elements = dst_stride_ / sizeof(DstType);
+ const bool kTrivialUnpack = SrcFormat == kIntermFormat;
+ const bool kTrivialPack = DstFormat == kIntermFormat &&
+ alphaOp == WebGLImageConversion::kAlphaDoNothing;
+ DCHECK(!kTrivialUnpack || !kTrivialPack);
+
+ const SrcType* src_row_start =
+ static_cast<const SrcType*>(static_cast<const void*>(
+ static_cast<const uint8_t*>(src_start_) +
+ ((src_stride_ * src_sub_rectangle_.Y()) + src_row_offset_)));
+
+ // If packing multiple images into a 3D texture, and flipY is true,
+ // then the sub-rectangle is pointing at the start of the
+ // "bottommost" of those images. Since the source pointer strides in
+ // the positive direction, we need to back it up to point at the
+ // last, or "topmost", of these images.
+ if (dst_stride_ < 0 && depth_ > 1) {
+ src_row_start -=
+ (depth_ - 1) * src_stride_in_elements * unpack_image_height_;
+ }
+
+ DstType* dst_row_start = static_cast<DstType*>(dst_start_);
+ if (kTrivialUnpack) {
+ for (int d = 0; d < depth_; ++d) {
+ for (int i = 0; i < src_sub_rectangle_.Height(); ++i) {
+ Pack<DstFormat, alphaOp>(src_row_start, dst_row_start,
+ src_sub_rectangle_.Width());
+ src_row_start += src_stride_in_elements;
+ dst_row_start += dst_stride_in_elements;
+ }
+ src_row_start += src_stride_in_elements *
+ (unpack_image_height_ - src_sub_rectangle_.Height());
+ }
+ } else if (kTrivialPack) {
+ for (int d = 0; d < depth_; ++d) {
+ for (int i = 0; i < src_sub_rectangle_.Height(); ++i) {
+ Unpack<SrcFormat>(src_row_start, dst_row_start,
+ src_sub_rectangle_.Width());
+ src_row_start += src_stride_in_elements;
+ dst_row_start += dst_stride_in_elements;
+ }
+ src_row_start += src_stride_in_elements *
+ (unpack_image_height_ - src_sub_rectangle_.Height());
+ }
+ } else {
+ for (int d = 0; d < depth_; ++d) {
+ for (int i = 0; i < src_sub_rectangle_.Height(); ++i) {
+ Unpack<SrcFormat>(src_row_start,
+ reinterpret_cast<IntermType*>(
+ unpacked_intermediate_src_data_.get()),
+ src_sub_rectangle_.Width());
+ Pack<DstFormat, alphaOp>(reinterpret_cast<IntermType*>(
+ unpacked_intermediate_src_data_.get()),
+ dst_row_start, src_sub_rectangle_.Width());
+ src_row_start += src_stride_in_elements;
+ dst_row_start += dst_stride_in_elements;
+ }
+ src_row_start += src_stride_in_elements *
+ (unpack_image_height_ - src_sub_rectangle_.Height());
+ }
+ }
+ success_ = true;
+ return;
+}
+
+bool FrameIsValid(const SkBitmap& frame_bitmap) {
+ return !frame_bitmap.isNull() && !frame_bitmap.empty() &&
+ frame_bitmap.colorType() == kN32_SkColorType;
+}
+
+} // anonymous namespace
+
+WebGLImageConversion::PixelStoreParams::PixelStoreParams()
+ : alignment(4),
+ row_length(0),
+ image_height(0),
+ skip_pixels(0),
+ skip_rows(0),
+ skip_images(0) {}
+
+bool WebGLImageConversion::ComputeFormatAndTypeParameters(
+ GLenum format,
+ GLenum type,
+ unsigned* components_per_pixel,
+ unsigned* bytes_per_component) {
+ switch (format) {
+ case GL_ALPHA:
+ case GL_LUMINANCE:
+ case GL_RED:
+ case GL_RED_INTEGER:
+ case GL_DEPTH_COMPONENT:
+ case GL_DEPTH_STENCIL: // Treat it as one component.
+ *components_per_pixel = 1;
+ break;
+ case GL_LUMINANCE_ALPHA:
+ case GL_RG:
+ case GL_RG_INTEGER:
+ *components_per_pixel = 2;
+ break;
+ case GL_RGB:
+ case GL_RGB_INTEGER:
+ case GL_SRGB_EXT: // GL_EXT_sRGB
+ *components_per_pixel = 3;
+ break;
+ case GL_RGBA:
+ case GL_RGBA_INTEGER:
+ case GL_BGRA_EXT: // GL_EXT_texture_format_BGRA8888
+ case GL_SRGB_ALPHA_EXT: // GL_EXT_sRGB
+ *components_per_pixel = 4;
+ break;
+ default:
+ return false;
+ }
+ switch (type) {
+ case GL_BYTE:
+ *bytes_per_component = sizeof(GLbyte);
+ break;
+ case GL_UNSIGNED_BYTE:
+ *bytes_per_component = sizeof(GLubyte);
+ break;
+ case GL_SHORT:
+ *bytes_per_component = sizeof(GLshort);
+ break;
+ case GL_UNSIGNED_SHORT:
+ *bytes_per_component = sizeof(GLushort);
+ break;
+ case GL_UNSIGNED_SHORT_5_6_5:
+ case GL_UNSIGNED_SHORT_4_4_4_4:
+ case GL_UNSIGNED_SHORT_5_5_5_1:
+ *components_per_pixel = 1;
+ *bytes_per_component = sizeof(GLushort);
+ break;
+ case GL_INT:
+ *bytes_per_component = sizeof(GLint);
+ break;
+ case GL_UNSIGNED_INT:
+ *bytes_per_component = sizeof(GLuint);
+ break;
+ case GL_UNSIGNED_INT_24_8_OES:
+ case GL_UNSIGNED_INT_10F_11F_11F_REV:
+ case GL_UNSIGNED_INT_5_9_9_9_REV:
+ case GL_UNSIGNED_INT_2_10_10_10_REV:
+ *components_per_pixel = 1;
+ *bytes_per_component = sizeof(GLuint);
+ break;
+ case GL_FLOAT: // OES_texture_float
+ *bytes_per_component = sizeof(GLfloat);
+ break;
+ case GL_HALF_FLOAT:
+ case GL_HALF_FLOAT_OES: // OES_texture_half_float
+ *bytes_per_component = sizeof(GLushort);
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+GLenum WebGLImageConversion::ComputeImageSizeInBytes(
+ GLenum format,
+ GLenum type,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ const PixelStoreParams& params,
+ unsigned* image_size_in_bytes,
+ unsigned* padding_in_bytes,
+ unsigned* skip_size_in_bytes) {
+ DCHECK(image_size_in_bytes);
+ DCHECK(params.alignment == 1 || params.alignment == 2 ||
+ params.alignment == 4 || params.alignment == 8);
+ DCHECK_GE(params.row_length, 0);
+ DCHECK_GE(params.image_height, 0);
+ DCHECK_GE(params.skip_pixels, 0);
+ DCHECK_GE(params.skip_rows, 0);
+ DCHECK_GE(params.skip_images, 0);
+ if (width < 0 || height < 0 || depth < 0)
+ return GL_INVALID_VALUE;
+ if (!width || !height || !depth) {
+ *image_size_in_bytes = 0;
+ if (padding_in_bytes)
+ *padding_in_bytes = 0;
+ if (skip_size_in_bytes)
+ *skip_size_in_bytes = 0;
+ return GL_NO_ERROR;
+ }
+
+ int row_length = params.row_length > 0 ? params.row_length : width;
+ int image_height = params.image_height > 0 ? params.image_height : height;
+
+ unsigned bytes_per_component, components_per_pixel;
+ if (!ComputeFormatAndTypeParameters(format, type, &bytes_per_component,
+ &components_per_pixel))
+ return GL_INVALID_ENUM;
+ unsigned bytes_per_group = bytes_per_component * components_per_pixel;
+ CheckedNumeric<uint32_t> checked_value = static_cast<uint32_t>(row_length);
+ checked_value *= bytes_per_group;
+ if (!checked_value.IsValid())
+ return GL_INVALID_VALUE;
+
+ unsigned last_row_size;
+ if (params.row_length > 0 && params.row_length != width) {
+ CheckedNumeric<uint32_t> tmp = width;
+ tmp *= bytes_per_group;
+ if (!tmp.IsValid())
+ return GL_INVALID_VALUE;
+ last_row_size = tmp.ValueOrDie();
+ } else {
+ last_row_size = checked_value.ValueOrDie();
+ }
+
+ unsigned padding = 0;
+ CheckedNumeric<uint32_t> checked_residual = checked_value % params.alignment;
+ if (!checked_residual.IsValid()) {
+ return GL_INVALID_VALUE;
+ }
+ unsigned residual = checked_residual.ValueOrDie();
+ if (residual) {
+ padding = params.alignment - residual;
+ checked_value += padding;
+ }
+ if (!checked_value.IsValid())
+ return GL_INVALID_VALUE;
+ unsigned padded_row_size = checked_value.ValueOrDie();
+
+ CheckedNumeric<uint32_t> rows = image_height;
+ rows *= (depth - 1);
+ // Last image is not affected by IMAGE_HEIGHT parameter.
+ rows += height;
+ if (!rows.IsValid())
+ return GL_INVALID_VALUE;
+ checked_value *= (rows - 1);
+ // Last row is not affected by ROW_LENGTH parameter.
+ checked_value += last_row_size;
+ if (!checked_value.IsValid())
+ return GL_INVALID_VALUE;
+ *image_size_in_bytes = checked_value.ValueOrDie();
+ if (padding_in_bytes)
+ *padding_in_bytes = padding;
+
+ CheckedNumeric<uint32_t> skip_size = 0;
+ if (params.skip_images > 0) {
+ CheckedNumeric<uint32_t> tmp = padded_row_size;
+ tmp *= image_height;
+ tmp *= params.skip_images;
+ if (!tmp.IsValid())
+ return GL_INVALID_VALUE;
+ skip_size += tmp.ValueOrDie();
+ }
+ if (params.skip_rows > 0) {
+ CheckedNumeric<uint32_t> tmp = padded_row_size;
+ tmp *= params.skip_rows;
+ if (!tmp.IsValid())
+ return GL_INVALID_VALUE;
+ skip_size += tmp.ValueOrDie();
+ }
+ if (params.skip_pixels > 0) {
+ CheckedNumeric<uint32_t> tmp = bytes_per_group;
+ tmp *= params.skip_pixels;
+ if (!tmp.IsValid())
+ return GL_INVALID_VALUE;
+ skip_size += tmp.ValueOrDie();
+ }
+ if (!skip_size.IsValid())
+ return GL_INVALID_VALUE;
+ if (skip_size_in_bytes)
+ *skip_size_in_bytes = skip_size.ValueOrDie();
+
+ checked_value += skip_size.ValueOrDie();
+ if (!checked_value.IsValid())
+ return GL_INVALID_VALUE;
+ return GL_NO_ERROR;
+}
+
+WebGLImageConversion::ImageExtractor::ImageExtractor(
+ Image* image,
+ ImageHtmlDomSource image_html_dom_source,
+ bool premultiply_alpha,
+ bool ignore_color_space) {
+ image_ = image;
+ image_html_dom_source_ = image_html_dom_source;
+ ExtractImage(premultiply_alpha, ignore_color_space);
+}
+
+void WebGLImageConversion::ImageExtractor::ExtractImage(
+ bool premultiply_alpha,
+ bool ignore_color_space) {
+ DCHECK(!image_pixel_locker_);
+
+ if (!image_)
+ return;
+
+ sk_sp<SkImage> skia_image = image_->PaintImageForCurrentFrame().GetSkImage();
+ SkImageInfo info =
+ skia_image ? SkImageInfo::MakeN32Premul(image_->width(), image_->height())
+ : SkImageInfo::MakeUnknown();
+ alpha_op_ = kAlphaDoNothing;
+ bool has_alpha = skia_image ? !skia_image->isOpaque() : true;
+
+ if ((!skia_image || ignore_color_space ||
+ (has_alpha && !premultiply_alpha)) &&
+ image_->Data()) {
+ // Attempt to get raw unpremultiplied image data.
+ std::unique_ptr<ImageDecoder> decoder(ImageDecoder::Create(
+ image_->Data(), true, ImageDecoder::kAlphaNotPremultiplied,
+ ignore_color_space ? ColorBehavior::Ignore()
+ : ColorBehavior::TransformToSRGB()));
+ if (!decoder || !decoder->FrameCount())
+ return;
+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(0);
+ if (!frame || frame->GetStatus() != ImageFrame::kFrameComplete)
+ return;
+ has_alpha = frame->HasAlpha();
+ SkBitmap bitmap = frame->Bitmap();
+ if (!FrameIsValid(bitmap))
+ return;
+
+ // TODO(fmalita): Partial frames are not supported currently: only fully
+ // decoded frames make it through. We could potentially relax this and
+ // use SkImage::MakeFromBitmap(bitmap) to make a copy.
+ skia_image = frame->FinalizePixelsAndGetImage();
+ info = bitmap.info();
+
+ if (has_alpha && premultiply_alpha)
+ alpha_op_ = kAlphaDoPremultiply;
+ } else if (!premultiply_alpha && has_alpha) {
+ // 1. For texImage2D with HTMLVideoElment input, assume no PremultiplyAlpha
+ // had been applied and the alpha value for each pixel is 0xFF. This is
+ // true at present; if it is changed in the future it will need
+ // adjustment accordingly.
+ // 2. For texImage2D with HTMLCanvasElement input in which alpha is already
+ // premultiplied in this port, do AlphaDoUnmultiply if
+ // UNPACK_PREMULTIPLY_ALPHA_WEBGL is set to false.
+ if (image_html_dom_source_ != kHtmlDomVideo)
+ alpha_op_ = kAlphaDoUnmultiply;
+ }
+
+ if (!skia_image)
+ return;
+
+ image_source_format_ = SK_B32_SHIFT ? kDataFormatRGBA8 : kDataFormatBGRA8;
+ image_source_unpack_alignment_ =
+ 0; // FIXME: this seems to always be zero - why use at all?
+
+ DCHECK(skia_image->width());
+ DCHECK(skia_image->height());
+ image_width_ = skia_image->width();
+ image_height_ = skia_image->height();
+
+ // Fail if the image was downsampled because of memory limits.
+ if (image_width_ != (unsigned)image_->width() ||
+ image_height_ != (unsigned)image_->height())
+ return;
+
+ image_pixel_locker_.emplace(std::move(skia_image), info.alphaType(),
+ kN32_SkColorType);
+}
+
+unsigned WebGLImageConversion::GetChannelBitsByFormat(GLenum format) {
+ switch (format) {
+ case GL_ALPHA:
+ return kChannelAlpha;
+ case GL_RED:
+ case GL_RED_INTEGER:
+ case GL_R8:
+ case GL_R8_SNORM:
+ case GL_R8UI:
+ case GL_R8I:
+ case GL_R16UI:
+ case GL_R16I:
+ case GL_R32UI:
+ case GL_R32I:
+ case GL_R16F:
+ case GL_R32F:
+ return kChannelRed;
+ case GL_RG:
+ case GL_RG_INTEGER:
+ case GL_RG8:
+ case GL_RG8_SNORM:
+ case GL_RG8UI:
+ case GL_RG8I:
+ case GL_RG16UI:
+ case GL_RG16I:
+ case GL_RG32UI:
+ case GL_RG32I:
+ case GL_RG16F:
+ case GL_RG32F:
+ return kChannelRG;
+ case GL_LUMINANCE:
+ return kChannelRGB;
+ case GL_LUMINANCE_ALPHA:
+ return kChannelRGBA;
+ case GL_RGB:
+ case GL_RGB_INTEGER:
+ case GL_RGB8:
+ case GL_RGB8_SNORM:
+ case GL_RGB8UI:
+ case GL_RGB8I:
+ case GL_RGB16UI:
+ case GL_RGB16I:
+ case GL_RGB32UI:
+ case GL_RGB32I:
+ case GL_RGB16F:
+ case GL_RGB32F:
+ case GL_RGB565:
+ case GL_R11F_G11F_B10F:
+ case GL_RGB9_E5:
+ case GL_SRGB_EXT:
+ case GL_SRGB8:
+ return kChannelRGB;
+ case GL_RGBA:
+ case GL_RGBA_INTEGER:
+ case GL_RGBA8:
+ case GL_RGBA8_SNORM:
+ case GL_RGBA8UI:
+ case GL_RGBA8I:
+ case GL_RGBA16UI:
+ case GL_RGBA16I:
+ case GL_RGBA32UI:
+ case GL_RGBA32I:
+ case GL_RGBA16F:
+ case GL_RGBA32F:
+ case GL_RGBA4:
+ case GL_RGB5_A1:
+ case GL_RGB10_A2:
+ case GL_RGB10_A2UI:
+ case GL_SRGB_ALPHA_EXT:
+ case GL_SRGB8_ALPHA8:
+ return kChannelRGBA;
+ case GL_DEPTH_COMPONENT:
+ case GL_DEPTH_COMPONENT16:
+ case GL_DEPTH_COMPONENT24:
+ case GL_DEPTH_COMPONENT32F:
+ return kChannelDepth;
+ case GL_STENCIL:
+ case GL_STENCIL_INDEX8:
+ return kChannelStencil;
+ case GL_DEPTH_STENCIL:
+ case GL_DEPTH24_STENCIL8:
+ case GL_DEPTH32F_STENCIL8:
+ return kChannelDepthStencil;
+ default:
+ return 0;
+ }
+}
+
+bool WebGLImageConversion::PackImageData(
+ Image* image,
+ const void* pixels,
+ GLenum format,
+ GLenum type,
+ bool flip_y,
+ AlphaOp alpha_op,
+ DataFormat source_format,
+ unsigned source_image_width,
+ unsigned source_image_height,
+ const IntRect& source_image_sub_rectangle,
+ int depth,
+ unsigned source_unpack_alignment,
+ int unpack_image_height,
+ Vector<uint8_t>& data) {
+ if (!pixels)
+ return false;
+
+ unsigned packed_size;
+ // Output data is tightly packed (alignment == 1).
+ PixelStoreParams params;
+ params.alignment = 1;
+ if (ComputeImageSizeInBytes(format, type, source_image_sub_rectangle.Width(),
+ source_image_sub_rectangle.Height(), depth,
+ params, &packed_size, nullptr,
+ nullptr) != GL_NO_ERROR)
+ return false;
+ data.resize(packed_size);
+
+ return PackPixels(reinterpret_cast<const uint8_t*>(pixels), source_format,
+ source_image_width, source_image_height,
+ source_image_sub_rectangle, depth, source_unpack_alignment,
+ unpack_image_height, format, type, alpha_op, data.data(),
+ flip_y);
+}
+
+bool WebGLImageConversion::ExtractImageData(
+ const uint8_t* image_data,
+ DataFormat source_data_format,
+ const IntSize& image_data_size,
+ const IntRect& source_image_sub_rectangle,
+ int depth,
+ int unpack_image_height,
+ GLenum format,
+ GLenum type,
+ bool flip_y,
+ bool premultiply_alpha,
+ Vector<uint8_t>& data) {
+ if (!image_data)
+ return false;
+ int width = image_data_size.Width();
+ int height = image_data_size.Height();
+
+ unsigned packed_size;
+ // Output data is tightly packed (alignment == 1).
+ PixelStoreParams params;
+ params.alignment = 1;
+ if (ComputeImageSizeInBytes(format, type, source_image_sub_rectangle.Width(),
+ source_image_sub_rectangle.Height(), depth,
+ params, &packed_size, nullptr,
+ nullptr) != GL_NO_ERROR)
+ return false;
+ data.resize(packed_size);
+
+ if (!PackPixels(image_data, source_data_format, width, height,
+ source_image_sub_rectangle, depth, 0, unpack_image_height,
+ format, type,
+ premultiply_alpha ? kAlphaDoPremultiply : kAlphaDoNothing,
+ data.data(), flip_y))
+ return false;
+
+ return true;
+}
+
+bool WebGLImageConversion::ExtractTextureData(
+ unsigned width,
+ unsigned height,
+ GLenum format,
+ GLenum type,
+ const PixelStoreParams& unpack_params,
+ bool flip_y,
+ bool premultiply_alpha,
+ const void* pixels,
+ Vector<uint8_t>& data) {
+ // Assumes format, type, etc. have already been validated.
+ DataFormat source_data_format = GetDataFormat(format, type);
+ if (source_data_format == kDataFormatNumFormats)
+ return false;
+
+ // Resize the output buffer.
+ unsigned int components_per_pixel, bytes_per_component;
+ if (!ComputeFormatAndTypeParameters(format, type, &components_per_pixel,
+ &bytes_per_component))
+ return false;
+ unsigned bytes_per_pixel = components_per_pixel * bytes_per_component;
+ data.resize(width * height * bytes_per_pixel);
+
+ unsigned image_size_in_bytes, skip_size_in_bytes;
+ ComputeImageSizeInBytes(format, type, width, height, 1, unpack_params,
+ &image_size_in_bytes, nullptr, &skip_size_in_bytes);
+ const uint8_t* src_data = static_cast<const uint8_t*>(pixels);
+ if (skip_size_in_bytes) {
+ src_data += skip_size_in_bytes;
+ }
+
+ if (!PackPixels(src_data, source_data_format,
+ unpack_params.row_length ? unpack_params.row_length : width,
+ height, IntRect(0, 0, width, height), 1,
+ unpack_params.alignment, 0, format, type,
+ (premultiply_alpha ? kAlphaDoPremultiply : kAlphaDoNothing),
+ data.data(), flip_y))
+ return false;
+
+ return true;
+}
+
+bool WebGLImageConversion::PackPixels(const uint8_t* source_data,
+ DataFormat source_data_format,
+ unsigned source_data_width,
+ unsigned source_data_height,
+ const IntRect& source_data_sub_rectangle,
+ int depth,
+ unsigned source_unpack_alignment,
+ int unpack_image_height,
+ unsigned destination_format,
+ unsigned destination_type,
+ AlphaOp alpha_op,
+ void* destination_data,
+ bool flip_y) {
+ DCHECK_GE(depth, 1);
+ if (unpack_image_height == 0) {
+ unpack_image_height = source_data_sub_rectangle.Height();
+ }
+ int valid_src = source_data_width * TexelBytesForFormat(source_data_format);
+ int remainder =
+ source_unpack_alignment ? (valid_src % source_unpack_alignment) : 0;
+ int src_stride =
+ remainder ? (valid_src + source_unpack_alignment - remainder) : valid_src;
+ int src_row_offset =
+ source_data_sub_rectangle.X() * TexelBytesForFormat(source_data_format);
+
+ DataFormat dst_data_format =
+ GetDataFormat(destination_format, destination_type);
+ if (dst_data_format == kDataFormatNumFormats)
+ return false;
+ int dst_stride =
+ source_data_sub_rectangle.Width() * TexelBytesForFormat(dst_data_format);
+ if (flip_y) {
+ destination_data =
+ static_cast<uint8_t*>(destination_data) +
+ dst_stride * ((depth * source_data_sub_rectangle.Height()) - 1);
+ dst_stride = -dst_stride;
+ }
+ if (!HasAlpha(source_data_format) || !HasColor(source_data_format) ||
+ !HasColor(dst_data_format))
+ alpha_op = kAlphaDoNothing;
+
+ if (source_data_format == dst_data_format && alpha_op == kAlphaDoNothing) {
+ const uint8_t* base_ptr =
+ source_data + src_stride * source_data_sub_rectangle.Y();
+ const uint8_t* base_end =
+ source_data + src_stride * source_data_sub_rectangle.MaxY();
+
+ // If packing multiple images into a 3D texture, and flipY is true,
+ // then the sub-rectangle is pointing at the start of the
+ // "bottommost" of those images. Since the source pointer strides in
+ // the positive direction, we need to back it up to point at the
+ // last, or "topmost", of these images.
+ if (flip_y && depth > 1) {
+ const ptrdiff_t distance_to_top_image =
+ (depth - 1) * src_stride * unpack_image_height;
+ base_ptr -= distance_to_top_image;
+ base_end -= distance_to_top_image;
+ }
+
+ unsigned row_size = (dst_stride > 0) ? dst_stride : -dst_stride;
+ uint8_t* dst = static_cast<uint8_t*>(destination_data);
+
+ for (int i = 0; i < depth; ++i) {
+ const uint8_t* ptr = base_ptr;
+ const uint8_t* ptr_end = base_end;
+ while (ptr < ptr_end) {
+ memcpy(dst, ptr + src_row_offset, row_size);
+ ptr += src_stride;
+ dst += dst_stride;
+ }
+ base_ptr += unpack_image_height * src_stride;
+ base_end += unpack_image_height * src_stride;
+ }
+ return true;
+ }
+
+ FormatConverter converter(source_data_sub_rectangle, depth,
+ unpack_image_height, source_data, destination_data,
+ src_stride, src_row_offset, dst_stride);
+ converter.Convert(source_data_format, dst_data_format, alpha_op);
+ if (!converter.Success())
+ return false;
+ return true;
+}
+
+void WebGLImageConversion::UnpackPixels(const uint16_t* source_data,
+ DataFormat source_data_format,
+ unsigned pixels_per_row,
+ uint8_t* destination_data) {
+ switch (source_data_format) {
+ case kDataFormatRGBA4444: {
+ typedef typename DataTypeForFormat<
+ WebGLImageConversion::kDataFormatRGBA4444>::Type SrcType;
+ const SrcType* src_row_start = static_cast<const SrcType*>(source_data);
+ Unpack<WebGLImageConversion::kDataFormatRGBA4444>(
+ src_row_start, destination_data, pixels_per_row);
+ } break;
+ case kDataFormatRGBA5551: {
+ typedef typename DataTypeForFormat<
+ WebGLImageConversion::kDataFormatRGBA5551>::Type SrcType;
+ const SrcType* src_row_start = static_cast<const SrcType*>(source_data);
+ Unpack<WebGLImageConversion::kDataFormatRGBA5551>(
+ src_row_start, destination_data, pixels_per_row);
+ } break;
+ case kDataFormatBGRA8: {
+ const uint8_t* psrc = (const uint8_t*)source_data;
+ typedef typename DataTypeForFormat<
+ WebGLImageConversion::kDataFormatBGRA8>::Type SrcType;
+ const SrcType* src_row_start = static_cast<const SrcType*>(psrc);
+ Unpack<WebGLImageConversion::kDataFormatBGRA8>(
+ src_row_start, destination_data, pixels_per_row);
+ } break;
+ default:
+ break;
+ }
+}
+
+void WebGLImageConversion::PackPixels(const uint8_t* source_data,
+ DataFormat source_data_format,
+ unsigned pixels_per_row,
+ uint8_t* destination_data) {
+ switch (source_data_format) {
+ case kDataFormatRA8: {
+ typedef typename DataTypeForFormat<
+ WebGLImageConversion::kDataFormatRGBA8>::Type SrcType;
+ const SrcType* src_row_start = static_cast<const SrcType*>(source_data);
+ Pack<WebGLImageConversion::kDataFormatRA8,
+ WebGLImageConversion::kAlphaDoUnmultiply>(
+ src_row_start, destination_data, pixels_per_row);
+ } break;
+ case kDataFormatR8: {
+ typedef typename DataTypeForFormat<
+ WebGLImageConversion::kDataFormatRGBA8>::Type SrcType;
+ const SrcType* src_row_start = static_cast<const SrcType*>(source_data);
+ Pack<WebGLImageConversion::kDataFormatR8,
+ WebGLImageConversion::kAlphaDoUnmultiply>(
+ src_row_start, destination_data, pixels_per_row);
+ } break;
+ case kDataFormatRGBA8: {
+ typedef typename DataTypeForFormat<
+ WebGLImageConversion::kDataFormatRGBA8>::Type SrcType;
+ const SrcType* src_row_start = static_cast<const SrcType*>(source_data);
+ Pack<WebGLImageConversion::kDataFormatRGBA8,
+ WebGLImageConversion::kAlphaDoUnmultiply>(
+ src_row_start, destination_data, pixels_per_row);
+ } break;
+ case kDataFormatRGBA4444: {
+ uint16_t* pdst = (uint16_t*)destination_data;
+ typedef typename DataTypeForFormat<
+ WebGLImageConversion::kDataFormatRGBA8>::Type SrcType;
+ const SrcType* src_row_start = static_cast<const SrcType*>(source_data);
+ typedef typename DataTypeForFormat<
+ WebGLImageConversion::kDataFormatRGBA4444>::Type DstType;
+ DstType* dst_row_start = static_cast<DstType*>(pdst);
+ Pack<WebGLImageConversion::kDataFormatRGBA4444,
+ WebGLImageConversion::kAlphaDoNothing>(src_row_start, dst_row_start,
+ pixels_per_row);
+ } break;
+ case kDataFormatRGBA5551: {
+ uint16_t* pdst = (uint16_t*)destination_data;
+ typedef typename DataTypeForFormat<
+ WebGLImageConversion::kDataFormatRGBA8>::Type SrcType;
+ const SrcType* src_row_start = static_cast<const SrcType*>(source_data);
+ typedef typename DataTypeForFormat<
+ WebGLImageConversion::kDataFormatRGBA5551>::Type DstType;
+ DstType* dst_row_start = static_cast<DstType*>(pdst);
+ Pack<WebGLImageConversion::kDataFormatRGBA5551,
+ WebGLImageConversion::kAlphaDoNothing>(src_row_start, dst_row_start,
+ pixels_per_row);
+ } break;
+ case kDataFormatRGB565: {
+ uint16_t* pdst = (uint16_t*)destination_data;
+ typedef typename DataTypeForFormat<
+ WebGLImageConversion::kDataFormatRGBA8>::Type SrcType;
+ const SrcType* src_row_start = static_cast<const SrcType*>(source_data);
+ typedef typename DataTypeForFormat<
+ WebGLImageConversion::kDataFormatRGB565>::Type DstType;
+ DstType* dst_row_start = static_cast<DstType*>(pdst);
+ Pack<WebGLImageConversion::kDataFormatRGB565,
+ WebGLImageConversion::kAlphaDoNothing>(src_row_start, dst_row_start,
+ pixels_per_row);
+ } break;
+ default:
+ break;
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion.h b/chromium/third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion.h
new file mode 100644
index 00000000000..ad66c2f7ca1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion.h
@@ -0,0 +1,293 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_WEBGL_IMAGE_CONVERSION_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_WEBGL_IMAGE_CONVERSION_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/graphics/image.h"
+#include "third_party/blink/renderer/platform/graphics/skia/image_pixel_locker.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+#include "third_party/khronos/GLES2/gl2.h"
+#include "third_party/khronos/GLES2/gl2ext.h"
+#include "third_party/khronos/GLES3/gl3.h"
+
+namespace blink {
+class Image;
+class IntSize;
+
+// Helper functions for texture uploading and pixel readback.
+class PLATFORM_EXPORT WebGLImageConversion final {
+ STATIC_ONLY(WebGLImageConversion);
+
+ public:
+ // Attempt to enumerate all possible native image formats to
+ // reduce the amount of temporary allocations during texture
+ // uploading. This enum must be public because it is accessed
+ // by non-member functions.
+ // "_S" postfix indicates signed type.
+ enum DataFormat {
+ kDataFormatRGBA8 = 0,
+ kDataFormatRGBA8_S,
+ kDataFormatRGBA16,
+ kDataFormatRGBA16_S,
+ kDataFormatRGBA32,
+ kDataFormatRGBA32_S,
+ kDataFormatRGBA16F,
+ kDataFormatRGBA32F,
+ kDataFormatRGBA2_10_10_10,
+ kDataFormatRGB8,
+ kDataFormatRGB8_S,
+ kDataFormatRGB16,
+ kDataFormatRGB16_S,
+ kDataFormatRGB32,
+ kDataFormatRGB32_S,
+ kDataFormatRGB16F,
+ kDataFormatRGB32F,
+ kDataFormatBGR8,
+ kDataFormatBGRA8,
+ kDataFormatARGB8,
+ kDataFormatABGR8,
+ kDataFormatRGBA5551,
+ kDataFormatRGBA4444,
+ kDataFormatRGB565,
+ kDataFormatRGB10F11F11F,
+ kDataFormatRGB5999,
+ kDataFormatRG8,
+ kDataFormatRG8_S,
+ kDataFormatRG16,
+ kDataFormatRG16_S,
+ kDataFormatRG32,
+ kDataFormatRG32_S,
+ kDataFormatRG16F,
+ kDataFormatRG32F,
+ kDataFormatR8,
+ kDataFormatR8_S,
+ kDataFormatR16,
+ kDataFormatR16_S,
+ kDataFormatR32,
+ kDataFormatR32_S,
+ kDataFormatR16F,
+ kDataFormatR32F,
+ kDataFormatRA8,
+ kDataFormatRA16F,
+ kDataFormatRA32F,
+ kDataFormatAR8,
+ kDataFormatA8,
+ kDataFormatA16F,
+ kDataFormatA32F,
+ kDataFormatD16,
+ kDataFormatD32,
+ kDataFormatD32F,
+ kDataFormatDS24_8,
+ kDataFormatNumFormats
+ };
+
+ enum ChannelBits {
+ kChannelRed = 1,
+ kChannelGreen = 2,
+ kChannelBlue = 4,
+ kChannelAlpha = 8,
+ kChannelDepth = 16,
+ kChannelStencil = 32,
+ kChannelRG = kChannelRed | kChannelGreen,
+ kChannelRGB = kChannelRed | kChannelGreen | kChannelBlue,
+ kChannelRGBA = kChannelRGB | kChannelAlpha,
+ kChannelDepthStencil = kChannelDepth | kChannelStencil,
+ };
+
+ // Possible alpha operations that may need to occur during
+ // pixel packing. FIXME: kAlphaDoUnmultiply is lossy and must
+ // be removed.
+ enum AlphaOp {
+ kAlphaDoNothing = 0,
+ kAlphaDoPremultiply = 1,
+ kAlphaDoUnmultiply = 2
+ };
+
+ enum ImageHtmlDomSource {
+ kHtmlDomImage = 0,
+ kHtmlDomCanvas = 1,
+ kHtmlDomVideo = 2,
+ kHtmlDomNone = 3
+ };
+
+ struct PLATFORM_EXPORT PixelStoreParams final {
+ PixelStoreParams();
+
+ GLint alignment;
+ GLint row_length;
+ GLint image_height;
+ GLint skip_pixels;
+ GLint skip_rows;
+ GLint skip_images;
+ };
+
+ class PLATFORM_EXPORT ImageExtractor final {
+ STACK_ALLOCATED();
+ WTF_MAKE_NONCOPYABLE(ImageExtractor);
+
+ public:
+ ImageExtractor(Image*,
+ ImageHtmlDomSource,
+ bool premultiply_alpha,
+ bool ignore_color_space);
+
+ const void* ImagePixelData() {
+ return image_pixel_locker_ ? image_pixel_locker_->Pixels() : nullptr;
+ }
+ unsigned ImageWidth() { return image_width_; }
+ unsigned ImageHeight() { return image_height_; }
+ DataFormat ImageSourceFormat() { return image_source_format_; }
+ AlphaOp ImageAlphaOp() { return alpha_op_; }
+ unsigned ImageSourceUnpackAlignment() {
+ return image_source_unpack_alignment_;
+ }
+
+ private:
+ // Extracts the image and keeps track of its status, such as width, height,
+ // Source Alignment, format, AlphaOp, etc. This needs to lock the resources
+ // or relevant data if needed.
+ void ExtractImage(bool premultiply_alpha, bool ignore_color_space);
+
+ Image* image_;
+ Optional<ImagePixelLocker> image_pixel_locker_;
+ ImageHtmlDomSource image_html_dom_source_;
+ unsigned image_width_;
+ unsigned image_height_;
+ DataFormat image_source_format_;
+ AlphaOp alpha_op_;
+ unsigned image_source_unpack_alignment_;
+ };
+
+ // Computes the components per pixel and bytes per component
+ // for the given format and type combination. Returns false if
+ // either was an invalid enum.
+ static bool ComputeFormatAndTypeParameters(GLenum format,
+ GLenum type,
+ unsigned* components_per_pixel,
+ unsigned* bytes_per_component);
+
+ // Computes the image size in bytes. If paddingInBytes is not null, padding
+ // is also calculated in return. Returns NO_ERROR if succeed, otherwise
+ // return the suggested GL error indicating the cause of the failure:
+ // INVALID_VALUE if width/height/depth is negative or overflow happens.
+ // INVALID_ENUM if format/type is illegal.
+ // Note that imageSizeBytes does not include skipSizeInBytes, but it is
+ // guaranteed if NO_ERROR is returned, adding the two sizes won't cause
+ // overflow.
+ // |paddingInBytes| and |skipSizeInBytes| are optional and can be null, but
+ // the overflow validation is still performed.
+ static GLenum ComputeImageSizeInBytes(GLenum format,
+ GLenum type,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ const PixelStoreParams&,
+ unsigned* image_size_in_bytes,
+ unsigned* padding_in_bytes,
+ unsigned* skip_size_in_bytes);
+
+ // Check if the format is one of the formats from the ImageData or DOM
+ // elements. The format from ImageData is always RGBA8. The formats from DOM
+ // elements vary with Graphics ports, but can only be RGBA8 or BGRA8.
+ static ALWAYS_INLINE bool SrcFormatComeFromDOMElementOrImageData(
+ DataFormat src_format) {
+ return src_format == kDataFormatBGRA8 || src_format == kDataFormatRGBA8;
+ }
+
+ // The input can be either format or internalformat.
+ static unsigned GetChannelBitsByFormat(GLenum);
+
+ // The Following functions are implemented in
+ // GraphicsContext3DImagePacking.cpp.
+
+ // Packs the contents of the given Image, which is passed in |pixels|, into
+ // the passed Vector according to the given format and type, and obeying the
+ // flipY and AlphaOp flags. Returns true upon success.
+ static bool PackImageData(Image*,
+ const void* pixels,
+ GLenum format,
+ GLenum type,
+ bool flip_y,
+ AlphaOp,
+ DataFormat source_format,
+ unsigned source_image_width,
+ unsigned source_image_height,
+ const IntRect& source_image_sub_rectangle,
+ int depth,
+ unsigned source_unpack_alignment,
+ int unpack_image_height,
+ Vector<uint8_t>& data);
+
+ // Extracts the contents of the given ImageData into the passed Vector,
+ // packing the pixel data according to the given format and type,
+ // and obeying the flipY and premultiplyAlpha flags. Returns true
+ // upon success.
+ static bool ExtractImageData(const uint8_t* image_data,
+ DataFormat source_data_format,
+ const IntSize& image_data_size,
+ const IntRect& source_image_sub_rectangle,
+ int depth,
+ int unpack_image_height,
+ GLenum format,
+ GLenum type,
+ bool flip_y,
+ bool premultiply_alpha,
+ Vector<uint8_t>& data);
+
+ // Helper function which extracts the user-supplied texture
+ // data, applying the flipY and premultiplyAlpha parameters.
+ // If the data is not tightly packed according to the passed
+ // unpackAlignment, the output data will be tightly packed.
+ // Returns true if successful, false if any error occurred.
+ static bool ExtractTextureData(unsigned width,
+ unsigned height,
+ GLenum format,
+ GLenum type,
+ const PixelStoreParams& unpack_params,
+ bool flip_y,
+ bool premultiply_alpha,
+ const void* pixels,
+ Vector<uint8_t>& data);
+
+ // End GraphicsContext3DImagePacking.cpp functions
+
+ private:
+ friend class WebGLImageConversionTest;
+ // Helper for packImageData/extractImageData/extractTextureData, which
+ // implement packing of pixel data into the specified OpenGL destination
+ // format and type. A sourceUnpackAlignment of zero indicates that the source
+ // data is tightly packed. Non-zero values may take a slow path. Destination
+ // data will have no gaps between rows. Implemented in
+ // GraphicsContext3DImagePacking.cpp.
+ static bool PackPixels(const uint8_t* source_data,
+ DataFormat source_data_format,
+ unsigned source_data_width,
+ unsigned source_data_height,
+ const IntRect& source_data_sub_rectangle,
+ int depth,
+ unsigned source_unpack_alignment,
+ int unpack_image_height,
+ unsigned destination_format,
+ unsigned destination_type,
+ AlphaOp,
+ void* destination_data,
+ bool flip_y);
+ static void UnpackPixels(const uint16_t* source_data,
+ DataFormat source_data_format,
+ unsigned pixels_per_row,
+ uint8_t* destination_data);
+ static void PackPixels(const uint8_t* source_data,
+ DataFormat source_data_format,
+ unsigned pixels_per_row,
+ uint8_t* destination_data);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_WEBGL_IMAGE_CONVERSION_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion_test.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion_test.cc
new file mode 100644
index 00000000000..e3156158588
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion_test.cc
@@ -0,0 +1,173 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion.h"
+
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+class WebGLImageConversionTest : public testing::Test {
+ protected:
+ void UnpackPixels(const uint16_t* source_data,
+ WebGLImageConversion::DataFormat source_data_format,
+ unsigned pixels_per_row,
+ uint8_t* destination_data) {
+ WebGLImageConversion::UnpackPixels(source_data, source_data_format,
+ pixels_per_row, destination_data);
+ }
+ void PackPixels(const uint8_t* source_data,
+ WebGLImageConversion::DataFormat source_data_format,
+ unsigned pixels_per_row,
+ uint8_t* destination_data) {
+ WebGLImageConversion::PackPixels(source_data, source_data_format,
+ pixels_per_row, destination_data);
+ }
+};
+
+TEST_F(WebGLImageConversionTest, ConvertRGBA4444toRGBA8) {
+ uint16_t source_data[9] = {0x1234, 0x3456, 0x1234, 0x3456, 0x1234,
+ 0x3456, 0x1234, 0x3456, 0x1234};
+ uint8_t expected_data[36] = {
+ 0x11, 0x22, 0x33, 0x44, 0x33, 0x44, 0x55, 0x66, 0x11, 0x22, 0x33, 0x44,
+ 0x33, 0x44, 0x55, 0x66, 0x11, 0x22, 0x33, 0x44, 0x33, 0x44, 0x55, 0x66,
+ 0x11, 0x22, 0x33, 0x44, 0x33, 0x44, 0x55, 0x66, 0x11, 0x22, 0x33, 0x44};
+ uint8_t destination_data[36];
+ UnpackPixels(source_data, WebGLImageConversion::kDataFormatRGBA4444, 9,
+ destination_data);
+ EXPECT_EQ(0,
+ memcmp(expected_data, destination_data, sizeof(destination_data)));
+}
+
+TEST_F(WebGLImageConversionTest, ConvertRGBA5551toRGBA8) {
+ uint16_t source_data[9] = {0x1234, 0x3456, 0x1234, 0x3456, 0x1234,
+ 0x3456, 0x1234, 0x3456, 0x1234};
+ uint8_t expected_data[36] = {
+ 0x12, 0x40, 0xd2, 0x0, 0x36, 0x89, 0x5b, 0x0, 0x12, 0x40, 0xd2, 0x0,
+ 0x36, 0x89, 0x5b, 0x0, 0x12, 0x40, 0xd2, 0x0, 0x36, 0x89, 0x5b, 0x0,
+ 0x12, 0x40, 0xd2, 0x0, 0x36, 0x89, 0x5b, 0x0, 0x12, 0x40, 0xd2, 0x0};
+ uint8_t destination_data[36];
+ UnpackPixels(source_data, WebGLImageConversion::kDataFormatRGBA5551, 9,
+ destination_data);
+ EXPECT_EQ(0,
+ memcmp(expected_data, destination_data, sizeof(destination_data)));
+}
+
+TEST_F(WebGLImageConversionTest, ConvertRGBA8toRA8) {
+ uint8_t source_data[40] = {0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56};
+ uint8_t expected_data[20] = {0x9a, 0x56, 0x9a, 0x56, 0x9a, 0x56, 0x9a,
+ 0x56, 0x9a, 0x56, 0x9a, 0x56, 0x9a, 0x56,
+ 0x9a, 0x56, 0x9a, 0x56, 0x9a, 0x56};
+ uint8_t destination_data[20];
+ PackPixels(source_data, WebGLImageConversion::kDataFormatRA8, 10,
+ destination_data);
+ EXPECT_EQ(0,
+ memcmp(expected_data, destination_data, sizeof(destination_data)));
+}
+
+TEST_F(WebGLImageConversionTest, convertBGRA8toRGBA8) {
+ uint32_t source_data[9] = {0x12345678, 0x34567888, 0x12345678,
+ 0x34567888, 0x12345678, 0x34567888,
+ 0x12345678, 0x34567888, 0x12345678};
+#if defined(ARCH_CPU_BIG_ENDIAN)
+ uint32_t expectedData[9] = {0x56341278, 0x78563488, 0x56341278,
+ 0x78563488, 0x56341278, 0x78563488,
+ 0x56341278, 0x78563488, 0x56341278};
+#else
+ uint32_t expected_data[9] = {0x12785634, 0x34887856, 0x12785634,
+ 0x34887856, 0x12785634, 0x34887856,
+ 0x12785634, 0x34887856, 0x12785634};
+#endif
+ uint32_t destination_data[9];
+ UnpackPixels(reinterpret_cast<uint16_t*>(&source_data[0]),
+ WebGLImageConversion::kDataFormatBGRA8, 9,
+ reinterpret_cast<uint8_t*>(&destination_data[0]));
+ EXPECT_EQ(0,
+ memcmp(expected_data, destination_data, sizeof(destination_data)));
+}
+
+TEST_F(WebGLImageConversionTest, ConvertRGBA8toR8) {
+ uint8_t source_data[40] = {0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56};
+ uint8_t expected_data[10] = {0x9a, 0x9a, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a};
+ uint8_t destination_data[10];
+ PackPixels(source_data, WebGLImageConversion::kDataFormatR8, 10,
+ destination_data);
+ EXPECT_EQ(0,
+ memcmp(expected_data, destination_data, sizeof(destination_data)));
+}
+
+TEST_F(WebGLImageConversionTest, ConvertRGBA8toRGBA8) {
+ uint8_t source_data[40] = {0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56};
+ uint8_t expected_data[40] = {0x9a, 0xff, 0x9a, 0x56, 0x9a, 0xff, 0x9a, 0x56,
+ 0x9a, 0xff, 0x9a, 0x56, 0x9a, 0xff, 0x9a, 0x56,
+ 0x9a, 0xff, 0x9a, 0x56, 0x9a, 0xff, 0x9a, 0x56,
+ 0x9a, 0xff, 0x9a, 0x56, 0x9a, 0xff, 0x9a, 0x56,
+ 0x9a, 0xff, 0x9a, 0x56, 0x9a, 0xff, 0x9a, 0x56};
+ uint8_t destination_data[40];
+ PackPixels(source_data, WebGLImageConversion::kDataFormatRGBA8, 10,
+ destination_data);
+ EXPECT_EQ(0,
+ memcmp(expected_data, destination_data, sizeof(destination_data)));
+}
+
+TEST_F(WebGLImageConversionTest, ConvertRGBA8ToUnsignedShort4444) {
+ uint8_t source_data[40] = {0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56};
+ uint16_t expected_data[10] = {0x3535, 0x3535, 0x3535, 0x3535, 0x3535,
+ 0x3535, 0x3535, 0x3535, 0x3535, 0x3535};
+ uint16_t destination_data[10];
+ PackPixels(source_data, WebGLImageConversion::kDataFormatRGBA4444, 10,
+ reinterpret_cast<uint8_t*>(&destination_data[0]));
+ EXPECT_EQ(0,
+ memcmp(expected_data, destination_data, sizeof(destination_data)));
+}
+
+TEST_F(WebGLImageConversionTest, ConvertRGBA8ToRGBA5551) {
+ uint8_t source_data[40] = {0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56};
+ uint16_t expected_data[10] = {0x328c, 0x328c, 0x328c, 0x328c, 0x328c,
+ 0x328c, 0x328c, 0x328c, 0x328c, 0x328c};
+ uint16_t destination_data[10];
+ PackPixels(source_data, WebGLImageConversion::kDataFormatRGBA5551, 10,
+ reinterpret_cast<uint8_t*>(&destination_data[0]));
+ EXPECT_EQ(0,
+ memcmp(expected_data, destination_data, sizeof(destination_data)));
+}
+
+TEST_F(WebGLImageConversionTest, ConvertRGBA8ToRGB565) {
+ uint8_t source_data[40] = {0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56,
+ 0x34, 0x56, 0x34, 0x56, 0x34, 0x56, 0x34, 0x56};
+ uint16_t expected_data[10] = {0x32a6, 0x32a6, 0x32a6, 0x32a6, 0x32a6,
+ 0x32a6, 0x32a6, 0x32a6, 0x32a6, 0x32a6};
+ uint16_t destination_data[10];
+ PackPixels(source_data, WebGLImageConversion::kDataFormatRGB565, 10,
+ reinterpret_cast<uint8_t*>(&destination_data[0]));
+ EXPECT_EQ(0,
+ memcmp(expected_data, destination_data, sizeof(destination_data)));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.cc
new file mode 100644
index 00000000000..8956cfa76cf
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.cc
@@ -0,0 +1,266 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.h"
+
+#include "build/build_config.h"
+#include "device/vr/public/mojom/vr_service.mojom-blink.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "gpu/command_buffer/common/mailbox_holder.h"
+#include "mojo/public/cpp/system/platform_handle.h"
+#include "third_party/blink/renderer/platform/graphics/gpu_memory_buffer_image_copy.h"
+#include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
+#include "ui/gfx/gpu_fence.h"
+
+namespace blink {
+
+XRFrameTransport::XRFrameTransport() : submit_frame_client_binding_(this) {}
+
+XRFrameTransport::~XRFrameTransport() {
+ CallPreviousFrameCallback();
+}
+
+void XRFrameTransport::PresentChange() {
+ frame_copier_ = nullptr;
+}
+
+void XRFrameTransport::SetTransportOptions(
+ device::mojom::blink::VRDisplayFrameTransportOptionsPtr transport_options) {
+ transport_options_ = std::move(transport_options);
+}
+
+device::mojom::blink::VRSubmitFrameClientPtr
+XRFrameTransport::GetSubmitFrameClient() {
+ device::mojom::blink::VRSubmitFrameClientPtr submit_frame_client;
+ submit_frame_client_binding_.Close();
+ submit_frame_client_binding_.Bind(mojo::MakeRequest(&submit_frame_client));
+ return submit_frame_client;
+}
+
+bool XRFrameTransport::DrawingIntoSharedBuffer() {
+ switch (transport_options_->transport_method) {
+ case device::mojom::blink::VRDisplayFrameTransportMethod::
+ SUBMIT_AS_TEXTURE_HANDLE:
+ case device::mojom::blink::VRDisplayFrameTransportMethod::
+ SUBMIT_AS_MAILBOX_HOLDER:
+ return false;
+ case device::mojom::blink::VRDisplayFrameTransportMethod::
+ DRAW_INTO_TEXTURE_MAILBOX:
+ return true;
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
+void XRFrameTransport::FramePreImage(gpu::gles2::GLES2Interface* gl) {
+ frame_wait_time_ = WTF::TimeDelta();
+
+ // If we're expecting a fence for the previous frame and it hasn't arrived
+ // yet, wait for it to be received.
+ if (waiting_for_previous_frame_fence_) {
+ frame_wait_time_ += WaitForGpuFenceReceived();
+ }
+ // If we have a GpuFence (it may be missing if WaitForIncomingMethodCall
+ // failed), send it to the GPU service process and ask it to do an
+ // asynchronous server wait.
+ if (previous_frame_fence_) {
+ DVLOG(3) << "CreateClientGpuFenceCHROMIUM";
+ GLuint id = gl->CreateClientGpuFenceCHROMIUM(
+ previous_frame_fence_->AsClientGpuFence());
+ gl->WaitGpuFenceCHROMIUM(id);
+ gl->DestroyGpuFenceCHROMIUM(id);
+ previous_frame_fence_.reset();
+ }
+}
+
+void XRFrameTransport::CallPreviousFrameCallback() {
+ if (previous_image_release_callback_) {
+ previous_image_release_callback_->Run(gpu::SyncToken(), false);
+ previous_image_release_callback_ = nullptr;
+ }
+}
+
+void XRFrameTransport::FrameSubmitMissing(
+ device::mojom::blink::VRPresentationProvider* vr_presentation_provider,
+ gpu::gles2::GLES2Interface* gl,
+ int16_t vr_frame_id) {
+ TRACE_EVENT0("gpu", __FUNCTION__);
+ gpu::SyncToken sync_token;
+ gl->GenSyncTokenCHROMIUM(sync_token.GetData());
+ vr_presentation_provider->SubmitFrameMissing(vr_frame_id, sync_token);
+}
+
+void XRFrameTransport::FrameSubmit(
+ device::mojom::blink::VRPresentationProvider* vr_presentation_provider,
+ gpu::gles2::GLES2Interface* gl,
+ DrawingBuffer::Client* drawing_buffer_client,
+ scoped_refptr<Image> image_ref,
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback,
+ int16_t vr_frame_id,
+ bool needs_copy) {
+ DCHECK(transport_options_);
+
+ if (transport_options_->transport_method ==
+ device::mojom::blink::VRDisplayFrameTransportMethod::
+ SUBMIT_AS_TEXTURE_HANDLE) {
+#if defined(OS_WIN)
+ // Currently, we assume that this transport needs a copy.
+ DCHECK(needs_copy);
+ TRACE_EVENT0("gpu", "XRFrameTransport::CopyImage");
+ // Update last_transfer_succeeded_ value. This should usually complete
+ // without waiting.
+ if (transport_options_->wait_for_transfer_notification)
+ WaitForPreviousTransfer();
+ CallPreviousFrameCallback();
+ previous_image_release_callback_ = std::move(release_callback);
+ if (!frame_copier_ || !last_transfer_succeeded_) {
+ frame_copier_ = std::make_unique<GpuMemoryBufferImageCopy>(gl);
+ }
+ auto gpu_memory_buffer = frame_copier_->CopyImage(image_ref.get());
+ drawing_buffer_client->DrawingBufferClientRestoreTexture2DBinding();
+ drawing_buffer_client->DrawingBufferClientRestoreFramebufferBinding();
+ drawing_buffer_client->DrawingBufferClientRestoreRenderbufferBinding();
+
+ // We can fail to obtain a GpuMemoryBuffer if we don't have GPU support, or
+ // for some out-of-memory situations.
+ // TODO(billorr): Consider whether we should just drop the frame or exit
+ // presentation.
+ if (gpu_memory_buffer) {
+ // We decompose the cloned handle, and use it to create a
+ // mojo::ScopedHandle which will own cleanup of the handle, and will be
+ // passed over IPC.
+ gfx::GpuMemoryBufferHandle gpu_handle =
+ CloneHandleForIPC(gpu_memory_buffer->GetHandle());
+ vr_presentation_provider->SubmitFrameWithTextureHandle(
+ vr_frame_id, mojo::WrapPlatformFile(gpu_handle.handle.GetHandle()));
+ }
+#else
+ NOTIMPLEMENTED();
+#endif
+ } else if (transport_options_->transport_method ==
+ device::mojom::blink::VRDisplayFrameTransportMethod::
+ SUBMIT_AS_MAILBOX_HOLDER) {
+ // Currently, this transport assumes we don't need to make a separate copy
+ // of the canvas content.
+ DCHECK(!needs_copy);
+
+ // The AcceleratedStaticBitmapImage must be kept alive until the
+ // mailbox is used via createAndConsumeTextureCHROMIUM, the mailbox
+ // itself does not keep it alive. We must keep a reference to the
+ // image until the mailbox was consumed.
+ StaticBitmapImage* static_image =
+ static_cast<StaticBitmapImage*>(image_ref.get());
+ TRACE_EVENT_BEGIN0("gpu", "XRFrameTransport::EnsureMailbox");
+ static_image->EnsureMailbox(kVerifiedSyncToken, GL_NEAREST);
+ TRACE_EVENT_END0("gpu", "XRFrameTransport::EnsureMailbox");
+
+ // Conditionally wait for the previous render to finish. A late wait here
+ // attempts to overlap work in parallel with the previous frame's
+ // rendering. This is used if submitting fully rendered frames to GVR, but
+ // is susceptible to bad GPU scheduling if the new frame competes with the
+ // previous frame's incomplete rendering.
+ if (waiting_for_previous_frame_render_)
+ frame_wait_time_ += WaitForPreviousRenderToFinish();
+
+ // Save a reference to the image to keep it alive until next frame,
+ // but first wait for the transfer to finish before overwriting it.
+ // Usually this check is satisfied without waiting.
+ if (transport_options_->wait_for_transfer_notification)
+ WaitForPreviousTransfer();
+ previous_image_ = std::move(image_ref);
+ CallPreviousFrameCallback();
+ previous_image_release_callback_ = std::move(release_callback);
+
+ // Create mailbox and sync token for transfer.
+ TRACE_EVENT_BEGIN0("gpu", "XRFrameTransport::GetMailbox");
+ auto mailbox = static_image->GetMailbox();
+ TRACE_EVENT_END0("gpu", "XRFrameTransport::GetMailbox");
+ auto sync_token = static_image->GetSyncToken();
+
+ TRACE_EVENT_BEGIN0("gpu", "XRFrameTransport::SubmitFrame");
+ vr_presentation_provider->SubmitFrame(
+ vr_frame_id, gpu::MailboxHolder(mailbox, sync_token, GL_TEXTURE_2D),
+ frame_wait_time_);
+ TRACE_EVENT_END0("gpu", "XRFrameTransport::SubmitFrame");
+ } else if (transport_options_->transport_method ==
+ device::mojom::blink::VRDisplayFrameTransportMethod::
+ DRAW_INTO_TEXTURE_MAILBOX) {
+ TRACE_EVENT0("gpu", "XRFrameTransport::SubmitFrameDrawnIntoTexture");
+ gpu::SyncToken sync_token;
+ {
+ TRACE_EVENT0("gpu", "GenSyncTokenCHROMIUM");
+ gl->GenSyncTokenCHROMIUM(sync_token.GetData());
+ }
+ if (waiting_for_previous_frame_render_)
+ frame_wait_time_ += WaitForPreviousRenderToFinish();
+ vr_presentation_provider->SubmitFrameDrawnIntoTexture(
+ vr_frame_id, sync_token, frame_wait_time_);
+ } else {
+ NOTREACHED() << "Unimplemented frame transport method";
+ }
+
+ // Set the expected notifications the next frame should wait for.
+ waiting_for_previous_frame_transfer_ =
+ transport_options_->wait_for_transfer_notification;
+ waiting_for_previous_frame_render_ =
+ transport_options_->wait_for_render_notification;
+ waiting_for_previous_frame_fence_ = transport_options_->wait_for_gpu_fence;
+}
+
+void XRFrameTransport::OnSubmitFrameTransferred(bool success) {
+ DVLOG(3) << __FUNCTION__;
+ waiting_for_previous_frame_transfer_ = false;
+ last_transfer_succeeded_ = success;
+}
+
+void XRFrameTransport::WaitForPreviousTransfer() {
+ TRACE_EVENT0("gpu", "waitForPreviousTransferToFinish");
+ while (waiting_for_previous_frame_transfer_) {
+ if (!submit_frame_client_binding_.WaitForIncomingMethodCall()) {
+ DLOG(ERROR) << __FUNCTION__ << ": Failed to receive response";
+ break;
+ }
+ }
+}
+
+void XRFrameTransport::OnSubmitFrameRendered() {
+ DVLOG(3) << __FUNCTION__;
+ waiting_for_previous_frame_render_ = false;
+}
+
+WTF::TimeDelta XRFrameTransport::WaitForPreviousRenderToFinish() {
+ TRACE_EVENT0("gpu", "waitForPreviousRenderToFinish");
+ WTF::TimeTicks start = WTF::CurrentTimeTicks();
+ while (waiting_for_previous_frame_render_) {
+ if (!submit_frame_client_binding_.WaitForIncomingMethodCall()) {
+ DLOG(ERROR) << __FUNCTION__ << ": Failed to receive response";
+ break;
+ }
+ }
+ return WTF::CurrentTimeTicks() - start;
+}
+
+void XRFrameTransport::OnSubmitFrameGpuFence(
+ const gfx::GpuFenceHandle& handle) {
+ // We just received a GpuFence, unblock WaitForGpuFenceReceived.
+ waiting_for_previous_frame_fence_ = false;
+ previous_frame_fence_ = std::make_unique<gfx::GpuFence>(handle);
+}
+
+WTF::TimeDelta XRFrameTransport::WaitForGpuFenceReceived() {
+ TRACE_EVENT0("gpu", "WaitForGpuFenceReceived");
+ WTF::TimeTicks start = WTF::CurrentTimeTicks();
+ while (waiting_for_previous_frame_fence_) {
+ if (!submit_frame_client_binding_.WaitForIncomingMethodCall()) {
+ DLOG(ERROR) << __FUNCTION__ << ": Failed to receive response";
+ break;
+ }
+ }
+ return WTF::CurrentTimeTicks() - start;
+}
+
+void XRFrameTransport::Trace(blink::Visitor* visitor) {}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.h b/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.h
new file mode 100644
index 00000000000..f416c8821d4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.h
@@ -0,0 +1,100 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_XR_FRAME_TRANSPORT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_XR_FRAME_TRANSPORT_H_
+
+#include "device/vr/public/mojom/vr_service.mojom-blink.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "third_party/blink/public/platform/web_graphics_context_3d_provider.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace gfx {
+class GpuFence;
+}
+
+namespace gpu {
+namespace gles2 {
+class GLES2Interface;
+}
+} // namespace gpu
+
+namespace blink {
+
+class GpuMemoryBufferImageCopy;
+class Image;
+
+class PLATFORM_EXPORT XRFrameTransport final
+ : public GarbageCollectedFinalized<XRFrameTransport>,
+ public device::mojom::blink::VRSubmitFrameClient {
+ public:
+ explicit XRFrameTransport();
+ ~XRFrameTransport();
+
+ device::mojom::blink::VRSubmitFrameClientPtr GetSubmitFrameClient();
+
+ void PresentChange();
+
+ void SetTransportOptions(
+ device::mojom::blink::VRDisplayFrameTransportOptionsPtr);
+
+ bool DrawingIntoSharedBuffer();
+
+ // Call before finalizing the frame's image snapshot.
+ void FramePreImage(gpu::gles2::GLES2Interface*);
+
+ void FrameSubmit(device::mojom::blink::VRPresentationProvider*,
+ gpu::gles2::GLES2Interface*,
+ DrawingBuffer::Client*,
+ scoped_refptr<Image> image_ref,
+ std::unique_ptr<viz::SingleReleaseCallback>,
+ int16_t vr_frame_id,
+ bool needs_copy);
+
+ void FrameSubmitMissing(device::mojom::blink::VRPresentationProvider*,
+ gpu::gles2::GLES2Interface*,
+ int16_t vr_frame_id);
+
+ virtual void Trace(blink::Visitor*);
+
+ private:
+ void WaitForPreviousTransfer();
+ WTF::TimeDelta WaitForPreviousRenderToFinish();
+ WTF::TimeDelta WaitForGpuFenceReceived();
+ void CallPreviousFrameCallback();
+
+ // VRSubmitFrameClient
+ void OnSubmitFrameTransferred(bool success) override;
+ void OnSubmitFrameRendered() override;
+ void OnSubmitFrameGpuFence(const gfx::GpuFenceHandle&) override;
+
+ mojo::Binding<device::mojom::blink::VRSubmitFrameClient>
+ submit_frame_client_binding_;
+
+ // Used to keep the image alive until the next frame if using
+ // waitForPreviousTransferToFinish.
+ scoped_refptr<Image> previous_image_;
+ std::unique_ptr<viz::SingleReleaseCallback> previous_image_release_callback_;
+
+ bool waiting_for_previous_frame_transfer_ = false;
+ bool last_transfer_succeeded_ = false;
+ WTF::TimeDelta frame_wait_time_;
+ bool waiting_for_previous_frame_render_ = false;
+ // If using GpuFence to separate frames, need to wait for the previous
+ // frame's fence, but not if this is the first frame. Separately track
+ // if we're expecting a fence and the received fence itself.
+ bool waiting_for_previous_frame_fence_ = false;
+ std::unique_ptr<gfx::GpuFence> previous_frame_fence_;
+
+ device::mojom::blink::VRDisplayFrameTransportOptionsPtr transport_options_;
+
+ std::unique_ptr<GpuMemoryBufferImageCopy> frame_copier_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_XR_FRAME_TRANSPORT_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.cc
new file mode 100644
index 00000000000..8eca5b8fa6e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.cc
@@ -0,0 +1,626 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.h"
+
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.h"
+#include "third_party/skia/include/core/SkSurface.h"
+
+namespace blink {
+
+// Large parts of this file have been shamelessly borrowed from
+// platform/graphics/gpu/DrawingBuffer.cpp and simplified where applicable due
+// to the more narrow use case. It may make sense in the future to abstract out
+// some of the common bits into a base class?
+
+XRWebGLDrawingBuffer::ColorBuffer::ColorBuffer(
+ XRWebGLDrawingBuffer* drawing_buffer,
+ const IntSize& size,
+ GLuint texture_id)
+ : drawing_buffer(drawing_buffer), size(size), texture_id(texture_id) {
+ drawing_buffer->ContextGL()->GenMailboxCHROMIUM(mailbox.name);
+}
+
+XRWebGLDrawingBuffer::ColorBuffer::~ColorBuffer() {
+ gpu::gles2::GLES2Interface* gl = drawing_buffer->ContextGL();
+ if (receive_sync_token.HasData())
+ gl->WaitSyncTokenCHROMIUM(receive_sync_token.GetConstData());
+ gl->DeleteTextures(1, &texture_id);
+}
+
+scoped_refptr<XRWebGLDrawingBuffer> XRWebGLDrawingBuffer::Create(
+ DrawingBuffer* drawing_buffer,
+ GLuint framebuffer,
+ const IntSize& size,
+ bool want_alpha_channel,
+ bool want_depth_buffer,
+ bool want_stencil_buffer,
+ bool want_antialiasing,
+ bool want_multiview) {
+ DCHECK(drawing_buffer);
+
+ // Don't proceeed if the context is already lost.
+ if (drawing_buffer->destroyed())
+ return nullptr;
+
+ gpu::gles2::GLES2Interface* gl = drawing_buffer->ContextGL();
+
+ std::unique_ptr<Extensions3DUtil> extensions_util =
+ Extensions3DUtil::Create(gl);
+ if (!extensions_util->IsValid()) {
+ return nullptr;
+ }
+
+ DCHECK(extensions_util->SupportsExtension("GL_OES_packed_depth_stencil"));
+ extensions_util->EnsureExtensionEnabled("GL_OES_packed_depth_stencil");
+ bool multisample_supported =
+ want_antialiasing &&
+ (extensions_util->SupportsExtension(
+ "GL_CHROMIUM_framebuffer_multisample") ||
+ extensions_util->SupportsExtension(
+ "GL_EXT_multisampled_render_to_texture")) &&
+ extensions_util->SupportsExtension("GL_OES_rgb8_rgba8");
+ if (multisample_supported) {
+ extensions_util->EnsureExtensionEnabled("GL_OES_rgb8_rgba8");
+ if (extensions_util->SupportsExtension(
+ "GL_CHROMIUM_framebuffer_multisample")) {
+ extensions_util->EnsureExtensionEnabled(
+ "GL_CHROMIUM_framebuffer_multisample");
+ } else {
+ extensions_util->EnsureExtensionEnabled(
+ "GL_EXT_multisampled_render_to_texture");
+ }
+ }
+ bool discard_framebuffer_supported =
+ extensions_util->SupportsExtension("GL_EXT_discard_framebuffer");
+ if (discard_framebuffer_supported)
+ extensions_util->EnsureExtensionEnabled("GL_EXT_discard_framebuffer");
+
+ // TODO(bajones): Support multiview.
+ bool multiview_supported = false;
+
+ scoped_refptr<XRWebGLDrawingBuffer> xr_drawing_buffer =
+ base::AdoptRef(new XRWebGLDrawingBuffer(
+ drawing_buffer, framebuffer, discard_framebuffer_supported,
+ want_alpha_channel, want_depth_buffer, want_stencil_buffer,
+ multiview_supported));
+ if (!xr_drawing_buffer->Initialize(size, multisample_supported,
+ multiview_supported)) {
+ DLOG(ERROR) << "XRWebGLDrawingBuffer Initialization Failed";
+ return nullptr;
+ }
+
+ return xr_drawing_buffer;
+}
+
+XRWebGLDrawingBuffer::XRWebGLDrawingBuffer(DrawingBuffer* drawing_buffer,
+ GLuint framebuffer,
+ bool discard_framebuffer_supported,
+ bool want_alpha_channel,
+ bool want_depth_buffer,
+ bool want_stencil_buffer,
+ bool multiview_supported)
+ : drawing_buffer_(drawing_buffer),
+ framebuffer_(framebuffer),
+ discard_framebuffer_supported_(discard_framebuffer_supported),
+ depth_(want_depth_buffer),
+ stencil_(want_stencil_buffer),
+ alpha_(want_alpha_channel),
+ multiview_(false) {}
+
+// TODO(bajones): The GL resources allocated in this function are leaking. Add
+// a way to clean up the buffers when the layer is GCed or the session ends.
+bool XRWebGLDrawingBuffer::Initialize(const IntSize& size,
+ bool use_multisampling,
+ bool use_multiview) {
+ gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL();
+
+ std::unique_ptr<Extensions3DUtil> extensions_util =
+ Extensions3DUtil::Create(gl);
+
+ gl->GetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size_);
+ DVLOG(2) << __FUNCTION__ << ": max_texture_size_=" << max_texture_size_;
+
+ // Check context capabilities
+ int max_sample_count = 0;
+ anti_aliasing_mode_ = kNone;
+ if (use_multisampling) {
+ gl->GetIntegerv(GL_MAX_SAMPLES_ANGLE, &max_sample_count);
+ anti_aliasing_mode_ = kMSAAExplicitResolve;
+ if (extensions_util->SupportsExtension(
+ "GL_EXT_multisampled_render_to_texture")) {
+ anti_aliasing_mode_ = kMSAAImplicitResolve;
+ } else if (extensions_util->SupportsExtension(
+ "GL_CHROMIUM_screen_space_antialiasing")) {
+ anti_aliasing_mode_ = kScreenSpaceAntialiasing;
+ }
+ }
+ DVLOG(2) << __FUNCTION__
+ << ": anti_aliasing_mode_=" << static_cast<int>(anti_aliasing_mode_);
+
+ storage_texture_supported_ =
+ (drawing_buffer_->webgl_version() > DrawingBuffer::kWebGL1 ||
+ extensions_util->SupportsExtension("GL_EXT_texture_storage")) &&
+ anti_aliasing_mode_ == kScreenSpaceAntialiasing;
+
+#if defined(OS_ANDROID)
+ // On Android devices use a smaller numer of samples to provide more breathing
+ // room for fill-rate-bound applications.
+ sample_count_ = std::min(2, max_sample_count);
+#else
+ sample_count_ = std::min(4, max_sample_count);
+#endif
+
+ Resize(size);
+
+ // It's possible that the drawing buffer allocation provokes a context loss,
+ // so check again just in case.
+ if (gl->GetGraphicsResetStatusKHR() != GL_NO_ERROR) {
+ return false;
+ }
+
+ return true;
+}
+
+gpu::gles2::GLES2Interface* XRWebGLDrawingBuffer::ContextGL() {
+ return drawing_buffer_->ContextGL();
+}
+
+void XRWebGLDrawingBuffer::SetMirrorClient(MirrorClient* client) {
+ mirror_client_ = client;
+ if (mirror_client_) {
+ // Immediately send a black 1x1 image to the mirror client to ensure that
+ // it has content to show.
+ sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(1, 1);
+ mirror_client_->OnMirrorImageAvailable(
+ StaticBitmapImage::Create(surface->makeImageSnapshot()), nullptr);
+ }
+}
+
+bool XRWebGLDrawingBuffer::ContextLost() {
+ return drawing_buffer_->destroyed();
+}
+
+IntSize XRWebGLDrawingBuffer::AdjustSize(const IntSize& new_size) {
+ // Ensure we always have at least a 1x1 buffer
+ float width = std::max(1, new_size.Width());
+ float height = std::max(1, new_size.Height());
+
+ float adjusted_scale =
+ std::min(static_cast<float>(max_texture_size_) / width,
+ static_cast<float>(max_texture_size_) / height);
+
+ // Clamp if the desired size is greater than the maximum texture size for the
+ // device. Scale both dimensions proportionally so that we avoid stretching.
+ if (adjusted_scale < 1.0f) {
+ width *= adjusted_scale;
+ height *= adjusted_scale;
+ }
+
+ return IntSize(width, height);
+}
+
+void XRWebGLDrawingBuffer::UseSharedBuffer(
+ const gpu::MailboxHolder& buffer_mailbox_holder) {
+ DVLOG(3) << __FUNCTION__;
+
+ gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL();
+
+ // Create a texture backed by the shared buffer image.
+ DCHECK(!shared_buffer_texture_id_);
+ shared_buffer_texture_id_ =
+ gl->CreateAndConsumeTextureCHROMIUM(buffer_mailbox_holder.mailbox.name);
+
+ if (WantExplicitResolve()) {
+ // Bind the shared texture to the destination framebuffer of
+ // the explicit resolve step.
+ if (!resolved_framebuffer_) {
+ gl->GenFramebuffers(1, &resolved_framebuffer_);
+ }
+ gl->BindFramebuffer(GL_FRAMEBUFFER, resolved_framebuffer_);
+ } else {
+ // Bind the shared texture directly to the drawing framebuffer.
+ gl->BindFramebuffer(GL_FRAMEBUFFER, framebuffer_);
+ }
+
+ if (anti_aliasing_mode_ == kMSAAImplicitResolve) {
+ gl->FramebufferTexture2DMultisampleEXT(
+ GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+ shared_buffer_texture_id_, 0, sample_count_);
+ } else {
+ // Explicit resolve, screen space antialiasing, or no antialiasing.
+ gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D, shared_buffer_texture_id_, 0);
+ }
+
+ if (!framebuffer_complete_checked_for_sharedbuffer_) {
+ DCHECK(gl->CheckFramebufferStatus(GL_FRAMEBUFFER) ==
+ GL_FRAMEBUFFER_COMPLETE);
+ framebuffer_complete_checked_for_sharedbuffer_ = true;
+ }
+
+ if (discard_framebuffer_supported_) {
+ const GLenum kAttachments[3] = {GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT,
+ GL_STENCIL_ATTACHMENT};
+ gl->DiscardFramebufferEXT(GL_FRAMEBUFFER, 3, kAttachments);
+ }
+
+ DrawingBuffer::Client* client = drawing_buffer_->client();
+ client->DrawingBufferClientRestoreFramebufferBinding();
+}
+
+void XRWebGLDrawingBuffer::DoneWithSharedBuffer() {
+ DVLOG(3) << __FUNCTION__;
+
+ BindAndResolveDestinationFramebuffer();
+
+ gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL();
+ if (discard_framebuffer_supported_) {
+ // Discard the depth and stencil attachments since we're done with them.
+ // Don't discard the color buffer, we do need this rendered into the
+ // shared buffer.
+ if (WantExplicitResolve()) {
+ gl->BindFramebuffer(GL_FRAMEBUFFER, resolved_framebuffer_);
+ } else {
+ gl->BindFramebuffer(GL_FRAMEBUFFER, framebuffer_);
+ }
+ const GLenum kAttachments[] = {GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT};
+ gl->DiscardFramebufferEXT(GL_FRAMEBUFFER,
+ sizeof(kAttachments) / sizeof(kAttachments[0]),
+ kAttachments);
+ }
+
+ // Always bind to the default framebuffer as a hint to the GPU to start
+ // rendering now.
+ gl->BindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ // Done with the texture created by CreateAndConsumeTexture, delete it.
+ DCHECK(shared_buffer_texture_id_);
+ gl->DeleteTextures(1, &shared_buffer_texture_id_);
+ shared_buffer_texture_id_ = 0;
+
+ DrawingBuffer::Client* client = drawing_buffer_->client();
+ client->DrawingBufferClientRestoreFramebufferBinding();
+}
+
+void XRWebGLDrawingBuffer::Resize(const IntSize& new_size) {
+ IntSize adjusted_size = AdjustSize(new_size);
+
+ if (adjusted_size == size_)
+ return;
+
+ // Don't attempt to resize if the context is lost.
+ if (ContextLost())
+ return;
+
+ gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL();
+
+ size_ = adjusted_size;
+
+ // Free all mailboxes, because they are now of the wrong size. Only the
+ // first call in this loop has any effect.
+ recycled_color_buffer_queue_.clear();
+
+ gl->BindFramebuffer(GL_FRAMEBUFFER, framebuffer_);
+
+ // Provide a depth and/or stencil buffer if requested.
+ if (depth_ || stencil_) {
+ if (depth_stencil_buffer_) {
+ gl->DeleteRenderbuffers(1, &depth_stencil_buffer_);
+ depth_stencil_buffer_ = 0;
+ }
+ gl->GenRenderbuffers(1, &depth_stencil_buffer_);
+ gl->BindRenderbuffer(GL_RENDERBUFFER, depth_stencil_buffer_);
+
+ if (anti_aliasing_mode_ == kMSAAImplicitResolve) {
+ gl->RenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, sample_count_,
+ GL_DEPTH24_STENCIL8_OES,
+ size_.Width(), size_.Height());
+ } else if (anti_aliasing_mode_ == kMSAAExplicitResolve) {
+ gl->RenderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER, sample_count_,
+ GL_DEPTH24_STENCIL8_OES,
+ size_.Width(), size_.Height());
+ } else {
+ gl->RenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES,
+ size_.Width(), size_.Height());
+ }
+
+ gl->FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
+ GL_RENDERBUFFER, depth_stencil_buffer_);
+ }
+
+ if (WantExplicitResolve()) {
+ // If we're doing an explicit multisample resolve use the main framebuffer
+ // as the multisample target and resolve into resolved_fbo_ when needed.
+ GLenum multisample_format = alpha_ ? GL_RGBA8_OES : GL_RGB8_OES;
+
+ if (multisample_renderbuffer_) {
+ gl->DeleteRenderbuffers(1, &multisample_renderbuffer_);
+ multisample_renderbuffer_ = 0;
+ }
+
+ gl->GenRenderbuffers(1, &multisample_renderbuffer_);
+ gl->BindRenderbuffer(GL_RENDERBUFFER, multisample_renderbuffer_);
+ gl->RenderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER, sample_count_,
+ multisample_format,
+ size_.Width(), size_.Height());
+
+ gl->FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_RENDERBUFFER, multisample_renderbuffer_);
+
+ // Now bind the resolve target framebuffer to attach the color textures to.
+ if (!resolved_framebuffer_) {
+ gl->GenFramebuffers(1, &resolved_framebuffer_);
+ }
+ gl->BindFramebuffer(GL_FRAMEBUFFER, resolved_framebuffer_);
+ }
+
+ back_color_buffer_ = CreateColorBuffer();
+ front_color_buffer_ = nullptr;
+
+ if (anti_aliasing_mode_ == kMSAAImplicitResolve) {
+ gl->FramebufferTexture2DMultisampleEXT(
+ GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+ back_color_buffer_->texture_id, 0, sample_count_);
+ } else {
+ gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D, back_color_buffer_->texture_id, 0);
+ }
+
+ if (!framebuffer_complete_checked_for_resize_) {
+ DCHECK(gl->CheckFramebufferStatus(GL_FRAMEBUFFER) ==
+ GL_FRAMEBUFFER_COMPLETE);
+ framebuffer_complete_checked_for_resize_ = true;
+ }
+
+ DrawingBuffer::Client* client = drawing_buffer_->client();
+ client->DrawingBufferClientRestoreRenderbufferBinding();
+ client->DrawingBufferClientRestoreFramebufferBinding();
+}
+
+scoped_refptr<XRWebGLDrawingBuffer::ColorBuffer>
+XRWebGLDrawingBuffer::CreateColorBuffer() {
+ gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL();
+
+ GLuint texture_id = 0;
+ gl->GenTextures(1, &texture_id);
+ gl->BindTexture(GL_TEXTURE_2D, texture_id);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ if (storage_texture_supported_) {
+ GLenum internal_storage_format = alpha_ ? GL_RGBA8 : GL_RGB8;
+ gl->TexStorage2DEXT(GL_TEXTURE_2D, 1, internal_storage_format,
+ size_.Width(), size_.Height());
+ } else {
+ GLenum gl_format = alpha_ ? GL_RGBA : GL_RGB;
+ gl->TexImage2D(GL_TEXTURE_2D, 0, gl_format, size_.Width(), size_.Height(),
+ 0, gl_format, GL_UNSIGNED_BYTE, nullptr);
+ }
+
+ DrawingBuffer::Client* client = drawing_buffer_->client();
+ client->DrawingBufferClientRestoreTexture2DBinding();
+
+ return base::AdoptRef(new ColorBuffer(this, size_, texture_id));
+}
+
+scoped_refptr<XRWebGLDrawingBuffer::ColorBuffer>
+XRWebGLDrawingBuffer::CreateOrRecycleColorBuffer() {
+ if (!recycled_color_buffer_queue_.IsEmpty()) {
+ scoped_refptr<ColorBuffer> recycled =
+ recycled_color_buffer_queue_.TakeLast();
+ if (recycled->receive_sync_token.HasData()) {
+ gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL();
+ gl->WaitSyncTokenCHROMIUM(recycled->receive_sync_token.GetData());
+ }
+ DCHECK(recycled->size == size_);
+ return recycled;
+ }
+ return CreateColorBuffer();
+}
+
+bool XRWebGLDrawingBuffer::WantExplicitResolve() const {
+ return anti_aliasing_mode_ == kMSAAExplicitResolve;
+}
+
+void XRWebGLDrawingBuffer::BindAndResolveDestinationFramebuffer() {
+ // Ensure that the mode-appropriate destination framebuffer's color
+ // attachment contains the drawn content after any antialiasing steps needed.
+
+ gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL();
+
+ DrawingBuffer::Client* client = drawing_buffer_->client();
+
+ // Resolve multisample buffers if needed
+ if (WantExplicitResolve()) {
+ DVLOG(3) << __FUNCTION__ << ": explicit resolve";
+ gl->BindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, framebuffer_);
+ gl->BindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, resolved_framebuffer_);
+ gl->Disable(GL_SCISSOR_TEST);
+
+ int width = size_.Width();
+ int height = size_.Height();
+ // Use NEAREST, because there is no scale performed during the blit.
+ gl->BlitFramebufferCHROMIUM(0, 0, width, height, 0, 0, width, height,
+ GL_COLOR_BUFFER_BIT, GL_NEAREST);
+
+ gl->BindFramebuffer(GL_FRAMEBUFFER, resolved_framebuffer_);
+
+ client->DrawingBufferClientRestoreScissorTest();
+ } else {
+ gl->BindFramebuffer(GL_FRAMEBUFFER, framebuffer_);
+ if (anti_aliasing_mode_ == kScreenSpaceAntialiasing) {
+ DVLOG(3) << __FUNCTION__ << ": screen space antialiasing";
+ gl->ApplyScreenSpaceAntialiasingCHROMIUM();
+ } else {
+ DVLOG(3) << __FUNCTION__ << ": nothing to do";
+ }
+ }
+
+ // On exit, leaves the destination framebuffer active. Caller is responsible
+ // for restoring client bindings.
+}
+
+// Swap the front and back buffers. After this call the front buffer should
+// contain the previously rendered content, resolved from the multisample
+// renderbuffer if needed.
+void XRWebGLDrawingBuffer::SwapColorBuffers() {
+ gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL();
+
+ DrawingBuffer::Client* client = drawing_buffer_->client();
+
+ BindAndResolveDestinationFramebuffer();
+
+ // Swap buffers
+ front_color_buffer_ = back_color_buffer_;
+ back_color_buffer_ = CreateOrRecycleColorBuffer();
+
+ if (anti_aliasing_mode_ == kMSAAImplicitResolve) {
+ gl->FramebufferTexture2DMultisampleEXT(
+ GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+ back_color_buffer_->texture_id, 0, sample_count_);
+ } else {
+ gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D, back_color_buffer_->texture_id, 0);
+ }
+
+ if (!framebuffer_complete_checked_for_swap_) {
+ DCHECK(gl->CheckFramebufferStatus(GL_FRAMEBUFFER) ==
+ GL_FRAMEBUFFER_COMPLETE);
+ framebuffer_complete_checked_for_swap_ = true;
+ }
+
+ if (discard_framebuffer_supported_) {
+ const GLenum kAttachments[3] = {GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT,
+ GL_STENCIL_ATTACHMENT};
+ gl->DiscardFramebufferEXT(GL_FRAMEBUFFER, 3, kAttachments);
+ }
+
+ client->DrawingBufferClientRestoreFramebufferBinding();
+}
+
+scoped_refptr<StaticBitmapImage>
+XRWebGLDrawingBuffer::TransferToStaticBitmapImage(
+ std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback) {
+ gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL();
+ scoped_refptr<ColorBuffer> buffer;
+ bool success = false;
+
+ // Ensure the context isn't lost and the framebuffer is complete before
+ // continuing.
+ if (!ContextLost()) {
+ SwapColorBuffers();
+
+ buffer = front_color_buffer_;
+
+ gl->ProduceTextureDirectCHROMIUM(buffer->texture_id, buffer->mailbox.name);
+ gl->GenUnverifiedSyncTokenCHROMIUM(buffer->produce_sync_token.GetData());
+
+ // This should only fail if the context is lost during the buffer swap.
+ if (buffer->produce_sync_token.HasData()) {
+ success = true;
+ }
+ }
+
+ if (!success) {
+ // If we can't get a mailbox, return an transparent black ImageBitmap.
+ // The only situation in which this could happen is when two or more calls
+ // to transferToImageBitmap are made back-to-back, if the framebuffer is
+ // incomplete (likely due to a failed buffer allocation), or when the
+ // context gets lost.
+ sk_sp<SkSurface> surface =
+ SkSurface::MakeRasterN32Premul(size_.Width(), size_.Height());
+ return StaticBitmapImage::Create(surface->makeImageSnapshot());
+ }
+
+ // This holds a ref on the XRWebGLDrawingBuffer that will keep it alive
+ // until the mailbox is released (and while the callback is running).
+ auto func =
+ WTF::Bind(mirror_client_ ? &XRWebGLDrawingBuffer::MailboxReleasedToMirror
+ : &XRWebGLDrawingBuffer::MailboxReleased,
+ scoped_refptr<XRWebGLDrawingBuffer>(this), buffer);
+
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback =
+ viz::SingleReleaseCallback::Create(std::move(func));
+
+ // Make our own textureId that is a reference on the same texture backing
+ // being used as the front buffer. We do not need to wait on the sync
+ // token since the mailbox was produced on the same GL context that we are
+ // using here. Similarly, the release callback will run on the same context so
+ // we don't need to send a sync token for this consume action back to it.
+ GLuint texture_id = gl->CreateAndConsumeTextureCHROMIUM(buffer->mailbox.name);
+
+ if (out_release_callback) {
+ *out_release_callback = std::move(release_callback);
+ } else {
+ release_callback->Run(gpu::SyncToken(), true /* lost_resource */);
+ }
+
+ return AcceleratedStaticBitmapImage::CreateFromWebGLContextImage(
+ buffer->mailbox, buffer->produce_sync_token, texture_id,
+ drawing_buffer_->ContextProviderWeakPtr(), size_);
+}
+
+void XRWebGLDrawingBuffer::MailboxReleased(
+ scoped_refptr<ColorBuffer> color_buffer,
+ const gpu::SyncToken& sync_token,
+ bool lost_resource) {
+ // If the mailbox has been returned by the compositor then it is no
+ // longer being presented, and so is no longer the front buffer.
+ if (color_buffer == front_color_buffer_)
+ front_color_buffer_ = nullptr;
+
+ // Update the SyncToken to ensure that we will wait for it even if we
+ // immediately destroy this buffer.
+ color_buffer->receive_sync_token = sync_token;
+
+ if (drawing_buffer_->destroyed() || color_buffer->size != size_ ||
+ lost_resource) {
+ return;
+ }
+
+ const size_t cache_limit = 2;
+ while (recycled_color_buffer_queue_.size() >= cache_limit)
+ recycled_color_buffer_queue_.TakeLast();
+
+ recycled_color_buffer_queue_.push_front(color_buffer);
+}
+
+void XRWebGLDrawingBuffer::MailboxReleasedToMirror(
+ scoped_refptr<ColorBuffer> color_buffer,
+ const gpu::SyncToken& sync_token,
+ bool lost_resource) {
+ if (!mirror_client_ || lost_resource) {
+ MailboxReleased(std::move(color_buffer), sync_token, lost_resource);
+ return;
+ }
+
+ gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL();
+ color_buffer->receive_sync_token = sync_token;
+
+ auto func =
+ WTF::Bind(&XRWebGLDrawingBuffer::MailboxReleased,
+ scoped_refptr<XRWebGLDrawingBuffer>(this), color_buffer);
+
+ std::unique_ptr<viz::SingleReleaseCallback> release_callback =
+ viz::SingleReleaseCallback::Create(std::move(func));
+
+ GLuint texture_id =
+ gl->CreateAndConsumeTextureCHROMIUM(color_buffer->mailbox.name);
+
+ scoped_refptr<StaticBitmapImage> image =
+ AcceleratedStaticBitmapImage::CreateFromWebGLContextImage(
+ color_buffer->mailbox, color_buffer->produce_sync_token, texture_id,
+ drawing_buffer_->ContextProviderWeakPtr(), color_buffer->size);
+
+ mirror_client_->OnMirrorImageAvailable(std::move(image),
+ std::move(release_callback));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.h b/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.h
new file mode 100644
index 00000000000..99514de6d6d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.h
@@ -0,0 +1,164 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_XR_WEBGL_DRAWING_BUFFER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_XR_WEBGL_DRAWING_BUFFER_H_
+
+#include "cc/layers/texture_layer_client.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "gpu/command_buffer/common/mailbox_holder.h"
+#include "third_party/blink/renderer/platform/geometry/int_size.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/deque.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+
+namespace blink {
+
+class DrawingBuffer;
+class StaticBitmapImage;
+
+class PLATFORM_EXPORT XRWebGLDrawingBuffer
+ : public RefCounted<XRWebGLDrawingBuffer> {
+ public:
+ static scoped_refptr<XRWebGLDrawingBuffer> Create(DrawingBuffer*,
+ GLuint framebuffer,
+ const IntSize&,
+ bool want_alpha_channel,
+ bool want_depth_buffer,
+ bool want_stencil_buffer,
+ bool want_antialiasing,
+ bool want_multiview);
+
+ gpu::gles2::GLES2Interface* ContextGL();
+ bool ContextLost();
+
+ const IntSize& size() const { return size_; }
+
+ bool antialias() const { return anti_aliasing_mode_ != kNone; }
+ bool depth() const { return depth_; }
+ bool stencil() const { return stencil_; }
+ bool alpha() const { return alpha_; }
+ bool multiview() const { return multiview_; }
+
+ void Resize(const IntSize&);
+
+ scoped_refptr<StaticBitmapImage> TransferToStaticBitmapImage(
+ std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback);
+
+ class MirrorClient {
+ public:
+ virtual void OnMirrorImageAvailable(
+ scoped_refptr<StaticBitmapImage>,
+ std::unique_ptr<viz::SingleReleaseCallback>) = 0;
+ };
+
+ void SetMirrorClient(MirrorClient*);
+
+ void UseSharedBuffer(const gpu::MailboxHolder&);
+ void DoneWithSharedBuffer();
+
+ private:
+ struct ColorBuffer : public RefCounted<ColorBuffer> {
+ ColorBuffer(XRWebGLDrawingBuffer*, const IntSize&, GLuint texture_id);
+ ~ColorBuffer();
+
+ // The owning XRWebGLDrawingBuffer. Note that DrawingBuffer is explicitly
+ // destroyed by the beginDestruction method, which will eventually drain all
+ // of its ColorBuffers.
+ scoped_refptr<XRWebGLDrawingBuffer> drawing_buffer;
+ const IntSize size;
+ const GLuint texture_id = 0;
+
+ // The mailbox used to send this buffer to the compositor.
+ gpu::Mailbox mailbox;
+
+ // The sync token for when this buffer was sent to the compositor.
+ gpu::SyncToken produce_sync_token;
+
+ // The sync token for when this buffer was received back from the
+ // compositor.
+ gpu::SyncToken receive_sync_token;
+
+ private:
+ WTF_MAKE_NONCOPYABLE(ColorBuffer);
+ };
+
+ XRWebGLDrawingBuffer(DrawingBuffer*,
+ GLuint framebuffer,
+ bool discard_framebuffer_supported,
+ bool want_alpha_channel,
+ bool want_depth_buffer,
+ bool want_stencil_buffer,
+ bool multiview_supported);
+
+ bool Initialize(const IntSize&, bool use_multisampling, bool use_multiview);
+
+ IntSize AdjustSize(const IntSize&);
+
+ scoped_refptr<ColorBuffer> CreateColorBuffer();
+ scoped_refptr<ColorBuffer> CreateOrRecycleColorBuffer();
+
+ bool WantExplicitResolve() const;
+ void BindAndResolveDestinationFramebuffer();
+ void SwapColorBuffers();
+
+ void MailboxReleased(scoped_refptr<ColorBuffer>,
+ const gpu::SyncToken&,
+ bool lost_resource);
+ void MailboxReleasedToMirror(scoped_refptr<ColorBuffer>,
+ const gpu::SyncToken&,
+ bool lost_resource);
+
+ // Reference to the DrawingBuffer that owns the GL context for this object.
+ scoped_refptr<DrawingBuffer> drawing_buffer_;
+
+ const GLuint framebuffer_ = 0;
+ GLuint resolved_framebuffer_ = 0;
+ GLuint multisample_renderbuffer_ = 0;
+ scoped_refptr<ColorBuffer> back_color_buffer_ = 0;
+ scoped_refptr<ColorBuffer> front_color_buffer_ = 0;
+ GLuint depth_stencil_buffer_ = 0;
+ IntSize size_;
+
+ // Nonzero for shared buffer mode from UseSharedBuffer until
+ // DoneWithSharedBuffer.
+ GLuint shared_buffer_texture_id_ = 0;
+
+ // Checking framebuffer completeness is extremely expensive, it's basically a
+ // glFinish followed by a synchronous wait for a reply. Do so only once per
+ // code path, and only in DCHECK mode.
+ bool framebuffer_complete_checked_for_resize_ = false;
+ bool framebuffer_complete_checked_for_swap_ = false;
+ bool framebuffer_complete_checked_for_sharedbuffer_ = false;
+
+ // Color buffers that were released by the XR compositor can be used again.
+ Deque<scoped_refptr<ColorBuffer>> recycled_color_buffer_queue_;
+
+ bool discard_framebuffer_supported_;
+ bool depth_;
+ bool stencil_;
+ bool alpha_;
+ bool multiview_;
+
+ enum AntialiasingMode {
+ kNone,
+ kMSAAImplicitResolve,
+ kMSAAExplicitResolve,
+ kScreenSpaceAntialiasing,
+ };
+
+ AntialiasingMode anti_aliasing_mode_ = kNone;
+
+ bool storage_texture_supported_ = false;
+ int max_texture_size_ = 0;
+ int sample_count_ = 0;
+
+ MirrorClient* mirror_client_ = nullptr;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_XR_WEBGL_DRAWING_BUFFER_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu_memory_buffer_image_copy.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu_memory_buffer_image_copy.cc
new file mode 100644
index 00000000000..e8a07967cb7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu_memory_buffer_image_copy.cc
@@ -0,0 +1,104 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_graphics_context_3d_provider.h"
+#include "third_party/blink/renderer/platform/graphics/gpu_memory_buffer_image_copy.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_types_3d.h"
+#include "third_party/blink/renderer/platform/graphics/image.h"
+#include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
+
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+
+namespace blink {
+
+GpuMemoryBufferImageCopy::GpuMemoryBufferImageCopy(
+ gpu::gles2::GLES2Interface* gl)
+ : gl_(gl) {
+}
+
+GpuMemoryBufferImageCopy::~GpuMemoryBufferImageCopy() {
+}
+
+bool GpuMemoryBufferImageCopy::EnsureMemoryBuffer(int width, int height) {
+ // Create a new memorybuffer if the size has changed, or we don't have one.
+ if (last_width_ != width || last_height_ != height || !gpu_memory_buffer_) {
+ gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager =
+ Platform::Current()->GetGpuMemoryBufferManager();
+ if (!gpu_memory_buffer_manager)
+ return false;
+
+ gpu_memory_buffer_ = gpu_memory_buffer_manager->CreateGpuMemoryBuffer(
+ gfx::Size(width, height), gfx::BufferFormat::RGBA_8888,
+ gfx::BufferUsage::SCANOUT, gpu::kNullSurfaceHandle);
+ if (!gpu_memory_buffer_)
+ return false;
+
+ last_width_ = width;
+ last_height_ = height;
+ }
+ return true;
+}
+
+gfx::GpuMemoryBuffer* GpuMemoryBufferImageCopy::CopyImage(Image* image) {
+ if (!image)
+ return nullptr;
+
+ TRACE_EVENT0("gpu", "GpuMemoryBufferImageCopy::CopyImage");
+
+ int width = image->width();
+ int height = image->height();
+ if (!EnsureMemoryBuffer(width, height))
+ return nullptr;
+
+ // Bind the write framebuffer to our memory buffer.
+ GLuint image_id = gl_->CreateImageCHROMIUM(
+ gpu_memory_buffer_->AsClientBuffer(), width, height, GL_RGBA);
+ if (!image_id)
+ return nullptr;
+ GLuint dest_texture_id;
+ gl_->GenTextures(1, &dest_texture_id);
+ GLenum target = GL_TEXTURE_2D;
+ {
+ gl_->BindTexture(target, dest_texture_id);
+ gl_->TexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ gl_->TexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ gl_->TexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ gl_->TexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ gl_->BindTexImage2DCHROMIUM(target, image_id);
+ }
+ gl_->BindTexture(GL_TEXTURE_2D, 0);
+
+ // Bind the read framebuffer to our image.
+ StaticBitmapImage* static_image = static_cast<StaticBitmapImage*>(image);
+ static_image->EnsureMailbox(kOrderingBarrier, GL_NEAREST);
+ auto mailbox = static_image->GetMailbox();
+ auto sync_token = static_image->GetSyncToken();
+ // Not strictly necessary since we are on the same context, but keeping
+ // for cleanliness and in case we ever move off the same context.
+ gl_->WaitSyncTokenCHROMIUM(sync_token.GetData());
+ GLuint source_texture_id = gl_->CreateAndConsumeTextureCHROMIUM(mailbox.name);
+ gl_->BindTexture(GL_TEXTURE_2D, 0);
+
+ gl_->CopySubTextureCHROMIUM(source_texture_id, 0, GL_TEXTURE_2D,
+ dest_texture_id, 0, 0, 0, 0, 0, width, height,
+ false, false, false);
+
+ // Cleanup the read framebuffer, associated image and texture.
+ gl_->BindTexture(GL_TEXTURE_2D, 0);
+ gl_->DeleteTextures(1, &source_texture_id);
+
+ // Cleanup the draw framebuffer, associated image and texture.
+ gl_->BindTexture(target, dest_texture_id);
+ gl_->ReleaseTexImage2DCHROMIUM(target, image_id);
+ gl_->DestroyImageCHROMIUM(image_id);
+ gl_->DeleteTextures(1, &dest_texture_id);
+ gl_->BindTexture(target, 0);
+
+ gl_->ShallowFlushCHROMIUM();
+ return gpu_memory_buffer_.get();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu_memory_buffer_image_copy.h b/chromium/third_party/blink/renderer/platform/graphics/gpu_memory_buffer_image_copy.h
new file mode 100644
index 00000000000..e25e7322163
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu_memory_buffer_image_copy.h
@@ -0,0 +1,38 @@
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_MEMORY_BUFFER_IMAGE_COPY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_MEMORY_BUFFER_IMAGE_COPY_H_
+
+#include <memory>
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "ui/gfx/gpu_memory_buffer.h"
+
+namespace blink {
+
+class Image;
+
+class PLATFORM_EXPORT GpuMemoryBufferImageCopy {
+ public:
+ GpuMemoryBufferImageCopy(gpu::gles2::GLES2Interface*);
+ ~GpuMemoryBufferImageCopy();
+
+ gfx::GpuMemoryBuffer* CopyImage(Image*);
+
+ private:
+ bool EnsureMemoryBuffer(int width, int height);
+ void OnContextLost();
+ void OnContextError(const char* msg, int32_t id);
+
+ std::unique_ptr<gfx::GpuMemoryBuffer> m_currentBuffer;
+
+ int last_width_ = 0;
+ int last_height_ = 0;
+ gpu::gles2::GLES2Interface* gl_;
+ std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer_;
+
+ // TODO(billorr): Add error handling for context loss or GL errors before we
+ // enable this by default.
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_MEMORY_BUFFER_IMAGE_COPY_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gradient.cc b/chromium/third_party/blink/renderer/platform/graphics/gradient.cc
new file mode 100644
index 00000000000..d46687d087f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gradient.cc
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2006, 2007, 2008, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2007 Alp Toker <alp@atoker.com>
+ * Copyright (C) 2013 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/graphics/gradient.h"
+
+#include <algorithm>
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_shader.h"
+#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkMatrix.h"
+#include "third_party/skia/include/core/SkShader.h"
+#include "third_party/skia/include/core/SkTLazy.h"
+#include "third_party/skia/include/effects/SkGradientShader.h"
+
+namespace blink {
+
+Gradient::Gradient(Type type,
+ GradientSpreadMethod spread_method,
+ ColorInterpolation interpolation)
+ : type_(type),
+ spread_method_(spread_method),
+ color_interpolation_(interpolation),
+ stops_sorted_(true) {}
+
+Gradient::~Gradient() = default;
+
+static inline bool CompareStops(const Gradient::ColorStop& a,
+ const Gradient::ColorStop& b) {
+ return a.stop < b.stop;
+}
+
+void Gradient::AddColorStop(const Gradient::ColorStop& stop) {
+ if (stops_.IsEmpty()) {
+ stops_sorted_ = true;
+ } else {
+ stops_sorted_ = stops_sorted_ && CompareStops(stops_.back(), stop);
+ }
+
+ stops_.push_back(stop);
+ cached_shader_.reset();
+}
+
+void Gradient::AddColorStops(const Vector<Gradient::ColorStop>& stops) {
+ for (const auto& stop : stops)
+ AddColorStop(stop);
+}
+
+void Gradient::SortStopsIfNecessary() {
+ if (stops_sorted_)
+ return;
+
+ stops_sorted_ = true;
+
+ if (!stops_.size())
+ return;
+
+ std::stable_sort(stops_.begin(), stops_.end(), CompareStops);
+}
+
+// FIXME: This would be more at home as Color::operator SkColor.
+static inline SkColor MakeSkColor(const Color& c) {
+ return SkColorSetARGB(c.Alpha(), c.Red(), c.Green(), c.Blue());
+}
+
+// Collect sorted stop position and color information into the pos and colors
+// buffers, ensuring stops at both 0.0 and 1.0.
+// TODO(fmalita): theoretically Skia should provide the same 0.0/1.0 padding
+// (making this logic redundant), but in practice there are rendering diffs;
+// investigate.
+void Gradient::FillSkiaStops(ColorBuffer& colors, OffsetBuffer& pos) const {
+ if (stops_.IsEmpty()) {
+ // A gradient with no stops must be transparent black.
+ pos.push_back(WebCoreFloatToSkScalar(0));
+ colors.push_back(SK_ColorTRANSPARENT);
+ } else if (stops_.front().stop > 0) {
+ // Copy the first stop to 0.0. The first stop position may have a slight
+ // rounding error, but we don't care in this float comparison, since
+ // 0.0 comes through cleanly and people aren't likely to want a gradient
+ // with a stop at (0 + epsilon).
+ pos.push_back(WebCoreFloatToSkScalar(0));
+ if (color_filter_) {
+ colors.push_back(
+ color_filter_->filterColor(MakeSkColor(stops_.front().color)));
+ } else {
+ colors.push_back(MakeSkColor(stops_.front().color));
+ }
+ }
+
+ for (const auto& stop : stops_) {
+ pos.push_back(WebCoreFloatToSkScalar(stop.stop));
+ if (color_filter_)
+ colors.push_back(color_filter_->filterColor(MakeSkColor(stop.color)));
+ else
+ colors.push_back(MakeSkColor(stop.color));
+ }
+
+ // Copy the last stop to 1.0 if needed. See comment above about this float
+ // comparison.
+ DCHECK(!pos.IsEmpty());
+ if (pos.back() < 1) {
+ pos.push_back(WebCoreFloatToSkScalar(1));
+ colors.push_back(colors.back());
+ }
+}
+
+sk_sp<PaintShader> Gradient::CreateShaderInternal(
+ const SkMatrix& local_matrix) {
+ SortStopsIfNecessary();
+ DCHECK(stops_sorted_);
+
+ ColorBuffer colors;
+ colors.ReserveCapacity(stops_.size());
+ OffsetBuffer pos;
+ pos.ReserveCapacity(stops_.size());
+
+ FillSkiaStops(colors, pos);
+ DCHECK_GE(colors.size(), 2ul);
+ DCHECK_EQ(pos.size(), colors.size());
+
+ SkShader::TileMode tile = SkShader::kClamp_TileMode;
+ switch (spread_method_) {
+ case kSpreadMethodReflect:
+ tile = SkShader::kMirror_TileMode;
+ break;
+ case kSpreadMethodRepeat:
+ tile = SkShader::kRepeat_TileMode;
+ break;
+ case kSpreadMethodPad:
+ tile = SkShader::kClamp_TileMode;
+ break;
+ }
+
+ uint32_t flags = color_interpolation_ == ColorInterpolation::kPremultiplied
+ ? SkGradientShader::kInterpolateColorsInPremul_Flag
+ : 0;
+ sk_sp<PaintShader> shader =
+ CreateShader(colors, pos, tile, flags, local_matrix, colors.back());
+ DCHECK(shader);
+
+ return shader;
+}
+
+void Gradient::ApplyToFlags(PaintFlags& flags, const SkMatrix& local_matrix) {
+ if (!cached_shader_ || local_matrix != cached_shader_->GetLocalMatrix() ||
+ flags.getColorFilter().get() != color_filter_.get()) {
+ color_filter_ = flags.getColorFilter();
+ flags.setColorFilter(nullptr);
+ cached_shader_ = CreateShaderInternal(local_matrix);
+ }
+
+ flags.setShader(cached_shader_);
+
+ // Legacy behavior: gradients are always dithered.
+ flags.setDither(true);
+}
+
+namespace {
+
+class LinearGradient final : public Gradient {
+ public:
+ LinearGradient(const FloatPoint& p0,
+ const FloatPoint& p1,
+ GradientSpreadMethod spread_method,
+ ColorInterpolation interpolation)
+ : Gradient(Type::kLinear, spread_method, interpolation),
+ p0_(p0),
+ p1_(p1) {}
+
+ protected:
+ sk_sp<PaintShader> CreateShader(const ColorBuffer& colors,
+ const OffsetBuffer& pos,
+ SkShader::TileMode tile_mode,
+ uint32_t flags,
+ const SkMatrix& local_matrix,
+ SkColor fallback_color) const override {
+ SkPoint pts[2] = {p0_.Data(), p1_.Data()};
+ return PaintShader::MakeLinearGradient(
+ pts, colors.data(), pos.data(), static_cast<int>(colors.size()),
+ tile_mode, flags, &local_matrix, fallback_color);
+ }
+
+ private:
+ const FloatPoint p0_;
+ const FloatPoint p1_;
+};
+
+class RadialGradient final : public Gradient {
+ public:
+ RadialGradient(const FloatPoint& p0,
+ float r0,
+ const FloatPoint& p1,
+ float r1,
+ float aspect_ratio,
+ GradientSpreadMethod spread_method,
+ ColorInterpolation interpolation)
+ : Gradient(Type::kRadial, spread_method, interpolation),
+ p0_(p0),
+ p1_(p1),
+ r0_(r0),
+ r1_(r1),
+ aspect_ratio_(aspect_ratio) {}
+
+ protected:
+ sk_sp<PaintShader> CreateShader(const ColorBuffer& colors,
+ const OffsetBuffer& pos,
+ SkShader::TileMode tile_mode,
+ uint32_t flags,
+ const SkMatrix& local_matrix,
+ SkColor fallback_color) const override {
+ SkTCopyOnFirstWrite<SkMatrix> adjusted_local_matrix(local_matrix);
+ if (aspect_ratio_ != 1) {
+ // CSS3 elliptical gradients: apply the elliptical scaling at the
+ // gradient center point.
+ DCHECK(p0_ == p1_);
+ adjusted_local_matrix.writable()->preScale(1, 1 / aspect_ratio_, p0_.X(),
+ p0_.Y());
+ }
+
+ // The radii we give to Skia must be positive. If we're given a
+ // negative radius, ask for zero instead.
+ const SkScalar radius0 = std::max(WebCoreFloatToSkScalar(r0_), 0.0f);
+ const SkScalar radius1 = std::max(WebCoreFloatToSkScalar(r1_), 0.0f);
+ return PaintShader::MakeTwoPointConicalGradient(
+ p0_.Data(), radius0, p1_.Data(), radius1, colors.data(), pos.data(),
+ static_cast<int>(colors.size()), tile_mode, flags,
+ adjusted_local_matrix, fallback_color);
+ }
+
+ private:
+ const FloatPoint p0_;
+ const FloatPoint p1_;
+ const float r0_;
+ const float r1_;
+ const float aspect_ratio_; // For elliptical gradient, width / height.
+};
+
+class ConicGradient final : public Gradient {
+ public:
+ ConicGradient(const FloatPoint& position,
+ float rotation,
+ float start_angle,
+ float end_angle,
+ GradientSpreadMethod spread_method,
+ ColorInterpolation interpolation)
+ : Gradient(Type::kConic, spread_method, interpolation),
+ position_(position),
+ rotation_(rotation),
+ start_angle_(start_angle),
+ end_angle_(end_angle) {}
+
+ protected:
+ sk_sp<PaintShader> CreateShader(const ColorBuffer& colors,
+ const OffsetBuffer& pos,
+ SkShader::TileMode tile_mode,
+ uint32_t flags,
+ const SkMatrix& local_matrix,
+ SkColor fallback_color) const override {
+ // Skia's sweep gradient angles are relative to the x-axis, not the y-axis.
+ const float skia_rotation = rotation_ - 90;
+ SkTCopyOnFirstWrite<SkMatrix> adjusted_local_matrix(local_matrix);
+ if (skia_rotation) {
+ adjusted_local_matrix.writable()->preRotate(skia_rotation, position_.X(),
+ position_.Y());
+ }
+
+ return PaintShader::MakeSweepGradient(
+ position_.X(), position_.Y(), colors.data(), pos.data(),
+ static_cast<int>(colors.size()), tile_mode, start_angle_, end_angle_,
+ flags, adjusted_local_matrix, fallback_color);
+ }
+
+ private:
+ const FloatPoint position_; // center point
+ const float rotation_; // global rotation (deg)
+ const float start_angle_; // angle (deg) corresponding to color position 0
+ const float end_angle_; // angle (deg) corresponding to color position 1
+};
+
+} // anonymous ns
+
+scoped_refptr<Gradient> Gradient::CreateLinear(
+ const FloatPoint& p0,
+ const FloatPoint& p1,
+ GradientSpreadMethod spread_method,
+ ColorInterpolation interpolation) {
+ return base::AdoptRef(
+ new LinearGradient(p0, p1, spread_method, interpolation));
+}
+
+scoped_refptr<Gradient> Gradient::CreateRadial(
+ const FloatPoint& p0,
+ float r0,
+ const FloatPoint& p1,
+ float r1,
+ float aspect_ratio,
+ GradientSpreadMethod spread_method,
+ ColorInterpolation interpolation) {
+ return base::AdoptRef(new RadialGradient(p0, r0, p1, r1, aspect_ratio,
+ spread_method, interpolation));
+}
+
+scoped_refptr<Gradient> Gradient::CreateConic(
+ const FloatPoint& position,
+ float rotation,
+ float start_angle,
+ float end_angle,
+ GradientSpreadMethod spread_method,
+ ColorInterpolation interpolation) {
+ return base::AdoptRef(new ConicGradient(position, rotation, start_angle,
+ end_angle, spread_method,
+ interpolation));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gradient.h b/chromium/third_party/blink/renderer/platform/graphics/gradient.h
new file mode 100644
index 00000000000..20f5231a3fd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gradient.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2006, 2007, 2008, 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2007 Alp Toker <alp@atoker.com>
+ * Copyright (C) 2008 Torch Mobile, Inc.
+ * Copyright (C) 2013 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GRADIENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GRADIENT_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/graphics/color.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_types.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_shader.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+class SkMatrix;
+class SkShader;
+
+namespace blink {
+
+class FloatPoint;
+
+class PLATFORM_EXPORT Gradient : public RefCounted<Gradient> {
+ WTF_MAKE_NONCOPYABLE(Gradient);
+
+ public:
+ enum class Type { kLinear, kRadial, kConic };
+
+ enum class ColorInterpolation {
+ kPremultiplied,
+ kUnpremultiplied,
+ };
+
+ static scoped_refptr<Gradient> CreateLinear(
+ const FloatPoint& p0,
+ const FloatPoint& p1,
+ GradientSpreadMethod = kSpreadMethodPad,
+ ColorInterpolation = ColorInterpolation::kUnpremultiplied);
+
+ static scoped_refptr<Gradient> CreateRadial(
+ const FloatPoint& p0,
+ float r0,
+ const FloatPoint& p1,
+ float r1,
+ float aspect_ratio = 1,
+ GradientSpreadMethod = kSpreadMethodPad,
+ ColorInterpolation = ColorInterpolation::kUnpremultiplied);
+
+ static scoped_refptr<Gradient> CreateConic(
+ const FloatPoint& position,
+ float rotation,
+ float start_angle,
+ float end_angle,
+ GradientSpreadMethod = kSpreadMethodPad,
+ ColorInterpolation = ColorInterpolation::kUnpremultiplied);
+
+ virtual ~Gradient();
+
+ Type GetType() const { return type_; }
+
+ struct ColorStop {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ float stop;
+ Color color;
+
+ ColorStop(float s, const Color& c) : stop(s), color(c) {}
+ };
+ void AddColorStop(const ColorStop&);
+ void AddColorStop(float value, const Color& color) {
+ AddColorStop(ColorStop(value, color));
+ }
+ void AddColorStops(const Vector<Gradient::ColorStop>&);
+
+ void ApplyToFlags(PaintFlags&, const SkMatrix& local_matrix);
+
+ protected:
+ Gradient(Type, GradientSpreadMethod, ColorInterpolation);
+
+ using ColorBuffer = Vector<SkColor, 8>;
+ using OffsetBuffer = Vector<SkScalar, 8>;
+ virtual sk_sp<PaintShader> CreateShader(const ColorBuffer&,
+ const OffsetBuffer&,
+ SkShader::TileMode,
+ uint32_t flags,
+ const SkMatrix&,
+ SkColor) const = 0;
+
+ private:
+ sk_sp<PaintShader> CreateShaderInternal(const SkMatrix& local_matrix);
+
+ sk_sp<SkColorFilter> color_filter_;
+
+ void SortStopsIfNecessary();
+ void FillSkiaStops(ColorBuffer&, OffsetBuffer&) const;
+
+ const Type type_;
+ const GradientSpreadMethod spread_method_;
+ const ColorInterpolation color_interpolation_;
+
+ Vector<ColorStop, 2> stops_;
+ bool stops_sorted_;
+
+ mutable sk_sp<PaintShader> cached_shader_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gradient_generated_image.cc b/chromium/third_party/blink/renderer/platform/graphics/gradient_generated_image.cc
new file mode 100644
index 00000000000..ebd3afe54aa
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gradient_generated_image.cc
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2008, 2009, 2010, 2012 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/graphics/gradient_generated_image.h"
+
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/geometry/int_size.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+
+namespace blink {
+
+void GradientGeneratedImage::Draw(PaintCanvas* canvas,
+ const PaintFlags& flags,
+ const FloatRect& dest_rect,
+ const FloatRect& src_rect,
+ RespectImageOrientationEnum,
+ ImageClampingMode,
+ ImageDecodingMode) {
+ SkRect visible_src_rect = src_rect;
+ if (!visible_src_rect.intersect(
+ SkRect::MakeWH(size_.Width(), size_.Height())))
+ return;
+
+ const SkMatrix transform =
+ SkMatrix::MakeRectToRect(src_rect, dest_rect, SkMatrix::kFill_ScaleToFit);
+ SkRect visible_dest_rect;
+ transform.mapRect(&visible_dest_rect, visible_src_rect);
+
+ PaintFlags gradient_flags(flags);
+ gradient_->ApplyToFlags(gradient_flags, transform);
+ canvas->drawRect(visible_dest_rect, gradient_flags);
+}
+
+void GradientGeneratedImage::DrawTile(GraphicsContext& context,
+ const FloatRect& src_rect) {
+ // TODO(ccameron): This function should not ignore |context|'s color behavior.
+ // https://crbug.com/672306
+ PaintFlags gradient_flags(context.FillFlags());
+ gradient_->ApplyToFlags(gradient_flags, SkMatrix::I());
+
+ context.DrawRect(src_rect, gradient_flags);
+}
+
+bool GradientGeneratedImage::ApplyShader(PaintFlags& flags,
+ const SkMatrix& local_matrix) {
+ DCHECK(gradient_);
+ gradient_->ApplyToFlags(flags, local_matrix);
+
+ return true;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gradient_generated_image.h b/chromium/third_party/blink/renderer/platform/graphics/gradient_generated_image.h
new file mode 100644
index 00000000000..ce79328d42b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/gradient_generated_image.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2008, 2012 Apple Computer, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GRADIENT_GENERATED_IMAGE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GRADIENT_GENERATED_IMAGE_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/graphics/generated_image.h"
+#include "third_party/blink/renderer/platform/graphics/gradient.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT GradientGeneratedImage final : public GeneratedImage {
+ public:
+ static scoped_refptr<GradientGeneratedImage> Create(
+ scoped_refptr<Gradient> generator,
+ const FloatSize& size) {
+ return base::AdoptRef(
+ new GradientGeneratedImage(std::move(generator), size));
+ }
+
+ ~GradientGeneratedImage() override = default;
+
+ bool ApplyShader(PaintFlags&, const SkMatrix&) override;
+
+ protected:
+ void Draw(PaintCanvas*,
+ const PaintFlags&,
+ const FloatRect&,
+ const FloatRect&,
+ RespectImageOrientationEnum,
+ ImageClampingMode,
+ ImageDecodingMode) override;
+ void DrawTile(GraphicsContext&, const FloatRect&) override;
+
+ GradientGeneratedImage(scoped_refptr<Gradient> generator,
+ const FloatSize& size)
+ : GeneratedImage(size), gradient_(std::move(generator)) {}
+
+ scoped_refptr<Gradient> gradient_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/graphics/graphics_context.cc b/chromium/third_party/blink/renderer/platform/graphics/graphics_context.cc
new file mode 100644
index 00000000000..e4d10c228b3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/graphics_context.cc
@@ -0,0 +1,1452 @@
+/*
+ * Copyright (C) 2003, 2004, 2005, 2006, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2013 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/graphics/graphics_context.h"
+
+#include <memory>
+
+#include "build/build_config.h"
+#include "skia/ext/platform_canvas.h"
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/geometry/float_rounded_rect.h"
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context_state_saver.h"
+#include "third_party/blink/renderer/platform/graphics/interpolation_space.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_record.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_recorder.h"
+#include "third_party/blink/renderer/platform/graphics/path.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/skia/include/core/SkAnnotation.h"
+#include "third_party/skia/include/core/SkColorFilter.h"
+#include "third_party/skia/include/core/SkData.h"
+#include "third_party/skia/include/core/SkRRect.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+#include "third_party/skia/include/effects/SkHighContrastFilter.h"
+#include "third_party/skia/include/effects/SkLumaColorFilter.h"
+#include "third_party/skia/include/effects/SkTableColorFilter.h"
+#include "third_party/skia/include/pathops/SkPathOps.h"
+#include "third_party/skia/include/utils/SkNullCanvas.h"
+
+namespace blink {
+
+GraphicsContext::GraphicsContext(PaintController& paint_controller,
+ DisabledMode disable_context_or_painting,
+ SkMetaData* meta_data)
+ : canvas_(nullptr),
+ paint_controller_(paint_controller),
+ paint_state_stack_(),
+ paint_state_index_(0),
+#if DCHECK_IS_ON()
+ layer_count_(0),
+ disable_destruction_checks_(false),
+ in_drawing_recorder_(false),
+#endif
+ disabled_state_(disable_context_or_painting),
+ device_scale_factor_(1.0f),
+ printing_(false),
+ has_meta_data_(!!meta_data) {
+ if (meta_data)
+ meta_data_ = *meta_data;
+
+ // FIXME: Do some tests to determine how many states are typically used, and
+ // allocate several here.
+ paint_state_stack_.push_back(GraphicsContextState::Create());
+ paint_state_ = paint_state_stack_.back().get();
+
+ if (ContextDisabled()) {
+ DEFINE_STATIC_LOCAL(SkCanvas*, null_sk_canvas,
+ (SkMakeNullCanvas().release()));
+ DEFINE_STATIC_LOCAL(SkiaPaintCanvas, null_canvas, (null_sk_canvas));
+ canvas_ = &null_canvas;
+ }
+}
+
+GraphicsContext::~GraphicsContext() {
+#if DCHECK_IS_ON()
+ if (!disable_destruction_checks_) {
+ DCHECK(!paint_state_index_);
+ DCHECK(!paint_state_->SaveCount());
+ DCHECK(!layer_count_);
+ DCHECK(!SaveCount());
+ }
+#endif
+}
+
+void GraphicsContext::Save() {
+ if (ContextDisabled())
+ return;
+
+ paint_state_->IncrementSaveCount();
+
+ DCHECK(canvas_);
+ canvas_->save();
+}
+
+void GraphicsContext::Restore() {
+ if (ContextDisabled())
+ return;
+
+ if (!paint_state_index_ && !paint_state_->SaveCount()) {
+ DLOG(ERROR) << "ERROR void GraphicsContext::restore() stack is empty";
+ return;
+ }
+
+ if (paint_state_->SaveCount()) {
+ paint_state_->DecrementSaveCount();
+ } else {
+ paint_state_index_--;
+ paint_state_ = paint_state_stack_[paint_state_index_].get();
+ }
+
+ DCHECK(canvas_);
+ canvas_->restore();
+}
+
+#if DCHECK_IS_ON()
+unsigned GraphicsContext::SaveCount() const {
+ // Each m_paintStateStack entry implies an additional save op
+ // (on top of its own saveCount), except for the first frame.
+ unsigned count = paint_state_index_;
+ DCHECK_GE(paint_state_stack_.size(), paint_state_index_);
+ for (unsigned i = 0; i <= paint_state_index_; ++i)
+ count += paint_state_stack_[i]->SaveCount();
+
+ return count;
+}
+#endif
+
+void GraphicsContext::SetHighContrast(const HighContrastSettings& settings) {
+ high_contrast_settings_ = settings;
+
+ SkHighContrastConfig config;
+ switch (high_contrast_settings_.mode) {
+ case HighContrastMode::kOff:
+ high_contrast_filter_.reset(nullptr);
+ return;
+ case HighContrastMode::kSimpleInvertForTesting: {
+ uint8_t identity[256], invert[256];
+ for (int i = 0; i < 256; ++i) {
+ identity[i] = i;
+ invert[i] = 255 - i;
+ }
+ high_contrast_filter_ =
+ SkTableColorFilter::MakeARGB(identity, invert, invert, invert);
+ return;
+ }
+ case HighContrastMode::kInvertBrightness:
+ config.fInvertStyle =
+ SkHighContrastConfig::InvertStyle::kInvertBrightness;
+ break;
+ case HighContrastMode::kInvertLightness:
+ config.fInvertStyle = SkHighContrastConfig::InvertStyle::kInvertLightness;
+ break;
+ }
+
+ config.fGrayscale = high_contrast_settings_.grayscale;
+ config.fContrast = high_contrast_settings_.contrast;
+ high_contrast_filter_ = SkHighContrastFilter::Make(config);
+}
+
+void GraphicsContext::SaveLayer(const SkRect* bounds, const PaintFlags* flags) {
+ if (ContextDisabled())
+ return;
+
+ DCHECK(canvas_);
+ canvas_->saveLayer(bounds, flags);
+}
+
+void GraphicsContext::RestoreLayer() {
+ if (ContextDisabled())
+ return;
+
+ DCHECK(canvas_);
+
+ canvas_->restore();
+}
+
+#if DCHECK_IS_ON()
+void GraphicsContext::SetInDrawingRecorder(bool val) {
+ // Nested drawing recorers are not allowed.
+ DCHECK(!val || !in_drawing_recorder_);
+ in_drawing_recorder_ = val;
+}
+#endif
+
+void GraphicsContext::SetShadow(
+ const FloatSize& offset,
+ float blur,
+ const Color& color,
+ DrawLooperBuilder::ShadowTransformMode shadow_transform_mode,
+ DrawLooperBuilder::ShadowAlphaMode shadow_alpha_mode,
+ ShadowMode shadow_mode) {
+ if (ContextDisabled())
+ return;
+
+ DrawLooperBuilder draw_looper_builder;
+ if (!color.Alpha()) {
+ // When shadow-only but there is no shadow, we use an empty draw looper
+ // to disable rendering of the source primitive. When not shadow-only, we
+ // clear the looper.
+ SetDrawLooper(shadow_mode != kDrawShadowOnly
+ ? nullptr
+ : draw_looper_builder.DetachDrawLooper());
+ return;
+ }
+
+ draw_looper_builder.AddShadow(offset, blur, color, shadow_transform_mode,
+ shadow_alpha_mode);
+ if (shadow_mode == kDrawShadowAndForeground) {
+ draw_looper_builder.AddUnmodifiedContent();
+ }
+ SetDrawLooper(draw_looper_builder.DetachDrawLooper());
+}
+
+void GraphicsContext::SetDrawLooper(sk_sp<SkDrawLooper> draw_looper) {
+ if (ContextDisabled())
+ return;
+
+ MutableState()->SetDrawLooper(std::move(draw_looper));
+}
+
+SkColorFilter* GraphicsContext::GetColorFilter() const {
+ return ImmutableState()->GetColorFilter();
+}
+
+void GraphicsContext::SetColorFilter(ColorFilter color_filter) {
+ GraphicsContextState* state_to_set = MutableState();
+
+ // We only support one active color filter at the moment. If (when) this
+ // becomes a problem, we should switch to using color filter chains (Skia work
+ // in progress).
+ DCHECK(!state_to_set->GetColorFilter());
+ state_to_set->SetColorFilter(
+ WebCoreColorFilterToSkiaColorFilter(color_filter));
+}
+
+void GraphicsContext::Concat(const SkMatrix& matrix) {
+ if (ContextDisabled())
+ return;
+
+ DCHECK(canvas_);
+
+ canvas_->concat(matrix);
+}
+
+void GraphicsContext::BeginLayer(float opacity,
+ SkBlendMode xfermode,
+ const FloatRect* bounds,
+ ColorFilter color_filter,
+ sk_sp<PaintFilter> image_filter) {
+ if (ContextDisabled())
+ return;
+
+ PaintFlags layer_flags;
+ layer_flags.setAlpha(static_cast<unsigned char>(opacity * 255));
+ layer_flags.setBlendMode(xfermode);
+ layer_flags.setColorFilter(WebCoreColorFilterToSkiaColorFilter(color_filter));
+ layer_flags.setImageFilter(std::move(image_filter));
+
+ if (bounds) {
+ SkRect sk_bounds = *bounds;
+ SaveLayer(&sk_bounds, &layer_flags);
+ } else {
+ SaveLayer(nullptr, &layer_flags);
+ }
+
+#if DCHECK_IS_ON()
+ ++layer_count_;
+#endif
+}
+
+void GraphicsContext::EndLayer() {
+ if (ContextDisabled())
+ return;
+
+ RestoreLayer();
+
+#if DCHECK_IS_ON()
+ DCHECK_GT(layer_count_--, 0);
+#endif
+}
+
+void GraphicsContext::BeginRecording(const FloatRect& bounds) {
+ if (ContextDisabled())
+ return;
+
+ DCHECK(!canvas_);
+ canvas_ = paint_recorder_.beginRecording(bounds);
+ if (has_meta_data_)
+ canvas_->getMetaData() = meta_data_;
+}
+
+namespace {
+
+sk_sp<PaintRecord> CreateEmptyPaintRecord() {
+ PaintRecorder recorder;
+ recorder.beginRecording(SkRect::MakeEmpty());
+ return recorder.finishRecordingAsPicture();
+}
+
+} // anonymous namespace
+
+sk_sp<PaintRecord> GraphicsContext::EndRecording() {
+ if (ContextDisabled()) {
+ // Clients expect endRecording() to always return a non-null paint record.
+ // Cache an empty one to minimize overhead when disabled.
+ DEFINE_STATIC_LOCAL(sk_sp<PaintRecord>, empty_paint_record,
+ (CreateEmptyPaintRecord()));
+ return empty_paint_record;
+ }
+
+ sk_sp<PaintRecord> record = paint_recorder_.finishRecordingAsPicture();
+ canvas_ = nullptr;
+ DCHECK(record);
+ return record;
+}
+
+void GraphicsContext::DrawRecord(sk_sp<const PaintRecord> record) {
+ if (ContextDisabled() || !record || !record->size())
+ return;
+
+ DCHECK(canvas_);
+ canvas_->drawPicture(std::move(record));
+}
+
+void GraphicsContext::CompositeRecord(sk_sp<PaintRecord> record,
+ const FloatRect& dest,
+ const FloatRect& src,
+ SkBlendMode op) {
+ if (ContextDisabled() || !record)
+ return;
+ DCHECK(canvas_);
+
+ PaintFlags flags;
+ flags.setBlendMode(op);
+ flags.setFilterQuality(
+ static_cast<SkFilterQuality>(ImageInterpolationQuality()));
+ canvas_->save();
+ canvas_->concat(
+ SkMatrix::MakeRectToRect(src, dest, SkMatrix::kFill_ScaleToFit));
+ canvas_->drawImage(PaintImageBuilder::WithDefault()
+ .set_paint_record(record, RoundedIntRect(src),
+ PaintImage::GetNextContentId())
+ .set_id(PaintImage::GetNextId())
+ .TakePaintImage(),
+ 0, 0, &flags);
+ canvas_->restore();
+}
+
+namespace {
+
+int AdjustedFocusRingOffset(int offset) {
+#if defined(OS_MACOSX)
+ return offset + 2;
+#else
+ return 0;
+#endif
+}
+
+} // anonymous ns
+
+int GraphicsContext::FocusRingOutsetExtent(int offset, int width) {
+ // Unlike normal outlines (whole width is outside of the offset), focus
+ // rings are drawn with the center of the path aligned with the offset, so
+ // only half of the width is outside of the offset.
+ return AdjustedFocusRingOffset(offset) + (width + 1) / 2;
+}
+
+void GraphicsContext::DrawFocusRingPath(const SkPath& path,
+ const Color& color,
+ float width) {
+ DrawPlatformFocusRing(path, canvas_, ApplyHighContrastFilter(color).Rgb(),
+ width);
+}
+
+void GraphicsContext::DrawFocusRingRect(const SkRect& rect,
+ const Color& color,
+ float width) {
+ DrawPlatformFocusRing(rect, canvas_, ApplyHighContrastFilter(color).Rgb(),
+ width);
+}
+
+void GraphicsContext::DrawFocusRing(const Path& focus_ring_path,
+ float width,
+ int offset,
+ const Color& color) {
+ // FIXME: Implement support for offset.
+ if (ContextDisabled())
+ return;
+
+ DrawFocusRingPath(focus_ring_path.GetSkPath(), color, width);
+}
+
+void GraphicsContext::DrawFocusRing(const Vector<IntRect>& rects,
+ float width,
+ int offset,
+ const Color& color) {
+ if (ContextDisabled())
+ return;
+
+ unsigned rect_count = rects.size();
+ if (!rect_count)
+ return;
+
+ SkRegion focus_ring_region;
+ offset = AdjustedFocusRingOffset(offset);
+ for (unsigned i = 0; i < rect_count; i++) {
+ SkIRect r = rects[i];
+ if (r.isEmpty())
+ continue;
+ r.outset(offset, offset);
+ focus_ring_region.op(r, SkRegion::kUnion_Op);
+ }
+
+ if (focus_ring_region.isEmpty())
+ return;
+
+ if (focus_ring_region.isRect()) {
+ DrawFocusRingRect(SkRect::Make(focus_ring_region.getBounds()), color,
+ width);
+ } else {
+ SkPath path;
+ if (focus_ring_region.getBoundaryPath(&path))
+ DrawFocusRingPath(path, color, width);
+ }
+}
+
+static inline FloatRect AreaCastingShadowInHole(
+ const FloatRect& hole_rect,
+ float shadow_blur,
+ float shadow_spread,
+ const FloatSize& shadow_offset) {
+ FloatRect bounds(hole_rect);
+
+ bounds.Inflate(shadow_blur);
+
+ if (shadow_spread < 0)
+ bounds.Inflate(-shadow_spread);
+
+ FloatRect offset_bounds = bounds;
+ offset_bounds.Move(-shadow_offset);
+ return UnionRect(bounds, offset_bounds);
+}
+
+void GraphicsContext::DrawInnerShadow(const FloatRoundedRect& rect,
+ const Color& orig_shadow_color,
+ const FloatSize& shadow_offset,
+ float shadow_blur,
+ float shadow_spread,
+ Edges clipped_edges) {
+ if (ContextDisabled())
+ return;
+
+ Color shadow_color = ApplyHighContrastFilter(orig_shadow_color);
+
+ FloatRect hole_rect(rect.Rect());
+ hole_rect.Inflate(-shadow_spread);
+
+ if (hole_rect.IsEmpty()) {
+ FillRoundedRect(rect, shadow_color);
+ return;
+ }
+
+ if (clipped_edges & kLeftEdge) {
+ hole_rect.Move(-std::max(shadow_offset.Width(), 0.0f) - shadow_blur, 0);
+ hole_rect.SetWidth(hole_rect.Width() +
+ std::max(shadow_offset.Width(), 0.0f) + shadow_blur);
+ }
+ if (clipped_edges & kTopEdge) {
+ hole_rect.Move(0, -std::max(shadow_offset.Height(), 0.0f) - shadow_blur);
+ hole_rect.SetHeight(hole_rect.Height() +
+ std::max(shadow_offset.Height(), 0.0f) + shadow_blur);
+ }
+ if (clipped_edges & kRightEdge)
+ hole_rect.SetWidth(hole_rect.Width() -
+ std::min(shadow_offset.Width(), 0.0f) + shadow_blur);
+ if (clipped_edges & kBottomEdge)
+ hole_rect.SetHeight(hole_rect.Height() -
+ std::min(shadow_offset.Height(), 0.0f) + shadow_blur);
+
+ Color fill_color(shadow_color.Red(), shadow_color.Green(),
+ shadow_color.Blue(), 255);
+
+ FloatRect outer_rect = AreaCastingShadowInHole(rect.Rect(), shadow_blur,
+ shadow_spread, shadow_offset);
+ FloatRoundedRect rounded_hole(hole_rect, rect.GetRadii());
+
+ GraphicsContextStateSaver state_saver(*this);
+ if (rect.IsRounded()) {
+ ClipRoundedRect(rect);
+ if (shadow_spread < 0)
+ rounded_hole.ExpandRadii(-shadow_spread);
+ else
+ rounded_hole.ShrinkRadii(shadow_spread);
+ } else {
+ Clip(rect.Rect());
+ }
+
+ DrawLooperBuilder draw_looper_builder;
+ draw_looper_builder.AddShadow(FloatSize(shadow_offset), shadow_blur,
+ shadow_color,
+ DrawLooperBuilder::kShadowRespectsTransforms,
+ DrawLooperBuilder::kShadowIgnoresAlpha);
+ SetDrawLooper(draw_looper_builder.DetachDrawLooper());
+ FillRectWithRoundedHole(outer_rect, rounded_hole, fill_color);
+}
+
+static void EnforceDotsAtEndpoints(GraphicsContext& context,
+ FloatPoint& p1,
+ FloatPoint& p2,
+ const int path_length,
+ const int width,
+ const PaintFlags& flags,
+ const bool is_vertical_line) {
+ // For narrow lines, we always want integral dot and dash sizes, and start
+ // and end points, to prevent anti-aliasing from erasing the dot effect.
+ // For 1-pixel wide lines, we must make one end a dash. Otherwise we have
+ // a little more scope to distribute the error. But we never want to reduce
+ // the size of the end dots because doing so makes corners of all-dotted
+ // paths look odd.
+ //
+ // There is no way to give custom start and end dash sizes or gaps to Skia,
+ // so if we need non-uniform gaps we need to draw the start, and maybe the
+ // end dot ourselves, and move the line start (and end) to the start/end of
+ // the second dot.
+ DCHECK_LE(width, 3); // Width is max 3 according to StrokeIsDashed
+ int mod_4 = path_length % 4;
+ int mod_6 = path_length % 6;
+ // New start dot to be explicitly drawn, if needed, and the amount to grow the
+ // start dot and the offset for first gap.
+ bool use_start_dot = false;
+ int start_dot_growth = 0;
+ int start_line_offset = 0;
+ // New end dot to be explicitly drawn, if needed, and the amount to grow the
+ // second dot.
+ bool use_end_dot = false;
+ int end_dot_growth = 0;
+ if ((width == 1 && path_length % 2 == 0) || (width == 3 && mod_6 == 0)) {
+ // Cases where we add one pixel to the first dot.
+ use_start_dot = true;
+ start_dot_growth = 1;
+ start_line_offset = 1;
+ }
+ if ((width == 2 && (mod_4 == 0 || mod_4 == 1)) ||
+ (width == 3 && (mod_6 == 1 || mod_6 == 2))) {
+ // Cases where we drop 1 pixel from the start gap
+ use_start_dot = true;
+ start_line_offset = -1;
+ }
+ if ((width == 2 && mod_4 == 0) || (width == 3 && mod_6 == 1)) {
+ // Cases where we drop 1 pixel from the end gap
+ use_end_dot = true;
+ }
+ if ((width == 2 && mod_4 == 3) ||
+ (width == 3 && (mod_6 == 4 || mod_6 == 5))) {
+ // Cases where we add 1 pixel to the start gap
+ use_start_dot = true;
+ start_line_offset = 1;
+ }
+ if (width == 3 && mod_6 == 5) {
+ // Case where we add 1 pixel to the end gap and leave the end
+ // dot the same size.
+ use_end_dot = true;
+ } else if (width == 3 && mod_6 == 0) {
+ // Case where we add one pixel gap and one pixel to the dot at the end
+ use_end_dot = true;
+ end_dot_growth = 1; // Moves the larger end pt for this case
+ }
+
+ if (use_start_dot || use_end_dot) {
+ PaintFlags fill_flags;
+ fill_flags.setColor(flags.getColor());
+ if (use_start_dot) {
+ SkRect start_dot;
+ if (is_vertical_line) {
+ start_dot.set(p1.X() - width / 2, p1.Y(), p1.X() + width - width / 2,
+ p1.Y() + width + start_dot_growth);
+ p1.SetY(p1.Y() + (2 * width + start_line_offset));
+ } else {
+ start_dot.set(p1.X(), p1.Y() - width / 2,
+ p1.X() + width + start_dot_growth,
+ p1.Y() + width - width / 2);
+ p1.SetX(p1.X() + (2 * width + start_line_offset));
+ }
+ context.DrawRect(start_dot, fill_flags);
+ }
+ if (use_end_dot) {
+ SkRect end_dot;
+ if (is_vertical_line) {
+ end_dot.set(p2.X() - width / 2, p2.Y() - width - end_dot_growth,
+ p2.X() + width - width / 2, p2.Y());
+ // Be sure to stop drawing before we get to the last dot
+ p2.SetY(p2.Y() - (width + end_dot_growth + 1));
+ } else {
+ end_dot.set(p2.X() - width - end_dot_growth, p2.Y() - width / 2, p2.X(),
+ p2.Y() + width - width / 2);
+ // Be sure to stop drawing before we get to the last dot
+ p2.SetX(p2.X() - (width + end_dot_growth + 1));
+ }
+ context.DrawRect(end_dot, fill_flags);
+ }
+ }
+}
+
+void GraphicsContext::DrawLine(const IntPoint& point1, const IntPoint& point2) {
+ if (ContextDisabled())
+ return;
+ DCHECK(canvas_);
+
+ StrokeStyle pen_style = GetStrokeStyle();
+ if (pen_style == kNoStroke)
+ return;
+
+ FloatPoint p1 = point1;
+ FloatPoint p2 = point2;
+ bool is_vertical_line = (p1.X() == p2.X());
+ int width = roundf(StrokeThickness());
+
+ // We know these are vertical or horizontal lines, so the length will just
+ // be the sum of the displacement component vectors give or take 1 -
+ // probably worth the speed up of no square root, which also won't be exact.
+ FloatSize disp = p2 - p1;
+ int length = SkScalarRoundToInt(disp.Width() + disp.Height());
+ PaintFlags flags(ImmutableState()->StrokeFlags(length));
+
+ if (pen_style == kDottedStroke) {
+ if (StrokeData::StrokeIsDashed(width, pen_style)) {
+ // We draw thin dotted lines as dashes and gaps that are always
+ // exactly the size of the width. When the length of the line is
+ // an odd multiple of the width, things work well because we get
+ // dots at each end of the line, but if the length is anything else,
+ // we get gaps or partial dots at the end of the line. Fix that by
+ // explicitly enforcing full dots at the ends of lines.
+ EnforceDotsAtEndpoints(*this, p1, p2, length, width, flags,
+ is_vertical_line);
+ } else {
+ // We draw thick dotted lines with 0 length dash strokes and round
+ // endcaps, producing circles. The endcaps extend beyond the line's
+ // endpoints, so move the start and end in.
+ if (is_vertical_line) {
+ p1.SetY(p1.Y() + width / 2.f);
+ p2.SetY(p2.Y() - width / 2.f);
+ } else {
+ p1.SetX(p1.X() + width / 2.f);
+ p2.SetX(p2.X() - width / 2.f);
+ }
+ }
+ }
+
+ AdjustLineToPixelBoundaries(p1, p2, width);
+ canvas_->drawLine(p1.X(), p1.Y(), p2.X(), p2.Y(),
+ ApplyHighContrastFilter(&flags));
+}
+
+void GraphicsContext::DrawLineForText(const FloatPoint& pt, float width) {
+ if (ContextDisabled())
+ return;
+
+ if (width <= 0)
+ return;
+
+ PaintFlags flags;
+ switch (GetStrokeStyle()) {
+ case kNoStroke:
+ case kSolidStroke:
+ case kDoubleStroke: {
+ int thickness = SkMax32(static_cast<int>(StrokeThickness()), 1);
+ SkRect r;
+ r.fLeft = WebCoreFloatToSkScalar(pt.X());
+ // Avoid anti-aliasing lines. Currently, these are always horizontal.
+ // Round to nearest pixel to match text and other content.
+ r.fTop = WebCoreFloatToSkScalar(floorf(pt.Y() + 0.5f));
+ r.fRight = r.fLeft + WebCoreFloatToSkScalar(width);
+ r.fBottom = r.fTop + SkIntToScalar(thickness);
+ flags = ImmutableState()->FillFlags();
+ // Text lines are drawn using the stroke color.
+ flags.setColor(StrokeColor().Rgb());
+ DrawRect(r, flags);
+ return;
+ }
+ case kDottedStroke:
+ case kDashedStroke: {
+ int y = floorf(pt.Y() + std::max<float>(StrokeThickness() / 2.0f, 0.5f));
+ DrawLine(IntPoint(pt.X(), y), IntPoint(pt.X() + width, y));
+ return;
+ }
+ case kWavyStroke:
+ default:
+ break;
+ }
+
+ NOTREACHED();
+}
+
+// Draws a filled rectangle with a stroked border.
+void GraphicsContext::DrawRect(const IntRect& rect) {
+ if (ContextDisabled())
+ return;
+
+ DCHECK(!rect.IsEmpty());
+ if (rect.IsEmpty())
+ return;
+
+ SkRect sk_rect = rect;
+ if (ImmutableState()->FillColor().Alpha())
+ DrawRect(sk_rect, ImmutableState()->FillFlags());
+
+ if (ImmutableState()->GetStrokeData().Style() != kNoStroke &&
+ ImmutableState()->StrokeColor().Alpha()) {
+ // Stroke a width: 1 inset border
+ PaintFlags flags(ImmutableState()->FillFlags());
+ flags.setColor(StrokeColor().Rgb());
+ flags.setStyle(PaintFlags::kStroke_Style);
+ flags.setStrokeWidth(1);
+
+ sk_rect.inset(0.5f, 0.5f);
+ DrawRect(sk_rect, flags);
+ }
+}
+
+template <typename TextPaintInfo>
+void GraphicsContext::DrawTextInternal(const Font& font,
+ const TextPaintInfo& text_info,
+ const FloatPoint& point,
+ const PaintFlags& flags) {
+ if (ContextDisabled())
+ return;
+
+ font.DrawText(canvas_, text_info, point, device_scale_factor_,
+ ApplyHighContrastFilter(&flags));
+}
+
+void GraphicsContext::DrawText(const Font& font,
+ const TextRunPaintInfo& text_info,
+ const FloatPoint& point,
+ const PaintFlags& flags) {
+ DrawTextInternal(font, text_info, point, flags);
+}
+
+void GraphicsContext::DrawText(const Font& font,
+ const NGTextFragmentPaintInfo& text_info,
+ const FloatPoint& point,
+ const PaintFlags& flags) {
+ DrawTextInternal(font, text_info, point, flags);
+}
+
+template <typename DrawTextFunc>
+void GraphicsContext::DrawTextPasses(const DrawTextFunc& draw_text) {
+ TextDrawingModeFlags mode_flags = TextDrawingMode();
+
+ if (mode_flags & kTextModeFill) {
+ draw_text(ImmutableState()->FillFlags());
+ }
+
+ if ((mode_flags & kTextModeStroke) && GetStrokeStyle() != kNoStroke &&
+ StrokeThickness() > 0) {
+ PaintFlags stroke_flags(ImmutableState()->StrokeFlags());
+ if (mode_flags & kTextModeFill) {
+ // shadow was already applied during fill pass
+ stroke_flags.setLooper(nullptr);
+ }
+ draw_text(stroke_flags);
+ }
+}
+
+template <typename TextPaintInfo>
+void GraphicsContext::DrawTextInternal(const Font& font,
+ const TextPaintInfo& text_info,
+ const FloatPoint& point) {
+ if (ContextDisabled())
+ return;
+
+ DrawTextPasses([&font, &text_info, &point, this](const PaintFlags& flags) {
+ font.DrawText(canvas_, text_info, point, device_scale_factor_,
+ ApplyHighContrastFilter(&flags));
+ });
+}
+
+void GraphicsContext::DrawText(const Font& font,
+ const TextRunPaintInfo& text_info,
+ const FloatPoint& point) {
+ DrawTextInternal(font, text_info, point);
+}
+
+void GraphicsContext::DrawText(const Font& font,
+ const NGTextFragmentPaintInfo& text_info,
+ const FloatPoint& point) {
+ DrawTextInternal(font, text_info, point);
+}
+
+template <typename TextPaintInfo>
+void GraphicsContext::DrawEmphasisMarksInternal(const Font& font,
+ const TextPaintInfo& text_info,
+ const AtomicString& mark,
+ const FloatPoint& point) {
+ if (ContextDisabled())
+ return;
+
+ DrawTextPasses(
+ [&font, &text_info, &mark, &point, this](const PaintFlags& flags) {
+ font.DrawEmphasisMarks(canvas_, text_info, mark, point,
+ device_scale_factor_,
+ ApplyHighContrastFilter(&flags));
+ });
+}
+
+void GraphicsContext::DrawEmphasisMarks(const Font& font,
+ const TextRunPaintInfo& text_info,
+ const AtomicString& mark,
+ const FloatPoint& point) {
+ DrawEmphasisMarksInternal(font, text_info, mark, point);
+}
+
+void GraphicsContext::DrawEmphasisMarks(
+ const Font& font,
+ const NGTextFragmentPaintInfo& text_info,
+ const AtomicString& mark,
+ const FloatPoint& point) {
+ DrawEmphasisMarksInternal(font, text_info, mark, point);
+}
+
+void GraphicsContext::DrawBidiText(
+ const Font& font,
+ const TextRunPaintInfo& run_info,
+ const FloatPoint& point,
+ Font::CustomFontNotReadyAction custom_font_not_ready_action) {
+ if (ContextDisabled())
+ return;
+
+ DrawTextPasses([&font, &run_info, &point, custom_font_not_ready_action,
+ this](const PaintFlags& flags) {
+ if (font.DrawBidiText(canvas_, run_info, point,
+ custom_font_not_ready_action, device_scale_factor_,
+ ApplyHighContrastFilter(&flags)))
+ paint_controller_.SetTextPainted();
+ });
+}
+
+void GraphicsContext::DrawHighlightForText(const Font& font,
+ const TextRun& run,
+ const FloatPoint& point,
+ int h,
+ const Color& background_color,
+ int from,
+ int to) {
+ if (ContextDisabled())
+ return;
+
+ FillRect(font.SelectionRectForText(run, point, h, from, to),
+ background_color);
+}
+
+void GraphicsContext::DrawImage(
+ Image* image,
+ Image::ImageDecodingMode decode_mode,
+ const FloatRect& dest,
+ const FloatRect* src_ptr,
+ SkBlendMode op,
+ RespectImageOrientationEnum should_respect_image_orientation) {
+ if (ContextDisabled() || !image)
+ return;
+
+ const FloatRect src = src_ptr ? *src_ptr : image->Rect();
+
+ PaintFlags image_flags = ImmutableState()->FillFlags();
+ image_flags.setBlendMode(op);
+ image_flags.setColor(SK_ColorBLACK);
+ image_flags.setFilterQuality(ComputeFilterQuality(image, dest, src));
+ image_flags.setAntiAlias(ShouldAntialias());
+ if (ShouldApplyHighContrastFilterToImage(*image))
+ image_flags.setColorFilter(high_contrast_filter_);
+ image->Draw(canvas_, image_flags, dest, src, should_respect_image_orientation,
+ Image::kClampImageToSourceRect, decode_mode);
+ paint_controller_.SetImagePainted();
+}
+
+void GraphicsContext::DrawImageRRect(
+ Image* image,
+ Image::ImageDecodingMode decode_mode,
+ const FloatRoundedRect& dest,
+ const FloatRect& src_rect,
+ SkBlendMode op,
+ RespectImageOrientationEnum respect_orientation) {
+ if (ContextDisabled() || !image)
+ return;
+
+ if (!dest.IsRounded()) {
+ DrawImage(image, decode_mode, dest.Rect(), &src_rect, op,
+ respect_orientation);
+ return;
+ }
+
+ DCHECK(dest.IsRenderable());
+
+ const FloatRect visible_src = Intersection(src_rect, image->Rect());
+ if (dest.IsEmpty() || visible_src.IsEmpty())
+ return;
+
+ PaintFlags image_flags = ImmutableState()->FillFlags();
+ image_flags.setBlendMode(op);
+ image_flags.setColor(SK_ColorBLACK);
+ image_flags.setFilterQuality(
+ ComputeFilterQuality(image, dest.Rect(), src_rect));
+ image_flags.setAntiAlias(ShouldAntialias());
+
+ bool use_shader = (visible_src == src_rect) &&
+ (respect_orientation == kDoNotRespectImageOrientation);
+ if (use_shader) {
+ const SkMatrix local_matrix = SkMatrix::MakeRectToRect(
+ visible_src, dest.Rect(), SkMatrix::kFill_ScaleToFit);
+ use_shader = image->ApplyShader(image_flags, local_matrix);
+ }
+
+ if (use_shader) {
+ // Shader-based fast path.
+ canvas_->drawRRect(dest, image_flags);
+ } else {
+ // Clip-based fallback.
+ PaintCanvasAutoRestore auto_restore(canvas_, true);
+ canvas_->clipRRect(dest, image_flags.isAntiAlias());
+ image->Draw(canvas_, image_flags, dest.Rect(), src_rect,
+ respect_orientation, Image::kClampImageToSourceRect,
+ decode_mode);
+ }
+
+ paint_controller_.SetImagePainted();
+}
+
+SkFilterQuality GraphicsContext::ComputeFilterQuality(
+ Image* image,
+ const FloatRect& dest,
+ const FloatRect& src) const {
+ InterpolationQuality resampling;
+ if (Printing()) {
+ resampling = kInterpolationNone;
+ } else if (image->CurrentFrameIsLazyDecoded()) {
+ resampling = kInterpolationDefault;
+ } else {
+ resampling = ComputeInterpolationQuality(
+ SkScalarToFloat(src.Width()), SkScalarToFloat(src.Height()),
+ SkScalarToFloat(dest.Width()), SkScalarToFloat(dest.Height()),
+ image->CurrentFrameIsComplete());
+
+ if (resampling == kInterpolationNone) {
+ // FIXME: This is to not break tests (it results in the filter bitmap flag
+ // being set to true). We need to decide if we respect InterpolationNone
+ // being returned from computeInterpolationQuality.
+ resampling = kInterpolationLow;
+ }
+ }
+ return static_cast<SkFilterQuality>(
+ std::min(resampling, ImageInterpolationQuality()));
+}
+
+void GraphicsContext::DrawTiledImage(Image* image,
+ const FloatRect& dest_rect,
+ const FloatPoint& src_point,
+ const FloatSize& tile_size,
+ SkBlendMode op,
+ const FloatSize& repeat_spacing) {
+ if (ContextDisabled() || !image)
+ return;
+ image->DrawTiledBackground(*this, dest_rect, src_point, tile_size, op,
+ repeat_spacing);
+ paint_controller_.SetImagePainted();
+}
+
+void GraphicsContext::DrawTiledImage(Image* image,
+ const FloatRect& dest,
+ const FloatRect& src_rect,
+ const FloatSize& tile_scale_factor,
+ Image::TileRule h_rule,
+ Image::TileRule v_rule,
+ SkBlendMode op) {
+ if (ContextDisabled() || !image)
+ return;
+
+ if (h_rule == Image::kStretchTile && v_rule == Image::kStretchTile) {
+ // Just do a scale.
+ // Since there is no way for the developer to specify decode behavior, use
+ // kSync by default.
+ DrawImage(image, Image::kSyncDecode, dest, &src_rect, op);
+ return;
+ }
+
+ image->DrawTiledBorder(*this, dest, src_rect, tile_scale_factor, h_rule,
+ v_rule, op);
+ paint_controller_.SetImagePainted();
+}
+
+void GraphicsContext::DrawOval(const SkRect& oval, const PaintFlags& flags) {
+ if (ContextDisabled())
+ return;
+ DCHECK(canvas_);
+
+ canvas_->drawOval(oval, ApplyHighContrastFilter(&flags));
+}
+
+void GraphicsContext::DrawPath(const SkPath& path, const PaintFlags& flags) {
+ if (ContextDisabled())
+ return;
+ DCHECK(canvas_);
+
+ canvas_->drawPath(path, ApplyHighContrastFilter(&flags));
+}
+
+void GraphicsContext::DrawRect(const SkRect& rect, const PaintFlags& flags) {
+ if (ContextDisabled())
+ return;
+ DCHECK(canvas_);
+
+ canvas_->drawRect(rect, ApplyHighContrastFilter(&flags));
+}
+
+void GraphicsContext::DrawRRect(const SkRRect& rrect, const PaintFlags& flags) {
+ if (ContextDisabled())
+ return;
+ DCHECK(canvas_);
+
+ canvas_->drawRRect(rrect, ApplyHighContrastFilter(&flags));
+}
+
+void GraphicsContext::FillPath(const Path& path_to_fill) {
+ if (ContextDisabled() || path_to_fill.IsEmpty())
+ return;
+
+ DrawPath(path_to_fill.GetSkPath(), ImmutableState()->FillFlags());
+}
+
+void GraphicsContext::FillRect(const FloatRect& rect) {
+ if (ContextDisabled())
+ return;
+
+ DrawRect(rect, ImmutableState()->FillFlags());
+}
+
+void GraphicsContext::FillRect(const FloatRect& rect,
+ const Color& color,
+ SkBlendMode xfer_mode) {
+ if (ContextDisabled())
+ return;
+
+ PaintFlags flags = ImmutableState()->FillFlags();
+ flags.setColor(color.Rgb());
+ flags.setBlendMode(xfer_mode);
+
+ DrawRect(rect, flags);
+}
+
+void GraphicsContext::FillRoundedRect(const FloatRoundedRect& rrect,
+ const Color& color) {
+ if (ContextDisabled())
+ return;
+
+ if (!rrect.IsRounded() || !rrect.IsRenderable()) {
+ FillRect(rrect.Rect(), color);
+ return;
+ }
+
+ if (color == FillColor()) {
+ DrawRRect(rrect, ImmutableState()->FillFlags());
+ return;
+ }
+
+ PaintFlags flags = ImmutableState()->FillFlags();
+ flags.setColor(color.Rgb());
+
+ DrawRRect(rrect, flags);
+}
+
+namespace {
+
+bool IsSimpleDRRect(const FloatRoundedRect& outer,
+ const FloatRoundedRect& inner) {
+ // A DRRect is "simple" (i.e. can be drawn as a rrect stroke) if
+ // 1) all sides have the same width
+ const FloatSize stroke_size =
+ inner.Rect().MinXMinYCorner() - outer.Rect().MinXMinYCorner();
+ if (!WebCoreFloatNearlyEqual(stroke_size.AspectRatio(), 1) ||
+ !WebCoreFloatNearlyEqual(stroke_size.Width(),
+ outer.Rect().MaxX() - inner.Rect().MaxX()) ||
+ !WebCoreFloatNearlyEqual(stroke_size.Height(),
+ outer.Rect().MaxY() - inner.Rect().MaxY()))
+ return false;
+
+ // and
+ // 2) the inner radii are not constrained
+ const FloatRoundedRect::Radii& o_radii = outer.GetRadii();
+ const FloatRoundedRect::Radii& i_radii = inner.GetRadii();
+ if (!WebCoreFloatNearlyEqual(o_radii.TopLeft().Width() - stroke_size.Width(),
+ i_radii.TopLeft().Width()) ||
+ !WebCoreFloatNearlyEqual(
+ o_radii.TopLeft().Height() - stroke_size.Height(),
+ i_radii.TopLeft().Height()) ||
+ !WebCoreFloatNearlyEqual(o_radii.TopRight().Width() - stroke_size.Width(),
+ i_radii.TopRight().Width()) ||
+ !WebCoreFloatNearlyEqual(
+ o_radii.TopRight().Height() - stroke_size.Height(),
+ i_radii.TopRight().Height()) ||
+ !WebCoreFloatNearlyEqual(
+ o_radii.BottomRight().Width() - stroke_size.Width(),
+ i_radii.BottomRight().Width()) ||
+ !WebCoreFloatNearlyEqual(
+ o_radii.BottomRight().Height() - stroke_size.Height(),
+ i_radii.BottomRight().Height()) ||
+ !WebCoreFloatNearlyEqual(
+ o_radii.BottomLeft().Width() - stroke_size.Width(),
+ i_radii.BottomLeft().Width()) ||
+ !WebCoreFloatNearlyEqual(
+ o_radii.BottomLeft().Height() - stroke_size.Height(),
+ i_radii.BottomLeft().Height()))
+ return false;
+
+ // We also ignore DRRects with a very thick relative stroke (shapes which are
+ // mostly filled by the stroke): Skia's stroke outline can diverge
+ // significantly from the outer/inner contours in some edge cases, so we fall
+ // back to drawDRRect() instead.
+ const float kMaxStrokeToSizeRatio = 0.75f;
+ if (2 * stroke_size.Width() / outer.Rect().Width() > kMaxStrokeToSizeRatio ||
+ 2 * stroke_size.Height() / outer.Rect().Height() > kMaxStrokeToSizeRatio)
+ return false;
+
+ return true;
+}
+
+} // anonymous namespace
+
+void GraphicsContext::FillDRRect(const FloatRoundedRect& outer,
+ const FloatRoundedRect& inner,
+ const Color& color) {
+ if (ContextDisabled())
+ return;
+ DCHECK(canvas_);
+
+ if (!IsSimpleDRRect(outer, inner)) {
+ if (color == FillColor()) {
+ canvas_->drawDRRect(outer, inner, ImmutableState()->FillFlags());
+ } else {
+ PaintFlags flags(ImmutableState()->FillFlags());
+ flags.setColor(ApplyHighContrastFilter(color).Rgb());
+ canvas_->drawDRRect(outer, inner, flags);
+ }
+
+ return;
+ }
+
+ // We can draw this as a stroked rrect.
+ float stroke_width = inner.Rect().X() - outer.Rect().X();
+ SkRRect stroke_r_rect = outer;
+ stroke_r_rect.inset(stroke_width / 2, stroke_width / 2);
+
+ PaintFlags stroke_flags(ImmutableState()->FillFlags());
+ stroke_flags.setColor(ApplyHighContrastFilter(color).Rgb());
+ stroke_flags.setStyle(PaintFlags::kStroke_Style);
+ stroke_flags.setStrokeWidth(stroke_width);
+
+ canvas_->drawRRect(stroke_r_rect, stroke_flags);
+}
+
+void GraphicsContext::FillEllipse(const FloatRect& ellipse) {
+ if (ContextDisabled())
+ return;
+
+ DrawOval(ellipse, ImmutableState()->FillFlags());
+}
+
+void GraphicsContext::StrokePath(const Path& path_to_stroke,
+ const int length,
+ const int dash_thickness) {
+ if (ContextDisabled() || path_to_stroke.IsEmpty())
+ return;
+
+ DrawPath(path_to_stroke.GetSkPath(),
+ ImmutableState()->StrokeFlags(length, dash_thickness));
+}
+
+void GraphicsContext::StrokeRect(const FloatRect& rect, float line_width) {
+ if (ContextDisabled())
+ return;
+
+ PaintFlags flags(ImmutableState()->StrokeFlags());
+ flags.setStrokeWidth(WebCoreFloatToSkScalar(line_width));
+ // Reset the dash effect to account for the width
+ ImmutableState()->GetStrokeData().SetupPaintDashPathEffect(&flags);
+ // strokerect has special rules for CSS when the rect is degenerate:
+ // if width==0 && height==0, do nothing
+ // if width==0 || height==0, then just draw line for the other dimension
+ SkRect r(rect);
+ bool valid_w = r.width() > 0;
+ bool valid_h = r.height() > 0;
+ if (valid_w && valid_h) {
+ DrawRect(r, flags);
+ } else if (valid_w || valid_h) {
+ // we are expected to respect the lineJoin, so we can't just call
+ // drawLine -- we have to create a path that doubles back on itself.
+ SkPath path;
+ path.moveTo(r.fLeft, r.fTop);
+ path.lineTo(r.fRight, r.fBottom);
+ path.close();
+ DrawPath(path, flags);
+ }
+}
+
+void GraphicsContext::StrokeEllipse(const FloatRect& ellipse) {
+ if (ContextDisabled())
+ return;
+
+ DrawOval(ellipse, ImmutableState()->StrokeFlags());
+}
+
+void GraphicsContext::ClipRoundedRect(const FloatRoundedRect& rrect,
+ SkClipOp clip_op,
+ AntiAliasingMode should_antialias) {
+ if (ContextDisabled())
+ return;
+
+ if (!rrect.IsRounded()) {
+ ClipRect(rrect.Rect(), should_antialias, clip_op);
+ return;
+ }
+
+ ClipRRect(rrect, should_antialias, clip_op);
+}
+
+void GraphicsContext::ClipOut(const Path& path_to_clip) {
+ if (ContextDisabled())
+ return;
+
+ // Use const_cast and temporarily toggle the inverse fill type instead of
+ // copying the path.
+ SkPath& path = const_cast<SkPath&>(path_to_clip.GetSkPath());
+ path.toggleInverseFillType();
+ ClipPath(path, kAntiAliased);
+ path.toggleInverseFillType();
+}
+
+void GraphicsContext::ClipOutRoundedRect(const FloatRoundedRect& rect) {
+ if (ContextDisabled())
+ return;
+
+ ClipRoundedRect(rect, SkClipOp::kDifference);
+}
+
+void GraphicsContext::ClipRect(const SkRect& rect,
+ AntiAliasingMode aa,
+ SkClipOp op) {
+ if (ContextDisabled())
+ return;
+ DCHECK(canvas_);
+
+ canvas_->clipRect(rect, op, aa == kAntiAliased);
+}
+
+void GraphicsContext::ClipPath(const SkPath& path,
+ AntiAliasingMode aa,
+ SkClipOp op) {
+ if (ContextDisabled())
+ return;
+ DCHECK(canvas_);
+
+ canvas_->clipPath(path, op, aa == kAntiAliased);
+}
+
+void GraphicsContext::ClipRRect(const SkRRect& rect,
+ AntiAliasingMode aa,
+ SkClipOp op) {
+ if (ContextDisabled())
+ return;
+ DCHECK(canvas_);
+
+ canvas_->clipRRect(rect, op, aa == kAntiAliased);
+}
+
+void GraphicsContext::Rotate(float angle_in_radians) {
+ if (ContextDisabled())
+ return;
+ DCHECK(canvas_);
+
+ canvas_->rotate(
+ WebCoreFloatToSkScalar(angle_in_radians * (180.0f / 3.14159265f)));
+}
+
+void GraphicsContext::Translate(float x, float y) {
+ if (ContextDisabled())
+ return;
+ DCHECK(canvas_);
+
+ if (!x && !y)
+ return;
+
+ canvas_->translate(WebCoreFloatToSkScalar(x), WebCoreFloatToSkScalar(y));
+}
+
+void GraphicsContext::Scale(float x, float y) {
+ if (ContextDisabled())
+ return;
+ DCHECK(canvas_);
+
+ canvas_->scale(WebCoreFloatToSkScalar(x), WebCoreFloatToSkScalar(y));
+}
+
+void GraphicsContext::SetURLForRect(const KURL& link,
+ const IntRect& dest_rect) {
+ if (ContextDisabled())
+ return;
+ DCHECK(canvas_);
+
+ sk_sp<SkData> url(SkData::MakeWithCString(link.GetString().Utf8().data()));
+ canvas_->Annotate(PaintCanvas::AnnotationType::URL, dest_rect,
+ std::move(url));
+}
+
+void GraphicsContext::SetURLFragmentForRect(const String& dest_name,
+ const IntRect& rect) {
+ if (ContextDisabled())
+ return;
+ DCHECK(canvas_);
+
+ sk_sp<SkData> sk_dest_name(SkData::MakeWithCString(dest_name.Utf8().data()));
+ canvas_->Annotate(PaintCanvas::AnnotationType::LINK_TO_DESTINATION, rect,
+ std::move(sk_dest_name));
+}
+
+void GraphicsContext::SetURLDestinationLocation(const String& name,
+ const IntPoint& location) {
+ if (ContextDisabled())
+ return;
+ DCHECK(canvas_);
+
+ SkRect rect = SkRect::MakeXYWH(location.X(), location.Y(), 0, 0);
+ sk_sp<SkData> sk_name(SkData::MakeWithCString(name.Utf8().data()));
+ canvas_->Annotate(PaintCanvas::AnnotationType::NAMED_DESTINATION, rect,
+ std::move(sk_name));
+}
+
+void GraphicsContext::ConcatCTM(const AffineTransform& affine) {
+ Concat(AffineTransformToSkMatrix(affine));
+}
+
+void GraphicsContext::FillRectWithRoundedHole(
+ const FloatRect& rect,
+ const FloatRoundedRect& rounded_hole_rect,
+ const Color& color) {
+ if (ContextDisabled())
+ return;
+
+ PaintFlags flags(ImmutableState()->FillFlags());
+ flags.setColor(ApplyHighContrastFilter(color).Rgb());
+ canvas_->drawDRRect(SkRRect::MakeRect(rect), rounded_hole_rect, flags);
+}
+
+void GraphicsContext::AdjustLineToPixelBoundaries(FloatPoint& p1,
+ FloatPoint& p2,
+ float stroke_width) {
+ // For odd widths, we add in 0.5 to the appropriate x/y so that the float
+ // arithmetic works out. For example, with a border width of 3, painting will
+ // pass us (y1+y2)/2, e.g., (50+53)/2 = 103/2 = 51 when we want 51.5. It is
+ // always true that an even width gave us a perfect position, but an odd width
+ // gave us a position that is off by exactly 0.5.
+ if (static_cast<int>(stroke_width) % 2) { // odd
+ if (p1.X() == p2.X()) {
+ // We're a vertical line. Adjust our x.
+ p1.SetX(p1.X() + 0.5f);
+ p2.SetX(p2.X() + 0.5f);
+ } else {
+ // We're a horizontal line. Adjust our y.
+ p1.SetY(p1.Y() + 0.5f);
+ p2.SetY(p2.Y() + 0.5f);
+ }
+ }
+}
+
+sk_sp<SkColorFilter> GraphicsContext::WebCoreColorFilterToSkiaColorFilter(
+ ColorFilter color_filter) {
+ switch (color_filter) {
+ case kColorFilterLuminanceToAlpha:
+ return SkLumaColorFilter::Make();
+ case kColorFilterLinearRGBToSRGB:
+ return InterpolationSpaceUtilities::CreateInterpolationSpaceFilter(
+ kInterpolationSpaceLinear, kInterpolationSpaceSRGB);
+ case kColorFilterSRGBToLinearRGB:
+ return InterpolationSpaceUtilities::CreateInterpolationSpaceFilter(
+ kInterpolationSpaceSRGB, kInterpolationSpaceLinear);
+ case kColorFilterNone:
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+
+ return nullptr;
+}
+
+bool GraphicsContext::ShouldApplyHighContrastFilterToImage(Image& image) {
+ if (!high_contrast_filter_)
+ return false;
+
+ switch (high_contrast_settings_.image_policy) {
+ case HighContrastImagePolicy::kFilterSmart:
+ return high_contrast_image_classifier_
+ .ShouldApplyHighContrastFilterToImage(image);
+ case HighContrastImagePolicy::kFilterAll:
+ return true;
+ default:
+ return false;
+ }
+}
+
+Color GraphicsContext::ApplyHighContrastFilter(const Color& input) const {
+ if (!high_contrast_filter_)
+ return input;
+
+ SkColor sk_input =
+ SkColorSetARGB(input.Alpha(), input.Red(), input.Green(), input.Blue());
+ SkColor sk_output = high_contrast_filter_->filterColor(sk_input);
+ return Color(MakeRGBA(SkColorGetR(sk_output), SkColorGetG(sk_output),
+ SkColorGetB(sk_output), SkColorGetA(sk_output)));
+}
+
+PaintFlags GraphicsContext::ApplyHighContrastFilter(
+ const PaintFlags* input) const {
+ if (input && !high_contrast_filter_)
+ return *input;
+
+ PaintFlags output;
+ if (input)
+ output = *input;
+ if (output.HasShader()) {
+ output.setColorFilter(high_contrast_filter_);
+ } else {
+ output.setColor(high_contrast_filter_->filterColor(output.getColor()));
+ }
+ return output;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/graphics_context.h b/chromium/third_party/blink/renderer/platform/graphics/graphics_context.h
new file mode 100644
index 00000000000..5a7394e9e39
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/graphics_context.h
@@ -0,0 +1,506 @@
+/*
+ * Copyright (C) 2003, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2008-2009 Torch Mobile, Inc.
+ * Copyright (C) 2013 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GRAPHICS_CONTEXT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GRAPHICS_CONTEXT_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/fonts/font.h"
+#include "third_party/blink/renderer/platform/graphics/dash_array.h"
+#include "third_party/blink/renderer/platform/graphics/draw_looper_builder.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context_state.h"
+#include "third_party/blink/renderer/platform/graphics/high_contrast_image_classifier.h"
+#include "third_party/blink/renderer/platform/graphics/high_contrast_settings.h"
+#include "third_party/blink/renderer/platform/graphics/image_orientation.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_filter.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_record.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_recorder.h"
+#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/skia/include/core/SkClipOp.h"
+#include "third_party/skia/include/core/SkMetaData.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+class SkPath;
+class SkRRect;
+struct SkRect;
+
+namespace blink {
+
+class FloatRect;
+class FloatRoundedRect;
+class KURL;
+class PaintController;
+class Path;
+
+class PLATFORM_EXPORT GraphicsContext {
+ WTF_MAKE_NONCOPYABLE(GraphicsContext);
+ USING_FAST_MALLOC(GraphicsContext);
+
+ public:
+ enum DisabledMode {
+ kNothingDisabled = 0, // Run as normal.
+ kFullyDisabled = 1 // Do absolutely minimal work to remove the cost of
+ // the context from performance tests.
+ };
+
+ explicit GraphicsContext(PaintController&,
+ DisabledMode = kNothingDisabled,
+ SkMetaData* = nullptr);
+
+ ~GraphicsContext();
+
+ PaintCanvas* Canvas() { return canvas_; }
+ const PaintCanvas* Canvas() const { return canvas_; }
+
+ PaintController& GetPaintController() { return paint_controller_; }
+ const PaintController& GetPaintController() const {
+ return paint_controller_;
+ }
+
+ bool ContextDisabled() const { return disabled_state_; }
+
+ const HighContrastSettings& high_contrast_settings() {
+ return high_contrast_settings_;
+ }
+
+ // ---------- State management methods -----------------
+ void Save();
+ void Restore();
+
+#if DCHECK_IS_ON()
+ unsigned SaveCount() const;
+#endif
+
+ void SetHighContrast(const HighContrastSettings&);
+
+ float StrokeThickness() const {
+ return ImmutableState()->GetStrokeData().Thickness();
+ }
+ void SetStrokeThickness(float thickness) {
+ MutableState()->SetStrokeThickness(thickness);
+ }
+
+ StrokeStyle GetStrokeStyle() const {
+ return ImmutableState()->GetStrokeData().Style();
+ }
+ void SetStrokeStyle(StrokeStyle style) {
+ MutableState()->SetStrokeStyle(style);
+ }
+
+ Color StrokeColor() const { return ImmutableState()->StrokeColor(); }
+ void SetStrokeColor(const Color& color) {
+ MutableState()->SetStrokeColor(color);
+ }
+
+ void SetLineCap(LineCap cap) { MutableState()->SetLineCap(cap); }
+ void SetLineDash(const DashArray& dashes, float dash_offset) {
+ MutableState()->SetLineDash(dashes, dash_offset);
+ }
+ void SetLineJoin(LineJoin join) { MutableState()->SetLineJoin(join); }
+ void SetMiterLimit(float limit) { MutableState()->SetMiterLimit(limit); }
+
+ Color FillColor() const { return ImmutableState()->FillColor(); }
+ void SetFillColor(const Color& color) { MutableState()->SetFillColor(color); }
+
+ void SetShouldAntialias(bool antialias) {
+ MutableState()->SetShouldAntialias(antialias);
+ }
+ bool ShouldAntialias() const { return ImmutableState()->ShouldAntialias(); }
+
+ void SetTextDrawingMode(TextDrawingModeFlags mode) {
+ MutableState()->SetTextDrawingMode(mode);
+ }
+ TextDrawingModeFlags TextDrawingMode() const {
+ return ImmutableState()->TextDrawingMode();
+ }
+
+ void SetImageInterpolationQuality(InterpolationQuality quality) {
+ MutableState()->SetInterpolationQuality(quality);
+ }
+ InterpolationQuality ImageInterpolationQuality() const {
+ return ImmutableState()->GetInterpolationQuality();
+ }
+
+ // Specify the device scale factor which may change the way document markers
+ // and fonts are rendered.
+ void SetDeviceScaleFactor(float factor) { device_scale_factor_ = factor; }
+ float DeviceScaleFactor() const { return device_scale_factor_; }
+
+ // Returns if the context is a printing context instead of a display
+ // context. Bitmap shouldn't be resampled when printing to keep the best
+ // possible quality.
+ bool Printing() const { return printing_; }
+ void SetPrinting(bool printing) { printing_ = printing; }
+
+ SkColorFilter* GetColorFilter() const;
+ void SetColorFilter(ColorFilter);
+ // ---------- End state management methods -----------------
+
+ // DrawRect() fills and always strokes using a 1-pixel stroke inset from
+ // the rect borders (of the pre-set stroke color).
+ void DrawRect(const IntRect&);
+
+ // DrawLine() only operates on horizontal or vertical lines and uses the
+ // current stroke settings.
+ void DrawLine(const IntPoint&, const IntPoint&);
+
+ void FillPath(const Path&);
+
+ // The length parameter is only used when the path has a dashed or dotted
+ // stroke style, with the default dash/dot path effect. If a non-zero length
+ // is provided the number of dashes/dots on a dashed/dotted
+ // line will be adjusted to start and end that length with a dash/dot.
+ // The dash_thickness parameter is only used when drawing dashed borders,
+ // where the stroke thickness has been set for corner miters but we want the
+ // dash length set from the border width.
+ void StrokePath(const Path&,
+ const int length = 0,
+ const int dash_thickness = 0);
+
+ void FillEllipse(const FloatRect&);
+ void StrokeEllipse(const FloatRect&);
+
+ void FillRect(const FloatRect&);
+ void FillRect(const FloatRect&,
+ const Color&,
+ SkBlendMode = SkBlendMode::kSrcOver);
+ void FillRoundedRect(const FloatRoundedRect&, const Color&);
+ void FillDRRect(const FloatRoundedRect&,
+ const FloatRoundedRect&,
+ const Color&);
+
+ void StrokeRect(const FloatRect&, float line_width);
+
+ void DrawRecord(sk_sp<const PaintRecord>);
+ void CompositeRecord(sk_sp<PaintRecord>,
+ const FloatRect& dest,
+ const FloatRect& src,
+ SkBlendMode);
+
+ void DrawImage(Image*,
+ Image::ImageDecodingMode,
+ const FloatRect& dest_rect,
+ const FloatRect* src_rect = nullptr,
+ SkBlendMode = SkBlendMode::kSrcOver,
+ RespectImageOrientationEnum = kDoNotRespectImageOrientation);
+ void DrawImageRRect(
+ Image*,
+ Image::ImageDecodingMode,
+ const FloatRoundedRect& dest,
+ const FloatRect& src_rect,
+ SkBlendMode = SkBlendMode::kSrcOver,
+ RespectImageOrientationEnum = kDoNotRespectImageOrientation);
+ void DrawTiledImage(Image*,
+ const FloatRect& dest_rect,
+ const FloatPoint& src_point,
+ const FloatSize& tile_size,
+ SkBlendMode = SkBlendMode::kSrcOver,
+ const FloatSize& repeat_spacing = FloatSize());
+ void DrawTiledImage(Image*,
+ const FloatRect& dest_rect,
+ const FloatRect& src_rect,
+ const FloatSize& tile_scale_factor,
+ Image::TileRule h_rule = Image::kStretchTile,
+ Image::TileRule v_rule = Image::kStretchTile,
+ SkBlendMode = SkBlendMode::kSrcOver);
+
+ // These methods write to the canvas.
+ // Also drawLine(const IntPoint& point1, const IntPoint& point2) and
+ // fillRoundedRect().
+ void DrawOval(const SkRect&, const PaintFlags&);
+ void DrawPath(const SkPath&, const PaintFlags&);
+ void DrawRect(const SkRect&, const PaintFlags&);
+ void DrawRRect(const SkRRect&, const PaintFlags&);
+
+ void Clip(const IntRect& rect) { ClipRect(rect); }
+ void Clip(const FloatRect& rect) { ClipRect(rect); }
+ void ClipRoundedRect(const FloatRoundedRect&,
+ SkClipOp = SkClipOp::kIntersect,
+ AntiAliasingMode = kAntiAliased);
+ void ClipOut(const IntRect& rect) {
+ ClipRect(rect, kNotAntiAliased, SkClipOp::kDifference);
+ }
+ void ClipOut(const FloatRect& rect) {
+ ClipRect(rect, kNotAntiAliased, SkClipOp::kDifference);
+ }
+ void ClipOut(const Path&);
+ void ClipOutRoundedRect(const FloatRoundedRect&);
+ void ClipPath(const SkPath&,
+ AntiAliasingMode = kNotAntiAliased,
+ SkClipOp = SkClipOp::kIntersect);
+ void ClipRect(const SkRect&,
+ AntiAliasingMode = kNotAntiAliased,
+ SkClipOp = SkClipOp::kIntersect);
+
+ void DrawText(const Font&, const TextRunPaintInfo&, const FloatPoint&);
+ void DrawText(const Font&, const NGTextFragmentPaintInfo&, const FloatPoint&);
+
+ void DrawText(const Font&,
+ const TextRunPaintInfo&,
+ const FloatPoint&,
+ const PaintFlags&);
+ void DrawText(const Font&,
+ const NGTextFragmentPaintInfo&,
+ const FloatPoint&,
+ const PaintFlags&);
+
+ void DrawEmphasisMarks(const Font&,
+ const TextRunPaintInfo&,
+ const AtomicString& mark,
+ const FloatPoint&);
+ void DrawEmphasisMarks(const Font&,
+ const NGTextFragmentPaintInfo&,
+ const AtomicString& mark,
+ const FloatPoint&);
+
+ void DrawBidiText(
+ const Font&,
+ const TextRunPaintInfo&,
+ const FloatPoint&,
+ Font::CustomFontNotReadyAction = Font::kDoNotPaintIfFontNotReady);
+ void DrawHighlightForText(const Font&,
+ const TextRun&,
+ const FloatPoint&,
+ int h,
+ const Color& background_color,
+ int from = 0,
+ int to = -1);
+
+ void DrawLineForText(const FloatPoint&, float width);
+
+ // beginLayer()/endLayer() behave like save()/restore() for CTM and clip
+ // states. Apply SkBlendMode when the layer is composited on the backdrop
+ // (i.e. endLayer()).
+ void BeginLayer(float opacity = 1.0f,
+ SkBlendMode = SkBlendMode::kSrcOver,
+ const FloatRect* = nullptr,
+ ColorFilter = kColorFilterNone,
+ sk_sp<PaintFilter> = nullptr);
+ void EndLayer();
+
+ // Instead of being dispatched to the active canvas, draw commands following
+ // beginRecording() are stored in a display list that can be replayed at a
+ // later time. Pass in the bounding rectangle for the content in the list.
+ void BeginRecording(const FloatRect&);
+
+ // Returns a record with any recorded draw commands since the prerequisite
+ // call to beginRecording(). The record is guaranteed to be non-null (but
+ // not necessarily non-empty), even when the context is disabled.
+ sk_sp<PaintRecord> EndRecording();
+
+ void SetShadow(const FloatSize& offset,
+ float blur,
+ const Color&,
+ DrawLooperBuilder::ShadowTransformMode =
+ DrawLooperBuilder::kShadowRespectsTransforms,
+ DrawLooperBuilder::ShadowAlphaMode =
+ DrawLooperBuilder::kShadowRespectsAlpha,
+ ShadowMode = kDrawShadowAndForeground);
+
+ void SetDrawLooper(sk_sp<SkDrawLooper>);
+
+ void DrawFocusRing(const Vector<IntRect>&,
+ float width,
+ int offset,
+ const Color&);
+ void DrawFocusRing(const Path&, float width, int offset, const Color&);
+
+ enum Edge {
+ kNoEdge = 0,
+ kTopEdge = 1 << 1,
+ kRightEdge = 1 << 2,
+ kBottomEdge = 1 << 3,
+ kLeftEdge = 1 << 4
+ };
+ typedef unsigned Edges;
+ void DrawInnerShadow(const FloatRoundedRect&,
+ const Color& shadow_color,
+ const FloatSize& shadow_offset,
+ float shadow_blur,
+ float shadow_spread,
+ Edges clipped_edges = kNoEdge);
+
+ const PaintFlags& FillFlags() const { return ImmutableState()->FillFlags(); }
+ // If the length of the path to be stroked is known, pass it in for correct
+ // dash or dot placement. Border painting uses a stroke thickness determined
+ // by the corner miters. Set the dash_thickness to a non-zero number for
+ // cases where dashes should be based on a different thickness.
+ const PaintFlags& StrokeFlags(const int length = 0,
+ const int dash_thickness = 0) const {
+ return ImmutableState()->StrokeFlags(length, dash_thickness);
+ }
+
+ // ---------- Transformation methods -----------------
+ void ConcatCTM(const AffineTransform&);
+
+ void Scale(float x, float y);
+ void Rotate(float angle_in_radians);
+ void Translate(float x, float y);
+ // ---------- End transformation methods -----------------
+
+ SkFilterQuality ComputeFilterQuality(Image*,
+ const FloatRect& dest,
+ const FloatRect& src) const;
+
+ // Sets target URL of a clickable area.
+ void SetURLForRect(const KURL&, const IntRect&);
+
+ // Sets the destination of a clickable area of a URL fragment (in a URL
+ // pointing to the same web page). When the area is clicked, the page should
+ // be scrolled to the location set by setURLDestinationLocation() for the
+ // destination whose name is |name|.
+ void SetURLFragmentForRect(const String& name, const IntRect&);
+
+ // Sets location of a URL destination (a.k.a. anchor) in the page.
+ void SetURLDestinationLocation(const String& name, const IntPoint&);
+
+ static void AdjustLineToPixelBoundaries(FloatPoint& p1,
+ FloatPoint& p2,
+ float stroke_width);
+
+ static int FocusRingOutsetExtent(int offset, int width);
+
+#if DCHECK_IS_ON()
+ void SetInDrawingRecorder(bool);
+#endif
+
+ static sk_sp<SkColorFilter> WebCoreColorFilterToSkiaColorFilter(ColorFilter);
+
+ private:
+ const GraphicsContextState* ImmutableState() const { return paint_state_; }
+
+ GraphicsContextState* MutableState() {
+ RealizePaintSave();
+ return paint_state_;
+ }
+
+ template <typename TextPaintInfo>
+ void DrawTextInternal(const Font&,
+ const TextPaintInfo&,
+ const FloatPoint&,
+ const PaintFlags&);
+
+ template <typename TextPaintInfo>
+ void DrawTextInternal(const Font&, const TextPaintInfo&, const FloatPoint&);
+
+ template <typename TextPaintInfo>
+ void DrawEmphasisMarksInternal(const Font&,
+ const TextPaintInfo&,
+ const AtomicString& mark,
+ const FloatPoint&);
+
+ template <typename DrawTextFunc>
+ void DrawTextPasses(const DrawTextFunc&);
+
+ void SaveLayer(const SkRect* bounds, const PaintFlags*);
+ void RestoreLayer();
+
+ // Helpers for drawing a focus ring (drawFocusRing)
+ void DrawFocusRingPath(const SkPath&, const Color&, float width);
+ void DrawFocusRingRect(const SkRect&, const Color&, float width);
+
+ // SkCanvas wrappers.
+ void ClipRRect(const SkRRect&,
+ AntiAliasingMode = kNotAntiAliased,
+ SkClipOp = SkClipOp::kIntersect);
+ void Concat(const SkMatrix&);
+
+ // Apply deferred paint state saves
+ void RealizePaintSave() {
+ if (ContextDisabled())
+ return;
+
+ if (paint_state_->SaveCount()) {
+ paint_state_->DecrementSaveCount();
+ ++paint_state_index_;
+ if (paint_state_stack_.size() == paint_state_index_) {
+ paint_state_stack_.push_back(
+ GraphicsContextState::CreateAndCopy(*paint_state_));
+ paint_state_ = paint_state_stack_[paint_state_index_].get();
+ } else {
+ GraphicsContextState* prior_paint_state = paint_state_;
+ paint_state_ = paint_state_stack_[paint_state_index_].get();
+ paint_state_->Copy(*prior_paint_state);
+ }
+ }
+ }
+
+ void FillRectWithRoundedHole(const FloatRect&,
+ const FloatRoundedRect& rounded_hole_rect,
+ const Color&);
+
+ const SkMetaData& MetaData() const { return meta_data_; }
+
+ bool ShouldApplyHighContrastFilterToImage(Image&);
+ Color ApplyHighContrastFilter(const Color& input) const;
+ PaintFlags ApplyHighContrastFilter(const PaintFlags* input) const;
+
+ // null indicates painting is contextDisabled. Never delete this object.
+ PaintCanvas* canvas_;
+
+ PaintController& paint_controller_;
+
+ // Paint states stack. The state controls the appearance of drawn content, so
+ // this stack enables local drawing state changes with save()/restore() calls.
+ // We do not delete from this stack to avoid memory churn.
+ Vector<std::unique_ptr<GraphicsContextState>> paint_state_stack_;
+
+ // Current index on the stack. May not be the last thing on the stack.
+ unsigned paint_state_index_;
+
+ // Raw pointer to the current state.
+ GraphicsContextState* paint_state_;
+
+ PaintRecorder paint_recorder_;
+
+ SkMetaData meta_data_;
+
+#if DCHECK_IS_ON()
+ int layer_count_;
+ bool disable_destruction_checks_;
+ bool in_drawing_recorder_;
+#endif
+
+ const DisabledMode disabled_state_;
+
+ float device_scale_factor_;
+
+ HighContrastSettings high_contrast_settings_;
+ sk_sp<SkColorFilter> high_contrast_filter_;
+ HighContrastImageClassifier high_contrast_image_classifier_;
+
+ unsigned printing_ : 1;
+ unsigned has_meta_data_ : 1;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GRAPHICS_CONTEXT_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/graphics_context_state.cc b/chromium/third_party/blink/renderer/platform/graphics/graphics_context_state.cc
new file mode 100644
index 00000000000..541d8cbadd5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/graphics_context_state.cc
@@ -0,0 +1,126 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/graphics_context_state.h"
+
+#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
+
+namespace blink {
+
+static inline SkFilterQuality FilterQualityForPaint(
+ InterpolationQuality quality) {
+ // The filter quality "selected" here will primarily be used when painting a
+ // primitive using one of the PaintFlags below. For the most part this will
+ // not affect things that are part of the Image class hierarchy (which use
+ // the unmodified m_interpolationQuality.)
+ return quality != kInterpolationNone ? kLow_SkFilterQuality
+ : kNone_SkFilterQuality;
+}
+
+GraphicsContextState::GraphicsContextState()
+ : text_drawing_mode_(kTextModeFill),
+ interpolation_quality_(kInterpolationDefault),
+ save_count_(0),
+ should_antialias_(true) {
+ stroke_flags_.setStyle(PaintFlags::kStroke_Style);
+ stroke_flags_.setStrokeWidth(SkFloatToScalar(stroke_data_.Thickness()));
+ stroke_flags_.setStrokeCap(PaintFlags::kDefault_Cap);
+ stroke_flags_.setStrokeJoin(PaintFlags::kDefault_Join);
+ stroke_flags_.setStrokeMiter(SkFloatToScalar(stroke_data_.MiterLimit()));
+ stroke_flags_.setFilterQuality(FilterQualityForPaint(interpolation_quality_));
+ stroke_flags_.setAntiAlias(should_antialias_);
+ fill_flags_.setFilterQuality(FilterQualityForPaint(interpolation_quality_));
+ fill_flags_.setAntiAlias(should_antialias_);
+}
+
+GraphicsContextState::GraphicsContextState(const GraphicsContextState& other)
+ : stroke_flags_(other.stroke_flags_),
+ fill_flags_(other.fill_flags_),
+ stroke_data_(other.stroke_data_),
+ text_drawing_mode_(other.text_drawing_mode_),
+ interpolation_quality_(other.interpolation_quality_),
+ save_count_(0),
+ should_antialias_(other.should_antialias_) {}
+
+void GraphicsContextState::Copy(const GraphicsContextState& source) {
+ this->~GraphicsContextState();
+ new (this) GraphicsContextState(source);
+}
+
+const PaintFlags& GraphicsContextState::StrokeFlags(
+ const int stroked_path_length,
+ const int dash_thickness) const {
+ stroke_data_.SetupPaintDashPathEffect(&stroke_flags_, stroked_path_length,
+ dash_thickness);
+ return stroke_flags_;
+}
+
+void GraphicsContextState::SetStrokeStyle(StrokeStyle style) {
+ stroke_data_.SetStyle(style);
+}
+
+void GraphicsContextState::SetStrokeThickness(float thickness) {
+ stroke_data_.SetThickness(thickness);
+ stroke_flags_.setStrokeWidth(SkFloatToScalar(thickness));
+}
+
+void GraphicsContextState::SetStrokeColor(const Color& color) {
+ stroke_flags_.setColor(color.Rgb());
+ stroke_flags_.setShader(nullptr);
+}
+
+void GraphicsContextState::SetLineCap(LineCap cap) {
+ stroke_data_.SetLineCap(cap);
+ stroke_flags_.setStrokeCap((PaintFlags::Cap)cap);
+}
+
+void GraphicsContextState::SetLineJoin(LineJoin join) {
+ stroke_data_.SetLineJoin(join);
+ stroke_flags_.setStrokeJoin((PaintFlags::Join)join);
+}
+
+void GraphicsContextState::SetMiterLimit(float miter_limit) {
+ stroke_data_.SetMiterLimit(miter_limit);
+ stroke_flags_.setStrokeMiter(SkFloatToScalar(miter_limit));
+}
+
+void GraphicsContextState::SetFillColor(const Color& color) {
+ fill_flags_.setColor(color.Rgb());
+ fill_flags_.setShader(nullptr);
+}
+
+// Shadow. (This will need tweaking if we use draw loopers for other things.)
+void GraphicsContextState::SetDrawLooper(sk_sp<SkDrawLooper> draw_looper) {
+ // Grab a new ref for stroke.
+ stroke_flags_.setLooper(draw_looper);
+ // Pass the existing ref to fill (to minimize refcount churn).
+ fill_flags_.setLooper(std::move(draw_looper));
+}
+
+void GraphicsContextState::SetLineDash(const DashArray& dashes,
+ float dash_offset) {
+ stroke_data_.SetLineDash(dashes, dash_offset);
+}
+
+void GraphicsContextState::SetColorFilter(sk_sp<SkColorFilter> color_filter) {
+ // Grab a new ref for stroke.
+ stroke_flags_.setColorFilter(color_filter);
+ // Pass the existing ref to fill (to minimize refcount churn).
+ fill_flags_.setColorFilter(std::move(color_filter));
+}
+
+void GraphicsContextState::SetInterpolationQuality(
+ InterpolationQuality quality) {
+ interpolation_quality_ = quality;
+ stroke_flags_.setFilterQuality(FilterQualityForPaint(quality));
+ fill_flags_.setFilterQuality(FilterQualityForPaint(quality));
+}
+
+void GraphicsContextState::SetShouldAntialias(bool should_antialias) {
+ should_antialias_ = should_antialias;
+ stroke_flags_.setAntiAlias(should_antialias);
+ fill_flags_.setAntiAlias(should_antialias);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/graphics_context_state.h b/chromium/third_party/blink/renderer/platform/graphics/graphics_context_state.h
new file mode 100644
index 00000000000..cbe5e352c88
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/graphics_context_state.h
@@ -0,0 +1,140 @@
+// Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GRAPHICS_CONTEXT_STATE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GRAPHICS_CONTEXT_STATE_H_
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/platform/graphics/draw_looper_builder.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_types.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
+#include "third_party/blink/renderer/platform/graphics/stroke_data.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/skia/include/core/SkColorFilter.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+namespace blink {
+
+// Encapsulates the state information we store for each pushed graphics state.
+// Only GraphicsContext can use this class.
+class PLATFORM_EXPORT GraphicsContextState final {
+ USING_FAST_MALLOC(GraphicsContextState);
+
+ public:
+ static std::unique_ptr<GraphicsContextState> Create() {
+ return base::WrapUnique(new GraphicsContextState());
+ }
+
+ static std::unique_ptr<GraphicsContextState> CreateAndCopy(
+ const GraphicsContextState& other) {
+ return base::WrapUnique(new GraphicsContextState(other));
+ }
+
+ void Copy(const GraphicsContextState&);
+
+ // PaintFlags objects that reflect the current state. If the length of the
+ // path to be stroked is known, pass it in for correct dash or dot placement.
+ const PaintFlags& StrokeFlags(const int stroked_path_length = 0,
+ const int dash_thickness = 0) const;
+ const PaintFlags& FillFlags() const { return fill_flags_; }
+
+ uint16_t SaveCount() const { return save_count_; }
+ void IncrementSaveCount() { ++save_count_; }
+ void DecrementSaveCount() { --save_count_; }
+
+ // Stroke data
+ Color StrokeColor() const { return stroke_flags_.getColor(); }
+ void SetStrokeColor(const Color&);
+
+ const StrokeData& GetStrokeData() const { return stroke_data_; }
+ void SetStrokeStyle(StrokeStyle);
+ void SetStrokeThickness(float);
+ void SetLineCap(LineCap);
+ void SetLineJoin(LineJoin);
+ void SetMiterLimit(float);
+ void SetLineDash(const DashArray&, float);
+
+ // Fill data
+ Color FillColor() const { return fill_flags_.getColor(); }
+ void SetFillColor(const Color&);
+
+ // Shadow. (This will need tweaking if we use draw loopers for other things.)
+ SkDrawLooper* DrawLooper() const {
+ DCHECK_EQ(fill_flags_.getLooper(), stroke_flags_.getLooper());
+ return fill_flags_.getLooper().get();
+ }
+ void SetDrawLooper(sk_sp<SkDrawLooper>);
+
+ // Text. (See TextModeFill & friends.)
+ TextDrawingModeFlags TextDrawingMode() const { return text_drawing_mode_; }
+ void SetTextDrawingMode(TextDrawingModeFlags mode) {
+ text_drawing_mode_ = mode;
+ }
+
+ SkColorFilter* GetColorFilter() const {
+ DCHECK_EQ(fill_flags_.getColorFilter(), stroke_flags_.getColorFilter());
+ return fill_flags_.getColorFilter().get();
+ }
+ void SetColorFilter(sk_sp<SkColorFilter>);
+
+ // Image interpolation control.
+ InterpolationQuality GetInterpolationQuality() const {
+ return interpolation_quality_;
+ }
+ void SetInterpolationQuality(InterpolationQuality);
+
+ bool ShouldAntialias() const { return should_antialias_; }
+ void SetShouldAntialias(bool);
+
+ private:
+ GraphicsContextState();
+ explicit GraphicsContextState(const GraphicsContextState&);
+ GraphicsContextState& operator=(const GraphicsContextState&) = delete;
+
+ // This is mutable to enable dash path effect updates when the paint is
+ // fetched for use.
+ mutable PaintFlags stroke_flags_;
+ PaintFlags fill_flags_;
+
+ StrokeData stroke_data_;
+
+ TextDrawingModeFlags text_drawing_mode_;
+
+ InterpolationQuality interpolation_quality_;
+
+ uint16_t save_count_;
+
+ bool should_antialias_ : 1;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GRAPHICS_CONTEXT_STATE_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/graphics_context_state_saver.h b/chromium/third_party/blink/renderer/platform/graphics/graphics_context_state_saver.h
new file mode 100644
index 00000000000..f7ca44839fd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/graphics_context_state_saver.h
@@ -0,0 +1,84 @@
+// Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GRAPHICS_CONTEXT_STATE_SAVER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GRAPHICS_CONTEXT_STATE_SAVER_H_
+
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT GraphicsContextStateSaver final {
+ USING_FAST_MALLOC(GraphicsContextStateSaver);
+ WTF_MAKE_NONCOPYABLE(GraphicsContextStateSaver);
+
+ public:
+ GraphicsContextStateSaver(GraphicsContext& context,
+ bool save_and_restore = true)
+ : context_(context), save_and_restore_(save_and_restore) {
+ if (save_and_restore_)
+ context_.Save();
+ }
+
+ ~GraphicsContextStateSaver() {
+ if (save_and_restore_)
+ context_.Restore();
+ }
+
+ void Save() {
+ DCHECK(!save_and_restore_);
+ context_.Save();
+ save_and_restore_ = true;
+ }
+
+ void SaveIfNeeded() {
+ if (Saved())
+ return;
+ Save();
+ }
+
+ void Restore() {
+ DCHECK(save_and_restore_);
+ context_.Restore();
+ save_and_restore_ = false;
+ }
+
+ GraphicsContext& Context() const { return context_; }
+ bool Saved() const { return save_and_restore_; }
+
+ private:
+ GraphicsContext& context_;
+ bool save_and_restore_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GRAPHICS_CONTEXT_STATE_SAVER_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/graphics_context_test.cc b/chromium/third_party/blink/renderer/platform/graphics/graphics_context_test.cc
new file mode 100644
index 00000000000..02042667031
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/graphics_context_test.cc
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2012 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/graphics/graphics_context.h"
+
+#include <memory>
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/graphics/bitmap_image.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_record.h"
+#include "third_party/blink/renderer/platform/graphics/path.h"
+#include "third_party/blink/renderer/platform/testing/font_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
+#include "third_party/blink/renderer/platform/text/text_run.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkShader.h"
+
+namespace blink {
+
+#define EXPECT_EQ_RECT(a, b) \
+ EXPECT_EQ(a.x(), b.x()); \
+ EXPECT_EQ(a.y(), b.y()); \
+ EXPECT_EQ(a.width(), b.width()); \
+ EXPECT_EQ(a.height(), b.height());
+
+#define EXPECT_OPAQUE_PIXELS_IN_RECT(bitmap, opaqueRect) \
+ { \
+ for (int y = opaqueRect.Y(); y < opaqueRect.MaxY(); ++y) \
+ for (int x = opaqueRect.X(); x < opaqueRect.MaxX(); ++x) { \
+ int alpha = *bitmap.getAddr32(x, y) >> 24; \
+ EXPECT_EQ(255, alpha); \
+ } \
+ }
+
+#define EXPECT_OPAQUE_PIXELS_ONLY_IN_RECT(bitmap, opaqueRect) \
+ { \
+ for (int y = 0; y < bitmap.height(); ++y) \
+ for (int x = 0; x < bitmap.width(); ++x) { \
+ int alpha = *bitmap.getAddr32(x, y) >> 24; \
+ bool opaque = opaqueRect.Contains(x, y); \
+ EXPECT_EQ(opaque, alpha == 255); \
+ } \
+ }
+
+TEST(GraphicsContextTest, Recording) {
+ SkBitmap bitmap;
+ bitmap.allocN32Pixels(100, 100);
+ bitmap.eraseColor(0);
+ SkiaPaintCanvas canvas(bitmap);
+
+ std::unique_ptr<PaintController> paint_controller = PaintController::Create();
+ GraphicsContext context(*paint_controller);
+
+ Color opaque(1.0f, 0.0f, 0.0f, 1.0f);
+ FloatRect bounds(0, 0, 100, 100);
+
+ context.BeginRecording(bounds);
+ context.FillRect(FloatRect(0, 0, 50, 50), opaque, SkBlendMode::kSrcOver);
+ canvas.drawPicture(context.EndRecording());
+ EXPECT_OPAQUE_PIXELS_ONLY_IN_RECT(bitmap, IntRect(0, 0, 50, 50))
+
+ context.BeginRecording(bounds);
+ context.FillRect(FloatRect(0, 0, 100, 100), opaque, SkBlendMode::kSrcOver);
+ // Make sure the opaque region was unaffected by the rect drawn during
+ // recording.
+ EXPECT_OPAQUE_PIXELS_ONLY_IN_RECT(bitmap, IntRect(0, 0, 50, 50))
+
+ canvas.drawPicture(context.EndRecording());
+ EXPECT_OPAQUE_PIXELS_ONLY_IN_RECT(bitmap, IntRect(0, 0, 100, 100))
+}
+
+TEST(GraphicsContextTest, UnboundedDrawsAreClipped) {
+ SkBitmap bitmap;
+ bitmap.allocN32Pixels(400, 400);
+ bitmap.eraseColor(0);
+ SkiaPaintCanvas canvas(bitmap);
+
+ Color opaque(1.0f, 0.0f, 0.0f, 1.0f);
+ Color alpha(0.0f, 0.0f, 0.0f, 0.0f);
+ FloatRect bounds(0, 0, 100, 100);
+
+ std::unique_ptr<PaintController> paint_controller = PaintController::Create();
+ GraphicsContext context(*paint_controller);
+ context.BeginRecording(bounds);
+
+ context.SetShouldAntialias(false);
+ context.SetMiterLimit(1);
+ context.SetStrokeThickness(5);
+ context.SetLineCap(kSquareCap);
+ context.SetStrokeStyle(kSolidStroke);
+
+ // Make skia unable to compute fast bounds for our paths.
+ DashArray dash_array;
+ dash_array.push_back(1);
+ dash_array.push_back(0);
+ context.SetLineDash(dash_array, 0);
+
+ // Make the device opaque in 10,10 40x40.
+ context.FillRect(FloatRect(10, 10, 40, 40), opaque, SkBlendMode::kSrcOver);
+ canvas.drawPicture(context.EndRecording());
+ EXPECT_OPAQUE_PIXELS_ONLY_IN_RECT(bitmap, IntRect(10, 10, 40, 40));
+
+ context.BeginRecording(bounds);
+ // Clip to the left edge of the opaque area.
+ context.Clip(IntRect(10, 10, 10, 40));
+
+ // Draw a path that gets clipped. This should destroy the opaque area, but
+ // only inside the clip.
+ Path path;
+ path.MoveTo(FloatPoint(10, 10));
+ path.AddLineTo(FloatPoint(40, 40));
+ PaintFlags flags;
+ flags.setColor(alpha.Rgb());
+ flags.setBlendMode(SkBlendMode::kSrcOut);
+ context.DrawPath(path.GetSkPath(), flags);
+
+ canvas.drawPicture(context.EndRecording());
+ EXPECT_OPAQUE_PIXELS_IN_RECT(bitmap, IntRect(20, 10, 30, 40));
+}
+
+class GraphicsContextHighConstrastTest : public testing::Test {
+ protected:
+ void SetUp() override {
+ bitmap_.allocN32Pixels(4, 1);
+ bitmap_.eraseColor(0);
+ canvas_ = std::make_unique<SkiaPaintCanvas>(bitmap_);
+ paint_controller_ = PaintController::Create();
+ context_ = std::make_unique<GraphicsContext>(*paint_controller_);
+ context_->BeginRecording(FloatRect(0, 0, 4, 1));
+ }
+
+ void DrawColorsToContext() {
+ Color black(0.0f, 0.0f, 0.0f, 1.0f);
+ Color white(1.0f, 1.0f, 1.0f, 1.0f);
+ Color red(1.0f, 0.0f, 0.0f, 1.0f);
+ Color gray(0.5f, 0.5f, 0.5f, 1.0f);
+ context_->FillRect(FloatRect(0, 0, 1, 1), black);
+ context_->FillRect(FloatRect(1, 0, 1, 1), white);
+ context_->FillRect(FloatRect(2, 0, 1, 1), red);
+ context_->FillRect(FloatRect(3, 0, 1, 1), gray);
+ // Capture the result in the bitmap.
+ canvas_->drawPicture(context_->EndRecording());
+ }
+
+ SkBitmap bitmap_;
+ std::unique_ptr<SkiaPaintCanvas> canvas_;
+ std::unique_ptr<PaintController> paint_controller_;
+ std::unique_ptr<GraphicsContext> context_;
+};
+
+// This is just a baseline test, compare against the other variants
+// of the test below, where high contrast mode is enabled.
+TEST_F(GraphicsContextHighConstrastTest, NoHighContrast) {
+ DrawColorsToContext();
+
+ EXPECT_EQ(0xff000000, *bitmap_.getAddr32(0, 0));
+ EXPECT_EQ(0xffffffff, *bitmap_.getAddr32(1, 0));
+ EXPECT_EQ(0xffff0000, *bitmap_.getAddr32(2, 0));
+ EXPECT_EQ(0xff808080, *bitmap_.getAddr32(3, 0));
+}
+
+TEST_F(GraphicsContextHighConstrastTest, HighContrastOff) {
+ HighContrastSettings settings;
+ settings.mode = HighContrastMode::kOff;
+ settings.grayscale = false;
+ settings.contrast = 0;
+ context_->SetHighContrast(settings);
+
+ DrawColorsToContext();
+
+ EXPECT_EQ(0xff000000, *bitmap_.getAddr32(0, 0));
+ EXPECT_EQ(0xffffffff, *bitmap_.getAddr32(1, 0));
+ EXPECT_EQ(0xffff0000, *bitmap_.getAddr32(2, 0));
+ EXPECT_EQ(0xff808080, *bitmap_.getAddr32(3, 0));
+}
+
+// Simple invert for testing. Each color component |c|
+// is replaced with |255 - c| for easy testing.
+TEST_F(GraphicsContextHighConstrastTest, SimpleInvertForTesting) {
+ HighContrastSettings settings;
+ settings.mode = HighContrastMode::kSimpleInvertForTesting;
+ settings.grayscale = false;
+ settings.contrast = 0;
+ context_->SetHighContrast(settings);
+
+ DrawColorsToContext();
+
+ EXPECT_EQ(0xffffffff, *bitmap_.getAddr32(0, 0));
+ EXPECT_EQ(0xff000000, *bitmap_.getAddr32(1, 0));
+ EXPECT_EQ(0xff00ffff, *bitmap_.getAddr32(2, 0));
+ EXPECT_EQ(0xff7f7f7f, *bitmap_.getAddr32(3, 0));
+}
+
+// Invert brightness (with gamma correction).
+TEST_F(GraphicsContextHighConstrastTest, InvertBrightness) {
+ HighContrastSettings settings;
+ settings.mode = HighContrastMode::kInvertBrightness;
+ settings.grayscale = false;
+ settings.contrast = 0;
+ context_->SetHighContrast(settings);
+
+ DrawColorsToContext();
+
+ EXPECT_EQ(0xffffffff, *bitmap_.getAddr32(0, 0));
+ EXPECT_EQ(0xff000000, *bitmap_.getAddr32(1, 0));
+ EXPECT_EQ(0xff00ffff, *bitmap_.getAddr32(2, 0));
+ EXPECT_EQ(0xffdddddd, *bitmap_.getAddr32(3, 0));
+}
+
+// Invert lightness (in HSL space).
+TEST_F(GraphicsContextHighConstrastTest, InvertLightness) {
+ HighContrastSettings settings;
+ settings.mode = HighContrastMode::kInvertLightness;
+ settings.grayscale = false;
+ settings.contrast = 0;
+ context_->SetHighContrast(settings);
+
+ DrawColorsToContext();
+
+ EXPECT_EQ(0xffffffff, *bitmap_.getAddr32(0, 0));
+ EXPECT_EQ(0xff000000, *bitmap_.getAddr32(1, 0));
+ EXPECT_EQ(0xffff0000, *bitmap_.getAddr32(2, 0));
+ EXPECT_EQ(0xffdddddd, *bitmap_.getAddr32(3, 0));
+}
+
+// Invert lightness plus grayscale.
+TEST_F(GraphicsContextHighConstrastTest, InvertLightnessPlusGrayscale) {
+ HighContrastSettings settings;
+ settings.mode = HighContrastMode::kInvertLightness;
+ settings.grayscale = true;
+ settings.contrast = 0;
+ context_->SetHighContrast(settings);
+
+ DrawColorsToContext();
+
+ EXPECT_EQ(0xffffffff, *bitmap_.getAddr32(0, 0));
+ EXPECT_EQ(0xff000000, *bitmap_.getAddr32(1, 0));
+ EXPECT_EQ(0xffe2e2e2, *bitmap_.getAddr32(2, 0));
+ EXPECT_EQ(0xffdddddd, *bitmap_.getAddr32(3, 0));
+}
+
+TEST_F(GraphicsContextHighConstrastTest, InvertLightnessPlusContrast) {
+ HighContrastSettings settings;
+ settings.mode = HighContrastMode::kInvertLightness;
+ settings.grayscale = false;
+ settings.contrast = 0.2;
+ context_->SetHighContrast(settings);
+
+ DrawColorsToContext();
+
+ EXPECT_EQ(0xffffffff, *bitmap_.getAddr32(0, 0));
+ EXPECT_EQ(0xff000000, *bitmap_.getAddr32(1, 0));
+ EXPECT_EQ(0xffff0000, *bitmap_.getAddr32(2, 0));
+ EXPECT_EQ(0xffeeeeee, *bitmap_.getAddr32(3, 0));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/graphics_layer.cc b/chromium/third_party/blink/renderer/platform/graphics/graphics_layer.cc
new file mode 100644
index 00000000000..09694f3d582
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/graphics_layer.cc
@@ -0,0 +1,1482 @@
+/*
+ * Copyright (C) 2009 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/graphics/graphics_layer.h"
+
+#include <algorithm>
+#include <cmath>
+#include <memory>
+#include <utility>
+#include "SkMatrix44.h"
+#include "base/memory/ptr_util.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "cc/layers/layer.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_compositor_support.h"
+#include "third_party/blink/public/platform/web_display_item_list.h"
+#include "third_party/blink/public/platform/web_float_point.h"
+#include "third_party/blink/public/platform/web_float_rect.h"
+#include "third_party/blink/public/platform/web_layer.h"
+#include "third_party/blink/public/platform/web_point.h"
+#include "third_party/blink/public/platform/web_size.h"
+#include "third_party/blink/renderer/platform/bindings/runtime_call_stats.h"
+#include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
+#include "third_party/blink/renderer/platform/drag_image.h"
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/geometry/geometry_as_json.h"
+#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
+#include "third_party/blink/renderer/platform/geometry/region.h"
+#include "third_party/blink/renderer/platform/graphics/bitmap_image.h"
+#include "third_party/blink/renderer/platform/graphics/compositing/composited_layer_raster_invalidator.h"
+#include "third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.h"
+#include "third_party/blink/renderer/platform/graphics/compositor_filter_operations.h"
+#include "third_party/blink/renderer/platform/graphics/first_paint_invalidation_tracking.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/image.h"
+#include "third_party/blink/renderer/platform/graphics/link_highlight.h"
+#include "third_party/blink/renderer/platform/graphics/logging_canvas.h"
+#include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
+#include "third_party/blink/renderer/platform/graphics/paint/property_tree_state.h"
+#include "third_party/blink/renderer/platform/graphics/paint/raster_invalidation_tracking.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/json/json_values.h"
+#include "third_party/blink/renderer/platform/scroll/scroll_snap_data.h"
+#include "third_party/blink/renderer/platform/scroll/scrollable_area.h"
+#include "third_party/blink/renderer/platform/text/text_stream.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+std::unique_ptr<GraphicsLayer> GraphicsLayer::Create(
+ GraphicsLayerClient& client) {
+ return base::WrapUnique(new GraphicsLayer(client));
+}
+
+GraphicsLayer::GraphicsLayer(GraphicsLayerClient& client)
+ : client_(client),
+ background_color_(Color::kTransparent),
+ opacity_(1),
+ blend_mode_(WebBlendMode::kNormal),
+ has_transform_origin_(false),
+ contents_opaque_(false),
+ should_flatten_transform_(true),
+ backface_visibility_(true),
+ draws_content_(false),
+ contents_visible_(true),
+ is_root_for_isolated_group_(false),
+ hit_testable_without_draws_content_(false),
+ needs_check_raster_invalidation_(false),
+ has_scroll_parent_(false),
+ has_clip_parent_(false),
+ painted_(false),
+ painting_phase_(kGraphicsLayerPaintAllWithOverflowClip),
+ parent_(nullptr),
+ mask_layer_(nullptr),
+ contents_clipping_mask_layer_(nullptr),
+ paint_count_(0),
+ contents_layer_(nullptr),
+ contents_layer_id_(0),
+ scrollable_area_(nullptr),
+ rendering_context3d_(0),
+ weak_ptr_factory_(this) {
+#if DCHECK_IS_ON()
+ client.VerifyNotPainting();
+#endif
+ layer_ = Platform::Current()->CompositorSupport()->CreateContentLayer(this);
+ layer_->Layer()->SetDrawsContent(draws_content_ && contents_visible_);
+ layer_->Layer()->SetLayerClient(weak_ptr_factory_.GetWeakPtr());
+
+ UpdateTrackingRasterInvalidations();
+}
+
+GraphicsLayer::~GraphicsLayer() {
+ layer_->Layer()->SetLayerClient(nullptr);
+ SetContentsLayer(nullptr);
+ for (size_t i = 0; i < link_highlights_.size(); ++i)
+ link_highlights_[i]->ClearCurrentGraphicsLayer();
+ link_highlights_.clear();
+
+#if DCHECK_IS_ON()
+ client_.VerifyNotPainting();
+#endif
+
+ RemoveAllChildren();
+ RemoveFromParent();
+ DCHECK(!parent_);
+}
+
+LayoutRect GraphicsLayer::VisualRect() const {
+ LayoutRect bounds = LayoutRect(FloatPoint(), Size());
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ DCHECK(layer_state_);
+ bounds.MoveBy(layer_state_->offset);
+ } else {
+ bounds.Move(OffsetFromLayoutObjectWithSubpixelAccumulation());
+ }
+ return bounds;
+}
+
+void GraphicsLayer::SetHasWillChangeTransformHint(
+ bool has_will_change_transform) {
+ layer_->Layer()->SetHasWillChangeTransformHint(has_will_change_transform);
+}
+
+void GraphicsLayer::SetOverscrollBehavior(
+ const WebOverscrollBehavior& behavior) {
+ layer_->Layer()->SetOverscrollBehavior(behavior);
+}
+
+void GraphicsLayer::SetSnapContainerData(Optional<SnapContainerData> data) {
+ layer_->Layer()->SetSnapContainerData(std::move(data));
+}
+
+void GraphicsLayer::SetIsResizedByBrowserControls(
+ bool is_resized_by_browser_controls) {
+ PlatformLayer()->SetIsResizedByBrowserControls(
+ is_resized_by_browser_controls);
+}
+
+void GraphicsLayer::SetIsContainerForFixedPositionLayers(bool is_container) {
+ PlatformLayer()->SetIsContainerForFixedPositionLayers(is_container);
+}
+
+void GraphicsLayer::SetParent(GraphicsLayer* layer) {
+#if DCHECK_IS_ON()
+ DCHECK(!layer || !layer->HasAncestor(this));
+#endif
+ parent_ = layer;
+}
+
+#if DCHECK_IS_ON()
+
+bool GraphicsLayer::HasAncestor(GraphicsLayer* ancestor) const {
+ for (GraphicsLayer* curr = Parent(); curr; curr = curr->Parent()) {
+ if (curr == ancestor)
+ return true;
+ }
+
+ return false;
+}
+
+#endif
+
+bool GraphicsLayer::SetChildren(const GraphicsLayerVector& new_children) {
+ // If the contents of the arrays are the same, nothing to do.
+ if (new_children == children_)
+ return false;
+
+ RemoveAllChildren();
+
+ size_t list_size = new_children.size();
+ for (size_t i = 0; i < list_size; ++i)
+ AddChildInternal(new_children[i]);
+
+ UpdateChildList();
+
+ return true;
+}
+
+void GraphicsLayer::AddChildInternal(GraphicsLayer* child_layer) {
+ DCHECK_NE(child_layer, this);
+
+ if (child_layer->Parent())
+ child_layer->RemoveFromParent();
+
+ child_layer->SetParent(this);
+ children_.push_back(child_layer);
+
+ // Don't call updateChildList here, this function is used in cases where it
+ // should not be called until all children are processed.
+}
+
+void GraphicsLayer::AddChild(GraphicsLayer* child_layer) {
+ AddChildInternal(child_layer);
+ UpdateChildList();
+}
+
+void GraphicsLayer::AddChildBelow(GraphicsLayer* child_layer,
+ GraphicsLayer* sibling) {
+ DCHECK_NE(child_layer, this);
+ child_layer->RemoveFromParent();
+
+ bool found = false;
+ for (unsigned i = 0; i < children_.size(); i++) {
+ if (sibling == children_[i]) {
+ children_.insert(i, child_layer);
+ found = true;
+ break;
+ }
+ }
+
+ child_layer->SetParent(this);
+
+ if (!found)
+ children_.push_back(child_layer);
+
+ UpdateChildList();
+}
+
+void GraphicsLayer::RemoveAllChildren() {
+ while (!children_.IsEmpty()) {
+ GraphicsLayer* cur_layer = children_.back();
+ DCHECK(cur_layer->Parent());
+ cur_layer->RemoveFromParent();
+ }
+}
+
+void GraphicsLayer::RemoveFromParent() {
+ if (parent_) {
+ // We use reverseFind so that removeAllChildren() isn't n^2.
+ parent_->children_.EraseAt(parent_->children_.ReverseFind(this));
+ SetParent(nullptr);
+ }
+
+ PlatformLayer()->RemoveFromParent();
+}
+
+void GraphicsLayer::SetOffsetFromLayoutObject(
+ const IntSize& offset,
+ ShouldSetNeedsDisplay should_set_needs_display) {
+ SetOffsetDoubleFromLayoutObject(offset);
+}
+
+void GraphicsLayer::SetOffsetDoubleFromLayoutObject(
+ const DoubleSize& offset,
+ ShouldSetNeedsDisplay should_set_needs_display) {
+ if (offset == offset_from_layout_object_)
+ return;
+
+ offset_from_layout_object_ = offset;
+ PlatformLayer()->SetFiltersOrigin(FloatPoint() - ToFloatSize(offset));
+
+ // If the compositing layer offset changes, we need to repaint.
+ if (should_set_needs_display == kSetNeedsDisplay)
+ SetNeedsDisplay();
+}
+
+LayoutSize GraphicsLayer::OffsetFromLayoutObjectWithSubpixelAccumulation()
+ const {
+ return LayoutSize(OffsetFromLayoutObject()) + client_.SubpixelAccumulation();
+}
+
+IntRect GraphicsLayer::InterestRect() {
+ return previous_interest_rect_;
+}
+
+void GraphicsLayer::PaintRecursively() {
+ Vector<GraphicsLayer*> repainted_layers;
+ PaintRecursivelyInternal(repainted_layers);
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ // Clear paint property change flags that are for this paint only.
+ for (auto* layer : repainted_layers) {
+ for (const auto& chunk :
+ layer->GetPaintController().GetPaintArtifact().PaintChunks())
+ chunk.properties.property_tree_state.ClearChangedToRoot();
+ }
+ }
+
+#if DCHECK_IS_ON()
+ if (VLOG_IS_ON(2)) {
+ static String s_previous_tree;
+ LayerTreeFlags flags = VLOG_IS_ON(3) ? 0xffffffff : kOutputAsLayerTree;
+ String new_tree = GetLayerTreeAsTextForTesting(flags);
+ if (new_tree != s_previous_tree) {
+ LOG(ERROR) << "After GraphicsLayer::PaintRecursively()\n"
+ << "GraphicsLayer tree:\n"
+ << new_tree.Utf8().data();
+ s_previous_tree = new_tree;
+ }
+ }
+#endif
+}
+
+void GraphicsLayer::PaintRecursivelyInternal(
+ Vector<GraphicsLayer*>& repainted_layers) {
+ if (DrawsContent()) {
+ if (Paint(nullptr))
+ repainted_layers.push_back(this);
+ }
+
+ if (MaskLayer())
+ MaskLayer()->PaintRecursivelyInternal(repainted_layers);
+ if (ContentsClippingMaskLayer())
+ ContentsClippingMaskLayer()->PaintRecursivelyInternal(repainted_layers);
+
+ for (auto* child : Children())
+ child->PaintRecursivelyInternal(repainted_layers);
+}
+
+bool GraphicsLayer::Paint(const IntRect* interest_rect,
+ GraphicsContext::DisabledMode disabled_mode) {
+ if (PaintWithoutCommit(interest_rect, disabled_mode))
+ GetPaintController().CommitNewDisplayItems();
+ else if (!needs_check_raster_invalidation_)
+ return false;
+
+#if DCHECK_IS_ON()
+ if (VLOG_IS_ON(2)) {
+ LOG(ERROR) << "Painted GraphicsLayer: " << DebugName()
+ << " interest_rect=" << InterestRect().ToString();
+ }
+#endif
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ DCHECK(layer_state_) << "No layer state for GraphicsLayer: " << DebugName();
+ // Generate raster invalidations for SPv175 (but not SPv2).
+ IntRect layer_bounds(layer_state_->offset, ExpandedIntSize(Size()));
+ EnsureRasterInvalidator().Generate(
+ layer_bounds, GetPaintController().GetPaintArtifact().PaintChunks(),
+ layer_state_->state, VisualRectSubpixelOffset());
+ }
+
+ if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled() &&
+ DrawsContent()) {
+ auto& tracking = EnsureRasterInvalidator().EnsureTracking();
+ tracking.CheckUnderInvalidations(DebugName(), CapturePaintRecord(),
+ InterestRect());
+ if (auto record = tracking.UnderInvalidationRecord()) {
+ // Add the under-invalidation overlay onto the painted result.
+ GetPaintController().AppendDebugDrawingAfterCommit(
+ *this, std::move(record),
+ layer_state_ ? &layer_state_->state : nullptr);
+ // Ensure the compositor will raster the under-invalidation overlay.
+ layer_->Layer()->Invalidate();
+ }
+ }
+
+ needs_check_raster_invalidation_ = false;
+ return true;
+}
+
+bool GraphicsLayer::PaintWithoutCommit(
+ const IntRect* interest_rect,
+ GraphicsContext::DisabledMode disabled_mode) {
+ DCHECK(DrawsContent());
+
+ if (client_.ShouldThrottleRendering())
+ return false;
+
+ if (FirstPaintInvalidationTracking::IsEnabled())
+ debug_info_.ClearAnnotatedInvalidateRects();
+ IncrementPaintCount();
+
+ IntRect new_interest_rect;
+ if (!interest_rect) {
+ new_interest_rect =
+ client_.ComputeInterestRect(this, previous_interest_rect_);
+ interest_rect = &new_interest_rect;
+ }
+
+ if (!GetPaintController().SubsequenceCachingIsDisabled() &&
+ !client_.NeedsRepaint(*this) &&
+ !GetPaintController().CacheIsAllInvalid() &&
+ previous_interest_rect_ == *interest_rect) {
+ return false;
+ }
+
+ GraphicsContext context(GetPaintController(), disabled_mode, nullptr);
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ DCHECK(layer_state_) << "No layer state for GraphicsLayer: " << DebugName();
+ GetPaintController().UpdateCurrentPaintChunkProperties(
+ WTF::nullopt, PaintChunkProperties(layer_state_->state));
+ }
+
+ previous_interest_rect_ = *interest_rect;
+ client_.PaintContents(this, context, painting_phase_, *interest_rect);
+ return true;
+}
+
+void GraphicsLayer::UpdateChildList() {
+ WebLayer* child_host = layer_->Layer();
+ child_host->RemoveAllChildren();
+
+ ClearContentsLayerIfUnregistered();
+
+ if (contents_layer_) {
+ // FIXME: Add the contents layer in the correct order with negative z-order
+ // children. This does not currently cause visible rendering issues because
+ // contents layers are only used for replaced elements that don't have
+ // children.
+ child_host->AddChild(contents_layer_);
+ }
+
+ for (size_t i = 0; i < children_.size(); ++i)
+ child_host->AddChild(children_[i]->PlatformLayer());
+
+ for (size_t i = 0; i < link_highlights_.size(); ++i)
+ child_host->AddChild(link_highlights_[i]->Layer());
+}
+
+void GraphicsLayer::UpdateLayerIsDrawable() {
+ // For the rest of the accelerated compositor code, there is no reason to make
+ // a distinction between drawsContent and contentsVisible. So, for
+ // m_layer->layer(), these two flags are combined here. |m_contentsLayer|
+ // shouldn't receive the drawsContent flag, so it is only given
+ // contentsVisible.
+
+ layer_->Layer()->SetDrawsContent(draws_content_ && contents_visible_);
+ if (WebLayer* contents_layer = ContentsLayerIfRegistered())
+ contents_layer->SetDrawsContent(contents_visible_);
+
+ if (draws_content_) {
+ layer_->Layer()->Invalidate();
+ for (size_t i = 0; i < link_highlights_.size(); ++i)
+ link_highlights_[i]->Invalidate();
+ }
+}
+
+void GraphicsLayer::UpdateContentsRect() {
+ WebLayer* contents_layer = ContentsLayerIfRegistered();
+ if (!contents_layer)
+ return;
+
+ contents_layer->SetPosition(
+ FloatPoint(contents_rect_.X(), contents_rect_.Y()));
+ contents_layer->SetBounds(
+ IntSize(contents_rect_.Width(), contents_rect_.Height()));
+
+ if (contents_clipping_mask_layer_) {
+ if (contents_clipping_mask_layer_->Size() != contents_rect_.Size()) {
+ contents_clipping_mask_layer_->SetSize(FloatSize(contents_rect_.Size()));
+ contents_clipping_mask_layer_->SetNeedsDisplay();
+ }
+ contents_clipping_mask_layer_->SetPosition(FloatPoint());
+ contents_clipping_mask_layer_->SetOffsetFromLayoutObject(
+ OffsetFromLayoutObject() +
+ IntSize(contents_rect_.Location().X(), contents_rect_.Location().Y()));
+ }
+}
+
+static HashSet<int>* g_registered_layer_set;
+
+void GraphicsLayer::RegisterContentsLayer(WebLayer* layer) {
+ if (!g_registered_layer_set)
+ g_registered_layer_set = new HashSet<int>;
+ CHECK(!g_registered_layer_set->Contains(layer->Id()));
+ g_registered_layer_set->insert(layer->Id());
+}
+
+void GraphicsLayer::UnregisterContentsLayer(WebLayer* layer) {
+ DCHECK(g_registered_layer_set);
+ CHECK(g_registered_layer_set->Contains(layer->Id()));
+ g_registered_layer_set->erase(layer->Id());
+ layer->SetLayerClient(nullptr);
+}
+
+void GraphicsLayer::SetContentsTo(WebLayer* layer) {
+ bool children_changed = false;
+ if (layer) {
+ DCHECK(g_registered_layer_set);
+ CHECK(g_registered_layer_set->Contains(layer->Id()));
+ if (contents_layer_id_ != layer->Id()) {
+ SetupContentsLayer(layer);
+ children_changed = true;
+ }
+ UpdateContentsRect();
+ } else {
+ if (contents_layer_) {
+ children_changed = true;
+
+ // The old contents layer will be removed via updateChildList.
+ SetContentsLayer(nullptr);
+ }
+ }
+
+ if (children_changed)
+ UpdateChildList();
+}
+
+void GraphicsLayer::SetupContentsLayer(WebLayer* contents_layer) {
+ DCHECK(contents_layer);
+ SetContentsLayer(contents_layer);
+
+ contents_layer_->SetTransformOrigin(FloatPoint3D());
+ contents_layer_->SetUseParentBackfaceVisibility(true);
+
+ // It is necessary to call setDrawsContent as soon as we receive the new
+ // contentsLayer, for the correctness of early exit conditions in
+ // setDrawsContent() and setContentsVisible().
+ contents_layer_->SetDrawsContent(contents_visible_);
+
+ // Insert the content layer first. Video elements require this, because they
+ // have shadow content that must display in front of the video.
+ layer_->Layer()->InsertChild(contents_layer_, 0);
+ WebLayer* border_web_layer =
+ contents_clipping_mask_layer_
+ ? contents_clipping_mask_layer_->PlatformLayer()
+ : nullptr;
+ contents_layer_->SetMaskLayer(border_web_layer);
+
+ contents_layer_->SetRenderingContext(rendering_context3d_);
+}
+
+void GraphicsLayer::ClearContentsLayerIfUnregistered() {
+ if (!contents_layer_id_ ||
+ g_registered_layer_set->Contains(contents_layer_id_))
+ return;
+
+ SetContentsLayer(nullptr);
+}
+
+void GraphicsLayer::SetContentsLayer(WebLayer* contents_layer) {
+ // If we have a previous contents layer which is still registered, then unset
+ // this client pointer. If unregistered, it has already nulled out the client
+ // pointer and may have been deleted.
+ if (contents_layer_ && g_registered_layer_set->Contains(contents_layer_id_))
+ contents_layer_->SetLayerClient(nullptr);
+ contents_layer_ = contents_layer;
+ if (!contents_layer_) {
+ contents_layer_id_ = 0;
+ return;
+ }
+ contents_layer_->SetLayerClient(weak_ptr_factory_.GetWeakPtr());
+ contents_layer_id_ = contents_layer_->Id();
+}
+
+GraphicsLayerDebugInfo& GraphicsLayer::DebugInfo() {
+ return debug_info_;
+}
+
+WebLayer* GraphicsLayer::ContentsLayerIfRegistered() {
+ ClearContentsLayerIfUnregistered();
+ return contents_layer_;
+}
+
+CompositedLayerRasterInvalidator& GraphicsLayer::EnsureRasterInvalidator() {
+ if (!raster_invalidator_) {
+ raster_invalidator_ = std::make_unique<CompositedLayerRasterInvalidator>(
+ [this](const IntRect& r) { SetNeedsDisplayInRectInternal(r); });
+ raster_invalidator_->SetTracksRasterInvalidations(
+ client_.IsTrackingRasterInvalidations());
+ }
+ return *raster_invalidator_;
+}
+
+void GraphicsLayer::UpdateTrackingRasterInvalidations() {
+ bool should_track = client_.IsTrackingRasterInvalidations();
+ if (should_track)
+ EnsureRasterInvalidator().SetTracksRasterInvalidations(true);
+ else if (raster_invalidator_)
+ raster_invalidator_->SetTracksRasterInvalidations(false);
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled() && paint_controller_)
+ paint_controller_->SetTracksRasterInvalidations(should_track);
+}
+
+void GraphicsLayer::ResetTrackedRasterInvalidations() {
+ if (auto* tracking = GetRasterInvalidationTracking())
+ tracking->ClearInvalidations();
+}
+
+bool GraphicsLayer::HasTrackedRasterInvalidations() const {
+ if (auto* tracking = GetRasterInvalidationTracking())
+ return tracking->HasInvalidations();
+ return false;
+}
+
+RasterInvalidationTracking* GraphicsLayer::GetRasterInvalidationTracking()
+ const {
+ return raster_invalidator_ ? raster_invalidator_->GetTracking() : nullptr;
+}
+
+void GraphicsLayer::TrackRasterInvalidation(const DisplayItemClient& client,
+ const IntRect& rect,
+ PaintInvalidationReason reason) {
+ if (FirstPaintInvalidationTracking::IsEnabled())
+ debug_info_.AppendAnnotatedInvalidateRect(rect, reason);
+
+ if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled())
+ EnsureRasterInvalidator().EnsureTracking();
+
+ // For SPv175, this only tracks invalidations that the WebLayer is fully
+ // invalidated directly, e.g. from SetContentsNeedsDisplay(), etc.
+ if (auto* tracking = GetRasterInvalidationTracking())
+ tracking->AddInvalidation(&client, client.DebugName(), rect, reason);
+}
+
+static String PointerAsString(const void* ptr) {
+ TextStream ts;
+ ts << ptr;
+ return ts.Release();
+}
+
+class GraphicsLayer::LayersAsJSONArray {
+ public:
+ LayersAsJSONArray(LayerTreeFlags flags)
+ : flags_(flags),
+ next_transform_id_(1),
+ layers_json_(JSONArray::Create()),
+ transforms_json_(JSONArray::Create()) {}
+
+ // Outputs the layer tree rooted at |layer| as a JSON array, in paint order,
+ // and the transform tree also as a JSON array.
+ std::unique_ptr<JSONObject> operator()(const GraphicsLayer& layer) {
+ auto json = JSONObject::Create();
+ Walk(layer, 0, FloatPoint());
+ json->SetArray("layers", std::move(layers_json_));
+ if (transforms_json_->size())
+ json->SetArray("transforms", std::move(transforms_json_));
+ return json;
+ }
+
+ JSONObject* AddTransformJSON(int& transform_id) {
+ auto transform_json = JSONObject::Create();
+ int parent_transform_id = transform_id;
+ transform_id = next_transform_id_++;
+ transform_json->SetInteger("id", transform_id);
+ if (parent_transform_id)
+ transform_json->SetInteger("parent", parent_transform_id);
+ auto* result = transform_json.get();
+ transforms_json_->PushObject(std::move(transform_json));
+ return result;
+ }
+
+ static FloatPoint ScrollPosition(const GraphicsLayer& layer) {
+ const auto* scrollable_area = layer.GetScrollableArea();
+ if (!RuntimeEnabledFeatures::RootLayerScrollingEnabled()) {
+ // The LayoutView layer's scrollable area is on the "Frame Scrolling
+ // Layer" ancestor.
+ if (layer.DebugName() == "LayoutView #document")
+ scrollable_area = layer.Parent()->Parent()->GetScrollableArea();
+ else if (layer.DebugName() == "Frame Scrolling Layer")
+ scrollable_area = nullptr;
+ }
+ return scrollable_area ? scrollable_area->ScrollPosition() : FloatPoint();
+ }
+
+ void AddLayer(const GraphicsLayer& layer,
+ int& transform_id,
+ FloatPoint& position) {
+ FloatPoint scroll_position = ScrollPosition(layer);
+ if (scroll_position != FloatPoint()) {
+ // Output scroll position as a transform.
+ auto* scroll_translate_json = AddTransformJSON(transform_id);
+ scroll_translate_json->SetArray(
+ "transform", TransformAsJSONArray(TransformationMatrix().Translate(
+ -scroll_position.X(), -scroll_position.Y())));
+ layer.AddFlattenInheritedTransformJSON(*scroll_translate_json);
+ }
+
+ if (!layer.transform_.IsIdentity() || layer.rendering_context3d_ ||
+ layer.GetCompositingReasons() & CompositingReason::k3DTransform) {
+ if (position != FloatPoint()) {
+ // Output position offset as a transform.
+ auto* position_translate_json = AddTransformJSON(transform_id);
+ position_translate_json->SetArray(
+ "transform", TransformAsJSONArray(TransformationMatrix().Translate(
+ position.X(), position.Y())));
+ layer.AddFlattenInheritedTransformJSON(*position_translate_json);
+ if (layer.Parent() && !layer.Parent()->should_flatten_transform_) {
+ position_translate_json->SetBoolean("flattenInheritedTransform",
+ false);
+ }
+ position = FloatPoint();
+ }
+
+ if (!layer.transform_.IsIdentity() || layer.rendering_context3d_) {
+ auto* transform_json = AddTransformJSON(transform_id);
+ layer.AddTransformJSONProperties(*transform_json,
+ rendering_context_map_);
+ }
+ }
+
+ auto json =
+ layer.LayerAsJSONInternal(flags_, rendering_context_map_, position);
+ if (transform_id)
+ json->SetInteger("transform", transform_id);
+ layers_json_->PushObject(std::move(json));
+ }
+
+ void Walk(const GraphicsLayer& layer,
+ int parent_transform_id,
+ const FloatPoint& parent_position) {
+ FloatPoint position = parent_position + layer.position_;
+ int transform_id = parent_transform_id;
+ AddLayer(layer, transform_id, position);
+ for (auto& child : layer.children_)
+ Walk(*child, transform_id, position);
+ }
+
+ private:
+ LayerTreeFlags flags_;
+ int next_transform_id_;
+ RenderingContextMap rendering_context_map_;
+ std::unique_ptr<JSONArray> layers_json_;
+ std::unique_ptr<JSONArray> transforms_json_;
+};
+
+std::unique_ptr<JSONObject> GraphicsLayer::LayerTreeAsJSON(
+ LayerTreeFlags flags) const {
+ if (flags & kOutputAsLayerTree) {
+ RenderingContextMap rendering_context_map;
+ return LayerTreeAsJSONInternal(flags, rendering_context_map);
+ }
+
+ return LayersAsJSONArray(flags)(*this);
+}
+
+// This is the SPv1 version of ContentLayerClientImpl::LayerAsJSON().
+std::unique_ptr<JSONObject> GraphicsLayer::LayerAsJSONInternal(
+ LayerTreeFlags flags,
+ RenderingContextMap& rendering_context_map,
+ const FloatPoint& position) const {
+ std::unique_ptr<JSONObject> json = JSONObject::Create();
+
+ if (flags & kLayerTreeIncludesDebugInfo)
+ json->SetString("this", PointerAsString(this));
+
+ json->SetString("name", DebugName());
+
+ if (position != FloatPoint())
+ json->SetArray("position", PointAsJSONArray(position));
+
+ if (flags & kLayerTreeIncludesDebugInfo &&
+ offset_from_layout_object_ != DoubleSize()) {
+ json->SetArray("offsetFromLayoutObject",
+ SizeAsJSONArray(offset_from_layout_object_));
+ }
+
+ if (size_ != IntSize())
+ json->SetArray("bounds", SizeAsJSONArray(size_));
+
+ if (opacity_ != 1)
+ json->SetDouble("opacity", opacity_);
+
+ if (blend_mode_ != WebBlendMode::kNormal) {
+ json->SetString("blendMode",
+ CompositeOperatorName(kCompositeSourceOver, blend_mode_));
+ }
+
+ if (is_root_for_isolated_group_)
+ json->SetBoolean("isolate", true);
+
+ if (contents_opaque_)
+ json->SetBoolean("contentsOpaque", true);
+
+ if (!draws_content_)
+ json->SetBoolean("drawsContent", false);
+
+ if (!contents_visible_)
+ json->SetBoolean("contentsVisible", false);
+
+ if (!backface_visibility_)
+ json->SetString("backfaceVisibility", "hidden");
+
+ if (flags & kLayerTreeIncludesDebugInfo)
+ json->SetString("client", PointerAsString(&client_));
+
+ if (background_color_.Alpha()) {
+ json->SetString("backgroundColor",
+ background_color_.NameForLayoutTreeAsText());
+ }
+
+ if (flags & kOutputAsLayerTree) {
+ AddTransformJSONProperties(*json, rendering_context_map);
+ if (!should_flatten_transform_)
+ json->SetBoolean("shouldFlattenTransform", false);
+ if (scrollable_area_ &&
+ scrollable_area_->ScrollPosition() != FloatPoint()) {
+ json->SetArray("scrollPosition",
+ PointAsJSONArray(scrollable_area_->ScrollPosition()));
+ }
+ }
+
+ if ((flags & kLayerTreeIncludesPaintInvalidations) &&
+ client_.IsTrackingRasterInvalidations() &&
+ GetRasterInvalidationTracking())
+ GetRasterInvalidationTracking()->AsJSON(json.get());
+
+ if ((flags & kLayerTreeIncludesPaintingPhases) && painting_phase_) {
+ std::unique_ptr<JSONArray> painting_phases_json = JSONArray::Create();
+ if (painting_phase_ & kGraphicsLayerPaintBackground)
+ painting_phases_json->PushString("GraphicsLayerPaintBackground");
+ if (painting_phase_ & kGraphicsLayerPaintForeground)
+ painting_phases_json->PushString("GraphicsLayerPaintForeground");
+ if (painting_phase_ & kGraphicsLayerPaintMask)
+ painting_phases_json->PushString("GraphicsLayerPaintMask");
+ if (painting_phase_ & kGraphicsLayerPaintChildClippingMask)
+ painting_phases_json->PushString("GraphicsLayerPaintChildClippingMask");
+ if (painting_phase_ & kGraphicsLayerPaintAncestorClippingMask)
+ painting_phases_json->PushString(
+ "GraphicsLayerPaintAncestorClippingMask");
+ if (painting_phase_ & kGraphicsLayerPaintOverflowContents)
+ painting_phases_json->PushString("GraphicsLayerPaintOverflowContents");
+ if (painting_phase_ & kGraphicsLayerPaintCompositedScroll)
+ painting_phases_json->PushString("GraphicsLayerPaintCompositedScroll");
+ if (painting_phase_ & kGraphicsLayerPaintDecoration)
+ painting_phases_json->PushString("GraphicsLayerPaintDecoration");
+ json->SetArray("paintingPhases", std::move(painting_phases_json));
+ }
+
+ if (flags & kLayerTreeIncludesClipAndScrollParents) {
+ if (has_scroll_parent_)
+ json->SetBoolean("hasScrollParent", true);
+ if (has_clip_parent_)
+ json->SetBoolean("hasClipParent", true);
+ }
+
+ if (flags &
+ (kLayerTreeIncludesDebugInfo | kLayerTreeIncludesCompositingReasons)) {
+ bool debug = flags & kLayerTreeIncludesDebugInfo;
+ {
+ std::unique_ptr<JSONArray> compositing_reasons_json = JSONArray::Create();
+ auto reasons = debug_info_.GetCompositingReasons();
+ auto names = debug ? CompositingReason::Descriptions(reasons)
+ : CompositingReason::ShortNames(reasons);
+ for (const char* name : names)
+ compositing_reasons_json->PushString(name);
+ json->SetArray("compositingReasons", std::move(compositing_reasons_json));
+ }
+ {
+ std::unique_ptr<JSONArray> squashing_disallowed_reasons_json =
+ JSONArray::Create();
+ auto reasons = debug_info_.GetSquashingDisallowedReasons();
+ auto names = debug ? SquashingDisallowedReason::Descriptions(reasons)
+ : SquashingDisallowedReason::ShortNames(reasons);
+ for (const char* name : names)
+ squashing_disallowed_reasons_json->PushString(name);
+ json->SetArray("squashingDisallowedReasons",
+ std::move(squashing_disallowed_reasons_json));
+ }
+ }
+
+ if (mask_layer_) {
+ std::unique_ptr<JSONArray> mask_layer_json = JSONArray::Create();
+ mask_layer_json->PushObject(mask_layer_->LayerAsJSONInternal(
+ flags, rendering_context_map, mask_layer_->position_));
+ json->SetArray("maskLayer", std::move(mask_layer_json));
+ }
+
+ if (contents_clipping_mask_layer_) {
+ std::unique_ptr<JSONArray> contents_clipping_mask_layer_json =
+ JSONArray::Create();
+ contents_clipping_mask_layer_json->PushObject(
+ contents_clipping_mask_layer_->LayerAsJSONInternal(
+ flags, rendering_context_map,
+ contents_clipping_mask_layer_->position_));
+ json->SetArray("contentsClippingMaskLayer",
+ std::move(contents_clipping_mask_layer_json));
+ }
+
+ if (layer_state_ && (flags & (kLayerTreeIncludesDebugInfo |
+ kLayerTreeIncludesPaintRecords))) {
+ json->SetString("layerState", layer_state_->state.ToString());
+ json->SetValue("layerOffset", PointAsJSONArray(layer_state_->offset));
+ }
+
+#if DCHECK_IS_ON()
+ if (DrawsContent() && (flags & kLayerTreeIncludesPaintRecords))
+ json->SetValue("paintRecord", RecordAsJSON(*CapturePaintRecord()));
+#endif
+
+ return json;
+}
+
+std::unique_ptr<JSONObject> GraphicsLayer::LayerTreeAsJSONInternal(
+ LayerTreeFlags flags,
+ RenderingContextMap& rendering_context_map) const {
+ std::unique_ptr<JSONObject> json =
+ LayerAsJSONInternal(flags, rendering_context_map, position_);
+
+ if (children_.size()) {
+ std::unique_ptr<JSONArray> children_json = JSONArray::Create();
+ for (size_t i = 0; i < children_.size(); i++) {
+ children_json->PushObject(
+ children_[i]->LayerTreeAsJSONInternal(flags, rendering_context_map));
+ }
+ json->SetArray("children", std::move(children_json));
+ }
+
+ return json;
+}
+
+void GraphicsLayer::AddTransformJSONProperties(
+ JSONObject& json,
+ RenderingContextMap& rendering_context_map) const {
+ if (!transform_.IsIdentity())
+ json.SetArray("transform", TransformAsJSONArray(transform_));
+
+ if (!transform_.IsIdentityOrTranslation())
+ json.SetArray("origin", PointAsJSONArray(transform_origin_));
+
+ AddFlattenInheritedTransformJSON(json);
+
+ if (rendering_context3d_) {
+ auto it = rendering_context_map.find(rendering_context3d_);
+ int context_id = rendering_context_map.size() + 1;
+ if (it == rendering_context_map.end())
+ rendering_context_map.Set(rendering_context3d_, context_id);
+ else
+ context_id = it->value;
+
+ json.SetInteger("renderingContext", context_id);
+ }
+}
+
+void GraphicsLayer::AddFlattenInheritedTransformJSON(JSONObject& json) const {
+ if (Parent() && !Parent()->should_flatten_transform_)
+ json.SetBoolean("flattenInheritedTransform", false);
+}
+
+String GraphicsLayer::GetLayerTreeAsTextForTesting(LayerTreeFlags flags) const {
+ return LayerTreeAsJSON(flags)->ToPrettyJSONString();
+}
+
+static const cc::Layer* CcLayerForWebLayer(const WebLayer* web_layer) {
+ return web_layer ? web_layer->CcLayer() : nullptr;
+}
+
+String GraphicsLayer::DebugName(cc::Layer* layer) const {
+ if (layer->id() == contents_layer_id_)
+ return "ContentsLayer for " + client_.DebugName(this);
+
+ for (size_t i = 0; i < link_highlights_.size(); ++i) {
+ if (layer == CcLayerForWebLayer(link_highlights_[i]->Layer())) {
+ return "LinkHighlight[" + String::Number(i) + "] for " +
+ client_.DebugName(this);
+ }
+ }
+
+ if (layer == CcLayerForWebLayer(layer_->Layer()))
+ return client_.DebugName(this);
+
+ NOTREACHED();
+ return "";
+}
+
+void GraphicsLayer::SetCompositingReasons(CompositingReasons reasons) {
+ debug_info_.SetCompositingReasons(reasons);
+}
+
+void GraphicsLayer::SetSquashingDisallowedReasons(
+ SquashingDisallowedReasons reasons) {
+ debug_info_.SetSquashingDisallowedReasons(reasons);
+}
+
+void GraphicsLayer::SetOwnerNodeId(int node_id) {
+ debug_info_.SetOwnerNodeId(node_id);
+}
+
+void GraphicsLayer::SetPosition(const FloatPoint& point) {
+ position_ = point;
+ PlatformLayer()->SetPosition(position_);
+}
+
+void GraphicsLayer::SetSize(const FloatSize& size) {
+ // We are receiving negative sizes here that cause assertions to fail in the
+ // compositor. Clamp them to 0 to avoid those assertions.
+ // FIXME: This should be an DCHECK instead, as negative sizes should not exist
+ // in WebCore.
+ FloatSize clamped_size = size;
+ if (clamped_size.Width() < 0 || clamped_size.Height() < 0)
+ clamped_size = FloatSize();
+
+ if (clamped_size == size_)
+ return;
+
+ size_ = clamped_size;
+
+ // Invalidate the layer as a DisplayItemClient.
+ SetDisplayItemsUncached();
+
+ layer_->Layer()->SetBounds(FlooredIntSize(size_));
+ // Note that we don't resize m_contentsLayer. It's up the caller to do that.
+}
+
+void GraphicsLayer::SetTransform(const TransformationMatrix& transform) {
+ transform_ = transform;
+ PlatformLayer()->SetTransform(TransformationMatrix::ToSkMatrix44(transform_));
+}
+
+void GraphicsLayer::SetTransformOrigin(const FloatPoint3D& transform_origin) {
+ has_transform_origin_ = true;
+ transform_origin_ = transform_origin;
+ PlatformLayer()->SetTransformOrigin(transform_origin);
+}
+
+void GraphicsLayer::SetShouldFlattenTransform(bool should_flatten) {
+ if (should_flatten == should_flatten_transform_)
+ return;
+
+ should_flatten_transform_ = should_flatten;
+
+ layer_->Layer()->SetShouldFlattenTransform(should_flatten);
+}
+
+void GraphicsLayer::SetRenderingContext(int context) {
+ if (rendering_context3d_ == context)
+ return;
+
+ rendering_context3d_ = context;
+ layer_->Layer()->SetRenderingContext(context);
+
+ if (contents_layer_)
+ contents_layer_->SetRenderingContext(rendering_context3d_);
+}
+
+bool GraphicsLayer::MasksToBounds() const {
+ return layer_->Layer()->MasksToBounds();
+}
+
+void GraphicsLayer::SetMasksToBounds(bool masks_to_bounds) {
+ layer_->Layer()->SetMasksToBounds(masks_to_bounds);
+}
+
+void GraphicsLayer::SetDrawsContent(bool draws_content) {
+ // NOTE: This early-exit is only correct because we also properly call
+ // WebLayer::setDrawsContent() whenever |m_contentsLayer| is set to a new
+ // layer in setupContentsLayer().
+ if (draws_content == draws_content_)
+ return;
+
+ draws_content_ = draws_content;
+ UpdateLayerIsDrawable();
+
+ if (!draws_content) {
+ paint_controller_.reset();
+ raster_invalidator_.reset();
+ }
+}
+
+void GraphicsLayer::SetContentsVisible(bool contents_visible) {
+ // NOTE: This early-exit is only correct because we also properly call
+ // WebLayer::setDrawsContent() whenever |m_contentsLayer| is set to a new
+ // layer in setupContentsLayer().
+ if (contents_visible == contents_visible_)
+ return;
+
+ contents_visible_ = contents_visible;
+ UpdateLayerIsDrawable();
+}
+
+void GraphicsLayer::SetClipParent(WebLayer* parent) {
+ has_clip_parent_ = !!parent;
+ layer_->Layer()->SetClipParent(parent);
+}
+
+void GraphicsLayer::SetScrollParent(WebLayer* parent) {
+ has_scroll_parent_ = !!parent;
+ layer_->Layer()->SetScrollParent(parent);
+}
+
+void GraphicsLayer::SetBackgroundColor(const Color& color) {
+ if (color == background_color_)
+ return;
+
+ background_color_ = color;
+ layer_->Layer()->SetBackgroundColor(background_color_.Rgb());
+}
+
+void GraphicsLayer::SetContentsOpaque(bool opaque) {
+ contents_opaque_ = opaque;
+ layer_->Layer()->SetOpaque(contents_opaque_);
+ ClearContentsLayerIfUnregistered();
+ if (contents_layer_)
+ contents_layer_->SetOpaque(opaque);
+}
+
+void GraphicsLayer::SetMaskLayer(GraphicsLayer* mask_layer) {
+ if (mask_layer == mask_layer_)
+ return;
+
+ mask_layer_ = mask_layer;
+ WebLayer* mask_web_layer =
+ mask_layer_ ? mask_layer_->PlatformLayer() : nullptr;
+ layer_->Layer()->SetMaskLayer(mask_web_layer);
+}
+
+void GraphicsLayer::SetContentsClippingMaskLayer(
+ GraphicsLayer* contents_clipping_mask_layer) {
+ if (contents_clipping_mask_layer == contents_clipping_mask_layer_)
+ return;
+
+ contents_clipping_mask_layer_ = contents_clipping_mask_layer;
+ WebLayer* contents_layer = ContentsLayerIfRegistered();
+ if (!contents_layer)
+ return;
+ WebLayer* contents_clipping_mask_web_layer =
+ contents_clipping_mask_layer_
+ ? contents_clipping_mask_layer_->PlatformLayer()
+ : nullptr;
+ contents_layer->SetMaskLayer(contents_clipping_mask_web_layer);
+ UpdateContentsRect();
+}
+
+void GraphicsLayer::SetBackfaceVisibility(bool visible) {
+ backface_visibility_ = visible;
+ PlatformLayer()->SetDoubleSided(backface_visibility_);
+}
+
+void GraphicsLayer::SetOpacity(float opacity) {
+ float clamped_opacity = clampTo(opacity, 0.0f, 1.0f);
+ opacity_ = clamped_opacity;
+ PlatformLayer()->SetOpacity(opacity);
+}
+
+void GraphicsLayer::SetBlendMode(WebBlendMode blend_mode) {
+ if (blend_mode_ == blend_mode)
+ return;
+ blend_mode_ = blend_mode;
+ PlatformLayer()->SetBlendMode(blend_mode);
+}
+
+void GraphicsLayer::SetIsRootForIsolatedGroup(bool isolated) {
+ if (is_root_for_isolated_group_ == isolated)
+ return;
+ is_root_for_isolated_group_ = isolated;
+ PlatformLayer()->SetIsRootForIsolatedGroup(isolated);
+}
+
+void GraphicsLayer::SetHitTestableWithoutDrawsContent(bool should_hit_test) {
+ if (hit_testable_without_draws_content_ == should_hit_test)
+ return;
+ hit_testable_without_draws_content_ = should_hit_test;
+ PlatformLayer()->SetHitTestableWithoutDrawsContent(should_hit_test);
+}
+
+void GraphicsLayer::SetContentsNeedsDisplay() {
+ if (WebLayer* contents_layer = ContentsLayerIfRegistered()) {
+ contents_layer->Invalidate();
+ TrackRasterInvalidation(*this, contents_rect_,
+ PaintInvalidationReason::kFull);
+ }
+}
+
+void GraphicsLayer::SetNeedsDisplay() {
+ if (!DrawsContent())
+ return;
+
+ // TODO(chrishtr): Stop invalidating the rects once
+ // FrameView::paintRecursively() does so.
+ layer_->Layer()->Invalidate();
+ for (size_t i = 0; i < link_highlights_.size(); ++i)
+ link_highlights_[i]->Invalidate();
+ GetPaintController().InvalidateAll();
+
+ TrackRasterInvalidation(*this, IntRect(IntPoint(), ExpandedIntSize(size_)),
+ PaintInvalidationReason::kFull);
+}
+
+DISABLE_CFI_PERF
+void GraphicsLayer::SetNeedsDisplayInRect(
+ const IntRect& rect,
+ PaintInvalidationReason invalidation_reason,
+ const DisplayItemClient& client) {
+ DCHECK(!RuntimeEnabledFeatures::SlimmingPaintV175Enabled());
+ if (!DrawsContent())
+ return;
+
+ if (!ScopedSetNeedsDisplayInRectForTrackingOnly::s_enabled_)
+ SetNeedsDisplayInRectInternal(rect);
+
+ TrackRasterInvalidation(client, rect, invalidation_reason);
+}
+
+void GraphicsLayer::SetNeedsDisplayInRectInternal(const IntRect& rect) {
+ DCHECK(DrawsContent());
+
+ layer_->Layer()->InvalidateRect(rect);
+ for (auto* link_highlight : link_highlights_)
+ link_highlight->Invalidate();
+}
+
+void GraphicsLayer::SetContentsRect(const IntRect& rect) {
+ if (rect == contents_rect_)
+ return;
+
+ contents_rect_ = rect;
+ UpdateContentsRect();
+}
+
+void GraphicsLayer::SetContentsToImage(
+ Image* image,
+ Image::ImageDecodingMode decode_mode,
+ RespectImageOrientationEnum respect_image_orientation) {
+ PaintImage paint_image;
+ if (image)
+ paint_image = image->PaintImageForCurrentFrame();
+
+ ImageOrientation image_orientation = kOriginTopLeft;
+ SkMatrix matrix;
+ if (paint_image && image->IsBitmapImage() &&
+ respect_image_orientation == kRespectImageOrientation) {
+ image_orientation = ToBitmapImage(image)->CurrentFrameOrientation();
+ FloatSize size(paint_image.width(), paint_image.height());
+ if (image_orientation.UsesWidthAsHeight())
+ size = size.TransposedSize();
+ auto affine = image_orientation.TransformFromDefault(size);
+ auto transform = affine.ToTransformationMatrix();
+ matrix = TransformationMatrix::ToSkMatrix44(transform);
+ } else {
+ matrix = SkMatrix::I();
+ }
+
+ if (paint_image) {
+ paint_image =
+ PaintImageBuilder::WithCopy(std::move(paint_image))
+ .set_decoding_mode(Image::ToPaintImageDecodingMode(decode_mode))
+ .TakePaintImage();
+ if (!image_layer_) {
+ image_layer_ =
+ Platform::Current()->CompositorSupport()->CreateImageLayer();
+ RegisterContentsLayer(image_layer_->Layer());
+ }
+ image_layer_->SetImage(std::move(paint_image), matrix,
+ image_orientation.UsesWidthAsHeight());
+ image_layer_->Layer()->SetOpaque(image->CurrentFrameKnownToBeOpaque());
+ UpdateContentsRect();
+ } else if (image_layer_) {
+ UnregisterContentsLayer(image_layer_->Layer());
+ image_layer_.reset();
+ }
+
+ SetContentsTo(image_layer_ ? image_layer_->Layer() : nullptr);
+}
+
+WebLayer* GraphicsLayer::PlatformLayer() const {
+ return layer_->Layer();
+}
+
+void GraphicsLayer::SetFilters(CompositorFilterOperations filters) {
+ PlatformLayer()->SetFilters(filters.ReleaseCcFilterOperations());
+}
+
+void GraphicsLayer::SetBackdropFilters(CompositorFilterOperations filters) {
+ PlatformLayer()->SetBackgroundFilters(filters.ReleaseCcFilterOperations());
+}
+
+void GraphicsLayer::SetStickyPositionConstraint(
+ const WebLayerStickyPositionConstraint& sticky_constraint) {
+ layer_->Layer()->SetStickyPositionConstraint(sticky_constraint);
+}
+
+void GraphicsLayer::SetFilterQuality(SkFilterQuality filter_quality) {
+ if (image_layer_)
+ image_layer_->SetNearestNeighbor(filter_quality == kNone_SkFilterQuality);
+}
+
+void GraphicsLayer::SetPaintingPhase(GraphicsLayerPaintingPhase phase) {
+ if (painting_phase_ == phase)
+ return;
+ painting_phase_ = phase;
+ SetNeedsDisplay();
+}
+
+void GraphicsLayer::AddLinkHighlight(LinkHighlight* link_highlight) {
+ DCHECK(link_highlight && !link_highlights_.Contains(link_highlight));
+ link_highlights_.push_back(link_highlight);
+ link_highlight->Layer()->SetLayerClient(weak_ptr_factory_.GetWeakPtr());
+ UpdateChildList();
+}
+
+void GraphicsLayer::RemoveLinkHighlight(LinkHighlight* link_highlight) {
+ link_highlights_.EraseAt(link_highlights_.Find(link_highlight));
+ UpdateChildList();
+}
+
+void GraphicsLayer::SetScrollableArea(ScrollableArea* scrollable_area) {
+ if (scrollable_area_ == scrollable_area)
+ return;
+ scrollable_area_ = scrollable_area;
+}
+
+std::unique_ptr<base::trace_event::ConvertableToTraceFormat>
+GraphicsLayer::TakeDebugInfo(cc::Layer* layer) {
+ std::unique_ptr<base::trace_event::TracedValue> traced_value(
+ debug_info_.AsTracedValue());
+ traced_value->SetString(
+ "layer_name", WTF::StringUTF8Adaptor(DebugName(layer)).AsStringPiece());
+ return std::move(traced_value);
+}
+
+void GraphicsLayer::didUpdateMainThreadScrollingReasons() {
+ debug_info_.SetMainThreadScrollingReasons(
+ PlatformLayer()->MainThreadScrollingReasons());
+}
+
+void GraphicsLayer::didChangeScrollbarsHiddenIfOverlay(bool hidden) {
+ if (scrollable_area_)
+ scrollable_area_->SetScrollbarsHiddenIfOverlay(hidden);
+}
+
+PaintController& GraphicsLayer::GetPaintController() const {
+ CHECK(DrawsContent());
+ if (!paint_controller_) {
+ paint_controller_ = PaintController::Create();
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ paint_controller_->SetTracksRasterInvalidations(
+ client_.IsTrackingRasterInvalidations());
+ }
+ }
+ return *paint_controller_;
+}
+
+void GraphicsLayer::SetElementId(const CompositorElementId& id) {
+ if (WebLayer* layer = PlatformLayer())
+ layer->SetElementId(id);
+}
+
+CompositorElementId GraphicsLayer::GetElementId() const {
+ if (WebLayer* layer = PlatformLayer())
+ return layer->GetElementId();
+ return CompositorElementId();
+}
+
+sk_sp<PaintRecord> GraphicsLayer::CapturePaintRecord() const {
+ DCHECK(DrawsContent());
+
+ if (client_.ShouldThrottleRendering())
+ return sk_sp<PaintRecord>(new PaintRecord);
+
+ FloatRect bounds(IntRect(IntPoint(0, 0), ExpandedIntSize(Size())));
+ GraphicsContext graphics_context(GetPaintController());
+ graphics_context.BeginRecording(bounds);
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ DCHECK(layer_state_) << "No layer state for GraphicsLayer: " << DebugName();
+ GetPaintController().GetPaintArtifact().Replay(
+ graphics_context, layer_state_->state, layer_state_->offset);
+ } else {
+ GetPaintController().GetPaintArtifact().Replay(graphics_context,
+ PropertyTreeState::Root());
+ }
+ return graphics_context.EndRecording();
+}
+
+void GraphicsLayer::SetLayerState(const PropertyTreeState& layer_state,
+ const IntPoint& layer_offset) {
+ DCHECK(RuntimeEnabledFeatures::SlimmingPaintV175Enabled());
+
+ if (!layer_state_) {
+ layer_state_ =
+ std::make_unique<LayerState>(LayerState{layer_state, layer_offset});
+ return;
+ }
+ layer_state_->state = layer_state;
+ layer_state_->offset = layer_offset;
+}
+
+void GraphicsLayer::PaintContents(WebDisplayItemList* web_display_item_list,
+ PaintingControlSetting painting_control) {
+ TRACE_EVENT0("blink,benchmark", "GraphicsLayer::PaintContents");
+
+ PaintController& paint_controller = GetPaintController();
+ paint_controller.SetDisplayItemConstructionIsDisabled(
+ painting_control == kDisplayListConstructionDisabled);
+ paint_controller.SetSubsequenceCachingIsDisabled(painting_control ==
+ kSubsequenceCachingDisabled);
+
+ if (painting_control == kPartialInvalidation)
+ client_.InvalidateTargetElementForTesting();
+
+ // We also disable caching when Painting or Construction are disabled. In both
+ // cases we would like to compare assuming the full cost of recording, not the
+ // cost of re-using cached content.
+ if (painting_control == kDisplayListCachingDisabled ||
+ painting_control == kDisplayListPaintingDisabled ||
+ painting_control == kDisplayListConstructionDisabled)
+ paint_controller.InvalidateAll();
+
+ GraphicsContext::DisabledMode disabled_mode =
+ GraphicsContext::kNothingDisabled;
+ if (painting_control == kDisplayListPaintingDisabled ||
+ painting_control == kDisplayListConstructionDisabled)
+ disabled_mode = GraphicsContext::kFullyDisabled;
+
+ // Anything other than PaintDefaultBehavior is for testing. In non-testing
+ // scenarios, it is an error to call GraphicsLayer::Paint. Actual painting
+ // occurs in LocalFrameView::PaintTree() which calls GraphicsLayer::Paint();
+ // this method merely copies the painted output to the WebDisplayItemList.
+ if (painting_control != kPaintDefaultBehavior)
+ Paint(nullptr, disabled_mode);
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ DCHECK(layer_state_) << "No layer state for GraphicsLayer: " << DebugName();
+ PaintChunksToCcLayer::ConvertInto(
+ GetPaintController().GetPaintArtifact().PaintChunks(),
+ layer_state_->state,
+ gfx::Vector2dF(layer_state_->offset.X(), layer_state_->offset.Y()),
+ VisualRectSubpixelOffset(),
+ paint_controller.GetPaintArtifact().GetDisplayItemList(),
+ *web_display_item_list->GetCcDisplayItemList());
+ } else {
+ paint_controller.GetPaintArtifact().AppendToWebDisplayItemList(
+ FloatSize(OffsetFromLayoutObjectWithSubpixelAccumulation()),
+ web_display_item_list);
+ }
+
+ paint_controller.SetDisplayItemConstructionIsDisabled(false);
+ paint_controller.SetSubsequenceCachingIsDisabled(false);
+}
+
+size_t GraphicsLayer::ApproximateUnsharedMemoryUsage() const {
+ size_t result = sizeof(*this);
+ result += GetPaintController().ApproximateUnsharedMemoryUsage();
+ if (raster_invalidator_)
+ result += raster_invalidator_->ApproximateUnsharedMemoryUsage();
+ return result;
+}
+
+// Subpixel offset for visual rects which excluded composited layer's subpixel
+// accumulation during paint invalidation.
+// See PaintInvalidator::ExcludeCompositedLayerSubpixelAccumulation().
+FloatSize GraphicsLayer::VisualRectSubpixelOffset() const {
+ if (GetCompositingReasons() & CompositingReason::kComboAllDirectReasons)
+ return FloatSize(client_.SubpixelAccumulation());
+ return FloatSize();
+}
+
+bool ScopedSetNeedsDisplayInRectForTrackingOnly::s_enabled_ = false;
+
+} // namespace blink
+
+#if DCHECK_IS_ON()
+void showGraphicsLayerTree(const blink::GraphicsLayer* layer) {
+ if (!layer) {
+ LOG(ERROR) << "Cannot showGraphicsLayerTree for (nil).";
+ return;
+ }
+
+ String output = layer->GetLayerTreeAsTextForTesting(0xffffffff);
+ LOG(ERROR) << output.Utf8().data();
+}
+
+void showGraphicsLayers(const blink::GraphicsLayer* layer) {
+ if (!layer) {
+ LOG(ERROR) << "Cannot showGraphicsLayers for (nil).";
+ return;
+ }
+
+ String output = layer->GetLayerTreeAsTextForTesting(
+ 0xffffffff & ~blink::kOutputAsLayerTree);
+ LOG(ERROR) << output.Utf8().data();
+}
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/graphics/graphics_layer.h b/chromium/third_party/blink/renderer/platform/graphics/graphics_layer.h
new file mode 100644
index 00000000000..2e9d925ea52
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/graphics_layer.h
@@ -0,0 +1,467 @@
+/*
+ * Copyright (C) 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2013 Intel Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GRAPHICS_LAYER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GRAPHICS_LAYER_H_
+
+#include <memory>
+#include "base/memory/weak_ptr.h"
+#include "cc/layers/layer_client.h"
+#include "third_party/blink/public/platform/web_content_layer.h"
+#include "third_party/blink/public/platform/web_content_layer_client.h"
+#include "third_party/blink/public/platform/web_image_layer.h"
+#include "third_party/blink/public/platform/web_layer_sticky_position_constraint.h"
+#include "third_party/blink/public/platform/web_overscroll_behavior.h"
+#include "third_party/blink/renderer/platform/geometry/float_point.h"
+#include "third_party/blink/renderer/platform/geometry/float_point_3d.h"
+#include "third_party/blink/renderer/platform/geometry/float_size.h"
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/graphics/color.h"
+#include "third_party/blink/renderer/platform/graphics/compositor_element_id.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_layer_client.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_layer_debug_info.h"
+#include "third_party/blink/renderer/platform/graphics/image_orientation.h"
+#include "third_party/blink/renderer/platform/graphics/paint/display_item_client.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
+#include "third_party/blink/renderer/platform/graphics/paint_invalidation_reason.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/transforms/transformation_matrix.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "third_party/skia/include/core/SkFilterQuality.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+namespace blink {
+
+class CompositorFilterOperations;
+class CompositedLayerRasterInvalidator;
+class Image;
+class JSONObject;
+class LinkHighlight;
+class PaintController;
+class RasterInvalidationTracking;
+class ScrollableArea;
+class WebLayer;
+
+typedef Vector<GraphicsLayer*, 64> GraphicsLayerVector;
+
+// GraphicsLayer is an abstraction for a rendering surface with backing store,
+// which may have associated transformation and animations.
+class PLATFORM_EXPORT GraphicsLayer : public cc::LayerClient,
+ public DisplayItemClient,
+ private WebContentLayerClient {
+ WTF_MAKE_NONCOPYABLE(GraphicsLayer);
+ USING_FAST_MALLOC(GraphicsLayer);
+
+ public:
+ static std::unique_ptr<GraphicsLayer> Create(GraphicsLayerClient&);
+
+ ~GraphicsLayer() override;
+
+ GraphicsLayerClient& Client() const { return client_; }
+
+ GraphicsLayerDebugInfo& DebugInfo();
+
+ void SetCompositingReasons(CompositingReasons);
+ CompositingReasons GetCompositingReasons() const {
+ return debug_info_.GetCompositingReasons();
+ }
+ void SetSquashingDisallowedReasons(SquashingDisallowedReasons);
+ void SetOwnerNodeId(int);
+
+ GraphicsLayer* Parent() const { return parent_; }
+ void SetParent(GraphicsLayer*); // Internal use only.
+
+ const Vector<GraphicsLayer*>& Children() const { return children_; }
+ // Returns true if the child list changed.
+ bool SetChildren(const GraphicsLayerVector&);
+
+ // Add child layers. If the child is already parented, it will be removed from
+ // its old parent.
+ void AddChild(GraphicsLayer*);
+ void AddChildBelow(GraphicsLayer*, GraphicsLayer* sibling);
+
+ void RemoveAllChildren();
+ void RemoveFromParent();
+
+ GraphicsLayer* MaskLayer() const { return mask_layer_; }
+ void SetMaskLayer(GraphicsLayer*);
+
+ GraphicsLayer* ContentsClippingMaskLayer() const {
+ return contents_clipping_mask_layer_;
+ }
+ void SetContentsClippingMaskLayer(GraphicsLayer*);
+
+ enum ShouldSetNeedsDisplay { kDontSetNeedsDisplay, kSetNeedsDisplay };
+
+ // The offset is the origin of the layoutObject minus the origin of the
+ // graphics layer (so either zero or negative).
+ IntSize OffsetFromLayoutObject() const {
+ return FlooredIntSize(offset_from_layout_object_);
+ }
+ void SetOffsetFromLayoutObject(const IntSize&,
+ ShouldSetNeedsDisplay = kSetNeedsDisplay);
+ LayoutSize OffsetFromLayoutObjectWithSubpixelAccumulation() const;
+
+ // The double version is only used in updateScrollingLayerGeometry() for
+ // detecting a scroll offset change at floating point precision.
+ DoubleSize OffsetDoubleFromLayoutObject() const {
+ return offset_from_layout_object_;
+ }
+ void SetOffsetDoubleFromLayoutObject(
+ const DoubleSize&,
+ ShouldSetNeedsDisplay = kSetNeedsDisplay);
+
+ // The position of the layer (the location of its top-left corner in its
+ // parent).
+ const FloatPoint& GetPosition() const { return position_; }
+ void SetPosition(const FloatPoint&);
+
+ const FloatPoint3D& TransformOrigin() const { return transform_origin_; }
+ void SetTransformOrigin(const FloatPoint3D&);
+
+ // The size of the layer.
+ const FloatSize& Size() const { return size_; }
+ void SetSize(const FloatSize&);
+
+ const TransformationMatrix& Transform() const { return transform_; }
+ void SetTransform(const TransformationMatrix&);
+ void SetShouldFlattenTransform(bool);
+ void SetRenderingContext(int id);
+
+ bool MasksToBounds() const;
+ void SetMasksToBounds(bool);
+
+ bool DrawsContent() const { return draws_content_; }
+ void SetDrawsContent(bool);
+
+ bool ContentsAreVisible() const { return contents_visible_; }
+ void SetContentsVisible(bool);
+
+ void SetScrollParent(WebLayer*);
+ void SetClipParent(WebLayer*);
+
+ // For special cases, e.g. drawing missing tiles on Android.
+ // The compositor should never paint this color in normal cases because the
+ // Layer will paint the background by itself.
+ Color BackgroundColor() const { return background_color_; }
+ void SetBackgroundColor(const Color&);
+
+ // opaque means that we know the layer contents have no alpha
+ bool ContentsOpaque() const { return contents_opaque_; }
+ void SetContentsOpaque(bool);
+
+ bool BackfaceVisibility() const { return backface_visibility_; }
+ void SetBackfaceVisibility(bool visible);
+
+ float Opacity() const { return opacity_; }
+ void SetOpacity(float);
+
+ void SetBlendMode(WebBlendMode);
+ void SetIsRootForIsolatedGroup(bool);
+
+ void SetHitTestableWithoutDrawsContent(bool);
+ bool GetHitTestableWithoutDrawsContentForTesting() {
+ return hit_testable_without_draws_content_;
+ }
+
+ void SetFilters(CompositorFilterOperations);
+ void SetBackdropFilters(CompositorFilterOperations);
+
+ void SetStickyPositionConstraint(const WebLayerStickyPositionConstraint&);
+
+ void SetFilterQuality(SkFilterQuality);
+
+ // Some GraphicsLayers paint only the foreground or the background content
+ GraphicsLayerPaintingPhase PaintingPhase() const { return painting_phase_; }
+ void SetPaintingPhase(GraphicsLayerPaintingPhase);
+
+ void SetNeedsDisplay();
+ // Mark the given rect (in layer coords) as needing display. Never goes deep.
+ void SetNeedsDisplayInRect(const IntRect&,
+ PaintInvalidationReason,
+ const DisplayItemClient&);
+
+ void SetContentsNeedsDisplay();
+
+ // Set that the position/size of the contents (image or video).
+ void SetContentsRect(const IntRect&);
+
+ // Layer contents
+ void SetContentsToImage(
+ Image*,
+ Image::ImageDecodingMode decode_mode,
+ RespectImageOrientationEnum = kDoNotRespectImageOrientation);
+ void SetContentsToPlatformLayer(WebLayer* layer) { SetContentsTo(layer); }
+ bool HasContentsLayer() const { return contents_layer_; }
+
+ // For hosting this GraphicsLayer in a native layer hierarchy.
+ WebLayer* PlatformLayer() const;
+
+ int PaintCount() const { return paint_count_; }
+
+ // Return a string with a human readable form of the layer tree. If debug is
+ // true, pointers for the layers and timing data will be included in the
+ // returned string.
+ String GetLayerTreeAsTextForTesting(LayerTreeFlags = kLayerTreeNormal) const;
+
+ std::unique_ptr<JSONObject> LayerTreeAsJSON(LayerTreeFlags) const;
+
+ void UpdateTrackingRasterInvalidations();
+ void ResetTrackedRasterInvalidations();
+ bool HasTrackedRasterInvalidations() const;
+ RasterInvalidationTracking* GetRasterInvalidationTracking() const;
+ void TrackRasterInvalidation(const DisplayItemClient&,
+ const IntRect&,
+ PaintInvalidationReason);
+
+ void AddLinkHighlight(LinkHighlight*);
+ void RemoveLinkHighlight(LinkHighlight*);
+ // Exposed for tests
+ unsigned NumLinkHighlights() { return link_highlights_.size(); }
+ LinkHighlight* GetLinkHighlight(int i) { return link_highlights_[i]; }
+
+ void SetScrollableArea(ScrollableArea*);
+ ScrollableArea* GetScrollableArea() const { return scrollable_area_; }
+
+ WebContentLayer* ContentLayer() const { return layer_.get(); }
+
+ static void RegisterContentsLayer(WebLayer*);
+ static void UnregisterContentsLayer(WebLayer*);
+
+ IntRect InterestRect();
+ void PaintRecursively();
+ // Returns true if this layer is repainted.
+ bool Paint(const IntRect* interest_rect,
+ GraphicsContext::DisabledMode = GraphicsContext::kNothingDisabled);
+
+ // cc::LayerClient implementation.
+ std::unique_ptr<base::trace_event::ConvertableToTraceFormat> TakeDebugInfo(
+ cc::Layer*) override;
+ void didUpdateMainThreadScrollingReasons() override;
+ void didChangeScrollbarsHiddenIfOverlay(bool) override;
+
+ PaintController& GetPaintController() const;
+
+ // Exposed for tests.
+ WebLayer* ContentsLayer() const { return contents_layer_; }
+
+ void SetElementId(const CompositorElementId&);
+ CompositorElementId GetElementId() const;
+
+ WebContentLayerClient& WebContentLayerClientForTesting() { return *this; }
+
+ // DisplayItemClient methods
+ String DebugName() const final { return client_.DebugName(this); }
+ LayoutRect VisualRect() const override;
+
+ void SetHasWillChangeTransformHint(bool);
+
+ void SetOverscrollBehavior(const WebOverscrollBehavior&);
+
+ void SetSnapContainerData(Optional<cc::SnapContainerData>);
+
+ void SetIsResizedByBrowserControls(bool);
+ void SetIsContainerForFixedPositionLayers(bool);
+
+ void SetLayerState(const PropertyTreeState&, const IntPoint& layer_offset);
+
+ // Capture the last painted result into a PaintRecord. This GraphicsLayer
+ // must DrawsContent. The result is never nullptr.
+ sk_sp<PaintRecord> CapturePaintRecord() const;
+
+ void SetNeedsCheckRasterInvalidation() {
+ needs_check_raster_invalidation_ = true;
+ }
+
+ protected:
+ String DebugName(cc::Layer*) const;
+ bool ShouldFlattenTransform() const { return should_flatten_transform_; }
+
+ explicit GraphicsLayer(GraphicsLayerClient&);
+
+ friend class CompositedLayerMappingTest;
+ friend class PaintControllerPaintTestBase;
+ friend class GraphicsLayerTest;
+
+ private:
+ // WebContentLayerClient implementation.
+ gfx::Rect PaintableRegion() final { return InterestRect(); }
+ void PaintContents(WebDisplayItemList*,
+ PaintingControlSetting = kPaintDefaultBehavior) final;
+ size_t ApproximateUnsharedMemoryUsage() const final;
+
+ void PaintRecursivelyInternal(Vector<GraphicsLayer*>& repainted_layers);
+
+ // Returns true if PaintController::paintArtifact() changed and needs commit.
+ bool PaintWithoutCommit(
+ const IntRect* interest_rect,
+ GraphicsContext::DisabledMode = GraphicsContext::kNothingDisabled);
+
+ // Adds a child without calling updateChildList(), so that adding children
+ // can be batched before updating.
+ void AddChildInternal(GraphicsLayer*);
+
+#if DCHECK_IS_ON()
+ bool HasAncestor(GraphicsLayer*) const;
+#endif
+
+ void IncrementPaintCount() { ++paint_count_; }
+
+ // Helper functions used by settors to keep layer's the state consistent.
+ void UpdateChildList();
+ void UpdateLayerIsDrawable();
+ void UpdateContentsRect();
+
+ void SetContentsTo(WebLayer*);
+ void SetupContentsLayer(WebLayer*);
+ void ClearContentsLayerIfUnregistered();
+ WebLayer* ContentsLayerIfRegistered();
+ void SetContentsLayer(WebLayer*);
+
+ typedef HashMap<int, int> RenderingContextMap;
+ std::unique_ptr<JSONObject> LayerTreeAsJSONInternal(
+ LayerTreeFlags,
+ RenderingContextMap&) const;
+ std::unique_ptr<JSONObject> LayerAsJSONInternal(
+ LayerTreeFlags,
+ RenderingContextMap&,
+ const FloatPoint& position) const;
+ void AddTransformJSONProperties(JSONObject&, RenderingContextMap&) const;
+ void AddFlattenInheritedTransformJSON(JSONObject&) const;
+ class LayersAsJSONArray;
+
+ CompositedLayerRasterInvalidator& EnsureRasterInvalidator();
+ void SetNeedsDisplayInRectInternal(const IntRect&);
+
+ FloatSize VisualRectSubpixelOffset() const;
+
+ GraphicsLayerClient& client_;
+
+ // Offset from the owning layoutObject
+ DoubleSize offset_from_layout_object_;
+
+ // Position is relative to the parent GraphicsLayer
+ FloatPoint position_;
+ FloatSize size_;
+
+ TransformationMatrix transform_;
+ FloatPoint3D transform_origin_;
+
+ Color background_color_;
+ float opacity_;
+
+ WebBlendMode blend_mode_;
+
+ bool has_transform_origin_ : 1;
+ bool contents_opaque_ : 1;
+ bool should_flatten_transform_ : 1;
+ bool backface_visibility_ : 1;
+ bool draws_content_ : 1;
+ bool contents_visible_ : 1;
+ bool is_root_for_isolated_group_ : 1;
+ bool hit_testable_without_draws_content_ : 1;
+ bool needs_check_raster_invalidation_ : 1;
+
+ bool has_scroll_parent_ : 1;
+ bool has_clip_parent_ : 1;
+
+ bool painted_ : 1;
+
+ GraphicsLayerPaintingPhase painting_phase_;
+
+ Vector<GraphicsLayer*> children_;
+ GraphicsLayer* parent_;
+
+ GraphicsLayer* mask_layer_; // Reference to mask layer. We don't own this.
+ GraphicsLayer* contents_clipping_mask_layer_; // Reference to clipping mask
+ // layer. We don't own this.
+
+ IntRect contents_rect_;
+
+ int paint_count_;
+
+ std::unique_ptr<WebContentLayer> layer_;
+ std::unique_ptr<WebImageLayer> image_layer_;
+ WebLayer* contents_layer_;
+ // We don't have ownership of m_contentsLayer, but we do want to know if a
+ // given layer is the same as our current layer in setContentsTo(). Since
+ // |m_contentsLayer| may be deleted at this point, we stash an ID away when we
+ // know |m_contentsLayer| is alive and use that for comparisons from that
+ // point on.
+ int contents_layer_id_;
+
+ Vector<LinkHighlight*> link_highlights_;
+
+ WeakPersistent<ScrollableArea> scrollable_area_;
+ GraphicsLayerDebugInfo debug_info_;
+ int rendering_context3d_;
+
+ mutable std::unique_ptr<PaintController> paint_controller_;
+
+ IntRect previous_interest_rect_;
+
+ struct LayerState {
+ PropertyTreeState state;
+ IntPoint offset;
+ };
+ std::unique_ptr<LayerState> layer_state_;
+
+ std::unique_ptr<CompositedLayerRasterInvalidator> raster_invalidator_;
+
+ base::WeakPtrFactory<GraphicsLayer> weak_ptr_factory_;
+
+ FRIEND_TEST_ALL_PREFIXES(CompositingLayerPropertyUpdaterTest, MaskLayerState);
+};
+
+// ObjectPaintInvalidatorWithContext::InvalidatePaintRectangleWithContext uses
+// this to reduce differences between layout test results of SPv1 and SPv2.
+class PLATFORM_EXPORT ScopedSetNeedsDisplayInRectForTrackingOnly {
+ public:
+ ScopedSetNeedsDisplayInRectForTrackingOnly() {
+ DCHECK(!s_enabled_);
+ s_enabled_ = true;
+ }
+ ~ScopedSetNeedsDisplayInRectForTrackingOnly() {
+ DCHECK(s_enabled_);
+ s_enabled_ = false;
+ }
+
+ private:
+ friend class GraphicsLayer;
+ static bool s_enabled_;
+};
+
+} // namespace blink
+
+#if DCHECK_IS_ON()
+// Outside the blink namespace for ease of invocation from gdb.
+void PLATFORM_EXPORT showGraphicsLayerTree(const blink::GraphicsLayer*);
+void PLATFORM_EXPORT showGraphicsLayers(const blink::GraphicsLayer*);
+#endif
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GRAPHICS_LAYER_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/graphics_layer_client.h b/chromium/third_party/blink/renderer/platform/graphics/graphics_layer_client.h
new file mode 100644
index 00000000000..11e38dc4927
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/graphics_layer_client.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2009 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GRAPHICS_LAYER_CLIENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GRAPHICS_LAYER_CLIENT_H_
+
+#include "third_party/blink/renderer/platform/geometry/layout_size.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class GraphicsContext;
+class GraphicsLayer;
+class IntRect;
+
+enum GraphicsLayerPaintingPhaseFlags {
+ kGraphicsLayerPaintBackground = (1 << 0),
+ kGraphicsLayerPaintForeground = (1 << 1),
+ kGraphicsLayerPaintMask = (1 << 2),
+ kGraphicsLayerPaintOverflowContents = (1 << 3),
+ kGraphicsLayerPaintCompositedScroll = (1 << 4),
+ kGraphicsLayerPaintChildClippingMask = (1 << 5),
+ kGraphicsLayerPaintAncestorClippingMask = (1 << 6),
+ kGraphicsLayerPaintDecoration = (1 << 7),
+ kGraphicsLayerPaintAllWithOverflowClip =
+ (kGraphicsLayerPaintBackground | kGraphicsLayerPaintForeground |
+ kGraphicsLayerPaintMask |
+ kGraphicsLayerPaintDecoration)
+};
+typedef unsigned GraphicsLayerPaintingPhase;
+
+// These values need to be kept consistent with the layer tree flags in
+// core/testing/Internals.idl.
+enum {
+ kLayerTreeNormal = 0,
+ // Dump extra debugging info like layer addresses.
+ kLayerTreeIncludesDebugInfo = 1 << 0,
+ kLayerTreeIncludesPaintInvalidations = 1 << 1,
+ kLayerTreeIncludesPaintingPhases = 1 << 2,
+ kLayerTreeIncludesRootLayer = 1 << 3,
+ kLayerTreeIncludesClipAndScrollParents = 1 << 4,
+ kLayerTreeIncludesCompositingReasons = 1 << 5,
+ kLayerTreeIncludesPaintRecords = 1 << 6,
+ // Outputs all layers as a layer tree. The default is output children
+ // (excluding the root) as a layer list, in paint (preorder) order.
+ kOutputAsLayerTree = 0x4000,
+};
+typedef unsigned LayerTreeFlags;
+
+class PLATFORM_EXPORT GraphicsLayerClient {
+ public:
+ virtual ~GraphicsLayerClient() = default;
+
+ virtual void InvalidateTargetElementForTesting() {}
+
+ virtual IntRect ComputeInterestRect(
+ const GraphicsLayer*,
+ const IntRect& previous_interest_rect) const = 0;
+ virtual LayoutSize SubpixelAccumulation() const { return LayoutSize(); }
+ // Returns whether the client needs to be repainted with respect to the given
+ // graphics layer.
+ virtual bool NeedsRepaint(const GraphicsLayer&) const = 0;
+ virtual void PaintContents(const GraphicsLayer*,
+ GraphicsContext&,
+ GraphicsLayerPaintingPhase,
+ const IntRect& interest_rect) const = 0;
+
+ // Returns true if the GraphicsLayer is under a frame that should not render
+ // (see LocalFrameView::ShouldThrottleRendering()).
+ virtual bool ShouldThrottleRendering() const { return false; }
+
+ virtual bool IsTrackingRasterInvalidations() const { return false; }
+
+ virtual String DebugName(const GraphicsLayer*) const = 0;
+
+#if DCHECK_IS_ON()
+ // CompositedLayerMapping overrides this to verify that it is not
+ // currently painting contents. An ASSERT fails, if it is.
+ // This is executed in GraphicsLayer construction and destruction
+ // to verify that we don't create or destroy GraphicsLayers
+ // while painting.
+ virtual void VerifyNotPainting() {}
+#endif
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GRAPHICS_LAYER_CLIENT_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/graphics_layer_debug_info.cc b/chromium/third_party/blink/renderer/platform/graphics/graphics_layer_debug_info.cc
new file mode 100644
index 00000000000..1f38d00b074
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/graphics_layer_debug_info.cc
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2014 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "third_party/blink/renderer/platform/graphics/graphics_layer_debug_info.h"
+
+#include "base/trace_event/trace_event_argument.h"
+#include "third_party/blink/renderer/platform/scroll/main_thread_scrolling_reason.h"
+
+namespace blink {
+
+GraphicsLayerDebugInfo::GraphicsLayerDebugInfo()
+ : compositing_reasons_(CompositingReason::kNone),
+ squashing_disallowed_reasons_(SquashingDisallowedReason::kNone),
+ owner_node_id_(0),
+ main_thread_scrolling_reasons_(0) {}
+
+GraphicsLayerDebugInfo::~GraphicsLayerDebugInfo() = default;
+
+std::unique_ptr<base::trace_event::TracedValue>
+GraphicsLayerDebugInfo::AsTracedValue() const {
+ std::unique_ptr<base::trace_event::TracedValue> traced_value(
+ new base::trace_event::TracedValue());
+ AppendAnnotatedInvalidateRects(traced_value.get());
+ AppendCompositingReasons(traced_value.get());
+ AppendSquashingDisallowedReasons(traced_value.get());
+ AppendOwnerNodeId(traced_value.get());
+ AppendMainThreadScrollingReasons(traced_value.get());
+ return traced_value;
+}
+
+void GraphicsLayerDebugInfo::AppendAnnotatedInvalidateRects(
+ base::trace_event::TracedValue* traced_value) const {
+ traced_value->BeginArray("annotated_invalidation_rects");
+ for (const auto& annotated_rect : previous_invalidations_) {
+ const FloatRect& rect = annotated_rect.rect;
+ traced_value->BeginDictionary();
+ traced_value->BeginArray("geometry_rect");
+ traced_value->AppendDouble(rect.X());
+ traced_value->AppendDouble(rect.Y());
+ traced_value->AppendDouble(rect.Width());
+ traced_value->AppendDouble(rect.Height());
+ traced_value->EndArray();
+ traced_value->SetString(
+ "reason", PaintInvalidationReasonToString(annotated_rect.reason));
+ traced_value->EndDictionary();
+ }
+ traced_value->EndArray();
+}
+
+void GraphicsLayerDebugInfo::AppendCompositingReasons(
+ base::trace_event::TracedValue* traced_value) const {
+ traced_value->BeginArray("compositing_reasons");
+ for (const char* description :
+ CompositingReason::Descriptions(compositing_reasons_))
+ traced_value->AppendString(description);
+ traced_value->EndArray();
+}
+
+void GraphicsLayerDebugInfo::AppendSquashingDisallowedReasons(
+ base::trace_event::TracedValue* traced_value) const {
+ traced_value->BeginArray("squashing_disallowed_reasons");
+ for (const char* description :
+ SquashingDisallowedReason::Descriptions(squashing_disallowed_reasons_))
+ traced_value->AppendString(description);
+ traced_value->EndArray();
+}
+
+void GraphicsLayerDebugInfo::AppendOwnerNodeId(
+ base::trace_event::TracedValue* traced_value) const {
+ if (!owner_node_id_)
+ return;
+
+ traced_value->SetInteger("owner_node", owner_node_id_);
+}
+
+void GraphicsLayerDebugInfo::AppendAnnotatedInvalidateRect(
+ const FloatRect& rect,
+ PaintInvalidationReason invalidation_reason) {
+ AnnotatedInvalidationRect annotated_rect = {rect, invalidation_reason};
+ invalidations_.push_back(annotated_rect);
+}
+
+void GraphicsLayerDebugInfo::ClearAnnotatedInvalidateRects() {
+ previous_invalidations_.clear();
+ previous_invalidations_.swap(invalidations_);
+}
+
+void GraphicsLayerDebugInfo::AppendMainThreadScrollingReasons(
+ base::trace_event::TracedValue* traced_value) const {
+ MainThreadScrollingReason::mainThreadScrollingReasonsAsTracedValue(
+ main_thread_scrolling_reasons_, traced_value);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/graphics_layer_debug_info.h b/chromium/third_party/blink/renderer/platform/graphics/graphics_layer_debug_info.h
new file mode 100644
index 00000000000..575e9469580
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/graphics_layer_debug_info.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2014 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GRAPHICS_LAYER_DEBUG_INFO_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GRAPHICS_LAYER_DEBUG_INFO_H_
+
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/graphics/compositing_reasons.h"
+#include "third_party/blink/renderer/platform/graphics/paint_invalidation_reason.h"
+#include "third_party/blink/renderer/platform/graphics/squashing_disallowed_reasons.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+#include <memory>
+
+namespace base {
+namespace trace_event {
+class TracedValue;
+}
+}
+
+namespace blink {
+
+class GraphicsLayerDebugInfo final {
+ DISALLOW_NEW();
+ WTF_MAKE_NONCOPYABLE(GraphicsLayerDebugInfo);
+
+ public:
+ GraphicsLayerDebugInfo();
+ ~GraphicsLayerDebugInfo();
+
+ std::unique_ptr<base::trace_event::TracedValue> AsTracedValue() const;
+
+ CompositingReasons GetCompositingReasons() const {
+ return compositing_reasons_;
+ }
+ void SetCompositingReasons(CompositingReasons reasons) {
+ compositing_reasons_ = reasons;
+ }
+
+ SquashingDisallowedReasons GetSquashingDisallowedReasons() const {
+ return squashing_disallowed_reasons_;
+ }
+ void SetSquashingDisallowedReasons(SquashingDisallowedReasons reasons) {
+ squashing_disallowed_reasons_ = reasons;
+ }
+ void SetOwnerNodeId(int id) { owner_node_id_ = id; }
+
+ void AppendAnnotatedInvalidateRect(const FloatRect&, PaintInvalidationReason);
+ void ClearAnnotatedInvalidateRects();
+
+ uint32_t GetMainThreadScrollingReasons() const {
+ return main_thread_scrolling_reasons_;
+ }
+ void SetMainThreadScrollingReasons(uint32_t reasons) {
+ main_thread_scrolling_reasons_ = reasons;
+ }
+
+ private:
+ void AppendAnnotatedInvalidateRects(base::trace_event::TracedValue*) const;
+ void AppendCompositingReasons(base::trace_event::TracedValue*) const;
+ void AppendSquashingDisallowedReasons(base::trace_event::TracedValue*) const;
+ void AppendOwnerNodeId(base::trace_event::TracedValue*) const;
+ void AppendMainThreadScrollingReasons(base::trace_event::TracedValue*) const;
+
+ struct AnnotatedInvalidationRect {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ FloatRect rect;
+ PaintInvalidationReason reason;
+ };
+
+ CompositingReasons compositing_reasons_;
+ SquashingDisallowedReasons squashing_disallowed_reasons_;
+ int owner_node_id_;
+ Vector<AnnotatedInvalidationRect> invalidations_;
+ Vector<AnnotatedInvalidationRect> previous_invalidations_;
+ uint32_t main_thread_scrolling_reasons_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/graphics/graphics_layer_test.cc b/chromium/third_party/blink/renderer/platform/graphics/graphics_layer_test.cc
new file mode 100644
index 00000000000..c7dfe1e0d57
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/graphics_layer_test.cc
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2012 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/graphics/graphics_layer.h"
+
+#include <memory>
+#include <utility>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_compositor_support.h"
+#include "third_party/blink/public/platform/web_layer.h"
+#include "third_party/blink/public/platform/web_layer_tree_view.h"
+#include "third_party/blink/public/platform/web_thread.h"
+#include "third_party/blink/renderer/platform/animation/compositor_animation.h"
+#include "third_party/blink/renderer/platform/animation/compositor_animation_client.h"
+#include "third_party/blink/renderer/platform/animation/compositor_animation_host.h"
+#include "third_party/blink/renderer/platform/animation/compositor_animation_timeline.h"
+#include "third_party/blink/renderer/platform/animation/compositor_float_animation_curve.h"
+#include "third_party/blink/renderer/platform/animation/compositor_keyframe_model.h"
+#include "third_party/blink/renderer/platform/animation/compositor_target_property.h"
+#include "third_party/blink/renderer/platform/graphics/compositor_element_id.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_controller_test.h"
+#include "third_party/blink/renderer/platform/graphics/paint/property_tree_state.h"
+#include "third_party/blink/renderer/platform/graphics/paint/scoped_paint_chunk_properties.h"
+#include "third_party/blink/renderer/platform/graphics/test/fake_scrollable_area.h"
+#include "third_party/blink/renderer/platform/scheduler/child/web_scheduler.h"
+#include "third_party/blink/renderer/platform/scroll/scrollable_area.h"
+#include "third_party/blink/renderer/platform/testing/fake_graphics_layer.h"
+#include "third_party/blink/renderer/platform/testing/fake_graphics_layer_client.h"
+#include "third_party/blink/renderer/platform/testing/paint_test_configurations.h"
+#include "third_party/blink/renderer/platform/testing/web_layer_tree_view_impl_for_testing.h"
+#include "third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.h"
+#include "third_party/blink/renderer/platform/transforms/rotate_transform_operation.h"
+#include "third_party/blink/renderer/platform/transforms/translate_transform_operation.h"
+
+namespace blink {
+
+class GraphicsLayerTest : public testing::Test, public PaintTestConfigurations {
+ public:
+ GraphicsLayerTest() {
+ clip_layer_ = std::make_unique<FakeGraphicsLayer>(client_);
+ scroll_elasticity_layer_ = std::make_unique<FakeGraphicsLayer>(client_);
+ page_scale_layer_ = std::make_unique<FakeGraphicsLayer>(client_);
+ graphics_layer_ = std::make_unique<FakeGraphicsLayer>(client_);
+ graphics_layer_->SetDrawsContent(true);
+ clip_layer_->AddChild(scroll_elasticity_layer_.get());
+ scroll_elasticity_layer_->AddChild(page_scale_layer_.get());
+ page_scale_layer_->AddChild(graphics_layer_.get());
+ graphics_layer_->PlatformLayer()->SetScrollable(
+ clip_layer_->PlatformLayer()->Bounds());
+ platform_layer_ = graphics_layer_->PlatformLayer();
+ layer_tree_view_ = std::make_unique<WebLayerTreeViewImplForTesting>();
+ DCHECK(layer_tree_view_);
+ layer_tree_view_->SetRootLayer(*clip_layer_->PlatformLayer());
+ WebLayerTreeView::ViewportLayers viewport_layers;
+ viewport_layers.overscroll_elasticity =
+ scroll_elasticity_layer_->PlatformLayer();
+ viewport_layers.page_scale = page_scale_layer_->PlatformLayer();
+ viewport_layers.inner_viewport_container = clip_layer_->PlatformLayer();
+ viewport_layers.inner_viewport_scroll = graphics_layer_->PlatformLayer();
+ layer_tree_view_->RegisterViewportLayers(viewport_layers);
+ layer_tree_view_->SetViewportSize(WebSize(1, 1));
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ graphics_layer_->SetLayerState(
+ PropertyTreeState(PropertyTreeState::Root()), IntPoint());
+ }
+ }
+
+ ~GraphicsLayerTest() override {
+ graphics_layer_.reset();
+ layer_tree_view_.reset();
+ }
+
+ WebLayerTreeView* LayerTreeView() { return layer_tree_view_.get(); }
+
+ protected:
+ bool PaintWithoutCommit(GraphicsLayer& layer, const IntRect* interest_rect) {
+ return layer.PaintWithoutCommit(interest_rect);
+ }
+
+ const CompositedLayerRasterInvalidator* GetInternalRasterInvalidator(
+ const GraphicsLayer& layer) {
+ return layer.raster_invalidator_.get();
+ }
+
+ CompositedLayerRasterInvalidator& EnsureRasterInvalidator(
+ GraphicsLayer& layer) {
+ return layer.EnsureRasterInvalidator();
+ }
+
+ const PaintController* GetInternalPaintController(
+ const GraphicsLayer& layer) {
+ return layer.paint_controller_.get();
+ }
+
+ WebLayer* platform_layer_;
+ std::unique_ptr<FakeGraphicsLayer> graphics_layer_;
+ std::unique_ptr<FakeGraphicsLayer> page_scale_layer_;
+ std::unique_ptr<FakeGraphicsLayer> scroll_elasticity_layer_;
+ std::unique_ptr<FakeGraphicsLayer> clip_layer_;
+ FakeGraphicsLayerClient client_;
+
+ private:
+ std::unique_ptr<WebLayerTreeViewImplForTesting> layer_tree_view_;
+};
+
+INSTANTIATE_TEST_CASE_P(All,
+ GraphicsLayerTest,
+ testing::Values(0, kSlimmingPaintV175));
+
+class AnimationForTesting : public CompositorAnimationClient {
+ public:
+ AnimationForTesting() {
+ compositor_animation_ = CompositorAnimation::Create();
+ }
+
+ CompositorAnimation* GetCompositorAnimation() const override {
+ return compositor_animation_.get();
+ }
+
+ std::unique_ptr<CompositorAnimation> compositor_animation_;
+};
+
+TEST_P(GraphicsLayerTest, updateLayerShouldFlattenTransformWithAnimations) {
+ ASSERT_FALSE(platform_layer_->HasTickingAnimationForTesting());
+
+ std::unique_ptr<CompositorFloatAnimationCurve> curve =
+ CompositorFloatAnimationCurve::Create();
+ curve->AddKeyframe(
+ CompositorFloatKeyframe(0.0, 0.0,
+ *CubicBezierTimingFunction::Preset(
+ CubicBezierTimingFunction::EaseType::EASE)));
+ std::unique_ptr<CompositorKeyframeModel> float_keyframe_model(
+ CompositorKeyframeModel::Create(*curve, CompositorTargetProperty::OPACITY,
+ 0, 0));
+ int keyframe_model_id = float_keyframe_model->Id();
+
+ std::unique_ptr<CompositorAnimationTimeline> compositor_timeline =
+ CompositorAnimationTimeline::Create();
+ AnimationForTesting animation;
+
+ CompositorAnimationHost host(LayerTreeView()->CompositorAnimationHost());
+
+ host.AddTimeline(*compositor_timeline);
+ compositor_timeline->AnimationAttached(animation);
+
+ platform_layer_->SetElementId(CompositorElementId(platform_layer_->Id()));
+
+ animation.GetCompositorAnimation()->AttachElement(
+ platform_layer_->GetElementId());
+ ASSERT_TRUE(animation.GetCompositorAnimation()->IsElementAttached());
+
+ animation.GetCompositorAnimation()->AddKeyframeModel(
+ std::move(float_keyframe_model));
+
+ ASSERT_TRUE(platform_layer_->HasTickingAnimationForTesting());
+
+ graphics_layer_->SetShouldFlattenTransform(false);
+
+ platform_layer_ = graphics_layer_->PlatformLayer();
+ ASSERT_TRUE(platform_layer_);
+
+ ASSERT_TRUE(platform_layer_->HasTickingAnimationForTesting());
+ animation.GetCompositorAnimation()->RemoveKeyframeModel(keyframe_model_id);
+ ASSERT_FALSE(platform_layer_->HasTickingAnimationForTesting());
+
+ graphics_layer_->SetShouldFlattenTransform(true);
+
+ platform_layer_ = graphics_layer_->PlatformLayer();
+ ASSERT_TRUE(platform_layer_);
+
+ ASSERT_FALSE(platform_layer_->HasTickingAnimationForTesting());
+
+ animation.GetCompositorAnimation()->DetachElement();
+ ASSERT_FALSE(animation.GetCompositorAnimation()->IsElementAttached());
+
+ compositor_timeline->AnimationDestroyed(animation);
+ host.RemoveTimeline(*compositor_timeline.get());
+}
+
+TEST_P(GraphicsLayerTest, Paint) {
+ IntRect interest_rect(1, 2, 3, 4);
+ EXPECT_TRUE(PaintWithoutCommit(*graphics_layer_, &interest_rect));
+ graphics_layer_->GetPaintController().CommitNewDisplayItems();
+
+ client_.SetNeedsRepaint(true);
+ EXPECT_TRUE(PaintWithoutCommit(*graphics_layer_, &interest_rect));
+ graphics_layer_->GetPaintController().CommitNewDisplayItems();
+
+ client_.SetNeedsRepaint(false);
+ EXPECT_FALSE(PaintWithoutCommit(*graphics_layer_, &interest_rect));
+
+ interest_rect.Move(IntSize(10, 20));
+ EXPECT_TRUE(PaintWithoutCommit(*graphics_layer_, &interest_rect));
+ graphics_layer_->GetPaintController().CommitNewDisplayItems();
+ EXPECT_FALSE(PaintWithoutCommit(*graphics_layer_, &interest_rect));
+
+ graphics_layer_->SetNeedsDisplay();
+ EXPECT_TRUE(PaintWithoutCommit(*graphics_layer_, &interest_rect));
+ graphics_layer_->GetPaintController().CommitNewDisplayItems();
+ EXPECT_FALSE(PaintWithoutCommit(*graphics_layer_, &interest_rect));
+}
+
+TEST_P(GraphicsLayerTest, PaintRecursively) {
+ if (!RuntimeEnabledFeatures::SlimmingPaintV175Enabled())
+ return;
+
+ IntRect interest_rect(1, 2, 3, 4);
+ auto transform_root = TransformPaintPropertyNode::Root();
+ auto transform1 = TransformPaintPropertyNode::Create(
+ transform_root, TransformationMatrix().Translate(10, 20), FloatPoint3D());
+ auto transform2 = TransformPaintPropertyNode::Create(
+ transform1, TransformationMatrix().Scale(2), FloatPoint3D());
+
+ client_.SetPainter([&](const GraphicsLayer* layer, GraphicsContext& context,
+ GraphicsLayerPaintingPhase, const IntRect&) {
+ {
+ ScopedPaintChunkProperties properties(
+ context.GetPaintController(), transform1, *layer, kBackgroundType);
+ PaintControllerTestBase::DrawRect(context, *layer, kBackgroundType,
+ interest_rect);
+ }
+ {
+ ScopedPaintChunkProperties properties(
+ context.GetPaintController(), transform2, *layer, kForegroundType);
+ PaintControllerTestBase::DrawRect(context, *layer, kForegroundType,
+ interest_rect);
+ }
+ });
+
+ transform1->Update(transform_root, TransformationMatrix().Translate(20, 30),
+ FloatPoint3D());
+ EXPECT_TRUE(transform1->Changed(*transform_root));
+ EXPECT_TRUE(transform2->Changed(*transform_root));
+ client_.SetNeedsRepaint(true);
+ graphics_layer_->PaintRecursively();
+
+ EXPECT_FALSE(transform1->Changed(*transform_root));
+ EXPECT_FALSE(transform2->Changed(*transform_root));
+}
+
+TEST_P(GraphicsLayerTest, SetDrawsContentFalse) {
+ EXPECT_TRUE(graphics_layer_->DrawsContent());
+ graphics_layer_->GetPaintController();
+ EXPECT_NE(nullptr, GetInternalPaintController(*graphics_layer_));
+ EnsureRasterInvalidator(*graphics_layer_);
+ EXPECT_NE(nullptr, GetInternalRasterInvalidator(*graphics_layer_));
+
+ graphics_layer_->SetDrawsContent(false);
+ EXPECT_EQ(nullptr, GetInternalPaintController(*graphics_layer_));
+ EXPECT_EQ(nullptr, GetInternalRasterInvalidator(*graphics_layer_));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/graphics_types.cc b/chromium/third_party/blink/renderer/platform/graphics/graphics_types.cc
new file mode 100644
index 00000000000..01cb7157199
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/graphics_types.cc
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2012 Rik Cabanier (cabanier@adobe.com)
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/graphics/graphics_types.h"
+
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+static const char* const kCompositeOperatorNames[] = {"clear",
+ "copy",
+ "source-over",
+ "source-in",
+ "source-out",
+ "source-atop",
+ "destination-over",
+ "destination-in",
+ "destination-out",
+ "destination-atop",
+ "xor",
+ "lighter"};
+
+static const char* const kBlendOperatorNames[] = {
+ "normal", "multiply", "screen", "overlay",
+ "darken", "lighten", "color-dodge", "color-burn",
+ "hard-light", "soft-light", "difference", "exclusion",
+ "hue", "saturation", "color", "luminosity"};
+const int kNumCompositeOperatorNames =
+ WTF_ARRAY_LENGTH(kCompositeOperatorNames);
+const int kNumBlendOperatorNames = WTF_ARRAY_LENGTH(kBlendOperatorNames);
+
+bool ParseCompositeAndBlendOperator(const String& s,
+ CompositeOperator& op,
+ WebBlendMode& blend_op) {
+ for (int i = 0; i < kNumCompositeOperatorNames; i++) {
+ if (s == kCompositeOperatorNames[i]) {
+ op = static_cast<CompositeOperator>(i);
+ blend_op = WebBlendMode::kNormal;
+ return true;
+ }
+ }
+
+ for (int i = 0; i < kNumBlendOperatorNames; i++) {
+ if (s == kBlendOperatorNames[i]) {
+ blend_op = static_cast<WebBlendMode>(i);
+ op = kCompositeSourceOver;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+String CompositeOperatorName(CompositeOperator op, WebBlendMode blend_op) {
+ DCHECK_GE(op, 0);
+ DCHECK_LT(op, kNumCompositeOperatorNames);
+ DCHECK_GE(static_cast<unsigned>(blend_op), 0u);
+ if (blend_op != WebBlendMode::kNormal)
+ return kBlendOperatorNames[static_cast<unsigned>(blend_op)];
+ return kCompositeOperatorNames[op];
+}
+
+bool ParseLineCap(const String& s, LineCap& cap) {
+ if (s == "butt") {
+ cap = kButtCap;
+ return true;
+ }
+ if (s == "round") {
+ cap = kRoundCap;
+ return true;
+ }
+ if (s == "square") {
+ cap = kSquareCap;
+ return true;
+ }
+ return false;
+}
+
+String LineCapName(LineCap cap) {
+ DCHECK_GE(cap, 0);
+ DCHECK_LT(cap, 3);
+ const char* const kNames[3] = {"butt", "round", "square"};
+ return kNames[cap];
+}
+
+bool ParseLineJoin(const String& s, LineJoin& join) {
+ if (s == "miter") {
+ join = kMiterJoin;
+ return true;
+ }
+ if (s == "round") {
+ join = kRoundJoin;
+ return true;
+ }
+ if (s == "bevel") {
+ join = kBevelJoin;
+ return true;
+ }
+ return false;
+}
+
+String LineJoinName(LineJoin join) {
+ DCHECK_GE(join, 0);
+ DCHECK_LT(join, 3);
+ const char* const kNames[3] = {"miter", "round", "bevel"};
+ return kNames[join];
+}
+
+String TextAlignName(TextAlign align) {
+ DCHECK_GE(align, 0);
+ DCHECK_LT(align, 5);
+ const char* const kNames[5] = {"start", "end", "left", "center", "right"};
+ return kNames[align];
+}
+
+bool ParseTextAlign(const String& s, TextAlign& align) {
+ if (s == "start") {
+ align = kStartTextAlign;
+ return true;
+ }
+ if (s == "end") {
+ align = kEndTextAlign;
+ return true;
+ }
+ if (s == "left") {
+ align = kLeftTextAlign;
+ return true;
+ }
+ if (s == "center") {
+ align = kCenterTextAlign;
+ return true;
+ }
+ if (s == "right") {
+ align = kRightTextAlign;
+ return true;
+ }
+ return false;
+}
+
+String TextBaselineName(TextBaseline baseline) {
+ DCHECK_GE(baseline, 0);
+ DCHECK_LT(baseline, 6);
+ const char* const kNames[6] = {"alphabetic", "top", "middle",
+ "bottom", "ideographic", "hanging"};
+ return kNames[baseline];
+}
+
+bool ParseTextBaseline(const String& s, TextBaseline& baseline) {
+ if (s == "alphabetic") {
+ baseline = kAlphabeticTextBaseline;
+ return true;
+ }
+ if (s == "top") {
+ baseline = kTopTextBaseline;
+ return true;
+ }
+ if (s == "middle") {
+ baseline = kMiddleTextBaseline;
+ return true;
+ }
+ if (s == "bottom") {
+ baseline = kBottomTextBaseline;
+ return true;
+ }
+ if (s == "ideographic") {
+ baseline = kIdeographicTextBaseline;
+ return true;
+ }
+ if (s == "hanging") {
+ baseline = kHangingTextBaseline;
+ return true;
+ }
+ return false;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/graphics_types.h b/chromium/third_party/blink/renderer/platform/graphics/graphics_types.h
new file mode 100644
index 00000000000..bec1ca3fc7f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/graphics_types.h
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2004, 2005, 2006 Apple Computer, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GRAPHICS_TYPES_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GRAPHICS_TYPES_H_
+
+#include "third_party/blink/public/platform/web_blend_mode.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/skia/include/core/SkFilterQuality.h"
+#include "third_party/skia/include/core/SkPaint.h"
+#include "third_party/skia/include/core/SkPath.h"
+
+namespace blink {
+
+enum AlphaDisposition {
+ kPremultiplyAlpha,
+ kUnpremultiplyAlpha,
+ kDontChangeAlpha,
+};
+
+enum DataU8ColorType {
+ kRGBAColorType,
+ kN32ColorType,
+};
+
+enum ImageDataStorageFormat {
+ kUint8ClampedArrayStorageFormat,
+ kUint16ArrayStorageFormat,
+ kFloat32ArrayStorageFormat,
+};
+
+enum StrokeStyle {
+ kNoStroke,
+ kSolidStroke,
+ kDottedStroke,
+ kDashedStroke,
+ kDoubleStroke,
+ kWavyStroke,
+};
+
+enum InterpolationQuality {
+ kInterpolationNone = kNone_SkFilterQuality,
+ kInterpolationLow = kLow_SkFilterQuality,
+ kInterpolationMedium = kMedium_SkFilterQuality,
+#if defined(WTF_USE_LOW_QUALITY_IMAGE_INTERPOLATION)
+ kInterpolationDefault = kInterpolationLow,
+#else
+ kInterpolationDefault = kInterpolationMedium,
+#endif
+};
+
+enum CompositeOperator {
+ kCompositeClear,
+ kCompositeCopy,
+ kCompositeSourceOver,
+ kCompositeSourceIn,
+ kCompositeSourceOut,
+ kCompositeSourceAtop,
+ kCompositeDestinationOver,
+ kCompositeDestinationIn,
+ kCompositeDestinationOut,
+ kCompositeDestinationAtop,
+ kCompositeXOR,
+ kCompositePlusLighter
+};
+
+enum OpacityMode {
+ kNonOpaque,
+ kOpaque,
+};
+
+enum AccelerationHint {
+ kPreferAcceleration,
+ // The PreferAccelerationAfterVisibilityChange hint suggests we should switch
+ // back to acceleration in the context of the canvas becoming visible again.
+ kPreferAccelerationAfterVisibilityChange,
+ kPreferNoAcceleration,
+};
+
+// Note: enum used directly for histogram, values must not change
+enum DisableDeferralReason {
+ kDisableDeferralReasonUnknown =
+ 0, // Should not appear in production histograms
+ kDisableDeferralReasonExpensiveOverdrawHeuristic = 1,
+ kDisableDeferralReasonUsingTextureBackedPattern = 2,
+ kDisableDeferralReasonDrawImageOfVideo = 3,
+ kDisableDeferralReasonDrawImageOfAnimated2dCanvas = 4,
+ kDisableDeferralReasonSubPixelTextAntiAliasingSupport = 5,
+ kDisableDeferralDrawImageWithTextureBackedSourceImage = 6,
+ kDisableDeferralReasonLowEndDevice = 7,
+ kDisableDeferralReasonCount,
+};
+
+enum MailboxSyncMode {
+ kVerifiedSyncToken,
+ kUnverifiedSyncToken,
+ kOrderingBarrier,
+};
+
+enum HighContrastClassification {
+ kNotClassified,
+ kApplyHighContrastFilter,
+ kDoNotApplyHighContrastFilter,
+};
+
+// TODO(junov): crbug.com/453113 Relocate ShadowMode to
+// CanvasRenderingContext2DState.h once GraphicsContext no longer uses it.
+enum ShadowMode {
+ kDrawShadowAndForeground,
+ kDrawShadowOnly,
+ kDrawForegroundOnly
+};
+
+enum AntiAliasingMode { kNotAntiAliased, kAntiAliased };
+
+enum GradientSpreadMethod {
+ kSpreadMethodPad,
+ kSpreadMethodReflect,
+ kSpreadMethodRepeat
+};
+
+enum LineCap {
+ kButtCap = SkPaint::kButt_Cap,
+ kRoundCap = SkPaint::kRound_Cap,
+ kSquareCap = SkPaint::kSquare_Cap
+};
+
+enum LineJoin {
+ kMiterJoin = SkPaint::kMiter_Join,
+ kRoundJoin = SkPaint::kRound_Join,
+ kBevelJoin = SkPaint::kBevel_Join
+};
+
+enum HorizontalAlignment { kAlignLeft, kAlignRight, kAlignHCenter };
+
+enum TextBaseline {
+ kAlphabeticTextBaseline,
+ kTopTextBaseline,
+ kMiddleTextBaseline,
+ kBottomTextBaseline,
+ kIdeographicTextBaseline,
+ kHangingTextBaseline
+};
+
+enum TextAlign {
+ kStartTextAlign,
+ kEndTextAlign,
+ kLeftTextAlign,
+ kCenterTextAlign,
+ kRightTextAlign
+};
+
+enum TextDrawingMode {
+ kTextModeFill = 1 << 0,
+ kTextModeStroke = 1 << 1,
+};
+typedef unsigned TextDrawingModeFlags;
+
+enum ColorFilter {
+ kColorFilterNone,
+ kColorFilterLuminanceToAlpha,
+ kColorFilterSRGBToLinearRGB,
+ kColorFilterLinearRGBToSRGB
+};
+
+enum WindRule {
+ RULE_NONZERO = SkPath::kWinding_FillType,
+ RULE_EVENODD = SkPath::kEvenOdd_FillType
+};
+
+PLATFORM_EXPORT String CompositeOperatorName(CompositeOperator, WebBlendMode);
+PLATFORM_EXPORT bool ParseCompositeAndBlendOperator(const String&,
+ CompositeOperator&,
+ WebBlendMode&);
+
+PLATFORM_EXPORT String LineCapName(LineCap);
+PLATFORM_EXPORT bool ParseLineCap(const String&, LineCap&);
+
+PLATFORM_EXPORT String LineJoinName(LineJoin);
+PLATFORM_EXPORT bool ParseLineJoin(const String&, LineJoin&);
+
+PLATFORM_EXPORT String TextAlignName(TextAlign);
+PLATFORM_EXPORT bool ParseTextAlign(const String&, TextAlign&);
+
+PLATFORM_EXPORT String TextBaselineName(TextBaseline);
+PLATFORM_EXPORT bool ParseTextBaseline(const String&, TextBaseline&);
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/graphics/graphics_types_3d.h b/chromium/third_party/blink/renderer/platform/graphics/graphics_types_3d.h
new file mode 100644
index 00000000000..fa1539bfac5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/graphics_types_3d.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GRAPHICS_TYPES_3D_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GRAPHICS_TYPES_3D_H_
+
+#ifndef __glext_h_
+#include "third_party/khronos/GLES3/gl3.h"
+#include "third_party/khronos/GLES2/gl2ext.h"
+#endif
+
+#include <stdint.h>
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+// WebGL-specific enums
+const unsigned GC3D_UNPACK_FLIP_Y_WEBGL = 0x9240;
+const unsigned GC3D_UNPACK_PREMULTIPLY_ALPHA_WEBGL = 0x9241;
+const unsigned GC3D_CONTEXT_LOST_WEBGL = 0x9242;
+const unsigned GC3D_UNPACK_COLORSPACE_CONVERSION_WEBGL = 0x9243;
+const unsigned GC3D_BROWSER_DEFAULT_WEBGL = 0x9244;
+const unsigned GC3D_MAX_CLIENT_WAIT_TIMEOUT_WEBGL = 0x9247;
+
+// GL_ARB_texture_rectangle
+const unsigned GC3D_TEXTURE_RECTANGLE_ARB = 0x84F5;
+
+// GL_AMD_compressed_ATC_texture
+const unsigned GC3D_COMPRESSED_ATC_RGB_AMD = 0x8C92;
+const unsigned GC3D_COMPRESSED_ATC_RGBA_EXPLICIT_ALPHA_AMD = 0x8C93;
+const unsigned GC3D_COMPRESSED_ATC_RGBA_INTERPOLATED_ALPHA_AMD = 0x87EE;
+
+namespace blink {
+
+enum SourceDrawingBuffer { kFrontBuffer, kBackBuffer };
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GRAPHICS_TYPES_3D_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/high_contrast_image_classifier.cc b/chromium/third_party/blink/renderer/platform/graphics/high_contrast_image_classifier.cc
new file mode 100644
index 00000000000..61fe5aabaa6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/high_contrast_image_classifier.cc
@@ -0,0 +1,366 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/high_contrast_image_classifier.h"
+
+#include "base/rand_util.h"
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/graphics/highcontrast/highcontrast_classifier.h"
+#include "third_party/skia/include/utils/SkNullCanvas.h"
+
+namespace {
+
+bool IsColorGray(const SkColor& color) {
+ return abs(static_cast<int>(SkColorGetR(color)) -
+ static_cast<int>(SkColorGetG(color))) +
+ abs(static_cast<int>(SkColorGetG(color)) -
+ static_cast<int>(SkColorGetB(color))) <=
+ 8;
+}
+
+void RGBAdd(const SkColor& new_pixel, SkColor4f* sink) {
+ sink->fR += SkColorGetR(new_pixel);
+ sink->fG += SkColorGetG(new_pixel);
+ sink->fB += SkColorGetB(new_pixel);
+}
+
+void RGBDivide(SkColor4f* sink, int divisor) {
+ sink->fR /= divisor;
+ sink->fG /= divisor;
+ sink->fB /= divisor;
+}
+
+float ColorDifference(const SkColor& color1, const SkColor4f& color2) {
+ return sqrt((pow(static_cast<float>(SkColorGetR(color1)) - color2.fR, 2.0) +
+ pow(static_cast<float>(SkColorGetG(color1)) - color2.fG, 2.0) +
+ pow(static_cast<float>(SkColorGetB(color1)) - color2.fB, 2.0)) /
+ 3.0);
+}
+
+const int kPixelsToSample = 1000;
+const int kBlocksCount1D = 10;
+const int kMinImageSizeForClassification1D = 24;
+
+// Decision tree lower and upper thresholds for grayscale and color images.
+const float kLowColorCountThreshold[2] = {0.8125, 0.015137};
+const float kHighColorCountThreshold[2] = {1, 0.025635};
+
+} // namespace
+
+namespace blink {
+
+HighContrastImageClassifier::HighContrastImageClassifier()
+ : use_testing_random_generator_(false), testing_random_generator_seed_(0) {}
+
+int HighContrastImageClassifier::GetRandomInt(const int min, const int max) {
+ if (use_testing_random_generator_) {
+ testing_random_generator_seed_ *= 7;
+ testing_random_generator_seed_ += 15485863;
+ testing_random_generator_seed_ %= 256205689;
+ return min + testing_random_generator_seed_ % (max - min);
+ }
+
+ return base::RandInt(min, max - 1);
+}
+
+bool HighContrastImageClassifier::ShouldApplyHighContrastFilterToImage(
+ Image& image) {
+ HighContrastClassification result = image.GetHighContrastClassification();
+ if (result != HighContrastClassification::kNotClassified)
+ return result == HighContrastClassification::kApplyHighContrastFilter;
+
+ if (image.width() < kMinImageSizeForClassification1D ||
+ image.height() < kMinImageSizeForClassification1D) {
+ result = HighContrastClassification::kApplyHighContrastFilter;
+ } else {
+ std::vector<float> features;
+ if (!ComputeImageFeatures(image, &features))
+ result = HighContrastClassification::kDoNotApplyHighContrastFilter;
+ else
+ result = ClassifyImage(features);
+ }
+
+ image.SetHighContrastClassification(result);
+ return result == HighContrastClassification::kApplyHighContrastFilter;
+}
+
+// This function computes a single feature vector based on a sample set of image
+// pixels. Please refer to |GetSamples| function for description of the sampling
+// method, and |GetFeatures| function for description of the features.
+bool HighContrastImageClassifier::ComputeImageFeatures(
+ Image& image,
+ std::vector<float>* features) {
+ SkBitmap bitmap;
+ if (!GetBitmap(image, &bitmap))
+ return false;
+
+ if (use_testing_random_generator_)
+ testing_random_generator_seed_ = 0;
+
+ std::vector<SkColor> sampled_pixels;
+ float transparency_ratio;
+ float background_ratio;
+ GetSamples(bitmap, &sampled_pixels, &transparency_ratio, &background_ratio);
+
+ GetFeatures(sampled_pixels, transparency_ratio, background_ratio, features);
+ return true;
+}
+
+bool HighContrastImageClassifier::GetBitmap(Image& image, SkBitmap* bitmap) {
+ if (!image.IsBitmapImage() || !image.width() || !image.height())
+ return false;
+
+ bitmap->allocPixels(
+ SkImageInfo::MakeN32(image.width(), image.height(), kPremul_SkAlphaType));
+ SkCanvas canvas(*bitmap);
+ canvas.clear(SK_ColorTRANSPARENT);
+ canvas.drawImageRect(image.PaintImageForCurrentFrame().GetSkImage(),
+ SkRect::MakeIWH(image.width(), image.height()), nullptr);
+ return true;
+}
+
+// Extracts sample pixels from the image, focusing on the foreground and
+// ignoring static background. The image is separated into uniformly distributed
+// blocks through its width and height, each block is sampled, and checked to
+// see if it seem to be background or foreground (See IsBlockBackground for
+// details). Samples from foreground blocks are collected, and if they are less
+// than 50% of requested samples, the foreground blocks are resampled to get
+// more points.
+void HighContrastImageClassifier::GetSamples(
+ const SkBitmap& bitmap,
+ std::vector<SkColor>* sampled_pixels,
+ float* transparency_ratio,
+ float* background_ratio) {
+ int pixels_per_block = kPixelsToSample / (kBlocksCount1D * kBlocksCount1D);
+
+ int transparent_pixels = 0;
+ int opaque_pixels = 0;
+ int blocks_count = 0;
+
+ std::vector<int> horizontal_grid(kBlocksCount1D + 1);
+ std::vector<int> vertical_grid(kBlocksCount1D + 1);
+ for (int block = 0; block <= kBlocksCount1D; block++) {
+ horizontal_grid[block] = static_cast<int>(
+ round(block * bitmap.width() / static_cast<float>(kBlocksCount1D)));
+ vertical_grid[block] = static_cast<int>(
+ round(block * bitmap.height() / static_cast<float>(kBlocksCount1D)));
+ }
+
+ sampled_pixels->clear();
+ std::vector<IntRect> foreground_blocks;
+
+ for (int y = 0; y < kBlocksCount1D; y++) {
+ for (int x = 0; x < kBlocksCount1D; x++) {
+ IntRect block(horizontal_grid[x], vertical_grid[y],
+ horizontal_grid[x + 1] - horizontal_grid[x],
+ vertical_grid[y + 1] - vertical_grid[y]);
+
+ std::vector<SkColor> block_samples;
+ int block_transparent_pixels;
+ GetBlockSamples(bitmap, block, pixels_per_block, &block_samples,
+ &block_transparent_pixels);
+ opaque_pixels += static_cast<int>(block_samples.size());
+ transparent_pixels += block_transparent_pixels;
+
+ if (!IsBlockBackground(block_samples, block_transparent_pixels)) {
+ sampled_pixels->insert(sampled_pixels->end(), block_samples.begin(),
+ block_samples.end());
+ foreground_blocks.push_back(block);
+ }
+ blocks_count++;
+ }
+ }
+
+ *transparency_ratio = static_cast<float>(transparent_pixels) /
+ (transparent_pixels + opaque_pixels);
+ *background_ratio =
+ 1.0 - static_cast<float>(foreground_blocks.size()) / blocks_count;
+
+ // If samples are too few, resample foreground blocks.
+ if (sampled_pixels->size() < kPixelsToSample / 2 &&
+ foreground_blocks.size()) {
+ pixels_per_block = static_cast<int>(
+ ceil((kPixelsToSample - static_cast<float>(sampled_pixels->size())) /
+ static_cast<float>(foreground_blocks.size())));
+ for (const IntRect& block : foreground_blocks) {
+ std::vector<SkColor> block_samples;
+ int unused;
+ GetBlockSamples(bitmap, block, pixels_per_block, &block_samples, &unused);
+ sampled_pixels->insert(sampled_pixels->end(), block_samples.begin(),
+ block_samples.end());
+ }
+ }
+}
+
+// Selects random samples from a block of the image. Returns the opaque sampled
+// pixels, and the number of transparent sampled pixels.
+void HighContrastImageClassifier::GetBlockSamples(
+ const SkBitmap& bitmap,
+ const IntRect& block,
+ const int required_samples_count,
+ std::vector<SkColor>* sampled_pixels,
+ int* transparent_pixels_count) {
+ *transparent_pixels_count = 0;
+
+ int x1 = block.X();
+ int y1 = block.Y();
+ int x2 = block.MaxX();
+ int y2 = block.MaxY();
+ DCHECK(x1 < bitmap.width());
+ DCHECK(y1 < bitmap.height());
+ DCHECK(x2 <= bitmap.width());
+ DCHECK(y2 <= bitmap.height());
+
+ sampled_pixels->clear();
+ for (int i = 0; i < required_samples_count; i++) {
+ int x = GetRandomInt(x1, x2);
+ int y = GetRandomInt(y1, y2);
+ SkColor new_sample = bitmap.getColor(x, y);
+ if (SkColorGetA(new_sample) < 128)
+ (*transparent_pixels_count)++;
+ else
+ sampled_pixels->push_back(new_sample);
+ }
+}
+
+// Given sampled pixels and transparent pixels count of a block of the image,
+// decides if that block is background or not. If more than 80% of the block is
+// transparent, or the divergence of colors in the block is less than 5%, the
+// block is considered background.
+bool HighContrastImageClassifier::IsBlockBackground(
+ const std::vector<SkColor>& sampled_pixels,
+ const int transparent_pixels) {
+ if (static_cast<int>(sampled_pixels.size()) <= transparent_pixels / 4)
+ return true;
+
+ SkColor4f average;
+ average.fR = 0;
+ average.fG = 0;
+ average.fB = 0;
+
+ for (const SkColor& sample : sampled_pixels)
+ RGBAdd(sample, &average);
+
+ RGBDivide(&average, sampled_pixels.size());
+
+ float sum = 0;
+ for (const auto& sample : sampled_pixels)
+ sum += ColorDifference(sample, average);
+ sum /= 128; // Normalize to 0..1 range.
+ float divergence = sum / sampled_pixels.size();
+ return divergence < 0.05;
+}
+
+// This function computes a single feature vector from a sample set of image
+// pixels. The current features are:
+// 0: 1 if color, 0 if grayscale.
+// 1: Ratio of the number of bucketed colors used in the image to all
+// possiblities. Color buckets are represented with 4 bits per color channel.
+// 2: Ratio of transparent area to the whole image.
+// 3: Ratio of the background area to the whole image.
+void HighContrastImageClassifier::GetFeatures(
+ const std::vector<SkColor>& sampled_pixels,
+ const float transparency_ratio,
+ const float background_ratio,
+ std::vector<float>* features) {
+ int samples_count = static_cast<int>(sampled_pixels.size());
+
+ // Is image grayscale.
+ int color_pixels = 0;
+ for (const SkColor& sample : sampled_pixels) {
+ if (!IsColorGray(sample))
+ color_pixels++;
+ }
+ ColorMode color_mode = (color_pixels > samples_count / 100)
+ ? ColorMode::kColor
+ : ColorMode::kGrayscale;
+
+ features->resize(4);
+
+ // Feature 0: Is Colorful?
+ (*features)[0] = color_mode == ColorMode::kColor;
+
+ // Feature 1: Color Buckets Ratio.
+ (*features)[1] = ComputeColorBucketsRatio(sampled_pixels, color_mode);
+
+ // Feature 2: Transparency Ratio
+ (*features)[2] = transparency_ratio;
+
+ // Feature 3: Background Ratio.
+ (*features)[3] = background_ratio;
+}
+
+float HighContrastImageClassifier::ComputeColorBucketsRatio(
+ const std::vector<SkColor>& sampled_pixels,
+ const ColorMode color_mode) {
+ std::set<unsigned> buckets;
+ // If image is in color, use 4 bits per color channel, otherwise 4 bits for
+ // illumination.
+ if (color_mode == ColorMode::kColor) {
+ for (const SkColor& sample : sampled_pixels) {
+ unsigned bucket = ((SkColorGetR(sample) >> 4) << 8) +
+ ((SkColorGetG(sample) >> 4) << 4) +
+ ((SkColorGetB(sample) >> 4));
+ buckets.insert(bucket);
+ }
+ } else {
+ for (const SkColor& sample : sampled_pixels) {
+ unsigned illumination =
+ (SkColorGetR(sample) * 5 + SkColorGetG(sample) * 3 +
+ SkColorGetB(sample) * 2) /
+ 10;
+ buckets.insert(illumination / 16);
+ }
+ }
+
+ // Using 4 bit per channel representation of each color bucket, there would be
+ // 2^4 buckets for grayscale images and 2^12 for color images.
+ const float max_buckets[] = {16, 4096};
+ return static_cast<float>(buckets.size()) /
+ max_buckets[color_mode == ColorMode::kColor];
+}
+
+HighContrastClassification
+HighContrastImageClassifier::ClassifyImageUsingDecisionTree(
+ const std::vector<float>& features) {
+ DCHECK_EQ(features.size(), 4u);
+
+ int is_color = features[0] > 0;
+ float color_count_ratio = features[1];
+ float low_color_count_threshold = kLowColorCountThreshold[is_color];
+ float high_color_count_threshold = kHighColorCountThreshold[is_color];
+
+ // Very few colors means it's not a photo, apply the filter.
+ if (color_count_ratio < low_color_count_threshold)
+ return HighContrastClassification::kApplyHighContrastFilter;
+
+ // Too many colors means it's probably photorealistic, do not apply it.
+ if (color_count_ratio > high_color_count_threshold)
+ return HighContrastClassification::kDoNotApplyHighContrastFilter;
+
+ // In-between, decision tree cannot give a precise result.
+ return HighContrastClassification::kNotClassified;
+}
+
+HighContrastClassification HighContrastImageClassifier::ClassifyImage(
+ const std::vector<float>& features) {
+ DCHECK_EQ(features.size(), 4u);
+
+ HighContrastClassification result = ClassifyImageUsingDecisionTree(features);
+
+ // If decision tree cannot decide, we use a neural network to decide whether
+ // to filter or not based on all the features.
+ if (result == HighContrastClassification::kNotClassified) {
+ highcontrast_tfnative_model::FixedAllocations nn_temp;
+ float nn_out;
+ highcontrast_tfnative_model::Inference(&features[0], &nn_out, &nn_temp);
+ result = nn_out > 0
+ ? HighContrastClassification::kApplyHighContrastFilter
+ : HighContrastClassification::kDoNotApplyHighContrastFilter;
+ }
+
+ return result;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/high_contrast_image_classifier.h b/chromium/third_party/blink/renderer/platform/graphics/high_contrast_image_classifier.h
new file mode 100644
index 00000000000..8786883777b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/high_contrast_image_classifier.h
@@ -0,0 +1,96 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_HIGH_CONTRAST_IMAGE_CLASSIFIER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_HIGH_CONTRAST_IMAGE_CLASSIFIER_H_
+
+#include <vector>
+
+#include "third_party/blink/renderer/platform/graphics/graphics_types.h"
+#include "third_party/blink/renderer/platform/graphics/image.h"
+
+namespace blink {
+
+class IntRect;
+
+class PLATFORM_EXPORT HighContrastImageClassifier {
+ public:
+ HighContrastImageClassifier();
+ ~HighContrastImageClassifier() = default;
+
+ // Decides if a high contrast filter should be applied to the image or not.
+ bool ShouldApplyHighContrastFilterToImage(Image&);
+
+ bool ComputeImageFeaturesForTesting(Image& image,
+ std::vector<float>* features) {
+ return ComputeImageFeatures(image, features);
+ }
+
+ void SetRandomGeneratorForTesting() { use_testing_random_generator_ = true; }
+
+ HighContrastClassification ClassifyImageUsingDecisionTreeForTesting(
+ const std::vector<float>& features) {
+ return ClassifyImageUsingDecisionTree(features);
+ }
+
+ private:
+ enum class ColorMode { kColor = 0, kGrayscale = 1 };
+
+ // Computes the features vector for a given image.
+ bool ComputeImageFeatures(Image&, std::vector<float>*);
+
+ // Converts image to SkBitmap and returns true if successful.
+ bool GetBitmap(Image&, SkBitmap*);
+
+ // Given a SkBitmap, extracts a sample set of pixels (|sampled_pixels|),
+ // |transparency_ratio|, and |background_ratio|.
+ void GetSamples(const SkBitmap&,
+ std::vector<SkColor>* sampled_pixels,
+ float* transparency_ratio,
+ float* background_ratio);
+
+ // Given |sampled_pixels|, |transparency_ratio|, and |background_ratio| for an
+ // image, computes the required |features| for classification.
+ void GetFeatures(const std::vector<SkColor>& sampled_pixels,
+ const float transparency_ratio,
+ const float background_ratio,
+ std::vector<float>* features);
+
+ // Makes a decision about the image given its features.
+ HighContrastClassification ClassifyImage(const std::vector<float>&);
+
+ // Receives sampled pixels and color mode, and returns the ratio of color
+ // buckets count to all possible color buckets. If image is in color, a color
+ // bucket is a 4 bit per channel representation of each RGB color, and if it
+ // is grayscale, each bucket is a 4 bit representation of luminance.
+ float ComputeColorBucketsRatio(const std::vector<SkColor>&, const ColorMode);
+
+ // Gets the |required_samples_count| for a specific |block| of the given
+ // SkBitmap, and returns |sampled_pixels| and |transparent_pixels_count|.
+ void GetBlockSamples(const SkBitmap&,
+ const IntRect& block,
+ const int required_samples_count,
+ std::vector<SkColor>* sampled_pixels,
+ int* transparent_pixels_count);
+
+ // Given sampled pixels from a block of image and the number of transparent
+ // pixels, decides if a block is part of background or or not.
+ bool IsBlockBackground(const std::vector<SkColor>&, const int);
+
+ // Returns a random number in range [min, max).
+ int GetRandomInt(const int min, const int max);
+
+ // Decides if the filter should be applied to the image or not, only using the
+ // decision tree. Returns 'kNotClassified' if decision tree cannot give a
+ // trustable answer.
+ HighContrastClassification ClassifyImageUsingDecisionTree(
+ const std::vector<float>&);
+
+ bool use_testing_random_generator_;
+ int testing_random_generator_seed_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_HIGH_CONTRAST_IMAGE_CLASSIFIER_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/high_contrast_image_classifier_test.cc b/chromium/third_party/blink/renderer/platform/graphics/high_contrast_image_classifier_test.cc
new file mode 100644
index 00000000000..e225f5e3309
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/high_contrast_image_classifier_test.cc
@@ -0,0 +1,120 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/high_contrast_image_classifier.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/graphics/bitmap_image.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
+#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
+
+namespace {
+const float kEpsilon = 0.00001;
+
+} // namespace
+
+namespace blink {
+
+class HighContrastImageClassifierTest : public testing::Test {
+ public:
+ // Loads the image from |file_name|, computes features vector into |features|,
+ // and returns the classification result.
+ bool GetFeaturesAndClassification(const std::string& file_name,
+ std::vector<float>* features) {
+ SCOPED_TRACE(file_name);
+ scoped_refptr<BitmapImage> image = LoadImage(file_name);
+ classifier_.SetRandomGeneratorForTesting();
+ classifier_.ComputeImageFeaturesForTesting(*image.get(), features);
+ return classifier_.ShouldApplyHighContrastFilterToImage(*image.get());
+ }
+
+ void AssertFeaturesEqual(const std::vector<float>& features,
+ const std::vector<float>& expected_features) {
+ EXPECT_EQ(features.size(), expected_features.size());
+ for (unsigned i = 0; i < features.size(); i++) {
+ EXPECT_NEAR(features[i], expected_features[i], kEpsilon)
+ << "Feature " << i;
+ }
+ }
+
+ HighContrastImageClassifier* classifier() { return &classifier_; }
+
+ protected:
+ scoped_refptr<BitmapImage> LoadImage(const std::string& file_name) {
+ String file_path = test::BlinkLayoutTestsDir();
+ file_path.append(file_name.c_str());
+ scoped_refptr<SharedBuffer> image_data = test::ReadFromFile(file_path);
+ EXPECT_TRUE(image_data.get() && image_data.get()->size());
+
+ scoped_refptr<BitmapImage> image = BitmapImage::Create();
+ image->SetData(image_data, true);
+ return image;
+ }
+
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform_;
+ HighContrastImageClassifier classifier_;
+};
+
+TEST_F(HighContrastImageClassifierTest, FeaturesAndClassification) {
+ std::vector<float> features;
+
+ // Test Case 1:
+ // Grayscale
+ // Color Buckets Ratio: Low
+ // Decision Tree: Apply
+ // Neural Network: NA
+ EXPECT_TRUE(GetFeaturesAndClassification("/images/resources/grid-large.png",
+ &features));
+ EXPECT_EQ(classifier()->ClassifyImageUsingDecisionTreeForTesting(features),
+ HighContrastClassification::kApplyHighContrastFilter);
+ AssertFeaturesEqual(features, {0.0f, 0.1875f, 0.0f, 0.1f});
+
+ // Test Case 2:
+ // Grayscale
+ // Color Buckets Ratio: Medium
+ // Decision Tree: Can't Decide
+ // Neural Network: Apply
+ EXPECT_TRUE(GetFeaturesAndClassification("/images/resources/apng08-ref.png",
+ &features));
+ EXPECT_EQ(classifier()->ClassifyImageUsingDecisionTreeForTesting(features),
+ HighContrastClassification::kNotClassified);
+ AssertFeaturesEqual(features, {0.0f, 0.8125f, 0.409f, 0.59f});
+
+ // Test Case 3:
+ // Color
+ // Color Buckets Ratio: Low
+ // Decision Tree: Apply
+ // Neural Network: NA.
+ EXPECT_TRUE(GetFeaturesAndClassification(
+ "/images/resources/count-down-color-test.png", &features));
+ EXPECT_EQ(classifier()->ClassifyImageUsingDecisionTreeForTesting(features),
+ HighContrastClassification::kApplyHighContrastFilter);
+ AssertFeaturesEqual(features, {1.0f, 0.0134277f, 0.0f, 0.43f});
+
+ // Test Case 4:
+ // Color
+ // Color Buckets Ratio: High
+ // Decision Tree: Do Not Apply
+ // Neural Network: NA.
+ EXPECT_FALSE(GetFeaturesAndClassification(
+ "/images/resources/blue-wheel-srgb-color-profile.png", &features));
+ EXPECT_EQ(classifier()->ClassifyImageUsingDecisionTreeForTesting(features),
+ HighContrastClassification::kDoNotApplyHighContrastFilter);
+ AssertFeaturesEqual(features, {1.0f, 0.03027f, 0.0f, 0.24f});
+
+ // Test Case 5:
+ // Color
+ // Color Buckets Ratio: Medium
+ // Decision Tree: Can't Decide
+ // Neural Network: Apply.
+ EXPECT_TRUE(GetFeaturesAndClassification(
+ "/images/resources/ycbcr-444-float.jpg", &features));
+ EXPECT_EQ(classifier()->ClassifyImageUsingDecisionTreeForTesting(features),
+ HighContrastClassification::kNotClassified);
+ AssertFeaturesEqual(features, {1.0f, 0.0166016f, 0.0f, 0.59f});
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/high_contrast_settings.h b/chromium/third_party/blink/renderer/platform/graphics/high_contrast_settings.h
new file mode 100644
index 00000000000..139db81c540
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/high_contrast_settings.h
@@ -0,0 +1,37 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_HIGH_CONTRAST_SETTINGS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_HIGH_CONTRAST_SETTINGS_H_
+
+namespace blink {
+
+enum class HighContrastMode {
+ // Default, drawing is unfiltered.
+ kOff,
+ // For testing only, does a simple 8-bit invert of every RGB pixel component.
+ kSimpleInvertForTesting,
+ kInvertBrightness,
+ kInvertLightness,
+};
+
+enum class HighContrastImagePolicy {
+ // Apply high-contrast filter to all images.
+ kFilterAll,
+ // Never apply high-contrast filter to any images.
+ kFilterNone,
+ // Apply high-contrast based on image content.
+ kFilterSmart,
+};
+
+struct HighContrastSettings {
+ HighContrastMode mode = HighContrastMode::kOff;
+ bool grayscale = false;
+ float contrast = 0.0; // Valid range from -1.0 to 1.0
+ HighContrastImagePolicy image_policy = HighContrastImagePolicy::kFilterAll;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_HIGH_CONTRAST_SETTINGS_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/highcontrast/README.md b/chromium/third_party/blink/renderer/platform/graphics/highcontrast/README.md
new file mode 100644
index 00000000000..a212d36f42e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/highcontrast/README.md
@@ -0,0 +1,5 @@
+# HighContrast Classifier
+
+These files are automatically generated using tfNative from a neural network
+trained by TensorFlow, to provide a classifier for High Contrast mode in cases
+that decision making is not possible based on color counts. \ No newline at end of file
diff --git a/chromium/third_party/blink/renderer/platform/graphics/highcontrast/highcontrast_classifier.cc b/chromium/third_party/blink/renderer/platform/graphics/highcontrast/highcontrast_classifier.cc
new file mode 100644
index 00000000000..c9093a9b8b9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/highcontrast/highcontrast_classifier.cc
@@ -0,0 +1,1157 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is automatically generated using tfNative from a neural network,
+// trained by TensorFlow. Please do not edit.
+
+#include "highcontrast_classifier.h"
+#include <algorithm>
+#include <cassert>
+#include <cmath>
+#include <cstdint>
+#include <cstring>
+#include <limits>
+#include <tuple>
+#if USE_EIGEN
+#include "third_party/eigen3/Eigen/Core"
+#endif
+namespace highcontrast_tfnative_model {
+namespace {
+
+// -----------------------------------------------------------------------------
+// OP LIBRARY
+// Copied here to make sure that the inferece code always stays in sync with the
+// lib that it was generated for.
+// -----------------------------------------------------------------------------
+
+// Default to using std::copy and std::fill over memcpy and memset as they
+// are usually faster, thanks to the compiler getting stricter alignment
+// guarantees.
+#ifndef USE_TYPED_MEMSETMEMCPY
+#define USE_TYPED_MEMSETMEMCPY 1
+#endif
+#define USE_EIGEN 0
+#ifndef USE_EIGEN
+#error Please define USE_EIGEN to either 0 or 1
+#endif
+
+// Helper to reinterpret memory as Eigen matrices.
+#if USE_EIGEN
+template <typename Scalar>
+using ConstMatrixMap = typename Eigen::Map<
+ const Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic>>;
+template <typename Scalar>
+using ConstRowVectorMap =
+ typename Eigen::Map<const Eigen::Matrix<Scalar, Eigen::Dynamic, 1>>;
+template <typename Scalar>
+using RowVectorMap =
+ typename Eigen::Map<Eigen::Matrix<Scalar, Eigen::Dynamic, 1>>;
+template <typename Scalar>
+using MatrixMap =
+ typename Eigen::Map<Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic>>;
+#endif
+
+#define BENCHMARK_TIMER(...)
+
+// The size of a shape in terms of number of coefficients.
+inline int ShapeSize(const int32_t rank, const int32_t* shape) {
+ int size = 1;
+ for (int i = 0; i < rank; ++i)
+ size *= shape[i];
+ return size;
+}
+
+// Helper to compute the size of the inner loop for an op that uses indices to
+// specify which axes are reduced.
+template <typename Tidx>
+int32_t GetReduceInnerSize(int32_t input_tensor_rank,
+ const int32_t* __restrict input_shape,
+ int32_t index_tensor_rank,
+ const int32_t* __restrict index_shape,
+ const Tidx* __restrict index_values) {
+ assert(index_tensor_rank <= 1);
+ const int32_t num_indices = index_tensor_rank > 0 ? index_shape[0] : 1;
+ int32_t inner_size = 1;
+ for (int32_t i = 0; i < num_indices; ++i) {
+ inner_size *= input_shape[index_values[i]];
+ }
+ return inner_size;
+}
+
+template <typename T>
+void ConcatV2Args2(int32_t arg0_rank,
+ const int32_t* __restrict arg0_shape,
+ const T* __restrict arg0_values,
+ int32_t arg1_rank,
+ const int32_t* __restrict arg1_shape,
+ const T* __restrict arg1_values,
+ const int32_t* __restrict axis_value,
+ T* __restrict output_values) {
+ BENCHMARK_TIMER("ConcatV2Args2");
+ const int axis = axis_value[0];
+ const int num_lines = ShapeSize(axis, arg0_shape);
+ const int arg0_line_size = ShapeSize(arg0_rank - axis, arg0_shape + axis);
+ const int arg1_line_size = ShapeSize(arg1_rank - axis, arg1_shape + axis);
+ for (int line = 0; line < num_lines; ++line) {
+ std::copy(arg0_values, arg0_values + arg0_line_size, output_values);
+ arg0_values += arg0_line_size;
+ output_values += arg0_line_size;
+ std::copy(arg1_values, arg1_values + arg1_line_size, output_values);
+ arg1_values += arg1_line_size;
+ output_values += arg1_line_size;
+ }
+}
+
+template <typename T>
+void Conv2DAsGemm(const int32_t* __restrict in_shape,
+ const T* __restrict in_values,
+ const int32_t* __restrict filter_shape,
+ const T* __restrict filter_values,
+ T* __restrict output_values) {
+ BENCHMARK_TIMER("Conv2DAsGemm");
+#if USE_EIGEN
+ const auto in = ConstMatrixMap<T>(in_values, in_shape[0], in_shape[1]);
+ const auto filter =
+ ConstMatrixMap<T>(filter_values, filter_shape[3],
+ filter_shape[0] * filter_shape[1] * filter_shape[2]);
+ auto result = MatrixMap<T>(output_values, filter_shape[3], in_shape[1]);
+ result.noalias() = filter * in;
+#else
+ const int32_t out_rows = in_shape[1];
+ const int32_t out_cols = filter_shape[3];
+ const int32_t dot_len = in_shape[0];
+ for (int row = 0; row < out_rows; ++row) {
+ for (int col = 0; col < out_cols; ++col) {
+ T value = 0;
+ for (int i = 0; i < dot_len; ++i) {
+ value +=
+ in_values[row * dot_len + i] * filter_values[i * out_cols + col];
+ }
+ *output_values++ = value;
+ }
+ }
+#endif
+}
+
+template <typename T>
+void DepthwiseConv2dNative(const int32_t* __restrict input_shape,
+ const T* __restrict input_values,
+ const int32_t* __restrict kernel_shape,
+ const T* __restrict kernel_values,
+ int32_t stride_y,
+ int32_t stride_x,
+ int32_t out_height,
+ int32_t out_width,
+ T* __restrict output_values) {
+ BENCHMARK_TIMER("DepthwiseConv2dNative");
+ // Give the shape values nicer names.
+ assert(input_shape[3] == kernel_shape[2]);
+ const int batch_size = input_shape[0];
+ const int kernel_height = kernel_shape[0];
+ const int kernel_width = kernel_shape[1];
+ const int in_depth = kernel_shape[2];
+ const int depth_mul = kernel_shape[3];
+ const int in_height = input_shape[1];
+ const int in_width = input_shape[2];
+
+ // Compute the amount of padding needed to get the desired output size.
+ const int pad_height =
+ ((out_height - 1) * stride_y + kernel_height - in_height) / 2;
+ const int pad_width =
+ ((out_width - 1) * stride_x + kernel_width - in_width) / 2;
+
+ // Cache the strides for address computations.
+ const int in_strides[4] = {
+ input_shape[1] * input_shape[2] * input_shape[3], // batch
+ input_shape[2] * input_shape[3], // y
+ input_shape[3], // x
+ 1, // channel
+ };
+ const int kernel_strides[4] = {
+ kernel_shape[1] * kernel_shape[2] * kernel_shape[3], // y
+ kernel_shape[2] * kernel_shape[3], // x
+ kernel_shape[3], // in channels
+ 1, // channel mult
+ };
+
+ T* out_write_ptr = output_values;
+ for (int batch = 0; batch < batch_size; ++batch) {
+ for (int out_y = 0; out_y < out_height; ++out_y) {
+ for (int out_x = 0; out_x < out_width; ++out_x) {
+ // Compute the input read offsets.
+ const int in_y_origin = (out_y * stride_y) - pad_height;
+ const int in_x_origin = (out_x * stride_x) - pad_width;
+
+ // Compute the range of the kernel to be applied (we may need to clip
+ // when we'd read outside of the valid input region - for SAME).
+ const int kernel_y_start = std::max(0, -in_y_origin);
+ const int kernel_y_end =
+ std::min(kernel_height, in_height - in_y_origin);
+ const int kernel_x_start = std::max(0, -in_x_origin);
+ const int kernel_x_end = std::min(kernel_width, in_width - in_x_origin);
+
+ for (int in_c = 0; in_c < in_depth; ++in_c) {
+ for (int mul_c = 0; mul_c < depth_mul; ++mul_c, ++out_write_ptr) {
+ // Convolve.
+ T sum = 0;
+ for (int k_y = kernel_y_start; k_y < kernel_y_end; ++k_y) {
+ const int in_y = in_y_origin + k_y;
+ assert(in_y >= 0 && in_y < in_height);
+ for (int k_x = kernel_x_start; k_x < kernel_x_end; ++k_x) {
+ const int in_x = in_x_origin + k_x;
+ assert(in_x >= 0 && in_x < in_width);
+ const T input_value =
+ input_values[batch * in_strides[0] + // batch
+ in_y * in_strides[1] + // y
+ in_x * in_strides[2] + // x
+ in_c]; // in chan
+ const T kernel_value =
+ kernel_values[k_y * kernel_strides[0] + // y
+ k_x * kernel_strides[1] + // x
+ in_c * kernel_strides[2] + // in chan
+ mul_c]; // chan mult
+ sum += input_value * kernel_value;
+ }
+ }
+ *out_write_ptr = sum;
+ } // mul_c
+ } // in_c
+ } // out_x
+ } // out_y
+ } // batch
+}
+
+template <typename T>
+void FullyConnected(const int32_t* __restrict input_shape,
+ const T* __restrict input_values,
+ const int32_t* __restrict weight_shape,
+ const T* __restrict weight_values,
+ const int32_t* __restrict bias_shape,
+ const T* __restrict bias_values,
+ T* __restrict output_values) {
+ BENCHMARK_TIMER("FullyConnected");
+#if USE_EIGEN
+ const auto in =
+ ConstMatrixMap<T>(input_values, input_shape[1], input_shape[0]);
+ const auto weight =
+ ConstMatrixMap<T>(weight_values, weight_shape[1], weight_shape[0]);
+ const auto bias = ConstRowVectorMap<T>(bias_values, bias_shape[0]);
+ auto result = MatrixMap<T>(output_values, weight_shape[1], input_shape[0]);
+ result.noalias() = (weight * in).colwise() + bias;
+#else
+ const int batch_size = input_shape[0];
+ const int num_inputs = weight_shape[0];
+ const int num_outputs = weight_shape[1];
+ assert(input_shape[1] == num_inputs);
+ assert(bias_shape[0] == num_outputs);
+ for (int batch = 0; batch < batch_size; ++batch) {
+ for (int out_i = 0; out_i < num_outputs; ++out_i) {
+ T value = 0;
+ for (int in_i = 0; in_i < num_inputs; ++in_i) {
+ value += input_values[batch * num_inputs + in_i] *
+ weight_values[in_i * num_outputs + out_i];
+ }
+ value += bias_values[out_i];
+ output_values[batch * num_outputs + out_i] = value;
+ }
+ }
+#endif
+}
+
+template <typename T, typename TIndex>
+void Gather(int params_rank,
+ const int32_t* __restrict params_shape,
+ const T* __restrict params_values,
+ int indices_rank,
+ const int32_t* __restrict indices_shape,
+ const TIndex* __restrict indices_values,
+ T* __restrict output_values) {
+ BENCHMARK_TIMER("Gather");
+ const int num_indices = ShapeSize(indices_rank, indices_shape);
+ const int num_params = params_shape[0];
+ const int slice_size = ShapeSize(params_rank - 1, params_shape + 1);
+ for (int i = 0; i < num_indices; ++i) {
+ const int index = indices_values[i];
+ if (index < 0 || index >= num_params) {
+ std::fill(output_values, output_values + slice_size, 0);
+ } else {
+ std::copy(params_values + index * slice_size,
+ params_values + index * slice_size + slice_size, output_values);
+ }
+ output_values += slice_size;
+ }
+}
+
+template <typename T, typename TIndex>
+void Im2Col(const int32_t* __restrict input_shape,
+ const T* __restrict input_values,
+ const int32_t* __restrict kernel_shape,
+ int32_t stride_y,
+ int32_t stride_x,
+ int32_t out_height,
+ int32_t out_width,
+ TIndex* output_shape,
+ T* __restrict output_values) {
+ BENCHMARK_TIMER("Im2Col");
+ // Give the shape values nicer names.
+ assert(input_shape[3] == kernel_shape[2]);
+ const int batch_size = input_shape[0];
+ const int kernel_height = kernel_shape[0];
+ const int kernel_width = kernel_shape[1];
+ const int in_depth = kernel_shape[2];
+ const int in_height = input_shape[1];
+ const int in_width = input_shape[2];
+
+ // Compute the amount of padding needed to get the desired output size.
+ const int pad_height =
+ ((out_height - 1) * stride_y + kernel_height - in_height) / 2;
+ const int pad_width =
+ ((out_width - 1) * stride_x + kernel_width - in_width) / 2;
+
+ // Cache the strides for address computations.
+ const int x_stride = input_shape[3];
+ const int y_stride = input_shape[2] * x_stride;
+ const int batch_stride = input_shape[1] * y_stride;
+
+ // Write the output shape.
+ output_shape[0] = kernel_height * kernel_width * in_depth;
+ output_shape[1] = input_shape[0] * out_width * out_height;
+
+ for (int batch = 0; batch < batch_size; ++batch) {
+ for (int out_y = 0; out_y < out_height; ++out_y) {
+ for (int out_x = 0; out_x < out_width; ++out_x) {
+ // Compute the input read offsets.
+ const int in_y_origin = (out_y * stride_y) - pad_height;
+ const int in_x_origin = (out_x * stride_x) - pad_width;
+
+ // Compute the range of the kernel to be applied (we may need to clip
+ // when we'd read outside of the valid input region - for SAME).
+ const int kernel_y_start = std::max(0, -in_y_origin);
+ const int kernel_y_end =
+ std::min(kernel_height, in_height - in_y_origin);
+ const int kernel_x_start = std::max(0, -in_x_origin);
+ const int kernel_x_end = std::min(kernel_width, in_width - in_x_origin);
+
+ // Padding top.
+ if (kernel_y_start != 0) {
+ const int num_lines = kernel_y_start;
+ const int num_coeffs = num_lines * kernel_width * in_depth;
+#if USE_TYPED_MEMSETMEMCPY
+ std::fill(output_values, output_values + num_coeffs, 0);
+#else
+ std::memset(output_values, 0, num_coeffs * sizeof(T));
+#endif
+ output_values += num_coeffs;
+ }
+ for (int k_y = kernel_y_start; k_y < kernel_y_end; ++k_y) {
+ // Padding left.
+ if (kernel_x_start != 0) {
+ const int num_coeffs = kernel_x_start * in_depth;
+#if USE_TYPED_MEMSETMEMCPY
+ std::fill(output_values, output_values + num_coeffs, 0);
+#else
+ std::memset(output_values, 0, num_coeffs * sizeof(T));
+#endif
+ output_values += num_coeffs;
+ }
+ // Valid values.
+ {
+ const int in_y = in_y_origin + k_y;
+ const int in_x = in_x_origin + kernel_x_start;
+ const int num_coeffs = (kernel_x_end - kernel_x_start) * in_depth;
+#if USE_TYPED_MEMSETMEMCPY
+ const int offset =
+ batch * batch_stride + in_y * y_stride + in_x * x_stride;
+ std::copy(input_values + offset, input_values + offset + num_coeffs,
+ output_values);
+#else
+ std::memcpy(output_values,
+ input_values // Reusing the restricted pointer.
+ + batch * batch_stride // batch
+ + in_y * y_stride // y
+ + in_x * x_stride, // x
+ num_coeffs * sizeof(T));
+#endif
+ output_values += num_coeffs;
+ }
+ // Padding right.
+ if (kernel_x_end != kernel_width) {
+ const int num_coeffs = (kernel_width - kernel_x_end) * in_depth;
+#if USE_TYPED_MEMSETMEMCPY
+ std::fill(output_values, output_values + num_coeffs, 0);
+#else
+ std::memset(output_values, 0, num_coeffs * sizeof(T));
+#endif
+ output_values += num_coeffs;
+ }
+ }
+ // Padding bottom.
+ if (kernel_y_end != kernel_height) {
+ const int num_lines = kernel_height - kernel_y_end;
+ const int num_coeffs = num_lines * kernel_width * in_depth;
+#if USE_TYPED_MEMSETMEMCPY
+ std::fill(output_values, output_values + num_coeffs, 0);
+#else
+ std::memset(output_values, 0, num_coeffs * sizeof(T));
+#endif
+ output_values += num_coeffs;
+ }
+ }
+ }
+ }
+}
+
+template <typename T>
+void MaxPool(const int32_t* __restrict input_shape,
+ const T* __restrict input_values,
+ int32_t stride_y,
+ int32_t stride_x,
+ int32_t kernel_height,
+ int32_t kernel_width,
+ int32_t out_height,
+ int32_t out_width,
+ T* __restrict output_values) {
+ BENCHMARK_TIMER("MaxPool");
+ // Give the shape values nicer names.
+ const int batch_size = input_shape[0];
+ const int in_height = input_shape[1];
+ const int in_width = input_shape[2];
+ const int depth = input_shape[3];
+
+ // Compute the amount of padding needed to get the desired output size.
+ const int pad_height =
+ ((out_height - 1) * stride_y + kernel_height - in_height) / 2;
+ const int pad_width =
+ ((out_width - 1) * stride_x + kernel_width - in_width) / 2;
+
+ // Cache the strides for address computations.
+ const int in_strides[4] = {
+ input_shape[1] * input_shape[2] * input_shape[3], // batch
+ input_shape[2] * input_shape[3], // y
+ input_shape[3], // x
+ 1, // channel
+ };
+
+ T* out_write_ptr = output_values;
+ for (int batch = 0; batch < batch_size; ++batch) {
+ for (int out_y = 0; out_y < out_height; ++out_y) {
+ for (int out_x = 0; out_x < out_width; ++out_x) {
+ // Compute the input read offsets.
+ const int in_y_origin = (out_y * stride_y) - pad_height;
+ const int in_x_origin = (out_x * stride_x) - pad_width;
+
+ // Compute the range of the kernel to be applied (we may need to clip
+ // when we'd read outside of the valid input region - for SAME).
+ const int kernel_y_start = std::max(0, -in_y_origin);
+ const int kernel_y_end =
+ std::min(kernel_height, in_height - in_y_origin);
+ const int kernel_x_start = std::max(0, -in_x_origin);
+ const int kernel_x_end = std::min(kernel_width, in_width - in_x_origin);
+
+ for (int chan = 0; chan < depth; ++chan, ++out_write_ptr) {
+ // Convolve.
+ T max_value = std::numeric_limits<T>::lowest();
+ for (int k_y = kernel_y_start; k_y < kernel_y_end; ++k_y) {
+ const int in_y = in_y_origin + k_y;
+ assert(in_y >= 0 && in_y < in_height);
+ for (int k_x = kernel_x_start; k_x < kernel_x_end; ++k_x) {
+ const int in_x = in_x_origin + k_x;
+ assert(in_x >= 0 && in_x < in_width);
+ const T input_value =
+ input_values[batch * in_strides[0] + // batch
+ in_y * in_strides[1] + // y
+ in_x * in_strides[2] + // x
+ chan]; // channel
+ max_value = std::max(max_value, input_value);
+ } // kernel_x
+ } // kernel_y
+ *out_write_ptr = max_value;
+ } // chan
+ } // out_x
+ } // out_y
+ } // batch
+}
+
+template <typename T>
+void Memcpy(const int32_t rank,
+ const int32_t* __restrict input_shape,
+ const T* __restrict input_values,
+ T* __restrict output_values) {
+ BENCHMARK_TIMER("Memcpy");
+ const int size = ShapeSize(rank, input_shape);
+ for (int i = 0; i < size; ++i) {
+ output_values[i] = input_values[i];
+ }
+}
+
+template <typename T>
+void Softmax(const int32_t rank,
+ const int32_t* __restrict input_shape,
+ const T* __restrict input_values,
+ const int32_t reduce_dim,
+ T* __restrict output_values,
+ T* __restrict scratch_values) {
+ BENCHMARK_TIMER("Softmax");
+ const int size = ShapeSize(rank, input_shape);
+ if (rank == 2 && reduce_dim == 1) {
+ T logits_max = std::numeric_limits<T>::lowest();
+
+ // Max.
+ for (int i = 0; i < size; ++i) {
+ logits_max = std::max(logits_max, input_values[i]);
+ }
+
+ // Pre-compute exp.
+ for (int i = 0; i < size; ++i) {
+ scratch_values[i] = std::exp(input_values[i] - logits_max);
+ }
+
+ // Sum over the last dimension, then divide the exps and write out.
+ for (int offset = 0; offset < size; offset += input_shape[1]) {
+ T sum = 0;
+ const int end_offset = offset + input_shape[1];
+ for (int i = offset; i < end_offset; ++i)
+ sum += scratch_values[i];
+ const T rcp_denom = static_cast<T>(1) / sum;
+ for (int i = 0; i < input_shape[1]; ++i) {
+ output_values[offset + i] = scratch_values[offset + i] * rcp_denom;
+ }
+ }
+ } else {
+ assert(false && "Generic Softmax not yet supported.");
+ }
+}
+
+// Returns the start position for a slice in a single dimension.
+template <typename T>
+int StridedSliceBegin(int range_mask,
+ const T* __restrict range_values,
+ const T* __restrict strides,
+ const int32_t* __restrict input_shape,
+ int dim) {
+ const bool is_explicit = 0 == (range_mask & (1 << dim));
+ if (is_explicit) {
+ return range_values[dim];
+ } else {
+ const bool is_reverse = strides[dim] < 0;
+ return is_reverse ? input_shape[dim] - 1 : 0;
+ }
+}
+
+// Returns the end position for a slice in a single dimension.
+template <typename T>
+int StridedSliceEnd(int range_mask,
+ const T* __restrict range_values,
+ const T* __restrict strides,
+ const int32_t* __restrict input_shape,
+ int dim) {
+ const bool is_explicit = 0 == (range_mask & (1 << dim));
+ if (is_explicit) {
+ return range_values[dim];
+ } else {
+ const bool is_reverse = strides[dim] < 0;
+ return is_reverse ? -1 : input_shape[dim];
+ }
+}
+
+template <typename T, typename TIdx>
+void StridedSlice(const int32_t input_rank,
+ const int32_t* __restrict input_shape,
+ const T* __restrict input_values,
+ const TIdx* __restrict begin,
+ const TIdx* __restrict end,
+ const TIdx* __restrict strides,
+ int32_t begin_mask,
+ int32_t end_mask,
+ T* __restrict output_values) {
+ BENCHMARK_TIMER("StridedSlice");
+ const int MAX_RANK = 8;
+ assert(input_rank < MAX_RANK);
+
+ // Compute the address strides for each dimension.
+ int dim_addr_strides[MAX_RANK] = {0};
+ dim_addr_strides[input_rank - 1] = 1;
+ for (int dim = input_rank - 2; dim >= 0; --dim) {
+ dim_addr_strides[dim] = dim_addr_strides[dim + 1] * input_shape[dim + 1];
+ }
+
+ // Resolve the masks and get explicit ranges for each dimension.
+ int dim_begin[MAX_RANK];
+ int dim_end[MAX_RANK];
+ bool dim_is_full_range[MAX_RANK];
+ for (int dim = 0; dim < input_rank; ++dim) {
+ const int stride = strides[dim];
+ dim_begin[dim] =
+ StridedSliceBegin(begin_mask, begin, strides, input_shape, dim);
+ dim_end[dim] = StridedSliceEnd(end_mask, end, strides, input_shape, dim);
+ dim_is_full_range[dim] =
+ dim_begin[dim] == 0 && dim_end[dim] == input_shape[dim] && stride == 1;
+
+ // Our termination criteria for loops is that we hit the end exactly, so
+ // we need to ensure that we don't step over the end with stride != 1.
+ const int length_mod = (dim_end[dim] - dim_begin[dim]) % stride;
+ if (length_mod != 0) {
+ dim_end[dim] += stride - length_mod;
+ }
+ }
+
+ // Find out how large the blocks are that we can copy contiguously. (All
+ // dimensions on the right for which we fetch the full range)
+ int last_sliced_dim = input_rank - 1;
+ int block_size = 1;
+ for (int dim = input_rank - 1; dim >= 0 && dim_is_full_range[dim]; --dim) {
+ block_size *= input_shape[dim];
+ last_sliced_dim--;
+ }
+
+ // Initialize the read pos for each dimension according to the begin offsets.
+ int read_pos[MAX_RANK] = {0};
+ for (int dim = 0; dim < input_rank; ++dim) {
+ read_pos[dim] = dim_begin[dim];
+ }
+
+ while (read_pos[0] != dim_end[0]) {
+ // Compute the read offset for the current position.
+ int32_t read_offset = 0;
+ for (int dim = 0; dim <= last_sliced_dim; ++dim) {
+ const int addr_stride = dim_addr_strides[dim];
+ if (read_pos[dim] < 0) {
+ read_offset += (input_shape[dim] + read_pos[dim]) * addr_stride;
+ } else {
+ read_offset += read_pos[dim] * addr_stride;
+ }
+ }
+
+#if USE_TYPED_MEMSETMEMCPY
+ std::copy(input_values + read_offset,
+ input_values + read_offset + block_size, output_values);
+#else
+ std::memcpy(output_values, input_values + read_offset,
+ block_size * sizeof(T));
+#endif
+ output_values += block_size;
+
+ // Advance the read position.
+ for (int dim = last_sliced_dim; dim >= 0; --dim) {
+ read_pos[dim] += strides[dim];
+ if (dim == 0 || read_pos[dim] != dim_end[dim])
+ break;
+ read_pos[dim] = dim_begin[dim];
+ }
+ }
+}
+
+template <typename T>
+void TransposeRank3(const int32_t* __restrict input_shape,
+ const T* __restrict input_values,
+ const int32_t* __restrict perm,
+ T* __restrict output_values) {
+ BENCHMARK_TIMER("TransposeRank3");
+ const int32_t in_strides[3] = {
+ input_shape[1] * input_shape[2], input_shape[2], 1,
+ };
+ const int32_t out_strides[3] = {in_strides[perm[0]], in_strides[perm[1]],
+ in_strides[perm[2]]};
+ const int32_t out_shape[3] = {input_shape[perm[0]], input_shape[perm[1]],
+ input_shape[perm[2]]};
+
+ int32_t write_offset = 0;
+ for (int32_t it0 = 0; it0 < out_shape[0]; ++it0) {
+ const int32_t read_offset0 = it0 * out_strides[0];
+ for (int32_t it1 = 0; it1 < out_shape[1]; ++it1) {
+ const int32_t read_offset01 = read_offset0 + it1 * out_strides[1];
+ for (int32_t it2 = 0; it2 < out_shape[2]; ++it2, ++write_offset) {
+ const int32_t read_offset = read_offset01 + it2 * out_strides[2];
+ output_values[write_offset] = input_values[read_offset];
+ }
+ }
+ }
+}
+
+template <typename T>
+void TransposeRank4(const int32_t* __restrict input_shape,
+ const T* __restrict input_values,
+ const int32_t* __restrict perm,
+ T* __restrict output_values) {
+ BENCHMARK_TIMER("TransposeRank4");
+ const int32_t in_strides[4] = {
+ input_shape[1] * input_shape[2] * input_shape[3],
+ input_shape[2] * input_shape[3], input_shape[3], 1,
+ };
+ const int32_t out_strides[4] = {in_strides[perm[0]], in_strides[perm[1]],
+ in_strides[perm[2]], in_strides[perm[3]]};
+ const int32_t out_shape[4] = {input_shape[perm[0]], input_shape[perm[1]],
+ input_shape[perm[2]], input_shape[perm[3]]};
+
+ int32_t write_offset = 0;
+ for (int32_t it0 = 0; it0 < out_shape[0]; ++it0) {
+ const int32_t read_offset0 = it0 * out_strides[0];
+ for (int32_t it1 = 0; it1 < out_shape[1]; ++it1) {
+ const int32_t read_offset01 = read_offset0 + it1 * out_strides[1];
+ for (int32_t it2 = 0; it2 < out_shape[2]; ++it2) {
+ const int32_t read_offset012 = read_offset01 + it2 * out_strides[2];
+ for (int32_t it3 = 0; it3 < out_shape[3]; ++it3, ++write_offset) {
+ const int32_t read_offset = read_offset012 + it3 * out_strides[3];
+ output_values[write_offset] = input_values[read_offset];
+ }
+ }
+ }
+ }
+}
+
+template <typename T, typename TIdx, typename TDepth>
+void OneHot(const int32_t input_rank,
+ const int32_t* __restrict input_shape,
+ const TIdx* __restrict input_values,
+ const TDepth* __restrict depth,
+ const T* __restrict on_value,
+ const T* __restrict off_value,
+ const int32_t axis,
+ T* __restrict output_values) {
+ BENCHMARK_TIMER("OneHot");
+ const int32_t num_elements = ShapeSize(input_rank, input_shape);
+ // We can assume axis >= 0 in this implementation.
+ const int32_t prefix_dim_size = ShapeSize(axis, input_shape);
+ const int32_t suffix_dim_size = num_elements / prefix_dim_size;
+ int32_t write_offset = 0;
+ for (int32_t i = 0; i < prefix_dim_size; i++) {
+ int32_t read_offset_pre = i * suffix_dim_size;
+ for (TDepth d = 0; d < *depth; d++) {
+ for (int32_t j = 0; j < suffix_dim_size; j++, write_offset++) {
+ const int32_t read_offset = read_offset_pre + j;
+ output_values[write_offset] =
+ (input_values[read_offset] == d) ? *on_value : *off_value;
+ }
+ }
+ }
+}
+
+template <typename T, typename TIdx, typename TDepth>
+void OneHotLastDim(const int32_t input_rank,
+ const int32_t* __restrict input_shape,
+ const TIdx* __restrict input_values,
+ const TDepth* __restrict depth,
+ const T* __restrict on_value,
+ const T* __restrict off_value,
+ T* __restrict output_values) {
+ BENCHMARK_TIMER("OneHotLastDim");
+ const int32_t num_elements = ShapeSize(input_rank, input_shape);
+ int32_t write_offset = 0;
+ for (int32_t i = 0; i < num_elements; i++) {
+ for (TDepth d = 0; d < *depth; d++, write_offset++) {
+ output_values[write_offset] =
+ (input_values[i] == d) ? *on_value : *off_value;
+ }
+ }
+}
+
+// -----------------------------------------------------------------------------
+// Simple unary ops
+// -----------------------------------------------------------------------------
+
+// We use macros instead of template functions with templated functors here
+// because it's a lot less verbose and easier for the compiler to optimize.
+
+#if USE_EIGEN
+
+#define SIMPLE_UNARY_OP(OP_NAME, _, EXPR_EIGEN) \
+ template <typename T> \
+ void OP_NAME(const int32_t rank, const int32_t* __restrict input_shape, \
+ const T* __restrict input_values, \
+ T* __restrict output_values) { \
+ BENCHMARK_TIMER(#OP_NAME); \
+ const int size = ShapeSize(rank, input_shape); \
+ auto values = ConstRowVectorMap<T>(input_values, size).array(); \
+ auto output = RowVectorMap<T>(output_values, size).array(); \
+ output = EXPR_EIGEN; \
+ }
+
+#else
+
+#define SIMPLE_UNARY_OP(OP_NAME, EXPR, _) \
+ template <typename T> \
+ void OP_NAME(const int32_t rank, const int32_t* __restrict input_shape, \
+ const T* __restrict input_values, \
+ T* __restrict output_values) { \
+ BENCHMARK_TIMER(#OP_NAME); \
+ const int size = ShapeSize(rank, input_shape); \
+ for (int i = 0; i < size; ++i) { \
+ const T value = input_values[i]; \
+ output_values[i] = EXPR; \
+ } \
+ }
+
+#endif
+
+// Second macro param is value expression, third entry is Eigen vector
+// expression.
+SIMPLE_UNARY_OP(Abs, std::abs(value), values.abs())
+SIMPLE_UNARY_OP(Acos, std::acos(value), values.acos())
+SIMPLE_UNARY_OP(Asin, std::asin(value), values.asin())
+SIMPLE_UNARY_OP(Atan, std::atan(value), values.atan())
+SIMPLE_UNARY_OP(Cos, std::cos(value), values.cos())
+SIMPLE_UNARY_OP(Cosh, std::cosh(value), values.cosh())
+SIMPLE_UNARY_OP(Exp, std::exp(value), values.exp())
+SIMPLE_UNARY_OP(Elu,
+ value < 0 ? std::expm1(value) : value,
+ // Use branchless version of Elu: min(ReLU, e^x - 1)
+ values.max(0).min(values.exp() - 1))
+SIMPLE_UNARY_OP(Log, std::log(value), values.log())
+SIMPLE_UNARY_OP(Log1p, std::log1p(value), values.log1p())
+SIMPLE_UNARY_OP(Neg, -value, -values)
+SIMPLE_UNARY_OP(Reciprocal, static_cast<T>(1) / value, values.cwiseInverse())
+SIMPLE_UNARY_OP(Relu, std::max(value, static_cast<T>(0)), values.max(0))
+SIMPLE_UNARY_OP(Relu6,
+ std::min(std::max(value, static_cast<T>(0)), static_cast<T>(6)),
+ values.max(0).min(6))
+SIMPLE_UNARY_OP(Rsqrt, static_cast<T>(1) / std::sqrt(value), values.rsqrt())
+SIMPLE_UNARY_OP(Sigmoid,
+ static_cast<T>(1) / (1 + std::exp(-value)),
+ ((-values).exp() + 1).cwiseInverse())
+SIMPLE_UNARY_OP(Sin, std::sin(value), values.sin())
+SIMPLE_UNARY_OP(Sinh, std::sinh(value), values.sinh())
+SIMPLE_UNARY_OP(Sqrt, std::sqrt(value), values.sqrt())
+SIMPLE_UNARY_OP(Square, value* value, values.square())
+SIMPLE_UNARY_OP(Tan, std::tan(value), values.tan())
+SIMPLE_UNARY_OP(Tanh, std::tanh(value), values.tanh())
+
+// -----------------------------------------------------------------------------
+// Broadcasting binary ops
+// -----------------------------------------------------------------------------
+
+template <typename T, typename OP>
+void OpNoBroadcast(const int32_t left_rank,
+ const int32_t* __restrict left_shape,
+ const T* __restrict left_values,
+ const int32_t right_rank,
+ const int32_t* __restrict right_shape,
+ const T* __restrict right_values,
+ T* __restrict output_values,
+ OP op) {
+ BENCHMARK_TIMER(op.name, "NoBroadcast");
+ const int32_t size = ShapeSize(left_rank, left_shape);
+#if USE_EIGEN
+ auto lhs = ConstRowVectorMap<T>(left_values, size).array();
+ auto rhs = ConstRowVectorMap<T>(right_values, size).array();
+ auto output = RowVectorMap<T>(output_values, size).array();
+ op.apply(lhs, rhs, output);
+#else
+ for (int32_t i = 0; i < size; ++i) {
+ output_values[i] = op(left_values[i], right_values[i]);
+ }
+#endif
+}
+
+template <typename T, typename OP>
+void OpInnerBroadcast(int32_t left_rank,
+ const int32_t* __restrict left_shape,
+ const T* __restrict left_values,
+ int32_t right_rank,
+ const int32_t* __restrict right_shape,
+ const T* __restrict right_values,
+ T* __restrict output_values,
+ OP op) {
+ BENCHMARK_TIMER(op.name, "InnerBroadcast");
+ const int32_t output_size = ShapeSize(left_rank, left_shape);
+ const int32_t inner_size = ShapeSize(right_rank, right_shape);
+ const int32_t outer_size = output_size / inner_size;
+#if USE_EIGEN
+ if (inner_size == 1) {
+ // Apply the same value to all elements.
+ auto left = ConstMatrixMap<T>(left_values, inner_size, outer_size);
+ auto output = MatrixMap<T>(output_values, inner_size, outer_size);
+ op.apply(left.array(), right_values[0], output.array());
+ } else {
+ auto left = ConstMatrixMap<T>(left_values, inner_size, outer_size);
+ auto right = ConstRowVectorMap<T>(right_values, inner_size);
+ auto output = MatrixMap<T>(output_values, inner_size, outer_size);
+ for (int32_t col = 0; col < outer_size; col++) {
+ op.apply(left.col(col).array(), right.array(), output.col(col).array());
+ }
+ }
+#else
+ for (int32_t idx_out = 0; idx_out < outer_size; ++idx_out) {
+ for (int32_t idx_in = 0; idx_in < inner_size; ++idx_in) {
+ const int32_t offset = idx_out * inner_size + idx_in;
+ output_values[offset] = op(left_values[offset], right_values[idx_in]);
+ }
+ }
+#endif
+}
+
+#define BROADCAST_BINARY_OP(OP_NAME, EXPR, EXPR_EIGEN) \
+ template <typename T> \
+ struct Op##OP_NAME { \
+ const char* name = #OP_NAME; \
+ T operator()(const T lhs, const T rhs) { return EXPR; } \
+ template <typename X, typename Y, typename Z> \
+ void apply(const X& lhs, const Y& rhs, Z out) { \
+ out = EXPR_EIGEN; \
+ } \
+ }; \
+ template <typename T> \
+ void OP_NAME##NoBroadcast( \
+ const int32_t left_rank, const int32_t* __restrict left_shape, \
+ const T* __restrict left_values, const int32_t right_rank, \
+ const int32_t* __restrict right_shape, const T* __restrict right_values, \
+ T* __restrict output_values) { \
+ OpNoBroadcast(left_rank, left_shape, left_values, right_rank, right_shape, \
+ right_values, output_values, Op##OP_NAME<T>()); \
+ } \
+ template <typename T> \
+ void OP_NAME##InnerBroadcast( \
+ const int32_t left_rank, const int32_t* __restrict left_shape, \
+ const T* __restrict left_values, const int32_t right_rank, \
+ const int32_t* __restrict right_shape, const T* __restrict right_values, \
+ T* __restrict output_values) { \
+ OpInnerBroadcast(left_rank, left_shape, left_values, right_rank, \
+ right_shape, right_values, output_values, \
+ Op##OP_NAME<T>()); \
+ }
+
+// Second macro param is value expression, third entry is Eigen vector
+// expression.
+BROADCAST_BINARY_OP(Add, lhs + rhs, lhs + rhs)
+BROADCAST_BINARY_OP(Maximum, std::max(lhs, rhs), lhs.max(rhs))
+BROADCAST_BINARY_OP(Minimum, std::min(lhs, rhs), lhs.min(rhs))
+BROADCAST_BINARY_OP(Mul, lhs* rhs, lhs* rhs)
+BROADCAST_BINARY_OP(Sub, lhs - rhs, lhs - rhs)
+BROADCAST_BINARY_OP(SquaredDifference,
+ (lhs - rhs) * (lhs - rhs),
+ (lhs - rhs).square())
+
+// -----------------------------------------------------------------------------
+// Reduce ops
+// -----------------------------------------------------------------------------
+
+// We use macros instead of template functions with templated functors here
+// because it's a lot less verbose and easier for the compiler to optimize.
+#define REDUCE_OP(OP_NAME, DEFAULT_VALUE, UPDATE_EXPR, RESULT_EXPR) \
+ template <typename T, typename Tidx> \
+ void OP_NAME##InnerReduce( \
+ int32_t input_rank, const int32_t* __restrict input_shape, \
+ const T* __restrict input_values, int32_t index_tensor_rank, \
+ const int32_t* __restrict index_shape, \
+ const Tidx* __restrict index_values, T* __restrict output_values) { \
+ BENCHMARK_TIMER(#OP_NAME, "InnerReduce"); \
+ const int32_t inner_size = \
+ GetReduceInnerSize(input_rank, input_shape, index_tensor_rank, \
+ index_shape, index_values); \
+ const int32_t input_size = ShapeSize(input_rank, input_shape); \
+ const int32_t outer_size = input_size / inner_size; \
+ for (int32_t idx_out = 0; idx_out < outer_size; ++idx_out) { \
+ T value = DEFAULT_VALUE; \
+ for (int32_t idx_in = 0; idx_in < inner_size; ++idx_in) { \
+ const T prev = value; \
+ const T next = input_values[idx_out * inner_size + idx_in]; \
+ value = UPDATE_EXPR; \
+ } \
+ const T count = inner_size; \
+ (void)sizeof(count); \
+ output_values[idx_out] = RESULT_EXPR; \
+ } \
+ } \
+ template <typename T, typename Tidx> \
+ void OP_NAME##GenericReduceRank4( \
+ int32_t input_rank, const int32_t* __restrict input_shape, \
+ const T* __restrict input_values, int32_t index_tensor_rank, \
+ const int32_t* __restrict index_shape, \
+ const Tidx* __restrict index_values, T* __restrict output_values) { \
+ assert(input_rank == 4); \
+ assert(index_tensor_rank <= 1); \
+ BENCHMARK_TIMER(#OP_NAME, "GenericReduceRank4"); \
+ int out_shape[4] = {input_shape[0], input_shape[1], input_shape[2], \
+ input_shape[3]}; \
+ bool reduce_mask[4] = {false, false, false, false}; \
+ const int num_indices = index_tensor_rank > 0 ? index_shape[0] : 1; \
+ for (int i = 0; i < num_indices; ++i) { \
+ reduce_mask[index_values[i]] = true; \
+ out_shape[index_values[i]] = 1; \
+ } \
+ const int out_strides[4] = { \
+ reduce_mask[0] ? 0 : out_shape[1] * out_shape[2] * out_shape[3], \
+ reduce_mask[1] ? 0 : out_shape[2] * out_shape[3], \
+ reduce_mask[2] ? 0 : out_shape[3], reduce_mask[3] ? 0 : 1, \
+ }; \
+ const int output_size = ShapeSize(input_rank, out_shape); \
+ std::fill_n(output_values, output_size, DEFAULT_VALUE); \
+ for (int dim0 = 0; dim0 < input_shape[0]; ++dim0) { \
+ for (int dim1 = 0; dim1 < input_shape[1]; ++dim1) { \
+ for (int dim2 = 0; dim2 < input_shape[2]; ++dim2) { \
+ for (int dim3 = 0; dim3 < input_shape[3]; ++dim3, ++input_values) { \
+ T* out_ptr = output_values + out_strides[0] * dim0 + \
+ out_strides[1] * dim1 + out_strides[2] * dim2 + \
+ out_strides[3] * dim3; \
+ const T prev = *out_ptr; \
+ const T next = *input_values; \
+ *out_ptr = UPDATE_EXPR; \
+ } \
+ } \
+ } \
+ } \
+ const T count = (reduce_mask[0] ? input_shape[0] : 1) * \
+ (reduce_mask[1] ? input_shape[1] : 1) * \
+ (reduce_mask[2] ? input_shape[2] : 1) * \
+ (reduce_mask[3] ? input_shape[3] : 1); \
+ (void)sizeof(count); \
+ for (int i = 0; i < output_size; ++i) { \
+ const T value = output_values[i]; \
+ output_values[i] = RESULT_EXPR; \
+ } \
+ }
+
+REDUCE_OP(Max, std::numeric_limits<T>::lowest(), std::max(prev, next), value);
+REDUCE_OP(Sum, 0, prev + next, value);
+REDUCE_OP(Mean, 0, prev + next, value / count);
+
+// -----------------------------------------------------------------------------
+// Dequantize ops
+// -----------------------------------------------------------------------------
+
+template <typename T>
+void DequantizeMinCombined(const int32_t rank,
+ const int32_t* __restrict input_shape,
+ const T* __restrict input_values,
+ const float* __restrict min_range,
+ const float* __restrict max_range,
+ float* __restrict output_values) {
+ BENCHMARK_TIMER("DequantizeMinCombined");
+ const int size = ShapeSize(rank, input_shape);
+ const float offset =
+ std::is_signed<T>::value
+ ? (static_cast<float>(std::numeric_limits<T>::max()) -
+ std::numeric_limits<T>::min() + 1) /
+ 2.0f
+ : 0.0f;
+ const float range_scale = (max_range[0] - min_range[0]) /
+ (static_cast<float>(std::numeric_limits<T>::max()) -
+ std::numeric_limits<T>::min());
+ for (int i = 0; i < size; i++) {
+ output_values[i] =
+ ((static_cast<int32_t>(input_values[i]) + offset) * range_scale) +
+ min_range[0];
+ }
+}
+
+template <typename T>
+void DequantizeMinFirst(const int32_t rank,
+ const int32_t* __restrict input_shape,
+ const T* __restrict input_values,
+ const float* __restrict min_range,
+ const float* __restrict max_range,
+ float* __restrict output_values) {
+ BENCHMARK_TIMER("DequantizeMinFirst");
+ const int size = ShapeSize(rank, input_shape);
+ const float range_scale = (max_range[0] - min_range[0]) /
+ (static_cast<float>(std::numeric_limits<T>::max()) -
+ std::numeric_limits<T>::min());
+ const float range_min_rounded =
+ (max_range[0] == min_range[0]
+ ? min_range[0]
+ : round(min_range[0] / range_scale) * range_scale);
+ for (int i = 0; i < size; i++) {
+ output_values[i] = ((static_cast<int32_t>(input_values[i]) -
+ std::numeric_limits<T>::min()) *
+ range_scale) +
+ range_min_rounded;
+ }
+}
+
+// -----------------------------------------------------------------------------
+// CONSTANTS
+// Note that for now, endianness of the target machine needs to match that of
+// the one training was performed on.
+// -----------------------------------------------------------------------------
+const int32_t dnn_hiddenlayer_0_weights_part_0_shape[2] = {4, 10};
+const union {
+ uint8_t bytes[160];
+ float values[40];
+} dnn_hiddenlayer_0_weights_part_0 = {{
+ 0xbc, 0x22, 0x0a, 0xbf, 0xb4, 0x46, 0x8c, 0x3f, 0xba, 0x31, 0x34, 0xbe,
+ 0x4c, 0x65, 0xdb, 0xbe, 0xf0, 0x54, 0x5e, 0xbe, 0xc1, 0x5d, 0xb3, 0x3f,
+ 0xf4, 0xe6, 0x15, 0xbf, 0x05, 0xc6, 0x34, 0xbf, 0xc0, 0x37, 0x7e, 0xbd,
+ 0x6c, 0x35, 0x0b, 0xbf, 0xca, 0x53, 0x26, 0xbf, 0x58, 0xb4, 0x87, 0x3f,
+ 0x37, 0xee, 0x39, 0xbf, 0xda, 0xfa, 0xf9, 0xbe, 0x97, 0xc1, 0x06, 0xbf,
+ 0xf9, 0x4e, 0x81, 0x3f, 0xb2, 0x44, 0x85, 0xbf, 0x7f, 0x98, 0x7c, 0x3d,
+ 0x15, 0x26, 0xbc, 0xbe, 0x5c, 0x48, 0x05, 0x3f, 0xc8, 0xaa, 0xa1, 0xbd,
+ 0x35, 0xb3, 0x43, 0xbe, 0xeb, 0x46, 0x91, 0x3f, 0x80, 0x71, 0xe3, 0x3c,
+ 0xd1, 0x98, 0x79, 0x3f, 0x3c, 0xd0, 0x0d, 0xbf, 0x1e, 0x02, 0xd3, 0x3e,
+ 0x5d, 0x4b, 0xa2, 0xbf, 0x68, 0xac, 0xaa, 0xbd, 0xf8, 0xe1, 0x75, 0x3e,
+ 0x4a, 0x9c, 0x27, 0xbe, 0xf8, 0xae, 0xb2, 0xbe, 0x7f, 0x9d, 0x91, 0x3f,
+ 0x1e, 0x8b, 0xa8, 0xbe, 0x35, 0x7e, 0xb2, 0x3f, 0xbe, 0x8c, 0xd3, 0xbe,
+ 0xf9, 0xcd, 0xb5, 0x3f, 0xa1, 0x50, 0xaa, 0x3f, 0xe4, 0x6d, 0xdd, 0xbe,
+ 0x0d, 0xce, 0xd3, 0xbe,
+}};
+const int32_t dnn_hiddenlayer_0_biases_part_0_shape[1] = {10};
+const union {
+ uint8_t bytes[40];
+ float values[10];
+} dnn_hiddenlayer_0_biases_part_0 = {{
+ 0x00, 0x00, 0x00, 0x00, 0xbf, 0x6a, 0x53, 0x3e, 0xd3, 0xc1,
+ 0xd0, 0x3e, 0x00, 0x00, 0x00, 0x00, 0xb6, 0xd8, 0xc0, 0x3e,
+ 0xca, 0xe7, 0x35, 0x3e, 0x23, 0xa5, 0x44, 0x3f, 0x61, 0xfd,
+ 0xd2, 0x3e, 0x00, 0x00, 0x00, 0x00, 0xb6, 0xe0, 0x43, 0x3c,
+}};
+const int32_t dnn_logits_biases_part_0_shape[1] = {1};
+const union {
+ uint8_t bytes[4];
+ float values[1];
+} dnn_logits_biases_part_0 = {{
+ 0x75, 0xca, 0xd7, 0xbe,
+}};
+const int32_t dnn_logits_weights_part_0_shape[2] = {10, 1};
+const union {
+ uint8_t bytes[40];
+ float values[10];
+} dnn_logits_weights_part_0 = {{
+ 0x13, 0x12, 0x39, 0x3f, 0xf3, 0xa5, 0xc2, 0xbf, 0x81, 0x7f,
+ 0xbe, 0x3f, 0xf8, 0x17, 0x26, 0x3e, 0xa4, 0x19, 0xa6, 0x3f,
+ 0xf0, 0xc9, 0xb7, 0xbf, 0x6a, 0x99, 0xd2, 0x3f, 0x8a, 0x7d,
+ 0xe9, 0x3f, 0x83, 0x9a, 0x3a, 0xbf, 0xf1, 0x6c, 0x08, 0x3e,
+}};
+
+} // anonymous namespace
+
+// -----------------------------------------------------------------------------
+// INFERENCE
+// -----------------------------------------------------------------------------
+
+int32_t input0Shape[2] = {1, 4};
+int32_t logits_MatMul_merged_with_dnn_logits_BiasAdd0Shape[2] = {1, 1};
+
+void Inference(
+ const float* __restrict input0 /* shape: 1,4 */,
+ float* __restrict logits_MatMul_merged_with_dnn_logits_BiasAdd0 /* shape:
+ 1,1 */
+ ,
+ FixedAllocations* __restrict fixed) {
+ const int32_t input0_shape[] = {1, 4};
+ int32_t logits_MatMul_merged_with_dnn_logits_BiasAdd0_shape[2];
+
+ // dnn/hiddenlayer_0/MatMul_merged_with_dnn/hiddenlayer_0/BiasAdd
+ FullyConnected<float>(input0_shape, input0,
+ dnn_hiddenlayer_0_weights_part_0_shape,
+ dnn_hiddenlayer_0_weights_part_0.values,
+ dnn_hiddenlayer_0_biases_part_0_shape,
+ dnn_hiddenlayer_0_biases_part_0.values, fixed->alloc0);
+ fixed->alloc0_shape[0] = 1;
+ fixed->alloc0_shape[1] = 10;
+
+ // dnn/hiddenlayer_0/hiddenlayer_0/Relu
+ Relu<float>(2, // rank
+ fixed->alloc0_shape, fixed->alloc0, fixed->alloc1);
+ fixed->alloc1_shape[0] = 1;
+ fixed->alloc1_shape[1] = 10;
+
+ // dnn/logits/MatMul_merged_with_dnn/logits/BiasAdd
+ FullyConnected<float>(
+ fixed->alloc1_shape, fixed->alloc1, dnn_logits_weights_part_0_shape,
+ dnn_logits_weights_part_0.values, dnn_logits_biases_part_0_shape,
+ dnn_logits_biases_part_0.values,
+ logits_MatMul_merged_with_dnn_logits_BiasAdd0);
+ logits_MatMul_merged_with_dnn_logits_BiasAdd0_shape[0] = 1;
+ logits_MatMul_merged_with_dnn_logits_BiasAdd0_shape[1] = 1;
+}
+
+} // namespace highcontrast_tfnative_model
diff --git a/chromium/third_party/blink/renderer/platform/graphics/highcontrast/highcontrast_classifier.h b/chromium/third_party/blink/renderer/platform/graphics/highcontrast/highcontrast_classifier.h
new file mode 100644
index 00000000000..2e649cec133
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/highcontrast/highcontrast_classifier.h
@@ -0,0 +1,34 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_HIGHCONTRAST_HIGHCONTRAST_CLASSIFIER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_HIGHCONTRAST_HIGHCONTRAST_CLASSIFIER_H_
+
+// This file is automatically generated using tfNative from a neural network,
+// trained by TensorFlow. Please do not edit.
+
+#include <cstdint>
+
+namespace highcontrast_tfnative_model {
+
+struct alignas(16) FixedAllocations {
+ float alloc0[10];
+ int32_t alloc0_shape[2];
+ float alloc1[10];
+ int32_t alloc1_shape[2];
+};
+
+extern int32_t input0Shape[2];
+extern int32_t logits_MatMul_merged_with_dnn_logits_BiasAdd0Shape[2];
+
+void Inference(
+ const float* __restrict input0 /* shape: 1,4 */,
+ float* __restrict logits_MatMul_merged_with_dnn_logits_BiasAdd0 /* shape:
+ 1,1 */
+ ,
+ FixedAllocations* __restrict fixed);
+
+} // namespace highcontrast_tfnative_model
+
+#endif // highcontrast_tfnative_model
diff --git a/chromium/third_party/blink/renderer/platform/graphics/image.cc b/chromium/third_party/blink/renderer/platform/graphics/image.cc
new file mode 100644
index 00000000000..e5ebd15b343
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/image.cc
@@ -0,0 +1,470 @@
+/*
+ * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
+ * Copyright (C) 2004, 2005, 2006 Apple Computer, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/graphics/image.h"
+
+#include "build/build_config.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_data.h"
+#include "third_party/blink/renderer/platform/geometry/float_point.h"
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/geometry/float_size.h"
+#include "third_party/blink/renderer/platform/graphics/bitmap_image.h"
+#include "third_party/blink/renderer/platform/graphics/deferred_image_decoder.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_image.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_recorder.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_shader.h"
+#include "third_party/blink/renderer/platform/graphics/scoped_interpolation_quality.h"
+#include "third_party/blink/renderer/platform/histogram.h"
+#include "third_party/blink/renderer/platform/instrumentation/platform_instrumentation.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/length.h"
+#include "third_party/blink/renderer/platform/network/mime/mime_type_registry.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/wtf/checked_numeric.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/skia/include/core/SkImage.h"
+
+#include <math.h>
+#include <tuple>
+
+namespace blink {
+namespace {
+
+bool HasCheckerableDimensions(const IntSize& size) {
+ CheckedNumeric<size_t> checked_size = 4u;
+ checked_size *= size.Width();
+ checked_size *= size.Height();
+
+// The constants used here should remain consistent with the values used for
+// LayerTreeSettings in render_widget_compositor.cc.
+#if defined(OS_ANDROID)
+ static const size_t kMinImageSizeCheckered = 512 * 1024;
+#else
+ static const size_t kMinImageSizeCheckered = 1 * 1024 * 1024;
+#endif
+
+ return checked_size.ValueOrDefault(std::numeric_limits<size_t>::max()) >=
+ kMinImageSizeCheckered;
+}
+
+} // namespace
+
+Image::Image(ImageObserver* observer, bool is_multipart)
+ : image_observer_disabled_(false),
+ image_observer_(observer),
+ stable_image_id_(PaintImage::GetNextId()),
+ is_multipart_(is_multipart),
+ high_contrast_classification_(
+ HighContrastClassification::kNotClassified) {}
+
+Image::~Image() = default;
+
+Image* Image::NullImage() {
+ DCHECK(IsMainThread());
+ DEFINE_STATIC_REF(Image, null_image, (BitmapImage::Create()));
+ return null_image;
+}
+
+scoped_refptr<Image> Image::LoadPlatformResource(const char* name) {
+ const WebData& resource = Platform::Current()->GetDataResource(name);
+ if (resource.IsEmpty())
+ return Image::NullImage();
+
+ scoped_refptr<Image> image = BitmapImage::Create();
+ image->SetData(resource, true);
+ return image;
+}
+
+bool Image::SupportsType(const String& type) {
+ return MIMETypeRegistry::IsSupportedImageResourceMIMEType(type);
+}
+
+Image::SizeAvailability Image::SetData(scoped_refptr<SharedBuffer> data,
+ bool all_data_received) {
+ encoded_image_data_ = std::move(data);
+ if (!encoded_image_data_.get())
+ return kSizeAvailable;
+
+ int length = encoded_image_data_->size();
+ if (!length)
+ return kSizeAvailable;
+
+ return DataChanged(all_data_received);
+}
+
+String Image::FilenameExtension() const {
+ return String();
+}
+
+void Image::DrawTiledBackground(GraphicsContext& ctxt,
+ const FloatRect& dest_rect,
+ const FloatPoint& src_point,
+ const FloatSize& scaled_tile_size,
+ SkBlendMode op,
+ const FloatSize& repeat_spacing) {
+ if (scaled_tile_size.IsEmpty())
+ return;
+
+ FloatSize intrinsic_tile_size(Size());
+ if (HasRelativeSize()) {
+ intrinsic_tile_size.SetWidth(scaled_tile_size.Width());
+ intrinsic_tile_size.SetHeight(scaled_tile_size.Height());
+ }
+
+ const FloatSize scale(
+ scaled_tile_size.Width() / intrinsic_tile_size.Width(),
+ scaled_tile_size.Height() / intrinsic_tile_size.Height());
+
+ const FloatRect one_tile_rect = ComputeTileContaining(
+ dest_rect.Location(), scaled_tile_size, src_point, repeat_spacing);
+
+ // Check and see if a single draw of the image can cover the entire area we
+ // are supposed to tile.
+ if (one_tile_rect.Contains(dest_rect)) {
+ const FloatRect visible_src_rect =
+ ComputeSubsetForTile(one_tile_rect, dest_rect, intrinsic_tile_size);
+ ctxt.DrawImage(this, kSyncDecode, dest_rect, &visible_src_rect, op,
+ kDoNotRespectImageOrientation);
+ return;
+ }
+
+ FloatRect tile_rect(FloatPoint(), intrinsic_tile_size);
+ DrawPattern(ctxt, tile_rect, scale, one_tile_rect.Location(), op, dest_rect,
+ repeat_spacing);
+
+ StartAnimation();
+}
+
+void Image::DrawTiledBorder(GraphicsContext& ctxt,
+ const FloatRect& dst_rect,
+ const FloatRect& src_rect,
+ const FloatSize& provided_tile_scale_factor,
+ TileRule h_rule,
+ TileRule v_rule,
+ SkBlendMode op) {
+ // TODO(cavalcantii): see crbug.com/662513.
+ FloatSize tile_scale_factor = provided_tile_scale_factor;
+ if (v_rule == kRoundTile) {
+ float v_repetitions = std::max(
+ 1.0f, roundf(dst_rect.Height() /
+ (tile_scale_factor.Height() * src_rect.Height())));
+ tile_scale_factor.SetHeight(dst_rect.Height() /
+ (src_rect.Height() * v_repetitions));
+ }
+
+ if (h_rule == kRoundTile) {
+ float h_repetitions =
+ std::max(1.0f, roundf(dst_rect.Width() /
+ (tile_scale_factor.Width() * src_rect.Width())));
+ tile_scale_factor.SetWidth(dst_rect.Width() /
+ (src_rect.Width() * h_repetitions));
+ }
+
+ // We want to construct the phase such that the pattern is centered (when
+ // stretch is not set for a particular rule).
+ float v_phase = tile_scale_factor.Height() * src_rect.Y();
+ float h_phase = tile_scale_factor.Width() * src_rect.X();
+ if (v_rule == kRepeatTile) {
+ float scaled_tile_height = tile_scale_factor.Height() * src_rect.Height();
+ v_phase -= (dst_rect.Height() - scaled_tile_height) / 2;
+ }
+
+ if (h_rule == kRepeatTile) {
+ float scaled_tile_width = tile_scale_factor.Width() * src_rect.Width();
+ h_phase -= (dst_rect.Width() - scaled_tile_width) / 2;
+ }
+
+ FloatSize spacing;
+ auto calculate_space_needed =
+ [](const float destination,
+ const float source) -> std::tuple<bool, float> {
+ DCHECK_GT(source, 0);
+ DCHECK_GT(destination, 0);
+
+ float repeat_tiles_count = floorf(destination / source);
+ if (!repeat_tiles_count)
+ return std::make_tuple(false, -1);
+
+ float space = destination;
+ space -= source * repeat_tiles_count;
+ space /= repeat_tiles_count + 1.0;
+
+ return std::make_tuple(true, space);
+ };
+
+ if (v_rule == kSpaceTile) {
+ std::tuple<bool, float> space =
+ calculate_space_needed(dst_rect.Height(), src_rect.Height());
+ if (!std::get<0>(space))
+ return;
+
+ spacing.SetHeight(std::get<1>(space));
+ tile_scale_factor.SetHeight(1.0);
+ v_phase = src_rect.Y();
+ v_phase -= spacing.Height();
+ }
+
+ if (h_rule == kSpaceTile) {
+ std::tuple<bool, float> space =
+ calculate_space_needed(dst_rect.Width(), src_rect.Width());
+ if (!std::get<0>(space))
+ return;
+
+ spacing.SetWidth(std::get<1>(space));
+ tile_scale_factor.SetWidth(1.0);
+ h_phase = src_rect.X();
+ h_phase -= spacing.Width();
+ }
+
+ FloatPoint pattern_phase(dst_rect.X() - h_phase, dst_rect.Y() - v_phase);
+
+ // TODO(cavalcantii): see crbug.com/662507.
+ if ((h_rule == kRoundTile) || (v_rule == kRoundTile)) {
+ ScopedInterpolationQuality interpolation_quality_scope(ctxt,
+ kInterpolationLow);
+ DrawPattern(ctxt, src_rect, tile_scale_factor, pattern_phase, op, dst_rect,
+ FloatSize());
+ } else {
+ DrawPattern(ctxt, src_rect, tile_scale_factor, pattern_phase, op, dst_rect,
+ spacing);
+ }
+
+ StartAnimation();
+}
+
+namespace {
+
+sk_sp<PaintShader> CreatePatternShader(const PaintImage& image,
+ const SkMatrix& shader_matrix,
+ const PaintFlags& paint,
+ const FloatSize& spacing,
+ SkShader::TileMode tmx,
+ SkShader::TileMode tmy) {
+ if (spacing.IsZero()) {
+ return PaintShader::MakeImage(image, tmx, tmy, &shader_matrix);
+ }
+
+ // Arbitrary tiling is currently only supported for SkPictureShader, so we use
+ // that instead of a plain bitmap shader to implement spacing.
+ const SkRect tile_rect = SkRect::MakeWH(image.width() + spacing.Width(),
+ image.height() + spacing.Height());
+
+ PaintRecorder recorder;
+ PaintCanvas* canvas = recorder.beginRecording(tile_rect);
+ canvas->drawImage(image, 0, 0, &paint);
+
+ return PaintShader::MakePaintRecord(recorder.finishRecordingAsPicture(),
+ tile_rect, tmx, tmy, &shader_matrix);
+}
+
+SkShader::TileMode ComputeTileMode(float left,
+ float right,
+ float min,
+ float max) {
+ DCHECK(left < right);
+ return left >= min && right <= max ? SkShader::kClamp_TileMode
+ : SkShader::kRepeat_TileMode;
+}
+
+} // anonymous namespace
+
+void Image::DrawPattern(GraphicsContext& context,
+ const FloatRect& float_src_rect,
+ const FloatSize& scale,
+ const FloatPoint& phase,
+ SkBlendMode composite_op,
+ const FloatRect& dest_rect,
+ const FloatSize& repeat_spacing) {
+ TRACE_EVENT0("skia", "Image::drawPattern");
+
+ PaintImage image = PaintImageForCurrentFrame();
+ if (!image)
+ return;
+
+ FloatRect norm_src_rect = float_src_rect;
+
+ norm_src_rect.Intersect(FloatRect(0, 0, image.width(), image.height()));
+ if (dest_rect.IsEmpty() || norm_src_rect.IsEmpty())
+ return; // nothing to draw
+
+ SkMatrix local_matrix;
+ // We also need to translate it such that the origin of the pattern is the
+ // origin of the destination rect, which is what WebKit expects. Skia uses
+ // the coordinate system origin as the base for the pattern. If WebKit wants
+ // a shifted image, it will shift it from there using the localMatrix.
+ const float adjusted_x = phase.X() + norm_src_rect.X() * scale.Width();
+ const float adjusted_y = phase.Y() + norm_src_rect.Y() * scale.Height();
+ local_matrix.setTranslate(SkFloatToScalar(adjusted_x),
+ SkFloatToScalar(adjusted_y));
+
+ // Because no resizing occurred, the shader transform should be
+ // set to the pattern's transform, which just includes scale.
+ local_matrix.preScale(scale.Width(), scale.Height());
+
+ // Fetch this now as subsetting may swap the image.
+ auto image_id = image.GetSkImage()->uniqueID();
+
+ image = PaintImageBuilder::WithCopy(std::move(image))
+ .make_subset(EnclosingIntRect(norm_src_rect))
+ .TakePaintImage();
+ if (!image)
+ return;
+
+ const FloatSize tile_size(
+ image.width() * scale.Width() + repeat_spacing.Width(),
+ image.height() * scale.Height() + repeat_spacing.Height());
+ const auto tmx = ComputeTileMode(dest_rect.X(), dest_rect.MaxX(), adjusted_x,
+ adjusted_x + tile_size.Width());
+ const auto tmy = ComputeTileMode(dest_rect.Y(), dest_rect.MaxY(), adjusted_y,
+ adjusted_y + tile_size.Height());
+
+ PaintFlags flags = context.FillFlags();
+ flags.setColor(SK_ColorBLACK);
+ flags.setBlendMode(composite_op);
+ flags.setFilterQuality(
+ context.ComputeFilterQuality(this, dest_rect, norm_src_rect));
+ flags.setAntiAlias(context.ShouldAntialias());
+ flags.setShader(
+ CreatePatternShader(image, local_matrix, flags,
+ FloatSize(repeat_spacing.Width() / scale.Width(),
+ repeat_spacing.Height() / scale.Height()),
+ tmx, tmy));
+ // If the shader could not be instantiated (e.g. non-invertible matrix),
+ // draw transparent.
+ // Note: we can't simply bail, because of arbitrary blend mode.
+ if (!flags.HasShader())
+ flags.setColor(SK_ColorTRANSPARENT);
+
+ context.DrawRect(dest_rect, flags);
+
+ if (CurrentFrameIsLazyDecoded())
+ PlatformInstrumentation::DidDrawLazyPixelRef(image_id);
+}
+
+scoped_refptr<Image> Image::ImageForDefaultFrame() {
+ scoped_refptr<Image> image(this);
+
+ return image;
+}
+
+PaintImageBuilder Image::CreatePaintImageBuilder() {
+ auto animation_type = MaybeAnimated() ? PaintImage::AnimationType::ANIMATED
+ : PaintImage::AnimationType::STATIC;
+ return PaintImageBuilder::WithDefault()
+ .set_id(stable_image_id_)
+ .set_animation_type(animation_type)
+ .set_is_multipart(is_multipart_);
+}
+
+bool Image::ApplyShader(PaintFlags& flags, const SkMatrix& local_matrix) {
+ // Default shader impl: attempt to build a shader based on the current frame
+ // SkImage.
+ PaintImage image = PaintImageForCurrentFrame();
+ if (!image)
+ return false;
+
+ flags.setShader(PaintShader::MakeImage(image, SkShader::kRepeat_TileMode,
+ SkShader::kRepeat_TileMode,
+ &local_matrix));
+ if (!flags.HasShader())
+ return false;
+
+ // Animation is normally refreshed in draw() impls, which we don't call when
+ // painting via shaders.
+ StartAnimation();
+
+ return true;
+}
+
+FloatRect Image::ComputeTileContaining(const FloatPoint& point,
+ const FloatSize& tile_size,
+ const FloatPoint& tile_phase,
+ const FloatSize& tile_spacing) {
+ const FloatSize actual_tile_size(tile_size + tile_spacing);
+ return FloatRect(
+ FloatPoint(
+ point.X() + fmodf(fmodf(-tile_phase.X(), actual_tile_size.Width()) -
+ actual_tile_size.Width(),
+ actual_tile_size.Width()),
+ point.Y() + fmodf(fmodf(-tile_phase.Y(), actual_tile_size.Height()) -
+ actual_tile_size.Height(),
+ actual_tile_size.Height())),
+ tile_size);
+}
+
+FloatRect Image::ComputeSubsetForTile(const FloatRect& tile,
+ const FloatRect& dest,
+ const FloatSize& image_size) {
+ DCHECK(tile.Contains(dest));
+
+ const FloatSize scale(tile.Width() / image_size.Width(),
+ tile.Height() / image_size.Height());
+
+ FloatRect subset = dest;
+ subset.SetX((dest.X() - tile.X()) / scale.Width());
+ subset.SetY((dest.Y() - tile.Y()) / scale.Height());
+ subset.SetWidth(dest.Width() / scale.Width());
+ subset.SetHeight(dest.Height() / scale.Height());
+
+ return subset;
+}
+
+// static
+void Image::RecordCheckerableImageUMA(Image& image, ImageType type) {
+ // This enum is used in UMA counting so should be treated as append only.
+ // Please keep it in sync with CheckerableImageType in enums.xml.
+ enum class CheckerableImageType {
+ kCheckerableImg = 0,
+ kCheckerableSvg = 1,
+ kCheckerableCss = 2,
+ kNotCheckerable = 3,
+ kCount = 4
+ };
+
+ CheckerableImageType checkerable_type = CheckerableImageType::kNotCheckerable;
+ if (image.IsSizeAvailable() && !image.MaybeAnimated() &&
+ HasCheckerableDimensions(image.Size())) {
+ switch (type) {
+ case ImageType::kImg:
+ checkerable_type = CheckerableImageType::kCheckerableImg;
+ break;
+ case ImageType::kSvg:
+ checkerable_type = CheckerableImageType::kCheckerableSvg;
+ break;
+ case ImageType::kCss:
+ checkerable_type = CheckerableImageType::kCheckerableCss;
+ break;
+ }
+ }
+
+ UMA_HISTOGRAM_ENUMERATION("Blink.CheckerableImageCount", checkerable_type,
+ CheckerableImageType::kCount);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/image.h b/chromium/third_party/blink/renderer/platform/graphics/image.h
new file mode 100644
index 00000000000..a952174716a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/image.h
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
+ * Copyright (C) 2004, 2005, 2006 Apple Computer, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_IMAGE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_IMAGE_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_types.h"
+#include "third_party/blink/renderer/platform/graphics/image_animation_policy.h"
+#include "third_party/blink/renderer/platform/graphics/image_observer.h"
+#include "third_party/blink/renderer/platform/graphics/image_orientation.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_image.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_record.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+class SkMatrix;
+
+namespace cc {
+class PaintCanvas;
+class PaintFlags;
+} // namespace cc
+
+namespace blink {
+
+class FloatPoint;
+class FloatRect;
+class FloatSize;
+class GraphicsContext;
+class Image;
+class KURL;
+class WebGraphicsContext3DProvider;
+class WebGraphicsContext3DProviderWrapper;
+
+using cc::PaintCanvas;
+using cc::PaintFlags;
+
+class PLATFORM_EXPORT Image : public ThreadSafeRefCounted<Image> {
+ friend class GeneratedImage;
+ friend class CrossfadeGeneratedImage;
+ friend class GradientGeneratedImage;
+ friend class GraphicsContext;
+ WTF_MAKE_NONCOPYABLE(Image);
+
+ public:
+ virtual ~Image();
+
+ static scoped_refptr<Image> LoadPlatformResource(const char* name);
+ static bool SupportsType(const String&);
+
+ virtual bool IsSVGImage() const { return false; }
+ virtual bool IsBitmapImage() const { return false; }
+ virtual bool IsStaticBitmapImage() const { return false; }
+ virtual bool IsPlaceholderImage() const { return false; }
+
+ virtual bool CurrentFrameKnownToBeOpaque() = 0;
+
+ virtual bool CurrentFrameIsComplete() { return false; }
+ virtual bool CurrentFrameIsLazyDecoded() { return false; }
+ virtual size_t FrameCount() { return 0; }
+ virtual bool IsTextureBacked() const { return false; }
+
+ // Derived classes should override this if they can assure that the current
+ // image frame contains only resources from its own security origin.
+ virtual bool CurrentFrameHasSingleSecurityOrigin() const { return false; }
+
+ static Image* NullImage();
+ bool IsNull() const { return Size().IsEmpty(); }
+
+ virtual bool UsesContainerSize() const { return false; }
+ virtual bool HasRelativeSize() const { return false; }
+
+ virtual IntSize Size() const = 0;
+ IntRect Rect() const { return IntRect(IntPoint(), Size()); }
+ int width() const { return Size().Width(); }
+ int height() const { return Size().Height(); }
+ virtual bool GetHotSpot(IntPoint&) const { return false; }
+
+ enum SizeAvailability {
+ kSizeUnavailable,
+ kSizeAvailableAndLoadingAsynchronously,
+ kSizeAvailable,
+ };
+
+ // If SetData() returns |kSizeAvailableAndLoadingAsynchronously|:
+ // Image loading is continuing asynchronously
+ // (only when |this| is SVGImage and |all_data_received| is true), and
+ // ImageResourceObserver::AsyncLoadCompleted() is called when finished.
+ // Otherwise:
+ // Image loading is completed synchronously.
+ // ImageResourceObserver::AsyncLoadCompleted() is not called.
+ virtual SizeAvailability SetData(scoped_refptr<SharedBuffer> data,
+ bool all_data_received);
+ virtual SizeAvailability DataChanged(bool /*all_data_received*/) {
+ return kSizeUnavailable;
+ }
+
+ // null string if unknown
+ virtual String FilenameExtension() const;
+
+ virtual void DestroyDecodedData() = 0;
+
+ virtual scoped_refptr<SharedBuffer> Data() { return encoded_image_data_; }
+
+ // Animation begins whenever someone draws the image, so startAnimation() is
+ // not normally called. It will automatically pause once all observers no
+ // longer want to render the image anywhere.
+ virtual void StartAnimation() {}
+ virtual void ResetAnimation() {}
+
+ // True if this image can potentially animate.
+ virtual bool MaybeAnimated() { return false; }
+
+ // Set animationPolicy
+ virtual void SetAnimationPolicy(ImageAnimationPolicy) {}
+ virtual ImageAnimationPolicy AnimationPolicy() {
+ return kImageAnimationPolicyAllowed;
+ }
+
+ // Advances an animated image. For BitmapImage (e.g., animated gifs) this
+ // will advance to the next frame. For SVGImage, this will trigger an
+ // animation update for CSS and advance the SMIL timeline by one frame.
+ virtual void AdvanceAnimationForTesting() {}
+
+ // Typically the ImageResourceContent that owns us.
+ ImageObserver* GetImageObserver() const {
+ return image_observer_disabled_ ? nullptr : image_observer_;
+ }
+ void ClearImageObserver() { image_observer_ = nullptr; }
+ // To avoid interleaved accesses to |m_imageObserverDisabled|, do not call
+ // setImageObserverDisabled() other than from ImageObserverDisabler.
+ void SetImageObserverDisabled(bool disabled) {
+ image_observer_disabled_ = disabled;
+ }
+
+ enum TileRule { kStretchTile, kRoundTile, kSpaceTile, kRepeatTile };
+
+ virtual scoped_refptr<Image> ImageForDefaultFrame();
+
+ enum ImageDecodingMode {
+ // No preference specified.
+ kUnspecifiedDecode,
+ // Prefer to display the image synchronously with the rest of the content
+ // updates.
+ kSyncDecode,
+ // Prefer to display the image asynchronously with the rest of the content
+ // updates.
+ kAsyncDecode
+ };
+
+ static PaintImage::DecodingMode ToPaintImageDecodingMode(
+ ImageDecodingMode mode) {
+ switch (mode) {
+ case kUnspecifiedDecode:
+ return PaintImage::DecodingMode::kUnspecified;
+ case kSyncDecode:
+ return PaintImage::DecodingMode::kSync;
+ case kAsyncDecode:
+ return PaintImage::DecodingMode::kAsync;
+ }
+
+ NOTREACHED();
+ return PaintImage::DecodingMode::kUnspecified;
+ }
+
+ virtual PaintImage PaintImageForCurrentFrame() = 0;
+
+ enum ImageClampingMode {
+ kClampImageToSourceRect,
+ kDoNotClampImageToSourceRect
+ };
+
+ virtual void Draw(PaintCanvas*,
+ const PaintFlags&,
+ const FloatRect& dst_rect,
+ const FloatRect& src_rect,
+ RespectImageOrientationEnum,
+ ImageClampingMode,
+ ImageDecodingMode) = 0;
+
+ virtual bool ApplyShader(PaintFlags&, const SkMatrix& local_matrix);
+
+ // Use ContextProvider() for immediate use only, use
+ // ContextProviderWrapper() to obtain a retainable reference. Note:
+ // Implemented only in sub-classes that use the GPU.
+ virtual WebGraphicsContext3DProvider* ContextProvider() const {
+ return nullptr;
+ }
+ virtual base::WeakPtr<WebGraphicsContext3DProviderWrapper>
+ ContextProviderWrapper() const {
+ return nullptr;
+ }
+
+ // Compute the tile which contains a given point (assuming a repeating tile
+ // grid). The point and returned value are in destination grid space.
+ static FloatRect ComputeTileContaining(const FloatPoint&,
+ const FloatSize& tile_size,
+ const FloatPoint& tile_phase,
+ const FloatSize& tile_spacing);
+
+ // Compute the image subset which gets mapped onto |dest|, when the whole
+ // image is drawn into |tile|. Assumes |tile| contains |dest|. The tile rect
+ // is in destination grid space while the return value is in image coordinate
+ // space.
+ static FloatRect ComputeSubsetForTile(const FloatRect& tile,
+ const FloatRect& dest,
+ const FloatSize& image_size);
+
+ enum class ImageType { kImg, kSvg, kCss };
+ static void RecordCheckerableImageUMA(Image&, ImageType);
+
+ virtual sk_sp<PaintRecord> PaintRecordForContainer(
+ const KURL& url,
+ const IntSize& container_size,
+ const IntRect& draw_src_rect,
+ const IntRect& draw_dst_rect,
+ bool flip_y) {
+ return nullptr;
+ }
+
+ HighContrastClassification GetHighContrastClassification() {
+ return high_contrast_classification_;
+ }
+
+ // High contrast classification result is cached to be consistent and have
+ // higher performance for future paints.
+ void SetHighContrastClassification(
+ const HighContrastClassification high_contrast_classification) {
+ high_contrast_classification_ = high_contrast_classification;
+ }
+
+ PaintImage::Id paint_image_id() const { return stable_image_id_; }
+
+ protected:
+ Image(ImageObserver* = nullptr, bool is_multipart = false);
+
+ void DrawTiledBackground(GraphicsContext&,
+ const FloatRect& dst_rect,
+ const FloatPoint& src_point,
+ const FloatSize& tile_size,
+ SkBlendMode,
+ const FloatSize& repeat_spacing);
+ void DrawTiledBorder(GraphicsContext&,
+ const FloatRect& dst_rect,
+ const FloatRect& src_rect,
+ const FloatSize& tile_scale_factor,
+ TileRule h_rule,
+ TileRule v_rule,
+ SkBlendMode);
+
+ virtual void DrawPattern(GraphicsContext&,
+ const FloatRect&,
+ const FloatSize&,
+ const FloatPoint& phase,
+ SkBlendMode,
+ const FloatRect&,
+ const FloatSize& repeat_spacing);
+
+ // Creates and initializes a PaintImageBuilder with the metadata flags for the
+ // PaintImage.
+ PaintImageBuilder CreatePaintImageBuilder();
+
+ // Whether or not size is available yet.
+ virtual bool IsSizeAvailable() { return true; }
+
+ private:
+ bool image_observer_disabled_;
+ scoped_refptr<SharedBuffer> encoded_image_data_;
+ // TODO(Oilpan): consider having Image on the Oilpan heap and
+ // turn this into a Member<>.
+ //
+ // The observer (an ImageResourceContent) is responsible for clearing
+ // itself out when it switches to another Image.
+ // When the ImageResourceContent is garbage collected while Image is still
+ // alive, |image_observer_| is cleared by WeakPersistent mechanism.
+ WeakPersistent<ImageObserver> image_observer_;
+ PaintImage::Id stable_image_id_;
+ const bool is_multipart_;
+ HighContrastClassification high_contrast_classification_;
+};
+
+#define DEFINE_IMAGE_TYPE_CASTS(typeName) \
+ DEFINE_TYPE_CASTS(typeName, Image, image, image->Is##typeName(), \
+ image.Is##typeName())
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/graphics/image_animation_policy.h b/chromium/third_party/blink/renderer/platform/graphics/image_animation_policy.h
new file mode 100644
index 00000000000..5398f0cc7ef
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/image_animation_policy.h
@@ -0,0 +1,24 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_IMAGE_ANIMATION_POLICY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_IMAGE_ANIMATION_POLICY_H_
+
+namespace blink {
+
+// ImageAnimationPolicy is used for controlling image animation
+// when image frame is rendered for animation
+
+enum ImageAnimationPolicy {
+ // Animate the image (the default).
+ kImageAnimationPolicyAllowed,
+ // Animate image just once.
+ kImageAnimationPolicyAnimateOnce,
+ // Show the first frame and do not animate.
+ kImageAnimationPolicyNoAnimation
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_IMAGE_ANIMATION_POLICY_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/image_data_buffer.cc b/chromium/third_party/blink/renderer/platform/graphics/image_data_buffer.cc
new file mode 100644
index 00000000000..f6a1acd85a2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/image_data_buffer.cc
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2008, Google Inc. All rights reserved.
+ * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
+ * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. 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 "third_party/blink/renderer/platform/graphics/image_data_buffer.h"
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
+#include "third_party/blink/renderer/platform/image-encoders/image_encoder.h"
+#include "third_party/blink/renderer/platform/network/mime/mime_type_registry.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/wtf/text/base64.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "third_party/skia/include/core/SkSwizzle.h"
+#include "third_party/skia/include/encode/SkJpegEncoder.h"
+
+namespace blink {
+
+ImageDataBuffer::ImageDataBuffer(scoped_refptr<StaticBitmapImage> image) {
+ if (!image)
+ return;
+ retained_image_ = image->PaintImageForCurrentFrame().GetSkImage();
+ if (!retained_image_)
+ return;
+ if (retained_image_->isTextureBacked() ||
+ retained_image_->isLazyGenerated() ||
+ retained_image_->alphaType() != kUnpremul_SkAlphaType) {
+ // Unpremul is handled upfront, using readPixels, which will correctly clamp
+ // premul color values that would otherwise cause overflows in the skia
+ // encoder unpremul logic.
+ SkColorType colorType = retained_image_->colorType();
+ if (colorType == kRGBA_8888_SkColorType ||
+ colorType == kBGRA_8888_SkColorType)
+ colorType = kN32_SkColorType; // Work around for bug with JPEG encoder
+ const SkImageInfo info =
+ SkImageInfo::Make(retained_image_->width(), retained_image_->height(),
+ retained_image_->colorType(), kUnpremul_SkAlphaType,
+ retained_image_->refColorSpace());
+ const size_t rowBytes = info.minRowBytes();
+ size_t size = info.computeByteSize(rowBytes);
+ if (SkImageInfo::ByteSizeOverflowed(size))
+ return;
+
+ sk_sp<SkData> data = SkData::MakeUninitialized(size);
+ pixmap_ = {info, data->writable_data(), info.minRowBytes()};
+ if (!retained_image_->readPixels(pixmap_, 0, 0)) {
+ pixmap_.reset();
+ return;
+ }
+ retained_image_ = SkImage::MakeRasterData(info, std::move(data), rowBytes);
+ } else {
+ if (!retained_image_->peekPixels(&pixmap_))
+ return;
+ }
+ is_valid_ = true;
+ size_ = IntSize(image->width(), image->height());
+}
+
+ImageDataBuffer::ImageDataBuffer(const SkPixmap& pixmap)
+ : pixmap_(pixmap),
+ size_(IntSize(pixmap.width(), pixmap.height())) {
+ is_valid_ = pixmap_.addr() && !size_.IsEmpty();
+}
+
+std::unique_ptr<ImageDataBuffer> ImageDataBuffer::Create(
+ scoped_refptr<StaticBitmapImage> image) {
+ std::unique_ptr<ImageDataBuffer> buffer =
+ base::WrapUnique(new ImageDataBuffer(image));
+ if (!buffer->IsValid())
+ return nullptr;
+ return buffer;
+}
+
+std::unique_ptr<ImageDataBuffer> ImageDataBuffer::Create(
+ const SkPixmap& pixmap) {
+ std::unique_ptr<ImageDataBuffer> buffer =
+ base::WrapUnique(new ImageDataBuffer(pixmap));
+ if (!buffer->IsValid())
+ return nullptr;
+ return buffer;
+}
+
+const unsigned char* ImageDataBuffer::Pixels() const {
+ DCHECK(is_valid_);
+ return static_cast<const unsigned char*>(pixmap_.addr());
+}
+
+bool ImageDataBuffer::EncodeImage(const String& mime_type,
+ const double& quality,
+ Vector<unsigned char>* encoded_image) const {
+ DCHECK(is_valid_);
+
+ if (mime_type == "image/jpeg") {
+ SkJpegEncoder::Options options;
+ options.fQuality = ImageEncoder::ComputeJpegQuality(quality);
+ options.fAlphaOption = SkJpegEncoder::AlphaOption::kBlendOnBlack;
+ // When the gamma is linear (which is always the case with currently
+ // supported color spaces in F16 format), it does not matter whether we use
+ // kRespect or kIgnore, but the JPEG encoder does not support kIgnore with
+ // F16 for some reason, so we switch to kRespect in that case, with no
+ // consequence on the encoded output.
+ options.fBlendBehavior = pixmap_.colorType() == kRGBA_F16_SkColorType
+ ? SkTransferFunctionBehavior::kRespect
+ : SkTransferFunctionBehavior::kIgnore;
+ if (options.fQuality == 100) {
+ options.fDownsample = SkJpegEncoder::Downsample::k444;
+ }
+ return ImageEncoder::Encode(encoded_image, pixmap_, options);
+ }
+
+ if (mime_type == "image/webp") {
+ SkWebpEncoder::Options options = ImageEncoder::ComputeWebpOptions(
+ quality, SkTransferFunctionBehavior::kIgnore);
+ return ImageEncoder::Encode(encoded_image, pixmap_, options);
+ }
+
+ DCHECK_EQ(mime_type, "image/png");
+ SkPngEncoder::Options options;
+ options.fFilterFlags = SkPngEncoder::FilterFlag::kSub;
+ options.fZLibLevel = 3;
+ options.fUnpremulBehavior = SkTransferFunctionBehavior::kIgnore;
+ return ImageEncoder::Encode(encoded_image, pixmap_, options);
+}
+
+String ImageDataBuffer::ToDataURL(const String& mime_type,
+ const double& quality) const {
+ DCHECK(is_valid_);
+ DCHECK(MIMETypeRegistry::IsSupportedImageMIMETypeForEncoding(mime_type));
+
+ Vector<unsigned char> result;
+ if (!EncodeImage(mime_type, quality, &result))
+ return "data:,";
+
+ return "data:" + mime_type + ";base64," + Base64Encode(result);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/image_data_buffer.h b/chromium/third_party/blink/renderer/platform/graphics/image_data_buffer.h
new file mode 100644
index 00000000000..489906bc7e1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/image_data_buffer.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_IMAGE_DATA_BUFFER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_IMAGE_DATA_BUFFER_H_
+
+#include <memory>
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/geometry/int_size.h"
+#include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/typed_arrays/uint8_clamped_array.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT ImageDataBuffer {
+ public:
+ static std::unique_ptr<ImageDataBuffer> Create(
+ scoped_refptr<StaticBitmapImage>);
+ static std::unique_ptr<ImageDataBuffer> Create(const SkPixmap&);
+
+ String ToDataURL(const String& mime_type, const double& quality) const;
+ bool EncodeImage(const String& mime_type,
+ const double& quality,
+ Vector<unsigned char>* encoded_image) const;
+ const unsigned char* Pixels() const;
+ const IntSize& size() const { return size_; }
+ int Height() const { return size_.Height(); }
+ int Width() const { return size_.Width(); }
+
+ private:
+ ImageDataBuffer(const IntSize&,
+ const unsigned char*,
+ const CanvasColorParams&);
+ ImageDataBuffer(const SkPixmap&);
+ ImageDataBuffer(scoped_refptr<StaticBitmapImage>);
+
+ bool IsValid() { return is_valid_; } // Only used by Create()
+
+ sk_sp<SkImage> retained_image_;
+ SkPixmap pixmap_;
+ bool is_valid_ = false;
+ IntSize size_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_IMAGE_DATA_BUFFER_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/image_decoding_store.cc b/chromium/third_party/blink/renderer/platform/graphics/image_decoding_store.cc
new file mode 100644
index 00000000000..02fff1a6bb5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/image_decoding_store.cc
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2012 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/graphics/image_decoding_store.h"
+
+#include <memory>
+#include "third_party/blink/renderer/platform/graphics/image_frame_generator.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/wtf/threading.h"
+
+namespace blink {
+
+namespace {
+
+static const size_t kDefaultMaxTotalSizeOfHeapEntries = 32 * 1024 * 1024;
+
+} // namespace
+
+ImageDecodingStore::ImageDecodingStore()
+ : heap_limit_in_bytes_(kDefaultMaxTotalSizeOfHeapEntries),
+ heap_memory_usage_in_bytes_(0) {}
+
+ImageDecodingStore::~ImageDecodingStore() {
+#if DCHECK_IS_ON()
+ SetCacheLimitInBytes(0);
+ DCHECK(!decoder_cache_map_.size());
+ DCHECK(!ordered_cache_list_.size());
+ DCHECK(!decoder_cache_key_map_.size());
+#endif
+}
+
+ImageDecodingStore& ImageDecodingStore::Instance() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(ImageDecodingStore, store, ());
+ return store;
+}
+
+bool ImageDecodingStore::LockDecoder(const ImageFrameGenerator* generator,
+ const SkISize& scaled_size,
+ ImageDecoder::AlphaOption alpha_option,
+ ImageDecoder** decoder) {
+ DCHECK(decoder);
+
+ MutexLocker lock(mutex_);
+ DecoderCacheMap::iterator iter = decoder_cache_map_.find(
+ DecoderCacheEntry::MakeCacheKey(generator, scaled_size, alpha_option));
+ if (iter == decoder_cache_map_.end())
+ return false;
+
+ DecoderCacheEntry* cache_entry = iter->value.get();
+
+ // There can only be one user of a decoder at a time.
+ DCHECK(!cache_entry->UseCount());
+ cache_entry->IncrementUseCount();
+ *decoder = cache_entry->CachedDecoder();
+ return true;
+}
+
+void ImageDecodingStore::UnlockDecoder(const ImageFrameGenerator* generator,
+ const ImageDecoder* decoder) {
+ MutexLocker lock(mutex_);
+ DecoderCacheMap::iterator iter = decoder_cache_map_.find(
+ DecoderCacheEntry::MakeCacheKey(generator, decoder));
+ SECURITY_DCHECK(iter != decoder_cache_map_.end());
+
+ CacheEntry* cache_entry = iter->value.get();
+ cache_entry->DecrementUseCount();
+
+ // Put the entry to the end of list.
+ ordered_cache_list_.Remove(cache_entry);
+ ordered_cache_list_.Append(cache_entry);
+}
+
+void ImageDecodingStore::InsertDecoder(const ImageFrameGenerator* generator,
+ std::unique_ptr<ImageDecoder> decoder) {
+ // Prune old cache entries to give space for the new one.
+ Prune();
+
+ std::unique_ptr<DecoderCacheEntry> new_cache_entry =
+ DecoderCacheEntry::Create(generator, std::move(decoder));
+
+ MutexLocker lock(mutex_);
+ DCHECK(!decoder_cache_map_.Contains(new_cache_entry->CacheKey()));
+ InsertCacheInternal(std::move(new_cache_entry), &decoder_cache_map_,
+ &decoder_cache_key_map_);
+}
+
+void ImageDecodingStore::RemoveDecoder(const ImageFrameGenerator* generator,
+ const ImageDecoder* decoder) {
+ Vector<std::unique_ptr<CacheEntry>> cache_entries_to_delete;
+ {
+ MutexLocker lock(mutex_);
+ DecoderCacheMap::iterator iter = decoder_cache_map_.find(
+ DecoderCacheEntry::MakeCacheKey(generator, decoder));
+ SECURITY_DCHECK(iter != decoder_cache_map_.end());
+
+ CacheEntry* cache_entry = iter->value.get();
+ DCHECK(cache_entry->UseCount());
+ cache_entry->DecrementUseCount();
+
+ // Delete only one decoder cache entry. Ownership of the cache entry
+ // is transfered to cacheEntriesToDelete such that object can be deleted
+ // outside of the lock.
+ RemoveFromCacheInternal(cache_entry, &cache_entries_to_delete);
+
+ // Remove from LRU list.
+ RemoveFromCacheListInternal(cache_entries_to_delete);
+ }
+}
+
+void ImageDecodingStore::RemoveCacheIndexedByGenerator(
+ const ImageFrameGenerator* generator) {
+ Vector<std::unique_ptr<CacheEntry>> cache_entries_to_delete;
+ {
+ MutexLocker lock(mutex_);
+
+ // Remove image cache objects and decoder cache objects associated
+ // with a ImageFrameGenerator.
+ RemoveCacheIndexedByGeneratorInternal(&decoder_cache_map_,
+ &decoder_cache_key_map_, generator,
+ &cache_entries_to_delete);
+
+ // Remove from LRU list as well.
+ RemoveFromCacheListInternal(cache_entries_to_delete);
+ }
+}
+
+void ImageDecodingStore::Clear() {
+ size_t cache_limit_in_bytes;
+ {
+ MutexLocker lock(mutex_);
+ cache_limit_in_bytes = heap_limit_in_bytes_;
+ heap_limit_in_bytes_ = 0;
+ }
+
+ Prune();
+
+ {
+ MutexLocker lock(mutex_);
+ heap_limit_in_bytes_ = cache_limit_in_bytes;
+ }
+}
+
+void ImageDecodingStore::SetCacheLimitInBytes(size_t cache_limit) {
+ {
+ MutexLocker lock(mutex_);
+ heap_limit_in_bytes_ = cache_limit;
+ }
+ Prune();
+}
+
+size_t ImageDecodingStore::MemoryUsageInBytes() {
+ MutexLocker lock(mutex_);
+ return heap_memory_usage_in_bytes_;
+}
+
+int ImageDecodingStore::CacheEntries() {
+ MutexLocker lock(mutex_);
+ return decoder_cache_map_.size();
+}
+
+void ImageDecodingStore::Prune() {
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("blink.image_decoding"),
+ "ImageDecodingStore::prune");
+
+ Vector<std::unique_ptr<CacheEntry>> cache_entries_to_delete;
+ {
+ MutexLocker lock(mutex_);
+
+ // Head of the list is the least recently used entry.
+ const CacheEntry* cache_entry = ordered_cache_list_.Head();
+
+ // Walk the list of cache entries starting from the least recently used
+ // and then keep them for deletion later.
+ while (cache_entry) {
+ const bool is_prune_needed =
+ heap_memory_usage_in_bytes_ > heap_limit_in_bytes_ ||
+ !heap_limit_in_bytes_;
+ if (!is_prune_needed)
+ break;
+
+ // Cache is not used; Remove it.
+ if (!cache_entry->UseCount())
+ RemoveFromCacheInternal(cache_entry, &cache_entries_to_delete);
+ cache_entry = cache_entry->Next();
+ }
+
+ // Remove from cache list as well.
+ RemoveFromCacheListInternal(cache_entries_to_delete);
+ }
+}
+
+template <class T, class U, class V>
+void ImageDecodingStore::InsertCacheInternal(std::unique_ptr<T> cache_entry,
+ U* cache_map,
+ V* identifier_map) {
+ const size_t cache_entry_bytes = cache_entry->MemoryUsageInBytes();
+ heap_memory_usage_in_bytes_ += cache_entry_bytes;
+
+ // m_orderedCacheList is used to support LRU operations to reorder cache
+ // entries quickly.
+ ordered_cache_list_.Append(cache_entry.get());
+
+ typename U::KeyType key = cache_entry->CacheKey();
+ typename V::AddResult result = identifier_map->insert(
+ cache_entry->Generator(), typename V::MappedType());
+ result.stored_value->value.insert(key);
+ cache_map->insert(key, std::move(cache_entry));
+
+ TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink.image_decoding"),
+ "ImageDecodingStoreHeapMemoryUsageBytes",
+ heap_memory_usage_in_bytes_);
+ TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink.image_decoding"),
+ "ImageDecodingStoreNumOfDecoders", decoder_cache_map_.size());
+}
+
+template <class T, class U, class V>
+void ImageDecodingStore::RemoveFromCacheInternal(
+ const T* cache_entry,
+ U* cache_map,
+ V* identifier_map,
+ Vector<std::unique_ptr<CacheEntry>>* deletion_list) {
+ const size_t cache_entry_bytes = cache_entry->MemoryUsageInBytes();
+ DCHECK_GE(heap_memory_usage_in_bytes_, cache_entry_bytes);
+ heap_memory_usage_in_bytes_ -= cache_entry_bytes;
+
+ // Remove entry from identifier map.
+ typename V::iterator iter = identifier_map->find(cache_entry->Generator());
+ DCHECK(iter != identifier_map->end());
+ iter->value.erase(cache_entry->CacheKey());
+ if (!iter->value.size())
+ identifier_map->erase(iter);
+
+ // Remove entry from cache map.
+ deletion_list->push_back(cache_map->Take(cache_entry->CacheKey()));
+
+ TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink.image_decoding"),
+ "ImageDecodingStoreHeapMemoryUsageBytes",
+ heap_memory_usage_in_bytes_);
+ TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink.image_decoding"),
+ "ImageDecodingStoreNumOfDecoders", decoder_cache_map_.size());
+}
+
+void ImageDecodingStore::RemoveFromCacheInternal(
+ const CacheEntry* cache_entry,
+ Vector<std::unique_ptr<CacheEntry>>* deletion_list) {
+ if (cache_entry->GetType() == CacheEntry::kTypeDecoder) {
+ RemoveFromCacheInternal(static_cast<const DecoderCacheEntry*>(cache_entry),
+ &decoder_cache_map_, &decoder_cache_key_map_,
+ deletion_list);
+ } else {
+ DCHECK(false);
+ }
+}
+
+template <class U, class V>
+void ImageDecodingStore::RemoveCacheIndexedByGeneratorInternal(
+ U* cache_map,
+ V* identifier_map,
+ const ImageFrameGenerator* generator,
+ Vector<std::unique_ptr<CacheEntry>>* deletion_list) {
+ typename V::iterator iter = identifier_map->find(generator);
+ if (iter == identifier_map->end())
+ return;
+
+ // Get all cache identifiers associated with generator.
+ Vector<typename U::KeyType> cache_identifier_list;
+ CopyToVector(iter->value, cache_identifier_list);
+
+ // For each cache identifier find the corresponding CacheEntry and remove it.
+ for (size_t i = 0; i < cache_identifier_list.size(); ++i) {
+ DCHECK(cache_map->Contains(cache_identifier_list[i]));
+ const auto& cache_entry = cache_map->at(cache_identifier_list[i]);
+ DCHECK(!cache_entry->UseCount());
+ RemoveFromCacheInternal(cache_entry, cache_map, identifier_map,
+ deletion_list);
+ }
+}
+
+void ImageDecodingStore::RemoveFromCacheListInternal(
+ const Vector<std::unique_ptr<CacheEntry>>& deletion_list) {
+ for (size_t i = 0; i < deletion_list.size(); ++i)
+ ordered_cache_list_.Remove(deletion_list[i].get());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/image_decoding_store.h b/chromium/third_party/blink/renderer/platform/graphics/image_decoding_store.h
new file mode 100644
index 00000000000..3ea28831cc8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/image_decoding_store.h
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2012 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_IMAGE_DECODING_STORE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_IMAGE_DECODING_STORE_H_
+
+#include <memory>
+#include <utility>
+
+#include "SkSize.h"
+#include "SkTypes.h"
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/platform/graphics/image_frame_generator.h"
+#include "third_party/blink/renderer/platform/graphics/skia/sk_size_hash.h"
+#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/doubly_linked_list.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+// Decoder cache entry is identified by:
+// 1. Pointer to ImageFrameGenerator.
+// 2. Size of the image.
+// 3. ImageDecoder::AlphaOption
+struct DecoderCacheKey {
+ const blink::ImageFrameGenerator* gen_;
+ SkISize size_;
+ blink::ImageDecoder::AlphaOption alpha_option_;
+
+ DecoderCacheKey()
+ : gen_(nullptr),
+ size_(SkISize::Make(0, 0)),
+ alpha_option_(static_cast<blink::ImageDecoder::AlphaOption>(0)) {}
+};
+
+static inline bool operator==(const DecoderCacheKey& a,
+ const DecoderCacheKey& b) {
+ return a.gen_ == b.gen_ && a.size_ == b.size_ &&
+ a.alpha_option_ == b.alpha_option_;
+}
+
+static inline bool operator!=(const DecoderCacheKey& a,
+ const DecoderCacheKey& b) {
+ return !(a == b);
+}
+
+// Base class for all cache entries.
+class CacheEntry : public DoublyLinkedListNode<CacheEntry> {
+ USING_FAST_MALLOC(CacheEntry);
+ WTF_MAKE_NONCOPYABLE(CacheEntry);
+ friend class WTF::DoublyLinkedListNode<CacheEntry>;
+
+ public:
+ enum CacheType {
+ kTypeDecoder,
+ };
+
+ CacheEntry(const ImageFrameGenerator* generator, int use_count)
+ : generator_(generator),
+ use_count_(use_count),
+ prev_(nullptr),
+ next_(nullptr) {}
+
+ virtual ~CacheEntry() { DCHECK(!use_count_); }
+
+ const ImageFrameGenerator* Generator() const { return generator_; }
+ int UseCount() const { return use_count_; }
+ void IncrementUseCount() { ++use_count_; }
+ void DecrementUseCount() {
+ --use_count_;
+ DCHECK_GE(use_count_, 0);
+ }
+
+ // FIXME: getSafeSize() returns the size in bytes truncated to a 32-bit
+ // integer. Find a way to get the size in 64-bits.
+ virtual size_t MemoryUsageInBytes() const = 0;
+ virtual CacheType GetType() const = 0;
+
+ protected:
+ const ImageFrameGenerator* generator_;
+ int use_count_;
+
+ private:
+ CacheEntry* prev_;
+ CacheEntry* next_;
+};
+
+class DecoderCacheEntry final : public CacheEntry {
+ public:
+ static std::unique_ptr<DecoderCacheEntry> Create(
+ const ImageFrameGenerator* generator,
+ std::unique_ptr<ImageDecoder> decoder) {
+ return base::WrapUnique(
+ new DecoderCacheEntry(generator, 0, std::move(decoder)));
+ }
+
+ size_t MemoryUsageInBytes() const override {
+ return size_.width() * size_.height() * 4;
+ }
+ CacheType GetType() const override { return kTypeDecoder; }
+
+ static DecoderCacheKey MakeCacheKey(const ImageFrameGenerator* generator,
+ const SkISize& size,
+ ImageDecoder::AlphaOption alpha_option) {
+ DecoderCacheKey key;
+ key.gen_ = generator;
+ key.size_ = size;
+ key.alpha_option_ = alpha_option;
+ return key;
+ }
+ static DecoderCacheKey MakeCacheKey(const ImageFrameGenerator* generator,
+ const ImageDecoder* decoder) {
+ return MakeCacheKey(generator,
+ SkISize::Make(decoder->DecodedSize().Width(),
+ decoder->DecodedSize().Height()),
+ decoder->GetAlphaOption());
+ }
+ DecoderCacheKey CacheKey() const {
+ return MakeCacheKey(generator_, size_, alpha_option_);
+ }
+ ImageDecoder* CachedDecoder() const { return cached_decoder_.get(); }
+
+ private:
+ DecoderCacheEntry(const ImageFrameGenerator* generator,
+ int count,
+ std::unique_ptr<ImageDecoder> decoder)
+ : CacheEntry(generator, count),
+ cached_decoder_(std::move(decoder)),
+ size_(SkISize::Make(cached_decoder_->DecodedSize().Width(),
+ cached_decoder_->DecodedSize().Height())),
+ alpha_option_(cached_decoder_->GetAlphaOption()) {}
+
+ std::unique_ptr<ImageDecoder> cached_decoder_;
+ SkISize size_;
+ ImageDecoder::AlphaOption alpha_option_;
+};
+
+} // namespace blink
+
+namespace WTF {
+
+template <>
+struct DefaultHash<blink::DecoderCacheKey> {
+ STATIC_ONLY(DefaultHash);
+ struct Hash {
+ STATIC_ONLY(Hash);
+ static unsigned GetHash(const blink::DecoderCacheKey& p) {
+ return HashInts(
+ HashInts(DefaultHash<blink::ImageFrameGenerator*>::Hash::GetHash(
+ const_cast<blink::ImageFrameGenerator*>(p.gen_)),
+ DefaultHash<SkISize>::Hash::GetHash(p.size_)),
+ DefaultHash<uint8_t>::Hash::GetHash(
+ static_cast<uint8_t>(p.alpha_option_)));
+ }
+ static bool Equal(const blink::DecoderCacheKey& a,
+ const blink::DecoderCacheKey& b) {
+ return a.gen_ == b.gen_ && a.size_ == b.size_ &&
+ a.alpha_option_ == b.alpha_option_;
+ }
+ static const bool safe_to_compare_to_empty_or_deleted = true;
+ };
+};
+
+template <>
+struct HashTraits<blink::DecoderCacheKey>
+ : GenericHashTraits<blink::DecoderCacheKey> {
+ STATIC_ONLY(HashTraits);
+ static const bool kEmptyValueIsZero = true;
+ static blink::DecoderCacheKey EmptyValue() {
+ return blink::DecoderCacheEntry::MakeCacheKey(
+ nullptr, SkISize::Make(0, 0),
+ static_cast<blink::ImageDecoder::AlphaOption>(0));
+ }
+ static void ConstructDeletedValue(blink::DecoderCacheKey& slot, bool) {
+ slot = blink::DecoderCacheEntry::MakeCacheKey(
+ nullptr, SkISize::Make(-1, -1),
+ static_cast<blink::ImageDecoder::AlphaOption>(0));
+ }
+ static bool IsDeletedValue(const blink::DecoderCacheKey& value) {
+ return value.size_ == SkISize::Make(-1, -1);
+ }
+};
+
+} // namespace WTF
+
+namespace blink {
+
+// FUNCTION
+//
+// ImageDecodingStore is a class used to manage cached decoder objects.
+//
+// EXTERNAL OBJECTS
+//
+// ImageDecoder
+// A decoder object. It is used to decode raw data into bitmap images.
+//
+// ImageFrameGenerator
+// This is a direct user of this cache. Responsible for generating bitmap
+// images using an ImageDecoder. It contains encoded image data and is used
+// to represent one image file. It is used to index image and decoder
+// objects in the cache.
+//
+// THREAD SAFETY
+//
+// All public methods can be used on any thread.
+
+class PLATFORM_EXPORT ImageDecodingStore final {
+ USING_FAST_MALLOC(ImageDecodingStore);
+ WTF_MAKE_NONCOPYABLE(ImageDecodingStore);
+
+ public:
+ static std::unique_ptr<ImageDecodingStore> Create() {
+ return base::WrapUnique(new ImageDecodingStore);
+ }
+ ~ImageDecodingStore();
+
+ static ImageDecodingStore& Instance();
+
+ // Accesses a cached decoder object. A decoder is indexed by origin
+ // (ImageFrameGenerator) and scaled size. Returns true if the cached object
+ // is found.
+ bool LockDecoder(const ImageFrameGenerator*,
+ const SkISize& scaled_size,
+ ImageDecoder::AlphaOption,
+ ImageDecoder**);
+ void UnlockDecoder(const ImageFrameGenerator*, const ImageDecoder*);
+ void InsertDecoder(const ImageFrameGenerator*, std::unique_ptr<ImageDecoder>);
+ void RemoveDecoder(const ImageFrameGenerator*, const ImageDecoder*);
+
+ // Remove all cache entries indexed by ImageFrameGenerator.
+ void RemoveCacheIndexedByGenerator(const ImageFrameGenerator*);
+
+ void Clear();
+ void SetCacheLimitInBytes(size_t);
+ size_t MemoryUsageInBytes();
+ int CacheEntries();
+ int DecoderCacheEntries();
+
+ private:
+ ImageDecodingStore();
+
+ void Prune();
+
+ // These helper methods are called while m_mutex is locked.
+ template <class T, class U, class V>
+ void InsertCacheInternal(std::unique_ptr<T> cache_entry,
+ U* cache_map,
+ V* identifier_map);
+
+ // Helper method to remove a cache entry. Ownership is transferred to
+ // deletionList. Use of Vector<> is handy when removing multiple entries.
+ template <class T, class U, class V>
+ void RemoveFromCacheInternal(
+ const T* cache_entry,
+ U* cache_map,
+ V* identifier_map,
+ Vector<std::unique_ptr<CacheEntry>>* deletion_list);
+
+ // Helper method to remove a cache entry. Uses the templated version base on
+ // the type of cache entry.
+ void RemoveFromCacheInternal(
+ const CacheEntry*,
+ Vector<std::unique_ptr<CacheEntry>>* deletion_list);
+
+ // Helper method to remove all cache entries associated with an
+ // ImageFrameGenerator. Ownership of the cache entries is transferred to
+ // |deletionList|.
+ template <class U, class V>
+ void RemoveCacheIndexedByGeneratorInternal(
+ U* cache_map,
+ V* identifier_map,
+ const ImageFrameGenerator*,
+ Vector<std::unique_ptr<CacheEntry>>* deletion_list);
+
+ // Helper method to remove cache entry pointers from the LRU list.
+ void RemoveFromCacheListInternal(
+ const Vector<std::unique_ptr<CacheEntry>>& deletion_list);
+
+ // A doubly linked list that maintains usage history of cache entries.
+ // This is used for eviction of old entries.
+ // Head of this list is the least recently used cache entry.
+ // Tail of this list is the most recently used cache entry.
+ DoublyLinkedList<CacheEntry> ordered_cache_list_;
+
+ // A lookup table for all decoder cache objects. Owns all decoder cache
+ // objects.
+ typedef HashMap<DecoderCacheKey, std::unique_ptr<DecoderCacheEntry>>
+ DecoderCacheMap;
+ DecoderCacheMap decoder_cache_map_;
+
+ // A lookup table to map ImageFrameGenerator to all associated
+ // decoder cache keys.
+ typedef HashSet<DecoderCacheKey> DecoderCacheKeySet;
+ typedef HashMap<const ImageFrameGenerator*, DecoderCacheKeySet>
+ DecoderCacheKeyMap;
+ DecoderCacheKeyMap decoder_cache_key_map_;
+
+ size_t heap_limit_in_bytes_;
+ size_t heap_memory_usage_in_bytes_;
+
+ // Protect concurrent access to these members:
+ // m_orderedCacheList
+ // m_decoderCacheMap and all CacheEntrys stored in it
+ // m_decoderCacheKeyMap
+ // m_heapLimitInBytes
+ // m_heapMemoryUsageInBytes
+ // This mutex also protects calls to underlying skBitmap's
+ // lockPixels()/unlockPixels() as they are not threadsafe.
+ Mutex mutex_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/graphics/image_decoding_store_test.cc b/chromium/third_party/blink/renderer/platform/graphics/image_decoding_store_test.cc
new file mode 100644
index 00000000000..40500788863
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/image_decoding_store_test.cc
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/graphics/image_decoding_store.h"
+
+#include <memory>
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/graphics/image_frame_generator.h"
+#include "third_party/blink/renderer/platform/graphics/test/mock_image_decoder.h"
+
+namespace blink {
+
+class ImageDecodingStoreTest : public testing::Test,
+ public MockImageDecoderClient {
+ public:
+ void SetUp() override {
+ ImageDecodingStore::Instance().SetCacheLimitInBytes(1024 * 1024);
+ generator_ = ImageFrameGenerator::Create(SkISize::Make(100, 100), true,
+ ColorBehavior::Ignore(), {});
+ decoders_destroyed_ = 0;
+ }
+
+ void TearDown() override { ImageDecodingStore::Instance().Clear(); }
+
+ void DecoderBeingDestroyed() override { ++decoders_destroyed_; }
+
+ void DecodeRequested() override {
+ // Decoder is never used by ImageDecodingStore.
+ ASSERT_TRUE(false);
+ }
+
+ ImageFrame::Status GetStatus(size_t index) override {
+ return ImageFrame::kFramePartial;
+ }
+
+ size_t FrameCount() override { return 1; }
+ int RepetitionCount() const override { return kAnimationNone; }
+ TimeDelta FrameDuration() const override { return TimeDelta(); }
+
+ protected:
+ void EvictOneCache() {
+ size_t memory_usage_in_bytes =
+ ImageDecodingStore::Instance().MemoryUsageInBytes();
+ if (memory_usage_in_bytes)
+ ImageDecodingStore::Instance().SetCacheLimitInBytes(
+ memory_usage_in_bytes - 1);
+ else
+ ImageDecodingStore::Instance().SetCacheLimitInBytes(0);
+ }
+
+ scoped_refptr<ImageFrameGenerator> generator_;
+ int decoders_destroyed_;
+};
+
+TEST_F(ImageDecodingStoreTest, insertDecoder) {
+ const SkISize size = SkISize::Make(1, 1);
+ std::unique_ptr<ImageDecoder> decoder = MockImageDecoder::Create(this);
+ decoder->SetSize(1, 1);
+ const ImageDecoder* ref_decoder = decoder.get();
+ ImageDecodingStore::Instance().InsertDecoder(generator_.get(),
+ std::move(decoder));
+ EXPECT_EQ(1, ImageDecodingStore::Instance().CacheEntries());
+ EXPECT_EQ(4u, ImageDecodingStore::Instance().MemoryUsageInBytes());
+
+ ImageDecoder* test_decoder;
+ EXPECT_TRUE(ImageDecodingStore::Instance().LockDecoder(
+ generator_.get(), size, ImageDecoder::kAlphaPremultiplied,
+ &test_decoder));
+ EXPECT_TRUE(test_decoder);
+ EXPECT_EQ(ref_decoder, test_decoder);
+ ImageDecodingStore::Instance().UnlockDecoder(generator_.get(), test_decoder);
+ EXPECT_EQ(1, ImageDecodingStore::Instance().CacheEntries());
+}
+
+TEST_F(ImageDecodingStoreTest, evictDecoder) {
+ std::unique_ptr<ImageDecoder> decoder1 = MockImageDecoder::Create(this);
+ std::unique_ptr<ImageDecoder> decoder2 = MockImageDecoder::Create(this);
+ std::unique_ptr<ImageDecoder> decoder3 = MockImageDecoder::Create(this);
+ decoder1->SetSize(1, 1);
+ decoder2->SetSize(2, 2);
+ decoder3->SetSize(3, 3);
+ ImageDecodingStore::Instance().InsertDecoder(generator_.get(),
+ std::move(decoder1));
+ ImageDecodingStore::Instance().InsertDecoder(generator_.get(),
+ std::move(decoder2));
+ ImageDecodingStore::Instance().InsertDecoder(generator_.get(),
+ std::move(decoder3));
+ EXPECT_EQ(3, ImageDecodingStore::Instance().CacheEntries());
+ EXPECT_EQ(56u, ImageDecodingStore::Instance().MemoryUsageInBytes());
+
+ EvictOneCache();
+ EXPECT_EQ(2, ImageDecodingStore::Instance().CacheEntries());
+ EXPECT_EQ(52u, ImageDecodingStore::Instance().MemoryUsageInBytes());
+
+ EvictOneCache();
+ EXPECT_EQ(1, ImageDecodingStore::Instance().CacheEntries());
+ EXPECT_EQ(36u, ImageDecodingStore::Instance().MemoryUsageInBytes());
+
+ EvictOneCache();
+ EXPECT_FALSE(ImageDecodingStore::Instance().CacheEntries());
+ EXPECT_FALSE(ImageDecodingStore::Instance().MemoryUsageInBytes());
+}
+
+TEST_F(ImageDecodingStoreTest, decoderInUseNotEvicted) {
+ std::unique_ptr<ImageDecoder> decoder1 = MockImageDecoder::Create(this);
+ std::unique_ptr<ImageDecoder> decoder2 = MockImageDecoder::Create(this);
+ std::unique_ptr<ImageDecoder> decoder3 = MockImageDecoder::Create(this);
+ decoder1->SetSize(1, 1);
+ decoder2->SetSize(2, 2);
+ decoder3->SetSize(3, 3);
+ ImageDecodingStore::Instance().InsertDecoder(generator_.get(),
+ std::move(decoder1));
+ ImageDecodingStore::Instance().InsertDecoder(generator_.get(),
+ std::move(decoder2));
+ ImageDecodingStore::Instance().InsertDecoder(generator_.get(),
+ std::move(decoder3));
+ EXPECT_EQ(3, ImageDecodingStore::Instance().CacheEntries());
+
+ ImageDecoder* test_decoder;
+ EXPECT_TRUE(ImageDecodingStore::Instance().LockDecoder(
+ generator_.get(), SkISize::Make(2, 2), ImageDecoder::kAlphaPremultiplied,
+ &test_decoder));
+
+ EvictOneCache();
+ EvictOneCache();
+ EvictOneCache();
+ EXPECT_EQ(1, ImageDecodingStore::Instance().CacheEntries());
+ EXPECT_EQ(16u, ImageDecodingStore::Instance().MemoryUsageInBytes());
+
+ ImageDecodingStore::Instance().UnlockDecoder(generator_.get(), test_decoder);
+ EvictOneCache();
+ EXPECT_FALSE(ImageDecodingStore::Instance().CacheEntries());
+ EXPECT_FALSE(ImageDecodingStore::Instance().MemoryUsageInBytes());
+}
+
+TEST_F(ImageDecodingStoreTest, removeDecoder) {
+ const SkISize size = SkISize::Make(1, 1);
+ std::unique_ptr<ImageDecoder> decoder = MockImageDecoder::Create(this);
+ decoder->SetSize(1, 1);
+ const ImageDecoder* ref_decoder = decoder.get();
+ ImageDecodingStore::Instance().InsertDecoder(generator_.get(),
+ std::move(decoder));
+ EXPECT_EQ(1, ImageDecodingStore::Instance().CacheEntries());
+ EXPECT_EQ(4u, ImageDecodingStore::Instance().MemoryUsageInBytes());
+
+ ImageDecoder* test_decoder;
+ EXPECT_TRUE(ImageDecodingStore::Instance().LockDecoder(
+ generator_.get(), size, ImageDecoder::kAlphaPremultiplied,
+ &test_decoder));
+ EXPECT_TRUE(test_decoder);
+ EXPECT_EQ(ref_decoder, test_decoder);
+ ImageDecodingStore::Instance().RemoveDecoder(generator_.get(), test_decoder);
+ EXPECT_FALSE(ImageDecodingStore::Instance().CacheEntries());
+
+ EXPECT_FALSE(ImageDecodingStore::Instance().LockDecoder(
+ generator_.get(), size, ImageDecoder::kAlphaPremultiplied,
+ &test_decoder));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/image_frame_generator.cc b/chromium/third_party/blink/renderer/platform/graphics/image_frame_generator.cc
new file mode 100644
index 00000000000..e7fe70ee12d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/image_frame_generator.cc
@@ -0,0 +1,442 @@
+/*
+ * Copyright (C) 2012 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/graphics/image_frame_generator.h"
+
+#include <memory>
+#include <utility>
+
+#include "SkData.h"
+#include "third_party/blink/renderer/platform/graphics/image_decoding_store.h"
+#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/skia/include/core/SkYUVSizeInfo.h"
+
+namespace blink {
+
+static void CopyPixels(void* dst_addr,
+ size_t dst_row_bytes,
+ const void* src_addr,
+ size_t src_row_bytes,
+ const SkImageInfo& info) {
+ size_t row_bytes = info.bytesPerPixel() * info.width();
+ for (int y = 0; y < info.height(); ++y) {
+ memcpy(dst_addr, src_addr, row_bytes);
+ src_addr = static_cast<const char*>(src_addr) + src_row_bytes;
+ dst_addr = static_cast<char*>(dst_addr) + dst_row_bytes;
+ }
+}
+
+static bool CompatibleInfo(const SkImageInfo& src, const SkImageInfo& dst) {
+ if (src == dst)
+ return true;
+
+ // It is legal to write kOpaque_SkAlphaType pixels into a kPremul_SkAlphaType
+ // buffer. This can happen when DeferredImageDecoder allocates an
+ // kOpaque_SkAlphaType image generator based on cached frame info, while the
+ // ImageFrame-allocated dest bitmap stays kPremul_SkAlphaType.
+ if (src.alphaType() == kOpaque_SkAlphaType &&
+ dst.alphaType() == kPremul_SkAlphaType) {
+ const SkImageInfo& tmp = src.makeAlphaType(kPremul_SkAlphaType);
+ return tmp == dst;
+ }
+
+ return false;
+}
+
+// Creates a SkPixelRef such that the memory for pixels is given by an external
+// body. This is used to write directly to the memory given by Skia during
+// decoding.
+class ExternalMemoryAllocator final : public SkBitmap::Allocator {
+ USING_FAST_MALLOC(ExternalMemoryAllocator);
+ WTF_MAKE_NONCOPYABLE(ExternalMemoryAllocator);
+
+ public:
+ ExternalMemoryAllocator(const SkImageInfo& info,
+ void* pixels,
+ size_t row_bytes)
+ : info_(info), pixels_(pixels), row_bytes_(row_bytes) {}
+
+ bool allocPixelRef(SkBitmap* dst) override {
+ const SkImageInfo& info = dst->info();
+ if (kUnknown_SkColorType == info.colorType())
+ return false;
+
+ if (!CompatibleInfo(info_, info) || row_bytes_ != dst->rowBytes())
+ return false;
+
+ return dst->installPixels(info, pixels_, row_bytes_);
+ }
+
+ private:
+ SkImageInfo info_;
+ void* pixels_;
+ size_t row_bytes_;
+};
+
+static bool UpdateYUVComponentSizes(ImageDecoder* decoder,
+ SkISize component_sizes[3],
+ size_t component_width_bytes[3]) {
+ if (!decoder->CanDecodeToYUV())
+ return false;
+
+ for (int yuv_index = 0; yuv_index < 3; ++yuv_index) {
+ IntSize size = decoder->DecodedYUVSize(yuv_index);
+ component_sizes[yuv_index].set(size.Width(), size.Height());
+ component_width_bytes[yuv_index] = decoder->DecodedYUVWidthBytes(yuv_index);
+ }
+
+ return true;
+}
+
+ImageFrameGenerator::ImageFrameGenerator(const SkISize& full_size,
+ bool is_multi_frame,
+ const ColorBehavior& color_behavior,
+ std::vector<SkISize> supported_sizes)
+ : full_size_(full_size),
+ decoder_color_behavior_(color_behavior),
+ is_multi_frame_(is_multi_frame),
+ decode_failed_(false),
+ yuv_decoding_failed_(false),
+ frame_count_(0),
+ supported_sizes_(std::move(supported_sizes)) {
+#if DCHECK_IS_ON()
+ // Verify that sizes are in an increasing order, since
+ // GetSupportedDecodeSize() depends on it.
+ SkISize last_size = SkISize::MakeEmpty();
+ for (auto& size : supported_sizes_) {
+ DCHECK_GE(size.width(), last_size.width());
+ DCHECK_GE(size.height(), last_size.height());
+ }
+#endif
+}
+
+ImageFrameGenerator::~ImageFrameGenerator() {
+ ImageDecodingStore::Instance().RemoveCacheIndexedByGenerator(this);
+}
+
+bool ImageFrameGenerator::DecodeAndScale(
+ SegmentReader* data,
+ bool all_data_received,
+ size_t index,
+ const SkImageInfo& info,
+ void* pixels,
+ size_t row_bytes,
+ ImageDecoder::AlphaOption alpha_option) {
+ if (decode_failed_)
+ return false;
+
+ TRACE_EVENT1("blink", "ImageFrameGenerator::decodeAndScale", "frame index",
+ static_cast<int>(index));
+
+ // Lock the mutex, so only one thread can use the decoder at once.
+ MutexLocker lock(decode_mutex_);
+
+ // This implementation does not support arbitrary scaling so check the
+ // requested size.
+ SkISize scaled_size = SkISize::Make(info.width(), info.height());
+ CHECK(GetSupportedDecodeSize(scaled_size) == scaled_size);
+
+ // It is okay to allocate ref-counted ExternalMemoryAllocator on the stack,
+ // because 1) it contains references to memory that will be invalid after
+ // returning (i.e. a pointer to |pixels|) and therefore 2) should not live
+ // longer than the call to the current method.
+ ExternalMemoryAllocator external_allocator(info, pixels, row_bytes);
+ SkBitmap bitmap =
+ TryToResumeDecode(data, all_data_received, index, scaled_size,
+ external_allocator, alpha_option);
+ DCHECK(external_allocator.unique()); // Verify we have the only ref-count.
+
+ if (bitmap.isNull())
+ return false;
+
+ // Check to see if the decoder has written directly to the pixel memory
+ // provided. If not, make a copy.
+ DCHECK_EQ(bitmap.width(), scaled_size.width());
+ DCHECK_EQ(bitmap.height(), scaled_size.height());
+ if (bitmap.getPixels() != pixels)
+ CopyPixels(pixels, row_bytes, bitmap.getPixels(), bitmap.rowBytes(), info);
+ return true;
+}
+
+bool ImageFrameGenerator::DecodeToYUV(SegmentReader* data,
+ size_t index,
+ const SkISize component_sizes[3],
+ void* planes[3],
+ const size_t row_bytes[3]) {
+ // TODO (scroggo): The only interesting thing this uses from the
+ // ImageFrameGenerator is m_decodeFailed. Move this into
+ // DecodingImageGenerator, which is the only class that calls it.
+ if (decode_failed_)
+ return false;
+
+ TRACE_EVENT1("blink", "ImageFrameGenerator::decodeToYUV", "frame index",
+ static_cast<int>(index));
+
+ if (!planes || !planes[0] || !planes[1] || !planes[2] || !row_bytes ||
+ !row_bytes[0] || !row_bytes[1] || !row_bytes[2]) {
+ return false;
+ }
+
+ std::unique_ptr<ImageDecoder> decoder = ImageDecoder::Create(
+ data, true, ImageDecoder::kAlphaPremultiplied, decoder_color_behavior_);
+ // getYUVComponentSizes was already called and was successful, so
+ // ImageDecoder::create must succeed.
+ DCHECK(decoder);
+
+ std::unique_ptr<ImagePlanes> image_planes =
+ std::make_unique<ImagePlanes>(planes, row_bytes);
+ decoder->SetImagePlanes(std::move(image_planes));
+
+ DCHECK(decoder->CanDecodeToYUV());
+
+ if (decoder->DecodeToYUV()) {
+ SetHasAlpha(0, false); // YUV is always opaque
+ return true;
+ }
+
+ DCHECK(decoder->Failed());
+ yuv_decoding_failed_ = true;
+ return false;
+}
+
+SkBitmap ImageFrameGenerator::TryToResumeDecode(
+ SegmentReader* data,
+ bool all_data_received,
+ size_t index,
+ const SkISize& scaled_size,
+ SkBitmap::Allocator& allocator,
+ ImageDecoder::AlphaOption alpha_option) {
+#if DCHECK_IS_ON()
+ DCHECK(decode_mutex_.Locked());
+#endif
+
+ TRACE_EVENT1("blink", "ImageFrameGenerator::tryToResumeDecode", "frame index",
+ static_cast<int>(index));
+
+ ImageDecoder* decoder = nullptr;
+ const bool resume_decoding = ImageDecodingStore::Instance().LockDecoder(
+ this, scaled_size, alpha_option, &decoder);
+ DCHECK(!resume_decoding || decoder);
+
+ bool used_external_allocator = false;
+ ImageFrame* current_frame =
+ Decode(data, all_data_received, index, &decoder, allocator, alpha_option,
+ scaled_size, used_external_allocator);
+
+ if (!decoder)
+ return SkBitmap();
+
+ // If we are not resuming decoding that means the decoder is freshly
+ // created and we have ownership. If we are resuming decoding then
+ // the decoder is owned by ImageDecodingStore.
+ std::unique_ptr<ImageDecoder> decoder_container;
+ if (!resume_decoding)
+ decoder_container = base::WrapUnique(decoder);
+
+ if (!current_frame || current_frame->Bitmap().isNull()) {
+ // If decoding has failed, we can save work in the future by
+ // ignoring further requests to decode the image.
+ decode_failed_ = decoder->Failed();
+ if (resume_decoding)
+ ImageDecodingStore::Instance().UnlockDecoder(this, decoder);
+ return SkBitmap();
+ }
+
+ SkBitmap scaled_size_bitmap = current_frame->Bitmap();
+ DCHECK_EQ(scaled_size_bitmap.width(), scaled_size.width());
+ DCHECK_EQ(scaled_size_bitmap.height(), scaled_size.height());
+ SetHasAlpha(index, !scaled_size_bitmap.isOpaque());
+
+ // Free as much memory as possible. For single-frame images, we can
+ // just delete the decoder entirely if they use the external allocator.
+ // For multi-frame images, we keep the decoder around in order to preserve
+ // decoded information such as the required previous frame indexes, but if
+ // we've reached the last frame we can at least delete all the cached frames.
+ // (If we were to do this before reaching the last frame, any subsequent
+ // requested frames which relied on the current frame would trigger extra
+ // re-decoding of all frames in the dependency chain.
+ bool remove_decoder = false;
+ if (current_frame->GetStatus() == ImageFrame::kFrameComplete ||
+ all_data_received) {
+ if (!is_multi_frame_) {
+ remove_decoder = true;
+ } else if (index == frame_count_ - 1) {
+ decoder->ClearCacheExceptFrame(kNotFound);
+ }
+ } else if (used_external_allocator) {
+ remove_decoder = true;
+ }
+
+ if (resume_decoding) {
+ if (remove_decoder)
+ ImageDecodingStore::Instance().RemoveDecoder(this, decoder);
+ else
+ ImageDecodingStore::Instance().UnlockDecoder(this, decoder);
+ } else if (!remove_decoder) {
+ ImageDecodingStore::Instance().InsertDecoder(this,
+ std::move(decoder_container));
+ }
+
+ return scaled_size_bitmap;
+}
+
+void ImageFrameGenerator::SetHasAlpha(size_t index, bool has_alpha) {
+ MutexLocker lock(alpha_mutex_);
+ if (index >= has_alpha_.size()) {
+ const size_t old_size = has_alpha_.size();
+ has_alpha_.resize(index + 1);
+ for (size_t i = old_size; i < has_alpha_.size(); ++i)
+ has_alpha_[i] = true;
+ }
+ has_alpha_[index] = has_alpha;
+}
+
+ImageFrame* ImageFrameGenerator::Decode(SegmentReader* data,
+ bool all_data_received,
+ size_t index,
+ ImageDecoder** decoder,
+ SkBitmap::Allocator& allocator,
+ ImageDecoder::AlphaOption alpha_option,
+ const SkISize& scaled_size,
+ bool& used_external_allocator) {
+#if DCHECK_IS_ON()
+ DCHECK(decode_mutex_.Locked());
+#endif
+ TRACE_EVENT2("blink", "ImageFrameGenerator::decode", "width",
+ full_size_.width(), "height", full_size_.height());
+
+ // Try to create an ImageDecoder if we are not given one.
+ DCHECK(decoder);
+ bool new_decoder = false;
+ bool should_call_set_data = true;
+ if (!*decoder) {
+ new_decoder = true;
+ // TODO(vmpstr): The factory is only used for tests. We can convert all
+ // calls to use a factory so that we don't need to worry about this call.
+ if (image_decoder_factory_)
+ *decoder = image_decoder_factory_->Create().release();
+
+ if (!*decoder) {
+ *decoder = ImageDecoder::Create(data, all_data_received, alpha_option,
+ decoder_color_behavior_, scaled_size)
+ .release();
+ // The newly created decoder just grabbed the data. No need to reset it.
+ should_call_set_data = false;
+ }
+
+ if (!*decoder)
+ return nullptr;
+ }
+
+ if (should_call_set_data)
+ (*decoder)->SetData(data, all_data_received);
+
+ // For multi-frame image decoders, we need to know how many frames are
+ // in that image in order to release the decoder when all frames are
+ // decoded. frameCount() is reliable only if all data is received and set in
+ // decoder, particularly with GIF.
+ if (all_data_received)
+ frame_count_ = (*decoder)->FrameCount();
+
+ used_external_allocator = false;
+ // External allocators don't work for multi-frame images right now.
+ if (!is_multi_frame_) {
+ if (Platform::Current()->IsLowEndDevice()) {
+ // On low-end devices, always use the external allocator, to avoid
+ // storing duplicate copies of the data in the ImageDecoder's cache.
+ used_external_allocator = true;
+ DCHECK(new_decoder);
+ // TODO (scroggo): If !is_multi_frame_ && new_decoder && frame_count_, it
+ // should always be the case that 1u == frame_count_. But it looks like it
+ // is currently possible for frame_count_ to be another value.
+ } else if (1u == frame_count_ && all_data_received && new_decoder) {
+ // Also use external allocator situations when all of the data has been
+ // received and there is not already a partial cache in the image decoder.
+ used_external_allocator = true;
+ }
+ }
+
+ if (used_external_allocator)
+ (*decoder)->SetMemoryAllocator(&allocator);
+
+ ImageFrame* frame = (*decoder)->DecodeFrameBufferAtIndex(index);
+
+ // SetMemoryAllocator() can try to access decoder's data, so
+ // we have to do it before clearing SegmentReader.
+ if (used_external_allocator)
+ (*decoder)->SetMemoryAllocator(nullptr);
+ (*decoder)->SetData(scoped_refptr<SegmentReader>(nullptr),
+ false); // Unref SegmentReader from ImageDecoder.
+ (*decoder)->ClearCacheExceptFrame(index);
+
+ if (!frame || frame->GetStatus() == ImageFrame::kFrameEmpty)
+ return nullptr;
+
+ return frame;
+}
+
+bool ImageFrameGenerator::HasAlpha(size_t index) {
+ MutexLocker lock(alpha_mutex_);
+ if (index < has_alpha_.size())
+ return has_alpha_[index];
+ return true;
+}
+
+bool ImageFrameGenerator::GetYUVComponentSizes(SegmentReader* data,
+ SkYUVSizeInfo* size_info) {
+ TRACE_EVENT2("blink", "ImageFrameGenerator::getYUVComponentSizes", "width",
+ full_size_.width(), "height", full_size_.height());
+
+ if (yuv_decoding_failed_)
+ return false;
+
+ std::unique_ptr<ImageDecoder> decoder = ImageDecoder::Create(
+ data, true, ImageDecoder::kAlphaPremultiplied, decoder_color_behavior_);
+ if (!decoder)
+ return false;
+
+ // Setting a dummy ImagePlanes object signals to the decoder that we want to
+ // do YUV decoding.
+ std::unique_ptr<ImagePlanes> dummy_image_planes =
+ std::make_unique<ImagePlanes>();
+ decoder->SetImagePlanes(std::move(dummy_image_planes));
+
+ return UpdateYUVComponentSizes(decoder.get(), size_info->fSizes,
+ size_info->fWidthBytes);
+}
+
+SkISize ImageFrameGenerator::GetSupportedDecodeSize(
+ const SkISize& requested_size) const {
+ for (auto& size : supported_sizes_) {
+ if (size.width() >= requested_size.width() &&
+ size.height() >= requested_size.height()) {
+ return size;
+ }
+ }
+ return full_size_;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/image_frame_generator.h b/chromium/third_party/blink/renderer/platform/graphics/image_frame_generator.h
new file mode 100644
index 00000000000..d7449212fb4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/image_frame_generator.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2012 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_IMAGE_FRAME_GENERATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_IMAGE_FRAME_GENERATOR_H_
+
+#include <memory>
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h"
+#include "third_party/blink/renderer/platform/image-decoders/segment_reader.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkSize.h"
+#include "third_party/skia/include/core/SkTypes.h"
+
+struct SkYUVSizeInfo;
+
+namespace blink {
+
+class ImageDecoder;
+
+class PLATFORM_EXPORT ImageDecoderFactory {
+ USING_FAST_MALLOC(ImageDecoderFactory);
+ WTF_MAKE_NONCOPYABLE(ImageDecoderFactory);
+
+ public:
+ ImageDecoderFactory() = default;
+ virtual ~ImageDecoderFactory() = default;
+ virtual std::unique_ptr<ImageDecoder> Create() = 0;
+};
+
+class PLATFORM_EXPORT ImageFrameGenerator final
+ : public ThreadSafeRefCounted<ImageFrameGenerator> {
+ WTF_MAKE_NONCOPYABLE(ImageFrameGenerator);
+
+ public:
+ static scoped_refptr<ImageFrameGenerator> Create(
+ const SkISize& full_size,
+ bool is_multi_frame,
+ const ColorBehavior& color_behavior,
+ std::vector<SkISize> supported_sizes) {
+ return base::AdoptRef(new ImageFrameGenerator(
+ full_size, is_multi_frame, color_behavior, std::move(supported_sizes)));
+ }
+
+ ~ImageFrameGenerator();
+
+ // Decodes and scales the specified frame at |index|. The dimensions and
+ // output format are given in SkImageInfo. Decoded pixels are written into
+ // |pixels| with a stride of |rowBytes|. Returns true if decoding was
+ // successful.
+ bool DecodeAndScale(SegmentReader*,
+ bool all_data_received,
+ size_t index,
+ const SkImageInfo&,
+ void* pixels,
+ size_t row_bytes,
+ ImageDecoder::AlphaOption);
+
+ // Decodes YUV components directly into the provided memory planes. Must not
+ // be called unless getYUVComponentSizes has been called and returned true.
+ // YUV decoding does not currently support progressive decoding. In order to
+ // support it, ImageDecoder needs something analagous to its ImageFrame cache
+ // to hold partial planes, and the GPU code needs to handle them.
+ bool DecodeToYUV(SegmentReader*,
+ size_t index,
+ const SkISize component_sizes[3],
+ void* planes[3],
+ const size_t row_bytes[3]);
+
+ const SkISize& GetFullSize() const { return full_size_; }
+
+ SkISize GetSupportedDecodeSize(const SkISize& requested_size) const;
+
+ bool IsMultiFrame() const { return is_multi_frame_; }
+ bool DecodeFailed() const { return decode_failed_; }
+
+ bool HasAlpha(size_t index);
+
+ // Must not be called unless the SkROBuffer has all the data. YUV decoding
+ // does not currently support progressive decoding. See comment above on
+ // decodeToYUV().
+ bool GetYUVComponentSizes(SegmentReader*, SkYUVSizeInfo*);
+
+ private:
+ ImageFrameGenerator(const SkISize& full_size,
+ bool is_multi_frame,
+ const ColorBehavior&,
+ std::vector<SkISize> supported_sizes);
+
+ friend class ImageFrameGeneratorTest;
+ friend class DeferredImageDecoderTest;
+ // For testing. |factory| will overwrite the default ImageDecoder creation
+ // logic if |factory->create()| returns non-zero.
+ void SetImageDecoderFactory(std::unique_ptr<ImageDecoderFactory> factory) {
+ image_decoder_factory_ = std::move(factory);
+ }
+
+ void SetHasAlpha(size_t index, bool has_alpha);
+
+ SkBitmap TryToResumeDecode(SegmentReader*,
+ bool all_data_received,
+ size_t index,
+ const SkISize& scaled_size,
+ SkBitmap::Allocator&,
+ ImageDecoder::AlphaOption);
+ // This method should only be called while decode_mutex_ is locked.
+ // Returns a pointer to frame |index|'s ImageFrame, if available.
+ // Sets |used_external_allocator| to true if the the image was decoded into
+ // |external_allocator|'s memory.
+ ImageFrame* Decode(SegmentReader*,
+ bool all_data_received,
+ size_t index,
+ ImageDecoder**,
+ SkBitmap::Allocator& external_allocator,
+ ImageDecoder::AlphaOption,
+ const SkISize& scaled_size,
+ bool& used_external_allocator);
+
+ const SkISize full_size_;
+
+ // Parameters used to create internal ImageDecoder objects.
+ const ColorBehavior decoder_color_behavior_;
+
+ const bool is_multi_frame_;
+ bool decode_failed_;
+ bool yuv_decoding_failed_;
+ size_t frame_count_;
+ Vector<bool> has_alpha_;
+ std::vector<SkISize> supported_sizes_;
+
+ std::unique_ptr<ImageDecoderFactory> image_decoder_factory_;
+
+ // Prevents multiple decode operations on the same data.
+ Mutex decode_mutex_;
+
+ // Protect concurrent access to has_alpha_.
+ Mutex alpha_mutex_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/graphics/image_frame_generator_test.cc b/chromium/third_party/blink/renderer/platform/graphics/image_frame_generator_test.cc
new file mode 100644
index 00000000000..b9ad8b751f3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/image_frame_generator_test.cc
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/graphics/image_frame_generator.h"
+
+#include <memory>
+#include "base/location.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_thread.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/graphics/image_decoding_store.h"
+#include "third_party/blink/renderer/platform/graphics/test/mock_image_decoder.h"
+#include "third_party/blink/renderer/platform/image-decoders/segment_reader.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
+#include "third_party/blink/renderer/platform/web_task_runner.h"
+
+namespace blink {
+
+namespace {
+
+// Helper methods to generate standard sizes.
+SkISize FullSize() {
+ return SkISize::Make(100, 100);
+}
+
+SkImageInfo ImageInfo() {
+ return SkImageInfo::Make(100, 100, kBGRA_8888_SkColorType,
+ kOpaque_SkAlphaType);
+}
+
+} // namespace
+
+class ImageFrameGeneratorTest : public testing::Test,
+ public MockImageDecoderClient {
+ public:
+ void SetUp() override {
+ ImageDecodingStore::Instance().SetCacheLimitInBytes(1024 * 1024);
+ generator_ = ImageFrameGenerator::Create(FullSize(), false,
+ ColorBehavior::Ignore(), {});
+ data_ = SharedBuffer::Create();
+ segment_reader_ = SegmentReader::CreateFromSharedBuffer(data_);
+ UseMockImageDecoderFactory();
+ decoders_destroyed_ = 0;
+ decode_request_count_ = 0;
+ memory_allocator_set_count_ = 0;
+ status_ = ImageFrame::kFrameEmpty;
+ frame_count_ = 1;
+ requested_clear_except_frame_ = kNotFound;
+ }
+
+ void TearDown() override { ImageDecodingStore::Instance().Clear(); }
+
+ void DecoderBeingDestroyed() override { ++decoders_destroyed_; }
+
+ void DecodeRequested() override { ++decode_request_count_; }
+
+ void MemoryAllocatorSet() override { ++memory_allocator_set_count_; }
+
+ ImageFrame::Status GetStatus(size_t index) override {
+ ImageFrame::Status current_status = status_;
+ status_ = next_frame_status_;
+ return current_status;
+ }
+
+ void ClearCacheExceptFrameRequested(size_t clear_except_frame) override {
+ requested_clear_except_frame_ = clear_except_frame;
+ };
+
+ size_t FrameCount() override { return frame_count_; }
+ int RepetitionCount() const override {
+ return frame_count_ == 1 ? kAnimationNone : kAnimationLoopOnce;
+ }
+ TimeDelta FrameDuration() const override { return TimeDelta(); }
+
+ protected:
+ void UseMockImageDecoderFactory() {
+ generator_->SetImageDecoderFactory(
+ MockImageDecoderFactory::Create(this, FullSize()));
+ }
+
+ void AddNewData() { data_->Append("g", 1u); }
+
+ void SetFrameStatus(ImageFrame::Status status) {
+ status_ = next_frame_status_ = status;
+ }
+ void SetNextFrameStatus(ImageFrame::Status status) {
+ next_frame_status_ = status;
+ }
+ void SetFrameCount(size_t count) {
+ frame_count_ = count;
+ if (count > 1) {
+ generator_ = nullptr;
+ generator_ = ImageFrameGenerator::Create(FullSize(), true,
+ ColorBehavior::Ignore(), {});
+ UseMockImageDecoderFactory();
+ }
+ }
+ void SetSupportedSizes(std::vector<SkISize> sizes) {
+ generator_ = nullptr;
+ generator_ = ImageFrameGenerator::Create(
+ FullSize(), true, ColorBehavior::Ignore(), std::move(sizes));
+ UseMockImageDecoderFactory();
+ }
+
+ scoped_refptr<SharedBuffer> data_;
+ scoped_refptr<SegmentReader> segment_reader_;
+ scoped_refptr<ImageFrameGenerator> generator_;
+ int decoders_destroyed_;
+ int decode_request_count_;
+ int memory_allocator_set_count_;
+ ImageFrame::Status status_;
+ ImageFrame::Status next_frame_status_;
+ size_t frame_count_;
+ size_t requested_clear_except_frame_;
+};
+
+TEST_F(ImageFrameGeneratorTest, GetSupportedSizes) {
+ ASSERT_TRUE(FullSize() == SkISize::Make(100, 100));
+
+ std::vector<SkISize> supported_sizes = {SkISize::Make(2, 2),
+ SkISize::Make(50, 50),
+ SkISize::Make(75, 75), FullSize()};
+ SetSupportedSizes(supported_sizes);
+
+ struct Test {
+ SkISize query_size;
+ size_t supported_size_index;
+ } tests[] = {{SkISize::Make(1, 1), 0}, {SkISize::Make(2, 2), 0},
+ {SkISize::Make(25, 10), 1}, {SkISize::Make(1, 25), 1},
+ {SkISize::Make(50, 51), 2}, {SkISize::Make(80, 80), 3},
+ {SkISize::Make(100, 100), 3}, {SkISize::Make(1000, 1000), 3}};
+ for (auto& test : tests) {
+ EXPECT_TRUE(generator_->GetSupportedDecodeSize(test.query_size) ==
+ supported_sizes[test.supported_size_index]);
+ }
+}
+
+TEST_F(ImageFrameGeneratorTest, incompleteDecode) {
+ SetFrameStatus(ImageFrame::kFramePartial);
+
+ char buffer[100 * 100 * 4];
+ generator_->DecodeAndScale(segment_reader_.get(), false, 0, ImageInfo(),
+ buffer, 100 * 4,
+ ImageDecoder::kAlphaPremultiplied);
+ EXPECT_EQ(1, decode_request_count_);
+ EXPECT_EQ(0, memory_allocator_set_count_);
+
+ AddNewData();
+ generator_->DecodeAndScale(segment_reader_.get(), false, 0, ImageInfo(),
+ buffer, 100 * 4,
+ ImageDecoder::kAlphaPremultiplied);
+ EXPECT_EQ(2, decode_request_count_);
+ EXPECT_EQ(0, decoders_destroyed_);
+ EXPECT_EQ(0, memory_allocator_set_count_);
+}
+
+class ImageFrameGeneratorTestPlatform : public TestingPlatformSupport {
+ public:
+ bool IsLowEndDevice() override { return true; }
+};
+
+// This is the same as incompleteData, but with a low-end device set.
+TEST_F(ImageFrameGeneratorTest, LowEndDeviceDestroysDecoderOnPartialDecode) {
+ ScopedTestingPlatformSupport<ImageFrameGeneratorTestPlatform> platform;
+
+ SetFrameStatus(ImageFrame::kFramePartial);
+
+ char buffer[100 * 100 * 4];
+ generator_->DecodeAndScale(segment_reader_.get(), false, 0, ImageInfo(),
+ buffer, 100 * 4,
+ ImageDecoder::kAlphaPremultiplied);
+ EXPECT_EQ(1, decode_request_count_);
+ EXPECT_EQ(1, decoders_destroyed_);
+ // The memory allocator is set to the external one, then cleared after decode.
+ EXPECT_EQ(2, memory_allocator_set_count_);
+
+ AddNewData();
+ generator_->DecodeAndScale(segment_reader_.get(), false, 0, ImageInfo(),
+ buffer, 100 * 4,
+ ImageDecoder::kAlphaPremultiplied);
+ EXPECT_EQ(2, decode_request_count_);
+ EXPECT_EQ(2, decoders_destroyed_);
+ // The memory allocator is set to the external one, then cleared after decode.
+ EXPECT_EQ(4, memory_allocator_set_count_);
+}
+
+TEST_F(ImageFrameGeneratorTest, incompleteDecodeBecomesComplete) {
+ SetFrameStatus(ImageFrame::kFramePartial);
+
+ char buffer[100 * 100 * 4];
+ generator_->DecodeAndScale(segment_reader_.get(), false, 0, ImageInfo(),
+ buffer, 100 * 4,
+ ImageDecoder::kAlphaPremultiplied);
+ EXPECT_EQ(1, decode_request_count_);
+ EXPECT_EQ(0, decoders_destroyed_);
+ EXPECT_EQ(0, memory_allocator_set_count_);
+
+ SetFrameStatus(ImageFrame::kFrameComplete);
+ AddNewData();
+
+ generator_->DecodeAndScale(segment_reader_.get(), false, 0, ImageInfo(),
+ buffer, 100 * 4,
+ ImageDecoder::kAlphaPremultiplied);
+ EXPECT_EQ(2, decode_request_count_);
+ EXPECT_EQ(1, decoders_destroyed_);
+
+ // Decoder created again.
+ generator_->DecodeAndScale(segment_reader_.get(), false, 0, ImageInfo(),
+ buffer, 100 * 4,
+ ImageDecoder::kAlphaPremultiplied);
+ EXPECT_EQ(3, decode_request_count_);
+}
+
+static void DecodeThreadMain(ImageFrameGenerator* generator,
+ SegmentReader* segment_reader) {
+ char buffer[100 * 100 * 4];
+ generator->DecodeAndScale(segment_reader, false, 0, ImageInfo(), buffer,
+ 100 * 4, ImageDecoder::kAlphaPremultiplied);
+}
+
+TEST_F(ImageFrameGeneratorTest, incompleteDecodeBecomesCompleteMultiThreaded) {
+ SetFrameStatus(ImageFrame::kFramePartial);
+
+ char buffer[100 * 100 * 4];
+ generator_->DecodeAndScale(segment_reader_.get(), false, 0, ImageInfo(),
+ buffer, 100 * 4,
+ ImageDecoder::kAlphaPremultiplied);
+ EXPECT_EQ(1, decode_request_count_);
+ EXPECT_EQ(0, decoders_destroyed_);
+
+ // LocalFrame can now be decoded completely.
+ SetFrameStatus(ImageFrame::kFrameComplete);
+ AddNewData();
+ std::unique_ptr<WebThread> thread = Platform::Current()->CreateThread(
+ WebThreadCreationParams(WebThreadType::kTestThread)
+ .SetThreadNameForTest("DecodeThread"));
+ PostCrossThreadTask(
+ *thread->GetTaskRunner(), FROM_HERE,
+ CrossThreadBind(&DecodeThreadMain, WTF::RetainedRef(generator_),
+ WTF::RetainedRef(segment_reader_)));
+ thread.reset();
+ EXPECT_EQ(2, decode_request_count_);
+ EXPECT_EQ(1, decoders_destroyed_);
+
+ // Decoder created again.
+ generator_->DecodeAndScale(segment_reader_.get(), false, 0, ImageInfo(),
+ buffer, 100 * 4,
+ ImageDecoder::kAlphaPremultiplied);
+ EXPECT_EQ(3, decode_request_count_);
+
+ AddNewData();
+
+ // Delete generator.
+ generator_ = nullptr;
+}
+
+TEST_F(ImageFrameGeneratorTest, frameHasAlpha) {
+ SetFrameStatus(ImageFrame::kFramePartial);
+
+ char buffer[100 * 100 * 4];
+ generator_->DecodeAndScale(segment_reader_.get(), false, 0, ImageInfo(),
+ buffer, 100 * 4,
+ ImageDecoder::kAlphaPremultiplied);
+ EXPECT_TRUE(generator_->HasAlpha(0));
+ EXPECT_EQ(1, decode_request_count_);
+
+ ImageDecoder* temp_decoder = nullptr;
+ EXPECT_TRUE(ImageDecodingStore::Instance().LockDecoder(
+ generator_.get(), FullSize(), ImageDecoder::kAlphaPremultiplied,
+ &temp_decoder));
+ ASSERT_TRUE(temp_decoder);
+ temp_decoder->DecodeFrameBufferAtIndex(0)->SetHasAlpha(false);
+ ImageDecodingStore::Instance().UnlockDecoder(generator_.get(), temp_decoder);
+ EXPECT_EQ(2, decode_request_count_);
+
+ SetFrameStatus(ImageFrame::kFrameComplete);
+ generator_->DecodeAndScale(segment_reader_.get(), false, 0, ImageInfo(),
+ buffer, 100 * 4,
+ ImageDecoder::kAlphaPremultiplied);
+ EXPECT_EQ(3, decode_request_count_);
+ EXPECT_FALSE(generator_->HasAlpha(0));
+}
+
+TEST_F(ImageFrameGeneratorTest, clearMultiFrameDecoder) {
+ SetFrameCount(3);
+ SetFrameStatus(ImageFrame::kFrameComplete);
+
+ char buffer[100 * 100 * 4];
+ generator_->DecodeAndScale(segment_reader_.get(), true, 0, ImageInfo(),
+ buffer, 100 * 4,
+ ImageDecoder::kAlphaPremultiplied);
+ EXPECT_EQ(1, decode_request_count_);
+ EXPECT_EQ(0, decoders_destroyed_);
+ EXPECT_EQ(0U, requested_clear_except_frame_);
+
+ SetFrameStatus(ImageFrame::kFrameComplete);
+
+ generator_->DecodeAndScale(segment_reader_.get(), true, 1, ImageInfo(),
+ buffer, 100 * 4,
+ ImageDecoder::kAlphaPremultiplied);
+ EXPECT_EQ(2, decode_request_count_);
+ EXPECT_EQ(0, decoders_destroyed_);
+ EXPECT_EQ(1U, requested_clear_except_frame_);
+
+ SetFrameStatus(ImageFrame::kFrameComplete);
+
+ // Decoding the last frame of a multi-frame images should trigger clearing
+ // all the frame data, but not destroying the decoder. See comments in
+ // ImageFrameGenerator::tryToResumeDecode().
+ generator_->DecodeAndScale(segment_reader_.get(), true, 2, ImageInfo(),
+ buffer, 100 * 4,
+ ImageDecoder::kAlphaPremultiplied);
+ EXPECT_EQ(3, decode_request_count_);
+ EXPECT_EQ(0, decoders_destroyed_);
+ EXPECT_EQ(kNotFound, requested_clear_except_frame_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/image_layer_chromium_test.cc b/chromium/third_party/blink/renderer/platform/graphics/image_layer_chromium_test.cc
new file mode 100644
index 00000000000..f61755d7a24
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/image_layer_chromium_test.cc
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/graphics/image.h"
+
+#include <memory>
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
+#include "third_party/blink/renderer/platform/testing/fake_graphics_layer.h"
+#include "third_party/blink/renderer/platform/testing/fake_graphics_layer_client.h"
+#include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/core/SkSurface.h"
+
+namespace blink {
+
+namespace {
+
+class TestImage : public Image {
+ public:
+ static scoped_refptr<TestImage> Create(const IntSize& size, bool opaque) {
+ return base::AdoptRef(new TestImage(size, opaque));
+ }
+
+ bool CurrentFrameKnownToBeOpaque() override { return image_->isOpaque(); }
+
+ IntSize Size() const override { return size_; }
+
+ void DestroyDecodedData() override {
+ // Image pure virtual stub.
+ }
+
+ void Draw(PaintCanvas*,
+ const PaintFlags&,
+ const FloatRect&,
+ const FloatRect&,
+ RespectImageOrientationEnum,
+ ImageClampingMode,
+ ImageDecodingMode) override {
+ // Image pure virtual stub.
+ }
+
+ PaintImage PaintImageForCurrentFrame() override {
+ return CreatePaintImageBuilder()
+ .set_image(image_, PaintImage::GetNextContentId())
+ .TakePaintImage();
+ }
+
+ private:
+ TestImage(IntSize size, bool opaque) : Image(nullptr), size_(size) {
+ sk_sp<SkSurface> surface = CreateSkSurface(size, opaque);
+ if (!surface)
+ return;
+
+ surface->getCanvas()->clear(SK_ColorTRANSPARENT);
+ image_ = surface->makeImageSnapshot();
+ }
+
+ static sk_sp<SkSurface> CreateSkSurface(IntSize size, bool opaque) {
+ return SkSurface::MakeRaster(SkImageInfo::MakeN32(
+ size.Width(), size.Height(),
+ opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType));
+ }
+
+ IntSize size_;
+ sk_sp<SkImage> image_;
+};
+
+} // anonymous namespace
+
+TEST(ImageLayerChromiumTest, imageLayerContentReset) {
+ FakeGraphicsLayerClient client;
+ std::unique_ptr<FakeGraphicsLayer> graphics_layer =
+ std::make_unique<FakeGraphicsLayer>(client);
+ ASSERT_TRUE(graphics_layer.get());
+
+ ASSERT_FALSE(graphics_layer->HasContentsLayer());
+ ASSERT_FALSE(graphics_layer->ContentsLayer());
+
+ bool opaque = false;
+ scoped_refptr<Image> image = TestImage::Create(IntSize(100, 100), opaque);
+ ASSERT_TRUE(image.get());
+
+ graphics_layer->SetContentsToImage(image.get(), Image::kUnspecifiedDecode);
+ ASSERT_TRUE(graphics_layer->HasContentsLayer());
+ ASSERT_TRUE(graphics_layer->ContentsLayer());
+
+ graphics_layer->SetContentsToImage(nullptr, Image::kUnspecifiedDecode);
+ ASSERT_FALSE(graphics_layer->HasContentsLayer());
+ ASSERT_FALSE(graphics_layer->ContentsLayer());
+}
+
+TEST(ImageLayerChromiumTest, opaqueImages) {
+ FakeGraphicsLayerClient client;
+ std::unique_ptr<FakeGraphicsLayer> graphics_layer =
+ std::make_unique<FakeGraphicsLayer>(client);
+ ASSERT_TRUE(graphics_layer.get());
+
+ bool opaque = true;
+ scoped_refptr<Image> opaque_image =
+ TestImage::Create(IntSize(100, 100), opaque);
+ ASSERT_TRUE(opaque_image.get());
+ scoped_refptr<Image> non_opaque_image =
+ TestImage::Create(IntSize(100, 100), !opaque);
+ ASSERT_TRUE(non_opaque_image.get());
+
+ ASSERT_FALSE(graphics_layer->ContentsLayer());
+
+ graphics_layer->SetContentsToImage(opaque_image.get(),
+ Image::kUnspecifiedDecode);
+ ASSERT_TRUE(graphics_layer->ContentsLayer()->Opaque());
+
+ graphics_layer->SetContentsToImage(non_opaque_image.get(),
+ Image::kUnspecifiedDecode);
+ ASSERT_FALSE(graphics_layer->ContentsLayer()->Opaque());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/image_observer.cc b/chromium/third_party/blink/renderer/platform/graphics/image_observer.cc
new file mode 100644
index 00000000000..48b54b8bbac
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/image_observer.cc
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2013 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:
+ *
+ * 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. ("Google") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/graphics/image_observer.h"
+
+namespace blink {
+
+ImageObserver::~ImageObserver() = default;
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/image_observer.h b/chromium/third_party/blink/renderer/platform/graphics/image_observer.h
new file mode 100644
index 00000000000..9417c7794ae
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/image_observer.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2004, 2005, 2006 Apple Computer, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_IMAGE_OBSERVER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_IMAGE_OBSERVER_H_
+
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+
+class Image;
+class IntRect;
+
+// Interface for notification about changes to an image, including decoding,
+// drawing, and animating.
+class PLATFORM_EXPORT ImageObserver : public GarbageCollectedMixin {
+ protected:
+ virtual ~ImageObserver();
+
+ public:
+ virtual void DecodedSizeChangedTo(const Image*, size_t new_size) = 0;
+
+ virtual bool ShouldPauseAnimation(const Image*) = 0;
+ virtual void AnimationAdvanced(const Image*) = 0;
+
+ virtual void ChangedInRect(const Image*, const IntRect&) = 0;
+
+ // See the comment of Image::SetData().
+ virtual void AsyncLoadCompleted(const Image*) = 0;
+
+ virtual void Trace(blink::Visitor* visitor) {}
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/graphics/image_orientation.cc b/chromium/third_party/blink/renderer/platform/graphics/image_orientation.cc
new file mode 100644
index 00000000000..0487ca31057
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/image_orientation.cc
@@ -0,0 +1,63 @@
+
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/graphics/image_orientation.h"
+
+#include "third_party/blink/renderer/platform/transforms/affine_transform.h"
+
+namespace blink {
+
+AffineTransform ImageOrientation::TransformFromDefault(
+ const FloatSize& drawn_size) const {
+ float w = drawn_size.Width();
+ float h = drawn_size.Height();
+
+ switch (orientation_) {
+ case kOriginTopLeft:
+ return AffineTransform();
+ case kOriginTopRight:
+ return AffineTransform(-1, 0, 0, 1, w, 0);
+ case kOriginBottomRight:
+ return AffineTransform(-1, 0, 0, -1, w, h);
+ case kOriginBottomLeft:
+ return AffineTransform(1, 0, 0, -1, 0, h);
+ case kOriginLeftTop:
+ return AffineTransform(0, 1, 1, 0, 0, 0);
+ case kOriginRightTop:
+ return AffineTransform(0, 1, -1, 0, w, 0);
+ case kOriginRightBottom:
+ return AffineTransform(0, -1, -1, 0, w, h);
+ case kOriginLeftBottom:
+ return AffineTransform(0, -1, 1, 0, 0, h);
+ default:
+ NOTREACHED();
+ }
+
+ NOTREACHED();
+ return AffineTransform();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/image_orientation.h b/chromium/third_party/blink/renderer/platform/graphics/image_orientation.h
new file mode 100644
index 00000000000..20c26028f56
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/image_orientation.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_IMAGE_ORIENTATION_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_IMAGE_ORIENTATION_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+class AffineTransform;
+class FloatSize;
+
+// This enum intentionally matches the orientation values from the EXIF spec.
+// See JEITA CP-3451, page 18. http://www.exif.org/Exif2-2.PDF
+enum ImageOrientationEnum {
+ // "TopLeft" means that the 0 row starts at the Top, the 0 column starts at
+ // the Left.
+ kOriginTopLeft = 1, // default
+ kOriginTopRight = 2, // mirror along y-axis
+ kOriginBottomRight = 3, // 180 degree rotation
+ kOriginBottomLeft = 4, // mirror along the x-axis
+ kOriginLeftTop = 5, // mirror along x-axis + 270 degree CW rotation
+ kOriginRightTop = 6, // 90 degree CW rotation
+ kOriginRightBottom = 7, // mirror along x-axis + 90 degree CW rotation
+ kOriginLeftBottom = 8, // 270 degree CW rotation
+ // All other values are "reserved" as of EXIF 2.2
+ kDefaultImageOrientation = kOriginTopLeft,
+ kImageOrientationEnumEnd = kOriginLeftBottom + 1,
+};
+
+enum RespectImageOrientationEnum {
+ kDoNotRespectImageOrientation = 0,
+ kRespectImageOrientation = 1
+};
+
+class PLATFORM_EXPORT ImageOrientation final {
+ DISALLOW_NEW();
+
+ public:
+ ImageOrientation(ImageOrientationEnum orientation = kDefaultImageOrientation)
+ : orientation_(orientation) {}
+
+ bool UsesWidthAsHeight() const {
+ // Values 5 through 8 all flip the width/height.
+ return orientation_ >= kOriginLeftTop;
+ }
+
+ // ImageOrientationEnum currently matches EXIF values, however code outside
+ // this function should never assume that.
+ static ImageOrientation FromEXIFValue(int exif_value) {
+ // Values direct from images may be invalid, in which case we use the
+ // default.
+ if (exif_value < kOriginTopLeft || exif_value > kOriginLeftBottom)
+ return kDefaultImageOrientation;
+ return static_cast<ImageOrientationEnum>(exif_value);
+ }
+
+ // This transform can be used for drawing an image according to the
+ // orientation. It should be used in a right-handed coordinate system.
+ AffineTransform TransformFromDefault(const FloatSize& drawn_size) const;
+
+ inline bool operator==(const ImageOrientation& other) const {
+ return other.orientation_ == orientation_;
+ }
+ inline bool operator!=(const ImageOrientation& other) const {
+ return !(*this == other);
+ }
+
+ ImageOrientationEnum Orientation() const { return orientation_; }
+
+ private:
+ // FIXME: This only needs to be one byte.
+ ImageOrientationEnum orientation_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_IMAGE_ORIENTATION_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/image_pattern.cc b/chromium/third_party/blink/renderer/platform/graphics/image_pattern.cc
new file mode 100644
index 00000000000..2eb7a86108b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/image_pattern.cc
@@ -0,0 +1,82 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/image_pattern.h"
+
+#include "third_party/blink/renderer/platform/graphics/image.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_recorder.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_shader.h"
+#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
+#include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/core/SkSurface.h"
+
+namespace blink {
+
+scoped_refptr<ImagePattern> ImagePattern::Create(scoped_refptr<Image> image,
+ RepeatMode repeat_mode) {
+ return base::AdoptRef(new ImagePattern(std::move(image), repeat_mode));
+}
+
+ImagePattern::ImagePattern(scoped_refptr<Image> image, RepeatMode repeat_mode)
+ : Pattern(repeat_mode), tile_image_(image->PaintImageForCurrentFrame()) {
+ previous_local_matrix_.setIdentity();
+}
+
+bool ImagePattern::IsLocalMatrixChanged(const SkMatrix& local_matrix) const {
+ if (IsRepeatXY())
+ return Pattern::IsLocalMatrixChanged(local_matrix);
+ return local_matrix != previous_local_matrix_;
+}
+
+sk_sp<PaintShader> ImagePattern::CreateShader(const SkMatrix& local_matrix) {
+ if (!tile_image_) {
+ return PaintShader::MakeColor(SK_ColorTRANSPARENT);
+ }
+
+ if (IsRepeatXY()) {
+ // Fast path: for repeatXY we just return a shader from the original image.
+ return PaintShader::MakeImage(tile_image_, SkShader::kRepeat_TileMode,
+ SkShader::kRepeat_TileMode, &local_matrix);
+ }
+
+ // Skia does not have a "draw the tile only once" option. Clamp_TileMode
+ // repeats the last line of the image after drawing one tile. To avoid
+ // filling the space with arbitrary pixels, this workaround forces the
+ // image to have a line of transparent pixels on the "repeated" edge(s),
+ // thus causing extra space to be transparent filled.
+ SkShader::TileMode tile_mode_x =
+ IsRepeatX() ? SkShader::kRepeat_TileMode : SkShader::kClamp_TileMode;
+ SkShader::TileMode tile_mode_y =
+ IsRepeatY() ? SkShader::kRepeat_TileMode : SkShader::kClamp_TileMode;
+ int border_pixel_x = IsRepeatX() ? 0 : 1;
+ int border_pixel_y = IsRepeatY() ? 0 : 1;
+
+ // Create a transparent image 2 pixels wider and/or taller than the
+ // original, then copy the orignal into the middle of it.
+ const SkRect tile_bounds =
+ SkRect::MakeWH(tile_image_.width() + 2 * border_pixel_x,
+ tile_image_.height() + 2 * border_pixel_y);
+ PaintRecorder recorder;
+ auto* canvas = recorder.beginRecording(tile_bounds);
+
+ PaintFlags paint;
+ paint.setBlendMode(SkBlendMode::kSrc);
+ canvas->drawImage(tile_image_, border_pixel_x, border_pixel_y, &paint);
+
+ previous_local_matrix_ = local_matrix;
+ SkMatrix adjusted_matrix(local_matrix);
+ adjusted_matrix.postTranslate(-border_pixel_x, -border_pixel_y);
+
+ // Note: we specify kFixedScale to lock-in the resolution (for 1px padding in
+ // particular).
+ return PaintShader::MakePaintRecord(
+ recorder.finishRecordingAsPicture(), tile_bounds, tile_mode_x,
+ tile_mode_y, &adjusted_matrix, PaintShader::ScalingBehavior::kFixedScale);
+}
+
+bool ImagePattern::IsTextureBacked() const {
+ return tile_image_ && tile_image_.GetSkImage()->isTextureBacked();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/image_pattern.h b/chromium/third_party/blink/renderer/platform/graphics/image_pattern.h
new file mode 100644
index 00000000000..db42125f29e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/image_pattern.h
@@ -0,0 +1,34 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_IMAGE_PATTERN_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_IMAGE_PATTERN_H_
+
+#include "third_party/blink/renderer/platform/graphics/paint/paint_image.h"
+#include "third_party/blink/renderer/platform/graphics/pattern.h"
+
+namespace blink {
+
+class Image;
+
+class PLATFORM_EXPORT ImagePattern final : public Pattern {
+ public:
+ static scoped_refptr<ImagePattern> Create(scoped_refptr<Image>, RepeatMode);
+
+ bool IsTextureBacked() const override;
+
+ protected:
+ sk_sp<PaintShader> CreateShader(const SkMatrix&) override;
+ bool IsLocalMatrixChanged(const SkMatrix&) const override;
+
+ private:
+ ImagePattern(scoped_refptr<Image>, RepeatMode);
+ SkMatrix previous_local_matrix_;
+
+ PaintImage tile_image_;
+};
+
+} // namespace blink
+
+#endif /* ImagePattern_h */
diff --git a/chromium/third_party/blink/renderer/platform/graphics/intercepting_canvas.cc b/chromium/third_party/blink/renderer/platform/graphics/intercepting_canvas.cc
new file mode 100644
index 00000000000..255c8b74b13
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/intercepting_canvas.cc
@@ -0,0 +1,31 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/intercepting_canvas.h"
+
+namespace blink {
+
+void InterceptingCanvasBase::UnrollDrawPicture(
+ const SkPicture* picture,
+ const SkMatrix* matrix,
+ const SkPaint* paint,
+ SkPicture::AbortCallback* abort_callback) {
+ int save_count = this->getSaveCount();
+ if (paint) {
+ SkRect new_bounds = picture->cullRect();
+ if (matrix)
+ matrix->mapRect(&new_bounds);
+ this->saveLayer(&new_bounds, paint);
+ } else if (matrix) {
+ this->save();
+ }
+ if (matrix)
+ this->concat(*matrix);
+
+ picture->playback(this, abort_callback);
+
+ this->restoreToCount(save_count);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/intercepting_canvas.h b/chromium/third_party/blink/renderer/platform/graphics/intercepting_canvas.h
new file mode 100644
index 00000000000..1b30367aebd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/intercepting_canvas.h
@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_INTERCEPTING_CANVAS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_INTERCEPTING_CANVAS_H_
+
+#include "third_party/blink/renderer/platform/graphics/paint/paint_record.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+
+namespace blink {
+
+class InterceptingCanvasBase : public SkCanvas {
+ WTF_MAKE_NONCOPYABLE(InterceptingCanvasBase);
+
+ public:
+ template <typename DerivedCanvas>
+ class CanvasInterceptorBase {
+ STACK_ALLOCATED();
+ WTF_MAKE_NONCOPYABLE(CanvasInterceptorBase);
+
+ protected:
+ CanvasInterceptorBase(InterceptingCanvasBase* canvas) : canvas_(canvas) {
+ ++canvas_->call_nesting_depth_;
+ }
+
+ ~CanvasInterceptorBase() {
+ DCHECK_GT(canvas_->call_nesting_depth_, 0u);
+ if (!--canvas_->call_nesting_depth_)
+ canvas_->call_count_++;
+ }
+
+ DerivedCanvas* Canvas() { return static_cast<DerivedCanvas*>(canvas_); }
+ bool TopLevelCall() const { return canvas_->CallNestingDepth() == 1; }
+ InterceptingCanvasBase* canvas_;
+ };
+
+ void ResetStepCount() { call_count_ = 0; }
+
+ protected:
+ explicit InterceptingCanvasBase(SkBitmap bitmap)
+ : SkCanvas(bitmap), call_nesting_depth_(0), call_count_(0) {}
+ InterceptingCanvasBase(int width, int height)
+ : SkCanvas(width, height), call_nesting_depth_(0), call_count_(0) {}
+
+ void UnrollDrawPicture(const SkPicture*,
+ const SkMatrix*,
+ const SkPaint*,
+ SkPicture::AbortCallback*);
+
+ void onDrawPaint(const SkPaint&) override = 0;
+ void onDrawPoints(PointMode,
+ size_t count,
+ const SkPoint pts[],
+ const SkPaint&) override = 0;
+ void onDrawRect(const SkRect&, const SkPaint&) override = 0;
+ void onDrawOval(const SkRect&, const SkPaint&) override = 0;
+ void onDrawRRect(const SkRRect&, const SkPaint&) override = 0;
+ void onDrawPath(const SkPath&, const SkPaint&) override = 0;
+ void onDrawBitmap(const SkBitmap&,
+ SkScalar left,
+ SkScalar top,
+ const SkPaint*) override = 0;
+ void onDrawBitmapRect(const SkBitmap&,
+ const SkRect* src,
+ const SkRect& dst,
+ const SkPaint*,
+ SrcRectConstraint) override = 0;
+ void onDrawBitmapNine(const SkBitmap&,
+ const SkIRect& center,
+ const SkRect& dst,
+ const SkPaint*) override = 0;
+ void onDrawImage(const SkImage*,
+ SkScalar,
+ SkScalar,
+ const SkPaint*) override = 0;
+ void onDrawImageRect(const SkImage*,
+ const SkRect* src,
+ const SkRect& dst,
+ const SkPaint*,
+ SrcRectConstraint) override = 0;
+ void onDrawVerticesObject(const SkVertices*,
+ SkBlendMode bmode,
+ const SkPaint&) override = 0;
+
+ void onDrawDRRect(const SkRRect& outer,
+ const SkRRect& inner,
+ const SkPaint&) override = 0;
+ void onDrawText(const void* text,
+ size_t byte_length,
+ SkScalar x,
+ SkScalar y,
+ const SkPaint&) override = 0;
+ void onDrawPosText(const void* text,
+ size_t byte_length,
+ const SkPoint pos[],
+ const SkPaint&) override = 0;
+ void onDrawPosTextH(const void* text,
+ size_t byte_length,
+ const SkScalar xpos[],
+ SkScalar const_y,
+ const SkPaint&) override = 0;
+ void onDrawTextOnPath(const void* text,
+ size_t byte_length,
+ const SkPath&,
+ const SkMatrix*,
+ const SkPaint&) override = 0;
+ void onDrawTextBlob(const SkTextBlob*,
+ SkScalar x,
+ SkScalar y,
+ const SkPaint&) override = 0;
+ void onClipRect(const SkRect&, SkClipOp, ClipEdgeStyle) override = 0;
+ void onClipRRect(const SkRRect&, SkClipOp, ClipEdgeStyle) override = 0;
+ void onClipPath(const SkPath&, SkClipOp, ClipEdgeStyle) override = 0;
+ void onClipRegion(const SkRegion&, SkClipOp) override = 0;
+ void onDrawPicture(const SkPicture*,
+ const SkMatrix*,
+ const SkPaint*) override = 0;
+ void didSetMatrix(const SkMatrix&) override = 0;
+ void didConcat(const SkMatrix&) override = 0;
+ void willSave() override = 0;
+ SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec&) override = 0;
+ void willRestore() override = 0;
+
+ unsigned CallNestingDepth() const { return call_nesting_depth_; }
+ unsigned CallCount() const { return call_count_; }
+
+ private:
+ unsigned call_nesting_depth_;
+ unsigned call_count_;
+};
+
+template <typename DerivedCanvas>
+class CanvasInterceptor {};
+
+template <typename DerivedCanvas,
+ typename Interceptor = CanvasInterceptor<DerivedCanvas>>
+class InterceptingCanvas : public InterceptingCanvasBase {
+ protected:
+ explicit InterceptingCanvas(SkBitmap bitmap)
+ : InterceptingCanvasBase(bitmap) {}
+ InterceptingCanvas(int width, int height)
+ : InterceptingCanvasBase(width, height) {}
+
+ void onDrawPaint(const SkPaint& paint) override {
+ Interceptor interceptor(this);
+ this->SkCanvas::onDrawPaint(paint);
+ }
+
+ void onDrawPoints(PointMode mode,
+ size_t count,
+ const SkPoint pts[],
+ const SkPaint& paint) override {
+ Interceptor interceptor(this);
+ this->SkCanvas::onDrawPoints(mode, count, pts, paint);
+ }
+
+ void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
+ Interceptor interceptor(this);
+ this->SkCanvas::onDrawRect(rect, paint);
+ }
+
+ void onDrawOval(const SkRect& rect, const SkPaint& paint) override {
+ Interceptor interceptor(this);
+ this->SkCanvas::onDrawOval(rect, paint);
+ }
+
+ void onDrawRRect(const SkRRect& rrect, const SkPaint& paint) override {
+ Interceptor interceptor(this);
+ this->SkCanvas::onDrawRRect(rrect, paint);
+ }
+
+ void onDrawPath(const SkPath& path, const SkPaint& paint) override {
+ Interceptor interceptor(this);
+ this->SkCanvas::onDrawPath(path, paint);
+ }
+
+ void onDrawBitmap(const SkBitmap& bitmap,
+ SkScalar left,
+ SkScalar top,
+ const SkPaint* paint) override {
+ Interceptor interceptor(this);
+ this->SkCanvas::onDrawBitmap(bitmap, left, top, paint);
+ }
+
+ void onDrawBitmapRect(const SkBitmap& bitmap,
+ const SkRect* src,
+ const SkRect& dst,
+ const SkPaint* paint,
+ SrcRectConstraint constraint) override {
+ Interceptor interceptor(this);
+ this->SkCanvas::onDrawBitmapRect(bitmap, src, dst, paint, constraint);
+ }
+
+ void onDrawBitmapNine(const SkBitmap& bitmap,
+ const SkIRect& center,
+ const SkRect& dst,
+ const SkPaint* paint) override {
+ Interceptor interceptor(this);
+ this->SkCanvas::onDrawBitmapNine(bitmap, center, dst, paint);
+ }
+
+ void onDrawImage(const SkImage* image,
+ SkScalar x,
+ SkScalar y,
+ const SkPaint* paint) override {
+ Interceptor interceptor(this);
+ this->SkCanvas::onDrawImage(image, x, y, paint);
+ }
+
+ void onDrawImageRect(const SkImage* image,
+ const SkRect* src,
+ const SkRect& dst,
+ const SkPaint* paint,
+ SrcRectConstraint constraint) override {
+ Interceptor interceptor(this);
+ this->SkCanvas::onDrawImageRect(image, src, dst, paint, constraint);
+ }
+
+ void onDrawVerticesObject(const SkVertices* vertices,
+ SkBlendMode bmode,
+ const SkPaint& paint) override {
+ Interceptor interceptor(this);
+ this->SkCanvas::onDrawVerticesObject(vertices, bmode, paint);
+ }
+
+ void onDrawDRRect(const SkRRect& outer,
+ const SkRRect& inner,
+ const SkPaint& paint) override {
+ Interceptor interceptor(this);
+ this->SkCanvas::onDrawDRRect(outer, inner, paint);
+ }
+
+ void onDrawText(const void* text,
+ size_t byte_length,
+ SkScalar x,
+ SkScalar y,
+ const SkPaint& paint) override {
+ Interceptor interceptor(this);
+ this->SkCanvas::onDrawText(text, byte_length, x, y, paint);
+ }
+
+ void onDrawPosText(const void* text,
+ size_t byte_length,
+ const SkPoint pos[],
+ const SkPaint& paint) override {
+ Interceptor interceptor(this);
+ this->SkCanvas::onDrawPosText(text, byte_length, pos, paint);
+ }
+
+ void onDrawPosTextH(const void* text,
+ size_t byte_length,
+ const SkScalar xpos[],
+ SkScalar const_y,
+ const SkPaint& paint) override {
+ Interceptor interceptor(this);
+ this->SkCanvas::onDrawPosTextH(text, byte_length, xpos, const_y, paint);
+ }
+
+ void onDrawTextOnPath(const void* text,
+ size_t byte_length,
+ const SkPath& path,
+ const SkMatrix* matrix,
+ const SkPaint& paint) override {
+ Interceptor interceptor(this);
+ this->SkCanvas::onDrawTextOnPath(text, byte_length, path, matrix, paint);
+ }
+
+ void onDrawTextBlob(const SkTextBlob* blob,
+ SkScalar x,
+ SkScalar y,
+ const SkPaint& paint) override {
+ Interceptor interceptor(this);
+ this->SkCanvas::onDrawTextBlob(blob, x, y, paint);
+ }
+
+ void onClipRect(const SkRect& rect,
+ SkClipOp op,
+ ClipEdgeStyle edge_style) override {
+ Interceptor interceptor(this);
+ this->SkCanvas::onClipRect(rect, op, edge_style);
+ }
+
+ void onClipRRect(const SkRRect& rrect,
+ SkClipOp op,
+ ClipEdgeStyle edge_style) override {
+ Interceptor interceptor(this);
+ this->SkCanvas::onClipRRect(rrect, op, edge_style);
+ }
+
+ void onClipPath(const SkPath& path,
+ SkClipOp op,
+ ClipEdgeStyle edge_style) override {
+ Interceptor interceptor(this);
+ this->SkCanvas::onClipPath(path, op, edge_style);
+ }
+
+ void onClipRegion(const SkRegion& region, SkClipOp op) override {
+ Interceptor interceptor(this);
+ this->SkCanvas::onClipRegion(region, op);
+ }
+
+ void onDrawPicture(const SkPicture* picture,
+ const SkMatrix* matrix,
+ const SkPaint* paint) override {
+ this->UnrollDrawPicture(picture, matrix, paint, nullptr);
+ }
+
+ void didSetMatrix(const SkMatrix& matrix) override {
+ Interceptor interceptor(this);
+ this->SkCanvas::didSetMatrix(matrix);
+ }
+
+ void didConcat(const SkMatrix& matrix) override {
+ Interceptor interceptor(this);
+ this->SkCanvas::didConcat(matrix);
+ }
+
+ void willSave() override {
+ Interceptor interceptor(this);
+ this->SkCanvas::willSave();
+ }
+
+ SkCanvas::SaveLayerStrategy getSaveLayerStrategy(
+ const SaveLayerRec& rec) override {
+ Interceptor interceptor(this);
+ return this->SkCanvas::getSaveLayerStrategy(rec);
+ }
+
+ void willRestore() override {
+ Interceptor interceptor(this);
+ this->SkCanvas::willRestore();
+ }
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_INTERCEPTING_CANVAS_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/interpolation_space.cc b/chromium/third_party/blink/renderer/platform/graphics/interpolation_space.cc
new file mode 100644
index 00000000000..19f751e0939
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/interpolation_space.cc
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2008, Google Inc. All rights reserved.
+ * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
+ * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. 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 "third_party/blink/renderer/platform/graphics/interpolation_space.h"
+
+#include "third_party/skia/include/core/SkColorFilter.h"
+
+namespace blink {
+
+namespace InterpolationSpaceUtilities {
+
+namespace {
+
+sk_sp<SkColorFilter> GetConversionFilter(
+ InterpolationSpace dst_interpolation_space,
+ InterpolationSpace src_interpolation_space) {
+ // Identity.
+ if (src_interpolation_space == dst_interpolation_space)
+ return nullptr;
+
+ switch (dst_interpolation_space) {
+ case kInterpolationSpaceLinear:
+ return SkColorFilter::MakeSRGBToLinearGamma();
+ case kInterpolationSpaceSRGB:
+ return SkColorFilter::MakeLinearToSRGBGamma();
+ }
+
+ NOTREACHED();
+ return nullptr;
+}
+
+} // namespace
+
+Color ConvertColor(const Color& src_color,
+ InterpolationSpace dst_interpolation_space,
+ InterpolationSpace src_interpolation_space) {
+ sk_sp<SkColorFilter> conversion_filter =
+ GetConversionFilter(dst_interpolation_space, src_interpolation_space);
+ return conversion_filter
+ ? Color(conversion_filter->filterColor(src_color.Rgb()))
+ : src_color;
+}
+
+sk_sp<SkColorFilter> CreateInterpolationSpaceFilter(
+ InterpolationSpace src_interpolation_space,
+ InterpolationSpace dst_interpolation_space) {
+ return GetConversionFilter(dst_interpolation_space, src_interpolation_space);
+}
+
+} // namespace InterpolationSpaceUtilities
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/interpolation_space.h b/chromium/third_party/blink/renderer/platform/graphics/interpolation_space.h
new file mode 100644
index 00000000000..93418c44ef3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/interpolation_space.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2009 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_INTERPOLATION_SPACE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_INTERPOLATION_SPACE_H_
+
+#include "third_party/blink/renderer/platform/graphics/color.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+class SkColorFilter;
+
+namespace blink {
+
+enum InterpolationSpace {
+ // Interpolation is performed on (assumed to be sRGB) pixel values directly,
+ // so a halfway interpolation of 0 and 255 is 127.
+ kInterpolationSpaceSRGB,
+ // Interpolation is performed in linear physical value space, so a halfway
+ // interpolation of 0 and 255 is 188.
+ kInterpolationSpaceLinear
+};
+
+namespace InterpolationSpaceUtilities {
+
+// Convert a Color assumed to be in the |src_interpolation_space| into the
+// |dst_interpolation_space|.
+Color ConvertColor(
+ const Color& src_color,
+ InterpolationSpace dst_interpolation_space,
+ InterpolationSpace src_interpolation_space = kInterpolationSpaceSRGB);
+
+// Create a color filter that will convert from |src_interpolation_space| into
+// |dst_interpolation_space|.
+sk_sp<SkColorFilter> CreateInterpolationSpaceFilter(
+ InterpolationSpace src_interpolation_space,
+ InterpolationSpace dst_interpolation_space);
+
+} // namespace InterpolationSpaceUtilities
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_INTERPOLATION_SPACE_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/link_highlight.h b/chromium/third_party/blink/renderer/platform/graphics/link_highlight.h
new file mode 100644
index 00000000000..f419ef4ac53
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/link_highlight.h
@@ -0,0 +1,26 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_LINK_HIGHLIGHT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_LINK_HIGHLIGHT_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+
+class WebLayer;
+
+class PLATFORM_EXPORT LinkHighlight {
+ public:
+ virtual void Invalidate() = 0;
+ virtual void ClearCurrentGraphicsLayer() = 0;
+ virtual WebLayer* Layer() = 0;
+
+ protected:
+ virtual ~LinkHighlight() = default;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_LINK_HIGHLIGHT_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/logging_canvas.cc b/chromium/third_party/blink/renderer/platform/graphics/logging_canvas.cc
new file mode 100644
index 00000000000..89b92fa9686
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/logging_canvas.cc
@@ -0,0 +1,948 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/renderer/platform/graphics/logging_canvas.h"
+
+#include <unicode/unistr.h>
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/geometry/int_size.h"
+#include "third_party/blink/renderer/platform/graphics/skia/image_pixel_locker.h"
+#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
+#include "third_party/blink/renderer/platform/image-encoders/image_encoder.h"
+#include "third_party/blink/renderer/platform/wtf/byte_swap.h"
+#include "third_party/blink/renderer/platform/wtf/hex_number.h"
+#include "third_party/blink/renderer/platform/wtf/text/base64.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
+#include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/core/SkImageInfo.h"
+#include "third_party/skia/include/core/SkPaint.h"
+#include "third_party/skia/include/core/SkPath.h"
+#include "third_party/skia/include/core/SkRRect.h"
+#include "third_party/skia/include/core/SkRect.h"
+
+namespace blink {
+
+namespace {
+
+struct VerbParams {
+ STACK_ALLOCATED();
+ String name;
+ unsigned point_count;
+ unsigned point_offset;
+
+ VerbParams(const String& name, unsigned point_count, unsigned point_offset)
+ : name(name), point_count(point_count), point_offset(point_offset) {}
+};
+
+std::unique_ptr<JSONObject> ObjectForSkRect(const SkRect& rect) {
+ std::unique_ptr<JSONObject> rect_item = JSONObject::Create();
+ rect_item->SetDouble("left", rect.left());
+ rect_item->SetDouble("top", rect.top());
+ rect_item->SetDouble("right", rect.right());
+ rect_item->SetDouble("bottom", rect.bottom());
+ return rect_item;
+}
+
+std::unique_ptr<JSONObject> ObjectForSkIRect(const SkIRect& rect) {
+ std::unique_ptr<JSONObject> rect_item = JSONObject::Create();
+ rect_item->SetDouble("left", rect.left());
+ rect_item->SetDouble("top", rect.top());
+ rect_item->SetDouble("right", rect.right());
+ rect_item->SetDouble("bottom", rect.bottom());
+ return rect_item;
+}
+
+String PointModeName(SkCanvas::PointMode mode) {
+ switch (mode) {
+ case SkCanvas::kPoints_PointMode:
+ return "Points";
+ case SkCanvas::kLines_PointMode:
+ return "Lines";
+ case SkCanvas::kPolygon_PointMode:
+ return "Polygon";
+ default:
+ NOTREACHED();
+ return "?";
+ };
+}
+
+std::unique_ptr<JSONObject> ObjectForSkPoint(const SkPoint& point) {
+ std::unique_ptr<JSONObject> point_item = JSONObject::Create();
+ point_item->SetDouble("x", point.x());
+ point_item->SetDouble("y", point.y());
+ return point_item;
+}
+
+std::unique_ptr<JSONArray> ArrayForSkPoints(size_t count,
+ const SkPoint points[]) {
+ std::unique_ptr<JSONArray> points_array_item = JSONArray::Create();
+ for (size_t i = 0; i < count; ++i)
+ points_array_item->PushObject(ObjectForSkPoint(points[i]));
+ return points_array_item;
+}
+
+std::unique_ptr<JSONObject> ObjectForRadius(const SkRRect& rrect,
+ SkRRect::Corner corner) {
+ std::unique_ptr<JSONObject> radius_item = JSONObject::Create();
+ SkVector radius = rrect.radii(corner);
+ radius_item->SetDouble("xRadius", radius.x());
+ radius_item->SetDouble("yRadius", radius.y());
+ return radius_item;
+}
+
+String RrectTypeName(SkRRect::Type type) {
+ switch (type) {
+ case SkRRect::kEmpty_Type:
+ return "Empty";
+ case SkRRect::kRect_Type:
+ return "Rect";
+ case SkRRect::kOval_Type:
+ return "Oval";
+ case SkRRect::kSimple_Type:
+ return "Simple";
+ case SkRRect::kNinePatch_Type:
+ return "Nine-patch";
+ case SkRRect::kComplex_Type:
+ return "Complex";
+ default:
+ NOTREACHED();
+ return "?";
+ };
+}
+
+String RadiusName(SkRRect::Corner corner) {
+ switch (corner) {
+ case SkRRect::kUpperLeft_Corner:
+ return "upperLeftRadius";
+ case SkRRect::kUpperRight_Corner:
+ return "upperRightRadius";
+ case SkRRect::kLowerRight_Corner:
+ return "lowerRightRadius";
+ case SkRRect::kLowerLeft_Corner:
+ return "lowerLeftRadius";
+ default:
+ NOTREACHED();
+ return "?";
+ }
+}
+
+std::unique_ptr<JSONObject> ObjectForSkRRect(const SkRRect& rrect) {
+ std::unique_ptr<JSONObject> rrect_item = JSONObject::Create();
+ rrect_item->SetString("type", RrectTypeName(rrect.type()));
+ rrect_item->SetDouble("left", rrect.rect().left());
+ rrect_item->SetDouble("top", rrect.rect().top());
+ rrect_item->SetDouble("right", rrect.rect().right());
+ rrect_item->SetDouble("bottom", rrect.rect().bottom());
+ for (int i = 0; i < 4; ++i)
+ rrect_item->SetObject(RadiusName((SkRRect::Corner)i),
+ ObjectForRadius(rrect, (SkRRect::Corner)i));
+ return rrect_item;
+}
+
+String FillTypeName(SkPath::FillType type) {
+ switch (type) {
+ case SkPath::kWinding_FillType:
+ return "Winding";
+ case SkPath::kEvenOdd_FillType:
+ return "EvenOdd";
+ case SkPath::kInverseWinding_FillType:
+ return "InverseWinding";
+ case SkPath::kInverseEvenOdd_FillType:
+ return "InverseEvenOdd";
+ default:
+ NOTREACHED();
+ return "?";
+ };
+}
+
+String ConvexityName(SkPath::Convexity convexity) {
+ switch (convexity) {
+ case SkPath::kUnknown_Convexity:
+ return "Unknown";
+ case SkPath::kConvex_Convexity:
+ return "Convex";
+ case SkPath::kConcave_Convexity:
+ return "Concave";
+ default:
+ NOTREACHED();
+ return "?";
+ };
+}
+
+VerbParams SegmentParams(SkPath::Verb verb) {
+ switch (verb) {
+ case SkPath::kMove_Verb:
+ return VerbParams("Move", 1, 0);
+ case SkPath::kLine_Verb:
+ return VerbParams("Line", 1, 1);
+ case SkPath::kQuad_Verb:
+ return VerbParams("Quad", 2, 1);
+ case SkPath::kConic_Verb:
+ return VerbParams("Conic", 2, 1);
+ case SkPath::kCubic_Verb:
+ return VerbParams("Cubic", 3, 1);
+ case SkPath::kClose_Verb:
+ return VerbParams("Close", 0, 0);
+ case SkPath::kDone_Verb:
+ return VerbParams("Done", 0, 0);
+ default:
+ NOTREACHED();
+ return VerbParams("?", 0, 0);
+ };
+}
+
+std::unique_ptr<JSONObject> ObjectForSkPath(const SkPath& path) {
+ std::unique_ptr<JSONObject> path_item = JSONObject::Create();
+ path_item->SetString("fillType", FillTypeName(path.getFillType()));
+ path_item->SetString("convexity", ConvexityName(path.getConvexity()));
+ path_item->SetBoolean("isRect", path.isRect(nullptr));
+ SkPath::Iter iter(path, false);
+ SkPoint points[4];
+ std::unique_ptr<JSONArray> path_points_array = JSONArray::Create();
+ for (SkPath::Verb verb = iter.next(points, false); verb != SkPath::kDone_Verb;
+ verb = iter.next(points, false)) {
+ VerbParams verb_params = SegmentParams(verb);
+ std::unique_ptr<JSONObject> path_point_item = JSONObject::Create();
+ path_point_item->SetString("verb", verb_params.name);
+ DCHECK_LE(verb_params.point_count + verb_params.point_offset,
+ WTF_ARRAY_LENGTH(points));
+ path_point_item->SetArray(
+ "points", ArrayForSkPoints(verb_params.point_count,
+ points + verb_params.point_offset));
+ if (SkPath::kConic_Verb == verb)
+ path_point_item->SetDouble("conicWeight", iter.conicWeight());
+ path_points_array->PushObject(std::move(path_point_item));
+ }
+ path_item->SetArray("pathPoints", std::move(path_points_array));
+ path_item->SetObject("bounds", ObjectForSkRect(path.getBounds()));
+ return path_item;
+}
+
+String ColorTypeName(SkColorType color_type) {
+ switch (color_type) {
+ case kUnknown_SkColorType:
+ return "None";
+ case kAlpha_8_SkColorType:
+ return "A8";
+ case kRGB_565_SkColorType:
+ return "RGB565";
+ case kARGB_4444_SkColorType:
+ return "ARGB4444";
+ case kN32_SkColorType:
+ return "ARGB8888";
+ default:
+ NOTREACHED();
+ return "?";
+ };
+}
+
+std::unique_ptr<JSONObject> ObjectForBitmapData(const SkBitmap& bitmap) {
+ Vector<unsigned char> output;
+
+ SkPixmap src;
+ bool peekResult = bitmap.peekPixels(&src);
+ DCHECK(peekResult);
+
+ SkPngEncoder::Options options;
+ options.fFilterFlags = SkPngEncoder::FilterFlag::kSub;
+ options.fZLibLevel = 3;
+ options.fUnpremulBehavior = SkTransferFunctionBehavior::kIgnore;
+ if (!ImageEncoder::Encode(&output, src, options)) {
+ return nullptr;
+ }
+
+ std::unique_ptr<JSONObject> data_item = JSONObject::Create();
+ data_item->SetString(
+ "base64",
+ WTF::Base64Encode(reinterpret_cast<char*>(output.data()), output.size()));
+ data_item->SetString("mimeType", "image/png");
+ return data_item;
+}
+
+std::unique_ptr<JSONObject> ObjectForSkBitmap(const SkBitmap& bitmap) {
+ std::unique_ptr<JSONObject> bitmap_item = JSONObject::Create();
+ bitmap_item->SetInteger("width", bitmap.width());
+ bitmap_item->SetInteger("height", bitmap.height());
+ bitmap_item->SetString("config", ColorTypeName(bitmap.colorType()));
+ bitmap_item->SetBoolean("opaque", bitmap.isOpaque());
+ bitmap_item->SetBoolean("immutable", bitmap.isImmutable());
+ bitmap_item->SetBoolean("volatile", bitmap.isVolatile());
+ bitmap_item->SetInteger("genID", bitmap.getGenerationID());
+ bitmap_item->SetObject("data", ObjectForBitmapData(bitmap));
+ return bitmap_item;
+}
+
+std::unique_ptr<JSONObject> ObjectForSkImage(const SkImage* image) {
+ std::unique_ptr<JSONObject> image_item = JSONObject::Create();
+ image_item->SetInteger("width", image->width());
+ image_item->SetInteger("height", image->height());
+ image_item->SetBoolean("opaque", image->isOpaque());
+ image_item->SetInteger("uniqueID", image->uniqueID());
+ return image_item;
+}
+
+std::unique_ptr<JSONArray> ArrayForSkMatrix(const SkMatrix& matrix) {
+ std::unique_ptr<JSONArray> matrix_array = JSONArray::Create();
+ for (int i = 0; i < 9; ++i)
+ matrix_array->PushDouble(matrix[i]);
+ return matrix_array;
+}
+
+std::unique_ptr<JSONObject> ObjectForSkShader(const SkShader& shader) {
+ std::unique_ptr<JSONObject> shader_item = JSONObject::Create();
+ const SkMatrix local_matrix = shader.getLocalMatrix();
+ if (!local_matrix.isIdentity())
+ shader_item->SetArray("localMatrix", ArrayForSkMatrix(local_matrix));
+ return shader_item;
+}
+
+String StringForSkColor(const SkColor& color) {
+ // #AARRGGBB.
+ Vector<LChar, 9> result;
+ result.push_back('#');
+ HexNumber::AppendUnsignedAsHex(color, result);
+ return String(result.data(), result.size());
+}
+
+void AppendFlagToString(String* flags_string, bool is_set, const String& name) {
+ if (!is_set)
+ return;
+ if (flags_string->length())
+ flags_string->append("|");
+ flags_string->append(name);
+}
+
+String StringForSkPaintFlags(const SkPaint& paint) {
+ if (!paint.getFlags())
+ return "none";
+ String flags_string = "";
+ AppendFlagToString(&flags_string, paint.isAntiAlias(), "AntiAlias");
+ AppendFlagToString(&flags_string, paint.isDither(), "Dither");
+ AppendFlagToString(&flags_string, paint.isFakeBoldText(), "FakeBoldText");
+ AppendFlagToString(&flags_string, paint.isLinearText(), "LinearText");
+ AppendFlagToString(&flags_string, paint.isSubpixelText(), "SubpixelText");
+ AppendFlagToString(&flags_string, paint.isDevKernText(), "DevKernText");
+ AppendFlagToString(&flags_string, paint.isLCDRenderText(), "LCDRenderText");
+ AppendFlagToString(&flags_string, paint.isEmbeddedBitmapText(),
+ "EmbeddedBitmapText");
+ AppendFlagToString(&flags_string, paint.isAutohinted(), "Autohinted");
+ AppendFlagToString(&flags_string, paint.isVerticalText(), "VerticalText");
+ return flags_string;
+}
+
+String FilterQualityName(SkFilterQuality filter_quality) {
+ switch (filter_quality) {
+ case kNone_SkFilterQuality:
+ return "None";
+ case kLow_SkFilterQuality:
+ return "Low";
+ case kMedium_SkFilterQuality:
+ return "Medium";
+ case kHigh_SkFilterQuality:
+ return "High";
+ default:
+ NOTREACHED();
+ return "?";
+ };
+}
+
+String TextAlignName(SkPaint::Align align) {
+ switch (align) {
+ case SkPaint::kLeft_Align:
+ return "Left";
+ case SkPaint::kCenter_Align:
+ return "Center";
+ case SkPaint::kRight_Align:
+ return "Right";
+ default:
+ NOTREACHED();
+ return "?";
+ };
+}
+
+String StrokeCapName(SkPaint::Cap cap) {
+ switch (cap) {
+ case SkPaint::kButt_Cap:
+ return "Butt";
+ case SkPaint::kRound_Cap:
+ return "Round";
+ case SkPaint::kSquare_Cap:
+ return "Square";
+ default:
+ NOTREACHED();
+ return "?";
+ };
+}
+
+String StrokeJoinName(SkPaint::Join join) {
+ switch (join) {
+ case SkPaint::kMiter_Join:
+ return "Miter";
+ case SkPaint::kRound_Join:
+ return "Round";
+ case SkPaint::kBevel_Join:
+ return "Bevel";
+ default:
+ NOTREACHED();
+ return "?";
+ };
+}
+
+String StyleName(SkPaint::Style style) {
+ switch (style) {
+ case SkPaint::kFill_Style:
+ return "Fill";
+ case SkPaint::kStroke_Style:
+ return "Stroke";
+ case SkPaint::kStrokeAndFill_Style:
+ return "StrokeAndFill";
+ default:
+ NOTREACHED();
+ return "?";
+ };
+}
+
+String TextEncodingName(SkPaint::TextEncoding encoding) {
+ switch (encoding) {
+ case SkPaint::kUTF8_TextEncoding:
+ return "UTF-8";
+ case SkPaint::kUTF16_TextEncoding:
+ return "UTF-16";
+ case SkPaint::kUTF32_TextEncoding:
+ return "UTF-32";
+ case SkPaint::kGlyphID_TextEncoding:
+ return "GlyphID";
+ default:
+ NOTREACHED();
+ return "?";
+ };
+}
+
+String HintingName(SkPaint::Hinting hinting) {
+ switch (hinting) {
+ case SkPaint::kNo_Hinting:
+ return "None";
+ case SkPaint::kSlight_Hinting:
+ return "Slight";
+ case SkPaint::kNormal_Hinting:
+ return "Normal";
+ case SkPaint::kFull_Hinting:
+ return "Full";
+ default:
+ NOTREACHED();
+ return "?";
+ };
+}
+
+std::unique_ptr<JSONObject> ObjectForSkPaint(const SkPaint& paint) {
+ std::unique_ptr<JSONObject> paint_item = JSONObject::Create();
+ paint_item->SetDouble("textSize", paint.getTextSize());
+ paint_item->SetDouble("textScaleX", paint.getTextScaleX());
+ paint_item->SetDouble("textSkewX", paint.getTextSkewX());
+ if (SkShader* shader = paint.getShader())
+ paint_item->SetObject("shader", ObjectForSkShader(*shader));
+ paint_item->SetString("color", StringForSkColor(paint.getColor()));
+ paint_item->SetDouble("strokeWidth", paint.getStrokeWidth());
+ paint_item->SetDouble("strokeMiter", paint.getStrokeMiter());
+ paint_item->SetString("flags", StringForSkPaintFlags(paint));
+ paint_item->SetString("filterLevel",
+ FilterQualityName(paint.getFilterQuality()));
+ paint_item->SetString("textAlign", TextAlignName(paint.getTextAlign()));
+ paint_item->SetString("strokeCap", StrokeCapName(paint.getStrokeCap()));
+ paint_item->SetString("strokeJoin", StrokeJoinName(paint.getStrokeJoin()));
+ paint_item->SetString("styleName", StyleName(paint.getStyle()));
+ paint_item->SetString("textEncoding",
+ TextEncodingName(paint.getTextEncoding()));
+ paint_item->SetString("hinting", HintingName(paint.getHinting()));
+ if (paint.getBlendMode() != SkBlendMode::kSrcOver)
+ paint_item->SetString("blendMode", SkBlendMode_Name(paint.getBlendMode()));
+ if (const auto* filter = paint.getImageFilter()) {
+ SkString str;
+ filter->toString(&str);
+ paint_item->SetString("imageFilter", str.c_str());
+ }
+ return paint_item;
+}
+
+std::unique_ptr<JSONArray> ArrayForSkScalars(size_t n,
+ const SkScalar scalars[]) {
+ std::unique_ptr<JSONArray> scalars_array = JSONArray::Create();
+ for (size_t i = 0; i < n; ++i)
+ scalars_array->PushDouble(scalars[i]);
+ return scalars_array;
+}
+
+String ClipOpName(SkClipOp op) {
+ switch (op) {
+ case SkClipOp::kDifference:
+ return "kDifference_Op";
+ case SkClipOp::kIntersect:
+ return "kIntersect_Op";
+ default:
+ return "Unknown type";
+ };
+}
+
+String SaveLayerFlagsToString(SkCanvas::SaveLayerFlags flags) {
+ String flags_string = "";
+ if (flags & SkCanvas::kPreserveLCDText_SaveLayerFlag)
+ flags_string.append("kPreserveLCDText_SaveLayerFlag ");
+ return flags_string;
+}
+
+String StringForUTF32LEText(const void* text, size_t byte_length) {
+ icu::UnicodeString utf16;
+#if defined(ARCH_CPU_BIG_ENDIAN)
+ // Swap LE to BE
+ size_t char_length = length / sizeof(UChar32);
+ WTF::Vector<UChar32> utf32be(char_length);
+ for (size_t i = 0; i < char_length; ++i)
+ utf32be[i] = WTF::Bswap32(static_cast<UChar32*>(text)[i]);
+ utf16 = icu::UnicodeString::fromUTF32(utf32be.data(),
+ static_cast<int32_t>(byte_length));
+#else
+ utf16 = icu::UnicodeString::fromUTF32(reinterpret_cast<const UChar32*>(text),
+ static_cast<int32_t>(byte_length));
+#endif
+ return String(icu::toUCharPtr(utf16.getBuffer()),
+ static_cast<unsigned>(utf16.length()));
+}
+
+String StringForText(const void* text,
+ size_t byte_length,
+ const SkPaint& paint) {
+ SkPaint::TextEncoding encoding = paint.getTextEncoding();
+ switch (encoding) {
+ case SkPaint::kUTF8_TextEncoding:
+ return WTF::TextEncoding("UTF-8").Decode(
+ reinterpret_cast<const char*>(text), byte_length);
+ case SkPaint::kUTF16_TextEncoding:
+ return WTF::TextEncoding("UTF-16LE")
+ .Decode(reinterpret_cast<const char*>(text), byte_length);
+ case SkPaint::kUTF32_TextEncoding:
+ return StringForUTF32LEText(text, byte_length);
+ case SkPaint::kGlyphID_TextEncoding: {
+ WTF::Vector<SkUnichar> data_vector(byte_length / 2);
+ SkUnichar* text_data = data_vector.data();
+ paint.glyphsToUnichars(static_cast<const uint16_t*>(text),
+ byte_length / 2, text_data);
+ return StringForUTF32LEText(text, byte_length);
+ }
+ default:
+ NOTREACHED();
+ return "?";
+ }
+}
+
+} // namespace
+
+class AutoLogger
+ : InterceptingCanvasBase::CanvasInterceptorBase<LoggingCanvas> {
+ public:
+ explicit AutoLogger(LoggingCanvas* canvas)
+ : InterceptingCanvasBase::CanvasInterceptorBase<LoggingCanvas>(canvas) {}
+
+ JSONObject* LogItem(const String& name);
+ JSONObject* LogItemWithParams(const String& name);
+ ~AutoLogger() {
+ if (TopLevelCall())
+ Canvas()->log_->PushObject(std::move(log_item_));
+ }
+
+ private:
+ std::unique_ptr<JSONObject> log_item_;
+};
+
+JSONObject* AutoLogger::LogItem(const String& name) {
+ std::unique_ptr<JSONObject> item = JSONObject::Create();
+ item->SetString("method", name);
+ log_item_ = std::move(item);
+ return log_item_.get();
+}
+
+JSONObject* AutoLogger::LogItemWithParams(const String& name) {
+ JSONObject* item = LogItem(name);
+ std::unique_ptr<JSONObject> params = JSONObject::Create();
+ item->SetObject("params", std::move(params));
+ return item->GetJSONObject("params");
+}
+
+LoggingCanvas::LoggingCanvas()
+ : InterceptingCanvasBase(0, 0), log_(JSONArray::Create()) {}
+
+void LoggingCanvas::onDrawPaint(const SkPaint& paint) {
+ AutoLogger logger(this);
+ logger.LogItemWithParams("drawPaint")
+ ->SetObject("paint", ObjectForSkPaint(paint));
+ this->SkCanvas::onDrawPaint(paint);
+}
+
+void LoggingCanvas::onDrawPoints(PointMode mode,
+ size_t count,
+ const SkPoint pts[],
+ const SkPaint& paint) {
+ AutoLogger logger(this);
+ JSONObject* params = logger.LogItemWithParams("drawPoints");
+ params->SetString("pointMode", PointModeName(mode));
+ params->SetArray("points", ArrayForSkPoints(count, pts));
+ params->SetObject("paint", ObjectForSkPaint(paint));
+ this->SkCanvas::onDrawPoints(mode, count, pts, paint);
+}
+
+void LoggingCanvas::onDrawRect(const SkRect& rect, const SkPaint& paint) {
+ AutoLogger logger(this);
+ JSONObject* params = logger.LogItemWithParams("drawRect");
+ params->SetObject("rect", ObjectForSkRect(rect));
+ params->SetObject("paint", ObjectForSkPaint(paint));
+ this->SkCanvas::onDrawRect(rect, paint);
+}
+
+void LoggingCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
+ AutoLogger logger(this);
+ JSONObject* params = logger.LogItemWithParams("drawOval");
+ params->SetObject("oval", ObjectForSkRect(oval));
+ params->SetObject("paint", ObjectForSkPaint(paint));
+ this->SkCanvas::onDrawOval(oval, paint);
+}
+
+void LoggingCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
+ AutoLogger logger(this);
+ JSONObject* params = logger.LogItemWithParams("drawRRect");
+ params->SetObject("rrect", ObjectForSkRRect(rrect));
+ params->SetObject("paint", ObjectForSkPaint(paint));
+ this->SkCanvas::onDrawRRect(rrect, paint);
+}
+
+void LoggingCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
+ AutoLogger logger(this);
+ JSONObject* params = logger.LogItemWithParams("drawPath");
+ params->SetObject("path", ObjectForSkPath(path));
+ params->SetObject("paint", ObjectForSkPaint(paint));
+ this->SkCanvas::onDrawPath(path, paint);
+}
+
+void LoggingCanvas::onDrawBitmap(const SkBitmap& bitmap,
+ SkScalar left,
+ SkScalar top,
+ const SkPaint* paint) {
+ AutoLogger logger(this);
+ JSONObject* params = logger.LogItemWithParams("drawBitmap");
+ params->SetDouble("left", left);
+ params->SetDouble("top", top);
+ params->SetObject("bitmap", ObjectForSkBitmap(bitmap));
+ if (paint)
+ params->SetObject("paint", ObjectForSkPaint(*paint));
+ this->SkCanvas::onDrawBitmap(bitmap, left, top, paint);
+}
+
+void LoggingCanvas::onDrawBitmapRect(const SkBitmap& bitmap,
+ const SkRect* src,
+ const SkRect& dst,
+ const SkPaint* paint,
+ SrcRectConstraint constraint) {
+ AutoLogger logger(this);
+ JSONObject* params = logger.LogItemWithParams("drawBitmapRectToRect");
+ params->SetObject("bitmap", ObjectForSkBitmap(bitmap));
+ if (src)
+ params->SetObject("src", ObjectForSkRect(*src));
+ params->SetObject("dst", ObjectForSkRect(dst));
+ if (paint)
+ params->SetObject("paint", ObjectForSkPaint(*paint));
+ params->SetInteger("flags", constraint);
+ this->SkCanvas::onDrawBitmapRect(bitmap, src, dst, paint, constraint);
+}
+
+void LoggingCanvas::onDrawBitmapNine(const SkBitmap& bitmap,
+ const SkIRect& center,
+ const SkRect& dst,
+ const SkPaint* paint) {
+ AutoLogger logger(this);
+ JSONObject* params = logger.LogItemWithParams("drawBitmapNine");
+ params->SetObject("bitmap", ObjectForSkBitmap(bitmap));
+ params->SetObject("center", ObjectForSkIRect(center));
+ params->SetObject("dst", ObjectForSkRect(dst));
+ if (paint)
+ params->SetObject("paint", ObjectForSkPaint(*paint));
+ this->SkCanvas::onDrawBitmapNine(bitmap, center, dst, paint);
+}
+
+void LoggingCanvas::onDrawImage(const SkImage* image,
+ SkScalar left,
+ SkScalar top,
+ const SkPaint* paint) {
+ AutoLogger logger(this);
+ JSONObject* params = logger.LogItemWithParams("drawImage");
+ params->SetDouble("left", left);
+ params->SetDouble("top", top);
+ params->SetObject("image", ObjectForSkImage(image));
+ if (paint)
+ params->SetObject("paint", ObjectForSkPaint(*paint));
+ this->SkCanvas::onDrawImage(image, left, top, paint);
+}
+
+void LoggingCanvas::onDrawImageRect(const SkImage* image,
+ const SkRect* src,
+ const SkRect& dst,
+ const SkPaint* paint,
+ SrcRectConstraint constraint) {
+ AutoLogger logger(this);
+ JSONObject* params = logger.LogItemWithParams("drawImageRect");
+ params->SetObject("image", ObjectForSkImage(image));
+ if (src)
+ params->SetObject("src", ObjectForSkRect(*src));
+ params->SetObject("dst", ObjectForSkRect(dst));
+ if (paint)
+ params->SetObject("paint", ObjectForSkPaint(*paint));
+ this->SkCanvas::onDrawImageRect(image, src, dst, paint, constraint);
+}
+
+void LoggingCanvas::onDrawVerticesObject(const SkVertices* vertices,
+ SkBlendMode bmode,
+ const SkPaint& paint) {
+ AutoLogger logger(this);
+ JSONObject* params = logger.LogItemWithParams("drawVertices");
+ params->SetObject("paint", ObjectForSkPaint(paint));
+ this->SkCanvas::onDrawVerticesObject(vertices, bmode, paint);
+}
+
+void LoggingCanvas::onDrawDRRect(const SkRRect& outer,
+ const SkRRect& inner,
+ const SkPaint& paint) {
+ AutoLogger logger(this);
+ JSONObject* params = logger.LogItemWithParams("drawDRRect");
+ params->SetObject("outer", ObjectForSkRRect(outer));
+ params->SetObject("inner", ObjectForSkRRect(inner));
+ params->SetObject("paint", ObjectForSkPaint(paint));
+ this->SkCanvas::onDrawDRRect(outer, inner, paint);
+}
+
+void LoggingCanvas::onDrawText(const void* text,
+ size_t byte_length,
+ SkScalar x,
+ SkScalar y,
+ const SkPaint& paint) {
+ AutoLogger logger(this);
+ JSONObject* params = logger.LogItemWithParams("drawText");
+ params->SetString("text", StringForText(text, byte_length, paint));
+ params->SetDouble("x", x);
+ params->SetDouble("y", y);
+ params->SetObject("paint", ObjectForSkPaint(paint));
+ this->SkCanvas::onDrawText(text, byte_length, x, y, paint);
+}
+
+void LoggingCanvas::onDrawPosText(const void* text,
+ size_t byte_length,
+ const SkPoint pos[],
+ const SkPaint& paint) {
+ AutoLogger logger(this);
+ JSONObject* params = logger.LogItemWithParams("drawPosText");
+ params->SetString("text", StringForText(text, byte_length, paint));
+ size_t points_count = paint.countText(text, byte_length);
+ params->SetArray("pos", ArrayForSkPoints(points_count, pos));
+ params->SetObject("paint", ObjectForSkPaint(paint));
+ this->SkCanvas::onDrawPosText(text, byte_length, pos, paint);
+}
+
+void LoggingCanvas::onDrawPosTextH(const void* text,
+ size_t byte_length,
+ const SkScalar xpos[],
+ SkScalar const_y,
+ const SkPaint& paint) {
+ AutoLogger logger(this);
+ JSONObject* params = logger.LogItemWithParams("drawPosTextH");
+ params->SetString("text", StringForText(text, byte_length, paint));
+ size_t points_count = paint.countText(text, byte_length);
+ params->SetArray("xpos", ArrayForSkScalars(points_count, xpos));
+ params->SetDouble("constY", const_y);
+ params->SetObject("paint", ObjectForSkPaint(paint));
+ this->SkCanvas::onDrawPosTextH(text, byte_length, xpos, const_y, paint);
+}
+
+void LoggingCanvas::onDrawTextOnPath(const void* text,
+ size_t byte_length,
+ const SkPath& path,
+ const SkMatrix* matrix,
+ const SkPaint& paint) {
+ AutoLogger logger(this);
+ JSONObject* params = logger.LogItemWithParams("drawTextOnPath");
+ params->SetString("text", StringForText(text, byte_length, paint));
+ params->SetObject("path", ObjectForSkPath(path));
+ if (matrix)
+ params->SetArray("matrix", ArrayForSkMatrix(*matrix));
+ params->SetObject("paint", ObjectForSkPaint(paint));
+ this->SkCanvas::onDrawTextOnPath(text, byte_length, path, matrix, paint);
+}
+
+void LoggingCanvas::onDrawTextBlob(const SkTextBlob* blob,
+ SkScalar x,
+ SkScalar y,
+ const SkPaint& paint) {
+ AutoLogger logger(this);
+ JSONObject* params = logger.LogItemWithParams("drawTextBlob");
+ params->SetDouble("x", x);
+ params->SetDouble("y", y);
+ params->SetObject("paint", ObjectForSkPaint(paint));
+ this->SkCanvas::onDrawTextBlob(blob, x, y, paint);
+}
+
+void LoggingCanvas::onClipRect(const SkRect& rect,
+ SkClipOp op,
+ ClipEdgeStyle style) {
+ AutoLogger logger(this);
+ JSONObject* params = logger.LogItemWithParams("clipRect");
+ params->SetObject("rect", ObjectForSkRect(rect));
+ params->SetString("SkRegion::Op", ClipOpName(op));
+ params->SetBoolean("softClipEdgeStyle", kSoft_ClipEdgeStyle == style);
+ this->SkCanvas::onClipRect(rect, op, style);
+}
+
+void LoggingCanvas::onClipRRect(const SkRRect& rrect,
+ SkClipOp op,
+ ClipEdgeStyle style) {
+ AutoLogger logger(this);
+ JSONObject* params = logger.LogItemWithParams("clipRRect");
+ params->SetObject("rrect", ObjectForSkRRect(rrect));
+ params->SetString("SkRegion::Op", ClipOpName(op));
+ params->SetBoolean("softClipEdgeStyle", kSoft_ClipEdgeStyle == style);
+ this->SkCanvas::onClipRRect(rrect, op, style);
+}
+
+void LoggingCanvas::onClipPath(const SkPath& path,
+ SkClipOp op,
+ ClipEdgeStyle style) {
+ AutoLogger logger(this);
+ JSONObject* params = logger.LogItemWithParams("clipPath");
+ params->SetObject("path", ObjectForSkPath(path));
+ params->SetString("SkRegion::Op", ClipOpName(op));
+ params->SetBoolean("softClipEdgeStyle", kSoft_ClipEdgeStyle == style);
+ this->SkCanvas::onClipPath(path, op, style);
+}
+
+void LoggingCanvas::onClipRegion(const SkRegion& region, SkClipOp op) {
+ AutoLogger logger(this);
+ JSONObject* params = logger.LogItemWithParams("clipRegion");
+ params->SetString("op", ClipOpName(op));
+ this->SkCanvas::onClipRegion(region, op);
+}
+
+void LoggingCanvas::onDrawPicture(const SkPicture* picture,
+ const SkMatrix* matrix,
+ const SkPaint* paint) {
+ this->UnrollDrawPicture(picture, matrix, paint, nullptr);
+}
+
+void LoggingCanvas::didSetMatrix(const SkMatrix& matrix) {
+ AutoLogger logger(this);
+ JSONObject* params = logger.LogItemWithParams("setMatrix");
+ params->SetArray("matrix", ArrayForSkMatrix(matrix));
+ this->SkCanvas::didSetMatrix(matrix);
+}
+
+void LoggingCanvas::didConcat(const SkMatrix& matrix) {
+ AutoLogger logger(this);
+ JSONObject* params;
+
+ switch (matrix.getType()) {
+ case SkMatrix::kTranslate_Mask:
+ params = logger.LogItemWithParams("translate");
+ params->SetDouble("dx", matrix.getTranslateX());
+ params->SetDouble("dy", matrix.getTranslateY());
+ break;
+
+ case SkMatrix::kScale_Mask:
+ params = logger.LogItemWithParams("scale");
+ params->SetDouble("scaleX", matrix.getScaleX());
+ params->SetDouble("scaleY", matrix.getScaleY());
+ break;
+
+ default:
+ params = logger.LogItemWithParams("concat");
+ params->SetArray("matrix", ArrayForSkMatrix(matrix));
+ }
+ this->SkCanvas::didConcat(matrix);
+}
+
+void LoggingCanvas::willSave() {
+ AutoLogger logger(this);
+ logger.LogItem("save");
+ this->SkCanvas::willSave();
+}
+
+SkCanvas::SaveLayerStrategy LoggingCanvas::getSaveLayerStrategy(
+ const SaveLayerRec& rec) {
+ AutoLogger logger(this);
+ JSONObject* params = logger.LogItemWithParams("saveLayer");
+ if (rec.fBounds)
+ params->SetObject("bounds", ObjectForSkRect(*rec.fBounds));
+ if (rec.fPaint)
+ params->SetObject("paint", ObjectForSkPaint(*rec.fPaint));
+ params->SetString("saveFlags", SaveLayerFlagsToString(rec.fSaveLayerFlags));
+ return this->SkCanvas::getSaveLayerStrategy(rec);
+}
+
+void LoggingCanvas::willRestore() {
+ AutoLogger logger(this);
+ logger.LogItem("restore");
+ this->SkCanvas::willRestore();
+}
+
+std::unique_ptr<JSONArray> LoggingCanvas::Log() {
+ return JSONArray::From(log_->Clone());
+}
+
+std::unique_ptr<JSONArray> RecordAsJSON(const PaintRecord& record) {
+ LoggingCanvas canvas;
+ record.Playback(&canvas);
+ return canvas.Log();
+}
+
+String RecordAsDebugString(const PaintRecord& record) {
+ return RecordAsJSON(record)->ToPrettyJSONString();
+}
+
+void ShowPaintRecord(const PaintRecord& record) {
+ DLOG(INFO) << RecordAsDebugString(record).Utf8().data();
+}
+
+std::unique_ptr<JSONArray> SkPictureAsJSON(const SkPicture& picture) {
+ LoggingCanvas canvas;
+ picture.playback(&canvas);
+ return canvas.Log();
+}
+
+String SkPictureAsDebugString(const SkPicture& picture) {
+ return SkPictureAsJSON(picture)->ToPrettyJSONString();
+}
+
+void ShowSkPicture(const SkPicture& picture) {
+ DLOG(INFO) << SkPictureAsDebugString(picture).Utf8().data();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/logging_canvas.h b/chromium/third_party/blink/renderer/platform/graphics/logging_canvas.h
new file mode 100644
index 00000000000..c27afc5b61b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/logging_canvas.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_LOGGING_CANVAS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_LOGGING_CANVAS_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/graphics/intercepting_canvas.h"
+#include "third_party/blink/renderer/platform/json/json_values.h"
+
+namespace blink {
+
+class LoggingCanvas : public InterceptingCanvasBase {
+ public:
+ LoggingCanvas();
+
+ // Returns a snapshot of the current log data.
+ std::unique_ptr<JSONArray> Log();
+
+ void onDrawPaint(const SkPaint&) override;
+ void onDrawPoints(PointMode,
+ size_t count,
+ const SkPoint pts[],
+ const SkPaint&) override;
+ void onDrawRect(const SkRect&, const SkPaint&) override;
+ void onDrawOval(const SkRect&, const SkPaint&) override;
+ void onDrawRRect(const SkRRect&, const SkPaint&) override;
+ void onDrawPath(const SkPath&, const SkPaint&) override;
+ void onDrawBitmap(const SkBitmap&,
+ SkScalar left,
+ SkScalar top,
+ const SkPaint*) override;
+ void onDrawBitmapRect(const SkBitmap&,
+ const SkRect* src,
+ const SkRect& dst,
+ const SkPaint*,
+ SrcRectConstraint) override;
+ void onDrawBitmapNine(const SkBitmap&,
+ const SkIRect& center,
+ const SkRect& dst,
+ const SkPaint*) override;
+ void onDrawImage(const SkImage*, SkScalar, SkScalar, const SkPaint*) override;
+ void onDrawImageRect(const SkImage*,
+ const SkRect* src,
+ const SkRect& dst,
+ const SkPaint*,
+ SrcRectConstraint) override;
+ void onDrawVerticesObject(const SkVertices*,
+ SkBlendMode bmode,
+ const SkPaint&) override;
+
+ void onDrawDRRect(const SkRRect& outer,
+ const SkRRect& inner,
+ const SkPaint&) override;
+ void onDrawText(const void* text,
+ size_t byte_length,
+ SkScalar x,
+ SkScalar y,
+ const SkPaint&) override;
+ void onDrawPosText(const void* text,
+ size_t byte_length,
+ const SkPoint pos[],
+ const SkPaint&) override;
+ void onDrawPosTextH(const void* text,
+ size_t byte_length,
+ const SkScalar xpos[],
+ SkScalar const_y,
+ const SkPaint&) override;
+ void onDrawTextOnPath(const void* text,
+ size_t byte_length,
+ const SkPath&,
+ const SkMatrix*,
+ const SkPaint&) override;
+ void onDrawTextBlob(const SkTextBlob*,
+ SkScalar x,
+ SkScalar y,
+ const SkPaint&) override;
+ void onClipRect(const SkRect&, SkClipOp, ClipEdgeStyle) override;
+ void onClipRRect(const SkRRect&, SkClipOp, ClipEdgeStyle) override;
+ void onClipPath(const SkPath&, SkClipOp, ClipEdgeStyle) override;
+ void onClipRegion(const SkRegion&, SkClipOp) override;
+ virtual void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*);
+ void didSetMatrix(const SkMatrix&) override;
+ void didConcat(const SkMatrix&) override;
+ void willSave() override;
+ SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec&) override;
+ void willRestore() override;
+
+ private:
+ friend class AutoLogger;
+
+ std::unique_ptr<JSONArray> log_;
+};
+
+PLATFORM_EXPORT std::unique_ptr<JSONArray> RecordAsJSON(const PaintRecord&);
+PLATFORM_EXPORT String RecordAsDebugString(const PaintRecord&);
+PLATFORM_EXPORT void ShowPaintRecord(const PaintRecord&);
+PLATFORM_EXPORT std::unique_ptr<JSONArray> SkPictureAsJSON(const SkPicture&);
+PLATFORM_EXPORT String SkPictureAsDebugString(const SkPicture&);
+PLATFORM_EXPORT void ShowSkPicture(const SkPicture&);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_LOGGING_CANVAS_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/mailbox_texture_holder.cc b/chromium/third_party/blink/renderer/platform/graphics/mailbox_texture_holder.cc
new file mode 100644
index 00000000000..886205c9ce5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/mailbox_texture_holder.cc
@@ -0,0 +1,163 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/mailbox_texture_holder.h"
+
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "skia/ext/texture_handle.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
+#include "third_party/blink/renderer/platform/graphics/skia_texture_holder.h"
+#include "third_party/blink/renderer/platform/web_task_runner.h"
+#include "third_party/skia/include/gpu/GrContext.h"
+
+namespace blink {
+
+namespace {
+
+void ReleaseTexture(
+ bool is_converted_from_skia_texture,
+ unsigned texture_id,
+ std::unique_ptr<gpu::Mailbox> mailbox,
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider,
+ std::unique_ptr<gpu::SyncToken> sync_token) {
+ if (!is_converted_from_skia_texture && texture_id && context_provider) {
+ context_provider->ContextProvider()->ContextGL()->WaitSyncTokenCHROMIUM(
+ sync_token->GetData());
+ context_provider->ContextProvider()->ContextGL()->DeleteTextures(
+ 1, &texture_id);
+ }
+}
+
+} // namespace
+
+MailboxTextureHolder::MailboxTextureHolder(
+ const gpu::Mailbox& mailbox,
+ const gpu::SyncToken& sync_token,
+ unsigned texture_id_to_delete_after_mailbox_consumed,
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&
+ context_provider_wrapper,
+ IntSize mailbox_size)
+ : TextureHolder(std::move(context_provider_wrapper)),
+ mailbox_(mailbox),
+ sync_token_(sync_token),
+ texture_id_(texture_id_to_delete_after_mailbox_consumed),
+ size_(mailbox_size),
+ is_converted_from_skia_texture_(false),
+ thread_id_(0) {
+ InitCommon();
+}
+
+MailboxTextureHolder::MailboxTextureHolder(
+ std::unique_ptr<TextureHolder> texture_holder,
+ GLenum filter)
+ : TextureHolder(texture_holder->ContextProviderWrapper()),
+ texture_id_(0),
+ is_converted_from_skia_texture_(true),
+ thread_id_(0) {
+ DCHECK(texture_holder->IsSkiaTextureHolder());
+ sk_sp<SkImage> image = texture_holder->GetSkImage();
+ DCHECK(image);
+ size_ = IntSize(image->width(), image->height());
+
+ if (!ContextProviderWrapper())
+ return;
+
+ ContextProviderWrapper()->Utils()->GetMailboxForSkImage(mailbox_, image,
+ filter);
+
+ InitCommon();
+}
+
+void MailboxTextureHolder::Sync(MailboxSyncMode mode) {
+ if (IsCrossThread()) {
+ // Was originally created on another thread. Should already have a sync
+ // token from the original source context, already verified if needed.
+ DCHECK(sync_token_.HasData());
+ DCHECK(mode != kVerifiedSyncToken || sync_token_.verified_flush());
+ return;
+ }
+
+ if (!ContextProviderWrapper() || IsAbandoned())
+ return;
+
+ gpu::gles2::GLES2Interface* gl =
+ ContextProviderWrapper()->ContextProvider()->ContextGL();
+
+ if (mode == kOrderingBarrier) {
+ if (!did_issue_ordering_barrier_) {
+ gl->OrderingBarrierCHROMIUM();
+ did_issue_ordering_barrier_ = true;
+ }
+ return;
+ }
+
+ if (!sync_token_.HasData()) {
+ if (mode == kVerifiedSyncToken) {
+ gl->GenSyncTokenCHROMIUM(sync_token_.GetData());
+ } else {
+ gl->GenUnverifiedSyncTokenCHROMIUM(sync_token_.GetData());
+ }
+ return;
+ }
+
+ // At this point we have a pre-existing sync token. We just need to verify
+ // it if needed. Providing a verified sync token when unverified is requested
+ // is fine.
+ if (mode == kVerifiedSyncToken && !sync_token_.verified_flush()) {
+ int8_t* token_data = sync_token_.GetData();
+ // TODO(junov): Batch this verification in the case where there are multiple
+ // offscreen canvases being committed.
+ gl->ShallowFlushCHROMIUM();
+ gl->VerifySyncTokensCHROMIUM(&token_data, 1);
+ sync_token_.SetVerifyFlush();
+ }
+}
+
+void MailboxTextureHolder::InitCommon() {
+ WebThread* thread = Platform::Current()->CurrentThread();
+ thread_id_ = thread->ThreadId();
+ texture_thread_task_runner_ = thread->GetTaskRunner();
+}
+
+bool MailboxTextureHolder::IsValid() const {
+ if (IsCrossThread()) {
+ // If context is is from another thread, validity cannot be verified.
+ // Just assume valid. Potential problem will be detected later.
+ return true;
+ }
+ return !IsAbandoned() && !!ContextProviderWrapper();
+}
+
+bool MailboxTextureHolder::IsCrossThread() const {
+ return thread_id_ != Platform::Current()->CurrentThread()->ThreadId();
+}
+
+MailboxTextureHolder::~MailboxTextureHolder() {
+ std::unique_ptr<gpu::SyncToken> passed_sync_token(
+ new gpu::SyncToken(sync_token_));
+ std::unique_ptr<gpu::Mailbox> passed_mailbox(new gpu::Mailbox(mailbox_));
+
+ if (!IsAbandoned()) {
+ if (texture_thread_task_runner_ &&
+ thread_id_ != Platform::Current()->CurrentThread()->ThreadId()) {
+ PostCrossThreadTask(
+ *texture_thread_task_runner_, FROM_HERE,
+ CrossThreadBind(&ReleaseTexture, is_converted_from_skia_texture_,
+ texture_id_, WTF::Passed(std::move(passed_mailbox)),
+ WTF::Passed(ContextProviderWrapper()),
+ WTF::Passed(std::move(passed_sync_token))));
+ } else {
+ ReleaseTexture(is_converted_from_skia_texture_, texture_id_,
+ std::move(passed_mailbox), ContextProviderWrapper(),
+ std::move(passed_sync_token));
+ }
+ }
+
+ texture_id_ = 0u; // invalidate the texture.
+ texture_thread_task_runner_ = nullptr;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/mailbox_texture_holder.h b/chromium/third_party/blink/renderer/platform/graphics/mailbox_texture_holder.h
new file mode 100644
index 00000000000..58a17c894e7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/mailbox_texture_holder.h
@@ -0,0 +1,63 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_MAILBOX_TEXTURE_HOLDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_MAILBOX_TEXTURE_HOLDER_H_
+
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_types.h"
+#include "third_party/blink/renderer/platform/graphics/texture_holder.h"
+#include "third_party/blink/renderer/platform/graphics/web_graphics_context_3d_provider_wrapper.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/khronos/GLES2/gl2.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT MailboxTextureHolder final : public TextureHolder {
+ public:
+ ~MailboxTextureHolder() override;
+
+ bool IsSkiaTextureHolder() final { return false; }
+ bool IsMailboxTextureHolder() final { return true; }
+ IntSize Size() const final { return size_; }
+ bool CurrentFrameKnownToBeOpaque() final { return false; }
+ bool IsValid() const final;
+ bool IsCrossThread() const final;
+
+ const gpu::Mailbox& GetMailbox() const final { return mailbox_; }
+ const gpu::SyncToken& GetSyncToken() const final { return sync_token_; }
+ void UpdateSyncToken(gpu::SyncToken sync_token) final {
+ sync_token_ = sync_token;
+ }
+
+ void Sync(MailboxSyncMode) final;
+ // In WebGL's commit or transferToImageBitmap calls, it will call the
+ // DrawingBuffer::transferToStaticBitmapImage function, which produces the
+ // input parameters for this method.
+ MailboxTextureHolder(const gpu::Mailbox&,
+ const gpu::SyncToken&,
+ unsigned texture_id_to_delete_after_mailbox_consumed,
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&,
+ IntSize mailbox_size);
+ // This function turns a texture-backed SkImage into a mailbox and a
+ // syncToken.
+ MailboxTextureHolder(std::unique_ptr<TextureHolder>, GLenum filter);
+
+ private:
+ void InitCommon();
+
+ gpu::Mailbox mailbox_;
+ gpu::SyncToken sync_token_;
+ unsigned texture_id_;
+ IntSize size_;
+ bool is_converted_from_skia_texture_;
+ scoped_refptr<base::SingleThreadTaskRunner> texture_thread_task_runner_;
+ PlatformThreadId thread_id_;
+ bool did_issue_ordering_barrier_ = false;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_MAILBOX_TEXTURE_HOLDER_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/offscreen_canvas_frame_dispatcher.h b/chromium/third_party/blink/renderer/platform/graphics/offscreen_canvas_frame_dispatcher.h
new file mode 100644
index 00000000000..f2fde10df9d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/offscreen_canvas_frame_dispatcher.h
@@ -0,0 +1,53 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_OFFSCREEN_CANVAS_FRAME_DISPATCHER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_OFFSCREEN_CANVAS_FRAME_DISPATCHER_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+
+class StaticBitmapImage;
+
+class OffscreenCanvasFrameDispatcherClient {
+ public:
+ virtual void BeginFrame() = 0;
+};
+
+class PLATFORM_EXPORT OffscreenCanvasFrameDispatcher {
+ public:
+ virtual ~OffscreenCanvasFrameDispatcher() = default;
+ virtual void DispatchFrame(scoped_refptr<StaticBitmapImage>,
+ double commit_start_time,
+ const SkIRect& damage_rect) = 0;
+ virtual void ReclaimResource(unsigned resource_id) = 0;
+ virtual void SetNeedsBeginFrame(bool) = 0;
+ virtual void SetSuspendAnimation(bool) = 0;
+ virtual bool NeedsBeginFrame() const = 0;
+ virtual bool IsAnimationSuspended() const = 0;
+
+ virtual void Reshape(int width, int height) = 0;
+
+ base::WeakPtr<OffscreenCanvasFrameDispatcher> GetWeakPtr() {
+ return weak_ptr_factory_.GetWeakPtr();
+ }
+
+ OffscreenCanvasFrameDispatcherClient* Client() { return client_; }
+
+ protected:
+ OffscreenCanvasFrameDispatcher(OffscreenCanvasFrameDispatcherClient* client)
+ : weak_ptr_factory_(this), client_(client) {}
+
+ private:
+ base::WeakPtrFactory<OffscreenCanvasFrameDispatcher> weak_ptr_factory_;
+ OffscreenCanvasFrameDispatcherClient* client_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_OFFSCREEN_CANVAS_FRAME_DISPATCHER_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/offscreen_canvas_frame_dispatcher_impl.cc b/chromium/third_party/blink/renderer/platform/graphics/offscreen_canvas_frame_dispatcher_impl.cc
new file mode 100644
index 00000000000..ed5638d1880
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/offscreen_canvas_frame_dispatcher_impl.cc
@@ -0,0 +1,449 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/offscreen_canvas_frame_dispatcher_impl.h"
+
+#include <memory>
+#include "base/single_thread_task_runner.h"
+#include "components/viz/common/quads/compositor_frame.h"
+#include "components/viz/common/quads/texture_draw_quad.h"
+#include "components/viz/common/resources/resource_format.h"
+#include "third_party/blink/public/platform/interface_provider.h"
+#include "third_party/blink/public/platform/modules/offscreencanvas/offscreen_canvas_surface.mojom-blink.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_graphics_context_3d_provider.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
+#include "third_party/blink/renderer/platform/graphics/offscreen_canvas_placeholder.h"
+#include "third_party/blink/renderer/platform/histogram.h"
+#include "third_party/blink/renderer/platform/scheduler/child/web_scheduler.h"
+#include "third_party/blink/renderer/platform/web_task_runner.h"
+
+namespace blink {
+
+enum {
+ kMaxPendingCompositorFrames = 2,
+ kMaxUnreclaimedPlaceholderFrames = 3,
+};
+
+OffscreenCanvasFrameDispatcherImpl::OffscreenCanvasFrameDispatcherImpl(
+ OffscreenCanvasFrameDispatcherClient* client,
+ uint32_t client_id,
+ uint32_t sink_id,
+ int canvas_id,
+ int width,
+ int height)
+ : OffscreenCanvasFrameDispatcher(client),
+ frame_sink_id_(viz::FrameSinkId(client_id, sink_id)),
+ width_(width),
+ height_(height),
+ change_size_for_next_commit_(false),
+ needs_begin_frame_(false),
+ binding_(this),
+ placeholder_canvas_id_(canvas_id),
+ num_unreclaimed_frames_posted_(0) {
+ if (frame_sink_id_.is_valid()) {
+ // Only frameless canvas pass an invalid frame sink id; we don't create
+ // mojo channel for this special case.
+ current_local_surface_id_ = parent_local_surface_id_allocator_.GenerateId();
+ DCHECK(!sink_.is_bound());
+ mojom::blink::OffscreenCanvasProviderPtr provider;
+ Platform::Current()->GetInterfaceProvider()->GetInterface(
+ mojo::MakeRequest(&provider));
+
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner;
+ auto scheduler = blink::Platform::Current()->CurrentThread()->Scheduler();
+ if (scheduler)
+ task_runner = scheduler->CompositorTaskRunner();
+ viz::mojom::blink::CompositorFrameSinkClientPtr client;
+ binding_.Bind(mojo::MakeRequest(&client), task_runner);
+ provider->CreateCompositorFrameSink(frame_sink_id_, std::move(client),
+ mojo::MakeRequest(&sink_));
+ }
+ offscreen_canvas_resource_provider_ =
+ std::make_unique<OffscreenCanvasResourceProvider>(width, height);
+}
+
+OffscreenCanvasFrameDispatcherImpl::~OffscreenCanvasFrameDispatcherImpl() =
+ default;
+
+namespace {
+
+void UpdatePlaceholderImage(
+ base::WeakPtr<OffscreenCanvasFrameDispatcher> dispatcher,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+ int placeholder_canvas_id,
+ scoped_refptr<blink::StaticBitmapImage> image,
+ unsigned resource_id) {
+ DCHECK(IsMainThread());
+ OffscreenCanvasPlaceholder* placeholder_canvas =
+ OffscreenCanvasPlaceholder::GetPlaceholderById(placeholder_canvas_id);
+ if (placeholder_canvas) {
+ placeholder_canvas->SetPlaceholderFrame(
+ std::move(image), std::move(dispatcher), std::move(task_runner),
+ resource_id);
+ }
+}
+
+} // namespace
+
+void OffscreenCanvasFrameDispatcherImpl::PostImageToPlaceholderIfNotBlocked(
+ scoped_refptr<StaticBitmapImage> image,
+ unsigned resource_id) {
+ if (placeholder_canvas_id_ == kInvalidPlaceholderCanvasId) {
+ offscreen_canvas_resource_provider_->ReclaimResource(resource_id);
+ return;
+ }
+ // Determines whether the main thread may be blocked. If unblocked, post the
+ // image. Otherwise, save the image and do not post it.
+ if (num_unreclaimed_frames_posted_ < kMaxUnreclaimedPlaceholderFrames) {
+ // After this point, |image| can only be used on the main thread, until it
+ // is returned.
+ image->Transfer();
+ this->PostImageToPlaceholder(std::move(image), resource_id);
+ num_unreclaimed_frames_posted_++;
+ } else {
+ DCHECK(num_unreclaimed_frames_posted_ == kMaxUnreclaimedPlaceholderFrames);
+ if (latest_unposted_image_) {
+ // The previous unposted image becomes obsolete now.
+ offscreen_canvas_resource_provider_->ReclaimResource(
+ latest_unposted_resource_id_);
+ }
+
+ latest_unposted_image_ = std::move(image);
+ latest_unposted_resource_id_ = resource_id;
+ }
+}
+
+void OffscreenCanvasFrameDispatcherImpl::PostImageToPlaceholder(
+ scoped_refptr<StaticBitmapImage> image,
+ unsigned resource_id) {
+ scoped_refptr<base::SingleThreadTaskRunner> dispatcher_task_runner =
+ Platform::Current()->CurrentThread()->GetTaskRunner();
+
+ PostCrossThreadTask(
+ *Platform::Current()->MainThread()->Scheduler()->CompositorTaskRunner(),
+ FROM_HERE,
+ CrossThreadBind(UpdatePlaceholderImage, this->GetWeakPtr(),
+ WTF::Passed(std::move(dispatcher_task_runner)),
+ placeholder_canvas_id_, std::move(image), resource_id));
+}
+
+void OffscreenCanvasFrameDispatcherImpl::DispatchFrame(
+ scoped_refptr<StaticBitmapImage> image,
+ double commit_start_time,
+ const SkIRect& damage_rect) {
+ if (!image || !VerifyImageSize(image->Size()))
+ return;
+
+ offscreen_canvas_resource_provider_->IncNextResourceId();
+
+ if (!frame_sink_id_.is_valid()) {
+ PostImageToPlaceholderIfNotBlocked(
+ std::move(image),
+ offscreen_canvas_resource_provider_->GetNextResourceId());
+ return;
+ }
+ viz::CompositorFrame frame;
+ // TODO(crbug.com/652931): update the device_scale_factor
+ frame.metadata.device_scale_factor = 1.0f;
+ if (current_begin_frame_ack_.sequence_number ==
+ viz::BeginFrameArgs::kInvalidFrameNumber) {
+ // TODO(eseckler): This shouldn't be necessary when OffscreenCanvas no
+ // longer submits CompositorFrames without prior BeginFrame.
+ current_begin_frame_ack_ = viz::BeginFrameAck::CreateManualAckWithDamage();
+ } else {
+ current_begin_frame_ack_.has_damage = true;
+ }
+ frame.metadata.begin_frame_ack = current_begin_frame_ack_;
+
+ const gfx::Rect bounds(width_, height_);
+ const int kRenderPassId = 1;
+ bool is_clipped = false;
+ // TODO(crbug.com/705019): optimize for contexts that have {alpha: false}
+ bool are_contents_opaque = false;
+ std::unique_ptr<viz::RenderPass> pass = viz::RenderPass::Create();
+ pass->SetNew(kRenderPassId, bounds,
+ gfx::Rect(damage_rect.x(), damage_rect.y(), damage_rect.width(),
+ damage_rect.height()),
+ gfx::Transform());
+
+ viz::SharedQuadState* sqs = pass->CreateAndAppendSharedQuadState();
+ sqs->SetAll(gfx::Transform(), bounds, bounds, bounds, is_clipped,
+ are_contents_opaque, 1.f, SkBlendMode::kSrcOver, 0);
+
+ viz::TransferableResource resource;
+ offscreen_canvas_resource_provider_->TransferResource(&resource);
+
+ bool yflipped = false;
+ OffscreenCanvasCommitType commit_type;
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(
+ EnumerationHistogram, commit_type_histogram,
+ ("OffscreenCanvas.CommitType", kOffscreenCanvasCommitTypeCount));
+ if (image->IsTextureBacked()) {
+ // While |image| is texture backed, it could be generated with "software
+ // rendering" aka swiftshader. If the compositor is not also using
+ // swiftshader, then we could not give a swiftshader based texture
+ // to the compositor. However in that case, IsGpuCompositingEnabled() will
+ // also be false, so we will avoid doing so.
+ if (SharedGpuContext::IsGpuCompositingEnabled()) {
+ // Case 1: both canvas and compositor are gpu accelerated.
+ commit_type = kCommitGPUCanvasGPUCompositing;
+ offscreen_canvas_resource_provider_
+ ->SetTransferableResourceToStaticBitmapImage(resource, image);
+ yflipped = true;
+ } else {
+ // Case 2: canvas is accelerated but gpu compositing is disabled.
+ commit_type = kCommitGPUCanvasSoftwareCompositing;
+ offscreen_canvas_resource_provider_
+ ->SetTransferableResourceToSharedBitmap(resource, image);
+ }
+ } else {
+ if (SharedGpuContext::IsGpuCompositingEnabled()) {
+ // Case 3: canvas is not gpu-accelerated, but compositor is.
+ commit_type = kCommitSoftwareCanvasGPUCompositing;
+ scoped_refptr<StaticBitmapImage> accelerated_image =
+ image->MakeAccelerated(SharedGpuContext::ContextProviderWrapper());
+ if (!accelerated_image)
+ return;
+ offscreen_canvas_resource_provider_
+ ->SetTransferableResourceToStaticBitmapImage(resource,
+ accelerated_image);
+ } else {
+ // Case 4: both canvas and compositor are not gpu accelerated.
+ commit_type = kCommitSoftwareCanvasSoftwareCompositing;
+ offscreen_canvas_resource_provider_
+ ->SetTransferableResourceToSharedBitmap(resource, image);
+ }
+ }
+
+ commit_type_histogram.Count(commit_type);
+
+ PostImageToPlaceholderIfNotBlocked(
+ std::move(image),
+ offscreen_canvas_resource_provider_->GetNextResourceId());
+
+ frame.resource_list.push_back(std::move(resource));
+
+ viz::TextureDrawQuad* quad =
+ pass->CreateAndAppendDrawQuad<viz::TextureDrawQuad>();
+ gfx::Size rect_size(width_, height_);
+
+ // TODO(crbug.com/705019): optimize for contexts that have {alpha: false}
+ const bool kNeedsBlending = true;
+
+ // TODO(crbug.com/645993): this should be inherited from WebGL context's
+ // creation settings.
+ const bool kPremultipliedAlpha = true;
+ const gfx::PointF uv_top_left(0.f, 0.f);
+ const gfx::PointF uv_bottom_right(1.f, 1.f);
+ float vertex_opacity[4] = {1.f, 1.f, 1.f, 1.f};
+ // TODO(crbug.com/645994): this should be true when using style
+ // "image-rendering: pixelated".
+ // TODO(crbug.com/645590): filter should respect the image-rendering CSS
+ // property of associated canvas element.
+ const bool kNearestNeighbor = false;
+ quad->SetAll(sqs, bounds, bounds, kNeedsBlending, resource.id, gfx::Size(),
+ kPremultipliedAlpha, uv_top_left, uv_bottom_right,
+ SK_ColorTRANSPARENT, vertex_opacity, yflipped, kNearestNeighbor,
+ false);
+
+ frame.render_pass_list.push_back(std::move(pass));
+
+ double elapsed_time = WTF::CurrentTimeTicksInSeconds() - commit_start_time;
+
+ switch (commit_type) {
+ case kCommitGPUCanvasGPUCompositing:
+ if (IsMainThread()) {
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, commit_gpu_canvas_gpu_compositing_main_timer,
+ ("Blink.Canvas.OffscreenCommit.GPUCanvasGPUCompositingMain", 0,
+ 10000000, 50));
+ commit_gpu_canvas_gpu_compositing_main_timer.Count(elapsed_time *
+ 1000000.0);
+ } else {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(
+ CustomCountHistogram,
+ commit_gpu_canvas_gpu_compositing_worker_timer,
+ ("Blink.Canvas.OffscreenCommit.GPUCanvasGPUCompositingWorker", 0,
+ 10000000, 50));
+ commit_gpu_canvas_gpu_compositing_worker_timer.Count(elapsed_time *
+ 1000000.0);
+ }
+ break;
+ case kCommitGPUCanvasSoftwareCompositing:
+ if (IsMainThread()) {
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram,
+ commit_gpu_canvas_software_compositing_main_timer,
+ ("Blink.Canvas.OffscreenCommit.GPUCanvasSoftwareCompositingMain", 0,
+ 10000000, 50));
+ commit_gpu_canvas_software_compositing_main_timer.Count(elapsed_time *
+ 1000000.0);
+ } else {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(
+ CustomCountHistogram,
+ commit_gpu_canvas_software_compositing_worker_timer,
+ ("Blink.Canvas.OffscreenCommit."
+ "GPUCanvasSoftwareCompositingWorker",
+ 0, 10000000, 50));
+ commit_gpu_canvas_software_compositing_worker_timer.Count(elapsed_time *
+ 1000000.0);
+ }
+ break;
+ case kCommitSoftwareCanvasGPUCompositing:
+ if (IsMainThread()) {
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram,
+ commit_software_canvas_gpu_compositing_main_timer,
+ ("Blink.Canvas.OffscreenCommit.SoftwareCanvasGPUCompositingMain", 0,
+ 10000000, 50));
+ commit_software_canvas_gpu_compositing_main_timer.Count(elapsed_time *
+ 1000000.0);
+ } else {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(
+ CustomCountHistogram,
+ commit_software_canvas_gpu_compositing_worker_timer,
+ ("Blink.Canvas.OffscreenCommit."
+ "SoftwareCanvasGPUCompositingWorker",
+ 0, 10000000, 50));
+ commit_software_canvas_gpu_compositing_worker_timer.Count(elapsed_time *
+ 1000000.0);
+ }
+ break;
+ case kCommitSoftwareCanvasSoftwareCompositing:
+ if (IsMainThread()) {
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram,
+ commit_software_canvas_software_compositing_main_timer,
+ ("Blink.Canvas.OffscreenCommit."
+ "SoftwareCanvasSoftwareCompositingMain",
+ 0, 10000000, 50));
+ commit_software_canvas_software_compositing_main_timer.Count(
+ elapsed_time * 1000000.0);
+ } else {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(
+ CustomCountHistogram,
+ commit_software_canvas_software_compositing_worker_timer,
+ ("Blink.Canvas.OffscreenCommit."
+ "SoftwareCanvasSoftwareCompositingWorker",
+ 0, 10000000, 50));
+ commit_software_canvas_software_compositing_worker_timer.Count(
+ elapsed_time * 1000000.0);
+ }
+ break;
+ case kOffscreenCanvasCommitTypeCount:
+ NOTREACHED();
+ }
+
+ if (change_size_for_next_commit_) {
+ current_local_surface_id_ = parent_local_surface_id_allocator_.GenerateId();
+ change_size_for_next_commit_ = false;
+ }
+
+ pending_compositor_frames_++;
+ sink_->SubmitCompositorFrame(current_local_surface_id_, std::move(frame),
+ nullptr, 0);
+}
+
+void OffscreenCanvasFrameDispatcherImpl::DidReceiveCompositorFrameAck(
+ const WTF::Vector<viz::ReturnedResource>& resources) {
+ ReclaimResources(resources);
+ pending_compositor_frames_--;
+ DCHECK_GE(pending_compositor_frames_, 0);
+}
+
+void OffscreenCanvasFrameDispatcherImpl::DidPresentCompositorFrame(
+ uint32_t presentation_token,
+ mojo_base::mojom::blink::TimeTicksPtr time,
+ WTF::TimeDelta refresh,
+ uint32_t flags) {
+ NOTIMPLEMENTED();
+}
+
+void OffscreenCanvasFrameDispatcherImpl::DidDiscardCompositorFrame(
+ uint32_t presentation_token) {
+ NOTIMPLEMENTED();
+}
+
+void OffscreenCanvasFrameDispatcherImpl::SetNeedsBeginFrame(
+ bool needs_begin_frame) {
+ if (needs_begin_frame_ == needs_begin_frame)
+ return;
+ needs_begin_frame_ = needs_begin_frame;
+ if (!suspend_animation_)
+ SetNeedsBeginFrameInternal();
+}
+
+void OffscreenCanvasFrameDispatcherImpl::SetSuspendAnimation(
+ bool suspend_animation) {
+ if (suspend_animation_ == suspend_animation)
+ return;
+ suspend_animation_ = suspend_animation;
+ if (needs_begin_frame_)
+ SetNeedsBeginFrameInternal();
+}
+
+void OffscreenCanvasFrameDispatcherImpl::SetNeedsBeginFrameInternal() {
+ if (sink_) {
+ sink_->SetNeedsBeginFrame(needs_begin_frame_ && !suspend_animation_);
+ }
+}
+
+void OffscreenCanvasFrameDispatcherImpl::OnBeginFrame(
+ const viz::BeginFrameArgs& begin_frame_args) {
+ DCHECK(Client());
+
+ current_begin_frame_ack_ = viz::BeginFrameAck(
+ begin_frame_args.source_id, begin_frame_args.sequence_number, false);
+
+ if (pending_compositor_frames_ >= kMaxPendingCompositorFrames ||
+ (begin_frame_args.type == viz::BeginFrameArgs::MISSED &&
+ base::TimeTicks::Now() > begin_frame_args.deadline)) {
+ sink_->DidNotProduceFrame(current_begin_frame_ack_);
+ return;
+ }
+
+ Client()->BeginFrame();
+ // TODO(eseckler): Tell |m_sink| if we did not draw during the BeginFrame.
+ current_begin_frame_ack_.sequence_number =
+ viz::BeginFrameArgs::kInvalidFrameNumber;
+}
+void OffscreenCanvasFrameDispatcherImpl::ReclaimResources(
+ const WTF::Vector<viz::ReturnedResource>& resources) {
+ offscreen_canvas_resource_provider_->ReclaimResources(resources);
+}
+
+void OffscreenCanvasFrameDispatcherImpl::ReclaimResource(unsigned resource_id) {
+ offscreen_canvas_resource_provider_->ReclaimResource(resource_id);
+ num_unreclaimed_frames_posted_--;
+
+ // The main thread has become unblocked recently and we have an image that
+ // have not been posted yet.
+ if (latest_unposted_image_) {
+ DCHECK(num_unreclaimed_frames_posted_ ==
+ kMaxUnreclaimedPlaceholderFrames - 1);
+ PostImageToPlaceholderIfNotBlocked(std::move(latest_unposted_image_),
+ latest_unposted_resource_id_);
+ latest_unposted_resource_id_ = 0;
+ }
+}
+
+bool OffscreenCanvasFrameDispatcherImpl::VerifyImageSize(
+ const IntSize image_size) {
+ if (image_size.Width() == width_ && image_size.Height() == height_)
+ return true;
+ return false;
+}
+
+void OffscreenCanvasFrameDispatcherImpl::Reshape(int width, int height) {
+ if (width_ != width || height_ != height) {
+ width_ = width;
+ height_ = height;
+ offscreen_canvas_resource_provider_->Reshape(width, height);
+ change_size_for_next_commit_ = true;
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/offscreen_canvas_frame_dispatcher_impl.h b/chromium/third_party/blink/renderer/platform/graphics/offscreen_canvas_frame_dispatcher_impl.h
new file mode 100644
index 00000000000..0630852971d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/offscreen_canvas_frame_dispatcher_impl.h
@@ -0,0 +1,111 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_OFFSCREEN_CANVAS_FRAME_DISPATCHER_IMPL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_OFFSCREEN_CANVAS_FRAME_DISPATCHER_IMPL_H_
+
+#include <memory>
+#include "components/viz/common/frame_sinks/begin_frame_args.h"
+#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "services/viz/public/interfaces/compositing/compositor_frame_sink.mojom-blink.h"
+#include "third_party/blink/renderer/platform/graphics/offscreen_canvas_frame_dispatcher.h"
+#include "third_party/blink/renderer/platform/graphics/offscreen_canvas_resource_provider.h"
+#include "third_party/blink/renderer/platform/wtf/compiler.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT OffscreenCanvasFrameDispatcherImpl
+ : public OffscreenCanvasFrameDispatcher,
+ public viz::mojom::blink::CompositorFrameSinkClient {
+ public:
+ enum {
+ kInvalidPlaceholderCanvasId = -1,
+ };
+
+ OffscreenCanvasFrameDispatcherImpl(OffscreenCanvasFrameDispatcherClient*,
+ uint32_t client_id,
+ uint32_t sink_id,
+ int placeholder_canvas_id,
+ int width,
+ int height);
+
+ // OffscreenCanvasFrameDispatcher implementation.
+ virtual ~OffscreenCanvasFrameDispatcherImpl();
+ void SetNeedsBeginFrame(bool) final;
+ void SetSuspendAnimation(bool) final;
+ bool NeedsBeginFrame() const final { return needs_begin_frame_; }
+ bool IsAnimationSuspended() const final { return suspend_animation_; }
+ void DispatchFrame(scoped_refptr<StaticBitmapImage>,
+ double commit_start_time,
+ const SkIRect& damage_rect) final;
+ void ReclaimResource(unsigned resource_id) final;
+ void Reshape(int width, int height) final;
+
+ // viz::mojom::blink::CompositorFrameSinkClient implementation.
+ void DidReceiveCompositorFrameAck(
+ const WTF::Vector<viz::ReturnedResource>& resources) final;
+ void DidPresentCompositorFrame(uint32_t presentation_token,
+ mojo_base::mojom::blink::TimeTicksPtr,
+ WTF::TimeDelta refresh,
+ uint32_t flags) final;
+ void DidDiscardCompositorFrame(uint32_t presentation_token) final;
+ void OnBeginFrame(const viz::BeginFrameArgs&) final;
+ void OnBeginFramePausedChanged(bool paused) final{};
+ void ReclaimResources(
+ const WTF::Vector<viz::ReturnedResource>& resources) final;
+
+ // This enum is used in histogram, so it should be append-only.
+ enum OffscreenCanvasCommitType {
+ kCommitGPUCanvasGPUCompositing = 0,
+ kCommitGPUCanvasSoftwareCompositing = 1,
+ kCommitSoftwareCanvasGPUCompositing = 2,
+ kCommitSoftwareCanvasSoftwareCompositing = 3,
+ kOffscreenCanvasCommitTypeCount,
+ };
+
+ private:
+ friend class OffscreenCanvasFrameDispatcherImplTest;
+
+ // Surface-related
+ viz::ParentLocalSurfaceIdAllocator parent_local_surface_id_allocator_;
+ const viz::FrameSinkId frame_sink_id_;
+ viz::LocalSurfaceId current_local_surface_id_;
+
+ int width_;
+ int height_;
+ bool change_size_for_next_commit_;
+ bool suspend_animation_ = false;
+ bool needs_begin_frame_ = false;
+ int pending_compositor_frames_ = 0;
+
+ void SetNeedsBeginFrameInternal();
+
+ std::unique_ptr<OffscreenCanvasResourceProvider>
+ offscreen_canvas_resource_provider_;
+
+ bool VerifyImageSize(const IntSize);
+ void PostImageToPlaceholderIfNotBlocked(scoped_refptr<StaticBitmapImage>,
+ unsigned resource_id);
+ // virtual for testing
+ virtual void PostImageToPlaceholder(scoped_refptr<StaticBitmapImage>,
+ unsigned resource_id);
+
+ viz::mojom::blink::CompositorFrameSinkPtr sink_;
+ mojo::Binding<viz::mojom::blink::CompositorFrameSinkClient> binding_;
+
+ int placeholder_canvas_id_;
+
+ // The latest_unposted_resource_id_ always refers to the Id of the frame
+ // resource used by the latest_unposted_image_.
+ scoped_refptr<StaticBitmapImage> latest_unposted_image_;
+ unsigned latest_unposted_resource_id_;
+ unsigned num_unreclaimed_frames_posted_;
+
+ viz::BeginFrameAck current_begin_frame_ack_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_OFFSCREEN_CANVAS_FRAME_DISPATCHER_IMPL_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/offscreen_canvas_frame_dispatcher_impl_test.cc b/chromium/third_party/blink/renderer/platform/graphics/offscreen_canvas_frame_dispatcher_impl_test.cc
new file mode 100644
index 00000000000..c3ec43e599a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/offscreen_canvas_frame_dispatcher_impl_test.cc
@@ -0,0 +1,160 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/offscreen_canvas_frame_dispatcher_impl.h"
+
+#include <memory>
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkSurface.h"
+
+using testing::_;
+using testing::Mock;
+
+namespace blink {
+
+class MockOffscreenCanvasFrameDispatcherImpl
+ : public OffscreenCanvasFrameDispatcherImpl {
+ public:
+ MockOffscreenCanvasFrameDispatcherImpl()
+ : OffscreenCanvasFrameDispatcherImpl(nullptr, 0, 0, 0, 10, 10) {}
+
+ MOCK_METHOD2(PostImageToPlaceholder,
+ void(scoped_refptr<StaticBitmapImage>, unsigned resource_id));
+};
+
+class OffscreenCanvasFrameDispatcherImplTest : public testing::Test {
+ public:
+ void DispatchOneFrame();
+ OffscreenCanvasResourceProvider* GetResourceProvider() {
+ return dispatcher_->offscreen_canvas_resource_provider_.get();
+ }
+
+ unsigned GetNumUnreclaimedFramesPosted() {
+ return dispatcher_->num_unreclaimed_frames_posted_;
+ }
+
+ StaticBitmapImage* GetLatestUnpostedImage() {
+ return dispatcher_->latest_unposted_image_.get();
+ }
+
+ unsigned GetLatestUnpostedResourceId() {
+ return dispatcher_->latest_unposted_resource_id_;
+ }
+
+ protected:
+ OffscreenCanvasFrameDispatcherImplTest() {
+ dispatcher_ = std::make_unique<MockOffscreenCanvasFrameDispatcherImpl>();
+ }
+
+ MockOffscreenCanvasFrameDispatcherImpl* Dispatcher() {
+ return dispatcher_.get();
+ }
+
+ private:
+ scoped_refptr<StaticBitmapImage> PrepareStaticBitmapImage();
+ std::unique_ptr<MockOffscreenCanvasFrameDispatcherImpl> dispatcher_;
+};
+
+void OffscreenCanvasFrameDispatcherImplTest::DispatchOneFrame() {
+ sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(10, 10);
+ dispatcher_->DispatchFrame(
+ StaticBitmapImage::Create(surface->makeImageSnapshot()), 0.0,
+ SkIRect::MakeEmpty());
+}
+
+TEST_F(OffscreenCanvasFrameDispatcherImplTest, PlaceholderRunsNormally) {
+ /* We allow OffscreenCanvas to post up to 3 frames without hearing a response
+ * from placeholder. */
+ // Post first frame
+ unsigned post_resource_id = 1u;
+ EXPECT_CALL(*(Dispatcher()), PostImageToPlaceholder(_, post_resource_id));
+ DispatchOneFrame();
+ EXPECT_EQ(1u, GetNumUnreclaimedFramesPosted());
+ EXPECT_EQ(1u, GetResourceProvider()->GetNextResourceId());
+ Mock::VerifyAndClearExpectations(Dispatcher());
+
+ // Post second frame
+ post_resource_id++;
+ EXPECT_CALL(*(Dispatcher()), PostImageToPlaceholder(_, post_resource_id));
+ DispatchOneFrame();
+ EXPECT_EQ(2u, GetNumUnreclaimedFramesPosted());
+ EXPECT_EQ(2u, GetResourceProvider()->GetNextResourceId());
+ Mock::VerifyAndClearExpectations(Dispatcher());
+
+ // Post third frame
+ post_resource_id++;
+ EXPECT_CALL(*(Dispatcher()), PostImageToPlaceholder(_, post_resource_id));
+ DispatchOneFrame();
+ EXPECT_EQ(3u, GetNumUnreclaimedFramesPosted());
+ EXPECT_EQ(3u, GetResourceProvider()->GetNextResourceId());
+ EXPECT_EQ(nullptr, GetLatestUnpostedImage());
+ Mock::VerifyAndClearExpectations(Dispatcher());
+
+ /* We mock the behavior of placeholder on main thread here, by reclaiming
+ * the resources in order. */
+ // Reclaim first frame
+ unsigned reclaim_resource_id = 1u;
+ Dispatcher()->ReclaimResource(reclaim_resource_id);
+ EXPECT_EQ(2u, GetNumUnreclaimedFramesPosted());
+
+ // Reclaim second frame
+ reclaim_resource_id++;
+ Dispatcher()->ReclaimResource(reclaim_resource_id);
+ EXPECT_EQ(1u, GetNumUnreclaimedFramesPosted());
+
+ // Reclaim third frame
+ reclaim_resource_id++;
+ Dispatcher()->ReclaimResource(reclaim_resource_id);
+ EXPECT_EQ(0u, GetNumUnreclaimedFramesPosted());
+}
+
+TEST_F(OffscreenCanvasFrameDispatcherImplTest, PlaceholderBeingBlocked) {
+ /* When main thread is blocked, attempting to post more than 3 frames will
+ * result in only 3 PostImageToPlaceholder. The latest unposted image will
+ * be saved. */
+ EXPECT_CALL(*(Dispatcher()), PostImageToPlaceholder(_, _)).Times(3);
+
+ // Attempt to post 4 times
+ DispatchOneFrame();
+ DispatchOneFrame();
+ DispatchOneFrame();
+ DispatchOneFrame();
+ unsigned post_resource_id = 4u;
+ EXPECT_EQ(3u, GetNumUnreclaimedFramesPosted());
+ EXPECT_EQ(post_resource_id, GetResourceProvider()->GetNextResourceId());
+ EXPECT_TRUE(GetLatestUnpostedImage());
+ EXPECT_EQ(post_resource_id, GetLatestUnpostedResourceId());
+
+ // Attempt to post the 5th time. The latest unposted image will be replaced.
+ post_resource_id++;
+ DispatchOneFrame();
+ EXPECT_EQ(3u, GetNumUnreclaimedFramesPosted());
+ EXPECT_EQ(post_resource_id, GetResourceProvider()->GetNextResourceId());
+ EXPECT_TRUE(GetLatestUnpostedImage());
+ EXPECT_EQ(post_resource_id, GetLatestUnpostedResourceId());
+
+ Mock::VerifyAndClearExpectations(Dispatcher());
+
+ /* When main thread becomes unblocked, the first reclaim called by placeholder
+ * will trigger OffscreenCanvasFrameDispatcher to post the last saved image.
+ * Resource reclaim happens in the same order as frame posting. */
+ unsigned reclaim_resource_id = 1u;
+ EXPECT_CALL(*(Dispatcher()), PostImageToPlaceholder(_, post_resource_id));
+ Dispatcher()->ReclaimResource(reclaim_resource_id);
+ // Reclaim 1 frame and post 1 frame, so numPostImagesUnresponded remains as 3
+ EXPECT_EQ(3u, GetNumUnreclaimedFramesPosted());
+ // Not generating new resource Id
+ EXPECT_EQ(post_resource_id, GetResourceProvider()->GetNextResourceId());
+ EXPECT_FALSE(GetLatestUnpostedImage());
+ EXPECT_EQ(0u, GetLatestUnpostedResourceId());
+ Mock::VerifyAndClearExpectations(Dispatcher());
+
+ reclaim_resource_id++;
+ Dispatcher()->ReclaimResource(reclaim_resource_id);
+ EXPECT_EQ(2u, GetNumUnreclaimedFramesPosted());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/offscreen_canvas_placeholder.cc b/chromium/third_party/blink/renderer/platform/graphics/offscreen_canvas_placeholder.cc
new file mode 100644
index 00000000000..4fd962d917d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/offscreen_canvas_placeholder.cc
@@ -0,0 +1,155 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/offscreen_canvas_placeholder.h"
+
+#include "base/single_thread_task_runner.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/graphics/offscreen_canvas_frame_dispatcher.h"
+#include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
+#include "third_party/blink/renderer/platform/web_task_runner.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+
+namespace {
+
+typedef HashMap<int, blink::OffscreenCanvasPlaceholder*> PlaceholderIdMap;
+
+PlaceholderIdMap& placeholderRegistry() {
+ DCHECK(IsMainThread());
+ DEFINE_STATIC_LOCAL(PlaceholderIdMap, s_placeholderRegistry, ());
+ return s_placeholderRegistry;
+}
+
+void releaseFrameToDispatcher(
+ base::WeakPtr<blink::OffscreenCanvasFrameDispatcher> dispatcher,
+ scoped_refptr<blink::Image> oldImage,
+ unsigned resourceId) {
+ oldImage = nullptr; // Needed to unref'ed on the right thread
+ if (dispatcher) {
+ dispatcher->ReclaimResource(resourceId);
+ }
+}
+
+void SetSuspendAnimation(
+ base::WeakPtr<blink::OffscreenCanvasFrameDispatcher> dispatcher,
+ bool suspend) {
+ if (dispatcher) {
+ dispatcher->SetSuspendAnimation(suspend);
+ }
+}
+
+} // unnamed namespace
+
+namespace blink {
+
+OffscreenCanvasPlaceholder::~OffscreenCanvasPlaceholder() {
+ UnregisterPlaceholder();
+}
+
+OffscreenCanvasPlaceholder* OffscreenCanvasPlaceholder::GetPlaceholderById(
+ unsigned placeholder_id) {
+ PlaceholderIdMap::iterator it = placeholderRegistry().find(placeholder_id);
+ if (it == placeholderRegistry().end())
+ return nullptr;
+ return it->value;
+}
+
+void OffscreenCanvasPlaceholder::RegisterPlaceholder(unsigned placeholder_id) {
+ DCHECK(!placeholderRegistry().Contains(placeholder_id));
+ DCHECK(!IsPlaceholderRegistered());
+ placeholderRegistry().insert(placeholder_id, this);
+ placeholder_id_ = placeholder_id;
+}
+
+void OffscreenCanvasPlaceholder::UnregisterPlaceholder() {
+ if (!IsPlaceholderRegistered())
+ return;
+ DCHECK(placeholderRegistry().find(placeholder_id_)->value == this);
+ placeholderRegistry().erase(placeholder_id_);
+ placeholder_id_ = kNoPlaceholderId;
+}
+
+void OffscreenCanvasPlaceholder::SetPlaceholderFrame(
+ scoped_refptr<StaticBitmapImage> new_frame,
+ base::WeakPtr<OffscreenCanvasFrameDispatcher> dispatcher,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+ unsigned resource_id) {
+ DCHECK(IsPlaceholderRegistered());
+ DCHECK(new_frame);
+ ReleasePlaceholderFrame();
+ placeholder_frame_ = std::move(new_frame);
+ frame_dispatcher_ = std::move(dispatcher);
+ frame_dispatcher_task_runner_ = std::move(task_runner);
+ placeholder_frame_resource_id_ = resource_id;
+
+ if (animation_state_ == kShouldSuspendAnimation) {
+ bool success = PostSetSuspendAnimationToOffscreenCanvasThread(true);
+ DCHECK(success);
+ animation_state_ = kSuspendedAnimation;
+ } else if (animation_state_ == kShouldActivateAnimation) {
+ bool success = PostSetSuspendAnimationToOffscreenCanvasThread(false);
+ DCHECK(success);
+ animation_state_ = kActiveAnimation;
+ }
+}
+
+void OffscreenCanvasPlaceholder::ReleasePlaceholderFrame() {
+ DCHECK(IsPlaceholderRegistered());
+ if (placeholder_frame_) {
+ placeholder_frame_->Transfer();
+ PostCrossThreadTask(
+ *frame_dispatcher_task_runner_, FROM_HERE,
+ CrossThreadBind(releaseFrameToDispatcher, std::move(frame_dispatcher_),
+ std::move(placeholder_frame_),
+ placeholder_frame_resource_id_));
+ }
+}
+
+void OffscreenCanvasPlaceholder::SetSuspendOffscreenCanvasAnimation(
+ bool suspend) {
+ switch (animation_state_) {
+ case kActiveAnimation:
+ if (suspend) {
+ if (PostSetSuspendAnimationToOffscreenCanvasThread(suspend)) {
+ animation_state_ = kSuspendedAnimation;
+ } else {
+ animation_state_ = kShouldSuspendAnimation;
+ }
+ }
+ break;
+ case kSuspendedAnimation:
+ if (!suspend) {
+ if (PostSetSuspendAnimationToOffscreenCanvasThread(suspend)) {
+ animation_state_ = kActiveAnimation;
+ } else {
+ animation_state_ = kShouldActivateAnimation;
+ }
+ }
+ break;
+ case kShouldSuspendAnimation:
+ if (!suspend) {
+ animation_state_ = kActiveAnimation;
+ }
+ break;
+ case kShouldActivateAnimation:
+ if (suspend) {
+ animation_state_ = kSuspendedAnimation;
+ }
+ break;
+ default:
+ NOTREACHED();
+ }
+}
+
+bool OffscreenCanvasPlaceholder::PostSetSuspendAnimationToOffscreenCanvasThread(
+ bool suspend) {
+ if (!frame_dispatcher_task_runner_)
+ return false;
+ PostCrossThreadTask(
+ *frame_dispatcher_task_runner_, FROM_HERE,
+ CrossThreadBind(SetSuspendAnimation, frame_dispatcher_, suspend));
+ return true;
+}
+
+} // blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/offscreen_canvas_placeholder.h b/chromium/third_party/blink/renderer/platform/graphics/offscreen_canvas_placeholder.h
new file mode 100644
index 00000000000..37820d2a726
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/offscreen_canvas_placeholder.h
@@ -0,0 +1,69 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_OFFSCREEN_CANVAS_PLACEHOLDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_OFFSCREEN_CANVAS_PLACEHOLDER_H_
+
+#include <memory>
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+
+class OffscreenCanvasFrameDispatcher;
+class StaticBitmapImage;
+
+class PLATFORM_EXPORT OffscreenCanvasPlaceholder {
+ public:
+ ~OffscreenCanvasPlaceholder();
+
+ virtual void SetPlaceholderFrame(
+ scoped_refptr<StaticBitmapImage>,
+ base::WeakPtr<OffscreenCanvasFrameDispatcher>,
+ scoped_refptr<base::SingleThreadTaskRunner>,
+ unsigned resource_id);
+ void ReleasePlaceholderFrame();
+
+ void SetSuspendOffscreenCanvasAnimation(bool);
+
+ static OffscreenCanvasPlaceholder* GetPlaceholderById(
+ unsigned placeholder_id);
+
+ void RegisterPlaceholder(unsigned placeholder_id);
+ void UnregisterPlaceholder();
+ const scoped_refptr<StaticBitmapImage>& PlaceholderFrame() const {
+ return placeholder_frame_;
+ }
+
+ bool IsPlaceholderRegistered() const {
+ return placeholder_id_ != kNoPlaceholderId;
+ }
+
+ private:
+ bool PostSetSuspendAnimationToOffscreenCanvasThread(bool suspend);
+
+ scoped_refptr<StaticBitmapImage> placeholder_frame_;
+ base::WeakPtr<OffscreenCanvasFrameDispatcher> frame_dispatcher_;
+ scoped_refptr<base::SingleThreadTaskRunner> frame_dispatcher_task_runner_;
+ unsigned placeholder_frame_resource_id_ = 0;
+
+ enum {
+ kNoPlaceholderId = -1,
+ };
+ int placeholder_id_ = kNoPlaceholderId;
+
+ enum AnimationState {
+ kActiveAnimation,
+ kSuspendedAnimation,
+ kShouldSuspendAnimation,
+ kShouldActivateAnimation,
+ };
+ AnimationState animation_state_ = kActiveAnimation;
+};
+
+} // blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/graphics/offscreen_canvas_resource_provider.cc b/chromium/third_party/blink/renderer/platform/graphics/offscreen_canvas_resource_provider.cc
new file mode 100644
index 00000000000..4fd69838c42
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/offscreen_canvas_resource_provider.cc
@@ -0,0 +1,151 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/offscreen_canvas_resource_provider.h"
+
+#include "base/numerics/checked_math.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "gpu/command_buffer/common/capabilities.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_graphics_context_3d_provider.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
+#include "third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer.h"
+#include "third_party/blink/renderer/platform/wtf/typed_arrays/uint8_array.h"
+#include "third_party/khronos/GLES2/gl2.h"
+#include "third_party/khronos/GLES2/gl2ext.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/core/SkSwizzle.h"
+#include "third_party/skia/include/gpu/GrContext.h"
+
+namespace blink {
+
+OffscreenCanvasResourceProvider::OffscreenCanvasResourceProvider(int width,
+ int height)
+ : width_(width), height_(height), next_resource_id_(0u) {}
+
+OffscreenCanvasResourceProvider::~OffscreenCanvasResourceProvider() = default;
+
+std::unique_ptr<OffscreenCanvasResourceProvider::FrameResource>
+OffscreenCanvasResourceProvider::CreateOrRecycleFrameResource() {
+ if (recyclable_resource_) {
+ recyclable_resource_->spare_lock_ = true;
+ return std::move(recyclable_resource_);
+ }
+ return std::make_unique<FrameResource>();
+}
+
+void OffscreenCanvasResourceProvider::TransferResource(
+ viz::TransferableResource* resource) {
+ resource->id = next_resource_id_;
+ resource->format = viz::ResourceFormat::RGBA_8888;
+ resource->size = gfx::Size(width_, height_);
+ // This indicates the filtering on the resource inherently, not the desired
+ // filtering effect on the quad.
+ resource->filter = GL_NEAREST;
+ // TODO(crbug.com/646022): making this overlay-able.
+ resource->is_overlay_candidate = false;
+}
+
+// TODO(xlai): Handle error cases when, by any reason,
+// OffscreenCanvasResourceProvider fails to get image data.
+void OffscreenCanvasResourceProvider::SetTransferableResourceToSharedBitmap(
+ viz::TransferableResource& resource,
+ scoped_refptr<StaticBitmapImage> image) {
+ std::unique_ptr<FrameResource> frame_resource =
+ CreateOrRecycleFrameResource();
+ if (!frame_resource->shared_bitmap_) {
+ frame_resource->shared_bitmap_ = Platform::Current()->AllocateSharedBitmap(
+ IntSize(width_, height_), viz::ResourceFormat::RGBA_8888);
+ if (!frame_resource->shared_bitmap_)
+ return;
+ }
+ unsigned char* pixels = frame_resource->shared_bitmap_->pixels();
+ DCHECK(pixels);
+ // TODO(xlai): Optimize to avoid copying pixels. See crbug.com/651456.
+ // However, in the case when |image| is texture backed, this function call
+ // does a GPU readback which is required.
+ sk_sp<SkImage> sk_image = image->PaintImageForCurrentFrame().GetSkImage();
+ if (sk_image->bounds().isEmpty())
+ return;
+ SkImageInfo image_info = SkImageInfo::Make(
+ width_, height_, kN32_SkColorType,
+ image->IsPremultiplied() ? kPremul_SkAlphaType : kUnpremul_SkAlphaType,
+ sk_image->refColorSpace());
+ if (image_info.isEmpty())
+ return;
+ bool read_pixels_successful =
+ sk_image->readPixels(image_info, pixels, image_info.minRowBytes(), 0, 0);
+ DCHECK(read_pixels_successful);
+ if (!read_pixels_successful)
+ return;
+ resource.mailbox_holder.mailbox = frame_resource->shared_bitmap_->id();
+ resource.mailbox_holder.texture_target = 0;
+ resource.is_software = true;
+
+ resources_.insert(next_resource_id_, std::move(frame_resource));
+}
+
+void OffscreenCanvasResourceProvider::
+ SetTransferableResourceToStaticBitmapImage(
+ viz::TransferableResource& resource,
+ scoped_refptr<StaticBitmapImage> image) {
+ DCHECK(image->IsTextureBacked());
+ DCHECK(image->IsValid());
+ image->EnsureMailbox(kVerifiedSyncToken, GL_LINEAR);
+ resource.mailbox_holder = gpu::MailboxHolder(
+ image->GetMailbox(), image->GetSyncToken(), GL_TEXTURE_2D);
+ resource.read_lock_fences_enabled = false;
+ resource.is_software = false;
+
+ std::unique_ptr<FrameResource> frame_resource =
+ CreateOrRecycleFrameResource();
+
+ frame_resource->image_ = std::move(image);
+ resources_.insert(next_resource_id_, std::move(frame_resource));
+}
+
+OffscreenCanvasResourceProvider::FrameResource::~FrameResource() = default;
+
+void OffscreenCanvasResourceProvider::ReclaimResources(
+ const WTF::Vector<viz::ReturnedResource>& resources) {
+ for (const auto& resource : resources) {
+ auto it = resources_.find(resource.id);
+
+ DCHECK(it != resources_.end());
+ if (it == resources_.end())
+ continue;
+
+ if (it->value->image_ && it->value->image_->ContextProviderWrapper() &&
+ resource.sync_token.HasData()) {
+ it->value->image_->ContextProviderWrapper()
+ ->ContextProvider()
+ ->ContextGL()
+ ->WaitSyncTokenCHROMIUM(resource.sync_token.GetConstData());
+ }
+ ReclaimResourceInternal(it);
+ }
+}
+
+void OffscreenCanvasResourceProvider::ReclaimResource(unsigned resource_id) {
+ auto it = resources_.find(resource_id);
+ if (it != resources_.end()) {
+ ReclaimResourceInternal(it);
+ }
+}
+
+void OffscreenCanvasResourceProvider::ReclaimResourceInternal(
+ const ResourceMap::iterator& it) {
+ if (it->value->spare_lock_) {
+ it->value->spare_lock_ = false;
+ } else {
+ // Really reclaim the resources
+ recyclable_resource_ = std::move(it->value);
+ // release SkImage immediately since it is not recyclable
+ recyclable_resource_->image_ = nullptr;
+ resources_.erase(it);
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/offscreen_canvas_resource_provider.h b/chromium/third_party/blink/renderer/platform/graphics/offscreen_canvas_resource_provider.h
new file mode 100644
index 00000000000..16b1b81b253
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/offscreen_canvas_resource_provider.h
@@ -0,0 +1,66 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_OFFSCREEN_CANVAS_RESOURCE_PROVIDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_OFFSCREEN_CANVAS_RESOURCE_PROVIDER_H_
+
+#include "components/viz/common/quads/shared_bitmap.h"
+#include "components/viz/common/resources/returned_resource.h"
+#include "components/viz/common/resources/transferable_resource.h"
+#include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT OffscreenCanvasResourceProvider {
+ public:
+ OffscreenCanvasResourceProvider(int width, int height);
+
+ ~OffscreenCanvasResourceProvider();
+
+ void TransferResource(viz::TransferableResource*);
+ void SetTransferableResourceToSharedBitmap(viz::TransferableResource&,
+ scoped_refptr<StaticBitmapImage>);
+ void SetTransferableResourceToStaticBitmapImage(
+ viz::TransferableResource&,
+ scoped_refptr<StaticBitmapImage>);
+
+ void ReclaimResource(unsigned resource_id);
+ void ReclaimResources(const WTF::Vector<viz::ReturnedResource>& resources);
+ void IncNextResourceId() { next_resource_id_++; }
+ unsigned GetNextResourceId() { return next_resource_id_; }
+
+ void Reshape(int width, int height) {
+ width_ = width;
+ height_ = height;
+ }
+
+ private:
+ int width_;
+ int height_;
+ unsigned next_resource_id_;
+
+ struct FrameResource {
+ scoped_refptr<StaticBitmapImage> image_;
+ std::unique_ptr<viz::SharedBitmap> shared_bitmap_;
+
+ bool spare_lock_ = true;
+ gpu::Mailbox mailbox_;
+
+ FrameResource() = default;
+ ~FrameResource();
+ };
+
+ std::unique_ptr<FrameResource> recyclable_resource_;
+ std::unique_ptr<FrameResource> CreateOrRecycleFrameResource();
+
+ void SetNeedsBeginFrameInternal();
+
+ typedef HashMap<unsigned, std::unique_ptr<FrameResource>> ResourceMap;
+ void ReclaimResourceInternal(const ResourceMap::iterator&);
+ ResourceMap resources_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_OFFSCREEN_CANVAS_RESOURCE_PROVIDER_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/README.md b/chromium/third_party/blink/renderer/platform/graphics/paint/README.md
new file mode 100644
index 00000000000..f3033e12d8c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/README.md
@@ -0,0 +1,285 @@
+# Platform paint code
+
+This directory contains the implementation of display lists and display
+list-based painting, except for code which requires knowledge of `core/`
+concepts, such as DOM elements and layout objects.
+
+This code is owned by the [paint team][paint-team-site].
+
+Slimming Paint v2 is currently being implemented. Unlike Slimming Paint v1, SPv2
+represents its paint artifact not as a flat display list, but as a list of
+drawings, and a list of paint chunks, stored together.
+
+This document explains the SPv2 world as it develops, not the SPv1 world it
+replaces.
+
+[paint-team-site]: https://www.chromium.org/developers/paint-team
+
+## Paint artifact
+
+The SPv2 [paint artifact](PaintArtifact.h) consists of a list of display items
+in paint order (ideally mostly or all drawings), partitioned into *paint chunks*
+which define certain *paint properties* which affect how the content should be
+drawn or composited.
+
+## Paint properties
+
+Paint properties define characteristics of how a paint chunk should be drawn,
+such as the transform it should be drawn with. To enable efficient updates,
+a chunk's paint properties are described hierarchically. For instance, each
+chunk is associated with a transform node, whose matrix should be multiplied by
+its ancestor transform nodes in order to compute the final transformation matrix
+to the screen.
+
+*** note
+Support for all paint properties has yet to be implemented in SPv2.
+***
+
+*** aside
+TODO(jbroman): Explain the semantics of transforms, clips, scrolls and effects
+as support for them is added to SPv2.
+***
+
+### Transforms
+
+Each paint chunk is associated with a [transform node](TransformPaintPropertyNode.h),
+which defines the coordinate space in which the content should be painted.
+
+Each transform node has:
+
+* a 4x4 [`TransformationMatrix`](../../transforms/TransformationMatrix.h)
+* a 3-dimensional transform origin, which defines the origin relative to which
+ the transformation matrix should be applied (e.g. a rotation applied with some
+ transform origin will rotate the plane about that point)
+* a pointer to the parent node, which defines the coordinate space relative to
+ which the above should be interpreted
+* a boolean indicating whether the transform should be projected into the plane
+ of its parent (i.e., whether the total transform inherited from its parent
+ should be flattened before this node's transform is applied and propagated to
+ children)
+* an integer rendering context ID; content whose transform nodes share a
+ rendering context ID should sort together
+
+The parent node pointers link the transform nodes in a hierarchy (the *transform
+tree*), which defines how the transform for any painted content can be
+determined.
+
+***promo
+The painting system may create transform nodes which don't affect the position
+of points in the xy-plane, but which have an apparent effect only when
+multiplied with other transformation matrices. In particular, a transform node
+may be created to establish a perspective matrix for descendant transforms in
+order to create the illusion of depth.
+***
+
+Note that, even though CSS does not permit it in the DOM, the transform tree can
+have nodes whose children do not flatten their inherited transform and
+participate in no 3D rendering context. For example, not flattening is necessary
+to preserve the 3D character of the perspective transform, but this does not
+imply any 3D sorting.
+
+### Clips
+
+Each paint chunk is associated with a [clip node](ClipPaintPropertyNode.h),
+which defines the raster region that will be applied on the canvas when
+the chunk is rastered.
+
+Each clip node has:
+
+* A float rect with (optionally) rounded corner radius.
+* An associated transform node, which the clip rect is based on.
+
+The raster region defined by a node is the rounded rect transformed to the
+root space, intersects with the raster region defined by its parent clip node
+(if not root).
+
+### Scrolling
+
+Each paint chunk is associated with a [scroll node](ScrollPaintPropertyNode.h)
+which defines information about how a subtree scrolls so threads other than the
+main thread can perform scrolling. Scroll information includes:
+
+* Which directions, if any, are scrollable by the user.
+* A reference to a [transform node](TransformPaintPropertyNode.h) which contains
+a 2d scroll offset.
+* The extent that can be scrolled. For example, an overflow clip of size 7x9
+with scrolling contents of size 7x13 can scroll 4px vertically and none
+horizontally.
+
+To ensure geometry operations are simple and only deal with transforms, the
+scroll offset is stored as a 2d transform in the transform tree.
+
+### Effects
+
+Each paint chunk is associated with an [effect node](EffectPaintPropertyNode.h),
+which defines the effect (opacity, transfer mode, filter, mask, etc.) that
+should be applied to the content before or as it is composited into the content
+below.
+
+Each effect node has:
+
+* a floating-point opacity (from 0 to 1, inclusive)
+* a pointer to the parent node, which will be applied to the result of this
+ effect before or as it is composited into its parent in the effect tree
+
+The paret node pointers link the effect nodes in a hierarchy (the *effect
+tree*), which defines the dependencies between rasterization of different
+contents.
+
+One can imagine each effect node as corresponding roughly to a bitmap that is
+drawn before being composited into another bitmap, though for implementation
+reasons this may not be how it is actually implemented.
+
+*** aside
+TODO(jbroman): Explain the connection with the transform and clip trees, once it
+exists, as well as effects other than opacity.
+***
+
+## Display items
+
+A display item is the smallest unit of a display list in Blink. Each display
+item is identified by an ID consisting of:
+
+* an opaque pointer to the *display item client* that produced it
+* a type (from the `DisplayItem::Type` enum)
+
+In practice, display item clients are generally subclasses of `LayoutObject`,
+but can be other Blink objects which get painted, such as inline boxes and drag
+images.
+
+*** note
+It is illegal for there to be two drawings with the same ID in a display item
+list, except for drawings that are marked uncacheable
+(see [DisplayItemCacheSkipper](DisplayItemCacheSkipper.h)).
+***
+
+Generally, clients of this code should use stack-allocated recorder classes to
+emit display items to a `PaintController` (using `GraphicsContext`).
+
+### Standalone display items
+
+#### [DrawingDisplayItem](DrawingDisplayItem.h)
+
+Holds a `PaintRecord` which contains the paint operations required to draw some
+atom of content.
+
+#### [ForeignLayerDisplayItem](ForeignLayerDisplayItem.h)
+
+Draws an atom of content, but using a `cc::Layer` produced by some agent outside
+of the normal Blink paint system (for example, a plugin). Since they always map
+to a `cc::Layer`, they are always the only display item in their paint chunk,
+and are ineligible for squashing with other layers.
+
+#### [ScrollHitTestDisplayItem](ScrollHitTestDisplayItem.h)
+
+Placeholder for creating a cc::Layer for scrolling in paint order. Hit testing
+in the compositor requires both property trees (scroll nodes) and a scrollable
+`cc::layer` in paint order. This should be associated with the scroll
+translation paint property node as well as any overflow clip nodes.
+
+### Paired begin/end display items
+
+*** aside
+TODO(jbroman): Describe how these work, once we've worked out what happens to
+them in SPv2.
+***
+
+## Paint controller
+
+Callers use `GraphicsContext` (via its drawing methods, and its
+`paintController()` accessor) and scoped recorder classes, which emit items into
+a `PaintController`.
+
+`PaintController` is responsible for producing the paint artifact. It contains
+the *current* paint artifact, and *new* display items and paint chunks, which
+are added as content is painted.
+
+Painters should call `PaintController::useCachedDrawingIfPossible()` or
+`PaintController::useCachedSubsequenceIfPossible()` and if the function returns
+`true`, existing display items that are still valid in the *current* paint artifact
+will be reused and the painter should skip real painting of the drawing or subsequence.
+
+When the new display items have been populated, clients call
+`commitNewDisplayItems`, which replaces the previous artifact with the new data,
+producing a new paint artifact.
+
+At this point, the paint artifact is ready to be drawn or composited.
+
+### Paint result caching and invalidation
+
+See [Display item caching](../../../core/paint/README.md#paint-result-caching)
+and [Paint invalidation](../../../core/paint/README.md#paint-invalidation) for
+more details about how caching and invalidation are handled in blink core
+module using `PaintController` API.
+
+We use 'cache generation' which is a unique id of cache status in each
+`DisplayItemClient` and `PaintController` to determine if the client is validly
+cached by a `PaintController`.
+
+A paint controller sets its cache generation to
+`DisplayItemCacheGeneration::next()` at the end of each
+`commitNewDisplayItems()`, and updates the cache generation of each client with
+cached drawings by calling `DisplayItemClient::setDisplayItemsCached()`.
+A display item is treated as validly cached in a paint controller if its cache
+generation matches the paint controller's cache generation.
+
+A cache generation value smaller than `kFirstValidGeneration` matches no
+other cache generations thus is always treated as invalid. When a
+`DisplayItemClient` is invalidated, we set its cache generation to one of
+`PaintInvalidationReason` values which are smaller than `kFirstValidGeneration`.
+When a `PaintController` is cleared (e.g. when the corresponding `GraphicsLayer`
+is fully invalidated), we also invalidate its cache generation.
+
+For now we use a uint32_t variable to store cache generation. Assuming there is
+an animation in 60fps needing main-thread repaint, the cache generation will
+overflow after 2^32/86400/60 = 828 days. The time may be shorter if there are
+multiple animating `PaintController`s in the same process. When it overflows,
+we may treat some object that is not cached as cached if the following
+conditions are all met:
+* the object was painted when the cache generation was *n*;
+* the object has been neither painted nor invalidated since cache generation
+ *n*;
+* when the cache generation wraps back to exact *n*, the object happens to be
+ painted again.
+As the paint controller doesn't have cached display items for the object, there
+will be corrupted painting or assertion failure. The chance is too low to be
+concerned.
+
+SPv1 only: If a display item is painted on multiple paint controllers, because
+cache generations are unique, the client's cache generation matches the last
+paint controller only. The client will be treated as invalid on other paint
+controllers regardless if it's validly cached by these paint controllers.
+The situation is very rare (about 0.07% clients were painted on multiple paint
+controllers in a [Cluster Telemetry run](https://ct.skia.org/chromium_perf_runs)
+(run 803) so the performance penalty is trivial.
+
+## Paint artifact compositor
+
+The [`PaintArtifactCompositor`](PaintArtifactCompositor.h) is responsible for
+consuming the `PaintArtifact` produced by the `PaintController`, and converting
+it into a form suitable for the compositor to consume.
+
+At present, `PaintArtifactCompositor` creates a cc layer tree, with one layer
+for each paint chunk. In the future, it is expected that we will use heuristics
+to combine paint chunks into a smaller number of layers.
+
+The owner of the `PaintArtifactCompositor` (e.g. `WebView`) can then attach its
+root layer to the overall layer hierarchy to be displayed to the user.
+
+In the future we would like to explore moving to a single shared property tree
+representation across both cc and
+Blink. See [Web Page Geometries](https://goo.gl/MwVIto) for more.
+
+## Geometry routines
+
+The [`GeometryMapper`](GeometryMapper.h) is responsible for efficiently computing
+visual and transformed rects of display items in the coordinate space of ancestor
+[`PropertyTreeState`](PropertyTreeState.h)s.
+
+The transformed rect of a display item in an ancestor `PropertyTreeState` is
+that rect, multiplied by the transforms between the display item's
+`PropertyTreeState` and the ancestors, then flattened into 2D.
+
+The visual rect of a display item in an ancestor `PropertyTreeState` is the
+intersection of all of the intermediate clips (transformed in to the ancestor
+state), with the display item's transformed rect.
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/clip_display_item.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/clip_display_item.cc
new file mode 100644
index 00000000000..d3b15fecc71
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/clip_display_item.cc
@@ -0,0 +1,55 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/clip_display_item.h"
+
+#include "third_party/blink/public/platform/web_display_item_list.h"
+#include "third_party/blink/renderer/platform/geometry/float_rounded_rect.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/skia/include/core/SkScalar.h"
+
+namespace blink {
+
+void ClipDisplayItem::Replay(GraphicsContext& context) const {
+ context.Save();
+
+ // RoundedInnerRectClipper only cares about rounded-rect clips,
+ // and passes an "infinite" rect clip; there is no reason to apply this clip.
+ // TODO(fmalita): convert RoundedInnerRectClipper to a better suited
+ // DisplayItem so we don't have to special-case its semantics.
+ if (clip_rect_ != LayoutRect::InfiniteIntRect())
+ context.ClipRect(clip_rect_, kAntiAliased);
+
+ for (const FloatRoundedRect& rounded_rect : rounded_rect_clips_)
+ context.ClipRoundedRect(rounded_rect);
+}
+
+void ClipDisplayItem::AppendToWebDisplayItemList(
+ const FloatSize&,
+ WebDisplayItemList* list) const {
+ WebVector<SkRRect> web_rounded_rects(rounded_rect_clips_.size());
+ for (size_t i = 0; i < rounded_rect_clips_.size(); ++i)
+ web_rounded_rects[i] = rounded_rect_clips_[i];
+
+ list->AppendClipItem(clip_rect_, web_rounded_rects);
+}
+
+void EndClipDisplayItem::Replay(GraphicsContext& context) const {
+ context.Restore();
+}
+
+void EndClipDisplayItem::AppendToWebDisplayItemList(
+ const FloatSize&,
+ WebDisplayItemList* list) const {
+ list->AppendEndClipItem();
+}
+
+#if DCHECK_IS_ON()
+void ClipDisplayItem::PropertiesAsJSON(JSONObject& json) const {
+ DisplayItem::PropertiesAsJSON(json);
+ json.SetString("clipRect", clip_rect_.ToString());
+}
+#endif
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/clip_display_item.h b/chromium/third_party/blink/renderer/platform/graphics/paint/clip_display_item.h
new file mode 100644
index 00000000000..d63595f08cf
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/clip_display_item.h
@@ -0,0 +1,76 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_CLIP_DISPLAY_ITEM_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_CLIP_DISPLAY_ITEM_H_
+
+#include "SkRegion.h"
+#include "third_party/blink/renderer/platform/geometry/float_rounded_rect.h"
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/graphics/paint/display_item.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT ClipDisplayItem final : public PairedBeginDisplayItem {
+ public:
+ ClipDisplayItem(const DisplayItemClient& client,
+ Type type,
+ const IntRect& clip_rect)
+ : PairedBeginDisplayItem(client, type, sizeof(*this)),
+ clip_rect_(clip_rect) {
+ DCHECK(IsClipType(type));
+ }
+
+ ClipDisplayItem(const DisplayItemClient& client,
+ Type type,
+ const IntRect& clip_rect,
+ Vector<FloatRoundedRect>& rounded_rect_clips)
+ : ClipDisplayItem(client, type, clip_rect) {
+ rounded_rect_clips_.swap(rounded_rect_clips);
+ }
+
+ void Replay(GraphicsContext&) const override;
+ void AppendToWebDisplayItemList(const FloatSize&,
+ WebDisplayItemList*) const override;
+
+ private:
+#if DCHECK_IS_ON()
+ void PropertiesAsJSON(JSONObject&) const override;
+#endif
+ bool Equals(const DisplayItem& other) const final {
+ return DisplayItem::Equals(other) &&
+ clip_rect_ ==
+ static_cast<const ClipDisplayItem&>(other).clip_rect_ &&
+ rounded_rect_clips_ ==
+ static_cast<const ClipDisplayItem&>(other).rounded_rect_clips_;
+ }
+
+ const IntRect clip_rect_;
+ Vector<FloatRoundedRect> rounded_rect_clips_;
+};
+
+class PLATFORM_EXPORT EndClipDisplayItem final : public PairedEndDisplayItem {
+ public:
+ EndClipDisplayItem(const DisplayItemClient& client, Type type)
+ : PairedEndDisplayItem(client, type, sizeof(*this)) {
+ DCHECK(IsEndClipType(type));
+ }
+
+ void Replay(GraphicsContext&) const override;
+ void AppendToWebDisplayItemList(const FloatSize&,
+ WebDisplayItemList*) const override;
+
+ private:
+#if DCHECK_IS_ON()
+ bool IsEndAndPairedWith(DisplayItem::Type other_type) const final {
+ return DisplayItem::IsClipType(other_type);
+ }
+#endif
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_CLIP_DISPLAY_ITEM_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.cc
new file mode 100644
index 00000000000..8647c0c1c22
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.cc
@@ -0,0 +1,40 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.h"
+
+#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
+
+namespace blink {
+
+ClipPaintPropertyNode* ClipPaintPropertyNode::Root() {
+ DEFINE_STATIC_REF(ClipPaintPropertyNode, root,
+ (ClipPaintPropertyNode::Create(
+ nullptr, TransformPaintPropertyNode::Root(),
+ FloatRoundedRect(LayoutRect::InfiniteIntRect()))));
+ return root;
+}
+
+std::unique_ptr<JSONObject> ClipPaintPropertyNode::ToJSON() const {
+ auto json = JSONObject::Create();
+ if (Parent())
+ json->SetString("parent", String::Format("%p", Parent()));
+ json->SetString("localTransformSpace",
+ String::Format("%p", local_transform_space_.get()));
+ json->SetString("rect", clip_rect_.ToString());
+ if (clip_rect_excluding_overlay_scrollbars_ != clip_rect_) {
+ json->SetString("rectExcludingOverlayScrollbars",
+ clip_rect_excluding_overlay_scrollbars_.ToString());
+ }
+ if (clip_path_) {
+ json->SetBoolean("hasClipPath", true);
+ }
+ if (direct_compositing_reasons_ != CompositingReason::kNone) {
+ json->SetString("directCompositingReasons",
+ CompositingReason::ToString(direct_compositing_reasons_));
+ }
+ return json;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.h b/chromium/third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.h
new file mode 100644
index 00000000000..20a4799929e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.h
@@ -0,0 +1,154 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_CLIP_PAINT_PROPERTY_NODE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_CLIP_PAINT_PROPERTY_NODE_H_
+
+#include "third_party/blink/renderer/platform/geometry/float_rounded_rect.h"
+#include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper_clip_cache.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_property_node.h"
+#include "third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h"
+#include "third_party/blink/renderer/platform/graphics/path.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+#include <iosfwd>
+
+namespace blink {
+
+class GeometryMapperClipCache;
+
+// A clip rect created by a css property such as "overflow" or "clip".
+// Along with a reference to the transform space the clip rect is based on,
+// and a parent ClipPaintPropertyNode for inherited clips.
+//
+// The clip tree is rooted at a node with no parent. This root node should
+// not be modified.
+class PLATFORM_EXPORT ClipPaintPropertyNode
+ : public PaintPropertyNode<ClipPaintPropertyNode> {
+ public:
+ // This node is really a sentinel, and does not represent a real clip
+ // space.
+ static ClipPaintPropertyNode* Root();
+
+ static scoped_refptr<ClipPaintPropertyNode> Create(
+ scoped_refptr<const ClipPaintPropertyNode> parent,
+ scoped_refptr<const TransformPaintPropertyNode> local_transform_space,
+ const FloatRoundedRect& clip_rect,
+ const FloatRoundedRect* clip_rect_excluding_overlay_scrollbars = nullptr,
+ scoped_refptr<const RefCountedPath> clip_path = nullptr,
+ CompositingReasons direct_compositing_reasons =
+ CompositingReason::kNone) {
+ return base::AdoptRef(new ClipPaintPropertyNode(
+ std::move(parent), std::move(local_transform_space), clip_rect,
+ clip_rect_excluding_overlay_scrollbars
+ ? *clip_rect_excluding_overlay_scrollbars
+ : clip_rect,
+ std::move(clip_path), direct_compositing_reasons));
+ }
+
+ bool Update(
+ scoped_refptr<const ClipPaintPropertyNode> parent,
+ scoped_refptr<const TransformPaintPropertyNode> local_transform_space,
+ const FloatRoundedRect& clip_rect,
+ const FloatRoundedRect* clip_rect_excluding_overlay_scrollbars = nullptr,
+ scoped_refptr<const RefCountedPath> clip_path = nullptr) {
+ bool parent_changed = PaintPropertyNode::Update(std::move(parent));
+
+ if (local_transform_space == local_transform_space_ &&
+ clip_rect == clip_rect_ &&
+ (!clip_rect_excluding_overlay_scrollbars ||
+ *clip_rect_excluding_overlay_scrollbars ==
+ clip_rect_excluding_overlay_scrollbars_) &&
+ clip_path == clip_path_)
+ return parent_changed;
+
+ SetChanged();
+ local_transform_space_ = std::move(local_transform_space);
+ clip_rect_ = clip_rect;
+ clip_rect_excluding_overlay_scrollbars_ =
+ clip_rect_excluding_overlay_scrollbars
+ ? *clip_rect_excluding_overlay_scrollbars
+ : clip_rect;
+ clip_path_ = std::move(clip_path);
+ return true;
+ }
+
+ const TransformPaintPropertyNode* LocalTransformSpace() const {
+ return local_transform_space_.get();
+ }
+ const FloatRoundedRect& ClipRect() const { return clip_rect_; }
+ const FloatRoundedRect& ClipRectExcludingOverlayScrollbars() const {
+ return clip_rect_excluding_overlay_scrollbars_;
+ }
+
+ const RefCountedPath* ClipPath() const { return clip_path_.get(); }
+
+#if DCHECK_IS_ON()
+ // The clone function is used by FindPropertiesNeedingUpdate.h for recording
+ // a clip node before it has been updated, to later detect changes.
+ scoped_refptr<ClipPaintPropertyNode> Clone() const {
+ return base::AdoptRef(new ClipPaintPropertyNode(
+ Parent(), local_transform_space_, clip_rect_, clip_rect_, clip_path_,
+ direct_compositing_reasons_));
+ }
+
+ // The equality operator is used by FindPropertiesNeedingUpdate.h for checking
+ // if a clip node has changed.
+ bool operator==(const ClipPaintPropertyNode& o) const {
+ return Parent() == o.Parent() &&
+ local_transform_space_ == o.local_transform_space_ &&
+ clip_rect_ == o.clip_rect_ && clip_path_ == o.clip_path_ &&
+ direct_compositing_reasons_ == o.direct_compositing_reasons_;
+ }
+#endif
+
+ std::unique_ptr<JSONObject> ToJSON() const;
+
+ bool HasDirectCompositingReasons() const {
+ return direct_compositing_reasons_ != CompositingReason::kNone;
+ }
+
+ private:
+ ClipPaintPropertyNode(
+ scoped_refptr<const ClipPaintPropertyNode> parent,
+ scoped_refptr<const TransformPaintPropertyNode> local_transform_space,
+ const FloatRoundedRect& clip_rect,
+ const FloatRoundedRect& clip_rect_excluding_overlay_scrollbars,
+ scoped_refptr<const RefCountedPath> clip_path,
+ CompositingReasons direct_compositing_reasons)
+ : PaintPropertyNode(std::move(parent)),
+ local_transform_space_(std::move(local_transform_space)),
+ clip_rect_(clip_rect),
+ clip_rect_excluding_overlay_scrollbars_(
+ clip_rect_excluding_overlay_scrollbars),
+ clip_path_(clip_path),
+ direct_compositing_reasons_(direct_compositing_reasons) {}
+
+ // For access to GetClipCache();
+ friend class GeometryMapper;
+ friend class GeometryMapperTest;
+
+ GeometryMapperClipCache& GetClipCache() const {
+ return const_cast<ClipPaintPropertyNode*>(this)->GetClipCache();
+ }
+
+ GeometryMapperClipCache& GetClipCache() {
+ if (!geometry_mapper_clip_cache_)
+ geometry_mapper_clip_cache_.reset(new GeometryMapperClipCache());
+ return *geometry_mapper_clip_cache_.get();
+ }
+
+ scoped_refptr<const TransformPaintPropertyNode> local_transform_space_;
+ FloatRoundedRect clip_rect_;
+ FloatRoundedRect clip_rect_excluding_overlay_scrollbars_;
+ scoped_refptr<const RefCountedPath> clip_path_;
+ CompositingReasons direct_compositing_reasons_;
+
+ std::unique_ptr<GeometryMapperClipCache> geometry_mapper_clip_cache_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_CLIP_PAINT_PROPERTY_NODE_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/clip_path_display_item.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/clip_path_display_item.cc
new file mode 100644
index 00000000000..2cd4a1a2530
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/clip_path_display_item.cc
@@ -0,0 +1,48 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/clip_path_display_item.h"
+
+#include "third_party/blink/public/platform/web_display_item_list.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/path.h"
+#include "third_party/skia/include/core/SkScalar.h"
+
+namespace blink {
+
+void BeginClipPathDisplayItem::Replay(GraphicsContext& context) const {
+ context.Save();
+ context.ClipPath(clip_path_, kAntiAliased);
+}
+
+void BeginClipPathDisplayItem::AppendToWebDisplayItemList(
+ const FloatSize&,
+ WebDisplayItemList* list) const {
+ list->AppendClipPathItem(clip_path_, true);
+}
+
+void EndClipPathDisplayItem::Replay(GraphicsContext& context) const {
+ context.Restore();
+}
+
+void EndClipPathDisplayItem::AppendToWebDisplayItemList(
+ const FloatSize&,
+ WebDisplayItemList* list) const {
+ list->AppendEndClipPathItem();
+}
+
+#if DCHECK_IS_ON()
+void BeginClipPathDisplayItem::PropertiesAsJSON(JSONObject& json) const {
+ DisplayItem::PropertiesAsJSON(json);
+ json.SetInteger("pathVerbs", clip_path_.countVerbs());
+ json.SetInteger("pathPoints", clip_path_.countPoints());
+ json.SetString("windRule",
+ clip_path_.getFillType() == SkPath::kWinding_FillType
+ ? "nonzero"
+ : "evenodd");
+}
+
+#endif
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/clip_path_display_item.h b/chromium/third_party/blink/renderer/platform/graphics/paint/clip_path_display_item.h
new file mode 100644
index 00000000000..63f85f72ce6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/clip_path_display_item.h
@@ -0,0 +1,60 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_CLIP_PATH_DISPLAY_ITEM_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_CLIP_PATH_DISPLAY_ITEM_H_
+
+#include "third_party/blink/renderer/platform/graphics/paint/display_item.h"
+#include "third_party/blink/renderer/platform/graphics/path.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/skia/include/core/SkPath.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT BeginClipPathDisplayItem final
+ : public PairedBeginDisplayItem {
+ public:
+ BeginClipPathDisplayItem(const DisplayItemClient& client,
+ const Path& clip_path)
+ : PairedBeginDisplayItem(client, kBeginClipPath, sizeof(*this)),
+ clip_path_(clip_path.GetSkPath()) {}
+
+ void Replay(GraphicsContext&) const override;
+ void AppendToWebDisplayItemList(const FloatSize&,
+ WebDisplayItemList*) const override;
+
+ private:
+#if DCHECK_IS_ON()
+ void PropertiesAsJSON(JSONObject&) const override;
+#endif
+ bool Equals(const DisplayItem& other) const final {
+ return DisplayItem::Equals(other) &&
+ clip_path_ ==
+ static_cast<const BeginClipPathDisplayItem&>(other).clip_path_;
+ }
+
+ const SkPath clip_path_;
+};
+
+class PLATFORM_EXPORT EndClipPathDisplayItem final
+ : public PairedEndDisplayItem {
+ public:
+ EndClipPathDisplayItem(const DisplayItemClient& client)
+ : PairedEndDisplayItem(client, kEndClipPath, sizeof(*this)) {}
+
+ void Replay(GraphicsContext&) const override;
+ void AppendToWebDisplayItemList(const FloatSize&,
+ WebDisplayItemList*) const override;
+
+ private:
+#if DCHECK_IS_ON()
+ bool IsEndAndPairedWith(DisplayItem::Type other_type) const final {
+ return other_type == kBeginClipPath;
+ }
+#endif
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_CLIP_PATH_DISPLAY_ITEM_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/clip_path_recorder.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/clip_path_recorder.cc
new file mode 100644
index 00000000000..926a68aa003
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/clip_path_recorder.cc
@@ -0,0 +1,29 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/clip_path_recorder.h"
+
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/paint/clip_path_display_item.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
+
+namespace blink {
+
+ClipPathRecorder::ClipPathRecorder(GraphicsContext& context,
+ const DisplayItemClient& client,
+ const Path& clip_path)
+ : context_(context), client_(client) {
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled())
+ return;
+ context_.GetPaintController().CreateAndAppend<BeginClipPathDisplayItem>(
+ client_, clip_path);
+}
+
+ClipPathRecorder::~ClipPathRecorder() {
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled())
+ return;
+ context_.GetPaintController().EndItem<EndClipPathDisplayItem>(client_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/clip_path_recorder.h b/chromium/third_party/blink/renderer/platform/graphics/paint/clip_path_recorder.h
new file mode 100644
index 00000000000..97c7bc175b7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/clip_path_recorder.h
@@ -0,0 +1,32 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_CLIP_PATH_RECORDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_CLIP_PATH_RECORDER_H_
+
+#include "third_party/blink/renderer/platform/graphics/paint/display_item_client.h"
+#include "third_party/blink/renderer/platform/graphics/path.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class GraphicsContext;
+
+class PLATFORM_EXPORT ClipPathRecorder {
+ USING_FAST_MALLOC(ClipPathRecorder);
+ WTF_MAKE_NONCOPYABLE(ClipPathRecorder);
+
+ public:
+ ClipPathRecorder(GraphicsContext&, const DisplayItemClient&, const Path&);
+ ~ClipPathRecorder();
+
+ private:
+ GraphicsContext& context_;
+ const DisplayItemClient& client_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_CLIP_PATH_RECORDER_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/clip_recorder.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/clip_recorder.cc
new file mode 100644
index 00000000000..848fc7ecf47
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/clip_recorder.cc
@@ -0,0 +1,31 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/clip_recorder.h"
+
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/paint/clip_display_item.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
+
+namespace blink {
+
+ClipRecorder::ClipRecorder(GraphicsContext& context,
+ const DisplayItemClient& client,
+ DisplayItem::Type type,
+ const IntRect& clip_rect)
+ : client_(client), context_(context), type_(type) {
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled())
+ return;
+ context_.GetPaintController().CreateAndAppend<ClipDisplayItem>(client_, type,
+ clip_rect);
+}
+
+ClipRecorder::~ClipRecorder() {
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled())
+ return;
+ context_.GetPaintController().EndItem<EndClipDisplayItem>(
+ client_, DisplayItem::ClipTypeToEndClipType(type_));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/clip_recorder.h b/chromium/third_party/blink/renderer/platform/graphics/paint/clip_recorder.h
new file mode 100644
index 00000000000..a17c1d65b89
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/clip_recorder.h
@@ -0,0 +1,35 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_CLIP_RECORDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_CLIP_RECORDER_H_
+
+#include "third_party/blink/renderer/platform/graphics/paint/display_item.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class GraphicsContext;
+
+class PLATFORM_EXPORT ClipRecorder {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ WTF_MAKE_NONCOPYABLE(ClipRecorder);
+
+ public:
+ ClipRecorder(GraphicsContext&,
+ const DisplayItemClient&,
+ DisplayItem::Type,
+ const IntRect& clip_rect);
+ ~ClipRecorder();
+
+ private:
+ const DisplayItemClient& client_;
+ GraphicsContext& context_;
+ DisplayItem::Type type_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_CLIP_RECORDER_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/compositing_display_item.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/compositing_display_item.cc
new file mode 100644
index 00000000000..7c451e164e4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/compositing_display_item.cc
@@ -0,0 +1,48 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/compositing_display_item.h"
+
+#include "third_party/blink/public/platform/web_display_item_list.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
+
+namespace blink {
+
+void BeginCompositingDisplayItem::Replay(GraphicsContext& context) const {
+ context.BeginLayer(opacity_, xfer_mode_, has_bounds_ ? &bounds_ : nullptr,
+ color_filter_);
+}
+
+void BeginCompositingDisplayItem::AppendToWebDisplayItemList(
+ const FloatSize&,
+ WebDisplayItemList* list) const {
+ SkRect bounds = bounds_;
+ list->AppendCompositingItem(
+ opacity_, xfer_mode_, has_bounds_ ? &bounds : nullptr,
+ GraphicsContext::WebCoreColorFilterToSkiaColorFilter(color_filter_)
+ .get());
+}
+
+#if DCHECK_IS_ON()
+void BeginCompositingDisplayItem::PropertiesAsJSON(JSONObject& json) const {
+ DisplayItem::PropertiesAsJSON(json);
+ json.SetInteger("xferMode", static_cast<int>(xfer_mode_));
+ json.SetDouble("opacity", opacity_);
+ if (has_bounds_)
+ json.SetString("bounds", bounds_.ToString());
+}
+#endif
+
+void EndCompositingDisplayItem::Replay(GraphicsContext& context) const {
+ context.EndLayer();
+}
+
+void EndCompositingDisplayItem::AppendToWebDisplayItemList(
+ const FloatSize&,
+ WebDisplayItemList* list) const {
+ list->AppendEndCompositingItem();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/compositing_display_item.h b/chromium/third_party/blink/renderer/platform/graphics/paint/compositing_display_item.h
new file mode 100644
index 00000000000..ba7ca463fa9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/compositing_display_item.h
@@ -0,0 +1,82 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_COMPOSITING_DISPLAY_ITEM_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_COMPOSITING_DISPLAY_ITEM_H_
+
+#include "third_party/blink/public/platform/web_blend_mode.h"
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_types.h"
+#include "third_party/blink/renderer/platform/graphics/paint/display_item.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT BeginCompositingDisplayItem final
+ : public PairedBeginDisplayItem {
+ public:
+ BeginCompositingDisplayItem(const DisplayItemClient& client,
+ const SkBlendMode xfer_mode,
+ const float opacity,
+ const FloatRect* bounds,
+ ColorFilter color_filter = kColorFilterNone)
+ : PairedBeginDisplayItem(client, kBeginCompositing, sizeof(*this)),
+ xfer_mode_(xfer_mode),
+ opacity_(opacity),
+ has_bounds_(bounds),
+ color_filter_(color_filter) {
+ if (bounds)
+ bounds_ = FloatRect(*bounds);
+ }
+
+ void Replay(GraphicsContext&) const override;
+ void AppendToWebDisplayItemList(const FloatSize&,
+ WebDisplayItemList*) const override;
+
+ private:
+#if DCHECK_IS_ON()
+ void PropertiesAsJSON(JSONObject&) const override;
+#endif
+ bool Equals(const DisplayItem& other) const final {
+ return DisplayItem::Equals(other) &&
+ xfer_mode_ == static_cast<const BeginCompositingDisplayItem&>(other)
+ .xfer_mode_ &&
+ opacity_ == static_cast<const BeginCompositingDisplayItem&>(other)
+ .opacity_ &&
+ has_bounds_ == static_cast<const BeginCompositingDisplayItem&>(other)
+ .has_bounds_ &&
+ bounds_ ==
+ static_cast<const BeginCompositingDisplayItem&>(other).bounds_ &&
+ color_filter_ ==
+ static_cast<const BeginCompositingDisplayItem&>(other)
+ .color_filter_;
+ }
+
+ const SkBlendMode xfer_mode_;
+ const float opacity_;
+ bool has_bounds_;
+ FloatRect bounds_;
+ ColorFilter color_filter_;
+};
+
+class PLATFORM_EXPORT EndCompositingDisplayItem final
+ : public PairedEndDisplayItem {
+ public:
+ EndCompositingDisplayItem(const DisplayItemClient& client)
+ : PairedEndDisplayItem(client, kEndCompositing, sizeof(*this)) {}
+
+ void Replay(GraphicsContext&) const override;
+ void AppendToWebDisplayItemList(const FloatSize&,
+ WebDisplayItemList*) const override;
+
+ private:
+#if DCHECK_IS_ON()
+ bool IsEndAndPairedWith(DisplayItem::Type other_type) const final {
+ return other_type == kBeginCompositing;
+ }
+#endif
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_COMPOSITING_DISPLAY_ITEM_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/compositing_recorder.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/compositing_recorder.cc
new file mode 100644
index 00000000000..7ea95979dca
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/compositing_recorder.cc
@@ -0,0 +1,36 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/compositing_recorder.h"
+
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
+#include "third_party/blink/renderer/platform/graphics/paint/compositing_display_item.h"
+#include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
+
+namespace blink {
+
+CompositingRecorder::CompositingRecorder(GraphicsContext& graphics_context,
+ const DisplayItemClient& client,
+ const SkBlendMode xfer_mode,
+ const float opacity,
+ const FloatRect* bounds,
+ ColorFilter color_filter)
+ : client_(client), graphics_context_(graphics_context) {
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled())
+ return;
+ graphics_context.GetPaintController()
+ .CreateAndAppend<BeginCompositingDisplayItem>(client_, xfer_mode, opacity,
+ bounds, color_filter);
+}
+
+CompositingRecorder::~CompositingRecorder() {
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled())
+ return;
+ graphics_context_.GetPaintController().EndItem<EndCompositingDisplayItem>(
+ client_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/compositing_recorder.h b/chromium/third_party/blink/renderer/platform/graphics/paint/compositing_recorder.h
new file mode 100644
index 00000000000..33895697af1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/compositing_recorder.h
@@ -0,0 +1,41 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_COMPOSITING_RECORDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_COMPOSITING_RECORDER_H_
+
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_types.h"
+#include "third_party/blink/renderer/platform/graphics/paint/display_item.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/skia/include/core/SkBlendMode.h"
+
+namespace blink {
+
+class GraphicsContext;
+
+class PLATFORM_EXPORT CompositingRecorder {
+ USING_FAST_MALLOC(CompositingRecorder);
+
+ public:
+ // If bounds is provided, the content will be explicitly clipped to those
+ // bounds.
+ CompositingRecorder(GraphicsContext&,
+ const DisplayItemClient&,
+ const SkBlendMode,
+ const float opacity,
+ const FloatRect* bounds = nullptr,
+ ColorFilter = kColorFilterNone);
+
+ ~CompositingRecorder();
+
+ private:
+ const DisplayItemClient& client_;
+ GraphicsContext& graphics_context_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_COMPOSITING_RECORDER_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/cull_rect.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/cull_rect.cc
new file mode 100644
index 00000000000..325f8c5af37
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/cull_rect.cc
@@ -0,0 +1,63 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/cull_rect.h"
+
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+
+namespace blink {
+
+CullRect::CullRect(const CullRect& cull_rect, const IntPoint& offset) {
+ rect_ = cull_rect.rect_;
+ rect_.MoveBy(offset);
+}
+
+CullRect::CullRect(const CullRect& cull_rect, const IntSize& offset) {
+ rect_ = cull_rect.rect_;
+ rect_.Move(offset);
+}
+
+bool CullRect::IntersectsCullRect(const IntRect& bounding_box) const {
+ return bounding_box.Intersects(rect_);
+}
+
+bool CullRect::IntersectsCullRect(const AffineTransform& transform,
+ const FloatRect& bounding_box) const {
+ return transform.MapRect(bounding_box).Intersects(rect_);
+}
+
+bool CullRect::IntersectsCullRect(const LayoutRect& rect_arg) const {
+ return rect_.Intersects(EnclosingIntRect(rect_arg));
+}
+
+bool CullRect::IntersectsHorizontalRange(LayoutUnit lo, LayoutUnit hi) const {
+ return !(lo >= rect_.MaxX() || hi <= rect_.X());
+}
+
+bool CullRect::IntersectsVerticalRange(LayoutUnit lo, LayoutUnit hi) const {
+ return !(lo >= rect_.MaxY() || hi <= rect_.Y());
+}
+
+void CullRect::UpdateCullRect(
+ const AffineTransform& local_to_parent_transform) {
+ if (rect_ != LayoutRect::InfiniteIntRect())
+ rect_ = local_to_parent_transform.Inverse().MapRect(rect_);
+}
+
+void CullRect::UpdateForScrollingContents(
+ const IntRect& overflow_clip_rect,
+ const AffineTransform& local_to_parent_transform) {
+ DCHECK(RuntimeEnabledFeatures::SlimmingPaintV2Enabled());
+ rect_.Intersect(overflow_clip_rect);
+ UpdateCullRect(local_to_parent_transform);
+
+ // TODO(wangxianzhu, chrishtr): How about non-composited scrolling contents?
+ // The distance to expand the cull rect for scrolling contents.
+ static const int kPixelDistanceToExpand = 4000;
+ rect_.Inflate(kPixelDistanceToExpand);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/cull_rect.h b/chromium/third_party/blink/renderer/platform/graphics/paint/cull_rect.h
new file mode 100644
index 00000000000..28967a81b74
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/cull_rect.h
@@ -0,0 +1,75 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_CULL_RECT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_CULL_RECT_H_
+
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/transforms/affine_transform.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+#include <limits>
+
+namespace blink {
+
+class FloatRect;
+class LayoutRect;
+class LayoutUnit;
+
+class PLATFORM_EXPORT CullRect {
+ DISALLOW_NEW();
+
+ public:
+ CullRect() = default;
+ explicit CullRect(const IntRect& rect) : rect_(rect) {}
+ CullRect(const CullRect&, const IntPoint& offset);
+ CullRect(const CullRect&, const IntSize& offset);
+
+ bool IntersectsCullRect(const AffineTransform&,
+ const FloatRect& bounding_box) const;
+ bool IntersectsCullRect(const IntRect&) const;
+ bool IntersectsCullRect(const LayoutRect&) const;
+ bool IntersectsHorizontalRange(LayoutUnit lo, LayoutUnit hi) const;
+ bool IntersectsVerticalRange(LayoutUnit lo, LayoutUnit hi) const;
+
+ void UpdateCullRect(const AffineTransform& local_to_parent_transform);
+
+ // |overflow_clip_rect| should be in the same coordinate space as |rect_|.
+ void UpdateForScrollingContents(
+ const IntRect& overflow_clip_rect,
+ const AffineTransform& local_to_parent_transform);
+
+ String ToString() const { return rect_.ToString(); }
+
+ private:
+ IntRect rect_;
+
+ friend bool operator==(const CullRect&, const CullRect&);
+
+ friend class CullRectTest;
+
+ // TODO(chrishtr): temporary while we implement CullRect everywhere.
+ friend class FramePainter;
+ friend class GridPainter;
+ friend class SVGInlineTextBoxPainter;
+ friend class SVGPaintContext;
+ friend class SVGRootInlineBoxPainter;
+ friend class SVGShapePainter;
+ friend class TableRowPainter;
+ friend class TableSectionPainter;
+ friend class ThemePainterMac;
+ friend class WebPluginContainerImpl;
+};
+
+inline bool operator==(const CullRect& a, const CullRect& b) {
+ return a.rect_ == b.rect_;
+}
+inline bool operator!=(const CullRect& a, const CullRect& b) {
+ return !(a == b);
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_CULL_RECT_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/cull_rect_test.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/cull_rect_test.cc
new file mode 100644
index 00000000000..c543a274bbc
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/cull_rect_test.cc
@@ -0,0 +1,82 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/cull_rect.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+
+namespace blink {
+
+class CullRectTest : public testing::Test {
+ protected:
+ IntRect Rect(const CullRect& cull_rect) { return cull_rect.rect_; }
+};
+
+TEST_F(CullRectTest, IntersectsCullRect) {
+ CullRect cull_rect(IntRect(0, 0, 50, 50));
+
+ EXPECT_TRUE(cull_rect.IntersectsCullRect(IntRect(0, 0, 1, 1)));
+ EXPECT_FALSE(cull_rect.IntersectsCullRect(IntRect(51, 51, 1, 1)));
+}
+
+TEST_F(CullRectTest, IntersectsCullRectWithLayoutRect) {
+ CullRect cull_rect(IntRect(0, 0, 50, 50));
+
+ EXPECT_TRUE(cull_rect.IntersectsCullRect(LayoutRect(0, 0, 1, 1)));
+ EXPECT_TRUE(cull_rect.IntersectsCullRect(LayoutRect(
+ LayoutUnit(0.1), LayoutUnit(0.1), LayoutUnit(0.1), LayoutUnit(0.1))));
+}
+
+TEST_F(CullRectTest, IntersectsCullRectWithTransform) {
+ CullRect cull_rect(IntRect(0, 0, 50, 50));
+ AffineTransform transform;
+ transform.Translate(-2, -2);
+
+ EXPECT_TRUE(cull_rect.IntersectsCullRect(transform, IntRect(51, 51, 1, 1)));
+ EXPECT_FALSE(cull_rect.IntersectsCullRect(IntRect(52, 52, 1, 1)));
+}
+
+TEST_F(CullRectTest, UpdateCullRect) {
+ CullRect cull_rect(IntRect(1, 1, 50, 50));
+ AffineTransform transform;
+ transform.Translate(1, 1);
+ cull_rect.UpdateCullRect(transform);
+
+ EXPECT_EQ(IntRect(0, 0, 50, 50), Rect(cull_rect));
+}
+
+TEST_F(CullRectTest, IntersectsVerticalRange) {
+ CullRect cull_rect(IntRect(0, 0, 50, 100));
+
+ EXPECT_TRUE(cull_rect.IntersectsVerticalRange(LayoutUnit(), LayoutUnit(1)));
+ EXPECT_FALSE(
+ cull_rect.IntersectsVerticalRange(LayoutUnit(100), LayoutUnit(101)));
+}
+
+TEST_F(CullRectTest, IntersectsHorizontalRange) {
+ CullRect cull_rect(IntRect(0, 0, 50, 100));
+
+ EXPECT_TRUE(cull_rect.IntersectsHorizontalRange(LayoutUnit(), LayoutUnit(1)));
+ EXPECT_FALSE(
+ cull_rect.IntersectsHorizontalRange(LayoutUnit(50), LayoutUnit(51)));
+}
+
+TEST_F(CullRectTest, UpdateForScrollingContents) {
+ ScopedSlimmingPaintV2ForTest spv2(true);
+
+ CullRect cull_rect(IntRect(0, 0, 50, 100));
+ AffineTransform transform;
+ transform.Translate(10, 15);
+ cull_rect.UpdateForScrollingContents(IntRect(20, 10, 40, 50), transform);
+
+ // Clipped: (20, 10, 30, 50)
+ // Expanded: (-3980, -3990, 8030, 8050)
+ // Inverse transformed: (-3990, -4005, 8030, 8050)
+ EXPECT_EQ(IntRect(-3990, -4005, 8030, 8050), Rect(cull_rect));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/display_item.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/display_item.cc
new file mode 100644
index 00000000000..6cc1a24450c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/display_item.cc
@@ -0,0 +1,252 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/display_item.h"
+
+namespace blink {
+
+struct SameSizeAsDisplayItem {
+ virtual ~SameSizeAsDisplayItem() = default; // Allocate vtable pointer.
+ void* pointer;
+ LayoutRect rect;
+ LayoutUnit outset;
+ int i;
+};
+static_assert(sizeof(DisplayItem) == sizeof(SameSizeAsDisplayItem),
+ "DisplayItem should stay small");
+
+#if DCHECK_IS_ON()
+
+static WTF::String PaintPhaseAsDebugString(int paint_phase) {
+ // Must be kept in sync with PaintPhase.
+ switch (paint_phase) {
+ case 0:
+ return "PaintPhaseBlockBackground";
+ case 1:
+ return "PaintPhaseSelfBlockBackground";
+ case 2:
+ return "PaintPhaseChildBlockBackgrounds";
+ case 3:
+ return "PaintPhaseFloat";
+ case 4:
+ return "PaintPhaseForeground";
+ case 5:
+ return "PaintPhaseOutline";
+ case 6:
+ return "PaintPhaseSelfOutline";
+ case 7:
+ return "PaintPhaseChildOutlines";
+ case 8:
+ return "PaintPhaseSelection";
+ case 9:
+ return "PaintPhaseTextClip";
+ case 10:
+ return "PaintPhaseMask";
+ case DisplayItem::kPaintPhaseMax:
+ return "PaintPhaseClippingMask";
+ default:
+ NOTREACHED();
+ return "Unknown";
+ }
+}
+
+#define PAINT_PHASE_BASED_DEBUG_STRINGS(Category) \
+ if (type >= DisplayItem::k##Category##PaintPhaseFirst && \
+ type <= DisplayItem::k##Category##PaintPhaseLast) \
+ return #Category + PaintPhaseAsDebugString( \
+ type - DisplayItem::k##Category##PaintPhaseFirst);
+
+#define DEBUG_STRING_CASE(DisplayItemName) \
+ case DisplayItem::k##DisplayItemName: \
+ return #DisplayItemName
+
+#define DEFAULT_CASE \
+ default: \
+ NOTREACHED(); \
+ return "Unknown"
+
+static WTF::String SpecialDrawingTypeAsDebugString(DisplayItem::Type type) {
+ switch (type) {
+ DEBUG_STRING_CASE(BoxDecorationBackground);
+ DEBUG_STRING_CASE(Caret);
+ DEBUG_STRING_CASE(ColumnRules);
+ DEBUG_STRING_CASE(DebugDrawing);
+ DEBUG_STRING_CASE(DocumentBackground);
+ DEBUG_STRING_CASE(DragImage);
+ DEBUG_STRING_CASE(DragCaret);
+ DEBUG_STRING_CASE(EmptyContentForFilters);
+ DEBUG_STRING_CASE(SVGImage);
+ DEBUG_STRING_CASE(LinkHighlight);
+ DEBUG_STRING_CASE(ImageAreaFocusRing);
+ DEBUG_STRING_CASE(PageOverlay);
+ DEBUG_STRING_CASE(PopupContainerBorder);
+ DEBUG_STRING_CASE(PopupListBoxBackground);
+ DEBUG_STRING_CASE(PopupListBoxRow);
+ DEBUG_STRING_CASE(PrintedContentDestinationLocations);
+ DEBUG_STRING_CASE(PrintedContentPDFURLRect);
+ DEBUG_STRING_CASE(Resizer);
+ DEBUG_STRING_CASE(SVGClip);
+ DEBUG_STRING_CASE(SVGFilter);
+ DEBUG_STRING_CASE(SVGMask);
+ DEBUG_STRING_CASE(ScrollbarBackButtonEnd);
+ DEBUG_STRING_CASE(ScrollbarBackButtonStart);
+ DEBUG_STRING_CASE(ScrollbarBackground);
+ DEBUG_STRING_CASE(ScrollbarBackTrack);
+ DEBUG_STRING_CASE(ScrollbarCorner);
+ DEBUG_STRING_CASE(ScrollbarForwardButtonEnd);
+ DEBUG_STRING_CASE(ScrollbarForwardButtonStart);
+ DEBUG_STRING_CASE(ScrollbarForwardTrack);
+ DEBUG_STRING_CASE(ScrollbarThumb);
+ DEBUG_STRING_CASE(ScrollbarTickmarks);
+ DEBUG_STRING_CASE(ScrollbarTrackBackground);
+ DEBUG_STRING_CASE(ScrollbarCompositedScrollbar);
+ DEBUG_STRING_CASE(SelectionTint);
+ DEBUG_STRING_CASE(TableCollapsedBorders);
+ DEBUG_STRING_CASE(VideoBitmap);
+ DEBUG_STRING_CASE(WebPlugin);
+ DEBUG_STRING_CASE(WebFont);
+ DEBUG_STRING_CASE(ReflectionMask);
+
+ DEFAULT_CASE;
+ }
+}
+
+static WTF::String DrawingTypeAsDebugString(DisplayItem::Type type) {
+ PAINT_PHASE_BASED_DEBUG_STRINGS(Drawing);
+ return "Drawing" + SpecialDrawingTypeAsDebugString(type);
+}
+
+static String ForeignLayerTypeAsDebugString(DisplayItem::Type type) {
+ switch (type) {
+ DEBUG_STRING_CASE(ForeignLayerCanvas);
+ DEBUG_STRING_CASE(ForeignLayerPlugin);
+ DEBUG_STRING_CASE(ForeignLayerVideo);
+ DEFAULT_CASE;
+ }
+}
+
+static String ScrollHitTestTypeAsDebugString(DisplayItem::Type type) {
+ switch (type) {
+ DEBUG_STRING_CASE(ScrollHitTest);
+ DEFAULT_CASE;
+ }
+}
+
+static WTF::String ClipTypeAsDebugString(DisplayItem::Type type) {
+ PAINT_PHASE_BASED_DEBUG_STRINGS(ClipBox);
+ PAINT_PHASE_BASED_DEBUG_STRINGS(ClipColumnBounds);
+ PAINT_PHASE_BASED_DEBUG_STRINGS(ClipLayerFragment);
+
+ switch (type) {
+ DEBUG_STRING_CASE(ClipFileUploadControlRect);
+ DEBUG_STRING_CASE(ClipFrameToVisibleContentRect);
+ DEBUG_STRING_CASE(ClipFrameScrollbars);
+ DEBUG_STRING_CASE(ClipLayerBackground);
+ DEBUG_STRING_CASE(ClipLayerColumnBounds);
+ DEBUG_STRING_CASE(ClipLayerFilter);
+ DEBUG_STRING_CASE(ClipLayerForeground);
+ DEBUG_STRING_CASE(ClipLayerParent);
+ DEBUG_STRING_CASE(ClipLayerOverflowControls);
+ DEBUG_STRING_CASE(ClipPopupListBoxFrame);
+ DEBUG_STRING_CASE(ClipScrollbarsToBoxBounds);
+ DEBUG_STRING_CASE(ClipSelectionImage);
+ DEFAULT_CASE;
+ }
+}
+
+static String ScrollTypeAsDebugString(DisplayItem::Type type) {
+ PAINT_PHASE_BASED_DEBUG_STRINGS(Scroll);
+ switch (type) {
+ DEBUG_STRING_CASE(ScrollOverflowControls);
+ DEFAULT_CASE;
+ }
+}
+
+static String Transform3DTypeAsDebugString(DisplayItem::Type type) {
+ switch (type) {
+ DEBUG_STRING_CASE(Transform3DElementTransform);
+ DEFAULT_CASE;
+ }
+}
+
+WTF::String DisplayItem::TypeAsDebugString(Type type) {
+ if (IsDrawingType(type))
+ return DrawingTypeAsDebugString(type);
+
+ if (IsForeignLayerType(type))
+ return ForeignLayerTypeAsDebugString(type);
+
+ if (IsClipType(type))
+ return ClipTypeAsDebugString(type);
+ if (IsEndClipType(type))
+ return "End" + ClipTypeAsDebugString(endClipTypeToClipType(type));
+
+ PAINT_PHASE_BASED_DEBUG_STRINGS(FloatClip);
+ if (type == kFloatClipClipPathBounds)
+ return "FloatClipClipPathBounds";
+ if (IsEndFloatClipType(type))
+ return "End" + TypeAsDebugString(endFloatClipTypeToFloatClipType(type));
+
+ if (IsScrollType(type))
+ return ScrollTypeAsDebugString(type);
+ if (IsEndScrollType(type))
+ return "End" + ScrollTypeAsDebugString(endScrollTypeToScrollType(type));
+
+ PAINT_PHASE_BASED_DEBUG_STRINGS(SVGTransform);
+ PAINT_PHASE_BASED_DEBUG_STRINGS(SVGEffect);
+
+ if (IsTransform3DType(type))
+ return Transform3DTypeAsDebugString(type);
+ if (IsEndTransform3DType(type))
+ return "End" + Transform3DTypeAsDebugString(
+ endTransform3DTypeToTransform3DType(type));
+
+ if (IsScrollHitTestType(type))
+ return ScrollHitTestTypeAsDebugString(type);
+
+ switch (type) {
+ DEBUG_STRING_CASE(BeginFilter);
+ DEBUG_STRING_CASE(EndFilter);
+ DEBUG_STRING_CASE(BeginCompositing);
+ DEBUG_STRING_CASE(EndCompositing);
+ DEBUG_STRING_CASE(BeginTransform);
+ DEBUG_STRING_CASE(EndTransform);
+ DEBUG_STRING_CASE(BeginClipPath);
+ DEBUG_STRING_CASE(EndClipPath);
+ DEBUG_STRING_CASE(UninitializedType);
+ DEFAULT_CASE;
+ }
+}
+
+WTF::String DisplayItem::AsDebugString() const {
+ auto json = JSONObject::Create();
+ PropertiesAsJSON(*json);
+ return json->ToPrettyJSONString();
+}
+
+void DisplayItem::PropertiesAsJSON(JSONObject& json) const {
+ if (IsTombstone())
+ json.SetBoolean("ISTOMBSTONE", true);
+
+ json.SetString("id", GetId().ToString());
+ json.SetString("visualRect", VisualRect().ToString());
+ if (OutsetForRasterEffects())
+ json.SetDouble("outset", OutsetForRasterEffects());
+ if (skipped_cache_)
+ json.SetBoolean("skippedCache", true);
+}
+
+#endif
+
+String DisplayItem::Id::ToString() const {
+#if DCHECK_IS_ON()
+ return String::Format("%p:%s:%d", &client,
+ DisplayItem::TypeAsDebugString(type).Ascii().data(),
+ fragment);
+#else
+ return String::Format("%p:%d:%d", &client, static_cast<int>(type), fragment);
+#endif
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/display_item.h b/chromium/third_party/blink/renderer/platform/graphics/paint/display_item.h
new file mode 100644
index 00000000000..b56dfee069c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/display_item.h
@@ -0,0 +1,436 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_DISPLAY_ITEM_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_DISPLAY_ITEM_H_
+
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/graphics/contiguous_container.h"
+#include "third_party/blink/renderer/platform/graphics/paint/display_item_client.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+#if DCHECK_IS_ON()
+#include "third_party/blink/renderer/platform/json/json_values.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#endif
+
+namespace blink {
+
+class GraphicsContext;
+class FloatSize;
+enum class PaintPhase;
+class WebDisplayItemList;
+
+class PLATFORM_EXPORT DisplayItem {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ enum {
+ // Must be kept in sync with core/paint/PaintPhase.h.
+ kPaintPhaseMax = 11,
+ };
+
+ // A display item type uniquely identifies a display item of a client.
+ // Some display item types can be categorized using the following directives:
+ // - In enum Type:
+ // - enum value <Category>First;
+ // - enum values of the category, first of which should equal
+ // <Category>First (for ease of maintenance, the values should be in
+ // alphabetic order);
+ // - enum value <Category>Last which should be equal to the last of the enum
+ // values of the category
+ // - DEFINE_CATEGORY_METHODS(<Category>) to define is<Category>Type(Type) and
+ // is<Category>() methods.
+ //
+ // A category or subset of a category can contain types each of which
+ // corresponds to a PaintPhase:
+ // - In enum Type:
+ // - enum value <Category>[<Subset>]PaintPhaseFirst;
+ // - enum value <Category>[<Subset>]PaintPhaseLast =
+ // <Category>[<Subset>]PaintPhaseFirst + PaintPhaseMax;
+ // - DEFINE_PAINT_PHASE_CONVERSION_METHOD(<Category>[<Subset>]) to define
+ // paintPhaseTo<Category>[<Subset>]Type(PaintPhase) method.
+ //
+ // A category can be derived from another category, containing types each of
+ // which corresponds to a value of the latter category:
+ // - In enum Type:
+ // - enum value <Category>First;
+ // - enum value <Category>Last =
+ // <Category>First + <BaseCategory>Last - <BaseCategory>First;
+ // - DEFINE_CONVERSION_METHODS(<Category>,
+ // <category>,
+ // <BaseCategory>,
+ // <baseCategory>)
+ // to define methods to convert types between the categories.
+ enum Type {
+ kDrawingFirst,
+ kDrawingPaintPhaseFirst = kDrawingFirst,
+ kDrawingPaintPhaseLast = kDrawingFirst + kPaintPhaseMax,
+ kBoxDecorationBackground,
+ kCaret,
+ kColumnRules,
+ kDebugDrawing,
+ kDocumentBackground,
+ kDragImage,
+ kDragCaret,
+ kEmptyContentForFilters,
+ kSVGImage,
+ kLinkHighlight,
+ kImageAreaFocusRing,
+ kPageOverlay,
+ kPopupContainerBorder,
+ kPopupListBoxBackground,
+ kPopupListBoxRow,
+ kPrintedContentDestinationLocations,
+ kPrintedContentPDFURLRect,
+ kResizer,
+ kSVGClip,
+ kSVGFilter,
+ kSVGMask,
+ kScrollbarBackButtonEnd,
+ kScrollbarBackButtonStart,
+ kScrollbarBackground,
+ kScrollbarBackTrack,
+ kScrollbarCorner,
+ kScrollbarForwardButtonEnd,
+ kScrollbarForwardButtonStart,
+ kScrollbarForwardTrack,
+ kScrollbarThumb,
+ kScrollbarTickmarks,
+ kScrollbarTrackBackground,
+ kScrollbarCompositedScrollbar,
+ kSelectionTint,
+ kTableCollapsedBorders,
+ kVideoBitmap,
+ kWebPlugin,
+ kWebFont,
+ kReflectionMask,
+ kDrawingLast = kReflectionMask,
+
+ kForeignLayerFirst,
+ kForeignLayerCanvas = kForeignLayerFirst,
+ kForeignLayerPlugin,
+ kForeignLayerVideo,
+ kForeignLayerLast = kForeignLayerVideo,
+
+ kClipFirst,
+ kClipBoxPaintPhaseFirst = kClipFirst,
+ kClipBoxPaintPhaseLast = kClipBoxPaintPhaseFirst + kPaintPhaseMax,
+ kClipColumnBoundsPaintPhaseFirst,
+ kClipColumnBoundsPaintPhaseLast =
+ kClipColumnBoundsPaintPhaseFirst + kPaintPhaseMax,
+ kClipLayerFragmentPaintPhaseFirst,
+ kClipLayerFragmentPaintPhaseLast =
+ kClipLayerFragmentPaintPhaseFirst + kPaintPhaseMax,
+ kClipFileUploadControlRect,
+ kClipFrameToVisibleContentRect,
+ kClipFrameScrollbars,
+ kClipLayerBackground,
+ kClipLayerColumnBounds,
+ kClipLayerFilter,
+ kClipLayerForeground,
+ kClipLayerParent,
+ kClipLayerOverflowControls,
+ kClipPopupListBoxFrame,
+ kClipScrollbarsToBoxBounds,
+ kClipSelectionImage,
+ kClipLast = kClipSelectionImage,
+
+ kEndClipFirst,
+ kEndClipLast = kEndClipFirst + kClipLast - kClipFirst,
+
+ kFloatClipFirst,
+ kFloatClipPaintPhaseFirst = kFloatClipFirst,
+ kFloatClipPaintPhaseLast = kFloatClipFirst + kPaintPhaseMax,
+ kFloatClipClipPathBounds,
+ kFloatClipLast = kFloatClipClipPathBounds,
+ kEndFloatClipFirst,
+ kEndFloatClipLast = kEndFloatClipFirst + kFloatClipLast - kFloatClipFirst,
+
+ kScrollFirst,
+ kScrollPaintPhaseFirst = kScrollFirst,
+ kScrollPaintPhaseLast = kScrollPaintPhaseFirst + kPaintPhaseMax,
+ kScrollOverflowControls,
+ kScrollLast = kScrollOverflowControls,
+ kEndScrollFirst,
+ kEndScrollLast = kEndScrollFirst + kScrollLast - kScrollFirst,
+
+ kSVGTransformPaintPhaseFirst,
+ kSVGTransformPaintPhaseLast = kSVGTransformPaintPhaseFirst + kPaintPhaseMax,
+
+ kSVGEffectPaintPhaseFirst,
+ kSVGEffectPaintPhaseLast = kSVGEffectPaintPhaseFirst + kPaintPhaseMax,
+
+ kTransform3DFirst,
+ kTransform3DElementTransform = kTransform3DFirst,
+ kTransform3DLast = kTransform3DElementTransform,
+ kEndTransform3DFirst,
+ kEndTransform3DLast =
+ kEndTransform3DFirst + kTransform3DLast - kTransform3DFirst,
+
+ kBeginFilter,
+ kEndFilter,
+ kBeginCompositing,
+ kEndCompositing,
+ kBeginTransform,
+ kEndTransform,
+ kBeginClipPath,
+ kEndClipPath,
+ kScrollHitTest,
+ kUninitializedType,
+ kTypeLast = kUninitializedType
+ };
+
+ DisplayItem(const DisplayItemClient& client, Type type, size_t derived_size)
+ : client_(&client),
+ visual_rect_(client.VisualRect()),
+ outset_for_raster_effects_(client.VisualRectOutsetForRasterEffects()),
+ type_(type),
+ derived_size_(derived_size),
+ fragment_(0),
+ skipped_cache_(false),
+ is_tombstone_(false) {
+ // |derived_size| must fit in |derived_size_|.
+ // If it doesn't, enlarge |derived_size_| and fix this assert.
+ SECURITY_DCHECK(derived_size < (1 << 8));
+ SECURITY_DCHECK(derived_size >= sizeof(*this));
+ }
+
+ virtual ~DisplayItem() = default;
+
+ // Ids are for matching new DisplayItems with existing DisplayItems.
+ struct Id {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ Id(const DisplayItemClient& client, const Type type, unsigned fragment = 0)
+ : client(client), type(type), fragment(fragment) {}
+ Id(const Id& id, unsigned fragment)
+ : client(id.client), type(id.type), fragment(fragment) {}
+
+ String ToString() const;
+
+ const DisplayItemClient& client;
+ const Type type;
+ const unsigned fragment;
+ };
+
+ Id GetId() const { return Id(*client_, GetType(), fragment_); }
+
+ virtual void Replay(GraphicsContext&) const {}
+
+ const DisplayItemClient& Client() const {
+ DCHECK(client_);
+ return *client_;
+ }
+
+ // This equals to Client().VisualRect() as long as the client is alive and is
+ // not invalidated. Otherwise it saves the previous visual rect of the client.
+ // See DisplayItemClient::VisualRect() about its coordinate space.
+ const FloatRect& VisualRect() const { return visual_rect_; }
+ float OutsetForRasterEffects() const { return outset_for_raster_effects_; }
+
+ // Visual rect can change without needing invalidation of the client, e.g.
+ // when ancestor clip changes. This is called from PaintController::
+ // UseCachedDrawingIfPossible() to update the visual rect of a cached display
+ // item.
+ void UpdateVisualRect() { visual_rect_ = FloatRect(client_->VisualRect()); }
+
+ Type GetType() const { return static_cast<Type>(type_); }
+
+ // Size of this object in memory, used to move it with memcpy.
+ // This is not sizeof(*this), because it needs to account for the size of
+ // the derived class (i.e. runtime type). Derived classes are expected to
+ // supply this to the DisplayItem constructor.
+ size_t DerivedSize() const { return derived_size_; }
+
+ // The fragment is part of the id, to uniquely identify display items in
+ // different fragments for the same client and type.
+ unsigned Fragment() const { return fragment_; }
+ void SetFragment(unsigned fragment) {
+ DCHECK(fragment < (1 << 14));
+ fragment_ = fragment;
+ }
+
+ // For PaintController only. Painters should use DisplayItemCacheSkipper
+ // instead.
+ void SetSkippedCache() { skipped_cache_ = true; }
+ bool SkippedCache() const { return skipped_cache_; }
+
+ // Appends this display item to the WebDisplayItemList, if applicable.
+ // |visual_rect_offset| is the offset between the space of the GraphicsLayer
+ // which owns the display item and the coordinate space of VisualRect().
+ // TODO(wangxianzhu): Remove the parameter for slimming paint v2.
+ virtual void AppendToWebDisplayItemList(const FloatSize& visual_rect_offset,
+ WebDisplayItemList*) const {}
+
+// See comments of enum Type for usage of the following macros.
+#define DEFINE_CATEGORY_METHODS(Category) \
+ static bool Is##Category##Type(Type type) { \
+ return type >= k##Category##First && type <= k##Category##Last; \
+ } \
+ bool Is##Category() const { return Is##Category##Type(GetType()); }
+
+#define DEFINE_CONVERSION_METHODS(Category1, category1, Category2, category2) \
+ static Type Category1##TypeTo##Category2##Type(Type type) { \
+ static_assert(k##Category1##Last - k##Category1##First == \
+ k##Category2##Last - k##Category2##First, \
+ "Categories " #Category1 " and " #Category2 \
+ " should have same number of enum values. See comments of " \
+ "DisplayItem::Type"); \
+ DCHECK(Is##Category1##Type(type)); \
+ return static_cast<Type>(type - k##Category1##First + \
+ k##Category2##First); \
+ } \
+ static Type category2##TypeTo##Category1##Type(Type type) { \
+ DCHECK(Is##Category2##Type(type)); \
+ return static_cast<Type>(type - k##Category2##First + \
+ k##Category1##First); \
+ }
+
+#define DEFINE_PAIRED_CATEGORY_METHODS(Category, category) \
+ DEFINE_CATEGORY_METHODS(Category) \
+ DEFINE_CATEGORY_METHODS(End##Category) \
+ DEFINE_CONVERSION_METHODS(Category, category, End##Category, end##Category)
+
+#define DEFINE_PAINT_PHASE_CONVERSION_METHOD(Category) \
+ static Type PaintPhaseTo##Category##Type(PaintPhase paint_phase) { \
+ static_assert( \
+ k##Category##PaintPhaseLast - k##Category##PaintPhaseFirst == \
+ kPaintPhaseMax, \
+ "Invalid paint-phase-based category " #Category \
+ ". See comments of DisplayItem::Type"); \
+ return static_cast<Type>(static_cast<int>(paint_phase) + \
+ k##Category##PaintPhaseFirst); \
+ }
+
+ DEFINE_CATEGORY_METHODS(Drawing)
+ DEFINE_PAINT_PHASE_CONVERSION_METHOD(Drawing)
+
+ DEFINE_CATEGORY_METHODS(ForeignLayer)
+
+ DEFINE_PAIRED_CATEGORY_METHODS(Clip, clip)
+ DEFINE_PAINT_PHASE_CONVERSION_METHOD(ClipLayerFragment)
+ DEFINE_PAINT_PHASE_CONVERSION_METHOD(ClipBox)
+ DEFINE_PAINT_PHASE_CONVERSION_METHOD(ClipColumnBounds)
+
+ DEFINE_PAIRED_CATEGORY_METHODS(FloatClip, floatClip)
+ DEFINE_PAINT_PHASE_CONVERSION_METHOD(FloatClip)
+
+ DEFINE_PAIRED_CATEGORY_METHODS(Scroll, scroll)
+ DEFINE_PAINT_PHASE_CONVERSION_METHOD(Scroll)
+
+ DEFINE_PAINT_PHASE_CONVERSION_METHOD(SVGTransform)
+ DEFINE_PAINT_PHASE_CONVERSION_METHOD(SVGEffect)
+
+ DEFINE_PAIRED_CATEGORY_METHODS(Transform3D, transform3D)
+
+ static bool IsScrollHitTestType(Type type) { return type == kScrollHitTest; }
+ bool IsScrollHitTest() const { return IsScrollHitTestType(GetType()); }
+
+ // TODO(pdr): Should this return true for IsScrollHitTestType too?
+ static bool IsCacheableType(Type type) { return IsDrawingType(type); }
+ bool IsCacheable() const {
+ return !SkippedCache() && IsCacheableType(GetType());
+ }
+
+ virtual bool IsBegin() const { return false; }
+ virtual bool IsEnd() const { return false; }
+
+#if DCHECK_IS_ON()
+ virtual bool IsEndAndPairedWith(DisplayItem::Type other_type) const {
+ return false;
+ }
+#endif
+
+ virtual bool Equals(const DisplayItem& other) const {
+ // Failure of this DCHECK would cause bad casts in subclasses.
+ SECURITY_CHECK(!is_tombstone_);
+ return client_ == other.client_ && type_ == other.type_ &&
+ fragment_ == other.fragment_ &&
+ derived_size_ == other.derived_size_ &&
+ skipped_cache_ == other.skipped_cache_;
+ }
+
+ // True if this DisplayItem is the tombstone/"dead display item" as part of
+ // moving an item from one list to another. See the default constructor of
+ // DisplayItem.
+ bool IsTombstone() const { return is_tombstone_; }
+
+ virtual bool DrawsContent() const { return false; }
+
+#if DCHECK_IS_ON()
+ static WTF::String TypeAsDebugString(DisplayItem::Type);
+ WTF::String AsDebugString() const;
+ virtual void PropertiesAsJSON(JSONObject&) const;
+#endif
+
+ private:
+ template <typename T, unsigned alignment>
+ friend class ContiguousContainer;
+ friend class DisplayItemList;
+
+ // The default DisplayItem constructor is only used by ContiguousContainer::
+ // AppendByMoving() where a tombstone DisplayItem is constructed at the source
+ // location. Only set is_tombstone_ to true, leaving other fields as-is so
+ // that we can get their original values for debugging. |visual_rect_| and
+ // |outset_for_raster_effects_| are special, see DisplayItemList::
+ // AppendByMoving().
+ DisplayItem() : is_tombstone_(true) {}
+
+ const DisplayItemClient* client_;
+ FloatRect visual_rect_;
+ float outset_for_raster_effects_;
+
+ static_assert(kTypeLast < (1 << 8), "DisplayItem::Type should fit in 8 bits");
+ unsigned type_ : 8;
+ unsigned derived_size_ : 8; // size of the actual derived class
+ unsigned fragment_ : 14;
+ unsigned skipped_cache_ : 1;
+ unsigned is_tombstone_ : 1;
+};
+
+inline bool operator==(const DisplayItem::Id& a, const DisplayItem::Id& b) {
+ return a.client == b.client && a.type == b.type && a.fragment == b.fragment;
+}
+
+inline bool operator!=(const DisplayItem::Id& a, const DisplayItem::Id& b) {
+ return !(a == b);
+}
+
+class PLATFORM_EXPORT PairedBeginDisplayItem : public DisplayItem {
+ protected:
+ PairedBeginDisplayItem(const DisplayItemClient& client,
+ Type type,
+ size_t derived_size)
+ : DisplayItem(client, type, derived_size) {
+ DCHECK(!RuntimeEnabledFeatures::SlimmingPaintV175Enabled());
+ }
+
+ private:
+ bool IsBegin() const final { return true; }
+};
+
+class PLATFORM_EXPORT PairedEndDisplayItem : public DisplayItem {
+ protected:
+ PairedEndDisplayItem(const DisplayItemClient& client,
+ Type type,
+ size_t derived_size)
+ : DisplayItem(client, type, derived_size) {
+ DCHECK(!RuntimeEnabledFeatures::SlimmingPaintV175Enabled());
+ }
+
+#if DCHECK_IS_ON()
+ bool IsEndAndPairedWith(DisplayItem::Type other_type) const override = 0;
+#endif
+
+ private:
+ bool IsEnd() const final { return true; }
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_DISPLAY_ITEM_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_cache_skipper.h b/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_cache_skipper.h
new file mode 100644
index 00000000000..17ec4e8148e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_cache_skipper.h
@@ -0,0 +1,33 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_DISPLAY_ITEM_CACHE_SKIPPER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_DISPLAY_ITEM_CACHE_SKIPPER_H_
+
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class DisplayItemCacheSkipper final {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ WTF_MAKE_NONCOPYABLE(DisplayItemCacheSkipper);
+
+ public:
+ DisplayItemCacheSkipper(GraphicsContext& context) : context_(context) {
+ context.GetPaintController().BeginSkippingCache();
+ }
+ ~DisplayItemCacheSkipper() {
+ context_.GetPaintController().EndSkippingCache();
+ }
+
+ private:
+ GraphicsContext& context_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_DISPLAY_ITEM_CACHE_SKIPPER_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_client.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_client.cc
new file mode 100644
index 00000000000..5fac148c2da
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_client.cc
@@ -0,0 +1,54 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/display_item_client.h"
+
+#if DCHECK_IS_ON()
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#endif
+
+namespace blink {
+
+DisplayItemClient::CacheGenerationOrInvalidationReason::ValueType
+ DisplayItemClient::CacheGenerationOrInvalidationReason::next_generation_ =
+ kFirstValidGeneration;
+
+#if DCHECK_IS_ON()
+
+HashSet<const DisplayItemClient*>* g_live_display_item_clients = nullptr;
+
+DisplayItemClient::DisplayItemClient() {
+ if (!g_live_display_item_clients)
+ g_live_display_item_clients = new HashSet<const DisplayItemClient*>();
+ g_live_display_item_clients->insert(this);
+}
+
+DisplayItemClient::~DisplayItemClient() {
+ g_live_display_item_clients->erase(this);
+}
+
+bool DisplayItemClient::IsAlive() const {
+ return g_live_display_item_clients &&
+ g_live_display_item_clients->Contains(this);
+}
+
+String DisplayItemClient::SafeDebugName(const DisplayItemClient& client,
+ bool known_to_be_safe) {
+ if (known_to_be_safe) {
+ DCHECK(client.IsAlive());
+ return client.DebugName();
+ }
+
+ // If the caller is not sure, we must ensure the client is alive, and it's
+ // not a destroyed client at the same address of a new client.
+ if (client.IsAlive() && !client.IsJustCreated())
+ return client.DebugName();
+
+ return "DEAD";
+}
+
+#endif // DCHECK_IS_ON()
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_client.h b/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_client.h
new file mode 100644
index 00000000000..680e5f33f0f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_client.h
@@ -0,0 +1,192 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_DISPLAY_ITEM_CLIENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_DISPLAY_ITEM_CLIENT_H_
+
+#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
+#include "third_party/blink/renderer/platform/graphics/paint_invalidation_reason.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+// The class for objects that can be associated with display items. A
+// DisplayItemClient object should live at least longer than the document cycle
+// in which its display items are created during painting. After the document
+// cycle, a pointer/reference to DisplayItemClient should be no longer
+// dereferenced unless we can make sure the client is still valid.
+class PLATFORM_EXPORT DisplayItemClient {
+ public:
+#if DCHECK_IS_ON()
+ DisplayItemClient();
+ virtual ~DisplayItemClient();
+
+ // Tests if this DisplayItemClient object has been created and has not been
+ // deleted yet.
+ bool IsAlive() const;
+ static String SafeDebugName(const DisplayItemClient&, bool known_to_be_safe);
+#else
+ DisplayItemClient() {}
+ virtual ~DisplayItemClient() {}
+#endif
+
+ virtual String DebugName() const = 0;
+
+ // The visual rect of this DisplayItemClient. For SPv1, it's in the object
+ // space of the object that owns the GraphicsLayer, i.e. offset by
+ // GraphicsLayer::OffsetFromLayoutObjectWithSubpixelAccumulation().
+ // For SPv2, it's in the space of the parent transform node.
+ virtual LayoutRect VisualRect() const = 0;
+
+ // The outset will be used to inflate visual rect after the visual rect is
+ // mapped into the space of the composited layer, for any special raster
+ // effects that might expand the rastered pixel area.
+ virtual float VisualRectOutsetForRasterEffects() const { return 0; }
+
+ // The rect that needs to be invalidated partially in this client. It's in the
+ // same coordinate space as VisualRect().
+ virtual LayoutRect PartialInvalidationRect() const { return LayoutRect(); }
+
+ // Called by PaintController::CommitNewDisplayItems() for all clients after
+ // painting.
+ virtual void ClearPartialInvalidationRect() const {}
+
+ // This is declared here instead of in LayoutObject for verifying the
+ // condition in DrawingRecorder.
+ // Returns true if the object itself will not generate any effective painted
+ // output no matter what size the object is. For example, this function can
+ // return false for an object whose size is currently 0x0 but would have
+ // effective painted output if it was set a non-empty size. It's used to skip
+ // unforced paint invalidation of LayoutObjects (which is when
+ // shouldDoFullPaintInvalidation is false, but mayNeedPaintInvalidation or
+ // childShouldCheckForPaintInvalidation is true) to avoid unnecessary paint
+ // invalidations of empty areas covered by such objects.
+ virtual bool PaintedOutputOfObjectHasNoEffectRegardlessOfSize() const {
+ return false;
+ }
+
+ // Indicates that the client will paint display items different from the ones
+ // cached by PaintController. However, PaintController allows a client to
+ // paint new display items that are not cached or to no longer paint some
+ // cached display items without calling this method.
+ // See PaintController::ClientCacheIsValid() for more details.
+ void SetDisplayItemsUncached(
+ PaintInvalidationReason reason = PaintInvalidationReason::kFull) const {
+ cache_generation_or_invalidation_reason_.Invalidate(reason);
+ }
+
+ PaintInvalidationReason GetPaintInvalidationReason() const {
+ return cache_generation_or_invalidation_reason_
+ .GetPaintInvalidationReason();
+ }
+
+ // A client is considered "just created" if its display items have never been
+ // committed.
+ bool IsJustCreated() const {
+ return cache_generation_or_invalidation_reason_.IsJustCreated();
+ }
+ void ClearIsJustCreated() const {
+ cache_generation_or_invalidation_reason_.ClearIsJustCreated();
+ }
+
+ private:
+ friend class FakeDisplayItemClient;
+ friend class PaintController;
+
+ // Holds a unique cache generation id of DisplayItemClients and
+ // PaintControllers, or PaintInvalidationReason if the DisplayItemClient or
+ // PaintController is invalidated.
+ //
+ // A paint controller sets its cache generation to
+ // DisplayItemCacheGeneration::next() at the end of each
+ // commitNewDisplayItems, and updates the cache generation of each client with
+ // cached drawings by calling DisplayItemClient::setDisplayItemsCached(). A
+ // display item is treated as validly cached in a paint controller if its
+ // cache generation matches the paint controller's cache generation.
+ //
+ // SPv1 only: If a display item is painted on multiple paint controllers,
+ // because cache generations are unique, the client's cache generation matches
+ // the last paint controller only. The client will be treated as invalid on
+ // other paint controllers regardless if it's validly cached by these paint
+ // controllers. This situation is very rare (about 0.07% of clients were
+ // painted on multiple paint controllers) so the performance penalty is
+ // trivial.
+ class PLATFORM_EXPORT CacheGenerationOrInvalidationReason {
+ DISALLOW_NEW();
+
+ public:
+ CacheGenerationOrInvalidationReason() : value_(kJustCreated) {}
+
+ void Invalidate(
+ PaintInvalidationReason reason = PaintInvalidationReason::kFull) {
+ if (value_ != kJustCreated)
+ value_ = static_cast<ValueType>(reason);
+ }
+
+ static CacheGenerationOrInvalidationReason Next() {
+ // In case the value overflowed in the previous call.
+ if (next_generation_ < kFirstValidGeneration)
+ next_generation_ = kFirstValidGeneration;
+ return CacheGenerationOrInvalidationReason(next_generation_++);
+ }
+
+ bool Matches(const CacheGenerationOrInvalidationReason& other) const {
+ return value_ >= kFirstValidGeneration &&
+ other.value_ >= kFirstValidGeneration && value_ == other.value_;
+ }
+
+ PaintInvalidationReason GetPaintInvalidationReason() const {
+ if (value_ == kJustCreated)
+ return PaintInvalidationReason::kAppeared;
+ if (value_ < kJustCreated)
+ return static_cast<PaintInvalidationReason>(value_);
+ return PaintInvalidationReason::kNone;
+ }
+
+ bool IsJustCreated() const { return value_ == kJustCreated; }
+ void ClearIsJustCreated() {
+ value_ = static_cast<ValueType>(PaintInvalidationReason::kFull);
+ }
+
+ private:
+ typedef uint32_t ValueType;
+ explicit CacheGenerationOrInvalidationReason(ValueType value)
+ : value_(value) {}
+
+ static const ValueType kJustCreated =
+ static_cast<ValueType>(PaintInvalidationReason::kMax) + 1;
+ static const ValueType kFirstValidGeneration =
+ static_cast<ValueType>(PaintInvalidationReason::kMax) + 2;
+ static ValueType next_generation_;
+ ValueType value_;
+ };
+
+ bool DisplayItemsAreCached(CacheGenerationOrInvalidationReason other) const {
+ return cache_generation_or_invalidation_reason_.Matches(other);
+ }
+ void SetDisplayItemsCached(
+ CacheGenerationOrInvalidationReason cache_generation) const {
+ cache_generation_or_invalidation_reason_ = cache_generation;
+ }
+
+ mutable CacheGenerationOrInvalidationReason
+ cache_generation_or_invalidation_reason_;
+
+ DISALLOW_COPY_AND_ASSIGN(DisplayItemClient);
+};
+
+inline bool operator==(const DisplayItemClient& client1,
+ const DisplayItemClient& client2) {
+ return &client1 == &client2;
+}
+inline bool operator!=(const DisplayItemClient& client1,
+ const DisplayItemClient& client2) {
+ return &client1 != &client2;
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_DISPLAY_ITEM_CLIENT_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_client_test.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_client_test.cc
new file mode 100644
index 00000000000..7cc270bd343
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_client_test.cc
@@ -0,0 +1,26 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/display_item_client.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/testing/fake_display_item_client.h"
+
+namespace blink {
+namespace {
+
+#if DCHECK_IS_ON() && !defined(UNDEFINED_SANITIZER)
+
+TEST(DisplayItemClientTest, IsAlive) {
+ EXPECT_FALSE(reinterpret_cast<DisplayItemClient*>(0x12345678)->IsAlive());
+ FakeDisplayItemClient* test_client = new FakeDisplayItemClient;
+ EXPECT_TRUE(test_client->IsAlive());
+ delete test_client;
+ EXPECT_FALSE(test_client->IsAlive());
+}
+
+#endif
+
+} // namespace
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_list.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_list.cc
new file mode 100644
index 00000000000..c072a60cf6b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_list.cc
@@ -0,0 +1,72 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/display_item_list.h"
+
+#include "third_party/blink/renderer/platform/graphics/logging_canvas.h"
+#include "third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_chunk.h"
+
+namespace blink {
+
+DisplayItemList::Range<DisplayItemList::iterator>
+DisplayItemList::ItemsInPaintChunk(const PaintChunk& paint_chunk) {
+ return Range<iterator>(begin() + paint_chunk.begin_index,
+ begin() + paint_chunk.end_index);
+}
+
+DisplayItemList::Range<DisplayItemList::const_iterator>
+DisplayItemList::ItemsInPaintChunk(const PaintChunk& paint_chunk) const {
+ return Range<const_iterator>(begin() + paint_chunk.begin_index,
+ begin() + paint_chunk.end_index);
+}
+
+#if DCHECK_IS_ON()
+
+std::unique_ptr<JSONArray> DisplayItemList::SubsequenceAsJSON(
+ size_t begin_index,
+ size_t end_index,
+ JsonFlags flags) const {
+ auto json_array = JSONArray::Create();
+ AppendSubsequenceAsJSON(begin_index, end_index, flags, *json_array);
+ return json_array;
+}
+
+void DisplayItemList::AppendSubsequenceAsJSON(size_t begin_index,
+ size_t end_index,
+ JsonFlags flags,
+ JSONArray& json_array) const {
+ for (size_t i = begin_index; i < end_index; ++i) {
+ std::unique_ptr<JSONObject> json = JSONObject::Create();
+
+ const auto& item = (*this)[i];
+ if ((flags & kSkipNonDrawings) && !item.IsDrawing())
+ continue;
+
+ json->SetInteger("index", i);
+
+ if (flags & kShownOnlyDisplayItemTypes) {
+ json->SetString("type", DisplayItem::TypeAsDebugString(item.GetType()));
+ } else {
+ json->SetString("clientDebugName",
+ DisplayItemClient::SafeDebugName(
+ item.Client(), flags & kClientKnownToBeAlive));
+ item.PropertiesAsJSON(*json);
+ }
+
+#ifndef NDEBUG
+ if ((flags & kShowPaintRecords) && item.IsDrawing()) {
+ const auto& drawing_item = static_cast<const DrawingDisplayItem&>(item);
+ if (const auto* record = drawing_item.GetPaintRecord().get())
+ json->SetArray("record", RecordAsJSON(*record));
+ }
+#endif
+
+ json_array.PushObject(std::move(json));
+ }
+}
+
+#endif // DCHECK_IS_ON()
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_list.h b/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_list.h
new file mode 100644
index 00000000000..4b60c7c9dc8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_list.h
@@ -0,0 +1,111 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_DISPLAY_ITEM_LIST_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_DISPLAY_ITEM_LIST_H_
+
+#include "third_party/blink/renderer/platform/graphics/contiguous_container.h"
+#include "third_party/blink/renderer/platform/graphics/paint/display_item.h"
+#include "third_party/blink/renderer/platform/graphics/paint/transform_3d_display_item.h"
+#include "third_party/blink/renderer/platform/wtf/alignment.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+namespace blink {
+
+class JSONArray;
+struct PaintChunk;
+
+// kDisplayItemAlignment must be a multiple of alignof(derived display item) for
+// each derived display item; the ideal value is the least common multiple.
+// Currently the limiting factor is TransformationMatrix (in
+// BeginTransform3DDisplayItem), which requests 16-byte alignment.
+static const size_t kDisplayItemAlignment =
+ WTF_ALIGN_OF(BeginTransform3DDisplayItem);
+static const size_t kMaximumDisplayItemSize =
+ sizeof(BeginTransform3DDisplayItem);
+
+// A container for a list of display items.
+class PLATFORM_EXPORT DisplayItemList
+ : public ContiguousContainer<DisplayItem, kDisplayItemAlignment> {
+ public:
+ DisplayItemList(size_t initial_size_bytes)
+ : ContiguousContainer(kMaximumDisplayItemSize, initial_size_bytes) {}
+ DisplayItemList(DisplayItemList&& source)
+ : ContiguousContainer(std::move(source)) {}
+
+ DisplayItemList& operator=(DisplayItemList&& source) {
+ ContiguousContainer::operator=(std::move(source));
+ return *this;
+ }
+
+ DisplayItem& AppendByMoving(DisplayItem& item) {
+ SECURITY_CHECK(!item.IsTombstone());
+ DisplayItem& result =
+ ContiguousContainer::AppendByMoving(item, item.DerivedSize());
+ // ContiguousContainer::AppendByMoving() calls an in-place constructor
+ // on item which replaces it with a tombstone/"dead display item" that
+ // can be safely destructed but should never be used except for debugging.
+ DCHECK(item.IsTombstone());
+ DCHECK(item.GetId() == result.GetId());
+ // We need |visual_rect_| and |outset_for_raster_effects_| of the old
+ // display item for raster invalidation. As their values were initialized
+ // to default values in DisplayItem's default constructor, now copy their
+ // original values back from |result|.
+ item.visual_rect_ = result.visual_rect_;
+ item.outset_for_raster_effects_ = result.outset_for_raster_effects_;
+ return result;
+ }
+
+ // This is used by PaintUnderInvalidationChecking in SPv1 to restore a
+ // paired-begin display item that was moved to the new display item list then
+ // was removed because the pair is a no-op. This ensures that we won't compare
+ // the next new display item against the tombstone display item.
+ void RestoreTombstone(size_t index, DisplayItem& item) {
+ DCHECK(!RuntimeEnabledFeatures::SlimmingPaintV175Enabled());
+ DCHECK((*this)[index].IsTombstone());
+ SECURITY_CHECK((*this)[index].DerivedSize() == item.DerivedSize());
+ memcpy(static_cast<void*>(&(*this)[index]), static_cast<void*>(&item),
+ item.DerivedSize());
+ new (&item) DisplayItem;
+ }
+
+ // Useful for iterating with a range-based for loop.
+ template <typename Iterator>
+ class Range {
+ public:
+ Range(const Iterator& begin, const Iterator& end)
+ : begin_(begin), end_(end) {}
+ Iterator begin() const { return begin_; }
+ Iterator end() const { return end_; }
+
+ private:
+ Iterator begin_;
+ Iterator end_;
+ };
+ Range<iterator> ItemsInPaintChunk(const PaintChunk&);
+ Range<const_iterator> ItemsInPaintChunk(const PaintChunk&) const;
+
+#if DCHECK_IS_ON()
+ enum JsonOptions {
+ kDefault = 0,
+ kShowPaintRecords = 1,
+ kSkipNonDrawings = 1 << 1,
+ kClientKnownToBeAlive = 1 << 2,
+ kShownOnlyDisplayItemTypes = 1 << 3
+ };
+ typedef unsigned JsonFlags;
+
+ std::unique_ptr<JSONArray> SubsequenceAsJSON(size_t begin_index,
+ size_t end_index,
+ JsonFlags) const;
+ void AppendSubsequenceAsJSON(size_t begin_index,
+ size_t end_index,
+ JsonFlags,
+ JSONArray&) const;
+#endif // DCHECK_IS_ON()
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_DISPLAY_ITEM_LIST_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_test.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_test.cc
new file mode 100644
index 00000000000..0114fe4153c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_test.cc
@@ -0,0 +1,24 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/display_item.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+namespace {
+
+#if DCHECK_IS_ON()
+TEST(DisplayItemTest, DebugStringsExist) {
+ for (int type = 0; type <= DisplayItem::kTypeLast; type++) {
+ String debug_string =
+ DisplayItem::TypeAsDebugString(static_cast<DisplayItem::Type>(type));
+ EXPECT_FALSE(debug_string.IsEmpty());
+ EXPECT_NE("Unknown", debug_string);
+ }
+}
+#endif // DCHECK_IS_ON()
+
+} // namespace
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/drawing_display_item.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/drawing_display_item.cc
new file mode 100644
index 00000000000..613ccf3ef05
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/drawing_display_item.cc
@@ -0,0 +1,119 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h"
+
+#include "third_party/blink/public/platform/web_display_item_list.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkData.h"
+
+namespace blink {
+
+void DrawingDisplayItem::Replay(GraphicsContext& context) const {
+ if (record_)
+ context.DrawRecord(record_);
+}
+
+void DrawingDisplayItem::AppendToWebDisplayItemList(
+ const FloatSize& visual_rect_offset,
+ WebDisplayItemList* list) const {
+ if (record_) {
+ // Convert visual rect into the GraphicsLayer's coordinate space.
+ auto visual_rect = VisualRect();
+ visual_rect.Move(-visual_rect_offset);
+ list->AppendDrawingItem(EnclosingIntRect(visual_rect), record_);
+ }
+}
+
+bool DrawingDisplayItem::DrawsContent() const {
+ return record_.get();
+}
+
+#if DCHECK_IS_ON()
+void DrawingDisplayItem::PropertiesAsJSON(JSONObject& json) const {
+ DisplayItem::PropertiesAsJSON(json);
+ json.SetBoolean("opaque", known_to_be_opaque_);
+}
+#endif
+
+static bool RecordsEqual(sk_sp<const PaintRecord> record1,
+ sk_sp<const PaintRecord> record2,
+ const FloatRect& bounds) {
+ if (record1->size() != record2->size())
+ return false;
+
+ // TODO(enne): PaintRecord should have an operator==
+ sk_sp<SkData> data1 = ToSkPicture(record1, bounds)->serialize();
+ sk_sp<SkData> data2 = ToSkPicture(record2, bounds)->serialize();
+ return data1->equals(data2.get());
+}
+
+static SkBitmap RecordToBitmap(sk_sp<const PaintRecord> record,
+ const IntRect& bounds) {
+ SkBitmap bitmap;
+ bitmap.allocPixels(
+ SkImageInfo::MakeN32Premul(bounds.Width(), bounds.Height()));
+ SkiaPaintCanvas canvas(bitmap);
+ canvas.clear(SK_ColorTRANSPARENT);
+ canvas.translate(-bounds.X(), -bounds.Y());
+ canvas.drawPicture(std::move(record));
+ return bitmap;
+}
+
+static bool BitmapsEqual(sk_sp<const PaintRecord> record1,
+ sk_sp<const PaintRecord> record2,
+ const IntRect& bounds) {
+ SkBitmap bitmap1 = RecordToBitmap(record1, bounds);
+ SkBitmap bitmap2 = RecordToBitmap(record2, bounds);
+ int mismatch_count = 0;
+ constexpr int kMaxMismatches = 10;
+ for (int y = 0; y < bounds.Height(); ++y) {
+ for (int x = 0; x < bounds.Width(); ++x) {
+ SkColor pixel1 = bitmap1.getColor(x, y);
+ SkColor pixel2 = bitmap2.getColor(x, y);
+ if (pixel1 != pixel2) {
+ if (!RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled())
+ return false;
+ LOG(ERROR) << "x=" << x << " y=" << y << " " << std::hex << pixel1
+ << " vs " << std::hex << pixel2;
+ if (++mismatch_count >= kMaxMismatches)
+ return false;
+ }
+ }
+ }
+ return !mismatch_count;
+}
+
+bool DrawingDisplayItem::Equals(const DisplayItem& other) const {
+ if (!DisplayItem::Equals(other))
+ return false;
+
+ const auto& record = GetPaintRecord();
+ const auto& other_record =
+ static_cast<const DrawingDisplayItem&>(other).GetPaintRecord();
+ if (!record && !other_record)
+ return true;
+ if (!record || !other_record)
+ return false;
+
+ const auto& bounds = this->VisualRect();
+ const auto& other_bounds = other.VisualRect();
+ if (bounds != other_bounds)
+ return false;
+
+ if (RecordsEqual(record, other_record, FloatRect(bounds)))
+ return true;
+
+ // Sometimes the client may produce different records for the same visual
+ // result, which should be treated as equal.
+ IntRect int_bounds = EnclosingIntRect(bounds);
+ // Limit the bounds to prevent OOM.
+ int_bounds.Intersect(IntRect(int_bounds.X(), int_bounds.Y(), 6000, 6000));
+ return BitmapsEqual(std::move(record), std::move(other_record), int_bounds);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h b/chromium/third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h
new file mode 100644
index 00000000000..22e55592f04
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h
@@ -0,0 +1,75 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_DRAWING_DISPLAY_ITEM_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_DRAWING_DISPLAY_ITEM_H_
+
+#include "base/compiler_specific.h"
+#include "third_party/blink/renderer/platform/graphics/paint/display_item.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_record.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+namespace blink {
+
+// DrawingDisplayItem contains recorded painting operations which can be
+// replayed to produce a rastered output.
+//
+// This class has two notions of the bounds around the content that was recorded
+// and will be produced by the item. The first is the |record_bounds| which
+// describes the bounds of all content in the |record| in the space of the
+// record. The second is the |visual_rect| which should describe the same thing,
+// but takes into account transforms and clips that would apply to the
+// PaintRecord, and is in the space of the DisplayItemList. This allows the
+// visual_rect to be compared between DrawingDisplayItems, and to give bounds
+// around what the user can actually see from the PaintRecord.
+class PLATFORM_EXPORT DrawingDisplayItem final : public DisplayItem {
+ public:
+ DISABLE_CFI_PERF
+ DrawingDisplayItem(const DisplayItemClient& client,
+ Type type,
+ sk_sp<const PaintRecord> record,
+ bool known_to_be_opaque);
+
+ void Replay(GraphicsContext&) const override;
+ void AppendToWebDisplayItemList(const FloatSize& visual_rect_offset,
+ WebDisplayItemList*) const override;
+ bool DrawsContent() const override;
+
+ const sk_sp<const PaintRecord>& GetPaintRecord() const { return record_; }
+
+ bool KnownToBeOpaque() const {
+ DCHECK(RuntimeEnabledFeatures::SlimmingPaintV2Enabled());
+ return known_to_be_opaque_;
+ }
+
+ bool Equals(const DisplayItem& other) const final;
+
+ private:
+#if DCHECK_IS_ON()
+ void PropertiesAsJSON(JSONObject&) const override;
+#endif
+
+ sk_sp<const PaintRecord> record_;
+
+ // True if there are no transparent areas. Only used for SlimmingPaintV2.
+ const bool known_to_be_opaque_;
+};
+
+// TODO(dcheng): Move this ctor back inline once the clang plugin is fixed.
+DISABLE_CFI_PERF
+inline DrawingDisplayItem::DrawingDisplayItem(const DisplayItemClient& client,
+ Type type,
+ sk_sp<const PaintRecord> record,
+ bool known_to_be_opaque = false)
+ : DisplayItem(client, type, sizeof(*this)),
+ record_(record && record->size() ? std::move(record) : nullptr),
+ known_to_be_opaque_(known_to_be_opaque) {
+ DCHECK(IsDrawingType(type));
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_DRAWING_DISPLAY_ITEM_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/drawing_display_item_test.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/drawing_display_item_test.cc
new file mode 100644
index 00000000000..b2a78e582c7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/drawing_display_item_test.cc
@@ -0,0 +1,129 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h"
+
+#include "SkTypes.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/web_display_item_list.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_recorder.h"
+#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
+#include "third_party/blink/renderer/platform/testing/fake_display_item_client.h"
+
+namespace blink {
+namespace {
+
+using testing::_;
+
+class DrawingDisplayItemTest : public testing::Test {
+ protected:
+ FakeDisplayItemClient client_;
+};
+
+class MockWebDisplayItemList : public WebDisplayItemList {
+ public:
+ MOCK_METHOD2(AppendDrawingItem,
+ void(const WebRect& visual_rect, sk_sp<const cc::PaintRecord>));
+};
+
+static sk_sp<PaintRecord> CreateRectRecord(const FloatRect& record_bounds) {
+ PaintRecorder recorder;
+ PaintCanvas* canvas =
+ recorder.beginRecording(record_bounds.Width(), record_bounds.Height());
+ canvas->drawRect(record_bounds, PaintFlags());
+ return recorder.finishRecordingAsPicture();
+}
+
+static sk_sp<PaintRecord> CreateRectRecordWithTranslate(
+ const FloatRect& record_bounds,
+ float dx,
+ float dy) {
+ PaintRecorder recorder;
+ PaintCanvas* canvas =
+ recorder.beginRecording(record_bounds.Width(), record_bounds.Height());
+ canvas->save();
+ canvas->translate(dx, dy);
+ canvas->drawRect(record_bounds, PaintFlags());
+ canvas->restore();
+ return recorder.finishRecordingAsPicture();
+}
+
+TEST_F(DrawingDisplayItemTest, VisualRectAndDrawingBounds) {
+ FloatRect record_bounds(5.5, 6.6, 7.7, 8.8);
+ LayoutRect drawing_bounds(record_bounds);
+ client_.SetVisualRect(drawing_bounds);
+
+ DrawingDisplayItem item(client_, DisplayItem::Type::kDocumentBackground,
+ CreateRectRecord(record_bounds));
+ EXPECT_EQ(FloatRect(drawing_bounds), item.VisualRect());
+
+ MockWebDisplayItemList list1;
+ WebRect expected_rect = EnclosingIntRect(drawing_bounds);
+ EXPECT_CALL(list1, AppendDrawingItem(expected_rect, _)).Times(1);
+ item.AppendToWebDisplayItemList(FloatSize(), &list1);
+
+ FloatSize offset(2.1, 3.6);
+ FloatRect visual_rect_with_offset(drawing_bounds);
+ visual_rect_with_offset.Move(-offset);
+ WebRect expected_visual_rect = EnclosingIntRect(visual_rect_with_offset);
+ MockWebDisplayItemList list2;
+ EXPECT_CALL(list2, AppendDrawingItem(expected_visual_rect, _)).Times(1);
+ item.AppendToWebDisplayItemList(offset, &list2);
+}
+
+TEST_F(DrawingDisplayItemTest, Equals) {
+ FloatRect bounds1(100.1, 100.2, 100.3, 100.4);
+ client_.SetVisualRect(LayoutRect(bounds1));
+ DrawingDisplayItem item1(client_, DisplayItem::kDocumentBackground,
+ CreateRectRecord(bounds1));
+ DrawingDisplayItem translated(client_, DisplayItem::kDocumentBackground,
+ CreateRectRecordWithTranslate(bounds1, 10, 20));
+ // This item contains a DrawingRecord that is different from but visually
+ // equivalent to item1's.
+ DrawingDisplayItem zero_translated(
+ client_, DisplayItem::kDocumentBackground,
+ CreateRectRecordWithTranslate(bounds1, 0, 0));
+
+ FloatRect bounds2(100.5, 100.6, 100.7, 100.8);
+ client_.SetVisualRect(LayoutRect(bounds2));
+ DrawingDisplayItem item2(client_, DisplayItem::kDocumentBackground,
+ CreateRectRecord(bounds2));
+
+ DrawingDisplayItem empty_item(client_, DisplayItem::kDocumentBackground,
+ nullptr);
+
+ EXPECT_TRUE(item1.Equals(item1));
+ EXPECT_FALSE(item1.Equals(item2));
+ EXPECT_FALSE(item1.Equals(translated));
+ EXPECT_TRUE(item1.Equals(zero_translated));
+ EXPECT_FALSE(item1.Equals(empty_item));
+
+ EXPECT_FALSE(item2.Equals(item1));
+ EXPECT_TRUE(item2.Equals(item2));
+ EXPECT_FALSE(item2.Equals(translated));
+ EXPECT_FALSE(item2.Equals(zero_translated));
+ EXPECT_FALSE(item2.Equals(empty_item));
+
+ EXPECT_FALSE(translated.Equals(item1));
+ EXPECT_FALSE(translated.Equals(item2));
+ EXPECT_TRUE(translated.Equals(translated));
+ EXPECT_FALSE(translated.Equals(zero_translated));
+ EXPECT_FALSE(translated.Equals(empty_item));
+
+ EXPECT_TRUE(zero_translated.Equals(item1));
+ EXPECT_FALSE(zero_translated.Equals(item2));
+ EXPECT_FALSE(zero_translated.Equals(translated));
+ EXPECT_TRUE(zero_translated.Equals(zero_translated));
+ EXPECT_FALSE(zero_translated.Equals(empty_item));
+
+ EXPECT_FALSE(empty_item.Equals(item1));
+ EXPECT_FALSE(empty_item.Equals(item2));
+ EXPECT_FALSE(empty_item.Equals(translated));
+ EXPECT_FALSE(empty_item.Equals(zero_translated));
+ EXPECT_TRUE(empty_item.Equals(empty_item));
+}
+
+} // namespace
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/drawing_recorder.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/drawing_recorder.cc
new file mode 100644
index 00000000000..b42de3e1340
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/drawing_recorder.cc
@@ -0,0 +1,80 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
+
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_record.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+
+namespace blink {
+
+#if DCHECK_IS_ON()
+static bool g_list_modification_check_disabled = false;
+DisableListModificationCheck::DisableListModificationCheck()
+ : disabler_(&g_list_modification_check_disabled, true) {}
+#endif
+
+DrawingRecorder::DrawingRecorder(GraphicsContext& context,
+ const DisplayItemClient& display_item_client,
+ DisplayItem::Type display_item_type)
+ : context_(context),
+ client_(display_item_client),
+ type_(display_item_type),
+ known_to_be_opaque_(false)
+#if DCHECK_IS_ON()
+ ,
+ initial_display_item_list_size_(
+ context_.GetPaintController().NewDisplayItemList().size())
+#endif
+{
+ if (context.GetPaintController().DisplayItemConstructionIsDisabled())
+ return;
+
+ // Must check DrawingRecorder::useCachedDrawingIfPossible before creating the
+ // DrawingRecorder.
+ DCHECK(RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled() ||
+ !UseCachedDrawingIfPossible(context_, client_, type_));
+
+ DCHECK(DisplayItem::IsDrawingType(display_item_type));
+
+#if DCHECK_IS_ON()
+ context.SetInDrawingRecorder(true);
+#endif
+
+ context.BeginRecording(FloatRect());
+}
+
+DrawingRecorder::~DrawingRecorder() {
+ if (context_.GetPaintController().DisplayItemConstructionIsDisabled())
+ return;
+
+#if DCHECK_IS_ON()
+ context_.SetInDrawingRecorder(false);
+
+ if (!g_list_modification_check_disabled) {
+ DCHECK(initial_display_item_list_size_ ==
+ context_.GetPaintController().NewDisplayItemList().size());
+ }
+#endif
+
+ sk_sp<const PaintRecord> picture = context_.EndRecording();
+
+#if DCHECK_IS_ON()
+ // When skipping cache (e.g. in PaintRecordBuilder with a temporary
+ // PaintController), the client's painting might be different from its normal
+ // painting.
+ if (!context_.GetPaintController().IsSkippingCache() &&
+ client_.PaintedOutputOfObjectHasNoEffectRegardlessOfSize()) {
+ DCHECK_EQ(0u, picture->size()) << client_.DebugName();
+ }
+#endif
+
+ context_.GetPaintController().CreateAndAppend<DrawingDisplayItem>(
+ client_, type_, picture, known_to_be_opaque_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h b/chromium/third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h
new file mode 100644
index 00000000000..0addcf1e460
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h
@@ -0,0 +1,89 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_DRAWING_RECORDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_DRAWING_RECORDER_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/auto_reset.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class GraphicsContext;
+
+class PLATFORM_EXPORT DrawingRecorder final {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ WTF_MAKE_NONCOPYABLE(DrawingRecorder);
+
+ public:
+ static bool UseCachedDrawingIfPossible(GraphicsContext& context,
+ const DisplayItemClient& client,
+ DisplayItem::Type type) {
+ return context.GetPaintController().UseCachedDrawingIfPossible(client,
+ type);
+ }
+
+ static bool UseCachedDrawingIfPossible(GraphicsContext& context,
+ const DisplayItemClient& client,
+ PaintPhase phase) {
+ return UseCachedDrawingIfPossible(
+ context, client, DisplayItem::PaintPhaseToDrawingType(phase));
+ }
+
+ DrawingRecorder(GraphicsContext&,
+ const DisplayItemClient&,
+ DisplayItem::Type);
+
+ DrawingRecorder(GraphicsContext& context,
+ const DisplayItemClient& client,
+ PaintPhase phase)
+ : DrawingRecorder(context,
+ client,
+ DisplayItem::PaintPhaseToDrawingType(phase)) {}
+
+ ~DrawingRecorder();
+
+ void SetKnownToBeOpaque() {
+ DCHECK(RuntimeEnabledFeatures::SlimmingPaintV2Enabled());
+ known_to_be_opaque_ = true;
+ }
+
+ private:
+ GraphicsContext& context_;
+ const DisplayItemClient& client_;
+ const DisplayItem::Type type_;
+
+ // True if there are no transparent areas. Only used for SlimmingPaintV2.
+ bool known_to_be_opaque_;
+
+#if DCHECK_IS_ON()
+ // Ensures the list size does not change during the recorder's scope.
+ size_t initial_display_item_list_size_;
+#endif
+};
+
+#if DCHECK_IS_ON()
+class DisableListModificationCheck {
+ STACK_ALLOCATED();
+ WTF_MAKE_NONCOPYABLE(DisableListModificationCheck);
+
+ public:
+ DisableListModificationCheck();
+
+ private:
+ AutoReset<bool> disabler_;
+};
+#endif // DCHECK_IS_ON()
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_DRAWING_RECORDER_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/drawing_recorder_test.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/drawing_recorder_test.cc
new file mode 100644
index 00000000000..4882dba1339
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/drawing_recorder_test.cc
@@ -0,0 +1,85 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_controller_test.h"
+#include "third_party/blink/renderer/platform/testing/fake_display_item_client.h"
+
+namespace blink {
+
+using DrawingRecorderTest = PaintControllerTestBase;
+
+namespace {
+
+const IntRect kBounds(1, 2, 3, 4);
+
+TEST_F(DrawingRecorderTest, Nothing) {
+ FakeDisplayItemClient client;
+ GraphicsContext context(GetPaintController());
+ InitRootChunk();
+ DrawNothing(context, client, kForegroundType);
+ GetPaintController().CommitNewDisplayItems();
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 1,
+ TestDisplayItem(client, kForegroundType));
+ EXPECT_FALSE(static_cast<const DrawingDisplayItem&>(
+ GetPaintController().GetDisplayItemList()[0])
+ .GetPaintRecord());
+}
+
+TEST_F(DrawingRecorderTest, Rect) {
+ FakeDisplayItemClient client;
+ GraphicsContext context(GetPaintController());
+ InitRootChunk();
+ DrawRect(context, client, kForegroundType, kBounds);
+ GetPaintController().CommitNewDisplayItems();
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 1,
+ TestDisplayItem(client, kForegroundType));
+}
+
+TEST_F(DrawingRecorderTest, Cached) {
+ FakeDisplayItemClient client;
+ GraphicsContext context(GetPaintController());
+ InitRootChunk();
+ DrawNothing(context, client, kBackgroundType);
+ DrawRect(context, client, kForegroundType, kBounds);
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 2,
+ TestDisplayItem(client, kBackgroundType),
+ TestDisplayItem(client, kForegroundType));
+
+ InitRootChunk();
+ DrawNothing(context, client, kBackgroundType);
+ DrawRect(context, client, kForegroundType, kBounds);
+
+ EXPECT_EQ(2, NumCachedNewItems());
+
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 2,
+ TestDisplayItem(client, kBackgroundType),
+ TestDisplayItem(client, kForegroundType));
+}
+
+template <typename T>
+FloatRect DrawAndGetCullRect(PaintController& controller,
+ const DisplayItemClient& client,
+ const T& bounds) {
+ {
+ // Draw some things which will produce a non-null picture.
+ GraphicsContext context(controller);
+ DrawingRecorder recorder(context, client, kBackgroundType, bounds);
+ context.DrawRect(EnclosedIntRect(FloatRect(bounds)));
+ }
+ controller.CommitNewDisplayItems();
+ const auto& drawing = static_cast<const DrawingDisplayItem&>(
+ controller.GetDisplayItemList()[0]);
+ return FloatRect(drawing.VisualRect());
+}
+
+} // namespace
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.cc
new file mode 100644
index 00000000000..c5277282c14
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.cc
@@ -0,0 +1,55 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h"
+
+namespace blink {
+
+EffectPaintPropertyNode* EffectPaintPropertyNode::Root() {
+ DEFINE_STATIC_REF(
+ EffectPaintPropertyNode, root,
+ (EffectPaintPropertyNode::Create(
+ nullptr, TransformPaintPropertyNode::Root(),
+ ClipPaintPropertyNode::Root(), kColorFilterNone,
+ CompositorFilterOperations(), 1.0, SkBlendMode::kSrcOver)));
+ return root;
+}
+
+FloatRect EffectPaintPropertyNode::MapRect(const FloatRect& input_rect) const {
+ FloatRect rect = input_rect;
+ rect.MoveBy(-paint_offset_);
+ FloatRect result = filter_.MapRect(rect);
+ result.MoveBy(paint_offset_);
+ return result;
+}
+
+std::unique_ptr<JSONObject> EffectPaintPropertyNode::ToJSON() const {
+ auto json = JSONObject::Create();
+ if (Parent())
+ json->SetString("parent", String::Format("%p", Parent()));
+ json->SetString("localTransformSpace",
+ String::Format("%p", local_transform_space_.get()));
+ json->SetString("outputClip", String::Format("%p", output_clip_.get()));
+ if (color_filter_ != kColorFilterNone)
+ json->SetInteger("colorFilter", color_filter_);
+ if (!filter_.IsEmpty())
+ json->SetString("filter", filter_.ToString());
+ if (opacity_ != 1.0f)
+ json->SetDouble("opacity", opacity_);
+ if (blend_mode_ != SkBlendMode::kSrcOver)
+ json->SetString("blendMode", SkBlendMode_Name(blend_mode_));
+ if (direct_compositing_reasons_ != CompositingReason::kNone) {
+ json->SetString("directCompositingReasons",
+ CompositingReason::ToString(direct_compositing_reasons_));
+ }
+ if (compositor_element_id_) {
+ json->SetString("compositorElementId",
+ compositor_element_id_.ToString().c_str());
+ }
+ if (paint_offset_ != FloatPoint())
+ json->SetString("paintOffset", paint_offset_.ToString());
+ return json;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h b/chromium/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h
new file mode 100644
index 00000000000..93980d76191
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h
@@ -0,0 +1,201 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_EFFECT_PAINT_PROPERTY_NODE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_EFFECT_PAINT_PROPERTY_NODE_H_
+
+#include "cc/layers/layer.h"
+#include "third_party/blink/renderer/platform/graphics/compositor_element_id.h"
+#include "third_party/blink/renderer/platform/graphics/compositor_filter_operations.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_types.h"
+#include "third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_property_node.h"
+#include "third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+#include <iosfwd>
+
+namespace blink {
+
+// Effect nodes are abstraction of isolated groups, along with optional effects
+// that can be applied to the composited output of the group.
+//
+// The effect tree is rooted at a node with no parent. This root node should
+// not be modified.
+class PLATFORM_EXPORT EffectPaintPropertyNode
+ : public PaintPropertyNode<EffectPaintPropertyNode> {
+ public:
+ // This node is really a sentinel, and does not represent a real effect.
+ static EffectPaintPropertyNode* Root();
+
+ static scoped_refptr<EffectPaintPropertyNode> Create(
+ scoped_refptr<const EffectPaintPropertyNode> parent,
+ scoped_refptr<const TransformPaintPropertyNode> local_transform_space,
+ scoped_refptr<const ClipPaintPropertyNode> output_clip,
+ ColorFilter color_filter,
+ CompositorFilterOperations filter,
+ float opacity,
+ SkBlendMode blend_mode,
+ CompositingReasons direct_compositing_reasons = CompositingReason::kNone,
+ const CompositorElementId& compositor_element_id = CompositorElementId(),
+ const FloatPoint& paint_offset = FloatPoint()) {
+ return base::AdoptRef(new EffectPaintPropertyNode(
+ std::move(parent), std::move(local_transform_space),
+ std::move(output_clip), color_filter, std::move(filter), opacity,
+ blend_mode, direct_compositing_reasons, compositor_element_id,
+ paint_offset));
+ }
+
+ bool Update(
+ scoped_refptr<const EffectPaintPropertyNode> parent,
+ scoped_refptr<const TransformPaintPropertyNode> local_transform_space,
+ scoped_refptr<const ClipPaintPropertyNode> output_clip,
+ ColorFilter color_filter,
+ CompositorFilterOperations filter,
+ float opacity,
+ SkBlendMode blend_mode,
+ CompositingReasons direct_compositing_reasons = CompositingReason::kNone,
+ const CompositorElementId& compositor_element_id = CompositorElementId(),
+ const FloatPoint& paint_offset = FloatPoint()) {
+ bool parent_changed = PaintPropertyNode::Update(std::move(parent));
+
+ if (local_transform_space == local_transform_space_ &&
+ output_clip == output_clip_ && color_filter == color_filter_ &&
+ filter == filter_ && opacity == opacity_ && blend_mode == blend_mode_ &&
+ (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled() ||
+ (direct_compositing_reasons == direct_compositing_reasons_ &&
+ compositor_element_id == compositor_element_id_)) &&
+ paint_offset == paint_offset_)
+ return parent_changed;
+
+ SetChanged();
+ local_transform_space_ = std::move(local_transform_space);
+ output_clip_ = std::move(output_clip);
+ color_filter_ = color_filter;
+ filter_ = std::move(filter);
+ opacity_ = opacity;
+ blend_mode_ = blend_mode;
+ direct_compositing_reasons_ = direct_compositing_reasons;
+ compositor_element_id_ = compositor_element_id;
+ paint_offset_ = paint_offset;
+ return true;
+ }
+
+ const TransformPaintPropertyNode* LocalTransformSpace() const {
+ return local_transform_space_.get();
+ }
+ const ClipPaintPropertyNode* OutputClip() const { return output_clip_.get(); }
+
+ SkBlendMode BlendMode() const { return blend_mode_; }
+ float Opacity() const { return opacity_; }
+ const CompositorFilterOperations& Filter() const { return filter_; }
+ ColorFilter GetColorFilter() const { return color_filter_; }
+
+ bool HasFilterThatMovesPixels() const {
+ return filter_.HasFilterThatMovesPixels();
+ }
+
+ FloatPoint PaintOffset() const { return paint_offset_; }
+
+ // Returns a rect covering the pixels that can be affected by pixels in
+ // |inputRect|. The rects are in the space of localTransformSpace.
+ FloatRect MapRect(const FloatRect& input_rect) const;
+
+#if DCHECK_IS_ON()
+ // The clone function is used by FindPropertiesNeedingUpdate.h for recording
+ // an effect node before it has been updated, to later detect changes.
+ scoped_refptr<EffectPaintPropertyNode> Clone() const {
+ return base::AdoptRef(new EffectPaintPropertyNode(
+ Parent(), local_transform_space_, output_clip_, color_filter_, filter_,
+ opacity_, blend_mode_, direct_compositing_reasons_,
+ compositor_element_id_, paint_offset_));
+ }
+
+ // The equality operator is used by FindPropertiesNeedingUpdate.h for checking
+ // if an effect node has changed. It ignores changes of reference filters
+ // because SkImageFilter doesn't have an equality operator.
+ bool operator==(const EffectPaintPropertyNode& o) const {
+ return Parent() == o.Parent() &&
+ local_transform_space_ == o.local_transform_space_ &&
+ output_clip_ == o.output_clip_ && color_filter_ == o.color_filter_ &&
+ filter_ == o.filter_ && opacity_ == o.opacity_ &&
+ blend_mode_ == o.blend_mode_ &&
+ (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled() ||
+ (direct_compositing_reasons_ == o.direct_compositing_reasons_ &&
+ compositor_element_id_ == o.compositor_element_id_)) &&
+ paint_offset_ == o.paint_offset_;
+ }
+#endif
+
+ std::unique_ptr<JSONObject> ToJSON() const;
+
+ bool HasDirectCompositingReasons() const {
+ return direct_compositing_reasons_ != CompositingReason::kNone;
+ }
+
+ bool RequiresCompositingForAnimation() const {
+ return direct_compositing_reasons_ &
+ CompositingReason::kComboActiveAnimation;
+ }
+
+ const CompositorElementId& GetCompositorElementId() const {
+ return compositor_element_id_;
+ }
+
+ private:
+ EffectPaintPropertyNode(
+ scoped_refptr<const EffectPaintPropertyNode> parent,
+ scoped_refptr<const TransformPaintPropertyNode> local_transform_space,
+ scoped_refptr<const ClipPaintPropertyNode> output_clip,
+ ColorFilter color_filter,
+ CompositorFilterOperations filter,
+ float opacity,
+ SkBlendMode blend_mode,
+ CompositingReasons direct_compositing_reasons,
+ CompositorElementId compositor_element_id,
+ const FloatPoint& paint_offset)
+ : PaintPropertyNode(std::move(parent)),
+ local_transform_space_(std::move(local_transform_space)),
+ output_clip_(std::move(output_clip)),
+ color_filter_(color_filter),
+ filter_(std::move(filter)),
+ opacity_(opacity),
+ blend_mode_(blend_mode),
+ direct_compositing_reasons_(direct_compositing_reasons),
+ compositor_element_id_(compositor_element_id),
+ paint_offset_(paint_offset) {}
+
+ // The local transform space serves two purposes:
+ // 1. Assign a depth mapping for 3D depth sorting against other paint chunks
+ // and effects under the same parent.
+ // 2. Some effects are spatial (namely blur filter and reflection), the
+ // effect parameters will be specified in the local space.
+ scoped_refptr<const TransformPaintPropertyNode> local_transform_space_;
+ // The output of the effect can be optionally clipped when composited onto
+ // the current backdrop.
+ scoped_refptr<const ClipPaintPropertyNode> output_clip_;
+
+ // Optionally a number of effects can be applied to the composited output.
+ // The chain of effects will be applied in the following order:
+ // === Begin of effects ===
+ ColorFilter color_filter_;
+ CompositorFilterOperations filter_;
+ float opacity_;
+ SkBlendMode blend_mode_;
+ // === End of effects ===
+
+ CompositingReasons direct_compositing_reasons_;
+ CompositorElementId compositor_element_id_;
+
+ // The offset of the effect's local space in m_localTransformSpace. Some
+ // effects e.g. reflection need this to apply geometry effects in the local
+ // space.
+ FloatPoint paint_offset_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_EFFECT_PAINT_PROPERTY_NODE_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/filter_display_item.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/filter_display_item.cc
new file mode 100644
index 00000000000..e1b0acd0078
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/filter_display_item.cc
@@ -0,0 +1,53 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/filter_display_item.h"
+
+#include "third_party/blink/public/platform/web_display_item_list.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+
+namespace blink {
+
+void BeginFilterDisplayItem::Replay(GraphicsContext& context) const {
+ FloatRect image_filter_bounds(bounds_);
+ image_filter_bounds.Move(-origin_.X(), -origin_.Y());
+ context.Save();
+ context.Translate(origin_.X(), origin_.Y());
+ context.BeginLayer(1, SkBlendMode::kSrcOver, &image_filter_bounds,
+ kColorFilterNone, image_filter_);
+ context.Translate(-origin_.X(), -origin_.Y());
+}
+
+void BeginFilterDisplayItem::AppendToWebDisplayItemList(
+ const FloatSize&,
+ WebDisplayItemList* list) const {
+ list->AppendFilterItem(compositor_filter_operations_.AsCcFilterOperations(),
+ bounds_, origin_);
+}
+
+bool BeginFilterDisplayItem::DrawsContent() const {
+ // Skia cannot currently tell us if a filter will draw content,
+ // even when no input primitives are drawn.
+ return true;
+}
+
+#if DCHECK_IS_ON()
+void BeginFilterDisplayItem::PropertiesAsJSON(JSONObject& json) const {
+ DisplayItem::PropertiesAsJSON(json);
+ json.SetString("filterBounds", bounds_.ToString());
+}
+#endif
+
+void EndFilterDisplayItem::Replay(GraphicsContext& context) const {
+ context.EndLayer();
+ context.Restore();
+}
+
+void EndFilterDisplayItem::AppendToWebDisplayItemList(
+ const FloatSize&,
+ WebDisplayItemList* list) const {
+ list->AppendEndFilterItem();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/filter_display_item.h b/chromium/third_party/blink/renderer/platform/graphics/paint/filter_display_item.h
new file mode 100644
index 00000000000..160eb05a3ed
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/filter_display_item.h
@@ -0,0 +1,77 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_FILTER_DISPLAY_ITEM_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_FILTER_DISPLAY_ITEM_H_
+
+#include <memory>
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/graphics/compositor_filter_operations.h"
+#include "third_party/blink/renderer/platform/graphics/paint/display_item.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT BeginFilterDisplayItem final
+ : public PairedBeginDisplayItem {
+ public:
+ BeginFilterDisplayItem(const DisplayItemClient& client,
+ sk_sp<PaintFilter> image_filter,
+ const FloatRect& bounds,
+ const FloatPoint& origin,
+ CompositorFilterOperations filter_operations)
+ : PairedBeginDisplayItem(client, kBeginFilter, sizeof(*this)),
+ image_filter_(std::move(image_filter)),
+ compositor_filter_operations_(std::move(filter_operations)),
+ bounds_(bounds),
+ origin_(origin) {}
+
+ void Replay(GraphicsContext&) const override;
+ void AppendToWebDisplayItemList(const FloatSize&,
+ WebDisplayItemList*) const override;
+ bool DrawsContent() const override;
+
+ private:
+#if DCHECK_IS_ON()
+ void PropertiesAsJSON(JSONObject&) const override;
+#endif
+ bool Equals(const DisplayItem& other) const final {
+ if (!DisplayItem::Equals(other))
+ return false;
+ const auto& other_item = static_cast<const BeginFilterDisplayItem&>(other);
+ // Ignores changes of reference filters because PaintFilter doesn't have
+ // an equality operator.
+ return bounds_ == other_item.bounds_ && origin_ == other_item.origin_ &&
+ compositor_filter_operations_.EqualsIgnoringReferenceFilters(
+ other_item.compositor_filter_operations_);
+ }
+
+ // FIXME: m_imageFilter should be replaced with m_webFilterOperations when
+ // copying data to the compositor.
+ sk_sp<PaintFilter> image_filter_;
+ CompositorFilterOperations compositor_filter_operations_;
+ const FloatRect bounds_;
+ const FloatPoint origin_;
+};
+
+class PLATFORM_EXPORT EndFilterDisplayItem final : public PairedEndDisplayItem {
+ public:
+ EndFilterDisplayItem(const DisplayItemClient& client)
+ : PairedEndDisplayItem(client, kEndFilter, sizeof(*this)) {}
+
+ void Replay(GraphicsContext&) const override;
+ void AppendToWebDisplayItemList(const FloatSize&,
+ WebDisplayItemList*) const override;
+
+ private:
+#if DCHECK_IS_ON()
+ bool IsEndAndPairedWith(DisplayItem::Type other_type) const final {
+ return other_type == kBeginFilter;
+ }
+#endif
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_FILTER_DISPLAY_ITEM_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/float_clip_display_item.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/float_clip_display_item.cc
new file mode 100644
index 00000000000..9818bc1e87c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/float_clip_display_item.cc
@@ -0,0 +1,41 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/float_clip_display_item.h"
+
+#include "third_party/blink/public/platform/web_display_item_list.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/skia/include/core/SkScalar.h"
+
+namespace blink {
+
+void FloatClipDisplayItem::Replay(GraphicsContext& context) const {
+ context.Save();
+ context.ClipRect(clip_rect_, kAntiAliased);
+}
+
+void FloatClipDisplayItem::AppendToWebDisplayItemList(
+ const FloatSize&,
+ WebDisplayItemList* list) const {
+ list->AppendFloatClipItem(clip_rect_);
+}
+
+void EndFloatClipDisplayItem::Replay(GraphicsContext& context) const {
+ context.Restore();
+}
+
+void EndFloatClipDisplayItem::AppendToWebDisplayItemList(
+ const FloatSize&,
+ WebDisplayItemList* list) const {
+ list->AppendEndFloatClipItem();
+}
+
+#if DCHECK_IS_ON()
+void FloatClipDisplayItem::PropertiesAsJSON(JSONObject& json) const {
+ DisplayItem::PropertiesAsJSON(json);
+ json.SetString("floatClipRect", clip_rect_.ToString());
+}
+#endif
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/float_clip_display_item.h b/chromium/third_party/blink/renderer/platform/graphics/paint/float_clip_display_item.h
new file mode 100644
index 00000000000..831955aba95
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/float_clip_display_item.h
@@ -0,0 +1,64 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_FLOAT_CLIP_DISPLAY_ITEM_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_FLOAT_CLIP_DISPLAY_ITEM_H_
+
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/graphics/paint/display_item.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT FloatClipDisplayItem final
+ : public PairedBeginDisplayItem {
+ public:
+ FloatClipDisplayItem(const DisplayItemClient& client,
+ Type type,
+ const FloatRect& clip_rect)
+ : PairedBeginDisplayItem(client, type, sizeof(*this)),
+ clip_rect_(clip_rect) {
+ DCHECK(IsFloatClipType(type));
+ }
+
+ void Replay(GraphicsContext&) const override;
+ void AppendToWebDisplayItemList(const FloatSize&,
+ WebDisplayItemList*) const override;
+
+ private:
+#if DCHECK_IS_ON()
+ void PropertiesAsJSON(JSONObject&) const override;
+#endif
+ bool Equals(const DisplayItem& other) const final {
+ return DisplayItem::Equals(other) &&
+ clip_rect_ ==
+ static_cast<const FloatClipDisplayItem&>(other).clip_rect_;
+ }
+
+ const FloatRect clip_rect_;
+};
+
+class PLATFORM_EXPORT EndFloatClipDisplayItem final
+ : public PairedEndDisplayItem {
+ public:
+ EndFloatClipDisplayItem(const DisplayItemClient& client, Type type)
+ : PairedEndDisplayItem(client, type, sizeof(*this)) {
+ DCHECK(IsEndFloatClipType(type));
+ }
+
+ void Replay(GraphicsContext&) const override;
+ void AppendToWebDisplayItemList(const FloatSize&,
+ WebDisplayItemList*) const override;
+
+ private:
+#if DCHECK_IS_ON()
+ bool IsEndAndPairedWith(DisplayItem::Type other_type) const final {
+ return DisplayItem::IsFloatClipType(other_type);
+ }
+#endif
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_FLOAT_CLIP_DISPLAY_ITEM_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/float_clip_rect.h b/chromium/third_party/blink/renderer/platform/graphics/paint/float_clip_rect.h
new file mode 100644
index 00000000000..a2eaac5b447
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/float_clip_rect.h
@@ -0,0 +1,112 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_FLOAT_CLIP_RECT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_FLOAT_CLIP_RECT_H_
+
+#include "third_party/blink/renderer/platform/geometry/float_quad.h"
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/geometry/float_rounded_rect.h"
+#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
+#include "third_party/blink/renderer/platform/transforms/transformation_matrix.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT FloatClipRect {
+ USING_FAST_MALLOC(FloatClipRect);
+
+ public:
+ FloatClipRect()
+ : rect_(FloatRect(LayoutRect::InfiniteIntRect())),
+ has_radius_(false),
+ is_tight_(true),
+ is_infinite_(true) {}
+
+ explicit FloatClipRect(const FloatRect& rect) { SetRect(rect); }
+
+ explicit FloatClipRect(const FloatRoundedRect& rect)
+ : rect_(rect.Rect()),
+ has_radius_(rect.IsRounded()),
+ is_tight_(!rect.IsRounded()),
+ is_infinite_(false) {}
+
+ const FloatRect& Rect() const { return rect_; }
+
+ FloatRect& Rect() { return rect_; }
+
+ void SetRect(const FloatRect& rect) {
+ rect_ = rect;
+ has_radius_ = false;
+ is_tight_ = true;
+ is_infinite_ = false;
+ }
+
+ void Intersect(const FloatClipRect& other) {
+ if (is_infinite_) {
+ is_infinite_ = other.is_infinite_;
+ rect_ = other.rect_;
+ } else {
+ rect_.Intersect(other.Rect());
+ }
+ if (other.HasRadius())
+ SetHasRadius();
+ else if (!other.IsTight())
+ ClearIsTight();
+ }
+
+ bool HasRadius() const { return has_radius_; }
+ void SetHasRadius() {
+ has_radius_ = true;
+ is_tight_ = false;
+ is_infinite_ = false;
+ }
+
+ // The rect is tight means that the rect covers only clipped result and
+ // nothing else.
+ bool IsTight() const {
+ DCHECK(!is_tight_ || !has_radius_);
+ return is_tight_;
+ }
+ void ClearIsTight() { is_tight_ = false; }
+
+ void MoveBy(const FloatPoint& offset) {
+ if (is_infinite_)
+ return;
+ rect_.MoveBy(offset);
+ }
+
+ void Map(const TransformationMatrix& matrix) {
+ if (is_tight_ && !matrix.IsIdentityOr2DTranslation())
+ is_tight_ = false;
+ if (is_infinite_)
+ return;
+ rect_ = matrix.MapRect(rect_);
+ }
+
+ bool IsInfinite() const { return is_infinite_; }
+
+ private:
+ FloatRect rect_;
+ bool has_radius_ : 1;
+ bool is_tight_ : 1;
+ bool is_infinite_ : 1;
+};
+
+inline bool operator==(const FloatClipRect& a, const FloatClipRect& b) {
+ if (a.IsTight() != b.IsTight())
+ return false;
+ if (a.IsInfinite() && b.IsInfinite())
+ return true;
+ return !a.IsInfinite() && !b.IsInfinite() && a.HasRadius() == b.HasRadius() &&
+ a.Rect() == b.Rect();
+}
+
+inline bool operator!=(const FloatClipRect& a, const FloatClipRect& b) {
+ return !(a == b);
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_FLOAT_CLIP_RECT_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/float_clip_rect_test.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/float_clip_rect_test.cc
new file mode 100644
index 00000000000..dd19c4cd3d1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/float_clip_rect_test.cc
@@ -0,0 +1,106 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/float_clip_rect.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+
+namespace blink {
+
+class FloatClipRectTest : public testing::Test {
+ public:
+};
+
+TEST_F(FloatClipRectTest, InfinitRect) {
+ FloatClipRect rect;
+ EXPECT_TRUE(rect.IsInfinite());
+ EXPECT_FALSE(rect.HasRadius());
+ EXPECT_TRUE(rect.IsTight());
+
+ FloatClipRect rect2((FloatRect(1, 2, 3, 4)));
+ EXPECT_FALSE(rect2.IsInfinite());
+ EXPECT_FALSE(rect.HasRadius());
+ EXPECT_TRUE(rect.IsTight());
+}
+
+TEST_F(FloatClipRectTest, MoveBy) {
+ FloatClipRect rect;
+ rect.MoveBy(FloatPoint(1, 2));
+ EXPECT_EQ(rect.Rect(), FloatClipRect().Rect());
+ EXPECT_TRUE(rect.IsInfinite());
+ EXPECT_FALSE(rect.HasRadius());
+ EXPECT_TRUE(rect.IsTight());
+
+ FloatClipRect rect2((FloatRect(1, 2, 3, 4)));
+ rect2.SetHasRadius();
+ rect2.MoveBy(FloatPoint(5, 6));
+ EXPECT_EQ(FloatRect(6, 8, 3, 4), rect2.Rect());
+ EXPECT_TRUE(rect2.HasRadius());
+ EXPECT_FALSE(rect2.IsTight());
+}
+
+TEST_F(FloatClipRectTest, Intersect) {
+ FloatClipRect rect;
+ FloatClipRect rect1(FloatRect(1, 2, 3, 4));
+ FloatClipRect rect2(FloatRect(3, 4, 5, 6));
+ rect2.SetHasRadius();
+
+ rect.Intersect(rect1);
+ EXPECT_FALSE(rect.IsInfinite());
+ EXPECT_EQ(FloatRect(1, 2, 3, 4), rect.Rect());
+ EXPECT_FALSE(rect.HasRadius());
+ EXPECT_TRUE(rect.IsTight());
+
+ rect.Intersect(rect2);
+ EXPECT_FALSE(rect.IsInfinite());
+ EXPECT_EQ(FloatRect(3, 4, 1, 2), rect.Rect());
+ EXPECT_TRUE(rect.HasRadius());
+ EXPECT_FALSE(rect.IsTight());
+}
+
+TEST_F(FloatClipRectTest, SetHasRadius) {
+ FloatClipRect rect;
+ rect.SetHasRadius();
+ EXPECT_FALSE(rect.IsInfinite());
+ EXPECT_TRUE(rect.HasRadius());
+ EXPECT_FALSE(rect.IsTight());
+}
+
+TEST_F(FloatClipRectTest, ClearIsTight) {
+ FloatClipRect rect;
+ rect.ClearIsTight();
+ EXPECT_TRUE(rect.IsInfinite());
+ EXPECT_FALSE(rect.HasRadius());
+ EXPECT_FALSE(rect.IsTight());
+}
+
+TEST_F(FloatClipRectTest, Map) {
+ FloatClipRect rect;
+ TransformationMatrix identity;
+ TransformationMatrix translation = TransformationMatrix().Translate(10, 20);
+ TransformationMatrix rotate = TransformationMatrix().Rotate(45);
+
+ rect.Map(rotate);
+ EXPECT_TRUE(rect.IsInfinite());
+ EXPECT_FALSE(rect.IsTight());
+
+ FloatClipRect rect2(FloatRect(1, 2, 3, 4));
+ rect2.Map(identity);
+ EXPECT_EQ(FloatRect(1, 2, 3, 4), rect2.Rect());
+ EXPECT_TRUE(rect2.IsTight());
+
+ rect2.Map(translation);
+ EXPECT_EQ(FloatRect(11, 22, 3, 4), rect2.Rect());
+ EXPECT_TRUE(rect2.IsTight());
+
+ rect2.Map(rotate);
+ EXPECT_EQ(rotate.MapRect(FloatRect(11, 22, 3, 4)), rect2.Rect());
+ EXPECT_FALSE(rect2.IsTight());
+
+ rect2.Map(identity);
+ EXPECT_FALSE(rect2.IsTight());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/foreign_layer_display_item.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/foreign_layer_display_item.cc
new file mode 100644
index 00000000000..3a06f64ede8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/foreign_layer_display_item.cc
@@ -0,0 +1,73 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/foreign_layer_display_item.h"
+
+#include <utility>
+#include "cc/layers/layer.h"
+#include "third_party/blink/public/platform/web_layer.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+namespace blink {
+
+ForeignLayerDisplayItem::ForeignLayerDisplayItem(
+ const DisplayItemClient& client,
+ Type type,
+ scoped_refptr<cc::Layer> layer,
+ const FloatPoint& location,
+ const IntSize& bounds)
+ : DisplayItem(client, type, sizeof(*this)),
+ layer_(std::move(layer)),
+ location_(location),
+ bounds_(bounds) {
+ DCHECK(RuntimeEnabledFeatures::SlimmingPaintV2Enabled());
+ DCHECK(IsForeignLayerType(type));
+ DCHECK(layer_);
+}
+
+ForeignLayerDisplayItem::~ForeignLayerDisplayItem() = default;
+
+void ForeignLayerDisplayItem::Replay(GraphicsContext&) const {
+ NOTREACHED();
+}
+
+void ForeignLayerDisplayItem::AppendToWebDisplayItemList(
+ const FloatSize&,
+ WebDisplayItemList*) const {
+ NOTREACHED();
+}
+
+bool ForeignLayerDisplayItem::DrawsContent() const {
+ return true;
+}
+
+bool ForeignLayerDisplayItem::Equals(const DisplayItem& other) const {
+ return DisplayItem::Equals(other) &&
+ layer_ == static_cast<const ForeignLayerDisplayItem&>(other).layer_;
+}
+
+#if DCHECK_IS_ON()
+void ForeignLayerDisplayItem::PropertiesAsJSON(JSONObject& json) const {
+ DisplayItem::PropertiesAsJSON(json);
+ json.SetInteger("layer", layer_->id());
+}
+#endif
+
+void RecordForeignLayer(GraphicsContext& context,
+ const DisplayItemClient& client,
+ DisplayItem::Type type,
+ WebLayer* web_layer,
+ const FloatPoint& location,
+ const IntSize& bounds) {
+ PaintController& paint_controller = context.GetPaintController();
+ if (paint_controller.DisplayItemConstructionIsDisabled())
+ return;
+
+ paint_controller.CreateAndAppend<ForeignLayerDisplayItem>(
+ client, type, web_layer->CcLayer(), location, bounds);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/foreign_layer_display_item.h b/chromium/third_party/blink/renderer/platform/graphics/paint/foreign_layer_display_item.h
new file mode 100644
index 00000000000..b8dec3d94f6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/foreign_layer_display_item.h
@@ -0,0 +1,67 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_FOREIGN_LAYER_DISPLAY_ITEM_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_FOREIGN_LAYER_DISPLAY_ITEM_H_
+
+#include "base/memory/ref_counted.h"
+#include "third_party/blink/renderer/platform/graphics/paint/display_item.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace cc {
+class Layer;
+}
+
+namespace blink {
+
+class GraphicsContext;
+class WebLayer;
+
+// Represents foreign content (produced outside Blink) which draws to a layer.
+// A client supplies a layer which can be unwrapped and inserted into the full
+// layer tree.
+//
+// Before SPv2, this content is not painted, but is instead inserted into the
+// GraphicsLayer tree.
+class PLATFORM_EXPORT ForeignLayerDisplayItem final : public DisplayItem {
+ public:
+ ForeignLayerDisplayItem(const DisplayItemClient&,
+ Type,
+ scoped_refptr<cc::Layer>,
+ const FloatPoint& location,
+ const IntSize& bounds);
+ ~ForeignLayerDisplayItem();
+
+ cc::Layer* GetLayer() const { return layer_.get(); }
+ const FloatPoint& Location() const { return location_; }
+ const IntSize& Bounds() const { return bounds_; }
+
+ // DisplayItem
+ void Replay(GraphicsContext&) const override;
+ void AppendToWebDisplayItemList(const FloatSize&,
+ WebDisplayItemList*) const override;
+ bool DrawsContent() const override;
+ bool Equals(const DisplayItem&) const override;
+#if DCHECK_IS_ON()
+ void PropertiesAsJSON(JSONObject&) const override;
+#endif
+
+ private:
+ scoped_refptr<cc::Layer> layer_;
+ FloatPoint location_;
+ IntSize bounds_;
+};
+
+// Records a foreign layer into a GraphicsContext.
+// Use this where you would use a recorder class.
+PLATFORM_EXPORT void RecordForeignLayer(GraphicsContext&,
+ const DisplayItemClient&,
+ DisplayItem::Type,
+ WebLayer*,
+ const FloatPoint& location,
+ const IntSize& bounds);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_FOREIGN_LAYER_DISPLAY_ITEM_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper.cc
new file mode 100644
index 00000000000..bccae25dbef
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper.cc
@@ -0,0 +1,372 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h"
+
+#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+
+namespace blink {
+
+const TransformationMatrix& GeometryMapper::SourceToDestinationProjection(
+ const TransformPaintPropertyNode* source,
+ const TransformPaintPropertyNode* destination) {
+ DCHECK(source && destination);
+ bool success = false;
+ const auto& result =
+ SourceToDestinationProjectionInternal(source, destination, success);
+ return result;
+}
+
+// Returns flatten(destination_to_screen)^-1 * flatten(source_to_screen)
+//
+// In case that source and destination are coplanar in tree hierarchy [1],
+// computes destination_to_plane_root ^ -1 * source_to_plane_root.
+// It can be proved that [2] the result will be the same (except numerical
+// errors) when the plane root has invertible screen projection, and this
+// offers fallback definition when plane root is singular. For example:
+// <div style="transform:rotateY(90deg); overflow:scroll;">
+// <div id="A" style="opacity:0.5;">
+// <div id="B" style="position:absolute;"></div>
+// </div>
+// </div>
+// Both A and B have non-invertible screen projection, nevertheless it is
+// useful to define projection between A and B. Say, the transform may be
+// animated in compositor thus become visible.
+// As SPv1 treats 3D transforms as compositing trigger, that implies mappings
+// within the same compositing layer can only contain 2D transforms, thus
+// intra-composited-layer queries are guaranteed to be handled correctly.
+//
+// [1] As defined by that all local transforms between source and some common
+// ancestor 'plane root' and all local transforms between the destination
+// and the plane root being flat.
+// [2] destination_to_screen = plane_root_to_screen * destination_to_plane_root
+// source_to_screen = plane_root_to_screen * source_to_plane_root
+// output = flatten(destination_to_screen)^-1 * flatten(source_to_screen)
+// = flatten(plane_root_to_screen * destination_to_plane_root)^-1 *
+// flatten(plane_root_to_screen * source_to_plane_root)
+// Because both destination_to_plane_root and source_to_plane_root are
+// already flat,
+// = flatten(plane_root_to_screen * flatten(destination_to_plane_root))^-1 *
+// flatten(plane_root_to_screen * flatten(source_to_plane_root))
+// By flatten lemma [3] flatten(A * flatten(B)) = flatten(A) * flatten(B),
+// = flatten(destination_to_plane_root)^-1 *
+// flatten(plane_root_to_screen)^-1 *
+// flatten(plane_root_to_screen) * flatten(source_to_plane_root)
+// If flatten(plane_root_to_screen) is invertible, they cancel out:
+// = flatten(destination_to_plane_root)^-1 * flatten(source_to_plane_root)
+// = destination_to_plane_root^-1 * source_to_plane_root
+// [3] Flatten lemma: https://goo.gl/DNKyOc
+const TransformationMatrix&
+GeometryMapper::SourceToDestinationProjectionInternal(
+ const TransformPaintPropertyNode* source,
+ const TransformPaintPropertyNode* destination,
+ bool& success) {
+ DCHECK(source && destination);
+ DEFINE_STATIC_LOCAL(TransformationMatrix, identity, (TransformationMatrix()));
+ DEFINE_STATIC_LOCAL(TransformationMatrix, temp, (TransformationMatrix()));
+
+ if (source == destination) {
+ success = true;
+ return identity;
+ }
+
+ const GeometryMapperTransformCache& source_cache =
+ source->GetTransformCache();
+ const GeometryMapperTransformCache& destination_cache =
+ destination->GetTransformCache();
+
+ // Case 1: Check if source and destination are known to be coplanar.
+ // Even if destination may have invertible screen projection,
+ // this formula is likely to be numerically more stable.
+ if (source_cache.plane_root() == destination_cache.plane_root()) {
+ success = true;
+ if (source == destination_cache.plane_root())
+ return destination_cache.from_plane_root();
+ if (destination == source_cache.plane_root())
+ return source_cache.to_plane_root();
+ temp = destination_cache.from_plane_root();
+ temp.Multiply(source_cache.to_plane_root());
+ return temp;
+ }
+
+ // Case 2: Check if we can fallback to the canonical definition of
+ // flatten(destination_to_screen)^-1 * flatten(source_to_screen)
+ // If flatten(destination_to_screen)^-1 is invalid, we are out of luck.
+ if (!destination_cache.projection_from_screen_is_valid()) {
+ success = false;
+ return identity;
+ }
+
+ // Case 3: Compute:
+ // flatten(destination_to_screen)^-1 * flatten(source_to_screen)
+ const auto* root = TransformPaintPropertyNode::Root();
+ success = true;
+ if (source == root)
+ return destination_cache.projection_from_screen();
+ if (destination == root) {
+ temp = source_cache.to_screen();
+ } else {
+ temp = destination_cache.projection_from_screen();
+ temp.Multiply(source_cache.to_screen());
+ }
+ temp.FlattenTo2d();
+ return temp;
+}
+
+void GeometryMapper::SourceToDestinationRect(
+ const TransformPaintPropertyNode* source_transform_node,
+ const TransformPaintPropertyNode* destination_transform_node,
+ FloatRect& mapping_rect) {
+ bool success = false;
+ const TransformationMatrix& source_to_destination =
+ SourceToDestinationProjectionInternal(
+ source_transform_node, destination_transform_node, success);
+ mapping_rect =
+ success ? source_to_destination.MapRect(mapping_rect) : FloatRect();
+}
+
+void GeometryMapper::LocalToAncestorVisualRect(
+ const PropertyTreeState& local_state,
+ const PropertyTreeState& ancestor_state,
+ FloatClipRect& mapping_rect,
+ OverlayScrollbarClipBehavior clip_behavior) {
+ bool success = false;
+ LocalToAncestorVisualRectInternal(local_state, ancestor_state, mapping_rect,
+ clip_behavior, success);
+ DCHECK(success);
+}
+
+bool GeometryMapper::PointVisibleInAncestorSpace(
+ const PropertyTreeState& local_state,
+ const PropertyTreeState& ancestor_state,
+ const FloatPoint& local_point) {
+ for (const auto* clip = local_state.Clip();
+ clip && clip != ancestor_state.Clip(); clip = clip->Parent()) {
+ FloatPoint mapped_point =
+ SourceToDestinationProjection(local_state.Transform(),
+ clip->LocalTransformSpace())
+ .MapPoint(local_point);
+
+ if (!clip->ClipRect().IntersectsQuad(
+ FloatRect(mapped_point, FloatSize(1, 1))))
+ return false;
+
+ if (clip->ClipPath() && !clip->ClipPath()->Contains(mapped_point))
+ return false;
+ }
+
+ return true;
+}
+
+void GeometryMapper::LocalToAncestorVisualRectInternal(
+ const PropertyTreeState& local_state,
+ const PropertyTreeState& ancestor_state,
+ FloatClipRect& rect_to_map,
+ OverlayScrollbarClipBehavior clip_behavior,
+ bool& success) {
+ if (local_state == ancestor_state) {
+ success = true;
+ return;
+ }
+
+ if (local_state.Effect() != ancestor_state.Effect()) {
+ SlowLocalToAncestorVisualRectWithEffects(
+ local_state, ancestor_state, rect_to_map, clip_behavior, success);
+ return;
+ }
+
+ const auto& transform_matrix = SourceToDestinationProjectionInternal(
+ local_state.Transform(), ancestor_state.Transform(), success);
+ if (!success) {
+ // A failure implies either source-to-plane or destination-to-plane being
+ // singular. A notable example of singular source-to-plane from valid CSS:
+ // <div id="plane" style="transform:rotateY(180deg)">
+ // <div style="overflow:overflow">
+ // <div id="ancestor" style="opacity:0.5;">
+ // <div id="local" style="position:absolute; transform:scaleX(0);">
+ // </div>
+ // </div>
+ // </div>
+ // </div>
+ // Either way, the element won't be renderable thus returning empty rect.
+ success = true;
+ rect_to_map = FloatClipRect(FloatRect());
+ return;
+ }
+ rect_to_map.Map(transform_matrix);
+
+ FloatClipRect clip_rect = LocalToAncestorClipRectInternal(
+ local_state.Clip(), ancestor_state.Clip(), ancestor_state.Transform(),
+ clip_behavior, success);
+ if (success) {
+ // This is where we propagate the roundedness and tightness of |clip_rect|
+ // to |rect_to_map|.
+ rect_to_map.Intersect(clip_rect);
+ return;
+ }
+
+ if (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
+ // On SPv1* we may fail when the paint invalidation container creates an
+ // overflow clip (in ancestor_state) which is not in localState of an
+ // out-of-flow positioned descendant. See crbug.com/513108 and layout test
+ // compositing/overflow/handle-non-ancestor-clip-parent.html (run with
+ // --enable-prefer-compositing-to-lcd-text) for details.
+ // Ignore it for SPv1* for now.
+ success = true;
+ rect_to_map.ClearIsTight();
+ }
+}
+
+void GeometryMapper::SlowLocalToAncestorVisualRectWithEffects(
+ const PropertyTreeState& local_state,
+ const PropertyTreeState& ancestor_state,
+ FloatClipRect& mapping_rect,
+ OverlayScrollbarClipBehavior clip_behavior,
+ bool& success) {
+ PropertyTreeState last_transform_and_clip_state(local_state.Transform(),
+ local_state.Clip(), nullptr);
+
+ for (const auto* effect = local_state.Effect();
+ effect && effect != ancestor_state.Effect(); effect = effect->Parent()) {
+ if (!effect->HasFilterThatMovesPixels())
+ continue;
+
+ DCHECK(effect->OutputClip());
+ PropertyTreeState transform_and_clip_state(effect->LocalTransformSpace(),
+ effect->OutputClip(), nullptr);
+ LocalToAncestorVisualRectInternal(last_transform_and_clip_state,
+ transform_and_clip_state, mapping_rect,
+ clip_behavior, success);
+ if (!success) {
+ success = true;
+ mapping_rect = FloatClipRect(FloatRect());
+ return;
+ }
+
+ mapping_rect = FloatClipRect(effect->MapRect(mapping_rect.Rect()));
+ last_transform_and_clip_state = transform_and_clip_state;
+ }
+
+ PropertyTreeState final_transform_and_clip_state(
+ ancestor_state.Transform(), ancestor_state.Clip(), nullptr);
+ LocalToAncestorVisualRectInternal(last_transform_and_clip_state,
+ final_transform_and_clip_state,
+ mapping_rect, clip_behavior, success);
+
+ // Many effects (e.g. filters, clip-paths) can make a clip rect not tight.
+ mapping_rect.ClearIsTight();
+}
+
+FloatClipRect GeometryMapper::LocalToAncestorClipRect(
+ const PropertyTreeState& local_state,
+ const PropertyTreeState& ancestor_state,
+ OverlayScrollbarClipBehavior clip_behavior) {
+ if (local_state.Clip() == ancestor_state.Clip())
+ return FloatClipRect();
+
+ bool success = false;
+ auto result = LocalToAncestorClipRectInternal(
+ local_state.Clip(), ancestor_state.Clip(), ancestor_state.Transform(),
+ clip_behavior, success);
+ DCHECK(success);
+
+ // Many effects (e.g. filters, clip-paths) can make a clip rect not tight.
+ if (local_state.Effect() != ancestor_state.Effect())
+ result.ClearIsTight();
+
+ return result;
+}
+
+static const FloatRoundedRect& GetClipRect(
+ const ClipPaintPropertyNode* clip_node,
+ OverlayScrollbarClipBehavior clip_behavior) {
+ return UNLIKELY(clip_behavior == kExcludeOverlayScrollbarSizeForHitTesting)
+ ? clip_node->ClipRectExcludingOverlayScrollbars()
+ : clip_node->ClipRect();
+}
+
+FloatClipRect GeometryMapper::LocalToAncestorClipRectInternal(
+ const ClipPaintPropertyNode* descendant,
+ const ClipPaintPropertyNode* ancestor_clip,
+ const TransformPaintPropertyNode* ancestor_transform,
+ OverlayScrollbarClipBehavior clip_behavior,
+ bool& success) {
+ if (descendant == ancestor_clip) {
+ success = true;
+ return FloatClipRect();
+ }
+
+ if (descendant->Parent() == ancestor_clip &&
+ descendant->LocalTransformSpace() == ancestor_transform) {
+ success = true;
+ return FloatClipRect(GetClipRect(descendant, clip_behavior));
+ }
+
+ FloatClipRect clip;
+ const ClipPaintPropertyNode* clip_node = descendant;
+ Vector<const ClipPaintPropertyNode*> intermediate_nodes;
+
+ GeometryMapperClipCache::ClipAndTransform clip_and_transform(
+ ancestor_clip, ancestor_transform, clip_behavior);
+ // Iterate over the path from localState.clip to ancestor_state.clip. Stop if
+ // we've found a memoized (precomputed) clip for any particular node.
+ while (clip_node && clip_node != ancestor_clip) {
+ if (const FloatClipRect* cached_clip =
+ clip_node->GetClipCache().GetCachedClip(clip_and_transform)) {
+ clip = *cached_clip;
+ break;
+ }
+
+ intermediate_nodes.push_back(clip_node);
+ clip_node = clip_node->Parent();
+ }
+ if (!clip_node) {
+ success = false;
+ if (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
+ // On SPv1 we may fail when the paint invalidation container creates an
+ // overflow clip (in ancestor_state) which is not in localState of an
+ // out-of-flow positioned descendant. See crbug.com/513108 and layout
+ // test compositing/overflow/handle-non-ancestor-clip-parent.html (run
+ // with --enable-prefer-compositing-to-lcd-text) for details.
+ // Ignore it for SPv1 for now.
+ success = true;
+ }
+ FloatClipRect loose_infinite;
+ loose_infinite.ClearIsTight();
+ return loose_infinite;
+ }
+
+ // Iterate down from the top intermediate node found in the previous loop,
+ // computing and memoizing clip rects as we go.
+ for (auto it = intermediate_nodes.rbegin(); it != intermediate_nodes.rend();
+ ++it) {
+ const TransformationMatrix& transform_matrix =
+ SourceToDestinationProjectionInternal((*it)->LocalTransformSpace(),
+ ancestor_transform, success);
+ if (!success) {
+ success = true;
+ return FloatClipRect(FloatRect());
+ }
+
+ // This is where we generate the roundedness and tightness of clip rect
+ // from clip and transform properties, and propagate them to |clip|.
+ FloatClipRect mapped_rect(GetClipRect((*it), clip_behavior));
+ mapped_rect.Map(transform_matrix);
+ clip.Intersect(mapped_rect);
+
+ (*it)->GetClipCache().SetCachedClip(clip_and_transform, clip);
+ }
+
+ DCHECK(*descendant->GetClipCache().GetCachedClip(clip_and_transform) == clip);
+ success = true;
+ return clip;
+}
+
+void GeometryMapper::ClearCache() {
+ GeometryMapperTransformCache::ClearCache();
+ GeometryMapperClipCache::ClearCache();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h b/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h
new file mode 100644
index 00000000000..6d23bbd2c51
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h
@@ -0,0 +1,152 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_GEOMETRY_MAPPER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_GEOMETRY_MAPPER_H_
+
+#include "third_party/blink/renderer/platform/graphics/paint/float_clip_rect.h"
+#include "third_party/blink/renderer/platform/graphics/paint/property_tree_state.h"
+#include "third_party/blink/renderer/platform/scroll/scroll_types.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+
+namespace blink {
+
+class TransformationMatrix;
+
+// GeometryMapper is a helper class for fast computations of transformed and
+// visual rects in different PropertyTreeStates. The design document has a
+// number of details on use cases, algorithmic definitions, and running times.
+//
+// NOTE: A GeometryMapper object is only valid for property trees that do not
+// change. If any mutation occurs, a new GeometryMapper object must be allocated
+// corresponding to the new state.
+//
+// ** WARNING** Callers to the methods below may not assume that any const
+// references returned remain const across multiple calls into GeometryMapper.
+// If needed, callers must store local copies of the return values.
+//
+// Design document: http://bit.ly/28P4FDA
+//
+// TODO(chrishtr): take effect tree into account.
+class PLATFORM_EXPORT GeometryMapper {
+ STATIC_ONLY(GeometryMapper);
+
+ public:
+ // Returns the matrix that is suitable to map geometries on the source plane
+ // to some backing in the destination plane.
+ // Formal definition:
+ // output = flatten(destination_to_screen)^-1 * flatten(source_to_screen)
+ // There are some cases that flatten(destination_to_screen) being
+ // singular yet we can still define a reasonable projection, for example:
+ // 1. Both nodes inherited a common singular flat ancestor:
+ // 2. Both nodes are co-planar to a common singular ancestor:
+ // Not every cases outlined above are supported!
+ // Read implementation comments for specific restrictions.
+ static const TransformationMatrix& SourceToDestinationProjection(
+ const TransformPaintPropertyNode* source,
+ const TransformPaintPropertyNode* destination);
+
+ // Same as SourceToDestinationProjection() except that it maps the rect
+ // rather than returning the matrix.
+ // |mapping_rect| is both input and output.
+ static void SourceToDestinationRect(
+ const TransformPaintPropertyNode* source_transform_node,
+ const TransformPaintPropertyNode* destination_transform_node,
+ FloatRect& mapping_rect);
+
+ // Returns the clip rect between |local_state| and |ancestor_state|. The clip
+ // rect is the total clip rect that should be applied when painting contents
+ // of |local_state| in |ancestor_state| space. Because this clip rect applies
+ // on contents of |local_state|, it's not affected by any effect nodes between
+ // |local_state| and |ancestor_state|.
+ //
+ // Note that the clip of |ancestor_state| is *not* applied.
+ //
+ // The output FloatClipRect may contain false positives for rounded-ness
+ // if a rounded clip is clipped out, and overly conservative results
+ // in the presences of transforms.
+ static FloatClipRect LocalToAncestorClipRect(
+ const PropertyTreeState& local_state,
+ const PropertyTreeState& ancestor_state,
+ OverlayScrollbarClipBehavior = kIgnorePlatformOverlayScrollbarSize);
+
+ // Maps from a rect in |local_state| to its visual rect in |ancestor_state|.
+ // If there is no effect node between |local_state| (included) and
+ // |ancestor_state| (not included), the result is computed by multiplying the
+ // rect by its combined transform between |local_state| and |ancestor_space|,
+ // then flattening into 2D space, then intersecting by the clip for
+ // |local_state|'s clips. If there are any pixel-moving effect nodes between
+ // |local_state| and |ancestor_state|, for each segment of states separated
+ // by the effect nodes, we'll execute the above process and map the result
+ // rect with the effect.
+ //
+ // Note that the clip of |ancestor_state| is *not* applied.
+ //
+ // DCHECK fails if any of the paint property tree nodes in |local_state| are
+ // not equal to or a descendant of that in |ancestor_state|.
+ //
+ // |mapping_rect| is both input and output.
+ //
+ // The output FloatClipRect may contain false positives for rounded-ness
+ // if a rounded clip is clipped out, and overly conservative results
+ // in the presences of transforms.
+ static void LocalToAncestorVisualRect(
+ const PropertyTreeState& local_state,
+ const PropertyTreeState& ancestor_state,
+ FloatClipRect& mapping_rect,
+ OverlayScrollbarClipBehavior = kIgnorePlatformOverlayScrollbarSize);
+
+ // Returns true if |local_rect| is *not* clipped out by any clips
+ // between |local_state| and |ancestor_state|. This includes not just
+ // rectangular clips but rounded clips, and any clip paths stored on the
+ // ClipPaintPropertyNodes. It does *not* include any "complex" clips (see
+ // LayoutSVGResourceClipper::AsPath for the implementation of the heuristic
+ // which differentiates "simple" from "complex".
+ static bool PointVisibleInAncestorSpace(
+ const PropertyTreeState& local_state,
+ const PropertyTreeState& ancestor_state,
+ const FloatPoint& local_point);
+
+ static void ClearCache();
+
+ private:
+ // The internal methods do the same things as their public counterparts, but
+ // take an extra |success| parameter which indicates if the function is
+ // successful on return. See comments of the public functions for failure
+ // conditions.
+
+ static const TransformationMatrix& SourceToDestinationProjectionInternal(
+ const TransformPaintPropertyNode* source,
+ const TransformPaintPropertyNode* destination,
+ bool& success);
+
+ static FloatClipRect LocalToAncestorClipRectInternal(
+ const ClipPaintPropertyNode* descendant,
+ const ClipPaintPropertyNode* ancestor_clip,
+ const TransformPaintPropertyNode* ancestor_transform,
+ OverlayScrollbarClipBehavior,
+ bool& success);
+
+ static void LocalToAncestorVisualRectInternal(
+ const PropertyTreeState& local_state,
+ const PropertyTreeState& ancestor_state,
+ FloatClipRect& mapping_rect,
+ OverlayScrollbarClipBehavior,
+ bool& success);
+
+ static void SlowLocalToAncestorVisualRectWithEffects(
+ const PropertyTreeState& local_state,
+ const PropertyTreeState& ancestor_state,
+ FloatClipRect& mapping_rect,
+ OverlayScrollbarClipBehavior,
+ bool& success);
+
+ friend class GeometryMapperTest;
+ friend class PaintLayerClipperTest;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_GEOMETRY_MAPPER_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_clip_cache.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_clip_cache.cc
new file mode 100644
index 00000000000..ca1a308ccd7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_clip_cache.cc
@@ -0,0 +1,54 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper_clip_cache.h"
+
+#include "third_party/blink/renderer/platform/graphics/paint/float_clip_rect.h"
+
+namespace blink {
+
+// All clip caches invalidate themselves by tracking a local cache generation,
+// and invalidating their cache if their cache generation disagrees with
+// s_clipCacheGeneration.
+static unsigned g_clip_cache_generation = 0;
+
+GeometryMapperClipCache::GeometryMapperClipCache()
+ : cache_generation_(g_clip_cache_generation) {}
+
+void GeometryMapperClipCache::ClearCache() {
+ g_clip_cache_generation++;
+}
+
+void GeometryMapperClipCache::InvalidateCacheIfNeeded() {
+ if (cache_generation_ != g_clip_cache_generation) {
+ clip_cache_.clear();
+ cache_generation_ = g_clip_cache_generation;
+ }
+}
+
+const FloatClipRect* GeometryMapperClipCache::GetCachedClip(
+ const ClipAndTransform& clip_and_transform) {
+ InvalidateCacheIfNeeded();
+ for (const auto& entry : clip_cache_) {
+ if (entry.clip_and_transform == clip_and_transform) {
+ return &entry.clip_rect;
+ }
+ }
+ return nullptr;
+}
+
+void GeometryMapperClipCache::SetCachedClip(
+ const ClipAndTransform& clip_and_transform,
+ const FloatClipRect& clip) {
+ InvalidateCacheIfNeeded();
+#if DCHECK_IS_ON()
+ for (const auto& entry : clip_cache_) {
+ if (entry.clip_and_transform == clip_and_transform)
+ DCHECK(false); // There should be no existing entry.
+ }
+#endif
+ clip_cache_.push_back(ClipCacheEntry(clip_and_transform, clip));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_clip_cache.h b/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_clip_cache.h
new file mode 100644
index 00000000000..35215bee343
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_clip_cache.h
@@ -0,0 +1,76 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_GEOMETRY_MAPPER_CLIP_CACHE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_GEOMETRY_MAPPER_CLIP_CACHE_H_
+
+#include "third_party/blink/renderer/platform/graphics/paint/float_clip_rect.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/scroll/scroll_types.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class ClipPaintPropertyNode;
+class FloatClipRect;
+class TransformPaintPropertyNode;
+
+// A GeometryMapperClipCache hangs off a ClipPaintPropertyNode. It stores
+// cached "clip visual rects" (See GeometryMapper.h) from that node in
+// ancestor spaces.
+class PLATFORM_EXPORT GeometryMapperClipCache {
+ USING_FAST_MALLOC(GeometryMapperClipCache);
+
+ public:
+ GeometryMapperClipCache();
+
+ struct ClipAndTransform {
+ const ClipPaintPropertyNode* ancestor_clip;
+ const TransformPaintPropertyNode* ancestor_transform;
+ OverlayScrollbarClipBehavior clip_behavior;
+ bool operator==(const ClipAndTransform& other) const {
+ return ancestor_clip == other.ancestor_clip &&
+ ancestor_transform == other.ancestor_transform &&
+ clip_behavior == other.clip_behavior;
+ }
+ ClipAndTransform(const ClipPaintPropertyNode* ancestor_clip_arg,
+ const TransformPaintPropertyNode* ancestor_transform_arg,
+ OverlayScrollbarClipBehavior clip_behavior_arg)
+ : ancestor_clip(ancestor_clip_arg),
+ ancestor_transform(ancestor_transform_arg),
+ clip_behavior(clip_behavior_arg) {}
+ };
+
+ // Returns the clip visual rect of the owning
+ // clip of |this| in the space of |ancestors|, if there is one cached.
+ // Otherwise returns null.
+ const FloatClipRect* GetCachedClip(const ClipAndTransform& ancestors);
+
+ // Stores the "clip visual rect" of |this in the space of |ancestors|,
+ // into a local cache.
+ void SetCachedClip(const ClipAndTransform&, const FloatClipRect&);
+
+ static void ClearCache();
+
+ private:
+ struct ClipCacheEntry {
+ const ClipAndTransform clip_and_transform;
+ const FloatClipRect clip_rect;
+ ClipCacheEntry(const ClipAndTransform& clip_and_transform_arg,
+ const FloatClipRect& clip_rect_arg)
+ : clip_and_transform(clip_and_transform_arg),
+ clip_rect(clip_rect_arg) {}
+ };
+
+ void InvalidateCacheIfNeeded();
+
+ Vector<ClipCacheEntry> clip_cache_;
+ unsigned cache_generation_;
+
+ DISALLOW_COPY_AND_ASSIGN(GeometryMapperClipCache);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_GEOMETRY_MAPPER_CLIP_CACHE_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_test.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_test.cc
new file mode 100644
index 00000000000..dc38a49cb36
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_test.cc
@@ -0,0 +1,846 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/geometry/geometry_test_helpers.h"
+#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
+#include "third_party/blink/renderer/platform/graphics/box_reflection.h"
+#include "third_party/blink/renderer/platform/graphics/filters/paint_filter_builder.h"
+#include "third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.h"
+#include "third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h"
+#include "third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h"
+#include "third_party/blink/renderer/platform/testing/paint_property_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/paint_test_configurations.h"
+
+namespace blink {
+
+class GeometryMapperTest : public testing::Test,
+ public PaintTestConfigurations {
+ public:
+ const FloatClipRect* GetCachedClip(
+ const ClipPaintPropertyNode* descendant_clip,
+ const PropertyTreeState& ancestor_property_tree_state) {
+ GeometryMapperClipCache::ClipAndTransform clip_and_transform(
+ ancestor_property_tree_state.Clip(),
+ ancestor_property_tree_state.Transform(),
+ kIgnorePlatformOverlayScrollbarSize);
+ return descendant_clip->GetClipCache().GetCachedClip(clip_and_transform);
+ }
+
+ void LocalToAncestorVisualRectInternal(
+ const PropertyTreeState& local_state,
+ const PropertyTreeState& ancestor_state,
+ FloatClipRect& mapping_rect,
+ bool& success) {
+ GeometryMapper::LocalToAncestorVisualRectInternal(
+ local_state, ancestor_state, mapping_rect,
+ kIgnorePlatformOverlayScrollbarSize, success);
+ }
+
+ // Variables required by CHECK_MAPPINGS(). The tests should set these
+ // variables with proper values before calling CHECK_MAPPINGS().
+ PropertyTreeState local_state = PropertyTreeState::Root();
+ PropertyTreeState ancestor_state = PropertyTreeState::Root();
+ FloatRect input_rect;
+ FloatClipRect expected_visual_rect;
+ TransformationMatrix expected_transform;
+ FloatClipRect expected_clip;
+ FloatRect expected_transformed_rect;
+};
+
+INSTANTIATE_TEST_CASE_P(All,
+ GeometryMapperTest,
+ testing::ValuesIn(kSlimmingPaintVersions));
+
+#define EXPECT_FLOAT_RECT_NEAR(expected, actual) \
+ do { \
+ EXPECT_PRED_FORMAT2(GeometryTest::AssertAlmostEqual, (actual).X(), \
+ (expected).X()); \
+ EXPECT_PRED_FORMAT2(GeometryTest::AssertAlmostEqual, (actual).Y(), \
+ (expected).Y()); \
+ EXPECT_PRED_FORMAT2(GeometryTest::AssertAlmostEqual, (actual).Width(), \
+ (expected).Width()); \
+ EXPECT_PRED_FORMAT2(GeometryTest::AssertAlmostEqual, (actual).Height(), \
+ (expected).Height()); \
+ } while (false)
+
+#define EXPECT_CLIP_RECT_EQ(expected, actual) \
+ do { \
+ SCOPED_TRACE("EXPECT_CLIP_RECT_EQ: " #expected " vs " #actual); \
+ EXPECT_EQ((expected).IsInfinite(), (actual).IsInfinite()); \
+ EXPECT_EQ((expected).HasRadius(), (actual).HasRadius()); \
+ EXPECT_EQ((expected).IsTight(), (actual).IsTight()); \
+ if (!(expected).IsInfinite()) \
+ EXPECT_FLOAT_RECT_NEAR((expected).Rect(), (actual).Rect()); \
+ } while (false)
+
+#define CHECK_LOCAL_TO_ANCESTOR_VISUAL_RECT() \
+ do { \
+ SCOPED_TRACE("Check LocalToAncestorVisualRect"); \
+ FloatClipRect actual_visual_rect(input_rect); \
+ GeometryMapper::LocalToAncestorVisualRect(local_state, ancestor_state, \
+ actual_visual_rect); \
+ EXPECT_CLIP_RECT_EQ(expected_visual_rect, actual_visual_rect); \
+ } while (false)
+
+#define CHECK_LOCAL_TO_ANCESTOR_CLIP_RECT() \
+ do { \
+ SCOPED_TRACE("Check LocalToAncestorClipRect"); \
+ FloatClipRect actual_clip_rect; \
+ actual_clip_rect = \
+ GeometryMapper::LocalToAncestorClipRect(local_state, ancestor_state); \
+ EXPECT_CLIP_RECT_EQ(expected_clip, actual_clip_rect); \
+ } while (false)
+
+#define CHECK_SOURCE_TO_DESTINATION_RECT() \
+ do { \
+ SCOPED_TRACE("Check SourceToDestinationRect"); \
+ auto actual_transformed_rect = input_rect; \
+ GeometryMapper::SourceToDestinationRect(local_state.Transform(), \
+ ancestor_state.Transform(), \
+ actual_transformed_rect); \
+ EXPECT_FLOAT_RECT_NEAR(expected_transformed_rect, \
+ actual_transformed_rect); \
+ } while (false)
+
+#define CHECK_SOURCE_TO_DESTINATION_PROJECTION() \
+ do { \
+ SCOPED_TRACE("Check SourceToDestinationProjection"); \
+ const auto& actual_transform_to_ancestor = \
+ GeometryMapper::SourceToDestinationProjection( \
+ local_state.Transform(), ancestor_state.Transform()); \
+ EXPECT_EQ(expected_transform, actual_transform_to_ancestor); \
+ } while (false)
+
+#define CHECK_CACHED_CLIP() \
+ do { \
+ if (ancestor_state.Effect() != local_state.Effect()) \
+ break; \
+ SCOPED_TRACE("Check cached clip"); \
+ const auto* cached_clip = \
+ GetCachedClip(local_state.Clip(), ancestor_state); \
+ if (ancestor_state.Clip() == local_state.Clip() || \
+ (ancestor_state.Clip() == local_state.Clip()->Parent() && \
+ ancestor_state.Transform() == \
+ local_state.Clip()->LocalTransformSpace())) { \
+ EXPECT_EQ(nullptr, cached_clip); \
+ break; \
+ } \
+ ASSERT_NE(nullptr, cached_clip); \
+ EXPECT_CLIP_RECT_EQ(expected_clip, *cached_clip); \
+ } while (false)
+
+// See the data fields of GeometryMapperTest for variables that will be used in
+// this macro.
+#define CHECK_MAPPINGS() \
+ do { \
+ CHECK_LOCAL_TO_ANCESTOR_VISUAL_RECT(); \
+ CHECK_LOCAL_TO_ANCESTOR_CLIP_RECT(); \
+ CHECK_SOURCE_TO_DESTINATION_RECT(); \
+ CHECK_SOURCE_TO_DESTINATION_PROJECTION(); \
+ { \
+ SCOPED_TRACE("Repeated check to test caching"); \
+ CHECK_LOCAL_TO_ANCESTOR_VISUAL_RECT(); \
+ CHECK_LOCAL_TO_ANCESTOR_CLIP_RECT(); \
+ CHECK_SOURCE_TO_DESTINATION_RECT(); \
+ CHECK_SOURCE_TO_DESTINATION_PROJECTION(); \
+ } \
+ CHECK_CACHED_CLIP(); \
+ } while (false)
+
+TEST_P(GeometryMapperTest, Root) {
+ input_rect = FloatRect(0, 0, 100, 100);
+ expected_visual_rect = FloatClipRect(input_rect);
+ expected_transformed_rect = input_rect;
+ CHECK_MAPPINGS();
+}
+
+TEST_P(GeometryMapperTest, IdentityTransform) {
+ auto transform = TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(), TransformationMatrix(),
+ FloatPoint3D());
+ local_state.SetTransform(transform.get());
+
+ input_rect = FloatRect(0, 0, 100, 100);
+ expected_transformed_rect = input_rect;
+ expected_visual_rect = FloatClipRect(input_rect);
+ CHECK_MAPPINGS();
+}
+
+TEST_P(GeometryMapperTest, TranslationTransform) {
+ expected_transform = TransformationMatrix().Translate(20, 10);
+ auto transform = TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(), expected_transform, FloatPoint3D());
+ local_state.SetTransform(transform.get());
+
+ input_rect = FloatRect(0, 0, 100, 100);
+ expected_transformed_rect = expected_transform.MapRect(input_rect);
+ expected_visual_rect = FloatClipRect(expected_transformed_rect);
+ CHECK_MAPPINGS();
+
+ FloatRect rect = expected_transformed_rect;
+ GeometryMapper::SourceToDestinationRect(TransformPaintPropertyNode::Root(),
+ local_state.Transform(), rect);
+ EXPECT_FLOAT_RECT_NEAR(input_rect, rect);
+}
+
+TEST_P(GeometryMapperTest, RotationAndScaleTransform) {
+ expected_transform = TransformationMatrix().Rotate(45).Scale(2);
+ auto transform = TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(), expected_transform,
+ FloatPoint3D(0, 0, 0));
+ local_state.SetTransform(transform.get());
+
+ input_rect = FloatRect(0, 0, 100, 100);
+ expected_transformed_rect = expected_transform.MapRect(input_rect);
+ expected_visual_rect = FloatClipRect(expected_transformed_rect);
+ expected_visual_rect.ClearIsTight();
+ CHECK_MAPPINGS();
+}
+
+TEST_P(GeometryMapperTest, RotationAndScaleTransformWithTransformOrigin) {
+ expected_transform = TransformationMatrix().Rotate(45).Scale(2);
+ auto transform = TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(), expected_transform,
+ FloatPoint3D(50, 50, 0));
+ local_state.SetTransform(transform.get());
+
+ input_rect = FloatRect(0, 0, 100, 100);
+ expected_transform.ApplyTransformOrigin(50, 50, 0);
+ expected_transformed_rect = expected_transform.MapRect(input_rect);
+ expected_visual_rect = FloatClipRect(expected_transformed_rect);
+ expected_visual_rect.ClearIsTight();
+ CHECK_MAPPINGS();
+}
+
+TEST_P(GeometryMapperTest, NestedTransforms) {
+ auto rotate_transform = TransformationMatrix().Rotate(45);
+ auto transform1 = TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(), rotate_transform, FloatPoint3D());
+
+ auto scale_transform = TransformationMatrix().Scale(2);
+ auto transform2 = TransformPaintPropertyNode::Create(
+ transform1, scale_transform, FloatPoint3D());
+ local_state.SetTransform(transform2.get());
+
+ input_rect = FloatRect(0, 0, 100, 100);
+ expected_transform = rotate_transform * scale_transform;
+ expected_transformed_rect = expected_transform.MapRect(input_rect);
+ expected_visual_rect = FloatClipRect(expected_transformed_rect);
+ expected_visual_rect.ClearIsTight();
+ CHECK_MAPPINGS();
+}
+
+TEST_P(GeometryMapperTest, NestedTransformsFlattening) {
+ auto rotate_transform = TransformationMatrix().Rotate3d(45, 0, 0);
+ auto transform1 = TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(), rotate_transform, FloatPoint3D());
+
+ auto inverse_rotate_transform = TransformationMatrix().Rotate3d(-45, 0, 0);
+ auto transform2 = TransformPaintPropertyNode::Create(
+ transform1, inverse_rotate_transform, FloatPoint3D(),
+ true); // Flattens
+ local_state.SetTransform(transform2.get());
+
+ input_rect = FloatRect(0, 0, 100, 100);
+ rotate_transform.FlattenTo2d();
+ expected_transform = rotate_transform * inverse_rotate_transform;
+ expected_transform.FlattenTo2d();
+ expected_transformed_rect = expected_transform.MapRect(input_rect);
+ expected_visual_rect = FloatClipRect(expected_transformed_rect);
+ expected_visual_rect.ClearIsTight();
+ CHECK_MAPPINGS();
+}
+
+TEST_P(GeometryMapperTest, NestedTransformsScaleAndTranslation) {
+ auto scale_transform = TransformationMatrix().Scale(2);
+ auto transform1 = TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(), scale_transform, FloatPoint3D());
+
+ auto translate_transform = TransformationMatrix().Translate(100, 0);
+ auto transform2 = TransformPaintPropertyNode::Create(
+ transform1, translate_transform, FloatPoint3D());
+ local_state.SetTransform(transform2.get());
+
+ input_rect = FloatRect(0, 0, 100, 100);
+ // Note: unlike NestedTransforms, the order of these transforms matters. This
+ // tests correct order of matrix multiplication.
+ expected_transform = scale_transform * translate_transform;
+ expected_transformed_rect = expected_transform.MapRect(input_rect);
+ expected_visual_rect = FloatClipRect(expected_transformed_rect);
+ expected_visual_rect.ClearIsTight();
+ CHECK_MAPPINGS();
+}
+
+TEST_P(GeometryMapperTest, NestedTransformsIntermediateDestination) {
+ auto rotate_transform = TransformationMatrix().Rotate(45);
+ auto transform1 = TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(), rotate_transform, FloatPoint3D());
+
+ auto scale_transform = TransformationMatrix().Translate(10, 20);
+ auto transform2 = TransformPaintPropertyNode::Create(
+ transform1, scale_transform, FloatPoint3D());
+
+ local_state.SetTransform(transform2.get());
+ ancestor_state.SetTransform(transform1.get());
+
+ expected_transform = scale_transform;
+ input_rect = FloatRect(0, 0, 100, 100);
+ expected_transformed_rect = expected_transform.MapRect(input_rect);
+ expected_visual_rect = FloatClipRect(expected_transformed_rect);
+ CHECK_MAPPINGS();
+}
+
+TEST_P(GeometryMapperTest, SimpleClip) {
+ auto clip = ClipPaintPropertyNode::Create(ClipPaintPropertyNode::Root(),
+ TransformPaintPropertyNode::Root(),
+ FloatRoundedRect(10, 10, 50, 50));
+ local_state.SetClip(clip.get());
+
+ input_rect = FloatRect(0, 0, 100, 100);
+ expected_transformed_rect = input_rect; // not clipped.
+ expected_clip = FloatClipRect(clip->ClipRect());
+ expected_visual_rect = expected_clip;
+ CHECK_MAPPINGS();
+}
+
+TEST_P(GeometryMapperTest, SimpleClipOverlayScrollbars) {
+ FloatRoundedRect rect_without_overlay_scrollbars(10, 10, 45, 43);
+ auto clip = ClipPaintPropertyNode::Create(
+ ClipPaintPropertyNode::Root(), TransformPaintPropertyNode::Root(),
+ FloatRoundedRect(10, 10, 50, 50), &rect_without_overlay_scrollbars);
+ local_state.SetClip(clip.get());
+
+ input_rect = FloatRect(0, 0, 100, 100);
+
+ FloatClipRect actual_visual_rect(input_rect);
+ GeometryMapper::LocalToAncestorVisualRect(
+ local_state, ancestor_state, actual_visual_rect,
+ kExcludeOverlayScrollbarSizeForHitTesting);
+ EXPECT_CLIP_RECT_EQ(FloatClipRect(FloatRect(10, 10, 45, 43)),
+ actual_visual_rect);
+
+ // Check that not passing kExcludeOverlayScrollbarSizeForHitTesting gives
+ // a different result.
+ actual_visual_rect = FloatClipRect(input_rect);
+ GeometryMapper::LocalToAncestorVisualRect(
+ local_state, ancestor_state, actual_visual_rect,
+ kIgnorePlatformOverlayScrollbarSize);
+ EXPECT_CLIP_RECT_EQ(FloatClipRect(FloatRect(10, 10, 50, 50)),
+ actual_visual_rect);
+
+ FloatClipRect actual_clip_rect = GeometryMapper::LocalToAncestorClipRect(
+ local_state, ancestor_state, kExcludeOverlayScrollbarSizeForHitTesting);
+ EXPECT_CLIP_RECT_EQ(FloatClipRect(FloatRect(10, 10, 45, 43)),
+ actual_clip_rect);
+
+ // Check that not passing kExcludeOverlayScrollbarSizeForHitTesting gives
+ // a different result.
+ actual_clip_rect = GeometryMapper::LocalToAncestorClipRect(
+ local_state, ancestor_state, kIgnorePlatformOverlayScrollbarSize);
+ EXPECT_CLIP_RECT_EQ(FloatClipRect(FloatRect(10, 10, 50, 50)),
+ actual_clip_rect);
+}
+
+TEST_P(GeometryMapperTest, RoundedClip) {
+ FloatRoundedRect rect(FloatRect(10, 10, 50, 50),
+ FloatRoundedRect::Radii(FloatSize(1, 1), FloatSize(),
+ FloatSize(), FloatSize()));
+ auto clip = ClipPaintPropertyNode::Create(
+ ClipPaintPropertyNode::Root(), TransformPaintPropertyNode::Root(), rect);
+ local_state.SetClip(clip.get());
+
+ input_rect = FloatRect(0, 0, 100, 100);
+ expected_transformed_rect = input_rect;
+ expected_clip = FloatClipRect(clip->ClipRect());
+ EXPECT_TRUE(expected_clip.HasRadius());
+ expected_visual_rect = expected_clip;
+ CHECK_MAPPINGS();
+}
+
+TEST_P(GeometryMapperTest, TwoClips) {
+ FloatRoundedRect clip_rect1(
+ FloatRect(10, 10, 30, 40),
+ FloatRoundedRect::Radii(FloatSize(1, 1), FloatSize(), FloatSize(),
+ FloatSize()));
+
+ auto clip1 = ClipPaintPropertyNode::Create(ClipPaintPropertyNode::Root(),
+ TransformPaintPropertyNode::Root(),
+ clip_rect1);
+
+ auto clip2 =
+ ClipPaintPropertyNode::Create(clip1, TransformPaintPropertyNode::Root(),
+ FloatRoundedRect(10, 10, 50, 50));
+ local_state.SetClip(clip2.get());
+
+ input_rect = FloatRect(0, 0, 100, 100);
+ expected_transformed_rect = input_rect;
+ expected_clip = FloatClipRect(clip1->ClipRect());
+ EXPECT_TRUE(expected_clip.HasRadius());
+ expected_visual_rect = expected_clip;
+ CHECK_MAPPINGS();
+
+ ancestor_state.SetClip(clip1.get());
+ expected_clip = FloatClipRect(clip2->ClipRect());
+ expected_visual_rect = expected_clip;
+ CHECK_MAPPINGS();
+}
+
+TEST_P(GeometryMapperTest, TwoClipsTransformAbove) {
+ auto transform = TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(), TransformationMatrix(),
+ FloatPoint3D());
+
+ FloatRoundedRect clip_rect1(
+ FloatRect(10, 10, 50, 50),
+ FloatRoundedRect::Radii(FloatSize(1, 1), FloatSize(), FloatSize(),
+ FloatSize()));
+
+ auto clip1 = ClipPaintPropertyNode::Create(ClipPaintPropertyNode::Root(),
+ transform.get(), clip_rect1);
+
+ auto clip2 = ClipPaintPropertyNode::Create(clip1, transform.get(),
+ FloatRoundedRect(10, 10, 30, 40));
+ local_state.SetClip(clip2.get());
+
+ input_rect = FloatRect(0, 0, 100, 100);
+ expected_transformed_rect = input_rect;
+ expected_clip = FloatClipRect(clip2->ClipRect());
+ expected_clip.SetHasRadius();
+ expected_visual_rect = expected_clip;
+ CHECK_MAPPINGS();
+
+ expected_clip = FloatClipRect(clip1->ClipRect());
+ EXPECT_TRUE(expected_clip.HasRadius());
+ local_state.SetClip(clip1.get());
+ expected_visual_rect = expected_clip;
+ CHECK_MAPPINGS();
+}
+
+TEST_P(GeometryMapperTest, ClipBeforeTransform) {
+ expected_transform = TransformationMatrix().Rotate(45);
+ auto transform = TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(), expected_transform, FloatPoint3D());
+ auto clip = ClipPaintPropertyNode::Create(ClipPaintPropertyNode::Root(),
+ transform.get(),
+ FloatRoundedRect(10, 10, 50, 50));
+ local_state.SetClip(clip.get());
+ local_state.SetTransform(transform.get());
+
+ input_rect = FloatRect(0, 0, 100, 100);
+ expected_visual_rect = FloatClipRect(input_rect);
+ expected_visual_rect.Intersect(FloatClipRect(clip->ClipRect()));
+ expected_visual_rect.Map(expected_transform);
+ EXPECT_FALSE(expected_visual_rect.IsTight());
+ expected_clip = FloatClipRect(clip->ClipRect());
+ expected_clip.Map(expected_transform);
+ EXPECT_FALSE(expected_clip.IsTight());
+ expected_transformed_rect = expected_transform.MapRect(input_rect);
+ CHECK_MAPPINGS();
+}
+
+TEST_P(GeometryMapperTest, ClipAfterTransform) {
+ expected_transform = TransformationMatrix().Rotate(45);
+ auto transform = TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(), expected_transform, FloatPoint3D());
+ auto clip = ClipPaintPropertyNode::Create(ClipPaintPropertyNode::Root(),
+ TransformPaintPropertyNode::Root(),
+ FloatRoundedRect(10, 10, 200, 200));
+ local_state.SetClip(clip.get());
+ local_state.SetTransform(transform.get());
+
+ input_rect = FloatRect(0, 0, 100, 100);
+ expected_transformed_rect = expected_transform.MapRect(input_rect);
+ expected_visual_rect = FloatClipRect(input_rect);
+ expected_visual_rect.Map(expected_transform);
+ expected_visual_rect.Intersect(FloatClipRect(clip->ClipRect()));
+ EXPECT_FALSE(expected_visual_rect.IsTight());
+ expected_clip = FloatClipRect(clip->ClipRect());
+ EXPECT_TRUE(expected_clip.IsTight());
+ CHECK_MAPPINGS();
+}
+
+TEST_P(GeometryMapperTest, TwoClipsWithTransformBetween) {
+ auto clip1 = ClipPaintPropertyNode::Create(
+ ClipPaintPropertyNode::Root(), TransformPaintPropertyNode::Root(),
+ FloatRoundedRect(10, 10, 200, 200));
+
+ expected_transform = TransformationMatrix().Rotate(45);
+ auto transform = TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(), expected_transform, FloatPoint3D());
+
+ auto clip2 = ClipPaintPropertyNode::Create(
+ clip1, transform.get(), FloatRoundedRect(10, 10, 200, 200));
+
+ input_rect = FloatRect(0, 0, 100, 100);
+ expected_transformed_rect = expected_transform.MapRect(input_rect);
+
+ {
+ local_state.SetClip(clip1.get());
+ local_state.SetTransform(transform.get());
+
+ expected_visual_rect = FloatClipRect(input_rect);
+ expected_visual_rect.Map(expected_transform);
+ expected_visual_rect.Intersect(FloatClipRect(clip1->ClipRect()));
+ EXPECT_FALSE(expected_visual_rect.IsTight());
+ expected_clip = FloatClipRect(clip1->ClipRect());
+ EXPECT_TRUE(expected_clip.IsTight());
+ CHECK_MAPPINGS();
+ }
+
+ {
+ local_state.SetClip(clip2.get());
+ local_state.SetTransform(transform.get());
+
+ expected_clip = FloatClipRect(clip2->ClipRect());
+ expected_clip.Map(expected_transform);
+ expected_clip.Intersect(FloatClipRect(clip1->ClipRect()));
+ EXPECT_FALSE(expected_clip.IsTight());
+
+ // All clips are performed in the space of the ancestor. In cases such as
+ // this, this means the clip is not tight.
+ expected_visual_rect = FloatClipRect(input_rect);
+ expected_visual_rect.Map(expected_transform);
+ // Intersect with all clips between local and ancestor, independently mapped
+ // to ancestor space.
+ expected_visual_rect.Intersect(expected_clip);
+ EXPECT_FALSE(expected_visual_rect.IsTight());
+
+ CHECK_MAPPINGS();
+ }
+}
+
+TEST_P(GeometryMapperTest, SiblingTransforms) {
+ // These transforms are siblings. Thus mapping from one to the other requires
+ // going through the root.
+ auto rotate_transform1 = TransformationMatrix().Rotate(45);
+ auto transform1 = TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(), rotate_transform1, FloatPoint3D());
+
+ auto rotate_transform2 = TransformationMatrix().Rotate(-45);
+ auto transform2 = TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(), rotate_transform2, FloatPoint3D());
+
+ auto transform1_state = PropertyTreeState::Root();
+ transform1_state.SetTransform(transform1.get());
+ auto transform2_state = PropertyTreeState::Root();
+ transform2_state.SetTransform(transform2.get());
+
+ input_rect = FloatRect(0, 0, 100, 100);
+ FloatClipRect result_clip(input_rect);
+ GeometryMapper::LocalToAncestorVisualRect(transform1_state, transform2_state,
+ result_clip);
+ FloatClipRect expected_clip(FloatRect(-100, 0, 100, 100));
+ // This is because the combined Rotate(45) and Rotate(-45) is not exactly a
+ // translation-only transform due to calculation errors.
+ expected_clip.ClearIsTight();
+ EXPECT_CLIP_RECT_EQ(expected_clip, result_clip);
+
+ FloatRect result = input_rect;
+ GeometryMapper::SourceToDestinationRect(transform1.get(), transform2.get(),
+ result);
+ EXPECT_FLOAT_RECT_NEAR(FloatRect(-100, 0, 100, 100), result);
+
+ result_clip = FloatClipRect(input_rect);
+ GeometryMapper::LocalToAncestorVisualRect(transform2_state, transform1_state,
+ result_clip);
+ expected_clip = FloatClipRect(FloatRect(0, -100, 100, 100));
+ expected_clip.ClearIsTight();
+ EXPECT_CLIP_RECT_EQ(expected_clip, result_clip);
+
+ result = input_rect;
+ GeometryMapper::SourceToDestinationRect(transform2.get(), transform1.get(),
+ result);
+ EXPECT_FLOAT_RECT_NEAR(FloatRect(0, -100, 100, 100), result);
+}
+
+TEST_P(GeometryMapperTest, SiblingTransformsWithClip) {
+ // These transforms are siblings. Thus mapping from one to the other requires
+ // going through the root.
+ auto rotate_transform1 = TransformationMatrix().Rotate(45);
+ auto transform1 = TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(), rotate_transform1, FloatPoint3D());
+
+ auto rotate_transform2 = TransformationMatrix().Rotate(-45);
+ auto transform2 = TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(), rotate_transform2, FloatPoint3D());
+
+ auto clip = ClipPaintPropertyNode::Create(ClipPaintPropertyNode::Root(),
+ transform2.get(),
+ FloatRoundedRect(10, 20, 30, 40));
+
+ auto transform1_state = PropertyTreeState::Root();
+ transform1_state.SetTransform(transform1.get());
+ auto transform2_and_clip_state = PropertyTreeState::Root();
+ transform2_and_clip_state.SetTransform(transform2.get());
+ transform2_and_clip_state.SetClip(clip.get());
+
+ bool success;
+ input_rect = FloatRect(0, 0, 100, 100);
+ FloatClipRect result(input_rect);
+ LocalToAncestorVisualRectInternal(transform1_state, transform2_and_clip_state,
+ result, success);
+ // Fails, because the clip of the destination state is not an ancestor of the
+ // clip of the source state. A known bug in SPv1* would make such query,
+ // in such case, no clips are applied.
+ if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
+ EXPECT_FALSE(success);
+ } else {
+ EXPECT_TRUE(success);
+ FloatClipRect expected(FloatRect(-100, 0, 100, 100));
+ expected.ClearIsTight();
+ EXPECT_CLIP_RECT_EQ(expected, result);
+ }
+
+ result = FloatClipRect(input_rect);
+ GeometryMapper::LocalToAncestorVisualRect(transform2_and_clip_state,
+ transform1_state, result);
+ FloatClipRect expected(FloatRect(20, -40, 40, 30));
+ // This is because the combined Rotate(45) and Rotate(-45) is not exactly a
+ // translation-only transform due to calculation errors.
+ expected.ClearIsTight();
+ EXPECT_CLIP_RECT_EQ(expected, result);
+}
+
+TEST_P(GeometryMapperTest, FilterWithClipsAndTransforms) {
+ auto transform_above_effect = TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(),
+ TransformationMatrix().Translate(40, 50), FloatPoint3D());
+ auto transform_below_effect = TransformPaintPropertyNode::Create(
+ transform_above_effect, TransformationMatrix().Translate(20, 30),
+ FloatPoint3D());
+
+ // This clip is between transformAboveEffect and the effect.
+ auto clip_above_effect = ClipPaintPropertyNode::Create(
+ ClipPaintPropertyNode::Root(), transform_above_effect,
+ FloatRoundedRect(-100, -100, 200, 200));
+ // This clip is between the effect and transformBelowEffect.
+ auto clip_below_effect =
+ ClipPaintPropertyNode::Create(clip_above_effect, transform_above_effect,
+ FloatRoundedRect(10, 10, 100, 100));
+
+ CompositorFilterOperations filters;
+ filters.AppendBlurFilter(20);
+ auto effect = EffectPaintPropertyNode::Create(
+ EffectPaintPropertyNode::Root(), transform_above_effect,
+ clip_above_effect, kColorFilterNone, filters, 1.0, SkBlendMode::kSrcOver);
+
+ local_state = PropertyTreeState(transform_below_effect.get(),
+ clip_below_effect.get(), effect.get());
+
+ input_rect = FloatRect(0, 0, 100, 100);
+ // 1. transformBelowEffect
+ auto output = transform_below_effect->Matrix().MapRect(input_rect);
+ // 2. clipBelowEffect
+ output.Intersect(clip_below_effect->ClipRect().Rect());
+ EXPECT_EQ(FloatRect(20, 30, 90, 80), output);
+ // 3. effect (the outset is 3 times of blur amount).
+ output = filters.MapRect(output);
+ EXPECT_EQ(FloatRect(-40, -30, 210, 200), output);
+ // 4. clipAboveEffect
+ output.Intersect(clip_above_effect->ClipRect().Rect());
+ EXPECT_EQ(FloatRect(-40, -30, 140, 130), output);
+ // 5. transformAboveEffect
+ output = transform_above_effect->Matrix().MapRect(output);
+ EXPECT_EQ(FloatRect(0, 20, 140, 130), output);
+
+ expected_transform =
+ transform_above_effect->Matrix() * transform_below_effect->Matrix();
+ expected_transformed_rect = expected_transform.MapRect(input_rect);
+ expected_visual_rect = FloatClipRect(output);
+ expected_visual_rect.ClearIsTight();
+ expected_clip = FloatClipRect(FloatRect(50, 60, 90, 90));
+ expected_clip.ClearIsTight();
+ CHECK_MAPPINGS();
+}
+
+TEST_P(GeometryMapperTest, ReflectionWithPaintOffset) {
+ CompositorFilterOperations filters;
+ filters.AppendReferenceFilter(PaintFilterBuilder::BuildBoxReflectFilter(
+ BoxReflection(BoxReflection::kHorizontalReflection, 0), nullptr));
+ auto effect = EffectPaintPropertyNode::Create(
+ EffectPaintPropertyNode::Root(), TransformPaintPropertyNode::Root(),
+ ClipPaintPropertyNode::Root(), kColorFilterNone, filters, 1.0,
+ SkBlendMode::kSrcOver, CompositingReason::kNone, CompositorElementId(),
+ FloatPoint(100, 100));
+ local_state.SetEffect(effect.get());
+
+ input_rect = FloatRect(100, 100, 50, 50);
+ expected_transformed_rect = input_rect;
+ // Reflection is at (50, 100, 50, 50).
+ expected_visual_rect = FloatClipRect(FloatRect(50, 100, 100, 50));
+ expected_visual_rect.ClearIsTight();
+
+ CHECK_MAPPINGS();
+}
+
+TEST_P(GeometryMapperTest, InvertedClip) {
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled())
+ return;
+
+ auto clip = ClipPaintPropertyNode::Create(ClipPaintPropertyNode::Root(),
+ TransformPaintPropertyNode::Root(),
+ FloatRoundedRect(10, 10, 50, 50));
+
+ PropertyTreeState dest(TransformPaintPropertyNode::Root(), clip.get(),
+ EffectPaintPropertyNode::Root());
+
+ FloatClipRect visual_rect(FloatRect(0, 0, 10, 200));
+ GeometryMapper::LocalToAncestorVisualRect(PropertyTreeState::Root(), dest,
+ visual_rect);
+
+ // The "ancestor" clip is below the source clip in this case, so
+ // LocalToAncestorVisualRect must fall back to the original rect, mapped
+ // into the root space.
+ EXPECT_EQ(FloatRect(0, 0, 10, 200), visual_rect.Rect());
+ EXPECT_FALSE(visual_rect.IsTight());
+}
+
+TEST_P(GeometryMapperTest, PointVisibleInAncestorSpaceSimpleClip) {
+ auto clip = CreateClip(ClipPaintPropertyNode::Root(),
+ TransformPaintPropertyNode::Root(),
+ FloatRoundedRect(10, 10, 50, 50));
+
+ PropertyTreeState local_state(TransformPaintPropertyNode::Root(), clip.get(),
+ EffectPaintPropertyNode::Root());
+
+ PropertyTreeState ancestor_state(TransformPaintPropertyNode::Root(),
+ ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root());
+
+ EXPECT_TRUE(GeometryMapper::PointVisibleInAncestorSpace(
+ local_state, ancestor_state, FloatPoint(30, 30)));
+
+ EXPECT_TRUE(GeometryMapper::PointVisibleInAncestorSpace(
+ local_state, ancestor_state, FloatPoint(11, 11)));
+
+ EXPECT_FALSE(GeometryMapper::PointVisibleInAncestorSpace(
+ local_state, ancestor_state, FloatPoint(5, 5)));
+}
+
+TEST_P(GeometryMapperTest, PointVisibleInAncestorSpaceRoundedClip) {
+ FloatRoundedRect clip_rect(FloatRoundedRect(10, 10, 50, 50));
+ FloatRoundedRect::Radii radii;
+ radii.SetTopLeft(FloatSize(8, 8));
+ clip_rect.SetRadii(radii);
+ auto clip = CreateClip(ClipPaintPropertyNode::Root(),
+ TransformPaintPropertyNode::Root(), clip_rect);
+
+ PropertyTreeState local_state(TransformPaintPropertyNode::Root(), clip.get(),
+ EffectPaintPropertyNode::Root());
+
+ PropertyTreeState ancestor_state(TransformPaintPropertyNode::Root(),
+ ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root());
+
+ EXPECT_TRUE(GeometryMapper::PointVisibleInAncestorSpace(
+ local_state, ancestor_state, FloatPoint(30, 30)));
+
+ // Clipped out by radius.
+ EXPECT_FALSE(GeometryMapper::PointVisibleInAncestorSpace(
+ local_state, ancestor_state, FloatPoint(11, 11)));
+
+ EXPECT_FALSE(GeometryMapper::PointVisibleInAncestorSpace(
+ local_state, ancestor_state, FloatPoint(5, 5)));
+}
+
+TEST_P(GeometryMapperTest, PointVisibleInAncestorSpaceClipPath) {
+ RefCountedPath* path = new RefCountedPath;
+ path->MoveTo(FloatPoint(10, 10));
+ path->AddLineTo(FloatPoint(10, 60));
+ path->AddLineTo(FloatPoint(60, 60));
+ path->AddLineTo(FloatPoint(60, 10));
+ path->AddLineTo(FloatPoint(10, 10));
+
+ ClipPaintPropertyNode::State state;
+ state.local_transform_space = TransformPaintPropertyNode::Root();
+ state.clip_rect = FloatRoundedRect(FloatRect(0, 0, 500, 500));
+ state.clip_path = base::AdoptRef(path);
+ auto clip = ClipPaintPropertyNode::Create(ClipPaintPropertyNode::Root(),
+ std::move(state));
+
+ PropertyTreeState local_state(TransformPaintPropertyNode::Root(), clip.get(),
+ EffectPaintPropertyNode::Root());
+
+ PropertyTreeState ancestor_state(TransformPaintPropertyNode::Root(),
+ ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root());
+
+ EXPECT_TRUE(GeometryMapper::PointVisibleInAncestorSpace(
+ local_state, ancestor_state, FloatPoint(30, 30)));
+
+ EXPECT_TRUE(GeometryMapper::PointVisibleInAncestorSpace(
+ local_state, ancestor_state, FloatPoint(11, 11)));
+
+ EXPECT_FALSE(GeometryMapper::PointVisibleInAncestorSpace(
+ local_state, ancestor_state, FloatPoint(5, 5)));
+}
+
+TEST_P(GeometryMapperTest, PointVisibleInAncestorSpaceSimpleClipWithTransform) {
+ TransformPaintPropertyNode::State translate_transform;
+ translate_transform.matrix.Translate(10, 10);
+ auto transform = TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(), std::move(translate_transform));
+
+ auto clip = CreateClip(ClipPaintPropertyNode::Root(),
+ TransformPaintPropertyNode::Root(),
+ FloatRoundedRect(FloatRect(20, 20, 50, 50)));
+
+ PropertyTreeState local_state(transform.get(), clip.get(),
+ EffectPaintPropertyNode::Root());
+
+ PropertyTreeState ancestor_state(TransformPaintPropertyNode::Root(),
+ ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root());
+
+ EXPECT_TRUE(GeometryMapper::PointVisibleInAncestorSpace(
+ local_state, ancestor_state, FloatPoint(30, 30)));
+
+ EXPECT_TRUE(GeometryMapper::PointVisibleInAncestorSpace(
+ local_state, ancestor_state, FloatPoint(11, 11)));
+
+ EXPECT_FALSE(GeometryMapper::PointVisibleInAncestorSpace(
+ local_state, ancestor_state, FloatPoint(5, 5)));
+}
+
+TEST_P(GeometryMapperTest, PointVisibleInAncestorSpaceClipPathWithTransform) {
+ TransformPaintPropertyNode::State translate_transform;
+ translate_transform.matrix.Translate(10, 10);
+ auto transform = TransformPaintPropertyNode::Create(
+ TransformPaintPropertyNode::Root(), std::move(translate_transform));
+
+ RefCountedPath* path = new RefCountedPath;
+ path->MoveTo(FloatPoint(20, 20));
+ path->AddLineTo(FloatPoint(20, 60));
+ path->AddLineTo(FloatPoint(60, 60));
+ path->AddLineTo(FloatPoint(60, 20));
+ path->AddLineTo(FloatPoint(20, 20));
+
+ ClipPaintPropertyNode::State state;
+ state.local_transform_space = TransformPaintPropertyNode::Root();
+ state.clip_rect = FloatRoundedRect(FloatRect(0, 0, 500, 500));
+ state.clip_path = base::AdoptRef(path);
+ auto clip = ClipPaintPropertyNode::Create(ClipPaintPropertyNode::Root(),
+ std::move(state));
+
+ PropertyTreeState local_state(transform.get(), clip.get(),
+ EffectPaintPropertyNode::Root());
+
+ PropertyTreeState ancestor_state(TransformPaintPropertyNode::Root(),
+ ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root());
+
+ EXPECT_TRUE(GeometryMapper::PointVisibleInAncestorSpace(
+ local_state, ancestor_state, FloatPoint(30, 30)));
+
+ EXPECT_TRUE(GeometryMapper::PointVisibleInAncestorSpace(
+ local_state, ancestor_state, FloatPoint(11, 11)));
+
+ EXPECT_FALSE(GeometryMapper::PointVisibleInAncestorSpace(
+ local_state, ancestor_state, FloatPoint(5, 5)));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache.cc
new file mode 100644
index 00000000000..d50d53c0d88
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache.cc
@@ -0,0 +1,74 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache.h"
+
+#include "third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h"
+
+namespace blink {
+
+// All transform caches invalidate themselves by tracking a local cache
+// generation, and invalidating their cache if their cache generation disagrees
+// with s_global_generation.
+unsigned GeometryMapperTransformCache::s_global_generation;
+
+void GeometryMapperTransformCache::ClearCache() {
+ s_global_generation++;
+}
+
+// Computes flatten(m) ^ -1, return true if the inversion succeeded.
+static bool InverseProjection(const TransformationMatrix& m,
+ TransformationMatrix& out) {
+ out = m;
+ out.FlattenTo2d();
+ if (!out.IsInvertible())
+ return false;
+ out = out.Inverse();
+ return true;
+}
+
+void GeometryMapperTransformCache::Update(
+ const TransformPaintPropertyNode& node) {
+ DCHECK_NE(cache_generation_, s_global_generation);
+ cache_generation_ = s_global_generation;
+
+ if (!node.Parent()) {
+ to_screen_.MakeIdentity();
+ to_screen_is_invertible_ = true;
+ projection_from_screen_.MakeIdentity();
+ projection_from_screen_is_valid_ = true;
+ plane_root_ = &node;
+ to_plane_root_.MakeIdentity();
+ from_plane_root_.MakeIdentity();
+ return;
+ }
+
+ const GeometryMapperTransformCache& parent =
+ node.Parent()->GetTransformCache();
+
+ TransformationMatrix local = node.Matrix();
+ local.ApplyTransformOrigin(node.Origin());
+
+ to_screen_ = parent.to_screen_;
+ if (node.FlattensInheritedTransform())
+ to_screen_.FlattenTo2d();
+ to_screen_.Multiply(local);
+ to_screen_is_invertible_ = to_screen_.IsInvertible();
+ projection_from_screen_is_valid_ =
+ InverseProjection(to_screen_, projection_from_screen_);
+
+ if (!local.IsFlat() || !local.IsInvertible()) {
+ plane_root_ = &node;
+ to_plane_root_.MakeIdentity();
+ from_plane_root_.MakeIdentity();
+ } else { // (local.IsFlat() && local.IsInvertible())
+ plane_root_ = parent.plane_root_;
+ to_plane_root_ = parent.to_plane_root_;
+ to_plane_root_.Multiply(local);
+ from_plane_root_ = local.Inverse();
+ from_plane_root_.Multiply(parent.from_plane_root_);
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache.h b/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache.h
new file mode 100644
index 00000000000..cadd1f5b3ad
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache.h
@@ -0,0 +1,123 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_GEOMETRY_MAPPER_TRANSFORM_CACHE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_GEOMETRY_MAPPER_TRANSFORM_CACHE_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+#include "third_party/blink/renderer/platform/transforms/transformation_matrix.h"
+
+namespace blink {
+
+class TransformPaintPropertyNode;
+
+// A GeometryMapperTransformCache hangs off a TransformPaintPropertyNode.
+// It stores useful intermediate results such as screen matrix for geometry
+// queries.
+class PLATFORM_EXPORT GeometryMapperTransformCache {
+ USING_FAST_MALLOC(GeometryMapperTransformCache);
+ public:
+ GeometryMapperTransformCache() = default;
+
+ static void ClearCache();
+
+ void UpdateIfNeeded(const TransformPaintPropertyNode& node) {
+ if (cache_generation_ != s_global_generation)
+ Update(node);
+ DCHECK_EQ(cache_generation_, s_global_generation);
+ }
+
+ const TransformationMatrix& to_screen() const { return to_screen_; }
+ bool to_screen_is_invertible() const { return to_screen_is_invertible_; }
+
+ const TransformationMatrix& projection_from_screen() const {
+ return projection_from_screen_;
+ }
+ bool projection_from_screen_is_valid() const {
+ return projection_from_screen_is_valid_;
+ }
+
+ const TransformationMatrix& to_plane_root() const { return to_plane_root_; }
+ const TransformationMatrix& from_plane_root() const {
+ return from_plane_root_;
+ }
+ const TransformPaintPropertyNode* plane_root() const { return plane_root_; }
+
+ private:
+ void Update(const TransformPaintPropertyNode&);
+
+ static unsigned s_global_generation;
+
+ // The cached values here can be categorized in two logical groups:
+ //
+ // [ Screen Transform ]
+ // to_screen : The screen matrix of the node, as defined by:
+ // to_screen = (flattens_inherited_transform ?
+ // flatten(parent.to_screen) : parent.to_screen) * local
+ // to_screen_is_invertible : Whether to_screen is invertible.
+ // projection_from_screen : Back projection from screen.
+ // projection_from_screen = flatten(to_screen) ^ -1
+ // Undefined if the inverse projection doesn't exist.
+ // Guaranteed to be flat.
+ // projection_from_screen_is_valid : Whether projection_from_screen
+ // is defined.
+ //
+ // [ Plane Root Transform ]
+ // plane_root : The oldest ancestor node such that every intermediate node
+ // in the ancestor chain has a flat and invertible local matrix. In other
+ // words, a node inherits its parent's plane_root if its local matrix is
+ // flat and invertible. Otherwise, it becomes its own plane root.
+ // For example:
+ // <xfrm id="A" matrix="rotateY(10deg)">
+ // <xfrm id="B" flatten_inherited matrix="translateX(10px)"/>
+ // <xfrm id="C" matrix="scaleX(0)">
+ // <xfrm id="D" matrix="scaleX(2)"/>
+ // <xfrm id="E" matrix="rotate(30deg)"/>
+ // </xfrm>
+ // <xfrm id="F" matrix="scaleZ(0)"/>
+ // </xfrm>
+ // A is the plane root of itself because its local matrix is 3D.
+ // B's plane root is A because its local matrix is flat.
+ // C is the plane root of itself because its local matrix is non-invertible.
+ // D and E's plane root is C because their local matrix is flat.
+ // F is the plane root of itself because its local matrix is 3D and
+ // non-invertible.
+ // to_plane_root : The accumulated matrix between this node and plane_root.
+ // to_plane_root = (plane_root == this) ? I : parent.to_plane_root * local
+ // Guaranteed to be flat.
+ // from_plane_root :
+ // from_plane_root = to_plane_root ^ -1
+ // Guaranteed to exist because each local matrices are invertible.
+ // Guaranteed to be flat.
+ // An important invariant is that
+ // flatten(to_screen) = flatten(plane_root.to_screen) * to_plane_root
+ // Proof by induction:
+ // If plane_root == this,
+ // flatten(plane_root.to_screen) * to_plane_root = flatten(to_screen) * I
+ // = flatten(to_screen)
+ // Otherwise,
+ // flatten(to_screen) = flatten((flattens_inherited_transform ?
+ // flatten(parent.to_screen) : parent.to_screen) * local)
+ // Because local is known to be flat,
+ // = flatten((flattens_inherited_transform ?
+ // flatten(parent.to_screen) : parent.to_screen) * flatten(local))
+ // Then by flatten lemma (https://goo.gl/DNKyOc),
+ // = flatten(parent.to_screen) * local
+ // = flatten(parent.plane_root.to_screen) * parent.to_plane_root * local
+ // = flatten(plane_root.to_screen) * to_plane_root
+ TransformationMatrix to_screen_;
+ TransformationMatrix projection_from_screen_;
+ TransformationMatrix to_plane_root_;
+ TransformationMatrix from_plane_root_;
+ const TransformPaintPropertyNode* plane_root_ = nullptr;
+ unsigned cache_generation_ = s_global_generation - 1;
+ unsigned to_screen_is_invertible_ : 1;
+ unsigned projection_from_screen_is_valid_ : 1;
+ DISALLOW_COPY_AND_ASSIGN(GeometryMapperTransformCache);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_GEOMETRY_MAPPER_TRANSFORM_CACHE_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_artifact.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_artifact.cc
new file mode 100644
index 00000000000..acab9ab196b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_artifact.cc
@@ -0,0 +1,113 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/paint_artifact.h"
+
+#include "cc/paint/display_item_list.h"
+#include "third_party/blink/public/platform/web_display_item_list.h"
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
+#include "third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h"
+#include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/skia/include/core/SkRegion.h"
+
+namespace blink {
+
+namespace {
+
+void ComputeChunkBoundsAndOpaqueness(const DisplayItemList& display_items,
+ Vector<PaintChunk>& paint_chunks) {
+ for (PaintChunk& chunk : paint_chunks) {
+ FloatRect bounds;
+ SkRegion known_to_be_opaque_region;
+ for (const DisplayItem& item : display_items.ItemsInPaintChunk(chunk)) {
+ bounds.Unite(item.VisualRect());
+ if (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled() ||
+ !item.IsDrawing())
+ continue;
+ const auto& drawing = static_cast<const DrawingDisplayItem&>(item);
+ if (drawing.GetPaintRecord() && drawing.KnownToBeOpaque()) {
+ known_to_be_opaque_region.op(
+ SkIRect(EnclosedIntRect(drawing.VisualRect())),
+ SkRegion::kUnion_Op);
+ }
+ }
+ chunk.bounds = bounds;
+ if (known_to_be_opaque_region.contains(EnclosingIntRect(bounds)))
+ chunk.known_to_be_opaque = true;
+ }
+}
+
+} // namespace
+
+PaintArtifact::PaintArtifact() : display_item_list_(0) {}
+
+PaintArtifact::PaintArtifact(DisplayItemList display_items,
+ Vector<PaintChunk> paint_chunks)
+ : display_item_list_(std::move(display_items)),
+ paint_chunks_(std::move(paint_chunks)) {
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled())
+ ComputeChunkBoundsAndOpaqueness(display_item_list_, paint_chunks_);
+}
+
+PaintArtifact::PaintArtifact(PaintArtifact&& source)
+ : display_item_list_(std::move(source.display_item_list_)),
+ paint_chunks_(std::move(source.paint_chunks_)) {}
+
+PaintArtifact::~PaintArtifact() = default;
+
+PaintArtifact& PaintArtifact::operator=(PaintArtifact&& source) {
+ display_item_list_ = std::move(source.display_item_list_);
+ paint_chunks_ = std::move(source.paint_chunks_);
+ return *this;
+}
+
+void PaintArtifact::Reset() {
+ display_item_list_.Clear();
+ paint_chunks_.clear();
+}
+
+size_t PaintArtifact::ApproximateUnsharedMemoryUsage() const {
+ return sizeof(*this) + display_item_list_.MemoryUsageInBytes() +
+ paint_chunks_.capacity() * sizeof(paint_chunks_[0]);
+}
+
+void PaintArtifact::Replay(GraphicsContext& graphics_context,
+ const PropertyTreeState& replay_state,
+ const IntPoint& offset) const {
+ if (!RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ DCHECK(offset == IntPoint());
+ TRACE_EVENT0("blink,benchmark", "PaintArtifact::replay");
+ for (const DisplayItem& display_item : display_item_list_)
+ display_item.Replay(graphics_context);
+ } else {
+ Replay(*graphics_context.Canvas(), replay_state, offset);
+ }
+}
+
+void PaintArtifact::Replay(PaintCanvas& canvas,
+ const PropertyTreeState& replay_state,
+ const IntPoint& offset) const {
+ TRACE_EVENT0("blink,benchmark", "PaintArtifact::replay");
+ DCHECK(RuntimeEnabledFeatures::SlimmingPaintV175Enabled());
+ scoped_refptr<cc::DisplayItemList> display_item_list =
+ PaintChunksToCcLayer::Convert(
+ PaintChunks(), replay_state, gfx::Vector2dF(offset.X(), offset.Y()),
+ GetDisplayItemList(),
+ cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer);
+ canvas.drawPicture(display_item_list->ReleaseAsRecord());
+}
+
+DISABLE_CFI_PERF
+void PaintArtifact::AppendToWebDisplayItemList(
+ const FloatSize& visual_rect_offset,
+ WebDisplayItemList* list) const {
+ TRACE_EVENT0("blink,benchmark", "PaintArtifact::appendToWebDisplayItemList");
+ for (const DisplayItem& item : display_item_list_)
+ item.AppendToWebDisplayItemList(visual_rect_offset, list);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_artifact.h b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_artifact.h
new file mode 100644
index 00000000000..f54b92f4370
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_artifact.h
@@ -0,0 +1,96 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_ARTIFACT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_ARTIFACT_H_
+
+#include "third_party/blink/renderer/platform/graphics/paint/display_item_list.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_chunk.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_chunk_subset.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class GraphicsContext;
+class WebDisplayItemList;
+
+// The output of painting, consisting of a series of drawings in paint order,
+// partitioned into discontiguous chunks with a common set of paint properties
+// (i.e. associated with the same transform, clip, effects, etc.).
+//
+// It represents a particular state of the world, and should be immutable
+// (const) to most of its users.
+//
+// Unless its dangerous accessors are used, it promises to be in a reasonable
+// state (e.g. chunk bounding boxes computed).
+//
+// Reminder: moved-from objects may not be in a known state. They can only
+// safely be assigned to or destroyed.
+class PLATFORM_EXPORT PaintArtifact final {
+ DISALLOW_NEW();
+ WTF_MAKE_NONCOPYABLE(PaintArtifact);
+
+ public:
+ PaintArtifact();
+ PaintArtifact(DisplayItemList, Vector<PaintChunk>);
+ PaintArtifact(PaintArtifact&&);
+ ~PaintArtifact();
+
+ PaintArtifact& operator=(PaintArtifact&&);
+
+ bool IsEmpty() const { return display_item_list_.IsEmpty(); }
+
+ DisplayItemList& GetDisplayItemList() { return display_item_list_; }
+ const DisplayItemList& GetDisplayItemList() const {
+ return display_item_list_;
+ }
+
+ Vector<PaintChunk>& PaintChunks() { return paint_chunks_; }
+ const Vector<PaintChunk>& PaintChunks() const { return paint_chunks_; }
+
+ PaintChunkSubset GetPaintChunkSubset(
+ const Vector<size_t>& subset_indices) const {
+ return PaintChunkSubset(paint_chunks_, subset_indices);
+ }
+
+ Vector<PaintChunk>::const_iterator FindChunkByDisplayItemIndex(
+ size_t index) const {
+ return FindChunkInVectorByDisplayItemIndex(paint_chunks_, index);
+ }
+
+ // Resets to an empty paint artifact.
+ void Reset();
+
+ // Returns the approximate memory usage, excluding memory likely to be
+ // shared with the embedder after copying to WebDisplayItemList.
+ size_t ApproximateUnsharedMemoryUsage() const;
+
+ // Draws the paint artifact to a GraphicsContext.
+ // In SPv175+ mode, replays into the ancestor state given by |replay_state|.
+ void Replay(GraphicsContext&,
+ const PropertyTreeState& replay_state,
+ const IntPoint& offset = IntPoint()) const;
+
+ // Draws the paint artifact to a PaintCanvas, into the ancestor state given
+ // by |replay_state|. For SPv175+ only.
+ void Replay(PaintCanvas&,
+ const PropertyTreeState& replay_state,
+ const IntPoint& offset = IntPoint()) const;
+
+ // Writes the paint artifact into a WebDisplayItemList.
+ void AppendToWebDisplayItemList(const FloatSize& visual_rect_offset,
+ WebDisplayItemList*) const;
+
+ private:
+ DisplayItemList display_item_list_;
+ Vector<PaintChunk> paint_chunks_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_ARTIFACT_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_canvas.h b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_canvas.h
new file mode 100644
index 00000000000..1b65ffd7783
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_canvas.h
@@ -0,0 +1,17 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_CANVAS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_CANVAS_H_
+
+#include "cc/paint/paint_canvas.h"
+#include "cc/paint/skia_paint_canvas.h"
+
+namespace blink {
+using cc::PaintCanvas;
+using cc::SkiaPaintCanvas;
+using cc::PaintCanvasAutoRestore;
+}
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_CANVAS_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunk.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunk.cc
new file mode 100644
index 00000000000..61d995b0914
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunk.cc
@@ -0,0 +1,24 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/paint_chunk.h"
+
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+String PaintChunk::ToString() const {
+ return String::Format(
+ "PaintChunk(begin=%zu, end=%zu, id=%s cacheable=%d props=(%s) bounds=%s "
+ "known_to_be_opaque=%d raster_invalidation_rects=%zu)",
+ begin_index, end_index, id.ToString().Ascii().data(), is_cacheable,
+ properties.ToString().Ascii().data(), bounds.ToString().Ascii().data(),
+ known_to_be_opaque, raster_invalidation_rects.size());
+}
+
+std::ostream& operator<<(std::ostream& os, const PaintChunk& chunk) {
+ return os << chunk.ToString().Utf8().data();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunk.h b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunk.h
new file mode 100644
index 00000000000..16c2ab11ee8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunk.h
@@ -0,0 +1,158 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_CHUNK_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_CHUNK_H_
+
+#include <iosfwd>
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/graphics/paint/display_item.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_chunk_properties.h"
+#include "third_party/blink/renderer/platform/graphics/paint/raster_invalidation_tracking.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+// A contiguous sequence of drawings with common paint properties.
+//
+// This is expected to be owned by the paint artifact which also owns the
+// related drawings.
+//
+// This is a Slimming Paint v175+ class.
+struct PLATFORM_EXPORT PaintChunk {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ using Id = DisplayItem::Id;
+
+ enum Cacheable {
+ kCacheable,
+ kUncacheable,
+ };
+
+ PaintChunk(size_t begin,
+ size_t end,
+ const Id& id,
+ const PaintChunkProperties& props,
+ Cacheable cacheable = kCacheable)
+ : begin_index(begin),
+ end_index(end),
+ id(id),
+ properties(props),
+ outset_for_raster_effects(0),
+ known_to_be_opaque(false),
+ is_cacheable(cacheable == kCacheable),
+ client_is_just_created(id.client.IsJustCreated()) {}
+
+ size_t size() const {
+ DCHECK_GE(end_index, begin_index);
+ return end_index - begin_index;
+ }
+
+ // Check if a new PaintChunk (this) created in the latest paint matches an old
+ // PaintChunk created in the previous paint.
+ bool Matches(const PaintChunk& old) const {
+ return old.is_cacheable && Matches(old.id);
+ }
+
+ bool Matches(const Id& other_id) const {
+ if (!is_cacheable || id != other_id)
+ return false;
+#if DCHECK_IS_ON()
+ DCHECK(id.client.IsAlive());
+#endif
+ // A chunk whose client is just created should not match any cached chunk,
+ // even if it's id equals the old chunk's id (which may happen if this
+ // chunk's client is just created at the same address of the old chunk's
+ // deleted client).
+ return !client_is_just_created;
+ }
+
+ // Index of the first drawing in this chunk.
+ size_t begin_index;
+
+ // Index of the first drawing not in this chunk, so that there are
+ // |endIndex - beginIndex| drawings in the chunk.
+ size_t end_index;
+
+ // Identifier of this chunk. If it has a value, it should be unique. This is
+ // used to match a new chunk to a cached old chunk to track changes of chunk
+ // contents, so the id should be stable across document cycles. If the
+ // contents of the chunk can't be cached (e.g. it's created when
+ // PaintController is skipping the cache, normally because display items can't
+ // be uniquely identified), |id| is nullopt so that the chunk won't match any
+ // other chunk.
+ Id id;
+
+ // The paint properties which apply to this chunk.
+ PaintChunkProperties properties;
+
+ // The total bounds of this paint chunk's contents, in the coordinate space of
+ // the containing transform node.
+ FloatRect bounds;
+
+ // Some raster effects can exceed |bounds| in the rasterization space. This
+ // is the maximum DisplayItemClient::VisualRectOutsetForRasterEffects() of
+ // all clients of items in this chunk.
+ float outset_for_raster_effects;
+
+ // True if the bounds are filled entirely with opaque contents.
+ bool known_to_be_opaque : 1;
+
+ bool is_cacheable : 1;
+
+ // TODO(wangxianzhu): The following fields are 'mutable' for
+ // ContentLayerClientImpl to clear them, which will be unnecessary if we don't
+ // call PaintArtifactCompositor::Update() when paint artifact is unchanged.
+ mutable bool client_is_just_created : 1;
+
+ // Rectangles that need to be re-rasterized in this chunk, in the coordinate
+ // space of the containing transform node.
+ mutable Vector<FloatRect> raster_invalidation_rects;
+
+ mutable Vector<RasterInvalidationInfo> raster_invalidation_tracking;
+
+ String ToString() const;
+};
+
+inline bool operator==(const PaintChunk& a, const PaintChunk& b) {
+ return a.begin_index == b.begin_index && a.end_index == b.end_index &&
+ a.id == b.id && a.properties == b.properties && a.bounds == b.bounds &&
+ a.known_to_be_opaque == b.known_to_be_opaque &&
+ a.is_cacheable == b.is_cacheable &&
+ a.raster_invalidation_rects == b.raster_invalidation_rects;
+}
+
+inline bool operator!=(const PaintChunk& a, const PaintChunk& b) {
+ return !(a == b);
+}
+
+inline bool ChunkLessThanIndex(const PaintChunk& chunk, size_t index) {
+ return chunk.end_index <= index;
+}
+
+inline Vector<PaintChunk>::iterator FindChunkInVectorByDisplayItemIndex(
+ Vector<PaintChunk>& chunks,
+ size_t index) {
+ auto chunk =
+ std::lower_bound(chunks.begin(), chunks.end(), index, ChunkLessThanIndex);
+ DCHECK(chunk == chunks.end() ||
+ (index >= chunk->begin_index && index < chunk->end_index));
+ return chunk;
+}
+
+inline Vector<PaintChunk>::const_iterator FindChunkInVectorByDisplayItemIndex(
+ const Vector<PaintChunk>& chunks,
+ size_t index) {
+ return FindChunkInVectorByDisplayItemIndex(
+ const_cast<Vector<PaintChunk>&>(chunks), index);
+}
+
+PLATFORM_EXPORT std::ostream& operator<<(std::ostream&, const PaintChunk&);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_CHUNK_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunk_properties.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunk_properties.cc
new file mode 100644
index 00000000000..c3d59636b88
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunk_properties.cc
@@ -0,0 +1,44 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/paint_chunk_properties.h"
+
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+String PaintChunkProperties::ToString() const {
+ StringBuilder sb;
+ if (property_tree_state.Transform()) {
+ sb.Append("transform=(");
+ sb.Append(property_tree_state.Transform()->ToString());
+ sb.Append(")");
+ }
+ if (property_tree_state.Clip()) {
+ if (sb.length())
+ sb.Append(", ");
+ sb.Append("clip=(");
+ sb.Append(property_tree_state.Clip()->ToString());
+ sb.Append(")");
+ }
+ if (property_tree_state.Effect()) {
+ if (sb.length())
+ sb.Append(", ");
+ sb.Append("effect=(");
+ sb.Append(property_tree_state.Effect()->ToString());
+ sb.Append(")");
+ }
+ if (sb.length())
+ sb.Append(", ");
+ sb.Append("backface_hidden=");
+ sb.Append(backface_hidden ? "1" : "0");
+ return sb.ToString();
+}
+
+std::ostream& operator<<(std::ostream& os,
+ const PaintChunkProperties& properties) {
+ return os << properties.ToString().Utf8().data();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunk_properties.h b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunk_properties.h
new file mode 100644
index 00000000000..0de460ada6a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunk_properties.h
@@ -0,0 +1,60 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_CHUNK_PROPERTIES_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_CHUNK_PROPERTIES_H_
+
+#include <iosfwd>
+#include "third_party/blink/renderer/platform/graphics/paint/ref_counted_property_tree_state.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+// The set of paint properties applying to a |PaintChunk|. These properties are
+// not local-only paint style parameters such as color, but instead represent
+// the hierarchy of transforms, clips, and effects that apply to a contiguous
+// chunk of display items. A single DisplayItemClient can generate multiple
+// properties of the same type and this struct represents the total state of all
+// properties for a given |PaintChunk|.
+//
+// This differs from |ObjectPaintProperties| because it only stores one property
+// for each type (e.g., either transform or perspective, but not both).
+struct PLATFORM_EXPORT PaintChunkProperties {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ explicit PaintChunkProperties(const PropertyTreeState& state)
+ : property_tree_state(state), backface_hidden(false) {}
+
+ PaintChunkProperties()
+ : property_tree_state(nullptr, nullptr, nullptr),
+ backface_hidden(false) {}
+
+ RefCountedPropertyTreeState property_tree_state;
+ bool backface_hidden;
+
+ String ToString() const;
+};
+
+// Equality is based only on the pointers and is not 'deep' which would require
+// crawling the entire property tree to compute.
+inline bool operator==(const PaintChunkProperties& a,
+ const PaintChunkProperties& b) {
+ return a.property_tree_state == b.property_tree_state &&
+ a.backface_hidden == b.backface_hidden;
+}
+
+inline bool operator!=(const PaintChunkProperties& a,
+ const PaintChunkProperties& b) {
+ return !(a == b);
+}
+
+PLATFORM_EXPORT std::ostream& operator<<(std::ostream&,
+ const PaintChunkProperties);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_CHUNK_PROPERTIES_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunk_subset.h b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunk_subset.h
new file mode 100644
index 00000000000..6004a2299d5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunk_subset.h
@@ -0,0 +1,66 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_CHUNK_SUBSET_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_CHUNK_SUBSET_H_
+
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+struct PaintChunk;
+
+// Provides access to a subset of a Vector<PaintChunk>.
+class PaintChunkSubset {
+ public:
+ PaintChunkSubset(const Vector<PaintChunk>& chunks,
+ const Vector<size_t>& subset_indices)
+ : chunks_(chunks), subset_indices_(&subset_indices) {}
+
+ // For convenience, this allows using a Vector<PaintChunk> in place of
+ // PaintChunkSubset to include all paint chunks.
+ PaintChunkSubset(const Vector<PaintChunk>& chunks)
+ : chunks_(chunks), subset_indices_(nullptr) {}
+
+ class Iterator {
+ public:
+ const PaintChunk& operator*() const { return subset_[offset_]; }
+ bool operator!=(const Iterator& other) const {
+ DCHECK_EQ(&subset_, &other.subset_);
+ return offset_ != other.offset_;
+ }
+ const Iterator& operator++() {
+ ++offset_;
+ return *this;
+ }
+
+ private:
+ friend class PaintChunkSubset;
+ Iterator(const PaintChunkSubset& subset, size_t offset)
+ : subset_(subset), offset_(offset) {}
+
+ const PaintChunkSubset& subset_;
+ size_t offset_;
+ };
+
+ Iterator begin() const { return Iterator(*this, 0); }
+
+ Iterator end() const { return Iterator(*this, size()); }
+
+ size_t size() const {
+ return subset_indices_ ? subset_indices_->size() : chunks_.size();
+ }
+
+ const PaintChunk& operator[](int i) const {
+ return chunks_[subset_indices_ ? (*subset_indices_)[i] : i];
+ }
+
+ private:
+ const Vector<PaintChunk>& chunks_;
+ const Vector<size_t>* subset_indices_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_CHUNK_SUBSET_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunk_test.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunk_test.cc
new file mode 100644
index 00000000000..309b621bc5b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunk_test.cc
@@ -0,0 +1,89 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/paint_chunk.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/testing/fake_display_item_client.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+
+namespace blink {
+
+TEST(PaintChunkTest, matchesSame) {
+ PaintChunkProperties properties;
+ FakeDisplayItemClient client;
+ client.UpdateCacheGeneration();
+ DisplayItem::Id id(client, DisplayItem::kDrawingFirst);
+ EXPECT_TRUE(PaintChunk(0, 1, id, properties)
+ .Matches(PaintChunk(0, 1, id, properties)));
+}
+
+TEST(PaintChunkTest, matchesEqual) {
+ PaintChunkProperties properties;
+ FakeDisplayItemClient client;
+ client.UpdateCacheGeneration();
+ DisplayItem::Id id(client, DisplayItem::kDrawingFirst);
+ DisplayItem::Id id_equal = id;
+ EXPECT_TRUE(PaintChunk(0, 1, id, properties)
+ .Matches(PaintChunk(0, 1, id_equal, properties)));
+ EXPECT_TRUE(PaintChunk(0, 1, id_equal, properties)
+ .Matches(PaintChunk(0, 1, id, properties)));
+}
+
+TEST(PaintChunkTest, IdNotMatches) {
+ PaintChunkProperties properties;
+ FakeDisplayItemClient client1;
+ client1.UpdateCacheGeneration();
+ DisplayItem::Id id1(client1, DisplayItem::kDrawingFirst);
+
+ FakeDisplayItemClient client2;
+ client2.UpdateCacheGeneration();
+ DisplayItem::Id id2(client2, DisplayItem::kDrawingFirst);
+ EXPECT_FALSE(PaintChunk(0, 1, id2, properties)
+ .Matches(PaintChunk(0, 1, id1, properties)));
+}
+
+TEST(PaintChunkTest, IdNotMatchesUncacheable) {
+ PaintChunkProperties properties;
+ FakeDisplayItemClient client;
+ client.UpdateCacheGeneration();
+ DisplayItem::Id id(client, DisplayItem::kDrawingFirst);
+ EXPECT_FALSE(PaintChunk(0, 1, id, properties, PaintChunk::kUncacheable)
+ .Matches(PaintChunk(0, 1, id, properties)));
+ EXPECT_FALSE(
+ PaintChunk(0, 1, id, properties)
+ .Matches(PaintChunk(0, 1, id, properties, PaintChunk::kUncacheable)));
+ EXPECT_FALSE(
+ PaintChunk(0, 1, id, properties, PaintChunk::kUncacheable)
+ .Matches(PaintChunk(0, 1, id, properties, PaintChunk::kUncacheable)));
+}
+
+TEST(PaintChunkTest, IdNotMatchesJustCreated) {
+ PaintChunkProperties properties;
+ Optional<FakeDisplayItemClient> client;
+ client.emplace();
+ EXPECT_TRUE(client->IsJustCreated());
+ // Invalidation won't change the "just created" status.
+ client->SetDisplayItemsUncached();
+ EXPECT_TRUE(client->IsJustCreated());
+
+ DisplayItem::Id id(*client, DisplayItem::kDrawingFirst);
+ // A chunk of a newly created client doesn't match any chunk because it's
+ // never cached.
+ EXPECT_FALSE(PaintChunk(0, 1, id, properties)
+ .Matches(PaintChunk(0, 1, id, properties)));
+
+ client->UpdateCacheGeneration();
+ EXPECT_TRUE(PaintChunk(0, 1, id, properties)
+ .Matches(PaintChunk(0, 1, id, properties)));
+
+ // Delete the current object and create a new object at the same address.
+ client = WTF::nullopt;
+ client.emplace();
+ EXPECT_TRUE(client->IsJustCreated());
+ EXPECT_FALSE(PaintChunk(0, 1, id, properties)
+ .Matches(PaintChunk(0, 1, id, properties)));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunker.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunker.cc
new file mode 100644
index 00000000000..408269c0ade
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunker.cc
@@ -0,0 +1,97 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/paint_chunker.h"
+
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+
+namespace blink {
+
+PaintChunker::PaintChunker() : force_new_chunk_(false) {}
+
+PaintChunker::~PaintChunker() = default;
+
+void PaintChunker::UpdateCurrentPaintChunkProperties(
+ const Optional<PaintChunk::Id>& chunk_id,
+ const PaintChunkProperties& properties) {
+ DCHECK(RuntimeEnabledFeatures::SlimmingPaintV175Enabled());
+
+ if (chunk_id)
+ current_chunk_id_.emplace(*chunk_id);
+ else
+ current_chunk_id_ = WTF::nullopt;
+ current_properties_ = properties;
+}
+
+bool PaintChunker::IncrementDisplayItemIndex(const DisplayItem& item) {
+ DCHECK(RuntimeEnabledFeatures::SlimmingPaintV175Enabled());
+
+#if DCHECK_IS_ON()
+ // Property nodes should never be null because they should either be set to
+ // properties created by a LayoutObject/FrameView, or be set to a non-null
+ // root node. If these DCHECKs are hit we are missing a call to update the
+ // properties. See: ScopedPaintChunkProperties.
+ // TODO(trchen): Enable this check for SPv175 too. Some drawable layers
+ // don't paint with property tree yet, e.g. scrollbar layers.
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ DCHECK(current_properties_.property_tree_state.Transform());
+ DCHECK(current_properties_.property_tree_state.Clip());
+ DCHECK(current_properties_.property_tree_state.Effect());
+ }
+#endif
+
+ bool item_forces_new_chunk = item.IsForeignLayer() || item.IsScrollHitTest();
+ if (item_forces_new_chunk) {
+ force_new_chunk_ = true;
+ // Clear current_chunk_id_ so that we will use the current display item's id
+ // as the chunk id, and any display items after the forcing item without a
+ // new chunk id will be treated as having no id to avoid the chunk from
+ // using the same id as the chunk before the forced chunk.
+ current_chunk_id_ = WTF::nullopt;
+ }
+
+ size_t new_chunk_begin_index;
+ if (chunks_.IsEmpty()) {
+ new_chunk_begin_index = 0;
+ } else {
+ auto& last_chunk = chunks_.back();
+ if (!force_new_chunk_ && current_properties_ == last_chunk.properties &&
+ (!current_chunk_id_ || current_chunk_id_ == last_chunk.id)) {
+ // Continue the current chunk.
+ last_chunk.end_index++;
+ return false;
+ }
+ new_chunk_begin_index = last_chunk.end_index;
+ }
+
+ auto cacheable =
+ item.SkippedCache() ? PaintChunk::kUncacheable : PaintChunk::kCacheable;
+ PaintChunk new_chunk(new_chunk_begin_index, new_chunk_begin_index + 1,
+ current_chunk_id_ ? *current_chunk_id_ : item.GetId(),
+ current_properties_, cacheable);
+ chunks_.push_back(new_chunk);
+
+ // When forcing a new chunk, we still need to force new chunk for the next
+ // display item. Otherwise reset force_new_chunk_ to false.
+ if (!item_forces_new_chunk)
+ force_new_chunk_ = false;
+
+ return true;
+}
+
+void PaintChunker::Clear() {
+ chunks_.clear();
+ current_chunk_id_ = WTF::nullopt;
+ current_properties_ = PaintChunkProperties();
+}
+
+Vector<PaintChunk> PaintChunker::ReleasePaintChunks() {
+ Vector<PaintChunk> chunks;
+ chunks.swap(chunks_);
+ current_chunk_id_ = WTF::nullopt;
+ current_properties_ = PaintChunkProperties();
+ return chunks;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunker.h b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunker.h
new file mode 100644
index 00000000000..18ce23899b3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunker.h
@@ -0,0 +1,80 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_CHUNKER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_CHUNKER_H_
+
+#include "third_party/blink/renderer/platform/graphics/paint/display_item.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_chunk.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_chunk_properties.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+// Accepts information about changes to |PaintChunkProperties| as drawings are
+// accumulated, and produces a series of paint chunks: contiguous ranges of the
+// display list with identical |PaintChunkProperties|.
+class PLATFORM_EXPORT PaintChunker final {
+ DISALLOW_NEW();
+ WTF_MAKE_NONCOPYABLE(PaintChunker);
+
+ public:
+ PaintChunker();
+ ~PaintChunker();
+
+ bool IsInInitialState() const {
+ return chunks_.IsEmpty() && current_properties_ == PaintChunkProperties();
+ }
+
+ const PaintChunkProperties& CurrentPaintChunkProperties() const {
+ return current_properties_;
+ }
+ void UpdateCurrentPaintChunkProperties(const Optional<PaintChunk::Id>&,
+ const PaintChunkProperties&);
+
+ void ForceNewChunk() { force_new_chunk_ = true; }
+
+ // Returns true if a new chunk is created.
+ bool IncrementDisplayItemIndex(const DisplayItem&);
+
+ const Vector<PaintChunk>& PaintChunks() const { return chunks_; }
+
+ PaintChunk& PaintChunkAt(size_t i) { return chunks_[i]; }
+ size_t LastChunkIndex() const {
+ return chunks_.IsEmpty() ? kNotFound : chunks_.size() - 1;
+ }
+ PaintChunk& LastChunk() { return chunks_.back(); }
+
+ PaintChunk& FindChunkByDisplayItemIndex(size_t index) {
+ auto chunk = FindChunkInVectorByDisplayItemIndex(chunks_, index);
+ DCHECK(chunk != chunks_.end());
+ return *chunk;
+ }
+
+ void Clear();
+
+ // Releases the generated paint chunk list and resets the state of this
+ // object.
+ Vector<PaintChunk> ReleasePaintChunks();
+
+ private:
+ Vector<PaintChunk> chunks_;
+ // TODO(pdr): Refactor current_chunk_id_ so that it is always the equal to
+ // the current chunk id. This is currently not true when there is a forced
+ // chunk because the current_chunk_id_ is cleared for subsequent chunks, even
+ // though those subsequent chunks will have valid chunk ids.
+ Optional<PaintChunk::Id> current_chunk_id_;
+ PaintChunkProperties current_properties_;
+ // True when an item forces a new chunk (e.g., foreign display items), and for
+ // the item following a forced chunk.
+ bool force_new_chunk_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_CHUNKER_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunker_test.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunker_test.cc
new file mode 100644
index 00000000000..3071d77976a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunker_test.cc
@@ -0,0 +1,409 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/paint_chunker.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/testing/paint_property_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+
+using ::blink::test::CreateOpacityOnlyEffect;
+using ::blink::test::DefaultPaintChunkProperties;
+using testing::ElementsAre;
+
+namespace blink {
+namespace {
+
+class PaintChunkerTest : public testing::Test,
+ private ScopedSlimmingPaintV175ForTest {
+ public:
+ PaintChunkerTest() : ScopedSlimmingPaintV175ForTest(true) {}
+
+ protected:
+ class TestDisplayItemClient : public DisplayItemClient {
+ String DebugName() const final { return "Test"; }
+ LayoutRect VisualRect() const final { return LayoutRect(); }
+ };
+ TestDisplayItemClient client_;
+};
+
+DisplayItem::Type DisplayItemType(int offset) {
+ return static_cast<DisplayItem::Type>(DisplayItem::kDrawingFirst + offset);
+}
+
+class TestChunkerDisplayItem : public DisplayItem {
+ public:
+ TestChunkerDisplayItem(const DisplayItemClient& client,
+ DisplayItem::Type type = DisplayItem::kDrawingFirst)
+ : DisplayItem(client, type, sizeof(*this)) {}
+
+ void Replay(GraphicsContext&) const final { NOTREACHED(); }
+ void AppendToWebDisplayItemList(const FloatSize&,
+ WebDisplayItemList*) const final {
+ NOTREACHED();
+ }
+};
+
+class TestDisplayItemRequiringSeparateChunk : public TestChunkerDisplayItem {
+ public:
+ TestDisplayItemRequiringSeparateChunk(const DisplayItemClient& client)
+ : TestChunkerDisplayItem(client, DisplayItem::kForeignLayerPlugin) {}
+};
+
+TEST_F(PaintChunkerTest, Empty) {
+ Vector<PaintChunk> chunks = PaintChunker().ReleasePaintChunks();
+ ASSERT_TRUE(chunks.IsEmpty());
+}
+
+TEST_F(PaintChunkerTest, SingleNonEmptyRange) {
+ PaintChunker chunker;
+ PaintChunk::Id id(client_, DisplayItemType(1));
+ chunker.UpdateCurrentPaintChunkProperties(id, DefaultPaintChunkProperties());
+ chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
+ chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
+ Vector<PaintChunk> chunks = chunker.ReleasePaintChunks();
+
+ EXPECT_THAT(chunks,
+ ElementsAre(PaintChunk(0, 2, id, DefaultPaintChunkProperties())));
+}
+
+TEST_F(PaintChunkerTest, SamePropertiesTwiceCombineIntoOneChunk) {
+ PaintChunker chunker;
+ PaintChunk::Id id(client_, DisplayItemType(1));
+ chunker.UpdateCurrentPaintChunkProperties(id, DefaultPaintChunkProperties());
+ chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
+ chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
+ chunker.UpdateCurrentPaintChunkProperties(id, DefaultPaintChunkProperties());
+ chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
+ Vector<PaintChunk> chunks = chunker.ReleasePaintChunks();
+
+ EXPECT_THAT(chunks,
+ ElementsAre(PaintChunk(0, 3, id, DefaultPaintChunkProperties())));
+}
+
+TEST_F(PaintChunkerTest, BuildMultipleChunksWithSinglePropertyChanging) {
+ PaintChunker chunker;
+ PaintChunk::Id id1(client_, DisplayItemType(1));
+ chunker.UpdateCurrentPaintChunkProperties(id1, DefaultPaintChunkProperties());
+ chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
+ chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
+
+ PaintChunkProperties simple_transform = DefaultPaintChunkProperties();
+ simple_transform.property_tree_state.SetTransform(
+ TransformPaintPropertyNode::Create(nullptr,
+ TransformationMatrix(0, 1, 2, 3, 4, 5),
+ FloatPoint3D(9, 8, 7))
+ .get());
+
+ PaintChunk::Id id2(client_, DisplayItemType(2));
+ chunker.UpdateCurrentPaintChunkProperties(id2, simple_transform);
+ chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
+
+ PaintChunkProperties another_transform = DefaultPaintChunkProperties();
+ another_transform.property_tree_state.SetTransform(
+ TransformPaintPropertyNode::Create(nullptr,
+ TransformationMatrix(0, 1, 2, 3, 4, 5),
+ FloatPoint3D(9, 8, 7))
+ .get());
+ PaintChunk::Id id3(client_, DisplayItemType(3));
+ chunker.UpdateCurrentPaintChunkProperties(id3, another_transform);
+ chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
+
+ Vector<PaintChunk> chunks = chunker.ReleasePaintChunks();
+
+ EXPECT_THAT(chunks,
+ ElementsAre(PaintChunk(0, 2, id1, DefaultPaintChunkProperties()),
+ PaintChunk(2, 3, id2, simple_transform),
+ PaintChunk(3, 4, id3, another_transform)));
+}
+
+TEST_F(PaintChunkerTest, BuildMultipleChunksWithDifferentPropertyChanges) {
+ PaintChunker chunker;
+ PaintChunk::Id id1(client_, DisplayItemType(1));
+ chunker.UpdateCurrentPaintChunkProperties(id1, DefaultPaintChunkProperties());
+ chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
+
+ PaintChunkProperties simple_transform = DefaultPaintChunkProperties();
+ simple_transform.property_tree_state.SetTransform(
+ TransformPaintPropertyNode::Create(nullptr,
+ TransformationMatrix(0, 0, 0, 0, 0, 0),
+ FloatPoint3D(9, 8, 7))
+ .get());
+ PaintChunk::Id id2(client_, DisplayItemType(2));
+ chunker.UpdateCurrentPaintChunkProperties(id2, simple_transform);
+ chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
+ chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
+
+ PaintChunkProperties simple_transform_and_effect =
+ DefaultPaintChunkProperties();
+ simple_transform_and_effect.property_tree_state.SetTransform(
+ simple_transform.property_tree_state.Transform());
+ simple_transform_and_effect.property_tree_state.SetEffect(
+ CreateOpacityOnlyEffect(EffectPaintPropertyNode::Root(), 0.5f).get());
+ PaintChunk::Id id3(client_, DisplayItemType(3));
+ chunker.UpdateCurrentPaintChunkProperties(id3, simple_transform_and_effect);
+ chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
+ chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
+
+ PaintChunkProperties simple_transform_and_effect_with_updated_transform =
+ DefaultPaintChunkProperties();
+ simple_transform_and_effect_with_updated_transform.property_tree_state
+ .SetTransform(TransformPaintPropertyNode::Create(
+ nullptr, TransformationMatrix(1, 1, 0, 0, 0, 0),
+ FloatPoint3D(9, 8, 7))
+ .get());
+ simple_transform_and_effect_with_updated_transform.property_tree_state
+ .SetEffect(CreateOpacityOnlyEffect(
+ EffectPaintPropertyNode::Root(),
+ simple_transform_and_effect.property_tree_state.Effect()
+ ->Opacity())
+ .get());
+ PaintChunk::Id id4(client_, DisplayItemType(4));
+ chunker.UpdateCurrentPaintChunkProperties(
+ id4, simple_transform_and_effect_with_updated_transform);
+ chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
+ chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
+
+ // Test that going back to a previous chunk property still creates a new
+ // chunk.
+ chunker.UpdateCurrentPaintChunkProperties(WTF::nullopt,
+ simple_transform_and_effect);
+ TestChunkerDisplayItem item_after_restore(client_, DisplayItemType(10));
+ chunker.IncrementDisplayItemIndex(item_after_restore);
+ chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
+
+ Vector<PaintChunk> chunks = chunker.ReleasePaintChunks();
+
+ EXPECT_THAT(
+ chunks,
+ ElementsAre(
+ PaintChunk(0, 1, id1, DefaultPaintChunkProperties()),
+ PaintChunk(1, 3, id2, simple_transform),
+ PaintChunk(3, 5, id3, simple_transform_and_effect),
+ PaintChunk(5, 7, id4,
+ simple_transform_and_effect_with_updated_transform),
+ PaintChunk(7, 9, item_after_restore.GetId(),
+ simple_transform_and_effect)));
+}
+
+TEST_F(PaintChunkerTest, BuildChunksFromNestedTransforms) {
+ // Test that "nested" transforms linearize using the following
+ // sequence of transforms and display items:
+ // <root xform>
+ // <paint>
+ // <a xform>
+ // <paint><paint>
+ // </a xform>
+ // <paint>
+ // </root xform>
+ PaintChunker chunker;
+ PaintChunk::Id id1(client_, DisplayItemType(1));
+ chunker.UpdateCurrentPaintChunkProperties(id1, DefaultPaintChunkProperties());
+ chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
+
+ PaintChunkProperties simple_transform = DefaultPaintChunkProperties();
+ simple_transform.property_tree_state.SetTransform(
+ TransformPaintPropertyNode::Create(nullptr,
+ TransformationMatrix(0, 1, 2, 3, 4, 5),
+ FloatPoint3D(9, 8, 7))
+ .get());
+ PaintChunk::Id id2(client_, DisplayItemType(2));
+ chunker.UpdateCurrentPaintChunkProperties(id2, simple_transform);
+ chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
+ chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
+
+ chunker.UpdateCurrentPaintChunkProperties(WTF::nullopt,
+ DefaultPaintChunkProperties());
+ TestChunkerDisplayItem item_after_restore(client_, DisplayItemType(10));
+ chunker.IncrementDisplayItemIndex(item_after_restore);
+
+ Vector<PaintChunk> chunks = chunker.ReleasePaintChunks();
+
+ EXPECT_THAT(chunks,
+ ElementsAre(PaintChunk(0, 1, id1, DefaultPaintChunkProperties()),
+ PaintChunk(1, 3, id2, simple_transform),
+ PaintChunk(3, 4, item_after_restore.GetId(),
+ DefaultPaintChunkProperties())));
+}
+
+TEST_F(PaintChunkerTest, ChangingPropertiesWithoutItems) {
+ // Test that properties can change without display items being generated.
+ PaintChunker chunker;
+ PaintChunk::Id id1(client_, DisplayItemType(1));
+ chunker.UpdateCurrentPaintChunkProperties(id1, DefaultPaintChunkProperties());
+ chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
+
+ PaintChunkProperties first_transform = DefaultPaintChunkProperties();
+ first_transform.property_tree_state.SetTransform(
+ TransformPaintPropertyNode::Create(nullptr,
+ TransformationMatrix(0, 1, 2, 3, 4, 5),
+ FloatPoint3D(9, 8, 7))
+ .get());
+ PaintChunk::Id id2(client_, DisplayItemType(2));
+ chunker.UpdateCurrentPaintChunkProperties(WTF::nullopt, first_transform);
+
+ PaintChunkProperties second_transform = DefaultPaintChunkProperties();
+ second_transform.property_tree_state.SetTransform(
+ TransformPaintPropertyNode::Create(nullptr,
+ TransformationMatrix(9, 8, 7, 6, 5, 4),
+ FloatPoint3D(3, 2, 1))
+ .get());
+ PaintChunk::Id id3(client_, DisplayItemType(3));
+ chunker.UpdateCurrentPaintChunkProperties(id3, second_transform);
+
+ chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
+ Vector<PaintChunk> chunks = chunker.ReleasePaintChunks();
+
+ EXPECT_THAT(chunks,
+ ElementsAre(PaintChunk(0, 1, id1, DefaultPaintChunkProperties()),
+ PaintChunk(1, 2, id3, second_transform)));
+}
+
+TEST_F(PaintChunkerTest, CreatesSeparateChunksWhenRequested) {
+ // Tests that the chunker creates a separate chunks for display items which
+ // require it.
+ PaintChunker chunker;
+ TestDisplayItemClient client1;
+ TestDisplayItemRequiringSeparateChunk i1(client1);
+ TestDisplayItemClient client2;
+ TestDisplayItemRequiringSeparateChunk i2(client2);
+ TestDisplayItemClient client3;
+ TestDisplayItemRequiringSeparateChunk i3(client3);
+
+ PaintChunk::Id id0(client_, DisplayItemType(0));
+ chunker.UpdateCurrentPaintChunkProperties(id0, DefaultPaintChunkProperties());
+ chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
+ chunker.IncrementDisplayItemIndex(i1);
+ chunker.IncrementDisplayItemIndex(i2);
+ TestChunkerDisplayItem after_i2(client_, DisplayItemType(10));
+ chunker.IncrementDisplayItemIndex(after_i2);
+ chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
+ chunker.IncrementDisplayItemIndex(i3);
+
+ Vector<PaintChunk> chunks = chunker.ReleasePaintChunks();
+ EXPECT_THAT(
+ chunks,
+ ElementsAre(
+ PaintChunk(0, 1, id0, DefaultPaintChunkProperties()),
+ PaintChunk(1, 2, i1.GetId(), DefaultPaintChunkProperties()),
+ PaintChunk(2, 3, i2.GetId(), DefaultPaintChunkProperties()),
+ PaintChunk(3, 5, after_i2.GetId(), DefaultPaintChunkProperties()),
+ PaintChunk(5, 6, i3.GetId(), DefaultPaintChunkProperties())));
+}
+
+TEST_F(PaintChunkerTest, ForceNewChunk) {
+ PaintChunker chunker;
+ PaintChunk::Id id0(client_, DisplayItemType(0));
+ chunker.UpdateCurrentPaintChunkProperties(id0, DefaultPaintChunkProperties());
+ chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
+ chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
+
+ chunker.ForceNewChunk();
+ PaintChunk::Id id1(client_, DisplayItemType(10));
+ chunker.UpdateCurrentPaintChunkProperties(id1, DefaultPaintChunkProperties());
+ chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
+ chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
+
+ PaintChunk::Id id2(client_, DisplayItemType(20));
+ chunker.UpdateCurrentPaintChunkProperties(id2, DefaultPaintChunkProperties());
+ chunker.ForceNewChunk();
+ chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
+ chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
+
+ Vector<PaintChunk> chunks = chunker.ReleasePaintChunks();
+ EXPECT_THAT(
+ chunks,
+ ElementsAre(PaintChunk(0, 2, id0, DefaultPaintChunkProperties()),
+ PaintChunk(2, 4, id1, DefaultPaintChunkProperties()),
+ PaintChunk(4, 6, id2, DefaultPaintChunkProperties())));
+}
+
+class TestScrollHitTestRequiringSeparateChunk : public TestChunkerDisplayItem {
+ public:
+ TestScrollHitTestRequiringSeparateChunk(const DisplayItemClient& client)
+ : TestChunkerDisplayItem(client, DisplayItem::kScrollHitTest) {}
+};
+
+// Ensure that items following a forced chunk begin using the next display
+// item's id.
+TEST_F(PaintChunkerTest, ChunksFollowingForcedChunk) {
+ PaintChunker chunker;
+ TestDisplayItemClient client;
+ TestChunkerDisplayItem before_forced1(client, DisplayItemType(9));
+ TestChunkerDisplayItem before_forced2(client, DisplayItemType(10));
+ TestScrollHitTestRequiringSeparateChunk forced(client);
+ TestChunkerDisplayItem after_forced1(client, DisplayItemType(11));
+ TestChunkerDisplayItem after_forced2(client, DisplayItemType(12));
+
+ PaintChunk::Id id0(client, DisplayItemType(8));
+ chunker.UpdateCurrentPaintChunkProperties(id0, DefaultPaintChunkProperties());
+ // Both before_forced items should be in a chunk together.
+ chunker.IncrementDisplayItemIndex(before_forced1);
+ chunker.IncrementDisplayItemIndex(before_forced2);
+ // The forced scroll hit test item should be in its own chunk.
+ chunker.IncrementDisplayItemIndex(forced);
+ // Both after_forced items should be in a chunk together.
+ chunker.IncrementDisplayItemIndex(after_forced1);
+ chunker.IncrementDisplayItemIndex(after_forced2);
+
+ Vector<PaintChunk> chunks = chunker.ReleasePaintChunks();
+ EXPECT_THAT(chunks,
+ ElementsAre(PaintChunk(0, 2, id0, DefaultPaintChunkProperties()),
+ PaintChunk(2, 3, forced.GetId(),
+ DefaultPaintChunkProperties()),
+ PaintChunk(3, 5, after_forced1.GetId(),
+ DefaultPaintChunkProperties())));
+}
+
+TEST_F(PaintChunkerTest, ChunkIdsSkippingCache) {
+ PaintChunker chunker;
+
+ PaintChunk::Id id1(client_, DisplayItemType(1));
+ chunker.UpdateCurrentPaintChunkProperties(id1, DefaultPaintChunkProperties());
+ chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
+ chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
+
+ PaintChunkProperties simple_transform = DefaultPaintChunkProperties();
+ simple_transform.property_tree_state.SetTransform(
+ TransformPaintPropertyNode::Create(nullptr,
+ TransformationMatrix(0, 1, 2, 3, 4, 5),
+ FloatPoint3D(9, 8, 7))
+ .get());
+ PaintChunk::Id id2(client_, DisplayItemType(2));
+ chunker.UpdateCurrentPaintChunkProperties(id2, simple_transform);
+
+ TestChunkerDisplayItem uncacheable_item(client_);
+ uncacheable_item.SetSkippedCache();
+ chunker.IncrementDisplayItemIndex(uncacheable_item);
+ chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
+
+ TestDisplayItemRequiringSeparateChunk uncacheable_separate_chunk_item(
+ client_);
+ uncacheable_separate_chunk_item.SetSkippedCache();
+ chunker.IncrementDisplayItemIndex(uncacheable_separate_chunk_item);
+
+ TestChunkerDisplayItem after_separate_chunk(client_, DisplayItemType(3));
+ chunker.IncrementDisplayItemIndex(after_separate_chunk);
+
+ chunker.UpdateCurrentPaintChunkProperties(WTF::nullopt,
+ DefaultPaintChunkProperties());
+ TestChunkerDisplayItem after_restore(client_, DisplayItemType(4));
+ chunker.IncrementDisplayItemIndex(after_restore);
+
+ Vector<PaintChunk> chunks = chunker.ReleasePaintChunks();
+ EXPECT_THAT(
+ chunks,
+ ElementsAre(
+ PaintChunk(0, 2, id1, DefaultPaintChunkProperties()),
+ PaintChunk(2, 4, id2, simple_transform, PaintChunk::kUncacheable),
+ PaintChunk(4, 5, uncacheable_separate_chunk_item.GetId(),
+ simple_transform, PaintChunk::kUncacheable),
+ PaintChunk(5, 6, after_separate_chunk.GetId(), simple_transform),
+ PaintChunk(6, 7, after_restore.GetId(),
+ DefaultPaintChunkProperties())));
+}
+
+} // namespace
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc
new file mode 100644
index 00000000000..8b5679c085d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc
@@ -0,0 +1,1186 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
+
+#include <memory>
+#include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
+#include "third_party/blink/renderer/platform/graphics/logging_canvas.h"
+#include "third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/wtf/auto_reset.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+
+namespace blink {
+
+void PaintController::SetTracksRasterInvalidations(bool value) {
+ if (value) {
+ raster_invalidation_tracking_info_ =
+ std::make_unique<RasterInvalidationTrackingInfo>();
+
+ // This is called just after a full document cycle update, so all clients in
+ // current_paint_artifact_ should be still alive.
+ DCHECK(new_display_item_list_.IsEmpty());
+ for (const auto& item : current_paint_artifact_.GetDisplayItemList()) {
+ raster_invalidation_tracking_info_->old_client_debug_names.Set(
+ &item.Client(), item.Client().DebugName());
+ }
+ } else if (!RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) {
+ raster_invalidation_tracking_info_ = nullptr;
+ }
+
+ for (auto& chunk : current_paint_artifact_.PaintChunks())
+ chunk.raster_invalidation_tracking.clear();
+}
+
+void PaintController::EnsureRasterInvalidationTracking() {
+ if (!raster_invalidation_tracking_info_) {
+ raster_invalidation_tracking_info_ =
+ std::make_unique<RasterInvalidationTrackingInfo>();
+ }
+}
+
+const PaintArtifact& PaintController::GetPaintArtifact() const {
+ DCHECK(new_display_item_list_.IsEmpty());
+ DCHECK(new_paint_chunks_.IsInInitialState());
+ return current_paint_artifact_;
+}
+
+bool PaintController::UseCachedDrawingIfPossible(
+ const DisplayItemClient& client,
+ DisplayItem::Type type) {
+ DCHECK(DisplayItem::IsDrawingType(type));
+
+ if (DisplayItemConstructionIsDisabled())
+ return false;
+
+ if (!ClientCacheIsValid(client))
+ return false;
+
+ if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled() &&
+ IsCheckingUnderInvalidation()) {
+ // We are checking under-invalidation of a subsequence enclosing this
+ // display item. Let the client continue to actually paint the display item.
+ return false;
+ }
+
+ size_t cached_item =
+ FindCachedItem(DisplayItem::Id(client, type, current_fragment_));
+ if (cached_item == kNotFound) {
+ // See FindOutOfOrderCachedItemForward() for explanation of the situation.
+ return false;
+ }
+
+ ++num_cached_new_items_;
+ EnsureNewDisplayItemListInitialCapacity();
+ // Visual rect can change without needing invalidation of the client, e.g.
+ // when ancestor clip changes. Update the visual rect to the current value.
+ current_paint_artifact_.GetDisplayItemList()[cached_item].UpdateVisualRect();
+ if (!RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled())
+ ProcessNewItem(MoveItemFromCurrentListToNewList(cached_item));
+
+ next_item_to_match_ = cached_item + 1;
+ // Items before |next_item_to_match_| have been copied so we don't need to
+ // index them.
+ if (next_item_to_match_ > next_item_to_index_)
+ next_item_to_index_ = next_item_to_match_;
+
+ if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) {
+ if (!IsCheckingUnderInvalidation()) {
+ under_invalidation_checking_begin_ = cached_item;
+ under_invalidation_checking_end_ = cached_item + 1;
+ under_invalidation_message_prefix_ = "";
+ }
+ // Return false to let the painter actually paint. We will check if the new
+ // painting is the same as the cached one.
+ return false;
+ }
+
+ return true;
+}
+
+bool PaintController::UseCachedSubsequenceIfPossible(
+ const DisplayItemClient& client) {
+ if (DisplayItemConstructionIsDisabled() || SubsequenceCachingIsDisabled())
+ return false;
+
+ if (!ClientCacheIsValid(client))
+ return false;
+
+ if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled() &&
+ IsCheckingUnderInvalidation()) {
+ // We are checking under-invalidation of an ancestor subsequence enclosing
+ // this one. The ancestor subsequence is supposed to have already "copied",
+ // so we should let the client continue to actually paint the descendant
+ // subsequences without "copying".
+ return false;
+ }
+
+ SubsequenceMarkers* markers = GetSubsequenceMarkers(client);
+ if (!markers) {
+ return false;
+ }
+
+ EnsureNewDisplayItemListInitialCapacity();
+
+ if (next_item_to_match_ == markers->start) {
+ // We are matching new and cached display items sequentially. Skip the
+ // subsequence for later sequential matching of individual display items.
+ next_item_to_match_ = markers->end;
+ // Items before |next_item_to_match_| have been copied so we don't need to
+ // index them.
+ if (next_item_to_match_ > next_item_to_index_)
+ next_item_to_index_ = next_item_to_match_;
+ }
+
+ num_cached_new_items_ += markers->end - markers->start;
+
+ if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) {
+ DCHECK(!IsCheckingUnderInvalidation());
+ under_invalidation_checking_begin_ = markers->start;
+ under_invalidation_checking_end_ = markers->end;
+ under_invalidation_message_prefix_ =
+ "(In cached subsequence for " + client.DebugName() + ")";
+ // Return false to let the painter actually paint. We will check if the new
+ // painting is the same as the cached one.
+ return false;
+ }
+
+ size_t start = BeginSubsequence();
+ CopyCachedSubsequence(markers->start, markers->end);
+ EndSubsequence(client, start);
+ return true;
+}
+
+PaintController::SubsequenceMarkers* PaintController::GetSubsequenceMarkers(
+ const DisplayItemClient& client) {
+ auto result = current_cached_subsequences_.find(&client);
+ if (result == current_cached_subsequences_.end())
+ return nullptr;
+ return &result->value;
+}
+
+size_t PaintController::BeginSubsequence() {
+ // Force new paint chunk which is required for subsequence caching.
+ new_paint_chunks_.ForceNewChunk();
+ return new_display_item_list_.size();
+}
+
+void PaintController::EndSubsequence(const DisplayItemClient& client,
+ size_t start) {
+ size_t end = new_display_item_list_.size();
+
+ if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled() &&
+ IsCheckingUnderInvalidation()) {
+ SubsequenceMarkers* markers = GetSubsequenceMarkers(client);
+ if (!markers && start != end) {
+ ShowSequenceUnderInvalidationError(
+ "under-invalidation : unexpected subsequence", client, start, end);
+ CHECK(false);
+ }
+ if (markers && markers->end - markers->start != end - start) {
+ ShowSequenceUnderInvalidationError(
+ "under-invalidation: new subsequence wrong length", client, start,
+ end);
+ CHECK(false);
+ }
+ }
+
+ if (start == end) {
+ // Omit the empty subsequence. The forcing-new-chunk flag set by
+ // BeginSubsequence() still applies, but this not a big deal because empty
+ // subsequences are not common. Also we should not clear the flag because
+ // there might be unhandled flag that was set before this empty subsequence.
+ return;
+ }
+
+ // Force new paint chunk which is required for subsequence caching.
+ new_paint_chunks_.ForceNewChunk();
+
+ DCHECK(!new_cached_subsequences_.Contains(&client))
+ << "Multiple subsequences for client: " << client.DebugName();
+
+ new_cached_subsequences_.insert(&client, SubsequenceMarkers(start, end));
+ last_cached_subsequence_end_ = end;
+}
+
+bool PaintController::LastDisplayItemIsNoopBegin() const {
+ DCHECK(!RuntimeEnabledFeatures::SlimmingPaintV175Enabled());
+
+ if (new_display_item_list_.IsEmpty())
+ return false;
+
+ const auto& last_display_item = new_display_item_list_.Last();
+ return last_display_item.IsBegin() && !last_display_item.DrawsContent();
+}
+
+bool PaintController::LastDisplayItemIsSubsequenceEnd() const {
+ return !new_cached_subsequences_.IsEmpty() &&
+ last_cached_subsequence_end_ == new_display_item_list_.size();
+}
+
+void PaintController::RemoveLastDisplayItem() {
+ DCHECK(!RuntimeEnabledFeatures::SlimmingPaintV175Enabled());
+
+ if (new_display_item_list_.IsEmpty())
+ return;
+
+#if DCHECK_IS_ON()
+ // Also remove the index pointing to the removed display item.
+ IndicesByClientMap::iterator it = new_display_item_indices_by_client_.find(
+ &new_display_item_list_.Last().Client());
+ if (it != new_display_item_indices_by_client_.end()) {
+ Vector<size_t>& indices = it->value;
+ if (!indices.IsEmpty() &&
+ indices.back() == (new_display_item_list_.size() - 1))
+ indices.pop_back();
+ }
+#endif
+
+ if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled() &&
+ IsCheckingUnderInvalidation()) {
+ if (skipped_probable_under_invalidation_count_) {
+ --skipped_probable_under_invalidation_count_;
+ } else {
+ DCHECK(under_invalidation_checking_begin_);
+ --under_invalidation_checking_begin_;
+ // The old display item is a tombstone because it was matched by the begin
+ // display item being removed. Restore the tombstone so that we can match
+ // the next new display item against it.
+ current_paint_artifact_.GetDisplayItemList().RestoreTombstone(
+ under_invalidation_checking_begin_, new_display_item_list_.Last());
+ }
+ }
+ new_display_item_list_.RemoveLast();
+}
+
+const DisplayItem* PaintController::LastDisplayItem(unsigned offset) {
+ if (offset < new_display_item_list_.size())
+ return &new_display_item_list_[new_display_item_list_.size() - offset - 1];
+ return nullptr;
+}
+
+void PaintController::ProcessNewItem(DisplayItem& display_item) {
+ DCHECK(!construction_disabled_);
+
+ if (IsSkippingCache())
+ display_item.SetSkippedCache();
+
+ if (raster_invalidation_tracking_info_) {
+ raster_invalidation_tracking_info_->new_client_debug_names.insert(
+ &display_item.Client(), display_item.Client().DebugName());
+ }
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ size_t last_chunk_index = new_paint_chunks_.LastChunkIndex();
+ bool chunk_added =
+ new_paint_chunks_.IncrementDisplayItemIndex(display_item);
+ auto& last_chunk = new_paint_chunks_.LastChunk();
+
+#if DCHECK_IS_ON()
+ if (chunk_added && last_chunk.is_cacheable) {
+ AddToIndicesByClientMap(last_chunk.id.client,
+ new_paint_chunks_.LastChunkIndex(),
+ new_paint_chunk_indices_by_client_);
+ }
+#endif
+
+ if (chunk_added && last_chunk_index != kNotFound) {
+ DCHECK(last_chunk_index != new_paint_chunks_.LastChunkIndex());
+ GenerateRasterInvalidations(
+ new_paint_chunks_.PaintChunkAt(last_chunk_index));
+ }
+
+ last_chunk.outset_for_raster_effects =
+ std::max(last_chunk.outset_for_raster_effects,
+ display_item.OutsetForRasterEffects());
+ }
+
+#if DCHECK_IS_ON()
+ // Verify noop begin/end pairs have been removed.
+ if (new_display_item_list_.size() >= 2 && display_item.IsEnd()) {
+ const auto& begin_display_item =
+ new_display_item_list_[new_display_item_list_.size() - 2];
+ if (begin_display_item.IsBegin() && !begin_display_item.DrawsContent())
+ DCHECK(!display_item.IsEndAndPairedWith(begin_display_item.GetType()));
+ }
+
+ if (display_item.IsCacheable()) {
+ size_t index = FindMatchingItemFromIndex(
+ display_item.GetId(), new_display_item_indices_by_client_,
+ new_display_item_list_);
+ if (index != kNotFound) {
+ ShowDebugData();
+ NOTREACHED()
+ << "DisplayItem " << display_item.AsDebugString().Utf8().data()
+ << " has duplicated id with previous "
+ << new_display_item_list_[index].AsDebugString().Utf8().data()
+ << " (index=" << index << ")";
+ }
+ AddToIndicesByClientMap(display_item.Client(),
+ new_display_item_list_.size() - 1,
+ new_display_item_indices_by_client_);
+ }
+#endif // DCHECK_IS_ON()
+
+ if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled())
+ CheckUnderInvalidation();
+
+ if (!frame_first_paints_.back().first_painted && display_item.IsDrawing() &&
+ // Here we ignore all document-background paintings because we don't
+ // know if the background is default. ViewPainter should have called
+ // setFirstPainted() if this display item is for non-default
+ // background.
+ display_item.GetType() != DisplayItem::kDocumentBackground &&
+ display_item.DrawsContent()) {
+ SetFirstPainted();
+ }
+}
+
+DisplayItem& PaintController::MoveItemFromCurrentListToNewList(size_t index) {
+ items_moved_into_new_list_.resize(
+ current_paint_artifact_.GetDisplayItemList().size());
+ items_moved_into_new_list_[index] = new_display_item_list_.size();
+ return new_display_item_list_.AppendByMoving(
+ current_paint_artifact_.GetDisplayItemList()[index]);
+}
+
+void PaintController::InvalidateAll() {
+ DCHECK(!RuntimeEnabledFeatures::SlimmingPaintV2Enabled());
+ InvalidateAllInternal();
+}
+
+void PaintController::InvalidateAllInternal() {
+ // TODO(wangxianzhu): Rename this to InvalidateAllForTesting() for SPv2.
+ // Can only be called during layout/paintInvalidation, not during painting.
+ DCHECK(new_display_item_list_.IsEmpty());
+ current_paint_artifact_.Reset();
+ current_cache_generation_.Invalidate();
+}
+
+bool PaintController::CacheIsAllInvalid() const {
+ DCHECK(!RuntimeEnabledFeatures::SlimmingPaintV2Enabled());
+ return current_paint_artifact_.IsEmpty() &&
+ current_cache_generation_.GetPaintInvalidationReason() !=
+ PaintInvalidationReason::kNone;
+}
+
+bool PaintController::ClientCacheIsValid(
+ const DisplayItemClient& client) const {
+#if DCHECK_IS_ON()
+ DCHECK(client.IsAlive());
+#endif
+ if (IsSkippingCache())
+ return false;
+ return client.DisplayItemsAreCached(current_cache_generation_);
+}
+
+size_t PaintController::FindMatchingItemFromIndex(
+ const DisplayItem::Id& id,
+ const IndicesByClientMap& display_item_indices_by_client,
+ const DisplayItemList& list) {
+ IndicesByClientMap::const_iterator it =
+ display_item_indices_by_client.find(&id.client);
+ if (it == display_item_indices_by_client.end())
+ return kNotFound;
+
+ const Vector<size_t>& indices = it->value;
+ for (size_t index : indices) {
+ const DisplayItem& existing_item = list[index];
+ if (existing_item.IsTombstone())
+ continue;
+ DCHECK(existing_item.Client() == id.client);
+ if (id == existing_item.GetId())
+ return index;
+ }
+
+ return kNotFound;
+}
+
+void PaintController::AddToIndicesByClientMap(const DisplayItemClient& client,
+ size_t index,
+ IndicesByClientMap& map) {
+ auto it = map.find(&client);
+ auto& indices =
+ it == map.end()
+ ? map.insert(&client, Vector<size_t>()).stored_value->value
+ : it->value;
+ indices.push_back(index);
+}
+
+size_t PaintController::FindCachedItem(const DisplayItem::Id& id) {
+ DCHECK(ClientCacheIsValid(id.client));
+
+ // Try to find the item sequentially first. This is fast if the current list
+ // and the new list are in the same order around the new item. If found, we
+ // don't need to update and lookup the index.
+ for (size_t i = next_item_to_match_;
+ i < current_paint_artifact_.GetDisplayItemList().size(); ++i) {
+ // We encounter an item that has already been copied which indicates we
+ // can't do sequential matching.
+ const DisplayItem& item = current_paint_artifact_.GetDisplayItemList()[i];
+ if (item.IsTombstone())
+ break;
+ if (id == item.GetId()) {
+#if DCHECK_IS_ON()
+ ++num_sequential_matches_;
+#endif
+ return i;
+ }
+ // We encounter a different cacheable item which also indicates we can't do
+ // sequential matching.
+ if (item.IsCacheable())
+ break;
+ }
+
+ size_t found_index =
+ FindMatchingItemFromIndex(id, out_of_order_item_indices_,
+ current_paint_artifact_.GetDisplayItemList());
+ if (found_index != kNotFound) {
+#if DCHECK_IS_ON()
+ ++num_out_of_order_matches_;
+#endif
+ return found_index;
+ }
+
+ return FindOutOfOrderCachedItemForward(id);
+}
+
+// Find forward for the item and index all skipped indexable items.
+size_t PaintController::FindOutOfOrderCachedItemForward(
+ const DisplayItem::Id& id) {
+ for (size_t i = next_item_to_index_;
+ i < current_paint_artifact_.GetDisplayItemList().size(); ++i) {
+ const DisplayItem& item = current_paint_artifact_.GetDisplayItemList()[i];
+ if (item.IsTombstone())
+ continue;
+ if (id == item.GetId()) {
+#if DCHECK_IS_ON()
+ ++num_sequential_matches_;
+#endif
+ return i;
+ }
+ if (item.IsCacheable()) {
+#if DCHECK_IS_ON()
+ ++num_indexed_items_;
+#endif
+ AddToIndicesByClientMap(item.Client(), i, out_of_order_item_indices_);
+ }
+ }
+
+ // The display item newly appears while the client is not invalidated. The
+ // situation alone (without other kinds of under-invalidations) won't corrupt
+ // rendering, but causes AddItemToIndexIfNeeded() for all remaining display
+ // item, which is not the best for performance. In this case, the caller
+ // should fall back to repaint the display item.
+ if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) {
+#if DCHECK_IS_ON()
+ ShowDebugData();
+#endif
+ // Ensure our paint invalidation tests don't trigger the less performant
+ // situation which should be rare.
+ LOG(WARNING) << "Can't find cached display item: " << id.client.DebugName()
+ << " " << id.ToString();
+ }
+ return kNotFound;
+}
+
+// Copies a cached subsequence from current list to the new list.
+// When paintUnderInvaldiationCheckingEnabled() we'll not actually
+// copy the subsequence, but mark the begin and end of the subsequence for
+// under-invalidation checking.
+void PaintController::CopyCachedSubsequence(size_t begin_index,
+ size_t end_index) {
+ DCHECK(!RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled());
+
+ AutoReset<size_t> subsequence_begin_index(
+ &current_cached_subsequence_begin_index_in_new_list_,
+ new_display_item_list_.size());
+ DisplayItem* cached_item =
+ &current_paint_artifact_.GetDisplayItemList()[begin_index];
+
+ Vector<PaintChunk>::const_iterator cached_chunk;
+ PaintChunkProperties properties_before_subsequence;
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ cached_chunk =
+ current_paint_artifact_.FindChunkByDisplayItemIndex(begin_index);
+ DCHECK(cached_chunk != current_paint_artifact_.PaintChunks().end());
+
+ properties_before_subsequence =
+ new_paint_chunks_.CurrentPaintChunkProperties();
+ new_paint_chunks_.ForceNewChunk();
+ UpdateCurrentPaintChunkPropertiesUsingIdWithFragment(
+ cached_chunk->id, cached_chunk->properties);
+ } else {
+ // Avoid uninitialized variable error on Windows.
+ cached_chunk = current_paint_artifact_.PaintChunks().begin();
+ }
+
+ for (size_t current_index = begin_index; current_index < end_index;
+ ++current_index) {
+ cached_item = &current_paint_artifact_.GetDisplayItemList()[current_index];
+ SECURITY_CHECK(!cached_item->IsTombstone());
+#if DCHECK_IS_ON()
+ DCHECK(cached_item->Client().IsAlive());
+#endif
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled() &&
+ current_index == cached_chunk->end_index) {
+ ++cached_chunk;
+ DCHECK(cached_chunk != current_paint_artifact_.PaintChunks().end());
+ new_paint_chunks_.ForceNewChunk();
+ UpdateCurrentPaintChunkPropertiesUsingIdWithFragment(
+ cached_chunk->id, cached_chunk->properties);
+ }
+
+#if DCHECK_IS_ON()
+ // Visual rect change should not happen in a cached subsequence.
+ // However, because of different method of pixel snapping in different
+ // paths, there are false positives. Just log an error.
+ if (cached_item->VisualRect() !=
+ FloatRect(cached_item->Client().VisualRect())) {
+ LOG(ERROR) << "Visual rect changed in a cached subsequence: "
+ << cached_item->Client().DebugName()
+ << " old=" << cached_item->VisualRect().ToString()
+ << " new=" << cached_item->Client().VisualRect().ToString();
+ }
+#endif
+
+ ProcessNewItem(MoveItemFromCurrentListToNewList(current_index));
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ DCHECK((!new_paint_chunks_.LastChunk().is_cacheable &&
+ !cached_chunk->is_cacheable) ||
+ new_paint_chunks_.LastChunk().Matches(*cached_chunk));
+ }
+ }
+
+ if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) {
+ under_invalidation_checking_end_ = end_index;
+ DCHECK(IsCheckingUnderInvalidation());
+ } else if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ // Restore properties and force new chunk for any trailing display items
+ // after the cached subsequence without new properties.
+ new_paint_chunks_.ForceNewChunk();
+ UpdateCurrentPaintChunkProperties(WTF::nullopt,
+ properties_before_subsequence);
+ }
+}
+
+void PaintController::ResetCurrentListIndices() {
+ next_item_to_match_ = 0;
+ next_item_to_index_ = 0;
+ next_chunk_to_match_ = 0;
+ under_invalidation_checking_begin_ = 0;
+ under_invalidation_checking_end_ = 0;
+ skipped_probable_under_invalidation_count_ = 0;
+}
+
+DISABLE_CFI_PERF
+void PaintController::CommitNewDisplayItems() {
+ TRACE_EVENT2("blink,benchmark", "PaintController::commitNewDisplayItems",
+ "current_display_list_size",
+ (int)current_paint_artifact_.GetDisplayItemList().size(),
+ "num_non_cached_new_items",
+ (int)new_display_item_list_.size() - num_cached_new_items_);
+
+ num_cached_new_items_ = 0;
+#if DCHECK_IS_ON()
+ new_display_item_indices_by_client_.clear();
+ new_paint_chunk_indices_by_client_.clear();
+#endif
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled() &&
+ !new_display_item_list_.IsEmpty())
+ GenerateRasterInvalidations(new_paint_chunks_.LastChunk());
+
+ auto old_cache_generation = current_cache_generation_;
+ current_cache_generation_ =
+ DisplayItemClient::CacheGenerationOrInvalidationReason::Next();
+
+ new_cached_subsequences_.swap(current_cached_subsequences_);
+ new_cached_subsequences_.clear();
+ last_cached_subsequence_end_ = 0;
+ for (auto& item : current_cached_subsequences_)
+ item.key->SetDisplayItemsCached(current_cache_generation_);
+
+ Vector<const DisplayItemClient*> skipped_cache_clients;
+ for (const auto& item : new_display_item_list_) {
+ const auto& client = item.Client();
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled())
+ client.ClearPartialInvalidationRect();
+
+ if (item.IsCacheable()) {
+ client.SetDisplayItemsCached(current_cache_generation_);
+ } else {
+ if (client.IsJustCreated())
+ client.ClearIsJustCreated();
+ if (item.SkippedCache())
+ skipped_cache_clients.push_back(&item.Client());
+ }
+ }
+
+ for (auto* client : skipped_cache_clients) {
+ // Set client uncached only if it is cached by this PaintController. The
+ // client may be still validly cached in another PaintController which
+ // should not be affected by skipping cache in this PaintController.
+ if (client->DisplayItemsAreCached(old_cache_generation) ||
+ // The client was set cached because it just painted some cacheable
+ // items in this PaintController. Need to set it uncached.
+ client->DisplayItemsAreCached(current_cache_generation_))
+ client->SetDisplayItemsUncached();
+ }
+
+ // The new list will not be appended to again so we can release unused memory.
+ new_display_item_list_.ShrinkToFit();
+
+ current_paint_artifact_ =
+ PaintArtifact(std::move(new_display_item_list_),
+ new_paint_chunks_.ReleasePaintChunks());
+
+ ResetCurrentListIndices();
+ out_of_order_item_indices_.clear();
+ out_of_order_chunk_indices_.clear();
+ items_moved_into_new_list_.clear();
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ for (const auto& chunk : current_paint_artifact_.PaintChunks()) {
+ if (chunk.id.client.IsJustCreated())
+ chunk.id.client.ClearIsJustCreated();
+ }
+ }
+
+ // We'll allocate the initial buffer when we start the next paint.
+ new_display_item_list_ = DisplayItemList(0);
+
+#if DCHECK_IS_ON()
+ num_sequential_matches_ = 0;
+ num_out_of_order_matches_ = 0;
+ num_indexed_items_ = 0;
+#endif
+
+ if (raster_invalidation_tracking_info_) {
+ raster_invalidation_tracking_info_->old_client_debug_names.clear();
+ std::swap(raster_invalidation_tracking_info_->old_client_debug_names,
+ raster_invalidation_tracking_info_->new_client_debug_names);
+ }
+
+#if DCHECK_IS_ON()
+ if (VLOG_IS_ON(2)) {
+ LOG(ERROR) << "PaintController::CommitNewDisplayItems() done";
+ if (VLOG_IS_ON(3))
+ ShowDebugDataWithRecords();
+ else
+ ShowDebugData();
+ }
+#endif
+}
+
+size_t PaintController::ApproximateUnsharedMemoryUsage() const {
+ size_t memory_usage = sizeof(*this);
+
+ // Memory outside this class due to current_paint_artifact_.
+ memory_usage += current_paint_artifact_.ApproximateUnsharedMemoryUsage() -
+ sizeof(current_paint_artifact_);
+
+ // TODO(jbroman): If display items begin to have significant external memory
+ // usage that's not shared with the embedder, we should account for it here.
+ //
+ // External objects, shared with the embedder, such as PaintRecord, should be
+ // excluded to avoid double counting. It is the embedder's responsibility to
+ // count such objects.
+ //
+ // At time of writing, the only known case of unshared external memory was
+ // the rounded clips vector in ClipDisplayItem, which is not expected to
+ // contribute significantly to memory usage.
+
+ // Memory outside this class due to new_display_item_list_.
+ DCHECK(new_display_item_list_.IsEmpty());
+ memory_usage += new_display_item_list_.MemoryUsageInBytes();
+
+ // Memory outside this class due to current_cached_subsequences_ and
+ // new_cached_subsequences_.
+ memory_usage += current_cached_subsequences_.Capacity() *
+ sizeof(*current_cached_subsequences_.begin());
+ DCHECK(new_cached_subsequences_.IsEmpty());
+ memory_usage += new_cached_subsequences_.Capacity() *
+ sizeof(*new_cached_subsequences_.begin());
+
+ return memory_usage;
+}
+
+void PaintController::AppendDebugDrawingAfterCommit(
+ const DisplayItemClient& display_item_client,
+ sk_sp<const PaintRecord> record,
+ const PropertyTreeState* property_tree_state) {
+ DCHECK(!RuntimeEnabledFeatures::SlimmingPaintV2Enabled());
+ DCHECK(new_display_item_list_.IsEmpty());
+ auto& display_item_list = current_paint_artifact_.GetDisplayItemList();
+ auto& display_item =
+ display_item_list.AllocateAndConstruct<DrawingDisplayItem>(
+ display_item_client, DisplayItem::kDebugDrawing, std::move(record));
+ display_item.SetSkippedCache();
+
+ if (property_tree_state) {
+ DCHECK(RuntimeEnabledFeatures::SlimmingPaintV175Enabled());
+ // Create a PaintChunk for the debug drawing.
+ PaintChunk chunk(display_item_list.size() - 1, display_item_list.size(),
+ display_item.GetId(),
+ PaintChunkProperties(*property_tree_state));
+ current_paint_artifact_.PaintChunks().push_back(chunk);
+ }
+}
+
+void PaintController::GenerateRasterInvalidations(PaintChunk& new_chunk) {
+ if (RuntimeEnabledFeatures::DisableRasterInvalidationEnabled())
+ return;
+
+ DCHECK(RuntimeEnabledFeatures::SlimmingPaintV175Enabled());
+ if (new_chunk.begin_index >=
+ current_cached_subsequence_begin_index_in_new_list_)
+ return;
+
+ // Uncacheable chunks will be invalidated in ContentLayerClientImpl.
+ if (!new_chunk.is_cacheable)
+ return;
+
+ // Try to match old chunk sequentially first.
+ const auto& old_chunks = current_paint_artifact_.PaintChunks();
+ while (next_chunk_to_match_ < old_chunks.size()) {
+ const PaintChunk& old_chunk = old_chunks[next_chunk_to_match_];
+ if (new_chunk.Matches(old_chunk)) {
+ GenerateRasterInvalidationsComparingChunks(new_chunk, old_chunk);
+ ++next_chunk_to_match_;
+ return;
+ }
+
+ // Add skipped old chunks into the index.
+ if (old_chunk.is_cacheable) {
+ auto it = out_of_order_chunk_indices_.find(&old_chunk.id.client);
+ Vector<size_t>& indices =
+ it == out_of_order_chunk_indices_.end()
+ ? out_of_order_chunk_indices_
+ .insert(&old_chunk.id.client, Vector<size_t>())
+ .stored_value->value
+ : it->value;
+ indices.push_back(next_chunk_to_match_);
+ }
+ ++next_chunk_to_match_;
+ }
+
+ // Sequential matching reaches the end. Find from the out-of-order index.
+ auto it = out_of_order_chunk_indices_.find(&new_chunk.id.client);
+ if (it != out_of_order_chunk_indices_.end()) {
+ for (size_t i : it->value) {
+ if (new_chunk.Matches(old_chunks[i])) {
+ GenerateRasterInvalidationsComparingChunks(new_chunk, old_chunks[i]);
+ return;
+ }
+ }
+ }
+}
+
+void PaintController::AddRasterInvalidation(const DisplayItemClient& client,
+ PaintChunk& chunk,
+ const FloatRect& rect,
+ PaintInvalidationReason reason) {
+ chunk.raster_invalidation_rects.push_back(rect);
+ if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled())
+ EnsureRasterInvalidationTracking();
+ if (raster_invalidation_tracking_info_)
+ TrackRasterInvalidation(client, chunk, reason);
+}
+
+void PaintController::TrackRasterInvalidation(const DisplayItemClient& client,
+ PaintChunk& chunk,
+ PaintInvalidationReason reason) {
+ DCHECK(raster_invalidation_tracking_info_);
+
+ RasterInvalidationInfo info;
+ info.client = &client;
+ if (reason == PaintInvalidationReason::kNone) {
+ // The client was validated by another PaintController, but not valid in
+ // this PaintController.
+ DCHECK(!ClientCacheIsValid(client));
+ info.reason = PaintInvalidationReason::kFull;
+ } else {
+ info.reason = reason;
+ }
+
+ if (reason == PaintInvalidationReason::kDisappeared) {
+ info.client_debug_name =
+ raster_invalidation_tracking_info_->old_client_debug_names.at(&client);
+ } else {
+ info.client_debug_name = client.DebugName();
+ }
+
+ chunk.raster_invalidation_tracking.push_back(info);
+}
+
+void PaintController::GenerateRasterInvalidationsComparingChunks(
+ PaintChunk& new_chunk,
+ const PaintChunk& old_chunk) {
+ DCHECK(RuntimeEnabledFeatures::SlimmingPaintV175Enabled());
+
+ // TODO(wangxianzhu): Optimize paint offset change.
+
+ struct OldAndNewDisplayItems {
+ const DisplayItem* old_item = nullptr;
+ const DisplayItem* new_item = nullptr;
+ };
+ HashMap<const DisplayItemClient*, OldAndNewDisplayItems>
+ clients_to_invalidate;
+
+ size_t highest_moved_to_index = 0;
+ // Find clients to invalidate the old visual rects from the old chunk.
+ for (size_t old_index = old_chunk.begin_index;
+ old_index < old_chunk.end_index; ++old_index) {
+ const DisplayItem& old_item =
+ current_paint_artifact_.GetDisplayItemList()[old_index];
+ const DisplayItemClient* client_to_invalidate_old_visual_rect = nullptr;
+
+ if (old_item.IsTombstone()) {
+ // old_item has been moved into new_display_item_list_ as a cached item.
+ size_t moved_to_index = items_moved_into_new_list_[old_index];
+ if (new_display_item_list_[moved_to_index].DrawsContent()) {
+ if (moved_to_index < new_chunk.begin_index ||
+ moved_to_index >= new_chunk.end_index) {
+ // The item has been moved into another chunk, so need to invalidate
+ // it in the chunk into which the item was moved.
+ const auto& new_item = new_display_item_list_[moved_to_index];
+ PaintChunk& moved_to_chunk =
+ new_paint_chunks_.FindChunkByDisplayItemIndex(moved_to_index);
+ AddRasterInvalidation(new_item.Client(), moved_to_chunk,
+ new_item.VisualRect(),
+ PaintInvalidationReason::kAppeared);
+ // And invalidate the old visual rect in this chunk.
+ client_to_invalidate_old_visual_rect = &new_item.Client();
+ } else if (moved_to_index < highest_moved_to_index) {
+ // The item has been moved behind other cached items, so need to
+ // invalidate the area that is probably exposed by the item moved
+ // earlier.
+ client_to_invalidate_old_visual_rect =
+ &new_display_item_list_[moved_to_index].Client();
+ } else {
+ highest_moved_to_index = moved_to_index;
+ }
+ }
+ } else if (old_item.DrawsContent()) {
+ // old_item has either changed or disappeared.
+ client_to_invalidate_old_visual_rect = &old_item.Client();
+ }
+
+ if (client_to_invalidate_old_visual_rect) {
+ clients_to_invalidate
+ .insert(client_to_invalidate_old_visual_rect, OldAndNewDisplayItems())
+ .stored_value->value.old_item = &old_item;
+ }
+ }
+
+ // Find clients to invalidate the new visual rects from the new chunk.
+ for (size_t new_index = new_chunk.begin_index;
+ new_index < new_chunk.end_index; ++new_index) {
+ const DisplayItem& new_item = new_display_item_list_[new_index];
+ if (new_item.DrawsContent() && !ClientCacheIsValid(new_item.Client())) {
+ clients_to_invalidate.insert(&new_item.Client(), OldAndNewDisplayItems())
+ .stored_value->value.new_item = &new_item;
+ }
+ }
+
+ for (const auto& item : clients_to_invalidate) {
+ GenerateRasterInvalidation(*item.key, new_chunk, item.value.old_item,
+ item.value.new_item);
+ }
+}
+
+void PaintController::GenerateRasterInvalidation(
+ const DisplayItemClient& client,
+ PaintChunk& chunk,
+ const DisplayItem* old_item,
+ const DisplayItem* new_item) {
+ if (!new_item || new_item->VisualRect().IsEmpty()) {
+ if (old_item && !old_item->VisualRect().IsEmpty()) {
+ AddRasterInvalidation(client, chunk, old_item->VisualRect(),
+ PaintInvalidationReason::kDisappeared);
+ }
+ return;
+ }
+
+ DCHECK(&client == &new_item->Client());
+ if (!old_item || old_item->VisualRect().IsEmpty()) {
+ AddRasterInvalidation(client, chunk, new_item->VisualRect(),
+ PaintInvalidationReason::kAppeared);
+ return;
+ }
+
+ if (client.IsJustCreated()) {
+ // The old client has been deleted and the new client happens to be at the
+ // same address. They have no relationship.
+ AddRasterInvalidation(client, chunk, old_item->VisualRect(),
+ PaintInvalidationReason::kDisappeared);
+ AddRasterInvalidation(client, chunk, new_item->VisualRect(),
+ PaintInvalidationReason::kAppeared);
+ return;
+ }
+
+ auto reason = client.GetPaintInvalidationReason();
+ bool partial_raster_invalidation =
+ RuntimeEnabledFeatures::PartialRasterInvalidationEnabled() &&
+ (reason == PaintInvalidationReason::kRectangle ||
+ reason == PaintInvalidationReason::kSelection);
+ if ((!partial_raster_invalidation &&
+ reason != PaintInvalidationReason::kIncremental) ||
+ // Need full invalidation when visual rect location changed.
+ old_item->VisualRect().Location() != new_item->VisualRect().Location()) {
+ GenerateFullRasterInvalidation(chunk, *old_item, *new_item);
+ return;
+ }
+
+ GenerateIncrementalRasterInvalidation(chunk, *old_item, *new_item);
+
+ if (RuntimeEnabledFeatures::PartialRasterInvalidationEnabled()) {
+ auto partial_rect = client.PartialInvalidationRect();
+ if (!partial_rect.IsEmpty())
+ AddRasterInvalidation(client, chunk, FloatRect(partial_rect), reason);
+ }
+}
+
+static FloatRect ComputeRightDelta(const FloatPoint& location,
+ const FloatSize& old_size,
+ const FloatSize& new_size) {
+ float delta = new_size.Width() - old_size.Width();
+ if (delta > 0) {
+ return FloatRect(location.X() + old_size.Width(), location.Y(), delta,
+ new_size.Height());
+ }
+ if (delta < 0) {
+ return FloatRect(location.X() + new_size.Width(), location.Y(), -delta,
+ old_size.Height());
+ }
+ return FloatRect();
+}
+
+static FloatRect ComputeBottomDelta(const FloatPoint& location,
+ const FloatSize& old_size,
+ const FloatSize& new_size) {
+ float delta = new_size.Height() - old_size.Height();
+ if (delta > 0) {
+ return FloatRect(location.X(), location.Y() + old_size.Height(),
+ new_size.Width(), delta);
+ }
+ if (delta < 0) {
+ return FloatRect(location.X(), location.Y() + new_size.Height(),
+ old_size.Width(), -delta);
+ }
+ return FloatRect();
+}
+
+void PaintController::GenerateIncrementalRasterInvalidation(
+ PaintChunk& chunk,
+ const DisplayItem& old_item,
+ const DisplayItem& new_item) {
+ DCHECK(&old_item.Client() == &new_item.Client());
+ FloatRect old_visual_rect(old_item.VisualRect());
+ FloatRect new_visual_rect(new_item.VisualRect());
+ DCHECK(old_visual_rect.Location() == new_visual_rect.Location());
+
+ FloatRect right_delta =
+ ComputeRightDelta(new_visual_rect.Location(), old_visual_rect.Size(),
+ new_visual_rect.Size());
+ if (!right_delta.IsEmpty()) {
+ AddRasterInvalidation(new_item.Client(), chunk, right_delta,
+ PaintInvalidationReason::kIncremental);
+ }
+
+ FloatRect bottom_delta =
+ ComputeBottomDelta(new_visual_rect.Location(), old_visual_rect.Size(),
+ new_visual_rect.Size());
+ if (!bottom_delta.IsEmpty()) {
+ AddRasterInvalidation(new_item.Client(), chunk, bottom_delta,
+ PaintInvalidationReason::kIncremental);
+ }
+}
+
+void PaintController::GenerateFullRasterInvalidation(
+ PaintChunk& chunk,
+ const DisplayItem& old_item,
+ const DisplayItem& new_item) {
+ DCHECK(&old_item.Client() == &new_item.Client());
+ FloatRect old_visual_rect(old_item.VisualRect());
+ FloatRect new_visual_rect(new_item.VisualRect());
+
+ if (!new_visual_rect.Contains(old_visual_rect)) {
+ AddRasterInvalidation(new_item.Client(), chunk, old_visual_rect,
+ new_item.Client().GetPaintInvalidationReason());
+ if (old_visual_rect.Contains(new_visual_rect))
+ return;
+ }
+
+ AddRasterInvalidation(new_item.Client(), chunk, new_visual_rect,
+ new_item.Client().GetPaintInvalidationReason());
+}
+
+void PaintController::ShowUnderInvalidationError(
+ const char* reason,
+ const DisplayItem& new_item,
+ const DisplayItem* old_item) const {
+ LOG(ERROR) << under_invalidation_message_prefix_ << " " << reason;
+#if DCHECK_IS_ON()
+ LOG(ERROR) << "New display item: " << new_item.AsDebugString();
+ LOG(ERROR) << "Old display item: "
+ << (old_item ? old_item->AsDebugString() : "None");
+ LOG(ERROR) << "See http://crbug.com/619103.";
+
+ const PaintRecord* new_record = nullptr;
+ if (new_item.IsDrawing()) {
+ new_record =
+ static_cast<const DrawingDisplayItem&>(new_item).GetPaintRecord().get();
+ }
+ const PaintRecord* old_record = nullptr;
+ if (old_item->IsDrawing()) {
+ old_record = static_cast<const DrawingDisplayItem*>(old_item)
+ ->GetPaintRecord()
+ .get();
+ }
+ LOG(INFO) << "new record:\n"
+ << (new_record ? RecordAsDebugString(*new_record).Utf8().data()
+ : "None");
+ LOG(INFO) << "old record:\n"
+ << (old_record ? RecordAsDebugString(*old_record).Utf8().data()
+ : "None");
+
+ ShowDebugData();
+#else
+ LOG(ERROR) << "Run a build with DCHECK on to get more details.";
+ LOG(ERROR) << "See http://crbug.com/619103.";
+#endif
+}
+
+void PaintController::ShowSequenceUnderInvalidationError(
+ const char* reason,
+ const DisplayItemClient& client,
+ int start,
+ int end) {
+ LOG(ERROR) << under_invalidation_message_prefix_ << " " << reason;
+ LOG(ERROR) << "Subsequence client: " << client.DebugName();
+#if DCHECK_IS_ON()
+ ShowDebugData();
+#else
+ LOG(ERROR) << "Run a build with DCHECK on to get more details.";
+#endif
+ LOG(ERROR) << "See http://crbug.com/619103.";
+}
+
+void PaintController::CheckUnderInvalidation() {
+ DCHECK(RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled());
+
+ if (!IsCheckingUnderInvalidation())
+ return;
+
+ const DisplayItem& new_item = new_display_item_list_.Last();
+ if (new_item.SkippedCache()) {
+ // We allow cache skipping and temporary under-invalidation in cached
+ // subsequences. See the usage of DisplayItemCacheSkipper in BoxPainter.
+ under_invalidation_checking_end_ = 0;
+ // Match the remaining display items in the subsequence normally.
+ next_item_to_match_ = next_item_to_index_ =
+ under_invalidation_checking_begin_;
+ return;
+ }
+
+ size_t old_item_index = under_invalidation_checking_begin_ +
+ skipped_probable_under_invalidation_count_;
+ DisplayItem* old_item =
+ old_item_index < current_paint_artifact_.GetDisplayItemList().size()
+ ? &current_paint_artifact_.GetDisplayItemList()[old_item_index]
+ : nullptr;
+
+ bool old_and_new_equal = (old_item && new_item.Equals(*old_item));
+ if (!old_and_new_equal) {
+ if (new_item.IsBegin()) {
+ // Temporarily skip mismatching begin display item which may be removed
+ // when we remove a no-op pair.
+ ++skipped_probable_under_invalidation_count_;
+ return;
+ }
+ if (new_item.IsDrawing() &&
+ skipped_probable_under_invalidation_count_ == 1) {
+ DCHECK_GE(new_display_item_list_.size(), 2u);
+ if (new_display_item_list_[new_display_item_list_.size() - 2].GetType() ==
+ DisplayItem::kBeginCompositing) {
+ // This might be a drawing item between a pair of begin/end compositing
+ // display items that will be folded into a single drawing display item.
+ ++skipped_probable_under_invalidation_count_;
+ return;
+ }
+ }
+ }
+
+ if (skipped_probable_under_invalidation_count_ || !old_and_new_equal) {
+ // If we ever skipped reporting any under-invalidations, report the earliest
+ // one.
+ ShowUnderInvalidationError(
+ "under-invalidation: display item changed",
+ new_display_item_list_[new_display_item_list_.size() -
+ skipped_probable_under_invalidation_count_ - 1],
+ &current_paint_artifact_
+ .GetDisplayItemList()[under_invalidation_checking_begin_]);
+ CHECK(false);
+ }
+
+ // Discard the forced repainted display item and move the cached item into
+ // new_display_item_list_. This is to align with the
+ // non-under-invalidation-checking path to empty the original cached slot,
+ // leaving only disappeared or invalidated display items in the old list after
+ // painting.
+ new_display_item_list_.RemoveLast();
+ MoveItemFromCurrentListToNewList(old_item_index);
+
+ ++under_invalidation_checking_begin_;
+}
+
+void PaintController::SetFirstPainted() {
+ frame_first_paints_.back().first_painted = true;
+}
+
+void PaintController::SetTextPainted() {
+ frame_first_paints_.back().text_painted = true;
+}
+
+void PaintController::SetImagePainted() {
+ frame_first_paints_.back().image_painted = true;
+}
+
+void PaintController::BeginFrame(const void* frame) {
+ frame_first_paints_.push_back(FrameFirstPaint(frame));
+}
+
+FrameFirstPaint PaintController::EndFrame(const void* frame) {
+ FrameFirstPaint result = frame_first_paints_.back();
+ DCHECK(result.frame == frame);
+ frame_first_paints_.pop_back();
+ return result;
+}
+
+#if DCHECK_IS_ON()
+void PaintController::CheckDuplicatePaintChunkId(const PaintChunk::Id& id) {
+ if (IsSkippingCache())
+ return;
+
+ auto it = new_paint_chunk_indices_by_client_.find(&id.client);
+ if (it != new_paint_chunk_indices_by_client_.end()) {
+ const auto& indices = it->value;
+ for (auto index : indices) {
+ const auto& chunk = new_paint_chunks_.PaintChunkAt(index);
+ if (chunk.id == id) {
+ ShowDebugData();
+ NOTREACHED() << "New paint chunk id " << id.ToString().Utf8().data()
+ << " has duplicated id with previous chuck "
+ << chunk.ToString().Utf8().data();
+ }
+ }
+ }
+}
+#endif
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller.h b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller.h
new file mode 100644
index 00000000000..e2190272cf6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller.h
@@ -0,0 +1,472 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_CONTROLLER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_CONTROLLER_H_
+
+#include <memory>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/geometry/layout_point.h"
+#include "third_party/blink/renderer/platform/graphics/contiguous_container.h"
+#include "third_party/blink/renderer/platform/graphics/paint/display_item.h"
+#include "third_party/blink/renderer/platform/graphics/paint/display_item_list.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_artifact.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_chunk.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_chunker.h"
+#include "third_party/blink/renderer/platform/graphics/paint/raster_invalidation_tracking.h"
+#include "third_party/blink/renderer/platform/graphics/paint/transform_3d_display_item.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/wtf/alignment.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+namespace blink {
+
+static const size_t kInitialDisplayItemListCapacityBytes = 512;
+
+// FrameFirstPaint stores first-paint, text or image painted for the
+// corresponding frame. They are never reset to false. First-paint is defined in
+// https://github.com/WICG/paint-timing. It excludes default background paint.
+struct FrameFirstPaint {
+ FrameFirstPaint(const void* frame)
+ : frame(frame),
+ first_painted(false),
+ text_painted(false),
+ image_painted(false) {}
+
+ const void* frame;
+ bool first_painted : 1;
+ bool text_painted : 1;
+ bool image_painted : 1;
+};
+
+// Responsible for processing display items as they are produced, and producing
+// a final paint artifact when complete. This class includes logic for caching,
+// cache invalidation, and merging.
+class PLATFORM_EXPORT PaintController {
+ WTF_MAKE_NONCOPYABLE(PaintController);
+ USING_FAST_MALLOC(PaintController);
+
+ public:
+ static std::unique_ptr<PaintController> Create() {
+ return base::WrapUnique(new PaintController());
+ }
+
+ ~PaintController() {
+ // New display items should be committed before PaintController is
+ // destructed.
+ DCHECK(new_display_item_list_.IsEmpty());
+ }
+
+ // For SPv1 only.
+ void InvalidateAll();
+ bool CacheIsAllInvalid() const;
+
+ // These methods are called during painting.
+
+ // Provide a new set of paint chunk properties to apply to recorded display
+ // items, for Slimming Paint v175+.
+ void UpdateCurrentPaintChunkProperties(
+ const Optional<PaintChunk::Id>& id,
+ const PaintChunkProperties& properties) {
+ if (id) {
+ PaintChunk::Id id_with_fragment(*id, current_fragment_);
+ UpdateCurrentPaintChunkPropertiesUsingIdWithFragment(id_with_fragment,
+ properties);
+#if DCHECK_IS_ON()
+ CheckDuplicatePaintChunkId(id_with_fragment);
+#endif
+ } else {
+ new_paint_chunks_.UpdateCurrentPaintChunkProperties(WTF::nullopt,
+ properties);
+ }
+ }
+
+ const PaintChunkProperties& CurrentPaintChunkProperties() const {
+ return new_paint_chunks_.CurrentPaintChunkProperties();
+ }
+
+ template <typename DisplayItemClass, typename... Args>
+ void CreateAndAppend(Args&&... args) {
+ static_assert(WTF::IsSubclass<DisplayItemClass, DisplayItem>::value,
+ "Can only createAndAppend subclasses of DisplayItem.");
+ static_assert(
+ sizeof(DisplayItemClass) <= kMaximumDisplayItemSize,
+ "DisplayItem subclass is larger than kMaximumDisplayItemSize.");
+
+ if (DisplayItemConstructionIsDisabled())
+ return;
+
+ EnsureNewDisplayItemListInitialCapacity();
+ DisplayItemClass& display_item =
+ new_display_item_list_.AllocateAndConstruct<DisplayItemClass>(
+ std::forward<Args>(args)...);
+ display_item.SetFragment(current_fragment_);
+ ProcessNewItem(display_item);
+ }
+
+ // Creates and appends an ending display item to pair with a preceding
+ // beginning item iff the display item actually draws content. For no-op
+ // items, rather than creating an ending item, the begin item will
+ // instead be removed, thereby maintaining brevity of the list. If display
+ // item construction is disabled, no list mutations will be performed.
+ template <typename DisplayItemClass, typename... Args>
+ void EndItem(Args&&... args) {
+ DCHECK(!RuntimeEnabledFeatures::SlimmingPaintV175Enabled());
+
+ if (DisplayItemConstructionIsDisabled())
+ return;
+ if (LastDisplayItemIsNoopBegin())
+ RemoveLastDisplayItem();
+ else
+ CreateAndAppend<DisplayItemClass>(std::forward<Args>(args)...);
+ }
+
+ // Tries to find the cached drawing display item corresponding to the given
+ // parameters. If found, appends the cached display item to the new display
+ // list and returns true. Otherwise returns false.
+ bool UseCachedDrawingIfPossible(const DisplayItemClient&, DisplayItem::Type);
+
+ // Tries to find the cached subsequence corresponding to the given parameters.
+ // If found, copies the cache subsequence to the new display list and returns
+ // true. Otherwise returns false.
+ bool UseCachedSubsequenceIfPossible(const DisplayItemClient&);
+
+ size_t BeginSubsequence();
+ // The |start| parameter should be the return value of the corresponding
+ // BeginSubsequence().
+ void EndSubsequence(const DisplayItemClient&, size_t start);
+
+ // True if the last display item is a begin that doesn't draw content.
+ void RemoveLastDisplayItem();
+ const DisplayItem* LastDisplayItem(unsigned offset);
+
+ void BeginSkippingCache() { ++skipping_cache_count_; }
+ void EndSkippingCache() {
+ DCHECK(skipping_cache_count_ > 0);
+ --skipping_cache_count_;
+ }
+ bool IsSkippingCache() const { return skipping_cache_count_; }
+
+ // Must be called when a painting is finished.
+ void CommitNewDisplayItems();
+
+ // Returns the approximate memory usage, excluding memory likely to be
+ // shared with the embedder after copying to WebPaintController.
+ // Should only be called after a full document life cycle update.
+ size_t ApproximateUnsharedMemoryUsage() const;
+
+ // Get the artifact generated after the last commit.
+ const PaintArtifact& GetPaintArtifact() const;
+ const DisplayItemList& GetDisplayItemList() const {
+ return GetPaintArtifact().GetDisplayItemList();
+ }
+ const Vector<PaintChunk>& PaintChunks() const {
+ return GetPaintArtifact().PaintChunks();
+ }
+
+ // For micro benchmarking of record time.
+ bool DisplayItemConstructionIsDisabled() const {
+ return construction_disabled_;
+ }
+ void SetDisplayItemConstructionIsDisabled(const bool disable) {
+ construction_disabled_ = disable;
+ }
+ bool SubsequenceCachingIsDisabled() const {
+ return subsequence_caching_disabled_;
+ }
+ void SetSubsequenceCachingIsDisabled(bool disable) {
+ subsequence_caching_disabled_ = disable;
+ }
+
+ void SetFirstPainted();
+ void SetTextPainted();
+ void SetImagePainted();
+
+ // Returns DisplayItemList added using CreateAndAppend() since beginning or
+ // the last CommitNewDisplayItems(). Use with care.
+ DisplayItemList& NewDisplayItemList() { return new_display_item_list_; }
+
+ void AppendDebugDrawingAfterCommit(const DisplayItemClient&,
+ sk_sp<const PaintRecord>,
+ const PropertyTreeState*);
+
+#if DCHECK_IS_ON()
+ void ShowDebugData() const;
+ void ShowDebugDataWithRecords() const;
+#endif
+
+ void SetTracksRasterInvalidations(bool);
+
+ bool LastDisplayItemIsSubsequenceEnd() const;
+
+ void BeginFrame(const void* frame);
+ FrameFirstPaint EndFrame(const void* frame);
+
+ // The current fragment will be part of the ids of all display items and
+ // paint chunks, to uniquely identify display items in different fragments
+ // for the same client and type.
+ unsigned CurrentFragment() const { return current_fragment_; }
+ void SetCurrentFragment(unsigned fragment) { current_fragment_ = fragment; }
+
+ protected:
+ PaintController()
+ : new_display_item_list_(0),
+ construction_disabled_(false),
+ subsequence_caching_disabled_(false),
+ skipping_cache_count_(0),
+ num_cached_new_items_(0),
+ current_cached_subsequence_begin_index_in_new_list_(kNotFound),
+#if DCHECK_IS_ON()
+ num_sequential_matches_(0),
+ num_out_of_order_matches_(0),
+ num_indexed_items_(0),
+#endif
+ under_invalidation_checking_begin_(0),
+ under_invalidation_checking_end_(0),
+ last_cached_subsequence_end_(0),
+ current_fragment_(0) {
+ ResetCurrentListIndices();
+ // frame_first_paints_ should have one null frame since the beginning, so
+ // that PaintController is robust even if it paints outside of BeginFrame
+ // and EndFrame cycles. It will also enable us to combine the first paint
+ // data in this PaintController into another PaintController on which we
+ // replay the recorded results in the future.
+ frame_first_paints_.push_back(FrameFirstPaint(nullptr));
+ }
+
+ private:
+ friend class PaintControllerTestBase;
+ friend class PaintControllerPaintTestBase;
+
+ // True if all display items associated with the client are validly cached.
+ // However, the current algorithm allows the following situations even if
+ // ClientCacheIsValid() is true for a client during painting:
+ // 1. The client paints a new display item that is not cached:
+ // UseCachedDrawingIfPossible() returns false for the display item and the
+ // newly painted display item will be added into the cache. This situation
+ // has slight performance hit (see FindOutOfOrderCachedItemForward()) so we
+ // print a warning in the situation and should keep it rare.
+ // 2. the client no longer paints a display item that is cached: the cached
+ // display item will be removed. This doesn't affect performance.
+ bool ClientCacheIsValid(const DisplayItemClient&) const;
+
+ void InvalidateAllForTesting() { InvalidateAllInternal(); }
+ void InvalidateAllInternal();
+
+ bool LastDisplayItemIsNoopBegin() const;
+
+ void EnsureNewDisplayItemListInitialCapacity() {
+ if (new_display_item_list_.IsEmpty()) {
+ // TODO(wangxianzhu): Consider revisiting this heuristic.
+ new_display_item_list_ =
+ DisplayItemList(current_paint_artifact_.GetDisplayItemList().IsEmpty()
+ ? kInitialDisplayItemListCapacityBytes
+ : current_paint_artifact_.GetDisplayItemList()
+ .UsedCapacityInBytes());
+ }
+ }
+
+ // Set new item state (cache skipping, etc) for a new item.
+ void ProcessNewItem(DisplayItem&);
+ DisplayItem& MoveItemFromCurrentListToNewList(size_t);
+
+ // Maps clients to indices of display items or chunks of each client.
+ using IndicesByClientMap = HashMap<const DisplayItemClient*, Vector<size_t>>;
+
+ static size_t FindMatchingItemFromIndex(const DisplayItem::Id&,
+ const IndicesByClientMap&,
+ const DisplayItemList&);
+ static void AddToIndicesByClientMap(const DisplayItemClient&,
+ size_t index,
+ IndicesByClientMap&);
+
+ size_t FindCachedItem(const DisplayItem::Id&);
+ size_t FindOutOfOrderCachedItemForward(const DisplayItem::Id&);
+ void CopyCachedSubsequence(size_t begin_index, size_t end_index);
+
+ void UpdateCurrentPaintChunkPropertiesUsingIdWithFragment(
+ const PaintChunk::Id& id_with_fragment,
+ const PaintChunkProperties& properties) {
+ new_paint_chunks_.UpdateCurrentPaintChunkProperties(id_with_fragment,
+ properties);
+ }
+
+ // Resets the indices (e.g. next_item_to_match_) of
+ // current_paint_artifact_.GetDisplayItemList() to their initial values. This
+ // should be called when the DisplayItemList in current_paint_artifact_ is
+ // newly created, or is changed causing the previous indices to be invalid.
+ void ResetCurrentListIndices();
+
+ void GenerateRasterInvalidations(PaintChunk& new_chunk);
+ void GenerateRasterInvalidationsComparingChunks(PaintChunk& new_chunk,
+ const PaintChunk& old_chunk);
+ inline void GenerateRasterInvalidation(const DisplayItemClient&,
+ PaintChunk&,
+ const DisplayItem* old_item,
+ const DisplayItem* new_item);
+ inline void GenerateIncrementalRasterInvalidation(
+ PaintChunk&,
+ const DisplayItem& old_item,
+ const DisplayItem& new_item);
+ inline void GenerateFullRasterInvalidation(PaintChunk&,
+ const DisplayItem& old_item,
+ const DisplayItem& new_item);
+ inline void AddRasterInvalidation(const DisplayItemClient&,
+ PaintChunk&,
+ const FloatRect&,
+ PaintInvalidationReason);
+ void EnsureRasterInvalidationTracking();
+ void TrackRasterInvalidation(const DisplayItemClient&,
+ PaintChunk&,
+ PaintInvalidationReason);
+
+ // The following two methods are for checking under-invalidations
+ // (when RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled).
+ void ShowUnderInvalidationError(const char* reason,
+ const DisplayItem& new_item,
+ const DisplayItem* old_item) const;
+
+ void ShowSequenceUnderInvalidationError(const char* reason,
+ const DisplayItemClient&,
+ int start,
+ int end);
+
+ void CheckUnderInvalidation();
+ bool IsCheckingUnderInvalidation() const {
+ return under_invalidation_checking_end_ >
+ under_invalidation_checking_begin_;
+ }
+
+ struct SubsequenceMarkers {
+ SubsequenceMarkers() : start(0), end(0) {}
+ SubsequenceMarkers(size_t start_arg, size_t end_arg)
+ : start(start_arg), end(end_arg) {}
+ // The start and end (not included) index within current_paint_artifact_
+ // of this subsequence.
+ size_t start;
+ size_t end;
+ };
+
+ SubsequenceMarkers* GetSubsequenceMarkers(const DisplayItemClient&);
+
+#if DCHECK_IS_ON()
+ void CheckDuplicatePaintChunkId(const PaintChunk::Id&);
+ void ShowDebugDataInternal(DisplayItemList::JsonFlags) const;
+#endif
+
+ // The last complete paint artifact.
+ // In SPv2, this includes paint chunks as well as display items.
+ PaintArtifact current_paint_artifact_;
+
+ // Data being used to build the next paint artifact.
+ DisplayItemList new_display_item_list_;
+ PaintChunker new_paint_chunks_;
+
+ // Stores indices into new_display_item_list_ for display items that have been
+ // moved from current_paint_artifact_.GetDisplayItemList(), indexed by the
+ // positions of the display items before the move. The values are undefined
+ // for display items that are not moved.
+ Vector<size_t> items_moved_into_new_list_;
+
+ // Allows display item construction to be disabled to isolate the costs of
+ // construction in performance metrics.
+ bool construction_disabled_;
+
+ // Allows subsequence caching to be disabled to test the cost of display item
+ // caching.
+ bool subsequence_caching_disabled_;
+
+ // A stack recording current frames' first paints.
+ Vector<FrameFirstPaint> frame_first_paints_;
+
+ int skipping_cache_count_;
+
+ int num_cached_new_items_;
+
+ // Stores indices to valid cacheable display items in
+ // current_paint_artifact_.GetDisplayItemList() that have not been matched by
+ // requests of cached display items (using UseCachedDrawingIfPossible() and
+ // UseCachedSubsequenceIfPossible()) during sequential matching. The indexed
+ // items will be matched by later out-of-order requests of cached display
+ // items. This ensures that when out-of-order cached display items are
+ // requested, we only traverse at most once over the current display list
+ // looking for potential matches. Thus we can ensure that the algorithm runs
+ // in linear time.
+ IndicesByClientMap out_of_order_item_indices_;
+
+ // The next item in the current list for sequential match.
+ size_t next_item_to_match_;
+
+ // The next item in the current list to be indexed for out-of-order cache
+ // requests.
+ size_t next_item_to_index_;
+
+ // Similar to out_of_order_item_indices_ but
+ // - the indices are chunk indices in current_paint_artifacts_.PaintChunks();
+ // - chunks are matched not only for requests of cached display items, but
+ // also non-cached display items.
+ IndicesByClientMap out_of_order_chunk_indices_;
+
+ size_t current_cached_subsequence_begin_index_in_new_list_;
+ size_t next_chunk_to_match_;
+
+ DisplayItemClient::CacheGenerationOrInvalidationReason
+ current_cache_generation_;
+
+#if DCHECK_IS_ON()
+ int num_sequential_matches_;
+ int num_out_of_order_matches_;
+ int num_indexed_items_;
+
+ // This is used to check duplicated ids during CreateAndAppend().
+ IndicesByClientMap new_display_item_indices_by_client_;
+ // This is used to check duplicated ids for new paint chunks.
+ IndicesByClientMap new_paint_chunk_indices_by_client_;
+#endif
+
+ // These are set in UseCachedDrawingIfPossible() and
+ // UseCachedSubsequenceIfPossible() when we could use cached drawing or
+ // subsequence and under-invalidation checking is on, indicating the begin and
+ // end of the cached drawing or subsequence in the current list. The functions
+ // return false to let the client do actual painting, and PaintController will
+ // check if the actual painting results are the same as the cached.
+ size_t under_invalidation_checking_begin_;
+ size_t under_invalidation_checking_end_;
+
+ // Number of probable under-invalidations that have been skipped temporarily
+ // because the mismatching display items may be removed in the future because
+ // of no-op pairs or compositing folding.
+ int skipped_probable_under_invalidation_count_;
+ String under_invalidation_message_prefix_;
+
+ struct RasterInvalidationTrackingInfo {
+ using ClientDebugNamesMap = HashMap<const DisplayItemClient*, String>;
+ ClientDebugNamesMap new_client_debug_names;
+ ClientDebugNamesMap old_client_debug_names;
+ };
+ std::unique_ptr<RasterInvalidationTrackingInfo>
+ raster_invalidation_tracking_info_;
+
+ using CachedSubsequenceMap =
+ HashMap<const DisplayItemClient*, SubsequenceMarkers>;
+ CachedSubsequenceMap current_cached_subsequences_;
+ CachedSubsequenceMap new_cached_subsequences_;
+ size_t last_cached_subsequence_end_;
+
+ unsigned current_fragment_;
+
+ class DisplayItemListAsJSON;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_CONTROLLER_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller_debug_data.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller_debug_data.cc
new file mode 100644
index 00000000000..611bb004452
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller_debug_data.cc
@@ -0,0 +1,186 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
+
+#include "third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h"
+
+#if DCHECK_IS_ON()
+
+namespace blink {
+
+class PaintController::DisplayItemListAsJSON {
+ STACK_ALLOCATED();
+
+ public:
+ DisplayItemListAsJSON(const DisplayItemList&,
+ const CachedSubsequenceMap&,
+ const Vector<PaintChunk>&,
+ DisplayItemList::JsonFlags);
+
+ String ToString() {
+ return SubsequenceAsJSONArrayRecursive(0, list_.size())
+ ->ToPrettyJSONString();
+ }
+
+ private:
+ std::unique_ptr<JSONObject> SubsequenceAsJSONObjectRecursive();
+ std::unique_ptr<JSONArray> SubsequenceAsJSONArrayRecursive(size_t, size_t);
+ void AppendSubsequenceAsJSON(size_t, size_t, JSONArray&);
+ String ClientName(const DisplayItemClient&) const;
+
+ struct SubsequenceInfo {
+ SubsequenceInfo(const DisplayItemClient* client, size_t start, size_t end)
+ : client(client), start(start), end(end) {}
+ const DisplayItemClient* client;
+ size_t start;
+ size_t end;
+ };
+
+ const DisplayItemList& list_;
+ Vector<SubsequenceInfo> subsequences_;
+ Vector<SubsequenceInfo>::const_iterator current_subsequence_;
+ const Vector<PaintChunk>& chunks_;
+ Vector<PaintChunk>::const_iterator current_chunk_;
+ DisplayItemList::JsonFlags flags_;
+};
+
+PaintController::DisplayItemListAsJSON::DisplayItemListAsJSON(
+ const DisplayItemList& list,
+ const CachedSubsequenceMap& subsequence_map,
+ const Vector<PaintChunk>& chunks,
+ DisplayItemList::JsonFlags flags)
+ : list_(list),
+ chunks_(chunks),
+ current_chunk_(chunks.begin()),
+ flags_(flags) {
+ for (const auto& item : subsequence_map) {
+ subsequences_.push_back(
+ SubsequenceInfo(item.key, item.value.start, item.value.end));
+ }
+ std::sort(subsequences_.begin(), subsequences_.end(),
+ [](const SubsequenceInfo& a, const SubsequenceInfo& b) {
+ return a.start == b.start ? a.end > b.end : a.start < b.start;
+ });
+
+ current_subsequence_ = subsequences_.begin();
+}
+
+std::unique_ptr<JSONObject>
+PaintController::DisplayItemListAsJSON::SubsequenceAsJSONObjectRecursive() {
+ const auto& subsequence = *current_subsequence_;
+ ++current_subsequence_;
+
+ auto json_object = JSONObject::Create();
+
+ json_object->SetString("subsequence",
+ String::Format("client: %p ", subsequence.client) +
+ ClientName(*subsequence.client));
+ json_object->SetArray(
+ RuntimeEnabledFeatures::SlimmingPaintV175Enabled() ? "chunks"
+ : "displayItems",
+ SubsequenceAsJSONArrayRecursive(subsequence.start, subsequence.end));
+
+ return json_object;
+}
+
+std::unique_ptr<JSONArray>
+PaintController::DisplayItemListAsJSON::SubsequenceAsJSONArrayRecursive(
+ size_t start_item,
+ size_t end_item) {
+ std::unique_ptr<JSONArray> array = JSONArray::Create();
+ size_t item_index = start_item;
+
+ while (current_subsequence_ != subsequences_.end() &&
+ current_subsequence_->start < end_item) {
+ const auto& subsequence = *current_subsequence_;
+ DCHECK(subsequence.start >= item_index);
+ DCHECK(subsequence.end <= end_item);
+
+ if (item_index < subsequence.start)
+ AppendSubsequenceAsJSON(item_index, subsequence.start, *array);
+ array->PushObject(SubsequenceAsJSONObjectRecursive());
+ item_index = subsequence.end;
+ }
+
+ if (item_index < end_item)
+ AppendSubsequenceAsJSON(item_index, end_item, *array);
+
+ return array;
+}
+
+void PaintController::DisplayItemListAsJSON::AppendSubsequenceAsJSON(
+ size_t start_item,
+ size_t end_item,
+ JSONArray& json_array) {
+ DCHECK(end_item > start_item);
+
+ if (!RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ list_.AppendSubsequenceAsJSON(start_item, end_item, flags_, json_array);
+ return;
+ }
+
+ DCHECK(current_chunk_ != chunks_.end());
+ DCHECK(current_chunk_->begin_index == start_item);
+
+ while (current_chunk_ != chunks_.end() &&
+ current_chunk_->end_index <= end_item) {
+ const auto& chunk = *current_chunk_;
+ auto json_object = JSONObject::Create();
+
+ json_object->SetString(
+ "chunk", ClientName(chunk.id.client) + " " + chunk.id.ToString());
+ json_object->SetString("state",
+ chunk.properties.property_tree_state.ToString());
+ if (flags_ & DisplayItemList::kShowPaintRecords)
+ json_object->SetString("chunkData", chunk.ToString());
+
+ json_object->SetArray(
+ "displayItems",
+ list_.SubsequenceAsJSON(chunk.begin_index, chunk.end_index, flags_));
+
+ json_array.PushObject(std::move(json_object));
+ ++current_chunk_;
+ }
+}
+
+String PaintController::DisplayItemListAsJSON::ClientName(
+ const DisplayItemClient& client) const {
+ return DisplayItemClient::SafeDebugName(
+ client, flags_ & DisplayItemList::kClientKnownToBeAlive);
+}
+
+void PaintController::ShowDebugDataInternal(
+ DisplayItemList::JsonFlags flags) const {
+ LOG(ERROR) << "current display item list: "
+ << DisplayItemListAsJSON(
+ current_paint_artifact_.GetDisplayItemList(),
+ current_cached_subsequences_,
+ current_paint_artifact_.PaintChunks(), flags)
+ .ToString()
+ .Utf8()
+ .data();
+
+ LOG(ERROR) << "new display item list: "
+ << DisplayItemListAsJSON(
+ new_display_item_list_, new_cached_subsequences_,
+ new_paint_chunks_.PaintChunks(),
+ // The clients in new_display_item_list_ are all alive.
+ flags | DisplayItemList::kClientKnownToBeAlive)
+ .ToString()
+ .Utf8()
+ .data();
+}
+
+void PaintController::ShowDebugData() const {
+ return ShowDebugDataInternal(DisplayItemList::kDefault);
+}
+
+void PaintController::ShowDebugDataWithRecords() const {
+ return ShowDebugDataInternal(DisplayItemList::kShowPaintRecords);
+}
+
+} // namespace blink
+
+#endif // DCHECK_IS_ON()
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.cc
new file mode 100644
index 00000000000..2bf51931fc5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.cc
@@ -0,0 +1,2443 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/paint_controller_test.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/paint/clip_path_display_item.h"
+#include "third_party/blink/renderer/platform/graphics/paint/clip_path_recorder.h"
+#include "third_party/blink/renderer/platform/graphics/paint/clip_recorder.h"
+#include "third_party/blink/renderer/platform/graphics/paint/compositing_recorder.h"
+#include "third_party/blink/renderer/platform/graphics/paint/display_item_cache_skipper.h"
+#include "third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h"
+#include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
+#include "third_party/blink/renderer/platform/graphics/paint/scoped_display_item_fragment.h"
+#include "third_party/blink/renderer/platform/graphics/paint/scoped_paint_chunk_properties.h"
+#include "third_party/blink/renderer/platform/graphics/paint/subsequence_recorder.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/testing/paint_test_configurations.h"
+
+using blink::test::CreateOpacityOnlyEffect;
+using testing::UnorderedElementsAre;
+
+namespace blink {
+
+// Tests using this class will be tested with under-invalidation-checking
+// enabled and disabled.
+class PaintControllerTest : public PaintTestConfigurations,
+ public PaintControllerTestBase {
+};
+
+INSTANTIATE_TEST_CASE_P(
+ All,
+ PaintControllerTest,
+ testing::Values(0,
+ kSlimmingPaintV175,
+ kSlimmingPaintV2,
+ kUnderInvalidationChecking,
+ kSlimmingPaintV175 | kUnderInvalidationChecking,
+ kSlimmingPaintV2 | kUnderInvalidationChecking));
+
+TEST_P(PaintControllerTest, NestedRecorders) {
+ GraphicsContext context(GetPaintController());
+ FakeDisplayItemClient client("client", LayoutRect(100, 100, 200, 200));
+ InitRootChunk();
+
+ {
+ ClipRecorder clip_recorder(context, client, kClipType,
+ IntRect(100, 100, 50, 50));
+ DrawRect(context, client, kBackgroundType, FloatRect(100, 100, 200, 200));
+ }
+ GetPaintController().CommitNewDisplayItems();
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 1,
+ TestDisplayItem(client, kBackgroundType));
+
+ EXPECT_EQ(1u, GetPaintController().PaintChunks().size());
+ // Raster invalidation for the whole chunk will be issued during
+ // PaintArtifactCompositor::Update().
+ EXPECT_TRUE(GetPaintController()
+ .PaintChunks()[0]
+ .raster_invalidation_rects.IsEmpty());
+ } else {
+ EXPECT_DISPLAY_LIST(
+ GetPaintController().GetDisplayItemList(), 3,
+ TestDisplayItem(client, kClipType),
+ TestDisplayItem(client, kBackgroundType),
+ TestDisplayItem(client, DisplayItem::ClipTypeToEndClipType(kClipType)));
+ }
+}
+
+TEST_P(PaintControllerTest, UpdateBasic) {
+ FakeDisplayItemClient first("first", LayoutRect(100, 100, 300, 300));
+ FakeDisplayItemClient second("second", LayoutRect(100, 100, 200, 200));
+ GraphicsContext context(GetPaintController());
+ InitRootChunk();
+
+ DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 300, 300));
+ DrawRect(context, second, kBackgroundType, FloatRect(100, 100, 200, 200));
+ DrawRect(context, first, kForegroundType, FloatRect(100, 100, 300, 300));
+
+ EXPECT_EQ(0, NumCachedNewItems());
+
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 3,
+ TestDisplayItem(first, kBackgroundType),
+ TestDisplayItem(second, kBackgroundType),
+ TestDisplayItem(first, kForegroundType));
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ EXPECT_EQ(1u, GetPaintController().PaintChunks().size());
+ // Raster invalidation for the whole chunk will be issued during
+ // PaintArtifactCompositor::Update().
+ EXPECT_TRUE(GetPaintController()
+ .PaintChunks()[0]
+ .raster_invalidation_rects.IsEmpty());
+
+ InitRootChunk();
+ }
+
+ DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 300, 300));
+ DrawRect(context, first, kForegroundType, FloatRect(100, 100, 300, 300));
+
+ EXPECT_EQ(2, NumCachedNewItems());
+#if DCHECK_IS_ON()
+ EXPECT_EQ(2, NumSequentialMatches());
+ EXPECT_EQ(0, NumOutOfOrderMatches());
+ EXPECT_EQ(1, NumIndexedItems());
+#endif
+
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 2,
+ TestDisplayItem(first, kBackgroundType),
+ TestDisplayItem(first, kForegroundType));
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ EXPECT_EQ(1u, GetPaintController().PaintChunks().size());
+ EXPECT_THAT(GetPaintController().PaintChunks()[0].raster_invalidation_rects,
+ // |second| disappeared from the chunk.
+ UnorderedElementsAre(FloatRect(100, 100, 200, 200)));
+ }
+}
+
+TEST_P(PaintControllerTest, UpdateSwapOrder) {
+ FakeDisplayItemClient first("first", LayoutRect(100, 100, 100, 100));
+ FakeDisplayItemClient second("second", LayoutRect(100, 100, 50, 200));
+ FakeDisplayItemClient unaffected("unaffected", LayoutRect(300, 300, 10, 10));
+ GraphicsContext context(GetPaintController());
+ InitRootChunk();
+
+ DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 100, 100));
+ DrawRect(context, first, kForegroundType, FloatRect(100, 100, 100, 100));
+ DrawRect(context, second, kBackgroundType, FloatRect(100, 100, 50, 200));
+ DrawRect(context, second, kForegroundType, FloatRect(100, 100, 50, 200));
+ DrawRect(context, unaffected, kBackgroundType, FloatRect(300, 300, 10, 10));
+ DrawRect(context, unaffected, kForegroundType, FloatRect(300, 300, 10, 10));
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 6,
+ TestDisplayItem(first, kBackgroundType),
+ TestDisplayItem(first, kForegroundType),
+ TestDisplayItem(second, kBackgroundType),
+ TestDisplayItem(second, kForegroundType),
+ TestDisplayItem(unaffected, kBackgroundType),
+ TestDisplayItem(unaffected, kForegroundType));
+
+ InitRootChunk();
+ DrawRect(context, second, kBackgroundType, FloatRect(100, 100, 50, 200));
+ DrawRect(context, second, kForegroundType, FloatRect(100, 100, 50, 200));
+ DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 100, 100));
+ DrawRect(context, first, kForegroundType, FloatRect(100, 100, 100, 100));
+ DrawRect(context, unaffected, kBackgroundType, FloatRect(300, 300, 10, 10));
+ DrawRect(context, unaffected, kForegroundType, FloatRect(300, 300, 10, 10));
+
+ EXPECT_EQ(6, NumCachedNewItems());
+#if DCHECK_IS_ON()
+ EXPECT_EQ(5, NumSequentialMatches()); // second, first foreground, unaffected
+ EXPECT_EQ(1, NumOutOfOrderMatches()); // first
+ EXPECT_EQ(2, NumIndexedItems()); // first
+#endif
+
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 6,
+ TestDisplayItem(second, kBackgroundType),
+ TestDisplayItem(second, kForegroundType),
+ TestDisplayItem(first, kBackgroundType),
+ TestDisplayItem(first, kForegroundType),
+ TestDisplayItem(unaffected, kBackgroundType),
+ TestDisplayItem(unaffected, kForegroundType));
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ EXPECT_EQ(1u, GetPaintController().PaintChunks().size());
+ EXPECT_THAT(GetPaintController().PaintChunks()[0].raster_invalidation_rects,
+ // Bounds of |second| (old and new are the same).
+ UnorderedElementsAre(FloatRect(100, 100, 50, 200)));
+ }
+}
+
+TEST_P(PaintControllerTest, UpdateSwapOrderWithInvalidation) {
+ FakeDisplayItemClient first("first", LayoutRect(100, 100, 100, 100));
+ FakeDisplayItemClient second("second", LayoutRect(100, 100, 50, 200));
+ FakeDisplayItemClient unaffected("unaffected", LayoutRect(300, 300, 10, 10));
+ GraphicsContext context(GetPaintController());
+ InitRootChunk();
+
+ DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 100, 100));
+ DrawRect(context, first, kForegroundType, FloatRect(100, 100, 100, 100));
+ DrawRect(context, second, kBackgroundType, FloatRect(100, 100, 50, 200));
+ DrawRect(context, second, kForegroundType, FloatRect(100, 100, 50, 200));
+ DrawRect(context, unaffected, kBackgroundType, FloatRect(300, 300, 10, 10));
+ DrawRect(context, unaffected, kForegroundType, FloatRect(300, 300, 10, 10));
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 6,
+ TestDisplayItem(first, kBackgroundType),
+ TestDisplayItem(first, kForegroundType),
+ TestDisplayItem(second, kBackgroundType),
+ TestDisplayItem(second, kForegroundType),
+ TestDisplayItem(unaffected, kBackgroundType),
+ TestDisplayItem(unaffected, kForegroundType));
+
+ InitRootChunk();
+ first.SetDisplayItemsUncached();
+ DrawRect(context, second, kBackgroundType, FloatRect(100, 100, 50, 200));
+ DrawRect(context, second, kForegroundType, FloatRect(100, 100, 50, 200));
+ DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 100, 100));
+ DrawRect(context, first, kForegroundType, FloatRect(100, 100, 100, 100));
+ DrawRect(context, unaffected, kBackgroundType, FloatRect(300, 300, 10, 10));
+ DrawRect(context, unaffected, kForegroundType, FloatRect(300, 300, 10, 10));
+
+ EXPECT_EQ(4, NumCachedNewItems());
+#if DCHECK_IS_ON()
+ EXPECT_EQ(4, NumSequentialMatches()); // second, unaffected
+ EXPECT_EQ(0, NumOutOfOrderMatches());
+ EXPECT_EQ(2, NumIndexedItems());
+#endif
+
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 6,
+ TestDisplayItem(second, kBackgroundType),
+ TestDisplayItem(second, kForegroundType),
+ TestDisplayItem(first, kBackgroundType),
+ TestDisplayItem(first, kForegroundType),
+ TestDisplayItem(unaffected, kBackgroundType),
+ TestDisplayItem(unaffected, kForegroundType));
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ EXPECT_EQ(1u, GetPaintController().PaintChunks().size());
+ EXPECT_THAT(GetPaintController().PaintChunks()[0].raster_invalidation_rects,
+ // Bounds of |first| (old and new are the same).
+ UnorderedElementsAre(FloatRect(100, 100, 100, 100)));
+ // No need to invalidate raster of |second|, because the client (|first|)
+ // which swapped order with it has been invalidated.
+ }
+}
+
+TEST_P(PaintControllerTest, UpdateNewItemInMiddle) {
+ FakeDisplayItemClient first("first", LayoutRect(100, 100, 100, 100));
+ FakeDisplayItemClient second("second", LayoutRect(100, 100, 50, 200));
+ FakeDisplayItemClient third("third", LayoutRect(125, 100, 200, 50));
+ GraphicsContext context(GetPaintController());
+ InitRootChunk();
+
+ DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 100, 100));
+ DrawRect(context, second, kBackgroundType, FloatRect(100, 100, 50, 200));
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 2,
+ TestDisplayItem(first, kBackgroundType),
+ TestDisplayItem(second, kBackgroundType));
+
+ InitRootChunk();
+
+ DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 100, 100));
+ DrawRect(context, third, kBackgroundType, FloatRect(125, 100, 200, 50));
+ DrawRect(context, second, kBackgroundType, FloatRect(100, 100, 50, 200));
+
+ EXPECT_EQ(2, NumCachedNewItems());
+#if DCHECK_IS_ON()
+ EXPECT_EQ(2, NumSequentialMatches()); // first, second
+ EXPECT_EQ(0, NumOutOfOrderMatches());
+ EXPECT_EQ(0, NumIndexedItems());
+#endif
+
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 3,
+ TestDisplayItem(first, kBackgroundType),
+ TestDisplayItem(third, kBackgroundType),
+ TestDisplayItem(second, kBackgroundType));
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ EXPECT_EQ(1u, GetPaintController().PaintChunks().size());
+ EXPECT_THAT(GetPaintController().PaintChunks()[0].raster_invalidation_rects,
+ // |third| newly appeared in the chunk.
+ UnorderedElementsAre(FloatRect(125, 100, 200, 50)));
+ }
+}
+
+TEST_P(PaintControllerTest, UpdateInvalidationWithPhases) {
+ FakeDisplayItemClient first("first", LayoutRect(100, 100, 100, 100));
+ FakeDisplayItemClient second("second", LayoutRect(100, 100, 50, 200));
+ FakeDisplayItemClient third("third", LayoutRect(300, 100, 50, 50));
+ GraphicsContext context(GetPaintController());
+ InitRootChunk();
+
+ DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 100, 100));
+ DrawRect(context, second, kBackgroundType, FloatRect(100, 100, 50, 200));
+ DrawRect(context, third, kBackgroundType, FloatRect(300, 100, 50, 50));
+ DrawRect(context, first, kForegroundType, FloatRect(100, 100, 100, 100));
+ DrawRect(context, second, kForegroundType, FloatRect(100, 100, 50, 200));
+ DrawRect(context, third, kForegroundType, FloatRect(300, 100, 50, 50));
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 6,
+ TestDisplayItem(first, kBackgroundType),
+ TestDisplayItem(second, kBackgroundType),
+ TestDisplayItem(third, kBackgroundType),
+ TestDisplayItem(first, kForegroundType),
+ TestDisplayItem(second, kForegroundType),
+ TestDisplayItem(third, kForegroundType));
+
+ InitRootChunk();
+
+ second.SetDisplayItemsUncached();
+ DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 100, 100));
+ DrawRect(context, second, kBackgroundType, FloatRect(100, 100, 50, 200));
+ DrawRect(context, third, kBackgroundType, FloatRect(300, 100, 50, 50));
+ DrawRect(context, first, kForegroundType, FloatRect(100, 100, 100, 100));
+ DrawRect(context, second, kForegroundType, FloatRect(100, 100, 50, 200));
+ DrawRect(context, third, kForegroundType, FloatRect(300, 100, 50, 50));
+
+ EXPECT_EQ(4, NumCachedNewItems());
+#if DCHECK_IS_ON()
+ EXPECT_EQ(4, NumSequentialMatches());
+ EXPECT_EQ(0, NumOutOfOrderMatches());
+ EXPECT_EQ(2, NumIndexedItems());
+#endif
+
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 6,
+ TestDisplayItem(first, kBackgroundType),
+ TestDisplayItem(second, kBackgroundType),
+ TestDisplayItem(third, kBackgroundType),
+ TestDisplayItem(first, kForegroundType),
+ TestDisplayItem(second, kForegroundType),
+ TestDisplayItem(third, kForegroundType));
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ EXPECT_EQ(1u, GetPaintController().PaintChunks().size());
+ EXPECT_THAT(GetPaintController().PaintChunks()[0].raster_invalidation_rects,
+ // Bounds of |second| (old and new are the same).
+ UnorderedElementsAre(FloatRect(100, 100, 50, 200)));
+ }
+}
+
+TEST_P(PaintControllerTest, IncrementalRasterInvalidation) {
+ if (!RuntimeEnabledFeatures::SlimmingPaintV175Enabled())
+ return;
+
+ LayoutRect initial_rect(100, 100, 100, 100);
+ std::unique_ptr<FakeDisplayItemClient> clients[6];
+ for (auto& client : clients)
+ client = std::make_unique<FakeDisplayItemClient>("", initial_rect);
+ GraphicsContext context(GetPaintController());
+
+ InitRootChunk();
+
+ for (auto& client : clients)
+ DrawRect(context, *client, kBackgroundType, FloatRect(initial_rect));
+ GetPaintController().CommitNewDisplayItems();
+
+ InitRootChunk();
+ clients[0]->SetVisualRect(LayoutRect(100, 100, 150, 100));
+ clients[1]->SetVisualRect(LayoutRect(100, 100, 100, 150));
+ clients[2]->SetVisualRect(LayoutRect(100, 100, 150, 80));
+ clients[3]->SetVisualRect(LayoutRect(100, 100, 80, 150));
+ clients[4]->SetVisualRect(LayoutRect(100, 100, 150, 150));
+ clients[5]->SetVisualRect(LayoutRect(100, 100, 80, 80));
+ for (auto& client : clients) {
+ client->SetDisplayItemsUncached(PaintInvalidationReason::kIncremental);
+ DrawRect(context, *client, kBackgroundType,
+ FloatRect(client->VisualRect()));
+ }
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_EQ(1u, GetPaintController().PaintChunks().size());
+ EXPECT_THAT(GetPaintController().PaintChunks()[0].raster_invalidation_rects,
+ UnorderedElementsAre(FloatRect(200, 100, 50, 100), // 0: right
+ FloatRect(100, 200, 100, 50), // 1: bottom
+ FloatRect(200, 100, 50, 80), // 2: right
+ FloatRect(100, 180, 100, 20), // 2: bottom
+ FloatRect(180, 100, 20, 100), // 3: right
+ FloatRect(100, 200, 80, 50), // 3: bottom
+ FloatRect(200, 100, 50, 150), // 4: right
+ FloatRect(100, 200, 150, 50), // 4: bottom
+ FloatRect(180, 100, 20, 100), // 5: right
+ FloatRect(100, 180, 100, 20))); // 5: bottom
+
+ InitRootChunk();
+}
+
+TEST_P(PaintControllerTest, UpdateAddFirstOverlap) {
+ FakeDisplayItemClient first("first", LayoutRect(100, 100, 150, 150));
+ FakeDisplayItemClient second("second", LayoutRect(200, 200, 50, 50));
+ GraphicsContext context(GetPaintController());
+ InitRootChunk();
+
+ DrawRect(context, second, kBackgroundType, FloatRect(200, 200, 50, 50));
+ DrawRect(context, second, kForegroundType, FloatRect(200, 200, 50, 50));
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 2,
+ TestDisplayItem(second, kBackgroundType),
+ TestDisplayItem(second, kForegroundType));
+
+ InitRootChunk();
+
+ first.SetDisplayItemsUncached();
+ second.SetDisplayItemsUncached();
+ second.SetVisualRect(LayoutRect(150, 250, 100, 100));
+ DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 150, 150));
+ DrawRect(context, first, kForegroundType, FloatRect(100, 100, 150, 150));
+ DrawRect(context, second, kBackgroundType, FloatRect(150, 250, 100, 100));
+ DrawRect(context, second, kForegroundType, FloatRect(150, 250, 100, 100));
+ EXPECT_EQ(0, NumCachedNewItems());
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 4,
+ TestDisplayItem(first, kBackgroundType),
+ TestDisplayItem(first, kForegroundType),
+ TestDisplayItem(second, kBackgroundType),
+ TestDisplayItem(second, kForegroundType));
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ EXPECT_EQ(1u, GetPaintController().PaintChunks().size());
+ EXPECT_THAT(
+ GetPaintController().PaintChunks()[0].raster_invalidation_rects,
+ UnorderedElementsAre(
+ // |first| newly appeared in the chunk.
+ FloatRect(100, 100, 150, 150),
+ // Old and new bounds of |second|.
+ FloatRect(200, 200, 50, 50), FloatRect(150, 250, 100, 100)));
+
+ InitRootChunk();
+ }
+
+ DrawRect(context, second, kBackgroundType, FloatRect(150, 250, 100, 100));
+ DrawRect(context, second, kForegroundType, FloatRect(150, 250, 100, 100));
+
+ EXPECT_EQ(2, NumCachedNewItems());
+#if DCHECK_IS_ON()
+ EXPECT_EQ(2, NumSequentialMatches());
+ EXPECT_EQ(0, NumOutOfOrderMatches());
+ EXPECT_EQ(2, NumIndexedItems());
+#endif
+
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 2,
+ TestDisplayItem(second, kBackgroundType),
+ TestDisplayItem(second, kForegroundType));
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ EXPECT_EQ(1u, GetPaintController().PaintChunks().size());
+ EXPECT_THAT(GetPaintController().PaintChunks()[0].raster_invalidation_rects,
+ // |first| disappeared from the chunk.
+ UnorderedElementsAre(FloatRect(100, 100, 150, 150)));
+ }
+}
+
+TEST_P(PaintControllerTest, UpdateAddLastOverlap) {
+ FakeDisplayItemClient first("first", LayoutRect(100, 100, 150, 150));
+ FakeDisplayItemClient second("second", LayoutRect(200, 200, 50, 50));
+ GraphicsContext context(GetPaintController());
+ InitRootChunk();
+
+ DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 150, 150));
+ DrawRect(context, first, kForegroundType, FloatRect(100, 100, 150, 150));
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 2,
+ TestDisplayItem(first, kBackgroundType),
+ TestDisplayItem(first, kForegroundType));
+
+ InitRootChunk();
+
+ first.SetDisplayItemsUncached();
+ first.SetVisualRect(LayoutRect(150, 150, 100, 100));
+ second.SetDisplayItemsUncached();
+ DrawRect(context, first, kBackgroundType, FloatRect(150, 150, 100, 100));
+ DrawRect(context, first, kForegroundType, FloatRect(150, 150, 100, 100));
+ DrawRect(context, second, kBackgroundType, FloatRect(200, 200, 50, 50));
+ DrawRect(context, second, kForegroundType, FloatRect(200, 200, 50, 50));
+ EXPECT_EQ(0, NumCachedNewItems());
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 4,
+ TestDisplayItem(first, kBackgroundType),
+ TestDisplayItem(first, kForegroundType),
+ TestDisplayItem(second, kBackgroundType),
+ TestDisplayItem(second, kForegroundType));
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ EXPECT_EQ(1u, GetPaintController().PaintChunks().size());
+ EXPECT_THAT(GetPaintController().PaintChunks()[0].raster_invalidation_rects,
+ UnorderedElementsAre(
+ // The bigger of old and new bounds of |first|.
+ FloatRect(100, 100, 150, 150),
+ // |second| newly appeared in the chunk.
+ FloatRect(200, 200, 50, 50)));
+
+ InitRootChunk();
+ }
+
+ first.SetDisplayItemsUncached();
+ first.SetVisualRect(LayoutRect(100, 100, 150, 150));
+ second.SetDisplayItemsUncached();
+ DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 150, 150));
+ DrawRect(context, first, kForegroundType, FloatRect(100, 100, 150, 150));
+ EXPECT_EQ(0, NumCachedNewItems());
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 2,
+ TestDisplayItem(first, kBackgroundType),
+ TestDisplayItem(first, kForegroundType));
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ EXPECT_EQ(1u, GetPaintController().PaintChunks().size());
+ EXPECT_THAT(GetPaintController().PaintChunks()[0].raster_invalidation_rects,
+ UnorderedElementsAre(
+ // The bigger of old and new bounds of |first|.
+ FloatRect(100, 100, 150, 150),
+ // |second| disappeared from the chunk.
+ FloatRect(200, 200, 50, 50)));
+ }
+}
+
+TEST_P(PaintControllerTest, UpdateClip) {
+ FakeDisplayItemClient first("first", LayoutRect(100, 100, 150, 150));
+ FakeDisplayItemClient second("second", LayoutRect(100, 100, 200, 200));
+ GraphicsContext context(GetPaintController());
+
+ scoped_refptr<ClipPaintPropertyNode> clip = ClipPaintPropertyNode::Create(
+ nullptr, nullptr, FloatRoundedRect(1, 1, 2, 2));
+
+ {
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ PaintChunk::Id id(first, kClipType);
+ PaintChunkProperties properties = test::DefaultPaintChunkProperties();
+ properties.property_tree_state.SetClip(clip.get());
+ GetPaintController().UpdateCurrentPaintChunkProperties(id, properties);
+ }
+ ClipRecorder clip_recorder(context, first, kClipType, IntRect(1, 1, 2, 2));
+ DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 150, 150));
+ DrawRect(context, second, kBackgroundType, FloatRect(100, 100, 200, 200));
+ }
+ GetPaintController().CommitNewDisplayItems();
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 2,
+ TestDisplayItem(first, kBackgroundType),
+ TestDisplayItem(second, kBackgroundType));
+
+ InitRootChunk();
+ } else {
+ EXPECT_DISPLAY_LIST(
+ GetPaintController().GetDisplayItemList(), 4,
+ TestDisplayItem(first, kClipType),
+ TestDisplayItem(first, kBackgroundType),
+ TestDisplayItem(second, kBackgroundType),
+ TestDisplayItem(first, DisplayItem::ClipTypeToEndClipType(kClipType)));
+ }
+
+ first.SetDisplayItemsUncached();
+ DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 150, 150));
+ DrawRect(context, second, kBackgroundType, FloatRect(100, 100, 200, 200));
+
+ EXPECT_EQ(1, NumCachedNewItems());
+#if DCHECK_IS_ON()
+ EXPECT_EQ(1, NumSequentialMatches());
+ EXPECT_EQ(0, NumOutOfOrderMatches());
+ EXPECT_EQ(1, NumIndexedItems());
+#endif
+
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 2,
+ TestDisplayItem(first, kBackgroundType),
+ TestDisplayItem(second, kBackgroundType));
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ EXPECT_EQ(1u, GetPaintController().PaintChunks().size());
+ // This is a new chunk. Raster invalidation for the whole chunk will be
+ // issued during PaintArtifactCompositor::Update().
+ EXPECT_TRUE(GetPaintController()
+ .PaintChunks()[0]
+ .raster_invalidation_rects.IsEmpty());
+
+ InitRootChunk();
+ }
+
+ second.SetDisplayItemsUncached();
+ DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 150, 150));
+
+ scoped_refptr<ClipPaintPropertyNode> clip2 = ClipPaintPropertyNode::Create(
+ nullptr, nullptr, FloatRoundedRect(1, 1, 2, 2));
+
+ {
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ PaintChunk::Id id(second, kClipType);
+ PaintChunkProperties properties = test::DefaultPaintChunkProperties();
+ properties.property_tree_state.SetClip(clip2.get());
+
+ GetPaintController().UpdateCurrentPaintChunkProperties(id, properties);
+ }
+ ClipRecorder clip_recorder(context, second, kClipType, IntRect(1, 1, 2, 2));
+ DrawRect(context, second, kBackgroundType, FloatRect(100, 100, 200, 200));
+ }
+ GetPaintController().CommitNewDisplayItems();
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 2,
+ TestDisplayItem(first, kBackgroundType),
+ TestDisplayItem(second, kBackgroundType));
+
+ EXPECT_EQ(2u, GetPaintController().PaintChunks().size());
+ EXPECT_THAT(GetPaintController().PaintChunks()[0].raster_invalidation_rects,
+ // |second| disappeared from the first chunk.
+ UnorderedElementsAre(FloatRect(100, 100, 200, 200)));
+ // This is a new chunk. Raster invalidation for the whole chunk will be
+ // issued during PaintArtifactCompositor::Update().
+ EXPECT_TRUE(GetPaintController()
+ .PaintChunks()[1]
+ .raster_invalidation_rects.IsEmpty());
+ } else {
+ EXPECT_DISPLAY_LIST(
+ GetPaintController().GetDisplayItemList(), 4,
+ TestDisplayItem(first, kBackgroundType),
+ TestDisplayItem(second, kClipType),
+ TestDisplayItem(second, kBackgroundType),
+ TestDisplayItem(second, DisplayItem::ClipTypeToEndClipType(kClipType)));
+ }
+}
+
+TEST_P(PaintControllerTest, CachedDisplayItems) {
+ FakeDisplayItemClient first("first");
+ FakeDisplayItemClient second("second");
+ GraphicsContext context(GetPaintController());
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ InitRootChunk();
+ }
+
+ DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 150, 150));
+ DrawRect(context, second, kBackgroundType, FloatRect(100, 100, 150, 150));
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 2,
+ TestDisplayItem(first, kBackgroundType),
+ TestDisplayItem(second, kBackgroundType));
+ EXPECT_TRUE(ClientCacheIsValid(first));
+ EXPECT_TRUE(ClientCacheIsValid(second));
+ sk_sp<const PaintRecord> first_paint_record =
+ static_cast<const DrawingDisplayItem&>(
+ GetPaintController().GetDisplayItemList()[0])
+ .GetPaintRecord();
+ sk_sp<const PaintRecord> second_paint_record =
+ static_cast<const DrawingDisplayItem&>(
+ GetPaintController().GetDisplayItemList()[1])
+ .GetPaintRecord();
+
+ first.SetDisplayItemsUncached();
+ EXPECT_FALSE(ClientCacheIsValid(first));
+ EXPECT_TRUE(ClientCacheIsValid(second));
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ InitRootChunk();
+ }
+ DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 150, 150));
+ DrawRect(context, second, kBackgroundType, FloatRect(100, 100, 150, 150));
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 2,
+ TestDisplayItem(first, kBackgroundType),
+ TestDisplayItem(second, kBackgroundType));
+ // The first display item should be updated.
+ EXPECT_NE(first_paint_record,
+ static_cast<const DrawingDisplayItem&>(
+ GetPaintController().GetDisplayItemList()[0])
+ .GetPaintRecord());
+ // The second display item should be cached.
+ if (!RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) {
+ EXPECT_EQ(second_paint_record,
+ static_cast<const DrawingDisplayItem&>(
+ GetPaintController().GetDisplayItemList()[1])
+ .GetPaintRecord());
+ }
+ EXPECT_TRUE(ClientCacheIsValid(first));
+ EXPECT_TRUE(ClientCacheIsValid(second));
+
+ InvalidateAll();
+ EXPECT_FALSE(ClientCacheIsValid(first));
+ EXPECT_FALSE(ClientCacheIsValid(second));
+}
+
+TEST_P(PaintControllerTest, UpdateSwapOrderWithChildren) {
+ FakeDisplayItemClient container1("container1",
+ LayoutRect(100, 100, 100, 100));
+ FakeDisplayItemClient content1("content1", LayoutRect(100, 100, 50, 200));
+ FakeDisplayItemClient container2("container2",
+ LayoutRect(100, 200, 100, 100));
+ FakeDisplayItemClient content2("content2", LayoutRect(100, 200, 50, 200));
+ GraphicsContext context(GetPaintController());
+ InitRootChunk();
+
+ DrawRect(context, container1, kBackgroundType, FloatRect(100, 100, 100, 100));
+ DrawRect(context, content1, kBackgroundType, FloatRect(100, 100, 50, 200));
+ DrawRect(context, content1, kForegroundType, FloatRect(100, 100, 50, 200));
+ DrawRect(context, container1, kForegroundType, FloatRect(100, 100, 100, 100));
+ DrawRect(context, container2, kBackgroundType, FloatRect(100, 200, 100, 100));
+ DrawRect(context, content2, kBackgroundType, FloatRect(100, 200, 50, 200));
+ DrawRect(context, content2, kForegroundType, FloatRect(100, 200, 50, 200));
+ DrawRect(context, container2, kForegroundType, FloatRect(100, 200, 100, 100));
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 8,
+ TestDisplayItem(container1, kBackgroundType),
+ TestDisplayItem(content1, kBackgroundType),
+ TestDisplayItem(content1, kForegroundType),
+ TestDisplayItem(container1, kForegroundType),
+ TestDisplayItem(container2, kBackgroundType),
+ TestDisplayItem(content2, kBackgroundType),
+ TestDisplayItem(content2, kForegroundType),
+ TestDisplayItem(container2, kForegroundType));
+
+ InitRootChunk();
+
+ // Simulate the situation when |container1| gets a z-index that is greater
+ // than that of |container2|.
+ DrawRect(context, container2, kBackgroundType, FloatRect(100, 200, 100, 100));
+ DrawRect(context, content2, kBackgroundType, FloatRect(100, 200, 50, 200));
+ DrawRect(context, content2, kForegroundType, FloatRect(100, 200, 50, 200));
+ DrawRect(context, container2, kForegroundType, FloatRect(100, 200, 100, 100));
+ DrawRect(context, container1, kBackgroundType, FloatRect(100, 100, 100, 100));
+ DrawRect(context, content1, kBackgroundType, FloatRect(100, 100, 50, 200));
+ DrawRect(context, content1, kForegroundType, FloatRect(100, 100, 50, 200));
+ DrawRect(context, container1, kForegroundType, FloatRect(100, 100, 100, 100));
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 8,
+ TestDisplayItem(container2, kBackgroundType),
+ TestDisplayItem(content2, kBackgroundType),
+ TestDisplayItem(content2, kForegroundType),
+ TestDisplayItem(container2, kForegroundType),
+ TestDisplayItem(container1, kBackgroundType),
+ TestDisplayItem(content1, kBackgroundType),
+ TestDisplayItem(content1, kForegroundType),
+ TestDisplayItem(container1, kForegroundType));
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ EXPECT_EQ(1u, GetPaintController().PaintChunks().size());
+ EXPECT_THAT(
+ GetPaintController().PaintChunks()[0].raster_invalidation_rects,
+ UnorderedElementsAre(
+ // Bounds of |container2| which was moved behind |container1|.
+ FloatRect(100, 200, 100, 100),
+ // Bounds of |content2| which was moved along with |container2|.
+ FloatRect(100, 200, 50, 200)));
+ }
+}
+
+TEST_P(PaintControllerTest, UpdateSwapOrderWithChildrenAndInvalidation) {
+ FakeDisplayItemClient container1("container1",
+ LayoutRect(100, 100, 100, 100));
+ FakeDisplayItemClient content1("content1", LayoutRect(100, 100, 50, 200));
+ FakeDisplayItemClient container2("container2",
+ LayoutRect(100, 200, 100, 100));
+ FakeDisplayItemClient content2("content2", LayoutRect(100, 200, 50, 200));
+ GraphicsContext context(GetPaintController());
+ InitRootChunk();
+
+ DrawRect(context, container1, kBackgroundType, FloatRect(100, 100, 100, 100));
+ DrawRect(context, content1, kBackgroundType, FloatRect(100, 100, 50, 200));
+ DrawRect(context, content1, kForegroundType, FloatRect(100, 100, 50, 200));
+ DrawRect(context, container1, kForegroundType, FloatRect(100, 100, 100, 100));
+ DrawRect(context, container2, kBackgroundType, FloatRect(100, 200, 100, 100));
+ DrawRect(context, content2, kBackgroundType, FloatRect(100, 200, 50, 200));
+ DrawRect(context, content2, kForegroundType, FloatRect(100, 200, 50, 200));
+ DrawRect(context, container2, kForegroundType, FloatRect(100, 200, 100, 100));
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 8,
+ TestDisplayItem(container1, kBackgroundType),
+ TestDisplayItem(content1, kBackgroundType),
+ TestDisplayItem(content1, kForegroundType),
+ TestDisplayItem(container1, kForegroundType),
+ TestDisplayItem(container2, kBackgroundType),
+ TestDisplayItem(content2, kBackgroundType),
+ TestDisplayItem(content2, kForegroundType),
+ TestDisplayItem(container2, kForegroundType));
+
+ InitRootChunk();
+
+ // Simulate the situation when |container1| gets a z-index that is greater
+ // than that of |container2|, and |container1| is invalidated.
+ container1.SetDisplayItemsUncached();
+ DrawRect(context, container2, kBackgroundType, FloatRect(100, 200, 100, 100));
+ DrawRect(context, content2, kBackgroundType, FloatRect(100, 200, 50, 200));
+ DrawRect(context, content2, kForegroundType, FloatRect(100, 200, 50, 200));
+ DrawRect(context, container2, kForegroundType, FloatRect(100, 200, 100, 100));
+ DrawRect(context, container1, kBackgroundType, FloatRect(100, 100, 100, 100));
+ DrawRect(context, content1, kBackgroundType, FloatRect(100, 100, 50, 200));
+ DrawRect(context, content1, kForegroundType, FloatRect(100, 100, 50, 200));
+ DrawRect(context, container1, kForegroundType, FloatRect(100, 100, 100, 100));
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 8,
+ TestDisplayItem(container2, kBackgroundType),
+ TestDisplayItem(content2, kBackgroundType),
+ TestDisplayItem(content2, kForegroundType),
+ TestDisplayItem(container2, kForegroundType),
+ TestDisplayItem(container1, kBackgroundType),
+ TestDisplayItem(content1, kBackgroundType),
+ TestDisplayItem(content1, kForegroundType),
+ TestDisplayItem(container1, kForegroundType));
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ EXPECT_EQ(1u, GetPaintController().PaintChunks().size());
+ EXPECT_THAT(
+ GetPaintController().PaintChunks()[0].raster_invalidation_rects,
+ UnorderedElementsAre(
+ // Bounds of |container1| (old and new are the same).
+ FloatRect(100, 100, 100, 100),
+ // Bounds of |container2| which was moved behind |container1|.
+ FloatRect(100, 200, 100, 100),
+ // Bounds of |content2| which was moved along with |container2|.
+ FloatRect(100, 200, 50, 200)));
+ }
+}
+
+TEST_P(PaintControllerTest, CachedSubsequenceForcePaintChunk) {
+ if (!RuntimeEnabledFeatures::SlimmingPaintV175Enabled() ||
+ RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled())
+ return;
+
+ GraphicsContext context(GetPaintController());
+
+ FakeDisplayItemClient root("root");
+ auto root_properties = test::DefaultPaintChunkProperties();
+ PaintChunk::Id root_id(root, DisplayItem::kCaret);
+ // Record a first chunk with backface_hidden == false
+ GetPaintController().UpdateCurrentPaintChunkProperties(root_id,
+ root_properties);
+ DrawRect(context, root, kBackgroundType, FloatRect(100, 100, 100, 100));
+
+ FakeDisplayItemClient container("container");
+ {
+ // Record a second chunk with backface_hidden == true
+ auto container_properties = test::DefaultPaintChunkProperties();
+ container_properties.backface_hidden = true;
+ PaintChunk::Id container_id(container, DisplayItem::kCaret);
+
+ SubsequenceRecorder r(context, container);
+ GetPaintController().UpdateCurrentPaintChunkProperties(
+ container_id, container_properties);
+ DrawRect(context, container, kBackgroundType,
+ FloatRect(100, 100, 100, 100));
+ DrawRect(context, container, kForegroundType,
+ FloatRect(100, 100, 100, 100));
+ }
+
+ DrawRect(context, root, kForegroundType, FloatRect(100, 100, 100, 100));
+
+ GetPaintController().CommitNewDisplayItems();
+
+ root_properties.backface_hidden = true;
+ // This time, record the fist chunk with backface_hidden == true
+ GetPaintController().UpdateCurrentPaintChunkProperties(root_id,
+ root_properties);
+ DrawRect(context, root, kBackgroundType, FloatRect(100, 100, 100, 100));
+ EXPECT_TRUE(GetPaintController().UseCachedSubsequenceIfPossible(container));
+ DrawRect(context, root, kForegroundType, FloatRect(100, 100, 100, 100));
+ GetPaintController().CommitNewDisplayItems();
+
+ // Even though the paint properties match, |container| should receive its
+ // own PaintChunk because it is a cached subsequence.
+ EXPECT_EQ(3u, GetPaintController().GetPaintArtifact().PaintChunks().size());
+ EXPECT_EQ(root,
+ GetPaintController().GetPaintArtifact().PaintChunks()[0].id.client);
+ EXPECT_EQ(container,
+ GetPaintController().GetPaintArtifact().PaintChunks()[1].id.client);
+ EXPECT_EQ(root,
+ GetPaintController().GetPaintArtifact().PaintChunks()[2].id.client);
+}
+
+TEST_P(PaintControllerTest, CachedSubsequenceSwapOrder) {
+ FakeDisplayItemClient container1("container1",
+ LayoutRect(100, 100, 100, 100));
+ FakeDisplayItemClient content1("content1", LayoutRect(100, 100, 50, 200));
+ FakeDisplayItemClient container2("container2",
+ LayoutRect(100, 200, 100, 100));
+ FakeDisplayItemClient content2("content2", LayoutRect(100, 200, 50, 200));
+ GraphicsContext context(GetPaintController());
+
+ PaintChunkProperties container1_properties =
+ test::DefaultPaintChunkProperties();
+ PaintChunkProperties container2_properties =
+ test::DefaultPaintChunkProperties();
+
+ {
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ PaintChunk::Id id(container1, kBackgroundType);
+ container1_properties.property_tree_state.SetEffect(
+ CreateOpacityOnlyEffect(EffectPaintPropertyNode::Root(), 0.5).get());
+ GetPaintController().UpdateCurrentPaintChunkProperties(
+ id, container1_properties);
+ }
+ SubsequenceRecorder r(context, container1);
+ DrawRect(context, container1, kBackgroundType,
+ FloatRect(100, 100, 100, 100));
+ DrawRect(context, content1, kBackgroundType, FloatRect(100, 100, 50, 200));
+ DrawRect(context, content1, kForegroundType, FloatRect(100, 100, 50, 200));
+ DrawRect(context, container1, kForegroundType,
+ FloatRect(100, 100, 100, 100));
+ }
+ {
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ PaintChunk::Id id(container2, kBackgroundType);
+ container2_properties.property_tree_state.SetEffect(
+ CreateOpacityOnlyEffect(EffectPaintPropertyNode::Root(), 0.5).get());
+ GetPaintController().UpdateCurrentPaintChunkProperties(
+ id, container2_properties);
+ }
+ SubsequenceRecorder r(context, container2);
+ DrawRect(context, container2, kBackgroundType,
+ FloatRect(100, 200, 100, 100));
+ DrawRect(context, content2, kBackgroundType, FloatRect(100, 200, 50, 200));
+ DrawRect(context, content2, kForegroundType, FloatRect(100, 200, 50, 200));
+ DrawRect(context, container2, kForegroundType,
+ FloatRect(100, 200, 100, 100));
+ }
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 8,
+ TestDisplayItem(container1, kBackgroundType),
+ TestDisplayItem(content1, kBackgroundType),
+ TestDisplayItem(content1, kForegroundType),
+ TestDisplayItem(container1, kForegroundType),
+
+ TestDisplayItem(container2, kBackgroundType),
+ TestDisplayItem(content2, kBackgroundType),
+ TestDisplayItem(content2, kForegroundType),
+ TestDisplayItem(container2, kForegroundType));
+
+ auto* markers = GetSubsequenceMarkers(container1);
+ CHECK(markers);
+ EXPECT_EQ(0u, markers->start);
+ EXPECT_EQ(4u, markers->end);
+
+ markers = GetSubsequenceMarkers(container2);
+ CHECK(markers);
+ EXPECT_EQ(4u, markers->start);
+ EXPECT_EQ(8u, markers->end);
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ EXPECT_EQ(2u, GetPaintController().PaintChunks().size());
+ EXPECT_EQ(PaintChunk::Id(container1, kBackgroundType),
+ GetPaintController().PaintChunks()[0].id);
+ EXPECT_EQ(PaintChunk::Id(container2, kBackgroundType),
+ GetPaintController().PaintChunks()[1].id);
+ // Raster invalidation for the whole chunks will be issued during
+ // PaintArtifactCompositor::Update().
+ EXPECT_TRUE(GetPaintController()
+ .PaintChunks()[0]
+ .raster_invalidation_rects.IsEmpty());
+ EXPECT_TRUE(GetPaintController()
+ .PaintChunks()[1]
+ .raster_invalidation_rects.IsEmpty());
+ }
+
+ // Simulate the situation when |container1| gets a z-index that is greater
+ // than that of |container2|.
+ if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) {
+ // When under-invalidation-checking is enabled,
+ // useCachedSubsequenceIfPossible is forced off, and the client is expected
+ // to create the same painting as in the previous paint.
+ EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+ context, container2));
+ {
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ PaintChunk::Id id(container2, kBackgroundType);
+ GetPaintController().UpdateCurrentPaintChunkProperties(
+ id, container2_properties);
+ }
+ SubsequenceRecorder r(context, container2);
+ DrawRect(context, container2, kBackgroundType,
+ FloatRect(100, 200, 100, 100));
+ DrawRect(context, content2, kBackgroundType,
+ FloatRect(100, 200, 50, 200));
+ DrawRect(context, content2, kForegroundType,
+ FloatRect(100, 200, 50, 200));
+ DrawRect(context, container2, kForegroundType,
+ FloatRect(100, 200, 100, 100));
+ }
+ EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+ context, container1));
+ {
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ PaintChunk::Id id(container1, kBackgroundType);
+ GetPaintController().UpdateCurrentPaintChunkProperties(
+ id, container1_properties);
+ }
+ SubsequenceRecorder r(context, container1);
+ DrawRect(context, container1, kBackgroundType,
+ FloatRect(100, 100, 100, 100));
+ DrawRect(context, content1, kBackgroundType,
+ FloatRect(100, 100, 50, 200));
+ DrawRect(context, content1, kForegroundType,
+ FloatRect(100, 100, 50, 200));
+ DrawRect(context, container1, kForegroundType,
+ FloatRect(100, 100, 100, 100));
+ }
+ } else {
+ EXPECT_TRUE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+ context, container2));
+ EXPECT_TRUE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+ context, container1));
+ }
+
+ EXPECT_EQ(8, NumCachedNewItems());
+#if DCHECK_IS_ON()
+ EXPECT_EQ(0, NumSequentialMatches());
+ EXPECT_EQ(0, NumOutOfOrderMatches());
+ EXPECT_EQ(0, NumIndexedItems());
+#endif
+
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 8,
+ TestDisplayItem(container2, kBackgroundType),
+ TestDisplayItem(content2, kBackgroundType),
+ TestDisplayItem(content2, kForegroundType),
+ TestDisplayItem(container2, kForegroundType),
+ TestDisplayItem(container1, kBackgroundType),
+ TestDisplayItem(content1, kBackgroundType),
+ TestDisplayItem(content1, kForegroundType),
+ TestDisplayItem(container1, kForegroundType));
+
+ markers = GetSubsequenceMarkers(container2);
+ CHECK(markers);
+ EXPECT_EQ(0u, markers->start);
+ EXPECT_EQ(4u, markers->end);
+
+ markers = GetSubsequenceMarkers(container1);
+ CHECK(markers);
+ EXPECT_EQ(4u, markers->start);
+ EXPECT_EQ(8u, markers->end);
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ EXPECT_EQ(2u, GetPaintController().PaintChunks().size());
+ EXPECT_EQ(PaintChunk::Id(container2, kBackgroundType),
+ GetPaintController().PaintChunks()[0].id);
+ EXPECT_EQ(PaintChunk::Id(container1, kBackgroundType),
+ GetPaintController().PaintChunks()[1].id);
+ // Swapping order of chunks should not invalidate anything.
+ EXPECT_THAT(GetPaintController().PaintChunks()[0].raster_invalidation_rects,
+ UnorderedElementsAre());
+ EXPECT_THAT(GetPaintController().PaintChunks()[1].raster_invalidation_rects,
+ UnorderedElementsAre());
+ }
+}
+
+TEST_P(PaintControllerTest, CachedSubsequenceAndDisplayItemsSwapOrder) {
+ FakeDisplayItemClient root("root", LayoutRect(0, 0, 300, 300));
+ FakeDisplayItemClient content1("content1", LayoutRect(100, 100, 50, 200));
+ FakeDisplayItemClient container2("container2",
+ LayoutRect(100, 200, 100, 100));
+ FakeDisplayItemClient content2("content2", LayoutRect(100, 200, 50, 200));
+ GraphicsContext context(GetPaintController());
+
+ InitRootChunk();
+
+ DrawRect(context, content1, kBackgroundType, FloatRect(100, 100, 50, 200));
+ {
+ SubsequenceRecorder r(context, container2);
+ DrawRect(context, container2, kBackgroundType,
+ FloatRect(100, 200, 100, 100));
+ DrawRect(context, content2, kBackgroundType, FloatRect(100, 200, 50, 200));
+ DrawRect(context, content2, kForegroundType, FloatRect(100, 200, 50, 200));
+ DrawRect(context, container2, kForegroundType,
+ FloatRect(100, 200, 100, 100));
+ }
+ DrawRect(context, content1, kForegroundType, FloatRect(100, 100, 50, 200));
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 6,
+ TestDisplayItem(content1, kBackgroundType),
+ TestDisplayItem(container2, kBackgroundType),
+ TestDisplayItem(content2, kBackgroundType),
+ TestDisplayItem(content2, kForegroundType),
+ TestDisplayItem(container2, kForegroundType),
+ TestDisplayItem(content1, kForegroundType));
+
+ auto* markers = GetSubsequenceMarkers(container2);
+ CHECK(markers);
+ EXPECT_EQ(1u, markers->start);
+ EXPECT_EQ(5u, markers->end);
+
+ // Simulate the situation when |container2| gets a z-index that is smaller
+ // than that of |content1|.
+ InitRootChunk();
+ if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) {
+ // When under-invalidation-checking is enabled,
+ // useCachedSubsequenceIfPossible is forced off, and the client is expected
+ // to create the same painting as in the previous paint.
+ EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+ context, container2));
+ {
+ SubsequenceRecorder r(context, container2);
+ DrawRect(context, container2, kBackgroundType,
+ FloatRect(100, 200, 100, 100));
+ DrawRect(context, content2, kBackgroundType,
+ FloatRect(100, 200, 50, 200));
+ DrawRect(context, content2, kForegroundType,
+ FloatRect(100, 200, 50, 200));
+ DrawRect(context, container2, kForegroundType,
+ FloatRect(100, 200, 100, 100));
+ }
+ DrawRect(context, content1, kBackgroundType, FloatRect(100, 100, 50, 200));
+ DrawRect(context, content1, kForegroundType, FloatRect(100, 100, 50, 200));
+ } else {
+ EXPECT_TRUE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+ context, container2));
+ EXPECT_TRUE(DrawingRecorder::UseCachedDrawingIfPossible(context, content1,
+ kBackgroundType));
+ EXPECT_TRUE(DrawingRecorder::UseCachedDrawingIfPossible(context, content1,
+ kForegroundType));
+ }
+
+ EXPECT_EQ(6, NumCachedNewItems());
+#if DCHECK_IS_ON()
+ EXPECT_EQ(2, NumSequentialMatches());
+ EXPECT_EQ(0, NumOutOfOrderMatches());
+ EXPECT_EQ(0, NumIndexedItems());
+#endif
+
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 6,
+ TestDisplayItem(container2, kBackgroundType),
+ TestDisplayItem(content2, kBackgroundType),
+ TestDisplayItem(content2, kForegroundType),
+ TestDisplayItem(container2, kForegroundType),
+ TestDisplayItem(content1, kBackgroundType),
+ TestDisplayItem(content1, kForegroundType));
+
+ markers = GetSubsequenceMarkers(container2);
+ CHECK(markers);
+ EXPECT_EQ(0u, markers->start);
+ EXPECT_EQ(4u, markers->end);
+}
+
+TEST_P(PaintControllerTest, CachedSubsequenceWithFragments) {
+ if (!RuntimeEnabledFeatures::SlimmingPaintV175Enabled())
+ return;
+
+ GraphicsContext context(GetPaintController());
+ FakeDisplayItemClient root("root");
+ constexpr size_t kFragmentCount = 3;
+ FakeDisplayItemClient container("container");
+
+ // The first paint.
+ auto paint_container = [this, &context, &container, kFragmentCount]() {
+ SubsequenceRecorder r(context, container);
+ for (size_t i = 0; i < kFragmentCount; ++i) {
+ ScopedDisplayItemFragment scoped_fragment(context, i);
+ ScopedPaintChunkProperties content_chunk_properties(
+ GetPaintController(), test::DefaultPaintChunkProperties(), container,
+ kBackgroundType);
+ DrawRect(context, container, kBackgroundType,
+ FloatRect(100, 100, 100, 100));
+ }
+ };
+ {
+ ScopedPaintChunkProperties root_chunk_properties(
+ GetPaintController(), test::DefaultPaintChunkProperties(), root,
+ kBackgroundType);
+ DrawRect(context, root, kBackgroundType, FloatRect(100, 100, 100, 100));
+ paint_container();
+ DrawRect(context, root, kForegroundType, FloatRect(100, 100, 100, 100));
+ }
+ GetPaintController().CommitNewDisplayItems();
+
+ // Check results of the first paint.
+ auto check_paint_results = [this, &root, &container, kFragmentCount]() {
+ const auto& chunks = GetPaintController().PaintChunks();
+ ASSERT_EQ(2u + kFragmentCount, chunks.size());
+ EXPECT_EQ(root, chunks[0].id.client);
+ EXPECT_EQ(kBackgroundType, chunks[0].id.type);
+ EXPECT_EQ(0u, chunks[0].id.fragment);
+ for (size_t i = 0; i < kFragmentCount; ++i) {
+ const auto& id = chunks[i + 1].id;
+ EXPECT_EQ(container, id.client);
+ EXPECT_EQ(kBackgroundType, id.type);
+ EXPECT_EQ(i, id.fragment);
+ }
+ EXPECT_EQ(root, chunks.back().id.client);
+ EXPECT_EQ(kForegroundType, chunks.back().id.type);
+ EXPECT_EQ(0u, chunks.back().id.fragment);
+ };
+ check_paint_results();
+
+ // The second paint.
+ {
+ ScopedPaintChunkProperties root_chunk_properties(
+ GetPaintController(), test::DefaultPaintChunkProperties(), root,
+ kBackgroundType);
+ DrawRect(context, root, kBackgroundType, FloatRect(100, 100, 100, 100));
+
+ if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) {
+ EXPECT_FALSE(
+ GetPaintController().UseCachedSubsequenceIfPossible(container));
+ paint_container();
+ } else {
+ EXPECT_TRUE(
+ GetPaintController().UseCachedSubsequenceIfPossible(container));
+ }
+ DrawRect(context, root, kForegroundType, FloatRect(100, 100, 100, 100));
+ }
+ GetPaintController().CommitNewDisplayItems();
+
+ // The second paint should produce the exactly same results.
+ check_paint_results();
+}
+
+TEST_P(PaintControllerTest, UpdateSwapOrderCrossingChunks) {
+ FakeDisplayItemClient container1("container1",
+ LayoutRect(100, 100, 100, 100));
+ FakeDisplayItemClient content1("content1", LayoutRect(100, 100, 50, 200));
+ FakeDisplayItemClient container2("container2",
+ LayoutRect(100, 200, 100, 100));
+ FakeDisplayItemClient content2("content2", LayoutRect(100, 200, 50, 200));
+ GraphicsContext context(GetPaintController());
+
+ PaintChunkProperties container1_properties =
+ test::DefaultPaintChunkProperties();
+ PaintChunkProperties container2_properties =
+ test::DefaultPaintChunkProperties();
+
+ {
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ PaintChunk::Id id(container1, kBackgroundType);
+ container1_properties.property_tree_state.SetEffect(
+ CreateOpacityOnlyEffect(EffectPaintPropertyNode::Root(), 0.5).get());
+ GetPaintController().UpdateCurrentPaintChunkProperties(
+ id, container1_properties);
+ }
+ DrawRect(context, container1, kBackgroundType,
+ FloatRect(100, 100, 100, 100));
+ DrawRect(context, content1, kBackgroundType, FloatRect(100, 100, 50, 200));
+ }
+ {
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ PaintChunk::Id id(container2, kBackgroundType);
+ container2_properties.property_tree_state.SetEffect(
+ CreateOpacityOnlyEffect(EffectPaintPropertyNode::Root(), 0.5).get());
+ GetPaintController().UpdateCurrentPaintChunkProperties(
+ id, container2_properties);
+ }
+ DrawRect(context, container2, kBackgroundType,
+ FloatRect(100, 200, 100, 100));
+ DrawRect(context, content2, kBackgroundType, FloatRect(100, 200, 50, 200));
+ }
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 4,
+ TestDisplayItem(container1, kBackgroundType),
+ TestDisplayItem(content1, kBackgroundType),
+ TestDisplayItem(container2, kBackgroundType),
+ TestDisplayItem(content2, kBackgroundType));
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ EXPECT_EQ(2u, GetPaintController().PaintChunks().size());
+ EXPECT_EQ(PaintChunk::Id(container1, kBackgroundType),
+ GetPaintController().PaintChunks()[0].id);
+ EXPECT_EQ(PaintChunk::Id(container2, kBackgroundType),
+ GetPaintController().PaintChunks()[1].id);
+ // Raster invalidation for the whole chunks will be issued during
+ // PaintArtifactCompositor::Update().
+ EXPECT_TRUE(GetPaintController()
+ .PaintChunks()[0]
+ .raster_invalidation_rects.IsEmpty());
+ EXPECT_TRUE(GetPaintController()
+ .PaintChunks()[1]
+ .raster_invalidation_rects.IsEmpty());
+ }
+
+ // Move content2 into container1, without invalidation.
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ PaintChunk::Id id(container1, kBackgroundType);
+ GetPaintController().UpdateCurrentPaintChunkProperties(
+ id, container1_properties);
+ }
+ DrawRect(context, container1, kBackgroundType, FloatRect(100, 100, 100, 100));
+ DrawRect(context, content1, kBackgroundType, FloatRect(100, 100, 50, 200));
+ DrawRect(context, content2, kBackgroundType, FloatRect(100, 200, 50, 200));
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ PaintChunk::Id id(container2, kBackgroundType);
+ GetPaintController().UpdateCurrentPaintChunkProperties(
+ id, container2_properties);
+ }
+ DrawRect(context, container2, kBackgroundType, FloatRect(100, 200, 100, 100));
+
+ EXPECT_EQ(4, NumCachedNewItems());
+#if DCHECK_IS_ON()
+ EXPECT_EQ(3, NumSequentialMatches());
+ EXPECT_EQ(1, NumOutOfOrderMatches());
+ EXPECT_EQ(1, NumIndexedItems());
+#endif
+
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 4,
+ TestDisplayItem(container1, kBackgroundType),
+ TestDisplayItem(content1, kBackgroundType),
+ TestDisplayItem(content2, kBackgroundType),
+ TestDisplayItem(container2, kBackgroundType));
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ EXPECT_EQ(2u, GetPaintController().PaintChunks().size());
+ EXPECT_EQ(PaintChunk::Id(container1, kBackgroundType),
+ GetPaintController().PaintChunks()[0].id);
+ EXPECT_EQ(PaintChunk::Id(container2, kBackgroundType),
+ GetPaintController().PaintChunks()[1].id);
+ // |content2| is invalidated raster on both the old chunk and the new chunk.
+ EXPECT_THAT(GetPaintController().PaintChunks()[0].raster_invalidation_rects,
+ UnorderedElementsAre(FloatRect(100, 200, 50, 200)));
+ EXPECT_THAT(GetPaintController().PaintChunks()[1].raster_invalidation_rects,
+ UnorderedElementsAre(FloatRect(100, 200, 50, 200)));
+ }
+}
+
+TEST_P(PaintControllerTest, OutOfOrderNoCrash) {
+ FakeDisplayItemClient client("client");
+ GraphicsContext context(GetPaintController());
+
+ const DisplayItem::Type kType1 = DisplayItem::kDrawingFirst;
+ const DisplayItem::Type kType2 =
+ static_cast<DisplayItem::Type>(DisplayItem::kDrawingFirst + 1);
+ const DisplayItem::Type kType3 =
+ static_cast<DisplayItem::Type>(DisplayItem::kDrawingFirst + 2);
+ const DisplayItem::Type kType4 =
+ static_cast<DisplayItem::Type>(DisplayItem::kDrawingFirst + 3);
+
+ InitRootChunk();
+ DrawRect(context, client, kType1, FloatRect(100, 100, 100, 100));
+ DrawRect(context, client, kType2, FloatRect(100, 100, 50, 200));
+ DrawRect(context, client, kType3, FloatRect(100, 100, 50, 200));
+ DrawRect(context, client, kType4, FloatRect(100, 100, 100, 100));
+
+ GetPaintController().CommitNewDisplayItems();
+
+ InitRootChunk();
+ DrawRect(context, client, kType2, FloatRect(100, 100, 50, 200));
+ DrawRect(context, client, kType3, FloatRect(100, 100, 50, 200));
+ DrawRect(context, client, kType1, FloatRect(100, 100, 100, 100));
+ DrawRect(context, client, kType4, FloatRect(100, 100, 100, 100));
+
+ GetPaintController().CommitNewDisplayItems();
+}
+
+TEST_P(PaintControllerTest, CachedNestedSubsequenceUpdate) {
+ FakeDisplayItemClient container1("container1",
+ LayoutRect(100, 100, 100, 100));
+ FakeDisplayItemClient content1("content1", LayoutRect(100, 100, 50, 200));
+ FakeDisplayItemClient container2("container2",
+ LayoutRect(100, 200, 100, 100));
+ FakeDisplayItemClient content2("content2", LayoutRect(100, 200, 50, 200));
+ GraphicsContext context(GetPaintController());
+
+ PaintChunkProperties container1_background_properties =
+ test::DefaultPaintChunkProperties();
+ PaintChunkProperties content1_properties =
+ test::DefaultPaintChunkProperties();
+ PaintChunkProperties container1_foreground_properties =
+ test::DefaultPaintChunkProperties();
+ PaintChunkProperties container2_background_properties =
+ test::DefaultPaintChunkProperties();
+ PaintChunkProperties content2_properties =
+ test::DefaultPaintChunkProperties();
+
+ {
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ PaintChunk::Id id(container1, kBackgroundType);
+ container1_background_properties.property_tree_state.SetEffect(
+ CreateOpacityOnlyEffect(EffectPaintPropertyNode::Root(), 0.5).get());
+ GetPaintController().UpdateCurrentPaintChunkProperties(
+ id, container1_background_properties);
+ }
+ SubsequenceRecorder r(context, container1);
+ DrawRect(context, container1, kBackgroundType,
+ FloatRect(100, 100, 100, 100));
+ {
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ PaintChunk::Id id(content1, kBackgroundType);
+ content1_properties.property_tree_state.SetEffect(
+ CreateOpacityOnlyEffect(EffectPaintPropertyNode::Root(), 0.6)
+ .get());
+ GetPaintController().UpdateCurrentPaintChunkProperties(
+ id, content1_properties);
+ }
+ SubsequenceRecorder r(context, content1);
+ DrawRect(context, content1, kBackgroundType,
+ FloatRect(100, 100, 50, 200));
+ DrawRect(context, content1, kForegroundType,
+ FloatRect(100, 100, 50, 200));
+ }
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ PaintChunk::Id id(container1, kForegroundType);
+ container1_foreground_properties.property_tree_state.SetEffect(
+ CreateOpacityOnlyEffect(EffectPaintPropertyNode::Root(), 0.5).get());
+ GetPaintController().UpdateCurrentPaintChunkProperties(
+ id, container1_foreground_properties);
+ }
+ DrawRect(context, container1, kForegroundType,
+ FloatRect(100, 100, 100, 100));
+ }
+ {
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ PaintChunk::Id id(container2, kBackgroundType);
+ container2_background_properties.property_tree_state.SetEffect(
+ CreateOpacityOnlyEffect(EffectPaintPropertyNode::Root(), 0.7).get());
+ GetPaintController().UpdateCurrentPaintChunkProperties(
+ id, container2_background_properties);
+ }
+ SubsequenceRecorder r(context, container2);
+ DrawRect(context, container2, kBackgroundType,
+ FloatRect(100, 200, 100, 100));
+ {
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ PaintChunk::Id id(content2, kBackgroundType);
+ content2_properties.property_tree_state.SetEffect(
+ CreateOpacityOnlyEffect(EffectPaintPropertyNode::Root(), 0.8)
+ .get());
+ GetPaintController().UpdateCurrentPaintChunkProperties(
+ id, content2_properties);
+ }
+ SubsequenceRecorder r(context, content2);
+ DrawRect(context, content2, kBackgroundType,
+ FloatRect(100, 200, 50, 200));
+ }
+ }
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 6,
+ TestDisplayItem(container1, kBackgroundType),
+ TestDisplayItem(content1, kBackgroundType),
+ TestDisplayItem(content1, kForegroundType),
+ TestDisplayItem(container1, kForegroundType),
+ TestDisplayItem(container2, kBackgroundType),
+ TestDisplayItem(content2, kBackgroundType));
+
+ auto* markers = GetSubsequenceMarkers(container1);
+ CHECK(markers);
+ EXPECT_EQ(0u, markers->start);
+ EXPECT_EQ(4u, markers->end);
+
+ markers = GetSubsequenceMarkers(content1);
+ CHECK(markers);
+ EXPECT_EQ(1u, markers->start);
+ EXPECT_EQ(3u, markers->end);
+
+ markers = GetSubsequenceMarkers(container2);
+ CHECK(markers);
+ EXPECT_EQ(4u, markers->start);
+ EXPECT_EQ(6u, markers->end);
+
+ markers = GetSubsequenceMarkers(content2);
+ CHECK(markers);
+ EXPECT_EQ(5u, markers->start);
+ EXPECT_EQ(6u, markers->end);
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ EXPECT_EQ(5u, GetPaintController().PaintChunks().size());
+ EXPECT_EQ(PaintChunk::Id(container1, kBackgroundType),
+ GetPaintController().PaintChunks()[0].id);
+ EXPECT_EQ(PaintChunk::Id(content1, kBackgroundType),
+ GetPaintController().PaintChunks()[1].id);
+ EXPECT_EQ(PaintChunk::Id(container1, kForegroundType),
+ GetPaintController().PaintChunks()[2].id);
+ EXPECT_EQ(PaintChunk::Id(container2, kBackgroundType),
+ GetPaintController().PaintChunks()[3].id);
+ EXPECT_EQ(PaintChunk::Id(content2, kBackgroundType),
+ GetPaintController().PaintChunks()[4].id);
+ // Raster invalidation for the whole chunks will be issued during
+ // PaintArtifactCompositor::Update().
+ EXPECT_TRUE(GetPaintController()
+ .PaintChunks()[0]
+ .raster_invalidation_rects.IsEmpty());
+ EXPECT_TRUE(GetPaintController()
+ .PaintChunks()[1]
+ .raster_invalidation_rects.IsEmpty());
+ EXPECT_TRUE(GetPaintController()
+ .PaintChunks()[2]
+ .raster_invalidation_rects.IsEmpty());
+ EXPECT_TRUE(GetPaintController()
+ .PaintChunks()[3]
+ .raster_invalidation_rects.IsEmpty());
+ EXPECT_TRUE(GetPaintController()
+ .PaintChunks()[4]
+ .raster_invalidation_rects.IsEmpty());
+ }
+
+ // Invalidate container1 but not content1.
+ container1.SetDisplayItemsUncached();
+
+ // Container2 itself now becomes empty (but still has the 'content2' child),
+ // and chooses not to output subsequence info.
+
+ container2.SetDisplayItemsUncached();
+ content2.SetDisplayItemsUncached();
+ EXPECT_FALSE(
+ SubsequenceRecorder::UseCachedSubsequenceIfPossible(context, container2));
+ EXPECT_FALSE(
+ SubsequenceRecorder::UseCachedSubsequenceIfPossible(context, content2));
+ // Content2 now outputs foreground only.
+ {
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ PaintChunk::Id id(content2, kForegroundType);
+ GetPaintController().UpdateCurrentPaintChunkProperties(
+ id, content2_properties);
+ }
+ SubsequenceRecorder r(context, content2);
+ DrawRect(context, content2, kForegroundType, FloatRect(100, 200, 50, 200));
+ }
+ // Repaint container1 with foreground only.
+ {
+ EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+ context, container1));
+ SubsequenceRecorder r(context, container1);
+ // Use cached subsequence of content1.
+ if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) {
+ // When under-invalidation-checking is enabled,
+ // useCachedSubsequenceIfPossible is forced off, and the client is
+ // expected to create the same painting as in the previous paint.
+ EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+ context, content1));
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ PaintChunk::Id id(content1, kBackgroundType);
+ GetPaintController().UpdateCurrentPaintChunkProperties(
+ id, content1_properties);
+ }
+ SubsequenceRecorder r(context, content1);
+ DrawRect(context, content1, kBackgroundType,
+ FloatRect(100, 100, 50, 200));
+ DrawRect(context, content1, kForegroundType,
+ FloatRect(100, 100, 50, 200));
+ } else {
+ EXPECT_TRUE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+ context, content1));
+ }
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ PaintChunk::Id id(container1, kForegroundType);
+ GetPaintController().UpdateCurrentPaintChunkProperties(
+ id, container1_foreground_properties);
+ }
+ DrawRect(context, container1, kForegroundType,
+ FloatRect(100, 100, 100, 100));
+ }
+
+ EXPECT_EQ(2, NumCachedNewItems());
+#if DCHECK_IS_ON()
+ EXPECT_EQ(0, NumSequentialMatches());
+ EXPECT_EQ(0, NumOutOfOrderMatches());
+ EXPECT_EQ(0, NumIndexedItems());
+#endif
+
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 4,
+ TestDisplayItem(content2, kForegroundType),
+ TestDisplayItem(content1, kBackgroundType),
+ TestDisplayItem(content1, kForegroundType),
+ TestDisplayItem(container1, kForegroundType));
+
+ markers = GetSubsequenceMarkers(content2);
+ CHECK(markers);
+ EXPECT_EQ(0u, markers->start);
+ EXPECT_EQ(1u, markers->end);
+
+ markers = GetSubsequenceMarkers(container1);
+ CHECK(markers);
+ EXPECT_EQ(1u, markers->start);
+ EXPECT_EQ(4u, markers->end);
+
+ markers = GetSubsequenceMarkers(content1);
+ CHECK(markers);
+ EXPECT_EQ(1u, markers->start);
+ EXPECT_EQ(3u, markers->end);
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ EXPECT_EQ(3u, GetPaintController().PaintChunks().size());
+ EXPECT_EQ(PaintChunk::Id(content2, kForegroundType),
+ GetPaintController().PaintChunks()[0].id);
+ EXPECT_EQ(PaintChunk::Id(content1, kBackgroundType),
+ GetPaintController().PaintChunks()[1].id);
+ EXPECT_EQ(PaintChunk::Id(container1, kForegroundType),
+ GetPaintController().PaintChunks()[2].id);
+ // This is a new chunk. Raster invalidation of the whole chunk will be
+ // issued during PaintArtifactCompositor::Update().
+ EXPECT_TRUE(GetPaintController()
+ .PaintChunks()[0]
+ .raster_invalidation_rects.IsEmpty());
+ // This chunk didn't change.
+ EXPECT_TRUE(GetPaintController()
+ .PaintChunks()[1]
+ .raster_invalidation_rects.IsEmpty());
+ // |container1| is invalidated.
+ EXPECT_THAT(GetPaintController().PaintChunks()[2].raster_invalidation_rects,
+ UnorderedElementsAre(FloatRect(100, 100, 100, 100)));
+ }
+}
+
+TEST_P(PaintControllerTest, SkipCache) {
+ FakeDisplayItemClient multicol("multicol", LayoutRect(100, 100, 200, 200));
+ FakeDisplayItemClient content("content", LayoutRect(100, 100, 100, 100));
+ GraphicsContext context(GetPaintController());
+ InitRootChunk();
+
+ FloatRect rect1(100, 100, 50, 50);
+ FloatRect rect2(150, 100, 50, 50);
+ FloatRect rect3(200, 100, 50, 50);
+
+ DrawRect(context, multicol, kBackgroundType, FloatRect(100, 200, 100, 100));
+
+ GetPaintController().BeginSkippingCache();
+ DrawRect(context, content, kForegroundType, rect1);
+ DrawRect(context, content, kForegroundType, rect2);
+ GetPaintController().EndSkippingCache();
+
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 3,
+ TestDisplayItem(multicol, kBackgroundType),
+ TestDisplayItem(content, kForegroundType),
+ TestDisplayItem(content, kForegroundType));
+ sk_sp<const PaintRecord> record1 =
+ static_cast<const DrawingDisplayItem&>(
+ GetPaintController().GetDisplayItemList()[1])
+ .GetPaintRecord();
+ sk_sp<const PaintRecord> record2 =
+ static_cast<const DrawingDisplayItem&>(
+ GetPaintController().GetDisplayItemList()[2])
+ .GetPaintRecord();
+ EXPECT_NE(record1, record2);
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ EXPECT_EQ(1u, GetPaintController().PaintChunks().size());
+ // Raster invalidation for the whole chunk will be issued during
+ // PaintArtifactCompositor::Update().
+ EXPECT_TRUE(GetPaintController()
+ .PaintChunks()[0]
+ .raster_invalidation_rects.IsEmpty());
+
+ InitRootChunk();
+ }
+
+ // Draw again with nothing invalidated.
+ EXPECT_TRUE(ClientCacheIsValid(multicol));
+ DrawRect(context, multicol, kBackgroundType, FloatRect(100, 200, 100, 100));
+
+ GetPaintController().BeginSkippingCache();
+ DrawRect(context, content, kForegroundType, rect1);
+ DrawRect(context, content, kForegroundType, rect2);
+ GetPaintController().EndSkippingCache();
+
+ EXPECT_EQ(1, NumCachedNewItems());
+#if DCHECK_IS_ON()
+ EXPECT_EQ(1, NumSequentialMatches());
+ EXPECT_EQ(0, NumOutOfOrderMatches());
+ EXPECT_EQ(0, NumIndexedItems());
+#endif
+
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 3,
+ TestDisplayItem(multicol, kBackgroundType),
+ TestDisplayItem(content, kForegroundType),
+ TestDisplayItem(content, kForegroundType));
+ EXPECT_NE(record1, static_cast<const DrawingDisplayItem&>(
+ GetPaintController().GetDisplayItemList()[1])
+ .GetPaintRecord());
+ EXPECT_NE(record2, static_cast<const DrawingDisplayItem&>(
+ GetPaintController().GetDisplayItemList()[2])
+ .GetPaintRecord());
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ EXPECT_EQ(1u, GetPaintController().PaintChunks().size());
+ EXPECT_THAT(GetPaintController().PaintChunks()[0].raster_invalidation_rects,
+ // Bounds of |content| (old and new are the same);
+ UnorderedElementsAre(FloatRect(100, 100, 100, 100)));
+
+ InitRootChunk();
+ }
+
+ // Now the multicol becomes 3 columns and repaints.
+ multicol.SetDisplayItemsUncached();
+ DrawRect(context, multicol, kBackgroundType, FloatRect(100, 100, 100, 100));
+
+ GetPaintController().BeginSkippingCache();
+ DrawRect(context, content, kForegroundType, rect1);
+ DrawRect(context, content, kForegroundType, rect2);
+ DrawRect(context, content, kForegroundType, rect3);
+ GetPaintController().EndSkippingCache();
+
+ // We should repaint everything on invalidation of the scope container.
+ EXPECT_DISPLAY_LIST(GetPaintController().NewDisplayItemList(), 4,
+ TestDisplayItem(multicol, kBackgroundType),
+ TestDisplayItem(content, kForegroundType),
+ TestDisplayItem(content, kForegroundType),
+ TestDisplayItem(content, kForegroundType));
+ EXPECT_NE(record1, static_cast<const DrawingDisplayItem&>(
+ GetPaintController().NewDisplayItemList()[1])
+ .GetPaintRecord());
+ EXPECT_NE(record2, static_cast<const DrawingDisplayItem&>(
+ GetPaintController().NewDisplayItemList()[2])
+ .GetPaintRecord());
+
+ GetPaintController().CommitNewDisplayItems();
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ EXPECT_EQ(1u, GetPaintController().PaintChunks().size());
+ EXPECT_THAT(GetPaintController().PaintChunks()[0].raster_invalidation_rects,
+ UnorderedElementsAre(
+ // Bounds of |multicol| (old and new are the same);
+ FloatRect(100, 100, 200, 200),
+ // Bounds of |content| (old and new are the same);
+ FloatRect(100, 100, 100, 100)));
+ }
+}
+
+TEST_P(PaintControllerTest, PartialSkipCache) {
+ FakeDisplayItemClient content("content");
+ GraphicsContext context(GetPaintController());
+
+ FloatRect rect1(100, 100, 50, 50);
+ FloatRect rect2(150, 100, 50, 50);
+ FloatRect rect3(200, 100, 50, 50);
+
+ InitRootChunk();
+ DrawRect(context, content, kBackgroundType, rect1);
+ GetPaintController().BeginSkippingCache();
+ DrawRect(context, content, kForegroundType, rect2);
+ GetPaintController().EndSkippingCache();
+ DrawRect(context, content, kForegroundType, rect3);
+
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 3,
+ TestDisplayItem(content, kBackgroundType),
+ TestDisplayItem(content, kForegroundType),
+ TestDisplayItem(content, kForegroundType));
+ sk_sp<const PaintRecord> record0 =
+ static_cast<const DrawingDisplayItem&>(
+ GetPaintController().GetDisplayItemList()[0])
+ .GetPaintRecord();
+ sk_sp<const PaintRecord> record1 =
+ static_cast<const DrawingDisplayItem&>(
+ GetPaintController().GetDisplayItemList()[1])
+ .GetPaintRecord();
+ sk_sp<const PaintRecord> record2 =
+ static_cast<const DrawingDisplayItem&>(
+ GetPaintController().GetDisplayItemList()[2])
+ .GetPaintRecord();
+ EXPECT_NE(record1, record2);
+
+ // Content's cache is invalid because it has display items skipped cache.
+ EXPECT_FALSE(ClientCacheIsValid(content));
+ EXPECT_EQ(PaintInvalidationReason::kFull,
+ content.GetPaintInvalidationReason());
+
+ InitRootChunk();
+ // Draw again with nothing invalidated.
+ DrawRect(context, content, kBackgroundType, rect1);
+ GetPaintController().BeginSkippingCache();
+ DrawRect(context, content, kForegroundType, rect2);
+ GetPaintController().EndSkippingCache();
+ DrawRect(context, content, kForegroundType, rect3);
+
+ EXPECT_EQ(0, NumCachedNewItems());
+#if DCHECK_IS_ON()
+ EXPECT_EQ(0, NumSequentialMatches());
+ EXPECT_EQ(0, NumOutOfOrderMatches());
+ EXPECT_EQ(0, NumIndexedItems());
+#endif
+
+ GetPaintController().CommitNewDisplayItems();
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 3,
+ TestDisplayItem(content, kBackgroundType),
+ TestDisplayItem(content, kForegroundType),
+ TestDisplayItem(content, kForegroundType));
+ EXPECT_NE(record0, static_cast<const DrawingDisplayItem&>(
+ GetPaintController().GetDisplayItemList()[0])
+ .GetPaintRecord());
+ EXPECT_NE(record1, static_cast<const DrawingDisplayItem&>(
+ GetPaintController().GetDisplayItemList()[1])
+ .GetPaintRecord());
+ EXPECT_NE(record2, static_cast<const DrawingDisplayItem&>(
+ GetPaintController().GetDisplayItemList()[2])
+ .GetPaintRecord());
+}
+
+TEST_P(PaintControllerTest, OptimizeNoopPairs) {
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled())
+ return;
+
+ FakeDisplayItemClient first("first");
+ FakeDisplayItemClient second("second");
+ FakeDisplayItemClient third("third");
+
+ GraphicsContext context(GetPaintController());
+ DrawRect(context, first, kBackgroundType, FloatRect(0, 0, 100, 100));
+ {
+ ClipPathRecorder clip_recorder(context, second, Path());
+ DrawRect(context, second, kBackgroundType, FloatRect(0, 0, 100, 100));
+ }
+ DrawRect(context, third, kBackgroundType, FloatRect(0, 0, 100, 100));
+
+ GetPaintController().CommitNewDisplayItems();
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 5,
+ TestDisplayItem(first, kBackgroundType),
+ TestDisplayItem(second, DisplayItem::kBeginClipPath),
+ TestDisplayItem(second, kBackgroundType),
+ TestDisplayItem(second, DisplayItem::kEndClipPath),
+ TestDisplayItem(third, kBackgroundType));
+
+ DrawRect(context, first, kBackgroundType, FloatRect(0, 0, 100, 100));
+ {
+ ClipRecorder clip_recorder(context, second, kClipType, IntRect(1, 1, 2, 2));
+ // Do not draw anything for second.
+ }
+ DrawRect(context, third, kBackgroundType, FloatRect(0, 0, 100, 100));
+ GetPaintController().CommitNewDisplayItems();
+
+ // Empty clips should have been optimized out.
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 2,
+ TestDisplayItem(first, kBackgroundType),
+ TestDisplayItem(third, kBackgroundType));
+
+ second.SetDisplayItemsUncached();
+ DrawRect(context, first, kBackgroundType, FloatRect(0, 0, 100, 100));
+ {
+ ClipRecorder clip_recorder(context, second, kClipType, IntRect(1, 1, 2, 2));
+ {
+ ClipPathRecorder clip_path_recorder(context, second, Path());
+ // Do not draw anything for second.
+ }
+ }
+ DrawRect(context, third, kBackgroundType, FloatRect(0, 0, 100, 100));
+ GetPaintController().CommitNewDisplayItems();
+
+ // Empty clips should have been optimized out.
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 2,
+ TestDisplayItem(first, kBackgroundType),
+ TestDisplayItem(third, kBackgroundType));
+}
+
+TEST_P(PaintControllerTest, SmallPaintControllerHasOnePaintChunk) {
+ ScopedSlimmingPaintV2ForTest enable_s_pv2(true);
+ FakeDisplayItemClient client("test client");
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ GetPaintController().UpdateCurrentPaintChunkProperties(
+ WTF::nullopt, test::DefaultPaintChunkProperties());
+ }
+ GraphicsContext context(GetPaintController());
+ DrawRect(context, client, kBackgroundType, FloatRect(0, 0, 100, 100));
+
+ GetPaintController().CommitNewDisplayItems();
+ const auto& paint_chunks = GetPaintController().PaintChunks();
+ ASSERT_EQ(1u, paint_chunks.size());
+ EXPECT_EQ(0u, paint_chunks[0].begin_index);
+ EXPECT_EQ(1u, paint_chunks[0].end_index);
+}
+
+void DrawPath(GraphicsContext& context,
+ DisplayItemClient& client,
+ DisplayItem::Type type,
+ unsigned count) {
+ if (DrawingRecorder::UseCachedDrawingIfPossible(context, client, type))
+ return;
+
+ DrawingRecorder recorder(context, client, type);
+ SkPath path;
+ path.moveTo(0, 0);
+ path.lineTo(0, 100);
+ path.lineTo(50, 50);
+ path.lineTo(100, 100);
+ path.lineTo(100, 0);
+ path.close();
+ PaintFlags flags;
+ flags.setAntiAlias(true);
+ for (unsigned i = 0; i < count; i++)
+ context.DrawPath(path, flags);
+}
+
+TEST_P(PaintControllerTest, BeginAndEndFrame) {
+ class FakeFrame {};
+
+ // PaintController should have one null frame in the stack since beginning.
+ GetPaintController().SetFirstPainted();
+ FrameFirstPaint result = GetPaintController().EndFrame(nullptr);
+ EXPECT_TRUE(result.first_painted);
+ EXPECT_FALSE(result.text_painted);
+ EXPECT_FALSE(result.image_painted);
+ // Readd the null frame.
+ GetPaintController().BeginFrame(nullptr);
+
+ std::unique_ptr<FakeFrame> frame1(new FakeFrame);
+ GetPaintController().BeginFrame(frame1.get());
+ GetPaintController().SetFirstPainted();
+ GetPaintController().SetTextPainted();
+ GetPaintController().SetImagePainted();
+
+ result = GetPaintController().EndFrame(frame1.get());
+ EXPECT_TRUE(result.first_painted);
+ EXPECT_TRUE(result.text_painted);
+ EXPECT_TRUE(result.image_painted);
+
+ std::unique_ptr<FakeFrame> frame2(new FakeFrame);
+ GetPaintController().BeginFrame(frame2.get());
+ GetPaintController().SetFirstPainted();
+
+ std::unique_ptr<FakeFrame> frame3(new FakeFrame);
+ GetPaintController().BeginFrame(frame3.get());
+ GetPaintController().SetTextPainted();
+ GetPaintController().SetImagePainted();
+
+ result = GetPaintController().EndFrame(frame3.get());
+ EXPECT_FALSE(result.first_painted);
+ EXPECT_TRUE(result.text_painted);
+ EXPECT_TRUE(result.image_painted);
+
+ result = GetPaintController().EndFrame(frame2.get());
+ EXPECT_TRUE(result.first_painted);
+ EXPECT_FALSE(result.text_painted);
+ EXPECT_FALSE(result.image_painted);
+}
+
+TEST_P(PaintControllerTest, PartialInvalidation) {
+ if (!RuntimeEnabledFeatures::SlimmingPaintV175Enabled())
+ return;
+
+ FakeDisplayItemClient client("client", LayoutRect(100, 100, 300, 300));
+ GraphicsContext context(GetPaintController());
+
+ // Test partial rect invalidation in a new chunk.
+ InitRootChunk();
+ client.SetPartialInvalidationRect(LayoutRect(200, 200, 100, 100));
+ DrawRect(context, client, kBackgroundType, FloatRect(100, 100, 300, 300));
+ GetPaintController().CommitNewDisplayItems();
+ ASSERT_EQ(1u, GetPaintController().PaintChunks().size());
+ // Raster invalidation for the whole new chunk will be issued during
+ // PaintArtifactCompositor::Update().
+ EXPECT_TRUE(GetPaintController()
+ .PaintChunks()[0]
+ .raster_invalidation_rects.IsEmpty());
+ EXPECT_EQ(LayoutRect(), client.PartialInvalidationRect());
+
+ // Test partial rect invalidation without other invalidations.
+ InitRootChunk();
+ client.SetPartialInvalidationRect(LayoutRect(150, 160, 170, 180));
+ DrawRect(context, client, kBackgroundType, FloatRect(100, 100, 300, 300));
+ GetPaintController().CommitNewDisplayItems();
+ ASSERT_EQ(1u, GetPaintController().PaintChunks().size());
+ EXPECT_THAT(GetPaintController().PaintChunks()[0].raster_invalidation_rects,
+ // Partial invalidation.
+ UnorderedElementsAre(
+ RuntimeEnabledFeatures::PartialRasterInvalidationEnabled()
+ ? FloatRect(150, 160, 170, 180)
+ : FloatRect(100, 100, 300, 300)));
+ EXPECT_EQ(LayoutRect(), client.PartialInvalidationRect());
+
+ // Test partial rect invalidation with full invalidation.
+ InitRootChunk();
+ client.SetPartialInvalidationRect(LayoutRect(150, 160, 170, 180));
+ client.SetDisplayItemsUncached();
+ DrawRect(context, client, kBackgroundType, FloatRect(100, 100, 300, 300));
+ GetPaintController().CommitNewDisplayItems();
+ ASSERT_EQ(1u, GetPaintController().PaintChunks().size());
+ EXPECT_THAT(GetPaintController().PaintChunks()[0].raster_invalidation_rects,
+ // Partial invalidation is shadowed by full invalidation.
+ UnorderedElementsAre(FloatRect(100, 100, 300, 300)));
+ EXPECT_EQ(LayoutRect(), client.PartialInvalidationRect());
+
+ // Test partial rect invalidation with incremental invalidation.
+ InitRootChunk();
+ client.SetPartialInvalidationRect(LayoutRect(150, 160, 170, 180));
+ client.SetVisualRect(LayoutRect(100, 100, 300, 400));
+ DrawRect(context, client, kBackgroundType, FloatRect(100, 100, 300, 400));
+ GetPaintController().CommitNewDisplayItems();
+ ASSERT_EQ(1u, GetPaintController().PaintChunks().size());
+ if (RuntimeEnabledFeatures::PartialRasterInvalidationEnabled()) {
+ EXPECT_THAT(GetPaintController().PaintChunks()[0].raster_invalidation_rects,
+ // Both partial invalidation and incremental invalidation.
+ UnorderedElementsAre(FloatRect(100, 400, 300, 100),
+ FloatRect(150, 160, 170, 180)));
+ } else {
+ EXPECT_THAT(GetPaintController().PaintChunks()[0].raster_invalidation_rects,
+ UnorderedElementsAre(FloatRect(100, 100, 300, 400)));
+ }
+ EXPECT_EQ(LayoutRect(), client.PartialInvalidationRect());
+}
+
+TEST_P(PaintControllerTest, InvalidateAll) {
+ if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled())
+ return;
+
+ EXPECT_TRUE(GetPaintController().CacheIsAllInvalid());
+ GetPaintController().CommitNewDisplayItems();
+ EXPECT_TRUE(GetPaintController().GetPaintArtifact().IsEmpty());
+ EXPECT_FALSE(GetPaintController().CacheIsAllInvalid());
+
+ InvalidateAll();
+ EXPECT_TRUE(GetPaintController().CacheIsAllInvalid());
+ GetPaintController().CommitNewDisplayItems();
+ EXPECT_TRUE(GetPaintController().GetPaintArtifact().IsEmpty());
+ EXPECT_FALSE(GetPaintController().CacheIsAllInvalid());
+
+ FakeDisplayItemClient client("client", LayoutRect(1, 2, 3, 4));
+ GraphicsContext context(GetPaintController());
+
+ InitRootChunk();
+ DrawRect(context, client, kBackgroundType, FloatRect(1, 2, 3, 4));
+ GetPaintController().CommitNewDisplayItems();
+ EXPECT_FALSE(GetPaintController().GetPaintArtifact().IsEmpty());
+ EXPECT_FALSE(GetPaintController().CacheIsAllInvalid());
+
+ InvalidateAll();
+ EXPECT_TRUE(GetPaintController().GetPaintArtifact().IsEmpty());
+ EXPECT_TRUE(GetPaintController().CacheIsAllInvalid());
+}
+
+// Death tests don't work properly on Android.
+#if defined(GTEST_HAS_DEATH_TEST) && !defined(OS_ANDROID)
+
+class PaintControllerUnderInvalidationTest
+ : public PaintControllerTestBase,
+ private ScopedPaintUnderInvalidationCheckingForTest {
+ public:
+ PaintControllerUnderInvalidationTest()
+ : PaintControllerTestBase(),
+ ScopedPaintUnderInvalidationCheckingForTest(true) {}
+
+ protected:
+ void SetUp() override {
+ testing::FLAGS_gtest_death_test_style = "threadsafe";
+ }
+
+ void TestChangeDrawing() {
+ FakeDisplayItemClient first("first");
+ GraphicsContext context(GetPaintController());
+
+ InitRootChunk();
+ first.SetVisualRect(LayoutRect(100, 100, 300, 300));
+ DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 300, 300));
+ DrawRect(context, first, kForegroundType, FloatRect(100, 100, 300, 300));
+ GetPaintController().CommitNewDisplayItems();
+
+ InitRootChunk();
+ first.SetVisualRect(LayoutRect(200, 200, 300, 300));
+ DrawRect(context, first, kBackgroundType, FloatRect(200, 200, 300, 300));
+ DrawRect(context, first, kForegroundType, FloatRect(100, 100, 300, 300));
+ GetPaintController().CommitNewDisplayItems();
+ }
+
+ void TestMoreDrawing() {
+ FakeDisplayItemClient first("first");
+ GraphicsContext context(GetPaintController());
+
+ InitRootChunk();
+ DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 300, 300));
+ GetPaintController().CommitNewDisplayItems();
+
+ InitRootChunk();
+ DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 300, 300));
+ DrawRect(context, first, kForegroundType, FloatRect(100, 100, 300, 300));
+ GetPaintController().CommitNewDisplayItems();
+ }
+
+ void TestLessDrawing() {
+ FakeDisplayItemClient first("first");
+ GraphicsContext context(GetPaintController());
+
+ InitRootChunk();
+ DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 300, 300));
+ DrawRect(context, first, kForegroundType, FloatRect(100, 100, 300, 300));
+ GetPaintController().CommitNewDisplayItems();
+
+ InitRootChunk();
+ DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 300, 300));
+ GetPaintController().CommitNewDisplayItems();
+ }
+
+ void TestNoopPairsInSubsequence() {
+ EXPECT_FALSE(GetPaintController().LastDisplayItemIsSubsequenceEnd());
+
+ FakeDisplayItemClient container("container");
+ GraphicsContext context(GetPaintController());
+
+ InitRootChunk();
+ {
+ SubsequenceRecorder r(context, container);
+ DrawRect(context, container, kBackgroundType,
+ FloatRect(100, 100, 100, 100));
+ }
+ GetPaintController().CommitNewDisplayItems();
+
+ InitRootChunk();
+ EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+ context, container));
+ {
+ // Generate some no-op pairs which should not affect under-invalidation
+ // checking.
+ ClipRecorder r1(context, container, kClipType, IntRect(1, 1, 9, 9));
+ ClipRecorder r2(context, container, kClipType, IntRect(1, 1, 2, 2));
+ ClipRecorder r3(context, container, kClipType, IntRect(1, 1, 3, 3));
+ ClipPathRecorder r4(context, container, Path());
+ }
+ {
+ EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+ context, container));
+ SubsequenceRecorder r(context, container);
+ DrawRect(context, container, kBackgroundType,
+ FloatRect(100, 100, 100, 100));
+ }
+ EXPECT_TRUE(GetPaintController().LastDisplayItemIsSubsequenceEnd());
+
+ GetPaintController().CommitNewDisplayItems();
+ }
+
+ void TestChangeDrawingInSubsequence() {
+ FakeDisplayItemClient first("first");
+ GraphicsContext context(GetPaintController());
+ InitRootChunk();
+ {
+ SubsequenceRecorder r(context, first);
+ first.SetVisualRect(LayoutRect(100, 100, 300, 300));
+ DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 300, 300));
+ DrawRect(context, first, kForegroundType, FloatRect(100, 100, 300, 300));
+ }
+ GetPaintController().CommitNewDisplayItems();
+
+ InitRootChunk();
+ {
+ EXPECT_FALSE(
+ SubsequenceRecorder::UseCachedSubsequenceIfPossible(context, first));
+ SubsequenceRecorder r(context, first);
+ first.SetVisualRect(LayoutRect(200, 200, 300, 300));
+ DrawRect(context, first, kBackgroundType, FloatRect(200, 200, 300, 300));
+ DrawRect(context, first, kForegroundType, FloatRect(100, 100, 300, 300));
+ }
+ GetPaintController().CommitNewDisplayItems();
+ }
+
+ void TestMoreDrawingInSubsequence() {
+ FakeDisplayItemClient first("first");
+ GraphicsContext context(GetPaintController());
+
+ InitRootChunk();
+ {
+ SubsequenceRecorder r(context, first);
+ DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 300, 300));
+ }
+ GetPaintController().CommitNewDisplayItems();
+
+ InitRootChunk();
+ {
+ EXPECT_FALSE(
+ SubsequenceRecorder::UseCachedSubsequenceIfPossible(context, first));
+ SubsequenceRecorder r(context, first);
+ DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 300, 300));
+ DrawRect(context, first, kForegroundType, FloatRect(100, 100, 300, 300));
+ }
+ GetPaintController().CommitNewDisplayItems();
+ }
+
+ void TestLessDrawingInSubsequence() {
+ FakeDisplayItemClient first("first");
+ GraphicsContext context(GetPaintController());
+
+ InitRootChunk();
+ {
+ SubsequenceRecorder r(context, first);
+ DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 300, 300));
+ DrawRect(context, first, kForegroundType, FloatRect(100, 100, 300, 300));
+ }
+ GetPaintController().CommitNewDisplayItems();
+
+ InitRootChunk();
+ {
+ EXPECT_FALSE(
+ SubsequenceRecorder::UseCachedSubsequenceIfPossible(context, first));
+ SubsequenceRecorder r(context, first);
+ DrawRect(context, first, kBackgroundType, FloatRect(100, 100, 300, 300));
+ }
+ GetPaintController().CommitNewDisplayItems();
+ }
+
+ void TestChangeNonDrawingInSubsequence() {
+ FakeDisplayItemClient container("container");
+ FakeDisplayItemClient content("content");
+ GraphicsContext context(GetPaintController());
+
+ InitRootChunk();
+ {
+ SubsequenceRecorder r(context, container);
+ { ClipPathRecorder clip_path_recorder(context, container, Path()); }
+ ClipRecorder clip(context, container, kClipType, IntRect(1, 1, 9, 9));
+ DrawRect(context, content, kBackgroundType,
+ FloatRect(100, 100, 300, 300));
+ }
+ GetPaintController().CommitNewDisplayItems();
+
+ InitRootChunk();
+ {
+ EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+ context, container));
+ SubsequenceRecorder r(context, container);
+ { ClipPathRecorder clip_path_recorder(context, container, Path()); }
+ ClipRecorder clip(context, container, kClipType, IntRect(1, 1, 30, 30));
+ DrawRect(context, content, kBackgroundType,
+ FloatRect(100, 100, 300, 300));
+ }
+ GetPaintController().CommitNewDisplayItems();
+ }
+
+ void TestInvalidationInSubsequence() {
+ FakeDisplayItemClient container("container");
+ FakeDisplayItemClient content("content");
+ GraphicsContext context(GetPaintController());
+
+ InitRootChunk();
+ {
+ SubsequenceRecorder r(context, container);
+ DrawRect(context, content, kBackgroundType,
+ FloatRect(100, 100, 300, 300));
+ }
+ GetPaintController().CommitNewDisplayItems();
+
+ content.SetDisplayItemsUncached();
+ InitRootChunk();
+ // Leave container not invalidated.
+ {
+ EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+ context, container));
+ SubsequenceRecorder r(context, container);
+ DrawRect(context, content, kBackgroundType,
+ FloatRect(100, 100, 300, 300));
+ }
+ GetPaintController().CommitNewDisplayItems();
+ }
+
+ void TestSubsequenceBecomesEmpty() {
+ FakeDisplayItemClient target("target");
+ GraphicsContext context(GetPaintController());
+
+ InitRootChunk();
+ {
+ SubsequenceRecorder r(context, target);
+ DrawRect(context, target, kBackgroundType, FloatRect(100, 100, 300, 300));
+ }
+ GetPaintController().CommitNewDisplayItems();
+
+ InitRootChunk();
+ {
+ EXPECT_FALSE(
+ SubsequenceRecorder::UseCachedSubsequenceIfPossible(context, target));
+ SubsequenceRecorder r(context, target);
+ }
+ GetPaintController().CommitNewDisplayItems();
+ }
+};
+
+TEST_F(PaintControllerUnderInvalidationTest, ChangeDrawing) {
+ EXPECT_DEATH(TestChangeDrawing(), "under-invalidation: display item changed");
+}
+
+TEST_F(PaintControllerUnderInvalidationTest, MoreDrawing) {
+ // We don't detect under-invalidation in this case, and PaintController can
+ // also handle the case gracefully.
+ TestMoreDrawing();
+}
+
+TEST_F(PaintControllerUnderInvalidationTest, LessDrawing) {
+ // We don't detect under-invalidation in this case, and PaintController can
+ // also handle the case gracefully.
+ TestLessDrawing();
+}
+
+TEST_F(PaintControllerUnderInvalidationTest, NoopPairsInSubsequence) {
+ // This should not die.
+ TestNoopPairsInSubsequence();
+}
+
+TEST_F(PaintControllerUnderInvalidationTest, ChangeDrawingInSubsequence) {
+ EXPECT_DEATH(TestChangeDrawingInSubsequence(),
+ "In cached subsequence for first.*"
+ "under-invalidation: display item changed");
+}
+
+TEST_F(PaintControllerUnderInvalidationTest, MoreDrawingInSubsequence) {
+ // TODO(wangxianzhu): Detect more drawings at the end of a subsequence.
+ TestMoreDrawingInSubsequence();
+}
+
+TEST_F(PaintControllerUnderInvalidationTest, LessDrawingInSubsequence) {
+ EXPECT_DEATH(TestLessDrawingInSubsequence(),
+ "In cached subsequence for first.*"
+ "under-invalidation: new subsequence wrong length");
+}
+
+TEST_F(PaintControllerUnderInvalidationTest, ChangeNonDrawingInSubsequence) {
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled())
+ return;
+ EXPECT_DEATH(TestChangeNonDrawingInSubsequence(),
+ "In cached subsequence for first.*"
+ "under-invalidation: new subsequence wrong length");
+}
+
+TEST_F(PaintControllerUnderInvalidationTest, InvalidationInSubsequence) {
+ // We allow invalidated display item clients as long as they would produce the
+ // same display items. The cases of changed display items are tested by other
+ // test cases.
+ TestInvalidationInSubsequence();
+}
+
+TEST_F(PaintControllerUnderInvalidationTest, SubsequenceBecomesEmpty) {
+ EXPECT_DEATH(TestSubsequenceBecomesEmpty(),
+ "In cached subsequence for target.*"
+ "under-invalidation: new subsequence wrong length");
+}
+
+TEST_F(PaintControllerUnderInvalidationTest, SkipCacheInSubsequence) {
+ FakeDisplayItemClient container("container");
+ FakeDisplayItemClient content("content");
+ GraphicsContext context(GetPaintController());
+
+ InitRootChunk();
+ {
+ SubsequenceRecorder r(context, container);
+ {
+ DisplayItemCacheSkipper cache_skipper(context);
+ DrawRect(context, content, kBackgroundType,
+ FloatRect(100, 100, 300, 300));
+ }
+ DrawRect(context, content, kForegroundType, FloatRect(200, 200, 400, 400));
+ }
+ GetPaintController().CommitNewDisplayItems();
+
+ InitRootChunk();
+ {
+ EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+ context, container));
+ SubsequenceRecorder r(context, container);
+ {
+ DisplayItemCacheSkipper cache_skipper(context);
+ DrawRect(context, content, kBackgroundType,
+ FloatRect(200, 200, 400, 400));
+ }
+ DrawRect(context, content, kForegroundType, FloatRect(200, 200, 400, 400));
+ }
+ GetPaintController().CommitNewDisplayItems();
+}
+
+TEST_F(PaintControllerUnderInvalidationTest,
+ EmptySubsequenceInCachedSubsequence) {
+ FakeDisplayItemClient container("container");
+ FakeDisplayItemClient content("content");
+ GraphicsContext context(GetPaintController());
+
+ InitRootChunk();
+ {
+ SubsequenceRecorder r(context, container);
+ DrawRect(context, container, kBackgroundType,
+ FloatRect(100, 100, 300, 300));
+ { SubsequenceRecorder r1(context, content); }
+ DrawRect(context, container, kForegroundType,
+ FloatRect(100, 100, 300, 300));
+ }
+ GetPaintController().CommitNewDisplayItems();
+
+ InitRootChunk();
+ {
+ EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
+ context, container));
+ SubsequenceRecorder r(context, container);
+ DrawRect(context, container, kBackgroundType,
+ FloatRect(100, 100, 300, 300));
+ EXPECT_FALSE(
+ SubsequenceRecorder::UseCachedSubsequenceIfPossible(context, content));
+ { SubsequenceRecorder r1(context, content); }
+ DrawRect(context, container, kForegroundType,
+ FloatRect(100, 100, 300, 300));
+ }
+ GetPaintController().CommitNewDisplayItems();
+}
+
+TEST_F(PaintControllerUnderInvalidationTest,
+ PairAfterNoopPairInCachedSubsequence) {
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled())
+ return;
+
+ FakeDisplayItemClient client("client");
+ GraphicsContext context(GetPaintController());
+
+ {
+ SubsequenceRecorder subsequence_recorder(context, client);
+ {
+ ClipRecorder clip_recorder(context, client, kClipType,
+ IntRect(100, 100, 50, 50));
+ }
+ {
+ ClipRecorder clip_recorder(context, client, kClipType,
+ IntRect(100, 100, 50, 50));
+ DrawRect(context, client, kBackgroundType, FloatRect(100, 100, 200, 200));
+ }
+ }
+ GetPaintController().CommitNewDisplayItems();
+ EXPECT_DISPLAY_LIST(
+ GetPaintController().GetDisplayItemList(), 3,
+ TestDisplayItem(client, kClipType),
+ TestDisplayItem(client, kBackgroundType),
+ TestDisplayItem(client, DisplayItem::ClipTypeToEndClipType(kClipType)));
+
+ {
+ EXPECT_FALSE(
+ SubsequenceRecorder::UseCachedSubsequenceIfPossible(context, client));
+ SubsequenceRecorder subsequence_recorder(context, client);
+ {
+ ClipRecorder clip_recorder(context, client, kClipType,
+ IntRect(100, 100, 50, 50));
+ }
+ {
+ ClipRecorder clip_recorder(context, client, kClipType,
+ IntRect(100, 100, 50, 50));
+ DrawRect(context, client, kBackgroundType, FloatRect(100, 100, 200, 200));
+ }
+ }
+ GetPaintController().CommitNewDisplayItems();
+}
+
+#endif // defined(GTEST_HAS_DEATH_TEST) && !defined(OS_ANDROID)
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.h b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.h
new file mode 100644
index 00000000000..f5f06c69e3f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.h
@@ -0,0 +1,135 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_CONTROLLER_TEST_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_CONTROLLER_TEST_H_
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/graphics/paint/clip_recorder.h"
+#include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
+#include "third_party/blink/renderer/platform/testing/fake_display_item_client.h"
+#include "third_party/blink/renderer/platform/testing/paint_property_test_helpers.h"
+
+namespace blink {
+
+class GraphicsContext;
+
+class PaintControllerTestBase : public testing::Test {
+ public:
+ PaintControllerTestBase()
+ : root_paint_property_client_("root"),
+ root_paint_chunk_id_(root_paint_property_client_,
+ DisplayItem::kUninitializedType),
+ paint_controller_(PaintController::Create()) {}
+
+ static void DrawNothing(GraphicsContext& context,
+ const DisplayItemClient& client,
+ DisplayItem::Type type) {
+ if (DrawingRecorder::UseCachedDrawingIfPossible(context, client, type))
+ return;
+ DrawingRecorder recorder(context, client, type);
+ }
+
+ template <typename Rect>
+ static void DrawRect(GraphicsContext& context,
+ const DisplayItemClient& client,
+ DisplayItem::Type type,
+ const Rect& bounds) {
+ if (DrawingRecorder::UseCachedDrawingIfPossible(context, client, type))
+ return;
+ DrawingRecorder recorder(context, client, type);
+ context.DrawRect(RoundedIntRect(FloatRect(bounds)));
+ }
+
+ void InitRootChunk() {
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ GetPaintController().UpdateCurrentPaintChunkProperties(
+ root_paint_chunk_id_, test::DefaultPaintChunkProperties());
+ }
+ }
+
+ protected:
+ PaintController& GetPaintController() { return *paint_controller_; }
+
+ int NumCachedNewItems() const {
+ return paint_controller_->num_cached_new_items_;
+ }
+
+#if DCHECK_IS_ON()
+ int NumSequentialMatches() const {
+ return paint_controller_->num_sequential_matches_;
+ }
+ int NumOutOfOrderMatches() const {
+ return paint_controller_->num_out_of_order_matches_;
+ }
+ int NumIndexedItems() const { return paint_controller_->num_indexed_items_; }
+#endif
+
+ void InvalidateAll() { paint_controller_->InvalidateAllForTesting(); }
+
+ using SubsequenceMarkers = PaintController::SubsequenceMarkers;
+ SubsequenceMarkers* GetSubsequenceMarkers(const DisplayItemClient& client) {
+ return paint_controller_->GetSubsequenceMarkers(client);
+ }
+
+ static bool ClientCacheIsValid(const PaintController& paint_controller,
+ const DisplayItemClient& client) {
+ return paint_controller.ClientCacheIsValid(client);
+ }
+
+ bool ClientCacheIsValid(const DisplayItemClient& client) const {
+ return ClientCacheIsValid(*paint_controller_, client);
+ }
+
+ private:
+ FakeDisplayItemClient root_paint_property_client_;
+ PaintChunk::Id root_paint_chunk_id_;
+ std::unique_ptr<PaintController> paint_controller_;
+};
+
+class TestDisplayItem final : public DisplayItem {
+ public:
+ TestDisplayItem(const DisplayItemClient& client, Type type)
+ : DisplayItem(client, type, sizeof(*this)) {}
+
+ void Replay(GraphicsContext&) const final { NOTREACHED(); }
+ void AppendToWebDisplayItemList(const FloatSize&,
+ WebDisplayItemList*) const final {
+ NOTREACHED();
+ }
+};
+
+#define EXPECT_DISPLAY_LIST(actual, expected_size, ...) \
+ do { \
+ EXPECT_EQ((size_t)expected_size, actual.size()); \
+ if (expected_size != actual.size()) \
+ break; \
+ const TestDisplayItem expected[] = {__VA_ARGS__}; \
+ for (size_t i = 0; i < expected_size; ++i) { \
+ SCOPED_TRACE( \
+ String::Format("%d: Expected:(client=%p:\"%s\" type=%d) " \
+ "Actual:(client=%p:%s type=%d)", \
+ (int)i, &expected[i].Client(), \
+ expected[i].Client().DebugName().Ascii().data(), \
+ (int)expected[i].GetType(), &actual[i].Client(), \
+ actual[i].Client().DebugName().Ascii().data(), \
+ (int)actual[i].GetType())); \
+ EXPECT_EQ(&expected[i].Client(), &actual[i].Client()); \
+ EXPECT_EQ(expected[i].GetType(), actual[i].GetType()); \
+ } \
+ } while (false);
+
+// Shorter names for frequently used display item types in tests.
+const DisplayItem::Type kBackgroundType = DisplayItem::kBoxDecorationBackground;
+const DisplayItem::Type kForegroundType =
+ static_cast<DisplayItem::Type>(DisplayItem::kDrawingPaintPhaseFirst + 4);
+const DisplayItem::Type kDocumentBackgroundType =
+ DisplayItem::kDocumentBackground;
+const DisplayItem::Type kScrollHitTestType = DisplayItem::kScrollHitTest;
+const DisplayItem::Type kClipType = DisplayItem::kClipFirst;
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_CONTROLLER_TEST_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_filter.h b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_filter.h
new file mode 100644
index 00000000000..06b8c9e9801
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_filter.h
@@ -0,0 +1,36 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_FILTER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_FILTER_H_
+
+#include "cc/paint/paint_filter.h"
+
+namespace blink {
+using cc::PaintFilter;
+using cc::ColorFilterPaintFilter;
+using cc::BlurPaintFilter;
+using cc::DropShadowPaintFilter;
+using cc::MagnifierPaintFilter;
+using cc::ComposePaintFilter;
+using cc::AlphaThresholdPaintFilter;
+using cc::XfermodePaintFilter;
+using cc::ArithmeticPaintFilter;
+using cc::MatrixConvolutionPaintFilter;
+using cc::DisplacementMapEffectPaintFilter;
+using cc::ImagePaintFilter;
+using cc::RecordPaintFilter;
+using cc::MergePaintFilter;
+using cc::MorphologyPaintFilter;
+using cc::OffsetPaintFilter;
+using cc::TilePaintFilter;
+using cc::TurbulencePaintFilter;
+using cc::PaintFlagsPaintFilter;
+using cc::MatrixPaintFilter;
+using cc::LightingDistantPaintFilter;
+using cc::LightingPointPaintFilter;
+using cc::LightingSpotPaintFilter;
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_FILTER_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_flags.h b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_flags.h
new file mode 100644
index 00000000000..5572d1cc81d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_flags.h
@@ -0,0 +1,14 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_FLAGS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_FLAGS_H_
+
+#include "cc/paint/paint_flags.h"
+
+namespace blink {
+using cc::PaintFlags;
+}
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_FLAGS_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_font.h b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_font.h
new file mode 100644
index 00000000000..f96e00bd8ea
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_font.h
@@ -0,0 +1,14 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_FONT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_FONT_H_
+
+#include "cc/paint/paint_font.h"
+
+namespace blink {
+using cc::PaintFont;
+}
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_FONT_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_image.h b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_image.h
new file mode 100644
index 00000000000..dcdf6691476
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_image.h
@@ -0,0 +1,19 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_IMAGE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_IMAGE_H_
+
+#include "cc/paint/paint_image.h"
+#include "cc/paint/paint_image_builder.h"
+
+namespace blink {
+using cc::FrameMetadata;
+using cc::PaintImage;
+using cc::PaintImageBuilder;
+using cc::PaintImageGenerator;
+using cc::SkiaPaintImageGenerator;
+}
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_IMAGE_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_property_node.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_property_node.cc
new file mode 100644
index 00000000000..5bdf8fe1412
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_property_node.cc
@@ -0,0 +1,93 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/paint_property_node.h"
+
+#include "third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.h"
+#include "third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h"
+#include "third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h"
+#include "third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h"
+
+namespace blink {
+
+namespace {
+
+// Returns -1 if |maybe_ancestor| is found in the ancestor chain, or returns
+// the depth of the node from the root.
+template <typename NodeType>
+int NodeDepthOrFoundAncestor(const NodeType& node,
+ const NodeType& maybe_ancestor) {
+ int depth = 0;
+ for (const NodeType* n = &node; n; n = n->Parent()) {
+ if (n == &maybe_ancestor)
+ return -1;
+ depth++;
+ }
+ return depth;
+}
+
+template <typename NodeType>
+const NodeType& LowestCommonAncestorTemplate(const NodeType& a,
+ const NodeType& b) {
+ // Measure both depths.
+ auto depth_a = NodeDepthOrFoundAncestor(a, b);
+ if (depth_a == -1)
+ return b;
+ auto depth_b = NodeDepthOrFoundAncestor(b, a);
+ if (depth_b == -1)
+ return a;
+
+ const auto* a_ptr = &a;
+ const auto* b_ptr = &b;
+
+ // Make it so depthA >= depthB.
+ if (depth_a < depth_b) {
+ std::swap(a_ptr, b_ptr);
+ std::swap(depth_a, depth_b);
+ }
+
+ // Make it so depthA == depthB.
+ while (depth_a > depth_b) {
+ a_ptr = a_ptr->Parent();
+ depth_a--;
+ }
+
+ // Walk up until we find the ancestor.
+ while (a_ptr != b_ptr) {
+ a_ptr = a_ptr->Parent();
+ b_ptr = b_ptr->Parent();
+ }
+
+ DCHECK(a_ptr) << "Malformed property tree. All nodes must be descendant of "
+ "the same root.";
+ return *a_ptr;
+}
+
+} // namespace
+
+const TransformPaintPropertyNode& LowestCommonAncestorInternal(
+ const TransformPaintPropertyNode& a,
+ const TransformPaintPropertyNode& b) {
+ return LowestCommonAncestorTemplate(a, b);
+}
+
+const ClipPaintPropertyNode& LowestCommonAncestorInternal(
+ const ClipPaintPropertyNode& a,
+ const ClipPaintPropertyNode& b) {
+ return LowestCommonAncestorTemplate(a, b);
+}
+
+const EffectPaintPropertyNode& LowestCommonAncestorInternal(
+ const EffectPaintPropertyNode& a,
+ const EffectPaintPropertyNode& b) {
+ return LowestCommonAncestorTemplate(a, b);
+}
+
+const ScrollPaintPropertyNode& LowestCommonAncestorInternal(
+ const ScrollPaintPropertyNode& a,
+ const ScrollPaintPropertyNode& b) {
+ return LowestCommonAncestorTemplate(a, b);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_property_node.h b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_property_node.h
new file mode 100644
index 00000000000..fa6ea39b181
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_property_node.h
@@ -0,0 +1,210 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_PROPERTY_NODE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_PROPERTY_NODE_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/json/json_values.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+#if DCHECK_IS_ON()
+#include "third_party/blink/renderer/platform/wtf/list_hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+#endif
+
+namespace blink {
+
+class ClipPaintPropertyNode;
+class EffectPaintPropertyNode;
+class ScrollPaintPropertyNode;
+class TransformPaintPropertyNode;
+
+// Returns the lowest common ancestor in the paint property tree.
+template <typename NodeType>
+const NodeType& LowestCommonAncestor(const NodeType& a, const NodeType& b) {
+ // Fast path of common cases.
+ if (&a == &b || !a.Parent() || b.Parent() == &a) {
+ DCHECK(a.IsAncestorOf(b));
+ return a;
+ }
+ if (!b.Parent() || a.Parent() == &b) {
+ DCHECK(b.IsAncestorOf(a));
+ return b;
+ }
+
+ return LowestCommonAncestorInternal(a, b);
+}
+
+PLATFORM_EXPORT const ClipPaintPropertyNode& LowestCommonAncestorInternal(
+ const ClipPaintPropertyNode&,
+ const ClipPaintPropertyNode&);
+PLATFORM_EXPORT const EffectPaintPropertyNode& LowestCommonAncestorInternal(
+ const EffectPaintPropertyNode&,
+ const EffectPaintPropertyNode&);
+PLATFORM_EXPORT const ScrollPaintPropertyNode& LowestCommonAncestorInternal(
+ const ScrollPaintPropertyNode&,
+ const ScrollPaintPropertyNode&);
+PLATFORM_EXPORT const TransformPaintPropertyNode& LowestCommonAncestorInternal(
+ const TransformPaintPropertyNode&,
+ const TransformPaintPropertyNode&);
+
+template <typename NodeType>
+class PaintPropertyNode : public RefCounted<NodeType> {
+ public:
+ // Parent property node, or nullptr if this is the root node.
+ const NodeType* Parent() const { return parent_.get(); }
+ bool IsRoot() const { return !parent_; }
+
+ bool IsAncestorOf(const NodeType& other) const {
+ for (const NodeType* node = &other; node != this; node = node->Parent()) {
+ if (!node)
+ return false;
+ }
+ return true;
+ }
+
+ // TODO(wangxianzhu): Changed() and ClearChangedToRoot() are inefficient
+ // due to the tree walks. Optimize this if this affects overall performance.
+
+ // Returns true if any node (excluding the lowest common ancestor of
+ // |relative_to_node| and |this|) is marked changed along the shortest path
+ // from |this| to |relative_to_node|.
+ bool Changed(const NodeType& relative_to_node) const {
+ if (this == &relative_to_node)
+ return false;
+
+ bool changed = false;
+ for (const auto* n = this; n; n = n->Parent()) {
+ if (n == &relative_to_node)
+ return changed;
+ if (n->changed_)
+ changed = true;
+ }
+
+ // We reach here if |relative_to_node| is not an ancestor of |this|.
+ const auto& lca = LowestCommonAncestor(static_cast<const NodeType&>(*this),
+ relative_to_node);
+ return Changed(lca) || relative_to_node.Changed(lca);
+ }
+
+ void ClearChangedToRoot() const {
+ for (auto* n = this; n; n = n->Parent())
+ n->changed_ = false;
+ }
+
+ String ToString() const {
+ auto s = static_cast<const NodeType*>(this)->ToJSON()->ToJSONString();
+#if DCHECK_IS_ON()
+ return debug_name_ + String::Format(" %p ", this) + s;
+#else
+ return s;
+#endif
+ }
+
+#if DCHECK_IS_ON()
+ String ToTreeString() const;
+
+ String DebugName() const { return debug_name_; }
+ void SetDebugName(const String& name) { debug_name_ = name; }
+#endif
+
+ protected:
+ PaintPropertyNode(scoped_refptr<const NodeType> parent)
+ : parent_(std::move(parent)), changed_(false) {}
+
+ bool Update(scoped_refptr<const NodeType> parent) {
+ DCHECK(!IsRoot());
+ DCHECK(parent != this);
+ if (parent == parent_)
+ return false;
+
+ SetChanged();
+ parent_ = std::move(parent);
+ return true;
+ }
+
+ void SetChanged() { changed_ = true; }
+
+ private:
+ scoped_refptr<const NodeType> parent_;
+ mutable bool changed_;
+
+#if DCHECK_IS_ON()
+ String debug_name_;
+#endif
+};
+
+#if DCHECK_IS_ON()
+
+template <typename NodeType>
+class PropertyTreePrinter {
+ public:
+ void AddNode(const NodeType* node) {
+ if (node)
+ nodes_.insert(node);
+ }
+
+ String NodesAsTreeString() {
+ if (nodes_.IsEmpty())
+ return "";
+ StringBuilder string_builder;
+ BuildTreeString(string_builder, RootNode(), 0);
+ return string_builder.ToString();
+ }
+
+ String PathAsString(const NodeType* last_node) {
+ for (const auto* n = last_node; n; n = n->Parent())
+ AddNode(n);
+ return NodesAsTreeString();
+ }
+
+ private:
+ void BuildTreeString(StringBuilder& string_builder,
+ const NodeType* node,
+ unsigned indent) {
+ DCHECK(node);
+ for (unsigned i = 0; i < indent; i++)
+ string_builder.Append(' ');
+ string_builder.Append(node->ToString());
+ string_builder.Append("\n");
+
+ for (const auto* child_node : nodes_) {
+ if (child_node->Parent() == node)
+ BuildTreeString(string_builder, child_node, indent + 2);
+ }
+ }
+
+ const NodeType* RootNode() {
+ const auto* node = nodes_.back();
+ while (!node->IsRoot())
+ node = node->Parent();
+ if (node->DebugName().IsEmpty())
+ const_cast<NodeType*>(node)->SetDebugName("root");
+ nodes_.insert(node);
+ return node;
+ }
+
+ ListHashSet<const NodeType*> nodes_;
+};
+
+template <typename NodeType>
+String PaintPropertyNode<NodeType>::ToTreeString() const {
+ return PropertyTreePrinter<NodeType>().PathAsString(
+ static_cast<const NodeType*>(this));
+}
+
+#endif // DCHECK_IS_ON()
+
+template <typename NodeType>
+std::ostream& operator<<(std::ostream& os,
+ const PaintPropertyNode<NodeType>& node) {
+ return os << static_cast<const NodeType&>(node).ToString().Utf8().data();
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_PROPERTY_NODE_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_property_node_test.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_property_node_test.cc
new file mode 100644
index 00000000000..b9b51f0e5bd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_property_node_test.cc
@@ -0,0 +1,134 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by node BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/paint_property_node.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/geometry/float_rounded_rect.h"
+#include "third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.h"
+
+namespace blink {
+
+class PaintPropertyNodeTest : public testing::Test {
+ protected:
+ void SetUp() override {
+ root = ClipPaintPropertyNode::Root();
+ node = ClipPaintPropertyNode::Create(root, nullptr, FloatRoundedRect());
+ child1 = ClipPaintPropertyNode::Create(node, nullptr, FloatRoundedRect());
+ child2 = ClipPaintPropertyNode::Create(node, nullptr, FloatRoundedRect());
+ grandchild1 =
+ ClipPaintPropertyNode::Create(child1, nullptr, FloatRoundedRect());
+ grandchild2 =
+ ClipPaintPropertyNode::Create(child2, nullptr, FloatRoundedRect());
+
+ // root
+ // |
+ // node
+ // / \
+ // child1 child2
+ // | |
+ // grandchild1 grandchild2
+ }
+
+ void ResetAllChanged() {
+ grandchild1->ClearChangedToRoot();
+ grandchild2->ClearChangedToRoot();
+ }
+
+ void ExpectInitialState() {
+ EXPECT_FALSE(root->Changed(*root));
+ EXPECT_FALSE(node->Changed(*root));
+ EXPECT_FALSE(child1->Changed(*root));
+ EXPECT_FALSE(child2->Changed(*root));
+ EXPECT_FALSE(grandchild1->Changed(*root));
+ EXPECT_FALSE(grandchild2->Changed(*root));
+ }
+
+ scoped_refptr<ClipPaintPropertyNode> root;
+ scoped_refptr<ClipPaintPropertyNode> node;
+ scoped_refptr<ClipPaintPropertyNode> child1;
+ scoped_refptr<ClipPaintPropertyNode> child2;
+ scoped_refptr<ClipPaintPropertyNode> grandchild1;
+ scoped_refptr<ClipPaintPropertyNode> grandchild2;
+};
+
+TEST_F(PaintPropertyNodeTest, LowestCommonAncestor) {
+ EXPECT_EQ(node, &LowestCommonAncestor(*node, *node));
+ EXPECT_EQ(root, &LowestCommonAncestor(*root, *root));
+
+ EXPECT_EQ(node, &LowestCommonAncestor(*grandchild1, *grandchild2));
+ EXPECT_EQ(node, &LowestCommonAncestor(*grandchild1, *child2));
+ EXPECT_EQ(root, &LowestCommonAncestor(*grandchild1, *root));
+ EXPECT_EQ(child1, &LowestCommonAncestor(*grandchild1, *child1));
+
+ EXPECT_EQ(node, &LowestCommonAncestor(*grandchild2, *grandchild1));
+ EXPECT_EQ(node, &LowestCommonAncestor(*grandchild2, *child1));
+ EXPECT_EQ(root, &LowestCommonAncestor(*grandchild2, *root));
+ EXPECT_EQ(child2, &LowestCommonAncestor(*grandchild2, *child2));
+
+ EXPECT_EQ(node, &LowestCommonAncestor(*child1, *child2));
+ EXPECT_EQ(node, &LowestCommonAncestor(*child2, *child1));
+}
+
+TEST_F(PaintPropertyNodeTest, InitialStateAndReset) {
+ ExpectInitialState();
+ ResetAllChanged();
+ ExpectInitialState();
+}
+
+TEST_F(PaintPropertyNodeTest, ChangeNode) {
+ node->Update(root, nullptr, FloatRoundedRect(1, 2, 3, 4));
+ EXPECT_TRUE(node->Changed(*root));
+ EXPECT_FALSE(node->Changed(*node));
+ EXPECT_TRUE(child1->Changed(*root));
+ EXPECT_FALSE(child1->Changed(*node));
+ EXPECT_TRUE(grandchild1->Changed(*root));
+ EXPECT_FALSE(grandchild1->Changed(*node));
+
+ EXPECT_FALSE(grandchild1->Changed(*child2));
+ EXPECT_FALSE(grandchild1->Changed(*grandchild2));
+
+ ResetAllChanged();
+ ExpectInitialState();
+}
+
+TEST_F(PaintPropertyNodeTest, ChangeOneChild) {
+ child1->Update(node, nullptr, FloatRoundedRect(1, 2, 3, 4));
+ EXPECT_FALSE(node->Changed(*root));
+ EXPECT_FALSE(node->Changed(*node));
+ EXPECT_TRUE(child1->Changed(*root));
+ EXPECT_TRUE(child1->Changed(*node));
+ EXPECT_TRUE(grandchild1->Changed(*node));
+ EXPECT_FALSE(grandchild1->Changed(*child1));
+ EXPECT_FALSE(child2->Changed(*node));
+ EXPECT_FALSE(grandchild2->Changed(*node));
+
+ EXPECT_TRUE(child2->Changed(*child1));
+ EXPECT_TRUE(child1->Changed(*child2));
+ EXPECT_TRUE(child2->Changed(*grandchild1));
+ EXPECT_TRUE(child1->Changed(*grandchild2));
+ EXPECT_TRUE(grandchild1->Changed(*child2));
+ EXPECT_TRUE(grandchild1->Changed(*grandchild2));
+ EXPECT_TRUE(grandchild2->Changed(*child1));
+ EXPECT_TRUE(grandchild2->Changed(*grandchild1));
+
+ ResetAllChanged();
+ ExpectInitialState();
+}
+
+TEST_F(PaintPropertyNodeTest, Reparent) {
+ child1->Update(child2, nullptr, FloatRoundedRect(1, 2, 3, 4));
+ EXPECT_FALSE(node->Changed(*root));
+ EXPECT_TRUE(child1->Changed(*node));
+ EXPECT_TRUE(child1->Changed(*child2));
+ EXPECT_FALSE(child2->Changed(*node));
+ EXPECT_TRUE(grandchild1->Changed(*node));
+ EXPECT_FALSE(grandchild1->Changed(*child1));
+ EXPECT_TRUE(grandchild1->Changed(*child2));
+
+ ResetAllChanged();
+ ExpectInitialState();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_record.h b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_record.h
new file mode 100644
index 00000000000..c8a251ba60b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_record.h
@@ -0,0 +1,15 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_RECORD_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_RECORD_H_
+
+#include "cc/paint/paint_record.h"
+
+namespace blink {
+using cc::PaintRecord;
+using cc::ToSkPicture;
+}
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_RECORD_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_record_builder.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_record_builder.cc
new file mode 100644
index 00000000000..97b549c0a19
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_record_builder.cc
@@ -0,0 +1,69 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/paint_record_builder.h"
+
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
+
+namespace blink {
+
+PaintRecordBuilder::PaintRecordBuilder(SkMetaData* meta_data,
+ GraphicsContext* containing_context,
+ PaintController* paint_controller)
+ : paint_controller_(nullptr) {
+ GraphicsContext::DisabledMode disabled_mode =
+ GraphicsContext::kNothingDisabled;
+ if (containing_context && containing_context->ContextDisabled())
+ disabled_mode = GraphicsContext::kFullyDisabled;
+
+ if (paint_controller) {
+ paint_controller_ = paint_controller;
+ } else {
+ own_paint_controller_ = PaintController::Create();
+ paint_controller_ = own_paint_controller_.get();
+ }
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ paint_controller_->UpdateCurrentPaintChunkProperties(
+ WTF::nullopt, PaintChunkProperties(PropertyTreeState::Root()));
+ }
+
+ const HighContrastSettings* high_contrast_settings =
+ containing_context ? &containing_context->high_contrast_settings()
+ : nullptr;
+ context_ = std::make_unique<GraphicsContext>(*paint_controller_,
+ disabled_mode, meta_data);
+ if (high_contrast_settings)
+ context_->SetHighContrast(*high_contrast_settings);
+
+ if (containing_context) {
+ context_->SetDeviceScaleFactor(containing_context->DeviceScaleFactor());
+ context_->SetPrinting(containing_context->Printing());
+ }
+
+ if (!paint_controller)
+ cache_skipper_.emplace(*context_);
+}
+
+sk_sp<PaintRecord> PaintRecordBuilder::EndRecording(
+ const PropertyTreeState& replay_state) {
+ context_->BeginRecording(FloatRect());
+ paint_controller_->CommitNewDisplayItems();
+ paint_controller_->GetPaintArtifact().Replay(*context_, replay_state);
+ return context_->EndRecording();
+}
+
+void PaintRecordBuilder::EndRecording(PaintCanvas& canvas,
+ const PropertyTreeState& replay_state) {
+ if (!RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ canvas.drawPicture(EndRecording());
+ } else {
+ paint_controller_->CommitNewDisplayItems();
+ paint_controller_->GetPaintArtifact().Replay(canvas, replay_state);
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_record_builder.h b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_record_builder.h
new file mode 100644
index 00000000000..744bb330022
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_record_builder.h
@@ -0,0 +1,75 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_RECORD_BUILDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_RECORD_BUILDER_H_
+
+#include <memory>
+
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/graphics/paint/display_item_cache_skipper.h"
+#include "third_party/blink/renderer/platform/graphics/paint/display_item_client.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_record.h"
+#include "third_party/blink/renderer/platform/graphics/paint/property_tree_state.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+class SkMetaData;
+
+namespace blink {
+
+class GraphicsContext;
+class PaintController;
+
+// TODO(enne): rename this class to not be named SkPicture
+// When slimming paint ships we can remove this PaintRecord abstraction and
+// rely on PaintController here.
+class PLATFORM_EXPORT PaintRecordBuilder final : public DisplayItemClient {
+ WTF_MAKE_NONCOPYABLE(PaintRecordBuilder);
+
+ public:
+ // Constructs a new builder for the resulting recorded picture. If |metadata|
+ // is specified, that metadata is propagated to the builder's internal canvas.
+ // If |containing_context| is specified, the device scale factor, printing,
+ // and disabled state are propagated to the builder's internal context.
+ // If a PaintController is passed, it is used as the PaintController for
+ // painting the picture (and hence we can use its cache). Otherwise, a new
+ // PaintController is used for the duration of the picture building, which
+ // therefore has no caching.
+ // In SPv175+ mode, resets paint chunks to PropertyTreeState::Root()
+ // before beginning to record.
+ PaintRecordBuilder(SkMetaData* = nullptr,
+ GraphicsContext* containing_context = nullptr,
+ PaintController* = nullptr);
+
+ GraphicsContext& Context() { return *context_; }
+
+ // Returns a PaintRecord capturing all drawing performed on the builder's
+ // context since construction.
+ // In SPv175+ mode, replays into the ancestor state given by |replay_state|.
+ sk_sp<PaintRecord> EndRecording(
+ const PropertyTreeState& replay_state = PropertyTreeState::Root());
+
+ // Replays the recording directly into the given canvas.
+ // In SPv175+ mode, replays into the ancestor state given by |replay_state|.
+ void EndRecording(
+ PaintCanvas&,
+ const PropertyTreeState& replay_state = PropertyTreeState::Root());
+
+ // DisplayItemClient methods
+ String DebugName() const final { return "PaintRecordBuilder"; }
+ LayoutRect VisualRect() const final { return LayoutRect(); }
+
+ private:
+ PaintController* paint_controller_;
+ std::unique_ptr<PaintController> own_paint_controller_;
+ std::unique_ptr<GraphicsContext> context_;
+ Optional<DisplayItemCacheSkipper> cache_skipper_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_RECORD_BUILDER_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_record_builder_test.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_record_builder_test.cc
new file mode 100644
index 00000000000..df7646149b2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_record_builder_test.cc
@@ -0,0 +1,105 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/paint_record_builder.h"
+
+#include "third_party/blink/renderer/platform/graphics/paint/paint_controller_test.h"
+#include "third_party/blink/renderer/platform/graphics/test/mock_paint_canvas.h"
+#include "third_party/blink/renderer/platform/testing/fake_display_item_client.h"
+
+using testing::_;
+
+namespace blink {
+
+using PaintRecordBuilderTest = PaintControllerTestBase;
+
+TEST_F(PaintRecordBuilderTest, TransientPaintController) {
+ PaintRecordBuilder builder;
+ auto& context = builder.Context();
+ FakeDisplayItemClient client("client", LayoutRect(10, 10, 20, 20));
+ DrawRect(context, client, kBackgroundType, FloatRect(10, 10, 20, 20));
+ DrawRect(context, client, kForegroundType, FloatRect(15, 15, 10, 10));
+ EXPECT_FALSE(ClientCacheIsValid(context.GetPaintController(), client));
+
+ MockPaintCanvas canvas;
+ PaintFlags flags;
+ EXPECT_CALL(canvas, drawPicture(_)).Times(1);
+ builder.EndRecording(canvas);
+
+ EXPECT_DISPLAY_LIST(context.GetPaintController().GetDisplayItemList(), 2,
+ TestDisplayItem(client, kBackgroundType),
+ TestDisplayItem(client, kForegroundType));
+ EXPECT_FALSE(ClientCacheIsValid(context.GetPaintController(), client));
+}
+
+TEST_F(PaintRecordBuilderTest, LastingPaintController) {
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ GetPaintController().UpdateCurrentPaintChunkProperties(
+ WTF::nullopt, PaintChunkProperties(PropertyTreeState::Root()));
+ }
+
+ PaintRecordBuilder builder(nullptr, nullptr, &GetPaintController());
+ auto& context = builder.Context();
+ EXPECT_EQ(&context.GetPaintController(), &GetPaintController());
+
+ FakeDisplayItemClient client("client", LayoutRect(10, 10, 20, 20));
+ DrawRect(context, client, kBackgroundType, FloatRect(10, 10, 20, 20));
+ DrawRect(context, client, kForegroundType, FloatRect(15, 15, 10, 10));
+ EXPECT_FALSE(ClientCacheIsValid(client));
+
+ MockPaintCanvas canvas;
+ PaintFlags flags;
+ EXPECT_CALL(canvas, drawPicture(_)).Times(1);
+ builder.EndRecording(canvas);
+ EXPECT_TRUE(ClientCacheIsValid(client));
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 2,
+ TestDisplayItem(client, kBackgroundType),
+ TestDisplayItem(client, kForegroundType));
+
+ if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
+ GetPaintController().UpdateCurrentPaintChunkProperties(
+ WTF::nullopt, PaintChunkProperties(PropertyTreeState::Root()));
+ }
+
+ EXPECT_TRUE(DrawingRecorder::UseCachedDrawingIfPossible(context, client,
+ kBackgroundType));
+ EXPECT_TRUE(DrawingRecorder::UseCachedDrawingIfPossible(context, client,
+ kForegroundType));
+ EXPECT_CALL(canvas, drawPicture(_)).Times(1);
+ builder.EndRecording(canvas);
+
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 2,
+ TestDisplayItem(client, kBackgroundType),
+ TestDisplayItem(client, kForegroundType));
+ EXPECT_TRUE(ClientCacheIsValid(client));
+}
+
+TEST_F(PaintRecordBuilderTest, TransientAndAnotherPaintController) {
+ GraphicsContext context(GetPaintController());
+
+ InitRootChunk();
+ FakeDisplayItemClient client("client", LayoutRect(10, 10, 20, 20));
+ DrawRect(context, client, kBackgroundType, FloatRect(10, 10, 20, 20));
+ DrawRect(context, client, kForegroundType, FloatRect(15, 15, 10, 10));
+ GetPaintController().CommitNewDisplayItems();
+ EXPECT_DISPLAY_LIST(GetPaintController().GetDisplayItemList(), 2,
+ TestDisplayItem(client, kBackgroundType),
+ TestDisplayItem(client, kForegroundType));
+ EXPECT_TRUE(ClientCacheIsValid(client));
+
+ PaintRecordBuilder builder;
+ EXPECT_NE(&builder.Context().GetPaintController(), &GetPaintController());
+ DrawRect(builder.Context(), client, kBackgroundType,
+ FloatRect(10, 10, 20, 20));
+ builder.EndRecording();
+
+ // The transient PaintController in PaintRecordBuilder doesn't affect the
+ // client's cache status in another PaintController.
+ EXPECT_TRUE(ClientCacheIsValid(client));
+ EXPECT_FALSE(
+ ClientCacheIsValid(builder.Context().GetPaintController(), client));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_recorder.h b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_recorder.h
new file mode 100644
index 00000000000..89d61d37de6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_recorder.h
@@ -0,0 +1,14 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_RECORDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_RECORDER_H_
+
+#include "cc/paint/paint_recorder.h"
+
+namespace blink {
+using cc::PaintRecorder;
+}
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_RECORDER_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_shader.h b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_shader.h
new file mode 100644
index 00000000000..2b9af057724
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_shader.h
@@ -0,0 +1,14 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_SHADER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_SHADER_H_
+
+#include "cc/paint/paint_shader.h"
+
+namespace blink {
+using cc::PaintShader;
+}
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_SHADER_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_text_blob.h b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_text_blob.h
new file mode 100644
index 00000000000..9f0d9892e61
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_text_blob.h
@@ -0,0 +1,16 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_TEXT_BLOB_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_TEXT_BLOB_H_
+
+#include "cc/paint/paint_text_blob.h"
+#include "cc/paint/paint_text_blob_builder.h"
+
+namespace blink {
+using cc::PaintTextBlob;
+using cc::PaintTextBlobBuilder;
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_TEXT_BLOB_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_typeface.h b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_typeface.h
new file mode 100644
index 00000000000..9e0bc45508e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_typeface.h
@@ -0,0 +1,14 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_TYPEFACE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_TYPEFACE_H_
+
+#include "cc/paint/paint_typeface.h"
+
+namespace blink {
+using cc::PaintTypeface;
+}
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_TYPEFACE_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/property_tree_state.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/property_tree_state.cc
new file mode 100644
index 00000000000..096ddf9f3a5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/property_tree_state.cc
@@ -0,0 +1,57 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/property_tree_state.h"
+
+#include <memory>
+
+namespace blink {
+
+const PropertyTreeState& PropertyTreeState::Root() {
+ DEFINE_STATIC_LOCAL(
+ std::unique_ptr<PropertyTreeState>, root,
+ (std::make_unique<PropertyTreeState>(TransformPaintPropertyNode::Root(),
+ ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())));
+ return *root;
+}
+
+const CompositorElementId PropertyTreeState::GetCompositorElementId(
+ const CompositorElementIdSet& element_ids) const {
+ // The effect or transform nodes could have a compositor element id. The order
+ // doesn't matter as the element id should be the same on all that have a
+ // non-default CompositorElementId.
+ //
+ // Note that PropertyTreeState acts as a context that accumulates state as we
+ // traverse the tree building layers. This means that we could see a
+ // compositor element id 'A' for a parent layer in conjunction with a
+ // compositor element id 'B' for a child layer. To preserve uniqueness of
+ // element ids, then, we check for presence in the |element_ids| set (which
+ // represents element ids already previously attached to a layer). This is an
+ // interim step while we pursue broader rework of animation subsystem noted in
+ // http://crbug.com/709137.
+ if (Effect()->GetCompositorElementId() &&
+ !element_ids.Contains(Effect()->GetCompositorElementId()))
+ return Effect()->GetCompositorElementId();
+ if (Transform()->GetCompositorElementId() &&
+ !element_ids.Contains(Transform()->GetCompositorElementId()))
+ return Transform()->GetCompositorElementId();
+ return CompositorElementId();
+}
+
+String PropertyTreeState::ToString() const {
+ return String::Format("t:%p c:%p e:%p", Transform(), Clip(), Effect());
+}
+
+#if DCHECK_IS_ON()
+
+String PropertyTreeState::ToTreeString() const {
+ return "transform:\n" + (Transform() ? Transform()->ToTreeString() : "null") +
+ "\nclip:\n" + (Clip() ? Clip()->ToTreeString() : "null") +
+ "\neffect:\n" + (Effect() ? Effect()->ToTreeString() : "null");
+}
+
+#endif
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/property_tree_state.h b/chromium/third_party/blink/renderer/platform/graphics/paint/property_tree_state.h
new file mode 100644
index 00000000000..55650c5cf7b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/property_tree_state.h
@@ -0,0 +1,78 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PROPERTY_TREE_STATE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PROPERTY_TREE_STATE_H_
+
+#include "third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.h"
+#include "third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h"
+#include "third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h"
+#include "third_party/blink/renderer/platform/wtf/hash_functions.h"
+#include "third_party/blink/renderer/platform/wtf/hash_traits.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+
+namespace blink {
+
+// A complete set of paint properties including those that are inherited from
+// other objects.
+class PLATFORM_EXPORT PropertyTreeState {
+ USING_FAST_MALLOC(PropertyTreeState);
+
+ public:
+ PropertyTreeState(const TransformPaintPropertyNode* transform,
+ const ClipPaintPropertyNode* clip,
+ const EffectPaintPropertyNode* effect)
+ : transform_(transform), clip_(clip), effect_(effect) {}
+
+ bool HasDirectCompositingReasons() const;
+
+ const TransformPaintPropertyNode* Transform() const { return transform_; }
+ void SetTransform(const TransformPaintPropertyNode* node) {
+ transform_ = node;
+ }
+
+ const ClipPaintPropertyNode* Clip() const { return clip_; }
+ void SetClip(const ClipPaintPropertyNode* node) { clip_ = node; }
+
+ const EffectPaintPropertyNode* Effect() const { return effect_; }
+ void SetEffect(const EffectPaintPropertyNode* node) { effect_ = node; }
+
+ static const PropertyTreeState& Root();
+
+ // Returns the compositor element id, if any, for this property state. If
+ // neither the effect nor transform nodes have a compositor element id then a
+ // default instance is returned.
+ const CompositorElementId GetCompositorElementId(
+ const CompositorElementIdSet& element_ids) const;
+
+ void ClearChangedToRoot() const {
+ Transform()->ClearChangedToRoot();
+ Clip()->ClearChangedToRoot();
+ Effect()->ClearChangedToRoot();
+ }
+
+ String ToString() const;
+#if DCHECK_IS_ON()
+ // Dumps the tree from this state up to the root as a string.
+ String ToTreeString() const;
+#endif
+
+ private:
+ const TransformPaintPropertyNode* transform_;
+ const ClipPaintPropertyNode* clip_;
+ const EffectPaintPropertyNode* effect_;
+};
+
+inline bool operator==(const PropertyTreeState& a, const PropertyTreeState& b) {
+ return a.Transform() == b.Transform() && a.Clip() == b.Clip() &&
+ a.Effect() == b.Effect();
+}
+
+inline bool operator!=(const PropertyTreeState& a, const PropertyTreeState& b) {
+ return !(a == b);
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PROPERTY_TREE_STATE_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/property_tree_state_test.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/property_tree_state_test.cc
new file mode 100644
index 00000000000..d6850e89221
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/property_tree_state_test.cc
@@ -0,0 +1,95 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/property_tree_state.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+class PropertyTreeStateTest : public testing::Test {};
+
+TEST_F(PropertyTreeStateTest, CompositorElementIdNoElementIdOnAnyNode) {
+ PropertyTreeState state(TransformPaintPropertyNode::Root(),
+ ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root());
+ EXPECT_EQ(CompositorElementId(),
+ state.GetCompositorElementId(CompositorElementIdSet()));
+}
+
+TEST_F(PropertyTreeStateTest, CompositorElementIdWithElementIdOnTransformNode) {
+ CompositorElementId expected_compositor_element_id = CompositorElementId(2);
+ scoped_refptr<TransformPaintPropertyNode> transform =
+ TransformPaintPropertyNode::Create(TransformPaintPropertyNode::Root(),
+ TransformationMatrix(), FloatPoint3D(),
+ false, 0, CompositingReason::kNone,
+ expected_compositor_element_id);
+ PropertyTreeState state(transform.get(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root());
+ EXPECT_EQ(expected_compositor_element_id,
+ state.GetCompositorElementId(CompositorElementIdSet()));
+}
+
+TEST_F(PropertyTreeStateTest, CompositorElementIdWithElementIdOnEffectNode) {
+ CompositorElementId expected_compositor_element_id = CompositorElementId(2);
+ scoped_refptr<EffectPaintPropertyNode> effect =
+ EffectPaintPropertyNode::Create(
+ EffectPaintPropertyNode::Root(), TransformPaintPropertyNode::Root(),
+ ClipPaintPropertyNode::Root(), kColorFilterNone,
+ CompositorFilterOperations(), 1.0, SkBlendMode::kSrcOver,
+ CompositingReason::kNone, expected_compositor_element_id);
+ PropertyTreeState state(TransformPaintPropertyNode::Root(),
+ ClipPaintPropertyNode::Root(), effect.get());
+ EXPECT_EQ(expected_compositor_element_id,
+ state.GetCompositorElementId(CompositorElementIdSet()));
+}
+
+TEST_F(PropertyTreeStateTest, CompositorElementIdWithElementIdOnMultipleNodes) {
+ CompositorElementId expected_compositor_element_id = CompositorElementId(2);
+ scoped_refptr<TransformPaintPropertyNode> transform =
+ TransformPaintPropertyNode::Create(TransformPaintPropertyNode::Root(),
+ TransformationMatrix(), FloatPoint3D(),
+ false, 0, CompositingReason::kNone,
+ expected_compositor_element_id);
+ scoped_refptr<EffectPaintPropertyNode> effect =
+ EffectPaintPropertyNode::Create(
+ EffectPaintPropertyNode::Root(), TransformPaintPropertyNode::Root(),
+ ClipPaintPropertyNode::Root(), kColorFilterNone,
+ CompositorFilterOperations(), 1.0, SkBlendMode::kSrcOver,
+ CompositingReason::kNone, expected_compositor_element_id);
+ PropertyTreeState state(transform.get(), ClipPaintPropertyNode::Root(),
+ effect.get());
+ EXPECT_EQ(expected_compositor_element_id,
+ state.GetCompositorElementId(CompositorElementIdSet()));
+}
+
+TEST_F(PropertyTreeStateTest, CompositorElementIdWithDifferingElementIds) {
+ CompositorElementId first_compositor_element_id = CompositorElementId(2);
+ CompositorElementId second_compositor_element_id = CompositorElementId(3);
+ scoped_refptr<TransformPaintPropertyNode> transform =
+ TransformPaintPropertyNode::Create(TransformPaintPropertyNode::Root(),
+ TransformationMatrix(), FloatPoint3D(),
+ false, 0, CompositingReason::kNone,
+ first_compositor_element_id);
+ scoped_refptr<EffectPaintPropertyNode> effect =
+ EffectPaintPropertyNode::Create(
+ EffectPaintPropertyNode::Root(), TransformPaintPropertyNode::Root(),
+ ClipPaintPropertyNode::Root(), kColorFilterNone,
+ CompositorFilterOperations(), 1.0, SkBlendMode::kSrcOver,
+ CompositingReason::kNone, second_compositor_element_id);
+ PropertyTreeState state(transform.get(), ClipPaintPropertyNode::Root(),
+ effect.get());
+
+ CompositorElementIdSet composited_element_ids;
+ composited_element_ids.insert(first_compositor_element_id);
+ EXPECT_EQ(second_compositor_element_id,
+ state.GetCompositorElementId(composited_element_ids));
+
+ composited_element_ids.clear();
+ composited_element_ids.insert(second_compositor_element_id);
+ EXPECT_EQ(first_compositor_element_id,
+ state.GetCompositorElementId(composited_element_ids));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/raster_invalidation_tracking.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/raster_invalidation_tracking.cc
new file mode 100644
index 00000000000..a5a9e132de1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/raster_invalidation_tracking.cc
@@ -0,0 +1,208 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/raster_invalidation_tracking.h"
+
+#include "SkImageFilter.h"
+#include "third_party/blink/renderer/platform/geometry/geometry_as_json.h"
+#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
+#include "third_party/blink/renderer/platform/graphics/color.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_recorder.h"
+
+namespace blink {
+
+static bool g_simulate_raster_under_invalidations = false;
+
+void RasterInvalidationTracking::SimulateRasterUnderInvalidations(bool enable) {
+ g_simulate_raster_under_invalidations = enable;
+}
+
+void RasterInvalidationTracking::AddInvalidation(
+ const DisplayItemClient* client,
+ const String& debug_name,
+ const IntRect& rect,
+ PaintInvalidationReason reason) {
+ if (rect.IsEmpty())
+ return;
+
+ RasterInvalidationInfo info;
+ info.client = client;
+ info.client_debug_name = debug_name;
+ info.rect = rect;
+ info.reason = reason;
+ invalidations_.push_back(info);
+
+ // TODO(crbug.com/496260): Some antialiasing effects overflow the paint
+ // invalidation rect.
+ IntRect r = rect;
+ r.Inflate(1);
+ invalidation_region_since_last_paint_.Unite(r);
+}
+
+static bool CompareRasterInvalidationInfo(const RasterInvalidationInfo& a,
+ const RasterInvalidationInfo& b) {
+ // Sort by rect first, bigger rects before smaller ones.
+ if (a.rect.Width() != b.rect.Width())
+ return a.rect.Width() > b.rect.Width();
+ if (a.rect.Height() != b.rect.Height())
+ return a.rect.Height() > b.rect.Height();
+ if (a.rect.X() != b.rect.X())
+ return a.rect.X() > b.rect.X();
+ if (a.rect.Y() != b.rect.Y())
+ return a.rect.Y() > b.rect.Y();
+
+ // Then compare clientDebugName, in alphabetic order.
+ int name_compare_result =
+ CodePointCompare(a.client_debug_name, b.client_debug_name);
+ if (name_compare_result != 0)
+ return name_compare_result < 0;
+
+ return a.reason < b.reason;
+}
+
+void RasterInvalidationTracking::AsJSON(JSONObject* json) {
+ if (!invalidations_.IsEmpty()) {
+ std::sort(invalidations_.begin(), invalidations_.end(),
+ &CompareRasterInvalidationInfo);
+ std::unique_ptr<JSONArray> paint_invalidations_json = JSONArray::Create();
+ for (auto& info : invalidations_) {
+ std::unique_ptr<JSONObject> info_json = JSONObject::Create();
+ info_json->SetString("object", info.client_debug_name);
+ if (!info.rect.IsEmpty()) {
+ if (info.rect == LayoutRect::InfiniteIntRect())
+ info_json->SetString("rect", "infinite");
+ else
+ info_json->SetArray("rect", RectAsJSONArray(info.rect));
+ }
+ info_json->SetString("reason",
+ PaintInvalidationReasonToString(info.reason));
+ paint_invalidations_json->PushObject(std::move(info_json));
+ }
+ json->SetArray("paintInvalidations", std::move(paint_invalidations_json));
+ }
+
+ if (!under_invalidations_.IsEmpty()) {
+ std::unique_ptr<JSONArray> under_paint_invalidations_json =
+ JSONArray::Create();
+ for (auto& under_paint_invalidation : under_invalidations_) {
+ std::unique_ptr<JSONObject> under_paint_invalidation_json =
+ JSONObject::Create();
+ under_paint_invalidation_json->SetDouble("x", under_paint_invalidation.x);
+ under_paint_invalidation_json->SetDouble("y", under_paint_invalidation.y);
+ under_paint_invalidation_json->SetString(
+ "oldPixel",
+ Color(under_paint_invalidation.old_pixel).NameForLayoutTreeAsText());
+ under_paint_invalidation_json->SetString(
+ "newPixel",
+ Color(under_paint_invalidation.new_pixel).NameForLayoutTreeAsText());
+ under_paint_invalidations_json->PushObject(
+ std::move(under_paint_invalidation_json));
+ }
+ json->SetArray("underPaintInvalidations",
+ std::move(under_paint_invalidations_json));
+ }
+}
+
+static bool PixelComponentsDiffer(int c1, int c2) {
+ // Compare strictly for saturated values.
+ if (c1 == 0 || c1 == 255 || c2 == 0 || c2 == 255)
+ return c1 != c2;
+ // Tolerate invisible differences that may occur in gradients etc.
+ return abs(c1 - c2) > 2;
+}
+
+static bool PixelsDiffer(SkColor p1, SkColor p2) {
+ return PixelComponentsDiffer(SkColorGetA(p1), SkColorGetA(p2)) ||
+ PixelComponentsDiffer(SkColorGetR(p1), SkColorGetR(p2)) ||
+ PixelComponentsDiffer(SkColorGetG(p1), SkColorGetG(p2)) ||
+ PixelComponentsDiffer(SkColorGetB(p1), SkColorGetB(p2));
+}
+
+void RasterInvalidationTracking::CheckUnderInvalidations(
+ const String& layer_debug_name,
+ sk_sp<PaintRecord> new_record,
+ const IntRect& new_interest_rect) {
+ auto old_interest_rect = last_interest_rect_;
+ Region invalidation_region;
+ if (!g_simulate_raster_under_invalidations)
+ invalidation_region = invalidation_region_since_last_paint_;
+ auto old_record = std::move(last_painted_record_);
+
+ last_painted_record_ = new_record;
+ last_interest_rect_ = new_interest_rect;
+ invalidation_region_since_last_paint_ = Region();
+
+ if (!old_record)
+ return;
+
+ IntRect rect = Intersection(old_interest_rect, new_interest_rect);
+ // Avoid too big area as the following code is slow.
+ rect.Intersect(IntRect(rect.X(), rect.Y(), 1200, 6000));
+ if (rect.IsEmpty())
+ return;
+
+ SkBitmap old_bitmap;
+ old_bitmap.allocPixels(
+ SkImageInfo::MakeN32Premul(rect.Width(), rect.Height()));
+ {
+ SkiaPaintCanvas canvas(old_bitmap);
+ canvas.clear(SK_ColorTRANSPARENT);
+ canvas.translate(-rect.X(), -rect.Y());
+ canvas.drawPicture(std::move(old_record));
+ }
+
+ SkBitmap new_bitmap;
+ new_bitmap.allocPixels(
+ SkImageInfo::MakeN32Premul(rect.Width(), rect.Height()));
+ {
+ SkiaPaintCanvas canvas(new_bitmap);
+ canvas.clear(SK_ColorTRANSPARENT);
+ canvas.translate(-rect.X(), -rect.Y());
+ canvas.drawPicture(std::move(new_record));
+ }
+
+ int mismatching_pixels = 0;
+ static const int kMaxMismatchesToReport = 50;
+ for (int bitmap_y = 0; bitmap_y < rect.Height(); ++bitmap_y) {
+ int layer_y = bitmap_y + rect.Y();
+ for (int bitmap_x = 0; bitmap_x < rect.Width(); ++bitmap_x) {
+ int layer_x = bitmap_x + rect.X();
+ SkColor old_pixel = old_bitmap.getColor(bitmap_x, bitmap_y);
+ SkColor new_pixel = new_bitmap.getColor(bitmap_x, bitmap_y);
+ if (PixelsDiffer(old_pixel, new_pixel) &&
+ !invalidation_region.Contains(IntPoint(layer_x, layer_y))) {
+ if (mismatching_pixels < kMaxMismatchesToReport) {
+ RasterUnderInvalidation under_invalidation = {layer_x, layer_y,
+ old_pixel, new_pixel};
+ under_invalidations_.push_back(under_invalidation);
+ LOG(ERROR) << layer_debug_name
+ << " Uninvalidated old/new pixels mismatch at " << layer_x
+ << "," << layer_y << " old:" << std::hex << old_pixel
+ << " new:" << new_pixel;
+ } else if (mismatching_pixels == kMaxMismatchesToReport) {
+ LOG(ERROR) << "and more...";
+ }
+ ++mismatching_pixels;
+ *new_bitmap.getAddr32(bitmap_x, bitmap_y) =
+ SkColorSetARGB(0xFF, 0xA0, 0, 0); // Dark red.
+ } else {
+ *new_bitmap.getAddr32(bitmap_x, bitmap_y) = SK_ColorTRANSPARENT;
+ }
+ }
+ }
+
+ if (!mismatching_pixels)
+ return;
+
+ PaintRecorder recorder;
+ recorder.beginRecording(rect);
+ auto* canvas = recorder.getRecordingCanvas();
+ if (under_invalidation_record_)
+ canvas->drawPicture(std::move(under_invalidation_record_));
+ canvas->drawBitmap(new_bitmap, rect.X(), rect.Y());
+ under_invalidation_record_ = recorder.finishRecordingAsPicture();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/raster_invalidation_tracking.h b/chromium/third_party/blink/renderer/platform/graphics/paint/raster_invalidation_tracking.h
new file mode 100644
index 00000000000..cba2bac99b4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/raster_invalidation_tracking.h
@@ -0,0 +1,95 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_RASTER_INVALIDATION_TRACKING_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_RASTER_INVALIDATION_TRACKING_H_
+
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/geometry/region.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_record.h"
+#include "third_party/blink/renderer/platform/graphics/paint_invalidation_reason.h"
+#include "third_party/blink/renderer/platform/json/json_values.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/skia/include/core/SkColor.h"
+
+namespace blink {
+
+class DisplayItemClient;
+
+struct RasterInvalidationInfo {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ // This is for comparison only. Don't dereference because the client may have
+ // died.
+ const DisplayItemClient* client = nullptr;
+ String client_debug_name;
+ // For SPv2, this is set in PaintArtifactCompositor when converting chunk
+ // raster invalidations to cc raster invalidations.
+ IntRect rect;
+ PaintInvalidationReason reason = PaintInvalidationReason::kFull;
+};
+
+inline bool operator==(const RasterInvalidationInfo& a,
+ const RasterInvalidationInfo& b) {
+ return a.rect == b.rect;
+}
+
+struct RasterUnderInvalidation {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ int x;
+ int y;
+ SkColor old_pixel;
+ SkColor new_pixel;
+};
+
+class PLATFORM_EXPORT RasterInvalidationTracking {
+ public:
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ // When RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled() and
+ // simulateRasterUnderInvalidation(true) is called, all changed pixels will
+ // be reported as raster under-invalidations. Used to visually test raster
+ // under-invalidation checking feature.
+ static void SimulateRasterUnderInvalidations(bool enable);
+
+ void AddInvalidation(const DisplayItemClient*,
+ const String& debug_name,
+ const IntRect&,
+ PaintInvalidationReason);
+ bool HasInvalidations() const { return !invalidations_.IsEmpty(); }
+ const Vector<RasterInvalidationInfo>& Invalidations() const {
+ return invalidations_;
+ }
+ void ClearInvalidations() { invalidations_.clear(); }
+
+ // Compares the last recording against |new_record|, by rastering both into
+ // bitmaps. If there are any differences outside of invalidated regions,
+ // the corresponding pixels in UnderInvalidationRecord() will be drawn in
+ // dark red. The caller can overlay UnderInvalidationRecord() onto the
+ // original drawings to show the under raster invalidations.
+ void CheckUnderInvalidations(const String& layer_debug_name,
+ sk_sp<PaintRecord> new_record,
+ const IntRect& new_interest_rect);
+
+ void AsJSON(JSONObject*);
+
+ // The record containing under-invalidated pixels in dark red.
+ sk_sp<const PaintRecord> UnderInvalidationRecord() const {
+ return under_invalidation_record_;
+ }
+
+ private:
+ Vector<RasterInvalidationInfo> invalidations_;
+
+ // The following fields are for raster under-invalidation detection.
+ sk_sp<PaintRecord> last_painted_record_;
+ IntRect last_interest_rect_;
+ Region invalidation_region_since_last_paint_;
+ Vector<RasterUnderInvalidation> under_invalidations_;
+ sk_sp<PaintRecord> under_invalidation_record_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_RASTER_INVALIDATION_TRACKING_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/ref_counted_property_tree_state.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/ref_counted_property_tree_state.cc
new file mode 100644
index 00000000000..1003213e594
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/ref_counted_property_tree_state.cc
@@ -0,0 +1,43 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/ref_counted_property_tree_state.h"
+
+#include <memory>
+
+namespace blink {
+
+const RefCountedPropertyTreeState& RefCountedPropertyTreeState::Root() {
+ DEFINE_STATIC_LOCAL(
+ std::unique_ptr<RefCountedPropertyTreeState>, root,
+ (std::make_unique<RefCountedPropertyTreeState>(
+ TransformPaintPropertyNode::Root(), ClipPaintPropertyNode::Root(),
+ EffectPaintPropertyNode::Root())));
+ return *root;
+}
+
+const CompositorElementId RefCountedPropertyTreeState::GetCompositorElementId(
+ const CompositorElementIdSet& element_ids) const {
+ // The effect or transform nodes could have a compositor element id. The order
+ // doesn't matter as the element id should be the same on all that have a
+ // non-default CompositorElementId.
+ //
+ // Note that RefCountedPropertyTreeState acts as a context that accumulates
+ // state as we traverse the tree building layers. This means that we could see
+ // a compositor element id 'A' for a parent layer in conjunction with a
+ // compositor element id 'B' for a child layer. To preserve uniqueness of
+ // element ids, then, we check for presence in the |element_ids| set (which
+ // represents element ids already previously attached to a layer). This is an
+ // interim step while we pursue broader rework of animation subsystem noted in
+ // http://crbug.com/709137.
+ if (Effect()->GetCompositorElementId() &&
+ !element_ids.Contains(Effect()->GetCompositorElementId()))
+ return Effect()->GetCompositorElementId();
+ if (Transform()->GetCompositorElementId() &&
+ !element_ids.Contains(Transform()->GetCompositorElementId()))
+ return Transform()->GetCompositorElementId();
+ return CompositorElementId();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/ref_counted_property_tree_state.h b/chromium/third_party/blink/renderer/platform/graphics/paint/ref_counted_property_tree_state.h
new file mode 100644
index 00000000000..340d6e95116
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/ref_counted_property_tree_state.h
@@ -0,0 +1,88 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_REF_COUNTED_PROPERTY_TREE_STATE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_REF_COUNTED_PROPERTY_TREE_STATE_H_
+
+#include "third_party/blink/renderer/platform/graphics/paint/property_tree_state.h"
+#include "third_party/blink/renderer/platform/wtf/hash_functions.h"
+#include "third_party/blink/renderer/platform/wtf/hash_traits.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+
+namespace blink {
+
+// A complete set of paint properties including those that are inherited from
+// other objects. RefPtrs are used to guard against use-after-free bugs.
+class PLATFORM_EXPORT RefCountedPropertyTreeState {
+ USING_FAST_MALLOC(RefCountedPropertyTreeState);
+
+ public:
+ RefCountedPropertyTreeState(const TransformPaintPropertyNode* transform,
+ const ClipPaintPropertyNode* clip,
+ const EffectPaintPropertyNode* effect)
+ : transform_(transform), clip_(clip), effect_(effect) {}
+
+ RefCountedPropertyTreeState(const PropertyTreeState& property_tree_state)
+ : transform_(property_tree_state.Transform()),
+ clip_(property_tree_state.Clip()),
+ effect_(property_tree_state.Effect()) {}
+
+ bool HasDirectCompositingReasons() const;
+
+ const TransformPaintPropertyNode* Transform() const {
+ return transform_.get();
+ }
+ void SetTransform(scoped_refptr<const TransformPaintPropertyNode> node) {
+ transform_ = std::move(node);
+ }
+
+ const ClipPaintPropertyNode* Clip() const { return clip_.get(); }
+ void SetClip(scoped_refptr<const ClipPaintPropertyNode> node) {
+ clip_ = std::move(node);
+ }
+
+ const EffectPaintPropertyNode* Effect() const { return effect_.get(); }
+ void SetEffect(scoped_refptr<const EffectPaintPropertyNode> node) {
+ effect_ = std::move(node);
+ }
+
+ static const RefCountedPropertyTreeState& Root();
+
+ PropertyTreeState GetPropertyTreeState() const {
+ return PropertyTreeState(transform_.get(), clip_.get(), effect_.get());
+ }
+
+ // Returns the compositor element id, if any, for this property state. If
+ // neither the effect nor transform nodes have a compositor element id then a
+ // default instance is returned.
+ const CompositorElementId GetCompositorElementId(
+ const CompositorElementIdSet& element_ids) const;
+
+ void ClearChangedToRoot() const {
+ Transform()->ClearChangedToRoot();
+ Clip()->ClearChangedToRoot();
+ Effect()->ClearChangedToRoot();
+ }
+
+ String ToString() const { return GetPropertyTreeState().ToString(); }
+#if DCHECK_IS_ON()
+ // Dumps the tree from this state up to the root as a string.
+ String ToTreeString() const { return GetPropertyTreeState().ToTreeString(); }
+#endif
+
+ private:
+ scoped_refptr<const TransformPaintPropertyNode> transform_;
+ scoped_refptr<const ClipPaintPropertyNode> clip_;
+ scoped_refptr<const EffectPaintPropertyNode> effect_;
+};
+
+inline bool operator==(const RefCountedPropertyTreeState& a,
+ const RefCountedPropertyTreeState& b) {
+ return a.Transform() == b.Transform() && a.Clip() == b.Clip() &&
+ a.Effect() == b.Effect();
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_REF_COUNTED_PROPERTY_TREE_STATE_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/scoped_display_item_fragment.h b/chromium/third_party/blink/renderer/platform/graphics/paint/scoped_display_item_fragment.h
new file mode 100644
index 00000000000..d0cef181f33
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/scoped_display_item_fragment.h
@@ -0,0 +1,36 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_SCOPED_DISPLAY_ITEM_FRAGMENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_SCOPED_DISPLAY_ITEM_FRAGMENT_H_
+
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class ScopedDisplayItemFragment final {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ WTF_MAKE_NONCOPYABLE(ScopedDisplayItemFragment);
+
+ public:
+ ScopedDisplayItemFragment(GraphicsContext& context, unsigned fragment)
+ : context_(context),
+ original_fragment_(context.GetPaintController().CurrentFragment()) {
+ context.GetPaintController().SetCurrentFragment(fragment);
+ }
+ ~ScopedDisplayItemFragment() {
+ context_.GetPaintController().SetCurrentFragment(original_fragment_);
+ }
+
+ private:
+ GraphicsContext& context_;
+ unsigned original_fragment_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_SCOPED_DISPLAY_ITEM_FRAGMENT_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/scoped_paint_chunk_properties.h b/chromium/third_party/blink/renderer/platform/graphics/paint/scoped_paint_chunk_properties.h
new file mode 100644
index 00000000000..9e9d2bfd0fa
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/scoped_paint_chunk_properties.h
@@ -0,0 +1,133 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_SCOPED_PAINT_CHUNK_PROPERTIES_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_SCOPED_PAINT_CHUNK_PROPERTIES_H_
+
+#include "third_party/blink/renderer/platform/graphics/paint/display_item.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_chunk.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_chunk_properties.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+
+namespace blink {
+
+class ScopedPaintChunkProperties {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ WTF_MAKE_NONCOPYABLE(ScopedPaintChunkProperties);
+
+ public:
+ // Use new PaintChunkProperties for the scope.
+ ScopedPaintChunkProperties(PaintController& paint_controller,
+ const PaintChunkProperties& properties,
+ const DisplayItemClient& client,
+ DisplayItem::Type type)
+ : paint_controller_(paint_controller),
+ previous_properties_(paint_controller.CurrentPaintChunkProperties()) {
+ paint_controller_.UpdateCurrentPaintChunkProperties(
+ PaintChunk::Id(client, type), properties);
+ }
+
+ // Use new PropertyTreeState, and keep the current backface_hidden.
+ ScopedPaintChunkProperties(PaintController& paint_controller,
+ const PropertyTreeState& state,
+ const DisplayItemClient& client,
+ DisplayItem::Type type)
+ : ScopedPaintChunkProperties(
+ paint_controller,
+ GetPaintChunkProperties(state, paint_controller),
+ client,
+ type) {}
+
+ // Use new transform state, and keep the current other properties.
+ ScopedPaintChunkProperties(
+ PaintController& paint_controller,
+ scoped_refptr<const TransformPaintPropertyNode> transform,
+ const DisplayItemClient& client,
+ DisplayItem::Type type)
+ : ScopedPaintChunkProperties(
+ paint_controller,
+ GetPaintChunkProperties(transform, paint_controller),
+ client,
+ type) {}
+
+ // Use new clip state, and keep the current other properties.
+ ScopedPaintChunkProperties(PaintController& paint_controller,
+ scoped_refptr<const ClipPaintPropertyNode> clip,
+ const DisplayItemClient& client,
+ DisplayItem::Type type)
+ : ScopedPaintChunkProperties(
+ paint_controller,
+ GetPaintChunkProperties(clip, paint_controller),
+ client,
+ type) {}
+
+ // Use new effect state, and keep the current other properties.
+ ScopedPaintChunkProperties(
+ PaintController& paint_controller,
+ scoped_refptr<const EffectPaintPropertyNode> effect,
+ const DisplayItemClient& client,
+ DisplayItem::Type type)
+ : ScopedPaintChunkProperties(
+ paint_controller,
+ GetPaintChunkProperties(effect, paint_controller),
+ client,
+ type) {}
+
+ ~ScopedPaintChunkProperties() {
+ // We should not return to the previous id, because that may cause a new
+ // chunk to use the same id as that of the previous chunk before this
+ // ScopedPaintChunkProperties. The painter should create another scope of
+ // paint properties with new id, or the new chunk will use the id of the
+ // first display item as its id.
+ paint_controller_.UpdateCurrentPaintChunkProperties(WTF::nullopt,
+ previous_properties_);
+ }
+
+ private:
+ static PaintChunkProperties GetPaintChunkProperties(
+ const PropertyTreeState& state,
+ PaintController& paint_controller) {
+ PaintChunkProperties properties(state);
+ properties.backface_hidden =
+ paint_controller.CurrentPaintChunkProperties().backface_hidden;
+ return properties;
+ }
+
+ static PaintChunkProperties GetPaintChunkProperties(
+ scoped_refptr<const TransformPaintPropertyNode> transform,
+ PaintController& paint_controller) {
+ PaintChunkProperties properties(
+ paint_controller.CurrentPaintChunkProperties());
+ properties.property_tree_state.SetTransform(std::move(transform));
+ return properties;
+ }
+
+ static PaintChunkProperties GetPaintChunkProperties(
+ scoped_refptr<const ClipPaintPropertyNode> clip,
+ PaintController& paint_controller) {
+ PaintChunkProperties properties(
+ paint_controller.CurrentPaintChunkProperties());
+ properties.property_tree_state.SetClip(std::move(clip));
+ return properties;
+ }
+
+ static PaintChunkProperties GetPaintChunkProperties(
+ scoped_refptr<const EffectPaintPropertyNode> effect,
+ PaintController& paint_controller) {
+ PaintChunkProperties properties(
+ paint_controller.CurrentPaintChunkProperties());
+ properties.property_tree_state.SetEffect(std::move(effect));
+ return properties;
+ }
+
+ PaintController& paint_controller_;
+ PaintChunkProperties previous_properties_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_SCOPED_PAINT_CHUNK_PROPERTIES_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/scroll_display_item.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/scroll_display_item.cc
new file mode 100644
index 00000000000..3e52d0ab021
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/scroll_display_item.cc
@@ -0,0 +1,42 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/scroll_display_item.h"
+
+#include "third_party/blink/public/platform/web_display_item_list.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/transforms/affine_transform.h"
+
+namespace blink {
+
+void BeginScrollDisplayItem::Replay(GraphicsContext& context) const {
+ context.Save();
+ context.Translate(-current_offset_.Width(), -current_offset_.Height());
+}
+
+void BeginScrollDisplayItem::AppendToWebDisplayItemList(
+ const FloatSize&,
+ WebDisplayItemList* list) const {
+ WebDisplayItemList::ScrollContainerId scroll_container_id = &Client();
+ list->AppendScrollItem(current_offset_, scroll_container_id);
+}
+
+#if DCHECK_IS_ON()
+void BeginScrollDisplayItem::PropertiesAsJSON(JSONObject& json) const {
+ PairedBeginDisplayItem::PropertiesAsJSON(json);
+ json.SetString("currentOffset", current_offset_.ToString());
+}
+#endif
+
+void EndScrollDisplayItem::Replay(GraphicsContext& context) const {
+ context.Restore();
+}
+
+void EndScrollDisplayItem::AppendToWebDisplayItemList(
+ const FloatSize&,
+ WebDisplayItemList* list) const {
+ list->AppendEndScrollItem();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/scroll_display_item.h b/chromium/third_party/blink/renderer/platform/graphics/paint/scroll_display_item.h
new file mode 100644
index 00000000000..b0dfdcb03c6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/scroll_display_item.h
@@ -0,0 +1,65 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_SCROLL_DISPLAY_ITEM_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_SCROLL_DISPLAY_ITEM_H_
+
+#include "third_party/blink/renderer/platform/geometry/int_size.h"
+#include "third_party/blink/renderer/platform/graphics/paint/display_item.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT BeginScrollDisplayItem final
+ : public PairedBeginDisplayItem {
+ public:
+ BeginScrollDisplayItem(const DisplayItemClient& client,
+ Type type,
+ const IntSize& current_offset)
+ : PairedBeginDisplayItem(client, type, sizeof(*this)),
+ current_offset_(current_offset) {
+ DCHECK(IsScrollType(type));
+ }
+
+ void Replay(GraphicsContext&) const override;
+ void AppendToWebDisplayItemList(const FloatSize&,
+ WebDisplayItemList*) const override;
+
+ const IntSize& CurrentOffset() const { return current_offset_; }
+
+ private:
+#if DCHECK_IS_ON()
+ void PropertiesAsJSON(JSONObject&) const final;
+#endif
+ bool Equals(const DisplayItem& other) const final {
+ return DisplayItem::Equals(other) &&
+ current_offset_ == static_cast<const BeginScrollDisplayItem&>(other)
+ .current_offset_;
+ }
+
+ const IntSize current_offset_;
+};
+
+class PLATFORM_EXPORT EndScrollDisplayItem final : public PairedEndDisplayItem {
+ public:
+ EndScrollDisplayItem(const DisplayItemClient& client, Type type)
+ : PairedEndDisplayItem(client, type, sizeof(*this)) {
+ DCHECK(IsEndScrollType(type));
+ }
+
+ void Replay(GraphicsContext&) const override;
+ void AppendToWebDisplayItemList(const FloatSize&,
+ WebDisplayItemList*) const override;
+
+ private:
+#if DCHECK_IS_ON()
+ bool IsEndAndPairedWith(DisplayItem::Type other_type) const final {
+ return DisplayItem::IsScrollType(other_type);
+ }
+#endif
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_SCROLL_DISPLAY_ITEM_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/scroll_hit_test_display_item.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/scroll_hit_test_display_item.cc
new file mode 100644
index 00000000000..468952c4a0d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/scroll_hit_test_display_item.cc
@@ -0,0 +1,70 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/scroll_hit_test_display_item.h"
+
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+namespace blink {
+
+ScrollHitTestDisplayItem::ScrollHitTestDisplayItem(
+ const DisplayItemClient& client,
+ Type type,
+ scoped_refptr<const TransformPaintPropertyNode> scroll_offset_node)
+ : DisplayItem(client, type, sizeof(*this)),
+ scroll_offset_node_(std::move(scroll_offset_node)) {
+ DCHECK(RuntimeEnabledFeatures::SlimmingPaintV2Enabled());
+ DCHECK(IsScrollHitTestType(type));
+ // The scroll offset transform node should have an associated scroll node.
+ DCHECK(scroll_offset_node_->ScrollNode());
+}
+
+ScrollHitTestDisplayItem::~ScrollHitTestDisplayItem() = default;
+
+void ScrollHitTestDisplayItem::Replay(GraphicsContext&) const {
+ NOTREACHED();
+}
+
+void ScrollHitTestDisplayItem::AppendToWebDisplayItemList(
+ const FloatSize&,
+ WebDisplayItemList*) const {
+ NOTREACHED();
+}
+
+bool ScrollHitTestDisplayItem::Equals(const DisplayItem& other) const {
+ return DisplayItem::Equals(other) &&
+ &scroll_node() ==
+ &static_cast<const ScrollHitTestDisplayItem&>(other).scroll_node();
+}
+
+#if DCHECK_IS_ON()
+void ScrollHitTestDisplayItem::PropertiesAsJSON(JSONObject& json) const {
+ DisplayItem::PropertiesAsJSON(json);
+ json.SetString("scrollOffsetNode",
+ String::Format("%p", scroll_offset_node_.get()));
+}
+#endif
+
+void ScrollHitTestDisplayItem::Record(
+ GraphicsContext& context,
+ const DisplayItemClient& client,
+ DisplayItem::Type type,
+ scoped_refptr<const TransformPaintPropertyNode> scroll_offset_node) {
+ PaintController& paint_controller = context.GetPaintController();
+
+ // The scroll hit test should be in the non-scrolled transform space and
+ // therefore should not be scrolled by the associated scroll offset.
+ DCHECK(paint_controller.CurrentPaintChunkProperties()
+ .property_tree_state.Transform() != scroll_offset_node);
+
+ if (paint_controller.DisplayItemConstructionIsDisabled())
+ return;
+
+ paint_controller.CreateAndAppend<ScrollHitTestDisplayItem>(
+ client, type, std::move(scroll_offset_node));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/scroll_hit_test_display_item.h b/chromium/third_party/blink/renderer/platform/graphics/paint/scroll_hit_test_display_item.h
new file mode 100644
index 00000000000..234b1b33932
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/scroll_hit_test_display_item.h
@@ -0,0 +1,65 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_SCROLL_HIT_TEST_DISPLAY_ITEM_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_SCROLL_HIT_TEST_DISPLAY_ITEM_H_
+
+#include "base/memory/ref_counted.h"
+#include "third_party/blink/renderer/platform/graphics/paint/display_item.h"
+#include "third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+
+class GraphicsContext;
+
+// Placeholder display item for creating a special cc::Layer marked as being
+// scrollable in PaintArtifactCompositor. A display item is needed because
+// scroll hit testing must be in paint order.
+//
+// The scroll hit test display item keeps track of the scroll offset translation
+// node which also has a reference to the associated scroll node. The scroll hit
+// test display item should be in the non-scrolled transform space and therefore
+// should not be scrolled by the associated scroll offset transform.
+class PLATFORM_EXPORT ScrollHitTestDisplayItem final : public DisplayItem {
+ public:
+ ScrollHitTestDisplayItem(
+ const DisplayItemClient&,
+ Type,
+ scoped_refptr<const TransformPaintPropertyNode> scroll_offset_node);
+ ~ScrollHitTestDisplayItem();
+
+ const TransformPaintPropertyNode& scroll_offset_node() const {
+ return *scroll_offset_node_;
+ }
+
+ // DisplayItem
+ void Replay(GraphicsContext&) const override;
+ void AppendToWebDisplayItemList(const FloatSize&,
+ WebDisplayItemList*) const override;
+ bool Equals(const DisplayItem&) const override;
+#if DCHECK_IS_ON()
+ void PropertiesAsJSON(JSONObject&) const override;
+#endif
+
+ // Create and append a ScrollHitTestDisplayItem onto the context. This is
+ // similar to a recorder class (e.g., DrawingRecorder) but just emits a single
+ // item.
+ static void Record(
+ GraphicsContext&,
+ const DisplayItemClient&,
+ DisplayItem::Type,
+ scoped_refptr<const TransformPaintPropertyNode> scroll_offset_node);
+
+ private:
+ const ScrollPaintPropertyNode& scroll_node() const {
+ return *scroll_offset_node_->ScrollNode();
+ }
+
+ scoped_refptr<const TransformPaintPropertyNode> scroll_offset_node_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_SCROLL_HIT_TEST_DISPLAY_ITEM_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.cc
new file mode 100644
index 00000000000..9acec3301c5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.cc
@@ -0,0 +1,45 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h"
+
+namespace blink {
+
+ScrollPaintPropertyNode* ScrollPaintPropertyNode::Root() {
+ DEFINE_STATIC_REF(ScrollPaintPropertyNode, root,
+ (ScrollPaintPropertyNode::Create(
+ nullptr, IntRect(), IntRect(), false, false,
+ MainThreadScrollingReason::kNotScrollingOnMain,
+ CompositorElementId())));
+ return root;
+}
+
+std::unique_ptr<JSONObject> ScrollPaintPropertyNode::ToJSON() const {
+ auto json = JSONObject::Create();
+ if (Parent())
+ json->SetString("parent", String::Format("%p", Parent()));
+ if (container_rect_ != IntRect())
+ json->SetString("containerRect", container_rect_.ToString());
+ if (contents_rect_ != IntRect())
+ json->SetString("contentsRect", contents_rect_.ToString());
+ if (user_scrollable_horizontal_ || user_scrollable_vertical_) {
+ json->SetString("userScrollable",
+ user_scrollable_horizontal_
+ ? (user_scrollable_vertical_ ? "both" : "horizontal")
+ : "vertical");
+ }
+ if (main_thread_scrolling_reasons_) {
+ json->SetString("mainThreadReasons",
+ MainThreadScrollingReason::mainThreadScrollingReasonsAsText(
+ main_thread_scrolling_reasons_)
+ .c_str());
+ }
+ if (compositor_element_id_) {
+ json->SetString("compositorElementId",
+ compositor_element_id_.ToString().c_str());
+ }
+ return json;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h b/chromium/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h
new file mode 100644
index 00000000000..72ead3e6a20
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h
@@ -0,0 +1,181 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_SCROLL_PAINT_PROPERTY_NODE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_SCROLL_PAINT_PROPERTY_NODE_H_
+
+#include "third_party/blink/renderer/platform/geometry/float_point.h"
+#include "third_party/blink/renderer/platform/geometry/float_size.h"
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/graphics/compositor_element_id.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_property_node.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/scroll/main_thread_scrolling_reason.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+#include <iosfwd>
+
+namespace blink {
+
+using MainThreadScrollingReasons = uint32_t;
+
+// A scroll node contains auxiliary scrolling information which includes how far
+// an area can be scrolled, main thread scrolling reasons, etc. Scroll nodes
+// are referenced by TransformPaintPropertyNodes that are used for the scroll
+// offset translation, though scroll offset translation can exist without a
+// scroll node (e.g., overflow: hidden).
+//
+// Main thread scrolling reasons force scroll updates to go to the main thread
+// and can have dependencies on other nodes. For example, all parents of a
+// scroll node with background attachment fixed set should also have it set.
+//
+// The scroll tree differs from the other trees because it does not affect
+// geometry directly.
+class PLATFORM_EXPORT ScrollPaintPropertyNode
+ : public PaintPropertyNode<ScrollPaintPropertyNode> {
+ public:
+ // This node is really a sentinel, and does not represent a real scroll.
+ static ScrollPaintPropertyNode* Root();
+
+ static scoped_refptr<ScrollPaintPropertyNode> Create(
+ scoped_refptr<const ScrollPaintPropertyNode> parent,
+ const IntRect& container_rect,
+ const IntRect& contents_rect,
+ bool user_scrollable_horizontal,
+ bool user_scrollable_vertical,
+ MainThreadScrollingReasons main_thread_scrolling_reasons,
+ CompositorElementId compositor_element_id) {
+ return base::AdoptRef(new ScrollPaintPropertyNode(
+ std::move(parent), container_rect, contents_rect,
+ user_scrollable_horizontal, user_scrollable_vertical,
+ main_thread_scrolling_reasons, compositor_element_id));
+ }
+
+ bool Update(scoped_refptr<const ScrollPaintPropertyNode> parent,
+ const IntRect& container_rect,
+ const IntRect& contents_rect,
+ bool user_scrollable_horizontal,
+ bool user_scrollable_vertical,
+ MainThreadScrollingReasons main_thread_scrolling_reasons,
+ CompositorElementId compositor_element_id) {
+ bool parent_changed = PaintPropertyNode::Update(std::move(parent));
+
+ if (container_rect == container_rect_ && contents_rect == contents_rect_ &&
+ user_scrollable_horizontal == user_scrollable_horizontal_ &&
+ user_scrollable_vertical == user_scrollable_vertical_ &&
+ main_thread_scrolling_reasons == main_thread_scrolling_reasons_ &&
+ compositor_element_id_ == compositor_element_id)
+ return parent_changed;
+
+ SetChanged();
+ container_rect_ = container_rect;
+ contents_rect_ = contents_rect;
+ user_scrollable_horizontal_ = user_scrollable_horizontal;
+ user_scrollable_vertical_ = user_scrollable_vertical;
+ main_thread_scrolling_reasons_ = main_thread_scrolling_reasons;
+ compositor_element_id_ = compositor_element_id;
+ DCHECK(ElementIdNamespaceIsForScrolling());
+ return true;
+ }
+
+ // Rect of the container area that the contents scrolls in, in the space of
+ // the parent of the associated transform node (ScrollTranslation).
+ // It doesn't include non-overlay scrollbars. Overlay scrollbars do not affect
+ // the rect.
+ const IntRect& ContainerRect() const { return container_rect_; }
+
+ // Rect of the contents that is scrolled within the container rect, in the
+ // space of the associated transform node (ScrollTranslation).
+ const IntRect& ContentsRect() const { return contents_rect_; }
+
+ bool UserScrollableHorizontal() const { return user_scrollable_horizontal_; }
+ bool UserScrollableVertical() const { return user_scrollable_vertical_; }
+
+ // Return reason bitfield with values from cc::MainThreadScrollingReason.
+ MainThreadScrollingReasons GetMainThreadScrollingReasons() const {
+ return main_thread_scrolling_reasons_;
+ }
+
+ // Main thread scrolling reason for the threaded scrolling disabled setting.
+ bool ThreadedScrollingDisabled() const {
+ return main_thread_scrolling_reasons_ &
+ MainThreadScrollingReason::kThreadedScrollingDisabled;
+ }
+
+ // Main thread scrolling reason for background attachment fixed descendants.
+ bool HasBackgroundAttachmentFixedDescendants() const {
+ return main_thread_scrolling_reasons_ &
+ MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects;
+ }
+
+ const CompositorElementId& GetCompositorElementId() const {
+ return compositor_element_id_;
+ }
+
+#if DCHECK_IS_ON()
+ // The clone function is used by FindPropertiesNeedingUpdate.h for recording
+ // a scroll node before it has been updated, to later detect changes.
+ scoped_refptr<ScrollPaintPropertyNode> Clone() const {
+ scoped_refptr<ScrollPaintPropertyNode> cloned =
+ base::AdoptRef(new ScrollPaintPropertyNode(
+ Parent(), container_rect_, contents_rect_,
+ user_scrollable_horizontal_, user_scrollable_vertical_,
+ main_thread_scrolling_reasons_, compositor_element_id_));
+ return cloned;
+ }
+
+ // The equality operator is used by FindPropertiesNeedingUpdate.h for checking
+ // if a scroll node has changed.
+ bool operator==(const ScrollPaintPropertyNode& o) const {
+ return Parent() == o.Parent() && container_rect_ == o.container_rect_ &&
+ contents_rect_ == o.contents_rect_ &&
+ user_scrollable_horizontal_ == o.user_scrollable_horizontal_ &&
+ user_scrollable_vertical_ == o.user_scrollable_vertical_ &&
+ main_thread_scrolling_reasons_ == o.main_thread_scrolling_reasons_ &&
+ compositor_element_id_ == o.compositor_element_id_;
+ }
+#endif
+
+ std::unique_ptr<JSONObject> ToJSON() const;
+
+ private:
+ ScrollPaintPropertyNode(
+ scoped_refptr<const ScrollPaintPropertyNode> parent,
+ const IntRect& container_rect,
+ const IntRect& contents_rect,
+ bool user_scrollable_horizontal,
+ bool user_scrollable_vertical,
+ MainThreadScrollingReasons main_thread_scrolling_reasons,
+ CompositorElementId compositor_element_id)
+ : PaintPropertyNode(std::move(parent)),
+ container_rect_(container_rect),
+ contents_rect_(contents_rect),
+ user_scrollable_horizontal_(user_scrollable_horizontal),
+ user_scrollable_vertical_(user_scrollable_vertical),
+ main_thread_scrolling_reasons_(main_thread_scrolling_reasons),
+ compositor_element_id_(compositor_element_id) {
+#if DCHECK_IS_ON()
+ DCHECK(ElementIdNamespaceIsForScrolling());
+#endif
+ }
+
+ bool ElementIdNamespaceIsForScrolling() const {
+ return !compositor_element_id_ ||
+ NamespaceFromCompositorElementId(compositor_element_id_) ==
+ CompositorElementIdNamespace::kScroll;
+ }
+
+ IntRect container_rect_;
+ IntRect contents_rect_;
+ bool user_scrollable_horizontal_ : 1;
+ bool user_scrollable_vertical_ : 1;
+ MainThreadScrollingReasons main_thread_scrolling_reasons_;
+ // The scrolling element id is stored directly on the scroll node and not on
+ // the associated TransformPaintPropertyNode used for scroll offset.
+ CompositorElementId compositor_element_id_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_SCROLL_PAINT_PROPERTY_NODE_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/subsequence_recorder.h b/chromium/third_party/blink/renderer/platform/graphics/paint/subsequence_recorder.h
new file mode 100644
index 00000000000..d5609bec997
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/subsequence_recorder.h
@@ -0,0 +1,60 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_SUBSEQUENCE_RECORDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_SUBSEQUENCE_RECORDER_H_
+
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/paint/display_item.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class GraphicsContext;
+class PaintController;
+
+// SubsequenceRecorder records BeginSubsequenceDisplayItem and
+// EndSubsequenceDisplayItem sentinels at either end of a continguous sequence
+// of DisplayItems, and supports caching via a CachedDisplayItem with the
+// CachedSubsequence DisplayItem type.
+//
+// Also note that useCachedSubsequenceIfPossible is not sufficient to determine
+// whether a CachedSubsequence can be used. In particular, the client is
+// responsible for checking that none of the DisplayItemClients that contribute
+// to the subsequence have been invalidated.
+//
+class SubsequenceRecorder final {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ WTF_MAKE_NONCOPYABLE(SubsequenceRecorder);
+
+ public:
+ static bool UseCachedSubsequenceIfPossible(GraphicsContext& context,
+ const DisplayItemClient& client) {
+ return context.GetPaintController().UseCachedSubsequenceIfPossible(client);
+ }
+
+ SubsequenceRecorder(GraphicsContext& context, const DisplayItemClient& client)
+ : paint_controller_(context.GetPaintController()),
+ client_(client),
+ start_(0) {
+ if (!paint_controller_.DisplayItemConstructionIsDisabled())
+ start_ = paint_controller_.BeginSubsequence();
+ }
+
+ ~SubsequenceRecorder() {
+ if (!paint_controller_.DisplayItemConstructionIsDisabled())
+ paint_controller_.EndSubsequence(client_, start_);
+ }
+
+ private:
+ PaintController& paint_controller_;
+ const DisplayItemClient& client_;
+ size_t start_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_SUBSEQUENCE_RECORDER_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/transform_3d_display_item.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/transform_3d_display_item.cc
new file mode 100644
index 00000000000..dcce1a0c662
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/transform_3d_display_item.cc
@@ -0,0 +1,47 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/transform_3d_display_item.h"
+
+#include "third_party/blink/public/platform/web_display_item_list.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/transforms/affine_transform.h"
+
+namespace blink {
+
+void BeginTransform3DDisplayItem::Replay(GraphicsContext& context) const {
+ TransformationMatrix transform(transform_);
+ transform.ApplyTransformOrigin(transform_origin_);
+ context.Save();
+ context.ConcatCTM(transform.ToAffineTransform());
+}
+
+void BeginTransform3DDisplayItem::AppendToWebDisplayItemList(
+ const FloatSize&,
+ WebDisplayItemList* list) const {
+ // TODO(jbroman): The compositor will need the transform origin separately.
+ TransformationMatrix transform(transform_);
+ transform.ApplyTransformOrigin(transform_origin_);
+ list->AppendTransformItem(TransformationMatrix::ToSkMatrix44(transform));
+}
+
+#if DCHECK_IS_ON()
+void BeginTransform3DDisplayItem::PropertiesAsJSON(JSONObject& json) const {
+ PairedBeginDisplayItem::PropertiesAsJSON(json);
+ json.SetString("transform", transform_.ToString());
+ json.SetString("origin", transform_origin_.ToString());
+}
+#endif
+
+void EndTransform3DDisplayItem::Replay(GraphicsContext& context) const {
+ context.Restore();
+}
+
+void EndTransform3DDisplayItem::AppendToWebDisplayItemList(
+ const FloatSize&,
+ WebDisplayItemList* list) const {
+ list->AppendEndTransformItem();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/transform_3d_display_item.h b/chromium/third_party/blink/renderer/platform/graphics/paint/transform_3d_display_item.h
new file mode 100644
index 00000000000..1c7e75f92af
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/transform_3d_display_item.h
@@ -0,0 +1,75 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_TRANSFORM_3D_DISPLAY_ITEM_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_TRANSFORM_3D_DISPLAY_ITEM_H_
+
+#include "third_party/blink/renderer/platform/geometry/float_point_3d.h"
+#include "third_party/blink/renderer/platform/graphics/paint/display_item.h"
+#include "third_party/blink/renderer/platform/transforms/transformation_matrix.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT BeginTransform3DDisplayItem final
+ : public PairedBeginDisplayItem {
+ public:
+ BeginTransform3DDisplayItem(const DisplayItemClient& client,
+ Type type,
+ const TransformationMatrix& transform,
+ const FloatPoint3D& transform_origin)
+ : PairedBeginDisplayItem(client, type, sizeof(*this)),
+ transform_(transform),
+ transform_origin_(transform_origin) {
+ DCHECK(DisplayItem::IsTransform3DType(type));
+ }
+
+ void Replay(GraphicsContext&) const override;
+ void AppendToWebDisplayItemList(const FloatSize&,
+ WebDisplayItemList*) const override;
+
+ const TransformationMatrix& Transform() const { return transform_; }
+ const FloatPoint3D& TransformOrigin() const { return transform_origin_; }
+
+ private:
+#if DCHECK_IS_ON()
+ void PropertiesAsJSON(JSONObject&) const final;
+#endif
+ bool Equals(const DisplayItem& other) const final {
+ return DisplayItem::Equals(other) &&
+ transform_ == static_cast<const BeginTransform3DDisplayItem&>(other)
+ .transform_ &&
+ transform_origin_ ==
+ static_cast<const BeginTransform3DDisplayItem&>(other)
+ .transform_origin_;
+ }
+
+ const TransformationMatrix transform_;
+ const FloatPoint3D transform_origin_;
+};
+
+class PLATFORM_EXPORT EndTransform3DDisplayItem final
+ : public PairedEndDisplayItem {
+ public:
+ EndTransform3DDisplayItem(const DisplayItemClient& client, Type type)
+ : PairedEndDisplayItem(client, type, sizeof(*this)) {
+ DCHECK(DisplayItem::IsEndTransform3DType(type));
+ }
+
+ void Replay(GraphicsContext&) const override;
+ void AppendToWebDisplayItemList(const FloatSize&,
+ WebDisplayItemList*) const override;
+
+ private:
+#if DCHECK_IS_ON()
+ bool IsEndAndPairedWith(DisplayItem::Type other_type) const final {
+ return DisplayItem::Transform3DTypeToEndTransform3DType(other_type) ==
+ GetType();
+ }
+#endif
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_TRANSFORM_3D_DISPLAY_ITEM_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/transform_display_item.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/transform_display_item.cc
new file mode 100644
index 00000000000..662db9aff26
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/transform_display_item.cc
@@ -0,0 +1,41 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/transform_display_item.h"
+
+#include "third_party/blink/public/platform/web_display_item_list.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/transforms/affine_transform.h"
+
+namespace blink {
+
+void BeginTransformDisplayItem::Replay(GraphicsContext& context) const {
+ context.Save();
+ context.ConcatCTM(transform_);
+}
+
+void BeginTransformDisplayItem::AppendToWebDisplayItemList(
+ const FloatSize&,
+ WebDisplayItemList* list) const {
+ list->AppendTransformItem(AffineTransformToSkMatrix(transform_));
+}
+
+#if DCHECK_IS_ON()
+void BeginTransformDisplayItem::PropertiesAsJSON(JSONObject& json) const {
+ PairedBeginDisplayItem::PropertiesAsJSON(json);
+ json.SetString("transform", transform_.ToString());
+}
+#endif
+
+void EndTransformDisplayItem::Replay(GraphicsContext& context) const {
+ context.Restore();
+}
+
+void EndTransformDisplayItem::AppendToWebDisplayItemList(
+ const FloatSize&,
+ WebDisplayItemList* list) const {
+ list->AppendEndTransformItem();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/transform_display_item.h b/chromium/third_party/blink/renderer/platform/graphics/paint/transform_display_item.h
new file mode 100644
index 00000000000..88a7865a662
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/transform_display_item.h
@@ -0,0 +1,60 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_TRANSFORM_DISPLAY_ITEM_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_TRANSFORM_DISPLAY_ITEM_H_
+
+#include "third_party/blink/renderer/platform/graphics/paint/display_item.h"
+#include "third_party/blink/renderer/platform/transforms/affine_transform.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT BeginTransformDisplayItem final
+ : public PairedBeginDisplayItem {
+ public:
+ BeginTransformDisplayItem(const DisplayItemClient& client,
+ const AffineTransform& transform)
+ : PairedBeginDisplayItem(client, kBeginTransform, sizeof(*this)),
+ transform_(transform) {}
+
+ void Replay(GraphicsContext&) const override;
+ void AppendToWebDisplayItemList(const FloatSize&,
+ WebDisplayItemList*) const override;
+
+ const AffineTransform& Transform() const { return transform_; }
+
+ private:
+#if DCHECK_IS_ON()
+ void PropertiesAsJSON(JSONObject&) const final;
+#endif
+ bool Equals(const DisplayItem& other) const final {
+ return DisplayItem::Equals(other) &&
+ transform_ ==
+ static_cast<const BeginTransformDisplayItem&>(other).transform_;
+ }
+
+ const AffineTransform transform_;
+};
+
+class PLATFORM_EXPORT EndTransformDisplayItem final
+ : public PairedEndDisplayItem {
+ public:
+ EndTransformDisplayItem(const DisplayItemClient& client)
+ : PairedEndDisplayItem(client, kEndTransform, sizeof(*this)) {}
+
+ void Replay(GraphicsContext&) const override;
+ void AppendToWebDisplayItemList(const FloatSize&,
+ WebDisplayItemList*) const override;
+
+ private:
+#if DCHECK_IS_ON()
+ bool IsEndAndPairedWith(DisplayItem::Type other_type) const final {
+ return other_type == kBeginTransform;
+ }
+#endif
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_TRANSFORM_DISPLAY_ITEM_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.cc
new file mode 100644
index 00000000000..6c6e321349a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.cc
@@ -0,0 +1,59 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h"
+
+namespace blink {
+
+// The root of the transform tree. The root transform node references the root
+// scroll node.
+TransformPaintPropertyNode* TransformPaintPropertyNode::Root() {
+ DEFINE_STATIC_REF(TransformPaintPropertyNode, root,
+ base::AdoptRef(new TransformPaintPropertyNode(
+ nullptr, TransformationMatrix(), FloatPoint3D(), false,
+ 0, CompositingReason::kNone, CompositorElementId(),
+ ScrollPaintPropertyNode::Root())));
+ return root;
+}
+
+const TransformPaintPropertyNode&
+TransformPaintPropertyNode::NearestScrollTranslationNode() const {
+ const auto* transform = this;
+ while (!transform->ScrollNode()) {
+ transform = transform->Parent();
+ // The transform should never be null because the root transform has an
+ // associated scroll node (see: TransformPaintPropertyNode::Root()).
+ DCHECK(transform);
+ }
+ return *transform;
+}
+
+std::unique_ptr<JSONObject> TransformPaintPropertyNode::ToJSON() const {
+ auto json = JSONObject::Create();
+ if (Parent())
+ json->SetString("parent", String::Format("%p", Parent()));
+ if (!matrix_.IsIdentity())
+ json->SetString("matrix", matrix_.ToString());
+ if (!matrix_.IsIdentityOrTranslation())
+ json->SetString("origin", origin_.ToString());
+ if (!flattens_inherited_transform_)
+ json->SetBoolean("flattensInheritedTransform", false);
+ if (rendering_context_id_) {
+ json->SetString("renderingContextId",
+ String::Format("%x", rendering_context_id_));
+ }
+ if (direct_compositing_reasons_ != CompositingReason::kNone) {
+ json->SetString("directCompositingReasons",
+ CompositingReason::ToString(direct_compositing_reasons_));
+ }
+ if (compositor_element_id_) {
+ json->SetString("compositorElementId",
+ compositor_element_id_.ToString().c_str());
+ }
+ if (scroll_)
+ json->SetString("scroll", String::Format("%p", scroll_.get()));
+ return json;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h b/chromium/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h
new file mode 100644
index 00000000000..8b4279a52dc
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h
@@ -0,0 +1,205 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_TRANSFORM_PAINT_PROPERTY_NODE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_TRANSFORM_PAINT_PROPERTY_NODE_H_
+
+#include "third_party/blink/renderer/platform/geometry/float_point_3d.h"
+#include "third_party/blink/renderer/platform/graphics/compositing_reasons.h"
+#include "third_party/blink/renderer/platform/graphics/compositor_element_id.h"
+#include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_property_node.h"
+#include "third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/transforms/transformation_matrix.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+#include <iosfwd>
+
+namespace blink {
+
+// A transform (e.g., created by css "transform" or "perspective", or for
+// internal positioning such as paint offset or scrolling) along with a
+// reference to the parent TransformPaintPropertyNode. The scroll tree is
+// referenced by transform nodes and a transform node with an associated scroll
+// node will be a 2d transform for scroll offset.
+//
+// The transform tree is rooted at a node with no parent. This root node should
+// not be modified.
+class PLATFORM_EXPORT TransformPaintPropertyNode
+ : public PaintPropertyNode<TransformPaintPropertyNode> {
+ public:
+ // This node is really a sentinel, and does not represent a real transform
+ // space.
+ static TransformPaintPropertyNode* Root();
+
+ static scoped_refptr<TransformPaintPropertyNode> Create(
+ scoped_refptr<const TransformPaintPropertyNode> parent,
+ const TransformationMatrix& matrix,
+ const FloatPoint3D& origin,
+ bool flattens_inherited_transform = false,
+ unsigned rendering_context_id = 0,
+ CompositingReasons direct_compositing_reasons = CompositingReason::kNone,
+ const CompositorElementId& compositor_element_id = CompositorElementId(),
+ scoped_refptr<const ScrollPaintPropertyNode> scroll = nullptr) {
+ if (scroll) {
+ // If there is an associated scroll node, this can only be a 2d
+ // translation for scroll offset.
+ DCHECK(matrix.IsIdentityOr2DTranslation());
+ // The scroll compositor element id should be stored on the scroll node.
+ DCHECK(!compositor_element_id);
+ }
+ return base::AdoptRef(new TransformPaintPropertyNode(
+ std::move(parent), matrix, origin, flattens_inherited_transform,
+ rendering_context_id, direct_compositing_reasons, compositor_element_id,
+ std::move(scroll)));
+ }
+
+ bool Update(
+ scoped_refptr<const TransformPaintPropertyNode> parent,
+ const TransformationMatrix& matrix,
+ const FloatPoint3D& origin,
+ bool flattens_inherited_transform = false,
+ unsigned rendering_context_id = 0,
+ CompositingReasons direct_compositing_reasons = CompositingReason::kNone,
+ CompositorElementId compositor_element_id = CompositorElementId(),
+ scoped_refptr<const ScrollPaintPropertyNode> scroll = nullptr) {
+ bool parent_changed = PaintPropertyNode::Update(std::move(parent));
+
+ if (scroll) {
+ // If there is an associated scroll node, this can only be a 2d
+ // translation for scroll offset.
+ DCHECK(matrix.IsIdentityOr2DTranslation());
+ // The scroll compositor element id should be stored on the scroll node.
+ DCHECK(!compositor_element_id);
+ }
+
+ if (matrix == matrix_ && origin == origin_ &&
+ flattens_inherited_transform == flattens_inherited_transform_ &&
+ (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled() ||
+ (rendering_context_id == rendering_context_id_ &&
+ direct_compositing_reasons == direct_compositing_reasons_ &&
+ compositor_element_id == compositor_element_id_)) &&
+ scroll == scroll_)
+ return parent_changed;
+
+ SetChanged();
+ matrix_ = matrix;
+ origin_ = origin;
+ flattens_inherited_transform_ = flattens_inherited_transform;
+ rendering_context_id_ = rendering_context_id;
+ direct_compositing_reasons_ = direct_compositing_reasons;
+ compositor_element_id_ = compositor_element_id;
+ scroll_ = std::move(scroll);
+ return true;
+ }
+
+ const TransformationMatrix& Matrix() const { return matrix_; }
+ const FloatPoint3D& Origin() const { return origin_; }
+
+ // The associated scroll node, or nullptr otherwise.
+ const ScrollPaintPropertyNode* ScrollNode() const { return scroll_.get(); }
+
+ // If this is a scroll offset translation (i.e., has an associated scroll
+ // node), returns this. Otherwise, returns the transform node that this node
+ // scrolls with respect to. This can require a full ancestor traversal.
+ const TransformPaintPropertyNode& NearestScrollTranslationNode() const;
+
+ // If true, content with this transform node (or its descendant) appears in
+ // the plane of its parent. This is implemented by flattening the total
+ // accumulated transform from its ancestors.
+ bool FlattensInheritedTransform() const {
+ return flattens_inherited_transform_;
+ }
+
+ bool HasDirectCompositingReasons() const {
+ return direct_compositing_reasons_ != CompositingReason::kNone;
+ }
+
+ bool RequiresCompositingForAnimation() const {
+ return direct_compositing_reasons_ &
+ CompositingReason::kComboActiveAnimation;
+ }
+
+ const CompositorElementId& GetCompositorElementId() const {
+ return compositor_element_id_;
+ }
+
+ // Content whose transform nodes have a common rendering context ID are 3D
+ // sorted. If this is 0, content will not be 3D sorted.
+ unsigned RenderingContextId() const { return rendering_context_id_; }
+ bool HasRenderingContext() const { return rendering_context_id_; }
+
+#if DCHECK_IS_ON()
+ // The clone function is used by FindPropertiesNeedingUpdate.h for recording
+ // a transform node before it has been updated, to later detect changes.
+ scoped_refptr<TransformPaintPropertyNode> Clone() const {
+ return base::AdoptRef(new TransformPaintPropertyNode(
+ Parent(), matrix_, origin_, flattens_inherited_transform_,
+ rendering_context_id_, direct_compositing_reasons_,
+ compositor_element_id_, scroll_));
+ }
+
+ // The equality operator is used by FindPropertiesNeedingUpdate.h for checking
+ // if a transform node has changed.
+ bool operator==(const TransformPaintPropertyNode& o) const {
+ return Parent() == o.Parent() && matrix_ == o.matrix_ &&
+ origin_ == o.origin_ &&
+ flattens_inherited_transform_ == o.flattens_inherited_transform_ &&
+ (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled() ||
+ (rendering_context_id_ == o.rendering_context_id_ &&
+ direct_compositing_reasons_ == o.direct_compositing_reasons_ &&
+ compositor_element_id_ == o.compositor_element_id_)) &&
+ scroll_ == o.scroll_;
+ }
+#endif
+
+ std::unique_ptr<JSONObject> ToJSON() const;
+
+ private:
+ TransformPaintPropertyNode(
+ scoped_refptr<const TransformPaintPropertyNode> parent,
+ const TransformationMatrix& matrix,
+ const FloatPoint3D& origin,
+ bool flattens_inherited_transform,
+ unsigned rendering_context_id,
+ CompositingReasons direct_compositing_reasons,
+ CompositorElementId compositor_element_id,
+ scoped_refptr<const ScrollPaintPropertyNode> scroll = nullptr)
+ : PaintPropertyNode(std::move(parent)),
+ matrix_(matrix),
+ origin_(origin),
+ flattens_inherited_transform_(flattens_inherited_transform),
+ rendering_context_id_(rendering_context_id),
+ direct_compositing_reasons_(direct_compositing_reasons),
+ compositor_element_id_(compositor_element_id),
+ scroll_(std::move(scroll)) {}
+
+ // For access to getTransformCache() and setCachedTransform.
+ friend class GeometryMapper;
+ friend class GeometryMapperTest;
+ friend class GeometryMapperTransformCache;
+
+ const GeometryMapperTransformCache& GetTransformCache() const {
+ if (!transform_cache_)
+ transform_cache_.reset(new GeometryMapperTransformCache);
+ transform_cache_->UpdateIfNeeded(*this);
+ return *transform_cache_;
+ }
+
+ TransformationMatrix matrix_;
+ FloatPoint3D origin_;
+ bool flattens_inherited_transform_;
+ unsigned rendering_context_id_;
+ CompositingReasons direct_compositing_reasons_;
+ CompositorElementId compositor_element_id_;
+ scoped_refptr<const ScrollPaintPropertyNode> scroll_;
+
+ mutable std::unique_ptr<GeometryMapperTransformCache> transform_cache_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_TRANSFORM_PAINT_PROPERTY_NODE_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint_generated_image.cc b/chromium/third_party/blink/renderer/platform/graphics/paint_generated_image.cc
new file mode 100644
index 00000000000..e09c3b5e8fb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint_generated_image.cc
@@ -0,0 +1,37 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint_generated_image.h"
+
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_record.h"
+
+namespace blink {
+
+void PaintGeneratedImage::Draw(PaintCanvas* canvas,
+ const PaintFlags& flags,
+ const FloatRect& dest_rect,
+ const FloatRect& src_rect,
+ RespectImageOrientationEnum,
+ ImageClampingMode,
+ ImageDecodingMode) {
+ PaintCanvasAutoRestore ar(canvas, true);
+ canvas->clipRect(dest_rect);
+ canvas->translate(dest_rect.X(), dest_rect.Y());
+ if (dest_rect.Size() != src_rect.Size())
+ canvas->scale(dest_rect.Width() / src_rect.Width(),
+ dest_rect.Height() / src_rect.Height());
+ canvas->translate(-src_rect.X(), -src_rect.Y());
+ SkRect bounds = src_rect;
+ canvas->saveLayer(&bounds, &flags);
+ canvas->drawPicture(record_);
+}
+
+void PaintGeneratedImage::DrawTile(GraphicsContext& context,
+ const FloatRect& src_rect) {
+ context.DrawRecord(record_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint_generated_image.h b/chromium/third_party/blink/renderer/platform/graphics/paint_generated_image.h
new file mode 100644
index 00000000000..074b828cdc6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint_generated_image.h
@@ -0,0 +1,41 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_GENERATED_IMAGE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_GENERATED_IMAGE_H_
+
+#include "third_party/blink/renderer/platform/geometry/int_size.h"
+#include "third_party/blink/renderer/platform/graphics/generated_image.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_record.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT PaintGeneratedImage : public GeneratedImage {
+ public:
+ static scoped_refptr<PaintGeneratedImage> Create(sk_sp<PaintRecord> record,
+ const FloatSize& size) {
+ return base::AdoptRef(new PaintGeneratedImage(std::move(record), size));
+ }
+ ~PaintGeneratedImage() override = default;
+
+ protected:
+ void Draw(PaintCanvas*,
+ const PaintFlags&,
+ const FloatRect&,
+ const FloatRect&,
+ RespectImageOrientationEnum,
+ ImageClampingMode,
+ ImageDecodingMode) override;
+ void DrawTile(GraphicsContext&, const FloatRect&) final;
+
+ PaintGeneratedImage(sk_sp<PaintRecord> record, const FloatSize& size)
+ : GeneratedImage(size), record_(std::move(record)) {}
+
+ sk_sp<PaintRecord> record_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_GENERATED_IMAGE_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint_invalidation_reason.cc b/chromium/third_party/blink/renderer/platform/graphics/paint_invalidation_reason.cc
new file mode 100644
index 00000000000..4b2381e686b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint_invalidation_reason.cc
@@ -0,0 +1,72 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint_invalidation_reason.h"
+
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+namespace blink {
+
+const char* PaintInvalidationReasonToString(PaintInvalidationReason reason) {
+ switch (reason) {
+ case PaintInvalidationReason::kNone:
+ return "none";
+ case PaintInvalidationReason::kIncremental:
+ return "incremental";
+ case PaintInvalidationReason::kRectangle:
+ return "invalidate paint rectangle";
+ case PaintInvalidationReason::kFull:
+ return "full";
+ case PaintInvalidationReason::kStyle:
+ return "style change";
+ case PaintInvalidationReason::kGeometry:
+ return "geometry";
+ case PaintInvalidationReason::kCompositing:
+ return "compositing update";
+ case PaintInvalidationReason::kBackground:
+ return "background";
+ case PaintInvalidationReason::kAppeared:
+ return "appeared";
+ case PaintInvalidationReason::kDisappeared:
+ return "disappeared";
+ case PaintInvalidationReason::kScroll:
+ return "scroll";
+ case PaintInvalidationReason::kScrollControl:
+ return "scroll control";
+ case PaintInvalidationReason::kSelection:
+ return "selection";
+ case PaintInvalidationReason::kOutline:
+ return "outline";
+ case PaintInvalidationReason::kSubtree:
+ return "subtree";
+ case PaintInvalidationReason::kSVGResource:
+ return "SVG resource change";
+ case PaintInvalidationReason::kBackgroundOnScrollingContentsLayer:
+ return "background on scrolling contents layer";
+ case PaintInvalidationReason::kCaret:
+ return "caret";
+ case PaintInvalidationReason::kDocumentMarker:
+ return "DocumentMarker change";
+ case PaintInvalidationReason::kImage:
+ return "image";
+ case PaintInvalidationReason::kChunkUncacheable:
+ return "chunk uncacheable";
+ case PaintInvalidationReason::kChunkReordered:
+ return "chunk reordered";
+ case PaintInvalidationReason::kPaintProperty:
+ return "paint property change";
+ case PaintInvalidationReason::kForTesting:
+ return "for testing";
+ case PaintInvalidationReason::kDelayedFull:
+ return "delayed full";
+ }
+ NOTREACHED();
+ return "";
+}
+
+std::ostream& operator<<(std::ostream& out, PaintInvalidationReason reason) {
+ return out << PaintInvalidationReasonToString(reason);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint_invalidation_reason.h b/chromium/third_party/blink/renderer/platform/graphics/paint_invalidation_reason.h
new file mode 100644
index 00000000000..c1a324d0260
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint_invalidation_reason.h
@@ -0,0 +1,66 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_INVALIDATION_REASON_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_INVALIDATION_REASON_H_
+
+#include <iosfwd>
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+
+enum class PaintInvalidationReason : unsigned {
+ kNone,
+ kIncremental,
+ kRectangle,
+ // The following reasons will all cause full paint invalidation.
+ kFull, // Any unspecified reason of full invalidation.
+ kStyle,
+ kGeometry, // Layout or visual geometry change.
+ kCompositing,
+ kAppeared,
+ kDisappeared,
+ kScroll,
+ kScrollControl, // scroll bars, scroll corner, etc.
+ kSelection,
+ kOutline,
+ kSubtree,
+ kSVGResource,
+ kBackground,
+ kBackgroundOnScrollingContentsLayer,
+ kCaret,
+ kDocumentMarker,
+ kImage,
+ kChunkUncacheable,
+ kChunkReordered,
+ kPaintProperty,
+ kForTesting,
+ // kDelayedFull means that kFull is needed in order to fully paint the
+ // content, but that painting of the object can be delayed until a future
+ // frame. This can be the case for an object whose content is not visible to
+ // the user.
+ kDelayedFull,
+
+ kMax = kDelayedFull
+};
+
+PLATFORM_EXPORT const char* PaintInvalidationReasonToString(
+ PaintInvalidationReason);
+
+inline bool IsFullPaintInvalidationReason(PaintInvalidationReason reason) {
+ return reason >= PaintInvalidationReason::kFull;
+}
+
+inline bool IsImmediateFullPaintInvalidationReason(
+ PaintInvalidationReason reason) {
+ return IsFullPaintInvalidationReason(reason) &&
+ reason != PaintInvalidationReason::kDelayedFull;
+}
+
+PLATFORM_EXPORT std::ostream& operator<<(std::ostream&,
+ PaintInvalidationReason);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_INVALIDATION_REASON_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint_invalidation_reason_test.cc b/chromium/third_party/blink/renderer/platform/graphics/paint_invalidation_reason_test.cc
new file mode 100644
index 00000000000..7ecdd3897dd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint_invalidation_reason_test.cc
@@ -0,0 +1,27 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint_invalidation_reason.h"
+
+#include <sstream>
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+TEST(PaintInvalidationReasonTest, StreamOutput) {
+ {
+ std::stringstream string_stream;
+ PaintInvalidationReason reason = PaintInvalidationReason::kNone;
+ string_stream << reason;
+ EXPECT_EQ("none", string_stream.str());
+ }
+ {
+ std::stringstream string_stream;
+ PaintInvalidationReason reason = PaintInvalidationReason::kDelayedFull;
+ string_stream << reason;
+ EXPECT_EQ("delayed full", string_stream.str());
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint_record_pattern.cc b/chromium/third_party/blink/renderer/platform/graphics/paint_record_pattern.cc
new file mode 100644
index 00000000000..a026a3bf7a8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint_record_pattern.cc
@@ -0,0 +1,43 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/paint_record_pattern.h"
+
+#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_record.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_shader.h"
+#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
+
+namespace blink {
+
+scoped_refptr<PaintRecordPattern> PaintRecordPattern::Create(
+ sk_sp<PaintRecord> record,
+ const FloatRect& record_bounds,
+ RepeatMode repeat_mode) {
+ return base::AdoptRef(
+ new PaintRecordPattern(std::move(record), record_bounds, repeat_mode));
+}
+
+PaintRecordPattern::PaintRecordPattern(sk_sp<PaintRecord> record,
+ const FloatRect& record_bounds,
+ RepeatMode mode)
+ : Pattern(mode),
+ tile_record_(std::move(record)),
+ tile_record_bounds_(record_bounds) {
+ // All current clients use RepeatModeXY, so we only support this mode for now.
+ DCHECK(IsRepeatXY());
+
+ // FIXME: we don't have a good way to account for DL memory utilization.
+}
+
+PaintRecordPattern::~PaintRecordPattern() = default;
+
+sk_sp<PaintShader> PaintRecordPattern::CreateShader(
+ const SkMatrix& local_matrix) {
+ return PaintShader::MakePaintRecord(
+ tile_record_, tile_record_bounds_, SkShader::kRepeat_TileMode,
+ SkShader::kRepeat_TileMode, &local_matrix);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint_record_pattern.h b/chromium/third_party/blink/renderer/platform/graphics/paint_record_pattern.h
new file mode 100644
index 00000000000..96b50d0c01b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint_record_pattern.h
@@ -0,0 +1,36 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_RECORD_PATTERN_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_RECORD_PATTERN_H_
+
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/graphics/pattern.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+namespace blink {
+
+// TODO(enne): rename this
+class PLATFORM_EXPORT PaintRecordPattern final : public Pattern {
+ public:
+ static scoped_refptr<PaintRecordPattern>
+ Create(sk_sp<PaintRecord>, const FloatRect& record_bounds, RepeatMode);
+
+ ~PaintRecordPattern() override;
+
+ protected:
+ sk_sp<PaintShader> CreateShader(const SkMatrix&) override;
+
+ private:
+ PaintRecordPattern(sk_sp<PaintRecord>,
+ const FloatRect& record_bounds,
+ RepeatMode);
+
+ sk_sp<PaintRecord> tile_record_;
+ FloatRect tile_record_bounds_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/graphics/path.cc b/chromium/third_party/blink/renderer/platform/graphics/path.cc
new file mode 100644
index 00000000000..361de3fac5c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/path.cc
@@ -0,0 +1,544 @@
+/*
+ * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved.
+ * 2006 Rob Buis <buis@kde.org>
+ * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ * Copyright (C) 2013 Intel Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/graphics/path.h"
+
+#include <math.h>
+#include "third_party/blink/renderer/platform/geometry/float_point.h"
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
+#include "third_party/blink/renderer/platform/transforms/affine_transform.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/skia/include/pathops/SkPathOps.h"
+
+namespace blink {
+
+Path::Path() : path_() {}
+
+Path::Path(const Path& other) {
+ path_ = SkPath(other.path_);
+}
+
+Path::Path(const SkPath& other) {
+ path_ = other;
+}
+
+Path::~Path() = default;
+
+Path& Path::operator=(const Path& other) {
+ path_ = SkPath(other.path_);
+ return *this;
+}
+
+Path& Path::operator=(const SkPath& other) {
+ path_ = other;
+ return *this;
+}
+
+bool Path::operator==(const Path& other) const {
+ return path_ == other.path_;
+}
+
+bool Path::Contains(const FloatPoint& point) const {
+ return path_.contains(WebCoreFloatToSkScalar(point.X()),
+ WebCoreFloatToSkScalar(point.Y()));
+}
+
+bool Path::Contains(const FloatPoint& point, WindRule rule) const {
+ SkScalar x = WebCoreFloatToSkScalar(point.X());
+ SkScalar y = WebCoreFloatToSkScalar(point.Y());
+ SkPath::FillType fill_type = WebCoreWindRuleToSkFillType(rule);
+ if (path_.getFillType() != fill_type) {
+ SkPath tmp(path_);
+ tmp.setFillType(fill_type);
+ return tmp.contains(x, y);
+ }
+ return path_.contains(x, y);
+}
+
+// FIXME: this method ignores the CTM and may yield inaccurate results for large
+// scales.
+SkPath Path::StrokePath(const StrokeData& stroke_data) const {
+ PaintFlags flags;
+ stroke_data.SetupPaint(&flags);
+
+ // Skia stroke resolution scale. This is multiplied by 4 internally
+ // (i.e. 1.0 corresponds to 1/4 pixel res).
+ static const SkScalar kResScale = 0.3f;
+
+ SkPath stroke_path;
+ flags.getFillPath(path_, &stroke_path, nullptr, kResScale);
+
+ return stroke_path;
+}
+
+bool Path::StrokeContains(const FloatPoint& point,
+ const StrokeData& stroke_data) const {
+ return StrokePath(stroke_data)
+ .contains(WebCoreFloatToSkScalar(point.X()),
+ WebCoreFloatToSkScalar(point.Y()));
+}
+
+FloatRect Path::BoundingRect() const {
+ return path_.computeTightBounds();
+}
+
+FloatRect Path::StrokeBoundingRect(const StrokeData& stroke_data) const {
+ return StrokePath(stroke_data).computeTightBounds();
+}
+
+static FloatPoint* ConvertPathPoints(FloatPoint dst[],
+ const SkPoint src[],
+ int count) {
+ for (int i = 0; i < count; i++) {
+ dst[i].SetX(SkScalarToFloat(src[i].fX));
+ dst[i].SetY(SkScalarToFloat(src[i].fY));
+ }
+ return dst;
+}
+
+void Path::Apply(void* info, PathApplierFunction function) const {
+ SkPath::RawIter iter(path_);
+ SkPoint pts[4];
+ PathElement path_element;
+ FloatPoint path_points[3];
+
+ for (;;) {
+ switch (iter.next(pts)) {
+ case SkPath::kMove_Verb:
+ path_element.type = kPathElementMoveToPoint;
+ path_element.points = ConvertPathPoints(path_points, &pts[0], 1);
+ break;
+ case SkPath::kLine_Verb:
+ path_element.type = kPathElementAddLineToPoint;
+ path_element.points = ConvertPathPoints(path_points, &pts[1], 1);
+ break;
+ case SkPath::kQuad_Verb:
+ path_element.type = kPathElementAddQuadCurveToPoint;
+ path_element.points = ConvertPathPoints(path_points, &pts[1], 2);
+ break;
+ case SkPath::kCubic_Verb:
+ path_element.type = kPathElementAddCurveToPoint;
+ path_element.points = ConvertPathPoints(path_points, &pts[1], 3);
+ break;
+ case SkPath::kConic_Verb: {
+ // Approximate with quads. Use two for now, increase if more precision
+ // is needed.
+ const int kPow2 = 1;
+ const unsigned kQuadCount = 1 << kPow2;
+ SkPoint quads[1 + 2 * kQuadCount];
+ SkPath::ConvertConicToQuads(pts[0], pts[1], pts[2], iter.conicWeight(),
+ quads, kPow2);
+
+ path_element.type = kPathElementAddQuadCurveToPoint;
+ for (unsigned i = 0; i < kQuadCount; ++i) {
+ path_element.points =
+ ConvertPathPoints(path_points, &quads[1 + 2 * i], 2);
+ function(info, &path_element);
+ }
+ continue;
+ }
+ case SkPath::kClose_Verb:
+ path_element.type = kPathElementCloseSubpath;
+ path_element.points = ConvertPathPoints(path_points, nullptr, 0);
+ break;
+ case SkPath::kDone_Verb:
+ return;
+ }
+ function(info, &path_element);
+ }
+}
+
+void Path::Transform(const AffineTransform& xform) {
+ path_.transform(AffineTransformToSkMatrix(xform));
+}
+
+float Path::length() const {
+ SkScalar length = 0;
+ SkPathMeasure measure(path_, false);
+
+ do {
+ length += measure.getLength();
+ } while (measure.nextContour());
+
+ return SkScalarToFloat(length);
+}
+
+FloatPoint Path::PointAtLength(float length) const {
+ FloatPoint point;
+ float normal;
+ PointAndNormalAtLength(length, point, normal);
+ return point;
+}
+
+static bool CalculatePointAndNormalOnPath(
+ SkPathMeasure& measure,
+ SkScalar length,
+ FloatPoint& point,
+ float& normal_angle,
+ SkScalar* accumulated_length = nullptr) {
+ do {
+ SkScalar contour_length = measure.getLength();
+ if (length <= contour_length) {
+ SkVector tangent;
+ SkPoint position;
+
+ if (measure.getPosTan(length, &position, &tangent)) {
+ normal_angle =
+ rad2deg(SkScalarToFloat(SkScalarATan2(tangent.fY, tangent.fX)));
+ point = FloatPoint(SkScalarToFloat(position.fX),
+ SkScalarToFloat(position.fY));
+ return true;
+ }
+ }
+ length -= contour_length;
+ if (accumulated_length)
+ *accumulated_length += contour_length;
+ } while (measure.nextContour());
+ return false;
+}
+
+void Path::PointAndNormalAtLength(float length,
+ FloatPoint& point,
+ float& normal) const {
+ SkPathMeasure measure(path_, false);
+ if (CalculatePointAndNormalOnPath(measure, WebCoreFloatToSkScalar(length),
+ point, normal))
+ return;
+
+ SkPoint position = path_.getPoint(0);
+ point =
+ FloatPoint(SkScalarToFloat(position.fX), SkScalarToFloat(position.fY));
+ normal = 0;
+}
+
+Path::PositionCalculator::PositionCalculator(const Path& path)
+ : path_(path.GetSkPath()),
+ path_measure_(path.GetSkPath(), false),
+ accumulated_length_(0) {}
+
+void Path::PositionCalculator::PointAndNormalAtLength(float length,
+ FloatPoint& point,
+ float& normal_angle) {
+ SkScalar sk_length = WebCoreFloatToSkScalar(length);
+ if (sk_length >= 0) {
+ if (sk_length < accumulated_length_) {
+ // Reset path measurer to rewind (and restart from 0).
+ path_measure_.setPath(&path_, false);
+ accumulated_length_ = 0;
+ } else {
+ sk_length -= accumulated_length_;
+ }
+
+ if (CalculatePointAndNormalOnPath(path_measure_, sk_length, point,
+ normal_angle, &accumulated_length_))
+ return;
+ }
+
+ SkPoint position = path_.getPoint(0);
+ point =
+ FloatPoint(SkScalarToFloat(position.fX), SkScalarToFloat(position.fY));
+ normal_angle = 0;
+ return;
+}
+
+void Path::Clear() {
+ path_.reset();
+}
+
+bool Path::IsEmpty() const {
+ return path_.isEmpty();
+}
+
+bool Path::IsClosed() const {
+ return path_.isLastContourClosed();
+}
+
+void Path::SetIsVolatile(bool is_volatile) {
+ path_.setIsVolatile(is_volatile);
+}
+
+bool Path::HasCurrentPoint() const {
+ return path_.getPoints(nullptr, 0);
+}
+
+FloatPoint Path::CurrentPoint() const {
+ if (path_.countPoints() > 0) {
+ SkPoint sk_result;
+ path_.getLastPt(&sk_result);
+ FloatPoint result;
+ result.SetX(SkScalarToFloat(sk_result.fX));
+ result.SetY(SkScalarToFloat(sk_result.fY));
+ return result;
+ }
+
+ // FIXME: Why does this return quietNaN? Other ports return 0,0.
+ float quiet_na_n = std::numeric_limits<float>::quiet_NaN();
+ return FloatPoint(quiet_na_n, quiet_na_n);
+}
+
+void Path::SetWindRule(const WindRule rule) {
+ path_.setFillType(WebCoreWindRuleToSkFillType(rule));
+}
+
+void Path::MoveTo(const FloatPoint& point) {
+ path_.moveTo(point.Data());
+}
+
+void Path::AddLineTo(const FloatPoint& point) {
+ path_.lineTo(point.Data());
+}
+
+void Path::AddQuadCurveTo(const FloatPoint& cp, const FloatPoint& ep) {
+ path_.quadTo(cp.Data(), ep.Data());
+}
+
+void Path::AddBezierCurveTo(const FloatPoint& p1,
+ const FloatPoint& p2,
+ const FloatPoint& ep) {
+ path_.cubicTo(p1.Data(), p2.Data(), ep.Data());
+}
+
+void Path::AddArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius) {
+ path_.arcTo(p1.Data(), p2.Data(), WebCoreFloatToSkScalar(radius));
+}
+
+void Path::AddArcTo(const FloatPoint& p,
+ const FloatSize& r,
+ float x_rotate,
+ bool large_arc,
+ bool sweep) {
+ path_.arcTo(WebCoreFloatToSkScalar(r.Width()),
+ WebCoreFloatToSkScalar(r.Height()),
+ WebCoreFloatToSkScalar(x_rotate),
+ large_arc ? SkPath::kLarge_ArcSize : SkPath::kSmall_ArcSize,
+ sweep ? SkPath::kCW_Direction : SkPath::kCCW_Direction,
+ WebCoreFloatToSkScalar(p.X()), WebCoreFloatToSkScalar(p.Y()));
+}
+
+void Path::CloseSubpath() {
+ path_.close();
+}
+
+void Path::AddEllipse(const FloatPoint& p,
+ float radius_x,
+ float radius_y,
+ float start_angle,
+ float end_angle,
+ bool anticlockwise) {
+ DCHECK(EllipseIsRenderable(start_angle, end_angle));
+ DCHECK_GE(start_angle, 0);
+ DCHECK_LT(start_angle, twoPiFloat);
+ DCHECK((anticlockwise && (start_angle - end_angle) >= 0) ||
+ (!anticlockwise && (end_angle - start_angle) >= 0));
+
+ SkScalar cx = WebCoreFloatToSkScalar(p.X());
+ SkScalar cy = WebCoreFloatToSkScalar(p.Y());
+ SkScalar radius_x_scalar = WebCoreFloatToSkScalar(radius_x);
+ SkScalar radius_y_scalar = WebCoreFloatToSkScalar(radius_y);
+
+ SkRect oval;
+ oval.set(cx - radius_x_scalar, cy - radius_y_scalar, cx + radius_x_scalar,
+ cy + radius_y_scalar);
+
+ float sweep = end_angle - start_angle;
+ SkScalar start_degrees = WebCoreFloatToSkScalar(start_angle * 180 / piFloat);
+ SkScalar sweep_degrees = WebCoreFloatToSkScalar(sweep * 180 / piFloat);
+ SkScalar s360 = SkIntToScalar(360);
+
+ // We can't use SkPath::addOval(), because addOval() makes a new sub-path.
+ // addOval() calls moveTo() and close() internally.
+
+ // Use s180, not s360, because SkPath::arcTo(oval, angle, s360, false) draws
+ // nothing.
+ SkScalar s180 = SkIntToScalar(180);
+ if (SkScalarNearlyEqual(sweep_degrees, s360)) {
+ // SkPath::arcTo can't handle the sweepAngle that is equal to or greater
+ // than 2Pi.
+ path_.arcTo(oval, start_degrees, s180, false);
+ path_.arcTo(oval, start_degrees + s180, s180, false);
+ return;
+ }
+ if (SkScalarNearlyEqual(sweep_degrees, -s360)) {
+ path_.arcTo(oval, start_degrees, -s180, false);
+ path_.arcTo(oval, start_degrees - s180, -s180, false);
+ return;
+ }
+
+ path_.arcTo(oval, start_degrees, sweep_degrees, false);
+}
+
+void Path::AddArc(const FloatPoint& p,
+ float radius,
+ float start_angle,
+ float end_angle,
+ bool anticlockwise) {
+ AddEllipse(p, radius, radius, start_angle, end_angle, anticlockwise);
+}
+
+void Path::AddRect(const FloatRect& rect) {
+ // Start at upper-left, add clock-wise.
+ path_.addRect(rect, SkPath::kCW_Direction, 0);
+}
+
+void Path::AddEllipse(const FloatPoint& p,
+ float radius_x,
+ float radius_y,
+ float rotation,
+ float start_angle,
+ float end_angle,
+ bool anticlockwise) {
+ DCHECK(EllipseIsRenderable(start_angle, end_angle));
+ DCHECK_GE(start_angle, 0);
+ DCHECK_LT(start_angle, twoPiFloat);
+ DCHECK((anticlockwise && (start_angle - end_angle) >= 0) ||
+ (!anticlockwise && (end_angle - start_angle) >= 0));
+
+ if (!rotation) {
+ AddEllipse(FloatPoint(p.X(), p.Y()), radius_x, radius_y, start_angle,
+ end_angle, anticlockwise);
+ return;
+ }
+
+ // Add an arc after the relevant transform.
+ AffineTransform ellipse_transform =
+ AffineTransform::Translation(p.X(), p.Y()).RotateRadians(rotation);
+ DCHECK(ellipse_transform.IsInvertible());
+ AffineTransform inverse_ellipse_transform = ellipse_transform.Inverse();
+ Transform(inverse_ellipse_transform);
+ AddEllipse(FloatPoint::Zero(), radius_x, radius_y, start_angle, end_angle,
+ anticlockwise);
+ Transform(ellipse_transform);
+}
+
+void Path::AddEllipse(const FloatRect& rect) {
+ // Start at 3 o'clock, add clock-wise.
+ path_.addOval(rect, SkPath::kCW_Direction, 1);
+}
+
+void Path::AddRoundedRect(const FloatRoundedRect& r) {
+ AddRoundedRect(r.Rect(), r.GetRadii().TopLeft(), r.GetRadii().TopRight(),
+ r.GetRadii().BottomLeft(), r.GetRadii().BottomRight());
+}
+
+void Path::AddRoundedRect(const FloatRect& rect,
+ const FloatSize& rounding_radii) {
+ if (rect.IsEmpty())
+ return;
+
+ FloatSize radius(rounding_radii);
+ FloatSize half_size(rect.Width() / 2, rect.Height() / 2);
+
+ // Apply the SVG corner radius constraints, per the rect section of the SVG
+ // shapes spec: if one of rx,ry is negative, then the other corner radius
+ // value is used. If both values are negative then rx = ry = 0. If rx is
+ // greater than half of the width of the rectangle then set rx to half of the
+ // width; ry is handled similarly.
+
+ if (radius.Width() < 0)
+ radius.SetWidth((radius.Height() < 0) ? 0 : radius.Height());
+
+ if (radius.Height() < 0)
+ radius.SetHeight(radius.Width());
+
+ if (radius.Width() > half_size.Width())
+ radius.SetWidth(half_size.Width());
+
+ if (radius.Height() > half_size.Height())
+ radius.SetHeight(half_size.Height());
+
+ AddPathForRoundedRect(rect, radius, radius, radius, radius);
+}
+
+void Path::AddRoundedRect(const FloatRect& rect,
+ const FloatSize& top_left_radius,
+ const FloatSize& top_right_radius,
+ const FloatSize& bottom_left_radius,
+ const FloatSize& bottom_right_radius) {
+ if (rect.IsEmpty())
+ return;
+
+ if (rect.Width() < top_left_radius.Width() + top_right_radius.Width() ||
+ rect.Width() < bottom_left_radius.Width() + bottom_right_radius.Width() ||
+ rect.Height() < top_left_radius.Height() + bottom_left_radius.Height() ||
+ rect.Height() <
+ top_right_radius.Height() + bottom_right_radius.Height()) {
+ // If all the radii cannot be accommodated, return a rect.
+ // FIXME: Is this an error scenario, given that it appears the code in
+ // FloatRoundedRect::constrainRadii() should be always called first? Should
+ // we assert that this code is not reached? This fallback is very bad, since
+ // it means that radii that are just barely too big due to rounding or
+ // snapping will get completely ignored.
+ AddRect(rect);
+ return;
+ }
+
+ AddPathForRoundedRect(rect, top_left_radius, top_right_radius,
+ bottom_left_radius, bottom_right_radius);
+}
+
+void Path::AddPathForRoundedRect(const FloatRect& rect,
+ const FloatSize& top_left_radius,
+ const FloatSize& top_right_radius,
+ const FloatSize& bottom_left_radius,
+ const FloatSize& bottom_right_radius) {
+ // Start at upper-left (after corner radii), add clock-wise.
+ path_.addRRect(FloatRoundedRect(rect, top_left_radius, top_right_radius,
+ bottom_left_radius, bottom_right_radius),
+ SkPath::kCW_Direction, 0);
+}
+
+void Path::AddPath(const Path& src, const AffineTransform& transform) {
+ path_.addPath(src.GetSkPath(), AffineTransformToSkMatrix(transform));
+}
+
+void Path::Translate(const FloatSize& size) {
+ path_.offset(WebCoreFloatToSkScalar(size.Width()),
+ WebCoreFloatToSkScalar(size.Height()));
+}
+
+bool Path::SubtractPath(const Path& other) {
+ return Op(path_, other.path_, kDifference_SkPathOp, &path_);
+}
+
+bool Path::UnionPath(const Path& other) {
+ return Op(path_, other.path_, kUnion_SkPathOp, &path_);
+}
+
+bool Path::IntersectPath(const Path& other) {
+ return Op(path_, other.path_, kIntersect_SkPathOp, &path_);
+}
+
+bool EllipseIsRenderable(float start_angle, float end_angle) {
+ return (std::abs(end_angle - start_angle) < twoPiFloat) ||
+ WebCoreFloatNearlyEqual(std::abs(end_angle - start_angle), twoPiFloat);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/path.h b/chromium/third_party/blink/renderer/platform/graphics/path.h
new file mode 100644
index 00000000000..058fc75be98
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/path.h
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2003, 2006, 2009 Apple Inc. All rights reserved.
+ * 2006 Rob Buis <buis@kde.org>
+ * Copyright (C) 2007-2008 Torch Mobile, Inc.
+ * Copyright (C) 2013 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PATH_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PATH_H_
+
+#include "base/macros.h"
+#include "third_party/blink/renderer/platform/geometry/float_rounded_rect.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_types.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/skia/include/core/SkPath.h"
+#include "third_party/skia/include/core/SkPathMeasure.h"
+
+namespace blink {
+
+class AffineTransform;
+class FloatPoint;
+class FloatRect;
+class FloatSize;
+class StrokeData;
+
+enum PathElementType {
+ kPathElementMoveToPoint, // The points member will contain 1 value.
+ kPathElementAddLineToPoint, // The points member will contain 1 value.
+ kPathElementAddQuadCurveToPoint, // The points member will contain 2 values.
+ kPathElementAddCurveToPoint, // The points member will contain 3 values.
+ kPathElementCloseSubpath // The points member will contain no values.
+};
+
+// The points in the structure are the same as those that would be used with the
+// add... method. For example, a line returns the endpoint, while a cubic
+// returns two tangent points and the endpoint.
+struct PathElement {
+ PathElementType type;
+ FloatPoint* points;
+};
+
+typedef void (*PathApplierFunction)(void* info, const PathElement*);
+
+class PLATFORM_EXPORT Path {
+ USING_FAST_MALLOC(Path);
+
+ public:
+ Path();
+ ~Path();
+
+ Path(const Path&);
+ Path(const SkPath&);
+ Path& operator=(const Path&);
+ Path& operator=(const SkPath&);
+ bool operator==(const Path&) const;
+
+ bool Contains(const FloatPoint&) const;
+ bool Contains(const FloatPoint&, WindRule) const;
+ bool StrokeContains(const FloatPoint&, const StrokeData&) const;
+
+ FloatRect BoundingRect() const;
+ FloatRect StrokeBoundingRect(const StrokeData&) const;
+
+ float length() const;
+ FloatPoint PointAtLength(float length) const;
+ void PointAndNormalAtLength(float length, FloatPoint&, float&) const;
+
+ // Helper for computing a sequence of positions and normals (normal angles) on
+ // a path. The best possible access pattern will be one where the |length|
+ // value is strictly increasing. For other access patterns, performance will
+ // vary depending on curvature and number of segments, but should never be
+ // worse than that of the state-less method on Path.
+ class PLATFORM_EXPORT PositionCalculator {
+ DISALLOW_COPY_AND_ASSIGN(PositionCalculator);
+ USING_FAST_MALLOC(PositionCalculator);
+
+ public:
+ explicit PositionCalculator(const Path&);
+
+ void PointAndNormalAtLength(float length, FloatPoint&, float&);
+
+ private:
+ SkPath path_;
+ SkPathMeasure path_measure_;
+ SkScalar accumulated_length_;
+ };
+
+ void Clear();
+ bool IsEmpty() const;
+ bool IsClosed() const;
+
+ // Specify whether this path is volatile. Temporary paths that are discarded
+ // or modified after use should be marked as volatile. This is a hint to the
+ // device to not cache this path.
+ void SetIsVolatile(bool);
+
+ // Gets the current point of the current path, which is conceptually the final
+ // point reached by the path so far. Note the Path can be empty
+ // (isEmpty() == true) and still have a current point.
+ bool HasCurrentPoint() const;
+ FloatPoint CurrentPoint() const;
+
+ void SetWindRule(const WindRule);
+
+ void MoveTo(const FloatPoint&);
+ void AddLineTo(const FloatPoint&);
+ void AddQuadCurveTo(const FloatPoint& control_point,
+ const FloatPoint& end_point);
+ void AddBezierCurveTo(const FloatPoint& control_point1,
+ const FloatPoint& control_point2,
+ const FloatPoint& end_point);
+ void AddArcTo(const FloatPoint&, const FloatPoint&, float radius);
+ void AddArcTo(const FloatPoint&,
+ const FloatSize& r,
+ float x_rotate,
+ bool large_arc,
+ bool sweep);
+ void CloseSubpath();
+
+ void AddArc(const FloatPoint&,
+ float radius,
+ float start_angle,
+ float end_angle,
+ bool anticlockwise);
+ void AddRect(const FloatRect&);
+ void AddEllipse(const FloatPoint&,
+ float radius_x,
+ float radius_y,
+ float rotation,
+ float start_angle,
+ float end_angle,
+ bool anticlockwise);
+ void AddEllipse(const FloatRect&);
+
+ void AddRoundedRect(const FloatRect&, const FloatSize& rounding_radii);
+ void AddRoundedRect(const FloatRect&,
+ const FloatSize& top_left_radius,
+ const FloatSize& top_right_radius,
+ const FloatSize& bottom_left_radius,
+ const FloatSize& bottom_right_radius);
+ void AddRoundedRect(const FloatRoundedRect&);
+
+ void AddPath(const Path&, const AffineTransform&);
+
+ void Translate(const FloatSize&);
+
+ const SkPath& GetSkPath() const { return path_; }
+
+ void Apply(void* info, PathApplierFunction) const;
+ void Transform(const AffineTransform&);
+
+ void AddPathForRoundedRect(const FloatRect&,
+ const FloatSize& top_left_radius,
+ const FloatSize& top_right_radius,
+ const FloatSize& bottom_left_radius,
+ const FloatSize& bottom_right_radius);
+
+ bool SubtractPath(const Path&);
+
+ // Updates the path to the union (inclusive-or) of itself with the given
+ // argument.
+ bool UnionPath(const Path& other);
+
+ bool IntersectPath(const Path& other);
+
+ private:
+ void AddEllipse(const FloatPoint&,
+ float radius_x,
+ float radius_y,
+ float start_angle,
+ float end_angle,
+ bool anticlockwise);
+ SkPath StrokePath(const StrokeData&) const;
+
+ SkPath path_;
+};
+
+class PLATFORM_EXPORT RefCountedPath : public Path,
+ public RefCounted<RefCountedPath> {
+ USING_FAST_MALLOC(RefCountedPath);
+
+ public:
+ template <typename... Args>
+ RefCountedPath(Args&&... args) : Path(std::forward<Args>(args)...) {}
+};
+
+// Only used for DCHECKs
+PLATFORM_EXPORT bool EllipseIsRenderable(float start_angle, float end_angle);
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/graphics/path_traversal_state.cc b/chromium/third_party/blink/renderer/platform/graphics/path_traversal_state.cc
new file mode 100644
index 00000000000..c60aaada846
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/path_traversal_state.cc
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2006, 2007 Eric Seidel <eric@webkit.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "third_party/blink/renderer/platform/graphics/path_traversal_state.h"
+
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+static inline FloatPoint MidPoint(const FloatPoint& first,
+ const FloatPoint& second) {
+ return FloatPoint((first.X() + second.X()) / 2.0f,
+ (first.Y() + second.Y()) / 2.0f);
+}
+
+static inline float DistanceLine(const FloatPoint& start,
+ const FloatPoint& end) {
+ return sqrtf((end.X() - start.X()) * (end.X() - start.X()) +
+ (end.Y() - start.Y()) * (end.Y() - start.Y()));
+}
+
+struct QuadraticBezier {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ QuadraticBezier() = default;
+ QuadraticBezier(const FloatPoint& s, const FloatPoint& c, const FloatPoint& e)
+ : start(s), control(c), end(e), split_depth(0) {}
+
+ double MagnitudeSquared() const {
+ return ((double)(start.Dot(start)) + (double)(control.Dot(control)) +
+ (double)(end.Dot(end))) /
+ 9.0;
+ }
+
+ float ApproximateDistance() const {
+ return DistanceLine(start, control) + DistanceLine(control, end);
+ }
+
+ void Split(QuadraticBezier& left, QuadraticBezier& right) const {
+ left.control = MidPoint(start, control);
+ right.control = MidPoint(control, end);
+
+ FloatPoint left_control_to_right_control =
+ MidPoint(left.control, right.control);
+ left.end = left_control_to_right_control;
+ right.start = left_control_to_right_control;
+
+ left.start = start;
+ right.end = end;
+
+ left.split_depth = right.split_depth = split_depth + 1;
+ }
+
+ FloatPoint start;
+ FloatPoint control;
+ FloatPoint end;
+ unsigned short split_depth;
+};
+
+struct CubicBezier {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ CubicBezier() = default;
+ CubicBezier(const FloatPoint& s,
+ const FloatPoint& c1,
+ const FloatPoint& c2,
+ const FloatPoint& e)
+ : start(s), control1(c1), control2(c2), end(e), split_depth(0) {}
+
+ double MagnitudeSquared() const {
+ return ((double)(start.Dot(start)) + (double)(control1.Dot(control1)) +
+ (double)(control2.Dot(control2)) + (double)(end.Dot(end))) /
+ 16.0;
+ }
+
+ float ApproximateDistance() const {
+ return DistanceLine(start, control1) + DistanceLine(control1, control2) +
+ DistanceLine(control2, end);
+ }
+
+ void Split(CubicBezier& left, CubicBezier& right) const {
+ FloatPoint start_to_control1 = MidPoint(control1, control2);
+
+ left.start = start;
+ left.control1 = MidPoint(start, control1);
+ left.control2 = MidPoint(left.control1, start_to_control1);
+
+ right.control2 = MidPoint(control2, end);
+ right.control1 = MidPoint(right.control2, start_to_control1);
+ right.end = end;
+
+ FloatPoint left_control2_to_right_control1 =
+ MidPoint(left.control2, right.control1);
+ left.end = left_control2_to_right_control1;
+ right.start = left_control2_to_right_control1;
+
+ left.split_depth = right.split_depth = split_depth + 1;
+ }
+
+ FloatPoint start;
+ FloatPoint control1;
+ FloatPoint control2;
+ FloatPoint end;
+ unsigned short split_depth;
+};
+
+template <class CurveType>
+static float CurveLength(PathTraversalState& traversal_state, CurveType curve) {
+ static const unsigned short kCurveSplitDepthLimit = 20;
+ static const double kPathSegmentLengthToleranceSquared = 1.e-16;
+
+ double curve_scale_for_tolerance_squared = curve.MagnitudeSquared();
+ if (curve_scale_for_tolerance_squared < kPathSegmentLengthToleranceSquared)
+ return 0;
+
+ Vector<CurveType> curve_stack;
+ curve_stack.push_back(curve);
+
+ float total_length = 0;
+ do {
+ float length = curve.ApproximateDistance();
+ double length_discrepancy = length - DistanceLine(curve.start, curve.end);
+ if ((length_discrepancy * length_discrepancy) /
+ curve_scale_for_tolerance_squared >
+ kPathSegmentLengthToleranceSquared &&
+ curve.split_depth < kCurveSplitDepthLimit) {
+ CurveType left_curve;
+ CurveType right_curve;
+ curve.Split(left_curve, right_curve);
+ curve = left_curve;
+ curve_stack.push_back(right_curve);
+ } else {
+ total_length += length;
+ if (traversal_state.action_ ==
+ PathTraversalState::kTraversalPointAtLength ||
+ traversal_state.action_ ==
+ PathTraversalState::kTraversalNormalAngleAtLength) {
+ traversal_state.previous_ = curve.start;
+ traversal_state.current_ = curve.end;
+ if (traversal_state.total_length_ + total_length >
+ traversal_state.desired_length_)
+ return total_length;
+ }
+ curve = curve_stack.back();
+ curve_stack.pop_back();
+ }
+ } while (!curve_stack.IsEmpty());
+
+ return total_length;
+}
+
+PathTraversalState::PathTraversalState(PathTraversalAction action)
+ : action_(action),
+ success_(false),
+ total_length_(0),
+ desired_length_(0),
+ normal_angle_(0) {}
+
+float PathTraversalState::CloseSubpath() {
+ float distance = DistanceLine(current_, start_);
+ current_ = start_;
+ return distance;
+}
+
+float PathTraversalState::MoveTo(const FloatPoint& point) {
+ current_ = start_ = point;
+ return 0;
+}
+
+float PathTraversalState::LineTo(const FloatPoint& point) {
+ float distance = DistanceLine(current_, point);
+ current_ = point;
+ return distance;
+}
+
+float PathTraversalState::CubicBezierTo(const FloatPoint& new_control1,
+ const FloatPoint& new_control2,
+ const FloatPoint& new_end) {
+ float distance = CurveLength<CubicBezier>(
+ *this, CubicBezier(current_, new_control1, new_control2, new_end));
+
+ if (action_ != kTraversalPointAtLength &&
+ action_ != kTraversalNormalAngleAtLength)
+ current_ = new_end;
+
+ return distance;
+}
+
+void PathTraversalState::ProcessSegment() {
+ if ((action_ == kTraversalPointAtLength ||
+ action_ == kTraversalNormalAngleAtLength) &&
+ total_length_ >= desired_length_) {
+ float slope = FloatPoint(current_ - previous_).SlopeAngleRadians();
+ if (action_ == kTraversalPointAtLength) {
+ float offset = desired_length_ - total_length_;
+ current_.Move(offset * cosf(slope), offset * sinf(slope));
+ } else {
+ normal_angle_ = rad2deg(slope);
+ }
+ success_ = true;
+ }
+ previous_ = current_;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/path_traversal_state.h b/chromium/third_party/blink/renderer/platform/graphics/path_traversal_state.h
new file mode 100644
index 00000000000..ef4e7cc1130
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/path_traversal_state.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2006, 2007 Eric Seidel <eric@webkit.org>
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PATH_TRAVERSAL_STATE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PATH_TRAVERSAL_STATE_H_
+
+#include "base/macros.h"
+#include "third_party/blink/renderer/platform/geometry/float_point.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT PathTraversalState final {
+ STACK_ALLOCATED();
+ DISALLOW_COPY_AND_ASSIGN(PathTraversalState);
+
+ public:
+ enum PathTraversalAction {
+ kTraversalTotalLength,
+ kTraversalPointAtLength,
+ kTraversalNormalAngleAtLength
+ };
+
+ PathTraversalState(PathTraversalAction);
+
+ float CloseSubpath();
+ float MoveTo(const FloatPoint&);
+ float LineTo(const FloatPoint&);
+ float CubicBezierTo(const FloatPoint& new_control1,
+ const FloatPoint& new_control2,
+ const FloatPoint& new_end);
+
+ void ProcessSegment();
+
+ public:
+ PathTraversalAction action_;
+ bool success_;
+
+ FloatPoint current_;
+ FloatPoint start_;
+
+ float total_length_;
+ float desired_length_;
+
+ // For normal calculations
+ FloatPoint previous_;
+ float normal_angle_; // degrees
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/graphics/pattern.cc b/chromium/third_party/blink/renderer/platform/graphics/pattern.cc
new file mode 100644
index 00000000000..45512c279b0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/pattern.cc
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2006, 2007, 2008 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2008 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2013 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/graphics/pattern.h"
+
+#include "third_party/blink/renderer/platform/graphics/image_pattern.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_record.h"
+#include "third_party/blink/renderer/platform/graphics/paint_record_pattern.h"
+#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
+#include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/core/SkShader.h"
+
+namespace blink {
+
+scoped_refptr<Pattern> Pattern::CreateImagePattern(
+ scoped_refptr<Image> tile_image,
+ RepeatMode repeat_mode) {
+ return ImagePattern::Create(std::move(tile_image), repeat_mode);
+}
+
+scoped_refptr<Pattern> Pattern::CreatePaintRecordPattern(
+ sk_sp<PaintRecord> record,
+ const FloatRect& record_bounds,
+ RepeatMode repeat_mode) {
+ return PaintRecordPattern::Create(std::move(record), record_bounds,
+ repeat_mode);
+}
+
+Pattern::Pattern(RepeatMode repeat_mode) : repeat_mode_(repeat_mode) {}
+
+Pattern::~Pattern() = default;
+
+void Pattern::ApplyToFlags(PaintFlags& flags, const SkMatrix& local_matrix) {
+ if (!cached_shader_ || IsLocalMatrixChanged(local_matrix))
+ cached_shader_ = CreateShader(local_matrix);
+
+ flags.setShader(cached_shader_);
+}
+
+bool Pattern::IsLocalMatrixChanged(const SkMatrix& local_matrix) const {
+ return local_matrix != cached_shader_->GetLocalMatrix();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/pattern.h b/chromium/third_party/blink/renderer/platform/graphics/pattern.h
new file mode 100644
index 00000000000..72bb655b20a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/pattern.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2006, 2007, 2008 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2008 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2007-2008 Torch Mobile, Inc.
+ * Copyright (C) 2013 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PATTERN_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PATTERN_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/graphics/image.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_record.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_shader.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+class SkMatrix;
+
+namespace blink {
+
+class PLATFORM_EXPORT Pattern : public RefCounted<Pattern> {
+ WTF_MAKE_NONCOPYABLE(Pattern);
+
+ public:
+ enum RepeatMode {
+ kRepeatModeX = 1 << 0,
+ kRepeatModeY = 1 << 1,
+
+ kRepeatModeNone = 0,
+ kRepeatModeXY = kRepeatModeX | kRepeatModeY
+ };
+
+ static scoped_refptr<Pattern> CreateImagePattern(scoped_refptr<Image>,
+ RepeatMode = kRepeatModeXY);
+ static scoped_refptr<Pattern> CreatePaintRecordPattern(
+ sk_sp<PaintRecord>,
+ const FloatRect& record_bounds,
+ RepeatMode = kRepeatModeXY);
+ virtual ~Pattern();
+
+ void ApplyToFlags(PaintFlags&, const SkMatrix&);
+
+ bool IsRepeatX() const { return repeat_mode_ & kRepeatModeX; }
+ bool IsRepeatY() const { return repeat_mode_ & kRepeatModeY; }
+ bool IsRepeatXY() const { return repeat_mode_ == kRepeatModeXY; }
+
+ virtual bool IsTextureBacked() const { return false; }
+
+ protected:
+ virtual sk_sp<PaintShader> CreateShader(const SkMatrix&) = 0;
+ virtual bool IsLocalMatrixChanged(const SkMatrix&) const;
+
+ RepeatMode repeat_mode_;
+
+ Pattern(RepeatMode);
+ mutable sk_sp<PaintShader> cached_shader_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/graphics/picture_snapshot.cc b/chromium/third_party/blink/renderer/platform/graphics/picture_snapshot.cc
new file mode 100644
index 00000000000..4e674990b21
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/picture_snapshot.cc
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/renderer/platform/graphics/picture_snapshot.h"
+
+#include <memory>
+#include "third_party/blink/renderer/platform/geometry/int_size.h"
+#include "third_party/blink/renderer/platform/graphics/logging_canvas.h"
+#include "third_party/blink/renderer/platform/graphics/profiling_canvas.h"
+#include "third_party/blink/renderer/platform/graphics/replaying_canvas.h"
+#include "third_party/blink/renderer/platform/graphics/skia/image_pixel_locker.h"
+#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h"
+#include "third_party/blink/renderer/platform/image-decoders/image_frame.h"
+#include "third_party/blink/renderer/platform/image-decoders/segment_reader.h"
+#include "third_party/blink/renderer/platform/image-encoders/image_encoder.h"
+#include "third_party/blink/renderer/platform/wtf/hex_number.h"
+#include "third_party/blink/renderer/platform/wtf/text/base64.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+#include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/core/SkPictureRecorder.h"
+
+namespace blink {
+
+PictureSnapshot::PictureSnapshot(sk_sp<const SkPicture> picture)
+ : picture_(std::move(picture)) {}
+
+scoped_refptr<PictureSnapshot> PictureSnapshot::Load(
+ const Vector<scoped_refptr<TilePictureStream>>& tiles) {
+ DCHECK(!tiles.IsEmpty());
+ Vector<sk_sp<SkPicture>> pictures;
+ pictures.ReserveCapacity(tiles.size());
+ FloatRect union_rect;
+ for (const auto& tile_stream : tiles) {
+ sk_sp<SkPicture> picture = SkPicture::MakeFromData(
+ tile_stream->data.begin(), tile_stream->data.size());
+ if (!picture)
+ return nullptr;
+ FloatRect cull_rect(picture->cullRect());
+ cull_rect.MoveBy(tile_stream->layer_offset);
+ union_rect.Unite(cull_rect);
+ pictures.push_back(std::move(picture));
+ }
+ if (tiles.size() == 1)
+ return base::AdoptRef(new PictureSnapshot(std::move(pictures[0])));
+ SkPictureRecorder recorder;
+ SkCanvas* canvas = recorder.beginRecording(union_rect.Width(),
+ union_rect.Height(), nullptr, 0);
+ for (size_t i = 0; i < pictures.size(); ++i) {
+ canvas->save();
+ canvas->translate(tiles[i]->layer_offset.X() - union_rect.X(),
+ tiles[i]->layer_offset.Y() - union_rect.Y());
+ pictures[i]->playback(canvas, nullptr);
+ canvas->restore();
+ }
+ return base::AdoptRef(
+ new PictureSnapshot(recorder.finishRecordingAsPicture()));
+}
+
+bool PictureSnapshot::IsEmpty() const {
+ return picture_->cullRect().isEmpty();
+}
+
+std::unique_ptr<Vector<char>> PictureSnapshot::Replay(unsigned from_step,
+ unsigned to_step,
+ double scale) const {
+ const SkIRect bounds = picture_->cullRect().roundOut();
+ int width = ceil(scale * bounds.width());
+ int height = ceil(scale * bounds.height());
+
+ // TODO(fmalita): convert this to SkSurface/SkImage, drop the intermediate
+ // SkBitmap.
+ SkBitmap bitmap;
+ bitmap.allocPixels(SkImageInfo::MakeN32Premul(width, height));
+ bitmap.eraseARGB(0, 0, 0, 0);
+ {
+ ReplayingCanvas canvas(bitmap, from_step, to_step);
+ // Disable LCD text preemptively, because the picture opacity is unknown.
+ // The canonical API involves SkSurface props, but since we're not
+ // SkSurface-based at this point (see TODO above) we (ab)use saveLayer for
+ // this purpose.
+ SkAutoCanvasRestore auto_restore(&canvas, false);
+ canvas.saveLayer(nullptr, nullptr);
+
+ canvas.scale(scale, scale);
+ canvas.ResetStepCount();
+ picture_->playback(&canvas, &canvas);
+ }
+ std::unique_ptr<Vector<char>> base64_data = std::make_unique<Vector<char>>();
+ Vector<char> encoded_image;
+
+ SkPixmap src;
+ bool peekResult = bitmap.peekPixels(&src);
+ DCHECK(peekResult);
+
+ SkPngEncoder::Options options;
+ options.fFilterFlags = SkPngEncoder::FilterFlag::kSub;
+ options.fZLibLevel = 3;
+ options.fUnpremulBehavior = SkTransferFunctionBehavior::kIgnore;
+ if (!ImageEncoder::Encode(
+ reinterpret_cast<Vector<unsigned char>*>(&encoded_image), src,
+ options)) {
+ return nullptr;
+ }
+
+ Base64Encode(encoded_image, *base64_data);
+ return base64_data;
+}
+
+std::unique_ptr<PictureSnapshot::Timings> PictureSnapshot::Profile(
+ unsigned min_repeat_count,
+ double min_duration,
+ const FloatRect* clip_rect) const {
+ std::unique_ptr<PictureSnapshot::Timings> timings =
+ std::make_unique<PictureSnapshot::Timings>();
+ timings->ReserveCapacity(min_repeat_count);
+ const SkIRect bounds = picture_->cullRect().roundOut();
+ SkBitmap bitmap;
+ bitmap.allocPixels(
+ SkImageInfo::MakeN32Premul(bounds.width(), bounds.height()));
+ bitmap.eraseARGB(0, 0, 0, 0);
+
+ double now = WTF::CurrentTimeTicksInSeconds();
+ double stop_time = now + min_duration;
+ for (unsigned step = 0; step < min_repeat_count || now < stop_time; ++step) {
+ timings->push_back(Vector<double>());
+ Vector<double>* current_timings = &timings->back();
+ if (timings->size() > 1)
+ current_timings->ReserveCapacity(timings->begin()->size());
+ ProfilingCanvas canvas(bitmap);
+ if (clip_rect) {
+ canvas.clipRect(SkRect::MakeXYWH(clip_rect->X(), clip_rect->Y(),
+ clip_rect->Width(),
+ clip_rect->Height()));
+ canvas.ResetStepCount();
+ }
+ canvas.SetTimings(current_timings);
+ picture_->playback(&canvas);
+ now = WTF::CurrentTimeTicksInSeconds();
+ }
+ return timings;
+}
+
+std::unique_ptr<JSONArray> PictureSnapshot::SnapshotCommandLog() const {
+ LoggingCanvas canvas;
+ picture_->playback(&canvas);
+ return canvas.Log();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/picture_snapshot.h b/chromium/third_party/blink/renderer/platform/graphics/picture_snapshot.h
new file mode 100644
index 00000000000..6a7b1ae4b6b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/picture_snapshot.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PICTURE_SNAPSHOT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PICTURE_SNAPSHOT_H_
+
+#include <memory>
+
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/json/json_values.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/skia/include/core/SkPicture.h"
+#include "third_party/skia/include/core/SkPictureRecorder.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+namespace blink {
+
+class FloatRect;
+
+class PLATFORM_EXPORT PictureSnapshot : public RefCounted<PictureSnapshot> {
+ WTF_MAKE_NONCOPYABLE(PictureSnapshot);
+
+ public:
+ typedef Vector<Vector<double>> Timings;
+
+ struct TilePictureStream : RefCounted<TilePictureStream> {
+ FloatPoint layer_offset;
+ Vector<char> data;
+ };
+
+ static scoped_refptr<PictureSnapshot> Load(
+ const Vector<scoped_refptr<TilePictureStream>>&);
+
+ PictureSnapshot(sk_sp<const SkPicture>);
+
+ std::unique_ptr<Vector<char>> Replay(unsigned from_step = 0,
+ unsigned to_step = 0,
+ double scale = 1.0) const;
+ std::unique_ptr<Timings> Profile(unsigned min_iterations,
+ double min_duration,
+ const FloatRect* clip_rect) const;
+ std::unique_ptr<JSONArray> SnapshotCommandLog() const;
+ bool IsEmpty() const;
+
+ private:
+ std::unique_ptr<SkBitmap> CreateBitmap() const;
+
+ sk_sp<const SkPicture> picture_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PICTURE_SNAPSHOT_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/placeholder_image.cc b/chromium/third_party/blink/renderer/platform/graphics/placeholder_image.cc
new file mode 100644
index 00000000000..bce17741869
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/placeholder_image.cc
@@ -0,0 +1,332 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/placeholder_image.h"
+
+#include <utility>
+
+#include "third_party/blink/renderer/platform/fonts/font.h"
+#include "third_party/blink/renderer/platform/fonts/font_description.h"
+#include "third_party/blink/renderer/platform/fonts/font_family.h"
+#include "third_party/blink/renderer/platform/fonts/font_selection_types.h"
+#include "third_party/blink/renderer/platform/geometry/float_point.h"
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/geometry/int_point.h"
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/graphics/bitmap_image.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/image_observer.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_record.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_recorder.h"
+#include "third_party/blink/renderer/platform/text/platform_locale.h"
+#include "third_party/blink/renderer/platform/text/text_run.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkRect.h"
+#include "third_party/skia/include/core/SkSize.h"
+
+namespace blink {
+
+namespace {
+
+// Placeholder image visual specifications:
+// https://docs.google.com/document/d/1BHeA1azbgCdZgCnr16VN2g7A9MHPQ_dwKn5szh8evMQ/edit
+
+constexpr int kIconWidth = 24;
+constexpr int kIconHeight = 24;
+constexpr int kFeaturePaddingX = 8;
+constexpr int kIconPaddingY = 5;
+constexpr int kPaddingBetweenIconAndText = 2;
+constexpr int kTextPaddingY = 9;
+
+constexpr int kFontSize = 14;
+
+void DrawIcon(PaintCanvas* canvas, const PaintFlags& flags, float x, float y) {
+ DEFINE_STATIC_REF(Image, icon_image,
+ (Image::LoadPlatformResource("placeholderIcon")));
+ DCHECK(!icon_image->IsNull());
+
+ // Note that the |icon_image| is not scaled according to dest_rect / src_rect,
+ // and is always drawn at the same size. This is so that placeholder icons are
+ // visible (e.g. when replacing a large image that's scaled down to a small
+ // area) and so that all placeholder images on the same page look consistent.
+ canvas->drawImageRect(icon_image->PaintImageForCurrentFrame(),
+ IntRect(IntPoint::Zero(), icon_image->Size()),
+ FloatRect(x, y, kIconWidth, kIconHeight), &flags,
+ PaintCanvas::kFast_SrcRectConstraint);
+}
+
+void DrawCenteredIcon(PaintCanvas* canvas,
+ const PaintFlags& flags,
+ const FloatRect& dest_rect) {
+ DrawIcon(canvas, flags,
+ dest_rect.X() + (dest_rect.Width() - kIconWidth) / 2.0f,
+ dest_rect.Y() + (dest_rect.Height() - kIconHeight) / 2.0f);
+}
+
+FontDescription CreatePlaceholderFontDescription() {
+ FontDescription description;
+ description.FirstFamily().SetFamily("Roboto");
+
+ scoped_refptr<SharedFontFamily> helvetica_neue = SharedFontFamily::Create();
+ helvetica_neue->SetFamily("Helvetica Neue");
+ scoped_refptr<SharedFontFamily> helvetica = SharedFontFamily::Create();
+ helvetica->SetFamily("Helvetica");
+ scoped_refptr<SharedFontFamily> arial = SharedFontFamily::Create();
+ arial->SetFamily("Arial");
+
+ helvetica->AppendFamily(std::move(arial));
+ helvetica_neue->AppendFamily(std::move(helvetica));
+ description.FirstFamily().AppendFamily(std::move(helvetica_neue));
+
+ description.SetGenericFamily(FontDescription::kSansSerifFamily);
+ description.SetComputedSize(kFontSize);
+ description.SetWeight(FontSelectionValue(500));
+
+ return description;
+}
+
+// Return a byte quantity as a string in a localized human-readable format,
+// suitable for being shown on a placeholder image to indicate the full original
+// size of the resource.
+//
+// Ex: FormatOriginalResourceSizeBytes(100) => "1 KB"
+// Ex: FormatOriginalResourceSizeBytes(102401) => "100 KB"
+// Ex: FormatOriginalResourceSizeBytes(1740800) => "1.7 MB"
+//
+// See the placeholder image number format specifications for more info:
+// https://docs.google.com/document/d/1BHeA1azbgCdZgCnr16VN2g7A9MHPQ_dwKn5szh8evMQ/edit#heading=h.d135l9z7tn0a
+String FormatOriginalResourceSizeBytes(int64_t bytes) {
+ DCHECK_LT(0, bytes);
+
+ static constexpr WebLocalizedString::Name kUnitsNames[] = {
+ WebLocalizedString::kUnitsKibibytes, WebLocalizedString::kUnitsMebibytes,
+ WebLocalizedString::kUnitsGibibytes, WebLocalizedString::kUnitsTebibytes,
+ WebLocalizedString::kUnitsPebibytes};
+
+ // Start with KB. The formatted text will be at least "1 KB", with any smaller
+ // amounts being rounded up to "1 KB".
+ const WebLocalizedString::Name* units = kUnitsNames;
+ int64_t denomenator = 1024;
+
+ // Find the smallest unit that can represent |bytes| in 3 digits or less.
+ // Round up to the next higher unit if possible when it would take 4 digits to
+ // display the amount, e.g. 1000 KB will be rounded up to 1 MB.
+ for (; units < kUnitsNames + (arraysize(kUnitsNames) - 1) &&
+ bytes >= denomenator * 1000;
+ ++units, denomenator *= 1024) {
+ }
+
+ String numeric_string;
+ if (bytes < denomenator) {
+ // Round up to 1.
+ numeric_string = String::Number(1);
+ } else if (units != kUnitsNames && bytes < denomenator * 10) {
+ // For amounts between 1 and 10 units and larger than 1 MB, allow up to one
+ // fractional digit.
+ numeric_string = String::Number(
+ static_cast<double>(bytes) / static_cast<double>(denomenator), 2);
+ } else {
+ numeric_string = String::Number(bytes / denomenator);
+ }
+
+ Locale& locale = Locale::DefaultLocale();
+ // Locale::QueryString() will return an empty string if the embedder hasn't
+ // defined the string resources for the units, which will cause the
+ // PlaceholderImage to not show any text.
+ return locale.QueryString(*units,
+ locale.ConvertToLocalizedNumber(numeric_string));
+}
+
+} // namespace
+
+// A simple RefCounted wrapper around a Font, so that multiple PlaceholderImages
+// can share the same Font.
+class PlaceholderImage::SharedFont : public RefCounted<SharedFont> {
+ public:
+ static scoped_refptr<SharedFont> GetOrCreateInstance() {
+ if (g_instance_)
+ return scoped_refptr<SharedFont>(g_instance_);
+
+ scoped_refptr<SharedFont> shared_font(base::AdoptRef(new SharedFont()));
+ g_instance_ = shared_font.get();
+ return shared_font;
+ }
+
+ ~SharedFont() {
+ DCHECK_EQ(this, g_instance_);
+ g_instance_ = nullptr;
+ }
+
+ const Font& font() const { return font_; }
+
+ private:
+ SharedFont() : font_(CreatePlaceholderFontDescription()) {
+ font_.Update(nullptr);
+ }
+
+ static SharedFont* g_instance_;
+ Font font_;
+};
+
+// static
+PlaceholderImage::SharedFont* PlaceholderImage::SharedFont::g_instance_ =
+ nullptr;
+
+PlaceholderImage::PlaceholderImage(ImageObserver* observer,
+ const IntSize& size,
+ int64_t original_resource_size)
+ : Image(observer),
+ size_(size),
+ text_(original_resource_size <= 0
+ ? String()
+ : FormatOriginalResourceSizeBytes(original_resource_size)),
+ paint_record_content_id_(-1) {}
+
+PlaceholderImage::~PlaceholderImage() = default;
+
+IntSize PlaceholderImage::Size() const {
+ return size_;
+}
+
+bool PlaceholderImage::IsPlaceholderImage() const {
+ return true;
+}
+
+bool PlaceholderImage::CurrentFrameHasSingleSecurityOrigin() const {
+ return true;
+}
+
+bool PlaceholderImage::CurrentFrameKnownToBeOpaque() {
+ // Placeholder images are translucent.
+ return false;
+}
+
+PaintImage PlaceholderImage::PaintImageForCurrentFrame() {
+ auto builder = CreatePaintImageBuilder().set_completion_state(
+ PaintImage::CompletionState::DONE);
+
+ const IntRect dest_rect(0, 0, size_.Width(), size_.Height());
+ if (paint_record_for_current_frame_) {
+ return builder
+ .set_paint_record(paint_record_for_current_frame_, dest_rect,
+ paint_record_content_id_)
+ .TakePaintImage();
+ }
+
+ PaintRecorder paint_recorder;
+ Draw(paint_recorder.beginRecording(FloatRect(dest_rect)), PaintFlags(),
+ FloatRect(dest_rect), FloatRect(dest_rect),
+ kDoNotRespectImageOrientation, kClampImageToSourceRect, kSyncDecode);
+
+ paint_record_for_current_frame_ = paint_recorder.finishRecordingAsPicture();
+ paint_record_content_id_ = PaintImage::GetNextContentId();
+ return builder
+ .set_paint_record(paint_record_for_current_frame_, dest_rect,
+ paint_record_content_id_)
+ .TakePaintImage();
+}
+
+void PlaceholderImage::Draw(PaintCanvas* canvas,
+ const PaintFlags& base_flags,
+ const FloatRect& dest_rect,
+ const FloatRect& src_rect,
+ RespectImageOrientationEnum respect_orientation,
+ ImageClampingMode image_clamping_mode,
+ ImageDecodingMode decode_mode) {
+ if (!src_rect.Intersects(FloatRect(0.0f, 0.0f,
+ static_cast<float>(size_.Width()),
+ static_cast<float>(size_.Height())))) {
+ return;
+ }
+
+ PaintFlags flags(base_flags);
+ flags.setStyle(PaintFlags::kFill_Style);
+ flags.setColor(SkColorSetARGB(0x80, 0xD9, 0xD9, 0xD9));
+ canvas->drawRect(dest_rect, flags);
+
+ if (dest_rect.Width() < kIconWidth + 2 * kFeaturePaddingX ||
+ dest_rect.Height() < kIconHeight + 2 * kIconPaddingY) {
+ return;
+ }
+
+ if (text_.IsEmpty()) {
+ DrawCenteredIcon(canvas, base_flags, dest_rect);
+ return;
+ }
+
+ if (!shared_font_)
+ shared_font_ = SharedFont::GetOrCreateInstance();
+ if (!cached_text_width_.has_value())
+ cached_text_width_ = shared_font_->font().Width(TextRun(text_));
+
+ const float icon_and_text_width =
+ cached_text_width_.value() +
+ (kIconWidth + 2 * kFeaturePaddingX + kPaddingBetweenIconAndText);
+
+ if (dest_rect.Width() < icon_and_text_width) {
+ DrawCenteredIcon(canvas, base_flags, dest_rect);
+ return;
+ }
+
+ const float feature_x =
+ dest_rect.X() + (dest_rect.Width() - icon_and_text_width) / 2.0f;
+ const float feature_y =
+ dest_rect.Y() +
+ (dest_rect.Height() - (kIconHeight + 2 * kIconPaddingY)) / 2.0f;
+
+ float icon_x, text_x;
+ if (Locale::DefaultLocale().IsRTL()) {
+ icon_x = feature_x + cached_text_width_.value() +
+ (kFeaturePaddingX + kPaddingBetweenIconAndText);
+ text_x = feature_x + kFeaturePaddingX;
+ } else {
+ icon_x = feature_x + kFeaturePaddingX;
+ text_x = feature_x +
+ (kFeaturePaddingX + kIconWidth + kPaddingBetweenIconAndText);
+ }
+
+ DrawIcon(canvas, base_flags, icon_x, feature_y + kIconPaddingY);
+
+ flags.setColor(SkColorSetARGB(0xAB, 0, 0, 0));
+ shared_font_->font().DrawBidiText(
+ canvas, TextRunPaintInfo(TextRun(text_)),
+ FloatPoint(text_x, feature_y + (kTextPaddingY + kFontSize)),
+ Font::kUseFallbackIfFontNotReady, 1.0f, flags);
+}
+
+void PlaceholderImage::DrawPattern(GraphicsContext& context,
+ const FloatRect& src_rect,
+ const FloatSize& scale,
+ const FloatPoint& phase,
+ SkBlendMode mode,
+ const FloatRect& dest_rect,
+ const FloatSize& repeat_spacing) {
+ DCHECK(context.Canvas());
+
+ PaintFlags flags = context.FillFlags();
+ flags.setBlendMode(mode);
+
+ // Ignore the pattern specifications and just draw a single placeholder image
+ // over the whole |dest_rect|. This is done in order to prevent repeated icons
+ // from cluttering tiled background images.
+ Draw(context.Canvas(), flags, dest_rect, src_rect,
+ kDoNotRespectImageOrientation, kClampImageToSourceRect,
+ kUnspecifiedDecode);
+}
+
+void PlaceholderImage::DestroyDecodedData() {
+ paint_record_for_current_frame_.reset();
+ shared_font_ = scoped_refptr<SharedFont>();
+}
+
+Image::SizeAvailability PlaceholderImage::SetData(scoped_refptr<SharedBuffer>,
+ bool) {
+ return Image::kSizeAvailable;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/placeholder_image.h b/chromium/third_party/blink/renderer/platform/graphics/placeholder_image.h
new file mode 100644
index 00000000000..e3398f05e31
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/placeholder_image.h
@@ -0,0 +1,94 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PLACEHOLDER_IMAGE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PLACEHOLDER_IMAGE_H_
+
+#include <stdint.h>
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/geometry/int_size.h"
+#include "third_party/blink/renderer/platform/graphics/image.h"
+#include "third_party/blink/renderer/platform/graphics/image_orientation.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+namespace blink {
+
+class FloatPoint;
+class FloatRect;
+class FloatSize;
+class GraphicsContext;
+class ImageObserver;
+
+// A generated placeholder image that shows a translucent gray rectangle.
+class PLATFORM_EXPORT PlaceholderImage final : public Image {
+ public:
+ static scoped_refptr<PlaceholderImage> Create(
+ ImageObserver* observer,
+ const IntSize& size,
+ int64_t original_resource_size) {
+ return base::AdoptRef(
+ new PlaceholderImage(observer, size, original_resource_size));
+ }
+
+ ~PlaceholderImage() override;
+
+ IntSize Size() const override;
+
+ void Draw(PaintCanvas*,
+ const PaintFlags&,
+ const FloatRect& dest_rect,
+ const FloatRect& src_rect,
+ RespectImageOrientationEnum,
+ ImageClampingMode,
+ ImageDecodingMode) override;
+
+ void DestroyDecodedData() override;
+
+ PaintImage PaintImageForCurrentFrame() override;
+
+ bool IsPlaceholderImage() const override;
+
+ const String& GetTextForTesting() const { return text_; }
+
+ private:
+ PlaceholderImage(ImageObserver*,
+ const IntSize&,
+ int64_t original_resource_size);
+
+ bool CurrentFrameHasSingleSecurityOrigin() const override;
+
+ bool CurrentFrameKnownToBeOpaque() override;
+
+ void DrawPattern(GraphicsContext&,
+ const FloatRect& src_rect,
+ const FloatSize& scale,
+ const FloatPoint& phase,
+ SkBlendMode,
+ const FloatRect& dest_rect,
+ const FloatSize& repeat_spacing) override;
+
+ // SetData does nothing, and the passed in buffer is ignored.
+ SizeAvailability SetData(scoped_refptr<SharedBuffer>, bool) override;
+
+ const IntSize size_;
+ const String text_;
+
+ class SharedFont;
+ // Lazily initialized. All instances of PlaceholderImage will share the same
+ // Font object, wrapped as a SharedFont.
+ scoped_refptr<SharedFont> shared_font_;
+
+ // Lazily initialized.
+ Optional<float> cached_text_width_;
+ sk_sp<PaintRecord> paint_record_for_current_frame_;
+ PaintImage::ContentId paint_record_content_id_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/graphics/placeholder_image_test.cc b/chromium/third_party/blink/renderer/platform/graphics/placeholder_image_test.cc
new file mode 100644
index 00000000000..5df5aca22ec
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/placeholder_image_test.cc
@@ -0,0 +1,89 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/placeholder_image.h"
+
+#include <stdint.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/web_localized_string.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/renderer/platform/geometry/int_size.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+namespace {
+
+class TestingUnitsPlatform : public TestingPlatformSupport {
+ public:
+ TestingUnitsPlatform() {}
+ ~TestingUnitsPlatform() override;
+
+ WebString QueryLocalizedString(WebLocalizedString::Name name,
+ const WebString& parameter) override {
+ String p = parameter;
+ switch (name) {
+ case WebLocalizedString::kUnitsKibibytes:
+ return String(p + " KB");
+ case WebLocalizedString::kUnitsMebibytes:
+ return String(p + " MB");
+ case WebLocalizedString::kUnitsGibibytes:
+ return String(p + " GB");
+ case WebLocalizedString::kUnitsTebibytes:
+ return String(p + " TB");
+ case WebLocalizedString::kUnitsPebibytes:
+ return String(p + " PB");
+ default:
+ return WebString();
+ }
+ }
+};
+
+TestingUnitsPlatform::~TestingUnitsPlatform() = default;
+
+class PlaceholderImageTest : public testing::Test {
+ private:
+ ScopedTestingPlatformSupport<TestingUnitsPlatform> platform_;
+};
+
+TEST_F(PlaceholderImageTest, FormatPlaceholderText) {
+ const struct {
+ int64_t bytes;
+ const char* expected;
+ } tests[] = {
+ // Placeholder image number format specifications:
+ // https://docs.google.com/document/d/1BHeA1azbgCdZgCnr16VN2g7A9MHPQ_dwKn5szh8evMQ/edit#heading=h.d135l9z7tn0a
+ {1, "1 KB"},
+ {500, "1 KB"},
+ {5 * 1024 + 200, "5 KB"},
+ {50 * 1024 + 200, "50 KB"},
+ {1000 * 1024 - 1, "999 KB"},
+ {1000 * 1024, "1 MB"},
+ {1024 * 1024 + 103 * 1024, "1.1 MB"},
+ {10 * 1024 * 1024, "10 MB"},
+ {10 * 1024 * 1024 + 103 * 1024, "10 MB"},
+ {1000 * 1024 * 1024 - 1, "999 MB"},
+ {1000 * 1024 * 1024, "1 GB"},
+ {1024 * 1024 * 1024, "1 GB"},
+ {(1LL << 50), "1 PB"},
+ {(1LL << 50) + 103 * (1LL << 40), "1.1 PB"},
+ {10 * (1LL << 50), "10 PB"},
+ {10 * (1LL << 50) + 103 * (1LL << 40), "10 PB"},
+ {~(1LL << 63), "8191 PB"},
+ };
+
+ for (const auto& test : tests) {
+ String expected = test.expected;
+ expected.Ensure16Bit();
+
+ EXPECT_EQ(expected,
+ PlaceholderImage::Create(nullptr, IntSize(400, 300), test.bytes)
+ ->GetTextForTesting());
+ }
+}
+
+} // namespace
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/profiling_canvas.cc b/chromium/third_party/blink/renderer/platform/graphics/profiling_canvas.cc
new file mode 100644
index 00000000000..fbadaa0964e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/profiling_canvas.cc
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/renderer/platform/graphics/profiling_canvas.h"
+
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+CanvasInterceptor<ProfilingCanvas>::CanvasInterceptor(
+ InterceptingCanvasBase* canvas)
+ : CanvasInterceptorBase(canvas),
+ start_time_(WTF::CurrentTimeTicksInSeconds()) {}
+
+CanvasInterceptor<ProfilingCanvas>::~CanvasInterceptor() {
+ if (!TopLevelCall())
+ return;
+ double delta = WTF::CurrentTimeTicksInSeconds() - start_time_;
+ if (auto timings = Canvas()->timings_) {
+ DCHECK_EQ(timings->size(), Canvas()->CallCount());
+ timings->push_back(delta);
+ }
+}
+
+ProfilingCanvas::ProfilingCanvas(SkBitmap bitmap)
+ : InterceptingCanvas(bitmap), timings_(nullptr) {}
+
+void ProfilingCanvas::SetTimings(Vector<double>* timings) {
+ timings_ = timings;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/profiling_canvas.h b/chromium/third_party/blink/renderer/platform/graphics/profiling_canvas.h
new file mode 100644
index 00000000000..5d67b563f1a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/profiling_canvas.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PROFILING_CANVAS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PROFILING_CANVAS_H_
+
+#include "third_party/blink/renderer/platform/graphics/intercepting_canvas.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class ProfilingCanvas;
+
+template <>
+class CanvasInterceptor<ProfilingCanvas>
+ : protected InterceptingCanvasBase::CanvasInterceptorBase<ProfilingCanvas> {
+ public:
+ CanvasInterceptor(InterceptingCanvasBase*);
+ ~CanvasInterceptor();
+
+ private:
+ double start_time_;
+};
+
+class ProfilingCanvas : public InterceptingCanvas<ProfilingCanvas> {
+ public:
+ explicit ProfilingCanvas(SkBitmap);
+ void SetTimings(Vector<double>*);
+
+ private:
+ friend class CanvasInterceptor<ProfilingCanvas>;
+
+ Vector<double>* timings_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PROFILING_CANVAS_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/replaying_canvas.cc b/chromium/third_party/blink/renderer/platform/graphics/replaying_canvas.cc
new file mode 100644
index 00000000000..405bea8b336
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/replaying_canvas.cc
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/renderer/platform/graphics/replaying_canvas.h"
+
+namespace blink {
+
+CanvasInterceptor<ReplayingCanvas>::~CanvasInterceptor() {
+ if (TopLevelCall())
+ Canvas()->UpdateInRange();
+}
+
+ReplayingCanvas::ReplayingCanvas(SkBitmap bitmap,
+ unsigned from_step,
+ unsigned to_step)
+ : InterceptingCanvas(bitmap),
+ from_step_(from_step),
+ to_step_(to_step),
+ abort_drawing_(false) {}
+
+void ReplayingCanvas::UpdateInRange() {
+ if (abort_drawing_)
+ return;
+ unsigned step = CallCount() + 1;
+ if (to_step_ && step > to_step_)
+ abort_drawing_ = true;
+ if (step == from_step_)
+ this->SkCanvas::clear(SK_ColorTRANSPARENT);
+}
+
+bool ReplayingCanvas::abort() {
+ return abort_drawing_;
+}
+
+SkCanvas::SaveLayerStrategy ReplayingCanvas::getSaveLayerStrategy(
+ const SaveLayerRec& rec) {
+ // We're about to create a layer and we have not cleared the device yet.
+ // Let's clear now, so it has effect on all layers.
+ if (CallCount() <= from_step_)
+ this->SkCanvas::clear(SK_ColorTRANSPARENT);
+
+ return this->InterceptingCanvas<ReplayingCanvas>::getSaveLayerStrategy(rec);
+}
+
+void ReplayingCanvas::onDrawPicture(const SkPicture* picture,
+ const SkMatrix* matrix,
+ const SkPaint* paint) {
+ this->UnrollDrawPicture(picture, matrix, paint, this);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/replaying_canvas.h b/chromium/third_party/blink/renderer/platform/graphics/replaying_canvas.h
new file mode 100644
index 00000000000..8d701c60f18
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/replaying_canvas.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_REPLAYING_CANVAS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_REPLAYING_CANVAS_H_
+
+#include "third_party/blink/renderer/platform/graphics/intercepting_canvas.h"
+
+namespace blink {
+
+class ReplayingCanvas;
+
+template <>
+class CanvasInterceptor<ReplayingCanvas>
+ : protected InterceptingCanvasBase::CanvasInterceptorBase<ReplayingCanvas> {
+ public:
+ CanvasInterceptor(InterceptingCanvasBase* canvas)
+ : InterceptingCanvasBase::CanvasInterceptorBase<ReplayingCanvas>(canvas) {
+ }
+ ~CanvasInterceptor();
+};
+
+class ReplayingCanvas : public InterceptingCanvas<ReplayingCanvas>,
+ public SkPicture::AbortCallback {
+ public:
+ ReplayingCanvas(SkBitmap, unsigned from_step, unsigned to_step);
+
+ bool abort() override;
+ SkCanvas::SaveLayerStrategy getSaveLayerStrategy(
+ const SaveLayerRec&) override;
+ void onDrawPicture(const SkPicture*,
+ const SkMatrix*,
+ const SkPaint*) override;
+
+ private:
+ friend class CanvasInterceptor<ReplayingCanvas>;
+
+ void UpdateInRange();
+
+ unsigned from_step_;
+ unsigned to_step_;
+ bool abort_drawing_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_REPLAYING_CANVAS_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/scoped_interpolation_quality.h b/chromium/third_party/blink/renderer/platform/graphics/scoped_interpolation_quality.h
new file mode 100644
index 00000000000..1cdc020bb1d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/scoped_interpolation_quality.h
@@ -0,0 +1,40 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_SCOPED_INTERPOLATION_QUALITY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_SCOPED_INTERPOLATION_QUALITY_H_
+
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_types.h"
+
+namespace blink {
+
+// Helper to update the interpolation quality setting of a GraphicsContext
+// within the current scope. Be careful when mixing with other GraphicsContext
+// mechanisms that save/restore state (like GraphicsContextStateSaver or the
+// Save/Restore methods) to ensure the restoration behavior is the expected
+// one.
+class ScopedInterpolationQuality {
+ public:
+ ScopedInterpolationQuality(GraphicsContext& context,
+ InterpolationQuality interpolation_quality)
+ : context_(context),
+ previous_interpolation_quality_(context.ImageInterpolationQuality()) {
+ if (previous_interpolation_quality_ != interpolation_quality)
+ context_.SetImageInterpolationQuality(interpolation_quality);
+ }
+
+ ~ScopedInterpolationQuality() {
+ if (previous_interpolation_quality_ != context_.ImageInterpolationQuality())
+ context_.SetImageInterpolationQuality(previous_interpolation_quality_);
+ }
+
+ private:
+ GraphicsContext& context_;
+ const InterpolationQuality previous_interpolation_quality_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_SCOPED_INTERPOLATION_QUALITY_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/skia/image_pixel_locker.cc b/chromium/third_party/blink/renderer/platform/graphics/skia/image_pixel_locker.cc
new file mode 100644
index 00000000000..55b62f32cc0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/skia/image_pixel_locker.cc
@@ -0,0 +1,63 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/skia/image_pixel_locker.h"
+
+#include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/core/SkImageInfo.h"
+
+namespace blink {
+
+namespace {
+
+bool InfoIsCompatible(const SkImageInfo& info,
+ SkAlphaType alpha_type,
+ SkColorType color_type) {
+ DCHECK_NE(alpha_type, kUnknown_SkAlphaType);
+
+ if (info.colorType() != color_type)
+ return false;
+
+ // kOpaque_SkAlphaType works regardless of the requested alphaType.
+ return info.alphaType() == alpha_type ||
+ info.alphaType() == kOpaque_SkAlphaType;
+}
+
+} // anonymous namespace
+
+ImagePixelLocker::ImagePixelLocker(sk_sp<const SkImage> image,
+ SkAlphaType alpha_type,
+ SkColorType color_type)
+ : image_(std::move(image)) {
+ // If the image has in-RAM pixels and their format matches, use them directly.
+ // TODO(fmalita): All current clients expect packed pixel rows. Maybe we
+ // could update them to support arbitrary rowBytes, and relax the check below.
+ SkPixmap pixmap;
+ image_->peekPixels(&pixmap);
+ pixels_ = pixmap.addr();
+ if (pixels_ && InfoIsCompatible(pixmap.info(), alpha_type, color_type) &&
+ pixmap.rowBytes() == pixmap.info().minRowBytes()) {
+ return;
+ }
+
+ pixels_ = nullptr;
+
+ // No luck, we need to read the pixels into our local buffer.
+ SkImageInfo info = SkImageInfo::Make(image_->width(), image_->height(),
+ color_type, alpha_type);
+ size_t row_bytes = info.minRowBytes();
+ size_t size = info.computeByteSize(row_bytes);
+ if (0 == size)
+ return;
+
+ pixel_storage_.resize(size); // this will throw on failure
+ pixmap.reset(info, pixel_storage_.data(), row_bytes);
+
+ if (!image_->readPixels(pixmap, 0, 0))
+ return;
+
+ pixels_ = pixel_storage_.data();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/skia/image_pixel_locker.h b/chromium/third_party/blink/renderer/platform/graphics/skia/image_pixel_locker.h
new file mode 100644
index 00000000000..fe40364d77b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/skia/image_pixel_locker.h
@@ -0,0 +1,36 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_SKIA_IMAGE_PIXEL_LOCKER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_SKIA_IMAGE_PIXEL_LOCKER_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/skia/include/core/SkImageInfo.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+class SkImage;
+
+namespace blink {
+
+class ImagePixelLocker final {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ WTF_MAKE_NONCOPYABLE(ImagePixelLocker);
+
+ public:
+ ImagePixelLocker(sk_sp<const SkImage>, SkAlphaType, SkColorType);
+
+ const void* Pixels() const { return pixels_; }
+
+ private:
+ const sk_sp<const SkImage> image_;
+ const void* pixels_;
+ Vector<char> pixel_storage_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/graphics/skia/sk_size_hash.h b/chromium/third_party/blink/renderer/platform/graphics/skia/sk_size_hash.h
new file mode 100644
index 00000000000..f8afda108ea
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/skia/sk_size_hash.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2012 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_SKIA_SK_SIZE_HASH_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_SKIA_SK_SIZE_HASH_H_
+
+#include "SkScalar.h"
+#include "SkSize.h"
+
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+
+namespace WTF {
+
+template <>
+struct DefaultHash<SkSize> {
+ STATIC_ONLY(DefaultHash);
+ struct Hash {
+ STATIC_ONLY(Hash);
+ static unsigned GetHash(const SkSize& key) {
+ return HashInts(key.width(), key.height());
+ }
+ static bool Equal(const SkSize& a, const SkSize& b) { return a == b; }
+ static const bool safe_to_compare_to_empty_or_deleted = true;
+ };
+};
+
+template <>
+struct HashTraits<SkSize> : GenericHashTraits<SkSize> {
+ STATIC_ONLY(HashTraits);
+ static const bool kEmptyValueIsZero = true;
+ static SkSize EmptyValue() { return SkSize::Make(0, 0); }
+ static void ConstructDeletedValue(SkSize& slot, bool) {
+ slot = SkSize::Make(-1, -1);
+ }
+ static bool IsDeletedValue(const SkSize& value) {
+ return value.width() == -1 && value.height() == -1;
+ }
+};
+
+template <>
+struct DefaultHash<SkISize> {
+ STATIC_ONLY(DefaultHash);
+ struct Hash {
+ STATIC_ONLY(Hash);
+ static unsigned GetHash(const SkISize& key) {
+ return HashInts(key.width(), key.height());
+ }
+ static bool Equal(const SkISize& a, const SkISize& b) { return a == b; }
+ static const bool safe_to_compare_to_empty_or_deleted = true;
+ };
+};
+
+template <>
+struct HashTraits<SkISize> : GenericHashTraits<SkISize> {
+ STATIC_ONLY(HashTraits);
+ static const bool kEmptyValueIsZero = true;
+ static SkISize EmptyValue() { return SkISize::Make(0, 0); }
+ static void ConstructDeletedValue(SkISize& slot, bool) {
+ slot = SkISize::Make(-1, -1);
+ }
+ static bool IsDeletedValue(const SkISize& value) {
+ return value.width() == -1 && value.height() == -1;
+ }
+};
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_SKIA_SK_SIZE_HASH_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/skia/skia_utils.cc b/chromium/third_party/blink/renderer/platform/graphics/skia/skia_utils.cc
new file mode 100644
index 00000000000..8d412e39318
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/skia/skia_utils.cc
@@ -0,0 +1,383 @@
+/*
+ * Copyright (c) 2006,2007,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 "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
+
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
+#include "third_party/skia/include/effects/SkCornerPathEffect.h"
+
+namespace blink {
+
+static const struct CompositOpToXfermodeMode {
+ CompositeOperator composit_op;
+ SkBlendMode xfermode_mode;
+} kGMapCompositOpsToXfermodeModes[] = {
+ {kCompositeClear, SkBlendMode::kClear},
+ {kCompositeCopy, SkBlendMode::kSrc},
+ {kCompositeSourceOver, SkBlendMode::kSrcOver},
+ {kCompositeSourceIn, SkBlendMode::kSrcIn},
+ {kCompositeSourceOut, SkBlendMode::kSrcOut},
+ {kCompositeSourceAtop, SkBlendMode::kSrcATop},
+ {kCompositeDestinationOver, SkBlendMode::kDstOver},
+ {kCompositeDestinationIn, SkBlendMode::kDstIn},
+ {kCompositeDestinationOut, SkBlendMode::kDstOut},
+ {kCompositeDestinationAtop, SkBlendMode::kDstATop},
+ {kCompositeXOR, SkBlendMode::kXor},
+ {kCompositePlusLighter, SkBlendMode::kPlus}};
+
+// Keep this array in sync with the WebBlendMode enum in
+// public/platform/WebBlendMode.h.
+static const SkBlendMode kGMapBlendOpsToXfermodeModes[] = {
+ SkBlendMode::kSrcOver, // WebBlendModeNormal
+ SkBlendMode::kMultiply, // WebBlendModeMultiply
+ SkBlendMode::kScreen, // WebBlendModeScreen
+ SkBlendMode::kOverlay, // WebBlendModeOverlay
+ SkBlendMode::kDarken, // WebBlendModeDarken
+ SkBlendMode::kLighten, // WebBlendModeLighten
+ SkBlendMode::kColorDodge, // WebBlendModeColorDodge
+ SkBlendMode::kColorBurn, // WebBlendModeColorBurn
+ SkBlendMode::kHardLight, // WebBlendModeHardLight
+ SkBlendMode::kSoftLight, // WebBlendModeSoftLight
+ SkBlendMode::kDifference, // WebBlendModeDifference
+ SkBlendMode::kExclusion, // WebBlendModeExclusion
+ SkBlendMode::kHue, // WebBlendModeHue
+ SkBlendMode::kSaturation, // WebBlendModeSaturation
+ SkBlendMode::kColor, // WebBlendModeColor
+ SkBlendMode::kLuminosity // WebBlendModeLuminosity
+};
+
+SkBlendMode WebCoreCompositeToSkiaComposite(CompositeOperator op,
+ WebBlendMode blend_mode) {
+ DCHECK(op == kCompositeSourceOver || blend_mode == WebBlendMode::kNormal);
+ if (blend_mode != WebBlendMode::kNormal) {
+ if (static_cast<uint8_t>(blend_mode) >=
+ SK_ARRAY_COUNT(kGMapBlendOpsToXfermodeModes)) {
+ SkDEBUGF(
+ ("GraphicsContext::setPlatformCompositeOperation unknown "
+ "WebBlendMode %d\n",
+ blend_mode));
+ return SkBlendMode::kSrcOver;
+ }
+ return kGMapBlendOpsToXfermodeModes[static_cast<uint8_t>(blend_mode)];
+ }
+
+ const CompositOpToXfermodeMode* table = kGMapCompositOpsToXfermodeModes;
+ if (static_cast<uint8_t>(op) >=
+ SK_ARRAY_COUNT(kGMapCompositOpsToXfermodeModes)) {
+ SkDEBUGF(
+ ("GraphicsContext::setPlatformCompositeOperation unknown "
+ "CompositeOperator %d\n",
+ op));
+ return SkBlendMode::kSrcOver;
+ }
+ SkASSERT(table[static_cast<uint8_t>(op)].composit_op == op);
+ return table[static_cast<uint8_t>(op)].xfermode_mode;
+}
+
+CompositeOperator CompositeOperatorFromSkia(SkBlendMode xfer_mode) {
+ switch (xfer_mode) {
+ case SkBlendMode::kClear:
+ return kCompositeClear;
+ case SkBlendMode::kSrc:
+ return kCompositeCopy;
+ case SkBlendMode::kSrcOver:
+ return kCompositeSourceOver;
+ case SkBlendMode::kSrcIn:
+ return kCompositeSourceIn;
+ case SkBlendMode::kSrcOut:
+ return kCompositeSourceOut;
+ case SkBlendMode::kSrcATop:
+ return kCompositeSourceAtop;
+ case SkBlendMode::kDstOver:
+ return kCompositeDestinationOver;
+ case SkBlendMode::kDstIn:
+ return kCompositeDestinationIn;
+ case SkBlendMode::kDstOut:
+ return kCompositeDestinationOut;
+ case SkBlendMode::kDstATop:
+ return kCompositeDestinationAtop;
+ case SkBlendMode::kXor:
+ return kCompositeXOR;
+ case SkBlendMode::kPlus:
+ return kCompositePlusLighter;
+ default:
+ break;
+ }
+ return kCompositeSourceOver;
+}
+
+WebBlendMode BlendModeFromSkia(SkBlendMode xfer_mode) {
+ switch (xfer_mode) {
+ case SkBlendMode::kSrcOver:
+ return WebBlendMode::kNormal;
+ case SkBlendMode::kMultiply:
+ return WebBlendMode::kMultiply;
+ case SkBlendMode::kScreen:
+ return WebBlendMode::kScreen;
+ case SkBlendMode::kOverlay:
+ return WebBlendMode::kOverlay;
+ case SkBlendMode::kDarken:
+ return WebBlendMode::kDarken;
+ case SkBlendMode::kLighten:
+ return WebBlendMode::kLighten;
+ case SkBlendMode::kColorDodge:
+ return WebBlendMode::kColorDodge;
+ case SkBlendMode::kColorBurn:
+ return WebBlendMode::kColorBurn;
+ case SkBlendMode::kHardLight:
+ return WebBlendMode::kHardLight;
+ case SkBlendMode::kSoftLight:
+ return WebBlendMode::kSoftLight;
+ case SkBlendMode::kDifference:
+ return WebBlendMode::kDifference;
+ case SkBlendMode::kExclusion:
+ return WebBlendMode::kExclusion;
+ case SkBlendMode::kHue:
+ return WebBlendMode::kHue;
+ case SkBlendMode::kSaturation:
+ return WebBlendMode::kSaturation;
+ case SkBlendMode::kColor:
+ return WebBlendMode::kColor;
+ case SkBlendMode::kLuminosity:
+ return WebBlendMode::kLuminosity;
+ default:
+ break;
+ }
+ return WebBlendMode::kNormal;
+}
+
+SkMatrix AffineTransformToSkMatrix(const AffineTransform& source) {
+ SkMatrix result;
+
+ result.setScaleX(WebCoreDoubleToSkScalar(source.A()));
+ result.setSkewX(WebCoreDoubleToSkScalar(source.C()));
+ result.setTranslateX(WebCoreDoubleToSkScalar(source.E()));
+
+ result.setScaleY(WebCoreDoubleToSkScalar(source.D()));
+ result.setSkewY(WebCoreDoubleToSkScalar(source.B()));
+ result.setTranslateY(WebCoreDoubleToSkScalar(source.F()));
+
+ // FIXME: Set perspective properly.
+ result.setPerspX(0);
+ result.setPerspY(0);
+ result.set(SkMatrix::kMPersp2, SK_Scalar1);
+
+ return result;
+}
+
+bool NearlyIntegral(float value) {
+ return fabs(value - floorf(value)) < std::numeric_limits<float>::epsilon();
+}
+
+bool IsValidImageSize(const IntSize& size) {
+ if (size.IsEmpty())
+ return false;
+ base::CheckedNumeric<int> area = size.Width();
+ area *= size.Height();
+ if (!area.IsValid() || area.ValueOrDie() > kMaxCanvasArea)
+ return false;
+ if (size.Width() > kMaxSkiaDim || size.Height() > kMaxSkiaDim)
+ return false;
+ return true;
+}
+
+InterpolationQuality ComputeInterpolationQuality(float src_width,
+ float src_height,
+ float dest_width,
+ float dest_height,
+ bool is_data_complete) {
+ // The percent change below which we will not resample. This usually means
+ // an off-by-one error on the web page, and just doing nearest neighbor
+ // sampling is usually good enough.
+ const float kFractionalChangeThreshold = 0.025f;
+
+ // Images smaller than this in either direction are considered "small" and
+ // are not resampled ever (see below).
+ const int kSmallImageSizeThreshold = 8;
+
+ // The amount an image can be stretched in a single direction before we
+ // say that it is being stretched so much that it must be a line or
+ // background that doesn't need resampling.
+ const float kLargeStretch = 3.0f;
+
+ // Figure out if we should resample this image. We try to prune out some
+ // common cases where resampling won't give us anything, since it is much
+ // slower than drawing stretched.
+ float diff_width = fabs(dest_width - src_width);
+ float diff_height = fabs(dest_height - src_height);
+ bool width_nearly_equal = diff_width < std::numeric_limits<float>::epsilon();
+ bool height_nearly_equal =
+ diff_height < std::numeric_limits<float>::epsilon();
+ // We don't need to resample if the source and destination are the same.
+ if (width_nearly_equal && height_nearly_equal)
+ return kInterpolationNone;
+
+ if (src_width <= kSmallImageSizeThreshold ||
+ src_height <= kSmallImageSizeThreshold ||
+ dest_width <= kSmallImageSizeThreshold ||
+ dest_height <= kSmallImageSizeThreshold) {
+ // Small image detected.
+
+ // Resample in the case where the new size would be non-integral.
+ // This can cause noticeable breaks in repeating patterns, except
+ // when the source image is only one pixel wide in that dimension.
+ if ((!NearlyIntegral(dest_width) &&
+ src_width > 1 + std::numeric_limits<float>::epsilon()) ||
+ (!NearlyIntegral(dest_height) &&
+ src_height > 1 + std::numeric_limits<float>::epsilon()))
+ return kInterpolationLow;
+
+ // Otherwise, don't resample small images. These are often used for
+ // borders and rules (think 1x1 images used to make lines).
+ return kInterpolationNone;
+ }
+
+ if (src_height * kLargeStretch <= dest_height ||
+ src_width * kLargeStretch <= dest_width) {
+ // Large image detected.
+
+ // Don't resample if it is being stretched a lot in only one direction.
+ // This is trying to catch cases where somebody has created a border
+ // (which might be large) and then is stretching it to fill some part
+ // of the page.
+ if (width_nearly_equal || height_nearly_equal)
+ return kInterpolationNone;
+
+ // The image is growing a lot and in more than one direction. Resampling
+ // is slow and doesn't give us very much when growing a lot.
+ return kInterpolationLow;
+ }
+
+ if ((diff_width / src_width < kFractionalChangeThreshold) &&
+ (diff_height / src_height < kFractionalChangeThreshold)) {
+ // It is disappointingly common on the web for image sizes to be off by
+ // one or two pixels. We don't bother resampling if the size difference
+ // is a small fraction of the original size.
+ return kInterpolationNone;
+ }
+
+ // When the image is not yet done loading, use linear. We don't cache the
+ // partially resampled images, and as they come in incrementally, it causes
+ // us to have to resample the whole thing every time.
+ if (!is_data_complete)
+ return kInterpolationLow;
+
+ // Everything else gets resampled at default quality.
+ return kInterpolationDefault;
+}
+
+int ClampedAlphaForBlending(float alpha) {
+ if (alpha < 0)
+ return 0;
+ int rounded_alpha = roundf(alpha * 256);
+ if (rounded_alpha > 256)
+ rounded_alpha = 256;
+ return rounded_alpha;
+}
+
+SkColor ScaleAlpha(SkColor color, float alpha) {
+ return ScaleAlpha(color, ClampedAlphaForBlending(alpha));
+}
+
+SkColor ScaleAlpha(SkColor color, int alpha) {
+ int a = (SkColorGetA(color) * alpha) >> 8;
+ return (color & 0x00FFFFFF) | (a << 24);
+}
+
+template <typename PrimitiveType>
+void DrawFocusRingPrimitive(const PrimitiveType&,
+ PaintCanvas*,
+ const PaintFlags&,
+ float corner_radius) {
+ NOTREACHED(); // Missing an explicit specialization?
+}
+
+template <>
+void DrawFocusRingPrimitive<SkRect>(const SkRect& rect,
+ PaintCanvas* canvas,
+ const PaintFlags& flags,
+ float corner_radius) {
+ SkRRect rrect;
+ rrect.setRectXY(rect, SkFloatToScalar(corner_radius),
+ SkFloatToScalar(corner_radius));
+ canvas->drawRRect(rrect, flags);
+}
+
+template <>
+void DrawFocusRingPrimitive<SkPath>(const SkPath& path,
+ PaintCanvas* canvas,
+ const PaintFlags& flags,
+ float corner_radius) {
+ PaintFlags path_flags = flags;
+ path_flags.setPathEffect(
+ SkCornerPathEffect::Make(SkFloatToScalar(corner_radius)));
+ canvas->drawPath(path, path_flags);
+}
+
+template <typename PrimitiveType>
+void DrawPlatformFocusRing(const PrimitiveType& primitive,
+ PaintCanvas* canvas,
+ SkColor color,
+ float width) {
+ PaintFlags flags;
+ flags.setAntiAlias(true);
+ flags.setStyle(PaintFlags::kStroke_Style);
+ flags.setColor(color);
+ flags.setStrokeWidth(width);
+
+#if defined(OS_MACOSX)
+ flags.setAlpha(64);
+ const float corner_radius = (width - 1) * 0.5f;
+#else
+ const float corner_radius = width;
+#endif
+
+ DrawFocusRingPrimitive(primitive, canvas, flags, corner_radius);
+
+#if defined(OS_MACOSX)
+ // Inner part
+ flags.setAlpha(128);
+ flags.setStrokeWidth(flags.getStrokeWidth() * 0.5f);
+ DrawFocusRingPrimitive(primitive, canvas, flags, corner_radius);
+#endif
+}
+
+template void PLATFORM_EXPORT DrawPlatformFocusRing<SkRect>(const SkRect&,
+ PaintCanvas*,
+ SkColor,
+ float width);
+template void PLATFORM_EXPORT DrawPlatformFocusRing<SkPath>(const SkPath&,
+ PaintCanvas*,
+ SkColor,
+ float width);
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/skia/skia_utils.h b/chromium/third_party/blink/renderer/platform/graphics/skia/skia_utils.h
new file mode 100644
index 00000000000..9da9dd90ed2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/skia/skia_utils.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2006,2007,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 of the functions in this file should move to new homes and this file
+// should be deleted.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_SKIA_SKIA_UTILS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_SKIA_SKIA_UTILS_H_
+
+#include "third_party/blink/renderer/platform/graphics/graphics_types.h"
+#include "third_party/blink/renderer/platform/graphics/image.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/transforms/affine_transform.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+#include "third_party/skia/include/core/SkScalar.h"
+
+namespace blink {
+
+/**** constants ****/
+
+enum {
+ // Firefox limits width/height to 32767 pixels, but slows down dramatically
+ // before it reaches that limit. We limit by area instead, giving us larger
+ // maximum dimensions, in exchange for a smaller maximum canvas size.
+ kMaxCanvasArea = 32768 * 8192, // Maximum canvas area in CSS pixels
+
+ // In Skia, we will also limit width/height to 32767.
+ kMaxSkiaDim = 32767 // Maximum width/height in CSS pixels.
+};
+
+bool PLATFORM_EXPORT IsValidImageSize(const IntSize&);
+
+SkBlendMode PLATFORM_EXPORT
+ WebCoreCompositeToSkiaComposite(CompositeOperator,
+ WebBlendMode = WebBlendMode::kNormal);
+CompositeOperator PLATFORM_EXPORT CompositeOperatorFromSkia(SkBlendMode);
+WebBlendMode PLATFORM_EXPORT BlendModeFromSkia(SkBlendMode);
+
+// Map alpha values from [0, 1] to [0, 256] for alpha blending.
+int PLATFORM_EXPORT ClampedAlphaForBlending(float);
+
+// Multiply a color's alpha channel by an additional alpha factor where
+// alpha is in the range [0, 1].
+SkColor PLATFORM_EXPORT ScaleAlpha(SkColor, float);
+
+// Multiply a color's alpha channel by an additional alpha factor where
+// alpha is in the range [0, 256].
+SkColor PLATFORM_EXPORT ScaleAlpha(SkColor, int);
+
+// Skia has problems when passed infinite, etc floats, filter them to 0.
+inline SkScalar WebCoreFloatToSkScalar(float f) {
+ return SkFloatToScalar(std::isfinite(f) ? f : 0);
+}
+
+inline SkScalar WebCoreDoubleToSkScalar(double d) {
+ return SkDoubleToScalar(std::isfinite(d) ? d : 0);
+}
+
+inline bool WebCoreFloatNearlyEqual(float a, float b) {
+ return SkScalarNearlyEqual(WebCoreFloatToSkScalar(a),
+ WebCoreFloatToSkScalar(b));
+}
+
+inline SkPath::FillType WebCoreWindRuleToSkFillType(WindRule rule) {
+ return static_cast<SkPath::FillType>(rule);
+}
+
+inline WindRule SkFillTypeToWindRule(SkPath::FillType fill_type) {
+ switch (fill_type) {
+ case SkPath::kWinding_FillType:
+ case SkPath::kEvenOdd_FillType:
+ return static_cast<WindRule>(fill_type);
+ default:
+ NOTREACHED();
+ break;
+ }
+ return RULE_NONZERO;
+}
+
+SkMatrix PLATFORM_EXPORT AffineTransformToSkMatrix(const AffineTransform&);
+
+bool NearlyIntegral(float value);
+
+InterpolationQuality ComputeInterpolationQuality(float src_width,
+ float src_height,
+ float dest_width,
+ float dest_height,
+ bool is_data_complete = true);
+
+// This replicates the old skia behavior when it used to take radius for blur.
+// Now it takes sigma.
+inline SkScalar SkBlurRadiusToSigma(SkScalar radius) {
+ SkASSERT(radius >= 0);
+ if (radius == 0)
+ return 0.0f;
+ return 0.288675f * radius + 0.5f;
+}
+
+template <typename PrimitiveType>
+void DrawPlatformFocusRing(const PrimitiveType&,
+ PaintCanvas*,
+ SkColor,
+ float width);
+
+// TODO(fmalita): remove in favor of direct SrcRectConstraint use.
+inline PaintCanvas::SrcRectConstraint WebCoreClampingModeToSkiaRectConstraint(
+ Image::ImageClampingMode clamp_mode) {
+ return clamp_mode == Image::kClampImageToSourceRect
+ ? PaintCanvas::kStrict_SrcRectConstraint
+ : PaintCanvas::kFast_SrcRectConstraint;
+}
+
+// Skia's smart pointer APIs are preferable over their legacy raw pointer
+// counterparts.
+//
+// General guidelines
+//
+// When receiving ref counted objects from Skia:
+//
+// 1) Use sk_sp-based Skia factories if available (e.g. SkShader::MakeFoo()
+// instead of SkShader::CreateFoo()).
+// 2) Use sk_sp<T> locals for all objects.
+//
+// When passing ref counted objects to Skia:
+//
+// 1) Use sk_sp-based Skia APIs when available (e.g.
+// SkPaint::setShader(sk_sp<SkShader>) instead of
+// SkPaint::setShader(SkShader*)).
+// 2) If object ownership is being passed to Skia, use std::move(sk_sp<T>).
+//
+// Example (creating a SkShader and setting it on SkPaint):
+//
+// a) ownership transferred
+//
+// // using Skia smart pointer locals
+// sk_sp<SkShader> shader = SkShader::MakeFoo(...);
+// paint.setShader(std::move(shader));
+//
+// // using no locals
+// paint.setShader(SkShader::MakeFoo(...));
+//
+// b) shared ownership
+//
+// sk_sp<SkShader> shader = SkShader::MakeFoo(...);
+// paint.setShader(shader);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_SKIA_SKIA_UTILS_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/skia_texture_holder.cc b/chromium/third_party/blink/renderer/platform/graphics/skia_texture_holder.cc
new file mode 100644
index 00000000000..be22081f4f3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/skia_texture_holder.cc
@@ -0,0 +1,79 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/skia_texture_holder.h"
+
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
+#include "third_party/blink/renderer/platform/graphics/mailbox_texture_holder.h"
+#include "third_party/khronos/GLES2/gl2ext.h"
+#include "third_party/skia/include/gpu/GrBackendSurface.h"
+#include "third_party/skia/include/gpu/GrContext.h"
+
+namespace blink {
+
+SkiaTextureHolder::SkiaTextureHolder(
+ sk_sp<SkImage> image,
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&
+ context_provider_wrapper)
+ : TextureHolder(std::move(context_provider_wrapper)),
+ image_(std::move(image)) {}
+
+SkiaTextureHolder::SkiaTextureHolder(
+ std::unique_ptr<TextureHolder> texture_holder)
+ : TextureHolder(SharedGpuContext::ContextProviderWrapper()) {
+ DCHECK(texture_holder->IsMailboxTextureHolder());
+ const gpu::Mailbox mailbox = texture_holder->GetMailbox();
+ const gpu::SyncToken sync_token = texture_holder->GetSyncToken();
+ const IntSize mailbox_size = texture_holder->Size();
+
+ if (!ContextProvider())
+ return;
+ if (texture_holder->IsAbandoned()) {
+ Abandon();
+ return;
+ }
+
+ gpu::gles2::GLES2Interface* shared_gl = ContextProvider()->ContextGL();
+ GrContext* shared_gr_context = ContextProvider()->GetGrContext();
+ DCHECK(shared_gl &&
+ shared_gr_context); // context isValid already checked in callers
+
+ shared_gl->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
+ GLuint shared_context_texture_id =
+ shared_gl->CreateAndConsumeTextureCHROMIUM(mailbox.name);
+ GrGLTextureInfo texture_info;
+ texture_info.fTarget = GL_TEXTURE_2D;
+ texture_info.fID = shared_context_texture_id;
+ if (kN32_SkColorType == kRGBA_8888_SkColorType) {
+ texture_info.fFormat = GL_RGBA8_OES;
+ } else {
+ DCHECK(kN32_SkColorType == kBGRA_8888_SkColorType);
+ texture_info.fFormat = GL_BGRA8_EXT;
+ }
+ GrBackendTexture backend_texture(mailbox_size.Width(), mailbox_size.Height(),
+ GrMipMapped::kNo, texture_info);
+ image_ = SkImage::MakeFromAdoptedTexture(shared_gr_context, backend_texture,
+ kBottomLeft_GrSurfaceOrigin,
+ kN32_SkColorType);
+}
+
+SkiaTextureHolder::~SkiaTextureHolder() {
+ // Object must be destroyed on the same thread where it was created.
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+}
+
+void SkiaTextureHolder::Abandon() {
+ if (image_ && image_->getTexture())
+ image_->getTexture()->abandon();
+ TextureHolder::Abandon();
+}
+
+bool SkiaTextureHolder::IsValid() const {
+ return !IsAbandoned() && !!ContextProviderWrapper();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/skia_texture_holder.h b/chromium/third_party/blink/renderer/platform/graphics/skia_texture_holder.h
new file mode 100644
index 00000000000..0cd6ea15413
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/skia_texture_holder.h
@@ -0,0 +1,49 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_SKIA_TEXTURE_HOLDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_SKIA_TEXTURE_HOLDER_H_
+
+#include "base/memory/weak_ptr.h"
+#include "third_party/blink/renderer/platform/graphics/texture_holder.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+
+class WebGraphicsContext3DProviderWrapper;
+
+class PLATFORM_EXPORT SkiaTextureHolder final : public TextureHolder {
+ public:
+ ~SkiaTextureHolder() override;
+
+ // Methods overriding TextureHolder
+ bool IsSkiaTextureHolder() final { return true; }
+ bool IsMailboxTextureHolder() final { return false; }
+ IntSize Size() const final {
+ return IntSize(image_->width(), image_->height());
+ }
+ bool IsValid() const final;
+ bool CurrentFrameKnownToBeOpaque() final { return image_->isOpaque(); }
+ sk_sp<SkImage> GetSkImage() final { return image_; }
+ void Abandon() final;
+
+ // When creating a AcceleratedStaticBitmap from a texture-backed SkImage, this
+ // function will be called to create a TextureHolder object.
+ SkiaTextureHolder(sk_sp<SkImage>,
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&);
+ // This function consumes the mailbox in the input parameter and turn it into
+ // a texture-backed SkImage.
+ SkiaTextureHolder(std::unique_ptr<TextureHolder>);
+
+ private:
+ // void ReleaseImageThreadSafe();
+
+ // The m_image should always be texture-backed
+ sk_sp<SkImage> image_;
+ THREAD_CHECKER(thread_checker_);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_SKIA_TEXTURE_HOLDER_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/squashing_disallowed_reasons.cc b/chromium/third_party/blink/renderer/platform/graphics/squashing_disallowed_reasons.cc
new file mode 100644
index 00000000000..0e8d14de9f2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/squashing_disallowed_reasons.cc
@@ -0,0 +1,120 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/squashing_disallowed_reasons.h"
+
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+
+namespace blink {
+
+namespace {
+
+struct SquashingDisallowedReasonStringMap {
+ SquashingDisallowedReasons reason;
+ const char* short_name;
+ const char* description;
+};
+
+constexpr SquashingDisallowedReasonStringMap
+ kSquashingDisallowedReasonsStringMap[] = {
+ {SquashingDisallowedReason::kScrollsWithRespectToSquashingLayer,
+ "scrollsWithRespectToSquashingLayer",
+ "Cannot be squashed since this layer scrolls with respect to the "
+ "squashing layer"},
+ {SquashingDisallowedReason::kSquashingSparsityExceeded,
+ "squashingSparsityExceeded",
+ "Cannot be squashed as the squashing layer would become too sparse"},
+ {SquashingDisallowedReason::kClippingContainerMismatch,
+ "squashingClippingContainerMismatch",
+ "Cannot be squashed because this layer has a different clipping "
+ "container than the squashing layer"},
+ {SquashingDisallowedReason::kOpacityAncestorMismatch,
+ "squashingOpacityAncestorMismatch",
+ "Cannot be squashed because this layer has a different opacity "
+ "ancestor than the squashing layer"},
+ {SquashingDisallowedReason::kTransformAncestorMismatch,
+ "squashingTransformAncestorMismatch",
+ "Cannot be squashed because this layer has a different transform "
+ "ancestor than the squashing layer"},
+ {SquashingDisallowedReason::kFilterMismatch,
+ "squashingFilterAncestorMismatch",
+ "Cannot be squashed because this layer has a different filter "
+ "ancestor than the squashing layer, or this layer has a filter"},
+ {SquashingDisallowedReason::kWouldBreakPaintOrder,
+ "squashingWouldBreakPaintOrder",
+ "Cannot be squashed without breaking paint order"},
+ {SquashingDisallowedReason::kSquashingVideoIsDisallowed,
+ "squashingVideoIsDisallowed", "Squashing video is not supported"},
+ {SquashingDisallowedReason::kSquashedLayerClipsCompositingDescendants,
+ "squashedLayerClipsSquashingDisallowedDescendants",
+ "Squashing a layer that clips composited descendants is not "
+ "supported."},
+ {SquashingDisallowedReason::kSquashingLayoutEmbeddedContentIsDisallowed,
+ "squashingLayoutEmbeddedContentIsDisallowed",
+ "Squashing a frame, iframe or plugin is not supported."},
+ {SquashingDisallowedReason::kSquashingBlendingIsDisallowed,
+ "squashingBlendingDisallowed",
+ "Squashing a layer with blending is not supported."},
+ {SquashingDisallowedReason::kNearestFixedPositionMismatch,
+ "squashingNearestFixedPositionMismatch",
+ "Cannot be squashed because this layer has a different nearest fixed "
+ "position layer than the squashing layer"},
+ {SquashingDisallowedReason::kScrollChildWithCompositedDescendants,
+ "scrollChildWithCompositedDescendants",
+ "Squashing a scroll child with composited descendants is not "
+ "supported."},
+ {SquashingDisallowedReason::kSquashingLayerIsAnimating,
+ "squashingLayerIsAnimating",
+ "Cannot squash into a layer that is animating."},
+ {SquashingDisallowedReason::kRenderingContextMismatch,
+ "squashingLayerRenderingContextMismatch",
+ "Cannot squash layers with different 3D contexts."},
+ {SquashingDisallowedReason::kFragmentedContent,
+ "SquashingDisallowedReasonFragmentedContent",
+ "Cannot squash layers that are inside fragmentation contexts."},
+ {SquashingDisallowedReason::kClipPathMismatch,
+ "SquashingDisallowedReasonClipPathMismatch",
+ "Cannot squash layers across clip-path boundaries."},
+ {SquashingDisallowedReason::kMaskMismatch,
+ "SquashingDisallowedReasonMaskMismatch",
+ "Cannot squash layers across mask boundaries."},
+};
+
+} // anonymous namespace
+
+Vector<const char*> SquashingDisallowedReason::ShortNames(
+ SquashingDisallowedReasons reasons) {
+#define V(name) \
+ static_assert(SquashingDisallowedReason::k##name == \
+ kSquashingDisallowedReasonsStringMap \
+ [SquashingDisallowedReason::kE##name] \
+ .reason, \
+ "kSquashingDisallowedReasonsStringMap needs update for " \
+ "SquashingDisallowedReason::k" #name); \
+ FOR_EACH_COMPOSITING_REASON(V)
+#undef V
+
+ Vector<const char*> result;
+ if (reasons == kNone)
+ return result;
+ for (auto& map : kSquashingDisallowedReasonsStringMap) {
+ if (reasons & map.reason)
+ result.push_back(map.short_name);
+ }
+ return result;
+}
+
+Vector<const char*> SquashingDisallowedReason::Descriptions(
+ SquashingDisallowedReasons reasons) {
+ Vector<const char*> result;
+ if (reasons == kNone)
+ return result;
+ for (auto& map : kSquashingDisallowedReasonsStringMap) {
+ if (reasons & map.reason)
+ result.push_back(map.description);
+ }
+ return result;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/squashing_disallowed_reasons.h b/chromium/third_party/blink/renderer/platform/graphics/squashing_disallowed_reasons.h
new file mode 100644
index 00000000000..aa61d194de7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/squashing_disallowed_reasons.h
@@ -0,0 +1,63 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_SQUASHING_DISALLOWED_REASONS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_SQUASHING_DISALLOWED_REASONS_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+using SquashingDisallowedReasons = unsigned;
+
+#define FOR_EACH_SQUASHING_DISALLOWED_REASON(V) \
+ V(ScrollsWithRespectToSquashingLayer) \
+ V(SquashingSparsityExceeded) \
+ V(ClippingContainerMismatch) \
+ V(OpacityAncestorMismatch) \
+ V(TransformAncestorMismatch) \
+ V(FilterMismatch) \
+ V(WouldBreakPaintOrder) \
+ V(SquashingVideoIsDisallowed) \
+ V(SquashedLayerClipsCompositingDescendants) \
+ V(SquashingLayoutEmbeddedContentIsDisallowed) \
+ V(SquashingBlendingIsDisallowed) \
+ V(NearestFixedPositionMismatch) \
+ V(ScrollChildWithCompositedDescendants) \
+ V(SquashingLayerIsAnimating) \
+ V(RenderingContextMismatch) \
+ V(FragmentedContent) \
+ V(ClipPathMismatch) \
+ V(MaskMismatch)
+
+class PLATFORM_EXPORT SquashingDisallowedReason {
+ private:
+ // This contains ordinal values for squashing disallowed reasons and will be
+ // used to generate the squashing disallowed reason bits.
+ enum {
+#define V(name) kE##name,
+ FOR_EACH_SQUASHING_DISALLOWED_REASON(V)
+#undef V
+ };
+
+#define V(name) static_assert(kE##name < 32, "Should fit in 32 bits");
+ FOR_EACH_SQUASHING_DISALLOWED_REASON(V)
+#undef V
+
+ public:
+ static Vector<const char*> ShortNames(SquashingDisallowedReasons);
+ static Vector<const char*> Descriptions(SquashingDisallowedReasons);
+
+ enum : SquashingDisallowedReasons {
+ kNone = 0,
+#define V(name) k##name = 1u << kE##name,
+ FOR_EACH_SQUASHING_DISALLOWED_REASON(V)
+#undef V
+ };
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_SQUASHING_DISALLOWED_REASONS_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/static_bitmap_image.cc b/chromium/third_party/blink/renderer/platform/graphics/static_bitmap_image.cc
new file mode 100644
index 00000000000..7de82499da3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/static_bitmap_image.cc
@@ -0,0 +1,166 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
+
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "skia/ext/texture_handle.h"
+#include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/image_observer.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_image.h"
+#include "third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h"
+#include "third_party/blink/renderer/platform/wtf/checked_numeric.h"
+#include "third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_contents.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/core/SkPaint.h"
+#include "third_party/skia/include/gpu/GrContext.h"
+
+namespace blink {
+
+scoped_refptr<StaticBitmapImage> StaticBitmapImage::Create(
+ sk_sp<SkImage> image,
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper>
+ context_provider_wrapper) {
+ if (!image)
+ return nullptr;
+ if (image->isTextureBacked()) {
+ CHECK(context_provider_wrapper);
+ return AcceleratedStaticBitmapImage::CreateFromSkImage(
+ image, std::move(context_provider_wrapper));
+ }
+ return UnacceleratedStaticBitmapImage::Create(image);
+}
+
+scoped_refptr<StaticBitmapImage> StaticBitmapImage::Create(PaintImage image) {
+ DCHECK(!image.GetSkImage()->isTextureBacked());
+ return UnacceleratedStaticBitmapImage::Create(std::move(image));
+}
+
+scoped_refptr<StaticBitmapImage> StaticBitmapImage::Create(
+ scoped_refptr<Uint8Array>&& image_pixels,
+ const SkImageInfo& info) {
+ SkPixmap pixmap(info, image_pixels->Data(), info.minRowBytes());
+
+ Uint8Array* pixels = image_pixels.get();
+ if (pixels) {
+ pixels->AddRef();
+ image_pixels = nullptr;
+ }
+
+ return Create(SkImage::MakeFromRaster(
+ pixmap,
+ [](const void*, void* p) { static_cast<Uint8Array*>(p)->Release(); },
+ pixels));
+}
+
+scoped_refptr<StaticBitmapImage> StaticBitmapImage::Create(
+ WTF::ArrayBufferContents& contents,
+ const SkImageInfo& info) {
+ SkPixmap pixmap(info, contents.Data(), info.minRowBytes());
+ return Create(SkImage::MakeFromRaster(pixmap, nullptr, nullptr));
+}
+
+void StaticBitmapImage::DrawHelper(PaintCanvas* canvas,
+ const PaintFlags& flags,
+ const FloatRect& dst_rect,
+ const FloatRect& src_rect,
+ ImageClampingMode clamp_mode,
+ const PaintImage& image) {
+ FloatRect adjusted_src_rect = src_rect;
+ adjusted_src_rect.Intersect(SkRect::MakeWH(image.width(), image.height()));
+
+ if (dst_rect.IsEmpty() || adjusted_src_rect.IsEmpty())
+ return; // Nothing to draw.
+
+ canvas->drawImageRect(image, adjusted_src_rect, dst_rect, &flags,
+ WebCoreClampingModeToSkiaRectConstraint(clamp_mode));
+}
+
+scoped_refptr<StaticBitmapImage> StaticBitmapImage::ConvertToColorSpace(
+ sk_sp<SkColorSpace> target,
+ SkTransferFunctionBehavior transfer_function_behavior) {
+ sk_sp<SkImage> skia_image = PaintImageForCurrentFrame().GetSkImage();
+ sk_sp<SkColorSpace> src_color_space = skia_image->refColorSpace();
+ if (!src_color_space.get())
+ src_color_space = SkColorSpace::MakeSRGB();
+ sk_sp<SkColorSpace> dst_color_space = target;
+ if (!dst_color_space.get())
+ dst_color_space = SkColorSpace::MakeSRGB();
+ if (SkColorSpace::Equals(src_color_space.get(), dst_color_space.get()))
+ return this;
+
+ sk_sp<SkImage> converted_skia_image =
+ skia_image->makeColorSpace(dst_color_space, transfer_function_behavior);
+ DCHECK(converted_skia_image.get());
+ DCHECK(skia_image.get() != converted_skia_image.get());
+
+ return StaticBitmapImage::Create(converted_skia_image,
+ ContextProviderWrapper());
+}
+
+bool StaticBitmapImage::ConvertToArrayBufferContents(
+ scoped_refptr<StaticBitmapImage> src_image,
+ WTF::ArrayBufferContents& dest_contents,
+ const IntRect& rect,
+ const CanvasColorParams& color_params,
+ bool is_accelerated) {
+ uint8_t bytes_per_pixel = color_params.BytesPerPixel();
+ CheckedNumeric<int> data_size = bytes_per_pixel;
+ data_size *= rect.Size().Area();
+ if (!data_size.IsValid() ||
+ data_size.ValueOrDie() > v8::TypedArray::kMaxLength)
+ return false;
+
+ size_t alloc_size_in_bytes = rect.Size().Area() * bytes_per_pixel;
+ if (!src_image) {
+ auto data = WTF::ArrayBufferContents::CreateDataHandle(
+ alloc_size_in_bytes, WTF::ArrayBufferContents::kZeroInitialize);
+ if (!data)
+ return false;
+ WTF::ArrayBufferContents result(std::move(data),
+ WTF::ArrayBufferContents::kNotShared);
+ result.Transfer(dest_contents);
+ return true;
+ }
+
+ const bool may_have_stray_area =
+ is_accelerated // GPU readback may fail silently
+ || rect.X() < 0 || rect.Y() < 0 ||
+ rect.MaxX() > src_image->Size().Width() ||
+ rect.MaxY() > src_image->Size().Height();
+ WTF::ArrayBufferContents::InitializationPolicy initialization_policy =
+ may_have_stray_area ? WTF::ArrayBufferContents::kZeroInitialize
+ : WTF::ArrayBufferContents::kDontInitialize;
+ auto data = WTF::ArrayBufferContents::CreateDataHandle(alloc_size_in_bytes,
+ initialization_policy);
+ if (!data)
+ return false;
+ WTF::ArrayBufferContents result(std::move(data),
+ WTF::ArrayBufferContents::kNotShared);
+
+ SkColorType color_type =
+ (color_params.GetSkColorType() == kRGBA_F16_SkColorType)
+ ? kRGBA_F16_SkColorType
+ : kRGBA_8888_SkColorType;
+ SkImageInfo info = SkImageInfo::Make(
+ rect.Width(), rect.Height(), color_type, kUnpremul_SkAlphaType,
+ color_params.GetSkColorSpaceForSkSurfaces());
+ sk_sp<SkImage> sk_image = src_image->PaintImageForCurrentFrame().GetSkImage();
+ bool read_pixels_successful = sk_image->readPixels(
+ info, result.Data(), info.minRowBytes(), rect.X(), rect.Y());
+ DCHECK(read_pixels_successful ||
+ !sk_image->bounds().intersect(SkIRect::MakeXYWH(
+ rect.X(), rect.Y(), info.width(), info.height())));
+ result.Transfer(dest_contents);
+ return true;
+}
+
+const gpu::SyncToken& StaticBitmapImage::GetSyncToken() const {
+ static const gpu::SyncToken sync_token;
+ return sync_token;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/static_bitmap_image.h b/chromium/third_party/blink/renderer/platform/graphics/static_bitmap_image.h
new file mode 100644
index 00000000000..ea20292876e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/static_bitmap_image.h
@@ -0,0 +1,145 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_STATIC_BITMAP_IMAGE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_STATIC_BITMAP_IMAGE_H_
+
+#include "base/memory/weak_ptr.h"
+#include "gpu/command_buffer/common/mailbox.h"
+#include "gpu/command_buffer/common/sync_token.h"
+#include "third_party/blink/renderer/platform/graphics/canvas_color_params.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_types.h"
+#include "third_party/blink/renderer/platform/graphics/image.h"
+#include "third_party/blink/renderer/platform/wtf/typed_arrays/uint8_array.h"
+#include "third_party/khronos/GLES2/gl2.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+namespace WTF {
+
+class ArrayBufferContents;
+
+} // namespace WTF
+
+namespace gpu {
+namespace gles2 {
+class GLES2Interface;
+}
+} // namespace gpu
+
+namespace blink {
+
+class WebGraphicsContext3DProviderWrapper;
+
+class PLATFORM_EXPORT StaticBitmapImage : public Image {
+ public:
+ // WebGraphicsContext3DProviderWrapper argument only needs to be provided if
+ // The SkImage is texture backed, in which case it must be a reference to the
+ // context provider that owns the GrContext with which the SkImage is
+ // associated.
+ static scoped_refptr<StaticBitmapImage> Create(
+ sk_sp<SkImage>,
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper> = nullptr);
+ static scoped_refptr<StaticBitmapImage> Create(PaintImage);
+ static scoped_refptr<StaticBitmapImage> Create(scoped_refptr<Uint8Array>&&,
+ const SkImageInfo&);
+ static scoped_refptr<StaticBitmapImage> Create(WTF::ArrayBufferContents&,
+ const SkImageInfo&);
+
+ bool IsStaticBitmapImage() const override { return true; }
+
+ // Methods overridden by all sub-classes
+ virtual ~StaticBitmapImage() = default;
+ // Creates a gpu copy of the image using the given ContextProvider. Should
+ // not be called if IsTextureBacked() is already true. May return null if the
+ // conversion failed (for instance if the context had an error).
+ virtual scoped_refptr<StaticBitmapImage> MakeAccelerated(
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_wrapper) = 0;
+
+ // Methods have common implementation for all sub-classes
+ bool CurrentFrameIsComplete() override { return true; }
+ void DestroyDecodedData() {}
+
+ // Methods that have a default implementation, and overridden by only one
+ // sub-class
+ virtual bool HasMailbox() const { return false; }
+ virtual bool IsValid() const { return true; }
+ virtual void Transfer() {}
+ virtual void Abandon() {}
+
+ // Creates a non-gpu copy of the image, or returns this if image is already
+ // non-gpu.
+ virtual scoped_refptr<StaticBitmapImage> MakeUnaccelerated() { return this; }
+
+ // Methods overridden by AcceleratedStaticBitmapImage only
+ // Assumes the destination texture has already been allocated.
+ virtual bool CopyToTexture(gpu::gles2::GLES2Interface*,
+ GLenum,
+ GLuint,
+ bool,
+ bool,
+ const IntPoint&,
+ const IntRect&) {
+ NOTREACHED();
+ return false;
+ }
+
+ // EnsureMailbox modifies the internal state of an accelerated static bitmap
+ // image to make sure that it is represented by a Mailbox. This must be
+ // called whenever the image is intende to be used in a differen GPU context.
+ // It is important to use the correct MailboxSyncMode in order to achieve
+ // optimal performance without compromising security or causeing graphics
+ // glitches. Here is how to select the aprropriate mode:
+ //
+ // Case 1: Passing to a gpu context that is on a separate channel.
+ // Note: a context in a separate process is necessarily on another channel.
+ // Use kVerifiedSyncToken. Or use kUnverifiedSyncToken with a later call
+ // to VerifySyncTokensCHROMIUM()
+ // Case 2: Passing to a gpu context that is on the same channel but not the
+ // same stream.
+ // Use kUnverifiedSyncToken
+ // Case 3: Passing to a gpu context on the same stream.
+ // Use kOrderingBarrier
+ virtual void EnsureMailbox(MailboxSyncMode, GLenum filter) { NOTREACHED(); }
+ virtual const gpu::Mailbox& GetMailbox() const {
+ NOTREACHED();
+ static const gpu::Mailbox mailbox;
+ return mailbox;
+ }
+ virtual const gpu::SyncToken& GetSyncToken() const;
+ virtual void UpdateSyncToken(gpu::SyncToken) { NOTREACHED(); }
+ virtual bool IsPremultiplied() const { return true; }
+
+ // Methods have exactly the same implementation for all sub-classes
+ bool OriginClean() const { return is_origin_clean_; }
+ void SetOriginClean(bool flag) { is_origin_clean_ = flag; }
+ scoped_refptr<StaticBitmapImage> ConvertToColorSpace(
+ sk_sp<SkColorSpace>,
+ SkTransferFunctionBehavior);
+
+ static bool ConvertToArrayBufferContents(
+ scoped_refptr<StaticBitmapImage> src_image,
+ WTF::ArrayBufferContents& dest_contents,
+ const IntRect&,
+ const CanvasColorParams&,
+ bool is_accelerated = false);
+
+ protected:
+ // Helper for sub-classes
+ void DrawHelper(PaintCanvas*,
+ const PaintFlags&,
+ const FloatRect&,
+ const FloatRect&,
+ ImageClampingMode,
+ const PaintImage&);
+
+ // The following property is here because the SkImage API doesn't expose the
+ // info. It is applied to both UnacceleratedStaticBitmapImage and
+ // AcceleratedStaticBitmapImage. To change this property, the call site would
+ // have to call SetOriginClean().
+ bool is_origin_clean_ = true;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/graphics/static_bitmap_image_test.cc b/chromium/third_party/blink/renderer/platform/graphics/static_bitmap_image_test.cc
new file mode 100644
index 00000000000..45c31b826b4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/static_bitmap_image_test.cc
@@ -0,0 +1,35 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_contents.h"
+#include "third_party/skia/include/core/SkSurface.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+class StaticBitmapImageTest : public testing::Test {};
+
+// This test verifies if requesting a large ImageData that cannot be handled by
+// V8 is denied by StaticBitmapImage. This prevents V8 from crashing the
+// renderer if the user asks to get back the ImageData.
+TEST_F(StaticBitmapImageTest,
+ ConvertArrayBufferContentsTooBigToAllocateDoesNotCrash) {
+ SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
+ sk_sp<SkSurface> surface = SkSurface::MakeRaster(info);
+ EXPECT_TRUE(!!surface);
+
+ scoped_refptr<StaticBitmapImage> image =
+ StaticBitmapImage::Create(surface->makeImageSnapshot());
+
+ IntRect too_big_rect(IntPoint(0, 0),
+ IntSize(1, (v8::TypedArray::kMaxLength / 4) + 1));
+ WTF::ArrayBufferContents contents;
+ EXPECT_FALSE(StaticBitmapImage::ConvertToArrayBufferContents(
+ image, contents, too_big_rect, CanvasColorParams()));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/stroke_data.cc b/chromium/third_party/blink/renderer/platform/graphics/stroke_data.cc
new file mode 100644
index 00000000000..bc2ef32e0a2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/stroke_data.cc
@@ -0,0 +1,144 @@
+// Copyright (C) 2013 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 <memory>
+#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
+#include "third_party/blink/renderer/platform/graphics/stroke_data.h"
+#include "third_party/skia/include/effects/SkDashPathEffect.h"
+
+namespace blink {
+
+void StrokeData::SetLineDash(const DashArray& dashes, float dash_offset) {
+ // FIXME: This is lifted directly off SkiaSupport, lines 49-74
+ // so it is not guaranteed to work correctly.
+ size_t dash_length = dashes.size();
+ if (!dash_length) {
+ // If no dash is set, revert to solid stroke
+ // FIXME: do we need to set NoStroke in some cases?
+ style_ = kSolidStroke;
+ dash_.reset();
+ return;
+ }
+
+ size_t count = !(dash_length % 2) ? dash_length : dash_length * 2;
+ auto intervals = std::make_unique<SkScalar[]>(count);
+
+ for (unsigned i = 0; i < count; i++)
+ intervals[i] = dashes[i % dash_length];
+
+ dash_ = SkDashPathEffect::Make(intervals.get(), count, dash_offset);
+}
+
+void StrokeData::SetupPaint(PaintFlags* flags,
+ const int length,
+ const int dash_thickness) const {
+ flags->setStyle(PaintFlags::kStroke_Style);
+ flags->setStrokeWidth(SkFloatToScalar(thickness_));
+ flags->setStrokeCap(line_cap_);
+ flags->setStrokeJoin(line_join_);
+ flags->setStrokeMiter(SkFloatToScalar(miter_limit_));
+
+ SetupPaintDashPathEffect(flags, length, dash_thickness);
+}
+
+void StrokeData::SetupPaintDashPathEffect(PaintFlags* flags,
+ const int length,
+ const int dash_thickness) const {
+ int dash_width = dash_thickness ? dash_thickness : thickness_;
+ if (dash_) {
+ flags->setPathEffect(dash_);
+ } else if (StrokeIsDashed(dash_width, style_)) {
+ float dash_length = dash_width;
+ float gap_length = dash_length;
+ if (style_ == kDashedStroke) {
+ dash_length *= StrokeData::DashLengthRatio(dash_width);
+ gap_length *= StrokeData::DashGapRatio(dash_width);
+ }
+ if (length <= dash_length * 2) {
+ // No space for dashes
+ flags->setPathEffect(nullptr);
+ } else if (length <= 2 * dash_length + gap_length) {
+ // Exactly 2 dashes proportionally sized
+ float multiplier = length / (2 * dash_length + gap_length);
+ SkScalar intervals[2] = {dash_length * multiplier,
+ gap_length * multiplier};
+ flags->setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
+ } else {
+ float gap = gap_length;
+ if (style_ == kDashedStroke)
+ gap = SelectBestDashGap(length, dash_length, gap_length);
+ SkScalar intervals[2] = {dash_length, gap};
+ flags->setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
+ }
+ } else if (style_ == kDottedStroke) {
+ flags->setStrokeCap((PaintFlags::Cap)kRoundCap);
+ // Adjust the width to get equal dot spacing as much as possible.
+ float per_dot_length = dash_width * 2;
+ if (length < per_dot_length) {
+ // Not enoguh space for 2 dots. Just draw 1 by giving a gap that is
+ // bigger than the length.
+ SkScalar intervals[2] = {0, per_dot_length};
+ flags->setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
+ return;
+ }
+
+ // Epsilon ensures that we get a whole dot at the end of the line,
+ // even if that dot is a little inside the true endpoint. Without it
+ // we can drop the end dot due to rounding along the line.
+ static const float kEpsilon = 1.0e-2f;
+ float gap = SelectBestDashGap(length, dash_width, dash_width);
+ SkScalar intervals[2] = {0, gap + dash_width - kEpsilon};
+ flags->setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
+ } else {
+ flags->setPathEffect(nullptr);
+ }
+}
+
+bool StrokeData::StrokeIsDashed(float width, StrokeStyle style) {
+ return style == kDashedStroke || (style == kDottedStroke && width <= 3);
+}
+
+float StrokeData::SelectBestDashGap(float stroke_length,
+ float dash_length,
+ float gap_length) {
+ // Determine what number of dashes gives the minimum deviation from
+ // gapLength between dashes. Set the gap to that width.
+ float min_num_dashes =
+ floorf((stroke_length + gap_length) / (dash_length + gap_length));
+ float max_num_dashes = min_num_dashes + 1;
+ float min_gap =
+ (stroke_length - min_num_dashes * dash_length) / (min_num_dashes - 1);
+ float max_gap =
+ (stroke_length - max_num_dashes * dash_length) / (max_num_dashes - 1);
+ return (max_gap <= 0) ||
+ (fabs(min_gap - gap_length) < fabs(max_gap - gap_length))
+ ? min_gap
+ : max_gap;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/stroke_data.h b/chromium/third_party/blink/renderer/platform/graphics/stroke_data.h
new file mode 100644
index 00000000000..859cd293d25
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/stroke_data.h
@@ -0,0 +1,128 @@
+// Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_STROKE_DATA_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_STROKE_DATA_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/graphics/dash_array.h"
+#include "third_party/blink/renderer/platform/graphics/gradient.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_types.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
+#include "third_party/blink/renderer/platform/graphics/pattern.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/skia/include/core/SkPaint.h"
+#include "third_party/skia/include/core/SkPathEffect.h"
+
+namespace blink {
+
+// Encapsulates stroke geometry information.
+// It is pulled out of GraphicsContextState to enable other methods to use it.
+class PLATFORM_EXPORT StrokeData final {
+ DISALLOW_NEW();
+
+ public:
+ StrokeData()
+ : style_(kSolidStroke),
+ thickness_(0),
+ line_cap_(PaintFlags::kDefault_Cap),
+ line_join_(PaintFlags::kDefault_Join),
+ miter_limit_(4) {}
+
+ StrokeStyle Style() const { return style_; }
+ void SetStyle(StrokeStyle style) { style_ = style; }
+
+ float Thickness() const { return thickness_; }
+ void SetThickness(float thickness) { thickness_ = thickness; }
+
+ void SetLineCap(LineCap cap) { line_cap_ = (PaintFlags::Cap)cap; }
+
+ void SetLineJoin(LineJoin join) { line_join_ = (PaintFlags::Join)join; }
+
+ float MiterLimit() const { return miter_limit_; }
+ void SetMiterLimit(float miter_limit) { miter_limit_ = miter_limit; }
+
+ void SetLineDash(const DashArray&, float);
+
+ // Sets everything on the paint except the pattern, gradient and color.
+ // If a non-zero length is provided, the number of dashes/dots on a
+ // dashed/dotted line will be adjusted to start and end that length with a
+ // dash/dot. If non-zero, dash_thickness is the thickness to use when
+ // deciding on dash sizes. Used in border painting when we stroke thick
+ // to allow for clipping at corners, but still want small dashes.
+ void SetupPaint(PaintFlags*,
+ const int length = 0,
+ const int dash_thickess = 0) const;
+
+ // Setup any DashPathEffect on the paint. See SetupPaint above for parameter
+ // information.
+ void SetupPaintDashPathEffect(PaintFlags*,
+ const int path_length = 0,
+ const int dash_thickness = 0) const;
+
+ // Determine whether a stroked line should be drawn using dashes. In practice,
+ // we draw dashes when a dashed stroke is specified or when a dotted stroke
+ // is specified but the line width is too small to draw circles.
+ static bool StrokeIsDashed(float width, StrokeStyle);
+
+ // The length of the dash relative to the line thickness for dashed stroking.
+ // A different dash length may be used when dashes are adjusted to better
+ // fit a given length path. Thin lines need longer dashes to avoid
+ // looking like dots when drawn.
+ static float DashLengthRatio(float thickness) {
+ return thickness >= 3 ? 2.0 : 3.0;
+ }
+
+ // The length of the gap between dashes relative to the line thickness for
+ // dashed stroking. A different gap may be used when dashes are adjusted to
+ // better fit a given length path. Thin lines need longer gaps to avoid
+ // looking like a continuous line when drawn.
+ static float DashGapRatio(float thickness) {
+ return thickness >= 3 ? 1.0 : 2.0;
+ }
+
+ // Return a dash gap size that places dashes at each end of a stroke that is
+ // strokeLength long, given preferred dash and gap sizes. The gap returned is
+ // the one that minimizes deviation from the preferred gap length.
+ static float SelectBestDashGap(float stroke_length,
+ float dash_length,
+ float gap_length);
+
+ private:
+ StrokeStyle style_;
+ float thickness_;
+ PaintFlags::Cap line_cap_;
+ PaintFlags::Join line_join_;
+ float miter_limit_;
+ sk_sp<SkPathEffect> dash_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_STROKE_DATA_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/surface_layer_bridge.cc b/chromium/third_party/blink/renderer/platform/graphics/surface_layer_bridge.cc
new file mode 100644
index 00000000000..f46a7d111e2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/surface_layer_bridge.cc
@@ -0,0 +1,103 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/surface_layer_bridge.h"
+
+#include "base/feature_list.h"
+#include "cc/layers/layer.h"
+#include "cc/layers/solid_color_layer.h"
+#include "cc/layers/surface_layer.h"
+#include "components/viz/common/surfaces/surface_id.h"
+#include "components/viz/common/surfaces/surface_info.h"
+#include "media/base/media_switches.h"
+#include "third_party/blink/public/platform/interface_provider.h"
+#include "third_party/blink/public/platform/modules/offscreencanvas/offscreen_canvas_surface.mojom-blink.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_compositor_support.h"
+#include "third_party/blink/public/platform/web_layer.h"
+#include "third_party/blink/public/platform/web_layer_tree_view.h"
+#include "third_party/blink/renderer/platform/mojo/mojo_helper.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace blink {
+
+SurfaceLayerBridge::SurfaceLayerBridge(WebLayerTreeView* layer_tree_view,
+ WebSurfaceLayerBridgeObserver* observer)
+ : observer_(observer),
+ binding_(this),
+ frame_sink_id_(Platform::Current()->GenerateFrameSinkId()),
+ parent_frame_sink_id_(layer_tree_view ? layer_tree_view->GetFrameSinkId()
+ : viz::FrameSinkId()) {
+ mojom::blink::OffscreenCanvasProviderPtr provider;
+ Platform::Current()->GetInterfaceProvider()->GetInterface(
+ mojo::MakeRequest(&provider));
+ // TODO(xlai): Ensure OffscreenCanvas commit() is still functional when a
+ // frame-less HTML canvas's document is reparenting under another frame.
+ // See crbug.com/683172.
+ blink::mojom::blink::OffscreenCanvasSurfaceClientPtr client;
+ binding_.Bind(mojo::MakeRequest(&client));
+ provider->CreateOffscreenCanvasSurface(parent_frame_sink_id_, frame_sink_id_,
+ std::move(client));
+}
+
+SurfaceLayerBridge::~SurfaceLayerBridge() {
+ observer_ = nullptr;
+}
+
+void SurfaceLayerBridge::CreateSolidColorLayer() {
+ cc_layer_ = cc::SolidColorLayer::Create();
+ cc_layer_->SetBackgroundColor(SK_ColorTRANSPARENT);
+
+ web_layer_ = Platform::Current()->CompositorSupport()->CreateLayerFromCCLayer(
+ cc_layer_.get());
+
+ if (observer_)
+ observer_->RegisterContentsLayer(web_layer_.get());
+}
+
+void SurfaceLayerBridge::OnFirstSurfaceActivation(
+ const viz::SurfaceInfo& surface_info) {
+ if (!current_surface_id_.is_valid() && surface_info.is_valid()) {
+ // First time a SurfaceId is received.
+ current_surface_id_ = surface_info.id();
+ if (web_layer_) {
+ if (observer_)
+ observer_->UnregisterContentsLayer(web_layer_.get());
+ web_layer_->RemoveFromParent();
+ }
+
+ scoped_refptr<cc::SurfaceLayer> surface_layer = cc::SurfaceLayer::Create();
+ surface_layer->SetPrimarySurfaceId(
+ surface_info.id(), cc::DeadlinePolicy::UseDefaultDeadline());
+ surface_layer->SetFallbackSurfaceId(surface_info.id());
+ surface_layer->SetStretchContentToFillBounds(true);
+ surface_layer->SetIsDrawable(true);
+ cc_layer_ = surface_layer;
+
+ web_layer_ =
+ Platform::Current()->CompositorSupport()->CreateLayerFromCCLayer(
+ cc_layer_.get());
+ if (observer_)
+ observer_->RegisterContentsLayer(web_layer_.get());
+ } else if (current_surface_id_ != surface_info.id()) {
+ // A different SurfaceId is received, prompting change to existing
+ // SurfaceLayer.
+ current_surface_id_ = surface_info.id();
+ cc::SurfaceLayer* surface_layer =
+ static_cast<cc::SurfaceLayer*>(cc_layer_.get());
+ surface_layer->SetPrimarySurfaceId(
+ surface_info.id(), cc::DeadlinePolicy::UseDefaultDeadline());
+ surface_layer->SetFallbackSurfaceId(surface_info.id());
+ }
+
+ if (observer_) {
+ observer_->OnWebLayerUpdated();
+ observer_->OnSurfaceIdUpdated(surface_info.id());
+ }
+
+ cc_layer_->SetBounds(surface_info.size_in_pixels());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/surface_layer_bridge.h b/chromium/third_party/blink/renderer/platform/graphics/surface_layer_bridge.h
new file mode 100644
index 00000000000..a2ad32b512b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/surface_layer_bridge.h
@@ -0,0 +1,65 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_SURFACE_LAYER_BRIDGE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_SURFACE_LAYER_BRIDGE_H_
+
+#include <memory>
+#include "base/memory/scoped_refptr.h"
+#include "components/viz/common/surfaces/surface_id.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "third_party/blink/public/platform/modules/offscreencanvas/offscreen_canvas_surface.mojom-blink.h"
+#include "third_party/blink/public/platform/web_surface_layer_bridge.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace cc {
+class Layer;
+} // namespace cc
+
+namespace viz {
+class SurfaceInfo;
+} // namespace viz
+
+namespace blink {
+
+class WebLayer;
+class WebLayerTreeView;
+
+// The SurfaceLayerBridge facilitates communication about changes to a Surface
+// between the Render and Browser processes.
+class PLATFORM_EXPORT SurfaceLayerBridge
+ : public blink::mojom::blink::OffscreenCanvasSurfaceClient,
+ public WebSurfaceLayerBridge {
+ public:
+ SurfaceLayerBridge(WebLayerTreeView*, WebSurfaceLayerBridgeObserver*);
+ virtual ~SurfaceLayerBridge();
+
+ void CreateSolidColorLayer();
+
+ // Implementation of blink::mojom::blink::OffscreenCanvasSurfaceClient
+ void OnFirstSurfaceActivation(const viz::SurfaceInfo&) override;
+
+ // Implementation of WebSurfaceLayerBridge.
+ WebLayer* GetWebLayer() const override { return web_layer_.get(); }
+
+ const viz::FrameSinkId& GetFrameSinkId() const override {
+ return frame_sink_id_;
+ }
+
+ private:
+ scoped_refptr<cc::Layer> cc_layer_;
+ std::unique_ptr<WebLayer> web_layer_;
+
+ WebSurfaceLayerBridgeObserver* observer_;
+
+ mojo::Binding<blink::mojom::blink::OffscreenCanvasSurfaceClient> binding_;
+
+ const viz::FrameSinkId frame_sink_id_;
+ viz::SurfaceId current_surface_id_;
+ const viz::FrameSinkId parent_frame_sink_id_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_SURFACE_LAYER_BRIDGE_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/texture_holder.h b/chromium/third_party/blink/renderer/platform/graphics/texture_holder.h
new file mode 100644
index 00000000000..def091d1f44
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/texture_holder.h
@@ -0,0 +1,83 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_TEXTURE_HOLDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_TEXTURE_HOLDER_H_
+
+#include "base/memory/weak_ptr.h"
+#include "gpu/command_buffer/common/mailbox.h"
+#include "gpu/command_buffer/common/sync_token.h"
+#include "third_party/blink/renderer/platform/geometry/int_size.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_types.h"
+#include "third_party/blink/renderer/platform/graphics/image.h"
+#include "third_party/blink/renderer/platform/graphics/web_graphics_context_3d_provider_wrapper.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT TextureHolder {
+ public:
+ virtual ~TextureHolder() = default;
+
+ // Methods overridden by all sub-classes
+ virtual bool IsSkiaTextureHolder() = 0;
+ virtual bool IsMailboxTextureHolder() = 0;
+ virtual IntSize Size() const = 0;
+ virtual bool CurrentFrameKnownToBeOpaque() = 0;
+ virtual bool IsValid() const = 0;
+
+ // Methods overrided by MailboxTextureHolder
+ virtual const gpu::Mailbox& GetMailbox() const {
+ NOTREACHED();
+ static const gpu::Mailbox mailbox;
+ return mailbox;
+ }
+ virtual const gpu::SyncToken& GetSyncToken() const {
+ static const gpu::SyncToken sync_token;
+ return sync_token;
+ }
+ virtual void UpdateSyncToken(gpu::SyncToken) { NOTREACHED(); }
+ virtual void Sync(MailboxSyncMode) { NOTREACHED(); }
+ virtual bool IsCrossThread() const { return false; }
+
+ // Methods overridden by SkiaTextureHolder
+ virtual sk_sp<SkImage> GetSkImage() {
+ NOTREACHED();
+ return nullptr;
+ }
+ virtual void Abandon() { is_abandoned_ = true; } // Overrides must call base.
+
+ // Methods that have exactly the same impelmentation for all sub-classes
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper> ContextProviderWrapper()
+ const {
+ return context_provider_wrapper_;
+ }
+
+ WebGraphicsContext3DProvider* ContextProvider() const {
+ return context_provider_wrapper_
+ ? context_provider_wrapper_->ContextProvider()
+ : nullptr;
+ }
+ bool IsAbandoned() const { return is_abandoned_; }
+
+ protected:
+ TextureHolder(base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&
+ context_provider_wrapper)
+ : context_provider_wrapper_(std::move(context_provider_wrapper)) {}
+
+ private:
+ // Keep a clone of the SingleThreadTaskRunner. This is to handle the case
+ // where the AcceleratedStaticBitmapImage was created on one thread and
+ // transferred to another thread, and the original thread gone out of scope,
+ // and that we need to clear the resouces associated with that
+ // AcceleratedStaticBitmapImage on the original thread.
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper_;
+ bool is_abandoned_ = false;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_TEXTURE_HOLDER_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/touch_action.h b/chromium/third_party/blink/renderer/platform/graphics/touch_action.h
new file mode 100644
index 00000000000..4ab2dbc2b3a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/touch_action.h
@@ -0,0 +1,17 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_TOUCH_ACTION_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_TOUCH_ACTION_H_
+
+#include "cc/input/touch_action.h"
+
+namespace blink {
+
+const size_t kTouchActionBits = cc::kTouchActionBits;
+using TouchAction = cc::TouchAction;
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_TOUCH_ACTION_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.cc b/chromium/third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.cc
new file mode 100644
index 00000000000..bafffea5e9a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.cc
@@ -0,0 +1,93 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h"
+
+#include "components/viz/common/gpu/context_provider.h"
+#include "third_party/blink/public/platform/web_graphics_context_3d_provider.h"
+#include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h"
+#include "third_party/blink/renderer/platform/graphics/web_graphics_context_3d_provider_wrapper.h"
+#include "third_party/skia/include/core/SkImage.h"
+
+namespace blink {
+
+scoped_refptr<UnacceleratedStaticBitmapImage>
+UnacceleratedStaticBitmapImage::Create(sk_sp<SkImage> image) {
+ DCHECK(!image->isTextureBacked());
+ return base::AdoptRef(new UnacceleratedStaticBitmapImage(std::move(image)));
+}
+
+UnacceleratedStaticBitmapImage::UnacceleratedStaticBitmapImage(
+ sk_sp<SkImage> image) {
+ CHECK(image);
+ DCHECK(!image->isLazyGenerated());
+
+ paint_image_ =
+ CreatePaintImageBuilder()
+ .set_image(std::move(image), cc::PaintImage::GetNextContentId())
+ .TakePaintImage();
+}
+
+scoped_refptr<UnacceleratedStaticBitmapImage>
+UnacceleratedStaticBitmapImage::Create(PaintImage image) {
+ return base::AdoptRef(new UnacceleratedStaticBitmapImage(std::move(image)));
+}
+
+UnacceleratedStaticBitmapImage::UnacceleratedStaticBitmapImage(PaintImage image)
+ : paint_image_(std::move(image)) {
+ CHECK(paint_image_.GetSkImage());
+}
+
+UnacceleratedStaticBitmapImage::~UnacceleratedStaticBitmapImage() = default;
+
+IntSize UnacceleratedStaticBitmapImage::Size() const {
+ return IntSize(paint_image_.width(), paint_image_.height());
+}
+
+bool UnacceleratedStaticBitmapImage::IsPremultiplied() const {
+ return paint_image_.GetSkImage()->alphaType() ==
+ SkAlphaType::kPremul_SkAlphaType;
+}
+
+scoped_refptr<StaticBitmapImage>
+UnacceleratedStaticBitmapImage::MakeAccelerated(
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_wrapper) {
+ if (!context_wrapper)
+ return nullptr; // Can happen if the context is lost.
+
+ GrContext* grcontext = context_wrapper->ContextProvider()->GetGrContext();
+ if (!grcontext)
+ return nullptr; // Can happen if the context is lost.
+
+ // TODO(crbug.com/782383): This can return a SkColorSpace, which should be
+ // passed along.
+ sk_sp<SkImage> gpu_skimage =
+ paint_image_.GetSkImage()->makeTextureImage(grcontext, nullptr);
+ if (!gpu_skimage)
+ return nullptr;
+
+ return AcceleratedStaticBitmapImage::CreateFromSkImage(
+ std::move(gpu_skimage), std::move(context_wrapper));
+}
+
+bool UnacceleratedStaticBitmapImage::CurrentFrameKnownToBeOpaque() {
+ return paint_image_.GetSkImage()->isOpaque();
+}
+
+void UnacceleratedStaticBitmapImage::Draw(PaintCanvas* canvas,
+ const PaintFlags& flags,
+ const FloatRect& dst_rect,
+ const FloatRect& src_rect,
+ RespectImageOrientationEnum,
+ ImageClampingMode clamp_mode,
+ ImageDecodingMode) {
+ StaticBitmapImage::DrawHelper(canvas, flags, dst_rect, src_rect, clamp_mode,
+ PaintImageForCurrentFrame());
+}
+
+PaintImage UnacceleratedStaticBitmapImage::PaintImageForCurrentFrame() {
+ return paint_image_;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h b/chromium/third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h
new file mode 100644
index 00000000000..fbd2ededff0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h
@@ -0,0 +1,46 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_UNACCELERATED_STATIC_BITMAP_IMAGE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_UNACCELERATED_STATIC_BITMAP_IMAGE_H_
+
+#include "base/memory/weak_ptr.h"
+#include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT UnacceleratedStaticBitmapImage final
+ : public StaticBitmapImage {
+ public:
+ ~UnacceleratedStaticBitmapImage() override;
+ static scoped_refptr<UnacceleratedStaticBitmapImage> Create(sk_sp<SkImage>);
+ static scoped_refptr<UnacceleratedStaticBitmapImage> Create(PaintImage);
+
+ bool CurrentFrameKnownToBeOpaque() override;
+ IntSize Size() const override;
+ bool IsPremultiplied() const override;
+ scoped_refptr<StaticBitmapImage> MakeAccelerated(
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_wrapper)
+ override;
+
+ void Draw(PaintCanvas*,
+ const PaintFlags&,
+ const FloatRect& dst_rect,
+ const FloatRect& src_rect,
+ RespectImageOrientationEnum,
+ ImageClampingMode,
+ ImageDecodingMode) override;
+
+ PaintImage PaintImageForCurrentFrame() override;
+
+ private:
+ UnacceleratedStaticBitmapImage(sk_sp<SkImage>);
+ UnacceleratedStaticBitmapImage(PaintImage);
+
+ PaintImage paint_image_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.cc b/chromium/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.cc
new file mode 100644
index 00000000000..de63be8779d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.cc
@@ -0,0 +1,128 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/video_frame_resource_provider.h"
+
+#include <memory>
+#include "base/bind.h"
+#include "base/trace_event/trace_event.h"
+#include "cc/resources/layer_tree_resource_provider.h"
+#include "cc/resources/video_resource_updater.h"
+#include "components/viz/common/gpu/context_provider.h"
+#include "components/viz/common/quads/render_pass.h"
+#include "components/viz/common/quads/solid_color_draw_quad.h"
+#include "components/viz/common/quads/texture_draw_quad.h"
+#include "components/viz/common/quads/yuv_video_draw_quad.h"
+#include "media/base/video_frame.h"
+
+namespace cc {
+class VideoFrameExternalResources;
+} // namespace cc
+
+namespace blink {
+
+VideoFrameResourceProvider::VideoFrameResourceProvider(
+ WebContextProviderCallback context_provider_callback,
+ viz::SharedBitmapManager* shared_bitmap_manager,
+ gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
+ const cc::LayerTreeSettings& settings)
+ : context_provider_callback_(std::move(context_provider_callback)),
+ shared_bitmap_manager_(shared_bitmap_manager),
+ gpu_memory_buffer_manager_(gpu_memory_buffer_manager),
+ settings_(settings),
+ weak_ptr_factory_(this) {}
+
+VideoFrameResourceProvider::~VideoFrameResourceProvider() {
+ if (context_provider_) {
+ resource_updater_ = nullptr;
+ resource_provider_ = nullptr;
+ }
+}
+
+void VideoFrameResourceProvider::ObtainContextProvider() {
+ context_provider_callback_.Run(base::BindOnce(
+ &VideoFrameResourceProvider::Initialize, weak_ptr_factory_.GetWeakPtr()));
+}
+
+void VideoFrameResourceProvider::Initialize(
+ viz::ContextProvider* media_context_provider) {
+ // TODO(lethalantidote): Need to handle null contexts.
+ // https://crbug/768565
+ CHECK(media_context_provider);
+ context_provider_ = media_context_provider;
+
+ resource_provider_ = std::make_unique<cc::LayerTreeResourceProvider>(
+ media_context_provider, shared_bitmap_manager_,
+ gpu_memory_buffer_manager_, true, settings_.resource_settings);
+
+ // TODO(kylechar): VideoResourceUpdater needs something it can notify about
+ // SharedBitmaps that isn't a LayerTreeFrameSink. https://crbug.com/730660#c88
+ resource_updater_ = std::make_unique<cc::VideoResourceUpdater>(
+ context_provider_, nullptr, resource_provider_.get(),
+ settings_.use_stream_video_draw_quad,
+ settings_.resource_settings.use_gpu_memory_buffer_resources);
+}
+
+void VideoFrameResourceProvider::AppendQuads(
+ viz::RenderPass* render_pass,
+ scoped_refptr<media::VideoFrame> frame,
+ media::VideoRotation rotation) {
+ TRACE_EVENT0("media", "VideoFrameResourceProvider::AppendQuads");
+ gfx::Transform transform = gfx::Transform();
+ gfx::Size rotated_size = frame->coded_size();
+
+ switch (rotation) {
+ case media::VIDEO_ROTATION_90:
+ rotated_size = gfx::Size(rotated_size.height(), rotated_size.width());
+ transform.Rotate(90.0);
+ transform.Translate(0.0, -rotated_size.height());
+ break;
+ case media::VIDEO_ROTATION_180:
+ transform.Rotate(180.0);
+ transform.Translate(-rotated_size.width(), -rotated_size.height());
+ break;
+ case media::VIDEO_ROTATION_270:
+ rotated_size = gfx::Size(rotated_size.height(), rotated_size.width());
+ transform.Rotate(270.0);
+ transform.Translate(-rotated_size.width(), 0);
+ break;
+ case media::VIDEO_ROTATION_0:
+ break;
+ }
+
+ resource_updater_->ObtainFrameResources(frame);
+ // TODO(lethalantidote) : update with true value;
+ bool contents_opaque = true;
+ gfx::Rect visible_layer_rect = gfx::Rect(rotated_size);
+ gfx::Rect clip_rect = gfx::Rect(frame->coded_size());
+ bool is_clipped = false;
+ float draw_opacity = 1.0f;
+ int sorting_context_id = 0;
+
+ // Internal to this compositor frame, this video quad is never occluded,
+ // thus the full quad is visible.
+ gfx::Rect visible_quad_rect = gfx::Rect(rotated_size);
+
+ resource_updater_->AppendQuads(render_pass, std::move(frame), transform,
+ rotated_size, visible_layer_rect, clip_rect,
+ is_clipped, contents_opaque, draw_opacity,
+ sorting_context_id, visible_quad_rect);
+}
+
+void VideoFrameResourceProvider::ReleaseFrameResources() {
+ resource_updater_->ReleaseFrameResources();
+}
+
+void VideoFrameResourceProvider::PrepareSendToParent(
+ const cc::LayerTreeResourceProvider::ResourceIdArray& resource_ids,
+ std::vector<viz::TransferableResource>* transferable_resources) {
+ resource_provider_->PrepareSendToParent(resource_ids, transferable_resources);
+}
+
+void VideoFrameResourceProvider::ReceiveReturnsFromParent(
+ const std::vector<viz::ReturnedResource>& transferable_resources) {
+ resource_provider_->ReceiveReturnsFromParent(transferable_resources);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.h b/chromium/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.h
new file mode 100644
index 00000000000..9320146c2b2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.h
@@ -0,0 +1,67 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_VIDEO_FRAME_RESOURCE_PROVIDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_VIDEO_FRAME_RESOURCE_PROVIDER_H_
+
+#include "base/memory/weak_ptr.h"
+#include "cc/resources/layer_tree_resource_provider.h"
+#include "cc/resources/video_resource_updater.h"
+#include "cc/trees/layer_tree_settings.h"
+#include "media/base/video_frame.h"
+#include "third_party/blink/public/platform/web_video_frame_submitter.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace gpu {
+class GpuMemoryBufferManager;
+}
+
+namespace viz {
+class RenderPass;
+class SharedBitmapManager;
+}
+
+namespace blink {
+
+// VideoFrameResourceProvider obtains required GPU resources for the video
+// frame.
+// VideoFrameResourceProvider methods are currently called on the media thread.
+// TODO(lethalantidote): Move the usage of this class off media thread
+// https://crbug.com/753605
+class PLATFORM_EXPORT VideoFrameResourceProvider {
+ public:
+ explicit VideoFrameResourceProvider(WebContextProviderCallback,
+ viz::SharedBitmapManager*,
+ gpu::GpuMemoryBufferManager*,
+ const cc::LayerTreeSettings&);
+
+ virtual ~VideoFrameResourceProvider();
+
+ virtual void ObtainContextProvider();
+ virtual void Initialize(viz::ContextProvider*);
+ virtual void AppendQuads(viz::RenderPass*,
+ scoped_refptr<media::VideoFrame>,
+ media::VideoRotation);
+ virtual void ReleaseFrameResources();
+
+ virtual void PrepareSendToParent(
+ const cc::LayerTreeResourceProvider::ResourceIdArray& resource_ids,
+ std::vector<viz::TransferableResource>* transferable_resources);
+ virtual void ReceiveReturnsFromParent(
+ const std::vector<viz::ReturnedResource>& transferable_resources);
+
+ private:
+ WebContextProviderCallback context_provider_callback_;
+ viz::SharedBitmapManager* shared_bitmap_manager_;
+ gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager_;
+ cc::LayerTreeSettings settings_;
+ std::unique_ptr<cc::VideoResourceUpdater> resource_updater_;
+ std::unique_ptr<cc::LayerTreeResourceProvider> resource_provider_;
+ viz::ContextProvider* context_provider_ = nullptr;
+ base::WeakPtrFactory<VideoFrameResourceProvider> weak_ptr_factory_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_VIDEO_FRAME_RESOURCE_PROVIDER_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc b/chromium/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
new file mode 100644
index 00000000000..a587c82470a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
@@ -0,0 +1,210 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/video_frame_submitter.h"
+
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/trace_event/trace_event.h"
+#include "cc/paint/filter_operations.h"
+#include "cc/resources/resource_provider.h"
+#include "cc/resources/video_resource_updater.h"
+#include "cc/scheduler/video_frame_controller.h"
+#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
+#include "media/base/video_frame.h"
+#include "services/viz/public/interfaces/compositing/compositor_frame_sink.mojom-blink.h"
+#include "third_party/blink/public/platform/interface_provider.h"
+#include "third_party/blink/public/platform/modules/offscreencanvas/offscreen_canvas_surface.mojom-blink.h"
+#include "third_party/blink/public/platform/platform.h"
+
+namespace blink {
+
+VideoFrameSubmitter::VideoFrameSubmitter(
+ std::unique_ptr<VideoFrameResourceProvider> resource_provider)
+ : binding_(this),
+ resource_provider_(std::move(resource_provider)),
+ is_rendering_(false),
+ weak_ptr_factory_(this) {
+ current_local_surface_id_ = parent_local_surface_id_allocator_.GenerateId();
+ DETACH_FROM_THREAD(media_thread_checker_);
+}
+
+VideoFrameSubmitter::~VideoFrameSubmitter() = default;
+
+void VideoFrameSubmitter::StopUsingProvider() {
+ DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
+ if (is_rendering_)
+ StopRendering();
+ provider_ = nullptr;
+}
+
+void VideoFrameSubmitter::StopRendering() {
+ DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
+ DCHECK(is_rendering_);
+ DCHECK(provider_);
+
+ // Push out final frame.
+ SubmitSingleFrame();
+
+ is_rendering_ = false;
+ compositor_frame_sink_->SetNeedsBeginFrame(false);
+}
+
+void VideoFrameSubmitter::SubmitSingleFrame() {
+ viz::BeginFrameAck current_begin_frame_ack =
+ viz::BeginFrameAck::CreateManualAckWithDamage();
+ scoped_refptr<media::VideoFrame> video_frame = provider_->GetCurrentFrame();
+ base::SequencedTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(&VideoFrameSubmitter::SubmitFrame,
+ weak_ptr_factory_.GetWeakPtr(),
+ current_begin_frame_ack, video_frame));
+ provider_->PutCurrentFrame();
+}
+
+void VideoFrameSubmitter::DidReceiveFrame() {
+ DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
+ DCHECK(provider_);
+
+ // DidReceiveFrame is called before renderering has started, as a part of
+ // PaintSingleFrame.
+ if (!is_rendering_) {
+ SubmitSingleFrame();
+ }
+}
+
+void VideoFrameSubmitter::StartRendering() {
+ DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
+ DCHECK(!is_rendering_);
+ compositor_frame_sink_->SetNeedsBeginFrame(true);
+ is_rendering_ = true;
+}
+
+void VideoFrameSubmitter::Initialize(cc::VideoFrameProvider* provider) {
+ DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
+ if (provider) {
+ DCHECK(!provider_);
+ provider_ = provider;
+ resource_provider_->ObtainContextProvider();
+ }
+}
+
+void VideoFrameSubmitter::StartSubmitting(const viz::FrameSinkId& id) {
+ DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
+ DCHECK(id.is_valid());
+
+ // TODO(lethalantidote): Class to be renamed.
+ mojom::blink::OffscreenCanvasProviderPtr canvas_provider;
+ Platform::Current()->GetInterfaceProvider()->GetInterface(
+ mojo::MakeRequest(&canvas_provider));
+
+ viz::mojom::blink::CompositorFrameSinkClientPtr client;
+ binding_.Bind(mojo::MakeRequest(&client));
+ canvas_provider->CreateCompositorFrameSink(
+ id, std::move(client), mojo::MakeRequest(&compositor_frame_sink_));
+
+ scoped_refptr<media::VideoFrame> video_frame = provider_->GetCurrentFrame();
+ if (video_frame) {
+ viz::BeginFrameAck current_begin_frame_ack =
+ viz::BeginFrameAck::CreateManualAckWithDamage();
+ base::SequencedTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(&VideoFrameSubmitter::SubmitFrame,
+ weak_ptr_factory_.GetWeakPtr(),
+ current_begin_frame_ack, video_frame));
+ provider_->PutCurrentFrame();
+ }
+}
+
+void VideoFrameSubmitter::SubmitFrame(
+ viz::BeginFrameAck begin_frame_ack,
+ scoped_refptr<media::VideoFrame> video_frame) {
+ TRACE_EVENT0("media", "VideoFrameSubmitter::SubmitFrame");
+ DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
+ DCHECK(compositor_frame_sink_);
+
+ viz::CompositorFrame compositor_frame;
+ std::unique_ptr<viz::RenderPass> render_pass = viz::RenderPass::Create();
+
+ render_pass->SetNew(1, gfx::Rect(video_frame->coded_size()),
+ gfx::Rect(video_frame->coded_size()), gfx::Transform());
+ render_pass->filters = cc::FilterOperations();
+ resource_provider_->AppendQuads(render_pass.get(), video_frame, rotation_);
+ compositor_frame.metadata.begin_frame_ack = begin_frame_ack;
+ compositor_frame.metadata.device_scale_factor = 1;
+ compositor_frame.metadata.may_contain_video = true;
+
+ cc::ResourceProvider::ResourceIdArray resources;
+ for (auto* quad : render_pass->quad_list) {
+ for (viz::ResourceId resource_id : quad->resources) {
+ resources.push_back(resource_id);
+ }
+ }
+ resource_provider_->PrepareSendToParent(resources,
+ &compositor_frame.resource_list);
+ compositor_frame.render_pass_list.push_back(std::move(render_pass));
+
+ if (compositor_frame.size_in_pixels() != current_size_in_pixels_) {
+ current_local_surface_id_ = parent_local_surface_id_allocator_.GenerateId();
+ current_size_in_pixels_ = compositor_frame.size_in_pixels();
+ }
+
+ // TODO(lethalantidote): Address third/fourth arg in SubmitCompositorFrame.
+ compositor_frame_sink_->SubmitCompositorFrame(
+ current_local_surface_id_, std::move(compositor_frame), nullptr, 0);
+ resource_provider_->ReleaseFrameResources();
+}
+
+void VideoFrameSubmitter::OnBeginFrame(const viz::BeginFrameArgs& args) {
+ TRACE_EVENT0("media", "VideoFrameSubmitter::OnBeginFrame");
+ DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
+ viz::BeginFrameAck current_begin_frame_ack =
+ viz::BeginFrameAck(args.source_id, args.sequence_number, false);
+ if (args.type == viz::BeginFrameArgs::MISSED) {
+ compositor_frame_sink_->DidNotProduceFrame(current_begin_frame_ack);
+ return;
+ }
+
+ current_begin_frame_ack.has_damage = true;
+
+ if (!provider_ ||
+ !provider_->UpdateCurrentFrame(args.frame_time + args.interval,
+ args.frame_time + 2 * args.interval) ||
+ !is_rendering_) {
+ compositor_frame_sink_->DidNotProduceFrame(current_begin_frame_ack);
+ return;
+ }
+
+ scoped_refptr<media::VideoFrame> video_frame = provider_->GetCurrentFrame();
+
+ SubmitFrame(current_begin_frame_ack, video_frame);
+
+ provider_->PutCurrentFrame();
+}
+
+void VideoFrameSubmitter::SetRotation(media::VideoRotation rotation) {
+ rotation_ = rotation;
+}
+
+void VideoFrameSubmitter::DidReceiveCompositorFrameAck(
+ const WTF::Vector<viz::ReturnedResource>& resources) {
+ DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
+ ReclaimResources(resources);
+}
+
+void VideoFrameSubmitter::ReclaimResources(
+ const WTF::Vector<viz::ReturnedResource>& resources) {
+ DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
+ WebVector<viz::ReturnedResource> temp_resources = resources;
+ std::vector<viz::ReturnedResource> std_resources =
+ temp_resources.ReleaseVector();
+ resource_provider_->ReceiveReturnsFromParent(std_resources);
+}
+
+void VideoFrameSubmitter::DidPresentCompositorFrame(
+ uint32_t presentation_token,
+ mojo_base::mojom::blink::TimeTicksPtr time,
+ WTF::TimeDelta refresh,
+ uint32_t flags) {}
+
+void VideoFrameSubmitter::DidDiscardCompositorFrame(
+ uint32_t presentation_token) {}
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/video_frame_submitter.h b/chromium/third_party/blink/renderer/platform/graphics/video_frame_submitter.h
new file mode 100644
index 00000000000..4797e36f45f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/video_frame_submitter.h
@@ -0,0 +1,99 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_VIDEO_FRAME_SUBMITTER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_VIDEO_FRAME_SUBMITTER_H_
+
+#include "base/memory/weak_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "services/viz/public/interfaces/compositing/compositor_frame_sink.mojom-blink.h"
+#include "third_party/blink/public/platform/web_video_frame_submitter.h"
+#include "third_party/blink/renderer/platform/graphics/video_frame_resource_provider.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+
+namespace blink {
+
+// This single-threaded class facilitates the communication between the media
+// stack and mojo, providing compositor frames containing video frames to the
+// |compositor_frame_sink_|. This class has dependencies on classes that use
+// the media thread's OpenGL ContextProvider, and thus, besides construction,
+// should be consistently ran from the same media SingleThreadTaskRunner.
+class PLATFORM_EXPORT VideoFrameSubmitter
+ : public WebVideoFrameSubmitter,
+ public viz::mojom::blink::CompositorFrameSinkClient {
+ public:
+ explicit VideoFrameSubmitter(std::unique_ptr<VideoFrameResourceProvider>);
+
+ ~VideoFrameSubmitter() override;
+
+ static void CreateCompositorFrameSink(
+ const viz::FrameSinkId,
+ mojo::Binding<viz::mojom::blink::CompositorFrameSinkClient>*,
+ viz::mojom::blink::CompositorFrameSinkPtr*);
+
+ bool Rendering() { return is_rendering_; };
+ cc::VideoFrameProvider* Provider() { return provider_; }
+ mojo::Binding<viz::mojom::blink::CompositorFrameSinkClient>* Binding() {
+ return &binding_;
+ }
+ void SetSink(viz::mojom::blink::CompositorFrameSinkPtr* sink) {
+ compositor_frame_sink_ = std::move(*sink);
+ }
+
+ // cc::VideoFrameProvider::Client implementation.
+ void StopUsingProvider() override;
+ void StartRendering() override;
+ void StopRendering() override;
+ void DidReceiveFrame() override;
+
+ // WebVideoFrameSubmitter implementation.
+ void Initialize(cc::VideoFrameProvider*) override;
+ void StartSubmitting(const viz::FrameSinkId&) override;
+ void SetRotation(media::VideoRotation) override;
+
+ // cc::mojom::CompositorFrameSinkClient implementation.
+ void DidReceiveCompositorFrameAck(
+ const WTF::Vector<viz::ReturnedResource>& resources) override;
+ void DidPresentCompositorFrame(uint32_t presentation_token,
+ mojo_base::mojom::blink::TimeTicksPtr,
+ WTF::TimeDelta refresh,
+ uint32_t flags) final;
+ void DidDiscardCompositorFrame(uint32_t presentation_token) final;
+ void OnBeginFrame(const viz::BeginFrameArgs&) override;
+ void OnBeginFramePausedChanged(bool paused) override {}
+ void ReclaimResources(
+ const WTF::Vector<viz::ReturnedResource>& resources) override;
+
+ private:
+ void SubmitFrame(viz::BeginFrameAck, scoped_refptr<media::VideoFrame>);
+
+ // Pulls frame and submits it to compositor.
+ // Used in cases like PaintSingleFrame, which occurs before video rendering
+ // has started to post a poster image, or to submit a final frame before
+ // ending rendering.
+ void SubmitSingleFrame();
+
+ cc::VideoFrameProvider* provider_ = nullptr;
+ viz::mojom::blink::CompositorFrameSinkPtr compositor_frame_sink_;
+ mojo::Binding<viz::mojom::blink::CompositorFrameSinkClient> binding_;
+ viz::ParentLocalSurfaceIdAllocator parent_local_surface_id_allocator_;
+ viz::LocalSurfaceId current_local_surface_id_;
+ std::unique_ptr<VideoFrameResourceProvider> resource_provider_;
+
+ bool is_rendering_;
+ media::VideoRotation rotation_;
+ gfx::Size current_size_in_pixels_;
+ base::WeakPtrFactory<VideoFrameSubmitter> weak_ptr_factory_;
+
+ THREAD_CHECKER(media_thread_checker_);
+
+ DISALLOW_COPY_AND_ASSIGN(VideoFrameSubmitter);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_VIDEO_FRAME_SUBMITTER_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/video_frame_submitter_test.cc b/chromium/third_party/blink/renderer/platform/graphics/video_frame_submitter_test.cc
new file mode 100644
index 00000000000..53c51912273
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/video_frame_submitter_test.cc
@@ -0,0 +1,412 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/video_frame_submitter.h"
+
+#include <memory>
+#include "base/memory/ptr_util.h"
+#include "base/task_runner_util.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "base/threading/thread.h"
+#include "cc/layers/video_frame_provider.h"
+#include "cc/test/layer_test_common.h"
+#include "cc/trees/layer_tree_settings.h"
+#include "cc/trees/task_runner_provider.h"
+#include "components/viz/test/fake_external_begin_frame_source.h"
+#include "components/viz/test/test_context_provider.h"
+#include "media/base/video_frame.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "services/viz/public/interfaces/compositing/compositor_frame_sink.mojom-blink.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/graphics/video_frame_resource_provider.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+
+using testing::_;
+using testing::Return;
+using testing::StrictMock;
+
+namespace blink {
+
+namespace {
+
+class MockVideoFrameProvider : public cc::VideoFrameProvider {
+ public:
+ MockVideoFrameProvider() = default;
+ ~MockVideoFrameProvider() = default;
+
+ MOCK_METHOD1(SetVideoFrameProviderClient, void(Client*));
+ MOCK_METHOD2(UpdateCurrentFrame, bool(base::TimeTicks, base::TimeTicks));
+ MOCK_METHOD0(HasCurrentFrame, bool());
+ MOCK_METHOD0(GetCurrentFrame, scoped_refptr<media::VideoFrame>());
+ MOCK_METHOD0(PutCurrentFrame, void());
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockVideoFrameProvider);
+};
+
+class MockCompositorFrameSink : public viz::mojom::blink::CompositorFrameSink {
+ public:
+ MockCompositorFrameSink(
+ viz::mojom::blink::CompositorFrameSinkRequest* request)
+ : binding_(this, std::move(*request)) {}
+ ~MockCompositorFrameSink() = default;
+
+ MOCK_METHOD1(SetNeedsBeginFrame, void(bool));
+ MOCK_METHOD0(SetWantsAnimateOnlyBeginFrames, void());
+
+ MOCK_METHOD2(DoSubmitCompositorFrame,
+ void(const viz::LocalSurfaceId&, viz::CompositorFrame*));
+ void SubmitCompositorFrame(
+ const viz::LocalSurfaceId& id,
+ viz::CompositorFrame frame,
+ viz::mojom::blink::HitTestRegionListPtr hit_test_region_list,
+ uint64_t submit_time) override {
+ DoSubmitCompositorFrame(id, &frame);
+ }
+
+ MOCK_METHOD1(DidNotProduceFrame, void(const viz::BeginFrameAck&));
+
+ MOCK_METHOD2(DidAllocateSharedBitmap_,
+ void(mojo::ScopedSharedBufferHandle* buffer,
+ gpu::mojom::blink::MailboxPtr* id));
+ void DidAllocateSharedBitmap(mojo::ScopedSharedBufferHandle buffer,
+ gpu::mojom::blink::MailboxPtr id) override {
+ DidAllocateSharedBitmap_(&buffer, &id);
+ }
+
+ MOCK_METHOD1(DidDeleteSharedBitmap_, void(gpu::mojom::blink::MailboxPtr* id));
+ void DidDeleteSharedBitmap(gpu::mojom::blink::MailboxPtr id) override {
+ DidDeleteSharedBitmap_(&id);
+ }
+
+ private:
+ mojo::Binding<viz::mojom::blink::CompositorFrameSink> binding_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockCompositorFrameSink);
+};
+
+class MockVideoFrameResourceProvider
+ : public blink::VideoFrameResourceProvider {
+ public:
+ MockVideoFrameResourceProvider(viz::ContextProvider* context_provider)
+ : blink::VideoFrameResourceProvider(
+ base::BindRepeating(
+ [](base::OnceCallback<void(viz::ContextProvider*)>) {}),
+ nullptr,
+ nullptr,
+ cc::LayerTreeSettings()) {
+ blink::VideoFrameResourceProvider::Initialize(context_provider);
+ }
+ ~MockVideoFrameResourceProvider() = default;
+
+ MOCK_METHOD1(Initialize, void(viz::ContextProvider*));
+ MOCK_METHOD3(AppendQuads,
+ void(viz::RenderPass*,
+ scoped_refptr<media::VideoFrame>,
+ media::VideoRotation));
+ MOCK_METHOD0(ReleaseFrameResources, void());
+ MOCK_METHOD2(PrepareSendToParent,
+ void(const cc::LayerTreeResourceProvider::ResourceIdArray&,
+ std::vector<viz::TransferableResource>*));
+ MOCK_METHOD1(
+ ReceiveReturnsFromParent,
+ void(const std::vector<viz::ReturnedResource>& transferable_resources));
+ MOCK_METHOD0(ObtainContextProvider, void());
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockVideoFrameResourceProvider);
+};
+} // namespace
+
+class VideoFrameSubmitterTest : public testing::Test {
+ public:
+ VideoFrameSubmitterTest()
+ : now_src_(new base::SimpleTestTickClock()),
+ begin_frame_source_(new viz::FakeExternalBeginFrameSource(0.f, false)),
+ provider_(new StrictMock<MockVideoFrameProvider>()),
+ context_provider_(viz::TestContextProvider::Create()) {
+ context_provider_->BindToCurrentThread();
+ }
+
+ void SetUp() override {
+ MakeSubmitter();
+ scoped_task_environment_.RunUntilIdle();
+ }
+
+ void MakeSubmitter() {
+ resource_provider_ =
+ new StrictMock<MockVideoFrameResourceProvider>(context_provider_.get());
+ submitter_ = std::make_unique<VideoFrameSubmitter>(
+ base::WrapUnique<MockVideoFrameResourceProvider>(resource_provider_));
+
+ EXPECT_CALL(*resource_provider_, ObtainContextProvider());
+ submitter_->Initialize(provider_.get());
+ viz::mojom::blink::CompositorFrameSinkPtr submitter_sink;
+ viz::mojom::blink::CompositorFrameSinkRequest request =
+ mojo::MakeRequest(&submitter_sink);
+ sink_ = std::make_unique<StrictMock<MockCompositorFrameSink>>(&request);
+ submitter_->SetSink(&submitter_sink);
+ }
+
+ protected:
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
+ std::unique_ptr<base::SimpleTestTickClock> now_src_;
+ std::unique_ptr<viz::FakeExternalBeginFrameSource> begin_frame_source_;
+ std::unique_ptr<StrictMock<MockCompositorFrameSink>> sink_;
+ std::unique_ptr<StrictMock<MockVideoFrameProvider>> provider_;
+ StrictMock<MockVideoFrameResourceProvider>* resource_provider_;
+ scoped_refptr<viz::TestContextProvider> context_provider_;
+ std::unique_ptr<VideoFrameSubmitter> submitter_;
+};
+
+TEST_F(VideoFrameSubmitterTest, StatRenderingFlipsBits) {
+ MakeSubmitter();
+ scoped_task_environment_.RunUntilIdle();
+
+ EXPECT_FALSE(submitter_->Rendering());
+ EXPECT_CALL(*sink_, SetNeedsBeginFrame(true));
+
+ submitter_->StartRendering();
+
+ scoped_task_environment_.RunUntilIdle();
+
+ EXPECT_TRUE(submitter_->Rendering());
+}
+
+TEST_F(VideoFrameSubmitterTest, StopUsingProviderNullsProvider) {
+ MakeSubmitter();
+ scoped_task_environment_.RunUntilIdle();
+
+ EXPECT_FALSE(submitter_->Rendering());
+ EXPECT_EQ(provider_.get(), submitter_->Provider());
+
+ submitter_->StopUsingProvider();
+
+ EXPECT_EQ(nullptr, submitter_->Provider());
+}
+
+TEST_F(VideoFrameSubmitterTest,
+ StopUsingProviderSubmitsFrameAndStopsRendering) {
+ MakeSubmitter();
+ scoped_task_environment_.RunUntilIdle();
+
+ EXPECT_CALL(*sink_, SetNeedsBeginFrame(true));
+
+ submitter_->StartRendering();
+ scoped_task_environment_.RunUntilIdle();
+
+ EXPECT_TRUE(submitter_->Rendering());
+
+ EXPECT_CALL(*provider_, GetCurrentFrame())
+ .WillOnce(Return(media::VideoFrame::CreateFrame(
+ media::PIXEL_FORMAT_YV12, gfx::Size(8, 8), gfx::Rect(gfx::Size(8, 8)),
+ gfx::Size(8, 8), base::TimeDelta())));
+ EXPECT_CALL(*sink_, DoSubmitCompositorFrame(_, _));
+ EXPECT_CALL(*provider_, PutCurrentFrame());
+ EXPECT_CALL(*sink_, SetNeedsBeginFrame(false));
+ EXPECT_CALL(*resource_provider_, AppendQuads(_, _, _));
+ EXPECT_CALL(*resource_provider_, PrepareSendToParent(_, _));
+ EXPECT_CALL(*resource_provider_, ReleaseFrameResources());
+
+ submitter_->StopUsingProvider();
+
+ scoped_task_environment_.RunUntilIdle();
+
+ EXPECT_FALSE(submitter_->Rendering());
+}
+
+TEST_F(VideoFrameSubmitterTest, DidReceiveFrameDoesNothingIfRendering) {
+ MakeSubmitter();
+ scoped_task_environment_.RunUntilIdle();
+
+ EXPECT_CALL(*sink_, SetNeedsBeginFrame(true));
+
+ submitter_->StartRendering();
+ scoped_task_environment_.RunUntilIdle();
+
+ EXPECT_TRUE(submitter_->Rendering());
+
+ submitter_->DidReceiveFrame();
+ scoped_task_environment_.RunUntilIdle();
+}
+
+TEST_F(VideoFrameSubmitterTest, DidReceiveFrameSubmitsFrame) {
+ MakeSubmitter();
+ scoped_task_environment_.RunUntilIdle();
+
+ EXPECT_FALSE(submitter_->Rendering());
+
+ EXPECT_CALL(*provider_, GetCurrentFrame())
+ .WillOnce(Return(media::VideoFrame::CreateFrame(
+ media::PIXEL_FORMAT_YV12, gfx::Size(8, 8), gfx::Rect(gfx::Size(8, 8)),
+ gfx::Size(8, 8), base::TimeDelta())));
+ EXPECT_CALL(*sink_, DoSubmitCompositorFrame(_, _));
+ EXPECT_CALL(*provider_, PutCurrentFrame());
+ EXPECT_CALL(*resource_provider_, AppendQuads(_, _, _));
+ EXPECT_CALL(*resource_provider_, PrepareSendToParent(_, _));
+ EXPECT_CALL(*resource_provider_, ReleaseFrameResources());
+
+ submitter_->DidReceiveFrame();
+ scoped_task_environment_.RunUntilIdle();
+}
+
+TEST_F(VideoFrameSubmitterTest, RotationInformationPassedToResourceProvider) {
+ // Check to see if rotation is communicated pre-rendering.
+ MakeSubmitter();
+ scoped_task_environment_.RunUntilIdle();
+
+ EXPECT_FALSE(submitter_->Rendering());
+
+ submitter_->SetRotation(media::VideoRotation::VIDEO_ROTATION_90);
+
+ EXPECT_CALL(*provider_, GetCurrentFrame())
+ .WillOnce(Return(media::VideoFrame::CreateFrame(
+ media::PIXEL_FORMAT_YV12, gfx::Size(8, 8), gfx::Rect(gfx::Size(8, 8)),
+ gfx::Size(8, 8), base::TimeDelta())));
+ EXPECT_CALL(*sink_, DoSubmitCompositorFrame(_, _));
+ EXPECT_CALL(*provider_, PutCurrentFrame());
+ EXPECT_CALL(*resource_provider_,
+ AppendQuads(_, _, media::VideoRotation::VIDEO_ROTATION_90));
+ EXPECT_CALL(*resource_provider_, PrepareSendToParent(_, _));
+ EXPECT_CALL(*resource_provider_, ReleaseFrameResources());
+
+ submitter_->DidReceiveFrame();
+ scoped_task_environment_.RunUntilIdle();
+
+ // Check to see if an update to rotation just before rendering is
+ // communicated.
+ submitter_->SetRotation(media::VideoRotation::VIDEO_ROTATION_180);
+
+ EXPECT_CALL(*sink_, SetNeedsBeginFrame(true));
+ submitter_->StartRendering();
+ scoped_task_environment_.RunUntilIdle();
+
+ EXPECT_CALL(*provider_, UpdateCurrentFrame(_, _)).WillOnce(Return(true));
+ EXPECT_CALL(*provider_, GetCurrentFrame())
+ .WillOnce(Return(media::VideoFrame::CreateFrame(
+ media::PIXEL_FORMAT_YV12, gfx::Size(8, 8), gfx::Rect(gfx::Size(8, 8)),
+ gfx::Size(8, 8), base::TimeDelta())));
+ EXPECT_CALL(*sink_, DoSubmitCompositorFrame(_, _));
+ EXPECT_CALL(*provider_, PutCurrentFrame());
+ EXPECT_CALL(*resource_provider_,
+ AppendQuads(_, _, media::VideoRotation::VIDEO_ROTATION_180));
+ EXPECT_CALL(*resource_provider_, PrepareSendToParent(_, _));
+ EXPECT_CALL(*resource_provider_, ReleaseFrameResources());
+
+ viz::BeginFrameArgs args = begin_frame_source_->CreateBeginFrameArgs(
+ BEGINFRAME_FROM_HERE, now_src_.get());
+ submitter_->OnBeginFrame(args);
+ scoped_task_environment_.RunUntilIdle();
+
+ // Check to see if changing rotation while rendering is handled.
+ submitter_->SetRotation(media::VideoRotation::VIDEO_ROTATION_270);
+
+ EXPECT_CALL(*provider_, UpdateCurrentFrame(_, _)).WillOnce(Return(true));
+ EXPECT_CALL(*provider_, GetCurrentFrame())
+ .WillOnce(Return(media::VideoFrame::CreateFrame(
+ media::PIXEL_FORMAT_YV12, gfx::Size(8, 8), gfx::Rect(gfx::Size(8, 8)),
+ gfx::Size(8, 8), base::TimeDelta())));
+ EXPECT_CALL(*sink_, DoSubmitCompositorFrame(_, _));
+ EXPECT_CALL(*provider_, PutCurrentFrame());
+ EXPECT_CALL(*resource_provider_,
+ AppendQuads(_, _, media::VideoRotation::VIDEO_ROTATION_270));
+ EXPECT_CALL(*resource_provider_, PrepareSendToParent(_, _));
+ EXPECT_CALL(*resource_provider_, ReleaseFrameResources());
+
+ submitter_->OnBeginFrame(args);
+ scoped_task_environment_.RunUntilIdle();
+}
+
+TEST_F(VideoFrameSubmitterTest, OnBeginFrameSubmitsFrame) {
+ MakeSubmitter();
+ scoped_task_environment_.RunUntilIdle();
+
+ EXPECT_CALL(*sink_, SetNeedsBeginFrame(true));
+
+ submitter_->StartRendering();
+ scoped_task_environment_.RunUntilIdle();
+
+ EXPECT_CALL(*provider_, UpdateCurrentFrame(_, _)).WillOnce(Return(true));
+ EXPECT_CALL(*provider_, GetCurrentFrame())
+ .WillOnce(Return(media::VideoFrame::CreateFrame(
+ media::PIXEL_FORMAT_YV12, gfx::Size(8, 8), gfx::Rect(gfx::Size(8, 8)),
+ gfx::Size(8, 8), base::TimeDelta())));
+ EXPECT_CALL(*sink_, DoSubmitCompositorFrame(_, _));
+ EXPECT_CALL(*provider_, PutCurrentFrame());
+ EXPECT_CALL(*resource_provider_, AppendQuads(_, _, _));
+ EXPECT_CALL(*resource_provider_, PrepareSendToParent(_, _));
+ EXPECT_CALL(*resource_provider_, ReleaseFrameResources());
+
+ viz::BeginFrameArgs args = begin_frame_source_->CreateBeginFrameArgs(
+ BEGINFRAME_FROM_HERE, now_src_.get());
+ submitter_->OnBeginFrame(args);
+ scoped_task_environment_.RunUntilIdle();
+}
+
+TEST_F(VideoFrameSubmitterTest, MissedFrameArgDoesNotProduceFrame) {
+ MakeSubmitter();
+ scoped_task_environment_.RunUntilIdle();
+
+ EXPECT_CALL(*sink_, DidNotProduceFrame(_));
+
+ viz::BeginFrameArgs args = begin_frame_source_->CreateBeginFrameArgs(
+ BEGINFRAME_FROM_HERE, now_src_.get());
+ args.type = viz::BeginFrameArgs::MISSED;
+ submitter_->OnBeginFrame(args);
+ scoped_task_environment_.RunUntilIdle();
+}
+
+TEST_F(VideoFrameSubmitterTest, MissingProviderDoesNotProduceFrame) {
+ MakeSubmitter();
+ scoped_task_environment_.RunUntilIdle();
+
+ submitter_->StopUsingProvider();
+
+ EXPECT_CALL(*sink_, DidNotProduceFrame(_));
+
+ viz::BeginFrameArgs args = begin_frame_source_->CreateBeginFrameArgs(
+ BEGINFRAME_FROM_HERE, now_src_.get());
+ submitter_->OnBeginFrame(args);
+ scoped_task_environment_.RunUntilIdle();
+}
+
+TEST_F(VideoFrameSubmitterTest, NoUpdateOnFrameDoesNotProduceFrame) {
+ MakeSubmitter();
+ scoped_task_environment_.RunUntilIdle();
+
+ EXPECT_CALL(*provider_, UpdateCurrentFrame(_, _)).WillOnce(Return(false));
+ EXPECT_CALL(*sink_, DidNotProduceFrame(_));
+
+ viz::BeginFrameArgs args = begin_frame_source_->CreateBeginFrameArgs(
+ BEGINFRAME_FROM_HERE, now_src_.get());
+ submitter_->OnBeginFrame(args);
+ scoped_task_environment_.RunUntilIdle();
+}
+
+TEST_F(VideoFrameSubmitterTest, NotRenderingDoesNotProduceFrame) {
+ MakeSubmitter();
+ scoped_task_environment_.RunUntilIdle();
+
+ EXPECT_CALL(*provider_, UpdateCurrentFrame(_, _)).WillOnce(Return(true));
+ EXPECT_CALL(*sink_, DidNotProduceFrame(_));
+
+ viz::BeginFrameArgs args = begin_frame_source_->CreateBeginFrameArgs(
+ BEGINFRAME_FROM_HERE, now_src_.get());
+ submitter_->OnBeginFrame(args);
+ scoped_task_environment_.RunUntilIdle();
+}
+
+TEST_F(VideoFrameSubmitterTest, ReturnsResourceOnCompositorAck) {
+ MakeSubmitter();
+ scoped_task_environment_.RunUntilIdle();
+
+ WTF::Vector<viz::ReturnedResource> resources;
+ EXPECT_CALL(*resource_provider_, ReceiveReturnsFromParent(_));
+ submitter_->DidReceiveCompositorFrameAck(resources);
+ scoped_task_environment_.RunUntilIdle();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/web_graphics_context_3d_provider_wrapper.cc b/chromium/third_party/blink/renderer/platform/graphics/web_graphics_context_3d_provider_wrapper.cc
new file mode 100644
index 00000000000..75023857923
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/web_graphics_context_3d_provider_wrapper.cc
@@ -0,0 +1,24 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/graphics/web_graphics_context_3d_provider_wrapper.h"
+
+namespace blink {
+
+WebGraphicsContext3DProviderWrapper::~WebGraphicsContext3DProviderWrapper() {
+ for (auto& observer : observers_)
+ observer.OnContextDestroyed();
+}
+
+void WebGraphicsContext3DProviderWrapper::AddObserver(
+ DestructionObserver* obs) {
+ observers_.AddObserver(obs);
+}
+
+void WebGraphicsContext3DProviderWrapper::RemoveObserver(
+ DestructionObserver* obs) {
+ observers_.RemoveObserver(obs);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/web_graphics_context_3d_provider_wrapper.h b/chromium/third_party/blink/renderer/platform/graphics/web_graphics_context_3d_provider_wrapper.h
new file mode 100644
index 00000000000..e0f35200c51
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/web_graphics_context_3d_provider_wrapper.h
@@ -0,0 +1,57 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_WEB_GRAPHICS_CONTEXT_3D_PROVIDER_WRAPPER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_WEB_GRAPHICS_CONTEXT_3D_PROVIDER_WRAPPER_H_
+
+#include <memory>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "third_party/blink/public/platform/web_graphics_context_3d_provider.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/graphics_context_3d_utils.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT WebGraphicsContext3DProviderWrapper {
+ public:
+ class DestructionObserver {
+ public:
+ virtual ~DestructionObserver() {}
+ virtual void OnContextDestroyed() = 0;
+ };
+
+ WebGraphicsContext3DProviderWrapper(
+ std::unique_ptr<WebGraphicsContext3DProvider> provider)
+ : context_provider_(std::move(provider)), weak_ptr_factory_(this) {
+ DCHECK(context_provider_);
+ utils_ = base::WrapUnique(new GraphicsContext3DUtils(GetWeakPtr()));
+ }
+ ~WebGraphicsContext3DProviderWrapper();
+
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper> GetWeakPtr() {
+ return weak_ptr_factory_.GetWeakPtr();
+ }
+ WebGraphicsContext3DProvider* ContextProvider() {
+ return context_provider_.get();
+ }
+
+ GraphicsContext3DUtils* Utils() { return utils_.get(); }
+
+ void AddObserver(DestructionObserver*);
+ void RemoveObserver(DestructionObserver*);
+
+ private:
+ std::unique_ptr<GraphicsContext3DUtils> utils_;
+ std::unique_ptr<WebGraphicsContext3DProvider> context_provider_;
+ base::ObserverList<DestructionObserver> observers_;
+ base::WeakPtrFactory<WebGraphicsContext3DProviderWrapper> weak_ptr_factory_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_WEB_GRAPHICS_CONTEXT_3D_PROVIDER_WRAPPER_H_
diff --git a/chromium/third_party/blink/renderer/platform/heap/BUILD.gn b/chromium/third_party/blink/renderer/platform/heap/BUILD.gn
new file mode 100644
index 00000000000..254b8b9d715
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/BUILD.gn
@@ -0,0 +1,137 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/buildflag_header.gni")
+import("//build/config/jumbo.gni")
+import("//third_party/blink/renderer/platform/platform.gni")
+import("//testing/test.gni")
+
+declare_args() {
+ # Enables incremental marking in Oilpan.
+ #
+ # Note: Incremental marking is currently considered experimental and also
+ # enables 'enable_blink_heap_incremental_marking'. See default value below.
+ enable_blink_heap_incremental_marking = false
+}
+
+declare_args() {
+ # Enables heap verification.
+ enable_blink_heap_verification = enable_blink_heap_incremental_marking
+}
+
+buildflag_header("blink_heap_buildflags") {
+ header = "heap_buildflags.h"
+ header_dir = "third_party/blink/renderer/platform/heap"
+
+ flags = [
+ "BLINK_HEAP_INCREMENTAL_MARKING=$enable_blink_heap_incremental_marking",
+ "BLINK_HEAP_VERIFICATION=$enable_blink_heap_verification",
+ ]
+}
+
+blink_platform_sources("heap") {
+ sources = [
+ "blink_gc.h",
+ "blink_gc_memory_dump_provider.cc",
+ "blink_gc_memory_dump_provider.h",
+ "garbage_collected.h",
+ "gc_info.cc",
+ "gc_info.h",
+ "gc_task_runner.h",
+ "handle.h",
+ "heap.cc",
+ "heap.h",
+ "heap_allocator.cc",
+ "heap_allocator.h",
+ "heap_compact.cc",
+ "heap_compact.h",
+ "heap_linked_stack.h",
+ "heap_page.cc",
+ "heap_page.h",
+ "heap_terminated_array.h",
+ "heap_terminated_array_builder.h",
+ "heap_traits.h",
+ "marking_verifier.h",
+ "marking_visitor.cc",
+ "marking_visitor.h",
+ "member.h",
+ "page_memory.cc",
+ "page_memory.h",
+ "page_pool.cc",
+ "page_pool.h",
+ "persistent.h",
+ "persistent_node.cc",
+ "persistent_node.h",
+ "process_heap.cc",
+ "process_heap.h",
+ "safe_point.h",
+ "self_keep_alive.h",
+ "sparse_heap_bitmap.cc",
+ "sparse_heap_bitmap.h",
+ "stack_frame_depth.cc",
+ "stack_frame_depth.h",
+ "thread_state.cc",
+ "thread_state.h",
+ "threading_traits.h",
+ "trace_traits.h",
+ "visitor.h",
+ "worklist.h",
+ ]
+
+ deps = [
+ ":blink_heap_buildflags",
+ "//base",
+ "//third_party/blink/renderer/platform:make_platform_generated",
+ "//third_party/blink/renderer/platform/heap/asm",
+ "//third_party/icu",
+ "//v8",
+ ]
+}
+
+test("blink_heap_unittests") {
+ deps = [
+ ":blink_heap_unittests_sources",
+ ]
+ if (is_android) {
+ deps += [
+ "//base:base_java",
+ "//content/shell/android:content_shell_assets",
+ "//net/android:net_java",
+ "//ui/android:ui_full_java",
+ ]
+ }
+}
+
+jumbo_source_set("blink_heap_unittests_sources") {
+ testonly = true
+ sources = [
+ "blink_gc_memory_dump_provider_test.cc",
+ "heap_compact_test.cc",
+ "heap_test.cc",
+ "heap_test_utilities.cc",
+ "heap_test_utilities.h",
+ "heap_traits_test.cc",
+ "incremental_marking_test.cc",
+ "object_start_bitmap_test.cc",
+ "persistent_test.cc",
+ "run_all_tests.cc",
+ "worklist_test.cc",
+ ]
+
+ configs += [
+ "//third_party/blink/renderer/platform/wtf:wtf_config",
+ "//third_party/blink/renderer:config",
+ "//third_party/blink/renderer:inside_blink",
+ ]
+
+ deps = [
+ "//base",
+ "//base/test:test_support",
+ "//content/test:test_support",
+ "//testing/gmock",
+ "//testing/gtest",
+ "//third_party/blink/renderer/platform:test_support",
+ "//third_party/blink/renderer/platform/wtf",
+ ]
+}
diff --git a/chromium/third_party/blink/renderer/platform/heap/BlinkGCAPIReference.md b/chromium/third_party/blink/renderer/platform/heap/BlinkGCAPIReference.md
new file mode 100644
index 00000000000..ea23a2d9ebd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/BlinkGCAPIReference.md
@@ -0,0 +1,577 @@
+# Blink GC API reference
+
+This is a through document for Oilpan API usage.
+If you want to learn the API usage quickly, look at
+[this tutorial](https://docs.google.com/presentation/d/1XPu03ymz8W295mCftEC9KshH9Icxfq81YwIJQzQrvxo/edit#slide=id.p).
+If you're just interested in wrapper tracing,
+see [Wrapper Tracing Reference](../bindings/TraceWrapperReference.md).
+
+[TOC]
+
+## Header file
+
+Unless otherwise noted, any of the primitives explained in this page requires the following `#include` statement:
+
+```c++
+#include "platform/heap/Handle.h"
+```
+
+## Base class templates
+
+### GarbageCollected
+
+A class that wants the lifetime management of its instances to be managed by Blink GC (Oilpan), it must inherit from
+`GarbageCollected<YourClass>`.
+
+```c++
+class YourClass : public GarbageCollected<YourClass> {
+ // ...
+};
+```
+
+Instances of such classes are said to be *on Oilpan heap*, or *on heap* for short, while instances of other classes
+are called *off heap*. In the rest of this document, the terms "on heap" or "on-heap objects" are used to mean the
+objects on Oilpan heap instead of on normal (default) dynamic allocator's heap space.
+
+You can create an instance of your class normally through `new`, while you may not free the object with `delete`,
+as the Blink GC system is responsible for deallocating the object once it determines the object is unreachable.
+
+You may not allocate an on-heap object on stack.
+
+Your class may need to have a tracing method. See [Tracing](#Tracing) for details.
+
+If your class needs finalization (i.e. some work needs to be done on destruction), use
+[GarbageCollectedFinalized](#GarbageCollectedFinalized) instead.
+
+`GarbageCollected<T>` or any class deriving from `GarbageCollected<T>`, directly or indirectly, must be the first
+element in its base class list (called "leftmost derivation rule"). This rule is needed to assure each on-heap object
+has its own canonical address.
+
+```c++
+class A : public GarbageCollected<A>, public P { // OK, GarbageCollected<A> is leftmost.
+};
+
+class B : public A, public Q { // OK, A is leftmost.
+};
+
+// class C : public R, public A { // BAD, A must be the first base class.
+// };
+```
+
+If a non-leftmost base class needs to retain an on-heap object, that base class needs to inherit from
+[GarbageCollectedMixin](#GarbageCollectedMixin). It's generally recommended to make *any* non-leftmost base class
+inherit from `GarbageCollectedMixin`, because it's dangerous to save a pointer to a non-leftmost
+non-`GarbageCollectedMixin` subclass of an on-heap object.
+
+```c++
+void someFunction(P*);
+
+class A : public GarbageCollected<A>, public P {
+public:
+ void someMemberFunction()
+ {
+ someFunction(this); // DANGEROUS, a raw pointer to an on-heap object.
+ }
+};
+```
+
+### GarbageCollectedFinalized
+
+If you want to make your class garbage-collected and the class needs finalization, your class needs to inherit from
+`GarbageCollectedFinalized<YourClass>` instead of `GarbageCollected<YourClass>`.
+
+A class is said to *need finalization* when it meets either of the following criteria:
+
+* It has non-empty destructor; or
+* It has a member that needs finalization.
+
+```c++
+class YourClass : public GarbageCollectedFinalized<YourClass> {
+public:
+ ~YourClass() { ... } // Non-empty destructor means finalization is needed.
+
+private:
+ scoped_refptr<Something> m_something; // scoped_refptr<> has non-empty destructor, so finalization is needed.
+};
+```
+
+Note that finalization is done at an arbitrary time after the object becomes unreachable.
+
+Any destructor executed within the finalization period must not touch any other on-heap object, because destructors
+can be executed in any order. If there is a need of having such destructor, consider using
+[EAGERLY_FINALIZE](#EAGERLY_FINALIZE).
+
+Because `GarbageCollectedFinalized<T>` is a special case of `GarbageCollected<T>`, all the restrictions that apply
+to `GarbageCollected<T>` classes also apply to `GarbageCollectedFinalized<T>`.
+
+### GarbageCollectedMixin
+
+A non-leftmost base class of a garbage-collected class may derive from `GarbageCollectedMixin`. If a direct child
+class of `GarbageCollected<T>` or `GarbageCollectedFinalized<T>` has a non-leftmost base class deriving from
+`GarbageCollectedMixin`, the garbage-collected class must declare the `USING_GARBAGE_COLLECTED_MIXIN(ClassName)` macro
+in its class declaration.
+
+A class deriving from `GarbageCollectedMixin` can be treated similarly as garbage-collected classes. Specifically, it
+can have `Member<T>`s and `WeakMember<T>`s, and a tracing method. A pointer to such a class must be retained in the
+same smart pointer wrappers as a pointer to a garbage-collected class, such as `Member<T>` or `Persistent<T>`.
+The tracing method of a garbage-collected class, if any, must contain a delegating call for each mixin base class.
+
+```c++
+class P : public GarbageCollectedMixin {
+ public:
+ // OK, needs to trace q_.
+ virtual void Trace(Visitor* visitor) { visitor->Trace(q_); }
+ private:
+ // OK, allowed to have Member<T>.
+ Member<Q> q_;
+};
+
+class A : public GarbageCollected<A>, public P {
+ USING_GARBAGE_COLLECTED_MIXIN(A);
+ public:
+ // Delegating call for P is needed.
+ virtual void Trace(Visitor* visitor) { ...; P::Trace(visitor); }
+ ...
+};
+```
+
+Internally, `GarbageCollectedMixin` defines pure virtual functions, and `USING_GARBAGE_COLLECTED_MIXIN(ClassName)`
+implements these virtual functions. Therefore, you cannot instantiate a class that is a descendant of
+`GarbageCollectedMixin` but not a descendant of `GarbageCollected<T>`. Two or more base classes inheritng from
+`GarbageCollectedMixin` can be resolved with a single `USING_GARBAGE_COLLECTED_MIXIN(ClassName)` declaration.
+
+```c++
+class P : public GarbageCollectedMixin { };
+class Q : public GarbageCollectedMixin { };
+class R : public Q { };
+
+class A : public GarbageCollected<A>, public P, public R {
+ USING_GARBAGE_COLLECTED_MIXIN(A); // OK, resolving pure virtual functions of P and R.
+};
+
+class B : public GarbageCollected<B>, public P {
+ USING_GARBAGE_COLLECTED_MIXIN(B); // OK, different garbage-collected classes may inherit from the same mixin (P).
+};
+
+void someFunction()
+{
+ new A; // OK, A can be instantiated.
+ // new R; // BAD, R has pure virtual functions.
+}
+```
+
+## Class properties
+
+### USING_GARBAGE_COLLECTED_MIXIN
+
+`USING_GARBAGE_COLLECTED_MIXIN(ClassName)` is a macro that must be declared in a garbage-collected class, if any of
+its base classes is a descendant of `GarbageCollectedMixin`.
+
+See [GarbageCollectedMixin](#GarbageCollectedMixin) for the use of `GarbageCollectedMixin` and this macro.
+
+### USING_PRE_FINALIZER
+
+`USING_PRE_FINALIZER(ClassName, functionName)` in a class declaration declares the class has a *pre-finalizer* of name
+`functionName`.
+
+A pre-finalizer is a user-defined member function of a garbage-collected class that is called when the object is going
+to be swept but before the garbage collector actually sweeps any objects. Therefore, it is allowed for a pre-finalizer
+to touch any other on-heap objects, while a destructor is not. It is useful for doing some cleanups that cannot be done
+with a destructor.
+
+A pre-finalizer must have the following function signature: `void preFinalizer()`. You can change the function name.
+
+```c++
+class YourClass : public GarbageCollectedFinalized<YourClass> {
+ USING_PRE_FINALIZER(YourClass, dispose);
+public:
+ void dispose()
+ {
+ m_other->dispose(); // OK; you can touch other on-heap objects in a pre-finalizer.
+ }
+ ~YourClass()
+ {
+ // m_other->dispose(); // BAD.
+ }
+
+private:
+ Member<OtherClass> m_other;
+};
+```
+
+Pre-finalizers have some implications on the garbage collector's performance: the garbage-collector needs to iterate
+all registered pre-finalizers at every GC. Therefore, a pre-finalizer should be avoided unless it is really necessary.
+Especially, avoid defining a pre-finalizer in a class that can be allocated a lot.
+
+### EAGERLY_FINALIZE
+
+A class-level annotation to indicate that class instances that the GC have determined as unreachable, should be eagerly
+swept and finalized by the garbage collector, before the Blink thread (the mutator) resumes after a garbage
+collection. The C++ destructor runs as part of this step. The default sweeping behavior is incremental, sweeping
+pages as demanded by later heap allocations.
+
+Like for the pre-finalizer mechanism, an `EAGERLY_FINALIZE()`d class is allowed to touch other heap objects, which
+is sometimes required, but the main use case for eager finalization is to promptly let go of off-heap resources
+and associations, by unregistering and destructing those eagerly. If not done, these external references would
+otherwise attempt to unsafely access an effectively-dead object (pending lazy sweeping of its heap page.)
+
+`EAGERLY_FINALIZE()` solves the same problem as pre-finalizers, but it arguably fits more naturally with the host
+language's mechanism for finalization (C++ destructors.) One `EAGERLY_FINALIZE()` caveat is that the destructor
+is not allowed to touch another eagerly finalized object (their finalization ordering isn't deterministic) nor
+any pre-finalized objects. Choose the one you think best fits your need for prompt finalization.
+
+### STACK_ALLOCATED
+
+Class level annotation that should be used if the object is only stack allocated; it disallows use
+of `operator new`. Any garbage-collected objects should be kept as `Member<T>` references, but you do not
+need to define a `Trace()` method as they are on the stack, and automatically traced and kept alive should
+a conservative GC be required.
+
+Classes with this annotation do not need a `Trace()` method, and should not inherit a garbage collected class.
+
+### DISALLOW_NEW()
+
+Class-level annotation declaring the class a part object that cannot be separately allocated using `operator new`.
+If the class has `Member<T>` references, you need a `Trace()` method which the object containing the `DISALLOW_NEW()`
+part object must call upon. The clang Blink GC plugin checks and enforces this.
+
+Classes with this annotation need a `Trace()` method, but should not inherit a garbage collected class.
+
+### DISALLOW_NEW_EXCEPT_PLACEMENT_NEW
+
+Class-level annotation allowing only the use of the placement `new` operator. This disallows general allocation of the
+object but allows putting the object as a value object in collections. If the class has `Member<T>` references,
+you need to declare a `Trace()` method. That trace method will be called automatically by the on-heap collections.
+
+Classes with this annotation need a `Trace()` method, but should not inherit a garbage collected class.
+
+## Handles
+
+Class templates in this section are smart pointers, each carrying a pointer to an on-heap object (think of `scoped_refptr<T>`
+for `RefCounted<T>`). Collectively, they are called *handles*.
+
+On-heap objects must be retained by any of these, depending on the situation.
+
+### Raw pointers
+
+On-stack references to on-heap objects must be raw pointers.
+
+```c++
+void someFunction()
+{
+ SomeGarbageCollectedClass* object = new SomeGarbageCollectedClass; // OK, retained by a pointer.
+ ...
+}
+// OK to leave the object behind. The Blink GC system will free it up when it becomes unused.
+```
+
+### Member, WeakMember
+
+In a garbage-collected class, on-heap objects must be retained by `Member<T>` or `WeakMember<T>`, depending on
+the desired semantics.
+
+`Member<T>` represents a *strong* reference to an object of type `T`, which means that the referred object is kept
+alive as long as the owner class instance is alive. Unlike `scoped_refptr<T>`, it is okay to form a reference cycle with
+members (in on-heap objects) and raw pointers (on stack).
+
+`WeakMember<T>` is a *weak* reference to an object of type `T`. Unlike `Member<T>`, `WeakMember<T>` does not keep
+the pointed object alive. The pointer in a `WeakMember<T>` can become `nullptr` when the object gets garbage-collected.
+It may take some time for the pointer in a `WeakMember<T>` to become `nullptr` after the object actually goes unused,
+because this rewrite is only done within Blink GC's garbage collection period.
+
+```c++
+class SomeGarbageCollectedClass : public GarbageCollected<GarbageCollectedSomething> {
+ ...
+private:
+ Member<AnotherGarbageCollectedClass> m_another; // OK, retained by Member<T>.
+ WeakMember<AnotherGarbageCollectedClass> m_anotherWeak; // OK, weak reference.
+};
+```
+
+The use of `WeakMember<T>` incurs some overhead in garbage collector's performance. Use it sparingly. Usually, weak
+members are not necessary at all, because reference cycles with members are allowed.
+
+More specifically, `WeakMember<T>` should be used only if the owner of a weak member can outlive the pointed object.
+Otherwise, `Member<T>` should be used.
+
+You need to trace every `Member<T>` and `WeakMember<T>` in your class. See [Tracing](#Tracing).
+
+### Persistent, WeakPersistent, CrossThreadPersistent, CrossThreadWeakPersistent
+
+In a non-garbage-collected class, on-heap objects must be retained by `Persistent<T>`, `WeakPersistent<T>`,
+`CrossThreadPersistent<T>`, or `CrossThreadWeakPersistent<T>`, depending on the situations and the desired semantics.
+
+`Persistent<T>` is the most basic handle in the persistent family, which makes the referred object alive
+unconditionally, as long as the persistent handle is alive.
+
+`WeakPersistent<T>` does not make the referred object alive, and becomes `nullptr` when the object gets
+garbage-collected, just like `WeakMember<T>`.
+
+`CrossThreadPersistent<T>` and `CrossThreadWeakPersistent<T>` are cross-thread variants of `Persistent<T>` and
+`WeakPersistent<T>`, respectively, which can point to an object in a different thread.
+
+```c++
+class NonGarbageCollectedClass {
+ ...
+private:
+ Persistent<SomeGarbageCollectedClass> m_something; // OK, the object will be alive while this persistent is alive.
+};
+```
+
+*** note
+**Warning:** `Persistent<T>` and `CrossThreadPersistent<T>` are vulnerable to reference cycles. If a reference cycle
+is formed with `Persistent`s, `Member`s, `RefPtr`s and `OwnPtr`s, all the objects in the cycle **will leak**, since
+nobody in the cycle can be aware of whether they are ever referred from anyone.
+
+When you are about to add a new persistent, be careful not to create a reference cycle. If a cycle is inevitable, make
+sure the cycle is eventually cut by someone outside the cycle.
+***
+
+Persistents have small overhead in itself, because they need to maintain the list of all persistents. Therefore, it's
+not a good idea to create or keep a lot of persistents at once.
+
+Weak variants have overhead just like `WeakMember<T>`. Use them sparingly.
+
+The need of cross-thread persistents may indicate a poor design in multi-thread object ownership. Think twice if they
+are really necessary.
+
+## Tracing
+
+A garbage-collected class may need to have *a tracing method*, which lists up all the on-heap objects it has. The
+tracing method is called when the garbage collector needs to determine (1) all the on-heap objects referred from a
+live object, and (2) all the weak handles that may be filled with `nullptr` later. These are done in the "marking"
+phase of the mark-and-sweep GC.
+
+The basic form of tracing is illustrated below:
+
+```c++
+// In a header file:
+class SomeGarbageCollectedClass
+ : public GarbageCollected<SomeGarbageCollectedClass> {
+ public:
+ void Trace(Visitor*);
+
+private:
+ Member<AnotherGarbageCollectedClass> another_;
+};
+
+// In an implementation file:
+void SomeGarbageCollectedClass::Trace(Visitor* visitor) {
+ visitor->Trace(another_);
+}
+```
+
+Specifically, if your class needs a tracing method, you need to declare and
+define a `Trace(Visitor*)` method. This method is normally declared in the
+header file and defined once in the implementation file, but there are
+variations. Another common variation is to declare a virtual `Trace()` for base
+classes that will be subclassed.
+
+The function implementation must contain:
+
+* For each on-heap object `object` in your class, a tracing call: `visitor->Trace(object_);`.
+* If your class has one or more weak references (`WeakMember<T>`), you have the option of
+ registering a *weak callback* for the object. See details below for how.
+* For each base class of your class `BaseClass` that is a descendant of `GarbageCollected<T>` or
+ `GarbageCollectedMixin`, a delegation call to base class: `BaseClass::Trace(visitor);`"
+
+It is recommended that the delegation call, if any, is put at the end of a tracing method.
+
+The following example shows more involved usage:
+
+```c++
+class A : public GarbageCollected<A> {
+ public:
+ virtual void Trace(Visitor*) { } // Nothing to trace here.
+};
+
+class B : public A {
+ // Nothing to trace here; exempted from having a tracing method.
+};
+
+class C : public B {
+ public:
+ void Trace(Visitor*) override;
+
+ private:
+ Member<X> x_;
+ WeakMember<Y> y_;
+ HeapVector<Member<Z>> z_;
+};
+
+void C::Trace(Visitor* visitor) {
+ visitor->Trace(x_);
+ visitor->Trace(y_); // Weak member needs to be traced.
+ visitor->Trace(z_); // Heap collection does, too.
+ B::Trace(visitor); // Delegate to the parent. In this case it's empty, but this is required.
+}
+```
+
+Given that the class `C` above contained a `WeakMember<Y>` field, you could alternatively
+register a *weak callback* in the trace method, and have it be invoked after the marking
+phase:
+
+```c++
+
+void C::ClearWeakMembers(Visitor* visitor)
+{
+ if (ThreadHeap::isHeapObjectAlive(y_))
+ return;
+
+ // |m_y| is not referred to by anyone else, clear the weak
+ // reference along with updating state / clearing any other
+ // resources at the same time. None of those operations are
+ // allowed to perform heap allocations:
+ y_->detach();
+
+ // Note: if the weak callback merely clears the weak reference,
+ // it is much simpler to just |trace| the field rather than
+ // install a custom weak callback.
+ y_ = nullptr;
+}
+
+void C::Trace(Visitor* visitor) {
+ visitor->template registerWeakMembers<C, &C::ClearWeakMembers>(this);
+ visitor->Trace(x_);
+ visitor->Trace(z_); // Heap collection does, too.
+ B::Trace(visitor); // Delegate to the parent. In this case it's empty, but this is required.
+}
+```
+
+Please notice that if the object (of type `C`) is also not reachable, its `Trace` method
+will not be invoked and any follow-on weak processing will not be done. Hence, if the
+object must always perform some operation when the weak reference is cleared, that
+needs to (also) happen during finalization.
+
+Weak callbacks have so far seen little use in Blink, but a mechanism that's available.
+
+## Heap collections
+
+Heap collections are WTF collection types that support `Member<T>`, `WeakMember<T>`(see [below](#Weak collections)), and garbage collected objects as its elements.
+
+Here is the complete list:
+
+- WTF::Vector → blink::HeapVector
+- WTF::Deque → blink::HeapDeque
+- WTF::HashMap → blink::HeapHashMap
+- WTF::HashSet → blink::HeapHashSet
+- WTF::LinkedHashSet → blink::HeapLinkedHashSet
+- WTF::ListHashSet → blink::HeapListHashSet
+- WTF::HashCountedSet → blink::HeapHashCountedSet
+
+These heap collections work mostly the same way as their WTF collection counterparts but there are some things to keep in mind.
+
+Heap collections are special in that the types themselves do not inherit from GarbageCollected (hence they are not allocated on the Oilpan heap) but they still *need to be traced* from the trace method (because we need to trace the backing store which is on the Oilpan heap).
+
+```c++
+class MyGarbageCollectedClass : public GarbageCollected<MyGarbageCollectedClass> {
+ public:
+ void Trace(Visitor* visitor) { visitor->Trace(list_); }
+ private:
+ HeapVector<Member<AnotherGarbageCollectedClass>> list_;
+};
+```
+
+When you want to add a heap collection as a member of a non-garbage-collected class, please use the persistent variants (just prefix the type with Persistent e.g. PersistentHeapVector, PersistentHeapHashMap, etc.).
+
+```c++
+class MyNotGarbageCollectedClass {
+ private:
+ PersistentHeapVector<Member<MyGarbageCollectedClass>> list_;
+};
+```
+
+Please be very cautious if you want to use a heap collection from multiple threads. Reference to heap collections may be passed to another thread using CrossThreadPersistents, but *you may not modify the collection from the non-owner thread*. This is because modifications to collections may trigger backing store reallocations, and Oilpan's per thread heap requires that modifications to a heap happen on its owner thread.
+
+### Weak collections
+
+You can put `WeakMember<T>` in heap collections except for `HeapVector` and `HeapDeque` which we do not support.
+
+During an Oilpan GC, the weak members that refernce a collected object will be removed from its heap collection, meaning the size of the collection will shrink and you do not have to check for null weak members when iterating through the collection.
+
+## Traits helpers
+
+At times, one may be working on code that needs to deal with both "regular" types and classes managed by the Blink GC. The following helpers can aid in writing code that needs to use different wrappers and containers based on whether a type is managed by Oilpan.
+
+### AddMemberIfNeeded<T>
+
+Given a type `T`, defines a type alias that is either `Member<T>` or `T` depending on whether `T` is a type managed by the Blink GC.
+
+```c++
+class MyGarbageCollectedClass : public GarbageCollected<MyGarbageCollectedClass> {
+ // ...
+};
+
+class MyNotGarbageCollectedClass {
+ // ...
+};
+
+AddMemberIfNeeded<MyNotGarbageCollectedClass> v1; // MyNotGarbageCollectedClass v1;
+AddMemberIfNeeded<int32_t> v2; // int32_t v2;
+AddMemberIfNeeded<MyGarbageCollectedClass> v3; // Member<MyGarbageCollectedClass> v3;
+```
+
+### VectorOf<T>
+
+Given a type `T`, defines a type alias that is either `HeapVector<T>`, `HeapVector<Member<T>>` or `Vector<T>` based on the following rules:
+
+* `T` is a type managed by the Blink GC → `HeapVector<Member<T>>`
+* `T` has a `Trace()` method but is not managed by the Blink GC → `HeapVector<T>` (this is a rare case; IDL unions and dictionaries fall in this category, for example)
+* All other cases → `Vector<T>`
+
+```c++
+class MyGarbageCollectedClass : public GarbageCollected<MyGarbageCollectedClass> {
+ // ...
+};
+
+class MyNonGCButTraceableClass {
+ public:
+ void Trace(Visitor* visitor) {
+ // ...
+ }
+};
+
+class MyNotGarbageCollectedClass {
+ // ...
+};
+
+VectorOf<float> v1; // Vector<float> v1;
+VectorOf<MyNotGarbageCollectedClass> v2; // Vector<MyNotGarbageCollectedClass> v2;
+VectorOf<MyNonGCButTraceableClass> v3; // HeapVector<MyNonGCButTraceableClass> v3;
+VectorOf<MyGarbageCollectedClass> v4; // HeapVector<Member<MyGarbageCollectedClass>> v4;
+```
+
+### VectorOfPairs<T, U>
+
+Similar to `VectorOf<T>`, but defines a type alias that is either `HeapVector<std::pair<V, X>>` (where `V` is either `T` or `Member<T>` and `X` is either `U` or `Member<U>`) or `Vector<std::pair<T, U>>`.
+
+In other words, if either `T` or `U` needs to be wrapped in a `HeapVector` instead of a `Vector`, `VectorOfPairs` will use a `HeapVector<std::pair<>>` and wrap them with `Member<>` appropriately. Otherwise, a `Vector<std::pair<>>` will be used.
+
+```c++
+class MyGarbageCollectedClass : public GarbageCollected<MyGarbageCollectedClass> {
+ // ...
+};
+
+class MyNonGCButTraceableClass {
+ public:
+ void Trace(Visitor* visitor) {
+ // ...
+ }
+};
+
+class MyNotGarbageCollectedClass {
+ // ...
+};
+
+// Vector<std::pair<double, int8_t>> v1;
+VectorOfPairs<double, int8_t> v1;
+
+// Vector<std::pair<MyNotGarbageCollectedClass, String>> v2;
+VectorOfPairs<MyNotGarbageCollectedClass, String> v2;
+
+// HeapVector<std::pair<float, MyNonGCButTraceableClass>> v3;
+VectorOfPairs<float, MyNonGCButTraceableClass> v3;
+
+// HeapVector<std::pair<MyNonGCButTraceableClass, Member<MyGarbageCollectedClass>>> v4;
+VectorOfPairs<MyNonGCButTraceableClass, MyGarbageCollectedClass> v4;
+```
diff --git a/chromium/third_party/blink/renderer/platform/heap/BlinkGCDesign.md b/chromium/third_party/blink/renderer/platform/heap/BlinkGCDesign.md
new file mode 100644
index 00000000000..9748c4d3691
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/BlinkGCDesign.md
@@ -0,0 +1,200 @@
+# Blink GC Design
+
+Oilpan is a garbage collection system for Blink objects.
+This document explains the design of the GC.
+If you're just interested in how to use Oilpan,
+see [BlinkGCAPIReference](BlinkGCAPIReference.md).
+
+[TOC]
+
+## Overview
+
+Oilpan is a single-threaded mark-and-sweep GC.
+It doesn't (yet) implement a generational or incremental GC.
+
+Blink has multiple threads including the main thread, HTML parser threads,
+database threads and worker threads. Threads that touch Oilpan's heap need
+to be attached to Oilpan. These threads can have cross-thread pointers.
+Oilpan scans the object graph spanning these threads and collects
+unreachable objects.
+
+## Threading model
+
+Oilpan runs a GC in the following steps:
+
+Step 1. A thread decides to trigger a GC. The thread can be any thread
+(it is likely to be the main thread because most allocations take place
+on the main thread). The thread is called a GCing thread.
+
+Step 2. The GCing thread waits for all other threads to enter safe points.
+A safe point is a place where it's guaranteed that the thread does not
+mutate the object graph of objects in Oilpan's heap. In common cases the thread
+stops at the safe point but doesn't necessarily need to stop. For example,
+the thread is allowed to execute a synchronous IO at the safe point as
+long as it doesn't mutate the object graph. Safe points are inserted into
+many places so that the thread can enter the safe point as soon as possible
+when the GCing thread requests the thread to do so. For example, safe points
+are inserted into V8 interruptors, at the end of event loops,
+before acquiring a mutex, before starting a synchronous IO operation etc.
+
+Step 3. Once all the threads enter the safe points, the GCing thread starts
+a marking phase. The GCing thread marks all objects reachable from the root
+set by calling trace() methods defined on each object. This means that the
+GCing thread marks objects owned by all the threads. This doesn't cause any
+threading race because all the other threads are at the safe points.
+
+Step 4. Once the marking is complete, the GCing thread resumes executions of
+all the other threads. Each thread starts a sweeping phase. Each thread is
+responsible for destructing objects that the thread has allocated.
+That way objects are guaranteed to get destructed on the thread that has
+allocated the objects. The sweeping is done by each thread lazily.
+Instead of completing the sweeping phase in one go, the thread sweeps
+objects incrementally as much as it allocates. Lazy sweeping is helpful
+to distribute a long pause time of the sweeping phase into small chunks.
+
+The step 2 and 3 is the pause time of the GC.
+The pause time is proportional to the number of objects marked
+in the marking phase, meaning that it is proportional to the number of
+live objects.
+
+Notes:
+
+* If the GCing thread fails at stopping all the other threads in a
+certain period of time, it gives up triggering a GC. That way we avoid
+introducing an unacceptably long pause time. (This will rarely happen
+because each thread enters safe points very frequently.)
+
+* It is not really nice that the GCing thread has to stop all the other threads.
+For example, a worker thread has to get involved in a GC
+caused by a lot of allocations happening on the main thread.
+To resolve the issue, we have a plan to split the Oilpan's heap
+into per-thread heaps. Once it's implemented, each thread can run
+GCs independently.
+
+## Precise GC and conservative GC
+
+Oilpan has two kinds of GCs.
+
+When all threads are stopped at the safe points at the end of event loops,
+a precise GC is triggered. At this point it is guaranteed that
+there are no on-stack pointers pointing to Oilpan's heap.
+Thus Oilpan runs a precise GC. The root set of a precise GC is
+persistent handles.
+
+Otherwise, a conservative GC is triggered. In this case, the GC scans
+a native stack of the threads (which are not stopped at the safe points
+at the end of event loops) and push the pointers discovered via the native
+stacks into the root set. (That's why you can use raw pointers on the
+native stack.) The root set of a conservative GC is persistent handles
+and the native stacks of the threads.
+
+A conservative GC is more expensive than a precise GC because
+the conservative GC needs to scan the native stacks.
+Thus Oilpan tries its best to trigger GCs at the end of an event loop.
+In particular, Oilpan tries its best to trigger GCs in idle tasks.
+
+## Marking phase
+
+The marking phase (the step 3 in the above description) consists of
+the following steps. The marking phase is executed in a stop-the-world manner.
+
+Step 3-1. The GCing thread marks all objects reachable from the root set
+by calling trace() methods defined on each object.
+
+Step 3-2. The GCing thread clears out all trivial WeakMembers.
+
+To prevent a use-after-free from happening, it is very important to
+make sure that Oilpan doesn't mis-trace any edge of the object graph.
+This means that all pointers except on-stack pointers must be wrapped
+with Oilpan's handles (i.e., Persistent<>, Member<>, WeakMember<> etc).
+Raw pointers to on-heap objects have a risk of creating an edge Oilpan
+cannot understand and causing a use-after-free. You should not use raw pointers
+to on-heap objects (except raw pointers on native stacks) unless you're pretty
+sure that the target objects are guaranteed to be kept alive in other ways.
+
+## Sweeping phase
+
+The sweeping phase (the step 4 in the above description) consists of
+the following steps. The sweeping phase is executed by each thread in parallel.
+
+Step 4-1. The thread clears out all non-trivial WeakMembers.
+Non-trivial WeakMembers are the ones that have manual weak processing
+(registered by registerWeakMembers()) and the ones embedded in HeapHashMap etc.
+The reason we don't run non-trivial WeakMembers in the marking phase is that
+clearing out the non-trivial WeakMembers can invoke some destructors
+(e.g., if you have HeapHashMap<WeakMember<X>, OwnPtr<Y>>, Y's destructor
+is invoked when the weak processing removes the key).
+The destructors must run in the same thread that has allocated the objects.
+
+Step 4-2. The thread invokes pre-finalizers.
+At this point, no destructors have been invoked.
+Thus the pre-finalizers are allowed to touch any other on-heap objects
+(which may get destructed in this sweeping phase).
+
+Step 4-3. The thread invokes destructors of dead objects that are marked
+as eagerly-finalized. See the following notes for more details about the
+eagerly-finalized objects.
+
+Step 4-4. The thread resumes mutator's execution. (A mutator means user code.)
+
+Step 4-5. As the mutator allocates new objects, lazy sweeping invokes
+destructors of the remaining dead objects incrementally.
+
+There is no guarantee of the order in which the destructors are invoked.
+That's why destructors must not touch any other on-heap objects
+(which might have already been destructed). If some destructor unavoidably
+needs to touch other on-heap objects, you need to use a pre-finalizer.
+The pre-finalizer is allowed to touch other on-heap objects.
+
+The mutator is resumed before all the destructors has run.
+For example, imagine a case where X is a client of Y, and Y holds
+a list of clients. If you rely on X's destructor removing X from the list,
+there is a risk that Y iterates the list and calls some method of X
+which may touch other on-heap objects. This causes a use-after-free.
+You need to make sure that X is explicitly removed from the list
+before the mutator resumes its execution in a way that doesn't rely on
+X's destructor.
+
+Either way, the most important thing is that there is no guarantee of
+when destructors run. You shouldn't put any assumption about
+the order and the timing.
+(In general, it's dangerous to do something complicated in a destructor.)
+
+Notes (The followings are features you'll need only when you have
+unusual destruction requirements):
+
+* Weak processing runs only when the holder object of the WeakMember
+outlives the pointed object. If the holder object and the pointed object die
+at the same time, the weak processing doesn't run. It is wrong to write code
+assuming that the weak processing always runs.
+
+* Pre-finalizers are heavy because the thread needs to scan all pre-finalizers
+at each sweeping phase to determine which pre-finalizers to be invoked
+(the thread needs to invoke pre-finalizers of dead objects).
+You should avoid adding pre-finalizers to frequently created objects.
+
+* Eagerly-finalized objects are guaranteed to get destructed before the
+mutator resumes its execution. This means that a destructor of
+an eagerly-finalized object is allowed to touch other not-eagerly-finalized
+objects whereas it's not allowed to touch other eagerly-finalized objects.
+This notion is useful for some objects, but nasty.
+We're planning to replace most eagerly-finalized objects with pre-finalizers.
+
+* There is a subtle scenario where a next GC is triggered before
+the thread finishes lazy sweeping. In that case, the not-yet-swept objects
+are marked as dead and the next GC starts. The objects marked as dead are
+swept in the sweeping phase of the next GC. This means that you cannot assume
+that some two objects get destructed in the same GC cycle.
+
+## Heap structures
+
+Each thread has its dedicated heap so that the thread can allocate an object
+without acquiring a lock. For example, an object allocated on thread 1 goes
+to a different heap than an object allocated on thread 2.
+
+In addition, each thread provides multiple arenas to group objects by their type
+and thus improves locality.
+For example, a Node object allocated on thread 1 goes to a different heap than
+a CSSValue object allocated on thread 1. (See BlinkGC.h to get the list of
+the typed arenas.)
+
diff --git a/chromium/third_party/blink/renderer/platform/heap/DEPS b/chromium/third_party/blink/renderer/platform/heap/DEPS
new file mode 100644
index 00000000000..3600042e6a4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/DEPS
@@ -0,0 +1,15 @@
+include_rules = [
+ # To whitelist base/ stuff Blink is allowed to include, we list up all
+ # directories and files instead of writing 'base/'.
+ "+base/atomicops.h",
+ "+base/bits.h",
+ "+base/compiler_specific.h",
+ "+base/synchronization/lock.h",
+ "+base/sys_info.h",
+]
+
+specific_include_rules = {
+ 'run_all_tests\.cc': [
+ "+content/public/test/blink_test_environment.h",
+ ],
+}
diff --git a/chromium/third_party/blink/renderer/platform/heap/OWNERS b/chromium/third_party/blink/renderer/platform/heap/OWNERS
new file mode 100644
index 00000000000..cf641acea1f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/OWNERS
@@ -0,0 +1,7 @@
+erik.corry@gmail.com
+haraken@chromium.org
+kouhei@chromium.org
+mlippautz@chromium.org
+
+# TEAM: oilpan-reviews@chromium.org
+# COMPONENT: Blink>MemoryAllocator>GarbageCollection
diff --git a/chromium/third_party/blink/renderer/platform/heap/README.md b/chromium/third_party/blink/renderer/platform/heap/README.md
new file mode 100644
index 00000000000..8cd0dea8f1a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/README.md
@@ -0,0 +1,8 @@
+# Blink GC
+
+This directory contains the implementation of Blink GC system (a.k.a. Oilpan).
+
+## Documentation
+
+* [API reference](BlinkGCAPIReference.md)
+* [GC design](BlinkGCDesign.md)
diff --git a/chromium/third_party/blink/renderer/platform/heap/asm/BUILD.gn b/chromium/third_party/blink/renderer/platform/heap/asm/BUILD.gn
new file mode 100644
index 00000000000..6c87950f41a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/asm/BUILD.gn
@@ -0,0 +1,55 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+if (current_cpu == "x86" || current_cpu == "x64") {
+ import("//third_party/yasm/yasm_assemble.gni")
+
+ yasm_assemble("asm") {
+ assert(current_cpu == "x86" || current_cpu == "x64")
+
+ sources = [
+ "SaveRegisters_x86.asm",
+ ]
+
+ yasm_flags = []
+ if (is_mac) {
+ # Necessary to ensure symbols end up with a _ prefix; added by
+ # yasm_compile.gypi for Windows, but not Mac.
+ yasm_flags += [ "-DPREFIX" ]
+ }
+ if (current_cpu == "x64") {
+ if (is_win) {
+ yasm_flags += [ "-DX64WIN=1" ]
+ } else {
+ yasm_flags += [ "-DX64POSIX=1" ]
+ }
+ } else { # current_cpu == "x86"
+ yasm_flags += [ "-DIA32=1" ]
+ }
+ }
+} else { # current_cpu == "x86" || current_cpu == "x64"
+ source_set("asm") {
+ if (current_cpu == "arm") {
+ sources = [
+ "SaveRegisters_arm.S",
+ ]
+ } else if (current_cpu == "arm64") {
+ sources = [
+ "SaveRegisters_arm64.S",
+ ]
+ } else if (current_cpu == "mipsel") {
+ sources = [
+ "SaveRegisters_mips.S",
+ ]
+ } else if (current_cpu == "mips64el") {
+ sources = [
+ "SaveRegisters_mips64.S",
+ ]
+ }
+
+ if (current_cpu == "arm") {
+ defines = [ "ARM=1" ]
+ }
+ }
+} # current_cpu == "x86" || current_cpu == "x64"
diff --git a/chromium/third_party/blink/renderer/platform/heap/asm/SaveRegisters_arm.S b/chromium/third_party/blink/renderer/platform/heap/asm/SaveRegisters_arm.S
new file mode 100644
index 00000000000..af961fe1a5d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/asm/SaveRegisters_arm.S
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+
+/*
+ * typedef void (*PushAllRegistersCallback)(SafePointBarrier*, ThreadState*, intptr_t*);
+ * extern "C" void PushAllRegisters(SafePointBarrier*, ThreadState*, PushAllRegistersCallback)
+ */
+
+.type PushAllRegisters, %function
+.global PushAllRegisters
+.hidden PushAllRegisters
+#ifdef __thumb__
+/* In THUMB Mode jump to ARM stub via bx to ensure CPU mode switch.
+ * FIXME: This trampoline is provided to workaround bugs in
+ * the THUMB/ARM interworking that appear in the component build.
+ * When these issues are resolved this stub can be removed.
+ */
+.align 2
+.code 16
+.thumb_func
+PushAllRegisters:
+ adr r3, pushAllRegistersARM
+ bx r3
+
+.type pushAllRegistersARM, %function
+.hidden pushAllRegistersARM
+.align 4
+.code 32
+pushAllRegistersARM:
+#else
+/* ARM Mode */
+.align 4
+.code 32
+PushAllRegisters:
+#endif
+ /* Push all callee-saved registers and save return address. */
+ push {r4-r11, lr}
+ /* Pass the two first arguments unchanged (r0, r1)
+ * and pass the stack pointer after pushing callee-saved
+ * registers to the callback function.
+ */
+ mov r3, r2
+ mov r2, sp
+ blx r3
+ /* Discard all the registers, and pop lr into pc which returns
+ * and switches mode if needed.
+ */
+ add sp, sp, #32
+ pop {pc}
diff --git a/chromium/third_party/blink/renderer/platform/heap/asm/SaveRegisters_arm64.S b/chromium/third_party/blink/renderer/platform/heap/asm/SaveRegisters_arm64.S
new file mode 100644
index 00000000000..a6f31555e6f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/asm/SaveRegisters_arm64.S
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+
+/*
+ * typedef void (*PushAllRegistersCallback)(SafePointBarrier*, ThreadState*, intptr_t*);
+ * extern "C" void PushAllRegisters(SafePointBarrier*, ThreadState*, PushAllRegistersCallback)
+ */
+
+.type PushAllRegisters, %function
+.global PushAllRegisters
+.hidden PushAllRegisters
+PushAllRegisters:
+ /* Save return address. */
+ sub sp, sp, #96
+ stp x19, x20, [sp, #80]
+ stp x21, x22, [sp, #64]
+ stp x23, x24, [sp, #48]
+ stp x25, x26, [sp, #32]
+ stp x27, x28, [sp, #16]
+ stp x30, x29, [sp, #0] // x30 is lr.
+
+ /* Pass the two first arguments unchanged (x0, x1)
+ * and pass the stack pointer as third argument to the
+ * callback function.
+ */
+ mov x3, x2
+ mov x2, sp
+ blr x3
+
+ /* We don't restore all registers since they are callee saved (so
+ * the callback didn't clobber them) and we didn't modify them either.
+ */
+ ldr x30, [sp], #96
+ ret
diff --git a/chromium/third_party/blink/renderer/platform/heap/asm/SaveRegisters_mips.S b/chromium/third_party/blink/renderer/platform/heap/asm/SaveRegisters_mips.S
new file mode 100644
index 00000000000..f2b22bda65f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/asm/SaveRegisters_mips.S
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+
+/*
+ * typedef void (*PushAllRegistersCallback)(SafePointBarrier*, ThreadState*, intptr_t*);
+ * extern "C" void PushAllRegisters(SafePointBarrier*, ThreadState*, PushAllRegistersCallback)
+ */
+
+.type PushAllRegisters, %function
+.global PushAllRegisters
+.hidden PushAllRegisters
+PushAllRegisters:
+ // Reserve space for callee-saved registers, return address,
+ // as well as for the callee arguments.
+ addiu $sp,$sp,-56
+ // Save the callee-saved registers and the return address.
+ sw $s0,16($sp)
+ sw $s1,20($sp)
+ sw $s2,24($sp)
+ sw $s3,28($sp)
+ sw $s4,32($sp)
+ sw $s5,36($sp)
+ sw $s6,40($sp)
+ sw $s7,44($sp)
+ sw $ra,48($sp)
+ // Pass the two first arguments untouched in a0 and a1 and the
+ // stack pointer to the callback.
+ move $t9,$a2
+ move $a2,$sp
+ jalr $t9
+ // Restore return address, adjust stack and return. No
+ // callee-saved register was changed so we do not have to
+ // restore callee-saved registers.
+ lw $ra,48($sp)
+ addiu $sp,$sp,56
+ jr $ra
diff --git a/chromium/third_party/blink/renderer/platform/heap/asm/SaveRegisters_mips64.S b/chromium/third_party/blink/renderer/platform/heap/asm/SaveRegisters_mips64.S
new file mode 100644
index 00000000000..71d04553403
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/asm/SaveRegisters_mips64.S
@@ -0,0 +1,41 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/*
+ * typedef void (*PushAllRegistersCallback)(SafePointBarrier*, ThreadState*, intptr_t*);
+ * extern "C" void PushAllRegisters(SafePointBarrier*, ThreadState*, PushAllRegistersCallback)
+ */
+
+.type PushAllRegisters, %function
+.global PushAllRegisters
+.hidden PushAllRegisters
+PushAllRegisters:
+ // Push all callee-saves registers to get them
+ // on the stack for conservative stack scanning.
+ // Reserve space for callee-saved registers and return address.
+ daddiu $sp,$sp,-80
+ // Save the callee-saved registers and the return address.
+ sd $s0,0($sp)
+ sd $s1,8($sp)
+ sd $s2,16($sp)
+ sd $s3,24($sp)
+ sd $s4,32($sp)
+ sd $s5,40($sp)
+ sd $s6,48($sp)
+ sd $s7,56($sp)
+ sd $ra,64($sp)
+ // Note: the callee-saved floating point registers do not need to be
+ // copied to the stack, because fp registers never hold heap pointers
+ // and so do not need to be kept visible to the garbage collector.
+ // Pass the two first arguments untouched in a0 and a1 and the
+ // stack pointer to the callback.
+ move $t9,$a2
+ move $a2,$sp
+ jalr $t9
+ // Restore return address, adjust stack and return.
+ // Note: the copied registers do not need to be reloaded here,
+ // because they were preserved by the called routine.
+ ld $ra,64($sp)
+ daddiu $sp,$sp,80
+ jr $ra
diff --git a/chromium/third_party/blink/renderer/platform/heap/asm/SaveRegisters_x86.asm b/chromium/third_party/blink/renderer/platform/heap/asm/SaveRegisters_x86.asm
new file mode 100644
index 00000000000..8374167f880
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/asm/SaveRegisters_x86.asm
@@ -0,0 +1,159 @@
+;; Copyright (C) 2013 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 X64POSIX
+%define X64POSIX 0
+%endif
+
+%ifndef X64WIN
+%define X64WIN 0
+%endif
+
+%ifndef IA32
+%define IA32 0
+%endif
+
+%ifndef ARM
+%define ARM 0
+%endif
+
+;; Prefix symbols by '_' if PREFIX is defined.
+%ifdef PREFIX
+%define mangle(x) _ %+ x
+%else
+%define mangle(x) x
+%endif
+
+
+; PRIVATE makes a symbol private.
+%ifidn __OUTPUT_FORMAT__,elf32
+ %define PRIVATE :hidden
+%elifidn __OUTPUT_FORMAT__,elf64
+ %define PRIVATE :hidden
+%elifidn __OUTPUT_FORMAT__,elfx32
+ %define PRIVATE :hidden
+%elif X64WIN
+ %define PRIVATE
+%else
+ %define PRIVATE :private_extern
+%endif
+
+;; typedef void (*PushAllRegistersCallback)(SafePointBarrier*, ThreadState*, intptr_t*);
+;; extern "C" void PushAllRegisters(SafePointBarrier*, ThreadState*, PushAllRegistersCallback)
+
+ global mangle(PushAllRegisters) PRIVATE
+
+%if X64POSIX
+
+mangle(PushAllRegisters):
+ ;; Push all callee-saves registers to get them
+ ;; on the stack for conservative stack scanning.
+ ;; We maintain 16-byte alignment at calls (required on Mac).
+ ;; There is an 8-byte return address on the stack and we push
+ ;; 56 bytes which maintains 16-byte stack alignment
+ ;; at the call.
+ push 0
+ push rbx
+ push rbp
+ push r12
+ push r13
+ push r14
+ push r15
+ ;; Pass the two first arguments unchanged (rdi, rsi)
+ ;; and the stack pointer after pushing callee-saved
+ ;; registers to the callback.
+ mov r8, rdx
+ mov rdx, rsp
+ call r8
+ ;; Pop the callee-saved registers. None of them were
+ ;; modified so no restoring is needed.
+ add rsp, 56
+ ret
+
+%elif X64WIN
+
+mangle(PushAllRegisters):
+ ;; Push all callee-saves registers to get them
+ ;; on the stack for conservative stack scanning.
+ ;; There is an 8-byte return address on the stack and we push
+ ;; 72 bytes which maintains the required 16-byte stack alignment
+ ;; at the call.
+ push 0
+ push rsi
+ push rdi
+ push rbx
+ push rbp
+ push r12
+ push r13
+ push r14
+ push r15
+ ;; Pass the two first arguments unchanged (rcx, rdx)
+ ;; and the stack pointer after pushing callee-saved
+ ;; registers to the callback.
+ mov r9, r8
+ mov r8, rsp
+ call r9
+ ;; Pop the callee-saved registers. None of them were
+ ;; modified so no restoring is needed.
+ add rsp, 72
+ ret
+
+%elif IA32
+
+mangle(PushAllRegisters):
+ ;; Push all callee-saves registers to get them
+ ;; on the stack for conservative stack scanning.
+ ;; We maintain 16-byte alignment at calls (required on
+ ;; Mac). There is a 4-byte return address on the stack
+ ;; and we push 28 bytes which maintains 16-byte alignment
+ ;; at the call.
+ push ebx
+ push ebp
+ push esi
+ push edi
+ ;; Pass the two first arguments unchanged and the
+ ;; stack pointer after pushing callee-save registers
+ ;; to the callback.
+ mov ecx, [esp + 28]
+ push esp
+ push dword [esp + 28]
+ push dword [esp + 28]
+ call ecx
+ ;; Pop arguments and the callee-saved registers.
+ ;; None of the callee-saved registers were modified
+ ;; so we do not need to restore them.
+ add esp, 28
+ ret
+
+
+%elif ARM
+%error "Yasm does not support arm. Use SaveRegisters_arm.S on arm."
+%else
+%error "Unsupported platform."
+%endif
diff --git a/chromium/third_party/blink/renderer/platform/heap/blink_gc.h b/chromium/third_party/blink/renderer/platform/heap/blink_gc.h
new file mode 100644
index 00000000000..65d62de254f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/blink_gc.h
@@ -0,0 +1,138 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_BLINK_GC_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_BLINK_GC_H_
+
+// BlinkGC.h is a file that defines common things used by Blink GC.
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+#define PRINT_HEAP_STATS 0 // Enable this macro to print heap stats to stderr.
+
+namespace blink {
+
+class HeapObjectHeader;
+class MarkingVisitor;
+class Visitor;
+class ScriptWrappableVisitor;
+
+using Address = uint8_t*;
+
+using FinalizationCallback = void (*)(void*);
+using VisitorCallback = void (*)(Visitor*, void*);
+using MarkingVisitorCallback = void (*)(MarkingVisitor*, void*);
+using TraceCallback = VisitorCallback;
+using TraceWrappersCallback = void (*)(ScriptWrappableVisitor*, void*);
+using WeakCallback = VisitorCallback;
+using EphemeronCallback = VisitorCallback;
+using MissedWriteBarrierCallback = void (*)();
+using NameCallback = const char* (*)(const void* self);
+
+// Callback used for unit testing the marking of conservative pointers
+// (|CheckAndMarkPointer|). For each pointer that has been discovered to point
+// to a heap object, the callback is invoked with a pointer to its header. If
+// the callback returns true, the object will not be marked.
+using MarkedPointerCallbackForTesting = bool (*)(HeapObjectHeader*);
+
+// Simple alias to avoid heap compaction type signatures turning into
+// a sea of generic |void*|s.
+using MovableReference = void*;
+
+// Heap compaction supports registering callbacks that are to be invoked
+// when an object is moved during compaction. This is to support internal
+// location fixups that need to happen as a result.
+//
+// i.e., when the object residing at |from| is moved to |to| by the compaction
+// pass, invoke the callback to adjust any internal references that now need
+// to be |to|-relative.
+using MovingObjectCallback = void (*)(void* callback_data,
+ MovableReference from,
+ MovableReference to,
+ size_t);
+
+// List of typed arenas. The list is used to generate the implementation
+// of typed arena related methods.
+//
+// To create a new typed arena add a H(<ClassName>) to the
+// FOR_EACH_TYPED_ARENA macro below.
+#define FOR_EACH_TYPED_ARENA(H) \
+ H(Node) \
+ H(CSSValue)
+
+#define TypedArenaEnumName(Type) k##Type##ArenaIndex,
+
+class PLATFORM_EXPORT WorklistTaskId {
+ public:
+ static constexpr int MainThread = 0;
+};
+
+class PLATFORM_EXPORT BlinkGC final {
+ STATIC_ONLY(BlinkGC);
+
+ public:
+ // When garbage collecting we need to know whether or not there
+ // can be pointers to Blink GC managed objects on the stack for
+ // each thread. When threads reach a safe point they record
+ // whether or not they have pointers on the stack.
+ enum StackState { kNoHeapPointersOnStack, kHeapPointersOnStack };
+
+ enum MarkingType {
+ // The marking completes synchronously.
+ kAtomicMarking,
+ // The marking task is split and executed in chunks.
+ kIncrementalMarking,
+ // We run marking to take a heap snapshot. Sweeping should do nothing and
+ // just clear the mark flags.
+ kTakeSnapshot,
+ };
+
+ enum SweepingType {
+ // The sweeping task is split into chunks and scheduled lazily.
+ kLazySweeping,
+ // The sweeping task executs synchronously right after marking.
+ kEagerSweeping,
+ };
+
+ enum GCReason {
+ kIdleGC,
+ kPreciseGC,
+ kConservativeGC,
+ kForcedGC,
+ kMemoryPressureGC,
+ kPageNavigationGC,
+ kThreadTerminationGC,
+ kLastGCReason = kThreadTerminationGC,
+ };
+
+ enum ArenaIndices {
+ kEagerSweepArenaIndex = 0,
+ kNormalPage1ArenaIndex,
+ kNormalPage2ArenaIndex,
+ kNormalPage3ArenaIndex,
+ kNormalPage4ArenaIndex,
+ kVector1ArenaIndex,
+ kVector2ArenaIndex,
+ kVector3ArenaIndex,
+ kVector4ArenaIndex,
+ kInlineVectorArenaIndex,
+ kHashTableArenaIndex,
+ FOR_EACH_TYPED_ARENA(TypedArenaEnumName) kLargeObjectArenaIndex,
+ // Values used for iteration of heap segments.
+ kNumberOfArenas,
+ };
+
+ enum V8GCType {
+ kV8MinorGC,
+ kV8MajorGC,
+ };
+
+ // Sentinel used to mark not-fully-constructed during mixins.
+ static constexpr void* kNotFullyConstructedObject = nullptr;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_BLINK_GC_H_
diff --git a/chromium/third_party/blink/renderer/platform/heap/blink_gc_memory_dump_provider.cc b/chromium/third_party/blink/renderer/platform/heap/blink_gc_memory_dump_provider.cc
new file mode 100644
index 00000000000..fb672d0bc11
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/blink_gc_memory_dump_provider.cc
@@ -0,0 +1,137 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/heap/blink_gc_memory_dump_provider.h"
+
+#include <unordered_map>
+
+#include "base/trace_event/heap_profiler_allocation_context_tracker.h"
+#include "base/trace_event/memory_allocator_dump.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "base/trace_event/trace_event_memory_overhead.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/web_memory_allocator_dump.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/threading.h"
+
+namespace blink {
+namespace {
+
+void DumpMemoryTotals(base::trace_event::ProcessMemoryDump* memory_dump) {
+ base::trace_event::MemoryAllocatorDump* allocator_dump =
+ memory_dump->CreateAllocatorDump("blink_gc");
+ allocator_dump->AddScalar("size", "bytes",
+ ProcessHeap::TotalAllocatedSpace());
+
+ base::trace_event::MemoryAllocatorDump* objects_dump =
+ memory_dump->CreateAllocatorDump("blink_gc/allocated_objects");
+
+ // ThreadHeap::markedObjectSize() can be underestimated if we're still in the
+ // process of lazy sweeping.
+ objects_dump->AddScalar("size", "bytes",
+ ProcessHeap::TotalAllocatedObjectSize() +
+ ProcessHeap::TotalMarkedObjectSize());
+}
+
+void ReportAllocation(Address address, size_t size, const char* type_name) {
+ BlinkGCMemoryDumpProvider::Instance()->insert(address, size, type_name);
+}
+
+void ReportFree(Address address) {
+ BlinkGCMemoryDumpProvider::Instance()->Remove(address);
+}
+
+} // namespace
+
+BlinkGCMemoryDumpProvider* BlinkGCMemoryDumpProvider::Instance() {
+ DEFINE_STATIC_LOCAL(BlinkGCMemoryDumpProvider, instance, ());
+ return &instance;
+}
+
+BlinkGCMemoryDumpProvider::~BlinkGCMemoryDumpProvider() = default;
+
+bool BlinkGCMemoryDumpProvider::OnMemoryDump(
+ const base::trace_event::MemoryDumpArgs& args,
+ base::trace_event::ProcessMemoryDump* memory_dump) {
+ using base::trace_event::MemoryDumpLevelOfDetail;
+ MemoryDumpLevelOfDetail level_of_detail = args.level_of_detail;
+ // In the case of a detailed dump perform a mark-only GC pass to collect
+ // more detailed stats.
+ if (level_of_detail == MemoryDumpLevelOfDetail::DETAILED) {
+ ThreadState::Current()->CollectGarbage(
+ BlinkGC::kNoHeapPointersOnStack, BlinkGC::kTakeSnapshot,
+ BlinkGC::kEagerSweeping, BlinkGC::kForcedGC);
+ }
+ DumpMemoryTotals(memory_dump);
+
+ if (allocation_register_.is_enabled()) {
+ // Overhead should always be reported, regardless of light vs. heavy.
+ base::trace_event::TraceEventMemoryOverhead overhead;
+ std::unordered_map<base::trace_event::AllocationContext,
+ base::trace_event::AllocationMetrics>
+ metrics_by_context;
+ if (level_of_detail == MemoryDumpLevelOfDetail::DETAILED) {
+ allocation_register_.UpdateAndReturnsMetrics(metrics_by_context);
+ }
+ allocation_register_.EstimateTraceMemoryOverhead(&overhead);
+ memory_dump->DumpHeapUsage(metrics_by_context, overhead, "blink_gc");
+ }
+
+ // Merge all dumps collected by ThreadHeap::collectGarbage.
+ if (level_of_detail == MemoryDumpLevelOfDetail::DETAILED)
+ memory_dump->TakeAllDumpsFrom(current_process_memory_dump_.get());
+ return true;
+}
+
+void BlinkGCMemoryDumpProvider::OnHeapProfilingEnabled(bool enabled) {
+ if (enabled) {
+ allocation_register_.SetEnabled();
+ HeapAllocHooks::SetAllocationHook(ReportAllocation);
+ HeapAllocHooks::SetFreeHook(ReportFree);
+ } else {
+ HeapAllocHooks::SetAllocationHook(nullptr);
+ HeapAllocHooks::SetFreeHook(nullptr);
+ allocation_register_.SetDisabled();
+ }
+}
+
+base::trace_event::MemoryAllocatorDump*
+BlinkGCMemoryDumpProvider::CreateMemoryAllocatorDumpForCurrentGC(
+ const String& absolute_name) {
+ // TODO(bashi): Change type name of |absoluteName|.
+ return current_process_memory_dump_->CreateAllocatorDump(
+ absolute_name.Utf8().data());
+}
+
+void BlinkGCMemoryDumpProvider::ClearProcessDumpForCurrentGC() {
+ current_process_memory_dump_->Clear();
+}
+
+BlinkGCMemoryDumpProvider::BlinkGCMemoryDumpProvider()
+ : current_process_memory_dump_(new base::trace_event::ProcessMemoryDump(
+ nullptr,
+ {base::trace_event::MemoryDumpLevelOfDetail::DETAILED})) {}
+
+void BlinkGCMemoryDumpProvider::insert(Address address,
+ size_t size,
+ const char* type_name) {
+ base::trace_event::AllocationContext context;
+ if (!base::trace_event::AllocationContextTracker::
+ GetInstanceForCurrentThread()
+ ->GetContextSnapshot(&context))
+ return;
+ context.type_name = type_name;
+ if (!allocation_register_.is_enabled())
+ return;
+ allocation_register_.Insert(address, size, context);
+}
+
+void BlinkGCMemoryDumpProvider::Remove(Address address) {
+ if (!allocation_register_.is_enabled())
+ return;
+ allocation_register_.Remove(address);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/heap/blink_gc_memory_dump_provider.h b/chromium/third_party/blink/renderer/platform/heap/blink_gc_memory_dump_provider.h
new file mode 100644
index 00000000000..89bf8b5fcd6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/blink_gc_memory_dump_provider.h
@@ -0,0 +1,66 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_BLINK_GC_MEMORY_DUMP_PROVIDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_BLINK_GC_MEMORY_DUMP_PROVIDER_H_
+
+#include "base/trace_event/memory_dump_provider.h"
+#include "base/trace_event/sharded_allocation_register.h"
+#include "third_party/blink/renderer/platform/heap/blink_gc.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace base {
+namespace trace_event {
+
+class MemoryAllocatorDump;
+
+} // namespace trace_event
+} // namespace base
+
+namespace blink {
+class WebMemoryAllocatorDump;
+
+class PLATFORM_EXPORT BlinkGCMemoryDumpProvider final
+ : public base::trace_event::MemoryDumpProvider {
+ USING_FAST_MALLOC(BlinkGCMemoryDumpProvider);
+
+ public:
+ static BlinkGCMemoryDumpProvider* Instance();
+ ~BlinkGCMemoryDumpProvider() override;
+
+ // MemoryDumpProvider implementation.
+ bool OnMemoryDump(const base::trace_event::MemoryDumpArgs&,
+ base::trace_event::ProcessMemoryDump*) override;
+ void OnHeapProfilingEnabled(bool) override;
+
+ // The returned WebMemoryAllocatorDump is owned by
+ // BlinkGCMemoryDumpProvider, and should not be retained (just used to
+ // dump in the current call stack).
+ base::trace_event::MemoryAllocatorDump* CreateMemoryAllocatorDumpForCurrentGC(
+ const String& absolute_name);
+
+ // This must be called before taking a new process-wide GC snapshot, to
+ // clear the previous dumps.
+ void ClearProcessDumpForCurrentGC();
+
+ base::trace_event::ProcessMemoryDump* CurrentProcessMemoryDump() {
+ return current_process_memory_dump_.get();
+ }
+
+ void insert(Address, size_t, const char*);
+ void Remove(Address);
+
+ private:
+ BlinkGCMemoryDumpProvider();
+
+ base::trace_event::ShardedAllocationRegister allocation_register_;
+ std::unique_ptr<base::trace_event::ProcessMemoryDump>
+ current_process_memory_dump_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/heap/blink_gc_memory_dump_provider_test.cc b/chromium/third_party/blink/renderer/platform/heap/blink_gc_memory_dump_provider_test.cc
new file mode 100644
index 00000000000..26209a0c80b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/blink_gc_memory_dump_provider_test.cc
@@ -0,0 +1,24 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/heap/blink_gc_memory_dump_provider.h"
+
+#include "base/trace_event/process_memory_dump.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/wtf/threading.h"
+
+namespace blink {
+
+TEST(BlinkGCDumpProviderTest, MemoryDump) {
+ base::trace_event::MemoryDumpArgs args = {
+ base::trace_event::MemoryDumpLevelOfDetail::DETAILED};
+ std::unique_ptr<base::trace_event::ProcessMemoryDump> dump(
+ new base::trace_event::ProcessMemoryDump(nullptr, args));
+ BlinkGCMemoryDumpProvider::Instance()->OnMemoryDump(args, dump.get());
+ DCHECK(dump->GetAllocatorDump("blink_gc"));
+ DCHECK(dump->GetAllocatorDump("blink_gc/allocated_objects"));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/heap/garbage_collected.h b/chromium/third_party/blink/renderer/platform/heap/garbage_collected.h
new file mode 100644
index 00000000000..cac0ef4cea7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/garbage_collected.h
@@ -0,0 +1,342 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_GARBAGE_COLLECTED_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_GARBAGE_COLLECTED_H_
+
+#include "base/macros.h"
+#include "third_party/blink/renderer/platform/heap/thread_state.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/type_traits.h"
+
+namespace blink {
+
+template <typename T>
+class GarbageCollected;
+class HeapObjectHeader;
+
+// GC_PLUGIN_IGNORE is used to make the plugin ignore a particular class or
+// field when checking for proper usage. When using GC_PLUGIN_IGNORE
+// a bug-number should be provided as an argument where the bug describes
+// what needs to happen to remove the GC_PLUGIN_IGNORE again.
+#if defined(__clang__)
+#define GC_PLUGIN_IGNORE(bug) \
+ __attribute__((annotate("blink_gc_plugin_ignore")))
+#else
+#define GC_PLUGIN_IGNORE(bug)
+#endif
+
+// Template to determine if a class is a GarbageCollectedMixin by checking if it
+// has IsGarbageCollectedMixinMarker
+template <typename T>
+struct IsGarbageCollectedMixin {
+ private:
+ typedef char YesType;
+ struct NoType {
+ char padding[8];
+ };
+
+ template <typename U>
+ static YesType CheckMarker(typename U::IsGarbageCollectedMixinMarker*);
+ template <typename U>
+ static NoType CheckMarker(...);
+
+ public:
+ static const bool value = sizeof(CheckMarker<T>(nullptr)) == sizeof(YesType);
+};
+
+struct TraceDescriptor {
+ STACK_ALLOCATED();
+ void* base_object_payload;
+ TraceCallback callback;
+ bool can_trace_eagerly;
+};
+
+struct TraceWrapperDescriptor {
+ STACK_ALLOCATED();
+ void* base_object_payload;
+ TraceWrappersCallback trace_wrappers_callback;
+ MissedWriteBarrierCallback missed_write_barrier_callback;
+ NameCallback name_callback;
+};
+
+// The GarbageCollectedMixin interface and helper macro
+// USING_GARBAGE_COLLECTED_MIXIN can be used to automatically define
+// TraceTrait/ObjectAliveTrait on non-leftmost deriving classes
+// which need to be garbage collected.
+//
+// Consider the following case:
+// class B {};
+// class A : public GarbageCollected, public B {};
+//
+// We can't correctly handle "Member<B> p = &a" as we can't compute addr of
+// object header statically. This can be solved by using GarbageCollectedMixin:
+// class B : public GarbageCollectedMixin {};
+// class A : public GarbageCollected, public B {
+// USING_GARBAGE_COLLECTED_MIXIN(A);
+// };
+//
+// With the helper, as long as we are using Member<B>, TypeTrait<B> will
+// dispatch dynamically to retrieve the necessary tracing and header methods.
+// Note that this is only enabled for Member<B>. For Member<A> which we can
+// compute the necessary methods and pointers statically and this dynamic
+// dispatch is not used.
+class PLATFORM_EXPORT GarbageCollectedMixin {
+ public:
+ typedef int IsGarbageCollectedMixinMarker;
+ virtual void Trace(Visitor*) {}
+ // Provide default implementations that indicate that the vtable is not yet
+ // set up properly. This way it is possible to get infos about mixins so that
+ // these objects can processed later on. This is necessary as
+ // not-fully-constructed mixin objects potentially require being processed
+ // as part emitting a write barrier for incremental marking. See
+ // IncrementalMarkingTest::WriteBarrierDuringMixinConstruction as an example.
+ //
+ // The not-fully-constructed objects are handled as follows:
+ // 1. Write barrier or marking of not fully constructed mixin gets called.
+ // 2. Default implementation of GetTraceDescriptor (and friends) returns
+ // kNotFullyConstructedObject as object base payload.
+ // 3. Visitor (e.g. MarkingVisitor) can intercept that value and delay
+ // processing that object until the atomic pause.
+ // 4. In the atomic phase, mark all not-fully-constructed objects that have
+ // found in the step 1.-3. conservatively.
+ //
+ // In general, delaying is required as write barriers are omitted in certain
+ // scenarios, e.g., initializing stores. As a result, we cannot depend on the
+ // write barriers for catching writes to member fields and thus have to
+ // process the object (instead of just marking only the header).
+ virtual HeapObjectHeader* GetHeapObjectHeader() const {
+ return reinterpret_cast<HeapObjectHeader*>(
+ BlinkGC::kNotFullyConstructedObject);
+ }
+ virtual TraceDescriptor GetTraceDescriptor() const {
+ return {BlinkGC::kNotFullyConstructedObject, nullptr, false};
+ }
+ virtual TraceWrapperDescriptor GetTraceWrapperDescriptor() const {
+ return {BlinkGC::kNotFullyConstructedObject, nullptr, nullptr, nullptr};
+ }
+};
+
+#define DEFINE_GARBAGE_COLLECTED_MIXIN_METHODS(TYPE) \
+ public: \
+ HeapObjectHeader* GetHeapObjectHeader() const override { \
+ static_assert( \
+ WTF::IsSubclassOfTemplate<typename std::remove_const<TYPE>::type, \
+ blink::GarbageCollected>::value, \
+ "only garbage collected objects can have garbage collected mixins"); \
+ return HeapObjectHeader::FromPayload(static_cast<const TYPE*>(this)); \
+ } \
+ \
+ TraceDescriptor GetTraceDescriptor() const override { \
+ return {const_cast<TYPE*>(static_cast<const TYPE*>(this)), \
+ TraceTrait<TYPE>::Trace, TraceEagerlyTrait<TYPE>::value}; \
+ } \
+ \
+ TraceWrapperDescriptor GetTraceWrapperDescriptor() const override { \
+ return {const_cast<TYPE*>(static_cast<const TYPE*>(this)), \
+ TraceTrait<TYPE>::TraceWrappers, \
+ ScriptWrappableVisitor::MissedWriteBarrier<TYPE>, \
+ ScriptWrappableVisitor::NameCallback<TYPE>}; \
+ } \
+ \
+ private:
+
+// A C++ object's vptr will be initialized to its leftmost base's vtable after
+// the constructors of all its subclasses have run, so if a subclass constructor
+// tries to access any of the vtbl entries of its leftmost base prematurely,
+// it'll find an as-yet incorrect vptr and fail. Which is exactly what a
+// garbage collector will try to do if it tries to access the leftmost base
+// while one of the subclass constructors of a GC mixin object triggers a GC.
+// It is consequently not safe to allow any GCs while these objects are under
+// (sub constructor) construction.
+//
+// To prevent GCs in that restricted window of a mixin object's construction:
+//
+// - The initial allocation of the mixin object will enter a no GC scope.
+// This is done by overriding 'operator new' for mixin instances.
+// - When the constructor for the mixin is invoked, after all the
+// derived constructors have run, it will invoke the constructor
+// for a field whose only purpose is to leave the GC scope.
+// GarbageCollectedMixinConstructorMarker's constructor takes care of
+// this and the field is declared by way of USING_GARBAGE_COLLECTED_MIXIN().
+
+#define DEFINE_GARBAGE_COLLECTED_MIXIN_CONSTRUCTOR_MARKER(TYPE) \
+ public: \
+ GC_PLUGIN_IGNORE("crbug.com/456823") \
+ NO_SANITIZE_UNRELATED_CAST void* operator new(size_t size) { \
+ CHECK_GE(kLargeObjectSizeThreshold, size) \
+ << "GarbageCollectedMixin may not be a large object"; \
+ void* object = \
+ TYPE::AllocateObject(size, IsEagerlyFinalizedType<TYPE>::value); \
+ ThreadState* state = \
+ ThreadStateFor<ThreadingTrait<TYPE>::kAffinity>::GetState(); \
+ state->EnterGCForbiddenScopeIfNeeded( \
+ &(reinterpret_cast<TYPE*>(object)->mixin_constructor_marker_)); \
+ return object; \
+ } \
+ GarbageCollectedMixinConstructorMarker<ThreadingTrait<TYPE>::kAffinity> \
+ mixin_constructor_marker_; \
+ \
+ private:
+
+// Mixins that wrap/nest others requires extra handling:
+//
+// class A : public GarbageCollected<A>, public GarbageCollectedMixin {
+// USING_GARBAGE_COLLECTED_MIXIN(A);
+// ...
+// }'
+// public B final : public A, public SomeOtherMixinInterface {
+// USING_GARBAGE_COLLECTED_MIXIN(B);
+// ...
+// };
+//
+// The "operator new" for B will enter the forbidden GC scope, but
+// upon construction, two GarbageCollectedMixinConstructorMarker constructors
+// will run -- one for A (first) and another for B (secondly). Only
+// the second one should leave the forbidden GC scope. This is realized by
+// recording the address of B's GarbageCollectedMixinConstructorMarker
+// when the "operator new" for B runs, and leaving the forbidden GC scope
+// when the constructor of the recorded GarbageCollectedMixinConstructorMarker
+// runs.
+#define USING_GARBAGE_COLLECTED_MIXIN(TYPE) \
+ IS_GARBAGE_COLLECTED_TYPE(); \
+ DEFINE_GARBAGE_COLLECTED_MIXIN_METHODS(TYPE) \
+ DEFINE_GARBAGE_COLLECTED_MIXIN_CONSTRUCTOR_MARKER(TYPE)
+
+// An empty class with a constructor that's arranged invoked when all derived
+// constructors of a mixin instance have completed and it is safe to allow GCs
+// again. See AllocateObjectTrait<> comment for more.
+//
+// USING_GARBAGE_COLLECTED_MIXIN() declares a
+// GarbageCollectedMixinConstructorMarker<> private field. By following Blink
+// convention of using the macro at the top of a class declaration, its
+// constructor will run first.
+class GarbageCollectedMixinConstructorMarkerBase {};
+template <ThreadAffinity affinity>
+class GarbageCollectedMixinConstructorMarker
+ : public GarbageCollectedMixinConstructorMarkerBase {
+ public:
+ GarbageCollectedMixinConstructorMarker() {
+ // FIXME: if prompt conservative GCs are needed, forced GCs that
+ // were denied while within this scope, could now be performed.
+ // For now, assume the next out-of-line allocation request will
+ // happen soon enough and take care of it. Mixin objects aren't
+ // overly common.
+ ThreadState* state = ThreadStateFor<affinity>::GetState();
+ state->LeaveGCForbiddenScopeIfNeeded(this);
+ }
+};
+
+// Merge two or more Mixins into one:
+//
+// class A : public GarbageCollectedMixin {};
+// class B : public GarbageCollectedMixin {};
+// class C : public A, public B {
+// // C::GetTraceDescriptor is now ambiguous because there are two
+// candidates:
+// // A::GetTraceDescriptor and B::GetTraceDescriptor. Ditto for other
+// functions.
+//
+// MERGE_GARBAGE_COLLECTED_MIXINS();
+// // The macro defines C::GetTraceDescriptor, etc. so that they are no
+// longer
+// // ambiguous. USING_GARBAGE_COLLECTED_MIXIN(TYPE) overrides them later
+// // and provides the implementations.
+// };
+#define MERGE_GARBAGE_COLLECTED_MIXINS() \
+ public: \
+ HeapObjectHeader* GetHeapObjectHeader() const override = 0; \
+ TraceDescriptor GetTraceDescriptor() const override = 0; \
+ TraceWrapperDescriptor GetTraceWrapperDescriptor() const override = 0; \
+ \
+ private: \
+ using merge_garbage_collected_mixins_requires_semicolon = void
+
+// Base class for objects allocated in the Blink garbage-collected heap.
+//
+// Defines a 'new' operator that allocates the memory in the heap. 'delete'
+// should not be called on objects that inherit from GarbageCollected.
+//
+// Instances of GarbageCollected will *NOT* get finalized. Their destructor
+// will not be called. Therefore, only classes that have trivial destructors
+// with no semantic meaning (including all their subclasses) should inherit from
+// GarbageCollected. If there are non-trival destructors in a given class or
+// any of its subclasses, GarbageCollectedFinalized should be used which
+// guarantees that the destructor is called on an instance when the garbage
+// collector determines that it is no longer reachable.
+template <typename T>
+class GarbageCollected;
+
+// Base class for objects allocated in the Blink garbage-collected heap.
+//
+// Defines a 'new' operator that allocates the memory in the heap. 'delete'
+// should not be called on objects that inherit from GarbageCollected.
+//
+// Instances of GarbageCollectedFinalized will have their destructor called when
+// the garbage collector determines that the object is no longer reachable.
+template <typename T>
+class GarbageCollectedFinalized : public GarbageCollected<T> {
+ protected:
+ // finalizeGarbageCollectedObject is called when the object is freed from
+ // the heap. By default finalization means calling the destructor on the
+ // object. finalizeGarbageCollectedObject can be overridden to support
+ // calling the destructor of a subclass. This is useful for objects without
+ // vtables that require explicit dispatching. The name is intentionally a
+ // bit long to make name conflicts less likely.
+ void FinalizeGarbageCollectedObject() { static_cast<T*>(this)->~T(); }
+
+ GarbageCollectedFinalized() = default;
+ ~GarbageCollectedFinalized() = default;
+
+ template <typename U>
+ friend struct HasFinalizer;
+ template <typename U, bool>
+ friend struct FinalizerTraitImpl;
+
+ DISALLOW_COPY_AND_ASSIGN(GarbageCollectedFinalized);
+};
+
+template <typename T,
+ bool = WTF::IsSubclassOfTemplate<typename std::remove_const<T>::type,
+ GarbageCollected>::value>
+class NeedsAdjustPointer;
+
+template <typename T>
+class NeedsAdjustPointer<T, true> {
+ static_assert(sizeof(T), "T must be fully defined");
+
+ public:
+ static const bool value = false;
+};
+
+template <typename T>
+class NeedsAdjustPointer<T, false> {
+ static_assert(sizeof(T), "T must be fully defined");
+
+ public:
+ static const bool value =
+ IsGarbageCollectedMixin<typename std::remove_const<T>::type>::value;
+};
+
+// TODO(sof): migrate to wtf/TypeTraits.h
+template <typename T>
+class IsFullyDefined {
+ using TrueType = char;
+ struct FalseType {
+ char dummy[2];
+ };
+
+ template <typename U, size_t sz = sizeof(U)>
+ static TrueType IsSizeofKnown(U*);
+ static FalseType IsSizeofKnown(...);
+ static T& t_;
+
+ public:
+ static const bool value = sizeof(TrueType) == sizeof(IsSizeofKnown(&t_));
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/heap/gc_info.cc b/chromium/third_party/blink/renderer/platform/heap/gc_info.cc
new file mode 100644
index 00000000000..10573e6dcde
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/gc_info.cc
@@ -0,0 +1,79 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/heap/gc_info.h"
+
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
+
+namespace blink {
+
+// GCInfo indices start from 1 for heap objects, with 0 being treated
+// specially as the index for freelist entries and large heap objects.
+size_t GCInfoTable::gc_info_index_ = 0;
+
+size_t GCInfoTable::gc_info_table_size_ = 0;
+GCInfo const** g_gc_info_table = nullptr;
+
+void GCInfoTable::EnsureGCInfoIndex(const GCInfo* gc_info,
+ size_t* gc_info_index_slot) {
+ DCHECK(gc_info);
+ DCHECK(gc_info_index_slot);
+ // Keep a global GCInfoTable lock while allocating a new slot.
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(Mutex, mutex, ());
+ MutexLocker locker(mutex);
+
+ // If more than one thread ends up allocating a slot for
+ // the same GCInfo, have later threads reuse the slot
+ // allocated by the first.
+ if (*gc_info_index_slot)
+ return;
+
+ int index = ++gc_info_index_;
+ size_t gc_info_index = static_cast<size_t>(index);
+ CHECK(gc_info_index < GCInfoTable::kMaxIndex);
+ if (gc_info_index >= gc_info_table_size_)
+ Resize();
+
+ g_gc_info_table[gc_info_index] = gc_info;
+ ReleaseStore(reinterpret_cast<int*>(gc_info_index_slot), index);
+}
+
+void GCInfoTable::Resize() {
+ static const int kGcInfoZapValue = 0x33;
+ // (Light) experimentation suggests that Blink doesn't need
+ // more than this while handling content on popular web properties.
+ const size_t kInitialSize = 512;
+
+ size_t new_size =
+ gc_info_table_size_ ? 2 * gc_info_table_size_ : kInitialSize;
+ DCHECK(new_size < GCInfoTable::kMaxIndex);
+ g_gc_info_table =
+ reinterpret_cast<GCInfo const**>(WTF::Partitions::FastRealloc(
+ g_gc_info_table, new_size * sizeof(GCInfo), "GCInfo"));
+ DCHECK(g_gc_info_table);
+ memset(reinterpret_cast<uint8_t*>(g_gc_info_table) +
+ gc_info_table_size_ * sizeof(GCInfo),
+ kGcInfoZapValue, (new_size - gc_info_table_size_) * sizeof(GCInfo));
+ gc_info_table_size_ = new_size;
+}
+
+void GCInfoTable::Init() {
+ CHECK(!g_gc_info_table);
+ Resize();
+}
+
+#if DCHECK_IS_ON()
+void AssertObjectHasGCInfo(const void* payload, size_t gc_info_index) {
+ HeapObjectHeader::CheckFromPayload(payload);
+#if !defined(COMPONENT_BUILD)
+ // On component builds we cannot compare the gcInfos as they are statically
+ // defined in each of the components and hence will not match.
+ DCHECK_EQ(HeapObjectHeader::FromPayload(payload)->GcInfoIndex(),
+ gc_info_index);
+#endif
+}
+#endif
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/heap/gc_info.h b/chromium/third_party/blink/renderer/platform/heap/gc_info.h
new file mode 100644
index 00000000000..adbbf23d6c7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/gc_info.h
@@ -0,0 +1,281 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_GC_INFO_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_GC_INFO_H_
+
+#include "third_party/blink/renderer/platform/heap/visitor.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/atomics.h"
+#include "third_party/blink/renderer/platform/wtf/deque.h"
+#include "third_party/blink/renderer/platform/wtf/hash_counted_set.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/hash_table.h"
+#include "third_party/blink/renderer/platform/wtf/linked_hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/list_hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/type_traits.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+// The FinalizerTraitImpl specifies how to finalize objects. Objects that
+// inherit from GarbageCollectedFinalized are finalized by calling their
+// |Finalize| method which by default will call the destructor on the object.
+template <typename T, bool isGarbageCollectedFinalized>
+struct FinalizerTraitImpl;
+
+template <typename T>
+struct FinalizerTraitImpl<T, true> {
+ STATIC_ONLY(FinalizerTraitImpl);
+ static void Finalize(void* obj) {
+ static_assert(sizeof(T), "T must be fully defined");
+ static_cast<T*>(obj)->FinalizeGarbageCollectedObject();
+ };
+};
+
+template <typename T>
+struct FinalizerTraitImpl<T, false> {
+ STATIC_ONLY(FinalizerTraitImpl);
+ static void Finalize(void* obj) {
+ static_assert(sizeof(T), "T must be fully defined");
+ };
+};
+
+// The FinalizerTrait is used to determine if a type requires finalization and
+// what finalization means.
+//
+// By default classes that inherit from GarbageCollectedFinalized need
+// finalization and finalization means calling the |Finalize| method of the
+// object. The FinalizerTrait can be specialized if the default behavior is not
+// desired.
+template <typename T>
+struct FinalizerTrait {
+ STATIC_ONLY(FinalizerTrait);
+ static const bool kNonTrivialFinalizer =
+ WTF::IsSubclassOfTemplate<typename std::remove_const<T>::type,
+ GarbageCollectedFinalized>::value;
+ static void Finalize(void* obj) {
+ FinalizerTraitImpl<T, kNonTrivialFinalizer>::Finalize(obj);
+ }
+};
+
+class HeapAllocator;
+template <typename ValueArg, size_t inlineCapacity>
+class HeapListHashSetAllocator;
+template <typename T, typename Traits>
+class HeapVectorBacking;
+template <typename Table>
+class HeapHashTableBacking;
+
+template <typename T, typename U, typename V>
+struct FinalizerTrait<LinkedHashSet<T, U, V, HeapAllocator>> {
+ STATIC_ONLY(FinalizerTrait);
+ static const bool kNonTrivialFinalizer = true;
+ static void Finalize(void* obj) {
+ FinalizerTraitImpl<LinkedHashSet<T, U, V, HeapAllocator>,
+ kNonTrivialFinalizer>::Finalize(obj);
+ }
+};
+
+template <typename T, typename Allocator>
+struct FinalizerTrait<WTF::ListHashSetNode<T, Allocator>> {
+ STATIC_ONLY(FinalizerTrait);
+ static const bool kNonTrivialFinalizer =
+ !WTF::IsTriviallyDestructible<T>::value;
+ static void Finalize(void* obj) {
+ FinalizerTraitImpl<WTF::ListHashSetNode<T, Allocator>,
+ kNonTrivialFinalizer>::Finalize(obj);
+ }
+};
+
+template <typename T, size_t inlineCapacity>
+struct FinalizerTrait<Vector<T, inlineCapacity, HeapAllocator>> {
+ STATIC_ONLY(FinalizerTrait);
+ static const bool kNonTrivialFinalizer =
+ inlineCapacity && VectorTraits<T>::kNeedsDestruction;
+ static void Finalize(void* obj) {
+ FinalizerTraitImpl<Vector<T, inlineCapacity, HeapAllocator>,
+ kNonTrivialFinalizer>::Finalize(obj);
+ }
+};
+
+template <typename T, size_t inlineCapacity>
+struct FinalizerTrait<Deque<T, inlineCapacity, HeapAllocator>> {
+ STATIC_ONLY(FinalizerTrait);
+ static const bool kNonTrivialFinalizer =
+ inlineCapacity && VectorTraits<T>::kNeedsDestruction;
+ static void Finalize(void* obj) {
+ FinalizerTraitImpl<Deque<T, inlineCapacity, HeapAllocator>,
+ kNonTrivialFinalizer>::Finalize(obj);
+ }
+};
+
+template <typename Table>
+struct FinalizerTrait<HeapHashTableBacking<Table>> {
+ STATIC_ONLY(FinalizerTrait);
+ static const bool kNonTrivialFinalizer =
+ !WTF::IsTriviallyDestructible<typename Table::ValueType>::value;
+ static void Finalize(void* obj) {
+ FinalizerTraitImpl<HeapHashTableBacking<Table>,
+ kNonTrivialFinalizer>::Finalize(obj);
+ }
+};
+
+template <typename T, typename Traits>
+struct FinalizerTrait<HeapVectorBacking<T, Traits>> {
+ STATIC_ONLY(FinalizerTrait);
+ static const bool kNonTrivialFinalizer = Traits::kNeedsDestruction;
+ static void Finalize(void* obj) {
+ FinalizerTraitImpl<HeapVectorBacking<T, Traits>,
+ kNonTrivialFinalizer>::Finalize(obj);
+ }
+};
+
+// GCInfo contains meta-data associated with object classes allocated in the
+// Blink heap. This meta-data consists of a function pointer used to trace the
+// pointers in the class instance during garbage collection, an indication of
+// whether or not the instance needs a finalization callback, and a function
+// pointer used to finalize the instance when the garbage collector determines
+// that the instance is no longer reachable. There is a GCInfo struct for each
+// class that directly inherits from GarbageCollected or
+// GarbageCollectedFinalized.
+struct GCInfo {
+ bool HasFinalizer() const { return non_trivial_finalizer_; }
+ bool HasVTable() const { return has_v_table_; }
+ TraceCallback trace_;
+ FinalizationCallback finalize_;
+ bool non_trivial_finalizer_;
+ bool has_v_table_;
+};
+
+// s_gcInfoTable holds the per-class GCInfo descriptors; each HeapObjectHeader
+// keeps an index into this table.
+extern PLATFORM_EXPORT GCInfo const** g_gc_info_table;
+
+#if DCHECK_IS_ON()
+PLATFORM_EXPORT void AssertObjectHasGCInfo(const void*, size_t gc_info_index);
+#endif
+
+class GCInfoTable {
+ STATIC_ONLY(GCInfoTable);
+
+ public:
+ PLATFORM_EXPORT static void EnsureGCInfoIndex(const GCInfo*, size_t*);
+
+ static void Init();
+
+ static size_t GcInfoIndex() { return gc_info_index_; }
+
+ // The (max + 1) GCInfo index supported.
+ //
+ // We assume that 14 bits is enough to represent all possible types: during
+ // telemetry runs, we see about 1,000 different types; looking at the output
+ // of the Oilpan GC Clang plugin, there appear to be at most about 6,000
+ // types. Thus 14 bits should be more than twice as many bits as we will ever
+ // need.
+ static const size_t kMaxIndex = 1 << 14;
+
+ private:
+ static void Resize();
+
+ static size_t gc_info_index_;
+ static size_t gc_info_table_size_;
+};
+
+// GCInfoAtBaseType should be used when returning a unique 14 bit integer
+// for a given gcInfo.
+template <typename T>
+struct GCInfoAtBaseType {
+ STATIC_ONLY(GCInfoAtBaseType);
+ static size_t Index() {
+ static_assert(sizeof(T), "T must be fully defined");
+ static const GCInfo kGcInfo = {
+ TraceTrait<T>::Trace, FinalizerTrait<T>::Finalize,
+ FinalizerTrait<T>::kNonTrivialFinalizer, std::is_polymorphic<T>::value,
+ };
+
+ static size_t gc_info_index = 0;
+ DCHECK(g_gc_info_table);
+ if (!AcquireLoad(&gc_info_index))
+ GCInfoTable::EnsureGCInfoIndex(&kGcInfo, &gc_info_index);
+ DCHECK_GE(gc_info_index, 1u);
+ DCHECK(gc_info_index < GCInfoTable::kMaxIndex);
+ return gc_info_index;
+ }
+};
+
+template <typename T,
+ bool = WTF::IsSubclassOfTemplate<typename std::remove_const<T>::type,
+ GarbageCollected>::value>
+struct GetGarbageCollectedType;
+
+template <typename T>
+struct GetGarbageCollectedType<T, true> {
+ STATIC_ONLY(GetGarbageCollectedType);
+ using type = typename T::GarbageCollectedType;
+};
+
+template <typename T>
+struct GetGarbageCollectedType<T, false> {
+ STATIC_ONLY(GetGarbageCollectedType);
+ using type = T;
+};
+
+template <typename T>
+struct GCInfoTrait {
+ STATIC_ONLY(GCInfoTrait);
+ static size_t Index() {
+ return GCInfoAtBaseType<typename GetGarbageCollectedType<T>::type>::Index();
+ }
+};
+
+template <typename U>
+class GCInfoTrait<const U> : public GCInfoTrait<U> {};
+
+template <typename T, typename U, typename V, typename W, typename X>
+class HeapHashMap;
+template <typename T, typename U, typename V>
+class HeapHashSet;
+template <typename T, typename U, typename V>
+class HeapLinkedHashSet;
+template <typename T, size_t inlineCapacity, typename U>
+class HeapListHashSet;
+template <typename T, size_t inlineCapacity>
+class HeapVector;
+template <typename T, size_t inlineCapacity>
+class HeapDeque;
+template <typename T, typename U, typename V>
+class HeapHashCountedSet;
+
+template <typename T, typename U, typename V, typename W, typename X>
+struct GCInfoTrait<HeapHashMap<T, U, V, W, X>>
+ : public GCInfoTrait<HashMap<T, U, V, W, X, HeapAllocator>> {};
+template <typename T, typename U, typename V>
+struct GCInfoTrait<HeapHashSet<T, U, V>>
+ : public GCInfoTrait<HashSet<T, U, V, HeapAllocator>> {};
+template <typename T, typename U, typename V>
+struct GCInfoTrait<HeapLinkedHashSet<T, U, V>>
+ : public GCInfoTrait<LinkedHashSet<T, U, V, HeapAllocator>> {};
+template <typename T, size_t inlineCapacity, typename U>
+struct GCInfoTrait<HeapListHashSet<T, inlineCapacity, U>>
+ : public GCInfoTrait<
+ ListHashSet<T,
+ inlineCapacity,
+ U,
+ HeapListHashSetAllocator<T, inlineCapacity>>> {};
+template <typename T, size_t inlineCapacity>
+struct GCInfoTrait<HeapVector<T, inlineCapacity>>
+ : public GCInfoTrait<Vector<T, inlineCapacity, HeapAllocator>> {};
+template <typename T, size_t inlineCapacity>
+struct GCInfoTrait<HeapDeque<T, inlineCapacity>>
+ : public GCInfoTrait<Deque<T, inlineCapacity, HeapAllocator>> {};
+template <typename T, typename U, typename V>
+struct GCInfoTrait<HeapHashCountedSet<T, U, V>>
+ : public GCInfoTrait<HashCountedSet<T, U, V, HeapAllocator>> {};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/heap/gc_task_runner.h b/chromium/third_party/blink/renderer/platform/heap/gc_task_runner.h
new file mode 100644
index 00000000000..b8d92cbc1a8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/gc_task_runner.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2014 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_GC_TASK_RUNNER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_GC_TASK_RUNNER_H_
+
+#include <memory>
+#include "base/location.h"
+#include "third_party/blink/public/platform/web_thread.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/heap/thread_state.h"
+
+namespace blink {
+
+class GCTaskObserver final : public WebThread::TaskObserver {
+ USING_FAST_MALLOC(GCTaskObserver);
+
+ public:
+ GCTaskObserver() : nesting_(0) {}
+
+ ~GCTaskObserver() {
+ // m_nesting can be 1 if this was unregistered in a task and
+ // didProcessTask was not called.
+ DCHECK(!nesting_ || nesting_ == 1);
+ }
+
+ virtual void WillProcessTask() { nesting_++; }
+
+ virtual void DidProcessTask() {
+ // In the production code WebKit::initialize is called from inside the
+ // message loop so we can get didProcessTask() without corresponding
+ // willProcessTask once. This is benign.
+ if (nesting_)
+ nesting_--;
+
+ ThreadState::Current()->SafePoint(nesting_
+ ? BlinkGC::kHeapPointersOnStack
+ : BlinkGC::kNoHeapPointersOnStack);
+ }
+
+ private:
+ int nesting_;
+};
+
+class GCTaskRunner final {
+ USING_FAST_MALLOC(GCTaskRunner);
+
+ public:
+ explicit GCTaskRunner(WebThread* thread)
+ : gc_task_observer_(std::make_unique<GCTaskObserver>()), thread_(thread) {
+ thread_->AddTaskObserver(gc_task_observer_.get());
+ }
+
+ ~GCTaskRunner() { thread_->RemoveTaskObserver(gc_task_observer_.get()); }
+
+ private:
+ std::unique_ptr<GCTaskObserver> gc_task_observer_;
+ WebThread* thread_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/heap/handle.h b/chromium/third_party/blink/renderer/platform/heap/handle.h
new file mode 100644
index 00000000000..5267f2de1f9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/handle.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2014 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HANDLE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HANDLE_H_
+
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/heap/heap_allocator.h"
+#include "third_party/blink/renderer/platform/heap/heap_traits.h"
+#include "third_party/blink/renderer/platform/heap/member.h"
+#include "third_party/blink/renderer/platform/heap/persistent.h"
+#include "third_party/blink/renderer/platform/heap/thread_state.h"
+#include "third_party/blink/renderer/platform/heap/trace_traits.h"
+#include "third_party/blink/renderer/platform/heap/visitor.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+#if defined(LEAK_SANITIZER)
+#include "third_party/blink/renderer/platform/wtf/leak_annotations.h"
+#endif
+
+namespace blink {
+
+// LEAK_SANITIZER_DISABLED_SCOPE: all allocations made in the current scope
+// will be exempted from LSan consideration.
+//
+// TODO(sof): move this to wtf/LeakAnnotations.h (LeakSanitizer.h?) once
+// wtf/ can freely call upon Oilpan functionality.
+#if defined(LEAK_SANITIZER)
+class LeakSanitizerDisableScope {
+ STACK_ALLOCATED();
+ WTF_MAKE_NONCOPYABLE(LeakSanitizerDisableScope);
+
+ public:
+ LeakSanitizerDisableScope() {
+ __lsan_disable();
+ if (ThreadState::Current())
+ ThreadState::Current()->enterStaticReferenceRegistrationDisabledScope();
+ }
+
+ ~LeakSanitizerDisableScope() {
+ __lsan_enable();
+ if (ThreadState::Current())
+ ThreadState::Current()->leaveStaticReferenceRegistrationDisabledScope();
+ }
+};
+#define LEAK_SANITIZER_DISABLED_SCOPE \
+ LeakSanitizerDisableScope lsanDisabledScope
+#else
+#define LEAK_SANITIZER_DISABLED_SCOPE
+#endif
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/heap/heap.cc b/chromium/third_party/blink/renderer/platform/heap/heap.cc
new file mode 100644
index 00000000000..f4509a7e93b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/heap.cc
@@ -0,0 +1,765 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/renderer/platform/heap/heap.h"
+
+#include <algorithm>
+#include <limits>
+#include <memory>
+
+#include "base/trace_event/process_memory_dump.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h"
+#include "third_party/blink/renderer/platform/heap/blink_gc_memory_dump_provider.h"
+#include "third_party/blink/renderer/platform/heap/heap_compact.h"
+#include "third_party/blink/renderer/platform/heap/marking_visitor.h"
+#include "third_party/blink/renderer/platform/heap/page_memory.h"
+#include "third_party/blink/renderer/platform/heap/page_pool.h"
+#include "third_party/blink/renderer/platform/heap/safe_point.h"
+#include "third_party/blink/renderer/platform/heap/thread_state.h"
+#include "third_party/blink/renderer/platform/histogram.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/web_memory_allocator_dump.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/web_process_memory_dump.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/leak_annotations.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+HeapAllocHooks::AllocationHook* HeapAllocHooks::allocation_hook_ = nullptr;
+HeapAllocHooks::FreeHook* HeapAllocHooks::free_hook_ = nullptr;
+
+void ThreadHeap::FlushHeapDoesNotContainCache() {
+ heap_does_not_contain_cache_->Flush();
+}
+
+ThreadHeapStats::ThreadHeapStats()
+ : allocated_space_(0),
+ allocated_object_size_(0),
+ object_size_at_last_gc_(0),
+ marked_object_size_(0),
+ marked_object_size_at_last_complete_sweep_(0),
+ wrapper_count_(0),
+ wrapper_count_at_last_gc_(0),
+ collected_wrapper_count_(0),
+ partition_alloc_size_at_last_gc_(
+ WTF::Partitions::TotalSizeOfCommittedPages()),
+ estimated_marking_time_per_byte_(0.0) {}
+
+double ThreadHeapStats::EstimatedMarkingTime() {
+ // Use 8 ms as initial estimated marking time.
+ // 8 ms is long enough for low-end mobile devices to mark common
+ // real-world object graphs.
+ if (estimated_marking_time_per_byte_ == 0)
+ return 0.008;
+
+ // Assuming that the collection rate of this GC will be mostly equal to
+ // the collection rate of the last GC, estimate the marking time of this GC.
+ return estimated_marking_time_per_byte_ *
+ (AllocatedObjectSize() + MarkedObjectSize());
+}
+
+void ThreadHeapStats::Reset() {
+ object_size_at_last_gc_ = allocated_object_size_ + marked_object_size_;
+ partition_alloc_size_at_last_gc_ =
+ WTF::Partitions::TotalSizeOfCommittedPages();
+ allocated_object_size_ = 0;
+ marked_object_size_ = 0;
+ wrapper_count_at_last_gc_ = wrapper_count_;
+ collected_wrapper_count_ = 0;
+}
+
+void ThreadHeapStats::IncreaseAllocatedObjectSize(size_t delta) {
+ allocated_object_size_ += delta;
+ ProcessHeap::IncreaseTotalAllocatedObjectSize(delta);
+}
+
+void ThreadHeapStats::DecreaseAllocatedObjectSize(size_t delta) {
+ allocated_object_size_ -= delta;
+ ProcessHeap::DecreaseTotalAllocatedObjectSize(delta);
+}
+
+void ThreadHeapStats::IncreaseMarkedObjectSize(size_t delta) {
+ marked_object_size_ += delta;
+ ProcessHeap::IncreaseTotalMarkedObjectSize(delta);
+}
+
+void ThreadHeapStats::IncreaseAllocatedSpace(size_t delta) {
+ allocated_space_ += delta;
+ ProcessHeap::IncreaseTotalAllocatedSpace(delta);
+}
+
+void ThreadHeapStats::DecreaseAllocatedSpace(size_t delta) {
+ allocated_space_ -= delta;
+ ProcessHeap::DecreaseTotalAllocatedSpace(delta);
+}
+
+double ThreadHeapStats::LiveObjectRateSinceLastGC() const {
+ if (ObjectSizeAtLastGC() > 0)
+ return static_cast<double>(MarkedObjectSize()) / ObjectSizeAtLastGC();
+ return 0.0;
+}
+
+ThreadHeap::ThreadHeap(ThreadState* thread_state)
+ : thread_state_(thread_state),
+ region_tree_(std::make_unique<RegionTree>()),
+ heap_does_not_contain_cache_(std::make_unique<HeapDoesNotContainCache>()),
+ free_page_pool_(std::make_unique<PagePool>()),
+ marking_worklist_(nullptr),
+ not_fully_constructed_worklist_(nullptr),
+ weak_callback_worklist_(nullptr),
+ vector_backing_arena_index_(BlinkGC::kVector1ArenaIndex),
+ current_arena_ages_(0),
+ should_flush_heap_does_not_contain_cache_(false) {
+ if (ThreadState::Current()->IsMainThread())
+ main_thread_heap_ = this;
+
+ for (int arena_index = 0; arena_index < BlinkGC::kLargeObjectArenaIndex;
+ arena_index++)
+ arenas_[arena_index] = new NormalPageArena(thread_state_, arena_index);
+ arenas_[BlinkGC::kLargeObjectArenaIndex] =
+ new LargeObjectArena(thread_state_, BlinkGC::kLargeObjectArenaIndex);
+
+ likely_to_be_promptly_freed_ =
+ std::make_unique<int[]>(kLikelyToBePromptlyFreedArraySize);
+ ClearArenaAges();
+}
+
+ThreadHeap::~ThreadHeap() {
+ for (int i = 0; i < BlinkGC::kNumberOfArenas; ++i)
+ delete arenas_[i];
+}
+
+Address ThreadHeap::CheckAndMarkPointer(MarkingVisitor* visitor,
+ Address address) {
+ DCHECK(thread_state_->InAtomicMarkingPause());
+
+#if !DCHECK_IS_ON()
+ if (heap_does_not_contain_cache_->Lookup(address))
+ return nullptr;
+#endif
+
+ if (BasePage* page = LookupPageForAddress(address)) {
+#if DCHECK_IS_ON()
+ DCHECK(page->Contains(address));
+#endif
+ DCHECK(!heap_does_not_contain_cache_->Lookup(address));
+ DCHECK(&visitor->Heap() == &page->Arena()->GetThreadState()->Heap());
+ visitor->ConservativelyMarkAddress(page, address);
+ return address;
+ }
+
+#if !DCHECK_IS_ON()
+ heap_does_not_contain_cache_->AddEntry(address);
+#else
+ if (!heap_does_not_contain_cache_->Lookup(address))
+ heap_does_not_contain_cache_->AddEntry(address);
+#endif
+ return nullptr;
+}
+
+#if DCHECK_IS_ON()
+// To support unit testing of the marking of off-heap root references
+// into the heap, provide a checkAndMarkPointer() version with an
+// extra notification argument.
+Address ThreadHeap::CheckAndMarkPointer(
+ MarkingVisitor* visitor,
+ Address address,
+ MarkedPointerCallbackForTesting callback) {
+ DCHECK(thread_state_->InAtomicMarkingPause());
+
+ if (BasePage* page = LookupPageForAddress(address)) {
+ DCHECK(page->Contains(address));
+ DCHECK(!heap_does_not_contain_cache_->Lookup(address));
+ DCHECK(&visitor->Heap() == &page->Arena()->GetThreadState()->Heap());
+ visitor->ConservativelyMarkAddress(page, address, callback);
+ return address;
+ }
+ if (!heap_does_not_contain_cache_->Lookup(address))
+ heap_does_not_contain_cache_->AddEntry(address);
+ return nullptr;
+}
+#endif // DCHECK_IS_ON()
+
+void ThreadHeap::RegisterWeakTable(void* table,
+ EphemeronCallback iteration_callback) {
+ DCHECK(thread_state_->InAtomicMarkingPause());
+#if DCHECK_IS_ON()
+ auto result = ephemeron_callbacks_.insert(table, iteration_callback);
+ DCHECK(result.is_new_entry ||
+ result.stored_value->value == iteration_callback);
+#else
+ ephemeron_callbacks_.insert(table, iteration_callback);
+#endif // DCHECK_IS_ON()
+}
+
+void ThreadHeap::CommitCallbackStacks() {
+ marking_worklist_.reset(new MarkingWorklist());
+ not_fully_constructed_worklist_.reset(new NotFullyConstructedWorklist());
+ weak_callback_worklist_.reset(new WeakCallbackWorklist());
+ DCHECK(ephemeron_callbacks_.IsEmpty());
+}
+
+void ThreadHeap::DecommitCallbackStacks() {
+ marking_worklist_.reset(nullptr);
+ not_fully_constructed_worklist_.reset(nullptr);
+ weak_callback_worklist_.reset(nullptr);
+ ephemeron_callbacks_.clear();
+}
+
+HeapCompact* ThreadHeap::Compaction() {
+ if (!compaction_)
+ compaction_ = HeapCompact::Create();
+ return compaction_.get();
+}
+
+void ThreadHeap::RegisterMovingObjectReference(MovableReference* slot) {
+ DCHECK(slot);
+ DCHECK(*slot);
+ Compaction()->RegisterMovingObjectReference(slot);
+}
+
+void ThreadHeap::RegisterMovingObjectCallback(MovableReference reference,
+ MovingObjectCallback callback,
+ void* callback_data) {
+ DCHECK(reference);
+ Compaction()->RegisterMovingObjectCallback(reference, callback,
+ callback_data);
+}
+
+void ThreadHeap::ProcessMarkingStack(Visitor* visitor) {
+ bool complete = AdvanceMarkingStackProcessing(
+ visitor, std::numeric_limits<double>::infinity());
+ CHECK(complete);
+}
+
+void ThreadHeap::MarkNotFullyConstructedObjects(Visitor* visitor) {
+ TRACE_EVENT0("blink_gc", "ThreadHeap::MarkNotFullyConstructedObjects");
+ DCHECK(!thread_state_->IsIncrementalMarking());
+
+ NotFullyConstructedItem item;
+ while (
+ not_fully_constructed_worklist_->Pop(WorklistTaskId::MainThread, &item)) {
+ BasePage* const page = PageFromObject(item);
+ reinterpret_cast<MarkingVisitor*>(visitor)->ConservativelyMarkAddress(
+ page, reinterpret_cast<Address>(item));
+ }
+}
+
+void ThreadHeap::InvokeEphemeronCallbacks(Visitor* visitor) {
+ // Mark any strong pointers that have now become reachable in ephemeron maps.
+ TRACE_EVENT0("blink_gc", "ThreadHeap::InvokeEphemeronCallbacks");
+
+ // Avoid supporting a subtle scheme that allows insertion while iterating
+ // by just creating temporary lists for iteration and sinking.
+ WTF::HashMap<void*, EphemeronCallback> iteration_set;
+ WTF::HashMap<void*, EphemeronCallback> final_set;
+
+ bool found_new = false;
+ do {
+ iteration_set = std::move(ephemeron_callbacks_);
+ ephemeron_callbacks_.clear();
+ for (auto& tuple : iteration_set) {
+ final_set.insert(tuple.key, tuple.value);
+ tuple.value(visitor, tuple.key);
+ }
+ found_new = !ephemeron_callbacks_.IsEmpty();
+ } while (found_new);
+ ephemeron_callbacks_ = std::move(final_set);
+}
+
+bool ThreadHeap::AdvanceMarkingStackProcessing(Visitor* visitor,
+ double deadline_seconds) {
+ const size_t kDeadlineCheckInterval = 2500;
+ size_t processed_callback_count = 0;
+ // Ephemeron fixed point loop.
+ do {
+ {
+ // Iteratively mark all objects that are reachable from the objects
+ // currently pushed onto the marking stack.
+ TRACE_EVENT0("blink_gc", "ThreadHeap::processMarkingStackSingleThreaded");
+ MarkingItem item;
+ while (marking_worklist_->Pop(WorklistTaskId::MainThread, &item)) {
+ item.callback(visitor, item.object);
+ processed_callback_count++;
+ if (processed_callback_count % kDeadlineCheckInterval == 0) {
+ if (deadline_seconds <= CurrentTimeTicksInSeconds()) {
+ return false;
+ }
+ }
+ }
+ }
+
+ InvokeEphemeronCallbacks(visitor);
+
+ // Rerun loop if ephemeron processing queued more objects for tracing.
+ } while (!marking_worklist_->IsGlobalEmpty());
+ return true;
+}
+
+void ThreadHeap::WeakProcessing(Visitor* visitor) {
+ TRACE_EVENT0("blink_gc", "ThreadHeap::WeakProcessing");
+ double start_time = WTF::CurrentTimeTicksInMilliseconds();
+
+ // Weak processing may access unmarked objects but are forbidden from
+ // ressurecting them.
+ ThreadState::ObjectResurrectionForbiddenScope object_resurrection_forbidden(
+ ThreadState::Current());
+
+ // Call weak callbacks on objects that may now be pointing to dead objects.
+ CustomCallbackItem item;
+ while (weak_callback_worklist_->Pop(WorklistTaskId::MainThread, &item)) {
+ item.callback(visitor, item.object);
+ }
+ // Weak callbacks should not add any new objects for marking.
+ DCHECK(marking_worklist_->IsGlobalEmpty());
+
+ double time_for_weak_processing =
+ WTF::CurrentTimeTicksInMilliseconds() - start_time;
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(
+ CustomCountHistogram, weak_processing_time_histogram,
+ ("BlinkGC.TimeForGlobalWeakProcessing", 1, 10 * 1000, 50));
+ weak_processing_time_histogram.Count(time_for_weak_processing);
+}
+
+void ThreadHeap::VerifyMarking() {
+ for (int i = 0; i < BlinkGC::kNumberOfArenas; ++i) {
+ arenas_[i]->VerifyMarking();
+ }
+}
+
+void ThreadHeap::ReportMemoryUsageHistogram() {
+ static size_t supported_max_size_in_mb = 4 * 1024;
+ static size_t observed_max_size_in_mb = 0;
+
+ // We only report the memory in the main thread.
+ if (!IsMainThread())
+ return;
+ // +1 is for rounding up the sizeInMB.
+ size_t size_in_mb =
+ ThreadState::Current()->Heap().HeapStats().AllocatedSpace() / 1024 /
+ 1024 +
+ 1;
+ if (size_in_mb >= supported_max_size_in_mb)
+ size_in_mb = supported_max_size_in_mb - 1;
+ if (size_in_mb > observed_max_size_in_mb) {
+ // Send a UseCounter only when we see the highest memory usage
+ // we've ever seen.
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(
+ EnumerationHistogram, commited_size_histogram,
+ ("BlinkGC.CommittedSize", supported_max_size_in_mb));
+ commited_size_histogram.Count(size_in_mb);
+ observed_max_size_in_mb = size_in_mb;
+ }
+}
+
+void ThreadHeap::ReportMemoryUsageForTracing() {
+ bool gc_tracing_enabled;
+ TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("blink_gc"),
+ &gc_tracing_enabled);
+ if (!gc_tracing_enabled)
+ return;
+
+ ThreadHeap& heap = ThreadState::Current()->Heap();
+ // These values are divided by 1024 to avoid overflow in practical cases
+ // (TRACE_COUNTER values are 32-bit ints).
+ // They are capped to INT_MAX just in case.
+ TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink_gc"),
+ "ThreadHeap::allocatedObjectSizeKB",
+ std::min(heap.HeapStats().AllocatedObjectSize() / 1024,
+ static_cast<size_t>(INT_MAX)));
+ TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink_gc"),
+ "ThreadHeap::markedObjectSizeKB",
+ std::min(heap.HeapStats().MarkedObjectSize() / 1024,
+ static_cast<size_t>(INT_MAX)));
+ TRACE_COUNTER1(
+ TRACE_DISABLED_BY_DEFAULT("blink_gc"),
+ "ThreadHeap::markedObjectSizeAtLastCompleteSweepKB",
+ std::min(heap.HeapStats().MarkedObjectSizeAtLastCompleteSweep() / 1024,
+ static_cast<size_t>(INT_MAX)));
+ TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink_gc"),
+ "ThreadHeap::allocatedSpaceKB",
+ std::min(heap.HeapStats().AllocatedSpace() / 1024,
+ static_cast<size_t>(INT_MAX)));
+ TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink_gc"),
+ "ThreadHeap::objectSizeAtLastGCKB",
+ std::min(heap.HeapStats().ObjectSizeAtLastGC() / 1024,
+ static_cast<size_t>(INT_MAX)));
+ TRACE_COUNTER1(
+ TRACE_DISABLED_BY_DEFAULT("blink_gc"), "ThreadHeap::wrapperCount",
+ std::min(heap.HeapStats().WrapperCount(), static_cast<size_t>(INT_MAX)));
+ TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink_gc"),
+ "ThreadHeap::wrapperCountAtLastGC",
+ std::min(heap.HeapStats().WrapperCountAtLastGC(),
+ static_cast<size_t>(INT_MAX)));
+ TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink_gc"),
+ "ThreadHeap::collectedWrapperCount",
+ std::min(heap.HeapStats().CollectedWrapperCount(),
+ static_cast<size_t>(INT_MAX)));
+ TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink_gc"),
+ "ThreadHeap::partitionAllocSizeAtLastGCKB",
+ std::min(heap.HeapStats().PartitionAllocSizeAtLastGC() / 1024,
+ static_cast<size_t>(INT_MAX)));
+ TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink_gc"),
+ "Partitions::totalSizeOfCommittedPagesKB",
+ std::min(WTF::Partitions::TotalSizeOfCommittedPages() / 1024,
+ static_cast<size_t>(INT_MAX)));
+}
+
+size_t ThreadHeap::ObjectPayloadSizeForTesting() {
+ ThreadState::AtomicPauseScope atomic_pause_scope(thread_state_);
+ size_t object_payload_size = 0;
+ thread_state_->SetGCPhase(ThreadState::GCPhase::kMarking);
+ thread_state_->Heap().MakeConsistentForGC();
+ thread_state_->Heap().PrepareForSweep();
+ for (int i = 0; i < BlinkGC::kNumberOfArenas; ++i)
+ object_payload_size += arenas_[i]->ObjectPayloadSizeForTesting();
+ MakeConsistentForMutator();
+ thread_state_->SetGCPhase(ThreadState::GCPhase::kSweeping);
+ thread_state_->SetGCPhase(ThreadState::GCPhase::kNone);
+ return object_payload_size;
+}
+
+void ThreadHeap::ShouldFlushHeapDoesNotContainCache() {
+ should_flush_heap_does_not_contain_cache_ = true;
+}
+
+void ThreadHeap::FlushHeapDoesNotContainCacheIfNeeded() {
+ if (should_flush_heap_does_not_contain_cache_) {
+ FlushHeapDoesNotContainCache();
+ should_flush_heap_does_not_contain_cache_ = false;
+ }
+}
+
+bool ThreadHeap::IsAddressInHeapDoesNotContainCache(Address address) {
+ // If the cache has been marked as invalidated, it's cleared prior
+ // to performing the next GC. Hence, consider the cache as being
+ // effectively empty.
+ if (should_flush_heap_does_not_contain_cache_)
+ return false;
+ return heap_does_not_contain_cache_->Lookup(address);
+}
+
+void ThreadHeap::VisitPersistentRoots(Visitor* visitor) {
+ DCHECK(thread_state_->InAtomicMarkingPause());
+ TRACE_EVENT0("blink_gc", "ThreadHeap::visitPersistentRoots");
+ thread_state_->VisitPersistents(visitor);
+}
+
+void ThreadHeap::VisitStackRoots(MarkingVisitor* visitor) {
+ DCHECK(thread_state_->InAtomicMarkingPause());
+ TRACE_EVENT0("blink_gc", "ThreadHeap::visitStackRoots");
+ thread_state_->VisitStack(visitor);
+}
+
+BasePage* ThreadHeap::LookupPageForAddress(Address address) {
+ DCHECK(thread_state_->InAtomicMarkingPause());
+ if (PageMemoryRegion* region = region_tree_->Lookup(address)) {
+ return region->PageFromAddress(address);
+ }
+ return nullptr;
+}
+
+void ThreadHeap::ResetHeapCounters() {
+ DCHECK(thread_state_->InAtomicMarkingPause());
+
+ ThreadHeap::ReportMemoryUsageForTracing();
+
+ ProcessHeap::DecreaseTotalAllocatedObjectSize(stats_.AllocatedObjectSize());
+ ProcessHeap::DecreaseTotalMarkedObjectSize(stats_.MarkedObjectSize());
+
+ stats_.Reset();
+}
+
+void ThreadHeap::MakeConsistentForGC() {
+ DCHECK(thread_state_->InAtomicMarkingPause());
+ TRACE_EVENT0("blink_gc", "ThreadHeap::MakeConsistentForGC");
+ for (int i = 0; i < BlinkGC::kNumberOfArenas; ++i)
+ arenas_[i]->MakeConsistentForGC();
+}
+
+void ThreadHeap::MakeConsistentForMutator() {
+ DCHECK(thread_state_->InAtomicMarkingPause());
+ for (int i = 0; i < BlinkGC::kNumberOfArenas; ++i)
+ arenas_[i]->MakeConsistentForMutator();
+}
+
+void ThreadHeap::Compact() {
+ if (!Compaction()->IsCompacting())
+ return;
+
+ // Compaction is done eagerly and before the mutator threads get
+ // to run again. Doing it lazily is problematic, as the mutator's
+ // references to live objects could suddenly be invalidated by
+ // compaction of a page/heap. We do know all the references to
+ // the relocating objects just after marking, but won't later.
+ // (e.g., stack references could have been created, new objects
+ // created which refer to old collection objects, and so on.)
+
+ // Compact the hash table backing store arena first, it usually has
+ // higher fragmentation and is larger.
+ //
+ // TODO: implement bail out wrt any overall deadline, not compacting
+ // the remaining arenas if the time budget has been exceeded.
+ Compaction()->StartThreadCompaction();
+ for (int i = BlinkGC::kHashTableArenaIndex; i >= BlinkGC::kVector1ArenaIndex;
+ --i)
+ static_cast<NormalPageArena*>(arenas_[i])->SweepAndCompact();
+ Compaction()->FinishThreadCompaction();
+}
+
+void ThreadHeap::PrepareForSweep() {
+ DCHECK(thread_state_->InAtomicMarkingPause());
+ DCHECK(thread_state_->CheckThread());
+ for (int i = 0; i < BlinkGC::kNumberOfArenas; i++)
+ arenas_[i]->PrepareForSweep();
+}
+
+void ThreadHeap::RemoveAllPages() {
+ DCHECK(thread_state_->CheckThread());
+ for (int i = 0; i < BlinkGC::kNumberOfArenas; ++i)
+ arenas_[i]->RemoveAllPages();
+}
+
+void ThreadHeap::CompleteSweep() {
+ static_assert(BlinkGC::kEagerSweepArenaIndex == 0,
+ "Eagerly swept arenas must be processed first.");
+ for (int i = 0; i < BlinkGC::kNumberOfArenas; i++)
+ arenas_[i]->CompleteSweep();
+}
+
+void ThreadHeap::ClearArenaAges() {
+ memset(arena_ages_, 0, sizeof(size_t) * BlinkGC::kNumberOfArenas);
+ memset(likely_to_be_promptly_freed_.get(), 0,
+ sizeof(int) * kLikelyToBePromptlyFreedArraySize);
+ current_arena_ages_ = 0;
+}
+
+int ThreadHeap::ArenaIndexOfVectorArenaLeastRecentlyExpanded(
+ int begin_arena_index,
+ int end_arena_index) {
+ size_t min_arena_age = arena_ages_[begin_arena_index];
+ int arena_index_with_min_arena_age = begin_arena_index;
+ for (int arena_index = begin_arena_index + 1; arena_index <= end_arena_index;
+ arena_index++) {
+ if (arena_ages_[arena_index] < min_arena_age) {
+ min_arena_age = arena_ages_[arena_index];
+ arena_index_with_min_arena_age = arena_index;
+ }
+ }
+ DCHECK(IsVectorArenaIndex(arena_index_with_min_arena_age));
+ return arena_index_with_min_arena_age;
+}
+
+BaseArena* ThreadHeap::ExpandedVectorBackingArena(size_t gc_info_index) {
+ size_t entry_index = gc_info_index & kLikelyToBePromptlyFreedArrayMask;
+ --likely_to_be_promptly_freed_[entry_index];
+ int arena_index = vector_backing_arena_index_;
+ arena_ages_[arena_index] = ++current_arena_ages_;
+ vector_backing_arena_index_ = ArenaIndexOfVectorArenaLeastRecentlyExpanded(
+ BlinkGC::kVector1ArenaIndex, BlinkGC::kVector4ArenaIndex);
+ return arenas_[arena_index];
+}
+
+void ThreadHeap::AllocationPointAdjusted(int arena_index) {
+ arena_ages_[arena_index] = ++current_arena_ages_;
+ if (vector_backing_arena_index_ == arena_index) {
+ vector_backing_arena_index_ = ArenaIndexOfVectorArenaLeastRecentlyExpanded(
+ BlinkGC::kVector1ArenaIndex, BlinkGC::kVector4ArenaIndex);
+ }
+}
+
+void ThreadHeap::PromptlyFreed(size_t gc_info_index) {
+ DCHECK(thread_state_->CheckThread());
+ size_t entry_index = gc_info_index & kLikelyToBePromptlyFreedArrayMask;
+ // See the comment in vectorBackingArena() for why this is +3.
+ likely_to_be_promptly_freed_[entry_index] += 3;
+}
+
+#if defined(ADDRESS_SANITIZER)
+void ThreadHeap::PoisonAllHeaps() {
+ RecursiveMutexLocker persistent_lock(
+ ProcessHeap::CrossThreadPersistentMutex());
+ // Poisoning all unmarked objects in the other arenas.
+ for (int i = 1; i < BlinkGC::kNumberOfArenas; i++)
+ arenas_[i]->PoisonArena();
+ // CrossThreadPersistents in unmarked objects may be accessed from other
+ // threads (e.g. in CrossThreadPersistentRegion::shouldTracePersistent) and
+ // that would be fine.
+ ProcessHeap::GetCrossThreadPersistentRegion()
+ .UnpoisonCrossThreadPersistents();
+}
+
+void ThreadHeap::PoisonEagerArena() {
+ RecursiveMutexLocker persistent_lock(
+ ProcessHeap::CrossThreadPersistentMutex());
+ arenas_[BlinkGC::kEagerSweepArenaIndex]->PoisonArena();
+ // CrossThreadPersistents in unmarked objects may be accessed from other
+ // threads (e.g. in CrossThreadPersistentRegion::shouldTracePersistent) and
+ // that would be fine.
+ ProcessHeap::GetCrossThreadPersistentRegion()
+ .UnpoisonCrossThreadPersistents();
+}
+#endif
+
+#if DCHECK_IS_ON()
+BasePage* ThreadHeap::FindPageFromAddress(Address address) {
+ for (int i = 0; i < BlinkGC::kNumberOfArenas; ++i) {
+ if (BasePage* page = arenas_[i]->FindPageFromAddress(address))
+ return page;
+ }
+ return nullptr;
+}
+#endif
+
+void ThreadHeap::TakeSnapshot(SnapshotType type) {
+ DCHECK(thread_state_->InAtomicMarkingPause());
+
+ // 0 is used as index for freelist entries. Objects are indexed 1 to
+ // gcInfoIndex.
+ ThreadState::GCSnapshotInfo info(GCInfoTable::GcInfoIndex() + 1);
+ String thread_dump_name =
+ String::Format("blink_gc/thread_%lu",
+ static_cast<unsigned long>(thread_state_->ThreadId()));
+ const String heaps_dump_name = thread_dump_name + "/heaps";
+ const String classes_dump_name = thread_dump_name + "/classes";
+
+ int number_of_heaps_reported = 0;
+#define SNAPSHOT_HEAP(ArenaType) \
+ { \
+ number_of_heaps_reported++; \
+ switch (type) { \
+ case SnapshotType::kHeapSnapshot: \
+ arenas_[BlinkGC::k##ArenaType##ArenaIndex]->TakeSnapshot( \
+ heaps_dump_name + "/" #ArenaType, info); \
+ break; \
+ case SnapshotType::kFreelistSnapshot: \
+ arenas_[BlinkGC::k##ArenaType##ArenaIndex]->TakeFreelistSnapshot( \
+ heaps_dump_name + "/" #ArenaType); \
+ break; \
+ default: \
+ NOTREACHED(); \
+ } \
+ }
+
+ SNAPSHOT_HEAP(NormalPage1);
+ SNAPSHOT_HEAP(NormalPage2);
+ SNAPSHOT_HEAP(NormalPage3);
+ SNAPSHOT_HEAP(NormalPage4);
+ SNAPSHOT_HEAP(EagerSweep);
+ SNAPSHOT_HEAP(Vector1);
+ SNAPSHOT_HEAP(Vector2);
+ SNAPSHOT_HEAP(Vector3);
+ SNAPSHOT_HEAP(Vector4);
+ SNAPSHOT_HEAP(InlineVector);
+ SNAPSHOT_HEAP(HashTable);
+ SNAPSHOT_HEAP(LargeObject);
+ FOR_EACH_TYPED_ARENA(SNAPSHOT_HEAP);
+
+ DCHECK_EQ(number_of_heaps_reported, BlinkGC::kNumberOfArenas);
+
+#undef SNAPSHOT_HEAP
+
+ if (type == SnapshotType::kFreelistSnapshot)
+ return;
+
+ size_t total_live_count = 0;
+ size_t total_dead_count = 0;
+ size_t total_live_size = 0;
+ size_t total_dead_size = 0;
+ for (size_t gc_info_index = 1; gc_info_index <= GCInfoTable::GcInfoIndex();
+ ++gc_info_index) {
+ total_live_count += info.live_count[gc_info_index];
+ total_dead_count += info.dead_count[gc_info_index];
+ total_live_size += info.live_size[gc_info_index];
+ total_dead_size += info.dead_size[gc_info_index];
+ }
+
+ base::trace_event::MemoryAllocatorDump* thread_dump =
+ BlinkGCMemoryDumpProvider::Instance()
+ ->CreateMemoryAllocatorDumpForCurrentGC(thread_dump_name);
+ thread_dump->AddScalar("live_count", "objects", total_live_count);
+ thread_dump->AddScalar("dead_count", "objects", total_dead_count);
+ thread_dump->AddScalar("live_size", "bytes", total_live_size);
+ thread_dump->AddScalar("dead_size", "bytes", total_dead_size);
+
+ base::trace_event::MemoryAllocatorDump* heaps_dump =
+ BlinkGCMemoryDumpProvider::Instance()
+ ->CreateMemoryAllocatorDumpForCurrentGC(heaps_dump_name);
+ base::trace_event::MemoryAllocatorDump* classes_dump =
+ BlinkGCMemoryDumpProvider::Instance()
+ ->CreateMemoryAllocatorDumpForCurrentGC(classes_dump_name);
+ BlinkGCMemoryDumpProvider::Instance()
+ ->CurrentProcessMemoryDump()
+ ->AddOwnershipEdge(classes_dump->guid(), heaps_dump->guid());
+}
+
+bool ThreadHeap::AdvanceLazySweep(double deadline_seconds) {
+ for (int i = 0; i < BlinkGC::kNumberOfArenas; i++) {
+ // lazySweepWithDeadline() won't check the deadline until it sweeps
+ // 10 pages. So we give a small slack for safety.
+ double slack = 0.001;
+ double remaining_budget =
+ deadline_seconds - slack - CurrentTimeTicksInSeconds();
+ if (remaining_budget <= 0 ||
+ !arenas_[i]->LazySweepWithDeadline(deadline_seconds)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void ThreadHeap::WriteBarrier(void* value) {
+ DCHECK(thread_state_->IsIncrementalMarking());
+ DCHECK(value);
+ // '-1' is used to indicate deleted values.
+ DCHECK_NE(value, reinterpret_cast<void*>(-1));
+
+ BasePage* const page = PageFromObject(value);
+ HeapObjectHeader* const header =
+ page->IsLargeObjectPage()
+ ? static_cast<LargeObjectPage*>(page)->GetHeapObjectHeader()
+ : static_cast<NormalPage*>(page)->FindHeaderFromAddress(
+ reinterpret_cast<Address>(const_cast<void*>(value)));
+ if (header->IsMarked())
+ return;
+
+ // Mark and push trace callback.
+ header->Mark();
+ marking_worklist_->Push(
+ WorklistTaskId::MainThread,
+ {header->Payload(), ThreadHeap::GcInfo(header->GcInfoIndex())->trace_});
+}
+
+ThreadHeap* ThreadHeap::main_thread_heap_ = nullptr;
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/heap/heap.h b/chromium/third_party/blink/renderer/platform/heap/heap.h
new file mode 100644
index 00000000000..f18a6f951be
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/heap.h
@@ -0,0 +1,750 @@
+/*
+ * Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/heap/gc_info.h"
+#include "third_party/blink/renderer/platform/heap/heap_page.h"
+#include "third_party/blink/renderer/platform/heap/process_heap.h"
+#include "third_party/blink/renderer/platform/heap/stack_frame_depth.h"
+#include "third_party/blink/renderer/platform/heap/thread_state.h"
+#include "third_party/blink/renderer/platform/heap/visitor.h"
+#include "third_party/blink/renderer/platform/heap/worklist.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/address_sanitizer.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/atomics.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+namespace incremental_marking_test {
+class IncrementalMarkingScopeBase;
+} // namespace incremental_marking_test
+
+class PagePool;
+class RegionTree;
+
+struct MarkingItem {
+ void* object;
+ TraceCallback callback;
+};
+
+using CustomCallbackItem = MarkingItem;
+using NotFullyConstructedItem = void*;
+
+// Segment size of 512 entries necessary to avoid throughput regressions. Since
+// the work list is currently a temporary object this is not a problem.
+using MarkingWorklist = Worklist<MarkingItem, 512 /* local entries */>;
+using NotFullyConstructedWorklist =
+ Worklist<NotFullyConstructedItem, 16 /* local entries */>;
+using WeakCallbackWorklist =
+ Worklist<CustomCallbackItem, 256 /* local entries */>;
+
+class PLATFORM_EXPORT HeapAllocHooks {
+ public:
+ // TODO(hajimehoshi): Pass a type name of the allocated object.
+ typedef void AllocationHook(Address, size_t, const char*);
+ typedef void FreeHook(Address);
+
+ // Sets allocation hook. Only one hook is supported.
+ static void SetAllocationHook(AllocationHook* hook) {
+ CHECK(!allocation_hook_ || !hook);
+ allocation_hook_ = hook;
+ }
+
+ // Sets free hook. Only one hook is supported.
+ static void SetFreeHook(FreeHook* hook) {
+ CHECK(!free_hook_ || !hook);
+ free_hook_ = hook;
+ }
+
+ static void AllocationHookIfEnabled(Address address,
+ size_t size,
+ const char* type_name) {
+ AllocationHook* allocation_hook = allocation_hook_;
+ if (UNLIKELY(!!allocation_hook))
+ allocation_hook(address, size, type_name);
+ }
+
+ static void FreeHookIfEnabled(Address address) {
+ FreeHook* free_hook = free_hook_;
+ if (UNLIKELY(!!free_hook))
+ free_hook(address);
+ }
+
+ private:
+ static AllocationHook* allocation_hook_;
+ static FreeHook* free_hook_;
+};
+
+class HeapCompact;
+template <typename T>
+class Member;
+template <typename T>
+class WeakMember;
+template <typename T>
+class UntracedMember;
+
+template <typename T, bool = NeedsAdjustPointer<T>::value>
+class ObjectAliveTrait;
+
+template <typename T>
+class ObjectAliveTrait<T, false> {
+ STATIC_ONLY(ObjectAliveTrait);
+
+ public:
+ static bool IsHeapObjectAlive(const T* object) {
+ static_assert(sizeof(T), "T must be fully defined");
+ return HeapObjectHeader::FromPayload(object)->IsMarked();
+ }
+};
+
+template <typename T>
+class ObjectAliveTrait<T, true> {
+ STATIC_ONLY(ObjectAliveTrait);
+
+ public:
+ NO_SANITIZE_ADDRESS
+ static bool IsHeapObjectAlive(const T* object) {
+ static_assert(sizeof(T), "T must be fully defined");
+ return object->GetHeapObjectHeader()->IsMarked();
+ }
+};
+
+// Stats for the heap.
+class ThreadHeapStats {
+ USING_FAST_MALLOC(ThreadHeapStats);
+
+ public:
+ ThreadHeapStats();
+ void SetMarkedObjectSizeAtLastCompleteSweep(size_t size) {
+ marked_object_size_at_last_complete_sweep_ = size;
+ }
+ size_t MarkedObjectSizeAtLastCompleteSweep() {
+ return marked_object_size_at_last_complete_sweep_;
+ }
+ void IncreaseAllocatedObjectSize(size_t delta);
+ void DecreaseAllocatedObjectSize(size_t delta);
+ size_t AllocatedObjectSize() { return allocated_object_size_; }
+ void IncreaseMarkedObjectSize(size_t delta);
+ size_t MarkedObjectSize() const { return marked_object_size_; }
+ void IncreaseAllocatedSpace(size_t delta);
+ void DecreaseAllocatedSpace(size_t delta);
+ size_t AllocatedSpace() { return allocated_space_; }
+ size_t ObjectSizeAtLastGC() const { return object_size_at_last_gc_; }
+ double LiveObjectRateSinceLastGC() const;
+ void IncreaseWrapperCount(size_t delta) { wrapper_count_ += delta; }
+ void DecreaseWrapperCount(size_t delta) { wrapper_count_ -= delta; }
+ size_t WrapperCount() { return AcquireLoad(&wrapper_count_); }
+ size_t WrapperCountAtLastGC() { return wrapper_count_at_last_gc_; }
+ void IncreaseCollectedWrapperCount(size_t delta) {
+ collected_wrapper_count_ += delta;
+ }
+ size_t CollectedWrapperCount() { return collected_wrapper_count_; }
+ size_t PartitionAllocSizeAtLastGC() {
+ return partition_alloc_size_at_last_gc_;
+ }
+ void SetEstimatedMarkingTimePerByte(double estimated_marking_time_per_byte) {
+ estimated_marking_time_per_byte_ = estimated_marking_time_per_byte;
+ }
+ double EstimatedMarkingTimePerByte() const {
+ return estimated_marking_time_per_byte_;
+ }
+ double EstimatedMarkingTime();
+ void Reset();
+
+ private:
+ size_t allocated_space_;
+ size_t allocated_object_size_;
+ size_t object_size_at_last_gc_;
+ size_t marked_object_size_;
+ size_t marked_object_size_at_last_complete_sweep_;
+ size_t wrapper_count_;
+ size_t wrapper_count_at_last_gc_;
+ size_t collected_wrapper_count_;
+ size_t partition_alloc_size_at_last_gc_;
+ double estimated_marking_time_per_byte_;
+};
+
+class PLATFORM_EXPORT ThreadHeap {
+ public:
+ explicit ThreadHeap(ThreadState*);
+ ~ThreadHeap();
+
+ // Returns true for main thread's heap.
+ // TODO(keishi): Per-thread-heap will return false.
+ bool IsMainThreadHeap() { return this == ThreadHeap::MainThreadHeap(); }
+ static ThreadHeap* MainThreadHeap() { return main_thread_heap_; }
+
+ template <typename T>
+ static inline bool IsHeapObjectAlive(const T* object) {
+ static_assert(sizeof(T), "T must be fully defined");
+ // The strongification of collections relies on the fact that once a
+ // collection has been strongified, there is no way that it can contain
+ // non-live entries, so no entries will be removed. Since you can't set
+ // the mark bit on a null pointer, that means that null pointers are
+ // always 'alive'.
+ if (!object)
+ return true;
+ // TODO(keishi): some tests create CrossThreadPersistent on non attached
+ // threads.
+ if (!ThreadState::Current())
+ return true;
+ DCHECK(&ThreadState::Current()->Heap() ==
+ &PageFromObject(object)->Arena()->GetThreadState()->Heap());
+ return ObjectAliveTrait<T>::IsHeapObjectAlive(object);
+ }
+ template <typename T>
+ static inline bool IsHeapObjectAlive(const Member<T>& member) {
+ return IsHeapObjectAlive(member.Get());
+ }
+ template <typename T>
+ static inline bool IsHeapObjectAlive(const WeakMember<T>& member) {
+ return IsHeapObjectAlive(member.Get());
+ }
+ template <typename T>
+ static inline bool IsHeapObjectAlive(const UntracedMember<T>& member) {
+ return IsHeapObjectAlive(member.Get());
+ }
+
+ StackFrameDepth& GetStackFrameDepth() { return stack_frame_depth_; }
+
+ ThreadHeapStats& HeapStats() { return stats_; }
+
+ MarkingWorklist* GetMarkingWorklist() const {
+ return marking_worklist_.get();
+ }
+
+ NotFullyConstructedWorklist* GetNotFullyConstructedWorklist() const {
+ return not_fully_constructed_worklist_.get();
+ }
+
+ WeakCallbackWorklist* GetWeakCallbackWorklist() const {
+ return weak_callback_worklist_.get();
+ }
+
+ void VisitPersistentRoots(Visitor*);
+ void VisitStackRoots(MarkingVisitor*);
+ void EnterSafePoint(ThreadState*);
+ void LeaveSafePoint();
+
+ // Is the finalizable GC object still alive, but slated for lazy sweeping?
+ // If a lazy sweep is in progress, returns true if the object was found
+ // to be not reachable during the marking phase, but it has yet to be swept
+ // and finalized. The predicate returns false in all other cases.
+ //
+ // Holding a reference to an already-dead object is not a valid state
+ // to be in; willObjectBeLazilySwept() has undefined behavior if passed
+ // such a reference.
+ template <typename T>
+ NO_SANITIZE_ADDRESS static bool WillObjectBeLazilySwept(
+ const T* object_pointer) {
+ static_assert(IsGarbageCollectedType<T>::value,
+ "only objects deriving from GarbageCollected can be used.");
+ BasePage* page = PageFromObject(object_pointer);
+ // Page has been swept and it is still alive.
+ if (page->HasBeenSwept())
+ return false;
+ DCHECK(page->Arena()->GetThreadState()->IsSweepingInProgress());
+
+ // If marked and alive, the object hasn't yet been swept..and won't
+ // be once its page is processed.
+ if (ThreadHeap::IsHeapObjectAlive(const_cast<T*>(object_pointer)))
+ return false;
+
+ if (page->IsLargeObjectPage())
+ return true;
+
+ // If the object is unmarked, it may be on the page currently being
+ // lazily swept.
+ return page->Arena()->WillObjectBeLazilySwept(
+ page, const_cast<T*>(object_pointer));
+ }
+
+ // Register an ephemeron table for fixed-point iteration.
+ void RegisterWeakTable(void* container_object,
+ EphemeronCallback);
+
+ // Heap compaction registration methods:
+
+ // Register |slot| as containing a reference to a movable heap object.
+ //
+ // When compaction moves the object pointed to by |*slot| to |newAddress|,
+ // |*slot| must be updated to hold |newAddress| instead.
+ void RegisterMovingObjectReference(MovableReference*);
+
+ // Register a callback to be invoked upon moving the object starting at
+ // |reference|; see |MovingObjectCallback| documentation for details.
+ //
+ // This callback mechanism is needed to account for backing store objects
+ // containing intra-object pointers, all of which must be relocated/rebased
+ // with respect to the moved-to location.
+ //
+ // For Blink, |HeapLinkedHashSet<>| is currently the only abstraction which
+ // relies on this feature.
+ void RegisterMovingObjectCallback(MovableReference,
+ MovingObjectCallback,
+ void* callback_data);
+
+ RegionTree* GetRegionTree() { return region_tree_.get(); }
+
+ static inline size_t AllocationSizeFromSize(size_t size) {
+ // Add space for header.
+ size_t allocation_size = size + sizeof(HeapObjectHeader);
+ // The allocation size calculation can overflow for large sizes.
+ CHECK_GT(allocation_size, size);
+ // Align size with allocation granularity.
+ allocation_size = (allocation_size + kAllocationMask) & ~kAllocationMask;
+ return allocation_size;
+ }
+ Address AllocateOnArenaIndex(ThreadState*,
+ size_t,
+ int arena_index,
+ size_t gc_info_index,
+ const char* type_name);
+ template <typename T>
+ static Address Allocate(size_t, bool eagerly_sweep = false);
+ template <typename T>
+ static Address Reallocate(void* previous, size_t);
+
+ void ProcessMarkingStack(Visitor*);
+ void WeakProcessing(Visitor*);
+ void MarkNotFullyConstructedObjects(Visitor*);
+ bool AdvanceMarkingStackProcessing(Visitor*, double deadline_seconds);
+ void VerifyMarking();
+
+ // Conservatively checks whether an address is a pointer in any of the
+ // thread heaps. If so marks the object pointed to as live.
+ Address CheckAndMarkPointer(MarkingVisitor*, Address);
+#if DCHECK_IS_ON()
+ Address CheckAndMarkPointer(MarkingVisitor*,
+ Address,
+ MarkedPointerCallbackForTesting);
+#endif
+
+ size_t ObjectPayloadSizeForTesting();
+
+ void FlushHeapDoesNotContainCache();
+ bool IsAddressInHeapDoesNotContainCache(Address);
+ void FlushHeapDoesNotContainCacheIfNeeded();
+ void ShouldFlushHeapDoesNotContainCache();
+
+ PagePool* GetFreePagePool() { return free_page_pool_.get(); }
+
+ // This look-up uses the region search tree and a negative contains cache to
+ // provide an efficient mapping from arbitrary addresses to the containing
+ // heap-page if one exists.
+ BasePage* LookupPageForAddress(Address);
+
+ static const GCInfo* GcInfo(size_t gc_info_index) {
+ DCHECK_GE(gc_info_index, 1u);
+ DCHECK(gc_info_index < GCInfoTable::kMaxIndex);
+ DCHECK(g_gc_info_table);
+ const GCInfo* info = g_gc_info_table[gc_info_index];
+ DCHECK(info);
+ return info;
+ }
+
+ static void ReportMemoryUsageHistogram();
+ static void ReportMemoryUsageForTracing();
+
+ HeapCompact* Compaction();
+
+ // Get one of the heap structures for this thread.
+ // The thread heap is split into multiple heap parts based on object types
+ // and object sizes.
+ BaseArena* Arena(int arena_index) const {
+ DCHECK_LE(0, arena_index);
+ DCHECK_LT(arena_index, BlinkGC::kNumberOfArenas);
+ return arenas_[arena_index];
+ }
+
+ // VectorBackingArena() returns an arena that the vector allocation should
+ // use. We have four vector arenas and want to choose the best arena here.
+ //
+ // The goal is to improve the succession rate where expand and
+ // promptlyFree happen at an allocation point. This is a key for reusing
+ // the same memory as much as possible and thus improves performance.
+ // To achieve the goal, we use the following heuristics:
+ //
+ // - A vector that has been expanded recently is likely to be expanded
+ // again soon.
+ // - A vector is likely to be promptly freed if the same type of vector
+ // has been frequently promptly freed in the past.
+ // - Given the above, when allocating a new vector, look at the four vectors
+ // that are placed immediately prior to the allocation point of each arena.
+ // Choose the arena where the vector is least likely to be expanded
+ // nor promptly freed.
+ //
+ // To implement the heuristics, we add an arenaAge to each arena. The arenaAge
+ // is updated if:
+ //
+ // - a vector on the arena is expanded; or
+ // - a vector that meets the condition (*) is allocated on the arena
+ //
+ // (*) More than 33% of the same type of vectors have been promptly
+ // freed since the last GC.
+ //
+ BaseArena* VectorBackingArena(size_t gc_info_index) {
+ DCHECK(thread_state_->CheckThread());
+ size_t entry_index = gc_info_index & kLikelyToBePromptlyFreedArrayMask;
+ --likely_to_be_promptly_freed_[entry_index];
+ int arena_index = vector_backing_arena_index_;
+ // If likely_to_be_promptly_freed_[entryIndex] > 0, that means that
+ // more than 33% of vectors of the type have been promptly freed
+ // since the last GC.
+ if (likely_to_be_promptly_freed_[entry_index] > 0) {
+ arena_ages_[arena_index] = ++current_arena_ages_;
+ vector_backing_arena_index_ =
+ ArenaIndexOfVectorArenaLeastRecentlyExpanded(
+ BlinkGC::kVector1ArenaIndex, BlinkGC::kVector4ArenaIndex);
+ }
+ DCHECK(IsVectorArenaIndex(arena_index));
+ return arenas_[arena_index];
+ }
+ BaseArena* ExpandedVectorBackingArena(size_t gc_info_index);
+ static bool IsVectorArenaIndex(int arena_index) {
+ return BlinkGC::kVector1ArenaIndex <= arena_index &&
+ arena_index <= BlinkGC::kVector4ArenaIndex;
+ }
+ static bool IsNormalArenaIndex(int);
+ void AllocationPointAdjusted(int arena_index);
+ void PromptlyFreed(size_t gc_info_index);
+ void ClearArenaAges();
+ int ArenaIndexOfVectorArenaLeastRecentlyExpanded(int begin_arena_index,
+ int end_arena_index);
+
+ void MakeConsistentForGC();
+ // MakeConsistentForMutator() drops marks from marked objects and rebuild
+ // free lists. This is called after taking a snapshot and before resuming
+ // the executions of mutators.
+ void MakeConsistentForMutator();
+
+ void Compact();
+
+ bool AdvanceLazySweep(double deadline_seconds);
+
+ void PrepareForSweep();
+ void RemoveAllPages();
+ void CompleteSweep();
+
+ enum SnapshotType { kHeapSnapshot, kFreelistSnapshot };
+ void TakeSnapshot(SnapshotType);
+
+#if defined(ADDRESS_SANITIZER)
+ void PoisonEagerArena();
+ void PoisonAllHeaps();
+#endif
+
+#if DCHECK_IS_ON()
+ // Infrastructure to determine if an address is within one of the
+ // address ranges for the Blink heap. If the address is in the Blink
+ // heap the containing heap page is returned.
+ BasePage* FindPageFromAddress(Address);
+ BasePage* FindPageFromAddress(const void* pointer) {
+ return FindPageFromAddress(
+ reinterpret_cast<Address>(const_cast<void*>(pointer)));
+ }
+#endif
+
+ private:
+ // Reset counters that track live and allocated-since-last-GC sizes.
+ void ResetHeapCounters();
+
+ static int ArenaIndexForObjectSize(size_t);
+
+ void CommitCallbackStacks();
+ void DecommitCallbackStacks();
+
+ void InvokeEphemeronCallbacks(Visitor*);
+
+ // Write barrier assuming that incremental marking is running and value is not
+ // nullptr. Use MarkingVisitor::WriteBarrier as entrypoint.
+ void WriteBarrier(void* value);
+
+ ThreadState* thread_state_;
+ ThreadHeapStats stats_;
+ std::unique_ptr<RegionTree> region_tree_;
+ std::unique_ptr<HeapDoesNotContainCache> heap_does_not_contain_cache_;
+ std::unique_ptr<PagePool> free_page_pool_;
+ std::unique_ptr<MarkingWorklist> marking_worklist_;
+ std::unique_ptr<NotFullyConstructedWorklist> not_fully_constructed_worklist_;
+ std::unique_ptr<WeakCallbackWorklist> weak_callback_worklist_;
+ // No duplicates allowed for ephemeron callbacks. Hence, we use a hashmap
+ // with the key being the HashTable.
+ WTF::HashMap<void*, EphemeronCallback> ephemeron_callbacks_;
+ StackFrameDepth stack_frame_depth_;
+
+ std::unique_ptr<HeapCompact> compaction_;
+
+ BaseArena* arenas_[BlinkGC::kNumberOfArenas];
+ int vector_backing_arena_index_;
+ size_t arena_ages_[BlinkGC::kNumberOfArenas];
+ size_t current_arena_ages_;
+ bool should_flush_heap_does_not_contain_cache_;
+
+ // Ideally we want to allocate an array of size |gcInfoTableMax| but it will
+ // waste memory. Thus we limit the array size to 2^8 and share one entry
+ // with multiple types of vectors. This won't be an issue in practice,
+ // since there will be less than 2^8 types of objects in common cases.
+ static const int kLikelyToBePromptlyFreedArraySize = (1 << 8);
+ static const int kLikelyToBePromptlyFreedArrayMask =
+ kLikelyToBePromptlyFreedArraySize - 1;
+ std::unique_ptr<int[]> likely_to_be_promptly_freed_;
+
+ static ThreadHeap* main_thread_heap_;
+
+ friend class incremental_marking_test::IncrementalMarkingScopeBase;
+ friend class MarkingVisitor;
+ template <typename T>
+ friend class Member;
+ friend class ThreadState;
+};
+
+template <typename T>
+struct IsEagerlyFinalizedType {
+ STATIC_ONLY(IsEagerlyFinalizedType);
+
+ private:
+ typedef char YesType;
+ struct NoType {
+ char padding[8];
+ };
+
+ template <typename U>
+ static YesType CheckMarker(typename U::IsEagerlyFinalizedMarker*);
+ template <typename U>
+ static NoType CheckMarker(...);
+
+ public:
+ static const bool value = sizeof(CheckMarker<T>(nullptr)) == sizeof(YesType);
+};
+
+template <typename T>
+class GarbageCollected {
+ IS_GARBAGE_COLLECTED_TYPE();
+
+ // For now direct allocation of arrays on the heap is not allowed.
+ void* operator new[](size_t size);
+
+#if defined(OS_WIN) && defined(COMPILER_MSVC)
+ // Due to some quirkiness in the MSVC compiler we have to provide
+ // the delete[] operator in the GarbageCollected subclasses as it
+ // is called when a class is exported in a DLL.
+ protected:
+ void operator delete[](void* p) { NOTREACHED(); }
+#else
+ void operator delete[](void* p);
+#endif
+
+ public:
+ using GarbageCollectedType = T;
+
+ void* operator new(size_t size) {
+ return AllocateObject(size, IsEagerlyFinalizedType<T>::value);
+ }
+
+ static void* AllocateObject(size_t size, bool eagerly_sweep) {
+ return ThreadHeap::Allocate<T>(size, eagerly_sweep);
+ }
+
+ void operator delete(void* p) { NOTREACHED(); }
+
+ protected:
+ GarbageCollected() = default;
+
+ DISALLOW_COPY_AND_ASSIGN(GarbageCollected);
+};
+
+// Assigning class types to their arenas.
+//
+// We use sized arenas for most 'normal' objects to improve memory locality.
+// It seems that the same type of objects are likely to be accessed together,
+// which means that we want to group objects by type. That's one reason
+// why we provide dedicated arenas for popular types (e.g., Node, CSSValue),
+// but it's not practical to prepare dedicated arenas for all types.
+// Thus we group objects by their sizes, hoping that this will approximately
+// group objects by their types.
+//
+// An exception to the use of sized arenas is made for class types that
+// require prompt finalization after a garbage collection. That is, their
+// instances have to be finalized early and cannot be delayed until lazy
+// sweeping kicks in for their heap and page. The EAGERLY_FINALIZE()
+// macro is used to declare a class (and its derived classes) as being
+// in need of eager finalization. Must be defined with 'public' visibility
+// for a class.
+//
+
+inline int ThreadHeap::ArenaIndexForObjectSize(size_t size) {
+ if (size < 64) {
+ if (size < 32)
+ return BlinkGC::kNormalPage1ArenaIndex;
+ return BlinkGC::kNormalPage2ArenaIndex;
+ }
+ if (size < 128)
+ return BlinkGC::kNormalPage3ArenaIndex;
+ return BlinkGC::kNormalPage4ArenaIndex;
+}
+
+inline bool ThreadHeap::IsNormalArenaIndex(int index) {
+ return index >= BlinkGC::kNormalPage1ArenaIndex &&
+ index <= BlinkGC::kNormalPage4ArenaIndex;
+}
+
+#define DECLARE_EAGER_FINALIZATION_OPERATOR_NEW() \
+ public: \
+ GC_PLUGIN_IGNORE("491488") \
+ void* operator new(size_t size) { return AllocateObject(size, true); }
+
+#define IS_EAGERLY_FINALIZED() \
+ (PageFromObject(this)->Arena()->ArenaIndex() == \
+ BlinkGC::kEagerSweepArenaIndex)
+#if DCHECK_IS_ON()
+class VerifyEagerFinalization {
+ DISALLOW_NEW();
+
+ public:
+ ~VerifyEagerFinalization() {
+ // If this assert triggers, the class annotated as eagerly
+ // finalized ended up not being allocated on the heap
+ // set aside for eager finalization. The reason is most
+ // likely that the effective 'operator new' overload for
+ // this class' leftmost base is for a class that is not
+ // eagerly finalized. Declaring and defining an 'operator new'
+ // for this class is what's required -- consider using
+ // DECLARE_EAGER_FINALIZATION_OPERATOR_NEW().
+ DCHECK(IS_EAGERLY_FINALIZED());
+ }
+};
+#define EAGERLY_FINALIZE() \
+ private: \
+ VerifyEagerFinalization verify_eager_finalization_; \
+ \
+ public: \
+ typedef int IsEagerlyFinalizedMarker
+#else
+#define EAGERLY_FINALIZE() \
+ public: \
+ typedef int IsEagerlyFinalizedMarker
+#endif
+
+inline Address ThreadHeap::AllocateOnArenaIndex(ThreadState* state,
+ size_t size,
+ int arena_index,
+ size_t gc_info_index,
+ const char* type_name) {
+ DCHECK(state->IsAllocationAllowed());
+ DCHECK_NE(arena_index, BlinkGC::kLargeObjectArenaIndex);
+ NormalPageArena* arena = static_cast<NormalPageArena*>(Arena(arena_index));
+ Address address =
+ arena->AllocateObject(AllocationSizeFromSize(size), gc_info_index);
+ HeapAllocHooks::AllocationHookIfEnabled(address, size, type_name);
+ return address;
+}
+
+template <typename T>
+Address ThreadHeap::Allocate(size_t size, bool eagerly_sweep) {
+ ThreadState* state = ThreadStateFor<ThreadingTrait<T>::kAffinity>::GetState();
+ const char* type_name = WTF_HEAP_PROFILER_TYPE_NAME(T);
+ return state->Heap().AllocateOnArenaIndex(
+ state, size,
+ eagerly_sweep ? BlinkGC::kEagerSweepArenaIndex
+ : ThreadHeap::ArenaIndexForObjectSize(size),
+ GCInfoTrait<T>::Index(), type_name);
+}
+
+template <typename T>
+Address ThreadHeap::Reallocate(void* previous, size_t size) {
+ // Not intended to be a full C realloc() substitute;
+ // realloc(nullptr, size) is not a supported alias for malloc(size).
+
+ // TODO(sof): promptly free the previous object.
+ if (!size) {
+ // If the new size is 0 this is considered equivalent to free(previous).
+ return nullptr;
+ }
+
+ ThreadState* state = ThreadStateFor<ThreadingTrait<T>::kAffinity>::GetState();
+ HeapObjectHeader* previous_header = HeapObjectHeader::FromPayload(previous);
+ BasePage* page = PageFromObject(previous_header);
+ DCHECK(page);
+
+ // Determine arena index of new allocation.
+ int arena_index;
+ if (size >= kLargeObjectSizeThreshold) {
+ arena_index = BlinkGC::kLargeObjectArenaIndex;
+ } else {
+ arena_index = page->Arena()->ArenaIndex();
+ if (IsNormalArenaIndex(arena_index) ||
+ arena_index == BlinkGC::kLargeObjectArenaIndex)
+ arena_index = ArenaIndexForObjectSize(size);
+ }
+
+ size_t gc_info_index = GCInfoTrait<T>::Index();
+ // TODO(haraken): We don't support reallocate() for finalizable objects.
+ DCHECK(!ThreadHeap::GcInfo(previous_header->GcInfoIndex())->HasFinalizer());
+ DCHECK_EQ(previous_header->GcInfoIndex(), gc_info_index);
+ HeapAllocHooks::FreeHookIfEnabled(static_cast<Address>(previous));
+ Address address;
+ if (arena_index == BlinkGC::kLargeObjectArenaIndex) {
+ address = page->Arena()->AllocateLargeObject(AllocationSizeFromSize(size),
+ gc_info_index);
+ } else {
+ const char* type_name = WTF_HEAP_PROFILER_TYPE_NAME(T);
+ address = state->Heap().AllocateOnArenaIndex(state, size, arena_index,
+ gc_info_index, type_name);
+ }
+ size_t copy_size = previous_header->PayloadSize();
+ if (copy_size > size)
+ copy_size = size;
+ memcpy(address, previous, copy_size);
+ return address;
+}
+
+template <typename T>
+void Visitor::HandleWeakCell(Visitor* self, void* object) {
+ T** cell = reinterpret_cast<T**>(object);
+ // '-1' means deleted value. This can happen when weak fields are deleted
+ // while incremental marking is running.
+ if (*cell && (*cell == reinterpret_cast<T*>(-1) ||
+ !ObjectAliveTrait<T>::IsHeapObjectAlive(*cell)))
+ *cell = nullptr;
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_H_
diff --git a/chromium/third_party/blink/renderer/platform/heap/heap_allocator.cc b/chromium/third_party/blink/renderer/platform/heap/heap_allocator.cc
new file mode 100644
index 00000000000..7791505bbcd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/heap_allocator.cc
@@ -0,0 +1,136 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/heap/heap_allocator.h"
+
+namespace blink {
+
+void HeapAllocator::BackingFree(void* address) {
+ if (!address)
+ return;
+
+ ThreadState* state = ThreadState::Current();
+ if (state->SweepForbidden())
+ return;
+ DCHECK(!state->in_atomic_pause());
+
+ // Don't promptly free large objects because their page is never reused.
+ // Don't free backings allocated on other threads.
+ BasePage* page = PageFromObject(address);
+ if (page->IsLargeObjectPage() || page->Arena()->GetThreadState() != state)
+ return;
+
+ HeapObjectHeader* header = HeapObjectHeader::FromPayload(address);
+ // Don't promptly free marked backing as they may be registered on the marking
+ // callback stack. The effect on non incremental marking GCs is that promptly
+ // free is disabled for surviving backings during lazy sweeping.
+ if (header->IsMarked())
+ return;
+ state->Heap().PromptlyFreed(header->GcInfoIndex());
+ static_cast<NormalPage*>(page)->ArenaForNormalPage()->PromptlyFreeObject(
+ header);
+}
+
+void HeapAllocator::FreeVectorBacking(void* address) {
+ BackingFree(address);
+}
+
+void HeapAllocator::FreeInlineVectorBacking(void* address) {
+ BackingFree(address);
+}
+
+void HeapAllocator::FreeHashTableBacking(void* address, bool is_weak_table) {
+ if (!ThreadState::Current()->IsMarkingInProgress() || !is_weak_table)
+ BackingFree(address);
+}
+
+bool HeapAllocator::BackingExpand(void* address, size_t new_size) {
+ if (!address)
+ return false;
+
+ ThreadState* state = ThreadState::Current();
+ if (state->SweepForbidden())
+ return false;
+ DCHECK(!state->in_atomic_pause());
+ DCHECK(state->IsAllocationAllowed());
+ DCHECK_EQ(&state->Heap(), &ThreadState::FromObject(address)->Heap());
+
+ // FIXME: Support expand for large objects.
+ // Don't expand backings allocated on other threads.
+ BasePage* page = PageFromObject(address);
+ if (page->IsLargeObjectPage() || page->Arena()->GetThreadState() != state)
+ return false;
+
+ HeapObjectHeader* header = HeapObjectHeader::FromPayload(address);
+ NormalPageArena* arena = static_cast<NormalPage*>(page)->ArenaForNormalPage();
+ bool succeed = arena->ExpandObject(header, new_size);
+ if (succeed)
+ state->Heap().AllocationPointAdjusted(arena->ArenaIndex());
+ return succeed;
+}
+
+bool HeapAllocator::ExpandVectorBacking(void* address, size_t new_size) {
+ return BackingExpand(address, new_size);
+}
+
+bool HeapAllocator::ExpandInlineVectorBacking(void* address, size_t new_size) {
+ return BackingExpand(address, new_size);
+}
+
+bool HeapAllocator::ExpandHashTableBacking(void* address, size_t new_size) {
+ return BackingExpand(address, new_size);
+}
+
+bool HeapAllocator::BackingShrink(void* address,
+ size_t quantized_current_size,
+ size_t quantized_shrunk_size) {
+ if (!address || quantized_shrunk_size == quantized_current_size)
+ return true;
+
+ DCHECK_LT(quantized_shrunk_size, quantized_current_size);
+
+ ThreadState* state = ThreadState::Current();
+ if (state->SweepForbidden())
+ return false;
+ DCHECK(!state->in_atomic_pause());
+ DCHECK(state->IsAllocationAllowed());
+ DCHECK_EQ(&state->Heap(), &ThreadState::FromObject(address)->Heap());
+
+ // FIXME: Support shrink for large objects.
+ // Don't shrink backings allocated on other threads.
+ BasePage* page = PageFromObject(address);
+ if (page->IsLargeObjectPage() || page->Arena()->GetThreadState() != state)
+ return false;
+
+ HeapObjectHeader* header = HeapObjectHeader::FromPayload(address);
+ NormalPageArena* arena = static_cast<NormalPage*>(page)->ArenaForNormalPage();
+ // We shrink the object only if the shrinking will make a non-small
+ // prompt-free block.
+ // FIXME: Optimize the threshold size.
+ if (quantized_current_size <= quantized_shrunk_size +
+ sizeof(HeapObjectHeader) +
+ sizeof(void*) * 32 &&
+ !arena->IsObjectAllocatedAtAllocationPoint(header))
+ return true;
+
+ bool succeeded_at_allocation_point =
+ arena->ShrinkObject(header, quantized_shrunk_size);
+ if (succeeded_at_allocation_point)
+ state->Heap().AllocationPointAdjusted(arena->ArenaIndex());
+ return true;
+}
+
+bool HeapAllocator::ShrinkVectorBacking(void* address,
+ size_t quantized_current_size,
+ size_t quantized_shrunk_size) {
+ return BackingShrink(address, quantized_current_size, quantized_shrunk_size);
+}
+
+bool HeapAllocator::ShrinkInlineVectorBacking(void* address,
+ size_t quantized_current_size,
+ size_t quantized_shrunk_size) {
+ return BackingShrink(address, quantized_current_size, quantized_shrunk_size);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/heap/heap_allocator.h b/chromium/third_party/blink/renderer/platform/heap/heap_allocator.h
new file mode 100644
index 00000000000..58e6656ed96
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/heap_allocator.h
@@ -0,0 +1,947 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_ALLOCATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_ALLOCATOR_H_
+
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/heap/heap_buildflags.h"
+#include "third_party/blink/renderer/platform/heap/marking_visitor.h"
+#include "third_party/blink/renderer/platform/heap/persistent.h"
+#include "third_party/blink/renderer/platform/heap/trace_traits.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/construct_traits.h"
+#include "third_party/blink/renderer/platform/wtf/deque.h"
+#include "third_party/blink/renderer/platform/wtf/doubly_linked_list.h"
+#include "third_party/blink/renderer/platform/wtf/hash_counted_set.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/hash_table.h"
+#include "third_party/blink/renderer/platform/wtf/linked_hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/list_hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/type_traits.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+template <typename T, typename Traits = WTF::VectorTraits<T>>
+class HeapVectorBacking {
+ DISALLOW_NEW();
+ IS_GARBAGE_COLLECTED_TYPE();
+
+ public:
+ static void Finalize(void* pointer);
+ void FinalizeGarbageCollectedObject() { Finalize(this); }
+};
+
+template <typename Table>
+class HeapHashTableBacking {
+ DISALLOW_NEW();
+ IS_GARBAGE_COLLECTED_TYPE();
+
+ public:
+ static void Finalize(void* pointer);
+ void FinalizeGarbageCollectedObject() { Finalize(this); }
+};
+
+// This is a static-only class used as a trait on collections to make them heap
+// allocated. However see also HeapListHashSetAllocator.
+class PLATFORM_EXPORT HeapAllocator {
+ STATIC_ONLY(HeapAllocator);
+
+ public:
+ using Visitor = blink::Visitor;
+ static constexpr bool kIsGarbageCollected = true;
+
+ template <typename T>
+ static size_t MaxElementCountInBackingStore() {
+ return kMaxHeapObjectSize / sizeof(T);
+ }
+
+ template <typename T>
+ static size_t QuantizedSize(size_t count) {
+ CHECK(count <= MaxElementCountInBackingStore<T>());
+ return ThreadHeap::AllocationSizeFromSize(count * sizeof(T)) -
+ sizeof(HeapObjectHeader);
+ }
+ template <typename T>
+ static T* AllocateVectorBacking(size_t size) {
+ ThreadState* state =
+ ThreadStateFor<ThreadingTrait<T>::kAffinity>::GetState();
+ DCHECK(state->IsAllocationAllowed());
+ size_t gc_info_index = GCInfoTrait<HeapVectorBacking<T>>::Index();
+ NormalPageArena* arena = static_cast<NormalPageArena*>(
+ state->Heap().VectorBackingArena(gc_info_index));
+ return reinterpret_cast<T*>(arena->AllocateObject(
+ ThreadHeap::AllocationSizeFromSize(size), gc_info_index));
+ }
+ template <typename T>
+ static T* AllocateExpandedVectorBacking(size_t size) {
+ ThreadState* state =
+ ThreadStateFor<ThreadingTrait<T>::kAffinity>::GetState();
+ DCHECK(state->IsAllocationAllowed());
+ size_t gc_info_index = GCInfoTrait<HeapVectorBacking<T>>::Index();
+ NormalPageArena* arena = static_cast<NormalPageArena*>(
+ state->Heap().ExpandedVectorBackingArena(gc_info_index));
+ return reinterpret_cast<T*>(arena->AllocateObject(
+ ThreadHeap::AllocationSizeFromSize(size), gc_info_index));
+ }
+ static void FreeVectorBacking(void*);
+ static bool ExpandVectorBacking(void*, size_t);
+ static bool ShrinkVectorBacking(void* address,
+ size_t quantized_current_size,
+ size_t quantized_shrunk_size);
+ template <typename T>
+ static T* AllocateInlineVectorBacking(size_t size) {
+ size_t gc_info_index = GCInfoTrait<HeapVectorBacking<T>>::Index();
+ ThreadState* state =
+ ThreadStateFor<ThreadingTrait<T>::kAffinity>::GetState();
+ const char* type_name = WTF_HEAP_PROFILER_TYPE_NAME(HeapVectorBacking<T>);
+ return reinterpret_cast<T*>(state->Heap().AllocateOnArenaIndex(
+ state, size, BlinkGC::kInlineVectorArenaIndex, gc_info_index,
+ type_name));
+ }
+ static void FreeInlineVectorBacking(void*);
+ static bool ExpandInlineVectorBacking(void*, size_t);
+ static bool ShrinkInlineVectorBacking(void* address,
+ size_t quantized_current_size,
+ size_t quantized_shrunk_size);
+
+ template <typename T, typename HashTable>
+ static T* AllocateHashTableBacking(size_t size) {
+ size_t gc_info_index =
+ GCInfoTrait<HeapHashTableBacking<HashTable>>::Index();
+ ThreadState* state =
+ ThreadStateFor<ThreadingTrait<T>::kAffinity>::GetState();
+ const char* type_name =
+ WTF_HEAP_PROFILER_TYPE_NAME(HeapHashTableBacking<HashTable>);
+ return reinterpret_cast<T*>(state->Heap().AllocateOnArenaIndex(
+ state, size, BlinkGC::kHashTableArenaIndex, gc_info_index, type_name));
+ }
+ template <typename T, typename HashTable>
+ static T* AllocateZeroedHashTableBacking(size_t size) {
+ return AllocateHashTableBacking<T, HashTable>(size);
+ }
+ static void FreeHashTableBacking(void* address, bool is_weak_table);
+ static bool ExpandHashTableBacking(void*, size_t);
+
+ static void BackingWriteBarrier(void* address) {
+ MarkingVisitor::WriteBarrier(address);
+ }
+
+ template <typename Return, typename Metadata>
+ static Return Malloc(size_t size, const char* type_name) {
+ return reinterpret_cast<Return>(ThreadHeap::Allocate<Metadata>(
+ size, IsEagerlyFinalizedType<Metadata>::value));
+ }
+
+#if defined(OS_WIN) && defined(COMPILER_MSVC)
+ // MSVC eagerly instantiates the unused 'operator delete',
+ // provide a version that asserts and fails at run-time if
+ // used.
+ // Elsewhere we expect compilation to fail if 'delete' is
+ // attempted used and instantiated with a HeapAllocator-based
+ // object, as HeapAllocator::free is not provided.
+ static void Free(void*) { NOTREACHED(); }
+#endif
+
+ template <typename T>
+ static void* NewArray(size_t bytes) {
+ NOTREACHED();
+ return nullptr;
+ }
+
+ static void DeleteArray(void* ptr) { NOTREACHED(); }
+
+ static bool IsAllocationAllowed() {
+ return ThreadState::Current()->IsAllocationAllowed();
+ }
+
+ static bool IsObjectResurrectionForbidden() {
+ return ThreadState::Current()->IsObjectResurrectionForbidden();
+ }
+
+ template <typename T>
+ static bool IsHeapObjectAlive(T* object) {
+ return ThreadHeap::IsHeapObjectAlive(object);
+ }
+
+ template <typename VisitorDispatcher, typename T, typename Traits>
+ static void Trace(VisitorDispatcher visitor, T& t) {
+ TraceCollectionIfEnabled<Traits::kWeakHandlingFlag, T, Traits>::Trace(
+ visitor, t);
+ }
+
+ template <typename VisitorDispatcher>
+ static bool RegisterWeakTable(VisitorDispatcher visitor,
+ const void* closure,
+ EphemeronCallback iteration_callback) {
+ return visitor->RegisterWeakTable(closure, iteration_callback);
+ }
+
+ template <typename T, typename VisitorDispatcher>
+ static void RegisterBackingStoreCallback(VisitorDispatcher visitor,
+ T* backing_store,
+ MovingObjectCallback callback,
+ void* callback_data) {
+ visitor->RegisterBackingStoreCallback(backing_store, callback,
+ callback_data);
+ }
+
+ static void EnterGCForbiddenScope() {
+ ThreadState::Current()->EnterGCForbiddenScope();
+ }
+
+ static void LeaveGCForbiddenScope() {
+ ThreadState::Current()->LeaveGCForbiddenScope();
+ }
+
+ template <typename T, typename Traits>
+ static void NotifyNewObject(T* object) {
+#if BUILDFLAG(BLINK_HEAP_INCREMENTAL_MARKING)
+ if (!ThreadState::IsAnyIncrementalMarking())
+ return;
+ // The object may have been in-place constructed as part of a large object.
+ // It is not safe to retrieve the page from the object here.
+ ThreadState* const thread_state = ThreadState::Current();
+ if (thread_state->IsIncrementalMarking()) {
+ // Eagerly trace the object ensuring that the object and all its children
+ // are discovered by the marker.
+ ThreadState::NoAllocationScope no_allocation_scope(thread_state);
+ DCHECK(thread_state->CurrentVisitor());
+ // This check ensures that the visitor will not eagerly recurse into
+ // children but rather push all blink::GarbageCollected objects and only
+ // eagerly trace non-managed objects.
+ DCHECK(!thread_state->Heap().GetStackFrameDepth().IsEnabled());
+ // No weak handling for write barriers. Modifying weakly reachable objects
+ // strongifies them for the current cycle.
+ DCHECK(!Traits::kCanHaveDeletedValue || !Traits::IsDeletedValue(*object));
+ TraceCollectionIfEnabled<
+ WTF::kNoWeakHandling, T, Traits>::Trace(thread_state
+ ->CurrentVisitor(),
+ *object);
+ }
+#endif // BUILDFLAG(BLINK_HEAP_INCREMENTAL_MARKING)
+ }
+
+ template <typename T, typename Traits>
+ static void NotifyNewObjects(T* array, size_t len) {
+#if BUILDFLAG(BLINK_HEAP_INCREMENTAL_MARKING)
+ if (!ThreadState::IsAnyIncrementalMarking())
+ return;
+ // The object may have been in-place constructed as part of a large object.
+ // It is not safe to retrieve the page from the object here.
+ ThreadState* const thread_state = ThreadState::Current();
+ if (thread_state->IsIncrementalMarking()) {
+ // See |NotifyNewObject| for details.
+ ThreadState::NoAllocationScope no_allocation_scope(thread_state);
+ DCHECK(thread_state->CurrentVisitor());
+ DCHECK(!thread_state->Heap().GetStackFrameDepth().IsEnabled());
+ // No weak handling for write barriers. Modifying weakly reachable objects
+ // strongifies them for the current cycle.
+ while (len-- > 0) {
+ DCHECK(!Traits::kCanHaveDeletedValue ||
+ !Traits::IsDeletedValue(*array));
+ TraceCollectionIfEnabled<
+ WTF::kNoWeakHandling, T, Traits>::Trace(thread_state
+ ->CurrentVisitor(),
+ *array);
+ array++;
+ }
+ }
+#endif // BUILDFLAG(BLINK_HEAP_INCREMENTAL_MARKING)
+ }
+
+ template <typename T>
+ static void TraceVectorBacking(Visitor* visitor,
+ T* backing,
+ T** backing_slot) {
+ visitor->TraceBackingStoreStrongly(
+ reinterpret_cast<HeapVectorBacking<T>*>(backing),
+ reinterpret_cast<HeapVectorBacking<T>**>(backing_slot));
+ }
+
+ template <typename T, typename HashTable>
+ static void TraceHashTableBackingStrongly(Visitor* visitor,
+ T* backing,
+ T** backing_slot) {
+ visitor->TraceBackingStoreStrongly(
+ reinterpret_cast<HeapHashTableBacking<HashTable>*>(backing),
+ reinterpret_cast<HeapHashTableBacking<HashTable>**>(backing_slot));
+ }
+
+ template <typename T, typename HashTable>
+ static void TraceHashTableBackingWeakly(Visitor* visitor,
+ T* backing,
+ T** backing_slot,
+ WeakCallback callback,
+ void* parameter) {
+ visitor->TraceBackingStoreWeakly(
+ reinterpret_cast<HeapHashTableBacking<HashTable>*>(backing),
+ reinterpret_cast<HeapHashTableBacking<HashTable>**>(backing_slot),
+ callback, parameter);
+ }
+
+ template <typename T, typename HashTable>
+ static void TraceHashTableBackingOnly(Visitor* visitor,
+ T* backing,
+ T** backing_slot) {
+ visitor->TraceBackingStoreOnly(
+ reinterpret_cast<HeapHashTableBacking<HashTable>*>(backing),
+ reinterpret_cast<HeapHashTableBacking<HashTable>**>(backing_slot));
+ }
+
+ private:
+ static void BackingFree(void*);
+ static bool BackingExpand(void*, size_t);
+ static bool BackingShrink(void*,
+ size_t quantized_current_size,
+ size_t quantized_shrunk_size);
+
+ template <typename T, size_t u, typename V>
+ friend class WTF::Vector;
+ template <typename T, typename U, typename V, typename W>
+ friend class WTF::HashSet;
+ template <typename T,
+ typename U,
+ typename V,
+ typename W,
+ typename X,
+ typename Y>
+ friend class WTF::HashMap;
+};
+
+template <typename VisitorDispatcher, typename Value>
+static void TraceListHashSetValue(VisitorDispatcher visitor, Value& value) {
+ // We use the default hash traits for the value in the node, because
+ // ListHashSet does not let you specify any specific ones.
+ // We don't allow ListHashSet of WeakMember, so we set that one false
+ // (there's an assert elsewhere), but we have to specify some value for the
+ // strongify template argument, so we specify WTF::WeakPointersActWeak,
+ // arbitrarily.
+ TraceCollectionIfEnabled<WTF::kNoWeakHandling, Value,
+ WTF::HashTraits<Value>>::Trace(visitor, value);
+}
+
+// The inline capacity is just a dummy template argument to match the off-heap
+// allocator.
+// This inherits from the static-only HeapAllocator trait class, but we do
+// declare pointers to instances. These pointers are always null, and no
+// objects are instantiated.
+template <typename ValueArg, size_t inlineCapacity>
+class HeapListHashSetAllocator : public HeapAllocator {
+ DISALLOW_NEW();
+
+ public:
+ using TableAllocator = HeapAllocator;
+ using Node = WTF::ListHashSetNode<ValueArg, HeapListHashSetAllocator>;
+
+ class AllocatorProvider {
+ DISALLOW_NEW();
+
+ public:
+ // For the heap allocation we don't need an actual allocator object, so
+ // we just return null.
+ HeapListHashSetAllocator* Get() const { return nullptr; }
+
+ // No allocator object is needed.
+ void CreateAllocatorIfNeeded() {}
+ void ReleaseAllocator() {}
+
+ // There is no allocator object in the HeapListHashSet (unlike in the
+ // regular ListHashSet) so there is nothing to swap.
+ void Swap(AllocatorProvider& other) {}
+ };
+
+ void Deallocate(void* dummy) {}
+
+ // This is not a static method even though it could be, because it needs to
+ // match the one that the (off-heap) ListHashSetAllocator has. The 'this'
+ // pointer will always be null.
+ void* AllocateNode() {
+ // Consider using a LinkedHashSet instead if this compile-time assert fails:
+ static_assert(!WTF::IsWeak<ValueArg>::value,
+ "weak pointers in a ListHashSet will result in null entries "
+ "in the set");
+
+ return Malloc<void*, Node>(
+ sizeof(Node),
+ nullptr /* Oilpan does not use the heap profiler at the moment. */);
+ }
+
+ template <typename VisitorDispatcher>
+ static void TraceValue(VisitorDispatcher visitor, Node* node) {
+ TraceListHashSetValue(visitor, node->value_);
+ }
+};
+
+template <typename T, typename Traits>
+void HeapVectorBacking<T, Traits>::Finalize(void* pointer) {
+ static_assert(Traits::kNeedsDestruction,
+ "Only vector buffers with items requiring destruction should "
+ "be finalized");
+ // See the comment in HeapVectorBacking::trace.
+ static_assert(
+ Traits::kCanClearUnusedSlotsWithMemset || std::is_polymorphic<T>::value,
+ "HeapVectorBacking doesn't support objects that cannot be cleared as "
+ "unused with memset or don't have a vtable");
+
+ DCHECK(!WTF::IsTriviallyDestructible<T>::value);
+ HeapObjectHeader* header = HeapObjectHeader::FromPayload(pointer);
+ // Use the payload size as recorded by the heap to determine how many
+ // elements to finalize.
+ size_t length = header->PayloadSize() / sizeof(T);
+ char* payload = static_cast<char*>(pointer);
+#ifdef ANNOTATE_CONTIGUOUS_CONTAINER
+ ANNOTATE_CHANGE_SIZE(payload, length * sizeof(T), 0, length * sizeof(T));
+#endif
+ // As commented above, HeapVectorBacking calls finalizers for unused slots
+ // (which are already zeroed out).
+ if (std::is_polymorphic<T>::value) {
+ for (unsigned i = 0; i < length; ++i) {
+ char* element = payload + i * sizeof(T);
+ if (blink::VTableInitialized(element))
+ reinterpret_cast<T*>(element)->~T();
+ }
+ } else {
+ T* buffer = reinterpret_cast<T*>(payload);
+ for (unsigned i = 0; i < length; ++i)
+ buffer[i].~T();
+ }
+}
+
+template <typename Table>
+void HeapHashTableBacking<Table>::Finalize(void* pointer) {
+ using Value = typename Table::ValueType;
+ DCHECK(!WTF::IsTriviallyDestructible<Value>::value);
+ HeapObjectHeader* header = HeapObjectHeader::FromPayload(pointer);
+ // Use the payload size as recorded by the heap to determine how many
+ // elements to finalize.
+ size_t length = header->PayloadSize() / sizeof(Value);
+ Value* table = reinterpret_cast<Value*>(pointer);
+ for (unsigned i = 0; i < length; ++i) {
+ if (!Table::IsEmptyOrDeletedBucket(table[i]))
+ table[i].~Value();
+ }
+}
+
+template <typename KeyArg,
+ typename MappedArg,
+ typename HashArg = typename DefaultHash<KeyArg>::Hash,
+ typename KeyTraitsArg = HashTraits<KeyArg>,
+ typename MappedTraitsArg = HashTraits<MappedArg>>
+class HeapHashMap : public HashMap<KeyArg,
+ MappedArg,
+ HashArg,
+ KeyTraitsArg,
+ MappedTraitsArg,
+ HeapAllocator> {
+ IS_GARBAGE_COLLECTED_TYPE();
+ static_assert(WTF::IsTraceable<KeyArg>::value ||
+ WTF::IsTraceable<MappedArg>::value,
+ "For hash maps without traceable elements, use HashMap<> "
+ "instead of HeapHashMap<>");
+};
+
+template <typename ValueArg,
+ typename HashArg = typename DefaultHash<ValueArg>::Hash,
+ typename TraitsArg = HashTraits<ValueArg>>
+class HeapHashSet
+ : public HashSet<ValueArg, HashArg, TraitsArg, HeapAllocator> {
+ IS_GARBAGE_COLLECTED_TYPE();
+ static_assert(WTF::IsTraceable<ValueArg>::value,
+ "For hash sets without traceable elements, use HashSet<> "
+ "instead of HeapHashSet<>");
+};
+
+template <typename ValueArg,
+ typename HashArg = typename DefaultHash<ValueArg>::Hash,
+ typename TraitsArg = HashTraits<ValueArg>>
+class HeapLinkedHashSet
+ : public LinkedHashSet<ValueArg, HashArg, TraitsArg, HeapAllocator> {
+ IS_GARBAGE_COLLECTED_TYPE();
+ static_assert(WTF::IsTraceable<ValueArg>::value,
+ "For sets without traceable elements, use LinkedHashSet<> "
+ "instead of HeapLinkedHashSet<>");
+};
+
+template <typename ValueArg,
+ size_t inlineCapacity = 0, // The inlineCapacity is just a dummy to
+ // match ListHashSet (off-heap).
+ typename HashArg = typename DefaultHash<ValueArg>::Hash>
+class HeapListHashSet
+ : public ListHashSet<ValueArg,
+ inlineCapacity,
+ HashArg,
+ HeapListHashSetAllocator<ValueArg, inlineCapacity>> {
+ IS_GARBAGE_COLLECTED_TYPE();
+ static_assert(WTF::IsTraceable<ValueArg>::value,
+ "For sets without traceable elements, use ListHashSet<> "
+ "instead of HeapListHashSet<>");
+};
+
+template <typename Value,
+ typename HashFunctions = typename DefaultHash<Value>::Hash,
+ typename Traits = HashTraits<Value>>
+class HeapHashCountedSet
+ : public HashCountedSet<Value, HashFunctions, Traits, HeapAllocator> {
+ IS_GARBAGE_COLLECTED_TYPE();
+ static_assert(WTF::IsTraceable<Value>::value,
+ "For counted sets without traceable elements, use "
+ "HashCountedSet<> instead of HeapHashCountedSet<>");
+};
+
+template <typename T, size_t inlineCapacity = 0>
+class HeapVector : public Vector<T, inlineCapacity, HeapAllocator> {
+ IS_GARBAGE_COLLECTED_TYPE();
+
+ public:
+ HeapVector() {
+ static_assert(WTF::IsTraceable<T>::value,
+ "For vectors without traceable elements, use Vector<> "
+ "instead of HeapVector<>");
+ }
+
+ explicit HeapVector(size_t size)
+ : Vector<T, inlineCapacity, HeapAllocator>(size) {}
+
+ HeapVector(size_t size, const T& val)
+ : Vector<T, inlineCapacity, HeapAllocator>(size, val) {}
+
+ template <size_t otherCapacity>
+ HeapVector(const HeapVector<T, otherCapacity>& other)
+ : Vector<T, inlineCapacity, HeapAllocator>(other) {}
+};
+
+template <typename T, size_t inlineCapacity = 0>
+class HeapDeque : public Deque<T, inlineCapacity, HeapAllocator> {
+ IS_GARBAGE_COLLECTED_TYPE();
+
+ public:
+ HeapDeque() {
+ static_assert(WTF::IsTraceable<T>::value,
+ "For vectors without traceable elements, use Deque<> instead "
+ "of HeapDeque<>");
+ }
+
+ explicit HeapDeque(size_t size)
+ : Deque<T, inlineCapacity, HeapAllocator>(size) {}
+
+ HeapDeque(size_t size, const T& val)
+ : Deque<T, inlineCapacity, HeapAllocator>(size, val) {}
+
+ HeapDeque& operator=(const HeapDeque& other) {
+ HeapDeque<T> copy(other);
+ Deque<T, inlineCapacity, HeapAllocator>::Swap(copy);
+ return *this;
+ }
+
+ template <size_t otherCapacity>
+ HeapDeque(const HeapDeque<T, otherCapacity>& other)
+ : Deque<T, inlineCapacity, HeapAllocator>(other) {}
+};
+
+template <typename T>
+class HeapDoublyLinkedList : public DoublyLinkedList<T, Member<T>> {
+ IS_GARBAGE_COLLECTED_TYPE();
+ DISALLOW_NEW();
+
+ public:
+ HeapDoublyLinkedList() {
+ static_assert(WTF::IsGarbageCollectedType<T>::value,
+ "This should only be used for garbage collected types.");
+ }
+
+ void Trace(Visitor* visitor) {
+ visitor->Trace(this->head_);
+ visitor->Trace(this->tail_);
+ }
+};
+
+} // namespace blink
+
+namespace WTF {
+
+template <typename T>
+struct VectorTraits<blink::Member<T>> : VectorTraitsBase<blink::Member<T>> {
+ STATIC_ONLY(VectorTraits);
+ static const bool kNeedsDestruction = false;
+ static const bool kCanInitializeWithMemset = true;
+ static const bool kCanClearUnusedSlotsWithMemset = true;
+ static const bool kCanCopyWithMemcpy = true;
+ static const bool kCanMoveWithMemcpy = true;
+};
+
+template <typename T>
+struct VectorTraits<blink::SameThreadCheckedMember<T>>
+ : VectorTraitsBase<blink::SameThreadCheckedMember<T>> {
+ STATIC_ONLY(VectorTraits);
+ static const bool kNeedsDestruction = false;
+ static const bool kCanInitializeWithMemset = true;
+ static const bool kCanClearUnusedSlotsWithMemset = true;
+ static const bool kCanMoveWithMemcpy = true;
+ static const bool kCanSwapUsingCopyOrMove = false;
+};
+
+template <typename T>
+struct VectorTraits<blink::TraceWrapperMember<T>>
+ : VectorTraitsBase<blink::TraceWrapperMember<T>> {
+ STATIC_ONLY(VectorTraits);
+ static const bool kNeedsDestruction = false;
+ static const bool kCanInitializeWithMemset = true;
+ static const bool kCanClearUnusedSlotsWithMemset = true;
+ static const bool kCanMoveWithMemcpy = true;
+ static const bool kCanSwapUsingCopyOrMove = false;
+};
+
+template <typename T>
+struct VectorTraits<blink::WeakMember<T>>
+ : VectorTraitsBase<blink::WeakMember<T>> {
+ STATIC_ONLY(VectorTraits);
+ static const bool kNeedsDestruction = false;
+ static const bool kCanInitializeWithMemset = true;
+ static const bool kCanClearUnusedSlotsWithMemset = true;
+ static const bool kCanMoveWithMemcpy = true;
+};
+
+template <typename T>
+struct VectorTraits<blink::UntracedMember<T>>
+ : VectorTraitsBase<blink::UntracedMember<T>> {
+ STATIC_ONLY(VectorTraits);
+ static const bool kNeedsDestruction = false;
+ static const bool kCanInitializeWithMemset = true;
+ static const bool kCanClearUnusedSlotsWithMemset = true;
+ static const bool kCanMoveWithMemcpy = true;
+};
+
+template <
+ typename T,
+ blink::WeaknessPersistentConfiguration weaknessConfiguration,
+ blink::CrossThreadnessPersistentConfiguration crossThreadnessConfiguration>
+struct VectorTraits<blink::PersistentBase<T,
+ weaknessConfiguration,
+ crossThreadnessConfiguration>>
+ : VectorTraitsBase<blink::PersistentBase<T,
+ weaknessConfiguration,
+ crossThreadnessConfiguration>> {
+ STATIC_ONLY(VectorTraits);
+ static const bool kNeedsDestruction = true;
+ static const bool kCanInitializeWithMemset = true;
+ static const bool kCanClearUnusedSlotsWithMemset = false;
+ static const bool kCanMoveWithMemcpy = true;
+};
+
+template <typename T>
+struct VectorTraits<blink::HeapVector<T, 0>>
+ : VectorTraitsBase<blink::HeapVector<T, 0>> {
+ STATIC_ONLY(VectorTraits);
+ static const bool kNeedsDestruction = false;
+ static const bool kCanInitializeWithMemset = true;
+ static const bool kCanClearUnusedSlotsWithMemset = true;
+ static const bool kCanMoveWithMemcpy = true;
+};
+
+template <typename T>
+struct VectorTraits<blink::HeapDeque<T, 0>>
+ : VectorTraitsBase<blink::HeapDeque<T, 0>> {
+ STATIC_ONLY(VectorTraits);
+ static const bool kNeedsDestruction = false;
+ static const bool kCanInitializeWithMemset = true;
+ static const bool kCanClearUnusedSlotsWithMemset = true;
+ static const bool kCanMoveWithMemcpy = true;
+};
+
+template <typename T, size_t inlineCapacity>
+struct VectorTraits<blink::HeapVector<T, inlineCapacity>>
+ : VectorTraitsBase<blink::HeapVector<T, inlineCapacity>> {
+ STATIC_ONLY(VectorTraits);
+ static const bool kNeedsDestruction = VectorTraits<T>::kNeedsDestruction;
+ static const bool kCanInitializeWithMemset =
+ VectorTraits<T>::kCanInitializeWithMemset;
+ static const bool kCanClearUnusedSlotsWithMemset =
+ VectorTraits<T>::kCanClearUnusedSlotsWithMemset;
+ static const bool kCanMoveWithMemcpy = VectorTraits<T>::kCanMoveWithMemcpy;
+};
+
+template <typename T, size_t inlineCapacity>
+struct VectorTraits<blink::HeapDeque<T, inlineCapacity>>
+ : VectorTraitsBase<blink::HeapDeque<T, inlineCapacity>> {
+ STATIC_ONLY(VectorTraits);
+ static const bool kNeedsDestruction = VectorTraits<T>::kNeedsDestruction;
+ static const bool kCanInitializeWithMemset =
+ VectorTraits<T>::kCanInitializeWithMemset;
+ static const bool kCanClearUnusedSlotsWithMemset =
+ VectorTraits<T>::kCanClearUnusedSlotsWithMemset;
+ static const bool kCanMoveWithMemcpy = VectorTraits<T>::kCanMoveWithMemcpy;
+};
+
+template <typename T>
+struct HashTraits<blink::Member<T>> : SimpleClassHashTraits<blink::Member<T>> {
+ STATIC_ONLY(HashTraits);
+ // FIXME: Implement proper const'ness for iterator types. Requires support
+ // in the marking Visitor.
+ using PeekInType = T*;
+ using IteratorGetType = blink::Member<T>*;
+ using IteratorConstGetType = const blink::Member<T>*;
+ using IteratorReferenceType = blink::Member<T>&;
+ using IteratorConstReferenceType = const blink::Member<T>&;
+ static IteratorReferenceType GetToReferenceConversion(IteratorGetType x) {
+ return *x;
+ }
+ static IteratorConstReferenceType GetToReferenceConstConversion(
+ IteratorConstGetType x) {
+ return *x;
+ }
+
+ using PeekOutType = T*;
+
+ template <typename U>
+ static void Store(const U& value, blink::Member<T>& storage) {
+ storage = value;
+ }
+
+ static PeekOutType Peek(const blink::Member<T>& value) { return value; }
+
+ static void ConstructDeletedValue(blink::Member<T>& slot, bool) {
+ slot = WTF::kHashTableDeletedValue;
+ }
+ static bool IsDeletedValue(const blink::Member<T>& value) {
+ return value.IsHashTableDeletedValue();
+ }
+};
+
+template <typename T>
+struct HashTraits<blink::SameThreadCheckedMember<T>>
+ : SimpleClassHashTraits<blink::SameThreadCheckedMember<T>> {
+ STATIC_ONLY(HashTraits);
+ // FIXME: Implement proper const'ness for iterator types. Requires support
+ // in the marking Visitor.
+ using PeekInType = T*;
+ using IteratorGetType = blink::SameThreadCheckedMember<T>*;
+ using IteratorConstGetType = const blink::SameThreadCheckedMember<T>*;
+ using IteratorReferenceType = blink::SameThreadCheckedMember<T>&;
+ using IteratorConstReferenceType = const blink::SameThreadCheckedMember<T>&;
+ static IteratorReferenceType GetToReferenceConversion(IteratorGetType x) {
+ return *x;
+ }
+ static IteratorConstReferenceType GetToReferenceConstConversion(
+ IteratorConstGetType x) {
+ return *x;
+ }
+
+ using PeekOutType = T*;
+
+ template <typename U>
+ static void Store(const U& value,
+ blink::SameThreadCheckedMember<T>& storage) {
+ storage = value;
+ }
+
+ static PeekOutType Peek(const blink::SameThreadCheckedMember<T>& value) {
+ return value;
+ }
+
+ static blink::SameThreadCheckedMember<T> EmptyValue() {
+ return blink::SameThreadCheckedMember<T>(nullptr, nullptr);
+ }
+};
+
+template <typename T>
+struct HashTraits<blink::TraceWrapperMember<T>>
+ : SimpleClassHashTraits<blink::TraceWrapperMember<T>> {
+ STATIC_ONLY(HashTraits);
+ // FIXME: Implement proper const'ness for iterator types. Requires support
+ // in the marking Visitor.
+ using PeekInType = T*;
+ using IteratorGetType = blink::TraceWrapperMember<T>*;
+ using IteratorConstGetType = const blink::TraceWrapperMember<T>*;
+ using IteratorReferenceType = blink::TraceWrapperMember<T>&;
+ using IteratorConstReferenceType = const blink::TraceWrapperMember<T>&;
+ static IteratorReferenceType GetToReferenceConversion(IteratorGetType x) {
+ return *x;
+ }
+ static IteratorConstReferenceType GetToReferenceConstConversion(
+ IteratorConstGetType x) {
+ return *x;
+ }
+
+ using PeekOutType = T*;
+
+ template <typename U>
+ static void Store(const U& value, blink::TraceWrapperMember<T>& storage) {
+ storage = value;
+ }
+
+ static PeekOutType Peek(const blink::TraceWrapperMember<T>& value) {
+ return value;
+ }
+
+ static blink::TraceWrapperMember<T> EmptyValue() { return nullptr; }
+};
+
+template <typename T>
+struct HashTraits<blink::WeakMember<T>>
+ : SimpleClassHashTraits<blink::WeakMember<T>> {
+ STATIC_ONLY(HashTraits);
+ static const bool kNeedsDestruction = false;
+ // FIXME: Implement proper const'ness for iterator types. Requires support
+ // in the marking Visitor.
+ using PeekInType = T*;
+ using IteratorGetType = blink::WeakMember<T>*;
+ using IteratorConstGetType = const blink::WeakMember<T>*;
+ using IteratorReferenceType = blink::WeakMember<T>&;
+ using IteratorConstReferenceType = const blink::WeakMember<T>&;
+ static IteratorReferenceType GetToReferenceConversion(IteratorGetType x) {
+ return *x;
+ }
+ static IteratorConstReferenceType GetToReferenceConstConversion(
+ IteratorConstGetType x) {
+ return *x;
+ }
+
+ using PeekOutType = T*;
+
+ template <typename U>
+ static void Store(const U& value, blink::WeakMember<T>& storage) {
+ storage = value;
+ }
+
+ static PeekOutType Peek(const blink::WeakMember<T>& value) { return value; }
+
+ static bool IsAlive(blink::WeakMember<T>& weak_member) {
+ return blink::ThreadHeap::IsHeapObjectAlive(weak_member);
+ }
+
+ template <typename VisitorDispatcher>
+ static bool TraceInCollection(VisitorDispatcher visitor,
+ blink::WeakMember<T>& weak_member,
+ WeakHandlingFlag weakness) {
+ if (weakness == kNoWeakHandling) {
+ visitor->Trace(weak_member.Get()); // Strongified visit.
+ return false;
+ }
+ return !blink::ThreadHeap::IsHeapObjectAlive(weak_member);
+ }
+};
+
+template <typename T>
+struct HashTraits<blink::UntracedMember<T>>
+ : SimpleClassHashTraits<blink::UntracedMember<T>> {
+ STATIC_ONLY(HashTraits);
+ static const bool kNeedsDestruction = false;
+ // FIXME: Implement proper const'ness for iterator types.
+ using PeekInType = T*;
+ using IteratorGetType = blink::UntracedMember<T>*;
+ using IteratorConstGetType = const blink::UntracedMember<T>*;
+ using IteratorReferenceType = blink::UntracedMember<T>&;
+ using IteratorConstReferenceType = const blink::UntracedMember<T>&;
+ static IteratorReferenceType GetToReferenceConversion(IteratorGetType x) {
+ return *x;
+ }
+ static IteratorConstReferenceType GetToReferenceConstConversion(
+ IteratorConstGetType x) {
+ return *x;
+ }
+ using PeekOutType = T*;
+
+ template <typename U>
+ static void Store(const U& value, blink::UntracedMember<T>& storage) {
+ storage = value;
+ }
+
+ static PeekOutType Peek(const blink::UntracedMember<T>& value) {
+ return value;
+ }
+};
+
+template <typename T, size_t inlineCapacity>
+struct IsTraceable<
+ ListHashSetNode<T, blink::HeapListHashSetAllocator<T, inlineCapacity>>*> {
+ STATIC_ONLY(IsTraceable);
+ static_assert(sizeof(T), "T must be fully defined");
+ // All heap allocated node pointers need visiting to keep the nodes alive,
+ // regardless of whether they contain pointers to other heap allocated
+ // objects.
+ static const bool value = true;
+};
+
+template <typename T, size_t inlineCapacity>
+struct IsGarbageCollectedType<
+ ListHashSetNode<T, blink::HeapListHashSetAllocator<T, inlineCapacity>>> {
+ static const bool value = true;
+};
+
+template <typename Set>
+struct IsGarbageCollectedType<ListHashSetIterator<Set>> {
+ static const bool value = IsGarbageCollectedType<Set>::value;
+};
+
+template <typename Set>
+struct IsGarbageCollectedType<ListHashSetConstIterator<Set>> {
+ static const bool value = IsGarbageCollectedType<Set>::value;
+};
+
+template <typename Set>
+struct IsGarbageCollectedType<ListHashSetReverseIterator<Set>> {
+ static const bool value = IsGarbageCollectedType<Set>::value;
+};
+
+template <typename Set>
+struct IsGarbageCollectedType<ListHashSetConstReverseIterator<Set>> {
+ static const bool value = IsGarbageCollectedType<Set>::value;
+};
+
+template <typename T, typename H>
+struct HandleHashTraits : SimpleClassHashTraits<H> {
+ STATIC_ONLY(HandleHashTraits);
+ // TODO: Implement proper const'ness for iterator types. Requires support
+ // in the marking Visitor.
+ using PeekInType = T*;
+ using IteratorGetType = H*;
+ using IteratorConstGetType = const H*;
+ using IteratorReferenceType = H&;
+ using IteratorConstReferenceType = const H&;
+ static IteratorReferenceType GetToReferenceConversion(IteratorGetType x) {
+ return *x;
+ }
+ static IteratorConstReferenceType GetToReferenceConstConversion(
+ IteratorConstGetType x) {
+ return *x;
+ }
+
+ using PeekOutType = T*;
+
+ template <typename U>
+ static void Store(const U& value, H& storage) {
+ storage = value;
+ }
+
+ static PeekOutType Peek(const H& value) { return value; }
+};
+
+template <typename T>
+struct HashTraits<blink::Persistent<T>>
+ : HandleHashTraits<T, blink::Persistent<T>> {};
+
+template <typename T>
+struct HashTraits<blink::CrossThreadPersistent<T>>
+ : HandleHashTraits<T, blink::CrossThreadPersistent<T>> {};
+
+template <typename Value,
+ typename HashFunctions,
+ typename Traits,
+ typename VectorType>
+inline void CopyToVector(
+ const blink::HeapHashCountedSet<Value, HashFunctions, Traits>& set,
+ VectorType& vector) {
+ CopyToVector(static_cast<const HashCountedSet<Value, HashFunctions, Traits,
+ blink::HeapAllocator>&>(set),
+ vector);
+}
+
+} // namespace WTF
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/heap/heap_compact.cc b/chromium/third_party/blink/renderer/platform/heap/heap_compact.cc
new file mode 100644
index 00000000000..7bff2f4046b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/heap_compact.cc
@@ -0,0 +1,478 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/heap/heap_compact.h"
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/heap/sparse_heap_bitmap.h"
+#include "third_party/blink/renderer/platform/histogram.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+bool HeapCompact::force_compaction_gc_ = false;
+
+// The real worker behind heap compaction, recording references to movable
+// objects ("slots".) When the objects end up being compacted and moved,
+// relocate() will adjust the slots to point to the new location of the
+// object along with handling fixups for interior pointers.
+//
+// The "fixups" object is created and maintained for the lifetime of one
+// heap compaction-enhanced GC.
+class HeapCompact::MovableObjectFixups final {
+ public:
+ static std::unique_ptr<MovableObjectFixups> Create() {
+ return base::WrapUnique(new MovableObjectFixups);
+ }
+
+ ~MovableObjectFixups() = default;
+
+ // For the arenas being compacted, record all pages belonging to them.
+ // This is needed to handle 'interior slots', pointers that themselves
+ // can move (independently from the reference the slot points to.)
+ void AddCompactingPage(BasePage* page) {
+ DCHECK(!page->IsLargeObjectPage());
+ relocatable_pages_.insert(page);
+ }
+
+ void AddInteriorFixup(MovableReference* slot) {
+ auto it = interior_fixups_.find(slot);
+ // Ephemeron fixpoint iterations may cause repeated registrations.
+ if (UNLIKELY(it != interior_fixups_.end())) {
+ DCHECK(!it->value);
+ return;
+ }
+ interior_fixups_.insert(slot, nullptr);
+ LOG_HEAP_COMPACTION() << "Interior slot: " << slot;
+ Address slot_address = reinterpret_cast<Address>(slot);
+ if (!interiors_) {
+ interiors_ = SparseHeapBitmap::Create(slot_address);
+ return;
+ }
+ interiors_->Add(slot_address);
+ }
+
+ void Add(MovableReference* slot) {
+ MovableReference reference = *slot;
+ BasePage* ref_page = PageFromObject(reference);
+ // Nothing to compact on a large object's page.
+ if (ref_page->IsLargeObjectPage())
+ return;
+
+#if DCHECK_IS_ON()
+ DCHECK(HeapCompact::IsCompactableArena(ref_page->Arena()->ArenaIndex()));
+ auto it = fixups_.find(reference);
+ DCHECK(it == fixups_.end() || it->value == slot);
+#endif
+
+ // TODO: when updateHeapResidency() becomes more discriminating about
+ // leaving out arenas that aren't worth compacting, a check for
+ // isCompactingArena() would be appropriate here, leaving early if
+ // |refPage|'s arena isn't in the set.
+
+ fixups_.insert(reference, slot);
+
+ // Note: |slot| will reside outside the Oilpan heap if it is a
+ // PersistentHeapCollectionBase. Hence pageFromObject() cannot be
+ // used, as it sanity checks the |BasePage| it returns. Simply
+ // derive the raw BasePage address here and check if it is a member
+ // of the compactable and relocatable page address set.
+ Address slot_address = reinterpret_cast<Address>(slot);
+ void* slot_page_address =
+ BlinkPageAddress(slot_address) + kBlinkGuardPageSize;
+ if (LIKELY(!relocatable_pages_.Contains(slot_page_address)))
+ return;
+#if DCHECK_IS_ON()
+ BasePage* slot_page = reinterpret_cast<BasePage*>(slot_page_address);
+ DCHECK(slot_page->Contains(slot_address));
+#endif
+ // Unlikely case, the slot resides on a compacting arena's page.
+ // => It is an 'interior slot' (interior to a movable backing store.)
+ // Record it as an interior slot, which entails:
+ //
+ // - Storing it in the interior map, which maps the slot to
+ // its (eventual) location. Initially nullptr.
+ // - Mark it as being interior pointer within the page's
+ // "interior" bitmap. This bitmap is used when moving a backing
+ // store, quickly/ier checking if interior slots will have to
+ // be additionally redirected.
+ AddInteriorFixup(slot);
+ }
+
+ void AddFixupCallback(MovableReference reference,
+ MovingObjectCallback callback,
+ void* callback_data) {
+ DCHECK(!fixup_callbacks_.Contains(reference));
+ fixup_callbacks_.insert(reference, std::pair<void*, MovingObjectCallback>(
+ callback_data, callback));
+ }
+
+ void RelocateInteriorFixups(Address from, Address to, size_t size) {
+ SparseHeapBitmap* range = interiors_->HasRange(from, size);
+ if (LIKELY(!range))
+ return;
+
+ // Scan through the payload, looking for interior pointer slots
+ // to adjust. If the backing store of such an interior slot hasn't
+ // been moved already, update the slot -> real location mapping.
+ // When the backing store is eventually moved, it'll use that location.
+ //
+ for (size_t offset = 0; offset < size; offset += sizeof(void*)) {
+ if (!range->IsSet(from + offset))
+ continue;
+ MovableReference* slot =
+ reinterpret_cast<MovableReference*>(from + offset);
+ auto it = interior_fixups_.find(slot);
+ if (it == interior_fixups_.end())
+ continue;
+
+ // TODO: with the right sparse bitmap representation, it could be possible
+ // to quickly determine if we've now stepped past the last address
+ // that needed fixup in [address, address + size). Breaking out of this
+ // loop might be worth doing for hash table backing stores with a very
+ // low load factor. But interior fixups are rare.
+
+ // If |slot|'s mapping is set, then the slot has been adjusted already.
+ if (it->value)
+ continue;
+ Address fixup = to + offset;
+ LOG_HEAP_COMPACTION() << "Range interior fixup: " << (from + offset)
+ << " " << it->value << " " << fixup;
+ // Fill in the relocated location of the original slot at |slot|.
+ // when the backing store corresponding to |slot| is eventually
+ // moved/compacted, it'll update |to + offset| with a pointer to the
+ // moved backing store.
+ interior_fixups_.Set(slot, fixup);
+ }
+ }
+
+ void Relocate(Address from, Address to) {
+ auto it = fixups_.find(from);
+ DCHECK(it != fixups_.end());
+#if DCHECK_IS_ON()
+ BasePage* from_page = PageFromObject(from);
+ DCHECK(relocatable_pages_.Contains(from_page));
+#endif
+ MovableReference* slot = reinterpret_cast<MovableReference*>(it->value);
+ auto interior = interior_fixups_.find(slot);
+ if (interior != interior_fixups_.end()) {
+ MovableReference* slot_location =
+ reinterpret_cast<MovableReference*>(interior->value);
+ if (!slot_location) {
+ interior_fixups_.Set(slot, to);
+ } else {
+ LOG_HEAP_COMPACTION()
+ << "Redirected slot: " << slot << " => " << slot_location;
+ slot = slot_location;
+ }
+ }
+ // If the slot has subsequently been updated, a prefinalizer or
+ // a destructor having mutated and expanded/shrunk the collection,
+ // do not update and relocate the slot -- |from| is no longer valid
+ // and referenced.
+ //
+ // The slot's contents may also have been cleared during weak processing;
+ // no work to be done in that case either.
+ if (UNLIKELY(*slot != from)) {
+ LOG_HEAP_COMPACTION()
+ << "No relocation: slot = " << slot << ", *slot = " << *slot
+ << ", from = " << from << ", to = " << to;
+#if DCHECK_IS_ON()
+ // Verify that the already updated slot is valid, meaning:
+ // - has been cleared.
+ // - has been updated & expanded with a large object backing store.
+ // - has been updated with a larger, freshly allocated backing store.
+ // (on a fresh page in a compactable arena that is not being
+ // compacted.)
+ if (!*slot)
+ return;
+ BasePage* slot_page = PageFromObject(*slot);
+ DCHECK(
+ slot_page->IsLargeObjectPage() ||
+ (HeapCompact::IsCompactableArena(slot_page->Arena()->ArenaIndex()) &&
+ !relocatable_pages_.Contains(slot_page)));
+#endif
+ return;
+ }
+ *slot = to;
+
+ size_t size = 0;
+ auto callback = fixup_callbacks_.find(from);
+ if (UNLIKELY(callback != fixup_callbacks_.end())) {
+ size = HeapObjectHeader::FromPayload(to)->PayloadSize();
+ callback->value.second(callback->value.first, from, to, size);
+ }
+
+ if (!interiors_)
+ return;
+
+ if (!size)
+ size = HeapObjectHeader::FromPayload(to)->PayloadSize();
+ RelocateInteriorFixups(from, to, size);
+ }
+
+#if DEBUG_HEAP_COMPACTION
+ void dumpDebugStats() {
+ LOG_HEAP_COMPACTION() << "Fixups: pages=" << relocatable_pages_.size()
+ << " objects=" << fixups_.size()
+ << " callbacks=" << fixup_callbacks_.size()
+ << " interior-size="
+ << (interiors_ ? interiors_->IntervalCount() : 0,
+ interior_fixups_.size());
+ }
+#endif
+
+ private:
+ MovableObjectFixups() = default;
+
+ // Tracking movable and updatable references. For now, we keep a
+ // map which for each movable object, recording the slot that
+ // points to it. Upon moving the object, that slot needs to be
+ // updated.
+ //
+ // (TODO: consider in-place updating schemes.)
+ HashMap<MovableReference, MovableReference*> fixups_;
+
+ // Map from movable reference to callbacks that need to be invoked
+ // when the object moves.
+ HashMap<MovableReference, std::pair<void*, MovingObjectCallback>>
+ fixup_callbacks_;
+
+ // Slot => relocated slot/final location.
+ HashMap<MovableReference*, Address> interior_fixups_;
+
+ // All pages that are being compacted. The set keeps references to
+ // BasePage instances. The void* type was selected to allow to check
+ // arbitrary addresses.
+ HashSet<void*> relocatable_pages_;
+
+ std::unique_ptr<SparseHeapBitmap> interiors_;
+};
+
+HeapCompact::HeapCompact()
+ : do_compact_(false),
+ gc_count_since_last_compaction_(0),
+ free_list_size_(0),
+ compactable_arenas_(0u),
+ freed_pages_(0),
+ freed_size_(0),
+ start_compaction_time_ms_(0) {
+ // The heap compaction implementation assumes the contiguous range,
+ //
+ // [Vector1ArenaIndex, HashTableArenaIndex]
+ //
+ // in a few places. Use static asserts here to not have that assumption
+ // be silently invalidated by ArenaIndices changes.
+ static_assert(BlinkGC::kVector1ArenaIndex + 3 == BlinkGC::kVector4ArenaIndex,
+ "unexpected ArenaIndices ordering");
+ static_assert(
+ BlinkGC::kVector4ArenaIndex + 1 == BlinkGC::kInlineVectorArenaIndex,
+ "unexpected ArenaIndices ordering");
+ static_assert(
+ BlinkGC::kInlineVectorArenaIndex + 1 == BlinkGC::kHashTableArenaIndex,
+ "unexpected ArenaIndices ordering");
+}
+
+HeapCompact::~HeapCompact() = default;
+
+HeapCompact::MovableObjectFixups& HeapCompact::Fixups() {
+ if (!fixups_)
+ fixups_ = MovableObjectFixups::Create();
+ return *fixups_;
+}
+
+bool HeapCompact::ShouldCompact(ThreadHeap* heap,
+ BlinkGC::StackState stack_state,
+ BlinkGC::MarkingType marking_type,
+ BlinkGC::GCReason reason) {
+#if !ENABLE_HEAP_COMPACTION
+ return false;
+#else
+ if (!RuntimeEnabledFeatures::HeapCompactionEnabled())
+ return false;
+
+ LOG_HEAP_COMPACTION() << "shouldCompact(): gc=" << reason
+ << " count=" << gc_count_since_last_compaction_
+ << " free=" << free_list_size_;
+ gc_count_since_last_compaction_++;
+ // It is only safe to compact during non-conservative GCs.
+ // TODO: for the main thread, limit this further to only idle GCs.
+ if (reason != BlinkGC::kIdleGC && reason != BlinkGC::kPreciseGC &&
+ reason != BlinkGC::kForcedGC)
+ return false;
+
+ // If the GCing thread requires a stack scan, do not compact.
+ // Why? Should the stack contain an iterator pointing into its
+ // associated backing store, its references wouldn't be
+ // correctly relocated.
+ if (stack_state == BlinkGC::kHeapPointersOnStack)
+ return false;
+
+ // TODO(keishi): Should be enable after fixing the crashes.
+ if (marking_type == BlinkGC::kIncrementalMarking)
+ return false;
+
+ // Compaction enable rules:
+ // - It's been a while since the last time.
+ // - "Considerable" amount of heap memory is bound up in freelist
+ // allocations. For now, use a fixed limit irrespective of heap
+ // size.
+ //
+ // As this isn't compacting all arenas, the cost of doing compaction
+ // isn't a worry as it will additionally only be done by idle GCs.
+ // TODO: add some form of compaction overhead estimate to the marking
+ // time estimate.
+
+ UpdateHeapResidency(heap);
+
+#if STRESS_TEST_HEAP_COMPACTION
+ // Exercise the handling of object movement by compacting as
+ // often as possible.
+ return true;
+#else
+ return force_compaction_gc_ || (gc_count_since_last_compaction_ >
+ kGCCountSinceLastCompactionThreshold &&
+ free_list_size_ > kFreeListSizeThreshold);
+#endif
+#endif
+}
+
+void HeapCompact::Initialize(ThreadState* state) {
+ DCHECK(RuntimeEnabledFeatures::HeapCompactionEnabled());
+ LOG_HEAP_COMPACTION() << "Compacting: free=" << free_list_size_;
+ do_compact_ = true;
+ freed_pages_ = 0;
+ freed_size_ = 0;
+ fixups_.reset();
+ gc_count_since_last_compaction_ = 0;
+ force_compaction_gc_ = false;
+}
+
+void HeapCompact::RegisterMovingObjectReference(MovableReference* slot) {
+ if (!do_compact_)
+ return;
+
+ Fixups().Add(slot);
+}
+
+void HeapCompact::RegisterMovingObjectCallback(MovableReference reference,
+ MovingObjectCallback callback,
+ void* callback_data) {
+ if (!do_compact_)
+ return;
+
+ Fixups().AddFixupCallback(reference, callback, callback_data);
+}
+
+void HeapCompact::UpdateHeapResidency(ThreadHeap* heap) {
+ size_t total_arena_size = 0;
+ size_t total_free_list_size = 0;
+
+ compactable_arenas_ = 0;
+#if DEBUG_HEAP_FREELIST
+ std::stringstream stream;
+#endif
+ for (int i = BlinkGC::kVector1ArenaIndex; i <= BlinkGC::kHashTableArenaIndex;
+ ++i) {
+ NormalPageArena* arena = static_cast<NormalPageArena*>(heap->Arena(i));
+ size_t arena_size = arena->ArenaSize();
+ size_t free_list_size = arena->FreeListSize();
+ total_arena_size += arena_size;
+ total_free_list_size += free_list_size;
+#if DEBUG_HEAP_FREELIST
+ stream << i << ": [" << arena_size << ", " << free_list_size << "], ";
+#endif
+ // TODO: be more discriminating and consider arena
+ // load factor, effectiveness of past compactions etc.
+ if (!arena_size)
+ continue;
+ // Mark the arena as compactable.
+ compactable_arenas_ |= 0x1u << i;
+ }
+#if DEBUG_HEAP_FREELIST
+ LOG_HEAP_FREELIST() << "Arena residencies: {" << stream.str() << "}";
+ LOG_HEAP_FREELIST() << "Total = " << total_arena_size
+ << ", Free = " << total_free_list_size;
+#endif
+
+ // TODO(sof): consider smoothing the reported sizes.
+ free_list_size_ = total_free_list_size;
+}
+
+void HeapCompact::FinishedArenaCompaction(NormalPageArena* arena,
+ size_t freed_pages,
+ size_t freed_size) {
+ if (!do_compact_)
+ return;
+
+ freed_pages_ += freed_pages;
+ freed_size_ += freed_size;
+}
+
+void HeapCompact::Relocate(Address from, Address to) {
+ DCHECK(fixups_);
+ fixups_->Relocate(from, to);
+}
+
+void HeapCompact::StartThreadCompaction() {
+ if (!do_compact_)
+ return;
+
+ if (!start_compaction_time_ms_)
+ start_compaction_time_ms_ = WTF::CurrentTimeTicksInMilliseconds();
+}
+
+void HeapCompact::FinishThreadCompaction() {
+ if (!do_compact_)
+ return;
+
+#if DEBUG_HEAP_COMPACTION
+ if (fixups_)
+ fixups_->dumpDebugStats();
+#endif
+ fixups_.reset();
+ do_compact_ = false;
+
+ double time_for_heap_compaction =
+ WTF::CurrentTimeTicksInMilliseconds() - start_compaction_time_ms_;
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(
+ CustomCountHistogram, time_for_heap_compaction_histogram,
+ ("BlinkGC.TimeForHeapCompaction", 1, 10 * 1000, 50));
+ time_for_heap_compaction_histogram.Count(time_for_heap_compaction);
+ start_compaction_time_ms_ = 0;
+
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(
+ CustomCountHistogram, object_size_freed_by_heap_compaction,
+ ("BlinkGC.ObjectSizeFreedByHeapCompaction", 1, 4 * 1024 * 1024, 50));
+ object_size_freed_by_heap_compaction.Count(freed_size_ / 1024);
+
+#if DEBUG_LOG_HEAP_COMPACTION_RUNNING_TIME
+ LOG_HEAP_COMPACTION_INTERNAL()
+ << "Compaction stats: time=" << time_for_heap_compaction
+ << "ms, pages freed=" << freed_pages_ << ", size=" << freed_size_;
+#else
+ LOG_HEAP_COMPACTION() << "Compaction stats: freed pages=" << freed_pages_
+ << " size=" << freed_size_;
+#endif
+}
+
+void HeapCompact::AddCompactingPage(BasePage* page) {
+ DCHECK(do_compact_);
+ DCHECK(IsCompactingArena(page->Arena()->ArenaIndex()));
+ Fixups().AddCompactingPage(page);
+}
+
+bool HeapCompact::ScheduleCompactionGCForTesting(bool value) {
+ bool current = force_compaction_gc_;
+ force_compaction_gc_ = value;
+ return current;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/heap/heap_compact.h b/chromium/third_party/blink/renderer/platform/heap/heap_compact.h
new file mode 100644
index 00000000000..30d489ef7f2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/heap_compact.h
@@ -0,0 +1,199 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_COMPACT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_COMPACT_H_
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/platform/heap/blink_gc.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
+
+#include <bitset>
+#include <utility>
+
+// Compaction-specific debug switches:
+
+// Set to 0 to prevent compaction GCs, disabling the heap compaction feature.
+#define ENABLE_HEAP_COMPACTION 1
+
+// Emit debug info during compaction.
+#define DEBUG_HEAP_COMPACTION 0
+
+// Emit stats on freelist occupancy.
+// 0 - disabled, 1 - minimal, 2 - verbose.
+#define DEBUG_HEAP_FREELIST 0
+
+// Log the amount of time spent compacting.
+#define DEBUG_LOG_HEAP_COMPACTION_RUNNING_TIME 0
+
+// Compact during all idle + precise GCs; for debugging.
+#define STRESS_TEST_HEAP_COMPACTION 0
+
+namespace blink {
+
+class NormalPageArena;
+class BasePage;
+class ThreadState;
+class ThreadHeap;
+
+class PLATFORM_EXPORT HeapCompact final {
+ public:
+ static std::unique_ptr<HeapCompact> Create() {
+ return base::WrapUnique(new HeapCompact);
+ }
+
+ ~HeapCompact();
+
+ // Determine if a GC for the given type and reason should also perform
+ // additional heap compaction.
+ //
+ bool ShouldCompact(ThreadHeap*,
+ BlinkGC::StackState,
+ BlinkGC::MarkingType,
+ BlinkGC::GCReason);
+
+ // Compaction should be performed as part of the ongoing GC, initialize
+ // the heap compaction pass. Returns the appropriate visitor type to
+ // use when running the marking phase.
+ void Initialize(ThreadState*);
+
+ // Returns true if the ongoing GC will perform compaction.
+ bool IsCompacting() const { return do_compact_; }
+
+ // Returns true if the ongoing GC will perform compaction for the given
+ // heap arena.
+ bool IsCompactingArena(int arena_index) const {
+ return do_compact_ && (compactable_arenas_ & (0x1u << arena_index));
+ }
+
+ // Returns |true| if the ongoing GC may compact the given arena/sub-heap.
+ static bool IsCompactableArena(int arena_index) {
+ return arena_index >= BlinkGC::kVector1ArenaIndex &&
+ arena_index <= BlinkGC::kHashTableArenaIndex;
+ }
+
+ // See |Heap::registerMovingObjectReference()| documentation.
+ void RegisterMovingObjectReference(MovableReference* slot);
+
+ // See |Heap::registerMovingObjectCallback()| documentation.
+ void RegisterMovingObjectCallback(MovableReference,
+ MovingObjectCallback,
+ void* callback_data);
+
+ // Thread signalling that a compaction pass is starting or has
+ // completed.
+ //
+ // A thread participating in a heap GC will wait on the completion
+ // of compaction across all threads. No thread can be allowed to
+ // potentially access another thread's heap arenas while they're
+ // still being compacted.
+ void StartThreadCompaction();
+ void FinishThreadCompaction();
+
+ // Perform any relocation post-processing after having completed compacting
+ // the given arena. The number of pages that were freed together with the
+ // total size (in bytes) of freed heap storage, are passed in as arguments.
+ void FinishedArenaCompaction(NormalPageArena*,
+ size_t freed_pages,
+ size_t freed_size);
+
+ // Register the heap page as containing live objects that will all be
+ // compacted. Registration happens as part of making the arenas ready
+ // for a GC.
+ void AddCompactingPage(BasePage*);
+
+ // Notify heap compaction that object at |from| has been relocated to.. |to|.
+ // (Called by the sweep compaction pass.)
+ void Relocate(Address from, Address to);
+
+ // For unit testing only: arrange for a compaction GC to be triggered
+ // next time a non-conservative GC is run. Sets the compact-next flag
+ // to the new value, returning old.
+ static bool ScheduleCompactionGCForTesting(bool);
+
+ // Test-only: verify that one or more of the vector arenas are
+ // in the process of being compacted.
+ bool IsCompactingVectorArenas() {
+ for (int i = BlinkGC::kVector1ArenaIndex; i <= BlinkGC::kVector4ArenaIndex;
+ ++i) {
+ if (IsCompactingArena(i))
+ return true;
+ }
+ return false;
+ }
+
+ private:
+ class MovableObjectFixups;
+
+ HeapCompact();
+
+ // Sample the amount of fragmentation and heap memory currently residing
+ // on the freelists of the arenas we're able to compact. The computed
+ // numbers will be subsequently used to determine if a heap compaction
+ // is on order (shouldCompact().)
+ void UpdateHeapResidency(ThreadHeap*);
+
+ // Parameters controlling when compaction should be done:
+
+ // Number of GCs that must have passed since last compaction GC.
+ static const int kGCCountSinceLastCompactionThreshold = 10;
+
+ // Freelist size threshold that must be exceeded before compaction
+ // should be considered.
+ static const size_t kFreeListSizeThreshold = 512 * 1024;
+
+ MovableObjectFixups& Fixups();
+
+ std::unique_ptr<MovableObjectFixups> fixups_;
+
+ // Set to |true| when a compacting sweep will go ahead.
+ bool do_compact_;
+ size_t gc_count_since_last_compaction_;
+
+ // Last reported freelist size, across all compactable arenas.
+ size_t free_list_size_;
+
+ // If compacting, i'th heap arena will be compacted
+ // if corresponding bit is set. Indexes are in
+ // the range of BlinkGC::ArenaIndices.
+ unsigned compactable_arenas_;
+
+ // Stats, number of (complete) pages freed/decommitted +
+ // bytes freed (which will include partial pages.)
+ size_t freed_pages_;
+ size_t freed_size_;
+
+ double start_compaction_time_ms_;
+
+ static bool force_compaction_gc_;
+};
+
+} // namespace blink
+
+// Logging macros activated by debug switches.
+
+#define LOG_HEAP_COMPACTION_INTERNAL() DLOG(INFO)
+
+#if DEBUG_HEAP_COMPACTION
+#define LOG_HEAP_COMPACTION() LOG_HEAP_COMPACTION_INTERNAL()
+#else
+#define LOG_HEAP_COMPACTION() EAT_STREAM_PARAMETERS
+#endif
+
+#if DEBUG_HEAP_FREELIST
+#define LOG_HEAP_FREELIST() LOG_HEAP_COMPACTION_INTERNAL()
+#else
+#define LOG_HEAP_FREELIST() EAT_STREAM_PARAMETERS
+#endif
+
+#if DEBUG_HEAP_FREELIST == 2
+#define LOG_HEAP_FREELIST_VERBOSE() LOG_HEAP_COMPACTION_INTERNAL()
+#else
+#define LOG_HEAP_FREELIST_VERBOSE() EAT_STREAM_PARAMETERS
+#endif
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_COMPACT_H_
diff --git a/chromium/third_party/blink/renderer/platform/heap/heap_compact_test.cc b/chromium/third_party/blink/renderer/platform/heap/heap_compact_test.cc
new file mode 100644
index 00000000000..2dcb6ab1e90
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/heap_compact_test.cc
@@ -0,0 +1,496 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/heap/heap_compact.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/heap/heap_test_utilities.h"
+#include "third_party/blink/renderer/platform/heap/sparse_heap_bitmap.h"
+#include "third_party/blink/renderer/platform/wtf/deque.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/linked_hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+#include <memory>
+
+namespace {
+
+enum VerifyArenaCompaction {
+ NoVerify,
+ VectorsAreCompacted,
+ HashTablesAreCompacted,
+};
+
+class IntWrapper : public blink::GarbageCollectedFinalized<IntWrapper> {
+ public:
+ static bool did_verify_at_least_once;
+
+ static IntWrapper* Create(int x, VerifyArenaCompaction verify = NoVerify) {
+ did_verify_at_least_once = false;
+ return new IntWrapper(x, verify);
+ }
+
+ virtual ~IntWrapper() = default;
+
+ void Trace(blink::Visitor* visitor) {
+ // Verify if compaction is indeed activated.
+
+ // There may be multiple passes over objects during a GC, even after
+ // compaction is finished. Filter out that cases here.
+ if (!visitor->Heap().Compaction()->IsCompacting())
+ return;
+
+ did_verify_at_least_once = true;
+ // What arenas end up being compacted is dependent on residency,
+ // so approximate the arena checks to fit.
+ blink::HeapCompact* compaction = visitor->Heap().Compaction();
+ switch (verify_) {
+ case NoVerify:
+ return;
+ case HashTablesAreCompacted:
+ CHECK(compaction->IsCompactingArena(
+ blink::BlinkGC::kHashTableArenaIndex));
+ return;
+ case VectorsAreCompacted:
+ CHECK(compaction->IsCompactingVectorArenas());
+ return;
+ }
+ }
+
+ int Value() const { return x_; }
+
+ bool operator==(const IntWrapper& other) const {
+ return other.Value() == Value();
+ }
+
+ unsigned GetHash() { return IntHash<int>::GetHash(x_); }
+
+ IntWrapper(int x, VerifyArenaCompaction verify) : x_(x), verify_(verify) {}
+
+ private:
+ IntWrapper() = delete;
+
+ int x_;
+ VerifyArenaCompaction verify_;
+};
+
+bool IntWrapper::did_verify_at_least_once = false;
+
+static_assert(WTF::IsTraceable<IntWrapper>::value,
+ "IsTraceable<> template failed to recognize trace method.");
+
+} // namespace
+
+#if ENABLE_HEAP_COMPACTION
+
+using IntVector = blink::HeapVector<blink::Member<IntWrapper>>;
+using IntDeque = blink::HeapDeque<blink::Member<IntWrapper>>;
+using IntMap = blink::HeapHashMap<blink::Member<IntWrapper>, int>;
+// TODO(sof): decide if this ought to be a global trait specialization.
+// (i.e., for HeapHash*<T>.)
+WTF_ALLOW_CLEAR_UNUSED_SLOTS_WITH_MEM_FUNCTIONS(IntMap);
+
+namespace blink {
+
+static const size_t kChunkRange = SparseHeapBitmap::kBitmapChunkRange;
+static const size_t kUnitPointer = 0x1u
+ << SparseHeapBitmap::kPointerAlignmentInBits;
+
+TEST(HeapCompactTest, SparseBitmapBasic) {
+ Address base = reinterpret_cast<Address>(0x10000u);
+ std::unique_ptr<SparseHeapBitmap> bitmap = SparseHeapBitmap::Create(base);
+
+ size_t double_chunk = 2 * kChunkRange;
+
+ // 101010... starting at |base|.
+ for (size_t i = 0; i < double_chunk; i += 2 * kUnitPointer)
+ bitmap->Add(base + i);
+
+ // Check that hasRange() returns a bitmap subtree, if any, for a given
+ // address.
+ EXPECT_TRUE(!!bitmap->HasRange(base, 1));
+ EXPECT_TRUE(!!bitmap->HasRange(base + kUnitPointer, 1));
+ EXPECT_FALSE(!!bitmap->HasRange(base - kUnitPointer, 1));
+
+ // Test implementation details.. that each SparseHeapBitmap node maps
+ // |s_bitmapChunkRange| ranges only.
+ EXPECT_EQ(bitmap->HasRange(base + kUnitPointer, 1),
+ bitmap->HasRange(base + 2 * kUnitPointer, 1));
+ // Second range will be just past the first.
+ EXPECT_NE(bitmap->HasRange(base, 1), bitmap->HasRange(base + kChunkRange, 1));
+
+ // Iterate a range that will encompass more than one 'chunk'.
+ SparseHeapBitmap* start =
+ bitmap->HasRange(base + 2 * kUnitPointer, double_chunk);
+ EXPECT_TRUE(!!start);
+ for (size_t i = 2 * kUnitPointer; i < double_chunk; i += 2 * kUnitPointer) {
+ EXPECT_TRUE(start->IsSet(base + i));
+ EXPECT_FALSE(start->IsSet(base + i + kUnitPointer));
+ }
+}
+
+TEST(HeapCompactTest, SparseBitmapBuild) {
+ Address base = reinterpret_cast<Address>(0x10000u);
+ std::unique_ptr<SparseHeapBitmap> bitmap = SparseHeapBitmap::Create(base);
+
+ size_t double_chunk = 2 * kChunkRange;
+
+ // Create a sparse bitmap containing at least three chunks.
+ bitmap->Add(base - double_chunk);
+ bitmap->Add(base + double_chunk);
+
+ // This is sanity testing internal implementation details of
+ // SparseHeapBitmap; probing |isSet()| outside the bitmap
+ // of the range used in |hasRange()|, is not defined.
+ //
+ // Regardless, the testing here verifies that a |hasRange()| that
+ // straddles multiple internal nodes, returns a bitmap that is
+ // capable of returning correct |isSet()| results.
+ SparseHeapBitmap* start = bitmap->HasRange(
+ base - double_chunk - 2 * kUnitPointer, 4 * kUnitPointer);
+ EXPECT_TRUE(!!start);
+ EXPECT_TRUE(start->IsSet(base - double_chunk));
+ EXPECT_FALSE(start->IsSet(base - double_chunk + kUnitPointer));
+ EXPECT_FALSE(start->IsSet(base));
+ EXPECT_FALSE(start->IsSet(base + kUnitPointer));
+ EXPECT_FALSE(start->IsSet(base + double_chunk));
+ EXPECT_FALSE(start->IsSet(base + double_chunk + kUnitPointer));
+
+ start = bitmap->HasRange(base - double_chunk - 2 * kUnitPointer,
+ 2 * double_chunk + 2 * kUnitPointer);
+ EXPECT_TRUE(!!start);
+ EXPECT_TRUE(start->IsSet(base - double_chunk));
+ EXPECT_FALSE(start->IsSet(base - double_chunk + kUnitPointer));
+ EXPECT_TRUE(start->IsSet(base));
+ EXPECT_FALSE(start->IsSet(base + kUnitPointer));
+ EXPECT_TRUE(start->IsSet(base + double_chunk));
+ EXPECT_FALSE(start->IsSet(base + double_chunk + kUnitPointer));
+
+ start = bitmap->HasRange(base, 20);
+ EXPECT_TRUE(!!start);
+ // Probing for values outside of hasRange() should be considered
+ // undefined, but do it to exercise the (left) tree traversal.
+ EXPECT_TRUE(start->IsSet(base - double_chunk));
+ EXPECT_FALSE(start->IsSet(base - double_chunk + kUnitPointer));
+ EXPECT_TRUE(start->IsSet(base));
+ EXPECT_FALSE(start->IsSet(base + kUnitPointer));
+ EXPECT_TRUE(start->IsSet(base + double_chunk));
+ EXPECT_FALSE(start->IsSet(base + double_chunk + kUnitPointer));
+
+ start = bitmap->HasRange(base + kChunkRange + 2 * kUnitPointer, 2048);
+ EXPECT_TRUE(!!start);
+ // Probing for values outside of hasRange() should be considered
+ // undefined, but do it to exercise node traversal.
+ EXPECT_FALSE(start->IsSet(base - double_chunk));
+ EXPECT_FALSE(start->IsSet(base - double_chunk + kUnitPointer));
+ EXPECT_FALSE(start->IsSet(base));
+ EXPECT_FALSE(start->IsSet(base + kUnitPointer));
+ EXPECT_FALSE(start->IsSet(base + kChunkRange));
+ EXPECT_TRUE(start->IsSet(base + double_chunk));
+ EXPECT_FALSE(start->IsSet(base + double_chunk + kUnitPointer));
+}
+
+TEST(HeapCompactTest, SparseBitmapLeftExtension) {
+ Address base = reinterpret_cast<Address>(0x10000u);
+ std::unique_ptr<SparseHeapBitmap> bitmap = SparseHeapBitmap::Create(base);
+
+ SparseHeapBitmap* start = bitmap->HasRange(base, 1);
+ EXPECT_TRUE(start);
+
+ // Verify that re-adding is a no-op.
+ bitmap->Add(base);
+ EXPECT_EQ(start, bitmap->HasRange(base, 1));
+
+ // Adding an Address |A| before a single-address SparseHeapBitmap node should
+ // cause that node to be "left extended" to use |A| as its new base.
+ bitmap->Add(base - 2 * kUnitPointer);
+ EXPECT_EQ(bitmap->HasRange(base, 1),
+ bitmap->HasRange(base - 2 * kUnitPointer, 1));
+
+ // Reset.
+ bitmap = SparseHeapBitmap::Create(base);
+
+ // If attempting same as above, but the Address |A| is outside the
+ // chunk size of a node, a new SparseHeapBitmap node needs to be
+ // created to the left of |bitmap|.
+ bitmap->Add(base - kChunkRange);
+ EXPECT_NE(bitmap->HasRange(base, 1),
+ bitmap->HasRange(base - 2 * kUnitPointer, 1));
+
+ bitmap = SparseHeapBitmap::Create(base);
+ bitmap->Add(base - kChunkRange + kUnitPointer);
+ // This address is just inside the horizon and shouldn't create a new chunk.
+ EXPECT_EQ(bitmap->HasRange(base, 1),
+ bitmap->HasRange(base - 2 * kUnitPointer, 1));
+ // ..but this one should, like for the sub-test above.
+ bitmap->Add(base - kChunkRange);
+ EXPECT_EQ(bitmap->HasRange(base, 1),
+ bitmap->HasRange(base - 2 * kUnitPointer, 1));
+ EXPECT_NE(bitmap->HasRange(base, 1), bitmap->HasRange(base - kChunkRange, 1));
+}
+
+static void PerformHeapCompaction() {
+ EXPECT_FALSE(HeapCompact::ScheduleCompactionGCForTesting(true));
+ PreciselyCollectGarbage();
+ EXPECT_FALSE(HeapCompact::ScheduleCompactionGCForTesting(false));
+}
+
+TEST(HeapCompactTest, CompactVector) {
+ ClearOutOldGarbage();
+
+ IntWrapper* val = IntWrapper::Create(1, VectorsAreCompacted);
+ Persistent<IntVector> vector = new IntVector(10, val);
+ EXPECT_EQ(10u, vector->size());
+
+ for (size_t i = 0; i < vector->size(); ++i)
+ EXPECT_EQ(val, (*vector)[i]);
+
+ PerformHeapCompaction();
+
+ for (size_t i = 0; i < vector->size(); ++i)
+ EXPECT_EQ(val, (*vector)[i]);
+}
+
+TEST(HeapCompactTest, CompactHashMap) {
+ ClearOutOldGarbage();
+
+ Persistent<IntMap> int_map = new IntMap();
+ for (size_t i = 0; i < 100; ++i) {
+ IntWrapper* val = IntWrapper::Create(i, HashTablesAreCompacted);
+ int_map->insert(val, 100 - i);
+ }
+
+ EXPECT_EQ(100u, int_map->size());
+ for (auto k : *int_map)
+ EXPECT_EQ(k.key->Value(), 100 - k.value);
+
+ PerformHeapCompaction();
+ EXPECT_TRUE(IntWrapper::did_verify_at_least_once);
+
+ for (auto k : *int_map)
+ EXPECT_EQ(k.key->Value(), 100 - k.value);
+}
+
+TEST(HeapCompactTest, CompactVectorPartHashMap) {
+ ClearOutOldGarbage();
+
+ using IntMapVector = HeapVector<IntMap>;
+
+ Persistent<IntMapVector> int_map_vector = new IntMapVector();
+ for (size_t i = 0; i < 10; ++i) {
+ IntMap map;
+ for (size_t j = 0; j < 10; ++j) {
+ IntWrapper* val = IntWrapper::Create(j, VectorsAreCompacted);
+ map.insert(val, 10 - j);
+ }
+ int_map_vector->push_back(map);
+ }
+
+ EXPECT_EQ(10u, int_map_vector->size());
+ for (auto map : *int_map_vector) {
+ EXPECT_EQ(10u, map.size());
+ for (auto k : map) {
+ EXPECT_EQ(k.key->Value(), 10 - k.value);
+ }
+ }
+
+ PerformHeapCompaction();
+ EXPECT_TRUE(IntWrapper::did_verify_at_least_once);
+
+ EXPECT_EQ(10u, int_map_vector->size());
+ for (auto map : *int_map_vector) {
+ EXPECT_EQ(10u, map.size());
+ for (auto k : map) {
+ EXPECT_EQ(k.key->Value(), 10 - k.value);
+ }
+ }
+}
+
+TEST(HeapCompactTest, CompactHashPartVector) {
+ ClearOutOldGarbage();
+
+ using IntVectorMap = HeapHashMap<int, IntVector>;
+
+ Persistent<IntVectorMap> int_vector_map = new IntVectorMap();
+ for (size_t i = 0; i < 10; ++i) {
+ IntVector vector;
+ for (size_t j = 0; j < 10; ++j) {
+ vector.push_back(IntWrapper::Create(j, HashTablesAreCompacted));
+ }
+ int_vector_map->insert(1 + i, vector);
+ }
+
+ EXPECT_EQ(10u, int_vector_map->size());
+ for (const IntVector& int_vector : int_vector_map->Values()) {
+ EXPECT_EQ(10u, int_vector.size());
+ for (size_t i = 0; i < int_vector.size(); ++i) {
+ EXPECT_EQ(static_cast<int>(i), int_vector[i]->Value());
+ }
+ }
+
+ PerformHeapCompaction();
+ EXPECT_TRUE(IntWrapper::did_verify_at_least_once);
+
+ EXPECT_EQ(10u, int_vector_map->size());
+ for (const IntVector& int_vector : int_vector_map->Values()) {
+ EXPECT_EQ(10u, int_vector.size());
+ for (size_t i = 0; i < int_vector.size(); ++i) {
+ EXPECT_EQ(static_cast<int>(i), int_vector[i]->Value());
+ }
+ }
+}
+
+TEST(HeapCompactTest, CompactDeques) {
+ Persistent<IntDeque> deque = new IntDeque;
+ for (int i = 0; i < 8; ++i) {
+ deque->push_front(IntWrapper::Create(i, VectorsAreCompacted));
+ }
+ EXPECT_EQ(8u, deque->size());
+
+ for (size_t i = 0; i < deque->size(); ++i)
+ EXPECT_EQ(static_cast<int>(7 - i), deque->at(i)->Value());
+
+ PerformHeapCompaction();
+ EXPECT_TRUE(IntWrapper::did_verify_at_least_once);
+
+ for (size_t i = 0; i < deque->size(); ++i)
+ EXPECT_EQ(static_cast<int>(7 - i), deque->at(i)->Value());
+}
+
+TEST(HeapCompactTest, CompactDequeVectors) {
+ Persistent<HeapDeque<IntVector>> deque = new HeapDeque<IntVector>;
+ for (int i = 0; i < 8; ++i) {
+ IntWrapper* value = IntWrapper::Create(i, VectorsAreCompacted);
+ IntVector vector = IntVector(8, value);
+ deque->push_front(vector);
+ }
+ EXPECT_EQ(8u, deque->size());
+
+ for (size_t i = 0; i < deque->size(); ++i)
+ EXPECT_EQ(static_cast<int>(7 - i), deque->at(i).at(i)->Value());
+
+ PerformHeapCompaction();
+ EXPECT_TRUE(IntWrapper::did_verify_at_least_once);
+
+ for (size_t i = 0; i < deque->size(); ++i)
+ EXPECT_EQ(static_cast<int>(7 - i), deque->at(i).at(i)->Value());
+}
+
+TEST(HeapCompactTest, CompactLinkedHashSet) {
+ using OrderedHashSet = HeapLinkedHashSet<Member<IntWrapper>>;
+ Persistent<OrderedHashSet> set = new OrderedHashSet;
+ for (int i = 0; i < 13; ++i) {
+ IntWrapper* value = IntWrapper::Create(i, HashTablesAreCompacted);
+ set->insert(value);
+ }
+ EXPECT_EQ(13u, set->size());
+
+ int expected = 0;
+ for (IntWrapper* v : *set) {
+ EXPECT_EQ(expected, v->Value());
+ expected++;
+ }
+
+ PerformHeapCompaction();
+ EXPECT_TRUE(IntWrapper::did_verify_at_least_once);
+
+ expected = 0;
+ for (IntWrapper* v : *set) {
+ EXPECT_EQ(expected, v->Value());
+ expected++;
+ }
+}
+
+TEST(HeapCompactTest, CompactLinkedHashSetVector) {
+ using OrderedHashSet = HeapLinkedHashSet<Member<IntVector>>;
+ Persistent<OrderedHashSet> set = new OrderedHashSet;
+ for (int i = 0; i < 13; ++i) {
+ IntWrapper* value = IntWrapper::Create(i);
+ IntVector* vector = new IntVector(19, value);
+ set->insert(vector);
+ }
+ EXPECT_EQ(13u, set->size());
+
+ int expected = 0;
+ for (IntVector* v : *set) {
+ EXPECT_EQ(expected, (*v)[0]->Value());
+ expected++;
+ }
+
+ PerformHeapCompaction();
+ EXPECT_TRUE(IntWrapper::did_verify_at_least_once);
+
+ expected = 0;
+ for (IntVector* v : *set) {
+ EXPECT_EQ(expected, (*v)[0]->Value());
+ expected++;
+ }
+}
+
+TEST(HeapCompactTest, CompactLinkedHashSetMap) {
+ using Inner = HeapHashSet<Member<IntWrapper>>;
+ using OrderedHashSet = HeapLinkedHashSet<Member<Inner>>;
+
+ Persistent<OrderedHashSet> set = new OrderedHashSet;
+ for (int i = 0; i < 13; ++i) {
+ IntWrapper* value = IntWrapper::Create(i);
+ Inner* inner = new Inner;
+ inner->insert(value);
+ set->insert(inner);
+ }
+ EXPECT_EQ(13u, set->size());
+
+ int expected = 0;
+ for (const Inner* v : *set) {
+ EXPECT_EQ(1u, v->size());
+ EXPECT_EQ(expected, (*v->begin())->Value());
+ expected++;
+ }
+
+ PerformHeapCompaction();
+ EXPECT_TRUE(IntWrapper::did_verify_at_least_once);
+
+ expected = 0;
+ for (const Inner* v : *set) {
+ EXPECT_EQ(1u, v->size());
+ EXPECT_EQ(expected, (*v->begin())->Value());
+ expected++;
+ }
+}
+
+TEST(HeapCompactTest, CompactLinkedHashSetNested) {
+ using Inner = HeapLinkedHashSet<Member<IntWrapper>>;
+ using OrderedHashSet = HeapLinkedHashSet<Member<Inner>>;
+
+ Persistent<OrderedHashSet> set = new OrderedHashSet;
+ for (int i = 0; i < 13; ++i) {
+ IntWrapper* value = IntWrapper::Create(i);
+ Inner* inner = new Inner;
+ inner->insert(value);
+ set->insert(inner);
+ }
+ EXPECT_EQ(13u, set->size());
+
+ int expected = 0;
+ for (const Inner* v : *set) {
+ EXPECT_EQ(1u, v->size());
+ EXPECT_EQ(expected, (*v->begin())->Value());
+ expected++;
+ }
+
+ PerformHeapCompaction();
+ EXPECT_TRUE(IntWrapper::did_verify_at_least_once);
+
+ expected = 0;
+ for (const Inner* v : *set) {
+ EXPECT_EQ(1u, v->size());
+ EXPECT_EQ(expected, (*v->begin())->Value());
+ expected++;
+ }
+}
+
+} // namespace blink
+
+#endif // ENABLE_HEAP_COMPACTION
diff --git a/chromium/third_party/blink/renderer/platform/heap/heap_linked_stack.h b/chromium/third_party/blink/renderer/platform/heap/heap_linked_stack.h
new file mode 100644
index 00000000000..8f6820b1b99
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/heap_linked_stack.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2014 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_LINKED_STACK_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_LINKED_STACK_H_
+
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/heap/visitor.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+// HeapLinkedStack<> is an Oilpan-managed stack that avoids pre-allocation
+// of memory and heap fragmentation.
+//
+// The API was originally implemented on the call stack by LinkedStack<>
+// (now removed: https://codereview.chromium.org/2761853003/).
+// See https://codereview.chromium.org/17314010 for the original use-case.
+template <typename T>
+class HeapLinkedStack : public GarbageCollected<HeapLinkedStack<T>> {
+ public:
+ HeapLinkedStack() : size_(0) {}
+
+ bool IsEmpty();
+
+ void Push(const T&);
+ const T& Peek();
+ void Pop();
+
+ size_t size();
+
+ void Trace(blink::Visitor* visitor) {
+ for (Node* current = head_; current; current = current->next_)
+ visitor->Trace(current);
+ }
+
+ private:
+ class Node : public GarbageCollected<Node> {
+ public:
+ Node(const T&, Node* next);
+
+ void Trace(blink::Visitor* visitor) { visitor->Trace(data_); }
+
+ T data_;
+ Member<Node> next_;
+ };
+
+ Member<Node> head_;
+ size_t size_;
+};
+
+template <typename T>
+HeapLinkedStack<T>::Node::Node(const T& data, Node* next)
+ : data_(data), next_(next) {}
+
+template <typename T>
+inline bool HeapLinkedStack<T>::IsEmpty() {
+ return !head_;
+}
+
+template <typename T>
+inline void HeapLinkedStack<T>::Push(const T& data) {
+ head_ = new Node(data, head_);
+ ++size_;
+}
+
+template <typename T>
+inline const T& HeapLinkedStack<T>::Peek() {
+ return head_->data_;
+}
+
+template <typename T>
+inline void HeapLinkedStack<T>::Pop() {
+ DCHECK(head_);
+ DCHECK(size_);
+ head_ = head_->next_;
+ --size_;
+}
+
+template <typename T>
+inline size_t HeapLinkedStack<T>::size() {
+ return size_;
+}
+
+template <typename T>
+class TraceEagerlyTrait<HeapLinkedStack<T>> {
+ STATIC_ONLY(TraceEagerlyTrait);
+
+ public:
+ static const bool value = TraceEagerlyTrait<T>::value;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_LINKED_STACK_H_
diff --git a/chromium/third_party/blink/renderer/platform/heap/heap_page.cc b/chromium/third_party/blink/renderer/platform/heap/heap_page.cc
new file mode 100644
index 00000000000..b73e8281dcb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/heap_page.cc
@@ -0,0 +1,1818 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/renderer/platform/heap/heap_page.h"
+
+#include "base/trace_event/process_memory_dump.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h"
+#include "third_party/blink/renderer/platform/heap/blink_gc_memory_dump_provider.h"
+#include "third_party/blink/renderer/platform/heap/heap_compact.h"
+#include "third_party/blink/renderer/platform/heap/marking_verifier.h"
+#include "third_party/blink/renderer/platform/heap/page_memory.h"
+#include "third_party/blink/renderer/platform/heap/page_pool.h"
+#include "third_party/blink/renderer/platform/heap/safe_point.h"
+#include "third_party/blink/renderer/platform/heap/thread_state.h"
+#include "third_party/blink/renderer/platform/histogram.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/web_memory_allocator_dump.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/web_process_memory_dump.h"
+#include "third_party/blink/renderer/platform/memory_coordinator.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/auto_reset.h"
+#include "third_party/blink/renderer/platform/wtf/container_annotations.h"
+#include "third_party/blink/renderer/platform/wtf/leak_annotations.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+#ifdef ANNOTATE_CONTIGUOUS_CONTAINER
+
+// When finalizing a non-inlined vector backing store/container, remove
+// its contiguous container annotation. Required as it will not be destructed
+// from its Vector.
+#define ASAN_RETIRE_CONTAINER_ANNOTATION(object, objectSize) \
+ do { \
+ BasePage* page = PageFromObject(object); \
+ DCHECK(page); \
+ bool is_container = \
+ ThreadHeap::IsVectorArenaIndex(page->Arena()->ArenaIndex()); \
+ if (!is_container && page->IsLargeObjectPage()) \
+ is_container = \
+ static_cast<LargeObjectPage*>(page)->IsVectorBackingPage(); \
+ if (is_container) \
+ ANNOTATE_DELETE_BUFFER(object, objectSize, 0); \
+ } while (0)
+
+// A vector backing store represented by a large object is marked
+// so that when it is finalized, its ASan annotation will be
+// correctly retired.
+#define ASAN_MARK_LARGE_VECTOR_CONTAINER(arena, large_object) \
+ if (ThreadHeap::IsVectorArenaIndex(arena->ArenaIndex())) { \
+ BasePage* large_page = PageFromObject(large_object); \
+ DCHECK(large_page->IsLargeObjectPage()); \
+ static_cast<LargeObjectPage*>(large_page)->SetIsVectorBackingPage(); \
+ }
+#else
+#define ASAN_RETIRE_CONTAINER_ANNOTATION(payload, payloadSize)
+#define ASAN_MARK_LARGE_VECTOR_CONTAINER(arena, largeObject)
+#endif
+
+namespace blink {
+
+#if DCHECK_IS_ON() && defined(ARCH_CPU_64_BITS)
+NO_SANITIZE_ADDRESS
+void HeapObjectHeader::ZapMagic() {
+ CheckHeader();
+ magic_ = kZappedMagic;
+}
+#endif
+
+void HeapObjectHeader::Finalize(Address object, size_t object_size) {
+ HeapAllocHooks::FreeHookIfEnabled(object);
+ const GCInfo* gc_info = ThreadHeap::GcInfo(GcInfoIndex());
+ if (gc_info->HasFinalizer())
+ gc_info->finalize_(object);
+
+ ASAN_RETIRE_CONTAINER_ANNOTATION(object, object_size);
+}
+
+BaseArena::BaseArena(ThreadState* state, int index)
+ : first_page_(nullptr),
+ first_unswept_page_(nullptr),
+ thread_state_(state),
+ index_(index) {}
+
+BaseArena::~BaseArena() {
+ DCHECK(!first_page_);
+ DCHECK(SweepingCompleted());
+}
+
+void BaseArena::RemoveAllPages() {
+ ClearFreeLists();
+
+ DCHECK(SweepingCompleted());
+ while (first_page_) {
+ BasePage* page = first_page_;
+ page->Unlink(&first_page_);
+ page->RemoveFromHeap();
+ }
+}
+
+void BaseArena::TakeSnapshot(const String& dump_base_name,
+ ThreadState::GCSnapshotInfo& info) {
+ // |dumpBaseName| at this point is "blink_gc/thread_X/heaps/HeapName"
+ base::trace_event::MemoryAllocatorDump* allocator_dump =
+ BlinkGCMemoryDumpProvider::Instance()
+ ->CreateMemoryAllocatorDumpForCurrentGC(dump_base_name);
+ size_t page_count = 0;
+ BasePage::HeapSnapshotInfo heap_info;
+ for (BasePage* page = first_unswept_page_; page; page = page->Next()) {
+ String dump_name = dump_base_name +
+ String::Format("/pages/page_%lu",
+ static_cast<unsigned long>(page_count++));
+ base::trace_event::MemoryAllocatorDump* page_dump =
+ BlinkGCMemoryDumpProvider::Instance()
+ ->CreateMemoryAllocatorDumpForCurrentGC(dump_name);
+
+ page->TakeSnapshot(page_dump, info, heap_info);
+ }
+ allocator_dump->AddScalar("blink_page_count", "objects", page_count);
+
+ // When taking a full dump (w/ freelist), both the /buckets and /pages
+ // report their free size but they are not meant to be added together.
+ // Therefore, here we override the free_size of the parent heap to be
+ // equal to the free_size of the sum of its heap pages.
+ allocator_dump->AddScalar("free_size", "bytes", heap_info.free_size);
+ allocator_dump->AddScalar("free_count", "objects", heap_info.free_count);
+}
+
+#if DCHECK_IS_ON()
+BasePage* BaseArena::FindPageFromAddress(Address address) {
+ for (BasePage* page = first_page_; page; page = page->Next()) {
+ if (page->Contains(address))
+ return page;
+ }
+ for (BasePage* page = first_unswept_page_; page; page = page->Next()) {
+ if (page->Contains(address))
+ return page;
+ }
+ return nullptr;
+}
+#endif
+
+void BaseArena::MakeConsistentForGC() {
+#if DCHECK_IS_ON()
+ DCHECK(IsConsistentForGC());
+#endif
+
+ // We should not start a new GC until we finish sweeping in the current GC.
+ CHECK(SweepingCompleted());
+
+ HeapCompact* heap_compactor = GetThreadState()->Heap().Compaction();
+ if (!heap_compactor->IsCompactingArena(ArenaIndex()))
+ return;
+
+ BasePage* next_page = first_page_;
+ while (next_page) {
+ if (!next_page->IsLargeObjectPage())
+ heap_compactor->AddCompactingPage(next_page);
+ next_page = next_page->Next();
+ }
+}
+
+void BaseArena::MakeConsistentForMutator() {
+ ClearFreeLists();
+#if DCHECK_IS_ON()
+ DCHECK(IsConsistentForGC());
+#endif
+ DCHECK(!first_page_);
+
+ // Drop marks from marked objects and rebuild free lists in preparation for
+ // resuming the executions of mutators.
+ BasePage* previous_page = nullptr;
+ for (BasePage *page = first_unswept_page_; page;
+ previous_page = page, page = page->Next()) {
+ page->MakeConsistentForMutator();
+ page->MarkAsSwept();
+ }
+ if (previous_page) {
+ DCHECK(!SweepingCompleted());
+ previous_page->next_ = first_page_;
+ first_page_ = first_unswept_page_;
+ first_unswept_page_ = nullptr;
+ }
+ DCHECK(SweepingCompleted());
+
+ VerifyObjectStartBitmap();
+}
+
+size_t BaseArena::ObjectPayloadSizeForTesting() {
+#if DCHECK_IS_ON()
+ DCHECK(IsConsistentForGC());
+#endif
+ // DCHECK(SweepingCompleted());
+
+ size_t object_payload_size = 0;
+ for (BasePage* page = first_unswept_page_; page; page = page->Next())
+ object_payload_size += page->ObjectPayloadSizeForTesting();
+ return object_payload_size;
+}
+
+void BaseArena::PrepareForSweep() {
+ DCHECK(GetThreadState()->InAtomicMarkingPause());
+ DCHECK(SweepingCompleted());
+
+ ClearFreeLists();
+
+ // Verification depends on the allocation point being cleared.
+ VerifyObjectStartBitmap();
+
+ for (BasePage* page = first_page_; page; page = page->Next()) {
+ page->MarkAsUnswept();
+ }
+
+ // Move all pages to a list of unswept pages.
+ first_unswept_page_ = first_page_;
+ first_page_ = nullptr;
+}
+
+#if defined(ADDRESS_SANITIZER)
+void BaseArena::PoisonArena() {
+ for (BasePage* page = first_unswept_page_; page; page = page->Next())
+ page->PoisonUnmarkedObjects();
+}
+#endif
+
+Address BaseArena::LazySweep(size_t allocation_size, size_t gc_info_index) {
+ // If there are no pages to be swept, return immediately.
+ if (SweepingCompleted())
+ return nullptr;
+
+ CHECK(GetThreadState()->IsSweepingInProgress());
+
+ // lazySweepPages() can be called recursively if finalizers invoked in
+ // page->sweep() allocate memory and the allocation triggers
+ // lazySweepPages(). This check prevents the sweeping from being executed
+ // recursively.
+ if (GetThreadState()->SweepForbidden())
+ return nullptr;
+
+ TRACE_EVENT0("blink_gc", "BaseArena::lazySweepPages");
+ ThreadState::SweepForbiddenScope sweep_forbidden(GetThreadState());
+ ScriptForbiddenScope script_forbidden;
+
+ double start_time = WTF::CurrentTimeTicksInMilliseconds();
+ Address result = LazySweepPages(allocation_size, gc_info_index);
+ GetThreadState()->AccumulateSweepingTime(
+ WTF::CurrentTimeTicksInMilliseconds() - start_time);
+ ThreadHeap::ReportMemoryUsageForTracing();
+
+ return result;
+}
+
+void BaseArena::SweepUnsweptPage() {
+ BasePage* page = first_unswept_page_;
+ if (page->IsEmpty()) {
+ page->Unlink(&first_unswept_page_);
+ page->RemoveFromHeap();
+ } else {
+ // Sweep a page and move the page from m_firstUnsweptPages to
+ // m_firstPages.
+ page->Sweep();
+ page->Unlink(&first_unswept_page_);
+ page->Link(&first_page_);
+ page->MarkAsSwept();
+ }
+}
+
+bool BaseArena::LazySweepWithDeadline(double deadline_seconds) {
+ // It might be heavy to call
+ // Platform::current()->monotonicallyIncreasingTimeSeconds() per page (i.e.,
+ // 128 KB sweep or one LargeObject sweep), so we check the deadline per 10
+ // pages.
+ static const int kDeadlineCheckInterval = 10;
+
+ CHECK(GetThreadState()->IsSweepingInProgress());
+ DCHECK(GetThreadState()->SweepForbidden());
+ DCHECK(ScriptForbiddenScope::IsScriptForbidden());
+
+ NormalPageArena* normal_arena = nullptr;
+ if (first_unswept_page_ && !first_unswept_page_->IsLargeObjectPage()) {
+ // Mark this NormalPageArena as being lazily swept.
+ NormalPage* normal_page =
+ reinterpret_cast<NormalPage*>(first_unswept_page_);
+ normal_arena = normal_page->ArenaForNormalPage();
+ normal_arena->SetIsLazySweeping(true);
+ }
+ int page_count = 1;
+ while (!SweepingCompleted()) {
+ SweepUnsweptPage();
+ if (page_count % kDeadlineCheckInterval == 0) {
+ if (deadline_seconds <= CurrentTimeTicksInSeconds()) {
+ // Deadline has come.
+ ThreadHeap::ReportMemoryUsageForTracing();
+ if (normal_arena)
+ normal_arena->SetIsLazySweeping(false);
+ return SweepingCompleted();
+ }
+ }
+ page_count++;
+ }
+ ThreadHeap::ReportMemoryUsageForTracing();
+ if (normal_arena)
+ normal_arena->SetIsLazySweeping(false);
+ return true;
+}
+
+void BaseArena::CompleteSweep() {
+ CHECK(GetThreadState()->IsSweepingInProgress());
+ DCHECK(GetThreadState()->SweepForbidden());
+ DCHECK(ScriptForbiddenScope::IsScriptForbidden());
+
+ // Some phases, e.g. verification, require iterability of a page.
+ MakeIterable();
+
+ while (!SweepingCompleted()) {
+ SweepUnsweptPage();
+ }
+ ThreadHeap::ReportMemoryUsageForTracing();
+}
+
+Address BaseArena::AllocateLargeObject(size_t allocation_size,
+ size_t gc_info_index) {
+ // TODO(sof): should need arise, support eagerly finalized large objects.
+ CHECK(ArenaIndex() != BlinkGC::kEagerSweepArenaIndex);
+ LargeObjectArena* large_object_arena = static_cast<LargeObjectArena*>(
+ GetThreadState()->Heap().Arena(BlinkGC::kLargeObjectArenaIndex));
+ Address large_object = large_object_arena->AllocateLargeObjectPage(
+ allocation_size, gc_info_index);
+ ASAN_MARK_LARGE_VECTOR_CONTAINER(this, large_object);
+ return large_object;
+}
+
+bool BaseArena::WillObjectBeLazilySwept(BasePage* page,
+ void* object_pointer) const {
+ // If not on the current page being (potentially) lazily swept,
+ // |objectPointer| is an unmarked, sweepable object.
+ if (page != first_unswept_page_)
+ return true;
+
+ DCHECK(!page->IsLargeObjectPage());
+ // Check if the arena is currently being lazily swept.
+ NormalPage* normal_page = reinterpret_cast<NormalPage*>(page);
+ NormalPageArena* normal_arena = normal_page->ArenaForNormalPage();
+ if (!normal_arena->IsLazySweeping())
+ return true;
+
+ // Rare special case: unmarked object is on the page being lazily swept,
+ // and a finalizer for an object on that page calls
+ // ThreadHeap::willObjectBeLazilySwept().
+ //
+ // Need to determine if |objectPointer| represents a live (unmarked) object or
+ // an unmarked object that will be lazily swept later. As lazy page sweeping
+ // doesn't record a frontier pointer representing how far along it is, the
+ // page is scanned from the start, skipping past freed & unmarked regions.
+ //
+ // If no marked objects are encountered before |objectPointer|, we know that
+ // the finalizing object calling willObjectBeLazilySwept() comes later, and
+ // |objectPointer| has been deemed to be alive already (=> it won't be swept.)
+ //
+ // If a marked object is encountered before |objectPointer|, it will
+ // not have been lazily swept past already. Hence it represents an unmarked,
+ // sweepable object.
+ //
+ // As willObjectBeLazilySwept() is used rarely and it happening to be
+ // used while runnning a finalizer on the page being lazily swept is
+ // even rarer, the page scan is considered acceptable and something
+ // really wanted -- willObjectBeLazilySwept()'s result can be trusted.
+ Address page_end = normal_page->PayloadEnd();
+ for (Address header_address = normal_page->Payload();
+ header_address < page_end;) {
+ HeapObjectHeader* header =
+ reinterpret_cast<HeapObjectHeader*>(header_address);
+ size_t size = header->size();
+ // Scan made it to |objectPointer| without encountering any marked objects.
+ // => lazy sweep will have processed this unmarked, but live, object.
+ // => |object_pointer| will not be lazily swept.
+ //
+ // Notice that |object_pointer| might be pointer to a GarbageCollectedMixin,
+ // hence using |FromPayload| to derive the HeapObjectHeader isn't possible
+ // (and use its value to check if |header_address| is equal to it.)
+ if (header_address > object_pointer)
+ return false;
+ if (!header->IsFree() && header->IsMarked()) {
+ // There must be a marked object on this page and the one located must
+ // have room after it for the unmarked |objectPointer| object.
+ DCHECK(header_address + size < page_end);
+ return true;
+ }
+ header_address += size;
+ }
+ NOTREACHED();
+ return true;
+}
+
+NormalPageArena::NormalPageArena(ThreadState* state, int index)
+ : BaseArena(state, index),
+ current_allocation_point_(nullptr),
+ remaining_allocation_size_(0),
+ last_remaining_allocation_size_(0),
+ promptly_freed_size_(0),
+ is_lazy_sweeping_(false) {
+ ClearFreeLists();
+}
+
+void NormalPageArena::MakeConsistentForGC() {
+ BaseArena::MakeConsistentForGC();
+
+ // Remove linear allocation area.
+ SetAllocationPoint(nullptr, 0);
+}
+
+void NormalPageArena::ClearFreeLists() {
+ SetAllocationPoint(nullptr, 0);
+ free_list_.Clear();
+ promptly_freed_size_ = 0;
+}
+
+void NormalPageArena::MakeIterable() {
+ SetAllocationPoint(nullptr, 0);
+}
+
+size_t NormalPageArena::ArenaSize() {
+ size_t size = 0;
+ BasePage* page = first_page_;
+ while (page) {
+ size += page->size();
+ page = page->Next();
+ }
+ LOG_HEAP_FREELIST_VERBOSE()
+ << "Heap size: " << size << "(" << ArenaIndex() << ")";
+ return size;
+}
+
+size_t NormalPageArena::FreeListSize() {
+ size_t free_size = free_list_.FreeListSize();
+ LOG_HEAP_FREELIST_VERBOSE()
+ << "Free size: " << free_size << "(" << ArenaIndex() << ")";
+ return free_size;
+}
+
+void NormalPageArena::SweepAndCompact() {
+ ThreadHeap& heap = GetThreadState()->Heap();
+ if (!heap.Compaction()->IsCompactingArena(ArenaIndex()))
+ return;
+
+ if (SweepingCompleted()) {
+ heap.Compaction()->FinishedArenaCompaction(this, 0, 0);
+ return;
+ }
+
+ // Compaction is performed in-place, sliding objects down over unused
+ // holes for a smaller heap page footprint and improved locality.
+ // A "compaction pointer" is consequently kept, pointing to the next
+ // available address to move objects down to. It will belong to one
+ // of the already sweep-compacted pages for this arena, but as compaction
+ // proceeds, it will not belong to the same page as the one being
+ // currently compacted.
+ //
+ // The compaction pointer is represented by the
+ // |(currentPage, allocationPoint)| pair, with |allocationPoint|
+ // being the offset into |currentPage|, making up the next
+ // available location. When the compaction of an arena page causes the
+ // compaction pointer to exhaust the current page it is compacting into,
+ // page compaction will advance the current page of the compaction
+ // pointer, as well as the allocation point.
+ //
+ // By construction, the page compaction can be performed without having
+ // to allocate any new pages. So to arrange for the page compaction's
+ // supply of freed, available pages, we chain them together after each
+ // has been "compacted from". The page compaction will then reuse those
+ // as needed, and once finished, the chained, available pages can be
+ // released back to the OS.
+ //
+ // To ease the passing of the compaction state when iterating over an
+ // arena's pages, package it up into a |CompactionContext|.
+ NormalPage::CompactionContext context;
+ context.compacted_pages_ = &first_page_;
+
+ while (!SweepingCompleted()) {
+ BasePage* page = first_unswept_page_;
+ if (page->IsEmpty()) {
+ page->Unlink(&first_unswept_page_);
+ page->RemoveFromHeap();
+ continue;
+ }
+ // Large objects do not belong to this arena.
+ DCHECK(!page->IsLargeObjectPage());
+ NormalPage* normal_page = static_cast<NormalPage*>(page);
+ normal_page->Unlink(&first_unswept_page_);
+ normal_page->MarkAsSwept();
+ // If not the first page, add |normalPage| onto the available pages chain.
+ if (!context.current_page_)
+ context.current_page_ = normal_page;
+ else
+ normal_page->Link(&context.available_pages_);
+ normal_page->SweepAndCompact(context);
+ }
+
+ // All pages were empty; nothing to compact.
+ if (!context.current_page_) {
+ heap.Compaction()->FinishedArenaCompaction(this, 0, 0);
+ return;
+ }
+
+ size_t freed_size = 0;
+ size_t freed_page_count = 0;
+
+ // If the current page hasn't been allocated into, add it to the available
+ // list, for subsequent release below.
+ size_t allocation_point = context.allocation_point_;
+ if (!allocation_point) {
+ context.current_page_->Link(&context.available_pages_);
+ } else {
+ NormalPage* current_page = context.current_page_;
+ current_page->Link(&first_page_);
+ if (allocation_point != current_page->PayloadSize()) {
+ // Put the remainder of the page onto the free list.
+ freed_size = current_page->PayloadSize() - allocation_point;
+ Address payload = current_page->Payload();
+ SET_MEMORY_INACCESSIBLE(payload + allocation_point, freed_size);
+ current_page->ArenaForNormalPage()->AddToFreeList(
+ payload + allocation_point, freed_size);
+ }
+ }
+
+ // Return available pages to the free page pool, decommitting them from
+ // the pagefile.
+ BasePage* available_pages = context.available_pages_;
+#if DEBUG_HEAP_COMPACTION
+ std::stringstream stream;
+#endif
+ while (available_pages) {
+ size_t page_size = available_pages->size();
+#if DEBUG_HEAP_COMPACTION
+ if (!freed_page_count)
+ stream << "Releasing:";
+ stream << " [" << available_pages << ", " << (available_pages + page_size)
+ << "]";
+#endif
+ freed_size += page_size;
+ freed_page_count++;
+ BasePage* next_page;
+ available_pages->Unlink(&next_page);
+#if !(DCHECK_IS_ON() || defined(LEAK_SANITIZER) || \
+ defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER))
+ // Clear out the page before adding it to the free page pool, which
+ // decommits it. Recommitting the page must find a zeroed page later.
+ // We cannot assume that the OS will hand back a zeroed page across
+ // its "decommit" operation.
+ //
+ // If in a debug setting, the unused page contents will have been
+ // zapped already; leave it in that state.
+ DCHECK(!available_pages->IsLargeObjectPage());
+ NormalPage* unused_page = reinterpret_cast<NormalPage*>(available_pages);
+ memset(unused_page->Payload(), 0, unused_page->PayloadSize());
+#endif
+ available_pages->RemoveFromHeap();
+ available_pages = static_cast<NormalPage*>(next_page);
+ }
+#if DEBUG_HEAP_COMPACTION
+ if (freed_page_count)
+ LOG_HEAP_COMPACTION() << stream.str();
+#endif
+ heap.Compaction()->FinishedArenaCompaction(this, freed_page_count,
+ freed_size);
+
+ VerifyObjectStartBitmap();
+}
+
+void NormalPageArena::VerifyObjectStartBitmap() {
+#if DCHECK_IS_ON()
+ // Verifying object start bitmap requires iterability of pages. As compaction
+ // may set up a new we have to reset here.
+ SetAllocationPoint(nullptr, 0);
+ for (NormalPage* page = static_cast<NormalPage*>(first_page_); page;
+ page = static_cast<NormalPage*>(page->Next()))
+ page->VerifyObjectStartBitmapIsConsistentWithPayload();
+#endif // DCHECK_IS_ON()
+}
+
+void NormalPageArena::VerifyMarking() {
+#if DCHECK_IS_ON()
+ // We cannot rely on other marking phases to clear the allocation area as
+ // for incremental marking the application is running between steps and
+ // might set up a new area.
+ SetAllocationPoint(nullptr, 0);
+ for (NormalPage* page = static_cast<NormalPage*>(first_page_); page;
+ page = static_cast<NormalPage*>(page->Next()))
+ page->VerifyMarking();
+#endif // DCHECK_IS_ON()
+}
+
+#if DCHECK_IS_ON()
+bool NormalPageArena::IsConsistentForGC() {
+ // A thread heap is consistent for sweeping if none of the pages to be swept
+ // contain a freelist block or the current allocation point.
+ for (size_t i = 0; i < kBlinkPageSizeLog2; ++i) {
+ for (FreeListEntry* free_list_entry = free_list_.free_lists_[i];
+ free_list_entry; free_list_entry = free_list_entry->Next()) {
+ if (PagesToBeSweptContains(free_list_entry->GetAddress()))
+ return false;
+ }
+ }
+ if (HasCurrentAllocationArea()) {
+ if (PagesToBeSweptContains(CurrentAllocationPoint()))
+ return false;
+ }
+ return true;
+}
+
+bool NormalPageArena::PagesToBeSweptContains(Address address) {
+ for (BasePage* page = first_unswept_page_; page; page = page->Next()) {
+ if (page->Contains(address))
+ return true;
+ }
+ return false;
+}
+#endif
+
+void NormalPageArena::TakeFreelistSnapshot(const String& dump_name) {
+ if (free_list_.TakeSnapshot(dump_name)) {
+ base::trace_event::MemoryAllocatorDump* buckets_dump =
+ BlinkGCMemoryDumpProvider::Instance()
+ ->CreateMemoryAllocatorDumpForCurrentGC(dump_name + "/buckets");
+ base::trace_event::MemoryAllocatorDump* pages_dump =
+ BlinkGCMemoryDumpProvider::Instance()
+ ->CreateMemoryAllocatorDumpForCurrentGC(dump_name + "/pages");
+ BlinkGCMemoryDumpProvider::Instance()
+ ->CurrentProcessMemoryDump()
+ ->AddOwnershipEdge(pages_dump->guid(), buckets_dump->guid());
+ }
+}
+
+void NormalPageArena::AllocatePage() {
+ GetThreadState()->Heap().ShouldFlushHeapDoesNotContainCache();
+ PageMemory* page_memory =
+ GetThreadState()->Heap().GetFreePagePool()->Take(ArenaIndex());
+
+ if (!page_memory) {
+ // Allocate a memory region for blinkPagesPerRegion pages that
+ // will each have the following layout.
+ //
+ // [ guard os page | ... payload ... | guard os page ]
+ // ^---{ aligned to blink page size }
+ PageMemoryRegion* region = PageMemoryRegion::AllocateNormalPages(
+ GetThreadState()->Heap().GetRegionTree());
+
+ // Setup the PageMemory object for each of the pages in the region.
+ for (size_t i = 0; i < kBlinkPagesPerRegion; ++i) {
+ PageMemory* memory = PageMemory::SetupPageMemoryInRegion(
+ region, i * kBlinkPageSize, BlinkPagePayloadSize());
+ // Take the first possible page ensuring that this thread actually
+ // gets a page and add the rest to the page pool.
+ if (!page_memory) {
+ bool result = memory->Commit();
+ // If you hit the CHECK, it will mean that you're hitting the limit
+ // of the number of mmapped regions the OS can support
+ // (e.g., /proc/sys/vm/max_map_count in Linux) or on that Windows you
+ // have exceeded the max commit charge across all processes for the
+ // system.
+ CHECK(result);
+ page_memory = memory;
+ } else {
+ GetThreadState()->Heap().GetFreePagePool()->Add(ArenaIndex(), memory);
+ }
+ }
+ }
+ NormalPage* page =
+ new (page_memory->WritableStart()) NormalPage(page_memory, this);
+ page->Link(&first_page_);
+
+ GetThreadState()->Heap().HeapStats().IncreaseAllocatedSpace(page->size());
+#if DCHECK_IS_ON() || defined(LEAK_SANITIZER) || defined(ADDRESS_SANITIZER)
+ // Allow the following addToFreeList() to add the newly allocated memory
+ // to the free list.
+ ASAN_UNPOISON_MEMORY_REGION(page->Payload(), page->PayloadSize());
+ Address address = page->Payload();
+ for (size_t i = 0; i < page->PayloadSize(); i++)
+ address[i] = kReuseAllowedZapValue;
+ ASAN_POISON_MEMORY_REGION(page->Payload(), page->PayloadSize());
+#endif
+ AddToFreeList(page->Payload(), page->PayloadSize());
+}
+
+void NormalPageArena::FreePage(NormalPage* page) {
+ GetThreadState()->Heap().HeapStats().DecreaseAllocatedSpace(page->size());
+
+ PageMemory* memory = page->Storage();
+ page->~NormalPage();
+ GetThreadState()->Heap().GetFreePagePool()->Add(ArenaIndex(), memory);
+}
+
+ObjectStartBitmap::ObjectStartBitmap(Address offset) : offset_(offset) {
+ Clear();
+}
+
+void ObjectStartBitmap::Clear() {
+ memset(&object_start_bit_map_, 0, kReservedForBitmap);
+}
+
+void NormalPageArena::PromptlyFreeObject(HeapObjectHeader* header) {
+ DCHECK(!GetThreadState()->SweepForbidden());
+ Address address = reinterpret_cast<Address>(header);
+ Address payload = header->Payload();
+ size_t size = header->size();
+ size_t payload_size = header->PayloadSize();
+ DCHECK_GT(size, 0u);
+#if DCHECK_IS_ON()
+ DCHECK_EQ(PageFromObject(address), FindPageFromAddress(address));
+#endif
+ {
+ ThreadState::SweepForbiddenScope forbidden_scope(GetThreadState());
+ header->Finalize(payload, payload_size);
+ if (IsObjectAllocatedAtAllocationPoint(header)) {
+ current_allocation_point_ -= size;
+ DCHECK_EQ(address, current_allocation_point_);
+ SetRemainingAllocationSize(remaining_allocation_size_ + size);
+ SET_MEMORY_INACCESSIBLE(address, size);
+ // Memory that is part of the allocation point is not allowed to be part
+ // of the object start bit map.
+ reinterpret_cast<NormalPage*>(PageFromObject(header))
+ ->object_start_bit_map()
+ ->ClearBit(address);
+ return;
+ }
+ PromptlyFreeObjectInFreeList(header, size);
+ }
+}
+
+void NormalPageArena::PromptlyFreeObjectInFreeList(HeapObjectHeader* header,
+ size_t size) {
+ Address address = reinterpret_cast<Address>(header);
+ NormalPage* page = reinterpret_cast<NormalPage*>(PageFromObject(header));
+ if (page->HasBeenSwept()) {
+ Address payload = header->Payload();
+ size_t payload_size = header->PayloadSize();
+ // If the page has been swept a promptly freed object may be adjacent
+ // to other free list entries. We make the object available for future
+ // allocation right away by adding it to the free list and increase the
+ // promptly_freed_size_ counter which may result in coalescing later.
+ SET_MEMORY_INACCESSIBLE(payload, payload_size);
+ CHECK_MEMORY_INACCESSIBLE(payload, payload_size);
+ AddToFreeList(address, size);
+ promptly_freed_size_ += size;
+ } else {
+ // If we do not have free list entries the sweeper will take care of
+ // coalescing.
+ header->Unmark();
+ }
+ GetThreadState()->Heap().HeapStats().DecreaseAllocatedObjectSize(size);
+}
+
+bool NormalPageArena::ExpandObject(HeapObjectHeader* header, size_t new_size) {
+ // It's possible that Vector requests a smaller expanded size because
+ // Vector::shrinkCapacity can set a capacity smaller than the actual payload
+ // size.
+ if (header->PayloadSize() >= new_size)
+ return true;
+ size_t allocation_size = ThreadHeap::AllocationSizeFromSize(new_size);
+ DCHECK_GT(allocation_size, header->size());
+ size_t expand_size = allocation_size - header->size();
+ if (IsObjectAllocatedAtAllocationPoint(header) &&
+ expand_size <= remaining_allocation_size_) {
+ current_allocation_point_ += expand_size;
+ DCHECK_GE(remaining_allocation_size_, expand_size);
+ SetRemainingAllocationSize(remaining_allocation_size_ - expand_size);
+ // Unpoison the memory used for the object (payload).
+ SET_MEMORY_ACCESSIBLE(header->PayloadEnd(), expand_size);
+ header->SetSize(allocation_size);
+#if DCHECK_IS_ON()
+ DCHECK(FindPageFromAddress(header->PayloadEnd() - 1));
+#endif
+ return true;
+ }
+ return false;
+}
+
+bool NormalPageArena::ShrinkObject(HeapObjectHeader* header, size_t new_size) {
+ DCHECK_GT(header->PayloadSize(), new_size);
+ size_t allocation_size = ThreadHeap::AllocationSizeFromSize(new_size);
+ DCHECK_GT(header->size(), allocation_size);
+ size_t shrink_size = header->size() - allocation_size;
+ if (IsObjectAllocatedAtAllocationPoint(header)) {
+ current_allocation_point_ -= shrink_size;
+ SetRemainingAllocationSize(remaining_allocation_size_ + shrink_size);
+ SET_MEMORY_INACCESSIBLE(current_allocation_point_, shrink_size);
+ header->SetSize(allocation_size);
+ return true;
+ }
+ DCHECK_GE(shrink_size, sizeof(HeapObjectHeader));
+ DCHECK_GT(header->GcInfoIndex(), 0u);
+ Address shrink_address = header->PayloadEnd() - shrink_size;
+ HeapObjectHeader* freed_header =
+ new (NotNull, shrink_address) HeapObjectHeader(
+ shrink_size, header->GcInfoIndex(), HeapObjectHeader::kNormalPage);
+ PromptlyFreeObjectInFreeList(freed_header, shrink_size);
+#if DCHECK_IS_ON()
+ DCHECK_EQ(PageFromObject(reinterpret_cast<Address>(header)),
+ FindPageFromAddress(reinterpret_cast<Address>(header)));
+#endif
+ header->SetSize(allocation_size);
+
+ return false;
+}
+
+Address NormalPageArena::LazySweepPages(size_t allocation_size,
+ size_t gc_info_index) {
+ DCHECK(!HasCurrentAllocationArea());
+ AutoReset<bool> is_lazy_sweeping(&is_lazy_sweeping_, true);
+ Address result = nullptr;
+ while (!SweepingCompleted()) {
+ BasePage* page = first_unswept_page_;
+ if (page->IsEmpty()) {
+ page->Unlink(&first_unswept_page_);
+ page->RemoveFromHeap();
+ } else {
+ // Sweep a page and move the page from m_firstUnsweptPages to
+ // m_firstPages.
+ page->Sweep();
+ page->Unlink(&first_unswept_page_);
+ page->Link(&first_page_);
+ page->MarkAsSwept();
+
+ // For NormalPage, stop lazy sweeping once we find a slot to
+ // allocate a new object.
+ result = AllocateFromFreeList(allocation_size, gc_info_index);
+ if (result)
+ break;
+ }
+ }
+ return result;
+}
+
+void NormalPageArena::SetRemainingAllocationSize(
+ size_t new_remaining_allocation_size) {
+ remaining_allocation_size_ = new_remaining_allocation_size;
+
+ // Sync recorded allocated-object size:
+ // - if previous alloc checkpoint is larger, allocation size has increased.
+ // - if smaller, a net reduction in size since last call to
+ // updateRemainingAllocationSize().
+ if (last_remaining_allocation_size_ > remaining_allocation_size_)
+ GetThreadState()->Heap().HeapStats().IncreaseAllocatedObjectSize(
+ last_remaining_allocation_size_ - remaining_allocation_size_);
+ else if (last_remaining_allocation_size_ != remaining_allocation_size_)
+ GetThreadState()->Heap().HeapStats().DecreaseAllocatedObjectSize(
+ remaining_allocation_size_ - last_remaining_allocation_size_);
+ last_remaining_allocation_size_ = remaining_allocation_size_;
+}
+
+void NormalPageArena::UpdateRemainingAllocationSize() {
+ if (last_remaining_allocation_size_ > RemainingAllocationSize()) {
+ GetThreadState()->Heap().HeapStats().IncreaseAllocatedObjectSize(
+ last_remaining_allocation_size_ - RemainingAllocationSize());
+ last_remaining_allocation_size_ = RemainingAllocationSize();
+ }
+ DCHECK_EQ(last_remaining_allocation_size_, RemainingAllocationSize());
+}
+
+void NormalPageArena::SetAllocationPoint(Address point, size_t size) {
+#if DCHECK_IS_ON()
+ if (point) {
+ DCHECK(size);
+ BasePage* page = PageFromObject(point);
+ DCHECK(!page->IsLargeObjectPage());
+ DCHECK_LE(size, static_cast<NormalPage*>(page)->PayloadSize());
+ }
+#endif
+ if (HasCurrentAllocationArea()) {
+ AddToFreeList(CurrentAllocationPoint(), RemainingAllocationSize());
+ }
+ UpdateRemainingAllocationSize();
+ current_allocation_point_ = point;
+ last_remaining_allocation_size_ = remaining_allocation_size_ = size;
+ if (point) {
+ // Current allocation point can never be part of the object bitmap start
+ // because the area can grow or shrink. Will be added back before a GC when
+ // clearing the allocation point.
+ NormalPage* page = reinterpret_cast<NormalPage*>(PageFromObject(point));
+ page->object_start_bit_map()->ClearBit(point);
+ }
+}
+
+Address NormalPageArena::OutOfLineAllocate(size_t allocation_size,
+ size_t gc_info_index) {
+ DCHECK_GT(allocation_size, RemainingAllocationSize());
+ DCHECK_GE(allocation_size, kAllocationGranularity);
+
+ // 1. If this allocation is big enough, allocate a large object.
+ if (allocation_size >= kLargeObjectSizeThreshold)
+ return AllocateLargeObject(allocation_size, gc_info_index);
+
+ // 2. Try to allocate from a free list.
+ UpdateRemainingAllocationSize();
+ Address result = AllocateFromFreeList(allocation_size, gc_info_index);
+ if (result)
+ return result;
+
+ // 3. Reset the allocation point.
+ SetAllocationPoint(nullptr, 0);
+
+ // 4. Lazily sweep pages of this heap until we find a freed area for
+ // this allocation or we finish sweeping all pages of this heap.
+ result = LazySweep(allocation_size, gc_info_index);
+ if (result)
+ return result;
+
+ // 5. Complete sweeping.
+ GetThreadState()->CompleteSweep();
+
+ // 6. Check if we should trigger a GC.
+ GetThreadState()->ScheduleGCIfNeeded();
+
+ // 7. Add a new page to this heap.
+ AllocatePage();
+
+ // 8. Try to allocate from a free list. This allocation must succeed.
+ result = AllocateFromFreeList(allocation_size, gc_info_index);
+ CHECK(result);
+ return result;
+}
+
+Address NormalPageArena::AllocateFromFreeList(size_t allocation_size,
+ size_t gc_info_index) {
+ // Try reusing a block from the largest bin. The underlying reasoning
+ // being that we want to amortize this slow allocation call by carving
+ // off as a large a free block as possible in one go; a block that will
+ // service this block and let following allocations be serviced quickly
+ // by bump allocation.
+ size_t bucket_size = static_cast<size_t>(1)
+ << free_list_.biggest_free_list_index_;
+ int index = free_list_.biggest_free_list_index_;
+ for (; index > 0; --index, bucket_size >>= 1) {
+ FreeListEntry* entry = free_list_.free_lists_[index];
+ if (allocation_size > bucket_size) {
+ // Final bucket candidate; check initial entry if it is able
+ // to service this allocation. Do not perform a linear scan,
+ // as it is considered too costly.
+ if (!entry || entry->size() < allocation_size)
+ break;
+ }
+ if (entry) {
+ entry->Unlink(&free_list_.free_lists_[index]);
+ SetAllocationPoint(entry->GetAddress(), entry->size());
+ DCHECK(HasCurrentAllocationArea());
+ DCHECK_GE(RemainingAllocationSize(), allocation_size);
+ free_list_.biggest_free_list_index_ = index;
+ return AllocateObject(allocation_size, gc_info_index);
+ }
+ }
+ free_list_.biggest_free_list_index_ = index;
+ return nullptr;
+}
+
+LargeObjectArena::LargeObjectArena(ThreadState* state, int index)
+ : BaseArena(state, index) {}
+
+Address LargeObjectArena::AllocateLargeObjectPage(size_t allocation_size,
+ size_t gc_info_index) {
+ // Caller already added space for object header and rounded up to allocation
+ // alignment
+ DCHECK(!(allocation_size & kAllocationMask));
+
+ // 1. Try to sweep large objects more than allocationSize bytes
+ // before allocating a new large object.
+ Address result = LazySweep(allocation_size, gc_info_index);
+ if (result)
+ return result;
+
+ // 2. If we have failed in sweeping allocationSize bytes,
+ // we complete sweeping before allocating this large object.
+ GetThreadState()->CompleteSweep();
+
+ // 3. Check if we should trigger a GC.
+ GetThreadState()->ScheduleGCIfNeeded();
+
+ return DoAllocateLargeObjectPage(allocation_size, gc_info_index);
+}
+
+Address LargeObjectArena::DoAllocateLargeObjectPage(size_t allocation_size,
+ size_t gc_info_index) {
+ size_t large_object_size =
+ LargeObjectPage::PageHeaderSize() + allocation_size;
+// If ASan is supported we add allocationGranularity bytes to the allocated
+// space and poison that to detect overflows
+#if defined(ADDRESS_SANITIZER)
+ large_object_size += kAllocationGranularity;
+#endif
+
+ GetThreadState()->Heap().ShouldFlushHeapDoesNotContainCache();
+ PageMemory* page_memory = PageMemory::Allocate(
+ large_object_size, GetThreadState()->Heap().GetRegionTree());
+ Address large_object_address = page_memory->WritableStart();
+ Address header_address =
+ large_object_address + LargeObjectPage::PageHeaderSize();
+#if DCHECK_IS_ON()
+ // Verify that the allocated PageMemory is expectedly zeroed.
+ for (size_t i = 0; i < large_object_size; ++i)
+ DCHECK(!large_object_address[i]);
+#endif
+ DCHECK_GT(gc_info_index, 0u);
+ LargeObjectPage* large_object = new (large_object_address)
+ LargeObjectPage(page_memory, this, allocation_size);
+ HeapObjectHeader* header = new (NotNull, header_address) HeapObjectHeader(
+ kLargeObjectSizeInHeader, gc_info_index, HeapObjectHeader::kLargePage);
+ Address result = header_address + sizeof(*header);
+ DCHECK(!(reinterpret_cast<uintptr_t>(result) & kAllocationMask));
+
+ // Poison the object header and allocationGranularity bytes after the object
+ ASAN_POISON_MEMORY_REGION(header, sizeof(*header));
+ ASAN_POISON_MEMORY_REGION(large_object->GetAddress() + large_object->size(),
+ kAllocationGranularity);
+
+ large_object->Link(&first_page_);
+
+ GetThreadState()->Heap().HeapStats().IncreaseAllocatedSpace(
+ large_object->size());
+ GetThreadState()->Heap().HeapStats().IncreaseAllocatedObjectSize(
+ large_object->size());
+ return result;
+}
+
+void LargeObjectArena::FreeLargeObjectPage(LargeObjectPage* object) {
+ ASAN_UNPOISON_MEMORY_REGION(object->Payload(), object->PayloadSize());
+ object->GetHeapObjectHeader()->Finalize(object->Payload(),
+ object->PayloadSize());
+ GetThreadState()->Heap().HeapStats().DecreaseAllocatedSpace(object->size());
+
+ // Unpoison the object header and allocationGranularity bytes after the
+ // object before freeing.
+ ASAN_UNPOISON_MEMORY_REGION(object->GetHeapObjectHeader(),
+ sizeof(HeapObjectHeader));
+ ASAN_UNPOISON_MEMORY_REGION(object->GetAddress() + object->size(),
+ kAllocationGranularity);
+
+ PageMemory* memory = object->Storage();
+ object->~LargeObjectPage();
+ delete memory;
+}
+
+Address LargeObjectArena::LazySweepPages(size_t allocation_size,
+ size_t gc_info_index) {
+ Address result = nullptr;
+ size_t swept_size = 0;
+ while (!SweepingCompleted()) {
+ BasePage* page = first_unswept_page_;
+ if (page->IsEmpty()) {
+ swept_size += static_cast<LargeObjectPage*>(page)->PayloadSize();
+ page->Unlink(&first_unswept_page_);
+ page->RemoveFromHeap();
+ // For LargeObjectPage, stop lazy sweeping once we have swept
+ // more than allocationSize bytes.
+ if (swept_size >= allocation_size) {
+ result = DoAllocateLargeObjectPage(allocation_size, gc_info_index);
+ DCHECK(result);
+ break;
+ }
+ } else {
+ // Sweep a page and move the page from m_firstUnsweptPages to
+ // m_firstPages.
+ page->Sweep();
+ page->Unlink(&first_unswept_page_);
+ page->Link(&first_page_);
+ page->MarkAsSwept();
+ }
+ }
+ return result;
+}
+
+FreeList::FreeList() : biggest_free_list_index_(0) {}
+
+void FreeList::AddToFreeList(Address address, size_t size) {
+ DCHECK_LT(size, BlinkPagePayloadSize());
+ // The free list entries are only pointer aligned (but when we allocate
+ // from them we are 8 byte aligned due to the header size).
+ DCHECK(!((reinterpret_cast<uintptr_t>(address) + sizeof(HeapObjectHeader)) &
+ kAllocationMask));
+ DCHECK(!(size & kAllocationMask));
+ ASAN_UNPOISON_MEMORY_REGION(address, size);
+ FreeListEntry* entry;
+ if (size < sizeof(*entry)) {
+ // Create a dummy header with only a size and freelist bit set.
+ DCHECK_GE(size, sizeof(HeapObjectHeader));
+ // Free list encode the size to mark the lost memory as freelist memory.
+ new (NotNull, address) HeapObjectHeader(size, kGcInfoIndexForFreeListHeader,
+ HeapObjectHeader::kNormalPage);
+
+ ASAN_POISON_MEMORY_REGION(address, size);
+ // This memory gets lost. Sweeping can reclaim it.
+ return;
+ }
+ entry = new (NotNull, address) FreeListEntry(size);
+
+#if DCHECK_IS_ON() || defined(LEAK_SANITIZER) || defined(ADDRESS_SANITIZER)
+ // The following logic delays reusing free lists for (at least) one GC
+ // cycle. This is helpful to detect use-after-free errors that could be caused
+ // by lazy sweeping etc.
+ size_t allowed_count = 0;
+ size_t forbidden_count = 0;
+ GetAllowedAndForbiddenCounts(address, size, allowed_count, forbidden_count);
+ size_t entry_count = size - sizeof(FreeListEntry);
+ if (forbidden_count == entry_count) {
+ // If all values in the memory region are reuseForbiddenZapValue,
+ // we flip them to reuseAllowedZapValue. This allows the next
+ // addToFreeList() to add the memory region to the free list
+ // (unless someone concatenates the memory region with another memory
+ // region that contains reuseForbiddenZapValue.)
+ for (size_t i = sizeof(FreeListEntry); i < size; i++)
+ address[i] = kReuseAllowedZapValue;
+ ASAN_POISON_MEMORY_REGION(address, size);
+ // Don't add the memory region to the free list in this addToFreeList().
+ return;
+ }
+ if (allowed_count != entry_count) {
+ // If the memory region mixes reuseForbiddenZapValue and
+ // reuseAllowedZapValue, we (conservatively) flip all the values
+ // to reuseForbiddenZapValue. These values will be changed to
+ // reuseAllowedZapValue in the next addToFreeList().
+ for (size_t i = sizeof(FreeListEntry); i < size; i++)
+ address[i] = kReuseForbiddenZapValue;
+ ASAN_POISON_MEMORY_REGION(address, size);
+ // Don't add the memory region to the free list in this addToFreeList().
+ return;
+ }
+// We reach here only when all the values in the memory region are
+// reuseAllowedZapValue. In this case, we are allowed to add the memory
+// region to the free list and reuse it for another object.
+#endif
+ ASAN_POISON_MEMORY_REGION(address, size);
+
+ int index = BucketIndexForSize(size);
+ entry->Link(&free_lists_[index]);
+ if (index > biggest_free_list_index_)
+ biggest_free_list_index_ = index;
+}
+
+#if DCHECK_IS_ON() || defined(LEAK_SANITIZER) || defined(ADDRESS_SANITIZER) || \
+ defined(MEMORY_SANITIZER)
+NO_SANITIZE_MEMORY
+void NEVER_INLINE
+FreeList::GetAllowedAndForbiddenCounts(Address address,
+ size_t size,
+ size_t& allowed_count,
+ size_t& forbidden_count) {
+ for (size_t i = sizeof(FreeListEntry); i < size; i++) {
+ if (address[i] == kReuseAllowedZapValue)
+ allowed_count++;
+ else if (address[i] == kReuseForbiddenZapValue)
+ forbidden_count++;
+ else
+ NOTREACHED();
+ }
+}
+
+NO_SANITIZE_ADDRESS
+NO_SANITIZE_MEMORY
+void NEVER_INLINE FreeList::ZapFreedMemory(Address address, size_t size) {
+ for (size_t i = 0; i < size; i++) {
+ // See the comment in addToFreeList().
+ if (address[i] != kReuseAllowedZapValue)
+ address[i] = kReuseForbiddenZapValue;
+ }
+}
+
+void NEVER_INLINE FreeList::CheckFreedMemoryIsZapped(Address address,
+ size_t size) {
+ for (size_t i = 0; i < size; i++) {
+ DCHECK(address[i] == kReuseAllowedZapValue ||
+ address[i] == kReuseForbiddenZapValue);
+ }
+}
+#endif
+
+size_t FreeList::FreeListSize() const {
+ size_t free_size = 0;
+ for (unsigned i = 0; i < kBlinkPageSizeLog2; ++i) {
+ FreeListEntry* entry = free_lists_[i];
+ while (entry) {
+ free_size += entry->size();
+ entry = entry->Next();
+ }
+ }
+#if DEBUG_HEAP_FREELIST
+ if (free_size) {
+ LOG_HEAP_FREELIST_VERBOSE() << "FreeList(" << this << "): " << free_size;
+ for (unsigned i = 0; i < kBlinkPageSizeLog2; ++i) {
+ FreeListEntry* entry = free_lists_[i];
+ size_t bucket = 0;
+ size_t count = 0;
+ while (entry) {
+ bucket += entry->size();
+ count++;
+ entry = entry->Next();
+ }
+ if (bucket) {
+ LOG_HEAP_FREELIST_VERBOSE()
+ << "[" << (0x1 << i) << ", " << (0x1 << (i + 1)) << "]: " << bucket
+ << " (" << count << ")";
+ }
+ }
+ }
+#endif
+ return free_size;
+}
+
+void FreeList::Clear() {
+ biggest_free_list_index_ = 0;
+ for (size_t i = 0; i < kBlinkPageSizeLog2; ++i)
+ free_lists_[i] = nullptr;
+}
+
+int FreeList::BucketIndexForSize(size_t size) {
+ DCHECK_GT(size, 0u);
+ int index = -1;
+ while (size) {
+ size >>= 1;
+ index++;
+ }
+ return index;
+}
+
+bool FreeList::TakeSnapshot(const String& dump_base_name) {
+ bool did_dump_bucket_stats = false;
+ for (size_t i = 0; i < kBlinkPageSizeLog2; ++i) {
+ size_t entry_count = 0;
+ size_t free_size = 0;
+ for (FreeListEntry* entry = free_lists_[i]; entry; entry = entry->Next()) {
+ ++entry_count;
+ free_size += entry->size();
+ }
+
+ String dump_name =
+ dump_base_name + String::Format("/buckets/bucket_%lu",
+ static_cast<unsigned long>(1 << i));
+ base::trace_event::MemoryAllocatorDump* bucket_dump =
+ BlinkGCMemoryDumpProvider::Instance()
+ ->CreateMemoryAllocatorDumpForCurrentGC(dump_name);
+ bucket_dump->AddScalar("free_count", "objects", entry_count);
+ bucket_dump->AddScalar("free_size", "bytes", free_size);
+ did_dump_bucket_stats = true;
+ }
+ return did_dump_bucket_stats;
+}
+
+BasePage::BasePage(PageMemory* storage, BaseArena* arena)
+ : magic_(GetMagic()),
+ storage_(storage),
+ arena_(arena),
+ next_(nullptr),
+ swept_(true) {
+#if DCHECK_IS_ON()
+ DCHECK(IsPageHeaderAddress(reinterpret_cast<Address>(this)));
+#endif
+}
+
+NormalPage::NormalPage(PageMemory* storage, BaseArena* arena)
+ : BasePage(storage, arena), object_start_bit_map_(Payload()) {
+#if DCHECK_IS_ON()
+ DCHECK(IsPageHeaderAddress(reinterpret_cast<Address>(this)));
+#endif // DCHECK_IS_ON()
+}
+
+NormalPage::~NormalPage() {
+#if DCHECK_IS_ON()
+ DCHECK(IsPageHeaderAddress(reinterpret_cast<Address>(this)));
+#endif
+}
+
+size_t NormalPage::ObjectPayloadSizeForTesting() {
+ size_t object_payload_size = 0;
+ Address header_address = Payload();
+ DCHECK_NE(header_address, PayloadEnd());
+ do {
+ HeapObjectHeader* header =
+ reinterpret_cast<HeapObjectHeader*>(header_address);
+ if (!header->IsFree()) {
+ object_payload_size += header->PayloadSize();
+ }
+ DCHECK_LT(header->size(), BlinkPagePayloadSize());
+ header_address += header->size();
+ DCHECK_LE(header_address, PayloadEnd());
+ } while (header_address < PayloadEnd());
+ return object_payload_size;
+}
+
+bool NormalPage::IsEmpty() {
+ HeapObjectHeader* header = reinterpret_cast<HeapObjectHeader*>(Payload());
+ return header->IsFree() && header->size() == PayloadSize();
+}
+
+void NormalPage::RemoveFromHeap() {
+ ArenaForNormalPage()->FreePage(this);
+}
+
+#if !DCHECK_IS_ON() && !defined(LEAK_SANITIZER) && !defined(ADDRESS_SANITIZER)
+static void DiscardPages(Address begin, Address end) {
+ uintptr_t begin_address =
+ WTF::RoundUpToSystemPage(reinterpret_cast<uintptr_t>(begin));
+ uintptr_t end_address =
+ WTF::RoundDownToSystemPage(reinterpret_cast<uintptr_t>(end));
+ if (begin_address < end_address)
+ WTF::DiscardSystemPages(reinterpret_cast<void*>(begin_address),
+ end_address - begin_address);
+}
+#endif
+
+void NormalPage::Sweep() {
+ object_start_bit_map()->Clear();
+ size_t marked_object_size = 0;
+ Address start_of_gap = Payload();
+ NormalPageArena* page_arena = ArenaForNormalPage();
+ for (Address header_address = start_of_gap; header_address < PayloadEnd();) {
+ HeapObjectHeader* header =
+ reinterpret_cast<HeapObjectHeader*>(header_address);
+ size_t size = header->size();
+ DCHECK_GT(size, 0u);
+ DCHECK_LT(size, BlinkPagePayloadSize());
+
+ if (header->IsFree()) {
+ // Zero the memory in the free list header to maintain the
+ // invariant that memory on the free list is zero filled.
+ // The rest of the memory is already on the free list and is
+ // therefore already zero filled.
+ SET_MEMORY_INACCESSIBLE(header_address, size < sizeof(FreeListEntry)
+ ? size
+ : sizeof(FreeListEntry));
+ CHECK_MEMORY_INACCESSIBLE(header_address, size);
+ header_address += size;
+ continue;
+ }
+ if (!header->IsMarked()) {
+ // This is a fast version of header->PayloadSize().
+ size_t payload_size = size - sizeof(HeapObjectHeader);
+ Address payload = header->Payload();
+ // For ASan, unpoison the object before calling the finalizer. The
+ // finalized object will be zero-filled and poison'ed afterwards.
+ // Given all other unmarked objects are poisoned, ASan will detect
+ // an error if the finalizer touches any other on-heap object that
+ // die at the same GC cycle.
+ ASAN_UNPOISON_MEMORY_REGION(payload, payload_size);
+ header->Finalize(payload, payload_size);
+ // This memory will be added to the freelist. Maintain the invariant
+ // that memory on the freelist is zero filled.
+ SET_MEMORY_INACCESSIBLE(header_address, size);
+ header_address += size;
+ continue;
+ }
+ if (start_of_gap != header_address) {
+ page_arena->AddToFreeList(start_of_gap, header_address - start_of_gap);
+#if !DCHECK_IS_ON() && !defined(LEAK_SANITIZER) && !defined(ADDRESS_SANITIZER)
+ // Discarding pages increases page faults and may regress performance.
+ // So we enable this only on low-RAM devices.
+ if (MemoryCoordinator::IsLowEndDevice())
+ DiscardPages(start_of_gap + sizeof(FreeListEntry), header_address);
+#endif
+ }
+ object_start_bit_map()->SetBit(header_address);
+ header->Unmark();
+ header_address += size;
+ marked_object_size += size;
+ start_of_gap = header_address;
+ }
+ if (start_of_gap != PayloadEnd()) {
+ page_arena->AddToFreeList(start_of_gap, PayloadEnd() - start_of_gap);
+#if !DCHECK_IS_ON() && !defined(LEAK_SANITIZER) && !defined(ADDRESS_SANITIZER)
+ if (MemoryCoordinator::IsLowEndDevice())
+ DiscardPages(start_of_gap + sizeof(FreeListEntry), PayloadEnd());
+#endif
+ }
+
+ if (marked_object_size) {
+ page_arena->GetThreadState()->Heap().HeapStats().IncreaseMarkedObjectSize(
+ marked_object_size);
+ }
+
+ VerifyObjectStartBitmapIsConsistentWithPayload();
+}
+
+void NormalPage::SweepAndCompact(CompactionContext& context) {
+ object_start_bit_map()->Clear();
+ NormalPage*& current_page = context.current_page_;
+ size_t& allocation_point = context.allocation_point_;
+
+ size_t marked_object_size = 0;
+ NormalPageArena* page_arena = ArenaForNormalPage();
+#if defined(ADDRESS_SANITIZER)
+ bool is_vector_arena =
+ ThreadHeap::IsVectorArenaIndex(page_arena->ArenaIndex());
+#endif
+ HeapCompact* compact = page_arena->GetThreadState()->Heap().Compaction();
+ for (Address header_address = Payload(); header_address < PayloadEnd();) {
+ HeapObjectHeader* header =
+ reinterpret_cast<HeapObjectHeader*>(header_address);
+ size_t size = header->size();
+ DCHECK_GT(size, 0u);
+ DCHECK_LT(size, BlinkPagePayloadSize());
+
+ if (header->IsFree()) {
+ // Unpoison the freelist entry so that we
+ // can compact into it as wanted.
+ ASAN_UNPOISON_MEMORY_REGION(header_address, size);
+ header_address += size;
+ continue;
+ }
+ // This is a fast version of header->PayloadSize().
+ size_t payload_size = size - sizeof(HeapObjectHeader);
+ Address payload = header->Payload();
+ if (!header->IsMarked()) {
+ // For ASan, unpoison the object before calling the finalizer. The
+ // finalized object will be zero-filled and poison'ed afterwards.
+ // Given all other unmarked objects are poisoned, ASan will detect
+ // an error if the finalizer touches any other on-heap object that
+ // die at the same GC cycle.
+ ASAN_UNPOISON_MEMORY_REGION(header_address, size);
+ header->Finalize(payload, payload_size);
+
+// As compaction is under way, leave the freed memory accessible
+// while compacting the rest of the page. We just zap the payload
+// to catch out other finalizers trying to access it.
+#if DCHECK_IS_ON() || defined(LEAK_SANITIZER) || defined(ADDRESS_SANITIZER) || \
+ defined(MEMORY_SANITIZER)
+ FreeList::ZapFreedMemory(payload, payload_size);
+#endif
+ header_address += size;
+ continue;
+ }
+ header->Unmark();
+ // Allocate and copy over the live object.
+ Address compact_frontier = current_page->Payload() + allocation_point;
+ if (compact_frontier + size > current_page->PayloadEnd()) {
+ // Can't fit on current allocation page; add remaining onto the
+ // freelist and advance to next available page.
+ //
+ // TODO(sof): be more clever & compact later objects into
+ // |currentPage|'s unused slop.
+ current_page->Link(context.compacted_pages_);
+ size_t free_size = current_page->PayloadSize() - allocation_point;
+ if (free_size) {
+ SET_MEMORY_INACCESSIBLE(compact_frontier, free_size);
+ current_page->ArenaForNormalPage()->AddToFreeList(compact_frontier,
+ free_size);
+ }
+
+ BasePage* next_available_page;
+ context.available_pages_->Unlink(&next_available_page);
+ current_page = reinterpret_cast<NormalPage*>(context.available_pages_);
+ context.available_pages_ = next_available_page;
+ allocation_point = 0;
+ compact_frontier = current_page->Payload();
+ }
+ if (LIKELY(compact_frontier != header_address)) {
+#if defined(ADDRESS_SANITIZER)
+ // Unpoison the header + if it is a vector backing
+ // store object, let go of the container annotations.
+ // Do that by unpoisoning the payload entirely.
+ ASAN_UNPOISON_MEMORY_REGION(header, sizeof(HeapObjectHeader));
+ if (is_vector_arena)
+ ASAN_UNPOISON_MEMORY_REGION(payload, payload_size);
+#endif
+ // Use a non-overlapping copy, if possible.
+ if (current_page == this)
+ memmove(compact_frontier, header_address, size);
+ else
+ memcpy(compact_frontier, header_address, size);
+ compact->Relocate(payload, compact_frontier + sizeof(HeapObjectHeader));
+ }
+ current_page->object_start_bit_map()->SetBit(compact_frontier);
+ header_address += size;
+ marked_object_size += size;
+ allocation_point += size;
+ DCHECK(allocation_point <= current_page->PayloadSize());
+ }
+ if (marked_object_size) {
+ page_arena->GetThreadState()->Heap().HeapStats().IncreaseMarkedObjectSize(
+ marked_object_size);
+ }
+
+#if DCHECK_IS_ON() || defined(LEAK_SANITIZER) || defined(ADDRESS_SANITIZER) || \
+ defined(MEMORY_SANITIZER)
+ // Zap the unused portion, until it is either compacted into or freed.
+ if (current_page != this) {
+ FreeList::ZapFreedMemory(Payload(), PayloadSize());
+ } else {
+ FreeList::ZapFreedMemory(Payload() + allocation_point,
+ PayloadSize() - allocation_point);
+ }
+#endif
+}
+
+void NormalPage::MakeConsistentForMutator() {
+ object_start_bit_map()->Clear();
+ Address start_of_gap = Payload();
+ NormalPageArena* normal_arena = ArenaForNormalPage();
+ for (Address header_address = Payload(); header_address < PayloadEnd();) {
+ HeapObjectHeader* header =
+ reinterpret_cast<HeapObjectHeader*>(header_address);
+ size_t size = header->size();
+ DCHECK_LT(size, BlinkPagePayloadSize());
+ if (header->IsFree()) {
+ // Zero the memory in the free list header to maintain the
+ // invariant that memory on the free list is zero filled.
+ // The rest of the memory is already on the free list and is
+ // therefore already zero filled.
+ SET_MEMORY_INACCESSIBLE(header_address, size < sizeof(FreeListEntry)
+ ? size
+ : sizeof(FreeListEntry));
+ CHECK_MEMORY_INACCESSIBLE(header_address, size);
+ header_address += size;
+ continue;
+ }
+ if (start_of_gap != header_address)
+ normal_arena->AddToFreeList(start_of_gap, header_address - start_of_gap);
+ if (header->IsMarked()) {
+ header->Unmark();
+ }
+ object_start_bit_map()->SetBit(header_address);
+ header_address += size;
+ start_of_gap = header_address;
+ DCHECK_LE(header_address, PayloadEnd());
+ }
+ if (start_of_gap != PayloadEnd())
+ normal_arena->AddToFreeList(start_of_gap, PayloadEnd() - start_of_gap);
+
+ VerifyObjectStartBitmapIsConsistentWithPayload();
+}
+
+#if defined(ADDRESS_SANITIZER)
+void NormalPage::PoisonUnmarkedObjects() {
+ for (Address header_address = Payload(); header_address < PayloadEnd();) {
+ HeapObjectHeader* header =
+ reinterpret_cast<HeapObjectHeader*>(header_address);
+ DCHECK_LT(header->size(), BlinkPagePayloadSize());
+ // Check if a free list entry first since we cannot call
+ // isMarked on a free list entry.
+ if (header->IsFree()) {
+ header_address += header->size();
+ continue;
+ }
+ if (!header->IsMarked())
+ ASAN_POISON_MEMORY_REGION(header->Payload(), header->PayloadSize());
+ header_address += header->size();
+ }
+}
+#endif
+
+void NormalPage::VerifyObjectStartBitmapIsConsistentWithPayload() {
+#if DCHECK_IS_ON()
+ Address current_allocation_point =
+ ArenaForNormalPage()->CurrentAllocationPoint();
+ DCHECK(!current_allocation_point ||
+ (PageFromObject(current_allocation_point) != this));
+
+ HeapObjectHeader* current_header =
+ reinterpret_cast<HeapObjectHeader*>(Payload());
+ object_start_bit_map()->Iterate([&current_header](Address object_address) {
+ const HeapObjectHeader* object_header =
+ reinterpret_cast<HeapObjectHeader*>(object_address);
+ DCHECK_EQ(object_header, current_header);
+ DCHECK(object_header->IsValidOrZapped());
+ current_header = reinterpret_cast<HeapObjectHeader*>(object_address +
+ object_header->size());
+ });
+#endif // DCHECK_IS_ON()
+}
+
+void NormalPage::VerifyMarking() {
+ DCHECK(!ArenaForNormalPage()
+ ->GetThreadState()
+ ->Heap()
+ .GetStackFrameDepth()
+ .IsSafeToRecurse());
+ DCHECK(!ArenaForNormalPage()->CurrentAllocationPoint());
+ MarkingVerifier verifier(ArenaForNormalPage()->GetThreadState());
+ for (Address header_address = Payload(); header_address < PayloadEnd();) {
+ HeapObjectHeader* header =
+ reinterpret_cast<HeapObjectHeader*>(header_address);
+ verifier.VerifyObject(header);
+ header_address += header->size();
+ }
+}
+
+Address ObjectStartBitmap::FindHeader(
+ Address address_maybe_pointing_to_the_middle_of_object) {
+ size_t object_offset =
+ address_maybe_pointing_to_the_middle_of_object - offset_;
+ size_t object_start_number = object_offset / kAllocationGranularity;
+ size_t cell_index = object_start_number / kCellSize;
+#if DCHECK_IS_ON()
+ const size_t bitmap_size = kReservedForBitmap;
+ DCHECK_LT(cell_index, bitmap_size);
+#endif
+ size_t bit = object_start_number & kCellMask;
+ uint8_t byte = object_start_bit_map_[cell_index] & ((1 << (bit + 1)) - 1);
+ while (!byte) {
+ DCHECK_LT(0u, cell_index);
+ byte = object_start_bit_map_[--cell_index];
+ }
+ int leading_zeroes = base::bits::CountLeadingZeroBits(byte);
+ object_start_number =
+ (cell_index * kCellSize) + (kCellSize - 1) - leading_zeroes;
+ object_offset = object_start_number * kAllocationGranularity;
+ return object_offset + offset_;
+}
+
+HeapObjectHeader* NormalPage::FindHeaderFromAddress(Address address) {
+ if (!ContainedInObjectPayload(address))
+ return nullptr;
+ if (ArenaForNormalPage()->IsInCurrentAllocationPointRegion(address))
+ return nullptr;
+ HeapObjectHeader* header = reinterpret_cast<HeapObjectHeader*>(
+ object_start_bit_map()->FindHeader(address));
+ if (header->IsFree())
+ return nullptr;
+ DCHECK_LT(0u, header->GcInfoIndex());
+ DCHECK_GT(header->PayloadEnd(), address);
+ return header;
+}
+
+void NormalPage::TakeSnapshot(base::trace_event::MemoryAllocatorDump* page_dump,
+ ThreadState::GCSnapshotInfo& info,
+ HeapSnapshotInfo& heap_info) {
+ HeapObjectHeader* header = nullptr;
+ size_t live_count = 0;
+ size_t dead_count = 0;
+ size_t free_count = 0;
+ size_t live_size = 0;
+ size_t dead_size = 0;
+ size_t free_size = 0;
+ for (Address header_address = Payload(); header_address < PayloadEnd();
+ header_address += header->size()) {
+ header = reinterpret_cast<HeapObjectHeader*>(header_address);
+ if (header->IsFree()) {
+ free_count++;
+ free_size += header->size();
+ } else if (header->IsMarked()) {
+ live_count++;
+ live_size += header->size();
+
+ size_t gc_info_index = header->GcInfoIndex();
+ info.live_count[gc_info_index]++;
+ info.live_size[gc_info_index] += header->size();
+ } else {
+ dead_count++;
+ dead_size += header->size();
+
+ size_t gc_info_index = header->GcInfoIndex();
+ info.dead_count[gc_info_index]++;
+ info.dead_size[gc_info_index] += header->size();
+ }
+ }
+
+ page_dump->AddScalar("live_count", "objects", live_count);
+ page_dump->AddScalar("dead_count", "objects", dead_count);
+ page_dump->AddScalar("free_count", "objects", free_count);
+ page_dump->AddScalar("live_size", "bytes", live_size);
+ page_dump->AddScalar("dead_size", "bytes", dead_size);
+ page_dump->AddScalar("free_size", "bytes", free_size);
+ heap_info.free_size += free_size;
+ heap_info.free_count += free_count;
+}
+
+#if DCHECK_IS_ON()
+bool NormalPage::Contains(Address addr) {
+ Address blink_page_start = RoundToBlinkPageStart(GetAddress());
+ // Page is at aligned address plus guard page size.
+ DCHECK_EQ(blink_page_start, GetAddress() - kBlinkGuardPageSize);
+ return blink_page_start <= addr && addr < blink_page_start + kBlinkPageSize;
+}
+#endif
+
+LargeObjectPage::LargeObjectPage(PageMemory* storage,
+ BaseArena* arena,
+ size_t payload_size)
+ : BasePage(storage, arena),
+ payload_size_(payload_size)
+#ifdef ANNOTATE_CONTIGUOUS_CONTAINER
+ ,
+ is_vector_backing_page_(false)
+#endif
+{
+}
+
+size_t LargeObjectPage::ObjectPayloadSizeForTesting() {
+ return PayloadSize();
+}
+
+bool LargeObjectPage::IsEmpty() {
+ return !GetHeapObjectHeader()->IsMarked();
+}
+
+void LargeObjectPage::RemoveFromHeap() {
+ static_cast<LargeObjectArena*>(Arena())->FreeLargeObjectPage(this);
+}
+
+void LargeObjectPage::Sweep() {
+ GetHeapObjectHeader()->Unmark();
+ Arena()->GetThreadState()->Heap().HeapStats().IncreaseMarkedObjectSize(
+ size());
+}
+
+void LargeObjectPage::MakeConsistentForMutator() {
+ HeapObjectHeader* header = GetHeapObjectHeader();
+ if (header->IsMarked())
+ header->Unmark();
+}
+
+#if defined(ADDRESS_SANITIZER)
+void LargeObjectPage::PoisonUnmarkedObjects() {
+ HeapObjectHeader* header = GetHeapObjectHeader();
+ if (!header->IsMarked())
+ ASAN_POISON_MEMORY_REGION(header->Payload(), header->PayloadSize());
+}
+#endif
+
+void LargeObjectPage::TakeSnapshot(
+ base::trace_event::MemoryAllocatorDump* page_dump,
+ ThreadState::GCSnapshotInfo& info,
+ HeapSnapshotInfo&) {
+ size_t live_size = 0;
+ size_t dead_size = 0;
+ size_t live_count = 0;
+ size_t dead_count = 0;
+ HeapObjectHeader* header = GetHeapObjectHeader();
+ size_t gc_info_index = header->GcInfoIndex();
+ size_t payload_size = header->PayloadSize();
+ if (header->IsMarked()) {
+ live_count = 1;
+ live_size += payload_size;
+ info.live_count[gc_info_index]++;
+ info.live_size[gc_info_index] += payload_size;
+ } else {
+ dead_count = 1;
+ dead_size += payload_size;
+ info.dead_count[gc_info_index]++;
+ info.dead_size[gc_info_index] += payload_size;
+ }
+
+ page_dump->AddScalar("live_count", "objects", live_count);
+ page_dump->AddScalar("dead_count", "objects", dead_count);
+ page_dump->AddScalar("live_size", "bytes", live_size);
+ page_dump->AddScalar("dead_size", "bytes", dead_size);
+}
+
+#if DCHECK_IS_ON()
+bool LargeObjectPage::Contains(Address object) {
+ return RoundToBlinkPageStart(GetAddress()) <= object &&
+ object < RoundToBlinkPageEnd(GetAddress() + size());
+}
+#endif
+
+void HeapDoesNotContainCache::Flush() {
+ if (has_entries_) {
+ for (size_t i = 0; i < kNumberOfEntries; ++i)
+ entries_[i] = nullptr;
+ has_entries_ = false;
+ }
+}
+
+size_t HeapDoesNotContainCache::GetHash(Address address) {
+ size_t value = (reinterpret_cast<size_t>(address) >> kBlinkPageSizeLog2);
+ value ^= value >> kNumberOfEntriesLog2;
+ value ^= value >> (kNumberOfEntriesLog2 * 2);
+ value &= kNumberOfEntries - 1;
+ return value & ~1; // Returns only even number.
+}
+
+bool HeapDoesNotContainCache::Lookup(Address address) {
+ DCHECK(ThreadState::Current()->InAtomicMarkingPause());
+
+ size_t index = GetHash(address);
+ DCHECK(!(index & 1));
+ Address cache_page = RoundToBlinkPageStart(address);
+ if (entries_[index] == cache_page)
+ return entries_[index];
+ if (entries_[index + 1] == cache_page)
+ return entries_[index + 1];
+ return false;
+}
+
+void HeapDoesNotContainCache::AddEntry(Address address) {
+ DCHECK(ThreadState::Current()->InAtomicMarkingPause());
+
+ has_entries_ = true;
+ size_t index = GetHash(address);
+ DCHECK(!(index & 1));
+ Address cache_page = RoundToBlinkPageStart(address);
+ entries_[index + 1] = entries_[index];
+ entries_[index] = cache_page;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/heap/heap_page.h b/chromium/third_party/blink/renderer/platform/heap/heap_page.h
new file mode 100644
index 00000000000..60f367ef845
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/heap_page.h
@@ -0,0 +1,1195 @@
+/*
+ * Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_PAGE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_PAGE_H_
+
+#include <stdint.h>
+#include "base/bits.h"
+#include "base/trace_event/memory_allocator_dump.h"
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/heap/blink_gc.h"
+#include "third_party/blink/renderer/platform/heap/gc_info.h"
+#include "third_party/blink/renderer/platform/heap/thread_state.h"
+#include "third_party/blink/renderer/platform/heap/visitor.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/address_sanitizer.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/container_annotations.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+// TODO(palmer): Document the reason for 17.
+constexpr size_t kBlinkPageSizeLog2 = 17;
+constexpr size_t kBlinkPageSize = 1 << kBlinkPageSizeLog2;
+constexpr size_t kBlinkPageOffsetMask = kBlinkPageSize - 1;
+constexpr size_t kBlinkPageBaseMask = ~kBlinkPageOffsetMask;
+
+// We allocate pages at random addresses but in groups of kBlinkPagesPerRegion
+// at a given random address. We group pages to not spread out too much over the
+// address space which would blow away the page tables and lead to bad
+// performance.
+constexpr size_t kBlinkPagesPerRegion = 10;
+
+// TODO(nya): Replace this with something like #if ENABLE_NACL.
+#if 0
+// NaCl's system page size is 64 KiB. This causes a problem in Oilpan's heap
+// layout because Oilpan allocates two guard pages for each Blink page (whose
+// size is kBlinkPageSize = 2^17 = 128 KiB). So we don't use guard pages in
+// NaCl.
+constexpr size_t kBlinkGuardPageSize = 0;
+#else
+constexpr size_t kBlinkGuardPageSize = base::kSystemPageSize;
+#endif
+
+// Double precision floats are more efficient when 8-byte aligned, so we 8-byte
+// align all allocations (even on 32 bit systems).
+static_assert(8 == sizeof(double), "We expect sizeof(double) to be 8");
+constexpr size_t kAllocationGranularity = sizeof(double);
+constexpr size_t kAllocationMask = kAllocationGranularity - 1;
+constexpr size_t kMaxHeapObjectSizeLog2 = 27;
+constexpr size_t kMaxHeapObjectSize = 1 << kMaxHeapObjectSizeLog2;
+constexpr size_t kLargeObjectSizeThreshold = kBlinkPageSize / 2;
+
+// A zap value used for freed memory that is allowed to be added to the free
+// list in the next call to AddToFreeList.
+constexpr uint8_t kReuseAllowedZapValue = 0x2a;
+// A zap value used for freed memory that is forbidden to be added to the free
+// list in the next call to AddToFreeList.
+constexpr uint8_t kReuseForbiddenZapValue = 0x2c;
+
+// In non-production builds, memory is zapped when it's freed. The zapped memory
+// is zeroed out when the memory is reused in ThreadHeap::AllocateObject.
+//
+// In production builds, memory is not zapped (for performance). The memory is
+// just zeroed out when it is added to the free list.
+#if defined(MEMORY_SANITIZER)
+// TODO(kojii): We actually need __msan_poison/unpoison here, but it'll be
+// added later.
+#define SET_MEMORY_INACCESSIBLE(address, size) \
+ FreeList::ZapFreedMemory(address, size);
+#define SET_MEMORY_ACCESSIBLE(address, size) memset((address), 0, (size))
+#define CHECK_MEMORY_INACCESSIBLE(address, size) \
+ ASAN_UNPOISON_MEMORY_REGION(address, size); \
+ FreeList::CheckFreedMemoryIsZapped(address, size); \
+ ASAN_POISON_MEMORY_REGION(address, size)
+#elif DCHECK_IS_ON() || defined(LEAK_SANITIZER) || defined(ADDRESS_SANITIZER)
+#define SET_MEMORY_INACCESSIBLE(address, size) \
+ FreeList::ZapFreedMemory(address, size); \
+ ASAN_POISON_MEMORY_REGION(address, size)
+#define SET_MEMORY_ACCESSIBLE(address, size) \
+ ASAN_UNPOISON_MEMORY_REGION(address, size); \
+ memset((address), 0, (size))
+#define CHECK_MEMORY_INACCESSIBLE(address, size) \
+ ASAN_UNPOISON_MEMORY_REGION(address, size); \
+ FreeList::CheckFreedMemoryIsZapped(address, size); \
+ ASAN_POISON_MEMORY_REGION(address, size)
+#else
+#define SET_MEMORY_INACCESSIBLE(address, size) memset((address), 0, (size))
+#define SET_MEMORY_ACCESSIBLE(address, size) \
+ do { \
+ } while (false)
+#define CHECK_MEMORY_INACCESSIBLE(address, size) \
+ do { \
+ } while (false)
+#endif
+
+class NormalPageArena;
+class PageMemory;
+class BaseArena;
+
+// Returns a random value.
+//
+// The implementation gets its randomness from the locations of 2 independent
+// sources of address space layout randomization: a function in a Chrome
+// executable image, and a function in an external DLL/so. This implementation
+// should be fast and small, and should have the benefit of requiring
+// attackers to discover and use 2 independent weak infoleak bugs, or 1
+// arbitrary infoleak bug (used twice).
+inline uint32_t GetRandomMagic();
+
+// HeapObjectHeader is a 64-bit (64-bit platforms) or 32-bit (32-bit platforms)
+// object that has the following layout:
+//
+// | random magic value (32 bits) | <- present on 64-bit platforms only
+// | gc_info_index (14 bits) |
+// | DOM mark bit (1 bit) |
+// | size (14 bits) |
+// | dead bit (1 bit) |
+// | freed bit (1 bit) |
+// | mark bit (1 bit) |
+//
+// - For non-large objects, 14 bits are enough for |size| because the Blink page
+// size is 2^kBlinkPageSizeLog2 (kBlinkPageSizeLog2 = 17) bytes, and each
+// object is guaranteed to be aligned on a kAllocationGranularity-byte
+// boundary.
+// - For large objects, |size| is 0. The actual size of a large object is
+// stored in |LargeObjectPage::payload_size_|.
+// - 1 bit used to mark DOM trees for V8.
+// - 14 bits are enough for |gc_info_index| because there are fewer than 2^14
+// types in Blink.
+constexpr size_t kHeaderWrapperMarkBitMask = 1u << kBlinkPageSizeLog2;
+constexpr size_t kHeaderGCInfoIndexShift = kBlinkPageSizeLog2 + 1;
+constexpr size_t kHeaderGCInfoIndexMask = (static_cast<size_t>((1 << 14) - 1))
+ << kHeaderGCInfoIndexShift;
+constexpr size_t kHeaderSizeMask = (static_cast<size_t>((1 << 14) - 1)) << 3;
+constexpr size_t kHeaderMarkBitMask = 1;
+constexpr size_t kHeaderFreedBitMask = 2;
+constexpr size_t kLargeObjectSizeInHeader = 0;
+constexpr size_t kGcInfoIndexForFreeListHeader = 0;
+constexpr size_t kNonLargeObjectPageSizeMax = 1 << kBlinkPageSizeLog2;
+
+static_assert(
+ kNonLargeObjectPageSizeMax >= kBlinkPageSize,
+ "max size supported by HeapObjectHeader must at least be kBlinkPageSize");
+
+class PLATFORM_EXPORT HeapObjectHeader {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ enum HeaderLocation { kNormalPage, kLargePage };
+
+ // If |gc_info_index| is 0, this header is interpreted as a free list header.
+ NO_SANITIZE_ADDRESS
+ inline HeapObjectHeader(size_t, size_t, HeaderLocation);
+
+ NO_SANITIZE_ADDRESS bool IsFree() const {
+ return encoded_ & kHeaderFreedBitMask;
+ }
+
+ size_t size() const;
+
+ NO_SANITIZE_ADDRESS size_t GcInfoIndex() const {
+ return (encoded_ & kHeaderGCInfoIndexMask) >> kHeaderGCInfoIndexShift;
+ }
+
+ NO_SANITIZE_ADDRESS void SetSize(size_t size) {
+ DCHECK_LT(size, kNonLargeObjectPageSizeMax);
+ CheckHeader();
+ encoded_ = static_cast<uint32_t>(size) | (encoded_ & ~kHeaderSizeMask);
+ }
+
+ bool IsWrapperHeaderMarked() const;
+ void MarkWrapperHeader();
+ void UnmarkWrapperHeader();
+ bool IsMarked() const;
+ void Mark();
+ void Unmark();
+ bool TryMark();
+
+ // The payload starts directly after the HeapObjectHeader, and the payload
+ // size does not include the sizeof(HeapObjectHeader).
+ Address Payload();
+ size_t PayloadSize();
+ Address PayloadEnd();
+
+ void Finalize(Address, size_t);
+ static HeapObjectHeader* FromPayload(const void*);
+
+ // Some callers formerly called |FromPayload| only for its side-effect of
+ // calling |CheckHeader| (which is now private). This function does that, but
+ // its explanatory name makes the intention at the call sites easier to
+ // understand, and is public.
+ static void CheckFromPayload(const void*);
+
+ // Returns true if magic number is valid.
+ bool IsValid() const;
+ // Returns true if magic number is valid or zapped.
+ bool IsValidOrZapped() const;
+
+ // The following values are used when zapping free list entries.
+ // Regular zapping value.
+ static const uint32_t kZappedMagic = 0xDEAD4321;
+ // On debug and sanitizer builds the zap values differ, indicating when free
+ // list entires are allowed to be reused.
+ static const uint32_t kZappedMagicAllowed = 0x2a2a2a2a;
+ static const uint32_t kZappedMagicForbidden = 0x2c2c2c2c;
+
+ protected:
+#if DCHECK_IS_ON() && defined(ARCH_CPU_64_BITS)
+ // Zap |m_magic| with a new magic number that means there was once an object
+ // allocated here, but it was freed because nobody marked it during GC.
+ void ZapMagic();
+#endif
+
+ private:
+ void CheckHeader() const;
+
+#if defined(ARCH_CPU_64_BITS)
+ // Returns a random magic value.
+ uint32_t GetMagic() const { return GetRandomMagic() ^ 0x6e0b6ead; }
+ uint32_t magic_;
+#endif // defined(ARCH_CPU_64_BITS)
+
+ uint32_t encoded_;
+};
+
+class FreeListEntry final : public HeapObjectHeader {
+ public:
+ NO_SANITIZE_ADDRESS
+ explicit FreeListEntry(size_t size)
+ : HeapObjectHeader(size,
+ kGcInfoIndexForFreeListHeader,
+ HeapObjectHeader::kNormalPage),
+ next_(nullptr) {
+#if DCHECK_IS_ON() && defined(ARCH_CPU_64_BITS)
+ DCHECK_GE(size, sizeof(HeapObjectHeader));
+ ZapMagic();
+#endif
+ }
+
+ Address GetAddress() { return reinterpret_cast<Address>(this); }
+
+ NO_SANITIZE_ADDRESS
+ void Unlink(FreeListEntry** previous_next) {
+ *previous_next = next_;
+ next_ = nullptr;
+ }
+
+ NO_SANITIZE_ADDRESS
+ void Link(FreeListEntry** previous_next) {
+ next_ = *previous_next;
+ *previous_next = this;
+ }
+
+ NO_SANITIZE_ADDRESS
+ FreeListEntry* Next() const { return next_; }
+
+ NO_SANITIZE_ADDRESS
+ void Append(FreeListEntry* next) {
+ DCHECK(!next_);
+ next_ = next;
+ }
+
+ private:
+ FreeListEntry* next_;
+};
+
+// Blink heap pages are set up with a guard page before and after the payload.
+inline size_t BlinkPagePayloadSize() {
+ return kBlinkPageSize - 2 * kBlinkGuardPageSize;
+}
+
+// Blink heap pages are aligned to the Blink heap page size. Therefore, the
+// start of a Blink page can be obtained by rounding down to the Blink page
+// size.
+inline Address RoundToBlinkPageStart(Address address) {
+ return reinterpret_cast<Address>(reinterpret_cast<uintptr_t>(address) &
+ kBlinkPageBaseMask);
+}
+
+inline Address RoundToBlinkPageEnd(Address address) {
+ return reinterpret_cast<Address>(reinterpret_cast<uintptr_t>(address - 1) &
+ kBlinkPageBaseMask) +
+ kBlinkPageSize;
+}
+
+// Masks an address down to the enclosing Blink page base address.
+inline Address BlinkPageAddress(Address address) {
+ return reinterpret_cast<Address>(reinterpret_cast<uintptr_t>(address) &
+ kBlinkPageBaseMask);
+}
+
+inline bool VTableInitialized(void* object_pointer) {
+ return !!(*reinterpret_cast<Address*>(object_pointer));
+}
+
+#if DCHECK_IS_ON()
+
+// Sanity check for a page header address: the address of the page header should
+// be 1 OS page size away from being Blink page size-aligned.
+inline bool IsPageHeaderAddress(Address address) {
+ return !((reinterpret_cast<uintptr_t>(address) & kBlinkPageOffsetMask) -
+ kBlinkGuardPageSize);
+}
+
+#endif
+
+// |BasePage| is a base class for |NormalPage| and |LargeObjectPage|.
+//
+// - |NormalPage| is a page whose size is |kBlinkPageSize|. A |NormalPage| can
+// contain multiple objects. An object whose size is smaller than
+// |kLargeObjectSizeThreshold| is stored in a |NormalPage|.
+//
+// - |LargeObjectPage| is a page that contains only one object. The object size
+// is arbitrary. An object whose size is larger than |kBlinkPageSize| is
+// stored as a single project in |LargeObjectPage|.
+//
+// Note: An object whose size is between |kLargeObjectSizeThreshold| and
+// |kBlinkPageSize| can go to either of |NormalPage| or |LargeObjectPage|.
+class BasePage {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ BasePage(PageMemory*, BaseArena*);
+ virtual ~BasePage() = default;
+
+ void Link(BasePage** previous_next) {
+ next_ = *previous_next;
+ *previous_next = this;
+ }
+ void Unlink(BasePage** previous_next) {
+ *previous_next = next_;
+ next_ = nullptr;
+ }
+ BasePage* Next() const { return next_; }
+
+ // Virtual methods are slow. So performance-sensitive methods should be
+ // defined as non-virtual methods on |NormalPage| and |LargeObjectPage|. The
+ // following methods are not performance-sensitive.
+ virtual size_t ObjectPayloadSizeForTesting() = 0;
+ virtual bool IsEmpty() = 0;
+ virtual void RemoveFromHeap() = 0;
+ virtual void Sweep() = 0;
+ virtual void MakeConsistentForMutator() = 0;
+
+#if defined(ADDRESS_SANITIZER)
+ virtual void PoisonUnmarkedObjects() = 0;
+#endif
+
+ class HeapSnapshotInfo {
+ STACK_ALLOCATED();
+
+ public:
+ size_t free_count = 0;
+ size_t free_size = 0;
+ };
+
+ virtual void TakeSnapshot(base::trace_event::MemoryAllocatorDump*,
+ ThreadState::GCSnapshotInfo&,
+ HeapSnapshotInfo&) = 0;
+#if DCHECK_IS_ON()
+ virtual bool Contains(Address) = 0;
+#endif
+ virtual size_t size() = 0;
+ virtual bool IsLargeObjectPage() { return false; }
+
+ Address GetAddress() { return reinterpret_cast<Address>(this); }
+ PageMemory* Storage() const { return storage_; }
+ BaseArena* Arena() const { return arena_; }
+
+ // Returns true if this page has been swept by the ongoing lazy sweep.
+ bool HasBeenSwept() const { return swept_; }
+
+ void MarkAsSwept() {
+ DCHECK(!swept_);
+ swept_ = true;
+ }
+
+ void MarkAsUnswept() {
+ DCHECK(swept_);
+ swept_ = false;
+ }
+
+ // Returns true if magic number is valid.
+ bool IsValid() const;
+
+ virtual void VerifyMarking() = 0;
+
+ private:
+ // Returns a random magic value.
+ uint32_t GetMagic() const { return GetRandomMagic() ^ 0xba5e4a9e; }
+
+ uint32_t const magic_;
+ PageMemory* const storage_;
+ BaseArena* const arena_;
+ BasePage* next_;
+
+ // Track the sweeping state of a page. Set to false at the start of a sweep,
+ // true upon completion of lazy sweeping.
+ bool swept_;
+
+ friend class BaseArena;
+};
+
+// A bitmap for recording object starts. Objects have to be allocated at
+// minimum granularity of kGranularity.
+//
+// Depends on internals such as:
+// - kBlinkPageSize
+// - kAllocationGranularity
+class PLATFORM_EXPORT ObjectStartBitmap {
+ DISALLOW_NEW();
+
+ public:
+ // Granularity of addresses added to the bitmap.
+ static constexpr size_t Granularity() { return kAllocationGranularity; }
+
+ // Maximum number of entries in the bitmap.
+ static constexpr size_t MaxEntries() {
+ return kReservedForBitmap * kCellSize;
+ }
+
+ explicit ObjectStartBitmap(Address offset);
+
+ // Finds an object header based on a
+ // address_maybe_pointing_to_the_middle_of_object. Will search for an object
+ // start in decreasing address order.
+ Address FindHeader(Address address_maybe_pointing_to_the_middle_of_object);
+
+ inline void SetBit(Address);
+ inline void ClearBit(Address);
+ inline bool CheckBit(Address) const;
+
+ // Iterates all object starts recorded in the bitmap.
+ //
+ // The callback is of type
+ // void(Address)
+ // and is passed the object start address as parameter.
+ template <typename Callback>
+ inline void Iterate(Callback) const;
+
+ // Clear the object start bitmap.
+ void Clear();
+
+ private:
+ static const size_t kCellSize = sizeof(uint8_t) * 8;
+ static const size_t kCellMask = sizeof(uint8_t) * 8 - 1;
+ static const size_t kBitmapSize =
+ (kBlinkPageSize + ((kCellSize * kAllocationGranularity) - 1)) /
+ (kCellSize * kAllocationGranularity);
+ static const size_t kReservedForBitmap =
+ ((kBitmapSize + kAllocationMask) & ~kAllocationMask);
+
+ inline void ObjectStartIndexAndBit(Address, size_t*, size_t*) const;
+
+ const Address offset_;
+ // The bitmap contains a bit for every kGranularity aligned address on a
+ // a NormalPage, i.e., for a page of size kBlinkPageSize.
+ uint8_t object_start_bit_map_[kReservedForBitmap];
+};
+
+class PLATFORM_EXPORT NormalPage final : public BasePage {
+ public:
+ NormalPage(PageMemory*, BaseArena*);
+ ~NormalPage();
+
+ Address Payload() { return GetAddress() + PageHeaderSize(); }
+ size_t PayloadSize() {
+ return (BlinkPagePayloadSize() - PageHeaderSize()) & ~kAllocationMask;
+ }
+ Address PayloadEnd() { return Payload() + PayloadSize(); }
+ bool ContainedInObjectPayload(Address address) {
+ return Payload() <= address && address < PayloadEnd();
+ }
+
+ size_t ObjectPayloadSizeForTesting() override;
+ bool IsEmpty() override;
+ void RemoveFromHeap() override;
+ void Sweep() override;
+ void MakeConsistentForMutator() override;
+#if defined(ADDRESS_SANITIZER)
+ void PoisonUnmarkedObjects() override;
+#endif
+
+ void TakeSnapshot(base::trace_event::MemoryAllocatorDump*,
+ ThreadState::GCSnapshotInfo&,
+ HeapSnapshotInfo&) override;
+#if DCHECK_IS_ON()
+ // Returns true for the whole |kBlinkPageSize| page that the page is on, even
+ // for the header, and the unmapped guard page at the start. That ensures the
+ // result can be used to populate the negative page cache.
+ bool Contains(Address) override;
+#endif
+ size_t size() override { return kBlinkPageSize; }
+ static size_t PageHeaderSize() {
+ // Compute the amount of padding we have to add to a header to make the size
+ // of the header plus the padding a multiple of 8 bytes.
+ size_t padding_size =
+ (sizeof(NormalPage) + kAllocationGranularity -
+ (sizeof(HeapObjectHeader) % kAllocationGranularity)) %
+ kAllocationGranularity;
+ return sizeof(NormalPage) + padding_size;
+ }
+
+ inline NormalPageArena* ArenaForNormalPage() const;
+
+ // Context object holding the state of the arena page compaction pass, passed
+ // in when compacting individual pages.
+ class CompactionContext {
+ STACK_ALLOCATED();
+
+ public:
+ // Page compacting into.
+ NormalPage* current_page_ = nullptr;
+ // Offset into |current_page_| to the next free address.
+ size_t allocation_point_ = 0;
+ // Chain of available pages to use for compaction. Page compaction picks the
+ // next one when the current one is exhausted.
+ BasePage* available_pages_ = nullptr;
+ // Chain of pages that have been compacted. Page compaction will add
+ // compacted pages once the current one becomes exhausted.
+ BasePage** compacted_pages_ = nullptr;
+ };
+
+ void SweepAndCompact(CompactionContext&);
+
+ // Object start bitmap of this page.
+ ObjectStartBitmap* object_start_bit_map() { return &object_start_bit_map_; }
+
+ // Verifies that the object start bitmap only contains a bit iff the object
+ // is also reachable through iteration on the page.
+ void VerifyObjectStartBitmapIsConsistentWithPayload();
+
+ // Uses the object_start_bit_map_ to find an object for a given address. The
+ // returned header is either nullptr, indicating that no object could be
+ // found, or it is pointing to valid object or free list entry.
+ HeapObjectHeader* FindHeaderFromAddress(Address);
+
+ void VerifyMarking() override;
+
+ private:
+ ObjectStartBitmap object_start_bit_map_;
+};
+
+// Large allocations are allocated as separate objects and linked in a list.
+//
+// In order to use the same memory allocation routines for everything allocated
+// in the heap, large objects are considered heap pages containing only one
+// object.
+class LargeObjectPage final : public BasePage {
+ public:
+ LargeObjectPage(PageMemory*, BaseArena*, size_t);
+
+ // LargeObjectPage has the following memory layout:
+ //
+ // | metadata | HeapObjectHeader | payload |
+ //
+ // LargeObjectPage::PayloadSize returns the size of HeapObjectHeader and the
+ // object payload. HeapObjectHeader::PayloadSize returns just the size of the
+ // payload.
+ Address Payload() { return GetHeapObjectHeader()->Payload(); }
+ size_t PayloadSize() { return payload_size_; }
+ Address PayloadEnd() { return Payload() + PayloadSize(); }
+ bool ContainedInObjectPayload(Address address) {
+ return Payload() <= address && address < PayloadEnd();
+ }
+
+ size_t ObjectPayloadSizeForTesting() override;
+ bool IsEmpty() override;
+ void RemoveFromHeap() override;
+ void Sweep() override;
+ void MakeConsistentForMutator() override;
+#if defined(ADDRESS_SANITIZER)
+ void PoisonUnmarkedObjects() override;
+#endif
+
+ void TakeSnapshot(base::trace_event::MemoryAllocatorDump*,
+ ThreadState::GCSnapshotInfo&,
+ HeapSnapshotInfo&) override;
+#if DCHECK_IS_ON()
+ // Returns true for any address that is on one of the pages that this large
+ // object uses. That ensures that we can use a negative result to populate the
+ // negative page cache.
+ bool Contains(Address) override;
+#endif
+ virtual size_t size() {
+ return PageHeaderSize() + sizeof(HeapObjectHeader) + payload_size_;
+ }
+ static size_t PageHeaderSize() {
+ // Compute the amount of padding we have to add to a header to make the size
+ // of the header plus the padding a multiple of 8 bytes.
+ size_t padding_size =
+ (sizeof(LargeObjectPage) + kAllocationGranularity -
+ (sizeof(HeapObjectHeader) % kAllocationGranularity)) %
+ kAllocationGranularity;
+ return sizeof(LargeObjectPage) + padding_size;
+ }
+ bool IsLargeObjectPage() override { return true; }
+
+ HeapObjectHeader* GetHeapObjectHeader() {
+ Address header_address = GetAddress() + PageHeaderSize();
+ return reinterpret_cast<HeapObjectHeader*>(header_address);
+ }
+
+#ifdef ANNOTATE_CONTIGUOUS_CONTAINER
+ void SetIsVectorBackingPage() { is_vector_backing_page_ = true; }
+ bool IsVectorBackingPage() const { return is_vector_backing_page_; }
+#endif
+
+ void VerifyMarking() override {}
+
+ private:
+ size_t payload_size_;
+#ifdef ANNOTATE_CONTIGUOUS_CONTAINER
+ bool is_vector_backing_page_;
+#endif
+};
+
+// |HeapDoesNotContainCache| provides a fast way to determine whether an
+// aribtrary pointer-sized word can be interpreted as a pointer to an area that
+// is managed by the garbage collected Blink heap. This is a cache of 'pages'
+// that have previously been determined to be wholly outside of the heap. The
+// size of these pages must be smaller than the allocation alignment of the heap
+// pages. We determine off-heap-ness by rounding down the pointer to the nearest
+// page and looking up the page in the cache. If there is a miss in the cache we
+// can determine the status of the pointer precisely using the heap
+// |RegionTree|.
+//
+// This is a negative cache, so it must be flushed when memory is added to the
+// heap.
+class HeapDoesNotContainCache {
+ USING_FAST_MALLOC(HeapDoesNotContainCache);
+
+ public:
+ HeapDoesNotContainCache() : has_entries_(false) {
+ // Start by flushing the cache in a non-empty state to initialize all the
+ // cache entries.
+ for (size_t i = 0; i < kNumberOfEntries; ++i)
+ entries_[i] = nullptr;
+ }
+
+ void Flush();
+ bool IsEmpty() { return !has_entries_; }
+
+ // Perform a lookup in the cache.
+ //
+ // If lookup returns false, the argument address was not found in the cache
+ // and it is unknown if the address is in the Blink heap.
+ //
+ // If lookup returns true, the argument address was found in the cache which
+ // means the address is not in the heap.
+ PLATFORM_EXPORT bool Lookup(Address);
+
+ // Add an entry to the cache.
+ PLATFORM_EXPORT void AddEntry(Address);
+
+ private:
+ static constexpr size_t kNumberOfEntriesLog2 = 12;
+ static constexpr size_t kNumberOfEntries = 1 << kNumberOfEntriesLog2;
+
+ static size_t GetHash(Address);
+
+ Address entries_[kNumberOfEntries];
+ bool has_entries_;
+};
+
+class FreeList {
+ DISALLOW_NEW();
+
+ public:
+ FreeList();
+
+ void AddToFreeList(Address, size_t);
+ void Clear();
+
+ // Returns a bucket number for inserting a |FreeListEntry| of a given size.
+ // All entries in the given bucket, n, have size >= 2^n.
+ static int BucketIndexForSize(size_t);
+
+ // Returns true if the freelist snapshot is captured.
+ bool TakeSnapshot(const String& dump_base_name);
+
+#if DCHECK_IS_ON() || defined(LEAK_SANITIZER) || defined(ADDRESS_SANITIZER) || \
+ defined(MEMORY_SANITIZER)
+ static void GetAllowedAndForbiddenCounts(Address, size_t, size_t&, size_t&);
+ static void ZapFreedMemory(Address, size_t);
+ static void CheckFreedMemoryIsZapped(Address, size_t);
+#endif
+
+ private:
+ int biggest_free_list_index_;
+
+ // All |FreeListEntry|s in the nth list have size >= 2^n.
+ FreeListEntry* free_lists_[kBlinkPageSizeLog2];
+
+ size_t FreeListSize() const;
+
+ friend class NormalPageArena;
+};
+
+// Each thread has a number of thread arenas (e.g., Generic arenas, typed arenas
+// for |Node|, arenas for collection backings, etc.) and |BaseArena| represents
+// each thread arena.
+//
+// |BaseArena| is a parent class of |NormalPageArena| and |LargeObjectArena|.
+// |NormalPageArena| represents a part of a heap that contains |NormalPage|s,
+// and |LargeObjectArena| represents a part of a heap that contains
+// |LargeObjectPage|s.
+class PLATFORM_EXPORT BaseArena {
+ USING_FAST_MALLOC(BaseArena);
+
+ public:
+ BaseArena(ThreadState*, int);
+ virtual ~BaseArena();
+ void RemoveAllPages();
+
+ void TakeSnapshot(const String& dump_base_name, ThreadState::GCSnapshotInfo&);
+#if DCHECK_IS_ON()
+ BasePage* FindPageFromAddress(Address);
+#endif
+ virtual void TakeFreelistSnapshot(const String& dump_base_name) {}
+ virtual void ClearFreeLists() {}
+ virtual void MakeIterable() {}
+ virtual void MakeConsistentForGC();
+ void MakeConsistentForMutator();
+#if DCHECK_IS_ON()
+ virtual bool IsConsistentForGC() = 0;
+#endif
+ size_t ObjectPayloadSizeForTesting();
+ void PrepareForSweep();
+#if defined(ADDRESS_SANITIZER)
+ void PoisonArena();
+#endif
+ Address LazySweep(size_t, size_t gc_info_index);
+ void SweepUnsweptPage();
+ // Returns true if we have swept all pages within the deadline. Returns false
+ // otherwise.
+ bool LazySweepWithDeadline(double deadline_seconds);
+ void CompleteSweep();
+
+ ThreadState* GetThreadState() { return thread_state_; }
+ int ArenaIndex() const { return index_; }
+
+ Address AllocateLargeObject(size_t allocation_size, size_t gc_info_index);
+
+ bool WillObjectBeLazilySwept(BasePage*, void*) const;
+
+ virtual void VerifyObjectStartBitmap(){};
+ virtual void VerifyMarking(){};
+
+ protected:
+ bool SweepingCompleted() const { return !first_unswept_page_; }
+
+ BasePage* first_page_;
+ BasePage* first_unswept_page_;
+
+ private:
+ virtual Address LazySweepPages(size_t, size_t gc_info_index) = 0;
+
+ ThreadState* thread_state_;
+
+ // Index into the page pools. This is used to ensure that the pages of the
+ // same type go into the correct page pool and thus avoid type confusion.
+ //
+ // TODO(palmer): Should this be size_t?
+ int index_;
+};
+
+class PLATFORM_EXPORT NormalPageArena final : public BaseArena {
+ public:
+ NormalPageArena(ThreadState*, int index);
+ void AddToFreeList(Address address, size_t size) {
+#if DCHECK_IS_ON()
+ DCHECK(FindPageFromAddress(address));
+ // TODO(palmer): Do we need to handle about integer overflow here (and in
+ // similar expressions elsewhere)?
+ DCHECK(FindPageFromAddress(address + size - 1));
+#endif
+ free_list_.AddToFreeList(address, size);
+ }
+ void ClearFreeLists() override;
+ void MakeIterable() override;
+
+#if DCHECK_IS_ON()
+ bool IsConsistentForGC() override;
+ bool PagesToBeSweptContains(Address);
+#endif
+ void TakeFreelistSnapshot(const String& dump_base_name) override;
+
+ Address AllocateObject(size_t allocation_size, size_t gc_info_index);
+
+ void FreePage(NormalPage*);
+
+ void PromptlyFreeObject(HeapObjectHeader*);
+ void PromptlyFreeObjectInFreeList(HeapObjectHeader*, size_t);
+ bool ExpandObject(HeapObjectHeader*, size_t);
+ bool ShrinkObject(HeapObjectHeader*, size_t);
+ size_t promptly_freed_size() const { return promptly_freed_size_; }
+
+ bool IsObjectAllocatedAtAllocationPoint(HeapObjectHeader* header) {
+ return header->PayloadEnd() == current_allocation_point_;
+ }
+
+ bool IsLazySweeping() const { return is_lazy_sweeping_; }
+ void SetIsLazySweeping(bool sweeping) { is_lazy_sweeping_ = sweeping; }
+
+ size_t ArenaSize();
+ size_t FreeListSize();
+
+ void SweepAndCompact();
+
+ void VerifyObjectStartBitmap() override;
+ void VerifyMarking() override;
+
+ Address CurrentAllocationPoint() const { return current_allocation_point_; }
+
+ bool IsInCurrentAllocationPointRegion(Address address) const {
+ return HasCurrentAllocationArea() &&
+ (CurrentAllocationPoint() <= address) &&
+ (address < (CurrentAllocationPoint() + RemainingAllocationSize()));
+ }
+
+ size_t RemainingAllocationSize() const { return remaining_allocation_size_; }
+
+ void MakeConsistentForGC() override;
+
+ private:
+ void AllocatePage();
+
+ Address OutOfLineAllocate(size_t allocation_size, size_t gc_info_index);
+ Address AllocateFromFreeList(size_t, size_t gc_info_index);
+
+ Address LazySweepPages(size_t, size_t gc_info_index) override;
+
+ bool HasCurrentAllocationArea() const {
+ return CurrentAllocationPoint() && RemainingAllocationSize();
+ }
+ void SetAllocationPoint(Address, size_t);
+
+ void SetRemainingAllocationSize(size_t);
+ void UpdateRemainingAllocationSize();
+
+ FreeList free_list_;
+ Address current_allocation_point_;
+ size_t remaining_allocation_size_;
+ size_t last_remaining_allocation_size_;
+
+ // The size of promptly freed objects in the heap. This counter is set to
+ // zero before sweeping when clearing the free list and after coalescing.
+ // It will increase for promptly freed objects on already swept pages.
+ size_t promptly_freed_size_;
+
+ bool is_lazy_sweeping_;
+};
+
+class LargeObjectArena final : public BaseArena {
+ public:
+ LargeObjectArena(ThreadState*, int index);
+ Address AllocateLargeObjectPage(size_t, size_t gc_info_index);
+ void FreeLargeObjectPage(LargeObjectPage*);
+#if DCHECK_IS_ON()
+ bool IsConsistentForGC() override { return true; }
+#endif
+ private:
+ Address DoAllocateLargeObjectPage(size_t, size_t gc_info_index);
+ Address LazySweepPages(size_t, size_t gc_info_index) override;
+};
+
+// Mask an address down to the enclosing Oilpan heap base page. All Oilpan heap
+// pages are aligned at |kBlinkPageBase| plus the size of a guard page. This
+// will work only for 1) a pointer pointing to a non-large object and 2) a
+// pointer pointing to the beginning of a large object.
+//
+// FIXME: Remove PLATFORM_EXPORT once we get a proper public interface to our
+// typed arenas. This is only exported to enable tests in HeapTest.cpp.
+PLATFORM_EXPORT ALWAYS_INLINE BasePage* PageFromObject(const void* object) {
+ Address address = reinterpret_cast<Address>(const_cast<void*>(object));
+ BasePage* page = reinterpret_cast<BasePage*>(BlinkPageAddress(address) +
+ kBlinkGuardPageSize);
+ // Page must have a valid magic.
+ DCHECK(page->IsValid());
+#if DCHECK_IS_ON()
+ DCHECK(page->Contains(address));
+#endif
+ return page;
+}
+
+NO_SANITIZE_ADDRESS inline size_t HeapObjectHeader::size() const {
+ size_t result = encoded_ & kHeaderSizeMask;
+ // Large objects should not refer to header->size(). The actual size of a
+ // large object is stored in |LargeObjectPage::payload_size_|.
+ DCHECK(result != kLargeObjectSizeInHeader);
+ DCHECK(!PageFromObject(this)->IsLargeObjectPage());
+ return result;
+}
+
+NO_SANITIZE_ADDRESS inline bool HeapObjectHeader::IsValid() const {
+#if defined(ARCH_CPU_64_BITS)
+ return GetMagic() == magic_;
+#else
+ return true;
+#endif
+}
+
+NO_SANITIZE_ADDRESS inline bool HeapObjectHeader::IsValidOrZapped() const {
+#if defined(ARCH_CPU_64_BITS)
+ return IsValid() || kZappedMagic == magic_ || kZappedMagicAllowed == magic_ ||
+ kZappedMagicForbidden == magic_;
+#else
+ return true;
+#endif
+}
+
+NO_SANITIZE_ADDRESS inline void HeapObjectHeader::CheckHeader() const {
+#if defined(ARCH_CPU_64_BITS)
+ CHECK(IsValid());
+#endif
+}
+
+inline Address HeapObjectHeader::Payload() {
+ return reinterpret_cast<Address>(this) + sizeof(HeapObjectHeader);
+}
+
+inline Address HeapObjectHeader::PayloadEnd() {
+ return reinterpret_cast<Address>(this) + size();
+}
+
+NO_SANITIZE_ADDRESS inline size_t HeapObjectHeader::PayloadSize() {
+ CheckHeader();
+ size_t size = encoded_ & kHeaderSizeMask;
+ if (UNLIKELY(size == kLargeObjectSizeInHeader)) {
+ DCHECK(PageFromObject(this)->IsLargeObjectPage());
+ return static_cast<LargeObjectPage*>(PageFromObject(this))->PayloadSize() -
+ sizeof(HeapObjectHeader);
+ }
+ DCHECK(!PageFromObject(this)->IsLargeObjectPage());
+ return size - sizeof(HeapObjectHeader);
+}
+
+inline HeapObjectHeader* HeapObjectHeader::FromPayload(const void* payload) {
+ Address addr = reinterpret_cast<Address>(const_cast<void*>(payload));
+ HeapObjectHeader* header =
+ reinterpret_cast<HeapObjectHeader*>(addr - sizeof(HeapObjectHeader));
+ header->CheckHeader();
+ return header;
+}
+
+inline void HeapObjectHeader::CheckFromPayload(const void* payload) {
+ (void)FromPayload(payload);
+}
+
+ALWAYS_INLINE uint32_t RotateLeft16(uint32_t x) {
+#if defined(COMPILER_MSVC)
+ return _lrotr(x, 16);
+#else
+ // http://blog.regehr.org/archives/1063
+ return (x << 16) | (x >> (-16 & 31));
+#endif
+}
+
+inline uint32_t GetRandomMagic() {
+// Ignore C4319: It is OK to 0-extend into the high-order bits of the uintptr_t
+// on 64-bit, in this case.
+#if defined(COMPILER_MSVC)
+#pragma warning(push)
+#pragma warning(disable : 4319)
+#endif
+
+ static const uintptr_t random1 = ~(RotateLeft16(reinterpret_cast<uintptr_t>(
+ base::trace_event::MemoryAllocatorDump::kNameSize)));
+
+#if defined(OS_WIN)
+ static const uintptr_t random2 =
+ ~(RotateLeft16(reinterpret_cast<uintptr_t>(::ReadFile)));
+#elif defined(OS_POSIX)
+ static const uintptr_t random2 =
+ ~(RotateLeft16(reinterpret_cast<uintptr_t>(::read)));
+#else
+#error OS not supported
+#endif
+
+#if defined(ARCH_CPU_64_BITS)
+ static_assert(sizeof(uintptr_t) == sizeof(uint64_t),
+ "uintptr_t is not uint64_t");
+ static const uint32_t random = static_cast<uint32_t>(
+ (random1 & 0x0FFFFULL) | ((random2 >> 32) & 0x0FFFF0000ULL));
+#elif defined(ARCH_CPU_32_BITS)
+ // Although we don't use heap metadata canaries on 32-bit due to memory
+ // pressure, keep this code around just in case we do, someday.
+ static_assert(sizeof(uintptr_t) == sizeof(uint32_t),
+ "uintptr_t is not uint32_t");
+ static const uint32_t random =
+ (random1 & 0x0FFFFUL) | (random2 & 0xFFFF0000UL);
+#else
+#error architecture not supported
+#endif
+
+#if defined(COMPILER_MSVC)
+#pragma warning(pop)
+#endif
+
+ return random;
+}
+
+NO_SANITIZE_ADDRESS inline bool HeapObjectHeader::IsWrapperHeaderMarked()
+ const {
+ CheckHeader();
+ return encoded_ & kHeaderWrapperMarkBitMask;
+}
+
+NO_SANITIZE_ADDRESS inline void HeapObjectHeader::MarkWrapperHeader() {
+ CheckHeader();
+ DCHECK(!IsWrapperHeaderMarked());
+ encoded_ |= kHeaderWrapperMarkBitMask;
+}
+
+NO_SANITIZE_ADDRESS inline void HeapObjectHeader::UnmarkWrapperHeader() {
+ CheckHeader();
+ DCHECK(IsWrapperHeaderMarked());
+ encoded_ &= ~kHeaderWrapperMarkBitMask;
+}
+
+NO_SANITIZE_ADDRESS inline bool HeapObjectHeader::IsMarked() const {
+ CheckHeader();
+ return encoded_ & kHeaderMarkBitMask;
+}
+
+NO_SANITIZE_ADDRESS inline void HeapObjectHeader::Mark() {
+ CheckHeader();
+ DCHECK(!IsMarked());
+ encoded_ = encoded_ | kHeaderMarkBitMask;
+}
+
+NO_SANITIZE_ADDRESS inline void HeapObjectHeader::Unmark() {
+ CheckHeader();
+ DCHECK(IsMarked());
+ encoded_ &= ~kHeaderMarkBitMask;
+}
+
+NO_SANITIZE_ADDRESS inline bool HeapObjectHeader::TryMark() {
+ CheckHeader();
+ if (encoded_ & kHeaderMarkBitMask)
+ return false;
+ encoded_ |= kHeaderMarkBitMask;
+ return true;
+}
+
+NO_SANITIZE_ADDRESS inline bool BasePage::IsValid() const {
+ return GetMagic() == magic_;
+}
+
+inline Address NormalPageArena::AllocateObject(size_t allocation_size,
+ size_t gc_info_index) {
+ if (LIKELY(allocation_size <= remaining_allocation_size_)) {
+ Address header_address = current_allocation_point_;
+ current_allocation_point_ += allocation_size;
+ remaining_allocation_size_ -= allocation_size;
+ DCHECK_GT(gc_info_index, 0u);
+ new (NotNull, header_address) HeapObjectHeader(
+ allocation_size, gc_info_index, HeapObjectHeader::kNormalPage);
+ Address result = header_address + sizeof(HeapObjectHeader);
+ DCHECK(!(reinterpret_cast<uintptr_t>(result) & kAllocationMask));
+
+ SET_MEMORY_ACCESSIBLE(result, allocation_size - sizeof(HeapObjectHeader));
+#if DCHECK_IS_ON()
+ DCHECK(FindPageFromAddress(header_address + allocation_size - 1));
+#endif
+ return result;
+ }
+ return OutOfLineAllocate(allocation_size, gc_info_index);
+}
+
+inline NormalPageArena* NormalPage::ArenaForNormalPage() const {
+ return static_cast<NormalPageArena*>(Arena());
+}
+
+inline void ObjectStartBitmap::SetBit(Address header_address) {
+ size_t cell_index, object_bit;
+ ObjectStartIndexAndBit(header_address, &cell_index, &object_bit);
+ object_start_bit_map_[cell_index] |= (1 << object_bit);
+}
+
+inline void ObjectStartBitmap::ClearBit(Address header_address) {
+ size_t cell_index, object_bit;
+ ObjectStartIndexAndBit(header_address, &cell_index, &object_bit);
+ object_start_bit_map_[cell_index] &= ~(1 << object_bit);
+}
+
+inline bool ObjectStartBitmap::CheckBit(Address header_address) const {
+ size_t cell_index, object_bit;
+ ObjectStartIndexAndBit(header_address, &cell_index, &object_bit);
+ return object_start_bit_map_[cell_index] & (1 << object_bit);
+}
+
+inline void ObjectStartBitmap::ObjectStartIndexAndBit(Address header_address,
+ size_t* cell_index,
+ size_t* bit) const {
+ const size_t object_offset = header_address - offset_;
+ DCHECK(!(object_offset & kAllocationMask));
+ const size_t object_start_number = object_offset / kAllocationGranularity;
+ *cell_index = object_start_number / kCellSize;
+#if DCHECK_IS_ON()
+ const size_t bitmap_size = kBitmapSize;
+ DCHECK_LT(*cell_index, bitmap_size);
+#endif
+ *bit = object_start_number & kCellMask;
+}
+
+template <typename Callback>
+inline void ObjectStartBitmap::Iterate(Callback callback) const {
+ for (size_t cell_index = 0; cell_index < kReservedForBitmap; cell_index++) {
+ if (!object_start_bit_map_[cell_index])
+ continue;
+
+ uint8_t value = object_start_bit_map_[cell_index];
+ while (value) {
+ const int trailing_zeroes = base::bits::CountTrailingZeroBits(value);
+ const size_t object_start_number =
+ (cell_index * kCellSize) + trailing_zeroes;
+ const Address object_address =
+ offset_ + (kAllocationGranularity * object_start_number);
+ callback(object_address);
+ // Clear current object bit in temporary value to advance iteration.
+ value &= ~(1 << (object_start_number & kCellMask));
+ }
+ }
+}
+
+inline HeapObjectHeader::HeapObjectHeader(size_t size,
+ size_t gc_info_index,
+ HeaderLocation header_location) {
+ // sizeof(HeapObjectHeader) must be equal to or smaller than
+ // |kAllocationGranularity|, because |HeapObjectHeader| is used as a header
+ // for a freed entry. Given that the smallest entry size is
+ // |kAllocationGranurarity|, |HeapObjectHeader| must fit into the size.
+ static_assert(
+ sizeof(HeapObjectHeader) <= kAllocationGranularity,
+ "size of HeapObjectHeader must be smaller than kAllocationGranularity");
+#if defined(ARCH_CPU_64_BITS)
+ static_assert(sizeof(HeapObjectHeader) == 8,
+ "sizeof(HeapObjectHeader) must be 8 bytes");
+ magic_ = GetMagic();
+#endif
+
+ DCHECK(gc_info_index < GCInfoTable::kMaxIndex);
+ DCHECK_LT(size, kNonLargeObjectPageSizeMax);
+ DCHECK(!(size & kAllocationMask));
+ encoded_ = static_cast<uint32_t>(
+ (gc_info_index << kHeaderGCInfoIndexShift) | size |
+ (gc_info_index == kGcInfoIndexForFreeListHeader ? kHeaderFreedBitMask
+ : 0));
+
+ if (header_location == kNormalPage) {
+ DCHECK(!PageFromObject(this)->IsLargeObjectPage());
+ static_cast<NormalPage*>(PageFromObject(this))
+ ->object_start_bit_map()
+ ->SetBit(reinterpret_cast<Address>(this));
+ } else {
+ DCHECK(PageFromObject(this)->IsLargeObjectPage());
+ }
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_PAGE_H_
diff --git a/chromium/third_party/blink/renderer/platform/heap/heap_terminated_array.h b/chromium/third_party/blink/renderer/platform/heap/heap_terminated_array.h
new file mode 100644
index 00000000000..1f5772757d5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/heap_terminated_array.h
@@ -0,0 +1,71 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_TERMINATED_ARRAY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_TERMINATED_ARRAY_H_
+
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
+#include "third_party/blink/renderer/platform/wtf/construct_traits.h"
+#include "third_party/blink/renderer/platform/wtf/terminated_array.h"
+#include "third_party/blink/renderer/platform/wtf/terminated_array_builder.h"
+
+namespace blink {
+
+template <typename T>
+class HeapTerminatedArray : public TerminatedArray<T> {
+ DISALLOW_NEW();
+ IS_GARBAGE_COLLECTED_TYPE();
+
+ public:
+ using TerminatedArray<T>::begin;
+ using TerminatedArray<T>::end;
+
+ void Trace(blink::Visitor* visitor) {
+ for (typename TerminatedArray<T>::iterator it = begin(); it != end(); ++it)
+ visitor->Trace(*it);
+ }
+
+ private:
+ // Allocator describes how HeapTerminatedArrayBuilder should create new
+ // instances of HeapTerminatedArray and manage their lifetimes.
+ struct Allocator final {
+ STATIC_ONLY(Allocator);
+ using BackendAllocator = HeapAllocator;
+ using PassPtr = HeapTerminatedArray*;
+ using Ptr = Member<HeapTerminatedArray>;
+
+ static PassPtr Release(Ptr& ptr) { return ptr; }
+
+ static PassPtr Create(size_t capacity) {
+ // No ConstructTraits as there are no real elements in the array after
+ // construction.
+ return reinterpret_cast<HeapTerminatedArray*>(
+ ThreadHeap::Allocate<HeapTerminatedArray>(
+ WTF::Partitions::ComputeAllocationSize(capacity, sizeof(T)),
+ IsEagerlyFinalizedType<T>::value));
+ }
+
+ static PassPtr Resize(PassPtr ptr, size_t capacity) {
+ PassPtr array = reinterpret_cast<HeapTerminatedArray*>(
+ ThreadHeap::Reallocate<HeapTerminatedArray>(
+ ptr,
+ WTF::Partitions::ComputeAllocationSize(capacity, sizeof(T))));
+ WTF::ConstructTraits<T, VectorTraits<T>, HeapAllocator>::
+ NotifyNewElements(reinterpret_cast<T*>(array), array->size());
+ return array;
+ }
+ };
+
+ // Prohibit construction. Allocator makes HeapTerminatedArray instances for
+ // HeapTerminatedArrayBuilder by pointer casting.
+ HeapTerminatedArray() = delete;
+
+ template <typename U, template <typename> class>
+ friend class WTF::TerminatedArrayBuilder;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_TERMINATED_ARRAY_H_
diff --git a/chromium/third_party/blink/renderer/platform/heap/heap_terminated_array_builder.h b/chromium/third_party/blink/renderer/platform/heap/heap_terminated_array_builder.h
new file mode 100644
index 00000000000..38bcf84edbd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/heap_terminated_array_builder.h
@@ -0,0 +1,26 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_TERMINATED_ARRAY_BUILDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_TERMINATED_ARRAY_BUILDER_H_
+
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/heap/heap_terminated_array.h"
+#include "third_party/blink/renderer/platform/wtf/terminated_array_builder.h"
+
+namespace blink {
+
+template <typename T>
+class HeapTerminatedArrayBuilder final
+ : public TerminatedArrayBuilder<T, HeapTerminatedArray> {
+ STACK_ALLOCATED();
+
+ public:
+ explicit HeapTerminatedArrayBuilder(HeapTerminatedArray<T>* array)
+ : TerminatedArrayBuilder<T, HeapTerminatedArray>(array) {}
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_TERMINATED_ARRAY_BUILDER_H_
diff --git a/chromium/third_party/blink/renderer/platform/heap/heap_test.cc b/chromium/third_party/blink/renderer/platform/heap/heap_test.cc
new file mode 100644
index 00000000000..974efd871b6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/heap_test.cc
@@ -0,0 +1,6851 @@
+/*
+ * Copyright (C) 2013 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 <memory>
+#include <utility>
+
+#include "base/location.h"
+#include "base/memory/ptr_util.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/heap/heap_linked_stack.h"
+#include "third_party/blink/renderer/platform/heap/heap_terminated_array_builder.h"
+#include "third_party/blink/renderer/platform/heap/heap_test_utilities.h"
+#include "third_party/blink/renderer/platform/heap/marking_visitor.h"
+#include "third_party/blink/renderer/platform/heap/safe_point.h"
+#include "third_party/blink/renderer/platform/heap/self_keep_alive.h"
+#include "third_party/blink/renderer/platform/heap/stack_frame_depth.h"
+#include "third_party/blink/renderer/platform/heap/thread_state.h"
+#include "third_party/blink/renderer/platform/heap/visitor.h"
+#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
+#include "third_party/blink/renderer/platform/web_task_runner.h"
+#include "third_party/blink/renderer/platform/wtf/hash_traits.h"
+#include "third_party/blink/renderer/platform/wtf/linked_hash_set.h"
+
+namespace blink {
+
+namespace {
+
+class IntWrapper : public GarbageCollectedFinalized<IntWrapper> {
+ public:
+ static IntWrapper* Create(int x) { return new IntWrapper(x); }
+
+ virtual ~IntWrapper() { ++destructor_calls_; }
+
+ static int destructor_calls_;
+ void Trace(blink::Visitor* visitor) {}
+
+ int Value() const { return x_; }
+
+ bool operator==(const IntWrapper& other) const {
+ return other.Value() == Value();
+ }
+
+ unsigned GetHash() { return IntHash<int>::GetHash(x_); }
+
+ IntWrapper(int x) : x_(x) {}
+
+ private:
+ IntWrapper() = delete;
+ int x_;
+};
+
+struct IntWrapperHash {
+ static unsigned GetHash(const IntWrapper& key) {
+ return WTF::HashInt(static_cast<uint32_t>(key.Value()));
+ }
+
+ static bool Equal(const IntWrapper& a, const IntWrapper& b) { return a == b; }
+};
+
+static_assert(WTF::IsTraceable<IntWrapper>::value,
+ "IsTraceable<> template failed to recognize trace method.");
+static_assert(WTF::IsTraceable<HeapVector<IntWrapper>>::value,
+ "HeapVector<IntWrapper> must be traceable.");
+static_assert(WTF::IsTraceable<HeapDeque<IntWrapper>>::value,
+ "HeapDeque<IntWrapper> must be traceable.");
+static_assert(WTF::IsTraceable<HeapHashSet<IntWrapper, IntWrapperHash>>::value,
+ "HeapHashSet<IntWrapper> must be traceable.");
+static_assert(WTF::IsTraceable<HeapHashMap<int, IntWrapper>>::value,
+ "HeapHashMap<int, IntWrapper> must be traceable.");
+
+class KeyWithCopyingMoveConstructor final {
+ public:
+ struct Hash final {
+ STATIC_ONLY(Hash);
+
+ public:
+ static unsigned GetHash(const KeyWithCopyingMoveConstructor& key) {
+ return key.hash_;
+ }
+
+ static bool Equal(const KeyWithCopyingMoveConstructor& x,
+ const KeyWithCopyingMoveConstructor& y) {
+ return x.hash_ == y.hash_;
+ }
+
+ static constexpr bool safe_to_compare_to_empty_or_deleted = true;
+ };
+
+ KeyWithCopyingMoveConstructor() = default;
+ KeyWithCopyingMoveConstructor(WTF::HashTableDeletedValueType) : hash_(-1) {}
+ ~KeyWithCopyingMoveConstructor() = default;
+ KeyWithCopyingMoveConstructor(unsigned hash, const String& string)
+ : hash_(hash), string_(string) {
+ DCHECK_NE(hash_, 0);
+ DCHECK_NE(hash_, -1);
+ }
+ KeyWithCopyingMoveConstructor(const KeyWithCopyingMoveConstructor&) = default;
+ // The move constructor delegates to the copy constructor intentionally.
+ KeyWithCopyingMoveConstructor(KeyWithCopyingMoveConstructor&& x)
+ : KeyWithCopyingMoveConstructor(x) {}
+ KeyWithCopyingMoveConstructor& operator=(
+ const KeyWithCopyingMoveConstructor&) = default;
+ bool operator==(const KeyWithCopyingMoveConstructor& x) const {
+ return hash_ == x.hash_;
+ }
+
+ bool IsHashTableDeletedValue() const { return hash_ == -1; }
+
+ private:
+ int hash_ = 0;
+ String string_;
+};
+
+struct SameSizeAsPersistent {
+ void* pointer_[4];
+};
+
+static_assert(sizeof(Persistent<IntWrapper>) <= sizeof(SameSizeAsPersistent),
+ "Persistent handle should stay small");
+
+class ThreadMarker {
+ public:
+ ThreadMarker()
+ : creating_thread_(reinterpret_cast<ThreadState*>(0)), num_(0) {}
+ ThreadMarker(unsigned i)
+ : creating_thread_(ThreadState::Current()), num_(i) {}
+ ThreadMarker(WTF::HashTableDeletedValueType deleted)
+ : creating_thread_(reinterpret_cast<ThreadState*>(-1)), num_(0) {}
+ ~ThreadMarker() {
+ EXPECT_TRUE((creating_thread_ == ThreadState::Current()) ||
+ (creating_thread_ == reinterpret_cast<ThreadState*>(0)) ||
+ (creating_thread_ == reinterpret_cast<ThreadState*>(-1)));
+ }
+ bool IsHashTableDeletedValue() const {
+ return creating_thread_ == reinterpret_cast<ThreadState*>(-1);
+ }
+ bool operator==(const ThreadMarker& other) const {
+ return other.creating_thread_ == creating_thread_ && other.num_ == num_;
+ }
+ ThreadState* creating_thread_;
+ unsigned num_;
+};
+
+struct ThreadMarkerHash {
+ static unsigned GetHash(const ThreadMarker& key) {
+ return static_cast<unsigned>(
+ reinterpret_cast<uintptr_t>(key.creating_thread_) + key.num_);
+ }
+
+ static bool Equal(const ThreadMarker& a, const ThreadMarker& b) {
+ return a == b;
+ }
+
+ static const bool safe_to_compare_to_empty_or_deleted = false;
+};
+
+typedef std::pair<Member<IntWrapper>, WeakMember<IntWrapper>> StrongWeakPair;
+
+struct PairWithWeakHandling : public StrongWeakPair {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ // Regular constructor.
+ PairWithWeakHandling(IntWrapper* one, IntWrapper* two)
+ : StrongWeakPair(one, two) {
+ DCHECK(one); // We use null first field to indicate empty slots in the hash
+ // table.
+ }
+
+ // The HashTable (via the HashTrait) calls this constructor with a
+ // placement new to mark slots in the hash table as being deleted. We will
+ // never call trace or the destructor on these slots. We mark ourselves
+ // deleted
+ // with a pointer to -1 in the first field.
+ PairWithWeakHandling(WTF::HashTableDeletedValueType)
+ : StrongWeakPair(WTF::kHashTableDeletedValue, nullptr) {}
+
+ // Used by the HashTable (via the HashTrait) to skip deleted slots in the
+ // table. Recognizes objects that were 'constructed' using the above
+ // constructor.
+ bool IsHashTableDeletedValue() const {
+ return first.IsHashTableDeletedValue();
+ }
+
+ bool IsAlive() { return ThreadHeap::IsHeapObjectAlive(second); }
+
+ // Since we don't allocate independent objects of this type, we don't need
+ // a regular trace method. Instead, we use a traceInCollection method. If
+ // the entry should be deleted from the collection we return true and don't
+ // trace the strong pointer.
+ template <typename VisitorDispatcher>
+ bool TraceInCollection(VisitorDispatcher visitor,
+ WTF::WeakHandlingFlag weakness) {
+ HashTraits<WeakMember<IntWrapper>>::TraceInCollection(visitor, second,
+ weakness);
+ if (!ThreadHeap::IsHeapObjectAlive(second))
+ return true;
+
+ visitor->Trace(first);
+ return false;
+ }
+
+ // Incremental marking requires that these objects have a regular tracing
+ // method that is used for eagerly tracing through them in case they are
+ // in-place constructed in a container. In this case, we only care about
+ // strong fields.
+ void Trace(blink::Visitor* visitor) { visitor->Trace(first); }
+};
+
+template <typename T>
+struct WeakHandlingHashTraits : WTF::SimpleClassHashTraits<T> {
+ // We want to treat the object as a weak object in the sense that it can
+ // disappear from hash sets and hash maps.
+ static const WTF::WeakHandlingFlag kWeakHandlingFlag = WTF::kWeakHandling;
+ // Normally whether or not an object needs tracing is inferred
+ // automatically from the presence of the trace method, but we don't
+ // necessarily have a trace method, and we may not need one because T
+ // can perhaps only be allocated inside collections, never as independent
+ // objects. Explicitly mark this as needing tracing and it will be traced
+ // in collections using the traceInCollection method, which it must have.
+ template <typename U = void>
+ struct IsTraceableInCollection {
+ static const bool value = true;
+ };
+ // The traceInCollection method traces differently depending on whether we
+ // are strongifying the trace operation. We strongify the trace operation
+ // when there are active iterators on the object. In this case all
+ // WeakMembers are marked like strong members so that elements do not
+ // suddenly disappear during iteration. Returns true if weak pointers to
+ // dead objects were found: In this case any strong pointers were not yet
+ // traced and the entry should be removed from the collection.
+ template <typename VisitorDispatcher>
+ static bool TraceInCollection(VisitorDispatcher visitor,
+ T& t,
+ WTF::WeakHandlingFlag weakness) {
+ return t.TraceInCollection(visitor, weakness);
+ }
+
+ static bool IsAlive(T& t) { return t.IsAlive(); }
+};
+
+} // namespace
+
+} // namespace blink
+
+namespace WTF {
+
+template <typename T>
+struct DefaultHash;
+template <>
+struct DefaultHash<blink::ThreadMarker> {
+ typedef blink::ThreadMarkerHash Hash;
+};
+
+// ThreadMarkerHash is the default hash for ThreadMarker
+template <>
+struct HashTraits<blink::ThreadMarker>
+ : GenericHashTraits<blink::ThreadMarker> {
+ static const bool kEmptyValueIsZero = true;
+ static void ConstructDeletedValue(blink::ThreadMarker& slot, bool) {
+ new (NotNull, &slot) blink::ThreadMarker(kHashTableDeletedValue);
+ }
+ static bool IsDeletedValue(const blink::ThreadMarker& slot) {
+ return slot.IsHashTableDeletedValue();
+ }
+};
+
+// The hash algorithm for our custom pair class is just the standard double
+// hash for pairs. Note that this means you can't mutate either of the parts of
+// the pair while they are in the hash table, as that would change their hash
+// code and thus their preferred placement in the table.
+template <>
+struct DefaultHash<blink::PairWithWeakHandling> {
+ typedef PairHash<blink::Member<blink::IntWrapper>,
+ blink::WeakMember<blink::IntWrapper>>
+ Hash;
+};
+
+// Custom traits for the pair. These are weakness handling traits, which means
+// PairWithWeakHandling must implement the traceInCollection method.
+// In addition, these traits are concerned with the two magic values for the
+// object, that represent empty and deleted slots in the hash table. The
+// SimpleClassHashTraits allow empty slots in the table to be initialzed with
+// memset to zero, and we use -1 in the first part of the pair to represent
+// deleted slots.
+template <>
+struct HashTraits<blink::PairWithWeakHandling>
+ : blink::WeakHandlingHashTraits<blink::PairWithWeakHandling> {
+ static const bool kHasIsEmptyValueFunction = true;
+ static bool IsEmptyValue(const blink::PairWithWeakHandling& value) {
+ return !value.first;
+ }
+ static void ConstructDeletedValue(blink::PairWithWeakHandling& slot, bool) {
+ new (NotNull, &slot) blink::PairWithWeakHandling(kHashTableDeletedValue);
+ }
+ static bool IsDeletedValue(const blink::PairWithWeakHandling& value) {
+ return value.IsHashTableDeletedValue();
+ }
+};
+
+template <>
+struct IsTraceable<blink::PairWithWeakHandling> {
+ static const bool value = IsTraceable<blink::StrongWeakPair>::value;
+};
+
+template <>
+struct DefaultHash<blink::KeyWithCopyingMoveConstructor> {
+ using Hash = blink::KeyWithCopyingMoveConstructor::Hash;
+};
+
+template <>
+struct HashTraits<blink::KeyWithCopyingMoveConstructor>
+ : public SimpleClassHashTraits<blink::KeyWithCopyingMoveConstructor> {};
+
+} // namespace WTF
+
+namespace blink {
+
+class TestGCCollectGarbageScope {
+ public:
+ explicit TestGCCollectGarbageScope(BlinkGC::StackState state) {
+ DCHECK(ThreadState::Current()->CheckThread());
+ }
+
+ ~TestGCCollectGarbageScope() { ThreadState::Current()->CompleteSweep(); }
+};
+
+class TestGCMarkingScope : public TestGCCollectGarbageScope {
+ public:
+ explicit TestGCMarkingScope(BlinkGC::StackState state)
+ : TestGCCollectGarbageScope(state),
+ atomic_pause_scope_(ThreadState::Current()),
+ persistent_lock_(ProcessHeap::CrossThreadPersistentMutex()) {
+ ThreadState::Current()->MarkPhasePrologue(state, BlinkGC::kAtomicMarking,
+ BlinkGC::kPreciseGC);
+ }
+ ~TestGCMarkingScope() {
+ ThreadState::Current()->MarkPhaseEpilogue(BlinkGC::kAtomicMarking);
+ ThreadState::Current()->PreSweep(BlinkGC::kAtomicMarking,
+ BlinkGC::kEagerSweeping);
+ }
+
+ private:
+ ThreadState::AtomicPauseScope atomic_pause_scope_;
+ RecursiveMutexLocker persistent_lock_;
+};
+
+class TestGCScope : public TestGCMarkingScope {
+ public:
+ explicit TestGCScope(BlinkGC::StackState state)
+ : TestGCMarkingScope(state), safe_point_scope_(state) {}
+ ~TestGCScope() {}
+
+ private:
+ SafePointScope safe_point_scope_;
+};
+
+class SimpleObject : public GarbageCollected<SimpleObject> {
+ public:
+ static SimpleObject* Create() { return new SimpleObject(); }
+ void Trace(blink::Visitor* visitor) {}
+ char GetPayload(int i) { return payload[i]; }
+ // This virtual method is unused but it is here to make sure
+ // that this object has a vtable. This object is used
+ // as the super class for objects that also have garbage
+ // collected mixins and having a virtual here makes sure
+ // that adjustment is needed both for marking and for isAlive
+ // checks.
+ virtual void VirtualMethod() {}
+
+ protected:
+ SimpleObject() = default;
+ char payload[64];
+};
+
+class HeapTestSuperClass
+ : public GarbageCollectedFinalized<HeapTestSuperClass> {
+ public:
+ static HeapTestSuperClass* Create() { return new HeapTestSuperClass(); }
+
+ virtual ~HeapTestSuperClass() { ++destructor_calls_; }
+
+ static int destructor_calls_;
+ void Trace(blink::Visitor* visitor) {}
+
+ protected:
+ HeapTestSuperClass() = default;
+};
+
+int HeapTestSuperClass::destructor_calls_ = 0;
+
+class HeapTestOtherSuperClass {
+ public:
+ int payload;
+};
+
+static const size_t kClassMagic = 0xABCDDBCA;
+
+class HeapTestSubClass : public HeapTestOtherSuperClass,
+ public HeapTestSuperClass {
+ public:
+ static HeapTestSubClass* Create() { return new HeapTestSubClass(); }
+
+ ~HeapTestSubClass() override {
+ EXPECT_EQ(kClassMagic, magic_);
+ ++destructor_calls_;
+ }
+
+ static int destructor_calls_;
+
+ private:
+ HeapTestSubClass() : magic_(kClassMagic) {}
+
+ const size_t magic_;
+};
+
+int HeapTestSubClass::destructor_calls_ = 0;
+
+class HeapAllocatedArray : public GarbageCollected<HeapAllocatedArray> {
+ public:
+ HeapAllocatedArray() {
+ for (int i = 0; i < kArraySize; ++i) {
+ array_[i] = i % 128;
+ }
+ }
+
+ int8_t at(size_t i) { return array_[i]; }
+ void Trace(blink::Visitor* visitor) {}
+
+ private:
+ static const int kArraySize = 1000;
+ int8_t array_[kArraySize];
+};
+
+class OffHeapInt : public RefCounted<OffHeapInt> {
+ public:
+ static scoped_refptr<OffHeapInt> Create(int x) {
+ return base::AdoptRef(new OffHeapInt(x));
+ }
+
+ virtual ~OffHeapInt() { ++destructor_calls_; }
+
+ static int destructor_calls_;
+
+ int Value() const { return x_; }
+
+ bool operator==(const OffHeapInt& other) const {
+ return other.Value() == Value();
+ }
+
+ unsigned GetHash() { return IntHash<int>::GetHash(x_); }
+ void VoidFunction() {}
+
+ protected:
+ OffHeapInt(int x) : x_(x) {}
+
+ private:
+ OffHeapInt() = delete;
+ int x_;
+};
+
+int IntWrapper::destructor_calls_ = 0;
+int OffHeapInt::destructor_calls_ = 0;
+
+class ThreadedTesterBase {
+ protected:
+ static void Test(ThreadedTesterBase* tester) {
+ Vector<std::unique_ptr<WebThread>, kNumberOfThreads> threads;
+ for (int i = 0; i < kNumberOfThreads; i++) {
+ threads.push_back(Platform::Current()->CreateThread(
+ WebThreadCreationParams(WebThreadType::kTestThread)
+ .SetThreadNameForTest("blink gc testing thread")));
+ PostCrossThreadTask(
+ *threads.back()->GetTaskRunner(), FROM_HERE,
+ CrossThreadBind(ThreadFunc, CrossThreadUnretained(tester)));
+ }
+ while (tester->threads_to_finish_) {
+ test::YieldCurrentThread();
+ }
+ delete tester;
+ }
+
+ virtual void RunThread() = 0;
+
+ protected:
+ static const int kNumberOfThreads = 10;
+ static const int kGcPerThread = 5;
+ static const int kNumberOfAllocations = 50;
+
+ ThreadedTesterBase() : gc_count_(0), threads_to_finish_(kNumberOfThreads) {}
+
+ virtual ~ThreadedTesterBase() = default;
+
+ inline bool Done() const {
+ return gc_count_ >= kNumberOfThreads * kGcPerThread;
+ }
+
+ volatile int gc_count_;
+ volatile int threads_to_finish_;
+
+ private:
+ static void ThreadFunc(void* data) {
+ reinterpret_cast<ThreadedTesterBase*>(data)->RunThread();
+ }
+};
+
+// Needed to give this variable a definition (the initializer above is only a
+// declaration), so that subclasses can use it.
+const int ThreadedTesterBase::kNumberOfThreads;
+
+class ThreadedHeapTester : public ThreadedTesterBase {
+ public:
+ static void Test() { ThreadedTesterBase::Test(new ThreadedHeapTester); }
+
+ ~ThreadedHeapTester() override {
+ // Verify that the threads cleared their CTPs when
+ // terminating, preventing access to a finalized heap.
+ for (auto& global_int_wrapper : cross_persistents_) {
+ DCHECK(global_int_wrapper.get());
+ EXPECT_FALSE(global_int_wrapper.get()->Get());
+ }
+ }
+
+ protected:
+ using GlobalIntWrapperPersistent = CrossThreadPersistent<IntWrapper>;
+
+ Mutex mutex_;
+ Vector<std::unique_ptr<GlobalIntWrapperPersistent>> cross_persistents_;
+
+ std::unique_ptr<GlobalIntWrapperPersistent> CreateGlobalPersistent(
+ int value) {
+ return std::make_unique<GlobalIntWrapperPersistent>(
+ IntWrapper::Create(value));
+ }
+
+ void AddGlobalPersistent() {
+ MutexLocker lock(mutex_);
+ cross_persistents_.push_back(CreateGlobalPersistent(0x2a2a2a2a));
+ }
+
+ void RunThread() override {
+ ThreadState::AttachCurrentThread();
+
+ // Add a cross-thread persistent from this thread; the test object
+ // verifies that it will have been cleared out after the threads
+ // have all detached, running their termination GCs while doing so.
+ AddGlobalPersistent();
+
+ int gc_count = 0;
+ while (!Done()) {
+ {
+ Persistent<IntWrapper> wrapper;
+
+ std::unique_ptr<GlobalIntWrapperPersistent> global_persistent =
+ CreateGlobalPersistent(0x0ed0cabb);
+
+ for (int i = 0; i < kNumberOfAllocations; i++) {
+ wrapper = IntWrapper::Create(0x0bbac0de);
+ if (!(i % 10)) {
+ global_persistent = CreateGlobalPersistent(0x0ed0cabb);
+ }
+ test::YieldCurrentThread();
+ }
+
+ if (gc_count < kGcPerThread) {
+ PreciselyCollectGarbage();
+ gc_count++;
+ AtomicIncrement(&gc_count_);
+ }
+
+ // Taking snapshot shouldn't have any bad side effect.
+ // TODO(haraken): This snapshot GC causes crashes, so disable
+ // it at the moment. Fix the crash and enable it.
+ // ThreadHeap::collectGarbage(BlinkGC::NoHeapPointersOnStack,
+ // BlinkGC::TakeSnapshot, BlinkGC::ForcedGC);
+ PreciselyCollectGarbage();
+ EXPECT_EQ(wrapper->Value(), 0x0bbac0de);
+ EXPECT_EQ((*global_persistent)->Value(), 0x0ed0cabb);
+ }
+ test::YieldCurrentThread();
+ }
+
+ ThreadState::DetachCurrentThread();
+ AtomicDecrement(&threads_to_finish_);
+ }
+};
+
+class ThreadedWeaknessTester : public ThreadedTesterBase {
+ public:
+ static void Test() { ThreadedTesterBase::Test(new ThreadedWeaknessTester); }
+
+ private:
+ void RunThread() override {
+ ThreadState::AttachCurrentThread();
+
+ int gc_count = 0;
+ while (!Done()) {
+ {
+ Persistent<HeapHashMap<ThreadMarker, WeakMember<IntWrapper>>> weak_map =
+ new HeapHashMap<ThreadMarker, WeakMember<IntWrapper>>;
+ PersistentHeapHashMap<ThreadMarker, WeakMember<IntWrapper>> weak_map2;
+
+ for (int i = 0; i < kNumberOfAllocations; i++) {
+ weak_map->insert(static_cast<unsigned>(i), IntWrapper::Create(0));
+ weak_map2.insert(static_cast<unsigned>(i), IntWrapper::Create(0));
+ test::YieldCurrentThread();
+ }
+
+ if (gc_count < kGcPerThread) {
+ PreciselyCollectGarbage();
+ gc_count++;
+ AtomicIncrement(&gc_count_);
+ }
+
+ // Taking snapshot shouldn't have any bad side effect.
+ // TODO(haraken): This snapshot GC causes crashes, so disable
+ // it at the moment. Fix the crash and enable it.
+ // ThreadHeap::collectGarbage(BlinkGC::NoHeapPointersOnStack,
+ // BlinkGC::TakeSnapshot, BlinkGC::ForcedGC);
+ PreciselyCollectGarbage();
+ EXPECT_TRUE(weak_map->IsEmpty());
+ EXPECT_TRUE(weak_map2.IsEmpty());
+ }
+ test::YieldCurrentThread();
+ }
+ ThreadState::DetachCurrentThread();
+ AtomicDecrement(&threads_to_finish_);
+ }
+};
+
+class ThreadPersistentHeapTester : public ThreadedTesterBase {
+ public:
+ static void Test() {
+ ThreadedTesterBase::Test(new ThreadPersistentHeapTester);
+ }
+
+ protected:
+ class Local final : public GarbageCollected<Local> {
+ public:
+ Local() = default;
+
+ void Trace(blink::Visitor* visitor) {}
+ };
+
+ class PersistentChain;
+
+ class RefCountedChain : public RefCounted<RefCountedChain> {
+ public:
+ static RefCountedChain* Create(int count) {
+ return new RefCountedChain(count);
+ }
+
+ private:
+ explicit RefCountedChain(int count) {
+ if (count > 0) {
+ --count;
+ persistent_chain_ = PersistentChain::Create(count);
+ }
+ }
+
+ Persistent<PersistentChain> persistent_chain_;
+ };
+
+ class PersistentChain : public GarbageCollectedFinalized<PersistentChain> {
+ public:
+ static PersistentChain* Create(int count) {
+ return new PersistentChain(count);
+ }
+
+ void Trace(blink::Visitor* visitor) {}
+
+ private:
+ explicit PersistentChain(int count) {
+ ref_counted_chain_ = base::AdoptRef(RefCountedChain::Create(count));
+ }
+
+ scoped_refptr<RefCountedChain> ref_counted_chain_;
+ };
+
+ void RunThread() override {
+ ThreadState::AttachCurrentThread();
+
+ PersistentChain::Create(100);
+
+ // Upon thread detach, GCs will run until all persistents have been
+ // released. We verify that the draining of persistents proceeds
+ // as expected by dropping one Persistent<> per GC until there
+ // are none left.
+ ThreadState::DetachCurrentThread();
+ AtomicDecrement(&threads_to_finish_);
+ }
+};
+
+// The accounting for memory includes the memory used by rounding up object
+// sizes. This is done in a different way on 32 bit and 64 bit, so we have to
+// have some slack in the tests.
+template <typename T>
+void CheckWithSlack(T expected, T actual, int slack) {
+ EXPECT_LE(expected, actual);
+ EXPECT_GE((intptr_t)expected + slack, (intptr_t)actual);
+}
+
+class TraceCounter : public GarbageCollectedFinalized<TraceCounter> {
+ public:
+ static TraceCounter* Create() { return new TraceCounter(); }
+
+ void Trace(blink::Visitor* visitor) { trace_count_++; }
+ int TraceCount() const { return trace_count_; }
+
+ private:
+ TraceCounter() : trace_count_(0) {}
+
+ int trace_count_;
+};
+
+TEST(HeapTest, IsHeapObjectAliveForConstPointer) {
+ // See http://crbug.com/661363.
+ SimpleObject* object = SimpleObject::Create();
+ HeapObjectHeader* header = HeapObjectHeader::FromPayload(object);
+ header->Mark();
+ EXPECT_TRUE(ThreadHeap::IsHeapObjectAlive(object));
+ const SimpleObject* const_object = const_cast<const SimpleObject*>(object);
+ EXPECT_TRUE(ThreadHeap::IsHeapObjectAlive(const_object));
+}
+
+class ClassWithMember : public GarbageCollected<ClassWithMember> {
+ public:
+ static ClassWithMember* Create() { return new ClassWithMember(); }
+
+ void Trace(blink::Visitor* visitor) {
+ visitor->Trace(trace_counter_);
+ }
+ int TraceCount() const { return trace_counter_->TraceCount(); }
+
+ private:
+ ClassWithMember() : trace_counter_(TraceCounter::Create()) {}
+
+ Member<TraceCounter> trace_counter_;
+};
+
+class SimpleFinalizedObject
+ : public GarbageCollectedFinalized<SimpleFinalizedObject> {
+ public:
+ static SimpleFinalizedObject* Create() { return new SimpleFinalizedObject(); }
+
+ ~SimpleFinalizedObject() { ++destructor_calls_; }
+
+ static int destructor_calls_;
+
+ void Trace(blink::Visitor* visitor) {}
+
+ private:
+ SimpleFinalizedObject() = default;
+};
+
+int SimpleFinalizedObject::destructor_calls_ = 0;
+
+class IntNode : public GarbageCollected<IntNode> {
+ public:
+ // IntNode is used to test typed heap allocation. Instead of
+ // redefining blink::Node to our test version, we keep it separate
+ // so as to avoid possible warnings about linker duplicates.
+ // Override operator new to allocate IntNode subtype objects onto
+ // the dedicated heap for blink::Node.
+ //
+ // TODO(haraken): untangling the heap unit tests from Blink would
+ // simplify and avoid running into this problem - http://crbug.com/425381
+ GC_PLUGIN_IGNORE("crbug.com/443854")
+ void* operator new(size_t size) {
+ ThreadState* state = ThreadState::Current();
+ const char* type_name = WTF_HEAP_PROFILER_TYPE_NAME(IntNode);
+ return state->Heap().AllocateOnArenaIndex(
+ state, size, BlinkGC::kNodeArenaIndex, GCInfoTrait<IntNode>::Index(),
+ type_name);
+ }
+
+ static IntNode* Create(int i) { return new IntNode(i); }
+
+ void Trace(blink::Visitor* visitor) {}
+
+ int Value() { return value_; }
+
+ private:
+ IntNode(int i) : value_(i) {}
+ int value_;
+};
+
+class Bar : public GarbageCollectedFinalized<Bar> {
+ public:
+ static Bar* Create() { return new Bar(); }
+
+ void FinalizeGarbageCollectedObject() {
+ EXPECT_TRUE(magic_ == kMagic);
+ magic_ = 0;
+ live_--;
+ }
+ bool HasBeenFinalized() const { return !magic_; }
+
+ virtual void Trace(blink::Visitor* visitor) {}
+ static unsigned live_;
+
+ protected:
+ static const int kMagic = 1337;
+ int magic_;
+
+ Bar() : magic_(kMagic) { live_++; }
+};
+
+WILL_NOT_BE_EAGERLY_TRACED_CLASS(Bar);
+
+unsigned Bar::live_ = 0;
+
+class Baz : public GarbageCollected<Baz> {
+ public:
+ static Baz* Create(Bar* bar) { return new Baz(bar); }
+
+ void Trace(blink::Visitor* visitor) { visitor->Trace(bar_); }
+
+ void Clear() { bar_.Release(); }
+
+ // willFinalize is called by FinalizationObserver.
+ void WillFinalize() { EXPECT_TRUE(!bar_->HasBeenFinalized()); }
+
+ private:
+ explicit Baz(Bar* bar) : bar_(bar) {}
+
+ Member<Bar> bar_;
+};
+
+class Foo : public Bar {
+ public:
+ static Foo* Create(Bar* bar) { return new Foo(bar); }
+
+ static Foo* Create(Foo* foo) { return new Foo(foo); }
+
+ virtual void Trace(blink::Visitor* visitor) {
+ if (points_to_foo_)
+ visitor->Trace(static_cast<Foo*>(bar_));
+ else
+ visitor->Trace(bar_);
+ }
+
+ private:
+ Foo(Bar* bar) : Bar(), bar_(bar), points_to_foo_(false) {}
+
+ Foo(Foo* foo) : Bar(), bar_(foo), points_to_foo_(true) {}
+
+ Bar* bar_;
+ bool points_to_foo_;
+};
+
+WILL_NOT_BE_EAGERLY_TRACED_CLASS(Foo);
+
+class Bars : public Bar {
+ public:
+ static Bars* Create() { return new Bars(); }
+
+ virtual void Trace(blink::Visitor* visitor) {
+ for (unsigned i = 0; i < width_; i++)
+ visitor->Trace(bars_[i]);
+ }
+
+ unsigned GetWidth() const { return width_; }
+
+ static const unsigned kWidth = 7500;
+
+ private:
+ Bars() : width_(0) {
+ for (unsigned i = 0; i < kWidth; i++) {
+ bars_[i] = Bar::Create();
+ width_++;
+ }
+ }
+
+ unsigned width_;
+ Member<Bar> bars_[kWidth];
+};
+
+WILL_NOT_BE_EAGERLY_TRACED_CLASS(Bars);
+
+class ConstructorAllocation : public GarbageCollected<ConstructorAllocation> {
+ public:
+ static ConstructorAllocation* Create() { return new ConstructorAllocation(); }
+
+ void Trace(blink::Visitor* visitor) { visitor->Trace(int_wrapper_); }
+
+ private:
+ ConstructorAllocation() { int_wrapper_ = IntWrapper::Create(42); }
+
+ Member<IntWrapper> int_wrapper_;
+};
+
+class LargeHeapObject : public GarbageCollectedFinalized<LargeHeapObject> {
+ public:
+ ~LargeHeapObject() { destructor_calls_++; }
+ static LargeHeapObject* Create() { return new LargeHeapObject(); }
+ char Get(size_t i) { return data_[i]; }
+ void Set(size_t i, char c) { data_[i] = c; }
+ size_t length() { return kLength; }
+ void Trace(blink::Visitor* visitor) { visitor->Trace(int_wrapper_); }
+ static int destructor_calls_;
+
+ private:
+ static const size_t kLength = 1024 * 1024;
+ LargeHeapObject() { int_wrapper_ = IntWrapper::Create(23); }
+ Member<IntWrapper> int_wrapper_;
+ char data_[kLength];
+};
+
+int LargeHeapObject::destructor_calls_ = 0;
+
+// This test class served a more important role while Blink
+// was transitioned over to using Oilpan. That required classes
+// that were hybrid, both ref-counted and on the Oilpan heap
+// (the RefCountedGarbageCollected<> class providing just that.)
+//
+// There's no current need for having a ref-counted veneer on
+// top of a GCed class, but we preserve it here to exercise the
+// implementation technique that it used -- keeping an internal
+// "keep alive" persistent reference that is set & cleared across
+// ref-counting operations.
+//
+class RefCountedAndGarbageCollected
+ : public GarbageCollectedFinalized<RefCountedAndGarbageCollected> {
+ public:
+ static RefCountedAndGarbageCollected* Create() {
+ return new RefCountedAndGarbageCollected;
+ }
+
+ ~RefCountedAndGarbageCollected() { ++destructor_calls_; }
+
+ void AddRef() {
+ if (UNLIKELY(!ref_count_)) {
+#if DCHECK_IS_ON()
+ DCHECK(ThreadState::Current()->Heap().FindPageFromAddress(
+ reinterpret_cast<Address>(this)));
+#endif
+ keep_alive_ = this;
+ }
+ ++ref_count_;
+ }
+
+ void Release() {
+ DCHECK_GT(ref_count_, 0);
+ if (!--ref_count_)
+ keep_alive_.Clear();
+ }
+
+ void Trace(blink::Visitor* visitor) {}
+
+ static int destructor_calls_;
+
+ private:
+ RefCountedAndGarbageCollected() : ref_count_(0) {}
+
+ int ref_count_;
+ SelfKeepAlive<RefCountedAndGarbageCollected> keep_alive_;
+};
+
+int RefCountedAndGarbageCollected::destructor_calls_ = 0;
+
+class RefCountedAndGarbageCollected2
+ : public HeapTestOtherSuperClass,
+ public GarbageCollectedFinalized<RefCountedAndGarbageCollected2> {
+ public:
+ static RefCountedAndGarbageCollected2* Create() {
+ return new RefCountedAndGarbageCollected2;
+ }
+
+ ~RefCountedAndGarbageCollected2() { ++destructor_calls_; }
+
+ void Ref() {
+ if (UNLIKELY(!ref_count_)) {
+#if DCHECK_IS_ON()
+ DCHECK(ThreadState::Current()->Heap().FindPageFromAddress(
+ reinterpret_cast<Address>(this)));
+#endif
+ keep_alive_ = this;
+ }
+ ++ref_count_;
+ }
+
+ void Deref() {
+ DCHECK_GT(ref_count_, 0);
+ if (!--ref_count_)
+ keep_alive_.Clear();
+ }
+
+ void Trace(blink::Visitor* visitor) {}
+
+ static int destructor_calls_;
+
+ private:
+ RefCountedAndGarbageCollected2() : ref_count_(0) {}
+
+ int ref_count_;
+ SelfKeepAlive<RefCountedAndGarbageCollected2> keep_alive_;
+};
+
+int RefCountedAndGarbageCollected2::destructor_calls_ = 0;
+
+class Weak : public Bar {
+ public:
+ static Weak* Create(Bar* strong, Bar* weak) { return new Weak(strong, weak); }
+
+ virtual void Trace(blink::Visitor* visitor) {
+ visitor->Trace(strong_bar_);
+ visitor->template RegisterWeakMembers<Weak, &Weak::ZapWeakMembers>(this);
+ }
+
+ void ZapWeakMembers(Visitor* visitor) {
+ if (!ThreadHeap::IsHeapObjectAlive(weak_bar_))
+ weak_bar_ = nullptr;
+ }
+
+ bool StrongIsThere() { return !!strong_bar_; }
+ bool WeakIsThere() { return !!weak_bar_; }
+
+ private:
+ Weak(Bar* strong_bar, Bar* weak_bar)
+ : Bar(), strong_bar_(strong_bar), weak_bar_(weak_bar) {}
+
+ Member<Bar> strong_bar_;
+ Bar* weak_bar_;
+};
+
+WILL_NOT_BE_EAGERLY_TRACED_CLASS(Weak);
+
+class WithWeakMember : public Bar {
+ public:
+ static WithWeakMember* Create(Bar* strong, Bar* weak) {
+ return new WithWeakMember(strong, weak);
+ }
+
+ virtual void Trace(blink::Visitor* visitor) {
+ visitor->Trace(strong_bar_);
+ visitor->Trace(weak_bar_);
+ }
+
+ bool StrongIsThere() { return !!strong_bar_; }
+ bool WeakIsThere() { return !!weak_bar_; }
+
+ private:
+ WithWeakMember(Bar* strong_bar, Bar* weak_bar)
+ : Bar(), strong_bar_(strong_bar), weak_bar_(weak_bar) {}
+
+ Member<Bar> strong_bar_;
+ WeakMember<Bar> weak_bar_;
+};
+
+WILL_NOT_BE_EAGERLY_TRACED_CLASS(WithWeakMember);
+
+class Observable : public GarbageCollectedFinalized<Observable> {
+ USING_PRE_FINALIZER(Observable, WillFinalize);
+
+ public:
+ static Observable* Create(Bar* bar) { return new Observable(bar); }
+ ~Observable() { was_destructed_ = true; }
+ void Trace(blink::Visitor* visitor) { visitor->Trace(bar_); }
+
+ // willFinalize is called by FinalizationObserver. willFinalize can touch
+ // other on-heap objects.
+ void WillFinalize() {
+ EXPECT_FALSE(was_destructed_);
+ EXPECT_FALSE(bar_->HasBeenFinalized());
+ will_finalize_was_called_ = true;
+ }
+ static bool will_finalize_was_called_;
+
+ private:
+ explicit Observable(Bar* bar) : bar_(bar), was_destructed_(false) {}
+
+ Member<Bar> bar_;
+ bool was_destructed_;
+};
+
+bool Observable::will_finalize_was_called_ = false;
+
+class ObservableWithPreFinalizer
+ : public GarbageCollectedFinalized<ObservableWithPreFinalizer> {
+ USING_PRE_FINALIZER(ObservableWithPreFinalizer, Dispose);
+
+ public:
+ static ObservableWithPreFinalizer* Create() {
+ return new ObservableWithPreFinalizer();
+ }
+ ~ObservableWithPreFinalizer() { was_destructed_ = true; }
+ void Trace(blink::Visitor* visitor) {}
+ void Dispose() {
+ EXPECT_FALSE(was_destructed_);
+ dispose_was_called_ = true;
+ }
+ static bool dispose_was_called_;
+
+ protected:
+ ObservableWithPreFinalizer() : was_destructed_(false) {}
+
+ bool was_destructed_;
+};
+
+bool ObservableWithPreFinalizer::dispose_was_called_ = false;
+
+bool g_dispose_was_called_for_pre_finalizer_base = false;
+bool g_dispose_was_called_for_pre_finalizer_mixin = false;
+bool g_dispose_was_called_for_pre_finalizer_sub_class = false;
+
+class PreFinalizerBase : public GarbageCollectedFinalized<PreFinalizerBase> {
+ USING_PRE_FINALIZER(PreFinalizerBase, Dispose);
+
+ public:
+ static PreFinalizerBase* Create() { return new PreFinalizerBase(); }
+ virtual ~PreFinalizerBase() { was_destructed_ = true; }
+ virtual void Trace(blink::Visitor* visitor) {}
+ void Dispose() {
+ EXPECT_FALSE(g_dispose_was_called_for_pre_finalizer_base);
+ EXPECT_TRUE(g_dispose_was_called_for_pre_finalizer_sub_class);
+ EXPECT_TRUE(g_dispose_was_called_for_pre_finalizer_mixin);
+ EXPECT_FALSE(was_destructed_);
+ g_dispose_was_called_for_pre_finalizer_base = true;
+ }
+
+ protected:
+ PreFinalizerBase() : was_destructed_(false) {}
+ bool was_destructed_;
+};
+
+class PreFinalizerMixin : public GarbageCollectedMixin {
+ USING_PRE_FINALIZER(PreFinalizerMixin, Dispose);
+
+ public:
+ ~PreFinalizerMixin() { was_destructed_ = true; }
+ virtual void Trace(blink::Visitor* visitor) {}
+ void Dispose() {
+ EXPECT_FALSE(g_dispose_was_called_for_pre_finalizer_base);
+ EXPECT_TRUE(g_dispose_was_called_for_pre_finalizer_sub_class);
+ EXPECT_FALSE(g_dispose_was_called_for_pre_finalizer_mixin);
+ EXPECT_FALSE(was_destructed_);
+ g_dispose_was_called_for_pre_finalizer_mixin = true;
+ }
+
+ protected:
+ PreFinalizerMixin() : was_destructed_(false) {}
+ bool was_destructed_;
+};
+
+class PreFinalizerSubClass : public PreFinalizerBase, public PreFinalizerMixin {
+ USING_GARBAGE_COLLECTED_MIXIN(PreFinalizerSubClass);
+ USING_PRE_FINALIZER(PreFinalizerSubClass, Dispose);
+
+ public:
+ static PreFinalizerSubClass* Create() { return new PreFinalizerSubClass(); }
+ ~PreFinalizerSubClass() { was_destructed_ = true; }
+ virtual void Trace(blink::Visitor* visitor) {}
+ void Dispose() {
+ EXPECT_FALSE(g_dispose_was_called_for_pre_finalizer_base);
+ EXPECT_FALSE(g_dispose_was_called_for_pre_finalizer_sub_class);
+ EXPECT_FALSE(g_dispose_was_called_for_pre_finalizer_mixin);
+ EXPECT_FALSE(was_destructed_);
+ g_dispose_was_called_for_pre_finalizer_sub_class = true;
+ }
+
+ protected:
+ PreFinalizerSubClass() : was_destructed_(false) {}
+ bool was_destructed_;
+};
+
+template <typename T>
+class FinalizationObserver : public GarbageCollected<FinalizationObserver<T>> {
+ public:
+ static FinalizationObserver* Create(T* data) {
+ return new FinalizationObserver(data);
+ }
+ bool DidCallWillFinalize() const { return did_call_will_finalize_; }
+
+ void Trace(blink::Visitor* visitor) {
+ visitor->template RegisterWeakMembers<
+ FinalizationObserver<T>, &FinalizationObserver<T>::ZapWeakMembers>(
+ this);
+ }
+
+ void ZapWeakMembers(Visitor* visitor) {
+ if (data_ && !ThreadHeap::IsHeapObjectAlive(data_)) {
+ data_->WillFinalize();
+ data_ = nullptr;
+ did_call_will_finalize_ = true;
+ }
+ }
+
+ private:
+ FinalizationObserver(T* data) : data_(data), did_call_will_finalize_(false) {}
+
+ WeakMember<T> data_;
+ bool did_call_will_finalize_;
+};
+
+class FinalizationObserverWithHashMap {
+ public:
+ typedef HeapHashMap<WeakMember<Observable>,
+ std::unique_ptr<FinalizationObserverWithHashMap>>
+ ObserverMap;
+
+ explicit FinalizationObserverWithHashMap(Observable& target)
+ : target_(target) {}
+ ~FinalizationObserverWithHashMap() {
+ target_.WillFinalize();
+ did_call_will_finalize_ = true;
+ }
+
+ static ObserverMap& Observe(Observable& target) {
+ ObserverMap& map = Observers();
+ ObserverMap::AddResult result = map.insert(&target, nullptr);
+ if (result.is_new_entry) {
+ result.stored_value->value =
+ std::make_unique<FinalizationObserverWithHashMap>(target);
+ } else {
+ DCHECK(result.stored_value->value);
+ }
+ return map;
+ }
+
+ static void ClearObservers() {
+ delete observer_map_;
+ observer_map_ = nullptr;
+ }
+
+ static bool did_call_will_finalize_;
+
+ private:
+ static ObserverMap& Observers() {
+ if (!observer_map_)
+ observer_map_ = new Persistent<ObserverMap>(new ObserverMap());
+ return **observer_map_;
+ }
+
+ Observable& target_;
+ static Persistent<ObserverMap>* observer_map_;
+};
+
+bool FinalizationObserverWithHashMap::did_call_will_finalize_ = false;
+Persistent<FinalizationObserverWithHashMap::ObserverMap>*
+ FinalizationObserverWithHashMap::observer_map_;
+
+class SuperClass;
+
+class PointsBack : public GarbageCollectedFinalized<PointsBack> {
+ public:
+ static PointsBack* Create() { return new PointsBack; }
+
+ ~PointsBack() { --alive_count_; }
+
+ void SetBackPointer(SuperClass* back_pointer) {
+ back_pointer_ = back_pointer;
+ }
+
+ SuperClass* BackPointer() const { return back_pointer_; }
+
+ void Trace(blink::Visitor* visitor) { visitor->Trace(back_pointer_); }
+
+ static int alive_count_;
+
+ private:
+ PointsBack() : back_pointer_(nullptr) { ++alive_count_; }
+
+ WeakMember<SuperClass> back_pointer_;
+};
+
+int PointsBack::alive_count_ = 0;
+
+class SuperClass : public GarbageCollectedFinalized<SuperClass> {
+ public:
+ static SuperClass* Create(PointsBack* points_back) {
+ return new SuperClass(points_back);
+ }
+
+ virtual ~SuperClass() { --alive_count_; }
+
+ void DoStuff(SuperClass* target,
+ PointsBack* points_back,
+ int super_class_count) {
+ ConservativelyCollectGarbage();
+ EXPECT_EQ(points_back, target->GetPointsBack());
+ EXPECT_EQ(super_class_count, SuperClass::alive_count_);
+ }
+
+ virtual void Trace(blink::Visitor* visitor) { visitor->Trace(points_back_); }
+
+ PointsBack* GetPointsBack() const { return points_back_.Get(); }
+
+ static int alive_count_;
+
+ protected:
+ explicit SuperClass(PointsBack* points_back) : points_back_(points_back) {
+ points_back_->SetBackPointer(this);
+ ++alive_count_;
+ }
+
+ private:
+ Member<PointsBack> points_back_;
+};
+
+int SuperClass::alive_count_ = 0;
+class SubData : public GarbageCollectedFinalized<SubData> {
+ public:
+ SubData() { ++alive_count_; }
+ ~SubData() { --alive_count_; }
+
+ void Trace(blink::Visitor* visitor) {}
+
+ static int alive_count_;
+};
+
+int SubData::alive_count_ = 0;
+
+class SubClass : public SuperClass {
+ public:
+ static SubClass* Create(PointsBack* points_back) {
+ return new SubClass(points_back);
+ }
+
+ ~SubClass() override { --alive_count_; }
+
+ virtual void Trace(blink::Visitor* visitor) {
+ visitor->Trace(data_);
+ SuperClass::Trace(visitor);
+ }
+
+ static int alive_count_;
+
+ private:
+ explicit SubClass(PointsBack* points_back)
+ : SuperClass(points_back), data_(new SubData) {
+ ++alive_count_;
+ }
+
+ private:
+ Member<SubData> data_;
+};
+
+int SubClass::alive_count_ = 0;
+
+class Mixin : public GarbageCollectedMixin {
+ public:
+ virtual void Trace(blink::Visitor* visitor) {}
+
+ virtual char GetPayload(int i) { return padding_[i]; }
+
+ protected:
+ int padding_[8];
+};
+
+class UseMixin : public SimpleObject, public Mixin {
+ USING_GARBAGE_COLLECTED_MIXIN(UseMixin)
+ public:
+ static UseMixin* Create() { return new UseMixin(); }
+
+ static int trace_count_;
+ virtual void Trace(blink::Visitor* visitor) {
+ SimpleObject::Trace(visitor);
+ Mixin::Trace(visitor);
+ ++trace_count_;
+ }
+
+ private:
+ UseMixin() {
+ // Verify that WTF::IsGarbageCollectedType<> works as expected for mixins.
+ static_assert(WTF::IsGarbageCollectedType<UseMixin>::value,
+ "IsGarbageCollectedType<> sanity check failed for GC mixin.");
+ trace_count_ = 0;
+ }
+};
+
+int UseMixin::trace_count_ = 0;
+
+class VectorObject {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ VectorObject() { value_ = SimpleFinalizedObject::Create(); }
+
+ void Trace(blink::Visitor* visitor) { visitor->Trace(value_); }
+
+ private:
+ Member<SimpleFinalizedObject> value_;
+};
+
+class VectorObjectInheritedTrace : public VectorObject {};
+
+class VectorObjectNoTrace {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ VectorObjectNoTrace() { value_ = SimpleFinalizedObject::Create(); }
+
+ private:
+ Member<SimpleFinalizedObject> value_;
+};
+
+class TerminatedArrayItem {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ TerminatedArrayItem(IntWrapper* payload)
+ : payload_(payload), is_last_(false) {}
+
+ void Trace(blink::Visitor* visitor) { visitor->Trace(payload_); }
+
+ bool IsLastInArray() const { return is_last_; }
+ void SetLastInArray(bool value) { is_last_ = value; }
+
+ IntWrapper* Payload() const { return payload_; }
+
+ private:
+ Member<IntWrapper> payload_;
+ bool is_last_;
+};
+
+} // namespace blink
+
+WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(blink::TerminatedArrayItem);
+WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(blink::VectorObject);
+WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(
+ blink::VectorObjectInheritedTrace);
+WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(blink::VectorObjectNoTrace);
+
+namespace blink {
+
+class OneKiloByteObject : public GarbageCollectedFinalized<OneKiloByteObject> {
+ public:
+ ~OneKiloByteObject() { destructor_calls_++; }
+ char* Data() { return data_; }
+ void Trace(blink::Visitor* visitor) {}
+ static int destructor_calls_;
+
+ private:
+ static const size_t kLength = 1024;
+ char data_[kLength];
+};
+
+int OneKiloByteObject::destructor_calls_ = 0;
+
+class DynamicallySizedObject : public GarbageCollected<DynamicallySizedObject> {
+ public:
+ static DynamicallySizedObject* Create(size_t size) {
+ void* slot = ThreadHeap::Allocate<DynamicallySizedObject>(size);
+ return new (slot) DynamicallySizedObject();
+ }
+
+ void* operator new(std::size_t, void* location) { return location; }
+
+ uint8_t Get(int i) { return *(reinterpret_cast<uint8_t*>(this) + i); }
+
+ void Trace(blink::Visitor* visitor) {}
+
+ private:
+ DynamicallySizedObject() = default;
+};
+
+class FinalizationAllocator
+ : public GarbageCollectedFinalized<FinalizationAllocator> {
+ public:
+ FinalizationAllocator(Persistent<IntWrapper>* wrapper) : wrapper_(wrapper) {}
+
+ ~FinalizationAllocator() {
+ for (int i = 0; i < 10; ++i)
+ *wrapper_ = IntWrapper::Create(42);
+ for (int i = 0; i < 512; ++i)
+ new OneKiloByteObject();
+ for (int i = 0; i < 32; ++i)
+ LargeHeapObject::Create();
+ }
+
+ void Trace(blink::Visitor* visitor) {}
+
+ private:
+ Persistent<IntWrapper>* wrapper_;
+};
+
+class PreFinalizationAllocator
+ : public GarbageCollectedFinalized<PreFinalizationAllocator> {
+ USING_PRE_FINALIZER(PreFinalizationAllocator, Dispose);
+
+ public:
+ PreFinalizationAllocator(Persistent<IntWrapper>* wrapper)
+ : wrapper_(wrapper) {}
+
+ void Dispose() {
+ for (int i = 0; i < 10; ++i)
+ *wrapper_ = IntWrapper::Create(42);
+ for (int i = 0; i < 512; ++i)
+ new OneKiloByteObject();
+ for (int i = 0; i < 32; ++i)
+ LargeHeapObject::Create();
+ }
+
+ void Trace(blink::Visitor* visitor) {}
+
+ private:
+ Persistent<IntWrapper>* wrapper_;
+};
+
+class PreFinalizerBackingShrinkForbidden
+ : public GarbageCollectedFinalized<PreFinalizerBackingShrinkForbidden> {
+ USING_PRE_FINALIZER(PreFinalizerBackingShrinkForbidden, Dispose);
+
+ public:
+ PreFinalizerBackingShrinkForbidden() {
+ for (int i = 0; i < 32; ++i) {
+ vector_.push_back(new IntWrapper(i));
+ }
+ EXPECT_LT(31ul, vector_.capacity());
+
+ for (int i = 0; i < 32; ++i) {
+ map_.insert(i + 1, new IntWrapper(i + 1));
+ }
+ EXPECT_LT(31ul, map_.Capacity());
+ }
+
+ void Dispose() {
+ // Remove all elemets except one so that vector_ will try to shrink.
+ for (int i = 1; i < 32; ++i) {
+ vector_.pop_back();
+ }
+ // Check that vector_ hasn't shrunk.
+ EXPECT_LT(31ul, vector_.capacity());
+ // Just releasing the backing is allowed.
+ vector_.clear();
+ EXPECT_EQ(0ul, vector_.capacity());
+
+ // Remove elemets so that map_ will try to shrink.
+ for (int i = 0; i < 32; ++i) {
+ map_.erase(i + 1);
+ }
+ // Check that map_ hasn't shrunk.
+ EXPECT_LT(31ul, map_.Capacity());
+ // Just releasing the backing is allowed.
+ map_.clear();
+ EXPECT_EQ(0ul, map_.Capacity());
+ }
+
+ void Trace(blink::Visitor* visitor) {
+ visitor->Trace(vector_);
+ visitor->Trace(map_);
+ }
+
+ private:
+ HeapVector<Member<IntWrapper>> vector_;
+ HeapHashMap<int, Member<IntWrapper>> map_;
+};
+
+TEST(HeapTest, PreFinalizerBackingShrinkForbidden) {
+ new PreFinalizerBackingShrinkForbidden();
+ PreciselyCollectGarbage();
+}
+
+class PreFinalizerVectorBackingExpandForbidden
+ : public GarbageCollectedFinalized<
+ PreFinalizerVectorBackingExpandForbidden> {
+ USING_PRE_FINALIZER(PreFinalizerVectorBackingExpandForbidden, Dispose);
+
+ public:
+ PreFinalizerVectorBackingExpandForbidden() {
+ vector_.push_back(new IntWrapper(1));
+ }
+
+ void Dispose() { EXPECT_DEATH(Test(), ""); }
+
+ void Test() {
+ // vector_'s backing will need to expand.
+ for (int i = 0; i < 32; ++i) {
+ vector_.push_back(nullptr);
+ }
+ }
+
+ void Trace(blink::Visitor* visitor) { visitor->Trace(vector_); }
+
+ private:
+ HeapVector<Member<IntWrapper>> vector_;
+};
+
+TEST(HeapDeathTest, PreFinalizerVectorBackingExpandForbidden) {
+ new PreFinalizerVectorBackingExpandForbidden();
+ PreciselyCollectGarbage();
+}
+
+class PreFinalizerHashTableBackingExpandForbidden
+ : public GarbageCollectedFinalized<
+ PreFinalizerHashTableBackingExpandForbidden> {
+ USING_PRE_FINALIZER(PreFinalizerHashTableBackingExpandForbidden, Dispose);
+
+ public:
+ PreFinalizerHashTableBackingExpandForbidden() {
+ map_.insert(123, new IntWrapper(123));
+ }
+
+ void Dispose() { EXPECT_DEATH(Test(), ""); }
+
+ void Test() {
+ // map_'s backing will need to expand.
+ for (int i = 1; i < 32; ++i) {
+ map_.insert(i, nullptr);
+ }
+ }
+
+ void Trace(blink::Visitor* visitor) { visitor->Trace(map_); }
+
+ private:
+ HeapHashMap<int, Member<IntWrapper>> map_;
+};
+
+TEST(HeapDeathTest, PreFinalizerHashTableBackingExpandForbidden) {
+ new PreFinalizerHashTableBackingExpandForbidden();
+ PreciselyCollectGarbage();
+}
+
+class LargeMixin : public GarbageCollected<LargeMixin>, public Mixin {
+ USING_GARBAGE_COLLECTED_MIXIN(LargeMixin);
+
+ private:
+ char data[65536];
+};
+
+TEST(HeapDeathTest, LargeGarbageCollectedMixin) {
+ EXPECT_DEATH(new LargeMixin(), "");
+}
+
+TEST(HeapTest, Transition) {
+ {
+ RefCountedAndGarbageCollected::destructor_calls_ = 0;
+ Persistent<RefCountedAndGarbageCollected> ref_counted =
+ RefCountedAndGarbageCollected::Create();
+ PreciselyCollectGarbage();
+ EXPECT_EQ(0, RefCountedAndGarbageCollected::destructor_calls_);
+ }
+ PreciselyCollectGarbage();
+ EXPECT_EQ(1, RefCountedAndGarbageCollected::destructor_calls_);
+ RefCountedAndGarbageCollected::destructor_calls_ = 0;
+
+ Persistent<PointsBack> points_back1 = PointsBack::Create();
+ Persistent<PointsBack> points_back2 = PointsBack::Create();
+ Persistent<SuperClass> super_class = SuperClass::Create(points_back1);
+ Persistent<SubClass> sub_class = SubClass::Create(points_back2);
+ EXPECT_EQ(2, PointsBack::alive_count_);
+ EXPECT_EQ(2, SuperClass::alive_count_);
+ EXPECT_EQ(1, SubClass::alive_count_);
+ EXPECT_EQ(1, SubData::alive_count_);
+
+ PreciselyCollectGarbage();
+ EXPECT_EQ(0, RefCountedAndGarbageCollected::destructor_calls_);
+ EXPECT_EQ(2, PointsBack::alive_count_);
+ EXPECT_EQ(2, SuperClass::alive_count_);
+ EXPECT_EQ(1, SubClass::alive_count_);
+ EXPECT_EQ(1, SubData::alive_count_);
+
+ super_class->DoStuff(super_class.Release(), points_back1.Get(), 2);
+ PreciselyCollectGarbage();
+ EXPECT_EQ(2, PointsBack::alive_count_);
+ EXPECT_EQ(1, SuperClass::alive_count_);
+ EXPECT_EQ(1, SubClass::alive_count_);
+ EXPECT_EQ(1, SubData::alive_count_);
+ EXPECT_EQ(nullptr, points_back1->BackPointer());
+
+ points_back1.Release();
+ PreciselyCollectGarbage();
+ EXPECT_EQ(1, PointsBack::alive_count_);
+ EXPECT_EQ(1, SuperClass::alive_count_);
+ EXPECT_EQ(1, SubClass::alive_count_);
+ EXPECT_EQ(1, SubData::alive_count_);
+
+ sub_class->DoStuff(sub_class.Release(), points_back2.Get(), 1);
+ PreciselyCollectGarbage();
+ EXPECT_EQ(1, PointsBack::alive_count_);
+ EXPECT_EQ(0, SuperClass::alive_count_);
+ EXPECT_EQ(0, SubClass::alive_count_);
+ EXPECT_EQ(0, SubData::alive_count_);
+ EXPECT_EQ(nullptr, points_back2->BackPointer());
+
+ points_back2.Release();
+ PreciselyCollectGarbage();
+ EXPECT_EQ(0, PointsBack::alive_count_);
+ EXPECT_EQ(0, SuperClass::alive_count_);
+ EXPECT_EQ(0, SubClass::alive_count_);
+ EXPECT_EQ(0, SubData::alive_count_);
+
+ EXPECT_TRUE(super_class == sub_class);
+}
+
+TEST(HeapTest, Threading) {
+ ThreadedHeapTester::Test();
+}
+
+TEST(HeapTest, ThreadedWeakness) {
+ ThreadedWeaknessTester::Test();
+}
+
+TEST(HeapTest, ThreadPersistent) {
+ ThreadPersistentHeapTester::Test();
+}
+
+TEST(HeapTest, BasicFunctionality) {
+ ThreadHeap& heap = ThreadState::Current()->Heap();
+ ClearOutOldGarbage();
+ size_t initial_object_payload_size = heap.ObjectPayloadSizeForTesting();
+ {
+ size_t slack = 0;
+
+ // When the test starts there may already have been leaked some memory
+ // on the heap, so we establish a base line.
+ size_t base_level = initial_object_payload_size;
+ bool test_pages_allocated = !base_level;
+ if (test_pages_allocated)
+ EXPECT_EQ(heap.HeapStats().AllocatedSpace(), 0ul);
+
+ // This allocates objects on the general heap which should add a page of
+ // memory.
+ DynamicallySizedObject* alloc32 = DynamicallySizedObject::Create(32);
+ slack += 4;
+ memset(alloc32, 40, 32);
+ DynamicallySizedObject* alloc64 = DynamicallySizedObject::Create(64);
+ slack += 4;
+ memset(alloc64, 27, 64);
+
+ size_t total = 96;
+
+ CheckWithSlack(base_level + total, heap.ObjectPayloadSizeForTesting(),
+ slack);
+ if (test_pages_allocated)
+ EXPECT_EQ(heap.HeapStats().AllocatedSpace(), kBlinkPageSize * 2);
+
+ EXPECT_EQ(alloc32->Get(0), 40);
+ EXPECT_EQ(alloc32->Get(31), 40);
+ EXPECT_EQ(alloc64->Get(0), 27);
+ EXPECT_EQ(alloc64->Get(63), 27);
+
+ ConservativelyCollectGarbage();
+
+ EXPECT_EQ(alloc32->Get(0), 40);
+ EXPECT_EQ(alloc32->Get(31), 40);
+ EXPECT_EQ(alloc64->Get(0), 27);
+ EXPECT_EQ(alloc64->Get(63), 27);
+ }
+
+ ClearOutOldGarbage();
+ size_t total = 0;
+ size_t slack = 0;
+ size_t base_level = heap.ObjectPayloadSizeForTesting();
+ bool test_pages_allocated = !base_level;
+ if (test_pages_allocated)
+ EXPECT_EQ(heap.HeapStats().AllocatedSpace(), 0ul);
+
+ size_t big = 1008;
+ Persistent<DynamicallySizedObject> big_area =
+ DynamicallySizedObject::Create(big);
+ total += big;
+ slack += 4;
+
+ size_t persistent_count = 0;
+ const size_t kNumPersistents = 100000;
+ Persistent<DynamicallySizedObject>* persistents[kNumPersistents];
+
+ for (int i = 0; i < 1000; i++) {
+ size_t size = 128 + i * 8;
+ total += size;
+ persistents[persistent_count++] = new Persistent<DynamicallySizedObject>(
+ DynamicallySizedObject::Create(size));
+ slack += 4;
+ CheckWithSlack(base_level + total, heap.ObjectPayloadSizeForTesting(),
+ slack);
+ if (test_pages_allocated)
+ EXPECT_EQ(0ul, heap.HeapStats().AllocatedSpace() & (kBlinkPageSize - 1));
+ }
+
+ {
+ DynamicallySizedObject* alloc32b(DynamicallySizedObject::Create(32));
+ slack += 4;
+ memset(alloc32b, 40, 32);
+ DynamicallySizedObject* alloc64b(DynamicallySizedObject::Create(64));
+ slack += 4;
+ memset(alloc64b, 27, 64);
+ EXPECT_TRUE(alloc32b != alloc64b);
+
+ total += 96;
+ CheckWithSlack(base_level + total, heap.ObjectPayloadSizeForTesting(),
+ slack);
+ if (test_pages_allocated)
+ EXPECT_EQ(0ul, heap.HeapStats().AllocatedSpace() & (kBlinkPageSize - 1));
+ }
+
+ ClearOutOldGarbage();
+ total -= 96;
+ slack -= 8;
+ if (test_pages_allocated)
+ EXPECT_EQ(0ul, heap.HeapStats().AllocatedSpace() & (kBlinkPageSize - 1));
+
+ // Clear the persistent, so that the big area will be garbage collected.
+ big_area.Release();
+ ClearOutOldGarbage();
+
+ total -= big;
+ slack -= 4;
+ CheckWithSlack(base_level + total, heap.ObjectPayloadSizeForTesting(), slack);
+ if (test_pages_allocated)
+ EXPECT_EQ(0ul, heap.HeapStats().AllocatedSpace() & (kBlinkPageSize - 1));
+
+ CheckWithSlack(base_level + total, heap.ObjectPayloadSizeForTesting(), slack);
+ if (test_pages_allocated)
+ EXPECT_EQ(0ul, heap.HeapStats().AllocatedSpace() & (kBlinkPageSize - 1));
+
+ for (size_t i = 0; i < persistent_count; i++) {
+ delete persistents[i];
+ persistents[i] = nullptr;
+ }
+
+ uint8_t* address = reinterpret_cast<uint8_t*>(
+ ThreadHeap::Allocate<DynamicallySizedObject>(100));
+ for (int i = 0; i < 100; i++)
+ address[i] = i;
+ address = reinterpret_cast<uint8_t*>(
+ ThreadHeap::Reallocate<DynamicallySizedObject>(address, 100000));
+ for (int i = 0; i < 100; i++)
+ EXPECT_EQ(address[i], i);
+ address = reinterpret_cast<uint8_t*>(
+ ThreadHeap::Reallocate<DynamicallySizedObject>(address, 50));
+ for (int i = 0; i < 50; i++)
+ EXPECT_EQ(address[i], i);
+ // This should be equivalent to free(address).
+ EXPECT_EQ(reinterpret_cast<uintptr_t>(
+ ThreadHeap::Reallocate<DynamicallySizedObject>(address, 0)),
+ 0ul);
+ // This should be equivalent to malloc(0).
+ EXPECT_EQ(reinterpret_cast<uintptr_t>(
+ ThreadHeap::Reallocate<DynamicallySizedObject>(nullptr, 0)),
+ 0ul);
+}
+
+TEST(HeapTest, SimpleAllocation) {
+ ThreadHeap& heap = ThreadState::Current()->Heap();
+ ClearOutOldGarbage();
+ EXPECT_EQ(0ul, heap.ObjectPayloadSizeForTesting());
+
+ // Allocate an object in the heap.
+ HeapAllocatedArray* array = new HeapAllocatedArray();
+ EXPECT_TRUE(heap.ObjectPayloadSizeForTesting() >= sizeof(HeapAllocatedArray));
+
+ // Sanity check of the contents in the heap.
+ EXPECT_EQ(0, array->at(0));
+ EXPECT_EQ(42, array->at(42));
+ EXPECT_EQ(0, array->at(128));
+ EXPECT_EQ(999 % 128, array->at(999));
+}
+
+TEST(HeapTest, SimplePersistent) {
+ Persistent<TraceCounter> trace_counter = TraceCounter::Create();
+ EXPECT_EQ(0, trace_counter->TraceCount());
+ PreciselyCollectGarbage();
+ int saved_trace_count = trace_counter->TraceCount();
+ EXPECT_LT(0, saved_trace_count);
+
+ Persistent<ClassWithMember> class_with_member = ClassWithMember::Create();
+ EXPECT_EQ(0, class_with_member->TraceCount());
+ PreciselyCollectGarbage();
+ EXPECT_LT(0, class_with_member->TraceCount());
+ EXPECT_LT(saved_trace_count, trace_counter->TraceCount());
+}
+
+TEST(HeapTest, SimpleFinalization) {
+ {
+ Persistent<SimpleFinalizedObject> finalized =
+ SimpleFinalizedObject::Create();
+ EXPECT_EQ(0, SimpleFinalizedObject::destructor_calls_);
+ PreciselyCollectGarbage();
+ EXPECT_EQ(0, SimpleFinalizedObject::destructor_calls_);
+ }
+
+ PreciselyCollectGarbage();
+ EXPECT_EQ(1, SimpleFinalizedObject::destructor_calls_);
+}
+
+#if DCHECK_IS_ON() || defined(LEAK_SANITIZER) || defined(ADDRESS_SANITIZER)
+TEST(HeapTest, FreelistReuse) {
+ ClearOutOldGarbage();
+
+ for (int i = 0; i < 100; i++)
+ new IntWrapper(i);
+ IntWrapper* p1 = new IntWrapper(100);
+ PreciselyCollectGarbage();
+ // In non-production builds, we delay reusing freed memory for at least
+ // one GC cycle.
+ for (int i = 0; i < 100; i++) {
+ IntWrapper* p2 = new IntWrapper(i);
+ EXPECT_NE(p1, p2);
+ }
+
+ PreciselyCollectGarbage();
+ PreciselyCollectGarbage();
+ // Now the freed memory in the first GC should be reused.
+ bool reused_memory_found = false;
+ for (int i = 0; i < 10000; i++) {
+ IntWrapper* p2 = new IntWrapper(i);
+ if (p1 == p2) {
+ reused_memory_found = true;
+ break;
+ }
+ }
+ EXPECT_TRUE(reused_memory_found);
+}
+#endif
+
+TEST(HeapTest, LazySweepingPages) {
+ ClearOutOldGarbage();
+
+ SimpleFinalizedObject::destructor_calls_ = 0;
+ EXPECT_EQ(0, SimpleFinalizedObject::destructor_calls_);
+ for (int i = 0; i < 1000; i++)
+ SimpleFinalizedObject::Create();
+ ThreadState::Current()->CollectGarbage(
+ BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
+ BlinkGC::kLazySweeping, BlinkGC::kForcedGC);
+ EXPECT_EQ(0, SimpleFinalizedObject::destructor_calls_);
+ for (int i = 0; i < 10000; i++)
+ SimpleFinalizedObject::Create();
+ EXPECT_EQ(1000, SimpleFinalizedObject::destructor_calls_);
+ PreciselyCollectGarbage();
+ EXPECT_EQ(11000, SimpleFinalizedObject::destructor_calls_);
+}
+
+TEST(HeapTest, LazySweepingLargeObjectPages) {
+ ClearOutOldGarbage();
+
+ // Create free lists that can be reused for IntWrappers created in
+ // LargeHeapObject::create().
+ Persistent<IntWrapper> p1 = new IntWrapper(1);
+ for (int i = 0; i < 100; i++) {
+ new IntWrapper(i);
+ }
+ Persistent<IntWrapper> p2 = new IntWrapper(2);
+ PreciselyCollectGarbage();
+ PreciselyCollectGarbage();
+
+ LargeHeapObject::destructor_calls_ = 0;
+ EXPECT_EQ(0, LargeHeapObject::destructor_calls_);
+ for (int i = 0; i < 10; i++)
+ LargeHeapObject::Create();
+ ThreadState::Current()->CollectGarbage(
+ BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
+ BlinkGC::kLazySweeping, BlinkGC::kForcedGC);
+ EXPECT_EQ(0, LargeHeapObject::destructor_calls_);
+ for (int i = 0; i < 10; i++) {
+ LargeHeapObject::Create();
+ EXPECT_EQ(i + 1, LargeHeapObject::destructor_calls_);
+ }
+ LargeHeapObject::Create();
+ LargeHeapObject::Create();
+ EXPECT_EQ(10, LargeHeapObject::destructor_calls_);
+ ThreadState::Current()->CollectGarbage(
+ BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
+ BlinkGC::kLazySweeping, BlinkGC::kForcedGC);
+ EXPECT_EQ(10, LargeHeapObject::destructor_calls_);
+ PreciselyCollectGarbage();
+ EXPECT_EQ(22, LargeHeapObject::destructor_calls_);
+}
+
+class SimpleFinalizedEagerObjectBase
+ : public GarbageCollectedFinalized<SimpleFinalizedEagerObjectBase> {
+ public:
+ virtual ~SimpleFinalizedEagerObjectBase() = default;
+ void Trace(blink::Visitor* visitor) {}
+
+ EAGERLY_FINALIZE();
+
+ protected:
+ SimpleFinalizedEagerObjectBase() = default;
+};
+
+class SimpleFinalizedEagerObject : public SimpleFinalizedEagerObjectBase {
+ public:
+ static SimpleFinalizedEagerObject* Create() {
+ return new SimpleFinalizedEagerObject();
+ }
+
+ ~SimpleFinalizedEagerObject() override { ++destructor_calls_; }
+
+ static int destructor_calls_;
+
+ private:
+ SimpleFinalizedEagerObject() = default;
+};
+
+template <typename T>
+class ParameterizedButEmpty {
+ public:
+ EAGERLY_FINALIZE();
+};
+
+class SimpleFinalizedObjectInstanceOfTemplate final
+ : public GarbageCollectedFinalized<SimpleFinalizedObjectInstanceOfTemplate>,
+ public ParameterizedButEmpty<SimpleFinalizedObjectInstanceOfTemplate> {
+ public:
+ static SimpleFinalizedObjectInstanceOfTemplate* Create() {
+ return new SimpleFinalizedObjectInstanceOfTemplate();
+ }
+ ~SimpleFinalizedObjectInstanceOfTemplate() { ++destructor_calls_; }
+
+ void Trace(blink::Visitor* visitor) {}
+
+ static int destructor_calls_;
+
+ private:
+ SimpleFinalizedObjectInstanceOfTemplate() = default;
+};
+
+int SimpleFinalizedEagerObject::destructor_calls_ = 0;
+int SimpleFinalizedObjectInstanceOfTemplate::destructor_calls_ = 0;
+
+TEST(HeapTest, EagerlySweepingPages) {
+ ClearOutOldGarbage();
+
+ SimpleFinalizedObject::destructor_calls_ = 0;
+ SimpleFinalizedEagerObject::destructor_calls_ = 0;
+ SimpleFinalizedObjectInstanceOfTemplate::destructor_calls_ = 0;
+ EXPECT_EQ(0, SimpleFinalizedObject::destructor_calls_);
+ EXPECT_EQ(0, SimpleFinalizedEagerObject::destructor_calls_);
+ for (int i = 0; i < 1000; i++)
+ SimpleFinalizedObject::Create();
+ for (int i = 0; i < 100; i++)
+ SimpleFinalizedEagerObject::Create();
+ for (int i = 0; i < 100; i++)
+ SimpleFinalizedObjectInstanceOfTemplate::Create();
+ ThreadState::Current()->CollectGarbage(
+ BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
+ BlinkGC::kLazySweeping, BlinkGC::kForcedGC);
+ EXPECT_EQ(0, SimpleFinalizedObject::destructor_calls_);
+ EXPECT_EQ(100, SimpleFinalizedEagerObject::destructor_calls_);
+ EXPECT_EQ(100, SimpleFinalizedObjectInstanceOfTemplate::destructor_calls_);
+}
+
+TEST(HeapTest, Finalization) {
+ {
+ HeapTestSubClass* t1 = HeapTestSubClass::Create();
+ HeapTestSubClass* t2 = HeapTestSubClass::Create();
+ HeapTestSuperClass* t3 = HeapTestSuperClass::Create();
+ // FIXME(oilpan): Ignore unused variables.
+ (void)t1;
+ (void)t2;
+ (void)t3;
+ }
+ // Nothing is marked so the GC should free everything and call
+ // the finalizer on all three objects.
+ PreciselyCollectGarbage();
+ EXPECT_EQ(2, HeapTestSubClass::destructor_calls_);
+ EXPECT_EQ(3, HeapTestSuperClass::destructor_calls_);
+ // Destructors not called again when GCing again.
+ PreciselyCollectGarbage();
+ EXPECT_EQ(2, HeapTestSubClass::destructor_calls_);
+ EXPECT_EQ(3, HeapTestSuperClass::destructor_calls_);
+}
+
+TEST(HeapTest, TypedArenaSanity) {
+ // We use TraceCounter for allocating an object on the general heap.
+ Persistent<TraceCounter> general_heap_object = TraceCounter::Create();
+ Persistent<IntNode> typed_heap_object = IntNode::Create(0);
+ EXPECT_NE(PageFromObject(general_heap_object.Get()),
+ PageFromObject(typed_heap_object.Get()));
+}
+
+TEST(HeapTest, NoAllocation) {
+ ThreadState* state = ThreadState::Current();
+ EXPECT_TRUE(state->IsAllocationAllowed());
+ {
+ // Disallow allocation
+ ThreadState::NoAllocationScope no_allocation_scope(state);
+ EXPECT_FALSE(state->IsAllocationAllowed());
+ }
+ EXPECT_TRUE(state->IsAllocationAllowed());
+}
+
+TEST(HeapTest, Members) {
+ Bar::live_ = 0;
+ {
+ Persistent<Baz> h1;
+ Persistent<Baz> h2;
+ {
+ h1 = Baz::Create(Bar::Create());
+ PreciselyCollectGarbage();
+ EXPECT_EQ(1u, Bar::live_);
+ h2 = Baz::Create(Bar::Create());
+ PreciselyCollectGarbage();
+ EXPECT_EQ(2u, Bar::live_);
+ }
+ PreciselyCollectGarbage();
+ EXPECT_EQ(2u, Bar::live_);
+ h1->Clear();
+ PreciselyCollectGarbage();
+ EXPECT_EQ(1u, Bar::live_);
+ }
+ PreciselyCollectGarbage();
+ EXPECT_EQ(0u, Bar::live_);
+}
+
+TEST(HeapTest, MarkTest) {
+ {
+ Bar::live_ = 0;
+ Persistent<Bar> bar = Bar::Create();
+#if DCHECK_IS_ON()
+ DCHECK(ThreadState::Current()->Heap().FindPageFromAddress(bar));
+#endif
+ EXPECT_EQ(1u, Bar::live_);
+ {
+ Foo* foo = Foo::Create(bar);
+#if DCHECK_IS_ON()
+ DCHECK(ThreadState::Current()->Heap().FindPageFromAddress(foo));
+#endif
+ EXPECT_EQ(2u, Bar::live_);
+ EXPECT_TRUE(reinterpret_cast<Address>(foo) !=
+ reinterpret_cast<Address>(bar.Get()));
+ ConservativelyCollectGarbage();
+ EXPECT_TRUE(foo != bar); // To make sure foo is kept alive.
+ EXPECT_EQ(2u, Bar::live_);
+ }
+ PreciselyCollectGarbage();
+ EXPECT_EQ(1u, Bar::live_);
+ }
+ PreciselyCollectGarbage();
+ EXPECT_EQ(0u, Bar::live_);
+}
+
+TEST(HeapTest, DeepTest) {
+ const unsigned kDepth = 100000;
+ Bar::live_ = 0;
+ {
+ Bar* bar = Bar::Create();
+#if DCHECK_IS_ON()
+ DCHECK(ThreadState::Current()->Heap().FindPageFromAddress(bar));
+#endif
+ Foo* foo = Foo::Create(bar);
+#if DCHECK_IS_ON()
+ DCHECK(ThreadState::Current()->Heap().FindPageFromAddress(foo));
+#endif
+ EXPECT_EQ(2u, Bar::live_);
+ for (unsigned i = 0; i < kDepth; i++) {
+ Foo* foo2 = Foo::Create(foo);
+ foo = foo2;
+#if DCHECK_IS_ON()
+ DCHECK(ThreadState::Current()->Heap().FindPageFromAddress(foo));
+#endif
+ }
+ EXPECT_EQ(kDepth + 2, Bar::live_);
+ ConservativelyCollectGarbage();
+ EXPECT_TRUE(foo != bar); // To make sure foo and bar are kept alive.
+ EXPECT_EQ(kDepth + 2, Bar::live_);
+ }
+ PreciselyCollectGarbage();
+ EXPECT_EQ(0u, Bar::live_);
+}
+
+TEST(HeapTest, WideTest) {
+ Bar::live_ = 0;
+ {
+ Bars* bars = Bars::Create();
+ unsigned width = Bars::kWidth;
+ EXPECT_EQ(width + 1, Bar::live_);
+ ConservativelyCollectGarbage();
+ EXPECT_EQ(width + 1, Bar::live_);
+ // Use bars here to make sure that it will be on the stack
+ // for the conservative stack scan to find.
+ EXPECT_EQ(width, bars->GetWidth());
+ }
+ EXPECT_EQ(Bars::kWidth + 1, Bar::live_);
+ PreciselyCollectGarbage();
+ EXPECT_EQ(0u, Bar::live_);
+}
+
+TEST(HeapTest, HashMapOfMembers) {
+ ThreadHeap& heap = ThreadState::Current()->Heap();
+ IntWrapper::destructor_calls_ = 0;
+
+ ClearOutOldGarbage();
+ size_t initial_object_payload_size = heap.ObjectPayloadSizeForTesting();
+ {
+ typedef HeapHashMap<Member<IntWrapper>, Member<IntWrapper>,
+ DefaultHash<Member<IntWrapper>>::Hash,
+ HashTraits<Member<IntWrapper>>,
+ HashTraits<Member<IntWrapper>>>
+ HeapObjectIdentityMap;
+
+ Persistent<HeapObjectIdentityMap> map = new HeapObjectIdentityMap();
+
+ map->clear();
+ size_t after_set_was_created = heap.ObjectPayloadSizeForTesting();
+ EXPECT_TRUE(after_set_was_created > initial_object_payload_size);
+
+ PreciselyCollectGarbage();
+ size_t after_gc = heap.ObjectPayloadSizeForTesting();
+ EXPECT_EQ(after_gc, after_set_was_created);
+
+ // If the additions below cause garbage collections, these
+ // pointers should be found by conservative stack scanning.
+ IntWrapper* one(IntWrapper::Create(1));
+ IntWrapper* another_one(IntWrapper::Create(1));
+
+ map->insert(one, one);
+
+ size_t after_one_add = heap.ObjectPayloadSizeForTesting();
+ EXPECT_TRUE(after_one_add > after_gc);
+
+ HeapObjectIdentityMap::iterator it(map->begin());
+ HeapObjectIdentityMap::iterator it2(map->begin());
+ ++it;
+ ++it2;
+
+ map->insert(another_one, one);
+
+ // The addition above can cause an allocation of a new
+ // backing store. We therefore garbage collect before
+ // taking the heap stats in order to get rid of the old
+ // backing store. We make sure to not use conservative
+ // stack scanning as that could find a pointer to the
+ // old backing.
+ PreciselyCollectGarbage();
+ size_t after_add_and_gc = heap.ObjectPayloadSizeForTesting();
+ EXPECT_TRUE(after_add_and_gc >= after_one_add);
+
+ EXPECT_EQ(map->size(), 2u); // Two different wrappings of '1' are distinct.
+
+ PreciselyCollectGarbage();
+ EXPECT_TRUE(map->Contains(one));
+ EXPECT_TRUE(map->Contains(another_one));
+
+ IntWrapper* gotten(map->at(one));
+ EXPECT_EQ(gotten->Value(), one->Value());
+ EXPECT_EQ(gotten, one);
+
+ size_t after_gc2 = heap.ObjectPayloadSizeForTesting();
+ EXPECT_EQ(after_gc2, after_add_and_gc);
+
+ IntWrapper* dozen = nullptr;
+
+ for (int i = 1; i < 1000; i++) { // 999 iterations.
+ IntWrapper* i_wrapper(IntWrapper::Create(i));
+ IntWrapper* i_squared(IntWrapper::Create(i * i));
+ map->insert(i_wrapper, i_squared);
+ if (i == 12)
+ dozen = i_wrapper;
+ }
+ size_t after_adding1000 = heap.ObjectPayloadSizeForTesting();
+ EXPECT_TRUE(after_adding1000 > after_gc2);
+
+ IntWrapper* gross(map->at(dozen));
+ EXPECT_EQ(gross->Value(), 144);
+
+ // This should clear out any junk backings created by all the adds.
+ PreciselyCollectGarbage();
+ size_t after_gc3 = heap.ObjectPayloadSizeForTesting();
+ EXPECT_TRUE(after_gc3 <= after_adding1000);
+ }
+
+ PreciselyCollectGarbage();
+ // The objects 'one', anotherOne, and the 999 other pairs.
+ EXPECT_EQ(IntWrapper::destructor_calls_, 2000);
+ size_t after_gc4 = heap.ObjectPayloadSizeForTesting();
+ EXPECT_EQ(after_gc4, initial_object_payload_size);
+}
+
+TEST(HeapTest, NestedAllocation) {
+ ThreadHeap& heap = ThreadState::Current()->Heap();
+ ClearOutOldGarbage();
+ size_t initial_object_payload_size = heap.ObjectPayloadSizeForTesting();
+ {
+ Persistent<ConstructorAllocation> constructor_allocation =
+ ConstructorAllocation::Create();
+ }
+ ClearOutOldGarbage();
+ size_t after_free = heap.ObjectPayloadSizeForTesting();
+ EXPECT_TRUE(initial_object_payload_size == after_free);
+}
+
+TEST(HeapTest, LargeHeapObjects) {
+ ThreadHeap& heap = ThreadState::Current()->Heap();
+ ClearOutOldGarbage();
+ size_t initial_object_payload_size = heap.ObjectPayloadSizeForTesting();
+ size_t initial_allocated_space = heap.HeapStats().AllocatedSpace();
+ IntWrapper::destructor_calls_ = 0;
+ LargeHeapObject::destructor_calls_ = 0;
+ {
+ int slack =
+ 8; // LargeHeapObject points to an IntWrapper that is also allocated.
+ Persistent<LargeHeapObject> object = LargeHeapObject::Create();
+#if DCHECK_IS_ON()
+ DCHECK(ThreadState::Current()->Heap().FindPageFromAddress(object));
+ DCHECK(ThreadState::Current()->Heap().FindPageFromAddress(
+ reinterpret_cast<char*>(object.Get()) + sizeof(LargeHeapObject) - 1));
+#endif
+ ClearOutOldGarbage();
+ size_t after_allocation = heap.HeapStats().AllocatedSpace();
+ {
+ object->Set(0, 'a');
+ EXPECT_EQ('a', object->Get(0));
+ object->Set(object->length() - 1, 'b');
+ EXPECT_EQ('b', object->Get(object->length() - 1));
+ size_t expected_large_heap_object_payload_size =
+ ThreadHeap::AllocationSizeFromSize(sizeof(LargeHeapObject));
+ size_t expected_object_payload_size =
+ expected_large_heap_object_payload_size + sizeof(IntWrapper);
+ size_t actual_object_payload_size =
+ heap.ObjectPayloadSizeForTesting() - initial_object_payload_size;
+ CheckWithSlack(expected_object_payload_size, actual_object_payload_size,
+ slack);
+ // There is probably space for the IntWrapper in a heap page without
+ // allocating extra pages. However, the IntWrapper allocation might cause
+ // the addition of a heap page.
+ size_t large_object_allocation_size =
+ sizeof(LargeObjectPage) + expected_large_heap_object_payload_size;
+ size_t allocated_space_lower_bound =
+ initial_allocated_space + large_object_allocation_size;
+ size_t allocated_space_upper_bound =
+ allocated_space_lower_bound + slack + kBlinkPageSize;
+ EXPECT_LE(allocated_space_lower_bound, after_allocation);
+ EXPECT_LE(after_allocation, allocated_space_upper_bound);
+ EXPECT_EQ(0, IntWrapper::destructor_calls_);
+ EXPECT_EQ(0, LargeHeapObject::destructor_calls_);
+ for (int i = 0; i < 10; i++)
+ object = LargeHeapObject::Create();
+ }
+ ClearOutOldGarbage();
+ EXPECT_TRUE(heap.HeapStats().AllocatedSpace() == after_allocation);
+ EXPECT_EQ(10, IntWrapper::destructor_calls_);
+ EXPECT_EQ(10, LargeHeapObject::destructor_calls_);
+ }
+ ClearOutOldGarbage();
+ EXPECT_TRUE(initial_object_payload_size ==
+ heap.ObjectPayloadSizeForTesting());
+ EXPECT_TRUE(initial_allocated_space == heap.HeapStats().AllocatedSpace());
+ EXPECT_EQ(11, IntWrapper::destructor_calls_);
+ EXPECT_EQ(11, LargeHeapObject::destructor_calls_);
+ PreciselyCollectGarbage();
+}
+
+TEST(HeapTest, LargeHashMap) {
+ ClearOutOldGarbage();
+ size_t size = (1 << 27) / sizeof(Member<IntWrapper>);
+ Persistent<HeapHashMap<int, Member<IntWrapper>>> map =
+ new HeapHashMap<int, Member<IntWrapper>>();
+ map->ReserveCapacityForSize(size);
+ EXPECT_LE(size, map->Capacity());
+}
+
+TEST(HeapTest, LargeVector) {
+ ClearOutOldGarbage();
+
+ size_t size = (1 << 27) / sizeof(int);
+ Persistent<HeapVector<int>> vector = new HeapVector<int>(size);
+ EXPECT_LE(size, vector->capacity());
+}
+
+typedef std::pair<Member<IntWrapper>, int> PairWrappedUnwrapped;
+typedef std::pair<int, Member<IntWrapper>> PairUnwrappedWrapped;
+typedef std::pair<WeakMember<IntWrapper>, Member<IntWrapper>> PairWeakStrong;
+typedef std::pair<Member<IntWrapper>, WeakMember<IntWrapper>> PairStrongWeak;
+typedef std::pair<WeakMember<IntWrapper>, int> PairWeakUnwrapped;
+typedef std::pair<int, WeakMember<IntWrapper>> PairUnwrappedWeak;
+
+class Container : public GarbageCollected<Container> {
+ public:
+ static Container* Create() { return new Container(); }
+ HeapHashMap<Member<IntWrapper>, Member<IntWrapper>> map;
+ HeapHashSet<Member<IntWrapper>> set;
+ HeapHashSet<Member<IntWrapper>> set2;
+ HeapHashCountedSet<Member<IntWrapper>> set3;
+ HeapVector<Member<IntWrapper>, 2> vector;
+ HeapVector<PairWrappedUnwrapped, 2> vector_wu;
+ HeapVector<PairUnwrappedWrapped, 2> vector_uw;
+ HeapDeque<Member<IntWrapper>, 0> deque;
+ HeapDeque<PairWrappedUnwrapped, 0> deque_wu;
+ HeapDeque<PairUnwrappedWrapped, 0> deque_uw;
+ void Trace(blink::Visitor* visitor) {
+ visitor->Trace(map);
+ visitor->Trace(set);
+ visitor->Trace(set2);
+ visitor->Trace(set3);
+ visitor->Trace(vector);
+ visitor->Trace(vector_wu);
+ visitor->Trace(vector_uw);
+ visitor->Trace(deque);
+ visitor->Trace(deque_wu);
+ visitor->Trace(deque_uw);
+ }
+};
+
+struct NeedsTracingTrait {
+ explicit NeedsTracingTrait(IntWrapper* wrapper) : wrapper_(wrapper) {}
+ void Trace(blink::Visitor* visitor) { visitor->Trace(wrapper_); }
+ Member<IntWrapper> wrapper_;
+};
+
+TEST(HeapTest, HeapVectorFilledWithValue) {
+ IntWrapper* val = IntWrapper::Create(1);
+ HeapVector<Member<IntWrapper>> vector(10, val);
+ EXPECT_EQ(10u, vector.size());
+ for (size_t i = 0; i < vector.size(); i++)
+ EXPECT_EQ(val, vector[i]);
+}
+
+TEST(HeapTest, HeapVectorWithInlineCapacity) {
+ IntWrapper* one = IntWrapper::Create(1);
+ IntWrapper* two = IntWrapper::Create(2);
+ IntWrapper* three = IntWrapper::Create(3);
+ IntWrapper* four = IntWrapper::Create(4);
+ IntWrapper* five = IntWrapper::Create(5);
+ IntWrapper* six = IntWrapper::Create(6);
+ {
+ HeapVector<Member<IntWrapper>, 2> vector;
+ vector.push_back(one);
+ vector.push_back(two);
+ ConservativelyCollectGarbage();
+ EXPECT_TRUE(vector.Contains(one));
+ EXPECT_TRUE(vector.Contains(two));
+
+ vector.push_back(three);
+ vector.push_back(four);
+ ConservativelyCollectGarbage();
+ EXPECT_TRUE(vector.Contains(one));
+ EXPECT_TRUE(vector.Contains(two));
+ EXPECT_TRUE(vector.Contains(three));
+ EXPECT_TRUE(vector.Contains(four));
+
+ vector.Shrink(1);
+ ConservativelyCollectGarbage();
+ EXPECT_TRUE(vector.Contains(one));
+ EXPECT_FALSE(vector.Contains(two));
+ EXPECT_FALSE(vector.Contains(three));
+ EXPECT_FALSE(vector.Contains(four));
+ }
+ {
+ HeapVector<Member<IntWrapper>, 2> vector1;
+ HeapVector<Member<IntWrapper>, 2> vector2;
+
+ vector1.push_back(one);
+ vector2.push_back(two);
+ vector1.swap(vector2);
+ ConservativelyCollectGarbage();
+ EXPECT_TRUE(vector1.Contains(two));
+ EXPECT_TRUE(vector2.Contains(one));
+ }
+ {
+ HeapVector<Member<IntWrapper>, 2> vector1;
+ HeapVector<Member<IntWrapper>, 2> vector2;
+
+ vector1.push_back(one);
+ vector1.push_back(two);
+ vector2.push_back(three);
+ vector2.push_back(four);
+ vector2.push_back(five);
+ vector2.push_back(six);
+ vector1.swap(vector2);
+ ConservativelyCollectGarbage();
+ EXPECT_TRUE(vector1.Contains(three));
+ EXPECT_TRUE(vector1.Contains(four));
+ EXPECT_TRUE(vector1.Contains(five));
+ EXPECT_TRUE(vector1.Contains(six));
+ EXPECT_TRUE(vector2.Contains(one));
+ EXPECT_TRUE(vector2.Contains(two));
+ }
+}
+
+TEST(HeapTest, HeapVectorShrinkCapacity) {
+ ClearOutOldGarbage();
+ HeapVector<Member<IntWrapper>> vector1;
+ HeapVector<Member<IntWrapper>> vector2;
+ vector1.ReserveCapacity(96);
+ EXPECT_LE(96u, vector1.capacity());
+ vector1.Grow(vector1.capacity());
+
+ // Assumes none was allocated just after a vector backing of vector1.
+ vector1.Shrink(56);
+ vector1.ShrinkToFit();
+ EXPECT_GT(96u, vector1.capacity());
+
+ vector2.ReserveCapacity(20);
+ // Assumes another vector backing was allocated just after the vector
+ // backing of vector1.
+ vector1.Shrink(10);
+ vector1.ShrinkToFit();
+ EXPECT_GT(56u, vector1.capacity());
+
+ vector1.Grow(192);
+ EXPECT_LE(192u, vector1.capacity());
+}
+
+TEST(HeapTest, HeapVectorShrinkInlineCapacity) {
+ ClearOutOldGarbage();
+ const size_t kInlineCapacity = 64;
+ HeapVector<Member<IntWrapper>, kInlineCapacity> vector1;
+ vector1.ReserveCapacity(128);
+ EXPECT_LE(128u, vector1.capacity());
+ vector1.Grow(vector1.capacity());
+
+ // Shrink the external buffer.
+ vector1.Shrink(90);
+ vector1.ShrinkToFit();
+ EXPECT_GT(128u, vector1.capacity());
+
+// TODO(sof): if the ASan support for 'contiguous containers' is enabled,
+// Vector inline buffers are disabled; that constraint should be attempted
+// removed, but until that time, disable testing handling of capacities
+// of inline buffers.
+#if !defined(ANNOTATE_CONTIGUOUS_CONTAINER)
+ // Shrinking switches the buffer from the external one to the inline one.
+ vector1.Shrink(kInlineCapacity - 1);
+ vector1.ShrinkToFit();
+ EXPECT_EQ(kInlineCapacity, vector1.capacity());
+
+ // Try to shrink the inline buffer.
+ vector1.Shrink(1);
+ vector1.ShrinkToFit();
+ EXPECT_EQ(kInlineCapacity, vector1.capacity());
+#endif
+}
+
+TEST(HeapTest, HeapVectorOnStackLargeObjectPageSized) {
+ ClearOutOldGarbage();
+ // Try to allocate a vector of a size that will end exactly where the
+ // LargeObjectPage ends.
+ using Container = HeapVector<Member<IntWrapper>>;
+ Container vector;
+ size_t size = (kLargeObjectSizeThreshold + kBlinkGuardPageSize -
+ LargeObjectPage::PageHeaderSize() - sizeof(HeapObjectHeader)) /
+ sizeof(Container::ValueType);
+ vector.ReserveCapacity(size);
+ for (unsigned i = 0; i < size; ++i)
+ vector.push_back(IntWrapper::Create(i));
+ ConservativelyCollectGarbage();
+}
+
+template <typename T, size_t inlineCapacity, typename U>
+bool DequeContains(HeapDeque<T, inlineCapacity>& deque, U u) {
+ typedef typename HeapDeque<T, inlineCapacity>::iterator iterator;
+ for (iterator it = deque.begin(); it != deque.end(); ++it) {
+ if (*it == u)
+ return true;
+ }
+ return false;
+}
+
+TEST(HeapTest, HeapCollectionTypes) {
+ IntWrapper::destructor_calls_ = 0;
+
+ typedef HeapHashMap<Member<IntWrapper>, Member<IntWrapper>> MemberMember;
+ typedef HeapHashMap<Member<IntWrapper>, int> MemberPrimitive;
+ typedef HeapHashMap<int, Member<IntWrapper>> PrimitiveMember;
+
+ typedef HeapHashSet<Member<IntWrapper>> MemberSet;
+ typedef HeapHashCountedSet<Member<IntWrapper>> MemberCountedSet;
+
+ typedef HeapVector<Member<IntWrapper>, 2> MemberVector;
+ typedef HeapDeque<Member<IntWrapper>, 0> MemberDeque;
+
+ typedef HeapVector<PairWrappedUnwrapped, 2> VectorWU;
+ typedef HeapVector<PairUnwrappedWrapped, 2> VectorUW;
+ typedef HeapDeque<PairWrappedUnwrapped, 0> DequeWU;
+ typedef HeapDeque<PairUnwrappedWrapped, 0> DequeUW;
+
+ Persistent<MemberMember> member_member = new MemberMember();
+ Persistent<MemberMember> member_member2 = new MemberMember();
+ Persistent<MemberMember> member_member3 = new MemberMember();
+ Persistent<MemberPrimitive> member_primitive = new MemberPrimitive();
+ Persistent<PrimitiveMember> primitive_member = new PrimitiveMember();
+ Persistent<MemberSet> set = new MemberSet();
+ Persistent<MemberSet> set2 = new MemberSet();
+ Persistent<MemberCountedSet> set3 = new MemberCountedSet();
+ Persistent<MemberVector> vector = new MemberVector();
+ Persistent<MemberVector> vector2 = new MemberVector();
+ Persistent<VectorWU> vector_wu = new VectorWU();
+ Persistent<VectorWU> vector_wu2 = new VectorWU();
+ Persistent<VectorUW> vector_uw = new VectorUW();
+ Persistent<VectorUW> vector_uw2 = new VectorUW();
+ Persistent<MemberDeque> deque = new MemberDeque();
+ Persistent<MemberDeque> deque2 = new MemberDeque();
+ Persistent<DequeWU> deque_wu = new DequeWU();
+ Persistent<DequeWU> deque_wu2 = new DequeWU();
+ Persistent<DequeUW> deque_uw = new DequeUW();
+ Persistent<DequeUW> deque_uw2 = new DequeUW();
+ Persistent<Container> container = Container::Create();
+
+ ClearOutOldGarbage();
+ {
+ Persistent<IntWrapper> one(IntWrapper::Create(1));
+ Persistent<IntWrapper> two(IntWrapper::Create(2));
+ Persistent<IntWrapper> one_b(IntWrapper::Create(1));
+ Persistent<IntWrapper> two_b(IntWrapper::Create(2));
+ Persistent<IntWrapper> one_c(IntWrapper::Create(1));
+ Persistent<IntWrapper> one_d(IntWrapper::Create(1));
+ Persistent<IntWrapper> one_e(IntWrapper::Create(1));
+ Persistent<IntWrapper> one_f(IntWrapper::Create(1));
+ {
+ IntWrapper* three_b(IntWrapper::Create(3));
+ IntWrapper* three_c(IntWrapper::Create(3));
+ IntWrapper* three_d(IntWrapper::Create(3));
+ IntWrapper* three_e(IntWrapper::Create(3));
+ IntWrapper* three_f(IntWrapper::Create(3));
+ IntWrapper* three(IntWrapper::Create(3));
+ IntWrapper* four_b(IntWrapper::Create(4));
+ IntWrapper* four_c(IntWrapper::Create(4));
+ IntWrapper* four_d(IntWrapper::Create(4));
+ IntWrapper* four_e(IntWrapper::Create(4));
+ IntWrapper* four_f(IntWrapper::Create(4));
+ IntWrapper* four(IntWrapper::Create(4));
+ IntWrapper* five_c(IntWrapper::Create(5));
+ IntWrapper* five_d(IntWrapper::Create(5));
+ IntWrapper* five_e(IntWrapper::Create(5));
+ IntWrapper* five_f(IntWrapper::Create(5));
+
+ // Member Collections.
+ member_member2->insert(one, two);
+ member_member2->insert(two, three);
+ member_member2->insert(three, four);
+ member_member2->insert(four, one);
+ primitive_member->insert(1, two);
+ primitive_member->insert(2, three);
+ primitive_member->insert(3, four);
+ primitive_member->insert(4, one);
+ member_primitive->insert(one, 2);
+ member_primitive->insert(two, 3);
+ member_primitive->insert(three, 4);
+ member_primitive->insert(four, 1);
+ set2->insert(one);
+ set2->insert(two);
+ set2->insert(three);
+ set2->insert(four);
+ set->insert(one_b);
+ set3->insert(one_b);
+ set3->insert(one_b);
+ vector->push_back(one_b);
+ deque->push_back(one_b);
+ vector2->push_back(three_b);
+ vector2->push_back(four_b);
+ deque2->push_back(three_e);
+ deque2->push_back(four_e);
+ vector_wu->push_back(PairWrappedUnwrapped(&*one_c, 42));
+ deque_wu->push_back(PairWrappedUnwrapped(&*one_e, 42));
+ vector_wu2->push_back(PairWrappedUnwrapped(&*three_c, 43));
+ vector_wu2->push_back(PairWrappedUnwrapped(&*four_c, 44));
+ vector_wu2->push_back(PairWrappedUnwrapped(&*five_c, 45));
+ deque_wu2->push_back(PairWrappedUnwrapped(&*three_e, 43));
+ deque_wu2->push_back(PairWrappedUnwrapped(&*four_e, 44));
+ deque_wu2->push_back(PairWrappedUnwrapped(&*five_e, 45));
+ vector_uw->push_back(PairUnwrappedWrapped(1, &*one_d));
+ vector_uw2->push_back(PairUnwrappedWrapped(103, &*three_d));
+ vector_uw2->push_back(PairUnwrappedWrapped(104, &*four_d));
+ vector_uw2->push_back(PairUnwrappedWrapped(105, &*five_d));
+ deque_uw->push_back(PairUnwrappedWrapped(1, &*one_f));
+ deque_uw2->push_back(PairUnwrappedWrapped(103, &*three_f));
+ deque_uw2->push_back(PairUnwrappedWrapped(104, &*four_f));
+ deque_uw2->push_back(PairUnwrappedWrapped(105, &*five_f));
+
+ EXPECT_TRUE(DequeContains(*deque, one_b));
+
+ // Collect garbage. This should change nothing since we are keeping
+ // alive the IntWrapper objects with on-stack pointers.
+ ConservativelyCollectGarbage();
+
+ EXPECT_TRUE(DequeContains(*deque, one_b));
+
+ EXPECT_EQ(0u, member_member->size());
+ EXPECT_EQ(4u, member_member2->size());
+ EXPECT_EQ(4u, primitive_member->size());
+ EXPECT_EQ(4u, member_primitive->size());
+ EXPECT_EQ(1u, set->size());
+ EXPECT_EQ(4u, set2->size());
+ EXPECT_EQ(1u, set3->size());
+ EXPECT_EQ(1u, vector->size());
+ EXPECT_EQ(2u, vector2->size());
+ EXPECT_EQ(1u, vector_wu->size());
+ EXPECT_EQ(3u, vector_wu2->size());
+ EXPECT_EQ(1u, vector_uw->size());
+ EXPECT_EQ(3u, vector_uw2->size());
+ EXPECT_EQ(1u, deque->size());
+ EXPECT_EQ(2u, deque2->size());
+ EXPECT_EQ(1u, deque_wu->size());
+ EXPECT_EQ(3u, deque_wu2->size());
+ EXPECT_EQ(1u, deque_uw->size());
+ EXPECT_EQ(3u, deque_uw2->size());
+
+ MemberVector& cvec = container->vector;
+ cvec.swap(*vector.Get());
+ vector2->swap(cvec);
+ vector->swap(cvec);
+
+ VectorWU& cvec_wu = container->vector_wu;
+ cvec_wu.swap(*vector_wu.Get());
+ vector_wu2->swap(cvec_wu);
+ vector_wu->swap(cvec_wu);
+
+ VectorUW& cvec_uw = container->vector_uw;
+ cvec_uw.swap(*vector_uw.Get());
+ vector_uw2->swap(cvec_uw);
+ vector_uw->swap(cvec_uw);
+
+ MemberDeque& c_deque = container->deque;
+ c_deque.Swap(*deque.Get());
+ deque2->Swap(c_deque);
+ deque->Swap(c_deque);
+
+ DequeWU& c_deque_wu = container->deque_wu;
+ c_deque_wu.Swap(*deque_wu.Get());
+ deque_wu2->Swap(c_deque_wu);
+ deque_wu->Swap(c_deque_wu);
+
+ DequeUW& c_deque_uw = container->deque_uw;
+ c_deque_uw.Swap(*deque_uw.Get());
+ deque_uw2->Swap(c_deque_uw);
+ deque_uw->Swap(c_deque_uw);
+
+ // Swap set and set2 in a roundabout way.
+ MemberSet& cset1 = container->set;
+ MemberSet& cset2 = container->set2;
+ set->swap(cset1);
+ set2->swap(cset2);
+ set->swap(cset2);
+ cset1.swap(cset2);
+ cset2.swap(*set2);
+
+ MemberCountedSet& c_counted_set = container->set3;
+ set3->swap(c_counted_set);
+ EXPECT_EQ(0u, set3->size());
+ set3->swap(c_counted_set);
+
+ // Triple swap.
+ container->map.swap(*member_member2);
+ MemberMember& contained_map = container->map;
+ member_member3->swap(contained_map);
+ member_member3->swap(*member_member);
+
+ EXPECT_TRUE(member_member->at(one) == two);
+ EXPECT_TRUE(member_member->at(two) == three);
+ EXPECT_TRUE(member_member->at(three) == four);
+ EXPECT_TRUE(member_member->at(four) == one);
+ EXPECT_TRUE(primitive_member->at(1) == two);
+ EXPECT_TRUE(primitive_member->at(2) == three);
+ EXPECT_TRUE(primitive_member->at(3) == four);
+ EXPECT_TRUE(primitive_member->at(4) == one);
+ EXPECT_EQ(1, member_primitive->at(four));
+ EXPECT_EQ(2, member_primitive->at(one));
+ EXPECT_EQ(3, member_primitive->at(two));
+ EXPECT_EQ(4, member_primitive->at(three));
+ EXPECT_TRUE(set->Contains(one));
+ EXPECT_TRUE(set->Contains(two));
+ EXPECT_TRUE(set->Contains(three));
+ EXPECT_TRUE(set->Contains(four));
+ EXPECT_TRUE(set2->Contains(one_b));
+ EXPECT_TRUE(set3->Contains(one_b));
+ EXPECT_TRUE(vector->Contains(three_b));
+ EXPECT_TRUE(vector->Contains(four_b));
+ EXPECT_TRUE(DequeContains(*deque, three_e));
+ EXPECT_TRUE(DequeContains(*deque, four_e));
+ EXPECT_TRUE(vector2->Contains(one_b));
+ EXPECT_FALSE(vector2->Contains(three_b));
+ EXPECT_TRUE(DequeContains(*deque2, one_b));
+ EXPECT_FALSE(DequeContains(*deque2, three_e));
+ EXPECT_TRUE(vector_wu->Contains(PairWrappedUnwrapped(&*three_c, 43)));
+ EXPECT_TRUE(vector_wu->Contains(PairWrappedUnwrapped(&*four_c, 44)));
+ EXPECT_TRUE(vector_wu->Contains(PairWrappedUnwrapped(&*five_c, 45)));
+ EXPECT_TRUE(vector_wu2->Contains(PairWrappedUnwrapped(&*one_c, 42)));
+ EXPECT_FALSE(vector_wu2->Contains(PairWrappedUnwrapped(&*three_c, 43)));
+ EXPECT_TRUE(vector_uw->Contains(PairUnwrappedWrapped(103, &*three_d)));
+ EXPECT_TRUE(vector_uw->Contains(PairUnwrappedWrapped(104, &*four_d)));
+ EXPECT_TRUE(vector_uw->Contains(PairUnwrappedWrapped(105, &*five_d)));
+ EXPECT_TRUE(vector_uw2->Contains(PairUnwrappedWrapped(1, &*one_d)));
+ EXPECT_FALSE(vector_uw2->Contains(PairUnwrappedWrapped(103, &*three_d)));
+ EXPECT_TRUE(
+ DequeContains(*deque_wu, PairWrappedUnwrapped(&*three_e, 43)));
+ EXPECT_TRUE(DequeContains(*deque_wu, PairWrappedUnwrapped(&*four_e, 44)));
+ EXPECT_TRUE(DequeContains(*deque_wu, PairWrappedUnwrapped(&*five_e, 45)));
+ EXPECT_TRUE(DequeContains(*deque_wu2, PairWrappedUnwrapped(&*one_e, 42)));
+ EXPECT_FALSE(
+ DequeContains(*deque_wu2, PairWrappedUnwrapped(&*three_e, 43)));
+ EXPECT_TRUE(
+ DequeContains(*deque_uw, PairUnwrappedWrapped(103, &*three_f)));
+ EXPECT_TRUE(
+ DequeContains(*deque_uw, PairUnwrappedWrapped(104, &*four_f)));
+ EXPECT_TRUE(
+ DequeContains(*deque_uw, PairUnwrappedWrapped(105, &*five_f)));
+ EXPECT_TRUE(DequeContains(*deque_uw2, PairUnwrappedWrapped(1, &*one_f)));
+ EXPECT_FALSE(
+ DequeContains(*deque_uw2, PairUnwrappedWrapped(103, &*three_f)));
+ }
+
+ PreciselyCollectGarbage();
+
+ EXPECT_EQ(4u, member_member->size());
+ EXPECT_EQ(0u, member_member2->size());
+ EXPECT_EQ(4u, primitive_member->size());
+ EXPECT_EQ(4u, member_primitive->size());
+ EXPECT_EQ(4u, set->size());
+ EXPECT_EQ(1u, set2->size());
+ EXPECT_EQ(1u, set3->size());
+ EXPECT_EQ(2u, vector->size());
+ EXPECT_EQ(1u, vector2->size());
+ EXPECT_EQ(3u, vector_uw->size());
+ EXPECT_EQ(1u, vector2->size());
+ EXPECT_EQ(2u, deque->size());
+ EXPECT_EQ(1u, deque2->size());
+ EXPECT_EQ(3u, deque_uw->size());
+ EXPECT_EQ(1u, deque2->size());
+
+ EXPECT_TRUE(member_member->at(one) == two);
+ EXPECT_TRUE(primitive_member->at(1) == two);
+ EXPECT_TRUE(primitive_member->at(4) == one);
+ EXPECT_EQ(2, member_primitive->at(one));
+ EXPECT_EQ(3, member_primitive->at(two));
+ EXPECT_TRUE(set->Contains(one));
+ EXPECT_TRUE(set->Contains(two));
+ EXPECT_FALSE(set->Contains(one_b));
+ EXPECT_TRUE(set2->Contains(one_b));
+ EXPECT_TRUE(set3->Contains(one_b));
+ EXPECT_EQ(2u, set3->find(one_b)->value);
+ EXPECT_EQ(3, vector->at(0)->Value());
+ EXPECT_EQ(4, vector->at(1)->Value());
+ EXPECT_EQ(3, deque->begin()->Get()->Value());
+ }
+
+ PreciselyCollectGarbage();
+ PreciselyCollectGarbage();
+
+ EXPECT_EQ(4u, member_member->size());
+ EXPECT_EQ(4u, primitive_member->size());
+ EXPECT_EQ(4u, member_primitive->size());
+ EXPECT_EQ(4u, set->size());
+ EXPECT_EQ(1u, set2->size());
+ EXPECT_EQ(2u, vector->size());
+ EXPECT_EQ(1u, vector2->size());
+ EXPECT_EQ(3u, vector_wu->size());
+ EXPECT_EQ(1u, vector_wu2->size());
+ EXPECT_EQ(3u, vector_uw->size());
+ EXPECT_EQ(1u, vector_uw2->size());
+ EXPECT_EQ(2u, deque->size());
+ EXPECT_EQ(1u, deque2->size());
+ EXPECT_EQ(3u, deque_wu->size());
+ EXPECT_EQ(1u, deque_wu2->size());
+ EXPECT_EQ(3u, deque_uw->size());
+ EXPECT_EQ(1u, deque_uw2->size());
+}
+
+TEST(HeapTest, PersistentVector) {
+ IntWrapper::destructor_calls_ = 0;
+
+ typedef Vector<Persistent<IntWrapper>> PersistentVector;
+
+ Persistent<IntWrapper> one(IntWrapper::Create(1));
+ Persistent<IntWrapper> two(IntWrapper::Create(2));
+ Persistent<IntWrapper> three(IntWrapper::Create(3));
+ Persistent<IntWrapper> four(IntWrapper::Create(4));
+ Persistent<IntWrapper> five(IntWrapper::Create(5));
+ Persistent<IntWrapper> six(IntWrapper::Create(6));
+ {
+ PersistentVector vector;
+ vector.push_back(one);
+ vector.push_back(two);
+ ConservativelyCollectGarbage();
+ EXPECT_TRUE(vector.Contains(one));
+ EXPECT_TRUE(vector.Contains(two));
+
+ vector.push_back(three);
+ vector.push_back(four);
+ ConservativelyCollectGarbage();
+ EXPECT_TRUE(vector.Contains(one));
+ EXPECT_TRUE(vector.Contains(two));
+ EXPECT_TRUE(vector.Contains(three));
+ EXPECT_TRUE(vector.Contains(four));
+
+ vector.Shrink(1);
+ ConservativelyCollectGarbage();
+ EXPECT_TRUE(vector.Contains(one));
+ EXPECT_FALSE(vector.Contains(two));
+ EXPECT_FALSE(vector.Contains(three));
+ EXPECT_FALSE(vector.Contains(four));
+ }
+ {
+ PersistentVector vector1;
+ PersistentVector vector2;
+
+ vector1.push_back(one);
+ vector2.push_back(two);
+ vector1.swap(vector2);
+ ConservativelyCollectGarbage();
+ EXPECT_TRUE(vector1.Contains(two));
+ EXPECT_TRUE(vector2.Contains(one));
+ }
+ {
+ PersistentVector vector1;
+ PersistentVector vector2;
+
+ vector1.push_back(one);
+ vector1.push_back(two);
+ vector2.push_back(three);
+ vector2.push_back(four);
+ vector2.push_back(five);
+ vector2.push_back(six);
+ vector1.swap(vector2);
+ ConservativelyCollectGarbage();
+ EXPECT_TRUE(vector1.Contains(three));
+ EXPECT_TRUE(vector1.Contains(four));
+ EXPECT_TRUE(vector1.Contains(five));
+ EXPECT_TRUE(vector1.Contains(six));
+ EXPECT_TRUE(vector2.Contains(one));
+ EXPECT_TRUE(vector2.Contains(two));
+ }
+}
+
+TEST(HeapTest, CrossThreadPersistentVector) {
+ IntWrapper::destructor_calls_ = 0;
+
+ typedef Vector<CrossThreadPersistent<IntWrapper>> CrossThreadPersistentVector;
+
+ CrossThreadPersistent<IntWrapper> one(IntWrapper::Create(1));
+ CrossThreadPersistent<IntWrapper> two(IntWrapper::Create(2));
+ CrossThreadPersistent<IntWrapper> three(IntWrapper::Create(3));
+ CrossThreadPersistent<IntWrapper> four(IntWrapper::Create(4));
+ CrossThreadPersistent<IntWrapper> five(IntWrapper::Create(5));
+ CrossThreadPersistent<IntWrapper> six(IntWrapper::Create(6));
+ {
+ CrossThreadPersistentVector vector;
+ vector.push_back(one);
+ vector.push_back(two);
+ ConservativelyCollectGarbage();
+ EXPECT_TRUE(vector.Contains(one));
+ EXPECT_TRUE(vector.Contains(two));
+
+ vector.push_back(three);
+ vector.push_back(four);
+ ConservativelyCollectGarbage();
+ EXPECT_TRUE(vector.Contains(one));
+ EXPECT_TRUE(vector.Contains(two));
+ EXPECT_TRUE(vector.Contains(three));
+ EXPECT_TRUE(vector.Contains(four));
+
+ vector.Shrink(1);
+ ConservativelyCollectGarbage();
+ EXPECT_TRUE(vector.Contains(one));
+ EXPECT_FALSE(vector.Contains(two));
+ EXPECT_FALSE(vector.Contains(three));
+ EXPECT_FALSE(vector.Contains(four));
+ }
+ {
+ CrossThreadPersistentVector vector1;
+ CrossThreadPersistentVector vector2;
+
+ vector1.push_back(one);
+ vector2.push_back(two);
+ vector1.swap(vector2);
+ ConservativelyCollectGarbage();
+ EXPECT_TRUE(vector1.Contains(two));
+ EXPECT_TRUE(vector2.Contains(one));
+ }
+ {
+ CrossThreadPersistentVector vector1;
+ CrossThreadPersistentVector vector2;
+
+ vector1.push_back(one);
+ vector1.push_back(two);
+ vector2.push_back(three);
+ vector2.push_back(four);
+ vector2.push_back(five);
+ vector2.push_back(six);
+ vector1.swap(vector2);
+ ConservativelyCollectGarbage();
+ EXPECT_TRUE(vector1.Contains(three));
+ EXPECT_TRUE(vector1.Contains(four));
+ EXPECT_TRUE(vector1.Contains(five));
+ EXPECT_TRUE(vector1.Contains(six));
+ EXPECT_TRUE(vector2.Contains(one));
+ EXPECT_TRUE(vector2.Contains(two));
+ }
+}
+
+TEST(HeapTest, PersistentSet) {
+ IntWrapper::destructor_calls_ = 0;
+
+ typedef HashSet<Persistent<IntWrapper>> PersistentSet;
+
+ IntWrapper* one_raw = IntWrapper::Create(1);
+ Persistent<IntWrapper> one(one_raw);
+ Persistent<IntWrapper> one2(one_raw);
+ Persistent<IntWrapper> two(IntWrapper::Create(2));
+ Persistent<IntWrapper> three(IntWrapper::Create(3));
+ Persistent<IntWrapper> four(IntWrapper::Create(4));
+ Persistent<IntWrapper> five(IntWrapper::Create(5));
+ Persistent<IntWrapper> six(IntWrapper::Create(6));
+ {
+ PersistentSet set;
+ set.insert(one);
+ set.insert(two);
+ ConservativelyCollectGarbage();
+ EXPECT_TRUE(set.Contains(one));
+ EXPECT_TRUE(set.Contains(one2));
+ EXPECT_TRUE(set.Contains(two));
+
+ set.insert(three);
+ set.insert(four);
+ ConservativelyCollectGarbage();
+ EXPECT_TRUE(set.Contains(one));
+ EXPECT_TRUE(set.Contains(two));
+ EXPECT_TRUE(set.Contains(three));
+ EXPECT_TRUE(set.Contains(four));
+
+ set.clear();
+ ConservativelyCollectGarbage();
+ EXPECT_FALSE(set.Contains(one));
+ EXPECT_FALSE(set.Contains(two));
+ EXPECT_FALSE(set.Contains(three));
+ EXPECT_FALSE(set.Contains(four));
+ }
+ {
+ PersistentSet set1;
+ PersistentSet set2;
+
+ set1.insert(one);
+ set2.insert(two);
+ set1.swap(set2);
+ ConservativelyCollectGarbage();
+ EXPECT_TRUE(set1.Contains(two));
+ EXPECT_TRUE(set2.Contains(one));
+ EXPECT_TRUE(set2.Contains(one2));
+ }
+}
+
+TEST(HeapTest, CrossThreadPersistentSet) {
+ IntWrapper::destructor_calls_ = 0;
+
+ typedef HashSet<CrossThreadPersistent<IntWrapper>> CrossThreadPersistentSet;
+
+ IntWrapper* one_raw = IntWrapper::Create(1);
+ CrossThreadPersistent<IntWrapper> one(one_raw);
+ CrossThreadPersistent<IntWrapper> one2(one_raw);
+ CrossThreadPersistent<IntWrapper> two(IntWrapper::Create(2));
+ CrossThreadPersistent<IntWrapper> three(IntWrapper::Create(3));
+ CrossThreadPersistent<IntWrapper> four(IntWrapper::Create(4));
+ CrossThreadPersistent<IntWrapper> five(IntWrapper::Create(5));
+ CrossThreadPersistent<IntWrapper> six(IntWrapper::Create(6));
+ {
+ CrossThreadPersistentSet set;
+ set.insert(one);
+ set.insert(two);
+ ConservativelyCollectGarbage();
+ EXPECT_TRUE(set.Contains(one));
+ EXPECT_TRUE(set.Contains(one2));
+ EXPECT_TRUE(set.Contains(two));
+
+ set.insert(three);
+ set.insert(four);
+ ConservativelyCollectGarbage();
+ EXPECT_TRUE(set.Contains(one));
+ EXPECT_TRUE(set.Contains(two));
+ EXPECT_TRUE(set.Contains(three));
+ EXPECT_TRUE(set.Contains(four));
+
+ set.clear();
+ ConservativelyCollectGarbage();
+ EXPECT_FALSE(set.Contains(one));
+ EXPECT_FALSE(set.Contains(two));
+ EXPECT_FALSE(set.Contains(three));
+ EXPECT_FALSE(set.Contains(four));
+ }
+ {
+ CrossThreadPersistentSet set1;
+ CrossThreadPersistentSet set2;
+
+ set1.insert(one);
+ set2.insert(two);
+ set1.swap(set2);
+ ConservativelyCollectGarbage();
+ EXPECT_TRUE(set1.Contains(two));
+ EXPECT_TRUE(set2.Contains(one));
+ EXPECT_TRUE(set2.Contains(one2));
+ }
+}
+
+class NonTrivialObject final {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ NonTrivialObject() = default;
+ explicit NonTrivialObject(int num) {
+ deque_.push_back(IntWrapper::Create(num));
+ vector_.push_back(IntWrapper::Create(num));
+ }
+ void Trace(blink::Visitor* visitor) {
+ visitor->Trace(deque_);
+ visitor->Trace(vector_);
+ }
+
+ private:
+ HeapDeque<Member<IntWrapper>> deque_;
+ HeapVector<Member<IntWrapper>> vector_;
+};
+
+TEST(HeapTest, HeapHashMapWithInlinedObject) {
+ HeapHashMap<int, NonTrivialObject> map;
+ for (int num = 1; num < 1000; num++) {
+ NonTrivialObject object(num);
+ map.insert(num, object);
+ }
+}
+
+template <typename T>
+void MapIteratorCheck(T& it, const T& end, int expected) {
+ int found = 0;
+ while (it != end) {
+ found++;
+ int key = it->key->Value();
+ int value = it->value->Value();
+ EXPECT_TRUE(key >= 0 && key < 1100);
+ EXPECT_TRUE(value >= 0 && value < 1100);
+ ++it;
+ }
+ EXPECT_EQ(expected, found);
+}
+
+template <typename T>
+void SetIteratorCheck(T& it, const T& end, int expected) {
+ int found = 0;
+ while (it != end) {
+ found++;
+ int value = (*it)->Value();
+ EXPECT_TRUE(value >= 0 && value < 1100);
+ ++it;
+ }
+ EXPECT_EQ(expected, found);
+}
+
+TEST(HeapTest, HeapWeakCollectionSimple) {
+ ClearOutOldGarbage();
+ IntWrapper::destructor_calls_ = 0;
+
+ PersistentHeapVector<Member<IntWrapper>> keep_numbers_alive;
+
+ typedef HeapHashMap<WeakMember<IntWrapper>, Member<IntWrapper>> WeakStrong;
+ typedef HeapHashMap<Member<IntWrapper>, WeakMember<IntWrapper>> StrongWeak;
+ typedef HeapHashMap<WeakMember<IntWrapper>, WeakMember<IntWrapper>> WeakWeak;
+ typedef HeapHashSet<WeakMember<IntWrapper>> WeakSet;
+ typedef HeapHashCountedSet<WeakMember<IntWrapper>> WeakCountedSet;
+
+ Persistent<WeakStrong> weak_strong = new WeakStrong();
+ Persistent<StrongWeak> strong_weak = new StrongWeak();
+ Persistent<WeakWeak> weak_weak = new WeakWeak();
+ Persistent<WeakSet> weak_set = new WeakSet();
+ Persistent<WeakCountedSet> weak_counted_set = new WeakCountedSet();
+
+ Persistent<IntWrapper> two = IntWrapper::Create(2);
+
+ keep_numbers_alive.push_back(IntWrapper::Create(103));
+ keep_numbers_alive.push_back(IntWrapper::Create(10));
+
+ {
+ weak_strong->insert(IntWrapper::Create(1), two);
+ strong_weak->insert(two, IntWrapper::Create(1));
+ weak_weak->insert(two, IntWrapper::Create(42));
+ weak_weak->insert(IntWrapper::Create(42), two);
+ weak_set->insert(IntWrapper::Create(0));
+ weak_set->insert(two);
+ weak_set->insert(keep_numbers_alive[0]);
+ weak_set->insert(keep_numbers_alive[1]);
+ weak_counted_set->insert(IntWrapper::Create(0));
+ weak_counted_set->insert(two);
+ weak_counted_set->insert(two);
+ weak_counted_set->insert(two);
+ weak_counted_set->insert(keep_numbers_alive[0]);
+ weak_counted_set->insert(keep_numbers_alive[1]);
+ EXPECT_EQ(1u, weak_strong->size());
+ EXPECT_EQ(1u, strong_weak->size());
+ EXPECT_EQ(2u, weak_weak->size());
+ EXPECT_EQ(4u, weak_set->size());
+ EXPECT_EQ(4u, weak_counted_set->size());
+ EXPECT_EQ(3u, weak_counted_set->find(two)->value);
+ weak_counted_set->erase(two);
+ EXPECT_EQ(2u, weak_counted_set->find(two)->value);
+ }
+
+ keep_numbers_alive[0] = nullptr;
+
+ PreciselyCollectGarbage();
+
+ EXPECT_EQ(0u, weak_strong->size());
+ EXPECT_EQ(0u, strong_weak->size());
+ EXPECT_EQ(0u, weak_weak->size());
+ EXPECT_EQ(2u, weak_set->size());
+ EXPECT_EQ(2u, weak_counted_set->size());
+}
+
+template <typename Set>
+void OrderedSetHelper(bool strong) {
+ ClearOutOldGarbage();
+ IntWrapper::destructor_calls_ = 0;
+
+ PersistentHeapVector<Member<IntWrapper>> keep_numbers_alive;
+
+ Persistent<Set> set1 = new Set();
+ Persistent<Set> set2 = new Set();
+
+ const Set& const_set = *set1.Get();
+
+ keep_numbers_alive.push_back(IntWrapper::Create(2));
+ keep_numbers_alive.push_back(IntWrapper::Create(103));
+ keep_numbers_alive.push_back(IntWrapper::Create(10));
+
+ set1->insert(IntWrapper::Create(0));
+ set1->insert(keep_numbers_alive[0]);
+ set1->insert(keep_numbers_alive[1]);
+ set1->insert(keep_numbers_alive[2]);
+
+ set2->clear();
+ set2->insert(IntWrapper::Create(42));
+ set2->clear();
+
+ EXPECT_EQ(4u, set1->size());
+ typename Set::iterator it(set1->begin());
+ typename Set::reverse_iterator reverse(set1->rbegin());
+ typename Set::const_iterator cit(const_set.begin());
+ typename Set::const_reverse_iterator creverse(const_set.rbegin());
+
+ EXPECT_EQ(0, (*it)->Value());
+ EXPECT_EQ(0, (*cit)->Value());
+ ++it;
+ ++cit;
+ EXPECT_EQ(2, (*it)->Value());
+ EXPECT_EQ(2, (*cit)->Value());
+ --it;
+ --cit;
+ EXPECT_EQ(0, (*it)->Value());
+ EXPECT_EQ(0, (*cit)->Value());
+ ++it;
+ ++cit;
+ ++it;
+ ++cit;
+ EXPECT_EQ(103, (*it)->Value());
+ EXPECT_EQ(103, (*cit)->Value());
+ ++it;
+ ++cit;
+ EXPECT_EQ(10, (*it)->Value());
+ EXPECT_EQ(10, (*cit)->Value());
+ ++it;
+ ++cit;
+
+ EXPECT_EQ(10, (*reverse)->Value());
+ EXPECT_EQ(10, (*creverse)->Value());
+ ++reverse;
+ ++creverse;
+ EXPECT_EQ(103, (*reverse)->Value());
+ EXPECT_EQ(103, (*creverse)->Value());
+ --reverse;
+ --creverse;
+ EXPECT_EQ(10, (*reverse)->Value());
+ EXPECT_EQ(10, (*creverse)->Value());
+ ++reverse;
+ ++creverse;
+ ++reverse;
+ ++creverse;
+ EXPECT_EQ(2, (*reverse)->Value());
+ EXPECT_EQ(2, (*creverse)->Value());
+ ++reverse;
+ ++creverse;
+ EXPECT_EQ(0, (*reverse)->Value());
+ EXPECT_EQ(0, (*creverse)->Value());
+ ++reverse;
+ ++creverse;
+
+ EXPECT_EQ(set1->end(), it);
+ EXPECT_EQ(const_set.end(), cit);
+ EXPECT_EQ(set1->rend(), reverse);
+ EXPECT_EQ(const_set.rend(), creverse);
+
+ typename Set::iterator i_x(set2->begin());
+ EXPECT_EQ(set2->end(), i_x);
+
+ if (strong)
+ set1->erase(keep_numbers_alive[0]);
+
+ keep_numbers_alive[0] = nullptr;
+
+ PreciselyCollectGarbage();
+
+ EXPECT_EQ(2u + (strong ? 1u : 0u), set1->size());
+
+ EXPECT_EQ(2 + (strong ? 0 : 1), IntWrapper::destructor_calls_);
+
+ typename Set::iterator i2(set1->begin());
+ if (strong) {
+ EXPECT_EQ(0, (*i2)->Value());
+ ++i2;
+ EXPECT_NE(set1->end(), i2);
+ }
+ EXPECT_EQ(103, (*i2)->Value());
+ ++i2;
+ EXPECT_NE(set1->end(), i2);
+ EXPECT_EQ(10, (*i2)->Value());
+ ++i2;
+ EXPECT_EQ(set1->end(), i2);
+}
+
+TEST(HeapTest, HeapWeakLinkedHashSet) {
+ OrderedSetHelper<HeapLinkedHashSet<Member<IntWrapper>>>(true);
+ OrderedSetHelper<HeapLinkedHashSet<WeakMember<IntWrapper>>>(false);
+ OrderedSetHelper<HeapListHashSet<Member<IntWrapper>>>(true);
+}
+
+class ThingWithDestructor {
+ public:
+ ThingWithDestructor() : x_(kEmptyValue) { live_things_with_destructor_++; }
+
+ ThingWithDestructor(int x) : x_(x) { live_things_with_destructor_++; }
+
+ ThingWithDestructor(const ThingWithDestructor& other) {
+ *this = other;
+ live_things_with_destructor_++;
+ }
+
+ ~ThingWithDestructor() { live_things_with_destructor_--; }
+
+ int Value() { return x_; }
+
+ static int live_things_with_destructor_;
+
+ unsigned GetHash() { return IntHash<int>::GetHash(x_); }
+
+ private:
+ static const int kEmptyValue = 0;
+ int x_;
+};
+
+int ThingWithDestructor::live_things_with_destructor_;
+
+static void HeapMapDestructorHelper(bool clear_maps) {
+ ClearOutOldGarbage();
+ ThingWithDestructor::live_things_with_destructor_ = 0;
+
+ typedef HeapHashMap<WeakMember<IntWrapper>,
+ Member<RefCountedAndGarbageCollected>>
+ RefMap;
+
+ typedef HeapHashMap<WeakMember<IntWrapper>, ThingWithDestructor,
+ DefaultHash<WeakMember<IntWrapper>>::Hash,
+ HashTraits<WeakMember<IntWrapper>>>
+ Map;
+
+ Persistent<Map> map(new Map());
+ Persistent<RefMap> ref_map(new RefMap());
+
+ Persistent<IntWrapper> luck(IntWrapper::Create(103));
+
+ int base_line, ref_base_line;
+
+ {
+ Map stack_map;
+ RefMap stack_ref_map;
+
+ PreciselyCollectGarbage();
+ PreciselyCollectGarbage();
+
+ stack_map.insert(IntWrapper::Create(42), ThingWithDestructor(1729));
+ stack_map.insert(luck, ThingWithDestructor(8128));
+ stack_ref_map.insert(IntWrapper::Create(42),
+ RefCountedAndGarbageCollected::Create());
+ stack_ref_map.insert(luck, RefCountedAndGarbageCollected::Create());
+
+ base_line = ThingWithDestructor::live_things_with_destructor_;
+ ref_base_line = RefCountedAndGarbageCollected::destructor_calls_;
+
+ // Although the heap maps are on-stack, we can't expect prompt
+ // finalization of the elements, so when they go out of scope here we
+ // will not necessarily have called the relevant destructors.
+ }
+
+ // The RefCountedAndGarbageCollected things need an extra GC to discover
+ // that they are no longer ref counted.
+ PreciselyCollectGarbage();
+ PreciselyCollectGarbage();
+ EXPECT_EQ(base_line - 2, ThingWithDestructor::live_things_with_destructor_);
+ EXPECT_EQ(ref_base_line + 2,
+ RefCountedAndGarbageCollected::destructor_calls_);
+
+ // Now use maps kept alive with persistents. Here we don't expect any
+ // destructors to be called before there have been GCs.
+
+ map->insert(IntWrapper::Create(42), ThingWithDestructor(1729));
+ map->insert(luck, ThingWithDestructor(8128));
+ ref_map->insert(IntWrapper::Create(42),
+ RefCountedAndGarbageCollected::Create());
+ ref_map->insert(luck, RefCountedAndGarbageCollected::Create());
+
+ base_line = ThingWithDestructor::live_things_with_destructor_;
+ ref_base_line = RefCountedAndGarbageCollected::destructor_calls_;
+
+ luck.Clear();
+ if (clear_maps) {
+ map->clear(); // Clear map.
+ ref_map->clear(); // Clear map.
+ } else {
+ map.Clear(); // Clear Persistent handle, not map.
+ ref_map.Clear(); // Clear Persistent handle, not map.
+ PreciselyCollectGarbage();
+ PreciselyCollectGarbage();
+ }
+
+ EXPECT_EQ(base_line - 2, ThingWithDestructor::live_things_with_destructor_);
+
+ // Need a GC to make sure that the RefCountedAndGarbageCollected thing
+ // noticies it's been decremented to zero.
+ PreciselyCollectGarbage();
+ EXPECT_EQ(ref_base_line + 2,
+ RefCountedAndGarbageCollected::destructor_calls_);
+}
+
+TEST(HeapTest, HeapMapDestructor) {
+ HeapMapDestructorHelper(true);
+ HeapMapDestructorHelper(false);
+}
+
+typedef HeapHashSet<PairWeakStrong> WeakStrongSet;
+typedef HeapHashSet<PairWeakUnwrapped> WeakUnwrappedSet;
+typedef HeapHashSet<PairStrongWeak> StrongWeakSet;
+typedef HeapHashSet<PairUnwrappedWeak> UnwrappedWeakSet;
+typedef HeapLinkedHashSet<PairWeakStrong> WeakStrongLinkedSet;
+typedef HeapLinkedHashSet<PairWeakUnwrapped> WeakUnwrappedLinkedSet;
+typedef HeapLinkedHashSet<PairStrongWeak> StrongWeakLinkedSet;
+typedef HeapLinkedHashSet<PairUnwrappedWeak> UnwrappedWeakLinkedSet;
+typedef HeapHashCountedSet<PairWeakStrong> WeakStrongCountedSet;
+typedef HeapHashCountedSet<PairWeakUnwrapped> WeakUnwrappedCountedSet;
+typedef HeapHashCountedSet<PairStrongWeak> StrongWeakCountedSet;
+typedef HeapHashCountedSet<PairUnwrappedWeak> UnwrappedWeakCountedSet;
+
+template <typename T>
+T& IteratorExtractor(WTF::KeyValuePair<T, unsigned>& pair) {
+ return pair.key;
+}
+
+template <typename T>
+T& IteratorExtractor(T& not_a_pair) {
+ return not_a_pair;
+}
+
+template <typename WSSet, typename SWSet, typename WUSet, typename UWSet>
+void CheckPairSets(Persistent<WSSet>& weak_strong,
+ Persistent<SWSet>& strong_weak,
+ Persistent<WUSet>& weak_unwrapped,
+ Persistent<UWSet>& unwrapped_weak,
+ bool ones,
+ Persistent<IntWrapper>& two) {
+ typename WSSet::iterator it_ws = weak_strong->begin();
+ typename SWSet::iterator it_sw = strong_weak->begin();
+ typename WUSet::iterator it_wu = weak_unwrapped->begin();
+ typename UWSet::iterator it_uw = unwrapped_weak->begin();
+
+ EXPECT_EQ(2u, weak_strong->size());
+ EXPECT_EQ(2u, strong_weak->size());
+ EXPECT_EQ(2u, weak_unwrapped->size());
+ EXPECT_EQ(2u, unwrapped_weak->size());
+
+ PairWeakStrong p = IteratorExtractor(*it_ws);
+ PairStrongWeak p2 = IteratorExtractor(*it_sw);
+ PairWeakUnwrapped p3 = IteratorExtractor(*it_wu);
+ PairUnwrappedWeak p4 = IteratorExtractor(*it_uw);
+ if (p.first == two && p.second == two)
+ ++it_ws;
+ if (p2.first == two && p2.second == two)
+ ++it_sw;
+ if (p3.first == two && p3.second == 2)
+ ++it_wu;
+ if (p4.first == 2 && p4.second == two)
+ ++it_uw;
+ p = IteratorExtractor(*it_ws);
+ p2 = IteratorExtractor(*it_sw);
+ p3 = IteratorExtractor(*it_wu);
+ p4 = IteratorExtractor(*it_uw);
+ IntWrapper* null_wrapper = nullptr;
+ if (ones) {
+ EXPECT_EQ(p.first->Value(), 1);
+ EXPECT_EQ(p2.second->Value(), 1);
+ EXPECT_EQ(p3.first->Value(), 1);
+ EXPECT_EQ(p4.second->Value(), 1);
+ } else {
+ EXPECT_EQ(p.first, null_wrapper);
+ EXPECT_EQ(p2.second, null_wrapper);
+ EXPECT_EQ(p3.first, null_wrapper);
+ EXPECT_EQ(p4.second, null_wrapper);
+ }
+
+ EXPECT_EQ(p.second->Value(), 2);
+ EXPECT_EQ(p2.first->Value(), 2);
+ EXPECT_EQ(p3.second, 2);
+ EXPECT_EQ(p4.first, 2);
+
+ EXPECT_TRUE(weak_strong->Contains(PairWeakStrong(&*two, &*two)));
+ EXPECT_TRUE(strong_weak->Contains(PairStrongWeak(&*two, &*two)));
+ EXPECT_TRUE(weak_unwrapped->Contains(PairWeakUnwrapped(&*two, 2)));
+ EXPECT_TRUE(unwrapped_weak->Contains(PairUnwrappedWeak(2, &*two)));
+}
+
+template <typename WSSet, typename SWSet, typename WUSet, typename UWSet>
+void WeakPairsHelper() {
+ IntWrapper::destructor_calls_ = 0;
+
+ PersistentHeapVector<Member<IntWrapper>> keep_numbers_alive;
+
+ Persistent<WSSet> weak_strong = new WSSet();
+ Persistent<SWSet> strong_weak = new SWSet();
+ Persistent<WUSet> weak_unwrapped = new WUSet();
+ Persistent<UWSet> unwrapped_weak = new UWSet();
+
+ Persistent<IntWrapper> two = IntWrapper::Create(2);
+
+ weak_strong->insert(PairWeakStrong(IntWrapper::Create(1), &*two));
+ weak_strong->insert(PairWeakStrong(&*two, &*two));
+ strong_weak->insert(PairStrongWeak(&*two, IntWrapper::Create(1)));
+ strong_weak->insert(PairStrongWeak(&*two, &*two));
+ weak_unwrapped->insert(PairWeakUnwrapped(IntWrapper::Create(1), 2));
+ weak_unwrapped->insert(PairWeakUnwrapped(&*two, 2));
+ unwrapped_weak->insert(PairUnwrappedWeak(2, IntWrapper::Create(1)));
+ unwrapped_weak->insert(PairUnwrappedWeak(2, &*two));
+
+ CheckPairSets<WSSet, SWSet, WUSet, UWSet>(
+ weak_strong, strong_weak, weak_unwrapped, unwrapped_weak, true, two);
+
+ PreciselyCollectGarbage();
+ CheckPairSets<WSSet, SWSet, WUSet, UWSet>(
+ weak_strong, strong_weak, weak_unwrapped, unwrapped_weak, false, two);
+}
+
+TEST(HeapTest, HeapWeakPairs) {
+ {
+ typedef HeapHashSet<PairWeakStrong> WeakStrongSet;
+ typedef HeapHashSet<PairWeakUnwrapped> WeakUnwrappedSet;
+ typedef HeapHashSet<PairStrongWeak> StrongWeakSet;
+ typedef HeapHashSet<PairUnwrappedWeak> UnwrappedWeakSet;
+ WeakPairsHelper<WeakStrongSet, StrongWeakSet, WeakUnwrappedSet,
+ UnwrappedWeakSet>();
+ }
+
+ {
+ typedef HeapListHashSet<PairWeakStrong> WeakStrongSet;
+ typedef HeapListHashSet<PairWeakUnwrapped> WeakUnwrappedSet;
+ typedef HeapListHashSet<PairStrongWeak> StrongWeakSet;
+ typedef HeapListHashSet<PairUnwrappedWeak> UnwrappedWeakSet;
+ WeakPairsHelper<WeakStrongSet, StrongWeakSet, WeakUnwrappedSet,
+ UnwrappedWeakSet>();
+ }
+
+ {
+ typedef HeapLinkedHashSet<PairWeakStrong> WeakStrongSet;
+ typedef HeapLinkedHashSet<PairWeakUnwrapped> WeakUnwrappedSet;
+ typedef HeapLinkedHashSet<PairStrongWeak> StrongWeakSet;
+ typedef HeapLinkedHashSet<PairUnwrappedWeak> UnwrappedWeakSet;
+ WeakPairsHelper<WeakStrongSet, StrongWeakSet, WeakUnwrappedSet,
+ UnwrappedWeakSet>();
+ }
+}
+
+TEST(HeapTest, HeapWeakCollectionTypes) {
+ IntWrapper::destructor_calls_ = 0;
+
+ typedef HeapHashMap<WeakMember<IntWrapper>, Member<IntWrapper>> WeakStrong;
+ typedef HeapHashMap<Member<IntWrapper>, WeakMember<IntWrapper>> StrongWeak;
+ typedef HeapHashMap<WeakMember<IntWrapper>, WeakMember<IntWrapper>> WeakWeak;
+ typedef HeapHashSet<WeakMember<IntWrapper>> WeakSet;
+ typedef HeapLinkedHashSet<WeakMember<IntWrapper>> WeakOrderedSet;
+
+ ClearOutOldGarbage();
+
+ const int kWeakStrongIndex = 0;
+ const int kStrongWeakIndex = 1;
+ const int kWeakWeakIndex = 2;
+ const int kNumberOfMapIndices = 3;
+ const int kWeakSetIndex = 3;
+ const int kWeakOrderedSetIndex = 4;
+ const int kNumberOfCollections = 5;
+
+ for (int test_run = 0; test_run < 4; test_run++) {
+ for (int collection_number = 0; collection_number < kNumberOfCollections;
+ collection_number++) {
+ bool delete_afterwards = (test_run == 1);
+ bool add_afterwards = (test_run == 2);
+ bool test_that_iterators_make_strong = (test_run == 3);
+
+ // The test doesn't work for strongWeak with deleting because we lost
+ // the key from the keepNumbersAlive array, so we can't do the lookup.
+ if (delete_afterwards && collection_number == kStrongWeakIndex)
+ continue;
+
+ unsigned added = add_afterwards ? 100 : 0;
+
+ Persistent<WeakStrong> weak_strong = new WeakStrong();
+ Persistent<StrongWeak> strong_weak = new StrongWeak();
+ Persistent<WeakWeak> weak_weak = new WeakWeak();
+
+ Persistent<WeakSet> weak_set = new WeakSet();
+ Persistent<WeakOrderedSet> weak_ordered_set = new WeakOrderedSet();
+
+ PersistentHeapVector<Member<IntWrapper>> keep_numbers_alive;
+ for (int i = 0; i < 128; i += 2) {
+ IntWrapper* wrapped = IntWrapper::Create(i);
+ IntWrapper* wrapped2 = IntWrapper::Create(i + 1);
+ keep_numbers_alive.push_back(wrapped);
+ keep_numbers_alive.push_back(wrapped2);
+ weak_strong->insert(wrapped, wrapped2);
+ strong_weak->insert(wrapped2, wrapped);
+ weak_weak->insert(wrapped, wrapped2);
+ weak_set->insert(wrapped);
+ weak_ordered_set->insert(wrapped);
+ }
+
+ EXPECT_EQ(64u, weak_strong->size());
+ EXPECT_EQ(64u, strong_weak->size());
+ EXPECT_EQ(64u, weak_weak->size());
+ EXPECT_EQ(64u, weak_set->size());
+ EXPECT_EQ(64u, weak_ordered_set->size());
+
+ // Collect garbage. This should change nothing since we are keeping
+ // alive the IntWrapper objects.
+ PreciselyCollectGarbage();
+
+ EXPECT_EQ(64u, weak_strong->size());
+ EXPECT_EQ(64u, strong_weak->size());
+ EXPECT_EQ(64u, weak_weak->size());
+ EXPECT_EQ(64u, weak_set->size());
+ EXPECT_EQ(64u, weak_ordered_set->size());
+
+ for (int i = 0; i < 128; i += 2) {
+ IntWrapper* wrapped = keep_numbers_alive[i];
+ IntWrapper* wrapped2 = keep_numbers_alive[i + 1];
+ EXPECT_EQ(wrapped2, weak_strong->at(wrapped));
+ EXPECT_EQ(wrapped, strong_weak->at(wrapped2));
+ EXPECT_EQ(wrapped2, weak_weak->at(wrapped));
+ EXPECT_TRUE(weak_set->Contains(wrapped));
+ EXPECT_TRUE(weak_ordered_set->Contains(wrapped));
+ }
+
+ for (int i = 0; i < 128; i += 3)
+ keep_numbers_alive[i] = nullptr;
+
+ if (collection_number != kWeakStrongIndex)
+ weak_strong->clear();
+ if (collection_number != kStrongWeakIndex)
+ strong_weak->clear();
+ if (collection_number != kWeakWeakIndex)
+ weak_weak->clear();
+ if (collection_number != kWeakSetIndex)
+ weak_set->clear();
+ if (collection_number != kWeakOrderedSetIndex)
+ weak_ordered_set->clear();
+
+ if (test_that_iterators_make_strong) {
+ WeakStrong::iterator it1 = weak_strong->begin();
+ StrongWeak::iterator it2 = strong_weak->begin();
+ WeakWeak::iterator it3 = weak_weak->begin();
+ WeakSet::iterator it4 = weak_set->begin();
+ WeakOrderedSet::iterator it5 = weak_ordered_set->begin();
+ // Collect garbage. This should change nothing since the
+ // iterators make the collections strong.
+ ConservativelyCollectGarbage();
+ if (collection_number == kWeakStrongIndex) {
+ EXPECT_EQ(64u, weak_strong->size());
+ MapIteratorCheck(it1, weak_strong->end(), 64);
+ } else if (collection_number == kStrongWeakIndex) {
+ EXPECT_EQ(64u, strong_weak->size());
+ MapIteratorCheck(it2, strong_weak->end(), 64);
+ } else if (collection_number == kWeakWeakIndex) {
+ EXPECT_EQ(64u, weak_weak->size());
+ MapIteratorCheck(it3, weak_weak->end(), 64);
+ } else if (collection_number == kWeakSetIndex) {
+ EXPECT_EQ(64u, weak_set->size());
+ SetIteratorCheck(it4, weak_set->end(), 64);
+ } else if (collection_number == kWeakOrderedSetIndex) {
+ EXPECT_EQ(64u, weak_ordered_set->size());
+ SetIteratorCheck(it5, weak_ordered_set->end(), 64);
+ }
+ } else {
+ // Collect garbage. This causes weak processing to remove
+ // things from the collections.
+ PreciselyCollectGarbage();
+ unsigned count = 0;
+ for (int i = 0; i < 128; i += 2) {
+ bool first_alive = keep_numbers_alive[i];
+ bool second_alive = keep_numbers_alive[i + 1];
+ if (first_alive && (collection_number == kWeakStrongIndex ||
+ collection_number == kStrongWeakIndex))
+ second_alive = true;
+ if (first_alive && second_alive &&
+ collection_number < kNumberOfMapIndices) {
+ if (collection_number == kWeakStrongIndex) {
+ if (delete_afterwards)
+ EXPECT_EQ(i + 1,
+ weak_strong->Take(keep_numbers_alive[i])->Value());
+ } else if (collection_number == kStrongWeakIndex) {
+ if (delete_afterwards)
+ EXPECT_EQ(
+ i, strong_weak->Take(keep_numbers_alive[i + 1])->Value());
+ } else if (collection_number == kWeakWeakIndex) {
+ if (delete_afterwards)
+ EXPECT_EQ(i + 1,
+ weak_weak->Take(keep_numbers_alive[i])->Value());
+ }
+ if (!delete_afterwards)
+ count++;
+ } else if (collection_number == kWeakSetIndex && first_alive) {
+ ASSERT_TRUE(weak_set->Contains(keep_numbers_alive[i]));
+ if (delete_afterwards)
+ weak_set->erase(keep_numbers_alive[i]);
+ else
+ count++;
+ } else if (collection_number == kWeakOrderedSetIndex && first_alive) {
+ ASSERT_TRUE(weak_ordered_set->Contains(keep_numbers_alive[i]));
+ if (delete_afterwards)
+ weak_ordered_set->erase(keep_numbers_alive[i]);
+ else
+ count++;
+ }
+ }
+ if (add_afterwards) {
+ for (int i = 1000; i < 1100; i++) {
+ IntWrapper* wrapped = IntWrapper::Create(i);
+ keep_numbers_alive.push_back(wrapped);
+ weak_strong->insert(wrapped, wrapped);
+ strong_weak->insert(wrapped, wrapped);
+ weak_weak->insert(wrapped, wrapped);
+ weak_set->insert(wrapped);
+ weak_ordered_set->insert(wrapped);
+ }
+ }
+ if (collection_number == kWeakStrongIndex)
+ EXPECT_EQ(count + added, weak_strong->size());
+ else if (collection_number == kStrongWeakIndex)
+ EXPECT_EQ(count + added, strong_weak->size());
+ else if (collection_number == kWeakWeakIndex)
+ EXPECT_EQ(count + added, weak_weak->size());
+ else if (collection_number == kWeakSetIndex)
+ EXPECT_EQ(count + added, weak_set->size());
+ else if (collection_number == kWeakOrderedSetIndex)
+ EXPECT_EQ(count + added, weak_ordered_set->size());
+ WeakStrong::iterator it1 = weak_strong->begin();
+ StrongWeak::iterator it2 = strong_weak->begin();
+ WeakWeak::iterator it3 = weak_weak->begin();
+ WeakSet::iterator it4 = weak_set->begin();
+ WeakOrderedSet::iterator it5 = weak_ordered_set->begin();
+ MapIteratorCheck(
+ it1, weak_strong->end(),
+ (collection_number == kWeakStrongIndex ? count : 0) + added);
+ MapIteratorCheck(
+ it2, strong_weak->end(),
+ (collection_number == kStrongWeakIndex ? count : 0) + added);
+ MapIteratorCheck(
+ it3, weak_weak->end(),
+ (collection_number == kWeakWeakIndex ? count : 0) + added);
+ SetIteratorCheck(
+ it4, weak_set->end(),
+ (collection_number == kWeakSetIndex ? count : 0) + added);
+ SetIteratorCheck(
+ it5, weak_ordered_set->end(),
+ (collection_number == kWeakOrderedSetIndex ? count : 0) + added);
+ }
+ for (unsigned i = 0; i < 128 + added; i++)
+ keep_numbers_alive[i] = nullptr;
+ PreciselyCollectGarbage();
+ EXPECT_EQ(0u, weak_strong->size());
+ EXPECT_EQ(0u, strong_weak->size());
+ EXPECT_EQ(0u, weak_weak->size());
+ EXPECT_EQ(0u, weak_set->size());
+ EXPECT_EQ(0u, weak_ordered_set->size());
+ }
+ }
+}
+
+TEST(HeapTest, HeapHashCountedSetToVector) {
+ HeapHashCountedSet<Member<IntWrapper>> set;
+ HeapVector<Member<IntWrapper>> vector;
+ set.insert(new IntWrapper(1));
+ set.insert(new IntWrapper(1));
+ set.insert(new IntWrapper(2));
+
+ CopyToVector(set, vector);
+ EXPECT_EQ(3u, vector.size());
+
+ Vector<int> int_vector;
+ for (const auto& i : vector)
+ int_vector.push_back(i->Value());
+ std::sort(int_vector.begin(), int_vector.end());
+ ASSERT_EQ(3u, int_vector.size());
+ EXPECT_EQ(1, int_vector[0]);
+ EXPECT_EQ(1, int_vector[1]);
+ EXPECT_EQ(2, int_vector[2]);
+}
+
+TEST(HeapTest, WeakHeapHashCountedSetToVector) {
+ HeapHashCountedSet<WeakMember<IntWrapper>> set;
+ HeapVector<Member<IntWrapper>> vector;
+ set.insert(new IntWrapper(1));
+ set.insert(new IntWrapper(1));
+ set.insert(new IntWrapper(2));
+
+ CopyToVector(set, vector);
+ EXPECT_LE(3u, vector.size());
+ for (const auto& i : vector)
+ EXPECT_TRUE(i->Value() == 1 || i->Value() == 2);
+}
+
+TEST(HeapTest, RefCountedGarbageCollected) {
+ RefCountedAndGarbageCollected::destructor_calls_ = 0;
+ {
+ scoped_refptr<RefCountedAndGarbageCollected> ref_ptr3;
+ {
+ Persistent<RefCountedAndGarbageCollected> persistent;
+ {
+ Persistent<RefCountedAndGarbageCollected> ref_ptr1 =
+ RefCountedAndGarbageCollected::Create();
+ Persistent<RefCountedAndGarbageCollected> ref_ptr2 =
+ RefCountedAndGarbageCollected::Create();
+ PreciselyCollectGarbage();
+ EXPECT_EQ(0, RefCountedAndGarbageCollected::destructor_calls_);
+ persistent = ref_ptr1.Get();
+ }
+ // Reference count is zero for both objects but one of
+ // them is kept alive by a persistent handle.
+ PreciselyCollectGarbage();
+ EXPECT_EQ(1, RefCountedAndGarbageCollected::destructor_calls_);
+ ref_ptr3 = persistent.Get();
+ }
+ // The persistent handle is gone but the ref count has been
+ // increased to 1.
+ PreciselyCollectGarbage();
+ EXPECT_EQ(1, RefCountedAndGarbageCollected::destructor_calls_);
+ }
+ // Both persistent handle is gone and ref count is zero so the
+ // object can be collected.
+ PreciselyCollectGarbage();
+ EXPECT_EQ(2, RefCountedAndGarbageCollected::destructor_calls_);
+}
+
+TEST(HeapTest, WeakMembers) {
+ Bar::live_ = 0;
+ {
+ Persistent<Bar> h1 = Bar::Create();
+ Persistent<Weak> h4;
+ Persistent<WithWeakMember> h5;
+ PreciselyCollectGarbage();
+ ASSERT_EQ(1u, Bar::live_); // h1 is live.
+ {
+ Bar* h2 = Bar::Create();
+ Bar* h3 = Bar::Create();
+ h4 = Weak::Create(h2, h3);
+ h5 = WithWeakMember::Create(h2, h3);
+ ConservativelyCollectGarbage();
+ EXPECT_EQ(5u, Bar::live_); // The on-stack pointer keeps h3 alive.
+ EXPECT_FALSE(h3->HasBeenFinalized());
+ EXPECT_TRUE(h4->StrongIsThere());
+ EXPECT_TRUE(h4->WeakIsThere());
+ EXPECT_TRUE(h5->StrongIsThere());
+ EXPECT_TRUE(h5->WeakIsThere());
+ }
+ // h3 is collected, weak pointers from h4 and h5 don't keep it alive.
+ PreciselyCollectGarbage();
+ EXPECT_EQ(4u, Bar::live_);
+ EXPECT_TRUE(h4->StrongIsThere());
+ EXPECT_FALSE(h4->WeakIsThere()); // h3 is gone from weak pointer.
+ EXPECT_TRUE(h5->StrongIsThere());
+ EXPECT_FALSE(h5->WeakIsThere()); // h3 is gone from weak pointer.
+ h1.Release(); // Zero out h1.
+ PreciselyCollectGarbage();
+ EXPECT_EQ(3u, Bar::live_); // Only h4, h5 and h2 are left.
+ EXPECT_TRUE(h4->StrongIsThere()); // h2 is still pointed to from h4.
+ EXPECT_TRUE(h5->StrongIsThere()); // h2 is still pointed to from h5.
+ }
+ // h4 and h5 have gone out of scope now and they were keeping h2 alive.
+ PreciselyCollectGarbage();
+ EXPECT_EQ(0u, Bar::live_); // All gone.
+}
+
+TEST(HeapTest, FinalizationObserver) {
+ Persistent<FinalizationObserver<Observable>> o;
+ {
+ Observable* foo = Observable::Create(Bar::Create());
+ // |o| observes |foo|.
+ o = FinalizationObserver<Observable>::Create(foo);
+ }
+ // FinalizationObserver doesn't have a strong reference to |foo|. So |foo|
+ // and its member will be collected.
+ PreciselyCollectGarbage();
+ EXPECT_EQ(0u, Bar::live_);
+ EXPECT_TRUE(o->DidCallWillFinalize());
+
+ FinalizationObserverWithHashMap::did_call_will_finalize_ = false;
+ Observable* foo = Observable::Create(Bar::Create());
+ FinalizationObserverWithHashMap::ObserverMap& map =
+ FinalizationObserverWithHashMap::Observe(*foo);
+ EXPECT_EQ(1u, map.size());
+ foo = nullptr;
+ // FinalizationObserverWithHashMap doesn't have a strong reference to
+ // |foo|. So |foo| and its member will be collected.
+ PreciselyCollectGarbage();
+ EXPECT_EQ(0u, Bar::live_);
+ EXPECT_EQ(0u, map.size());
+ EXPECT_TRUE(FinalizationObserverWithHashMap::did_call_will_finalize_);
+
+ FinalizationObserverWithHashMap::ClearObservers();
+}
+
+TEST(HeapTest, PreFinalizer) {
+ Observable::will_finalize_was_called_ = false;
+ { Observable::Create(Bar::Create()); }
+ PreciselyCollectGarbage();
+ EXPECT_TRUE(Observable::will_finalize_was_called_);
+}
+
+TEST(HeapTest, PreFinalizerUnregistersItself) {
+ ObservableWithPreFinalizer::dispose_was_called_ = false;
+ ObservableWithPreFinalizer::Create();
+ PreciselyCollectGarbage();
+ EXPECT_TRUE(ObservableWithPreFinalizer::dispose_was_called_);
+ // Don't crash, and assertions don't fail.
+}
+
+TEST(HeapTest, NestedPreFinalizer) {
+ g_dispose_was_called_for_pre_finalizer_base = false;
+ g_dispose_was_called_for_pre_finalizer_sub_class = false;
+ g_dispose_was_called_for_pre_finalizer_mixin = false;
+ PreFinalizerSubClass::Create();
+ PreciselyCollectGarbage();
+ EXPECT_TRUE(g_dispose_was_called_for_pre_finalizer_base);
+ EXPECT_TRUE(g_dispose_was_called_for_pre_finalizer_sub_class);
+ EXPECT_TRUE(g_dispose_was_called_for_pre_finalizer_mixin);
+ // Don't crash, and assertions don't fail.
+}
+
+TEST(HeapTest, Comparisons) {
+ Persistent<Bar> bar_persistent = Bar::Create();
+ Persistent<Foo> foo_persistent = Foo::Create(bar_persistent);
+ EXPECT_TRUE(bar_persistent != foo_persistent);
+ bar_persistent = foo_persistent;
+ EXPECT_TRUE(bar_persistent == foo_persistent);
+}
+
+#if DCHECK_IS_ON()
+namespace {
+
+static size_t g_check_mark_count = 0;
+
+bool ReportMarkedPointer(HeapObjectHeader*) {
+ g_check_mark_count++;
+ // Do not try to mark the located heap object.
+ return true;
+}
+}
+#endif
+
+TEST(HeapTest, CheckAndMarkPointer) {
+#if DCHECK_IS_ON()
+ ThreadHeap& heap = ThreadState::Current()->Heap();
+ ClearOutOldGarbage();
+
+ Vector<Address> object_addresses;
+ Vector<Address> end_addresses;
+ Address large_object_address;
+ Address large_object_end_address;
+ for (int i = 0; i < 10; i++) {
+ SimpleObject* object = SimpleObject::Create();
+ Address object_address = reinterpret_cast<Address>(object);
+ object_addresses.push_back(object_address);
+ end_addresses.push_back(object_address + sizeof(SimpleObject) - 1);
+ }
+ LargeHeapObject* large_object = LargeHeapObject::Create();
+ large_object_address = reinterpret_cast<Address>(large_object);
+ large_object_end_address = large_object_address + sizeof(LargeHeapObject) - 1;
+
+ // This is a low-level test where we call checkAndMarkPointer. This method
+ // causes the object start bitmap to be computed which requires the heap
+ // to be in a consistent state (e.g. the free allocation area must be put
+ // into a free list header). However when we call makeConsistentForGC it
+ // also clears out the freelists so we have to rebuild those before trying
+ // to allocate anything again. We do this by forcing a GC after doing the
+ // checkAndMarkPointer tests.
+ {
+ TestGCScope scope(BlinkGC::kHeapPointersOnStack);
+ MarkingVisitor visitor(ThreadState::Current(),
+ MarkingVisitor::kGlobalMarking);
+ heap.FlushHeapDoesNotContainCache();
+ for (size_t i = 0; i < object_addresses.size(); i++) {
+ EXPECT_TRUE(heap.CheckAndMarkPointer(&visitor, object_addresses[i],
+ ReportMarkedPointer));
+ EXPECT_TRUE(heap.CheckAndMarkPointer(&visitor, end_addresses[i],
+ ReportMarkedPointer));
+ }
+ EXPECT_EQ(object_addresses.size() * 2, g_check_mark_count);
+ g_check_mark_count = 0;
+ EXPECT_TRUE(heap.CheckAndMarkPointer(&visitor, large_object_address,
+ ReportMarkedPointer));
+ EXPECT_TRUE(heap.CheckAndMarkPointer(&visitor, large_object_end_address,
+ ReportMarkedPointer));
+ EXPECT_EQ(2ul, g_check_mark_count);
+ g_check_mark_count = 0ul;
+ }
+ // This forces a GC without stack scanning which results in the objects
+ // being collected. This will also rebuild the above mentioned freelists,
+ // however we don't rely on that below since we don't have any allocations.
+ ClearOutOldGarbage();
+ {
+ TestGCScope scope(BlinkGC::kHeapPointersOnStack);
+ MarkingVisitor visitor(ThreadState::Current(),
+ MarkingVisitor::kGlobalMarking);
+ heap.FlushHeapDoesNotContainCache();
+ for (size_t i = 0; i < object_addresses.size(); i++) {
+ // We would like to assert that checkAndMarkPointer returned false
+ // here because the pointers no longer point into a valid object
+ // (it's been freed by the GCs. But checkAndMarkPointer will return
+ // true for any pointer that points into a heap page, regardless of
+ // whether it points at a valid object (this ensures the
+ // correctness of the page-based on-heap address caches), so we
+ // can't make that assert.
+ heap.CheckAndMarkPointer(&visitor, object_addresses[i],
+ ReportMarkedPointer);
+ heap.CheckAndMarkPointer(&visitor, end_addresses[i], ReportMarkedPointer);
+ }
+ EXPECT_EQ(0ul, g_check_mark_count);
+ heap.CheckAndMarkPointer(&visitor, large_object_address,
+ ReportMarkedPointer);
+ heap.CheckAndMarkPointer(&visitor, large_object_end_address,
+ ReportMarkedPointer);
+ EXPECT_EQ(0ul, g_check_mark_count);
+ }
+ // This round of GC is important to make sure that the object start
+ // bitmap are cleared out and that the free lists are rebuild.
+ ClearOutOldGarbage();
+#endif
+}
+
+TEST(HeapTest, PersistentHeapCollectionTypes) {
+ IntWrapper::destructor_calls_ = 0;
+
+ typedef HeapVector<Member<IntWrapper>> Vec;
+ typedef PersistentHeapVector<Member<IntWrapper>> PVec;
+ typedef PersistentHeapHashSet<Member<IntWrapper>> PSet;
+ typedef PersistentHeapListHashSet<Member<IntWrapper>> PListSet;
+ typedef PersistentHeapLinkedHashSet<Member<IntWrapper>> PLinkedSet;
+ typedef PersistentHeapHashMap<Member<IntWrapper>, Member<IntWrapper>> PMap;
+ typedef PersistentHeapHashMap<WeakMember<IntWrapper>, Member<IntWrapper>>
+ WeakPMap;
+ typedef PersistentHeapDeque<Member<IntWrapper>> PDeque;
+
+ ClearOutOldGarbage();
+ {
+ PVec p_vec;
+ PDeque p_deque;
+ PSet p_set;
+ PListSet p_list_set;
+ PLinkedSet p_linked_set;
+ PMap p_map;
+ WeakPMap wp_map;
+
+ IntWrapper* one(IntWrapper::Create(1));
+ IntWrapper* two(IntWrapper::Create(2));
+ IntWrapper* three(IntWrapper::Create(3));
+ IntWrapper* four(IntWrapper::Create(4));
+ IntWrapper* five(IntWrapper::Create(5));
+ IntWrapper* six(IntWrapper::Create(6));
+ IntWrapper* seven(IntWrapper::Create(7));
+ IntWrapper* eight(IntWrapper::Create(8));
+ IntWrapper* nine(IntWrapper::Create(9));
+ Persistent<IntWrapper> ten(IntWrapper::Create(10));
+ IntWrapper* eleven(IntWrapper::Create(11));
+
+ p_vec.push_back(one);
+ p_vec.push_back(two);
+
+ p_deque.push_back(seven);
+ p_deque.push_back(two);
+
+ Vec* vec = new Vec();
+ vec->swap(p_vec);
+
+ p_vec.push_back(two);
+ p_vec.push_back(three);
+
+ p_set.insert(four);
+ p_list_set.insert(eight);
+ p_linked_set.insert(nine);
+ p_map.insert(five, six);
+ wp_map.insert(ten, eleven);
+
+ // Collect |vec| and |one|.
+ vec = nullptr;
+ PreciselyCollectGarbage();
+ EXPECT_EQ(1, IntWrapper::destructor_calls_);
+
+ EXPECT_EQ(2u, p_vec.size());
+ EXPECT_EQ(two, p_vec.at(0));
+ EXPECT_EQ(three, p_vec.at(1));
+
+ EXPECT_EQ(2u, p_deque.size());
+ EXPECT_EQ(seven, p_deque.front());
+ EXPECT_EQ(seven, p_deque.TakeFirst());
+ EXPECT_EQ(two, p_deque.front());
+
+ EXPECT_EQ(1u, p_deque.size());
+
+ EXPECT_EQ(1u, p_set.size());
+ EXPECT_TRUE(p_set.Contains(four));
+
+ EXPECT_EQ(1u, p_list_set.size());
+ EXPECT_TRUE(p_list_set.Contains(eight));
+
+ EXPECT_EQ(1u, p_linked_set.size());
+ EXPECT_TRUE(p_linked_set.Contains(nine));
+
+ EXPECT_EQ(1u, p_map.size());
+ EXPECT_EQ(six, p_map.at(five));
+
+ EXPECT_EQ(1u, wp_map.size());
+ EXPECT_EQ(eleven, wp_map.at(ten));
+ ten.Clear();
+ PreciselyCollectGarbage();
+ EXPECT_EQ(0u, wp_map.size());
+ }
+
+ // Collect previous roots.
+ PreciselyCollectGarbage();
+ EXPECT_EQ(11, IntWrapper::destructor_calls_);
+}
+
+TEST(HeapTest, CollectionNesting) {
+ ClearOutOldGarbage();
+ int* key = &IntWrapper::destructor_calls_;
+ IntWrapper::destructor_calls_ = 0;
+ typedef HeapVector<Member<IntWrapper>> IntVector;
+ typedef HeapDeque<Member<IntWrapper>> IntDeque;
+ HeapHashMap<void*, IntVector>* map = new HeapHashMap<void*, IntVector>();
+ HeapHashMap<void*, IntDeque>* map2 = new HeapHashMap<void*, IntDeque>();
+ static_assert(WTF::IsTraceable<IntVector>::value,
+ "Failed to recognize HeapVector as traceable");
+ static_assert(WTF::IsTraceable<IntDeque>::value,
+ "Failed to recognize HeapDeque as traceable");
+
+ map->insert(key, IntVector());
+ map2->insert(key, IntDeque());
+
+ HeapHashMap<void*, IntVector>::iterator it = map->find(key);
+ EXPECT_EQ(0u, map->at(key).size());
+
+ HeapHashMap<void*, IntDeque>::iterator it2 = map2->find(key);
+ EXPECT_EQ(0u, map2->at(key).size());
+
+ it->value.push_back(IntWrapper::Create(42));
+ EXPECT_EQ(1u, map->at(key).size());
+
+ it2->value.push_back(IntWrapper::Create(42));
+ EXPECT_EQ(1u, map2->at(key).size());
+
+ Persistent<HeapHashMap<void*, IntVector>> keep_alive(map);
+ Persistent<HeapHashMap<void*, IntDeque>> keep_alive2(map2);
+
+ for (int i = 0; i < 100; i++) {
+ map->insert(key + 1 + i, IntVector());
+ map2->insert(key + 1 + i, IntDeque());
+ }
+
+ PreciselyCollectGarbage();
+
+ EXPECT_EQ(1u, map->at(key).size());
+ EXPECT_EQ(1u, map2->at(key).size());
+ EXPECT_EQ(0, IntWrapper::destructor_calls_);
+
+ keep_alive = nullptr;
+ PreciselyCollectGarbage();
+ EXPECT_EQ(1, IntWrapper::destructor_calls_);
+}
+
+TEST(HeapTest, GarbageCollectedMixin) {
+ ClearOutOldGarbage();
+
+ Persistent<UseMixin> usemixin = UseMixin::Create();
+ EXPECT_EQ(0, UseMixin::trace_count_);
+ PreciselyCollectGarbage();
+ EXPECT_EQ(1, UseMixin::trace_count_);
+
+ Persistent<Mixin> mixin = usemixin;
+ usemixin = nullptr;
+ PreciselyCollectGarbage();
+ EXPECT_EQ(2, UseMixin::trace_count_);
+
+ PersistentHeapHashSet<WeakMember<Mixin>> weak_map;
+ weak_map.insert(UseMixin::Create());
+ PreciselyCollectGarbage();
+ EXPECT_EQ(0u, weak_map.size());
+}
+
+TEST(HeapTest, CollectionNesting2) {
+ ClearOutOldGarbage();
+ void* key = &IntWrapper::destructor_calls_;
+ IntWrapper::destructor_calls_ = 0;
+ typedef HeapHashSet<Member<IntWrapper>> IntSet;
+ HeapHashMap<void*, IntSet>* map = new HeapHashMap<void*, IntSet>();
+
+ map->insert(key, IntSet());
+
+ HeapHashMap<void*, IntSet>::iterator it = map->find(key);
+ EXPECT_EQ(0u, map->at(key).size());
+
+ it->value.insert(IntWrapper::Create(42));
+ EXPECT_EQ(1u, map->at(key).size());
+
+ Persistent<HeapHashMap<void*, IntSet>> keep_alive(map);
+ PreciselyCollectGarbage();
+ EXPECT_EQ(1u, map->at(key).size());
+ EXPECT_EQ(0, IntWrapper::destructor_calls_);
+}
+
+TEST(HeapTest, CollectionNesting3) {
+ ClearOutOldGarbage();
+ IntWrapper::destructor_calls_ = 0;
+ typedef HeapVector<Member<IntWrapper>> IntVector;
+ typedef HeapDeque<Member<IntWrapper>> IntDeque;
+ HeapVector<IntVector>* vector = new HeapVector<IntVector>();
+ HeapDeque<IntDeque>* deque = new HeapDeque<IntDeque>();
+
+ vector->push_back(IntVector());
+ deque->push_back(IntDeque());
+
+ HeapVector<IntVector>::iterator it = vector->begin();
+ HeapDeque<IntDeque>::iterator it2 = deque->begin();
+ EXPECT_EQ(0u, it->size());
+ EXPECT_EQ(0u, it2->size());
+
+ it->push_back(IntWrapper::Create(42));
+ it2->push_back(IntWrapper::Create(42));
+ EXPECT_EQ(1u, it->size());
+ EXPECT_EQ(1u, it2->size());
+
+ Persistent<HeapVector<IntVector>> keep_alive(vector);
+ Persistent<HeapDeque<IntDeque>> keep_alive2(deque);
+ PreciselyCollectGarbage();
+ EXPECT_EQ(1u, it->size());
+ EXPECT_EQ(1u, it2->size());
+ EXPECT_EQ(0, IntWrapper::destructor_calls_);
+}
+
+TEST(HeapTest, EmbeddedInVector) {
+ ClearOutOldGarbage();
+ SimpleFinalizedObject::destructor_calls_ = 0;
+ {
+ PersistentHeapVector<VectorObject, 2> inline_vector;
+ PersistentHeapVector<VectorObject> outline_vector;
+ VectorObject i1, i2;
+ inline_vector.push_back(i1);
+ inline_vector.push_back(i2);
+
+ VectorObject o1, o2;
+ outline_vector.push_back(o1);
+ outline_vector.push_back(o2);
+
+ PersistentHeapVector<VectorObjectInheritedTrace> vector_inherited_trace;
+ VectorObjectInheritedTrace it1, it2;
+ vector_inherited_trace.push_back(it1);
+ vector_inherited_trace.push_back(it2);
+
+ PreciselyCollectGarbage();
+ EXPECT_EQ(0, SimpleFinalizedObject::destructor_calls_);
+ }
+ PreciselyCollectGarbage();
+ EXPECT_EQ(6, SimpleFinalizedObject::destructor_calls_);
+}
+
+TEST(HeapTest, EmbeddedInDeque) {
+ ClearOutOldGarbage();
+ SimpleFinalizedObject::destructor_calls_ = 0;
+ {
+ PersistentHeapDeque<VectorObject, 2> inline_deque;
+ PersistentHeapDeque<VectorObject> outline_deque;
+ VectorObject i1, i2;
+ inline_deque.push_back(i1);
+ inline_deque.push_back(i2);
+
+ VectorObject o1, o2;
+ outline_deque.push_back(o1);
+ outline_deque.push_back(o2);
+
+ PersistentHeapDeque<VectorObjectInheritedTrace> deque_inherited_trace;
+ VectorObjectInheritedTrace it1, it2;
+ deque_inherited_trace.push_back(it1);
+ deque_inherited_trace.push_back(it2);
+
+ PreciselyCollectGarbage();
+ EXPECT_EQ(0, SimpleFinalizedObject::destructor_calls_);
+ }
+ PreciselyCollectGarbage();
+ EXPECT_EQ(6, SimpleFinalizedObject::destructor_calls_);
+}
+
+class InlinedVectorObject {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ InlinedVectorObject() = default;
+ ~InlinedVectorObject() { destructor_calls_++; }
+ void Trace(blink::Visitor* visitor) {}
+
+ static int destructor_calls_;
+};
+
+int InlinedVectorObject::destructor_calls_ = 0;
+
+class InlinedVectorObjectWithVtable {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ InlinedVectorObjectWithVtable() = default;
+ virtual ~InlinedVectorObjectWithVtable() { destructor_calls_++; }
+ virtual void VirtualMethod() {}
+ void Trace(blink::Visitor* visitor) {}
+
+ static int destructor_calls_;
+};
+
+int InlinedVectorObjectWithVtable::destructor_calls_ = 0;
+
+} // namespace blink
+
+WTF_ALLOW_MOVE_AND_INIT_WITH_MEM_FUNCTIONS(blink::InlinedVectorObject);
+
+namespace blink {
+
+class InlinedVectorObjectWrapper final
+ : public GarbageCollectedFinalized<InlinedVectorObjectWrapper> {
+ public:
+ InlinedVectorObjectWrapper() {
+ InlinedVectorObject i1, i2;
+ vector1_.push_back(i1);
+ vector1_.push_back(i2);
+ vector2_.push_back(i1);
+ vector2_.push_back(i2); // This allocates an out-of-line buffer.
+ vector3_.push_back(i1);
+ vector3_.push_back(i2);
+ }
+
+ void Trace(blink::Visitor* visitor) {
+ visitor->Trace(vector1_);
+ visitor->Trace(vector2_);
+ visitor->Trace(vector3_);
+ }
+
+ private:
+ HeapVector<InlinedVectorObject> vector1_;
+ HeapVector<InlinedVectorObject, 1> vector2_;
+ HeapVector<InlinedVectorObject, 2> vector3_;
+};
+
+class InlinedVectorObjectWithVtableWrapper final
+ : public GarbageCollectedFinalized<InlinedVectorObjectWithVtableWrapper> {
+ public:
+ InlinedVectorObjectWithVtableWrapper() {
+ InlinedVectorObjectWithVtable i1, i2;
+ vector1_.push_back(i1);
+ vector1_.push_back(i2);
+ vector2_.push_back(i1);
+ vector2_.push_back(i2); // This allocates an out-of-line buffer.
+ vector3_.push_back(i1);
+ vector3_.push_back(i2);
+ }
+
+ void Trace(blink::Visitor* visitor) {
+ visitor->Trace(vector1_);
+ visitor->Trace(vector2_);
+ visitor->Trace(vector3_);
+ }
+
+ private:
+ HeapVector<InlinedVectorObjectWithVtable> vector1_;
+ HeapVector<InlinedVectorObjectWithVtable, 1> vector2_;
+ HeapVector<InlinedVectorObjectWithVtable, 2> vector3_;
+};
+
+TEST(HeapTest, VectorDestructors) {
+ ClearOutOldGarbage();
+ InlinedVectorObject::destructor_calls_ = 0;
+ {
+ HeapVector<InlinedVectorObject> vector;
+ InlinedVectorObject i1, i2;
+ vector.push_back(i1);
+ vector.push_back(i2);
+ }
+ PreciselyCollectGarbage();
+ // This is not EXPECT_EQ but EXPECT_LE because a HeapVectorBacking calls
+ // destructors for all elements in (not the size but) the capacity of
+ // the vector. Thus the number of destructors called becomes larger
+ // than the actual number of objects in the vector.
+ EXPECT_LE(4, InlinedVectorObject::destructor_calls_);
+
+ InlinedVectorObject::destructor_calls_ = 0;
+ {
+ HeapVector<InlinedVectorObject, 1> vector;
+ InlinedVectorObject i1, i2;
+ vector.push_back(i1);
+ vector.push_back(i2); // This allocates an out-of-line buffer.
+ }
+ PreciselyCollectGarbage();
+ EXPECT_LE(4, InlinedVectorObject::destructor_calls_);
+
+ InlinedVectorObject::destructor_calls_ = 0;
+ {
+ HeapVector<InlinedVectorObject, 2> vector;
+ InlinedVectorObject i1, i2;
+ vector.push_back(i1);
+ vector.push_back(i2);
+ }
+ PreciselyCollectGarbage();
+ EXPECT_LE(4, InlinedVectorObject::destructor_calls_);
+
+ InlinedVectorObject::destructor_calls_ = 0;
+ {
+ Persistent<InlinedVectorObjectWrapper> vector_wrapper =
+ new InlinedVectorObjectWrapper();
+ ConservativelyCollectGarbage();
+ EXPECT_EQ(2, InlinedVectorObject::destructor_calls_);
+ }
+ PreciselyCollectGarbage();
+ EXPECT_LE(8, InlinedVectorObject::destructor_calls_);
+}
+
+// TODO(Oilpan): when Vector.h's contiguous container support no longer disables
+// Vector<>s with inline capacity, enable this test.
+#if !defined(ANNOTATE_CONTIGUOUS_CONTAINER)
+TEST(HeapTest, VectorDestructorsWithVtable) {
+ ClearOutOldGarbage();
+ InlinedVectorObjectWithVtable::destructor_calls_ = 0;
+ {
+ HeapVector<InlinedVectorObjectWithVtable> vector;
+ InlinedVectorObjectWithVtable i1, i2;
+ vector.push_back(i1);
+ vector.push_back(i2);
+ }
+ PreciselyCollectGarbage();
+ EXPECT_EQ(4, InlinedVectorObjectWithVtable::destructor_calls_);
+
+ InlinedVectorObjectWithVtable::destructor_calls_ = 0;
+ {
+ HeapVector<InlinedVectorObjectWithVtable, 1> vector;
+ InlinedVectorObjectWithVtable i1, i2;
+ vector.push_back(i1);
+ vector.push_back(i2); // This allocates an out-of-line buffer.
+ }
+ PreciselyCollectGarbage();
+ EXPECT_EQ(5, InlinedVectorObjectWithVtable::destructor_calls_);
+
+ InlinedVectorObjectWithVtable::destructor_calls_ = 0;
+ {
+ HeapVector<InlinedVectorObjectWithVtable, 2> vector;
+ InlinedVectorObjectWithVtable i1, i2;
+ vector.push_back(i1);
+ vector.push_back(i2);
+ }
+ PreciselyCollectGarbage();
+ EXPECT_EQ(4, InlinedVectorObjectWithVtable::destructor_calls_);
+
+ InlinedVectorObjectWithVtable::destructor_calls_ = 0;
+ {
+ Persistent<InlinedVectorObjectWithVtableWrapper> vector_wrapper =
+ new InlinedVectorObjectWithVtableWrapper();
+ ConservativelyCollectGarbage();
+ EXPECT_EQ(3, InlinedVectorObjectWithVtable::destructor_calls_);
+ }
+ PreciselyCollectGarbage();
+ EXPECT_EQ(9, InlinedVectorObjectWithVtable::destructor_calls_);
+}
+#endif
+
+template <typename Set>
+void RawPtrInHashHelper() {
+ Set set;
+ set.Add(new int(42));
+ set.Add(new int(42));
+ EXPECT_EQ(2u, set.size());
+ for (typename Set::iterator it = set.begin(); it != set.end(); ++it) {
+ EXPECT_EQ(42, **it);
+ delete *it;
+ }
+}
+
+TEST(HeapTest, HeapTerminatedArray) {
+ ClearOutOldGarbage();
+ IntWrapper::destructor_calls_ = 0;
+
+ HeapTerminatedArray<TerminatedArrayItem>* arr = nullptr;
+
+ const size_t kPrefixSize = 4;
+ const size_t kSuffixSize = 4;
+
+ {
+ HeapTerminatedArrayBuilder<TerminatedArrayItem> builder(arr);
+ builder.Grow(kPrefixSize);
+ ConservativelyCollectGarbage();
+ for (size_t i = 0; i < kPrefixSize; i++)
+ builder.Append(TerminatedArrayItem(IntWrapper::Create(i)));
+ arr = builder.Release();
+ }
+
+ ConservativelyCollectGarbage();
+ EXPECT_EQ(0, IntWrapper::destructor_calls_);
+ EXPECT_EQ(kPrefixSize, arr->size());
+ for (size_t i = 0; i < kPrefixSize; i++)
+ EXPECT_EQ(i, static_cast<size_t>(arr->at(i).Payload()->Value()));
+
+ {
+ HeapTerminatedArrayBuilder<TerminatedArrayItem> builder(arr);
+ builder.Grow(kSuffixSize);
+ for (size_t i = 0; i < kSuffixSize; i++)
+ builder.Append(TerminatedArrayItem(IntWrapper::Create(kPrefixSize + i)));
+ arr = builder.Release();
+ }
+
+ ConservativelyCollectGarbage();
+ EXPECT_EQ(0, IntWrapper::destructor_calls_);
+ EXPECT_EQ(kPrefixSize + kSuffixSize, arr->size());
+ for (size_t i = 0; i < kPrefixSize + kSuffixSize; i++)
+ EXPECT_EQ(i, static_cast<size_t>(arr->at(i).Payload()->Value()));
+
+ {
+ Persistent<HeapTerminatedArray<TerminatedArrayItem>> persistent_arr = arr;
+ arr = nullptr;
+ PreciselyCollectGarbage();
+ arr = persistent_arr.Get();
+ EXPECT_EQ(0, IntWrapper::destructor_calls_);
+ EXPECT_EQ(kPrefixSize + kSuffixSize, arr->size());
+ for (size_t i = 0; i < kPrefixSize + kSuffixSize; i++)
+ EXPECT_EQ(i, static_cast<size_t>(arr->at(i).Payload()->Value()));
+ }
+
+ arr = nullptr;
+ PreciselyCollectGarbage();
+ EXPECT_EQ(8, IntWrapper::destructor_calls_);
+}
+
+TEST(HeapTest, HeapLinkedStack) {
+ ClearOutOldGarbage();
+ IntWrapper::destructor_calls_ = 0;
+
+ HeapLinkedStack<TerminatedArrayItem>* stack =
+ new HeapLinkedStack<TerminatedArrayItem>();
+
+ const size_t kStackSize = 10;
+
+ for (size_t i = 0; i < kStackSize; i++)
+ stack->Push(TerminatedArrayItem(IntWrapper::Create(i)));
+
+ ConservativelyCollectGarbage();
+ EXPECT_EQ(0, IntWrapper::destructor_calls_);
+ EXPECT_EQ(kStackSize, stack->size());
+ while (!stack->IsEmpty()) {
+ EXPECT_EQ(stack->size() - 1,
+ static_cast<size_t>(stack->Peek().Payload()->Value()));
+ stack->Pop();
+ }
+
+ Persistent<HeapLinkedStack<TerminatedArrayItem>> p_stack = stack;
+
+ PreciselyCollectGarbage();
+ EXPECT_EQ(kStackSize, static_cast<size_t>(IntWrapper::destructor_calls_));
+ EXPECT_EQ(0u, p_stack->size());
+}
+
+TEST(HeapTest, AllocationDuringFinalization) {
+ ClearOutOldGarbage();
+ IntWrapper::destructor_calls_ = 0;
+ OneKiloByteObject::destructor_calls_ = 0;
+ LargeHeapObject::destructor_calls_ = 0;
+
+ Persistent<IntWrapper> wrapper;
+ new FinalizationAllocator(&wrapper);
+
+ PreciselyCollectGarbage();
+ EXPECT_EQ(0, IntWrapper::destructor_calls_);
+ EXPECT_EQ(0, OneKiloByteObject::destructor_calls_);
+ EXPECT_EQ(0, LargeHeapObject::destructor_calls_);
+ // Check that the wrapper allocated during finalization is not
+ // swept away and zapped later in the same sweeping phase.
+ EXPECT_EQ(42, wrapper->Value());
+
+ wrapper.Clear();
+ PreciselyCollectGarbage();
+ // The 42 IntWrappers were the ones allocated in ~FinalizationAllocator
+ // and the ones allocated in LargeHeapObject.
+ EXPECT_EQ(42, IntWrapper::destructor_calls_);
+ EXPECT_EQ(512, OneKiloByteObject::destructor_calls_);
+ EXPECT_EQ(32, LargeHeapObject::destructor_calls_);
+}
+
+TEST(HeapTest, AllocationDuringPrefinalizer) {
+ ClearOutOldGarbage();
+ IntWrapper::destructor_calls_ = 0;
+ OneKiloByteObject::destructor_calls_ = 0;
+ LargeHeapObject::destructor_calls_ = 0;
+
+ Persistent<IntWrapper> wrapper;
+ new PreFinalizationAllocator(&wrapper);
+
+ PreciselyCollectGarbage();
+ EXPECT_EQ(0, IntWrapper::destructor_calls_);
+ EXPECT_EQ(0, OneKiloByteObject::destructor_calls_);
+ EXPECT_EQ(0, LargeHeapObject::destructor_calls_);
+ // Check that the wrapper allocated during finalization is not
+ // swept away and zapped later in the same sweeping phase.
+ EXPECT_EQ(42, wrapper->Value());
+
+ wrapper.Clear();
+ PreciselyCollectGarbage();
+ // The 42 IntWrappers were the ones allocated in the pre-finalizer
+ // of PreFinalizationAllocator and the ones allocated in LargeHeapObject.
+ EXPECT_EQ(42, IntWrapper::destructor_calls_);
+ EXPECT_EQ(512, OneKiloByteObject::destructor_calls_);
+ EXPECT_EQ(32, LargeHeapObject::destructor_calls_);
+}
+
+class SimpleClassWithDestructor {
+ public:
+ SimpleClassWithDestructor() = default;
+ ~SimpleClassWithDestructor() { was_destructed_ = true; }
+ static bool was_destructed_;
+};
+
+bool SimpleClassWithDestructor::was_destructed_;
+
+class RefCountedWithDestructor : public RefCounted<RefCountedWithDestructor> {
+ public:
+ RefCountedWithDestructor() = default;
+ ~RefCountedWithDestructor() { was_destructed_ = true; }
+ static bool was_destructed_;
+};
+
+bool RefCountedWithDestructor::was_destructed_;
+
+template <typename Set>
+void DestructorsCalledOnGC(bool add_lots) {
+ RefCountedWithDestructor::was_destructed_ = false;
+ {
+ Set set;
+ RefCountedWithDestructor* has_destructor = new RefCountedWithDestructor();
+ set.Add(base::AdoptRef(has_destructor));
+ EXPECT_FALSE(RefCountedWithDestructor::was_destructed_);
+
+ if (add_lots) {
+ for (int i = 0; i < 1000; i++) {
+ set.Add(base::AdoptRef(new RefCountedWithDestructor()));
+ }
+ }
+
+ EXPECT_FALSE(RefCountedWithDestructor::was_destructed_);
+ ConservativelyCollectGarbage();
+ EXPECT_FALSE(RefCountedWithDestructor::was_destructed_);
+ }
+ // The destructors of the sets don't call the destructors of the elements
+ // in the heap sets. You have to actually remove the elments, call clear()
+ // or have a GC to get the destructors called.
+ EXPECT_FALSE(RefCountedWithDestructor::was_destructed_);
+ PreciselyCollectGarbage();
+ EXPECT_TRUE(RefCountedWithDestructor::was_destructed_);
+}
+
+template <typename Set>
+void DestructorsCalledOnClear(bool add_lots) {
+ RefCountedWithDestructor::was_destructed_ = false;
+ Set set;
+ RefCountedWithDestructor* has_destructor = new RefCountedWithDestructor();
+ set.Add(base::AdoptRef(has_destructor));
+ EXPECT_FALSE(RefCountedWithDestructor::was_destructed_);
+
+ if (add_lots) {
+ for (int i = 0; i < 1000; i++) {
+ set.Add(base::AdoptRef(new RefCountedWithDestructor()));
+ }
+ }
+
+ EXPECT_FALSE(RefCountedWithDestructor::was_destructed_);
+ set.Clear();
+ EXPECT_TRUE(RefCountedWithDestructor::was_destructed_);
+}
+
+TEST(HeapTest, DestructorsCalled) {
+ HeapHashMap<Member<IntWrapper>, std::unique_ptr<SimpleClassWithDestructor>>
+ map;
+ SimpleClassWithDestructor* has_destructor = new SimpleClassWithDestructor();
+ map.insert(IntWrapper::Create(1), base::WrapUnique(has_destructor));
+ SimpleClassWithDestructor::was_destructed_ = false;
+ map.clear();
+ EXPECT_TRUE(SimpleClassWithDestructor::was_destructed_);
+}
+
+class MixinA : public GarbageCollectedMixin {
+ public:
+ MixinA() : obj_(IntWrapper::Create(100)) {}
+ virtual void Trace(blink::Visitor* visitor) {
+ trace_count_++;
+ visitor->Trace(obj_);
+ }
+
+ static int trace_count_;
+
+ Member<IntWrapper> obj_;
+};
+
+int MixinA::trace_count_ = 0;
+
+class MixinB : public GarbageCollectedMixin {
+ public:
+ MixinB() : obj_(IntWrapper::Create(101)) {}
+ virtual void Trace(blink::Visitor* visitor) { visitor->Trace(obj_); }
+ Member<IntWrapper> obj_;
+};
+
+class MultipleMixins : public GarbageCollected<MultipleMixins>,
+ public MixinA,
+ public MixinB {
+ USING_GARBAGE_COLLECTED_MIXIN(MultipleMixins);
+
+ public:
+ MultipleMixins() : obj_(IntWrapper::Create(102)) {}
+ virtual void Trace(blink::Visitor* visitor) {
+ visitor->Trace(obj_);
+ MixinA::Trace(visitor);
+ MixinB::Trace(visitor);
+ }
+ Member<IntWrapper> obj_;
+};
+
+class DerivedMultipleMixins : public MultipleMixins {
+ public:
+ DerivedMultipleMixins() : obj_(IntWrapper::Create(103)) {}
+
+ virtual void Trace(blink::Visitor* visitor) {
+ trace_called_++;
+ visitor->Trace(obj_);
+ MultipleMixins::Trace(visitor);
+ }
+
+ static int trace_called_;
+
+ private:
+ Member<IntWrapper> obj_;
+};
+
+int DerivedMultipleMixins::trace_called_ = 0;
+
+static const bool kIsMixinTrue = IsGarbageCollectedMixin<MultipleMixins>::value;
+static const bool kIsMixinFalse = IsGarbageCollectedMixin<IntWrapper>::value;
+
+TEST(HeapTest, MultipleMixins) {
+ EXPECT_TRUE(kIsMixinTrue);
+ EXPECT_FALSE(kIsMixinFalse);
+
+ ClearOutOldGarbage();
+ IntWrapper::destructor_calls_ = 0;
+ MultipleMixins* obj = new MultipleMixins();
+ {
+ Persistent<MixinA> a = obj;
+ PreciselyCollectGarbage();
+ EXPECT_EQ(0, IntWrapper::destructor_calls_);
+ }
+ {
+ Persistent<MixinB> b = obj;
+ PreciselyCollectGarbage();
+ EXPECT_EQ(0, IntWrapper::destructor_calls_);
+ }
+ PreciselyCollectGarbage();
+ EXPECT_EQ(3, IntWrapper::destructor_calls_);
+}
+
+TEST(HeapTest, DerivedMultipleMixins) {
+ ClearOutOldGarbage();
+ IntWrapper::destructor_calls_ = 0;
+ DerivedMultipleMixins::trace_called_ = 0;
+
+ DerivedMultipleMixins* obj = new DerivedMultipleMixins();
+ {
+ Persistent<MixinA> a = obj;
+ PreciselyCollectGarbage();
+ EXPECT_EQ(0, IntWrapper::destructor_calls_);
+ EXPECT_LT(0, DerivedMultipleMixins::trace_called_);
+ }
+ {
+ Persistent<MixinB> b = obj;
+ PreciselyCollectGarbage();
+ EXPECT_EQ(0, IntWrapper::destructor_calls_);
+ EXPECT_LT(0, DerivedMultipleMixins::trace_called_);
+ }
+ PreciselyCollectGarbage();
+ EXPECT_EQ(4, IntWrapper::destructor_calls_);
+}
+
+class MixinInstanceWithoutTrace
+ : public GarbageCollected<MixinInstanceWithoutTrace>,
+ public MixinA {
+ USING_GARBAGE_COLLECTED_MIXIN(MixinInstanceWithoutTrace);
+
+ public:
+ MixinInstanceWithoutTrace() = default;
+};
+
+TEST(HeapTest, MixinInstanceWithoutTrace) {
+ // Verify that a mixin instance without any traceable
+ // references inherits the mixin's trace implementation.
+ ClearOutOldGarbage();
+ MixinA::trace_count_ = 0;
+ MixinInstanceWithoutTrace* obj = new MixinInstanceWithoutTrace();
+ int saved_trace_count = 0;
+ {
+ Persistent<MixinA> a = obj;
+ PreciselyCollectGarbage();
+ saved_trace_count = MixinA::trace_count_;
+ EXPECT_LT(0, saved_trace_count);
+ }
+ {
+ Persistent<MixinInstanceWithoutTrace> b = obj;
+ PreciselyCollectGarbage();
+ EXPECT_LT(saved_trace_count, MixinA::trace_count_);
+ saved_trace_count = MixinA::trace_count_;
+ }
+ PreciselyCollectGarbage();
+ // Oilpan might still call trace on dead objects for various reasons which is
+ // valid before sweeping started.
+ EXPECT_LE(saved_trace_count, MixinA::trace_count_);
+}
+
+TEST(HeapTest, NeedsAdjustPointer) {
+ // class Mixin : public GarbageCollectedMixin {};
+ static_assert(NeedsAdjustPointer<Mixin>::value,
+ "A Mixin pointer needs adjustment");
+ static_assert(NeedsAdjustPointer<Mixin>::value,
+ "A const Mixin pointer needs adjustment");
+
+ // class SimpleObject : public GarbageCollected<SimpleObject> {};
+ static_assert(!NeedsAdjustPointer<SimpleObject>::value,
+ "A SimpleObject pointer does not need adjustment");
+ static_assert(!NeedsAdjustPointer<const SimpleObject>::value,
+ "A const SimpleObject pointer does not need adjustment");
+
+ // class UseMixin : public SimpleObject, public Mixin {};
+ static_assert(!NeedsAdjustPointer<UseMixin>::value,
+ "A UseMixin pointer does not need adjustment");
+ static_assert(!NeedsAdjustPointer<const UseMixin>::value,
+ "A const UseMixin pointer does not need adjustment");
+}
+
+template <typename Set>
+void SetWithCustomWeaknessHandling() {
+ typedef typename Set::iterator Iterator;
+ Persistent<IntWrapper> living_int(IntWrapper::Create(42));
+ Persistent<Set> set1(new Set());
+ {
+ Set set2;
+ Set* set3 = new Set();
+ set2.insert(
+ PairWithWeakHandling(IntWrapper::Create(0), IntWrapper::Create(1)));
+ set3->insert(
+ PairWithWeakHandling(IntWrapper::Create(2), IntWrapper::Create(3)));
+ set1->insert(
+ PairWithWeakHandling(IntWrapper::Create(4), IntWrapper::Create(5)));
+ ConservativelyCollectGarbage();
+ // The first set is pointed to from a persistent, so it's referenced, but
+ // the weak processing may have taken place.
+ if (set1->size()) {
+ Iterator i1 = set1->begin();
+ EXPECT_EQ(4, i1->first->Value());
+ EXPECT_EQ(5, i1->second->Value());
+ }
+ // The second set is on-stack, so its backing store must be referenced from
+ // the stack. That makes the weak references strong.
+ Iterator i2 = set2.begin();
+ EXPECT_EQ(0, i2->first->Value());
+ EXPECT_EQ(1, i2->second->Value());
+ // The third set is pointed to from the stack, so it's referenced, but the
+ // weak processing may have taken place.
+ if (set3->size()) {
+ Iterator i3 = set3->begin();
+ EXPECT_EQ(2, i3->first->Value());
+ EXPECT_EQ(3, i3->second->Value());
+ }
+ }
+ PreciselyCollectGarbage();
+ EXPECT_EQ(0u, set1->size());
+ set1->insert(PairWithWeakHandling(IntWrapper::Create(103), living_int));
+ // This one gets zapped at GC time because nothing holds the 103 alive.
+ set1->insert(PairWithWeakHandling(living_int, IntWrapper::Create(103)));
+ set1->insert(PairWithWeakHandling(
+ IntWrapper::Create(103),
+ IntWrapper::Create(103))); // This one gets zapped too.
+ set1->insert(PairWithWeakHandling(living_int, living_int));
+ // This one is identical to the previous and doesn't add anything.
+ set1->insert(PairWithWeakHandling(living_int, living_int));
+ EXPECT_EQ(4u, set1->size());
+ PreciselyCollectGarbage();
+ EXPECT_EQ(2u, set1->size());
+ Iterator i1 = set1->begin();
+ EXPECT_TRUE(i1->first->Value() == 103 || i1->first == living_int);
+ EXPECT_EQ(living_int, i1->second);
+ ++i1;
+ EXPECT_TRUE(i1->first->Value() == 103 || i1->first == living_int);
+ EXPECT_EQ(living_int, i1->second);
+}
+
+TEST(HeapTest, SetWithCustomWeaknessHandling) {
+ SetWithCustomWeaknessHandling<HeapHashSet<PairWithWeakHandling>>();
+ SetWithCustomWeaknessHandling<HeapLinkedHashSet<PairWithWeakHandling>>();
+}
+
+TEST(HeapTest, MapWithCustomWeaknessHandling) {
+ typedef HeapHashMap<PairWithWeakHandling, scoped_refptr<OffHeapInt>> Map;
+ typedef Map::iterator Iterator;
+ ClearOutOldGarbage();
+ OffHeapInt::destructor_calls_ = 0;
+
+ Persistent<Map> map1(new Map());
+ Persistent<IntWrapper> living_int(IntWrapper::Create(42));
+ {
+ Map map2;
+ Map* map3 = new Map();
+ map2.insert(
+ PairWithWeakHandling(IntWrapper::Create(0), IntWrapper::Create(1)),
+ OffHeapInt::Create(1001));
+ map3->insert(
+ PairWithWeakHandling(IntWrapper::Create(2), IntWrapper::Create(3)),
+ OffHeapInt::Create(1002));
+ map1->insert(
+ PairWithWeakHandling(IntWrapper::Create(4), IntWrapper::Create(5)),
+ OffHeapInt::Create(1003));
+ EXPECT_EQ(0, OffHeapInt::destructor_calls_);
+
+ ConservativelyCollectGarbage();
+ // The first map2 is pointed to from a persistent, so it's referenced, but
+ // the weak processing may have taken place.
+ if (map1->size()) {
+ Iterator i1 = map1->begin();
+ EXPECT_EQ(4, i1->key.first->Value());
+ EXPECT_EQ(5, i1->key.second->Value());
+ EXPECT_EQ(1003, i1->value->Value());
+ }
+ // The second map2 is on-stack, so its backing store must be referenced from
+ // the stack. That makes the weak references strong.
+ Iterator i2 = map2.begin();
+ EXPECT_EQ(0, i2->key.first->Value());
+ EXPECT_EQ(1, i2->key.second->Value());
+ EXPECT_EQ(1001, i2->value->Value());
+ // The third map2 is pointed to from the stack, so it's referenced, but the
+ // weak processing may have taken place.
+ if (map3->size()) {
+ Iterator i3 = map3->begin();
+ EXPECT_EQ(2, i3->key.first->Value());
+ EXPECT_EQ(3, i3->key.second->Value());
+ EXPECT_EQ(1002, i3->value->Value());
+ }
+ }
+ PreciselyCollectGarbage();
+
+ EXPECT_EQ(0u, map1->size());
+ EXPECT_EQ(3, OffHeapInt::destructor_calls_);
+
+ OffHeapInt::destructor_calls_ = 0;
+
+ map1->insert(PairWithWeakHandling(IntWrapper::Create(103), living_int),
+ OffHeapInt::Create(2000));
+ map1->insert(PairWithWeakHandling(living_int, IntWrapper::Create(103)),
+ OffHeapInt::Create(2001)); // This one gets zapped at GC time
+ // because nothing holds the 103 alive.
+ map1->insert(
+ PairWithWeakHandling(IntWrapper::Create(103), IntWrapper::Create(103)),
+ OffHeapInt::Create(2002)); // This one gets zapped too.
+ scoped_refptr<OffHeapInt> dupe_int(OffHeapInt::Create(2003));
+ map1->insert(PairWithWeakHandling(living_int, living_int), dupe_int);
+ map1->insert(
+ PairWithWeakHandling(living_int, living_int),
+ dupe_int); // This one is identical to the previous and doesn't add
+ // anything.
+ dupe_int = nullptr;
+
+ EXPECT_EQ(0, OffHeapInt::destructor_calls_);
+ EXPECT_EQ(4u, map1->size());
+ PreciselyCollectGarbage();
+ EXPECT_EQ(2, OffHeapInt::destructor_calls_);
+ EXPECT_EQ(2u, map1->size());
+ Iterator i1 = map1->begin();
+ EXPECT_TRUE(i1->key.first->Value() == 103 || i1->key.first == living_int);
+ EXPECT_EQ(living_int, i1->key.second);
+ ++i1;
+ EXPECT_TRUE(i1->key.first->Value() == 103 || i1->key.first == living_int);
+ EXPECT_EQ(living_int, i1->key.second);
+}
+
+TEST(HeapTest, MapWithCustomWeaknessHandling2) {
+ typedef HeapHashMap<scoped_refptr<OffHeapInt>, PairWithWeakHandling> Map;
+ typedef Map::iterator Iterator;
+ ClearOutOldGarbage();
+ OffHeapInt::destructor_calls_ = 0;
+
+ Persistent<Map> map1(new Map());
+ Persistent<IntWrapper> living_int(IntWrapper::Create(42));
+
+ {
+ Map map2;
+ Map* map3 = new Map();
+ map2.insert(
+ OffHeapInt::Create(1001),
+ PairWithWeakHandling(IntWrapper::Create(0), IntWrapper::Create(1)));
+ map3->insert(
+ OffHeapInt::Create(1002),
+ PairWithWeakHandling(IntWrapper::Create(2), IntWrapper::Create(3)));
+ map1->insert(
+ OffHeapInt::Create(1003),
+ PairWithWeakHandling(IntWrapper::Create(4), IntWrapper::Create(5)));
+ EXPECT_EQ(0, OffHeapInt::destructor_calls_);
+
+ ConservativelyCollectGarbage();
+ // The first map2 is pointed to from a persistent, so it's referenced, but
+ // the weak processing may have taken place.
+ if (map1->size()) {
+ Iterator i1 = map1->begin();
+ EXPECT_EQ(4, i1->value.first->Value());
+ EXPECT_EQ(5, i1->value.second->Value());
+ EXPECT_EQ(1003, i1->key->Value());
+ }
+ // The second map2 is on-stack, so its backing store must be referenced from
+ // the stack. That makes the weak references strong.
+ Iterator i2 = map2.begin();
+ EXPECT_EQ(0, i2->value.first->Value());
+ EXPECT_EQ(1, i2->value.second->Value());
+ EXPECT_EQ(1001, i2->key->Value());
+ // The third map2 is pointed to from the stack, so it's referenced, but the
+ // weak processing may have taken place.
+ if (map3->size()) {
+ Iterator i3 = map3->begin();
+ EXPECT_EQ(2, i3->value.first->Value());
+ EXPECT_EQ(3, i3->value.second->Value());
+ EXPECT_EQ(1002, i3->key->Value());
+ }
+ }
+ PreciselyCollectGarbage();
+
+ EXPECT_EQ(0u, map1->size());
+ EXPECT_EQ(3, OffHeapInt::destructor_calls_);
+
+ OffHeapInt::destructor_calls_ = 0;
+
+ map1->insert(OffHeapInt::Create(2000),
+ PairWithWeakHandling(IntWrapper::Create(103), living_int));
+ // This one gets zapped at GC time because nothing holds the 103 alive.
+ map1->insert(OffHeapInt::Create(2001),
+ PairWithWeakHandling(living_int, IntWrapper::Create(103)));
+ map1->insert(OffHeapInt::Create(2002),
+ PairWithWeakHandling(
+ IntWrapper::Create(103),
+ IntWrapper::Create(103))); // This one gets zapped too.
+ scoped_refptr<OffHeapInt> dupe_int(OffHeapInt::Create(2003));
+ map1->insert(dupe_int, PairWithWeakHandling(living_int, living_int));
+ // This one is identical to the previous and doesn't add anything.
+ map1->insert(dupe_int, PairWithWeakHandling(living_int, living_int));
+ dupe_int = nullptr;
+
+ EXPECT_EQ(0, OffHeapInt::destructor_calls_);
+ EXPECT_EQ(4u, map1->size());
+ PreciselyCollectGarbage();
+ EXPECT_EQ(2, OffHeapInt::destructor_calls_);
+ EXPECT_EQ(2u, map1->size());
+ Iterator i1 = map1->begin();
+ EXPECT_TRUE(i1->value.first->Value() == 103 || i1->value.first == living_int);
+ EXPECT_EQ(living_int, i1->value.second);
+ ++i1;
+ EXPECT_TRUE(i1->value.first->Value() == 103 || i1->value.first == living_int);
+ EXPECT_EQ(living_int, i1->value.second);
+}
+
+static void AddElementsToWeakMap(
+ HeapHashMap<int, WeakMember<IntWrapper>>* map) {
+ // Key cannot be zero in hashmap.
+ for (int i = 1; i < 11; i++)
+ map->insert(i, IntWrapper::Create(i));
+}
+
+// crbug.com/402426
+// If it doesn't assert a concurrent modification to the map, then it's passing.
+TEST(HeapTest, RegressNullIsStrongified) {
+ Persistent<HeapHashMap<int, WeakMember<IntWrapper>>> map =
+ new HeapHashMap<int, WeakMember<IntWrapper>>();
+ AddElementsToWeakMap(map);
+ HeapHashMap<int, WeakMember<IntWrapper>>::AddResult result =
+ map->insert(800, nullptr);
+ ConservativelyCollectGarbage();
+ result.stored_value->value = IntWrapper::Create(42);
+}
+
+TEST(HeapTest, Bind) {
+ base::OnceClosure closure =
+ WTF::Bind(static_cast<void (Bar::*)(Visitor*)>(&Bar::Trace),
+ WrapPersistent(Bar::Create()), nullptr);
+ // OffHeapInt* should not make Persistent.
+ base::OnceClosure closure2 =
+ WTF::Bind(&OffHeapInt::VoidFunction, OffHeapInt::Create(1));
+ PreciselyCollectGarbage();
+ // The closure should have a persistent handle to the Bar.
+ EXPECT_EQ(1u, Bar::live_);
+
+ UseMixin::trace_count_ = 0;
+ Mixin* mixin = UseMixin::Create();
+ base::OnceClosure mixin_closure =
+ WTF::Bind(static_cast<void (Mixin::*)(Visitor*)>(&Mixin::Trace),
+ WrapPersistent(mixin), nullptr);
+ PreciselyCollectGarbage();
+ // The closure should have a persistent handle to the mixin.
+ EXPECT_EQ(1, UseMixin::trace_count_);
+}
+
+typedef HeapHashSet<WeakMember<IntWrapper>> WeakSet;
+
+// These special traits will remove a set from a map when the set is empty.
+struct EmptyClearingHashSetTraits : HashTraits<WeakSet> {
+ static const WTF::WeakHandlingFlag kWeakHandlingFlag = WTF::kWeakHandling;
+
+ static bool IsAlive(WeakSet& set) {
+ bool live_entries_found = false;
+ WeakSet::iterator end = set.end();
+ for (WeakSet::iterator it = set.begin(); it != end; ++it) {
+ if (ThreadHeap::IsHeapObjectAlive(*it)) {
+ live_entries_found = true;
+ break;
+ }
+ }
+ return live_entries_found;
+ }
+
+ template <typename VisitorDispatcher>
+ static bool TraceInCollection(VisitorDispatcher visitor,
+ WeakSet& set,
+ WTF::WeakHandlingFlag weakenss) {
+ bool live_entries_found = false;
+ WeakSet::iterator end = set.end();
+ for (WeakSet::iterator it = set.begin(); it != end; ++it) {
+ if (ThreadHeap::IsHeapObjectAlive(*it)) {
+ live_entries_found = true;
+ break;
+ }
+ }
+ // If there are live entries in the set then the set cannot be removed
+ // from the map it is contained in, and we need to mark it (and its
+ // backing) live. We just trace normally, which will invoke the normal
+ // weak handling for any entries that are not live.
+ if (live_entries_found)
+ set.Trace(visitor);
+ return !live_entries_found;
+ }
+};
+
+// This is an example to show how you can remove entries from a T->WeakSet map
+// when the weak sets become empty. For this example we are using a type that
+// is given to use (HeapHashSet) rather than a type of our own. This means:
+// 1) We can't just override the HashTrait for the type since this would affect
+// all collections that use this kind of weak set. Instead we have our own
+// traits and use a map with custom traits for the value type. These traits
+// are the 5th template parameter, so we have to supply default values for
+// the 3rd and 4th template parameters
+// 2) We can't just inherit from WeakHandlingHashTraits, since that trait
+// assumes we can add methods to the type, but we can't add methods to
+// HeapHashSet.
+TEST(HeapTest, RemoveEmptySets) {
+ ClearOutOldGarbage();
+ OffHeapInt::destructor_calls_ = 0;
+
+ Persistent<IntWrapper> living_int(IntWrapper::Create(42));
+
+ typedef scoped_refptr<OffHeapInt> Key;
+ typedef HeapHashMap<Key, WeakSet, WTF::DefaultHash<Key>::Hash,
+ HashTraits<Key>, EmptyClearingHashSetTraits>
+ Map;
+ Persistent<Map> map(new Map());
+ map->insert(OffHeapInt::Create(1), WeakSet());
+ {
+ WeakSet& set = map->begin()->value;
+ set.insert(IntWrapper::Create(103)); // Weak set can't hold this long.
+ set.insert(living_int); // This prevents the set from being emptied.
+ EXPECT_EQ(2u, set.size());
+ }
+
+ // The set we add here is empty, so the entry will be removed from the map
+ // at the next GC.
+ map->insert(OffHeapInt::Create(2), WeakSet());
+ EXPECT_EQ(2u, map->size());
+
+ PreciselyCollectGarbage();
+ EXPECT_EQ(1u, map->size()); // The one with key 2 was removed.
+ EXPECT_EQ(1, OffHeapInt::destructor_calls_);
+ {
+ WeakSet& set = map->begin()->value;
+ EXPECT_EQ(1u, set.size());
+ }
+
+ living_int.Clear(); // The weak set can no longer keep the '42' alive now.
+ PreciselyCollectGarbage();
+ EXPECT_EQ(0u, map->size());
+}
+
+TEST(HeapTest, EphemeronsInEphemerons) {
+ typedef HeapHashMap<WeakMember<IntWrapper>, Member<IntWrapper>> InnerMap;
+ typedef HeapHashMap<WeakMember<IntWrapper>, InnerMap> OuterMap;
+
+ for (int keep_outer_alive = 0; keep_outer_alive <= 1; keep_outer_alive++) {
+ for (int keep_inner_alive = 0; keep_inner_alive <= 1; keep_inner_alive++) {
+ Persistent<OuterMap> outer = new OuterMap();
+ Persistent<IntWrapper> one = IntWrapper::Create(1);
+ Persistent<IntWrapper> two = IntWrapper::Create(2);
+ outer->insert(one, InnerMap());
+ outer->begin()->value.insert(two, IntWrapper::Create(3));
+ EXPECT_EQ(1u, outer->at(one).size());
+ if (!keep_outer_alive)
+ one.Clear();
+ if (!keep_inner_alive)
+ two.Clear();
+ PreciselyCollectGarbage();
+ if (keep_outer_alive) {
+ const InnerMap& inner = outer->at(one);
+ if (keep_inner_alive) {
+ EXPECT_EQ(1u, inner.size());
+ IntWrapper* three = inner.at(two);
+ EXPECT_EQ(3, three->Value());
+ } else {
+ EXPECT_EQ(0u, inner.size());
+ }
+ } else {
+ EXPECT_EQ(0u, outer->size());
+ }
+ outer->clear();
+ Persistent<IntWrapper> deep = IntWrapper::Create(42);
+ Persistent<IntWrapper> home = IntWrapper::Create(103);
+ Persistent<IntWrapper> composite = IntWrapper::Create(91);
+ Persistent<HeapVector<Member<IntWrapper>>> keep_alive =
+ new HeapVector<Member<IntWrapper>>();
+ for (int i = 0; i < 10000; i++) {
+ IntWrapper* value = IntWrapper::Create(i);
+ keep_alive->push_back(value);
+ OuterMap::AddResult new_entry = outer->insert(value, InnerMap());
+ new_entry.stored_value->value.insert(deep, home);
+ new_entry.stored_value->value.insert(composite, home);
+ }
+ composite.Clear();
+ PreciselyCollectGarbage();
+ EXPECT_EQ(10000u, outer->size());
+ for (int i = 0; i < 10000; i++) {
+ IntWrapper* value = keep_alive->at(i);
+ EXPECT_EQ(1u,
+ outer->at(value)
+ .size()); // Other one was deleted by weak handling.
+ if (i & 1)
+ keep_alive->at(i) = nullptr;
+ }
+ PreciselyCollectGarbage();
+ EXPECT_EQ(5000u, outer->size());
+ }
+ }
+}
+
+class EphemeronWrapper : public GarbageCollected<EphemeronWrapper> {
+ public:
+ void Trace(blink::Visitor* visitor) { visitor->Trace(map_); }
+
+ typedef HeapHashMap<WeakMember<IntWrapper>, Member<EphemeronWrapper>> Map;
+ Map& GetMap() { return map_; }
+
+ private:
+ Map map_;
+};
+
+TEST(HeapTest, EphemeronsPointToEphemerons) {
+ Persistent<IntWrapper> key = IntWrapper::Create(42);
+ Persistent<IntWrapper> key2 = IntWrapper::Create(103);
+
+ Persistent<EphemeronWrapper> chain;
+ for (int i = 0; i < 100; i++) {
+ EphemeronWrapper* old_head = chain;
+ chain = new EphemeronWrapper();
+ if (i == 50)
+ chain->GetMap().insert(key2, old_head);
+ else
+ chain->GetMap().insert(key, old_head);
+ chain->GetMap().insert(IntWrapper::Create(103), new EphemeronWrapper());
+ }
+
+ PreciselyCollectGarbage();
+
+ EphemeronWrapper* wrapper = chain;
+ for (int i = 0; i < 100; i++) {
+ EXPECT_EQ(1u, wrapper->GetMap().size());
+ if (i == 49)
+ wrapper = wrapper->GetMap().at(key2);
+ else
+ wrapper = wrapper->GetMap().at(key);
+ }
+ EXPECT_EQ(nullptr, wrapper);
+
+ key2.Clear();
+ PreciselyCollectGarbage();
+
+ wrapper = chain;
+ for (int i = 0; i < 50; i++) {
+ EXPECT_EQ(i == 49 ? 0u : 1u, wrapper->GetMap().size());
+ wrapper = wrapper->GetMap().at(key);
+ }
+ EXPECT_EQ(nullptr, wrapper);
+
+ key.Clear();
+ PreciselyCollectGarbage();
+ EXPECT_EQ(0u, chain->GetMap().size());
+}
+
+TEST(HeapTest, Ephemeron) {
+ typedef HeapHashMap<WeakMember<IntWrapper>, PairWithWeakHandling> WeakPairMap;
+ typedef HeapHashMap<PairWithWeakHandling, WeakMember<IntWrapper>> PairWeakMap;
+ typedef HeapHashSet<WeakMember<IntWrapper>> Set;
+
+ Persistent<WeakPairMap> weak_pair_map = new WeakPairMap();
+ Persistent<WeakPairMap> weak_pair_map2 = new WeakPairMap();
+ Persistent<WeakPairMap> weak_pair_map3 = new WeakPairMap();
+ Persistent<WeakPairMap> weak_pair_map4 = new WeakPairMap();
+
+ Persistent<PairWeakMap> pair_weak_map = new PairWeakMap();
+ Persistent<PairWeakMap> pair_weak_map2 = new PairWeakMap();
+
+ Persistent<Set> set = new Set();
+
+ Persistent<IntWrapper> wp1 = IntWrapper::Create(1);
+ Persistent<IntWrapper> wp2 = IntWrapper::Create(2);
+ Persistent<IntWrapper> pw1 = IntWrapper::Create(3);
+ Persistent<IntWrapper> pw2 = IntWrapper::Create(4);
+
+ weak_pair_map->insert(wp1, PairWithWeakHandling(wp1, wp1));
+ weak_pair_map->insert(wp2, PairWithWeakHandling(wp1, wp1));
+ weak_pair_map2->insert(wp1, PairWithWeakHandling(wp1, wp2));
+ weak_pair_map2->insert(wp2, PairWithWeakHandling(wp1, wp2));
+ // The map from wp1 to (wp2, wp1) would mark wp2 live, so we skip that.
+ weak_pair_map3->insert(wp2, PairWithWeakHandling(wp2, wp1));
+ weak_pair_map4->insert(wp1, PairWithWeakHandling(wp2, wp2));
+ weak_pair_map4->insert(wp2, PairWithWeakHandling(wp2, wp2));
+
+ pair_weak_map->insert(PairWithWeakHandling(pw1, pw1), pw1);
+ pair_weak_map->insert(PairWithWeakHandling(pw1, pw2), pw1);
+ // The map from (pw2, pw1) to pw1 would make pw2 live, so we skip that.
+ pair_weak_map->insert(PairWithWeakHandling(pw2, pw2), pw1);
+ pair_weak_map2->insert(PairWithWeakHandling(pw1, pw1), pw2);
+ pair_weak_map2->insert(PairWithWeakHandling(pw1, pw2), pw2);
+ pair_weak_map2->insert(PairWithWeakHandling(pw2, pw1), pw2);
+ pair_weak_map2->insert(PairWithWeakHandling(pw2, pw2), pw2);
+
+ set->insert(wp1);
+ set->insert(wp2);
+ set->insert(pw1);
+ set->insert(pw2);
+
+ PreciselyCollectGarbage();
+
+ EXPECT_EQ(2u, weak_pair_map->size());
+ EXPECT_EQ(2u, weak_pair_map2->size());
+ EXPECT_EQ(1u, weak_pair_map3->size());
+ EXPECT_EQ(2u, weak_pair_map4->size());
+
+ EXPECT_EQ(3u, pair_weak_map->size());
+ EXPECT_EQ(4u, pair_weak_map2->size());
+
+ EXPECT_EQ(4u, set->size());
+
+ wp2.Clear(); // Kills all entries in the weakPairMaps except the first.
+ pw2.Clear(); // Kills all entries in the pairWeakMaps except the first.
+
+ for (int i = 0; i < 2; i++) {
+ PreciselyCollectGarbage();
+
+ EXPECT_EQ(1u, weak_pair_map->size());
+ EXPECT_EQ(0u, weak_pair_map2->size());
+ EXPECT_EQ(0u, weak_pair_map3->size());
+ EXPECT_EQ(0u, weak_pair_map4->size());
+
+ EXPECT_EQ(1u, pair_weak_map->size());
+ EXPECT_EQ(0u, pair_weak_map2->size());
+
+ EXPECT_EQ(2u, set->size()); // wp1 and pw1.
+ }
+
+ wp1.Clear();
+ pw1.Clear();
+
+ PreciselyCollectGarbage();
+
+ EXPECT_EQ(0u, weak_pair_map->size());
+ EXPECT_EQ(0u, pair_weak_map->size());
+ EXPECT_EQ(0u, set->size());
+}
+
+class Link1 : public GarbageCollected<Link1> {
+ public:
+ Link1(IntWrapper* link) : link_(link) {}
+
+ void Trace(blink::Visitor* visitor) { visitor->Trace(link_); }
+
+ IntWrapper* Link() { return link_; }
+
+ private:
+ Member<IntWrapper> link_;
+};
+
+TEST(HeapTest, IndirectStrongToWeak) {
+ typedef HeapHashMap<WeakMember<IntWrapper>, Member<Link1>> Map;
+ Persistent<Map> map = new Map();
+ Persistent<IntWrapper> dead_object =
+ IntWrapper::Create(100); // Named for "Drowning by Numbers" (1988).
+ Persistent<IntWrapper> life_object = IntWrapper::Create(42);
+ map->insert(dead_object, new Link1(dead_object));
+ map->insert(life_object, new Link1(life_object));
+ EXPECT_EQ(2u, map->size());
+ PreciselyCollectGarbage();
+ EXPECT_EQ(2u, map->size());
+ EXPECT_EQ(dead_object, map->at(dead_object)->Link());
+ EXPECT_EQ(life_object, map->at(life_object)->Link());
+ dead_object.Clear(); // Now it can live up to its name.
+ PreciselyCollectGarbage();
+ EXPECT_EQ(1u, map->size());
+ EXPECT_EQ(life_object, map->at(life_object)->Link());
+ life_object.Clear(); // Despite its name.
+ PreciselyCollectGarbage();
+ EXPECT_EQ(0u, map->size());
+}
+
+static Mutex& MainThreadMutex() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(Mutex, main_mutex, ());
+ return main_mutex;
+}
+
+static ThreadCondition& MainThreadCondition() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadCondition, main_condition, ());
+ return main_condition;
+}
+
+static void ParkMainThread() {
+ MainThreadCondition().Wait(MainThreadMutex());
+}
+
+static void WakeMainThread() {
+ MutexLocker locker(MainThreadMutex());
+ MainThreadCondition().Signal();
+}
+
+static Mutex& WorkerThreadMutex() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(Mutex, worker_mutex, ());
+ return worker_mutex;
+}
+
+static ThreadCondition& WorkerThreadCondition() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadCondition, worker_condition, ());
+ return worker_condition;
+}
+
+static void ParkWorkerThread() {
+ WorkerThreadCondition().Wait(WorkerThreadMutex());
+}
+
+static void WakeWorkerThread() {
+ MutexLocker locker(WorkerThreadMutex());
+ WorkerThreadCondition().Signal();
+}
+
+class ThreadedStrongificationTester {
+ public:
+ static void Test() {
+ IntWrapper::destructor_calls_ = 0;
+
+ MutexLocker locker(MainThreadMutex());
+ std::unique_ptr<WebThread> worker_thread =
+ Platform::Current()->CreateThread(
+ WebThreadCreationParams(WebThreadType::kTestThread)
+ .SetThreadNameForTest("Test Worker Thread"));
+ PostCrossThreadTask(*worker_thread->GetTaskRunner(), FROM_HERE,
+ CrossThreadBind(WorkerThreadMain));
+
+ // Wait for the worker thread initialization. The worker
+ // allocates a weak collection where both collection and
+ // contents are kept alive via persistent pointers.
+ ParkMainThread();
+
+ // Perform two garbage collections where the worker thread does
+ // not wake up in between. This will cause us to remove marks
+ // and mark unmarked objects dead. The collection on the worker
+ // heap is found through the persistent and the backing should
+ // be marked.
+ PreciselyCollectGarbage();
+ PreciselyCollectGarbage();
+
+ // Wake up the worker thread so it can continue. It will sweep
+ // and perform another GC where the backing store of its
+ // collection should be strongified.
+ WakeWorkerThread();
+
+ // Wait for the worker thread to sweep its heaps before checking.
+ ParkMainThread();
+ }
+
+ private:
+ using WeakCollectionType =
+ HeapHashMap<WeakMember<IntWrapper>, Member<IntWrapper>>;
+
+ static WeakCollectionType* AllocateCollection() {
+ // Create a weak collection that is kept alive by a persistent
+ // and keep the contents alive with a persistents as
+ // well.
+ Persistent<IntWrapper> wrapper1 = IntWrapper::Create(32);
+ Persistent<IntWrapper> wrapper2 = IntWrapper::Create(32);
+ Persistent<IntWrapper> wrapper3 = IntWrapper::Create(32);
+ Persistent<IntWrapper> wrapper4 = IntWrapper::Create(32);
+ Persistent<IntWrapper> wrapper5 = IntWrapper::Create(32);
+ Persistent<IntWrapper> wrapper6 = IntWrapper::Create(32);
+ Persistent<WeakCollectionType> weak_collection = new WeakCollectionType;
+ weak_collection->insert(wrapper1, wrapper1);
+ weak_collection->insert(wrapper2, wrapper2);
+ weak_collection->insert(wrapper3, wrapper3);
+ weak_collection->insert(wrapper4, wrapper4);
+ weak_collection->insert(wrapper5, wrapper5);
+ weak_collection->insert(wrapper6, wrapper6);
+
+ // Signal the main thread that the worker is done with its allocation.
+ WakeMainThread();
+
+ // Wait for the main thread to do two GCs without sweeping
+ // this thread heap.
+ ParkWorkerThread();
+
+ return weak_collection;
+ }
+
+ static void WorkerThreadMain() {
+ MutexLocker locker(WorkerThreadMutex());
+
+ ThreadState::AttachCurrentThread();
+
+ {
+ Persistent<WeakCollectionType> collection = AllocateCollection();
+ {
+ // Prevent weak processing with an iterator and GC.
+ WeakCollectionType::iterator it = collection->begin();
+ ConservativelyCollectGarbage();
+
+ // The backing should be strongified because of the iterator.
+ EXPECT_EQ(6u, collection->size());
+ EXPECT_EQ(32, it->value->Value());
+ }
+
+ // Disregarding the iterator but keeping the collection alive
+ // with a persistent should lead to weak processing.
+ PreciselyCollectGarbage();
+ EXPECT_EQ(0u, collection->size());
+ }
+
+ WakeMainThread();
+ ThreadState::DetachCurrentThread();
+ }
+
+ static volatile uintptr_t worker_object_pointer_;
+};
+
+TEST(HeapTest, ThreadedStrongification) {
+ ThreadedStrongificationTester::Test();
+}
+
+class MemberSameThreadCheckTester {
+ public:
+ void Test() {
+ IntWrapper::destructor_calls_ = 0;
+
+ MutexLocker locker(MainThreadMutex());
+ std::unique_ptr<WebThread> worker_thread =
+ Platform::Current()->CreateThread(
+ WebThreadCreationParams(WebThreadType::kTestThread)
+ .SetThreadNameForTest("Test Worker Thread"));
+ PostCrossThreadTask(
+ *worker_thread->GetTaskRunner(), FROM_HERE,
+ CrossThreadBind(&MemberSameThreadCheckTester::WorkerThreadMain,
+ CrossThreadUnretained(this)));
+
+ ParkMainThread();
+ }
+
+ private:
+ Member<IntWrapper> wrapper_;
+
+ void WorkerThreadMain() {
+ MutexLocker locker(WorkerThreadMutex());
+
+ ThreadState::AttachCurrentThread();
+
+ // Setting an object created on the worker thread to a Member allocated on
+ // the main thread is not allowed.
+ wrapper_ = IntWrapper::Create(42);
+
+ WakeMainThread();
+ ThreadState::DetachCurrentThread();
+ }
+};
+
+#if DCHECK_IS_ON()
+// TODO(keishi) This test is flaky on mac_chromium_rel_ng bot.
+// crbug.com/709069
+#if !defined(OS_MACOSX)
+TEST(HeapDeathTest, MemberSameThreadCheck) {
+ EXPECT_DEATH(MemberSameThreadCheckTester().Test(), "");
+}
+#endif
+#endif
+
+class PersistentSameThreadCheckTester {
+ public:
+ void Test() {
+ IntWrapper::destructor_calls_ = 0;
+
+ MutexLocker locker(MainThreadMutex());
+ std::unique_ptr<WebThread> worker_thread =
+ Platform::Current()->CreateThread(
+ WebThreadCreationParams(WebThreadType::kTestThread)
+ .SetThreadNameForTest("Test Worker Thread"));
+ PostCrossThreadTask(
+ *worker_thread->GetTaskRunner(), FROM_HERE,
+ CrossThreadBind(&PersistentSameThreadCheckTester::WorkerThreadMain,
+ CrossThreadUnretained(this)));
+
+ ParkMainThread();
+ }
+
+ private:
+ Persistent<IntWrapper> wrapper_;
+
+ void WorkerThreadMain() {
+ MutexLocker locker(WorkerThreadMutex());
+
+ ThreadState::AttachCurrentThread();
+
+ // Setting an object created on the worker thread to a Persistent allocated
+ // on the main thread is not allowed.
+ wrapper_ = IntWrapper::Create(42);
+
+ WakeMainThread();
+ ThreadState::DetachCurrentThread();
+ }
+};
+
+#if DCHECK_IS_ON()
+// TODO(keishi) This test is flaky on mac_chromium_rel_ng bot.
+// crbug.com/709069
+#if !defined(OS_MACOSX)
+TEST(HeapDeathTest, PersistentSameThreadCheck) {
+ EXPECT_DEATH(PersistentSameThreadCheckTester().Test(), "");
+}
+#endif
+#endif
+
+class MarkingSameThreadCheckTester {
+ public:
+ void Test() {
+ IntWrapper::destructor_calls_ = 0;
+
+ MutexLocker locker(MainThreadMutex());
+ std::unique_ptr<WebThread> worker_thread =
+ Platform::Current()->CreateThread(
+ WebThreadCreationParams(WebThreadType::kTestThread)
+ .SetThreadNameForTest("Test Worker Thread"));
+ Persistent<MainThreadObject> main_thread_object = new MainThreadObject();
+ PostCrossThreadTask(
+ *worker_thread->GetTaskRunner(), FROM_HERE,
+ CrossThreadBind(&MarkingSameThreadCheckTester::WorkerThreadMain,
+ CrossThreadUnretained(this),
+ WrapCrossThreadPersistent(main_thread_object.Get())));
+ ParkMainThread();
+ // This will try to mark MainThreadObject when it tries to mark IntWrapper
+ // it should crash.
+ PreciselyCollectGarbage();
+ }
+
+ private:
+ class MainThreadObject : public GarbageCollectedFinalized<MainThreadObject> {
+ public:
+ void Trace(blink::Visitor* visitor) { visitor->Trace(wrapper_set_); }
+ void AddToSet(IntWrapper* wrapper) { wrapper_set_.insert(42, wrapper); }
+
+ private:
+ HeapHashMap<int, Member<IntWrapper>> wrapper_set_;
+ };
+
+ void WorkerThreadMain(MainThreadObject* main_thread_object) {
+ MutexLocker locker(WorkerThreadMutex());
+
+ ThreadState::AttachCurrentThread();
+
+ // Adding a reference to an object created on the worker thread to a
+ // HeapHashMap created on the main thread is not allowed.
+ main_thread_object->AddToSet(IntWrapper::Create(42));
+
+ WakeMainThread();
+ ThreadState::DetachCurrentThread();
+ }
+};
+
+#if DCHECK_IS_ON()
+// TODO(keishi) This test is flaky on mac_chromium_rel_ng bot.
+// crbug.com/709069
+#if !defined(OS_MACOSX)
+TEST(HeapDeathTest, MarkingSameThreadCheck) {
+ // This will crash during marking, at the DCHECK in Visitor::markHeader() or
+ // earlier.
+ EXPECT_DEATH(MarkingSameThreadCheckTester().Test(), "");
+}
+#endif
+#endif
+
+static bool AllocateAndReturnBool() {
+ ConservativelyCollectGarbage();
+ return true;
+}
+
+static bool CheckGCForbidden() {
+ DCHECK(ThreadState::Current()->IsGCForbidden());
+ return true;
+}
+
+class MixinClass : public GarbageCollectedMixin {
+ public:
+ MixinClass() : dummy_(CheckGCForbidden()) {}
+
+ private:
+ bool dummy_;
+};
+
+class ClassWithGarbageCollectingMixinConstructor
+ : public GarbageCollected<ClassWithGarbageCollectingMixinConstructor>,
+ public MixinClass {
+ USING_GARBAGE_COLLECTED_MIXIN(ClassWithGarbageCollectingMixinConstructor);
+
+ public:
+ static int trace_called_;
+
+ ClassWithGarbageCollectingMixinConstructor()
+ : trace_counter_(TraceCounter::Create()),
+ wrapper_(IntWrapper::Create(32)) {}
+
+ virtual void Trace(blink::Visitor* visitor) {
+ trace_called_++;
+ visitor->Trace(trace_counter_);
+ visitor->Trace(wrapper_);
+ }
+
+ void Verify() {
+ EXPECT_EQ(32, wrapper_->Value());
+ EXPECT_EQ(0, trace_counter_->TraceCount());
+ EXPECT_EQ(0, trace_called_);
+ }
+
+ private:
+ Member<TraceCounter> trace_counter_;
+ Member<IntWrapper> wrapper_;
+};
+
+int ClassWithGarbageCollectingMixinConstructor::trace_called_ = 0;
+
+// Regression test for out of bounds call through vtable.
+// Passes if it doesn't crash.
+TEST(HeapTest, GarbageCollectionDuringMixinConstruction) {
+ ClassWithGarbageCollectingMixinConstructor* a =
+ new ClassWithGarbageCollectingMixinConstructor();
+ a->Verify();
+}
+
+static RecursiveMutex& GetRecursiveMutex() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(RecursiveMutex, recursive_mutex, ());
+ return recursive_mutex;
+}
+
+class DestructorLockingObject
+ : public GarbageCollectedFinalized<DestructorLockingObject> {
+ public:
+ static DestructorLockingObject* Create() {
+ return new DestructorLockingObject();
+ }
+
+ virtual ~DestructorLockingObject() {
+ RecursiveMutexLocker lock(GetRecursiveMutex());
+ ++destructor_calls_;
+ }
+
+ static int destructor_calls_;
+ void Trace(blink::Visitor* visitor) {}
+
+ private:
+ DestructorLockingObject() = default;
+};
+
+int DestructorLockingObject::destructor_calls_ = 0;
+
+template <typename T>
+class TraceIfNeededTester
+ : public GarbageCollectedFinalized<TraceIfNeededTester<T>> {
+ public:
+ static TraceIfNeededTester<T>* Create() {
+ return new TraceIfNeededTester<T>();
+ }
+ static TraceIfNeededTester<T>* Create(const T& obj) {
+ return new TraceIfNeededTester<T>(obj);
+ }
+ void Trace(blink::Visitor* visitor) {
+ TraceIfNeeded<T>::Trace(visitor, obj_);
+ }
+ T& Obj() { return obj_; }
+ ~TraceIfNeededTester() = default;
+
+ private:
+ TraceIfNeededTester() = default;
+ explicit TraceIfNeededTester(const T& obj) : obj_(obj) {}
+ T obj_;
+};
+
+class PartObject {
+ DISALLOW_NEW();
+
+ public:
+ PartObject() : obj_(SimpleObject::Create()) {}
+ void Trace(blink::Visitor* visitor) { visitor->Trace(obj_); }
+
+ private:
+ Member<SimpleObject> obj_;
+};
+
+class AllocatesOnAssignment {
+ public:
+ AllocatesOnAssignment(std::nullptr_t) : value_(nullptr) {}
+ AllocatesOnAssignment(int x) : value_(new IntWrapper(x)) {}
+ AllocatesOnAssignment(IntWrapper* x) : value_(x) {}
+
+ AllocatesOnAssignment& operator=(const AllocatesOnAssignment x) {
+ value_ = x.value_;
+ return *this;
+ }
+
+ enum DeletedMarker { kDeletedValue };
+
+ AllocatesOnAssignment(const AllocatesOnAssignment& other) {
+ if (!ThreadState::Current()->IsGCForbidden())
+ ConservativelyCollectGarbage();
+ value_ = new IntWrapper(other.value_->Value());
+ }
+
+ AllocatesOnAssignment(DeletedMarker) : value_(WTF::kHashTableDeletedValue) {}
+
+ inline bool IsDeleted() const { return value_.IsHashTableDeletedValue(); }
+
+ void Trace(blink::Visitor* visitor) { visitor->Trace(value_); }
+
+ int Value() { return value_->Value(); }
+
+ private:
+ Member<IntWrapper> value_;
+
+ friend bool operator==(const AllocatesOnAssignment&,
+ const AllocatesOnAssignment&);
+ friend void swap(AllocatesOnAssignment&, AllocatesOnAssignment&);
+};
+
+bool operator==(const AllocatesOnAssignment& a,
+ const AllocatesOnAssignment& b) {
+ if (a.value_)
+ return b.value_ && a.value_->Value() == b.value_->Value();
+ return !b.value_;
+}
+
+void swap(AllocatesOnAssignment& a, AllocatesOnAssignment& b) {
+ std::swap(a.value_, b.value_);
+}
+
+struct DegenerateHash {
+ static unsigned GetHash(const AllocatesOnAssignment&) { return 0; }
+ static bool Equal(const AllocatesOnAssignment& a,
+ const AllocatesOnAssignment& b) {
+ return !a.IsDeleted() && a == b;
+ }
+ static const bool safe_to_compare_to_empty_or_deleted = true;
+};
+
+struct AllocatesOnAssignmentHashTraits
+ : WTF::GenericHashTraits<AllocatesOnAssignment> {
+ typedef AllocatesOnAssignment T;
+ typedef std::nullptr_t EmptyValueType;
+ static EmptyValueType EmptyValue() { return nullptr; }
+ static const bool kEmptyValueIsZero =
+ false; // Can't be zero if it has a vtable.
+ static void ConstructDeletedValue(T& slot, bool) {
+ slot = T(AllocatesOnAssignment::kDeletedValue);
+ }
+ static bool IsDeletedValue(const T& value) { return value.IsDeleted(); }
+};
+
+} // namespace blink
+
+namespace WTF {
+
+template <>
+struct DefaultHash<blink::AllocatesOnAssignment> {
+ typedef blink::DegenerateHash Hash;
+};
+
+template <>
+struct HashTraits<blink::AllocatesOnAssignment>
+ : blink::AllocatesOnAssignmentHashTraits {};
+
+} // namespace WTF
+
+namespace blink {
+
+TEST(HeapTest, GCInHashMapOperations) {
+ typedef HeapHashMap<AllocatesOnAssignment, AllocatesOnAssignment> Map;
+ Map* map = new Map();
+ IntWrapper* key = new IntWrapper(42);
+ map->insert(key, AllocatesOnAssignment(103));
+ map->erase(key);
+ for (int i = 0; i < 10; i++)
+ map->insert(AllocatesOnAssignment(i), AllocatesOnAssignment(i));
+ for (Map::iterator it = map->begin(); it != map->end(); ++it)
+ EXPECT_EQ(it->key.Value(), it->value.Value());
+}
+
+class PartObjectWithVirtualMethod {
+ public:
+ virtual void Trace(blink::Visitor* visitor) {}
+};
+
+class ObjectWithVirtualPartObject
+ : public GarbageCollected<ObjectWithVirtualPartObject> {
+ public:
+ ObjectWithVirtualPartObject() : dummy_(AllocateAndReturnBool()) {}
+ void Trace(blink::Visitor* visitor) { visitor->Trace(part_); }
+
+ private:
+ bool dummy_;
+ PartObjectWithVirtualMethod part_;
+};
+
+TEST(HeapTest, PartObjectWithVirtualMethod) {
+ ObjectWithVirtualPartObject* object = new ObjectWithVirtualPartObject();
+ EXPECT_TRUE(object);
+}
+
+class AllocInSuperConstructorArgumentSuper
+ : public GarbageCollectedFinalized<AllocInSuperConstructorArgumentSuper> {
+ public:
+ AllocInSuperConstructorArgumentSuper(bool value) : value_(value) {}
+ virtual ~AllocInSuperConstructorArgumentSuper() = default;
+ virtual void Trace(blink::Visitor* visitor) {}
+ bool Value() { return value_; }
+
+ private:
+ bool value_;
+};
+
+class AllocInSuperConstructorArgument
+ : public AllocInSuperConstructorArgumentSuper {
+ public:
+ AllocInSuperConstructorArgument()
+ : AllocInSuperConstructorArgumentSuper(AllocateAndReturnBool()) {}
+};
+
+// Regression test for crbug.com/404511. Tests conservative marking of
+// an object with an uninitialized vtable.
+TEST(HeapTest, AllocationInSuperConstructorArgument) {
+ AllocInSuperConstructorArgument* object =
+ new AllocInSuperConstructorArgument();
+ EXPECT_TRUE(object);
+ ThreadState::Current()->CollectAllGarbage();
+}
+
+class NonNodeAllocatingNodeInDestructor
+ : public GarbageCollectedFinalized<NonNodeAllocatingNodeInDestructor> {
+ public:
+ ~NonNodeAllocatingNodeInDestructor() {
+ node_ = new Persistent<IntNode>(IntNode::Create(10));
+ }
+
+ void Trace(blink::Visitor* visitor) {}
+
+ static Persistent<IntNode>* node_;
+};
+
+Persistent<IntNode>* NonNodeAllocatingNodeInDestructor::node_ = nullptr;
+
+TEST(HeapTest, NonNodeAllocatingNodeInDestructor) {
+ new NonNodeAllocatingNodeInDestructor();
+ PreciselyCollectGarbage();
+ EXPECT_EQ(10, (*NonNodeAllocatingNodeInDestructor::node_)->Value());
+ delete NonNodeAllocatingNodeInDestructor::node_;
+ NonNodeAllocatingNodeInDestructor::node_ = nullptr;
+}
+
+class TraceTypeEagerly1 : public GarbageCollected<TraceTypeEagerly1> {};
+class TraceTypeEagerly2 : public TraceTypeEagerly1 {};
+
+class TraceTypeNonEagerly1 {};
+WILL_NOT_BE_EAGERLY_TRACED_CLASS(TraceTypeNonEagerly1);
+class TraceTypeNonEagerly2 : public TraceTypeNonEagerly1 {};
+
+TEST(HeapTest, TraceTypesEagerly) {
+ static_assert(TraceEagerlyTrait<TraceTypeEagerly1>::value, "should be true");
+ static_assert(TraceEagerlyTrait<Member<TraceTypeEagerly1>>::value,
+ "should be true");
+ static_assert(TraceEagerlyTrait<WeakMember<TraceTypeEagerly1>>::value,
+ "should be true");
+ static_assert(TraceEagerlyTrait<HeapVector<Member<TraceTypeEagerly1>>>::value,
+ "should be true");
+ static_assert(
+ TraceEagerlyTrait<HeapVector<WeakMember<TraceTypeEagerly1>>>::value,
+ "should be true");
+ static_assert(
+ TraceEagerlyTrait<HeapHashSet<Member<TraceTypeEagerly1>>>::value,
+ "should be true");
+ static_assert(
+ TraceEagerlyTrait<HeapHashSet<Member<TraceTypeEagerly1>>>::value,
+ "should be true");
+ using HashMapIntToObj = HeapHashMap<int, Member<TraceTypeEagerly1>>;
+ static_assert(TraceEagerlyTrait<HashMapIntToObj>::value, "should be true");
+ using HashMapObjToInt = HeapHashMap<Member<TraceTypeEagerly1>, int>;
+ static_assert(TraceEagerlyTrait<HashMapObjToInt>::value, "should be true");
+
+ static_assert(TraceEagerlyTrait<TraceTypeEagerly2>::value, "should be true");
+ static_assert(TraceEagerlyTrait<Member<TraceTypeEagerly2>>::value,
+ "should be true");
+
+ static_assert(!TraceEagerlyTrait<TraceTypeNonEagerly1>::value,
+ "should be false");
+ static_assert(TraceEagerlyTrait<TraceTypeNonEagerly2>::value,
+ "should be true");
+}
+
+class DeepEagerly final : public GarbageCollected<DeepEagerly> {
+ public:
+ DeepEagerly(DeepEagerly* next) : next_(next) {}
+
+ void Trace(blink::Visitor* visitor) {
+ int calls = ++s_trace_calls_;
+ if (s_trace_lazy_ <= 2)
+ visitor->Trace(next_);
+ if (s_trace_calls_ == calls)
+ s_trace_lazy_++;
+ }
+
+ Member<DeepEagerly> next_;
+
+ static int s_trace_calls_;
+ static int s_trace_lazy_;
+};
+
+int DeepEagerly::s_trace_calls_ = 0;
+int DeepEagerly::s_trace_lazy_ = 0;
+
+TEST(HeapTest, TraceDeepEagerly) {
+// The allocation & GC overhead is considerable for this test,
+// straining debug builds and lower-end targets too much to be
+// worth running.
+#if !DCHECK_IS_ON() && !defined(OS_ANDROID)
+ DeepEagerly* obj = nullptr;
+ for (int i = 0; i < 10000000; i++)
+ obj = new DeepEagerly(obj);
+
+ Persistent<DeepEagerly> persistent(obj);
+ PreciselyCollectGarbage();
+
+ // Verify that the DeepEagerly chain isn't completely unravelled
+ // by performing eager trace() calls, but the explicit mark
+ // stack is switched once some nesting limit is exceeded.
+ EXPECT_GT(DeepEagerly::s_trace_lazy_, 2);
+#endif
+}
+
+TEST(HeapTest, DequeExpand) {
+ // Test expansion of a HeapDeque<>'s buffer.
+
+ typedef HeapDeque<Member<IntWrapper>> IntDeque;
+
+ Persistent<IntDeque> deque = new IntDeque();
+
+ // Append a sequence, bringing about repeated expansions of the
+ // deque's buffer.
+ int i = 0;
+ for (; i < 60; ++i)
+ deque->push_back(IntWrapper::Create(i));
+
+ EXPECT_EQ(60u, deque->size());
+ i = 0;
+ for (const auto& int_wrapper : *deque) {
+ EXPECT_EQ(i, int_wrapper->Value());
+ i++;
+ }
+
+ // Remove most of the queued objects and have the buffer's start index
+ // 'point' somewhere into the buffer, just behind the end index.
+ for (i = 0; i < 50; ++i)
+ deque->TakeFirst();
+
+ EXPECT_EQ(10u, deque->size());
+ i = 0;
+ for (const auto& int_wrapper : *deque) {
+ EXPECT_EQ(50 + i, int_wrapper->Value());
+ i++;
+ }
+
+ // Append even more, eventually causing an expansion of the underlying
+ // buffer once the end index wraps around and reaches the start index.
+ for (i = 0; i < 70; ++i)
+ deque->push_back(IntWrapper::Create(60 + i));
+
+ // Verify that the final buffer expansion copied the start and end segments
+ // of the old buffer to both ends of the expanded buffer, along with
+ // re-adjusting both start&end indices in terms of that expanded buffer.
+ EXPECT_EQ(80u, deque->size());
+ i = 0;
+ for (const auto& int_wrapper : *deque) {
+ EXPECT_EQ(i + 50, int_wrapper->Value());
+ i++;
+ }
+}
+
+class SimpleRefValue : public RefCounted<SimpleRefValue> {
+ public:
+ static scoped_refptr<SimpleRefValue> Create(int i) {
+ return base::AdoptRef(new SimpleRefValue(i));
+ }
+
+ int Value() const { return value_; }
+
+ private:
+ explicit SimpleRefValue(int value) : value_(value) {}
+
+ int value_;
+};
+
+class PartObjectWithRef {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ PartObjectWithRef(int i) : value_(SimpleRefValue::Create(i)) {}
+
+ void Trace(blink::Visitor* visitor) {}
+
+ int Value() const { return value_->Value(); }
+
+ private:
+ scoped_refptr<SimpleRefValue> value_;
+};
+
+} // namespace blink
+
+WTF_ALLOW_INIT_WITH_MEM_FUNCTIONS(blink::PartObjectWithRef);
+
+namespace blink {
+
+TEST(HeapTest, DequePartObjectsExpand) {
+ // Test expansion of HeapDeque<PartObject>
+
+ using PartDeque = HeapDeque<PartObjectWithRef>;
+
+ Persistent<PartDeque> deque = new PartDeque();
+ // Auxillary Deque used to prevent 'inline' buffer expansion.
+ Persistent<PartDeque> deque_unused = new PartDeque();
+
+ // Append a sequence, bringing about repeated expansions of the
+ // deque's buffer.
+ int i = 0;
+ for (; i < 60; ++i) {
+ deque->push_back(PartObjectWithRef(i));
+ deque_unused->push_back(PartObjectWithRef(i));
+ }
+
+ EXPECT_EQ(60u, deque->size());
+ i = 0;
+ for (const PartObjectWithRef& part : *deque) {
+ EXPECT_EQ(i, part.Value());
+ i++;
+ }
+
+ // Remove most of the queued objects and have the buffer's start index
+ // 'point' somewhere into the buffer, just behind the end index.
+ for (i = 0; i < 50; ++i)
+ deque->TakeFirst();
+
+ EXPECT_EQ(10u, deque->size());
+ i = 0;
+ for (const PartObjectWithRef& part : *deque) {
+ EXPECT_EQ(50 + i, part.Value());
+ i++;
+ }
+
+ // Append even more, eventually causing an expansion of the underlying
+ // buffer once the end index wraps around and reaches the start index.
+ for (i = 0; i < 70; ++i)
+ deque->push_back(PartObjectWithRef(60 + i));
+
+ // Verify that the final buffer expansion copied the start and end segments
+ // of the old buffer to both ends of the expanded buffer, along with
+ // re-adjusting both start&end indices in terms of that expanded buffer.
+ EXPECT_EQ(80u, deque->size());
+ i = 0;
+ for (const PartObjectWithRef& part : *deque) {
+ EXPECT_EQ(i + 50, part.Value());
+ i++;
+ }
+
+ for (i = 0; i < 70; ++i)
+ deque->push_back(PartObjectWithRef(130 + i));
+
+ EXPECT_EQ(150u, deque->size());
+ i = 0;
+ for (const PartObjectWithRef& part : *deque) {
+ EXPECT_EQ(i + 50, part.Value());
+ i++;
+ }
+}
+
+TEST(HeapTest, HeapVectorPartObjects) {
+ HeapVector<PartObjectWithRef> vector1;
+ HeapVector<PartObjectWithRef> vector2;
+
+ for (int i = 0; i < 10; ++i) {
+ vector1.push_back(PartObjectWithRef(i));
+ vector2.push_back(PartObjectWithRef(i));
+ }
+
+ vector1.ReserveCapacity(150);
+ EXPECT_LE(150u, vector1.capacity());
+ EXPECT_EQ(10u, vector1.size());
+
+ vector2.ReserveCapacity(100);
+ EXPECT_LE(100u, vector2.capacity());
+ EXPECT_EQ(10u, vector2.size());
+
+ for (int i = 0; i < 4; ++i) {
+ vector1.push_back(PartObjectWithRef(10 + i));
+ vector2.push_back(PartObjectWithRef(10 + i));
+ vector2.push_back(PartObjectWithRef(10 + i));
+ }
+
+ // Shrinking heap vector backing stores always succeeds,
+ // so these two will not currently exercise the code path
+ // where shrinking causes copying into a new, small buffer.
+ vector2.ShrinkToReasonableCapacity();
+ EXPECT_EQ(18u, vector2.size());
+
+ vector1.ShrinkToReasonableCapacity();
+ EXPECT_EQ(14u, vector1.size());
+}
+
+namespace {
+
+enum GrowthDirection {
+ kGrowsTowardsHigher,
+ kGrowsTowardsLower,
+};
+
+NEVER_INLINE NO_SANITIZE_ADDRESS GrowthDirection StackGrowthDirection() {
+ // Disable ASan, otherwise its stack checking (use-after-return) will
+ // confuse the direction check.
+ static char* previous = nullptr;
+ char dummy;
+ if (!previous) {
+ previous = &dummy;
+ GrowthDirection result = StackGrowthDirection();
+ previous = nullptr;
+ return result;
+ }
+ DCHECK_NE(&dummy, previous);
+ return &dummy < previous ? kGrowsTowardsLower : kGrowsTowardsHigher;
+}
+
+} // namespace
+
+TEST(HeapTest, StackGrowthDirection) {
+ // The implementation of marking probes stack usage as it runs,
+ // and has a builtin assumption that the stack grows towards
+ // lower addresses.
+ EXPECT_EQ(kGrowsTowardsLower, StackGrowthDirection());
+}
+
+TEST(HeapTest, StackFrameDepthDisabledByDefault) {
+ StackFrameDepth depth;
+ // Only allow recursion after explicitly enabling the stack limit.
+ EXPECT_FALSE(depth.IsSafeToRecurse());
+}
+
+TEST(HeapTest, StackFrameDepthEnable) {
+ StackFrameDepth depth;
+ StackFrameDepthScope scope(&depth);
+ // The scope may fail to enable recursion when the stack is close to the
+ // limit. In all other cases we should be able to safely recurse.
+ EXPECT_TRUE(depth.IsSafeToRecurse() || !depth.IsEnabled());
+}
+
+class TestMixinAllocationA : public GarbageCollected<TestMixinAllocationA>,
+ public GarbageCollectedMixin {
+ USING_GARBAGE_COLLECTED_MIXIN(TestMixinAllocationA);
+
+ public:
+ TestMixinAllocationA() {
+ // Completely wrong in general, but test only
+ // runs this constructor while constructing another mixin.
+ DCHECK(ThreadState::Current()->IsGCForbidden());
+ }
+
+ virtual void Trace(blink::Visitor* visitor) {}
+};
+
+class TestMixinAllocationB : public TestMixinAllocationA {
+ USING_GARBAGE_COLLECTED_MIXIN(TestMixinAllocationB);
+
+ public:
+ TestMixinAllocationB()
+ : a_(new TestMixinAllocationA()) // Construct object during a mixin
+ // construction.
+ {
+ // Completely wrong in general, but test only
+ // runs this constructor while constructing another mixin.
+ DCHECK(ThreadState::Current()->IsGCForbidden());
+ }
+
+ void Trace(blink::Visitor* visitor) {
+ visitor->Trace(a_);
+ TestMixinAllocationA::Trace(visitor);
+ }
+
+ private:
+ Member<TestMixinAllocationA> a_;
+};
+
+class TestMixinAllocationC final : public TestMixinAllocationB {
+ USING_GARBAGE_COLLECTED_MIXIN(TestMixinAllocationC);
+
+ public:
+ TestMixinAllocationC() { DCHECK(!ThreadState::Current()->IsGCForbidden()); }
+
+ void Trace(blink::Visitor* visitor) { TestMixinAllocationB::Trace(visitor); }
+};
+
+TEST(HeapTest, NestedMixinConstruction) {
+ TestMixinAllocationC* object = new TestMixinAllocationC();
+ EXPECT_TRUE(object);
+}
+
+class ObjectWithLargeAmountsOfAllocationInConstructor {
+ public:
+ ObjectWithLargeAmountsOfAllocationInConstructor(
+ size_t number_of_large_objects_to_allocate,
+ ClassWithMember* member) {
+ // Should a constructor allocate plenty in its constructor,
+ // and it is a base of GC mixin, GCs will remain locked out
+ // regardless, as we cannot safely trace the leftmost GC
+ // mixin base.
+ DCHECK(ThreadState::Current()->IsGCForbidden());
+ for (size_t i = 0; i < number_of_large_objects_to_allocate; i++) {
+ LargeHeapObject* large_object = LargeHeapObject::Create();
+ EXPECT_TRUE(large_object);
+ EXPECT_EQ(0, member->TraceCount());
+ }
+ }
+};
+
+class TestMixinAllocatingObject final
+ : public TestMixinAllocationB,
+ public ObjectWithLargeAmountsOfAllocationInConstructor {
+ USING_GARBAGE_COLLECTED_MIXIN(TestMixinAllocatingObject);
+
+ public:
+ static TestMixinAllocatingObject* Create(ClassWithMember* member) {
+ return new TestMixinAllocatingObject(member);
+ }
+
+ void Trace(blink::Visitor* visitor) {
+ visitor->Trace(trace_counter_);
+ TestMixinAllocationB::Trace(visitor);
+ }
+
+ int TraceCount() const { return trace_counter_->TraceCount(); }
+
+ private:
+ TestMixinAllocatingObject(ClassWithMember* member)
+ : ObjectWithLargeAmountsOfAllocationInConstructor(600, member),
+ trace_counter_(TraceCounter::Create()) {
+ DCHECK(!ThreadState::Current()->IsGCForbidden());
+ ConservativelyCollectGarbage();
+ EXPECT_GT(member->TraceCount(), 0);
+ EXPECT_GT(TraceCount(), 0);
+ }
+
+ Member<TraceCounter> trace_counter_;
+};
+
+TEST(HeapTest, MixinConstructionNoGC) {
+ Persistent<ClassWithMember> object = ClassWithMember::Create();
+ EXPECT_EQ(0, object->TraceCount());
+ TestMixinAllocatingObject* mixin =
+ TestMixinAllocatingObject::Create(object.Get());
+ EXPECT_TRUE(mixin);
+ EXPECT_GT(object->TraceCount(), 0);
+ EXPECT_GT(mixin->TraceCount(), 0);
+}
+
+class WeakPersistentHolder final {
+ public:
+ explicit WeakPersistentHolder(IntWrapper* object) : object_(object) {}
+ IntWrapper* Object() const { return object_; }
+
+ private:
+ WeakPersistent<IntWrapper> object_;
+};
+
+TEST(HeapTest, WeakPersistent) {
+ Persistent<IntWrapper> object = new IntWrapper(20);
+ std::unique_ptr<WeakPersistentHolder> holder =
+ std::make_unique<WeakPersistentHolder>(object);
+ PreciselyCollectGarbage();
+ EXPECT_TRUE(holder->Object());
+ object = nullptr;
+ PreciselyCollectGarbage();
+ EXPECT_FALSE(holder->Object());
+}
+
+namespace {
+
+void WorkerThreadMainForCrossThreadWeakPersistentTest(
+ DestructorLockingObject** object) {
+ // Step 2: Create an object and store the pointer.
+ MutexLocker locker(WorkerThreadMutex());
+ ThreadState::AttachCurrentThread();
+ *object = DestructorLockingObject::Create();
+ WakeMainThread();
+ ParkWorkerThread();
+
+ // Step 4: Run a GC.
+ ThreadState::Current()->CollectGarbage(
+ BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
+ BlinkGC::kEagerSweeping, BlinkGC::kForcedGC);
+ WakeMainThread();
+ ParkWorkerThread();
+
+ // Step 6: Finish.
+ ThreadState::DetachCurrentThread();
+ WakeMainThread();
+}
+
+} // anonymous namespace
+
+TEST(HeapTest, CrossThreadWeakPersistent) {
+ // Create an object in the worker thread, have a CrossThreadWeakPersistent
+ // pointing to it on the main thread, clear the reference in the worker
+ // thread, run a GC in the worker thread, and see if the
+ // CrossThreadWeakPersistent is cleared.
+
+ DestructorLockingObject::destructor_calls_ = 0;
+
+ // Step 1: Initiate a worker thread, and wait for |object| to get allocated on
+ // the worker thread.
+ MutexLocker main_thread_mutex_locker(MainThreadMutex());
+ std::unique_ptr<WebThread> worker_thread = Platform::Current()->CreateThread(
+ WebThreadCreationParams(WebThreadType::kTestThread)
+ .SetThreadNameForTest("Test Worker Thread"));
+ DestructorLockingObject* object = nullptr;
+ PostCrossThreadTask(
+ *worker_thread->GetTaskRunner(), FROM_HERE,
+ CrossThreadBind(WorkerThreadMainForCrossThreadWeakPersistentTest,
+ CrossThreadUnretained(&object)));
+ ParkMainThread();
+
+ // Step 3: Set up a CrossThreadWeakPersistent.
+ ASSERT_TRUE(object);
+ CrossThreadWeakPersistent<DestructorLockingObject>
+ cross_thread_weak_persistent(object);
+ object = nullptr;
+ {
+ RecursiveMutexLocker recursive_mutex_locker(GetRecursiveMutex());
+ EXPECT_EQ(0, DestructorLockingObject::destructor_calls_);
+ }
+
+ {
+ // Pretend we have no pointers on stack during the step 4.
+ WakeWorkerThread();
+ ParkMainThread();
+ }
+
+ // Step 5: Make sure the weak persistent is cleared.
+ EXPECT_FALSE(cross_thread_weak_persistent.Get());
+ {
+ RecursiveMutexLocker recursive_mutex_locker(GetRecursiveMutex());
+ EXPECT_EQ(1, DestructorLockingObject::destructor_calls_);
+ }
+
+ WakeWorkerThread();
+ ParkMainThread();
+}
+
+class TestPersistentHeapVectorWithUnusedSlots
+ : public PersistentHeapVector<VectorObject, 16> {
+ public:
+ void CheckUnused() { CheckUnusedSlots(end(), end() + (capacity() - size())); }
+};
+
+TEST(HeapTest, TestPersistentHeapVectorWithUnusedSlots) {
+ TestPersistentHeapVectorWithUnusedSlots vector1;
+ TestPersistentHeapVectorWithUnusedSlots vector2(vector1);
+
+ vector1.CheckUnused();
+ vector2.CheckUnused();
+
+ vector2.push_back(VectorObject());
+ vector2.CheckUnused();
+
+ EXPECT_EQ(0u, vector1.size());
+
+ EXPECT_EQ(1u, vector2.size());
+// TODO(Oilpan): when Vector.h's contiguous container support no longer disables
+// Vector<>s with inline capacity, remove.
+#if !defined(ANNOTATE_CONTIGUOUS_CONTAINER)
+ EXPECT_EQ(16u, vector1.capacity());
+ EXPECT_EQ(16u, vector2.capacity());
+#endif
+}
+
+TEST(HeapTest, TestStaticLocals) {
+ // Sanity check DEFINE_STATIC_LOCAL()s over heap allocated objects and
+ // collections.
+
+ DEFINE_STATIC_LOCAL(IntWrapper, int_wrapper, (new IntWrapper(33)));
+ DEFINE_STATIC_LOCAL(PersistentHeapVector<Member<IntWrapper>>,
+ persistent_heap_vector_int_wrapper, ());
+ DEFINE_STATIC_LOCAL(HeapVector<Member<IntWrapper>>, heap_vector_int_wrapper,
+ (new HeapVector<Member<IntWrapper>>));
+
+ EXPECT_EQ(33, int_wrapper.Value());
+ EXPECT_EQ(0u, persistent_heap_vector_int_wrapper.size());
+ EXPECT_EQ(0u, heap_vector_int_wrapper.size());
+
+ persistent_heap_vector_int_wrapper.push_back(&int_wrapper);
+ heap_vector_int_wrapper.push_back(&int_wrapper);
+ EXPECT_EQ(1u, persistent_heap_vector_int_wrapper.size());
+ EXPECT_EQ(1u, heap_vector_int_wrapper.size());
+
+ EXPECT_EQ(persistent_heap_vector_int_wrapper[0], heap_vector_int_wrapper[0]);
+ EXPECT_EQ(33, heap_vector_int_wrapper[0]->Value());
+}
+
+namespace {
+
+class ThreadedClearOnShutdownTester : public ThreadedTesterBase {
+ public:
+ static void Test() {
+ IntWrapper::destructor_calls_ = 0;
+ ThreadedTesterBase::Test(new ThreadedClearOnShutdownTester);
+ EXPECT_EQ(kNumberOfThreads, IntWrapper::destructor_calls_);
+ }
+
+ private:
+ void RunWhileAttached();
+
+ void RunThread() override {
+ ThreadState::AttachCurrentThread();
+ EXPECT_EQ(42, ThreadSpecificIntWrapper().Value());
+ RunWhileAttached();
+ ThreadState::DetachCurrentThread();
+ AtomicDecrement(&threads_to_finish_);
+ }
+
+ class HeapObject;
+ friend class HeapObject;
+
+ using WeakHeapObjectSet = PersistentHeapHashSet<WeakMember<HeapObject>>;
+
+ static WeakHeapObjectSet& GetWeakHeapObjectSet();
+
+ using HeapObjectSet = PersistentHeapHashSet<Member<HeapObject>>;
+ static HeapObjectSet& GetHeapObjectSet();
+
+ static IntWrapper& ThreadSpecificIntWrapper() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadSpecific<Persistent<IntWrapper>>,
+ int_wrapper, ());
+ Persistent<IntWrapper>& handle = *int_wrapper;
+ if (!handle) {
+ handle = new IntWrapper(42);
+ handle.RegisterAsStaticReference();
+ }
+ return *handle;
+ }
+};
+
+class ThreadedClearOnShutdownTester::HeapObject final
+ : public GarbageCollectedFinalized<
+ ThreadedClearOnShutdownTester::HeapObject> {
+ public:
+ static HeapObject* Create(bool test_destructor) {
+ return new HeapObject(test_destructor);
+ }
+
+ ~HeapObject() {
+ if (!test_destructor_)
+ return;
+
+ // Verify that the weak reference is gone.
+ EXPECT_FALSE(GetWeakHeapObjectSet().Contains(this));
+
+ // Add a new member to the static singleton; this will
+ // re-initializes the persistent node of the collection
+ // object. Done while terminating the test thread, so
+ // verify that this brings about the release of the
+ // persistent also.
+ GetHeapObjectSet().insert(Create(false));
+ }
+
+ void Trace(blink::Visitor* visitor) {}
+
+ private:
+ explicit HeapObject(bool test_destructor)
+ : test_destructor_(test_destructor) {}
+
+ bool test_destructor_;
+};
+
+ThreadedClearOnShutdownTester::WeakHeapObjectSet&
+ThreadedClearOnShutdownTester::GetWeakHeapObjectSet() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadSpecific<WeakHeapObjectSet>, singleton,
+ ());
+ if (!singleton.IsSet())
+ singleton->RegisterAsStaticReference();
+
+ return *singleton;
+}
+
+ThreadedClearOnShutdownTester::HeapObjectSet&
+ThreadedClearOnShutdownTester::GetHeapObjectSet() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadSpecific<HeapObjectSet>, singleton, ());
+ if (!singleton.IsSet())
+ singleton->RegisterAsStaticReference();
+
+ return *singleton;
+}
+
+void ThreadedClearOnShutdownTester::RunWhileAttached() {
+ EXPECT_EQ(42, ThreadSpecificIntWrapper().Value());
+ // Creates a thread-specific singleton to a weakly held object.
+ GetWeakHeapObjectSet().insert(HeapObject::Create(true));
+}
+
+} // namespace
+
+TEST(HeapTest, TestClearOnShutdown) {
+ ThreadedClearOnShutdownTester::Test();
+}
+
+// Verify that WeakMember<const T> compiles and behaves as expected.
+class WithWeakConstObject final : public GarbageCollected<WithWeakConstObject> {
+ public:
+ static WithWeakConstObject* Create(const IntWrapper* int_wrapper) {
+ return new WithWeakConstObject(int_wrapper);
+ }
+
+ void Trace(blink::Visitor* visitor) { visitor->Trace(wrapper_); }
+
+ const IntWrapper* Value() const { return wrapper_; }
+
+ private:
+ WithWeakConstObject(const IntWrapper* int_wrapper) : wrapper_(int_wrapper) {}
+
+ WeakMember<const IntWrapper> wrapper_;
+};
+
+TEST(HeapTest, TestWeakConstObject) {
+ Persistent<WithWeakConstObject> weak_wrapper;
+ {
+ const IntWrapper* wrapper = IntWrapper::Create(42);
+ weak_wrapper = WithWeakConstObject::Create(wrapper);
+ ConservativelyCollectGarbage();
+ EXPECT_EQ(wrapper, weak_wrapper->Value());
+ // Stub out any stack reference.
+ wrapper = nullptr;
+ }
+ PreciselyCollectGarbage();
+ EXPECT_EQ(nullptr, weak_wrapper->Value());
+}
+
+class EmptyMixin : public GarbageCollectedMixin {};
+class UseMixinFromLeftmostInherited : public UseMixin, public EmptyMixin {
+ public:
+ ~UseMixinFromLeftmostInherited() = default;
+};
+
+TEST(HeapTest, IsGarbageCollected) {
+ // Static sanity checks covering the correct operation of
+ // IsGarbageCollectedType<>.
+
+ static_assert(WTF::IsGarbageCollectedType<SimpleObject>::value,
+ "GarbageCollected<>");
+ static_assert(WTF::IsGarbageCollectedType<const SimpleObject>::value,
+ "const GarbageCollected<>");
+ static_assert(WTF::IsGarbageCollectedType<IntWrapper>::value,
+ "GarbageCollectedFinalized<>");
+ static_assert(WTF::IsGarbageCollectedType<GarbageCollectedMixin>::value,
+ "GarbageCollectedMixin");
+ static_assert(WTF::IsGarbageCollectedType<const GarbageCollectedMixin>::value,
+ "const GarbageCollectedMixin");
+ static_assert(WTF::IsGarbageCollectedType<UseMixin>::value,
+ "GarbageCollectedMixin instance");
+ static_assert(WTF::IsGarbageCollectedType<const UseMixin>::value,
+ "const GarbageCollectedMixin instance");
+ static_assert(
+ WTF::IsGarbageCollectedType<UseMixinFromLeftmostInherited>::value,
+ "GarbageCollectedMixin derived instance");
+ static_assert(WTF::IsGarbageCollectedType<MultipleMixins>::value,
+ "GarbageCollectedMixin");
+
+ static_assert(
+ WTF::IsGarbageCollectedType<HeapHashSet<Member<IntWrapper>>>::value,
+ "HeapHashSet");
+ static_assert(
+ WTF::IsGarbageCollectedType<HeapLinkedHashSet<Member<IntWrapper>>>::value,
+ "HeapLinkedHashSet");
+ static_assert(
+ WTF::IsGarbageCollectedType<HeapListHashSet<Member<IntWrapper>>>::value,
+ "HeapListHashSet");
+ static_assert(WTF::IsGarbageCollectedType<
+ HeapHashCountedSet<Member<IntWrapper>>>::value,
+ "HeapHashCountedSet");
+ static_assert(
+ WTF::IsGarbageCollectedType<HeapHashMap<int, Member<IntWrapper>>>::value,
+ "HeapHashMap");
+ static_assert(
+ WTF::IsGarbageCollectedType<HeapVector<Member<IntWrapper>>>::value,
+ "HeapVector");
+ static_assert(
+ WTF::IsGarbageCollectedType<HeapDeque<Member<IntWrapper>>>::value,
+ "HeapDeque");
+ static_assert(WTF::IsGarbageCollectedType<
+ HeapTerminatedArray<Member<IntWrapper>>>::value,
+ "HeapTerminatedArray");
+}
+
+TEST(HeapTest, HeapHashMapCallsDestructor) {
+ String string = "string";
+ EXPECT_TRUE(string.Impl()->HasOneRef());
+
+ HeapHashMap<KeyWithCopyingMoveConstructor, Member<IntWrapper>> map;
+
+ EXPECT_TRUE(string.Impl()->HasOneRef());
+
+ for (int i = 1; i <= 100; ++i) {
+ KeyWithCopyingMoveConstructor key(i, string);
+ map.insert(key, IntWrapper::Create(i));
+ }
+
+ EXPECT_FALSE(string.Impl()->HasOneRef());
+ map.clear();
+
+ EXPECT_TRUE(string.Impl()->HasOneRef());
+}
+
+class DoublyLinkedListNodeImpl
+ : public GarbageCollectedFinalized<DoublyLinkedListNodeImpl>,
+ public DoublyLinkedListNode<DoublyLinkedListNodeImpl> {
+ public:
+ DoublyLinkedListNodeImpl() = default;
+ static DoublyLinkedListNodeImpl* Create() {
+ return new DoublyLinkedListNodeImpl();
+ }
+
+ static int destructor_calls_;
+ ~DoublyLinkedListNodeImpl() { ++destructor_calls_; }
+
+ void Trace(Visitor* visitor) {
+ visitor->Trace(prev_);
+ visitor->Trace(next_);
+ }
+
+ private:
+ friend class WTF::DoublyLinkedListNode<DoublyLinkedListNodeImpl>;
+ Member<DoublyLinkedListNodeImpl> prev_;
+ Member<DoublyLinkedListNodeImpl> next_;
+};
+
+int DoublyLinkedListNodeImpl::destructor_calls_ = 0;
+
+template <typename T>
+class HeapDoublyLinkedListContainer
+ : public GarbageCollected<HeapDoublyLinkedListContainer<T>> {
+ public:
+ static HeapDoublyLinkedListContainer<T>* Create() {
+ return new HeapDoublyLinkedListContainer<T>();
+ }
+ HeapDoublyLinkedListContainer<T>() = default;
+ HeapDoublyLinkedList<T> list_;
+ void Trace(Visitor* visitor) { visitor->Trace(list_); }
+};
+
+TEST(HeapTest, HeapDoublyLinkedList) {
+ Persistent<HeapDoublyLinkedListContainer<DoublyLinkedListNodeImpl>>
+ container =
+ HeapDoublyLinkedListContainer<DoublyLinkedListNodeImpl>::Create();
+ DoublyLinkedListNodeImpl::destructor_calls_ = 0;
+
+ container->list_.Append(DoublyLinkedListNodeImpl::Create());
+ container->list_.Append(DoublyLinkedListNodeImpl::Create());
+
+ PreciselyCollectGarbage();
+ EXPECT_EQ(DoublyLinkedListNodeImpl::destructor_calls_, 0);
+
+ container->list_.RemoveHead();
+ PreciselyCollectGarbage();
+ EXPECT_EQ(DoublyLinkedListNodeImpl::destructor_calls_, 1);
+
+ container->list_.RemoveHead();
+ PreciselyCollectGarbage();
+ EXPECT_EQ(DoublyLinkedListNodeImpl::destructor_calls_, 2);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/heap/heap_test_utilities.cc b/chromium/third_party/blink/renderer/platform/heap/heap_test_utilities.cc
new file mode 100644
index 00000000000..afe0188157f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/heap_test_utilities.cc
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2017 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "third_party/blink/renderer/platform/heap/heap_test_utilities.h"
+
+#include "third_party/blink/renderer/platform/heap/heap.h"
+
+namespace blink {
+
+void PreciselyCollectGarbage() {
+ ThreadState::Current()->CollectGarbage(
+ BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
+ BlinkGC::kEagerSweeping, BlinkGC::kForcedGC);
+}
+
+void ConservativelyCollectGarbage() {
+ ThreadState::Current()->CollectGarbage(
+ BlinkGC::kHeapPointersOnStack, BlinkGC::kAtomicMarking,
+ BlinkGC::kEagerSweeping, BlinkGC::kForcedGC);
+}
+
+// Do several GCs to make sure that later GCs don't free up old memory from
+// previously run tests in this process.
+void ClearOutOldGarbage() {
+ ThreadHeap& heap = ThreadState::Current()->Heap();
+ while (true) {
+ size_t used = heap.ObjectPayloadSizeForTesting();
+ PreciselyCollectGarbage();
+ if (heap.ObjectPayloadSizeForTesting() >= used)
+ break;
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/heap/heap_test_utilities.h b/chromium/third_party/blink/renderer/platform/heap/heap_test_utilities.h
new file mode 100644
index 00000000000..5a5408109cb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/heap_test_utilities.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2017 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_TEST_UTILITIES_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_TEST_UTILITIES_H_
+
+namespace blink {
+
+void PreciselyCollectGarbage();
+void ConservativelyCollectGarbage();
+void ClearOutOldGarbage();
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_TEST_UTILITIES_H_
diff --git a/chromium/third_party/blink/renderer/platform/heap/heap_traits.h b/chromium/third_party/blink/renderer/platform/heap/heap_traits.h
new file mode 100644
index 00000000000..f72489def15
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/heap_traits.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_TRAITS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_TRAITS_H_
+
+#include <type_traits>
+#include "third_party/blink/renderer/platform/heap/heap_allocator.h"
+#include "third_party/blink/renderer/platform/heap/member.h"
+#include "third_party/blink/renderer/platform/wtf/type_traits.h"
+
+namespace blink {
+
+// Given a type T, returns a type that is either Member<T> or just T depending
+// on whether T is a garbage-collected type.
+template <typename T>
+using AddMemberIfNeeded =
+ std::conditional_t<WTF::IsGarbageCollectedType<T>::value, Member<T>, T>;
+
+// Given a type T, returns a type that is either HeapVector<T>,
+// HeapVector<Member<T>> or Vector<T> depending on T.
+template <typename T>
+using VectorOf = std::conditional_t<WTF::IsTraceable<T>::value,
+ HeapVector<AddMemberIfNeeded<T>>,
+ Vector<T>>;
+
+// Given types T and U, returns a type that is one of the following:
+// - HeapVector<std::pair<V, X>>
+// (where V is either T or Member<T> and X is either U or Member<U>)
+// - Vector<std::pair<T, U>>
+template <typename T, typename U>
+using VectorOfPairs = std::conditional_t<
+ WTF::IsTraceable<T>::value || WTF::IsTraceable<U>::value,
+ HeapVector<std::pair<AddMemberIfNeeded<T>, AddMemberIfNeeded<U>>>,
+ Vector<std::pair<T, U>>>;
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_TRAITS_H_
diff --git a/chromium/third_party/blink/renderer/platform/heap/heap_traits_test.cc b/chromium/third_party/blink/renderer/platform/heap/heap_traits_test.cc
new file mode 100644
index 00000000000..539c26c3757
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/heap_traits_test.cc
@@ -0,0 +1,126 @@
+// Copyright (c) 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <type_traits>
+#include <utility>
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/heap/heap_traits.h"
+#include "third_party/blink/renderer/platform/heap/member.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+// No gtest tests; only static_assert checks.
+
+namespace blink {
+
+class Visitor;
+
+namespace {
+
+struct Empty {};
+
+// Similar to an IDL union or dictionary, which have Trace() methods but are
+// not garbage-collected types themselves.
+struct StructWithTraceMethod {
+ void Trace(blink::Visitor*) {}
+};
+
+struct GarbageCollectedStruct
+ : public GarbageCollected<GarbageCollectedStruct> {
+ void Trace(blink::Visitor*) {}
+};
+
+// AddMemberIfNeeded<T>
+static_assert(std::is_same<AddMemberIfNeeded<double>, double>::value,
+ "AddMemberIfNeeded<double> must not add a Member wrapper");
+static_assert(std::is_same<AddMemberIfNeeded<double*>, double*>::value,
+ "AddMemberIfNeeded<double*> must not add a Member wrapper");
+
+static_assert(std::is_same<AddMemberIfNeeded<Empty>, Empty>::value,
+ "AddMemberIfNeeded<Empty> must not add a Member wrapper");
+
+static_assert(
+ std::is_same<AddMemberIfNeeded<StructWithTraceMethod>,
+ StructWithTraceMethod>::value,
+ "AddMemberIfNeeded<StructWithTraceMethod> must not add a Member wrapper");
+
+static_assert(
+ std::is_same<AddMemberIfNeeded<GarbageCollectedStruct>,
+ Member<GarbageCollectedStruct>>::value,
+ "AddMemberIfNeeded<GarbageCollectedStruct> must not add a Member wrapper");
+
+static_assert(
+ std::is_same<AddMemberIfNeeded<HeapVector<Member<GarbageCollectedStruct>>>,
+ Member<HeapVector<Member<GarbageCollectedStruct>>>>::value,
+ "AddMemberIfNeeded on a HeapVector<Member<T>> must wrap it in a Member<>");
+
+// VectorOf<T>
+static_assert(std::is_same<VectorOf<double>, Vector<double>>::value,
+ "VectorOf<double> should use a Vector");
+static_assert(std::is_same<VectorOf<double*>, Vector<double*>>::value,
+ "VectorOf<double*> should use a Vector");
+static_assert(std::is_same<VectorOf<Empty>, Vector<Empty>>::value,
+ "VectorOf<Empty> should use a Vector");
+
+static_assert(
+ std::is_same<VectorOf<StructWithTraceMethod>,
+ HeapVector<StructWithTraceMethod>>::value,
+ "VectorOf<StructWithTraceMethod> must not add a Member<> wrapper");
+static_assert(std::is_same<VectorOf<GarbageCollectedStruct>,
+ HeapVector<Member<GarbageCollectedStruct>>>::value,
+ "VectorOf<GarbageCollectedStruct> must add a Member<> wrapper");
+
+static_assert(
+ std::is_same<VectorOf<Vector<double>>, Vector<Vector<double>>>::value,
+ "Nested Vectors must not add HeapVectors");
+static_assert(
+ std::is_same<VectorOf<HeapVector<StructWithTraceMethod>>,
+ HeapVector<Member<HeapVector<StructWithTraceMethod>>>>::value,
+ "Nested HeapVector<StructWithTraceMethod> must add a HeapVector");
+static_assert(
+ std::is_same<
+ VectorOf<HeapVector<Member<GarbageCollectedStruct>>>,
+ HeapVector<Member<HeapVector<Member<GarbageCollectedStruct>>>>>::value,
+ "Nested HeapVectors must not add Vectors");
+
+// VectorOfPairs<T, U>
+static_assert(std::is_same<VectorOfPairs<int, double>,
+ Vector<std::pair<int, double>>>::value,
+ "POD types must use a regular Vector");
+static_assert(std::is_same<VectorOfPairs<Empty, double>,
+ Vector<std::pair<Empty, double>>>::value,
+ "POD types must use a regular Vector");
+
+static_assert(
+ std::is_same<VectorOfPairs<StructWithTraceMethod, float>,
+ HeapVector<std::pair<StructWithTraceMethod, float>>>::value,
+ "StructWithTraceMethod causes a HeapVector to be used");
+static_assert(
+ std::is_same<VectorOfPairs<float, StructWithTraceMethod>,
+ HeapVector<std::pair<float, StructWithTraceMethod>>>::value,
+ "StructWithTraceMethod causes a HeapVector to be used");
+static_assert(
+ std::is_same<VectorOfPairs<StructWithTraceMethod, StructWithTraceMethod>,
+ HeapVector<std::pair<StructWithTraceMethod,
+ StructWithTraceMethod>>>::value,
+ "StructWithTraceMethod causes a HeapVector to be used");
+
+static_assert(
+ std::is_same<
+ VectorOfPairs<GarbageCollectedStruct, float>,
+ HeapVector<std::pair<Member<GarbageCollectedStruct>, float>>>::value,
+ "GarbageCollectedStruct causes a HeapVector to be used");
+static_assert(
+ std::is_same<
+ VectorOfPairs<float, GarbageCollectedStruct>,
+ HeapVector<std::pair<float, Member<GarbageCollectedStruct>>>>::value,
+ "GarbageCollectedStruct causes a HeapVector to be used");
+static_assert(
+ std::is_same<VectorOfPairs<GarbageCollectedStruct, GarbageCollectedStruct>,
+ HeapVector<std::pair<Member<GarbageCollectedStruct>,
+ Member<GarbageCollectedStruct>>>>::value,
+ "GarbageCollectedStruct causes a HeapVector to be used");
+
+} // namespace
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/heap/incremental_marking_test.cc b/chromium/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
new file mode 100644
index 00000000000..c1db0127070
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
@@ -0,0 +1,1663 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <initializer_list>
+#include <vector>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/heap/heap_allocator.h"
+#include "third_party/blink/renderer/platform/heap/heap_buildflags.h"
+#include "third_party/blink/renderer/platform/heap/heap_terminated_array.h"
+#include "third_party/blink/renderer/platform/heap/heap_terminated_array_builder.h"
+#include "third_party/blink/renderer/platform/heap/member.h"
+#include "third_party/blink/renderer/platform/heap/thread_state.h"
+#include "third_party/blink/renderer/platform/heap/trace_traits.h"
+#include "third_party/blink/renderer/platform/heap/visitor.h"
+
+#if BUILDFLAG(BLINK_HEAP_INCREMENTAL_MARKING)
+
+namespace blink {
+namespace incremental_marking_test {
+
+// Visitor that expects every directly reachable object from a given backing
+// store to be in the set of provided objects.
+template <typename T>
+class BackingVisitor : public Visitor {
+ public:
+ explicit BackingVisitor(ThreadState* state, std::vector<T*>* objects)
+ : Visitor(state), objects_(objects) {}
+ virtual ~BackingVisitor() {}
+
+ void ProcessBackingStore(HeapObjectHeader* header) {
+ EXPECT_TRUE(header->IsValid());
+ EXPECT_TRUE(header->IsMarked());
+ header->Unmark();
+ ThreadHeap::GcInfo(header->GcInfoIndex())->trace_(this, header->Payload());
+ }
+
+ void Visit(void* obj, TraceDescriptor desc) final {
+ EXPECT_TRUE(obj);
+ auto pos = std::find(objects_->begin(), objects_->end(), obj);
+ if (objects_->end() != pos)
+ objects_->erase(pos);
+ // The garbage collector will find those objects so we can mark them.
+ HeapObjectHeader* const header =
+ HeapObjectHeader::FromPayload(desc.base_object_payload);
+ if (!header->IsMarked())
+ header->Mark();
+ }
+
+ // Unused overrides.
+ void VisitWeak(void* object,
+ void** object_slot,
+ TraceDescriptor desc,
+ WeakCallback callback) final {}
+ void VisitBackingStoreStrongly(void* object,
+ void** object_slot,
+ TraceDescriptor desc) final {}
+ void VisitBackingStoreWeakly(void*,
+ void**,
+ TraceDescriptor,
+ WeakCallback,
+ void*) final {}
+ void VisitBackingStoreOnly(void*, void**) final {}
+ void RegisterBackingStoreCallback(void* backing_store,
+ MovingObjectCallback,
+ void* callback_data) final {}
+ void RegisterWeakCallback(void* closure, WeakCallback) final {}
+
+ private:
+ std::vector<T*>* objects_;
+};
+
+// Base class for initializing worklists.
+class IncrementalMarkingScopeBase {
+ public:
+ IncrementalMarkingScopeBase(ThreadState* thread_state)
+ : thread_state_(thread_state), heap_(thread_state_->Heap()) {
+ heap_.CommitCallbackStacks();
+ }
+
+ ~IncrementalMarkingScopeBase() { heap_.DecommitCallbackStacks(); }
+
+ ThreadHeap& heap() const { return heap_; }
+
+ protected:
+ ThreadState* const thread_state_;
+ ThreadHeap& heap_;
+};
+
+class IncrementalMarkingScope : public IncrementalMarkingScopeBase {
+ public:
+ explicit IncrementalMarkingScope(ThreadState* thread_state)
+ : IncrementalMarkingScopeBase(thread_state),
+ gc_forbidden_scope_(thread_state),
+ marking_worklist_(heap_.GetMarkingWorklist()),
+ not_fully_constructed_worklist_(
+ heap_.GetNotFullyConstructedWorklist()) {
+ thread_state_->SetGCPhase(ThreadState::GCPhase::kMarking);
+ ThreadState::AtomicPauseScope atomic_pause_scope_(thread_state_);
+ EXPECT_TRUE(marking_worklist_->IsGlobalEmpty());
+ EXPECT_TRUE(not_fully_constructed_worklist_->IsGlobalEmpty());
+ thread_state->EnableIncrementalMarkingBarrier();
+ thread_state->current_gc_data_.visitor =
+ MarkingVisitor::Create(thread_state, MarkingVisitor::kGlobalMarking);
+ }
+
+ ~IncrementalMarkingScope() {
+ EXPECT_TRUE(marking_worklist_->IsGlobalEmpty());
+ EXPECT_TRUE(not_fully_constructed_worklist_->IsGlobalEmpty());
+ thread_state_->DisableIncrementalMarkingBarrier();
+ // Need to clear out unused worklists that might have been polluted during
+ // test.
+ heap_.GetWeakCallbackWorklist()->Clear();
+ thread_state_->SetGCPhase(ThreadState::GCPhase::kSweeping);
+ thread_state_->SetGCPhase(ThreadState::GCPhase::kNone);
+ }
+
+ MarkingWorklist* marking_worklist() const { return marking_worklist_; }
+ NotFullyConstructedWorklist* not_fully_constructed_worklist() const {
+ return not_fully_constructed_worklist_;
+ }
+
+ protected:
+ ThreadState::GCForbiddenScope gc_forbidden_scope_;
+ MarkingWorklist* const marking_worklist_;
+ NotFullyConstructedWorklist* const not_fully_constructed_worklist_;
+};
+
+// Expects that the write barrier fires for the objects passed to the
+// constructor. This requires that the objects are added to the marking stack
+// as well as headers being marked.
+template <typename T>
+class ExpectWriteBarrierFires : public IncrementalMarkingScope {
+ public:
+ ExpectWriteBarrierFires(ThreadState* thread_state,
+ std::initializer_list<T*> objects)
+ : IncrementalMarkingScope(thread_state), objects_(objects) {
+ EXPECT_TRUE(marking_worklist_->IsGlobalEmpty());
+ for (T* object : objects_) {
+ // Ensure that the object is in the normal arena so we can ignore backing
+ // objects on the marking stack.
+ CHECK(ThreadHeap::IsNormalArenaIndex(
+ PageFromObject(object)->Arena()->ArenaIndex()));
+ headers_.push_back(HeapObjectHeader::FromPayload(object));
+ EXPECT_FALSE(headers_.back()->IsMarked());
+ }
+ EXPECT_FALSE(objects_.empty());
+ }
+
+ ~ExpectWriteBarrierFires() {
+ EXPECT_FALSE(marking_worklist_->IsGlobalEmpty());
+ MarkingItem item;
+ // All objects watched should be on the marking stack.
+ while (marking_worklist_->Pop(WorklistTaskId::MainThread, &item)) {
+ T* obj = reinterpret_cast<T*>(item.object);
+ // Inspect backing stores to allow specifying objects that are only
+ // reachable through a backing store.
+ if (!ThreadHeap::IsNormalArenaIndex(
+ PageFromObject(obj)->Arena()->ArenaIndex())) {
+ BackingVisitor<T> visitor(thread_state_, &objects_);
+ visitor.ProcessBackingStore(HeapObjectHeader::FromPayload(obj));
+ continue;
+ }
+ auto pos = std::find(objects_.begin(), objects_.end(), obj);
+ if (objects_.end() != pos)
+ objects_.erase(pos);
+ }
+ EXPECT_TRUE(objects_.empty());
+ // All headers of objects watched should be marked at this point.
+ for (HeapObjectHeader* header : headers_) {
+ EXPECT_TRUE(header->IsMarked());
+ header->Unmark();
+ }
+ EXPECT_TRUE(marking_worklist_->IsGlobalEmpty());
+ }
+
+ private:
+ std::vector<T*> objects_;
+ std::vector<HeapObjectHeader*> headers_;
+};
+
+// Expects that no write barrier fires for the objects passed to the
+// constructor. This requires that the marking stack stays empty and the marking
+// state of the object stays the same across the lifetime of the scope.
+template <typename T>
+class ExpectNoWriteBarrierFires : public IncrementalMarkingScope {
+ public:
+ ExpectNoWriteBarrierFires(ThreadState* thread_state,
+ std::initializer_list<T*> objects)
+ : IncrementalMarkingScope(thread_state), objects_(objects) {
+ EXPECT_TRUE(marking_worklist_->IsGlobalEmpty());
+ for (T* object : objects_) {
+ HeapObjectHeader* header = HeapObjectHeader::FromPayload(object);
+ headers_.push_back(header);
+ was_marked_.push_back(header->IsMarked());
+ }
+ }
+
+ ~ExpectNoWriteBarrierFires() {
+ EXPECT_TRUE(marking_worklist_->IsGlobalEmpty());
+ for (size_t i = 0; i < headers_.size(); i++) {
+ EXPECT_EQ(was_marked_[i], headers_[i]->IsMarked());
+ headers_[i]->Unmark();
+ }
+ }
+
+ private:
+ std::vector<T*> objects_;
+ std::vector<HeapObjectHeader*> headers_;
+ std::vector<bool> was_marked_;
+};
+
+class Object : public GarbageCollected<Object> {
+ public:
+ static Object* Create() { return new Object(); }
+ static Object* Create(Object* next) { return new Object(next); }
+
+ void set_next(Object* next) { next_ = next; }
+
+ virtual void Trace(blink::Visitor* visitor) { visitor->Trace(next_); }
+
+ bool IsMarked() const {
+ return HeapObjectHeader::FromPayload(this)->IsMarked();
+ }
+
+ private:
+ Object() : next_(nullptr) {}
+ explicit Object(Object* next) : next_(next) {}
+
+ Member<Object> next_;
+};
+
+// =============================================================================
+// Basic infrastructure support. ===============================================
+// =============================================================================
+
+TEST(IncrementalMarkingTest, EnableDisableBarrier) {
+ EXPECT_FALSE(ThreadState::Current()->IsIncrementalMarking());
+ ThreadState::Current()->EnableIncrementalMarkingBarrier();
+ EXPECT_TRUE(ThreadState::Current()->IsIncrementalMarking());
+ EXPECT_TRUE(ThreadState::IsAnyIncrementalMarking());
+ ThreadState::Current()->DisableIncrementalMarkingBarrier();
+ EXPECT_FALSE(ThreadState::Current()->IsIncrementalMarking());
+}
+
+TEST(IncrementalMarkingTest, StackFrameDepthDisabled) {
+ IncrementalMarkingScope scope(ThreadState::Current());
+ EXPECT_FALSE(scope.heap().GetStackFrameDepth().IsSafeToRecurse());
+}
+
+TEST(IncrementalMarkingTest, ManualWriteBarrierTriggersWhenMarkingIsOn) {
+ Object* object = Object::Create();
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {object});
+ EXPECT_FALSE(object->IsMarked());
+ MarkingVisitor::WriteBarrier(object);
+ EXPECT_TRUE(object->IsMarked());
+ }
+}
+
+TEST(IncrementalMarkingTest, ManualWriteBarrierBailoutWhenMarkingIsOff) {
+ Object* object = Object::Create();
+ EXPECT_FALSE(object->IsMarked());
+ MarkingVisitor::WriteBarrier(object);
+ EXPECT_FALSE(object->IsMarked());
+}
+
+// =============================================================================
+// Member<T> support. ==========================================================
+// =============================================================================
+
+TEST(IncrementalMarkingTest, MemberSetUnmarkedObject) {
+ Object* parent = Object::Create();
+ Object* child = Object::Create();
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {child});
+ EXPECT_FALSE(child->IsMarked());
+ parent->set_next(child);
+ EXPECT_TRUE(child->IsMarked());
+ }
+}
+
+TEST(IncrementalMarkingTest, MemberSetMarkedObjectNoBarrier) {
+ Object* parent = Object::Create();
+ Object* child = Object::Create();
+ HeapObjectHeader::FromPayload(child)->Mark();
+ {
+ ExpectNoWriteBarrierFires<Object> scope(ThreadState::Current(), {child});
+ parent->set_next(child);
+ }
+}
+
+TEST(IncrementalMarkingTest, MemberInitializingStoreNoBarrier) {
+ Object* object1 = Object::Create();
+ HeapObjectHeader* object1_header = HeapObjectHeader::FromPayload(object1);
+ {
+ IncrementalMarkingScope scope(ThreadState::Current());
+ EXPECT_FALSE(object1_header->IsMarked());
+ Object* object2 = Object::Create(object1);
+ HeapObjectHeader* object2_header = HeapObjectHeader::FromPayload(object2);
+ EXPECT_FALSE(object1_header->IsMarked());
+ EXPECT_FALSE(object2_header->IsMarked());
+ }
+}
+
+TEST(IncrementalMarkingTest, MemberReferenceAssignMember) {
+ Object* obj = Object::Create();
+ Member<Object> m1;
+ Member<Object>& m2 = m1;
+ Member<Object> m3(obj);
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
+ m2 = m3;
+ }
+}
+
+TEST(IncrementalMarkingTest, MemberSetDeletedValueNoBarrier) {
+ Member<Object> m;
+ {
+ ExpectNoWriteBarrierFires<Object> scope(ThreadState::Current(), {});
+ m = WTF::kHashTableDeletedValue;
+ }
+}
+
+TEST(IncrementalMarkingTest, MemberCopyDeletedValueNoBarrier) {
+ Member<Object> m1(WTF::kHashTableDeletedValue);
+ {
+ ExpectNoWriteBarrierFires<Object> scope(ThreadState::Current(), {});
+ Member<Object> m2(m1);
+ }
+}
+
+TEST(IncrementalMarkingTest, MemberHashTraitConstructDeletedValueNoBarrier) {
+ Member<Object> m1;
+ {
+ ExpectNoWriteBarrierFires<Object> scope(ThreadState::Current(), {});
+ HashTraits<Member<Object>>::ConstructDeletedValue(m1, false);
+ }
+}
+
+TEST(IncrementalMarkingTest, MemberHashTraitIsDeletedValueNoBarrier) {
+ Member<Object> m1(Object::Create());
+ {
+ ExpectNoWriteBarrierFires<Object> scope(ThreadState::Current(), {});
+ EXPECT_FALSE(HashTraits<Member<Object>>::IsDeletedValue(m1));
+ }
+}
+
+// =============================================================================
+// Mixin support. ==============================================================
+// =============================================================================
+
+namespace {
+
+class Mixin : public GarbageCollectedMixin {
+ public:
+ Mixin() : next_(nullptr) {}
+ virtual ~Mixin() {}
+
+ virtual void Trace(blink::Visitor* visitor) { visitor->Trace(next_); }
+
+ virtual void Bar() {}
+
+ protected:
+ Member<Object> next_;
+};
+
+class ClassWithVirtual {
+ protected:
+ virtual void Foo() {}
+};
+
+class Child : public GarbageCollected<Child>,
+ public ClassWithVirtual,
+ public Mixin {
+ USING_GARBAGE_COLLECTED_MIXIN(Child);
+
+ public:
+ static Child* Create() { return new Child(); }
+ virtual ~Child() {}
+
+ virtual void Trace(blink::Visitor* visitor) { Mixin::Trace(visitor); }
+
+ virtual void Foo() {}
+ virtual void Bar() {}
+
+ protected:
+ Child() : ClassWithVirtual(), Mixin() {}
+};
+
+class ParentWithMixinPointer : public GarbageCollected<ParentWithMixinPointer> {
+ public:
+ static ParentWithMixinPointer* Create() {
+ return new ParentWithMixinPointer();
+ }
+
+ void set_mixin(Mixin* mixin) { mixin_ = mixin; }
+
+ virtual void Trace(blink::Visitor* visitor) { visitor->Trace(mixin_); }
+
+ protected:
+ ParentWithMixinPointer() : mixin_(nullptr) {}
+
+ Member<Mixin> mixin_;
+};
+
+} // namespace
+
+TEST(IncrementalMarkingTest, WriteBarrierOnUnmarkedMixinApplication) {
+ ParentWithMixinPointer* parent = ParentWithMixinPointer::Create();
+ Child* child = Child::Create();
+ Mixin* mixin = static_cast<Mixin*>(child);
+ EXPECT_NE(static_cast<void*>(child), static_cast<void*>(mixin));
+ {
+ ExpectWriteBarrierFires<Child> scope(ThreadState::Current(), {child});
+ parent->set_mixin(mixin);
+ }
+}
+
+TEST(IncrementalMarkingTest, NoWriteBarrierOnMarkedMixinApplication) {
+ ParentWithMixinPointer* parent = ParentWithMixinPointer::Create();
+ Child* child = Child::Create();
+ HeapObjectHeader::FromPayload(child)->Mark();
+ Mixin* mixin = static_cast<Mixin*>(child);
+ EXPECT_NE(static_cast<void*>(child), static_cast<void*>(mixin));
+ {
+ ExpectNoWriteBarrierFires<Child> scope(ThreadState::Current(), {child});
+ parent->set_mixin(mixin);
+ }
+}
+
+// =============================================================================
+// HeapVector support. =========================================================
+// =============================================================================
+
+namespace {
+
+// HeapVector allows for insertion of container objects that can be traced but
+// are themselves non-garbage collected.
+class NonGarbageCollectedContainer {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ NonGarbageCollectedContainer(Object* obj, int y) : obj_(obj), y_(y) {}
+
+ virtual ~NonGarbageCollectedContainer() {}
+ virtual void Trace(blink::Visitor* visitor) { visitor->Trace(obj_); }
+
+ private:
+ Member<Object> obj_;
+ int y_;
+};
+
+class NonGarbageCollectedContainerRoot {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ NonGarbageCollectedContainerRoot(Object* obj1, Object* obj2, int y)
+ : next_(obj1, y), obj_(obj2) {}
+ virtual ~NonGarbageCollectedContainerRoot() {}
+
+ virtual void Trace(blink::Visitor* visitor) {
+ visitor->Trace(next_);
+ visitor->Trace(obj_);
+ }
+
+ private:
+ NonGarbageCollectedContainer next_;
+ Member<Object> obj_;
+};
+
+} // namespace
+
+TEST(IncrementalMarkingTest, HeapVectorPushBackMember) {
+ Object* obj = Object::Create();
+ HeapVector<Member<Object>> vec;
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
+ vec.push_back(obj);
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapVectorPushBackNonGCedContainer) {
+ Object* obj = Object::Create();
+ HeapVector<NonGarbageCollectedContainer> vec;
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
+ vec.push_back(NonGarbageCollectedContainer(obj, 1));
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapVectorPushBackStdPair) {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ HeapVector<std::pair<Member<Object>, Member<Object>>> vec;
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
+ vec.push_back(std::make_pair(Member<Object>(obj1), Member<Object>(obj2)));
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapVectorEmplaceBackMember) {
+ Object* obj = Object::Create();
+ HeapVector<Member<Object>> vec;
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
+ vec.emplace_back(obj);
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapVectorEmplaceBackNonGCedContainer) {
+ Object* obj = Object::Create();
+ HeapVector<NonGarbageCollectedContainer> vec;
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
+ vec.emplace_back(obj, 1);
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapVectorEmplaceBackStdPair) {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ HeapVector<std::pair<Member<Object>, Member<Object>>> vec;
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
+ vec.emplace_back(obj1, obj2);
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapVectorCopyMember) {
+ Object* object = Object::Create();
+ HeapVector<Member<Object>> vec1;
+ vec1.push_back(object);
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {object});
+ HeapVector<Member<Object>> vec2(vec1);
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapVectorCopyNonGCedContainer) {
+ Object* obj = Object::Create();
+ HeapVector<NonGarbageCollectedContainer> vec1;
+ vec1.emplace_back(obj, 1);
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
+ HeapVector<NonGarbageCollectedContainer> vec2(vec1);
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapVectorCopyStdPair) {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ HeapVector<std::pair<Member<Object>, Member<Object>>> vec1;
+ vec1.emplace_back(obj1, obj2);
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
+ HeapVector<std::pair<Member<Object>, Member<Object>>> vec2(vec1);
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapVectorMoveMember) {
+ Object* obj = Object::Create();
+ HeapVector<Member<Object>> vec1;
+ vec1.push_back(obj);
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
+ HeapVector<Member<Object>> vec2(std::move(vec1));
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapVectorMoveNonGCedContainer) {
+ Object* obj = Object::Create();
+ HeapVector<NonGarbageCollectedContainer> vec1;
+ vec1.emplace_back(obj, 1);
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
+ HeapVector<NonGarbageCollectedContainer> vec2(std::move(vec1));
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapVectorMoveStdPair) {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ HeapVector<std::pair<Member<Object>, Member<Object>>> vec1;
+ vec1.emplace_back(obj1, obj2);
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
+ HeapVector<std::pair<Member<Object>, Member<Object>>> vec2(std::move(vec1));
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapVectorSwapMember) {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ HeapVector<Member<Object>> vec1;
+ vec1.push_back(obj1);
+ HeapVector<Member<Object>> vec2;
+ vec2.push_back(obj2);
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
+ std::swap(vec1, vec2);
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapVectorSwapNonGCedContainer) {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ HeapVector<NonGarbageCollectedContainer> vec1;
+ vec1.emplace_back(obj1, 1);
+ HeapVector<NonGarbageCollectedContainer> vec2;
+ vec2.emplace_back(obj2, 2);
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
+ std::swap(vec1, vec2);
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapVectorSwapStdPair) {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ HeapVector<std::pair<Member<Object>, Member<Object>>> vec1;
+ vec1.emplace_back(obj1, nullptr);
+ HeapVector<std::pair<Member<Object>, Member<Object>>> vec2;
+ vec2.emplace_back(nullptr, obj2);
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
+ std::swap(vec1, vec2);
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapVectorSubscriptOperator) {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ HeapVector<Member<Object>> vec;
+ vec.push_back(obj1);
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj2});
+ EXPECT_EQ(1u, vec.size());
+ EXPECT_EQ(obj1, vec[0]);
+ vec[0] = obj2;
+ EXPECT_EQ(obj2, vec[0]);
+ EXPECT_FALSE(obj1->IsMarked());
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapVectorEagerTracingStopsAtMember) {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ Object* obj3 = Object::Create();
+ obj1->set_next(obj3);
+ HeapVector<NonGarbageCollectedContainerRoot> vec;
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
+ vec.emplace_back(obj1, obj2, 3);
+ // |obj3| is only reachable from |obj1| which is not eagerly traced. Only
+ // objects without object headers are eagerly traced.
+ EXPECT_FALSE(obj3->IsMarked());
+ }
+}
+
+// =============================================================================
+// HeapDoublyLinkedList support. ===============================================
+// =============================================================================
+
+namespace {
+
+class ObjectNode : public GarbageCollected<ObjectNode>,
+ public DoublyLinkedListNode<ObjectNode> {
+ public:
+ explicit ObjectNode(Object* obj) : obj_(obj) {}
+
+ void Trace(Visitor* visitor) {
+ visitor->Trace(obj_);
+ visitor->Trace(prev_);
+ visitor->Trace(next_);
+ }
+
+ private:
+ friend class WTF::DoublyLinkedListNode<ObjectNode>;
+
+ Member<Object> obj_;
+ Member<ObjectNode> prev_;
+ Member<ObjectNode> next_;
+};
+
+} // namespace
+
+TEST(IncrementalMarkingTest, HeapDoublyLinkedListPush) {
+ Object* obj = Object::Create();
+ ObjectNode* obj_node = new ObjectNode(obj);
+ HeapDoublyLinkedList<ObjectNode> list;
+ {
+ ExpectWriteBarrierFires<ObjectNode> scope(ThreadState::Current(),
+ {obj_node});
+ list.Push(obj_node);
+ // |obj| will be marked once |obj_node| gets processed.
+ EXPECT_FALSE(obj->IsMarked());
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapDoublyLinkedListAppend) {
+ Object* obj = Object::Create();
+ ObjectNode* obj_node = new ObjectNode(obj);
+ HeapDoublyLinkedList<ObjectNode> list;
+ {
+ ExpectWriteBarrierFires<ObjectNode> scope(ThreadState::Current(),
+ {obj_node});
+ list.Append(obj_node);
+ // |obj| will be marked once |obj_node| gets processed.
+ EXPECT_FALSE(obj->IsMarked());
+ }
+}
+
+// =============================================================================
+// HeapDeque support. ==========================================================
+// =============================================================================
+
+TEST(IncrementalMarkingTest, HeapDequePushBackMember) {
+ Object* obj = Object::Create();
+ HeapDeque<Member<Object>> deq;
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
+ deq.push_back(obj);
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapDequePushFrontMember) {
+ Object* obj = Object::Create();
+ HeapDeque<Member<Object>> deq;
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
+ deq.push_front(obj);
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapDequeEmplaceBackMember) {
+ Object* obj = Object::Create();
+ HeapDeque<Member<Object>> deq;
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
+ deq.emplace_back(obj);
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapDequeEmplaceFrontMember) {
+ Object* obj = Object::Create();
+ HeapDeque<Member<Object>> deq;
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
+ deq.emplace_front(obj);
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapDequeCopyMember) {
+ Object* object = Object::Create();
+ HeapDeque<Member<Object>> deq1;
+ deq1.push_back(object);
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {object});
+ HeapDeque<Member<Object>> deq2(deq1);
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapDequeMoveMember) {
+ Object* object = Object::Create();
+ HeapDeque<Member<Object>> deq1;
+ deq1.push_back(object);
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {object});
+ HeapDeque<Member<Object>> deq2(std::move(deq1));
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapDequeSwapMember) {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ HeapDeque<Member<Object>> deq1;
+ deq1.push_back(obj1);
+ HeapDeque<Member<Object>> deq2;
+ deq2.push_back(obj2);
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
+ std::swap(deq1, deq2);
+ }
+}
+
+// =============================================================================
+// HeapHashSet support. ========================================================
+// =============================================================================
+
+namespace {
+
+template <typename Container>
+void Insert() {
+ Object* obj = Object::Create();
+ Container container;
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
+ container.insert(obj);
+ }
+}
+
+template <typename Container>
+void InsertNoBarrier() {
+ Object* obj = Object::Create();
+ Container container;
+ {
+ ExpectNoWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
+ container.insert(obj);
+ }
+}
+
+template <typename Container>
+void Copy() {
+ Object* obj = Object::Create();
+ Container container1;
+ container1.insert(obj);
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
+ Container container2(container1);
+ EXPECT_TRUE(container1.Contains(obj));
+ EXPECT_TRUE(container2.Contains(obj));
+ }
+}
+
+template <typename Container>
+void CopyNoBarrier() {
+ Object* obj = Object::Create();
+ Container container1;
+ container1.insert(obj);
+ {
+ ExpectNoWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
+ Container container2(container1);
+ EXPECT_TRUE(container1.Contains(obj));
+ EXPECT_TRUE(container2.Contains(obj));
+ }
+}
+
+template <typename Container>
+void Move() {
+ Object* obj = Object::Create();
+ Container container1;
+ container1.insert(obj);
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
+ Container container2(std::move(container1));
+ }
+}
+
+template <typename Container>
+void MoveNoBarrier() {
+ Object* obj = Object::Create();
+ Container container1;
+ container1.insert(obj);
+ {
+ ExpectNoWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
+ Container container2(std::move(container1));
+ }
+}
+
+template <typename Container>
+void Swap() {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ Container container1;
+ container1.insert(obj1);
+ Container container2;
+ container2.insert(obj2);
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
+ std::swap(container1, container2);
+ }
+}
+
+template <typename Container>
+void SwapNoBarrier() {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ Container container1;
+ container1.insert(obj1);
+ Container container2;
+ container2.insert(obj2);
+ {
+ ExpectNoWriteBarrierFires<Object> scope(ThreadState::Current(),
+ {obj1, obj2});
+ std::swap(container1, container2);
+ }
+}
+
+} // namespace
+
+TEST(IncrementalMarkingTest, HeapHashSetInsert) {
+ Insert<HeapHashSet<Member<Object>>>();
+ // Weak references are strongified for the current cycle.
+ Insert<HeapHashSet<WeakMember<Object>>>();
+}
+
+TEST(IncrementalMarkingTest, HeapHashSetCopy) {
+ Copy<HeapHashSet<Member<Object>>>();
+ // Weak references are strongified for the current cycle.
+ Copy<HeapHashSet<WeakMember<Object>>>();
+}
+
+TEST(IncrementalMarkingTest, HeapHashSetMove) {
+ Move<HeapHashSet<Member<Object>>>();
+ // Weak references are strongified for the current cycle.
+ Move<HeapHashSet<WeakMember<Object>>>();
+}
+
+TEST(IncrementalMarkingTest, HeapHashSetSwap) {
+ Swap<HeapHashSet<Member<Object>>>();
+ // Weak references are strongified for the current cycle.
+ Swap<HeapHashSet<WeakMember<Object>>>();
+}
+
+class StrongWeakPair : public std::pair<Member<Object>, WeakMember<Object>> {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ typedef std::pair<Member<Object>, WeakMember<Object>> Base;
+
+ public:
+ StrongWeakPair(Object* obj1, Object* obj2) : Base(obj1, obj2) {}
+
+ StrongWeakPair(WTF::HashTableDeletedValueType)
+ : Base(WTF::kHashTableDeletedValue, nullptr) {}
+
+ bool IsHashTableDeletedValue() const {
+ return first.IsHashTableDeletedValue();
+ }
+
+ // Trace will be called for write barrier invocations. Only strong members
+ // are interesting.
+ void Trace(blink::Visitor* visitor) { visitor->Trace(first); }
+
+ // TraceInCollection will be called for weak processing.
+ template <typename VisitorDispatcher>
+ bool TraceInCollection(VisitorDispatcher visitor,
+ WTF::WeakHandlingFlag weakness) {
+ visitor->Trace(first);
+ if (weakness == WTF::kNoWeakHandling) {
+ visitor->Trace(second);
+ }
+ return false;
+ }
+};
+
+} // namespace incremental_marking_test
+} // namespace blink
+
+namespace WTF {
+
+template <>
+struct HashTraits<blink::incremental_marking_test::StrongWeakPair>
+ : SimpleClassHashTraits<blink::incremental_marking_test::StrongWeakPair> {
+ static const WTF::WeakHandlingFlag kWeakHandlingFlag = WTF::kWeakHandling;
+
+ template <typename U = void>
+ struct IsTraceableInCollection {
+ static const bool value = true;
+ };
+
+ static const bool kHasIsEmptyValueFunction = true;
+ static bool IsEmptyValue(
+ const blink::incremental_marking_test::StrongWeakPair& value) {
+ return !value.first;
+ }
+
+ static void ConstructDeletedValue(
+ blink::incremental_marking_test::StrongWeakPair& slot,
+ bool) {
+ new (NotNull, &slot)
+ blink::incremental_marking_test::StrongWeakPair(kHashTableDeletedValue);
+ }
+
+ static bool IsDeletedValue(
+ const blink::incremental_marking_test::StrongWeakPair& value) {
+ return value.IsHashTableDeletedValue();
+ }
+
+ template <typename VisitorDispatcher>
+ static bool TraceInCollection(
+ VisitorDispatcher visitor,
+ blink::incremental_marking_test::StrongWeakPair& t,
+ WTF::WeakHandlingFlag weakness) {
+ return t.TraceInCollection(visitor, weakness);
+ }
+};
+
+template <>
+struct DefaultHash<blink::incremental_marking_test::StrongWeakPair> {
+ typedef PairHash<blink::Member<blink::incremental_marking_test::Object>,
+ blink::WeakMember<blink::incremental_marking_test::Object>>
+ Hash;
+};
+
+template <>
+struct IsTraceable<blink::incremental_marking_test::StrongWeakPair> {
+ static const bool value = IsTraceable<std::pair<
+ blink::Member<blink::incremental_marking_test::Object>,
+ blink::WeakMember<blink::incremental_marking_test::Object>>>::value;
+};
+
+} // namespace WTF
+
+namespace blink {
+namespace incremental_marking_test {
+
+TEST(IncrementalMarkingTest, HeapHashSetStrongWeakPair) {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ HeapHashSet<StrongWeakPair> set;
+ {
+ // Only the strong field in the StrongWeakPair should be hit by the
+ // write barrier.
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1});
+ set.insert(StrongWeakPair(obj1, obj2));
+ EXPECT_FALSE(obj2->IsMarked());
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapLinkedHashSetStrongWeakPair) {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ HeapLinkedHashSet<StrongWeakPair> set;
+ {
+ // Only the strong field in the StrongWeakPair should be hit by the
+ // write barrier.
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1});
+ set.insert(StrongWeakPair(obj1, obj2));
+ EXPECT_FALSE(obj2->IsMarked());
+ }
+}
+
+// =============================================================================
+// HeapLinkedHashSet support. ==================================================
+// =============================================================================
+
+TEST(IncrementalMarkingTest, HeapLinkedHashSetInsert) {
+ Insert<HeapLinkedHashSet<Member<Object>>>();
+ // Weak references are strongified for the current cycle.
+ Insert<HeapLinkedHashSet<WeakMember<Object>>>();
+}
+
+TEST(IncrementalMarkingTest, HeapLinkedHashSetCopy) {
+ Copy<HeapLinkedHashSet<Member<Object>>>();
+ // Weak references are strongified for the current cycle.
+ Copy<HeapLinkedHashSet<WeakMember<Object>>>();
+}
+
+TEST(IncrementalMarkingTest, HeapLinkedHashSetMove) {
+ Move<HeapLinkedHashSet<Member<Object>>>();
+ // Weak references are strongified for the current cycle.
+ Move<HeapLinkedHashSet<WeakMember<Object>>>();
+}
+
+TEST(IncrementalMarkingTest, HeapLinkedHashSetSwap) {
+ Swap<HeapLinkedHashSet<Member<Object>>>();
+ // Weak references are strongified for the current cycle.
+ Move<HeapLinkedHashSet<WeakMember<Object>>>();
+}
+
+// =============================================================================
+// HeapHashCountedSet support. =================================================
+// =============================================================================
+
+// HeapHashCountedSet does not support copy or move.
+
+TEST(IncrementalMarkingTest, HeapHashCountedSetInsert) {
+ Insert<HeapHashCountedSet<Member<Object>>>();
+ // Weak references are strongified for the current cycle.
+ Insert<HeapHashCountedSet<WeakMember<Object>>>();
+}
+
+TEST(IncrementalMarkingTest, HeapHashCountedSetSwap) {
+ // HeapHashCountedSet is not move constructible so we cannot use std::swap.
+ {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ HeapHashCountedSet<Member<Object>> container1;
+ container1.insert(obj1);
+ HeapHashCountedSet<Member<Object>> container2;
+ container2.insert(obj2);
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(),
+ {obj1, obj2});
+ container1.swap(container2);
+ }
+ }
+ {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ HeapHashCountedSet<WeakMember<Object>> container1;
+ container1.insert(obj1);
+ HeapHashCountedSet<WeakMember<Object>> container2;
+ container2.insert(obj2);
+ {
+ // Weak references are strongified for the current cycle.
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(),
+ {obj1, obj2});
+ container1.swap(container2);
+ }
+ }
+}
+
+// =============================================================================
+// HeapTerminatedArray support. ================================================
+// =============================================================================
+
+class TerminatedArrayNode {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ TerminatedArrayNode(Object* obj) : obj_(obj), is_last_in_array_(false) {}
+
+ // TerminatedArray support.
+ bool IsLastInArray() const { return is_last_in_array_; }
+ void SetLastInArray(bool flag) { is_last_in_array_ = flag; }
+
+ void Trace(blink::Visitor* visitor) { visitor->Trace(obj_); }
+
+ private:
+ Member<Object> obj_;
+ bool is_last_in_array_;
+};
+
+} // namespace incremental_marking_test
+} // namespace blink
+
+WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(
+ blink::incremental_marking_test::TerminatedArrayNode);
+
+namespace blink {
+namespace incremental_marking_test {
+
+TEST(IncrementalMarkingTest, HeapTerminatedArrayBuilder) {
+ Object* obj = Object::Create();
+ HeapTerminatedArray<TerminatedArrayNode>* array = nullptr;
+ {
+ // The builder allocates the backing store on Oilpans heap, effectively
+ // triggering a write barrier.
+ HeapTerminatedArrayBuilder<TerminatedArrayNode> builder(array);
+ builder.Grow(1);
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj});
+ builder.Append(TerminatedArrayNode(obj));
+ }
+ array = builder.Release();
+ }
+}
+
+// =============================================================================
+// HeapHashMap support. ========================================================
+// =============================================================================
+
+TEST(IncrementalMarkingTest, HeapHashMapInsertMember) {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ HeapHashMap<Member<Object>, Member<Object>> map;
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
+ map.insert(obj1, obj2);
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapHashMapInsertWeakMember) {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ HeapHashMap<WeakMember<Object>, WeakMember<Object>> map;
+ {
+ // Weak references are strongified for the current cycle.
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
+ map.insert(obj1, obj2);
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapHashMapInsertMemberWeakMember) {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ HeapHashMap<Member<Object>, WeakMember<Object>> map;
+ {
+ // Weak references are strongified for the current cycle.
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
+ map.insert(obj1, obj2);
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapHashMapInsertWeakMemberMember) {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ HeapHashMap<WeakMember<Object>, Member<Object>> map;
+ {
+ // Weak references are strongified for the current cycle.
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
+ map.insert(obj1, obj2);
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapHashMapSetMember) {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ HeapHashMap<Member<Object>, Member<Object>> map;
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
+ map.Set(obj1, obj2);
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapHashMapSetMemberUpdateValue) {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ Object* obj3 = Object::Create();
+ HeapHashMap<Member<Object>, Member<Object>> map;
+ map.insert(obj1, obj2);
+ {
+ // Only |obj3| is newly added to |map|, so we only expect the barrier to
+ // fire on this one.
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj3});
+ map.Set(obj1, obj3);
+ EXPECT_FALSE(HeapObjectHeader::FromPayload(obj1)->IsMarked());
+ EXPECT_FALSE(HeapObjectHeader::FromPayload(obj2)->IsMarked());
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapHashMapIteratorChangeKey) {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ Object* obj3 = Object::Create();
+ HeapHashMap<Member<Object>, Member<Object>> map;
+ map.insert(obj1, obj2);
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj3});
+ auto it = map.find(obj1);
+ EXPECT_NE(map.end(), it);
+ it->key = obj3;
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapHashMapIteratorChangeValue) {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ Object* obj3 = Object::Create();
+ HeapHashMap<Member<Object>, Member<Object>> map;
+ map.insert(obj1, obj2);
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj3});
+ auto it = map.find(obj1);
+ EXPECT_NE(map.end(), it);
+ it->value = obj3;
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapHashMapCopyMemberMember) {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ HeapHashMap<Member<Object>, Member<Object>> map1;
+ map1.insert(obj1, obj2);
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
+ EXPECT_TRUE(map1.Contains(obj1));
+ HeapHashMap<Member<Object>, Member<Object>> map2(map1);
+ EXPECT_TRUE(map1.Contains(obj1));
+ EXPECT_TRUE(map2.Contains(obj1));
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapHashMapCopyWeakMemberWeakMember) {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ HeapHashMap<WeakMember<Object>, WeakMember<Object>> map1;
+ map1.insert(obj1, obj2);
+ {
+ // Weak references are strongified for the current cycle.
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
+ EXPECT_TRUE(map1.Contains(obj1));
+ HeapHashMap<WeakMember<Object>, WeakMember<Object>> map2(map1);
+ EXPECT_TRUE(map1.Contains(obj1));
+ EXPECT_TRUE(map2.Contains(obj1));
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapHashMapCopyMemberWeakMember) {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ HeapHashMap<Member<Object>, WeakMember<Object>> map1;
+ map1.insert(obj1, obj2);
+ {
+ // Weak references are strongified for the current cycle.
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
+ EXPECT_TRUE(map1.Contains(obj1));
+ HeapHashMap<Member<Object>, WeakMember<Object>> map2(map1);
+ EXPECT_TRUE(map1.Contains(obj1));
+ EXPECT_TRUE(map2.Contains(obj1));
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapHashMapCopyWeakMemberMember) {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ HeapHashMap<WeakMember<Object>, Member<Object>> map1;
+ map1.insert(obj1, obj2);
+ {
+ // Weak references are strongified for the current cycle.
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
+ EXPECT_TRUE(map1.Contains(obj1));
+ HeapHashMap<WeakMember<Object>, Member<Object>> map2(map1);
+ EXPECT_TRUE(map1.Contains(obj1));
+ EXPECT_TRUE(map2.Contains(obj1));
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapHashMapMoveMember) {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ HeapHashMap<Member<Object>, Member<Object>> map1;
+ map1.insert(obj1, obj2);
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
+ HeapHashMap<Member<Object>, Member<Object>> map2(std::move(map1));
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapHashMapMoveWeakMember) {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ HeapHashMap<WeakMember<Object>, WeakMember<Object>> map1;
+ map1.insert(obj1, obj2);
+ {
+ // Weak references are strongified for the current cycle.
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
+ HeapHashMap<WeakMember<Object>, WeakMember<Object>> map2(std::move(map1));
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapHashMapMoveMemberWeakMember) {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ HeapHashMap<Member<Object>, WeakMember<Object>> map1;
+ map1.insert(obj1, obj2);
+ {
+ // Weak references are strongified for the current cycle.
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
+ HeapHashMap<Member<Object>, WeakMember<Object>> map2(std::move(map1));
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapHashMapMoveWeakMemberMember) {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ HeapHashMap<WeakMember<Object>, Member<Object>> map1;
+ map1.insert(obj1, obj2);
+ {
+ // Weak references are strongified for the current cycle.
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
+ HeapHashMap<WeakMember<Object>, Member<Object>> map2(std::move(map1));
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapHashMapSwapMemberMember) {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ Object* obj3 = Object::Create();
+ Object* obj4 = Object::Create();
+ HeapHashMap<Member<Object>, Member<Object>> map1;
+ map1.insert(obj1, obj2);
+ HeapHashMap<Member<Object>, Member<Object>> map2;
+ map2.insert(obj3, obj4);
+ {
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(),
+ {obj1, obj2, obj3, obj4});
+ std::swap(map1, map2);
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapHashMapSwapWeakMemberWeakMember) {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ Object* obj3 = Object::Create();
+ Object* obj4 = Object::Create();
+ HeapHashMap<WeakMember<Object>, WeakMember<Object>> map1;
+ map1.insert(obj1, obj2);
+ HeapHashMap<WeakMember<Object>, WeakMember<Object>> map2;
+ map2.insert(obj3, obj4);
+ {
+ // Weak references are strongified for the current cycle.
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(),
+ {obj1, obj2, obj3, obj4});
+ std::swap(map1, map2);
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapHashMapSwapMemberWeakMember) {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ Object* obj3 = Object::Create();
+ Object* obj4 = Object::Create();
+ HeapHashMap<Member<Object>, WeakMember<Object>> map1;
+ map1.insert(obj1, obj2);
+ HeapHashMap<Member<Object>, WeakMember<Object>> map2;
+ map2.insert(obj3, obj4);
+ {
+ // Weak references are strongified for the current cycle.
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(),
+ {obj1, obj2, obj3, obj4});
+ std::swap(map1, map2);
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapHashMapSwapWeakMemberMember) {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ Object* obj3 = Object::Create();
+ Object* obj4 = Object::Create();
+ HeapHashMap<WeakMember<Object>, Member<Object>> map1;
+ map1.insert(obj1, obj2);
+ HeapHashMap<WeakMember<Object>, Member<Object>> map2;
+ map2.insert(obj3, obj4);
+ {
+ // Weak references are strongified for the current cycle.
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(),
+ {obj1, obj2, obj3, obj4});
+ std::swap(map1, map2);
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapHashMapInsertStrongWeakPairMember) {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ Object* obj3 = Object::Create();
+ HeapHashMap<StrongWeakPair, Member<Object>> map;
+ {
+ // Tests that the write barrier also fires for entities such as
+ // StrongWeakPair that don't overload assignment operators in translators.
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj3});
+ map.insert(StrongWeakPair(obj1, obj2), obj3);
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapHashMapInsertMemberStrongWeakPair) {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ Object* obj3 = Object::Create();
+ HeapHashMap<Member<Object>, StrongWeakPair> map;
+ {
+ // Tests that the write barrier also fires for entities such as
+ // StrongWeakPair that don't overload assignment operators in translators.
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1, obj2});
+ map.insert(obj1, StrongWeakPair(obj2, obj3));
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapHashMapCopyKeysToVectorMember) {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ HeapHashMap<Member<Object>, Member<Object>> map;
+ map.insert(obj1, obj2);
+ HeapVector<Member<Object>> vec;
+ {
+ // Only key should have its write barrier fired. A write barrier call for
+ // value hints to an inefficient implementation.
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj1});
+ CopyKeysToVector(map, vec);
+ }
+}
+
+TEST(IncrementalMarkingTest, HeapHashMapCopyValuesToVectorMember) {
+ Object* obj1 = Object::Create();
+ Object* obj2 = Object::Create();
+ HeapHashMap<Member<Object>, Member<Object>> map;
+ map.insert(obj1, obj2);
+ HeapVector<Member<Object>> vec;
+ {
+ // Only value should have its write barrier fired. A write barrier call for
+ // key hints to an inefficient implementation.
+ ExpectWriteBarrierFires<Object> scope(ThreadState::Current(), {obj2});
+ CopyValuesToVector(map, vec);
+ }
+}
+
+// TODO(keishi) Non-weak hash table backings should be promptly freed but they
+// are currently not because we emit write barriers for the backings, and we
+// don't free marked backings.
+TEST(IncrementalMarkingTest, DISABLED_WeakHashMapPromptlyFreeDisabled) {
+ ThreadState* state = ThreadState::Current();
+ state->SetGCState(ThreadState::kIncrementalMarkingStartScheduled);
+ state->SetGCState(ThreadState::kIncrementalMarkingStepScheduled);
+ Persistent<Object> obj1 = Object::Create();
+ NormalPageArena* arena = static_cast<NormalPageArena*>(
+ ThreadState::Current()->Heap().Arena(BlinkGC::kHashTableArenaIndex));
+ CHECK(arena);
+ {
+ size_t before = arena->promptly_freed_size();
+ // Create two maps so we don't promptly free at the allocation point.
+ HeapHashMap<WeakMember<Object>, Member<Object>> weak_map1;
+ HeapHashMap<WeakMember<Object>, Member<Object>> weak_map2;
+ weak_map1.insert(obj1, obj1);
+ weak_map2.insert(obj1, obj1);
+ weak_map1.clear();
+ size_t after = arena->promptly_freed_size();
+ // Weak hash table backings should not be promptly freed.
+ EXPECT_EQ(after, before);
+ }
+ {
+ size_t before = arena->promptly_freed_size();
+ // Create two maps so we don't promptly free at the allocation point.
+ HeapHashMap<Member<Object>, Member<Object>> map1;
+ HeapHashMap<Member<Object>, Member<Object>> map2;
+ map1.insert(obj1, obj1);
+ map2.insert(obj1, obj1);
+ map1.clear();
+ size_t after = arena->promptly_freed_size();
+ // Non-weak hash table backings should be promptly freed.
+ EXPECT_GT(after, before);
+ }
+ state->SetGCState(ThreadState::kIncrementalMarkingFinalizeScheduled);
+ state->SetGCState(ThreadState::kNoGCScheduled);
+}
+
+namespace {
+
+class RegisteringMixin;
+using ObjectRegistry = HeapHashMap<void*, Member<RegisteringMixin>>;
+
+class RegisteringMixin : public GarbageCollectedMixin {
+ public:
+ explicit RegisteringMixin(ObjectRegistry* registry) {
+ HeapObjectHeader* header = GetHeapObjectHeader();
+ const void* uninitialized_value = BlinkGC::kNotFullyConstructedObject;
+ EXPECT_EQ(uninitialized_value, header);
+ registry->insert(reinterpret_cast<void*>(this), this);
+ }
+};
+
+class RegisteringObject : public GarbageCollected<RegisteringObject>,
+ public RegisteringMixin {
+ USING_GARBAGE_COLLECTED_MIXIN(RegisteringObject);
+
+ public:
+ explicit RegisteringObject(ObjectRegistry* registry)
+ : RegisteringMixin(registry) {}
+};
+
+} // namespace
+
+TEST(IncrementalMarkingTest, WriteBarrierDuringMixinConstruction) {
+ IncrementalMarkingScope scope(ThreadState::Current());
+ ObjectRegistry registry;
+ RegisteringObject* object = new RegisteringObject(&registry);
+ EXPECT_FALSE(scope.marking_worklist()->IsGlobalEmpty());
+ MarkingItem item;
+ EXPECT_TRUE(scope.marking_worklist()->Pop(WorklistTaskId::MainThread, &item));
+ RegisteringObject* recorded_object =
+ reinterpret_cast<RegisteringObject*>(item.object);
+ // In this case, the Member write barrier will also add the object to the
+ // regular marking stack. The not-fully-constructed object marking stack is
+ // only needed when going through custom off-heap data structures that require
+ // eager tracing.
+ EXPECT_EQ(object, recorded_object);
+ // In this example, there are two more objects on the callback stack: the
+ // backing store for which a write barrier triggers after rehashing, and
+ // a write barrier for the Member assignment (which might not always happen).
+ scope.marking_worklist()->Pop(WorklistTaskId::MainThread, &item);
+ scope.marking_worklist()->Pop(WorklistTaskId::MainThread, &item);
+ // The mixin object should be on the not-fully-constructed object marking
+ // stack.
+ EXPECT_FALSE(scope.not_fully_constructed_worklist()->IsGlobalEmpty());
+ NotFullyConstructedItem item2;
+ EXPECT_TRUE(scope.not_fully_constructed_worklist()->Pop(
+ WorklistTaskId::MainThread, &item2));
+ RegisteringObject* recorded_object2 =
+ reinterpret_cast<RegisteringObject*>(item2);
+ EXPECT_EQ(object, recorded_object2);
+}
+
+TEST(IncrementalMarkingTest, OverrideAfterMixinConstruction) {
+ ObjectRegistry registry;
+ RegisteringMixin* mixin = new RegisteringObject(&registry);
+ HeapObjectHeader* header = mixin->GetHeapObjectHeader();
+ const void* uninitialized_value = BlinkGC::kNotFullyConstructedObject;
+ EXPECT_NE(uninitialized_value, header);
+}
+
+// =============================================================================
+// Tests that execute complete incremental garbage collections. ================
+// =============================================================================
+
+// Test driver for incremental marking. Assumes that no stack handling is
+// required.
+class IncrementalMarkingTestDriver {
+ public:
+ explicit IncrementalMarkingTestDriver(ThreadState* thread_state)
+ : thread_state_(thread_state) {}
+ ~IncrementalMarkingTestDriver() {
+ if (thread_state_->IsIncrementalMarking())
+ FinishGC();
+ }
+
+ void Start() {
+ thread_state_->ScheduleIncrementalMarkingStart();
+ thread_state_->RunScheduledGC(BlinkGC::kNoHeapPointersOnStack);
+ }
+
+ bool SingleStep() {
+ CHECK(thread_state_->IsIncrementalMarking());
+ if (thread_state_->GcState() ==
+ ThreadState::kIncrementalMarkingStepScheduled) {
+ thread_state_->RunScheduledGC(BlinkGC::kNoHeapPointersOnStack);
+ return true;
+ }
+ return false;
+ }
+
+ void FinishSteps() {
+ CHECK(thread_state_->IsIncrementalMarking());
+ while (SingleStep()) {
+ }
+ }
+
+ void FinishGC() {
+ CHECK(thread_state_->IsIncrementalMarking());
+ FinishSteps();
+ CHECK_EQ(ThreadState::kIncrementalMarkingFinalizeScheduled,
+ thread_state_->GcState());
+ thread_state_->RunScheduledGC(BlinkGC::kNoHeapPointersOnStack);
+ CHECK(!thread_state_->IsIncrementalMarking());
+ thread_state_->CompleteSweep();
+ }
+
+ private:
+ ThreadState* const thread_state_;
+};
+
+TEST(IncrementalMarkingTest, TestDriver) {
+ IncrementalMarkingTestDriver driver(ThreadState::Current());
+ driver.Start();
+ EXPECT_TRUE(ThreadState::Current()->IsIncrementalMarking());
+ driver.SingleStep();
+ EXPECT_TRUE(ThreadState::Current()->IsIncrementalMarking());
+ driver.FinishGC();
+ EXPECT_FALSE(ThreadState::Current()->IsIncrementalMarking());
+}
+
+TEST(IncrementalMarkingTest, DropBackingStore) {
+ // Regression test: crbug.com/828537
+ using WeakStore = HeapHashCountedSet<WeakMember<Object>>;
+
+ Persistent<WeakStore> persistent(new WeakStore);
+ persistent->insert(Object::Create());
+ IncrementalMarkingTestDriver driver(ThreadState::Current());
+ driver.Start();
+ driver.FinishSteps();
+ persistent->clear();
+ // Marking verifier should not crash on a black backing store with all
+ // black->white edges.
+ driver.FinishGC();
+}
+
+} // namespace incremental_marking_test
+} // namespace blink
+
+#endif // BUILDFLAG(BLINK_HEAP_INCREMENTAL_MARKING)
diff --git a/chromium/third_party/blink/renderer/platform/heap/marking_verifier.h b/chromium/third_party/blink/renderer/platform/heap/marking_verifier.h
new file mode 100644
index 00000000000..61c68bf4d0d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/marking_verifier.h
@@ -0,0 +1,74 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_MARKING_VERIFIER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_MARKING_VERIFIER_H_
+
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/heap/heap_page.h"
+#include "third_party/blink/renderer/platform/heap/visitor.h"
+
+namespace blink {
+
+// Marking verifier that checks that a child is marked if its parent is marked.
+class MarkingVerifier final : public Visitor {
+ public:
+ explicit MarkingVerifier(ThreadState* state) : Visitor(state) {}
+ virtual ~MarkingVerifier() {}
+
+ void VerifyObject(HeapObjectHeader* header) {
+ // Verify only non-free marked objects.
+ if (header->IsFree() || !header->IsMarked())
+ return;
+
+ const GCInfo* info = ThreadHeap::GcInfo(header->GcInfoIndex());
+ const bool can_verify =
+ !info->HasVTable() || blink::VTableInitialized(header->Payload());
+ if (can_verify) {
+ CHECK(header->IsValid());
+ info->trace_(this, header->Payload());
+ }
+ }
+
+ void Visit(void* object, TraceDescriptor desc) final {
+ CHECK(object);
+ VerifyChild(desc.base_object_payload);
+ }
+
+ void VisitWeak(void* object,
+ void** object_slot,
+ TraceDescriptor desc,
+ WeakCallback callback) final {
+ CHECK(object);
+ VerifyChild(desc.base_object_payload);
+ }
+
+ // Unused overrides.
+ void VisitBackingStoreStrongly(void*, void**, TraceDescriptor) final {}
+ void VisitBackingStoreWeakly(void*,
+ void**,
+ TraceDescriptor,
+ WeakCallback,
+ void*) final {}
+ void VisitBackingStoreOnly(void*, void**) final {}
+ void RegisterBackingStoreCallback(void*, MovingObjectCallback, void*) final {}
+ void RegisterWeakCallback(void*, WeakCallback) final {}
+
+ private:
+ void VerifyChild(void* base_object_payload) {
+ CHECK(base_object_payload);
+ HeapObjectHeader* child_header =
+ HeapObjectHeader::FromPayload(base_object_payload);
+ // This CHECKs ensure that any children reachable from marked parents are
+ // also marked. If you hit these CHECKs then marking is in an inconsistent
+ // state meaning that there are unmarked objects reachable from marked
+ // ones.
+ CHECK(child_header);
+ CHECK(child_header->IsMarked());
+ }
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_MARKING_VERIFIER_H_
diff --git a/chromium/third_party/blink/renderer/platform/heap/marking_visitor.cc b/chromium/third_party/blink/renderer/platform/heap/marking_visitor.cc
new file mode 100644
index 00000000000..ab2dc1f1fec
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/marking_visitor.cc
@@ -0,0 +1,134 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/heap/marking_visitor.h"
+
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/heap/thread_state.h"
+
+namespace blink {
+
+std::unique_ptr<MarkingVisitor> MarkingVisitor::Create(ThreadState* state,
+ MarkingMode mode) {
+ return std::make_unique<MarkingVisitor>(state, mode);
+}
+
+MarkingVisitor::MarkingVisitor(ThreadState* state, MarkingMode marking_mode)
+ : Visitor(state),
+ marking_worklist_(Heap().GetMarkingWorklist(),
+ WorklistTaskId::MainThread),
+ not_fully_constructed_worklist_(Heap().GetNotFullyConstructedWorklist(),
+ WorklistTaskId::MainThread),
+ weak_callback_worklist_(Heap().GetWeakCallbackWorklist(),
+ WorklistTaskId::MainThread),
+ marking_mode_(marking_mode) {
+ DCHECK(state->InAtomicMarkingPause());
+#if DCHECK_IS_ON()
+ DCHECK(state->CheckThread());
+#endif // DCHECK_IS_ON
+}
+
+MarkingVisitor::~MarkingVisitor() = default;
+
+void MarkingVisitor::ConservativelyMarkAddress(BasePage* page,
+ Address address) {
+#if DCHECK_IS_ON()
+ DCHECK(page->Contains(address));
+#endif
+ HeapObjectHeader* const header =
+ page->IsLargeObjectPage()
+ ? static_cast<LargeObjectPage*>(page)->GetHeapObjectHeader()
+ : static_cast<NormalPage*>(page)->FindHeaderFromAddress(address);
+ if (!header)
+ return;
+ ConservativelyMarkHeader(header);
+}
+
+#if DCHECK_IS_ON()
+void MarkingVisitor::ConservativelyMarkAddress(
+ BasePage* page,
+ Address address,
+ MarkedPointerCallbackForTesting callback) {
+ DCHECK(page->Contains(address));
+ HeapObjectHeader* const header =
+ page->IsLargeObjectPage()
+ ? static_cast<LargeObjectPage*>(page)->GetHeapObjectHeader()
+ : static_cast<NormalPage*>(page)->FindHeaderFromAddress(address);
+ if (!header)
+ return;
+ if (!callback(header))
+ ConservativelyMarkHeader(header);
+}
+#endif // DCHECK_IS_ON
+
+namespace {
+
+#if DCHECK_IS_ON()
+bool IsUninitializedMemory(void* object_pointer, size_t object_size) {
+ // Scan through the object's fields and check that they are all zero.
+ Address* object_fields = reinterpret_cast<Address*>(object_pointer);
+ for (size_t i = 0; i < object_size / sizeof(Address); ++i) {
+ if (object_fields[i])
+ return false;
+ }
+ return true;
+}
+#endif
+
+} // namespace
+
+void MarkingVisitor::ConservativelyMarkHeader(HeapObjectHeader* header) {
+ const GCInfo* gc_info = ThreadHeap::GcInfo(header->GcInfoIndex());
+ if (gc_info->HasVTable() && !VTableInitialized(header->Payload())) {
+ // We hit this branch when a GC strikes before GarbageCollected<>'s
+ // constructor runs.
+ //
+ // class A : public GarbageCollected<A> { virtual void f() = 0; };
+ // class B : public A {
+ // B() : A(foo()) { };
+ // };
+ //
+ // If foo() allocates something and triggers a GC, the vtable of A
+ // has not yet been initialized. In this case, we should mark the A
+ // object without tracing any member of the A object.
+ MarkHeaderNoTracing(header);
+#if DCHECK_IS_ON()
+ DCHECK(IsUninitializedMemory(header->Payload(), header->PayloadSize()));
+#endif
+ } else {
+ MarkHeader(header, gc_info->trace_);
+ }
+}
+
+void MarkingVisitor::RegisterWeakCallback(void* object, WeakCallback callback) {
+ // We don't want to run weak processings when taking a snapshot.
+ if (marking_mode_ == kSnapshotMarking)
+ return;
+ weak_callback_worklist_.Push({object, callback});
+}
+
+void MarkingVisitor::RegisterBackingStoreReference(void* slot) {
+ if (marking_mode_ != kGlobalMarkingWithCompaction)
+ return;
+ Heap().RegisterMovingObjectReference(
+ reinterpret_cast<MovableReference*>(slot));
+}
+
+void MarkingVisitor::RegisterBackingStoreCallback(void* backing_store,
+ MovingObjectCallback callback,
+ void* callback_data) {
+ if (marking_mode_ != kGlobalMarkingWithCompaction)
+ return;
+ Heap().RegisterMovingObjectCallback(
+ reinterpret_cast<MovableReference>(backing_store), callback,
+ callback_data);
+}
+
+bool MarkingVisitor::RegisterWeakTable(const void* closure,
+ EphemeronCallback iteration_callback) {
+ Heap().RegisterWeakTable(const_cast<void*>(closure), iteration_callback);
+ return true;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/heap/marking_visitor.h b/chromium/third_party/blink/renderer/platform/heap/marking_visitor.h
new file mode 100644
index 00000000000..ec072aca4bb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/marking_visitor.h
@@ -0,0 +1,191 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_MARKING_VISITOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_MARKING_VISITOR_H_
+
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/heap/heap_buildflags.h"
+#include "third_party/blink/renderer/platform/heap/heap_page.h"
+#include "third_party/blink/renderer/platform/heap/visitor.h"
+
+namespace blink {
+
+class BasePage;
+
+// Visitor used to mark Oilpan objects.
+class PLATFORM_EXPORT MarkingVisitor final : public Visitor {
+ public:
+ enum MarkingMode {
+ // This is a default visitor. This is used for MarkingType=kAtomicMarking
+ // and MarkingType=kIncrementalMarking.
+ kGlobalMarking,
+ // This visitor just marks objects and ignores weak processing.
+ // This is used for MarkingType=kTakeSnapshot.
+ kSnapshotMarking,
+ // Perform global marking along with preparing for additional sweep
+ // compaction of heap arenas afterwards. Compared to the GlobalMarking
+ // visitor, this visitor will also register references to objects
+ // that might be moved during arena compaction -- the compaction
+ // pass will then fix up those references when the object move goes
+ // ahead.
+ kGlobalMarkingWithCompaction,
+ };
+
+ static std::unique_ptr<MarkingVisitor> Create(ThreadState*, MarkingMode);
+
+ inline static void WriteBarrier(void* value);
+
+ MarkingVisitor(ThreadState*, MarkingMode);
+ virtual ~MarkingVisitor();
+
+ // Marking implementation.
+
+ // Conservatively marks an object if pointed to by Address.
+ void ConservativelyMarkAddress(BasePage*, Address);
+#if DCHECK_IS_ON()
+ void ConservativelyMarkAddress(BasePage*,
+ Address,
+ MarkedPointerCallbackForTesting);
+#endif // DCHECK_IS_ON()
+
+ // Marks an object and adds a tracing callback for processing of the object.
+ inline void MarkHeader(HeapObjectHeader*, TraceCallback);
+
+ // Try to mark an object without tracing. Returns true when the object was not
+ // marked upon calling.
+ inline bool MarkHeaderNoTracing(HeapObjectHeader*);
+
+ // Implementation of the visitor interface. See above for descriptions.
+
+ void Visit(void* object, TraceDescriptor desc) final {
+ DCHECK(object);
+ if (desc.base_object_payload == BlinkGC::kNotFullyConstructedObject) {
+ // This means that the objects are not-yet-fully-constructed. See comments
+ // on GarbageCollectedMixin for how those objects are handled.
+ not_fully_constructed_worklist_.Push(object);
+ return;
+ }
+ // Default mark method of the trait just calls the two-argument mark
+ // method on the visitor. The second argument is the static trace method
+ // of the trait, which by default calls the instance method
+ // trace(Visitor*) on the object.
+ //
+ // If the trait allows it, invoke the trace callback right here on the
+ // not-yet-marked object.
+ if (desc.can_trace_eagerly) {
+ // Protect against too deep trace call chains, and the
+ // unbounded system stack usage they can bring about.
+ //
+ // Assert against deep stacks so as to flush them out,
+ // but test and appropriately handle them should they occur
+ // in release builds.
+ //
+ // If you hit this assert, it means that you're creating an object
+ // graph that causes too many recursions, which might cause a stack
+ // overflow. To break the recursions, you need to add
+ // WILL_NOT_BE_EAGERLY_TRACED_CLASS() to classes that hold pointers
+ // that lead to many recursions.
+ DCHECK(Heap().GetStackFrameDepth().IsAcceptableStackUse());
+ if (LIKELY(Heap().GetStackFrameDepth().IsSafeToRecurse())) {
+ if (MarkHeaderNoTracing(
+ HeapObjectHeader::FromPayload(desc.base_object_payload))) {
+ desc.callback(this, desc.base_object_payload);
+ }
+ return;
+ }
+ }
+ MarkHeader(HeapObjectHeader::FromPayload(desc.base_object_payload),
+ desc.callback);
+ }
+
+ void VisitWeak(void* object,
+ void** object_slot,
+ TraceDescriptor desc,
+ WeakCallback callback) final {
+ RegisterWeakCallback(object_slot, callback);
+ }
+
+ void VisitBackingStoreStrongly(void* object,
+ void** object_slot,
+ TraceDescriptor desc) final {
+ RegisterBackingStoreReference(object_slot);
+ Visit(object, desc);
+ }
+
+ // All work is registered through RegisterWeakCallback.
+ void VisitBackingStoreWeakly(void* object,
+ void** object_slot,
+ TraceDescriptor desc,
+ WeakCallback callback,
+ void* parameter) final {
+ RegisterWeakCallback(parameter, callback);
+ }
+
+ // Used to only mark the backing store when it has been registered for weak
+ // processing. In this case, the contents are processed separately using
+ // the corresponding traits but the backing store requires marking.
+ void VisitBackingStoreOnly(void* object, void** object_slot) final {
+ MarkHeaderNoTracing(HeapObjectHeader::FromPayload(object));
+ RegisterBackingStoreReference(object_slot);
+ }
+
+ void RegisterBackingStoreCallback(void* backing_store,
+ MovingObjectCallback,
+ void* callback_data) final;
+ bool RegisterWeakTable(const void* closure,
+ EphemeronCallback iteration_callback) final;
+ void RegisterWeakCallback(void* closure, WeakCallback) final;
+
+ private:
+ void RegisterBackingStoreReference(void* slot);
+
+ void ConservativelyMarkHeader(HeapObjectHeader*);
+
+ MarkingWorklist::View marking_worklist_;
+ NotFullyConstructedWorklist::View not_fully_constructed_worklist_;
+ WeakCallbackWorklist::View weak_callback_worklist_;
+ const MarkingMode marking_mode_;
+};
+
+inline bool MarkingVisitor::MarkHeaderNoTracing(HeapObjectHeader* header) {
+ DCHECK(header);
+ DCHECK(State()->InAtomicMarkingPause() || State()->IsIncrementalMarking());
+ // A GC should only mark the objects that belong in its heap.
+ DCHECK_EQ(State(),
+ PageFromObject(header->Payload())->Arena()->GetThreadState());
+ // Never mark free space objects. This would e.g. hint to marking a promptly
+ // freed backing store.
+ DCHECK(!header->IsFree());
+
+ return header->TryMark();
+}
+
+inline void MarkingVisitor::MarkHeader(HeapObjectHeader* header,
+ TraceCallback callback) {
+ DCHECK(header);
+ DCHECK(callback);
+
+ if (MarkHeaderNoTracing(header)) {
+ marking_worklist_.Push(
+ {reinterpret_cast<void*>(header->Payload()), callback});
+ }
+}
+
+inline void MarkingVisitor::WriteBarrier(void* value) {
+#if BUILDFLAG(BLINK_HEAP_INCREMENTAL_MARKING)
+ if (!ThreadState::IsAnyIncrementalMarking() || !value)
+ return;
+
+ ThreadState* const thread_state = ThreadState::Current();
+ if (!thread_state->IsIncrementalMarking())
+ return;
+
+ thread_state->Heap().WriteBarrier(value);
+#endif // BUILDFLAG(BLINK_HEAP_INCREMENTAL_MARKING)
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_MARKING_VISITOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/heap/member.h b/chromium/third_party/blink/renderer/platform/heap/member.h
new file mode 100644
index 00000000000..10e22d4b10d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/member.h
@@ -0,0 +1,621 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_MEMBER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_MEMBER_H_
+
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/heap/heap_buildflags.h"
+#include "third_party/blink/renderer/platform/heap/heap_page.h"
+#include "third_party/blink/renderer/platform/heap/marking_visitor.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/hash_functions.h"
+#include "third_party/blink/renderer/platform/wtf/hash_traits.h"
+
+namespace WTF {
+template <typename P, typename Traits, typename Allocator>
+class ConstructTraits;
+} // namespace WTF
+
+namespace blink {
+
+template <typename T>
+class Persistent;
+template <typename T>
+class TraceWrapperMember;
+
+enum class TracenessMemberConfiguration {
+ kTraced,
+ kUntraced,
+};
+
+template <typename T,
+ TracenessMemberConfiguration tracenessConfiguration =
+ TracenessMemberConfiguration::kTraced>
+class MemberBase {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ MemberBase() : raw_(nullptr) { SaveCreationThreadState(); }
+
+ MemberBase(std::nullptr_t) : raw_(nullptr) { SaveCreationThreadState(); }
+
+ MemberBase(T* raw) : raw_(raw) {
+ SaveCreationThreadState();
+ CheckPointer();
+ }
+
+ explicit MemberBase(T& raw) : raw_(&raw) {
+ SaveCreationThreadState();
+ CheckPointer();
+ }
+
+ MemberBase(WTF::HashTableDeletedValueType) : raw_(reinterpret_cast<T*>(-1)) {
+ SaveCreationThreadState();
+ }
+
+ bool IsHashTableDeletedValue() const {
+ return raw_ == reinterpret_cast<T*>(-1);
+ }
+
+ MemberBase(const MemberBase& other) : raw_(other) {
+ SaveCreationThreadState();
+ CheckPointer();
+ }
+
+ template <typename U>
+ MemberBase(const Persistent<U>& other) {
+ SaveCreationThreadState();
+ raw_ = other;
+ CheckPointer();
+ }
+
+ template <typename U>
+ MemberBase(const MemberBase<U>& other) : raw_(other) {
+ SaveCreationThreadState();
+ CheckPointer();
+ }
+
+ T* Release() {
+ T* result = raw_;
+ raw_ = nullptr;
+ return result;
+ }
+
+ explicit operator bool() const { return raw_; }
+
+ operator T*() const { return raw_; }
+
+ T* operator->() const { return raw_; }
+ T& operator*() const { return *raw_; }
+
+ template <typename U>
+ MemberBase& operator=(const Persistent<U>& other) {
+ raw_ = other;
+ CheckPointer();
+ return *this;
+ }
+
+ template <typename U>
+ MemberBase& operator=(const MemberBase<U>& other) {
+ raw_ = other;
+ CheckPointer();
+ return *this;
+ }
+
+ template <typename U>
+ MemberBase& operator=(U* other) {
+ raw_ = other;
+ CheckPointer();
+ return *this;
+ }
+
+ MemberBase& operator=(WTF::HashTableDeletedValueType) {
+ raw_ = reinterpret_cast<T*>(-1);
+ return *this;
+ }
+
+ MemberBase& operator=(std::nullptr_t) {
+ raw_ = nullptr;
+ return *this;
+ }
+
+ void Swap(MemberBase<T>& other) {
+ std::swap(raw_, other.raw_);
+ CheckPointer();
+ }
+
+ T* Get() const { return raw_; }
+
+ void Clear() { raw_ = nullptr; }
+
+ protected:
+ void CheckPointer() {
+#if DCHECK_IS_ON()
+ if (!raw_)
+ return;
+ // HashTable can store a special value (which is not aligned to the
+ // allocation granularity) to Member<> to represent a deleted entry.
+ // Thus we treat a pointer that is not aligned to the granularity
+ // as a valid pointer.
+ if (reinterpret_cast<intptr_t>(raw_) % kAllocationGranularity)
+ return;
+
+ if (tracenessConfiguration != TracenessMemberConfiguration::kUntraced) {
+ ThreadState* current = ThreadState::Current();
+ DCHECK(current);
+ // m_creationThreadState may be null when this is used in a heap
+ // collection which initialized the Member with memset and the
+ // constructor wasn't called.
+ if (creation_thread_state_) {
+ // Member should point to objects that belong in the same ThreadHeap.
+ DCHECK(creation_thread_state_->IsOnThreadHeap(raw_));
+ // Member should point to objects that belong in the same ThreadHeap.
+ DCHECK_EQ(&current->Heap(), &creation_thread_state_->Heap());
+ } else {
+ DCHECK(current->IsOnThreadHeap(raw_));
+ }
+ }
+
+#if defined(ADDRESS_SANITIZER)
+ // TODO(haraken): What we really want to check here is that the pointer
+ // is a traceable object. In other words, the pointer is either of:
+ //
+ // (a) a pointer to the head of an on-heap object.
+ // (b) a pointer to the head of an on-heap mixin object.
+ //
+ // We can check it by calling ThreadHeap::isHeapObjectAlive(raw_),
+ // but we cannot call it here because it requires to include T.h.
+ // So we currently only try to implement the check for (a), but do
+ // not insist that T's definition is in scope.
+ if (IsFullyDefined<T>::value && !IsGarbageCollectedMixin<T>::value)
+ HeapObjectHeader::CheckFromPayload(raw_);
+#endif
+#endif
+ }
+
+ void SaveCreationThreadState() {
+#if DCHECK_IS_ON()
+ if (tracenessConfiguration == TracenessMemberConfiguration::kUntraced) {
+ creation_thread_state_ = nullptr;
+ } else {
+ creation_thread_state_ = ThreadState::Current();
+ // Members should be created in an attached thread. But an empty
+ // value Member may be created on an unattached thread by a heap
+ // collection iterator.
+ DCHECK(creation_thread_state_ || !raw_);
+ }
+#endif
+ }
+
+ T* raw_;
+#if DCHECK_IS_ON()
+ const ThreadState* creation_thread_state_;
+#endif
+};
+
+// Members are used in classes to contain strong pointers to other oilpan heap
+// allocated objects.
+// All Member fields of a class must be traced in the class' trace method.
+// During the mark phase of the GC all live objects are marked as live and
+// all Member fields of a live object will be traced marked as live as well.
+template <typename T>
+class Member : public MemberBase<T, TracenessMemberConfiguration::kTraced> {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ typedef MemberBase<T, TracenessMemberConfiguration::kTraced> Parent;
+
+ public:
+ Member() : Parent() {}
+ Member(std::nullptr_t) : Parent(nullptr) {}
+ Member(T* raw) : Parent(raw) {
+ // No write barrier for initializing stores.
+ }
+ Member(T& raw) : Parent(raw) {
+ // No write barrier for initializing stores.
+ }
+ Member(WTF::HashTableDeletedValueType x) : Parent(x) {}
+
+ Member(const Member& other) : Parent(other) { WriteBarrier(this->raw_); }
+ template <typename U>
+ Member(const Member<U>& other) : Parent(other) {
+ WriteBarrier(this->raw_);
+ }
+ template <typename U>
+ Member(const Persistent<U>& other) : Parent(other) {
+ WriteBarrier(this->raw_);
+ }
+
+ template <typename U>
+ Member& operator=(const Persistent<U>& other) {
+ Parent::operator=(other);
+ WriteBarrier(this->raw_);
+ return *this;
+ }
+
+ Member& operator=(const Member& other) {
+ Parent::operator=(other);
+ WriteBarrier(this->raw_);
+ return *this;
+ }
+
+ template <typename U>
+ Member& operator=(const Member<U>& other) {
+ Parent::operator=(other);
+ WriteBarrier(this->raw_);
+ return *this;
+ }
+
+ template <typename U>
+ Member& operator=(const WeakMember<U>& other) {
+ Parent::operator=(other);
+ WriteBarrier(this->raw_);
+ return *this;
+ }
+
+ template <typename U>
+ Member& operator=(U* other) {
+ Parent::operator=(other);
+ WriteBarrier(this->raw_);
+ return *this;
+ }
+
+ Member& operator=(WTF::HashTableDeletedValueType x) {
+ Parent::operator=(x);
+ return *this;
+ }
+
+ Member& operator=(std::nullptr_t) {
+ Parent::operator=(nullptr);
+ return *this;
+ }
+
+ protected:
+ ALWAYS_INLINE void WriteBarrier(T* value) const {
+#if BUILDFLAG(BLINK_HEAP_INCREMENTAL_MARKING)
+ if (LIKELY(!this->IsHashTableDeletedValue())) {
+ MarkingVisitor::WriteBarrier(
+ const_cast<typename std::remove_const<T>::type*>(value));
+ }
+#endif // BUILDFLAG(BLINK_HEAP_INCREMENTAL_MARKING)
+ }
+
+ template <typename P, typename Traits, typename Allocator>
+ friend class WTF::ConstructTraits;
+};
+
+// A checked version of Member<>, verifying that only same-thread references
+// are kept in the smart pointer. Intended to be used to diagnose unclean
+// thread reference usage in release builds. It simply exposes the debug-only
+// MemberBase<> checking we already have in place for select usage to diagnose
+// per-thread issues. Only intended used temporarily while diagnosing suspected
+// problems with cross-thread references.
+template <typename T>
+class SameThreadCheckedMember : public Member<T> {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ typedef Member<T> Parent;
+
+ public:
+ SameThreadCheckedMember() : Parent() { SaveCreationThreadState(); }
+ SameThreadCheckedMember(std::nullptr_t) : Parent(nullptr) {
+ SaveCreationThreadState();
+ }
+
+ SameThreadCheckedMember(T* raw) : Parent(raw) {
+ SaveCreationThreadState();
+ CheckPointer();
+ }
+
+ SameThreadCheckedMember(T& raw) : Parent(raw) {
+ SaveCreationThreadState();
+ CheckPointer();
+ }
+
+ SameThreadCheckedMember(WTF::HashTableDeletedValueType x) : Parent(x) {
+ SaveCreationThreadState();
+ CheckPointer();
+ }
+
+ SameThreadCheckedMember(const SameThreadCheckedMember& other)
+ : Parent(other) {
+ SaveCreationThreadState();
+ }
+ template <typename U>
+ SameThreadCheckedMember(const SameThreadCheckedMember<U>& other)
+ : Parent(other) {
+ SaveCreationThreadState();
+ CheckPointer();
+ }
+
+ template <typename U>
+ SameThreadCheckedMember(const Persistent<U>& other) : Parent(other) {
+ SaveCreationThreadState();
+ CheckPointer();
+ }
+
+ template <typename U>
+ SameThreadCheckedMember& operator=(const Persistent<U>& other) {
+ Parent::operator=(other);
+ CheckPointer();
+ return *this;
+ }
+
+ template <typename U>
+ SameThreadCheckedMember& operator=(const SameThreadCheckedMember<U>& other) {
+ Parent::operator=(other);
+ CheckPointer();
+ return *this;
+ }
+
+ template <typename U>
+ SameThreadCheckedMember& operator=(const WeakMember<U>& other) {
+ Parent::operator=(other);
+ CheckPointer();
+ return *this;
+ }
+
+ template <typename U>
+ SameThreadCheckedMember& operator=(U* other) {
+ Parent::operator=(other);
+ CheckPointer();
+ return *this;
+ }
+
+ SameThreadCheckedMember& operator=(std::nullptr_t) {
+ Parent::operator=(nullptr);
+ return *this;
+ }
+
+ private:
+ void CheckPointer() {
+ if (!this->raw_)
+ return;
+ // HashTable can store a special value (which is not aligned to the
+ // allocation granularity) to Member<> to represent a deleted entry.
+ // Thus we treat a pointer that is not aligned to the granularity
+ // as a valid pointer.
+ if (reinterpret_cast<intptr_t>(this->raw_) % kAllocationGranularity)
+ return;
+
+ ThreadState* current = ThreadState::Current();
+ DCHECK(current);
+ // m_creationThreadState may be null when this is used in a heap
+ // collection which initialized the Member with memset and the
+ // constructor wasn't called.
+ if (creation_thread_state_) {
+ // Member should point to objects that belong in the same ThreadHeap.
+ CHECK(creation_thread_state_->IsOnThreadHeap(this->raw_));
+ // Member should point to objects that belong in the same ThreadHeap.
+ CHECK_EQ(&current->Heap(), &creation_thread_state_->Heap());
+ } else {
+ CHECK(current->IsOnThreadHeap(this->raw_));
+ }
+ }
+
+ void SaveCreationThreadState() {
+ creation_thread_state_ = ThreadState::Current();
+ // All Members should be created in an attached thread, but an empty
+ // value Member may be created on an unattached thread by a heap
+ // collection iterator.
+ CHECK(this->creation_thread_state_ || !this->raw_);
+ }
+
+ const ThreadState* creation_thread_state_;
+};
+
+// WeakMember is similar to Member in that it is used to point to other oilpan
+// heap allocated objects.
+// However instead of creating a strong pointer to the object, the WeakMember
+// creates a weak pointer, which does not keep the pointee alive. Hence if all
+// pointers to to a heap allocated object are weak the object will be garbage
+// collected. At the time of GC the weak pointers will automatically be set to
+// null.
+template <typename T>
+class WeakMember : public MemberBase<T, TracenessMemberConfiguration::kTraced> {
+ typedef MemberBase<T, TracenessMemberConfiguration::kTraced> Parent;
+
+ public:
+ WeakMember() : Parent() {}
+
+ WeakMember(std::nullptr_t) : Parent(nullptr) {}
+
+ WeakMember(T* raw) : Parent(raw) {}
+
+ WeakMember(WTF::HashTableDeletedValueType x) : Parent(x) {}
+
+ template <typename U>
+ WeakMember(const Persistent<U>& other) : Parent(other) {}
+
+ template <typename U>
+ WeakMember(const Member<U>& other) : Parent(other) {}
+
+ template <typename U>
+ WeakMember& operator=(const Persistent<U>& other) {
+ this->raw_ = other;
+ this->CheckPointer();
+ return *this;
+ }
+
+ template <typename U>
+ WeakMember& operator=(const Member<U>& other) {
+ this->raw_ = other;
+ this->CheckPointer();
+ return *this;
+ }
+
+ template <typename U>
+ WeakMember& operator=(U* other) {
+ this->raw_ = other;
+ this->CheckPointer();
+ return *this;
+ }
+
+ WeakMember& operator=(std::nullptr_t) {
+ this->raw_ = nullptr;
+ return *this;
+ }
+
+ private:
+ T** Cell() const { return const_cast<T**>(&this->raw_); }
+
+ friend class Visitor;
+};
+
+// UntracedMember is a pointer to an on-heap object that is not traced for some
+// reason. Please don't use this unless you understand what you're doing.
+// Basically, all pointers to on-heap objects must be stored in either of
+// Persistent, Member or WeakMember. It is not allowed to leave raw pointers to
+// on-heap objects. However, there can be scenarios where you have to use raw
+// pointers for some reason, and in that case you can use UntracedMember. Of
+// course, it must be guaranteed that the pointing on-heap object is kept alive
+// while the raw pointer is pointing to the object.
+template <typename T>
+class UntracedMember final
+ : public MemberBase<T, TracenessMemberConfiguration::kUntraced> {
+ typedef MemberBase<T, TracenessMemberConfiguration::kUntraced> Parent;
+
+ public:
+ UntracedMember() : Parent() {}
+
+ UntracedMember(std::nullptr_t) : Parent(nullptr) {}
+
+ UntracedMember(T* raw) : Parent(raw) {}
+
+ template <typename U>
+ UntracedMember(const Persistent<U>& other) : Parent(other) {}
+
+ template <typename U>
+ UntracedMember(const Member<U>& other) : Parent(other) {}
+
+ UntracedMember(WTF::HashTableDeletedValueType x) : Parent(x) {}
+
+ template <typename U>
+ UntracedMember& operator=(const Persistent<U>& other) {
+ this->raw_ = other;
+ this->CheckPointer();
+ return *this;
+ }
+
+ template <typename U>
+ UntracedMember& operator=(const Member<U>& other) {
+ this->raw_ = other;
+ this->CheckPointer();
+ return *this;
+ }
+
+ template <typename U>
+ UntracedMember& operator=(U* other) {
+ this->raw_ = other;
+ this->CheckPointer();
+ return *this;
+ }
+
+ UntracedMember& operator=(std::nullptr_t) {
+ this->raw_ = nullptr;
+ return *this;
+ }
+};
+
+} // namespace blink
+
+namespace WTF {
+
+// PtrHash is the default hash for hash tables with Member<>-derived elements.
+template <typename T>
+struct MemberHash : PtrHash<T> {
+ STATIC_ONLY(MemberHash);
+ template <typename U>
+ static unsigned GetHash(const U& key) {
+ return PtrHash<T>::GetHash(key);
+ }
+ template <typename U, typename V>
+ static bool Equal(const U& a, const V& b) {
+ return a == b;
+ }
+};
+
+template <typename T>
+struct DefaultHash<blink::Member<T>> {
+ STATIC_ONLY(DefaultHash);
+ using Hash = MemberHash<T>;
+};
+
+template <typename T>
+struct DefaultHash<blink::WeakMember<T>> {
+ STATIC_ONLY(DefaultHash);
+ using Hash = MemberHash<T>;
+};
+
+template <typename T>
+struct DefaultHash<blink::UntracedMember<T>> {
+ STATIC_ONLY(DefaultHash);
+ using Hash = MemberHash<T>;
+};
+
+template <typename T>
+struct DefaultHash<blink::SameThreadCheckedMember<T>> {
+ STATIC_ONLY(DefaultHash);
+ using Hash = MemberHash<T>;
+};
+
+template <typename T>
+struct DefaultHash<blink::TraceWrapperMember<T>> {
+ STATIC_ONLY(DefaultHash);
+ using Hash = MemberHash<T>;
+};
+
+template <typename T>
+struct IsTraceable<blink::Member<T>> {
+ STATIC_ONLY(IsTraceable);
+ static const bool value = true;
+};
+
+template <typename T>
+struct IsWeak<blink::WeakMember<T>> {
+ STATIC_ONLY(IsWeak);
+ static const bool value = true;
+};
+
+template <typename T>
+struct IsTraceable<blink::WeakMember<T>> {
+ STATIC_ONLY(IsTraceable);
+ static const bool value = true;
+};
+
+template <typename T>
+struct IsTraceable<blink::SameThreadCheckedMember<T>> {
+ STATIC_ONLY(IsTraceable);
+ static const bool value = true;
+};
+
+template <typename T>
+struct IsTraceable<blink::TraceWrapperMember<T>> {
+ STATIC_ONLY(IsTraceable);
+ static const bool value = true;
+};
+
+template <typename T, typename Traits, typename Allocator>
+class ConstructTraits<blink::Member<T>, Traits, Allocator> {
+ STATIC_ONLY(ConstructTraits);
+
+ public:
+ template <typename... Args>
+ static blink::Member<T>* ConstructAndNotifyElement(void* location,
+ Args&&... args) {
+ blink::Member<T>* object =
+ new (NotNull, location) blink::Member<T>(std::forward<Args>(args)...);
+ object->WriteBarrier(object->raw_);
+ return object;
+ }
+
+ static void NotifyNewElements(blink::Member<T>* array, size_t len) {
+ while (len-- > 0) {
+ array->WriteBarrier(array->raw_);
+ array++;
+ }
+ }
+};
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_MEMBER_H_
diff --git a/chromium/third_party/blink/renderer/platform/heap/object_start_bitmap_test.cc b/chromium/third_party/blink/renderer/platform/heap/object_start_bitmap_test.cc
new file mode 100644
index 00000000000..b9a8dc8350a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/object_start_bitmap_test.cc
@@ -0,0 +1,169 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/heap/heap_page.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+namespace {
+
+bool IsEmpty(const ObjectStartBitmap& bitmap) {
+ size_t count = 0;
+ bitmap.Iterate([&count](Address) { count++; });
+ return count == 0;
+}
+
+// Abstraction for objects that hides ObjectStartBitmap::kGranularity and
+// the base address as getting either of it wrong will result in failed DCHECKs.
+class Object {
+ public:
+ static Address kBaseOffset;
+
+ Object(size_t number) : number_(number) {
+ const size_t max_entries = ObjectStartBitmap::MaxEntries();
+ EXPECT_GE(max_entries, number_);
+ }
+
+ Address address() const {
+ return kBaseOffset + ObjectStartBitmap::Granularity() * number_;
+ }
+
+ // Allow implicitly converting Object to Address.
+ operator Address() const { return address(); }
+
+ private:
+ const size_t number_;
+};
+
+Address Object::kBaseOffset = reinterpret_cast<Address>(0x4000);
+
+} // namespace
+
+TEST(ObjectStartBitmapTest, MoreThanZeroEntriesPossible) {
+ const size_t max_entries = ObjectStartBitmap::MaxEntries();
+ EXPECT_LT(0u, max_entries);
+}
+
+TEST(ObjectStartBitmapTest, InitialEmpty) {
+ ObjectStartBitmap bitmap(Object::kBaseOffset);
+ EXPECT_TRUE(IsEmpty(bitmap));
+}
+
+TEST(ObjectStartBitmapTest, SetBitImpliesNonEmpty) {
+ ObjectStartBitmap bitmap(Object::kBaseOffset);
+ bitmap.SetBit(Object(0));
+ EXPECT_FALSE(IsEmpty(bitmap));
+}
+
+TEST(ObjectStartBitmapTest, SetBitCheckBit) {
+ ObjectStartBitmap bitmap(Object::kBaseOffset);
+ Object object(7);
+ bitmap.SetBit(object);
+ EXPECT_TRUE(bitmap.CheckBit(object));
+}
+
+TEST(ObjectStartBitmapTest, SetBitClearbitCheckBit) {
+ ObjectStartBitmap bitmap(Object::kBaseOffset);
+ Object object(77);
+ bitmap.SetBit(object);
+ bitmap.ClearBit(object);
+ EXPECT_FALSE(bitmap.CheckBit(object));
+}
+
+TEST(ObjectStartBitmapTest, SetBitClearBitImpliesEmpty) {
+ ObjectStartBitmap bitmap(Object::kBaseOffset);
+ Object object(123);
+ bitmap.SetBit(object);
+ bitmap.ClearBit(object);
+ EXPECT_TRUE(IsEmpty(bitmap));
+}
+
+TEST(ObjectStartBitmapTest, AdjacentObjectsAtBegin) {
+ ObjectStartBitmap bitmap(Object::kBaseOffset);
+ Object object0(0);
+ Object object1(1);
+ bitmap.SetBit(object0);
+ bitmap.SetBit(object1);
+ EXPECT_FALSE(bitmap.CheckBit(Object(3)));
+ size_t count = 0;
+ bitmap.Iterate([&count, object0, object1](Address current) {
+ if (count == 0) {
+ EXPECT_EQ(object0.address(), current);
+ } else if (count == 1) {
+ EXPECT_EQ(object1.address(), current);
+ }
+ count++;
+ });
+ EXPECT_EQ(2u, count);
+}
+
+TEST(ObjectStartBitmapTest, AdjacentObjectsAtEnd) {
+ ObjectStartBitmap bitmap(Object::kBaseOffset);
+ const size_t last_entry_index = ObjectStartBitmap::MaxEntries() - 1;
+ Object object0(last_entry_index - 1);
+ Object object1(last_entry_index);
+ bitmap.SetBit(object0);
+ bitmap.SetBit(object1);
+ EXPECT_FALSE(bitmap.CheckBit(Object(last_entry_index - 2)));
+ size_t count = 0;
+ bitmap.Iterate([&count, object0, object1](Address current) {
+ if (count == 0) {
+ EXPECT_EQ(object0.address(), current);
+ } else if (count == 1) {
+ EXPECT_EQ(object1.address(), current);
+ }
+ count++;
+ });
+ EXPECT_EQ(2u, count);
+}
+
+TEST(ObjectStartBitmapTest, FindHeaderExact) {
+ ObjectStartBitmap bitmap(Object::kBaseOffset);
+ Object object(654);
+ bitmap.SetBit(object);
+ EXPECT_EQ(object.address(), bitmap.FindHeader(object.address()));
+}
+
+TEST(ObjectStartBitmapTest, FindHeaderApproximate) {
+ static const size_t kInternalDelta = 37;
+ ObjectStartBitmap bitmap(Object::kBaseOffset);
+ Object object(654);
+ bitmap.SetBit(object);
+ EXPECT_EQ(object.address(),
+ bitmap.FindHeader(object.address() + kInternalDelta));
+}
+
+TEST(ObjectStartBitmapTest, FindHeaderIteratingWholeBitmap) {
+ ObjectStartBitmap bitmap(Object::kBaseOffset);
+ Object object_to_find(Object(0));
+ Address hint_index = Object(ObjectStartBitmap::MaxEntries() - 1);
+ bitmap.SetBit(object_to_find);
+ EXPECT_EQ(object_to_find.address(), bitmap.FindHeader(hint_index));
+}
+
+TEST(ObjectStartBitmapTest, FindHeaderNextCell) {
+ // This white box test makes use of the fact that cells are of type uint8_t.
+ const size_t kCellSize = sizeof(uint8_t);
+ ObjectStartBitmap bitmap(Object::kBaseOffset);
+ Object object_to_find(Object(kCellSize - 1));
+ Address hint = Object(kCellSize);
+ bitmap.SetBit(Object(0));
+ bitmap.SetBit(object_to_find);
+ EXPECT_EQ(object_to_find.address(), bitmap.FindHeader(hint));
+}
+
+TEST(ObjectStartBitmapTest, FindHeaderSameCell) {
+ // This white box test makes use of the fact that cells are of type uint8_t.
+ const size_t kCellSize = sizeof(uint8_t);
+ ObjectStartBitmap bitmap(Object::kBaseOffset);
+ Object object_to_find(Object(kCellSize - 1));
+ bitmap.SetBit(Object(0));
+ bitmap.SetBit(object_to_find);
+ EXPECT_EQ(object_to_find.address(),
+ bitmap.FindHeader(object_to_find.address()));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/heap/page_memory.cc b/chromium/third_party/blink/renderer/platform/heap/page_memory.cc
new file mode 100644
index 00000000000..bdf575e3871
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/page_memory.cc
@@ -0,0 +1,179 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/heap/page_memory.h"
+
+#include "base/allocator/partition_allocator/oom.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/wtf/address_sanitizer.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/atomics.h"
+
+namespace blink {
+
+void MemoryRegion::Release() {
+ WTF::FreePages(base_, size_);
+}
+
+bool MemoryRegion::Commit() {
+ CHECK(WTF::RecommitSystemPages(base_, size_, WTF::PageReadWrite));
+ return WTF::SetSystemPagesAccess(base_, size_, WTF::PageReadWrite);
+}
+
+void MemoryRegion::Decommit() {
+ ASAN_UNPOISON_MEMORY_REGION(base_, size_);
+ WTF::DecommitSystemPages(base_, size_);
+ CHECK(WTF::SetSystemPagesAccess(base_, size_, WTF::PageInaccessible));
+}
+
+PageMemoryRegion::PageMemoryRegion(Address base,
+ size_t size,
+ unsigned num_pages,
+ RegionTree* region_tree)
+ : MemoryRegion(base, size),
+ is_large_page_(num_pages == 1),
+ num_pages_(num_pages),
+ region_tree_(region_tree) {
+ region_tree_->Add(this);
+ for (size_t i = 0; i < kBlinkPagesPerRegion; ++i)
+ in_use_[i] = false;
+}
+
+PageMemoryRegion::~PageMemoryRegion() {
+ if (region_tree_)
+ region_tree_->Remove(this);
+ Release();
+}
+
+void PageMemoryRegion::PageDeleted(Address page) {
+ MarkPageUnused(page);
+ if (!AtomicDecrement(&num_pages_))
+ delete this;
+}
+
+// TODO(haraken): Like partitionOutOfMemoryWithLotsOfUncommitedPages(),
+// we should probably have a way to distinguish physical memory OOM from
+// virtual address space OOM.
+static NEVER_INLINE void BlinkGCOutOfMemory() {
+ OOM_CRASH();
+}
+
+PageMemoryRegion* PageMemoryRegion::Allocate(size_t size,
+ unsigned num_pages,
+ RegionTree* region_tree) {
+ // Round size up to the allocation granularity.
+ size = (size + WTF::kPageAllocationGranularityOffsetMask) &
+ WTF::kPageAllocationGranularityBaseMask;
+ Address base = static_cast<Address>(
+ WTF::AllocPages(nullptr, size, kBlinkPageSize, WTF::PageInaccessible));
+ if (!base)
+ BlinkGCOutOfMemory();
+ return new PageMemoryRegion(base, size, num_pages, region_tree);
+}
+
+PageMemoryRegion* RegionTree::Lookup(Address address) {
+ RegionTreeNode* current = root_;
+ while (current) {
+ Address base = current->region_->Base();
+ if (address < base) {
+ current = current->left_;
+ continue;
+ }
+ if (address >= base + current->region_->size()) {
+ current = current->right_;
+ continue;
+ }
+ DCHECK(current->region_->Contains(address));
+ return current->region_;
+ }
+ return nullptr;
+}
+
+void RegionTree::Add(PageMemoryRegion* region) {
+ DCHECK(region);
+ RegionTreeNode* new_tree = new RegionTreeNode(region);
+ new_tree->AddTo(&root_);
+}
+
+void RegionTreeNode::AddTo(RegionTreeNode** context) {
+ Address base = region_->Base();
+ for (RegionTreeNode* current = *context; current; current = *context) {
+ DCHECK(!current->region_->Contains(base));
+ context =
+ (base < current->region_->Base()) ? &current->left_ : &current->right_;
+ }
+ *context = this;
+}
+
+void RegionTree::Remove(PageMemoryRegion* region) {
+ DCHECK(region);
+ DCHECK(root_);
+ Address base = region->Base();
+ RegionTreeNode** context = &root_;
+ RegionTreeNode* current = root_;
+ for (; current; current = *context) {
+ if (region == current->region_)
+ break;
+ context =
+ (base < current->region_->Base()) ? &current->left_ : &current->right_;
+ }
+
+ // Shutdown via detachMainThread might not have populated the region tree.
+ if (!current)
+ return;
+
+ *context = nullptr;
+ if (current->left_) {
+ current->left_->AddTo(context);
+ current->left_ = nullptr;
+ }
+ if (current->right_) {
+ current->right_->AddTo(context);
+ current->right_ = nullptr;
+ }
+ delete current;
+}
+
+PageMemory::PageMemory(PageMemoryRegion* reserved, const MemoryRegion& writable)
+ : reserved_(reserved), writable_(writable) {
+ DCHECK(reserved->Contains(writable));
+
+ // Register the writable area of the memory as part of the LSan root set.
+ // Only the writable area is mapped and can contain C++ objects. Those
+ // C++ objects can contain pointers to objects outside of the heap and
+ // should therefore be part of the LSan root set.
+ __lsan_register_root_region(writable_.Base(), writable_.size());
+}
+
+PageMemory* PageMemory::SetupPageMemoryInRegion(PageMemoryRegion* region,
+ size_t page_offset,
+ size_t payload_size) {
+ // Setup the payload one guard page into the page memory.
+ Address payload_address = region->Base() + page_offset + kBlinkGuardPageSize;
+ return new PageMemory(region, MemoryRegion(payload_address, payload_size));
+}
+
+static size_t RoundToOsPageSize(size_t size) {
+ return (size + WTF::kSystemPageSize - 1) & ~(WTF::kSystemPageSize - 1);
+}
+
+PageMemory* PageMemory::Allocate(size_t payload_size, RegionTree* region_tree) {
+ DCHECK_GT(payload_size, 0u);
+
+ // Virtual memory allocation routines operate in OS page sizes.
+ // Round up the requested size to nearest os page size.
+ payload_size = RoundToOsPageSize(payload_size);
+
+ // Overallocate by 2 times OS page size to have space for a
+ // guard page at the beginning and end of blink heap page.
+ size_t allocation_size = payload_size + 2 * kBlinkGuardPageSize;
+ PageMemoryRegion* page_memory_region =
+ PageMemoryRegion::AllocateLargePage(allocation_size, region_tree);
+ PageMemory* storage =
+ SetupPageMemoryInRegion(page_memory_region, 0, payload_size);
+ CHECK(storage->Commit());
+ return storage;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/heap/page_memory.h b/chromium/third_party/blink/renderer/platform/heap/page_memory.h
new file mode 100644
index 00000000000..7f4ca6ef9f7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/page_memory.h
@@ -0,0 +1,218 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_PAGE_MEMORY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_PAGE_MEMORY_H_
+
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/heap/heap_page.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/compiler.h"
+
+namespace blink {
+
+class RegionTree;
+class RegionTreeNode;
+
+class MemoryRegion {
+ USING_FAST_MALLOC(MemoryRegion);
+
+ public:
+ MemoryRegion(Address base, size_t size) : base_(base), size_(size) {
+ DCHECK_GT(size, 0u);
+ }
+
+ bool Contains(Address addr) const {
+ return base_ <= addr && addr < (base_ + size_);
+ }
+
+ bool Contains(const MemoryRegion& other) const {
+ return Contains(other.base_) && Contains(other.base_ + other.size_ - 1);
+ }
+
+ void Release();
+ WARN_UNUSED_RESULT bool Commit();
+ void Decommit();
+
+ Address Base() const { return base_; }
+ size_t size() const { return size_; }
+
+ private:
+ Address base_;
+ size_t size_;
+};
+
+// A PageMemoryRegion represents a chunk of reserved virtual address
+// space containing a number of blink heap pages. On Windows, reserved
+// virtual address space can only be given back to the system as a
+// whole. The PageMemoryRegion allows us to do that by keeping track
+// of the number of pages using it in order to be able to release all
+// of the virtual address space when there are no more pages using it.
+class PageMemoryRegion : public MemoryRegion {
+ public:
+ ~PageMemoryRegion();
+
+ void PageDeleted(Address);
+
+ void MarkPageUsed(Address page) {
+ DCHECK(!in_use_[Index(page)]);
+ in_use_[Index(page)] = true;
+ }
+
+ void MarkPageUnused(Address page) { in_use_[Index(page)] = false; }
+
+ static PageMemoryRegion* AllocateLargePage(size_t size,
+ RegionTree* region_tree) {
+ return Allocate(size, 1, region_tree);
+ }
+
+ static PageMemoryRegion* AllocateNormalPages(RegionTree* region_tree) {
+ return Allocate(kBlinkPageSize * kBlinkPagesPerRegion, kBlinkPagesPerRegion,
+ region_tree);
+ }
+
+ BasePage* PageFromAddress(Address address) {
+ DCHECK(Contains(address));
+ if (!in_use_[Index(address)])
+ return nullptr;
+ if (is_large_page_)
+ return PageFromObject(Base());
+ return PageFromObject(address);
+ }
+
+ private:
+ PageMemoryRegion(Address base, size_t, unsigned num_pages, RegionTree*);
+
+ unsigned Index(Address address) const {
+ DCHECK(Contains(address));
+ if (is_large_page_)
+ return 0;
+ size_t offset = BlinkPageAddress(address) - Base();
+ DCHECK_EQ(offset % kBlinkPageSize, 0u);
+ return offset / kBlinkPageSize;
+ }
+
+ static PageMemoryRegion* Allocate(size_t, unsigned num_pages, RegionTree*);
+
+ const bool is_large_page_;
+ // A thread owns a page, but not a region. Represent the in-use
+ // bitmap such that thread non-interference comes for free.
+ bool in_use_[kBlinkPagesPerRegion];
+ int num_pages_;
+ RegionTree* region_tree_;
+};
+
+// A RegionTree is a simple binary search tree of PageMemoryRegions sorted
+// by base addresses.
+class RegionTree {
+ USING_FAST_MALLOC(RegionTree);
+
+ public:
+ RegionTree() : root_(nullptr) {}
+
+ void Add(PageMemoryRegion*);
+ void Remove(PageMemoryRegion*);
+ PageMemoryRegion* Lookup(Address);
+
+ private:
+ RegionTreeNode* root_;
+};
+
+class RegionTreeNode {
+ USING_FAST_MALLOC(RegionTreeNode);
+
+ public:
+ explicit RegionTreeNode(PageMemoryRegion* region)
+ : region_(region), left_(nullptr), right_(nullptr) {}
+
+ ~RegionTreeNode() {
+ delete left_;
+ delete right_;
+ }
+
+ void AddTo(RegionTreeNode** context);
+
+ private:
+ PageMemoryRegion* region_;
+ RegionTreeNode* left_;
+ RegionTreeNode* right_;
+
+ friend RegionTree;
+};
+
+// Representation of the memory used for a Blink heap page.
+//
+// The representation keeps track of two memory regions:
+//
+// 1. The virtual memory reserved from the system in order to be able
+// to free all the virtual memory reserved. Multiple PageMemory
+// instances can share the same reserved memory region and
+// therefore notify the reserved memory region on destruction so
+// that the system memory can be given back when all PageMemory
+// instances for that memory are gone.
+//
+// 2. The writable memory (a sub-region of the reserved virtual
+// memory region) that is used for the actual heap page payload.
+//
+// Guard pages are created before and after the writable memory.
+class PageMemory {
+ USING_FAST_MALLOC(PageMemory);
+
+ public:
+ ~PageMemory() {
+ __lsan_unregister_root_region(writable_.Base(), writable_.size());
+ reserved_->PageDeleted(WritableStart());
+ }
+
+ WARN_UNUSED_RESULT bool Commit() {
+ reserved_->MarkPageUsed(WritableStart());
+ // Check that in-use page isn't also marked as being a non-heap page
+ // by the current heap's negative cache. That cache is invalidated
+ // when allocating new pages, but crbug.com/649485 suggests that
+ // we do get out of sync somehow.
+ //
+ // TODO(sof): consider removing check once bug has been diagnosed
+ // and addressed.
+ CHECK(!ThreadState::Current()->Heap().IsAddressInHeapDoesNotContainCache(
+ WritableStart()));
+ return writable_.Commit();
+ }
+
+ void Decommit() {
+ reserved_->MarkPageUnused(WritableStart());
+ writable_.Decommit();
+ }
+
+ void MarkUnused() { reserved_->MarkPageUnused(WritableStart()); }
+
+ PageMemoryRegion* Region() { return reserved_; }
+
+ Address WritableStart() { return writable_.Base(); }
+
+ static PageMemory* SetupPageMemoryInRegion(PageMemoryRegion*,
+ size_t page_offset,
+ size_t payload_size);
+
+ // Allocate a virtual address space for one blink page with the
+ // following layout:
+ //
+ // [ guard os page | ... payload ... | guard os page ]
+ // ^---{ aligned to blink page size }
+ //
+ // The returned page memory region will be zeroed.
+ //
+ static PageMemory* Allocate(size_t payload_size, RegionTree*);
+
+ private:
+ PageMemory(PageMemoryRegion* reserved, const MemoryRegion& writable);
+
+ PageMemoryRegion* reserved_;
+ MemoryRegion writable_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/heap/page_pool.cc b/chromium/third_party/blink/renderer/platform/heap/page_pool.cc
new file mode 100644
index 00000000000..100a7803614
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/page_pool.cc
@@ -0,0 +1,55 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/heap/page_pool.h"
+
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/heap/page_memory.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+namespace blink {
+
+PagePool::PagePool() {
+ for (int i = 0; i < BlinkGC::kNumberOfArenas; ++i) {
+ pool_[i] = nullptr;
+ }
+}
+
+PagePool::~PagePool() {
+ for (int index = 0; index < BlinkGC::kNumberOfArenas; ++index) {
+ while (PoolEntry* entry = pool_[index]) {
+ pool_[index] = entry->next;
+ PageMemory* memory = entry->data;
+ DCHECK(memory);
+ delete memory;
+ delete entry;
+ }
+ }
+}
+
+void PagePool::Add(int index, PageMemory* memory) {
+ // When adding a page to the pool we decommit it to ensure it is unused
+ // while in the pool. This also allows the physical memory, backing the
+ // page, to be given back to the OS.
+ memory->Decommit();
+ PoolEntry* entry = new PoolEntry(memory, pool_[index]);
+ pool_[index] = entry;
+}
+
+PageMemory* PagePool::Take(int index) {
+ while (PoolEntry* entry = pool_[index]) {
+ pool_[index] = entry->next;
+ PageMemory* memory = entry->data;
+ DCHECK(memory);
+ delete entry;
+ if (memory->Commit())
+ return memory;
+
+ // We got some memory, but failed to commit it, try again.
+ delete memory;
+ }
+ return nullptr;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/heap/page_pool.h b/chromium/third_party/blink/renderer/platform/heap/page_pool.h
new file mode 100644
index 00000000000..0acf9101950
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/page_pool.h
@@ -0,0 +1,48 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_PAGE_POOL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_PAGE_POOL_H_
+
+#include "third_party/blink/renderer/platform/heap/thread_state.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+class PageMemory;
+
+// Once pages have been used for one type of thread heap they will never be
+// reused for another type of thread heap. Instead of unmapping, we add the
+// pages to a pool of pages to be reused later by a thread heap of the same
+// type. This is done as a security feature to avoid type confusion. The
+// heaps are type segregated by having separate thread arenas for different
+// types of objects. Holding on to pages ensures that the same virtual address
+// space cannot be used for objects of another type than the type contained
+// in this page to begin with.
+class PagePool {
+ USING_FAST_MALLOC(PagePool);
+
+ public:
+ PagePool();
+ ~PagePool();
+ void Add(int, PageMemory*);
+ PageMemory* Take(int);
+
+ private:
+ class PoolEntry {
+ USING_FAST_MALLOC(PoolEntry);
+
+ public:
+ PoolEntry(PageMemory* data, PoolEntry* next) : data(data), next(next) {}
+
+ PageMemory* data;
+ PoolEntry* next;
+ };
+
+ PoolEntry* pool_[BlinkGC::kNumberOfArenas];
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/heap/persistent.h b/chromium/third_party/blink/renderer/platform/heap/persistent.h
new file mode 100644
index 00000000000..b7a40b82ce8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/persistent.h
@@ -0,0 +1,852 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_PERSISTENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_PERSISTENT_H_
+
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/heap/member.h"
+#include "third_party/blink/renderer/platform/heap/persistent_node.h"
+#include "third_party/blink/renderer/platform/heap/visitor.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/atomics.h"
+
+namespace blink {
+
+// Marker used to annotate persistent objects and collections with,
+// so as to enable reliable testing for persistent references via
+// a type trait (see TypeTraits.h's IsPersistentReferenceType<>.)
+#define IS_PERSISTENT_REFERENCE_TYPE() \
+ public: \
+ using IsPersistentReferenceTypeMarker = int; \
+ \
+ private:
+
+enum WeaknessPersistentConfiguration {
+ kNonWeakPersistentConfiguration,
+ kWeakPersistentConfiguration
+};
+
+enum CrossThreadnessPersistentConfiguration {
+ kSingleThreadPersistentConfiguration,
+ kCrossThreadPersistentConfiguration
+};
+
+template <typename T,
+ WeaknessPersistentConfiguration weaknessConfiguration,
+ CrossThreadnessPersistentConfiguration crossThreadnessConfiguration>
+class PersistentBase {
+ USING_FAST_MALLOC(PersistentBase);
+ IS_PERSISTENT_REFERENCE_TYPE();
+
+ public:
+ PersistentBase() : raw_(nullptr) {
+ SaveCreationThreadHeap();
+ Initialize();
+ }
+
+ PersistentBase(std::nullptr_t) : raw_(nullptr) {
+ SaveCreationThreadHeap();
+ Initialize();
+ }
+
+ PersistentBase(T* raw) : raw_(raw) {
+ SaveCreationThreadHeap();
+ Initialize();
+ CheckPointer();
+ }
+
+ PersistentBase(T& raw) : raw_(&raw) {
+ SaveCreationThreadHeap();
+ Initialize();
+ CheckPointer();
+ }
+
+ PersistentBase(const PersistentBase& other) : raw_(other) {
+ SaveCreationThreadHeap();
+ Initialize();
+ CheckPointer();
+ }
+
+ template <typename U>
+ PersistentBase(const PersistentBase<U,
+ weaknessConfiguration,
+ crossThreadnessConfiguration>& other)
+ : raw_(other) {
+ SaveCreationThreadHeap();
+ Initialize();
+ CheckPointer();
+ }
+
+ template <typename U>
+ PersistentBase(const Member<U>& other) : raw_(other) {
+ SaveCreationThreadHeap();
+ Initialize();
+ CheckPointer();
+ }
+
+ PersistentBase(WTF::HashTableDeletedValueType)
+ : raw_(reinterpret_cast<T*>(-1)) {
+ SaveCreationThreadHeap();
+ Initialize();
+ CheckPointer();
+ }
+
+ ~PersistentBase() {
+ Uninitialize();
+ raw_ = nullptr;
+ }
+
+ bool IsHashTableDeletedValue() const {
+ return raw_ == reinterpret_cast<T*>(-1);
+ }
+
+ T* Release() {
+ T* result = raw_;
+ Assign(nullptr);
+ return result;
+ }
+
+ void Clear() { Assign(nullptr); }
+ T& operator*() const {
+ CheckPointer();
+ return *raw_;
+ }
+ explicit operator bool() const { return raw_; }
+ operator T*() const {
+ CheckPointer();
+ return raw_;
+ }
+ T* operator->() const { return *this; }
+
+ T* Get() const {
+ CheckPointer();
+ return raw_;
+ }
+
+ template <typename U>
+ PersistentBase& operator=(U* other) {
+ Assign(other);
+ return *this;
+ }
+
+ PersistentBase& operator=(std::nullptr_t) {
+ Assign(nullptr);
+ return *this;
+ }
+
+ PersistentBase& operator=(const PersistentBase& other) {
+ Assign(other);
+ return *this;
+ }
+
+ template <typename U>
+ PersistentBase& operator=(
+ const PersistentBase<U,
+ weaknessConfiguration,
+ crossThreadnessConfiguration>& other) {
+ Assign(other);
+ return *this;
+ }
+
+ template <typename U>
+ PersistentBase& operator=(const Member<U>& other) {
+ Assign(other);
+ return *this;
+ }
+
+ // Register the persistent node as a 'static reference',
+ // belonging to the current thread and a persistent that must
+ // be cleared when the ThreadState itself is cleared out and
+ // destructed.
+ //
+ // Static singletons arrange for this to happen, either to ensure
+ // clean LSan leak reports or to register a thread-local persistent
+ // needing to be cleared out before the thread is terminated.
+ PersistentBase* RegisterAsStaticReference() {
+ CHECK_EQ(weaknessConfiguration, kNonWeakPersistentConfiguration);
+ if (persistent_node_) {
+ DCHECK(ThreadState::Current());
+ ThreadState::Current()->RegisterStaticPersistentNode(persistent_node_,
+ nullptr);
+ LEAK_SANITIZER_IGNORE_OBJECT(this);
+ }
+ return this;
+ }
+
+ protected:
+ NO_SANITIZE_ADDRESS
+ T* AtomicGet() {
+ return reinterpret_cast<T*>(AcquireLoad(reinterpret_cast<void* volatile*>(
+ const_cast<typename std::remove_const<T>::type**>(&raw_))));
+ }
+
+ private:
+ NO_SANITIZE_ADDRESS
+ void Assign(T* ptr) {
+ if (crossThreadnessConfiguration == kCrossThreadPersistentConfiguration) {
+ RecursiveMutexLocker persistent_lock(
+ ProcessHeap::CrossThreadPersistentMutex());
+ raw_ = ptr;
+ } else {
+ raw_ = ptr;
+ }
+ CheckPointer();
+ if (raw_) {
+ if (!persistent_node_)
+ Initialize();
+ return;
+ }
+ Uninitialize();
+ }
+
+ template <typename VisitorDispatcher>
+ void TracePersistent(VisitorDispatcher visitor) {
+ static_assert(sizeof(T), "T must be fully defined");
+ static_assert(IsGarbageCollectedType<T>::value,
+ "T needs to be a garbage collected object");
+ if (weaknessConfiguration == kWeakPersistentConfiguration) {
+ visitor->RegisterWeakCallback(this, HandleWeakPersistent);
+ } else {
+ visitor->Trace(raw_);
+ }
+ }
+
+ NO_SANITIZE_ADDRESS
+ void Initialize() {
+ DCHECK(!persistent_node_);
+ if (!raw_ || IsHashTableDeletedValue())
+ return;
+
+ TraceCallback trace_callback =
+ TraceMethodDelegate<PersistentBase,
+ &PersistentBase::TracePersistent>::Trampoline;
+ if (crossThreadnessConfiguration == kCrossThreadPersistentConfiguration) {
+ CrossThreadPersistentRegion& region =
+ weaknessConfiguration == kWeakPersistentConfiguration
+ ? ProcessHeap::GetCrossThreadWeakPersistentRegion()
+ : ProcessHeap::GetCrossThreadPersistentRegion();
+ region.AllocatePersistentNode(persistent_node_, this, trace_callback);
+ return;
+ }
+ ThreadState* state =
+ ThreadStateFor<ThreadingTrait<T>::kAffinity>::GetState();
+ DCHECK(state->CheckThread());
+ PersistentRegion* region =
+ weaknessConfiguration == kWeakPersistentConfiguration
+ ? state->GetWeakPersistentRegion()
+ : state->GetPersistentRegion();
+ persistent_node_ = region->AllocatePersistentNode(this, trace_callback);
+#if DCHECK_IS_ON()
+ state_ = state;
+#endif
+ }
+
+ void Uninitialize() {
+ if (crossThreadnessConfiguration == kCrossThreadPersistentConfiguration) {
+ if (AcquireLoad(reinterpret_cast<void* volatile*>(&persistent_node_))) {
+ CrossThreadPersistentRegion& region =
+ weaknessConfiguration == kWeakPersistentConfiguration
+ ? ProcessHeap::GetCrossThreadWeakPersistentRegion()
+ : ProcessHeap::GetCrossThreadPersistentRegion();
+ region.FreePersistentNode(persistent_node_);
+ }
+ return;
+ }
+
+ if (!persistent_node_)
+ return;
+ ThreadState* state =
+ ThreadStateFor<ThreadingTrait<T>::kAffinity>::GetState();
+ DCHECK(state->CheckThread());
+ // Persistent handle must be created and destructed in the same thread.
+#if DCHECK_IS_ON()
+ DCHECK_EQ(state_, state);
+#endif
+ PersistentRegion* region =
+ weaknessConfiguration == kWeakPersistentConfiguration
+ ? state->GetWeakPersistentRegion()
+ : state->GetPersistentRegion();
+ state->FreePersistentNode(region, persistent_node_);
+ persistent_node_ = nullptr;
+ }
+
+ void CheckPointer() const {
+#if DCHECK_IS_ON()
+ if (!raw_ || IsHashTableDeletedValue())
+ return;
+
+ if (crossThreadnessConfiguration != kCrossThreadPersistentConfiguration) {
+ ThreadState* current = ThreadState::Current();
+ DCHECK(current);
+ // m_creationThreadState may be null when this is used in a heap
+ // collection which initialized the Persistent with memset and the
+ // constructor wasn't called.
+ if (creation_thread_state_) {
+ // Member should point to objects that belong in the same ThreadHeap.
+ DCHECK_EQ(&ThreadState::FromObject(raw_)->Heap(),
+ &creation_thread_state_->Heap());
+ // Member should point to objects that belong in the same ThreadHeap.
+ DCHECK_EQ(&current->Heap(), &creation_thread_state_->Heap());
+ }
+ }
+#endif
+ }
+
+ void SaveCreationThreadHeap() {
+#if DCHECK_IS_ON()
+ if (crossThreadnessConfiguration == kCrossThreadPersistentConfiguration) {
+ creation_thread_state_ = nullptr;
+ } else {
+ creation_thread_state_ = ThreadState::Current();
+ DCHECK(creation_thread_state_);
+ }
+#endif
+ }
+
+ static void HandleWeakPersistent(Visitor* self, void* persistent_pointer) {
+ using Base =
+ PersistentBase<typename std::remove_const<T>::type,
+ weaknessConfiguration, crossThreadnessConfiguration>;
+ Base* persistent = reinterpret_cast<Base*>(persistent_pointer);
+ T* object = persistent->Get();
+ if (object && !ObjectAliveTrait<T>::IsHeapObjectAlive(object))
+ persistent->Clear();
+ }
+
+ // m_raw is accessed most, so put it at the first field.
+ T* raw_;
+ PersistentNode* persistent_node_ = nullptr;
+#if DCHECK_IS_ON()
+ ThreadState* state_ = nullptr;
+ const ThreadState* creation_thread_state_;
+#endif
+};
+
+// Persistent is a way to create a strong pointer from an off-heap object
+// to another on-heap object. As long as the Persistent handle is alive
+// the GC will keep the object pointed to alive. The Persistent handle is
+// always a GC root from the point of view of the GC.
+//
+// We have to construct and destruct Persistent in the same thread.
+template <typename T>
+class Persistent : public PersistentBase<T,
+ kNonWeakPersistentConfiguration,
+ kSingleThreadPersistentConfiguration> {
+ typedef PersistentBase<T,
+ kNonWeakPersistentConfiguration,
+ kSingleThreadPersistentConfiguration>
+ Parent;
+
+ public:
+ Persistent() : Parent() {}
+ Persistent(std::nullptr_t) : Parent(nullptr) {}
+ Persistent(T* raw) : Parent(raw) {}
+ Persistent(T& raw) : Parent(raw) {}
+ Persistent(const Persistent& other) : Parent(other) {}
+ template <typename U>
+ Persistent(const Persistent<U>& other) : Parent(other) {}
+ template <typename U>
+ Persistent(const Member<U>& other) : Parent(other) {}
+ Persistent(WTF::HashTableDeletedValueType x) : Parent(x) {}
+
+ template <typename U>
+ Persistent& operator=(U* other) {
+ Parent::operator=(other);
+ return *this;
+ }
+
+ Persistent& operator=(std::nullptr_t) {
+ Parent::operator=(nullptr);
+ return *this;
+ }
+
+ Persistent& operator=(const Persistent& other) {
+ Parent::operator=(other);
+ return *this;
+ }
+
+ template <typename U>
+ Persistent& operator=(const Persistent<U>& other) {
+ Parent::operator=(other);
+ return *this;
+ }
+
+ template <typename U>
+ Persistent& operator=(const Member<U>& other) {
+ Parent::operator=(other);
+ return *this;
+ }
+};
+
+// WeakPersistent is a way to create a weak pointer from an off-heap object
+// to an on-heap object. The m_raw is automatically cleared when the pointee
+// gets collected.
+//
+// We have to construct and destruct WeakPersistent in the same thread.
+//
+// Note that collections of WeakPersistents are not supported. Use a persistent
+// collection of WeakMembers instead.
+//
+// HashSet<WeakPersistent<T>> m_set; // wrong
+// PersistentHeapHashSet<WeakMember<T>> m_set; // correct
+template <typename T>
+class WeakPersistent
+ : public PersistentBase<T,
+ kWeakPersistentConfiguration,
+ kSingleThreadPersistentConfiguration> {
+ typedef PersistentBase<T,
+ kWeakPersistentConfiguration,
+ kSingleThreadPersistentConfiguration>
+ Parent;
+
+ public:
+ WeakPersistent() : Parent() {}
+ WeakPersistent(std::nullptr_t) : Parent(nullptr) {}
+ WeakPersistent(T* raw) : Parent(raw) {}
+ WeakPersistent(T& raw) : Parent(raw) {}
+ WeakPersistent(const WeakPersistent& other) : Parent(other) {}
+ template <typename U>
+ WeakPersistent(const WeakPersistent<U>& other) : Parent(other) {}
+ template <typename U>
+ WeakPersistent(const Member<U>& other) : Parent(other) {}
+
+ template <typename U>
+ WeakPersistent& operator=(U* other) {
+ Parent::operator=(other);
+ return *this;
+ }
+
+ WeakPersistent& operator=(std::nullptr_t) {
+ Parent::operator=(nullptr);
+ return *this;
+ }
+
+ WeakPersistent& operator=(const WeakPersistent& other) {
+ Parent::operator=(other);
+ return *this;
+ }
+
+ template <typename U>
+ WeakPersistent& operator=(const WeakPersistent<U>& other) {
+ Parent::operator=(other);
+ return *this;
+ }
+
+ template <typename U>
+ WeakPersistent& operator=(const Member<U>& other) {
+ Parent::operator=(other);
+ return *this;
+ }
+};
+
+// Unlike Persistent, we can destruct a CrossThreadPersistent in a thread
+// different from the construction thread.
+template <typename T>
+class CrossThreadPersistent
+ : public PersistentBase<T,
+ kNonWeakPersistentConfiguration,
+ kCrossThreadPersistentConfiguration> {
+ typedef PersistentBase<T,
+ kNonWeakPersistentConfiguration,
+ kCrossThreadPersistentConfiguration>
+ Parent;
+
+ public:
+ CrossThreadPersistent() : Parent() {}
+ CrossThreadPersistent(std::nullptr_t) : Parent(nullptr) {}
+ CrossThreadPersistent(T* raw) : Parent(raw) {}
+ CrossThreadPersistent(T& raw) : Parent(raw) {}
+ CrossThreadPersistent(const CrossThreadPersistent& other) : Parent(other) {}
+ template <typename U>
+ CrossThreadPersistent(const CrossThreadPersistent<U>& other)
+ : Parent(other) {}
+ template <typename U>
+ CrossThreadPersistent(const Member<U>& other) : Parent(other) {}
+ CrossThreadPersistent(WTF::HashTableDeletedValueType x) : Parent(x) {}
+
+ T* AtomicGet() { return Parent::AtomicGet(); }
+
+ // Instead of using release(), assign then clear() instead.
+ // Using release() with per thread heap enabled can cause the object to be
+ // destroyed before assigning it to a new handle.
+ T* Release() = delete;
+
+ template <typename U>
+ CrossThreadPersistent& operator=(U* other) {
+ Parent::operator=(other);
+ return *this;
+ }
+
+ CrossThreadPersistent& operator=(std::nullptr_t) {
+ Parent::operator=(nullptr);
+ return *this;
+ }
+
+ CrossThreadPersistent& operator=(const CrossThreadPersistent& other) {
+ Parent::operator=(other);
+ return *this;
+ }
+
+ template <typename U>
+ CrossThreadPersistent& operator=(const CrossThreadPersistent<U>& other) {
+ Parent::operator=(other);
+ return *this;
+ }
+
+ template <typename U>
+ CrossThreadPersistent& operator=(const Member<U>& other) {
+ Parent::operator=(other);
+ return *this;
+ }
+};
+
+// Combines the behavior of CrossThreadPersistent and WeakPersistent.
+template <typename T>
+class CrossThreadWeakPersistent
+ : public PersistentBase<T,
+ kWeakPersistentConfiguration,
+ kCrossThreadPersistentConfiguration> {
+ typedef PersistentBase<T,
+ kWeakPersistentConfiguration,
+ kCrossThreadPersistentConfiguration>
+ Parent;
+
+ public:
+ CrossThreadWeakPersistent() : Parent() {}
+ CrossThreadWeakPersistent(std::nullptr_t) : Parent(nullptr) {}
+ CrossThreadWeakPersistent(T* raw) : Parent(raw) {}
+ CrossThreadWeakPersistent(T& raw) : Parent(raw) {}
+ CrossThreadWeakPersistent(const CrossThreadWeakPersistent& other)
+ : Parent(other) {}
+ template <typename U>
+ CrossThreadWeakPersistent(const CrossThreadWeakPersistent<U>& other)
+ : Parent(other) {}
+ template <typename U>
+ CrossThreadWeakPersistent(const Member<U>& other) : Parent(other) {}
+
+ template <typename U>
+ CrossThreadWeakPersistent& operator=(U* other) {
+ Parent::operator=(other);
+ return *this;
+ }
+
+ CrossThreadWeakPersistent& operator=(std::nullptr_t) {
+ Parent::operator=(nullptr);
+ return *this;
+ }
+
+ CrossThreadWeakPersistent& operator=(const CrossThreadWeakPersistent& other) {
+ Parent::operator=(other);
+ return *this;
+ }
+
+ template <typename U>
+ CrossThreadWeakPersistent& operator=(
+ const CrossThreadWeakPersistent<U>& other) {
+ Parent::operator=(other);
+ return *this;
+ }
+
+ template <typename U>
+ CrossThreadWeakPersistent& operator=(const Member<U>& other) {
+ Parent::operator=(other);
+ return *this;
+ }
+};
+
+template <typename Collection>
+class PersistentHeapCollectionBase : public Collection {
+ // We overload the various new and delete operators with using the WTF
+ // PartitionAllocator to ensure persistent heap collections are always
+ // allocated off-heap. This allows persistent collections to be used in
+ // DEFINE_STATIC_LOCAL et. al.
+ USE_ALLOCATOR(PersistentHeapCollectionBase, WTF::PartitionAllocator);
+ IS_PERSISTENT_REFERENCE_TYPE();
+
+ public:
+ PersistentHeapCollectionBase() { Initialize(); }
+
+ PersistentHeapCollectionBase(const PersistentHeapCollectionBase& other)
+ : Collection(other) {
+ Initialize();
+ }
+
+ template <typename OtherCollection>
+ PersistentHeapCollectionBase(const OtherCollection& other)
+ : Collection(other) {
+ Initialize();
+ }
+
+ ~PersistentHeapCollectionBase() { Uninitialize(); }
+
+ // See PersistentBase::registerAsStaticReference() comment.
+ PersistentHeapCollectionBase* RegisterAsStaticReference() {
+ if (persistent_node_) {
+ DCHECK(ThreadState::Current());
+ ThreadState::Current()->RegisterStaticPersistentNode(
+ persistent_node_,
+ &PersistentHeapCollectionBase<Collection>::ClearPersistentNode);
+ LEAK_SANITIZER_IGNORE_OBJECT(this);
+ }
+ return this;
+ }
+
+ private:
+ template <typename VisitorDispatcher>
+ void TracePersistent(VisitorDispatcher visitor) {
+ static_assert(sizeof(Collection), "Collection must be fully defined");
+ visitor->Trace(*static_cast<Collection*>(this));
+ }
+
+ // Used when the registered PersistentNode of this object is
+ // released during ThreadState shutdown, clearing the association.
+ static void ClearPersistentNode(void* self) {
+ PersistentHeapCollectionBase<Collection>* collection =
+ (reinterpret_cast<PersistentHeapCollectionBase<Collection>*>(self));
+ collection->Uninitialize();
+ collection->clear();
+ }
+
+ NO_SANITIZE_ADDRESS
+ void Initialize() {
+ // FIXME: Derive affinity based on the collection.
+ ThreadState* state = ThreadState::Current();
+ DCHECK(state->CheckThread());
+ persistent_node_ = state->GetPersistentRegion()->AllocatePersistentNode(
+ this,
+ TraceMethodDelegate<PersistentHeapCollectionBase<Collection>,
+ &PersistentHeapCollectionBase<
+ Collection>::TracePersistent>::Trampoline);
+#if DCHECK_IS_ON()
+ state_ = state;
+#endif
+ }
+
+ void Uninitialize() {
+ if (!persistent_node_)
+ return;
+ ThreadState* state = ThreadState::Current();
+ DCHECK(state->CheckThread());
+ // Persistent handle must be created and destructed in the same thread.
+#if DCHECK_IS_ON()
+ DCHECK_EQ(state_, state);
+#endif
+ state->FreePersistentNode(state->GetPersistentRegion(), persistent_node_);
+ persistent_node_ = nullptr;
+ }
+
+ PersistentNode* persistent_node_;
+#if DCHECK_IS_ON()
+ ThreadState* state_;
+#endif
+};
+
+template <typename KeyArg,
+ typename MappedArg,
+ typename HashArg = typename DefaultHash<KeyArg>::Hash,
+ typename KeyTraitsArg = HashTraits<KeyArg>,
+ typename MappedTraitsArg = HashTraits<MappedArg>>
+class PersistentHeapHashMap
+ : public PersistentHeapCollectionBase<HeapHashMap<KeyArg,
+ MappedArg,
+ HashArg,
+ KeyTraitsArg,
+ MappedTraitsArg>> {};
+
+template <typename ValueArg,
+ typename HashArg = typename DefaultHash<ValueArg>::Hash,
+ typename TraitsArg = HashTraits<ValueArg>>
+class PersistentHeapHashSet : public PersistentHeapCollectionBase<
+ HeapHashSet<ValueArg, HashArg, TraitsArg>> {};
+
+template <typename ValueArg,
+ typename HashArg = typename DefaultHash<ValueArg>::Hash,
+ typename TraitsArg = HashTraits<ValueArg>>
+class PersistentHeapLinkedHashSet
+ : public PersistentHeapCollectionBase<
+ HeapLinkedHashSet<ValueArg, HashArg, TraitsArg>> {};
+
+template <typename ValueArg,
+ size_t inlineCapacity = 0,
+ typename HashArg = typename DefaultHash<ValueArg>::Hash>
+class PersistentHeapListHashSet
+ : public PersistentHeapCollectionBase<
+ HeapListHashSet<ValueArg, inlineCapacity, HashArg>> {};
+
+template <typename ValueArg,
+ typename HashFunctions = typename DefaultHash<ValueArg>::Hash,
+ typename Traits = HashTraits<ValueArg>>
+class PersistentHeapHashCountedSet
+ : public PersistentHeapCollectionBase<
+ HeapHashCountedSet<ValueArg, HashFunctions, Traits>> {};
+
+template <typename T, size_t inlineCapacity = 0>
+class PersistentHeapVector
+ : public PersistentHeapCollectionBase<HeapVector<T, inlineCapacity>> {
+ public:
+ PersistentHeapVector() { InitializeUnusedSlots(); }
+
+ explicit PersistentHeapVector(size_t size)
+ : PersistentHeapCollectionBase<HeapVector<T, inlineCapacity>>(size) {
+ InitializeUnusedSlots();
+ }
+
+ PersistentHeapVector(const PersistentHeapVector& other)
+ : PersistentHeapCollectionBase<HeapVector<T, inlineCapacity>>(other) {
+ InitializeUnusedSlots();
+ }
+
+ template <size_t otherCapacity>
+ PersistentHeapVector(const HeapVector<T, otherCapacity>& other)
+ : PersistentHeapCollectionBase<HeapVector<T, inlineCapacity>>(other) {
+ InitializeUnusedSlots();
+ }
+
+ private:
+ void InitializeUnusedSlots() {
+ // The PersistentHeapVector is allocated off heap along with its
+ // inline buffer (if any.) Maintain the invariant that unused
+ // slots are cleared for the off-heap inline buffer also.
+ size_t unused_slots = this->capacity() - this->size();
+ if (unused_slots)
+ this->ClearUnusedSlots(this->end(), this->end() + unused_slots);
+ }
+};
+
+template <typename T, size_t inlineCapacity = 0>
+class PersistentHeapDeque
+ : public PersistentHeapCollectionBase<HeapDeque<T, inlineCapacity>> {
+ public:
+ PersistentHeapDeque() = default;
+
+ template <size_t otherCapacity>
+ PersistentHeapDeque(const HeapDeque<T, otherCapacity>& other)
+ : PersistentHeapCollectionBase<HeapDeque<T, inlineCapacity>>(other) {}
+};
+
+template <typename T>
+Persistent<T> WrapPersistent(T* value) {
+ // There is no technical need to require a complete type here. However, types
+ // that support wrapper-tracing are not suitable with WrapPersistent because
+ // Persistent<T> does not perform wrapper-tracing. We'd like to delete such
+ // overloads for sure. Thus, we require a complete type here so that it makes
+ // sure that an appropriate header is included and such an overload is
+ // deleted.
+ static_assert(sizeof(T), "T must be fully defined");
+
+ return Persistent<T>(value);
+}
+
+template <typename T,
+ typename = std::enable_if_t<WTF::IsGarbageCollectedType<T>::value>>
+Persistent<T> WrapPersistentIfNeeded(T* value) {
+ return Persistent<T>(value);
+}
+
+template <typename T>
+T& WrapPersistentIfNeeded(T& value) {
+ return value;
+}
+
+template <typename T>
+WeakPersistent<T> WrapWeakPersistent(T* value) {
+ return WeakPersistent<T>(value);
+}
+
+template <typename T>
+CrossThreadPersistent<T> WrapCrossThreadPersistent(T* value) {
+ return CrossThreadPersistent<T>(value);
+}
+
+template <typename T>
+CrossThreadWeakPersistent<T> WrapCrossThreadWeakPersistent(T* value) {
+ return CrossThreadWeakPersistent<T>(value);
+}
+
+// Comparison operators between (Weak)Members, Persistents, and UntracedMembers.
+template <typename T, typename U>
+inline bool operator==(const Member<T>& a, const Member<U>& b) {
+ return a.Get() == b.Get();
+}
+template <typename T, typename U>
+inline bool operator!=(const Member<T>& a, const Member<U>& b) {
+ return a.Get() != b.Get();
+}
+template <typename T, typename U>
+inline bool operator==(const Persistent<T>& a, const Persistent<U>& b) {
+ return a.Get() == b.Get();
+}
+template <typename T, typename U>
+inline bool operator!=(const Persistent<T>& a, const Persistent<U>& b) {
+ return a.Get() != b.Get();
+}
+
+template <typename T, typename U>
+inline bool operator==(const Member<T>& a, const Persistent<U>& b) {
+ return a.Get() == b.Get();
+}
+template <typename T, typename U>
+inline bool operator!=(const Member<T>& a, const Persistent<U>& b) {
+ return a.Get() != b.Get();
+}
+template <typename T, typename U>
+inline bool operator==(const Persistent<T>& a, const Member<U>& b) {
+ return a.Get() == b.Get();
+}
+template <typename T, typename U>
+inline bool operator!=(const Persistent<T>& a, const Member<U>& b) {
+ return a.Get() != b.Get();
+}
+
+} // namespace blink
+
+namespace WTF {
+
+template <typename T>
+struct DefaultHash<blink::Persistent<T>> {
+ STATIC_ONLY(DefaultHash);
+ using Hash = MemberHash<T>;
+};
+
+template <typename T>
+struct DefaultHash<blink::WeakPersistent<T>> {
+ STATIC_ONLY(DefaultHash);
+ using Hash = MemberHash<T>;
+};
+
+template <typename T>
+struct DefaultHash<blink::CrossThreadPersistent<T>> {
+ STATIC_ONLY(DefaultHash);
+ using Hash = MemberHash<T>;
+};
+
+template <typename T>
+struct DefaultHash<blink::CrossThreadWeakPersistent<T>> {
+ STATIC_ONLY(DefaultHash);
+ using Hash = MemberHash<T>;
+};
+
+} // namespace WTF
+
+namespace base {
+
+template <typename T>
+struct IsWeakReceiver<blink::WeakPersistent<T>> : std::true_type {};
+
+template <typename T>
+struct IsWeakReceiver<blink::CrossThreadWeakPersistent<T>> : std::true_type {};
+
+template <typename T>
+struct BindUnwrapTraits<blink::CrossThreadWeakPersistent<T>> {
+ static blink::CrossThreadPersistent<T> Unwrap(
+ const blink::CrossThreadWeakPersistent<T>& wrapped) {
+ WTF::RecursiveMutexLocker persistent_lock(
+ blink::ProcessHeap::CrossThreadPersistentMutex());
+ return blink::CrossThreadPersistent<T>(wrapped.Get());
+ }
+};
+}
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_PERSISTENT_H_
diff --git a/chromium/third_party/blink/renderer/platform/heap/persistent_node.cc b/chromium/third_party/blink/renderer/platform/heap/persistent_node.cc
new file mode 100644
index 00000000000..c79d85cc5fd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/persistent_node.cc
@@ -0,0 +1,199 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/heap/persistent_node.h"
+
+#include "base/debug/alias.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/heap/process_heap.h"
+
+namespace blink {
+
+namespace {
+
+class DummyGCBase final : public GarbageCollected<DummyGCBase> {
+ public:
+ void Trace(blink::Visitor* visitor) {}
+};
+}
+
+PersistentRegion::~PersistentRegion() {
+ PersistentNodeSlots* slots = slots_;
+ while (slots) {
+ PersistentNodeSlots* dead_slots = slots;
+ slots = slots->next_;
+ delete dead_slots;
+ }
+}
+
+int PersistentRegion::NumberOfPersistents() {
+ int persistent_count = 0;
+ for (PersistentNodeSlots* slots = slots_; slots; slots = slots->next_) {
+ for (int i = 0; i < PersistentNodeSlots::kSlotCount; ++i) {
+ if (!slots->slot_[i].IsUnused())
+ ++persistent_count;
+ }
+ }
+#if DCHECK_IS_ON()
+ DCHECK_EQ(persistent_count, persistent_count_);
+#endif
+ return persistent_count;
+}
+
+void PersistentRegion::EnsurePersistentNodeSlots(void* self,
+ TraceCallback trace) {
+ DCHECK(!free_list_head_);
+ PersistentNodeSlots* slots = new PersistentNodeSlots;
+ for (int i = 0; i < PersistentNodeSlots::kSlotCount; ++i) {
+ PersistentNode* node = &slots->slot_[i];
+ node->SetFreeListNext(free_list_head_);
+ free_list_head_ = node;
+ DCHECK(node->IsUnused());
+ }
+ slots->next_ = slots_;
+ slots_ = slots;
+}
+
+void PersistentRegion::ReleasePersistentNode(
+ PersistentNode* persistent_node,
+ ThreadState::PersistentClearCallback callback) {
+ DCHECK(!persistent_node->IsUnused());
+ // 'self' is in use, containing the persistent wrapper object.
+ void* self = persistent_node->Self();
+ if (callback) {
+ (*callback)(self);
+ DCHECK(persistent_node->IsUnused());
+ return;
+ }
+ Persistent<DummyGCBase>* persistent =
+ reinterpret_cast<Persistent<DummyGCBase>*>(self);
+ persistent->Clear();
+ DCHECK(persistent_node->IsUnused());
+}
+
+// This function traces all PersistentNodes. If we encounter
+// a PersistentNodeSlot that contains only freed PersistentNodes,
+// we delete the PersistentNodeSlot. This function rebuilds the free
+// list of PersistentNodes.
+void PersistentRegion::TracePersistentNodes(Visitor* visitor,
+ ShouldTraceCallback should_trace) {
+ size_t debug_marked_object_size = ProcessHeap::TotalMarkedObjectSize();
+ base::debug::Alias(&debug_marked_object_size);
+
+ free_list_head_ = nullptr;
+ int persistent_count = 0;
+ PersistentNodeSlots** prev_next = &slots_;
+ PersistentNodeSlots* slots = slots_;
+ while (slots) {
+ PersistentNode* free_list_next = nullptr;
+ PersistentNode* free_list_last = nullptr;
+ int free_count = 0;
+ for (int i = 0; i < PersistentNodeSlots::kSlotCount; ++i) {
+ PersistentNode* node = &slots->slot_[i];
+ if (node->IsUnused()) {
+ if (!free_list_next)
+ free_list_last = node;
+ node->SetFreeListNext(free_list_next);
+ free_list_next = node;
+ ++free_count;
+ } else {
+ ++persistent_count;
+ if (!should_trace(visitor, node))
+ continue;
+ node->TracePersistentNode(visitor);
+ debug_marked_object_size = ProcessHeap::TotalMarkedObjectSize();
+ }
+ }
+ if (free_count == PersistentNodeSlots::kSlotCount) {
+ PersistentNodeSlots* dead_slots = slots;
+ *prev_next = slots->next_;
+ slots = slots->next_;
+ delete dead_slots;
+ } else {
+ if (free_list_last) {
+ DCHECK(free_list_next);
+ DCHECK(!free_list_last->FreeListNext());
+ free_list_last->SetFreeListNext(free_list_head_);
+ free_list_head_ = free_list_next;
+ }
+ prev_next = &slots->next_;
+ slots = slots->next_;
+ }
+ }
+#if DCHECK_IS_ON()
+ DCHECK_EQ(persistent_count, persistent_count_);
+#endif
+}
+
+bool CrossThreadPersistentRegion::ShouldTracePersistentNode(
+ Visitor* visitor,
+ PersistentNode* node) {
+ CrossThreadPersistent<DummyGCBase>* persistent =
+ reinterpret_cast<CrossThreadPersistent<DummyGCBase>*>(node->Self());
+ DCHECK(persistent);
+ DCHECK(!persistent->IsHashTableDeletedValue());
+ Address raw_object = reinterpret_cast<Address>(persistent->Get());
+ if (!raw_object)
+ return false;
+ return &visitor->Heap() == &ThreadState::FromObject(raw_object)->Heap();
+}
+
+void CrossThreadPersistentRegion::PrepareForThreadStateTermination(
+ ThreadState* thread_state) {
+ // For heaps belonging to a thread that's detaching, any cross-thread
+ // persistents pointing into them needs to be disabled. Do that by clearing
+ // out the underlying heap reference.
+ RecursiveMutexLocker lock(ProcessHeap::CrossThreadPersistentMutex());
+
+ // TODO(sof): consider ways of reducing overhead. (e.g., tracking number of
+ // active CrossThreadPersistent<>s pointing into the heaps of each ThreadState
+ // and use that count to bail out early.)
+ PersistentNodeSlots* slots = persistent_region_->slots_;
+ while (slots) {
+ for (int i = 0; i < PersistentNodeSlots::kSlotCount; ++i) {
+ if (slots->slot_[i].IsUnused())
+ continue;
+
+ // 'self' is in use, containing the cross-thread persistent wrapper
+ // object.
+ CrossThreadPersistent<DummyGCBase>* persistent =
+ reinterpret_cast<CrossThreadPersistent<DummyGCBase>*>(
+ slots->slot_[i].Self());
+ DCHECK(persistent);
+ void* raw_object = persistent->AtomicGet();
+ if (!raw_object)
+ continue;
+ BasePage* page = PageFromObject(raw_object);
+ DCHECK(page);
+ if (page->Arena()->GetThreadState() == thread_state) {
+ persistent->Clear();
+ DCHECK(slots->slot_[i].IsUnused());
+ }
+ }
+ slots = slots->next_;
+ }
+}
+
+#if defined(ADDRESS_SANITIZER)
+void CrossThreadPersistentRegion::UnpoisonCrossThreadPersistents() {
+ RecursiveMutexLocker lock(ProcessHeap::CrossThreadPersistentMutex());
+ int persistent_count = 0;
+ for (PersistentNodeSlots* slots = persistent_region_->slots_; slots;
+ slots = slots->next_) {
+ for (int i = 0; i < PersistentNodeSlots::kSlotCount; ++i) {
+ const PersistentNode& node = slots->slot_[i];
+ if (!node.IsUnused()) {
+ ASAN_UNPOISON_MEMORY_REGION(node.Self(),
+ sizeof(CrossThreadPersistent<void*>));
+ ++persistent_count;
+ }
+ }
+ }
+#if DCHECK_IS_ON()
+ DCHECK_EQ(persistent_count, persistent_region_->persistent_count_);
+#endif
+}
+#endif
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/heap/persistent_node.h b/chromium/third_party/blink/renderer/platform/heap/persistent_node.h
new file mode 100644
index 00000000000..a3eb2b117b2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/persistent_node.h
@@ -0,0 +1,235 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_PERSISTENT_NODE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_PERSISTENT_NODE_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/heap/process_heap.h"
+#include "third_party/blink/renderer/platform/heap/thread_state.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
+
+namespace blink {
+
+class CrossThreadPersistentRegion;
+
+class PersistentNode final {
+ DISALLOW_NEW();
+
+ public:
+ PersistentNode() : self_(nullptr), trace_(nullptr) { DCHECK(IsUnused()); }
+
+#if DCHECK_IS_ON()
+ ~PersistentNode() {
+ // If you hit this assert, it means that the thread finished
+ // without clearing persistent handles that the thread created.
+ // We don't enable the assert for the main thread because the
+ // main thread finishes without clearing all persistent handles.
+ DCHECK(IsMainThread() || IsUnused());
+ }
+#endif
+
+ // It is dangerous to copy the PersistentNode because it breaks the
+ // free list.
+ PersistentNode& operator=(const PersistentNode& otherref) = delete;
+
+ // Ideally the trace method should be virtual and automatically dispatch
+ // to the most specific implementation. However having a virtual method
+ // on PersistentNode leads to too eager template instantiation with MSVC
+ // which leads to include cycles.
+ // Instead we call the constructor with a TraceCallback which knows the
+ // type of the most specific child and calls trace directly. See
+ // TraceMethodDelegate in Visitor.h for how this is done.
+ void TracePersistentNode(Visitor* visitor) {
+ DCHECK(!IsUnused());
+ DCHECK(trace_);
+ trace_(visitor, self_);
+ }
+
+ void Initialize(void* self, TraceCallback trace) {
+ DCHECK(IsUnused());
+ self_ = self;
+ trace_ = trace;
+ }
+
+ void SetFreeListNext(PersistentNode* node) {
+ DCHECK(!node || node->IsUnused());
+ self_ = node;
+ trace_ = nullptr;
+ DCHECK(IsUnused());
+ }
+
+ PersistentNode* FreeListNext() {
+ DCHECK(IsUnused());
+ PersistentNode* node = reinterpret_cast<PersistentNode*>(self_);
+ DCHECK(!node || node->IsUnused());
+ return node;
+ }
+
+ bool IsUnused() const { return !trace_; }
+
+ void* Self() const { return self_; }
+
+ private:
+ // If this PersistentNode is in use:
+ // - m_self points to the corresponding Persistent handle.
+ // - m_trace points to the trace method.
+ // If this PersistentNode is freed:
+ // - m_self points to the next freed PersistentNode.
+ // - m_trace is nullptr.
+ void* self_;
+ TraceCallback trace_;
+};
+
+struct PersistentNodeSlots final {
+ USING_FAST_MALLOC(PersistentNodeSlots);
+
+ private:
+ static const int kSlotCount = 256;
+ PersistentNodeSlots* next_;
+ PersistentNode slot_[kSlotCount];
+ friend class PersistentRegion;
+ friend class CrossThreadPersistentRegion;
+};
+
+// PersistentRegion provides a region of PersistentNodes. PersistentRegion
+// holds a linked list of PersistentNodeSlots, each of which stores
+// a predefined number of PersistentNodes. You can call allocatePersistentNode/
+// freePersistentNode to allocate/free a PersistentNode on the region.
+class PLATFORM_EXPORT PersistentRegion final {
+ USING_FAST_MALLOC(PersistentRegion);
+
+ public:
+ PersistentRegion()
+ : free_list_head_(nullptr),
+ slots_(nullptr)
+#if DCHECK_IS_ON()
+ ,
+ persistent_count_(0)
+#endif
+ {
+ }
+ ~PersistentRegion();
+
+ PersistentNode* AllocatePersistentNode(void* self, TraceCallback trace) {
+#if DCHECK_IS_ON()
+ ++persistent_count_;
+#endif
+ if (UNLIKELY(!free_list_head_))
+ EnsurePersistentNodeSlots(self, trace);
+ DCHECK(free_list_head_);
+ PersistentNode* node = free_list_head_;
+ free_list_head_ = free_list_head_->FreeListNext();
+ node->Initialize(self, trace);
+ DCHECK(!node->IsUnused());
+ return node;
+ }
+
+ void FreePersistentNode(PersistentNode* persistent_node) {
+#if DCHECK_IS_ON()
+ DCHECK_GT(persistent_count_, 0);
+#endif
+ persistent_node->SetFreeListNext(free_list_head_);
+ free_list_head_ = persistent_node;
+#if DCHECK_IS_ON()
+ --persistent_count_;
+#endif
+ }
+
+ static bool ShouldTracePersistentNode(Visitor*, PersistentNode*) {
+ return true;
+ }
+
+ void ReleasePersistentNode(PersistentNode*,
+ ThreadState::PersistentClearCallback);
+ using ShouldTraceCallback = bool (*)(Visitor*, PersistentNode*);
+ void TracePersistentNodes(
+ Visitor*,
+ ShouldTraceCallback = PersistentRegion::ShouldTracePersistentNode);
+ int NumberOfPersistents();
+
+ private:
+ friend CrossThreadPersistentRegion;
+
+ void EnsurePersistentNodeSlots(void*, TraceCallback);
+
+ PersistentNode* free_list_head_;
+ PersistentNodeSlots* slots_;
+#if DCHECK_IS_ON()
+ int persistent_count_;
+#endif
+};
+
+class CrossThreadPersistentRegion final {
+ USING_FAST_MALLOC(CrossThreadPersistentRegion);
+
+ public:
+ CrossThreadPersistentRegion()
+ : persistent_region_(std::make_unique<PersistentRegion>()) {}
+
+ void AllocatePersistentNode(PersistentNode*& persistent_node,
+ void* self,
+ TraceCallback trace) {
+ RecursiveMutexLocker lock(ProcessHeap::CrossThreadPersistentMutex());
+ PersistentNode* node =
+ persistent_region_->AllocatePersistentNode(self, trace);
+ ReleaseStore(reinterpret_cast<void* volatile*>(&persistent_node), node);
+ }
+
+ void FreePersistentNode(PersistentNode*& persistent_node) {
+ RecursiveMutexLocker lock(ProcessHeap::CrossThreadPersistentMutex());
+ // When the thread that holds the heap object that the cross-thread
+ // persistent shuts down, prepareForThreadStateTermination() will clear out
+ // the associated CrossThreadPersistent<> and PersistentNode so as to avoid
+ // unsafe access. This can overlap with a holder of the
+ // CrossThreadPersistent<> also clearing the persistent and freeing the
+ // PersistentNode.
+ //
+ // The lock ensures the updating is ordered, but by the time lock has been
+ // acquired the PersistentNode reference may have been cleared out already;
+ // check for this.
+ if (!persistent_node)
+ return;
+ persistent_region_->FreePersistentNode(persistent_node);
+ ReleaseStore(reinterpret_cast<void* volatile*>(&persistent_node), nullptr);
+ }
+
+ void TracePersistentNodes(Visitor* visitor) {
+// If this assert triggers, you're tracing without being in a LockScope.
+#if DCHECK_IS_ON()
+ DCHECK(ProcessHeap::CrossThreadPersistentMutex().Locked());
+#endif
+ persistent_region_->TracePersistentNodes(
+ visitor, CrossThreadPersistentRegion::ShouldTracePersistentNode);
+ }
+
+ void PrepareForThreadStateTermination(ThreadState*);
+
+ NO_SANITIZE_ADDRESS
+ static bool ShouldTracePersistentNode(Visitor*, PersistentNode*);
+
+#if defined(ADDRESS_SANITIZER)
+ void UnpoisonCrossThreadPersistents();
+#endif
+
+ private:
+
+ // We don't make CrossThreadPersistentRegion inherit from PersistentRegion
+ // because we don't want to virtualize performance-sensitive methods
+ // such as PersistentRegion::allocate/freePersistentNode.
+ std::unique_ptr<PersistentRegion> persistent_region_;
+
+ // Recursive as prepareForThreadStateTermination() clears a PersistentNode's
+ // associated Persistent<> -- it in turn freeing the PersistentNode. And both
+ // CrossThreadPersistentRegion operations need a lock on the region before
+ // mutating.
+ RecursiveMutex mutex_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/heap/persistent_test.cc b/chromium/third_party/blink/renderer/platform/heap/persistent_test.cc
new file mode 100644
index 00000000000..fad42771309
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/persistent_test.cc
@@ -0,0 +1,54 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/heap/persistent.h"
+
+#include <memory>
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/heap/heap_test_utilities.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+
+namespace blink {
+namespace {
+
+class Receiver : public GarbageCollected<Receiver> {
+ public:
+ void Increment(int* counter) { ++*counter; }
+
+ void Trace(blink::Visitor* visitor) {}
+};
+
+TEST(PersistentTest, BindCancellation) {
+ Receiver* receiver = new Receiver;
+ int counter = 0;
+ base::RepeatingClosure function =
+ WTF::BindRepeating(&Receiver::Increment, WrapWeakPersistent(receiver),
+ WTF::Unretained(&counter));
+
+ function.Run();
+ EXPECT_EQ(1, counter);
+
+ receiver = nullptr;
+ PreciselyCollectGarbage();
+ function.Run();
+ EXPECT_EQ(1, counter);
+}
+
+TEST(PersistentTest, CrossThreadBindCancellation) {
+ Receiver* receiver = new Receiver;
+ int counter = 0;
+ CrossThreadClosure function = blink::CrossThreadBind(
+ &Receiver::Increment, WrapCrossThreadWeakPersistent(receiver),
+ WTF::CrossThreadUnretained(&counter));
+
+ receiver = nullptr;
+ PreciselyCollectGarbage();
+ std::move(function).Run();
+ EXPECT_EQ(0, counter);
+}
+
+} // namespace
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/heap/process_heap.cc b/chromium/third_party/blink/renderer/platform/heap/process_heap.cc
new file mode 100644
index 00000000000..5890dea8cc2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/process_heap.cc
@@ -0,0 +1,66 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/heap/process_heap.h"
+
+#include "base/sampling_heap_profiler/sampling_heap_profiler.h"
+#include "third_party/blink/renderer/platform/heap/gc_info.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/heap/persistent_node.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+namespace blink {
+
+namespace {
+
+void BlinkGCAllocHook(uint8_t* address, size_t size, const char*) {
+ base::SamplingHeapProfiler::RecordAlloc(address, size);
+}
+
+void BlinkGCFreeHook(uint8_t* address) {
+ base::SamplingHeapProfiler::RecordFree(address);
+}
+
+} // namespace
+
+void ProcessHeap::Init() {
+ total_allocated_space_ = 0;
+ total_allocated_object_size_ = 0;
+ total_marked_object_size_ = 0;
+
+ GCInfoTable::Init();
+
+ base::SamplingHeapProfiler::SetHooksInstallCallback([]() {
+ HeapAllocHooks::SetAllocationHook(&BlinkGCAllocHook);
+ HeapAllocHooks::SetFreeHook(&BlinkGCFreeHook);
+ });
+}
+
+void ProcessHeap::ResetHeapCounters() {
+ total_allocated_object_size_ = 0;
+ total_marked_object_size_ = 0;
+}
+
+CrossThreadPersistentRegion& ProcessHeap::GetCrossThreadPersistentRegion() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(CrossThreadPersistentRegion,
+ persistent_region, ());
+ return persistent_region;
+}
+
+CrossThreadPersistentRegion& ProcessHeap::GetCrossThreadWeakPersistentRegion() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(CrossThreadPersistentRegion,
+ persistent_region, ());
+ return persistent_region;
+}
+
+RecursiveMutex& ProcessHeap::CrossThreadPersistentMutex() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(RecursiveMutex, mutex, ());
+ return mutex;
+}
+
+size_t ProcessHeap::total_allocated_space_ = 0;
+size_t ProcessHeap::total_allocated_object_size_ = 0;
+size_t ProcessHeap::total_marked_object_size_ = 0;
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/heap/process_heap.h b/chromium/third_party/blink/renderer/platform/heap/process_heap.h
new file mode 100644
index 00000000000..e9f504e1ac9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/process_heap.h
@@ -0,0 +1,71 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_PROCESS_HEAP_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_PROCESS_HEAP_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/atomics.h"
+#include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
+
+namespace blink {
+
+class CrossThreadPersistentRegion;
+
+class PLATFORM_EXPORT ProcessHeap {
+ STATIC_ONLY(ProcessHeap);
+
+ public:
+ static void Init();
+
+ static CrossThreadPersistentRegion& GetCrossThreadPersistentRegion();
+ static CrossThreadPersistentRegion& GetCrossThreadWeakPersistentRegion();
+
+ // Recursive as prepareForThreadStateTermination() clears a PersistentNode's
+ // associated Persistent<> -- it in turn freeing the PersistentNode. And both
+ // CrossThreadPersistentRegion operations need a lock on the region before
+ // mutating.
+ static RecursiveMutex& CrossThreadPersistentMutex();
+
+ static void IncreaseTotalAllocatedObjectSize(size_t delta) {
+ AtomicAdd(&total_allocated_object_size_, static_cast<long>(delta));
+ }
+ static void DecreaseTotalAllocatedObjectSize(size_t delta) {
+ AtomicSubtract(&total_allocated_object_size_, static_cast<long>(delta));
+ }
+ static size_t TotalAllocatedObjectSize() {
+ return AcquireLoad(&total_allocated_object_size_);
+ }
+ static void IncreaseTotalMarkedObjectSize(size_t delta) {
+ AtomicAdd(&total_marked_object_size_, static_cast<long>(delta));
+ }
+ static void DecreaseTotalMarkedObjectSize(size_t delta) {
+ AtomicSubtract(&total_marked_object_size_, static_cast<long>(delta));
+ }
+ static size_t TotalMarkedObjectSize() {
+ return AcquireLoad(&total_marked_object_size_);
+ }
+ static void IncreaseTotalAllocatedSpace(size_t delta) {
+ AtomicAdd(&total_allocated_space_, static_cast<long>(delta));
+ }
+ static void DecreaseTotalAllocatedSpace(size_t delta) {
+ AtomicSubtract(&total_allocated_space_, static_cast<long>(delta));
+ }
+ static size_t TotalAllocatedSpace() {
+ return AcquireLoad(&total_allocated_space_);
+ }
+ static void ResetHeapCounters();
+
+ private:
+ static size_t total_allocated_space_;
+ static size_t total_allocated_object_size_;
+ static size_t total_marked_object_size_;
+
+ friend class ThreadState;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/heap/run_all_tests.cc b/chromium/third_party/blink/renderer/platform/heap/run_all_tests.cc
new file mode 100644
index 00000000000..d8ee785ad2d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/run_all_tests.cc
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2014 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 "base/bind.h"
+#include "base/test/launcher/unit_test_launcher.h"
+#include "base/test/test_suite.h"
+#include "content/public/test/blink_test_environment.h"
+#include "third_party/blink/renderer/platform/heap/thread_state.h"
+
+class BlinkTestEnvironmentScope {
+ public:
+ BlinkTestEnvironmentScope() { content::SetUpBlinkTestEnvironment(); }
+ ~BlinkTestEnvironmentScope() { content::TearDownBlinkTestEnvironment(); }
+};
+
+int runHelper(base::TestSuite* testSuite) {
+ BlinkTestEnvironmentScope blinkTestEnvironment;
+ blink::ThreadState* currentThreadState = blink::ThreadState::Current();
+ currentThreadState->RegisterTraceDOMWrappers(nullptr, nullptr, nullptr,
+ nullptr);
+ int result = testSuite->Run();
+ currentThreadState->CollectAllGarbage();
+ return result;
+}
+
+int main(int argc, char** argv) {
+ base::TestSuite testSuite(argc, argv);
+ return base::LaunchUnitTests(
+ argc, argv, base::BindOnce(runHelper, base::Unretained(&testSuite)));
+}
diff --git a/chromium/third_party/blink/renderer/platform/heap/safe_point.h b/chromium/third_party/blink/renderer/platform/heap/safe_point.h
new file mode 100644
index 00000000000..41a3e7b1177
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/safe_point.h
@@ -0,0 +1,39 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_SAFE_POINT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_SAFE_POINT_H_
+
+#include "base/macros.h"
+#include "third_party/blink/renderer/platform/heap/thread_state.h"
+#include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
+
+namespace blink {
+
+class SafePointScope final {
+ STACK_ALLOCATED();
+
+ public:
+ explicit SafePointScope(BlinkGC::StackState stack_state,
+ ThreadState* state = ThreadState::Current())
+ : state_(state) {
+ if (state_) {
+ state_->EnterSafePoint(stack_state, this);
+ }
+ }
+
+ ~SafePointScope() {
+ if (state_)
+ state_->LeaveSafePoint();
+ }
+
+ private:
+ ThreadState* state_;
+
+ DISALLOW_COPY_AND_ASSIGN(SafePointScope);
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/heap/self_keep_alive.h b/chromium/third_party/blink/renderer/platform/heap/self_keep_alive.h
new file mode 100644
index 00000000000..5e60054db89
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/self_keep_alive.h
@@ -0,0 +1,74 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_SELF_KEEP_ALIVE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_SELF_KEEP_ALIVE_H_
+
+#include "third_party/blink/renderer/platform/heap/persistent.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+namespace blink {
+
+// SelfKeepAlive<Object> is the idiom to use for objects that have to keep
+// themselves temporarily alive and cannot rely on there being some
+// external reference in that interval:
+//
+// class Opener {
+// public:
+// ...
+// void open()
+// {
+// // Retain a self-reference while in an open()ed state:
+// m_keepAlive = this;
+// ....
+// }
+//
+// void close()
+// {
+// // Clear self-reference that ensured we were kept alive while opened.
+// m_keepAlive.clear();
+// ....
+// }
+//
+// private:
+// ...
+// SelfKeepAlive m_keepAlive;
+// };
+//
+// The responsibility to call clear() in a timely fashion resides with the
+// implementation of the object.
+//
+//
+template <typename Self>
+class SelfKeepAlive final {
+ DISALLOW_NEW();
+
+ public:
+ SelfKeepAlive() = default;
+
+ explicit SelfKeepAlive(Self* self) { Assign(self); }
+
+ SelfKeepAlive& operator=(Self* self) {
+ Assign(self);
+ return *this;
+ }
+
+ void Clear() { keep_alive_.Clear(); }
+
+ explicit operator bool() const { return keep_alive_; }
+
+ private:
+ void Assign(Self* self) {
+ DCHECK(!keep_alive_ || keep_alive_.Get() == self);
+ keep_alive_ = self;
+ }
+
+ GC_PLUGIN_IGNORE("420515")
+ Persistent<Self> keep_alive_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_SELF_KEEP_ALIVE_H_
diff --git a/chromium/third_party/blink/renderer/platform/heap/sparse_heap_bitmap.cc b/chromium/third_party/blink/renderer/platform/heap/sparse_heap_bitmap.cc
new file mode 100644
index 00000000000..04793f31d5f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/sparse_heap_bitmap.cc
@@ -0,0 +1,111 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/heap/sparse_heap_bitmap.h"
+
+#include "third_party/blink/renderer/platform/heap/heap.h"
+
+namespace blink {
+
+// Return the subtree/bitmap that covers the
+// [address, address + size) range. Null if there is none.
+SparseHeapBitmap* SparseHeapBitmap::HasRange(Address address, size_t size) {
+ DCHECK(!(reinterpret_cast<uintptr_t>(address) & kPointerAlignmentMask));
+ SparseHeapBitmap* bitmap = this;
+ while (bitmap) {
+ // Interval starts after, |m_right| handles.
+ if (address > bitmap->end()) {
+ bitmap = bitmap->right_.get();
+ continue;
+ }
+ // Interval starts within, |bitmap| is included in the resulting range.
+ if (address >= bitmap->Base())
+ break;
+
+ Address right = address + size - 1;
+ // Interval starts before, but intersects with |bitmap|'s range.
+ if (right >= bitmap->Base())
+ break;
+
+ // Interval is before entirely, for |m_left| to handle.
+ bitmap = bitmap->left_.get();
+ }
+ return bitmap;
+}
+
+bool SparseHeapBitmap::IsSet(Address address) {
+ DCHECK(!(reinterpret_cast<uintptr_t>(address) & kPointerAlignmentMask));
+ SparseHeapBitmap* bitmap = this;
+ while (bitmap) {
+ if (address > bitmap->end()) {
+ bitmap = bitmap->right_.get();
+ continue;
+ }
+ if (address >= bitmap->Base()) {
+ if (bitmap->bitmap_) {
+ return bitmap->bitmap_->test((address - bitmap->Base()) >>
+ kPointerAlignmentInBits);
+ }
+ DCHECK(address == bitmap->Base());
+ DCHECK_EQ(bitmap->size(), 1u);
+ return true;
+ }
+ bitmap = bitmap->left_.get();
+ }
+ return false;
+}
+
+void SparseHeapBitmap::Add(Address address) {
+ DCHECK(!(reinterpret_cast<uintptr_t>(address) & kPointerAlignmentMask));
+ // |address| is beyond the maximum that this SparseHeapBitmap node can
+ // encompass.
+ if (address >= MaxEnd()) {
+ if (!right_) {
+ right_ = SparseHeapBitmap::Create(address);
+ return;
+ }
+ right_->Add(address);
+ return;
+ }
+ // Same on the other side.
+ if (address < MinStart()) {
+ if (!left_) {
+ left_ = SparseHeapBitmap::Create(address);
+ return;
+ }
+ left_->Add(address);
+ return;
+ }
+ if (address == Base())
+ return;
+ // |address| can be encompassed by |this| by expanding its size.
+ if (address > Base()) {
+ if (!bitmap_)
+ CreateBitmap();
+ bitmap_->set((address - Base()) >> kPointerAlignmentInBits);
+ return;
+ }
+ // Use |address| as the new base for this interval.
+ Address old_base = SwapBase(address);
+ CreateBitmap();
+ bitmap_->set((old_base - address) >> kPointerAlignmentInBits);
+}
+
+void SparseHeapBitmap::CreateBitmap() {
+ DCHECK(!bitmap_ && size() == 1);
+ bitmap_ = std::make_unique<std::bitset<kBitmapChunkSize>>();
+ size_ = kBitmapChunkRange;
+ bitmap_->set(0);
+}
+
+size_t SparseHeapBitmap::IntervalCount() const {
+ size_t count = 1;
+ if (left_)
+ count += left_->IntervalCount();
+ if (right_)
+ count += right_->IntervalCount();
+ return count;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/heap/sparse_heap_bitmap.h b/chromium/third_party/blink/renderer/platform/heap/sparse_heap_bitmap.h
new file mode 100644
index 00000000000..f0614a9854d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/sparse_heap_bitmap.h
@@ -0,0 +1,137 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_SPARSE_HEAP_BITMAP_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_SPARSE_HEAP_BITMAP_H_
+
+#include <bitset>
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/platform/heap/blink_gc.h"
+#include "third_party/blink/renderer/platform/heap/heap_page.h"
+#include "third_party/blink/renderer/platform/wtf/alignment.h"
+
+namespace blink {
+
+// A sparse bitmap of heap addresses where the (very few) addresses that are
+// set are likely to be in small clusters. The abstraction is tailored to
+// support heap compaction, assuming the following:
+//
+// - Addresses will be bitmap-marked from lower to higher addresses.
+// - Bitmap lookups are performed for each object that is compacted
+// and moved to some new location, supplying the (base, size)
+// pair of the object's heap allocation.
+// - If the sparse bitmap has any marked addresses in that range, it
+// returns a sub-bitmap that can be quickly iterated over to check which
+// addresses within the range are actually set.
+// - The bitmap is needed to support something that is very rarely done
+// by the current Blink codebase, which is to have nested collection
+// part objects. Consequently, it is safe to assume sparseness.
+//
+// Support the above by having a sparse bitmap organized as a binary
+// tree with nodes covering fixed size ranges via a simple bitmap/set.
+// That is, each SparseHeapBitmap node will contain a bitmap/set for
+// some fixed size range, along with pointers to SparseHeapBitmaps
+// for addresses on each side its range.
+//
+// This bitmap tree isn't kept balanced across the Address additions
+// made.
+//
+class PLATFORM_EXPORT SparseHeapBitmap {
+ public:
+ static std::unique_ptr<SparseHeapBitmap> Create(Address base) {
+ return base::WrapUnique(new SparseHeapBitmap(base));
+ }
+
+ ~SparseHeapBitmap() = default;
+
+ // Return the sparse bitmap subtree that at least covers the
+ // [address, address + size) range, or nullptr if none.
+ //
+ // The returned SparseHeapBitmap can be used to quickly lookup what
+ // addresses in that range are set or not; see |isSet()|. Its
+ // |isSet()| behavior outside that range is not defined.
+ SparseHeapBitmap* HasRange(Address, size_t);
+
+ // True iff |address| is set for this SparseHeapBitmap tree.
+ bool IsSet(Address);
+
+ // Mark |address| as present/set.
+ void Add(Address);
+
+ // The assumed minimum alignment of the pointers being added. Cannot
+ // exceed |log2(allocationGranularity)|; having it be equal to
+ // the platform pointer alignment is what's wanted.
+ static const int kPointerAlignmentInBits = WTF_ALIGN_OF(void*) == 8 ? 3 : 2;
+ static const size_t kPointerAlignmentMask =
+ (0x1u << kPointerAlignmentInBits) - 1;
+
+ // Represent ranges in 0x100 bitset chunks; bit I is set iff Address
+ // |m_base + I * (0x1 << s_pointerAlignmentInBits)| has been added to the
+ // |SparseHeapBitmap|.
+ static const size_t kBitmapChunkSize = 0x100;
+
+ // A SparseHeapBitmap either contains a single Address or a bitmap
+ // recording the mapping for [m_base, m_base + s_bitmapChunkRange)
+ static const size_t kBitmapChunkRange = kBitmapChunkSize
+ << kPointerAlignmentInBits;
+
+ // Return the number of nodes; for debug stats.
+ size_t IntervalCount() const;
+
+ private:
+ explicit SparseHeapBitmap(Address base) : base_(base), size_(1) {
+ DCHECK(!(reinterpret_cast<uintptr_t>(base_) & kPointerAlignmentMask));
+ static_assert(kPointerAlignmentMask <= kAllocationMask,
+ "address shift exceeds heap pointer alignment");
+ // For now, only recognize 8 and 4.
+ static_assert(WTF_ALIGN_OF(void*) == 8 || WTF_ALIGN_OF(void*) == 4,
+ "unsupported pointer alignment");
+ }
+
+ Address Base() const { return base_; }
+ size_t size() const { return size_; }
+ Address end() const { return Base() + (size_ - 1); }
+
+ Address MaxEnd() const { return Base() + kBitmapChunkRange; }
+
+ Address MinStart() const {
+ // If this bitmap node represents the sparse [m_base, s_bitmapChunkRange)
+ // range, do not allow it to be "left extended" as that would entail
+ // having to shift down the contents of the std::bitset somehow.
+ //
+ // This shouldn't be a real problem as any clusters of set addresses
+ // will be marked while iterating from lower to higher addresses, hence
+ // "left extension" are unlikely to be common.
+ if (bitmap_)
+ return Base();
+ return (base_ > reinterpret_cast<Address>(kBitmapChunkRange))
+ ? (Base() - kBitmapChunkRange + 1)
+ : nullptr;
+ }
+
+ Address SwapBase(Address address) {
+ DCHECK(!(reinterpret_cast<uintptr_t>(address) & kPointerAlignmentMask));
+ Address old_base = base_;
+ base_ = address;
+ return old_base;
+ }
+
+ void CreateBitmap();
+
+ Address base_;
+ // Either 1 or |s_bitmapChunkRange|.
+ size_t size_;
+
+ // If non-null, contains a bitmap for addresses within [m_base, m_size)
+ std::unique_ptr<std::bitset<kBitmapChunkSize>> bitmap_;
+
+ std::unique_ptr<SparseHeapBitmap> left_;
+ std::unique_ptr<SparseHeapBitmap> right_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_SPARSE_HEAP_BITMAP_H_
diff --git a/chromium/third_party/blink/renderer/platform/heap/stack_frame_depth.cc b/chromium/third_party/blink/renderer/platform/heap/stack_frame_depth.cc
new file mode 100644
index 00000000000..e6237ed57cd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/stack_frame_depth.cc
@@ -0,0 +1,65 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/heap/stack_frame_depth.h"
+
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/wtf/stack_util.h"
+
+#if defined(OS_WIN)
+#include <stddef.h>
+#include <windows.h>
+#include <winnt.h>
+#elif defined(__GLIBC__)
+extern "C" void* __libc_stack_end; // NOLINT
+#endif
+
+namespace blink {
+
+static const char* g_avoid_optimization = nullptr;
+
+// NEVER_INLINE ensures that |dummy| array on configureLimit() is not optimized
+// away, and the stack frame base register is adjusted |kSafeStackFrameSize|.
+NEVER_INLINE static uintptr_t CurrentStackFrameBaseOnCallee(const char* dummy) {
+ g_avoid_optimization = dummy;
+ return StackFrameDepth::CurrentStackFrame();
+}
+
+uintptr_t StackFrameDepth::GetFallbackStackLimit() {
+ // Allocate an |kSafeStackFrameSize|-sized object on stack and query
+ // stack frame base after it.
+ char dummy[kSafeStackFrameSize];
+
+ // Check that the stack frame can be used.
+ dummy[sizeof(dummy) - 1] = 0;
+ return CurrentStackFrameBaseOnCallee(dummy);
+}
+
+void StackFrameDepth::EnableStackLimit() {
+ // All supported platforms will currently return a non-zero estimate,
+ // except if ASan is enabled.
+ size_t stack_size = WTF::GetUnderestimatedStackSize();
+ if (!stack_size) {
+ stack_frame_limit_ = GetFallbackStackLimit();
+ return;
+ }
+
+ // Adjust the following when running out of stack space in between turns of
+ // checking |IsSafeToRecurse()|. The required room size depends on the actions
+ // performed between turns and how well compiler optimizations apply.
+ static const int kStackRoomSize = 4096;
+
+ Address stack_base = reinterpret_cast<Address>(WTF::GetStackStart());
+ CHECK_GT(stack_size, static_cast<const size_t>(kStackRoomSize));
+ size_t stack_room = stack_size - kStackRoomSize;
+ CHECK_GT(stack_base, reinterpret_cast<Address>(stack_room));
+ stack_frame_limit_ = reinterpret_cast<uintptr_t>(stack_base - stack_room);
+
+ // If current stack use is already exceeding estimated limit, mark as
+ // disabled.
+ if (!IsSafeToRecurse())
+ DisableStackLimit();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/heap/stack_frame_depth.h b/chromium/third_party/blink/renderer/platform/heap/stack_frame_depth.h
new file mode 100644
index 00000000000..853db1148c3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/stack_frame_depth.h
@@ -0,0 +1,107 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_STACK_FRAME_DEPTH_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_STACK_FRAME_DEPTH_H_
+
+#include <stdint.h>
+#include <cstddef>
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+namespace blink {
+
+// StackFrameDepth keeps track of current call stack frame depth.
+// It is specifically used to control stack usage while tracing
+// the object graph during a GC.
+//
+// Use isSafeToRecurse() to determine if it is safe to consume
+// more stack by invoking another recursive call.
+class PLATFORM_EXPORT StackFrameDepth final {
+ DISALLOW_NEW();
+
+ public:
+ StackFrameDepth() : stack_frame_limit_(kMinimumStackLimit) {}
+ bool IsSafeToRecurse() {
+ // Asssume that the stack grows towards lower addresses, which
+ // all the ABIs currently supported do.
+ //
+ // A unit test checks that the assumption holds for a target
+ // (HeapTest.StackGrowthDirection.)
+ return CurrentStackFrame() > stack_frame_limit_;
+ }
+
+ void EnableStackLimit();
+ void DisableStackLimit() { stack_frame_limit_ = kMinimumStackLimit; }
+
+ bool IsEnabled() { return stack_frame_limit_ != kMinimumStackLimit; }
+ bool IsAcceptableStackUse() {
+#if defined(ADDRESS_SANITIZER)
+ // ASan adds extra stack usage leading to too noisy asserts.
+ return true;
+#else
+ return !IsEnabled() || IsSafeToRecurse();
+#endif
+ }
+
+#if defined(COMPILER_MSVC)
+// Ignore C4172: returning address of local variable or temporary: dummy. This
+// warning suppression has to go outside of the function to take effect.
+#pragma warning(push)
+#pragma warning(disable : 4172)
+#endif
+ static uintptr_t CurrentStackFrame(const char* dummy = nullptr) {
+#if defined(COMPILER_GCC)
+ return reinterpret_cast<uintptr_t>(__builtin_frame_address(0));
+#elif defined(COMPILER_MSVC)
+ return reinterpret_cast<uintptr_t>(&dummy) - sizeof(void*);
+#else
+#error "Stack frame pointer estimation not supported on this platform."
+ return 0;
+#endif
+ }
+#if defined(COMPILER_MSVC)
+#pragma warning(pop)
+#endif
+
+ private:
+ // The maximum depth of eager, unrolled trace() calls that is
+ // considered safe and allowed for targets with an unknown
+ // thread stack size.
+ static const int kSafeStackFrameSize = 32 * 1024;
+
+ // The stack pointer is assumed to grow towards lower addresses;
+ // |kMinimumStackLimit| then being the limit that a stack
+ // pointer will always exceed.
+ static const uintptr_t kMinimumStackLimit = ~uintptr_t{0};
+
+ static uintptr_t GetFallbackStackLimit();
+
+ // The (pointer-valued) stack limit.
+ uintptr_t stack_frame_limit_;
+};
+
+class StackFrameDepthScope {
+ STACK_ALLOCATED();
+ DISALLOW_COPY_AND_ASSIGN(StackFrameDepthScope);
+
+ public:
+ explicit StackFrameDepthScope(StackFrameDepth* depth) : depth_(depth) {
+ depth_->EnableStackLimit();
+ // Enabled unless under stack pressure.
+ DCHECK(depth_->IsSafeToRecurse() || !depth_->IsEnabled());
+ }
+
+ ~StackFrameDepthScope() { depth_->DisableStackLimit(); }
+
+ private:
+ StackFrameDepth* depth_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_STACK_FRAME_DEPTH_H_
diff --git a/chromium/third_party/blink/renderer/platform/heap/thread_state.cc b/chromium/third_party/blink/renderer/platform/heap/thread_state.cc
new file mode 100644
index 00000000000..7a7edae6823
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/thread_state.cc
@@ -0,0 +1,1532 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/renderer/platform/heap/thread_state.h"
+
+#include <v8.h>
+
+#include <algorithm>
+#include <iomanip>
+#include <limits>
+#include <memory>
+
+#include "base/atomicops.h"
+#include "base/location.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "build/build_config.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_thread.h"
+#include "third_party/blink/renderer/platform/bindings/runtime_call_stats.h"
+#include "third_party/blink/renderer/platform/heap/blink_gc_memory_dump_provider.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/heap/heap_buildflags.h"
+#include "third_party/blink/renderer/platform/heap/heap_compact.h"
+#include "third_party/blink/renderer/platform/heap/marking_visitor.h"
+#include "third_party/blink/renderer/platform/heap/page_pool.h"
+#include "third_party/blink/renderer/platform/heap/safe_point.h"
+#include "third_party/blink/renderer/platform/heap/visitor.h"
+#include "third_party/blink/renderer/platform/histogram.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/web_memory_allocator_dump.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/web_process_memory_dump.h"
+#include "third_party/blink/renderer/platform/scheduler/child/web_scheduler.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
+#include "third_party/blink/renderer/platform/wtf/stack_util.h"
+#include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+#if defined(OS_WIN)
+#include <stddef.h>
+#include <windows.h>
+#include <winnt.h>
+#endif
+
+#if defined(MEMORY_SANITIZER)
+#include <sanitizer/msan_interface.h>
+#endif
+
+#if defined(OS_FREEBSD)
+#include <pthread_np.h>
+#endif
+
+namespace blink {
+
+WTF::ThreadSpecific<ThreadState*>* ThreadState::thread_specific_ = nullptr;
+uint8_t ThreadState::main_thread_state_storage_[sizeof(ThreadState)];
+
+const size_t kDefaultAllocatedObjectSizeThreshold = 100 * 1024;
+
+// Duration of one incremental marking step. Should be short enough that it
+// doesn't cause jank even though it is scheduled as a normal task.
+const double kIncrementalMarkingStepDurationInSeconds = 0.001;
+
+namespace {
+
+const char* GcReasonString(BlinkGC::GCReason reason) {
+ switch (reason) {
+ case BlinkGC::kIdleGC:
+ return "IdleGC";
+ case BlinkGC::kPreciseGC:
+ return "PreciseGC";
+ case BlinkGC::kConservativeGC:
+ return "ConservativeGC";
+ case BlinkGC::kForcedGC:
+ return "ForcedGC";
+ case BlinkGC::kMemoryPressureGC:
+ return "MemoryPressureGC";
+ case BlinkGC::kPageNavigationGC:
+ return "PageNavigationGC";
+ case BlinkGC::kThreadTerminationGC:
+ return "ThreadTerminationGC";
+ }
+ return "<Unknown>";
+}
+
+const char* MarkingTypeString(BlinkGC::MarkingType type) {
+ switch (type) {
+ case BlinkGC::kAtomicMarking:
+ return "AtomicMarking";
+ case BlinkGC::kIncrementalMarking:
+ return "IncrementalMarking";
+ case BlinkGC::kTakeSnapshot:
+ return "TakeSnapshot";
+ }
+ return "<Unknown>";
+}
+
+const char* SweepingTypeString(BlinkGC::SweepingType type) {
+ switch (type) {
+ case BlinkGC::kLazySweeping:
+ return "LazySweeping";
+ case BlinkGC::kEagerSweeping:
+ return "EagerSweeping";
+ }
+ return "<Unknown>";
+}
+
+const char* StackStateString(BlinkGC::StackState state) {
+ switch (state) {
+ case BlinkGC::kNoHeapPointersOnStack:
+ return "NoHeapPointersOnStack";
+ case BlinkGC::kHeapPointersOnStack:
+ return "HeapPointersOnStack";
+ }
+ return "<Unknown>";
+}
+
+} // namespace
+
+ThreadState::ThreadState()
+ : thread_(CurrentThread()),
+ persistent_region_(std::make_unique<PersistentRegion>()),
+ weak_persistent_region_(std::make_unique<PersistentRegion>()),
+ start_of_stack_(reinterpret_cast<intptr_t*>(WTF::GetStackStart())),
+ end_of_stack_(reinterpret_cast<intptr_t*>(WTF::GetStackStart())),
+ safe_point_scope_marker_(nullptr),
+ sweep_forbidden_(false),
+ no_allocation_count_(0),
+ gc_forbidden_count_(0),
+ mixins_being_constructed_count_(0),
+ accumulated_sweeping_time_(0),
+ object_resurrection_forbidden_(false),
+ in_atomic_pause_(false),
+ gc_mixin_marker_(nullptr),
+ gc_state_(kNoGCScheduled),
+ gc_phase_(GCPhase::kNone),
+ isolate_(nullptr),
+ trace_dom_wrappers_(nullptr),
+ invalidate_dead_objects_in_wrappers_marking_deque_(nullptr),
+ perform_cleanup_(nullptr),
+ wrapper_tracing_in_progress_(false),
+ incremental_marking_(false),
+#if defined(ADDRESS_SANITIZER)
+ asan_fake_stack_(__asan_get_current_fake_stack()),
+#endif
+#if defined(LEAK_SANITIZER)
+ disabled_static_persistent_registration_(0),
+#endif
+ reported_memory_to_v8_(0) {
+ DCHECK(CheckThread());
+ DCHECK(!**thread_specific_);
+ **thread_specific_ = this;
+
+ heap_ = std::make_unique<ThreadHeap>(this);
+}
+
+ThreadState::~ThreadState() {
+ DCHECK(CheckThread());
+ if (IsMainThread())
+ DCHECK_EQ(Heap().HeapStats().AllocatedSpace(), 0u);
+ CHECK(GcState() == ThreadState::kNoGCScheduled);
+
+ **thread_specific_ = nullptr;
+}
+
+void ThreadState::AttachMainThread() {
+ thread_specific_ = new WTF::ThreadSpecific<ThreadState*>();
+ new (main_thread_state_storage_) ThreadState();
+}
+
+void ThreadState::AttachCurrentThread() {
+ new ThreadState();
+}
+
+void ThreadState::DetachCurrentThread() {
+ ThreadState* state = Current();
+ DCHECK(!state->IsMainThread());
+ state->RunTerminationGC();
+ delete state;
+}
+
+void ThreadState::RunTerminationGC() {
+ DCHECK(!IsMainThread());
+ DCHECK(CheckThread());
+
+ // Finish sweeping.
+ CompleteSweep();
+
+ ReleaseStaticPersistentNodes();
+
+ // PrepareForThreadStateTermination removes strong references so no need to
+ // call it on CrossThreadWeakPersistentRegion.
+ ProcessHeap::GetCrossThreadPersistentRegion()
+ .PrepareForThreadStateTermination(this);
+
+ // Do thread local GC's as long as the count of thread local Persistents
+ // changes and is above zero.
+ int old_count = -1;
+ int current_count = GetPersistentRegion()->NumberOfPersistents();
+ DCHECK_GE(current_count, 0);
+ while (current_count != old_count) {
+ CollectGarbage(BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
+ BlinkGC::kEagerSweeping, BlinkGC::kThreadTerminationGC);
+ // Release the thread-local static persistents that were
+ // instantiated while running the termination GC.
+ ReleaseStaticPersistentNodes();
+ old_count = current_count;
+ current_count = GetPersistentRegion()->NumberOfPersistents();
+ }
+ // We should not have any persistents left when getting to this point,
+ // if we have it is probably a bug so adding a debug ASSERT to catch this.
+ DCHECK(!current_count);
+ // All of pre-finalizers should be consumed.
+ DCHECK(ordered_pre_finalizers_.IsEmpty());
+ CHECK_EQ(GcState(), kNoGCScheduled);
+
+ Heap().RemoveAllPages();
+}
+
+NO_SANITIZE_ADDRESS
+void ThreadState::VisitAsanFakeStackForPointer(MarkingVisitor* visitor,
+ Address ptr) {
+#if defined(ADDRESS_SANITIZER)
+ Address* start = reinterpret_cast<Address*>(start_of_stack_);
+ Address* end = reinterpret_cast<Address*>(end_of_stack_);
+ Address* fake_frame_start = nullptr;
+ Address* fake_frame_end = nullptr;
+ Address* maybe_fake_frame = reinterpret_cast<Address*>(ptr);
+ Address* real_frame_for_fake_frame = reinterpret_cast<Address*>(
+ __asan_addr_is_in_fake_stack(asan_fake_stack_, maybe_fake_frame,
+ reinterpret_cast<void**>(&fake_frame_start),
+ reinterpret_cast<void**>(&fake_frame_end)));
+ if (real_frame_for_fake_frame) {
+ // This is a fake frame from the asan fake stack.
+ if (real_frame_for_fake_frame > end && start > real_frame_for_fake_frame) {
+ // The real stack address for the asan fake frame is
+ // within the stack range that we need to scan so we need
+ // to visit the values in the fake frame.
+ for (Address* p = fake_frame_start; p < fake_frame_end; ++p)
+ heap_->CheckAndMarkPointer(visitor, *p);
+ }
+ }
+#endif
+}
+
+// Stack scanning may overrun the bounds of local objects and/or race with
+// other threads that use this stack.
+NO_SANITIZE_ADDRESS
+NO_SANITIZE_THREAD
+void ThreadState::VisitStack(MarkingVisitor* visitor) {
+ if (stack_state_ == BlinkGC::kNoHeapPointersOnStack)
+ return;
+
+ Address* start = reinterpret_cast<Address*>(start_of_stack_);
+ // If there is a safepoint scope marker we should stop the stack
+ // scanning there to not touch active parts of the stack. Anything
+ // interesting beyond that point is in the safepoint stack copy.
+ // If there is no scope marker the thread is blocked and we should
+ // scan all the way to the recorded end stack pointer.
+ Address* end = reinterpret_cast<Address*>(end_of_stack_);
+ Address* safe_point_scope_marker =
+ reinterpret_cast<Address*>(safe_point_scope_marker_);
+ Address* current = safe_point_scope_marker ? safe_point_scope_marker : end;
+
+ // Ensure that current is aligned by address size otherwise the loop below
+ // will read past start address.
+ current = reinterpret_cast<Address*>(reinterpret_cast<intptr_t>(current) &
+ ~(sizeof(Address) - 1));
+
+ for (; current < start; ++current) {
+ Address ptr = *current;
+#if defined(MEMORY_SANITIZER)
+ // |ptr| may be uninitialized by design. Mark it as initialized to keep
+ // MSan from complaining.
+ // Note: it may be tempting to get rid of |ptr| and simply use |current|
+ // here, but that would be incorrect. We intentionally use a local
+ // variable because we don't want to unpoison the original stack.
+ __msan_unpoison(&ptr, sizeof(ptr));
+#endif
+ heap_->CheckAndMarkPointer(visitor, ptr);
+ VisitAsanFakeStackForPointer(visitor, ptr);
+ }
+
+ for (Address ptr : safe_point_stack_copy_) {
+#if defined(MEMORY_SANITIZER)
+ // See the comment above.
+ __msan_unpoison(&ptr, sizeof(ptr));
+#endif
+ heap_->CheckAndMarkPointer(visitor, ptr);
+ VisitAsanFakeStackForPointer(visitor, ptr);
+ }
+}
+
+void ThreadState::VisitPersistents(Visitor* visitor) {
+ ProcessHeap::GetCrossThreadPersistentRegion().TracePersistentNodes(visitor);
+ persistent_region_->TracePersistentNodes(visitor);
+ if (trace_dom_wrappers_) {
+ TRACE_EVENT0("blink_gc", "V8GCController::traceDOMWrappers");
+ trace_dom_wrappers_(isolate_, visitor);
+ }
+}
+
+void ThreadState::VisitWeakPersistents(Visitor* visitor) {
+ ProcessHeap::GetCrossThreadWeakPersistentRegion().TracePersistentNodes(
+ visitor);
+ weak_persistent_region_->TracePersistentNodes(visitor);
+}
+
+ThreadState::GCSnapshotInfo::GCSnapshotInfo(size_t num_object_types)
+ : live_count(Vector<int>(num_object_types)),
+ dead_count(Vector<int>(num_object_types)),
+ live_size(Vector<size_t>(num_object_types)),
+ dead_size(Vector<size_t>(num_object_types)) {}
+
+size_t ThreadState::TotalMemorySize() {
+ return heap_->HeapStats().AllocatedObjectSize() +
+ heap_->HeapStats().MarkedObjectSize() +
+ WTF::Partitions::TotalSizeOfCommittedPages();
+}
+
+size_t ThreadState::EstimatedLiveSize(size_t estimation_base_size,
+ size_t size_at_last_gc) {
+ if (heap_->HeapStats().WrapperCountAtLastGC() == 0)
+ return estimation_base_size;
+
+ // (estimated size) = (estimation base size) - (heap size at the last GC) /
+ // (# of persistent handles at the last GC) *
+ // (# of persistent handles collected since the last GC);
+ size_t size_retained_by_collected_persistents = static_cast<size_t>(
+ 1.0 * size_at_last_gc / heap_->HeapStats().WrapperCountAtLastGC() *
+ heap_->HeapStats().CollectedWrapperCount());
+ if (estimation_base_size < size_retained_by_collected_persistents)
+ return 0;
+ return estimation_base_size - size_retained_by_collected_persistents;
+}
+
+double ThreadState::HeapGrowingRate() {
+ size_t current_size = heap_->HeapStats().AllocatedObjectSize() +
+ heap_->HeapStats().MarkedObjectSize();
+ size_t estimated_size = EstimatedLiveSize(
+ heap_->HeapStats().MarkedObjectSizeAtLastCompleteSweep(),
+ heap_->HeapStats().MarkedObjectSizeAtLastCompleteSweep());
+
+ // If the estimatedSize is 0, we set a high growing rate to trigger a GC.
+ double growing_rate =
+ estimated_size > 0 ? 1.0 * current_size / estimated_size : 100;
+ TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink_gc"),
+ "ThreadState::heapEstimatedSizeKB",
+ std::min(estimated_size / 1024, static_cast<size_t>(INT_MAX)));
+ TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink_gc"),
+ "ThreadState::heapGrowingRate",
+ static_cast<int>(100 * growing_rate));
+ return growing_rate;
+}
+
+double ThreadState::PartitionAllocGrowingRate() {
+ size_t current_size = WTF::Partitions::TotalSizeOfCommittedPages();
+ size_t estimated_size = EstimatedLiveSize(
+ current_size, heap_->HeapStats().PartitionAllocSizeAtLastGC());
+
+ // If the estimatedSize is 0, we set a high growing rate to trigger a GC.
+ double growing_rate =
+ estimated_size > 0 ? 1.0 * current_size / estimated_size : 100;
+ TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink_gc"),
+ "ThreadState::partitionAllocEstimatedSizeKB",
+ std::min(estimated_size / 1024, static_cast<size_t>(INT_MAX)));
+ TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink_gc"),
+ "ThreadState::partitionAllocGrowingRate",
+ static_cast<int>(100 * growing_rate));
+ return growing_rate;
+}
+
+// TODO(haraken): We should improve the GC heuristics. The heuristics affect
+// performance significantly.
+bool ThreadState::JudgeGCThreshold(size_t allocated_object_size_threshold,
+ size_t total_memory_size_threshold,
+ double heap_growing_rate_threshold) {
+ // If the allocated object size or the total memory size is small, don't
+ // trigger a GC.
+ if (heap_->HeapStats().AllocatedObjectSize() <
+ allocated_object_size_threshold ||
+ TotalMemorySize() < total_memory_size_threshold)
+ return false;
+
+ VLOG(2) << "[state:" << this << "] JudgeGCThreshold:"
+ << " heapGrowingRate=" << std::setprecision(1) << HeapGrowingRate()
+ << " partitionAllocGrowingRate=" << std::setprecision(1)
+ << PartitionAllocGrowingRate();
+ // If the growing rate of Oilpan's heap or PartitionAlloc is high enough,
+ // trigger a GC.
+ return HeapGrowingRate() >= heap_growing_rate_threshold ||
+ PartitionAllocGrowingRate() >= heap_growing_rate_threshold;
+}
+
+bool ThreadState::ShouldScheduleIncrementalMarking() const {
+#if BUILDFLAG(BLINK_HEAP_INCREMENTAL_MARKING)
+ // TODO(mlippautz): For now immediately schedule incremental marking if
+ // the runtime flag is provided, basically exercising a stress test.
+ return GcState() == kNoGCScheduled &&
+ RuntimeEnabledFeatures::HeapIncrementalMarkingEnabled();
+#else
+ return false;
+#endif // BUILDFLAG(BLINK_HEAP_INCREMENTAL_MARKING)
+}
+
+bool ThreadState::ShouldScheduleIdleGC() {
+ if (GcState() != kNoGCScheduled)
+ return false;
+ return JudgeGCThreshold(kDefaultAllocatedObjectSizeThreshold, 1024 * 1024,
+ 1.5);
+}
+
+bool ThreadState::ShouldScheduleV8FollowupGC() {
+ return JudgeGCThreshold(kDefaultAllocatedObjectSizeThreshold,
+ 32 * 1024 * 1024, 1.5);
+}
+
+bool ThreadState::ShouldSchedulePageNavigationGC(
+ float estimated_removal_ratio) {
+ // If estimatedRemovalRatio is low we should let IdleGC handle this.
+ if (estimated_removal_ratio < 0.01)
+ return false;
+ return JudgeGCThreshold(kDefaultAllocatedObjectSizeThreshold,
+ 32 * 1024 * 1024,
+ 1.5 * (1 - estimated_removal_ratio));
+}
+
+bool ThreadState::ShouldForceConservativeGC() {
+ // TODO(haraken): 400% is too large. Lower the heap growing factor.
+ return JudgeGCThreshold(kDefaultAllocatedObjectSizeThreshold,
+ 32 * 1024 * 1024, 5.0);
+}
+
+// If we're consuming too much memory, trigger a conservative GC
+// aggressively. This is a safe guard to avoid OOM.
+bool ThreadState::ShouldForceMemoryPressureGC() {
+ if (TotalMemorySize() < 300 * 1024 * 1024)
+ return false;
+ return JudgeGCThreshold(0, 0, 1.5);
+}
+
+void ThreadState::ScheduleV8FollowupGCIfNeeded(BlinkGC::V8GCType gc_type) {
+ VLOG(2) << "[state:" << this << "] ScheduleV8FollowupGCIfNeeded: v8_gc_type="
+ << ((gc_type == BlinkGC::kV8MajorGC) ? "MajorGC" : "MinorGC");
+ DCHECK(CheckThread());
+ ThreadHeap::ReportMemoryUsageForTracing();
+
+ if (IsGCForbidden())
+ return;
+
+ // This completeSweep() will do nothing in common cases since we've
+ // called completeSweep() before V8 starts minor/major GCs.
+ CompleteSweep();
+ DCHECK(!IsSweepingInProgress());
+ DCHECK(!SweepForbidden());
+
+ if ((gc_type == BlinkGC::kV8MajorGC && ShouldForceMemoryPressureGC()) ||
+ ShouldScheduleV8FollowupGC()) {
+ VLOG(2) << "[state:" << this << "] "
+ << "ScheduleV8FollowupGCIfNeeded: Scheduled precise GC";
+ SchedulePreciseGC();
+ return;
+ }
+ if (gc_type == BlinkGC::kV8MajorGC && ShouldScheduleIdleGC()) {
+ VLOG(2) << "[state:" << this << "] "
+ << "ScheduleV8FollowupGCIfNeeded: Scheduled idle GC";
+ ScheduleIdleGC();
+ return;
+ }
+}
+
+void ThreadState::WillStartV8GC(BlinkGC::V8GCType gc_type) {
+ // Finish Oilpan's complete sweeping before running a V8 major GC.
+ // This will let the GC collect more V8 objects.
+ //
+ // TODO(haraken): It's a bit too late for a major GC to schedule
+ // completeSweep() here, because gcPrologue for a major GC is called
+ // not at the point where the major GC started but at the point where
+ // the major GC requests object grouping.
+ if (gc_type == BlinkGC::kV8MajorGC)
+ CompleteSweep();
+}
+
+void ThreadState::SchedulePageNavigationGCIfNeeded(
+ float estimated_removal_ratio) {
+ VLOG(2) << "[state:" << this << "] SchedulePageNavigationGCIfNeeded: "
+ << "estimatedRemovalRatio=" << std::setprecision(2)
+ << estimated_removal_ratio;
+ DCHECK(CheckThread());
+ ThreadHeap::ReportMemoryUsageForTracing();
+
+ if (IsGCForbidden())
+ return;
+
+ // Finish on-going lazy sweeping.
+ // TODO(haraken): It might not make sense to force completeSweep() for all
+ // page navigations.
+ CompleteSweep();
+ DCHECK(!IsSweepingInProgress());
+ DCHECK(!SweepForbidden());
+
+ if (ShouldForceMemoryPressureGC()) {
+ VLOG(2) << "[state:" << this << "] "
+ << "SchedulePageNavigationGCIfNeeded: Scheduled memory pressure GC";
+ CollectGarbage(BlinkGC::kHeapPointersOnStack, BlinkGC::kAtomicMarking,
+ BlinkGC::kLazySweeping, BlinkGC::kMemoryPressureGC);
+ return;
+ }
+ if (ShouldSchedulePageNavigationGC(estimated_removal_ratio)) {
+ VLOG(2) << "[state:" << this << "] "
+ << "SchedulePageNavigationGCIfNeeded: Scheduled page navigation GC";
+ SchedulePageNavigationGC();
+ }
+}
+
+void ThreadState::SchedulePageNavigationGC() {
+ DCHECK(CheckThread());
+ DCHECK(!IsSweepingInProgress());
+ SetGCState(kPageNavigationGCScheduled);
+}
+
+void ThreadState::ScheduleGCIfNeeded() {
+ VLOG(2) << "[state:" << this << "] ScheduleGCIfNeeded";
+ DCHECK(CheckThread());
+ ThreadHeap::ReportMemoryUsageForTracing();
+
+ // Allocation is allowed during sweeping, but those allocations should not
+ // trigger nested GCs.
+ if (IsGCForbidden() || SweepForbidden())
+ return;
+
+ ReportMemoryToV8();
+
+ if (ShouldForceMemoryPressureGC()) {
+ CompleteSweep();
+ if (ShouldForceMemoryPressureGC()) {
+ VLOG(2) << "[state:" << this << "] "
+ << "ScheduleGCIfNeeded: Scheduled memory pressure GC";
+ CollectGarbage(BlinkGC::kHeapPointersOnStack, BlinkGC::kAtomicMarking,
+ BlinkGC::kLazySweeping, BlinkGC::kMemoryPressureGC);
+ return;
+ }
+ }
+
+ if (ShouldForceConservativeGC()) {
+ CompleteSweep();
+ if (ShouldForceConservativeGC()) {
+ VLOG(2) << "[state:" << this << "] "
+ << "ScheduleGCIfNeeded: Scheduled conservative GC";
+ CollectGarbage(BlinkGC::kHeapPointersOnStack, BlinkGC::kAtomicMarking,
+ BlinkGC::kLazySweeping, BlinkGC::kConservativeGC);
+ return;
+ }
+ }
+
+ if (ShouldScheduleIdleGC()) {
+ VLOG(2) << "[state:" << this << "] "
+ << "ScheduleGCIfNeeded: Scheduled idle GC";
+ ScheduleIdleGC();
+ return;
+ }
+
+ if (ShouldScheduleIncrementalMarking()) {
+ VLOG(2) << "[state:" << this << "] "
+ << "ScheduleGCIfNeeded: Scheduled incremental marking";
+ ScheduleIncrementalMarkingStart();
+ }
+}
+
+ThreadState* ThreadState::FromObject(const void* object) {
+ DCHECK(object);
+ BasePage* page = PageFromObject(object);
+ DCHECK(page);
+ DCHECK(page->Arena());
+ return page->Arena()->GetThreadState();
+}
+
+void ThreadState::PerformIdleGC(double deadline_seconds) {
+ DCHECK(CheckThread());
+ DCHECK(Platform::Current()->CurrentThread()->Scheduler());
+
+ if (GcState() != kIdleGCScheduled)
+ return;
+
+ if (IsGCForbidden()) {
+ // If GC is forbidden at this point, try again.
+ ScheduleIdleGC();
+ return;
+ }
+
+ double idle_delta_in_seconds = deadline_seconds - CurrentTimeTicksInSeconds();
+ if (idle_delta_in_seconds <= heap_->HeapStats().EstimatedMarkingTime() &&
+ !Platform::Current()
+ ->CurrentThread()
+ ->Scheduler()
+ ->CanExceedIdleDeadlineIfRequired()) {
+ // If marking is estimated to take longer than the deadline and we can't
+ // exceed the deadline, then reschedule for the next idle period.
+ ScheduleIdleGC();
+ return;
+ }
+
+ TRACE_EVENT2("blink_gc", "ThreadState::performIdleGC", "idleDeltaInSeconds",
+ idle_delta_in_seconds, "estimatedMarkingTime",
+ heap_->HeapStats().EstimatedMarkingTime());
+ CollectGarbage(BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
+ BlinkGC::kLazySweeping, BlinkGC::kIdleGC);
+}
+
+void ThreadState::PerformIdleLazySweep(double deadline_seconds) {
+ DCHECK(CheckThread());
+
+ // If we are not in a sweeping phase, there is nothing to do here.
+ if (!IsSweepingInProgress())
+ return;
+
+ // This check is here to prevent performIdleLazySweep() from being called
+ // recursively. I'm not sure if it can happen but it would be safer to have
+ // the check just in case.
+ if (SweepForbidden())
+ return;
+
+ RUNTIME_CALL_TIMER_SCOPE_IF_ISOLATE_EXISTS(
+ GetIsolate(), RuntimeCallStats::CounterId::kPerformIdleLazySweep);
+
+ TRACE_EVENT1("blink_gc,devtools.timeline",
+ "ThreadState::performIdleLazySweep", "idleDeltaInSeconds",
+ deadline_seconds - CurrentTimeTicksInSeconds());
+
+ AtomicPauseScope atomic_pause_scope(this);
+ SweepForbiddenScope scope(this);
+
+ double start_time = WTF::CurrentTimeTicksInMilliseconds();
+ bool sweep_completed = Heap().AdvanceLazySweep(deadline_seconds);
+ // We couldn't finish the sweeping within the deadline.
+ // We request another idle task for the remaining sweeping.
+ if (!sweep_completed)
+ ScheduleIdleLazySweep();
+ AccumulateSweepingTime(WTF::CurrentTimeTicksInMilliseconds() - start_time);
+
+ if (sweep_completed)
+ PostSweep();
+}
+
+void ThreadState::ScheduleIncrementalMarkingStart() {
+ // TODO(mlippautz): Incorporate incremental sweeping into incremental steps.
+ if (IsSweepingInProgress())
+ CompleteSweep();
+
+ SetGCState(kIncrementalMarkingStartScheduled);
+}
+
+void ThreadState::ScheduleIncrementalMarkingStep() {
+ CHECK(!IsSweepingInProgress());
+
+ SetGCState(kIncrementalMarkingStepScheduled);
+}
+
+void ThreadState::ScheduleIncrementalMarkingFinalize() {
+ CHECK(!IsSweepingInProgress());
+
+ SetGCState(kIncrementalMarkingFinalizeScheduled);
+}
+
+void ThreadState::ScheduleIdleGC() {
+ SetGCState(kIdleGCScheduled);
+ if (IsSweepingInProgress())
+ return;
+ // Some threads (e.g. PPAPI thread) don't have a scheduler.
+ // Also some tests can call Platform::SetCurrentPlatformForTesting() at any
+ // time, so we need to check for the scheduler here instead of
+ // ScheduleIdleGC().
+ if (!Platform::Current()->CurrentThread()->Scheduler()) {
+ SetGCState(kNoGCScheduled);
+ return;
+ }
+ Platform::Current()->CurrentThread()->Scheduler()->PostNonNestableIdleTask(
+ FROM_HERE, WTF::Bind(&ThreadState::PerformIdleGC, WTF::Unretained(this)));
+}
+
+void ThreadState::ScheduleIdleLazySweep() {
+ // Some threads (e.g. PPAPI thread) don't have a scheduler.
+ if (!Platform::Current()->CurrentThread()->Scheduler())
+ return;
+
+ Platform::Current()->CurrentThread()->Scheduler()->PostIdleTask(
+ FROM_HERE,
+ WTF::Bind(&ThreadState::PerformIdleLazySweep, WTF::Unretained(this)));
+}
+
+void ThreadState::SchedulePreciseGC() {
+ DCHECK(CheckThread());
+ SetGCState(kPreciseGCScheduled);
+}
+
+namespace {
+
+#define UNEXPECTED_GCSTATE(s) \
+ case ThreadState::s: \
+ LOG(FATAL) << "Unexpected transition while in GCState " #s; \
+ return
+
+void UnexpectedGCState(ThreadState::GCState gc_state) {
+ switch (gc_state) {
+ UNEXPECTED_GCSTATE(kNoGCScheduled);
+ UNEXPECTED_GCSTATE(kIdleGCScheduled);
+ UNEXPECTED_GCSTATE(kPreciseGCScheduled);
+ UNEXPECTED_GCSTATE(kFullGCScheduled);
+ UNEXPECTED_GCSTATE(kIncrementalMarkingStartScheduled);
+ UNEXPECTED_GCSTATE(kIncrementalMarkingStepScheduled);
+ UNEXPECTED_GCSTATE(kIncrementalMarkingFinalizeScheduled);
+ UNEXPECTED_GCSTATE(kPageNavigationGCScheduled);
+ }
+}
+
+#undef UNEXPECTED_GCSTATE
+
+} // namespace
+
+#define VERIFY_STATE_TRANSITION(condition) \
+ if (UNLIKELY(!(condition))) \
+ UnexpectedGCState(gc_state_)
+
+void ThreadState::SetGCState(GCState gc_state) {
+ switch (gc_state) {
+ case kNoGCScheduled:
+ DCHECK(CheckThread());
+ VERIFY_STATE_TRANSITION(
+ gc_state_ == kNoGCScheduled || gc_state_ == kIdleGCScheduled ||
+ gc_state_ == kPreciseGCScheduled || gc_state_ == kFullGCScheduled ||
+ gc_state_ == kPageNavigationGCScheduled ||
+ gc_state_ == kIncrementalMarkingStartScheduled ||
+ gc_state_ == kIncrementalMarkingStepScheduled ||
+ gc_state_ == kIncrementalMarkingFinalizeScheduled);
+ break;
+ case kIncrementalMarkingStartScheduled:
+ DCHECK(CheckThread());
+ VERIFY_STATE_TRANSITION(gc_state_ == kNoGCScheduled);
+ break;
+ case kIncrementalMarkingStepScheduled:
+ DCHECK(CheckThread());
+ VERIFY_STATE_TRANSITION(gc_state_ == kIncrementalMarkingStartScheduled ||
+ gc_state_ == kIncrementalMarkingStepScheduled);
+ break;
+ case kIncrementalMarkingFinalizeScheduled:
+ DCHECK(CheckThread());
+ VERIFY_STATE_TRANSITION(gc_state_ == kIncrementalMarkingStepScheduled);
+ break;
+ case kFullGCScheduled:
+ case kPageNavigationGCScheduled:
+ // These GCs should not be scheduled while sweeping is in progress.
+ DCHECK(!IsSweepingInProgress());
+ FALLTHROUGH;
+ case kIdleGCScheduled:
+ case kPreciseGCScheduled:
+ DCHECK(CheckThread());
+ VERIFY_STATE_TRANSITION(
+ gc_state_ == kNoGCScheduled || gc_state_ == kIdleGCScheduled ||
+ gc_state_ == kIncrementalMarkingStartScheduled ||
+ gc_state_ == kIncrementalMarkingStepScheduled ||
+ gc_state_ == kIncrementalMarkingFinalizeScheduled ||
+ gc_state_ == kPreciseGCScheduled || gc_state_ == kFullGCScheduled ||
+ gc_state_ == kPageNavigationGCScheduled);
+ CompleteSweep();
+ break;
+ default:
+ NOTREACHED();
+ }
+ gc_state_ = gc_state;
+}
+
+#undef VERIFY_STATE_TRANSITION
+
+void ThreadState::SetGCPhase(GCPhase gc_phase) {
+ switch (gc_phase) {
+ case GCPhase::kNone:
+ DCHECK_EQ(gc_phase_, GCPhase::kSweeping);
+ break;
+ case GCPhase::kMarking:
+ DCHECK_EQ(gc_phase_, GCPhase::kNone);
+ break;
+ case GCPhase::kSweeping:
+ DCHECK_EQ(gc_phase_, GCPhase::kMarking);
+ break;
+ }
+ gc_phase_ = gc_phase;
+}
+
+void ThreadState::RunScheduledGC(BlinkGC::StackState stack_state) {
+ DCHECK(CheckThread());
+ if (stack_state != BlinkGC::kNoHeapPointersOnStack)
+ return;
+
+ // If a safe point is entered while initiating a GC, we clearly do
+ // not want to do another as part of that -- the safe point is only
+ // entered after checking if a scheduled GC ought to run first.
+ // Prevent that from happening by marking GCs as forbidden while
+ // one is initiated and later running.
+ if (IsGCForbidden())
+ return;
+
+ switch (GcState()) {
+ case kFullGCScheduled:
+ CollectAllGarbage();
+ break;
+ case kPreciseGCScheduled:
+ CollectGarbage(BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
+ BlinkGC::kLazySweeping, BlinkGC::kPreciseGC);
+ break;
+ case kPageNavigationGCScheduled:
+ CollectGarbage(BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
+ BlinkGC::kEagerSweeping, BlinkGC::kPageNavigationGC);
+ break;
+ case kIdleGCScheduled:
+ // Idle time GC will be scheduled by Blink Scheduler.
+ break;
+ case kIncrementalMarkingStartScheduled:
+ IncrementalMarkingStart();
+ break;
+ case kIncrementalMarkingStepScheduled:
+ IncrementalMarkingStep();
+ break;
+ case kIncrementalMarkingFinalizeScheduled:
+ IncrementalMarkingFinalize();
+ break;
+ default:
+ break;
+ }
+}
+
+void ThreadState::PreSweep(BlinkGC::MarkingType marking_type,
+ BlinkGC::SweepingType sweeping_type) {
+ DCHECK(InAtomicMarkingPause());
+ DCHECK(CheckThread());
+ Heap().PrepareForSweep();
+
+ if (marking_type == BlinkGC::kTakeSnapshot) {
+ // Doing lazy sweeping for kTakeSnapshot doesn't make any sense so the
+ // sweeping type should always be kEagerSweeping.
+ DCHECK_EQ(sweeping_type, BlinkGC::kEagerSweeping);
+ Heap().TakeSnapshot(ThreadHeap::SnapshotType::kHeapSnapshot);
+
+ // This unmarks all marked objects and marks all unmarked objects dead.
+ Heap().MakeConsistentForMutator();
+
+ Heap().TakeSnapshot(ThreadHeap::SnapshotType::kFreelistSnapshot);
+
+ // Force setting NoGCScheduled to circumvent checkThread()
+ // in setGCState().
+ gc_state_ = kNoGCScheduled;
+ SetGCPhase(GCPhase::kSweeping);
+ SetGCPhase(GCPhase::kNone);
+ return;
+ }
+
+ // We have to set the GCPhase to Sweeping before calling pre-finalizers
+ // to disallow a GC during the pre-finalizers.
+ SetGCPhase(GCPhase::kSweeping);
+
+ // Allocation is allowed during the pre-finalizers and destructors.
+ // However, they must not mutate an object graph in a way in which
+ // a dead object gets resurrected.
+ InvokePreFinalizers();
+
+ accumulated_sweeping_time_ = 0;
+
+ EagerSweep();
+
+ // Any sweep compaction must happen after pre-finalizers and eager
+ // sweeping, as it will finalize dead objects in compactable arenas
+ // (e.g., backing stores for container objects.)
+ //
+ // As per-contract for prefinalizers, those finalizable objects must
+ // still be accessible when the prefinalizer runs, hence we cannot
+ // schedule compaction until those have run. Similarly for eager sweeping.
+ {
+ SweepForbiddenScope scope(this);
+ NoAllocationScope no_allocation_scope(this);
+ Heap().Compact();
+ }
+
+#if defined(ADDRESS_SANITIZER)
+ Heap().PoisonAllHeaps();
+#endif
+}
+
+void ThreadState::EagerSweep() {
+#if defined(ADDRESS_SANITIZER)
+ Heap().PoisonEagerArena();
+#endif
+ DCHECK(CheckThread());
+ // Some objects need to be finalized promptly and cannot be handled
+ // by lazy sweeping. Keep those in a designated heap and sweep it
+ // eagerly.
+ DCHECK(IsSweepingInProgress());
+
+ SweepForbiddenScope scope(this);
+
+ double start_time = WTF::CurrentTimeTicksInMilliseconds();
+ Heap().Arena(BlinkGC::kEagerSweepArenaIndex)->CompleteSweep();
+ AccumulateSweepingTime(WTF::CurrentTimeTicksInMilliseconds() - start_time);
+}
+
+void ThreadState::CompleteSweep() {
+ DCHECK(CheckThread());
+ // If we are not in a sweeping phase, there is nothing to do here.
+ if (!IsSweepingInProgress())
+ return;
+
+ // completeSweep() can be called recursively if finalizers can allocate
+ // memory and the allocation triggers completeSweep(). This check prevents
+ // the sweeping from being executed recursively.
+ if (SweepForbidden())
+ return;
+
+ AtomicPauseScope atomic_pause_scope(this);
+ SweepForbiddenScope scope(this);
+
+ TRACE_EVENT0("blink_gc,devtools.timeline", "ThreadState::completeSweep");
+ double start_time = WTF::CurrentTimeTicksInMilliseconds();
+
+ Heap().CompleteSweep();
+
+ double time_for_complete_sweep =
+ WTF::CurrentTimeTicksInMilliseconds() - start_time;
+ AccumulateSweepingTime(time_for_complete_sweep);
+
+ if (IsMainThread()) {
+ DEFINE_STATIC_LOCAL(CustomCountHistogram, complete_sweep_histogram,
+ ("BlinkGC.CompleteSweep", 1, 10 * 1000, 50));
+ complete_sweep_histogram.Count(time_for_complete_sweep);
+ }
+
+ PostSweep();
+}
+
+BlinkGCObserver::BlinkGCObserver(ThreadState* thread_state)
+ : thread_state_(thread_state) {
+ thread_state_->AddObserver(this);
+}
+
+BlinkGCObserver::~BlinkGCObserver() {
+ thread_state_->RemoveObserver(this);
+}
+
+void ThreadState::PostSweep() {
+ DCHECK(CheckThread());
+ ThreadHeap::ReportMemoryUsageForTracing();
+
+ if (IsMainThread()) {
+ double collection_rate =
+ 1.0 - heap_->HeapStats().LiveObjectRateSinceLastGC();
+ TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("blink_gc"),
+ "ThreadState::collectionRate",
+ static_cast<int>(100 * collection_rate));
+
+ VLOG(1) << "[state:" << this << "]"
+ << " PostSweep: collection_rate: " << std::setprecision(2)
+ << (100 * collection_rate) << "%";
+
+ // ThreadHeap::markedObjectSize() may be underestimated here if any other
+ // thread has not yet finished lazy sweeping.
+ heap_->HeapStats().SetMarkedObjectSizeAtLastCompleteSweep(
+ heap_->HeapStats().MarkedObjectSize());
+
+ DEFINE_STATIC_LOCAL(CustomCountHistogram, object_size_before_gc_histogram,
+ ("BlinkGC.ObjectSizeBeforeGC", 1, 4 * 1024 * 1024, 50));
+ object_size_before_gc_histogram.Count(
+ heap_->HeapStats().ObjectSizeAtLastGC() / 1024);
+ DEFINE_STATIC_LOCAL(CustomCountHistogram, object_size_after_gc_histogram,
+ ("BlinkGC.ObjectSizeAfterGC", 1, 4 * 1024 * 1024, 50));
+ object_size_after_gc_histogram.Count(heap_->HeapStats().MarkedObjectSize() /
+ 1024);
+ DEFINE_STATIC_LOCAL(CustomCountHistogram, collection_rate_histogram,
+ ("BlinkGC.CollectionRate", 1, 100, 20));
+ collection_rate_histogram.Count(static_cast<int>(100 * collection_rate));
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, time_for_sweep_histogram,
+ ("BlinkGC.TimeForSweepingAllObjects", 1, 10 * 1000, 50));
+ time_for_sweep_histogram.Count(accumulated_sweeping_time_);
+
+#define COUNT_COLLECTION_RATE_HISTOGRAM_BY_GC_REASON(GCReason) \
+ case BlinkGC::k##GCReason: { \
+ DEFINE_STATIC_LOCAL(CustomCountHistogram, histogram, \
+ ("BlinkGC.CollectionRate_" #GCReason, 1, 100, 20)); \
+ histogram.Count(static_cast<int>(100 * collection_rate)); \
+ break; \
+ }
+
+ switch (current_gc_data_.reason) {
+ COUNT_COLLECTION_RATE_HISTOGRAM_BY_GC_REASON(IdleGC)
+ COUNT_COLLECTION_RATE_HISTOGRAM_BY_GC_REASON(PreciseGC)
+ COUNT_COLLECTION_RATE_HISTOGRAM_BY_GC_REASON(ConservativeGC)
+ COUNT_COLLECTION_RATE_HISTOGRAM_BY_GC_REASON(ForcedGC)
+ COUNT_COLLECTION_RATE_HISTOGRAM_BY_GC_REASON(MemoryPressureGC)
+ COUNT_COLLECTION_RATE_HISTOGRAM_BY_GC_REASON(PageNavigationGC)
+ default:
+ break;
+ }
+ }
+
+ SetGCPhase(GCPhase::kNone);
+ if (GcState() == kIdleGCScheduled)
+ ScheduleIdleGC();
+
+ gc_age_++;
+
+ for (const auto& observer : observers_)
+ observer->OnCompleteSweepDone();
+}
+
+void ThreadState::SafePoint(BlinkGC::StackState stack_state) {
+ DCHECK(CheckThread());
+ ThreadHeap::ReportMemoryUsageForTracing();
+
+ RunScheduledGC(stack_state);
+ stack_state_ = BlinkGC::kHeapPointersOnStack;
+}
+
+#ifdef ADDRESS_SANITIZER
+// When we are running under AddressSanitizer with
+// detect_stack_use_after_return=1 then stack marker obtained from
+// SafePointScope will point into a fake stack. Detect this case by checking if
+// it falls in between current stack frame and stack start and use an arbitrary
+// high enough value for it. Don't adjust stack marker in any other case to
+// match behavior of code running without AddressSanitizer.
+NO_SANITIZE_ADDRESS static void* AdjustScopeMarkerForAdressSanitizer(
+ void* scope_marker) {
+ Address start = reinterpret_cast<Address>(WTF::GetStackStart());
+ Address end = reinterpret_cast<Address>(&start);
+ CHECK_LT(end, start);
+
+ if (end <= scope_marker && scope_marker < start)
+ return scope_marker;
+
+ // 256 is as good an approximation as any else.
+ const size_t kBytesToCopy = sizeof(Address) * 256;
+ if (static_cast<size_t>(start - end) < kBytesToCopy)
+ return start;
+
+ return end + kBytesToCopy;
+}
+#endif
+
+// TODO(haraken): The first void* pointer is unused. Remove it.
+using PushAllRegistersCallback = void (*)(void*, ThreadState*, intptr_t*);
+extern "C" void PushAllRegisters(void*, ThreadState*, PushAllRegistersCallback);
+
+static void EnterSafePointAfterPushRegisters(void*,
+ ThreadState* state,
+ intptr_t* stack_end) {
+ state->RecordStackEnd(stack_end);
+ state->CopyStackUntilSafePointScope();
+}
+
+void ThreadState::EnterSafePoint(BlinkGC::StackState stack_state,
+ void* scope_marker) {
+ DCHECK(CheckThread());
+#ifdef ADDRESS_SANITIZER
+ if (stack_state == BlinkGC::kHeapPointersOnStack)
+ scope_marker = AdjustScopeMarkerForAdressSanitizer(scope_marker);
+#endif
+ DCHECK(stack_state == BlinkGC::kNoHeapPointersOnStack || scope_marker);
+ DCHECK(IsGCForbidden());
+ stack_state_ = stack_state;
+ safe_point_scope_marker_ = scope_marker;
+ PushAllRegisters(nullptr, this, EnterSafePointAfterPushRegisters);
+}
+
+void ThreadState::LeaveSafePoint() {
+ DCHECK(CheckThread());
+ stack_state_ = BlinkGC::kHeapPointersOnStack;
+ ClearSafePointScopeMarker();
+}
+
+void ThreadState::AddObserver(BlinkGCObserver* observer) {
+ DCHECK(observer);
+ DCHECK(observers_.find(observer) == observers_.end());
+ observers_.insert(observer);
+}
+
+void ThreadState::RemoveObserver(BlinkGCObserver* observer) {
+ DCHECK(observer);
+ DCHECK(observers_.find(observer) != observers_.end());
+ observers_.erase(observer);
+}
+
+void ThreadState::ReportMemoryToV8() {
+ if (!isolate_)
+ return;
+
+ size_t current_heap_size = heap_->HeapStats().AllocatedObjectSize() +
+ heap_->HeapStats().MarkedObjectSize();
+ int64_t diff = static_cast<int64_t>(current_heap_size) -
+ static_cast<int64_t>(reported_memory_to_v8_);
+ isolate_->AdjustAmountOfExternalAllocatedMemory(diff);
+ reported_memory_to_v8_ = current_heap_size;
+}
+
+void ThreadState::CopyStackUntilSafePointScope() {
+ if (!safe_point_scope_marker_ ||
+ stack_state_ == BlinkGC::kNoHeapPointersOnStack)
+ return;
+
+ Address* to = reinterpret_cast<Address*>(safe_point_scope_marker_);
+ Address* from = reinterpret_cast<Address*>(end_of_stack_);
+ CHECK_LT(from, to);
+ CHECK_LE(to, reinterpret_cast<Address*>(start_of_stack_));
+ size_t slot_count = static_cast<size_t>(to - from);
+// Catch potential performance issues.
+#if defined(LEAK_SANITIZER) || defined(ADDRESS_SANITIZER)
+ // ASan/LSan use more space on the stack and we therefore
+ // increase the allowed stack copying for those builds.
+ DCHECK_LT(slot_count, 2048u);
+#else
+ DCHECK_LT(slot_count, 1024u);
+#endif
+
+ DCHECK(!safe_point_stack_copy_.size());
+ safe_point_stack_copy_.resize(slot_count);
+ for (size_t i = 0; i < slot_count; ++i) {
+ safe_point_stack_copy_[i] = from[i];
+ }
+}
+
+void ThreadState::RegisterStaticPersistentNode(
+ PersistentNode* node,
+ PersistentClearCallback callback) {
+#if defined(LEAK_SANITIZER)
+ if (disabled_static_persistent_registration_)
+ return;
+#endif
+
+ DCHECK(!static_persistents_.Contains(node));
+ static_persistents_.insert(node, callback);
+}
+
+void ThreadState::ReleaseStaticPersistentNodes() {
+ HashMap<PersistentNode*, ThreadState::PersistentClearCallback>
+ static_persistents;
+ static_persistents.swap(static_persistents_);
+
+ PersistentRegion* persistent_region = GetPersistentRegion();
+ for (const auto& it : static_persistents)
+ persistent_region->ReleasePersistentNode(it.key, it.value);
+}
+
+void ThreadState::FreePersistentNode(PersistentRegion* persistent_region,
+ PersistentNode* persistent_node) {
+ persistent_region->FreePersistentNode(persistent_node);
+ // Do not allow static persistents to be freed before
+ // they're all released in releaseStaticPersistentNodes().
+ //
+ // There's no fundamental reason why this couldn't be supported,
+ // but no known use for it.
+ if (persistent_region == GetPersistentRegion())
+ DCHECK(!static_persistents_.Contains(persistent_node));
+}
+
+#if defined(LEAK_SANITIZER)
+void ThreadState::enterStaticReferenceRegistrationDisabledScope() {
+ disabled_static_persistent_registration_++;
+}
+
+void ThreadState::leaveStaticReferenceRegistrationDisabledScope() {
+ DCHECK(disabled_static_persistent_registration_);
+ disabled_static_persistent_registration_--;
+}
+#endif
+
+void ThreadState::InvokePreFinalizers() {
+ DCHECK(CheckThread());
+ DCHECK(!SweepForbidden());
+ TRACE_EVENT0("blink_gc", "ThreadState::invokePreFinalizers");
+
+ SweepForbiddenScope sweep_forbidden(this);
+ // Pre finalizers may access unmarked objects but are forbidden from
+ // ressurecting them.
+ ObjectResurrectionForbiddenScope object_resurrection_forbidden(this);
+
+ double start_time = WTF::CurrentTimeTicksInMilliseconds();
+
+ // Call the prefinalizers in the opposite order to their registration.
+ //
+ // LinkedHashSet does not support modification during iteration, so
+ // copy items first.
+ //
+ // The prefinalizer callback wrapper returns |true| when its associated
+ // object is unreachable garbage and the prefinalizer callback has run.
+ // The registered prefinalizer entry must then be removed and deleted.
+ Vector<PreFinalizer> reversed;
+ for (auto rit = ordered_pre_finalizers_.rbegin();
+ rit != ordered_pre_finalizers_.rend(); ++rit) {
+ reversed.push_back(*rit);
+ }
+ for (PreFinalizer pre_finalizer : reversed) {
+ if ((pre_finalizer.second)(pre_finalizer.first))
+ ordered_pre_finalizers_.erase(pre_finalizer);
+ }
+ if (IsMainThread()) {
+ double time_for_invoking_pre_finalizers =
+ WTF::CurrentTimeTicksInMilliseconds() - start_time;
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, pre_finalizers_histogram,
+ ("BlinkGC.TimeForInvokingPreFinalizers", 1, 10 * 1000, 50));
+ pre_finalizers_histogram.Count(time_for_invoking_pre_finalizers);
+ }
+}
+
+// static
+base::subtle::AtomicWord ThreadState::incremental_marking_counter_ = 0;
+
+void ThreadState::EnableIncrementalMarkingBarrier() {
+ CHECK(!IsIncrementalMarking());
+ base::subtle::Barrier_AtomicIncrement(&incremental_marking_counter_, 1);
+ SetIncrementalMarking(true);
+}
+
+void ThreadState::DisableIncrementalMarkingBarrier() {
+ CHECK(IsIncrementalMarking());
+ base::subtle::Barrier_AtomicIncrement(&incremental_marking_counter_, -1);
+ SetIncrementalMarking(false);
+}
+
+void ThreadState::IncrementalMarkingStart() {
+ VLOG(2) << "[state:" << this << "] "
+ << "IncrementalMarking: Start";
+ CompleteSweep();
+ AtomicPauseScope atomic_pause_scope(this);
+ RecursiveMutexLocker persistent_lock(
+ ProcessHeap::CrossThreadPersistentMutex());
+ MarkPhasePrologue(BlinkGC::kNoHeapPointersOnStack,
+ BlinkGC::kIncrementalMarking, BlinkGC::kIdleGC);
+ MarkPhaseVisitRoots();
+ EnableIncrementalMarkingBarrier();
+ ScheduleIncrementalMarkingStep();
+ DCHECK(IsMarkingInProgress());
+}
+
+void ThreadState::IncrementalMarkingStep() {
+ VLOG(2) << "[state:" << this << "] "
+ << "IncrementalMarking: Step";
+ AtomicPauseScope atomic_pause_scope(this);
+ DCHECK(IsMarkingInProgress());
+ RecursiveMutexLocker persistent_lock(
+ ProcessHeap::CrossThreadPersistentMutex());
+ bool complete = MarkPhaseAdvanceMarking(
+ CurrentTimeTicksInSeconds() + kIncrementalMarkingStepDurationInSeconds);
+ if (complete)
+ ScheduleIncrementalMarkingFinalize();
+ else
+ ScheduleIncrementalMarkingStep();
+ DCHECK(IsMarkingInProgress());
+}
+
+void ThreadState::IncrementalMarkingFinalize() {
+ VLOG(2) << "[state:" << this << "] "
+ << "IncrementalMarking: Finalize";
+ SetGCState(kNoGCScheduled);
+ DisableIncrementalMarkingBarrier();
+ AtomicPauseScope atomic_pause_scope(this);
+ DCHECK(IsMarkingInProgress());
+ RecursiveMutexLocker persistent_lock(
+ ProcessHeap::CrossThreadPersistentMutex());
+ MarkPhaseVisitRoots();
+ bool complete =
+ MarkPhaseAdvanceMarking(std::numeric_limits<double>::infinity());
+ CHECK(complete);
+ MarkPhaseEpilogue(current_gc_data_.marking_type);
+ PreSweep(current_gc_data_.marking_type, BlinkGC::kLazySweeping);
+ DCHECK(IsSweepingInProgress());
+ DCHECK_EQ(GcState(), kNoGCScheduled);
+}
+
+void ThreadState::CollectGarbage(BlinkGC::StackState stack_state,
+ BlinkGC::MarkingType marking_type,
+ BlinkGC::SweepingType sweeping_type,
+ BlinkGC::GCReason reason) {
+ // Nested garbage collection invocations are not supported.
+ CHECK(!IsGCForbidden());
+ // Garbage collection during sweeping is not supported. This can happen when
+ // finalizers trigger garbage collections.
+ if (SweepForbidden())
+ return;
+
+ double start_total_collect_garbage_time =
+ WTF::CurrentTimeTicksInMilliseconds();
+
+ RUNTIME_CALL_TIMER_SCOPE_IF_ISOLATE_EXISTS(
+ GetIsolate(), RuntimeCallStats::CounterId::kCollectGarbage);
+
+ const bool was_incremental_marking = IsMarkingInProgress();
+
+ if (was_incremental_marking) {
+ // Set stack state in case we are starting a Conservative GC while
+ // incremental marking is in progress.
+ current_gc_data_.stack_state = stack_state;
+ IncrementalMarkingFinalize();
+ }
+
+ // We don't want floating garbage for the specific garbage collection types
+ // mentioned below. In this case we will follow up with a regular full garbage
+ // collection.
+ const bool should_do_full_gc = !was_incremental_marking ||
+ reason == BlinkGC::kForcedGC ||
+ reason == BlinkGC::kMemoryPressureGC ||
+ reason == BlinkGC::kThreadTerminationGC;
+ if (should_do_full_gc) {
+ CompleteSweep();
+ SetGCState(kNoGCScheduled);
+ AtomicPauseScope atomic_pause_scope(this);
+ {
+ TRACE_EVENT2("blink_gc,devtools.timeline", "BlinkGCMarking",
+ "lazySweeping", sweeping_type == BlinkGC::kLazySweeping,
+ "gcReason", GcReasonString(reason));
+ // Access to the CrossThreadPersistentRegion has to be prevented
+ // while in the marking phase because otherwise other threads may
+ // allocate or free PersistentNodes and we can't handle
+ // that. Grabbing this lock also prevents non-attached threads
+ // from accessing any GCed heap while a GC runs.
+ RecursiveMutexLocker persistent_lock(
+ ProcessHeap::CrossThreadPersistentMutex());
+
+ MarkPhasePrologue(stack_state, marking_type, reason);
+ MarkPhaseVisitRoots();
+ CHECK(MarkPhaseAdvanceMarking(std::numeric_limits<double>::infinity()));
+ MarkPhaseEpilogue(marking_type);
+ }
+ PreSweep(marking_type, sweeping_type);
+ }
+
+ if (sweeping_type == BlinkGC::kEagerSweeping) {
+ // Eager sweeping should happen only in testing.
+ CompleteSweep();
+ } else {
+ DCHECK(sweeping_type == BlinkGC::kLazySweeping);
+ // The default behavior is lazy sweeping.
+ ScheduleIdleLazySweep();
+ }
+
+ double total_collect_garbage_time =
+ WTF::CurrentTimeTicksInMilliseconds() - start_total_collect_garbage_time;
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(
+ CustomCountHistogram, time_for_total_collect_garbage_histogram,
+ ("BlinkGC.TimeForTotalCollectGarbage", 1, 10 * 1000, 50));
+ time_for_total_collect_garbage_histogram.Count(total_collect_garbage_time);
+ VLOG(1) << "[state:" << this << "]"
+ << " CollectGarbage: time: " << std::setprecision(2)
+ << total_collect_garbage_time << "ms"
+ << " stack: " << StackStateString(stack_state)
+ << " marking: " << MarkingTypeString(marking_type)
+ << " sweeping: " << SweepingTypeString(sweeping_type)
+ << " reason: " << GcReasonString(reason);
+}
+
+void ThreadState::MarkPhasePrologue(BlinkGC::StackState stack_state,
+ BlinkGC::MarkingType marking_type,
+ BlinkGC::GCReason reason) {
+ SetGCPhase(GCPhase::kMarking);
+ Heap().CommitCallbackStacks();
+
+ current_gc_data_.stack_state = stack_state;
+ current_gc_data_.marking_type = marking_type;
+ current_gc_data_.reason = reason;
+ current_gc_data_.marking_time_in_milliseconds = 0;
+
+ if (marking_type == BlinkGC::kTakeSnapshot) {
+ current_gc_data_.visitor =
+ MarkingVisitor::Create(this, MarkingVisitor::kSnapshotMarking);
+ } else {
+ DCHECK(marking_type == BlinkGC::kAtomicMarking ||
+ marking_type == BlinkGC::kIncrementalMarking);
+ if (Heap().Compaction()->ShouldCompact(&Heap(), stack_state, marking_type,
+ reason)) {
+ Heap().Compaction()->Initialize(this);
+ current_gc_data_.visitor = MarkingVisitor::Create(
+ this, MarkingVisitor::kGlobalMarkingWithCompaction);
+ } else {
+ current_gc_data_.visitor =
+ MarkingVisitor::Create(this, MarkingVisitor::kGlobalMarking);
+ }
+ }
+
+ if (marking_type == BlinkGC::kTakeSnapshot)
+ BlinkGCMemoryDumpProvider::Instance()->ClearProcessDumpForCurrentGC();
+
+ if (isolate_ && perform_cleanup_)
+ perform_cleanup_(isolate_);
+
+ DCHECK(InAtomicMarkingPause());
+ Heap().MakeConsistentForGC();
+ Heap().FlushHeapDoesNotContainCacheIfNeeded();
+ Heap().ClearArenaAges();
+
+ current_gc_data_.marked_object_size =
+ Heap().HeapStats().AllocatedObjectSize() +
+ Heap().HeapStats().MarkedObjectSize();
+ if (marking_type != BlinkGC::kTakeSnapshot)
+ Heap().ResetHeapCounters();
+}
+
+void ThreadState::MarkPhaseVisitRoots() {
+ double start_time = WTF::CurrentTimeTicksInMilliseconds();
+
+ // StackFrameDepth should be disabled so we don't trace most of the object
+ // graph in one incremental marking step.
+ DCHECK(!Heap().GetStackFrameDepth().IsEnabled());
+
+ // 1. Trace persistent roots.
+ Heap().VisitPersistentRoots(current_gc_data_.visitor.get());
+
+ // 2. Trace objects reachable from the stack.
+ {
+ SafePointScope safe_point_scope(current_gc_data_.stack_state, this);
+ Heap().VisitStackRoots(current_gc_data_.visitor.get());
+ }
+ current_gc_data_.marking_time_in_milliseconds +=
+ WTF::CurrentTimeTicksInMilliseconds() - start_time;
+}
+
+bool ThreadState::MarkPhaseAdvanceMarking(double deadline_seconds) {
+ double start_time = WTF::CurrentTimeTicksInMilliseconds();
+
+ StackFrameDepthScope stack_depth_scope(&Heap().GetStackFrameDepth());
+
+ // 3. Transitive closure to trace objects including ephemerons.
+ bool complete = Heap().AdvanceMarkingStackProcessing(
+ current_gc_data_.visitor.get(), deadline_seconds);
+
+ current_gc_data_.marking_time_in_milliseconds +=
+ WTF::CurrentTimeTicksInMilliseconds() - start_time;
+ return complete;
+}
+
+void ThreadState::MarkPhaseEpilogue(BlinkGC::MarkingType marking_type) {
+ Visitor* visitor = current_gc_data_.visitor.get();
+ // Finish marking of not-fully-constructed objects.
+ Heap().MarkNotFullyConstructedObjects(visitor);
+ CHECK(Heap().AdvanceMarkingStackProcessing(
+ visitor, std::numeric_limits<double>::infinity()));
+
+ VisitWeakPersistents(visitor);
+ Heap().WeakProcessing(visitor);
+ Heap().DecommitCallbackStacks();
+
+ current_gc_data_.visitor.reset();
+
+#if BUILDFLAG(BLINK_HEAP_VERIFICATION)
+ VerifyMarking(marking_type);
+#endif // BLINK_HEAP_VERIFICATION
+
+ Heap().HeapStats().SetEstimatedMarkingTimePerByte(
+ current_gc_data_.marked_object_size
+ ? (current_gc_data_.marking_time_in_milliseconds / 1000 /
+ current_gc_data_.marked_object_size)
+ : 0);
+
+ ThreadHeap::ReportMemoryUsageHistogram();
+ WTF::Partitions::ReportMemoryUsageHistogram();
+
+ if (invalidate_dead_objects_in_wrappers_marking_deque_)
+ invalidate_dead_objects_in_wrappers_marking_deque_(isolate_);
+
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(CustomCountHistogram, marking_time_histogram,
+ ("BlinkGC.CollectGarbage", 0, 10 * 1000, 50));
+ marking_time_histogram.Count(current_gc_data_.marking_time_in_milliseconds);
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(
+ CustomCountHistogram, total_object_space_histogram,
+ ("BlinkGC.TotalObjectSpace", 0, 4 * 1024 * 1024, 50));
+ total_object_space_histogram.Count(ProcessHeap::TotalAllocatedObjectSize() /
+ 1024);
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(
+ CustomCountHistogram, total_allocated_space_histogram,
+ ("BlinkGC.TotalAllocatedSpace", 0, 4 * 1024 * 1024, 50));
+ total_allocated_space_histogram.Count(ProcessHeap::TotalAllocatedSpace() /
+ 1024);
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(
+ EnumerationHistogram, gc_reason_histogram,
+ ("BlinkGC.GCReason", BlinkGC::kLastGCReason + 1));
+ gc_reason_histogram.Count(current_gc_data_.reason);
+}
+
+void ThreadState::VerifyMarking(BlinkGC::MarkingType marking_type) {
+ // Marking for snapshot does not clear unreachable weak fields prohibiting
+ // verification of markbits as we leave behind non-marked non-cleared weak
+ // fields.
+ if (marking_type == BlinkGC::kTakeSnapshot)
+ return;
+ Heap().VerifyMarking();
+}
+
+void ThreadState::CollectAllGarbage() {
+ // We need to run multiple GCs to collect a chain of persistent handles.
+ size_t previous_live_objects = 0;
+ for (int i = 0; i < 5; ++i) {
+ CollectGarbage(BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
+ BlinkGC::kEagerSweeping, BlinkGC::kForcedGC);
+ size_t live_objects = Heap().HeapStats().MarkedObjectSize();
+ if (live_objects == previous_live_objects)
+ break;
+ previous_live_objects = live_objects;
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/heap/thread_state.h b/chromium/third_party/blink/renderer/platform/heap/thread_state.h
new file mode 100644
index 00000000000..6a01829c573
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/thread_state.h
@@ -0,0 +1,770 @@
+/*
+ * Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_THREAD_STATE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_THREAD_STATE_H_
+
+#include <memory>
+
+#include "base/atomicops.h"
+#include "base/macros.h"
+#include "third_party/blink/public/platform/web_thread.h"
+#include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h"
+#include "third_party/blink/renderer/platform/heap/blink_gc.h"
+#include "third_party/blink/renderer/platform/heap/threading_traits.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/address_sanitizer.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/linked_hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/thread_specific.h"
+#include "third_party/blink/renderer/platform/wtf/threading.h"
+#include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
+
+namespace v8 {
+class Isolate;
+};
+
+namespace blink {
+
+namespace incremental_marking_test {
+class IncrementalMarkingScope;
+class IncrementalMarkingTestDriver;
+} // namespace incremental_marking_test
+
+class GarbageCollectedMixinConstructorMarkerBase;
+class MarkingVisitor;
+class PersistentNode;
+class PersistentRegion;
+class ThreadHeap;
+class ThreadState;
+class Visitor;
+
+template <ThreadAffinity affinity>
+class ThreadStateFor;
+
+// Declare that a class has a pre-finalizer. The pre-finalizer is called
+// before any object gets swept, so it is safe to touch on-heap objects
+// that may be collected in the same GC cycle. If you cannot avoid touching
+// on-heap objects in a destructor (which is not allowed), you can consider
+// using the pre-finalizer. The only restriction is that the pre-finalizer
+// must not resurrect dead objects (e.g., store unmarked objects into
+// Members etc). The pre-finalizer is called on the thread that registered
+// the pre-finalizer.
+//
+// Since a pre-finalizer adds pressure on GC performance, you should use it
+// only if necessary.
+//
+// A pre-finalizer is similar to the
+// HeapHashMap<WeakMember<Foo>, std::unique_ptr<Disposer>> idiom. The
+// difference between this and the idiom is that pre-finalizer function is
+// called whenever an object is destructed with this feature. The
+// HeapHashMap<WeakMember<Foo>, std::unique_ptr<Disposer>> idiom requires an
+// assumption that the HeapHashMap outlives objects pointed by WeakMembers.
+// FIXME: Replace all of the
+// HeapHashMap<WeakMember<Foo>, std::unique_ptr<Disposer>> idiom usages with the
+// pre-finalizer if the replacement won't cause performance regressions.
+//
+// Usage:
+//
+// class Foo : GarbageCollected<Foo> {
+// USING_PRE_FINALIZER(Foo, dispose);
+// private:
+// void dispose()
+// {
+// bar_->...; // It is safe to touch other on-heap objects.
+// }
+// Member<Bar> bar_;
+// };
+#define USING_PRE_FINALIZER(Class, preFinalizer) \
+ public: \
+ static bool InvokePreFinalizer(void* object) { \
+ Class* self = reinterpret_cast<Class*>(object); \
+ if (ThreadHeap::IsHeapObjectAlive(self)) \
+ return false; \
+ self->Class::preFinalizer(); \
+ return true; \
+ } \
+ \
+ private: \
+ ThreadState::PrefinalizerRegistration<Class> prefinalizer_dummy_ = this; \
+ using UsingPreFinalizerMacroNeedsTrailingSemiColon = char
+
+class PLATFORM_EXPORT BlinkGCObserver {
+ public:
+ // The constructor automatically register this object to ThreadState's
+ // observer lists. The argument must not be null.
+ explicit BlinkGCObserver(ThreadState*);
+
+ // The destructor automatically unregister this object from ThreadState's
+ // observer lists.
+ virtual ~BlinkGCObserver();
+
+ virtual void OnCompleteSweepDone() = 0;
+
+ private:
+ // As a ThreadState must live when a BlinkGCObserver lives, holding a raw
+ // pointer is safe.
+ ThreadState* thread_state_;
+};
+
+class PLATFORM_EXPORT ThreadState {
+ USING_FAST_MALLOC(ThreadState);
+
+ public:
+ // See setGCState() for possible state transitions.
+ enum GCState {
+ kNoGCScheduled,
+ kIdleGCScheduled,
+ kIncrementalMarkingStartScheduled,
+ kIncrementalMarkingStepScheduled,
+ kIncrementalMarkingFinalizeScheduled,
+ kPreciseGCScheduled,
+ kFullGCScheduled,
+ kPageNavigationGCScheduled,
+ };
+
+ // The phase that the GC is in. The GCPhase will not return kNone for mutators
+ // running during incremental marking and lazy sweeping. See SetGCPhase() for
+ // possible state transitions.
+ enum class GCPhase {
+ // GC is doing nothing.
+ kNone,
+ // GC is in marking phase.
+ kMarking,
+ // GC is in sweeping phase.
+ kSweeping,
+ };
+
+ // The NoAllocationScope class is used in debug mode to catch unwanted
+ // allocations. E.g. allocations during GC.
+ class NoAllocationScope final {
+ STACK_ALLOCATED();
+
+ public:
+ explicit NoAllocationScope(ThreadState* state) : state_(state) {
+ state_->EnterNoAllocationScope();
+ }
+ ~NoAllocationScope() { state_->LeaveNoAllocationScope(); }
+
+ private:
+ ThreadState* state_;
+ };
+
+ class SweepForbiddenScope final {
+ STACK_ALLOCATED();
+
+ public:
+ explicit SweepForbiddenScope(ThreadState* state) : state_(state) {
+ DCHECK(!state_->sweep_forbidden_);
+ state_->sweep_forbidden_ = true;
+ }
+ ~SweepForbiddenScope() {
+ DCHECK(state_->sweep_forbidden_);
+ state_->sweep_forbidden_ = false;
+ }
+
+ private:
+ ThreadState* state_;
+ };
+
+ // Used to denote when access to unmarked objects is allowed but we shouldn't
+ // ressurect it by making new references (e.g. during weak processing and pre
+ // finalizer).
+ class ObjectResurrectionForbiddenScope final {
+ STACK_ALLOCATED();
+
+ public:
+ explicit ObjectResurrectionForbiddenScope(ThreadState* state)
+ : state_(state) {
+ state_->EnterObjectResurrectionForbiddenScope();
+ }
+ ~ObjectResurrectionForbiddenScope() {
+ state_->LeaveObjectResurrectionForbiddenScope();
+ }
+
+ private:
+ ThreadState* state_;
+ };
+
+ // Returns true if any thread is currently incremental marking its heap and
+ // false otherwise. For an exact check use
+ // ThreadState::IsIncrementalMarking().
+ static bool IsAnyIncrementalMarking() {
+ // Stores use full barrier to allow using the simplest relaxed load here.
+ return base::subtle::NoBarrier_Load(&incremental_marking_counter_) > 0;
+ }
+
+ static void AttachMainThread();
+
+ // Associate ThreadState object with the current thread. After this
+ // call thread can start using the garbage collected heap infrastructure.
+ // It also has to periodically check for safepoints.
+ static void AttachCurrentThread();
+
+ // Disassociate attached ThreadState from the current thread. The thread
+ // can no longer use the garbage collected heap after this call.
+ static void DetachCurrentThread();
+
+ static ThreadState* Current() { return **thread_specific_; }
+
+ static ThreadState* MainThreadState() {
+ return reinterpret_cast<ThreadState*>(main_thread_state_storage_);
+ }
+
+ static ThreadState* FromObject(const void*);
+
+ bool IsMainThread() const { return this == MainThreadState(); }
+ bool CheckThread() const { return thread_ == CurrentThread(); }
+
+ ThreadHeap& Heap() const { return *heap_; }
+ ThreadIdentifier ThreadId() const { return thread_; }
+
+ // When ThreadState is detaching from non-main thread its
+ // heap is expected to be empty (because it is going away).
+ // Perform registered cleanup tasks and garbage collection
+ // to sweep away any objects that are left on this heap.
+ // We assert that nothing must remain after this cleanup.
+ // If assertion does not hold we crash as we are potentially
+ // in the dangling pointer situation.
+ void RunTerminationGC();
+
+ void PerformIdleGC(double deadline_seconds);
+ void PerformIdleLazySweep(double deadline_seconds);
+
+ void ScheduleIdleGC();
+ void ScheduleIdleLazySweep();
+ void SchedulePreciseGC();
+ void ScheduleV8FollowupGCIfNeeded(BlinkGC::V8GCType);
+ void SchedulePageNavigationGCIfNeeded(float estimated_removal_ratio);
+ void SchedulePageNavigationGC();
+ void ScheduleGCIfNeeded();
+ void PostIdleGCTask();
+ void WillStartV8GC(BlinkGC::V8GCType);
+ void SetGCState(GCState);
+ GCState GcState() const { return gc_state_; }
+ void SetGCPhase(GCPhase);
+ bool IsMarkingInProgress() const { return gc_phase_ == GCPhase::kMarking; }
+ bool IsSweepingInProgress() const { return gc_phase_ == GCPhase::kSweeping; }
+
+ // Incremental GC.
+
+ void ScheduleIncrementalMarkingStart();
+ void ScheduleIncrementalMarkingStep();
+ void ScheduleIncrementalMarkingFinalize();
+
+ void IncrementalMarkingStart();
+ void IncrementalMarkingStep();
+ void IncrementalMarkingFinalize();
+
+ void EnableIncrementalMarkingBarrier();
+ void DisableIncrementalMarkingBarrier();
+
+ // A GC runs in the following sequence.
+ //
+ // 1) preGC() is called.
+ // 2) ThreadHeap::collectGarbage() is called. This marks live objects.
+ // 3) postGC() is called. This does thread-local weak processing.
+ // 4) preSweep() is called. This does pre-finalization, eager sweeping and
+ // heap compaction.
+ // 4) Lazy sweeping sweeps heaps incrementally. completeSweep() may be called
+ // to complete the sweeping.
+ // 5) postSweep() is called.
+ void MarkPhasePrologue(BlinkGC::StackState,
+ BlinkGC::MarkingType,
+ BlinkGC::GCReason);
+ void MarkPhaseVisitRoots();
+ bool MarkPhaseAdvanceMarking(double deadline_seconds);
+ void MarkPhaseEpilogue(BlinkGC::MarkingType);
+ void VerifyMarking(BlinkGC::MarkingType);
+
+ void CompleteSweep();
+ void PreSweep(BlinkGC::MarkingType, BlinkGC::SweepingType);
+ void PostSweep();
+
+ // Support for disallowing allocation. Mainly used for sanity
+ // checks asserts.
+ bool IsAllocationAllowed() const {
+ // Allocation is not allowed during atomic marking pause, but it is allowed
+ // during atomic sweeping pause.
+ return !InAtomicMarkingPause() && !no_allocation_count_;
+ }
+ void EnterNoAllocationScope() { no_allocation_count_++; }
+ void LeaveNoAllocationScope() { no_allocation_count_--; }
+ bool IsWrapperTracingForbidden() { return IsMixinInConstruction(); }
+ bool IsGCForbidden() const {
+ return gc_forbidden_count_ || IsMixinInConstruction();
+ }
+ void EnterGCForbiddenScope() { gc_forbidden_count_++; }
+ void LeaveGCForbiddenScope() {
+ DCHECK_GT(gc_forbidden_count_, 0u);
+ gc_forbidden_count_--;
+ }
+ bool IsMixinInConstruction() const { return mixins_being_constructed_count_; }
+ void EnterMixinConstructionScope() { mixins_being_constructed_count_++; }
+ void LeaveMixinConstructionScope() {
+ DCHECK_GT(mixins_being_constructed_count_, 0u);
+ mixins_being_constructed_count_--;
+ }
+ bool SweepForbidden() const { return sweep_forbidden_; }
+ bool IsObjectResurrectionForbidden() const {
+ return object_resurrection_forbidden_;
+ }
+ void EnterObjectResurrectionForbiddenScope() {
+ DCHECK(!object_resurrection_forbidden_);
+ object_resurrection_forbidden_ = true;
+ }
+ void LeaveObjectResurrectionForbiddenScope() {
+ DCHECK(object_resurrection_forbidden_);
+ object_resurrection_forbidden_ = false;
+ }
+ bool in_atomic_pause() const { return in_atomic_pause_; }
+ void EnterAtomicPause() {
+ DCHECK(!in_atomic_pause_);
+ in_atomic_pause_ = true;
+ }
+ void LeaveAtomicPause() {
+ DCHECK(in_atomic_pause_);
+ in_atomic_pause_ = false;
+ }
+ bool InAtomicMarkingPause() const {
+ return in_atomic_pause() && IsMarkingInProgress();
+ }
+ bool InAtomicSweepingPause() const {
+ return in_atomic_pause() && IsSweepingInProgress();
+ }
+
+ bool WrapperTracingInProgress() const { return wrapper_tracing_in_progress_; }
+ void SetWrapperTracingInProgress(bool value) {
+ wrapper_tracing_in_progress_ = value;
+ }
+
+ bool IsIncrementalMarking() const { return incremental_marking_; }
+ void SetIncrementalMarking(bool value) { incremental_marking_ = value; }
+
+ class MainThreadGCForbiddenScope final {
+ STACK_ALLOCATED();
+
+ public:
+ MainThreadGCForbiddenScope()
+ : thread_state_(ThreadState::MainThreadState()) {
+ thread_state_->EnterGCForbiddenScope();
+ }
+ ~MainThreadGCForbiddenScope() { thread_state_->LeaveGCForbiddenScope(); }
+
+ private:
+ ThreadState* const thread_state_;
+ };
+
+ class GCForbiddenScope final {
+ STACK_ALLOCATED();
+
+ public:
+ explicit GCForbiddenScope(ThreadState* thread_state)
+ : thread_state_(thread_state) {
+ thread_state_->EnterGCForbiddenScope();
+ }
+ ~GCForbiddenScope() { thread_state_->LeaveGCForbiddenScope(); }
+
+ private:
+ ThreadState* const thread_state_;
+ };
+
+ // Used to mark when we are in an atomic pause for GC.
+ class AtomicPauseScope final {
+ public:
+ explicit AtomicPauseScope(ThreadState* thread_state)
+ : thread_state_(thread_state), gc_forbidden_scope(thread_state) {
+ thread_state_->EnterAtomicPause();
+ }
+ ~AtomicPauseScope() { thread_state_->LeaveAtomicPause(); }
+
+ private:
+ ThreadState* const thread_state_;
+ ScriptForbiddenScope script_forbidden_scope;
+ GCForbiddenScope gc_forbidden_scope;
+ };
+
+ void FlushHeapDoesNotContainCacheIfNeeded();
+
+ // Safepoint related functionality.
+ //
+ // When a thread attempts to perform GC it needs to stop all other threads
+ // that use the heap or at least guarantee that they will not touch any
+ // heap allocated object until GC is complete.
+ //
+ // We say that a thread is at a safepoint if this thread is guaranteed to
+ // not touch any heap allocated object or any heap related functionality until
+ // it leaves the safepoint.
+ //
+ // Notice that a thread does not have to be paused if it is at safepoint it
+ // can continue to run and perform tasks that do not require interaction
+ // with the heap. It will be paused if it attempts to leave the safepoint and
+ // there is a GC in progress.
+ //
+ // Each thread that has ThreadState attached must:
+ // - periodically check if GC is requested from another thread by calling a
+ // safePoint() method;
+ // - use SafePointScope around long running loops that have no safePoint()
+ // invocation inside, such loops must not touch any heap object;
+ //
+ // Check if GC is requested by another thread and pause this thread if this is
+ // the case. Can only be called when current thread is in a consistent state.
+ void SafePoint(BlinkGC::StackState);
+
+ // Mark current thread as running inside safepoint.
+ void EnterSafePoint(BlinkGC::StackState, void*);
+ void LeaveSafePoint();
+
+ void RecordStackEnd(intptr_t* end_of_stack) { end_of_stack_ = end_of_stack; }
+ NO_SANITIZE_ADDRESS void CopyStackUntilSafePointScope();
+
+ // A region of non-weak PersistentNodes allocated on the given thread.
+ PersistentRegion* GetPersistentRegion() const {
+ return persistent_region_.get();
+ }
+
+ // A region of PersistentNodes for WeakPersistents allocated on the given
+ // thread.
+ PersistentRegion* GetWeakPersistentRegion() const {
+ return weak_persistent_region_.get();
+ }
+
+ // Visit local thread stack and trace all pointers conservatively.
+ void VisitStack(MarkingVisitor*);
+
+ // Visit the asan fake stack frame corresponding to a slot on the
+ // real machine stack if there is one.
+ void VisitAsanFakeStackForPointer(MarkingVisitor*, Address);
+
+ // Visit all non-weak persistents allocated on this thread.
+ void VisitPersistents(Visitor*);
+
+ // Visit all weak persistents allocated on this thread.
+ void VisitWeakPersistents(Visitor*);
+
+ struct GCSnapshotInfo {
+ STACK_ALLOCATED();
+ GCSnapshotInfo(size_t num_object_types);
+
+ // Map from gcInfoIndex (vector-index) to count/size.
+ Vector<int> live_count;
+ Vector<int> dead_count;
+ Vector<size_t> live_size;
+ Vector<size_t> dead_size;
+ };
+
+ void RegisterTraceDOMWrappers(
+ v8::Isolate* isolate,
+ void (*trace_dom_wrappers)(v8::Isolate*, Visitor*),
+ void (*invalidate_dead_objects_in_wrappers_marking_deque)(v8::Isolate*),
+ void (*perform_cleanup)(v8::Isolate*)) {
+ isolate_ = isolate;
+ DCHECK(!isolate_ || trace_dom_wrappers);
+ DCHECK(!isolate_ || invalidate_dead_objects_in_wrappers_marking_deque);
+ DCHECK(!isolate_ || perform_cleanup);
+ trace_dom_wrappers_ = trace_dom_wrappers;
+ invalidate_dead_objects_in_wrappers_marking_deque_ =
+ invalidate_dead_objects_in_wrappers_marking_deque;
+ perform_cleanup_ = perform_cleanup;
+ }
+
+ // By entering a gc-forbidden scope, conservative GCs will not
+ // be allowed while handling an out-of-line allocation request.
+ // Intended used when constructing subclasses of GC mixins, where
+ // the object being constructed cannot be safely traced & marked
+ // fully should a GC be allowed while its subclasses are being
+ // constructed.
+ void EnterGCForbiddenScopeIfNeeded(
+ GarbageCollectedMixinConstructorMarkerBase* gc_mixin_marker) {
+ DCHECK(CheckThread());
+ if (!gc_mixin_marker_) {
+ EnterMixinConstructionScope();
+ gc_mixin_marker_ = gc_mixin_marker;
+ }
+ }
+ void LeaveGCForbiddenScopeIfNeeded(
+ GarbageCollectedMixinConstructorMarkerBase* gc_mixin_marker) {
+ DCHECK(CheckThread());
+ if (gc_mixin_marker_ == gc_mixin_marker) {
+ LeaveMixinConstructionScope();
+ gc_mixin_marker_ = nullptr;
+ }
+ }
+
+ void AccumulateSweepingTime(double time) {
+ accumulated_sweeping_time_ += time;
+ }
+
+ void FreePersistentNode(PersistentRegion*, PersistentNode*);
+
+ using PersistentClearCallback = void (*)(void*);
+
+ void RegisterStaticPersistentNode(PersistentNode*, PersistentClearCallback);
+ void ReleaseStaticPersistentNodes();
+
+#if defined(LEAK_SANITIZER)
+ void enterStaticReferenceRegistrationDisabledScope();
+ void leaveStaticReferenceRegistrationDisabledScope();
+#endif
+
+ v8::Isolate* GetIsolate() const { return isolate_; }
+
+ BlinkGC::StackState GetStackState() const { return stack_state_; }
+
+ void CollectGarbage(BlinkGC::StackState,
+ BlinkGC::MarkingType,
+ BlinkGC::SweepingType,
+ BlinkGC::GCReason);
+ void CollectAllGarbage();
+
+ // Register the pre-finalizer for the |self| object. The class T must have
+ // USING_PRE_FINALIZER().
+ template <typename T>
+ class PrefinalizerRegistration final {
+ public:
+ PrefinalizerRegistration(T* self) {
+ static_assert(sizeof(&T::InvokePreFinalizer) > 0,
+ "USING_PRE_FINALIZER(T) must be defined.");
+ ThreadState* state =
+ ThreadStateFor<ThreadingTrait<T>::kAffinity>::GetState();
+#if DCHECK_IS_ON()
+ DCHECK(state->CheckThread());
+#endif
+ DCHECK(!state->SweepForbidden());
+ DCHECK(!state->ordered_pre_finalizers_.Contains(
+ PreFinalizer(self, T::InvokePreFinalizer)));
+ state->ordered_pre_finalizers_.insert(
+ PreFinalizer(self, T::InvokePreFinalizer));
+ }
+ };
+
+ // Returns |true| if |object| resides on this thread's heap.
+ // It is well-defined to call this method on any heap allocated
+ // reference, provided its associated heap hasn't been detached
+ // and shut down. Its behavior is undefined for any other pointer
+ // value.
+ bool IsOnThreadHeap(const void* object) const {
+ return &FromObject(object)->Heap() == &Heap();
+ }
+
+ int GcAge() const { return gc_age_; }
+
+ MarkingVisitor* CurrentVisitor() { return current_gc_data_.visitor.get(); }
+
+ private:
+ // Needs to set up visitor for testing purposes.
+ friend class incremental_marking_test::IncrementalMarkingScope;
+ friend class incremental_marking_test::IncrementalMarkingTestDriver;
+ template <typename T>
+ friend class PrefinalizerRegistration;
+
+ // Number of ThreadState's that are currently in incremental marking. The
+ // counter is incremented by one when some ThreadState enters incremental
+ // marking and decremented upon finishing.
+ static base::subtle::AtomicWord incremental_marking_counter_;
+
+ ThreadState();
+ ~ThreadState();
+
+ void ClearSafePointScopeMarker() {
+ safe_point_stack_copy_.clear();
+ safe_point_scope_marker_ = nullptr;
+ }
+
+ // shouldScheduleIdleGC and shouldForceConservativeGC
+ // implement the heuristics that are used to determine when to collect
+ // garbage.
+ // If shouldForceConservativeGC returns true, we force the garbage
+ // collection immediately. Otherwise, if should*GC returns true, we
+ // record that we should garbage collect the next time we return
+ // to the event loop. If both return false, we don't need to
+ // collect garbage at this point.
+ bool ShouldScheduleIdleGC();
+ bool ShouldForceConservativeGC();
+ bool ShouldScheduleIncrementalMarking() const;
+ // V8 minor or major GC is likely to drop a lot of references to objects
+ // on Oilpan's heap. We give a chance to schedule a GC.
+ bool ShouldScheduleV8FollowupGC();
+ // Page navigation is likely to drop a lot of references to objects
+ // on Oilpan's heap. We give a chance to schedule a GC.
+ // estimatedRemovalRatio is the estimated ratio of objects that will be no
+ // longer necessary due to the navigation.
+ bool ShouldSchedulePageNavigationGC(float estimated_removal_ratio);
+
+ // Internal helpers to handle memory pressure conditions.
+
+ // Returns true if memory use is in a near-OOM state
+ // (aka being under "memory pressure".)
+ bool ShouldForceMemoryPressureGC();
+
+ // Returns true if shouldForceMemoryPressureGC() held and a
+ // conservative GC was performed to handle the emergency.
+ bool ForceMemoryPressureGCIfNeeded();
+
+ size_t EstimatedLiveSize(size_t current_size, size_t size_at_last_gc);
+ size_t TotalMemorySize();
+ double HeapGrowingRate();
+ double PartitionAllocGrowingRate();
+ bool JudgeGCThreshold(size_t allocated_object_size_threshold,
+ size_t total_memory_size_threshold,
+ double heap_growing_rate_threshold);
+
+ void RunScheduledGC(BlinkGC::StackState);
+
+ void EagerSweep();
+
+ void InvokePreFinalizers();
+
+ void ReportMemoryToV8();
+
+ friend class SafePointScope;
+
+ friend class BlinkGCObserver;
+
+ // Adds the given observer to the ThreadState's observer list. This doesn't
+ // take ownership of the argument. The argument must not be null. The argument
+ // must not be registered before calling this.
+ void AddObserver(BlinkGCObserver*);
+
+ // Removes the given observer from the ThreadState's observer list. This
+ // doesn't take ownership of the argument. The argument must not be null.
+ // The argument must be registered before calling this.
+ void RemoveObserver(BlinkGCObserver*);
+
+ static WTF::ThreadSpecific<ThreadState*>* thread_specific_;
+
+ // We can't create a static member of type ThreadState here
+ // because it will introduce global constructor and destructor.
+ // We would like to manage lifetime of the ThreadState attached
+ // to the main thread explicitly instead and still use normal
+ // constructor and destructor for the ThreadState class.
+ // For this we reserve static storage for the main ThreadState
+ // and lazily construct ThreadState in it using placement new.
+ static uint8_t main_thread_state_storage_[];
+
+ std::unique_ptr<ThreadHeap> heap_;
+ ThreadIdentifier thread_;
+ std::unique_ptr<PersistentRegion> persistent_region_;
+ std::unique_ptr<PersistentRegion> weak_persistent_region_;
+ BlinkGC::StackState stack_state_;
+ intptr_t* start_of_stack_;
+ intptr_t* end_of_stack_;
+
+ void* safe_point_scope_marker_;
+ Vector<Address> safe_point_stack_copy_;
+ bool sweep_forbidden_;
+ size_t no_allocation_count_;
+ size_t gc_forbidden_count_;
+ size_t mixins_being_constructed_count_;
+ double accumulated_sweeping_time_;
+ bool object_resurrection_forbidden_;
+ bool in_atomic_pause_;
+
+ GarbageCollectedMixinConstructorMarkerBase* gc_mixin_marker_;
+
+ GCState gc_state_;
+ GCPhase gc_phase_;
+
+ using PreFinalizerCallback = bool (*)(void*);
+ using PreFinalizer = std::pair<void*, PreFinalizerCallback>;
+
+ // Pre-finalizers are called in the reverse order in which they are
+ // registered by the constructors (including constructors of Mixin objects)
+ // for an object, by processing the ordered_pre_finalizers_ back-to-front.
+ LinkedHashSet<PreFinalizer> ordered_pre_finalizers_;
+
+ v8::Isolate* isolate_;
+ void (*trace_dom_wrappers_)(v8::Isolate*, Visitor*);
+ void (*invalidate_dead_objects_in_wrappers_marking_deque_)(v8::Isolate*);
+ void (*perform_cleanup_)(v8::Isolate*);
+ bool wrapper_tracing_in_progress_;
+ bool incremental_marking_;
+
+#if defined(ADDRESS_SANITIZER)
+ void* asan_fake_stack_;
+#endif
+
+ HashSet<BlinkGCObserver*> observers_;
+
+ // PersistentNodes that are stored in static references;
+ // references that either have to be cleared upon the thread
+ // detaching from Oilpan and shutting down or references we
+ // have to clear before initiating LSan's leak detection.
+ HashMap<PersistentNode*, PersistentClearCallback> static_persistents_;
+
+#if defined(LEAK_SANITIZER)
+ // Count that controls scoped disabling of persistent registration.
+ size_t disabled_static_persistent_registration_;
+#endif
+
+ size_t reported_memory_to_v8_;
+
+ int gc_age_ = 0;
+
+ struct GCData {
+ BlinkGC::StackState stack_state;
+ BlinkGC::MarkingType marking_type;
+ BlinkGC::GCReason reason;
+ double marking_time_in_milliseconds;
+ size_t marked_object_size;
+ std::unique_ptr<MarkingVisitor> visitor;
+ };
+ GCData current_gc_data_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadState);
+};
+
+template <>
+class ThreadStateFor<kMainThreadOnly> {
+ STATIC_ONLY(ThreadStateFor);
+
+ public:
+ static ThreadState* GetState() {
+ // This specialization must only be used from the main thread.
+ DCHECK(ThreadState::Current()->IsMainThread());
+ return ThreadState::MainThreadState();
+ }
+};
+
+template <>
+class ThreadStateFor<kAnyThread> {
+ STATIC_ONLY(ThreadStateFor);
+
+ public:
+ static ThreadState* GetState() { return ThreadState::Current(); }
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_THREAD_STATE_H_
diff --git a/chromium/third_party/blink/renderer/platform/heap/threading_traits.h b/chromium/third_party/blink/renderer/platform/heap/threading_traits.h
new file mode 100644
index 00000000000..34a66f1ae5b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/threading_traits.h
@@ -0,0 +1,207 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_THREADING_TRAITS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_THREADING_TRAITS_H_
+
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/deque.h"
+#include "third_party/blink/renderer/platform/wtf/hash_counted_set.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/hash_table.h"
+#include "third_party/blink/renderer/platform/wtf/type_traits.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+template <typename T>
+class SameThreadCheckedMember;
+template <typename T>
+class TraceWrapperMember;
+
+// ThreadAffinity indicates which threads objects can be used on. We
+// distinguish between objects that can be used on the main thread
+// only and objects that can be used on any thread.
+//
+// For objects that can only be used on the main thread, we avoid going
+// through thread-local storage to get to the thread state. This is
+// important for performance.
+enum ThreadAffinity {
+ kAnyThread,
+ kMainThreadOnly,
+};
+
+// TODO(haraken): These forward declarations violate dependency rules.
+// Remove them.
+class Node;
+class NodeList;
+class NodeRareDataBase;
+
+template <
+ typename T,
+ bool mainThreadOnly =
+ WTF::IsSubclass<typename std::remove_const<T>::type, Node>::value ||
+ WTF::IsSubclass<typename std::remove_const<T>::type, NodeList>::value ||
+ WTF::IsSubclass<typename std::remove_const<T>::type,
+ NodeRareDataBase>::value>
+struct DefaultThreadingTrait;
+
+template <typename T>
+struct DefaultThreadingTrait<T, false> {
+ STATIC_ONLY(DefaultThreadingTrait);
+ static const ThreadAffinity kAffinity = kAnyThread;
+};
+
+template <typename T>
+struct DefaultThreadingTrait<T, true> {
+ STATIC_ONLY(DefaultThreadingTrait);
+ static const ThreadAffinity kAffinity = kMainThreadOnly;
+};
+
+class HeapAllocator;
+template <typename Table>
+class HeapHashTableBacking;
+template <typename T, typename Traits>
+class HeapVectorBacking;
+template <typename T>
+class Member;
+template <typename T>
+class WeakMember;
+
+template <typename T>
+struct ThreadingTrait {
+ STATIC_ONLY(ThreadingTrait);
+ static const ThreadAffinity kAffinity = DefaultThreadingTrait<T>::kAffinity;
+};
+
+template <typename U>
+class ThreadingTrait<const U> : public ThreadingTrait<U> {};
+
+template <typename T>
+struct ThreadingTrait<Member<T>> {
+ STATIC_ONLY(ThreadingTrait);
+ static const ThreadAffinity kAffinity = ThreadingTrait<T>::kAffinity;
+};
+
+template <typename T>
+struct ThreadingTrait<SameThreadCheckedMember<T>> {
+ STATIC_ONLY(ThreadingTrait);
+ static const ThreadAffinity kAffinity = ThreadingTrait<T>::Affinity;
+};
+
+template <typename T>
+struct ThreadingTrait<TraceWrapperMember<T>> {
+ STATIC_ONLY(ThreadingTrait);
+ static const ThreadAffinity kAffinity = ThreadingTrait<T>::kAffinity;
+};
+
+template <typename T>
+struct ThreadingTrait<WeakMember<T>> {
+ STATIC_ONLY(ThreadingTrait);
+ static const ThreadAffinity kAffinity = ThreadingTrait<T>::kAffinity;
+};
+
+template <typename Key, typename Value, typename T, typename U, typename V>
+struct ThreadingTrait<HashMap<Key, Value, T, U, V, HeapAllocator>> {
+ STATIC_ONLY(ThreadingTrait);
+ static const ThreadAffinity kAffinity =
+ (ThreadingTrait<Key>::kAffinity == kMainThreadOnly) &&
+ (ThreadingTrait<Value>::kAffinity == kMainThreadOnly)
+ ? kMainThreadOnly
+ : kAnyThread;
+};
+
+template <typename First, typename Second>
+struct ThreadingTrait<WTF::KeyValuePair<First, Second>> {
+ STATIC_ONLY(ThreadingTrait);
+ static const ThreadAffinity kAffinity =
+ (ThreadingTrait<First>::kAffinity == kMainThreadOnly) &&
+ (ThreadingTrait<Second>::kAffinity == kMainThreadOnly)
+ ? kMainThreadOnly
+ : kAnyThread;
+};
+
+template <typename T, typename U, typename V>
+struct ThreadingTrait<HashSet<T, U, V, HeapAllocator>> {
+ STATIC_ONLY(ThreadingTrait);
+ static const ThreadAffinity kAffinity = ThreadingTrait<T>::kAffinity;
+};
+
+template <typename T, size_t inlineCapacity>
+struct ThreadingTrait<Vector<T, inlineCapacity, HeapAllocator>> {
+ STATIC_ONLY(ThreadingTrait);
+ static const ThreadAffinity kAffinity = ThreadingTrait<T>::kAffinity;
+};
+
+template <typename T, typename Traits>
+struct ThreadingTrait<HeapVectorBacking<T, Traits>> {
+ STATIC_ONLY(ThreadingTrait);
+ static const ThreadAffinity kAffinity = ThreadingTrait<T>::Affinity;
+};
+
+template <typename T, size_t inlineCapacity>
+struct ThreadingTrait<Deque<T, inlineCapacity, HeapAllocator>> {
+ STATIC_ONLY(ThreadingTrait);
+ static const ThreadAffinity kAffinity = ThreadingTrait<T>::kAffinity;
+};
+
+template <typename T, typename U, typename V>
+struct ThreadingTrait<HashCountedSet<T, U, V, HeapAllocator>> {
+ STATIC_ONLY(ThreadingTrait);
+ static const ThreadAffinity kAffinity = ThreadingTrait<T>::kAffinity;
+};
+
+template <typename Table>
+struct ThreadingTrait<HeapHashTableBacking<Table>> {
+ STATIC_ONLY(ThreadingTrait);
+ using Key = typename Table::KeyType;
+ using Value = typename Table::ValueType;
+ static const ThreadAffinity kAffinity =
+ (ThreadingTrait<Key>::Affinity == kMainThreadOnly) &&
+ (ThreadingTrait<Value>::Affinity == kMainThreadOnly)
+ ? kMainThreadOnly
+ : kAnyThread;
+};
+
+template <typename T, typename U, typename V, typename W, typename X>
+class HeapHashMap;
+template <typename T, typename U, typename V>
+class HeapHashSet;
+template <typename T, size_t inlineCapacity>
+class HeapVector;
+template <typename T, size_t inlineCapacity>
+class HeapDeque;
+template <typename T, typename U, typename V>
+class HeapHashCountedSet;
+
+template <typename T, typename U, typename V, typename W, typename X>
+struct ThreadingTrait<HeapHashMap<T, U, V, W, X>>
+ : public ThreadingTrait<HashMap<T, U, V, W, X, HeapAllocator>> {
+ STATIC_ONLY(ThreadingTrait);
+};
+template <typename T, typename U, typename V>
+struct ThreadingTrait<HeapHashSet<T, U, V>>
+ : public ThreadingTrait<HashSet<T, U, V, HeapAllocator>> {
+ STATIC_ONLY(ThreadingTrait);
+};
+template <typename T, size_t inlineCapacity>
+struct ThreadingTrait<HeapVector<T, inlineCapacity>>
+ : public ThreadingTrait<Vector<T, inlineCapacity, HeapAllocator>> {
+ STATIC_ONLY(ThreadingTrait);
+};
+template <typename T, size_t inlineCapacity>
+struct ThreadingTrait<HeapDeque<T, inlineCapacity>>
+ : public ThreadingTrait<Deque<T, inlineCapacity, HeapAllocator>> {
+ STATIC_ONLY(ThreadingTrait);
+};
+template <typename T, typename U, typename V>
+struct ThreadingTrait<HeapHashCountedSet<T, U, V>>
+ : public ThreadingTrait<HashCountedSet<T, U, V, HeapAllocator>> {
+ STATIC_ONLY(ThreadingTrait);
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/heap/trace_traits.h b/chromium/third_party/blink/renderer/platform/heap/trace_traits.h
new file mode 100644
index 00000000000..a746a221aea
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/trace_traits.h
@@ -0,0 +1,817 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_TRACE_TRAITS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_TRACE_TRAITS_H_
+
+#include "third_party/blink/renderer/platform/bindings/script_wrappable_visitor.h"
+#include "third_party/blink/renderer/platform/heap/gc_info.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/heap/stack_frame_depth.h"
+#include "third_party/blink/renderer/platform/heap/visitor.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/hash_counted_set.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/hash_table.h"
+#include "third_party/blink/renderer/platform/wtf/linked_hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/list_hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+#include "third_party/blink/renderer/platform/wtf/type_traits.h"
+
+namespace blink {
+
+template <typename T>
+class CrossThreadPersistent;
+template <typename T>
+class CrossThreadWeakPersistent;
+template <typename T>
+class HeapDoublyLinkedList;
+template <typename T>
+class HeapTerminatedArray;
+template <typename T>
+class Member;
+template <typename T>
+class TraceEagerlyTrait;
+template <typename T>
+class TraceTrait;
+template <typename T>
+class WeakMember;
+template <typename T>
+class WeakPersistent;
+
+template <typename T, bool = NeedsAdjustPointer<T>::value>
+class AdjustPointerTrait;
+
+template <typename T>
+class AdjustPointerTrait<T, false> {
+ STATIC_ONLY(AdjustPointerTrait);
+
+ public:
+ static TraceDescriptor GetTraceDescriptor(void* self) {
+ return {self, TraceTrait<T>::Trace, TraceEagerlyTrait<T>::value};
+ }
+
+ static TraceWrapperDescriptor GetTraceWrapperDescriptor(void* self) {
+ return {self, TraceTrait<T>::TraceWrappers,
+ ScriptWrappableVisitor::MissedWriteBarrier<T>,
+ ScriptWrappableVisitor::NameCallback<T>};
+ }
+
+ static HeapObjectHeader* GetHeapObjectHeader(void* self) {
+#if DCHECK_IS_ON()
+ HeapObjectHeader::CheckFromPayload(self);
+#endif
+ return HeapObjectHeader::FromPayload(self);
+ }
+};
+
+template <typename T>
+class AdjustPointerTrait<T, true> {
+ STATIC_ONLY(AdjustPointerTrait);
+
+ public:
+ static TraceDescriptor GetTraceDescriptor(const T* self) {
+ DCHECK(self);
+ return self->GetTraceDescriptor();
+ }
+
+ static TraceWrapperDescriptor GetTraceWrapperDescriptor(const T* self) {
+ DCHECK(self);
+ return self->GetTraceWrapperDescriptor();
+ }
+
+ static HeapObjectHeader* GetHeapObjectHeader(const T* self) {
+ DCHECK(self);
+ return self->GetHeapObjectHeader();
+ }
+};
+
+template <typename T, bool isTraceable>
+struct TraceIfEnabled;
+
+template <typename T>
+struct TraceIfEnabled<T, false> {
+ STATIC_ONLY(TraceIfEnabled);
+ template <typename VisitorDispatcher>
+ static void Trace(VisitorDispatcher, T&) {
+ static_assert(!WTF::IsTraceable<T>::value, "T should not be traced");
+ }
+};
+
+template <typename T>
+struct TraceIfEnabled<T, true> {
+ STATIC_ONLY(TraceIfEnabled);
+ template <typename VisitorDispatcher>
+ static void Trace(VisitorDispatcher visitor, T& t) {
+ static_assert(WTF::IsTraceable<T>::value, "T should not be traced");
+ visitor->Trace(t);
+ }
+};
+
+template <WTF::WeakHandlingFlag weakness,
+ typename T,
+ typename Traits,
+ bool isTraceableInCollection =
+ WTF::IsTraceableInCollectionTrait<Traits>::value,
+ WTF::WeakHandlingFlag traitWeakHandling = Traits::kWeakHandlingFlag>
+struct TraceCollectionIfEnabled;
+
+template <WTF::WeakHandlingFlag weakness, typename T, typename Traits>
+struct TraceCollectionIfEnabled<weakness,
+ T,
+ Traits,
+ false,
+ WTF::kNoWeakHandling> {
+ STATIC_ONLY(TraceCollectionIfEnabled);
+
+ static bool IsAlive(T&) { return true; }
+
+ template <typename VisitorDispatcher>
+ static bool Trace(VisitorDispatcher, T&) {
+ static_assert(!WTF::IsTraceableInCollectionTrait<Traits>::value,
+ "T should not be traced");
+ return false;
+ }
+};
+
+template <typename T, typename Traits>
+struct TraceCollectionIfEnabled<WTF::kNoWeakHandling,
+ T,
+ Traits,
+ false,
+ WTF::kWeakHandling> {
+ STATIC_ONLY(TraceCollectionIfEnabled);
+ template <typename VisitorDispatcher>
+ static bool Trace(VisitorDispatcher visitor, T& t) {
+ return WTF::TraceInCollectionTrait<WTF::kNoWeakHandling, T, Traits>::Trace(
+ visitor, t);
+ }
+};
+
+template <WTF::WeakHandlingFlag weakness,
+ typename T,
+ typename Traits,
+ bool isTraceableInCollection,
+ WTF::WeakHandlingFlag traitWeakHandling>
+struct TraceCollectionIfEnabled {
+ STATIC_ONLY(TraceCollectionIfEnabled);
+
+ static bool IsAlive(T& traceable) {
+ return WTF::TraceInCollectionTrait<weakness, T, Traits>::IsAlive(traceable);
+ }
+
+ template <typename VisitorDispatcher>
+ static bool Trace(VisitorDispatcher visitor, T& t) {
+ static_assert(WTF::IsTraceableInCollectionTrait<Traits>::value ||
+ weakness == WTF::kWeakHandling,
+ "Traits should be traced");
+ return WTF::TraceInCollectionTrait<weakness, T, Traits>::Trace(visitor, t);
+ }
+};
+
+// The TraceTrait is used to specify how to trace and object for Oilpan and
+// wrapper tracing.
+//
+//
+// By default, the 'Trace' method implemented on an object itself is
+// used to trace the pointers to other heap objects inside the object.
+//
+// However, the TraceTrait can be specialized to use a different
+// implementation. A common case where a TraceTrait specialization is
+// needed is when multiple inheritance leads to pointers that are not
+// to the start of the object in the Blink garbage-collected heap. In
+// that case the pointer has to be adjusted before marking.
+template <typename T>
+class TraceTrait {
+ STATIC_ONLY(TraceTrait);
+
+ public:
+ static TraceDescriptor GetTraceDescriptor(void* self) {
+ return AdjustPointerTrait<T>::GetTraceDescriptor(static_cast<T*>(self));
+ }
+
+ static TraceWrapperDescriptor GetTraceWrapperDescriptor(void* self) {
+ return AdjustPointerTrait<T>::GetTraceWrapperDescriptor(
+ static_cast<T*>(self));
+ }
+
+ static HeapObjectHeader* GetHeapObjectHeader(void* self) {
+ return AdjustPointerTrait<T>::GetHeapObjectHeader(static_cast<T*>(self));
+ }
+
+ static void Trace(Visitor*, void* self);
+ static void TraceWrappers(ScriptWrappableVisitor*, void*);
+};
+
+template <typename T>
+class TraceTrait<const T> : public TraceTrait<T> {};
+
+template <typename T>
+void TraceTrait<T>::Trace(Visitor* visitor, void* self) {
+ static_assert(WTF::IsTraceable<T>::value, "T should not be traced");
+ static_cast<T*>(self)->Trace(visitor);
+}
+
+template <typename T>
+void TraceTrait<T>::TraceWrappers(ScriptWrappableVisitor* visitor, void* self) {
+ static_assert(sizeof(T), "type needs to be defined");
+ static_assert(IsGarbageCollectedType<T>::value,
+ "only objects deriving from GarbageCollected can be used");
+ visitor->DispatchTraceWrappers(static_cast<T*>(self));
+}
+
+template <typename T, typename Traits>
+struct TraceTrait<HeapVectorBacking<T, Traits>> {
+ STATIC_ONLY(TraceTrait);
+ using Backing = HeapVectorBacking<T, Traits>;
+
+ static TraceDescriptor GetTraceDescriptor(void* self) {
+ return {self, TraceTrait<Backing>::Trace,
+ TraceEagerlyTrait<Backing>::value};
+ }
+
+ template <typename VisitorDispatcher>
+ static void Trace(VisitorDispatcher visitor, void* self) {
+ static_assert(!WTF::IsWeak<T>::value,
+ "Weakness is not supported in HeapVector and HeapDeque");
+ if (WTF::IsTraceableInCollectionTrait<Traits>::value) {
+ WTF::TraceInCollectionTrait<WTF::kNoWeakHandling,
+ HeapVectorBacking<T, Traits>,
+ void>::Trace(visitor, self);
+ }
+ }
+};
+
+// The trace trait for the heap hashtable backing is used when we find a
+// direct pointer to the backing from the conservative stack scanner. This
+// normally indicates that there is an ongoing iteration over the table, and so
+// we disable weak processing of table entries. When the backing is found
+// through the owning hash table we mark differently, in order to do weak
+// processing.
+template <typename Table>
+struct TraceTrait<HeapHashTableBacking<Table>> {
+ STATIC_ONLY(TraceTrait);
+ using Backing = HeapHashTableBacking<Table>;
+ using Traits = typename Table::ValueTraits;
+
+ static TraceDescriptor GetTraceDescriptor(void* self) {
+ return {self, TraceTrait<Backing>::Trace,
+ TraceEagerlyTrait<Backing>::value};
+ }
+
+ template <typename VisitorDispatcher>
+ static void Trace(VisitorDispatcher visitor, void* self) {
+ if (WTF::IsTraceableInCollectionTrait<Traits>::value ||
+ Traits::kWeakHandlingFlag == WTF::kWeakHandling) {
+ WTF::TraceInCollectionTrait<WTF::kNoWeakHandling, Backing, void>::Trace(
+ visitor, self);
+ }
+ }
+};
+
+// This trace trait for std::pair will null weak members if their referent is
+// collected. If you have a collection that contain weakness it does not remove
+// entries from the collection that contain nulled weak members.
+template <typename T, typename U>
+class TraceTrait<std::pair<T, U>> {
+ STATIC_ONLY(TraceTrait);
+
+ public:
+ static const bool kFirstIsTraceable = WTF::IsTraceable<T>::value;
+ static const bool kSecondIsTraceable = WTF::IsTraceable<U>::value;
+ template <typename VisitorDispatcher>
+ static void Trace(VisitorDispatcher visitor, std::pair<T, U>* pair) {
+ TraceIfEnabled<T, kFirstIsTraceable>::Trace(visitor, pair->first);
+ TraceIfEnabled<U, kSecondIsTraceable>::Trace(visitor, pair->second);
+ }
+};
+
+// While using Optional<T> with garbage-collected types is generally disallowed
+// by the OptionalGarbageCollected check in blink_gc_plugin, garbage-collected
+// containers such as HeapVector are allowed and need to be traced.
+template <typename T>
+class TraceTrait<WTF::Optional<T>> {
+ STATIC_ONLY(TraceTrait);
+
+ public:
+ template <typename VisitorDispatcher>
+ static void Trace(VisitorDispatcher visitor, WTF::Optional<T>* optional) {
+ if (*optional != WTF::nullopt) {
+ TraceIfEnabled<T, WTF::IsTraceable<T>::value>::Trace(visitor,
+ optional->value());
+ }
+ }
+};
+
+// If eager tracing leads to excessively deep |trace()| call chains (and
+// the system stack usage that this brings), the marker implementation will
+// switch to using an explicit mark stack. Recursive and deep object graphs
+// are uncommon for Blink objects.
+//
+// A class type can opt out of eager tracing by declaring a TraceEagerlyTrait<>
+// specialization, mapping the trait's |value| to |false| (see the
+// WILL_NOT_BE_EAGERLY_TRACED_CLASS() macros below.) For Blink, this is done for
+// the small set of GCed classes that are directly recursive.
+//
+// The TraceEagerlyTrait<T> trait controls whether or not a class
+// (and its subclasses) should be eagerly traced or not.
+//
+// If |TraceEagerlyTrait<T>::value| is |true|, then the marker thread
+// should invoke |trace()| on not-yet-marked objects deriving from class T
+// right away, and not queue their trace callbacks on its marker stack,
+// which it will do if |value| is |false|.
+//
+// The trait can be declared to enable/disable eager tracing for a class T
+// and any of its subclasses, or just to the class T, but none of its
+// subclasses.
+//
+template <typename T>
+class TraceEagerlyTrait {
+ STATIC_ONLY(TraceEagerlyTrait);
+
+ public:
+ static const bool value = true;
+};
+
+// Disable eager tracing for TYPE, but not any of its subclasses.
+#define WILL_NOT_BE_EAGERLY_TRACED_CLASS(TYPE) \
+ template <> \
+ class TraceEagerlyTrait<TYPE> { \
+ STATIC_ONLY(TraceEagerlyTrait); \
+ \
+ public: \
+ static const bool value = false; \
+ }
+
+template <typename T>
+class TraceEagerlyTrait<Member<T>> {
+ STATIC_ONLY(TraceEagerlyTrait);
+
+ public:
+ static const bool value = TraceEagerlyTrait<T>::value;
+};
+
+template <typename T>
+class TraceEagerlyTrait<SameThreadCheckedMember<T>> {
+ STATIC_ONLY(TraceEagerlyTrait);
+
+ public:
+ static const bool value = TraceEagerlyTrait<T>::value;
+};
+
+template <typename T>
+class TraceEagerlyTrait<TraceWrapperMember<T>> {
+ STATIC_ONLY(TraceEagerlyTrait);
+
+ public:
+ static const bool value = TraceEagerlyTrait<T>::value;
+};
+
+template <typename T>
+class TraceEagerlyTrait<WeakMember<T>> {
+ STATIC_ONLY(TraceEagerlyTrait);
+
+ public:
+ static const bool value = TraceEagerlyTrait<T>::value;
+};
+
+template <typename T>
+class TraceEagerlyTrait<Persistent<T>> {
+ STATIC_ONLY(TraceEagerlyTrait);
+
+ public:
+ static const bool value = TraceEagerlyTrait<T>::value;
+};
+
+template <typename T>
+class TraceEagerlyTrait<WeakPersistent<T>> {
+ STATIC_ONLY(TraceEagerlyTrait);
+
+ public:
+ static const bool value = TraceEagerlyTrait<T>::value;
+};
+
+template <typename T>
+class TraceEagerlyTrait<CrossThreadPersistent<T>> {
+ STATIC_ONLY(TraceEagerlyTrait);
+
+ public:
+ static const bool value = TraceEagerlyTrait<T>::value;
+};
+
+template <typename T>
+class TraceEagerlyTrait<CrossThreadWeakPersistent<T>> {
+ STATIC_ONLY(TraceEagerlyTrait);
+
+ public:
+ static const bool value = TraceEagerlyTrait<T>::value;
+};
+
+template <typename T>
+class TraceEagerlyTrait<HeapTerminatedArray<T>> {
+ public:
+ static const bool value = TraceEagerlyTrait<T>::value;
+};
+
+template <typename T>
+class TraceEagerlyTrait<HeapDoublyLinkedList<T>> {
+ STATIC_ONLY(TraceEagerlyTrait);
+
+ public:
+ static const bool value = TraceEagerlyTrait<T>::value;
+};
+
+template <typename ValueArg, size_t inlineCapacity>
+class HeapListHashSetAllocator;
+template <typename T, size_t inlineCapacity>
+class TraceEagerlyTrait<
+ WTF::ListHashSetNode<T, HeapListHashSetAllocator<T, inlineCapacity>>> {
+ STATIC_ONLY(TraceEagerlyTrait);
+
+ public:
+ static const bool value = false;
+};
+
+template <typename T>
+struct TraceIfNeeded : public TraceIfEnabled<T, WTF::IsTraceable<T>::value> {
+ STATIC_ONLY(TraceIfNeeded);
+};
+
+} // namespace blink
+
+namespace WTF {
+
+// Helper used for tracing without weak handling in collections that support
+// weak handling. It dispatches to |TraceInCollection| if the provided trait
+// supports weak handling, and to |Trace| otherwise.
+template <typename T,
+ typename Traits,
+ WTF::WeakHandlingFlag traitsWeakness = Traits::kWeakHandlingFlag>
+struct TraceNoWeakHandlingInCollectionHelper;
+
+template <typename T, typename Traits>
+struct TraceNoWeakHandlingInCollectionHelper<T, Traits, kWeakHandling> {
+ template <typename VisitorDispatcher>
+ static bool Trace(VisitorDispatcher visitor, T& t) {
+ return Traits::TraceInCollection(visitor, t, kNoWeakHandling);
+ }
+};
+
+template <typename T, typename Traits>
+struct TraceNoWeakHandlingInCollectionHelper<T, Traits, kNoWeakHandling> {
+ template <typename VisitorDispatcher>
+ static bool Trace(VisitorDispatcher visitor, T& t) {
+ static_assert(IsTraceableInCollectionTrait<Traits>::value,
+ "T should not be traced");
+ visitor->Trace(t);
+ return false;
+ }
+};
+
+// Catch-all for types that have a way to trace that don't have special
+// handling for weakness in collections. This means that if this type
+// contains WeakMember fields, they will simply be zeroed, but the entry
+// will not be removed from the collection. This always happens for
+// things in vectors, which don't currently support special handling of
+// weak elements.
+template <typename T, typename Traits>
+struct TraceInCollectionTrait<kNoWeakHandling, T, Traits> {
+ static bool IsAlive(T& t) { return true; }
+
+ template <typename VisitorDispatcher>
+ static bool Trace(VisitorDispatcher visitor, T& t) {
+ return TraceNoWeakHandlingInCollectionHelper<T, Traits>::Trace(visitor, t);
+ }
+};
+
+// Catch-all for types that have HashTrait support for tracing with weakness.
+template <typename T, typename Traits>
+struct TraceInCollectionTrait<kWeakHandling, T, Traits> {
+ static bool IsAlive(T& value) { return Traits::IsAlive(value); }
+
+ template <typename VisitorDispatcher>
+ static bool Trace(VisitorDispatcher visitor, T& t) {
+ return Traits::TraceInCollection(visitor, t, kWeakHandling);
+ }
+};
+
+// This trace method is used only for on-stack HeapVectors found in
+// conservative scanning. On-heap HeapVectors are traced by Vector::trace.
+template <typename T, typename Traits>
+struct TraceInCollectionTrait<kNoWeakHandling,
+ blink::HeapVectorBacking<T, Traits>,
+ void> {
+ template <typename VisitorDispatcher>
+ static bool Trace(VisitorDispatcher visitor, void* self) {
+ // HeapVectorBacking does not know the exact size of the vector
+ // and just knows the capacity of the vector. Due to the constraint,
+ // HeapVectorBacking can support only the following objects:
+ //
+ // - An object that has a vtable. In this case, HeapVectorBacking
+ // traces only slots that are not zeroed out. This is because if
+ // the object has a vtable, the zeroed slot means that it is
+ // an unused slot (Remember that the unused slots are guaranteed
+ // to be zeroed out by VectorUnusedSlotClearer).
+ //
+ // - An object that can be initialized with memset. In this case,
+ // HeapVectorBacking traces all slots including unused slots.
+ // This is fine because the fact that the object can be initialized
+ // with memset indicates that it is safe to treat the zerod slot
+ // as a valid object.
+ static_assert(!IsTraceableInCollectionTrait<Traits>::value ||
+ Traits::kCanClearUnusedSlotsWithMemset ||
+ std::is_polymorphic<T>::value,
+ "HeapVectorBacking doesn't support objects that cannot be "
+ "cleared as unused with memset.");
+
+ // This trace method is instantiated for vectors where
+ // IsTraceableInCollectionTrait<Traits>::value is false, but the trace
+ // method should not be called. Thus we cannot static-assert
+ // IsTraceableInCollectionTrait<Traits>::value but should runtime-assert it.
+ DCHECK(IsTraceableInCollectionTrait<Traits>::value);
+
+ T* array = reinterpret_cast<T*>(self);
+ blink::HeapObjectHeader* header =
+ blink::HeapObjectHeader::FromPayload(self);
+ // Use the payload size as recorded by the heap to determine how many
+ // elements to trace.
+ size_t length = header->PayloadSize() / sizeof(T);
+#ifdef ANNOTATE_CONTIGUOUS_CONTAINER
+ // As commented above, HeapVectorBacking can trace unused slots
+ // (which are already zeroed out).
+ ANNOTATE_CHANGE_SIZE(array, length, 0, length);
+#endif
+ if (std::is_polymorphic<T>::value) {
+ char* pointer = reinterpret_cast<char*>(array);
+ for (unsigned i = 0; i < length; ++i) {
+ char* element = pointer + i * sizeof(T);
+ if (blink::VTableInitialized(element))
+ blink::TraceIfEnabled<
+ T, IsTraceableInCollectionTrait<Traits>::value>::Trace(visitor,
+ array[i]);
+ }
+ } else {
+ for (size_t i = 0; i < length; ++i)
+ blink::TraceIfEnabled<
+ T, IsTraceableInCollectionTrait<Traits>::value>::Trace(visitor,
+ array[i]);
+ }
+ return false;
+ }
+};
+
+// This trace method is used only for on-stack HeapHashTables found in
+// conservative scanning. On-heap HeapHashTables are traced by HashTable::trace.
+template <typename Table>
+struct TraceInCollectionTrait<kNoWeakHandling,
+ blink::HeapHashTableBacking<Table>,
+ void> {
+ using Value = typename Table::ValueType;
+ using Traits = typename Table::ValueTraits;
+
+ template <typename VisitorDispatcher>
+ static bool Trace(VisitorDispatcher visitor, void* self) {
+ static_assert(IsTraceableInCollectionTrait<Traits>::value ||
+ Traits::kWeakHandlingFlag == kWeakHandling,
+ "Table should not be traced");
+ Value* array = reinterpret_cast<Value*>(self);
+ blink::HeapObjectHeader* header =
+ blink::HeapObjectHeader::FromPayload(self);
+ // Use the payload size as recorded by the heap to determine how many
+ // elements to trace.
+ size_t length = header->PayloadSize() / sizeof(Value);
+ for (size_t i = 0; i < length; ++i) {
+ if (!HashTableHelper<Value, typename Table::ExtractorType,
+ typename Table::KeyTraitsType>::
+ IsEmptyOrDeletedBucket(array[i])) {
+ blink::TraceCollectionIfEnabled<kNoWeakHandling, Value, Traits>::Trace(
+ visitor, array[i]);
+ }
+ }
+ return false;
+ }
+};
+
+// This specialization of TraceInCollectionTrait is for the backing of
+// HeapListHashSet. This is for the case that we find a reference to the
+// backing from the stack. That probably means we have a GC while we are in a
+// ListHashSet method since normal API use does not put pointers to the backing
+// on the stack.
+template <typename NodeContents,
+ size_t inlineCapacity,
+ typename T,
+ typename U,
+ typename V,
+ typename W,
+ typename X,
+ typename Y>
+struct TraceInCollectionTrait<
+ kNoWeakHandling,
+ blink::HeapHashTableBacking<HashTable<
+ ListHashSetNode<NodeContents,
+ blink::HeapListHashSetAllocator<T, inlineCapacity>>*,
+ U,
+ V,
+ W,
+ X,
+ Y,
+ blink::HeapAllocator>>,
+ void> {
+ using Node =
+ ListHashSetNode<NodeContents,
+ blink::HeapListHashSetAllocator<T, inlineCapacity>>;
+ using Table = HashTable<Node*, U, V, W, X, Y, blink::HeapAllocator>;
+
+ template <typename VisitorDispatcher>
+ static bool Trace(VisitorDispatcher visitor, void* self) {
+ Node** array = reinterpret_cast<Node**>(self);
+ blink::HeapObjectHeader* header =
+ blink::HeapObjectHeader::FromPayload(self);
+ size_t length = header->PayloadSize() / sizeof(Node*);
+ for (size_t i = 0; i < length; ++i) {
+ if (!HashTableHelper<Node*, typename Table::ExtractorType,
+ typename Table::KeyTraitsType>::
+ IsEmptyOrDeletedBucket(array[i])) {
+ visitor->Trace(array[i]);
+ }
+ }
+ return false;
+ }
+};
+
+// Key value pairs, as used in HashMap. To disambiguate template choice we have
+// to have two versions, first the one with no special weak handling, then the
+// one with weak handling.
+template <typename Key, typename Value, typename Traits>
+struct TraceInCollectionTrait<kNoWeakHandling,
+ KeyValuePair<Key, Value>,
+ Traits> {
+ template <typename VisitorDispatcher>
+ static bool Trace(VisitorDispatcher visitor, KeyValuePair<Key, Value>& self) {
+ static_assert(IsTraceableInCollectionTrait<Traits>::value ||
+ Traits::kWeakHandlingFlag == WTF::kWeakHandling,
+ "T should not be traced");
+ blink::TraceCollectionIfEnabled<
+ kNoWeakHandling, Key, typename Traits::KeyTraits>::Trace(visitor,
+ self.key);
+ blink::TraceCollectionIfEnabled<
+ kNoWeakHandling, Value,
+ typename Traits::ValueTraits>::Trace(visitor, self.value);
+ return false;
+ }
+};
+
+template <typename Key, typename Value, typename Traits>
+struct TraceInCollectionTrait<kWeakHandling, KeyValuePair<Key, Value>, Traits> {
+ static constexpr bool kKeyIsWeak =
+ Traits::KeyTraits::kWeakHandlingFlag == kWeakHandling;
+ static constexpr bool kValueIsWeak =
+ Traits::ValueTraits::kWeakHandlingFlag == kWeakHandling;
+ static const bool kKeyHasStrongRefs =
+ IsTraceableInCollectionTrait<typename Traits::KeyTraits>::value;
+ static const bool kValueHasStrongRefs =
+ IsTraceableInCollectionTrait<typename Traits::ValueTraits>::value;
+
+ static bool IsAlive(KeyValuePair<Key, Value>& self) {
+ static_assert(!kKeyIsWeak || !kValueIsWeak || !kKeyHasStrongRefs ||
+ !kValueHasStrongRefs,
+ "this configuration is disallowed to avoid unexpected leaks");
+ if ((kValueIsWeak && !kKeyIsWeak) ||
+ (kValueIsWeak && kKeyIsWeak && !kValueHasStrongRefs)) {
+ // Check value first.
+ bool value_side_alive = blink::TraceCollectionIfEnabled<
+ Traits::ValueTraits::kWeakHandlingFlag, Value,
+ typename Traits::ValueTraits>::IsAlive(self.value);
+ if (!value_side_alive)
+ return false;
+ return blink::TraceCollectionIfEnabled<
+ Traits::KeyTraits::kWeakHandlingFlag, Key,
+ typename Traits::KeyTraits>::IsAlive(self.key);
+ }
+ // Check key first.
+ bool key_side_alive = blink::TraceCollectionIfEnabled<
+ Traits::KeyTraits::kWeakHandlingFlag, Key,
+ typename Traits::KeyTraits>::IsAlive(self.key);
+ if (!key_side_alive)
+ return false;
+ return blink::TraceCollectionIfEnabled<
+ Traits::ValueTraits::kWeakHandlingFlag, Value,
+ typename Traits::ValueTraits>::IsAlive(self.value);
+ }
+
+ template <typename VisitorDispatcher>
+ static bool Trace(VisitorDispatcher visitor, KeyValuePair<Key, Value>& self) {
+ // This is the core of the ephemeron-like functionality. If there is
+ // weakness on the key side then we first check whether there are
+ // dead weak pointers on that side, and if there are we don't mark the
+ // value side (yet). Conversely if there is weakness on the value side
+ // we check that first and don't mark the key side yet if we find dead
+ // weak pointers.
+ // Corner case: If there is weakness on both the key and value side,
+ // and there are also strong pointers on the both sides then we could
+ // unexpectedly leak. The scenario is that the weak pointer on the key
+ // side is alive, which causes the strong pointer on the key side to be
+ // marked. If that then results in the object pointed to by the weak
+ // pointer on the value side being marked live, then the whole
+ // key-value entry is leaked. To avoid unexpected leaking, we disallow
+ // this case, but if you run into this assert, please reach out to Blink
+ // reviewers, and we may relax it.
+ static_assert(!kKeyIsWeak || !kValueIsWeak || !kKeyHasStrongRefs ||
+ !kValueHasStrongRefs,
+ "this configuration is disallowed to avoid unexpected leaks");
+ if ((kValueIsWeak && !kKeyIsWeak) ||
+ (kValueIsWeak && kKeyIsWeak && !kValueHasStrongRefs)) {
+ // Check value first.
+ bool dead_weak_objects_found_on_value_side =
+ blink::TraceCollectionIfEnabled<
+ Traits::ValueTraits::kWeakHandlingFlag, Value,
+ typename Traits::ValueTraits>::Trace(visitor, self.value);
+ if (dead_weak_objects_found_on_value_side)
+ return true;
+ return blink::TraceCollectionIfEnabled<
+ Traits::KeyTraits::kWeakHandlingFlag, Key,
+ typename Traits::KeyTraits>::Trace(visitor, self.key);
+ }
+ // Check key first.
+ bool dead_weak_objects_found_on_key_side = blink::TraceCollectionIfEnabled<
+ Traits::KeyTraits::kWeakHandlingFlag, Key,
+ typename Traits::KeyTraits>::Trace(visitor, self.key);
+ if (dead_weak_objects_found_on_key_side)
+ return true;
+ return blink::TraceCollectionIfEnabled<
+ Traits::ValueTraits::kWeakHandlingFlag, Value,
+ typename Traits::ValueTraits>::Trace(visitor, self.value);
+ }
+};
+
+// Nodes used by LinkedHashSet. Again we need two versions to disambiguate the
+// template.
+template <typename Value, typename Allocator, typename Traits>
+struct TraceInCollectionTrait<kNoWeakHandling,
+ LinkedHashSetNode<Value, Allocator>,
+ Traits> {
+ static bool IsAlive(LinkedHashSetNode<Value, Allocator>& self) {
+ return TraceInCollectionTrait<
+ kNoWeakHandling, Value,
+ typename Traits::ValueTraits>::IsAlive(self.value_);
+ }
+
+ template <typename VisitorDispatcher>
+ static bool Trace(VisitorDispatcher visitor,
+ LinkedHashSetNode<Value, Allocator>& self) {
+ static_assert(IsTraceableInCollectionTrait<Traits>::value ||
+ Traits::kWeakHandlingFlag == WTF::kWeakHandling,
+ "T should not be traced");
+ return TraceInCollectionTrait<
+ kNoWeakHandling, Value,
+ typename Traits::ValueTraits>::Trace(visitor, self.value_);
+ }
+};
+
+template <typename Value, typename Allocator, typename Traits>
+struct TraceInCollectionTrait<kWeakHandling,
+ LinkedHashSetNode<Value, Allocator>,
+ Traits> {
+ static bool IsAlive(LinkedHashSetNode<Value, Allocator>& self) {
+ return TraceInCollectionTrait<
+ kWeakHandling, Value,
+ typename Traits::ValueTraits>::IsAlive(self.value_);
+ }
+
+ template <typename VisitorDispatcher>
+ static bool Trace(VisitorDispatcher visitor,
+ LinkedHashSetNode<Value, Allocator>& self) {
+ return TraceInCollectionTrait<
+ kWeakHandling, Value, typename Traits::ValueTraits>::Trace(visitor,
+ self.value_);
+ }
+};
+
+// ListHashSetNode pointers (a ListHashSet is implemented as a hash table of
+// these pointers).
+template <typename Value, size_t inlineCapacity, typename Traits>
+struct TraceInCollectionTrait<
+ kNoWeakHandling,
+ ListHashSetNode<Value,
+ blink::HeapListHashSetAllocator<Value, inlineCapacity>>*,
+ Traits> {
+ using Node =
+ ListHashSetNode<Value,
+ blink::HeapListHashSetAllocator<Value, inlineCapacity>>;
+
+ template <typename VisitorDispatcher>
+ static bool Trace(VisitorDispatcher visitor, Node* node) {
+ static_assert(IsTraceableInCollectionTrait<Traits>::value ||
+ Traits::kWeakHandlingFlag == WTF::kWeakHandling,
+ "T should not be traced");
+ visitor->Trace(node);
+ return false;
+ }
+};
+
+} // namespace WTF
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/heap/visitor.h b/chromium/third_party/blink/renderer/platform/heap/visitor.h
new file mode 100644
index 00000000000..159e40106f9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/visitor.h
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_VISITOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_VISITOR_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/hash_traits.h"
+#include "third_party/blink/renderer/platform/wtf/type_traits.h"
+
+namespace blink {
+
+template <typename T>
+class GarbageCollected;
+template <typename T>
+class TraceTrait;
+class ThreadState;
+class Visitor;
+template <typename T>
+class SameThreadCheckedMember;
+template <typename T>
+class TraceWrapperMember;
+
+// The TraceMethodDelegate is used to convert a trace method for type T to a
+// TraceCallback. This allows us to pass a type's trace method as a parameter
+// to the PersistentNode constructor. The PersistentNode constructor needs the
+// specific trace method due an issue with the Windows compiler which
+// instantiates even unused variables. This causes problems
+// in header files where we have only forward declarations of classes.
+template <typename T, void (T::*method)(Visitor*)>
+struct TraceMethodDelegate {
+ STATIC_ONLY(TraceMethodDelegate);
+ static void Trampoline(Visitor* visitor, void* self) {
+ (reinterpret_cast<T*>(self)->*method)(visitor);
+ }
+};
+
+// Visitor is used to traverse Oilpan's object graph.
+class PLATFORM_EXPORT Visitor {
+ public:
+ explicit Visitor(ThreadState* state) : state_(state) {}
+ virtual ~Visitor() = default;
+
+ inline ThreadState* State() const { return state_; }
+ inline ThreadHeap& Heap() const { return state_->Heap(); }
+
+ // Static visitor implementation forwarding to dynamic interface.
+
+ // Member version of the one-argument templated trace method.
+ template <typename T>
+ void Trace(const Member<T>& t) {
+ DCHECK(!t.IsHashTableDeletedValue());
+ Trace(t.Get());
+ }
+
+ template <typename T>
+ void Trace(const TraceWrapperMember<T>& t) {
+ Trace(*(static_cast<const Member<T>*>(&t)));
+ }
+
+ template <typename T>
+ void Trace(const SameThreadCheckedMember<T>& t) {
+ Trace(*(static_cast<const Member<T>*>(&t)));
+ }
+
+ // Fallback methods used only when we need to trace raw pointers of T. This is
+ // the case when a member is a union where we do not support members.
+ template <typename T>
+ void Trace(const T* t) {
+ Trace(const_cast<T*>(t));
+ }
+
+ template <typename T>
+ void Trace(T* t) {
+ static_assert(sizeof(T), "T must be fully defined");
+ static_assert(IsGarbageCollectedType<T>::value,
+ "T needs to be a garbage collected object");
+ if (!t)
+ return;
+ Visit(const_cast<void*>(reinterpret_cast<const void*>(t)),
+ TraceTrait<T>::GetTraceDescriptor(
+ const_cast<void*>(reinterpret_cast<const void*>(t))));
+ }
+
+ template <typename T>
+ void TraceBackingStoreStrongly(T* backing_store, T** backing_store_slot) {
+ static_assert(sizeof(T), "T must be fully defined");
+ static_assert(IsGarbageCollectedType<T>::value,
+ "T needs to be a garbage collected object");
+
+ if (!backing_store)
+ return;
+ VisitBackingStoreStrongly(reinterpret_cast<void*>(backing_store),
+ reinterpret_cast<void**>(backing_store_slot),
+ TraceTrait<T>::GetTraceDescriptor(
+ reinterpret_cast<void*>(backing_store)));
+ }
+
+ template <typename T>
+ void TraceBackingStoreWeakly(T* backing_store,
+ T** backing_store_slot,
+ WeakCallback callback,
+ void* parameter) {
+ static_assert(sizeof(T), "T must be fully defined");
+ static_assert(IsGarbageCollectedType<T>::value,
+ "T needs to be a garbage collected object");
+
+ if (!backing_store)
+ return;
+ VisitBackingStoreWeakly(reinterpret_cast<void*>(backing_store),
+ reinterpret_cast<void**>(backing_store_slot),
+ TraceTrait<T>::GetTraceDescriptor(
+ reinterpret_cast<void*>(backing_store)),
+ callback, parameter);
+ }
+
+ template <typename T>
+ void TraceBackingStoreOnly(T* backing_store, T** backing_store_slot) {
+ static_assert(sizeof(T), "T must be fully defined");
+ static_assert(IsGarbageCollectedType<T>::value,
+ "T needs to be a garbage collected object");
+
+ if (!backing_store)
+ return;
+ VisitBackingStoreOnly(reinterpret_cast<void*>(backing_store),
+ reinterpret_cast<void**>(backing_store_slot));
+ }
+
+ // WeakMember version of the templated trace method. It doesn't keep
+ // the traced thing alive, but will write null to the WeakMember later
+ // if the pointed-to object is dead. It's lying for this to be const,
+ // but the overloading resolver prioritizes constness too high when
+ // picking the correct overload, so all these trace methods have to have
+ // the same constness on their argument to allow the type to decide.
+ template <typename T>
+ void Trace(const WeakMember<T>& t) {
+ static_assert(sizeof(T), "T must be fully defined");
+ static_assert(IsGarbageCollectedType<T>::value,
+ "T needs to be a garbage collected object");
+
+ if (!t.Get())
+ return;
+
+ DCHECK(!t.IsHashTableDeletedValue());
+ VisitWeak(const_cast<void*>(reinterpret_cast<const void*>(t.Get())),
+ reinterpret_cast<void**>(
+ const_cast<typename std::remove_const<T>::type**>(t.Cell())),
+ TraceTrait<T>::GetTraceDescriptor(
+ const_cast<void*>(reinterpret_cast<const void*>(t.Get()))),
+ &HandleWeakCell<T>);
+ }
+
+ // Fallback trace method for part objects to allow individual trace methods
+ // to trace through a part object with visitor->trace(m_partObject). This
+ // takes a const argument, because otherwise it will match too eagerly: a
+ // non-const argument would match a non-const Vector<T>& argument better
+ // than the specialization that takes const Vector<T>&. For a similar reason,
+ // the other specializations take a const argument even though they are
+ // usually used with non-const arguments, otherwise this function would match
+ // too well.
+ template <typename T>
+ void Trace(const T& t) {
+ static_assert(sizeof(T), "T must be fully defined");
+ if (std::is_polymorphic<T>::value) {
+ intptr_t vtable = *reinterpret_cast<const intptr_t*>(&t);
+ if (!vtable)
+ return;
+ }
+ TraceTrait<T>::Trace(this, &const_cast<T&>(t));
+ }
+
+ // Registers a callback for custom weakness.
+ template <typename T, void (T::*method)(Visitor*)>
+ void RegisterWeakMembers(const T* obj) {
+ RegisterWeakCallback(const_cast<T*>(obj),
+ &TraceMethodDelegate<T, method>::Trampoline);
+ }
+
+ // Dynamic visitor interface.
+
+ // Visits an object through a strong reference.
+ virtual void Visit(void*, TraceDescriptor) = 0;
+
+ // Visits an object through a weak reference.
+ virtual void VisitWeak(void*, void**, TraceDescriptor, WeakCallback) = 0;
+
+ // Visitors for collection backing stores.
+ virtual void VisitBackingStoreStrongly(void*, void**, TraceDescriptor) = 0;
+ virtual void VisitBackingStoreWeakly(void*,
+ void**,
+ TraceDescriptor,
+ WeakCallback,
+ void*) = 0;
+ virtual void VisitBackingStoreOnly(void*, void**) = 0;
+
+ // Registers backing store pointers so that they can be moved and properly
+ // updated.
+ virtual void RegisterBackingStoreCallback(void* backing_store,
+ MovingObjectCallback,
+ void* callback_data) = 0;
+
+ // Used to register ephemeron callbacks.
+ virtual bool RegisterWeakTable(const void* closure,
+ EphemeronCallback iteration_callback) {
+ return false;
+ }
+
+ // |WeakCallback| will usually use |ObjectAliveTrait| to figure out liveness
+ // of any children of |closure|. Upon return from the callback all references
+ // to dead objects must have been purged. Any operation that extends the
+ // object graph, including allocation or reviving objects, is prohibited.
+ // Clearing out additional pointers is allowed. Note that removing elements
+ // from heap collections such as HeapHashSet can cause an allocation if the
+ // backing store requires resizing. These collections know how to deal with
+ // WeakMember elements though.
+ virtual void RegisterWeakCallback(void* closure, WeakCallback) = 0;
+
+ private:
+ template <typename T>
+ static void HandleWeakCell(Visitor* self, void*);
+
+ ThreadState* const state_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_VISITOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/heap/worklist.h b/chromium/third_party/blink/renderer/platform/heap/worklist.h
new file mode 100644
index 00000000000..c5d96b22fed
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/worklist.h
@@ -0,0 +1,420 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Copied and adopted from V8.
+//
+// Copyright 2017 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_WORKLIST_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_WORKLIST_H_
+
+#include <cstddef>
+#include <utility>
+
+#include "base/atomicops.h"
+#include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
+#include "base/logging.h"
+#include "base/synchronization/lock.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+// A concurrent worklist based on segments. Each tasks gets private
+// push and pop segments. Empty pop segments are swapped with their
+// corresponding push segments. Full push segments are published to a global
+// pool of segments and replaced with empty segments.
+//
+// Work stealing is best effort, i.e., there is no way to inform other tasks
+// of the need of items.
+template <typename EntryType, int segment_size, int max_tasks = 1>
+class Worklist {
+ USING_FAST_MALLOC(Worklist);
+ using WorklistType = Worklist<EntryType, segment_size, max_tasks>;
+
+ public:
+ class View {
+ public:
+ View(WorklistType* worklist, int task_id)
+ : worklist_(worklist), task_id_(task_id) {}
+
+ // Pushes an entry onto the worklist.
+ bool Push(EntryType entry) { return worklist_->Push(task_id_, entry); }
+
+ // Pops an entry from the worklist.
+ bool Pop(EntryType* entry) { return worklist_->Pop(task_id_, entry); }
+
+ // Returns true if the local portion of the worklist is empty.
+ bool IsLocalEmpty() const { return worklist_->IsLocalEmpty(task_id_); }
+
+ // Returns true if the worklist is empty. Can only be used from the main
+ // thread without concurrent access.
+ bool IsGlobalEmpty() { return worklist_->IsGlobalEmpty(); }
+
+ bool IsGlobalPoolEmpty() { return worklist_->IsGlobalPoolEmpty(); }
+
+ size_t LocalPushSegmentSize() const {
+ return worklist_->LocalPushSegmentSize(task_id_);
+ }
+
+ private:
+ WorklistType* const worklist_;
+ const int task_id_;
+ };
+
+ static constexpr size_t kSegmentCapacity = segment_size;
+
+ Worklist() : Worklist(max_tasks) {}
+
+ explicit Worklist(int num_tasks) : num_tasks_(num_tasks) {
+ CHECK_LE(num_tasks, max_tasks);
+ for (int i = 0; i < num_tasks_; i++) {
+ private_push_segment(i) = NewSegment();
+ private_pop_segment(i) = NewSegment();
+ }
+ }
+
+ ~Worklist() {
+ CHECK(IsGlobalEmpty());
+ for (int i = 0; i < num_tasks_; i++) {
+ DCHECK(private_push_segment(i));
+ DCHECK(private_pop_segment(i));
+ delete private_push_segment(i);
+ delete private_pop_segment(i);
+ }
+ }
+
+ bool Push(int task_id, EntryType entry) {
+ DCHECK_LT(task_id, num_tasks_);
+ DCHECK(private_push_segment(task_id));
+ if (!private_push_segment(task_id)->Push(entry)) {
+ PublishPushSegmentToGlobal(task_id);
+ bool success = private_push_segment(task_id)->Push(entry);
+ ANALYZER_ALLOW_UNUSED(success);
+ DCHECK(success);
+ }
+ return true;
+ }
+
+ bool Pop(int task_id, EntryType* entry) {
+ DCHECK_LT(task_id, num_tasks_);
+ DCHECK(private_pop_segment(task_id));
+ if (!private_pop_segment(task_id)->Pop(entry)) {
+ if (!private_push_segment(task_id)->IsEmpty()) {
+ Segment* tmp = private_pop_segment(task_id);
+ private_pop_segment(task_id) = private_push_segment(task_id);
+ private_push_segment(task_id) = tmp;
+ } else if (!StealPopSegmentFromGlobal(task_id)) {
+ return false;
+ }
+ bool success = private_pop_segment(task_id)->Pop(entry);
+ ANALYZER_ALLOW_UNUSED(success);
+ DCHECK(success);
+ }
+ return true;
+ }
+
+ bool IsLocalEmpty(int task_id) const {
+ return private_pop_segment(task_id)->IsEmpty() &&
+ private_push_segment(task_id)->IsEmpty();
+ }
+
+ bool IsGlobalPoolEmpty() { return global_pool_.IsEmpty(); }
+
+ bool IsGlobalEmpty() {
+ for (int i = 0; i < num_tasks_; i++) {
+ if (!IsLocalEmpty(i))
+ return false;
+ }
+ return global_pool_.IsEmpty();
+ }
+
+ size_t LocalSize(int task_id) const {
+ return private_pop_segment(task_id)->Size() +
+ private_push_segment(task_id)->Size();
+ }
+
+ size_t LocalPushSegmentSize(int task_id) const {
+ return private_push_segment(task_id)->Size();
+ }
+
+ // Clears all segments. Frees the global segment pool.
+ //
+ // Assumes that no other tasks are running.
+ void Clear() {
+ for (int i = 0; i < num_tasks_; i++) {
+ private_pop_segment(i)->Clear();
+ private_push_segment(i)->Clear();
+ }
+ global_pool_.Clear();
+ }
+
+ // Calls the specified callback on each element of the deques and replaces
+ // the element with the result of the callback.
+ // The signature of the callback is
+ // bool Callback(EntryType old, EntryType* new).
+ // If the callback returns |false| then the element is removed from the
+ // worklist. Otherwise the |new| entry is updated.
+ //
+ // Assumes that no other tasks are running.
+ template <typename Callback>
+ void Update(Callback callback) {
+ for (int i = 0; i < num_tasks_; i++) {
+ private_pop_segment(i)->Update(callback);
+ private_push_segment(i)->Update(callback);
+ }
+ global_pool_.Update(callback);
+ }
+
+ template <typename Callback>
+ void IterateGlobalPool(Callback callback) {
+ global_pool_.Iterate(callback);
+ }
+
+ void FlushToGlobal(int task_id) {
+ PublishPushSegmentToGlobal(task_id);
+ PublishPopSegmentToGlobal(task_id);
+ }
+
+ void MergeGlobalPool(Worklist* other) {
+ auto pair = other->global_pool_.Extract();
+ global_pool_.MergeList(pair.first, pair.second);
+ }
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(WorklistTest, SegmentCreate);
+ FRIEND_TEST_ALL_PREFIXES(WorklistTest, SegmentPush);
+ FRIEND_TEST_ALL_PREFIXES(WorklistTest, SegmentPushPop);
+ FRIEND_TEST_ALL_PREFIXES(WorklistTest, SegmentIsEmpty);
+ FRIEND_TEST_ALL_PREFIXES(WorklistTest, SegmentIsFull);
+ FRIEND_TEST_ALL_PREFIXES(WorklistTest, SegmentClear);
+ FRIEND_TEST_ALL_PREFIXES(WorklistTest, SegmentFullPushFails);
+ FRIEND_TEST_ALL_PREFIXES(WorklistTest, SegmentEmptyPopFails);
+ FRIEND_TEST_ALL_PREFIXES(WorklistTest, SegmentUpdateFalse);
+ FRIEND_TEST_ALL_PREFIXES(WorklistTest, SegmentUpdate);
+
+ class Segment {
+ USING_FAST_MALLOC(Segment);
+
+ public:
+ static const size_t kCapacity = kSegmentCapacity;
+
+ Segment() : index_(0) {}
+
+ bool Push(EntryType entry) {
+ if (IsFull())
+ return false;
+ entries_[index_++] = entry;
+ return true;
+ }
+
+ bool Pop(EntryType* entry) {
+ if (IsEmpty())
+ return false;
+ *entry = entries_[--index_];
+ return true;
+ }
+
+ size_t Size() const { return index_; }
+ bool IsEmpty() const { return index_ == 0; }
+ bool IsFull() const { return index_ == kCapacity; }
+ void Clear() { index_ = 0; }
+
+ template <typename Callback>
+ void Update(Callback callback) {
+ size_t new_index = 0;
+ for (size_t i = 0; i < index_; i++) {
+ if (callback(entries_[i], &entries_[new_index])) {
+ new_index++;
+ }
+ }
+ index_ = new_index;
+ }
+
+ template <typename Callback>
+ void Iterate(Callback callback) const {
+ for (size_t i = 0; i < index_; i++) {
+ callback(entries_[i]);
+ }
+ }
+
+ Segment* next() const { return next_; }
+ void set_next(Segment* segment) { next_ = segment; }
+
+ private:
+ Segment* next_;
+ size_t index_;
+ EntryType entries_[kCapacity];
+ };
+
+ struct PrivateSegmentHolder {
+ Segment* private_push_segment;
+ Segment* private_pop_segment;
+ char cache_line_padding[64];
+ };
+
+ class GlobalPool {
+ public:
+ GlobalPool() : top_(nullptr) {}
+
+ inline void Push(Segment* segment) {
+ base::AutoLock guard(lock_);
+ segment->set_next(top_);
+ set_top(segment);
+ }
+
+ inline bool Pop(Segment** segment) {
+ base::AutoLock guard(lock_);
+ if (top_) {
+ *segment = top_;
+ set_top(top_->next());
+ return true;
+ }
+ return false;
+ }
+
+ inline bool IsEmpty() {
+ return base::subtle::NoBarrier_Load(
+ reinterpret_cast<base::subtle::AtomicWord*>(&top_)) == 0;
+ }
+
+ void Clear() {
+ base::AutoLock guard(lock_);
+ Segment* current = top_;
+ while (current) {
+ Segment* tmp = current;
+ current = current->next();
+ delete tmp;
+ }
+ set_top(nullptr);
+ }
+
+ // See Worklist::Update.
+ template <typename Callback>
+ void Update(Callback callback) {
+ base::AutoLock guard(lock_);
+ Segment* prev = nullptr;
+ Segment* current = top_;
+ while (current) {
+ current->Update(callback);
+ if (current->IsEmpty()) {
+ if (!prev) {
+ top_ = current->next();
+ } else {
+ prev->set_next(current->next());
+ }
+ Segment* tmp = current;
+ current = current->next();
+ delete tmp;
+ } else {
+ prev = current;
+ current = current->next();
+ }
+ }
+ }
+
+ // See Worklist::Iterate.
+ template <typename Callback>
+ void Iterate(Callback callback) {
+ base::AutoLock guard(lock_);
+ for (Segment* current = top_; current; current = current->next()) {
+ current->Iterate(callback);
+ }
+ }
+
+ std::pair<Segment*, Segment*> Extract() {
+ Segment* top = nullptr;
+ {
+ base::AutoLock guard(lock_);
+ if (!top_)
+ return std::make_pair(nullptr, nullptr);
+ top = top_;
+ set_top(nullptr);
+ }
+ Segment* end = top;
+ while (end->next())
+ end = end->next();
+ return std::make_pair(top, end);
+ }
+
+ void MergeList(Segment* start, Segment* end) {
+ if (!start)
+ return;
+ {
+ base::AutoLock guard(lock_);
+ end->set_next(top_);
+ set_top(start);
+ }
+ }
+
+ private:
+ void set_top(Segment* segment) {
+ return base::subtle::NoBarrier_Store(
+ reinterpret_cast<base::subtle::AtomicWord*>(&top_),
+ reinterpret_cast<base::subtle::AtomicWord>(segment));
+ }
+
+ mutable base::Lock lock_;
+ Segment* top_;
+ };
+
+ ALWAYS_INLINE Segment*& private_push_segment(int task_id) {
+ return private_segments_[task_id].private_push_segment;
+ }
+
+ ALWAYS_INLINE Segment* const& private_push_segment(int task_id) const {
+ return const_cast<const PrivateSegmentHolder*>(private_segments_)[task_id]
+ .private_push_segment;
+ }
+
+ ALWAYS_INLINE Segment*& private_pop_segment(int task_id) {
+ return private_segments_[task_id].private_pop_segment;
+ }
+
+ ALWAYS_INLINE Segment* const& private_pop_segment(int task_id) const {
+ return const_cast<const PrivateSegmentHolder*>(private_segments_)[task_id]
+ .private_pop_segment;
+ }
+
+ ALWAYS_INLINE void PublishPushSegmentToGlobal(int task_id) {
+ if (!private_push_segment(task_id)->IsEmpty()) {
+ global_pool_.Push(private_push_segment(task_id));
+ private_push_segment(task_id) = NewSegment();
+ }
+ }
+
+ ALWAYS_INLINE void PublishPopSegmentToGlobal(int task_id) {
+ if (!private_pop_segment(task_id)->IsEmpty()) {
+ global_pool_.Push(private_pop_segment(task_id));
+ private_pop_segment(task_id) = NewSegment();
+ }
+ }
+
+ ALWAYS_INLINE bool StealPopSegmentFromGlobal(int task_id) {
+ if (global_pool_.IsEmpty())
+ return false;
+ Segment* new_segment = nullptr;
+ if (global_pool_.Pop(&new_segment)) {
+ delete private_pop_segment(task_id);
+ private_pop_segment(task_id) = new_segment;
+ return true;
+ }
+ return false;
+ }
+
+ ALWAYS_INLINE Segment* NewSegment() {
+ // Bottleneck for filtering in crash dumps.
+ return new Segment();
+ }
+
+ PrivateSegmentHolder private_segments_[max_tasks];
+ GlobalPool global_pool_;
+ int num_tasks_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_WORKLIST_H_
diff --git a/chromium/third_party/blink/renderer/platform/heap/worklist_test.cc b/chromium/third_party/blink/renderer/platform/heap/worklist_test.cc
new file mode 100644
index 00000000000..f563a75c199
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/worklist_test.cc
@@ -0,0 +1,337 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Copied and adopted from V8.
+//
+// Copyright 2017 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/heap/worklist.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+namespace {
+class SomeObject {};
+} // namespace
+
+using TestWorklist = Worklist<SomeObject*, 64 /* entries */, 8 /* tasks */>;
+
+TEST(WorklistTest, SegmentCreate) {
+ TestWorklist::Segment segment;
+ EXPECT_TRUE(segment.IsEmpty());
+ EXPECT_EQ(0u, segment.Size());
+ EXPECT_FALSE(segment.IsFull());
+}
+
+TEST(WorklistTest, SegmentPush) {
+ TestWorklist::Segment segment;
+ EXPECT_EQ(0u, segment.Size());
+ EXPECT_TRUE(segment.Push(nullptr));
+ EXPECT_EQ(1u, segment.Size());
+}
+
+TEST(WorklistTest, SegmentPushPop) {
+ TestWorklist::Segment segment;
+ EXPECT_TRUE(segment.Push(nullptr));
+ EXPECT_EQ(1u, segment.Size());
+ SomeObject dummy;
+ SomeObject* object = &dummy;
+ EXPECT_TRUE(segment.Pop(&object));
+ EXPECT_EQ(0u, segment.Size());
+ EXPECT_EQ(nullptr, object);
+}
+
+TEST(WorklistTest, SegmentIsEmpty) {
+ TestWorklist::Segment segment;
+ EXPECT_TRUE(segment.IsEmpty());
+ EXPECT_TRUE(segment.Push(nullptr));
+ EXPECT_FALSE(segment.IsEmpty());
+}
+
+TEST(WorklistTest, SegmentIsFull) {
+ TestWorklist::Segment segment;
+ EXPECT_FALSE(segment.IsFull());
+ for (size_t i = 0; i < TestWorklist::Segment::kCapacity; i++) {
+ EXPECT_TRUE(segment.Push(nullptr));
+ }
+ EXPECT_TRUE(segment.IsFull());
+}
+
+TEST(WorklistTest, SegmentClear) {
+ TestWorklist::Segment segment;
+ EXPECT_TRUE(segment.Push(nullptr));
+ EXPECT_FALSE(segment.IsEmpty());
+ segment.Clear();
+ EXPECT_TRUE(segment.IsEmpty());
+ for (size_t i = 0; i < TestWorklist::Segment::kCapacity; i++) {
+ EXPECT_TRUE(segment.Push(nullptr));
+ }
+}
+
+TEST(WorklistTest, SegmentFullPushFails) {
+ TestWorklist::Segment segment;
+ EXPECT_FALSE(segment.IsFull());
+ for (size_t i = 0; i < TestWorklist::Segment::kCapacity; i++) {
+ EXPECT_TRUE(segment.Push(nullptr));
+ }
+ EXPECT_TRUE(segment.IsFull());
+ EXPECT_FALSE(segment.Push(nullptr));
+}
+
+TEST(WorklistTest, SegmentEmptyPopFails) {
+ TestWorklist::Segment segment;
+ EXPECT_TRUE(segment.IsEmpty());
+ SomeObject* object;
+ EXPECT_FALSE(segment.Pop(&object));
+}
+
+TEST(WorklistTest, SegmentUpdateFalse) {
+ TestWorklist::Segment segment;
+ SomeObject* object;
+ object = reinterpret_cast<SomeObject*>(&object);
+ EXPECT_TRUE(segment.Push(object));
+ segment.Update([](SomeObject* object, SomeObject** out) { return false; });
+ EXPECT_TRUE(segment.IsEmpty());
+}
+
+TEST(WorklistTest, SegmentUpdate) {
+ TestWorklist::Segment segment;
+ SomeObject* objectA;
+ objectA = reinterpret_cast<SomeObject*>(&objectA);
+ SomeObject* objectB;
+ objectB = reinterpret_cast<SomeObject*>(&objectB);
+ EXPECT_TRUE(segment.Push(objectA));
+ segment.Update([objectB](SomeObject* object, SomeObject** out) {
+ *out = objectB;
+ return true;
+ });
+ SomeObject* object;
+ EXPECT_TRUE(segment.Pop(&object));
+ EXPECT_EQ(object, objectB);
+}
+
+TEST(WorklistTest, CreateEmpty) {
+ TestWorklist worklist;
+ TestWorklist::View worklist_view(&worklist, 0);
+ EXPECT_TRUE(worklist_view.IsLocalEmpty());
+ EXPECT_TRUE(worklist.IsGlobalEmpty());
+}
+
+TEST(WorklistTest, LocalPushPop) {
+ TestWorklist worklist;
+ TestWorklist::View worklist_view(&worklist, 0);
+ SomeObject dummy;
+ SomeObject* retrieved = nullptr;
+ EXPECT_TRUE(worklist_view.Push(&dummy));
+ EXPECT_FALSE(worklist_view.IsLocalEmpty());
+ EXPECT_TRUE(worklist_view.Pop(&retrieved));
+ EXPECT_EQ(&dummy, retrieved);
+}
+
+TEST(WorklistTest, LocalIsBasedOnId) {
+ TestWorklist worklist;
+ // Use the same id.
+ TestWorklist::View worklist_view1(&worklist, 0);
+ TestWorklist::View worklist_view2(&worklist, 0);
+ SomeObject dummy;
+ SomeObject* retrieved = nullptr;
+ EXPECT_TRUE(worklist_view1.Push(&dummy));
+ EXPECT_FALSE(worklist_view1.IsLocalEmpty());
+ EXPECT_FALSE(worklist_view2.IsLocalEmpty());
+ EXPECT_TRUE(worklist_view2.Pop(&retrieved));
+ EXPECT_EQ(&dummy, retrieved);
+ EXPECT_TRUE(worklist_view1.IsLocalEmpty());
+ EXPECT_TRUE(worklist_view2.IsLocalEmpty());
+}
+
+TEST(WorklistTest, LocalPushStaysPrivate) {
+ TestWorklist worklist;
+ TestWorklist::View worklist_view1(&worklist, 0);
+ TestWorklist::View worklist_view2(&worklist, 1);
+ SomeObject dummy;
+ SomeObject* retrieved = nullptr;
+ EXPECT_TRUE(worklist.IsGlobalEmpty());
+ EXPECT_TRUE(worklist_view1.Push(&dummy));
+ EXPECT_FALSE(worklist.IsGlobalEmpty());
+ EXPECT_FALSE(worklist_view2.Pop(&retrieved));
+ EXPECT_EQ(nullptr, retrieved);
+ EXPECT_TRUE(worklist_view1.Pop(&retrieved));
+ EXPECT_EQ(&dummy, retrieved);
+ EXPECT_TRUE(worklist.IsGlobalEmpty());
+}
+
+TEST(WorklistTest, GlobalUpdateNull) {
+ TestWorklist worklist;
+ TestWorklist::View worklist_view(&worklist, 0);
+ SomeObject* object;
+ object = reinterpret_cast<SomeObject*>(&object);
+ for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) {
+ EXPECT_TRUE(worklist_view.Push(object));
+ }
+ EXPECT_TRUE(worklist_view.Push(object));
+ worklist.Update([](SomeObject* object, SomeObject** out) { return false; });
+ EXPECT_TRUE(worklist.IsGlobalEmpty());
+}
+
+TEST(WorklistTest, GlobalUpdate) {
+ TestWorklist worklist;
+ TestWorklist::View worklist_view(&worklist, 0);
+ SomeObject* objectA = nullptr;
+ objectA = reinterpret_cast<SomeObject*>(&objectA);
+ SomeObject* objectB = nullptr;
+ objectB = reinterpret_cast<SomeObject*>(&objectB);
+ SomeObject* objectC = nullptr;
+ objectC = reinterpret_cast<SomeObject*>(&objectC);
+ for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) {
+ EXPECT_TRUE(worklist_view.Push(objectA));
+ }
+ for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) {
+ EXPECT_TRUE(worklist_view.Push(objectB));
+ }
+ EXPECT_TRUE(worklist_view.Push(objectA));
+ worklist.Update([objectA, objectC](SomeObject* object, SomeObject** out) {
+ if (object != objectA) {
+ *out = objectC;
+ return true;
+ }
+ return false;
+ });
+ for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) {
+ SomeObject* object;
+ EXPECT_TRUE(worklist_view.Pop(&object));
+ EXPECT_EQ(object, objectC);
+ }
+}
+
+TEST(WorklistTest, FlushToGlobalPushSegment) {
+ TestWorklist worklist;
+ TestWorklist::View worklist_view0(&worklist, 0);
+ TestWorklist::View worklist_view1(&worklist, 1);
+ SomeObject* object = nullptr;
+ SomeObject* objectA = nullptr;
+ objectA = reinterpret_cast<SomeObject*>(&objectA);
+ EXPECT_TRUE(worklist_view0.Push(objectA));
+ worklist.FlushToGlobal(0);
+ EXPECT_TRUE(worklist_view1.Pop(&object));
+}
+
+TEST(WorklistTest, FlushToGlobalPopSegment) {
+ TestWorklist worklist;
+ TestWorklist::View worklist_view0(&worklist, 0);
+ TestWorklist::View worklist_view1(&worklist, 1);
+ SomeObject* object = nullptr;
+ SomeObject* objectA = nullptr;
+ objectA = reinterpret_cast<SomeObject*>(&objectA);
+ EXPECT_TRUE(worklist_view0.Push(objectA));
+ EXPECT_TRUE(worklist_view0.Push(objectA));
+ EXPECT_TRUE(worklist_view0.Pop(&object));
+ worklist.FlushToGlobal(0);
+ EXPECT_TRUE(worklist_view1.Pop(&object));
+}
+
+TEST(WorklistTest, Clear) {
+ TestWorklist worklist;
+ TestWorklist::View worklist_view(&worklist, 0);
+ SomeObject* object;
+ object = reinterpret_cast<SomeObject*>(&object);
+ for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) {
+ EXPECT_TRUE(worklist_view.Push(object));
+ }
+ EXPECT_TRUE(worklist_view.Push(object));
+ worklist.Clear();
+ EXPECT_TRUE(worklist.IsGlobalEmpty());
+}
+
+TEST(WorklistTest, SingleSegmentSteal) {
+ TestWorklist worklist;
+ TestWorklist::View worklist_view1(&worklist, 0);
+ TestWorklist::View worklist_view2(&worklist, 1);
+ SomeObject dummy;
+ for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) {
+ EXPECT_TRUE(worklist_view1.Push(&dummy));
+ }
+ SomeObject* retrieved = nullptr;
+ // One more push/pop to publish the full segment.
+ EXPECT_TRUE(worklist_view1.Push(nullptr));
+ EXPECT_TRUE(worklist_view1.Pop(&retrieved));
+ EXPECT_EQ(nullptr, retrieved);
+ // Stealing.
+ for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) {
+ EXPECT_TRUE(worklist_view2.Pop(&retrieved));
+ EXPECT_EQ(&dummy, retrieved);
+ EXPECT_FALSE(worklist_view1.Pop(&retrieved));
+ }
+ EXPECT_TRUE(worklist.IsGlobalEmpty());
+}
+
+TEST(WorklistTest, MultipleSegmentsStolen) {
+ TestWorklist worklist;
+ TestWorklist::View worklist_view1(&worklist, 0);
+ TestWorklist::View worklist_view2(&worklist, 1);
+ TestWorklist::View worklist_view3(&worklist, 2);
+ SomeObject dummy1;
+ SomeObject dummy2;
+ for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) {
+ EXPECT_TRUE(worklist_view1.Push(&dummy1));
+ }
+ for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) {
+ EXPECT_TRUE(worklist_view1.Push(&dummy2));
+ }
+ SomeObject* retrieved = nullptr;
+ SomeObject dummy3;
+ // One more push/pop to publish the full segment.
+ EXPECT_TRUE(worklist_view1.Push(&dummy3));
+ EXPECT_TRUE(worklist_view1.Pop(&retrieved));
+ EXPECT_EQ(&dummy3, retrieved);
+ // Stealing.
+ EXPECT_TRUE(worklist_view2.Pop(&retrieved));
+ SomeObject* const expect_bag2 = retrieved;
+ EXPECT_TRUE(worklist_view3.Pop(&retrieved));
+ SomeObject* const expect_bag3 = retrieved;
+ EXPECT_NE(expect_bag2, expect_bag3);
+ EXPECT_TRUE(expect_bag2 == &dummy1 || expect_bag2 == &dummy2);
+ EXPECT_TRUE(expect_bag3 == &dummy1 || expect_bag3 == &dummy2);
+ for (size_t i = 1; i < TestWorklist::kSegmentCapacity; i++) {
+ EXPECT_TRUE(worklist_view2.Pop(&retrieved));
+ EXPECT_EQ(expect_bag2, retrieved);
+ EXPECT_FALSE(worklist_view1.Pop(&retrieved));
+ }
+ for (size_t i = 1; i < TestWorklist::kSegmentCapacity; i++) {
+ EXPECT_TRUE(worklist_view3.Pop(&retrieved));
+ EXPECT_EQ(expect_bag3, retrieved);
+ EXPECT_FALSE(worklist_view1.Pop(&retrieved));
+ }
+ EXPECT_TRUE(worklist.IsGlobalEmpty());
+}
+
+TEST(WorklistTest, MergeGlobalPool) {
+ TestWorklist worklist1;
+ TestWorklist::View worklist_view1(&worklist1, 0);
+ SomeObject dummy;
+ for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) {
+ EXPECT_TRUE(worklist_view1.Push(&dummy));
+ }
+ SomeObject* retrieved = nullptr;
+ // One more push/pop to publish the full segment.
+ EXPECT_TRUE(worklist_view1.Push(nullptr));
+ EXPECT_TRUE(worklist_view1.Pop(&retrieved));
+ EXPECT_EQ(nullptr, retrieved);
+ // Merging global pool into a new Worklist.
+ TestWorklist worklist2;
+ TestWorklist::View worklist_view2(&worklist2, 0);
+ worklist2.MergeGlobalPool(&worklist1);
+ EXPECT_FALSE(worklist2.IsGlobalEmpty());
+ for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) {
+ EXPECT_TRUE(worklist_view2.Pop(&retrieved));
+ EXPECT_EQ(&dummy, retrieved);
+ EXPECT_FALSE(worklist_view1.Pop(&retrieved));
+ }
+ EXPECT_TRUE(worklist1.IsGlobalEmpty());
+ EXPECT_TRUE(worklist2.IsGlobalEmpty());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/histogram.cc b/chromium/third_party/blink/renderer/platform/histogram.cc
new file mode 100644
index 00000000000..18ddfca5c99
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/histogram.cc
@@ -0,0 +1,63 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/histogram.h"
+
+#include "base/metrics/histogram.h"
+#include "base/metrics/sparse_histogram.h"
+
+namespace blink {
+
+CustomCountHistogram::CustomCountHistogram(const char* name,
+ base::HistogramBase::Sample min,
+ base::HistogramBase::Sample max,
+ int32_t bucket_count) {
+ histogram_ = base::Histogram::FactoryGet(
+ name, min, max, bucket_count,
+ base::HistogramBase::kUmaTargetedHistogramFlag);
+}
+
+CustomCountHistogram::CustomCountHistogram(base::HistogramBase* histogram)
+ : histogram_(histogram) {}
+
+void CustomCountHistogram::Count(base::HistogramBase::Sample sample) {
+ histogram_->Add(sample);
+}
+
+BooleanHistogram::BooleanHistogram(const char* name)
+ : CustomCountHistogram(base::BooleanHistogram::FactoryGet(
+ name,
+ base::HistogramBase::kUmaTargetedHistogramFlag)) {}
+
+EnumerationHistogram::EnumerationHistogram(
+ const char* name,
+ base::HistogramBase::Sample boundary_value)
+ : CustomCountHistogram(base::LinearHistogram::FactoryGet(
+ name,
+ 1,
+ boundary_value,
+ boundary_value + 1,
+ base::HistogramBase::kUmaTargetedHistogramFlag)) {}
+
+SparseHistogram::SparseHistogram(const char* name) {
+ histogram_ = base::SparseHistogram::FactoryGet(
+ name, base::HistogramBase::kUmaTargetedHistogramFlag);
+}
+
+void SparseHistogram::Sample(base::HistogramBase::Sample sample) {
+ histogram_->Add(sample);
+}
+
+LinearHistogram::LinearHistogram(const char* name,
+ base::HistogramBase::Sample min,
+ base::HistogramBase::Sample max,
+ int32_t bucket_count)
+ : CustomCountHistogram(base::LinearHistogram::FactoryGet(
+ name,
+ min,
+ max,
+ bucket_count,
+ base::HistogramBase::kUmaTargetedHistogramFlag)) {}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/histogram.h b/chromium/third_party/blink/renderer/platform/histogram.h
new file mode 100644
index 00000000000..fe3c09c920d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/histogram.h
@@ -0,0 +1,104 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HISTOGRAM_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HISTOGRAM_H_
+
+#include <stdint.h>
+#include "base/metrics/histogram_base.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/time/tick_clock.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace base {
+class HistogramBase;
+};
+
+namespace blink {
+
+class PLATFORM_EXPORT CustomCountHistogram {
+ public:
+ // Min values should be >=1 as emitted 0s still go into the underflow bucket.
+ CustomCountHistogram(const char* name,
+ base::HistogramBase::Sample min,
+ base::HistogramBase::Sample max,
+ int32_t bucket_count);
+ void Count(base::HistogramBase::Sample);
+
+ protected:
+ explicit CustomCountHistogram(base::HistogramBase*);
+
+ base::HistogramBase* histogram_;
+};
+
+class PLATFORM_EXPORT BooleanHistogram : public CustomCountHistogram {
+ public:
+ BooleanHistogram(const char* name);
+};
+
+class PLATFORM_EXPORT EnumerationHistogram : public CustomCountHistogram {
+ public:
+ // |boundaryValue| must be strictly greater than samples passed to |count|.
+ EnumerationHistogram(const char* name,
+ base::HistogramBase::Sample boundary_value);
+};
+
+class PLATFORM_EXPORT SparseHistogram {
+ public:
+ explicit SparseHistogram(const char* name);
+
+ void Sample(base::HistogramBase::Sample);
+
+ private:
+ base::HistogramBase* histogram_;
+};
+
+class PLATFORM_EXPORT LinearHistogram : public CustomCountHistogram {
+ public:
+ explicit LinearHistogram(const char* name,
+ base::HistogramBase::Sample min,
+ base::HistogramBase::Sample max,
+ int32_t bucket_count);
+};
+
+class PLATFORM_EXPORT ScopedUsHistogramTimer {
+ public:
+ explicit ScopedUsHistogramTimer(CustomCountHistogram& counter)
+ : start_time_(CurrentTimeTicks()), counter_(counter) {}
+
+ ~ScopedUsHistogramTimer() {
+ counter_.Count((CurrentTimeTicks() - start_time_).InMicroseconds());
+ }
+
+ private:
+ TimeTicks start_time_;
+ CustomCountHistogram& counter_;
+};
+
+#define SCOPED_BLINK_UMA_HISTOGRAM_TIMER_IMPL(name, allow_cross_thread) \
+ DEFINE_STATIC_LOCAL_IMPL(CustomCountHistogram, scoped_us_counter, \
+ (name, 0, 10000000, 50), allow_cross_thread); \
+ ScopedUsHistogramTimer timer(scoped_us_counter);
+
+// Use code like this to record time, in microseconds, to execute a block of
+// code:
+//
+// {
+// SCOPED_BLINK_UMA_HISTOGRAM_TIMER(myUmaStatName)
+// RunMyCode();
+// }
+// This macro records all times between 0us and 10 seconds.
+// Do not change this macro without renaming all metrics that use it!
+#define SCOPED_BLINK_UMA_HISTOGRAM_TIMER(name) \
+ SCOPED_BLINK_UMA_HISTOGRAM_TIMER_IMPL(name, false)
+
+// Thread-safe variant of SCOPED_BLINK_UMA_HISTOGRAM_TIMER.
+// Use if the histogram can be accessed by multiple threads.
+#define SCOPED_BLINK_UMA_HISTOGRAM_TIMER_THREAD_SAFE(name) \
+ SCOPED_BLINK_UMA_HISTOGRAM_TIMER_IMPL(name, true)
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HISTOGRAM_H_
diff --git a/chromium/third_party/blink/renderer/platform/histogram_test.cc b/chromium/third_party/blink/renderer/platform/histogram_test.cc
new file mode 100644
index 00000000000..ac514eed026
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/histogram_test.cc
@@ -0,0 +1,36 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/histogram.h"
+
+#include "base/metrics/histogram_samples.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/testing/wtf/scoped_mock_clock.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+class TestCustomCountHistogram : public CustomCountHistogram {
+ public:
+ TestCustomCountHistogram(const char* name,
+ base::HistogramBase::Sample min,
+ base::HistogramBase::Sample max,
+ int32_t bucket_count)
+ : CustomCountHistogram(name, min, max, bucket_count) {}
+
+ base::HistogramBase* Histogram() { return histogram_; }
+};
+
+TEST(ScopedUsHistogramTimerTest, Basic) {
+ TestCustomCountHistogram scoped_us_counter("test", 0, 10000000, 50);
+ {
+ WTF::ScopedMockClock clock;
+ ScopedUsHistogramTimer timer(scoped_us_counter);
+ clock.Advance(TimeDelta::FromMilliseconds(500));
+ }
+ // 500ms == 500000us
+ EXPECT_EQ(500000, scoped_us_counter.Histogram()->SnapshotSamples()->sum());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/DEPS b/chromium/third_party/blink/renderer/platform/image-decoders/DEPS
new file mode 100644
index 00000000000..3c137cbce31
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+cc/paint/image_animation_count.h",
+] \ No newline at end of file
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/OWNERS b/chromium/third_party/blink/renderer/platform/image-decoders/OWNERS
new file mode 100644
index 00000000000..9cf2710a1d5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/OWNERS
@@ -0,0 +1,6 @@
+urvang@chromium.org
+pkasting@chromium.org
+noel@chromium.org
+scroggo@chromium.org
+
+# COMPONENT: Internals>Images>Codecs
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_decoder.cc b/chromium/third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_decoder.cc
new file mode 100644
index 00000000000..613de9cc507
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_decoder.cc
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2008, 2009, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_decoder.h"
+
+#include "third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_reader.h"
+#include "third_party/blink/renderer/platform/image-decoders/fast_shared_buffer_reader.h"
+
+namespace blink {
+
+// Number of bits in .BMP used to store the file header (doesn't match
+// "sizeof(BMPImageDecoder::BitmapFileHeader)" since we omit some fields and
+// don't pack).
+static const size_t kSizeOfFileHeader = 14;
+
+BMPImageDecoder::BMPImageDecoder(AlphaOption alpha_option,
+ const ColorBehavior& color_behavior,
+ size_t max_decoded_bytes)
+ : ImageDecoder(alpha_option, color_behavior, max_decoded_bytes),
+ decoded_offset_(0) {}
+
+BMPImageDecoder::~BMPImageDecoder() = default;
+
+void BMPImageDecoder::OnSetData(SegmentReader* data) {
+ if (reader_)
+ reader_->SetData(data);
+}
+
+bool BMPImageDecoder::SetFailed() {
+ reader_.reset();
+ return ImageDecoder::SetFailed();
+}
+
+void BMPImageDecoder::Decode(bool only_size) {
+ if (Failed())
+ return;
+
+ // If we couldn't decode the image but we've received all the data, decoding
+ // has failed.
+ if (!DecodeHelper(only_size) && IsAllDataReceived())
+ SetFailed();
+ // If we're done decoding the image, we don't need the BMPImageReader
+ // anymore. (If we failed, |reader_| has already been cleared.)
+ else if (!frame_buffer_cache_.IsEmpty() &&
+ (frame_buffer_cache_.front().GetStatus() ==
+ ImageFrame::kFrameComplete))
+ reader_.reset();
+}
+
+bool BMPImageDecoder::DecodeHelper(bool only_size) {
+ size_t img_data_offset = 0;
+ if ((decoded_offset_ < kSizeOfFileHeader) &&
+ !ProcessFileHeader(img_data_offset))
+ return false;
+
+ if (!reader_) {
+ reader_ = std::make_unique<BMPImageReader>(this, decoded_offset_,
+ img_data_offset, false);
+ reader_->SetData(data_.get());
+ }
+
+ if (!frame_buffer_cache_.IsEmpty())
+ reader_->SetBuffer(&frame_buffer_cache_.front());
+
+ return reader_->DecodeBMP(only_size);
+}
+
+bool BMPImageDecoder::ProcessFileHeader(size_t& img_data_offset) {
+ // Read file header.
+ DCHECK(!decoded_offset_);
+ if (data_->size() < kSizeOfFileHeader)
+ return false;
+
+ char buffer[kSizeOfFileHeader];
+ FastSharedBufferReader fast_reader(data_);
+ const char* file_header =
+ fast_reader.GetConsecutiveData(0, kSizeOfFileHeader, buffer);
+ const uint16_t file_type =
+ (file_header[0] << 8) | static_cast<uint8_t>(file_header[1]);
+ img_data_offset = BMPImageReader::ReadUint32(&file_header[10]);
+ decoded_offset_ = kSizeOfFileHeader;
+
+ // See if this is a bitmap filetype we understand.
+ enum {
+ BMAP = 0x424D, // "BM"
+ // The following additional OS/2 2.x header values (see
+ // http://www.fileformat.info/format/os2bmp/egff.htm ) aren't widely
+ // decoded, and are unlikely to be in much use.
+ /*
+ ICON = 0x4943, // "IC"
+ POINTER = 0x5054, // "PT"
+ COLORICON = 0x4349, // "CI"
+ COLORPOINTER = 0x4350, // "CP"
+ BITMAPARRAY = 0x4241, // "BA"
+ */
+ };
+ return (file_type == BMAP) || SetFailed();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_decoder.h b/chromium/third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_decoder.h
new file mode 100644
index 00000000000..628069378b4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_decoder.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2008, 2009, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_BMP_BMP_IMAGE_DECODER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_BMP_BMP_IMAGE_DECODER_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h"
+
+namespace blink {
+
+class BMPImageReader;
+
+// This class decodes the BMP image format.
+class PLATFORM_EXPORT BMPImageDecoder final : public ImageDecoder {
+ public:
+ BMPImageDecoder(AlphaOption, const ColorBehavior&, size_t max_decoded_bytes);
+
+ ~BMPImageDecoder() override;
+
+ // ImageDecoder:
+ String FilenameExtension() const override { return "bmp"; }
+ void OnSetData(SegmentReader*) override;
+ // CAUTION: SetFailed() deletes |reader_|. Be careful to avoid
+ // accessing deleted memory, especially when calling this from inside
+ // BMPImageReader!
+ bool SetFailed() override;
+
+ private:
+ // ImageDecoder:
+ void DecodeSize() override { Decode(true); }
+ void Decode(size_t) override { Decode(false); }
+
+ // Decodes the image. If |only_size| is true, stops decoding after
+ // calculating the image size. If decoding fails but there is no more
+ // data coming, sets the "decode failure" flag.
+ void Decode(bool only_size);
+
+ // Decodes the image. If |only_size| is true, stops decoding after
+ // calculating the image size. Returns whether decoding succeeded.
+ bool DecodeHelper(bool only_size);
+
+ // Processes the file header at the beginning of the data. Sets
+ // |img_data_offset| based on the header contents. Returns true if the
+ // file header could be decoded.
+ bool ProcessFileHeader(size_t& img_data_offset);
+
+ // An index into |data_| representing how much we've already decoded.
+ // Note that this only tracks data _this_ class decodes; once the
+ // BMPImageReader takes over this will not be updated further.
+ size_t decoded_offset_;
+
+ // The reader used to do most of the BMP decoding.
+ std::unique_ptr<BMPImageReader> reader_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_decoder_test.cc b/chromium/third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_decoder_test.cc
new file mode 100644
index 00000000000..ec92a5b0469
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_decoder_test.cc
@@ -0,0 +1,98 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_decoder.h"
+
+#include <memory>
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/image-decoders/image_decoder_test_helpers.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+
+namespace blink {
+
+namespace {
+
+std::unique_ptr<ImageDecoder> CreateBMPDecoder() {
+ return std::make_unique<BMPImageDecoder>(
+ ImageDecoder::kAlphaNotPremultiplied, ColorBehavior::TransformToSRGB(),
+ ImageDecoder::kNoDecodedImageByteLimit);
+}
+
+} // anonymous namespace
+
+TEST(BMPImageDecoderTest, isSizeAvailable) {
+ static constexpr char kBmpFile[] = "/images/resources/lenna.bmp"; // 256x256
+ scoped_refptr<SharedBuffer> data = ReadFile(kBmpFile);
+ ASSERT_TRUE(data.get());
+
+ std::unique_ptr<ImageDecoder> decoder = CreateBMPDecoder();
+ decoder->SetData(data.get(), true);
+ EXPECT_TRUE(decoder->IsSizeAvailable());
+ EXPECT_EQ(256, decoder->Size().Width());
+ EXPECT_EQ(256, decoder->Size().Height());
+}
+
+TEST(BMPImageDecoderTest, parseAndDecode) {
+ static constexpr char kBmpFile[] = "/images/resources/lenna.bmp"; // 256x256
+ scoped_refptr<SharedBuffer> data = ReadFile(kBmpFile);
+ ASSERT_TRUE(data.get());
+
+ std::unique_ptr<ImageDecoder> decoder = CreateBMPDecoder();
+ decoder->SetData(data.get(), true);
+
+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(0);
+ ASSERT_TRUE(frame);
+ EXPECT_EQ(ImageFrame::kFrameComplete, frame->GetStatus());
+ EXPECT_EQ(256, frame->Bitmap().width());
+ EXPECT_EQ(256, frame->Bitmap().height());
+ EXPECT_FALSE(decoder->Failed());
+}
+
+// Test if a BMP decoder returns a proper error while decoding an empty image.
+TEST(BMPImageDecoderTest, emptyImage) {
+ static constexpr char kBmpFile[] = "/images/resources/0x0.bmp"; // 0x0
+ scoped_refptr<SharedBuffer> data = ReadFile(kBmpFile);
+ ASSERT_TRUE(data.get());
+
+ std::unique_ptr<ImageDecoder> decoder = CreateBMPDecoder();
+ decoder->SetData(data.get(), true);
+
+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(0);
+ ASSERT_TRUE(frame);
+ EXPECT_EQ(ImageFrame::kFrameEmpty, frame->GetStatus());
+ EXPECT_TRUE(decoder->Failed());
+}
+
+TEST(BMPImageDecoderTest, int32MinHeight) {
+ static constexpr char kBmpFile[] =
+ "/images/resources/1xint32_min.bmp"; // 0xINT32_MIN
+ scoped_refptr<SharedBuffer> data = ReadFile(kBmpFile);
+ std::unique_ptr<ImageDecoder> decoder = CreateBMPDecoder();
+ // Test when not all data is received.
+ decoder->SetData(data.get(), false);
+ EXPECT_FALSE(decoder->IsSizeAvailable());
+ EXPECT_TRUE(decoder->Failed());
+}
+
+// This test verifies that calling SharedBuffer::MergeSegmentsIntoBuffer() does
+// not break BMP decoding at a critical point: in between a call to decode the
+// size (when BMPImageDecoder stops while it may still have input data to
+// read) and a call to do a full decode.
+TEST(BMPImageDecoderTest, mergeBuffer) {
+ static constexpr char kBmpFile[] = "/images/resources/lenna.bmp";
+ TestMergeBuffer(&CreateBMPDecoder, kBmpFile);
+}
+
+// Verify that decoding this image does not crash.
+TEST(BMPImageDecoderTest, crbug752898) {
+ static constexpr char kBmpFile[] = "/images/resources/crbug752898.bmp";
+ scoped_refptr<SharedBuffer> data = ReadFile(kBmpFile);
+ ASSERT_TRUE(data.get());
+
+ std::unique_ptr<ImageDecoder> decoder = CreateBMPDecoder();
+ decoder->SetData(data.get(), true);
+ decoder->DecodeFrameBufferAtIndex(0);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_reader.cc b/chromium/third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_reader.cc
new file mode 100644
index 00000000000..0320face151
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_reader.cc
@@ -0,0 +1,857 @@
+/*
+ * Copyright (c) 2008, 2009, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_reader.h"
+
+namespace {
+
+// See comments on lookup_table_addresses_ in the header.
+const uint8_t nBitTo8BitlookupTable[] = {
+ // 1 bit
+ 0, 255,
+ // 2 bits
+ 0, 85, 170, 255,
+ // 3 bits
+ 0, 36, 73, 109, 146, 182, 219, 255,
+ // 4 bits
+ 0, 17, 34, 51, 68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255,
+ // 5 bits
+ 0, 8, 16, 25, 33, 41, 49, 58, 66, 74, 82, 90, 99, 107, 115, 123, 132, 140,
+ 148, 156, 165, 173, 181, 189, 197, 206, 214, 222, 230, 239, 247, 255,
+ // 6 bits
+ 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 45, 49, 53, 57, 61, 65, 69, 73, 77,
+ 81, 85, 89, 93, 97, 101, 105, 109, 113, 117, 121, 125, 130, 134, 138, 142,
+ 146, 150, 154, 158, 162, 166, 170, 174, 178, 182, 186, 190, 194, 198, 202,
+ 206, 210, 215, 219, 223, 227, 231, 235, 239, 243, 247, 251, 255,
+ // 7 bits
+ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38,
+ 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76,
+ 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110,
+ 112, 114, 116, 118, 120, 122, 124, 126, 129, 131, 133, 135, 137, 139, 141,
+ 143, 145, 147, 149, 151, 153, 155, 157, 159, 161, 163, 165, 167, 169, 171,
+ 173, 175, 177, 179, 181, 183, 185, 187, 189, 191, 193, 195, 197, 199, 201,
+ 203, 205, 207, 209, 211, 213, 215, 217, 219, 221, 223, 225, 227, 229, 231,
+ 233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255,
+};
+
+} // namespace
+
+namespace blink {
+
+BMPImageReader::BMPImageReader(ImageDecoder* parent,
+ size_t decoded_and_header_offset,
+ size_t img_data_offset,
+ bool is_in_ico)
+ : parent_(parent),
+ buffer_(nullptr),
+ fast_reader_(nullptr),
+ decoded_offset_(decoded_and_header_offset),
+ header_offset_(decoded_and_header_offset),
+ img_data_offset_(img_data_offset),
+ is_os21x_(false),
+ is_os22x_(false),
+ is_top_down_(false),
+ need_to_process_bitmasks_(false),
+ need_to_process_color_table_(false),
+ seen_non_zero_alpha_pixel_(false),
+ seen_zero_alpha_pixel_(false),
+ is_in_ico_(is_in_ico),
+ decoding_and_mask_(false) {
+ // Clue-in decodeBMP() that we need to detect the correct info header size.
+ memset(&info_header_, 0, sizeof(info_header_));
+}
+
+bool BMPImageReader::DecodeBMP(bool only_size) {
+ // Defensively clear the FastSharedBufferReader's cache, as another caller
+ // may have called SharedBuffer::MergeSegmentsIntoBuffer().
+ fast_reader_.ClearCache();
+
+ // Calculate size of info header.
+ if (!info_header_.bi_size && !ReadInfoHeaderSize())
+ return false;
+
+ const size_t header_end = header_offset_ + info_header_.bi_size;
+ // Read and process info header.
+ if ((decoded_offset_ < header_end) && !ProcessInfoHeader())
+ return false;
+
+ // ProcessInfoHeader() set the size, so if that's all we needed, we're done.
+ if (only_size)
+ return true;
+
+ // Read and process the bitmasks, if needed.
+ if (need_to_process_bitmasks_ && !ProcessBitmasks())
+ return false;
+
+ // Read and process the color table, if needed.
+ if (need_to_process_color_table_ && !ProcessColorTable())
+ return false;
+
+ // Initialize the framebuffer if needed.
+ DCHECK(buffer_); // Parent should set this before asking us to decode!
+ if (buffer_->GetStatus() == ImageFrame::kFrameEmpty) {
+ if (!buffer_->AllocatePixelData(parent_->Size().Width(),
+ parent_->Size().Height(),
+ parent_->ColorSpaceForSkImages())) {
+ return parent_->SetFailed(); // Unable to allocate.
+ }
+ buffer_->ZeroFillPixelData();
+ buffer_->SetStatus(ImageFrame::kFramePartial);
+ // SetSize() calls EraseARGB(), which resets the alpha flag, so we force
+ // it back to false here. We'll set it true below in all cases where
+ // these 0s could actually show through.
+ buffer_->SetHasAlpha(false);
+
+ // For BMPs, the frame always fills the entire image.
+ buffer_->SetOriginalFrameRect(IntRect(IntPoint(), parent_->Size()));
+
+ if (!is_top_down_)
+ coord_.SetY(parent_->Size().Height() - 1);
+ }
+
+ // Decode the data.
+ if (!decoding_and_mask_ && !PastEndOfImage(0) &&
+ !DecodePixelData((info_header_.bi_compression != RLE4) &&
+ (info_header_.bi_compression != RLE8) &&
+ (info_header_.bi_compression != RLE24)))
+ return false;
+
+ // If the image has an AND mask and there was no alpha data, process the
+ // mask.
+ if (is_in_ico_ && !decoding_and_mask_ &&
+ ((info_header_.bi_bit_count < 16) || !bit_masks_[3] ||
+ !seen_non_zero_alpha_pixel_)) {
+ // Reset decoding coordinates to start of image.
+ coord_.SetX(0);
+ coord_.SetY(is_top_down_ ? 0 : (parent_->Size().Height() - 1));
+
+ // The AND mask is stored as 1-bit data.
+ info_header_.bi_bit_count = 1;
+
+ decoding_and_mask_ = true;
+ }
+ if (decoding_and_mask_ && !DecodePixelData(true))
+ return false;
+
+ // Done!
+ buffer_->SetStatus(ImageFrame::kFrameComplete);
+ return true;
+}
+
+bool BMPImageReader::DecodePixelData(bool non_rle) {
+ const IntPoint coord(coord_);
+ const ProcessingResult result =
+ non_rle ? ProcessNonRLEData(false, 0) : ProcessRLEData();
+ if (coord_ != coord)
+ buffer_->SetPixelsChanged(true);
+ return (result == kFailure) ? parent_->SetFailed() : (result == kSuccess);
+}
+
+bool BMPImageReader::ReadInfoHeaderSize() {
+ // Get size of info header.
+ DCHECK_EQ(decoded_offset_, header_offset_);
+ if ((decoded_offset_ > data_->size()) ||
+ ((data_->size() - decoded_offset_) < 4))
+ return false;
+ info_header_.bi_size = ReadUint32(0);
+ // Don't increment decoded_offset here, it just makes the code in
+ // ProcessInfoHeader() more confusing.
+
+ // Don't allow the header to overflow (which would be harmless here, but
+ // problematic or at least confusing in other places), or to overrun the
+ // image data.
+ const size_t header_end = header_offset_ + info_header_.bi_size;
+ if ((header_end < header_offset_) ||
+ (img_data_offset_ && (img_data_offset_ < header_end)))
+ return parent_->SetFailed();
+
+ // See if this is a header size we understand:
+ // OS/2 1.x: 12
+ if (info_header_.bi_size == 12)
+ is_os21x_ = true;
+ // Windows V3: 40
+ else if ((info_header_.bi_size == 40) || IsWindowsV4Plus())
+ ;
+ // OS/2 2.x: any multiple of 4 between 16 and 64, inclusive, or 42 or 46
+ else if ((info_header_.bi_size >= 16) && (info_header_.bi_size <= 64) &&
+ (!(info_header_.bi_size & 3) || (info_header_.bi_size == 42) ||
+ (info_header_.bi_size == 46)))
+ is_os22x_ = true;
+ else
+ return parent_->SetFailed();
+
+ return true;
+}
+
+bool BMPImageReader::ProcessInfoHeader() {
+ // Read info header.
+ DCHECK_EQ(decoded_offset_, header_offset_);
+ if ((decoded_offset_ > data_->size()) ||
+ ((data_->size() - decoded_offset_) < info_header_.bi_size) ||
+ !ReadInfoHeader())
+ return false;
+ decoded_offset_ += info_header_.bi_size;
+
+ // Sanity-check header values.
+ if (!IsInfoHeaderValid())
+ return parent_->SetFailed();
+
+ // Set our size.
+ if (!parent_->SetSize(info_header_.bi_width, info_header_.bi_height))
+ return false;
+
+ // For paletted images, bitmaps can set biClrUsed to 0 to mean "all
+ // colors", so set it to the maximum number of colors for this bit depth.
+ // Also do this for bitmaps that put too large a value here.
+ if (info_header_.bi_bit_count < 16) {
+ const uint32_t max_colors = static_cast<uint32_t>(1)
+ << info_header_.bi_bit_count;
+ if (!info_header_.bi_clr_used || (info_header_.bi_clr_used > max_colors))
+ info_header_.bi_clr_used = max_colors;
+ }
+
+ // For any bitmaps that set their BitCount to the wrong value, reset the
+ // counts now that we've calculated the number of necessary colors, since
+ // other code relies on this value being correct.
+ if (info_header_.bi_compression == RLE8)
+ info_header_.bi_bit_count = 8;
+ else if (info_header_.bi_compression == RLE4)
+ info_header_.bi_bit_count = 4;
+
+ // Tell caller what still needs to be processed.
+ if (info_header_.bi_bit_count >= 16)
+ need_to_process_bitmasks_ = true;
+ else if (info_header_.bi_bit_count)
+ need_to_process_color_table_ = true;
+
+ return true;
+}
+
+bool BMPImageReader::ReadInfoHeader() {
+ // Pre-initialize some fields that not all headers set.
+ info_header_.bi_compression = RGB;
+ info_header_.bi_clr_used = 0;
+
+ if (is_os21x_) {
+ info_header_.bi_width = ReadUint16(4);
+ info_header_.bi_height = ReadUint16(6);
+ DCHECK(!is_in_ico_); // ICO is a Windows format, not OS/2!
+ info_header_.bi_bit_count = ReadUint16(10);
+ return true;
+ }
+
+ info_header_.bi_width = ReadUint32(4);
+ info_header_.bi_height = ReadUint32(8);
+ if (is_in_ico_)
+ info_header_.bi_height /= 2;
+ info_header_.bi_bit_count = ReadUint16(14);
+
+ // Read compression type, if present.
+ if (info_header_.bi_size >= 20) {
+ uint32_t bi_compression = ReadUint32(16);
+
+ // Detect OS/2 2.x-specific compression types.
+ if ((bi_compression == 3) && (info_header_.bi_bit_count == 1)) {
+ info_header_.bi_compression = HUFFMAN1D;
+ is_os22x_ = true;
+ } else if ((bi_compression == 4) && (info_header_.bi_bit_count == 24)) {
+ info_header_.bi_compression = RLE24;
+ is_os22x_ = true;
+ } else if (bi_compression > 5)
+ return parent_->SetFailed(); // Some type we don't understand.
+ else
+ info_header_.bi_compression =
+ static_cast<CompressionType>(bi_compression);
+ }
+
+ // Read colors used, if present.
+ if (info_header_.bi_size >= 36)
+ info_header_.bi_clr_used = ReadUint32(32);
+
+ // Windows V4+ can safely read the four bitmasks from 40-56 bytes in, so do
+ // that here. If the bit depth is less than 16, these values will be ignored
+ // by the image data decoders. If the bit depth is at least 16 but the
+ // compression format isn't BITFIELDS, the RGB bitmasks will be ignored and
+ // overwritten in processBitmasks(). (The alpha bitmask will never be
+ // overwritten: images that actually want alpha have to specify a valid
+ // alpha mask. See comments in ProcessBitmasks().)
+ //
+ // For non-Windows V4+, bit_masks_[] et. al will be initialized later
+ // during ProcessBitmasks().
+ if (IsWindowsV4Plus()) {
+ bit_masks_[0] = ReadUint32(40);
+ bit_masks_[1] = ReadUint32(44);
+ bit_masks_[2] = ReadUint32(48);
+ bit_masks_[3] = ReadUint32(52);
+ }
+
+ // Detect top-down BMPs.
+ if (info_header_.bi_height < 0) {
+ // We can't negate INT32_MIN below to get a positive int32_t.
+ // IsInfoHeaderValid() will reject heights of 1 << 16 or larger anyway,
+ // so just reject this bitmap now.
+ if (info_header_.bi_height == INT32_MIN)
+ return parent_->SetFailed();
+ is_top_down_ = true;
+ info_header_.bi_height = -info_header_.bi_height;
+ }
+
+ return true;
+}
+
+bool BMPImageReader::IsInfoHeaderValid() const {
+ // Non-positive widths/heights are invalid. (We've already flipped the
+ // sign of the height for top-down bitmaps.)
+ if ((info_header_.bi_width <= 0) || !info_header_.bi_height)
+ return false;
+
+ // Only Windows V3+ has top-down bitmaps.
+ if (is_top_down_ && (is_os21x_ || is_os22x_))
+ return false;
+
+ // Only bit depths of 1, 4, 8, or 24 are universally supported.
+ if ((info_header_.bi_bit_count != 1) && (info_header_.bi_bit_count != 4) &&
+ (info_header_.bi_bit_count != 8) && (info_header_.bi_bit_count != 24)) {
+ // Windows V3+ additionally supports bit depths of 0 (for embedded
+ // JPEG/PNG images), 16, and 32.
+ if (is_os21x_ || is_os22x_ ||
+ (info_header_.bi_bit_count && (info_header_.bi_bit_count != 16) &&
+ (info_header_.bi_bit_count != 32)))
+ return false;
+ }
+
+ // Each compression type is only valid with certain bit depths (except RGB,
+ // which can be used with any bit depth). Also, some formats do not support
+ // some compression types.
+ switch (info_header_.bi_compression) {
+ case RGB:
+ if (!info_header_.bi_bit_count)
+ return false;
+ break;
+
+ case RLE8:
+ // Supposedly there are undocumented formats like "BitCount = 1,
+ // Compression = RLE4" (which means "4 bit, but with a 2-color table"),
+ // so also allow the paletted RLE compression types to have too low a
+ // bit count; we'll correct this later.
+ if (!info_header_.bi_bit_count || (info_header_.bi_bit_count > 8))
+ return false;
+ break;
+
+ case RLE4:
+ // See comments in RLE8.
+ if (!info_header_.bi_bit_count || (info_header_.bi_bit_count > 4))
+ return false;
+ break;
+
+ case BITFIELDS:
+ // Only valid for Windows V3+.
+ if (is_os21x_ || is_os22x_ ||
+ ((info_header_.bi_bit_count != 16) &&
+ (info_header_.bi_bit_count != 32)))
+ return false;
+ break;
+
+ case JPEG:
+ case PNG:
+ // Only valid for Windows V3+.
+ if (is_os21x_ || is_os22x_ || info_header_.bi_bit_count)
+ return false;
+ break;
+
+ case HUFFMAN1D:
+ // Only valid for OS/2 2.x.
+ if (!is_os22x_ || (info_header_.bi_bit_count != 1))
+ return false;
+ break;
+
+ case RLE24:
+ // Only valid for OS/2 2.x.
+ if (!is_os22x_ || (info_header_.bi_bit_count != 24))
+ return false;
+ break;
+
+ default:
+ // Some type we don't understand. This should have been caught in
+ // ReadInfoHeader().
+ NOTREACHED();
+ return false;
+ }
+
+ // Top-down bitmaps cannot be compressed; they must be RGB or BITFIELDS.
+ if (is_top_down_ && (info_header_.bi_compression != RGB) &&
+ (info_header_.bi_compression != BITFIELDS))
+ return false;
+
+ // Reject the following valid bitmap types that we don't currently bother
+ // decoding. Few other people decode these either, they're unlikely to be
+ // in much use.
+ // TODO(pkasting): Consider supporting these someday.
+ // * Bitmaps larger than 2^16 pixels in either dimension (Windows
+ // probably doesn't draw these well anyway, and the decoded data would
+ // take a lot of memory).
+ if ((info_header_.bi_width >= (1 << 16)) ||
+ (info_header_.bi_height >= (1 << 16)))
+ return false;
+ // * Windows V3+ JPEG-in-BMP and PNG-in-BMP bitmaps (supposedly not found
+ // in the wild, only used to send data to printers?).
+ if ((info_header_.bi_compression == JPEG) ||
+ (info_header_.bi_compression == PNG))
+ return false;
+ // * OS/2 2.x Huffman-encoded monochrome bitmaps (see
+ // http://www.fileformat.info/mirror/egff/ch09_05.htm , re: "G31D"
+ // algorithm).
+ if (info_header_.bi_compression == HUFFMAN1D)
+ return false;
+
+ return true;
+}
+
+bool BMPImageReader::ProcessBitmasks() {
+ // Create bit_masks_[] values for R/G/B.
+ if (info_header_.bi_compression != BITFIELDS) {
+ // The format doesn't actually use bitmasks. To simplify the decode
+ // logic later, create bitmasks for the RGB data. For Windows V4+,
+ // this overwrites the masks we read from the header, which are
+ // supposed to be ignored in non-BITFIELDS cases.
+ // 16 bits: MSB <- xRRRRRGG GGGBBBBB -> LSB
+ // 24/32 bits: MSB <- [AAAAAAAA] RRRRRRRR GGGGGGGG BBBBBBBB -> LSB
+ const int num_bits = (info_header_.bi_bit_count == 16) ? 5 : 8;
+ for (int i = 0; i <= 2; ++i)
+ bit_masks_[i] = ((static_cast<uint32_t>(1) << (num_bits * (3 - i))) - 1) ^
+ ((static_cast<uint32_t>(1) << (num_bits * (2 - i))) - 1);
+ } else if (!IsWindowsV4Plus()) {
+ // For Windows V4+ BITFIELDS mode bitmaps, this was already done when
+ // we read the info header.
+
+ // Fail if we don't have enough file space for the bitmasks.
+ const size_t header_end = header_offset_ + info_header_.bi_size;
+ const size_t kBitmasksSize = 12;
+ const size_t bitmasks_end = header_end + kBitmasksSize;
+ if ((bitmasks_end < header_end) ||
+ (img_data_offset_ && (img_data_offset_ < bitmasks_end)))
+ return parent_->SetFailed();
+
+ // Read bitmasks.
+ if ((data_->size() - decoded_offset_) < kBitmasksSize)
+ return false;
+ bit_masks_[0] = ReadUint32(0);
+ bit_masks_[1] = ReadUint32(4);
+ bit_masks_[2] = ReadUint32(8);
+
+ decoded_offset_ += kBitmasksSize;
+ }
+
+ // Alpha is a poorly-documented and inconsistently-used feature.
+ //
+ // Windows V4+ has an alpha bitmask in the info header. Unlike the R/G/B
+ // bitmasks, the MSDN docs don't indicate that it is only valid for the
+ // BITFIELDS compression format, so we respect it at all times.
+ //
+ // To complicate things, Windows V3 BMPs, which lack this mask, can specify
+ // 32bpp format, which to any sane reader would imply an 8-bit alpha
+ // channel -- and for BMPs-in-ICOs, that's precisely what's intended to
+ // happen. There also exist standalone BMPs in this format which clearly
+ // expect the alpha channel to be respected. However, there are many other
+ // BMPs which, for example, fill this channel with all 0s, yet clearly
+ // expect to not be displayed as a fully-transparent rectangle.
+ //
+ // If these were the only two types of Windows V3, 32bpp BMPs in the wild,
+ // we could distinguish between them by scanning the alpha channel in the
+ // image, looking for nonzero values, and only enabling alpha if we found
+ // some. (It turns out we have to do this anyway, because, crazily, there
+ // are also Windows V4+ BMPs with an explicit, non-zero alpha mask, which
+ // then zero-fill their alpha channels! See comments in
+ // processNonRLEData().)
+ //
+ // Unfortunately there are also V3 BMPs -- indeed, probably more than the
+ // number of 32bpp, V3 BMPs which intentionally use alpha -- which specify
+ // 32bpp format, use nonzero (and non-255) alpha values, and yet expect to
+ // be rendered fully-opaque. And other browsers do so.
+ //
+ // So it's impossible to display every BMP in the way its creators intended,
+ // and we have to choose what to break. Given the paragraph above, we match
+ // other browsers and ignore alpha in Windows V3 BMPs except inside ICO
+ // files.
+ if (!IsWindowsV4Plus())
+ bit_masks_[3] = (is_in_ico_ && (info_header_.bi_compression != BITFIELDS) &&
+ (info_header_.bi_bit_count == 32))
+ ? static_cast<uint32_t>(0xff000000)
+ : 0;
+
+ // We've now decoded all the non-image data we care about. Skip anything
+ // else before the actual raster data.
+ if (img_data_offset_)
+ decoded_offset_ = img_data_offset_;
+ need_to_process_bitmasks_ = false;
+
+ // Check masks and set shift and LUT address values.
+ for (int i = 0; i < 4; ++i) {
+ // Trim the mask to the allowed bit depth. Some Windows V4+ BMPs
+ // specify a bogus alpha channel in bits that don't exist in the pixel
+ // data (for example, bits 25-31 in a 24-bit RGB format).
+ if (info_header_.bi_bit_count < 32)
+ bit_masks_[i] &=
+ ((static_cast<uint32_t>(1) << info_header_.bi_bit_count) - 1);
+
+ // For empty masks (common on the alpha channel, especially after the
+ // trimming above), quickly clear the shift and LUT address and
+ // continue, to avoid an infinite loop in the counting code below.
+ uint32_t temp_mask = bit_masks_[i];
+ if (!temp_mask) {
+ bit_shifts_right_[i] = 0;
+ lookup_table_addresses_[i] = nullptr;
+ continue;
+ }
+
+ // Make sure bitmask does not overlap any other bitmasks.
+ for (int j = 0; j < i; ++j) {
+ if (temp_mask & bit_masks_[j])
+ return parent_->SetFailed();
+ }
+
+ // Count offset into pixel data.
+ for (bit_shifts_right_[i] = 0; !(temp_mask & 1); temp_mask >>= 1)
+ ++bit_shifts_right_[i];
+
+ // Count size of mask.
+ size_t num_bits = 0;
+ for (; temp_mask & 1; temp_mask >>= 1)
+ ++num_bits;
+
+ // Make sure bitmask is contiguous.
+ if (temp_mask)
+ return parent_->SetFailed();
+
+ // Since RGBABuffer tops out at 8 bits per channel, adjust the shift
+ // amounts to use the most significant 8 bits of the channel.
+ if (num_bits >= 8) {
+ bit_shifts_right_[i] += (num_bits - 8);
+ num_bits = 0;
+ }
+
+ // Calculate LUT address.
+ lookup_table_addresses_[i] =
+ num_bits ? (nBitTo8BitlookupTable + (1 << num_bits) - 2) : nullptr;
+ }
+
+ return true;
+}
+
+bool BMPImageReader::ProcessColorTable() {
+ // Fail if we don't have enough file space for the color table.
+ const size_t header_end = header_offset_ + info_header_.bi_size;
+ const size_t table_size_in_bytes =
+ info_header_.bi_clr_used * (is_os21x_ ? 3 : 4);
+ const size_t table_end = header_end + table_size_in_bytes;
+ if ((table_end < header_end) ||
+ (img_data_offset_ && (img_data_offset_ < table_end)))
+ return parent_->SetFailed();
+
+ // Read color table.
+ if ((decoded_offset_ > data_->size()) ||
+ ((data_->size() - decoded_offset_) < table_size_in_bytes))
+ return false;
+ color_table_.resize(info_header_.bi_clr_used);
+
+ // On non-OS/2 1.x, an extra padding byte is present, which we need to skip.
+ const size_t bytes_per_color = is_os21x_ ? 3 : 4;
+ for (size_t i = 0; i < info_header_.bi_clr_used; ++i) {
+ color_table_[i].rgb_blue = ReadUint8(0);
+ color_table_[i].rgb_green = ReadUint8(1);
+ color_table_[i].rgb_red = ReadUint8(2);
+ decoded_offset_ += bytes_per_color;
+ }
+
+ // We've now decoded all the non-image data we care about. Skip anything
+ // else before the actual raster data.
+ if (img_data_offset_)
+ decoded_offset_ = img_data_offset_;
+ need_to_process_color_table_ = false;
+
+ return true;
+}
+
+BMPImageReader::ProcessingResult BMPImageReader::ProcessRLEData() {
+ if (decoded_offset_ > data_->size())
+ return kInsufficientData;
+
+ // RLE decoding is poorly specified. Two main problems:
+ // (1) Are EOL markers necessary? What happens when we have too many
+ // pixels for one row?
+ // http://www.fileformat.info/format/bmp/egff.htm says extra pixels
+ // should wrap to the next line. Real BMPs I've encountered seem to
+ // instead expect extra pixels to be ignored until the EOL marker is
+ // seen, although this has only happened in a few cases and I suspect
+ // those BMPs may be invalid. So we only change lines on EOL (or Delta
+ // with dy > 0), and fail in most cases when pixels extend past the end
+ // of the line.
+ // (2) When Delta, EOL, or EOF are seen, what happens to the "skipped"
+ // pixels?
+ // http://www.daubnet.com/formats/BMP.html says these should be filled
+ // with color 0. However, the "do nothing" and "don't care" comments
+ // of other references suggest leaving these alone, i.e. letting them
+ // be transparent to the background behind the image. This seems to
+ // match how MSPAINT treats BMPs, so we do that. Note that when we
+ // actually skip pixels for a case like this, we need to note on the
+ // framebuffer that we have alpha.
+
+ // Impossible to decode row-at-a-time, so just do things as a stream of
+ // bytes.
+ while (true) {
+ // Every entry takes at least two bytes; bail if there isn't enough
+ // data.
+ if ((data_->size() - decoded_offset_) < 2)
+ return kInsufficientData;
+
+ // For every entry except EOF, we'd better not have reached the end of
+ // the image.
+ const uint8_t count = ReadUint8(0);
+ const uint8_t code = ReadUint8(1);
+ if ((count || (code != 1)) && PastEndOfImage(0))
+ return kFailure;
+
+ // Decode.
+ if (!count) {
+ switch (code) {
+ case 0: // Magic token: EOL
+ // Skip any remaining pixels in this row.
+ if (coord_.X() < parent_->Size().Width())
+ buffer_->SetHasAlpha(true);
+ MoveBufferToNextRow();
+
+ decoded_offset_ += 2;
+ break;
+
+ case 1: // Magic token: EOF
+ // Skip any remaining pixels in the image.
+ if ((coord_.X() < parent_->Size().Width()) ||
+ (is_top_down_ ? (coord_.Y() < (parent_->Size().Height() - 1))
+ : (coord_.Y() > 0)))
+ buffer_->SetHasAlpha(true);
+ // There's no need to move |coord_| here to trigger the caller
+ // to call SetPixelsChanged(). If the only thing that's changed
+ // is the alpha state, that will be properly written into the
+ // underlying SkBitmap when we mark the frame complete.
+ return kSuccess;
+
+ case 2: { // Magic token: Delta
+ // The next two bytes specify dx and dy. Bail if there isn't
+ // enough data.
+ if ((data_->size() - decoded_offset_) < 4)
+ return kInsufficientData;
+
+ // Fail if this takes us past the end of the desired row or
+ // past the end of the image.
+ const uint8_t dx = ReadUint8(2);
+ const uint8_t dy = ReadUint8(3);
+ if (dx || dy)
+ buffer_->SetHasAlpha(true);
+ if (((coord_.X() + dx) > parent_->Size().Width()) ||
+ PastEndOfImage(dy))
+ return kFailure;
+
+ // Skip intervening pixels.
+ coord_.Move(dx, is_top_down_ ? dy : -dy);
+
+ decoded_offset_ += 4;
+ break;
+ }
+
+ default: { // Absolute mode
+ // |code| pixels specified as in BI_RGB, zero-padded at the end
+ // to a multiple of 16 bits.
+ // Because ProcessNonRLEData() expects decoded_offset_ to
+ // point to the beginning of the pixel data, bump it past
+ // the escape bytes and then reset if decoding failed.
+ decoded_offset_ += 2;
+ const ProcessingResult result = ProcessNonRLEData(true, code);
+ if (result != kSuccess) {
+ decoded_offset_ -= 2;
+ return result;
+ }
+ break;
+ }
+ }
+ } else { // Encoded mode
+ // The following color data is repeated for |count| total pixels.
+ // Strangely, some BMPs seem to specify excessively large counts
+ // here; ignore pixels past the end of the row.
+ const int end_x = std::min(coord_.X() + count, parent_->Size().Width());
+
+ if (info_header_.bi_compression == RLE24) {
+ // Bail if there isn't enough data.
+ if ((data_->size() - decoded_offset_) < 4)
+ return kInsufficientData;
+
+ // One BGR triple that we copy |count| times.
+ FillRGBA(end_x, ReadUint8(3), ReadUint8(2), code, 0xff);
+ decoded_offset_ += 4;
+ } else {
+ // RLE8 has one color index that gets repeated; RLE4 has two
+ // color indexes in the upper and lower 4 bits of the byte,
+ // which are alternated.
+ size_t color_indexes[2] = {code, code};
+ if (info_header_.bi_compression == RLE4) {
+ color_indexes[0] = (color_indexes[0] >> 4) & 0xf;
+ color_indexes[1] &= 0xf;
+ }
+ for (int which = 0; coord_.X() < end_x;) {
+ // Some images specify color values past the end of the
+ // color table; set these pixels to black.
+ if (color_indexes[which] < info_header_.bi_clr_used)
+ SetI(color_indexes[which]);
+ else
+ SetRGBA(0, 0, 0, 255);
+ which = !which;
+ }
+
+ decoded_offset_ += 2;
+ }
+ }
+ }
+}
+
+BMPImageReader::ProcessingResult BMPImageReader::ProcessNonRLEData(
+ bool in_rle,
+ int num_pixels) {
+ if (decoded_offset_ > data_->size())
+ return kInsufficientData;
+
+ if (!in_rle)
+ num_pixels = parent_->Size().Width();
+
+ // Fail if we're being asked to decode more pixels than remain in the row.
+ const int end_x = coord_.X() + num_pixels;
+ if (end_x > parent_->Size().Width())
+ return kFailure;
+
+ // Determine how many bytes of data the requested number of pixels
+ // requires.
+ const size_t pixels_per_byte = 8 / info_header_.bi_bit_count;
+ const size_t bytes_per_pixel = info_header_.bi_bit_count / 8;
+ const size_t unpadded_num_bytes =
+ (info_header_.bi_bit_count < 16)
+ ? ((num_pixels + pixels_per_byte - 1) / pixels_per_byte)
+ : (num_pixels * bytes_per_pixel);
+ // RLE runs are zero-padded at the end to a multiple of 16 bits. Non-RLE
+ // data is in rows and is zero-padded to a multiple of 32 bits.
+ const size_t align_bits = in_rle ? 1 : 3;
+ const size_t padded_num_bytes =
+ (unpadded_num_bytes + align_bits) & ~align_bits;
+
+ // Decode as many rows as we can. (For RLE, where we only want to decode
+ // one row, we've already checked that this condition is true.)
+ while (!PastEndOfImage(0)) {
+ // Bail if we don't have enough data for the desired number of pixels.
+ if ((data_->size() - decoded_offset_) < padded_num_bytes)
+ return kInsufficientData;
+
+ if (info_header_.bi_bit_count < 16) {
+ // Paletted data. Pixels are stored little-endian within bytes.
+ // Decode pixels one byte at a time, left to right (so, starting at
+ // the most significant bits in the byte).
+ const uint8_t mask = (1 << info_header_.bi_bit_count) - 1;
+ for (size_t end_offset = decoded_offset_ + unpadded_num_bytes;
+ decoded_offset_ < end_offset; ++decoded_offset_) {
+ uint8_t pixel_data = ReadUint8(0);
+ for (size_t pixel = 0;
+ (pixel < pixels_per_byte) && (coord_.X() < end_x); ++pixel) {
+ const size_t color_index =
+ (pixel_data >> (8 - info_header_.bi_bit_count)) & mask;
+ if (decoding_and_mask_) {
+ // There's no way to accurately represent an AND + XOR
+ // operation as an RGBA image, so where the AND values
+ // are 1, we simply set the framebuffer pixels to fully
+ // transparent, on the assumption that most ICOs on the
+ // web will not be doing a lot of inverting.
+ if (color_index) {
+ SetRGBA(0, 0, 0, 0);
+ buffer_->SetHasAlpha(true);
+ } else
+ coord_.Move(1, 0);
+ } else {
+ // See comments near the end of ProcessRLEData().
+ if (color_index < info_header_.bi_clr_used)
+ SetI(color_index);
+ else
+ SetRGBA(0, 0, 0, 255);
+ }
+ pixel_data <<= info_header_.bi_bit_count;
+ }
+ }
+ } else {
+ // RGB data. Decode pixels one at a time, left to right.
+ for (; coord_.X() < end_x; decoded_offset_ += bytes_per_pixel) {
+ const uint32_t pixel = ReadCurrentPixel(bytes_per_pixel);
+
+ // Some BMPs specify an alpha channel but don't actually use it
+ // (it contains all 0s). To avoid displaying these images as
+ // fully-transparent, decode as if images are fully opaque
+ // until we actually see a non-zero alpha value; at that point,
+ // reset any previously-decoded pixels to fully transparent and
+ // continue decoding based on the real alpha channel values.
+ // As an optimization, avoid calling SetHasAlpha(true) for
+ // images where all alpha values are 255; opaque images are
+ // faster to draw.
+ int alpha = GetAlpha(pixel);
+ if (!seen_non_zero_alpha_pixel_ && !alpha) {
+ seen_zero_alpha_pixel_ = true;
+ alpha = 255;
+ } else {
+ seen_non_zero_alpha_pixel_ = true;
+ if (seen_zero_alpha_pixel_) {
+ buffer_->ZeroFillPixelData();
+ seen_zero_alpha_pixel_ = false;
+ } else if (alpha != 255)
+ buffer_->SetHasAlpha(true);
+ }
+
+ SetRGBA(GetComponent(pixel, 0), GetComponent(pixel, 1),
+ GetComponent(pixel, 2), alpha);
+ }
+ }
+
+ // Success, keep going.
+ decoded_offset_ += (padded_num_bytes - unpadded_num_bytes);
+ if (in_rle)
+ return kSuccess;
+ MoveBufferToNextRow();
+ }
+
+ // Finished decoding whole image.
+ return kSuccess;
+}
+
+void BMPImageReader::MoveBufferToNextRow() {
+ coord_.Move(-coord_.X(), is_top_down_ ? 1 : -1);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_reader.h b/chromium/third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_reader.h
new file mode 100644
index 00000000000..db5a170fe66
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_reader.h
@@ -0,0 +1,367 @@
+/*
+ * Copyright (c) 2008, 2009, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_BMP_BMP_IMAGE_READER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_BMP_BMP_IMAGE_READER_H_
+
+#include <stdint.h>
+#include "third_party/blink/renderer/platform/image-decoders/fast_shared_buffer_reader.h"
+#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/cpu.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+// This class decodes a BMP image. It is used in the BMP and ICO decoders,
+// which wrap it in the appropriate code to read file headers, etc.
+class PLATFORM_EXPORT BMPImageReader final {
+ USING_FAST_MALLOC(BMPImageReader);
+ WTF_MAKE_NONCOPYABLE(BMPImageReader);
+
+ public:
+ // Read a value from |buffer|, converting to an int assuming little
+ // endianness
+ static inline uint16_t ReadUint16(const char* buffer) {
+ return *reinterpret_cast<const uint16_t*>(buffer);
+ }
+
+ static inline uint32_t ReadUint32(const char* buffer) {
+ return *reinterpret_cast<const uint32_t*>(buffer);
+ }
+
+ // |parent| is the decoder that owns us.
+ // |start_offset| points to the start of the BMP within the file.
+ // |buffer| points at an empty ImageFrame that we'll initialize and
+ // fill with decoded data.
+ BMPImageReader(ImageDecoder* parent,
+ size_t decoded_and_header_offset,
+ size_t img_data_offset,
+ bool is_in_ico);
+
+ void SetBuffer(ImageFrame* buffer) { buffer_ = buffer; }
+ void SetData(SegmentReader* data) {
+ data_ = data;
+ fast_reader_.SetData(data);
+ }
+
+ // Does the actual decoding. If |only_size| is true, decoding only
+ // progresses as far as necessary to get the image size. Returns
+ // whether decoding succeeded.
+ bool DecodeBMP(bool only_size);
+
+ private:
+ friend class PixelChangedScoper;
+
+ // Helper for DecodeBMP() which will call either ProcessRLEData() or
+ // ProcessNonRLEData(), depending on the value of |non_rle|, call any
+ // appropriate notifications to deal with the result, then return whether
+ // decoding succeeded.
+ bool DecodePixelData(bool non_rle);
+
+ // The various BMP compression types. We don't currently decode all
+ // these.
+ enum CompressionType {
+ // Universal types
+ RGB = 0,
+ RLE8 = 1,
+ RLE4 = 2,
+ // Windows V3+ only
+ BITFIELDS = 3,
+ JPEG = 4,
+ PNG = 5,
+ // OS/2 2.x-only
+ HUFFMAN1D, // Stored in file as 3
+ RLE24, // Stored in file as 4
+ };
+ enum ProcessingResult {
+ kSuccess,
+ kFailure,
+ kInsufficientData,
+ };
+
+ // These are based on the Windows BITMAPINFOHEADER and RGBTRIPLE
+ // structs, but with unnecessary entries removed.
+ struct BitmapInfoHeader {
+ DISALLOW_NEW();
+ uint32_t bi_size;
+ int32_t bi_width;
+ int32_t bi_height;
+ uint16_t bi_bit_count;
+ CompressionType bi_compression;
+ uint32_t bi_clr_used;
+ };
+ struct RGBTriple {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ uint8_t rgb_blue;
+ uint8_t rgb_green;
+ uint8_t rgb_red;
+ };
+
+ inline uint8_t ReadUint8(size_t offset) const {
+ return fast_reader_.GetOneByte(decoded_offset_ + offset);
+ }
+
+ inline uint16_t ReadUint16(int offset) const {
+ char buffer[2];
+ const char* data =
+ fast_reader_.GetConsecutiveData(decoded_offset_ + offset, 2, buffer);
+ return ReadUint16(data);
+ }
+
+ inline uint32_t ReadUint32(int offset) const {
+ char buffer[4];
+ const char* data =
+ fast_reader_.GetConsecutiveData(decoded_offset_ + offset, 4, buffer);
+ return ReadUint32(data);
+ }
+
+ // Determines the size of the BMP info header. Returns true if the size
+ // is valid.
+ bool ReadInfoHeaderSize();
+
+ // Processes the BMP info header. Returns true if the info header could
+ // be decoded.
+ bool ProcessInfoHeader();
+
+ // Helper function for ProcessInfoHeader() which does the actual reading
+ // of header values from the byte stream. Returns false on error.
+ bool ReadInfoHeader();
+
+ // Returns true if this is a Windows V4+ BMP.
+ inline bool IsWindowsV4Plus() const {
+ // Windows V4 info header is 108 bytes. V5 is 124 bytes.
+ return (info_header_.bi_size == 108) || (info_header_.bi_size == 124);
+ }
+
+ // Returns false if consistency errors are found in the info header.
+ bool IsInfoHeaderValid() const;
+
+ // For BI_BITFIELDS images, initializes the bit_masks_[] and
+ // bit_offsets_[] arrays. ProcessInfoHeader() will initialize these for
+ // other compression types where needed.
+ bool ProcessBitmasks();
+
+ // For paletted images, allocates and initializes the color_table_[]
+ // array.
+ bool ProcessColorTable();
+
+ // The next two functions return a ProcessingResult instead of a bool so
+ // they can avoid calling parent_->SetFailed(), which could lead to memory
+ // corruption since that will delete |this| but some callers still want
+ // to access member variables after they return.
+
+ // Processes an RLE-encoded image.
+ ProcessingResult ProcessRLEData();
+
+ // Processes a set of non-RLE-compressed pixels. Two cases:
+ // * in_rle = true: the data is inside an RLE-encoded bitmap. Tries to
+ // process |num_pixels| pixels on the current row.
+ // * in_rle = false: the data is inside a non-RLE-encoded bitmap.
+ // |num_pixels| is ignored. Expects |coord_| to point at the
+ // beginning of the next row to be decoded. Tries to process as
+ // many complete rows as possible. Returns InsufficientData if
+ // there wasn't enough data to decode the whole image.
+ ProcessingResult ProcessNonRLEData(bool in_rle, int num_pixels);
+
+ // Returns true if the current y-coordinate plus |num_rows| would be past
+ // the end of the image. Here "plus" means "toward the end of the
+ // image", so downwards for is_top_down_ images and upwards otherwise.
+ inline bool PastEndOfImage(int num_rows) {
+ return is_top_down_ ? ((coord_.Y() + num_rows) >= parent_->Size().Height())
+ : ((coord_.Y() - num_rows) < 0);
+ }
+
+ // Returns the pixel data for the current |decoded_offset_| in a uint32_t.
+ // NOTE: Only as many bytes of the return value as are needed to hold
+ // the pixel data will actually be set.
+ inline uint32_t ReadCurrentPixel(int bytes_per_pixel) const {
+ // We need at most 4 bytes, starting at decoded_offset_.
+ char buffer[4];
+ const char* encoded_pixel = fast_reader_.GetConsecutiveData(
+ decoded_offset_, bytes_per_pixel, buffer);
+ switch (bytes_per_pixel) {
+ case 2:
+ return ReadUint16(encoded_pixel);
+
+ case 3: {
+ // It doesn't matter that we never set the most significant byte
+ // of the return value, the caller won't read it.
+ uint32_t pixel;
+ memcpy(&pixel, encoded_pixel, 3);
+ return pixel;
+ }
+
+ case 4:
+ return ReadUint32(encoded_pixel);
+
+ default:
+ NOTREACHED();
+ return 0;
+ }
+ }
+
+ // Returns the value of the desired component (0, 1, 2, 3 == R, G, B, A)
+ // in the given pixel data.
+ inline unsigned GetComponent(uint32_t pixel, int component) const {
+ uint8_t value =
+ (pixel & bit_masks_[component]) >> bit_shifts_right_[component];
+ return lookup_table_addresses_[component]
+ ? lookup_table_addresses_[component][value]
+ : value;
+ }
+
+ inline unsigned GetAlpha(uint32_t pixel) const {
+ // For images without alpha, return alpha of 0xff.
+ return bit_masks_[3] ? GetComponent(pixel, 3) : 0xff;
+ }
+
+ // Sets the current pixel to the color given by |color_index|. This also
+ // increments the relevant local variables to move the current pixel
+ // right by one.
+ inline void SetI(size_t color_index) {
+ SetRGBA(color_table_[color_index].rgb_red,
+ color_table_[color_index].rgb_green,
+ color_table_[color_index].rgb_blue, 0xff);
+ }
+
+ // Like SetI(), but with the individual component values specified.
+ inline void SetRGBA(unsigned red,
+ unsigned green,
+ unsigned blue,
+ unsigned alpha) {
+ buffer_->SetRGBA(coord_.X(), coord_.Y(), red, green, blue, alpha);
+ coord_.Move(1, 0);
+ }
+
+ // Fills pixels from the current X-coordinate up to, but not including,
+ // |end_coord| with the color given by the individual components. This
+ // also increments the relevant local variables to move the current
+ // pixel right to |end_coord|.
+ inline void FillRGBA(int end_coord,
+ unsigned red,
+ unsigned green,
+ unsigned blue,
+ unsigned alpha) {
+ while (coord_.X() < end_coord)
+ SetRGBA(red, green, blue, alpha);
+ }
+
+ // Resets the relevant local variables to start drawing at the left edge
+ // of the "next" row, where "next" is above or below the current row
+ // depending on the value of |is_top_down_|.
+ void MoveBufferToNextRow();
+
+ // The decoder that owns us.
+ ImageDecoder* parent_;
+
+ // The destination for the pixel data.
+ ImageFrame* buffer_;
+
+ // The file to decode.
+ scoped_refptr<SegmentReader> data_;
+ FastSharedBufferReader fast_reader_;
+
+ // An index into |data_| representing how much we've already decoded.
+ size_t decoded_offset_;
+
+ // The file offset at which the BMP info header starts.
+ size_t header_offset_;
+
+ // The file offset at which the actual image bits start. When decoding
+ // ICO files, this is set to 0, since it's not stored anywhere in a
+ // header; the reader functions expect the image data to start
+ // immediately after the header and (if necessary) color table.
+ size_t img_data_offset_;
+
+ // The BMP info header.
+ BitmapInfoHeader info_header_;
+
+ // True if this is an OS/2 1.x (aka Windows 2.x) BMP. The struct
+ // layouts for this type of BMP are slightly different from the later,
+ // more common formats.
+ bool is_os21x_;
+
+ // True if this is an OS/2 2.x BMP. The meanings of compression types 3
+ // and 4 for this type of BMP differ from Windows V3+ BMPs.
+ //
+ // This will be falsely negative in some cases, but only ones where the
+ // way we misinterpret the data is irrelevant.
+ bool is_os22x_;
+
+ // True if the BMP is not vertically flipped, that is, the first line of
+ // raster data in the file is the top line of the image.
+ bool is_top_down_;
+
+ // These flags get set to false as we finish each processing stage.
+ bool need_to_process_bitmasks_;
+ bool need_to_process_color_table_;
+
+ // Masks/offsets for the color values for non-palette formats. These are
+ // bitwise, with array entries 0, 1, 2, 3 corresponding to R, G, B, A.
+ // These are uninitialized (and ignored) for images with less than 16bpp.
+ uint32_t bit_masks_[4];
+
+ // Right shift values, meant to be applied after the masks. We need to shift
+ // the bitfield values down from their offsets into the 32 bits of pixel
+ // data, as well as truncate the least significant bits of > 8-bit fields.
+ int bit_shifts_right_[4];
+
+ // We use a lookup table to convert < 8-bit values into 8-bit values. The
+ // values in the table are "round(val * 255.0 / ((1 << n) - 1))" for an
+ // n-bit source value. These elements are set to 0 for 8-bit sources.
+ const uint8_t* lookup_table_addresses_[4];
+
+ // The color palette, for paletted formats.
+ Vector<RGBTriple> color_table_;
+
+ // The coordinate to which we've decoded the image.
+ IntPoint coord_;
+
+ // Variables that track whether we've seen pixels with alpha values != 0
+ // and == 0, respectively. See comments in ProcessNonRLEData() on how
+ // these are used.
+ bool seen_non_zero_alpha_pixel_;
+ bool seen_zero_alpha_pixel_;
+
+ // BMPs-in-ICOs have a few differences from standalone BMPs, so we need to
+ // know if we're in an ICO container.
+ bool is_in_ico_;
+
+ // ICOs store a 1bpp "mask" immediately after the main bitmap image data
+ // (and, confusingly, add its height to the biHeight value in the info
+ // header, thus doubling it). If |is_in_ico_| is true, this variable tracks
+ // whether we've begun decoding this mask yet.
+ bool decoding_and_mask_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/fast_shared_buffer_reader.cc b/chromium/third_party/blink/renderer/platform/image-decoders/fast_shared_buffer_reader.cc
new file mode 100644
index 00000000000..edb658ec772
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/fast_shared_buffer_reader.cc
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2015 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 "third_party/blink/renderer/platform/image-decoders/fast_shared_buffer_reader.h"
+
+namespace blink {
+
+FastSharedBufferReader::FastSharedBufferReader(
+ scoped_refptr<SegmentReader> data)
+ : data_(std::move(data)),
+ segment_(nullptr),
+ segment_length_(0),
+ data_position_(0) {}
+
+void FastSharedBufferReader::SetData(scoped_refptr<SegmentReader> data) {
+ if (data == data_)
+ return;
+ data_ = std::move(data);
+ ClearCache();
+}
+
+void FastSharedBufferReader::ClearCache() {
+ segment_ = nullptr;
+ segment_length_ = 0;
+ data_position_ = 0;
+}
+
+const char* FastSharedBufferReader::GetConsecutiveData(size_t data_position,
+ size_t length,
+ char* buffer) const {
+ CHECK_LE(data_position + length, data_->size());
+
+ // Use the cached segment if it can serve the request.
+ if (data_position >= data_position_ &&
+ data_position + length <= data_position_ + segment_length_)
+ return segment_ + data_position - data_position_;
+
+ // Return a pointer into |data_| if the request doesn't span segments.
+ GetSomeDataInternal(data_position);
+ if (length <= segment_length_)
+ return segment_;
+
+ for (char* dest = buffer;;) {
+ size_t copy = std::min(length, segment_length_);
+ memcpy(dest, segment_, copy);
+ length -= copy;
+ if (!length)
+ return buffer;
+
+ // Continue reading the next segment.
+ dest += copy;
+ GetSomeDataInternal(data_position_ + copy);
+ }
+}
+
+size_t FastSharedBufferReader::GetSomeData(const char*& some_data,
+ size_t data_position) const {
+ GetSomeDataInternal(data_position);
+ some_data = segment_;
+ return segment_length_;
+}
+
+void FastSharedBufferReader::GetSomeDataInternal(size_t data_position) const {
+ data_position_ = data_position;
+ segment_length_ = data_->GetSomeData(segment_, data_position);
+ DCHECK(segment_length_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/fast_shared_buffer_reader.h b/chromium/third_party/blink/renderer/platform/image-decoders/fast_shared_buffer_reader.h
new file mode 100644
index 00000000000..89244919e77
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/fast_shared_buffer_reader.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2015 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_FAST_SHARED_BUFFER_READER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_FAST_SHARED_BUFFER_READER_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/image-decoders/segment_reader.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+// This class is used by image decoders to avoid memory consolidation and
+// therefore minimizes the cost of memory copying when the decoders
+// repeatedly read from a buffer that is continually growing due to network
+// traffic.
+class PLATFORM_EXPORT FastSharedBufferReader final {
+ DISALLOW_NEW();
+ WTF_MAKE_NONCOPYABLE(FastSharedBufferReader);
+
+ public:
+ FastSharedBufferReader(scoped_refptr<SegmentReader> data);
+
+ void SetData(scoped_refptr<SegmentReader>);
+
+ // Returns a consecutive buffer that carries the data starting
+ // at |data_position| with |length| bytes.
+ // This method returns a pointer to a memory segment stored in
+ // |data_| if such a consecutive buffer can be found.
+ // Otherwise copies into |buffer| and returns it.
+ // Caller must ensure there are enough bytes in |data_| and |buffer|.
+ const char* GetConsecutiveData(size_t data_position,
+ size_t length,
+ char* buffer) const;
+
+ // Wraps SegmentReader::GetSomeData().
+ size_t GetSomeData(const char*& some_data, size_t data_position) const;
+
+ // Returns a byte at |data_position|.
+ // Caller must ensure there are enough bytes in |data_|.
+ inline char GetOneByte(size_t data_position) const {
+ return *GetConsecutiveData(data_position, 1, nullptr);
+ }
+
+ size_t size() const { return data_->size(); }
+
+ // This class caches the last access for faster subsequent reads. This
+ // method clears that cache in case the SegmentReader has been modified
+ // (e.g. with MergeSegmentsIntoBuffer on a wrapped SharedBuffer).
+ void ClearCache();
+
+ private:
+ void GetSomeDataInternal(size_t data_position) const;
+
+ scoped_refptr<SegmentReader> data_;
+
+ // Caches the last segment of |data_| accessed, since subsequent reads are
+ // likely to re-access it.
+ mutable const char* segment_;
+ mutable size_t segment_length_;
+
+ // Data position in |data_| pointed to by |segment_|.
+ mutable size_t data_position_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/fast_shared_buffer_reader_test.cc b/chromium/third_party/blink/renderer/platform/image-decoders/fast_shared_buffer_reader_test.cc
new file mode 100644
index 00000000000..29bf9fcd6c9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/fast_shared_buffer_reader_test.cc
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2015 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 "third_party/blink/renderer/platform/image-decoders/fast_shared_buffer_reader.h"
+#include "third_party/blink/renderer/platform/image-decoders/image_decoder_test_helpers.h"
+#include "third_party/blink/renderer/platform/image-decoders/segment_reader.h"
+#include "third_party/skia/include/core/SkData.h"
+#include "third_party/skia/include/core/SkRWBuffer.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+namespace {
+
+scoped_refptr<SegmentReader> CopyToROBufferSegmentReader(
+ scoped_refptr<SegmentReader> input) {
+ SkRWBuffer rw_buffer;
+ const char* segment = nullptr;
+ size_t position = 0;
+ while (size_t length = input->GetSomeData(segment, position)) {
+ rw_buffer.append(segment, length);
+ position += length;
+ }
+ return SegmentReader::CreateFromSkROBuffer(rw_buffer.makeROBufferSnapshot());
+}
+
+scoped_refptr<SegmentReader> CopyToDataSegmentReader(
+ scoped_refptr<SegmentReader> input) {
+ return SegmentReader::CreateFromSkData(input->GetAsSkData());
+}
+
+struct SegmentReaders {
+ scoped_refptr<SegmentReader> segment_readers[3];
+
+ SegmentReaders(scoped_refptr<SharedBuffer> input) {
+ segment_readers[0] =
+ SegmentReader::CreateFromSharedBuffer(std::move(input));
+ segment_readers[1] = CopyToROBufferSegmentReader(segment_readers[0]);
+ segment_readers[2] = CopyToDataSegmentReader(segment_readers[0]);
+ }
+};
+
+} // namespace
+
+TEST(FastSharedBufferReaderTest, nonSequentialReads) {
+ char reference_data[kDefaultTestSize];
+ PrepareReferenceData(reference_data, sizeof(reference_data));
+ scoped_refptr<SharedBuffer> data = SharedBuffer::Create();
+ data->Append(reference_data, sizeof(reference_data));
+
+ SegmentReaders reader_struct(data);
+ for (auto segment_reader : reader_struct.segment_readers) {
+ FastSharedBufferReader reader(segment_reader);
+ // Read size is prime such there will be a segment-spanning
+ // read eventually.
+ char temp_buffer[17];
+ for (size_t data_position = 0;
+ data_position + sizeof(temp_buffer) < sizeof(reference_data);
+ data_position += sizeof(temp_buffer)) {
+ const char* block = reader.GetConsecutiveData(
+ data_position, sizeof(temp_buffer), temp_buffer);
+ ASSERT_FALSE(
+ memcmp(block, reference_data + data_position, sizeof(temp_buffer)));
+ }
+ }
+}
+
+TEST(FastSharedBufferReaderTest, readBackwards) {
+ char reference_data[kDefaultTestSize];
+ PrepareReferenceData(reference_data, sizeof(reference_data));
+ scoped_refptr<SharedBuffer> data = SharedBuffer::Create();
+ data->Append(reference_data, sizeof(reference_data));
+
+ SegmentReaders reader_struct(data);
+ for (auto segment_reader : reader_struct.segment_readers) {
+ FastSharedBufferReader reader(segment_reader);
+ // Read size is prime such there will be a segment-spanning
+ // read eventually.
+ char temp_buffer[17];
+ for (size_t data_offset = sizeof(temp_buffer);
+ data_offset < sizeof(reference_data);
+ data_offset += sizeof(temp_buffer)) {
+ const char* block =
+ reader.GetConsecutiveData(sizeof(reference_data) - data_offset,
+ sizeof(temp_buffer), temp_buffer);
+ ASSERT_FALSE(memcmp(block,
+ reference_data + sizeof(reference_data) - data_offset,
+ sizeof(temp_buffer)));
+ }
+ }
+}
+
+TEST(FastSharedBufferReaderTest, byteByByte) {
+ char reference_data[kDefaultTestSize];
+ PrepareReferenceData(reference_data, sizeof(reference_data));
+ scoped_refptr<SharedBuffer> data = SharedBuffer::Create();
+ data->Append(reference_data, sizeof(reference_data));
+
+ SegmentReaders reader_struct(data);
+ for (auto segment_reader : reader_struct.segment_readers) {
+ FastSharedBufferReader reader(segment_reader);
+ for (size_t i = 0; i < sizeof(reference_data); ++i) {
+ ASSERT_EQ(reference_data[i], reader.GetOneByte(i));
+ }
+ }
+}
+
+// Tests that a read from inside the penultimate segment to the very end of the
+// buffer doesn't try to read off the end of the buffer.
+TEST(FastSharedBufferReaderTest, readAllOverlappingLastSegmentBoundary) {
+ const unsigned kDataSize = 2 * SharedBuffer::kSegmentSize;
+ char reference_data[kDataSize];
+ PrepareReferenceData(reference_data, kDataSize);
+ scoped_refptr<SharedBuffer> data = SharedBuffer::Create();
+ data->Append(reference_data, kDataSize);
+
+ SegmentReaders reader_struct(data);
+ for (auto segment_reader : reader_struct.segment_readers) {
+ FastSharedBufferReader reader(segment_reader);
+ char buffer[kDataSize];
+ reader.GetConsecutiveData(0, kDataSize, buffer);
+ ASSERT_FALSE(memcmp(buffer, reference_data, kDataSize));
+ }
+}
+
+// Verify that reading past the end of the buffer does not break future reads.
+TEST(SegmentReaderTest, readPastEndThenRead) {
+ const unsigned kDataSize = 2 * SharedBuffer::kSegmentSize;
+ char reference_data[kDataSize];
+ PrepareReferenceData(reference_data, kDataSize);
+ scoped_refptr<SharedBuffer> data = SharedBuffer::Create();
+ data->Append(reference_data, kDataSize);
+
+ SegmentReaders reader_struct(data);
+ for (auto segment_reader : reader_struct.segment_readers) {
+ const char* contents;
+ size_t length = segment_reader->GetSomeData(contents, kDataSize);
+ EXPECT_EQ(0u, length);
+
+ length = segment_reader->GetSomeData(contents, 0);
+ EXPECT_LE(SharedBuffer::kSegmentSize, length);
+ }
+}
+
+TEST(SegmentReaderTest, getAsSkData) {
+ const unsigned kDataSize = 4 * SharedBuffer::kSegmentSize;
+ char reference_data[kDataSize];
+ PrepareReferenceData(reference_data, kDataSize);
+ scoped_refptr<SharedBuffer> data = SharedBuffer::Create();
+ data->Append(reference_data, kDataSize);
+
+ SegmentReaders reader_struct(data);
+ for (auto segment_reader : reader_struct.segment_readers) {
+ sk_sp<SkData> skdata = segment_reader->GetAsSkData();
+ EXPECT_EQ(data->size(), skdata->size());
+
+ const char* segment;
+ size_t position = 0;
+ for (size_t length = segment_reader->GetSomeData(segment, position); length;
+ length = segment_reader->GetSomeData(segment, position)) {
+ ASSERT_FALSE(memcmp(segment, skdata->bytes() + position, length));
+ position += length;
+ }
+ EXPECT_EQ(position, kDataSize);
+ }
+}
+
+TEST(SegmentReaderTest, variableSegments) {
+ const size_t kDataSize = 3.5 * SharedBuffer::kSegmentSize;
+ char reference_data[kDataSize];
+ PrepareReferenceData(reference_data, kDataSize);
+
+ scoped_refptr<SegmentReader> segment_reader;
+ {
+ // Create a SegmentReader with difference sized segments, to test that
+ // the SkROBuffer implementation works when two consecutive segments
+ // are not the same size. This test relies on knowledge of the
+ // internals of SkRWBuffer: it ensures that each segment is at least
+ // 4096 (though the actual data may be smaller, if it has not been
+ // written to yet), but when appending a larger amount it may create a
+ // larger segment.
+ SkRWBuffer rw_buffer;
+ rw_buffer.append(reference_data, SharedBuffer::kSegmentSize);
+ rw_buffer.append(reference_data + SharedBuffer::kSegmentSize,
+ 2 * SharedBuffer::kSegmentSize);
+ rw_buffer.append(reference_data + 3 * SharedBuffer::kSegmentSize,
+ .5 * SharedBuffer::kSegmentSize);
+
+ segment_reader =
+ SegmentReader::CreateFromSkROBuffer(rw_buffer.makeROBufferSnapshot());
+ }
+
+ const char* segment;
+ size_t position = 0;
+ size_t last_length = 0;
+ for (size_t length = segment_reader->GetSomeData(segment, position); length;
+ length = segment_reader->GetSomeData(segment, position)) {
+ // It is not a bug to have consecutive segments of the same length, but
+ // it does mean that the following test does not actually test what it
+ // is intended to test.
+ ASSERT_NE(length, last_length);
+ last_length = length;
+
+ ASSERT_FALSE(memcmp(segment, reference_data + position, length));
+ position += length;
+ }
+ EXPECT_EQ(position, kDataSize);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/gif/gif_image_decoder.cc b/chromium/third_party/blink/renderer/platform/image-decoders/gif/gif_image_decoder.cc
new file mode 100644
index 00000000000..d7e37fe2be8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/gif/gif_image_decoder.cc
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2006 Apple Computer, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/image-decoders/gif/gif_image_decoder.h"
+
+#include <limits>
+#include <memory>
+#include "third_party/blink/renderer/platform/image-decoders/gif/gif_image_reader.h"
+#include "third_party/blink/renderer/platform/wtf/not_found.h"
+
+namespace blink {
+
+GIFImageDecoder::GIFImageDecoder(AlphaOption alpha_option,
+ const ColorBehavior& color_behavior,
+ size_t max_decoded_bytes)
+ : ImageDecoder(alpha_option, color_behavior, max_decoded_bytes),
+ repetition_count_(kAnimationLoopOnce) {}
+
+GIFImageDecoder::~GIFImageDecoder() = default;
+
+void GIFImageDecoder::OnSetData(SegmentReader* data) {
+ if (reader_)
+ reader_->SetData(data);
+}
+
+int GIFImageDecoder::RepetitionCount() const {
+ // This value can arrive at any point in the image data stream. Most GIFs
+ // in the wild declare it near the beginning of the file, so it usually is
+ // set by the time we've decoded the size, but (depending on the GIF and the
+ // packets sent back by the webserver) not always. If the reader hasn't
+ // seen a loop count yet, it will return kCLoopCountNotSeen, in which case we
+ // should default to looping once (the initial value for
+ // |repetition_count_|).
+ //
+ // There are some additional wrinkles here. First, ImageSource::Clear()
+ // may destroy the reader, making the result from the reader _less_
+ // authoritative on future calls if the recreated reader hasn't seen the
+ // loop count. We don't need to special-case this because in this case the
+ // new reader will once again return kCLoopCountNotSeen, and we won't
+ // overwrite the cached correct value.
+ //
+ // Second, a GIF might never set a loop count at all, in which case we
+ // should continue to treat it as a "loop once" animation. We don't need
+ // special code here either, because in this case we'll never change
+ // |repetition_count_| from its default value.
+ //
+ // Third, we use the same GIFImageReader for counting frames and we might
+ // see the loop count and then encounter a decoding error which happens
+ // later in the stream. It is also possible that no frames are in the
+ // stream. In these cases we should just loop once.
+ if (IsAllDataReceived() && ParseCompleted() && reader_->ImagesCount() == 1)
+ repetition_count_ = kAnimationNone;
+ else if (Failed() || (reader_ && (!reader_->ImagesCount())))
+ repetition_count_ = kAnimationLoopOnce;
+ else if (reader_ && reader_->LoopCount() != kCLoopCountNotSeen)
+ repetition_count_ = reader_->LoopCount();
+ return repetition_count_;
+}
+
+bool GIFImageDecoder::FrameIsReceivedAtIndex(size_t index) const {
+ return reader_ && (index < reader_->ImagesCount()) &&
+ reader_->FrameContext(index)->IsComplete();
+}
+
+TimeDelta GIFImageDecoder::FrameDurationAtIndex(size_t index) const {
+ return (reader_ && (index < reader_->ImagesCount()) &&
+ reader_->FrameContext(index)->IsHeaderDefined())
+ ? TimeDelta::FromMilliseconds(
+ reader_->FrameContext(index)->DelayTime())
+ : TimeDelta();
+}
+
+bool GIFImageDecoder::SetFailed() {
+ reader_.reset();
+ return ImageDecoder::SetFailed();
+}
+
+bool GIFImageDecoder::HaveDecodedRow(size_t frame_index,
+ GIFRow::const_iterator row_begin,
+ size_t width,
+ size_t row_number,
+ unsigned repeat_count,
+ bool write_transparent_pixels) {
+ const GIFFrameContext* frame_context = reader_->FrameContext(frame_index);
+ // The pixel data and coordinates supplied to us are relative to the frame's
+ // origin within the entire image size, i.e.
+ // (frameC_context->xOffset, frame_context->yOffset). There is no guarantee
+ // that width == (size().width() - frame_context->xOffset), so
+ // we must ensure we don't run off the end of either the source data or the
+ // row's X-coordinates.
+ const int x_begin = frame_context->XOffset();
+ const int y_begin = frame_context->YOffset() + row_number;
+ const int x_end = std::min(static_cast<int>(frame_context->XOffset() + width),
+ Size().Width());
+ const int y_end = std::min(
+ static_cast<int>(frame_context->YOffset() + row_number + repeat_count),
+ Size().Height());
+ if (!width || (x_begin < 0) || (y_begin < 0) || (x_end <= x_begin) ||
+ (y_end <= y_begin))
+ return true;
+
+ const GIFColorMap::Table& color_table =
+ frame_context->LocalColorMap().IsDefined()
+ ? frame_context->LocalColorMap().GetTable()
+ : reader_->GlobalColorMap().GetTable();
+
+ if (color_table.IsEmpty())
+ return true;
+
+ GIFColorMap::Table::const_iterator color_table_iter = color_table.begin();
+
+ // Initialize the frame if necessary.
+ ImageFrame& buffer = frame_buffer_cache_[frame_index];
+ if (!InitFrameBuffer(frame_index))
+ return false;
+
+ const size_t transparent_pixel = frame_context->TransparentPixel();
+ GIFRow::const_iterator row_end = row_begin + (x_end - x_begin);
+ ImageFrame::PixelData* current_address = buffer.GetAddr(x_begin, y_begin);
+
+ // We may or may not need to write transparent pixels to the buffer.
+ // If we're compositing against a previous image, it's wrong, and if
+ // we're writing atop a cleared, fully transparent buffer, it's
+ // unnecessary; but if we're decoding an interlaced gif and
+ // displaying it "Haeberli"-style, we must write these for passes
+ // beyond the first, or the initial passes will "show through" the
+ // later ones.
+ //
+ // The loops below are almost identical. One writes a transparent pixel
+ // and one doesn't based on the value of |write_transparent_pixels|.
+ // The condition check is taken out of the loop to enhance performance.
+ // This optimization reduces decoding time by about 15% for a 3MB image.
+ if (write_transparent_pixels) {
+ for (; row_begin != row_end; ++row_begin, ++current_address) {
+ const size_t source_value = *row_begin;
+ if ((source_value != transparent_pixel) &&
+ (source_value < color_table.size())) {
+ *current_address = color_table_iter[source_value];
+ } else {
+ *current_address = 0;
+ current_buffer_saw_alpha_ = true;
+ }
+ }
+ } else {
+ for (; row_begin != row_end; ++row_begin, ++current_address) {
+ const size_t source_value = *row_begin;
+ if ((source_value != transparent_pixel) &&
+ (source_value < color_table.size()))
+ *current_address = color_table_iter[source_value];
+ else
+ current_buffer_saw_alpha_ = true;
+ }
+ }
+
+ // Tell the frame to copy the row data if need be.
+ if (repeat_count > 1)
+ buffer.CopyRowNTimes(x_begin, x_end, y_begin, y_end);
+
+ buffer.SetPixelsChanged(true);
+ return true;
+}
+
+bool GIFImageDecoder::ParseCompleted() const {
+ return reader_ && reader_->ParseCompleted();
+}
+
+bool GIFImageDecoder::FrameComplete(size_t frame_index) {
+ // Initialize the frame if necessary. Some GIFs insert do-nothing frames,
+ // in which case we never reach HaveDecodedRow() before getting here.
+ if (!InitFrameBuffer(frame_index))
+ return SetFailed();
+
+ if (!current_buffer_saw_alpha_)
+ CorrectAlphaWhenFrameBufferSawNoAlpha(frame_index);
+
+ frame_buffer_cache_[frame_index].SetStatus(ImageFrame::kFrameComplete);
+
+ return true;
+}
+
+void GIFImageDecoder::ClearFrameBuffer(size_t frame_index) {
+ if (reader_ && frame_buffer_cache_[frame_index].GetStatus() ==
+ ImageFrame::kFramePartial) {
+ // Reset the state of the partial frame in the reader so that the frame
+ // can be decoded again when requested.
+ reader_->ClearDecodeState(frame_index);
+ }
+ ImageDecoder::ClearFrameBuffer(frame_index);
+}
+
+size_t GIFImageDecoder::DecodeFrameCount() {
+ Parse(kGIFFrameCountQuery);
+ // If decoding fails, |reader_| will have been destroyed. Instead of
+ // returning 0 in this case, return the existing number of frames. This way
+ // if we get halfway through the image before decoding fails, we won't
+ // suddenly start reporting that the image has zero frames.
+ return Failed() ? frame_buffer_cache_.size() : reader_->ImagesCount();
+}
+
+void GIFImageDecoder::InitializeNewFrame(size_t index) {
+ ImageFrame* buffer = &frame_buffer_cache_[index];
+ const GIFFrameContext* frame_context = reader_->FrameContext(index);
+ buffer->SetOriginalFrameRect(
+ Intersection(frame_context->FrameRect(), IntRect(IntPoint(), Size())));
+ buffer->SetDuration(TimeDelta::FromMilliseconds(frame_context->DelayTime()));
+ buffer->SetDisposalMethod(frame_context->GetDisposalMethod());
+ buffer->SetRequiredPreviousFrameIndex(
+ FindRequiredPreviousFrame(index, false));
+}
+
+void GIFImageDecoder::Decode(size_t index) {
+ Parse(kGIFFrameCountQuery);
+
+ if (Failed())
+ return;
+
+ UpdateAggressivePurging(index);
+
+ Vector<size_t> frames_to_decode = FindFramesToDecode(index);
+ for (auto i = frames_to_decode.rbegin(); i != frames_to_decode.rend(); ++i) {
+ if (!reader_->Decode(*i)) {
+ SetFailed();
+ return;
+ }
+
+ // If this returns false, we need more data to continue decoding.
+ if (!PostDecodeProcessing(*i))
+ break;
+ }
+
+ // It is also a fatal error if all data is received and we have decoded all
+ // frames available but the file is truncated.
+ if (index >= frame_buffer_cache_.size() - 1 && IsAllDataReceived() &&
+ reader_ && !reader_->ParseCompleted())
+ SetFailed();
+}
+
+void GIFImageDecoder::Parse(GIFParseQuery query) {
+ if (Failed())
+ return;
+
+ if (!reader_) {
+ reader_ = std::make_unique<GIFImageReader>(this);
+ reader_->SetData(data_);
+ }
+
+ if (!reader_->Parse(query))
+ SetFailed();
+}
+
+void GIFImageDecoder::OnInitFrameBuffer(size_t frame_index) {
+ current_buffer_saw_alpha_ = false;
+}
+
+bool GIFImageDecoder::CanReusePreviousFrameBuffer(size_t frame_index) const {
+ DCHECK(frame_index < frame_buffer_cache_.size());
+ return frame_buffer_cache_[frame_index].GetDisposalMethod() !=
+ ImageFrame::kDisposeOverwritePrevious;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/gif/gif_image_decoder.h b/chromium/third_party/blink/renderer/platform/image-decoders/gif/gif_image_decoder.h
new file mode 100644
index 00000000000..504d556c1e9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/gif/gif_image_decoder.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2006 Apple Computer, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_GIF_GIF_IMAGE_DECODER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_GIF_GIF_IMAGE_DECODER_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+class GIFImageReader;
+
+using GIFRow = Vector<unsigned char>;
+
+// This class decodes the GIF image format.
+class PLATFORM_EXPORT GIFImageDecoder final : public ImageDecoder {
+ WTF_MAKE_NONCOPYABLE(GIFImageDecoder);
+
+ public:
+ GIFImageDecoder(AlphaOption, const ColorBehavior&, size_t max_decoded_bytes);
+ ~GIFImageDecoder() override;
+
+ enum GIFParseQuery { kGIFSizeQuery, kGIFFrameCountQuery };
+
+ // ImageDecoder:
+ String FilenameExtension() const override { return "gif"; }
+ void OnSetData(SegmentReader* data) override;
+ int RepetitionCount() const override;
+ bool FrameIsReceivedAtIndex(size_t) const override;
+ TimeDelta FrameDurationAtIndex(size_t) const override;
+ // CAUTION: SetFailed() deletes |reader_|. Be careful to avoid
+ // accessing deleted memory, especially when calling this from inside
+ // GIFImageReader!
+ bool SetFailed() override;
+
+ // Callbacks from the GIF reader.
+ bool HaveDecodedRow(size_t frame_index,
+ GIFRow::const_iterator row_begin,
+ size_t width,
+ size_t row_number,
+ unsigned repeat_count,
+ bool write_transparent_pixels);
+ bool FrameComplete(size_t frame_index);
+
+ // For testing.
+ bool ParseCompleted() const;
+
+ private:
+ // ImageDecoder:
+ void ClearFrameBuffer(size_t frame_index) override;
+ virtual void DecodeSize() { Parse(kGIFSizeQuery); }
+ size_t DecodeFrameCount() override;
+ void InitializeNewFrame(size_t) override;
+ void Decode(size_t) override;
+
+ // Parses as much as is needed to answer the query, ignoring bitmap
+ // data. If parsing fails, sets the "decode failure" flag.
+ void Parse(GIFParseQuery);
+
+ // Reset the alpha tracker for this frame. Before calling this method, the
+ // caller must verify that the frame exists.
+ void OnInitFrameBuffer(size_t) override;
+
+ // When the disposal method of the frame is DisposeOverWritePrevious, the
+ // next frame will use the previous frame's buffer as its starting state, so
+ // we can't take over the data in that case. Before calling this method, the
+ // caller must verify that the frame exists.
+ bool CanReusePreviousFrameBuffer(size_t) const override;
+
+ bool current_buffer_saw_alpha_;
+ mutable int repetition_count_;
+ std::unique_ptr<GIFImageReader> reader_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/gif/gif_image_decoder_test.cc b/chromium/third_party/blink/renderer/platform/image-decoders/gif/gif_image_decoder_test.cc
new file mode 100644
index 00000000000..1991f29bb08
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/gif/gif_image_decoder_test.cc
@@ -0,0 +1,507 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/renderer/platform/image-decoders/gif/gif_image_decoder.h"
+
+#include <memory>
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/web_data.h"
+#include "third_party/blink/public/platform/web_size.h"
+#include "third_party/blink/renderer/platform/image-decoders/image_decoder_test_helpers.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+namespace {
+
+const char kLayoutTestResourcesDir[] = "LayoutTests/images/resources";
+
+std::unique_ptr<ImageDecoder> CreateDecoder() {
+ return std::make_unique<GIFImageDecoder>(
+ ImageDecoder::kAlphaNotPremultiplied, ColorBehavior::TransformToSRGB(),
+ ImageDecoder::kNoDecodedImageByteLimit);
+}
+
+void TestRepetitionCount(const char* dir,
+ const char* file,
+ int expected_repetition_count) {
+ std::unique_ptr<ImageDecoder> decoder = CreateDecoder();
+ scoped_refptr<SharedBuffer> data = ReadFile(dir, file);
+ ASSERT_TRUE(data.get());
+ decoder->SetData(data.get(), true);
+ EXPECT_EQ(kAnimationLoopOnce,
+ decoder->RepetitionCount()); // Default value before decode.
+
+ for (size_t i = 0; i < decoder->FrameCount(); ++i) {
+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(i);
+ EXPECT_EQ(ImageFrame::kFrameComplete, frame->GetStatus());
+ }
+
+ EXPECT_EQ(expected_repetition_count,
+ decoder->RepetitionCount()); // Expected value after decode.
+}
+
+} // anonymous namespace
+
+TEST(GIFImageDecoderTest, decodeTwoFrames) {
+ std::unique_ptr<ImageDecoder> decoder = CreateDecoder();
+
+ scoped_refptr<SharedBuffer> data =
+ ReadFile(kLayoutTestResourcesDir, "animated.gif");
+ ASSERT_TRUE(data.get());
+ decoder->SetData(data.get(), true);
+ EXPECT_EQ(kAnimationLoopOnce, decoder->RepetitionCount());
+
+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(0);
+ uint32_t generation_id0 = frame->Bitmap().getGenerationID();
+ EXPECT_EQ(ImageFrame::kFrameComplete, frame->GetStatus());
+ EXPECT_EQ(16, frame->Bitmap().width());
+ EXPECT_EQ(16, frame->Bitmap().height());
+
+ frame = decoder->DecodeFrameBufferAtIndex(1);
+ uint32_t generation_id1 = frame->Bitmap().getGenerationID();
+ EXPECT_EQ(ImageFrame::kFrameComplete, frame->GetStatus());
+ EXPECT_EQ(16, frame->Bitmap().width());
+ EXPECT_EQ(16, frame->Bitmap().height());
+ EXPECT_TRUE(generation_id0 != generation_id1);
+
+ EXPECT_EQ(2u, decoder->FrameCount());
+ EXPECT_EQ(kAnimationLoopInfinite, decoder->RepetitionCount());
+}
+
+TEST(GIFImageDecoderTest, crbug779261) {
+ std::unique_ptr<ImageDecoder> decoder = CreateDecoder();
+ scoped_refptr<SharedBuffer> data =
+ ReadFile(kLayoutTestResourcesDir, "crbug779261.gif");
+ ASSERT_TRUE(data.get());
+ decoder->SetData(data.get(), true);
+
+ for (size_t i = 0; i < decoder->FrameCount(); ++i) {
+ // In crbug.com/779261, an independent, transparent frame following an
+ // opaque frame failed to decode. This image has an opaque frame 0 with
+ // DisposalMethod::kDisposeOverwriteBgcolor, making frame 1, which has
+ // transparency, independent and contain alpha.
+ const bool has_alpha = 0 == i ? false : true;
+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(i);
+ EXPECT_EQ(ImageFrame::kFrameComplete, frame->GetStatus());
+ EXPECT_EQ(has_alpha, frame->HasAlpha());
+ }
+
+ EXPECT_FALSE(decoder->Failed());
+}
+
+TEST(GIFImageDecoderTest, parseAndDecode) {
+ std::unique_ptr<ImageDecoder> decoder = CreateDecoder();
+
+ scoped_refptr<SharedBuffer> data =
+ ReadFile(kLayoutTestResourcesDir, "animated.gif");
+ ASSERT_TRUE(data.get());
+ decoder->SetData(data.get(), true);
+ EXPECT_EQ(kAnimationLoopOnce, decoder->RepetitionCount());
+
+ // This call will parse the entire file.
+ EXPECT_EQ(2u, decoder->FrameCount());
+
+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(0);
+ EXPECT_EQ(ImageFrame::kFrameComplete, frame->GetStatus());
+ EXPECT_EQ(16, frame->Bitmap().width());
+ EXPECT_EQ(16, frame->Bitmap().height());
+
+ frame = decoder->DecodeFrameBufferAtIndex(1);
+ EXPECT_EQ(ImageFrame::kFrameComplete, frame->GetStatus());
+ EXPECT_EQ(16, frame->Bitmap().width());
+ EXPECT_EQ(16, frame->Bitmap().height());
+ EXPECT_EQ(kAnimationLoopInfinite, decoder->RepetitionCount());
+}
+
+TEST(GIFImageDecoderTest, parseByteByByte) {
+ std::unique_ptr<ImageDecoder> decoder = CreateDecoder();
+
+ const Vector<char> data =
+ ReadFile(kLayoutTestResourcesDir, "animated.gif")->Copy();
+
+ size_t frame_count = 0;
+
+ // Pass data to decoder byte by byte.
+ for (size_t length = 1; length <= data.size(); ++length) {
+ scoped_refptr<SharedBuffer> temp_data =
+ SharedBuffer::Create(data.data(), length);
+ decoder->SetData(temp_data.get(), length == data.size());
+
+ EXPECT_LE(frame_count, decoder->FrameCount());
+ frame_count = decoder->FrameCount();
+ }
+
+ EXPECT_EQ(2u, decoder->FrameCount());
+
+ decoder->DecodeFrameBufferAtIndex(0);
+ decoder->DecodeFrameBufferAtIndex(1);
+ EXPECT_EQ(kAnimationLoopInfinite, decoder->RepetitionCount());
+}
+
+TEST(GIFImageDecoderTest, parseAndDecodeByteByByte) {
+ TestByteByByteDecode(&CreateDecoder, kLayoutTestResourcesDir,
+ "animated-gif-with-offsets.gif", 5u,
+ kAnimationLoopInfinite);
+}
+
+TEST(GIFImageDecoderTest, brokenSecondFrame) {
+ std::unique_ptr<ImageDecoder> decoder = CreateDecoder();
+
+ scoped_refptr<SharedBuffer> data =
+ ReadFile(kDecodersTestingDir, "broken.gif");
+ ASSERT_TRUE(data.get());
+ decoder->SetData(data.get(), true);
+
+ // One frame is detected but cannot be decoded.
+ EXPECT_EQ(1u, decoder->FrameCount());
+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(1);
+ EXPECT_FALSE(frame);
+}
+
+TEST(GIFImageDecoderTest, progressiveDecode) {
+ TestProgressiveDecoding(&CreateDecoder, kDecodersTestingDir, "radient.gif");
+}
+
+TEST(GIFImageDecoderTest, allDataReceivedTruncation) {
+ std::unique_ptr<ImageDecoder> decoder = CreateDecoder();
+
+ const Vector<char> data =
+ ReadFile(kLayoutTestResourcesDir, "animated.gif")->Copy();
+
+ ASSERT_GE(data.size(), 10u);
+ scoped_refptr<SharedBuffer> temp_data =
+ SharedBuffer::Create(data.data(), data.size() - 10);
+ decoder->SetData(temp_data.get(), true);
+
+ EXPECT_EQ(2u, decoder->FrameCount());
+ EXPECT_FALSE(decoder->Failed());
+
+ decoder->DecodeFrameBufferAtIndex(0);
+ EXPECT_FALSE(decoder->Failed());
+ decoder->DecodeFrameBufferAtIndex(1);
+ EXPECT_TRUE(decoder->Failed());
+}
+
+TEST(GIFImageDecoderTest, frameIsComplete) {
+ std::unique_ptr<ImageDecoder> decoder = CreateDecoder();
+
+ scoped_refptr<SharedBuffer> data =
+ ReadFile(kLayoutTestResourcesDir, "animated.gif");
+ ASSERT_TRUE(data.get());
+ decoder->SetData(data.get(), true);
+
+ EXPECT_EQ(2u, decoder->FrameCount());
+ EXPECT_FALSE(decoder->Failed());
+ EXPECT_TRUE(decoder->FrameIsReceivedAtIndex(0));
+ EXPECT_TRUE(decoder->FrameIsReceivedAtIndex(1));
+ EXPECT_EQ(kAnimationLoopInfinite, decoder->RepetitionCount());
+}
+
+TEST(GIFImageDecoderTest, frameIsCompleteLoading) {
+ std::unique_ptr<ImageDecoder> decoder = CreateDecoder();
+
+ scoped_refptr<SharedBuffer> data_buffer =
+ ReadFile(kLayoutTestResourcesDir, "animated.gif");
+ ASSERT_TRUE(data_buffer.get());
+ const Vector<char> data = data_buffer->Copy();
+
+ ASSERT_GE(data.size(), 10u);
+ scoped_refptr<SharedBuffer> temp_data =
+ SharedBuffer::Create(data.data(), data.size() - 10);
+ decoder->SetData(temp_data.get(), false);
+
+ EXPECT_EQ(2u, decoder->FrameCount());
+ EXPECT_FALSE(decoder->Failed());
+ EXPECT_TRUE(decoder->FrameIsReceivedAtIndex(0));
+ EXPECT_FALSE(decoder->FrameIsReceivedAtIndex(1));
+
+ decoder->SetData(data_buffer.get(), true);
+ EXPECT_EQ(2u, decoder->FrameCount());
+ EXPECT_TRUE(decoder->FrameIsReceivedAtIndex(0));
+ EXPECT_TRUE(decoder->FrameIsReceivedAtIndex(1));
+}
+
+TEST(GIFImageDecoderTest, badTerminator) {
+ scoped_refptr<SharedBuffer> reference_data =
+ ReadFile(kDecodersTestingDir, "radient.gif");
+ scoped_refptr<SharedBuffer> test_data =
+ ReadFile(kDecodersTestingDir, "radient-bad-terminator.gif");
+ ASSERT_TRUE(reference_data.get());
+ ASSERT_TRUE(test_data.get());
+
+ std::unique_ptr<ImageDecoder> reference_decoder = CreateDecoder();
+ reference_decoder->SetData(reference_data.get(), true);
+ EXPECT_EQ(1u, reference_decoder->FrameCount());
+ ImageFrame* reference_frame = reference_decoder->DecodeFrameBufferAtIndex(0);
+ DCHECK(reference_frame);
+
+ std::unique_ptr<ImageDecoder> test_decoder = CreateDecoder();
+ test_decoder->SetData(test_data.get(), true);
+ EXPECT_EQ(1u, test_decoder->FrameCount());
+ ImageFrame* test_frame = test_decoder->DecodeFrameBufferAtIndex(0);
+ DCHECK(test_frame);
+
+ EXPECT_EQ(HashBitmap(reference_frame->Bitmap()),
+ HashBitmap(test_frame->Bitmap()));
+}
+
+TEST(GIFImageDecoderTest, updateRequiredPreviousFrameAfterFirstDecode) {
+ TestUpdateRequiredPreviousFrameAfterFirstDecode(
+ &CreateDecoder, kLayoutTestResourcesDir, "animated-10color.gif");
+}
+
+TEST(GIFImageDecoderTest, randomFrameDecode) {
+ // Single frame image.
+ TestRandomFrameDecode(&CreateDecoder, kDecodersTestingDir, "radient.gif");
+ // Multiple frame images.
+ TestRandomFrameDecode(&CreateDecoder, kLayoutTestResourcesDir,
+ "animated-gif-with-offsets.gif");
+ TestRandomFrameDecode(&CreateDecoder, kLayoutTestResourcesDir,
+ "animated-10color.gif");
+}
+
+TEST(GIFImageDecoderTest, randomDecodeAfterClearFrameBufferCache) {
+ // Single frame image.
+ TestRandomDecodeAfterClearFrameBufferCache(
+ &CreateDecoder, kDecodersTestingDir, "radient.gif");
+ // Multiple frame images.
+ TestRandomDecodeAfterClearFrameBufferCache(
+ &CreateDecoder, kLayoutTestResourcesDir, "animated-gif-with-offsets.gif");
+ TestRandomDecodeAfterClearFrameBufferCache(
+ &CreateDecoder, kLayoutTestResourcesDir, "animated-10color.gif");
+}
+
+TEST(GIFImageDecoderTest, resumePartialDecodeAfterClearFrameBufferCache) {
+ TestResumePartialDecodeAfterClearFrameBufferCache(
+ &CreateDecoder, kLayoutTestResourcesDir, "animated-10color.gif");
+}
+
+// The first LZW codes in the image are invalid values that try to create a loop
+// in the dictionary. Decoding should fail, but not infinitely loop or corrupt
+// memory.
+TEST(GIFImageDecoderTest, badInitialCode) {
+ scoped_refptr<SharedBuffer> test_data =
+ ReadFile(kDecodersTestingDir, "bad-initial-code.gif");
+ ASSERT_TRUE(test_data.get());
+
+ std::unique_ptr<ImageDecoder> test_decoder = CreateDecoder();
+ test_decoder->SetData(test_data.get(), true);
+ EXPECT_EQ(1u, test_decoder->FrameCount());
+ ASSERT_TRUE(test_decoder->DecodeFrameBufferAtIndex(0));
+ EXPECT_TRUE(test_decoder->Failed());
+}
+
+// The image has an invalid LZW code that exceeds dictionary size. Decoding
+// should fail.
+TEST(GIFImageDecoderTest, badCode) {
+ scoped_refptr<SharedBuffer> test_data =
+ ReadFile(kDecodersTestingDir, "bad-code.gif");
+ ASSERT_TRUE(test_data.get());
+
+ std::unique_ptr<ImageDecoder> test_decoder = CreateDecoder();
+ test_decoder->SetData(test_data.get(), true);
+ EXPECT_EQ(1u, test_decoder->FrameCount());
+ ASSERT_TRUE(test_decoder->DecodeFrameBufferAtIndex(0));
+ EXPECT_TRUE(test_decoder->Failed());
+}
+
+TEST(GIFImageDecoderTest, invalidDisposalMethod) {
+ std::unique_ptr<ImageDecoder> decoder = CreateDecoder();
+
+ // The image has 2 frames, with disposal method 4 and 5, respectively.
+ scoped_refptr<SharedBuffer> data =
+ ReadFile(kDecodersTestingDir, "invalid-disposal-method.gif");
+ ASSERT_TRUE(data.get());
+ decoder->SetData(data.get(), true);
+
+ EXPECT_EQ(2u, decoder->FrameCount());
+ // Disposal method 4 is converted to ImageFrame::DisposeOverwritePrevious.
+ EXPECT_EQ(ImageFrame::kDisposeOverwritePrevious,
+ decoder->DecodeFrameBufferAtIndex(0)->GetDisposalMethod());
+ // Disposal method 5 is ignored.
+ EXPECT_EQ(ImageFrame::kDisposeNotSpecified,
+ decoder->DecodeFrameBufferAtIndex(1)->GetDisposalMethod());
+}
+
+TEST(GIFImageDecoderTest, firstFrameHasGreaterSizeThanScreenSize) {
+ const Vector<char> full_data =
+ ReadFile(kDecodersTestingDir,
+ "first-frame-has-greater-size-than-screen-size.gif")
+ ->Copy();
+
+ std::unique_ptr<ImageDecoder> decoder;
+ IntSize frame_size;
+
+ // Compute hashes when the file is truncated.
+ for (size_t i = 1; i <= full_data.size(); ++i) {
+ decoder = CreateDecoder();
+ scoped_refptr<SharedBuffer> data =
+ SharedBuffer::Create(full_data.data(), i);
+ decoder->SetData(data.get(), i == full_data.size());
+
+ if (decoder->IsSizeAvailable() && !frame_size.Width() &&
+ !frame_size.Height()) {
+ frame_size = decoder->DecodedSize();
+ continue;
+ }
+
+ ASSERT_EQ(frame_size.Width(), decoder->DecodedSize().Width());
+ ASSERT_EQ(frame_size.Height(), decoder->DecodedSize().Height());
+ }
+}
+
+TEST(GIFImageDecoderTest, verifyRepetitionCount) {
+ TestRepetitionCount(kLayoutTestResourcesDir, "full2loop.gif", 2);
+ TestRepetitionCount(kDecodersTestingDir, "radient.gif", kAnimationNone);
+}
+
+TEST(GIFImageDecoderTest, bitmapAlphaType) {
+ scoped_refptr<SharedBuffer> full_data_buffer =
+ ReadFile(kDecodersTestingDir, "radient.gif");
+ ASSERT_TRUE(full_data_buffer.get());
+ const Vector<char> full_data = full_data_buffer->Copy();
+
+ // Empirically chosen truncation size:
+ // a) large enough to produce a partial frame &&
+ // b) small enough to not fully decode the frame
+ const size_t kTruncateSize = 800;
+ ASSERT_TRUE(kTruncateSize < full_data.size());
+ scoped_refptr<SharedBuffer> partial_data =
+ SharedBuffer::Create(full_data.data(), kTruncateSize);
+
+ std::unique_ptr<ImageDecoder> premul_decoder =
+ std::make_unique<GIFImageDecoder>(ImageDecoder::kAlphaPremultiplied,
+ ColorBehavior::TransformToSRGB(),
+ ImageDecoder::kNoDecodedImageByteLimit);
+ std::unique_ptr<ImageDecoder> unpremul_decoder =
+ std::make_unique<GIFImageDecoder>(ImageDecoder::kAlphaNotPremultiplied,
+ ColorBehavior::TransformToSRGB(),
+ ImageDecoder::kNoDecodedImageByteLimit);
+
+ // Partially decoded frame => the frame alpha type is unknown and should
+ // reflect the requested format.
+ premul_decoder->SetData(partial_data.get(), false);
+ ASSERT_TRUE(premul_decoder->FrameCount());
+ unpremul_decoder->SetData(partial_data.get(), false);
+ ASSERT_TRUE(unpremul_decoder->FrameCount());
+ ImageFrame* premul_frame = premul_decoder->DecodeFrameBufferAtIndex(0);
+ EXPECT_TRUE(premul_frame &&
+ premul_frame->GetStatus() != ImageFrame::kFrameComplete);
+ EXPECT_EQ(premul_frame->Bitmap().alphaType(), kPremul_SkAlphaType);
+ ImageFrame* unpremul_frame = unpremul_decoder->DecodeFrameBufferAtIndex(0);
+ EXPECT_TRUE(unpremul_frame &&
+ unpremul_frame->GetStatus() != ImageFrame::kFrameComplete);
+ EXPECT_EQ(unpremul_frame->Bitmap().alphaType(), kUnpremul_SkAlphaType);
+
+ // Fully decoded frame => the frame alpha type is known (opaque).
+ premul_decoder->SetData(full_data_buffer.get(), true);
+ ASSERT_TRUE(premul_decoder->FrameCount());
+ unpremul_decoder->SetData(full_data_buffer.get(), true);
+ ASSERT_TRUE(unpremul_decoder->FrameCount());
+ premul_frame = premul_decoder->DecodeFrameBufferAtIndex(0);
+ EXPECT_TRUE(premul_frame &&
+ premul_frame->GetStatus() == ImageFrame::kFrameComplete);
+ EXPECT_EQ(premul_frame->Bitmap().alphaType(), kOpaque_SkAlphaType);
+ unpremul_frame = unpremul_decoder->DecodeFrameBufferAtIndex(0);
+ EXPECT_TRUE(unpremul_frame &&
+ unpremul_frame->GetStatus() == ImageFrame::kFrameComplete);
+ EXPECT_EQ(unpremul_frame->Bitmap().alphaType(), kOpaque_SkAlphaType);
+}
+
+namespace {
+// Needed to exercise ImageDecoder::SetMemoryAllocator, but still does the
+// default allocation.
+class Allocator final : public SkBitmap::Allocator {
+ bool allocPixelRef(SkBitmap* dst) override { return dst->tryAllocPixels(); }
+};
+}
+
+// Ensure that calling SetMemoryAllocator does not short-circuit
+// InitializeNewFrame.
+TEST(GIFImageDecoderTest, externalAllocator) {
+ auto data = ReadFile(kLayoutTestResourcesDir, "boston.gif");
+ ASSERT_TRUE(data.get());
+
+ auto decoder = CreateDecoder();
+ decoder->SetData(data.get(), true);
+
+ Allocator allocator;
+ decoder->SetMemoryAllocator(&allocator);
+ EXPECT_EQ(1u, decoder->FrameCount());
+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(0);
+ decoder->SetMemoryAllocator(nullptr);
+
+ ASSERT_TRUE(frame);
+ EXPECT_EQ(IntRect(IntPoint(), decoder->Size()), frame->OriginalFrameRect());
+ EXPECT_FALSE(frame->HasAlpha());
+}
+
+TEST(GIFImageDecoderTest, recursiveDecodeFailure) {
+ auto data = ReadFile(kLayoutTestResourcesDir, "count-down-color-test.gif");
+ ASSERT_TRUE(data.get());
+
+ {
+ auto decoder = CreateDecoder();
+ decoder->SetData(data.get(), true);
+ for (size_t i = 0; i <= 3; ++i) {
+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(i);
+ ASSERT_NE(frame, nullptr);
+ ASSERT_EQ(frame->GetStatus(), ImageFrame::kFrameComplete);
+ }
+ }
+
+ // Modify data to have an error in frame 2.
+ const size_t kErrorOffset = 15302u;
+ scoped_refptr<SharedBuffer> modified_data =
+ SharedBuffer::Create(data->Data(), kErrorOffset);
+ modified_data->Append("A", 1u);
+ modified_data->Append(data->Data() + kErrorOffset + 1,
+ data->size() - kErrorOffset - 1);
+ {
+ auto decoder = CreateDecoder();
+ decoder->SetData(modified_data.get(), true);
+ decoder->DecodeFrameBufferAtIndex(2);
+ ASSERT_TRUE(decoder->Failed());
+ }
+
+ {
+ // Decode frame 3, recursively decoding frame 2, which 3 depends on.
+ auto decoder = CreateDecoder();
+ decoder->SetData(modified_data.get(), true);
+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(3);
+ EXPECT_TRUE(decoder->Failed());
+ ASSERT_NE(frame, nullptr);
+ ASSERT_EQ(frame->RequiredPreviousFrameIndex(), 2u);
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/gif/gif_image_reader.cc b/chromium/third_party/blink/renderer/platform/image-decoders/gif/gif_image_reader.cc
new file mode 100644
index 00000000000..908a76fc7ba
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/gif/gif_image_reader.cc
@@ -0,0 +1,902 @@
+/* -*- Mode: C; tab-width: 2; 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 mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Chris Saari <saari@netscape.com>
+ * Apple Computer
+ *
+ * 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 ***** */
+
+/*
+The Graphics Interchange Format(c) is the copyright property of CompuServe
+Incorporated. Only CompuServe Incorporated is authorized to define, redefine,
+enhance, alter, modify or change in any way the definition of the format.
+
+CompuServe Incorporated hereby grants a limited, non-exclusive, royalty-free
+license for the use of the Graphics Interchange Format(sm) in computer
+software; computer software utilizing GIF(sm) must acknowledge ownership of the
+Graphics Interchange Format and its Service Mark by CompuServe Incorporated, in
+User and Technical Documentation. Computer software utilizing GIF, which is
+distributed or may be distributed without User or Technical Documentation must
+display to the screen or printer a message acknowledging ownership of the
+Graphics Interchange Format and the Service Mark by CompuServe Incorporated; in
+this case, the acknowledgement may be displayed in an opening screen or leading
+banner, or a closing screen or trailing banner. A message such as the following
+may be used:
+
+ "The Graphics Interchange Format(c) is the Copyright property of
+ CompuServe Incorporated. GIF(sm) is a Service Mark property of
+ CompuServe Incorporated."
+
+For further information, please contact :
+
+ CompuServe Incorporated
+ Graphics Technology Department
+ 5000 Arlington Center Boulevard
+ Columbus, Ohio 43220
+ U. S. A.
+
+CompuServe Incorporated maintains a mailing list with all those individuals and
+organizations who wish to receive copies of this document when it is corrected
+or revised. This service is offered free of charge; please provide us with your
+mailing address.
+*/
+
+#include "third_party/blink/renderer/platform/image-decoders/gif/gif_image_reader.h"
+
+#include <string.h>
+#include <algorithm>
+
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/platform/image-decoders/fast_shared_buffer_reader.h"
+
+namespace blink {
+
+namespace {
+
+static constexpr unsigned kMaxColors = 256u;
+static constexpr int kBytesPerColormapEntry = 3;
+
+} // namespace
+
+// GETN(n, s) requests at least 'n' bytes available from 'q', at start of state
+// 's'.
+//
+// Note: the hold will never need to be bigger than 256 bytes, as each GIF block
+// (except colormaps) can never be bigger than 256 bytes. Colormaps are directly
+// copied in the resp. global_colormap or dynamically allocated local_colormap,
+// so a fixed buffer in GIFImageReader is good enough. This buffer is only
+// needed to copy left-over data from one GifWrite call to the next.
+#define GETN(n, s) \
+ do { \
+ bytes_to_consume_ = (n); \
+ state_ = (s); \
+ } while (0)
+
+// Get a 16-bit value stored in little-endian format.
+#define GETINT16(p) ((p)[1] << 8 | (p)[0])
+
+// Send the data to the display front-end.
+bool GIFLZWContext::OutputRow(GIFRow::const_iterator row_begin) {
+ int drow_start = irow;
+ int drow_end = irow;
+
+ // Haeberli-inspired hack for interlaced GIFs: Replicate lines while
+ // displaying to diminish the "venetian-blind" effect as the image is
+ // loaded. Adjust pixel vertical positions to avoid the appearance of the
+ // image crawling up the screen as successive passes are drawn.
+ if (frame_context_->ProgressiveDisplay() && frame_context_->Interlaced() &&
+ ipass < 4) {
+ unsigned row_dup = 0;
+ unsigned row_shift = 0;
+
+ switch (ipass) {
+ case 1:
+ row_dup = 7;
+ row_shift = 3;
+ break;
+ case 2:
+ row_dup = 3;
+ row_shift = 1;
+ break;
+ case 3:
+ row_dup = 1;
+ row_shift = 0;
+ break;
+ default:
+ break;
+ }
+
+ drow_start -= row_shift;
+ drow_end = drow_start + row_dup;
+
+ // Extend if bottom edge isn't covered because of the shift upward.
+ if (((frame_context_->Height() - 1) - drow_end) <= row_shift)
+ drow_end = frame_context_->Height() - 1;
+
+ // Clamp first and last rows to upper and lower edge of image.
+ if (drow_start < 0)
+ drow_start = 0;
+
+ if ((unsigned)drow_end >= frame_context_->Height())
+ drow_end = frame_context_->Height() - 1;
+ }
+
+ // Protect against too much image data.
+ if ((unsigned)drow_start >= frame_context_->Height())
+ return true;
+
+ // CALLBACK: Let the client know we have decoded a row.
+ if (!client_->HaveDecodedRow(frame_context_->FrameId(), row_begin,
+ frame_context_->Width(), drow_start,
+ drow_end - drow_start + 1,
+ frame_context_->ProgressiveDisplay() &&
+ frame_context_->Interlaced() && ipass > 1))
+ return false;
+
+ if (!frame_context_->Interlaced()) {
+ irow++;
+ } else {
+ do {
+ switch (ipass) {
+ case 1:
+ irow += 8;
+ if (irow >= frame_context_->Height()) {
+ ipass++;
+ irow = 4;
+ }
+ break;
+
+ case 2:
+ irow += 8;
+ if (irow >= frame_context_->Height()) {
+ ipass++;
+ irow = 2;
+ }
+ break;
+
+ case 3:
+ irow += 4;
+ if (irow >= frame_context_->Height()) {
+ ipass++;
+ irow = 1;
+ }
+ break;
+
+ case 4:
+ irow += 2;
+ if (irow >= frame_context_->Height()) {
+ ipass++;
+ irow = 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+ } while (irow > (frame_context_->Height() - 1));
+ }
+ return true;
+}
+
+// Performs Lempel-Ziv-Welch decoding. Returns whether decoding was successful.
+// If successful, the block will have been completely consumed and/or
+// rowsRemaining will be 0.
+bool GIFLZWContext::DoLZW(const unsigned char* block, size_t bytes_in_block) {
+ const size_t width = frame_context_->Width();
+
+ if (row_iter == row_buffer.end())
+ return true;
+
+ for (const unsigned char* ch = block; bytes_in_block-- > 0; ch++) {
+ // Feed the next byte into the decoder's 32-bit input buffer.
+ datum += ((int)*ch) << bits;
+ bits += 8;
+
+ // Check for underflow of decoder's 32-bit input buffer.
+ while (bits >= codesize) {
+ // Get the leading variable-length symbol from the data stream.
+ int code = datum & codemask;
+ datum >>= codesize;
+ bits -= codesize;
+
+ // Reset the dictionary to its original state, if requested.
+ if (code == clear_code) {
+ codesize = frame_context_->DataSize() + 1;
+ codemask = (1 << codesize) - 1;
+ avail = clear_code + 2;
+ oldcode = -1;
+ continue;
+ }
+
+ // Check for explicit end-of-stream code.
+ if (code == (clear_code + 1)) {
+ // end-of-stream should only appear after all image data.
+ if (!rows_remaining)
+ return true;
+ return false;
+ }
+
+ const int temp_code = code;
+ unsigned short code_length = 0;
+ if (code < avail) {
+ // This is a pre-existing code, so we already know what it
+ // encodes.
+ code_length = suffix_length[code];
+ row_iter += code_length;
+ } else if (code == avail && oldcode != -1) {
+ // This is a new code just being added to the dictionary.
+ // It must encode the contents of the previous code, plus
+ // the first character of the previous code again.
+ code_length = suffix_length[oldcode] + 1;
+ row_iter += code_length;
+ *--row_iter = firstchar;
+ code = oldcode;
+ } else {
+ // This is an invalid code. The dictionary is just initialized
+ // and the code is incomplete. We don't know how to handle
+ // this case.
+ return false;
+ }
+
+ while (code >= clear_code) {
+ *--row_iter = suffix[code];
+ code = prefix[code];
+ }
+
+ *--row_iter = firstchar = suffix[code];
+
+ // Define a new codeword in the dictionary as long as we've read
+ // more than one value from the stream.
+ if (avail < kMaxDictionaryEntries && oldcode != -1) {
+ prefix[avail] = oldcode;
+ suffix[avail] = firstchar;
+ suffix_length[avail] = suffix_length[oldcode] + 1;
+ ++avail;
+
+ // If we've used up all the codewords of a given length
+ // increase the length of codewords by one bit, but don't
+ // exceed the specified maximum codeword size.
+ if (!(avail & codemask) && avail < kMaxDictionaryEntries) {
+ ++codesize;
+ codemask += avail;
+ }
+ }
+ oldcode = temp_code;
+ row_iter += code_length;
+
+ // Output as many rows as possible.
+ GIFRow::iterator row_begin = row_buffer.begin();
+ for (; row_begin + width <= row_iter; row_begin += width) {
+ if (!OutputRow(row_begin))
+ return false;
+ rows_remaining--;
+ if (!rows_remaining)
+ return true;
+ }
+
+ if (row_begin != row_buffer.begin()) {
+ // Move the remaining bytes to the beginning of the buffer.
+ const size_t bytes_to_copy = row_iter - row_begin;
+ memcpy(row_buffer.begin(), row_begin, bytes_to_copy);
+ row_iter = row_buffer.begin() + bytes_to_copy;
+ }
+ }
+ }
+ return true;
+}
+
+void GIFColorMap::BuildTable(FastSharedBufferReader* reader) {
+ if (!is_defined_ || !table_.IsEmpty())
+ return;
+
+ CHECK_LE(position_ + colors_ * kBytesPerColormapEntry, reader->size());
+ DCHECK_LE(colors_, kMaxColors);
+ char buffer[kMaxColors * kBytesPerColormapEntry];
+ const unsigned char* src_colormap =
+ reinterpret_cast<const unsigned char*>(reader->GetConsecutiveData(
+ position_, colors_ * kBytesPerColormapEntry, buffer));
+ table_.resize(colors_);
+ for (Table::iterator iter = table_.begin(); iter != table_.end(); ++iter) {
+ *iter = SkPackARGB32NoCheck(255, src_colormap[0], src_colormap[1],
+ src_colormap[2]);
+ src_colormap += kBytesPerColormapEntry;
+ }
+}
+
+// Decodes this frame. |frameDecoded| will be set to true if the entire frame is
+// decoded. Returns true if decoding progressed further than before without
+// error, or there is insufficient new data to decode further. Otherwise, a
+// decoding error occurred; returns false in this case.
+bool GIFFrameContext::Decode(FastSharedBufferReader* reader,
+ GIFImageDecoder* client,
+ bool* frame_decoded) {
+ local_color_map_.BuildTable(reader);
+
+ *frame_decoded = false;
+ if (!lzw_context_) {
+ // Wait for more data to properly initialize GIFLZWContext.
+ if (!IsDataSizeDefined() || !IsHeaderDefined())
+ return true;
+
+ lzw_context_ = std::make_unique<GIFLZWContext>(client, this);
+ if (!lzw_context_->PrepareToDecode()) {
+ lzw_context_.reset();
+ return false;
+ }
+
+ current_lzw_block_ = 0;
+ }
+
+ // Some bad GIFs have extra blocks beyond the last row, which we don't want to
+ // decode.
+ while (current_lzw_block_ < lzw_blocks_.size() &&
+ lzw_context_->HasRemainingRows()) {
+ size_t block_position = lzw_blocks_[current_lzw_block_].block_position;
+ size_t block_size = lzw_blocks_[current_lzw_block_].block_size;
+ if (block_position + block_size > reader->size())
+ return false;
+
+ while (block_size) {
+ const char* segment = nullptr;
+ size_t segment_length = reader->GetSomeData(segment, block_position);
+ size_t decode_size = std::min(segment_length, block_size);
+ if (!lzw_context_->DoLZW(reinterpret_cast<const unsigned char*>(segment),
+ decode_size))
+ return false;
+ block_position += decode_size;
+ block_size -= decode_size;
+ }
+ ++current_lzw_block_;
+ }
+
+ // If this frame is data complete then the previous loop must have completely
+ // decoded all LZW blocks.
+ // There will be no more decoding for this frame so it's time to cleanup.
+ if (IsComplete()) {
+ *frame_decoded = true;
+ lzw_context_.reset();
+ }
+ return true;
+}
+
+// Decodes a frame using GIFFrameContext:decode(). Returns true if decoding has
+// progressed, or false if an error has occurred.
+bool GIFImageReader::Decode(size_t frame_index) {
+ FastSharedBufferReader reader(data_);
+ global_color_map_.BuildTable(&reader);
+
+ bool frame_decoded = false;
+ GIFFrameContext* current_frame = frames_[frame_index].get();
+
+ return current_frame->Decode(&reader, client_, &frame_decoded) &&
+ (!frame_decoded || client_->FrameComplete(frame_index));
+}
+
+bool GIFImageReader::Parse(GIFImageDecoder::GIFParseQuery query) {
+ if (bytes_read_ >= data_->size()) {
+ // This data has already been parsed. For example, in deferred
+ // decoding, a DecodingImageGenerator with more data may have already
+ // used this same ImageDecoder to decode. This can happen if two
+ // SkImages created by a DeferredImageDecoder are drawn/prerolled
+ // out of order (with respect to how much data they had at creation
+ // time).
+ return !client_->Failed();
+ }
+
+ return ParseData(bytes_read_, data_->size() - bytes_read_, query);
+}
+
+// Parse incoming GIF data stream into internal data structures.
+// Return true if parsing has progressed or there is not enough data.
+// Return false if a fatal error is encountered.
+bool GIFImageReader::ParseData(size_t data_position,
+ size_t len,
+ GIFImageDecoder::GIFParseQuery query) {
+ if (!len) {
+ // No new data has come in since the last call, just ignore this call.
+ return true;
+ }
+
+ if (len < bytes_to_consume_)
+ return true;
+
+ FastSharedBufferReader reader(data_);
+
+ // A read buffer of 16 bytes is enough to accomodate all possible reads for
+ // parsing.
+ char read_buffer[16];
+
+ // Read as many components from |m_data| as possible. At the beginning of each
+ // iteration, |dataPosition| is advanced by m_bytesToConsume to point to the
+ // next component. |len| is decremented accordingly.
+ while (len >= bytes_to_consume_) {
+ const size_t current_component_position = data_position;
+
+ // Mark the current component as consumed. Note that currentComponent will
+ // remain pointed at this component until the next loop iteration.
+ data_position += bytes_to_consume_;
+ len -= bytes_to_consume_;
+
+ switch (state_) {
+ case GIFLZW:
+ DCHECK(!frames_.IsEmpty());
+ // m_bytesToConsume is the current component size because it hasn't been
+ // updated.
+ frames_.back()->AddLzwBlock(current_component_position,
+ bytes_to_consume_);
+ GETN(1, kGIFSubBlock);
+ break;
+
+ case kGIFLZWStart: {
+ DCHECK(!frames_.IsEmpty());
+ frames_.back()->SetDataSize(static_cast<unsigned char>(
+ reader.GetOneByte(current_component_position)));
+ GETN(1, kGIFSubBlock);
+ break;
+ }
+
+ case kGIFType: {
+ const char* current_component = reader.GetConsecutiveData(
+ current_component_position, 6, read_buffer);
+
+ // All GIF files begin with "GIF87a" or "GIF89a".
+ if (!memcmp(current_component, "GIF89a", 6))
+ version_ = 89;
+ else if (!memcmp(current_component, "GIF87a", 6))
+ version_ = 87;
+ else
+ return false;
+ GETN(7, kGIFGlobalHeader);
+ break;
+ }
+
+ case kGIFGlobalHeader: {
+ const unsigned char* current_component =
+ reinterpret_cast<const unsigned char*>(reader.GetConsecutiveData(
+ current_component_position, 5, read_buffer));
+
+ // This is the height and width of the "screen" or frame into which
+ // images are rendered. The individual images can be smaller than
+ // the screen size and located with an origin anywhere within the
+ // screen.
+ // Note that we don't inform the client of the size yet, as it might
+ // change after we read the first frame's image header.
+ screen_width_ = GETINT16(current_component);
+ screen_height_ = GETINT16(current_component + 2);
+
+ const size_t global_color_map_colors = 2
+ << (current_component[4] & 0x07);
+
+ if ((current_component[4] & 0x80) &&
+ global_color_map_colors > 0) { /* global map */
+ global_color_map_.SetTablePositionAndSize(data_position,
+ global_color_map_colors);
+ GETN(kBytesPerColormapEntry * global_color_map_colors,
+ kGIFGlobalColormap);
+ break;
+ }
+
+ GETN(1, kGIFImageStart);
+ break;
+ }
+
+ case kGIFGlobalColormap: {
+ global_color_map_.SetDefined();
+ GETN(1, kGIFImageStart);
+ break;
+ }
+
+ case kGIFImageStart: {
+ const char current_component =
+ reader.GetOneByte(current_component_position);
+
+ if (current_component == '!') { // extension.
+ GETN(2, kGIFExtension);
+ break;
+ }
+
+ if (current_component == ',') { // image separator.
+ GETN(9, kGIFImageHeader);
+ break;
+ }
+
+ // If we get anything other than ',' (image separator), '!'
+ // (extension), or ';' (trailer), there is extraneous data
+ // between blocks. The GIF87a spec tells us to keep reading
+ // until we find an image separator, but GIF89a says such
+ // a file is corrupt. We follow Mozilla's implementation and
+ // proceed as if the file were correctly terminated, so the
+ // GIF will display.
+ GETN(0, kGIFDone);
+ break;
+ }
+
+ case kGIFExtension: {
+ const unsigned char* current_component =
+ reinterpret_cast<const unsigned char*>(reader.GetConsecutiveData(
+ current_component_position, 2, read_buffer));
+
+ size_t bytes_in_block = current_component[1];
+ GIFState exception_state = kGIFSkipBlock;
+
+ switch (*current_component) {
+ case 0xf9:
+ exception_state = kGIFControlExtension;
+ // The GIF spec mandates that the GIFControlExtension header block
+ // length is 4 bytes, and the parser for this block reads 4 bytes,
+ // so we must enforce that the buffer contains at least this many
+ // bytes. If the GIF specifies a different length, we allow that, so
+ // long as it's larger; the additional data will simply be ignored.
+ bytes_in_block = std::max(bytes_in_block, static_cast<size_t>(4));
+ break;
+
+ // The GIF spec also specifies the lengths of the following two
+ // extensions' headers (as 12 and 11 bytes, respectively). Because we
+ // ignore the plain text extension entirely and sanity-check the
+ // actual length of the application extension header before reading
+ // it, we allow GIFs to deviate from these values in either direction.
+ // This is important for real-world compatibility, as GIFs in the wild
+ // exist with application extension headers that are both shorter and
+ // longer than 11 bytes.
+ case 0x01:
+ // ignoring plain text extension
+ break;
+
+ case 0xff:
+ exception_state = kGIFApplicationExtension;
+ break;
+
+ case 0xfe:
+ exception_state = kGIFConsumeComment;
+ break;
+ }
+
+ if (bytes_in_block)
+ GETN(bytes_in_block, exception_state);
+ else
+ GETN(1, kGIFImageStart);
+ break;
+ }
+
+ case kGIFConsumeBlock: {
+ const unsigned char current_component = static_cast<unsigned char>(
+ reader.GetOneByte(current_component_position));
+ if (!current_component)
+ GETN(1, kGIFImageStart);
+ else
+ GETN(current_component, kGIFSkipBlock);
+ break;
+ }
+
+ case kGIFSkipBlock: {
+ GETN(1, kGIFConsumeBlock);
+ break;
+ }
+
+ case kGIFControlExtension: {
+ const unsigned char* current_component =
+ reinterpret_cast<const unsigned char*>(reader.GetConsecutiveData(
+ current_component_position, 4, read_buffer));
+
+ AddFrameIfNecessary();
+ GIFFrameContext* current_frame = frames_.back().get();
+ if (*current_component & 0x1)
+ current_frame->SetTransparentPixel(current_component[3]);
+
+ // We ignore the "user input" bit.
+
+ // NOTE: This relies on the values in the FrameDisposalMethod enum
+ // matching those in the GIF spec!
+ int disposal_method = ((*current_component) >> 2) & 0x7;
+ if (disposal_method < 4) {
+ current_frame->SetDisposalMethod(
+ static_cast<ImageFrame::DisposalMethod>(disposal_method));
+ } else if (disposal_method == 4) {
+ // Some specs say that disposal method 3 is "overwrite previous",
+ // others that setting the third bit of the field (i.e. method 4) is.
+ // We map both to the same value.
+ current_frame->SetDisposalMethod(
+ ImageFrame::kDisposeOverwritePrevious);
+ }
+ current_frame->SetDelayTime(GETINT16(current_component + 1) * 10);
+ GETN(1, kGIFConsumeBlock);
+ break;
+ }
+
+ case kGIFCommentExtension: {
+ const unsigned char current_component = static_cast<unsigned char>(
+ reader.GetOneByte(current_component_position));
+ if (current_component)
+ GETN(current_component, kGIFConsumeComment);
+ else
+ GETN(1, kGIFImageStart);
+ break;
+ }
+
+ case kGIFConsumeComment: {
+ GETN(1, kGIFCommentExtension);
+ break;
+ }
+
+ case kGIFApplicationExtension: {
+ // Check for netscape application extension.
+ if (bytes_to_consume_ == 11) {
+ const unsigned char* current_component =
+ reinterpret_cast<const unsigned char*>(reader.GetConsecutiveData(
+ current_component_position, 11, read_buffer));
+
+ if (!memcmp(current_component, "NETSCAPE2.0", 11) ||
+ !memcmp(current_component, "ANIMEXTS1.0", 11))
+ GETN(1, kGIFNetscapeExtensionBlock);
+ }
+
+ if (state_ != kGIFNetscapeExtensionBlock)
+ GETN(1, kGIFConsumeBlock);
+ break;
+ }
+
+ // Netscape-specific GIF extension: animation looping.
+ case kGIFNetscapeExtensionBlock: {
+ const int current_component = static_cast<unsigned char>(
+ reader.GetOneByte(current_component_position));
+ // GIFConsumeNetscapeExtension always reads 3 bytes from the stream; we
+ // should at least wait for this amount.
+ if (current_component)
+ GETN(std::max(3, current_component), kGIFConsumeNetscapeExtension);
+ else
+ GETN(1, kGIFImageStart);
+ break;
+ }
+
+ // Parse netscape-specific application extensions
+ case kGIFConsumeNetscapeExtension: {
+ const unsigned char* current_component =
+ reinterpret_cast<const unsigned char*>(reader.GetConsecutiveData(
+ current_component_position, 3, read_buffer));
+
+ int netscape_extension = current_component[0] & 7;
+
+ // Loop entire animation specified # of times. Only read the loop count
+ // during the first iteration.
+ if (netscape_extension == 1) {
+ loop_count_ = GETINT16(current_component + 1);
+
+ // Zero loop count is infinite animation loop request.
+ if (!loop_count_)
+ loop_count_ = kAnimationLoopInfinite;
+
+ GETN(1, kGIFNetscapeExtensionBlock);
+ } else if (netscape_extension == 2) {
+ // Wait for specified # of bytes to enter buffer.
+
+ // Don't do this, this extension doesn't exist (isn't used at all)
+ // and doesn't do anything, as our streaming/buffering takes care of
+ // it all. See http://semmix.pl/color/exgraf/eeg24.htm .
+ GETN(1, kGIFNetscapeExtensionBlock);
+ } else {
+ // 0,3-7 are yet to be defined netscape extension codes
+ return false;
+ }
+ break;
+ }
+
+ case kGIFImageHeader: {
+ unsigned height, width, x_offset, y_offset;
+ const unsigned char* current_component =
+ reinterpret_cast<const unsigned char*>(reader.GetConsecutiveData(
+ current_component_position, 9, read_buffer));
+
+ /* Get image offsets, with respect to the screen origin */
+ x_offset = GETINT16(current_component);
+ y_offset = GETINT16(current_component + 2);
+
+ /* Get image width and height. */
+ width = GETINT16(current_component + 4);
+ height = GETINT16(current_component + 6);
+
+ // Some GIF files have frames that don't fit in the specified
+ // overall image size. For the first frame, we can simply enlarge
+ // the image size to allow the frame to be visible. We can't do
+ // this on subsequent frames because the rest of the decoding
+ // infrastructure assumes the image size won't change as we
+ // continue decoding, so any subsequent frames that are even
+ // larger will be cropped.
+ // Luckily, handling just the first frame is sufficient to deal
+ // with most cases, e.g. ones where the image size is erroneously
+ // set to zero, since usually the first frame completely fills
+ // the image.
+ if (CurrentFrameIsFirstFrame()) {
+ screen_height_ = std::max(screen_height_, y_offset + height);
+ screen_width_ = std::max(screen_width_, x_offset + width);
+ }
+
+ // Inform the client of the final size.
+ if (!sent_size_to_client_ && client_ &&
+ !client_->SetSize(screen_width_, screen_height_))
+ return false;
+ sent_size_to_client_ = true;
+
+ if (query == GIFImageDecoder::kGIFSizeQuery) {
+ // The decoder needs to stop. Hand back the number of bytes we
+ // consumed from the buffer minus 9 (the amount we consumed to read
+ // the header).
+ SetRemainingBytes(len + 9);
+ GETN(9, kGIFImageHeader);
+ return true;
+ }
+
+ AddFrameIfNecessary();
+ GIFFrameContext* current_frame = frames_.back().get();
+
+ current_frame->SetHeaderDefined();
+
+ // Work around more broken GIF files that have zero image width or
+ // height.
+ if (!height || !width) {
+ height = screen_height_;
+ width = screen_width_;
+ if (!height || !width)
+ return false;
+ }
+ current_frame->SetRect(x_offset, y_offset, width, height);
+ current_frame->SetInterlaced(current_component[8] & 0x40);
+
+ // Overlaying interlaced, transparent GIFs over
+ // existing image data using the Haeberli display hack
+ // requires saving the underlying image in order to
+ // avoid jaggies at the transparency edges. We are
+ // unprepared to deal with that, so don't display such
+ // images progressively. Which means only the first
+ // frame can be progressively displayed.
+ // FIXME: It is possible that a non-transparent frame
+ // can be interlaced and progressively displayed.
+ current_frame->SetProgressiveDisplay(CurrentFrameIsFirstFrame());
+
+ const bool is_local_colormap_defined = current_component[8] & 0x80;
+ if (is_local_colormap_defined) {
+ // The three low-order bits of currentComponent[8] specify the bits
+ // per pixel.
+ const size_t num_colors = 2 << (current_component[8] & 0x7);
+ current_frame->LocalColorMap().SetTablePositionAndSize(data_position,
+ num_colors);
+ GETN(kBytesPerColormapEntry * num_colors, kGIFImageColormap);
+ break;
+ }
+
+ GETN(1, kGIFLZWStart);
+ break;
+ }
+
+ case kGIFImageColormap: {
+ DCHECK(!frames_.IsEmpty());
+ frames_.back()->LocalColorMap().SetDefined();
+ GETN(1, kGIFLZWStart);
+ break;
+ }
+
+ case kGIFSubBlock: {
+ const size_t bytes_in_block = static_cast<unsigned char>(
+ reader.GetOneByte(current_component_position));
+ if (bytes_in_block) {
+ GETN(bytes_in_block, GIFLZW);
+ } else {
+ // Finished parsing one frame; Process next frame.
+ DCHECK(!frames_.IsEmpty());
+ // Note that some broken GIF files do not have enough LZW blocks to
+ // fully decode all rows; we treat this case as "frame complete".
+ frames_.back()->SetComplete();
+ GETN(1, kGIFImageStart);
+ }
+ break;
+ }
+
+ case kGIFDone: {
+ parse_completed_ = true;
+ return true;
+ }
+
+ default:
+ // We shouldn't ever get here.
+ return false;
+ break;
+ }
+ }
+
+ SetRemainingBytes(len);
+ return true;
+}
+
+void GIFImageReader::SetRemainingBytes(size_t remaining_bytes) {
+ DCHECK_LE(remaining_bytes, data_->size());
+ bytes_read_ = data_->size() - remaining_bytes;
+}
+
+void GIFImageReader::AddFrameIfNecessary() {
+ if (frames_.IsEmpty() || frames_.back()->IsComplete())
+ frames_.push_back(base::WrapUnique(new GIFFrameContext(frames_.size())));
+}
+
+// FIXME: Move this method to close to doLZW().
+bool GIFLZWContext::PrepareToDecode() {
+ DCHECK(frame_context_->IsDataSizeDefined());
+ DCHECK(frame_context_->IsHeaderDefined());
+
+ // Since we use a codesize of 1 more than the datasize, we need to ensure
+ // that our datasize is strictly less than the kMaxDictionaryEntryBits.
+ if (frame_context_->DataSize() >= kMaxDictionaryEntryBits)
+ return false;
+ clear_code = 1 << frame_context_->DataSize();
+ avail = clear_code + 2;
+ oldcode = -1;
+ codesize = frame_context_->DataSize() + 1;
+ codemask = (1 << codesize) - 1;
+ datum = bits = 0;
+ ipass = frame_context_->Interlaced() ? 1 : 0;
+ irow = 0;
+
+ // We want to know the longest sequence encodable by a dictionary with
+ // kMaxDictionaryEntries entries. If we ignore the need to encode the base
+ // values themselves at the beginning of the dictionary, as well as the need
+ // for a clear code or a termination code, we could use every entry to
+ // encode a series of multiple values. If the input value stream looked
+ // like "AAAAA..." (a long string of just one value), the first dictionary
+ // entry would encode AA, the next AAA, the next AAAA, and so forth. Thus
+ // the longest sequence would be kMaxDictionaryEntries + 1 values.
+ //
+ // However, we have to account for reserved entries. The first |datasize|
+ // bits are reserved for the base values, and the next two entries are
+ // reserved for the clear code and termination code. In theory a GIF can
+ // set the datasize to 0, meaning we have just two reserved entries, making
+ // the longest sequence (kMaxDictionaryEntries + 1) - 2 values long. Since
+ // each value is a byte, this is also the number of bytes in the longest
+ // encodable sequence.
+ const size_t kMaxBytes = kMaxDictionaryEntries - 1;
+
+ // Now allocate the output buffer. We decode directly into this buffer
+ // until we have at least one row worth of data, then call outputRow().
+ // This means worst case we may have (row width - 1) bytes in the buffer
+ // and then decode a sequence |maxBytes| long to append.
+ row_buffer.resize(frame_context_->Width() - 1 + kMaxBytes);
+ row_iter = row_buffer.begin();
+ rows_remaining = frame_context_->Height();
+
+ // Clearing the whole suffix table lets us be more tolerant of bad data.
+ for (int i = 0; i < clear_code; ++i) {
+ suffix[i] = i;
+ suffix_length[i] = 1;
+ }
+ return true;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/gif/gif_image_reader.h b/chromium/third_party/blink/renderer/platform/image-decoders/gif/gif_image_reader.h
new file mode 100644
index 00000000000..8d56800b6f9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/gif/gif_image_reader.h
@@ -0,0 +1,371 @@
+/* -*- 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 Mozilla Communicator client code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_GIF_GIF_IMAGE_READER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_GIF_GIF_IMAGE_READER_H_
+
+// Define ourselves as the clientPtr. Mozilla just hacked their C++ callback
+// class into this old C decoder, so we will too.
+#include <memory>
+#include "third_party/blink/renderer/platform/image-decoders/gif/gif_image_decoder.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class FastSharedBufferReader;
+
+const int kCLoopCountNotSeen = -2;
+
+// List of possible parsing states.
+enum GIFState {
+ kGIFType,
+ kGIFGlobalHeader,
+ kGIFGlobalColormap,
+ kGIFImageStart,
+ kGIFImageHeader,
+ kGIFImageColormap,
+ kGIFImageBody,
+ kGIFLZWStart,
+ GIFLZW,
+ kGIFSubBlock,
+ kGIFExtension,
+ kGIFControlExtension,
+ kGIFConsumeBlock,
+ kGIFSkipBlock,
+ kGIFDone,
+ kGIFCommentExtension,
+ kGIFApplicationExtension,
+ kGIFNetscapeExtensionBlock,
+ kGIFConsumeNetscapeExtension,
+ kGIFConsumeComment
+};
+
+struct GIFFrameContext;
+
+// LZW decoder state machine.
+class GIFLZWContext final {
+ USING_FAST_MALLOC(GIFLZWContext);
+ WTF_MAKE_NONCOPYABLE(GIFLZWContext);
+
+ public:
+ GIFLZWContext(blink::GIFImageDecoder* client,
+ const GIFFrameContext* frame_context)
+ : codesize(0),
+ codemask(0),
+ clear_code(0),
+ avail(0),
+ oldcode(0),
+ firstchar(0),
+ bits(0),
+ datum(0),
+ ipass(0),
+ irow(0),
+ rows_remaining(0),
+ row_iter(nullptr),
+ client_(client),
+ frame_context_(frame_context) {}
+
+ bool PrepareToDecode();
+ bool OutputRow(GIFRow::const_iterator row_begin);
+ bool DoLZW(const unsigned char* block, size_t bytes_in_block);
+ bool HasRemainingRows() { return rows_remaining; }
+
+ private:
+ enum {
+ kMaxDictionaryEntryBits = 12,
+ // 2^kMaxDictionaryEntryBits
+ kMaxDictionaryEntries = 4096,
+ };
+
+ // LZW decoding states and output states.
+ int codesize;
+ int codemask;
+ int clear_code; // Codeword used to trigger dictionary reset.
+ int avail; // Index of next available slot in dictionary.
+ int oldcode;
+ unsigned char firstchar;
+ int bits; // Number of unread bits in "datum".
+ int datum; // 32-bit input buffer.
+ int ipass; // Interlace pass; Ranges 1-4 if interlaced.
+ size_t irow; // Current output row, starting at zero.
+ size_t rows_remaining; // Rows remaining to be output.
+
+ unsigned short prefix[kMaxDictionaryEntries];
+ unsigned char suffix[kMaxDictionaryEntries];
+ unsigned short suffix_length[kMaxDictionaryEntries];
+ GIFRow row_buffer; // Single scanline temporary buffer.
+ GIFRow::iterator row_iter;
+
+ // Initialized during construction and read-only.
+ blink::GIFImageDecoder* client_;
+ const GIFFrameContext* frame_context_;
+};
+
+// Data structure for one LZW block.
+struct GIFLZWBlock {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ GIFLZWBlock(size_t position, size_t size)
+ : block_position(position), block_size(size) {}
+
+ size_t block_position;
+ size_t block_size;
+};
+
+class GIFColorMap final {
+ DISALLOW_NEW();
+
+ public:
+ typedef Vector<blink::ImageFrame::PixelData> Table;
+
+ GIFColorMap() : is_defined_(false), position_(0), colors_(0) {}
+
+ // Set position and number of colors for the RGB table in the data stream.
+ void SetTablePositionAndSize(size_t position, size_t colors) {
+ position_ = position;
+ colors_ = colors;
+ }
+ void SetDefined() { is_defined_ = true; }
+ bool IsDefined() const { return is_defined_; }
+
+ // Build RGBA table using the data stream.
+ void BuildTable(blink::FastSharedBufferReader*);
+ const Table& GetTable() const { return table_; }
+
+ private:
+ bool is_defined_;
+ size_t position_;
+ size_t colors_;
+ Table table_;
+};
+
+// LocalFrame output state machine.
+struct GIFFrameContext {
+ USING_FAST_MALLOC(GIFFrameContext);
+ WTF_MAKE_NONCOPYABLE(GIFFrameContext);
+
+ public:
+ GIFFrameContext(int id)
+ : frame_id_(id),
+ x_offset_(0),
+ y_offset_(0),
+ width_(0),
+ height_(0),
+ transparent_pixel_(kNotFound),
+ disposal_method_(blink::ImageFrame::kDisposeNotSpecified),
+ data_size_(0),
+ progressive_display_(false),
+ interlaced_(false),
+ delay_time_(0),
+ current_lzw_block_(0),
+ is_complete_(false),
+ is_header_defined_(false),
+ is_data_size_defined_(false) {}
+
+ ~GIFFrameContext() = default;
+
+ void AddLzwBlock(size_t position, size_t size) {
+ lzw_blocks_.push_back(GIFLZWBlock(position, size));
+ }
+
+ bool Decode(blink::FastSharedBufferReader*,
+ blink::GIFImageDecoder* client,
+ bool* frame_decoded);
+
+ int FrameId() const { return frame_id_; }
+ void SetRect(unsigned x, unsigned y, unsigned width, unsigned height) {
+ x_offset_ = x;
+ y_offset_ = y;
+ width_ = width;
+ height_ = height;
+ }
+ blink::IntRect FrameRect() const {
+ return blink::IntRect(x_offset_, y_offset_, width_, height_);
+ }
+ unsigned XOffset() const { return x_offset_; }
+ unsigned YOffset() const { return y_offset_; }
+ unsigned Width() const { return width_; }
+ unsigned Height() const { return height_; }
+ size_t TransparentPixel() const { return transparent_pixel_; }
+ void SetTransparentPixel(size_t pixel) { transparent_pixel_ = pixel; }
+ blink::ImageFrame::DisposalMethod GetDisposalMethod() const {
+ return disposal_method_;
+ }
+ void SetDisposalMethod(blink::ImageFrame::DisposalMethod disposal_method) {
+ disposal_method_ = disposal_method;
+ }
+ unsigned DelayTime() const { return delay_time_; }
+ void SetDelayTime(unsigned delay) { delay_time_ = delay; }
+ bool IsComplete() const { return is_complete_; }
+ void SetComplete() { is_complete_ = true; }
+ bool IsHeaderDefined() const { return is_header_defined_; }
+ void SetHeaderDefined() { is_header_defined_ = true; }
+ bool IsDataSizeDefined() const { return is_data_size_defined_; }
+ int DataSize() const { return data_size_; }
+ void SetDataSize(int size) {
+ data_size_ = size;
+ is_data_size_defined_ = true;
+ }
+ bool ProgressiveDisplay() const { return progressive_display_; }
+ void SetProgressiveDisplay(bool progressive_display) {
+ progressive_display_ = progressive_display;
+ }
+ bool Interlaced() const { return interlaced_; }
+ void SetInterlaced(bool interlaced) { interlaced_ = interlaced; }
+
+ void ClearDecodeState() { lzw_context_.reset(); }
+ const GIFColorMap& LocalColorMap() const { return local_color_map_; }
+ GIFColorMap& LocalColorMap() { return local_color_map_; }
+
+ private:
+ int frame_id_;
+ unsigned x_offset_;
+ unsigned y_offset_; // With respect to "screen" origin.
+ unsigned width_;
+ unsigned height_;
+ size_t transparent_pixel_; // Index of transparent pixel. Value is kNotFound
+ // if there is no transparent pixel.
+ blink::ImageFrame::DisposalMethod
+ disposal_method_; // Restore to background, leave in place, etc.
+ int data_size_;
+
+ bool progressive_display_; // If true, do Haeberli interlace hack.
+ bool interlaced_; // True, if scanlines arrive interlaced order.
+
+ unsigned delay_time_; // Display time, in milliseconds, for this image in a
+ // multi-image GIF.
+
+ std::unique_ptr<GIFLZWContext> lzw_context_;
+ Vector<GIFLZWBlock> lzw_blocks_; // LZW blocks for this frame.
+ GIFColorMap local_color_map_;
+
+ size_t current_lzw_block_;
+ bool is_complete_;
+ bool is_header_defined_;
+ bool is_data_size_defined_;
+};
+
+class PLATFORM_EXPORT GIFImageReader final {
+ USING_FAST_MALLOC(GIFImageReader);
+ WTF_MAKE_NONCOPYABLE(GIFImageReader);
+
+ public:
+ GIFImageReader(blink::GIFImageDecoder* client = nullptr)
+ : client_(client),
+ state_(kGIFType),
+ // Number of bytes for GIF type, either "GIF87a" or "GIF89a".
+ bytes_to_consume_(6),
+ bytes_read_(0),
+ version_(0),
+ screen_width_(0),
+ screen_height_(0),
+ sent_size_to_client_(false),
+ loop_count_(kCLoopCountNotSeen),
+ parse_completed_(false) {}
+
+ ~GIFImageReader() = default;
+
+ void SetData(scoped_refptr<blink::SegmentReader> data) {
+ data_ = std::move(data);
+ }
+ bool Parse(blink::GIFImageDecoder::GIFParseQuery);
+ bool Decode(size_t frame_index);
+
+ size_t ImagesCount() const {
+ if (frames_.IsEmpty())
+ return 0;
+
+ // This avoids counting an empty frame when the file is truncated right
+ // after GIFControlExtension but before GIFImageHeader.
+ // FIXME: This extra complexity is not necessary and we should just report
+ // m_frames.size().
+ return frames_.back()->IsHeaderDefined() ? frames_.size()
+ : frames_.size() - 1;
+ }
+ int LoopCount() const { return loop_count_; }
+
+ const GIFColorMap& GlobalColorMap() const { return global_color_map_; }
+
+ const GIFFrameContext* FrameContext(size_t index) const {
+ return index < frames_.size() ? frames_[index].get() : nullptr;
+ }
+
+ bool ParseCompleted() const { return parse_completed_; }
+
+ void ClearDecodeState(size_t index) { frames_[index]->ClearDecodeState(); }
+
+ private:
+ bool ParseData(size_t data_position,
+ size_t len,
+ blink::GIFImageDecoder::GIFParseQuery);
+ void SetRemainingBytes(size_t);
+
+ void AddFrameIfNecessary();
+ bool CurrentFrameIsFirstFrame() const {
+ return frames_.IsEmpty() ||
+ (frames_.size() == 1u && !frames_[0]->IsComplete());
+ }
+
+ blink::GIFImageDecoder* client_;
+
+ // Parsing state machine.
+ GIFState state_; // Current decoder master state.
+ size_t bytes_to_consume_; // Number of bytes to consume for next stage of
+ // parsing.
+ size_t bytes_read_; // Number of bytes processed.
+
+ // Global (multi-image) state.
+ int version_; // Either 89 for GIF89 or 87 for GIF87.
+ unsigned screen_width_; // Logical screen width & height.
+ unsigned screen_height_;
+ bool sent_size_to_client_;
+ GIFColorMap global_color_map_;
+ int loop_count_; // Netscape specific extension block to control the number
+ // of animation loops a GIF renders.
+
+ Vector<std::unique_ptr<GIFFrameContext>> frames_;
+
+ scoped_refptr<blink::SegmentReader> data_;
+ bool parse_completed_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/ico/ico_image_decoder.cc b/chromium/third_party/blink/renderer/platform/image-decoders/ico/ico_image_decoder.cc
new file mode 100644
index 00000000000..9a52f370cf2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/ico/ico_image_decoder.cc
@@ -0,0 +1,344 @@
+/*
+ * Copyright (c) 2008, 2009, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/renderer/platform/image-decoders/ico/ico_image_decoder.h"
+
+#include <algorithm>
+#include "third_party/blink/renderer/platform/image-decoders/png/png_image_decoder.h"
+
+namespace blink {
+
+// Number of bits in .ICO/.CUR used to store the directory and its entries,
+// respectively (doesn't match sizeof values for member structs since we omit
+// some fields).
+static const size_t kSizeOfDirectory = 6;
+static const size_t kSizeOfDirEntry = 16;
+
+ICOImageDecoder::ICOImageDecoder(AlphaOption alpha_option,
+ const ColorBehavior& color_behavior,
+ size_t max_decoded_bytes)
+ : ImageDecoder(alpha_option, color_behavior, max_decoded_bytes),
+ fast_reader_(nullptr),
+ decoded_offset_(0),
+ dir_entries_count_(0),
+ color_behavior_(color_behavior) {}
+
+ICOImageDecoder::~ICOImageDecoder() = default;
+
+void ICOImageDecoder::OnSetData(SegmentReader* data) {
+ fast_reader_.SetData(data);
+
+ for (BMPReaders::iterator i(bmp_readers_.begin()); i != bmp_readers_.end();
+ ++i) {
+ if (*i)
+ (*i)->SetData(data);
+ }
+ for (size_t i = 0; i < png_decoders_.size(); ++i)
+ SetDataForPNGDecoderAtIndex(i);
+}
+
+IntSize ICOImageDecoder::Size() const {
+ return frame_size_.IsEmpty() ? ImageDecoder::Size() : frame_size_;
+}
+
+IntSize ICOImageDecoder::FrameSizeAtIndex(size_t index) const {
+ return (index && (index < dir_entries_.size())) ? dir_entries_[index].size_
+ : Size();
+}
+
+bool ICOImageDecoder::SetSize(unsigned width, unsigned height) {
+ // The size calculated inside the BMPImageReader had better match the one in
+ // the icon directory.
+ return frame_size_.IsEmpty()
+ ? ImageDecoder::SetSize(width, height)
+ : ((IntSize(width, height) == frame_size_) || SetFailed());
+}
+
+bool ICOImageDecoder::FrameIsReceivedAtIndex(size_t index) const {
+ if (index >= dir_entries_.size())
+ return false;
+
+ SECURITY_DCHECK(data_);
+ const IconDirectoryEntry& dir_entry = dir_entries_[index];
+ return (dir_entry.image_offset_ + dir_entry.byte_size_) <= data_->size();
+}
+
+bool ICOImageDecoder::SetFailed() {
+ bmp_readers_.clear();
+ png_decoders_.clear();
+ return ImageDecoder::SetFailed();
+}
+
+bool ICOImageDecoder::HotSpot(IntPoint& hot_spot) const {
+ // When unspecified, the default frame is always frame 0. This is consistent
+ // with BitmapImage, where CurrentFrame() starts at 0 and only increases when
+ // animation is requested.
+ return HotSpotAtIndex(0, hot_spot);
+}
+
+bool ICOImageDecoder::HotSpotAtIndex(size_t index, IntPoint& hot_spot) const {
+ if (index >= dir_entries_.size() || file_type_ != CURSOR)
+ return false;
+
+ hot_spot = dir_entries_[index].hot_spot_;
+ return true;
+}
+
+// static
+bool ICOImageDecoder::CompareEntries(const IconDirectoryEntry& a,
+ const IconDirectoryEntry& b) {
+ // Larger icons are better. After that, higher bit-depth icons are better.
+ const int a_entry_area = a.size_.Width() * a.size_.Height();
+ const int b_entry_area = b.size_.Width() * b.size_.Height();
+ return (a_entry_area == b_entry_area) ? (a.bit_count_ > b.bit_count_)
+ : (a_entry_area > b_entry_area);
+}
+
+size_t ICOImageDecoder::DecodeFrameCount() {
+ DecodeSize();
+
+ // If DecodeSize() fails, return the existing number of frames. This way
+ // if we get halfway through the image before decoding fails, we won't
+ // suddenly start reporting that the image has zero frames.
+ if (Failed() || !data_)
+ return frame_buffer_cache_.size();
+
+ // If the file is incomplete, return the length of the sequence of completely
+ // received frames. We don't do this when the file is fully received, since
+ // some ICOs have entries whose claimed offset + size extends past the end of
+ // the file, and we still want to display these if they don't trigger decoding
+ // failures elsewhere.
+ if (!IsAllDataReceived()) {
+ for (size_t i = 0; i < dir_entries_.size(); ++i) {
+ const IconDirectoryEntry& dir_entry = dir_entries_[i];
+ if ((dir_entry.image_offset_ + dir_entry.byte_size_) > data_->size())
+ return i;
+ }
+ }
+ return dir_entries_.size();
+}
+
+void ICOImageDecoder::SetDataForPNGDecoderAtIndex(size_t index) {
+ if (!png_decoders_[index])
+ return;
+
+ png_decoders_[index]->SetData(data_.get(), IsAllDataReceived());
+}
+
+void ICOImageDecoder::Decode(size_t index, bool only_size) {
+ if (Failed() || !data_)
+ return;
+
+ // Defensively clear the FastSharedBufferReader's cache, as another caller
+ // may have called SharedBuffer::MergeSegmentsIntoBuffer().
+ fast_reader_.ClearCache();
+
+ // If we couldn't decode the image but we've received all the data, decoding
+ // has failed.
+ if ((!DecodeDirectory() || (!only_size && !DecodeAtIndex(index))) &&
+ IsAllDataReceived()) {
+ SetFailed();
+ // If we're done decoding this frame, we don't need the BMPImageReader or
+ // PNGImageDecoder anymore. (If we failed, these have already been
+ // cleared.)
+ } else if ((frame_buffer_cache_.size() > index) &&
+ (frame_buffer_cache_[index].GetStatus() ==
+ ImageFrame::kFrameComplete)) {
+ bmp_readers_[index].reset();
+ png_decoders_[index].reset();
+ }
+}
+
+bool ICOImageDecoder::DecodeDirectory() {
+ // Read and process directory.
+ if ((decoded_offset_ < kSizeOfDirectory) && !ProcessDirectory())
+ return false;
+
+ // Read and process directory entries.
+ return (decoded_offset_ >=
+ (kSizeOfDirectory + (dir_entries_count_ * kSizeOfDirEntry))) ||
+ ProcessDirectoryEntries();
+}
+
+bool ICOImageDecoder::DecodeAtIndex(size_t index) {
+ SECURITY_DCHECK(index < dir_entries_.size());
+ const IconDirectoryEntry& dir_entry = dir_entries_[index];
+ const ImageType image_type = ImageTypeAtIndex(index);
+ if (image_type == kUnknown)
+ return false; // Not enough data to determine image type yet.
+
+ if (image_type == BMP) {
+ if (!bmp_readers_[index]) {
+ bmp_readers_[index] = std::make_unique<BMPImageReader>(
+ this, dir_entry.image_offset_, 0, true);
+ bmp_readers_[index]->SetData(data_.get());
+ }
+ // Update the pointer to the buffer as it could change after
+ // frame_buffer_cache_.resize().
+ bmp_readers_[index]->SetBuffer(&frame_buffer_cache_[index]);
+ frame_size_ = dir_entry.size_;
+ bool result = bmp_readers_[index]->DecodeBMP(false);
+ frame_size_ = IntSize();
+ return result;
+ }
+
+ if (!png_decoders_[index]) {
+ AlphaOption alpha_option =
+ premultiply_alpha_ ? kAlphaPremultiplied : kAlphaNotPremultiplied;
+ png_decoders_[index] = std::make_unique<PNGImageDecoder>(
+ alpha_option, color_behavior_, max_decoded_bytes_,
+ dir_entry.image_offset_);
+ SetDataForPNGDecoderAtIndex(index);
+ }
+ auto* png_decoder = png_decoders_[index].get();
+ if (png_decoder->IsSizeAvailable()) {
+ // Fail if the size the PNGImageDecoder calculated does not match the size
+ // in the directory.
+ if (png_decoder->Size() != dir_entry.size_)
+ return SetFailed();
+
+ const auto* frame = png_decoder->DecodeFrameBufferAtIndex(0);
+ if (frame)
+ frame_buffer_cache_[index] = *frame;
+ }
+ if (png_decoder->Failed())
+ return SetFailed();
+ return frame_buffer_cache_[index].GetStatus() == ImageFrame::kFrameComplete;
+}
+
+bool ICOImageDecoder::ProcessDirectory() {
+ // Read directory.
+ SECURITY_DCHECK(data_);
+ DCHECK(!decoded_offset_);
+ if (data_->size() < kSizeOfDirectory)
+ return false;
+ const uint16_t file_type = ReadUint16(2);
+ dir_entries_count_ = ReadUint16(4);
+ decoded_offset_ = kSizeOfDirectory;
+
+ // See if this is an icon filetype we understand, and make sure we have at
+ // least one entry in the directory.
+ if (((file_type != ICON) && (file_type != CURSOR)) || (!dir_entries_count_))
+ return SetFailed();
+
+ file_type_ = static_cast<FileType>(file_type);
+ return true;
+}
+
+bool ICOImageDecoder::ProcessDirectoryEntries() {
+ // Read directory entries.
+ SECURITY_DCHECK(data_);
+ DCHECK_EQ(decoded_offset_, kSizeOfDirectory);
+ if ((decoded_offset_ > data_->size()) ||
+ ((data_->size() - decoded_offset_) <
+ (dir_entries_count_ * kSizeOfDirEntry)))
+ return false;
+
+ // Enlarge member vectors to hold all the entries.
+ dir_entries_.resize(dir_entries_count_);
+ bmp_readers_.resize(dir_entries_count_);
+ png_decoders_.resize(dir_entries_count_);
+
+ for (IconDirectoryEntries::iterator i(dir_entries_.begin());
+ i != dir_entries_.end(); ++i)
+ *i = ReadDirectoryEntry(); // Updates decoded_offset_.
+
+ // Make sure the specified image offsets are past the end of the directory
+ // entries.
+ for (IconDirectoryEntries::iterator i(dir_entries_.begin());
+ i != dir_entries_.end(); ++i) {
+ if (i->image_offset_ < decoded_offset_)
+ return SetFailed();
+ }
+
+ // Arrange frames in decreasing quality order.
+ std::sort(dir_entries_.begin(), dir_entries_.end(), CompareEntries);
+
+ // The image size is the size of the largest entry.
+ const IconDirectoryEntry& dir_entry = dir_entries_.front();
+ // Technically, this next call shouldn't be able to fail, since the width
+ // and height here are each <= 256, and |frame_size_| is empty.
+ return SetSize(dir_entry.size_.Width(), dir_entry.size_.Height());
+}
+
+ICOImageDecoder::IconDirectoryEntry ICOImageDecoder::ReadDirectoryEntry() {
+ // Read icon data.
+ // The following calls to ReadUint8() return a uint8_t, which is appropriate
+ // because that's the on-disk type of the width and height values. Storing
+ // them in ints (instead of matching uint8_ts) is so we can record dimensions
+ // of size 256 (which is what a zero byte really means).
+ int width = ReadUint8(0);
+ if (!width)
+ width = 256;
+ int height = ReadUint8(1);
+ if (!height)
+ height = 256;
+ IconDirectoryEntry entry;
+ entry.size_ = IntSize(width, height);
+ if (file_type_ == CURSOR) {
+ entry.bit_count_ = 0;
+ entry.hot_spot_ = IntPoint(ReadUint16(4), ReadUint16(6));
+ } else {
+ entry.bit_count_ = ReadUint16(6);
+ entry.hot_spot_ = IntPoint();
+ }
+ entry.byte_size_ = ReadUint32(8);
+ entry.image_offset_ = ReadUint32(12);
+
+ // Some icons don't have a bit depth, only a color count. Convert the
+ // color count to the minimum necessary bit depth. It doesn't matter if
+ // this isn't quite what the bitmap info header says later, as we only use
+ // this value to determine which icon entry is best.
+ if (!entry.bit_count_) {
+ int color_count = ReadUint8(2);
+ if (!color_count)
+ color_count = 256; // Vague in the spec, needed by real-world icons.
+ for (--color_count; color_count; color_count >>= 1)
+ ++entry.bit_count_;
+ }
+
+ decoded_offset_ += kSizeOfDirEntry;
+ return entry;
+}
+
+ICOImageDecoder::ImageType ICOImageDecoder::ImageTypeAtIndex(size_t index) {
+ // Check if this entry is a BMP or a PNG; we need 4 bytes to check the magic
+ // number.
+ SECURITY_DCHECK(data_);
+ SECURITY_DCHECK(index < dir_entries_.size());
+ const uint32_t image_offset = dir_entries_[index].image_offset_;
+ if ((image_offset > data_->size()) || ((data_->size() - image_offset) < 4))
+ return kUnknown;
+ char buffer[4];
+ const char* data = fast_reader_.GetConsecutiveData(image_offset, 4, buffer);
+ return strncmp(data, "\x89PNG", 4) ? BMP : PNG;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/ico/ico_image_decoder.h b/chromium/third_party/blink/renderer/platform/image-decoders/ico/ico_image_decoder.h
new file mode 100644
index 00000000000..5524ec50702
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/ico/ico_image_decoder.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2008, 2009, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_ICO_ICO_IMAGE_DECODER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_ICO_ICO_IMAGE_DECODER_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_reader.h"
+#include "third_party/blink/renderer/platform/image-decoders/fast_shared_buffer_reader.h"
+
+namespace blink {
+
+class PNGImageDecoder;
+
+// This class decodes the ICO and CUR image formats.
+class PLATFORM_EXPORT ICOImageDecoder final : public ImageDecoder {
+ WTF_MAKE_NONCOPYABLE(ICOImageDecoder);
+
+ public:
+ ICOImageDecoder(AlphaOption, const ColorBehavior&, size_t max_decoded_bytes);
+ ~ICOImageDecoder() override;
+
+ // ImageDecoder:
+ String FilenameExtension() const override { return "ico"; }
+ void OnSetData(SegmentReader*) override;
+ IntSize Size() const override;
+ IntSize FrameSizeAtIndex(size_t) const override;
+ bool SetSize(unsigned width, unsigned height) override;
+ bool FrameIsReceivedAtIndex(size_t) const override;
+ // CAUTION: SetFailed() deletes all readers and decoders. Be careful to
+ // avoid accessing deleted memory, especially when calling this from
+ // inside BMPImageReader!
+ bool SetFailed() override;
+ bool HotSpot(IntPoint&) const override;
+
+ private:
+ enum ImageType {
+ kUnknown,
+ BMP,
+ PNG,
+ };
+
+ enum FileType {
+ ICON = 1,
+ CURSOR = 2,
+ };
+
+ struct IconDirectoryEntry {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ IntSize size_;
+ uint16_t bit_count_;
+ IntPoint hot_spot_;
+ uint32_t image_offset_;
+ uint32_t byte_size_;
+ };
+
+ // Returns true if |a| is a preferable icon entry to |b|.
+ // Larger sizes, or greater bitdepths at the same size, are preferable.
+ static bool CompareEntries(const IconDirectoryEntry& a,
+ const IconDirectoryEntry& b);
+
+ // ImageDecoder:
+ void DecodeSize() override { Decode(0, true); }
+ size_t DecodeFrameCount() override;
+ void Decode(size_t index) override { Decode(index, false); }
+
+ // TODO (scroggo): These functions are identical to functions in
+ // BMPImageReader. Share code?
+ inline uint8_t ReadUint8(size_t offset) const {
+ return fast_reader_.GetOneByte(decoded_offset_ + offset);
+ }
+
+ inline uint16_t ReadUint16(int offset) const {
+ char buffer[2];
+ const char* data =
+ fast_reader_.GetConsecutiveData(decoded_offset_ + offset, 2, buffer);
+ return BMPImageReader::ReadUint16(data);
+ }
+
+ inline uint32_t ReadUint32(int offset) const {
+ char buffer[4];
+ const char* data =
+ fast_reader_.GetConsecutiveData(decoded_offset_ + offset, 4, buffer);
+ return BMPImageReader::ReadUint32(data);
+ }
+
+ // If the desired PNGImageDecoder exists, gives it the appropriate data.
+ void SetDataForPNGDecoderAtIndex(size_t);
+
+ // Decodes the entry at |index|. If |only_size| is true, stops decoding
+ // after calculating the image size. If decoding fails but there is no
+ // more data coming, sets the "decode failure" flag.
+ void Decode(size_t index, bool only_size);
+
+ // Decodes the directory and directory entries at the beginning of the
+ // data, and initializes members. Returns true if all decoding
+ // succeeded. Once this returns true, all entries' sizes are known.
+ bool DecodeDirectory();
+
+ // Decodes the specified entry.
+ bool DecodeAtIndex(size_t);
+
+ // Processes the ICONDIR at the beginning of the data. Returns true if
+ // the directory could be decoded.
+ bool ProcessDirectory();
+
+ // Processes the ICONDIRENTRY records after the directory. Keeps the
+ // "best" entry as the one we'll decode. Returns true if the entries
+ // could be decoded.
+ bool ProcessDirectoryEntries();
+
+ // Stores the hot-spot for |index| in |hot_spot| and returns true,
+ // or returns false if there is none.
+ bool HotSpotAtIndex(size_t index, IntPoint& hot_spot) const;
+
+ // Reads and returns a directory entry from the current offset into
+ // |data|.
+ IconDirectoryEntry ReadDirectoryEntry();
+
+ // Determines whether the desired entry is a BMP or PNG. Returns true
+ // if the type could be determined.
+ ImageType ImageTypeAtIndex(size_t);
+
+ FastSharedBufferReader fast_reader_;
+
+ // An index into |data_| representing how much we've already decoded.
+ // Note that this only tracks data _this_ class decodes; once the
+ // BMPImageReader takes over this will not be updated further.
+ size_t decoded_offset_;
+
+ // Which type of file (ICO/CUR) this is.
+ FileType file_type_;
+
+ // The headers for the ICO.
+ typedef Vector<IconDirectoryEntry> IconDirectoryEntries;
+ IconDirectoryEntries dir_entries_;
+
+ // Count of directory entries is parsed from header before initializing
+ // dir_entries_. dir_entries_ is populated only when full header
+ // information including directory entries is available.
+ size_t dir_entries_count_;
+
+ // The image decoders for the various frames.
+ typedef Vector<std::unique_ptr<BMPImageReader>> BMPReaders;
+ BMPReaders bmp_readers_;
+ typedef Vector<std::unique_ptr<PNGImageDecoder>> PNGDecoders;
+ PNGDecoders png_decoders_;
+
+ // Valid only while a BMPImageReader is decoding, this holds the size
+ // for the particular entry being decoded.
+ IntSize frame_size_;
+
+ // Used to pass on to an internally created PNG decoder.
+ const ColorBehavior color_behavior_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/ico/ico_image_decoder_test.cc b/chromium/third_party/blink/renderer/platform/image-decoders/ico/ico_image_decoder_test.cc
new file mode 100644
index 00000000000..d17bcef08f3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/ico/ico_image_decoder_test.cc
@@ -0,0 +1,110 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/image-decoders/ico/ico_image_decoder.h"
+
+#include <memory>
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/image-decoders/image_decoder_test_helpers.h"
+
+namespace blink {
+
+namespace {
+
+std::unique_ptr<ImageDecoder> CreateICODecoder() {
+ return std::make_unique<ICOImageDecoder>(
+ ImageDecoder::kAlphaNotPremultiplied, ColorBehavior::TransformToSRGB(),
+ ImageDecoder::kNoDecodedImageByteLimit);
+}
+}
+
+TEST(ICOImageDecoderTests, trunctedIco) {
+ const Vector<char> data =
+ ReadFile("/images/resources/png-in-ico.ico")->Copy();
+ ASSERT_FALSE(data.IsEmpty());
+
+ scoped_refptr<SharedBuffer> truncated_data =
+ SharedBuffer::Create(data.data(), data.size() / 2);
+ auto decoder = CreateICODecoder();
+
+ decoder->SetData(truncated_data.get(), false);
+ decoder->DecodeFrameBufferAtIndex(0);
+ EXPECT_FALSE(decoder->Failed());
+
+ decoder->SetData(truncated_data.get(), true);
+ decoder->DecodeFrameBufferAtIndex(0);
+ EXPECT_TRUE(decoder->Failed());
+}
+
+TEST(ICOImageDecoderTests, errorInPngInIco) {
+ const Vector<char> data =
+ ReadFile("/images/resources/png-in-ico.ico")->Copy();
+ ASSERT_FALSE(data.IsEmpty());
+
+ // Modify the file to have a broken CRC in IHDR.
+ constexpr size_t kCrcOffset = 22 + 29;
+ constexpr size_t kCrcSize = 4;
+ scoped_refptr<SharedBuffer> modified_data =
+ SharedBuffer::Create(data.data(), kCrcOffset);
+ Vector<char> bad_crc(kCrcSize, 0);
+ modified_data->Append(bad_crc);
+ modified_data->Append(data.data() + kCrcOffset + kCrcSize,
+ data.size() - kCrcOffset - kCrcSize);
+
+ auto decoder = CreateICODecoder();
+ decoder->SetData(modified_data.get(), true);
+
+ // ICOImageDecoder reports the frame count based on whether enough data has
+ // been received according to the icon directory. So even though the
+ // embedded PNG is broken, there is enough data to include it in the frame
+ // count.
+ EXPECT_EQ(1u, decoder->FrameCount());
+
+ decoder->DecodeFrameBufferAtIndex(0);
+ EXPECT_TRUE(decoder->Failed());
+}
+
+TEST(ICOImageDecoderTests, parseAndDecodeByteByByte) {
+ TestByteByByteDecode(&CreateICODecoder, "/images/resources/png-in-ico.ico",
+ 1u, kAnimationNone);
+ TestByteByByteDecode(&CreateICODecoder, "/images/resources/2entries.ico", 2u,
+ kAnimationNone);
+ TestByteByByteDecode(&CreateICODecoder,
+ "/images/resources/greenbox-3frames.cur", 3u,
+ kAnimationNone);
+ TestByteByByteDecode(&CreateICODecoder,
+ "/images/resources/icon-without-and-bitmap.ico", 1u,
+ kAnimationNone);
+ TestByteByByteDecode(&CreateICODecoder, "/images/resources/1bit.ico", 1u,
+ kAnimationNone);
+ TestByteByByteDecode(&CreateICODecoder, "/images/resources/bug653075.ico", 2u,
+ kAnimationNone);
+}
+
+TEST(ICOImageDecoderTests, NullData) {
+ static constexpr size_t kSizeOfBadBlock = 6 + 16 + 1;
+
+ scoped_refptr<SharedBuffer> ico_file_data =
+ ReadFile("/images/resources/png-in-ico.ico");
+ ASSERT_FALSE(ico_file_data->IsEmpty());
+ ASSERT_LT(kSizeOfBadBlock, ico_file_data->size());
+
+ scoped_refptr<SharedBuffer> truncated_data =
+ SharedBuffer::Create(ico_file_data->Data(), kSizeOfBadBlock);
+ auto decoder = CreateICODecoder();
+
+ decoder->SetData(truncated_data.get(), false);
+ decoder->SetMemoryAllocator(nullptr);
+ EXPECT_FALSE(decoder->Failed());
+
+ auto* frame = decoder->DecodeFrameBufferAtIndex(0);
+ EXPECT_EQ(nullptr, frame);
+
+ decoder->SetData(scoped_refptr<SegmentReader>(nullptr), false);
+ decoder->ClearCacheExceptFrame(0);
+ decoder->SetMemoryAllocator(nullptr);
+ EXPECT_FALSE(decoder->Failed());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/image_animation.h b/chromium/third_party/blink/renderer/platform/image-decoders/image_animation.h
new file mode 100644
index 00000000000..4a222d98268
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/image_animation.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_IMAGE_ANIMATION_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_IMAGE_ANIMATION_H_
+
+#include "cc/paint/image_animation_count.h"
+
+namespace blink {
+using cc::kAnimationLoopOnce;
+using cc::kAnimationLoopInfinite;
+using cc::kAnimationNone;
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/image_decoder.cc b/chromium/third_party/blink/renderer/platform/image-decoders/image_decoder.cc
new file mode 100644
index 00000000000..c3da2e76b9f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/image_decoder.cc
@@ -0,0 +1,564 @@
+/*
+ * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h"
+
+#include <memory>
+#include "third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h"
+#include "third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_decoder.h"
+#include "third_party/blink/renderer/platform/image-decoders/fast_shared_buffer_reader.h"
+#include "third_party/blink/renderer/platform/image-decoders/gif/gif_image_decoder.h"
+#include "third_party/blink/renderer/platform/image-decoders/ico/ico_image_decoder.h"
+#include "third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.h"
+#include "third_party/blink/renderer/platform/image-decoders/png/png_image_decoder.h"
+#include "third_party/blink/renderer/platform/image-decoders/webp/webp_image_decoder.h"
+#include "third_party/blink/renderer/platform/instrumentation/platform_instrumentation.h"
+
+namespace blink {
+
+const size_t ImageDecoder::kNoDecodedImageByteLimit;
+
+inline bool MatchesJPEGSignature(const char* contents) {
+ return !memcmp(contents, "\xFF\xD8\xFF", 3);
+}
+
+inline bool MatchesPNGSignature(const char* contents) {
+ return !memcmp(contents, "\x89PNG\r\n\x1A\n", 8);
+}
+
+inline bool MatchesGIFSignature(const char* contents) {
+ return !memcmp(contents, "GIF87a", 6) || !memcmp(contents, "GIF89a", 6);
+}
+
+inline bool MatchesWebPSignature(const char* contents) {
+ return !memcmp(contents, "RIFF", 4) && !memcmp(contents + 8, "WEBPVP", 6);
+}
+
+inline bool MatchesICOSignature(const char* contents) {
+ return !memcmp(contents, "\x00\x00\x01\x00", 4);
+}
+
+inline bool MatchesCURSignature(const char* contents) {
+ return !memcmp(contents, "\x00\x00\x02\x00", 4);
+}
+
+inline bool MatchesBMPSignature(const char* contents) {
+ return !memcmp(contents, "BM", 2);
+}
+
+static constexpr size_t kLongestSignatureLength = sizeof("RIFF????WEBPVP") - 1;
+
+std::unique_ptr<ImageDecoder> ImageDecoder::Create(
+ scoped_refptr<SegmentReader> data,
+ bool data_complete,
+ AlphaOption alpha_option,
+ const ColorBehavior& color_behavior,
+ const SkISize& desired_size) {
+ // At least kLongestSignatureLength bytes are needed to sniff the signature.
+ if (data->size() < kLongestSignatureLength)
+ return nullptr;
+
+ size_t max_decoded_bytes = Platform::Current()
+ ? Platform::Current()->MaxDecodedImageBytes()
+ : kNoDecodedImageByteLimit;
+ if (!desired_size.isEmpty()) {
+ static const size_t kBytesPerPixels = 4;
+ size_t requested_decoded_bytes =
+ kBytesPerPixels * desired_size.width() * desired_size.height();
+ max_decoded_bytes = std::min(requested_decoded_bytes, max_decoded_bytes);
+ }
+
+ // Access the first kLongestSignatureLength chars to sniff the signature.
+ // (note: FastSharedBufferReader only makes a copy if the bytes are segmented)
+ char buffer[kLongestSignatureLength];
+ const FastSharedBufferReader fast_reader(data);
+ const char* contents =
+ fast_reader.GetConsecutiveData(0, kLongestSignatureLength, buffer);
+
+ std::unique_ptr<ImageDecoder> decoder;
+ if (MatchesJPEGSignature(contents)) {
+ decoder.reset(
+ new JPEGImageDecoder(alpha_option, color_behavior, max_decoded_bytes));
+ } else if (MatchesPNGSignature(contents)) {
+ decoder.reset(
+ new PNGImageDecoder(alpha_option, color_behavior, max_decoded_bytes));
+ } else if (MatchesGIFSignature(contents)) {
+ decoder.reset(
+ new GIFImageDecoder(alpha_option, color_behavior, max_decoded_bytes));
+ } else if (MatchesWebPSignature(contents)) {
+ decoder.reset(
+ new WEBPImageDecoder(alpha_option, color_behavior, max_decoded_bytes));
+ } else if (MatchesICOSignature(contents) || MatchesCURSignature(contents)) {
+ decoder.reset(
+ new ICOImageDecoder(alpha_option, color_behavior, max_decoded_bytes));
+ } else if (MatchesBMPSignature(contents)) {
+ decoder.reset(
+ new BMPImageDecoder(alpha_option, color_behavior, max_decoded_bytes));
+ }
+
+ if (decoder)
+ decoder->SetData(std::move(data), data_complete);
+
+ return decoder;
+}
+
+bool ImageDecoder::HasSufficientDataToSniffImageType(const SharedBuffer& data) {
+ return data.size() >= kLongestSignatureLength;
+}
+
+size_t ImageDecoder::FrameCount() {
+ const size_t old_size = frame_buffer_cache_.size();
+ const size_t new_size = DecodeFrameCount();
+ if (old_size != new_size) {
+ frame_buffer_cache_.resize(new_size);
+ for (size_t i = old_size; i < new_size; ++i) {
+ frame_buffer_cache_[i].SetPremultiplyAlpha(premultiply_alpha_);
+ InitializeNewFrame(i);
+ }
+ }
+ return new_size;
+}
+
+ImageFrame* ImageDecoder::DecodeFrameBufferAtIndex(size_t index) {
+ if (index >= FrameCount())
+ return nullptr;
+ ImageFrame* frame = &frame_buffer_cache_[index];
+ if (frame->GetStatus() != ImageFrame::kFrameComplete) {
+ PlatformInstrumentation::WillDecodeImage(FilenameExtension());
+ Decode(index);
+ PlatformInstrumentation::DidDecodeImage();
+ }
+
+ if (!has_histogrammed_color_space_) {
+ BitmapImageMetrics::CountImageGammaAndGamut(embedded_color_space_.get());
+ has_histogrammed_color_space_ = true;
+ }
+
+ frame->NotifyBitmapIfPixelsChanged();
+ return frame;
+}
+
+bool ImageDecoder::FrameHasAlphaAtIndex(size_t index) const {
+ return !FrameIsReceivedAtIndex(index) ||
+ frame_buffer_cache_[index].HasAlpha();
+}
+
+bool ImageDecoder::FrameIsReceivedAtIndex(size_t index) const {
+ // Animated images override this method to return the status based on the data
+ // received for the queried frame.
+ return IsAllDataReceived();
+}
+
+bool ImageDecoder::FrameIsDecodedAtIndex(size_t index) const {
+ return index < frame_buffer_cache_.size() &&
+ frame_buffer_cache_[index].GetStatus() == ImageFrame::kFrameComplete;
+}
+
+size_t ImageDecoder::FrameBytesAtIndex(size_t index) const {
+ if (index >= frame_buffer_cache_.size() ||
+ frame_buffer_cache_[index].GetStatus() == ImageFrame::kFrameEmpty)
+ return 0;
+
+ struct ImageSize {
+ explicit ImageSize(IntSize size) {
+ area = static_cast<uint64_t>(size.Width()) * size.Height();
+ }
+
+ uint64_t area;
+ };
+
+ return ImageSize(FrameSizeAtIndex(index)).area *
+ sizeof(ImageFrame::PixelData);
+}
+
+size_t ImageDecoder::ClearCacheExceptFrame(size_t clear_except_frame) {
+ // Don't clear if there are no frames or only one frame.
+ if (frame_buffer_cache_.size() <= 1)
+ return 0;
+
+ // We expect that after this call, we'll be asked to decode frames after this
+ // one. So we want to avoid clearing frames such that those requests would
+ // force re-decoding from the beginning of the image. There are two cases in
+ // which preserving |clear_except_frame| is not enough to avoid that:
+ //
+ // 1. |clear_except_frame| is not yet sufficiently decoded to decode
+ // subsequent frames. We need the previous frame to sufficiently decode
+ // this frame.
+ // 2. The disposal method of |clear_except_frame| is DisposeOverwritePrevious.
+ // In that case, we need to keep the required previous frame in the cache
+ // to prevent re-decoding that frame when |clear_except_frame| is disposed.
+ //
+ // If either 1 or 2 is true, store the required previous frame in
+ // |clear_except_frame2| so it won't be cleared.
+ size_t clear_except_frame2 = kNotFound;
+ if (clear_except_frame < frame_buffer_cache_.size()) {
+ const ImageFrame& frame = frame_buffer_cache_[clear_except_frame];
+ if (!FrameStatusSufficientForSuccessors(clear_except_frame) ||
+ frame.GetDisposalMethod() == ImageFrame::kDisposeOverwritePrevious)
+ clear_except_frame2 = frame.RequiredPreviousFrameIndex();
+ }
+
+ // Now |clear_except_frame2| indicates the frame that |clear_except_frame|
+ // depends on, as described above. But if decoding is skipping forward past
+ // intermediate frames, this frame may be insufficiently decoded. So we need
+ // to keep traversing back through the required previous frames until we find
+ // the nearest ancestor that is sufficiently decoded. Preserving that will
+ // minimize the amount of future decoding needed.
+ while (clear_except_frame2 < frame_buffer_cache_.size() &&
+ !FrameStatusSufficientForSuccessors(clear_except_frame2)) {
+ clear_except_frame2 =
+ frame_buffer_cache_[clear_except_frame2].RequiredPreviousFrameIndex();
+ }
+
+ return ClearCacheExceptTwoFrames(clear_except_frame, clear_except_frame2);
+}
+
+size_t ImageDecoder::ClearCacheExceptTwoFrames(size_t clear_except_frame1,
+ size_t clear_except_frame2) {
+ size_t frame_bytes_cleared = 0;
+ for (size_t i = 0; i < frame_buffer_cache_.size(); ++i) {
+ if (frame_buffer_cache_[i].GetStatus() != ImageFrame::kFrameEmpty &&
+ i != clear_except_frame1 && i != clear_except_frame2) {
+ frame_bytes_cleared += FrameBytesAtIndex(i);
+ ClearFrameBuffer(i);
+ }
+ }
+ return frame_bytes_cleared;
+}
+
+void ImageDecoder::ClearFrameBuffer(size_t frame_index) {
+ frame_buffer_cache_[frame_index].ClearPixelData();
+}
+
+Vector<size_t> ImageDecoder::FindFramesToDecode(size_t index) const {
+ DCHECK(index < frame_buffer_cache_.size());
+
+ Vector<size_t> frames_to_decode;
+ do {
+ frames_to_decode.push_back(index);
+ index = frame_buffer_cache_[index].RequiredPreviousFrameIndex();
+ } while (index != kNotFound && frame_buffer_cache_[index].GetStatus() !=
+ ImageFrame::kFrameComplete);
+ return frames_to_decode;
+}
+
+bool ImageDecoder::PostDecodeProcessing(size_t index) {
+ DCHECK(index < frame_buffer_cache_.size());
+
+ if (frame_buffer_cache_[index].GetStatus() != ImageFrame::kFrameComplete)
+ return false;
+
+ if (purge_aggressively_)
+ ClearCacheExceptFrame(index);
+
+ return true;
+}
+
+void ImageDecoder::CorrectAlphaWhenFrameBufferSawNoAlpha(size_t index) {
+ DCHECK(index < frame_buffer_cache_.size());
+ ImageFrame& buffer = frame_buffer_cache_[index];
+
+ // When this frame spans the entire image rect we can SetHasAlpha to false,
+ // since there are logically no transparent pixels outside of the frame rect.
+ if (buffer.OriginalFrameRect().Contains(IntRect(IntPoint(), Size()))) {
+ buffer.SetHasAlpha(false);
+ buffer.SetRequiredPreviousFrameIndex(kNotFound);
+ } else if (buffer.RequiredPreviousFrameIndex() != kNotFound) {
+ // When the frame rect does not span the entire image rect, and it does
+ // *not* have a required previous frame, the pixels outside of the frame
+ // rect will be fully transparent, so we shoudn't SetHasAlpha to false.
+ //
+ // It is a tricky case when the frame does have a required previous frame.
+ // The frame does not have alpha only if everywhere outside its rect
+ // doesn't have alpha. To know whether this is true, we check the start
+ // state of the frame -- if it doesn't have alpha, we're safe.
+ //
+ // We first check that the required previous frame does not have
+ // DisposeOverWritePrevious as its disposal method - this should never
+ // happen, since the required frame should in that case be the required
+ // frame of this frame's required frame.
+ //
+ // If |prev_buffer| is DisposeNotSpecified or DisposeKeep, |buffer| has no
+ // alpha if |prev_buffer| had no alpha. Since InitFrameBuffer() already
+ // copied the alpha state, there's nothing to do here.
+ //
+ // The only remaining case is a DisposeOverwriteBgcolor frame. If
+ // it had no alpha, and its rect is contained in the current frame's
+ // rect, we know the current frame has no alpha.
+ //
+ // For DisposeNotSpecified, DisposeKeep and DisposeOverwriteBgcolor there
+ // is one situation that is not taken into account - when |prev_buffer|
+ // *does* have alpha, but only in the frame rect of |buffer|, we can still
+ // say that this frame has no alpha. However, to determine this, we
+ // potentially need to analyze all image pixels of |prev_buffer|, which is
+ // too computationally expensive.
+ const ImageFrame* prev_buffer =
+ &frame_buffer_cache_[buffer.RequiredPreviousFrameIndex()];
+ DCHECK(prev_buffer->GetDisposalMethod() !=
+ ImageFrame::kDisposeOverwritePrevious);
+
+ if ((prev_buffer->GetDisposalMethod() ==
+ ImageFrame::kDisposeOverwriteBgcolor) &&
+ !prev_buffer->HasAlpha() &&
+ buffer.OriginalFrameRect().Contains(prev_buffer->OriginalFrameRect()))
+ buffer.SetHasAlpha(false);
+ }
+}
+
+bool ImageDecoder::InitFrameBuffer(size_t frame_index) {
+ DCHECK(frame_index < frame_buffer_cache_.size());
+
+ ImageFrame* const buffer = &frame_buffer_cache_[frame_index];
+
+ // If the frame is already initialized, return true.
+ if (buffer->GetStatus() != ImageFrame::kFrameEmpty)
+ return true;
+
+ size_t required_previous_frame_index = buffer->RequiredPreviousFrameIndex();
+ if (required_previous_frame_index == kNotFound) {
+ // This frame doesn't rely on any previous data.
+ if (!buffer->AllocatePixelData(Size().Width(), Size().Height(),
+ ColorSpaceForSkImages())) {
+ return false;
+ }
+ buffer->ZeroFillPixelData();
+ } else {
+ ImageFrame* const prev_buffer =
+ &frame_buffer_cache_[required_previous_frame_index];
+ DCHECK(prev_buffer->GetStatus() == ImageFrame::kFrameComplete);
+
+ // We try to reuse |prev_buffer| as starting state to avoid copying.
+ // If CanReusePreviousFrameBuffer returns false, we must copy the data since
+ // |prev_buffer| is necessary to decode this or later frames. In that case,
+ // copy the data instead.
+ if ((!CanReusePreviousFrameBuffer(frame_index) ||
+ !buffer->TakeBitmapDataIfWritable(prev_buffer)) &&
+ !buffer->CopyBitmapData(*prev_buffer))
+ return false;
+
+ if (prev_buffer->GetDisposalMethod() ==
+ ImageFrame::kDisposeOverwriteBgcolor) {
+ // We want to clear the previous frame to transparent, without
+ // affecting pixels in the image outside of the frame.
+ const IntRect& prev_rect = prev_buffer->OriginalFrameRect();
+ DCHECK(!prev_rect.Contains(IntRect(IntPoint(), Size())));
+ buffer->ZeroFillFrameRect(prev_rect);
+ }
+ }
+
+ OnInitFrameBuffer(frame_index);
+
+ // Update our status to be partially complete.
+ buffer->SetStatus(ImageFrame::kFramePartial);
+
+ return true;
+}
+
+void ImageDecoder::UpdateAggressivePurging(size_t index) {
+ if (purge_aggressively_)
+ return;
+
+ // We don't want to cache so much that we cause a memory issue.
+ //
+ // If we used a LRU cache we would fill it and then on next animation loop
+ // we would need to decode all the frames again -- the LRU would give no
+ // benefit and would consume more memory.
+ // So instead, simply purge unused frames if caching all of the frames of
+ // the image would use more memory than the image decoder is allowed
+ // (|max_decoded_bytes|) or would overflow 32 bits..
+ //
+ // As we decode we will learn the total number of frames, and thus total
+ // possible image memory used.
+
+ const uint64_t frame_memory_usage =
+ DecodedSize().Area() * 4; // 4 bytes per pixel
+
+ // This condition never fails in the current code. Our existing image decoders
+ // parse for the image size and SetFailed() if that size overflows
+ DCHECK_EQ(frame_memory_usage / 4, DecodedSize().Area());
+
+ const uint64_t total_memory_usage = frame_memory_usage * index;
+ if (total_memory_usage / frame_memory_usage != index) { // overflow occurred
+ purge_aggressively_ = true;
+ return;
+ }
+
+ if (total_memory_usage > max_decoded_bytes_) {
+ purge_aggressively_ = true;
+ }
+}
+
+size_t ImageDecoder::FindRequiredPreviousFrame(size_t frame_index,
+ bool frame_rect_is_opaque) {
+ DCHECK_LT(frame_index, frame_buffer_cache_.size());
+ if (!frame_index) {
+ // The first frame doesn't rely on any previous data.
+ return kNotFound;
+ }
+
+ const ImageFrame* curr_buffer = &frame_buffer_cache_[frame_index];
+ if ((frame_rect_is_opaque ||
+ curr_buffer->GetAlphaBlendSource() == ImageFrame::kBlendAtopBgcolor) &&
+ curr_buffer->OriginalFrameRect().Contains(IntRect(IntPoint(), Size())))
+ return kNotFound;
+
+ // The starting state for this frame depends on the previous frame's
+ // disposal method.
+ size_t prev_frame = frame_index - 1;
+ const ImageFrame* prev_buffer = &frame_buffer_cache_[prev_frame];
+
+ // Frames that use the DisposeOverwritePrevious method are effectively
+ // no-ops in terms of changing the starting state of a frame compared to
+ // the starting state of the previous frame, so skip over them.
+ while (prev_buffer->GetDisposalMethod() ==
+ ImageFrame::kDisposeOverwritePrevious) {
+ if (prev_frame == 0) {
+ return kNotFound;
+ }
+ prev_frame--;
+ prev_buffer = &frame_buffer_cache_[prev_frame];
+ }
+
+ switch (prev_buffer->GetDisposalMethod()) {
+ case ImageFrame::kDisposeNotSpecified:
+ case ImageFrame::kDisposeKeep:
+ // |prev_frame| will be used as the starting state for this frame.
+ // FIXME: Be even smarter by checking the frame sizes and/or
+ // alpha-containing regions.
+ return prev_frame;
+ case ImageFrame::kDisposeOverwriteBgcolor:
+ // If the previous frame fills the whole image, then the current frame
+ // can be decoded alone. Likewise, if the previous frame could be
+ // decoded without reference to any prior frame, the starting state for
+ // this frame is a blank frame, so it can again be decoded alone.
+ // Otherwise, the previous frame contributes to this frame.
+ return (prev_buffer->OriginalFrameRect().Contains(
+ IntRect(IntPoint(), Size())) ||
+ (prev_buffer->RequiredPreviousFrameIndex() == kNotFound))
+ ? kNotFound
+ : prev_frame;
+ case ImageFrame::kDisposeOverwritePrevious:
+ default:
+ NOTREACHED();
+ return kNotFound;
+ }
+}
+
+ImagePlanes::ImagePlanes() {
+ for (int i = 0; i < 3; ++i) {
+ planes_[i] = nullptr;
+ row_bytes_[i] = 0;
+ }
+}
+
+ImagePlanes::ImagePlanes(void* planes[3], const size_t row_bytes[3]) {
+ for (int i = 0; i < 3; ++i) {
+ planes_[i] = planes[i];
+ row_bytes_[i] = row_bytes[i];
+ }
+}
+
+void* ImagePlanes::Plane(int i) {
+ DCHECK_GE(i, 0);
+ DCHECK_LT(i, 3);
+ return planes_[i];
+}
+
+size_t ImagePlanes::RowBytes(int i) const {
+ DCHECK_GE(i, 0);
+ DCHECK_LT(i, 3);
+ return row_bytes_[i];
+}
+
+void ImageDecoder::SetEmbeddedColorSpace(sk_sp<SkColorSpace> color_space) {
+ DCHECK(!IgnoresColorSpace());
+ DCHECK(!has_histogrammed_color_space_);
+
+ embedded_color_space_ = color_space;
+ source_to_target_color_transform_needs_update_ = true;
+}
+
+SkColorSpaceXform* ImageDecoder::ColorTransform() {
+ if (!source_to_target_color_transform_needs_update_)
+ return source_to_target_color_transform_.get();
+ source_to_target_color_transform_needs_update_ = false;
+ source_to_target_color_transform_ = nullptr;
+
+ if (color_behavior_.IsIgnore()) {
+ return nullptr;
+ }
+
+ sk_sp<SkColorSpace> src_color_space = nullptr;
+ sk_sp<SkColorSpace> dst_color_space = nullptr;
+ if (color_behavior_.IsTransformToSRGB()) {
+ if (!embedded_color_space_) {
+ return nullptr;
+ }
+ src_color_space = embedded_color_space_;
+ dst_color_space = SkColorSpace::MakeSRGB();
+ } else {
+ DCHECK(color_behavior_.IsTag());
+ src_color_space = embedded_color_space_;
+ if (!src_color_space) {
+ src_color_space = SkColorSpace::MakeSRGB();
+ }
+
+ // This will most likely be equal to the |src_color_space|.
+ // In that case, we skip the xform when we check for equality below.
+ dst_color_space = ColorSpaceForSkImages();
+ }
+
+ if (SkColorSpace::Equals(src_color_space.get(), dst_color_space.get())) {
+ return nullptr;
+ }
+
+ source_to_target_color_transform_ =
+ SkColorSpaceXform::New(src_color_space.get(), dst_color_space.get());
+ return source_to_target_color_transform_.get();
+}
+
+sk_sp<SkColorSpace> ImageDecoder::ColorSpaceForSkImages() const {
+ if (!color_behavior_.IsTag())
+ return nullptr;
+
+ if (embedded_color_space_) {
+ SkColorSpaceTransferFn fn;
+ if (embedded_color_space_->isNumericalTransferFn(&fn)) {
+ // The embedded color space is supported by Skia.
+ return embedded_color_space_;
+ }
+
+ // In the rare case that the embedded color space is unsupported, xform at
+ // decode time.
+ SkMatrix44 to_xyz_d50(SkMatrix44::kUninitialized_Constructor);
+ if (embedded_color_space_->toXYZD50(&to_xyz_d50)) {
+ // Preserve the gamut, but convert to a standard transfer function.
+ return SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
+ to_xyz_d50);
+ }
+
+ // For color spaces without an identifiable gamut, just fall through to
+ // sRGB.
+ }
+
+ return SkColorSpace::MakeSRGB();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/image_decoder.h b/chromium/third_party/blink/renderer/platform/image-decoders/image_decoder.h
new file mode 100644
index 00000000000..bba85871cdf
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/image_decoder.h
@@ -0,0 +1,437 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_IMAGE_DECODER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_IMAGE_DECODER_H_
+
+#include <memory>
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/graphics/color_behavior.h"
+#include "third_party/blink/renderer/platform/graphics/image_orientation.h"
+#include "third_party/blink/renderer/platform/image-decoders/image_animation.h"
+#include "third_party/blink/renderer/platform/image-decoders/image_frame.h"
+#include "third_party/blink/renderer/platform/image-decoders/segment_reader.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "third_party/skia/include/core/SkColorSpaceXform.h"
+
+namespace blink {
+
+#if SK_B32_SHIFT
+inline SkColorSpaceXform::ColorFormat XformColorFormat() {
+ return SkColorSpaceXform::kRGBA_8888_ColorFormat;
+}
+#else
+inline SkColorSpaceXform::ColorFormat XformColorFormat() {
+ return SkColorSpaceXform::kBGRA_8888_ColorFormat;
+}
+#endif
+
+// ImagePlanes can be used to decode color components into provided buffers
+// instead of using an ImageFrame.
+class PLATFORM_EXPORT ImagePlanes final {
+ USING_FAST_MALLOC(ImagePlanes);
+ WTF_MAKE_NONCOPYABLE(ImagePlanes);
+
+ public:
+ ImagePlanes();
+ ImagePlanes(void* planes[3], const size_t row_bytes[3]);
+
+ void* Plane(int);
+ size_t RowBytes(int) const;
+
+ private:
+ void* planes_[3];
+ size_t row_bytes_[3];
+};
+
+// ImageDecoder is a base for all format-specific decoders
+// (e.g. JPEGImageDecoder). This base manages the ImageFrame cache.
+//
+class PLATFORM_EXPORT ImageDecoder {
+ WTF_MAKE_NONCOPYABLE(ImageDecoder);
+ USING_FAST_MALLOC(ImageDecoder);
+
+ public:
+ static const size_t kNoDecodedImageByteLimit =
+ Platform::kNoDecodedImageByteLimit;
+
+ enum AlphaOption { kAlphaPremultiplied, kAlphaNotPremultiplied };
+
+ virtual ~ImageDecoder() = default;
+
+ // Returns a caller-owned decoder of the appropriate type. Returns nullptr if
+ // we can't sniff a supported type from the provided data (possibly
+ // because there isn't enough data yet).
+ // Sets |max_decoded_bytes_| to Platform::MaxImageDecodedBytes().
+ static std::unique_ptr<ImageDecoder> Create(
+ scoped_refptr<SegmentReader> data,
+ bool data_complete,
+ AlphaOption,
+ const ColorBehavior&,
+ const SkISize& desired_size = SkISize::MakeEmpty());
+ static std::unique_ptr<ImageDecoder> Create(
+ scoped_refptr<SharedBuffer> data,
+ bool data_complete,
+ AlphaOption alpha_option,
+ const ColorBehavior& color_behavior,
+ const SkISize& desired_size = SkISize::MakeEmpty()) {
+ return Create(SegmentReader::CreateFromSharedBuffer(std::move(data)),
+ data_complete, alpha_option, color_behavior, desired_size);
+ }
+
+ virtual String FilenameExtension() const = 0;
+
+ bool IsAllDataReceived() const { return is_all_data_received_; }
+
+ // Returns true if the buffer holds enough data to instantiate a decoder.
+ // This is useful for callers to determine whether a decoder instantiation
+ // failure is due to insufficient or bad data.
+ static bool HasSufficientDataToSniffImageType(const SharedBuffer&);
+
+ void SetData(scoped_refptr<SegmentReader> data, bool all_data_received) {
+ if (failed_)
+ return;
+ data_ = std::move(data);
+ is_all_data_received_ = all_data_received;
+ OnSetData(data_.get());
+ }
+
+ void SetData(scoped_refptr<SharedBuffer> data, bool all_data_received) {
+ SetData(SegmentReader::CreateFromSharedBuffer(std::move(data)),
+ all_data_received);
+ }
+
+ virtual void OnSetData(SegmentReader* data) {}
+
+ bool IsSizeAvailable() {
+ if (failed_)
+ return false;
+ if (!size_available_)
+ DecodeSize();
+ return IsDecodedSizeAvailable();
+ }
+
+ bool IsDecodedSizeAvailable() const { return !failed_ && size_available_; }
+
+ virtual IntSize Size() const { return size_; }
+ virtual std::vector<SkISize> GetSupportedDecodeSizes() const { return {}; };
+
+ // Decoders which downsample images should override this method to
+ // return the actual decoded size.
+ virtual IntSize DecodedSize() const { return Size(); }
+
+ // Image decoders that support YUV decoding must override this to
+ // provide the size of each component.
+ virtual IntSize DecodedYUVSize(int component) const {
+ NOTREACHED();
+ return IntSize();
+ }
+
+ // Image decoders that support YUV decoding must override this to
+ // return the width of each row of the memory allocation.
+ virtual size_t DecodedYUVWidthBytes(int component) const {
+ NOTREACHED();
+ return 0;
+ }
+
+ // This will only differ from size() for ICO (where each frame is a
+ // different icon) or other formats where different frames are different
+ // sizes. This does NOT differ from size() for GIF or WebP, since
+ // decoding GIF or WebP composites any smaller frames against previous
+ // frames to create full-size frames.
+ virtual IntSize FrameSizeAtIndex(size_t) const { return Size(); }
+
+ // Returns whether the size is legal (i.e. not going to result in
+ // overflow elsewhere). If not, marks decoding as failed.
+ virtual bool SetSize(unsigned width, unsigned height) {
+ if (SizeCalculationMayOverflow(width, height))
+ return SetFailed();
+
+ size_ = IntSize(width, height);
+ size_available_ = true;
+ return true;
+ }
+
+ // Calls DecodeFrameCount() to get the frame count (if possible), without
+ // decoding the individual frames. Resizes |frame_buffer_cache_| to the
+ // correct size and returns its size.
+ size_t FrameCount();
+
+ virtual int RepetitionCount() const { return kAnimationNone; }
+
+ // Decodes as much of the requested frame as possible, and returns an
+ // ImageDecoder-owned pointer.
+ ImageFrame* DecodeFrameBufferAtIndex(size_t);
+
+ // Whether the requested frame has alpha.
+ virtual bool FrameHasAlphaAtIndex(size_t) const;
+
+ // Whether or not the frame is fully received.
+ virtual bool FrameIsReceivedAtIndex(size_t) const;
+
+ // Returns true if a cached complete decode is available.
+ bool FrameIsDecodedAtIndex(size_t) const;
+
+ // Duration for displaying a frame. This method is only used by animated
+ // images.
+ virtual TimeDelta FrameDurationAtIndex(size_t) const { return TimeDelta(); }
+
+ // Number of bytes in the decoded frame. Returns 0 if the decoder doesn't
+ // have this frame cached (either because it hasn't been decoded, or because
+ // it has been cleared).
+ virtual size_t FrameBytesAtIndex(size_t) const;
+
+ ImageOrientation Orientation() const { return orientation_; }
+
+ bool IgnoresColorSpace() const { return color_behavior_.IsIgnore(); }
+ const ColorBehavior& GetColorBehavior() const { return color_behavior_; }
+
+ // This returns the color space that will be included in the SkImageInfo of
+ // SkImages created from this decoder. This will be nullptr unless the
+ // decoder was created with the option ColorSpaceTagged.
+ sk_sp<SkColorSpace> ColorSpaceForSkImages() const;
+
+ // This returns whether or not the image included a not-ignored embedded
+ // color space. This is independent of whether or not that space's transform
+ // has been baked into the pixel values.
+ bool HasEmbeddedColorSpace() const { return embedded_color_space_.get(); }
+
+ void SetEmbeddedColorSpace(sk_sp<SkColorSpace> src_space);
+
+ // Transformation from embedded color space to target color space.
+ SkColorSpaceXform* ColorTransform();
+
+ AlphaOption GetAlphaOption() const {
+ return premultiply_alpha_ ? kAlphaPremultiplied : kAlphaNotPremultiplied;
+ }
+
+ // Sets the "decode failure" flag. For caller convenience (since so
+ // many callers want to return false after calling this), returns false
+ // to enable easy tailcalling. Subclasses may override this to also
+ // clean up any local data.
+ virtual bool SetFailed() {
+ failed_ = true;
+ return false;
+ }
+
+ bool Failed() const { return failed_; }
+
+ // Clears decoded pixel data from all frames except the provided frame. If
+ // subsequent frames depend on this frame's required previous frame, then that
+ // frame is also kept in cache to prevent re-decoding from the beginning.
+ // Callers may pass WTF::kNotFound to clear all frames.
+ // Note: If |frame_buffer_cache_| contains only one frame, it won't be
+ // cleared. Returns the number of bytes of frame data actually cleared.
+ //
+ // This is a virtual method because MockImageDecoder needs to override it in
+ // order to run the test ImageFrameGeneratorTest::ClearMultiFrameDecode.
+ //
+ // @TODO Let MockImageDecoder override ImageFrame::ClearFrameBuffer instead,
+ // so this method can be made non-virtual. It is used in the test
+ // ImageFrameGeneratorTest::ClearMultiFrameDecode. The test needs to
+ // be modified since two frames may be kept in cache, instead of
+ // always just one, with this ClearCacheExceptFrame implementation.
+ virtual size_t ClearCacheExceptFrame(size_t);
+
+ // If the image has a cursor hot-spot, stores it in the argument
+ // and returns true. Otherwise returns false.
+ virtual bool HotSpot(IntPoint&) const { return false; }
+
+ virtual void SetMemoryAllocator(SkBitmap::Allocator* allocator) {
+ // FIXME: this doesn't work for images with multiple frames.
+ if (frame_buffer_cache_.IsEmpty()) {
+ // Ensure that InitializeNewFrame is called, after parsing if
+ // necessary.
+ if (!FrameCount())
+ return;
+ }
+
+ frame_buffer_cache_[0].SetMemoryAllocator(allocator);
+ }
+
+ virtual bool CanDecodeToYUV() { return false; }
+ virtual bool DecodeToYUV() { return false; }
+ virtual void SetImagePlanes(std::unique_ptr<ImagePlanes>) {}
+
+ protected:
+ ImageDecoder(AlphaOption alpha_option,
+ const ColorBehavior& color_behavior,
+ size_t max_decoded_bytes)
+ : premultiply_alpha_(alpha_option == kAlphaPremultiplied),
+ color_behavior_(color_behavior),
+ max_decoded_bytes_(max_decoded_bytes),
+ purge_aggressively_(false) {}
+
+ // Calculates the most recent frame whose image data may be needed in
+ // order to decode frame |frame_index|, based on frame disposal methods
+ // and |frame_rect_is_opaque|, where |frame_rect_is_opaque| signifies whether
+ // the rectangle of frame at |frame_index| is known to be opaque.
+ // If no previous frame's data is required, returns WTF::kNotFound.
+ //
+ // This function requires that the previous frame's
+ // |required_previous_frame_index_| member has been set correctly. The
+ // easiest way to ensure this is for subclasses to call this method and
+ // store the result on the frame via SetRequiredPreviousFrameIndex()
+ // as soon as the frame has been created and parsed sufficiently to
+ // determine the disposal method; assuming this happens for all frames
+ // in order, the required invariant will hold.
+ //
+ // Image formats which do not use more than one frame do not need to
+ // worry about this; see comments on
+ // ImageFrame::required_previous_frame+index_.
+ size_t FindRequiredPreviousFrame(size_t frame_index,
+ bool frame_rect_is_opaque);
+
+ // This is called by ClearCacheExceptFrame() if that method decides it wants
+ // to preserve another frame, to avoid unnecessary redecoding.
+ size_t ClearCacheExceptTwoFrames(size_t, size_t);
+ virtual void ClearFrameBuffer(size_t frame_index);
+
+ // Decodes the image sufficiently to determine the image size.
+ virtual void DecodeSize() = 0;
+
+ // Decodes the image sufficiently to determine the number of frames and
+ // returns that number.
+ virtual size_t DecodeFrameCount() { return 1; }
+
+ // Called to initialize the frame buffer with the given index, based on the
+ // provided and previous frame's characteristics. Returns true on success.
+ // Before calling this method, the caller must verify that the frame exists.
+ // On failure, the client should call SetFailed. This method does not call
+ // SetFailed itself because that might delete the object directly making this
+ // call.
+ bool InitFrameBuffer(size_t);
+
+ // Performs any additional setup of the requested frame after it has been
+ // initially created, e.g. setting a duration or disposal method.
+ virtual void InitializeNewFrame(size_t) {}
+
+ // Decodes the requested frame.
+ virtual void Decode(size_t) = 0;
+
+ // This method is only required for animated images. It returns a vector with
+ // all frame indices that need to be decoded in order to succesfully decode
+ // the provided frame. The indices are returned in reverse order, so the
+ // last frame needs to be decoded first. Before calling this method, the
+ // caller must verify that the frame exists.
+ Vector<size_t> FindFramesToDecode(size_t) const;
+
+ // This is called by Decode() after decoding a frame in an animated image.
+ // Before calling this method, the caller must verify that the frame exists.
+ // @return true if the frame was fully decoded,
+ // false otherwise.
+ bool PostDecodeProcessing(size_t);
+
+ // The GIF and PNG decoders set the default alpha setting of the ImageFrame to
+ // true. When the frame rect does not contain any (semi-) transparent pixels,
+ // this may need to be changed to false. This depends on whether the required
+ // previous frame adds transparency to the image, outside of the frame rect.
+ // This methods corrects the alpha setting of the frame buffer to false when
+ // the whole frame is opaque.
+ //
+ // This method should be called by the GIF and PNG decoder when the pixels in
+ // the frame rect do *not* contain any transparent pixels. Before calling
+ // this method, the caller must verify that the frame exists.
+ void CorrectAlphaWhenFrameBufferSawNoAlpha(size_t);
+
+ scoped_refptr<SegmentReader> data_; // The encoded data.
+ Vector<ImageFrame, 1> frame_buffer_cache_;
+ const bool premultiply_alpha_;
+ const ColorBehavior color_behavior_;
+ ImageOrientation orientation_;
+
+ // The maximum amount of memory a decoded image should require. Ideally,
+ // image decoders should downsample large images to fit under this limit
+ // (and then return the downsampled size from DecodedSize()). Ignoring
+ // this limit can cause excessive memory use or even crashes on low-
+ // memory devices.
+ const size_t max_decoded_bytes_;
+
+ // While decoding, we may learn that there are so many animation frames that
+ // we would go beyond our cache budget.
+ // If that happens, purge_aggressively_ is set to true. This signals
+ // future decodes to purge old frames as it goes.
+ void UpdateAggressivePurging(size_t index);
+
+ // The method is only relevant for multi-frame images.
+ //
+ // This method indicates whether the provided frame has enough data to decode
+ // successive frames that depend on it. It is used by ClearCacheExceptFrame
+ // to determine which frame to keep in cache when the indicated frame is not
+ // yet sufficiently decoded.
+ //
+ // The default condition is that the frame status needs to be FramePartial or
+ // FrameComplete, since the data of previous frames is copied in
+ // InitFrameBuffer() before setting the status to FramePartial. For WebP,
+ // however, the status needs to be FrameComplete since the complete buffer is
+ // used to do alpha blending in WEBPImageDecoder::ApplyPostProcessing().
+ //
+ // Before calling this, verify that frame |index| exists by checking that
+ // |index| is smaller than |frame_buffer_cache_|.size().
+ virtual bool FrameStatusSufficientForSuccessors(size_t index) {
+ DCHECK(index < frame_buffer_cache_.size());
+ return frame_buffer_cache_[index].GetStatus() != ImageFrame::kFrameEmpty;
+ }
+
+ private:
+ // Some code paths compute the size of the image as "width * height * 4"
+ // and return it as a (signed) int. Avoid overflow.
+ static bool SizeCalculationMayOverflow(unsigned width, unsigned height) {
+ unsigned long long total_size = static_cast<unsigned long long>(width) *
+ static_cast<unsigned long long>(height);
+ return total_size > ((1 << 29) - 1);
+ }
+
+ bool purge_aggressively_;
+
+ // This methods gets called at the end of InitFrameBuffer. Subclasses can do
+ // format specific initialization, for e.g. alpha settings, here.
+ virtual void OnInitFrameBuffer(size_t){};
+
+ // Called by InitFrameBuffer to determine if it can take the bitmap of the
+ // previous frame. This condition is different for GIF and WEBP.
+ virtual bool CanReusePreviousFrameBuffer(size_t) const { return false; }
+
+ IntSize size_;
+ bool size_available_ = false;
+ bool is_all_data_received_ = false;
+ bool failed_ = false;
+ bool has_histogrammed_color_space_ = false;
+
+ sk_sp<SkColorSpace> embedded_color_space_ = nullptr;
+ bool source_to_target_color_transform_needs_update_ = false;
+ std::unique_ptr<SkColorSpaceXform> source_to_target_color_transform_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/image_decoder_test.cc b/chromium/third_party/blink/renderer/platform/image-decoders/image_decoder_test.cc
new file mode 100644
index 00000000000..3c7d25bec40
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/image_decoder_test.cc
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/renderer/platform/image-decoders/image_decoder.h"
+
+#include <memory>
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/image-decoders/image_frame.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class TestImageDecoder : public ImageDecoder {
+ public:
+ TestImageDecoder()
+ : ImageDecoder(kAlphaNotPremultiplied,
+ ColorBehavior::TransformToSRGB(),
+ kNoDecodedImageByteLimit) {}
+
+ String FilenameExtension() const override { return ""; }
+
+ Vector<ImageFrame, 1>& FrameBufferCache() { return frame_buffer_cache_; }
+
+ void ResetRequiredPreviousFrames(bool known_opaque = false) {
+ for (size_t i = 0; i < frame_buffer_cache_.size(); ++i)
+ frame_buffer_cache_[i].SetRequiredPreviousFrameIndex(
+ FindRequiredPreviousFrame(i, known_opaque));
+ }
+
+ void InitFrames(size_t num_frames,
+ unsigned width = 100,
+ unsigned height = 100) {
+ SetSize(width, height);
+ frame_buffer_cache_.resize(num_frames);
+ for (size_t i = 0; i < num_frames; ++i)
+ frame_buffer_cache_[i].SetOriginalFrameRect(IntRect(0, 0, width, height));
+ }
+
+ private:
+ void DecodeSize() override {}
+ void Decode(size_t index) override {}
+};
+
+TEST(ImageDecoderTest, sizeCalculationMayOverflow) {
+ std::unique_ptr<TestImageDecoder> decoder(
+ std::make_unique<TestImageDecoder>());
+ EXPECT_FALSE(decoder->SetSize(1 << 29, 1));
+ EXPECT_FALSE(decoder->SetSize(1, 1 << 29));
+ EXPECT_FALSE(decoder->SetSize(1 << 15, 1 << 15));
+ EXPECT_TRUE(decoder->SetSize(1 << 28, 1));
+ EXPECT_TRUE(decoder->SetSize(1, 1 << 28));
+ EXPECT_TRUE(decoder->SetSize(1 << 14, 1 << 14));
+}
+
+TEST(ImageDecoderTest, requiredPreviousFrameIndex) {
+ std::unique_ptr<TestImageDecoder> decoder(
+ std::make_unique<TestImageDecoder>());
+ decoder->InitFrames(6);
+ Vector<ImageFrame, 1>& frame_buffers = decoder->FrameBufferCache();
+
+ frame_buffers[1].SetDisposalMethod(ImageFrame::kDisposeKeep);
+ frame_buffers[2].SetDisposalMethod(ImageFrame::kDisposeOverwritePrevious);
+ frame_buffers[3].SetDisposalMethod(ImageFrame::kDisposeOverwritePrevious);
+ frame_buffers[4].SetDisposalMethod(ImageFrame::kDisposeKeep);
+
+ decoder->ResetRequiredPreviousFrames();
+
+ // The first frame doesn't require any previous frame.
+ EXPECT_EQ(kNotFound, frame_buffers[0].RequiredPreviousFrameIndex());
+ // The previous DisposeNotSpecified frame is required.
+ EXPECT_EQ(0u, frame_buffers[1].RequiredPreviousFrameIndex());
+ // DisposeKeep is treated as DisposeNotSpecified.
+ EXPECT_EQ(1u, frame_buffers[2].RequiredPreviousFrameIndex());
+ // Previous DisposeOverwritePrevious frames are skipped.
+ EXPECT_EQ(1u, frame_buffers[3].RequiredPreviousFrameIndex());
+ EXPECT_EQ(1u, frame_buffers[4].RequiredPreviousFrameIndex());
+ EXPECT_EQ(4u, frame_buffers[5].RequiredPreviousFrameIndex());
+}
+
+TEST(ImageDecoderTest, requiredPreviousFrameIndexDisposeOverwriteBgcolor) {
+ std::unique_ptr<TestImageDecoder> decoder(
+ std::make_unique<TestImageDecoder>());
+ decoder->InitFrames(3);
+ Vector<ImageFrame, 1>& frame_buffers = decoder->FrameBufferCache();
+
+ // Fully covering DisposeOverwriteBgcolor previous frame resets the starting
+ // state.
+ frame_buffers[1].SetDisposalMethod(ImageFrame::kDisposeOverwriteBgcolor);
+ decoder->ResetRequiredPreviousFrames();
+ EXPECT_EQ(kNotFound, frame_buffers[2].RequiredPreviousFrameIndex());
+
+ // Partially covering DisposeOverwriteBgcolor previous frame is required by
+ // this frame.
+ frame_buffers[1].SetOriginalFrameRect(IntRect(50, 50, 50, 50));
+ decoder->ResetRequiredPreviousFrames();
+ EXPECT_EQ(1u, frame_buffers[2].RequiredPreviousFrameIndex());
+}
+
+TEST(ImageDecoderTest, requiredPreviousFrameIndexForFrame1) {
+ std::unique_ptr<TestImageDecoder> decoder(
+ std::make_unique<TestImageDecoder>());
+ decoder->InitFrames(2);
+ Vector<ImageFrame, 1>& frame_buffers = decoder->FrameBufferCache();
+
+ decoder->ResetRequiredPreviousFrames();
+ EXPECT_EQ(0u, frame_buffers[1].RequiredPreviousFrameIndex());
+
+ // The first frame with DisposeOverwritePrevious or DisposeOverwriteBgcolor
+ // resets the starting state.
+ frame_buffers[0].SetDisposalMethod(ImageFrame::kDisposeOverwritePrevious);
+ decoder->ResetRequiredPreviousFrames();
+ EXPECT_EQ(kNotFound, frame_buffers[1].RequiredPreviousFrameIndex());
+ frame_buffers[0].SetDisposalMethod(ImageFrame::kDisposeOverwriteBgcolor);
+ decoder->ResetRequiredPreviousFrames();
+ EXPECT_EQ(kNotFound, frame_buffers[1].RequiredPreviousFrameIndex());
+
+ // ... even if it partially covers.
+ frame_buffers[0].SetOriginalFrameRect(IntRect(50, 50, 50, 50));
+
+ frame_buffers[0].SetDisposalMethod(ImageFrame::kDisposeOverwritePrevious);
+ decoder->ResetRequiredPreviousFrames();
+ EXPECT_EQ(kNotFound, frame_buffers[1].RequiredPreviousFrameIndex());
+ frame_buffers[0].SetDisposalMethod(ImageFrame::kDisposeOverwriteBgcolor);
+ decoder->ResetRequiredPreviousFrames();
+ EXPECT_EQ(kNotFound, frame_buffers[1].RequiredPreviousFrameIndex());
+}
+
+TEST(ImageDecoderTest, requiredPreviousFrameIndexBlendAtopBgcolor) {
+ std::unique_ptr<TestImageDecoder> decoder(
+ std::make_unique<TestImageDecoder>());
+ decoder->InitFrames(3);
+ Vector<ImageFrame, 1>& frame_buffers = decoder->FrameBufferCache();
+
+ frame_buffers[1].SetOriginalFrameRect(IntRect(25, 25, 50, 50));
+ frame_buffers[2].SetAlphaBlendSource(ImageFrame::kBlendAtopBgcolor);
+
+ // A full frame with 'blending method == BlendAtopBgcolor' doesn't depend on
+ // any prior frames.
+ for (int dispose_method = ImageFrame::kDisposeNotSpecified;
+ dispose_method <= ImageFrame::kDisposeOverwritePrevious;
+ ++dispose_method) {
+ frame_buffers[1].SetDisposalMethod(
+ static_cast<ImageFrame::DisposalMethod>(dispose_method));
+ decoder->ResetRequiredPreviousFrames();
+ EXPECT_EQ(kNotFound, frame_buffers[2].RequiredPreviousFrameIndex());
+ }
+
+ // A non-full frame with 'blending method == BlendAtopBgcolor' does depend on
+ // a prior frame.
+ frame_buffers[2].SetOriginalFrameRect(IntRect(50, 50, 50, 50));
+ for (int dispose_method = ImageFrame::kDisposeNotSpecified;
+ dispose_method <= ImageFrame::kDisposeOverwritePrevious;
+ ++dispose_method) {
+ frame_buffers[1].SetDisposalMethod(
+ static_cast<ImageFrame::DisposalMethod>(dispose_method));
+ decoder->ResetRequiredPreviousFrames();
+ EXPECT_NE(kNotFound, frame_buffers[2].RequiredPreviousFrameIndex());
+ }
+}
+
+TEST(ImageDecoderTest, requiredPreviousFrameIndexKnownOpaque) {
+ std::unique_ptr<TestImageDecoder> decoder(
+ std::make_unique<TestImageDecoder>());
+ decoder->InitFrames(3);
+ Vector<ImageFrame, 1>& frame_buffers = decoder->FrameBufferCache();
+
+ frame_buffers[1].SetOriginalFrameRect(IntRect(25, 25, 50, 50));
+
+ // A full frame that is known to be opaque doesn't depend on any prior frames.
+ for (int dispose_method = ImageFrame::kDisposeNotSpecified;
+ dispose_method <= ImageFrame::kDisposeOverwritePrevious;
+ ++dispose_method) {
+ frame_buffers[1].SetDisposalMethod(
+ static_cast<ImageFrame::DisposalMethod>(dispose_method));
+ decoder->ResetRequiredPreviousFrames(true);
+ EXPECT_EQ(kNotFound, frame_buffers[2].RequiredPreviousFrameIndex());
+ }
+
+ // A non-full frame that is known to be opaque does depend on a prior frame.
+ frame_buffers[2].SetOriginalFrameRect(IntRect(50, 50, 50, 50));
+ for (int dispose_method = ImageFrame::kDisposeNotSpecified;
+ dispose_method <= ImageFrame::kDisposeOverwritePrevious;
+ ++dispose_method) {
+ frame_buffers[1].SetDisposalMethod(
+ static_cast<ImageFrame::DisposalMethod>(dispose_method));
+ decoder->ResetRequiredPreviousFrames(true);
+ EXPECT_NE(kNotFound, frame_buffers[2].RequiredPreviousFrameIndex());
+ }
+}
+
+TEST(ImageDecoderTest, clearCacheExceptFrameDoNothing) {
+ std::unique_ptr<TestImageDecoder> decoder(
+ std::make_unique<TestImageDecoder>());
+ decoder->ClearCacheExceptFrame(0);
+
+ // This should not crash.
+ decoder->InitFrames(20);
+ decoder->ClearCacheExceptFrame(kNotFound);
+}
+
+TEST(ImageDecoderTest, clearCacheExceptFrameAll) {
+ const size_t kNumFrames = 10;
+ std::unique_ptr<TestImageDecoder> decoder(
+ std::make_unique<TestImageDecoder>());
+ decoder->InitFrames(kNumFrames);
+ Vector<ImageFrame, 1>& frame_buffers = decoder->FrameBufferCache();
+ for (size_t i = 0; i < kNumFrames; ++i)
+ frame_buffers[i].SetStatus(i % 2 ? ImageFrame::kFramePartial
+ : ImageFrame::kFrameComplete);
+
+ decoder->ClearCacheExceptFrame(kNotFound);
+
+ for (size_t i = 0; i < kNumFrames; ++i) {
+ SCOPED_TRACE(testing::Message() << i);
+ EXPECT_EQ(ImageFrame::kFrameEmpty, frame_buffers[i].GetStatus());
+ }
+}
+
+TEST(ImageDecoderTest, clearCacheExceptFramePreverveClearExceptFrame) {
+ const size_t kNumFrames = 10;
+ std::unique_ptr<TestImageDecoder> decoder(
+ std::make_unique<TestImageDecoder>());
+ decoder->InitFrames(kNumFrames);
+ Vector<ImageFrame, 1>& frame_buffers = decoder->FrameBufferCache();
+ for (size_t i = 0; i < kNumFrames; ++i)
+ frame_buffers[i].SetStatus(ImageFrame::kFrameComplete);
+
+ decoder->ResetRequiredPreviousFrames();
+ decoder->ClearCacheExceptFrame(5);
+ for (size_t i = 0; i < kNumFrames; ++i) {
+ SCOPED_TRACE(testing::Message() << i);
+ if (i == 5)
+ EXPECT_EQ(ImageFrame::kFrameComplete, frame_buffers[i].GetStatus());
+ else
+ EXPECT_EQ(ImageFrame::kFrameEmpty, frame_buffers[i].GetStatus());
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/image_decoder_test_helpers.cc b/chromium/third_party/blink/renderer/platform/image-decoders/image_decoder_test_helpers.cc
new file mode 100644
index 00000000000..c4d4c787890
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/image_decoder_test_helpers.cc
@@ -0,0 +1,598 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/image-decoders/image_decoder_test_helpers.h"
+
+#include <memory>
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/image-decoders/image_frame.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
+#include "third_party/blink/renderer/platform/wtf/string_hasher.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+
+namespace blink {
+
+scoped_refptr<SharedBuffer> ReadFile(const char* file_name) {
+ String file_path = test::BlinkLayoutTestsDir();
+ file_path.append(file_name);
+ return test::ReadFromFile(file_path);
+}
+
+scoped_refptr<SharedBuffer> ReadFile(const char* dir, const char* file_name) {
+ StringBuilder file_path;
+ if (strncmp(dir, "LayoutTests/", 12) == 0) {
+ file_path.Append(test::BlinkLayoutTestsDir());
+ file_path.Append('/');
+ file_path.Append(dir + 12);
+ } else {
+ file_path.Append(test::BlinkRootDir());
+ file_path.Append('/');
+ file_path.Append(dir);
+ }
+ file_path.Append('/');
+ file_path.Append(file_name);
+ return test::ReadFromFile(file_path.ToString());
+}
+
+unsigned HashBitmap(const SkBitmap& bitmap) {
+ return StringHasher::HashMemory(bitmap.getPixels(), bitmap.computeByteSize());
+}
+
+static unsigned CreateDecodingBaseline(DecoderCreator create_decoder,
+ SharedBuffer* data) {
+ std::unique_ptr<ImageDecoder> decoder = create_decoder();
+ decoder->SetData(data, true);
+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(0);
+ return HashBitmap(frame->Bitmap());
+}
+
+void CreateDecodingBaseline(DecoderCreator create_decoder,
+ SharedBuffer* data,
+ Vector<unsigned>* baseline_hashes) {
+ std::unique_ptr<ImageDecoder> decoder = create_decoder();
+ decoder->SetData(data, true);
+ size_t frame_count = decoder->FrameCount();
+ for (size_t i = 0; i < frame_count; ++i) {
+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(i);
+ baseline_hashes->push_back(HashBitmap(frame->Bitmap()));
+ }
+}
+
+void TestByteByByteDecode(DecoderCreator create_decoder,
+ SharedBuffer* shared_data,
+ size_t expected_frame_count,
+ int expected_repetition_count) {
+ const Vector<char> data = shared_data->Copy();
+
+ Vector<unsigned> baseline_hashes;
+ CreateDecodingBaseline(create_decoder, shared_data, &baseline_hashes);
+
+ std::unique_ptr<ImageDecoder> decoder = create_decoder();
+
+ size_t frame_count = 0;
+ size_t frames_decoded = 0;
+
+ // Pass data to decoder byte by byte.
+ scoped_refptr<SharedBuffer> source_data[2] = {SharedBuffer::Create(),
+ SharedBuffer::Create()};
+ const char* source = data.data();
+
+ for (size_t length = 1; length <= data.size() && !decoder->Failed();
+ ++length) {
+ source_data[0]->Append(source, 1u);
+ source_data[1]->Append(source++, 1u);
+ // Alternate the buffers to cover the JPEGImageDecoder::OnSetData restart
+ // code.
+ decoder->SetData(source_data[length & 1].get(), length == data.size());
+
+ EXPECT_LE(frame_count, decoder->FrameCount());
+ frame_count = decoder->FrameCount();
+
+ if (!decoder->IsSizeAvailable())
+ continue;
+
+ for (size_t i = frames_decoded; i < frame_count; ++i) {
+ // In ICOImageDecoder memory layout could differ from frame order.
+ // E.g. memory layout could be |<frame1><frame0>| and frame_count
+ // would return 1 until receiving full file.
+ // When file is completely received frame_count would return 2 and
+ // only then both frames could be completely decoded.
+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(i);
+ if (frame && frame->GetStatus() == ImageFrame::kFrameComplete)
+ ++frames_decoded;
+ }
+ }
+
+ EXPECT_FALSE(decoder->Failed());
+ EXPECT_EQ(expected_frame_count, decoder->FrameCount());
+ EXPECT_EQ(expected_frame_count, frames_decoded);
+ EXPECT_EQ(expected_repetition_count, decoder->RepetitionCount());
+
+ ASSERT_EQ(expected_frame_count, baseline_hashes.size());
+ for (size_t i = 0; i < decoder->FrameCount(); i++) {
+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(i);
+ EXPECT_EQ(baseline_hashes[i], HashBitmap(frame->Bitmap()));
+ }
+}
+
+// This test verifies that calling SharedBuffer::MergeSegmentsIntoBuffer() does
+// not break decoding at a critical point: in between a call to decode the size
+// (when the decoder stops while it may still have input data to read) and a
+// call to do a full decode.
+static void TestMergeBuffer(DecoderCreator create_decoder,
+ SharedBuffer* shared_data) {
+ const unsigned hash = CreateDecodingBaseline(create_decoder, shared_data);
+ const Vector<char> data = shared_data->Copy();
+
+ // In order to do any verification, this test needs to move the data owned
+ // by the SharedBuffer. A way to guarantee that is to create a new one, and
+ // then append a string of characters greater than kSegmentSize. This
+ // results in writing the data into a segment, skipping the internal
+ // contiguous buffer.
+ scoped_refptr<SharedBuffer> segmented_data = SharedBuffer::Create();
+ segmented_data->Append(data.data(), data.size());
+
+ std::unique_ptr<ImageDecoder> decoder = create_decoder();
+ decoder->SetData(segmented_data.get(), true);
+
+ ASSERT_TRUE(decoder->IsSizeAvailable());
+
+ // This will call SharedBuffer::MergeSegmentsIntoBuffer, copying all
+ // segments into the contiguous buffer. If the ImageDecoder was pointing to
+ // data in a segment, its pointer would no longer be valid.
+ segmented_data->Data();
+
+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(0);
+ ASSERT_FALSE(decoder->Failed());
+ EXPECT_EQ(frame->GetStatus(), ImageFrame::kFrameComplete);
+ EXPECT_EQ(HashBitmap(frame->Bitmap()), hash);
+}
+
+static void TestRandomFrameDecode(DecoderCreator create_decoder,
+ SharedBuffer* full_data,
+ size_t skipping_step) {
+ Vector<unsigned> baseline_hashes;
+ CreateDecodingBaseline(create_decoder, full_data, &baseline_hashes);
+ size_t frame_count = baseline_hashes.size();
+
+ // Random decoding should get the same results as sequential decoding.
+ std::unique_ptr<ImageDecoder> decoder = create_decoder();
+ decoder->SetData(full_data, true);
+ for (size_t i = 0; i < skipping_step; ++i) {
+ for (size_t j = i; j < frame_count; j += skipping_step) {
+ SCOPED_TRACE(testing::Message() << "Random i:" << i << " j:" << j);
+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(j);
+ EXPECT_EQ(baseline_hashes[j], HashBitmap(frame->Bitmap()));
+ }
+ }
+
+ // Decoding in reverse order.
+ decoder = create_decoder();
+ decoder->SetData(full_data, true);
+ for (size_t i = frame_count; i; --i) {
+ SCOPED_TRACE(testing::Message() << "Reverse i:" << i);
+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(i - 1);
+ EXPECT_EQ(baseline_hashes[i - 1], HashBitmap(frame->Bitmap()));
+ }
+}
+
+static void TestRandomDecodeAfterClearFrameBufferCache(
+ DecoderCreator create_decoder,
+ SharedBuffer* data,
+ size_t skipping_step) {
+ Vector<unsigned> baseline_hashes;
+ CreateDecodingBaseline(create_decoder, data, &baseline_hashes);
+ size_t frame_count = baseline_hashes.size();
+
+ std::unique_ptr<ImageDecoder> decoder = create_decoder();
+ decoder->SetData(data, true);
+ for (size_t clear_except_frame = 0; clear_except_frame < frame_count;
+ ++clear_except_frame) {
+ decoder->ClearCacheExceptFrame(clear_except_frame);
+ for (size_t i = 0; i < skipping_step; ++i) {
+ for (size_t j = 0; j < frame_count; j += skipping_step) {
+ SCOPED_TRACE(testing::Message() << "Random i:" << i << " j:" << j);
+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(j);
+ EXPECT_EQ(baseline_hashes[j], HashBitmap(frame->Bitmap()));
+ }
+ }
+ }
+}
+
+static void TestDecodeAfterReallocatingData(DecoderCreator create_decoder,
+ SharedBuffer* data) {
+ std::unique_ptr<ImageDecoder> decoder = create_decoder();
+
+ // Parse from 'data'.
+ decoder->SetData(data, true);
+ size_t frame_count = decoder->FrameCount();
+
+ // ... and then decode frames from 'reallocated_data'.
+ Vector<char> copy = data->Copy();
+ scoped_refptr<SharedBuffer> reallocated_data =
+ SharedBuffer::AdoptVector(copy);
+ ASSERT_TRUE(reallocated_data.get());
+ data->Clear();
+ decoder->SetData(reallocated_data.get(), true);
+
+ for (size_t i = 0; i < frame_count; ++i) {
+ const ImageFrame* const frame = decoder->DecodeFrameBufferAtIndex(i);
+ EXPECT_EQ(ImageFrame::kFrameComplete, frame->GetStatus());
+ }
+}
+
+static void TestByteByByteSizeAvailable(DecoderCreator create_decoder,
+ SharedBuffer* data,
+ size_t frame_offset,
+ bool has_color_space,
+ int expected_repetition_count) {
+ std::unique_ptr<ImageDecoder> decoder = create_decoder();
+ EXPECT_LT(frame_offset, data->size());
+
+ // Send data to the decoder byte-by-byte and use the provided frame offset in
+ // the data to check that IsSizeAvailable() changes state only when that
+ // offset is reached. Also check other decoder state.
+ scoped_refptr<SharedBuffer> temp_data = SharedBuffer::Create();
+ const Vector<char> source_buffer = data->Copy();
+ const char* source = source_buffer.data();
+ for (size_t length = 1; length <= frame_offset; ++length) {
+ temp_data->Append(source++, 1u);
+ decoder->SetData(temp_data.get(), false);
+
+ if (length < frame_offset) {
+ EXPECT_FALSE(decoder->IsSizeAvailable());
+ EXPECT_TRUE(decoder->Size().IsEmpty());
+ EXPECT_FALSE(decoder->HasEmbeddedColorSpace());
+ EXPECT_EQ(0u, decoder->FrameCount());
+ EXPECT_EQ(kAnimationLoopOnce, decoder->RepetitionCount());
+ EXPECT_FALSE(decoder->DecodeFrameBufferAtIndex(0));
+ } else {
+ EXPECT_TRUE(decoder->IsSizeAvailable());
+ EXPECT_FALSE(decoder->Size().IsEmpty());
+ EXPECT_EQ(decoder->HasEmbeddedColorSpace(), has_color_space);
+ EXPECT_EQ(1u, decoder->FrameCount());
+ EXPECT_EQ(expected_repetition_count, decoder->RepetitionCount());
+ }
+
+ ASSERT_FALSE(decoder->Failed());
+ }
+}
+
+static void TestProgressiveDecoding(DecoderCreator create_decoder,
+ SharedBuffer* full_buffer,
+ size_t increment) {
+ const Vector<char> full_data = full_buffer->Copy();
+ const size_t full_length = full_data.size();
+
+ std::unique_ptr<ImageDecoder> decoder;
+
+ Vector<unsigned> truncated_hashes;
+ Vector<unsigned> progressive_hashes;
+
+ // Compute hashes when the file is truncated.
+ scoped_refptr<SharedBuffer> data = SharedBuffer::Create();
+ const char* source = full_data.data();
+ for (size_t i = 1; i <= full_length; i += increment) {
+ decoder = create_decoder();
+ data->Append(source++, 1u);
+ decoder->SetData(data.get(), i == full_length);
+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(0);
+ if (!frame) {
+ truncated_hashes.push_back(0);
+ continue;
+ }
+ truncated_hashes.push_back(HashBitmap(frame->Bitmap()));
+ }
+
+ // Compute hashes when the file is progressively decoded.
+ decoder = create_decoder();
+ data = SharedBuffer::Create();
+ source = full_data.data();
+ for (size_t i = 1; i <= full_length; i += increment) {
+ data->Append(source++, 1u);
+ decoder->SetData(data.get(), i == full_length);
+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(0);
+ if (!frame) {
+ progressive_hashes.push_back(0);
+ continue;
+ }
+ progressive_hashes.push_back(HashBitmap(frame->Bitmap()));
+ }
+
+ for (size_t i = 0; i < truncated_hashes.size(); ++i)
+ ASSERT_EQ(truncated_hashes[i], progressive_hashes[i]);
+}
+
+void TestUpdateRequiredPreviousFrameAfterFirstDecode(
+ DecoderCreator create_decoder,
+ SharedBuffer* full_buffer) {
+ const Vector<char> full_data = full_buffer->Copy();
+ std::unique_ptr<ImageDecoder> decoder = create_decoder();
+
+ // Give it data that is enough to parse but not decode in order to check the
+ // status of RequiredPreviousFrameIndex before decoding.
+ scoped_refptr<SharedBuffer> data = SharedBuffer::Create();
+ const char* source = full_data.data();
+ do {
+ data->Append(source++, 1u);
+ decoder->SetData(data.get(), false);
+ } while (!decoder->FrameCount() ||
+ decoder->DecodeFrameBufferAtIndex(0)->GetStatus() ==
+ ImageFrame::kFrameEmpty);
+
+ EXPECT_EQ(kNotFound,
+ decoder->DecodeFrameBufferAtIndex(0)->RequiredPreviousFrameIndex());
+ unsigned frame_count = decoder->FrameCount();
+ for (size_t i = 1; i < frame_count; ++i) {
+ EXPECT_EQ(
+ i - 1,
+ decoder->DecodeFrameBufferAtIndex(i)->RequiredPreviousFrameIndex());
+ }
+
+ decoder->SetData(full_buffer, true);
+ for (size_t i = 0; i < frame_count; ++i) {
+ EXPECT_EQ(
+ kNotFound,
+ decoder->DecodeFrameBufferAtIndex(i)->RequiredPreviousFrameIndex());
+ }
+}
+
+void TestResumePartialDecodeAfterClearFrameBufferCache(
+ DecoderCreator create_decoder,
+ SharedBuffer* full_buffer) {
+ const Vector<char> full_data = full_buffer->Copy();
+ Vector<unsigned> baseline_hashes;
+ CreateDecodingBaseline(create_decoder, full_buffer, &baseline_hashes);
+ size_t frame_count = baseline_hashes.size();
+
+ std::unique_ptr<ImageDecoder> decoder = create_decoder();
+
+ // Let frame 0 be partially decoded.
+ scoped_refptr<SharedBuffer> data = SharedBuffer::Create();
+ const char* source = full_data.data();
+ do {
+ data->Append(source++, 1u);
+ decoder->SetData(data.get(), false);
+ } while (!decoder->FrameCount() ||
+ decoder->DecodeFrameBufferAtIndex(0)->GetStatus() ==
+ ImageFrame::kFrameEmpty);
+
+ // Skip to the last frame and clear.
+ decoder->SetData(full_buffer, true);
+ EXPECT_EQ(frame_count, decoder->FrameCount());
+ ImageFrame* last_frame = decoder->DecodeFrameBufferAtIndex(frame_count - 1);
+ EXPECT_EQ(baseline_hashes[frame_count - 1], HashBitmap(last_frame->Bitmap()));
+ decoder->ClearCacheExceptFrame(kNotFound);
+
+ // Resume decoding of the first frame.
+ ImageFrame* first_frame = decoder->DecodeFrameBufferAtIndex(0);
+ EXPECT_EQ(ImageFrame::kFrameComplete, first_frame->GetStatus());
+ EXPECT_EQ(baseline_hashes[0], HashBitmap(first_frame->Bitmap()));
+}
+
+void TestByteByByteDecode(DecoderCreator create_decoder,
+ const char* file,
+ size_t expected_frame_count,
+ int expected_repetition_count) {
+ scoped_refptr<SharedBuffer> data = ReadFile(file);
+ ASSERT_TRUE(data.get());
+ TestByteByByteDecode(create_decoder, data.get(), expected_frame_count,
+ expected_repetition_count);
+}
+void TestByteByByteDecode(DecoderCreator create_decoder,
+ const char* dir,
+ const char* file,
+ size_t expected_frame_count,
+ int expected_repetition_count) {
+ scoped_refptr<SharedBuffer> data = ReadFile(dir, file);
+ ASSERT_TRUE(data.get());
+ TestByteByByteDecode(create_decoder, data.get(), expected_frame_count,
+ expected_repetition_count);
+}
+
+void TestMergeBuffer(DecoderCreator create_decoder, const char* file) {
+ scoped_refptr<SharedBuffer> data = ReadFile(file);
+ ASSERT_TRUE(data.get());
+ TestMergeBuffer(create_decoder, data.get());
+}
+
+void TestMergeBuffer(DecoderCreator create_decoder,
+ const char* dir,
+ const char* file) {
+ scoped_refptr<SharedBuffer> data = ReadFile(dir, file);
+ ASSERT_TRUE(data.get());
+ TestMergeBuffer(create_decoder, data.get());
+}
+
+void TestRandomFrameDecode(DecoderCreator create_decoder,
+ const char* file,
+ size_t skipping_step) {
+ scoped_refptr<SharedBuffer> data = ReadFile(file);
+ ASSERT_TRUE(data.get());
+ SCOPED_TRACE(file);
+ TestRandomFrameDecode(create_decoder, data.get(), skipping_step);
+}
+void TestRandomFrameDecode(DecoderCreator create_decoder,
+ const char* dir,
+ const char* file,
+ size_t skipping_step) {
+ scoped_refptr<SharedBuffer> data = ReadFile(dir, file);
+ ASSERT_TRUE(data.get());
+ SCOPED_TRACE(file);
+ TestRandomFrameDecode(create_decoder, data.get(), skipping_step);
+}
+
+void TestRandomDecodeAfterClearFrameBufferCache(DecoderCreator create_decoder,
+ const char* file,
+ size_t skipping_step) {
+ scoped_refptr<SharedBuffer> data = ReadFile(file);
+ ASSERT_TRUE(data.get());
+ SCOPED_TRACE(file);
+ TestRandomDecodeAfterClearFrameBufferCache(create_decoder, data.get(),
+ skipping_step);
+}
+
+void TestRandomDecodeAfterClearFrameBufferCache(DecoderCreator create_decoder,
+ const char* dir,
+ const char* file,
+ size_t skipping_step) {
+ scoped_refptr<SharedBuffer> data = ReadFile(dir, file);
+ ASSERT_TRUE(data.get());
+ SCOPED_TRACE(file);
+ TestRandomDecodeAfterClearFrameBufferCache(create_decoder, data.get(),
+ skipping_step);
+}
+
+void TestDecodeAfterReallocatingData(DecoderCreator create_decoder,
+ const char* file) {
+ scoped_refptr<SharedBuffer> data = ReadFile(file);
+ ASSERT_TRUE(data.get());
+ TestDecodeAfterReallocatingData(create_decoder, data.get());
+}
+
+void TestDecodeAfterReallocatingData(DecoderCreator create_decoder,
+ const char* dir,
+ const char* file) {
+ scoped_refptr<SharedBuffer> data = ReadFile(dir, file);
+ ASSERT_TRUE(data.get());
+ TestDecodeAfterReallocatingData(create_decoder, data.get());
+}
+
+void TestByteByByteSizeAvailable(DecoderCreator create_decoder,
+ const char* file,
+ size_t frame_offset,
+ bool has_color_space,
+ int expected_repetition_count) {
+ scoped_refptr<SharedBuffer> data = ReadFile(file);
+ ASSERT_TRUE(data.get());
+ TestByteByByteSizeAvailable(create_decoder, data.get(), frame_offset,
+ has_color_space, expected_repetition_count);
+}
+
+void TestByteByByteSizeAvailable(DecoderCreator create_decoder,
+ const char* dir,
+ const char* file,
+ size_t frame_offset,
+ bool has_color_space,
+ int expected_repetition_count) {
+ scoped_refptr<SharedBuffer> data = ReadFile(dir, file);
+ ASSERT_TRUE(data.get());
+ TestByteByByteSizeAvailable(create_decoder, data.get(), frame_offset,
+ has_color_space, expected_repetition_count);
+}
+
+void TestProgressiveDecoding(DecoderCreator create_decoder,
+ const char* file,
+ size_t increment) {
+ scoped_refptr<SharedBuffer> data = ReadFile(file);
+ ASSERT_TRUE(data.get());
+ TestProgressiveDecoding(create_decoder, data.get(), increment);
+}
+
+void TestProgressiveDecoding(DecoderCreator create_decoder,
+ const char* dir,
+ const char* file,
+ size_t increment) {
+ scoped_refptr<SharedBuffer> data = ReadFile(dir, file);
+ ASSERT_TRUE(data.get());
+ TestProgressiveDecoding(create_decoder, data.get(), increment);
+}
+
+void TestUpdateRequiredPreviousFrameAfterFirstDecode(
+ DecoderCreator create_decoder,
+ const char* dir,
+ const char* file) {
+ scoped_refptr<SharedBuffer> data = ReadFile(dir, file);
+ ASSERT_TRUE(data.get());
+ TestUpdateRequiredPreviousFrameAfterFirstDecode(create_decoder, data.get());
+}
+
+void TestUpdateRequiredPreviousFrameAfterFirstDecode(
+ DecoderCreator create_decoder,
+ const char* file) {
+ scoped_refptr<SharedBuffer> data = ReadFile(file);
+ ASSERT_TRUE(data.get());
+ TestUpdateRequiredPreviousFrameAfterFirstDecode(create_decoder, data.get());
+}
+
+void TestResumePartialDecodeAfterClearFrameBufferCache(
+ DecoderCreator create_decoder,
+ const char* dir,
+ const char* file) {
+ scoped_refptr<SharedBuffer> data = ReadFile(dir, file);
+ ASSERT_TRUE(data.get());
+ TestResumePartialDecodeAfterClearFrameBufferCache(create_decoder, data.get());
+}
+
+void TestResumePartialDecodeAfterClearFrameBufferCache(
+ DecoderCreator create_decoder,
+ const char* file) {
+ scoped_refptr<SharedBuffer> data = ReadFile(file);
+ ASSERT_TRUE(data.get());
+ TestResumePartialDecodeAfterClearFrameBufferCache(create_decoder, data.get());
+}
+
+static uint32_t PremultiplyColor(uint32_t c) {
+ return SkPremultiplyARGBInline(SkGetPackedA32(c), SkGetPackedR32(c),
+ SkGetPackedG32(c), SkGetPackedB32(c));
+}
+
+static void VerifyFramesMatch(const char* file,
+ const ImageFrame* const a,
+ const ImageFrame* const b) {
+ const SkBitmap& bitmap_a = a->Bitmap();
+ const SkBitmap& bitmap_b = b->Bitmap();
+ ASSERT_EQ(bitmap_a.width(), bitmap_b.width());
+ ASSERT_EQ(bitmap_a.height(), bitmap_b.height());
+
+ int max_difference = 0;
+ for (int y = 0; y < bitmap_a.height(); ++y) {
+ for (int x = 0; x < bitmap_a.width(); ++x) {
+ uint32_t color_a = *bitmap_a.getAddr32(x, y);
+ if (!a->PremultiplyAlpha())
+ color_a = PremultiplyColor(color_a);
+ uint32_t color_b = *bitmap_b.getAddr32(x, y);
+ if (!b->PremultiplyAlpha())
+ color_b = PremultiplyColor(color_b);
+ uint8_t* pixel_a = reinterpret_cast<uint8_t*>(&color_a);
+ uint8_t* pixel_b = reinterpret_cast<uint8_t*>(&color_b);
+ for (int channel = 0; channel < 4; ++channel) {
+ const int difference = abs(pixel_a[channel] - pixel_b[channel]);
+ if (difference > max_difference)
+ max_difference = difference;
+ }
+ }
+ }
+
+ // Pre-multiplication could round the RGBA channel values. So, we declare
+ // that the frames match if the RGBA channel values differ by at most 2.
+ EXPECT_GE(2, max_difference) << file;
+}
+
+// Verifies that result of alpha blending is similar for AlphaPremultiplied and
+// AlphaNotPremultiplied cases.
+void TestAlphaBlending(DecoderCreatorWithAlpha create_decoder,
+ const char* file) {
+ scoped_refptr<SharedBuffer> data = ReadFile(file);
+ ASSERT_TRUE(data.get());
+
+ std::unique_ptr<ImageDecoder> decoder_a =
+ create_decoder(ImageDecoder::kAlphaPremultiplied);
+ decoder_a->SetData(data.get(), true);
+
+ std::unique_ptr<ImageDecoder> decoder_b =
+ create_decoder(ImageDecoder::kAlphaNotPremultiplied);
+ decoder_b->SetData(data.get(), true);
+
+ size_t frame_count = decoder_a->FrameCount();
+ ASSERT_EQ(frame_count, decoder_b->FrameCount());
+
+ for (size_t i = 0; i < frame_count; ++i) {
+ VerifyFramesMatch(file, decoder_a->DecodeFrameBufferAtIndex(i),
+ decoder_b->DecodeFrameBufferAtIndex(i));
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/image_decoder_test_helpers.h b/chromium/third_party/blink/renderer/platform/image-decoders/image_decoder_test_helpers.h
new file mode 100644
index 00000000000..51759b9a4db
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/image_decoder_test_helpers.h
@@ -0,0 +1,122 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_IMAGE_DECODER_TEST_HELPERS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_IMAGE_DECODER_TEST_HELPERS_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+class SkBitmap;
+
+namespace blink {
+class ImageDecoder;
+
+const char kDecodersTestingDir[] = "renderer/platform/image-decoders/testing";
+const unsigned kDefaultTestSize = 4 * SharedBuffer::kSegmentSize;
+
+using DecoderCreator = std::unique_ptr<ImageDecoder> (*)();
+using DecoderCreatorWithAlpha =
+ std::unique_ptr<ImageDecoder> (*)(ImageDecoder::AlphaOption);
+
+inline void PrepareReferenceData(char* buffer, size_t size) {
+ for (size_t i = 0; i < size; ++i)
+ buffer[i] = static_cast<char>(i);
+}
+
+scoped_refptr<SharedBuffer> ReadFile(const char* file_name);
+scoped_refptr<SharedBuffer> ReadFile(const char* dir, const char* file_name);
+unsigned HashBitmap(const SkBitmap&);
+void CreateDecodingBaseline(DecoderCreator,
+ SharedBuffer*,
+ Vector<unsigned>* baseline_hashes);
+
+void TestByteByByteDecode(DecoderCreator create_decoder,
+ SharedBuffer* data,
+ size_t expected_frame_count,
+ int expected_repetition_count);
+void TestByteByByteDecode(DecoderCreator create_decoder,
+ const char* file,
+ size_t expected_frame_count,
+ int expected_repetition_count);
+void TestByteByByteDecode(DecoderCreator create_decoder,
+ const char* dir,
+ const char* file,
+ size_t expected_frame_count,
+ int expected_repetition_count);
+
+void TestMergeBuffer(DecoderCreator create_decoder, const char* file);
+void TestMergeBuffer(DecoderCreator create_decoder,
+ const char* dir,
+ const char* file);
+
+// |skipping_step| is used to randomize the decoding order. For images with
+// a small number of frames (e.g. < 10), this value should be smaller, on the
+// order of (number of frames) / 2.
+void TestRandomFrameDecode(DecoderCreator,
+ const char* file,
+ size_t skipping_step = 5);
+void TestRandomFrameDecode(DecoderCreator,
+ const char* dir,
+ const char* file,
+ size_t skipping_step = 5);
+
+// |skipping_step| is used to randomize the decoding order. For images with
+// a small number of frames (e.g. < 10), this value should be smaller, on the
+// order of (number of frames) / 2.
+void TestRandomDecodeAfterClearFrameBufferCache(DecoderCreator,
+ const char* file,
+ size_t skipping_step = 5);
+void TestRandomDecodeAfterClearFrameBufferCache(DecoderCreator,
+ const char* dir,
+ const char* file,
+ size_t skipping_step = 5);
+
+void TestDecodeAfterReallocatingData(DecoderCreator, const char* file);
+void TestDecodeAfterReallocatingData(DecoderCreator,
+ const char* dir,
+ const char* file);
+void TestByteByByteSizeAvailable(DecoderCreator,
+ const char* file,
+ size_t frame_offset,
+ bool has_color_space,
+ int expected_repetition_count);
+void TestByteByByteSizeAvailable(DecoderCreator,
+ const char* dir,
+ const char* file,
+ size_t frame_offset,
+ bool has_color_space,
+ int expected_repetition_count);
+
+// Data is provided in chunks of length |increment| to the decoder. This value
+// can be increased to reduce processing time.
+void TestProgressiveDecoding(DecoderCreator,
+ const char* file,
+ size_t increment = 1);
+void TestProgressiveDecoding(DecoderCreator,
+ const char* dir,
+ const char* file,
+ size_t increment = 1);
+
+void TestUpdateRequiredPreviousFrameAfterFirstDecode(DecoderCreator,
+ const char* dir,
+ const char* file);
+void TestUpdateRequiredPreviousFrameAfterFirstDecode(DecoderCreator,
+ const char* file);
+
+void TestResumePartialDecodeAfterClearFrameBufferCache(DecoderCreator,
+ const char* dir,
+ const char* file);
+void TestResumePartialDecodeAfterClearFrameBufferCache(DecoderCreator,
+ const char* file);
+
+// Verifies that result of alpha blending is similar for AlphaPremultiplied and
+// AlphaNotPremultiplied cases.
+void TestAlphaBlending(DecoderCreatorWithAlpha, const char*);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_IMAGE_DECODER_TEST_HELPERS_H_
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/image_frame.cc b/chromium/third_party/blink/renderer/platform/image-decoders/image_frame.cc
new file mode 100644
index 00000000000..e92d01e5ba7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/image_frame.cc
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2008, 2009 Google, Inc.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/image-decoders/image_frame.h"
+
+#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
+#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h"
+
+namespace blink {
+
+ImageFrame::ImageFrame()
+ : allocator_(nullptr),
+ has_alpha_(true),
+ status_(kFrameEmpty),
+ disposal_method_(kDisposeNotSpecified),
+ alpha_blend_source_(kBlendAtopPreviousFrame),
+ premultiply_alpha_(true),
+ pixels_changed_(false),
+ required_previous_frame_index_(kNotFound) {}
+
+ImageFrame& ImageFrame::operator=(const ImageFrame& other) {
+ if (this == &other)
+ return *this;
+
+ bitmap_ = other.bitmap_;
+ // Be sure to assign this before calling SetStatus(), since SetStatus() may
+ // call NotifyBitmapIfPixelsChanged().
+ pixels_changed_ = other.pixels_changed_;
+ SetMemoryAllocator(other.GetAllocator());
+ SetOriginalFrameRect(other.OriginalFrameRect());
+ SetStatus(other.GetStatus());
+ SetDuration(other.Duration());
+ SetDisposalMethod(other.GetDisposalMethod());
+ SetAlphaBlendSource(other.GetAlphaBlendSource());
+ SetPremultiplyAlpha(other.PremultiplyAlpha());
+ // Be sure that this is called after we've called SetStatus(), since we
+ // look at our status to know what to do with the alpha value.
+ SetHasAlpha(other.HasAlpha());
+ SetRequiredPreviousFrameIndex(other.RequiredPreviousFrameIndex());
+ return *this;
+}
+
+void ImageFrame::ClearPixelData() {
+ bitmap_.reset();
+ status_ = kFrameEmpty;
+ // NOTE: Do not reset other members here; ClearFrameBufferCache()
+ // calls this to free the bitmap data, but other functions like
+ // InitFrameBuffer() and FrameComplete() may still need to read
+ // other metadata out of this frame later.
+}
+
+void ImageFrame::ZeroFillPixelData() {
+ bitmap_.eraseARGB(0, 0, 0, 0);
+ has_alpha_ = true;
+}
+
+bool ImageFrame::CopyBitmapData(const ImageFrame& other) {
+ DCHECK_NE(this, &other);
+ has_alpha_ = other.has_alpha_;
+ bitmap_.reset();
+ SkImageInfo info = other.bitmap_.info();
+ return bitmap_.tryAllocPixels(info) &&
+ other.bitmap_.readPixels(info, bitmap_.getPixels(), bitmap_.rowBytes(),
+ 0, 0);
+}
+
+bool ImageFrame::TakeBitmapDataIfWritable(ImageFrame* other) {
+ DCHECK(other);
+ DCHECK_EQ(kFrameComplete, other->status_);
+ DCHECK_EQ(kFrameEmpty, status_);
+ DCHECK_NE(this, other);
+ if (other->bitmap_.isImmutable())
+ return false;
+ has_alpha_ = other->has_alpha_;
+ bitmap_.reset();
+ bitmap_.swap(other->bitmap_);
+ other->status_ = kFrameEmpty;
+ return true;
+}
+
+bool ImageFrame::AllocatePixelData(int new_width,
+ int new_height,
+ sk_sp<SkColorSpace> color_space) {
+ // AllocatePixelData() should only be called once.
+ DCHECK(!Width() && !Height());
+
+ bitmap_.setInfo(SkImageInfo::MakeN32(
+ new_width, new_height,
+ premultiply_alpha_ ? kPremul_SkAlphaType : kUnpremul_SkAlphaType,
+ std::move(color_space)));
+ return bitmap_.tryAllocPixels(allocator_);
+}
+
+bool ImageFrame::HasAlpha() const {
+ return has_alpha_;
+}
+
+sk_sp<SkImage> ImageFrame::FinalizePixelsAndGetImage() {
+ DCHECK_EQ(kFrameComplete, status_);
+ bitmap_.setImmutable();
+ return SkImage::MakeFromBitmap(bitmap_);
+}
+
+void ImageFrame::SetHasAlpha(bool alpha) {
+ has_alpha_ = alpha;
+
+ bitmap_.setAlphaType(ComputeAlphaType());
+}
+
+void ImageFrame::SetStatus(Status status) {
+ status_ = status;
+ if (status_ == kFrameComplete) {
+ bitmap_.setAlphaType(ComputeAlphaType());
+ // Send pending pixels changed notifications now, because we can't do
+ // this after the bitmap has been marked immutable. We don't set the
+ // bitmap immutable here because it would defeat
+ // TakeBitmapDataIfWritable(). Instead we let the bitmap stay mutable
+ // until someone calls FinalizePixelsAndGetImage() to actually get the
+ // SkImage.
+ NotifyBitmapIfPixelsChanged();
+ }
+}
+
+void ImageFrame::ZeroFillFrameRect(const IntRect& rect) {
+ if (rect.IsEmpty())
+ return;
+
+ bitmap_.eraseArea(rect, SkColorSetARGB(0, 0, 0, 0));
+ SetHasAlpha(true);
+}
+
+static uint8_t BlendChannel(uint8_t src,
+ uint8_t src_a,
+ uint8_t dst,
+ uint8_t dst_a,
+ unsigned scale) {
+ unsigned blend_unscaled = src * src_a + dst * dst_a;
+ DCHECK(blend_unscaled < (1ULL << 32) / scale);
+ return (blend_unscaled * scale) >> 24;
+}
+
+static uint32_t BlendSrcOverDstNonPremultiplied(uint32_t src, uint32_t dst) {
+ uint8_t src_a = SkGetPackedA32(src);
+ if (src_a == 0)
+ return dst;
+
+ uint8_t dst_a = SkGetPackedA32(dst);
+ uint8_t dst_factor_a = (dst_a * SkAlpha255To256(255 - src_a)) >> 8;
+ DCHECK(src_a + dst_factor_a < (1U << 8));
+ uint8_t blend_a = src_a + dst_factor_a;
+ unsigned scale = (1UL << 24) / blend_a;
+
+ uint8_t blend_r = BlendChannel(SkGetPackedR32(src), src_a,
+ SkGetPackedR32(dst), dst_factor_a, scale);
+ uint8_t blend_g = BlendChannel(SkGetPackedG32(src), src_a,
+ SkGetPackedG32(dst), dst_factor_a, scale);
+ uint8_t blend_b = BlendChannel(SkGetPackedB32(src), src_a,
+ SkGetPackedB32(dst), dst_factor_a, scale);
+
+ return SkPackARGB32NoCheck(blend_a, blend_r, blend_g, blend_b);
+}
+
+void ImageFrame::BlendRGBARaw(PixelData* dest,
+ unsigned r,
+ unsigned g,
+ unsigned b,
+ unsigned a) {
+ *dest =
+ BlendSrcOverDstNonPremultiplied(SkPackARGB32NoCheck(a, r, g, b), *dest);
+}
+
+void ImageFrame::BlendSrcOverDstRaw(PixelData* src, PixelData dst) {
+ *src = BlendSrcOverDstNonPremultiplied(*src, dst);
+}
+
+SkAlphaType ImageFrame::ComputeAlphaType() const {
+ // If the frame is not fully loaded, there will be transparent pixels,
+ // so we can't tell skia we're opaque, even for image types that logically
+ // always are (e.g. jpeg).
+ if (!has_alpha_ && status_ == kFrameComplete)
+ return kOpaque_SkAlphaType;
+
+ return premultiply_alpha_ ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/image_frame.h b/chromium/third_party/blink/renderer/platform/image-decoders/image_frame.h
new file mode 100644
index 00000000000..f67a5309479
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/image_frame.h
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_IMAGE_FRAME_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_IMAGE_FRAME_H_
+
+#include "third_party/blink/public/platform/web_vector.h"
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkColorPriv.h"
+
+class SkImage;
+
+namespace blink {
+
+// ImageFrame represents the decoded image data. This buffer is what all
+// decoders write a single frame into.
+class PLATFORM_EXPORT ImageFrame final {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ enum Status { kFrameEmpty, kFramePartial, kFrameComplete };
+ enum DisposalMethod {
+ // If you change the numeric values of these, make sure you audit
+ // all users, as some users may cast raw values to/from these
+ // constants.
+ kDisposeNotSpecified, // Leave frame in framebuffer
+ kDisposeKeep, // Leave frame in framebuffer
+ kDisposeOverwriteBgcolor, // Clear frame to fully transparent
+ kDisposeOverwritePrevious // Clear frame to previous framebuffer contents
+ };
+ // Indicates how non-opaque pixels in the current frame rectangle
+ // are blended with those in the previous frame.
+ // Notes:
+ // * GIF always uses 'BlendAtopPreviousFrame'.
+ // * WebP also uses the 'BlendAtopBgcolor' option. This is useful for
+ // cases where one wants to transform a few opaque pixels of the
+ // previous frame into non-opaque pixels in the current frame.
+ enum AlphaBlendSource {
+ // Blend non-opaque pixels atop the corresponding pixels in the
+ // initial buffer state (i.e. any previous frame buffer after having
+ // been properly disposed).
+ kBlendAtopPreviousFrame,
+
+ // Blend non-opaque pixels against fully transparent (i.e. simply
+ // overwrite the corresponding pixels).
+ kBlendAtopBgcolor,
+ };
+ typedef uint32_t PixelData;
+
+ typedef WebVector<char> ICCProfile;
+
+ ImageFrame();
+
+ // The assignment operator reads has_alpha_ (inside SetStatus()) before it
+ // sets it (in SetHasAlpha()). This doesn't cause any problems, since the
+ // SetHasAlpha() call ensures all state is set correctly, but it means we
+ // need to initialize has_alpha_ to some value before calling the operator
+ // lest any tools complain about using an uninitialized value.
+ ImageFrame(const ImageFrame& other) : has_alpha_(false) { operator=(other); }
+
+ // For backends which refcount their data, this operator doesn't need to
+ // create a new copy of the image data, only increase the ref count.
+ ImageFrame& operator=(const ImageFrame& other);
+
+ // These do not touch other metadata, only the raw pixel data.
+ void ClearPixelData();
+ void ZeroFillPixelData();
+ void ZeroFillFrameRect(const IntRect&);
+
+ // Makes this frame have an independent copy of the provided image's
+ // pixel data, so that modifications in one frame are not reflected in
+ // the other. Returns whether the copy succeeded.
+ bool CopyBitmapData(const ImageFrame&);
+
+ // Moves the bitmap data from the provided frame to this one, leaving the
+ // provided frame empty. Operation is successful only if bitmap data is not
+ // marked as done (immutable). Returns whether the move succeeded.
+ bool TakeBitmapDataIfWritable(ImageFrame*);
+
+ // Copies the pixel data at [(start_x, start_y), (end_x, start_y)) to the
+ // same X-coordinates on each subsequent row up to but not including
+ // end_y.
+ void CopyRowNTimes(int start_x, int end_x, int start_y, int end_y) {
+ DCHECK_LT(start_x, Width());
+ DCHECK_LE(end_x, Width());
+ DCHECK_LT(start_y, Height());
+ DCHECK_LE(end_y, Height());
+ const int row_bytes = (end_x - start_x) * sizeof(PixelData);
+ const PixelData* const start_addr = GetAddr(start_x, start_y);
+ for (int dest_y = start_y + 1; dest_y < end_y; ++dest_y)
+ memcpy(GetAddr(start_x, dest_y), start_addr, row_bytes);
+ }
+
+ // Allocates space for the pixel data. Must be called before any pixels are
+ // written, and should only be called once. The specified color space may be
+ // null if and only if color correct rendering is enabled. Returns true if the
+ // allocation succeeded.
+ bool AllocatePixelData(int new_width, int new_height, sk_sp<SkColorSpace>);
+
+ bool HasAlpha() const;
+ const IntRect& OriginalFrameRect() const { return original_frame_rect_; }
+ Status GetStatus() const { return status_; }
+ TimeDelta Duration() const { return duration_; }
+ DisposalMethod GetDisposalMethod() const { return disposal_method_; }
+ AlphaBlendSource GetAlphaBlendSource() const { return alpha_blend_source_; }
+ bool PremultiplyAlpha() const { return premultiply_alpha_; }
+ SkBitmap::Allocator* GetAllocator() const { return allocator_; }
+
+ // Returns the bitmap that is the output of decoding.
+ const SkBitmap& Bitmap() const { return bitmap_; }
+
+ // Create SkImage from Bitmap() and return it. This should be called only
+ // if frame is complete. The bitmap is set immutable before creating
+ // SkImage to avoid copying bitmap in SkImage::MakeFromBitmap(bitmap_).
+ sk_sp<SkImage> FinalizePixelsAndGetImage();
+
+ // Returns true if the pixels changed, but the bitmap has not yet been
+ // notified.
+ bool PixelsChanged() const { return pixels_changed_; }
+ size_t RequiredPreviousFrameIndex() const {
+ return required_previous_frame_index_;
+ }
+ void SetHasAlpha(bool alpha);
+ void SetOriginalFrameRect(const IntRect& r) { original_frame_rect_ = r; }
+ void SetStatus(Status);
+ void SetDuration(TimeDelta duration) { duration_ = duration; }
+ void SetDisposalMethod(DisposalMethod disposal_method) {
+ disposal_method_ = disposal_method;
+ }
+ void SetAlphaBlendSource(AlphaBlendSource alpha_blend_source) {
+ alpha_blend_source_ = alpha_blend_source;
+ }
+ void SetPremultiplyAlpha(bool premultiply_alpha) {
+ premultiply_alpha_ = premultiply_alpha;
+ }
+ void SetMemoryAllocator(SkBitmap::Allocator* allocator) {
+ allocator_ = allocator;
+ }
+ // The pixels_changed flag needs to be set when the raw pixel data was
+ // directly modified (e.g. through a pointer or SetRGBA). The flag is usually
+ // set after a batch of changes has been made.
+ void SetPixelsChanged(bool pixels_changed) {
+ pixels_changed_ = pixels_changed;
+ }
+ void SetRequiredPreviousFrameIndex(size_t previous_frame_index) {
+ required_previous_frame_index_ = previous_frame_index;
+ }
+
+ inline PixelData* GetAddr(int x, int y) { return bitmap_.getAddr32(x, y); }
+
+ inline void SetRGBA(int x,
+ int y,
+ unsigned r,
+ unsigned g,
+ unsigned b,
+ unsigned a) {
+ SetRGBA(GetAddr(x, y), r, g, b, a);
+ }
+
+ inline void SetRGBA(PixelData* dest,
+ unsigned r,
+ unsigned g,
+ unsigned b,
+ unsigned a) {
+ if (premultiply_alpha_)
+ SetRGBAPremultiply(dest, r, g, b, a);
+ else
+ *dest = SkPackARGB32NoCheck(a, r, g, b);
+ }
+
+ static inline void SetRGBAPremultiply(PixelData* dest,
+ unsigned r,
+ unsigned g,
+ unsigned b,
+ unsigned a) {
+ enum FractionControl { kRoundFractionControl = 257 * 128 };
+
+ if (a < 255) {
+ unsigned alpha = a * 257;
+ r = (r * alpha + kRoundFractionControl) >> 16;
+ g = (g * alpha + kRoundFractionControl) >> 16;
+ b = (b * alpha + kRoundFractionControl) >> 16;
+ }
+
+ *dest = SkPackARGB32NoCheck(a, r, g, b);
+ }
+
+ static inline void SetRGBARaw(PixelData* dest,
+ unsigned r,
+ unsigned g,
+ unsigned b,
+ unsigned a) {
+ *dest = SkPackARGB32NoCheck(a, r, g, b);
+ }
+
+ // Blend the RGBA pixel provided by |red|, |green|, |blue| and |alpha| over
+ // the pixel in |dest|, without premultiplication, and overwrite |dest| with
+ // the result.
+ static void BlendRGBARaw(PixelData* dest,
+ unsigned red,
+ unsigned green,
+ unsigned blue,
+ unsigned alpha);
+
+ // Blend the pixel, without premultiplication, in |src| over |dst| and
+ // overwrite |src| with the result.
+ static void BlendSrcOverDstRaw(PixelData* src, PixelData dst);
+
+ // Blend the RGBA pixel provided by |r|, |g|, |b|, |a| over the pixel in
+ // |dest| and overwrite |dest| with the result. Premultiply the pixel values
+ // before blending.
+ static inline void BlendRGBAPremultiplied(PixelData* dest,
+ unsigned r,
+ unsigned g,
+ unsigned b,
+ unsigned a) {
+ // If the new pixel is completely transparent, no operation is necessary
+ // since |dest| contains the background pixel.
+ if (a == 0x0)
+ return;
+
+ // If the new pixel is opaque, no need for blending - just write the pixel.
+ if (a == 0xFF) {
+ SetRGBAPremultiply(dest, r, g, b, a);
+ return;
+ }
+
+ PixelData src;
+ SetRGBAPremultiply(&src, r, g, b, a);
+ *dest = SkPMSrcOver(src, *dest);
+ }
+
+ // Blend the pixel in |src| over |dst| and overwrite |src| with the result.
+ static inline void BlendSrcOverDstPremultiplied(PixelData* src,
+ PixelData dst) {
+ *src = SkPMSrcOver(*src, dst);
+ }
+
+ // Notifies the SkBitmap if any pixels changed and resets the flag.
+ inline void NotifyBitmapIfPixelsChanged() {
+ if (pixels_changed_)
+ bitmap_.notifyPixelsChanged();
+ pixels_changed_ = false;
+ }
+
+ private:
+ int Width() const { return bitmap_.width(); }
+
+ int Height() const { return bitmap_.height(); }
+
+ SkAlphaType ComputeAlphaType() const;
+
+ SkBitmap bitmap_;
+ SkBitmap::Allocator* allocator_;
+ bool has_alpha_;
+ // This will always just be the entire buffer except for GIF or WebP
+ // frames whose original rect was smaller than the overall image size.
+ IntRect original_frame_rect_;
+ Status status_;
+ TimeDelta duration_;
+ DisposalMethod disposal_method_;
+ AlphaBlendSource alpha_blend_source_;
+ bool premultiply_alpha_;
+ // True if the pixels changed, but the bitmap has not yet been notified.
+ bool pixels_changed_;
+
+ // The frame that must be decoded before this frame can be decoded.
+ // WTF::kNotFound if this frame doesn't require any previous frame.
+ // This is used by ImageDecoder::ClearCacheExceptFrame(), and will never
+ // be read for image formats that do not have multiple frames.
+ size_t required_previous_frame_index_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.cc b/chromium/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.cc
new file mode 100644
index 00000000000..f3e4416d756
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.cc
@@ -0,0 +1,1074 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc.
+ *
+ * Portions are Copyright (C) 2001-6 mozilla.org
+ *
+ * Other contributors:
+ * Stuart Parmenter <stuart@mozilla.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Alternatively, the contents of this file may be used under the terms
+ * of either the Mozilla Public License Version 1.1, found at
+ * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
+ * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
+ * (the "GPL"), in which case the provisions of the MPL or the GPL are
+ * applicable instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of one of those two
+ * licenses (the MPL or the GPL) and not to allow others to use your
+ * version of this file under the LGPL, indicate your decision by
+ * deletingthe provisions above and replace them with the notice and
+ * other provisions required by the MPL or the GPL, as the case may be.
+ * If you do not delete the provisions above, a recipient may use your
+ * version of this file under any of the LGPL, the MPL or the GPL.
+ */
+
+#include "third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.h"
+
+#include <memory>
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/instrumentation/platform_instrumentation.h"
+
+extern "C" {
+#include <stdio.h> // jpeglib.h needs stdio FILE.
+#include "jpeglib.h"
+#include "iccjpeg.h"
+#include <setjmp.h>
+}
+
+#if defined(ARCH_CPU_BIG_ENDIAN)
+#error Blink assumes a little-endian target.
+#endif
+
+#if defined(JCS_ALPHA_EXTENSIONS)
+#define TURBO_JPEG_RGB_SWIZZLE
+#if SK_B32_SHIFT // Output little-endian RGBA pixels (Android).
+inline J_COLOR_SPACE rgbOutputColorSpace() {
+ return JCS_EXT_RGBA;
+}
+#else // Output little-endian BGRA pixels.
+inline J_COLOR_SPACE rgbOutputColorSpace() {
+ return JCS_EXT_BGRA;
+}
+#endif
+inline bool turboSwizzled(J_COLOR_SPACE colorSpace) {
+ return colorSpace == JCS_EXT_RGBA || colorSpace == JCS_EXT_BGRA;
+}
+#else
+inline J_COLOR_SPACE rgbOutputColorSpace() {
+ return JCS_RGB;
+}
+#endif
+
+namespace {
+
+const int exifMarker = JPEG_APP0 + 1;
+
+// JPEG only supports a denominator of 8.
+const unsigned g_scale_denomiator = 8;
+
+} // namespace
+
+namespace blink {
+
+struct decoder_error_mgr {
+ DISALLOW_NEW();
+ struct jpeg_error_mgr pub; // "public" fields for IJG library
+ int num_corrupt_warnings; // Counts corrupt warning messages
+ jmp_buf setjmp_buffer; // For handling catastropic errors
+};
+
+struct decoder_source_mgr {
+ DISALLOW_NEW();
+ struct jpeg_source_mgr pub; // "public" fields for IJG library
+ JPEGImageReader* reader;
+};
+
+enum jstate {
+ JPEG_HEADER, // Reading JFIF headers
+ JPEG_START_DECOMPRESS,
+ JPEG_DECOMPRESS_PROGRESSIVE, // Output progressive pixels
+ JPEG_DECOMPRESS_SEQUENTIAL, // Output sequential pixels
+ JPEG_DONE
+};
+
+enum yuv_subsampling {
+ YUV_UNKNOWN,
+ YUV_410,
+ YUV_411,
+ YUV_420,
+ YUV_422,
+ YUV_440,
+ YUV_444
+};
+
+void init_source(j_decompress_ptr jd);
+boolean fill_input_buffer(j_decompress_ptr jd);
+void skip_input_data(j_decompress_ptr jd, long num_bytes);
+void term_source(j_decompress_ptr jd);
+void error_exit(j_common_ptr cinfo);
+void emit_message(j_common_ptr cinfo, int msg_level);
+
+static unsigned ReadUint16(JOCTET* data, bool is_big_endian) {
+ if (is_big_endian)
+ return (GETJOCTET(data[0]) << 8) | GETJOCTET(data[1]);
+ return (GETJOCTET(data[1]) << 8) | GETJOCTET(data[0]);
+}
+
+static unsigned ReadUint32(JOCTET* data, bool is_big_endian) {
+ if (is_big_endian)
+ return (GETJOCTET(data[0]) << 24) | (GETJOCTET(data[1]) << 16) |
+ (GETJOCTET(data[2]) << 8) | GETJOCTET(data[3]);
+ return (GETJOCTET(data[3]) << 24) | (GETJOCTET(data[2]) << 16) |
+ (GETJOCTET(data[1]) << 8) | GETJOCTET(data[0]);
+}
+
+static bool CheckExifHeader(jpeg_saved_marker_ptr marker,
+ bool& is_big_endian,
+ unsigned& ifd_offset) {
+ // For exif data, the APP1 block is followed by 'E', 'x', 'i', 'f', '\0',
+ // then a fill byte, and then a tiff file that contains the metadata.
+ // A tiff file starts with 'I', 'I' (intel / little endian byte order) or
+ // 'M', 'M' (motorola / big endian byte order), followed by (uint16_t)42,
+ // followed by an uint32_t with the offset to the tag block, relative to the
+ // tiff file start.
+ const unsigned kExifHeaderSize = 14;
+ if (!(marker->marker == exifMarker &&
+ marker->data_length >= kExifHeaderSize && marker->data[0] == 'E' &&
+ marker->data[1] == 'x' && marker->data[2] == 'i' &&
+ marker->data[3] == 'f' &&
+ marker->data[4] == '\0'
+ // data[5] is a fill byte
+ && ((marker->data[6] == 'I' && marker->data[7] == 'I') ||
+ (marker->data[6] == 'M' && marker->data[7] == 'M'))))
+ return false;
+
+ is_big_endian = marker->data[6] == 'M';
+ if (ReadUint16(marker->data + 8, is_big_endian) != 42)
+ return false;
+
+ ifd_offset = ReadUint32(marker->data + 10, is_big_endian);
+ return true;
+}
+
+static ImageOrientation ReadImageOrientation(jpeg_decompress_struct* info) {
+ // The JPEG decoder looks at EXIF metadata.
+ // FIXME: Possibly implement XMP and IPTC support.
+ const unsigned kOrientationTag = 0x112;
+ const unsigned kShortType = 3;
+ for (jpeg_saved_marker_ptr marker = info->marker_list; marker;
+ marker = marker->next) {
+ bool is_big_endian;
+ unsigned ifd_offset;
+ if (!CheckExifHeader(marker, is_big_endian, ifd_offset))
+ continue;
+ const unsigned kOffsetToTiffData =
+ 6; // Account for 'Exif\0<fill byte>' header.
+ if (marker->data_length < kOffsetToTiffData ||
+ ifd_offset >= marker->data_length - kOffsetToTiffData)
+ continue;
+ ifd_offset += kOffsetToTiffData;
+
+ // The jpeg exif container format contains a tiff block for metadata.
+ // A tiff image file directory (ifd) consists of a uint16_t describing
+ // the number of ifd entries, followed by that many entries.
+ // When touching this code, it's useful to look at the tiff spec:
+ // http://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf
+ JOCTET* ifd = marker->data + ifd_offset;
+ JOCTET* end = marker->data + marker->data_length;
+ if (end - ifd < 2)
+ continue;
+ unsigned tag_count = ReadUint16(ifd, is_big_endian);
+ ifd += 2; // Skip over the uint16 that was just read.
+
+ // Every ifd entry is 2 bytes of tag, 2 bytes of contents datatype,
+ // 4 bytes of number-of-elements, and 4 bytes of either offset to the
+ // tag data, or if the data is small enough, the inlined data itself.
+ const int kIfdEntrySize = 12;
+ for (unsigned i = 0; i < tag_count && end - ifd >= kIfdEntrySize;
+ ++i, ifd += kIfdEntrySize) {
+ unsigned tag = ReadUint16(ifd, is_big_endian);
+ unsigned type = ReadUint16(ifd + 2, is_big_endian);
+ unsigned count = ReadUint32(ifd + 4, is_big_endian);
+ if (tag == kOrientationTag && type == kShortType && count == 1)
+ return ImageOrientation::FromEXIFValue(
+ ReadUint16(ifd + 8, is_big_endian));
+ }
+ }
+
+ return ImageOrientation();
+}
+
+static IntSize ComputeYUVSize(const jpeg_decompress_struct* info,
+ int component) {
+ return IntSize(info->cur_comp_info[component]->downsampled_width,
+ info->cur_comp_info[component]->downsampled_height);
+}
+
+static size_t ComputeYUVWidthBytes(const jpeg_decompress_struct* info,
+ int component) {
+ return info->cur_comp_info[component]->width_in_blocks * DCTSIZE;
+}
+
+static yuv_subsampling YuvSubsampling(const jpeg_decompress_struct& info) {
+ if ((DCTSIZE == 8) && (info.num_components == 3) && (info.scale_denom <= 8) &&
+ (info.cur_comp_info[0]) && (info.cur_comp_info[1]) &&
+ (info.cur_comp_info[2]) && (info.cur_comp_info[1]->h_samp_factor == 1) &&
+ (info.cur_comp_info[1]->v_samp_factor == 1) &&
+ (info.cur_comp_info[2]->h_samp_factor == 1) &&
+ (info.cur_comp_info[2]->v_samp_factor == 1)) {
+ int h = info.cur_comp_info[0]->h_samp_factor;
+ int v = info.cur_comp_info[0]->v_samp_factor;
+ // 4:4:4 : (h == 1) && (v == 1)
+ // 4:4:0 : (h == 1) && (v == 2)
+ // 4:2:2 : (h == 2) && (v == 1)
+ // 4:2:0 : (h == 2) && (v == 2)
+ // 4:1:1 : (h == 4) && (v == 1)
+ // 4:1:0 : (h == 4) && (v == 2)
+ if (v == 1) {
+ switch (h) {
+ case 1:
+ return YUV_444;
+ case 2:
+ return YUV_422;
+ case 4:
+ return YUV_411;
+ default:
+ break;
+ }
+ } else if (v == 2) {
+ switch (h) {
+ case 1:
+ return YUV_440;
+ case 2:
+ return YUV_420;
+ case 4:
+ return YUV_410;
+ default:
+ break;
+ }
+ }
+ }
+
+ return YUV_UNKNOWN;
+}
+
+static void ProgressMonitor(j_common_ptr info) {
+ int scan = ((j_decompress_ptr)info)->input_scan_number;
+ // Progressive images with a very large number of scans can cause the
+ // decoder to hang. Here we use the progress monitor to abort on
+ // a very large number of scans. 100 is arbitrary, but much larger
+ // than the number of scans we might expect in a normal image.
+ if (scan >= 100) {
+ error_exit(info);
+ }
+}
+
+class JPEGImageReader final {
+ USING_FAST_MALLOC(JPEGImageReader);
+ WTF_MAKE_NONCOPYABLE(JPEGImageReader);
+
+ public:
+ JPEGImageReader(JPEGImageDecoder* decoder)
+ : decoder_(decoder),
+ needs_restart_(false),
+ restart_position_(0),
+ next_read_position_(0),
+ last_set_byte_(nullptr),
+ state_(JPEG_HEADER),
+ samples_(nullptr) {
+ memset(&info_, 0, sizeof(jpeg_decompress_struct));
+
+ // Set up the normal JPEG error routines, then override error_exit.
+ info_.err = jpeg_std_error(&err_.pub);
+ err_.pub.error_exit = error_exit;
+
+ // Allocate and initialize JPEG decompression object.
+ jpeg_create_decompress(&info_);
+
+ // Initialize source manager.
+ memset(&src_, 0, sizeof(decoder_source_mgr));
+ info_.src = reinterpret_cast_ptr<jpeg_source_mgr*>(&src_);
+
+ // Set up callback functions.
+ src_.pub.init_source = init_source;
+ src_.pub.fill_input_buffer = fill_input_buffer;
+ src_.pub.skip_input_data = skip_input_data;
+ src_.pub.resync_to_restart = jpeg_resync_to_restart;
+ src_.pub.term_source = term_source;
+ src_.reader = this;
+
+ // Set up a progress monitor.
+ info_.progress = &progress_mgr_;
+ progress_mgr_.progress_monitor = ProgressMonitor;
+
+ // Retain ICC color profile markers for color management.
+ setup_read_icc_profile(&info_);
+
+ // Keep APP1 blocks, for obtaining exif data.
+ jpeg_save_markers(&info_, exifMarker, 0xFFFF);
+ }
+
+ ~JPEGImageReader() { jpeg_destroy_decompress(&info_); }
+
+ void SkipBytes(long num_bytes) {
+ if (num_bytes <= 0)
+ return;
+
+ size_t bytes_to_skip = static_cast<size_t>(num_bytes);
+
+ if (bytes_to_skip < info_.src->bytes_in_buffer) {
+ // The next byte needed is in the buffer. Move to it.
+ info_.src->bytes_in_buffer -= bytes_to_skip;
+ info_.src->next_input_byte += bytes_to_skip;
+ } else {
+ // Move beyond the buffer and empty it.
+ next_read_position_ =
+ next_read_position_ + bytes_to_skip - info_.src->bytes_in_buffer;
+ info_.src->bytes_in_buffer = 0;
+ info_.src->next_input_byte = nullptr;
+ }
+
+ // This is a valid restart position.
+ restart_position_ = next_read_position_ - info_.src->bytes_in_buffer;
+ // We updated |next_input_byte|, so we need to update |last_byte_set_|
+ // so we know not to update |restart_position_| again.
+ last_set_byte_ = info_.src->next_input_byte;
+ }
+
+ bool FillBuffer() {
+ if (needs_restart_) {
+ needs_restart_ = false;
+ next_read_position_ = restart_position_;
+ } else {
+ UpdateRestartPosition();
+ }
+
+ const char* segment;
+ const size_t bytes = data_->GetSomeData(segment, next_read_position_);
+ if (bytes == 0) {
+ // We had to suspend. When we resume, we will need to start from the
+ // restart position.
+ needs_restart_ = true;
+ ClearBuffer();
+ return false;
+ }
+
+ next_read_position_ += bytes;
+ info_.src->bytes_in_buffer = bytes;
+ const JOCTET* next_byte = reinterpret_cast_ptr<const JOCTET*>(segment);
+ info_.src->next_input_byte = next_byte;
+ last_set_byte_ = next_byte;
+ return true;
+ }
+
+ void SetData(SegmentReader* data) {
+ if (data_.get() == data)
+ return;
+
+ data_ = data;
+
+ // If a restart is needed, the next call to fillBuffer will read from the
+ // new SegmentReader.
+ if (needs_restart_)
+ return;
+
+ // Otherwise, empty the buffer, and leave the position the same, so
+ // FillBuffer continues reading from the same position in the new
+ // SegmentReader.
+ next_read_position_ -= info_.src->bytes_in_buffer;
+ ClearBuffer();
+ }
+
+ // Decode the JPEG data. If |only_size| is specified, then only the size
+ // information will be decoded.
+ bool Decode(bool only_size) {
+ // We need to do the setjmp here. Otherwise bad things will happen
+ if (setjmp(err_.setjmp_buffer))
+ return decoder_->SetFailed();
+
+ J_COLOR_SPACE override_color_space = JCS_UNKNOWN;
+ switch (state_) {
+ case JPEG_HEADER: {
+ // Read file parameters with jpeg_read_header().
+ if (jpeg_read_header(&info_, true) == JPEG_SUSPENDED)
+ return false; // I/O suspension.
+
+ switch (info_.jpeg_color_space) {
+ case JCS_YCbCr:
+ // libjpeg can convert YCbCr image pixels to RGB.
+ info_.out_color_space = rgbOutputColorSpace();
+ if (decoder_->HasImagePlanes() &&
+ (YuvSubsampling(info_) != YUV_UNKNOWN))
+ override_color_space = JCS_YCbCr;
+ break;
+ case JCS_GRAYSCALE:
+ case JCS_RGB:
+ // libjpeg can convert GRAYSCALE image pixels to RGB.
+ info_.out_color_space = rgbOutputColorSpace();
+ break;
+ case JCS_CMYK:
+ case JCS_YCCK:
+ // libjpeg can convert YCCK to CMYK, but neither to RGB, so we
+ // manually convert CMKY to RGB.
+ info_.out_color_space = JCS_CMYK;
+ break;
+ default:
+ return decoder_->SetFailed();
+ }
+
+ state_ = JPEG_START_DECOMPRESS;
+
+ // We can fill in the size now that the header is available.
+ if (!decoder_->SetSize(info_.image_width, info_.image_height))
+ return false;
+
+ // Calculate and set decoded size.
+ int max_numerator = decoder_->DesiredScaleNumerator();
+ info_.scale_denom = g_scale_denomiator;
+
+ if (decoder_->ShouldGenerateAllSizes()) {
+ std::vector<SkISize> sizes;
+ sizes.reserve(max_numerator);
+ for (int numerator = 1; numerator <= max_numerator; ++numerator) {
+ info_.scale_num = numerator;
+ jpeg_calc_output_dimensions(&info_);
+ sizes.push_back(
+ SkISize::Make(info_.output_width, info_.output_height));
+ }
+ decoder_->SetSupportedDecodeSizes(std::move(sizes));
+ }
+
+ info_.scale_num = max_numerator;
+ // Scaling caused by running low on memory isn't supported by YUV
+ // decoding since YUV decoding is performed on full sized images. At
+ // this point, buffers and various image info structs have already been
+ // set up for the scaled size after reading the image header using this
+ // decoder, so using the full size is no longer possible.
+ if (info_.scale_num != info_.scale_denom)
+ override_color_space = JCS_UNKNOWN;
+ jpeg_calc_output_dimensions(&info_);
+ decoder_->SetDecodedSize(info_.output_width, info_.output_height);
+
+ decoder_->SetOrientation(ReadImageOrientation(Info()));
+
+ // Allow color management of the decoded RGBA pixels if possible.
+ if (!decoder_->IgnoresColorSpace()) {
+ JOCTET* profile = nullptr;
+ unsigned profile_length = 0;
+ if (read_icc_profile(Info(), &profile, &profile_length)) {
+ sk_sp<SkColorSpace> color_space =
+ SkColorSpace::MakeICC(profile, profile_length);
+ if (color_space) {
+ const SkColorSpace::Type type = color_space->type();
+ switch (info_.jpeg_color_space) {
+ case JCS_CMYK:
+ case JCS_YCCK:
+ if (type != SkColorSpace::kCMYK_Type)
+ color_space = nullptr;
+ break;
+ case JCS_GRAYSCALE:
+ if (type != SkColorSpace::kGray_Type &&
+ type != SkColorSpace::kRGB_Type)
+ color_space = nullptr;
+ break;
+ default:
+ if (type != SkColorSpace::kRGB_Type)
+ color_space = nullptr;
+ break;
+ }
+ Decoder()->SetEmbeddedColorSpace(std::move(color_space));
+ } else {
+ DLOG(ERROR) << "Failed to parse image ICC profile";
+ }
+ free(profile);
+ }
+ if (Decoder()->ColorTransform()) {
+ override_color_space = JCS_UNKNOWN;
+ }
+ }
+ if (override_color_space == JCS_YCbCr) {
+ info_.out_color_space = JCS_YCbCr;
+ info_.raw_data_out = TRUE;
+ uv_size_ = ComputeYUVSize(
+ &info_,
+ 1); // U size and V size have to be the same if we got here
+ }
+
+ // Don't allocate a giant and superfluous memory buffer when the
+ // image is a sequential JPEG.
+ info_.buffered_image = jpeg_has_multiple_scans(&info_);
+ if (info_.buffered_image) {
+ err_.pub.emit_message = emit_message;
+ err_.num_corrupt_warnings = 0;
+ }
+
+ if (only_size) {
+ // This exits the function while there is still potentially
+ // data in the buffer. Before this function is called again,
+ // the SharedBuffer may be collapsed (by a call to
+ // MergeSegmentsIntoBuffer), invalidating the "buffer" (which
+ // in reality is a pointer into the SharedBuffer's data).
+ // Defensively empty the buffer, but first find the latest
+ // restart position and signal to restart, so the next call to
+ // FillBuffer will resume from the correct point.
+ needs_restart_ = true;
+ UpdateRestartPosition();
+ ClearBuffer();
+ return true;
+ }
+ }
+ FALLTHROUGH;
+ case JPEG_START_DECOMPRESS:
+ // Set parameters for decompression.
+ // FIXME -- Should reset dct_method and dither mode for final pass
+ // of progressive JPEG.
+ info_.dct_method = JDCT_ISLOW;
+ info_.dither_mode = JDITHER_FS;
+ info_.do_fancy_upsampling = true;
+ info_.do_block_smoothing = true;
+ info_.enable_2pass_quant = false;
+ // FIXME: should we just assert these?
+ info_.enable_external_quant = false;
+ info_.enable_1pass_quant = false;
+ info_.quantize_colors = false;
+ info_.colormap = nullptr;
+
+ // Make a one-row-high sample array that will go away when done with
+ // image. Always make it big enough to hold one RGBA row. Since this
+ // uses the IJG memory manager, it must be allocated before the call
+ // to jpeg_start_decompress().
+ samples_ = AllocateSampleArray();
+
+ // Start decompressor.
+ if (!jpeg_start_decompress(&info_))
+ return false; // I/O suspension.
+
+ // If this is a progressive JPEG ...
+ state_ = (info_.buffered_image) ? JPEG_DECOMPRESS_PROGRESSIVE
+ : JPEG_DECOMPRESS_SEQUENTIAL;
+ FALLTHROUGH;
+
+ case JPEG_DECOMPRESS_SEQUENTIAL:
+ if (state_ == JPEG_DECOMPRESS_SEQUENTIAL) {
+ if (!decoder_->OutputScanlines())
+ return false; // I/O suspension.
+
+ // If we've completed image output...
+ DCHECK_EQ(info_.output_scanline, info_.output_height);
+ state_ = JPEG_DONE;
+ }
+ FALLTHROUGH;
+
+ case JPEG_DECOMPRESS_PROGRESSIVE:
+ if (state_ == JPEG_DECOMPRESS_PROGRESSIVE) {
+ int status = 0;
+ do {
+ decoder_error_mgr* err =
+ reinterpret_cast_ptr<decoder_error_mgr*>(info_.err);
+ if (err->num_corrupt_warnings)
+ break;
+ status = jpeg_consume_input(&info_);
+ } while ((status != JPEG_SUSPENDED) && (status != JPEG_REACHED_EOI));
+
+ for (;;) {
+ if (!info_.output_scanline) {
+ int scan = info_.input_scan_number;
+
+ // If we haven't displayed anything yet
+ // (output_scan_number == 0) and we have enough data for
+ // a complete scan, force output of the last full scan.
+ if (!info_.output_scan_number && (scan > 1) &&
+ (status != JPEG_REACHED_EOI))
+ --scan;
+
+ if (!jpeg_start_output(&info_, scan))
+ return false; // I/O suspension.
+ }
+
+ if (info_.output_scanline == 0xffffff)
+ info_.output_scanline = 0;
+
+ if (!decoder_->OutputScanlines()) {
+ if (decoder_->Failed())
+ return false;
+ // If no scan lines were read, flag it so we don't call
+ // jpeg_start_output() multiple times for the same scan.
+ if (!info_.output_scanline)
+ info_.output_scanline = 0xffffff;
+
+ return false; // I/O suspension.
+ }
+
+ if (info_.output_scanline == info_.output_height) {
+ if (!jpeg_finish_output(&info_))
+ return false; // I/O suspension.
+
+ if (jpeg_input_complete(&info_) &&
+ (info_.input_scan_number == info_.output_scan_number))
+ break;
+
+ info_.output_scanline = 0;
+ }
+ }
+
+ state_ = JPEG_DONE;
+ }
+ FALLTHROUGH;
+
+ case JPEG_DONE:
+ // Finish decompression.
+ return jpeg_finish_decompress(&info_);
+ }
+
+ return true;
+ }
+
+ jpeg_decompress_struct* Info() { return &info_; }
+ JSAMPARRAY Samples() const { return samples_; }
+ JPEGImageDecoder* Decoder() { return decoder_; }
+ IntSize UvSize() const { return uv_size_; }
+
+ private:
+ JSAMPARRAY AllocateSampleArray() {
+// Some output color spaces don't need the sample array: don't allocate in that
+// case.
+#if defined(TURBO_JPEG_RGB_SWIZZLE)
+ if (turboSwizzled(info_.out_color_space))
+ return nullptr;
+#endif
+
+ if (info_.out_color_space != JCS_YCbCr)
+ return (*info_.mem->alloc_sarray)(
+ reinterpret_cast_ptr<j_common_ptr>(&info_), JPOOL_IMAGE,
+ 4 * info_.output_width, 1);
+
+ // Compute the width of the Y plane in bytes. This may be larger than the
+ // output width, since the jpeg library requires that the allocated width be
+ // a multiple of DCTSIZE. Note that this buffer will be used as garbage
+ // memory for rows that extend below the actual height of the image. We can
+ // reuse the same memory for the U and V planes, since we are guaranteed
+ // that the Y plane width is at least as large as the U and V plane widths.
+ int width_bytes = ComputeYUVWidthBytes(&info_, 0);
+ return (*info_.mem->alloc_sarray)(
+ reinterpret_cast_ptr<j_common_ptr>(&info_), JPOOL_IMAGE, width_bytes,
+ 1);
+ }
+
+ void UpdateRestartPosition() {
+ if (last_set_byte_ != info_.src->next_input_byte) {
+ // next_input_byte was updated by jpeg, meaning that it found a restart
+ // position.
+ restart_position_ = next_read_position_ - info_.src->bytes_in_buffer;
+ }
+ }
+
+ void ClearBuffer() {
+ // Let libjpeg know that the buffer needs to be refilled.
+ info_.src->bytes_in_buffer = 0;
+ info_.src->next_input_byte = nullptr;
+ last_set_byte_ = nullptr;
+ }
+
+ scoped_refptr<SegmentReader> data_;
+ JPEGImageDecoder* decoder_;
+
+ // Input reading: True if we need to back up to restart_position_.
+ bool needs_restart_;
+ // If libjpeg needed to restart, this is the position to restart from.
+ size_t restart_position_;
+ // This is the position where we will read from, unless there is a restart.
+ size_t next_read_position_;
+ // This is how we know to update the restart position. It is the last value
+ // we set to next_input_byte. libjpeg will update next_input_byte when it
+ // has found the next restart position, so if it no longer matches this
+ // value, we know we've reached the next restart position.
+ const JOCTET* last_set_byte_;
+
+ jpeg_decompress_struct info_;
+ decoder_error_mgr err_;
+ decoder_source_mgr src_;
+ jpeg_progress_mgr progress_mgr_;
+ jstate state_;
+
+ JSAMPARRAY samples_;
+ IntSize uv_size_;
+};
+
+void error_exit(
+ j_common_ptr cinfo) // Decoding failed: return control to the setjmp point.
+{
+ longjmp(reinterpret_cast_ptr<decoder_error_mgr*>(cinfo->err)->setjmp_buffer,
+ -1);
+}
+
+void emit_message(j_common_ptr cinfo, int msg_level) {
+ if (msg_level >= 0)
+ return;
+
+ decoder_error_mgr* err = reinterpret_cast_ptr<decoder_error_mgr*>(cinfo->err);
+ err->pub.num_warnings++;
+
+ // Detect and count corrupt JPEG warning messages.
+ const char* warning = nullptr;
+ int code = err->pub.msg_code;
+ if (code > 0 && code <= err->pub.last_jpeg_message)
+ warning = err->pub.jpeg_message_table[code];
+ if (warning && !strncmp("Corrupt JPEG", warning, 12))
+ err->num_corrupt_warnings++;
+}
+
+void init_source(j_decompress_ptr) {}
+
+void skip_input_data(j_decompress_ptr jd, long num_bytes) {
+ reinterpret_cast_ptr<decoder_source_mgr*>(jd->src)->reader->SkipBytes(
+ num_bytes);
+}
+
+boolean fill_input_buffer(j_decompress_ptr jd) {
+ return reinterpret_cast_ptr<decoder_source_mgr*>(jd->src)
+ ->reader->FillBuffer();
+}
+
+void term_source(j_decompress_ptr jd) {
+ reinterpret_cast_ptr<decoder_source_mgr*>(jd->src)
+ ->reader->Decoder()
+ ->Complete();
+}
+
+JPEGImageDecoder::JPEGImageDecoder(AlphaOption alpha_option,
+ const ColorBehavior& color_behavior,
+ size_t max_decoded_bytes)
+ : ImageDecoder(alpha_option, color_behavior, max_decoded_bytes) {}
+
+JPEGImageDecoder::~JPEGImageDecoder() = default;
+
+bool JPEGImageDecoder::SetSize(unsigned width, unsigned height) {
+ if (!ImageDecoder::SetSize(width, height))
+ return false;
+
+ if (!DesiredScaleNumerator())
+ return SetFailed();
+
+ SetDecodedSize(width, height);
+ return true;
+}
+
+void JPEGImageDecoder::OnSetData(SegmentReader* data) {
+ if (reader_)
+ reader_->SetData(data);
+}
+
+void JPEGImageDecoder::SetDecodedSize(unsigned width, unsigned height) {
+ decoded_size_ = IntSize(width, height);
+}
+
+IntSize JPEGImageDecoder::DecodedYUVSize(int component) const {
+ DCHECK_GE(component, 0);
+ DCHECK_LE(component, 2);
+ DCHECK(reader_);
+ const jpeg_decompress_struct* info = reader_->Info();
+
+ DCHECK_EQ(info->out_color_space, JCS_YCbCr);
+ return ComputeYUVSize(info, component);
+}
+
+size_t JPEGImageDecoder::DecodedYUVWidthBytes(int component) const {
+ DCHECK_GE(component, 0);
+ DCHECK_LE(component, 2);
+ DCHECK(reader_);
+ const jpeg_decompress_struct* info = reader_->Info();
+
+ DCHECK_EQ(info->out_color_space, JCS_YCbCr);
+ return ComputeYUVWidthBytes(info, component);
+}
+
+unsigned JPEGImageDecoder::DesiredScaleNumerator() const {
+ size_t original_bytes = Size().Width() * Size().Height() * 4;
+
+ if (original_bytes <= max_decoded_bytes_)
+ return g_scale_denomiator;
+
+ // Downsample according to the maximum decoded size.
+ unsigned scale_numerator = static_cast<unsigned>(floor(sqrt(
+ // MSVC needs explicit parameter type for sqrt().
+ static_cast<float>(max_decoded_bytes_ * g_scale_denomiator *
+ g_scale_denomiator / original_bytes))));
+
+ return scale_numerator;
+}
+
+bool JPEGImageDecoder::ShouldGenerateAllSizes() const {
+ return supported_decode_sizes_.empty();
+}
+
+bool JPEGImageDecoder::CanDecodeToYUV() {
+ // Calling IsSizeAvailable() ensures the reader is created and the output
+ // color space is set.
+ return IsSizeAvailable() && reader_->Info()->out_color_space == JCS_YCbCr;
+}
+
+bool JPEGImageDecoder::DecodeToYUV() {
+ if (!HasImagePlanes())
+ return false;
+
+ PlatformInstrumentation::WillDecodeImage("JPEG");
+ Decode(false);
+ PlatformInstrumentation::DidDecodeImage();
+ return !Failed();
+}
+
+void JPEGImageDecoder::SetImagePlanes(
+ std::unique_ptr<ImagePlanes> image_planes) {
+ image_planes_ = std::move(image_planes);
+}
+
+void JPEGImageDecoder::SetSupportedDecodeSizes(std::vector<SkISize> sizes) {
+ supported_decode_sizes_ = std::move(sizes);
+}
+
+std::vector<SkISize> JPEGImageDecoder::GetSupportedDecodeSizes() const {
+ // DCHECK IsDecodedSizeAvailable instead of IsSizeAvailable, since the latter
+ // has side effects of actually doing the decode.
+ DCHECK(IsDecodedSizeAvailable());
+ return supported_decode_sizes_;
+}
+
+// At the moment we support only JCS_RGB and JCS_CMYK values of the
+// J_COLOR_SPACE enum.
+// If you need a specific implementation for other J_COLOR_SPACE values,
+// please add a full template specialization for this function below.
+template <J_COLOR_SPACE colorSpace>
+void SetPixel(ImageFrame::PixelData*, JSAMPARRAY samples, int column) = delete;
+
+// Used only for debugging with libjpeg (instead of libjpeg-turbo).
+template <>
+void SetPixel<JCS_RGB>(ImageFrame::PixelData* pixel,
+ JSAMPARRAY samples,
+ int column) {
+ JSAMPLE* jsample = *samples + column * 3;
+ ImageFrame::SetRGBARaw(pixel, jsample[0], jsample[1], jsample[2], 255);
+}
+
+template <>
+void SetPixel<JCS_CMYK>(ImageFrame::PixelData* pixel,
+ JSAMPARRAY samples,
+ int column) {
+ JSAMPLE* jsample = *samples + column * 4;
+
+ // Source is 'Inverted CMYK', output is RGB.
+ // See: http://www.easyrgb.com/math.php?MATH=M12#text12
+ // Or: http://www.ilkeratalay.com/colorspacesfaq.php#rgb
+ // From CMYK to CMY:
+ // X = X * (1 - K ) + K [for X = C, M, or Y]
+ // Thus, from Inverted CMYK to CMY is:
+ // X = (1-iX) * (1 - (1-iK)) + (1-iK) => 1 - iX*iK
+ // From CMY (0..1) to RGB (0..1):
+ // R = 1 - C => 1 - (1 - iC*iK) => iC*iK [G and B similar]
+ unsigned k = jsample[3];
+ ImageFrame::SetRGBARaw(pixel, jsample[0] * k / 255, jsample[1] * k / 255,
+ jsample[2] * k / 255, 255);
+}
+
+// Used only for JCS_CMYK and JCS_RGB output. Note that JCS_RGB is used only
+// for debugging with libjpeg (instead of libjpeg-turbo).
+template <J_COLOR_SPACE colorSpace>
+bool OutputRows(JPEGImageReader* reader, ImageFrame& buffer) {
+ JSAMPARRAY samples = reader->Samples();
+ jpeg_decompress_struct* info = reader->Info();
+ int width = info->output_width;
+
+ while (info->output_scanline < info->output_height) {
+ // jpeg_read_scanlines will increase the scanline counter, so we
+ // save the scanline before calling it.
+ int y = info->output_scanline;
+ // Request one scanline: returns 0 or 1 scanlines.
+ if (jpeg_read_scanlines(info, samples, 1) != 1)
+ return false;
+
+ ImageFrame::PixelData* pixel = buffer.GetAddr(0, y);
+ for (int x = 0; x < width; ++pixel, ++x)
+ SetPixel<colorSpace>(pixel, samples, x);
+
+ SkColorSpaceXform* xform = reader->Decoder()->ColorTransform();
+ if (xform) {
+ ImageFrame::PixelData* row = buffer.GetAddr(0, y);
+ bool color_converison_successful =
+ xform->apply(XformColorFormat(), row, XformColorFormat(), row, width,
+ kOpaque_SkAlphaType);
+ DCHECK(color_converison_successful);
+ }
+ }
+
+ buffer.SetPixelsChanged(true);
+ return true;
+}
+
+static bool OutputRawData(JPEGImageReader* reader, ImagePlanes* image_planes) {
+ JSAMPARRAY samples = reader->Samples();
+ jpeg_decompress_struct* info = reader->Info();
+
+ JSAMPARRAY bufferraw[3];
+ JSAMPROW bufferraw2[32];
+ bufferraw[0] = &bufferraw2[0]; // Y channel rows (8 or 16)
+ bufferraw[1] = &bufferraw2[16]; // U channel rows (8)
+ bufferraw[2] = &bufferraw2[24]; // V channel rows (8)
+ int y_height = info->output_height;
+ int v = info->comp_info[0].v_samp_factor;
+ IntSize uv_size = reader->UvSize();
+ int uv_height = uv_size.Height();
+ JSAMPROW output_y = static_cast<JSAMPROW>(image_planes->Plane(0));
+ JSAMPROW output_u = static_cast<JSAMPROW>(image_planes->Plane(1));
+ JSAMPROW output_v = static_cast<JSAMPROW>(image_planes->Plane(2));
+ size_t row_bytes_y = image_planes->RowBytes(0);
+ size_t row_bytes_u = image_planes->RowBytes(1);
+ size_t row_bytes_v = image_planes->RowBytes(2);
+
+ // Request 8 or 16 scanlines: returns 0 or more scanlines.
+ int y_scanlines_to_read = DCTSIZE * v;
+ JSAMPROW dummy_row = *samples;
+ while (info->output_scanline < info->output_height) {
+ // Assign 8 or 16 rows of memory to read the Y channel.
+ for (int i = 0; i < y_scanlines_to_read; ++i) {
+ int scanline = info->output_scanline + i;
+ if (scanline < y_height) {
+ bufferraw2[i] = &output_y[scanline * row_bytes_y];
+ } else {
+ bufferraw2[i] = dummy_row;
+ }
+ }
+
+ // Assign 8 rows of memory to read the U and V channels.
+ int scaled_scanline = info->output_scanline / v;
+ for (int i = 0; i < 8; ++i) {
+ int scanline = scaled_scanline + i;
+ if (scanline < uv_height) {
+ bufferraw2[16 + i] = &output_u[scanline * row_bytes_u];
+ bufferraw2[24 + i] = &output_v[scanline * row_bytes_v];
+ } else {
+ bufferraw2[16 + i] = dummy_row;
+ bufferraw2[24 + i] = dummy_row;
+ }
+ }
+
+ JDIMENSION scanlines_read =
+ jpeg_read_raw_data(info, bufferraw, y_scanlines_to_read);
+ if (!scanlines_read)
+ return false;
+ }
+
+ info->output_scanline = std::min(info->output_scanline, info->output_height);
+ return true;
+}
+
+bool JPEGImageDecoder::OutputScanlines() {
+ if (HasImagePlanes())
+ return OutputRawData(reader_.get(), image_planes_.get());
+
+ if (frame_buffer_cache_.IsEmpty())
+ return false;
+
+ jpeg_decompress_struct* info = reader_->Info();
+
+ // Initialize the framebuffer if needed.
+ ImageFrame& buffer = frame_buffer_cache_[0];
+ if (buffer.GetStatus() == ImageFrame::kFrameEmpty) {
+ DCHECK_EQ(info->output_width,
+ static_cast<JDIMENSION>(decoded_size_.Width()));
+ DCHECK_EQ(info->output_height,
+ static_cast<JDIMENSION>(decoded_size_.Height()));
+
+ if (!buffer.AllocatePixelData(info->output_width, info->output_height,
+ ColorSpaceForSkImages()))
+ return SetFailed();
+
+ buffer.ZeroFillPixelData();
+ // The buffer is transparent outside the decoded area while the image is
+ // loading. The image will be marked fully opaque in Complete().
+ buffer.SetStatus(ImageFrame::kFramePartial);
+ buffer.SetHasAlpha(true);
+
+ // For JPEGs, the frame always fills the entire image.
+ buffer.SetOriginalFrameRect(IntRect(IntPoint(), Size()));
+ }
+
+#if defined(TURBO_JPEG_RGB_SWIZZLE)
+ if (turboSwizzled(info->out_color_space)) {
+ while (info->output_scanline < info->output_height) {
+ unsigned char* row = reinterpret_cast_ptr<unsigned char*>(
+ buffer.GetAddr(0, info->output_scanline));
+ if (jpeg_read_scanlines(info, &row, 1) != 1)
+ return false;
+
+ SkColorSpaceXform* xform = ColorTransform();
+ if (xform) {
+ bool color_converison_successful =
+ xform->apply(XformColorFormat(), row, XformColorFormat(), row,
+ info->output_width, kOpaque_SkAlphaType);
+ DCHECK(color_converison_successful);
+ }
+ }
+ buffer.SetPixelsChanged(true);
+ return true;
+ }
+#endif
+
+ switch (info->out_color_space) {
+ case JCS_RGB:
+ return OutputRows<JCS_RGB>(reader_.get(), buffer);
+ case JCS_CMYK:
+ return OutputRows<JCS_CMYK>(reader_.get(), buffer);
+ default:
+ NOTREACHED();
+ }
+
+ return SetFailed();
+}
+
+void JPEGImageDecoder::Complete() {
+ if (frame_buffer_cache_.IsEmpty())
+ return;
+
+ frame_buffer_cache_[0].SetHasAlpha(false);
+ frame_buffer_cache_[0].SetStatus(ImageFrame::kFrameComplete);
+}
+
+inline bool IsComplete(const JPEGImageDecoder* decoder, bool only_size) {
+ if (decoder->HasImagePlanes() && !only_size)
+ return true;
+
+ return decoder->FrameIsDecodedAtIndex(0);
+}
+
+void JPEGImageDecoder::Decode(bool only_size) {
+ if (Failed())
+ return;
+
+ if (!reader_) {
+ reader_ = std::make_unique<JPEGImageReader>(this);
+ reader_->SetData(data_.get());
+ }
+
+ // If we couldn't decode the image but have received all the data, decoding
+ // has failed.
+ if (!reader_->Decode(only_size) && IsAllDataReceived())
+ SetFailed();
+
+ // If decoding is done or failed, we don't need the JPEGImageReader anymore.
+ if (IsComplete(this, only_size) || Failed())
+ reader_.reset();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.h b/chromium/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.h
new file mode 100644
index 00000000000..b721bd8cec0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2006 Apple Computer, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_JPEG_JPEG_IMAGE_DECODER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_JPEG_JPEG_IMAGE_DECODER_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h"
+
+namespace blink {
+
+class JPEGImageReader;
+
+class PLATFORM_EXPORT JPEGImageDecoder final : public ImageDecoder {
+ WTF_MAKE_NONCOPYABLE(JPEGImageDecoder);
+
+ public:
+ JPEGImageDecoder(AlphaOption, const ColorBehavior&, size_t max_decoded_bytes);
+ ~JPEGImageDecoder() override;
+
+ // ImageDecoder:
+ String FilenameExtension() const override { return "jpg"; }
+ void OnSetData(SegmentReader* data) override;
+ IntSize DecodedSize() const override { return decoded_size_; }
+ bool SetSize(unsigned width, unsigned height) override;
+ IntSize DecodedYUVSize(int component) const override;
+ size_t DecodedYUVWidthBytes(int component) const override;
+ bool CanDecodeToYUV() override;
+ bool DecodeToYUV() override;
+ void SetImagePlanes(std::unique_ptr<ImagePlanes>) override;
+ std::vector<SkISize> GetSupportedDecodeSizes() const override;
+ bool HasImagePlanes() const { return image_planes_.get(); }
+
+ bool OutputScanlines();
+ unsigned DesiredScaleNumerator() const;
+ bool ShouldGenerateAllSizes() const;
+ void Complete();
+
+ void SetOrientation(ImageOrientation orientation) {
+ orientation_ = orientation;
+ }
+ void SetDecodedSize(unsigned width, unsigned height);
+
+ void SetSupportedDecodeSizes(std::vector<SkISize> sizes);
+
+ private:
+ // ImageDecoder:
+ void DecodeSize() override { Decode(true); }
+ void Decode(size_t) override { Decode(false); }
+
+ // Decodes the image. If |only_size| is true, stops decoding after
+ // calculating the image size. If decoding fails but there is no more
+ // data coming, sets the "decode failure" flag.
+ void Decode(bool only_size);
+
+ std::unique_ptr<JPEGImageReader> reader_;
+ std::unique_ptr<ImagePlanes> image_planes_;
+ IntSize decoded_size_;
+ std::vector<SkISize> supported_decode_sizes_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder_test.cc b/chromium/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder_test.cc
new file mode 100644
index 00000000000..bbc8ebf1cfa
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder_test.cc
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.h"
+
+#include <limits>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/web_data.h"
+#include "third_party/blink/public/platform/web_size.h"
+#include "third_party/blink/renderer/platform/image-decoders/image_animation.h"
+#include "third_party/blink/renderer/platform/image-decoders/image_decoder_test_helpers.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer.h"
+
+namespace blink {
+
+static const size_t kLargeEnoughSize = 1000 * 1000;
+
+namespace {
+
+std::unique_ptr<ImageDecoder> CreateJPEGDecoder(size_t max_decoded_bytes) {
+ return std::make_unique<JPEGImageDecoder>(
+ ImageDecoder::kAlphaNotPremultiplied, ColorBehavior::TransformToSRGB(),
+ max_decoded_bytes);
+}
+
+std::unique_ptr<ImageDecoder> CreateJPEGDecoder() {
+ return CreateJPEGDecoder(ImageDecoder::kNoDecodedImageByteLimit);
+}
+
+} // anonymous namespace
+
+void Downsample(size_t max_decoded_bytes,
+ unsigned* output_width,
+ unsigned* output_height,
+ const char* image_file_path) {
+ scoped_refptr<SharedBuffer> data = ReadFile(image_file_path);
+ ASSERT_TRUE(data);
+
+ std::unique_ptr<ImageDecoder> decoder = CreateJPEGDecoder(max_decoded_bytes);
+ decoder->SetData(data.get(), true);
+
+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(0);
+ ASSERT_TRUE(frame);
+ *output_width = frame->Bitmap().width();
+ *output_height = frame->Bitmap().height();
+ EXPECT_EQ(IntSize(*output_width, *output_height), decoder->DecodedSize());
+}
+
+void ReadYUV(size_t max_decoded_bytes,
+ unsigned* output_y_width,
+ unsigned* output_y_height,
+ unsigned* output_uv_width,
+ unsigned* output_uv_height,
+ const char* image_file_path) {
+ scoped_refptr<SharedBuffer> data = ReadFile(image_file_path);
+ ASSERT_TRUE(data);
+
+ std::unique_ptr<ImageDecoder> decoder = CreateJPEGDecoder(max_decoded_bytes);
+ decoder->SetData(data.get(), true);
+
+ // Setting a dummy ImagePlanes object signals to the decoder that we want to
+ // do YUV decoding.
+ std::unique_ptr<ImagePlanes> dummy_image_planes =
+ std::make_unique<ImagePlanes>();
+ decoder->SetImagePlanes(std::move(dummy_image_planes));
+
+ bool size_is_available = decoder->IsSizeAvailable();
+ ASSERT_TRUE(size_is_available);
+
+ IntSize size = decoder->DecodedSize();
+ IntSize y_size = decoder->DecodedYUVSize(0);
+ IntSize u_size = decoder->DecodedYUVSize(1);
+ IntSize v_size = decoder->DecodedYUVSize(2);
+
+ ASSERT_TRUE(size.Width() == y_size.Width());
+ ASSERT_TRUE(size.Height() == y_size.Height());
+ ASSERT_TRUE(u_size.Width() == v_size.Width());
+ ASSERT_TRUE(u_size.Height() == v_size.Height());
+
+ *output_y_width = y_size.Width();
+ *output_y_height = y_size.Height();
+ *output_uv_width = u_size.Width();
+ *output_uv_height = u_size.Height();
+
+ size_t row_bytes[3];
+ row_bytes[0] = decoder->DecodedYUVWidthBytes(0);
+ row_bytes[1] = decoder->DecodedYUVWidthBytes(1);
+ row_bytes[2] = decoder->DecodedYUVWidthBytes(2);
+
+ scoped_refptr<ArrayBuffer> buffer(ArrayBuffer::Create(
+ row_bytes[0] * y_size.Height() + row_bytes[1] * u_size.Height() +
+ row_bytes[2] * v_size.Height(),
+ 1));
+ void* planes[3];
+ planes[0] = buffer->Data();
+ planes[1] = ((char*)planes[0]) + row_bytes[0] * y_size.Height();
+ planes[2] = ((char*)planes[1]) + row_bytes[1] * u_size.Height();
+
+ std::unique_ptr<ImagePlanes> image_planes =
+ std::make_unique<ImagePlanes>(planes, row_bytes);
+ decoder->SetImagePlanes(std::move(image_planes));
+
+ ASSERT_TRUE(decoder->DecodeToYUV());
+}
+
+// Tests failure on a too big image.
+TEST(JPEGImageDecoderTest, tooBig) {
+ std::unique_ptr<ImageDecoder> decoder = CreateJPEGDecoder(100);
+ EXPECT_FALSE(decoder->SetSize(10000, 10000));
+ EXPECT_TRUE(decoder->Failed());
+}
+
+// Tests that the JPEG decoder can downsample image whose width and height are
+// multiples of 8, to ensure we compute the correct DecodedSize and pass correct
+// parameters to libjpeg to output the image with the expected size.
+TEST(JPEGImageDecoderTest, downsampleImageSizeMultipleOf8) {
+ const char* jpeg_file = "/images/resources/lenna.jpg"; // 256x256
+ unsigned output_width, output_height;
+
+ // 1/8 downsample.
+ Downsample(40 * 40 * 4, &output_width, &output_height, jpeg_file);
+ EXPECT_EQ(32u, output_width);
+ EXPECT_EQ(32u, output_height);
+
+ // 2/8 downsample.
+ Downsample(70 * 70 * 4, &output_width, &output_height, jpeg_file);
+ EXPECT_EQ(64u, output_width);
+ EXPECT_EQ(64u, output_height);
+
+ // 3/8 downsample.
+ Downsample(100 * 100 * 4, &output_width, &output_height, jpeg_file);
+ EXPECT_EQ(96u, output_width);
+ EXPECT_EQ(96u, output_height);
+
+ // 4/8 downsample.
+ Downsample(130 * 130 * 4, &output_width, &output_height, jpeg_file);
+ EXPECT_EQ(128u, output_width);
+ EXPECT_EQ(128u, output_height);
+
+ // 5/8 downsample.
+ Downsample(170 * 170 * 4, &output_width, &output_height, jpeg_file);
+ EXPECT_EQ(160u, output_width);
+ EXPECT_EQ(160u, output_height);
+
+ // 6/8 downsample.
+ Downsample(200 * 200 * 4, &output_width, &output_height, jpeg_file);
+ EXPECT_EQ(192u, output_width);
+ EXPECT_EQ(192u, output_height);
+
+ // 7/8 downsample.
+ Downsample(230 * 230 * 4, &output_width, &output_height, jpeg_file);
+ EXPECT_EQ(224u, output_width);
+ EXPECT_EQ(224u, output_height);
+}
+
+// Tests that JPEG decoder can downsample image whose width and height are not
+// multiple of 8. Ensures that we round using the same algorithm as libjpeg.
+TEST(JPEGImageDecoderTest, downsampleImageSizeNotMultipleOf8) {
+ const char* jpeg_file = "/images/resources/icc-v2-gbr.jpg"; // 275x207
+ unsigned output_width, output_height;
+
+ // 1/8 downsample.
+ Downsample(40 * 40 * 4, &output_width, &output_height, jpeg_file);
+ EXPECT_EQ(35u, output_width);
+ EXPECT_EQ(26u, output_height);
+
+ // 2/8 downsample.
+ Downsample(70 * 70 * 4, &output_width, &output_height, jpeg_file);
+ EXPECT_EQ(69u, output_width);
+ EXPECT_EQ(52u, output_height);
+
+ // 3/8 downsample.
+ Downsample(100 * 100 * 4, &output_width, &output_height, jpeg_file);
+ EXPECT_EQ(104u, output_width);
+ EXPECT_EQ(78u, output_height);
+
+ // 4/8 downsample.
+ Downsample(130 * 130 * 4, &output_width, &output_height, jpeg_file);
+ EXPECT_EQ(138u, output_width);
+ EXPECT_EQ(104u, output_height);
+
+ // 5/8 downsample.
+ Downsample(170 * 170 * 4, &output_width, &output_height, jpeg_file);
+ EXPECT_EQ(172u, output_width);
+ EXPECT_EQ(130u, output_height);
+
+ // 6/8 downsample.
+ Downsample(200 * 200 * 4, &output_width, &output_height, jpeg_file);
+ EXPECT_EQ(207u, output_width);
+ EXPECT_EQ(156u, output_height);
+
+ // 7/8 downsample.
+ Downsample(230 * 230 * 4, &output_width, &output_height, jpeg_file);
+ EXPECT_EQ(241u, output_width);
+ EXPECT_EQ(182u, output_height);
+}
+
+// Tests that upsampling is not allowed.
+TEST(JPEGImageDecoderTest, upsample) {
+ const char* jpeg_file = "/images/resources/lenna.jpg"; // 256x256
+ unsigned output_width, output_height;
+ Downsample(kLargeEnoughSize, &output_width, &output_height, jpeg_file);
+ EXPECT_EQ(256u, output_width);
+ EXPECT_EQ(256u, output_height);
+}
+
+TEST(JPEGImageDecoderTest, yuv) {
+ const char* jpeg_file = "/images/resources/lenna.jpg"; // 256x256, YUV 4:2:0
+ unsigned output_y_width, output_y_height, output_uv_width, output_uv_height;
+ ReadYUV(kLargeEnoughSize, &output_y_width, &output_y_height, &output_uv_width,
+ &output_uv_height, jpeg_file);
+ EXPECT_EQ(256u, output_y_width);
+ EXPECT_EQ(256u, output_y_height);
+ EXPECT_EQ(128u, output_uv_width);
+ EXPECT_EQ(128u, output_uv_height);
+
+ const char* jpeg_file_image_size_not_multiple_of8 =
+ "/images/resources/cropped_mandrill.jpg"; // 439x154
+ ReadYUV(kLargeEnoughSize, &output_y_width, &output_y_height, &output_uv_width,
+ &output_uv_height, jpeg_file_image_size_not_multiple_of8);
+ EXPECT_EQ(439u, output_y_width);
+ EXPECT_EQ(154u, output_y_height);
+ EXPECT_EQ(220u, output_uv_width);
+ EXPECT_EQ(77u, output_uv_height);
+
+ // Make sure we revert to RGBA decoding when we're about to downscale,
+ // which can occur on memory-constrained android devices.
+ scoped_refptr<SharedBuffer> data = ReadFile(jpeg_file);
+ ASSERT_TRUE(data);
+
+ std::unique_ptr<ImageDecoder> decoder = CreateJPEGDecoder(230 * 230 * 4);
+ decoder->SetData(data.get(), true);
+
+ std::unique_ptr<ImagePlanes> image_planes = std::make_unique<ImagePlanes>();
+ decoder->SetImagePlanes(std::move(image_planes));
+ ASSERT_TRUE(decoder->IsSizeAvailable());
+ ASSERT_FALSE(decoder->CanDecodeToYUV());
+}
+
+TEST(JPEGImageDecoderTest,
+ byteByByteBaselineJPEGWithColorProfileAndRestartMarkers) {
+ TestByteByByteDecode(&CreateJPEGDecoder,
+ "/images/resources/"
+ "small-square-with-colorspin-profile.jpg",
+ 1u, kAnimationNone);
+}
+
+TEST(JPEGImageDecoderTest, byteByByteProgressiveJPEG) {
+ TestByteByByteDecode(&CreateJPEGDecoder, "/images/resources/bug106024.jpg",
+ 1u, kAnimationNone);
+}
+
+TEST(JPEGImageDecoderTest, byteByByteRGBJPEGWithAdobeMarkers) {
+ TestByteByByteDecode(&CreateJPEGDecoder,
+ "/images/resources/rgb-jpeg-with-adobe-marker-only.jpg",
+ 1u, kAnimationNone);
+}
+
+// This test verifies that calling SharedBuffer::MergeSegmentsIntoBuffer() does
+// not break JPEG decoding at a critical point: in between a call to decode the
+// size (when JPEGImageDecoder stops while it may still have input data to
+// read) and a call to do a full decode.
+TEST(JPEGImageDecoderTest, mergeBuffer) {
+ const char* jpeg_file = "/images/resources/lenna.jpg";
+ TestMergeBuffer(&CreateJPEGDecoder, jpeg_file);
+}
+
+// This tests decoding a JPEG with many progressive scans. Decoding should
+// fail, but not hang (crbug.com/642462).
+TEST(JPEGImageDecoderTest, manyProgressiveScans) {
+ scoped_refptr<SharedBuffer> test_data =
+ ReadFile(kDecodersTestingDir, "many-progressive-scans.jpg");
+ ASSERT_TRUE(test_data.get());
+
+ std::unique_ptr<ImageDecoder> test_decoder = CreateJPEGDecoder();
+ test_decoder->SetData(test_data.get(), true);
+ EXPECT_EQ(1u, test_decoder->FrameCount());
+ ASSERT_TRUE(test_decoder->DecodeFrameBufferAtIndex(0));
+ EXPECT_TRUE(test_decoder->Failed());
+}
+
+TEST(JPEGImageDecoderTest, SupportedSizesSquare) {
+ const char* jpeg_file = "/images/resources/lenna.jpg"; // 256x256
+ scoped_refptr<SharedBuffer> data = ReadFile(jpeg_file);
+ ASSERT_TRUE(data);
+
+ std::unique_ptr<ImageDecoder> decoder =
+ CreateJPEGDecoder(std::numeric_limits<int>::max());
+ decoder->SetData(data.get(), true);
+ // This will decode the size and needs to be called to avoid DCHECKs
+ ASSERT_TRUE(decoder->IsSizeAvailable());
+ std::vector<SkISize> expected_sizes = {
+ SkISize::Make(32, 32), SkISize::Make(64, 64), SkISize::Make(96, 96),
+ SkISize::Make(128, 128), SkISize::Make(160, 160), SkISize::Make(192, 192),
+ SkISize::Make(224, 224), SkISize::Make(256, 256)};
+ auto sizes = decoder->GetSupportedDecodeSizes();
+ ASSERT_EQ(expected_sizes.size(), sizes.size());
+ for (size_t i = 0; i < sizes.size(); ++i) {
+ EXPECT_TRUE(expected_sizes[i] == sizes[i])
+ << "Expected " << expected_sizes[i].width() << "x"
+ << expected_sizes[i].height() << ". Got " << sizes[i].width() << "x"
+ << sizes[i].height();
+ }
+}
+
+TEST(JPEGImageDecoderTest, SupportedSizesRectangle) {
+ const char* jpeg_file = "/images/resources/icc-v2-gbr.jpg"; // 275x207
+
+ scoped_refptr<SharedBuffer> data = ReadFile(jpeg_file);
+ ASSERT_TRUE(data);
+
+ std::unique_ptr<ImageDecoder> decoder =
+ CreateJPEGDecoder(std::numeric_limits<int>::max());
+ decoder->SetData(data.get(), true);
+ // This will decode the size and needs to be called to avoid DCHECKs
+ ASSERT_TRUE(decoder->IsSizeAvailable());
+ std::vector<SkISize> expected_sizes = {
+ SkISize::Make(35, 26), SkISize::Make(69, 52), SkISize::Make(104, 78),
+ SkISize::Make(138, 104), SkISize::Make(172, 130), SkISize::Make(207, 156),
+ SkISize::Make(241, 182), SkISize::Make(275, 207)};
+
+ auto sizes = decoder->GetSupportedDecodeSizes();
+ ASSERT_EQ(expected_sizes.size(), sizes.size());
+ for (size_t i = 0; i < sizes.size(); ++i) {
+ EXPECT_TRUE(expected_sizes[i] == sizes[i])
+ << "Expected " << expected_sizes[i].width() << "x"
+ << expected_sizes[i].height() << ". Got " << sizes[i].width() << "x"
+ << sizes[i].height();
+ }
+}
+
+TEST(JPEGImageDecoderTest, SupportedSizesTruncatedIfMemoryBound) {
+ const char* jpeg_file = "/images/resources/lenna.jpg"; // 256x256
+ scoped_refptr<SharedBuffer> data = ReadFile(jpeg_file);
+ ASSERT_TRUE(data);
+
+ // Limit the memory so that 128 would be the largest size possible.
+ std::unique_ptr<ImageDecoder> decoder = CreateJPEGDecoder(130 * 130 * 4);
+ decoder->SetData(data.get(), true);
+ // This will decode the size and needs to be called to avoid DCHECKs
+ ASSERT_TRUE(decoder->IsSizeAvailable());
+ std::vector<SkISize> expected_sizes = {
+ SkISize::Make(32, 32), SkISize::Make(64, 64), SkISize::Make(96, 96),
+ SkISize::Make(128, 128)};
+ auto sizes = decoder->GetSupportedDecodeSizes();
+ ASSERT_EQ(expected_sizes.size(), sizes.size());
+ for (size_t i = 0; i < sizes.size(); ++i) {
+ EXPECT_TRUE(expected_sizes[i] == sizes[i])
+ << "Expected " << expected_sizes[i].width() << "x"
+ << expected_sizes[i].height() << ". Got " << sizes[i].width() << "x"
+ << sizes[i].height();
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder.cc b/chromium/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder.cc
new file mode 100644
index 00000000000..cad2f68db51
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder.cc
@@ -0,0 +1,706 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc.
+ * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
+ *
+ * Portions are Copyright (C) 2001 mozilla.org
+ *
+ * Other contributors:
+ * Stuart Parmenter <stuart@mozilla.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Alternatively, the contents of this file may be used under the terms
+ * of either the Mozilla Public License Version 1.1, found at
+ * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
+ * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
+ * (the "GPL"), in which case the provisions of the MPL or the GPL are
+ * applicable instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of one of those two
+ * licenses (the MPL or the GPL) and not to allow others to use your
+ * version of this file under the LGPL, indicate your decision by
+ * deletingthe provisions above and replace them with the notice and
+ * other provisions required by the MPL or the GPL, as the case may be.
+ * If you do not delete the provisions above, a recipient may use your
+ * version of this file under any of the LGPL, the MPL or the GPL.
+ */
+
+#include "third_party/blink/renderer/platform/image-decoders/png/png_image_decoder.h"
+
+#include <memory>
+
+#if (defined(__ARM_NEON__) || defined(__ARM_NEON))
+#include <arm_neon.h>
+#endif
+
+namespace blink {
+
+PNGImageDecoder::PNGImageDecoder(AlphaOption alpha_option,
+ const ColorBehavior& color_behavior,
+ size_t max_decoded_bytes,
+ size_t offset)
+ : ImageDecoder(alpha_option, color_behavior, max_decoded_bytes),
+ offset_(offset),
+ current_frame_(0),
+ // It would be logical to default to kAnimationNone, but BitmapImage uses
+ // that as a signal to never check again, meaning the actual count will
+ // never be respected.
+ repetition_count_(kAnimationLoopOnce),
+ has_alpha_channel_(false),
+ current_buffer_saw_alpha_(false) {}
+
+PNGImageDecoder::~PNGImageDecoder() = default;
+
+bool PNGImageDecoder::SetFailed() {
+ reader_.reset();
+ return ImageDecoder::SetFailed();
+}
+
+size_t PNGImageDecoder::DecodeFrameCount() {
+ Parse(ParseQuery::kMetaData);
+ return Failed() ? frame_buffer_cache_.size() : reader_->FrameCount();
+}
+
+void PNGImageDecoder::Decode(size_t index) {
+ Parse(ParseQuery::kMetaData);
+
+ if (Failed())
+ return;
+
+ UpdateAggressivePurging(index);
+
+ Vector<size_t> frames_to_decode = FindFramesToDecode(index);
+ for (auto i = frames_to_decode.rbegin(); i != frames_to_decode.rend(); i++) {
+ current_frame_ = *i;
+ if (!reader_->Decode(*data_, *i)) {
+ SetFailed();
+ return;
+ }
+
+ // If this returns false, we need more data to continue decoding.
+ if (!PostDecodeProcessing(*i))
+ break;
+ }
+
+ // It is also a fatal error if all data is received and we have decoded all
+ // frames available but the file is truncated.
+ if (index >= frame_buffer_cache_.size() - 1 && IsAllDataReceived() &&
+ reader_ && !reader_->ParseCompleted())
+ SetFailed();
+}
+
+void PNGImageDecoder::Parse(ParseQuery query) {
+ if (Failed() || (reader_ && reader_->ParseCompleted()))
+ return;
+
+ if (!reader_)
+ reader_ = std::make_unique<PNGImageReader>(this, offset_);
+
+ if (!reader_->Parse(*data_, query))
+ SetFailed();
+}
+
+void PNGImageDecoder::ClearFrameBuffer(size_t index) {
+ if (reader_)
+ reader_->ClearDecodeState(index);
+ ImageDecoder::ClearFrameBuffer(index);
+}
+
+bool PNGImageDecoder::CanReusePreviousFrameBuffer(size_t index) const {
+ DCHECK(index < frame_buffer_cache_.size());
+ return frame_buffer_cache_[index].GetDisposalMethod() !=
+ ImageFrame::kDisposeOverwritePrevious;
+}
+
+void PNGImageDecoder::SetRepetitionCount(int repetition_count) {
+ repetition_count_ = repetition_count;
+}
+
+int PNGImageDecoder::RepetitionCount() const {
+ return Failed() ? kAnimationLoopOnce : repetition_count_;
+}
+
+void PNGImageDecoder::InitializeNewFrame(size_t index) {
+ const PNGImageReader::FrameInfo& frame_info = reader_->GetFrameInfo(index);
+ ImageFrame& buffer = frame_buffer_cache_[index];
+
+ DCHECK(IntRect(IntPoint(), Size()).Contains(frame_info.frame_rect));
+ buffer.SetOriginalFrameRect(frame_info.frame_rect);
+
+ buffer.SetDuration(TimeDelta::FromMilliseconds(frame_info.duration));
+ buffer.SetDisposalMethod(frame_info.disposal_method);
+ buffer.SetAlphaBlendSource(frame_info.alpha_blend);
+
+ size_t previous_frame_index = FindRequiredPreviousFrame(index, false);
+ buffer.SetRequiredPreviousFrameIndex(previous_frame_index);
+}
+
+inline sk_sp<SkColorSpace> ReadColorSpace(png_structp png, png_infop info) {
+ if (png_get_valid(png, info, PNG_INFO_sRGB))
+ return SkColorSpace::MakeSRGB();
+
+ png_charp name;
+ int compression;
+ png_bytep profile;
+ png_uint_32 length;
+ if (png_get_iCCP(png, info, &name, &compression, &profile, &length))
+ return SkColorSpace::MakeICC(profile, length);
+
+ png_fixed_point chrm[8];
+ if (!png_get_cHRM_fixed(png, info, &chrm[0], &chrm[1], &chrm[2], &chrm[3],
+ &chrm[4], &chrm[5], &chrm[6], &chrm[7]))
+ return nullptr;
+
+ png_fixed_point inverse_gamma;
+ if (!png_get_gAMA_fixed(png, info, &inverse_gamma))
+ return nullptr;
+
+ // cHRM and gAMA tags are both present. The PNG spec states that cHRM is
+ // valid even without gAMA but we cannot apply the cHRM without guessing
+ // a gAMA. Color correction is not a guessing game: match the behavior
+ // of Safari and Firefox instead (compat).
+
+ struct pngFixedToFloat {
+ explicit pngFixedToFloat(png_fixed_point value)
+ : float_value(.00001f * value) {}
+ operator float() { return float_value; }
+ float float_value;
+ };
+
+ SkColorSpacePrimaries primaries;
+ primaries.fRX = pngFixedToFloat(chrm[2]);
+ primaries.fRY = pngFixedToFloat(chrm[3]);
+ primaries.fGX = pngFixedToFloat(chrm[4]);
+ primaries.fGY = pngFixedToFloat(chrm[5]);
+ primaries.fBX = pngFixedToFloat(chrm[6]);
+ primaries.fBY = pngFixedToFloat(chrm[7]);
+ primaries.fWX = pngFixedToFloat(chrm[0]);
+ primaries.fWY = pngFixedToFloat(chrm[1]);
+
+ SkMatrix44 to_xyzd50(SkMatrix44::kUninitialized_Constructor);
+ if (!primaries.toXYZD50(&to_xyzd50))
+ return nullptr;
+
+ SkColorSpaceTransferFn fn;
+ fn.fG = 1.0f / pngFixedToFloat(inverse_gamma);
+ fn.fA = 1.0f;
+ fn.fB = fn.fC = fn.fD = fn.fE = fn.fF = 0.0f;
+
+ return SkColorSpace::MakeRGB(fn, to_xyzd50);
+}
+
+void PNGImageDecoder::SetColorSpace() {
+ if (IgnoresColorSpace())
+ return;
+ png_structp png = reader_->PngPtr();
+ png_infop info = reader_->InfoPtr();
+ const int color_type = png_get_color_type(png, info);
+ if (!(color_type & PNG_COLOR_MASK_COLOR))
+ return;
+ // We only support color profiles for color PALETTE and RGB[A] PNG.
+ // TODO(msarett): Add GRAY profile support, block CYMK?
+ sk_sp<SkColorSpace> color_space = ReadColorSpace(png, info);
+ if (color_space)
+ SetEmbeddedColorSpace(color_space);
+}
+
+bool PNGImageDecoder::SetSize(unsigned width, unsigned height) {
+ DCHECK(!IsDecodedSizeAvailable());
+ // Protect against large PNGs. See http://bugzil.la/251381 for more details.
+ const unsigned long kMaxPNGSize = 1000000UL;
+ return (width <= kMaxPNGSize) && (height <= kMaxPNGSize) &&
+ ImageDecoder::SetSize(width, height);
+}
+
+void PNGImageDecoder::HeaderAvailable() {
+ DCHECK(IsDecodedSizeAvailable());
+
+ png_structp png = reader_->PngPtr();
+ png_infop info = reader_->InfoPtr();
+
+ png_uint_32 width, height;
+ int bit_depth, color_type, interlace_type, compression_type;
+ png_get_IHDR(png, info, &width, &height, &bit_depth, &color_type,
+ &interlace_type, &compression_type, nullptr);
+
+ // The options we set here match what Mozilla does.
+
+ // 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);
+
+ if (png_get_valid(png, info, PNG_INFO_tRNS))
+ png_set_expand(png);
+
+ if (bit_depth == 16)
+ png_set_strip_16(png);
+
+ if (color_type == PNG_COLOR_TYPE_GRAY ||
+ color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+ png_set_gray_to_rgb(png);
+
+ if (!HasEmbeddedColorSpace()) {
+ const double kInverseGamma = 0.45455;
+ const double kDefaultGamma = 2.2;
+ double gamma;
+ if (!IgnoresColorSpace() && png_get_gAMA(png, info, &gamma)) {
+ const double kMaxGamma = 21474.83;
+ if ((gamma <= 0.0) || (gamma > kMaxGamma)) {
+ gamma = kInverseGamma;
+ png_set_gAMA(png, info, gamma);
+ }
+ png_set_gamma(png, kDefaultGamma, gamma);
+ } else {
+ png_set_gamma(png, kDefaultGamma, kInverseGamma);
+ }
+ }
+
+ // Tell libpng to send us rows for interlaced pngs.
+ if (interlace_type == PNG_INTERLACE_ADAM7)
+ png_set_interlace_handling(png);
+
+ // Update our info now (so we can get color channel info).
+ png_read_update_info(png, info);
+
+ int channels = png_get_channels(png, info);
+ DCHECK(channels == 3 || channels == 4);
+ has_alpha_channel_ = (channels == 4);
+}
+
+#if (defined(__ARM_NEON__) || defined(__ARM_NEON))
+// Premultiply RGB color channels by alpha, swizzle RGBA to SkPMColor
+// order, and return the AND of all alpha channels.
+static inline void SetRGBAPremultiplyRowNeon(png_bytep src_ptr,
+ const int pixel_count,
+ ImageFrame::PixelData* dst_pixel,
+ unsigned* const alpha_mask) {
+ assert(dst_pixel);
+ assert(alpha_mask);
+
+ constexpr int kPixelsPerLoad = 8;
+ // Input registers.
+ uint8x8x4_t rgba;
+ // Alpha mask.
+ uint8x8_t alpha_mask_vector = vdup_n_u8(255);
+
+ // Scale the color channel by alpha - the opacity coefficient.
+ auto premultiply = [](uint8x8_t c, uint8x8_t a) {
+ // First multiply the color by alpha, expanding to 16-bit (max 255*255).
+ uint16x8_t ca = vmull_u8(c, a);
+ // Now we need to round back down to 8-bit, returning (x+127)/255.
+ // (x+127)/255 == (x + ((x+128)>>8) + 128)>>8. This form is well suited
+ // to NEON: vrshrq_n_u16(...,8) gives the inner (x+128)>>8, and
+ // vraddhn_u16() both the outer add-shift and our conversion back to 8-bit.
+ return vraddhn_u16(ca, vrshrq_n_u16(ca, 8));
+ };
+
+ int i = pixel_count;
+ for (; i >= kPixelsPerLoad; i -= kPixelsPerLoad) {
+ // Reads 8 pixels at once, each color channel in a different
+ // 64-bit register.
+ rgba = vld4_u8(src_ptr);
+ // AND pixel alpha values into the alpha detection mask.
+ alpha_mask_vector = vand_u8(alpha_mask_vector, rgba.val[3]);
+
+ uint64_t alphas_u64 = vget_lane_u64(vreinterpret_u64_u8(rgba.val[3]), 0);
+
+ // If all of the pixels are opaque, no need to premultiply.
+ if (~alphas_u64 == 0) {
+#if SK_PMCOLOR_BYTE_ORDER(R, G, B, A)
+ // Already in right order, write back (interleaved) results to memory.
+ vst4_u8(reinterpret_cast<uint8_t*>(dst_pixel), rgba);
+
+#elif SK_PMCOLOR_BYTE_ORDER(B, G, R, A)
+ // Re-order color channels for BGRA.
+ uint8x8x4_t bgra = {rgba.val[2], rgba.val[1], rgba.val[0], rgba.val[3]};
+ // Write back (interleaved) results to memory.
+ vst4_u8(reinterpret_cast<uint8_t*>(dst_pixel), bgra);
+
+#endif
+
+ } else {
+#if SK_PMCOLOR_BYTE_ORDER(R, G, B, A)
+ // Premultiply color channels, already in right order.
+ rgba.val[0] = premultiply(rgba.val[0], rgba.val[3]);
+ rgba.val[1] = premultiply(rgba.val[1], rgba.val[3]);
+ rgba.val[2] = premultiply(rgba.val[2], rgba.val[3]);
+ // Write back (interleaved) results to memory.
+ vst4_u8(reinterpret_cast<uint8_t*>(dst_pixel), rgba);
+
+#elif SK_PMCOLOR_BYTE_ORDER(B, G, R, A)
+ uint8x8x4_t bgra;
+ // Premultiply and re-order color channels for BGRA.
+ bgra.val[0] = premultiply(rgba.val[2], rgba.val[3]);
+ bgra.val[1] = premultiply(rgba.val[1], rgba.val[3]);
+ bgra.val[2] = premultiply(rgba.val[0], rgba.val[3]);
+ bgra.val[3] = rgba.val[3];
+ // Write back (interleaved) results to memory.
+ vst4_u8(reinterpret_cast<uint8_t*>(dst_pixel), bgra);
+
+#endif
+ }
+
+ // Advance to next elements.
+ src_ptr += kPixelsPerLoad * 4;
+ dst_pixel += kPixelsPerLoad;
+ }
+
+ // AND together the 8 alpha values in the alpha_mask_vector.
+ uint64_t alpha_mask_u64 =
+ vget_lane_u64(vreinterpret_u64_u8(alpha_mask_vector), 0);
+ alpha_mask_u64 &= (alpha_mask_u64 >> 32);
+ alpha_mask_u64 &= (alpha_mask_u64 >> 16);
+ alpha_mask_u64 &= (alpha_mask_u64 >> 8);
+ *alpha_mask &= alpha_mask_u64;
+
+ // Handle the tail elements.
+ for (; i > 0; i--, dst_pixel++, src_ptr += 4) {
+ ImageFrame::SetRGBAPremultiply(dst_pixel, src_ptr[0], src_ptr[1],
+ src_ptr[2], src_ptr[3]);
+ *alpha_mask &= src_ptr[3];
+ }
+}
+
+// Swizzle RGBA to SkPMColor order, and return the AND of all alpha channels.
+static inline void SetRGBARawRowNeon(png_bytep src_ptr,
+ const int pixel_count,
+ ImageFrame::PixelData* dst_pixel,
+ unsigned* const alpha_mask) {
+ assert(dst_pixel);
+ assert(alpha_mask);
+
+ constexpr int kPixelsPerLoad = 16;
+ // Input registers.
+ uint8x16x4_t rgba;
+ // Alpha mask.
+ uint8x16_t alpha_mask_vector = vdupq_n_u8(255);
+
+ int i = pixel_count;
+ for (; i >= kPixelsPerLoad; i -= kPixelsPerLoad) {
+ // Reads 16 pixels at once, each color channel in a different
+ // 128-bit register.
+ rgba = vld4q_u8(src_ptr);
+ // AND pixel alpha values into the alpha detection mask.
+ alpha_mask_vector = vandq_u8(alpha_mask_vector, rgba.val[3]);
+
+#if SK_PMCOLOR_BYTE_ORDER(R, G, B, A)
+ // Already in right order, write back (interleaved) results to memory.
+ vst4q_u8(reinterpret_cast<uint8_t*>(dst_pixel), rgba);
+
+#elif SK_PMCOLOR_BYTE_ORDER(B, G, R, A)
+ // Re-order color channels for BGRA.
+ uint8x16x4_t bgra = {rgba.val[2], rgba.val[1], rgba.val[0], rgba.val[3]};
+ // Write back (interleaved) results to memory.
+ vst4q_u8(reinterpret_cast<uint8_t*>(dst_pixel), bgra);
+
+#endif
+
+ // Advance to next elements.
+ src_ptr += kPixelsPerLoad * 4;
+ dst_pixel += kPixelsPerLoad;
+ }
+
+ // AND together the 16 alpha values in the alpha_mask_vector.
+ uint64_t alpha_mask_u64 =
+ vget_lane_u64(vreinterpret_u64_u8(vget_low_u8(alpha_mask_vector)), 0);
+ alpha_mask_u64 &=
+ vget_lane_u64(vreinterpret_u64_u8(vget_high_u8(alpha_mask_vector)), 0);
+ alpha_mask_u64 &= (alpha_mask_u64 >> 32);
+ alpha_mask_u64 &= (alpha_mask_u64 >> 16);
+ alpha_mask_u64 &= (alpha_mask_u64 >> 8);
+ *alpha_mask &= alpha_mask_u64;
+
+ // Handle the tail elements.
+ for (; i > 0; i--, dst_pixel++, src_ptr += 4) {
+ ImageFrame::SetRGBARaw(dst_pixel, src_ptr[0], src_ptr[1], src_ptr[2],
+ src_ptr[3]);
+ *alpha_mask &= src_ptr[3];
+ }
+}
+
+// Swizzle RGB to opaque SkPMColor order, and return the AND
+// of all alpha channels.
+static inline void SetRGBARawRowNoAlphaNeon(png_bytep src_ptr,
+ const int pixel_count,
+ ImageFrame::PixelData* dst_pixel) {
+ assert(dst_pixel);
+
+ constexpr int kPixelsPerLoad = 16;
+ // Input registers.
+ uint8x16x3_t rgb;
+
+ int i = pixel_count;
+ for (; i >= kPixelsPerLoad; i -= kPixelsPerLoad) {
+ // Reads 16 pixels at once, each color channel in a different
+ // 128-bit register.
+ rgb = vld3q_u8(src_ptr);
+
+#if SK_PMCOLOR_BYTE_ORDER(R, G, B, A)
+ // RGB already in right order, add opaque alpha channel.
+ uint8x16x4_t rgba = {rgb.val[0], rgb.val[1], rgb.val[2], vdupq_n_u8(255)};
+ // Write back (interleaved) results to memory.
+ vst4q_u8(reinterpret_cast<uint8_t*>(dst_pixel), rgba);
+
+#elif SK_PMCOLOR_BYTE_ORDER(B, G, R, A)
+ // Re-order color channels for BGR, add opaque alpha channel.
+ uint8x16x4_t bgra = {rgb.val[2], rgb.val[1], rgb.val[0], vdupq_n_u8(255)};
+ // Write back (interleaved) results to memory.
+ vst4q_u8(reinterpret_cast<uint8_t*>(dst_pixel), bgra);
+
+#endif
+
+ // Advance to next elements.
+ src_ptr += kPixelsPerLoad * 3;
+ dst_pixel += kPixelsPerLoad;
+ }
+
+ // Handle the tail elements.
+ for (; i > 0; i--, dst_pixel++, src_ptr += 3) {
+ ImageFrame::SetRGBARaw(dst_pixel, src_ptr[0], src_ptr[1], src_ptr[2], 255);
+ }
+}
+#endif
+
+void PNGImageDecoder::RowAvailable(unsigned char* row_buffer,
+ unsigned row_index,
+ int) {
+ if (current_frame_ >= frame_buffer_cache_.size())
+ return;
+
+ ImageFrame& buffer = frame_buffer_cache_[current_frame_];
+ if (buffer.GetStatus() == ImageFrame::kFrameEmpty) {
+ png_structp png = reader_->PngPtr();
+ if (!InitFrameBuffer(current_frame_)) {
+ longjmp(JMPBUF(png), 1);
+ return;
+ }
+
+ DCHECK_EQ(ImageFrame::kFramePartial, buffer.GetStatus());
+
+ if (PNG_INTERLACE_ADAM7 ==
+ png_get_interlace_type(png, reader_->InfoPtr())) {
+ unsigned color_channels = has_alpha_channel_ ? 4 : 3;
+ reader_->CreateInterlaceBuffer(color_channels * Size().Area());
+ if (!reader_->InterlaceBuffer()) {
+ longjmp(JMPBUF(png), 1);
+ return;
+ }
+ }
+
+ current_buffer_saw_alpha_ = false;
+ }
+
+ const IntRect& frame_rect = buffer.OriginalFrameRect();
+ DCHECK(IntRect(IntPoint(), Size()).Contains(frame_rect));
+
+ /* libpng comments (here to explain what follows).
+ *
+ * this function is called for every row in the image. If the
+ * image is interlacing, and you turned on the interlace handler,
+ * this function will be called for every row in every pass.
+ * Some of these rows will not be changed from the previous pass.
+ * When the row is not changed, the new_row variable will be NULL.
+ * The rows and passes are called in order, so you don't really
+ * need the row_num and pass, but I'm supplying them because it
+ * may make your life easier.
+ */
+
+ // Nothing to do if the row is unchanged, or the row is outside the image
+ // bounds. In the case that a frame presents more data than the indicated
+ // frame size, ignore the extra rows and use the frame size as the source
+ // of truth. libpng can send extra rows: ignore them too, this to prevent
+ // memory writes outside of the image bounds (security).
+ if (!row_buffer)
+ return;
+
+ DCHECK_GT(frame_rect.Height(), 0);
+ if (row_index >= static_cast<unsigned>(frame_rect.Height()))
+ return;
+
+ int y = row_index + frame_rect.Y();
+ if (y < 0)
+ return;
+ DCHECK_LT(y, Size().Height());
+
+ /* libpng comments (continued).
+ *
+ * For the non-NULL rows of interlaced images, you must call
+ * png_progressive_combine_row() passing in the row and the
+ * old row. You can call this function for NULL rows (it will
+ * just return) and for non-interlaced images (it just does the
+ * memcpy for you) if it will make the code easier. Thus, you
+ * can just do this for all cases:
+ *
+ * png_progressive_combine_row(png_ptr, old_row, new_row);
+ *
+ * where old_row is what was displayed for previous rows. Note
+ * that the first pass (pass == 0 really) will completely cover
+ * the old row, so the rows do not have to be initialized. After
+ * the first pass (and only for interlaced images), you will have
+ * to pass the current row, and the function will combine the
+ * old row and the new row.
+ */
+
+ bool has_alpha = has_alpha_channel_;
+ png_bytep row = row_buffer;
+
+ if (png_bytep interlace_buffer = reader_->InterlaceBuffer()) {
+ unsigned color_channels = has_alpha ? 4 : 3;
+ row = interlace_buffer + (row_index * color_channels * Size().Width());
+ png_progressive_combine_row(reader_->PngPtr(), row, row_buffer);
+ }
+
+ // Write the decoded row pixels to the frame buffer. The repetitive
+ // form of the row write loops is for speed.
+ ImageFrame::PixelData* const dst_row = buffer.GetAddr(frame_rect.X(), y);
+ const int width = frame_rect.Width();
+
+ png_bytep src_ptr = row;
+ if (has_alpha) {
+ // Here we apply the color space transformation to the dst space.
+ // It does not really make sense to transform to a gamma-encoded
+ // space and then immediately after, perform a linear premultiply.
+ // Ideally we would pass kPremul_SkAlphaType to xform->apply(),
+ // instructing SkColorSpaceXform to perform the linear premultiply
+ // while the pixels are a linear space.
+ // We cannot do this because when we apply the gamma encoding after
+ // the premultiply, we will very likely end up with valid pixels
+ // where R, G, and/or B are greater than A. The legacy drawing
+ // pipeline does not know how to handle this.
+ if (SkColorSpaceXform* xform = ColorTransform()) {
+ SkColorSpaceXform::ColorFormat color_format =
+ SkColorSpaceXform::kRGBA_8888_ColorFormat;
+ bool color_converison_successful =
+ xform->apply(color_format, dst_row, color_format, src_ptr, width,
+ kUnpremul_SkAlphaType);
+ DCHECK(color_converison_successful);
+ src_ptr = png_bytep(dst_row);
+ }
+
+ unsigned alpha_mask = 255;
+ if (frame_buffer_cache_[current_frame_].GetAlphaBlendSource() ==
+ ImageFrame::kBlendAtopBgcolor) {
+ if (buffer.PremultiplyAlpha()) {
+#if (defined(__ARM_NEON__) || defined(__ARM_NEON))
+ SetRGBAPremultiplyRowNeon(src_ptr, width, dst_row, &alpha_mask);
+#else
+ for (auto *dst_pixel = dst_row; dst_pixel < dst_row + width;
+ dst_pixel++, src_ptr += 4) {
+ ImageFrame::SetRGBAPremultiply(dst_pixel, src_ptr[0], src_ptr[1],
+ src_ptr[2], src_ptr[3]);
+ alpha_mask &= src_ptr[3];
+ }
+#endif
+ } else {
+#if (defined(__ARM_NEON__) || defined(__ARM_NEON))
+ SetRGBARawRowNeon(src_ptr, width, dst_row, &alpha_mask);
+#else
+ for (auto *dst_pixel = dst_row; dst_pixel < dst_row + width;
+ dst_pixel++, src_ptr += 4) {
+ ImageFrame::SetRGBARaw(dst_pixel, src_ptr[0], src_ptr[1], src_ptr[2],
+ src_ptr[3]);
+ alpha_mask &= src_ptr[3];
+ }
+#endif
+ }
+ } else {
+ // Now, the blend method is ImageFrame::BlendAtopPreviousFrame. Since the
+ // frame data of the previous frame is copied at InitFrameBuffer, we can
+ // blend the pixel of this frame, stored in |src_ptr|, over the previous
+ // pixel stored in |dst_pixel|.
+ if (buffer.PremultiplyAlpha()) {
+ for (auto *dst_pixel = dst_row; dst_pixel < dst_row + width;
+ dst_pixel++, src_ptr += 4) {
+ ImageFrame::BlendRGBAPremultiplied(dst_pixel, src_ptr[0], src_ptr[1],
+ src_ptr[2], src_ptr[3]);
+ alpha_mask &= src_ptr[3];
+ }
+ } else {
+ for (auto *dst_pixel = dst_row; dst_pixel < dst_row + width;
+ dst_pixel++, src_ptr += 4) {
+ ImageFrame::BlendRGBARaw(dst_pixel, src_ptr[0], src_ptr[1],
+ src_ptr[2], src_ptr[3]);
+ alpha_mask &= src_ptr[3];
+ }
+ }
+ }
+
+ if (alpha_mask != 255)
+ current_buffer_saw_alpha_ = true;
+
+ } else {
+#if (defined(__ARM_NEON__) || defined(__ARM_NEON))
+ SetRGBARawRowNoAlphaNeon(src_ptr, width, dst_row);
+#else
+ for (auto *dst_pixel = dst_row; dst_pixel < dst_row + width;
+ src_ptr += 3, ++dst_pixel) {
+ ImageFrame::SetRGBARaw(dst_pixel, src_ptr[0], src_ptr[1], src_ptr[2],
+ 255);
+ }
+#endif
+ // We'll apply the color space xform to opaque pixels after they have been
+ // written to the ImageFrame, purely because SkColorSpaceXform supports
+ // RGBA (and not RGB).
+ if (SkColorSpaceXform* xform = ColorTransform()) {
+ bool color_converison_successful =
+ xform->apply(XformColorFormat(), dst_row, XformColorFormat(), dst_row,
+ width, kOpaque_SkAlphaType);
+ DCHECK(color_converison_successful);
+ }
+ }
+
+ buffer.SetPixelsChanged(true);
+}
+
+void PNGImageDecoder::FrameComplete() {
+ if (current_frame_ >= frame_buffer_cache_.size())
+ return;
+
+ if (reader_->InterlaceBuffer())
+ reader_->ClearInterlaceBuffer();
+
+ ImageFrame& buffer = frame_buffer_cache_[current_frame_];
+ if (buffer.GetStatus() == ImageFrame::kFrameEmpty) {
+ longjmp(JMPBUF(reader_->PngPtr()), 1);
+ return;
+ }
+
+ if (!current_buffer_saw_alpha_)
+ CorrectAlphaWhenFrameBufferSawNoAlpha(current_frame_);
+
+ buffer.SetStatus(ImageFrame::kFrameComplete);
+}
+
+bool PNGImageDecoder::FrameIsReceivedAtIndex(size_t index) const {
+ if (!IsDecodedSizeAvailable())
+ return false;
+
+ DCHECK(!Failed() && reader_);
+
+ // For non-animated images, return ImageDecoder::FrameIsReceivedAtIndex.
+ // This matches the behavior of WEBPImageDecoder.
+ if (reader_->ParseCompleted() && reader_->FrameCount() == 1)
+ return ImageDecoder::FrameIsReceivedAtIndex(index);
+
+ return reader_->FrameIsReceivedAtIndex(index);
+}
+
+TimeDelta PNGImageDecoder::FrameDurationAtIndex(size_t index) const {
+ if (index < frame_buffer_cache_.size())
+ return frame_buffer_cache_[index].Duration();
+ return TimeDelta();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder.h b/chromium/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder.h
new file mode 100644
index 00000000000..dc39091673b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2006 Apple Computer, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_PNG_PNG_IMAGE_DECODER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_PNG_PNG_IMAGE_DECODER_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h"
+#include "third_party/blink/renderer/platform/image-decoders/png/png_image_reader.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT PNGImageDecoder final : public ImageDecoder {
+ WTF_MAKE_NONCOPYABLE(PNGImageDecoder);
+
+ public:
+ PNGImageDecoder(AlphaOption,
+ const ColorBehavior&,
+ size_t max_decoded_bytes,
+ size_t offset = 0);
+ ~PNGImageDecoder() override;
+
+ // ImageDecoder:
+ String FilenameExtension() const override { return "png"; }
+ bool SetSize(unsigned, unsigned) override;
+ int RepetitionCount() const override;
+ bool FrameIsReceivedAtIndex(size_t) const override;
+ TimeDelta FrameDurationAtIndex(size_t) const override;
+ bool SetFailed() override;
+
+ // Callbacks from libpng
+ void HeaderAvailable();
+ void RowAvailable(unsigned char* row, unsigned row_index, int);
+ void FrameComplete();
+
+ void SetColorSpace();
+ void SetRepetitionCount(int);
+
+ private:
+ using ParseQuery = PNGImageReader::ParseQuery;
+
+ // ImageDecoder:
+ void DecodeSize() override { Parse(ParseQuery::kSize); }
+ void Decode(size_t) override;
+ void Parse(ParseQuery);
+ size_t DecodeFrameCount() override;
+ void InitializeNewFrame(size_t) override;
+ void ClearFrameBuffer(size_t) override;
+ bool CanReusePreviousFrameBuffer(size_t) const override;
+
+ std::unique_ptr<PNGImageReader> reader_;
+ const unsigned offset_;
+ size_t current_frame_;
+ int repetition_count_;
+ bool has_alpha_channel_;
+ bool current_buffer_saw_alpha_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder_test.cc b/chromium/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder_test.cc
new file mode 100644
index 00000000000..00daa8a2d8f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder_test.cc
@@ -0,0 +1,1084 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/image-decoders/png/png_image_decoder.h"
+
+#include <memory>
+#include "png.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/image-decoders/image_decoder_test_helpers.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+// /LayoutTests/images/resources/png-animated-idat-part-of-animation.png
+// is modified in multiple tests to simulate erroneous PNGs. As a reference,
+// the table below shows how the file is structured.
+//
+// Offset | 8 33 95 133 172 210 241 279 314 352 422
+// -------------------------------------------------------------------------
+// Chunk | IHDR acTL fcTL IDAT fcTL fdAT fcTL fdAT fcTL fdAT IEND
+//
+// In between the acTL and fcTL there are two other chunks, PLTE and tRNS, but
+// those are not specifically used in this test suite. The same holds for a
+// tEXT chunk in between the last fdAT and IEND.
+//
+// In the current behavior of PNG image decoders, the 4 frames are detected when
+// respectively 141, 249, 322 and 430 bytes are received. The first frame should
+// be detected when the IDAT has been received, and non-first frames when the
+// next fcTL or IEND chunk has been received. Note that all offsets are +8,
+// because a chunk is identified by byte 4-7.
+
+namespace blink {
+
+namespace {
+
+std::unique_ptr<ImageDecoder> CreatePNGDecoder(
+ ImageDecoder::AlphaOption alpha_option) {
+ return std::make_unique<PNGImageDecoder>(
+ alpha_option, ColorBehavior::TransformToSRGB(),
+ ImageDecoder::kNoDecodedImageByteLimit);
+}
+
+std::unique_ptr<ImageDecoder> CreatePNGDecoder() {
+ return CreatePNGDecoder(ImageDecoder::kAlphaNotPremultiplied);
+}
+
+std::unique_ptr<ImageDecoder> CreatePNGDecoderWithPngData(
+ const char* png_file) {
+ auto decoder = CreatePNGDecoder();
+ scoped_refptr<SharedBuffer> data = ReadFile(png_file);
+ EXPECT_FALSE(data->IsEmpty());
+ decoder->SetData(data.get(), true);
+ return decoder;
+}
+
+void TestSize(const char* png_file, IntSize expected_size) {
+ auto decoder = CreatePNGDecoderWithPngData(png_file);
+ EXPECT_TRUE(decoder->IsSizeAvailable());
+ EXPECT_EQ(expected_size, decoder->Size());
+}
+
+// Test whether querying for the size of the image works if we present the
+// data byte by byte.
+void TestSizeByteByByte(const char* png_file,
+ size_t bytes_needed_to_decode_size,
+ IntSize expected_size) {
+ auto decoder = CreatePNGDecoder();
+ scoped_refptr<SharedBuffer> data = ReadFile(png_file);
+ ASSERT_FALSE(data->IsEmpty());
+ ASSERT_LT(bytes_needed_to_decode_size, data->size());
+
+ const char* source = data->Data();
+ scoped_refptr<SharedBuffer> partial_data = SharedBuffer::Create();
+ for (size_t length = 1; length <= bytes_needed_to_decode_size; length++) {
+ partial_data->Append(source++, 1u);
+ decoder->SetData(partial_data.get(), false);
+
+ if (length < bytes_needed_to_decode_size) {
+ EXPECT_FALSE(decoder->IsSizeAvailable());
+ EXPECT_TRUE(decoder->Size().IsEmpty());
+ EXPECT_FALSE(decoder->Failed());
+ } else {
+ EXPECT_TRUE(decoder->IsSizeAvailable());
+ EXPECT_EQ(expected_size, decoder->Size());
+ }
+ }
+ EXPECT_FALSE(decoder->Failed());
+}
+
+void WriteUint32(uint32_t val, png_byte* data) {
+ data[0] = val >> 24;
+ data[1] = val >> 16;
+ data[2] = val >> 8;
+ data[3] = val;
+}
+
+void TestRepetitionCount(const char* png_file, int expected_repetition_count) {
+ auto decoder = CreatePNGDecoderWithPngData(png_file);
+ // Decoding the frame count sets the number of repetitions as well.
+ decoder->FrameCount();
+ EXPECT_FALSE(decoder->Failed());
+ EXPECT_EQ(expected_repetition_count, decoder->RepetitionCount());
+}
+
+struct PublicFrameInfo {
+ TimeDelta duration;
+ IntRect frame_rect;
+ ImageFrame::AlphaBlendSource alpha_blend;
+ ImageFrame::DisposalMethod disposal_method;
+};
+
+// This is the frame data for the following PNG image:
+// /LayoutTests/images/resources/png-animated-idat-part-of-animation.png
+static PublicFrameInfo g_png_animated_frame_info[] = {
+ {TimeDelta::FromMilliseconds(500),
+ {IntPoint(0, 0), IntSize(5, 5)},
+ ImageFrame::kBlendAtopBgcolor,
+ ImageFrame::kDisposeKeep},
+ {TimeDelta::FromMilliseconds(900),
+ {IntPoint(1, 1), IntSize(3, 1)},
+ ImageFrame::kBlendAtopBgcolor,
+ ImageFrame::kDisposeOverwriteBgcolor},
+ {TimeDelta::FromMilliseconds(2000),
+ {IntPoint(1, 2), IntSize(3, 2)},
+ ImageFrame::kBlendAtopPreviousFrame,
+ ImageFrame::kDisposeKeep},
+ {TimeDelta::FromMilliseconds(1500),
+ {IntPoint(1, 2), IntSize(3, 1)},
+ ImageFrame::kBlendAtopBgcolor,
+ ImageFrame::kDisposeKeep},
+};
+
+void CompareFrameWithExpectation(const PublicFrameInfo& expected,
+ ImageDecoder* decoder,
+ size_t index) {
+ EXPECT_EQ(expected.duration, decoder->FrameDurationAtIndex(index));
+
+ const auto* frame = decoder->DecodeFrameBufferAtIndex(index);
+ ASSERT_TRUE(frame);
+
+ EXPECT_EQ(expected.duration, frame->Duration());
+ EXPECT_EQ(expected.frame_rect, frame->OriginalFrameRect());
+ EXPECT_EQ(expected.disposal_method, frame->GetDisposalMethod());
+ EXPECT_EQ(expected.alpha_blend, frame->GetAlphaBlendSource());
+}
+
+// This function removes |length| bytes at |offset|, and then calls FrameCount.
+// It assumes the missing bytes should result in a failed decode because the
+// parser jumps |length| bytes too far in the next chunk.
+void TestMissingDataBreaksDecoding(const char* png_file,
+ size_t offset,
+ size_t length) {
+ auto decoder = CreatePNGDecoder();
+ scoped_refptr<SharedBuffer> data = ReadFile(png_file);
+ ASSERT_FALSE(data->IsEmpty());
+
+ scoped_refptr<SharedBuffer> invalid_data =
+ SharedBuffer::Create(data->Data(), offset);
+ invalid_data->Append(data->Data() + offset + length,
+ data->size() - offset - length);
+ ASSERT_EQ(data->size() - length, invalid_data->size());
+
+ decoder->SetData(invalid_data, true);
+ decoder->FrameCount();
+ EXPECT_TRUE(decoder->Failed());
+}
+
+// Verify that a decoder with a parse error converts to a static image.
+static void ExpectStatic(ImageDecoder* decoder) {
+ EXPECT_EQ(1u, decoder->FrameCount());
+ EXPECT_FALSE(decoder->Failed());
+
+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(0);
+ ASSERT_NE(nullptr, frame);
+ EXPECT_EQ(ImageFrame::kFrameComplete, frame->GetStatus());
+ EXPECT_FALSE(decoder->Failed());
+ EXPECT_EQ(kAnimationNone, decoder->RepetitionCount());
+}
+
+// Decode up to the indicated fcTL offset and then provide an fcTL with the
+// wrong chunk size (20 instead of 26).
+void TestInvalidFctlSize(const char* png_file,
+ size_t offset_fctl,
+ size_t expected_frame_count,
+ bool should_fail) {
+ scoped_refptr<SharedBuffer> data = ReadFile(png_file);
+ ASSERT_FALSE(data->IsEmpty());
+
+ auto decoder = CreatePNGDecoder();
+ scoped_refptr<SharedBuffer> invalid_data =
+ SharedBuffer::Create(data->Data(), offset_fctl);
+
+ // Test if this gives the correct frame count, before the fcTL is parsed.
+ decoder->SetData(invalid_data, false);
+ EXPECT_EQ(expected_frame_count, decoder->FrameCount());
+ ASSERT_FALSE(decoder->Failed());
+
+ // Append the wrong size to the data stream
+ png_byte size_chunk[4];
+ WriteUint32(20, size_chunk);
+ invalid_data->Append(reinterpret_cast<char*>(size_chunk), 4u);
+
+ // Skip the size in the original data, but provide a truncated fcTL,
+ // which is 4B of tag, 20B of data and 4B of CRC, totalling 28B.
+ invalid_data->Append(data->Data() + offset_fctl + 4, 28u);
+ // Append the rest of the data
+ const size_t offset_post_fctl = offset_fctl + 38;
+ invalid_data->Append(data->Data() + offset_post_fctl,
+ data->size() - offset_post_fctl);
+
+ decoder->SetData(invalid_data, false);
+ if (should_fail) {
+ EXPECT_EQ(expected_frame_count, decoder->FrameCount());
+ EXPECT_EQ(true, decoder->Failed());
+ } else {
+ ExpectStatic(decoder.get());
+ }
+}
+
+// Verify that the decoder can successfully decode the first frame when
+// initially only half of the frame data is received, resulting in a partially
+// decoded image, and then the rest of the image data is received. Verify that
+// the bitmap hashes of the two stages are different. Also verify that the final
+// bitmap hash is equivalent to the hash when all data is provided at once.
+//
+// This verifies that the decoder correctly keeps track of where it stopped
+// decoding when the image was not yet fully received.
+void TestProgressiveDecodingContinuesAfterFullData(
+ const char* png_file,
+ size_t offset_mid_first_frame) {
+ scoped_refptr<SharedBuffer> full_data = ReadFile(png_file);
+ ASSERT_FALSE(full_data->IsEmpty());
+
+ auto decoder_upfront = CreatePNGDecoder();
+ decoder_upfront->SetData(full_data.get(), true);
+ EXPECT_GE(decoder_upfront->FrameCount(), 1u);
+ const ImageFrame* const frame_upfront =
+ decoder_upfront->DecodeFrameBufferAtIndex(0);
+ ASSERT_EQ(ImageFrame::kFrameComplete, frame_upfront->GetStatus());
+ const unsigned hash_upfront = HashBitmap(frame_upfront->Bitmap());
+
+ auto decoder = CreatePNGDecoder();
+ scoped_refptr<SharedBuffer> partial_data =
+ SharedBuffer::Create(full_data->Data(), offset_mid_first_frame);
+ decoder->SetData(partial_data, false);
+
+ EXPECT_EQ(1u, decoder->FrameCount());
+ const ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(0);
+ EXPECT_EQ(frame->GetStatus(), ImageFrame::kFramePartial);
+ const unsigned hash_partial = HashBitmap(frame->Bitmap());
+
+ decoder->SetData(full_data.get(), true);
+ frame = decoder->DecodeFrameBufferAtIndex(0);
+ EXPECT_EQ(frame->GetStatus(), ImageFrame::kFrameComplete);
+ const unsigned hash_full = HashBitmap(frame->Bitmap());
+
+ EXPECT_FALSE(decoder->Failed());
+ EXPECT_NE(hash_full, hash_partial);
+ EXPECT_EQ(hash_full, hash_upfront);
+}
+
+} // Anonymous namespace
+
+// Animated PNG Tests
+
+TEST(AnimatedPNGTests, sizeTest) {
+ TestSize(
+ "/images/resources/"
+ "png-animated-idat-part-of-animation.png",
+ IntSize(5, 5));
+ TestSize(
+ "/images/resources/"
+ "png-animated-idat-not-part-of-animation.png",
+ IntSize(227, 35));
+}
+
+TEST(AnimatedPNGTests, repetitionCountTest) {
+ TestRepetitionCount(
+ "/images/resources/"
+ "png-animated-idat-part-of-animation.png",
+ 6u);
+ // This is an "animated" image with only one frame, that is, the IDAT is
+ // ignored and there is one fdAT frame. so it should be considered
+ // non-animated.
+ TestRepetitionCount(
+ "/images/resources/"
+ "png-animated-idat-not-part-of-animation.png",
+ kAnimationNone);
+}
+
+// Test if the decoded metdata corresponds to the defined expectations
+TEST(AnimatedPNGTests, MetaDataTest) {
+ const char* png_file =
+ "/images/resources/"
+ "png-animated-idat-part-of-animation.png";
+ constexpr size_t kExpectedFrameCount = 4;
+
+ auto decoder = CreatePNGDecoderWithPngData(png_file);
+ ASSERT_EQ(kExpectedFrameCount, decoder->FrameCount());
+ for (size_t i = 0; i < kExpectedFrameCount; i++) {
+ CompareFrameWithExpectation(g_png_animated_frame_info[i], decoder.get(), i);
+ }
+}
+
+TEST(AnimatedPNGTests, EmptyFrame) {
+ const char* png_file = "/images/resources/empty-frame.png";
+ auto decoder = CreatePNGDecoderWithPngData(png_file);
+ // Frame 0 is empty. Ensure that decoding frame 1 (which depends on frame 0)
+ // fails (rather than crashing).
+ EXPECT_EQ(2u, decoder->FrameCount());
+ EXPECT_FALSE(decoder->Failed());
+
+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(1);
+ EXPECT_TRUE(decoder->Failed());
+ ASSERT_NE(nullptr, frame);
+ EXPECT_EQ(ImageFrame::kFrameEmpty, frame->GetStatus());
+}
+
+TEST(AnimatedPNGTests, ByteByByteSizeAvailable) {
+ TestSizeByteByByte(
+ "/images/resources/"
+ "png-animated-idat-part-of-animation.png",
+ 141u, IntSize(5, 5));
+ TestSizeByteByByte(
+ "/images/resources/"
+ "png-animated-idat-not-part-of-animation.png",
+ 79u, IntSize(227, 35));
+}
+
+TEST(AnimatedPNGTests, ByteByByteMetaData) {
+ const char* png_file =
+ "/images/resources/"
+ "png-animated-idat-part-of-animation.png";
+ constexpr size_t kExpectedFrameCount = 4;
+
+ // These are the byte offsets where each frame should have been parsed.
+ // It boils down to the offset of the first fcTL / IEND after the last
+ // frame data chunk, plus 8 bytes for recognition. The exception on this is
+ // the first frame, which is reported when its first framedata is seen.
+ size_t frame_offsets[kExpectedFrameCount] = {141, 249, 322, 430};
+
+ auto decoder = CreatePNGDecoder();
+ scoped_refptr<SharedBuffer> data = ReadFile(png_file);
+ ASSERT_FALSE(data->IsEmpty());
+ size_t frames_parsed = 0;
+
+ const char* source = data->Data();
+ scoped_refptr<SharedBuffer> partial_data = SharedBuffer::Create();
+ for (size_t length = 1; length <= frame_offsets[kExpectedFrameCount - 1];
+ length++) {
+ partial_data->Append(source++, 1u);
+ decoder->SetData(partial_data.get(), false);
+ EXPECT_FALSE(decoder->Failed());
+ if (length < frame_offsets[frames_parsed]) {
+ EXPECT_EQ(frames_parsed, decoder->FrameCount());
+ } else {
+ ASSERT_EQ(frames_parsed + 1, decoder->FrameCount());
+ CompareFrameWithExpectation(g_png_animated_frame_info[frames_parsed],
+ decoder.get(), frames_parsed);
+ frames_parsed++;
+ }
+ }
+ EXPECT_EQ(kExpectedFrameCount, decoder->FrameCount());
+ EXPECT_FALSE(decoder->Failed());
+}
+
+TEST(AnimatedPNGTests, TestRandomFrameDecode) {
+ TestRandomFrameDecode(&CreatePNGDecoder,
+ "/images/resources/"
+ "png-animated-idat-part-of-animation.png",
+ 2u);
+}
+
+TEST(AnimatedPNGTests, TestDecodeAfterReallocation) {
+ TestDecodeAfterReallocatingData(&CreatePNGDecoder,
+ "/images/resources/"
+ "png-animated-idat-part-of-animation.png");
+}
+
+TEST(AnimatedPNGTests, ProgressiveDecode) {
+ TestProgressiveDecoding(&CreatePNGDecoder,
+ "/images/resources/"
+ "png-animated-idat-part-of-animation.png",
+ 13u);
+}
+
+TEST(AnimatedPNGTests, ParseAndDecodeByteByByte) {
+ TestByteByByteDecode(&CreatePNGDecoder,
+ "/images/resources/"
+ "png-animated-idat-part-of-animation.png",
+ 4u, 6u);
+}
+
+TEST(AnimatedPNGTests, FailureDuringParsing) {
+ // Test the first fcTL in the stream. Because no frame data has been set at
+ // this point, the expected frame count is zero. 95 bytes is just before the
+ // first fcTL chunk, at which the first frame is detected. This is before the
+ // IDAT, so it should be treated as a static image.
+ TestInvalidFctlSize(
+ "/images/resources/"
+ "png-animated-idat-part-of-animation.png",
+ 95u, 0u, false);
+
+ // Test for the third fcTL in the stream. This should see 1 frame before the
+ // fcTL, and then fail when parsing it.
+ TestInvalidFctlSize(
+ "/images/resources/"
+ "png-animated-idat-part-of-animation.png",
+ 241u, 1u, true);
+}
+
+TEST(AnimatedPNGTests, ActlErrors) {
+ const char* png_file =
+ "/images/resources/"
+ "png-animated-idat-part-of-animation.png";
+ scoped_refptr<SharedBuffer> data = ReadFile(png_file);
+ ASSERT_FALSE(data->IsEmpty());
+
+ const size_t kOffsetActl = 33u;
+ const size_t kAcTLSize = 20u;
+ {
+ // Remove the acTL chunk from the stream. This results in a static image.
+ scoped_refptr<SharedBuffer> no_actl_data =
+ SharedBuffer::Create(data->Data(), kOffsetActl);
+ no_actl_data->Append(data->Data() + kOffsetActl + kAcTLSize,
+ data->size() - kOffsetActl - kAcTLSize);
+
+ auto decoder = CreatePNGDecoder();
+ decoder->SetData(no_actl_data, true);
+ EXPECT_EQ(1u, decoder->FrameCount());
+ EXPECT_FALSE(decoder->Failed());
+ EXPECT_EQ(kAnimationNone, decoder->RepetitionCount());
+ }
+
+ // Store the acTL for more tests.
+ char ac_tl[kAcTLSize];
+ memcpy(ac_tl, data->Data() + kOffsetActl, kAcTLSize);
+
+ // Insert an extra acTL at a couple of different offsets.
+ // Prior to the IDAT, this should result in a static image. After, this
+ // should fail.
+ const struct {
+ size_t offset;
+ bool should_fail;
+ } kGRecs[] = {{8u, false},
+ {kOffsetActl, false},
+ {133u, false},
+ {172u, true},
+ {422u, true}};
+ for (const auto& rec : kGRecs) {
+ const size_t offset = rec.offset;
+ scoped_refptr<SharedBuffer> extra_actl_data =
+ SharedBuffer::Create(data->Data(), offset);
+ extra_actl_data->Append(ac_tl, kAcTLSize);
+ extra_actl_data->Append(data->Data() + offset, data->size() - offset);
+ auto decoder = CreatePNGDecoder();
+ decoder->SetData(extra_actl_data, true);
+ EXPECT_EQ(rec.should_fail ? 0u : 1u, decoder->FrameCount());
+ EXPECT_EQ(rec.should_fail, decoder->Failed());
+ }
+
+ // An acTL after IDAT is ignored.
+ png_file =
+ "/images/resources/"
+ "cHRM_color_spin.png";
+ {
+ scoped_refptr<SharedBuffer> data2 = ReadFile(png_file);
+ ASSERT_FALSE(data2->IsEmpty());
+ const size_t kPostIDATOffset = 30971u;
+ for (size_t times = 0; times < 2; times++) {
+ scoped_refptr<SharedBuffer> extra_actl_data =
+ SharedBuffer::Create(data2->Data(), kPostIDATOffset);
+ for (size_t i = 0; i < times; i++)
+ extra_actl_data->Append(ac_tl, kAcTLSize);
+ extra_actl_data->Append(data2->Data() + kPostIDATOffset,
+ data2->size() - kPostIDATOffset);
+
+ auto decoder = CreatePNGDecoder();
+ decoder->SetData(extra_actl_data, true);
+ EXPECT_EQ(1u, decoder->FrameCount());
+ EXPECT_FALSE(decoder->Failed());
+ EXPECT_EQ(kAnimationNone, decoder->RepetitionCount());
+ EXPECT_NE(nullptr, decoder->DecodeFrameBufferAtIndex(0));
+ EXPECT_FALSE(decoder->Failed());
+ }
+ }
+}
+
+TEST(AnimatedPNGTests, fdatBeforeIdat) {
+ const char* png_file =
+ "/images/resources/"
+ "png-animated-idat-not-part-of-animation.png";
+ scoped_refptr<SharedBuffer> data = ReadFile(png_file);
+ ASSERT_FALSE(data->IsEmpty());
+
+ // Insert fcTL and fdAT prior to the IDAT
+ const size_t kIdatOffset = 71u;
+ scoped_refptr<SharedBuffer> modified_data =
+ SharedBuffer::Create(data->Data(), kIdatOffset);
+ // Copy fcTL and fdAT
+ const size_t kFctlPlusFdatSize = 38u + 1566u;
+ modified_data->Append(data->Data() + 2519u, kFctlPlusFdatSize);
+ // Copy IDAT
+ modified_data->Append(data->Data() + kIdatOffset, 2448u);
+ // Copy the remaining
+ modified_data->Append(data->Data() + 4123u, 39u + 12u);
+ // Data has just been rearranged.
+ ASSERT_EQ(data->size(), modified_data->size());
+
+ {
+ // This broken APNG will be treated as a static png.
+ auto decoder = CreatePNGDecoder();
+ decoder->SetData(modified_data.get(), true);
+ ExpectStatic(decoder.get());
+ }
+
+ {
+ // Remove the acTL from the modified image. It now has fdAT before
+ // IDAT, but no acTL, so fdAT should be ignored.
+ const size_t kOffsetActl = 33u;
+ const size_t kAcTLSize = 20u;
+ scoped_refptr<SharedBuffer> modified_data2 =
+ SharedBuffer::Create(modified_data->Data(), kOffsetActl);
+ modified_data2->Append(modified_data->Data() + kOffsetActl + kAcTLSize,
+ modified_data->size() - kOffsetActl - kAcTLSize);
+ auto decoder = CreatePNGDecoder();
+ decoder->SetData(modified_data2.get(), true);
+ ExpectStatic(decoder.get());
+
+ // Likewise, if an acTL follows the fdAT, it is ignored.
+ const size_t kInsertionOffset = kIdatOffset + kFctlPlusFdatSize - kAcTLSize;
+ scoped_refptr<SharedBuffer> modified_data3 =
+ SharedBuffer::Create(modified_data2->Data(), kInsertionOffset);
+ modified_data3->Append(data->Data() + kOffsetActl, kAcTLSize);
+ modified_data3->Append(modified_data2->Data() + kInsertionOffset,
+ modified_data2->size() - kInsertionOffset);
+ decoder = CreatePNGDecoder();
+ decoder->SetData(modified_data3.get(), true);
+ ExpectStatic(decoder.get());
+ }
+}
+
+TEST(AnimatedPNGTests, IdatSizeMismatch) {
+ // The default image must fill the image
+ const char* png_file =
+ "/images/resources/"
+ "png-animated-idat-part-of-animation.png";
+ scoped_refptr<SharedBuffer> data = ReadFile(png_file);
+ ASSERT_FALSE(data->IsEmpty());
+
+ const size_t kFctlOffset = 95u;
+ scoped_refptr<SharedBuffer> modified_data =
+ SharedBuffer::Create(data->Data(), kFctlOffset);
+ const size_t kFctlSize = 38u;
+ png_byte fctl[kFctlSize];
+ memcpy(fctl, data->Data() + kFctlOffset, kFctlSize);
+ // Set the height to a smaller value, so it does not fill the image.
+ WriteUint32(3, fctl + 16);
+ // Correct the crc
+ WriteUint32(3210324191, fctl + 34);
+ modified_data->Append((const char*)fctl, kFctlSize);
+ const size_t kAfterFctl = kFctlOffset + kFctlSize;
+ modified_data->Append(data->Data() + kAfterFctl, data->size() - kAfterFctl);
+
+ auto decoder = CreatePNGDecoder();
+ decoder->SetData(modified_data.get(), true);
+ ExpectStatic(decoder.get());
+}
+
+// Originally, the third frame has an offset of (1,2) and a size of (3,2). By
+// changing the offset to (4,4), the frame rect is no longer within the image
+// size of 5x5. This results in a failure.
+TEST(AnimatedPNGTests, VerifyFrameOutsideImageSizeFails) {
+ const char* png_file =
+ "/images/resources/"
+ "png-animated-idat-part-of-animation.png";
+ scoped_refptr<SharedBuffer> data = ReadFile(png_file);
+ auto decoder = CreatePNGDecoder();
+ ASSERT_FALSE(data->IsEmpty());
+
+ const size_t kOffsetThirdFctl = 241;
+ scoped_refptr<SharedBuffer> modified_data =
+ SharedBuffer::Create(data->Data(), kOffsetThirdFctl);
+ const size_t kFctlSize = 38u;
+ png_byte fctl[kFctlSize];
+ memcpy(fctl, data->Data() + kOffsetThirdFctl, kFctlSize);
+ // Modify offset and crc.
+ WriteUint32(4, fctl + 20u);
+ WriteUint32(4, fctl + 24u);
+ WriteUint32(3700322018, fctl + 34u);
+
+ modified_data->Append(const_cast<const char*>(reinterpret_cast<char*>(fctl)),
+ kFctlSize);
+ modified_data->Append(data->Data() + kOffsetThirdFctl + kFctlSize,
+ data->size() - kOffsetThirdFctl - kFctlSize);
+
+ decoder->SetData(modified_data, true);
+
+ IntSize expected_size(5, 5);
+ EXPECT_TRUE(decoder->IsSizeAvailable());
+ EXPECT_EQ(expected_size, decoder->Size());
+
+ const size_t kExpectedFrameCount = 0;
+ EXPECT_EQ(kExpectedFrameCount, decoder->FrameCount());
+ EXPECT_TRUE(decoder->Failed());
+}
+
+TEST(AnimatedPNGTests, ProgressiveDecodingContinuesAfterFullData) {
+ // 160u is a randomly chosen offset in the IDAT chunk of the first frame.
+ TestProgressiveDecodingContinuesAfterFullData(
+ "/images/resources/"
+ "png-animated-idat-part-of-animation.png",
+ 160u);
+}
+
+TEST(AnimatedPNGTests, RandomDecodeAfterClearFrameBufferCache) {
+ TestRandomDecodeAfterClearFrameBufferCache(
+ &CreatePNGDecoder,
+ "/images/resources/"
+ "png-animated-idat-part-of-animation.png",
+ 2u);
+}
+
+TEST(AnimatedPNGTests, VerifyAlphaBlending) {
+ TestAlphaBlending(&CreatePNGDecoder,
+ "/images/resources/"
+ "png-animated-idat-part-of-animation.png");
+}
+
+// This tests if the frame count gets set correctly when parsing FrameCount
+// fails in one of the parsing queries.
+//
+// First, enough data is provided such that two frames should be registered.
+// The decoder should at this point not be in the failed status.
+//
+// Then, we provide the rest of the data except for the last IEND chunk, but
+// tell the decoder that this is all the data we have. The frame count should
+// be three, since one extra frame should be discovered. The fourth frame
+// should *not* be registered since the reader should not be able to determine
+// where the frame ends. The decoder should *not* be in the failed state since
+// there are three frames which can be shown.
+// Attempting to decode the third frame should fail, since the file is
+// truncated.
+TEST(AnimatedPNGTests, FailureMissingIendChunk) {
+ scoped_refptr<SharedBuffer> full_data = ReadFile(
+ "/images/resources/"
+ "png-animated-idat-part-of-animation.png");
+ ASSERT_FALSE(full_data->IsEmpty());
+ auto decoder = CreatePNGDecoder();
+
+ const size_t kOffsetTwoFrames = 249;
+ const size_t kExpectedFramesAfter249Bytes = 2;
+ scoped_refptr<SharedBuffer> temp_data =
+ SharedBuffer::Create(full_data->Data(), kOffsetTwoFrames);
+ decoder->SetData(temp_data.get(), false);
+ EXPECT_EQ(kExpectedFramesAfter249Bytes, decoder->FrameCount());
+ EXPECT_FALSE(decoder->Failed());
+
+ // Provide the rest of the data except for the last IEND chunk.
+ const size_t kExpectedFramesAfterAllExcept12Bytes = 3;
+ temp_data = SharedBuffer::Create(full_data->Data(), full_data->size() - 12);
+ decoder->SetData(temp_data.get(), true);
+ ASSERT_EQ(kExpectedFramesAfterAllExcept12Bytes, decoder->FrameCount());
+
+ for (size_t i = 0; i < kExpectedFramesAfterAllExcept12Bytes; i++) {
+ EXPECT_FALSE(decoder->Failed());
+ decoder->DecodeFrameBufferAtIndex(i);
+ }
+
+ EXPECT_TRUE(decoder->Failed());
+}
+
+// Verify that a malformatted PNG, where the IEND appears before any frame data
+// (IDAT), invalidates the decoder.
+TEST(AnimatedPNGTests, VerifyIENDBeforeIDATInvalidatesDecoder) {
+ scoped_refptr<SharedBuffer> full_data = ReadFile(
+ "/images/resources/"
+ "png-animated-idat-part-of-animation.png");
+ ASSERT_FALSE(full_data->IsEmpty());
+ auto decoder = CreatePNGDecoder();
+
+ const size_t kOffsetIDAT = 133;
+ scoped_refptr<SharedBuffer> data =
+ SharedBuffer::Create(full_data->Data(), kOffsetIDAT);
+ data->Append(full_data->Data() + full_data->size() - 12u, 12u);
+ data->Append(full_data->Data() + kOffsetIDAT,
+ full_data->size() - kOffsetIDAT);
+ decoder->SetData(data.get(), true);
+
+ const size_t kExpectedFrameCount = 0u;
+ EXPECT_EQ(kExpectedFrameCount, decoder->FrameCount());
+ EXPECT_TRUE(decoder->Failed());
+}
+
+// All IDAT chunks must be before all fdAT chunks
+TEST(AnimatedPNGTests, MixedDataChunks) {
+ const char* png_file =
+ "/images/resources/"
+ "png-animated-idat-part-of-animation.png";
+ scoped_refptr<SharedBuffer> full_data = ReadFile(png_file);
+ ASSERT_FALSE(full_data->IsEmpty());
+
+ // Add an extra fdAT after the first IDAT, skipping fcTL.
+ const size_t kPostIDAT = 172u;
+ scoped_refptr<SharedBuffer> data =
+ SharedBuffer::Create(full_data->Data(), kPostIDAT);
+ const size_t kFcTLSize = 38u;
+ const size_t kFdATSize = 31u;
+ png_byte fd_at[kFdATSize];
+ memcpy(fd_at, full_data->Data() + kPostIDAT + kFcTLSize, kFdATSize);
+ // Modify the sequence number
+ WriteUint32(1u, fd_at + 8);
+ data->Append((const char*)fd_at, kFdATSize);
+ const size_t kIENDOffset = 422u;
+ data->Append(full_data->Data() + kIENDOffset,
+ full_data->size() - kIENDOffset);
+ auto decoder = CreatePNGDecoder();
+ decoder->SetData(data.get(), true);
+ decoder->FrameCount();
+ EXPECT_TRUE(decoder->Failed());
+
+ // Insert an IDAT after an fdAT.
+ const size_t kPostfdAT = kPostIDAT + kFcTLSize + kFdATSize;
+ data = SharedBuffer::Create(full_data->Data(), kPostfdAT);
+ const size_t kIDATOffset = 133u;
+ data->Append(full_data->Data() + kIDATOffset, kPostIDAT - kIDATOffset);
+ // Append the rest.
+ data->Append(full_data->Data() + kPostIDAT, full_data->size() - kPostIDAT);
+ decoder = CreatePNGDecoder();
+ decoder->SetData(data.get(), true);
+ decoder->FrameCount();
+ EXPECT_TRUE(decoder->Failed());
+}
+
+// Verify that erroneous values for the disposal method and alpha blending
+// cause the decoder to fail.
+TEST(AnimatedPNGTests, VerifyInvalidDisposalAndBlending) {
+ const char* png_file =
+ "/images/resources/"
+ "png-animated-idat-part-of-animation.png";
+ scoped_refptr<SharedBuffer> full_data = ReadFile(png_file);
+ ASSERT_FALSE(full_data->IsEmpty());
+ auto decoder = CreatePNGDecoder();
+
+ // The disposal byte in the frame control chunk is the 24th byte, alpha
+ // blending the 25th. |kOffsetDisposalOp| is 241 bytes to get to the third
+ // fctl chunk, 8 bytes to skip the length and tag bytes, and 24 bytes to get
+ // to the disposal op.
+ //
+ // Write invalid values to the disposal and alpha blending byte, correct the
+ // crc and append the rest of the buffer.
+ const size_t kOffsetDisposalOp = 241 + 8 + 24;
+ scoped_refptr<SharedBuffer> data =
+ SharedBuffer::Create(full_data->Data(), kOffsetDisposalOp);
+ png_byte disposal_and_blending[6u];
+ disposal_and_blending[0] = 7;
+ disposal_and_blending[1] = 9;
+ WriteUint32(2408835439u, disposal_and_blending + 2u);
+ data->Append(reinterpret_cast<char*>(disposal_and_blending), 6u);
+ data->Append(full_data->Data() + kOffsetDisposalOp + 6u,
+ full_data->size() - kOffsetDisposalOp - 6u);
+
+ decoder->SetData(data.get(), true);
+ decoder->FrameCount();
+ ASSERT_TRUE(decoder->Failed());
+}
+
+// This test verifies that the following situation does not invalidate the
+// decoder:
+// - Frame 0 is decoded progressively, but there's not enough data to fully
+// decode it.
+// - The rest of the image data is received.
+// - Frame X, with X > 0, and X does not depend on frame 0, is decoded.
+// - Frame 0 is decoded.
+// This is a tricky case since the decoder resets the png struct for each frame,
+// and this test verifies that it does not break the decoding of frame 0, even
+// though it already started in the first call.
+TEST(AnimatedPNGTests, VerifySuccessfulFirstFrameDecodeAfterLaterFrame) {
+ const char* png_file =
+ "/images/resources/"
+ "png-animated-three-independent-frames.png";
+ auto decoder = CreatePNGDecoder();
+ scoped_refptr<SharedBuffer> full_data = ReadFile(png_file);
+ ASSERT_FALSE(full_data->IsEmpty());
+
+ // 160u is a randomly chosen offset in the IDAT chunk of the first frame.
+ const size_t kMiddleFirstFrame = 160u;
+ scoped_refptr<SharedBuffer> data =
+ SharedBuffer::Create(full_data->Data(), kMiddleFirstFrame);
+ decoder->SetData(data.get(), false);
+
+ ASSERT_EQ(1u, decoder->FrameCount());
+ ASSERT_EQ(ImageFrame::kFramePartial,
+ decoder->DecodeFrameBufferAtIndex(0)->GetStatus());
+
+ decoder->SetData(full_data.get(), true);
+ ASSERT_EQ(3u, decoder->FrameCount());
+ ASSERT_EQ(ImageFrame::kFrameComplete,
+ decoder->DecodeFrameBufferAtIndex(1)->GetStatus());
+ // The point is that this call does not decode frame 0, which it won't do if
+ // it does not have it as its required previous frame.
+ ASSERT_EQ(kNotFound,
+ decoder->DecodeFrameBufferAtIndex(1)->RequiredPreviousFrameIndex());
+
+ EXPECT_EQ(ImageFrame::kFrameComplete,
+ decoder->DecodeFrameBufferAtIndex(0)->GetStatus());
+ EXPECT_FALSE(decoder->Failed());
+}
+
+// If the decoder attempts to decode a non-first frame which is subset and
+// independent, it needs to discard its png_struct so it can use a modified
+// IHDR. Test this by comparing a decode of frame 1 after frame 0 to a decode
+// of frame 1 without decoding frame 0.
+TEST(AnimatedPNGTests, DecodeFromIndependentFrame) {
+ const char* png_file =
+ "/images/resources/"
+ "png-animated-idat-part-of-animation.png";
+ scoped_refptr<SharedBuffer> original_data = ReadFile(png_file);
+ ASSERT_FALSE(original_data->IsEmpty());
+
+ // This file almost fits the bill. Modify it to dispose frame 0, making
+ // frame 1 independent.
+ const size_t kDisposeOffset = 127u;
+ auto data = SharedBuffer::Create(original_data->Data(), kDisposeOffset);
+ // 1 Corresponds to APNG_DISPOSE_OP_BACKGROUND
+ const char kOne = '\001';
+ data->Append(&kOne, 1u);
+ // No need to modify the blend op
+ data->Append(original_data->Data() + kDisposeOffset + 1, 1u);
+ // Modify the CRC
+ png_byte crc[4];
+ WriteUint32(2226670956, crc);
+ data->Append(reinterpret_cast<const char*>(crc), 4u);
+ data->Append(original_data->Data() + data->size(),
+ original_data->size() - data->size());
+ ASSERT_EQ(original_data->size(), data->size());
+
+ auto decoder = CreatePNGDecoder();
+ decoder->SetData(data.get(), true);
+
+ ASSERT_EQ(4u, decoder->FrameCount());
+ ASSERT_FALSE(decoder->Failed());
+
+ auto* frame = decoder->DecodeFrameBufferAtIndex(0);
+ ASSERT_TRUE(frame);
+ ASSERT_EQ(ImageFrame::kDisposeOverwriteBgcolor, frame->GetDisposalMethod());
+
+ frame = decoder->DecodeFrameBufferAtIndex(1);
+ ASSERT_TRUE(frame);
+ ASSERT_FALSE(decoder->Failed());
+ ASSERT_NE(IntRect({}, decoder->Size()), frame->OriginalFrameRect());
+ ASSERT_EQ(kNotFound, frame->RequiredPreviousFrameIndex());
+
+ const auto hash = HashBitmap(frame->Bitmap());
+
+ // Now decode starting from frame 1.
+ decoder = CreatePNGDecoder();
+ decoder->SetData(data.get(), true);
+
+ frame = decoder->DecodeFrameBufferAtIndex(1);
+ ASSERT_TRUE(frame);
+ EXPECT_EQ(hash, HashBitmap(frame->Bitmap()));
+}
+
+// If the first frame is subset from IHDR (only allowed if the first frame is
+// not the default image), the decoder has to destroy the png_struct it used
+// for parsing so it can use a modified IHDR.
+TEST(AnimatedPNGTests, SubsetFromIHDR) {
+ const char* png_file =
+ "/images/resources/"
+ "png-animated-idat-not-part-of-animation.png";
+ scoped_refptr<SharedBuffer> original_data = ReadFile(png_file);
+ ASSERT_FALSE(original_data->IsEmpty());
+
+ const size_t kFcTLOffset = 2519u;
+ auto data = SharedBuffer::Create(original_data->Data(), kFcTLOffset);
+
+ const size_t kFcTLSize = 38u;
+ png_byte fc_tl[kFcTLSize];
+ memcpy(fc_tl, original_data->Data() + kFcTLOffset, kFcTLSize);
+ // Modify to have a subset frame (yOffset 1, height 34 out of 35).
+ WriteUint32(34, fc_tl + 16u);
+ WriteUint32(1, fc_tl + 24u);
+ WriteUint32(3972842751, fc_tl + 34u);
+ data->Append(reinterpret_cast<const char*>(fc_tl), kFcTLSize);
+
+ // Append the rest of the data.
+ // Note: If PNGImageDecoder changes to reject an image with too many
+ // rows, the fdAT data will need to be modified as well.
+ data->Append(original_data->Data() + kFcTLOffset + kFcTLSize,
+ original_data->size() - data->size());
+ ASSERT_EQ(original_data->size(), data->size());
+
+ // This will test both byte by byte and using the full data, and compare.
+ TestByteByByteDecode(CreatePNGDecoder, data.get(), 1, kAnimationNone);
+}
+
+TEST(AnimatedPNGTests, Offset) {
+ const char* png_file = "/images/resources/apng18.png";
+ scoped_refptr<SharedBuffer> original_data = ReadFile(png_file);
+ ASSERT_FALSE(original_data->IsEmpty());
+
+ Vector<unsigned> baseline_hashes;
+ CreateDecodingBaseline(CreatePNGDecoder, original_data.get(),
+ &baseline_hashes);
+ constexpr size_t kExpectedFrameCount = 13;
+ ASSERT_EQ(kExpectedFrameCount, baseline_hashes.size());
+
+ constexpr size_t kOffset = 37;
+ char buffer[kOffset] = {};
+
+ auto data = SharedBuffer::Create(buffer, kOffset);
+ data->Append(*original_data.get());
+
+ // Use the same defaults as CreatePNGDecoder, except use the (arbitrary)
+ // non-zero offset.
+ auto decoder = std::make_unique<PNGImageDecoder>(
+ ImageDecoder::kAlphaNotPremultiplied, ColorBehavior::TransformToSRGB(),
+ ImageDecoder::kNoDecodedImageByteLimit, kOffset);
+ decoder->SetData(data, true);
+ ASSERT_EQ(kExpectedFrameCount, decoder->FrameCount());
+
+ for (size_t i = 0; i < kExpectedFrameCount; ++i) {
+ auto* frame = decoder->DecodeFrameBufferAtIndex(i);
+ EXPECT_EQ(baseline_hashes[i], HashBitmap(frame->Bitmap()));
+ }
+}
+
+TEST(AnimatedPNGTests, ExtraChunksBeforeIHDR) {
+ const char* png_file = "/images/resources/apng18.png";
+ scoped_refptr<SharedBuffer> original_data = ReadFile(png_file);
+ ASSERT_FALSE(original_data->IsEmpty());
+
+ Vector<unsigned> baseline_hashes;
+ CreateDecodingBaseline(CreatePNGDecoder, original_data.get(),
+ &baseline_hashes);
+ constexpr size_t kExpectedFrameCount = 13;
+ ASSERT_EQ(kExpectedFrameCount, baseline_hashes.size());
+
+ constexpr size_t kPngSignatureSize = 8;
+ auto data = SharedBuffer::Create(original_data->Data(), kPngSignatureSize);
+
+ // Arbitrary chunk of data.
+ constexpr size_t kExtraChunkSize = 13;
+ constexpr png_byte kExtraChunk[kExtraChunkSize] = {
+ 0, 0, 0, 1, 't', 'R', 'c', 'N', 68, 82, 0, 87, 10};
+ data->Append(reinterpret_cast<const char*>(kExtraChunk), kExtraChunkSize);
+
+ // Append the rest of the data from the original.
+ data->Append(original_data->Data() + kPngSignatureSize,
+ original_data->size() - kPngSignatureSize);
+ ASSERT_EQ(original_data->size() + kExtraChunkSize, data->size());
+
+ auto decoder = CreatePNGDecoder();
+ decoder->SetData(data, true);
+ ASSERT_EQ(kExpectedFrameCount, decoder->FrameCount());
+
+ for (size_t i = 0; i < kExpectedFrameCount; ++i) {
+ auto* frame = decoder->DecodeFrameBufferAtIndex(i);
+ EXPECT_EQ(baseline_hashes[i], HashBitmap(frame->Bitmap()));
+ }
+}
+
+// Static PNG tests
+
+TEST(StaticPNGTests, repetitionCountTest) {
+ TestRepetitionCount("/images/resources/png-simple.png", kAnimationNone);
+}
+
+TEST(StaticPNGTests, sizeTest) {
+ TestSize("/images/resources/png-simple.png", IntSize(111, 29));
+}
+
+TEST(StaticPNGTests, MetaDataTest) {
+ const size_t kExpectedFrameCount = 1;
+ const TimeDelta kExpectedDuration;
+ auto decoder =
+ CreatePNGDecoderWithPngData("/images/resources/png-simple.png");
+ EXPECT_EQ(kExpectedFrameCount, decoder->FrameCount());
+ EXPECT_EQ(kExpectedDuration, decoder->FrameDurationAtIndex(0));
+}
+
+TEST(StaticPNGTests, InvalidIHDRChunk) {
+ TestMissingDataBreaksDecoding("/images/resources/png-simple.png", 20u, 2u);
+}
+
+TEST(StaticPNGTests, ProgressiveDecoding) {
+ TestProgressiveDecoding(&CreatePNGDecoder, "/images/resources/png-simple.png",
+ 11u);
+}
+
+TEST(StaticPNGTests, ProgressiveDecodingContinuesAfterFullData) {
+ TestProgressiveDecodingContinuesAfterFullData(
+ "/images/resources/png-simple.png", 1000u);
+}
+
+TEST(PNGTests, VerifyFrameCompleteBehavior) {
+ struct {
+ const char* name;
+ size_t expected_frame_count;
+ size_t offset_in_first_frame;
+ } g_recs[] = {
+ {"/images/resources/"
+ "png-animated-three-independent-frames.png",
+ 3u, 150u},
+ {"/images/resources/"
+ "png-animated-idat-part-of-animation.png",
+ 4u, 160u},
+
+ {"/images/resources/png-simple.png", 1u, 700u},
+ {"/images/resources/lenna.png", 1u, 40000u},
+ };
+ for (const auto& rec : g_recs) {
+ scoped_refptr<SharedBuffer> full_data = ReadFile(rec.name);
+ ASSERT_TRUE(full_data.get());
+
+ // Create with enough data for part of the first frame.
+ auto decoder = CreatePNGDecoder();
+ auto data =
+ SharedBuffer::Create(full_data->Data(), rec.offset_in_first_frame);
+ decoder->SetData(data.get(), false);
+
+ EXPECT_FALSE(decoder->FrameIsReceivedAtIndex(0));
+
+ // Parsing the size is not enough to mark the frame as complete.
+ EXPECT_TRUE(decoder->IsSizeAvailable());
+ EXPECT_FALSE(decoder->FrameIsReceivedAtIndex(0));
+
+ const auto partial_frame_count = decoder->FrameCount();
+ EXPECT_EQ(1u, partial_frame_count);
+
+ // Frame is not complete, even after decoding partially.
+ EXPECT_FALSE(decoder->FrameIsReceivedAtIndex(0));
+ auto* frame = decoder->DecodeFrameBufferAtIndex(0);
+ ASSERT_TRUE(frame);
+ EXPECT_NE(ImageFrame::kFrameComplete, frame->GetStatus());
+ EXPECT_FALSE(decoder->FrameIsReceivedAtIndex(0));
+
+ decoder->SetData(full_data.get(), true);
+
+ // With full data, parsing the size still does not mark a frame as
+ // complete for animated images.
+ EXPECT_TRUE(decoder->IsSizeAvailable());
+ if (rec.expected_frame_count > 1)
+ EXPECT_FALSE(decoder->FrameIsReceivedAtIndex(0));
+ else
+ EXPECT_TRUE(decoder->FrameIsReceivedAtIndex(0));
+
+ const auto frame_count = decoder->FrameCount();
+ ASSERT_EQ(rec.expected_frame_count, frame_count);
+
+ // After parsing (the full file), all frames are complete.
+ for (size_t i = 0; i < frame_count; ++i)
+ EXPECT_TRUE(decoder->FrameIsReceivedAtIndex(i));
+
+ frame = decoder->DecodeFrameBufferAtIndex(0);
+ ASSERT_TRUE(frame);
+ EXPECT_EQ(ImageFrame::kFrameComplete, frame->GetStatus());
+ EXPECT_TRUE(decoder->FrameIsReceivedAtIndex(0));
+ }
+}
+
+TEST(PNGTests, sizeMayOverflow) {
+ auto decoder =
+ CreatePNGDecoderWithPngData("/images/resources/crbug702934.png");
+ EXPECT_FALSE(decoder->IsSizeAvailable());
+ EXPECT_TRUE(decoder->Failed());
+}
+
+TEST(PNGTests, truncated) {
+ auto decoder =
+ CreatePNGDecoderWithPngData("/images/resources/crbug807324.png");
+
+ // An update to libpng (without using the libpng-provided workaround) resulted
+ // in truncating this image. It has no transparency, so no pixel should be
+ // transparent.
+ auto* frame = decoder->DecodeFrameBufferAtIndex(0);
+ auto size = decoder->Size();
+ for (int i = 0; i < size.Width(); ++i)
+ for (int j = 0; j < size.Height(); ++j) {
+ ASSERT_NE(SK_ColorTRANSPARENT, *frame->GetAddr(i, j));
+ }
+}
+
+}; // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/png/png_image_reader.cc b/chromium/third_party/blink/renderer/platform/image-decoders/png/png_image_reader.cc
new file mode 100644
index 00000000000..a0b617fb4d9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/png/png_image_reader.cc
@@ -0,0 +1,701 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc.
+ * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
+ *
+ * Portions are Copyright (C) 2001 mozilla.org
+ *
+ * Other contributors:
+ * Stuart Parmenter <stuart@mozilla.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Alternatively, the contents of this file may be used under the terms
+ * of either the Mozilla Public License Version 1.1, found at
+ * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
+ * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
+ * (the "GPL"), in which case the provisions of the MPL or the GPL are
+ * applicable instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of one of those two
+ * licenses (the MPL or the GPL) and not to allow others to use your
+ * version of this file under the LGPL, indicate your decision by
+ * deletingthe provisions above and replace them with the notice and
+ * other provisions required by the MPL or the GPL, as the case may be.
+ * If you do not delete the provisions above, a recipient may use your
+ * version of this file under any of the LGPL, the MPL or the GPL.
+ */
+
+#include "third_party/blink/renderer/platform/image-decoders/png/png_image_reader.h"
+
+#include <memory>
+#include "third_party/blink/renderer/platform/image-decoders/fast_shared_buffer_reader.h"
+#include "third_party/blink/renderer/platform/image-decoders/png/png_image_decoder.h"
+#include "third_party/blink/renderer/platform/image-decoders/segment_reader.h"
+#include "zlib.h"
+
+namespace {
+
+inline blink::PNGImageDecoder* imageDecoder(png_structp png) {
+ return static_cast<blink::PNGImageDecoder*>(png_get_progressive_ptr(png));
+}
+
+void PNGAPI pngHeaderAvailable(png_structp png, png_infop) {
+ imageDecoder(png)->HeaderAvailable();
+}
+
+void PNGAPI pngRowAvailable(png_structp png,
+ png_bytep row,
+ png_uint_32 rowIndex,
+ int state) {
+ imageDecoder(png)->RowAvailable(row, rowIndex, state);
+}
+
+void PNGAPI pngFrameComplete(png_structp png, png_infop) {
+ imageDecoder(png)->FrameComplete();
+}
+
+void PNGAPI pngFailed(png_structp png, png_const_charp) {
+ longjmp(JMPBUF(png), 1);
+}
+
+} // namespace
+
+namespace blink {
+
+PNGImageReader::PNGImageReader(PNGImageDecoder* decoder, size_t initial_offset)
+ : width_(0),
+ height_(0),
+ decoder_(decoder),
+ initial_offset_(initial_offset),
+ read_offset_(initial_offset),
+ progressive_decode_offset_(0),
+ ihdr_offset_(0),
+ idat_offset_(0),
+ idat_is_part_of_animation_(false),
+ expect_idats_(true),
+ is_animated_(false),
+ parsed_signature_(false),
+ parsed_ihdr_(false),
+ parse_completed_(false),
+ reported_frame_count_(0),
+ next_sequence_number_(0),
+ fctl_needs_dat_chunk_(false),
+ ignore_animation_(false) {
+ png_ = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, pngFailed,
+ nullptr);
+ info_ = png_create_info_struct(png_);
+ png_set_progressive_read_fn(png_, decoder_, nullptr, pngRowAvailable,
+ pngFrameComplete);
+ // This setting ensures that we display images with incorrect CMF bytes.
+ // See crbug.com/807324.
+ png_set_option(png_, PNG_MAXIMUM_INFLATE_WINDOW, PNG_OPTION_ON);
+}
+
+PNGImageReader::~PNGImageReader() {
+ png_destroy_read_struct(png_ ? &png_ : nullptr, info_ ? &info_ : nullptr,
+ nullptr);
+ DCHECK(!png_ && !info_);
+}
+
+// This method reads from the FastSharedBufferReader, starting at offset,
+// and returns |length| bytes in the form of a pointer to a const png_byte*.
+// This function is used to make it easy to access data from the reader in a
+// png friendly way, and pass it to libpng for decoding.
+//
+// Pre-conditions before using this:
+// - |reader|.size() >= |read_offset| + |length|
+// - |buffer|.size() >= |length|
+// - |length| <= |kPngReadBufferSize|
+//
+// The reason for the last two precondition is that currently the png signature
+// plus IHDR chunk (8B + 25B = 33B) is the largest chunk that is read using this
+// method. If the data is not consecutive, it is stored in |buffer|, which must
+// have the size of (at least) |length|, but there's no need for it to be larger
+// than |kPngReadBufferSize|.
+static constexpr size_t kPngReadBufferSize = 33;
+const png_byte* ReadAsConstPngBytep(const FastSharedBufferReader& reader,
+ size_t read_offset,
+ size_t length,
+ char* buffer) {
+ DCHECK(length <= kPngReadBufferSize);
+ return reinterpret_cast<const png_byte*>(
+ reader.GetConsecutiveData(read_offset, length, buffer));
+}
+
+bool PNGImageReader::ShouldDecodeWithNewPNG(size_t index) const {
+ if (!png_)
+ return true;
+ const bool first_frame_decode_in_progress = progressive_decode_offset_;
+ const bool frame_size_matches_ihdr =
+ frame_info_[index].frame_rect == IntRect(0, 0, width_, height_);
+ if (index)
+ return first_frame_decode_in_progress || !frame_size_matches_ihdr;
+ return !first_frame_decode_in_progress && !frame_size_matches_ihdr;
+}
+
+// Return false on a fatal error.
+bool PNGImageReader::Decode(SegmentReader& data, size_t index) {
+ if (index >= frame_info_.size())
+ return true;
+
+ const FastSharedBufferReader reader(&data);
+
+ if (!is_animated_) {
+ if (setjmp(JMPBUF(png_)))
+ return false;
+ DCHECK_EQ(0u, index);
+ progressive_decode_offset_ += ProcessData(
+ reader, frame_info_[0].start_offset + progressive_decode_offset_, 0);
+ return true;
+ }
+
+ DCHECK(is_animated_);
+
+ const bool decode_with_new_png = ShouldDecodeWithNewPNG(index);
+ if (decode_with_new_png) {
+ ClearDecodeState(0);
+ png_ = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, pngFailed,
+ nullptr);
+ info_ = png_create_info_struct(png_);
+ png_set_progressive_read_fn(png_, decoder_, pngHeaderAvailable,
+ pngRowAvailable, pngFrameComplete);
+ }
+
+ if (setjmp(JMPBUF(png_)))
+ return false;
+
+ if (decode_with_new_png)
+ StartFrameDecoding(reader, index);
+
+ if (!index && (!FirstFrameFullyReceived() || progressive_decode_offset_)) {
+ const bool decoded_entire_frame = ProgressivelyDecodeFirstFrame(reader);
+ if (!decoded_entire_frame)
+ return true;
+ progressive_decode_offset_ = 0;
+ } else {
+ DecodeFrame(reader, index);
+ }
+
+ static png_byte iend[12] = {0, 0, 0, 0, 'I', 'E', 'N', 'D', 174, 66, 96, 130};
+ png_process_data(png_, info_, iend, 12);
+ png_destroy_read_struct(&png_, &info_, nullptr);
+ DCHECK(!png_ && !info_);
+
+ return true;
+}
+
+void PNGImageReader::StartFrameDecoding(const FastSharedBufferReader& reader,
+ size_t index) {
+ DCHECK_GT(ihdr_offset_, initial_offset_);
+ ProcessData(reader, initial_offset_, ihdr_offset_ - initial_offset_);
+
+ const IntRect& frame_rect = frame_info_[index].frame_rect;
+ if (frame_rect == IntRect(0, 0, width_, height_)) {
+ DCHECK_GT(idat_offset_, ihdr_offset_);
+ ProcessData(reader, ihdr_offset_, idat_offset_ - ihdr_offset_);
+ return;
+ }
+
+ // Process the IHDR chunk, but change the width and height so it reflects
+ // the frame's width and height. ImageDecoder will apply the x,y offset.
+ constexpr size_t kHeaderSize = 25;
+ char read_buffer[kHeaderSize];
+ const png_byte* chunk =
+ ReadAsConstPngBytep(reader, ihdr_offset_, kHeaderSize, read_buffer);
+ png_byte* header = reinterpret_cast<png_byte*>(read_buffer);
+ if (chunk != header)
+ memcpy(header, chunk, kHeaderSize);
+ png_save_uint_32(header + 8, frame_rect.Width());
+ png_save_uint_32(header + 12, frame_rect.Height());
+ // IHDR has been modified, so tell libpng to ignore CRC errors.
+ png_set_crc_action(png_, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
+ png_process_data(png_, info_, header, kHeaderSize);
+
+ // Process the rest of the header chunks.
+ DCHECK_GE(idat_offset_, ihdr_offset_ + kHeaderSize);
+ ProcessData(reader, ihdr_offset_ + kHeaderSize,
+ idat_offset_ - ihdr_offset_ - kHeaderSize);
+}
+
+// Determine if the bytes 4 to 7 of |chunk| indicate that it is a |tag| chunk.
+// - The length of |chunk| must be >= 8
+// - The length of |tag| must be = 4
+static inline bool IsChunk(const png_byte* chunk, const char* tag) {
+ return memcmp(chunk + 4, tag, 4) == 0;
+}
+
+bool PNGImageReader::ProgressivelyDecodeFirstFrame(
+ const FastSharedBufferReader& reader) {
+ size_t offset = frame_info_[0].start_offset;
+
+ // Loop while there is enough data to do progressive decoding.
+ while (reader.size() >= offset + 8) {
+ char read_buffer[8];
+ // At the beginning of each loop, the offset is at the start of a chunk.
+ const png_byte* chunk = ReadAsConstPngBytep(reader, offset, 8, read_buffer);
+ const png_uint_32 length = png_get_uint_32(chunk);
+ DCHECK(length <= PNG_UINT_31_MAX);
+
+ // When an fcTL or IEND chunk is encountered, the frame data has ended.
+ // Return true, since all frame data is decoded.
+ if (IsChunk(chunk, "fcTL") || IsChunk(chunk, "IEND"))
+ return true;
+
+ // If this chunk was already decoded, move on to the next.
+ if (progressive_decode_offset_ >= offset + length + 12) {
+ offset += length + 12;
+ continue;
+ }
+
+ // Three scenarios are possible here:
+ // 1) Some bytes of this chunk were already decoded in a previous call.
+ // Continue from there.
+ // 2) This is an fdAT chunk. Convert it to an IDAT chunk to decode.
+ // 3) This is any other chunk. Pass it to libpng for processing.
+ size_t end_offset_chunk = offset + length + 12;
+
+ if (progressive_decode_offset_ >= offset + 8) {
+ offset = progressive_decode_offset_;
+ } else if (IsChunk(chunk, "fdAT")) {
+ ProcessFdatChunkAsIdat(length);
+ // Skip the sequence number.
+ offset += 12;
+ } else {
+ png_process_data(png_, info_, const_cast<png_byte*>(chunk), 8);
+ offset += 8;
+ }
+
+ size_t bytes_left_in_chunk = end_offset_chunk - offset;
+ size_t bytes_decoded = ProcessData(reader, offset, bytes_left_in_chunk);
+ progressive_decode_offset_ = offset + bytes_decoded;
+ if (bytes_decoded < bytes_left_in_chunk)
+ return false;
+ offset += bytes_decoded;
+ }
+
+ return false;
+}
+
+void PNGImageReader::ProcessFdatChunkAsIdat(png_uint_32 fdat_length) {
+ // An fdAT chunk is build up as follows:
+ // - |length| (4B)
+ // - fdAT tag (4B)
+ // - sequence number (4B)
+ // - frame data (|length| - 4B)
+ // - CRC (4B)
+ // Thus, to reformat this into an IDAT chunk, do the following:
+ // - write |length| - 4 as the new length, since the sequence number
+ // must be removed.
+ // - change the tag to IDAT.
+ // - omit the sequence number from the data part of the chunk.
+ png_byte chunk_idat[] = {0, 0, 0, 0, 'I', 'D', 'A', 'T'};
+ png_save_uint_32(chunk_idat, fdat_length - 4);
+ // The CRC is incorrect when applied to the modified fdAT.
+ png_set_crc_action(png_, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
+ png_process_data(png_, info_, chunk_idat, 8);
+}
+
+void PNGImageReader::DecodeFrame(const FastSharedBufferReader& reader,
+ size_t index) {
+ size_t offset = frame_info_[index].start_offset;
+ size_t end_offset = offset + frame_info_[index].byte_length;
+ char read_buffer[8];
+
+ while (offset < end_offset) {
+ const png_byte* chunk = ReadAsConstPngBytep(reader, offset, 8, read_buffer);
+ const png_uint_32 length = png_get_uint_32(chunk);
+ DCHECK(length <= PNG_UINT_31_MAX);
+
+ if (IsChunk(chunk, "fdAT")) {
+ ProcessFdatChunkAsIdat(length);
+ // The frame data and the CRC span |length| bytes, so skip the
+ // sequence number and process |length| bytes to decode the frame.
+ ProcessData(reader, offset + 12, length);
+ } else {
+ png_process_data(png_, info_, const_cast<png_byte*>(chunk), 8);
+ ProcessData(reader, offset + 8, length + 4);
+ }
+
+ offset += 12 + length;
+ }
+}
+
+// Compute the CRC and compare to the stored value.
+static bool CheckCrc(const FastSharedBufferReader& reader,
+ size_t chunk_start,
+ size_t chunk_length) {
+ constexpr size_t kSizeNeededForfcTL = 26 + 4;
+ char read_buffer[kSizeNeededForfcTL];
+ DCHECK(chunk_length + 4 <= kSizeNeededForfcTL);
+ const png_byte* chunk = ReadAsConstPngBytep(reader, chunk_start + 4,
+ chunk_length + 4, read_buffer);
+
+ char crc_buffer[4];
+ const png_byte* crc_position = ReadAsConstPngBytep(
+ reader, chunk_start + 8 + chunk_length, 4, crc_buffer);
+ png_uint_32 crc = png_get_uint_32(crc_position);
+ return crc == crc32(crc32(0, Z_NULL, 0), chunk, chunk_length + 4);
+}
+
+bool PNGImageReader::CheckSequenceNumber(const png_byte* position) {
+ png_uint_32 sequence = png_get_uint_32(position);
+ if (sequence != next_sequence_number_ || sequence > PNG_UINT_31_MAX)
+ return false;
+
+ ++next_sequence_number_;
+ return true;
+}
+
+// Return false if there was a fatal error; true otherwise.
+bool PNGImageReader::Parse(SegmentReader& data, ParseQuery query) {
+ if (parse_completed_)
+ return true;
+
+ const FastSharedBufferReader reader(&data);
+
+ if (!ParseSize(reader))
+ return false;
+
+ if (!decoder_->IsDecodedSizeAvailable())
+ return true;
+
+ // For non animated images (identified by no acTL chunk before the IDAT),
+ // there is no need to continue parsing.
+ if (!is_animated_) {
+ FrameInfo frame;
+ frame.start_offset = read_offset_;
+ // This should never be read in this case, but initialize just in case.
+ frame.byte_length = kFirstFrameIndicator;
+ frame.duration = 0;
+ frame.frame_rect = IntRect(0, 0, width_, height_);
+ frame.disposal_method = ImageFrame::DisposalMethod::kDisposeKeep;
+ frame.alpha_blend = ImageFrame::AlphaBlendSource::kBlendAtopBgcolor;
+ DCHECK(frame_info_.IsEmpty());
+ frame_info_.push_back(frame);
+ parse_completed_ = true;
+ return true;
+ }
+
+ if (query == ParseQuery::kSize)
+ return true;
+
+ DCHECK_EQ(ParseQuery::kMetaData, query);
+ DCHECK(is_animated_);
+
+ // Loop over the data and manually register all frames. Nothing is passed to
+ // libpng for processing. A frame is registered on the next fcTL chunk or
+ // when the IEND chunk is found. This ensures that only complete frames are
+ // reported, unless there is an error in the stream.
+ char read_buffer[kPngReadBufferSize];
+ while (reader.size() >= read_offset_ + 8) {
+ const png_byte* chunk =
+ ReadAsConstPngBytep(reader, read_offset_, 8, read_buffer);
+ const size_t length = png_get_uint_32(chunk);
+ if (length > PNG_UINT_31_MAX)
+ return false;
+
+ const bool idat = IsChunk(chunk, "IDAT");
+ if (idat && !expect_idats_)
+ return false;
+
+ const bool fd_at = IsChunk(chunk, "fdAT");
+ if (fd_at && expect_idats_)
+ return false;
+
+ if (fd_at || (idat && idat_is_part_of_animation_)) {
+ fctl_needs_dat_chunk_ = false;
+ if (!new_frame_.start_offset) {
+ // Beginning of a new frame's data.
+ new_frame_.start_offset = read_offset_;
+
+ if (frame_info_.IsEmpty()) {
+ // This is the first frame. Report it immediately so it can be
+ // decoded progressively.
+ new_frame_.byte_length = kFirstFrameIndicator;
+ frame_info_.push_back(new_frame_);
+ }
+ }
+
+ if (fd_at) {
+ if (reader.size() < read_offset_ + 8 + 4)
+ return true;
+ const png_byte* sequence_position =
+ ReadAsConstPngBytep(reader, read_offset_ + 8, 4, read_buffer);
+ if (!CheckSequenceNumber(sequence_position))
+ return false;
+ }
+
+ } else if (IsChunk(chunk, "fcTL") || IsChunk(chunk, "IEND")) {
+ // This marks the end of the previous frame.
+ if (new_frame_.start_offset) {
+ new_frame_.byte_length = read_offset_ - new_frame_.start_offset;
+ if (frame_info_[0].byte_length == kFirstFrameIndicator) {
+ frame_info_[0].byte_length = new_frame_.byte_length;
+ } else {
+ frame_info_.push_back(new_frame_);
+ if (IsChunk(chunk, "fcTL")) {
+ if (frame_info_.size() >= reported_frame_count_)
+ return false;
+ } else { // IEND
+ if (frame_info_.size() != reported_frame_count_)
+ return false;
+ }
+ }
+
+ new_frame_.start_offset = 0;
+ }
+
+ if (reader.size() < read_offset_ + 12 + length)
+ return true;
+
+ if (IsChunk(chunk, "IEND")) {
+ parse_completed_ = true;
+ return true;
+ }
+
+ if (length != 26 || !CheckCrc(reader, read_offset_, length))
+ return false;
+
+ chunk =
+ ReadAsConstPngBytep(reader, read_offset_ + 8, length, read_buffer);
+ if (!ParseFrameInfo(chunk))
+ return false;
+
+ expect_idats_ = false;
+ } else if (IsChunk(chunk, "acTL")) {
+ // There should only be one acTL chunk, and it should be before the
+ // IDAT chunk.
+ return false;
+ }
+
+ read_offset_ += 12 + length;
+ }
+ return true;
+}
+
+// If |length| == 0, read until the stream ends. Return number of bytes
+// processed.
+size_t PNGImageReader::ProcessData(const FastSharedBufferReader& reader,
+ size_t offset,
+ size_t length) {
+ const char* segment;
+ size_t total_processed_bytes = 0;
+ while (reader.size() > offset) {
+ size_t segment_length = reader.GetSomeData(segment, offset);
+ if (length > 0 && segment_length + total_processed_bytes > length)
+ segment_length = length - total_processed_bytes;
+
+ png_process_data(png_, info_,
+ reinterpret_cast<png_byte*>(const_cast<char*>(segment)),
+ segment_length);
+ offset += segment_length;
+ total_processed_bytes += segment_length;
+ if (total_processed_bytes == length)
+ return length;
+ }
+ return total_processed_bytes;
+}
+
+// Process up to the start of the IDAT with libpng.
+// Return false for a fatal error. True otherwise.
+bool PNGImageReader::ParseSize(const FastSharedBufferReader& reader) {
+ if (decoder_->IsDecodedSizeAvailable())
+ return true;
+
+ char read_buffer[kPngReadBufferSize];
+
+ if (setjmp(JMPBUF(png_)))
+ return false;
+
+ if (!parsed_signature_) {
+ if (reader.size() < read_offset_ + 8)
+ return true;
+
+ const png_byte* chunk =
+ ReadAsConstPngBytep(reader, read_offset_, 8, read_buffer);
+ png_process_data(png_, info_, const_cast<png_byte*>(chunk), 8);
+ read_offset_ += 8;
+ parsed_signature_ = true;
+ new_frame_.start_offset = 0;
+ }
+
+ // Process APNG chunks manually, pass other chunks to libpng.
+ for (png_uint_32 length = 0; reader.size() >= read_offset_ + 8;
+ read_offset_ += length + 12) {
+ const png_byte* chunk =
+ ReadAsConstPngBytep(reader, read_offset_, 8, read_buffer);
+ length = png_get_uint_32(chunk);
+
+ if (IsChunk(chunk, "IDAT")) {
+ // Done with header chunks.
+ idat_offset_ = read_offset_;
+ fctl_needs_dat_chunk_ = false;
+ if (ignore_animation_)
+ is_animated_ = false;
+ if (!is_animated_ || 1 == reported_frame_count_)
+ decoder_->SetRepetitionCount(kAnimationNone);
+ if (!decoder_->SetSize(width_, height_))
+ return false;
+ decoder_->SetColorSpace();
+ decoder_->HeaderAvailable();
+ return true;
+ }
+
+ // Wait until the entire chunk is available for parsing simplicity.
+ if (reader.size() < read_offset_ + length + 12)
+ break;
+
+ if (IsChunk(chunk, "acTL")) {
+ if (ignore_animation_)
+ continue;
+ if (is_animated_ || length != 8 || !parsed_ihdr_ ||
+ !CheckCrc(reader, read_offset_, 8)) {
+ ignore_animation_ = true;
+ continue;
+ }
+ chunk =
+ ReadAsConstPngBytep(reader, read_offset_ + 8, length, read_buffer);
+ reported_frame_count_ = png_get_uint_32(chunk);
+ if (!reported_frame_count_ || reported_frame_count_ > PNG_UINT_31_MAX) {
+ ignore_animation_ = true;
+ continue;
+ }
+ png_uint_32 repetition_count = png_get_uint_32(chunk + 4);
+ if (repetition_count > PNG_UINT_31_MAX) {
+ ignore_animation_ = true;
+ continue;
+ }
+ is_animated_ = true;
+ decoder_->SetRepetitionCount(static_cast<int>(repetition_count) - 1);
+ } else if (IsChunk(chunk, "fcTL")) {
+ if (ignore_animation_)
+ continue;
+ if (length != 26 || !parsed_ihdr_ ||
+ !CheckCrc(reader, read_offset_, 26)) {
+ ignore_animation_ = true;
+ continue;
+ }
+ chunk =
+ ReadAsConstPngBytep(reader, read_offset_ + 8, length, read_buffer);
+ if (!ParseFrameInfo(chunk) ||
+ new_frame_.frame_rect != IntRect(0, 0, width_, height_)) {
+ ignore_animation_ = true;
+ continue;
+ }
+ idat_is_part_of_animation_ = true;
+ } else if (IsChunk(chunk, "fdAT")) {
+ ignore_animation_ = true;
+ } else {
+ png_process_data(png_, info_, const_cast<png_byte*>(chunk), 8);
+ ProcessData(reader, read_offset_ + 8, length + 4);
+ if (IsChunk(chunk, "IHDR")) {
+ parsed_ihdr_ = true;
+ ihdr_offset_ = read_offset_;
+ width_ = png_get_image_width(png_, info_);
+ height_ = png_get_image_height(png_, info_);
+ }
+ }
+ }
+
+ // Not enough data to call HeaderAvailable.
+ return true;
+}
+
+void PNGImageReader::ClearDecodeState(size_t index) {
+ if (index)
+ return;
+ png_destroy_read_struct(png_ ? &png_ : nullptr, info_ ? &info_ : nullptr,
+ nullptr);
+ DCHECK(!png_ && !info_);
+ progressive_decode_offset_ = 0;
+}
+
+const PNGImageReader::FrameInfo& PNGImageReader::GetFrameInfo(
+ size_t index) const {
+ DCHECK(index < frame_info_.size());
+ return frame_info_[index];
+}
+
+// Extract the fcTL frame control info and store it in new_frame_. The length
+// check on the fcTL data has been done by the calling code.
+bool PNGImageReader::ParseFrameInfo(const png_byte* data) {
+ if (fctl_needs_dat_chunk_)
+ return false;
+
+ png_uint_32 frame_width = png_get_uint_32(data + 4);
+ png_uint_32 frame_height = png_get_uint_32(data + 8);
+ png_uint_32 x_offset = png_get_uint_32(data + 12);
+ png_uint_32 y_offset = png_get_uint_32(data + 16);
+ png_uint_16 delay_numerator = png_get_uint_16(data + 20);
+ png_uint_16 delay_denominator = png_get_uint_16(data + 22);
+
+ if (!CheckSequenceNumber(data))
+ return false;
+ if (!frame_width || !frame_height)
+ return false;
+ if (x_offset + frame_width > width_ || y_offset + frame_height > height_)
+ return false;
+
+ new_frame_.frame_rect =
+ IntRect(x_offset, y_offset, frame_width, frame_height);
+
+ if (delay_denominator)
+ new_frame_.duration = delay_numerator * 1000 / delay_denominator;
+ else
+ new_frame_.duration = delay_numerator * 10;
+
+ enum DisposeOperations : png_byte {
+ kAPNG_DISPOSE_OP_NONE = 0,
+ kAPNG_DISPOSE_OP_BACKGROUND = 1,
+ kAPNG_DISPOSE_OP_PREVIOUS = 2,
+ };
+ const png_byte& dispose_op = data[24];
+ switch (dispose_op) {
+ case kAPNG_DISPOSE_OP_NONE:
+ new_frame_.disposal_method = ImageFrame::DisposalMethod::kDisposeKeep;
+ break;
+ case kAPNG_DISPOSE_OP_BACKGROUND:
+ new_frame_.disposal_method =
+ ImageFrame::DisposalMethod::kDisposeOverwriteBgcolor;
+ break;
+ case kAPNG_DISPOSE_OP_PREVIOUS:
+ new_frame_.disposal_method =
+ ImageFrame::DisposalMethod::kDisposeOverwritePrevious;
+ break;
+ default:
+ return false;
+ }
+
+ enum BlendOperations : png_byte {
+ kAPNG_BLEND_OP_SOURCE = 0,
+ kAPNG_BLEND_OP_OVER = 1,
+ };
+ const png_byte& blend_op = data[25];
+ switch (blend_op) {
+ case kAPNG_BLEND_OP_SOURCE:
+ new_frame_.alpha_blend = ImageFrame::AlphaBlendSource::kBlendAtopBgcolor;
+ break;
+ case kAPNG_BLEND_OP_OVER:
+ new_frame_.alpha_blend =
+ ImageFrame::AlphaBlendSource::kBlendAtopPreviousFrame;
+ break;
+ default:
+ return false;
+ }
+
+ fctl_needs_dat_chunk_ = true;
+ return true;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/png/png_image_reader.h b/chromium/third_party/blink/renderer/platform/image-decoders/png/png_image_reader.h
new file mode 100644
index 00000000000..3826493ed60
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/png/png_image_reader.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2006 Apple Computer, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_PNG_PNG_IMAGE_READER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_PNG_PNG_IMAGE_READER_H_
+
+#include "base/macros.h"
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/image-decoders/image_frame.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+#define PNG_SET_OPTION_SUPPORTED
+#include "png.h"
+
+#if !defined(PNG_LIBPNG_VER_MAJOR) || !defined(PNG_LIBPNG_VER_MINOR)
+#error version error: compile against a versioned libpng.
+#endif
+
+#if PNG_LIBPNG_VER_MAJOR > 1 || \
+ (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4)
+#define JMPBUF(png_ptr) png_jmpbuf(png_ptr)
+#else
+#define JMPBUF(png_ptr) png_ptr->jmpbuf
+#endif
+
+namespace blink {
+
+class FastSharedBufferReader;
+class PNGImageDecoder;
+class SegmentReader;
+
+class PLATFORM_EXPORT PNGImageReader final {
+ USING_FAST_MALLOC(PNGImageReader);
+
+ public:
+ PNGImageReader(PNGImageDecoder*, size_t initial_offset);
+ ~PNGImageReader();
+
+ struct FrameInfo {
+ // The offset where the frame data of this frame starts.
+ size_t start_offset;
+ // The number of bytes that contain frame data, starting at start_offset.
+ size_t byte_length;
+ size_t duration;
+ IntRect frame_rect;
+ ImageFrame::DisposalMethod disposal_method;
+ ImageFrame::AlphaBlendSource alpha_blend;
+ };
+
+ enum class ParseQuery { kSize, kMetaData };
+
+ bool Parse(SegmentReader&, ParseQuery);
+
+ // Returns false on a fatal error.
+ bool Decode(SegmentReader&, size_t);
+ const FrameInfo& GetFrameInfo(size_t) const;
+
+ // Number of complete frames parsed so far; includes frame 0 even if partial.
+ size_t FrameCount() const { return frame_info_.size(); }
+
+ bool ParseCompleted() const { return parse_completed_; };
+
+ bool FrameIsReceivedAtIndex(size_t index) const {
+ if (!index)
+ return FirstFrameFullyReceived();
+ return index < FrameCount();
+ }
+
+ void ClearDecodeState(size_t);
+
+ png_structp PngPtr() const { return png_; }
+ png_infop InfoPtr() const { return info_; }
+
+ png_bytep InterlaceBuffer() const { return interlace_buffer_.get(); }
+ void CreateInterlaceBuffer(int size) {
+ interlace_buffer_ = std::make_unique<png_byte[]>(size);
+ }
+ void ClearInterlaceBuffer() { interlace_buffer_.reset(); }
+
+ private:
+ png_structp png_;
+ png_infop info_;
+ png_uint_32 width_;
+ png_uint_32 height_;
+
+ PNGImageDecoder* decoder_;
+
+ // The offset in the stream where the PNG image starts.
+ const size_t initial_offset_;
+ // How many bytes have been read during parsing.
+ size_t read_offset_;
+ size_t progressive_decode_offset_;
+ size_t ihdr_offset_;
+ size_t idat_offset_;
+
+ bool idat_is_part_of_animation_;
+ // All IDAT chunks must precede the first fdAT chunk, and all fdAT chunks
+ // should be separated from the IDAT chunks by an fcTL chunk. So this is true
+ // until the first fcTL chunk after an IDAT chunk. After that, only fdAT
+ // chunks are expected.
+ bool expect_idats_;
+ bool is_animated_;
+ bool parsed_signature_;
+ bool parsed_ihdr_;
+ bool parse_completed_;
+ uint32_t reported_frame_count_;
+ uint32_t next_sequence_number_;
+ // True when an fcTL has been parsed but not its corresponding fdAT or IDAT
+ // chunk. Consecutive fcTLs is an error.
+ bool fctl_needs_dat_chunk_;
+ bool ignore_animation_;
+
+ std::unique_ptr<png_byte[]> interlace_buffer_;
+
+ // Value used for the byte_length of a FrameInfo struct to indicate that it is
+ // the first frame and its byte_length is not yet known. 1 is a safe value
+ // since the byte_length field of a frame is at least 12.
+ static constexpr size_t kFirstFrameIndicator = 1;
+
+ // Stores information about a frame until it can be pushed to |frame_info|
+ // once all the frame data has been read from the stream.
+ FrameInfo new_frame_;
+ Vector<FrameInfo, 1> frame_info_;
+
+ size_t ProcessData(const FastSharedBufferReader&,
+ size_t offset,
+ size_t length);
+ // Returns false on a fatal error.
+ bool ParseSize(const FastSharedBufferReader&);
+ // Returns false on an error.
+ bool ParseFrameInfo(const png_byte* data);
+ bool ShouldDecodeWithNewPNG(size_t) const;
+ void StartFrameDecoding(const FastSharedBufferReader&, size_t);
+ // Returns whether the frame was completely decoded.
+ bool ProgressivelyDecodeFirstFrame(const FastSharedBufferReader&);
+ void DecodeFrame(const FastSharedBufferReader&, size_t);
+ void ProcessFdatChunkAsIdat(png_uint_32 fdat_length);
+ // Returns false on a fatal error.
+ bool CheckSequenceNumber(const png_byte* position);
+ bool FirstFrameFullyReceived() const {
+ return !frame_info_.IsEmpty() &&
+ frame_info_[0].byte_length != kFirstFrameIndicator;
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(PNGImageReader);
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/segment_reader.cc b/chromium/third_party/blink/renderer/platform/image-decoders/segment_reader.cc
new file mode 100644
index 00000000000..7ce0ba3acba
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/segment_reader.cc
@@ -0,0 +1,199 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/image-decoders/segment_reader.h"
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
+#include "third_party/skia/include/core/SkData.h"
+#include "third_party/skia/include/core/SkRWBuffer.h"
+
+namespace blink {
+
+// SharedBufferSegmentReader ---------------------------------------------------
+
+// Interface for ImageDecoder to read a SharedBuffer.
+class SharedBufferSegmentReader final : public SegmentReader {
+ WTF_MAKE_NONCOPYABLE(SharedBufferSegmentReader);
+
+ public:
+ SharedBufferSegmentReader(scoped_refptr<SharedBuffer>);
+ size_t size() const override;
+ size_t GetSomeData(const char*& data, size_t position) const override;
+ sk_sp<SkData> GetAsSkData() const override;
+
+ private:
+ scoped_refptr<SharedBuffer> shared_buffer_;
+};
+
+SharedBufferSegmentReader::SharedBufferSegmentReader(
+ scoped_refptr<SharedBuffer> buffer)
+ : shared_buffer_(std::move(buffer)) {}
+
+size_t SharedBufferSegmentReader::size() const {
+ return shared_buffer_->size();
+}
+
+size_t SharedBufferSegmentReader::GetSomeData(const char*& data,
+ size_t position) const {
+ return shared_buffer_->GetSomeData(data, position);
+}
+
+sk_sp<SkData> SharedBufferSegmentReader::GetAsSkData() const {
+ return shared_buffer_->GetAsSkData();
+}
+
+// DataSegmentReader -----------------------------------------------------------
+
+// Interface for ImageDecoder to read an SkData.
+class DataSegmentReader final : public SegmentReader {
+ WTF_MAKE_NONCOPYABLE(DataSegmentReader);
+
+ public:
+ DataSegmentReader(sk_sp<SkData>);
+ size_t size() const override;
+ size_t GetSomeData(const char*& data, size_t position) const override;
+ sk_sp<SkData> GetAsSkData() const override;
+
+ private:
+ sk_sp<SkData> data_;
+};
+
+DataSegmentReader::DataSegmentReader(sk_sp<SkData> data)
+ : data_(std::move(data)) {}
+
+size_t DataSegmentReader::size() const {
+ return data_->size();
+}
+
+size_t DataSegmentReader::GetSomeData(const char*& data,
+ size_t position) const {
+ if (position >= data_->size())
+ return 0;
+
+ data = reinterpret_cast<const char*>(data_->bytes() + position);
+ return data_->size() - position;
+}
+
+sk_sp<SkData> DataSegmentReader::GetAsSkData() const {
+ return data_;
+}
+
+// ROBufferSegmentReader -------------------------------------------------------
+
+class ROBufferSegmentReader final : public SegmentReader {
+ WTF_MAKE_NONCOPYABLE(ROBufferSegmentReader);
+
+ public:
+ ROBufferSegmentReader(sk_sp<SkROBuffer>);
+
+ size_t size() const override;
+ size_t GetSomeData(const char*& data, size_t position) const override;
+ sk_sp<SkData> GetAsSkData() const override;
+
+ private:
+ sk_sp<SkROBuffer> ro_buffer_;
+ // Protects access to mutable fields.
+ mutable Mutex read_mutex_;
+ // Position of the first char in the current block of iter_.
+ mutable size_t position_of_block_;
+ mutable SkROBuffer::Iter iter_;
+};
+
+ROBufferSegmentReader::ROBufferSegmentReader(sk_sp<SkROBuffer> buffer)
+ : ro_buffer_(std::move(buffer)),
+ position_of_block_(0),
+ iter_(ro_buffer_.get()) {}
+
+size_t ROBufferSegmentReader::size() const {
+ return ro_buffer_ ? ro_buffer_->size() : 0;
+}
+
+size_t ROBufferSegmentReader::GetSomeData(const char*& data,
+ size_t position) const {
+ if (!ro_buffer_)
+ return 0;
+
+ MutexLocker lock(read_mutex_);
+
+ if (position < position_of_block_) {
+ // SkROBuffer::Iter only iterates forwards. Start from the beginning.
+ iter_.reset(ro_buffer_.get());
+ position_of_block_ = 0;
+ }
+
+ for (size_t size_of_block = iter_.size(); size_of_block != 0;
+ position_of_block_ += size_of_block, size_of_block = iter_.size()) {
+ DCHECK_LE(position_of_block_, position);
+
+ if (position_of_block_ + size_of_block > position) {
+ // |position| is in this block.
+ const size_t position_in_block = position - position_of_block_;
+ data = static_cast<const char*>(iter_.data()) + position_in_block;
+ return size_of_block - position_in_block;
+ }
+
+ // Move to next block.
+ if (!iter_.next()) {
+ // Reset to the beginning, so future calls can succeed.
+ iter_.reset(ro_buffer_.get());
+ position_of_block_ = 0;
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+static void UnrefROBuffer(const void* ptr, void* context) {
+ static_cast<SkROBuffer*>(context)->unref();
+}
+
+sk_sp<SkData> ROBufferSegmentReader::GetAsSkData() const {
+ if (!ro_buffer_)
+ return nullptr;
+
+ // Check to see if the data is already contiguous.
+ SkROBuffer::Iter iter(ro_buffer_.get());
+ const bool multiple_blocks = iter.next();
+ iter.reset(ro_buffer_.get());
+
+ if (!multiple_blocks) {
+ // Contiguous data. No need to copy.
+ ro_buffer_->ref();
+ return SkData::MakeWithProc(iter.data(), iter.size(), &UnrefROBuffer,
+ ro_buffer_.get());
+ }
+
+ sk_sp<SkData> data = SkData::MakeUninitialized(ro_buffer_->size());
+ char* dst = static_cast<char*>(data->writable_data());
+ do {
+ size_t size = iter.size();
+ memcpy(dst, iter.data(), size);
+ dst += size;
+ } while (iter.next());
+ return data;
+}
+
+// SegmentReader ---------------------------------------------------------------
+
+scoped_refptr<SegmentReader> SegmentReader::CreateFromSharedBuffer(
+ scoped_refptr<SharedBuffer> buffer) {
+ return base::AdoptRef(new SharedBufferSegmentReader(std::move(buffer)));
+}
+
+scoped_refptr<SegmentReader> SegmentReader::CreateFromSkData(
+ sk_sp<SkData> data) {
+ return base::AdoptRef(new DataSegmentReader(std::move(data)));
+}
+
+scoped_refptr<SegmentReader> SegmentReader::CreateFromSkROBuffer(
+ sk_sp<SkROBuffer> buffer) {
+ return base::AdoptRef(new ROBufferSegmentReader(std::move(buffer)));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/segment_reader.h b/chromium/third_party/blink/renderer/platform/image-decoders/segment_reader.h
new file mode 100644
index 00000000000..acc68b38254
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/segment_reader.h
@@ -0,0 +1,52 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_SEGMENT_READER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_SEGMENT_READER_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
+
+class SkData;
+class SkROBuffer;
+
+namespace blink {
+
+// Interface that looks like SharedBuffer. Used by ImageDecoders to use various
+// sources of input including:
+// - SharedBuffer
+// - for when the caller already has a SharedBuffer
+// - SkData
+// - for when the caller already has an SkData
+// - SkROBuffer
+// - for when the caller wants to read/write in different threads
+//
+// Unlike SharedBuffer, this is a read-only interface. There is no way to
+// modify the underlying data source.
+class PLATFORM_EXPORT SegmentReader
+ : public ThreadSafeRefCounted<SegmentReader> {
+ WTF_MAKE_NONCOPYABLE(SegmentReader);
+
+ public:
+ // This version is thread-safe so long as no thread is modifying the
+ // underlying SharedBuffer. This class does not modify it, so that would
+ // mean modifying it in another way.
+ static scoped_refptr<SegmentReader> CreateFromSharedBuffer(
+ scoped_refptr<SharedBuffer>);
+
+ // These versions use thread-safe input, so they are always thread-safe.
+ static scoped_refptr<SegmentReader> CreateFromSkData(sk_sp<SkData>);
+ static scoped_refptr<SegmentReader> CreateFromSkROBuffer(sk_sp<SkROBuffer>);
+
+ SegmentReader() = default;
+ virtual ~SegmentReader() = default;
+ virtual size_t size() const = 0;
+ virtual size_t GetSomeData(const char*& data, size_t position) const = 0;
+ virtual sk_sp<SkData> GetAsSkData() const = 0;
+};
+
+} // namespace blink
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_SEGMENT_READER_H_
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/testing/bad-code.gif b/chromium/third_party/blink/renderer/platform/image-decoders/testing/bad-code.gif
new file mode 100644
index 00000000000..ed519e76794
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/testing/bad-code.gif
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/testing/bad-initial-code.gif b/chromium/third_party/blink/renderer/platform/image-decoders/testing/bad-initial-code.gif
new file mode 100644
index 00000000000..3dc27ec4056
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/testing/bad-initial-code.gif
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/testing/broken.gif b/chromium/third_party/blink/renderer/platform/image-decoders/testing/broken.gif
new file mode 100644
index 00000000000..e3a7c4ebe7d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/testing/broken.gif
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/testing/first-frame-has-greater-size-than-screen-size.gif b/chromium/third_party/blink/renderer/platform/image-decoders/testing/first-frame-has-greater-size-than-screen-size.gif
new file mode 100644
index 00000000000..fda744aaf76
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/testing/first-frame-has-greater-size-than-screen-size.gif
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/testing/invalid-disposal-method.gif b/chromium/third_party/blink/renderer/platform/image-decoders/testing/invalid-disposal-method.gif
new file mode 100644
index 00000000000..158f37677d7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/testing/invalid-disposal-method.gif
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/testing/many-progressive-scans.jpg b/chromium/third_party/blink/renderer/platform/image-decoders/testing/many-progressive-scans.jpg
new file mode 100644
index 00000000000..05a1a00b09b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/testing/many-progressive-scans.jpg
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/testing/radient-bad-terminator.gif b/chromium/third_party/blink/renderer/platform/image-decoders/testing/radient-bad-terminator.gif
new file mode 100644
index 00000000000..b4e9ebdafb7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/testing/radient-bad-terminator.gif
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/testing/radient.gif b/chromium/third_party/blink/renderer/platform/image-decoders/testing/radient.gif
new file mode 100644
index 00000000000..92de286ebbe
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/testing/radient.gif
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/webp/webp_image_decoder.cc b/chromium/third_party/blink/renderer/platform/image-decoders/webp/webp_image_decoder.cc
new file mode 100644
index 00000000000..391da54bb27
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/webp/webp_image_decoder.cc
@@ -0,0 +1,557 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/image-decoders/webp/webp_image_decoder.h"
+
+#include "build/build_config.h"
+#include "third_party/skia/include/core/SkData.h"
+
+#if defined(ARCH_CPU_BIG_ENDIAN)
+#error Blink assumes a little-endian target.
+#endif
+
+#if SK_B32_SHIFT // Output little-endian RGBA pixels (Android).
+inline WEBP_CSP_MODE outputMode(bool hasAlpha) {
+ return hasAlpha ? MODE_rgbA : MODE_RGBA;
+}
+#else // Output little-endian BGRA pixels.
+inline WEBP_CSP_MODE outputMode(bool hasAlpha) {
+ return hasAlpha ? MODE_bgrA : MODE_BGRA;
+}
+#endif
+
+namespace {
+
+// Returns two point ranges (<left, width> pairs) at row |canvasY| which belong
+// to |src| but not |dst|. A range is empty if its width is 0.
+inline void findBlendRangeAtRow(const blink::IntRect& src,
+ const blink::IntRect& dst,
+ int canvasY,
+ int& left1,
+ int& width1,
+ int& left2,
+ int& width2) {
+ SECURITY_DCHECK(canvasY >= src.Y() && canvasY < src.MaxY());
+ left1 = -1;
+ width1 = 0;
+ left2 = -1;
+ width2 = 0;
+
+ if (canvasY < dst.Y() || canvasY >= dst.MaxY() || src.X() >= dst.MaxX() ||
+ src.MaxX() <= dst.X()) {
+ left1 = src.X();
+ width1 = src.Width();
+ return;
+ }
+
+ if (src.X() < dst.X()) {
+ left1 = src.X();
+ width1 = dst.X() - src.X();
+ }
+
+ if (src.MaxX() > dst.MaxX()) {
+ left2 = dst.MaxX();
+ width2 = src.MaxX() - dst.MaxX();
+ }
+}
+
+// alphaBlendPremultiplied and alphaBlendNonPremultiplied are separate methods,
+// even though they only differ by one line. This is done so that the compiler
+// can inline BlendSrcOverDstPremultiplied() and BlensSrcOverDstRaw() calls.
+// For GIF images, this optimization reduces decoding time by 15% for 3MB
+// images.
+void alphaBlendPremultiplied(blink::ImageFrame& src,
+ blink::ImageFrame& dst,
+ int canvasY,
+ int left,
+ int width) {
+ for (int x = 0; x < width; ++x) {
+ int canvasX = left + x;
+ blink::ImageFrame::PixelData* pixel = src.GetAddr(canvasX, canvasY);
+ if (SkGetPackedA32(*pixel) != 0xff) {
+ blink::ImageFrame::PixelData prevPixel = *dst.GetAddr(canvasX, canvasY);
+ blink::ImageFrame::BlendSrcOverDstPremultiplied(pixel, prevPixel);
+ }
+ }
+}
+
+void alphaBlendNonPremultiplied(blink::ImageFrame& src,
+ blink::ImageFrame& dst,
+ int canvasY,
+ int left,
+ int width) {
+ for (int x = 0; x < width; ++x) {
+ int canvasX = left + x;
+ blink::ImageFrame::PixelData* pixel = src.GetAddr(canvasX, canvasY);
+ if (SkGetPackedA32(*pixel) != 0xff) {
+ blink::ImageFrame::PixelData prevPixel = *dst.GetAddr(canvasX, canvasY);
+ blink::ImageFrame::BlendSrcOverDstRaw(pixel, prevPixel);
+ }
+ }
+}
+
+} // namespace
+
+namespace blink {
+
+WEBPImageDecoder::WEBPImageDecoder(AlphaOption alpha_option,
+ const ColorBehavior& color_behavior,
+ size_t max_decoded_bytes)
+ : ImageDecoder(alpha_option, color_behavior, max_decoded_bytes),
+ decoder_(nullptr),
+ format_flags_(0),
+ frame_background_has_alpha_(false),
+ demux_(nullptr),
+ demux_state_(WEBP_DEMUX_PARSING_HEADER),
+ have_already_parsed_this_data_(false),
+ repetition_count_(kAnimationLoopOnce),
+ decoded_height_(0) {
+ blend_function_ = (alpha_option == kAlphaPremultiplied)
+ ? alphaBlendPremultiplied
+ : alphaBlendNonPremultiplied;
+}
+
+WEBPImageDecoder::~WEBPImageDecoder() {
+ Clear();
+}
+
+void WEBPImageDecoder::Clear() {
+ WebPDemuxDelete(demux_);
+ demux_ = nullptr;
+ consolidated_data_.reset();
+ ClearDecoder();
+}
+
+void WEBPImageDecoder::ClearDecoder() {
+ WebPIDelete(decoder_);
+ decoder_ = nullptr;
+ decoded_height_ = 0;
+ frame_background_has_alpha_ = false;
+}
+
+void WEBPImageDecoder::OnSetData(SegmentReader*) {
+ have_already_parsed_this_data_ = false;
+}
+
+int WEBPImageDecoder::RepetitionCount() const {
+ return Failed() ? kAnimationLoopOnce : repetition_count_;
+}
+
+bool WEBPImageDecoder::FrameIsReceivedAtIndex(size_t index) const {
+ if (!demux_ || demux_state_ <= WEBP_DEMUX_PARSING_HEADER)
+ return false;
+ if (!(format_flags_ & ANIMATION_FLAG))
+ return ImageDecoder::FrameIsReceivedAtIndex(index);
+ bool frame_is_received_at_index = index < frame_buffer_cache_.size();
+ return frame_is_received_at_index;
+}
+
+TimeDelta WEBPImageDecoder::FrameDurationAtIndex(size_t index) const {
+ return index < frame_buffer_cache_.size()
+ ? frame_buffer_cache_[index].Duration()
+ : TimeDelta();
+}
+
+bool WEBPImageDecoder::UpdateDemuxer() {
+ if (Failed())
+ return false;
+
+ const unsigned kWebpHeaderSize = 30;
+ if (data_->size() < kWebpHeaderSize)
+ return IsAllDataReceived() ? SetFailed() : false;
+
+ if (have_already_parsed_this_data_)
+ return true;
+
+ have_already_parsed_this_data_ = true;
+
+ if (consolidated_data_ && consolidated_data_->size() >= data_->size()) {
+ // Less data provided than last time. |consolidated_data_| is guaranteed
+ // to be its own copy of the data, so it is safe to keep it.
+ return true;
+ }
+
+ if (IsAllDataReceived() && !consolidated_data_) {
+ consolidated_data_ = data_->GetAsSkData();
+ } else {
+ buffer_.ReserveCapacity(data_->size());
+ while (buffer_.size() < data_->size()) {
+ const char* segment;
+ const size_t bytes = data_->GetSomeData(segment, buffer_.size());
+ DCHECK(bytes);
+ buffer_.Append(segment, bytes);
+ }
+ DCHECK_EQ(buffer_.size(), data_->size());
+ consolidated_data_ =
+ SkData::MakeWithoutCopy(buffer_.data(), buffer_.size());
+ }
+
+ WebPDemuxDelete(demux_);
+ WebPData input_data = {
+ reinterpret_cast<const uint8_t*>(consolidated_data_->data()),
+ consolidated_data_->size()};
+ demux_ = WebPDemuxPartial(&input_data, &demux_state_);
+ if (!demux_ || (IsAllDataReceived() && demux_state_ != WEBP_DEMUX_DONE)) {
+ if (!demux_)
+ consolidated_data_.reset();
+ return SetFailed();
+ }
+
+ DCHECK_GT(demux_state_, WEBP_DEMUX_PARSING_HEADER);
+ if (!WebPDemuxGetI(demux_, WEBP_FF_FRAME_COUNT))
+ return false; // Wait until the encoded image frame data arrives.
+
+ if (!IsDecodedSizeAvailable()) {
+ int width = WebPDemuxGetI(demux_, WEBP_FF_CANVAS_WIDTH);
+ int height = WebPDemuxGetI(demux_, WEBP_FF_CANVAS_HEIGHT);
+ if (!SetSize(width, height))
+ return SetFailed();
+
+ format_flags_ = WebPDemuxGetI(demux_, WEBP_FF_FORMAT_FLAGS);
+ if (!(format_flags_ & ANIMATION_FLAG)) {
+ repetition_count_ = kAnimationNone;
+ } else {
+ // Since we have parsed at least one frame, even if partially,
+ // the global animation (ANIM) properties have been read since
+ // an ANIM chunk must precede the ANMF frame chunks.
+ repetition_count_ = WebPDemuxGetI(demux_, WEBP_FF_LOOP_COUNT);
+ // Repetition count is always <= 16 bits.
+ DCHECK_EQ(repetition_count_, repetition_count_ & 0xffff);
+ // Repetition count is treated as n + 1 cycles for GIF. WebP defines loop
+ // count as the number of cycles, with 0 meaning infinite.
+ repetition_count_ = repetition_count_ == 0 ? kAnimationLoopInfinite
+ : repetition_count_ - 1;
+ // FIXME: Implement ICC profile support for animated images.
+ format_flags_ &= ~ICCP_FLAG;
+ }
+
+ if ((format_flags_ & ICCP_FLAG) && !IgnoresColorSpace())
+ ReadColorProfile();
+ }
+
+ DCHECK(IsDecodedSizeAvailable());
+
+ size_t frame_count = WebPDemuxGetI(demux_, WEBP_FF_FRAME_COUNT);
+ UpdateAggressivePurging(frame_count);
+
+ return true;
+}
+
+void WEBPImageDecoder::OnInitFrameBuffer(size_t frame_index) {
+ // ImageDecoder::InitFrameBuffer does a DCHECK if |frame_index| exists.
+ ImageFrame& buffer = frame_buffer_cache_[frame_index];
+
+ const size_t required_previous_frame_index =
+ buffer.RequiredPreviousFrameIndex();
+ if (required_previous_frame_index == kNotFound) {
+ frame_background_has_alpha_ =
+ !buffer.OriginalFrameRect().Contains(IntRect(IntPoint(), Size()));
+ } else {
+ const ImageFrame& prev_buffer =
+ frame_buffer_cache_[required_previous_frame_index];
+ frame_background_has_alpha_ =
+ prev_buffer.HasAlpha() || (prev_buffer.GetDisposalMethod() ==
+ ImageFrame::kDisposeOverwriteBgcolor);
+ }
+
+ // The buffer is transparent outside the decoded area while the image is
+ // loading. The correct alpha value for the frame will be set when it is fully
+ // decoded.
+ buffer.SetHasAlpha(true);
+}
+
+bool WEBPImageDecoder::CanReusePreviousFrameBuffer(size_t frame_index) const {
+ DCHECK(frame_index < frame_buffer_cache_.size());
+ return frame_buffer_cache_[frame_index].GetAlphaBlendSource() !=
+ ImageFrame::kBlendAtopPreviousFrame;
+}
+
+void WEBPImageDecoder::ClearFrameBuffer(size_t frame_index) {
+ if (demux_ && demux_state_ >= WEBP_DEMUX_PARSED_HEADER &&
+ frame_buffer_cache_[frame_index].GetStatus() ==
+ ImageFrame::kFramePartial) {
+ // Clear the decoder state so that this partial frame can be decoded again
+ // when requested.
+ ClearDecoder();
+ }
+ ImageDecoder::ClearFrameBuffer(frame_index);
+}
+
+void WEBPImageDecoder::ReadColorProfile() {
+ WebPChunkIterator chunk_iterator;
+ if (!WebPDemuxGetChunk(demux_, "ICCP", 1, &chunk_iterator)) {
+ WebPDemuxReleaseChunkIterator(&chunk_iterator);
+ return;
+ }
+
+ const char* profile_data =
+ reinterpret_cast<const char*>(chunk_iterator.chunk.bytes);
+ size_t profile_size = chunk_iterator.chunk.size;
+
+ sk_sp<SkColorSpace> color_space =
+ SkColorSpace::MakeICC(profile_data, profile_size);
+ if (color_space) {
+ if (color_space->type() == SkColorSpace::kRGB_Type)
+ SetEmbeddedColorSpace(std::move(color_space));
+ } else {
+ DLOG(ERROR) << "Failed to parse image ICC profile";
+ }
+
+ WebPDemuxReleaseChunkIterator(&chunk_iterator);
+}
+
+void WEBPImageDecoder::ApplyPostProcessing(size_t frame_index) {
+ ImageFrame& buffer = frame_buffer_cache_[frame_index];
+ int width;
+ int decoded_height;
+ if (!WebPIDecGetRGB(decoder_, &decoded_height, &width, nullptr, nullptr))
+ return; // See also https://bugs.webkit.org/show_bug.cgi?id=74062
+ if (decoded_height <= 0)
+ return;
+
+ const IntRect& frame_rect = buffer.OriginalFrameRect();
+ SECURITY_DCHECK(width == frame_rect.Width());
+ SECURITY_DCHECK(decoded_height <= frame_rect.Height());
+ const int left = frame_rect.X();
+ const int top = frame_rect.Y();
+
+ // TODO (msarett):
+ // Here we apply the color space transformation to the dst space.
+ // It does not really make sense to transform to a gamma-encoded
+ // space and then immediately after, perform a linear premultiply
+ // and linear blending. Can we find a way to perform the
+ // premultiplication and blending in a linear space?
+ SkColorSpaceXform* xform = ColorTransform();
+ if (xform) {
+ const SkColorSpaceXform::ColorFormat kSrcFormat =
+ SkColorSpaceXform::kBGRA_8888_ColorFormat;
+ const SkColorSpaceXform::ColorFormat kDstFormat =
+ SkColorSpaceXform::kRGBA_8888_ColorFormat;
+ for (int y = decoded_height_; y < decoded_height; ++y) {
+ const int canvas_y = top + y;
+ uint8_t* row = reinterpret_cast<uint8_t*>(buffer.GetAddr(left, canvas_y));
+ bool color_converison_successful = xform->apply(
+ kDstFormat, row, kSrcFormat, row, width, kUnpremul_SkAlphaType);
+ DCHECK(color_converison_successful);
+ uint8_t* pixel = row;
+ for (int x = 0; x < width; ++x, pixel += 4) {
+ const int canvas_x = left + x;
+ buffer.SetRGBA(canvas_x, canvas_y, pixel[0], pixel[1], pixel[2],
+ pixel[3]);
+ }
+ }
+ }
+
+ // During the decoding of the current frame, we may have set some pixels to be
+ // transparent (i.e. alpha < 255). If the alpha blend source was
+ // 'BlendAtopPreviousFrame', the values of these pixels should be determined
+ // by blending them against the pixels of the corresponding previous frame.
+ // Compute the correct opaque values now.
+ // FIXME: This could be avoided if libwebp decoder had an API that used the
+ // previous required frame to do the alpha-blending by itself.
+ if ((format_flags_ & ANIMATION_FLAG) && frame_index &&
+ buffer.GetAlphaBlendSource() == ImageFrame::kBlendAtopPreviousFrame &&
+ buffer.RequiredPreviousFrameIndex() != kNotFound) {
+ ImageFrame& prev_buffer = frame_buffer_cache_[frame_index - 1];
+ DCHECK_EQ(prev_buffer.GetStatus(), ImageFrame::kFrameComplete);
+ ImageFrame::DisposalMethod prev_disposal_method =
+ prev_buffer.GetDisposalMethod();
+ if (prev_disposal_method == ImageFrame::kDisposeKeep) {
+ // Blend transparent pixels with pixels in previous canvas.
+ for (int y = decoded_height_; y < decoded_height; ++y) {
+ blend_function_(buffer, prev_buffer, top + y, left, width);
+ }
+ } else if (prev_disposal_method == ImageFrame::kDisposeOverwriteBgcolor) {
+ const IntRect& prev_rect = prev_buffer.OriginalFrameRect();
+ // We need to blend a transparent pixel with the starting value (from just
+ // after the InitFrame() call). If the pixel belongs to prev_rect, the
+ // starting value was fully transparent, so this is a no-op. Otherwise, we
+ // need to blend against the pixel from the previous canvas.
+ for (int y = decoded_height_; y < decoded_height; ++y) {
+ int canvas_y = top + y;
+ int left1, width1, left2, width2;
+ findBlendRangeAtRow(frame_rect, prev_rect, canvas_y, left1, width1,
+ left2, width2);
+ if (width1 > 0)
+ blend_function_(buffer, prev_buffer, canvas_y, left1, width1);
+ if (width2 > 0)
+ blend_function_(buffer, prev_buffer, canvas_y, left2, width2);
+ }
+ }
+ }
+
+ decoded_height_ = decoded_height;
+ buffer.SetPixelsChanged(true);
+}
+
+size_t WEBPImageDecoder::DecodeFrameCount() {
+ // If UpdateDemuxer() fails, return the existing number of frames. This way
+ // if we get halfway through the image before decoding fails, we won't
+ // suddenly start reporting that the image has zero frames.
+ return UpdateDemuxer() ? WebPDemuxGetI(demux_, WEBP_FF_FRAME_COUNT)
+ : frame_buffer_cache_.size();
+}
+
+void WEBPImageDecoder::InitializeNewFrame(size_t index) {
+ if (!(format_flags_ & ANIMATION_FLAG)) {
+ DCHECK(!index);
+ return;
+ }
+ WebPIterator animated_frame;
+ WebPDemuxGetFrame(demux_, index + 1, &animated_frame);
+ DCHECK_EQ(animated_frame.complete, 1);
+ ImageFrame* buffer = &frame_buffer_cache_[index];
+ IntRect frame_rect(animated_frame.x_offset, animated_frame.y_offset,
+ animated_frame.width, animated_frame.height);
+ buffer->SetOriginalFrameRect(
+ Intersection(frame_rect, IntRect(IntPoint(), Size())));
+ buffer->SetDuration(TimeDelta::FromMilliseconds(animated_frame.duration));
+ buffer->SetDisposalMethod(animated_frame.dispose_method ==
+ WEBP_MUX_DISPOSE_BACKGROUND
+ ? ImageFrame::kDisposeOverwriteBgcolor
+ : ImageFrame::kDisposeKeep);
+ buffer->SetAlphaBlendSource(animated_frame.blend_method == WEBP_MUX_BLEND
+ ? ImageFrame::kBlendAtopPreviousFrame
+ : ImageFrame::kBlendAtopBgcolor);
+ buffer->SetRequiredPreviousFrameIndex(
+ FindRequiredPreviousFrame(index, !animated_frame.has_alpha));
+ WebPDemuxReleaseIterator(&animated_frame);
+}
+
+void WEBPImageDecoder::Decode(size_t index) {
+ if (Failed())
+ return;
+
+ Vector<size_t> frames_to_decode = FindFramesToDecode(index);
+
+ DCHECK(demux_);
+ for (auto i = frames_to_decode.rbegin(); i != frames_to_decode.rend(); ++i) {
+ if ((format_flags_ & ANIMATION_FLAG) && !InitFrameBuffer(*i)) {
+ SetFailed();
+ return;
+ }
+
+ WebPIterator webp_frame;
+ if (!WebPDemuxGetFrame(demux_, *i + 1, &webp_frame)) {
+ SetFailed();
+ } else {
+ DecodeSingleFrame(webp_frame.fragment.bytes, webp_frame.fragment.size,
+ *i);
+ WebPDemuxReleaseIterator(&webp_frame);
+ }
+ if (Failed())
+ return;
+
+ // If this returns false, we need more data to continue decoding.
+ if (!PostDecodeProcessing(*i))
+ break;
+ }
+
+ // It is also a fatal error if all data is received and we have decoded all
+ // frames available but the file is truncated.
+ if (index >= frame_buffer_cache_.size() - 1 && IsAllDataReceived() &&
+ demux_ && demux_state_ != WEBP_DEMUX_DONE)
+ SetFailed();
+}
+
+bool WEBPImageDecoder::DecodeSingleFrame(const uint8_t* data_bytes,
+ size_t data_size,
+ size_t frame_index) {
+ if (Failed())
+ return false;
+
+ DCHECK(IsDecodedSizeAvailable());
+
+ DCHECK_GT(frame_buffer_cache_.size(), frame_index);
+ ImageFrame& buffer = frame_buffer_cache_[frame_index];
+ DCHECK_NE(buffer.GetStatus(), ImageFrame::kFrameComplete);
+
+ if (buffer.GetStatus() == ImageFrame::kFrameEmpty) {
+ if (!buffer.AllocatePixelData(Size().Width(), Size().Height(),
+ ColorSpaceForSkImages()))
+ return SetFailed();
+ buffer.ZeroFillPixelData();
+ buffer.SetStatus(ImageFrame::kFramePartial);
+ // The buffer is transparent outside the decoded area while the image is
+ // loading. The correct alpha value for the frame will be set when it is
+ // fully decoded.
+ buffer.SetHasAlpha(true);
+ buffer.SetOriginalFrameRect(IntRect(IntPoint(), Size()));
+ }
+
+ const IntRect& frame_rect = buffer.OriginalFrameRect();
+ if (!decoder_) {
+ WEBP_CSP_MODE mode = outputMode(format_flags_ & ALPHA_FLAG);
+ if (!premultiply_alpha_)
+ mode = outputMode(false);
+ if (ColorTransform()) {
+ // Swizzling between RGBA and BGRA is zero cost in a color transform.
+ // So when we have a color transform, we should decode to whatever is
+ // easiest for libwebp, and then let the color transform swizzle if
+ // necessary.
+ // Lossy webp is encoded as YUV (so RGBA and BGRA are the same cost).
+ // Lossless webp is encoded as BGRA. This means decoding to BGRA is
+ // either faster or the same cost as RGBA.
+ mode = MODE_BGRA;
+ }
+ WebPInitDecBuffer(&decoder_buffer_);
+ decoder_buffer_.colorspace = mode;
+ decoder_buffer_.u.RGBA.stride =
+ Size().Width() * sizeof(ImageFrame::PixelData);
+ decoder_buffer_.u.RGBA.size =
+ decoder_buffer_.u.RGBA.stride * frame_rect.Height();
+ decoder_buffer_.is_external_memory = 1;
+ decoder_ = WebPINewDecoder(&decoder_buffer_);
+ if (!decoder_)
+ return SetFailed();
+ }
+
+ decoder_buffer_.u.RGBA.rgba = reinterpret_cast<uint8_t*>(
+ buffer.GetAddr(frame_rect.X(), frame_rect.Y()));
+
+ switch (WebPIUpdate(decoder_, data_bytes, data_size)) {
+ case VP8_STATUS_OK:
+ ApplyPostProcessing(frame_index);
+ buffer.SetHasAlpha((format_flags_ & ALPHA_FLAG) ||
+ frame_background_has_alpha_);
+ buffer.SetStatus(ImageFrame::kFrameComplete);
+ ClearDecoder();
+ return true;
+ case VP8_STATUS_SUSPENDED:
+ if (!IsAllDataReceived() && !FrameIsReceivedAtIndex(frame_index)) {
+ ApplyPostProcessing(frame_index);
+ return false;
+ }
+ FALLTHROUGH;
+ default:
+ Clear();
+ return SetFailed();
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/webp/webp_image_decoder.h b/chromium/third_party/blink/renderer/platform/image-decoders/webp/webp_image_decoder.h
new file mode 100644
index 00000000000..651d35d0534
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/webp/webp_image_decoder.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_WEBP_WEBP_IMAGE_DECODER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_WEBP_WEBP_IMAGE_DECODER_H_
+
+#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "webp/decode.h"
+#include "webp/demux.h"
+
+class SkData;
+
+namespace blink {
+
+class PLATFORM_EXPORT WEBPImageDecoder final : public ImageDecoder {
+ WTF_MAKE_NONCOPYABLE(WEBPImageDecoder);
+
+ public:
+ WEBPImageDecoder(AlphaOption, const ColorBehavior&, size_t max_decoded_bytes);
+ ~WEBPImageDecoder() override;
+
+ // ImageDecoder:
+ String FilenameExtension() const override { return "webp"; }
+ void OnSetData(SegmentReader* data) override;
+ int RepetitionCount() const override;
+ bool FrameIsReceivedAtIndex(size_t) const override;
+ TimeDelta FrameDurationAtIndex(size_t) const override;
+
+ private:
+ // ImageDecoder:
+ virtual void DecodeSize() { UpdateDemuxer(); }
+ size_t DecodeFrameCount() override;
+ void InitializeNewFrame(size_t) override;
+ void Decode(size_t) override;
+
+ bool DecodeSingleFrame(const uint8_t* data_bytes,
+ size_t data_size,
+ size_t frame_index);
+
+ // For WebP images, the frame status needs to be FrameComplete to decode
+ // subsequent frames that depend on frame |index|. The reason for this is that
+ // WebP uses the previous frame for alpha blending, in ApplyPostProcessing().
+ //
+ // Before calling this, verify that frame |index| exists by checking that
+ // |index| is smaller than |frame_buffer_cache_|.size().
+ bool FrameStatusSufficientForSuccessors(size_t index) override {
+ DCHECK(index < frame_buffer_cache_.size());
+ return frame_buffer_cache_[index].GetStatus() == ImageFrame::kFrameComplete;
+ }
+
+ WebPIDecoder* decoder_;
+ WebPDecBuffer decoder_buffer_;
+ int format_flags_;
+ bool frame_background_has_alpha_;
+
+ void ReadColorProfile();
+ bool UpdateDemuxer();
+
+ // Set |frame_background_has_alpha_| based on this frame's characteristics.
+ // Before calling this method, the caller must verify that the frame exists.
+ void OnInitFrameBuffer(size_t frame_index) override;
+
+ // When the blending method of this frame is BlendAtopPreviousFrame, the
+ // previous frame's buffer is necessary to decode this frame in
+ // ApplyPostProcessing, so we can't take over the data. Before calling this
+ // method, the caller must verify that the frame exists.
+ bool CanReusePreviousFrameBuffer(size_t frame_index) const override;
+
+ void ApplyPostProcessing(size_t frame_index);
+ void ClearFrameBuffer(size_t frame_index) override;
+
+ WebPDemuxer* demux_;
+ WebPDemuxState demux_state_;
+ bool have_already_parsed_this_data_;
+ int repetition_count_;
+ int decoded_height_;
+
+ typedef void (*AlphaBlendFunction)(ImageFrame&, ImageFrame&, int, int, int);
+ AlphaBlendFunction blend_function_;
+
+ void Clear();
+ void ClearDecoder();
+
+ // This will point to one of three things:
+ // - the SegmentReader's data, if contiguous.
+ // - its own copy, if not, and all data was received initially.
+ // - |buffer_|, if streaming.
+ sk_sp<SkData> consolidated_data_;
+ Vector<char> buffer_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/webp/webp_image_decoder_test.cc b/chromium/third_party/blink/renderer/platform/image-decoders/webp/webp_image_decoder_test.cc
new file mode 100644
index 00000000000..975d3d1b84f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/webp/webp_image_decoder_test.cc
@@ -0,0 +1,539 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/renderer/platform/image-decoders/webp/webp_image_decoder.h"
+
+#include <memory>
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/web_data.h"
+#include "third_party/blink/public/platform/web_size.h"
+#include "third_party/blink/renderer/platform/image-decoders/image_decoder_test_helpers.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/wtf/dtoa/utils.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+namespace {
+
+struct AnimParam {
+ int x_offset, y_offset, width, height;
+ ImageFrame::DisposalMethod disposal_method;
+ ImageFrame::AlphaBlendSource alpha_blend_source;
+ TimeDelta duration;
+ bool has_alpha;
+};
+
+std::unique_ptr<ImageDecoder> CreateWEBPDecoder(
+ ImageDecoder::AlphaOption alpha_option) {
+ return std::make_unique<WEBPImageDecoder>(
+ alpha_option, ColorBehavior::TransformToSRGB(),
+ ImageDecoder::kNoDecodedImageByteLimit);
+}
+
+std::unique_ptr<ImageDecoder> CreateWEBPDecoder() {
+ return CreateWEBPDecoder(ImageDecoder::kAlphaNotPremultiplied);
+}
+
+// If 'parse_error_expected' is true, error is expected during parse
+// (FrameCount() call); else error is expected during decode
+// (FrameBufferAtIndex() call).
+void TestInvalidImage(const char* webp_file, bool parse_error_expected) {
+ std::unique_ptr<ImageDecoder> decoder = CreateWEBPDecoder();
+
+ scoped_refptr<SharedBuffer> data = ReadFile(webp_file);
+ ASSERT_TRUE(data.get());
+ decoder->SetData(data.get(), true);
+
+ if (parse_error_expected) {
+ EXPECT_EQ(0u, decoder->FrameCount());
+ EXPECT_FALSE(decoder->DecodeFrameBufferAtIndex(0));
+ } else {
+ EXPECT_GT(decoder->FrameCount(), 0u);
+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(0);
+ ASSERT_TRUE(frame);
+ EXPECT_EQ(ImageFrame::kFramePartial, frame->GetStatus());
+ }
+ EXPECT_EQ(kAnimationLoopOnce, decoder->RepetitionCount());
+ EXPECT_TRUE(decoder->Failed());
+}
+
+} // anonymous namespace
+
+TEST(AnimatedWebPTests, uniqueGenerationIDs) {
+ std::unique_ptr<ImageDecoder> decoder = CreateWEBPDecoder();
+
+ scoped_refptr<SharedBuffer> data =
+ ReadFile("/images/resources/webp-animated.webp");
+ ASSERT_TRUE(data.get());
+ decoder->SetData(data.get(), true);
+
+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(0);
+ uint32_t generation_id0 = frame->Bitmap().getGenerationID();
+ frame = decoder->DecodeFrameBufferAtIndex(1);
+ uint32_t generation_id1 = frame->Bitmap().getGenerationID();
+
+ EXPECT_TRUE(generation_id0 != generation_id1);
+}
+
+TEST(AnimatedWebPTests, verifyAnimationParametersTransparentImage) {
+ std::unique_ptr<ImageDecoder> decoder = CreateWEBPDecoder();
+ EXPECT_EQ(kAnimationLoopOnce, decoder->RepetitionCount());
+
+ scoped_refptr<SharedBuffer> data =
+ ReadFile("/images/resources/webp-animated.webp");
+ ASSERT_TRUE(data.get());
+ decoder->SetData(data.get(), true);
+
+ const int kCanvasWidth = 11;
+ const int kCanvasHeight = 29;
+ const AnimParam kFrameParameters[] = {
+ {0, 0, 11, 29, ImageFrame::kDisposeKeep,
+ ImageFrame::kBlendAtopPreviousFrame, TimeDelta::FromMilliseconds(1000),
+ true},
+ {2, 10, 7, 17, ImageFrame::kDisposeKeep,
+ ImageFrame::kBlendAtopPreviousFrame, TimeDelta::FromMilliseconds(500),
+ true},
+ {2, 2, 7, 16, ImageFrame::kDisposeKeep,
+ ImageFrame::kBlendAtopPreviousFrame, TimeDelta::FromMilliseconds(1000),
+ true},
+ };
+
+ for (size_t i = 0; i < WTF_ARRAY_LENGTH(kFrameParameters); ++i) {
+ const ImageFrame* const frame = decoder->DecodeFrameBufferAtIndex(i);
+ EXPECT_EQ(ImageFrame::kFrameComplete, frame->GetStatus());
+ EXPECT_EQ(kCanvasWidth, frame->Bitmap().width());
+ EXPECT_EQ(kCanvasHeight, frame->Bitmap().height());
+ EXPECT_EQ(kFrameParameters[i].x_offset, frame->OriginalFrameRect().X());
+ EXPECT_EQ(kFrameParameters[i].y_offset, frame->OriginalFrameRect().Y());
+ EXPECT_EQ(kFrameParameters[i].width, frame->OriginalFrameRect().Width());
+ EXPECT_EQ(kFrameParameters[i].height, frame->OriginalFrameRect().Height());
+ EXPECT_EQ(kFrameParameters[i].disposal_method, frame->GetDisposalMethod());
+ EXPECT_EQ(kFrameParameters[i].alpha_blend_source,
+ frame->GetAlphaBlendSource());
+ EXPECT_EQ(kFrameParameters[i].duration, frame->Duration());
+ EXPECT_EQ(kFrameParameters[i].has_alpha, frame->HasAlpha());
+ }
+
+ EXPECT_EQ(WTF_ARRAY_LENGTH(kFrameParameters), decoder->FrameCount());
+ EXPECT_EQ(kAnimationLoopInfinite, decoder->RepetitionCount());
+}
+
+TEST(AnimatedWebPTests,
+ verifyAnimationParametersOpaqueFramesTransparentBackground) {
+ std::unique_ptr<ImageDecoder> decoder = CreateWEBPDecoder();
+ EXPECT_EQ(kAnimationLoopOnce, decoder->RepetitionCount());
+
+ scoped_refptr<SharedBuffer> data =
+ ReadFile("/images/resources/webp-animated-opaque.webp");
+ ASSERT_TRUE(data.get());
+ decoder->SetData(data.get(), true);
+
+ const int kCanvasWidth = 94;
+ const int kCanvasHeight = 87;
+ const AnimParam kFrameParameters[] = {
+ {4, 10, 33, 32, ImageFrame::kDisposeOverwriteBgcolor,
+ ImageFrame::kBlendAtopPreviousFrame, TimeDelta::FromMilliseconds(1000),
+ true},
+ {34, 30, 33, 32, ImageFrame::kDisposeOverwriteBgcolor,
+ ImageFrame::kBlendAtopPreviousFrame, TimeDelta::FromMilliseconds(1000),
+ true},
+ {62, 50, 32, 32, ImageFrame::kDisposeOverwriteBgcolor,
+ ImageFrame::kBlendAtopPreviousFrame, TimeDelta::FromMilliseconds(1000),
+ true},
+ {10, 54, 32, 33, ImageFrame::kDisposeOverwriteBgcolor,
+ ImageFrame::kBlendAtopPreviousFrame, TimeDelta::FromMilliseconds(1000),
+ true},
+ };
+
+ for (size_t i = 0; i < WTF_ARRAY_LENGTH(kFrameParameters); ++i) {
+ const ImageFrame* const frame = decoder->DecodeFrameBufferAtIndex(i);
+ EXPECT_EQ(ImageFrame::kFrameComplete, frame->GetStatus());
+ EXPECT_EQ(kCanvasWidth, frame->Bitmap().width());
+ EXPECT_EQ(kCanvasHeight, frame->Bitmap().height());
+ EXPECT_EQ(kFrameParameters[i].x_offset, frame->OriginalFrameRect().X());
+ EXPECT_EQ(kFrameParameters[i].y_offset, frame->OriginalFrameRect().Y());
+ EXPECT_EQ(kFrameParameters[i].width, frame->OriginalFrameRect().Width());
+ EXPECT_EQ(kFrameParameters[i].height, frame->OriginalFrameRect().Height());
+ EXPECT_EQ(kFrameParameters[i].disposal_method, frame->GetDisposalMethod());
+ EXPECT_EQ(kFrameParameters[i].alpha_blend_source,
+ frame->GetAlphaBlendSource());
+ EXPECT_EQ(kFrameParameters[i].duration, frame->Duration());
+ EXPECT_EQ(kFrameParameters[i].has_alpha, frame->HasAlpha());
+ }
+
+ EXPECT_EQ(WTF_ARRAY_LENGTH(kFrameParameters), decoder->FrameCount());
+ EXPECT_EQ(kAnimationLoopInfinite, decoder->RepetitionCount());
+}
+
+TEST(AnimatedWebPTests, verifyAnimationParametersBlendOverwrite) {
+ std::unique_ptr<ImageDecoder> decoder = CreateWEBPDecoder();
+ EXPECT_EQ(kAnimationLoopOnce, decoder->RepetitionCount());
+
+ scoped_refptr<SharedBuffer> data =
+ ReadFile("/images/resources/webp-animated-no-blend.webp");
+ ASSERT_TRUE(data.get());
+ decoder->SetData(data.get(), true);
+
+ const int kCanvasWidth = 94;
+ const int kCanvasHeight = 87;
+ const AnimParam kFrameParameters[] = {
+ {4, 10, 33, 32, ImageFrame::kDisposeOverwriteBgcolor,
+ ImageFrame::kBlendAtopBgcolor, TimeDelta::FromMilliseconds(1000), true},
+ {34, 30, 33, 32, ImageFrame::kDisposeOverwriteBgcolor,
+ ImageFrame::kBlendAtopBgcolor, TimeDelta::FromMilliseconds(1000), true},
+ {62, 50, 32, 32, ImageFrame::kDisposeOverwriteBgcolor,
+ ImageFrame::kBlendAtopBgcolor, TimeDelta::FromMilliseconds(1000), true},
+ {10, 54, 32, 33, ImageFrame::kDisposeOverwriteBgcolor,
+ ImageFrame::kBlendAtopBgcolor, TimeDelta::FromMilliseconds(1000), true},
+ };
+
+ for (size_t i = 0; i < WTF_ARRAY_LENGTH(kFrameParameters); ++i) {
+ const ImageFrame* const frame = decoder->DecodeFrameBufferAtIndex(i);
+ EXPECT_EQ(ImageFrame::kFrameComplete, frame->GetStatus());
+ EXPECT_EQ(kCanvasWidth, frame->Bitmap().width());
+ EXPECT_EQ(kCanvasHeight, frame->Bitmap().height());
+ EXPECT_EQ(kFrameParameters[i].x_offset, frame->OriginalFrameRect().X());
+ EXPECT_EQ(kFrameParameters[i].y_offset, frame->OriginalFrameRect().Y());
+ EXPECT_EQ(kFrameParameters[i].width, frame->OriginalFrameRect().Width());
+ EXPECT_EQ(kFrameParameters[i].height, frame->OriginalFrameRect().Height());
+ EXPECT_EQ(kFrameParameters[i].disposal_method, frame->GetDisposalMethod());
+ EXPECT_EQ(kFrameParameters[i].alpha_blend_source,
+ frame->GetAlphaBlendSource());
+ EXPECT_EQ(kFrameParameters[i].duration, frame->Duration());
+ EXPECT_EQ(kFrameParameters[i].has_alpha, frame->HasAlpha());
+ }
+
+ EXPECT_EQ(WTF_ARRAY_LENGTH(kFrameParameters), decoder->FrameCount());
+ EXPECT_EQ(kAnimationLoopInfinite, decoder->RepetitionCount());
+}
+
+TEST(AnimatedWebPTests, parseAndDecodeByteByByte) {
+ TestByteByByteDecode(&CreateWEBPDecoder,
+ "/images/resources/webp-animated.webp", 3u,
+ kAnimationLoopInfinite);
+ TestByteByByteDecode(&CreateWEBPDecoder,
+ "/images/resources/webp-animated-icc-xmp.webp", 13u,
+ 31999);
+}
+
+TEST(AnimatedWebPTests, invalidImages) {
+ // ANMF chunk size is smaller than ANMF header size.
+ TestInvalidImage("/images/resources/invalid-animated-webp.webp", true);
+ // One of the frame rectangles extends outside the image boundary.
+ TestInvalidImage("/images/resources/invalid-animated-webp3.webp", true);
+}
+
+TEST(AnimatedWebPTests, truncatedLastFrame) {
+ std::unique_ptr<ImageDecoder> decoder = CreateWEBPDecoder();
+
+ scoped_refptr<SharedBuffer> data =
+ ReadFile("/images/resources/invalid-animated-webp2.webp");
+ ASSERT_TRUE(data.get());
+ decoder->SetData(data.get(), true);
+
+ size_t frame_count = 8;
+ EXPECT_EQ(frame_count, decoder->FrameCount());
+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(0);
+ ASSERT_TRUE(frame);
+ EXPECT_EQ(ImageFrame::kFrameComplete, frame->GetStatus());
+ EXPECT_FALSE(decoder->Failed());
+ frame = decoder->DecodeFrameBufferAtIndex(frame_count - 1);
+ ASSERT_TRUE(frame);
+ EXPECT_EQ(ImageFrame::kFramePartial, frame->GetStatus());
+ EXPECT_TRUE(decoder->Failed());
+ frame = decoder->DecodeFrameBufferAtIndex(0);
+ ASSERT_TRUE(frame);
+ EXPECT_EQ(ImageFrame::kFrameComplete, frame->GetStatus());
+}
+
+TEST(AnimatedWebPTests, truncatedInBetweenFrame) {
+ std::unique_ptr<ImageDecoder> decoder = CreateWEBPDecoder();
+
+ const Vector<char> full_data =
+ ReadFile("/images/resources/invalid-animated-webp4.webp")->Copy();
+ scoped_refptr<SharedBuffer> data =
+ SharedBuffer::Create(full_data.data(), full_data.size() - 1);
+ decoder->SetData(data.get(), false);
+
+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(1);
+ ASSERT_TRUE(frame);
+ EXPECT_EQ(ImageFrame::kFrameComplete, frame->GetStatus());
+ frame = decoder->DecodeFrameBufferAtIndex(2);
+ ASSERT_TRUE(frame);
+ EXPECT_EQ(ImageFrame::kFramePartial, frame->GetStatus());
+ EXPECT_TRUE(decoder->Failed());
+}
+
+// Tests for a crash that used to happen for a specific file with specific
+// sequence of method calls.
+TEST(AnimatedWebPTests, reproCrash) {
+ std::unique_ptr<ImageDecoder> decoder = CreateWEBPDecoder();
+
+ scoped_refptr<SharedBuffer> full_data_buffer =
+ ReadFile("/images/resources/invalid_vp8_vp8x.webp");
+ ASSERT_TRUE(full_data_buffer.get());
+ const Vector<char> full_data = full_data_buffer->Copy();
+
+ // Parse partial data up to which error in bitstream is not detected.
+ const size_t kPartialSize = 32768;
+ ASSERT_GT(full_data.size(), kPartialSize);
+ scoped_refptr<SharedBuffer> data =
+ SharedBuffer::Create(full_data.data(), kPartialSize);
+ decoder->SetData(data.get(), false);
+ EXPECT_EQ(1u, decoder->FrameCount());
+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(0);
+ ASSERT_TRUE(frame);
+ EXPECT_EQ(ImageFrame::kFramePartial, frame->GetStatus());
+ EXPECT_FALSE(decoder->Failed());
+
+ // Parse full data now. The error in bitstream should now be detected.
+ decoder->SetData(full_data_buffer.get(), true);
+ EXPECT_EQ(1u, decoder->FrameCount());
+ frame = decoder->DecodeFrameBufferAtIndex(0);
+ ASSERT_TRUE(frame);
+ EXPECT_EQ(ImageFrame::kFramePartial, frame->GetStatus());
+ EXPECT_EQ(kAnimationLoopOnce, decoder->RepetitionCount());
+ EXPECT_TRUE(decoder->Failed());
+}
+
+TEST(AnimatedWebPTests, progressiveDecode) {
+ TestProgressiveDecoding(&CreateWEBPDecoder,
+ "/images/resources/webp-animated.webp");
+}
+
+TEST(AnimatedWebPTests, frameIsCompleteAndDuration) {
+ std::unique_ptr<ImageDecoder> decoder = CreateWEBPDecoder();
+
+ scoped_refptr<SharedBuffer> data_buffer =
+ ReadFile("/images/resources/webp-animated.webp");
+ ASSERT_TRUE(data_buffer.get());
+ const Vector<char> data = data_buffer->Copy();
+
+ ASSERT_GE(data.size(), 10u);
+ scoped_refptr<SharedBuffer> temp_data =
+ SharedBuffer::Create(data.data(), data.size() - 10);
+ decoder->SetData(temp_data.get(), false);
+
+ EXPECT_EQ(2u, decoder->FrameCount());
+ EXPECT_FALSE(decoder->Failed());
+ EXPECT_TRUE(decoder->FrameIsReceivedAtIndex(0));
+ EXPECT_EQ(TimeDelta::FromMilliseconds(1000),
+ decoder->FrameDurationAtIndex(0));
+ EXPECT_TRUE(decoder->FrameIsReceivedAtIndex(1));
+ EXPECT_EQ(TimeDelta::FromMilliseconds(500), decoder->FrameDurationAtIndex(1));
+
+ decoder->SetData(data_buffer.get(), true);
+ EXPECT_EQ(3u, decoder->FrameCount());
+ EXPECT_TRUE(decoder->FrameIsReceivedAtIndex(0));
+ EXPECT_EQ(TimeDelta::FromMilliseconds(1000),
+ decoder->FrameDurationAtIndex(0));
+ EXPECT_TRUE(decoder->FrameIsReceivedAtIndex(1));
+ EXPECT_EQ(TimeDelta::FromMilliseconds(500), decoder->FrameDurationAtIndex(1));
+ EXPECT_TRUE(decoder->FrameIsReceivedAtIndex(2));
+ EXPECT_EQ(TimeDelta::FromMilliseconds(1000),
+ decoder->FrameDurationAtIndex(2));
+}
+
+TEST(AnimatedWebPTests, updateRequiredPreviousFrameAfterFirstDecode) {
+ TestUpdateRequiredPreviousFrameAfterFirstDecode(
+ &CreateWEBPDecoder, "/images/resources/webp-animated.webp");
+}
+
+TEST(AnimatedWebPTests, randomFrameDecode) {
+ TestRandomFrameDecode(&CreateWEBPDecoder,
+ "/images/resources/webp-animated.webp");
+ TestRandomFrameDecode(&CreateWEBPDecoder,
+ "/images/resources/webp-animated-opaque.webp");
+ TestRandomFrameDecode(&CreateWEBPDecoder,
+ "/images/resources/webp-animated-large.webp");
+ TestRandomFrameDecode(&CreateWEBPDecoder,
+ "/images/resources/webp-animated-icc-xmp.webp");
+}
+
+TEST(AnimatedWebPTests, randomDecodeAfterClearFrameBufferCache) {
+ TestRandomDecodeAfterClearFrameBufferCache(
+ &CreateWEBPDecoder, "/images/resources/webp-animated.webp");
+ TestRandomDecodeAfterClearFrameBufferCache(
+ &CreateWEBPDecoder, "/images/resources/webp-animated-opaque.webp");
+ TestRandomDecodeAfterClearFrameBufferCache(
+ &CreateWEBPDecoder, "/images/resources/webp-animated-large.webp");
+ TestRandomDecodeAfterClearFrameBufferCache(
+ &CreateWEBPDecoder, "/images/resources/webp-animated-icc-xmp.webp");
+}
+
+// This test is disabled since it timed out on the Windows bot. See
+// crrev.com/962853004
+TEST(AnimatedWebPTests,
+ DISABLED_resumePartialDecodeAfterClearFrameBufferCache) {
+ TestResumePartialDecodeAfterClearFrameBufferCache(
+ &CreateWEBPDecoder, "/images/resources/webp-animated-large.webp");
+}
+
+TEST(AnimatedWebPTests, decodeAfterReallocatingData) {
+ TestDecodeAfterReallocatingData(&CreateWEBPDecoder,
+ "/images/resources/webp-animated.webp");
+ TestDecodeAfterReallocatingData(
+ &CreateWEBPDecoder, "/images/resources/webp-animated-icc-xmp.webp");
+}
+
+TEST(AnimatedWebPTests, alphaBlending) {
+ TestAlphaBlending(&CreateWEBPDecoder, "/images/resources/webp-animated.webp");
+ TestAlphaBlending(&CreateWEBPDecoder,
+ "/images/resources/webp-animated-semitransparent1.webp");
+ TestAlphaBlending(&CreateWEBPDecoder,
+ "/images/resources/webp-animated-semitransparent2.webp");
+ TestAlphaBlending(&CreateWEBPDecoder,
+ "/images/resources/webp-animated-semitransparent3.webp");
+ TestAlphaBlending(&CreateWEBPDecoder,
+ "/images/resources/webp-animated-semitransparent4.webp");
+}
+
+TEST(AnimatedWebPTests, isSizeAvailable) {
+ TestByteByByteSizeAvailable(&CreateWEBPDecoder,
+ "/images/resources/webp-animated.webp", 142u,
+ false, kAnimationLoopInfinite);
+ // FIXME: Add color profile support for animated webp images.
+ TestByteByByteSizeAvailable(&CreateWEBPDecoder,
+ "/images/resources/webp-animated-icc-xmp.webp",
+ 1404u, false, 31999);
+}
+
+TEST(AnimatedWEBPTests, clearCacheExceptFrameWithAncestors) {
+ std::unique_ptr<ImageDecoder> decoder = CreateWEBPDecoder();
+
+ scoped_refptr<SharedBuffer> full_data =
+ ReadFile("/images/resources/webp-animated.webp");
+ ASSERT_TRUE(full_data.get());
+ decoder->SetData(full_data.get(), true);
+
+ ASSERT_EQ(3u, decoder->FrameCount());
+ // We need to store pointers to the image frames, since calling
+ // FrameBufferAtIndex will decode the frame if it is not FrameComplete,
+ // and we want to read the status of the frame without decoding it again.
+ ImageFrame* buffers[3];
+ size_t buffer_sizes[3];
+ for (size_t i = 0; i < decoder->FrameCount(); i++) {
+ buffers[i] = decoder->DecodeFrameBufferAtIndex(i);
+ ASSERT_EQ(ImageFrame::kFrameComplete, buffers[i]->GetStatus());
+ buffer_sizes[i] = decoder->FrameBytesAtIndex(i);
+ }
+
+ // Explicitly set the required previous frame for the frames, since this test
+ // is designed on this chain. Whether the frames actually depend on each
+ // other is not important for this test - ClearCacheExceptFrame just looks at
+ // the frame status and the required previous frame.
+ buffers[1]->SetRequiredPreviousFrameIndex(0);
+ buffers[2]->SetRequiredPreviousFrameIndex(1);
+
+ // Clear the cache except for a single frame. All other frames should be
+ // cleared to FrameEmpty, since this frame is FrameComplete.
+ EXPECT_EQ(buffer_sizes[0] + buffer_sizes[2],
+ decoder->ClearCacheExceptFrame(1));
+ EXPECT_EQ(ImageFrame::kFrameEmpty, buffers[0]->GetStatus());
+ EXPECT_EQ(ImageFrame::kFrameComplete, buffers[1]->GetStatus());
+ EXPECT_EQ(ImageFrame::kFrameEmpty, buffers[2]->GetStatus());
+
+ // Verify that the required previous frame is also preserved if the provided
+ // frame is not FrameComplete. The simulated situation is:
+ //
+ // Frame 0 <--------- Frame 1 <--------- Frame 2
+ // FrameComplete depends on FrameComplete depends on FramePartial
+ //
+ // The expected outcome is that frame 1 and frame 2 are preserved, since
+ // frame 1 is necessary to fully decode frame 2.
+ for (size_t i = 0; i < decoder->FrameCount(); i++) {
+ ASSERT_EQ(ImageFrame::kFrameComplete,
+ decoder->DecodeFrameBufferAtIndex(i)->GetStatus());
+ }
+ buffers[2]->SetStatus(ImageFrame::kFramePartial);
+ EXPECT_EQ(buffer_sizes[0], decoder->ClearCacheExceptFrame(2));
+ EXPECT_EQ(ImageFrame::kFrameEmpty, buffers[0]->GetStatus());
+ EXPECT_EQ(ImageFrame::kFrameComplete, buffers[1]->GetStatus());
+ EXPECT_EQ(ImageFrame::kFramePartial, buffers[2]->GetStatus());
+
+ // Verify that the nearest FrameComplete required frame is preserved if
+ // earlier required frames in the ancestor list are not FrameComplete. The
+ // simulated situation is:
+ //
+ // Frame 0 <--------- Frame 1 <--------- Frame 2
+ // FrameComplete depends on FrameEmpty depends on FramePartial
+ //
+ // The expected outcome is that frame 0 and frame 2 are preserved. Frame 2
+ // should be preserved since it is the frame passed to ClearCacheExceptFrame.
+ // Frame 0 should be preserved since it is the nearest FrameComplete ancestor.
+ // Thus, since frame 1 is FrameEmpty, no data is cleared in this case.
+ for (size_t i = 0; i < decoder->FrameCount(); i++) {
+ ASSERT_EQ(ImageFrame::kFrameComplete,
+ decoder->DecodeFrameBufferAtIndex(i)->GetStatus());
+ }
+ buffers[1]->SetStatus(ImageFrame::kFrameEmpty);
+ buffers[2]->SetStatus(ImageFrame::kFramePartial);
+ EXPECT_EQ(0u, decoder->ClearCacheExceptFrame(2));
+ EXPECT_EQ(ImageFrame::kFrameComplete, buffers[0]->GetStatus());
+ EXPECT_EQ(ImageFrame::kFrameEmpty, buffers[1]->GetStatus());
+ EXPECT_EQ(ImageFrame::kFramePartial, buffers[2]->GetStatus());
+}
+
+TEST(StaticWebPTests, truncatedImage) {
+ // VP8 data is truncated.
+ TestInvalidImage("/images/resources/truncated.webp", false);
+ // Chunk size in RIFF header doesn't match the file size.
+ TestInvalidImage("/images/resources/truncated2.webp", true);
+}
+
+// Regression test for a bug where some valid images were failing to decode
+// incrementally.
+TEST(StaticWebPTests, incrementalDecode) {
+ TestByteByByteDecode(&CreateWEBPDecoder,
+ "/images/resources/crbug.364830.webp", 1u,
+ kAnimationNone);
+}
+
+TEST(StaticWebPTests, isSizeAvailable) {
+ TestByteByByteSizeAvailable(&CreateWEBPDecoder,
+ "/images/resources/webp-color-profile-lossy.webp",
+ 520u, true, kAnimationNone);
+ TestByteByByteSizeAvailable(&CreateWEBPDecoder, "/images/resources/test.webp",
+ 30u, false, kAnimationNone);
+}
+
+TEST(StaticWebPTests, notAnimated) {
+ std::unique_ptr<ImageDecoder> decoder = CreateWEBPDecoder();
+ scoped_refptr<SharedBuffer> data =
+ ReadFile("/images/resources/webp-color-profile-lossy.webp");
+ ASSERT_TRUE(data.get());
+ decoder->SetData(data.get(), true);
+ EXPECT_EQ(1u, decoder->FrameCount());
+ EXPECT_EQ(kAnimationNone, decoder->RepetitionCount());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/image-encoders/image_encoder.cc b/chromium/third_party/blink/renderer/platform/image-encoders/image_encoder.cc
new file mode 100644
index 00000000000..1236b897722
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-encoders/image_encoder.cc
@@ -0,0 +1,84 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/image-encoders/image_encoder.h"
+
+namespace blink {
+
+bool ImageEncoder::Encode(Vector<unsigned char>* dst,
+ const SkPixmap& src,
+ const SkJpegEncoder::Options& options) {
+ VectorWStream dst_stream(dst);
+ return SkJpegEncoder::Encode(&dst_stream, src, options);
+}
+
+bool ImageEncoder::Encode(Vector<unsigned char>* dst,
+ const SkPixmap& src,
+ const SkPngEncoder::Options& options) {
+ VectorWStream dst_stream(dst);
+ return SkPngEncoder::Encode(&dst_stream, src, options);
+}
+
+bool ImageEncoder::Encode(Vector<unsigned char>* dst,
+ const SkPixmap& src,
+ const SkWebpEncoder::Options& options) {
+ VectorWStream dst_stream(dst);
+ return SkWebpEncoder::Encode(&dst_stream, src, options);
+}
+
+std::unique_ptr<ImageEncoder> ImageEncoder::Create(
+ Vector<unsigned char>* dst,
+ const SkPixmap& src,
+ const SkJpegEncoder::Options& options) {
+ std::unique_ptr<ImageEncoder> image_encoder(new ImageEncoder(dst));
+ image_encoder->encoder_ =
+ SkJpegEncoder::Make(&image_encoder->dst_, src, options);
+ if (!image_encoder->encoder_) {
+ return nullptr;
+ }
+
+ return image_encoder;
+}
+
+std::unique_ptr<ImageEncoder> ImageEncoder::Create(
+ Vector<unsigned char>* dst,
+ const SkPixmap& src,
+ const SkPngEncoder::Options& options) {
+ std::unique_ptr<ImageEncoder> image_encoder(new ImageEncoder(dst));
+ image_encoder->encoder_ =
+ SkPngEncoder::Make(&image_encoder->dst_, src, options);
+ if (!image_encoder->encoder_) {
+ return nullptr;
+ }
+
+ return image_encoder;
+}
+
+int ImageEncoder::ComputeJpegQuality(double quality) {
+ int compression_quality = 92; // Default value
+ if (0.0f <= quality && quality <= 1.0)
+ compression_quality = static_cast<int>(quality * 100 + 0.5);
+ return compression_quality;
+}
+
+SkWebpEncoder::Options ImageEncoder::ComputeWebpOptions(
+ double quality,
+ SkTransferFunctionBehavior unpremulBehavior) {
+ SkWebpEncoder::Options options;
+ options.fUnpremulBehavior = unpremulBehavior;
+
+ if (quality == 1.0) {
+ // Choose a lossless encode. When performing a lossless encode, higher
+ // quality corresponds to slower encoding and smaller output size.
+ options.fCompression = SkWebpEncoder::Compression::kLossless;
+ options.fQuality = 75.0f;
+ } else {
+ options.fQuality = 80.0f; // Default value
+ if (0.0f <= quality && quality <= 1.0)
+ options.fQuality = quality * 100.0f;
+ }
+
+ return options;
+}
+};
diff --git a/chromium/third_party/blink/renderer/platform/image-encoders/image_encoder.h b/chromium/third_party/blink/renderer/platform/image-encoders/image_encoder.h
new file mode 100644
index 00000000000..85ad21e03a8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-encoders/image_encoder.h
@@ -0,0 +1,91 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_ENCODERS_IMAGE_ENCODER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_ENCODERS_IMAGE_ENCODER_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "third_party/skia/include/core/SkStream.h"
+#include "third_party/skia/include/encode/SkJpegEncoder.h"
+#include "third_party/skia/include/encode/SkPngEncoder.h"
+#include "third_party/skia/include/encode/SkWebpEncoder.h"
+
+namespace blink {
+
+class VectorWStream : public SkWStream {
+ public:
+ VectorWStream(Vector<unsigned char>* dst) : dst_(dst) {
+ DCHECK(dst_);
+ DCHECK_EQ(0UL, dst->size());
+ }
+
+ bool write(const void* buffer, size_t size) override {
+ dst_->Append((const unsigned char*)buffer, size);
+ return true;
+ }
+
+ size_t bytesWritten() const override { return dst_->size(); }
+
+ private:
+ // Does not have ownership.
+ Vector<unsigned char>* dst_;
+};
+
+class PLATFORM_EXPORT ImageEncoder {
+ public:
+ static bool Encode(Vector<unsigned char>* dst,
+ const SkPixmap& src,
+ const SkJpegEncoder::Options&);
+
+ static bool Encode(Vector<unsigned char>* dst,
+ const SkPixmap& src,
+ const SkPngEncoder::Options&);
+
+ static bool Encode(Vector<unsigned char>* dst,
+ const SkPixmap& src,
+ const SkWebpEncoder::Options&);
+
+ static std::unique_ptr<ImageEncoder> Create(Vector<unsigned char>* dst,
+ const SkPixmap& src,
+ const SkJpegEncoder::Options&);
+
+ static std::unique_ptr<ImageEncoder> Create(Vector<unsigned char>* dst,
+ const SkPixmap& src,
+ const SkPngEncoder::Options&);
+
+ bool encodeRows(int numRows) { return encoder_->encodeRows(numRows); }
+
+ /**
+ * If quality is in [0, 1], this will simply convert to a [0, 100]
+ * integer scale (which is what is used by libjpeg-turbo).
+ *
+ * Otherwise, this will return the default value (92).
+ */
+ static int ComputeJpegQuality(double quality);
+
+ /**
+ * Sets Skia encoding options based on the requested quality.
+ *
+ * If quality is 1, this will signal a lossless encode.
+ *
+ * Otherwise, this will use webp lossy encoding.
+ * If quality is in [0, 1), this will simply convert to a [0, 100)
+ * float scale (which is what is used by libwebp). If the quality
+ * is out of range, this will perform a lossy encode with the default
+ * value (80).
+ */
+ static SkWebpEncoder::Options ComputeWebpOptions(
+ double quality,
+ SkTransferFunctionBehavior unpremulBehavior);
+
+ private:
+ ImageEncoder(Vector<unsigned char>* dst) : dst_(dst) {}
+
+ VectorWStream dst_;
+ std::unique_ptr<SkEncoder> encoder_;
+};
+};
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/image-encoders/image_encoder_utils.cc b/chromium/third_party/blink/renderer/platform/image-encoders/image_encoder_utils.cc
new file mode 100644
index 00000000000..3578bfd004c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-encoders/image_encoder_utils.cc
@@ -0,0 +1,84 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/image-encoders/image_encoder_utils.h"
+
+#include "third_party/blink/renderer/platform/histogram.h"
+#include "third_party/blink/renderer/platform/network/mime/mime_type_registry.h"
+#include "third_party/blink/renderer/platform/wtf/threading.h"
+
+namespace blink {
+
+const char ImageEncoderUtils::kDefaultMimeType[] = "image/png";
+
+// This enum is used in a UMA histogram; the values should not be changed.
+enum RequestedImageMimeType {
+ kRequestedImageMimeTypePng = 0,
+ kRequestedImageMimeTypeJpeg = 1,
+ kRequestedImageMimeTypeWebp = 2,
+ kRequestedImageMimeTypeGif = 3,
+ kRequestedImageMimeTypeBmp = 4,
+ kRequestedImageMimeTypeIco = 5,
+ kRequestedImageMimeTypeTiff = 6,
+ kRequestedImageMimeTypeUnknown = 7,
+ kNumberOfRequestedImageMimeTypes
+};
+
+String ImageEncoderUtils::ToEncodingMimeType(const String& mime_type,
+ const EncodeReason encode_reason) {
+ String lowercase_mime_type = mime_type.DeprecatedLower();
+
+ if (mime_type.IsNull())
+ lowercase_mime_type = kDefaultMimeType;
+
+ RequestedImageMimeType image_format;
+ if (lowercase_mime_type == "image/png") {
+ image_format = kRequestedImageMimeTypePng;
+ } else if (lowercase_mime_type == "image/jpeg") {
+ image_format = kRequestedImageMimeTypeJpeg;
+ } else if (lowercase_mime_type == "image/webp") {
+ image_format = kRequestedImageMimeTypeWebp;
+ } else if (lowercase_mime_type == "image/gif") {
+ image_format = kRequestedImageMimeTypeGif;
+ } else if (lowercase_mime_type == "image/bmp" ||
+ lowercase_mime_type == "image/x-windows-bmp") {
+ image_format = kRequestedImageMimeTypeBmp;
+ } else if (lowercase_mime_type == "image/x-icon") {
+ image_format = kRequestedImageMimeTypeIco;
+ } else if (lowercase_mime_type == "image/tiff" ||
+ lowercase_mime_type == "image/x-tiff") {
+ image_format = kRequestedImageMimeTypeTiff;
+ } else {
+ image_format = kRequestedImageMimeTypeUnknown;
+ }
+
+ if (encode_reason == kEncodeReasonToDataURL) {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(EnumerationHistogram,
+ to_data_url_image_format_histogram,
+ ("Canvas.RequestedImageMimeTypes_toDataURL",
+ kNumberOfRequestedImageMimeTypes));
+ to_data_url_image_format_histogram.Count(image_format);
+ } else if (encode_reason == kEncodeReasonToBlobCallback) {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(
+ EnumerationHistogram, to_blob_callback_image_format_histogram,
+ ("Canvas.RequestedImageMimeTypes_toBlobCallback",
+ kNumberOfRequestedImageMimeTypes));
+ to_blob_callback_image_format_histogram.Count(image_format);
+ } else if (encode_reason == kEncodeReasonConvertToBlobPromise) {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(
+ EnumerationHistogram, convert_to_blob_promise_image_format_histogram,
+ ("Canvas.RequestedImageMimeTypes_convertToBlobPromise",
+ kNumberOfRequestedImageMimeTypes));
+ convert_to_blob_promise_image_format_histogram.Count(image_format);
+ }
+
+ // FIXME: Make isSupportedImageMIMETypeForEncoding threadsafe (to allow this
+ // method to be used on a worker thread).
+ if (!MIMETypeRegistry::IsSupportedImageMIMETypeForEncoding(
+ lowercase_mime_type))
+ lowercase_mime_type = kDefaultMimeType;
+ return lowercase_mime_type;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/image-encoders/image_encoder_utils.h b/chromium/third_party/blink/renderer/platform/image-encoders/image_encoder_utils.h
new file mode 100644
index 00000000000..f5293beb41f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/image-encoders/image_encoder_utils.h
@@ -0,0 +1,29 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_ENCODERS_IMAGE_ENCODER_UTILS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_ENCODERS_IMAGE_ENCODER_UTILS_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT ImageEncoderUtils {
+ public:
+ enum EncodeReason {
+ kEncodeReasonToDataURL = 0,
+ kEncodeReasonToBlobCallback = 1,
+ kEncodeReasonConvertToBlobPromise = 2,
+ kNumberOfEncodeReasons
+ };
+ static String ToEncodingMimeType(const String& mime_type, const EncodeReason);
+
+ // Default image mime type for toDataURL and toBlob functions
+ static const char kDefaultMimeType[];
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_ENCODERS_IMAGE_ENCODER_UTILS_H_
diff --git a/chromium/third_party/blink/renderer/platform/instance_counters.cc b/chromium/third_party/blink/renderer/platform/instance_counters.cc
new file mode 100644
index 00000000000..64734a7dff8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/instance_counters.cc
@@ -0,0 +1,41 @@
+/*
+* Copyright (C) 2012 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 "third_party/blink/renderer/platform/instance_counters.h"
+
+namespace blink {
+
+int InstanceCounters::counters_[kCounterTypeLength];
+
+int InstanceCounters::CounterValue(CounterType type) {
+ return AcquireLoad(&counters_[type]);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/instance_counters.h b/chromium/third_party/blink/renderer/platform/instance_counters.h
new file mode 100644
index 00000000000..9aa201902a9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/instance_counters.h
@@ -0,0 +1,97 @@
+/*
+* Copyright (C) 2012 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_INSTANCE_COUNTERS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_INSTANCE_COUNTERS_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/atomics.h"
+
+namespace blink {
+
+#define INSTANCE_COUNTERS_LIST(V) \
+ V(AudioHandler) \
+ V(Document) \
+ V(Frame) \
+ V(JSEventListener) \
+ V(LayoutObject) \
+ V(MediaKeySession) \
+ V(MediaKeys) \
+ V(Node) \
+ V(Resource) \
+ V(ScriptPromise) \
+ V(PausableObject) \
+ V(V8PerContextData) \
+ V(WorkerGlobalScope) \
+ V(UACSSResource) \
+ V(RTCPeerConnection) \
+ V(ResourceFetcher)
+
+class InstanceCounters {
+ STATIC_ONLY(InstanceCounters);
+
+ public:
+ enum CounterType {
+#define DECLARE_INSTANCE_COUNTER(name) k##name##Counter,
+ INSTANCE_COUNTERS_LIST(DECLARE_INSTANCE_COUNTER)
+#undef DECLARE_INSTANCE_COUNTER
+ kCounterTypeLength
+ };
+
+ static inline void IncrementCounter(CounterType type) {
+ // There are lots of nodes created. Atomic barriers or locks
+ // should be avoided for the sake of performance. See crbug.com/641019
+ if (type == kNodeCounter) {
+ DCHECK(IsMainThread());
+ ++counters_[kNodeCounter];
+ } else {
+ AtomicIncrement(&counters_[type]);
+ }
+ }
+
+ static inline void DecrementCounter(CounterType type) {
+ if (type == kNodeCounter) {
+ DCHECK(IsMainThread());
+ --counters_[kNodeCounter];
+ } else {
+ AtomicDecrement(&counters_[type]);
+ }
+ }
+
+ PLATFORM_EXPORT static int CounterValue(CounterType);
+
+ private:
+ PLATFORM_EXPORT static int counters_[];
+};
+
+} // namespace blink
+
+#endif // !defined(InstanceCounters_h)
diff --git a/chromium/third_party/blink/renderer/platform/instance_counters_memory_dump_provider.h b/chromium/third_party/blink/renderer/platform/instance_counters_memory_dump_provider.h
new file mode 100644
index 00000000000..b750b673d87
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/instance_counters_memory_dump_provider.h
@@ -0,0 +1,35 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_INSTANCE_COUNTERS_MEMORY_DUMP_PROVIDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_INSTANCE_COUNTERS_MEMORY_DUMP_PROVIDER_H_
+
+#include "base/trace_event/memory_dump_provider.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT InstanceCountersMemoryDumpProvider final
+ : public base::trace_event::MemoryDumpProvider {
+ USING_FAST_MALLOC(InstanceCountersMemoryDumpProvider);
+
+ public:
+ static InstanceCountersMemoryDumpProvider* Instance();
+ ~InstanceCountersMemoryDumpProvider() override = default;
+
+ // MemoryDumpProvider implementation.
+ bool OnMemoryDump(const base::trace_event::MemoryDumpArgs&,
+ base::trace_event::ProcessMemoryDump*) override;
+
+ private:
+ InstanceCountersMemoryDumpProvider() = default;
+
+ WTF_MAKE_NONCOPYABLE(InstanceCountersMemoryDumpProvider);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_INSTANCE_COUNTERS_MEMORY_DUMP_PROVIDER_H_
diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/BUILD.gn b/chromium/third_party/blink/renderer/platform/instrumentation/BUILD.gn
new file mode 100644
index 00000000000..24be946f7cd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/instrumentation/BUILD.gn
@@ -0,0 +1,50 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/jumbo.gni")
+import("//third_party/blink/renderer/platform/platform.gni")
+
+blink_platform_sources("instrumentation") {
+ sources = [
+ "platform_instrumentation.cc",
+ "platform_instrumentation.h",
+ "resource_coordinator/blink_resource_coordinator_base.h",
+ "resource_coordinator/frame_resource_coordinator.cc",
+ "resource_coordinator/frame_resource_coordinator.h",
+ "resource_coordinator/renderer_resource_coordinator.cc",
+ "resource_coordinator/renderer_resource_coordinator.h",
+ "tracing/memory_cache_dump_provider.cc",
+ "tracing/memory_cache_dump_provider.h",
+ "tracing/trace_event.cc",
+ "tracing/trace_event.h",
+ "tracing/traced_value.cc",
+ "tracing/traced_value.h",
+ "tracing/web_memory_allocator_dump.cc",
+ "tracing/web_memory_allocator_dump.h",
+ "tracing/web_process_memory_dump.cc",
+ "tracing/web_process_memory_dump.h",
+ ]
+
+ deps = [
+ "//services/service_manager/public/cpp",
+ ]
+}
+
+jumbo_source_set("unit_tests") {
+ testonly = true
+
+ sources = [
+ "tracing/traced_value_test.cc",
+ "tracing/web_process_memory_dump_test.cc",
+ ]
+
+ deps = [
+ "//base",
+ "//base/test:test_support",
+ "//testing/gmock",
+ "//testing/gtest",
+ ]
+
+ configs += [ "//third_party/blink/renderer/platform:blink_platform_config" ]
+}
diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/DEPS b/chromium/third_party/blink/renderer/platform/instrumentation/DEPS
new file mode 100644
index 00000000000..18a8e5ed671
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/instrumentation/DEPS
@@ -0,0 +1,11 @@
+include_rules = [
+ "+base/gtest_prod_util.h",
+ "+base/json",
+ "+base/macros.h",
+ "+base/memory",
+ "+base/strings",
+ "+base/time",
+ "+base/trace_event",
+ "+base/values.h",
+ "+skia/ext/skia_trace_memory_dump_impl.h",
+]
diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/OWNERS b/chromium/third_party/blink/renderer/platform/instrumentation/OWNERS
new file mode 100644
index 00000000000..001a7e75339
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/instrumentation/OWNERS
@@ -0,0 +1,4 @@
+primiano@chromium.org
+
+# TEAM: tracing@chromium.org
+# COMPONENT: Speed>Tracing
diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/platform_instrumentation.cc b/chromium/third_party/blink/renderer/platform/instrumentation/platform_instrumentation.cc
new file mode 100644
index 00000000000..c7c9d6aacc7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/instrumentation/platform_instrumentation.cc
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2012 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 "third_party/blink/renderer/platform/instrumentation/platform_instrumentation.h"
+
+namespace blink {
+
+const char PlatformInstrumentation::kCategoryName[] =
+ TRACE_DISABLED_BY_DEFAULT("devtools.timeline");
+
+const char PlatformInstrumentation::kImageDecodeEvent[] = "Decode Image";
+const char PlatformInstrumentation::kImageResizeEvent[] = "Resize Image";
+const char PlatformInstrumentation::kDrawLazyPixelRefEvent[] =
+ "Draw LazyPixelRef";
+const char PlatformInstrumentation::kDecodeLazyPixelRefEvent[] =
+ "Decode LazyPixelRef";
+
+const char PlatformInstrumentation::kLazyPixelRef[] = "LazyPixelRef";
+
+const char PlatformInstrumentation::kImageTypeArgument[] = "imageType";
+const char PlatformInstrumentation::kCachedArgument[] = "cached";
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/platform_instrumentation.h b/chromium/third_party/blink/renderer/platform/instrumentation/platform_instrumentation.h
new file mode 100644
index 00000000000..919b32af0f0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/instrumentation/platform_instrumentation.h
@@ -0,0 +1,72 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_INSTRUMENTATION_PLATFORM_INSTRUMENTATION_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_INSTRUMENTATION_PLATFORM_INSTRUMENTATION_H_
+
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT PlatformInstrumentation {
+ public:
+ class LazyPixelRefTracker : TraceEvent::TraceScopedTrackableObject<void*> {
+ public:
+ LazyPixelRefTracker(void* instance)
+ : TraceEvent::TraceScopedTrackableObject<void*>(kCategoryName,
+ kLazyPixelRef,
+ instance) {}
+ };
+
+ static const char kImageDecodeEvent[];
+ static const char kImageResizeEvent[];
+ static const char kDrawLazyPixelRefEvent[];
+ static const char kDecodeLazyPixelRefEvent[];
+
+ static const char kImageTypeArgument[];
+ static const char kCachedArgument[];
+
+ static const char kLazyPixelRef[];
+
+ static void WillDecodeImage(const String& image_type);
+ static void DidDecodeImage();
+ static void DidDrawLazyPixelRef(unsigned long long lazy_pixel_ref_id);
+ static void WillDecodeLazyPixelRef(unsigned long long lazy_pixel_ref_id);
+ static void DidDecodeLazyPixelRef();
+
+ private:
+ static const char kCategoryName[];
+};
+
+inline void PlatformInstrumentation::WillDecodeImage(const String& image_type) {
+ TRACE_EVENT_BEGIN1(kCategoryName, kImageDecodeEvent, kImageTypeArgument,
+ image_type.Ascii());
+}
+
+inline void PlatformInstrumentation::DidDecodeImage() {
+ TRACE_EVENT_END0(kCategoryName, kImageDecodeEvent);
+}
+
+inline void PlatformInstrumentation::DidDrawLazyPixelRef(
+ unsigned long long lazy_pixel_ref_id) {
+ TRACE_EVENT_INSTANT1(kCategoryName, kDrawLazyPixelRefEvent,
+ TRACE_EVENT_SCOPE_THREAD, kLazyPixelRef,
+ lazy_pixel_ref_id);
+}
+
+inline void PlatformInstrumentation::WillDecodeLazyPixelRef(
+ unsigned long long lazy_pixel_ref_id) {
+ TRACE_EVENT_BEGIN1(kCategoryName, kDecodeLazyPixelRefEvent, kLazyPixelRef,
+ lazy_pixel_ref_id);
+}
+
+inline void PlatformInstrumentation::DidDecodeLazyPixelRef() {
+ TRACE_EVENT_END0(kCategoryName, kDecodeLazyPixelRefEvent);
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_INSTRUMENTATION_PLATFORM_INSTRUMENTATION_H_
diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/resource_coordinator/DEPS b/chromium/third_party/blink/renderer/platform/instrumentation/resource_coordinator/DEPS
new file mode 100644
index 00000000000..0d498429ae5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/instrumentation/resource_coordinator/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+services/resource_coordinator/public",
+] \ No newline at end of file
diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/resource_coordinator/OWNERS b/chromium/third_party/blink/renderer/platform/instrumentation/resource_coordinator/OWNERS
new file mode 100644
index 00000000000..ee9a18a44de
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/instrumentation/resource_coordinator/OWNERS
@@ -0,0 +1 @@
+file://services/resource_coordinator/OWNERS \ No newline at end of file
diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/resource_coordinator/blink_resource_coordinator_base.h b/chromium/third_party/blink/renderer/platform/instrumentation/resource_coordinator/blink_resource_coordinator_base.h
new file mode 100644
index 00000000000..55e4d3112df
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/instrumentation/resource_coordinator/blink_resource_coordinator_base.h
@@ -0,0 +1,28 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_INSTRUMENTATION_RESOURCE_COORDINATOR_BLINK_RESOURCE_COORDINATOR_BASE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_INSTRUMENTATION_RESOURCE_COORDINATOR_BLINK_RESOURCE_COORDINATOR_BASE_H_
+
+#include "services/resource_coordinator/public/cpp/resource_coordinator_features.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+// Base class for Resource Coordinators in Blink.
+class PLATFORM_EXPORT BlinkResourceCoordinatorBase {
+ WTF_MAKE_NONCOPYABLE(BlinkResourceCoordinatorBase);
+
+ public:
+ static bool IsEnabled() {
+ return resource_coordinator::IsResourceCoordinatorEnabled();
+ }
+
+ BlinkResourceCoordinatorBase() = default;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_INSTRUMENTATION_RESOURCE_COORDINATOR_BLINK_RESOURCE_COORDINATOR_BASE_H_
diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/resource_coordinator/frame_resource_coordinator.cc b/chromium/third_party/blink/renderer/platform/instrumentation/resource_coordinator/frame_resource_coordinator.cc
new file mode 100644
index 00000000000..ddaf7992cf5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/instrumentation/resource_coordinator/frame_resource_coordinator.cc
@@ -0,0 +1,36 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/instrumentation/resource_coordinator/frame_resource_coordinator.h"
+
+#include "services/service_manager/public/cpp/interface_provider.h"
+
+namespace blink {
+
+// static
+FrameResourceCoordinator* FrameResourceCoordinator::Create(
+ service_manager::InterfaceProvider* interface_provider) {
+ return new FrameResourceCoordinator(interface_provider);
+}
+
+FrameResourceCoordinator::FrameResourceCoordinator(
+ service_manager::InterfaceProvider* interface_provider) {
+ interface_provider->GetInterface(mojo::MakeRequest(&service_));
+}
+
+FrameResourceCoordinator::~FrameResourceCoordinator() = default;
+
+void FrameResourceCoordinator::SetNetworkAlmostIdle(bool idle) {
+ if (!service_)
+ return;
+ service_->SetNetworkAlmostIdle(idle);
+}
+
+void FrameResourceCoordinator::OnNonPersistentNotificationCreated() {
+ if (!service_)
+ return;
+ service_->OnNonPersistentNotificationCreated();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/resource_coordinator/frame_resource_coordinator.h b/chromium/third_party/blink/renderer/platform/instrumentation/resource_coordinator/frame_resource_coordinator.h
new file mode 100644
index 00000000000..13649425dce
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/instrumentation/resource_coordinator/frame_resource_coordinator.h
@@ -0,0 +1,36 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_INSTRUMENTATION_RESOURCE_COORDINATOR_FRAME_RESOURCE_COORDINATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_INSTRUMENTATION_RESOURCE_COORDINATOR_FRAME_RESOURCE_COORDINATOR_H_
+
+#include "services/resource_coordinator/public/mojom/coordination_unit.mojom-blink.h"
+#include "third_party/blink/renderer/platform/instrumentation/resource_coordinator/blink_resource_coordinator_base.h"
+
+namespace service_manager {
+class InterfaceProvider;
+} // namespace service_manager
+
+namespace blink {
+
+class PLATFORM_EXPORT FrameResourceCoordinator final
+ : public BlinkResourceCoordinatorBase {
+ WTF_MAKE_NONCOPYABLE(FrameResourceCoordinator);
+
+ public:
+ static FrameResourceCoordinator* Create(service_manager::InterfaceProvider*);
+ ~FrameResourceCoordinator();
+
+ void SetNetworkAlmostIdle(bool);
+ void OnNonPersistentNotificationCreated();
+
+ private:
+ explicit FrameResourceCoordinator(service_manager::InterfaceProvider*);
+
+ resource_coordinator::mojom::blink::FrameCoordinationUnitPtr service_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_INSTRUMENTATION_RESOURCE_COORDINATOR_FRAME_RESOURCE_COORDINATOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/resource_coordinator/renderer_resource_coordinator.cc b/chromium/third_party/blink/renderer/platform/instrumentation/resource_coordinator/renderer_resource_coordinator.cc
new file mode 100644
index 00000000000..b3f6767abfa
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/instrumentation/resource_coordinator/renderer_resource_coordinator.cc
@@ -0,0 +1,65 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/instrumentation/resource_coordinator/renderer_resource_coordinator.h"
+
+#include "services/service_manager/public/cpp/connector.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/heap/thread_state.h"
+
+namespace blink {
+
+namespace {
+
+RendererResourceCoordinator* g_renderer_resource_coordinator = nullptr;
+
+} // namespace
+
+// static
+void RendererResourceCoordinator::Initialize() {
+ blink::Platform* platform = Platform::Current();
+ DCHECK(IsMainThread());
+ DCHECK(platform);
+ g_renderer_resource_coordinator = new RendererResourceCoordinator(
+ platform->GetConnector(), platform->GetBrowserServiceName());
+}
+
+// static
+void RendererResourceCoordinator::
+ SetCurrentRendererResourceCoordinatorForTesting(
+ RendererResourceCoordinator* renderer_resource_coordinator) {
+ g_renderer_resource_coordinator = renderer_resource_coordinator;
+}
+
+// static
+RendererResourceCoordinator& RendererResourceCoordinator::Get() {
+ DCHECK(g_renderer_resource_coordinator);
+ return *g_renderer_resource_coordinator;
+}
+
+RendererResourceCoordinator::RendererResourceCoordinator(
+ service_manager::Connector* connector,
+ const std::string& service_name) {
+ connector->BindInterface(service_name, &service_);
+}
+
+RendererResourceCoordinator::RendererResourceCoordinator() = default;
+
+RendererResourceCoordinator::~RendererResourceCoordinator() = default;
+
+void RendererResourceCoordinator::SetExpectedTaskQueueingDuration(
+ base::TimeDelta duration) {
+ if (!service_)
+ return;
+ service_->SetExpectedTaskQueueingDuration(duration);
+}
+
+void RendererResourceCoordinator::SetMainThreadTaskLoadIsLow(
+ bool main_thread_task_load_is_low) {
+ if (!service_)
+ return;
+ service_->SetMainThreadTaskLoadIsLow(main_thread_task_load_is_low);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/resource_coordinator/renderer_resource_coordinator.h b/chromium/third_party/blink/renderer/platform/instrumentation/resource_coordinator/renderer_resource_coordinator.h
new file mode 100644
index 00000000000..049c20663eb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/instrumentation/resource_coordinator/renderer_resource_coordinator.h
@@ -0,0 +1,45 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_INSTRUMENTATION_RESOURCE_COORDINATOR_RENDERER_RESOURCE_COORDINATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_INSTRUMENTATION_RESOURCE_COORDINATOR_RENDERER_RESOURCE_COORDINATOR_H_
+
+#include "services/resource_coordinator/public/mojom/coordination_unit.mojom-blink.h"
+#include "third_party/blink/renderer/platform/instrumentation/resource_coordinator/blink_resource_coordinator_base.h"
+
+namespace service_manager {
+class Connector;
+} // namespace service_manager
+
+namespace blink {
+
+class PLATFORM_EXPORT RendererResourceCoordinator
+ : public BlinkResourceCoordinatorBase {
+ WTF_MAKE_NONCOPYABLE(RendererResourceCoordinator);
+
+ public:
+ static void Initialize();
+ static RendererResourceCoordinator& Get();
+
+ // Used to switch the current renderer resource coordinator only for testing.
+ static void SetCurrentRendererResourceCoordinatorForTesting(
+ RendererResourceCoordinator*);
+
+ ~RendererResourceCoordinator();
+
+ void SetExpectedTaskQueueingDuration(base::TimeDelta);
+ void SetMainThreadTaskLoadIsLow(bool);
+
+ protected:
+ RendererResourceCoordinator();
+
+ private:
+ RendererResourceCoordinator(service_manager::Connector*, const std::string&);
+
+ resource_coordinator::mojom::blink::ProcessCoordinationUnitPtr service_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_INSTRUMENTATION_RESOURCE_COORDINATOR_RENDERER_RESOURCE_COORDINATOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/tracing/memory_cache_dump_provider.cc b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/memory_cache_dump_provider.cc
new file mode 100644
index 00000000000..f94bc634a74
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/memory_cache_dump_provider.cc
@@ -0,0 +1,47 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/instrumentation/tracing/memory_cache_dump_provider.h"
+
+namespace blink {
+
+void MemoryCacheDumpClient::Trace(blink::Visitor* visitor) {}
+
+MemoryCacheDumpProvider* MemoryCacheDumpProvider::Instance() {
+ DEFINE_STATIC_LOCAL(MemoryCacheDumpProvider, instance, ());
+ return &instance;
+}
+
+bool MemoryCacheDumpProvider::OnMemoryDump(
+ const base::trace_event::MemoryDumpArgs& args,
+ base::trace_event::ProcessMemoryDump* memory_dump) {
+ DCHECK(IsMainThread());
+ if (!client_)
+ return false;
+
+ WebMemoryDumpLevelOfDetail level;
+ switch (args.level_of_detail) {
+ case base::trace_event::MemoryDumpLevelOfDetail::BACKGROUND:
+ level = blink::WebMemoryDumpLevelOfDetail::kBackground;
+ break;
+ case base::trace_event::MemoryDumpLevelOfDetail::LIGHT:
+ level = blink::WebMemoryDumpLevelOfDetail::kLight;
+ break;
+ case base::trace_event::MemoryDumpLevelOfDetail::DETAILED:
+ level = blink::WebMemoryDumpLevelOfDetail::kDetailed;
+ break;
+ default:
+ NOTREACHED();
+ return false;
+ }
+
+ WebProcessMemoryDump dump(args.level_of_detail, memory_dump);
+ return client_->OnMemoryDump(level, &dump);
+}
+
+MemoryCacheDumpProvider::MemoryCacheDumpProvider() = default;
+
+MemoryCacheDumpProvider::~MemoryCacheDumpProvider() = default;
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/tracing/memory_cache_dump_provider.h b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/memory_cache_dump_provider.h
new file mode 100644
index 00000000000..9c4ea8d628c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/memory_cache_dump_provider.h
@@ -0,0 +1,57 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_INSTRUMENTATION_TRACING_MEMORY_CACHE_DUMP_PROVIDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_INSTRUMENTATION_TRACING_MEMORY_CACHE_DUMP_PROVIDER_H_
+
+#include "base/macros.h"
+#include "base/trace_event/memory_dump_provider.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/web_process_memory_dump.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT MemoryCacheDumpClient : public GarbageCollectedMixin {
+ public:
+ virtual ~MemoryCacheDumpClient() = default;
+ virtual bool OnMemoryDump(WebMemoryDumpLevelOfDetail,
+ WebProcessMemoryDump*) = 0;
+
+ virtual void Trace(blink::Visitor*);
+};
+
+// This class is wrapper around MemoryCache to take memory snapshots. It dumps
+// the stats of cache only after the cache is created.
+class PLATFORM_EXPORT MemoryCacheDumpProvider final
+ : public base::trace_event::MemoryDumpProvider {
+ USING_FAST_MALLOC(MemoryCacheDumpProvider);
+
+ public:
+ // This class is singleton since there is a global MemoryCache object.
+ static MemoryCacheDumpProvider* Instance();
+ ~MemoryCacheDumpProvider() override;
+
+ // base::trace_event::MemoryDumpProvider implementation.
+ bool OnMemoryDump(const base::trace_event::MemoryDumpArgs&,
+ base::trace_event::ProcessMemoryDump*) override;
+
+ void SetMemoryCache(MemoryCacheDumpClient* client) {
+ DCHECK(IsMainThread());
+ client_ = client;
+ }
+
+ private:
+ MemoryCacheDumpProvider();
+
+ WeakPersistent<MemoryCacheDumpClient> client_;
+
+ DISALLOW_COPY_AND_ASSIGN(MemoryCacheDumpProvider);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_INSTRUMENTATION_TRACING_MEMORY_CACHE_DUMP_PROVIDER_H_
diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/tracing/trace_event.cc b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/trace_event.cc
new file mode 100644
index 00000000000..04695c14f86
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/trace_event.cc
@@ -0,0 +1,23 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+
+#include "base/trace_event/trace_event.h"
+
+namespace blink {
+namespace TraceEvent {
+
+void EnableTracing(const String& category_filter) {
+ base::trace_event::TraceLog::GetInstance()->SetEnabled(
+ base::trace_event::TraceConfig(category_filter.Utf8().data(), ""),
+ base::trace_event::TraceLog::RECORDING_MODE);
+}
+
+void DisableTracing() {
+ base::trace_event::TraceLog::GetInstance()->SetDisabled();
+}
+
+} // namespace TraceEvent
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h
new file mode 100644
index 00000000000..d189d432d3d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h
@@ -0,0 +1,46 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_INSTRUMENTATION_TRACING_TRACE_EVENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_INSTRUMENTATION_TRACING_TRACE_EVENT_H_
+
+#include "base/time/time.h"
+#include "base/trace_event/trace_event.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace WTF {
+
+// CString version of SetTraceValue so that trace arguments can be strings.
+static inline void SetTraceValue(const CString& arg,
+ unsigned char* type,
+ unsigned long long* value) {
+ trace_event_internal::TraceValueUnion type_value;
+ type_value.as_string = arg.data();
+ *type = TRACE_VALUE_TYPE_COPY_STRING;
+ *value = type_value.as_uint;
+}
+
+} // namespace WTF
+
+namespace blink {
+namespace TraceEvent {
+
+using base::trace_event::TraceScopedTrackableObject;
+
+inline base::TimeTicks ToTraceTimestamp(double seconds) {
+ return base::TimeTicks() + base::TimeDelta::FromSecondsD(seconds);
+}
+
+// This is to avoid error of passing a chromium time internal value.
+void ToTraceTimestamp(int64_t);
+
+PLATFORM_EXPORT void EnableTracing(const String& category_filter);
+PLATFORM_EXPORT void DisableTracing();
+
+} // namespace TraceEvent
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/tracing/traced_value.cc b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/traced_value.cc
new file mode 100644
index 00000000000..811f4c65e5b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/traced_value.cc
@@ -0,0 +1,120 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/instrumentation/tracing/traced_value.h"
+
+#include <memory>
+#include <string>
+
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
+
+namespace blink {
+
+std::unique_ptr<TracedValue> TracedValue::Create() {
+ return base::WrapUnique(new TracedValue());
+}
+
+TracedValue::TracedValue() = default;
+
+TracedValue::~TracedValue() = default;
+
+void TracedValue::SetInteger(const char* name, int value) {
+ traced_value_.SetInteger(name, value);
+}
+
+void TracedValue::SetIntegerWithCopiedName(const char* name, int value) {
+ traced_value_.SetIntegerWithCopiedName(name, value);
+}
+
+void TracedValue::SetDouble(const char* name, double value) {
+ traced_value_.SetDouble(name, value);
+}
+
+void TracedValue::SetDoubleWithCopiedName(const char* name, double value) {
+ traced_value_.SetDoubleWithCopiedName(name, value);
+}
+
+void TracedValue::SetBoolean(const char* name, bool value) {
+ traced_value_.SetBoolean(name, value);
+}
+
+void TracedValue::SetBooleanWithCopiedName(const char* name, bool value) {
+ traced_value_.SetBooleanWithCopiedName(name, value);
+}
+
+void TracedValue::SetString(const char* name, const String& value) {
+ StringUTF8Adaptor adaptor(value);
+ traced_value_.SetString(name, adaptor.AsStringPiece());
+}
+
+void TracedValue::SetStringWithCopiedName(const char* name,
+ const String& value) {
+ StringUTF8Adaptor adaptor(value);
+ traced_value_.SetStringWithCopiedName(name, adaptor.AsStringPiece());
+}
+
+void TracedValue::BeginDictionary(const char* name) {
+ traced_value_.BeginDictionary(name);
+}
+
+void TracedValue::BeginDictionaryWithCopiedName(const char* name) {
+ traced_value_.BeginDictionaryWithCopiedName(name);
+}
+
+void TracedValue::BeginArray(const char* name) {
+ traced_value_.BeginArray(name);
+}
+
+void TracedValue::BeginArrayWithCopiedName(const char* name) {
+ traced_value_.BeginArrayWithCopiedName(name);
+}
+
+void TracedValue::EndDictionary() {
+ traced_value_.EndDictionary();
+}
+
+void TracedValue::PushInteger(int value) {
+ traced_value_.AppendInteger(value);
+}
+
+void TracedValue::PushDouble(double value) {
+ traced_value_.AppendDouble(value);
+}
+
+void TracedValue::PushBoolean(bool value) {
+ traced_value_.AppendBoolean(value);
+}
+
+void TracedValue::PushString(const String& value) {
+ StringUTF8Adaptor adaptor(value);
+ traced_value_.AppendString(adaptor.AsStringPiece());
+}
+
+void TracedValue::BeginArray() {
+ traced_value_.BeginArray();
+}
+
+void TracedValue::BeginDictionary() {
+ traced_value_.BeginDictionary();
+}
+
+void TracedValue::EndArray() {
+ traced_value_.EndArray();
+}
+
+String TracedValue::ToString() const {
+ return String(traced_value_.ToString().c_str());
+}
+
+void TracedValue::AppendAsTraceFormat(std::string* out) const {
+ traced_value_.AppendAsTraceFormat(out);
+}
+
+void TracedValue::EstimateTraceMemoryOverhead(
+ base::trace_event::TraceEventMemoryOverhead* overhead) {
+ traced_value_.EstimateTraceMemoryOverhead(overhead);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/tracing/traced_value.h b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/traced_value.h
new file mode 100644
index 00000000000..6a0b4136452
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/traced_value.h
@@ -0,0 +1,65 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_INSTRUMENTATION_TRACING_TRACED_VALUE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_INSTRUMENTATION_TRACING_TRACED_VALUE_H_
+
+#include "base/macros.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+// Thin wrapper around base::trace_event::TracedValue.
+class PLATFORM_EXPORT TracedValue final
+ : public base::trace_event::ConvertableToTraceFormat {
+ public:
+ ~TracedValue();
+
+ static std::unique_ptr<TracedValue> Create();
+
+ void EndDictionary();
+ void EndArray();
+
+ void SetInteger(const char* name, int value);
+ void SetDouble(const char* name, double value);
+ void SetBoolean(const char* name, bool value);
+ void SetString(const char* name, const String& value);
+ void BeginArray(const char* name);
+ void BeginDictionary(const char* name);
+
+ void SetIntegerWithCopiedName(const char* name, int value);
+ void SetDoubleWithCopiedName(const char* name, double value);
+ void SetBooleanWithCopiedName(const char* name, bool value);
+ void SetStringWithCopiedName(const char* name, const String& value);
+ void BeginArrayWithCopiedName(const char* name);
+ void BeginDictionaryWithCopiedName(const char* name);
+
+ void PushInteger(int);
+ void PushDouble(double);
+ void PushBoolean(bool);
+ void PushString(const String&);
+ void BeginArray();
+ void BeginDictionary();
+
+ String ToString() const;
+
+ private:
+ TracedValue();
+
+ // ConvertableToTraceFormat
+
+ void AppendAsTraceFormat(std::string*) const final;
+ void EstimateTraceMemoryOverhead(
+ base::trace_event::TraceEventMemoryOverhead*) final;
+
+ base::trace_event::TracedValue traced_value_;
+
+ DISALLOW_COPY_AND_ASSIGN(TracedValue);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_INSTRUMENTATION_TRACING_TRACED_VALUE_H_
diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/tracing/traced_value_test.cc b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/traced_value_test.cc
new file mode 100644
index 00000000000..ba8b9deba65
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/traced_value_test.cc
@@ -0,0 +1,170 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/instrumentation/tracing/traced_value.h"
+
+#include "base/json/json_reader.h"
+#include "base/values.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include <memory>
+
+namespace blink {
+
+std::unique_ptr<base::Value> ParseTracedValue(
+ std::unique_ptr<TracedValue> value) {
+ base::JSONReader reader;
+ CString utf8 = value->ToString().Utf8();
+ return reader.Read(utf8.data());
+}
+
+TEST(TracedValueTest, FlatDictionary) {
+ std::unique_ptr<TracedValue> value = TracedValue::Create();
+ value->SetIntegerWithCopiedName("int", 2014);
+ value->SetDoubleWithCopiedName("double", 0.0);
+ value->SetBooleanWithCopiedName("bool", true);
+ value->SetStringWithCopiedName("string", "string");
+
+ std::unique_ptr<base::Value> parsed = ParseTracedValue(std::move(value));
+ base::DictionaryValue* dictionary;
+ ASSERT_TRUE(parsed->GetAsDictionary(&dictionary));
+ int int_value;
+ EXPECT_TRUE(dictionary->GetInteger("int", &int_value));
+ EXPECT_EQ(2014, int_value);
+ double double_value;
+ EXPECT_TRUE(dictionary->GetDouble("double", &double_value));
+ EXPECT_EQ(0.0, double_value);
+ std::string string_value;
+ EXPECT_TRUE(dictionary->GetString("string", &string_value));
+ EXPECT_EQ("string", string_value);
+}
+
+TEST(TracedValueTest, Hierarchy) {
+ std::unique_ptr<TracedValue> value = TracedValue::Create();
+ value->SetIntegerWithCopiedName("i0", 2014);
+ value->BeginDictionaryWithCopiedName("dict1");
+ value->SetIntegerWithCopiedName("i1", 2014);
+ value->BeginDictionaryWithCopiedName("dict2");
+ value->SetBooleanWithCopiedName("b2", false);
+ value->EndDictionary();
+ value->SetStringWithCopiedName("s1", "foo");
+ value->EndDictionary();
+ value->SetDoubleWithCopiedName("d0", 0.0);
+ value->SetBooleanWithCopiedName("b0", true);
+ value->BeginArrayWithCopiedName("a1");
+ value->PushInteger(1);
+ value->PushBoolean(true);
+ value->BeginDictionary();
+ value->SetIntegerWithCopiedName("i2", 3);
+ value->EndDictionary();
+ value->EndArray();
+ value->SetStringWithCopiedName("s0", "foo");
+
+ std::unique_ptr<base::Value> parsed = ParseTracedValue(std::move(value));
+ base::DictionaryValue* dictionary;
+ ASSERT_TRUE(parsed->GetAsDictionary(&dictionary));
+ int i0;
+ EXPECT_TRUE(dictionary->GetInteger("i0", &i0));
+ EXPECT_EQ(2014, i0);
+ int i1;
+ EXPECT_TRUE(dictionary->GetInteger("dict1.i1", &i1));
+ EXPECT_EQ(2014, i1);
+ bool b2;
+ EXPECT_TRUE(dictionary->GetBoolean("dict1.dict2.b2", &b2));
+ EXPECT_FALSE(b2);
+ std::string s1;
+ EXPECT_TRUE(dictionary->GetString("dict1.s1", &s1));
+ EXPECT_EQ("foo", s1);
+ double d0;
+ EXPECT_TRUE(dictionary->GetDouble("d0", &d0));
+ EXPECT_EQ(0.0, d0);
+ bool b0;
+ EXPECT_TRUE(dictionary->GetBoolean("b0", &b0));
+ EXPECT_TRUE(b0);
+ base::ListValue* a1;
+ EXPECT_TRUE(dictionary->GetList("a1", &a1));
+ int a1i0;
+ EXPECT_TRUE(a1->GetInteger(0, &a1i0));
+ EXPECT_EQ(1, a1i0);
+ bool a1b1;
+ EXPECT_TRUE(a1->GetBoolean(1, &a1b1));
+ EXPECT_TRUE(a1b1);
+ base::DictionaryValue* a1d2;
+ EXPECT_TRUE(a1->GetDictionary(2, &a1d2));
+ int i2;
+ EXPECT_TRUE(a1d2->GetInteger("i2", &i2));
+ EXPECT_EQ(3, i2);
+ std::string s0;
+ EXPECT_TRUE(dictionary->GetString("s0", &s0));
+ EXPECT_EQ("foo", s0);
+}
+
+TEST(TracedValueTest, Escape) {
+ std::unique_ptr<TracedValue> value = TracedValue::Create();
+ value->SetStringWithCopiedName("s0", "value0\\");
+ value->SetStringWithCopiedName("s1", "value\n1");
+ value->SetStringWithCopiedName("s2", "\"value2\"");
+ value->SetStringWithCopiedName("s3\\", "value3");
+ value->SetStringWithCopiedName("\"s4\"", "value4");
+
+ std::unique_ptr<base::Value> parsed = ParseTracedValue(std::move(value));
+ base::DictionaryValue* dictionary;
+ ASSERT_TRUE(parsed->GetAsDictionary(&dictionary));
+ std::string s0;
+ EXPECT_TRUE(dictionary->GetString("s0", &s0));
+ EXPECT_EQ("value0\\", s0);
+ std::string s1;
+ EXPECT_TRUE(dictionary->GetString("s1", &s1));
+ EXPECT_EQ("value\n1", s1);
+ std::string s2;
+ EXPECT_TRUE(dictionary->GetString("s2", &s2));
+ EXPECT_EQ("\"value2\"", s2);
+ std::string s3;
+ EXPECT_TRUE(dictionary->GetString("s3\\", &s3));
+ EXPECT_EQ("value3", s3);
+ std::string s4;
+ EXPECT_TRUE(dictionary->GetString("\"s4\"", &s4));
+ EXPECT_EQ("value4", s4);
+}
+
+TEST(TracedValueTest, NonCopiedNames) {
+ std::unique_ptr<TracedValue> value = TracedValue::Create();
+ const char* int_str = "int";
+ const char* double_str = "double";
+ const char* bool_str = "bool";
+ const char* string_str = "string";
+ const char* array_str = "array";
+ value->SetInteger(int_str, 2014);
+ value->SetDouble(double_str, 0.0);
+ value->SetBoolean(bool_str, true);
+ value->SetString(string_str, "string");
+ value->BeginArray(array_str);
+ value->PushInteger(1);
+ value->PushInteger(2);
+ value->EndArray();
+
+ std::unique_ptr<base::Value> parsed = ParseTracedValue(std::move(value));
+ base::DictionaryValue* dictionary;
+ ASSERT_TRUE(parsed->GetAsDictionary(&dictionary));
+ int int_value;
+ EXPECT_TRUE(dictionary->GetInteger(int_str, &int_value));
+ EXPECT_EQ(2014, int_value);
+ double double_value;
+ EXPECT_TRUE(dictionary->GetDouble(double_str, &double_value));
+ EXPECT_EQ(0.0, double_value);
+ bool bool_value;
+ EXPECT_TRUE(dictionary->GetBoolean(bool_str, &bool_value));
+ EXPECT_TRUE(bool_value);
+ std::string string_value;
+ EXPECT_TRUE(dictionary->GetString(string_str, &string_value));
+ EXPECT_EQ("string", string_value);
+ base::ListValue* a1;
+ EXPECT_TRUE(dictionary->GetList(array_str, &a1));
+ int el0, el1;
+ EXPECT_TRUE(a1->GetInteger(0, &el0));
+ EXPECT_TRUE(a1->GetInteger(1, &el1));
+ EXPECT_EQ(el0, 1);
+ EXPECT_EQ(el1, 2);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/tracing/web_memory_allocator_dump.cc b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/web_memory_allocator_dump.cc
new file mode 100644
index 00000000000..ac5f287adaf
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/web_memory_allocator_dump.cc
@@ -0,0 +1,37 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/instrumentation/tracing/web_memory_allocator_dump.h"
+
+#include "base/trace_event/memory_allocator_dump.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
+
+namespace blink {
+
+WebMemoryAllocatorDump::WebMemoryAllocatorDump(
+ base::trace_event::MemoryAllocatorDump* memory_allocator_dump)
+ : memory_allocator_dump_(memory_allocator_dump),
+ guid_(memory_allocator_dump->guid().ToUint64()) {}
+
+WebMemoryAllocatorDump::~WebMemoryAllocatorDump() = default;
+
+void WebMemoryAllocatorDump::AddScalar(const char* name,
+ const char* units,
+ uint64_t value) {
+ memory_allocator_dump_->AddScalar(name, units, value);
+}
+
+void WebMemoryAllocatorDump::AddString(const char* name,
+ const char* units,
+ const String& value) {
+ StringUTF8Adaptor adapter(value);
+ std::string utf8(adapter.Data(), adapter.length());
+ memory_allocator_dump_->AddString(name, units, utf8);
+}
+
+WebMemoryAllocatorDumpGuid WebMemoryAllocatorDump::Guid() const {
+ return guid_;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/tracing/web_memory_allocator_dump.h b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/web_memory_allocator_dump.h
new file mode 100644
index 00000000000..4e4e96884e2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/web_memory_allocator_dump.h
@@ -0,0 +1,60 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WebMemoryAllocatorDump_h
+#define WebMemoryAllocatorDump_h
+
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace base {
+namespace trace_event {
+class MemoryAllocatorDump;
+} // namespace base
+} // namespace trace_event
+
+namespace blink {
+
+typedef uint64_t WebMemoryAllocatorDumpGuid;
+
+// A container which holds all the attributes of a particular dump for a given
+// allocator.
+class PLATFORM_EXPORT WebMemoryAllocatorDump final {
+ public:
+ explicit WebMemoryAllocatorDump(
+ base::trace_event::MemoryAllocatorDump* memory_allocator_dump);
+ ~WebMemoryAllocatorDump();
+
+ // Adds a scalar attribute to the dump.
+ // Arguments:
+ // name: name of the attribute. Typical names, emitted by most allocators
+ // dump providers are: "size" and "objects_count".
+ // units: the units for the attribute. Gives a hint to the trace-viewer UI
+ // about the semantics of the attribute.
+ // Currently supported values are "bytes" and "objects".
+ // value: the value of the attribute.
+ void AddScalar(const char* name, const char* units, uint64_t value);
+ void AddString(const char* name, const char* units, const String& value);
+
+ // |guid| is an optional global dump identifier, unique across all processes
+ // within the scope of a global dump. It is only required when using the
+ // graph APIs (see AddOwnershipEdge) to express retention / suballocation or
+ // cross process sharing. See crbug.com/492102 for design docs.
+ // Subsequent MemoryAllocatorDump(s) with the same |absolute_name| are
+ // expected to have the same guid.
+ blink::WebMemoryAllocatorDumpGuid Guid() const;
+
+ private:
+ base::trace_event::MemoryAllocatorDump* memory_allocator_dump_; // Not owned.
+ blink::WebMemoryAllocatorDumpGuid guid_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebMemoryAllocatorDump);
+};
+
+} // namespace blink
+
+#endif // WebMemoryAllocatorDump_h
diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/tracing/web_process_memory_dump.cc b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/web_process_memory_dump.cc
new file mode 100644
index 00000000000..33d96ca67a7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/web_process_memory_dump.cc
@@ -0,0 +1,187 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/instrumentation/tracing/web_process_memory_dump.h"
+
+#include <stddef.h>
+#include <string>
+
+#include "base/memory/discardable_memory.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/trace_event/heap_profiler_heap_dump_writer.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "base/trace_event/trace_event_memory_overhead.h"
+#include "skia/ext/skia_trace_memory_dump_impl.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/web_memory_allocator_dump.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
+
+namespace blink {
+
+WebProcessMemoryDump::WebProcessMemoryDump()
+ : owned_process_memory_dump_(new base::trace_event::ProcessMemoryDump(
+ nullptr,
+ {base::trace_event::MemoryDumpLevelOfDetail::DETAILED})),
+ process_memory_dump_(owned_process_memory_dump_.get()),
+ level_of_detail_(base::trace_event::MemoryDumpLevelOfDetail::DETAILED) {}
+
+WebProcessMemoryDump::WebProcessMemoryDump(
+ base::trace_event::MemoryDumpLevelOfDetail level_of_detail,
+ base::trace_event::ProcessMemoryDump* process_memory_dump)
+ : process_memory_dump_(process_memory_dump),
+ level_of_detail_(level_of_detail) {}
+
+WebProcessMemoryDump::~WebProcessMemoryDump() = default;
+
+blink::WebMemoryAllocatorDump* WebProcessMemoryDump::CreateMemoryAllocatorDump(
+ const String& absolute_name) {
+ StringUTF8Adaptor adapter(absolute_name);
+ std::string name(adapter.Data(), adapter.length());
+ // Get a MemoryAllocatorDump from the base/ object.
+ base::trace_event::MemoryAllocatorDump* memory_allocator_dump =
+ process_memory_dump_->CreateAllocatorDump(name);
+
+ return CreateWebMemoryAllocatorDump(memory_allocator_dump);
+}
+
+blink::WebMemoryAllocatorDump* WebProcessMemoryDump::CreateMemoryAllocatorDump(
+ const String& absolute_name,
+ blink::WebMemoryAllocatorDumpGuid guid) {
+ StringUTF8Adaptor adapter(absolute_name);
+ std::string name(adapter.Data(), adapter.length());
+ // Get a MemoryAllocatorDump from the base/ object with given guid.
+ base::trace_event::MemoryAllocatorDump* memory_allocator_dump =
+ process_memory_dump_->CreateAllocatorDump(
+ name, base::trace_event::MemoryAllocatorDumpGuid(guid));
+ return CreateWebMemoryAllocatorDump(memory_allocator_dump);
+}
+
+blink::WebMemoryAllocatorDump*
+WebProcessMemoryDump::CreateWebMemoryAllocatorDump(
+ base::trace_event::MemoryAllocatorDump* memory_allocator_dump) {
+ if (!memory_allocator_dump)
+ return nullptr;
+
+ // Wrap it and return to blink.
+ WebMemoryAllocatorDump* web_memory_allocator_dump =
+ new WebMemoryAllocatorDump(memory_allocator_dump);
+
+ // memory_allocator_dumps_ will take ownership of
+ // |web_memory_allocator_dump|.
+ memory_allocator_dumps_.Set(memory_allocator_dump,
+ base::WrapUnique(web_memory_allocator_dump));
+ return web_memory_allocator_dump;
+}
+
+blink::WebMemoryAllocatorDump* WebProcessMemoryDump::GetMemoryAllocatorDump(
+ const String& absolute_name) const {
+ StringUTF8Adaptor adapter(absolute_name);
+ std::string name(adapter.Data(), adapter.length());
+ // Retrieve the base MemoryAllocatorDump object and then reverse lookup
+ // its wrapper.
+ base::trace_event::MemoryAllocatorDump* memory_allocator_dump =
+ process_memory_dump_->GetAllocatorDump(name);
+ if (!memory_allocator_dump)
+ return nullptr;
+
+ // The only case of (memory_allocator_dump && !web_memory_allocator_dump)
+ // is something from blink trying to get a MAD that was created from chromium,
+ // which is an odd use case.
+ blink::WebMemoryAllocatorDump* web_memory_allocator_dump =
+ memory_allocator_dumps_.at(memory_allocator_dump);
+ DCHECK(web_memory_allocator_dump);
+ return web_memory_allocator_dump;
+}
+
+void WebProcessMemoryDump::Clear() {
+ // Clear all the WebMemoryAllocatorDump wrappers.
+ memory_allocator_dumps_.clear();
+
+ // Clear the actual MemoryAllocatorDump objects from the underlying PMD.
+ process_memory_dump_->Clear();
+}
+
+void WebProcessMemoryDump::TakeAllDumpsFrom(
+ blink::WebProcessMemoryDump* other) {
+ // WebProcessMemoryDump is a container of WebMemoryAllocatorDump(s) which
+ // in turn are wrappers of base::trace_event::MemoryAllocatorDump(s).
+ // In order to expose the move and ownership transfer semantics of the
+ // underlying ProcessMemoryDump, we need to:
+
+ // 1) Move and transfer the ownership of the wrapped
+ // base::trace_event::MemoryAllocatorDump(s) instances.
+ process_memory_dump_->TakeAllDumpsFrom(other->process_memory_dump_);
+
+ // 2) Move and transfer the ownership of the WebMemoryAllocatorDump wrappers.
+ const size_t expected_final_size =
+ memory_allocator_dumps_.size() + other->memory_allocator_dumps_.size();
+ while (!other->memory_allocator_dumps_.IsEmpty()) {
+ auto first_entry = other->memory_allocator_dumps_.begin();
+ base::trace_event::MemoryAllocatorDump* memory_allocator_dump =
+ first_entry->key;
+ memory_allocator_dumps_.Set(
+ memory_allocator_dump,
+ other->memory_allocator_dumps_.Take(memory_allocator_dump));
+ }
+ DCHECK_EQ(expected_final_size, memory_allocator_dumps_.size());
+ DCHECK(other->memory_allocator_dumps_.IsEmpty());
+}
+
+void WebProcessMemoryDump::AddOwnershipEdge(
+ blink::WebMemoryAllocatorDumpGuid source,
+ blink::WebMemoryAllocatorDumpGuid target,
+ int importance) {
+ process_memory_dump_->AddOwnershipEdge(
+ base::trace_event::MemoryAllocatorDumpGuid(source),
+ base::trace_event::MemoryAllocatorDumpGuid(target), importance);
+}
+
+void WebProcessMemoryDump::AddOwnershipEdge(
+ blink::WebMemoryAllocatorDumpGuid source,
+ blink::WebMemoryAllocatorDumpGuid target) {
+ process_memory_dump_->AddOwnershipEdge(
+ base::trace_event::MemoryAllocatorDumpGuid(source),
+ base::trace_event::MemoryAllocatorDumpGuid(target));
+}
+
+void WebProcessMemoryDump::AddSuballocation(
+ blink::WebMemoryAllocatorDumpGuid source,
+ const String& target_node_name) {
+ StringUTF8Adaptor adapter(target_node_name);
+ std::string target_node(adapter.Data(), adapter.length());
+ process_memory_dump_->AddSuballocation(
+ base::trace_event::MemoryAllocatorDumpGuid(source), target_node);
+}
+
+SkTraceMemoryDump* WebProcessMemoryDump::CreateDumpAdapterForSkia(
+ const String& dump_name_prefix) {
+ StringUTF8Adaptor adapter(dump_name_prefix);
+ std::string prefix(adapter.Data(), adapter.length());
+ sk_trace_dump_list_.push_back(std::make_unique<skia::SkiaTraceMemoryDumpImpl>(
+ prefix, level_of_detail_, process_memory_dump_));
+ return sk_trace_dump_list_.back().get();
+}
+
+blink::WebMemoryAllocatorDump*
+WebProcessMemoryDump::CreateDiscardableMemoryAllocatorDump(
+ const std::string& name,
+ base::DiscardableMemory* discardable) {
+ base::trace_event::MemoryAllocatorDump* dump =
+ discardable->CreateMemoryAllocatorDump(name.c_str(),
+ process_memory_dump_);
+ return CreateWebMemoryAllocatorDump(dump);
+}
+
+void WebProcessMemoryDump::DumpHeapUsage(
+ const std::unordered_map<base::trace_event::AllocationContext,
+ base::trace_event::AllocationMetrics>&
+ metrics_by_context,
+ base::trace_event::TraceEventMemoryOverhead& overhead,
+ const char* allocator_name) {
+ process_memory_dump_->DumpHeapUsage(metrics_by_context, overhead,
+ allocator_name);
+}
+
+} // namespace content
diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/tracing/web_process_memory_dump.h b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/web_process_memory_dump.h
new file mode 100644
index 00000000000..6eafa6e8f7b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/web_process_memory_dump.h
@@ -0,0 +1,171 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WebProcessMemoryDump_h
+#define WebProcessMemoryDump_h
+
+#include <map>
+#include <memory>
+#include <unordered_map>
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/trace_event/heap_profiler_allocation_context.h"
+#include "base/trace_event/memory_dump_request_args.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/web_memory_allocator_dump.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+class SkTraceMemoryDump;
+
+namespace base {
+class DiscardableMemory;
+namespace trace_event {
+class MemoryAllocatorDump;
+class ProcessMemoryDump;
+class TraceEventMemoryOverhead;
+} // namespace base
+} // namespace trace_event
+
+namespace skia {
+class SkiaTraceMemoryDumpImpl;
+} // namespace skia
+
+namespace blink {
+
+// Used to specify the type of memory dump the WebProcessMemoryDump should
+// generate on dump requests.
+// TODO(hajimehoshi): Remove this and use base::trace_event::
+// MemoryDumpLevelOfDetail instead.
+enum class WebMemoryDumpLevelOfDetail { kBackground, kLight, kDetailed };
+
+// A container which holds all the dumps for the various allocators for a given
+// process. Embedders of WebMemoryDumpProvider are expected to populate a
+// WebProcessMemoryDump instance with the stats of their allocators.
+class PLATFORM_EXPORT WebProcessMemoryDump final {
+ public:
+ // Creates a standalone WebProcessMemoryDump, which owns the underlying
+ // ProcessMemoryDump.
+ WebProcessMemoryDump();
+
+ // Wraps (without owning) an existing ProcessMemoryDump.
+ explicit WebProcessMemoryDump(
+ base::trace_event::MemoryDumpLevelOfDetail level_of_detail,
+ base::trace_event::ProcessMemoryDump* process_memory_dump);
+
+ ~WebProcessMemoryDump();
+
+ // Creates a new MemoryAllocatorDump with the given name and returns the
+ // empty object back to the caller. |absoluteName| uniquely identifies the
+ // dump within the scope of a ProcessMemoryDump. It is possible to express
+ // nesting by means of a slash-separated path naming (e.g.,
+ // "allocator_name/arena_1/subheap_X").
+ // |guid| is an optional identifier, unique among all processes within the
+ // scope of a global dump. This is only relevant when using
+ // addOwnershipEdge(). If omitted, it will be automatically generated.
+ blink::WebMemoryAllocatorDump* CreateMemoryAllocatorDump(
+ const String& absolute_name);
+ blink::WebMemoryAllocatorDump* CreateMemoryAllocatorDump(
+ const String& absolute_name,
+ blink::WebMemoryAllocatorDumpGuid guid);
+
+ // Gets a previously created MemoryAllocatorDump given its name.
+ blink::WebMemoryAllocatorDump* GetMemoryAllocatorDump(
+ const String& absolute_name) const;
+
+ // Removes all the WebMemoryAllocatorDump(s) contained in this instance.
+ // This WebProcessMemoryDump can be safely reused as if it was new once this
+ // method returns.
+ void Clear();
+
+ // Merges all WebMemoryAllocatorDump(s) contained in |other| inside this
+ // WebProcessMemoryDump, transferring their ownership to this instance.
+ // |other| will be an empty WebProcessMemoryDump after this method returns
+ // and can be reused as if it was new.
+ void TakeAllDumpsFrom(blink::WebProcessMemoryDump* other);
+
+ // Adds an ownership relationship between two MemoryAllocatorDump(s) with
+ // the semantics: |source| owns |target|, and has the effect of attributing
+ // the memory usage of |target| to |source|. |importance| is optional and
+ // relevant only for the cases of co-ownership, where it acts as a z-index:
+ // the owner with the highest importance will be attributed |target|'s
+ // memory.
+ void AddOwnershipEdge(blink::WebMemoryAllocatorDumpGuid source,
+ blink::WebMemoryAllocatorDumpGuid target,
+ int importance);
+ void AddOwnershipEdge(blink::WebMemoryAllocatorDumpGuid source,
+ blink::WebMemoryAllocatorDumpGuid target);
+
+ // Utility method to add a suballocation relationship with the following
+ // semantics: |source| is suballocated from |target_node_name|.
+ // This creates a child node of |target_node_name| and adds an ownership
+ // edge between |source| and the new child node. As a result, the UI will
+ // not account the memory of |source| in the target node.
+ void AddSuballocation(blink::WebMemoryAllocatorDumpGuid source,
+ const String& target_node_name);
+
+ // Returns the SkTraceMemoryDump proxy interface that can be passed to Skia
+ // to dump into this WebProcessMemoryDump. Multiple SkTraceMemoryDump
+ // objects can be created using this method. The created dumpers are owned
+ // by WebProcessMemoryDump and cannot outlive the WebProcessMemoryDump
+ // object owning them. |dumpNamePrefix| is prefix appended to each dump
+ // created by the SkTraceMemoryDump implementation, if the dump should be
+ // placed under different namespace and not "skia".
+ SkTraceMemoryDump* CreateDumpAdapterForSkia(const String& dump_name_prefix);
+
+ const base::trace_event::ProcessMemoryDump* process_memory_dump() const {
+ return process_memory_dump_;
+ }
+
+ blink::WebMemoryAllocatorDump* CreateDiscardableMemoryAllocatorDump(
+ const std::string& name,
+ base::DiscardableMemory* discardable);
+
+ // Dumps heap memory usage. |allocatorName| is used as an absolute name for
+ // base::trace_event::ProcessMemoryDump::DumpHeapUsage().
+ void DumpHeapUsage(
+ const std::unordered_map<base::trace_event::AllocationContext,
+ base::trace_event::AllocationMetrics>&
+ metrics_by_context,
+ base::trace_event::TraceEventMemoryOverhead& overhead,
+ const char* allocator_name);
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(WebProcessMemoryDumpTest, IntegrationTest);
+
+ blink::WebMemoryAllocatorDump* CreateWebMemoryAllocatorDump(
+ base::trace_event::MemoryAllocatorDump* memory_allocator_dump);
+
+ // Only for the case of ProcessMemoryDump being owned (i.e. the default ctor).
+ std::unique_ptr<base::trace_event::ProcessMemoryDump>
+ owned_process_memory_dump_;
+
+ // The underlying ProcessMemoryDump instance to which the
+ // createMemoryAllocatorDump() calls will be proxied to.
+ base::trace_event::ProcessMemoryDump* process_memory_dump_; // Not owned.
+
+ // TODO(ssid): Remove it once this information is added to ProcessMemoryDump.
+ base::trace_event::MemoryDumpLevelOfDetail level_of_detail_;
+
+ // Reverse index of MemoryAllocatorDump -> WebMemoryAllocatorDump wrapper.
+ // By design WebMemoryDumpProvider(s) are not supposed to hold the pointer
+ // to the WebProcessMemoryDump passed as argument of the onMemoryDump() call.
+ // Those pointers are valid only within the scope of the call and can be
+ // safely torn down once the WebProcessMemoryDump itself is destroyed.
+ HashMap<base::trace_event::MemoryAllocatorDump*,
+ std::unique_ptr<WebMemoryAllocatorDump>>
+ memory_allocator_dumps_;
+
+ // Stores SkTraceMemoryDump for the current ProcessMemoryDump.
+ std::vector<std::unique_ptr<skia::SkiaTraceMemoryDumpImpl>>
+ sk_trace_dump_list_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebProcessMemoryDump);
+};
+
+} // namespace blink
+
+#endif // WebProcessMemoryDump_h
diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/tracing/web_process_memory_dump_test.cc b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/web_process_memory_dump_test.cc
new file mode 100644
index 00000000000..7bcfac8c98f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/web_process_memory_dump_test.cc
@@ -0,0 +1,136 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/instrumentation/tracing/web_process_memory_dump.h"
+
+#include "base/memory/discardable_memory.h"
+#include "base/test/test_discardable_memory_allocator.h"
+#include "base/trace_event/memory_allocator_dump.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "base/values.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/web_memory_allocator_dump.h"
+
+namespace blink {
+
+using testing::Contains;
+using testing::Eq;
+using testing::ByRef;
+using base::trace_event::MemoryAllocatorDump;
+
+// Tests that the Chromium<>Blink plumbing that exposes the MemoryInfra classes
+// behaves correctly, performs the right transfers of memory ownerships and
+// doesn't leak objects.
+TEST(WebProcessMemoryDumpTest, IntegrationTest) {
+ std::unique_ptr<base::trace_event::TracedValue> traced_value(
+ new base::trace_event::TracedValue());
+
+ std::unique_ptr<WebProcessMemoryDump> wpmd1(new WebProcessMemoryDump());
+ auto wmad1 = wpmd1->CreateMemoryAllocatorDump("1/1");
+ auto wmad2 = wpmd1->CreateMemoryAllocatorDump("1/2");
+ ASSERT_EQ(wmad1, wpmd1->GetMemoryAllocatorDump("1/1"));
+ ASSERT_EQ(wmad2, wpmd1->GetMemoryAllocatorDump("1/2"));
+
+ std::unique_ptr<WebProcessMemoryDump> wpmd2(new WebProcessMemoryDump());
+ wpmd2->CreateMemoryAllocatorDump("2/1");
+ wpmd2->CreateMemoryAllocatorDump("2/2");
+
+ wpmd1->TakeAllDumpsFrom(wpmd2.get());
+
+ // Make sure that wpmd2 still owns its own PMD, even if empty.
+ ASSERT_NE(static_cast<base::trace_event::ProcessMemoryDump*>(nullptr),
+ wpmd2->process_memory_dump_);
+ ASSERT_EQ(wpmd2->owned_process_memory_dump_.get(),
+ wpmd2->process_memory_dump());
+ ASSERT_TRUE(wpmd2->process_memory_dump()->allocator_dumps().empty());
+
+ // Make sure that wpmd2 is still usable after it has been emptied.
+ auto wmad = wpmd2->CreateMemoryAllocatorDump("2/new");
+ wmad->AddScalar("attr_name", "bytes", 42);
+ ASSERT_EQ(1u, wpmd2->process_memory_dump()->allocator_dumps().size());
+ auto mad = wpmd2->process_memory_dump()->GetAllocatorDump("2/new");
+ ASSERT_NE(static_cast<MemoryAllocatorDump*>(nullptr), mad);
+ ASSERT_EQ(wmad, wpmd2->GetMemoryAllocatorDump("2/new"));
+
+ // Check that the attributes are propagated correctly.
+ MemoryAllocatorDump::Entry expected("attr_name", "bytes", 42);
+ ASSERT_THAT(mad->entries(), Contains(Eq(ByRef(expected))));
+
+ // Check that calling serialization routines doesn't cause a crash.
+ wpmd2->process_memory_dump()->SerializeAllocatorDumpsInto(traced_value.get());
+ wpmd2->process_memory_dump()->SerializeHeapProfilerDumpsInto(
+ traced_value.get());
+
+ // Free the |wpmd2| to check that the memory ownership of the two MAD(s)
+ // has been transferred to |wpmd1|.
+ wpmd2.reset();
+
+ // Now check that |wpmd1| has been effectively merged.
+ ASSERT_EQ(4u, wpmd1->process_memory_dump()->allocator_dumps().size());
+ ASSERT_EQ(1u, wpmd1->process_memory_dump()->allocator_dumps().count("1/1"));
+ ASSERT_EQ(1u, wpmd1->process_memory_dump()->allocator_dumps().count("1/2"));
+ ASSERT_EQ(1u, wpmd1->process_memory_dump()->allocator_dumps().count("2/1"));
+ ASSERT_EQ(1u, wpmd1->process_memory_dump()->allocator_dumps().count("1/2"));
+
+ // Check that also the WMAD wrappers got merged.
+ blink::WebMemoryAllocatorDump* null_wmad = nullptr;
+ ASSERT_NE(null_wmad, wpmd1->GetMemoryAllocatorDump("1/1"));
+ ASSERT_NE(null_wmad, wpmd1->GetMemoryAllocatorDump("1/2"));
+ ASSERT_NE(null_wmad, wpmd1->GetMemoryAllocatorDump("2/1"));
+ ASSERT_NE(null_wmad, wpmd1->GetMemoryAllocatorDump("2/2"));
+
+ // Check that calling serialization routines doesn't cause a crash.
+ traced_value.reset(new base::trace_event::TracedValue);
+ wpmd1->process_memory_dump()->SerializeAllocatorDumpsInto(traced_value.get());
+ wpmd1->process_memory_dump()->SerializeHeapProfilerDumpsInto(
+ traced_value.get());
+
+ // Check that clear() actually works.
+ wpmd1->Clear();
+ ASSERT_TRUE(wpmd1->process_memory_dump()->allocator_dumps().empty());
+ ASSERT_EQ(nullptr, wpmd1->process_memory_dump()->GetAllocatorDump("1/1"));
+ ASSERT_EQ(nullptr, wpmd1->process_memory_dump()->GetAllocatorDump("2/1"));
+
+ // Check that calling serialization routines doesn't cause a crash.
+ traced_value.reset(new base::trace_event::TracedValue);
+ wpmd1->process_memory_dump()->SerializeAllocatorDumpsInto(traced_value.get());
+ wpmd1->process_memory_dump()->SerializeHeapProfilerDumpsInto(
+ traced_value.get());
+
+ // Check if a WebMemoryAllocatorDump created with guid, has correct guid.
+ blink::WebMemoryAllocatorDumpGuid guid =
+ base::trace_event::MemoryAllocatorDumpGuid("id_1").ToUint64();
+ auto wmad3 = wpmd1->CreateMemoryAllocatorDump("1/3", guid);
+ ASSERT_EQ(wmad3->Guid(), guid);
+ ASSERT_EQ(wmad3, wpmd1->GetMemoryAllocatorDump("1/3"));
+
+ // Check that AddOwnershipEdge is propagated correctly.
+ auto wmad4 = wpmd1->CreateMemoryAllocatorDump("1/4");
+ wpmd1->AddOwnershipEdge(wmad4->Guid(), guid);
+ auto allocator_dumps_edges =
+ wpmd1->process_memory_dump()->allocator_dumps_edges();
+ ASSERT_EQ(1u, allocator_dumps_edges.size());
+ auto it = allocator_dumps_edges.begin();
+ ASSERT_NE(allocator_dumps_edges.end(), it);
+ ASSERT_EQ(wmad4->Guid(), it->first.ToUint64());
+ ASSERT_EQ(guid, it->second.target.ToUint64());
+
+ // Check that createDumpAdapterForSkia() works.
+ auto skia_trace_memory_dump = wpmd1->CreateDumpAdapterForSkia("1/skia");
+ ASSERT_TRUE(skia_trace_memory_dump);
+
+ // Check that createDiscardableMemoryAllocatorDump() works.
+ base::TestDiscardableMemoryAllocator discardable_memory_allocator;
+ auto discardable_memory =
+ discardable_memory_allocator.AllocateLockedDiscardableMemory(1024);
+ wpmd1->CreateDiscardableMemoryAllocatorDump("1/discardable",
+ discardable_memory.get());
+ discardable_memory->Unlock();
+
+ wpmd1.reset();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/json/OWNERS b/chromium/third_party/blink/renderer/platform/json/OWNERS
new file mode 100644
index 00000000000..273c6c0bbbe
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/json/OWNERS
@@ -0,0 +1,4 @@
+pfeldman@chromium.org
+iclelland@chromium.org
+
+# COMPONENT: Blink>Internals
diff --git a/chromium/third_party/blink/renderer/platform/json/json_parser.cc b/chromium/third_party/blink/renderer/platform/json/json_parser.cc
new file mode 100644
index 00000000000..a90acd2e0d8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/json/json_parser.cc
@@ -0,0 +1,544 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/json/json_parser.h"
+
+#include "third_party/blink/renderer/platform/decimal.h"
+#include "third_party/blink/renderer/platform/json/json_values.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_to_number.h"
+
+namespace blink {
+
+namespace {
+
+const int kMaxStackLimit = 1000;
+
+enum Token {
+ kObjectBegin,
+ kObjectEnd,
+ kArrayBegin,
+ kArrayEnd,
+ kStringLiteral,
+ kNumber,
+ kBoolTrue,
+ kBoolFalse,
+ kNullToken,
+ kListSeparator,
+ kObjectPairSeparator,
+ kInvalidToken,
+};
+
+template <typename CharType>
+bool ParseConstToken(const CharType* start,
+ const CharType* end,
+ const CharType** token_end,
+ const char* token) {
+ while (start < end && *token != '\0' && *start++ == *token++) {
+ }
+ if (*token != '\0')
+ return false;
+ *token_end = start;
+ return true;
+}
+
+template <typename CharType>
+bool ReadInt(const CharType* start,
+ const CharType* end,
+ const CharType** token_end,
+ bool can_have_leading_zeros) {
+ if (start == end)
+ return false;
+ bool have_leading_zero = '0' == *start;
+ int length = 0;
+ while (start < end && '0' <= *start && *start <= '9') {
+ ++start;
+ ++length;
+ }
+ if (!length)
+ return false;
+ if (!can_have_leading_zeros && length > 1 && have_leading_zero)
+ return false;
+ *token_end = start;
+ return true;
+}
+
+template <typename CharType>
+bool ParseNumberToken(const CharType* start,
+ const CharType* end,
+ const CharType** token_end) {
+ // We just grab the number here. We validate the size in DecodeNumber.
+ // According to RFC4627, a valid number is: [minus] int [frac] [exp]
+ if (start == end)
+ return false;
+ CharType c = *start;
+ if ('-' == c)
+ ++start;
+
+ if (!ReadInt(start, end, &start, false))
+ return false;
+ if (start == end) {
+ *token_end = start;
+ return true;
+ }
+
+ // Optional fraction part
+ c = *start;
+ if ('.' == c) {
+ ++start;
+ if (!ReadInt(start, end, &start, true))
+ return false;
+ if (start == end) {
+ *token_end = start;
+ return true;
+ }
+ c = *start;
+ }
+
+ // Optional exponent part
+ if ('e' == c || 'E' == c) {
+ ++start;
+ if (start == end)
+ return false;
+ c = *start;
+ if ('-' == c || '+' == c) {
+ ++start;
+ if (start == end)
+ return false;
+ }
+ if (!ReadInt(start, end, &start, true))
+ return false;
+ }
+
+ *token_end = start;
+ return true;
+}
+
+template <typename CharType>
+bool ReadHexDigits(const CharType* start,
+ const CharType* end,
+ const CharType** token_end,
+ int digits) {
+ if (end - start < digits)
+ return false;
+ for (int i = 0; i < digits; ++i) {
+ CharType c = *start++;
+ if (!(('0' <= c && c <= '9') || ('a' <= c && c <= 'f') ||
+ ('A' <= c && c <= 'F')))
+ return false;
+ }
+ *token_end = start;
+ return true;
+}
+
+template <typename CharType>
+bool ParseStringToken(const CharType* start,
+ const CharType* end,
+ const CharType** token_end) {
+ while (start < end) {
+ CharType c = *start++;
+ if ('\\' == c) {
+ if (start == end)
+ return false;
+ c = *start++;
+ // Make sure the escaped char is valid.
+ switch (c) {
+ case 'x':
+ if (!ReadHexDigits(start, end, &start, 2))
+ return false;
+ break;
+ case 'u':
+ if (!ReadHexDigits(start, end, &start, 4))
+ return false;
+ break;
+ case '\\':
+ case '/':
+ case 'b':
+ case 'f':
+ case 'n':
+ case 'r':
+ case 't':
+ case 'v':
+ case '"':
+ break;
+ default:
+ return false;
+ }
+ } else if ('"' == c) {
+ *token_end = start;
+ return true;
+ }
+ }
+ return false;
+}
+
+template <typename CharType>
+bool SkipComment(const CharType* start,
+ const CharType* end,
+ const CharType** comment_end) {
+ if (start == end)
+ return false;
+
+ if (*start != '/' || start + 1 >= end)
+ return false;
+ ++start;
+
+ if (*start == '/') {
+ // Single line comment, read to newline.
+ for (++start; start < end; ++start) {
+ if (*start == '\n' || *start == '\r') {
+ *comment_end = start + 1;
+ return true;
+ }
+ }
+ *comment_end = end;
+ // Comment reaches end-of-input, which is fine.
+ return true;
+ }
+
+ if (*start == '*') {
+ CharType previous = '\0';
+ // Block comment, read until end marker.
+ for (++start; start < end; previous = *start++) {
+ if (previous == '*' && *start == '/') {
+ *comment_end = start + 1;
+ return true;
+ }
+ }
+ // Block comment must close before end-of-input.
+ return false;
+ }
+
+ return false;
+}
+
+template <typename CharType>
+void SkipWhitespaceAndComments(const CharType* start,
+ const CharType* end,
+ const CharType** whitespace_end) {
+ while (start < end) {
+ if (IsSpaceOrNewline(*start)) {
+ ++start;
+ } else if (*start == '/') {
+ const CharType* comment_end;
+ if (!SkipComment(start, end, &comment_end))
+ break;
+ start = comment_end;
+ } else {
+ break;
+ }
+ }
+ *whitespace_end = start;
+}
+
+template <typename CharType>
+Token ParseToken(const CharType* start,
+ const CharType* end,
+ const CharType** token_start,
+ const CharType** token_end) {
+ SkipWhitespaceAndComments(start, end, token_start);
+ start = *token_start;
+
+ if (start == end)
+ return kInvalidToken;
+
+ switch (*start) {
+ case 'n':
+ if (ParseConstToken(start, end, token_end, kJSONNullString))
+ return kNullToken;
+ break;
+ case 't':
+ if (ParseConstToken(start, end, token_end, kJSONTrueString))
+ return kBoolTrue;
+ break;
+ case 'f':
+ if (ParseConstToken(start, end, token_end, kJSONFalseString))
+ return kBoolFalse;
+ break;
+ case '[':
+ *token_end = start + 1;
+ return kArrayBegin;
+ case ']':
+ *token_end = start + 1;
+ return kArrayEnd;
+ case ',':
+ *token_end = start + 1;
+ return kListSeparator;
+ case '{':
+ *token_end = start + 1;
+ return kObjectBegin;
+ case '}':
+ *token_end = start + 1;
+ return kObjectEnd;
+ case ':':
+ *token_end = start + 1;
+ return kObjectPairSeparator;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '-':
+ if (ParseNumberToken(start, end, token_end))
+ return kNumber;
+ break;
+ case '"':
+ if (ParseStringToken(start + 1, end, token_end))
+ return kStringLiteral;
+ break;
+ }
+ return kInvalidToken;
+}
+
+template <typename CharType>
+inline int HexToInt(CharType c) {
+ if ('0' <= c && c <= '9')
+ return c - '0';
+ if ('A' <= c && c <= 'F')
+ return c - 'A' + 10;
+ if ('a' <= c && c <= 'f')
+ return c - 'a' + 10;
+ NOTREACHED();
+ return 0;
+}
+
+template <typename CharType>
+bool DecodeString(const CharType* start,
+ const CharType* end,
+ StringBuilder* output) {
+ while (start < end) {
+ UChar c = *start++;
+ if ('\\' != c) {
+ output->Append(c);
+ continue;
+ }
+ if (start == end)
+ return false;
+ c = *start++;
+
+ if (c == 'x') {
+ // \x is not supported.
+ return false;
+ }
+
+ switch (c) {
+ case '"':
+ case '/':
+ case '\\':
+ break;
+ case 'b':
+ c = '\b';
+ break;
+ case 'f':
+ c = '\f';
+ break;
+ case 'n':
+ c = '\n';
+ break;
+ case 'r':
+ c = '\r';
+ break;
+ case 't':
+ c = '\t';
+ break;
+ case 'v':
+ c = '\v';
+ break;
+ case 'u':
+ c = (HexToInt(*start) << 12) + (HexToInt(*(start + 1)) << 8) +
+ (HexToInt(*(start + 2)) << 4) + HexToInt(*(start + 3));
+ start += 4;
+ break;
+ default:
+ return false;
+ }
+ output->Append(c);
+ }
+ return true;
+}
+
+template <typename CharType>
+bool DecodeString(const CharType* start, const CharType* end, String* output) {
+ if (start == end) {
+ *output = "";
+ return true;
+ }
+ if (start > end)
+ return false;
+ StringBuilder buffer;
+ buffer.ReserveCapacity(end - start);
+ if (!DecodeString(start, end, &buffer))
+ return false;
+ *output = buffer.ToString();
+ // Validate constructed utf16 string.
+ if (output->Utf8(kStrictUTF8Conversion).IsNull())
+ return false;
+ return true;
+}
+
+template <typename CharType>
+std::unique_ptr<JSONValue> BuildValue(const CharType* start,
+ const CharType* end,
+ const CharType** value_token_end,
+ int max_depth) {
+ if (max_depth == 0)
+ return nullptr;
+
+ std::unique_ptr<JSONValue> result;
+ const CharType* token_start;
+ const CharType* token_end;
+ Token token = ParseToken(start, end, &token_start, &token_end);
+ switch (token) {
+ case kInvalidToken:
+ return nullptr;
+ case kNullToken:
+ result = JSONValue::Null();
+ break;
+ case kBoolTrue:
+ result = JSONBasicValue::Create(true);
+ break;
+ case kBoolFalse:
+ result = JSONBasicValue::Create(false);
+ break;
+ case kNumber: {
+ bool ok;
+ double value =
+ CharactersToDouble(token_start, token_end - token_start, &ok);
+ if (Decimal::FromDouble(value).IsInfinity())
+ ok = false;
+ if (!ok)
+ return nullptr;
+ int number = static_cast<int>(value);
+ if (number == value)
+ result = JSONBasicValue::Create(number);
+ else
+ result = JSONBasicValue::Create(value);
+ break;
+ }
+ case kStringLiteral: {
+ String value;
+ bool ok = DecodeString(token_start + 1, token_end - 1, &value);
+ if (!ok)
+ return nullptr;
+ result = JSONString::Create(value);
+ break;
+ }
+ case kArrayBegin: {
+ std::unique_ptr<JSONArray> array = JSONArray::Create();
+ start = token_end;
+ token = ParseToken(start, end, &token_start, &token_end);
+ while (token != kArrayEnd) {
+ std::unique_ptr<JSONValue> array_node =
+ BuildValue(start, end, &token_end, max_depth - 1);
+ if (!array_node)
+ return nullptr;
+ array->PushValue(std::move(array_node));
+
+ // After a list value, we expect a comma or the end of the list.
+ start = token_end;
+ token = ParseToken(start, end, &token_start, &token_end);
+ if (token == kListSeparator) {
+ start = token_end;
+ token = ParseToken(start, end, &token_start, &token_end);
+ if (token == kArrayEnd)
+ return nullptr;
+ } else if (token != kArrayEnd) {
+ // Unexpected value after list value. Bail out.
+ return nullptr;
+ }
+ }
+ if (token != kArrayEnd)
+ return nullptr;
+ result = std::move(array);
+ break;
+ }
+ case kObjectBegin: {
+ std::unique_ptr<JSONObject> object = JSONObject::Create();
+ start = token_end;
+ token = ParseToken(start, end, &token_start, &token_end);
+ while (token != kObjectEnd) {
+ if (token != kStringLiteral)
+ return nullptr;
+ String key;
+ if (!DecodeString(token_start + 1, token_end - 1, &key))
+ return nullptr;
+ start = token_end;
+
+ token = ParseToken(start, end, &token_start, &token_end);
+ if (token != kObjectPairSeparator)
+ return nullptr;
+ start = token_end;
+
+ std::unique_ptr<JSONValue> value =
+ BuildValue(start, end, &token_end, max_depth - 1);
+ if (!value)
+ return nullptr;
+ object->SetValue(key, std::move(value));
+ start = token_end;
+
+ // After a key/value pair, we expect a comma or the end of the
+ // object.
+ token = ParseToken(start, end, &token_start, &token_end);
+ if (token == kListSeparator) {
+ start = token_end;
+ token = ParseToken(start, end, &token_start, &token_end);
+ if (token == kObjectEnd)
+ return nullptr;
+ } else if (token != kObjectEnd) {
+ // Unexpected value after last object value. Bail out.
+ return nullptr;
+ }
+ }
+ if (token != kObjectEnd)
+ return nullptr;
+ result = std::move(object);
+ break;
+ }
+
+ default:
+ // We got a token that's not a value.
+ return nullptr;
+ }
+
+ SkipWhitespaceAndComments(token_end, end, value_token_end);
+ return result;
+}
+
+template <typename CharType>
+std::unique_ptr<JSONValue> ParseJSONInternal(const CharType* start,
+ unsigned length,
+ int max_depth) {
+ const CharType* end = start + length;
+ const CharType* token_end;
+ std::unique_ptr<JSONValue> value =
+ BuildValue(start, end, &token_end, max_depth);
+ if (!value || token_end != end)
+ return nullptr;
+ return value;
+}
+
+} // anonymous namespace
+
+std::unique_ptr<JSONValue> ParseJSON(const String& json) {
+ return ParseJSON(json, kMaxStackLimit);
+}
+
+std::unique_ptr<JSONValue> ParseJSON(const String& json, int max_depth) {
+ if (json.IsEmpty())
+ return nullptr;
+ if (max_depth < 0)
+ max_depth = 0;
+ if (max_depth > kMaxStackLimit)
+ max_depth = kMaxStackLimit;
+ if (json.Is8Bit())
+ return ParseJSONInternal(json.Characters8(), json.length(), max_depth);
+ return ParseJSONInternal(json.Characters16(), json.length(), max_depth);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/json/json_parser.h b/chromium/third_party/blink/renderer/platform/json/json_parser.h
new file mode 100644
index 00000000000..b8e1383108c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/json/json_parser.h
@@ -0,0 +1,24 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_JSON_JSON_PARSER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_JSON_JSON_PARSER_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+#include <memory>
+
+namespace blink {
+
+class JSONValue;
+
+PLATFORM_EXPORT std::unique_ptr<JSONValue> ParseJSON(const String& json);
+
+PLATFORM_EXPORT std::unique_ptr<JSONValue> ParseJSON(const String& json,
+ int max_depth);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_JSON_JSON_PARSER_H_
diff --git a/chromium/third_party/blink/renderer/platform/json/json_parser_fuzzer.cc b/chromium/third_party/blink/renderer/platform/json/json_parser_fuzzer.cc
new file mode 100644
index 00000000000..4f7f22496f4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/json/json_parser_fuzzer.cc
@@ -0,0 +1,18 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/json/json_parser.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include "third_party/blink/renderer/platform/json/json_values.h"
+#include "third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ static blink::BlinkFuzzerTestSupport test_support =
+ blink::BlinkFuzzerTestSupport();
+ blink::ParseJSON(WTF::String(data, size), 500);
+ return 0;
+}
diff --git a/chromium/third_party/blink/renderer/platform/json/json_parser_test.cc b/chromium/third_party/blink/renderer/platform/json/json_parser_test.cc
new file mode 100644
index 00000000000..443a218334b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/json/json_parser_test.cc
@@ -0,0 +1,550 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/json/json_parser.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/json/json_values.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+
+namespace blink {
+
+TEST(JSONParserTest, Reading) {
+ JSONValue* tmp_value;
+ std::unique_ptr<JSONValue> root;
+ std::unique_ptr<JSONValue> root2;
+ String str_val;
+ int int_val = 0;
+
+ // some whitespace checking
+ root = ParseJSON(" null ");
+ ASSERT_TRUE(root.get());
+ EXPECT_EQ(JSONValue::kTypeNull, root->GetType());
+
+ // Invalid JSON string
+ root = ParseJSON("nu");
+ EXPECT_FALSE(root.get());
+
+ // Simple bool
+ root = ParseJSON("true ");
+ ASSERT_TRUE(root.get());
+ EXPECT_EQ(JSONValue::kTypeBoolean, root->GetType());
+
+ // Embedded comment
+ root = ParseJSON("40 /*/");
+ EXPECT_FALSE(root.get());
+ root = ParseJSON("/* comment */null");
+ ASSERT_TRUE(root.get());
+ EXPECT_EQ(JSONValue::kTypeNull, root->GetType());
+ root = ParseJSON("40 /* comment */");
+ ASSERT_TRUE(root.get());
+ EXPECT_EQ(JSONValue::kTypeInteger, root->GetType());
+ EXPECT_TRUE(root->AsInteger(&int_val));
+ EXPECT_EQ(40, int_val);
+ root = ParseJSON("/**/ 40 /* multi-line\n comment */ // more comment");
+ ASSERT_TRUE(root.get());
+ EXPECT_EQ(JSONValue::kTypeInteger, root->GetType());
+ EXPECT_TRUE(root->AsInteger(&int_val));
+ EXPECT_EQ(40, int_val);
+ root = ParseJSON("true // comment");
+ ASSERT_TRUE(root.get());
+ EXPECT_EQ(JSONValue::kTypeBoolean, root->GetType());
+ root = ParseJSON("/* comment */\"sample string\"");
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->AsString(&str_val));
+ EXPECT_EQ("sample string", str_val);
+ root = ParseJSON("[1, /* comment, 2 ] */ \n 3]");
+ ASSERT_TRUE(root.get());
+ JSONArray* list = JSONArray::Cast(root.get());
+ ASSERT_TRUE(list);
+ EXPECT_EQ(2u, list->size());
+ tmp_value = list->at(0);
+ ASSERT_TRUE(tmp_value);
+ EXPECT_TRUE(tmp_value->AsInteger(&int_val));
+ EXPECT_EQ(1, int_val);
+ tmp_value = list->at(1);
+ ASSERT_TRUE(tmp_value);
+ EXPECT_TRUE(tmp_value->AsInteger(&int_val));
+ EXPECT_EQ(3, int_val);
+ root = ParseJSON("[1, /*a*/2, 3]");
+ ASSERT_TRUE(root.get());
+ list = JSONArray::Cast(root.get());
+ ASSERT_TRUE(list);
+ EXPECT_EQ(3u, list->size());
+ root = ParseJSON("/* comment **/42");
+ ASSERT_TRUE(root.get());
+ EXPECT_EQ(JSONValue::kTypeInteger, root->GetType());
+ EXPECT_TRUE(root->AsInteger(&int_val));
+ EXPECT_EQ(42, int_val);
+ root = ParseJSON(
+ "/* comment **/\n"
+ "// */ 43\n"
+ "44");
+ ASSERT_TRUE(root.get());
+ EXPECT_EQ(JSONValue::kTypeInteger, root->GetType());
+ EXPECT_TRUE(root->AsInteger(&int_val));
+ EXPECT_EQ(44, int_val);
+
+ // Test number formats
+ root = ParseJSON("43");
+ ASSERT_TRUE(root.get());
+ EXPECT_EQ(JSONValue::kTypeInteger, root->GetType());
+ EXPECT_TRUE(root->AsInteger(&int_val));
+ EXPECT_EQ(43, int_val);
+
+ // According to RFC4627, oct, hex, and leading zeros are invalid JSON.
+ root = ParseJSON("043");
+ EXPECT_FALSE(root.get());
+ root = ParseJSON("0x43");
+ EXPECT_FALSE(root.get());
+ root = ParseJSON("00");
+ EXPECT_FALSE(root.get());
+
+ // Test 0 (which needs to be special cased because of the leading zero
+ // clause).
+ root = ParseJSON("0");
+ ASSERT_TRUE(root.get());
+ EXPECT_EQ(JSONValue::kTypeInteger, root->GetType());
+ int_val = 1;
+ EXPECT_TRUE(root->AsInteger(&int_val));
+ EXPECT_EQ(0, int_val);
+
+ // Numbers that overflow ints should succeed, being internally promoted to
+ // storage as doubles
+ root = ParseJSON("2147483648");
+ ASSERT_TRUE(root.get());
+ double double_val;
+ EXPECT_EQ(JSONValue::kTypeDouble, root->GetType());
+ double_val = 0.0;
+ EXPECT_TRUE(root->AsDouble(&double_val));
+ EXPECT_DOUBLE_EQ(2147483648.0, double_val);
+ root = ParseJSON("-2147483649");
+ ASSERT_TRUE(root.get());
+ EXPECT_EQ(JSONValue::kTypeDouble, root->GetType());
+ double_val = 0.0;
+ EXPECT_TRUE(root->AsDouble(&double_val));
+ EXPECT_DOUBLE_EQ(-2147483649.0, double_val);
+
+ // Parse a double
+ root = ParseJSON("43.1");
+ ASSERT_TRUE(root.get());
+ EXPECT_EQ(JSONValue::kTypeDouble, root->GetType());
+ double_val = 0.0;
+ EXPECT_TRUE(root->AsDouble(&double_val));
+ EXPECT_DOUBLE_EQ(43.1, double_val);
+
+ root = ParseJSON("4.3e-1");
+ ASSERT_TRUE(root.get());
+ EXPECT_EQ(JSONValue::kTypeDouble, root->GetType());
+ double_val = 0.0;
+ EXPECT_TRUE(root->AsDouble(&double_val));
+ EXPECT_DOUBLE_EQ(.43, double_val);
+
+ root = ParseJSON("2.1e0");
+ ASSERT_TRUE(root.get());
+ EXPECT_EQ(JSONValue::kTypeDouble, root->GetType());
+ double_val = 0.0;
+ EXPECT_TRUE(root->AsDouble(&double_val));
+ EXPECT_DOUBLE_EQ(2.1, double_val);
+
+ root = ParseJSON("2.1e+0001");
+ ASSERT_TRUE(root.get());
+ EXPECT_EQ(JSONValue::kTypeInteger, root->GetType());
+ double_val = 0.0;
+ EXPECT_TRUE(root->AsDouble(&double_val));
+ EXPECT_DOUBLE_EQ(21.0, double_val);
+
+ root = ParseJSON("0.01");
+ ASSERT_TRUE(root.get());
+ EXPECT_EQ(JSONValue::kTypeDouble, root->GetType());
+ double_val = 0.0;
+ EXPECT_TRUE(root->AsDouble(&double_val));
+ EXPECT_DOUBLE_EQ(0.01, double_val);
+
+ root = ParseJSON("1.00");
+ ASSERT_TRUE(root.get());
+ EXPECT_EQ(JSONValue::kTypeInteger, root->GetType());
+ double_val = 0.0;
+ EXPECT_TRUE(root->AsDouble(&double_val));
+ EXPECT_DOUBLE_EQ(1.0, double_val);
+
+ // Fractional parts must have a digit before and after the decimal point.
+ root = ParseJSON("1.");
+ EXPECT_FALSE(root.get());
+ root = ParseJSON(".1");
+ EXPECT_FALSE(root.get());
+ root = ParseJSON("1.e10");
+ EXPECT_FALSE(root.get());
+
+ // Exponent must have a digit following the 'e'.
+ root = ParseJSON("1e");
+ EXPECT_FALSE(root.get());
+ root = ParseJSON("1E");
+ EXPECT_FALSE(root.get());
+ root = ParseJSON("1e1.");
+ EXPECT_FALSE(root.get());
+ root = ParseJSON("1e1.0");
+ EXPECT_FALSE(root.get());
+
+ // INF/-INF/NaN are not valid
+ root = ParseJSON("NaN");
+ EXPECT_FALSE(root.get());
+ root = ParseJSON("nan");
+ EXPECT_FALSE(root.get());
+ root = ParseJSON("inf");
+ EXPECT_FALSE(root.get());
+
+ // Invalid number formats
+ root = ParseJSON("4.3.1");
+ EXPECT_FALSE(root.get());
+ root = ParseJSON("4e3.1");
+ EXPECT_FALSE(root.get());
+
+ // Test string parser
+ root = ParseJSON("\"hello world\"");
+ ASSERT_TRUE(root.get());
+ EXPECT_EQ(JSONValue::kTypeString, root->GetType());
+ EXPECT_TRUE(root->AsString(&str_val));
+ EXPECT_EQ("hello world", str_val);
+
+ // Empty string
+ root = ParseJSON("\"\"");
+ ASSERT_TRUE(root.get());
+ EXPECT_EQ(JSONValue::kTypeString, root->GetType());
+ EXPECT_TRUE(root->AsString(&str_val));
+ EXPECT_EQ("", str_val);
+
+ // Test basic string escapes
+ root = ParseJSON("\" \\\"\\\\\\/\\b\\f\\n\\r\\t\\v\"");
+ ASSERT_TRUE(root.get());
+ EXPECT_EQ(JSONValue::kTypeString, root->GetType());
+ EXPECT_TRUE(root->AsString(&str_val));
+ EXPECT_EQ(" \"\\/\b\f\n\r\t\v", str_val);
+
+ // Test hex and unicode escapes including the null character.
+ root = ParseJSON("\"\\x41\\x00\\u1234\"");
+ EXPECT_FALSE(root.get());
+
+ // Test invalid strings
+ root = ParseJSON("\"no closing quote");
+ EXPECT_FALSE(root.get());
+ root = ParseJSON("\"\\z invalid escape char\"");
+ EXPECT_FALSE(root.get());
+ root = ParseJSON("\"not enough escape chars\\u123\"");
+ EXPECT_FALSE(root.get());
+ root = ParseJSON("\"extra backslash at end of input\\\"");
+ EXPECT_FALSE(root.get());
+
+ // Basic array
+ root = ParseJSON("[true, false, null]");
+ ASSERT_TRUE(root.get());
+ EXPECT_EQ(JSONValue::kTypeArray, root->GetType());
+ list = JSONArray::Cast(root.get());
+ ASSERT_TRUE(list);
+ EXPECT_EQ(3U, list->size());
+
+ // Empty array
+ root = ParseJSON("[]");
+ ASSERT_TRUE(root.get());
+ EXPECT_EQ(JSONValue::kTypeArray, root->GetType());
+ list = JSONArray::Cast(root.get());
+ ASSERT_TRUE(list);
+ EXPECT_EQ(0U, list->size());
+
+ // Nested arrays
+ root = ParseJSON("[[true], [], [false, [], [null]], null]");
+ ASSERT_TRUE(root.get());
+ EXPECT_EQ(JSONValue::kTypeArray, root->GetType());
+ list = JSONArray::Cast(root.get());
+ ASSERT_TRUE(list);
+ EXPECT_EQ(4U, list->size());
+
+ // Invalid, missing close brace.
+ root = ParseJSON("[[true], [], [false, [], [null]], null");
+ EXPECT_FALSE(root.get());
+
+ // Invalid, too many commas
+ root = ParseJSON("[true,, null]");
+ EXPECT_FALSE(root.get());
+
+ // Invalid, no commas
+ root = ParseJSON("[true null]");
+ EXPECT_FALSE(root.get());
+
+ // Invalid, trailing comma
+ root = ParseJSON("[true,]");
+ EXPECT_FALSE(root.get());
+
+ root = ParseJSON("[true]");
+ ASSERT_TRUE(root.get());
+ EXPECT_EQ(JSONValue::kTypeArray, root->GetType());
+ list = JSONArray::Cast(root.get());
+ ASSERT_TRUE(list);
+ EXPECT_EQ(1U, list->size());
+ tmp_value = list->at(0);
+ ASSERT_TRUE(tmp_value);
+ EXPECT_EQ(JSONValue::kTypeBoolean, tmp_value->GetType());
+ bool bool_value = false;
+ EXPECT_TRUE(tmp_value->AsBoolean(&bool_value));
+ EXPECT_TRUE(bool_value);
+
+ // Don't allow empty elements.
+ root = ParseJSON("[,]");
+ EXPECT_FALSE(root.get());
+ root = ParseJSON("[true,,]");
+ EXPECT_FALSE(root.get());
+ root = ParseJSON("[,true,]");
+ EXPECT_FALSE(root.get());
+ root = ParseJSON("[true,,false]");
+ EXPECT_FALSE(root.get());
+
+ // Test objects
+ root = ParseJSON("{}");
+ ASSERT_TRUE(root.get());
+ EXPECT_EQ(JSONValue::kTypeObject, root->GetType());
+
+ root = ParseJSON("{\"number\":9.87654321, \"null\":null , \"S\" : \"str\" }");
+ ASSERT_TRUE(root.get());
+ EXPECT_EQ(JSONValue::kTypeObject, root->GetType());
+ JSONObject* object_val = JSONObject::Cast(root.get());
+ ASSERT_TRUE(object_val);
+ double_val = 0.0;
+ EXPECT_TRUE(object_val->GetDouble("number", &double_val));
+ EXPECT_DOUBLE_EQ(9.87654321, double_val);
+ JSONValue* null_val = object_val->Get("null");
+ ASSERT_TRUE(null_val);
+ EXPECT_EQ(JSONValue::kTypeNull, null_val->GetType());
+ EXPECT_TRUE(object_val->GetString("S", &str_val));
+ EXPECT_EQ("str", str_val);
+
+ // Test newline equivalence.
+ root2 = ParseJSON(
+ "{\n"
+ " \"number\":9.87654321,\n"
+ " \"null\":null,\n"
+ " \"S\":\"str\"\n"
+ "}\n");
+ ASSERT_TRUE(root2.get());
+ EXPECT_EQ(root->ToJSONString(), root2->ToJSONString());
+
+ root2 = ParseJSON(
+ "{\r\n"
+ " \"number\":9.87654321,\r\n"
+ " \"null\":null,\r\n"
+ " \"S\":\"str\"\r\n"
+ "}\r\n");
+ ASSERT_TRUE(root2.get());
+ EXPECT_EQ(root->ToJSONString(), root2->ToJSONString());
+
+ // Test nesting
+ root = ParseJSON("{\"inner\":{\"array\":[true]},\"false\":false,\"d\":{}}");
+ ASSERT_TRUE(root.get());
+ EXPECT_EQ(JSONValue::kTypeObject, root->GetType());
+ object_val = JSONObject::Cast(root.get());
+ ASSERT_TRUE(object_val);
+ JSONObject* inner_object = object_val->GetJSONObject("inner");
+ ASSERT_TRUE(inner_object);
+ JSONArray* inner_array = inner_object->GetArray("array");
+ ASSERT_TRUE(inner_array);
+ EXPECT_EQ(1U, inner_array->size());
+ bool_value = true;
+ EXPECT_TRUE(object_val->GetBoolean("false", &bool_value));
+ EXPECT_FALSE(bool_value);
+ inner_object = object_val->GetJSONObject("d");
+ EXPECT_TRUE(inner_object);
+
+ // Test keys with periods
+ root = ParseJSON("{\"a.b\":3,\"c\":2,\"d.e.f\":{\"g.h.i.j\":1}}");
+ ASSERT_TRUE(root.get());
+ EXPECT_EQ(JSONValue::kTypeObject, root->GetType());
+ object_val = JSONObject::Cast(root.get());
+ ASSERT_TRUE(object_val);
+ int integer_value = 0;
+ EXPECT_TRUE(object_val->GetInteger("a.b", &integer_value));
+ EXPECT_EQ(3, integer_value);
+ EXPECT_TRUE(object_val->GetInteger("c", &integer_value));
+ EXPECT_EQ(2, integer_value);
+ inner_object = object_val->GetJSONObject("d.e.f");
+ ASSERT_TRUE(inner_object);
+ EXPECT_EQ(1U, inner_object->size());
+ EXPECT_TRUE(inner_object->GetInteger("g.h.i.j", &integer_value));
+ EXPECT_EQ(1, integer_value);
+
+ root = ParseJSON("{\"a\":{\"b\":2},\"a.b\":1}");
+ ASSERT_TRUE(root.get());
+ EXPECT_EQ(JSONValue::kTypeObject, root->GetType());
+ object_val = JSONObject::Cast(root.get());
+ ASSERT_TRUE(object_val);
+ inner_object = object_val->GetJSONObject("a");
+ ASSERT_TRUE(inner_object);
+ EXPECT_TRUE(inner_object->GetInteger("b", &integer_value));
+ EXPECT_EQ(2, integer_value);
+ EXPECT_TRUE(object_val->GetInteger("a.b", &integer_value));
+ EXPECT_EQ(1, integer_value);
+
+ // Invalid, no closing brace
+ root = ParseJSON("{\"a\": true");
+ EXPECT_FALSE(root.get());
+
+ // Invalid, keys must be quoted
+ root = ParseJSON("{foo:true}");
+ EXPECT_FALSE(root.get());
+
+ // Invalid, trailing comma
+ root = ParseJSON("{\"a\":true,}");
+ EXPECT_FALSE(root.get());
+
+ // Invalid, too many commas
+ root = ParseJSON("{\"a\":true,,\"b\":false}");
+ EXPECT_FALSE(root.get());
+
+ // Invalid, no separator
+ root = ParseJSON("{\"a\" \"b\"}");
+ EXPECT_FALSE(root.get());
+
+ // Invalid, lone comma.
+ root = ParseJSON("{,}");
+ EXPECT_FALSE(root.get());
+ root = ParseJSON("{\"a\":true,,}");
+ EXPECT_FALSE(root.get());
+ root = ParseJSON("{,\"a\":true}");
+ EXPECT_FALSE(root.get());
+ root = ParseJSON("{\"a\":true,,\"b\":false}");
+ EXPECT_FALSE(root.get());
+
+ // Test stack overflow
+ StringBuilder evil;
+ evil.ReserveCapacity(2000000);
+ for (int i = 0; i < 1000000; ++i)
+ evil.Append('[');
+ for (int i = 0; i < 1000000; ++i)
+ evil.Append(']');
+ root = ParseJSON(evil.ToString());
+ EXPECT_FALSE(root.get());
+
+ // A few thousand adjacent lists is fine.
+ StringBuilder not_evil;
+ not_evil.ReserveCapacity(15010);
+ not_evil.Append('[');
+ for (int i = 0; i < 5000; ++i)
+ not_evil.Append("[],");
+ not_evil.Append("[]]");
+ root = ParseJSON(not_evil.ToString());
+ ASSERT_TRUE(root.get());
+ EXPECT_EQ(JSONValue::kTypeArray, root->GetType());
+ list = JSONArray::Cast(root.get());
+ ASSERT_TRUE(list);
+ EXPECT_EQ(5001U, list->size());
+
+ // Test utf8 encoded input
+ root = ParseJSON("\"\\xe7\\xbd\\x91\\xe9\\xa1\\xb5\"");
+ ASSERT_FALSE(root.get());
+
+ // Test utf16 encoded strings.
+ root = ParseJSON("\"\\u20ac3,14\"");
+ ASSERT_TRUE(root.get());
+ EXPECT_EQ(JSONValue::kTypeString, root->GetType());
+ EXPECT_TRUE(root->AsString(&str_val));
+ UChar tmp2[] = {0x20ac, 0x33, 0x2c, 0x31, 0x34};
+ EXPECT_EQ(String(tmp2, WTF_ARRAY_LENGTH(tmp2)), str_val);
+
+ root = ParseJSON("\"\\ud83d\\udca9\\ud83d\\udc6c\"");
+ ASSERT_TRUE(root.get());
+ EXPECT_EQ(JSONValue::kTypeString, root->GetType());
+ EXPECT_TRUE(root->AsString(&str_val));
+ UChar tmp3[] = {0xd83d, 0xdca9, 0xd83d, 0xdc6c};
+ EXPECT_EQ(String(tmp3, WTF_ARRAY_LENGTH(tmp3)), str_val);
+
+ // Test literal root objects.
+ root = ParseJSON("null");
+ EXPECT_EQ(JSONValue::kTypeNull, root->GetType());
+
+ root = ParseJSON("true");
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->AsBoolean(&bool_value));
+ EXPECT_TRUE(bool_value);
+
+ root = ParseJSON("10");
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->AsInteger(&integer_value));
+ EXPECT_EQ(10, integer_value);
+
+ root = ParseJSON("\"root\"");
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->AsString(&str_val));
+ EXPECT_EQ("root", str_val);
+}
+
+TEST(JSONParserTest, InvalidSanity) {
+ const char* const kInvalidJson[] = {
+ "/* test *", "{\"foo\"", "{\"foo\":", " [", "\"\\u123g\"", "{\n\"eh:\n}",
+ "////", "*/**/", "/**/", "/*/", "//**/", "\"\\"};
+
+ for (size_t i = 0; i < WTF_ARRAY_LENGTH(kInvalidJson); ++i) {
+ std::unique_ptr<JSONValue> result = ParseJSON(kInvalidJson[i]);
+ EXPECT_FALSE(result.get());
+ }
+}
+
+// Test that the nesting depth can be limited to values less than 1000, but
+// cannot be extended past that maximum.
+TEST(JSONParserTest, LimitedDepth) {
+ std::unique_ptr<JSONValue> root;
+
+ // Test cases. Each pair is a JSON string, and the minimum depth required
+ // to successfully parse that string.
+ std::vector<std::pair<const char*, int>> test_cases = {
+ {"[[[[[]]]]]", 5},
+ {"[[[[[\"a\"]]]]]", 6},
+ {"[[],[],[],[],[]]", 2},
+ {"{\"a\":{\"a\":{\"a\":{\"a\":{\"a\": \"a\"}}}}}", 6},
+ {"\"root\"", 1}};
+
+ for (const auto& test_case : test_cases) {
+ // Each test case should parse successfully at the default depth
+ root = ParseJSON(test_case.first);
+ EXPECT_TRUE(root.get());
+
+ // ... and should parse successfully at the minimum depth
+ root = ParseJSON(test_case.first, test_case.second);
+ EXPECT_TRUE(root.get());
+
+ // ... but should fail to parse at a shallower depth.
+ root = ParseJSON(test_case.first, test_case.second - 1);
+ EXPECT_FALSE(root.get());
+ }
+
+ // Test that everything fails to parse with depth 0
+ root = ParseJSON("", 0);
+ EXPECT_FALSE(root.get());
+ root = ParseJSON("", -1);
+ EXPECT_FALSE(root.get());
+ root = ParseJSON("true", 0);
+ EXPECT_FALSE(root.get());
+
+ // Test that the limit can be set to the constant maximum.
+ StringBuilder evil;
+ evil.ReserveCapacity(2002);
+ for (int i = 0; i < 1000; ++i)
+ evil.Append('[');
+ for (int i = 0; i < 1000; ++i)
+ evil.Append(']');
+ root = ParseJSON(evil.ToString());
+ EXPECT_TRUE(root.get());
+ root = ParseJSON(evil.ToString(), 1000);
+ EXPECT_TRUE(root.get());
+
+ // Test that the limit cannot be set higher than the constant maximum.
+ evil.Clear();
+ for (int i = 0; i < 1001; ++i)
+ evil.Append('[');
+ for (int i = 0; i < 1001; ++i)
+ evil.Append(']');
+ root = ParseJSON(evil.ToString());
+ EXPECT_FALSE(root.get());
+ root = ParseJSON(evil.ToString(), 1001);
+ EXPECT_FALSE(root.get());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/json/json_values.cc b/chromium/third_party/blink/renderer/platform/json/json_values.cc
new file mode 100644
index 00000000000..0ccaad7fc7f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/json/json_values.cc
@@ -0,0 +1,491 @@
+/*
+ * Copyright (C) 2010 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 "third_party/blink/renderer/platform/json/json_values.h"
+
+#include <algorithm>
+#include <cmath>
+#include "third_party/blink/renderer/platform/decimal.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+
+namespace blink {
+
+namespace {
+
+inline bool EscapeChar(UChar c, StringBuilder* 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 '\\':
+ dst->Append("\\\\");
+ break;
+ case '"':
+ dst->Append("\\\"");
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+const LChar kHexDigits[17] = "0123456789ABCDEF";
+
+void AppendUnsignedAsHex(UChar number, StringBuilder* dst) {
+ dst->Append("\\u");
+ for (size_t i = 0; i < 4; ++i) {
+ dst->Append(kHexDigits[(number & 0xF000) >> 12]);
+ number <<= 4;
+ }
+}
+
+void WriteIndent(int depth, StringBuilder* output) {
+ for (int i = 0; i < depth; ++i)
+ output->Append(" ");
+}
+
+} // anonymous namespace
+
+const char* kJSONNullString = "null";
+const char* kJSONTrueString = "true";
+const char* kJSONFalseString = "false";
+
+void EscapeStringForJSON(const String& str, StringBuilder* dst) {
+ for (unsigned i = 0; i < str.length(); ++i) {
+ UChar c = str[i];
+ if (!EscapeChar(c, dst)) {
+ if (c < 32 || c > 126 || c == '<' || c == '>') {
+ // 1. Escaping <, > to prevent script execution.
+ // 2. Technically, we could also pass through c > 126 as UTF8, but this
+ // is also optional. It would also be a pain to implement here.
+ AppendUnsignedAsHex(c, dst);
+ } else {
+ dst->Append(c);
+ }
+ }
+ }
+}
+
+void DoubleQuoteStringForJSON(const String& str, StringBuilder* dst) {
+ dst->Append('"');
+ EscapeStringForJSON(str, dst);
+ dst->Append('"');
+}
+
+String JSONValue::QuoteString(const String& input) {
+ StringBuilder builder;
+ DoubleQuoteStringForJSON(input, &builder);
+ return builder.ToString();
+}
+
+bool JSONValue::AsBoolean(bool*) const {
+ return false;
+}
+
+bool JSONValue::AsDouble(double*) const {
+ return false;
+}
+
+bool JSONValue::AsInteger(int*) const {
+ return false;
+}
+
+bool JSONValue::AsString(String*) const {
+ return false;
+}
+
+String JSONValue::ToJSONString() const {
+ StringBuilder result;
+ result.ReserveCapacity(512);
+ WriteJSON(&result);
+ return result.ToString();
+}
+
+String JSONValue::ToPrettyJSONString() const {
+ StringBuilder result;
+ result.ReserveCapacity(512);
+ PrettyWriteJSON(&result);
+ return result.ToString();
+}
+
+void JSONValue::WriteJSON(StringBuilder* output) const {
+ DCHECK(type_ == kTypeNull);
+ output->Append(kJSONNullString, 4);
+}
+
+void JSONValue::PrettyWriteJSON(StringBuilder* output) const {
+ PrettyWriteJSONInternal(output, 0);
+ output->Append('\n');
+}
+
+void JSONValue::PrettyWriteJSONInternal(StringBuilder* output,
+ int depth) const {
+ WriteJSON(output);
+}
+
+std::unique_ptr<JSONValue> JSONValue::Clone() const {
+ return JSONValue::Null();
+}
+
+bool JSONBasicValue::AsBoolean(bool* output) const {
+ if (GetType() != kTypeBoolean)
+ return false;
+ *output = bool_value_;
+ return true;
+}
+
+bool JSONBasicValue::AsDouble(double* output) const {
+ if (GetType() == kTypeDouble) {
+ *output = double_value_;
+ return true;
+ }
+ if (GetType() == kTypeInteger) {
+ *output = integer_value_;
+ return true;
+ }
+ return false;
+}
+
+bool JSONBasicValue::AsInteger(int* output) const {
+ if (GetType() != kTypeInteger)
+ return false;
+ *output = integer_value_;
+ return true;
+}
+
+void JSONBasicValue::WriteJSON(StringBuilder* output) const {
+ DCHECK(GetType() == kTypeBoolean || GetType() == kTypeInteger ||
+ GetType() == kTypeDouble);
+ if (GetType() == kTypeBoolean) {
+ if (bool_value_)
+ output->Append(kJSONTrueString, 4);
+ else
+ output->Append(kJSONFalseString, 5);
+ } else if (GetType() == kTypeDouble) {
+ if (!std::isfinite(double_value_)) {
+ output->Append(kJSONNullString, 4);
+ return;
+ }
+ output->Append(Decimal::FromDouble(double_value_).ToString());
+ } else if (GetType() == kTypeInteger) {
+ output->Append(String::Number(integer_value_));
+ }
+}
+
+std::unique_ptr<JSONValue> JSONBasicValue::Clone() const {
+ switch (GetType()) {
+ case kTypeDouble:
+ return JSONBasicValue::Create(double_value_);
+ case kTypeInteger:
+ return JSONBasicValue::Create(integer_value_);
+ case kTypeBoolean:
+ return JSONBasicValue::Create(bool_value_);
+ default:
+ NOTREACHED();
+ }
+ return nullptr;
+}
+
+bool JSONString::AsString(String* output) const {
+ *output = string_value_;
+ return true;
+}
+
+void JSONString::WriteJSON(StringBuilder* output) const {
+ DCHECK(GetType() == kTypeString);
+ DoubleQuoteStringForJSON(string_value_, output);
+}
+
+std::unique_ptr<JSONValue> JSONString::Clone() const {
+ return JSONString::Create(string_value_);
+}
+
+JSONObject::~JSONObject() = default;
+
+void JSONObject::SetBoolean(const String& name, bool value) {
+ SetValue(name, JSONBasicValue::Create(value));
+}
+
+void JSONObject::SetInteger(const String& name, int value) {
+ SetValue(name, JSONBasicValue::Create(value));
+}
+
+void JSONObject::SetDouble(const String& name, double value) {
+ SetValue(name, JSONBasicValue::Create(value));
+}
+
+void JSONObject::SetString(const String& name, const String& value) {
+ SetValue(name, JSONString::Create(value));
+}
+
+void JSONObject::SetValue(const String& name,
+ std::unique_ptr<JSONValue> value) {
+ Set(name, value);
+}
+
+void JSONObject::SetObject(const String& name,
+ std::unique_ptr<JSONObject> value) {
+ Set(name, value);
+}
+
+void JSONObject::SetArray(const String& name,
+ std::unique_ptr<JSONArray> value) {
+ Set(name, value);
+}
+
+bool JSONObject::GetBoolean(const String& name, bool* output) const {
+ JSONValue* value = Get(name);
+ if (!value)
+ return false;
+ return value->AsBoolean(output);
+}
+
+bool JSONObject::GetInteger(const String& name, int* output) const {
+ JSONValue* value = Get(name);
+ if (!value)
+ return false;
+ return value->AsInteger(output);
+}
+
+bool JSONObject::GetDouble(const String& name, double* output) const {
+ JSONValue* value = Get(name);
+ if (!value)
+ return false;
+ return value->AsDouble(output);
+}
+
+bool JSONObject::GetString(const String& name, String* output) const {
+ JSONValue* value = Get(name);
+ if (!value)
+ return false;
+ return value->AsString(output);
+}
+
+JSONObject* JSONObject::GetJSONObject(const String& name) const {
+ return JSONObject::Cast(Get(name));
+}
+
+JSONArray* JSONObject::GetArray(const String& name) const {
+ return JSONArray::Cast(Get(name));
+}
+
+JSONValue* JSONObject::Get(const String& name) const {
+ Dictionary::const_iterator it = data_.find(name);
+ if (it == data_.end())
+ return nullptr;
+ return it->value.get();
+}
+
+JSONObject::Entry JSONObject::at(size_t index) const {
+ const String key = order_[index];
+ return std::make_pair(key, data_.find(key)->value.get());
+}
+
+bool JSONObject::BooleanProperty(const String& name, bool default_value) const {
+ bool result = default_value;
+ GetBoolean(name, &result);
+ return result;
+}
+
+int JSONObject::IntegerProperty(const String& name, int default_value) const {
+ int result = default_value;
+ GetInteger(name, &result);
+ return result;
+}
+
+double JSONObject::DoubleProperty(const String& name,
+ double default_value) const {
+ double result = default_value;
+ GetDouble(name, &result);
+ return result;
+}
+
+void JSONObject::Remove(const String& name) {
+ data_.erase(name);
+ for (size_t i = 0; i < order_.size(); ++i) {
+ if (order_[i] == name) {
+ order_.EraseAt(i);
+ break;
+ }
+ }
+}
+
+void JSONObject::WriteJSON(StringBuilder* output) const {
+ output->Append('{');
+ for (size_t i = 0; i < order_.size(); ++i) {
+ Dictionary::const_iterator it = data_.find(order_[i]);
+ CHECK(it != data_.end());
+ if (i)
+ output->Append(',');
+ DoubleQuoteStringForJSON(it->key, output);
+ output->Append(':');
+ it->value->WriteJSON(output);
+ }
+ output->Append('}');
+}
+
+void JSONObject::PrettyWriteJSONInternal(StringBuilder* output,
+ int depth) const {
+ output->Append("{\n");
+ for (size_t i = 0; i < order_.size(); ++i) {
+ Dictionary::const_iterator it = data_.find(order_[i]);
+ CHECK(it != data_.end());
+ if (i)
+ output->Append(",\n");
+ WriteIndent(depth + 1, output);
+ DoubleQuoteStringForJSON(it->key, output);
+ output->Append(": ");
+ it->value->PrettyWriteJSONInternal(output, depth + 1);
+ }
+ output->Append('\n');
+ WriteIndent(depth, output);
+ output->Append('}');
+}
+
+std::unique_ptr<JSONValue> JSONObject::Clone() const {
+ std::unique_ptr<JSONObject> result = JSONObject::Create();
+ for (size_t i = 0; i < order_.size(); ++i) {
+ String key = order_[i];
+ Dictionary::const_iterator value = data_.find(key);
+ DCHECK(value != data_.end() && value->value);
+ result->SetValue(key, value->value->Clone());
+ }
+ return std::move(result);
+}
+
+JSONObject::JSONObject() : JSONValue(kTypeObject), data_(), order_() {}
+
+JSONArray::~JSONArray() = default;
+
+void JSONArray::WriteJSON(StringBuilder* output) const {
+ output->Append('[');
+ bool first = true;
+ for (const std::unique_ptr<JSONValue>& value : data_) {
+ if (!first)
+ output->Append(',');
+ value->WriteJSON(output);
+ first = false;
+ }
+ output->Append(']');
+}
+
+void JSONArray::PrettyWriteJSONInternal(StringBuilder* output,
+ int depth) const {
+ output->Append('[');
+ bool first = true;
+ bool last_inserted_new_line = false;
+ for (const std::unique_ptr<JSONValue>& value : data_) {
+ bool insert_new_line = value->GetType() == JSONValue::kTypeObject ||
+ value->GetType() == JSONValue::kTypeArray ||
+ value->GetType() == JSONValue::kTypeString;
+ if (first) {
+ if (insert_new_line) {
+ output->Append('\n');
+ WriteIndent(depth + 1, output);
+ }
+ first = false;
+ } else {
+ output->Append(',');
+ if (last_inserted_new_line) {
+ output->Append('\n');
+ WriteIndent(depth + 1, output);
+ } else {
+ output->Append(' ');
+ }
+ }
+ value->PrettyWriteJSONInternal(output, depth + 1);
+ last_inserted_new_line = insert_new_line;
+ }
+ if (last_inserted_new_line) {
+ output->Append('\n');
+ WriteIndent(depth, output);
+ }
+ output->Append(']');
+}
+
+std::unique_ptr<JSONValue> JSONArray::Clone() const {
+ std::unique_ptr<JSONArray> result = JSONArray::Create();
+ for (const std::unique_ptr<JSONValue>& value : data_)
+ result->PushValue(value->Clone());
+ return std::move(result);
+}
+
+JSONArray::JSONArray() : JSONValue(kTypeArray) {}
+
+void JSONArray::PushBoolean(bool value) {
+ data_.push_back(JSONBasicValue::Create(value));
+}
+
+void JSONArray::PushInteger(int value) {
+ data_.push_back(JSONBasicValue::Create(value));
+}
+
+void JSONArray::PushDouble(double value) {
+ data_.push_back(JSONBasicValue::Create(value));
+}
+
+void JSONArray::PushString(const String& value) {
+ data_.push_back(JSONString::Create(value));
+}
+
+void JSONArray::PushValue(std::unique_ptr<JSONValue> value) {
+ DCHECK(value);
+ data_.push_back(std::move(value));
+}
+
+void JSONArray::PushObject(std::unique_ptr<JSONObject> value) {
+ DCHECK(value);
+ data_.push_back(std::move(value));
+}
+
+void JSONArray::PushArray(std::unique_ptr<JSONArray> value) {
+ DCHECK(value);
+ data_.push_back(std::move(value));
+}
+
+JSONValue* JSONArray::at(size_t index) const {
+ DCHECK_LT(index, data_.size());
+ return data_[index].get();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/json/json_values.h b/chromium/third_party/blink/renderer/platform/json/json_values.h
new file mode 100644
index 00000000000..ae4d6c06b0c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/json/json_values.h
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_JSON_JSON_VALUES_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_JSON_JSON_VALUES_H_
+
+#include <memory>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/type_traits.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class JSONValue;
+
+} // namespace blink
+
+namespace blink {
+
+class JSONArray;
+class JSONObject;
+
+class PLATFORM_EXPORT JSONValue {
+ USING_FAST_MALLOC(JSONValue);
+ WTF_MAKE_NONCOPYABLE(JSONValue);
+
+ public:
+ static const int kMaxDepth = 1000;
+
+ virtual ~JSONValue() = default;
+
+ static std::unique_ptr<JSONValue> Null() {
+ return base::WrapUnique(new JSONValue());
+ }
+
+ enum ValueType {
+ kTypeNull = 0,
+ kTypeBoolean,
+ kTypeInteger,
+ kTypeDouble,
+ kTypeString,
+ kTypeObject,
+ kTypeArray
+ };
+
+ ValueType GetType() const { return type_; }
+
+ bool IsNull() const { return type_ == kTypeNull; }
+
+ virtual bool AsBoolean(bool* output) const;
+ virtual bool AsDouble(double* output) const;
+ virtual bool AsInteger(int* output) const;
+ virtual bool AsString(String* output) const;
+
+ String ToJSONString() const;
+ String ToPrettyJSONString() const;
+ virtual void WriteJSON(StringBuilder* output) const;
+ virtual void PrettyWriteJSON(StringBuilder* output) const;
+ virtual std::unique_ptr<JSONValue> Clone() const;
+
+ static String QuoteString(const String&);
+
+ protected:
+ JSONValue() : type_(kTypeNull) {}
+ explicit JSONValue(ValueType type) : type_(type) {}
+ virtual void PrettyWriteJSONInternal(StringBuilder* output, int depth) const;
+
+ private:
+ friend class JSONObject;
+ friend class JSONArray;
+
+ ValueType type_;
+};
+
+class PLATFORM_EXPORT JSONBasicValue : public JSONValue {
+ public:
+ static std::unique_ptr<JSONBasicValue> Create(bool value) {
+ return base::WrapUnique(new JSONBasicValue(value));
+ }
+
+ static std::unique_ptr<JSONBasicValue> Create(int value) {
+ return base::WrapUnique(new JSONBasicValue(value));
+ }
+
+ static std::unique_ptr<JSONBasicValue> Create(double value) {
+ return base::WrapUnique(new JSONBasicValue(value));
+ }
+
+ bool AsBoolean(bool* output) const override;
+ bool AsDouble(double* output) const override;
+ bool AsInteger(int* output) const override;
+ void WriteJSON(StringBuilder* output) const override;
+ std::unique_ptr<JSONValue> Clone() const override;
+
+ private:
+ explicit JSONBasicValue(bool value)
+ : JSONValue(kTypeBoolean), bool_value_(value) {}
+ explicit JSONBasicValue(int value)
+ : JSONValue(kTypeInteger), integer_value_(value) {}
+ explicit JSONBasicValue(double value)
+ : JSONValue(kTypeDouble), double_value_(value) {}
+
+ union {
+ bool bool_value_;
+ double double_value_;
+ int integer_value_;
+ };
+};
+
+class PLATFORM_EXPORT JSONString : public JSONValue {
+ public:
+ static std::unique_ptr<JSONString> Create(const String& value) {
+ return base::WrapUnique(new JSONString(value));
+ }
+
+ static std::unique_ptr<JSONString> Create(const char* value) {
+ return base::WrapUnique(new JSONString(value));
+ }
+
+ bool AsString(String* output) const override;
+ void WriteJSON(StringBuilder* output) const override;
+ std::unique_ptr<JSONValue> Clone() const override;
+
+ private:
+ explicit JSONString(const String& value)
+ : JSONValue(kTypeString), string_value_(value) {}
+ explicit JSONString(const char* value)
+ : JSONValue(kTypeString), string_value_(value) {}
+
+ String string_value_;
+};
+
+class PLATFORM_EXPORT JSONObject : public JSONValue {
+ public:
+ using Entry = std::pair<String, JSONValue*>;
+ static std::unique_ptr<JSONObject> Create() {
+ return base::WrapUnique(new JSONObject());
+ }
+
+ static JSONObject* Cast(JSONValue* value) {
+ if (!value || value->GetType() != kTypeObject)
+ return nullptr;
+ return static_cast<JSONObject*>(value);
+ }
+
+ static const JSONObject* Cast(const JSONValue* value) {
+ if (!value || value->GetType() != kTypeObject)
+ return nullptr;
+ return static_cast<const JSONObject*>(value);
+ }
+
+ static std::unique_ptr<JSONObject> From(std::unique_ptr<JSONValue> value) {
+ auto maybe_object = base::WrapUnique(JSONObject::Cast(value.get()));
+ if (maybe_object)
+ value.release();
+ return maybe_object;
+ }
+
+ static void Cast(JSONObject*) = delete;
+ static void Cast(std::unique_ptr<JSONObject>) = delete;
+
+ void WriteJSON(StringBuilder* output) const override;
+ std::unique_ptr<JSONValue> Clone() const override;
+
+ size_t size() const { return data_.size(); }
+
+ void SetBoolean(const String& name, bool);
+ void SetInteger(const String& name, int);
+ void SetDouble(const String& name, double);
+ void SetString(const String& name, const String&);
+ void SetValue(const String& name, std::unique_ptr<JSONValue>);
+ void SetObject(const String& name, std::unique_ptr<JSONObject>);
+ void SetArray(const String& name, std::unique_ptr<JSONArray>);
+
+ bool GetBoolean(const String& name, bool* output) const;
+ bool GetInteger(const String& name, int* output) const;
+ bool GetDouble(const String& name, double* output) const;
+ bool GetString(const String& name, String* output) const;
+
+ // This function is not "GetObject" in order to avoid replacement by
+ // windows.h.
+ JSONObject* GetJSONObject(const String& name) const;
+ JSONArray* GetArray(const String& name) const;
+ JSONValue* Get(const String& name) const;
+ Entry at(size_t index) const;
+
+ bool BooleanProperty(const String& name, bool default_value) const;
+ int IntegerProperty(const String& name, int default_value) const;
+ double DoubleProperty(const String& name, double default_value) const;
+ void Remove(const String& name);
+
+ ~JSONObject() override;
+
+ protected:
+ void PrettyWriteJSONInternal(StringBuilder* output, int depth) const override;
+
+ private:
+ JSONObject();
+ template <typename T>
+ void Set(const String& key, std::unique_ptr<T>& value) {
+ DCHECK(value);
+ if (data_.Set(key, std::move(value)).is_new_entry)
+ order_.push_back(key);
+ }
+
+ using Dictionary = HashMap<String, std::unique_ptr<JSONValue>>;
+ Dictionary data_;
+ Vector<String> order_;
+};
+
+class PLATFORM_EXPORT JSONArray : public JSONValue {
+ public:
+ static std::unique_ptr<JSONArray> Create() {
+ return base::WrapUnique(new JSONArray());
+ }
+
+ static JSONArray* Cast(JSONValue* value) {
+ if (!value || value->GetType() != kTypeArray)
+ return nullptr;
+ return static_cast<JSONArray*>(value);
+ }
+
+ static std::unique_ptr<JSONArray> From(std::unique_ptr<JSONValue> value) {
+ auto maybe_array = base::WrapUnique(JSONArray::Cast(value.get()));
+ if (maybe_array)
+ value.release();
+ return maybe_array;
+ }
+
+ static void Cast(JSONArray*) = delete;
+ static void Cast(std::unique_ptr<JSONArray>) = delete;
+
+ ~JSONArray() override;
+
+ void WriteJSON(StringBuilder* output) const override;
+ std::unique_ptr<JSONValue> Clone() const override;
+
+ void PushBoolean(bool);
+ void PushInteger(int);
+ void PushDouble(double);
+ void PushString(const String&);
+ void PushValue(std::unique_ptr<JSONValue>);
+ void PushObject(std::unique_ptr<JSONObject>);
+ void PushArray(std::unique_ptr<JSONArray>);
+
+ JSONValue* at(size_t index) const;
+ size_t size() const { return data_.size(); }
+
+ protected:
+ void PrettyWriteJSONInternal(StringBuilder* output, int depth) const override;
+
+ private:
+ JSONArray();
+ Vector<std::unique_ptr<JSONValue>> data_;
+};
+
+extern const char* kJSONNullString;
+extern const char* kJSONTrueString;
+extern const char* kJSONFalseString;
+
+PLATFORM_EXPORT void EscapeStringForJSON(const String&, StringBuilder*);
+void DoubleQuoteStringForJSON(const String&, StringBuilder*);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_JSON_JSON_VALUES_H_
diff --git a/chromium/third_party/blink/renderer/platform/json/json_values_test.cc b/chromium/third_party/blink/renderer/platform/json/json_values_test.cc
new file mode 100644
index 00000000000..0e4dae2fc4e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/json/json_values_test.cc
@@ -0,0 +1,42 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/json/json_values.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+
+namespace blink {
+
+namespace {
+
+class JSONValueDeletionVerifier : public JSONValue {
+ public:
+ JSONValueDeletionVerifier(int& counter) : counter_(counter) {}
+
+ ~JSONValueDeletionVerifier() override { ++counter_; }
+
+ private:
+ int& counter_;
+};
+
+} // namespace
+
+TEST(JSONValuesTest, ArrayCastDoesNotLeak) {
+ int deletion_count = 0;
+ std::unique_ptr<JSONValueDeletionVerifier> not_an_array(
+ new JSONValueDeletionVerifier(deletion_count));
+ EXPECT_EQ(nullptr, JSONArray::From(std::move(not_an_array)));
+ EXPECT_EQ(1, deletion_count);
+}
+
+TEST(JSONValuesTest, ObjectCastDoesNotLeak) {
+ int deletion_count = 0;
+ std::unique_ptr<JSONValueDeletionVerifier> not_an_object(
+ new JSONValueDeletionVerifier(deletion_count));
+ EXPECT_EQ(nullptr, JSONArray::From(std::move(not_an_object)));
+ EXPECT_EQ(1, deletion_count);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/keyboard_codes.h b/chromium/third_party/blink/renderer/platform/keyboard_codes.h
new file mode 100644
index 00000000000..d1e198e106b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/keyboard_codes.h
@@ -0,0 +1,577 @@
+/*
+ * Copyright (c) 2008, 2009, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_KEYBOARD_CODES_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_KEYBOARD_CODES_H_
+
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+#include "third_party/blink/renderer/platform/windows_keyboard_codes.h"
+
+namespace blink {
+
+enum {
+ // VKEY_LBUTTON (01) Left mouse button
+ // VKEY_RBUTTON (02) Right mouse button
+ // VKEY_CANCEL (03) Control-break processing
+ // VKEY_MBUTTON (04) Middle mouse button (three-button mouse)
+ // VKEY_XBUTTON1 (05)
+ // VKEY_XBUTTON2 (06)
+
+ // VKEY_BACK (08) BACKSPACE key
+ VKEY_BACK = VK_BACK,
+
+ // VKEY_TAB (09) TAB key
+ VKEY_TAB = VK_TAB,
+
+ // VKEY_CLEAR (0C) CLEAR key
+ VKEY_CLEAR = VK_CLEAR,
+
+ // VKEY_RETURN (0D)
+ VKEY_RETURN = VK_RETURN,
+
+ // VKEY_SHIFT (10) SHIFT key
+ VKEY_SHIFT = VK_SHIFT,
+
+ // VKEY_CONTROL (11) CTRL key
+ VKEY_CONTROL = VK_CONTROL,
+
+ // VKEY_MENU (12) ALT key
+ VKEY_MENU = VK_MENU,
+
+ // VKEY_PAUSE (13) PAUSE key
+ VKEY_PAUSE = VK_PAUSE,
+
+ // VKEY_CAPITAL (14) CAPS LOCK key
+ VKEY_CAPITAL = VK_CAPITAL,
+
+ // VKEY_KANA (15) Input Method Editor (IME) Kana mode
+ VKEY_KANA = VK_KANA,
+
+ // VKEY_HANGUEL (15) IME Hanguel mode (maintained for compatibility,
+ // use VKEY_HANGUL)
+ // VKEY_HANGUL (15) IME Hangul mode
+ VKEY_HANGUL = VK_HANGUL,
+
+ // VKEY_JUNJA (17) IME Junja mode
+ VKEY_JUNJA = VK_JUNJA,
+
+ // VKEY_FINAL (18) IME final mode
+ VKEY_FINAL = VK_FINAL,
+
+ // VKEY_HANJA (19) IME Hanja mode
+ VKEY_HANJA = VK_HANJA,
+
+ // VKEY_KANJI (19) IME Kanji mode
+ VKEY_KANJI = VK_KANJI,
+
+ // VKEY_ESCAPE (1B) ESC key
+ VKEY_ESCAPE = VK_ESCAPE,
+
+ // VKEY_CONVERT (1C) IME convert
+ VKEY_CONVERT = VK_CONVERT,
+
+ // VKEY_NONCONVERT (1D) IME nonconvert
+ VKEY_NONCONVERT = VK_NONCONVERT,
+
+ // VKEY_ACCEPT (1E) IME accept
+ VKEY_ACCEPT = VK_ACCEPT,
+
+ // VKEY_MODECHANGE (1F) IME mode change request
+ VKEY_MODECHANGE = VK_MODECHANGE,
+
+ // VKEY_SPACE (20) SPACEBAR
+ VKEY_SPACE = VK_SPACE,
+
+ // VKEY_PRIOR (21) PAGE UP key
+ VKEY_PRIOR = VK_PRIOR,
+
+ // VKEY_NEXT (22) PAGE DOWN key
+ VKEY_NEXT = VK_NEXT,
+
+ // VKEY_END (23) END key
+ VKEY_END = VK_END,
+
+ // VKEY_HOME (24) HOME key
+ VKEY_HOME = VK_HOME,
+
+ // VKEY_LEFT (25) LEFT ARROW key
+ VKEY_LEFT = VK_LEFT,
+
+ // VKEY_UP (26) UP ARROW key
+ VKEY_UP = VK_UP,
+
+ // VKEY_RIGHT (27) RIGHT ARROW key
+ VKEY_RIGHT = VK_RIGHT,
+
+ // VKEY_DOWN (28) DOWN ARROW key
+ VKEY_DOWN = VK_DOWN,
+
+ // VKEY_SELECT (29) SELECT key
+ VKEY_SELECT = VK_SELECT,
+
+ // VKEY_PRINT (2A) PRINT key
+ VKEY_PRINT = VK_PRINT,
+
+ // VKEY_EXECUTE (2B) EXECUTE key
+ VKEY_EXECUTE = VK_EXECUTE,
+
+ // VKEY_SNAPSHOT (2C) PRINT SCREEN key
+ VKEY_SNAPSHOT = VK_SNAPSHOT,
+
+ // VKEY_INSERT (2D) INS key
+ VKEY_INSERT = VK_INSERT,
+
+ // VKEY_DELETE (2E) DEL key
+ VKEY_DELETE = VK_DELETE,
+
+ // VKEY_HELP (2F) HELP key
+ VKEY_HELP = VK_HELP,
+
+ // (30) 0 key
+ VKEY_0 = '0',
+
+ // (31) 1 key
+ VKEY_1 = '1',
+
+ // (32) 2 key
+ VKEY_2 = '2',
+
+ // (33) 3 key
+ VKEY_3 = '3',
+
+ // (34) 4 key
+ VKEY_4 = '4',
+
+ // (35) 5 key,
+
+ VKEY_5 = '5',
+
+ // (36) 6 key
+ VKEY_6 = '6',
+
+ // (37) 7 key
+ VKEY_7 = '7',
+
+ // (38) 8 key
+ VKEY_8 = '8',
+
+ // (39) 9 key
+ VKEY_9 = '9',
+
+ // (41) A key
+ VKEY_A = 'A',
+
+ // (42) B key
+ VKEY_B = 'B',
+
+ // (43) C key
+ VKEY_C = 'C',
+
+ // (44) D key
+ VKEY_D = 'D',
+
+ // (45) E key
+ VKEY_E = 'E',
+
+ // (46) F key
+ VKEY_F = 'F',
+
+ // (47) G key
+ VKEY_G = 'G',
+
+ // (48) H key
+ VKEY_H = 'H',
+
+ // (49) I key
+ VKEY_I = 'I',
+
+ // (4A) J key
+ VKEY_J = 'J',
+
+ // (4B) K key
+ VKEY_K = 'K',
+
+ // (4C) L key
+ VKEY_L = 'L',
+
+ // (4D) M key
+ VKEY_M = 'M',
+
+ // (4E) N key
+ VKEY_N = 'N',
+
+ // (4F) O key
+ VKEY_O = 'O',
+
+ // (50) P key
+ VKEY_P = 'P',
+
+ // (51) Q key
+ VKEY_Q = 'Q',
+
+ // (52) R key
+ VKEY_R = 'R',
+
+ // (53) S key
+ VKEY_S = 'S',
+
+ // (54) T key
+ VKEY_T = 'T',
+
+ // (55) U key
+ VKEY_U = 'U',
+
+ // (56) V key
+ VKEY_V = 'V',
+
+ // (57) W key
+ VKEY_W = 'W',
+
+ // (58) X key
+ VKEY_X = 'X',
+
+ // (59) Y key
+ VKEY_Y = 'Y',
+
+ // (5A) Z key
+ VKEY_Z = 'Z',
+
+ // VKEY_LWIN (5B) Left Windows key (Microsoft Natural keyboard)
+ VKEY_LWIN = VK_LWIN,
+
+ // VKEY_RWIN (5C) Right Windows key (Natural keyboard)
+ VKEY_RWIN = VK_RWIN,
+
+ // VKEY_APPS (5D) Applications key (Natural keyboard)
+ VKEY_APPS = VK_APPS,
+
+ // VKEY_SLEEP (5F) Computer Sleep key
+ VKEY_SLEEP = VK_SLEEP,
+
+ // VKEY_NUMPAD0 (60) Numeric keypad 0 key
+ VKEY_NUMPAD0 = VK_NUMPAD0,
+
+ // VKEY_NUMPAD1 (61) Numeric keypad 1 key
+ VKEY_NUMPAD1 = VK_NUMPAD1,
+
+ // VKEY_NUMPAD2 (62) Numeric keypad 2 key
+ VKEY_NUMPAD2 = VK_NUMPAD2,
+
+ // VKEY_NUMPAD3 (63) Numeric keypad 3 key
+ VKEY_NUMPAD3 = VK_NUMPAD3,
+
+ // VKEY_NUMPAD4 (64) Numeric keypad 4 key
+ VKEY_NUMPAD4 = VK_NUMPAD4,
+
+ // VKEY_NUMPAD5 (65) Numeric keypad 5 key
+ VKEY_NUMPAD5 = VK_NUMPAD5,
+
+ // VKEY_NUMPAD6 (66) Numeric keypad 6 key
+ VKEY_NUMPAD6 = VK_NUMPAD6,
+
+ // VKEY_NUMPAD7 (67) Numeric keypad 7 key
+ VKEY_NUMPAD7 = VK_NUMPAD7,
+
+ // VKEY_NUMPAD8 (68) Numeric keypad 8 key
+ VKEY_NUMPAD8 = VK_NUMPAD8,
+
+ // VKEY_NUMPAD9 (69) Numeric keypad 9 key
+ VKEY_NUMPAD9 = VK_NUMPAD9,
+
+ // VKEY_MULTIPLY (6A) Multiply key
+ VKEY_MULTIPLY = VK_MULTIPLY,
+
+ // VKEY_ADD (6B) Add key
+ VKEY_ADD = VK_ADD,
+
+ // VKEY_SEPARATOR (6C) Separator key
+ VKEY_SEPARATOR = VK_SEPARATOR,
+
+ // VKEY_SUBTRACT (6D) Subtract key
+ VKEY_SUBTRACT = VK_SUBTRACT,
+
+ // VKEY_DECIMAL (6E) Decimal key
+ VKEY_DECIMAL = VK_DECIMAL,
+
+ // VKEY_DIVIDE (6F) Divide key
+ VKEY_DIVIDE = VK_DIVIDE,
+
+ // VKEY_F1 (70) F1 key
+ VKEY_F1 = VK_F1,
+
+ // VKEY_F2 (71) F2 key
+ VKEY_F2 = VK_F2,
+
+ // VKEY_F3 (72) F3 key
+ VKEY_F3 = VK_F3,
+
+ // VKEY_F4 (73) F4 key
+ VKEY_F4 = VK_F4,
+
+ // VKEY_F5 (74) F5 key
+ VKEY_F5 = VK_F5,
+
+ // VKEY_F6 (75) F6 key
+ VKEY_F6 = VK_F6,
+
+ // VKEY_F7 (76) F7 key
+ VKEY_F7 = VK_F7,
+
+ // VKEY_F8 (77) F8 key
+ VKEY_F8 = VK_F8,
+
+ // VKEY_F9 (78) F9 key
+ VKEY_F9 = VK_F9,
+
+ // VKEY_F10 (79) F10 key
+ VKEY_F10 = VK_F10,
+
+ // VKEY_F11 (7A) F11 key
+ VKEY_F11 = VK_F11,
+
+ // VKEY_F12 (7B) F12 key
+ VKEY_F12 = VK_F12,
+
+ // VKEY_F13 (7C) F13 key
+ VKEY_F13 = VK_F13,
+
+ // VKEY_F14 (7D) F14 key
+ VKEY_F14 = VK_F14,
+
+ // VKEY_F15 (7E) F15 key
+ VKEY_F15 = VK_F15,
+
+ // VKEY_F16 (7F) F16 key
+ VKEY_F16 = VK_F16,
+
+ // VKEY_F17 (80H) F17 key
+ VKEY_F17 = VK_F17,
+
+ // VKEY_F18 (81H) F18 key
+ VKEY_F18 = VK_F18,
+
+ // VKEY_F19 (82H) F19 key
+ VKEY_F19 = VK_F19,
+
+ // VKEY_F20 (83H) F20 key
+ VKEY_F20 = VK_F20,
+
+ // VKEY_F21 (84H) F21 key
+ VKEY_F21 = VK_F21,
+
+ // VKEY_F22 (85H) F22 key
+ VKEY_F22 = VK_F22,
+
+ // VKEY_F23 (86H) F23 key
+ VKEY_F23 = VK_F23,
+
+ // VKEY_F24 (87H) F24 key
+ VKEY_F24 = VK_F24,
+
+ // VKEY_NUMLOCK (90) NUM LOCK key
+ VKEY_NUMLOCK = VK_NUMLOCK,
+
+ // VKEY_SCROLL (91) SCROLL LOCK key
+ VKEY_SCROLL = VK_SCROLL,
+
+ // VKEY_LSHIFT (A0) Left SHIFT key
+ VKEY_LSHIFT = VK_LSHIFT,
+
+ // VKEY_RSHIFT (A1) Right SHIFT key
+ VKEY_RSHIFT = VK_RSHIFT,
+
+ // VKEY_LCONTROL (A2) Left CONTROL key
+ VKEY_LCONTROL = VK_LCONTROL,
+
+ // VKEY_RCONTROL (A3) Right CONTROL key
+ VKEY_RCONTROL = VK_RCONTROL,
+
+ // VKEY_LMENU (A4) Left MENU key
+ VKEY_LMENU = VK_LMENU,
+
+ // VKEY_RMENU (A5) Right MENU key
+ VKEY_RMENU = VK_RMENU,
+
+ // VKEY_BROWSER_BACK (A6) Windows 2000/XP: Browser Back key
+ VKEY_BROWSER_BACK = VK_BROWSER_BACK,
+
+ // VKEY_BROWSER_FORWARD (A7) Windows 2000/XP: Browser Forward key
+ VKEY_BROWSER_FORWARD = VK_BROWSER_FORWARD,
+
+ // VKEY_BROWSER_REFRESH (A8) Windows 2000/XP: Browser Refresh key
+ VKEY_BROWSER_REFRESH = VK_BROWSER_REFRESH,
+
+ // VKEY_BROWSER_STOP (A9) Windows 2000/XP: Browser Stop key
+ VKEY_BROWSER_STOP = VK_BROWSER_STOP,
+
+ // VKEY_BROWSER_SEARCH (AA) Windows 2000/XP: Browser Search key
+ VKEY_BROWSER_SEARCH = VK_BROWSER_SEARCH,
+
+ // VKEY_BROWSER_FAVORITES (AB) Windows 2000/XP: Browser Favorites key
+ VKEY_BROWSER_FAVORITES = VK_BROWSER_FAVORITES,
+
+ // VKEY_BROWSER_HOME (AC) Windows 2000/XP: Browser Start and Home key
+ VKEY_BROWSER_HOME = VK_BROWSER_HOME,
+
+ // VKEY_VOLUME_MUTE (AD) Windows 2000/XP: Volume Mute key
+ VKEY_VOLUME_MUTE = VK_VOLUME_MUTE,
+
+ // VKEY_VOLUME_DOWN (AE) Windows 2000/XP: Volume Down key
+ VKEY_VOLUME_DOWN = VK_VOLUME_DOWN,
+
+ // VKEY_VOLUME_UP (AF) Windows 2000/XP: Volume Up key
+ VKEY_VOLUME_UP = VK_VOLUME_UP,
+
+ // VKEY_MEDIA_NEXT_TRACK (B0) Windows 2000/XP: Next Track key
+ VKEY_MEDIA_NEXT_TRACK = VK_MEDIA_NEXT_TRACK,
+
+ // VKEY_MEDIA_PREV_TRACK (B1) Windows 2000/XP: Previous Track key
+ VKEY_MEDIA_PREV_TRACK = VK_MEDIA_PREV_TRACK,
+
+ // VKEY_MEDIA_STOP (B2) Windows 2000/XP: Stop Media key
+ VKEY_MEDIA_STOP = VK_MEDIA_STOP,
+
+ // VKEY_MEDIA_PLAY_PAUSE (B3) Windows 2000/XP: Play/Pause Media key
+ VKEY_MEDIA_PLAY_PAUSE = VK_MEDIA_PLAY_PAUSE,
+
+ // VKEY_LAUNCH_MAIL (B4) Windows 2000/XP: Start Mail key
+ VKEY_MEDIA_LAUNCH_MAIL = 0xB4,
+
+ // VKEY_LAUNCH_MEDIA_SELECT (B5) Windows 2000/XP: Select Media key
+ VKEY_MEDIA_LAUNCH_MEDIA_SELECT = 0xB5,
+
+ // VKEY_LAUNCH_APP1 (B6) Windows 2000/XP: Start Application 1 key
+ VKEY_MEDIA_LAUNCH_APP1 = 0xB6,
+
+ // VKEY_LAUNCH_APP2 (B7) Windows 2000/XP: Start Application 2 key
+ VKEY_MEDIA_LAUNCH_APP2 = 0xB7,
+
+ // VKEY_OEM_1 (BA) Used for miscellaneous characters, it can vary by keyboard.
+ // Windows 2000/XP: For the US standard keyboard, the ',:' key
+ VKEY_OEM_1 = VK_OEM_1,
+
+ // VKEY_OEM_PLUS (BB) Windows 2000/XP: For any country/region, the '+' key
+ VKEY_OEM_PLUS = VK_OEM_PLUS,
+
+ // VKEY_OEM_COMMA (BC) Windows 2000/XP: For any country/region, the ',' key
+ VKEY_OEM_COMMA = VK_OEM_COMMA,
+
+ // VKEY_OEM_MINUS (BD) Windows 2000/XP: For any country/region, the '-' key
+ VKEY_OEM_MINUS = VK_OEM_MINUS,
+
+ // VKEY_OEM_PERIOD (BE) Windows 2000/XP: For any country/region, the '.' key
+ VKEY_OEM_PERIOD = VK_OEM_PERIOD,
+
+ // VKEY_OEM_2 (BF) Used for miscellaneous characters, it can vary by keyboard.
+ // Windows 2000/XP: For the US standard keyboard, the '/?' key
+ VKEY_OEM_2 = VK_OEM_2,
+
+ // VKEY_OEM_3 (C0) Used for miscellaneous characters, it can vary by keyboard.
+ // Windows 2000/XP: For the US standard keyboard, the '`~' key
+ VKEY_OEM_3 = VK_OEM_3,
+
+ // VKEY_OEM_4 (DB) Used for miscellaneous characters, it can vary by keyboard.
+ // Windows 2000/XP: For the US standard keyboard, the '[{' key
+ VKEY_OEM_4 = VK_OEM_4,
+
+ // VKEY_OEM_5 (DC) Used for miscellaneous characters, it can vary by keyboard.
+ // Windows 2000/XP: For the US standard keyboard, the '\|' key
+ VKEY_OEM_5 = VK_OEM_5,
+
+ // VKEY_OEM_6 (DD) Used for miscellaneous characters, it can vary by keyboard.
+ // Windows 2000/XP: For the US standard keyboard, the ']}' key
+ VKEY_OEM_6 = VK_OEM_6,
+
+ // VKEY_OEM_7 (DE) Used for miscellaneous characters, it can vary by keyboard.
+ // Windows 2000/XP: For the US standard keyboard, the
+ // 'single-quote/double-quote' key
+ VKEY_OEM_7 = VK_OEM_7,
+
+ // VKEY_OEM_8 (DF) Used for miscellaneous characters, it can vary by keyboard.
+ VKEY_OEM_8 = VK_OEM_8,
+
+ // VKEY_OEM_102 (E2) Windows 2000/XP: Either the angle bracket key or the
+ // backslash key on the RT 102-key keyboard
+ VKEY_OEM_102 = VK_OEM_102,
+
+ // VKEY_OEM_103 (E3) GTV KEYCODE_MEDIA_REWIND
+ VKEY_OEM_103 = 0xE3,
+
+ // VKEY_OEM_104 (E4) GTV KEYCODE_MEDIA_FAST_FORWARD
+ VKEY_OEM_104 = 0xE4,
+
+ // VKEY_PROCESSKEY (E5) Windows 95/98/Me, Windows NT 4.0, Windows 2000/XP: IME
+ // PROCESS key
+ VKEY_PROCESSKEY = VK_PROCESSKEY,
+
+ // VKEY_PACKET (E7) Windows 2000/XP: Used to pass Unicode characters as if
+ // they were keystrokes. The VKEY_PACKET key is the low word of a 32-bit
+ // Virtual Key value used for non-keyboard input methods. For more
+ // information, see Remark in KEYBDINPUT,SendInput, WM_KEYDOWN, and WM_KEYUP
+ VKEY_PACKET = VK_PACKET,
+
+ // VKEY_ATTN (F6) Attn key
+ VKEY_ATTN = VK_ATTN,
+
+ // VKEY_CRSEL (F7) CrSel key
+ VKEY_CRSEL = VK_CRSEL,
+
+ // VKEY_EXSEL (F8) ExSel key
+ VKEY_EXSEL = VK_EXSEL,
+
+ // VKEY_EREOF (F9) Erase EOF key
+ VKEY_EREOF = VK_EREOF,
+
+ // VKEY_PLAY (FA) Play key
+ VKEY_PLAY = VK_PLAY,
+
+ // VKEY_ZOOM (FB) Zoom key
+ VKEY_ZOOM = VK_ZOOM,
+
+ // VKEY_NONAME (FC) Reserved for future use
+ VKEY_NONAME = VK_NONAME,
+
+ // VKEY_PA1 (FD) PA1 key
+ VKEY_PA1 = VK_PA1,
+
+ // VKEY_OEM_CLEAR (FE) Clear key
+ VKEY_OEM_CLEAR = VK_OEM_CLEAR,
+
+ VKEY_UNKNOWN = 0
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/kill_ring.h b/chromium/third_party/blink/renderer/platform/kill_ring.h
new file mode 100644
index 00000000000..d675e9cfdee
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/kill_ring.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2010 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_KILL_RING_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_KILL_RING_H_
+
+#include "base/macros.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT KillRing {
+ USING_FAST_MALLOC(KillRing);
+
+ public:
+ KillRing() = default;
+ void Append(const String&);
+ void Prepend(const String&);
+ String Yank();
+ void StartNewSequence();
+ void SetToYankedState();
+
+ DISALLOW_COPY_AND_ASSIGN(KillRing);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_KILL_RING_H_
diff --git a/chromium/third_party/blink/renderer/platform/kill_ring_none.cc b/chromium/third_party/blink/renderer/platform/kill_ring_none.cc
new file mode 100644
index 00000000000..94bcf1e3d42
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/kill_ring_none.cc
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2010 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 "third_party/blink/renderer/platform/kill_ring.h"
+
+namespace blink {
+
+void KillRing::Append(const String&) {}
+
+void KillRing::Prepend(const String&) {}
+
+String KillRing::Yank() {
+ return String();
+}
+
+void KillRing::StartNewSequence() {}
+
+void KillRing::SetToYankedState() {}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/language.cc b/chromium/third_party/blink/renderer/platform/language.cc
new file mode 100644
index 00000000000..fec31ed01b2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/language.cc
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2010, 2013 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/language.h"
+
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/text/platform_locale.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+namespace {
+
+static String CanonicalizeLanguageIdentifier(const String& language_code) {
+ String copied_code = language_code;
+ // Platform::defaultLocale() might provide a language code with '_'.
+ copied_code.Replace('_', '-');
+ return copied_code;
+}
+
+// Main thread static AtomicString. This can be safely shared across threads.
+const AtomicString* g_platform_language = nullptr;
+
+const AtomicString& PlatformLanguage() {
+ DCHECK(g_platform_language->Impl()->IsStatic())
+ << "global language string is used from multiple threads, and must be "
+ "static";
+ return *g_platform_language;
+}
+
+Vector<AtomicString>& PreferredLanguagesOverride() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadSpecific<Vector<AtomicString>>,
+ thread_specific_languages, ());
+ return *thread_specific_languages;
+}
+
+} // namespace
+
+void InitializePlatformLanguage() {
+ DCHECK(IsMainThread());
+ DEFINE_STATIC_LOCAL(
+ // We add the platform language as a static string for two reasons:
+ // 1. it can be shared across threads.
+ // 2. since this is done very early on, we don't want to accidentally
+ // collide with a hard coded static string (like "fr" on SVG).
+ const AtomicString, platform_language, (([]() {
+ String canonicalized = CanonicalizeLanguageIdentifier(
+ Platform::Current()->DefaultLocale());
+ if (!canonicalized.IsEmpty()) {
+ StringImpl* impl = StringImpl::CreateStatic(
+ reinterpret_cast<const char*>(canonicalized.Characters8()),
+ canonicalized.length(),
+ StringHasher::ComputeHashAndMaskTop8Bits(
+ canonicalized.Characters8(), canonicalized.length()));
+
+ return AtomicString(impl);
+ }
+ return AtomicString();
+ })()));
+
+ g_platform_language = &platform_language;
+}
+
+void OverrideUserPreferredLanguagesForTesting(
+ const Vector<AtomicString>& override) {
+ Vector<AtomicString>& canonicalized = PreferredLanguagesOverride();
+ canonicalized.resize(0);
+ canonicalized.ReserveCapacity(override.size());
+ for (const auto& lang : override)
+ canonicalized.push_back(CanonicalizeLanguageIdentifier(lang));
+ Locale::ResetDefaultLocale();
+}
+
+AtomicString DefaultLanguage() {
+ Vector<AtomicString>& override = PreferredLanguagesOverride();
+ if (!override.IsEmpty())
+ return override[0];
+ return PlatformLanguage();
+}
+
+Vector<AtomicString> UserPreferredLanguages() {
+ Vector<AtomicString>& override = PreferredLanguagesOverride();
+ if (!override.IsEmpty())
+ return override;
+
+ Vector<AtomicString> languages;
+ languages.ReserveInitialCapacity(1);
+ languages.push_back(PlatformLanguage());
+ return languages;
+}
+
+size_t IndexOfBestMatchingLanguageInList(
+ const AtomicString& language,
+ const Vector<AtomicString>& language_list) {
+ AtomicString language_without_locale_match;
+ AtomicString language_match_but_not_locale;
+ size_t language_without_locale_match_index = 0;
+ size_t language_match_but_not_locale_match_index = 0;
+ bool can_match_language_only =
+ (language.length() == 2 ||
+ (language.length() >= 3 && language[2] == '-'));
+
+ for (size_t i = 0; i < language_list.size(); ++i) {
+ String canonicalized_language_from_list =
+ CanonicalizeLanguageIdentifier(language_list[i]);
+
+ if (language == canonicalized_language_from_list)
+ return i;
+
+ if (can_match_language_only &&
+ canonicalized_language_from_list.length() >= 2) {
+ if (language[0] == canonicalized_language_from_list[0] &&
+ language[1] == canonicalized_language_from_list[1]) {
+ if (!language_without_locale_match.length() &&
+ canonicalized_language_from_list.length() == 2) {
+ language_without_locale_match = language_list[i];
+ language_without_locale_match_index = i;
+ }
+ if (!language_match_but_not_locale.length() &&
+ canonicalized_language_from_list.length() >= 3) {
+ language_match_but_not_locale = language_list[i];
+ language_match_but_not_locale_match_index = i;
+ }
+ }
+ }
+ }
+
+ // If we have both a language-only match and a languge-but-not-locale match,
+ // return the languge-only match as is considered a "better" match. For
+ // example, if the list provided has both "en-GB" and "en" and the user
+ // prefers "en-US" we will return "en".
+ if (language_without_locale_match.length())
+ return language_without_locale_match_index;
+
+ if (language_match_but_not_locale.length())
+ return language_match_but_not_locale_match_index;
+
+ return language_list.size();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/language.h b/chromium/third_party/blink/renderer/platform/language.h
new file mode 100644
index 00000000000..9419642a974
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/language.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2003, 2006, 2010, 2013 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_LANGUAGE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LANGUAGE_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+// Returns a BCP-47 language tag such as "en-US". This is the UI locale of the
+// browser application.
+PLATFORM_EXPORT AtomicString DefaultLanguage();
+// Returns a list of BCP-47 language tags. This never returns multiple values
+// in production. This is not a value of Accept-Languages header. See
+// ChromeClient::acceptLanguages.
+PLATFORM_EXPORT Vector<AtomicString> UserPreferredLanguages();
+PLATFORM_EXPORT void OverrideUserPreferredLanguagesForTesting(
+ const Vector<AtomicString>&);
+PLATFORM_EXPORT size_t
+IndexOfBestMatchingLanguageInList(const AtomicString& language,
+ const Vector<AtomicString>& language_list);
+PLATFORM_EXPORT void InitializePlatformLanguage();
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/layout_locale.cc b/chromium/third_party/blink/renderer/platform/layout_locale.cc
new file mode 100644
index 00000000000..7fb1f9cfc10
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/layout_locale.cc
@@ -0,0 +1,226 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/layout_locale.h"
+
+#include "third_party/blink/renderer/platform/fonts/accept_languages_resolver.h"
+#include "third_party/blink/renderer/platform/fonts/font_global_context.h"
+#include "third_party/blink/renderer/platform/language.h"
+#include "third_party/blink/renderer/platform/text/icu_error.h"
+#include "third_party/blink/renderer/platform/text/locale_to_script_mapping.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+
+#include <hb.h>
+#include <unicode/locid.h>
+
+namespace blink {
+
+static hb_language_t ToHarfbuzLanguage(const AtomicString& locale) {
+ CString locale_as_latin1 = locale.Latin1();
+ return hb_language_from_string(locale_as_latin1.data(),
+ locale_as_latin1.length());
+}
+
+// SkFontMgr requires script-based locale names, like "zh-Hant" and "zh-Hans",
+// instead of "zh-CN" and "zh-TW".
+static const char* ToSkFontMgrLocale(UScriptCode script) {
+ switch (script) {
+ case USCRIPT_KATAKANA_OR_HIRAGANA:
+ return "ja-JP";
+ case USCRIPT_HANGUL:
+ return "ko-KR";
+ case USCRIPT_SIMPLIFIED_HAN:
+ return "zh-Hans";
+ case USCRIPT_TRADITIONAL_HAN:
+ return "zh-Hant";
+ default:
+ return nullptr;
+ }
+}
+
+const char* LayoutLocale::LocaleForSkFontMgr() const {
+ if (string_for_sk_font_mgr_.IsNull()) {
+ string_for_sk_font_mgr_ = ToSkFontMgrLocale(script_);
+ if (string_for_sk_font_mgr_.IsNull())
+ string_for_sk_font_mgr_ = string_.Ascii();
+ }
+ return string_for_sk_font_mgr_.data();
+}
+
+void LayoutLocale::ComputeScriptForHan() const {
+ if (IsUnambiguousHanScript(script_)) {
+ script_for_han_ = script_;
+ has_script_for_han_ = true;
+ return;
+ }
+
+ script_for_han_ = ScriptCodeForHanFromSubtags(string_);
+ if (script_for_han_ == USCRIPT_COMMON)
+ script_for_han_ = USCRIPT_SIMPLIFIED_HAN;
+ else
+ has_script_for_han_ = true;
+ DCHECK(IsUnambiguousHanScript(script_for_han_));
+}
+
+UScriptCode LayoutLocale::GetScriptForHan() const {
+ if (script_for_han_ == USCRIPT_COMMON)
+ ComputeScriptForHan();
+ return script_for_han_;
+}
+
+bool LayoutLocale::HasScriptForHan() const {
+ if (script_for_han_ == USCRIPT_COMMON)
+ ComputeScriptForHan();
+ return has_script_for_han_;
+}
+
+const LayoutLocale* LayoutLocale::LocaleForHan(
+ const LayoutLocale* content_locale) {
+ if (content_locale && content_locale->HasScriptForHan())
+ return content_locale;
+
+ if (FontGlobalContext::HasDefaultLocaleForHan())
+ return FontGlobalContext::GetDefaultLocaleForHan();
+
+ const LayoutLocale* default_for_han;
+ if (const LayoutLocale* locale = AcceptLanguagesResolver::LocaleForHan())
+ default_for_han = locale;
+ else if (GetDefault().HasScriptForHan())
+ default_for_han = &GetDefault();
+ else if (GetSystem().HasScriptForHan())
+ default_for_han = &GetSystem();
+ else
+ default_for_han = nullptr;
+ FontGlobalContext::SetDefaultLocaleForHan(default_for_han);
+ return default_for_han;
+}
+
+const char* LayoutLocale::LocaleForHanForSkFontMgr() const {
+ const char* locale = ToSkFontMgrLocale(GetScriptForHan());
+ DCHECK(locale);
+ return locale;
+}
+
+LayoutLocale::LayoutLocale(const AtomicString& locale)
+ : string_(locale),
+ harfbuzz_language_(ToHarfbuzLanguage(locale)),
+ script_(LocaleToScriptCodeForFontSelection(locale)),
+ script_for_han_(USCRIPT_COMMON),
+ has_script_for_han_(false),
+ hyphenation_computed_(false) {}
+
+const LayoutLocale* LayoutLocale::Get(const AtomicString& locale) {
+ if (locale.IsNull())
+ return nullptr;
+
+ auto result = FontGlobalContext::GetLayoutLocaleMap().insert(locale, nullptr);
+ if (result.is_new_entry)
+ result.stored_value->value = base::AdoptRef(new LayoutLocale(locale));
+ return result.stored_value->value.get();
+}
+
+const LayoutLocale& LayoutLocale::GetDefault() {
+ if (const LayoutLocale* locale = FontGlobalContext::GetDefaultLayoutLocale())
+ return *locale;
+
+ AtomicString language = DefaultLanguage();
+ const LayoutLocale* locale =
+ LayoutLocale::Get(!language.IsEmpty() ? language : "en");
+ FontGlobalContext::SetDefaultLayoutLocale(locale);
+ return *locale;
+}
+
+const LayoutLocale& LayoutLocale::GetSystem() {
+ if (const LayoutLocale* locale = FontGlobalContext::GetSystemLayoutLocale())
+ return *locale;
+
+ // Platforms such as Windows can give more information than the default
+ // locale, such as "en-JP" for English speakers in Japan.
+ String name = icu::Locale::getDefault().getName();
+ const LayoutLocale* locale =
+ LayoutLocale::Get(AtomicString(name.Replace('_', '-')));
+ FontGlobalContext::SetSystemLayoutLocale(locale);
+ return *locale;
+}
+
+scoped_refptr<LayoutLocale> LayoutLocale::CreateForTesting(
+ const AtomicString& locale) {
+ return base::AdoptRef(new LayoutLocale(locale));
+}
+
+Hyphenation* LayoutLocale::GetHyphenation() const {
+ if (hyphenation_computed_)
+ return hyphenation_.get();
+
+ hyphenation_computed_ = true;
+ hyphenation_ = Hyphenation::PlatformGetHyphenation(LocaleString());
+ return hyphenation_.get();
+}
+
+void LayoutLocale::SetHyphenationForTesting(
+ const AtomicString& locale_string,
+ scoped_refptr<Hyphenation> hyphenation) {
+ const LayoutLocale& locale = ValueOrDefault(Get(locale_string));
+ locale.hyphenation_computed_ = true;
+ locale.hyphenation_ = std::move(hyphenation);
+}
+
+AtomicString LayoutLocale::LocaleWithBreakKeyword(
+ LineBreakIteratorMode mode) const {
+ if (string_.IsEmpty())
+ return string_;
+
+ // uloc_setKeywordValue_58 has a problem to handle "@" in the original
+ // string. crbug.com/697859
+ if (string_.Contains('@'))
+ return string_;
+
+ CString utf8_locale = string_.Utf8();
+ Vector<char> buffer(utf8_locale.length() + 11, 0);
+ memcpy(buffer.data(), utf8_locale.data(), utf8_locale.length());
+
+ const char* keyword_value = nullptr;
+ switch (mode) {
+ default:
+ NOTREACHED();
+ FALLTHROUGH;
+ case LineBreakIteratorMode::kDefault:
+ // nullptr will cause any existing values to be removed.
+ break;
+ case LineBreakIteratorMode::kNormal:
+ keyword_value = "normal";
+ break;
+ case LineBreakIteratorMode::kStrict:
+ keyword_value = "strict";
+ break;
+ case LineBreakIteratorMode::kLoose:
+ keyword_value = "loose";
+ break;
+ }
+
+ ICUError status;
+ int32_t length_needed = uloc_setKeywordValue(
+ "lb", keyword_value, buffer.data(), buffer.size(), &status);
+ if (U_SUCCESS(status))
+ return AtomicString::FromUTF8(buffer.data(), length_needed);
+
+ if (status == U_BUFFER_OVERFLOW_ERROR) {
+ buffer.Grow(length_needed + 1);
+ memset(buffer.data() + utf8_locale.length(), 0,
+ buffer.size() - utf8_locale.length());
+ status = U_ZERO_ERROR;
+ int32_t length_needed2 = uloc_setKeywordValue(
+ "lb", keyword_value, buffer.data(), buffer.size(), &status);
+ DCHECK_EQ(length_needed, length_needed2);
+ if (U_SUCCESS(status) && length_needed == length_needed2)
+ return AtomicString::FromUTF8(buffer.data(), length_needed);
+ }
+
+ NOTREACHED();
+ return string_;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/layout_locale.h b/chromium/third_party/blink/renderer/platform/layout_locale.h
new file mode 100644
index 00000000000..ffbcea9d9ba
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/layout_locale.h
@@ -0,0 +1,88 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LAYOUT_LOCALE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LAYOUT_LOCALE_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/text/hyphenation.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+
+#include <unicode/uscript.h>
+
+struct hb_language_impl_t;
+
+namespace blink {
+
+class Hyphenation;
+
+enum class LineBreakIteratorMode { kDefault, kNormal, kStrict, kLoose };
+
+class PLATFORM_EXPORT LayoutLocale : public RefCounted<LayoutLocale> {
+ public:
+ static const LayoutLocale* Get(const AtomicString& locale);
+ static const LayoutLocale& GetDefault();
+ static const LayoutLocale& GetSystem();
+ static const LayoutLocale& ValueOrDefault(const LayoutLocale* locale) {
+ return locale ? *locale : GetDefault();
+ }
+
+ bool operator==(const LayoutLocale& other) const {
+ return string_ == other.string_;
+ }
+ bool operator!=(const LayoutLocale& other) const {
+ return string_ != other.string_;
+ }
+
+ const AtomicString& LocaleString() const { return string_; }
+ static const AtomicString& LocaleString(const LayoutLocale* locale) {
+ return locale ? locale->string_ : g_null_atom;
+ }
+ operator const AtomicString&() const { return string_; }
+ CString Ascii() const { return string_.Ascii(); }
+
+ const hb_language_impl_t* HarfbuzzLanguage() const {
+ return harfbuzz_language_;
+ }
+ const char* LocaleForSkFontMgr() const;
+ UScriptCode GetScript() const { return script_; }
+
+ // Disambiguation of the Unified Han Ideographs.
+ UScriptCode GetScriptForHan() const;
+ bool HasScriptForHan() const;
+ static const LayoutLocale* LocaleForHan(const LayoutLocale*);
+ const char* LocaleForHanForSkFontMgr() const;
+
+ Hyphenation* GetHyphenation() const;
+
+ AtomicString LocaleWithBreakKeyword(LineBreakIteratorMode) const;
+
+ static scoped_refptr<LayoutLocale> CreateForTesting(const AtomicString&);
+ static void SetHyphenationForTesting(const AtomicString&,
+ scoped_refptr<Hyphenation>);
+
+ private:
+ explicit LayoutLocale(const AtomicString&);
+
+ void ComputeScriptForHan() const;
+
+ AtomicString string_;
+ mutable CString string_for_sk_font_mgr_;
+ mutable scoped_refptr<Hyphenation> hyphenation_;
+
+ // hb_language_t is defined in hb.h, which not all files can include.
+ const hb_language_impl_t* harfbuzz_language_;
+
+ UScriptCode script_;
+ mutable UScriptCode script_for_han_;
+
+ mutable unsigned has_script_for_han_ : 1;
+ mutable unsigned hyphenation_computed_ : 1;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LAYOUT_LOCALE_H_
diff --git a/chromium/third_party/blink/renderer/platform/layout_locale_test.cc b/chromium/third_party/blink/renderer/platform/layout_locale_test.cc
new file mode 100644
index 00000000000..ea37d5539aa
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/layout_locale_test.cc
@@ -0,0 +1,155 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/layout_locale.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/fonts/font_global_context.h"
+
+namespace blink {
+
+TEST(LayoutLocaleTest, Get) {
+ FontGlobalContext::ClearForTesting();
+
+ EXPECT_EQ(nullptr, LayoutLocale::Get(g_null_atom));
+
+ EXPECT_EQ(g_empty_atom, LayoutLocale::Get(g_empty_atom)->LocaleString());
+
+ EXPECT_STRCASEEQ("en-us",
+ LayoutLocale::Get("en-us")->LocaleString().Ascii().data());
+ EXPECT_STRCASEEQ("ja-jp",
+ LayoutLocale::Get("ja-jp")->LocaleString().Ascii().data());
+
+ FontGlobalContext::ClearForTesting();
+}
+
+TEST(LayoutLocaleTest, GetCaseInsensitive) {
+ const LayoutLocale* en_us = LayoutLocale::Get("en-us");
+ EXPECT_EQ(en_us, LayoutLocale::Get("en-US"));
+}
+
+TEST(LayoutLocaleTest, ScriptTest) {
+ // Test combinations of BCP 47 locales.
+ // https://tools.ietf.org/html/bcp47
+ struct {
+ const char* locale;
+ UScriptCode script;
+ bool has_script_for_han;
+ UScriptCode script_for_han;
+ } tests[] = {
+ {"en-US", USCRIPT_LATIN},
+
+ // Common lang-script.
+ {"en-Latn", USCRIPT_LATIN},
+ {"ar-Arab", USCRIPT_ARABIC},
+
+ // Common lang-region in East Asia.
+ {"ja-JP", USCRIPT_KATAKANA_OR_HIRAGANA, true},
+ {"ko-KR", USCRIPT_HANGUL, true},
+ {"zh", USCRIPT_SIMPLIFIED_HAN, true},
+ {"zh-CN", USCRIPT_SIMPLIFIED_HAN, true},
+ {"zh-HK", USCRIPT_TRADITIONAL_HAN, true},
+ {"zh-MO", USCRIPT_TRADITIONAL_HAN, true},
+ {"zh-SG", USCRIPT_SIMPLIFIED_HAN, true},
+ {"zh-TW", USCRIPT_TRADITIONAL_HAN, true},
+
+ // Encompassed languages within the Chinese macrolanguage.
+ // Both "lang" and "lang-extlang" should work.
+ {"nan", USCRIPT_TRADITIONAL_HAN, true},
+ {"wuu", USCRIPT_SIMPLIFIED_HAN, true},
+ {"yue", USCRIPT_TRADITIONAL_HAN, true},
+ {"zh-nan", USCRIPT_TRADITIONAL_HAN, true},
+ {"zh-wuu", USCRIPT_SIMPLIFIED_HAN, true},
+ {"zh-yue", USCRIPT_TRADITIONAL_HAN, true},
+
+ // Script has priority over other subtags.
+ {"zh-Hant", USCRIPT_TRADITIONAL_HAN, true},
+ {"en-Hans", USCRIPT_SIMPLIFIED_HAN, true},
+ {"en-Hant", USCRIPT_TRADITIONAL_HAN, true},
+ {"en-Hans-TW", USCRIPT_SIMPLIFIED_HAN, true},
+ {"en-Hant-CN", USCRIPT_TRADITIONAL_HAN, true},
+ {"wuu-Hant", USCRIPT_TRADITIONAL_HAN, true},
+ {"yue-Hans", USCRIPT_SIMPLIFIED_HAN, true},
+ {"zh-wuu-Hant", USCRIPT_TRADITIONAL_HAN, true},
+ {"zh-yue-Hans", USCRIPT_SIMPLIFIED_HAN, true},
+
+ // Lang has priority over region.
+ // icu::Locale::getDefault() returns other combinations if, for instnace,
+ // English Windows with the display language set to Japanese.
+ {"ja", USCRIPT_KATAKANA_OR_HIRAGANA, true},
+ {"ja-US", USCRIPT_KATAKANA_OR_HIRAGANA, true},
+ {"ko", USCRIPT_HANGUL, true},
+ {"ko-US", USCRIPT_HANGUL, true},
+ {"wuu-TW", USCRIPT_SIMPLIFIED_HAN, true},
+ {"yue-CN", USCRIPT_TRADITIONAL_HAN, true},
+ {"zh-wuu-TW", USCRIPT_SIMPLIFIED_HAN, true},
+ {"zh-yue-CN", USCRIPT_TRADITIONAL_HAN, true},
+
+ // Region should not affect script, but it can influence scriptForHan.
+ {"en-CN", USCRIPT_LATIN, false},
+ {"en-HK", USCRIPT_LATIN, true, USCRIPT_TRADITIONAL_HAN},
+ {"en-MO", USCRIPT_LATIN, true, USCRIPT_TRADITIONAL_HAN},
+ {"en-SG", USCRIPT_LATIN, false},
+ {"en-TW", USCRIPT_LATIN, true, USCRIPT_TRADITIONAL_HAN},
+ {"en-JP", USCRIPT_LATIN, true, USCRIPT_KATAKANA_OR_HIRAGANA},
+ {"en-KR", USCRIPT_LATIN, true, USCRIPT_HANGUL},
+
+ // Multiple regions are invalid, but it can still give hints for the font
+ // selection.
+ {"en-US-JP", USCRIPT_LATIN, true, USCRIPT_KATAKANA_OR_HIRAGANA},
+ };
+
+ for (const auto& test : tests) {
+ scoped_refptr<LayoutLocale> locale =
+ LayoutLocale::CreateForTesting(test.locale);
+ EXPECT_EQ(test.script, locale->GetScript()) << test.locale;
+ EXPECT_EQ(test.has_script_for_han, locale->HasScriptForHan())
+ << test.locale;
+ if (!test.has_script_for_han) {
+ EXPECT_EQ(USCRIPT_SIMPLIFIED_HAN, locale->GetScriptForHan())
+ << test.locale;
+ } else if (test.script_for_han) {
+ EXPECT_EQ(test.script_for_han, locale->GetScriptForHan()) << test.locale;
+ } else {
+ EXPECT_EQ(test.script, locale->GetScriptForHan()) << test.locale;
+ }
+ }
+}
+
+TEST(LayoutLocaleTest, BreakKeyword) {
+ struct {
+ const char* expected;
+ const char* locale;
+ LineBreakIteratorMode mode;
+ } tests[] = {
+ {nullptr, nullptr, LineBreakIteratorMode::kDefault},
+ {"", "", LineBreakIteratorMode::kDefault},
+ {nullptr, nullptr, LineBreakIteratorMode::kStrict},
+ {"", "", LineBreakIteratorMode::kStrict},
+ {"ja", "ja", LineBreakIteratorMode::kDefault},
+ {"ja@lb=normal", "ja", LineBreakIteratorMode::kNormal},
+ {"ja@lb=strict", "ja", LineBreakIteratorMode::kStrict},
+ {"ja@lb=loose", "ja", LineBreakIteratorMode::kLoose},
+ };
+ for (const auto& test : tests) {
+ scoped_refptr<LayoutLocale> locale =
+ LayoutLocale::CreateForTesting(test.locale);
+ EXPECT_EQ(test.expected, locale->LocaleWithBreakKeyword(test.mode))
+ << String::Format("'%s' with line-break %d should be '%s'", test.locale,
+ static_cast<int>(test.mode), test.expected);
+ }
+}
+
+TEST(LayoutLocaleTest, ExistingKeywordName) {
+ const char* tests[] = {
+ "en@x=", "en@lb=xyz", "en@ =",
+ };
+ for (const auto& test : tests) {
+ scoped_refptr<LayoutLocale> locale = LayoutLocale::CreateForTesting(test);
+ EXPECT_EQ(test,
+ locale->LocaleWithBreakKeyword(LineBreakIteratorMode::kNormal));
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/layout_test_support.cc b/chromium/third_party/blink/renderer/platform/layout_test_support.cc
new file mode 100644
index 00000000000..a5c36884d92
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/layout_test_support.cc
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2012 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 "third_party/blink/renderer/platform/layout_test_support.h"
+
+#include "third_party/blink/public/web/blink.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+namespace blink {
+
+// Wrapper functions defined in WebKit.h
+void SetLayoutTestMode(bool value) {
+ LayoutTestSupport::SetIsRunningLayoutTest(value);
+}
+
+bool LayoutTestMode() {
+ return LayoutTestSupport::IsRunningLayoutTest();
+}
+
+void SetFontAntialiasingEnabledForTest(bool value) {
+ LayoutTestSupport::SetFontAntialiasingEnabledForTest(value);
+}
+
+bool FontAntialiasingEnabledForTest() {
+ return LayoutTestSupport::IsFontAntialiasingEnabledForTest();
+}
+
+static bool g_is_running_layout_test = false;
+static bool g_is_mock_theme_enabled = false;
+static bool g_is_font_antialiasing_enabled = false;
+static bool g_is_subpixel_positioning_allowed = true;
+
+bool LayoutTestSupport::IsRunningLayoutTest() {
+ return g_is_running_layout_test;
+}
+
+void LayoutTestSupport::SetIsRunningLayoutTest(bool value) {
+ g_is_running_layout_test = value;
+}
+
+bool LayoutTestSupport::IsMockThemeEnabledForTest() {
+ return g_is_mock_theme_enabled;
+}
+
+void LayoutTestSupport::SetMockThemeEnabledForTest(bool value) {
+ DCHECK(g_is_running_layout_test);
+ g_is_mock_theme_enabled = value;
+}
+
+bool LayoutTestSupport::IsFontAntialiasingEnabledForTest() {
+ return g_is_font_antialiasing_enabled;
+}
+
+void LayoutTestSupport::SetFontAntialiasingEnabledForTest(bool value) {
+ g_is_font_antialiasing_enabled = value;
+}
+
+bool LayoutTestSupport::IsTextSubpixelPositioningAllowedForTest() {
+ return g_is_subpixel_positioning_allowed;
+}
+
+void LayoutTestSupport::SetTextSubpixelPositioningAllowedForTest(bool value) {
+ g_is_subpixel_positioning_allowed = value;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/layout_test_support.h b/chromium/third_party/blink/renderer/platform/layout_test_support.h
new file mode 100644
index 00000000000..161819fd3a3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/layout_test_support.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2012 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_LAYOUT_TEST_SUPPORT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LAYOUT_TEST_SUPPORT_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+class LayoutTestSupport {
+ STATIC_ONLY(LayoutTestSupport);
+
+ public:
+ PLATFORM_EXPORT static bool IsRunningLayoutTest();
+ PLATFORM_EXPORT static void SetIsRunningLayoutTest(bool);
+ PLATFORM_EXPORT static bool IsMockThemeEnabledForTest();
+ PLATFORM_EXPORT static void SetMockThemeEnabledForTest(bool);
+ PLATFORM_EXPORT static bool IsFontAntialiasingEnabledForTest();
+ PLATFORM_EXPORT static void SetFontAntialiasingEnabledForTest(bool);
+ PLATFORM_EXPORT static bool IsTextSubpixelPositioningAllowedForTest();
+ PLATFORM_EXPORT static void SetTextSubpixelPositioningAllowedForTest(bool);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LAYOUT_TEST_SUPPORT_H_
diff --git a/chromium/third_party/blink/renderer/platform/layout_unit.cc b/chromium/third_party/blink/renderer/platform/layout_unit.cc
new file mode 100644
index 00000000000..3a0936da9e1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/layout_unit.cc
@@ -0,0 +1,28 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/layout_unit.h"
+
+#include <ostream>
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+String LayoutUnit::ToString() const {
+ if (value_ == LayoutUnit::Max().RawValue())
+ return "LayoutUnit::max(" + String::Number(ToDouble()) + ")";
+ if (value_ == LayoutUnit::Min().RawValue())
+ return "LayoutUnit::min(" + String::Number(ToDouble()) + ")";
+ if (value_ == LayoutUnit::NearlyMax().RawValue())
+ return "LayoutUnit::nearlyMax(" + String::Number(ToDouble()) + ")";
+ if (value_ == LayoutUnit::NearlyMin().RawValue())
+ return "LayoutUnit::nearlyMin(" + String::Number(ToDouble()) + ")";
+ return String::Number(ToDouble());
+}
+
+std::ostream& operator<<(std::ostream& stream, const LayoutUnit& value) {
+ return stream << value.ToString();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/layout_unit.h b/chromium/third_party/blink/renderer/platform/layout_unit.h
new file mode 100644
index 00000000000..d9cd9b51509
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/layout_unit.h
@@ -0,0 +1,717 @@
+/*
+ * Copyright (c) 2012, 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_LAYOUT_UNIT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LAYOUT_UNIT_H_
+
+#include <iosfwd>
+#include <limits>
+#include "base/numerics/safe_conversions.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/saturated_arithmetic.h"
+
+namespace blink {
+
+#if DCHECK_IS_ON()
+#define REPORT_OVERFLOW(doesOverflow) \
+ DLOG_IF(ERROR, !(doesOverflow)) << "LayoutUnit overflow !(" << #doesOverflow \
+ << ") in " << WTF_PRETTY_FUNCTION
+#else
+#define REPORT_OVERFLOW(doesOverflow) ((void)0)
+#endif
+
+static const unsigned kLayoutUnitFractionalBits = 6;
+static const int kFixedPointDenominator = 1 << kLayoutUnitFractionalBits;
+
+const int kIntMaxForLayoutUnit = INT_MAX / kFixedPointDenominator;
+const int kIntMinForLayoutUnit = INT_MIN / kFixedPointDenominator;
+
+// TODO(thakis): Remove these two lines once http://llvm.org/PR26504 is resolved
+class PLATFORM_EXPORT LayoutUnit;
+inline bool operator<(const LayoutUnit&, const LayoutUnit&);
+
+class LayoutUnit {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ LayoutUnit() : value_(0) {}
+ explicit LayoutUnit(int value) { SetValue(value); }
+ explicit LayoutUnit(unsigned short value) { SetValue(value); }
+ explicit LayoutUnit(unsigned value) { SetValue(value); }
+ explicit LayoutUnit(unsigned long value) {
+ value_ = base::saturated_cast<int>(value * kFixedPointDenominator);
+ }
+ explicit LayoutUnit(unsigned long long value) {
+ value_ = base::saturated_cast<int>(value * kFixedPointDenominator);
+ }
+ explicit LayoutUnit(float value) {
+ value_ = base::saturated_cast<int>(value * kFixedPointDenominator);
+ }
+ explicit LayoutUnit(double value) {
+ value_ = base::saturated_cast<int>(value * kFixedPointDenominator);
+ }
+
+ static LayoutUnit FromFloatCeil(float value) {
+ LayoutUnit v;
+ v.value_ = base::saturated_cast<int>(ceilf(value * kFixedPointDenominator));
+ return v;
+ }
+
+ static LayoutUnit FromFloatFloor(float value) {
+ LayoutUnit v;
+ v.value_ =
+ base::saturated_cast<int>(floorf(value * kFixedPointDenominator));
+ return v;
+ }
+
+ static LayoutUnit FromFloatRound(float value) {
+ LayoutUnit v;
+ v.value_ =
+ base::saturated_cast<int>(roundf(value * kFixedPointDenominator));
+ return v;
+ }
+
+ static LayoutUnit FromDoubleRound(double value) {
+ LayoutUnit v;
+ v.value_ = base::saturated_cast<int>(round(value * kFixedPointDenominator));
+ return v;
+ }
+
+ int ToInt() const { return value_ / kFixedPointDenominator; }
+ float ToFloat() const {
+ return static_cast<float>(value_) / kFixedPointDenominator;
+ }
+ double ToDouble() const {
+ return static_cast<double>(value_) / kFixedPointDenominator;
+ }
+ unsigned ToUnsigned() const {
+ REPORT_OVERFLOW(value_ >= 0);
+ return ToInt();
+ }
+
+ // Conversion to int or unsigned is lossy. 'explicit' on these operators won't
+ // work because there are also other implicit conversion paths (e.g. operator
+ // bool then to int which would generate wrong result). Use toInt() and
+ // toUnsigned() instead.
+ operator int() const = delete;
+ operator unsigned() const = delete;
+
+ operator double() const { return ToDouble(); }
+ operator float() const { return ToFloat(); }
+ operator bool() const { return value_; }
+
+ LayoutUnit operator++(int) {
+ value_ = ClampAdd(value_, kFixedPointDenominator);
+ return *this;
+ }
+
+ inline int RawValue() const { return value_; }
+ inline void SetRawValue(int value) { value_ = value; }
+ void SetRawValue(long long value) {
+ REPORT_OVERFLOW(value > std::numeric_limits<int>::min() &&
+ value < std::numeric_limits<int>::max());
+ value_ = static_cast<int>(value);
+ }
+
+ LayoutUnit Abs() const {
+ LayoutUnit return_value;
+ return_value.SetRawValue(::abs(value_));
+ return return_value;
+ }
+ int Ceil() const {
+ if (UNLIKELY(value_ >= INT_MAX - kFixedPointDenominator + 1))
+ return kIntMaxForLayoutUnit;
+
+ if (value_ >= 0)
+ return (value_ + kFixedPointDenominator - 1) / kFixedPointDenominator;
+ return ToInt();
+ }
+ ALWAYS_INLINE int Round() const {
+ return ToInt() + ((Fraction().RawValue() + (kFixedPointDenominator / 2)) >>
+ kLayoutUnitFractionalBits);
+ }
+
+ int Floor() const {
+ if (UNLIKELY(value_ <= INT_MIN + kFixedPointDenominator - 1))
+ return kIntMinForLayoutUnit;
+
+ return value_ >> kLayoutUnitFractionalBits;
+ }
+
+ LayoutUnit ClampNegativeToZero() const {
+ return value_ < 0 ? LayoutUnit() : *this;
+ }
+
+ LayoutUnit ClampPositiveToZero() const {
+ return value_ > 0 ? LayoutUnit() : *this;
+ }
+
+ LayoutUnit Fraction() const {
+ // Add the fraction to the size (as opposed to the full location) to avoid
+ // overflows. Compute fraction using the mod operator to preserve the sign
+ // of the value as it may affect rounding.
+ LayoutUnit fraction;
+ fraction.SetRawValue(RawValue() % kFixedPointDenominator);
+ return fraction;
+ }
+
+ bool MightBeSaturated() const {
+ return RawValue() == std::numeric_limits<int>::max() ||
+ RawValue() == std::numeric_limits<int>::min();
+ }
+
+ static float Epsilon() { return 1.0f / kFixedPointDenominator; }
+
+ static const LayoutUnit Max() {
+ LayoutUnit m;
+ m.value_ = std::numeric_limits<int>::max();
+ return m;
+ }
+ static const LayoutUnit Min() {
+ LayoutUnit m;
+ m.value_ = std::numeric_limits<int>::min();
+ return m;
+ }
+
+ // Versions of max/min that are slightly smaller/larger than max/min() to
+ // allow for roinding without overflowing.
+ static const LayoutUnit NearlyMax() {
+ LayoutUnit m;
+ m.value_ = std::numeric_limits<int>::max() - kFixedPointDenominator / 2;
+ return m;
+ }
+ static const LayoutUnit NearlyMin() {
+ LayoutUnit m;
+ m.value_ = std::numeric_limits<int>::min() + kFixedPointDenominator / 2;
+ return m;
+ }
+
+ static LayoutUnit Clamp(double value) { return FromFloatFloor(value); }
+
+ String ToString() const;
+
+ private:
+ static bool IsInBounds(int value) {
+ return ::abs(value) <=
+ std::numeric_limits<int>::max() / kFixedPointDenominator;
+ }
+ static bool IsInBounds(unsigned value) {
+ return value <= static_cast<unsigned>(std::numeric_limits<int>::max()) /
+ kFixedPointDenominator;
+ }
+ static bool IsInBounds(double value) {
+ return ::fabs(value) <=
+ std::numeric_limits<int>::max() / kFixedPointDenominator;
+ }
+
+ ALWAYS_INLINE void SetValue(int value) {
+ value_ = SaturatedSet<kLayoutUnitFractionalBits>(value);
+ }
+
+ inline void SetValue(unsigned value) {
+ value_ = SaturatedSet<kLayoutUnitFractionalBits>(value);
+ }
+
+ int value_;
+};
+
+inline bool operator<=(const LayoutUnit& a, const LayoutUnit& b) {
+ return a.RawValue() <= b.RawValue();
+}
+
+inline bool operator<=(const LayoutUnit& a, float b) {
+ return a.ToFloat() <= b;
+}
+
+inline bool operator<=(const LayoutUnit& a, int b) {
+ return a <= LayoutUnit(b);
+}
+
+inline bool operator<=(const float a, const LayoutUnit& b) {
+ return a <= b.ToFloat();
+}
+
+inline bool operator<=(const int a, const LayoutUnit& b) {
+ return LayoutUnit(a) <= b;
+}
+
+inline bool operator>=(const LayoutUnit& a, const LayoutUnit& b) {
+ return a.RawValue() >= b.RawValue();
+}
+
+inline bool operator>=(const LayoutUnit& a, int b) {
+ return a >= LayoutUnit(b);
+}
+
+inline bool operator>=(const float a, const LayoutUnit& b) {
+ return a >= b.ToFloat();
+}
+
+inline bool operator>=(const LayoutUnit& a, float b) {
+ return a.ToFloat() >= b;
+}
+
+inline bool operator>=(const int a, const LayoutUnit& b) {
+ return LayoutUnit(a) >= b;
+}
+
+inline bool operator<(const LayoutUnit& a, const LayoutUnit& b) {
+ return a.RawValue() < b.RawValue();
+}
+
+inline bool operator<(const LayoutUnit& a, int b) {
+ return a < LayoutUnit(b);
+}
+
+inline bool operator<(const LayoutUnit& a, float b) {
+ return a.ToFloat() < b;
+}
+
+inline bool operator<(const LayoutUnit& a, double b) {
+ return a.ToDouble() < b;
+}
+
+inline bool operator<(const int a, const LayoutUnit& b) {
+ return LayoutUnit(a) < b;
+}
+
+inline bool operator<(const float a, const LayoutUnit& b) {
+ return a < b.ToFloat();
+}
+
+inline bool operator>(const LayoutUnit& a, const LayoutUnit& b) {
+ return a.RawValue() > b.RawValue();
+}
+
+inline bool operator>(const LayoutUnit& a, double b) {
+ return a.ToDouble() > b;
+}
+
+inline bool operator>(const LayoutUnit& a, float b) {
+ return a.ToFloat() > b;
+}
+
+inline bool operator>(const LayoutUnit& a, int b) {
+ return a > LayoutUnit(b);
+}
+
+inline bool operator>(const int a, const LayoutUnit& b) {
+ return LayoutUnit(a) > b;
+}
+
+inline bool operator>(const float a, const LayoutUnit& b) {
+ return a > b.ToFloat();
+}
+
+inline bool operator>(const double a, const LayoutUnit& b) {
+ return a > b.ToDouble();
+}
+
+inline bool operator!=(const LayoutUnit& a, const LayoutUnit& b) {
+ return a.RawValue() != b.RawValue();
+}
+
+inline bool operator!=(const LayoutUnit& a, float b) {
+ return a != LayoutUnit(b);
+}
+
+inline bool operator!=(const int a, const LayoutUnit& b) {
+ return LayoutUnit(a) != b;
+}
+
+inline bool operator!=(const LayoutUnit& a, int b) {
+ return a != LayoutUnit(b);
+}
+
+inline bool operator==(const LayoutUnit& a, const LayoutUnit& b) {
+ return a.RawValue() == b.RawValue();
+}
+
+inline bool operator==(const LayoutUnit& a, int b) {
+ return a == LayoutUnit(b);
+}
+
+inline bool operator==(const int a, const LayoutUnit& b) {
+ return LayoutUnit(a) == b;
+}
+
+inline bool operator==(const LayoutUnit& a, float b) {
+ return a.ToFloat() == b;
+}
+
+inline bool operator==(const float a, const LayoutUnit& b) {
+ return a == b.ToFloat();
+}
+
+// For multiplication that's prone to overflow, this bounds it to
+// LayoutUnit::max() and ::min()
+inline LayoutUnit BoundedMultiply(const LayoutUnit& a, const LayoutUnit& b) {
+ int64_t result = static_cast<int64_t>(a.RawValue()) *
+ static_cast<int64_t>(b.RawValue()) / kFixedPointDenominator;
+ int32_t high = static_cast<int32_t>(result >> 32);
+ int32_t low = static_cast<int32_t>(result);
+ uint32_t saturated =
+ (static_cast<uint32_t>(a.RawValue() ^ b.RawValue()) >> 31) +
+ std::numeric_limits<int>::max();
+ // If the higher 32 bits does not match the lower 32 with sign extension the
+ // operation overflowed.
+ if (high != low >> 31)
+ result = saturated;
+
+ LayoutUnit return_val;
+ return_val.SetRawValue(static_cast<int>(result));
+ return return_val;
+}
+
+inline LayoutUnit operator*(const LayoutUnit& a, const LayoutUnit& b) {
+ return BoundedMultiply(a, b);
+}
+
+inline double operator*(const LayoutUnit& a, double b) {
+ return a.ToDouble() * b;
+}
+
+inline float operator*(const LayoutUnit& a, float b) {
+ return a.ToFloat() * b;
+}
+
+inline LayoutUnit operator*(const LayoutUnit& a, int b) {
+ return a * LayoutUnit(b);
+}
+
+inline LayoutUnit operator*(const LayoutUnit& a, unsigned short b) {
+ return a * LayoutUnit(b);
+}
+
+inline LayoutUnit operator*(const LayoutUnit& a, unsigned b) {
+ return a * LayoutUnit(b);
+}
+
+inline LayoutUnit operator*(const LayoutUnit& a, unsigned long b) {
+ return a * LayoutUnit(b);
+}
+
+inline LayoutUnit operator*(const LayoutUnit& a, unsigned long long b) {
+ return a * LayoutUnit(b);
+}
+
+inline LayoutUnit operator*(unsigned short a, const LayoutUnit& b) {
+ return LayoutUnit(a) * b;
+}
+
+inline LayoutUnit operator*(unsigned a, const LayoutUnit& b) {
+ return LayoutUnit(a) * b;
+}
+
+inline LayoutUnit operator*(unsigned long a, const LayoutUnit& b) {
+ return LayoutUnit(a) * b;
+}
+
+inline LayoutUnit operator*(unsigned long long a, const LayoutUnit& b) {
+ return LayoutUnit(a) * b;
+}
+
+inline LayoutUnit operator*(const int a, const LayoutUnit& b) {
+ return LayoutUnit(a) * b;
+}
+
+inline float operator*(const float a, const LayoutUnit& b) {
+ return a * b.ToFloat();
+}
+
+inline double operator*(const double a, const LayoutUnit& b) {
+ return a * b.ToDouble();
+}
+
+inline LayoutUnit operator/(const LayoutUnit& a, const LayoutUnit& b) {
+ LayoutUnit return_val;
+ long long raw_val = static_cast<long long>(kFixedPointDenominator) *
+ a.RawValue() / b.RawValue();
+ return_val.SetRawValue(base::saturated_cast<int>(raw_val));
+ return return_val;
+}
+
+inline float operator/(const LayoutUnit& a, float b) {
+ return a.ToFloat() / b;
+}
+
+inline double operator/(const LayoutUnit& a, double b) {
+ return a.ToDouble() / b;
+}
+
+inline LayoutUnit operator/(const LayoutUnit& a, int b) {
+ return a / LayoutUnit(b);
+}
+
+inline LayoutUnit operator/(const LayoutUnit& a, unsigned short b) {
+ return a / LayoutUnit(b);
+}
+
+inline LayoutUnit operator/(const LayoutUnit& a, unsigned b) {
+ return a / LayoutUnit(b);
+}
+
+inline LayoutUnit operator/(const LayoutUnit& a, unsigned long b) {
+ return a / LayoutUnit(b);
+}
+
+inline LayoutUnit operator/(const LayoutUnit& a, unsigned long long b) {
+ return a / LayoutUnit(b);
+}
+
+inline float operator/(const float a, const LayoutUnit& b) {
+ return a / b.ToFloat();
+}
+
+inline double operator/(const double a, const LayoutUnit& b) {
+ return a / b.ToDouble();
+}
+
+inline LayoutUnit operator/(const int a, const LayoutUnit& b) {
+ return LayoutUnit(a) / b;
+}
+
+inline LayoutUnit operator/(unsigned short a, const LayoutUnit& b) {
+ return LayoutUnit(a) / b;
+}
+
+inline LayoutUnit operator/(unsigned a, const LayoutUnit& b) {
+ return LayoutUnit(a) / b;
+}
+
+inline LayoutUnit operator/(unsigned long a, const LayoutUnit& b) {
+ return LayoutUnit(a) / b;
+}
+
+inline LayoutUnit operator/(unsigned long long a, const LayoutUnit& b) {
+ return LayoutUnit(a) / b;
+}
+
+ALWAYS_INLINE LayoutUnit operator+(const LayoutUnit& a, const LayoutUnit& b) {
+ LayoutUnit return_val;
+ return_val.SetRawValue(ClampAdd(a.RawValue(), b.RawValue()).RawValue());
+ return return_val;
+}
+
+inline LayoutUnit operator+(const LayoutUnit& a, int b) {
+ return a + LayoutUnit(b);
+}
+
+inline float operator+(const LayoutUnit& a, float b) {
+ return a.ToFloat() + b;
+}
+
+inline double operator+(const LayoutUnit& a, double b) {
+ return a.ToDouble() + b;
+}
+
+inline LayoutUnit operator+(const int a, const LayoutUnit& b) {
+ return LayoutUnit(a) + b;
+}
+
+inline float operator+(const float a, const LayoutUnit& b) {
+ return a + b.ToFloat();
+}
+
+inline double operator+(const double a, const LayoutUnit& b) {
+ return a + b.ToDouble();
+}
+
+ALWAYS_INLINE LayoutUnit operator-(const LayoutUnit& a, const LayoutUnit& b) {
+ LayoutUnit return_val;
+ return_val.SetRawValue(ClampSub(a.RawValue(), b.RawValue()).RawValue());
+ return return_val;
+}
+
+inline LayoutUnit operator-(const LayoutUnit& a, int b) {
+ return a - LayoutUnit(b);
+}
+
+inline LayoutUnit operator-(const LayoutUnit& a, unsigned b) {
+ return a - LayoutUnit(b);
+}
+
+inline float operator-(const LayoutUnit& a, float b) {
+ return a.ToFloat() - b;
+}
+
+inline double operator-(const LayoutUnit& a, double b) {
+ return a.ToDouble() - b;
+}
+
+inline LayoutUnit operator-(const int a, const LayoutUnit& b) {
+ return LayoutUnit(a) - b;
+}
+
+inline float operator-(const float a, const LayoutUnit& b) {
+ return a - b.ToFloat();
+}
+
+inline LayoutUnit operator-(const LayoutUnit& a) {
+ LayoutUnit return_val;
+ return_val.SetRawValue((-MakeClampedNum(a.RawValue())).RawValue());
+ return return_val;
+}
+
+// For returning the remainder after a division with integer results.
+inline LayoutUnit IntMod(const LayoutUnit& a, const LayoutUnit& b) {
+ // This calculates the modulo so that: a = static_cast<int>(a / b) * b +
+ // intMod(a, b).
+ LayoutUnit return_val;
+ return_val.SetRawValue(a.RawValue() % b.RawValue());
+ return return_val;
+}
+
+inline LayoutUnit operator%(const LayoutUnit& a, const LayoutUnit& b) {
+ // This calculates the modulo so that: a = (a / b) * b + a % b.
+ LayoutUnit return_val;
+ long long raw_val =
+ (static_cast<long long>(kFixedPointDenominator) * a.RawValue()) %
+ b.RawValue();
+ return_val.SetRawValue(raw_val / kFixedPointDenominator);
+ return return_val;
+}
+
+inline LayoutUnit operator%(const LayoutUnit& a, int b) {
+ return a % LayoutUnit(b);
+}
+
+inline LayoutUnit operator%(int a, const LayoutUnit& b) {
+ return LayoutUnit(a) % b;
+}
+
+inline LayoutUnit& operator+=(LayoutUnit& a, const LayoutUnit& b) {
+ a.SetRawValue(ClampAdd(a.RawValue(), b.RawValue()).RawValue());
+ return a;
+}
+
+inline LayoutUnit& operator+=(LayoutUnit& a, int b) {
+ a = a + LayoutUnit(b);
+ return a;
+}
+
+inline LayoutUnit& operator+=(LayoutUnit& a, float b) {
+ a = LayoutUnit(a + b);
+ return a;
+}
+
+inline float& operator+=(float& a, const LayoutUnit& b) {
+ a = a + b;
+ return a;
+}
+
+inline LayoutUnit& operator-=(LayoutUnit& a, int b) {
+ a = a - LayoutUnit(b);
+ return a;
+}
+
+inline LayoutUnit& operator-=(LayoutUnit& a, const LayoutUnit& b) {
+ a.SetRawValue(ClampSub(a.RawValue(), b.RawValue()).RawValue());
+ return a;
+}
+
+inline LayoutUnit& operator-=(LayoutUnit& a, float b) {
+ a = LayoutUnit(a - b);
+ return a;
+}
+
+inline float& operator-=(float& a, const LayoutUnit& b) {
+ a = a - b;
+ return a;
+}
+
+inline LayoutUnit& operator*=(LayoutUnit& a, const LayoutUnit& b) {
+ a = a * b;
+ return a;
+}
+
+inline LayoutUnit& operator*=(LayoutUnit& a, float b) {
+ a = LayoutUnit(a * b);
+ return a;
+}
+
+inline float& operator*=(float& a, const LayoutUnit& b) {
+ a = a * b;
+ return a;
+}
+
+inline LayoutUnit& operator/=(LayoutUnit& a, const LayoutUnit& b) {
+ a = a / b;
+ return a;
+}
+
+inline LayoutUnit& operator/=(LayoutUnit& a, float b) {
+ a = LayoutUnit(a / b);
+ return a;
+}
+
+inline float& operator/=(float& a, const LayoutUnit& b) {
+ a = a / b;
+ return a;
+}
+
+inline int SnapSizeToPixel(LayoutUnit size, LayoutUnit location) {
+ LayoutUnit fraction = location.Fraction();
+ return (fraction + size).Round() - fraction.Round();
+}
+
+inline int RoundToInt(LayoutUnit value) {
+ return value.Round();
+}
+
+inline int FloorToInt(LayoutUnit value) {
+ return value.Floor();
+}
+
+inline LayoutUnit AbsoluteValue(const LayoutUnit& value) {
+ return value.Abs();
+}
+
+inline LayoutUnit LayoutMod(const LayoutUnit& numerator,
+ const LayoutUnit& denominator) {
+ return numerator % denominator;
+}
+
+inline LayoutUnit LayoutMod(const LayoutUnit& numerator, int denominator) {
+ return numerator % LayoutUnit(denominator);
+}
+
+inline bool IsIntegerValue(const LayoutUnit value) {
+ return value.ToInt() == value;
+}
+
+PLATFORM_EXPORT std::ostream& operator<<(std::ostream&, const LayoutUnit&);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LAYOUT_UNIT_H_
diff --git a/chromium/third_party/blink/renderer/platform/layout_unit_test.cc b/chromium/third_party/blink/renderer/platform/layout_unit_test.cc
new file mode 100644
index 00000000000..9545139af4b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/layout_unit_test.cc
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) 2012, 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 "third_party/blink/renderer/platform/layout_unit.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include <limits.h>
+
+namespace blink {
+
+TEST(LayoutUnitTest, LayoutUnitInt) {
+ EXPECT_EQ(kIntMinForLayoutUnit, LayoutUnit(INT_MIN).ToInt());
+ EXPECT_EQ(kIntMinForLayoutUnit, LayoutUnit(INT_MIN / 2).ToInt());
+ EXPECT_EQ(kIntMinForLayoutUnit, LayoutUnit(kIntMinForLayoutUnit - 1).ToInt());
+ EXPECT_EQ(kIntMinForLayoutUnit, LayoutUnit(kIntMinForLayoutUnit).ToInt());
+ EXPECT_EQ(kIntMinForLayoutUnit + 1,
+ LayoutUnit(kIntMinForLayoutUnit + 1).ToInt());
+ EXPECT_EQ(kIntMinForLayoutUnit / 2,
+ LayoutUnit(kIntMinForLayoutUnit / 2).ToInt());
+ EXPECT_EQ(-10000, LayoutUnit(-10000).ToInt());
+ EXPECT_EQ(-1000, LayoutUnit(-1000).ToInt());
+ EXPECT_EQ(-100, LayoutUnit(-100).ToInt());
+ EXPECT_EQ(-10, LayoutUnit(-10).ToInt());
+ EXPECT_EQ(-1, LayoutUnit(-1).ToInt());
+ EXPECT_EQ(0, LayoutUnit(0).ToInt());
+ EXPECT_EQ(1, LayoutUnit(1).ToInt());
+ EXPECT_EQ(100, LayoutUnit(100).ToInt());
+ EXPECT_EQ(1000, LayoutUnit(1000).ToInt());
+ EXPECT_EQ(10000, LayoutUnit(10000).ToInt());
+ EXPECT_EQ(kIntMaxForLayoutUnit / 2,
+ LayoutUnit(kIntMaxForLayoutUnit / 2).ToInt());
+ EXPECT_EQ(kIntMaxForLayoutUnit - 1,
+ LayoutUnit(kIntMaxForLayoutUnit - 1).ToInt());
+ EXPECT_EQ(kIntMaxForLayoutUnit, LayoutUnit(kIntMaxForLayoutUnit).ToInt());
+ EXPECT_EQ(kIntMaxForLayoutUnit, LayoutUnit(kIntMaxForLayoutUnit + 1).ToInt());
+ EXPECT_EQ(kIntMaxForLayoutUnit, LayoutUnit(INT_MAX / 2).ToInt());
+ EXPECT_EQ(kIntMaxForLayoutUnit, LayoutUnit(INT_MAX).ToInt());
+}
+
+TEST(LayoutUnitTest, LayoutUnitFloat) {
+ const float kTolerance = 1.0f / kFixedPointDenominator;
+ EXPECT_FLOAT_EQ(1.0f, LayoutUnit(1.0f).ToFloat());
+ EXPECT_FLOAT_EQ(1.25f, LayoutUnit(1.25f).ToFloat());
+ EXPECT_NEAR(LayoutUnit(1.1f).ToFloat(), 1.1f, kTolerance);
+ EXPECT_NEAR(LayoutUnit(1.33f).ToFloat(), 1.33f, kTolerance);
+ EXPECT_NEAR(LayoutUnit(1.3333f).ToFloat(), 1.3333f, kTolerance);
+ EXPECT_NEAR(LayoutUnit(1.53434f).ToFloat(), 1.53434f, kTolerance);
+ EXPECT_NEAR(LayoutUnit(345634).ToFloat(), 345634.0f, kTolerance);
+ EXPECT_NEAR(LayoutUnit(345634.12335f).ToFloat(), 345634.12335f, kTolerance);
+ EXPECT_NEAR(LayoutUnit(-345634.12335f).ToFloat(), -345634.12335f, kTolerance);
+ EXPECT_NEAR(LayoutUnit(-345634).ToFloat(), -345634.0f, kTolerance);
+}
+
+TEST(LayoutUnitTest, LayoutUnitRounding) {
+ EXPECT_EQ(-2, LayoutUnit(-1.9f).Round());
+ EXPECT_EQ(-2, LayoutUnit(-1.6f).Round());
+ EXPECT_EQ(-2, LayoutUnit::FromFloatRound(-1.51f).Round());
+ EXPECT_EQ(-1, LayoutUnit::FromFloatRound(-1.5f).Round());
+ EXPECT_EQ(-1, LayoutUnit::FromFloatRound(-1.49f).Round());
+ EXPECT_EQ(-1, LayoutUnit(-1.0f).Round());
+ EXPECT_EQ(-1, LayoutUnit::FromFloatRound(-0.99f).Round());
+ EXPECT_EQ(-1, LayoutUnit::FromFloatRound(-0.51f).Round());
+ EXPECT_EQ(0, LayoutUnit::FromFloatRound(-0.50f).Round());
+ EXPECT_EQ(0, LayoutUnit::FromFloatRound(-0.49f).Round());
+ EXPECT_EQ(0, LayoutUnit(-0.1f).Round());
+ EXPECT_EQ(0, LayoutUnit(0.0f).Round());
+ EXPECT_EQ(0, LayoutUnit(0.1f).Round());
+ EXPECT_EQ(0, LayoutUnit::FromFloatRound(0.49f).Round());
+ EXPECT_EQ(1, LayoutUnit::FromFloatRound(0.50f).Round());
+ EXPECT_EQ(1, LayoutUnit::FromFloatRound(0.51f).Round());
+ EXPECT_EQ(1, LayoutUnit(0.99f).Round());
+ EXPECT_EQ(1, LayoutUnit(1.0f).Round());
+ EXPECT_EQ(1, LayoutUnit::FromFloatRound(1.49f).Round());
+ EXPECT_EQ(2, LayoutUnit::FromFloatRound(1.5f).Round());
+ EXPECT_EQ(2, LayoutUnit::FromFloatRound(1.51f).Round());
+ // The fractional part of LayoutUnit::Max() is 0x3f, so it should round up.
+ EXPECT_EQ(((std::numeric_limits<int>::max() / kFixedPointDenominator) + 1),
+ LayoutUnit::Max().Round());
+ // The fractional part of LayoutUnit::Min() is 0, so the next bigger possible
+ // value should round down.
+ LayoutUnit epsilon;
+ epsilon.SetRawValue(1);
+ EXPECT_EQ(((std::numeric_limits<int>::min() / kFixedPointDenominator)),
+ (LayoutUnit::Min() + epsilon).Round());
+}
+
+TEST(LayoutUnitTest, LayoutUnitSnapSizeToPixel) {
+ EXPECT_EQ(1, SnapSizeToPixel(LayoutUnit(1), LayoutUnit(0)));
+ EXPECT_EQ(1, SnapSizeToPixel(LayoutUnit(1), LayoutUnit(0.5)));
+ EXPECT_EQ(2, SnapSizeToPixel(LayoutUnit(1.5), LayoutUnit(0)));
+ EXPECT_EQ(2, SnapSizeToPixel(LayoutUnit(1.5), LayoutUnit(0.49)));
+ EXPECT_EQ(1, SnapSizeToPixel(LayoutUnit(1.5), LayoutUnit(0.5)));
+ EXPECT_EQ(1, SnapSizeToPixel(LayoutUnit(1.5), LayoutUnit(0.75)));
+ EXPECT_EQ(1, SnapSizeToPixel(LayoutUnit(1.5), LayoutUnit(0.99)));
+ EXPECT_EQ(2, SnapSizeToPixel(LayoutUnit(1.5), LayoutUnit(1)));
+
+ EXPECT_EQ(0, SnapSizeToPixel(LayoutUnit(0.5), LayoutUnit(1.5)));
+ EXPECT_EQ(0, SnapSizeToPixel(LayoutUnit(0.99), LayoutUnit(1.5)));
+ EXPECT_EQ(1, SnapSizeToPixel(LayoutUnit(1.0), LayoutUnit(1.5)));
+ EXPECT_EQ(1, SnapSizeToPixel(LayoutUnit(1.49), LayoutUnit(1.5)));
+ EXPECT_EQ(1, SnapSizeToPixel(LayoutUnit(1.5), LayoutUnit(1.5)));
+
+ EXPECT_EQ(101, SnapSizeToPixel(LayoutUnit(100.5), LayoutUnit(100)));
+ EXPECT_EQ(kIntMaxForLayoutUnit,
+ SnapSizeToPixel(LayoutUnit(kIntMaxForLayoutUnit), LayoutUnit(0.3)));
+ EXPECT_EQ(
+ kIntMinForLayoutUnit,
+ SnapSizeToPixel(LayoutUnit(kIntMinForLayoutUnit), LayoutUnit(-0.3)));
+}
+
+TEST(LayoutUnitTest, LayoutUnitMultiplication) {
+ EXPECT_EQ(1, (LayoutUnit(1) * LayoutUnit(1)).ToInt());
+ EXPECT_EQ(2, (LayoutUnit(1) * LayoutUnit(2)).ToInt());
+ EXPECT_EQ(2, (LayoutUnit(2) * LayoutUnit(1)).ToInt());
+ EXPECT_EQ(1, (LayoutUnit(2) * LayoutUnit(0.5)).ToInt());
+ EXPECT_EQ(1, (LayoutUnit(0.5) * LayoutUnit(2)).ToInt());
+ EXPECT_EQ(100, (LayoutUnit(100) * LayoutUnit(1)).ToInt());
+
+ EXPECT_EQ(-1, (LayoutUnit(-1) * LayoutUnit(1)).ToInt());
+ EXPECT_EQ(-2, (LayoutUnit(-1) * LayoutUnit(2)).ToInt());
+ EXPECT_EQ(-2, (LayoutUnit(-2) * LayoutUnit(1)).ToInt());
+ EXPECT_EQ(-1, (LayoutUnit(-2) * LayoutUnit(0.5)).ToInt());
+ EXPECT_EQ(-1, (LayoutUnit(-0.5) * LayoutUnit(2)).ToInt());
+ EXPECT_EQ(-100, (LayoutUnit(-100) * LayoutUnit(1)).ToInt());
+
+ EXPECT_EQ(1, (LayoutUnit(-1) * LayoutUnit(-1)).ToInt());
+ EXPECT_EQ(2, (LayoutUnit(-1) * LayoutUnit(-2)).ToInt());
+ EXPECT_EQ(2, (LayoutUnit(-2) * LayoutUnit(-1)).ToInt());
+ EXPECT_EQ(1, (LayoutUnit(-2) * LayoutUnit(-0.5)).ToInt());
+ EXPECT_EQ(1, (LayoutUnit(-0.5) * LayoutUnit(-2)).ToInt());
+ EXPECT_EQ(100, (LayoutUnit(-100) * LayoutUnit(-1)).ToInt());
+
+ EXPECT_EQ(333, (LayoutUnit(100) * LayoutUnit(3.33)).Round());
+ EXPECT_EQ(-333, (LayoutUnit(-100) * LayoutUnit(3.33)).Round());
+ EXPECT_EQ(333, (LayoutUnit(-100) * LayoutUnit(-3.33)).Round());
+
+ size_t a_hundred_size_t = 100;
+ EXPECT_EQ(100, (LayoutUnit(a_hundred_size_t) * LayoutUnit(1)).ToInt());
+ EXPECT_EQ(400, (a_hundred_size_t * LayoutUnit(4)).ToInt());
+ EXPECT_EQ(400, (LayoutUnit(4) * a_hundred_size_t).ToInt());
+
+ int quarter_max = kIntMaxForLayoutUnit / 4;
+ EXPECT_EQ(quarter_max * 2, (LayoutUnit(quarter_max) * LayoutUnit(2)).ToInt());
+ EXPECT_EQ(quarter_max * 3, (LayoutUnit(quarter_max) * LayoutUnit(3)).ToInt());
+ EXPECT_EQ(quarter_max * 4, (LayoutUnit(quarter_max) * LayoutUnit(4)).ToInt());
+ EXPECT_EQ(kIntMaxForLayoutUnit,
+ (LayoutUnit(quarter_max) * LayoutUnit(5)).ToInt());
+
+ size_t overflow_int_size_t = kIntMaxForLayoutUnit * 4;
+ EXPECT_EQ(kIntMaxForLayoutUnit,
+ (LayoutUnit(overflow_int_size_t) * LayoutUnit(2)).ToInt());
+ EXPECT_EQ(kIntMaxForLayoutUnit,
+ (overflow_int_size_t * LayoutUnit(4)).ToInt());
+ EXPECT_EQ(kIntMaxForLayoutUnit,
+ (LayoutUnit(4) * overflow_int_size_t).ToInt());
+}
+
+TEST(LayoutUnitTest, LayoutUnitDivision) {
+ EXPECT_EQ(1, (LayoutUnit(1) / LayoutUnit(1)).ToInt());
+ EXPECT_EQ(0, (LayoutUnit(1) / LayoutUnit(2)).ToInt());
+ EXPECT_EQ(2, (LayoutUnit(2) / LayoutUnit(1)).ToInt());
+ EXPECT_EQ(4, (LayoutUnit(2) / LayoutUnit(0.5)).ToInt());
+ EXPECT_EQ(0, (LayoutUnit(0.5) / LayoutUnit(2)).ToInt());
+ EXPECT_EQ(10, (LayoutUnit(100) / LayoutUnit(10)).ToInt());
+ EXPECT_FLOAT_EQ(0.5f, (LayoutUnit(1) / LayoutUnit(2)).ToFloat());
+ EXPECT_FLOAT_EQ(0.25f, (LayoutUnit(0.5) / LayoutUnit(2)).ToFloat());
+
+ EXPECT_EQ(-1, (LayoutUnit(-1) / LayoutUnit(1)).ToInt());
+ EXPECT_EQ(0, (LayoutUnit(-1) / LayoutUnit(2)).ToInt());
+ EXPECT_EQ(-2, (LayoutUnit(-2) / LayoutUnit(1)).ToInt());
+ EXPECT_EQ(-4, (LayoutUnit(-2) / LayoutUnit(0.5)).ToInt());
+ EXPECT_EQ(0, (LayoutUnit(-0.5) / LayoutUnit(2)).ToInt());
+ EXPECT_EQ(-10, (LayoutUnit(-100) / LayoutUnit(10)).ToInt());
+ EXPECT_FLOAT_EQ(-0.5f, (LayoutUnit(-1) / LayoutUnit(2)).ToFloat());
+ EXPECT_FLOAT_EQ(-0.25f, (LayoutUnit(-0.5) / LayoutUnit(2)).ToFloat());
+
+ EXPECT_EQ(1, (LayoutUnit(-1) / LayoutUnit(-1)).ToInt());
+ EXPECT_EQ(0, (LayoutUnit(-1) / LayoutUnit(-2)).ToInt());
+ EXPECT_EQ(2, (LayoutUnit(-2) / LayoutUnit(-1)).ToInt());
+ EXPECT_EQ(4, (LayoutUnit(-2) / LayoutUnit(-0.5)).ToInt());
+ EXPECT_EQ(0, (LayoutUnit(-0.5) / LayoutUnit(-2)).ToInt());
+ EXPECT_EQ(10, (LayoutUnit(-100) / LayoutUnit(-10)).ToInt());
+ EXPECT_FLOAT_EQ(0.5f, (LayoutUnit(-1) / LayoutUnit(-2)).ToFloat());
+ EXPECT_FLOAT_EQ(0.25f, (LayoutUnit(-0.5) / LayoutUnit(-2)).ToFloat());
+
+ size_t a_hundred_size_t = 100;
+ EXPECT_EQ(50, (LayoutUnit(a_hundred_size_t) / LayoutUnit(2)).ToInt());
+ EXPECT_EQ(25, (a_hundred_size_t / LayoutUnit(4)).ToInt());
+ EXPECT_EQ(4, (LayoutUnit(400) / a_hundred_size_t).ToInt());
+
+ EXPECT_EQ(kIntMaxForLayoutUnit / 2,
+ (LayoutUnit(kIntMaxForLayoutUnit) / LayoutUnit(2)).ToInt());
+ EXPECT_EQ(kIntMaxForLayoutUnit,
+ (LayoutUnit(kIntMaxForLayoutUnit) / LayoutUnit(0.5)).ToInt());
+}
+
+TEST(LayoutUnitTest, LayoutUnitCeil) {
+ EXPECT_EQ(0, LayoutUnit(0).Ceil());
+ EXPECT_EQ(1, LayoutUnit(0.1).Ceil());
+ EXPECT_EQ(1, LayoutUnit(0.5).Ceil());
+ EXPECT_EQ(1, LayoutUnit(0.9).Ceil());
+ EXPECT_EQ(1, LayoutUnit(1.0).Ceil());
+ EXPECT_EQ(2, LayoutUnit(1.1).Ceil());
+
+ EXPECT_EQ(0, LayoutUnit(-0.1).Ceil());
+ EXPECT_EQ(0, LayoutUnit(-0.5).Ceil());
+ EXPECT_EQ(0, LayoutUnit(-0.9).Ceil());
+ EXPECT_EQ(-1, LayoutUnit(-1.0).Ceil());
+
+ EXPECT_EQ(kIntMaxForLayoutUnit, LayoutUnit(kIntMaxForLayoutUnit).Ceil());
+ EXPECT_EQ(kIntMaxForLayoutUnit,
+ (LayoutUnit(kIntMaxForLayoutUnit) - LayoutUnit(0.5)).Ceil());
+ EXPECT_EQ(kIntMaxForLayoutUnit - 1,
+ (LayoutUnit(kIntMaxForLayoutUnit) - LayoutUnit(1)).Ceil());
+
+ EXPECT_EQ(kIntMinForLayoutUnit, LayoutUnit(kIntMinForLayoutUnit).Ceil());
+}
+
+TEST(LayoutUnitTest, LayoutUnitFloor) {
+ EXPECT_EQ(0, LayoutUnit(0).Floor());
+ EXPECT_EQ(0, LayoutUnit(0.1).Floor());
+ EXPECT_EQ(0, LayoutUnit(0.5).Floor());
+ EXPECT_EQ(0, LayoutUnit(0.9).Floor());
+ EXPECT_EQ(1, LayoutUnit(1.0).Floor());
+ EXPECT_EQ(1, LayoutUnit(1.1).Floor());
+
+ EXPECT_EQ(-1, LayoutUnit(-0.1).Floor());
+ EXPECT_EQ(-1, LayoutUnit(-0.5).Floor());
+ EXPECT_EQ(-1, LayoutUnit(-0.9).Floor());
+ EXPECT_EQ(-1, LayoutUnit(-1.0).Floor());
+
+ EXPECT_EQ(kIntMaxForLayoutUnit, LayoutUnit(kIntMaxForLayoutUnit).Floor());
+
+ EXPECT_EQ(kIntMinForLayoutUnit, LayoutUnit(kIntMinForLayoutUnit).Floor());
+ EXPECT_EQ(kIntMinForLayoutUnit,
+ (LayoutUnit(kIntMinForLayoutUnit) + LayoutUnit(0.5)).Floor());
+ EXPECT_EQ(kIntMinForLayoutUnit + 1,
+ (LayoutUnit(kIntMinForLayoutUnit) + LayoutUnit(1)).Floor());
+}
+
+TEST(LayoutUnitTest, LayoutUnitFloatOverflow) {
+ // These should overflow to the max/min according to their sign.
+ EXPECT_EQ(kIntMaxForLayoutUnit, LayoutUnit(176972000.0f).ToInt());
+ EXPECT_EQ(kIntMinForLayoutUnit, LayoutUnit(-176972000.0f).ToInt());
+ EXPECT_EQ(kIntMaxForLayoutUnit, LayoutUnit(176972000.0).ToInt());
+ EXPECT_EQ(kIntMinForLayoutUnit, LayoutUnit(-176972000.0).ToInt());
+}
+
+TEST(LayoutUnitTest, UnaryMinus) {
+ EXPECT_EQ(LayoutUnit(), -LayoutUnit());
+ EXPECT_EQ(LayoutUnit(999), -LayoutUnit(-999));
+ EXPECT_EQ(LayoutUnit(-999), -LayoutUnit(999));
+
+ LayoutUnit negative_max;
+ negative_max.SetRawValue(LayoutUnit::Min().RawValue() + 1);
+ EXPECT_EQ(negative_max, -LayoutUnit::Max());
+ EXPECT_EQ(LayoutUnit::Max(), -negative_max);
+
+ // -LayoutUnit::min() is saturated to LayoutUnit::max()
+ EXPECT_EQ(LayoutUnit::Max(), -LayoutUnit::Min());
+}
+
+TEST(LayoutUnitTest, LayoutUnitPlusPlus) {
+ EXPECT_EQ(LayoutUnit(-1), LayoutUnit(-2)++);
+ EXPECT_EQ(LayoutUnit(0), LayoutUnit(-1)++);
+ EXPECT_EQ(LayoutUnit(1), LayoutUnit(0)++);
+ EXPECT_EQ(LayoutUnit(2), LayoutUnit(1)++);
+ EXPECT_EQ(LayoutUnit::Max(), LayoutUnit(LayoutUnit::Max())++);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/length.cc b/chromium/third_party/blink/renderer/platform/length.cc
new file mode 100644
index 00000000000..6ddb1526742
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/length.cc
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * (C) 2001 Dirk Mueller ( mueller@kde.org )
+ * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights
+ * reserved.
+ * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "third_party/blink/renderer/platform/length.h"
+
+#include "base/macros.h"
+#include "third_party/blink/renderer/platform/animation/animation_utilities.h"
+#include "third_party/blink/renderer/platform/calculation_value.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+
+namespace blink {
+
+class CalculationValueHandleMap {
+ USING_FAST_MALLOC(CalculationValueHandleMap);
+
+ public:
+ CalculationValueHandleMap() : index_(1) {}
+
+ int insert(scoped_refptr<CalculationValue> calc_value) {
+ DCHECK(index_);
+ // FIXME calc(): https://bugs.webkit.org/show_bug.cgi?id=80489
+ // This monotonically increasing handle generation scheme is potentially
+ // wasteful of the handle space. Consider reusing empty handles.
+ while (map_.Contains(index_))
+ index_++;
+
+ map_.Set(index_, std::move(calc_value));
+
+ return index_;
+ }
+
+ void Remove(int index) {
+ DCHECK(map_.Contains(index));
+ map_.erase(index);
+ }
+
+ CalculationValue& Get(int index) {
+ DCHECK(map_.Contains(index));
+ return *map_.at(index);
+ }
+
+ void DecrementRef(int index) {
+ DCHECK(map_.Contains(index));
+ CalculationValue* value = map_.at(index);
+ if (value->HasOneRef()) {
+ // Force the CalculationValue destructor early to avoid a potential
+ // recursive call inside HashMap remove().
+ map_.Set(index, nullptr);
+ map_.erase(index);
+ } else {
+ value->Release();
+ }
+ }
+
+ private:
+ int index_;
+ HashMap<int, scoped_refptr<CalculationValue>> map_;
+
+ DISALLOW_COPY_AND_ASSIGN(CalculationValueHandleMap);
+};
+
+static CalculationValueHandleMap& CalcHandles() {
+ DEFINE_STATIC_LOCAL(CalculationValueHandleMap, handle_map, ());
+ return handle_map;
+}
+
+Length::Length(scoped_refptr<CalculationValue> calc)
+ : quirk_(false), type_(kCalculated), is_float_(false) {
+ int_value_ = CalcHandles().insert(std::move(calc));
+}
+
+Length Length::BlendMixedTypes(const Length& from,
+ double progress,
+ ValueRange range) const {
+ DCHECK(from.IsSpecified());
+ DCHECK(IsSpecified());
+ PixelsAndPercent from_pixels_and_percent = from.GetPixelsAndPercent();
+ PixelsAndPercent to_pixels_and_percent = GetPixelsAndPercent();
+ const float pixels = blink::Blend(from_pixels_and_percent.pixels,
+ to_pixels_and_percent.pixels, progress);
+ const float percent = blink::Blend(from_pixels_and_percent.percent,
+ to_pixels_and_percent.percent, progress);
+ return Length(
+ CalculationValue::Create(PixelsAndPercent(pixels, percent), range));
+}
+
+Length Length::BlendSameTypes(const Length& from,
+ double progress,
+ ValueRange range) const {
+ LengthType result_type = GetType();
+ if (IsZero())
+ result_type = from.GetType();
+
+ float blended_value = blink::Blend(from.Value(), Value(), progress);
+ if (range == kValueRangeNonNegative)
+ blended_value = clampTo<float>(blended_value, 0);
+ return Length(blended_value, result_type);
+}
+
+PixelsAndPercent Length::GetPixelsAndPercent() const {
+ switch (GetType()) {
+ case kFixed:
+ return PixelsAndPercent(Value(), 0);
+ case kPercent:
+ return PixelsAndPercent(0, Value());
+ case kCalculated:
+ return GetCalculationValue().GetPixelsAndPercent();
+ default:
+ NOTREACHED();
+ return PixelsAndPercent(0, 0);
+ }
+}
+
+Length Length::SubtractFromOneHundredPercent() const {
+ PixelsAndPercent result = GetPixelsAndPercent();
+ result.pixels = -result.pixels;
+ result.percent = 100 - result.percent;
+ if (result.pixels && result.percent)
+ return Length(CalculationValue::Create(result, kValueRangeAll));
+ if (result.percent)
+ return Length(result.percent, kPercent);
+ return Length(result.pixels, kFixed);
+}
+
+Length Length::Zoom(double factor) const {
+ switch (GetType()) {
+ case kFixed:
+ return Length(GetFloatValue() * factor, kFixed);
+ case kCalculated: {
+ PixelsAndPercent result = GetPixelsAndPercent();
+ result.pixels *= factor;
+ return Length(CalculationValue::Create(
+ result, GetCalculationValue().GetValueRange()));
+ }
+ default:
+ return *this;
+ }
+}
+
+CalculationValue& Length::GetCalculationValue() const {
+ DCHECK(IsCalculated());
+ return CalcHandles().Get(CalculationHandle());
+}
+
+void Length::IncrementCalculatedRef() const {
+ DCHECK(IsCalculated());
+ GetCalculationValue().AddRef();
+}
+
+void Length::DecrementCalculatedRef() const {
+ DCHECK(IsCalculated());
+ CalcHandles().DecrementRef(CalculationHandle());
+}
+
+float Length::NonNanCalculatedValue(LayoutUnit max_value) const {
+ DCHECK(IsCalculated());
+ float result = GetCalculationValue().Evaluate(max_value.ToFloat());
+ if (std::isnan(result))
+ return 0;
+ return result;
+}
+
+bool Length::IsCalculatedEqual(const Length& o) const {
+ return IsCalculated() &&
+ (&GetCalculationValue() == &o.GetCalculationValue() ||
+ GetCalculationValue() == o.GetCalculationValue());
+}
+
+struct SameSizeAsLength {
+ int32_t value;
+ int32_t meta_data;
+};
+static_assert(sizeof(Length) == sizeof(SameSizeAsLength),
+ "length should stay small");
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/length.h b/chromium/third_party/blink/renderer/platform/length.h
new file mode 100644
index 00000000000..32ab8ce550f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/length.h
@@ -0,0 +1,308 @@
+/*
+ Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
+ Copyright (C) 2011 Rik Cabanier (cabanier@adobe.com)
+ Copyright (C) 2011 Adobe Systems Incorporated. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LENGTH_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LENGTH_H_
+
+#include "third_party/blink/renderer/platform/layout_unit.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+// FIXME: This enum makes it hard to tell in general what values may be
+// appropriate for any given Length.
+enum LengthType {
+ kAuto,
+ kPercent,
+ kFixed,
+ kMinContent,
+ kMaxContent,
+ kFillAvailable,
+ kFitContent,
+ kCalculated,
+ kExtendToZoom,
+ kDeviceWidth,
+ kDeviceHeight,
+ kMaxSizeNone
+};
+
+enum ValueRange { kValueRangeAll, kValueRangeNonNegative };
+
+struct PixelsAndPercent {
+ DISALLOW_NEW();
+ PixelsAndPercent(float pixels, float percent)
+ : pixels(pixels), percent(percent) {}
+ float pixels;
+ float percent;
+};
+
+class CalculationValue;
+
+class PLATFORM_EXPORT Length {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ Length() : int_value_(0), quirk_(false), type_(kAuto), is_float_(false) {}
+
+ explicit Length(LengthType t)
+ : int_value_(0), quirk_(false), type_(t), is_float_(false) {
+ DCHECK_NE(t, kCalculated);
+ }
+
+ Length(int v, LengthType t, bool q = false)
+ : int_value_(v), quirk_(q), type_(t), is_float_(false) {
+ DCHECK_NE(t, kCalculated);
+ }
+
+ Length(LayoutUnit v, LengthType t, bool q = false)
+ : float_value_(v.ToFloat()), quirk_(q), type_(t), is_float_(true) {
+ DCHECK_NE(t, kCalculated);
+ }
+
+ Length(float v, LengthType t, bool q = false)
+ : float_value_(v), quirk_(q), type_(t), is_float_(true) {
+ DCHECK_NE(t, kCalculated);
+ }
+
+ Length(double v, LengthType t, bool q = false)
+ : quirk_(q), type_(t), is_float_(true) {
+ float_value_ = static_cast<float>(v);
+ }
+
+ explicit Length(scoped_refptr<CalculationValue>);
+
+ Length(const Length& length) {
+ memcpy(this, &length, sizeof(Length));
+ if (IsCalculated())
+ IncrementCalculatedRef();
+ }
+
+ Length& operator=(const Length& length) {
+ if (length.IsCalculated())
+ length.IncrementCalculatedRef();
+ if (IsCalculated())
+ DecrementCalculatedRef();
+ memcpy(this, &length, sizeof(Length));
+ return *this;
+ }
+
+ ~Length() {
+ if (IsCalculated())
+ DecrementCalculatedRef();
+ }
+
+ bool operator==(const Length& o) const {
+ return (type_ == o.type_) && (quirk_ == o.quirk_) &&
+ (IsMaxSizeNone() || (GetFloatValue() == o.GetFloatValue()) ||
+ IsCalculatedEqual(o));
+ }
+ bool operator!=(const Length& o) const { return !(*this == o); }
+
+ const Length& operator*=(float v) {
+ if (IsCalculated()) {
+ NOTREACHED();
+ return *this;
+ }
+
+ if (is_float_)
+ float_value_ = static_cast<float>(float_value_ * v);
+ else
+ int_value_ = static_cast<int>(int_value_ * v);
+
+ return *this;
+ }
+
+ // FIXME: Make this private (if possible) or at least rename it
+ // (http://crbug.com/432707).
+ inline float Value() const {
+ DCHECK(!IsCalculated());
+ return GetFloatValue();
+ }
+
+ int IntValue() const {
+ if (IsCalculated()) {
+ NOTREACHED();
+ return 0;
+ }
+ return GetIntValue();
+ }
+
+ float Pixels() const {
+ DCHECK_EQ(GetType(), kFixed);
+ return GetFloatValue();
+ }
+
+ float Percent() const {
+ DCHECK_EQ(GetType(), kPercent);
+ return GetFloatValue();
+ }
+
+ PixelsAndPercent GetPixelsAndPercent() const;
+
+ CalculationValue& GetCalculationValue() const;
+
+ LengthType GetType() const { return static_cast<LengthType>(type_); }
+ bool Quirk() const { return quirk_; }
+
+ void SetQuirk(bool quirk) { quirk_ = quirk; }
+
+ void SetValue(LengthType t, int value) {
+ type_ = t;
+ int_value_ = value;
+ is_float_ = false;
+ }
+
+ void SetValue(int value) {
+ if (IsCalculated()) {
+ NOTREACHED();
+ return;
+ }
+ SetValue(kFixed, value);
+ }
+
+ void SetValue(LengthType t, float value) {
+ type_ = t;
+ float_value_ = value;
+ is_float_ = true;
+ }
+
+ void SetValue(LengthType t, LayoutUnit value) {
+ type_ = t;
+ float_value_ = value.ToFloat();
+ is_float_ = true;
+ }
+
+ void SetValue(float value) { *this = Length(value, kFixed); }
+
+ bool IsMaxSizeNone() const { return GetType() == kMaxSizeNone; }
+
+ // FIXME calc: https://bugs.webkit.org/show_bug.cgi?id=80357. A calculated
+ // Length always contains a percentage, and without a maxValue passed to these
+ // functions it's impossible to determine the sign or zero-ness. We assume all
+ // calc values are positive and non-zero for now.
+ bool IsZero() const {
+ DCHECK(!IsMaxSizeNone());
+ if (IsCalculated())
+ return false;
+
+ return is_float_ ? !float_value_ : !int_value_;
+ }
+ bool IsPositive() const {
+ if (IsMaxSizeNone())
+ return false;
+ if (IsCalculated())
+ return true;
+
+ return GetFloatValue() > 0;
+ }
+ bool IsNegative() const {
+ if (IsMaxSizeNone() || IsCalculated())
+ return false;
+
+ return GetFloatValue() < 0;
+ }
+
+ bool IsAuto() const { return GetType() == kAuto; }
+ bool IsFixed() const { return GetType() == kFixed; }
+ bool IsIntrinsicOrAuto() const { return GetType() == kAuto || IsIntrinsic(); }
+ bool IsIntrinsic() const {
+ return GetType() == kMinContent || GetType() == kMaxContent ||
+ GetType() == kFillAvailable || GetType() == kFitContent;
+ }
+ bool IsSpecified() const {
+ return GetType() == kFixed || GetType() == kPercent ||
+ GetType() == kCalculated;
+ }
+ bool IsSpecifiedOrIntrinsic() const { return IsSpecified() || IsIntrinsic(); }
+ bool IsCalculated() const { return GetType() == kCalculated; }
+ bool IsCalculatedEqual(const Length&) const;
+ bool IsMinContent() const { return GetType() == kMinContent; }
+ bool IsMaxContent() const { return GetType() == kMaxContent; }
+ bool IsFillAvailable() const { return GetType() == kFillAvailable; }
+ bool IsFitContent() const { return GetType() == kFitContent; }
+ bool IsPercent() const { return GetType() == kPercent; }
+ bool IsPercentOrCalc() const {
+ return GetType() == kPercent || GetType() == kCalculated;
+ }
+
+ Length Blend(const Length& from, double progress, ValueRange range) const {
+ DCHECK(IsSpecified());
+ DCHECK(from.IsSpecified());
+
+ if (progress == 0.0)
+ return from;
+
+ if (progress == 1.0)
+ return *this;
+
+ if (from.GetType() == kCalculated || GetType() == kCalculated)
+ return BlendMixedTypes(from, progress, range);
+
+ if (!from.IsZero() && !IsZero() && from.GetType() != GetType())
+ return BlendMixedTypes(from, progress, range);
+
+ if (from.IsZero() && IsZero())
+ return *this;
+
+ return BlendSameTypes(from, progress, range);
+ }
+
+ float GetFloatValue() const {
+ DCHECK(!IsMaxSizeNone());
+ return is_float_ ? float_value_ : int_value_;
+ }
+ float NonNanCalculatedValue(LayoutUnit max_value) const;
+
+ Length SubtractFromOneHundredPercent() const;
+
+ Length Zoom(double factor) const;
+
+ private:
+ int GetIntValue() const {
+ DCHECK(!IsMaxSizeNone());
+ return is_float_ ? static_cast<int>(float_value_) : int_value_;
+ }
+
+ Length BlendMixedTypes(const Length& from, double progress, ValueRange) const;
+
+ Length BlendSameTypes(const Length& from, double progress, ValueRange) const;
+
+ int CalculationHandle() const {
+ DCHECK(IsCalculated());
+ return GetIntValue();
+ }
+ void IncrementCalculatedRef() const;
+ void DecrementCalculatedRef() const;
+
+ union {
+ int int_value_;
+ float float_value_;
+ };
+ bool quirk_;
+ unsigned char type_;
+ bool is_float_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LENGTH_H_
diff --git a/chromium/third_party/blink/renderer/platform/length_box.h b/chromium/third_party/blink/renderer/platform/length_box.h
new file mode 100644
index 00000000000..62e23d47f33
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/length_box.h
@@ -0,0 +1,82 @@
+/*
+ Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
+ Copyright (c) 2012, Google Inc. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LENGTH_BOX_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LENGTH_BOX_H_
+
+#include "third_party/blink/renderer/platform/length.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/text/text_direction.h"
+#include "third_party/blink/renderer/platform/text/writing_mode.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT LengthBox {
+ DISALLOW_NEW();
+
+ public:
+ LengthBox() = default;
+
+ LengthBox(LengthType t) : left_(t), right_(t), top_(t), bottom_(t) {}
+
+ LengthBox(int v)
+ : left_(Length(v, kFixed)),
+ right_(Length(v, kFixed)),
+ top_(Length(v, kFixed)),
+ bottom_(Length(v, kFixed)) {}
+
+ LengthBox(const Length& t, const Length& r, const Length& b, const Length& l)
+ : left_(l), right_(r), top_(t), bottom_(b) {}
+
+ LengthBox(int t, int r, int b, int l)
+ : left_(Length(l, kFixed)),
+ right_(Length(r, kFixed)),
+ top_(Length(t, kFixed)),
+ bottom_(Length(b, kFixed)) {}
+
+ const Length& Left() const { return left_; }
+ const Length& Right() const { return right_; }
+ const Length& Top() const { return top_; }
+ const Length& Bottom() const { return bottom_; }
+
+ bool operator==(const LengthBox& o) const {
+ return left_ == o.left_ && right_ == o.right_ && top_ == o.top_ &&
+ bottom_ == o.bottom_;
+ }
+
+ bool operator!=(const LengthBox& o) const { return !(*this == o); }
+
+ bool NonZero() const {
+ return !(left_.IsZero() && right_.IsZero() && top_.IsZero() &&
+ bottom_.IsZero());
+ }
+
+ // Must be public for SET_VAR in ComputedStyle.h
+ Length left_;
+ Length right_;
+ Length top_;
+ Length bottom_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LENGTH_BOX_H_
diff --git a/chromium/third_party/blink/renderer/platform/length_functions.cc b/chromium/third_party/blink/renderer/platform/length_functions.cc
new file mode 100644
index 00000000000..0fa4f38ba82
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/length_functions.cc
@@ -0,0 +1,136 @@
+/*
+ Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
+ Copyright (C) 2011 Rik Cabanier (cabanier@adobe.com)
+ Copyright (C) 2011 Adobe Systems Incorporated. All rights reserved.
+ Copyright (C) 2012 Motorola Mobility, Inc. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "third_party/blink/renderer/platform/length_functions.h"
+
+#include "third_party/blink/renderer/platform/geometry/float_point.h"
+#include "third_party/blink/renderer/platform/geometry/float_size.h"
+#include "third_party/blink/renderer/platform/layout_unit.h"
+#include "third_party/blink/renderer/platform/length_point.h"
+#include "third_party/blink/renderer/platform/length_size.h"
+
+namespace blink {
+
+int IntValueForLength(const Length& length, int maximum_value) {
+ return ValueForLength(length, LayoutUnit(maximum_value)).ToInt();
+}
+
+float FloatValueForLength(const Length& length, float maximum_value) {
+ switch (length.GetType()) {
+ case kFixed:
+ return length.GetFloatValue();
+ case kPercent:
+ return static_cast<float>(maximum_value * length.Percent() / 100.0f);
+ case kFillAvailable:
+ case kAuto:
+ return static_cast<float>(maximum_value);
+ case kCalculated:
+ return length.NonNanCalculatedValue(LayoutUnit(maximum_value));
+ case kMinContent:
+ case kMaxContent:
+ case kFitContent:
+ case kExtendToZoom:
+ case kDeviceWidth:
+ case kDeviceHeight:
+ case kMaxSizeNone:
+ NOTREACHED();
+ return 0;
+ }
+ NOTREACHED();
+ return 0;
+}
+
+LayoutUnit MinimumValueForLength(const Length& length,
+ LayoutUnit maximum_value) {
+ switch (length.GetType()) {
+ case kFixed:
+ return LayoutUnit(length.Value());
+ case kPercent:
+ // Don't remove the extra cast to float. It is needed for rounding on
+ // 32-bit Intel machines that use the FPU stack.
+ return LayoutUnit(
+ static_cast<float>(maximum_value * length.Percent() / 100.0f));
+ case kCalculated:
+ return LayoutUnit(length.NonNanCalculatedValue(maximum_value));
+ case kFillAvailable:
+ case kAuto:
+ return LayoutUnit();
+ case kMinContent:
+ case kMaxContent:
+ case kFitContent:
+ case kExtendToZoom:
+ case kDeviceWidth:
+ case kDeviceHeight:
+ case kMaxSizeNone:
+ NOTREACHED();
+ return LayoutUnit();
+ }
+ NOTREACHED();
+ return LayoutUnit();
+}
+
+LayoutUnit RoundedMinimumValueForLength(const Length& length,
+ LayoutUnit maximum_value) {
+ if (length.GetType() == kPercent)
+ return static_cast<LayoutUnit>(
+ round(maximum_value * length.Percent() / 100.0f));
+ return MinimumValueForLength(length, maximum_value);
+}
+
+LayoutUnit ValueForLength(const Length& length, LayoutUnit maximum_value) {
+ switch (length.GetType()) {
+ case kFixed:
+ case kPercent:
+ case kCalculated:
+ return MinimumValueForLength(length, maximum_value);
+ case kFillAvailable:
+ case kAuto:
+ return maximum_value;
+ case kMinContent:
+ case kMaxContent:
+ case kFitContent:
+ case kExtendToZoom:
+ case kDeviceWidth:
+ case kDeviceHeight:
+ case kMaxSizeNone:
+ NOTREACHED();
+ return LayoutUnit();
+ }
+ NOTREACHED();
+ return LayoutUnit();
+}
+
+FloatSize FloatSizeForLengthSize(const LengthSize& length_size,
+ const FloatSize& box_size) {
+ return FloatSize(
+ FloatValueForLength(length_size.Width(), box_size.Width()),
+ FloatValueForLength(length_size.Height(), box_size.Height()));
+}
+
+FloatPoint FloatPointForLengthPoint(const LengthPoint& length_point,
+ const FloatSize& box_size) {
+ return FloatPoint(FloatValueForLength(length_point.X(), box_size.Width()),
+ FloatValueForLength(length_point.Y(), box_size.Height()));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/length_functions.h b/chromium/third_party/blink/renderer/platform/length_functions.h
new file mode 100644
index 00000000000..8d1626f3775
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/length_functions.h
@@ -0,0 +1,54 @@
+/*
+ Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
+ Copyright (C) 2011 Rik Cabanier (cabanier@adobe.com)
+ Copyright (C) 2011 Adobe Systems Incorporated. All rights reserved.
+ Copyright (C) 2012 Motorola Mobility, Inc. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LENGTH_FUNCTIONS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LENGTH_FUNCTIONS_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+
+class FloatPoint;
+class FloatSize;
+class LayoutUnit;
+class Length;
+class LengthSize;
+
+struct LengthPoint;
+
+PLATFORM_EXPORT int IntValueForLength(const Length&, int maximum_value);
+PLATFORM_EXPORT float FloatValueForLength(const Length&, float maximum_value);
+PLATFORM_EXPORT LayoutUnit MinimumValueForLength(const Length&,
+ LayoutUnit maximum_value);
+PLATFORM_EXPORT LayoutUnit
+RoundedMinimumValueForLength(const Length&, LayoutUnit maximum_value);
+PLATFORM_EXPORT LayoutUnit ValueForLength(const Length&,
+ LayoutUnit maximum_value);
+PLATFORM_EXPORT FloatSize FloatSizeForLengthSize(const LengthSize&,
+ const FloatSize& box_size);
+PLATFORM_EXPORT FloatPoint FloatPointForLengthPoint(const LengthPoint&,
+ const FloatSize& box_size);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LENGTH_FUNCTIONS_H_
diff --git a/chromium/third_party/blink/renderer/platform/length_point.h b/chromium/third_party/blink/renderer/platform/length_point.h
new file mode 100644
index 00000000000..88fde56cd09
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/length_point.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2013, Opera Software ASA. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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 Opera Software ASA 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 HOLDER 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_LENGTH_POINT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LENGTH_POINT_H_
+
+#include "third_party/blink/renderer/platform/length.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+struct LengthPoint {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ LengthPoint() = default;
+
+ LengthPoint(const Length& x, const Length& y) : x_(x), y_(y) {}
+
+ bool operator==(const LengthPoint& o) const {
+ return x_ == o.x_ && y_ == o.y_;
+ }
+ bool operator!=(const LengthPoint& o) const {
+ return x_ != o.x_ || y_ != o.y_;
+ }
+
+ void SetX(const Length& x) { x_ = x; }
+ const Length& X() const { return x_; }
+
+ void SetY(const Length& y) { y_ = y; }
+ const Length& Y() const { return y_; }
+
+ private:
+ Length x_;
+ Length y_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LENGTH_POINT_H_
diff --git a/chromium/third_party/blink/renderer/platform/length_size.h b/chromium/third_party/blink/renderer/platform/length_size.h
new file mode 100644
index 00000000000..bafdfa7c211
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/length_size.h
@@ -0,0 +1,55 @@
+/*
+ Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LENGTH_SIZE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LENGTH_SIZE_H_
+
+#include "third_party/blink/renderer/platform/length.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+class LengthSize {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ LengthSize() = default;
+
+ LengthSize(const Length& width, const Length& height)
+ : width_(width), height_(height) {}
+
+ bool operator==(const LengthSize& o) const {
+ return width_ == o.width_ && height_ == o.height_;
+ }
+
+ void SetWidth(const Length& width) { width_ = width; }
+ const Length& Width() const { return width_; }
+
+ void SetHeight(const Length& height) { height_ = height; }
+ const Length& Height() const { return height_; }
+
+ private:
+ Length width_;
+ Length height_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LENGTH_SIZE_H_
diff --git a/chromium/third_party/blink/renderer/platform/lifecycle_context_test.cc b/chromium/third_party/blink/renderer/platform/lifecycle_context_test.cc
new file mode 100644
index 00000000000..1bb2403a810
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/lifecycle_context_test.cc
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2013 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/lifecycle_notifier.h"
+#include "third_party/blink/renderer/platform/lifecycle_observer.h"
+
+namespace blink {
+
+class TestingObserver;
+
+class DummyContext final
+ : public GarbageCollectedFinalized<DummyContext>,
+ public LifecycleNotifier<DummyContext, TestingObserver> {
+ USING_GARBAGE_COLLECTED_MIXIN(DummyContext);
+
+ public:
+ static DummyContext* Create() { return new DummyContext; }
+
+ void Trace(blink::Visitor* visitor) {
+ LifecycleNotifier<DummyContext, TestingObserver>::Trace(visitor);
+ }
+};
+
+class TestingObserver final
+ : public GarbageCollected<TestingObserver>,
+ public LifecycleObserver<DummyContext, TestingObserver> {
+ USING_GARBAGE_COLLECTED_MIXIN(TestingObserver);
+
+ public:
+ static TestingObserver* Create(DummyContext* context) {
+ return new TestingObserver(context);
+ }
+
+ void ContextDestroyed(DummyContext* destroyed_context) {
+ if (observer_to_remove_on_destruct_) {
+ destroyed_context->RemoveObserver(observer_to_remove_on_destruct_);
+ observer_to_remove_on_destruct_.Clear();
+ }
+ context_destroyed_called_ = true;
+ }
+
+ void Trace(blink::Visitor* visitor) {
+ visitor->Trace(observer_to_remove_on_destruct_);
+ LifecycleObserver::Trace(visitor);
+ }
+
+ void Unobserve() { SetContext(nullptr); }
+
+ void SetObserverToRemoveAndDestroy(
+ TestingObserver* observer_to_remove_on_destruct) {
+ DCHECK(!observer_to_remove_on_destruct_);
+ observer_to_remove_on_destruct_ = observer_to_remove_on_destruct;
+ }
+
+ TestingObserver* InnerObserver() const {
+ return observer_to_remove_on_destruct_;
+ }
+ bool ContextDestroyedCalled() const { return context_destroyed_called_; }
+
+ private:
+ explicit TestingObserver(DummyContext* context)
+ : LifecycleObserver(context), context_destroyed_called_(false) {}
+
+ Member<TestingObserver> observer_to_remove_on_destruct_;
+ bool context_destroyed_called_;
+};
+
+TEST(LifecycleContextTest, shouldObserveContextDestroyed) {
+ DummyContext* context = DummyContext::Create();
+ Persistent<TestingObserver> observer = TestingObserver::Create(context);
+
+ EXPECT_EQ(observer->LifecycleContext(), context);
+ EXPECT_FALSE(observer->ContextDestroyedCalled());
+ context->NotifyContextDestroyed();
+ context = nullptr;
+ ThreadState::Current()->CollectAllGarbage();
+ EXPECT_EQ(observer->LifecycleContext(), static_cast<DummyContext*>(nullptr));
+ EXPECT_TRUE(observer->ContextDestroyedCalled());
+}
+
+TEST(LifecycleContextTest, shouldNotObserveContextDestroyedIfUnobserve) {
+ DummyContext* context = DummyContext::Create();
+ Persistent<TestingObserver> observer = TestingObserver::Create(context);
+ observer->Unobserve();
+ context->NotifyContextDestroyed();
+ context = nullptr;
+ ThreadState::Current()->CollectAllGarbage();
+ EXPECT_EQ(observer->LifecycleContext(), static_cast<DummyContext*>(nullptr));
+ EXPECT_FALSE(observer->ContextDestroyedCalled());
+}
+
+TEST(LifecycleContextTest, observerRemovedDuringNotifyDestroyed) {
+ DummyContext* context = DummyContext::Create();
+ Persistent<TestingObserver> observer = TestingObserver::Create(context);
+ TestingObserver* inner_observer = TestingObserver::Create(context);
+ // Attach the observer to the other. When 'observer' is notified
+ // of destruction, it will remove & destroy 'innerObserver'.
+ observer->SetObserverToRemoveAndDestroy(inner_observer);
+
+ EXPECT_EQ(observer->LifecycleContext(), context);
+ EXPECT_EQ(observer->InnerObserver()->LifecycleContext(), context);
+ EXPECT_FALSE(observer->ContextDestroyedCalled());
+ EXPECT_FALSE(observer->InnerObserver()->ContextDestroyedCalled());
+
+ context->NotifyContextDestroyed();
+ EXPECT_EQ(observer->InnerObserver(), nullptr);
+ context = nullptr;
+ ThreadState::Current()->CollectAllGarbage();
+ EXPECT_EQ(observer->LifecycleContext(), static_cast<DummyContext*>(nullptr));
+ EXPECT_TRUE(observer->ContextDestroyedCalled());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/lifecycle_notifier.h b/chromium/third_party/blink/renderer/platform/lifecycle_notifier.h
new file mode 100644
index 00000000000..97ab7bc7616
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/lifecycle_notifier.h
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ * Copyright (C) 2013 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_LIFECYCLE_NOTIFIER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LIFECYCLE_NOTIFIER_H_
+
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/wtf/auto_reset.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+
+namespace blink {
+
+template <typename T, typename Observer>
+class LifecycleNotifier : public GarbageCollectedMixin {
+ public:
+ virtual ~LifecycleNotifier();
+
+ void AddObserver(Observer*);
+ void RemoveObserver(Observer*);
+
+ // notifyContextDestroyed() should be explicitly dispatched from an
+ // observed context to detach its observers, and if the observer kind
+ // requires it, notify each observer by invoking contextDestroyed().
+ //
+ // When contextDestroyed() is called, it is supplied the context as
+ // an argument, but the observer's lifecycleContext() is still valid
+ // and safe to use while handling the notification.
+ virtual void NotifyContextDestroyed();
+
+ virtual void Trace(blink::Visitor* visitor) { visitor->Trace(observers_); }
+
+ bool IsIteratingOverObservers() const {
+ return iteration_state_ != kNotIterating;
+ }
+
+ protected:
+ LifecycleNotifier() : iteration_state_(kNotIterating) {}
+
+ T* Context() { return static_cast<T*>(this); }
+
+ using ObserverSet = HeapHashSet<WeakMember<Observer>>;
+
+ enum IterationState {
+ kAllowingNone = 0,
+ kAllowingAddition = 1,
+ kAllowingRemoval = 2,
+ kNotIterating = kAllowingAddition | kAllowingRemoval,
+ kAllowPendingRemoval = 4,
+ };
+
+ // Iteration state is recorded while iterating the observer set,
+ // optionally barring add or remove mutations.
+ IterationState iteration_state_;
+ ObserverSet observers_;
+};
+
+template <typename T, typename Observer>
+inline LifecycleNotifier<T, Observer>::~LifecycleNotifier() {
+ // FIXME: Enable the following ASSERT. Also see a FIXME in
+ // Document::detachLayoutTree().
+ // DCHECK(!m_observers.size());
+}
+
+// Determine if |contextDestroyed(Observer*) is a public method on
+// class type |Observer|, or any of the class types it derives from.
+template <typename Observer, typename T>
+class HasContextDestroyed {
+ using YesType = char;
+ using NoType = int;
+
+ template <typename V>
+ static YesType CheckHasContextDestroyedMethod(
+ V* observer,
+ T* context = nullptr,
+ typename std::enable_if<
+ std::is_same<decltype(observer->ContextDestroyed(context)),
+ void>::value>::type* g = nullptr);
+ template <typename V>
+ static NoType CheckHasContextDestroyedMethod(...);
+
+ public:
+ static_assert(sizeof(Observer), "Observer's class declaration not in scope");
+ static const bool value =
+ sizeof(YesType) ==
+ sizeof(CheckHasContextDestroyedMethod<Observer>(nullptr));
+};
+
+// If |Observer::contextDestroyed()| is present, invoke it.
+template <typename Observer,
+ typename T,
+ bool = HasContextDestroyed<Observer, T>::value>
+class ContextDestroyedNotifier {
+ STATIC_ONLY(ContextDestroyedNotifier);
+
+ public:
+ static void Call(Observer* observer, T* context) {
+ observer->ContextDestroyed(context);
+ }
+};
+
+template <typename Observer, typename T>
+class ContextDestroyedNotifier<Observer, T, false> {
+ STATIC_ONLY(ContextDestroyedNotifier);
+
+ public:
+ static void Call(Observer*, T*) {}
+};
+
+template <typename T, typename Observer>
+inline void LifecycleNotifier<T, Observer>::NotifyContextDestroyed() {
+ // Observer unregistration is allowed, but effectively a no-op.
+ AutoReset<IterationState> scope(&iteration_state_, kAllowingRemoval);
+ ObserverSet observers;
+ observers_.swap(observers);
+ for (Observer* observer : observers) {
+ DCHECK(observer->LifecycleContext() == Context());
+ ContextDestroyedNotifier<Observer, T>::Call(observer, Context());
+ observer->ClearContext();
+ }
+}
+
+template <typename T, typename Observer>
+inline void LifecycleNotifier<T, Observer>::AddObserver(Observer* observer) {
+ CHECK(iteration_state_ & kAllowingAddition);
+ observers_.insert(observer);
+}
+
+template <typename T, typename Observer>
+inline void LifecycleNotifier<T, Observer>::RemoveObserver(Observer* observer) {
+ // If immediate removal isn't currently allowed,
+ // |observer| is recorded for pending removal.
+ if (iteration_state_ & kAllowPendingRemoval) {
+ observers_.insert(observer);
+ return;
+ }
+ CHECK(iteration_state_ & kAllowingRemoval);
+ observers_.erase(observer);
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LIFECYCLE_NOTIFIER_H_
diff --git a/chromium/third_party/blink/renderer/platform/lifecycle_observer.h b/chromium/third_party/blink/renderer/platform/lifecycle_observer.h
new file mode 100644
index 00000000000..9b72fba6169
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/lifecycle_observer.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2008 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_LIFECYCLE_OBSERVER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LIFECYCLE_OBSERVER_H_
+
+#include "third_party/blink/renderer/platform/heap/handle.h"
+
+namespace blink {
+
+template <typename Context, typename Observer>
+class LifecycleNotifier;
+
+template <typename Context, typename Observer>
+class LifecycleObserver : public GarbageCollectedMixin {
+ public:
+ virtual void Trace(blink::Visitor* visitor) {
+ visitor->Trace(lifecycle_context_);
+ }
+
+ Context* LifecycleContext() const { return lifecycle_context_; }
+
+ void ClearContext() { SetContext(nullptr); }
+
+ protected:
+ explicit LifecycleObserver(Context* context) : lifecycle_context_(nullptr) {
+ SetContext(context);
+ }
+
+ void SetContext(Context*);
+
+ private:
+ WeakMember<Context> lifecycle_context_;
+};
+
+template <typename Context, typename Observer>
+inline void LifecycleObserver<Context, Observer>::SetContext(Context* context) {
+ using Notifier = LifecycleNotifier<Context, Observer>;
+
+ if (lifecycle_context_ == context)
+ return;
+
+ if (lifecycle_context_) {
+ static_cast<Notifier*>(lifecycle_context_)
+ ->RemoveObserver(static_cast<Observer*>(this));
+ }
+
+ lifecycle_context_ = context;
+
+ if (lifecycle_context_) {
+ static_cast<Notifier*>(lifecycle_context_)
+ ->AddObserver(static_cast<Observer*>(this));
+ }
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LIFECYCLE_OBSERVER_H_
diff --git a/chromium/third_party/blink/renderer/platform/link_hash.cc b/chromium/third_party/blink/renderer/platform/link_hash.cc
new file mode 100644
index 00000000000..17328c195dc
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/link_hash.cc
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2008, 2009, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/renderer/platform/link_hash.h"
+
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
+#include "url/url_util.h"
+
+namespace blink {
+
+static bool ResolveRelative(const KURL& base,
+ const String& relative,
+ url::RawCanonOutput<2048>* buffer) {
+ // We use these low-level GURL functions to avoid converting back and forth
+ // from UTF-8 unnecessarily.
+ url::Parsed parsed;
+ StringUTF8Adaptor base_utf8(base.GetString());
+ if (relative.Is8Bit()) {
+ StringUTF8Adaptor relative_utf8(relative);
+ return url::ResolveRelative(
+ base_utf8.Data(), base_utf8.length(), base.GetParsed(),
+ relative_utf8.Data(), relative_utf8.length(), nullptr, buffer, &parsed);
+ }
+ return url::ResolveRelative(base_utf8.Data(), base_utf8.length(),
+ base.GetParsed(), relative.Characters16(),
+ relative.length(), nullptr, buffer, &parsed);
+}
+
+LinkHash VisitedLinkHash(const KURL& base, const AtomicString& relative) {
+ if (relative.IsNull())
+ return 0;
+ url::RawCanonOutput<2048> buffer;
+ if (!ResolveRelative(base, relative.GetString(), &buffer))
+ return 0;
+ return Platform::Current()->VisitedLinkHash(buffer.data(), buffer.length());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/link_hash.h b/chromium/third_party/blink/renderer/platform/link_hash.h
new file mode 100644
index 00000000000..a8097f040fb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/link_hash.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_LINK_HASH_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LINK_HASH_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+
+namespace blink {
+
+class KURL;
+
+typedef uint64_t LinkHash;
+
+// Use the low 32-bits of the 64-bit LinkHash as the key for HashSets.
+struct LinkHashHash {
+ STATIC_ONLY(LinkHashHash);
+ static unsigned GetHash(LinkHash key) { return static_cast<unsigned>(key); }
+ static bool Equal(LinkHash a, LinkHash b) { return a == b; }
+ static const bool safe_to_compare_to_empty_or_deleted = true;
+
+ // See AlreadyHashed::avoidDeletedValue.
+ static unsigned AvoidDeletedValue(LinkHash hash64) {
+ DCHECK(hash64);
+ unsigned hash = static_cast<unsigned>(hash64);
+ unsigned new_hash = hash | (!(hash + 1) << 31);
+ DCHECK(new_hash);
+ DCHECK_NE(new_hash, 0xFFFFFFFF);
+ return new_hash;
+ }
+};
+
+// Resolves the potentially relative URL "attributeURL" relative to the given
+// base URL, and returns the hash of the string that will be used for visited
+// link coloring. It will return the special value of 0 if attributeURL does not
+// look like a relative URL.
+PLATFORM_EXPORT LinkHash VisitedLinkHash(const KURL& base,
+ const AtomicString& attribute_url);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LINK_HASH_H_
diff --git a/chromium/third_party/blink/renderer/platform/loader/BUILD.gn b/chromium/third_party/blink/renderer/platform/loader/BUILD.gn
new file mode 100644
index 00000000000..0ce462242b6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/BUILD.gn
@@ -0,0 +1,164 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/jumbo.gni")
+import("//third_party/blink/renderer/build/scripts/scripts.gni")
+import("//third_party/blink/renderer/platform/platform.gni")
+import("//third_party/blink/renderer/platform/platform_generated.gni")
+
+make_names("make_platform_loader_generated_fetch_initiator_type_names") {
+ in_files = [ "fetch/fetch_initiator_type_names.json5" ]
+ output_dir = "$blink_platform_output_dir/loader/fetch"
+}
+
+blink_platform_sources("loader") {
+ sources = [
+ "cors/cors.cc",
+ "cors/cors.h",
+ "cors/cors_error_string.cc",
+ "cors/cors_error_string.h",
+ "fetch/access_control_status.h",
+ "fetch/buffering_data_pipe_writer.cc",
+ "fetch/buffering_data_pipe_writer.h",
+ "fetch/cached_metadata.cc",
+ "fetch/cached_metadata.h",
+ "fetch/cached_metadata_handler.h",
+ "fetch/client_hints_preferences.cc",
+ "fetch/client_hints_preferences.h",
+ "fetch/fetch_context.cc",
+ "fetch/fetch_context.h",
+ "fetch/fetch_initiator_info.h",
+ "fetch/fetch_parameters.cc",
+ "fetch/fetch_parameters.h",
+ "fetch/fetch_utils.cc",
+ "fetch/fetch_utils.h",
+ "fetch/integrity_metadata.cc",
+ "fetch/integrity_metadata.h",
+ "fetch/memory_cache.cc",
+ "fetch/memory_cache.h",
+ "fetch/preload_key.h",
+ "fetch/raw_resource.cc",
+ "fetch/raw_resource.h",
+ "fetch/resource.cc",
+ "fetch/resource.h",
+ "fetch/resource_client.h",
+ "fetch/resource_client_walker.h",
+ "fetch/resource_error.cc",
+ "fetch/resource_error.h",
+ "fetch/resource_fetcher.cc",
+ "fetch/resource_fetcher.h",
+ "fetch/resource_finish_observer.h",
+ "fetch/resource_load_info.h",
+ "fetch/resource_load_priority.h",
+ "fetch/resource_load_scheduler.cc",
+ "fetch/resource_load_scheduler.h",
+ "fetch/resource_load_timing.cc",
+ "fetch/resource_load_timing.h",
+ "fetch/resource_loader.cc",
+ "fetch/resource_loader.h",
+ "fetch/resource_loader_options.h",
+ "fetch/resource_loading_log.h",
+ "fetch/resource_priority.h",
+ "fetch/resource_request.cc",
+ "fetch/resource_request.h",
+ "fetch/resource_response.cc",
+ "fetch/resource_response.h",
+ "fetch/resource_status.h",
+ "fetch/resource_timing_info.cc",
+ "fetch/resource_timing_info.h",
+ "fetch/script_fetch_options.cc",
+ "fetch/script_fetch_options.h",
+ "fetch/source_keyed_cached_metadata_handler.cc",
+ "fetch/source_keyed_cached_metadata_handler.h",
+ "fetch/substitute_data.h",
+ "fetch/text_resource_decoder_options.cc",
+ "fetch/text_resource_decoder_options.h",
+ "fetch/unique_identifier.cc",
+ "fetch/unique_identifier.h",
+ "link_header.cc",
+ "link_header.h",
+ "subresource_integrity.cc",
+ "subresource_integrity.h",
+ ]
+
+ sources += get_target_outputs(
+ ":make_platform_loader_generated_fetch_initiator_type_names")
+
+ deps = [
+ ":make_platform_loader_generated_fetch_initiator_type_names",
+ "//components/link_header_util",
+ "//services/network/public/cpp",
+ "//services/network/public/mojom:mojom_blink",
+ ]
+}
+
+jumbo_source_set("unit_tests") {
+ # This target defines test files for blink_platform_unittests and only the
+ # blink_platform_unittests target should depend on it.
+ visibility = [ "//third_party/blink/renderer/platform:*" ]
+ testonly = true
+
+ # Source files for blink_platform_unittests.
+ sources = [
+ "fetch/buffering_data_pipe_writer_test.cc",
+ "fetch/client_hints_preferences_test.cc",
+ "fetch/fetch_utils_test.cc",
+ "fetch/memory_cache_correctness_test.cc",
+ "fetch/memory_cache_test.cc",
+ "fetch/raw_resource_test.cc",
+ "fetch/resource_fetcher_test.cc",
+ "fetch/resource_load_scheduler_test.cc",
+ "fetch/resource_loader_options_test.cc",
+ "fetch/resource_loader_test.cc",
+ "fetch/resource_request_test.cc",
+ "fetch/resource_response_test.cc",
+ "fetch/resource_test.cc",
+ "fetch/source_keyed_cached_metadata_handler_test.cc",
+ "link_header_test.cc",
+ "subresource_integrity_test.cc",
+ ]
+
+ configs += [ "//third_party/blink/renderer/platform:blink_platform_config" ]
+
+ deps = [
+ "//testing/gmock",
+ "//testing/gtest",
+ "//third_party/blink/renderer/platform:platform",
+ ]
+}
+
+jumbo_source_set("test_support") {
+ # This target defines test files for platform:test_support that
+ # blink_platform_unittests and webkit_unit_tests can use.
+ visibility = [ "//third_party/blink/renderer/platform:test_support" ]
+ testonly = true
+
+ # Source files that can be called from blink_platform_unittests and
+ # webkit_unit_tests.
+ sources = [
+ "testing/crypto_testing_platform_support.h",
+ "testing/fetch_testing_platform_support.cc",
+ "testing/fetch_testing_platform_support.h",
+ "testing/mock_fetch_context.h",
+ "testing/mock_resource.cc",
+ "testing/mock_resource.h",
+ "testing/mock_resource_client.h",
+ "testing/web_url_loader_factory_with_mock.cc",
+ "testing/web_url_loader_factory_with_mock.h",
+ ]
+
+ configs += [
+ "//third_party/blink/renderer:non_test_config",
+ "//third_party/blink/renderer/platform:blink_platform_config",
+ ]
+
+ public_deps = [
+ "//net",
+ "//skia",
+ "//third_party/blink/public:blink_headers",
+ "//third_party/blink/renderer/platform:platform",
+ "//third_party/blink/renderer/platform/blob:generator",
+ "//third_party/icu",
+ ]
+}
diff --git a/chromium/third_party/blink/renderer/platform/loader/DEPS b/chromium/third_party/blink/renderer/platform/loader/DEPS
new file mode 100644
index 00000000000..3577746624c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/DEPS
@@ -0,0 +1,13 @@
+include_rules = [
+ "+base/metrics/field_trial_params.h", # for fetch/ResourceLoadScheduler.cpp
+ "+base/strings/string_number_conversions.h", # for fetch/ResourceLoadScheduler.cpp
+ "+components/link_header_util", # for LinkHeader.cpp
+ "+services/network/public", # for Fetch API and CORS
+ "+third_party/boringssl/src/include/openssl/curve25519.h" # for SubresourceIntegrity.cpp
+]
+
+specific_include_rules = {
+ "resource_error\.cc": [
+ "+net/base/net_errors.h"
+ ]
+}
diff --git a/chromium/third_party/blink/renderer/platform/loader/OWNERS b/chromium/third_party/blink/renderer/platform/loader/OWNERS
new file mode 100644
index 00000000000..fa3423b99fb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/OWNERS
@@ -0,0 +1,7 @@
+japhet@chromium.org
+mkwst@chromium.org
+yhirano@chromium.org
+yoav@yoav.ws
+
+# TEAM: loading-dev@chromium.org
+# COMPONENT: Blink>Loader
diff --git a/chromium/third_party/blink/renderer/platform/loader/README.md b/chromium/third_party/blink/renderer/platform/loader/README.md
new file mode 100644
index 00000000000..83fa16c4885
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/README.md
@@ -0,0 +1,22 @@
+# platform/loader/
+
+This document describes how files under `platform/loader/` are organized.
+
+## cors
+
+Contains Cross-Origin Resource Sharing (CORS) related files. Some functions
+in this directory will be removed once CORS support is moved to
+//services/network. Please contact {kinuko,tyoshino,toyoshim}@chromium.org when
+you need to depend on this directory from new code.
+
+## fetch
+
+Contains files for low-level loading APIs. The `PLATFORM_EXPORT` macro is
+needed to use them from `core/`. Otherwise they can be used only in
+`platform/`.
+
+## testing
+
+Contains helper files for testing that are available in both
+`blink_platform_unittests` and `webkit_unit_tests`.
+These files are built as a part of the `platform:test_support` static library.
diff --git a/chromium/third_party/blink/renderer/platform/loader/cors/cors.cc b/chromium/third_party/blink/renderer/platform/loader/cors/cors.cc
new file mode 100644
index 00000000000..370971d02b8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/cors/cors.cc
@@ -0,0 +1,215 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/loader/cors/cors.h"
+
+#include <string>
+
+#include "services/network/public/cpp/cors/cors.h"
+#include "services/network/public/cpp/cors/preflight_cache.h"
+#include "third_party/blink/renderer/platform/loader/cors/cors_error_string.h"
+#include "third_party/blink/renderer/platform/network/http_header_map.h"
+#include "third_party/blink/renderer/platform/network/http_names.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/weborigin/scheme_registry.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#include "third_party/blink/renderer/platform/wtf/thread_specific.h"
+#include "url/gurl.h"
+
+namespace blink {
+
+namespace {
+
+base::Optional<std::string> GetHeaderValue(const HTTPHeaderMap& header_map,
+ const AtomicString& header_name) {
+ if (header_map.Contains(header_name)) {
+ const AtomicString& atomic_value = header_map.Get(header_name);
+ CString string_value = atomic_value.GetString().Utf8();
+ return std::string(string_value.data(), string_value.length());
+ }
+ return base::nullopt;
+}
+
+network::cors::PreflightCache& GetPerThreadPreflightCache() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadSpecific<network::cors::PreflightCache>,
+ cache, ());
+ return *cache;
+}
+
+base::Optional<std::string> GetOptionalHeaderValue(
+ const HTTPHeaderMap& header_map,
+ const AtomicString& header_name) {
+ const AtomicString& result = header_map.Get(header_name);
+ if (result.IsNull())
+ return base::nullopt;
+
+ return std::string(result.Ascii().data());
+}
+
+std::unique_ptr<net::HttpRequestHeaders> CreateNetHttpRequestHeaders(
+ const HTTPHeaderMap& header_map) {
+ std::unique_ptr<net::HttpRequestHeaders> request_headers =
+ std::make_unique<net::HttpRequestHeaders>();
+ for (HTTPHeaderMap::const_iterator i = header_map.begin(),
+ end = header_map.end();
+ i != end; ++i) {
+ DCHECK(!i->key.IsNull());
+ DCHECK(!i->value.IsNull());
+ request_headers->SetHeader(std::string(i->key.Ascii().data()),
+ std::string(i->value.Ascii().data()));
+ }
+ return request_headers;
+}
+
+} // namespace
+
+namespace CORS {
+
+WTF::Optional<network::mojom::CORSError> CheckAccess(
+ const KURL& response_url,
+ const int response_status_code,
+ const HTTPHeaderMap& response_header,
+ network::mojom::FetchCredentialsMode credentials_mode,
+ const SecurityOrigin& origin) {
+ std::unique_ptr<SecurityOrigin::PrivilegeData> privilege =
+ origin.CreatePrivilegeData();
+ return network::cors::CheckAccess(
+ response_url, response_status_code,
+ GetHeaderValue(response_header, HTTPNames::Access_Control_Allow_Origin),
+ GetHeaderValue(response_header,
+ HTTPNames::Access_Control_Allow_Credentials),
+ credentials_mode, origin.ToUrlOrigin(),
+ !privilege->block_local_access_from_local_origin_);
+}
+
+WTF::Optional<network::mojom::CORSError> CheckRedirectLocation(
+ const KURL& url) {
+ static const bool run_blink_side_scheme_check =
+ !RuntimeEnabledFeatures::OutOfBlinkCORSEnabled();
+ // TODO(toyoshim): Deprecate Blink side scheme check when we enable
+ // out-of-renderer CORS support. This will need to deprecate Blink APIs that
+ // are currently used by an embedder. See https://crbug.com/800669.
+ if (run_blink_side_scheme_check &&
+ !SchemeRegistry::ShouldTreatURLSchemeAsCORSEnabled(url.Protocol())) {
+ return network::mojom::CORSError::kRedirectDisallowedScheme;
+ }
+ return network::cors::CheckRedirectLocation(url, run_blink_side_scheme_check);
+}
+
+WTF::Optional<network::mojom::CORSError> CheckPreflight(
+ const int preflight_response_status_code) {
+ return network::cors::CheckPreflight(preflight_response_status_code);
+}
+
+WTF::Optional<network::mojom::CORSError> CheckExternalPreflight(
+ const HTTPHeaderMap& response_header) {
+ return network::cors::CheckExternalPreflight(GetHeaderValue(
+ response_header, HTTPNames::Access_Control_Allow_External));
+}
+
+bool IsCORSEnabledRequestMode(network::mojom::FetchRequestMode request_mode) {
+ return network::cors::IsCORSEnabledRequestMode(request_mode);
+}
+
+bool EnsurePreflightResultAndCacheOnSuccess(
+ const HTTPHeaderMap& response_header_map,
+ const String& origin,
+ const KURL& request_url,
+ const String& request_method,
+ const HTTPHeaderMap& request_header_map,
+ network::mojom::FetchCredentialsMode request_credentials_mode,
+ String* error_description) {
+ DCHECK(!origin.IsNull());
+ DCHECK(!request_method.IsNull());
+ DCHECK(error_description);
+
+ base::Optional<network::mojom::CORSError> error;
+
+ std::unique_ptr<network::cors::PreflightResult> result =
+ network::cors::PreflightResult::Create(
+ request_credentials_mode,
+ GetOptionalHeaderValue(response_header_map,
+ HTTPNames::Access_Control_Allow_Methods),
+ GetOptionalHeaderValue(response_header_map,
+ HTTPNames::Access_Control_Allow_Headers),
+ GetOptionalHeaderValue(response_header_map,
+ HTTPNames::Access_Control_Max_Age),
+ &error);
+ if (error) {
+ *error_description = CORS::GetErrorString(
+ CORS::ErrorParameter::CreateForPreflightResponseCheck(*error,
+ String()));
+ return false;
+ }
+
+ error = result->EnsureAllowedCrossOriginMethod(
+ std::string(request_method.Ascii().data()));
+ if (error) {
+ *error_description = CORS::GetErrorString(
+ CORS::ErrorParameter::CreateForPreflightResponseCheck(*error,
+ request_method));
+ return false;
+ }
+
+ std::string detected_error_header;
+ error = result->EnsureAllowedCrossOriginHeaders(
+ *CreateNetHttpRequestHeaders(request_header_map), &detected_error_header);
+ if (error) {
+ *error_description = CORS::GetErrorString(
+ CORS::ErrorParameter::CreateForPreflightResponseCheck(
+ *error, String(detected_error_header.data(),
+ detected_error_header.length())));
+ return false;
+ }
+
+ DCHECK(!error);
+
+ GetPerThreadPreflightCache().AppendEntry(std::string(origin.Ascii().data()),
+ request_url, std::move(result));
+ return true;
+}
+
+bool CheckIfRequestCanSkipPreflight(
+ const String& origin,
+ const KURL& url,
+ network::mojom::FetchCredentialsMode credentials_mode,
+ const String& method,
+ const HTTPHeaderMap& request_header_map) {
+ DCHECK(!origin.IsNull());
+ DCHECK(!method.IsNull());
+
+ return GetPerThreadPreflightCache().CheckIfRequestCanSkipPreflight(
+ std::string(origin.Ascii().data()), url, credentials_mode,
+ std::string(method.Ascii().data()),
+ *CreateNetHttpRequestHeaders(request_header_map));
+}
+
+bool IsCORSSafelistedMethod(const String& method) {
+ DCHECK(!method.IsNull());
+ CString utf8_method = method.Utf8();
+ return network::cors::IsCORSSafelistedMethod(
+ std::string(utf8_method.data(), utf8_method.length()));
+}
+
+bool IsCORSSafelistedContentType(const String& media_type) {
+ CString utf8_media_type = media_type.Utf8();
+ return network::cors::IsCORSSafelistedContentType(
+ std::string(utf8_media_type.data(), utf8_media_type.length()));
+}
+
+bool IsCORSSafelistedHeader(const String& name, const String& value) {
+ DCHECK(!name.IsNull());
+ DCHECK(!value.IsNull());
+ CString utf8_name = name.Utf8();
+ CString utf8_value = value.Utf8();
+ return network::cors::IsCORSSafelistedHeader(
+ std::string(utf8_name.data(), utf8_name.length()),
+ std::string(utf8_value.data(), utf8_value.length()));
+}
+
+} // namespace CORS
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/cors/cors.h b/chromium/third_party/blink/renderer/platform/loader/cors/cors.h
new file mode 100644
index 00000000000..0ba71c770f7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/cors/cors.h
@@ -0,0 +1,71 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_CORS_CORS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_CORS_CORS_H_
+
+#include "services/network/public/mojom/cors.mojom-shared.h"
+#include "services/network/public/mojom/fetch_api.mojom-shared.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class HTTPHeaderMap;
+class KURL;
+class SecurityOrigin;
+
+// CORS related utility functions.
+namespace CORS {
+
+// Thin wrapper functions below are for calling ::network::cors functions from
+// Blink core. Once Out-of-renderer CORS is enabled, following functions will
+// be removed.
+PLATFORM_EXPORT WTF::Optional<network::mojom::CORSError> CheckAccess(
+ const KURL&,
+ const int response_status_code,
+ const HTTPHeaderMap&,
+ network::mojom::FetchCredentialsMode,
+ const SecurityOrigin&);
+
+PLATFORM_EXPORT WTF::Optional<network::mojom::CORSError> CheckRedirectLocation(
+ const KURL&);
+
+PLATFORM_EXPORT WTF::Optional<network::mojom::CORSError> CheckPreflight(
+ const int preflight_response_status_code);
+
+PLATFORM_EXPORT WTF::Optional<network::mojom::CORSError> CheckExternalPreflight(
+ const HTTPHeaderMap&);
+
+PLATFORM_EXPORT bool IsCORSEnabledRequestMode(network::mojom::FetchRequestMode);
+
+PLATFORM_EXPORT bool EnsurePreflightResultAndCacheOnSuccess(
+ const HTTPHeaderMap& response_header_map,
+ const String& origin,
+ const KURL& request_url,
+ const String& request_method,
+ const HTTPHeaderMap& request_header_map,
+ network::mojom::FetchCredentialsMode request_credentials_mode,
+ String* error_description);
+
+PLATFORM_EXPORT bool CheckIfRequestCanSkipPreflight(
+ const String& origin,
+ const KURL&,
+ network::mojom::FetchCredentialsMode,
+ const String& method,
+ const HTTPHeaderMap& request_header_map);
+
+// Thin wrapper functions that will not be removed even after out-of-renderer
+// CORS is enabled.
+PLATFORM_EXPORT bool IsCORSSafelistedMethod(const String& method);
+PLATFORM_EXPORT bool IsCORSSafelistedContentType(const String&);
+PLATFORM_EXPORT bool IsCORSSafelistedHeader(const String& name,
+ const String& value);
+
+} // namespace CORS
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_CORS_CORS_H_
diff --git a/chromium/third_party/blink/renderer/platform/loader/cors/cors_error_string.cc b/chromium/third_party/blink/renderer/platform/loader/cors/cors_error_string.cc
new file mode 100644
index 00000000000..e0d653ff462
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/cors/cors_error_string.cc
@@ -0,0 +1,344 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/loader/cors/cors_error_string.h"
+
+#include "third_party/blink/renderer/platform/network/http_header_map.h"
+#include "third_party/blink/renderer/platform/network/http_names.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+
+namespace blink {
+
+namespace CORS {
+
+namespace {
+
+const KURL& GetInvalidURL() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(KURL, invalid_url, ());
+ return invalid_url;
+}
+
+bool IsInterestingStatusCode(int status_code) {
+ // Predicate that gates what status codes should be included in console error
+ // messages for responses containing no access control headers.
+ return status_code >= 400;
+}
+
+ErrorParameter CreateWrongParameter(network::mojom::CORSError error) {
+ return ErrorParameter(
+ error, GetInvalidURL(), GetInvalidURL(), 0 /* status_code */,
+ HTTPHeaderMap(), *SecurityOrigin::CreateUnique(),
+ WebURLRequest::kRequestContextUnspecified, String(), true);
+}
+
+} // namespace
+
+// static
+ErrorParameter ErrorParameter::Create(
+ const network::mojom::CORSError error,
+ const KURL& first_url,
+ const KURL& second_url,
+ const int status_code,
+ const HTTPHeaderMap& header_map,
+ const SecurityOrigin& origin,
+ const WebURLRequest::RequestContext context) {
+ return ErrorParameter(error, first_url, second_url, status_code, header_map,
+ origin, context, String(), false);
+}
+
+// static
+ErrorParameter ErrorParameter::CreateForDisallowedByMode(
+ const KURL& request_url) {
+ return ErrorParameter(network::mojom::CORSError::kDisallowedByMode,
+ request_url, GetInvalidURL(), 0 /* status_code */,
+ HTTPHeaderMap(), *SecurityOrigin::CreateUnique(),
+ WebURLRequest::kRequestContextUnspecified, String(),
+ false);
+}
+
+// static
+ErrorParameter ErrorParameter::CreateForInvalidResponse(
+ const KURL& request_url,
+ const SecurityOrigin& origin) {
+ return ErrorParameter(
+ network::mojom::CORSError::kInvalidResponse, request_url, GetInvalidURL(),
+ 0 /* status_code */, HTTPHeaderMap(), origin,
+ WebURLRequest::kRequestContextUnspecified, String(), false);
+}
+
+// static
+ErrorParameter ErrorParameter::CreateForAccessCheck(
+ const network::mojom::CORSError error,
+ const KURL& request_url,
+ int response_status_code,
+ const HTTPHeaderMap& response_header_map,
+ const SecurityOrigin& origin,
+ const WebURLRequest::RequestContext context,
+ const KURL& redirect_url) {
+ switch (error) {
+ case network::mojom::CORSError::kInvalidResponse:
+ case network::mojom::CORSError::kWildcardOriginNotAllowed:
+ case network::mojom::CORSError::kMissingAllowOriginHeader:
+ case network::mojom::CORSError::kMultipleAllowOriginValues:
+ case network::mojom::CORSError::kInvalidAllowOriginValue:
+ case network::mojom::CORSError::kAllowOriginMismatch:
+ case network::mojom::CORSError::kDisallowCredentialsNotSetToTrue:
+ return ErrorParameter(error, request_url, redirect_url,
+ response_status_code, response_header_map, origin,
+ context, String(), false);
+ default:
+ NOTREACHED();
+ }
+ return CreateWrongParameter(error);
+}
+
+// static
+ErrorParameter ErrorParameter::CreateForPreflightStatusCheck(
+ int response_status_code) {
+ return ErrorParameter(network::mojom::CORSError::kPreflightInvalidStatus,
+ GetInvalidURL(), GetInvalidURL(), response_status_code,
+ HTTPHeaderMap(), *SecurityOrigin::CreateUnique(),
+ WebURLRequest::kRequestContextUnspecified, String(),
+ false);
+}
+
+// static
+ErrorParameter ErrorParameter::CreateForExternalPreflightCheck(
+ const network::mojom::CORSError error,
+ const HTTPHeaderMap& response_header_map) {
+ switch (error) {
+ case network::mojom::CORSError::kPreflightMissingAllowExternal:
+ case network::mojom::CORSError::kPreflightInvalidAllowExternal:
+ return ErrorParameter(
+ error, GetInvalidURL(), GetInvalidURL(), 0 /* status_code */,
+ response_header_map, *SecurityOrigin::CreateUnique(),
+ WebURLRequest::kRequestContextUnspecified, String(), false);
+ default:
+ NOTREACHED();
+ }
+ return CreateWrongParameter(error);
+}
+
+// static
+ErrorParameter ErrorParameter::CreateForPreflightResponseCheck(
+ const network::mojom::CORSError error,
+ const String& hint) {
+ switch (error) {
+ case network::mojom::CORSError::kInvalidAllowMethodsPreflightResponse:
+ case network::mojom::CORSError::kInvalidAllowHeadersPreflightResponse:
+ case network::mojom::CORSError::kMethodDisallowedByPreflightResponse:
+ case network::mojom::CORSError::kHeaderDisallowedByPreflightResponse:
+ return ErrorParameter(
+ error, GetInvalidURL(), GetInvalidURL(), 0 /* status_code */,
+ HTTPHeaderMap(), *SecurityOrigin::CreateUnique(),
+ WebURLRequest::kRequestContextUnspecified, hint, false);
+ default:
+ NOTREACHED();
+ }
+ return CreateWrongParameter(error);
+}
+
+// static
+ErrorParameter ErrorParameter::CreateForRedirectCheck(
+ network::mojom::CORSError error,
+ const KURL& request_url,
+ const KURL& redirect_url) {
+ switch (error) {
+ case network::mojom::CORSError::kRedirectDisallowedScheme:
+ case network::mojom::CORSError::kRedirectContainsCredentials:
+ return ErrorParameter(
+ error, request_url, redirect_url, 0 /* status_code */,
+ HTTPHeaderMap(), *SecurityOrigin::CreateUnique(),
+ WebURLRequest::kRequestContextUnspecified, String(), false);
+ default:
+ NOTREACHED();
+ }
+ return CreateWrongParameter(error);
+}
+
+ErrorParameter::ErrorParameter(const network::mojom::CORSError error,
+ const KURL& first_url,
+ const KURL& second_url,
+ const int status_code,
+ const HTTPHeaderMap& header_map,
+ const SecurityOrigin& origin,
+ const WebURLRequest::RequestContext context,
+ const String& hint,
+ bool unknown)
+ : error(error),
+ first_url(first_url),
+ second_url(second_url),
+ status_code(status_code),
+ header_map(header_map),
+ origin(origin),
+ context(context),
+ hint(hint),
+ unknown(unknown) {}
+
+String GetErrorString(const ErrorParameter& param) {
+ static const char kNoCorsInformation[] =
+ " Have the server send the header with a valid value, or, if an opaque "
+ "response serves your needs, set the request's mode to 'no-cors' to "
+ "fetch the resource with CORS disabled.";
+
+ if (param.unknown)
+ return String::Format("CORS error, code %d", static_cast<int>(param.error));
+
+ String redirect_denied =
+ param.second_url.IsValid()
+ ? String::Format(
+ "Redirect from '%s' to '%s' has been blocked by CORS policy: ",
+ param.first_url.GetString().Utf8().data(),
+ param.second_url.GetString().Utf8().data())
+ : String();
+
+ switch (param.error) {
+ case network::mojom::CORSError::kDisallowedByMode:
+ return String::Format(
+ "Failed to load '%s': Cross origin requests are not allowed by "
+ "request mode.",
+ param.first_url.GetString().Utf8().data());
+ case network::mojom::CORSError::kInvalidResponse:
+ return String::Format(
+ "%sInvalid response. Origin '%s' is therefore not allowed access.",
+ redirect_denied.Utf8().data(), param.origin.ToString().Utf8().data());
+ case network::mojom::CORSError::kWildcardOriginNotAllowed:
+ return String::Format(
+ "%sThe value of the 'Access-Control-Allow-Origin' header in the "
+ "response must not be the wildcard '*' when the request's "
+ "credentials mode is 'include'. Origin '%s' is therefore not allowed "
+ "access.%s",
+ redirect_denied.Utf8().data(), param.origin.ToString().Utf8().data(),
+ param.context == WebURLRequest::kRequestContextXMLHttpRequest
+ ? " The credentials mode of requests initiated by the "
+ "XMLHttpRequest is controlled by the withCredentials attribute."
+ : "");
+ case network::mojom::CORSError::kMissingAllowOriginHeader:
+ return String::Format(
+ "%sNo 'Access-Control-Allow-Origin' header is present on the "
+ "requested resource. Origin '%s' is therefore not allowed access."
+ "%s%s",
+ redirect_denied.Utf8().data(), param.origin.ToString().Utf8().data(),
+ IsInterestingStatusCode(param.status_code)
+ ? String::Format(" The response had HTTP status code %d.",
+ param.status_code)
+ .Utf8()
+ .data()
+ : "",
+ param.context == WebURLRequest::kRequestContextFetch
+ ? " If an opaque response serves your needs, set the request's "
+ "mode to 'no-cors' to fetch the resource with CORS disabled."
+ : "");
+ case network::mojom::CORSError::kMultipleAllowOriginValues:
+ return String::Format(
+ "%sThe 'Access-Control-Allow-Origin' header contains multiple values "
+ "'%s', but only one is allowed. Origin '%s' is therefore not allowed "
+ "access.%s",
+ redirect_denied.Utf8().data(),
+ param.header_map.Get(HTTPNames::Access_Control_Allow_Origin)
+ .Utf8()
+ .data(),
+ param.origin.ToString().Utf8().data(),
+ param.context == WebURLRequest::kRequestContextFetch
+ ? kNoCorsInformation
+ : "");
+ case network::mojom::CORSError::kInvalidAllowOriginValue:
+ return String::Format(
+ "%sThe 'Access-Control-Allow-Origin' header contains the invalid "
+ "value '%s'. Origin '%s' is therefore not allowed access.%s",
+ redirect_denied.Utf8().data(),
+ param.header_map.Get(HTTPNames::Access_Control_Allow_Origin)
+ .Utf8()
+ .data(),
+ param.origin.ToString().Utf8().data(),
+ param.context == WebURLRequest::kRequestContextFetch
+ ? kNoCorsInformation
+ : "");
+ case network::mojom::CORSError::kAllowOriginMismatch:
+ return String::Format(
+ "%sThe 'Access-Control-Allow-Origin' header has a value '%s' that is "
+ "not equal to the supplied origin. Origin '%s' is therefore not "
+ "allowed access.%s",
+ redirect_denied.Utf8().data(),
+ param.header_map.Get(HTTPNames::Access_Control_Allow_Origin)
+ .Utf8()
+ .data(),
+ param.origin.ToString().Utf8().data(),
+ param.context == WebURLRequest::kRequestContextFetch
+ ? kNoCorsInformation
+ : "");
+ case network::mojom::CORSError::kDisallowCredentialsNotSetToTrue:
+ return String::Format(
+ "%sThe value of the 'Access-Control-Allow-Credentials' header in "
+ "the response is '%s' which must be 'true' when the request's "
+ "credentials mode is 'include'. Origin '%s' is therefore not allowed "
+ "access.%s",
+ redirect_denied.Utf8().data(),
+ param.header_map.Get(HTTPNames::Access_Control_Allow_Credentials)
+ .Utf8()
+ .data(),
+ param.origin.ToString().Utf8().data(),
+ (param.context == WebURLRequest::kRequestContextXMLHttpRequest
+ ? " The credentials mode of requests initiated by the "
+ "XMLHttpRequest is controlled by the withCredentials "
+ "attribute."
+ : ""));
+ case network::mojom::CORSError::kPreflightInvalidStatus:
+ return String::Format(
+ "Response for preflight has invalid HTTP status code %d.",
+ param.status_code);
+ case network::mojom::CORSError::kPreflightMissingAllowExternal:
+ return String(
+ "No 'Access-Control-Allow-External' header was present in the "
+ "preflight response for this external request (This is an "
+ "experimental header which is defined in "
+ "'https://wicg.github.io/cors-rfc1918/').");
+ case network::mojom::CORSError::kPreflightInvalidAllowExternal:
+ return String::Format(
+ "The 'Access-Control-Allow-External' header in the preflight "
+ "response for this external request had a value of '%s', not 'true' "
+ "(This is an experimental header which is defined in "
+ "'https://wicg.github.io/cors-rfc1918/').",
+ param.header_map.Get(HTTPNames::Access_Control_Allow_External)
+ .Utf8()
+ .data());
+ case network::mojom::CORSError::kInvalidAllowMethodsPreflightResponse:
+ return String(
+ "Cannot parse Access-Control-Allow-Methods response header field in "
+ "preflight response.");
+ case network::mojom::CORSError::kInvalidAllowHeadersPreflightResponse:
+ return String(
+ "Cannot parse Access-Control-Allow-Headers response header field in "
+ "preflight response.");
+ case network::mojom::CORSError::kMethodDisallowedByPreflightResponse:
+ return String::Format(
+ "Method %s is not allowed by Access-Control-Allow-Methods in "
+ "preflight response.",
+ param.hint.Utf8().data());
+ case network::mojom::CORSError::kHeaderDisallowedByPreflightResponse:
+ return String::Format(
+ "Request header field %s is not allowed by "
+ "Access-Control-Allow-Headers in preflight response.",
+ param.hint.Utf8().data());
+ case network::mojom::CORSError::kRedirectDisallowedScheme:
+ return String::Format(
+ "%sRedirect location '%s' has a disallowed scheme for cross-origin "
+ "requests.",
+ redirect_denied.Utf8().data(),
+ param.second_url.GetString().Utf8().data());
+ case network::mojom::CORSError::kRedirectContainsCredentials:
+ return String::Format(
+ "%sRedirect location '%s' contains a username and password, which is "
+ "disallowed for cross-origin requests.",
+ redirect_denied.Utf8().data(),
+ param.second_url.GetString().Utf8().data());
+ }
+ NOTREACHED();
+ return String();
+}
+
+} // namespace CORS
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/cors/cors_error_string.h b/chromium/third_party/blink/renderer/platform/loader/cors/cors_error_string.h
new file mode 100644
index 00000000000..73bd0f9e07d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/cors/cors_error_string.h
@@ -0,0 +1,112 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_CORS_CORS_ERROR_STRING_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_CORS_CORS_ERROR_STRING_H_
+
+#include "base/macros.h"
+#include "services/network/public/mojom/cors.mojom-shared.h"
+#include "third_party/blink/public/platform/web_url_request.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+
+namespace blink {
+
+class HTTPHeaderMap;
+class SecurityOrigin;
+
+// CORS error strings related utility functions.
+namespace CORS {
+
+// A struct to pass error dependent arguments for |GetErrorString|.
+struct PLATFORM_EXPORT ErrorParameter {
+ // Creates an ErrorParameter for generic cases. Use this function if |error|
+ // can contain any.
+ static ErrorParameter Create(const network::mojom::CORSError,
+ const KURL& first_url,
+ const KURL& second_url,
+ const int status_code,
+ const HTTPHeaderMap&,
+ const SecurityOrigin&,
+ const WebURLRequest::RequestContext);
+
+ // Creates an ErrorParameter for kDisallowedByMode.
+ static ErrorParameter CreateForDisallowedByMode(const KURL& request_url);
+
+ // Creates an ErrorParameter for kInvalidResponse.
+ static ErrorParameter CreateForInvalidResponse(const KURL& request_url,
+ const SecurityOrigin&);
+
+ // Creates an ErrorParameter for an error that CORS::CheckAccess() returns.
+ // |error| for redirect check needs to specify a valid |redirect_url|. The
+ // |redirect_url| can be omitted not to include redirect related information.
+ static ErrorParameter CreateForAccessCheck(
+ const network::mojom::CORSError,
+ const KURL& request_url,
+ int response_status_code,
+ const HTTPHeaderMap& response_header_map,
+ const SecurityOrigin&,
+ const WebURLRequest::RequestContext,
+ const KURL& redirect_url = KURL());
+
+ // Creates an ErrorParameter for kPreflightInvalidStatus that
+ // CORS::CheckPreflight() returns.
+ static ErrorParameter CreateForPreflightStatusCheck(int response_status_code);
+
+ // Creates an ErrorParameter for an error that CORS::CheckExternalPreflight()
+ // returns.
+ static ErrorParameter CreateForExternalPreflightCheck(
+ const network::mojom::CORSError,
+ const HTTPHeaderMap& response_header_map);
+
+ // Creates an ErrorParameter for an error that is related to CORS-preflight
+ // response checks.
+ // |hint| should contain a banned request method for
+ // kMethodDisallowedByPreflightResponse, a banned request header name for
+ // kHeaderDisallowedByPreflightResponse, or can be omitted for others.
+ static ErrorParameter CreateForPreflightResponseCheck(
+ const network::mojom::CORSError,
+ const String& hint);
+
+ // Creates an ErrorParameter for CORS::CheckRedirectLocation() returns.
+ static ErrorParameter CreateForRedirectCheck(network::mojom::CORSError,
+ const KURL& request_url,
+ const KURL& redirect_url);
+
+ // Should not be used directly by external callers. Use Create functions
+ // above.
+ ErrorParameter(const network::mojom::CORSError,
+ const KURL& first_url,
+ const KURL& second_url,
+ const int status_code,
+ const HTTPHeaderMap&,
+ const SecurityOrigin&,
+ const WebURLRequest::RequestContext,
+ const String& hint,
+ bool unknown);
+
+ // Members that this struct carries.
+ const network::mojom::CORSError error;
+ const KURL& first_url;
+ const KURL& second_url;
+ const int status_code;
+ const HTTPHeaderMap& header_map;
+ const SecurityOrigin& origin;
+ const WebURLRequest::RequestContext context;
+ const String& hint;
+
+ // Set to true when an ErrorParameter was created in a wrong way. Used in
+ // GetErrorString() to be robust for coding errors.
+ const bool unknown;
+};
+
+// Stringify CORSError mainly for inspector messages. Generated string should
+// not be exposed to JavaScript for security reasons.
+PLATFORM_EXPORT String GetErrorString(const ErrorParameter&);
+
+} // namespace CORS
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_CORS_CORS_ERROR_STRING_H_
diff --git a/chromium/third_party/blink/renderer/platform/loader/cors/cors_status.h b/chromium/third_party/blink/renderer/platform/loader/cors/cors_status.h
new file mode 100644
index 00000000000..d6ffdc89e3f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/cors/cors_status.h
@@ -0,0 +1,27 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_CORS_CORS_STATUS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_CORS_CORS_STATUS_H_
+
+namespace blink {
+
+enum class CORSStatus {
+ kUnknown, // Status not determined - not supposed to be seen by users.
+ kNotApplicable, // E.g. for main resources or if not in fetch mode CORS.
+
+ // Response not handled by service worker:
+ kSameOrigin, // Request was same origin.
+ kSuccessful, // Request was cross origin and CORS checks passed.
+ kFailed, // Request was cross origin and CORS checks failed.
+
+ // Response handled by service worker:
+ kServiceWorkerSuccessful, // ResponseType other than opaque (including
+ // error).
+ kServiceWorkerOpaque, // ResponseType was opaque.
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/README.md b/chromium/third_party/blink/renderer/platform/loader/fetch/README.md
new file mode 100644
index 00000000000..5e6da7211f2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/README.md
@@ -0,0 +1,6 @@
+Low-level fetching code.
+
+Fetching/loading code is divided into:
+- core/fetch: Fetch API
+- core/loader: high-level fetching
+- platform/loader/fetch: low-level fetching
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/access_control_status.h b/chromium/third_party/blink/renderer/platform/loader/fetch/access_control_status.h
new file mode 100644
index 00000000000..bd8d9454dc5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/access_control_status.h
@@ -0,0 +1,18 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_ACCESS_CONTROL_STATUS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_ACCESS_CONTROL_STATUS_H_
+
+namespace blink {
+
+enum AccessControlStatus {
+ kNotSharableCrossOrigin,
+ kSharableCrossOrigin,
+ kOpaqueResource
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_ACCESS_CONTROL_STATUS_H_
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_data_pipe_writer.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_data_pipe_writer.cc
new file mode 100644
index 00000000000..e7f1ee3bfcb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_data_pipe_writer.cc
@@ -0,0 +1,109 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/loader/fetch/buffering_data_pipe_writer.h"
+
+#include "base/single_thread_task_runner.h"
+
+namespace blink {
+
+namespace {
+
+const auto kNone = MOJO_WRITE_DATA_FLAG_NONE;
+
+} // namespace
+
+BufferingDataPipeWriter::BufferingDataPipeWriter(
+ mojo::ScopedDataPipeProducerHandle handle,
+ base::SingleThreadTaskRunner* runner)
+ : handle_(std::move(handle)),
+ watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL, runner) {
+ watcher_.Watch(handle_.get(), MOJO_HANDLE_SIGNAL_WRITABLE,
+ MOJO_WATCH_CONDITION_SATISFIED,
+ base::BindRepeating(&BufferingDataPipeWriter::OnWritable,
+ base::Unretained(this)));
+}
+
+bool BufferingDataPipeWriter::Write(const char* buffer, uint32_t num_bytes) {
+ DCHECK(!finished_);
+ if (!handle_.is_valid())
+ return false;
+
+ if (buffer_.empty()) {
+ while (num_bytes > 0) {
+ uint32_t size = num_bytes;
+ MojoResult result = handle_->WriteData(buffer, &size, kNone);
+ if (result == MOJO_RESULT_SHOULD_WAIT)
+ break;
+ if (result != MOJO_RESULT_OK) {
+ Clear();
+ return false;
+ }
+ num_bytes -= size;
+ buffer += size;
+ }
+ }
+ if (num_bytes == 0)
+ return true;
+
+ buffer_.push_back(Vector<char>());
+ buffer_.back().Append(buffer, num_bytes);
+ if (!waiting_) {
+ waiting_ = true;
+ watcher_.ArmOrNotify();
+ }
+ return true;
+}
+
+void BufferingDataPipeWriter::Finish() {
+ finished_ = true;
+ ClearIfNeeded();
+}
+
+void BufferingDataPipeWriter::OnWritable(MojoResult,
+ const mojo::HandleSignalsState&) {
+ if (!handle_.is_valid())
+ return;
+ waiting_ = false;
+ while (!buffer_.empty()) {
+ WTF::Vector<char>& front = buffer_.front();
+
+ uint32_t size = front.size() - front_written_size_;
+
+ MojoResult result =
+ handle_->WriteData(front.data() + front_written_size_, &size, kNone);
+ if (result == MOJO_RESULT_SHOULD_WAIT) {
+ waiting_ = true;
+ watcher_.ArmOrNotify();
+ return;
+ }
+ if (result != MOJO_RESULT_OK) {
+ Clear();
+ return;
+ }
+ front_written_size_ += size;
+
+ if (front_written_size_ == front.size()) {
+ front_written_size_ = 0;
+ buffer_.TakeFirst();
+ }
+ }
+ ClearIfNeeded();
+}
+
+void BufferingDataPipeWriter::Clear() {
+ handle_.reset();
+ watcher_.Cancel();
+ buffer_.clear();
+}
+
+void BufferingDataPipeWriter::ClearIfNeeded() {
+ if (!finished_)
+ return;
+
+ if (buffer_.empty())
+ Clear();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_data_pipe_writer.h b/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_data_pipe_writer.h
new file mode 100644
index 00000000000..8b3175fd537
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_data_pipe_writer.h
@@ -0,0 +1,46 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_BUFFERING_DATA_PIPE_WRITER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_BUFFERING_DATA_PIPE_WRITER_H_
+
+#include "base/single_thread_task_runner.h"
+#include "mojo/public/cpp/system/data_pipe.h"
+#include "mojo/public/cpp/system/simple_watcher.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/deque.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+// A writer to a mojo data pipe which has a buffer to store contents. As a
+// result, it is possible for a caller to miss write failures.
+class PLATFORM_EXPORT BufferingDataPipeWriter {
+ public:
+ BufferingDataPipeWriter(mojo::ScopedDataPipeProducerHandle,
+ base::SingleThreadTaskRunner*);
+
+ // Writes buffer[0:num_bytes] to the data pipe. Returns true if there is no
+ // error.
+ bool Write(const char* buffer, uint32_t num_bytes);
+
+ // Finishes writing. After calling this function, Write must not be called.
+ void Finish();
+
+ private:
+ void OnWritable(MojoResult, const mojo::HandleSignalsState&);
+ void ClearIfNeeded();
+ void Clear();
+
+ mojo::ScopedDataPipeProducerHandle handle_;
+ mojo::SimpleWatcher watcher_;
+ Deque<Vector<char>> buffer_;
+ size_t front_written_size_ = 0;
+ bool waiting_ = false;
+ bool finished_ = false;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_data_pipe_writer_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_data_pipe_writer_test.cc
new file mode 100644
index 00000000000..0788a286d23
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_data_pipe_writer_test.cc
@@ -0,0 +1,78 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/loader/fetch/buffering_data_pipe_writer.h"
+
+#include <memory>
+#include <random>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
+
+namespace blink {
+namespace {
+
+TEST(BufferingDataPipeWriterTest, WriteMany) {
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform;
+ constexpr int kCapacity = 4096;
+
+ std::minstd_rand engine(99);
+
+ mojo::ScopedDataPipeProducerHandle producer;
+ mojo::ScopedDataPipeConsumerHandle consumer;
+ MojoCreateDataPipeOptions options;
+ options.struct_size = sizeof(MojoCreateDataPipeOptions);
+ options.flags = MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE;
+ options.element_num_bytes = 1;
+ options.capacity_num_bytes = kCapacity;
+
+ MojoResult result = mojo::CreateDataPipe(&options, &producer, &consumer);
+ ASSERT_EQ(MOJO_RESULT_OK, result);
+
+ constexpr size_t total = kCapacity * 3;
+ constexpr size_t writing_chunk_size = 5;
+ constexpr size_t reading_chunk_size = 7;
+ Vector<char> input, output;
+
+ for (size_t i = 0; i < total; ++i)
+ input.push_back(static_cast<char>(engine() % 26 + 'A'));
+
+ auto writer = std::make_unique<BufferingDataPipeWriter>(
+ std::move(producer), platform->CurrentThread()->GetTaskRunner().get());
+
+ for (size_t i = 0; i < total;) {
+ // We use a temporary buffer to check that the buffer is copied immediately.
+ char temp[writing_chunk_size] = {};
+ size_t size = std::min(total - i, writing_chunk_size);
+
+ std::copy(input.data() + i, input.data() + i + size, temp);
+ ASSERT_TRUE(writer->Write(temp, size));
+
+ i += size;
+ }
+
+ writer->Finish();
+
+ while (true) {
+ constexpr auto kNone = MOJO_READ_DATA_FLAG_NONE;
+ char buffer[reading_chunk_size] = {};
+ uint32_t size = reading_chunk_size;
+ result = consumer->ReadData(buffer, &size, kNone);
+
+ if (result == MOJO_RESULT_SHOULD_WAIT) {
+ platform->RunUntilIdle();
+ continue;
+ }
+ if (result == MOJO_RESULT_FAILED_PRECONDITION)
+ break;
+
+ ASSERT_EQ(MOJO_RESULT_OK, result);
+ output.Append(buffer, size);
+ }
+ EXPECT_EQ(output, input);
+}
+
+} // namespace
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/cached_metadata.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/cached_metadata.cc
new file mode 100644
index 00000000000..563a6742750
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/cached_metadata.cc
@@ -0,0 +1,54 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/loader/fetch/cached_metadata.h"
+
+#include "third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h"
+
+namespace blink {
+
+scoped_refptr<CachedMetadata> CachedMetadata::CreateFromSerializedData(
+ const char* data,
+ size_t size) {
+ // Ensure the data is big enough, otherwise discard the data.
+ if (size < kCachedMetaDataStart) {
+ return nullptr;
+ }
+ // Ensure the marker matches, otherwise discard the data.
+ if (*reinterpret_cast<const uint32_t*>(data) !=
+ CachedMetadataHandler::kSingleEntry) {
+ return nullptr;
+ }
+ return base::AdoptRef(new CachedMetadata(data, size));
+}
+
+CachedMetadata::CachedMetadata(const char* data, size_t size) {
+ // Serialized metadata should have non-empty data.
+ DCHECK_GT(size, kCachedMetaDataStart);
+ DCHECK(data);
+ // Make sure that the first int in the data is the single entry marker.
+ CHECK_EQ(*reinterpret_cast<const uint32_t*>(data),
+ CachedMetadataHandler::kSingleEntry);
+
+ serialized_data_.ReserveInitialCapacity(size);
+ serialized_data_.Append(data, size);
+}
+
+CachedMetadata::CachedMetadata(uint32_t data_type_id,
+ const char* data,
+ size_t size) {
+ // Don't allow an ID of 0, it is used internally to indicate errors.
+ DCHECK(data_type_id);
+ DCHECK(data);
+
+ serialized_data_.ReserveInitialCapacity(kCachedMetaDataStart + size);
+ uint32_t marker = CachedMetadataHandler::kSingleEntry;
+ serialized_data_.Append(reinterpret_cast<const char*>(&marker),
+ sizeof(uint32_t));
+ serialized_data_.Append(reinterpret_cast<const char*>(&data_type_id),
+ sizeof(uint32_t));
+ serialized_data_.Append(data, size);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/cached_metadata.h b/chromium/third_party/blink/renderer/platform/loader/fetch/cached_metadata.h
new file mode 100644
index 00000000000..1763b23fd42
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/cached_metadata.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2010 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_CACHED_METADATA_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_CACHED_METADATA_H_
+
+#include <stdint.h>
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+// |m_serializedData| consists of a 32 bit marker, 32 bits type ID, and actual
+// data.
+constexpr size_t kCacheDataTypeStart = sizeof(uint32_t);
+constexpr size_t kCachedMetaDataStart = kCacheDataTypeStart + sizeof(uint32_t);
+
+// Metadata retrieved from the embedding application's cache.
+//
+// Serialized data is NOT portable across architectures. However, reading the
+// data type ID will reject data generated with a different byte-order.
+class PLATFORM_EXPORT CachedMetadata : public RefCounted<CachedMetadata> {
+ public:
+ static scoped_refptr<CachedMetadata> Create(uint32_t data_type_id,
+ const char* data,
+ size_t size) {
+ return base::AdoptRef(new CachedMetadata(data_type_id, data, size));
+ }
+
+ static scoped_refptr<CachedMetadata> CreateFromSerializedData(
+ const char* data,
+ size_t);
+
+ ~CachedMetadata() = default;
+
+ const Vector<char>& SerializedData() const { return serialized_data_; }
+
+ uint32_t DataTypeID() const {
+ DCHECK_GE(serialized_data_.size(), kCachedMetaDataStart);
+ return *reinterpret_cast_ptr<uint32_t*>(
+ const_cast<char*>(serialized_data_.data() + kCacheDataTypeStart));
+ }
+
+ const char* Data() const {
+ DCHECK_GE(serialized_data_.size(), kCachedMetaDataStart);
+ return serialized_data_.data() + kCachedMetaDataStart;
+ }
+
+ size_t size() const {
+ DCHECK_GE(serialized_data_.size(), kCachedMetaDataStart);
+ return serialized_data_.size() - kCachedMetaDataStart;
+ }
+
+ private:
+ CachedMetadata(const char* data, size_t);
+ CachedMetadata(uint32_t data_type_id, const char* data, size_t);
+
+ // Since the serialization format supports random access, storing it in
+ // serialized form avoids need for a copy during serialization.
+ Vector<char> serialized_data_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h b/chromium/third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h
new file mode 100644
index 00000000000..626544fddef
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h
@@ -0,0 +1,70 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_CACHED_METADATA_HANDLER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_CACHED_METADATA_HANDLER_H_
+
+#include <stdint.h>
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+class CachedMetadata;
+
+// Handler class for caching operations.
+class CachedMetadataHandler
+ : public GarbageCollectedFinalized<CachedMetadataHandler> {
+ public:
+ enum CacheType {
+ kSendToPlatform, // send cache data to blink::Platform::cacheMetadata
+ kCacheLocally // cache only in Resource's member variables
+ };
+
+ // Enum for marking serialized cached metadatas so that the deserializers
+ // do not conflict.
+ enum CachedMetadataType : uint32_t {
+ kSingleEntry, // the metadata is a single CachedMetadata entry
+ kSourceKeyedMap // the metadata is multiple CachedMetadata entries keyed by
+ // a source string.
+ };
+
+ virtual ~CachedMetadataHandler() = default;
+ virtual void Trace(blink::Visitor* visitor) {}
+
+ // Reset existing metadata, to allow setting new data.
+ virtual void ClearCachedMetadata(CacheType = kCacheLocally) = 0;
+
+ // Returns the encoding to which the cache is specific.
+ virtual String Encoding() const = 0;
+
+ virtual bool IsServedFromCacheStorage() const = 0;
+
+ protected:
+ CachedMetadataHandler() = default;
+};
+
+// A CachedMetadataHandler which stores one piece of metadata.
+class SingleCachedMetadataHandler : public CachedMetadataHandler {
+ public:
+ // Caches the given metadata in association with this resource and suggests
+ // that the platform persist it. The dataTypeID is a pseudo-randomly chosen
+ // identifier that is used to distinguish data generated by the caller.
+ virtual void SetCachedMetadata(uint32_t data_type_id,
+ const char*,
+ size_t,
+ CacheType = kSendToPlatform) = 0;
+
+ // Returns cached metadata of the given type associated with this resource.
+ // This cached metadata can be pruned at any time.
+ virtual scoped_refptr<CachedMetadata> GetCachedMetadata(
+ uint32_t data_type_id) const = 0;
+
+ protected:
+ SingleCachedMetadataHandler() = default;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_CACHED_METADATA_HANDLER_H_
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.cc
new file mode 100644
index 00000000000..6a3d226f8d3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.cc
@@ -0,0 +1,150 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.h"
+
+#include "base/macros.h"
+#include "third_party/blink/public/common/client_hints/client_hints.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
+#include "third_party/blink/renderer/platform/network/http_names.h"
+#include "third_party/blink/renderer/platform/network/http_parsers.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+
+namespace blink {
+
+namespace {
+
+void ParseAcceptChHeader(const String& header_value,
+ WebEnabledClientHints& enabled_hints) {
+ CommaDelimitedHeaderSet accept_client_hints_header;
+ ParseCommaDelimitedHeader(header_value, accept_client_hints_header);
+
+ for (size_t i = 0;
+ i < static_cast<int>(mojom::WebClientHintsType::kMaxValue) + 1; ++i) {
+ enabled_hints.SetIsEnabled(
+ static_cast<mojom::WebClientHintsType>(i),
+ accept_client_hints_header.Contains(kClientHintsHeaderMapping[i]));
+ }
+
+ enabled_hints.SetIsEnabled(
+ mojom::WebClientHintsType::kDeviceMemory,
+ enabled_hints.IsEnabled(mojom::WebClientHintsType::kDeviceMemory) &&
+ RuntimeEnabledFeatures::DeviceMemoryHeaderEnabled());
+
+ enabled_hints.SetIsEnabled(
+ mojom::WebClientHintsType::kRtt,
+ enabled_hints.IsEnabled(mojom::WebClientHintsType::kRtt) &&
+ RuntimeEnabledFeatures::NetInfoRttHeaderEnabled());
+
+ enabled_hints.SetIsEnabled(
+ mojom::WebClientHintsType::kDownlink,
+ enabled_hints.IsEnabled(mojom::WebClientHintsType::kDownlink) &&
+ RuntimeEnabledFeatures::NetInfoDownlinkHeaderEnabled());
+
+ enabled_hints.SetIsEnabled(
+ mojom::WebClientHintsType::kEct,
+ enabled_hints.IsEnabled(mojom::WebClientHintsType::kEct) &&
+ RuntimeEnabledFeatures::NetInfoEffectiveTypeHeaderEnabled());
+}
+
+} // namespace
+
+ClientHintsPreferences::ClientHintsPreferences() {
+ DCHECK_EQ(static_cast<size_t>(mojom::WebClientHintsType::kMaxValue) + 1,
+ kClientHintsHeaderMappingCount);
+}
+
+void ClientHintsPreferences::UpdateFrom(
+ const ClientHintsPreferences& preferences) {
+ for (size_t i = 0;
+ i < static_cast<int>(mojom::WebClientHintsType::kMaxValue) + 1; ++i) {
+ mojom::WebClientHintsType type = static_cast<mojom::WebClientHintsType>(i);
+ enabled_hints_.SetIsEnabled(type, preferences.ShouldSend(type));
+ }
+}
+
+void ClientHintsPreferences::UpdateFromAcceptClientHintsHeader(
+ const String& header_value,
+ const KURL& url,
+ Context* context) {
+ if (header_value.IsEmpty())
+ return;
+
+ // If the persistent client hint feature is enabled, then client hints
+ // should be allowed only on secure URLs.
+ if (blink::RuntimeEnabledFeatures::ClientHintsPersistentEnabled() &&
+ !IsClientHintsAllowed(url)) {
+ return;
+ }
+
+ WebEnabledClientHints new_enabled_types;
+
+ ParseAcceptChHeader(header_value, new_enabled_types);
+
+ for (size_t i = 0;
+ i < static_cast<int>(mojom::WebClientHintsType::kMaxValue) + 1; ++i) {
+ mojom::WebClientHintsType type = static_cast<mojom::WebClientHintsType>(i);
+ enabled_hints_.SetIsEnabled(type, enabled_hints_.IsEnabled(type) ||
+ new_enabled_types.IsEnabled(type));
+ }
+
+ if (context) {
+ for (size_t i = 0;
+ i < static_cast<int>(mojom::WebClientHintsType::kMaxValue) + 1; ++i) {
+ mojom::WebClientHintsType type =
+ static_cast<mojom::WebClientHintsType>(i);
+ if (enabled_hints_.IsEnabled(type))
+ context->CountClientHints(type);
+ }
+ }
+}
+
+// static
+void ClientHintsPreferences::UpdatePersistentHintsFromHeaders(
+ const ResourceResponse& response,
+ Context* context,
+ WebEnabledClientHints& enabled_hints,
+ TimeDelta* persist_duration) {
+ *persist_duration = base::TimeDelta();
+
+ if (response.WasCached())
+ return;
+
+ String accept_ch_header_value =
+ response.HttpHeaderField(HTTPNames::Accept_CH);
+ String accept_ch_lifetime_header_value =
+ response.HttpHeaderField(HTTPNames::Accept_CH_Lifetime);
+
+ if (!RuntimeEnabledFeatures::ClientHintsPersistentEnabled() ||
+ accept_ch_header_value.IsEmpty() ||
+ accept_ch_lifetime_header_value.IsEmpty()) {
+ return;
+ }
+
+ const KURL url = response.Url();
+ if (!IsClientHintsAllowed(url))
+ return;
+
+ bool conversion_ok = false;
+ int64_t persist_duration_seconds =
+ accept_ch_lifetime_header_value.ToInt64Strict(&conversion_ok);
+ if (!conversion_ok || persist_duration_seconds <= 0)
+ return;
+
+ *persist_duration = TimeDelta::FromSeconds(persist_duration_seconds);
+ if (context)
+ context->CountPersistentClientHintHeaders();
+
+ ParseAcceptChHeader(accept_ch_header_value, enabled_hints);
+}
+
+// static
+bool ClientHintsPreferences::IsClientHintsAllowed(const KURL& url) {
+ return (url.ProtocolIs("http") || url.ProtocolIs("https")) &&
+ (SecurityOrigin::IsSecure(url) ||
+ SecurityOrigin::Create(url)->IsLocalhost());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.h b/chromium/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.h
new file mode 100644
index 00000000000..6900745d56e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.h
@@ -0,0 +1,77 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_CLIENT_HINTS_PREFERENCES_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_CLIENT_HINTS_PREFERENCES_H_
+
+#include "third_party/blink/public/platform/web_client_hints_type.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+class KURL;
+class ResourceResponse;
+
+// TODO (tbansal): Remove PLATFORM_EXPORT, and pass WebClientHintsType
+// everywhere.
+class PLATFORM_EXPORT ClientHintsPreferences {
+ DISALLOW_NEW();
+
+ public:
+ class Context {
+ public:
+ virtual void CountClientHints(mojom::WebClientHintsType) = 0;
+ virtual void CountPersistentClientHintHeaders() = 0;
+
+ protected:
+ virtual ~Context() = default;
+ };
+
+ ClientHintsPreferences();
+
+ void UpdateFrom(const ClientHintsPreferences&);
+
+ // Parses the client hints headers, and populates |this| with the client hint
+ // preferences. |url| is the URL of the resource whose response included the
+ // |header_value|. |context| may be null. If client hints are not allowed for
+ // |url|, then |this| would not be updated.
+ void UpdateFromAcceptClientHintsHeader(const String& header_value,
+ const KURL&,
+ Context*);
+
+ bool ShouldSend(mojom::WebClientHintsType type) const {
+ return enabled_hints_.IsEnabled(type);
+ }
+ void SetShouldSendForTesting(mojom::WebClientHintsType type) {
+ enabled_hints_.SetIsEnabled(type, true);
+ }
+
+ // Parses the client hints headers, and populates |enabled_hints| with the
+ // client hint preferences that should be persisted for |persist_duration|.
+ // |persist_duration| should be non-null.
+ // If there are no client hints that need to be persisted,
+ // |persist_duration| is not set, otherwise it is set to the duration for
+ // which the client hint preferences should be persisted.
+ // UpdatePersistentHintsFromHeaders may be called for all responses
+ // received (including subresources). |context| may be null.
+ static void UpdatePersistentHintsFromHeaders(
+ const ResourceResponse&,
+ Context*,
+ WebEnabledClientHints& enabled_hints,
+ TimeDelta* persist_duration);
+
+ // Returns true if client hints are allowed for the provided KURL. Client
+ // hints are allowed only on HTTP URLs that belong to secure contexts.
+ static bool IsClientHintsAllowed(const KURL&);
+
+ private:
+ WebEnabledClientHints enabled_hints_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences_test.cc
new file mode 100644
index 00000000000..dde2ec04ca4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences_test.cc
@@ -0,0 +1,158 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/web_runtime_features.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
+#include "third_party/blink/renderer/platform/network/http_names.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+class ClientHintsPreferencesTest : public testing::Test {};
+
+TEST_F(ClientHintsPreferencesTest, BasicSecure) {
+ struct TestCase {
+ const char* header_value;
+ bool expectation_resource_width;
+ bool expectation_dpr;
+ bool expectation_viewport_width;
+ bool expectation_rtt;
+ bool expectation_downlink;
+ bool expectation_ect;
+ } cases[] = {
+ {"width, dpr, viewportWidth", true, true, false, false, false, false},
+ {"WiDtH, dPr, viewport-width, rtt, downlink, ect", true, true, true, true,
+ true, true},
+ {"WiDtH, dPr, viewport-width, rtt, downlink, effective-connection-type",
+ true, true, true, true, true, false},
+ {"WIDTH, DPR, VIWEPROT-Width", true, true, false, false, false, false},
+ {"VIewporT-Width, wutwut, width", true, false, true, false, false, false},
+ {"dprw", false, false, false, false, false, false},
+ {"DPRW", false, false, false, false, false, false},
+ };
+
+ for (const auto& test_case : cases) {
+ ClientHintsPreferences preferences;
+ const KURL kurl(String::FromUTF8("https://www.google.com/"));
+ preferences.UpdateFromAcceptClientHintsHeader(test_case.header_value, kurl,
+ nullptr);
+ EXPECT_EQ(
+ test_case.expectation_resource_width,
+ preferences.ShouldSend(mojom::WebClientHintsType::kResourceWidth));
+ EXPECT_EQ(test_case.expectation_dpr,
+ preferences.ShouldSend(mojom::WebClientHintsType::kDpr));
+ EXPECT_EQ(
+ test_case.expectation_viewport_width,
+ preferences.ShouldSend(mojom::WebClientHintsType::kViewportWidth));
+ EXPECT_EQ(test_case.expectation_rtt,
+ preferences.ShouldSend(mojom::WebClientHintsType::kRtt));
+ EXPECT_EQ(test_case.expectation_downlink,
+ preferences.ShouldSend(mojom::WebClientHintsType::kDownlink));
+ EXPECT_EQ(test_case.expectation_ect,
+ preferences.ShouldSend(mojom::WebClientHintsType::kEct));
+
+ // Calling UpdateFromAcceptClientHintsHeader with empty header should have
+ // no impact on client hint preferences.
+ preferences.UpdateFromAcceptClientHintsHeader("", kurl, nullptr);
+ EXPECT_EQ(
+ test_case.expectation_resource_width,
+ preferences.ShouldSend(mojom::WebClientHintsType::kResourceWidth));
+ EXPECT_EQ(test_case.expectation_dpr,
+ preferences.ShouldSend(mojom::WebClientHintsType::kDpr));
+ EXPECT_EQ(
+ test_case.expectation_viewport_width,
+ preferences.ShouldSend(mojom::WebClientHintsType::kViewportWidth));
+
+ // Calling UpdateFromAcceptClientHintsHeader with an invalid header should
+ // have no impact on client hint preferences.
+ preferences.UpdateFromAcceptClientHintsHeader("foobar", kurl, nullptr);
+ EXPECT_EQ(
+ test_case.expectation_resource_width,
+ preferences.ShouldSend(mojom::WebClientHintsType::kResourceWidth));
+ EXPECT_EQ(test_case.expectation_dpr,
+ preferences.ShouldSend(mojom::WebClientHintsType::kDpr));
+ EXPECT_EQ(
+ test_case.expectation_viewport_width,
+ preferences.ShouldSend(mojom::WebClientHintsType::kViewportWidth));
+ }
+}
+
+TEST_F(ClientHintsPreferencesTest, Insecure) {
+ for (const auto& use_secure_url : {false, true}) {
+ ClientHintsPreferences preferences;
+ const KURL kurl = use_secure_url
+ ? KURL(String::FromUTF8("https://www.google.com/"))
+ : KURL(String::FromUTF8("http://www.google.com/"));
+ preferences.UpdateFromAcceptClientHintsHeader("dpr", kurl, nullptr);
+ EXPECT_EQ(use_secure_url,
+ preferences.ShouldSend(mojom::WebClientHintsType::kDpr));
+ }
+}
+
+TEST_F(ClientHintsPreferencesTest, PersistentHints) {
+ struct TestCase {
+ bool enable_persistent_runtime_feature;
+ const char* accept_ch_header_value;
+ const char* accept_lifetime_header_value;
+ int64_t expect_persist_duration_seconds;
+ } test_cases[] = {
+ {true, "width, dpr, viewportWidth", "", 0},
+ {true, "width, dpr, viewportWidth", "-1000", 0},
+ {true, "width, dpr, viewportWidth", "1000s", 0},
+ {true, "width, dpr, viewportWidth", "1000.5", 0},
+ {false, "width, dpr, viewportWidth", "1000", 0},
+ {true, "width, dpr, rtt, downlink, ect", "1000", 1000},
+ };
+
+ for (const auto& test : test_cases) {
+ WebRuntimeFeatures::EnableClientHintsPersistent(
+ test.enable_persistent_runtime_feature);
+ WebEnabledClientHints enabled_types;
+ TimeDelta persist_duration;
+
+ const KURL kurl(String::FromUTF8("https://www.google.com/"));
+
+ ResourceResponse response(kurl);
+ response.SetHTTPHeaderField(HTTPNames::Accept_CH,
+ test.accept_ch_header_value);
+ response.SetHTTPHeaderField(HTTPNames::Accept_CH_Lifetime,
+ test.accept_lifetime_header_value);
+
+ ClientHintsPreferences::UpdatePersistentHintsFromHeaders(
+ response, nullptr, enabled_types, &persist_duration);
+ EXPECT_EQ(test.expect_persist_duration_seconds,
+ persist_duration.InSeconds());
+ if (test.expect_persist_duration_seconds > 0) {
+ EXPECT_FALSE(
+ enabled_types.IsEnabled(mojom::WebClientHintsType::kDeviceMemory));
+ EXPECT_TRUE(enabled_types.IsEnabled(mojom::WebClientHintsType::kDpr));
+ EXPECT_TRUE(
+ enabled_types.IsEnabled(mojom::WebClientHintsType::kResourceWidth));
+ EXPECT_FALSE(
+ enabled_types.IsEnabled(mojom::WebClientHintsType::kViewportWidth));
+ EXPECT_TRUE(enabled_types.IsEnabled(mojom::WebClientHintsType::kRtt));
+ EXPECT_TRUE(
+ enabled_types.IsEnabled(mojom::WebClientHintsType::kDownlink));
+ EXPECT_TRUE(enabled_types.IsEnabled(mojom::WebClientHintsType::kEct));
+ } else {
+ EXPECT_FALSE(
+ enabled_types.IsEnabled(mojom::WebClientHintsType::kDeviceMemory));
+ EXPECT_FALSE(enabled_types.IsEnabled(mojom::WebClientHintsType::kDpr));
+ EXPECT_FALSE(
+ enabled_types.IsEnabled(mojom::WebClientHintsType::kResourceWidth));
+ EXPECT_FALSE(
+ enabled_types.IsEnabled(mojom::WebClientHintsType::kViewportWidth));
+ EXPECT_FALSE(enabled_types.IsEnabled(mojom::WebClientHintsType::kRtt));
+ EXPECT_FALSE(
+ enabled_types.IsEnabled(mojom::WebClientHintsType::kDownlink));
+ EXPECT_FALSE(enabled_types.IsEnabled(mojom::WebClientHintsType::kEct));
+ }
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_context.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_context.cc
new file mode 100644
index 00000000000..ab5e1ae5900
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_context.cc
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/renderer/platform/loader/fetch/fetch_context.h"
+
+#include "third_party/blink/renderer/platform/PlatformProbeSink.h"
+#include "third_party/blink/renderer/platform/probe/PlatformTraceEventsAgent.h"
+
+namespace blink {
+
+FetchContext& FetchContext::NullInstance() {
+ return *(new FetchContext);
+}
+
+FetchContext::FetchContext() : platform_probe_sink_(new PlatformProbeSink) {
+ platform_probe_sink_->addPlatformTraceEventsAgent(
+ new PlatformTraceEventsAgent);
+}
+
+void FetchContext::Trace(blink::Visitor* visitor) {
+ visitor->Trace(platform_probe_sink_);
+}
+
+void FetchContext::DispatchDidChangeResourcePriority(unsigned long,
+ ResourceLoadPriority,
+ int) {}
+
+void FetchContext::AddAdditionalRequestHeaders(ResourceRequest&,
+ FetchResourceType) {}
+
+mojom::FetchCacheMode FetchContext::ResourceRequestCachePolicy(
+ const ResourceRequest&,
+ Resource::Type,
+ FetchParameters::DeferOption defer) const {
+ return mojom::FetchCacheMode::kDefault;
+}
+
+void FetchContext::PrepareRequest(ResourceRequest&, RedirectType) {}
+
+void FetchContext::DispatchWillSendRequest(unsigned long,
+ ResourceRequest&,
+ const ResourceResponse&,
+ Resource::Type,
+ const FetchInitiatorInfo&) {}
+
+void FetchContext::DispatchDidLoadResourceFromMemoryCache(
+ unsigned long,
+ const ResourceRequest&,
+ const ResourceResponse&) {}
+
+void FetchContext::DispatchDidReceiveResponse(
+ unsigned long,
+ const ResourceResponse&,
+ network::mojom::RequestContextFrameType FrameType,
+ WebURLRequest::RequestContext,
+ Resource*,
+ ResourceResponseType) {}
+
+void FetchContext::DispatchDidReceiveData(unsigned long, const char*, int) {}
+
+void FetchContext::DispatchDidReceiveEncodedData(unsigned long, int) {}
+
+void FetchContext::DispatchDidDownloadData(unsigned long, int, int) {}
+
+void FetchContext::DispatchDidDownloadToBlob(unsigned long identifier,
+ BlobDataHandle*) {}
+
+void FetchContext::DispatchDidFinishLoading(unsigned long,
+ double,
+ int64_t,
+ int64_t,
+ bool) {}
+
+void FetchContext::DispatchDidFail(const KURL&,
+ unsigned long,
+ const ResourceError&,
+ int64_t,
+ bool) {}
+
+void FetchContext::RecordLoadingActivity(
+ const ResourceRequest&,
+ Resource::Type,
+ const AtomicString& fetch_initiator_name) {}
+
+void FetchContext::DidLoadResource(Resource*) {}
+
+void FetchContext::AddResourceTiming(const ResourceTimingInfo&) {}
+
+void FetchContext::AddWarningConsoleMessage(const String&, LogSource) const {}
+
+void FetchContext::AddErrorConsoleMessage(const String&, LogSource) const {}
+
+void FetchContext::PopulateResourceRequest(
+ Resource::Type,
+ const ClientHintsPreferences&,
+ const FetchParameters::ResourceWidth&,
+ ResourceRequest&) {}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_context.h b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_context.h
new file mode 100644
index 00000000000..600b89d2900
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_context.h
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_FETCH_CONTEXT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_FETCH_CONTEXT_H_
+
+#include "services/network/public/mojom/request_context_frame_type.mojom-shared.h"
+#include "third_party/blink/public/platform/modules/fetch/fetch_api_request.mojom-shared.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_application_cache_host.h"
+#include "third_party/blink/public/platform/web_url_loader.h"
+#include "third_party/blink/public/platform/web_url_request.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_initiator_info.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_load_priority.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
+#include "third_party/blink/renderer/platform/network/content_security_policy_parsers.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
+#include "third_party/blink/renderer/platform/weborigin/security_violation_reporting_policy.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class ClientHintsPreferences;
+class KURL;
+class MHTMLArchive;
+class PlatformProbeSink;
+class ResourceError;
+class ResourceResponse;
+class ResourceTimingInfo;
+
+enum FetchResourceType { kFetchMainResource, kFetchSubresource };
+
+// The FetchContext is an interface for performing context specific processing
+// in response to events in the ResourceFetcher. The ResourceFetcher or its job
+// class, ResourceLoader, may call the methods on a FetchContext.
+//
+// Any processing that depends on components outside platform/loader/fetch/
+// should be implemented on a subclass of this interface, and then exposed to
+// the ResourceFetcher via this interface.
+class PLATFORM_EXPORT FetchContext
+ : public GarbageCollectedFinalized<FetchContext> {
+ WTF_MAKE_NONCOPYABLE(FetchContext);
+
+ public:
+ // This enum corresponds to blink::MessageSource. We have this not to
+ // introduce any dependency to core/.
+ //
+ // Currently only kJSMessageSource is used, but not to impress readers that
+ // AddConsoleMessage() call from FetchContext() should always use it, which is
+ // not true, we ask users of the Add.*ConsoleMessage() methods to explicitly
+ // specify the MessageSource to use.
+ //
+ // Extend this when needed.
+ enum LogSource { kJSSource };
+
+ static FetchContext& NullInstance();
+
+ virtual ~FetchContext() = default;
+
+ virtual void Trace(blink::Visitor*);
+
+ virtual bool IsFrameFetchContext() { return false; }
+
+ virtual void AddAdditionalRequestHeaders(ResourceRequest&, FetchResourceType);
+
+ // Called when the ResourceFetcher observes a data: URI load that contains an
+ // octothorpe ('#') character. This is a temporary method to support an Intent
+ // to Deprecate for spec incompliant handling of '#' characters in data URIs.
+ //
+ // TODO(crbug.com/123004): Remove once we have enough data for the I2D.
+ virtual void RecordDataUriWithOctothorpe() {}
+
+ // Returns the cache policy for the resource. ResourceRequest is not passed as
+ // a const reference as a header needs to be added for doc.write blocking
+ // intervention.
+ virtual mojom::FetchCacheMode ResourceRequestCachePolicy(
+ const ResourceRequest&,
+ Resource::Type,
+ FetchParameters::DeferOption) const;
+
+ virtual void DispatchDidChangeResourcePriority(unsigned long identifier,
+ ResourceLoadPriority,
+ int intra_priority_value);
+
+ // This internally dispatches WebFrameClient::willSendRequest and hooks
+ // request interceptors like ServiceWorker and ApplicationCache.
+ // This may modify the request.
+ enum class RedirectType { kForRedirect, kNotForRedirect };
+ virtual void PrepareRequest(ResourceRequest&, RedirectType);
+
+ // The last callback before a request is actually sent to the browser process.
+ // TODO(https://crbug.com/632580): make this take const ResourceRequest&.
+ virtual void DispatchWillSendRequest(
+ unsigned long identifier,
+ ResourceRequest&,
+ const ResourceResponse& redirect_response,
+ Resource::Type,
+ const FetchInitiatorInfo& = FetchInitiatorInfo());
+ virtual void DispatchDidLoadResourceFromMemoryCache(unsigned long identifier,
+ const ResourceRequest&,
+ const ResourceResponse&);
+ enum class ResourceResponseType { kNotFromMemoryCache, kFromMemoryCache };
+ virtual void DispatchDidReceiveResponse(
+ unsigned long identifier,
+ const ResourceResponse&,
+ network::mojom::RequestContextFrameType,
+ WebURLRequest::RequestContext,
+ Resource*,
+ ResourceResponseType);
+ virtual void DispatchDidReceiveData(unsigned long identifier,
+ const char* data,
+ int data_length);
+ virtual void DispatchDidReceiveEncodedData(unsigned long identifier,
+ int encoded_data_length);
+ virtual void DispatchDidDownloadData(unsigned long identifier,
+ int data_length,
+ int encoded_data_length);
+ virtual void DispatchDidDownloadToBlob(unsigned long identifier,
+ BlobDataHandle*);
+ virtual void DispatchDidFinishLoading(unsigned long identifier,
+ double finish_time,
+ int64_t encoded_data_length,
+ int64_t decoded_body_length,
+ bool blocked_cross_site_document);
+ virtual void DispatchDidFail(const KURL&,
+ unsigned long identifier,
+ const ResourceError&,
+ int64_t encoded_data_length,
+ bool is_internal_request);
+
+ virtual bool ShouldLoadNewResource(Resource::Type) const { return false; }
+
+ // Called when a resource load is first requested, which may not be when the
+ // load actually begins.
+ virtual void RecordLoadingActivity(const ResourceRequest&,
+ Resource::Type,
+ const AtomicString& fetch_initiator_name);
+
+ virtual void DidLoadResource(Resource*);
+
+ virtual void AddResourceTiming(const ResourceTimingInfo&);
+ virtual bool AllowImage(bool, const KURL&) const { return false; }
+ virtual ResourceRequestBlockedReason CanRequest(
+ Resource::Type,
+ const ResourceRequest&,
+ const KURL&,
+ const ResourceLoaderOptions&,
+ SecurityViolationReportingPolicy,
+ FetchParameters::OriginRestriction,
+ ResourceRequest::RedirectStatus) const {
+ return ResourceRequestBlockedReason::kOther;
+ }
+ virtual ResourceRequestBlockedReason CheckCSPForRequest(
+ WebURLRequest::RequestContext,
+ const KURL&,
+ const ResourceLoaderOptions&,
+ SecurityViolationReportingPolicy,
+ ResourceRequest::RedirectStatus) const {
+ return ResourceRequestBlockedReason::kOther;
+ }
+ virtual ResourceRequestBlockedReason CheckResponseNosniff(
+ WebURLRequest::RequestContext,
+ const ResourceResponse&) const {
+ return ResourceRequestBlockedReason::kOther;
+ }
+
+ virtual bool IsControlledByServiceWorker() const { return false; }
+ virtual int64_t ServiceWorkerID() const { return -1; }
+ virtual int ApplicationCacheHostID() const {
+ return WebApplicationCacheHost::kAppCacheNoHostId;
+ }
+
+ virtual bool IsMainFrame() const { return true; }
+ virtual bool DefersLoading() const { return false; }
+ virtual bool IsLoadComplete() const { return false; }
+ virtual bool UpdateTimingInfoForIFrameNavigation(ResourceTimingInfo*) {
+ return false;
+ }
+
+ virtual void AddWarningConsoleMessage(const String&, LogSource) const;
+ virtual void AddErrorConsoleMessage(const String&, LogSource) const;
+
+ virtual const SecurityOrigin* GetSecurityOrigin() const { return nullptr; }
+
+ // Populates the ResourceRequest using the given values and information
+ // stored in the FetchContext implementation. Used by ResourceFetcher to
+ // prepare a ResourceRequest instance at the start of resource loading.
+ virtual void PopulateResourceRequest(Resource::Type,
+ const ClientHintsPreferences&,
+ const FetchParameters::ResourceWidth&,
+ ResourceRequest&);
+
+ virtual MHTMLArchive* Archive() const { return nullptr; }
+
+ PlatformProbeSink* GetPlatformProbeSink() const {
+ return platform_probe_sink_;
+ }
+
+ virtual std::unique_ptr<WebURLLoader> CreateURLLoader(
+ const ResourceRequest&,
+ scoped_refptr<base::SingleThreadTaskRunner>,
+ const ResourceLoaderOptions&) {
+ NOTREACHED();
+ return nullptr;
+ }
+
+ // Returns the initial throttling policy used by the associated
+ // ResourceLoadScheduler.
+ virtual ResourceLoadScheduler::ThrottlingPolicy InitialLoadThrottlingPolicy()
+ const {
+ return ResourceLoadScheduler::ThrottlingPolicy::kNormal;
+ }
+
+ virtual bool IsDetached() const { return false; }
+
+ // Obtains FrameScheduler instance that is used in the attached frame.
+ // May return nullptr if a frame is not attached or detached.
+ virtual FrameScheduler* GetFrameScheduler() const { return nullptr; }
+
+ // Returns a task runner intended for loading tasks. Should work even in a
+ // worker context, where FrameScheduler doesn't exist, but the returned
+ // base::SingleThreadTaskRunner will not work after the context detaches
+ // (after Detach() is called, this will return a generic timer suitable for
+ // post-detach actions like keepalive requests.
+ virtual scoped_refptr<base::SingleThreadTaskRunner> GetLoadingTaskRunner() {
+ return Platform::Current()->CurrentThread()->GetTaskRunner();
+ }
+
+ // Called when the underlying context is detached. Note that some
+ // FetchContexts continue working after detached (e.g., for fetch() operations
+ // with "keepalive" specified).
+ // Returns a "detached" fetch context which can be null.
+ virtual FetchContext* Detach() { return nullptr; }
+
+ // Returns the updated priority of the resource based on the experiments that
+ // may be currently enabled.
+ virtual ResourceLoadPriority ModifyPriorityForExperiments(
+ ResourceLoadPriority priority) const {
+ return priority;
+ }
+
+ // Returns if the |resource_url| is identified as ad.
+ virtual bool IsAdResource(
+ const KURL& resource_url,
+ Resource::Type type,
+ WebURLRequest::RequestContext request_context) const {
+ return false;
+ }
+
+ protected:
+ FetchContext();
+
+ private:
+ Member<PlatformProbeSink> platform_probe_sink_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_initiator_info.h b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_initiator_info.h
new file mode 100644
index 00000000000..8d471afcb18
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_initiator_info.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2013 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_FETCH_INITIATOR_INFO_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_FETCH_INITIATOR_INFO_H_
+
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_position.h"
+
+namespace blink {
+
+struct FetchInitiatorInfo {
+ DISALLOW_NEW();
+ FetchInitiatorInfo()
+ : name(),
+ position(TextPosition::BelowRangePosition()),
+ start_time(0.0),
+ is_link_preload(false) {}
+
+ // ATTENTION: When adding members, update CrossThreadFetchInitiatorInfoData,
+ // too.
+ AtomicString name;
+ TextPosition position;
+ double start_time;
+ bool is_link_preload;
+ String imported_module_referrer;
+};
+
+// Encode AtomicString as String to cross threads.
+struct CrossThreadFetchInitiatorInfoData {
+ DISALLOW_NEW();
+ explicit CrossThreadFetchInitiatorInfoData(const FetchInitiatorInfo& info)
+ : name(info.name.GetString().IsolatedCopy()),
+ position(info.position),
+ start_time(info.start_time),
+ is_link_preload(info.is_link_preload),
+ imported_module_referrer(info.imported_module_referrer.IsolatedCopy()) {
+ }
+
+ operator FetchInitiatorInfo() const {
+ FetchInitiatorInfo info;
+ info.name = AtomicString(name);
+ info.position = position;
+ info.start_time = start_time;
+ info.is_link_preload = is_link_preload;
+ info.imported_module_referrer = imported_module_referrer;
+ return info;
+ }
+
+ String name;
+ TextPosition position;
+ double start_time;
+ bool is_link_preload;
+ String imported_module_referrer;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_initiator_type_names.json5 b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_initiator_type_names.json5
new file mode 100644
index 00000000000..d2d2aac3a55
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_initiator_type_names.json5
@@ -0,0 +1,22 @@
+{
+ metadata: {
+ namespace: "FetchInitiatorType",
+ export: "PLATFORM_EXPORT",
+ },
+
+ data: [
+ "beacon",
+ "css",
+ "document",
+ "icon",
+ "internal",
+ "link",
+ "ping",
+ "processinginstruction",
+ "texttrack",
+ "uacss",
+ "violationreport",
+ "xml",
+ "xmlhttprequest",
+ ],
+}
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.cc
new file mode 100644
index 00000000000..88a956d43af
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.cc
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2012 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h"
+
+#include <memory>
+
+#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+
+namespace blink {
+
+FetchParameters::FetchParameters(const ResourceRequest& resource_request)
+ : resource_request_(resource_request),
+ decoder_options_(TextResourceDecoderOptions::kPlainTextContent),
+ speculative_preload_type_(SpeculativePreloadType::kNotSpeculative),
+ preload_discovery_time_(0.0),
+ defer_(kNoDefer),
+ origin_restriction_(kUseDefaultOriginRestrictionForType),
+ placeholder_image_request_type_(kDisallowPlaceholder) {}
+
+FetchParameters::FetchParameters(
+ std::unique_ptr<CrossThreadFetchParametersData> data)
+ : resource_request_(data->resource_request.get()),
+ decoder_options_(data->decoder_options),
+ options_(data->options),
+ speculative_preload_type_(data->speculative_preload_type),
+ preload_discovery_time_(data->preload_discovery_time),
+ defer_(data->defer),
+ origin_restriction_(data->origin_restriction),
+ resource_width_(data->resource_width),
+ client_hint_preferences_(data->client_hint_preferences),
+ placeholder_image_request_type_(data->placeholder_image_request_type) {}
+
+FetchParameters::FetchParameters(const ResourceRequest& resource_request,
+ const ResourceLoaderOptions& options)
+ : resource_request_(resource_request),
+ decoder_options_(TextResourceDecoderOptions::kPlainTextContent),
+ options_(options),
+ speculative_preload_type_(SpeculativePreloadType::kNotSpeculative),
+ preload_discovery_time_(0.0),
+ defer_(kNoDefer),
+ origin_restriction_(kUseDefaultOriginRestrictionForType),
+ placeholder_image_request_type_(kDisallowPlaceholder) {}
+
+FetchParameters::~FetchParameters() = default;
+
+void FetchParameters::SetCrossOriginAccessControl(
+ const SecurityOrigin* origin,
+ CrossOriginAttributeValue cross_origin) {
+ switch (cross_origin) {
+ case kCrossOriginAttributeNotSet:
+ NOTREACHED();
+ break;
+ case kCrossOriginAttributeAnonymous:
+ SetCrossOriginAccessControl(
+ origin, network::mojom::FetchCredentialsMode::kSameOrigin);
+ break;
+ case kCrossOriginAttributeUseCredentials:
+ SetCrossOriginAccessControl(
+ origin, network::mojom::FetchCredentialsMode::kInclude);
+ break;
+ }
+}
+
+void FetchParameters::SetCrossOriginAccessControl(
+ const SecurityOrigin* origin,
+ network::mojom::FetchCredentialsMode credentials_mode) {
+ // Currently FetchParametersMode is only used when the request goes to
+ // Service Worker.
+ resource_request_.SetFetchRequestMode(
+ network::mojom::FetchRequestMode::kCORS);
+ resource_request_.SetFetchCredentialsMode(credentials_mode);
+
+ options_.security_origin = origin;
+
+ // TODO: Credentials should be removed only when the request is cross origin.
+ resource_request_.RemoveUserAndPassFromURL();
+
+ if (origin)
+ resource_request_.SetHTTPOrigin(origin);
+}
+
+void FetchParameters::SetResourceWidth(ResourceWidth resource_width) {
+ if (resource_width.is_set) {
+ resource_width_.width = resource_width.width;
+ resource_width_.is_set = true;
+ }
+}
+
+void FetchParameters::SetSpeculativePreloadType(
+ SpeculativePreloadType speculative_preload_type,
+ double discovery_time) {
+ speculative_preload_type_ = speculative_preload_type;
+ preload_discovery_time_ = discovery_time;
+}
+
+void FetchParameters::MakeSynchronous() {
+ // Synchronous requests should always be max priority, lest they hang the
+ // renderer.
+ resource_request_.SetPriority(ResourceLoadPriority::kHighest);
+ if (resource_request_.TimeoutInterval() == INT_MAX) {
+ resource_request_.SetTimeoutInterval(10);
+ }
+ // Skip ServiceWorker for synchronous loads from the main thread to avoid
+ // deadlocks.
+ if (IsMainThread())
+ resource_request_.SetSkipServiceWorker(true);
+ options_.synchronous_policy = kRequestSynchronously;
+}
+
+void FetchParameters::SetAllowImagePlaceholder() {
+ DCHECK_EQ(kDisallowPlaceholder, placeholder_image_request_type_);
+ if (!resource_request_.Url().ProtocolIsInHTTPFamily() ||
+ resource_request_.HttpMethod() != "GET" ||
+ !resource_request_.HttpHeaderField("range").IsNull()) {
+ // Make sure that the request isn't marked as using Client Lo-Fi, since
+ // without loading an image placeholder, Client Lo-Fi isn't really in use.
+ resource_request_.SetPreviewsState(resource_request_.GetPreviewsState() &
+ ~(WebURLRequest::kClientLoFiOn));
+ return;
+ }
+
+ placeholder_image_request_type_ = kAllowPlaceholder;
+
+ // Fetch the first few bytes of the image. This number is tuned to both (a)
+ // likely capture the entire image for small images and (b) likely contain
+ // the dimensions for larger images.
+ // TODO(sclittle): Calculate the optimal value for this number.
+ resource_request_.SetHTTPHeaderField("range", "bytes=0-2047");
+
+ // TODO(sclittle): Indicate somehow (e.g. through a new request bit) to the
+ // embedder that it should return the full resource if the entire resource is
+ // fresh in the cache.
+}
+
+std::unique_ptr<CrossThreadFetchParametersData> FetchParameters::CopyData()
+ const {
+ auto data = std::make_unique<CrossThreadFetchParametersData>();
+ data->resource_request = resource_request_.CopyData();
+ data->decoder_options = decoder_options_;
+ data->options = CrossThreadResourceLoaderOptionsData(options_);
+ data->speculative_preload_type = speculative_preload_type_;
+ data->preload_discovery_time = preload_discovery_time_;
+ data->defer = defer_;
+ data->origin_restriction = origin_restriction_;
+ data->resource_width = resource_width_;
+ data->client_hint_preferences = client_hint_preferences_;
+ data->placeholder_image_request_type = placeholder_image_request_type_;
+ return data;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h
new file mode 100644
index 00000000000..4ed9aba1b11
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2012 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_FETCH_PARAMETERS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_FETCH_PARAMETERS_H_
+
+#include "third_party/blink/public/platform/web_url_request.h"
+#include "third_party/blink/renderer/platform/cross_origin_attribute_value.h"
+#include "third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.h"
+#include "third_party/blink/renderer/platform/loader/fetch/integrity_metadata.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
+#include "third_party/blink/renderer/platform/loader/fetch/text_resource_decoder_options.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
+
+namespace blink {
+
+class SecurityOrigin;
+struct CrossThreadFetchParametersData;
+
+// A FetchParameters is a "parameter object" for
+// ResourceFetcher::requestResource to avoid the method having too many
+// arguments.
+//
+// There are cases where we need to copy a FetchParameters across threads, and
+// CrossThreadFetchParametersData is a struct for the purpose. When you add a
+// member variable to this class, do not forget to add the corresponding
+// one in CrossThreadFetchParametersData and write copying logic.
+class PLATFORM_EXPORT FetchParameters {
+ DISALLOW_NEW();
+
+ public:
+ enum DeferOption { kNoDefer, kLazyLoad, kIdleLoad };
+ enum class SpeculativePreloadType {
+ kNotSpeculative,
+ kInDocument, // The request was discovered in the main document
+ kInserted // The request was discovered in a document.write()
+ };
+ enum OriginRestriction {
+ kUseDefaultOriginRestrictionForType,
+ kRestrictToSameOrigin,
+ kNoOriginRestriction
+ };
+ enum PlaceholderImageRequestType {
+ kDisallowPlaceholder = 0, // The requested image must not be a placeholder.
+ kAllowPlaceholder, // The image is allowed to be a placeholder.
+ };
+ struct ResourceWidth {
+ DISALLOW_NEW();
+ float width;
+ bool is_set;
+
+ ResourceWidth() : width(0), is_set(false) {}
+ };
+
+ explicit FetchParameters(const ResourceRequest&);
+ explicit FetchParameters(std::unique_ptr<CrossThreadFetchParametersData>);
+ FetchParameters(const ResourceRequest&, const ResourceLoaderOptions&);
+ ~FetchParameters();
+
+ ResourceRequest& MutableResourceRequest() { return resource_request_; }
+ const ResourceRequest& GetResourceRequest() const {
+ return resource_request_;
+ }
+ const KURL& Url() const { return resource_request_.Url(); }
+
+ void SetRequestContext(WebURLRequest::RequestContext context) {
+ resource_request_.SetRequestContext(context);
+ }
+
+ const TextResourceDecoderOptions& DecoderOptions() const {
+ return decoder_options_;
+ }
+ void SetDecoderOptions(const TextResourceDecoderOptions& decoder_options) {
+ decoder_options_ = decoder_options;
+ }
+ void OverrideContentType(
+ TextResourceDecoderOptions::ContentType content_type) {
+ decoder_options_.OverrideContentType(content_type);
+ }
+ void SetCharset(const WTF::TextEncoding& charset) {
+ SetDecoderOptions(TextResourceDecoderOptions(
+ TextResourceDecoderOptions::kPlainTextContent, charset));
+ }
+
+ ResourceLoaderOptions& MutableOptions() { return options_; }
+ const ResourceLoaderOptions& Options() const { return options_; }
+
+ DeferOption Defer() const { return defer_; }
+ void SetDefer(DeferOption defer) { defer_ = defer; }
+
+ ResourceWidth GetResourceWidth() const { return resource_width_; }
+ void SetResourceWidth(ResourceWidth);
+
+ ClientHintsPreferences& GetClientHintsPreferences() {
+ return client_hint_preferences_;
+ }
+
+ bool IsSpeculativePreload() const {
+ return speculative_preload_type_ != SpeculativePreloadType::kNotSpeculative;
+ }
+ SpeculativePreloadType GetSpeculativePreloadType() const {
+ return speculative_preload_type_;
+ }
+ void SetSpeculativePreloadType(SpeculativePreloadType,
+ double discovery_time = 0);
+
+ double PreloadDiscoveryTime() const { return preload_discovery_time_; }
+
+ bool IsLinkPreload() const { return options_.initiator_info.is_link_preload; }
+ void SetLinkPreload(bool is_link_preload) {
+ options_.initiator_info.is_link_preload = is_link_preload;
+ }
+
+ void SetContentSecurityCheck(
+ ContentSecurityPolicyDisposition content_security_policy_option) {
+ options_.content_security_policy_option = content_security_policy_option;
+ }
+ // Configures the request to use the "cors" mode and the credentials mode
+ // specified by the crossOrigin attribute.
+ void SetCrossOriginAccessControl(const SecurityOrigin*,
+ CrossOriginAttributeValue);
+ // Configures the request to use the "cors" mode and the specified
+ // credentials mode.
+ void SetCrossOriginAccessControl(const SecurityOrigin*,
+ network::mojom::FetchCredentialsMode);
+ OriginRestriction GetOriginRestriction() const { return origin_restriction_; }
+ void SetOriginRestriction(OriginRestriction restriction) {
+ origin_restriction_ = restriction;
+ }
+ const IntegrityMetadataSet IntegrityMetadata() const {
+ return options_.integrity_metadata;
+ }
+ void SetIntegrityMetadata(const IntegrityMetadataSet& metadata) {
+ options_.integrity_metadata = metadata;
+ }
+
+ String ContentSecurityPolicyNonce() const {
+ return options_.content_security_policy_nonce;
+ }
+ void SetContentSecurityPolicyNonce(const String& nonce) {
+ options_.content_security_policy_nonce = nonce;
+ }
+
+ void SetParserDisposition(ParserDisposition parser_disposition) {
+ options_.parser_disposition = parser_disposition;
+ }
+
+ void SetCacheAwareLoadingEnabled(
+ CacheAwareLoadingEnabled cache_aware_loading_enabled) {
+ options_.cache_aware_loading_enabled = cache_aware_loading_enabled;
+ }
+
+ void MakeSynchronous();
+
+ PlaceholderImageRequestType GetPlaceholderImageRequestType() const {
+ return placeholder_image_request_type_;
+ }
+
+ // Configures the request to load an image placeholder if the request is
+ // eligible (e.g. the url's protocol is HTTP, etc.). If this request is
+ // non-eligible, this method doesn't modify the ResourceRequest. Calling this
+ // method sets m_placeholderImageRequestType to the appropriate value.
+ void SetAllowImagePlaceholder();
+
+ // Gets a copy of the data suitable for passing to another thread.
+ std::unique_ptr<CrossThreadFetchParametersData> CopyData() const;
+
+ private:
+ ResourceRequest resource_request_;
+ // |decoder_options_|'s ContentType is set to |kPlainTextContent| in
+ // FetchParameters but is later overridden by ResourceFactory::ContentType()
+ // in ResourceFetcher::PrepareRequest() before actual use.
+ TextResourceDecoderOptions decoder_options_;
+ ResourceLoaderOptions options_;
+ SpeculativePreloadType speculative_preload_type_;
+ double preload_discovery_time_;
+ DeferOption defer_;
+ OriginRestriction origin_restriction_;
+ ResourceWidth resource_width_;
+ ClientHintsPreferences client_hint_preferences_;
+ PlaceholderImageRequestType placeholder_image_request_type_;
+};
+
+// This class is needed to copy a FetchParameters across threads, because it
+// has some members which cannot be transferred across threads (AtomicString
+// for example).
+// There are some rules / restrictions:
+// - This struct cannot contain an object that cannot be transferred across
+// threads (e.g., AtomicString)
+// - Non-simple members need explicit copying (e.g., String::IsolatedCopy,
+// KURL::Copy) rather than the copy constructor or the assignment operator.
+struct CrossThreadFetchParametersData {
+ WTF_MAKE_NONCOPYABLE(CrossThreadFetchParametersData);
+ USING_FAST_MALLOC(CrossThreadFetchParametersData);
+
+ public:
+ CrossThreadFetchParametersData()
+ : decoder_options(TextResourceDecoderOptions::kPlainTextContent),
+ options(ResourceLoaderOptions()) {}
+
+ std::unique_ptr<CrossThreadResourceRequestData> resource_request;
+ TextResourceDecoderOptions decoder_options;
+ CrossThreadResourceLoaderOptionsData options;
+ FetchParameters::SpeculativePreloadType speculative_preload_type;
+ double preload_discovery_time;
+ FetchParameters::DeferOption defer;
+ FetchParameters::OriginRestriction origin_restriction;
+ FetchParameters::ResourceWidth resource_width;
+ ClientHintsPreferences client_hint_preferences;
+ FetchParameters::PlaceholderImageRequestType placeholder_image_request_type;
+};
+
+template <>
+struct CrossThreadCopier<FetchParameters> {
+ STATIC_ONLY(CrossThreadCopier);
+ using Type =
+ WTF::PassedWrapper<std::unique_ptr<CrossThreadFetchParametersData>>;
+ static Type Copy(const FetchParameters& fetch_params) {
+ return WTF::Passed(fetch_params.CopyData());
+ }
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_utils.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_utils.cc
new file mode 100644
index 00000000000..643ed618ac3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_utils.cc
@@ -0,0 +1,97 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_utils.h"
+
+#include "services/network/public/cpp/cors/cors.h"
+#include "third_party/blink/renderer/platform/loader/cors/cors.h"
+#include "third_party/blink/renderer/platform/network/http_header_map.h"
+#include "third_party/blink/renderer/platform/network/http_names.h"
+#include "third_party/blink/renderer/platform/network/http_parsers.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/threading.h"
+
+namespace blink {
+
+namespace {
+
+bool IsHTTPWhitespace(UChar chr) {
+ return chr == ' ' || chr == '\n' || chr == '\t' || chr == '\r';
+}
+
+} // namespace
+
+bool FetchUtils::IsForbiddenMethod(const String& method) {
+ // http://fetch.spec.whatwg.org/#forbidden-method
+ // "A forbidden method is a method that is a byte case-insensitive match"
+ // for one of `CONNECT`, `TRACE`, and `TRACK`."
+ return EqualIgnoringASCIICase(method, "TRACE") ||
+ EqualIgnoringASCIICase(method, "TRACK") ||
+ EqualIgnoringASCIICase(method, "CONNECT");
+}
+
+bool FetchUtils::IsForbiddenHeaderName(const String& name) {
+ const CString utf8_name = name.Utf8();
+ return network::cors::IsForbiddenHeader(
+ std::string(utf8_name.data(), utf8_name.length()));
+}
+
+bool FetchUtils::IsForbiddenResponseHeaderName(const String& name) {
+ // http://fetch.spec.whatwg.org/#forbidden-response-header-name
+ // "A forbidden response header name is a header name that is one of:
+ // `Set-Cookie`, `Set-Cookie2`"
+
+ return EqualIgnoringASCIICase(name, "set-cookie") ||
+ EqualIgnoringASCIICase(name, "set-cookie2");
+}
+
+AtomicString FetchUtils::NormalizeMethod(const AtomicString& method) {
+ // https://fetch.spec.whatwg.org/#concept-method-normalize
+
+ // We place GET and POST first because they are more commonly used than
+ // others.
+ const char* const kMethods[] = {
+ "GET", "POST", "DELETE", "HEAD", "OPTIONS", "PUT",
+ };
+
+ for (auto* const known : kMethods) {
+ if (EqualIgnoringASCIICase(method, known)) {
+ // Don't bother allocating a new string if it's already all
+ // uppercase.
+ return method == known ? method : known;
+ }
+ }
+ return method;
+}
+
+String FetchUtils::NormalizeHeaderValue(const String& value) {
+ // https://fetch.spec.whatwg.org/#concept-header-value-normalize
+ // Strip leading and trailing whitespace from header value.
+ // HTTP whitespace bytes are 0x09, 0x0A, 0x0D, and 0x20.
+
+ return value.StripWhiteSpace(IsHTTPWhitespace);
+}
+
+bool FetchUtils::ContainsOnlyCORSSafelistedHeaders(
+ const HTTPHeaderMap& header_map) {
+ for (const auto& header : header_map) {
+ if (!CORS::IsCORSSafelistedHeader(header.key, header.value))
+ return false;
+ }
+ return true;
+}
+
+bool FetchUtils::ContainsOnlyCORSSafelistedOrForbiddenHeaders(
+ const HTTPHeaderMap& header_map) {
+ for (const auto& header : header_map) {
+ if (!CORS::IsCORSSafelistedHeader(header.key, header.value) &&
+ !IsForbiddenHeaderName(header.key))
+ return false;
+ }
+ return true;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_utils.h b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_utils.h
new file mode 100644
index 00000000000..991ffcce318
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_utils.h
@@ -0,0 +1,38 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_FETCH_UTILS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_FETCH_UTILS_H_
+
+#include "third_party/blink/public/platform/web_url_request.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+class HTTPHeaderMap;
+
+class PLATFORM_EXPORT FetchUtils {
+ STATIC_ONLY(FetchUtils);
+
+ public:
+ static bool IsForbiddenMethod(const String& method);
+ static bool IsForbiddenHeaderName(const String& name);
+ static bool IsForbiddenResponseHeaderName(const String& name);
+ static AtomicString NormalizeMethod(const AtomicString& method);
+ static String NormalizeHeaderValue(const String& value);
+ static bool ContainsOnlyCORSSafelistedHeaders(const HTTPHeaderMap&);
+ static bool ContainsOnlyCORSSafelistedOrForbiddenHeaders(
+ const HTTPHeaderMap&);
+
+ // https://fetch.spec.whatwg.org/#ok-status aka a successful 2xx status
+ // code, https://tools.ietf.org/html/rfc7231#section-6.3 . We opt to use
+ // the Fetch term in naming the predicate.
+ static bool IsOkStatus(int status) { return status >= 200 && status < 300; }
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_utils_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_utils_test.cc
new file mode 100644
index 00000000000..c7619245046
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_utils_test.cc
@@ -0,0 +1,40 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_utils.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+namespace {
+
+TEST(FetchUtilsTest, NormalizeHeaderValue) {
+ EXPECT_EQ("t", FetchUtils::NormalizeHeaderValue(" t"));
+ EXPECT_EQ("t", FetchUtils::NormalizeHeaderValue("t "));
+ EXPECT_EQ("t", FetchUtils::NormalizeHeaderValue(" t "));
+ EXPECT_EQ("test", FetchUtils::NormalizeHeaderValue("test\r"));
+ EXPECT_EQ("test", FetchUtils::NormalizeHeaderValue("test\n"));
+ EXPECT_EQ("test", FetchUtils::NormalizeHeaderValue("test\r\n"));
+ EXPECT_EQ("test", FetchUtils::NormalizeHeaderValue("test\t"));
+ EXPECT_EQ("t t", FetchUtils::NormalizeHeaderValue("t t"));
+ EXPECT_EQ("t\tt", FetchUtils::NormalizeHeaderValue("t\tt"));
+ EXPECT_EQ("t\rt", FetchUtils::NormalizeHeaderValue("t\rt"));
+ EXPECT_EQ("t\nt", FetchUtils::NormalizeHeaderValue("t\nt"));
+ EXPECT_EQ("t\r\nt", FetchUtils::NormalizeHeaderValue("t\r\nt"));
+ EXPECT_EQ("test", FetchUtils::NormalizeHeaderValue("\rtest"));
+ EXPECT_EQ("test", FetchUtils::NormalizeHeaderValue("\ntest"));
+ EXPECT_EQ("test", FetchUtils::NormalizeHeaderValue("\r\ntest"));
+ EXPECT_EQ("test", FetchUtils::NormalizeHeaderValue("\ttest"));
+ EXPECT_EQ("", FetchUtils::NormalizeHeaderValue(""));
+ EXPECT_EQ("", FetchUtils::NormalizeHeaderValue(" "));
+ EXPECT_EQ("", FetchUtils::NormalizeHeaderValue("\r\n\r\n\r\n"));
+ EXPECT_EQ("\xd0\xa1", FetchUtils::NormalizeHeaderValue("\xd0\xa1"));
+ EXPECT_EQ("test", FetchUtils::NormalizeHeaderValue("test"));
+}
+
+} // namespace
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/integrity_metadata.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/integrity_metadata.cc
new file mode 100644
index 00000000000..864e5db6a10
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/integrity_metadata.cc
@@ -0,0 +1,33 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/loader/fetch/integrity_metadata.h"
+
+namespace blink {
+
+IntegrityMetadata::IntegrityMetadata(WTF::String digest,
+ IntegrityAlgorithm algorithm)
+ : digest_(digest), algorithm_(algorithm) {}
+
+IntegrityMetadata::IntegrityMetadata(IntegrityMetadataPair pair)
+ : digest_(pair.first), algorithm_(pair.second) {}
+
+IntegrityMetadataPair IntegrityMetadata::ToPair() const {
+ return IntegrityMetadataPair(digest_, algorithm_);
+}
+
+bool IntegrityMetadata::SetsEqual(const IntegrityMetadataSet& set1,
+ const IntegrityMetadataSet& set2) {
+ if (set1.size() != set2.size())
+ return false;
+
+ for (const IntegrityMetadataPair& metadata : set1) {
+ if (!set2.Contains(metadata))
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/integrity_metadata.h b/chromium/third_party/blink/renderer/platform/loader/fetch/integrity_metadata.h
new file mode 100644
index 00000000000..5854670d344
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/integrity_metadata.h
@@ -0,0 +1,69 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_INTEGRITY_METADATA_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_INTEGRITY_METADATA_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/string_hasher.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class IntegrityMetadata;
+enum class IntegrityAlgorithm : uint8_t;
+
+using IntegrityMetadataPair = std::pair<String, IntegrityAlgorithm>;
+using IntegrityMetadataSet = WTF::HashSet<IntegrityMetadataPair>;
+
+class PLATFORM_EXPORT IntegrityMetadata {
+ public:
+ IntegrityMetadata() = default;
+ IntegrityMetadata(String digest, IntegrityAlgorithm);
+ IntegrityMetadata(IntegrityMetadataPair);
+
+ String Digest() const { return digest_; }
+ void SetDigest(const String& digest) { digest_ = digest; }
+ IntegrityAlgorithm Algorithm() const { return algorithm_; }
+ void SetAlgorithm(IntegrityAlgorithm algorithm) { algorithm_ = algorithm; }
+
+ IntegrityMetadataPair ToPair() const;
+
+ static bool SetsEqual(const IntegrityMetadataSet& set1,
+ const IntegrityMetadataSet& set2);
+
+ private:
+ String digest_;
+ IntegrityAlgorithm algorithm_;
+};
+
+enum class ResourceIntegrityDisposition : uint8_t {
+ kNotChecked = 0,
+ kFailed,
+ kPassed
+};
+
+enum class IntegrityAlgorithm : uint8_t { kSha256, kSha384, kSha512, kEd25519 };
+
+} // namespace blink
+
+namespace WTF {
+
+template <>
+struct DefaultHash<blink::IntegrityAlgorithm> {
+ STATIC_ONLY(DefaultHash);
+ typedef IntHash<blink::IntegrityAlgorithm> Hash;
+};
+
+template <>
+struct HashTraits<blink::IntegrityAlgorithm>
+ : UnsignedWithZeroKeyHashTraits<blink::IntegrityAlgorithm> {
+ STATIC_ONLY(HashTraits);
+};
+
+} // namespace WTF
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache.cc
new file mode 100644
index 00000000000..20be134ae12
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache.cc
@@ -0,0 +1,476 @@
+/*
+ Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
+ Copyright (C) 2001 Dirk Mueller (mueller@kde.org)
+ Copyright (C) 2002 Waldo Bastian (bastian@kde.org)
+ Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "third_party/blink/renderer/platform/loader/fetch/memory_cache.h"
+
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loading_log.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin_hash.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/auto_reset.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+static Persistent<MemoryCache>* g_memory_cache;
+
+static const unsigned kCDefaultCacheCapacity = 8192 * 1024;
+static const int kCMinDelayBeforeLiveDecodedPrune = 1; // Seconds.
+static const double kCMaxPruneDeferralDelay = 0.5; // Seconds.
+
+// Percentage of capacity toward which we prune, to avoid immediately pruning
+// again.
+static const float kCTargetPrunePercentage = .95f;
+
+MemoryCache* GetMemoryCache() {
+ DCHECK(WTF::IsMainThread());
+ if (!g_memory_cache)
+ g_memory_cache = new Persistent<MemoryCache>(MemoryCache::Create());
+ return g_memory_cache->Get();
+}
+
+MemoryCache* ReplaceMemoryCacheForTesting(MemoryCache* cache) {
+ GetMemoryCache();
+ MemoryCache* old_cache = g_memory_cache->Release();
+ *g_memory_cache = cache;
+ MemoryCacheDumpProvider::Instance()->SetMemoryCache(cache);
+ return old_cache;
+}
+
+void MemoryCacheEntry::Trace(blink::Visitor* visitor) {
+ visitor->template RegisterWeakMembers<MemoryCacheEntry,
+ &MemoryCacheEntry::ClearResourceWeak>(
+ this);
+}
+
+void MemoryCacheEntry::ClearResourceWeak(Visitor* visitor) {
+ if (!resource_ || ThreadHeap::IsHeapObjectAlive(resource_))
+ return;
+ GetMemoryCache()->Remove(resource_.Get());
+ resource_.Clear();
+}
+
+inline MemoryCache::MemoryCache()
+ : in_prune_resources_(false),
+ prune_pending_(false),
+ max_prune_deferral_delay_(kCMaxPruneDeferralDelay),
+ prune_time_stamp_(0.0),
+ prune_frame_time_stamp_(0.0),
+ last_frame_paint_time_stamp_(0.0),
+ capacity_(kCDefaultCacheCapacity),
+ delay_before_live_decoded_prune_(kCMinDelayBeforeLiveDecodedPrune),
+ size_(0) {
+ MemoryCacheDumpProvider::Instance()->SetMemoryCache(this);
+ if (MemoryCoordinator::IsLowEndDevice())
+ MemoryCoordinator::Instance().RegisterClient(this);
+}
+
+MemoryCache* MemoryCache::Create() {
+ return new MemoryCache;
+}
+
+MemoryCache::~MemoryCache() {
+ if (prune_pending_)
+ Platform::Current()->CurrentThread()->RemoveTaskObserver(this);
+}
+
+void MemoryCache::Trace(blink::Visitor* visitor) {
+ visitor->Trace(resource_maps_);
+ MemoryCacheDumpClient::Trace(visitor);
+ MemoryCoordinatorClient::Trace(visitor);
+}
+
+KURL MemoryCache::RemoveFragmentIdentifierIfNeeded(const KURL& original_url) {
+ if (!original_url.HasFragmentIdentifier())
+ return original_url;
+ // Strip away fragment identifier from HTTP URLs. Data URLs must be
+ // unmodified. For file and custom URLs clients may expect resources to be
+ // unique even when they differ by the fragment identifier only.
+ if (!original_url.ProtocolIsInHTTPFamily())
+ return original_url;
+ KURL url = original_url;
+ url.RemoveFragmentIdentifier();
+ return url;
+}
+
+String MemoryCache::DefaultCacheIdentifier() {
+ return g_empty_string;
+}
+
+MemoryCache::ResourceMap* MemoryCache::EnsureResourceMap(
+ const String& cache_identifier) {
+ if (!resource_maps_.Contains(cache_identifier)) {
+ ResourceMapIndex::AddResult result =
+ resource_maps_.insert(cache_identifier, new ResourceMap);
+ CHECK(result.is_new_entry);
+ }
+ return resource_maps_.at(cache_identifier);
+}
+
+void MemoryCache::Add(Resource* resource) {
+ DCHECK(resource);
+ ResourceMap* resources = EnsureResourceMap(resource->CacheIdentifier());
+ AddInternal(resources, MemoryCacheEntry::Create(resource));
+ RESOURCE_LOADING_DVLOG(1)
+ << "MemoryCache::add Added " << resource->Url().GetString()
+ << ", resource " << resource;
+}
+
+void MemoryCache::AddInternal(ResourceMap* resource_map,
+ MemoryCacheEntry* entry) {
+ DCHECK(WTF::IsMainThread());
+ DCHECK(resource_map);
+
+ Resource* resource = entry->GetResource();
+ if (!resource)
+ return;
+ DCHECK(resource->Url().IsValid());
+
+ KURL url = RemoveFragmentIdentifierIfNeeded(resource->Url());
+ ResourceMap::iterator it = resource_map->find(url);
+ if (it != resource_map->end()) {
+ Resource* old_resource = it->value->GetResource();
+ CHECK_NE(old_resource, resource);
+ Update(old_resource, old_resource->size(), 0);
+ }
+ resource_map->Set(url, entry);
+ Update(resource, 0, resource->size());
+}
+
+void MemoryCache::Remove(Resource* resource) {
+ DCHECK(WTF::IsMainThread());
+ DCHECK(resource);
+ // Resources can be created with garbage urls in error cases. These Resources
+ // should never be added to the cache (AddInternal() DCHECKs that the url is
+ // valid). Null urls will crash if we attempt to hash them, so early exit.
+ if (resource->Url().IsNull())
+ return;
+
+ RESOURCE_LOADING_DVLOG(1) << "Evicting resource " << resource << " for "
+ << resource->Url().GetString() << " from cache";
+ TRACE_EVENT1("blink", "MemoryCache::evict", "resource",
+ resource->Url().GetString().Utf8());
+
+ ResourceMap* resources = resource_maps_.at(resource->CacheIdentifier());
+ if (!resources)
+ return;
+
+ KURL url = RemoveFragmentIdentifierIfNeeded(resource->Url());
+ ResourceMap::iterator it = resources->find(url);
+ if (it == resources->end() || it->value->GetResource() != resource)
+ return;
+ RemoveInternal(resources, it);
+}
+
+void MemoryCache::RemoveInternal(ResourceMap* resource_map,
+ const ResourceMap::iterator& it) {
+ DCHECK(WTF::IsMainThread());
+ DCHECK(resource_map);
+
+ Resource* resource = it->value->GetResource();
+ DCHECK(resource);
+
+ Update(resource, resource->size(), 0);
+ resource_map->erase(it);
+}
+
+bool MemoryCache::Contains(const Resource* resource) const {
+ if (!resource || resource->Url().IsEmpty())
+ return false;
+ const ResourceMap* resources = resource_maps_.at(resource->CacheIdentifier());
+ if (!resources)
+ return false;
+ KURL url = RemoveFragmentIdentifierIfNeeded(resource->Url());
+ MemoryCacheEntry* entry = resources->at(url);
+ return entry && resource == entry->GetResource();
+}
+
+Resource* MemoryCache::ResourceForURL(const KURL& resource_url) const {
+ return ResourceForURL(resource_url, DefaultCacheIdentifier());
+}
+
+Resource* MemoryCache::ResourceForURL(const KURL& resource_url,
+ const String& cache_identifier) const {
+ DCHECK(WTF::IsMainThread());
+ if (!resource_url.IsValid() || resource_url.IsNull())
+ return nullptr;
+ DCHECK(!cache_identifier.IsNull());
+ const ResourceMap* resources = resource_maps_.at(cache_identifier);
+ if (!resources)
+ return nullptr;
+ MemoryCacheEntry* entry =
+ resources->at(RemoveFragmentIdentifierIfNeeded(resource_url));
+ if (!entry)
+ return nullptr;
+ return entry->GetResource();
+}
+
+HeapVector<Member<Resource>> MemoryCache::ResourcesForURL(
+ const KURL& resource_url) const {
+ DCHECK(WTF::IsMainThread());
+ KURL url = RemoveFragmentIdentifierIfNeeded(resource_url);
+ HeapVector<Member<Resource>> results;
+ for (const auto& resource_map_iter : resource_maps_) {
+ if (MemoryCacheEntry* entry = resource_map_iter.value->at(url)) {
+ Resource* resource = entry->GetResource();
+ DCHECK(resource);
+ results.push_back(resource);
+ }
+ }
+ return results;
+}
+
+void MemoryCache::PruneResources(PruneStrategy strategy) {
+ DCHECK(!prune_pending_);
+ const size_t size_limit = (strategy == kMaximalPrune) ? 0 : Capacity();
+ if (size_ <= size_limit)
+ return;
+
+ // Cut by a percentage to avoid immediately pruning again.
+ size_t target_size =
+ static_cast<size_t>(size_limit * kCTargetPrunePercentage);
+
+ for (const auto& resource_map_iter : resource_maps_) {
+ for (const auto& resource_iter : *resource_map_iter.value) {
+ Resource* resource = resource_iter.value->GetResource();
+ DCHECK(resource);
+ if (resource->IsLoaded() && resource->DecodedSize()) {
+ // Check to see if the remaining resources are too new to prune.
+ double elapsed_time = prune_frame_time_stamp_ -
+ resource_iter.value->last_decoded_access_time_;
+ if (strategy == kAutomaticPrune &&
+ elapsed_time < delay_before_live_decoded_prune_)
+ continue;
+ resource->Prune();
+ if (size_ <= target_size)
+ return;
+ }
+ }
+ }
+}
+
+void MemoryCache::SetCapacity(size_t total_bytes) {
+ capacity_ = total_bytes;
+ Prune();
+}
+
+void MemoryCache::Update(Resource* resource, size_t old_size, size_t new_size) {
+ if (!Contains(resource))
+ return;
+ ptrdiff_t delta = new_size - old_size;
+ DCHECK(delta >= 0 || size_ >= static_cast<size_t>(-delta));
+ size_ += delta;
+}
+
+void MemoryCache::RemoveURLFromCache(const KURL& url) {
+ HeapVector<Member<Resource>> resources = ResourcesForURL(url);
+ for (Resource* resource : resources)
+ Remove(resource);
+}
+
+void MemoryCache::TypeStatistic::AddResource(Resource* o) {
+ count++;
+ size += o->size();
+ decoded_size += o->DecodedSize();
+ encoded_size += o->EncodedSize();
+ overhead_size += o->OverheadSize();
+ encoded_size_duplicated_in_data_urls +=
+ o->Url().ProtocolIsData() ? o->EncodedSize() : 0;
+}
+
+MemoryCache::Statistics MemoryCache::GetStatistics() const {
+ Statistics stats;
+ for (const auto& resource_map_iter : resource_maps_) {
+ for (const auto& resource_iter : *resource_map_iter.value) {
+ Resource* resource = resource_iter.value->GetResource();
+ DCHECK(resource);
+ switch (resource->GetType()) {
+ case Resource::kImage:
+ stats.images.AddResource(resource);
+ break;
+ case Resource::kCSSStyleSheet:
+ stats.css_style_sheets.AddResource(resource);
+ break;
+ case Resource::kScript:
+ stats.scripts.AddResource(resource);
+ break;
+ case Resource::kXSLStyleSheet:
+ stats.xsl_style_sheets.AddResource(resource);
+ break;
+ case Resource::kFont:
+ stats.fonts.AddResource(resource);
+ break;
+ default:
+ stats.other.AddResource(resource);
+ break;
+ }
+ }
+ }
+ return stats;
+}
+
+void MemoryCache::EvictResources(EvictResourcePolicy policy) {
+ for (auto resource_map_iter = resource_maps_.begin();
+ resource_map_iter != resource_maps_.end();) {
+ ResourceMap* resources = resource_map_iter->value.Get();
+ HeapVector<Member<MemoryCacheEntry>> unused_preloads;
+ for (auto resource_iter = resources->begin();
+ resource_iter != resources->end();
+ resource_iter = resources->begin()) {
+ DCHECK(resource_iter.Get());
+ DCHECK(resource_iter->value.Get());
+ DCHECK(resource_iter->value->GetResource());
+ Resource* resource = resource_iter->value->GetResource();
+ DCHECK(resource);
+ if (policy != kEvictAllResources && resource->IsUnusedPreload()) {
+ // Store unused preloads aside, so they could be added back later.
+ // That is in order to avoid the performance impact of iterating over
+ // the same resource multiple times.
+ unused_preloads.push_back(resource_iter->value.Get());
+ }
+ RemoveInternal(resources, resource_iter);
+ }
+ for (const auto& unused_preload : unused_preloads) {
+ AddInternal(resources, unused_preload);
+ }
+ // We may iterate multiple times over resourceMaps with unused preloads.
+ // That's extremely unlikely to have any real-life performance impact.
+ if (!resources->size()) {
+ resource_maps_.erase(resource_map_iter);
+ resource_map_iter = resource_maps_.begin();
+ } else {
+ ++resource_map_iter;
+ }
+ }
+}
+
+void MemoryCache::Prune() {
+ TRACE_EVENT0("renderer", "MemoryCache::prune()");
+
+ if (in_prune_resources_)
+ return;
+ if (size_ <= capacity_) // Fast path.
+ return;
+
+ // To avoid burdening the current thread with repetitive pruning jobs, pruning
+ // is postponed until the end of the current task. If it has been more than
+ // m_maxPruneDeferralDelay since the last prune, then we prune immediately. If
+ // the current thread's run loop is not active, then pruning will happen
+ // immediately only if it has been over m_maxPruneDeferralDelay since the last
+ // prune.
+ double current_time = WTF::CurrentTime();
+ if (prune_pending_) {
+ if (current_time - prune_time_stamp_ >= max_prune_deferral_delay_) {
+ PruneNow(current_time, kAutomaticPrune);
+ }
+ } else {
+ if (current_time - prune_time_stamp_ >= max_prune_deferral_delay_) {
+ PruneNow(current_time, kAutomaticPrune); // Delay exceeded, prune now.
+ } else {
+ // Defer.
+ Platform::Current()->CurrentThread()->AddTaskObserver(this);
+ prune_pending_ = true;
+ }
+ }
+}
+
+void MemoryCache::WillProcessTask() {}
+
+void MemoryCache::DidProcessTask() {
+ // Perform deferred pruning
+ DCHECK(prune_pending_);
+ PruneNow(WTF::CurrentTime(), kAutomaticPrune);
+}
+
+void MemoryCache::PruneAll() {
+ double current_time = WTF::CurrentTime();
+ PruneNow(current_time, kMaximalPrune);
+}
+
+void MemoryCache::PruneNow(double current_time, PruneStrategy strategy) {
+ if (prune_pending_) {
+ prune_pending_ = false;
+ Platform::Current()->CurrentThread()->RemoveTaskObserver(this);
+ }
+
+ AutoReset<bool> reentrancy_protector(&in_prune_resources_, true);
+
+ PruneResources(strategy);
+ prune_frame_time_stamp_ = last_frame_paint_time_stamp_;
+ prune_time_stamp_ = current_time;
+}
+
+void MemoryCache::UpdateFramePaintTimestamp() {
+ last_frame_paint_time_stamp_ = CurrentTime();
+}
+
+bool MemoryCache::OnMemoryDump(WebMemoryDumpLevelOfDetail level_of_detail,
+ WebProcessMemoryDump* memory_dump) {
+ if (level_of_detail == WebMemoryDumpLevelOfDetail::kBackground) {
+ Statistics stats = GetStatistics();
+ WebMemoryAllocatorDump* dump1 =
+ memory_dump->CreateMemoryAllocatorDump("web_cache/Image_resources");
+ dump1->AddScalar("size", "bytes",
+ stats.images.encoded_size + stats.images.overhead_size);
+ WebMemoryAllocatorDump* dump2 = memory_dump->CreateMemoryAllocatorDump(
+ "web_cache/CSS stylesheet_resources");
+ dump2->AddScalar("size", "bytes",
+ stats.css_style_sheets.encoded_size +
+ stats.css_style_sheets.overhead_size);
+ WebMemoryAllocatorDump* dump3 =
+ memory_dump->CreateMemoryAllocatorDump("web_cache/Script_resources");
+ dump3->AddScalar("size", "bytes",
+ stats.scripts.encoded_size + stats.scripts.overhead_size);
+ WebMemoryAllocatorDump* dump4 = memory_dump->CreateMemoryAllocatorDump(
+ "web_cache/XSL stylesheet_resources");
+ dump4->AddScalar("size", "bytes",
+ stats.xsl_style_sheets.encoded_size +
+ stats.xsl_style_sheets.overhead_size);
+ WebMemoryAllocatorDump* dump5 =
+ memory_dump->CreateMemoryAllocatorDump("web_cache/Font_resources");
+ dump5->AddScalar("size", "bytes",
+ stats.fonts.encoded_size + stats.fonts.overhead_size);
+ WebMemoryAllocatorDump* dump6 =
+ memory_dump->CreateMemoryAllocatorDump("web_cache/Other_resources");
+ dump6->AddScalar("size", "bytes",
+ stats.other.encoded_size + stats.other.overhead_size);
+ return true;
+ }
+
+ for (const auto& resource_map_iter : resource_maps_) {
+ for (const auto& resource_iter : *resource_map_iter.value) {
+ Resource* resource = resource_iter.value->GetResource();
+ resource->OnMemoryDump(level_of_detail, memory_dump);
+ }
+ }
+ return true;
+}
+
+void MemoryCache::OnMemoryPressure(WebMemoryPressureLevel level) {
+ PruneAll();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache.h b/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache.h
new file mode 100644
index 00000000000..bd63614edc1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache.h
@@ -0,0 +1,220 @@
+/*
+ Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
+ Copyright (C) 2001 Dirk Mueller <mueller@kde.org>
+ Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ This class provides all functionality needed for loading images, style
+ sheets and html
+ pages from the web. It has a memory cache for these objects.
+*/
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_MEMORY_CACHE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_MEMORY_CACHE_H_
+
+#include "third_party/blink/public/platform/web_thread.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/memory_cache_dump_provider.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource.h"
+#include "third_party/blink/renderer/platform/memory_coordinator.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class KURL;
+
+// Member<MemoryCacheEntry> + MemoryCacheEntry::clearResourceWeak() monitors
+// eviction from MemoryCache due to Resource garbage collection.
+// WeakMember<Resource> + Resource's prefinalizer cannot determine whether the
+// Resource was on MemoryCache or not, because WeakMember is already cleared
+// when the prefinalizer is executed.
+class MemoryCacheEntry final : public GarbageCollected<MemoryCacheEntry> {
+ public:
+ static MemoryCacheEntry* Create(Resource* resource) {
+ return new MemoryCacheEntry(resource);
+ }
+ void Trace(blink::Visitor*);
+ Resource* GetResource() const { return resource_; }
+
+ double last_decoded_access_time_; // Used as a thrash guard
+
+ private:
+ explicit MemoryCacheEntry(Resource* resource)
+ : last_decoded_access_time_(0.0), resource_(resource) {}
+
+ void ClearResourceWeak(Visitor*);
+
+ WeakMember<Resource> resource_;
+};
+
+WILL_NOT_BE_EAGERLY_TRACED_CLASS(MemoryCacheEntry);
+
+// This cache holds subresources used by Web pages: images, scripts,
+// stylesheets, etc.
+class PLATFORM_EXPORT MemoryCache final
+ : public GarbageCollectedFinalized<MemoryCache>,
+ public WebThread::TaskObserver,
+ public MemoryCacheDumpClient,
+ public MemoryCoordinatorClient {
+ USING_GARBAGE_COLLECTED_MIXIN(MemoryCache);
+ WTF_MAKE_NONCOPYABLE(MemoryCache);
+
+ public:
+ static MemoryCache* Create();
+ ~MemoryCache() override;
+ void Trace(blink::Visitor*) override;
+
+ struct TypeStatistic {
+ STACK_ALLOCATED();
+ size_t count;
+ size_t size;
+ size_t decoded_size;
+ size_t encoded_size;
+ size_t overhead_size;
+ size_t encoded_size_duplicated_in_data_urls;
+
+ TypeStatistic()
+ : count(0),
+ size(0),
+ decoded_size(0),
+ encoded_size(0),
+ overhead_size(0),
+ encoded_size_duplicated_in_data_urls(0) {}
+
+ void AddResource(Resource*);
+ };
+
+ struct Statistics {
+ STACK_ALLOCATED();
+ TypeStatistic images;
+ TypeStatistic css_style_sheets;
+ TypeStatistic scripts;
+ TypeStatistic xsl_style_sheets;
+ TypeStatistic fonts;
+ TypeStatistic other;
+ };
+
+ Resource* ResourceForURL(const KURL&) const;
+ Resource* ResourceForURL(const KURL&, const String& cache_identifier) const;
+ HeapVector<Member<Resource>> ResourcesForURL(const KURL&) const;
+
+ void Add(Resource*);
+ void Remove(Resource*);
+ bool Contains(const Resource*) const;
+
+ static KURL RemoveFragmentIdentifierIfNeeded(const KURL& original_url);
+
+ static String DefaultCacheIdentifier();
+
+ // Sets the cache's memory capacities, in bytes. These will hold only
+ // approximately, since the decoded cost of resources like scripts and
+ // stylesheets is not known.
+ // - totalBytes: The maximum number of bytes that the cache should consume
+ // overall.
+ void SetCapacity(size_t total_bytes);
+ void SetDelayBeforeLiveDecodedPrune(double seconds) {
+ delay_before_live_decoded_prune_ = seconds;
+ }
+ void SetMaxPruneDeferralDelay(double seconds) {
+ max_prune_deferral_delay_ = seconds;
+ }
+
+ enum EvictResourcePolicy { kEvictAllResources, kDoNotEvictUnusedPreloads };
+ void EvictResources(EvictResourcePolicy = kEvictAllResources);
+
+ void Prune();
+
+ // Called to update MemoryCache::size().
+ void Update(Resource*, size_t old_size, size_t new_size);
+
+ void RemoveURLFromCache(const KURL&);
+
+ Statistics GetStatistics() const;
+
+ size_t Capacity() const { return capacity_; }
+ size_t size() const { return size_; }
+
+ // TaskObserver implementation
+ void WillProcessTask() override;
+ void DidProcessTask() override;
+
+ void PruneAll();
+
+ void UpdateFramePaintTimestamp();
+
+ // Take memory usage snapshot for tracing.
+ bool OnMemoryDump(WebMemoryDumpLevelOfDetail, WebProcessMemoryDump*) override;
+
+ void OnMemoryPressure(WebMemoryPressureLevel) override;
+
+ private:
+ enum PruneStrategy {
+ // Automatically decide how much to prune.
+ kAutomaticPrune,
+ // Maximally prune resources.
+ kMaximalPrune
+ };
+
+ // A URL-based map of all resources that are in the cache (including the
+ // freshest version of objects that are currently being referenced by a Web
+ // page). removeFragmentIdentifierIfNeeded() should be called for the url
+ // before using it as a key for the map.
+ using ResourceMap = HeapHashMap<String, Member<MemoryCacheEntry>>;
+ using ResourceMapIndex = HeapHashMap<String, Member<ResourceMap>>;
+ ResourceMap* EnsureResourceMap(const String& cache_identifier);
+ ResourceMapIndex resource_maps_;
+
+ MemoryCache();
+
+ void AddInternal(ResourceMap*, MemoryCacheEntry*);
+ void RemoveInternal(ResourceMap*, const ResourceMap::iterator&);
+
+ void PruneResources(PruneStrategy);
+ void PruneNow(double current_time, PruneStrategy);
+
+ bool in_prune_resources_;
+ bool prune_pending_;
+ double max_prune_deferral_delay_;
+ double prune_time_stamp_;
+ double prune_frame_time_stamp_;
+ double last_frame_paint_time_stamp_; // used for detecting decoded resource
+ // thrash in the cache
+
+ size_t capacity_;
+ double delay_before_live_decoded_prune_;
+
+ // The number of bytes currently consumed by resources in the cache.
+ size_t size_;
+
+ friend class MemoryCacheTest;
+};
+
+// Returns the global cache.
+PLATFORM_EXPORT MemoryCache* GetMemoryCache();
+
+// Sets the global cache, used to swap in a test instance. Returns the old
+// MemoryCache object.
+PLATFORM_EXPORT MemoryCache* ReplaceMemoryCacheForTesting(MemoryCache*);
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache_correctness_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache_correctness_test.cc
new file mode 100644
index 00000000000..75a0e6c10dd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache_correctness_test.cc
@@ -0,0 +1,564 @@
+/*
+ * Copyright (c) 2014, 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 "third_party/blink/renderer/platform/loader/fetch/memory_cache.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_context.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h"
+#include "third_party/blink/renderer/platform/loader/fetch/raw_resource.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
+#include "third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h"
+#include "third_party/blink/renderer/platform/loader/testing/mock_resource.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
+
+namespace blink {
+
+namespace {
+
+// An URL for the original request.
+constexpr char kResourceURL[] = "http://resource.com/";
+
+// The origin time of our first request.
+constexpr char kOriginalRequestDateAsString[] = "Thu, 25 May 1977 18:30:00 GMT";
+constexpr char kOneDayBeforeOriginalRequest[] = "Wed, 24 May 1977 18:30:00 GMT";
+constexpr char kOneDayAfterOriginalRequest[] = "Fri, 26 May 1977 18:30:00 GMT";
+
+} // namespace
+
+class MemoryCacheCorrectnessTest : public testing::Test {
+ protected:
+ MockResource* ResourceFromResourceResponse(ResourceResponse response) {
+ if (response.Url().IsNull())
+ response.SetURL(KURL(kResourceURL));
+ ResourceRequest request(response.Url());
+ MockResource* resource = MockResource::Create(request);
+ resource->SetResponse(response);
+ resource->FinishForTest();
+ AddResourceToMemoryCache(resource);
+
+ return resource;
+ }
+ MockResource* ResourceFromResourceRequest(ResourceRequest request) {
+ if (request.Url().IsNull())
+ request.SetURL(KURL(kResourceURL));
+ request.SetFetchCredentialsMode(
+ network::mojom::FetchCredentialsMode::kOmit);
+ MockResource* resource = MockResource::Create(request);
+ resource->SetResponse(ResourceResponse(KURL(kResourceURL), "text/html"));
+ resource->FinishForTest();
+ AddResourceToMemoryCache(resource);
+
+ return resource;
+ }
+ void AddResourceToMemoryCache(Resource* resource) {
+ resource->SetSourceOrigin(security_origin_);
+ GetMemoryCache()->Add(resource);
+ }
+ // TODO(toyoshim): Consider to use MockResource for all tests instead of
+ // RawResource.
+ RawResource* FetchRawResource() {
+ ResourceRequest resource_request{KURL(kResourceURL)};
+ resource_request.SetRequestContext(WebURLRequest::kRequestContextInternal);
+ FetchParameters fetch_params(resource_request);
+ return RawResource::Fetch(fetch_params, Fetcher(), nullptr);
+ }
+ MockResource* FetchMockResource() {
+ ResourceRequest resource_request{KURL(kResourceURL)};
+ FetchParameters fetch_params(resource_request);
+ return MockResource::Fetch(fetch_params, Fetcher(), nullptr);
+ }
+ ResourceFetcher* Fetcher() const { return fetcher_.Get(); }
+ void AdvanceClock(double seconds) { platform_->AdvanceClockSeconds(seconds); }
+
+ private:
+ // Overrides testing::Test.
+ void SetUp() override {
+ // Save the global memory cache to restore it upon teardown.
+ global_memory_cache_ = ReplaceMemoryCacheForTesting(MemoryCache::Create());
+
+ MockFetchContext* context =
+ MockFetchContext::Create(MockFetchContext::kShouldNotLoadNewResource);
+ security_origin_ = SecurityOrigin::CreateUnique();
+ context->SetSecurityOrigin(security_origin_);
+
+ fetcher_ = ResourceFetcher::Create(context);
+ }
+ void TearDown() override {
+ GetMemoryCache()->EvictResources();
+
+ // Yield the ownership of the global memory cache back.
+ ReplaceMemoryCacheForTesting(global_memory_cache_.Release());
+ }
+
+ Persistent<MemoryCache> global_memory_cache_;
+ scoped_refptr<const SecurityOrigin> security_origin_;
+ Persistent<ResourceFetcher> fetcher_;
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform_;
+};
+
+TEST_F(MemoryCacheCorrectnessTest, FreshFromLastModified) {
+ ResourceResponse fresh200_response;
+ fresh200_response.SetHTTPStatusCode(200);
+ fresh200_response.SetHTTPHeaderField("Date", kOriginalRequestDateAsString);
+ fresh200_response.SetHTTPHeaderField("Last-Modified",
+ kOneDayBeforeOriginalRequest);
+
+ MockResource* fresh200 = ResourceFromResourceResponse(fresh200_response);
+
+ // Advance the clock within the implicit freshness period of this resource
+ // before we make a request.
+ AdvanceClock(600.);
+
+ MockResource* fetched = FetchMockResource();
+ EXPECT_EQ(fresh200, fetched);
+}
+
+TEST_F(MemoryCacheCorrectnessTest, FreshFromExpires) {
+ ResourceResponse fresh200_response;
+ fresh200_response.SetHTTPStatusCode(200);
+ fresh200_response.SetHTTPHeaderField("Date", kOriginalRequestDateAsString);
+ fresh200_response.SetHTTPHeaderField("Expires", kOneDayAfterOriginalRequest);
+
+ MockResource* fresh200 = ResourceFromResourceResponse(fresh200_response);
+
+ // Advance the clock within the freshness period of this resource before we
+ // make a request.
+ AdvanceClock(24. * 60. * 60. - 15.);
+
+ MockResource* fetched = FetchMockResource();
+ EXPECT_EQ(fresh200, fetched);
+}
+
+TEST_F(MemoryCacheCorrectnessTest, FreshFromMaxAge) {
+ ResourceResponse fresh200_response;
+ fresh200_response.SetHTTPStatusCode(200);
+ fresh200_response.SetHTTPHeaderField("Date", kOriginalRequestDateAsString);
+ fresh200_response.SetHTTPHeaderField("Cache-Control", "max-age=600");
+
+ MockResource* fresh200 = ResourceFromResourceResponse(fresh200_response);
+
+ // Advance the clock within the freshness period of this resource before we
+ // make a request.
+ AdvanceClock(500.);
+
+ MockResource* fetched = FetchMockResource();
+ EXPECT_EQ(fresh200, fetched);
+}
+
+// The strong validator causes a revalidation to be launched, and the proxy and
+// original resources leak because of their reference loop.
+TEST_F(MemoryCacheCorrectnessTest, DISABLED_ExpiredFromLastModified) {
+ ResourceResponse expired200_response;
+ expired200_response.SetHTTPStatusCode(200);
+ expired200_response.SetHTTPHeaderField("Date", kOriginalRequestDateAsString);
+ expired200_response.SetHTTPHeaderField("Last-Modified",
+ kOneDayBeforeOriginalRequest);
+
+ MockResource* expired200 = ResourceFromResourceResponse(expired200_response);
+
+ // Advance the clock beyond the implicit freshness period.
+ AdvanceClock(24. * 60. * 60. * 0.2);
+
+ MockResource* fetched = FetchMockResource();
+ EXPECT_NE(expired200, fetched);
+}
+
+TEST_F(MemoryCacheCorrectnessTest, ExpiredFromExpires) {
+ ResourceResponse expired200_response;
+ expired200_response.SetHTTPStatusCode(200);
+ expired200_response.SetHTTPHeaderField("Date", kOriginalRequestDateAsString);
+ expired200_response.SetHTTPHeaderField("Expires",
+ kOneDayAfterOriginalRequest);
+
+ MockResource* expired200 = ResourceFromResourceResponse(expired200_response);
+
+ // Advance the clock within the expiredness period of this resource before we
+ // make a request.
+ AdvanceClock(24. * 60. * 60. + 15.);
+
+ MockResource* fetched = FetchMockResource();
+ EXPECT_NE(expired200, fetched);
+}
+
+// If the resource hasn't been loaded in this "document" before, then it
+// shouldn't have list of available resources logic.
+TEST_F(MemoryCacheCorrectnessTest, NewMockResourceExpiredFromExpires) {
+ ResourceResponse expired200_response;
+ expired200_response.SetHTTPStatusCode(200);
+ expired200_response.SetHTTPHeaderField("Date", kOriginalRequestDateAsString);
+ expired200_response.SetHTTPHeaderField("Expires",
+ kOneDayAfterOriginalRequest);
+
+ MockResource* expired200 = ResourceFromResourceResponse(expired200_response);
+
+ // Advance the clock within the expiredness period of this resource before we
+ // make a request.
+ AdvanceClock(24. * 60. * 60. + 15.);
+
+ MockResource* fetched = FetchMockResource();
+ EXPECT_NE(expired200, fetched);
+}
+
+// If the resource has been loaded in this "document" before, then it should
+// have list of available resources logic, and so normal cache testing should be
+// bypassed.
+TEST_F(MemoryCacheCorrectnessTest, ReuseMockResourceExpiredFromExpires) {
+ ResourceResponse expired200_response;
+ expired200_response.SetHTTPStatusCode(200);
+ expired200_response.SetHTTPHeaderField("Date", kOriginalRequestDateAsString);
+ expired200_response.SetHTTPHeaderField("Expires",
+ kOneDayAfterOriginalRequest);
+
+ MockResource* expired200 = ResourceFromResourceResponse(expired200_response);
+
+ // Advance the clock within the freshness period, and make a request to add
+ // this resource to the document resources.
+ AdvanceClock(15.);
+ MockResource* first_fetched = FetchMockResource();
+ EXPECT_EQ(expired200, first_fetched);
+
+ // Advance the clock within the expiredness period of this resource before we
+ // make a request.
+ AdvanceClock(24. * 60. * 60. + 15.);
+
+ MockResource* fetched = FetchMockResource();
+ EXPECT_EQ(expired200, fetched);
+}
+
+TEST_F(MemoryCacheCorrectnessTest, ExpiredFromMaxAge) {
+ ResourceResponse expired200_response;
+ expired200_response.SetHTTPStatusCode(200);
+ expired200_response.SetHTTPHeaderField("Date", kOriginalRequestDateAsString);
+ expired200_response.SetHTTPHeaderField("Cache-Control", "max-age=600");
+
+ MockResource* expired200 = ResourceFromResourceResponse(expired200_response);
+
+ // Advance the clock within the expiredness period of this resource before we
+ // make a request.
+ AdvanceClock(700.);
+
+ MockResource* fetched = FetchMockResource();
+ EXPECT_NE(expired200, fetched);
+}
+
+TEST_F(MemoryCacheCorrectnessTest, FreshButNoCache) {
+ ResourceResponse fresh200_nocache_response;
+ fresh200_nocache_response.SetHTTPStatusCode(200);
+ fresh200_nocache_response.SetHTTPHeaderField(HTTPNames::Date,
+ kOriginalRequestDateAsString);
+ fresh200_nocache_response.SetHTTPHeaderField(HTTPNames::Expires,
+ kOneDayAfterOriginalRequest);
+ fresh200_nocache_response.SetHTTPHeaderField(HTTPNames::Cache_Control,
+ "no-cache");
+
+ MockResource* fresh200_nocache =
+ ResourceFromResourceResponse(fresh200_nocache_response);
+
+ // Advance the clock within the freshness period of this resource before we
+ // make a request.
+ AdvanceClock(24. * 60. * 60. - 15.);
+
+ MockResource* fetched = FetchMockResource();
+ EXPECT_NE(fresh200_nocache, fetched);
+}
+
+TEST_F(MemoryCacheCorrectnessTest, RequestWithNoCache) {
+ ResourceRequest no_cache_request;
+ no_cache_request.SetHTTPHeaderField(HTTPNames::Cache_Control, "no-cache");
+ MockResource* no_cache_resource =
+ ResourceFromResourceRequest(no_cache_request);
+ MockResource* fetched = FetchMockResource();
+ EXPECT_NE(no_cache_resource, fetched);
+}
+
+TEST_F(MemoryCacheCorrectnessTest, FreshButNoStore) {
+ ResourceResponse fresh200_nostore_response;
+ fresh200_nostore_response.SetHTTPStatusCode(200);
+ fresh200_nostore_response.SetHTTPHeaderField(HTTPNames::Date,
+ kOriginalRequestDateAsString);
+ fresh200_nostore_response.SetHTTPHeaderField(HTTPNames::Expires,
+ kOneDayAfterOriginalRequest);
+ fresh200_nostore_response.SetHTTPHeaderField(HTTPNames::Cache_Control,
+ "no-store");
+
+ MockResource* fresh200_nostore =
+ ResourceFromResourceResponse(fresh200_nostore_response);
+
+ // Advance the clock within the freshness period of this resource before we
+ // make a request.
+ AdvanceClock(24. * 60. * 60. - 15.);
+
+ MockResource* fetched = FetchMockResource();
+ EXPECT_NE(fresh200_nostore, fetched);
+}
+
+TEST_F(MemoryCacheCorrectnessTest, RequestWithNoStore) {
+ ResourceRequest no_store_request;
+ no_store_request.SetHTTPHeaderField(HTTPNames::Cache_Control, "no-store");
+ MockResource* no_store_resource =
+ ResourceFromResourceRequest(no_store_request);
+ MockResource* fetched = FetchMockResource();
+ EXPECT_NE(no_store_resource, fetched);
+}
+
+// FIXME: Determine if ignoring must-revalidate for blink is correct behaviour.
+// See crbug.com/340088 .
+TEST_F(MemoryCacheCorrectnessTest, DISABLED_FreshButMustRevalidate) {
+ ResourceResponse fresh200_must_revalidate_response;
+ fresh200_must_revalidate_response.SetHTTPStatusCode(200);
+ fresh200_must_revalidate_response.SetHTTPHeaderField(
+ HTTPNames::Date, kOriginalRequestDateAsString);
+ fresh200_must_revalidate_response.SetHTTPHeaderField(
+ HTTPNames::Expires, kOneDayAfterOriginalRequest);
+ fresh200_must_revalidate_response.SetHTTPHeaderField(HTTPNames::Cache_Control,
+ "must-revalidate");
+
+ MockResource* fresh200_must_revalidate =
+ ResourceFromResourceResponse(fresh200_must_revalidate_response);
+
+ // Advance the clock within the freshness period of this resource before we
+ // make a request.
+ AdvanceClock(24. * 60. * 60. - 15.);
+
+ MockResource* fetched = FetchMockResource();
+ EXPECT_NE(fresh200_must_revalidate, fetched);
+}
+
+TEST_F(MemoryCacheCorrectnessTest, FreshWithFreshRedirect) {
+ KURL redirect_url(kResourceURL);
+ const char kRedirectTargetUrlString[] = "http://redirect-target.com";
+ KURL redirect_target_url(kRedirectTargetUrlString);
+
+ ResourceRequest request(redirect_url);
+ MockResource* first_resource = MockResource::Create(request);
+
+ ResourceResponse fresh301_response(redirect_url);
+ fresh301_response.SetHTTPStatusCode(301);
+ fresh301_response.SetHTTPHeaderField(HTTPNames::Date,
+ kOriginalRequestDateAsString);
+ fresh301_response.SetHTTPHeaderField(HTTPNames::Location,
+ kRedirectTargetUrlString);
+ fresh301_response.SetHTTPHeaderField(HTTPNames::Cache_Control, "max-age=600");
+
+ // Add the redirect to our request.
+ ResourceRequest redirect_request = ResourceRequest(redirect_target_url);
+ first_resource->WillFollowRedirect(redirect_request, fresh301_response);
+
+ // Add the final response to our request.
+ ResourceResponse fresh200_response(redirect_target_url);
+ fresh200_response.SetHTTPStatusCode(200);
+ fresh200_response.SetHTTPHeaderField(HTTPNames::Date,
+ kOriginalRequestDateAsString);
+ fresh200_response.SetHTTPHeaderField(HTTPNames::Expires,
+ kOneDayAfterOriginalRequest);
+
+ first_resource->SetResponse(fresh200_response);
+ first_resource->FinishForTest();
+ AddResourceToMemoryCache(first_resource);
+
+ AdvanceClock(500.);
+
+ MockResource* fetched = FetchMockResource();
+ EXPECT_EQ(first_resource, fetched);
+}
+
+TEST_F(MemoryCacheCorrectnessTest, FreshWithStaleRedirect) {
+ KURL redirect_url(kResourceURL);
+ const char kRedirectTargetUrlString[] = "http://redirect-target.com";
+ KURL redirect_target_url(kRedirectTargetUrlString);
+
+ ResourceRequest request(redirect_url);
+ request.SetFetchCredentialsMode(network::mojom::FetchCredentialsMode::kOmit);
+ MockResource* first_resource = MockResource::Create(request);
+
+ ResourceResponse stale301_response(redirect_url);
+ stale301_response.SetHTTPStatusCode(301);
+ stale301_response.SetHTTPHeaderField(HTTPNames::Date,
+ kOriginalRequestDateAsString);
+ stale301_response.SetHTTPHeaderField(HTTPNames::Location,
+ kRedirectTargetUrlString);
+
+ // Add the redirect to our request.
+ ResourceRequest redirect_request = ResourceRequest(redirect_target_url);
+ first_resource->WillFollowRedirect(redirect_request, stale301_response);
+
+ // Add the final response to our request.
+ ResourceResponse fresh200_response(redirect_target_url);
+ fresh200_response.SetHTTPStatusCode(200);
+ fresh200_response.SetHTTPHeaderField(HTTPNames::Date,
+ kOriginalRequestDateAsString);
+ fresh200_response.SetHTTPHeaderField(HTTPNames::Expires,
+ kOneDayAfterOriginalRequest);
+
+ first_resource->SetResponse(fresh200_response);
+ first_resource->FinishForTest();
+ AddResourceToMemoryCache(first_resource);
+
+ AdvanceClock(500.);
+
+ MockResource* fetched = FetchMockResource();
+ EXPECT_NE(first_resource, fetched);
+}
+
+TEST_F(MemoryCacheCorrectnessTest, PostToSameURLTwice) {
+ ResourceRequest request1{KURL(kResourceURL)};
+ request1.SetHTTPMethod(HTTPNames::POST);
+ RawResource* resource1 = RawResource::CreateForTest(request1, Resource::kRaw);
+ resource1->SetStatus(ResourceStatus::kPending);
+ AddResourceToMemoryCache(resource1);
+
+ ResourceRequest request2{KURL(kResourceURL)};
+ request2.SetHTTPMethod(HTTPNames::POST);
+ FetchParameters fetch2(request2);
+ RawResource* resource2 = RawResource::FetchSynchronously(fetch2, Fetcher());
+ EXPECT_NE(resource1, resource2);
+}
+
+TEST_F(MemoryCacheCorrectnessTest, 302RedirectNotImplicitlyFresh) {
+ KURL redirect_url(kResourceURL);
+ const char kRedirectTargetUrlString[] = "http://redirect-target.com";
+ KURL redirect_target_url(kRedirectTargetUrlString);
+
+ RawResource* first_resource =
+ RawResource::CreateForTest(redirect_url, Resource::kRaw);
+
+ ResourceResponse fresh302_response(redirect_url);
+ fresh302_response.SetHTTPStatusCode(302);
+ fresh302_response.SetHTTPHeaderField(HTTPNames::Date,
+ kOriginalRequestDateAsString);
+ fresh302_response.SetHTTPHeaderField(HTTPNames::Last_Modified,
+ kOneDayBeforeOriginalRequest);
+ fresh302_response.SetHTTPHeaderField(HTTPNames::Location,
+ kRedirectTargetUrlString);
+
+ // Add the redirect to our request.
+ ResourceRequest redirect_request = ResourceRequest(redirect_target_url);
+ first_resource->WillFollowRedirect(redirect_request, fresh302_response);
+
+ // Add the final response to our request.
+ ResourceResponse fresh200_response(redirect_target_url);
+ fresh200_response.SetHTTPStatusCode(200);
+ fresh200_response.SetHTTPHeaderField(HTTPNames::Date,
+ kOriginalRequestDateAsString);
+ fresh200_response.SetHTTPHeaderField(HTTPNames::Expires,
+ kOneDayAfterOriginalRequest);
+
+ first_resource->SetResponse(fresh200_response);
+ first_resource->FinishForTest();
+ AddResourceToMemoryCache(first_resource);
+
+ AdvanceClock(500.);
+
+ RawResource* fetched = FetchRawResource();
+ EXPECT_NE(first_resource, fetched);
+}
+
+TEST_F(MemoryCacheCorrectnessTest, 302RedirectExplicitlyFreshMaxAge) {
+ KURL redirect_url(kResourceURL);
+ const char kRedirectTargetUrlString[] = "http://redirect-target.com";
+ KURL redirect_target_url(kRedirectTargetUrlString);
+
+ ResourceRequest request(redirect_url);
+ MockResource* first_resource = MockResource::Create(request);
+
+ ResourceResponse fresh302_response(redirect_url);
+ fresh302_response.SetHTTPStatusCode(302);
+ fresh302_response.SetHTTPHeaderField(HTTPNames::Date,
+ kOriginalRequestDateAsString);
+ fresh302_response.SetHTTPHeaderField(HTTPNames::Cache_Control, "max-age=600");
+ fresh302_response.SetHTTPHeaderField(HTTPNames::Location,
+ kRedirectTargetUrlString);
+
+ // Add the redirect to our request.
+ ResourceRequest redirect_request = ResourceRequest(redirect_target_url);
+ first_resource->WillFollowRedirect(redirect_request, fresh302_response);
+
+ // Add the final response to our request.
+ ResourceResponse fresh200_response(redirect_target_url);
+ fresh200_response.SetHTTPStatusCode(200);
+ fresh200_response.SetHTTPHeaderField(HTTPNames::Date,
+ kOriginalRequestDateAsString);
+ fresh200_response.SetHTTPHeaderField(HTTPNames::Expires,
+ kOneDayAfterOriginalRequest);
+
+ first_resource->SetResponse(fresh200_response);
+ first_resource->FinishForTest();
+ AddResourceToMemoryCache(first_resource);
+
+ AdvanceClock(500.);
+
+ MockResource* fetched = FetchMockResource();
+ EXPECT_EQ(first_resource, fetched);
+}
+
+TEST_F(MemoryCacheCorrectnessTest, 302RedirectExplicitlyFreshExpires) {
+ KURL redirect_url(kResourceURL);
+ const char kRedirectTargetUrlString[] = "http://redirect-target.com";
+ KURL redirect_target_url(kRedirectTargetUrlString);
+
+ ResourceRequest request(redirect_url);
+ MockResource* first_resource = MockResource::Create(request);
+
+ ResourceResponse fresh302_response(redirect_url);
+ fresh302_response.SetHTTPStatusCode(302);
+ fresh302_response.SetHTTPHeaderField(HTTPNames::Date,
+ kOriginalRequestDateAsString);
+ fresh302_response.SetHTTPHeaderField(HTTPNames::Expires,
+ kOneDayAfterOriginalRequest);
+ fresh302_response.SetHTTPHeaderField(HTTPNames::Location,
+ kRedirectTargetUrlString);
+
+ // Add the redirect to our request.
+ ResourceRequest redirect_request = ResourceRequest(redirect_target_url);
+ first_resource->WillFollowRedirect(redirect_request, fresh302_response);
+
+ // Add the final response to our request.
+ ResourceResponse fresh200_response(redirect_target_url);
+ fresh200_response.SetHTTPStatusCode(200);
+ fresh200_response.SetHTTPHeaderField(HTTPNames::Date,
+ kOriginalRequestDateAsString);
+ fresh200_response.SetHTTPHeaderField(HTTPNames::Expires,
+ kOneDayAfterOriginalRequest);
+
+ first_resource->SetResponse(fresh200_response);
+ first_resource->FinishForTest();
+ AddResourceToMemoryCache(first_resource);
+
+ AdvanceClock(500.);
+
+ MockResource* fetched = FetchMockResource();
+ EXPECT_EQ(first_resource, fetched);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache_test.cc
new file mode 100644
index 00000000000..d107d869f93
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache_test.cc
@@ -0,0 +1,419 @@
+/*
+ * Copyright (c) 2013, 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 "third_party/blink/renderer/platform/loader/fetch/memory_cache.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/loader/fetch/raw_resource.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
+#include "third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h"
+#include "third_party/blink/renderer/platform/loader/testing/mock_resource_client.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
+#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+
+namespace blink {
+
+class FakeDecodedResource final : public Resource {
+ public:
+ static FakeDecodedResource* Fetch(FetchParameters& params,
+ ResourceFetcher* fetcher,
+ ResourceClient* client) {
+ return static_cast<FakeDecodedResource*>(
+ fetcher->RequestResource(params, Factory(), client));
+ }
+
+ void AppendData(const char* data, size_t len) override {
+ Resource::AppendData(data, len);
+ SetDecodedSize(this->size());
+ }
+
+ void FakeEncodedSize(size_t size) { SetEncodedSize(size); }
+
+ private:
+ class Factory final : public NonTextResourceFactory {
+ public:
+ Factory() : NonTextResourceFactory(kMock) {}
+
+ Resource* Create(const ResourceRequest& request,
+ const ResourceLoaderOptions& options) const override {
+ return new FakeDecodedResource(request, options);
+ }
+ };
+
+ FakeDecodedResource(const ResourceRequest& request,
+ const ResourceLoaderOptions& options)
+ : Resource(request, kMock, options) {}
+
+ void DestroyDecodedDataIfPossible() override { SetDecodedSize(0); }
+};
+
+class MemoryCacheTest : public testing::Test {
+ public:
+ class FakeResource final : public Resource {
+ public:
+ static FakeResource* Create(const char* url, Type type) {
+ return Create(KURL(url), type);
+ }
+ static FakeResource* Create(const KURL& url, Type type) {
+ ResourceRequest request(url);
+ request.SetFetchCredentialsMode(
+ network::mojom::FetchCredentialsMode::kOmit);
+
+ ResourceLoaderOptions options;
+
+ return new FakeResource(request, type, options);
+ }
+
+ private:
+ FakeResource(const ResourceRequest& request,
+ Type type,
+ const ResourceLoaderOptions& options)
+ : Resource(request, type, options) {}
+ };
+
+ protected:
+ void SetUp() override {
+ // Save the global memory cache to restore it upon teardown.
+ global_memory_cache_ = ReplaceMemoryCacheForTesting(MemoryCache::Create());
+ fetcher_ = ResourceFetcher::Create(
+ MockFetchContext::Create(MockFetchContext::kShouldLoadNewResource));
+ }
+
+ void TearDown() override {
+ ReplaceMemoryCacheForTesting(global_memory_cache_.Release());
+ }
+
+ Persistent<MemoryCache> global_memory_cache_;
+ Persistent<ResourceFetcher> fetcher_;
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform_;
+};
+
+// Verifies that setters and getters for cache capacities work correcty.
+TEST_F(MemoryCacheTest, CapacityAccounting) {
+ const size_t kSizeMax = ~static_cast<size_t>(0);
+ const size_t kTotalCapacity = kSizeMax / 4;
+ GetMemoryCache()->SetCapacity(kTotalCapacity);
+ EXPECT_EQ(kTotalCapacity, GetMemoryCache()->Capacity());
+}
+
+TEST_F(MemoryCacheTest, VeryLargeResourceAccounting) {
+ const size_t kSizeMax = ~static_cast<size_t>(0);
+ const size_t kTotalCapacity = kSizeMax / 4;
+ const size_t kResourceSize1 = kSizeMax / 16;
+ const size_t kResourceSize2 = kSizeMax / 20;
+ GetMemoryCache()->SetCapacity(kTotalCapacity);
+ Persistent<MockResourceClient> client = new MockResourceClient;
+ FetchParameters params(ResourceRequest("data:text/html,"));
+ FakeDecodedResource* cached_resource =
+ FakeDecodedResource::Fetch(params, fetcher_, client);
+ cached_resource->FakeEncodedSize(kResourceSize1);
+
+ EXPECT_TRUE(GetMemoryCache()->Contains(cached_resource));
+ EXPECT_EQ(cached_resource->size(), GetMemoryCache()->size());
+
+ client->RemoveAsClient();
+ EXPECT_EQ(cached_resource->size(), GetMemoryCache()->size());
+
+ cached_resource->FakeEncodedSize(kResourceSize2);
+ EXPECT_EQ(cached_resource->size(), GetMemoryCache()->size());
+}
+
+static void RunTask1(Resource* resource1, Resource* resource2) {
+ // The resource size has to be nonzero for this test to be meaningful, but
+ // we do not rely on it having any particular value.
+ EXPECT_GT(resource1->size(), 0u);
+ EXPECT_GT(resource2->size(), 0u);
+
+ EXPECT_EQ(0u, GetMemoryCache()->size());
+
+ GetMemoryCache()->Add(resource1);
+ GetMemoryCache()->Add(resource2);
+
+ size_t total_size = resource1->size() + resource2->size();
+ EXPECT_EQ(total_size, GetMemoryCache()->size());
+ EXPECT_GT(resource1->DecodedSize(), 0u);
+ EXPECT_GT(resource2->DecodedSize(), 0u);
+
+ // We expect actual pruning doesn't occur here synchronously but deferred
+ // to the end of this task, due to the previous pruning invoked in
+ // testResourcePruningAtEndOfTask().
+ GetMemoryCache()->Prune();
+ EXPECT_EQ(total_size, GetMemoryCache()->size());
+ EXPECT_GT(resource1->DecodedSize(), 0u);
+ EXPECT_GT(resource2->DecodedSize(), 0u);
+}
+
+static void RunTask2(unsigned size_without_decode) {
+ // Next task: now, the resources was pruned.
+ EXPECT_EQ(size_without_decode, GetMemoryCache()->size());
+}
+
+static void TestResourcePruningAtEndOfTask(ResourceFetcher* fetcher,
+ const String& identifier1,
+ const String& identifier2) {
+ GetMemoryCache()->SetDelayBeforeLiveDecodedPrune(0);
+
+ // Enforce pruning by adding |dummyResource| and then call prune().
+ Resource* dummy_resource =
+ RawResource::CreateForTest("http://dummy", Resource::kRaw);
+ GetMemoryCache()->Add(dummy_resource);
+ EXPECT_GT(GetMemoryCache()->size(), 1u);
+ const unsigned kTotalCapacity = 1;
+ GetMemoryCache()->SetCapacity(kTotalCapacity);
+ GetMemoryCache()->Prune();
+ GetMemoryCache()->Remove(dummy_resource);
+ EXPECT_EQ(0u, GetMemoryCache()->size());
+
+ const char kData[6] = "abcde";
+ FetchParameters params1(ResourceRequest("data:text/html,resource1"));
+ Resource* resource1 = FakeDecodedResource::Fetch(params1, fetcher, nullptr);
+ GetMemoryCache()->Remove(resource1);
+ if (!identifier1.IsEmpty())
+ resource1->SetCacheIdentifier(identifier1);
+ resource1->AppendData(kData, 3u);
+ resource1->FinishForTest();
+ FetchParameters params2(ResourceRequest("data:text/html,resource2"));
+ Persistent<MockResourceClient> client = new MockResourceClient;
+ Resource* resource2 = FakeDecodedResource::Fetch(params2, fetcher, client);
+ GetMemoryCache()->Remove(resource2);
+ if (!identifier2.IsEmpty())
+ resource2->SetCacheIdentifier(identifier2);
+ resource2->AppendData(kData, 4u);
+ resource2->FinishForTest();
+
+ Platform::Current()->CurrentThread()->GetTaskRunner()->PostTask(
+ FROM_HERE, WTF::Bind(&RunTask1, WrapPersistent(resource1),
+ WrapPersistent(resource2)));
+ Platform::Current()->CurrentThread()->GetTaskRunner()->PostTask(
+ FROM_HERE,
+ WTF::Bind(&RunTask2,
+ resource1->EncodedSize() + resource1->OverheadSize() +
+ resource2->EncodedSize() + resource2->OverheadSize()));
+ static_cast<TestingPlatformSupportWithMockScheduler*>(Platform::Current())
+ ->RunUntilIdle();
+}
+
+// Verified that when ordering a prune in a runLoop task, the prune
+// is deferred to the end of the task.
+TEST_F(MemoryCacheTest, ResourcePruningAtEndOfTask_Basic) {
+ TestResourcePruningAtEndOfTask(fetcher_, "", "");
+}
+
+TEST_F(MemoryCacheTest, ResourcePruningAtEndOfTask_MultipleResourceMaps) {
+ {
+ TestResourcePruningAtEndOfTask(fetcher_, "foo", "");
+ GetMemoryCache()->EvictResources();
+ }
+ {
+ TestResourcePruningAtEndOfTask(fetcher_, "foo", "bar");
+ GetMemoryCache()->EvictResources();
+ }
+}
+
+// Verifies that
+// - Resources are not pruned synchronously when ResourceClient is removed.
+// - size() is updated appropriately when Resources are added to MemoryCache
+// and garbage collected.
+static void TestClientRemoval(ResourceFetcher* fetcher,
+ const String& identifier1,
+ const String& identifier2) {
+ GetMemoryCache()->SetCapacity(0);
+ const char kData[6] = "abcde";
+ Persistent<MockResourceClient> client1 = new MockResourceClient;
+ Persistent<MockResourceClient> client2 = new MockResourceClient;
+ FetchParameters params1(ResourceRequest("data:text/html,foo"));
+ Resource* resource1 = FakeDecodedResource::Fetch(params1, fetcher, client1);
+ FetchParameters params2(ResourceRequest("data:text/html,bar"));
+ Resource* resource2 = FakeDecodedResource::Fetch(params2, fetcher, client2);
+ resource1->AppendData(kData, 4u);
+ resource2->AppendData(kData, 4u);
+
+ GetMemoryCache()->SetCapacity(0);
+ // Remove and re-Add the resources, with proper cache identifiers.
+ GetMemoryCache()->Remove(resource1);
+ GetMemoryCache()->Remove(resource2);
+ if (!identifier1.IsEmpty())
+ resource1->SetCacheIdentifier(identifier1);
+ if (!identifier2.IsEmpty())
+ resource2->SetCacheIdentifier(identifier2);
+ GetMemoryCache()->Add(resource1);
+ GetMemoryCache()->Add(resource2);
+
+ size_t original_total_size = resource1->size() + resource2->size();
+
+ // Call prune. There is nothing to prune, but this will initialize
+ // the prune timestamp, allowing future prunes to be deferred.
+ GetMemoryCache()->Prune();
+ EXPECT_GT(resource1->DecodedSize(), 0u);
+ EXPECT_GT(resource2->DecodedSize(), 0u);
+ EXPECT_EQ(original_total_size, GetMemoryCache()->size());
+
+ // Removing the client from resource1 should not trigger pruning.
+ client1->RemoveAsClient();
+ EXPECT_GT(resource1->DecodedSize(), 0u);
+ EXPECT_GT(resource2->DecodedSize(), 0u);
+ EXPECT_EQ(original_total_size, GetMemoryCache()->size());
+ EXPECT_TRUE(GetMemoryCache()->Contains(resource1));
+ EXPECT_TRUE(GetMemoryCache()->Contains(resource2));
+
+ // Removing the client from resource2 should not trigger pruning.
+ client2->RemoveAsClient();
+ EXPECT_GT(resource1->DecodedSize(), 0u);
+ EXPECT_GT(resource2->DecodedSize(), 0u);
+ EXPECT_EQ(original_total_size, GetMemoryCache()->size());
+ EXPECT_TRUE(GetMemoryCache()->Contains(resource1));
+ EXPECT_TRUE(GetMemoryCache()->Contains(resource2));
+
+ WeakPersistent<Resource> resource1_weak = resource1;
+ WeakPersistent<Resource> resource2_weak = resource2;
+
+ ThreadState::Current()->CollectGarbage(
+ BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
+ BlinkGC::kEagerSweeping, BlinkGC::kForcedGC);
+ // Resources are garbage-collected (WeakMemoryCache) and thus removed
+ // from MemoryCache.
+ EXPECT_FALSE(resource1_weak);
+ EXPECT_FALSE(resource2_weak);
+ EXPECT_EQ(0u, GetMemoryCache()->size());
+}
+
+TEST_F(MemoryCacheTest, ClientRemoval_Basic) {
+ TestClientRemoval(fetcher_, "", "");
+}
+
+TEST_F(MemoryCacheTest, ClientRemoval_MultipleResourceMaps) {
+ {
+ TestClientRemoval(fetcher_, "foo", "");
+ GetMemoryCache()->EvictResources();
+ }
+ {
+ TestClientRemoval(fetcher_, "", "foo");
+ GetMemoryCache()->EvictResources();
+ }
+ {
+ TestClientRemoval(fetcher_, "foo", "bar");
+ GetMemoryCache()->EvictResources();
+ }
+}
+
+TEST_F(MemoryCacheTest, RemoveDuringRevalidation) {
+ FakeResource* resource1 =
+ FakeResource::Create("http://test/resource", Resource::kRaw);
+ GetMemoryCache()->Add(resource1);
+
+ FakeResource* resource2 =
+ FakeResource::Create("http://test/resource", Resource::kRaw);
+ GetMemoryCache()->Remove(resource1);
+ GetMemoryCache()->Add(resource2);
+ EXPECT_TRUE(GetMemoryCache()->Contains(resource2));
+ EXPECT_FALSE(GetMemoryCache()->Contains(resource1));
+
+ FakeResource* resource3 =
+ FakeResource::Create("http://test/resource", Resource::kRaw);
+ GetMemoryCache()->Remove(resource2);
+ GetMemoryCache()->Add(resource3);
+ EXPECT_TRUE(GetMemoryCache()->Contains(resource3));
+ EXPECT_FALSE(GetMemoryCache()->Contains(resource2));
+}
+
+TEST_F(MemoryCacheTest, ResourceMapIsolation) {
+ FakeResource* resource1 =
+ FakeResource::Create("http://test/resource", Resource::kRaw);
+ GetMemoryCache()->Add(resource1);
+
+ FakeResource* resource2 =
+ FakeResource::Create("http://test/resource", Resource::kRaw);
+ resource2->SetCacheIdentifier("foo");
+ GetMemoryCache()->Add(resource2);
+ EXPECT_TRUE(GetMemoryCache()->Contains(resource1));
+ EXPECT_TRUE(GetMemoryCache()->Contains(resource2));
+
+ const KURL url = KURL("http://test/resource");
+ EXPECT_EQ(resource1, GetMemoryCache()->ResourceForURL(url));
+ EXPECT_EQ(resource1, GetMemoryCache()->ResourceForURL(
+ url, GetMemoryCache()->DefaultCacheIdentifier()));
+ EXPECT_EQ(resource2, GetMemoryCache()->ResourceForURL(url, "foo"));
+ EXPECT_EQ(nullptr, GetMemoryCache()->ResourceForURL(NullURL()));
+
+ FakeResource* resource3 =
+ FakeResource::Create("http://test/resource", Resource::kRaw);
+ resource3->SetCacheIdentifier("foo");
+ GetMemoryCache()->Remove(resource2);
+ GetMemoryCache()->Add(resource3);
+ EXPECT_TRUE(GetMemoryCache()->Contains(resource1));
+ EXPECT_FALSE(GetMemoryCache()->Contains(resource2));
+ EXPECT_TRUE(GetMemoryCache()->Contains(resource3));
+
+ HeapVector<Member<Resource>> resources =
+ GetMemoryCache()->ResourcesForURL(url);
+ EXPECT_EQ(2u, resources.size());
+
+ GetMemoryCache()->EvictResources();
+ EXPECT_FALSE(GetMemoryCache()->Contains(resource1));
+ EXPECT_FALSE(GetMemoryCache()->Contains(resource3));
+}
+
+TEST_F(MemoryCacheTest, FragmentIdentifier) {
+ const KURL url1 = KURL("http://test/resource#foo");
+ FakeResource* resource = FakeResource::Create(url1, Resource::kRaw);
+ GetMemoryCache()->Add(resource);
+ EXPECT_TRUE(GetMemoryCache()->Contains(resource));
+
+ EXPECT_EQ(resource, GetMemoryCache()->ResourceForURL(url1));
+
+ const KURL url2 = MemoryCache::RemoveFragmentIdentifierIfNeeded(url1);
+ EXPECT_EQ(resource, GetMemoryCache()->ResourceForURL(url2));
+}
+
+TEST_F(MemoryCacheTest, RemoveURLFromCache) {
+ const KURL url1 = KURL("http://test/resource1");
+ Persistent<FakeResource> resource1 =
+ FakeResource::Create(url1, Resource::kRaw);
+ GetMemoryCache()->Add(resource1);
+ EXPECT_TRUE(GetMemoryCache()->Contains(resource1));
+
+ GetMemoryCache()->RemoveURLFromCache(url1);
+ EXPECT_FALSE(GetMemoryCache()->Contains(resource1));
+
+ const KURL url2 = KURL("http://test/resource2#foo");
+ FakeResource* resource2 = FakeResource::Create(url2, Resource::kRaw);
+ GetMemoryCache()->Add(resource2);
+ EXPECT_TRUE(GetMemoryCache()->Contains(resource2));
+
+ GetMemoryCache()->RemoveURLFromCache(url2);
+ EXPECT_FALSE(GetMemoryCache()->Contains(resource2));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/preload_key.h b/chromium/third_party/blink/renderer/platform/loader/fetch/preload_key.h
new file mode 100644
index 00000000000..81273b6808a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/preload_key.h
@@ -0,0 +1,75 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_PRELOAD_KEY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_PRELOAD_KEY_H_
+
+#include "third_party/blink/renderer/platform/loader/fetch/resource.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl_hash.h"
+#include "third_party/blink/renderer/platform/wtf/hash_traits.h"
+
+namespace blink {
+
+// PreloadKey is a key type of the preloads map in a fetch group (a.k.a.
+// blink::ResourceFetcher).
+struct PreloadKey final {
+ public:
+ struct Hash {
+ STATIC_ONLY(Hash);
+
+ public:
+ static unsigned GetHash(const PreloadKey& key) {
+ return KURLHash::GetHash(key.url);
+ }
+ static bool Equal(const PreloadKey& x, const PreloadKey& y) {
+ return x == y;
+ }
+ static constexpr bool safe_to_compare_to_empty_or_deleted = false;
+ };
+
+ PreloadKey() = default;
+ PreloadKey(const KURL& url, Resource::Type type)
+ : url(RemoveFragmentFromUrl(url)), type(type) {}
+
+ bool operator==(const PreloadKey& x) const {
+ return url == x.url && type == x.type;
+ }
+
+ static KURL RemoveFragmentFromUrl(const KURL& src) {
+ if (!src.HasFragmentIdentifier())
+ return src;
+ KURL url = src;
+ url.RemoveFragmentIdentifier();
+ return url;
+ }
+
+ KURL url;
+ Resource::Type type = Resource::kMainResource;
+};
+
+} // namespace blink
+
+namespace WTF {
+
+template <>
+struct DefaultHash<blink::PreloadKey> {
+ using Hash = blink::PreloadKey::Hash;
+};
+
+template <>
+struct HashTraits<blink::PreloadKey>
+ : public SimpleClassHashTraits<blink::PreloadKey> {
+ static bool IsDeletedValue(const blink::PreloadKey& value) {
+ return HashTraits<blink::KURL>::IsDeletedValue(value.url);
+ }
+
+ static void ConstructDeletedValue(blink::PreloadKey& slot, bool zero_value) {
+ HashTraits<blink::KURL>::ConstructDeletedValue(slot.url, zero_value);
+ }
+};
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_PRELOAD_KEY_H_
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc
new file mode 100644
index 00000000000..083f95fa3f8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc
@@ -0,0 +1,470 @@
+/*
+ * Copyright (C) 2011 Google Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE INC. 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 "third_party/blink/renderer/platform/loader/fetch/raw_resource.h"
+
+#include <memory>
+#include "mojo/public/cpp/system/data_pipe.h"
+#include "services/network/public/mojom/request_context_frame_type.mojom-shared.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_thread.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h"
+#include "third_party/blink/renderer/platform/loader/fetch/memory_cache.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_client_walker.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
+#include "third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.h"
+#include "third_party/blink/renderer/platform/network/http_names.h"
+#include "third_party/blink/renderer/platform/scheduler/child/web_scheduler.h"
+
+namespace blink {
+
+RawResource* RawResource::FetchSynchronously(FetchParameters& params,
+ ResourceFetcher* fetcher) {
+ params.MakeSynchronous();
+ return ToRawResource(fetcher->RequestResource(
+ params, RawResourceFactory(Resource::kRaw), nullptr));
+}
+
+RawResource* RawResource::FetchImport(FetchParameters& params,
+ ResourceFetcher* fetcher,
+ RawResourceClient* client) {
+ DCHECK_EQ(params.GetResourceRequest().GetFrameType(),
+ network::mojom::RequestContextFrameType::kNone);
+ params.SetRequestContext(WebURLRequest::kRequestContextImport);
+ return ToRawResource(fetcher->RequestResource(
+ params, RawResourceFactory(Resource::kImportResource), client));
+}
+
+RawResource* RawResource::Fetch(FetchParameters& params,
+ ResourceFetcher* fetcher,
+ RawResourceClient* client) {
+ DCHECK_EQ(params.GetResourceRequest().GetFrameType(),
+ network::mojom::RequestContextFrameType::kNone);
+ DCHECK_NE(params.GetResourceRequest().GetRequestContext(),
+ WebURLRequest::kRequestContextUnspecified);
+ return ToRawResource(fetcher->RequestResource(
+ params, RawResourceFactory(Resource::kRaw), client));
+}
+
+RawResource* RawResource::FetchMainResource(
+ FetchParameters& params,
+ ResourceFetcher* fetcher,
+ RawResourceClient* client,
+ const SubstituteData& substitute_data) {
+ DCHECK_NE(params.GetResourceRequest().GetFrameType(),
+ network::mojom::RequestContextFrameType::kNone);
+ DCHECK(params.GetResourceRequest().GetRequestContext() ==
+ WebURLRequest::kRequestContextForm ||
+ params.GetResourceRequest().GetRequestContext() ==
+ WebURLRequest::kRequestContextFrame ||
+ params.GetResourceRequest().GetRequestContext() ==
+ WebURLRequest::kRequestContextHyperlink ||
+ params.GetResourceRequest().GetRequestContext() ==
+ WebURLRequest::kRequestContextIframe ||
+ params.GetResourceRequest().GetRequestContext() ==
+ WebURLRequest::kRequestContextInternal ||
+ params.GetResourceRequest().GetRequestContext() ==
+ WebURLRequest::kRequestContextLocation);
+
+ return ToRawResource(fetcher->RequestResource(
+ params, RawResourceFactory(Resource::kMainResource), client,
+ substitute_data));
+}
+
+RawResource* RawResource::FetchMedia(FetchParameters& params,
+ ResourceFetcher* fetcher,
+ RawResourceClient* client) {
+ DCHECK_EQ(params.GetResourceRequest().GetFrameType(),
+ network::mojom::RequestContextFrameType::kNone);
+ auto context = params.GetResourceRequest().GetRequestContext();
+ DCHECK(context == WebURLRequest::kRequestContextAudio ||
+ context == WebURLRequest::kRequestContextVideo);
+ Resource::Type type = (context == WebURLRequest::kRequestContextAudio)
+ ? Resource::kAudio
+ : Resource::kVideo;
+ return ToRawResource(
+ fetcher->RequestResource(params, RawResourceFactory(type), client));
+}
+
+RawResource* RawResource::FetchTextTrack(FetchParameters& params,
+ ResourceFetcher* fetcher,
+ RawResourceClient* client) {
+ DCHECK_EQ(params.GetResourceRequest().GetFrameType(),
+ network::mojom::RequestContextFrameType::kNone);
+ params.SetRequestContext(WebURLRequest::kRequestContextTrack);
+ return ToRawResource(fetcher->RequestResource(
+ params, RawResourceFactory(Resource::kTextTrack), client));
+}
+
+RawResource* RawResource::FetchManifest(FetchParameters& params,
+ ResourceFetcher* fetcher,
+ RawResourceClient* client) {
+ DCHECK_EQ(params.GetResourceRequest().GetFrameType(),
+ network::mojom::RequestContextFrameType::kNone);
+ DCHECK_EQ(params.GetResourceRequest().GetRequestContext(),
+ WebURLRequest::kRequestContextManifest);
+ return ToRawResource(fetcher->RequestResource(
+ params, RawResourceFactory(Resource::kManifest), client));
+}
+
+RawResource::RawResource(const ResourceRequest& resource_request,
+ Type type,
+ const ResourceLoaderOptions& options)
+ : Resource(resource_request, type, options) {}
+
+void RawResource::AppendData(const char* data, size_t length) {
+ if (data_pipe_writer_) {
+ DCHECK_EQ(kDoNotBufferData, GetDataBufferingPolicy());
+ data_pipe_writer_->Write(data, length);
+ } else {
+ Resource::AppendData(data, length);
+ }
+}
+
+void RawResource::DidAddClient(ResourceClient* c) {
+ // CHECK()/RevalidationStartForbiddenScope are for
+ // https://crbug.com/640960#c24.
+ CHECK(!IsCacheValidator());
+ if (!HasClient(c))
+ return;
+ DCHECK(RawResourceClient::IsExpectedType(c));
+ RevalidationStartForbiddenScope revalidation_start_forbidden_scope(this);
+ RawResourceClient* client = static_cast<RawResourceClient*>(c);
+ for (const auto& redirect : RedirectChain()) {
+ ResourceRequest request(redirect.request_);
+ client->RedirectReceived(this, request, redirect.redirect_response_);
+ if (!HasClient(c))
+ return;
+ }
+
+ if (!GetResponse().IsNull()) {
+ client->ResponseReceived(this, GetResponse(),
+ std::move(data_consumer_handle_));
+ }
+ if (!HasClient(c))
+ return;
+ Resource::DidAddClient(client);
+}
+
+bool RawResource::WillFollowRedirect(
+ const ResourceRequest& new_request,
+ const ResourceResponse& redirect_response) {
+ bool follow = Resource::WillFollowRedirect(new_request, redirect_response);
+ // The base class method takes a const reference of a ResourceRequest and
+ // returns bool just for allowing RawResource to reject redirect. It must
+ // always return true.
+ DCHECK(follow);
+
+ DCHECK(!redirect_response.IsNull());
+ ResourceClientWalker<RawResourceClient> w(Clients());
+ while (RawResourceClient* c = w.Next()) {
+ if (!c->RedirectReceived(this, new_request, redirect_response))
+ follow = false;
+ }
+
+ return follow;
+}
+
+void RawResource::WillNotFollowRedirect() {
+ ResourceClientWalker<RawResourceClient> w(Clients());
+ while (RawResourceClient* c = w.Next())
+ c->RedirectBlocked();
+}
+
+SourceKeyedCachedMetadataHandler* RawResource::CacheHandler() {
+ return static_cast<SourceKeyedCachedMetadataHandler*>(
+ Resource::CacheHandler());
+}
+
+void RawResource::ResponseReceived(
+ const ResourceResponse& response,
+ std::unique_ptr<WebDataConsumerHandle> handle) {
+ if (response.WasFallbackRequiredByServiceWorker()) {
+ // The ServiceWorker asked us to re-fetch the request. This resource must
+ // not be reused.
+ // Note: This logic is needed here because DocumentThreadableLoader handles
+ // CORS independently from ResourceLoader. Fix it.
+ if (IsMainThread())
+ GetMemoryCache()->Remove(this);
+ }
+
+ Resource::ResponseReceived(response, nullptr);
+
+ DCHECK(!handle || !data_consumer_handle_);
+ if (!handle && Clients().size() > 0)
+ handle = std::move(data_consumer_handle_);
+ ResourceClientWalker<RawResourceClient> w(Clients());
+ DCHECK(Clients().size() <= 1 || !handle);
+ while (RawResourceClient* c = w.Next()) {
+ // |handle| is cleared when passed, but it's not a problem because |handle|
+ // is null when there are two or more clients, as asserted.
+ c->ResponseReceived(this, this->GetResponse(), std::move(handle));
+ }
+}
+
+CachedMetadataHandler* RawResource::CreateCachedMetadataHandler(
+ std::unique_ptr<CachedMetadataSender> send_callback) {
+ return new SourceKeyedCachedMetadataHandler(Encoding(),
+ std::move(send_callback));
+}
+
+void RawResource::SetSerializedCachedMetadata(const char* data, size_t size) {
+ Resource::SetSerializedCachedMetadata(data, size);
+
+ SourceKeyedCachedMetadataHandler* cache_handler = CacheHandler();
+ if (cache_handler) {
+ cache_handler->SetSerializedCachedMetadata(data, size);
+ }
+
+ ResourceClientWalker<RawResourceClient> w(Clients());
+ while (RawResourceClient* c = w.Next())
+ c->SetSerializedCachedMetadata(this, data, size);
+}
+
+void RawResource::DidSendData(unsigned long long bytes_sent,
+ unsigned long long total_bytes_to_be_sent) {
+ ResourceClientWalker<RawResourceClient> w(Clients());
+ while (RawResourceClient* c = w.Next())
+ c->DataSent(this, bytes_sent, total_bytes_to_be_sent);
+}
+
+void RawResource::DidDownloadData(int data_length) {
+ downloaded_file_length_ =
+ (downloaded_file_length_ ? *downloaded_file_length_ : 0) + data_length;
+ ResourceClientWalker<RawResourceClient> w(Clients());
+ while (RawResourceClient* c = w.Next())
+ c->DataDownloaded(this, data_length);
+}
+
+void RawResource::DidDownloadToBlob(scoped_refptr<BlobDataHandle> blob) {
+ downloaded_blob_ = blob;
+ ResourceClientWalker<RawResourceClient> w(Clients());
+ while (RawResourceClient* c = w.Next())
+ c->DidDownloadToBlob(this, blob);
+}
+
+void RawResource::ReportResourceTimingToClients(
+ const ResourceTimingInfo& info) {
+ ResourceClientWalker<RawResourceClient> w(Clients());
+ while (RawResourceClient* c = w.Next())
+ c->DidReceiveResourceTiming(this, info);
+}
+
+bool RawResource::MatchPreload(const FetchParameters& params,
+ base::SingleThreadTaskRunner* task_runner) {
+ if (!Resource::MatchPreload(params, task_runner))
+ return false;
+
+ // This is needed to call Platform::Current() below. Remove this branch
+ // when the calls are removed.
+ if (!IsMainThread())
+ return false;
+
+ if (!params.GetResourceRequest().UseStreamOnResponse())
+ return true;
+
+ if (ErrorOccurred())
+ return true;
+
+ // A preloaded resource is not for streaming.
+ DCHECK(!GetResourceRequest().UseStreamOnResponse());
+ DCHECK_EQ(GetDataBufferingPolicy(), kBufferData);
+
+ // Preloading for raw resources are not cached.
+ DCHECK(!IsMainThread() || !GetMemoryCache()->Contains(this));
+
+ constexpr auto kCapacity = 32 * 1024;
+ mojo::ScopedDataPipeProducerHandle producer;
+ mojo::ScopedDataPipeConsumerHandle consumer;
+ MojoCreateDataPipeOptions options;
+ options.struct_size = sizeof(MojoCreateDataPipeOptions);
+ options.flags = MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE;
+ options.element_num_bytes = 1;
+ options.capacity_num_bytes = kCapacity;
+
+ MojoResult result = mojo::CreateDataPipe(&options, &producer, &consumer);
+ if (result != MOJO_RESULT_OK)
+ return false;
+
+ data_consumer_handle_ =
+ Platform::Current()->CreateDataConsumerHandle(std::move(consumer));
+ data_pipe_writer_ = std::make_unique<BufferingDataPipeWriter>(
+ std::move(producer), task_runner);
+
+ if (Data()) {
+ Data()->ForEachSegment(
+ [this](const char* segment, size_t size, size_t offset) -> bool {
+ return data_pipe_writer_->Write(segment, size);
+ });
+ }
+ SetDataBufferingPolicy(kDoNotBufferData);
+
+ if (IsLoaded())
+ data_pipe_writer_->Finish();
+ return true;
+}
+
+void RawResource::NotifyFinished() {
+ if (data_pipe_writer_)
+ data_pipe_writer_->Finish();
+ Resource::NotifyFinished();
+}
+
+static bool ShouldIgnoreHeaderForCacheReuse(AtomicString header_name) {
+ // FIXME: This list of headers that don't affect cache policy almost certainly
+ // isn't complete.
+ DEFINE_STATIC_LOCAL(
+ HashSet<AtomicString>, headers,
+ ({"Cache-Control", "If-Modified-Since", "If-None-Match", "Origin",
+ "Pragma", "Purpose", "Referer", "User-Agent",
+ HTTPNames::X_DevTools_Emulate_Network_Conditions_Client_Id}));
+ return headers.Contains(header_name);
+}
+
+static bool IsCacheableHTTPMethod(const AtomicString& method) {
+ // Per http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.10,
+ // these methods always invalidate the cache entry.
+ return method != HTTPNames::POST && method != HTTPNames::PUT &&
+ method != "DELETE";
+}
+
+bool RawResource::CanReuse(
+ const FetchParameters& new_fetch_parameters,
+ scoped_refptr<const SecurityOrigin> new_source_origin) const {
+ const ResourceRequest& new_request =
+ new_fetch_parameters.GetResourceRequest();
+
+ if (GetDataBufferingPolicy() == kDoNotBufferData)
+ return false;
+
+ if (!IsCacheableHTTPMethod(GetResourceRequest().HttpMethod()))
+ return false;
+ if (GetResourceRequest().HttpMethod() != new_request.HttpMethod())
+ return false;
+
+ if (GetResourceRequest().HttpBody() != new_request.HttpBody())
+ return false;
+
+ if (GetResourceRequest().AllowStoredCredentials() !=
+ new_request.AllowStoredCredentials())
+ return false;
+
+ // Ensure most headers match the existing headers before continuing. Note that
+ // the list of ignored headers includes some headers explicitly related to
+ // caching. A more detailed check of caching policy will be performed later,
+ // this is simply a list of headers that we might permit to be different and
+ // still reuse the existing Resource.
+ const HTTPHeaderMap& new_headers = new_request.HttpHeaderFields();
+ const HTTPHeaderMap& old_headers = GetResourceRequest().HttpHeaderFields();
+
+ for (const auto& header : new_headers) {
+ AtomicString header_name = header.key;
+ if (!ShouldIgnoreHeaderForCacheReuse(header_name) &&
+ header.value != old_headers.Get(header_name))
+ return false;
+ }
+
+ for (const auto& header : old_headers) {
+ AtomicString header_name = header.key;
+ if (!ShouldIgnoreHeaderForCacheReuse(header_name) &&
+ header.value != new_headers.Get(header_name))
+ return false;
+ }
+
+ return Resource::CanReuse(new_fetch_parameters, std::move(new_source_origin));
+}
+
+RawResourceClientStateChecker::RawResourceClientStateChecker()
+ : state_(kNotAddedAsClient) {}
+
+RawResourceClientStateChecker::~RawResourceClientStateChecker() = default;
+
+NEVER_INLINE void RawResourceClientStateChecker::WillAddClient() {
+ SECURITY_CHECK(state_ == kNotAddedAsClient);
+ state_ = kStarted;
+}
+
+NEVER_INLINE void RawResourceClientStateChecker::WillRemoveClient() {
+ SECURITY_CHECK(state_ != kNotAddedAsClient);
+ state_ = kNotAddedAsClient;
+}
+
+NEVER_INLINE void RawResourceClientStateChecker::RedirectReceived() {
+ SECURITY_CHECK(state_ == kStarted);
+}
+
+NEVER_INLINE void RawResourceClientStateChecker::RedirectBlocked() {
+ SECURITY_CHECK(state_ == kStarted);
+ state_ = kRedirectBlocked;
+}
+
+NEVER_INLINE void RawResourceClientStateChecker::DataSent() {
+ SECURITY_CHECK(state_ == kStarted);
+}
+
+NEVER_INLINE void RawResourceClientStateChecker::ResponseReceived() {
+ SECURITY_CHECK(state_ == kStarted);
+ state_ = kResponseReceived;
+}
+
+NEVER_INLINE void RawResourceClientStateChecker::SetSerializedCachedMetadata() {
+ SECURITY_CHECK(state_ == kResponseReceived);
+ state_ = kSetSerializedCachedMetadata;
+}
+
+NEVER_INLINE void RawResourceClientStateChecker::DataReceived() {
+ SECURITY_CHECK(state_ == kResponseReceived ||
+ state_ == kSetSerializedCachedMetadata ||
+ state_ == kDataReceived);
+ state_ = kDataReceived;
+}
+
+NEVER_INLINE void RawResourceClientStateChecker::DataDownloaded() {
+ SECURITY_CHECK(state_ == kResponseReceived ||
+ state_ == kSetSerializedCachedMetadata ||
+ state_ == kDataDownloaded);
+ state_ = kDataDownloaded;
+}
+
+NEVER_INLINE void RawResourceClientStateChecker::DidDownloadToBlob() {
+ SECURITY_CHECK(state_ == kResponseReceived ||
+ state_ == kSetSerializedCachedMetadata ||
+ state_ == kDataDownloaded);
+ state_ = kDidDownloadToBlob;
+}
+
+NEVER_INLINE void RawResourceClientStateChecker::NotifyFinished(
+ Resource* resource) {
+ SECURITY_CHECK(state_ != kNotAddedAsClient);
+ SECURITY_CHECK(state_ != kNotifyFinished);
+ SECURITY_CHECK(resource->ErrorOccurred() ||
+ (state_ == kResponseReceived ||
+ state_ == kSetSerializedCachedMetadata ||
+ state_ == kDataReceived || state_ == kDataDownloaded ||
+ state_ == kDidDownloadToBlob));
+ state_ = kNotifyFinished;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource.h b/chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource.h
new file mode 100644
index 00000000000..88c232ccea0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource.h
@@ -0,0 +1,247 @@
+/*
+ Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
+ Copyright (C) 2001 Dirk Mueller <mueller@kde.org>
+ Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
+ Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RAW_RESOURCE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RAW_RESOURCE_H_
+
+#include <memory>
+
+#include "third_party/blink/public/platform/web_data_consumer_handle.h"
+#include "third_party/blink/renderer/platform/loader/fetch/buffering_data_pipe_writer.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_client.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+
+namespace blink {
+class WebDataConsumerHandle;
+class FetchParameters;
+class RawResourceClient;
+class ResourceFetcher;
+class SubstituteData;
+class SourceKeyedCachedMetadataHandler;
+
+class PLATFORM_EXPORT RawResource final : public Resource {
+ public:
+ static RawResource* FetchSynchronously(FetchParameters&, ResourceFetcher*);
+ static RawResource* Fetch(FetchParameters&,
+ ResourceFetcher*,
+ RawResourceClient*);
+ static RawResource* FetchMainResource(FetchParameters&,
+ ResourceFetcher*,
+ RawResourceClient*,
+ const SubstituteData&);
+ static RawResource* FetchImport(FetchParameters&,
+ ResourceFetcher*,
+ RawResourceClient*);
+ static RawResource* FetchMedia(FetchParameters&,
+ ResourceFetcher*,
+ RawResourceClient*);
+ static RawResource* FetchTextTrack(FetchParameters&,
+ ResourceFetcher*,
+ RawResourceClient*);
+ static RawResource* FetchManifest(FetchParameters&,
+ ResourceFetcher*,
+ RawResourceClient*);
+
+ // Exposed for testing
+ static RawResource* CreateForTest(ResourceRequest request, Type type) {
+ ResourceLoaderOptions options;
+ return new RawResource(request, type, options);
+ }
+ static RawResource* CreateForTest(const KURL& url, Type type) {
+ ResourceRequest request(url);
+ return CreateForTest(request, type);
+ }
+ static RawResource* CreateForTest(const char* url, Type type) {
+ return CreateForTest(KURL(url), type);
+ }
+
+ // Resource implementation
+ bool CanReuse(
+ const FetchParameters&,
+ scoped_refptr<const SecurityOrigin> new_source_origin) const override;
+ bool WillFollowRedirect(const ResourceRequest&,
+ const ResourceResponse&) override;
+
+ void SetSerializedCachedMetadata(const char*, size_t) override;
+
+ // Used for code caching of scripts with source code inline in the HTML.
+ // Returns a cache handler which can store multiple cache metadata entries,
+ // keyed by the source code of the script.
+ SourceKeyedCachedMetadataHandler* CacheHandler();
+
+ WTF::Optional<int64_t> DownloadedFileLength() const {
+ return downloaded_file_length_;
+ }
+ scoped_refptr<BlobDataHandle> DownloadedBlob() const {
+ return downloaded_blob_;
+ }
+
+ protected:
+ CachedMetadataHandler* CreateCachedMetadataHandler(
+ std::unique_ptr<CachedMetadataSender> send_callback) override;
+
+ private:
+ class RawResourceFactory : public NonTextResourceFactory {
+ public:
+ explicit RawResourceFactory(Resource::Type type)
+ : NonTextResourceFactory(type) {}
+
+ Resource* Create(const ResourceRequest& request,
+ const ResourceLoaderOptions& options) const override {
+ return new RawResource(request, type_, options);
+ }
+ };
+
+ RawResource(const ResourceRequest&, Type, const ResourceLoaderOptions&);
+
+ // Resource implementation
+ void DidAddClient(ResourceClient*) override;
+ void AppendData(const char*, size_t) override;
+ bool ShouldIgnoreHTTPStatusCodeErrors() const override {
+ return !IsLinkPreload();
+ }
+ void WillNotFollowRedirect() override;
+ void ResponseReceived(const ResourceResponse&,
+ std::unique_ptr<WebDataConsumerHandle>) override;
+ void DidSendData(unsigned long long bytes_sent,
+ unsigned long long total_bytes_to_be_sent) override;
+ void DidDownloadData(int) override;
+ void DidDownloadToBlob(scoped_refptr<BlobDataHandle>) override;
+ void ReportResourceTimingToClients(const ResourceTimingInfo&) override;
+ bool MatchPreload(const FetchParameters&,
+ base::SingleThreadTaskRunner*) override;
+ void NotifyFinished() override;
+
+ WTF::Optional<int64_t> downloaded_file_length_;
+ scoped_refptr<BlobDataHandle> downloaded_blob_;
+
+ // Used for preload matching.
+ std::unique_ptr<BufferingDataPipeWriter> data_pipe_writer_;
+ std::unique_ptr<WebDataConsumerHandle> data_consumer_handle_;
+};
+
+// TODO(yhirano): Recover #if ENABLE_SECURITY_ASSERT when we stop adding
+// RawResources to MemoryCache.
+inline bool IsRawResource(const Resource& resource) {
+ Resource::Type type = resource.GetType();
+ return type == Resource::kMainResource || type == Resource::kRaw ||
+ type == Resource::kTextTrack || type == Resource::kAudio ||
+ type == Resource::kVideo || type == Resource::kManifest ||
+ type == Resource::kImportResource;
+}
+inline RawResource* ToRawResource(Resource* resource) {
+ SECURITY_DCHECK(!resource || IsRawResource(*resource));
+ return static_cast<RawResource*>(resource);
+}
+
+class PLATFORM_EXPORT RawResourceClient : public ResourceClient {
+ public:
+ static bool IsExpectedType(ResourceClient* client) {
+ return client->GetResourceClientType() == kRawResourceType;
+ }
+ ResourceClientType GetResourceClientType() const final {
+ return kRawResourceType;
+ }
+
+ // The order of the callbacks is as follows:
+ // [Case 1] A successful load:
+ // 0+ redirectReceived() and/or dataSent()
+ // 1 responseReceived()
+ // 0-1 setSerializedCachedMetadata()
+ // 0+ dataReceived() or dataDownloaded(), but never both
+ // 1 notifyFinished() with errorOccurred() = false
+ // [Case 2] When redirect is blocked:
+ // 0+ redirectReceived() and/or dataSent()
+ // 1 redirectBlocked()
+ // 1 notifyFinished() with errorOccurred() = true
+ // [Case 3] Other failures:
+ // notifyFinished() with errorOccurred() = true is called at any time
+ // (unless notifyFinished() is already called).
+ // In all cases:
+ // No callbacks are made after notifyFinished() or
+ // removeClient() is called.
+ virtual void DataSent(Resource*,
+ unsigned long long /* bytesSent */,
+ unsigned long long /* totalBytesToBeSent */) {}
+ virtual void ResponseReceived(Resource*,
+ const ResourceResponse&,
+ std::unique_ptr<WebDataConsumerHandle>) {}
+ virtual void SetSerializedCachedMetadata(Resource*, const char*, size_t) {}
+ virtual bool RedirectReceived(Resource*,
+ const ResourceRequest&,
+ const ResourceResponse&) {
+ return true;
+ }
+ virtual void RedirectBlocked() {}
+ virtual void DataDownloaded(Resource*, int) {}
+ virtual void DidReceiveResourceTiming(Resource*, const ResourceTimingInfo&) {}
+ // Called for requests that had DownloadToBlob set to true. Can be called with
+ // null if creating the blob failed for some reason (but the download itself
+ // otherwise succeeded). Could also not be called at all if the downloaded
+ // resource ended up being zero bytes.
+ virtual void DidDownloadToBlob(Resource*, scoped_refptr<BlobDataHandle>) {}
+};
+
+// Checks the sequence of callbacks of RawResourceClient. This can be used only
+// when a RawResourceClient is added as a client to at most one RawResource.
+class PLATFORM_EXPORT RawResourceClientStateChecker final {
+ public:
+ RawResourceClientStateChecker();
+ ~RawResourceClientStateChecker();
+
+ // Call before addClient()/removeClient() is called.
+ void WillAddClient();
+ void WillRemoveClient();
+
+ // Call RawResourceClientStateChecker::f() at the beginning of
+ // RawResourceClient::f().
+ void RedirectReceived();
+ void RedirectBlocked();
+ void DataSent();
+ void ResponseReceived();
+ void SetSerializedCachedMetadata();
+ void DataReceived();
+ void DataDownloaded();
+ void DidDownloadToBlob();
+ void NotifyFinished(Resource*);
+
+ private:
+ enum State {
+ kNotAddedAsClient,
+ kStarted,
+ kRedirectBlocked,
+ kResponseReceived,
+ kSetSerializedCachedMetadata,
+ kDataReceived,
+ kDataDownloaded,
+ kDidDownloadToBlob,
+ kNotifyFinished
+ };
+ State state_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RAW_RESOURCE_H_
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource_test.cc
new file mode 100644
index 00000000000..3a973b9476e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource_test.cc
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 2013, 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 "third_party/blink/renderer/platform/loader/fetch/raw_resource.h"
+
+#include <memory>
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_thread.h"
+#include "third_party/blink/public/platform/web_url.h"
+#include "third_party/blink/public/platform/web_url_response.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/loader/fetch/memory_cache.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h"
+#include "third_party/blink/renderer/platform/scheduler/child/web_scheduler.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
+#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+
+namespace blink {
+
+class RawResourceTest : public testing::Test {
+ public:
+ RawResourceTest() = default;
+ ~RawResourceTest() override = default;
+
+ protected:
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RawResourceTest);
+};
+
+TEST_F(RawResourceTest, DontIgnoreAcceptForCacheReuse) {
+ ResourceRequest jpeg_request;
+ jpeg_request.SetHTTPAccept("image/jpeg");
+
+ scoped_refptr<const SecurityOrigin> source_origin =
+ SecurityOrigin::CreateUnique();
+
+ RawResource* jpeg_resource(
+ RawResource::CreateForTest(jpeg_request, Resource::kRaw));
+ jpeg_resource->SetSourceOrigin(source_origin);
+
+ ResourceRequest png_request;
+ png_request.SetHTTPAccept("image/png");
+
+ EXPECT_FALSE(
+ jpeg_resource->CanReuse(FetchParameters(png_request), source_origin));
+}
+
+class DummyClient final : public GarbageCollectedFinalized<DummyClient>,
+ public RawResourceClient {
+ USING_GARBAGE_COLLECTED_MIXIN(DummyClient);
+
+ public:
+ DummyClient() : called_(false), number_of_redirects_received_(0) {}
+ ~DummyClient() override = default;
+
+ // ResourceClient implementation.
+ void NotifyFinished(Resource* resource) override { called_ = true; }
+ String DebugName() const override { return "DummyClient"; }
+
+ void DataReceived(Resource*, const char* data, size_t length) override {
+ data_.Append(data, length);
+ }
+
+ bool RedirectReceived(Resource*,
+ const ResourceRequest&,
+ const ResourceResponse&) override {
+ ++number_of_redirects_received_;
+ return true;
+ }
+
+ bool Called() { return called_; }
+ int NumberOfRedirectsReceived() const {
+ return number_of_redirects_received_;
+ }
+ const Vector<char>& Data() { return data_; }
+ void Trace(blink::Visitor* visitor) override {
+ RawResourceClient::Trace(visitor);
+ }
+
+ private:
+ bool called_;
+ int number_of_redirects_received_;
+ Vector<char> data_;
+};
+
+// This client adds another client when notified.
+class AddingClient final : public GarbageCollectedFinalized<AddingClient>,
+ public RawResourceClient {
+ USING_GARBAGE_COLLECTED_MIXIN(AddingClient);
+
+ public:
+ AddingClient(DummyClient* client, Resource* resource)
+ : dummy_client_(client), resource_(resource) {}
+
+ ~AddingClient() override = default;
+
+ // ResourceClient implementation.
+ void NotifyFinished(Resource* resource) override {
+ // First schedule an asynchronous task to remove the client.
+ // We do not expect a client to be called if the client is removed before
+ // a callback invocation task queued inside addClient() is scheduled.
+ Platform::Current()->CurrentThread()->GetTaskRunner()->PostTask(
+ FROM_HERE,
+ WTF::Bind(&AddingClient::RemoveClient, WrapPersistent(this)));
+ resource->AddClient(
+ dummy_client_,
+ Platform::Current()->CurrentThread()->GetTaskRunner().get());
+ }
+ String DebugName() const override { return "AddingClient"; }
+
+ void RemoveClient() { resource_->RemoveClient(dummy_client_); }
+
+ void Trace(blink::Visitor* visitor) override {
+ visitor->Trace(dummy_client_);
+ visitor->Trace(resource_);
+ RawResourceClient::Trace(visitor);
+ }
+
+ private:
+ Member<DummyClient> dummy_client_;
+ Member<Resource> resource_;
+};
+
+TEST_F(RawResourceTest, AddClientDuringCallback) {
+ Resource* raw = RawResource::CreateForTest("data:text/html,", Resource::kRaw);
+ raw->SetResponse(ResourceResponse(KURL("http://600.613/")));
+ raw->FinishForTest();
+ EXPECT_FALSE(raw->GetResponse().IsNull());
+
+ Persistent<DummyClient> dummy_client = new DummyClient();
+ Persistent<AddingClient> adding_client =
+ new AddingClient(dummy_client.Get(), raw);
+ raw->AddClient(adding_client,
+ Platform::Current()->CurrentThread()->GetTaskRunner().get());
+ platform_->RunUntilIdle();
+ raw->RemoveClient(adding_client);
+ EXPECT_FALSE(dummy_client->Called());
+ EXPECT_FALSE(raw->IsAlive());
+}
+
+// This client removes another client when notified.
+class RemovingClient : public GarbageCollectedFinalized<RemovingClient>,
+ public RawResourceClient {
+ USING_GARBAGE_COLLECTED_MIXIN(RemovingClient);
+
+ public:
+ explicit RemovingClient(DummyClient* client) : dummy_client_(client) {}
+
+ ~RemovingClient() override = default;
+
+ // ResourceClient implementation.
+ void NotifyFinished(Resource* resource) override {
+ resource->RemoveClient(dummy_client_);
+ resource->RemoveClient(this);
+ }
+ String DebugName() const override { return "RemovingClient"; }
+ void Trace(blink::Visitor* visitor) override {
+ visitor->Trace(dummy_client_);
+ RawResourceClient::Trace(visitor);
+ }
+
+ private:
+ Member<DummyClient> dummy_client_;
+};
+
+TEST_F(RawResourceTest, RemoveClientDuringCallback) {
+ Resource* raw = RawResource::CreateForTest("data:text/html,", Resource::kRaw);
+ raw->SetResponse(ResourceResponse(KURL("http://600.613/")));
+ raw->FinishForTest();
+ EXPECT_FALSE(raw->GetResponse().IsNull());
+
+ Persistent<DummyClient> dummy_client = new DummyClient();
+ Persistent<RemovingClient> removing_client =
+ new RemovingClient(dummy_client.Get());
+ raw->AddClient(dummy_client,
+ Platform::Current()->CurrentThread()->GetTaskRunner().get());
+ raw->AddClient(removing_client,
+ Platform::Current()->CurrentThread()->GetTaskRunner().get());
+ platform_->RunUntilIdle();
+ EXPECT_FALSE(raw->IsAlive());
+}
+
+TEST_F(RawResourceTest,
+ CanReuseDevToolsEmulateNetworkConditionsClientIdHeader) {
+ scoped_refptr<const SecurityOrigin> source_origin =
+ SecurityOrigin::CreateUnique();
+ ResourceRequest request("data:text/html,");
+ request.SetHTTPHeaderField(
+ HTTPNames::X_DevTools_Emulate_Network_Conditions_Client_Id, "Foo");
+ Resource* raw = RawResource::CreateForTest(request, Resource::kRaw);
+ raw->SetSourceOrigin(source_origin);
+ EXPECT_TRUE(raw->CanReuse(FetchParameters(ResourceRequest("data:text/html,")),
+ source_origin));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource.cc
new file mode 100644
index 00000000000..13a3f06f3d0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource.cc
@@ -0,0 +1,1207 @@
+/*
+ Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
+ Copyright (C) 2001 Dirk Mueller (mueller@kde.org)
+ Copyright (C) 2002 Waldo Bastian (bastian@kde.org)
+ Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
+ Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All
+ rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "third_party/blink/renderer/platform/loader/fetch/resource.h"
+
+#include <stdint.h>
+
+#include <algorithm>
+#include <cassert>
+#include <memory>
+
+#include "base/single_thread_task_runner.h"
+#include "build/build_config.h"
+#include "services/network/public/mojom/fetch_api.mojom-blink.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_security_origin.h"
+#include "third_party/blink/renderer/platform/histogram.h"
+#include "third_party/blink/renderer/platform/instance_counters.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/loader/cors/cors.h"
+#include "third_party/blink/renderer/platform/loader/fetch/cached_metadata.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_initiator_type_names.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h"
+#include "third_party/blink/renderer/platform/loader/fetch/integrity_metadata.h"
+#include "third_party/blink/renderer/platform/loader/fetch/memory_cache.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_client.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_client_walker.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_finish_observer.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loader.h"
+#include "third_party/blink/renderer/platform/network/http_parsers.h"
+#include "third_party/blink/renderer/platform/scheduler/child/web_scheduler.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+namespace {
+
+void NotifyFinishObservers(
+ HeapHashSet<WeakMember<ResourceFinishObserver>>* observers) {
+ for (const auto& observer : *observers)
+ observer->NotifyFinished();
+}
+
+} // namespace
+
+// These response headers are not copied from a revalidated response to the
+// cached response headers. For compatibility, this list is based on Chromium's
+// net/http/http_response_headers.cc.
+const char* const kHeadersToIgnoreAfterRevalidation[] = {
+ "allow",
+ "connection",
+ "etag",
+ "expires",
+ "keep-alive",
+ "last-modified",
+ "proxy-authenticate",
+ "proxy-connection",
+ "trailer",
+ "transfer-encoding",
+ "upgrade",
+ "www-authenticate",
+ "x-frame-options",
+ "x-xss-protection",
+};
+
+// Some header prefixes mean "Don't copy this header from a 304 response.".
+// Rather than listing all the relevant headers, we can consolidate them into
+// this list, also grabbed from Chromium's net/http/http_response_headers.cc.
+const char* const kHeaderPrefixesToIgnoreAfterRevalidation[] = {
+ "content-", "x-content-", "x-webkit-"};
+
+static inline bool ShouldUpdateHeaderAfterRevalidation(
+ const AtomicString& header) {
+ for (size_t i = 0; i < WTF_ARRAY_LENGTH(kHeadersToIgnoreAfterRevalidation);
+ i++) {
+ if (DeprecatedEqualIgnoringCase(header,
+ kHeadersToIgnoreAfterRevalidation[i]))
+ return false;
+ }
+ for (size_t i = 0;
+ i < WTF_ARRAY_LENGTH(kHeaderPrefixesToIgnoreAfterRevalidation); i++) {
+ if (header.StartsWithIgnoringASCIICase(
+ kHeaderPrefixesToIgnoreAfterRevalidation[i]))
+ return false;
+ }
+ return true;
+}
+
+// This is a CachedMetadataSender implementation for normal responses.
+class CachedMetadataSenderImpl : public CachedMetadataSender {
+ public:
+ CachedMetadataSenderImpl(const Resource*);
+ ~CachedMetadataSenderImpl() override = default;
+
+ void Send(const char*, size_t) override;
+ bool IsServedFromCacheStorage() override { return false; }
+
+ private:
+ const KURL response_url_;
+ const Time response_time_;
+};
+
+CachedMetadataSenderImpl::CachedMetadataSenderImpl(const Resource* resource)
+ : response_url_(resource->GetResponse().Url()),
+ response_time_(resource->GetResponse().ResponseTime()) {
+ DCHECK(resource->GetResponse().CacheStorageCacheName().IsNull());
+}
+
+void CachedMetadataSenderImpl::Send(const char* data, size_t size) {
+ Platform::Current()->CacheMetadata(response_url_, response_time_, data, size);
+}
+
+// This is a CachedMetadataSender implementation that does nothing.
+class NullCachedMetadataSender : public CachedMetadataSender {
+ public:
+ NullCachedMetadataSender() = default;
+ ~NullCachedMetadataSender() override = default;
+
+ void Send(const char*, size_t) override {}
+ bool IsServedFromCacheStorage() override { return false; }
+};
+
+// This is a CachedMetadataSender implementation for responses that are served
+// by a ServiceWorker from cache storage.
+class ServiceWorkerCachedMetadataSender : public CachedMetadataSender {
+ public:
+ ServiceWorkerCachedMetadataSender(const Resource*, const SecurityOrigin*);
+ ~ServiceWorkerCachedMetadataSender() override = default;
+
+ void Send(const char*, size_t) override;
+ bool IsServedFromCacheStorage() override { return true; }
+
+ private:
+ const KURL response_url_;
+ const Time response_time_;
+ const String cache_storage_cache_name_;
+ scoped_refptr<const SecurityOrigin> security_origin_;
+};
+
+ServiceWorkerCachedMetadataSender::ServiceWorkerCachedMetadataSender(
+ const Resource* resource,
+ const SecurityOrigin* security_origin)
+ : response_url_(resource->GetResponse().Url()),
+ response_time_(resource->GetResponse().ResponseTime()),
+ cache_storage_cache_name_(
+ resource->GetResponse().CacheStorageCacheName()),
+ security_origin_(security_origin) {
+ DCHECK(!cache_storage_cache_name_.IsNull());
+}
+
+void ServiceWorkerCachedMetadataSender::Send(const char* data, size_t size) {
+ Platform::Current()->CacheMetadataInCacheStorage(
+ response_url_, response_time_, data, size,
+ WebSecurityOrigin(security_origin_), cache_storage_cache_name_);
+}
+
+Resource::Resource(const ResourceRequest& request,
+ Type type,
+ const ResourceLoaderOptions& options)
+ : type_(type),
+ status_(ResourceStatus::kNotStarted),
+ load_finish_time_(0),
+ identifier_(0),
+ preload_discovery_time_(0.0),
+ encoded_size_(0),
+ encoded_size_memory_usage_(0),
+ decoded_size_(0),
+ overhead_size_(CalculateOverheadSize()),
+ cache_identifier_(MemoryCache::DefaultCacheIdentifier()),
+ link_preload_(false),
+ is_revalidating_(false),
+ is_alive_(false),
+ is_add_remove_client_prohibited_(false),
+ integrity_disposition_(ResourceIntegrityDisposition::kNotChecked),
+ options_(options),
+ response_timestamp_(CurrentTime()),
+ resource_request_(request) {
+ InstanceCounters::IncrementCounter(InstanceCounters::kResourceCounter);
+
+ if (IsMainThread())
+ MemoryCoordinator::Instance().RegisterClient(this);
+}
+
+Resource::~Resource() {
+ InstanceCounters::DecrementCounter(InstanceCounters::kResourceCounter);
+}
+
+void Resource::Trace(blink::Visitor* visitor) {
+ visitor->Trace(loader_);
+ visitor->Trace(cache_handler_);
+ visitor->Trace(clients_);
+ visitor->Trace(clients_awaiting_callback_);
+ visitor->Trace(finished_clients_);
+ visitor->Trace(finish_observers_);
+ MemoryCoordinatorClient::Trace(visitor);
+}
+
+void Resource::SetLoader(ResourceLoader* loader) {
+ CHECK(!loader_);
+ DCHECK(StillNeedsLoad());
+ loader_ = loader;
+ status_ = ResourceStatus::kPending;
+}
+
+void Resource::CheckResourceIntegrity() {
+ // Skip the check and reuse the previous check result, especially on
+ // successful revalidation.
+ if (IntegrityDisposition() != ResourceIntegrityDisposition::kNotChecked)
+ return;
+
+ // Loading error occurred? Then result is uncheckable.
+ integrity_report_info_.Clear();
+ if (ErrorOccurred()) {
+ CHECK(!Data());
+ integrity_disposition_ = ResourceIntegrityDisposition::kFailed;
+ return;
+ }
+
+ // No integrity attributes to check? Then we're passing.
+ if (IntegrityMetadata().IsEmpty()) {
+ integrity_disposition_ = ResourceIntegrityDisposition::kPassed;
+ return;
+ }
+
+ const char* data = nullptr;
+ size_t data_length = 0;
+
+ // Edge case: If a resource actually has zero bytes then it will not
+ // typically have a resource buffer, but we still need to check integrity
+ // because people might want to assert a zero-length resource.
+ CHECK(DecodedSize() == 0 || Data());
+ if (Data()) {
+ data = Data()->Data();
+ data_length = Data()->size();
+ }
+
+ if (SubresourceIntegrity::CheckSubresourceIntegrity(IntegrityMetadata(), data,
+ data_length, Url(), *this,
+ integrity_report_info_))
+ integrity_disposition_ = ResourceIntegrityDisposition::kPassed;
+ else
+ integrity_disposition_ = ResourceIntegrityDisposition::kFailed;
+ DCHECK_NE(IntegrityDisposition(), ResourceIntegrityDisposition::kNotChecked);
+}
+
+void Resource::NotifyFinished() {
+ DCHECK(IsLoaded());
+
+ ResourceClientWalker<ResourceClient> w(clients_);
+ while (ResourceClient* c = w.Next()) {
+ MarkClientFinished(c);
+ c->NotifyFinished(this);
+ }
+}
+
+void Resource::MarkClientFinished(ResourceClient* client) {
+ if (clients_.Contains(client)) {
+ finished_clients_.insert(client);
+ clients_.erase(client);
+ }
+}
+
+void Resource::AppendData(const char* data, size_t length) {
+ TRACE_EVENT0("blink", "Resource::appendData");
+ DCHECK(!is_revalidating_);
+ DCHECK(!ErrorOccurred());
+ if (options_.data_buffering_policy == kBufferData) {
+ if (data_)
+ data_->Append(data, length);
+ else
+ data_ = SharedBuffer::Create(data, length);
+ SetEncodedSize(data_->size());
+ }
+ ResourceClientWalker<ResourceClient> w(Clients());
+ while (ResourceClient* c = w.Next())
+ c->DataReceived(this, data, length);
+}
+
+void Resource::SetResourceBuffer(scoped_refptr<SharedBuffer> resource_buffer) {
+ DCHECK(!is_revalidating_);
+ DCHECK(!ErrorOccurred());
+ DCHECK_EQ(options_.data_buffering_policy, kBufferData);
+ data_ = std::move(resource_buffer);
+ SetEncodedSize(data_->size());
+}
+
+void Resource::ClearData() {
+ data_ = nullptr;
+ encoded_size_memory_usage_ = 0;
+}
+
+void Resource::TriggerNotificationForFinishObservers(
+ base::SingleThreadTaskRunner* task_runner) {
+ if (finish_observers_.IsEmpty())
+ return;
+
+ auto* new_collections = new HeapHashSet<WeakMember<ResourceFinishObserver>>(
+ std::move(finish_observers_));
+ finish_observers_.clear();
+
+ task_runner->PostTask(FROM_HERE, WTF::Bind(&NotifyFinishObservers,
+ WrapPersistent(new_collections)));
+
+ DidRemoveClientOrObserver();
+}
+
+void Resource::SetDataBufferingPolicy(
+ DataBufferingPolicy data_buffering_policy) {
+ options_.data_buffering_policy = data_buffering_policy;
+ ClearData();
+ SetEncodedSize(0);
+}
+
+void Resource::FinishAsError(const ResourceError& error,
+ base::SingleThreadTaskRunner* task_runner) {
+ error_ = error;
+ is_revalidating_ = false;
+
+ if (IsMainThread())
+ GetMemoryCache()->Remove(this);
+
+ if (!ErrorOccurred())
+ SetStatus(ResourceStatus::kLoadError);
+ DCHECK(ErrorOccurred());
+ ClearData();
+ loader_ = nullptr;
+ CheckResourceIntegrity();
+ TriggerNotificationForFinishObservers(task_runner);
+ NotifyFinished();
+}
+
+void Resource::Finish(double load_finish_time,
+ base::SingleThreadTaskRunner* task_runner) {
+ DCHECK(!is_revalidating_);
+ load_finish_time_ = load_finish_time;
+ if (!ErrorOccurred())
+ status_ = ResourceStatus::kCached;
+ loader_ = nullptr;
+ CheckResourceIntegrity();
+ TriggerNotificationForFinishObservers(task_runner);
+ NotifyFinished();
+}
+
+AtomicString Resource::HttpContentType() const {
+ return GetResponse().HttpContentType();
+}
+
+bool Resource::PassesAccessControlCheck(
+ const SecurityOrigin& security_origin) const {
+ WTF::Optional<network::mojom::CORSError> cors_error = CORS::CheckAccess(
+ GetResponse().Url(), GetResponse().HttpStatusCode(),
+ GetResponse().HttpHeaderFields(),
+ LastResourceRequest().GetFetchCredentialsMode(), security_origin);
+
+ return !cors_error;
+}
+
+bool Resource::MustRefetchDueToIntegrityMetadata(
+ const FetchParameters& params) const {
+ if (params.IntegrityMetadata().IsEmpty())
+ return false;
+
+ return !IntegrityMetadata::SetsEqual(IntegrityMetadata(),
+ params.IntegrityMetadata());
+}
+
+static double CurrentAge(const ResourceResponse& response,
+ double response_timestamp) {
+ // RFC2616 13.2.3
+ // No compensation for latency as that is not terribly important in practice
+ double date_value = response.Date();
+ double apparent_age = std::isfinite(date_value)
+ ? std::max(0., response_timestamp - date_value)
+ : 0;
+ double age_value = response.Age();
+ double corrected_received_age = std::isfinite(age_value)
+ ? std::max(apparent_age, age_value)
+ : apparent_age;
+ double resident_time = CurrentTime() - response_timestamp;
+ return corrected_received_age + resident_time;
+}
+
+static double FreshnessLifetime(const ResourceResponse& response,
+ double response_timestamp) {
+#if !defined(OS_ANDROID)
+ // On desktop, local files should be reloaded in case they change.
+ if (response.Url().IsLocalFile())
+ return 0;
+#endif
+
+ // Cache other non-http / non-filesystem resources liberally.
+ if (!response.Url().ProtocolIsInHTTPFamily() &&
+ !response.Url().ProtocolIs("filesystem"))
+ return std::numeric_limits<double>::max();
+
+ // RFC2616 13.2.4
+ double max_age_value = response.CacheControlMaxAge();
+ if (std::isfinite(max_age_value))
+ return max_age_value;
+ double expires_value = response.Expires();
+ double date_value = response.Date();
+ double creation_time =
+ std::isfinite(date_value) ? date_value : response_timestamp;
+ if (std::isfinite(expires_value))
+ return expires_value - creation_time;
+ double last_modified_value = response.LastModified();
+ if (std::isfinite(last_modified_value))
+ return (creation_time - last_modified_value) * 0.1;
+ // If no cache headers are present, the specification leaves the decision to
+ // the UA. Other browsers seem to opt for 0.
+ return 0;
+}
+
+static bool CanUseResponse(const ResourceResponse& response,
+ double response_timestamp) {
+ if (response.IsNull())
+ return false;
+
+ if (response.CacheControlContainsNoCache() ||
+ response.CacheControlContainsNoStore())
+ return false;
+
+ if (response.HttpStatusCode() == 303) {
+ // Must not be cached.
+ return false;
+ }
+
+ if (response.HttpStatusCode() == 302 || response.HttpStatusCode() == 307) {
+ // Default to not cacheable unless explicitly allowed.
+ bool has_max_age = std::isfinite(response.CacheControlMaxAge());
+ bool has_expires = std::isfinite(response.Expires());
+ // TODO: consider catching Cache-Control "private" and "public" here.
+ if (!has_max_age && !has_expires)
+ return false;
+ }
+
+ return CurrentAge(response, response_timestamp) <=
+ FreshnessLifetime(response, response_timestamp);
+}
+
+const ResourceRequest& Resource::LastResourceRequest() const {
+ if (!redirect_chain_.size())
+ return GetResourceRequest();
+ return redirect_chain_.back().request_;
+}
+
+void Resource::SetRevalidatingRequest(const ResourceRequest& request) {
+ SECURITY_CHECK(redirect_chain_.IsEmpty());
+ SECURITY_CHECK(!is_unused_preload_);
+ DCHECK(!request.IsNull());
+ CHECK(!is_revalidation_start_forbidden_);
+ is_revalidating_ = true;
+ resource_request_ = request;
+ status_ = ResourceStatus::kNotStarted;
+}
+
+bool Resource::WillFollowRedirect(const ResourceRequest& new_request,
+ const ResourceResponse& redirect_response) {
+ if (is_revalidating_)
+ RevalidationFailed();
+ redirect_chain_.push_back(RedirectPair(new_request, redirect_response));
+ return true;
+}
+
+void Resource::SetResponse(const ResourceResponse& response) {
+ response_ = response;
+
+ // Currently we support the metadata caching only for HTTP family.
+ if (!GetResourceRequest().Url().ProtocolIsInHTTPFamily() ||
+ !GetResponse().Url().ProtocolIsInHTTPFamily()) {
+ cache_handler_.Clear();
+ return;
+ }
+
+ cache_handler_ = CreateCachedMetadataHandler(CreateCachedMetadataSender());
+}
+
+std::unique_ptr<CachedMetadataSender> Resource::CreateCachedMetadataSender()
+ const {
+ if (GetResponse().WasFetchedViaServiceWorker()) {
+ // TODO(leszeks): Check whether it's correct that the source_origin can be
+ // null.
+ if (!source_origin_ || GetResponse().CacheStorageCacheName().IsNull()) {
+ return std::make_unique<NullCachedMetadataSender>();
+ }
+ return std::make_unique<ServiceWorkerCachedMetadataSender>(
+ this, source_origin_.get());
+ }
+ return std::make_unique<CachedMetadataSenderImpl>(this);
+}
+
+void Resource::ResponseReceived(const ResourceResponse& response,
+ std::unique_ptr<WebDataConsumerHandle>) {
+ response_timestamp_ = CurrentTime();
+ if (preload_discovery_time_) {
+ int time_since_discovery = static_cast<int>(
+ 1000 * (CurrentTimeTicksInSeconds() - preload_discovery_time_));
+ DEFINE_STATIC_LOCAL(CustomCountHistogram,
+ preload_discovery_to_first_byte_histogram,
+ ("PreloadScanner.TTFB", 0, 10000, 50));
+ preload_discovery_to_first_byte_histogram.Count(time_since_discovery);
+ }
+
+ if (is_revalidating_) {
+ if (response.HttpStatusCode() == 304) {
+ RevalidationSucceeded(response);
+ return;
+ }
+ RevalidationFailed();
+ }
+ SetResponse(response);
+ String encoding = response.TextEncodingName();
+ if (!encoding.IsNull())
+ SetEncoding(encoding);
+}
+
+void Resource::SetSerializedCachedMetadata(const char* data, size_t size) {
+ DCHECK(!is_revalidating_);
+ DCHECK(!GetResponse().IsNull());
+}
+
+String Resource::ReasonNotDeletable() const {
+ StringBuilder builder;
+ if (HasClientsOrObservers()) {
+ builder.Append("hasClients(");
+ builder.AppendNumber(clients_.size());
+ if (!clients_awaiting_callback_.IsEmpty()) {
+ builder.Append(", AwaitingCallback=");
+ builder.AppendNumber(clients_awaiting_callback_.size());
+ }
+ if (!finished_clients_.IsEmpty()) {
+ builder.Append(", Finished=");
+ builder.AppendNumber(finished_clients_.size());
+ }
+ builder.Append(')');
+ }
+ if (loader_) {
+ if (!builder.IsEmpty())
+ builder.Append(' ');
+ builder.Append("loader_");
+ }
+ if (IsMainThread() && GetMemoryCache()->Contains(this)) {
+ if (!builder.IsEmpty())
+ builder.Append(' ');
+ builder.Append("in_memory_cache");
+ }
+ return builder.ToString();
+}
+
+void Resource::DidAddClient(ResourceClient* c) {
+ if (scoped_refptr<SharedBuffer> data = Data()) {
+ data->ForEachSegment([this, &c](const char* segment, size_t segment_size,
+ size_t segment_offset) -> bool {
+ c->DataReceived(this, segment, segment_size);
+
+ // Stop pushing data if the client removed itself.
+ return HasClient(c);
+ });
+ }
+ if (!HasClient(c))
+ return;
+ if (IsLoaded()) {
+ c->NotifyFinished(this);
+ if (clients_.Contains(c)) {
+ finished_clients_.insert(c);
+ clients_.erase(c);
+ }
+ }
+}
+
+static bool TypeNeedsSynchronousCacheHit(Resource::Type type) {
+ // Some resources types default to return data synchronously. For most of
+ // these, it's because there are layout tests that expect data to return
+ // synchronously in case of cache hit. In the case of fonts, there was a
+ // performance regression.
+ // FIXME: Get to the point where we don't need to special-case sync/async
+ // behavior for different resource types.
+ if (type == Resource::kCSSStyleSheet)
+ return true;
+ if (type == Resource::kScript)
+ return true;
+ if (type == Resource::kFont)
+ return true;
+ return false;
+}
+
+void Resource::WillAddClientOrObserver() {
+ if (!HasClientsOrObservers()) {
+ is_alive_ = true;
+ }
+}
+
+void Resource::AddClient(ResourceClient* client,
+ base::SingleThreadTaskRunner* task_runner) {
+ CHECK(!is_add_remove_client_prohibited_);
+
+ WillAddClientOrObserver();
+
+ if (is_revalidating_) {
+ clients_.insert(client);
+ return;
+ }
+
+ // If an error has occurred or we have existing data to send to the new client
+ // and the resource type supports it, send it asynchronously.
+ if ((ErrorOccurred() || !GetResponse().IsNull()) &&
+ !TypeNeedsSynchronousCacheHit(GetType())) {
+ clients_awaiting_callback_.insert(client);
+ if (!async_finish_pending_clients_task_.IsActive()) {
+ async_finish_pending_clients_task_ = PostCancellableTask(
+ *task_runner, FROM_HERE,
+ WTF::Bind(&Resource::FinishPendingClients, WrapWeakPersistent(this)));
+ }
+ return;
+ }
+
+ clients_.insert(client);
+ DidAddClient(client);
+ return;
+}
+
+void Resource::RemoveClient(ResourceClient* client) {
+ CHECK(!is_add_remove_client_prohibited_);
+
+ // This code may be called in a pre-finalizer, where weak members in the
+ // HashCountedSet are already swept out.
+
+ if (finished_clients_.Contains(client))
+ finished_clients_.erase(client);
+ else if (clients_awaiting_callback_.Contains(client))
+ clients_awaiting_callback_.erase(client);
+ else
+ clients_.erase(client);
+
+ if (clients_awaiting_callback_.IsEmpty() &&
+ async_finish_pending_clients_task_.IsActive()) {
+ async_finish_pending_clients_task_.Cancel();
+ }
+
+ DidRemoveClientOrObserver();
+}
+
+void Resource::AddFinishObserver(ResourceFinishObserver* client,
+ base::SingleThreadTaskRunner* task_runner) {
+ CHECK(!is_add_remove_client_prohibited_);
+ DCHECK(!finish_observers_.Contains(client));
+
+ WillAddClientOrObserver();
+ finish_observers_.insert(client);
+ if (IsLoaded())
+ TriggerNotificationForFinishObservers(task_runner);
+}
+
+void Resource::RemoveFinishObserver(ResourceFinishObserver* client) {
+ CHECK(!is_add_remove_client_prohibited_);
+
+ finish_observers_.erase(client);
+ DidRemoveClientOrObserver();
+}
+
+void Resource::DidRemoveClientOrObserver() {
+ if (!HasClientsOrObservers() && is_alive_) {
+ is_alive_ = false;
+ AllClientsAndObserversRemoved();
+
+ // RFC2616 14.9.2:
+ // "no-store: ... MUST make a best-effort attempt to remove the information
+ // from volatile storage as promptly as possible"
+ // "... History buffers MAY store such responses as part of their normal
+ // operation."
+ // We allow non-secure content to be reused in history, but we do not allow
+ // secure content to be reused.
+ if (HasCacheControlNoStoreHeader() && Url().ProtocolIs("https") &&
+ IsMainThread())
+ GetMemoryCache()->Remove(this);
+ }
+}
+
+void Resource::AllClientsAndObserversRemoved() {
+ if (loader_ && !detachable_)
+ loader_->ScheduleCancel();
+}
+
+void Resource::SetDecodedSize(size_t decoded_size) {
+ if (decoded_size == decoded_size_)
+ return;
+ size_t old_size = size();
+ decoded_size_ = decoded_size;
+ if (IsMainThread())
+ GetMemoryCache()->Update(this, old_size, size());
+}
+
+void Resource::SetEncodedSize(size_t encoded_size) {
+ if (encoded_size == encoded_size_ &&
+ encoded_size == encoded_size_memory_usage_)
+ return;
+ size_t old_size = size();
+ encoded_size_ = encoded_size;
+ encoded_size_memory_usage_ = encoded_size;
+ if (IsMainThread())
+ GetMemoryCache()->Update(this, old_size, size());
+}
+
+void Resource::FinishPendingClients() {
+ // We're going to notify clients one by one. It is simple if the client does
+ // nothing. However there are a couple other things that can happen.
+ //
+ // 1. Clients can be added during the loop. Make sure they are not processed.
+ // 2. Clients can be removed during the loop. Make sure they are always
+ // available to be removed. Also don't call removed clients or add them
+ // back.
+ //
+ // Handle case (1) by saving a list of clients to notify. A separate list also
+ // ensure a client is either in cliens_ or clients_awaiting_callback_.
+ HeapVector<Member<ResourceClient>> clients_to_notify;
+ CopyToVector(clients_awaiting_callback_, clients_to_notify);
+
+ for (const auto& client : clients_to_notify) {
+ // Handle case (2) to skip removed clients.
+ if (!clients_awaiting_callback_.erase(client))
+ continue;
+ clients_.insert(client);
+
+ // When revalidation starts after waiting clients are scheduled and
+ // before they are added here. In such cases, we just add the clients
+ // to |clients_| without DidAddClient(), as in Resource::AddClient().
+ if (!is_revalidating_)
+ DidAddClient(client);
+ }
+
+ // It is still possible for the above loop to finish a new client
+ // synchronously. If there's no client waiting we should deschedule.
+ bool scheduled = async_finish_pending_clients_task_.IsActive();
+ if (scheduled && clients_awaiting_callback_.IsEmpty())
+ async_finish_pending_clients_task_.Cancel();
+
+ // Prevent the case when there are clients waiting but no callback scheduled.
+ DCHECK(clients_awaiting_callback_.IsEmpty() || scheduled);
+}
+
+bool Resource::CanReuse(
+ const FetchParameters& params,
+ scoped_refptr<const SecurityOrigin> new_source_origin) const {
+ const ResourceRequest& new_request = params.GetResourceRequest();
+ const ResourceLoaderOptions& new_options = params.Options();
+
+ // Never reuse opaque responses from a service worker for requests that are
+ // not no-cors. https://crbug.com/625575
+ // TODO(yhirano): Remove this.
+ if (GetResponse().WasFetchedViaServiceWorker() &&
+ GetResponse().ResponseTypeViaServiceWorker() ==
+ network::mojom::FetchResponseType::kOpaque &&
+ new_request.GetFetchRequestMode() !=
+ network::mojom::FetchRequestMode::kNoCORS) {
+ return false;
+ }
+
+ // If credentials were sent with the previous request and won't be with this
+ // one, or vice versa, re-fetch the resource.
+ //
+ // This helps with the case where the server sends back
+ // "Access-Control-Allow-Origin: *" all the time, but some of the client's
+ // requests are made without CORS and some with.
+ if (GetResourceRequest().AllowStoredCredentials() !=
+ new_request.AllowStoredCredentials())
+ return false;
+
+ // Certain requests (e.g., XHRs) might have manually set headers that require
+ // revalidation. In theory, this should be a Revalidate case. In practice, the
+ // MemoryCache revalidation path assumes a whole bunch of things about how
+ // revalidation works that manual headers violate, so punt to Reload instead.
+ //
+ // Similarly, a request with manually added revalidation headers can lead to a
+ // 304 response for a request that wasn't flagged as a revalidation attempt.
+ // Normally, successful revalidation will maintain the original response's
+ // status code, but for a manual revalidation the response code remains 304.
+ // In this case, the Resource likely has insufficient context to provide a
+ // useful cache hit or revalidation. See http://crbug.com/643659
+ if (new_request.IsConditional() || response_.HttpStatusCode() == 304)
+ return false;
+
+ // Answers the question "can a separate request with different options be
+ // re-used" (e.g. preload request). The safe (but possibly slow) answer is
+ // always false.
+ //
+ // Data buffering policy differences are believed to be safe for re-use.
+ //
+ // TODO: Check content_security_policy_option.
+ //
+ // initiator_info is purely informational and should be benign for re-use.
+ //
+ // request_initiator_context is benign (indicates document vs. worker).
+
+ // Reuse only if both the existing Resource and the new request are
+ // asynchronous. Particularly,
+ // 1. Sync and async Resource/requests shouldn't be mixed (crbug.com/652172),
+ // 2. Sync existing Resources shouldn't be revalidated, and
+ // 3. Sync new requests shouldn't revalidate existing Resources.
+ //
+ // 2. and 3. are because SyncResourceHandler handles redirects without
+ // calling WillFollowRedirect, and causes response URL mismatch
+ // (crbug.com/618967) and bypassing redirect restriction around revalidation
+ // (crbug.com/613971 for 2. and crbug.com/614989 for 3.).
+ if (new_options.synchronous_policy == kRequestSynchronously ||
+ options_.synchronous_policy == kRequestSynchronously)
+ return false;
+
+ if (resource_request_.GetKeepalive() || new_request.GetKeepalive()) {
+ return false;
+ }
+
+ DCHECK(source_origin_);
+ DCHECK(new_source_origin);
+
+ // Don't reuse an existing resource when the source origin is different.
+ if (!source_origin_->IsSameSchemeHostPort(new_source_origin.get()))
+ return false;
+
+ // securityOrigin has more complicated checks which callers are responsible
+ // for.
+
+ if (new_request.GetFetchCredentialsMode() !=
+ resource_request_.GetFetchCredentialsMode())
+ return false;
+
+ const auto new_mode = new_request.GetFetchRequestMode();
+ const auto existing_mode = resource_request_.GetFetchRequestMode();
+
+ if (new_mode != existing_mode)
+ return false;
+
+ switch (new_mode) {
+ case network::mojom::FetchRequestMode::kNoCORS:
+ case network::mojom::FetchRequestMode::kNavigate:
+ break;
+
+ case network::mojom::FetchRequestMode::kCORS:
+ case network::mojom::FetchRequestMode::kSameOrigin:
+ case network::mojom::FetchRequestMode::kCORSWithForcedPreflight:
+ // We have two separate CORS handling logics in DocumentThreadableLoader
+ // and ResourceLoader and sharing resources is difficult when they are
+ // handled differently.
+ if (options_.cors_handling_by_resource_fetcher !=
+ new_options.cors_handling_by_resource_fetcher) {
+ // If the existing one is handled in DocumentThreadableLoader and the
+ // new one is handled in ResourceLoader, reusing the existing one will
+ // lead to CORS violations.
+ if (!options_.cors_handling_by_resource_fetcher)
+ return false;
+
+ // Otherwise (i.e., if the existing one is handled in ResourceLoader
+ // and the new one is handled in DocumentThreadableLoader), reusing
+ // the existing one will lead to double check which is harmless.
+ }
+ break;
+ }
+
+ return true;
+}
+
+void Resource::Prune() {
+ DestroyDecodedDataIfPossible();
+}
+
+void Resource::OnPurgeMemory() {
+ Prune();
+ if (!cache_handler_)
+ return;
+ cache_handler_->ClearCachedMetadata(CachedMetadataHandler::kCacheLocally);
+}
+
+void Resource::OnMemoryDump(WebMemoryDumpLevelOfDetail level_of_detail,
+ WebProcessMemoryDump* memory_dump) const {
+ static const size_t kMaxURLReportLength = 128;
+ static const int kMaxResourceClientToShowInMemoryInfra = 10;
+
+ const String dump_name = GetMemoryDumpName();
+ WebMemoryAllocatorDump* dump =
+ memory_dump->CreateMemoryAllocatorDump(dump_name);
+ dump->AddScalar("encoded_size", "bytes", encoded_size_memory_usage_);
+ if (HasClientsOrObservers())
+ dump->AddScalar("live_size", "bytes", encoded_size_memory_usage_);
+ else
+ dump->AddScalar("dead_size", "bytes", encoded_size_memory_usage_);
+
+ if (data_)
+ data_->OnMemoryDump(dump_name, memory_dump);
+
+ if (level_of_detail == WebMemoryDumpLevelOfDetail::kDetailed) {
+ String url_to_report = Url().GetString();
+ if (url_to_report.length() > kMaxURLReportLength) {
+ url_to_report.Truncate(kMaxURLReportLength);
+ url_to_report = url_to_report + "...";
+ }
+ dump->AddString("url", "", url_to_report);
+
+ dump->AddString("reason_not_deletable", "", ReasonNotDeletable());
+
+ Vector<String> client_names;
+ ResourceClientWalker<ResourceClient> walker(clients_);
+ while (ResourceClient* client = walker.Next())
+ client_names.push_back(client->DebugName());
+ ResourceClientWalker<ResourceClient> walker2(clients_awaiting_callback_);
+ while (ResourceClient* client = walker2.Next())
+ client_names.push_back("(awaiting) " + client->DebugName());
+ ResourceClientWalker<ResourceClient> walker3(finished_clients_);
+ while (ResourceClient* client = walker3.Next())
+ client_names.push_back("(finished) " + client->DebugName());
+ std::sort(client_names.begin(), client_names.end(),
+ WTF::CodePointCompareLessThan);
+
+ StringBuilder builder;
+ for (size_t i = 0;
+ i < client_names.size() && i < kMaxResourceClientToShowInMemoryInfra;
+ ++i) {
+ if (i > 0)
+ builder.Append(" / ");
+ builder.Append(client_names[i]);
+ }
+ if (client_names.size() > kMaxResourceClientToShowInMemoryInfra) {
+ builder.Append(" / and ");
+ builder.AppendNumber(client_names.size() -
+ kMaxResourceClientToShowInMemoryInfra);
+ builder.Append(" more");
+ }
+ dump->AddString("ResourceClient", "", builder.ToString());
+ }
+
+ const String overhead_name = dump_name + "/metadata";
+ WebMemoryAllocatorDump* overhead_dump =
+ memory_dump->CreateMemoryAllocatorDump(overhead_name);
+ overhead_dump->AddScalar("size", "bytes", OverheadSize());
+ memory_dump->AddSuballocation(
+ overhead_dump->Guid(), String(WTF::Partitions::kAllocatedObjectPoolName));
+}
+
+String Resource::GetMemoryDumpName() const {
+ return String::Format(
+ "web_cache/%s_resources/%ld",
+ ResourceTypeToString(GetType(), Options().initiator_info.name),
+ identifier_);
+}
+
+void Resource::SetCachePolicyBypassingCache() {
+ resource_request_.SetCacheMode(mojom::FetchCacheMode::kBypassCache);
+}
+
+void Resource::SetPreviewsState(WebURLRequest::PreviewsState previews_state) {
+ resource_request_.SetPreviewsState(previews_state);
+}
+
+void Resource::ClearRangeRequestHeader() {
+ resource_request_.ClearHTTPHeaderField("range");
+}
+
+void Resource::RevalidationSucceeded(
+ const ResourceResponse& validating_response) {
+ SECURITY_CHECK(redirect_chain_.IsEmpty());
+ SECURITY_CHECK(EqualIgnoringFragmentIdentifier(validating_response.Url(),
+ GetResponse().Url()));
+ response_.SetResourceLoadTiming(validating_response.GetResourceLoadTiming());
+
+ // RFC2616 10.3.5
+ // Update cached headers from the 304 response
+ const HTTPHeaderMap& new_headers = validating_response.HttpHeaderFields();
+ for (const auto& header : new_headers) {
+ // Entity headers should not be sent by servers when generating a 304
+ // response; misconfigured servers send them anyway. We shouldn't allow such
+ // headers to update the original request. We'll base this on the list
+ // defined by RFC2616 7.1, with a few additions for extension headers we
+ // care about.
+ if (!ShouldUpdateHeaderAfterRevalidation(header.key))
+ continue;
+ response_.SetHTTPHeaderField(header.key, header.value);
+ }
+
+ is_revalidating_ = false;
+}
+
+void Resource::RevalidationFailed() {
+ SECURITY_CHECK(redirect_chain_.IsEmpty());
+ ClearData();
+ cache_handler_.Clear();
+ integrity_disposition_ = ResourceIntegrityDisposition::kNotChecked;
+ integrity_report_info_.Clear();
+ DestroyDecodedDataForFailedRevalidation();
+ is_revalidating_ = false;
+}
+
+void Resource::MarkAsPreload() {
+ DCHECK(!is_unused_preload_);
+ is_unused_preload_ = true;
+}
+
+bool Resource::MatchPreload(const FetchParameters& params,
+ base::SingleThreadTaskRunner*) {
+ DCHECK(is_unused_preload_);
+ is_unused_preload_ = false;
+
+ if (preload_discovery_time_) {
+ int time_since_discovery = static_cast<int>(
+ 1000 * (CurrentTimeTicksInSeconds() - preload_discovery_time_));
+ DEFINE_STATIC_LOCAL(CustomCountHistogram, preload_discovery_histogram,
+ ("PreloadScanner.ReferenceTime", 0, 10000, 50));
+ preload_discovery_histogram.Count(time_since_discovery);
+ }
+ return true;
+}
+
+bool Resource::CanReuseRedirectChain() const {
+ for (auto& redirect : redirect_chain_) {
+ if (!CanUseResponse(redirect.redirect_response_, response_timestamp_))
+ return false;
+ if (redirect.request_.CacheControlContainsNoCache() ||
+ redirect.request_.CacheControlContainsNoStore())
+ return false;
+ }
+ return true;
+}
+
+bool Resource::HasCacheControlNoStoreHeader() const {
+ return GetResponse().CacheControlContainsNoStore() ||
+ GetResourceRequest().CacheControlContainsNoStore();
+}
+
+bool Resource::MustReloadDueToVaryHeader(
+ const ResourceRequest& new_request) const {
+ const AtomicString& vary = GetResponse().HttpHeaderField(HTTPNames::Vary);
+ if (vary.IsNull())
+ return false;
+ if (vary == "*")
+ return true;
+
+ CommaDelimitedHeaderSet vary_headers;
+ ParseCommaDelimitedHeader(vary, vary_headers);
+ for (const String& header : vary_headers) {
+ AtomicString atomic_header(header);
+ if (GetResourceRequest().HttpHeaderField(atomic_header) !=
+ new_request.HttpHeaderField(atomic_header)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Resource::MustRevalidateDueToCacheHeaders() const {
+ return !CanUseResponse(GetResponse(), response_timestamp_) ||
+ GetResourceRequest().CacheControlContainsNoCache() ||
+ GetResourceRequest().CacheControlContainsNoStore();
+}
+
+bool Resource::CanUseCacheValidator() const {
+ if (IsLoading() || ErrorOccurred())
+ return false;
+
+ if (HasCacheControlNoStoreHeader())
+ return false;
+
+ // Do not revalidate Resource with redirects. https://crbug.com/613971
+ if (!RedirectChain().IsEmpty())
+ return false;
+
+ return GetResponse().HasCacheValidatorFields() ||
+ GetResourceRequest().HasCacheValidatorFields();
+}
+
+size_t Resource::CalculateOverheadSize() const {
+ static const int kAverageClientsHashMapSize = 384;
+ return sizeof(Resource) + GetResponse().MemoryUsage() +
+ kAverageClientsHashMapSize +
+ GetResourceRequest().Url().GetString().length() * 2;
+}
+
+void Resource::DidChangePriority(ResourceLoadPriority load_priority,
+ int intra_priority_value) {
+ resource_request_.SetPriority(load_priority, intra_priority_value);
+ if (loader_)
+ loader_->DidChangePriority(load_priority, intra_priority_value);
+}
+
+// TODO(toyoshim): Consider to generate automatically. https://crbug.com/675515.
+static const char* InitiatorTypeNameToString(
+ const AtomicString& initiator_type_name) {
+ if (initiator_type_name == FetchInitiatorTypeNames::css)
+ return "CSS resource";
+ if (initiator_type_name == FetchInitiatorTypeNames::document)
+ return "Document";
+ if (initiator_type_name == FetchInitiatorTypeNames::icon)
+ return "Icon";
+ if (initiator_type_name == FetchInitiatorTypeNames::internal)
+ return "Internal resource";
+ if (initiator_type_name == FetchInitiatorTypeNames::link)
+ return "Link element resource";
+ if (initiator_type_name == FetchInitiatorTypeNames::processinginstruction)
+ return "Processing instruction";
+ if (initiator_type_name == FetchInitiatorTypeNames::texttrack)
+ return "Text track";
+ if (initiator_type_name == FetchInitiatorTypeNames::uacss)
+ return "User Agent CSS resource";
+ if (initiator_type_name == FetchInitiatorTypeNames::xml)
+ return "XML resource";
+ if (initiator_type_name == FetchInitiatorTypeNames::xmlhttprequest)
+ return "XMLHttpRequest";
+
+ static_assert(
+ FetchInitiatorTypeNames::FetchInitiatorTypeNamesCount == 13,
+ "New FetchInitiatorTypeNames should be handled correctly here.");
+
+ return "Resource";
+}
+
+const char* Resource::ResourceTypeToString(
+ Type type,
+ const AtomicString& fetch_initiator_name) {
+ switch (type) {
+ case Resource::kMainResource:
+ return "Main resource";
+ case Resource::kImage:
+ return "Image";
+ case Resource::kCSSStyleSheet:
+ return "CSS stylesheet";
+ case Resource::kScript:
+ return "Script";
+ case Resource::kFont:
+ return "Font";
+ case Resource::kRaw:
+ return InitiatorTypeNameToString(fetch_initiator_name);
+ case Resource::kSVGDocument:
+ return "SVG document";
+ case Resource::kXSLStyleSheet:
+ return "XSL stylesheet";
+ case Resource::kLinkPrefetch:
+ return "Link prefetch resource";
+ case Resource::kTextTrack:
+ return "Text track";
+ case Resource::kImportResource:
+ return "Imported resource";
+ case Resource::kAudio:
+ return "Audio";
+ case Resource::kVideo:
+ return "Video";
+ case Resource::kManifest:
+ return "Manifest";
+ case Resource::kMock:
+ return "Mock";
+ }
+ NOTREACHED();
+ return InitiatorTypeNameToString(fetch_initiator_name);
+}
+
+bool Resource::ShouldBlockLoadEvent() const {
+ return !link_preload_ && IsLoadEventBlockingResourceType();
+}
+
+bool Resource::IsLoadEventBlockingResourceType() const {
+ switch (type_) {
+ case Resource::kMainResource:
+ case Resource::kImage:
+ case Resource::kCSSStyleSheet:
+ case Resource::kScript:
+ case Resource::kFont:
+ case Resource::kSVGDocument:
+ case Resource::kXSLStyleSheet:
+ case Resource::kImportResource:
+ return true;
+ case Resource::kRaw:
+ case Resource::kLinkPrefetch:
+ case Resource::kTextTrack:
+ case Resource::kAudio:
+ case Resource::kVideo:
+ case Resource::kManifest:
+ case Resource::kMock:
+ return false;
+ }
+ NOTREACHED();
+ return false;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource.h
new file mode 100644
index 00000000000..b68b9c5abb0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource.h
@@ -0,0 +1,596 @@
+/*
+ Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
+ Copyright (C) 2001 Dirk Mueller <mueller@kde.org>
+ Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
+ Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All
+ rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_H_
+
+#include <memory>
+#include "base/single_thread_task_runner.h"
+#include "third_party/blink/public/platform/web_data_consumer_handle.h"
+#include "third_party/blink/public/platform/web_scoped_virtual_time_pauser.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/web_process_memory_dump.h"
+#include "third_party/blink/renderer/platform/loader/cors/cors_status.h"
+#include "third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h"
+#include "third_party/blink/renderer/platform/loader/fetch/integrity_metadata.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_error.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_load_priority.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_priority.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_status.h"
+#include "third_party/blink/renderer/platform/loader/fetch/text_resource_decoder_options.h"
+#include "third_party/blink/renderer/platform/loader/subresource_integrity.h"
+#include "third_party/blink/renderer/platform/memory_coordinator.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/timer.h"
+#include "third_party/blink/renderer/platform/web_task_runner.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/auto_reset.h"
+#include "third_party/blink/renderer/platform/wtf/hash_counted_set.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class FetchParameters;
+class ResourceClient;
+class ResourceFetcher;
+class ResourceFinishObserver;
+class ResourceTimingInfo;
+class ResourceLoader;
+class SecurityOrigin;
+
+// A callback for sending the serialized data of cached metadata back to the
+// platform.
+class CachedMetadataSender {
+ public:
+ virtual ~CachedMetadataSender() = default;
+ virtual void Send(const char*, size_t) = 0;
+
+ // IsServedFromCacheStorage is used to alter caching strategy to be more
+ // aggressive. See ScriptController.cpp CacheOptions() for an example.
+ virtual bool IsServedFromCacheStorage() = 0;
+};
+
+// A resource that is held in the cache. Classes who want to use this object
+// should derive from ResourceClient, to get the function calls in case the
+// requested data has arrived. This class also does the actual communication
+// with the loader to obtain the resource from the network.
+class PLATFORM_EXPORT Resource : public GarbageCollectedFinalized<Resource>,
+ public MemoryCoordinatorClient {
+ USING_GARBAGE_COLLECTED_MIXIN(Resource);
+ WTF_MAKE_NONCOPYABLE(Resource);
+
+ public:
+ // |Type| enum values are used in UMAs, so do not change the values of
+ // existing |Type|. When adding a new |Type|, append it at the end and update
+ // |kLastResourceType|.
+ enum Type : uint8_t {
+ kMainResource,
+ kImage,
+ kCSSStyleSheet,
+ kScript,
+ kFont,
+ kRaw,
+ kSVGDocument,
+ kXSLStyleSheet,
+ kLinkPrefetch,
+ kTextTrack,
+ kImportResource,
+ kAudio,
+ kVideo,
+ kManifest,
+ kMock // Only for testing
+ };
+ static const int kLastResourceType = kMock + 1;
+
+ // Used by reloadIfLoFiOrPlaceholderImage().
+ enum ReloadLoFiOrPlaceholderPolicy {
+ kReloadIfNeeded,
+ kReloadAlways,
+ };
+
+ ~Resource() override;
+
+ void Trace(blink::Visitor*) override;
+
+ virtual WTF::TextEncoding Encoding() const { return WTF::TextEncoding(); }
+ virtual void AppendData(const char*, size_t);
+ virtual void FinishAsError(const ResourceError&,
+ base::SingleThreadTaskRunner*);
+
+ void SetLinkPreload(bool is_link_preload) { link_preload_ = is_link_preload; }
+ bool IsLinkPreload() const { return link_preload_; }
+
+ void SetPreloadDiscoveryTime(double preload_discovery_time) {
+ preload_discovery_time_ = preload_discovery_time;
+ }
+
+ const ResourceError& GetResourceError() const {
+ DCHECK(error_);
+ return *error_;
+ }
+
+ void SetIdentifier(unsigned long identifier) { identifier_ = identifier; }
+ unsigned long Identifier() const { return identifier_; }
+
+ virtual bool ShouldIgnoreHTTPStatusCodeErrors() const { return false; }
+
+ const ResourceRequest& GetResourceRequest() const {
+ return resource_request_;
+ }
+ const ResourceRequest& LastResourceRequest() const;
+
+ virtual void SetRevalidatingRequest(const ResourceRequest&);
+
+ // This url can have a fragment, but it can match resources that differ by the
+ // fragment only.
+ const KURL& Url() const { return GetResourceRequest().Url(); }
+ Type GetType() const { return static_cast<Type>(type_); }
+ const ResourceLoaderOptions& Options() const { return options_; }
+ ResourceLoaderOptions& MutableOptions() { return options_; }
+
+ void DidChangePriority(ResourceLoadPriority, int intra_priority_value);
+ virtual ResourcePriority PriorityFromObservers() {
+ return ResourcePriority();
+ }
+
+ // If this Resource is already finished when AddClient is called, the
+ // ResourceClient will be notified asynchronously by a task scheduled
+ // on the given base::SingleThreadTaskRunner. Otherwise, the given
+ // base::SingleThreadTaskRunner is unused.
+ void AddClient(ResourceClient*, base::SingleThreadTaskRunner*);
+ void RemoveClient(ResourceClient*);
+ // Once called, this resource will not be canceled until load finishes
+ // even if associated with no client.
+ void SetDetachable() { detachable_ = true; }
+
+ // If this Resource is already finished when AddFinishObserver is called, the
+ // ResourceFinishObserver will be notified asynchronously by a task scheduled
+ // on the given base::SingleThreadTaskRunner. Otherwise, the given
+ // base::SingleThreadTaskRunner is unused.
+ void AddFinishObserver(ResourceFinishObserver*,
+ base::SingleThreadTaskRunner*);
+ void RemoveFinishObserver(ResourceFinishObserver*);
+
+ bool IsUnusedPreload() const { return is_unused_preload_; }
+
+ ResourceStatus GetStatus() const { return status_; }
+ void SetStatus(ResourceStatus status) { status_ = status; }
+
+ size_t size() const { return EncodedSize() + DecodedSize() + OverheadSize(); }
+
+ // Returns the size of content (response body) before decoding. Adding a new
+ // usage of this function is not recommended (See the TODO below).
+ //
+ // TODO(hiroshige): Now EncodedSize/DecodedSize states are inconsistent and
+ // need to be refactored (crbug/643135).
+ size_t EncodedSize() const { return encoded_size_; }
+
+ // Returns the current memory usage for the encoded data. Adding a new usage
+ // of this function is not recommended as the same reason as |EncodedSize()|.
+ //
+ // |EncodedSize()| and |EncodedSizeMemoryUsageForTesting()| can return
+ // different values, e.g., when ImageResource purges encoded image data after
+ // finishing loading.
+ size_t EncodedSizeMemoryUsageForTesting() const {
+ return encoded_size_memory_usage_;
+ }
+
+ size_t DecodedSize() const { return decoded_size_; }
+ size_t OverheadSize() const { return overhead_size_; }
+
+ bool IsLoaded() const { return status_ > ResourceStatus::kPending; }
+
+ bool IsLoading() const { return status_ == ResourceStatus::kPending; }
+ bool StillNeedsLoad() const { return status_ < ResourceStatus::kPending; }
+
+ void SetLoader(ResourceLoader*);
+ ResourceLoader* Loader() const { return loader_.Get(); }
+
+ bool ShouldBlockLoadEvent() const;
+ bool IsLoadEventBlockingResourceType() const;
+
+ // Computes the status of an object after loading. Updates the expire date on
+ // the cache entry file
+ virtual void Finish(double finish_time, base::SingleThreadTaskRunner*);
+ void FinishForTest() { Finish(0.0, nullptr); }
+
+ bool PassesAccessControlCheck(const SecurityOrigin&) const;
+
+ virtual scoped_refptr<const SharedBuffer> ResourceBuffer() const {
+ return data_;
+ }
+ void SetResourceBuffer(scoped_refptr<SharedBuffer>);
+
+ virtual bool WillFollowRedirect(const ResourceRequest&,
+ const ResourceResponse&);
+
+ // Called when a redirect response was received but a decision has already
+ // been made to not follow it.
+ virtual void WillNotFollowRedirect() {}
+
+ virtual void ResponseReceived(const ResourceResponse&,
+ std::unique_ptr<WebDataConsumerHandle>);
+ void SetResponse(const ResourceResponse&);
+ const ResourceResponse& GetResponse() const { return response_; }
+
+ virtual void ReportResourceTimingToClients(const ResourceTimingInfo&) {}
+
+ // Sets the serialized metadata retrieved from the platform's cache.
+ // Subclasses of Resource that support cached metadata should override this
+ // method with one that fills the current CachedMetadataHandler.
+ virtual void SetSerializedCachedMetadata(const char*, size_t);
+
+ AtomicString HttpContentType() const;
+
+ bool WasCanceled() const { return error_ && error_->IsCancellation(); }
+ bool ErrorOccurred() const {
+ return status_ == ResourceStatus::kLoadError ||
+ status_ == ResourceStatus::kDecodeError;
+ }
+ bool LoadFailedOrCanceled() const { return !!error_; }
+
+ DataBufferingPolicy GetDataBufferingPolicy() const {
+ return options_.data_buffering_policy;
+ }
+ void SetDataBufferingPolicy(DataBufferingPolicy);
+
+ void MarkAsPreload();
+ // Returns true if |this| resource is matched with the given parameters.
+ virtual bool MatchPreload(const FetchParameters&,
+ base::SingleThreadTaskRunner*);
+
+ bool CanReuseRedirectChain() const;
+ bool MustRevalidateDueToCacheHeaders() const;
+ virtual bool CanUseCacheValidator() const;
+ bool IsCacheValidator() const { return is_revalidating_; }
+ bool HasCacheControlNoStoreHeader() const;
+ bool MustReloadDueToVaryHeader(const ResourceRequest& new_request) const;
+
+ const IntegrityMetadataSet& IntegrityMetadata() const {
+ return options_.integrity_metadata;
+ }
+ ResourceIntegrityDisposition IntegrityDisposition() const {
+ return integrity_disposition_;
+ }
+ const SubresourceIntegrity::ReportInfo& IntegrityReportInfo() const {
+ return integrity_report_info_;
+ }
+ bool MustRefetchDueToIntegrityMetadata(const FetchParameters&) const;
+
+ bool IsAlive() const { return is_alive_; }
+
+ CORSStatus GetCORSStatus() const { return cors_status_; }
+
+ bool IsSameOriginOrCORSSuccessful() const {
+ return cors_status_ == CORSStatus::kSameOrigin ||
+ cors_status_ == CORSStatus::kSuccessful ||
+ cors_status_ == CORSStatus::kServiceWorkerSuccessful;
+ }
+
+ void SetCacheIdentifier(const String& cache_identifier) {
+ cache_identifier_ = cache_identifier;
+ }
+ String CacheIdentifier() const { return cache_identifier_; }
+
+ void SetSourceOrigin(scoped_refptr<const SecurityOrigin> source_origin) {
+ source_origin_ = source_origin;
+ }
+
+ virtual void DidSendData(unsigned long long /* bytesSent */,
+ unsigned long long /* totalBytesToBeSent */) {}
+ virtual void DidDownloadData(int) {}
+ virtual void DidDownloadToBlob(scoped_refptr<BlobDataHandle>) {}
+
+ double LoadFinishTime() const { return load_finish_time_; }
+
+ void SetEncodedDataLength(int64_t value) {
+ response_.SetEncodedDataLength(value);
+ }
+ void SetEncodedBodyLength(int value) {
+ response_.SetEncodedBodyLength(value);
+ }
+ void SetDecodedBodyLength(int value) {
+ response_.SetDecodedBodyLength(value);
+ }
+
+ virtual bool CanReuse(
+ const FetchParameters&,
+ scoped_refptr<const SecurityOrigin> new_source_origin) const;
+
+ // If cache-aware loading is activated, this callback is called when the first
+ // disk-cache-only request failed due to cache miss. After this callback,
+ // cache-aware loading is deactivated and a reload with original request will
+ // be triggered right away in ResourceLoader.
+ virtual void WillReloadAfterDiskCacheMiss() {}
+
+ // TODO(shaochuan): This is for saving back the actual ResourceRequest sent
+ // in ResourceFetcher::StartLoad() for retry in cache-aware loading, remove
+ // once ResourceRequest is not modified in StartLoad(). crbug.com/632580
+ void SetResourceRequest(const ResourceRequest& resource_request) {
+ resource_request_ = resource_request;
+ }
+
+ // Used by the MemoryCache to reduce the memory consumption of the entry.
+ void Prune();
+
+ virtual void OnMemoryDump(WebMemoryDumpLevelOfDetail,
+ WebProcessMemoryDump*) const;
+
+ // If this Resource is ImageResource and has the Lo-Fi response headers or is
+ // a placeholder, reload the full original image with the Lo-Fi state set to
+ // off and optionally bypassing the cache.
+ virtual void ReloadIfLoFiOrPlaceholderImage(ResourceFetcher*,
+ ReloadLoFiOrPlaceholderPolicy) {}
+
+ // Used to notify ImageResourceContent of the start of actual loading.
+ // JavaScript calls or client/observer notifications are disallowed inside
+ // NotifyStartLoad().
+ virtual void NotifyStartLoad() {}
+
+ static const char* ResourceTypeToString(
+ Type,
+ const AtomicString& fetch_initiator_name);
+
+ class ProhibitAddRemoveClientInScope : public AutoReset<bool> {
+ public:
+ ProhibitAddRemoveClientInScope(Resource* resource)
+ : AutoReset(&resource->is_add_remove_client_prohibited_, true) {}
+ };
+
+ class RevalidationStartForbiddenScope : public AutoReset<bool> {
+ public:
+ RevalidationStartForbiddenScope(Resource* resource)
+ : AutoReset(&resource->is_revalidation_start_forbidden_, true) {}
+ };
+
+ WebScopedVirtualTimePauser& VirtualTimePauser() {
+ return virtual_time_pauser_;
+ }
+
+ protected:
+ Resource(const ResourceRequest&, Type, const ResourceLoaderOptions&);
+
+ virtual void NotifyFinished();
+
+ void MarkClientFinished(ResourceClient*);
+
+ virtual bool HasClientsOrObservers() const {
+ return !clients_.IsEmpty() || !clients_awaiting_callback_.IsEmpty() ||
+ !finished_clients_.IsEmpty() || !finish_observers_.IsEmpty();
+ }
+ virtual void DestroyDecodedDataForFailedRevalidation() {}
+
+ void SetEncodedSize(size_t);
+ void SetDecodedSize(size_t);
+
+ void FinishPendingClients();
+
+ virtual void DidAddClient(ResourceClient*);
+ void WillAddClientOrObserver();
+
+ void DidRemoveClientOrObserver();
+ virtual void AllClientsAndObserversRemoved();
+
+ bool HasClient(ResourceClient* client) const {
+ return clients_.Contains(client) ||
+ clients_awaiting_callback_.Contains(client) ||
+ finished_clients_.Contains(client);
+ }
+
+ struct RedirectPair {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ explicit RedirectPair(const ResourceRequest& request,
+ const ResourceResponse& redirect_response)
+ : request_(request), redirect_response_(redirect_response) {}
+
+ ResourceRequest request_;
+ ResourceResponse redirect_response_;
+ };
+ const Vector<RedirectPair>& RedirectChain() const { return redirect_chain_; }
+
+ virtual void DestroyDecodedDataIfPossible() {}
+
+ // Returns the memory dump name used for tracing. See Resource::OnMemoryDump.
+ String GetMemoryDumpName() const;
+
+ const HeapHashCountedSet<WeakMember<ResourceClient>>& Clients() const {
+ return clients_;
+ }
+
+ void SetCachePolicyBypassingCache();
+ void SetPreviewsState(WebURLRequest::PreviewsState);
+ void ClearRangeRequestHeader();
+
+ SharedBuffer* Data() const { return data_.get(); }
+ void ClearData();
+
+ virtual void SetEncoding(const String&) {}
+
+ // Create a handler for the cached metadata of this resource. Subclasses of
+ // Resource that support cached metadata should override this method with one
+ // that creates an appropriate CachedMetadataHandler implementation, and
+ // override SetSerializedCachedMetadata with an implementation that fills the
+ // cache handler.
+ virtual CachedMetadataHandler* CreateCachedMetadataHandler(
+ std::unique_ptr<CachedMetadataSender> send_callback) {
+ return nullptr;
+ }
+
+ CachedMetadataHandler* CacheHandler() { return cache_handler_.Get(); }
+
+ private:
+ // To allow access to SetCORSStatus
+ friend class ResourceLoader;
+ friend class SubresourceIntegrityTest;
+
+ void RevalidationSucceeded(const ResourceResponse&);
+ void RevalidationFailed();
+
+ size_t CalculateOverheadSize() const;
+
+ String ReasonNotDeletable() const;
+
+ void SetCORSStatus(const CORSStatus cors_status) {
+ cors_status_ = cors_status;
+ }
+
+ // MemoryCoordinatorClient overrides:
+ void OnPurgeMemory() override;
+
+ void CheckResourceIntegrity();
+ void TriggerNotificationForFinishObservers(base::SingleThreadTaskRunner*);
+
+ // Helper for creating the send callback function for the cached metadata
+ // handler.
+ std::unique_ptr<CachedMetadataSender> CreateCachedMetadataSender() const;
+
+ Type type_;
+ ResourceStatus status_;
+
+ // A SecurityOrigin representing the origin from which the loading of the
+ // Resource was initiated. This is calculated and set on Resource creation.
+ //
+ // Unlike |security_origin| on |options_|, which:
+ // - holds a SecurityOrigin to override the FetchContext's SecurityOrigin
+ // (in case of e.g. that the script initiated the loading is in an isolated
+ // world)
+ //
+ // Used for isolating resources for different origins in the MemoryCache.
+ //
+ // Note: A Resource returned from the memory cache has an origin for the first
+ // initiator that fetched the Resource. It may be different from the origin
+ // that you need for any runtime security check in Blink.
+ scoped_refptr<const SecurityOrigin> source_origin_;
+
+ CORSStatus cors_status_;
+
+ Member<CachedMetadataHandler> cache_handler_;
+
+ Optional<ResourceError> error_;
+
+ double load_finish_time_;
+
+ unsigned long identifier_;
+
+ double preload_discovery_time_;
+
+ size_t encoded_size_;
+ size_t encoded_size_memory_usage_;
+ size_t decoded_size_;
+
+ // Resource::CalculateOverheadSize() is affected by changes in
+ // |m_resourceRequest.url()|, but |m_overheadSize| is not updated after
+ // initial |m_resourceRequest| is given, to reduce MemoryCache manipulation
+ // and thus potential bugs. crbug.com/594644
+ const size_t overhead_size_;
+
+ String cache_identifier_;
+
+ bool link_preload_;
+ bool is_revalidating_;
+ bool is_alive_;
+ bool is_add_remove_client_prohibited_;
+ bool is_revalidation_start_forbidden_ = false;
+ bool is_unused_preload_ = false;
+ bool detachable_ = false;
+
+ ResourceIntegrityDisposition integrity_disposition_;
+ SubresourceIntegrity::ReportInfo integrity_report_info_;
+
+ // Ordered list of all redirects followed while fetching this resource.
+ Vector<RedirectPair> redirect_chain_;
+
+ HeapHashCountedSet<WeakMember<ResourceClient>> clients_;
+ HeapHashCountedSet<WeakMember<ResourceClient>> clients_awaiting_callback_;
+ HeapHashCountedSet<WeakMember<ResourceClient>> finished_clients_;
+ HeapHashSet<WeakMember<ResourceFinishObserver>> finish_observers_;
+
+ ResourceLoaderOptions options_;
+
+ double response_timestamp_;
+
+ TaskHandle async_finish_pending_clients_task_;
+
+ ResourceRequest resource_request_;
+ Member<ResourceLoader> loader_;
+ ResourceResponse response_;
+
+ scoped_refptr<SharedBuffer> data_;
+
+ WebScopedVirtualTimePauser virtual_time_pauser_;
+};
+
+class ResourceFactory {
+ STACK_ALLOCATED();
+
+ public:
+ virtual Resource* Create(const ResourceRequest&,
+ const ResourceLoaderOptions&,
+ const TextResourceDecoderOptions&) const = 0;
+
+ Resource::Type GetType() const { return type_; }
+ TextResourceDecoderOptions::ContentType ContentType() const {
+ return content_type_;
+ }
+
+ protected:
+ explicit ResourceFactory(Resource::Type type,
+ TextResourceDecoderOptions::ContentType content_type)
+ : type_(type), content_type_(content_type) {}
+
+ Resource::Type type_;
+ TextResourceDecoderOptions::ContentType content_type_;
+};
+
+class NonTextResourceFactory : public ResourceFactory {
+ protected:
+ explicit NonTextResourceFactory(Resource::Type type)
+ : ResourceFactory(type, TextResourceDecoderOptions::kPlainTextContent) {}
+
+ virtual Resource* Create(const ResourceRequest&,
+ const ResourceLoaderOptions&) const = 0;
+
+ Resource* Create(const ResourceRequest& request,
+ const ResourceLoaderOptions& options,
+ const TextResourceDecoderOptions&) const final {
+ return Create(request, options);
+ }
+};
+
+#define DEFINE_RESOURCE_TYPE_CASTS(typeName) \
+ DEFINE_TYPE_CASTS(typeName##Resource, Resource, resource, \
+ resource->GetType() == Resource::k##typeName, \
+ resource.GetType() == Resource::k##typeName);
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_client.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_client.h
new file mode 100644
index 00000000000..f4476b61496
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_client.h
@@ -0,0 +1,113 @@
+/*
+ Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
+ Copyright (C) 2001 Dirk Mueller <mueller@kde.org>
+ Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All
+ rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ This class provides all functionality needed for loading images, style
+ sheets and html pages from the web. It has a memory cache for these objects.
+*/
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_CLIENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_CLIENT_H_
+
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT ResourceClient : public GarbageCollectedMixin {
+ USING_PRE_FINALIZER(ResourceClient, ClearResource);
+
+ public:
+ enum ResourceClientType {
+ kBaseResourceType,
+ kFontType,
+ kRawResourceType
+ };
+
+ virtual ~ResourceClient() = default;
+
+ // DataReceived() is called each time a chunk of data is received.
+ // For cache hits, the data is replayed before NotifyFinished() is called.
+ // For successful revalidation responses, the data is NOT replayed, because
+ // the Resource may not be in an entirely consistent state in the middle of
+ // completing the revalidation, when DataReceived() would have to be called.
+ // Some RawResourceClients depends on receiving all bytes via DataReceived(),
+ // but RawResources forbid revalidation attempts, so they still are guaranteed
+ // to get all data via DataReceived().
+ virtual void DataReceived(Resource*,
+ const char* /* data */,
+ size_t /* length */) {}
+ virtual void NotifyFinished(Resource*) {}
+
+ static bool IsExpectedType(ResourceClient*) { return true; }
+ virtual ResourceClientType GetResourceClientType() const {
+ return kBaseResourceType;
+ }
+
+ Resource* GetResource() const { return resource_; }
+
+ // Name for debugging, e.g. shown in memory-infra.
+ virtual String DebugName() const = 0;
+
+ void Trace(blink::Visitor* visitor) override { visitor->Trace(resource_); }
+
+ protected:
+ ResourceClient() = default;
+
+ void ClearResource() { SetResource(nullptr, nullptr); }
+
+ private:
+ // ResourceFetcher is primarily responsible for calling SetResource() with a
+ // non-null Resource*. ResourceClient subclasses are responsible for calling
+ // ClearResource().
+ friend class ResourceFetcher;
+ // TODO(japhet): There isn't a clean way for SVGResourceClients to determine
+ // whether SVGElementProxy is holding a Resource that it should register with,
+ // so SVGElementProxy handles it for those clients. SVGResourceClients should
+ // have a better way to register themselves as clients. crbug.com/789198
+ friend class SVGElementProxy;
+ // CSSFontFaceSrcValue only ever requests a Resource once, and acts as an
+ // intermediate caching layer of sorts. It needs to be able to register
+ // additional clients.
+ friend class CSSFontFaceSrcValue;
+
+ void SetResource(Resource* new_resource,
+ base::SingleThreadTaskRunner* task_runner) {
+ if (new_resource == resource_)
+ return;
+
+ // Some ResourceClient implementations reenter this so
+ // we need to prevent double removal.
+ if (Resource* old_resource = resource_.Release())
+ old_resource->RemoveClient(this);
+ resource_ = new_resource;
+ if (resource_)
+ resource_->AddClient(this, task_runner);
+ }
+
+ Member<Resource> resource_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_client_walker.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_client_walker.h
new file mode 100644
index 00000000000..33e7c8c8368
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_client_walker.h
@@ -0,0 +1,69 @@
+/*
+ Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
+ Copyright (C) 2001 Dirk Mueller <mueller@kde.org>
+ Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ This class provides all functionality needed for loading images, style
+ sheets and html pages from the web. It has a memory cache for these objects.
+*/
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_CLIENT_WALKER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_CLIENT_WALKER_H_
+
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_client.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+// Call this "walker" instead of iterator so people won't expect Qt or STL-style
+// iterator interface. Just keep calling next() on this. It's safe from
+// deletions of items.
+template <typename T>
+class ResourceClientWalker {
+ STACK_ALLOCATED();
+
+ public:
+ explicit ResourceClientWalker(
+ const HeapHashCountedSet<WeakMember<ResourceClient>>& set)
+ : client_set_(set) {
+ CopyToVector(client_set_, client_vector_);
+ }
+
+ T* Next() {
+ size_t size = client_vector_.size();
+ while (index_ < size) {
+ ResourceClient* next = client_vector_[index_++];
+ DCHECK(next);
+ if (client_set_.Contains(next)) {
+ DCHECK(T::IsExpectedType(next));
+ return static_cast<T*>(next);
+ }
+ }
+ return nullptr;
+ }
+
+ private:
+ const HeapHashCountedSet<WeakMember<ResourceClient>>& client_set_;
+ HeapVector<Member<ResourceClient>> client_vector_;
+ size_t index_ = 0;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_error.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_error.cc
new file mode 100644
index 00000000000..4a4c0e1f55f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_error.cc
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/loader/fetch/resource_error.h"
+
+#include "net/base/net_errors.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/platform/web_url.h"
+#include "third_party/blink/public/platform/web_url_error.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
+
+namespace blink {
+
+namespace {
+constexpr char kThrottledErrorDescription[] =
+ "Request throttled. Visit http://dev.chromium.org/throttling for more "
+ "information.";
+} // namespace
+
+int ResourceError::BlockedByXSSAuditorErrorCode() {
+ return net::ERR_BLOCKED_BY_XSS_AUDITOR;
+}
+
+ResourceError ResourceError::CancelledError(const KURL& url) {
+ return ResourceError(net::ERR_ABORTED, url, WTF::nullopt);
+}
+
+ResourceError ResourceError::CancelledDueToAccessCheckError(
+ const KURL& url,
+ ResourceRequestBlockedReason blocked_reason) {
+ ResourceError error = CancelledError(url);
+ error.is_access_check_ = true;
+ error.should_collapse_initiator_ =
+ blocked_reason == ResourceRequestBlockedReason::kSubresourceFilter;
+ return error;
+}
+
+ResourceError ResourceError::CancelledDueToAccessCheckError(
+ const KURL& url,
+ ResourceRequestBlockedReason blocked_reason,
+ const String& localized_description) {
+ ResourceError error = CancelledDueToAccessCheckError(url, blocked_reason);
+ error.localized_description_ = localized_description;
+ return error;
+}
+
+ResourceError ResourceError::CacheMissError(const KURL& url) {
+ return ResourceError(net::ERR_CACHE_MISS, url, WTF::nullopt);
+}
+
+ResourceError ResourceError::TimeoutError(const KURL& url) {
+ return ResourceError(net::ERR_TIMED_OUT, url, WTF::nullopt);
+}
+
+ResourceError ResourceError::Failure(const KURL& url) {
+ return ResourceError(net::ERR_FAILED, url, WTF::nullopt);
+}
+
+ResourceError::ResourceError(
+ int error_code,
+ const KURL& url,
+ WTF::Optional<network::CORSErrorStatus> cors_error_status)
+ : error_code_(error_code),
+ failing_url_(url),
+ cors_error_status_(cors_error_status) {
+ DCHECK_NE(error_code_, 0);
+ InitializeDescription();
+}
+
+ResourceError::ResourceError(const WebURLError& error)
+ : error_code_(error.reason()),
+ extended_error_code_(error.extended_reason()),
+ failing_url_(error.url()),
+ is_access_check_(error.is_web_security_violation()),
+ has_copy_in_cache_(error.has_copy_in_cache()),
+ cors_error_status_(error.cors_error_status()) {
+ DCHECK_NE(error_code_, 0);
+ InitializeDescription();
+}
+
+ResourceError ResourceError::Copy() const {
+ ResourceError error_copy(error_code_, failing_url_.Copy(),
+ cors_error_status_);
+ error_copy.extended_error_code_ = extended_error_code_;
+ error_copy.has_copy_in_cache_ = has_copy_in_cache_;
+ error_copy.localized_description_ = localized_description_.IsolatedCopy();
+ error_copy.is_access_check_ = is_access_check_;
+ return error_copy;
+}
+
+ResourceError::operator WebURLError() const {
+ WebURLError::HasCopyInCache has_copy_in_cache =
+ has_copy_in_cache_ ? WebURLError::HasCopyInCache::kTrue
+ : WebURLError::HasCopyInCache::kFalse;
+
+ if (cors_error_status_) {
+ DCHECK_EQ(net::ERR_FAILED, error_code_);
+ return WebURLError(*cors_error_status_, has_copy_in_cache, failing_url_);
+ }
+
+ return WebURLError(error_code_, extended_error_code_, has_copy_in_cache,
+ is_access_check_
+ ? WebURLError::IsWebSecurityViolation::kTrue
+ : WebURLError::IsWebSecurityViolation::kFalse,
+ failing_url_);
+}
+
+bool ResourceError::Compare(const ResourceError& a, const ResourceError& b) {
+ if (a.ErrorCode() != b.ErrorCode())
+ return false;
+
+ if (a.FailingURL() != b.FailingURL())
+ return false;
+
+ if (a.LocalizedDescription() != b.LocalizedDescription())
+ return false;
+
+ if (a.IsAccessCheck() != b.IsAccessCheck())
+ return false;
+
+ if (a.HasCopyInCache() != b.HasCopyInCache())
+ return false;
+
+ if (a.CORSErrorStatus() != b.CORSErrorStatus())
+ return false;
+
+ return true;
+}
+
+bool ResourceError::IsTimeout() const {
+ return error_code_ == net::ERR_TIMED_OUT;
+}
+
+bool ResourceError::IsCancellation() const {
+ return error_code_ == net::ERR_ABORTED;
+}
+
+bool ResourceError::IsCacheMiss() const {
+ return error_code_ == net::ERR_CACHE_MISS;
+}
+
+bool ResourceError::WasBlockedByResponse() const {
+ return error_code_ == net::ERR_BLOCKED_BY_RESPONSE;
+}
+
+void ResourceError::InitializeDescription() {
+ if (error_code_ == net::ERR_TEMPORARILY_THROTTLED) {
+ localized_description_ = WebString::FromASCII(kThrottledErrorDescription);
+ } else {
+ localized_description_ = WebString::FromASCII(
+ net::ExtendedErrorToString(error_code_, extended_error_code_));
+ }
+}
+
+std::ostream& operator<<(std::ostream& os, const ResourceError& error) {
+ return os << ", ErrorCode = " << error.ErrorCode()
+ << ", FailingURL = " << error.FailingURL()
+ << ", LocalizedDescription = " << error.LocalizedDescription()
+ << ", IsCancellation = " << error.IsCancellation()
+ << ", IsAccessCheck = " << error.IsAccessCheck()
+ << ", IsTimeout = " << error.IsTimeout()
+ << ", HasCopyInCache = " << error.HasCopyInCache()
+ << ", IsCacheMiss = " << error.IsCacheMiss();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_error.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_error.h
new file mode 100644
index 00000000000..ea6e0f6acb2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_error.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2013 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_ERROR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_ERROR_H_
+
+#include <iosfwd>
+#include "services/network/public/cpp/cors/cors_error_status.h"
+#include "third_party/blink/public/platform/web_url_error.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+enum class ResourceRequestBlockedReason;
+
+// ResourceError represents an error for loading a resource. There is no
+// "no-error" instance. Use Optional for nullable errors.
+class PLATFORM_EXPORT ResourceError final {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ static ResourceError CancelledError(const KURL&);
+ static ResourceError CancelledDueToAccessCheckError(
+ const KURL&,
+ ResourceRequestBlockedReason);
+ static ResourceError CancelledDueToAccessCheckError(
+ const KURL&,
+ ResourceRequestBlockedReason,
+ const String& localized_description);
+
+ static ResourceError CacheMissError(const KURL&);
+ static ResourceError TimeoutError(const KURL&);
+ static ResourceError Failure(const KURL&);
+
+ ResourceError() = delete;
+ // |error_code| must not be 0.
+ ResourceError(int error_code,
+ const KURL& failing_url,
+ WTF::Optional<network::CORSErrorStatus>);
+ ResourceError(const WebURLError&);
+
+ // Makes a deep copy. Useful for when you need to use a ResourceError on
+ // another thread.
+ ResourceError Copy() const;
+
+ int ErrorCode() const { return error_code_; }
+ const String& FailingURL() const { return failing_url_; }
+ const String& LocalizedDescription() const { return localized_description_; }
+
+ bool IsCancellation() const;
+ bool IsAccessCheck() const { return is_access_check_; }
+ bool HasCopyInCache() const { return has_copy_in_cache_; }
+ bool IsTimeout() const;
+ bool IsCacheMiss() const;
+ bool WasBlockedByResponse() const;
+ bool ShouldCollapseInitiator() const { return should_collapse_initiator_; }
+
+ WTF::Optional<network::CORSErrorStatus> CORSErrorStatus() const {
+ return cors_error_status_;
+ }
+
+ operator WebURLError() const;
+
+ static bool Compare(const ResourceError&, const ResourceError&);
+
+ // Net error code getters are here to avoid unpreferred header inclusion.
+ static int BlockedByXSSAuditorErrorCode();
+
+ private:
+ void InitializeDescription();
+
+ int error_code_;
+ int extended_error_code_;
+ KURL failing_url_;
+ String localized_description_;
+ bool is_access_check_ = false;
+ bool has_copy_in_cache_ = false;
+ bool should_collapse_initiator_ = false;
+ WTF::Optional<network::CORSErrorStatus> cors_error_status_;
+};
+
+inline bool operator==(const ResourceError& a, const ResourceError& b) {
+ return ResourceError::Compare(a, b);
+}
+inline bool operator!=(const ResourceError& a, const ResourceError& b) {
+ return !(a == b);
+}
+
+PLATFORM_EXPORT std::ostream& operator<<(std::ostream&, const ResourceError&);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_ERROR_H_
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
new file mode 100644
index 00000000000..7a5b12a467f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
@@ -0,0 +1,1700 @@
+/*
+ Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
+ Copyright (C) 2001 Dirk Mueller (mueller@kde.org)
+ Copyright (C) 2002 Waldo Bastian (bastian@kde.org)
+ Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All
+ rights reserved.
+ Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ This class provides all functionality needed for loading images, style
+ sheets and html pages from the web. It has a memory cache for these objects.
+*/
+
+#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
+
+#include <algorithm>
+#include <limits>
+#include <utility>
+
+#include "base/time/time.h"
+#include "third_party/blink/public/platform/modules/fetch/fetch_api_request.mojom-blink.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_url.h"
+#include "third_party/blink/public/platform/web_url_request.h"
+#include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h"
+#include "third_party/blink/renderer/platform/histogram.h"
+#include "third_party/blink/renderer/platform/instance_counters.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/traced_value.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_context.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_initiator_type_names.h"
+#include "third_party/blink/renderer/platform/loader/fetch/memory_cache.h"
+#include "third_party/blink/renderer/platform/loader/fetch/raw_resource.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loader.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loading_log.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h"
+#include "third_party/blink/renderer/platform/loader/fetch/unique_identifier.h"
+#include "third_party/blink/renderer/platform/mhtml/archive_resource.h"
+#include "third_party/blink/renderer/platform/mhtml/mhtml_archive.h"
+#include "third_party/blink/renderer/platform/network/network_instrumentation.h"
+#include "third_party/blink/renderer/platform/network/network_utils.h"
+#include "third_party/blink/renderer/platform/probe/platform_probes.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/scheduler/child/web_scheduler.h"
+#include "third_party/blink/renderer/platform/weborigin/known_ports.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+#include "third_party/blink/renderer/platform/weborigin/security_policy.h"
+#include "third_party/blink/renderer/platform/weborigin/security_violation_reporting_policy.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+using blink::WebURLRequest;
+
+namespace blink {
+
+constexpr uint32_t ResourceFetcher::kKeepaliveInflightBytesQuota;
+
+namespace {
+
+constexpr base::TimeDelta kKeepaliveLoadersTimeout =
+ base::TimeDelta::FromSeconds(30);
+
+#define DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, name) \
+ case Resource::k##name: { \
+ DEFINE_THREAD_SAFE_STATIC_LOCAL( \
+ EnumerationHistogram, resource_histogram, \
+ ("Blink.MemoryCache.RevalidationPolicy." prefix #name, kLoad + 1)); \
+ resource_histogram.Count(policy); \
+ break; \
+ }
+
+#define DEFINE_RESOURCE_HISTOGRAM(prefix) \
+ switch (factory.GetType()) { \
+ DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, CSSStyleSheet) \
+ DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, Font) \
+ DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, Image) \
+ DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, ImportResource) \
+ DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, LinkPrefetch) \
+ DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, MainResource) \
+ DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, Manifest) \
+ DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, Audio) \
+ DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, Video) \
+ DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, Mock) \
+ DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, Raw) \
+ DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, Script) \
+ DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, SVGDocument) \
+ DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, TextTrack) \
+ DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, XSLStyleSheet) \
+ }
+
+void AddRedirectsToTimingInfo(Resource* resource, ResourceTimingInfo* info) {
+ // Store redirect responses that were packed inside the final response.
+ const auto& responses = resource->GetResponse().RedirectResponses();
+ for (size_t i = 0; i < responses.size(); ++i) {
+ const KURL& new_url = i + 1 < responses.size()
+ ? KURL(responses[i + 1].Url())
+ : resource->GetResourceRequest().Url();
+ bool cross_origin =
+ !SecurityOrigin::AreSameSchemeHostPort(responses[i].Url(), new_url);
+ info->AddRedirect(responses[i], cross_origin);
+ }
+}
+
+ResourceLoadPriority TypeToPriority(Resource::Type type) {
+ switch (type) {
+ case Resource::kMainResource:
+ case Resource::kCSSStyleSheet:
+ case Resource::kFont:
+ // Also parser-blocking scripts (set explicitly in loadPriority)
+ return ResourceLoadPriority::kVeryHigh;
+ case Resource::kXSLStyleSheet:
+ DCHECK(RuntimeEnabledFeatures::XSLTEnabled());
+ FALLTHROUGH;
+ case Resource::kRaw:
+ case Resource::kImportResource:
+ case Resource::kScript:
+ // Also visible resources/images (set explicitly in loadPriority)
+ return ResourceLoadPriority::kHigh;
+ case Resource::kManifest:
+ case Resource::kMock:
+ // Also late-body scripts discovered by the preload scanner (set
+ // explicitly in loadPriority)
+ return ResourceLoadPriority::kMedium;
+ case Resource::kImage:
+ case Resource::kTextTrack:
+ case Resource::kAudio:
+ case Resource::kVideo:
+ case Resource::kSVGDocument:
+ // Also async scripts (set explicitly in loadPriority)
+ return ResourceLoadPriority::kLow;
+ case Resource::kLinkPrefetch:
+ return ResourceLoadPriority::kVeryLow;
+ }
+
+ NOTREACHED();
+ return ResourceLoadPriority::kUnresolved;
+}
+
+bool ShouldResourceBeAddedToMemoryCache(const FetchParameters& params,
+ Resource* resource) {
+ if (!IsMainThread())
+ return false;
+ if (params.Options().data_buffering_policy == kDoNotBufferData)
+ return false;
+ if (IsRawResource(*resource))
+ return false;
+ return true;
+}
+
+} // namespace
+
+ResourceLoadPriority ResourceFetcher::ComputeLoadPriority(
+ Resource::Type type,
+ const ResourceRequest& resource_request,
+ ResourcePriority::VisibilityStatus visibility,
+ FetchParameters::DeferOption defer_option,
+ FetchParameters::SpeculativePreloadType speculative_preload_type,
+ bool is_link_preload) {
+ ResourceLoadPriority priority = TypeToPriority(type);
+
+ // Visible resources (images in practice) get a boost to High priority.
+ if (visibility == ResourcePriority::kVisible)
+ priority = ResourceLoadPriority::kHigh;
+
+ // Resources before the first image are considered "early" in the document and
+ // resources after the first image are "late" in the document. Important to
+ // note that this is based on when the preload scanner discovers a resource
+ // for the most part so the main parser may not have reached the image element
+ // yet.
+ if (type == Resource::kImage && !is_link_preload)
+ image_fetched_ = true;
+
+ // A preloaded font should not take precedence over critical CSS or
+ // parser-blocking scripts.
+ if (type == Resource::kFont && is_link_preload)
+ priority = ResourceLoadPriority::kHigh;
+
+ if (FetchParameters::kIdleLoad == defer_option) {
+ priority = ResourceLoadPriority::kVeryLow;
+ } else if (type == Resource::kScript) {
+ // Special handling for scripts.
+ // Default/Parser-Blocking/Preload early in document: High (set in
+ // typeToPriority)
+ // Async/Defer: Low Priority (applies to both preload and parser-inserted)
+ // Preload late in document: Medium
+ if (FetchParameters::kLazyLoad == defer_option) {
+ priority = ResourceLoadPriority::kLow;
+ } else if (speculative_preload_type ==
+ FetchParameters::SpeculativePreloadType::kInDocument &&
+ image_fetched_) {
+ // Speculative preload is used as a signal for scripts at the bottom of
+ // the document.
+ priority = ResourceLoadPriority::kMedium;
+ }
+ } else if (FetchParameters::kLazyLoad == defer_option) {
+ priority = ResourceLoadPriority::kVeryLow;
+ } else if (resource_request.GetRequestContext() ==
+ WebURLRequest::kRequestContextBeacon ||
+ resource_request.GetRequestContext() ==
+ WebURLRequest::kRequestContextPing ||
+ resource_request.GetRequestContext() ==
+ WebURLRequest::kRequestContextCSPReport) {
+ priority = ResourceLoadPriority::kVeryLow;
+ }
+
+ // A manually set priority acts as a floor. This is used to ensure that
+ // synchronous requests are always given the highest possible priority, as
+ // well as to ensure that there isn't priority churn if images move in and out
+ // of the viewport, or are displayed more than once, both in and out of the
+ // viewport.
+ return std::max(Context().ModifyPriorityForExperiments(priority),
+ resource_request.Priority());
+}
+
+static void PopulateTimingInfo(ResourceTimingInfo* info, Resource* resource) {
+ KURL initial_url = resource->GetResponse().RedirectResponses().IsEmpty()
+ ? resource->GetResourceRequest().Url()
+ : resource->GetResponse().RedirectResponses()[0].Url();
+ info->SetInitialURL(initial_url);
+ info->SetFinalResponse(resource->GetResponse());
+}
+
+WebURLRequest::RequestContext ResourceFetcher::DetermineRequestContext(
+ Resource::Type type,
+ IsImageSet is_image_set,
+ bool is_main_frame) {
+ DCHECK((is_image_set == kImageNotImageSet) ||
+ (type == Resource::kImage && is_image_set == kImageIsImageSet));
+ switch (type) {
+ case Resource::kMainResource:
+ if (!is_main_frame)
+ return WebURLRequest::kRequestContextIframe;
+ // FIXME: Change this to a context frame type (once we introduce them):
+ // http://fetch.spec.whatwg.org/#concept-request-context-frame-type
+ return WebURLRequest::kRequestContextHyperlink;
+ case Resource::kXSLStyleSheet:
+ DCHECK(RuntimeEnabledFeatures::XSLTEnabled());
+ FALLTHROUGH;
+ case Resource::kCSSStyleSheet:
+ return WebURLRequest::kRequestContextStyle;
+ case Resource::kScript:
+ return WebURLRequest::kRequestContextScript;
+ case Resource::kFont:
+ return WebURLRequest::kRequestContextFont;
+ case Resource::kImage:
+ if (is_image_set == kImageIsImageSet)
+ return WebURLRequest::kRequestContextImageSet;
+ return WebURLRequest::kRequestContextImage;
+ case Resource::kRaw:
+ return WebURLRequest::kRequestContextSubresource;
+ case Resource::kImportResource:
+ return WebURLRequest::kRequestContextImport;
+ case Resource::kLinkPrefetch:
+ return WebURLRequest::kRequestContextPrefetch;
+ case Resource::kTextTrack:
+ return WebURLRequest::kRequestContextTrack;
+ case Resource::kSVGDocument:
+ return WebURLRequest::kRequestContextImage;
+ case Resource::kAudio:
+ return WebURLRequest::kRequestContextAudio;
+ case Resource::kVideo:
+ return WebURLRequest::kRequestContextVideo;
+ case Resource::kManifest:
+ return WebURLRequest::kRequestContextManifest;
+ case Resource::kMock:
+ return WebURLRequest::kRequestContextSubresource;
+ }
+ NOTREACHED();
+ return WebURLRequest::kRequestContextSubresource;
+}
+
+ResourceFetcher::ResourceFetcher(FetchContext* new_context)
+ : context_(new_context),
+ scheduler_(ResourceLoadScheduler::Create(&Context())),
+ archive_(Context().IsMainFrame() ? nullptr : Context().Archive()),
+ resource_timing_report_timer_(
+ Context().GetLoadingTaskRunner(),
+ this,
+ &ResourceFetcher::ResourceTimingReportTimerFired),
+ auto_load_images_(true),
+ images_enabled_(true),
+ allow_stale_resources_(false),
+ image_fetched_(false) {
+ InstanceCounters::IncrementCounter(InstanceCounters::kResourceFetcherCounter);
+}
+
+ResourceFetcher::~ResourceFetcher() {
+ InstanceCounters::DecrementCounter(InstanceCounters::kResourceFetcherCounter);
+}
+
+Resource* ResourceFetcher::CachedResource(const KURL& resource_url) const {
+ KURL url = MemoryCache::RemoveFragmentIdentifierIfNeeded(resource_url);
+ const WeakMember<Resource>& resource = cached_resources_map_.at(url);
+ return resource.Get();
+}
+
+void ResourceFetcher::HoldResourcesFromPreviousFetcher(
+ ResourceFetcher* old_fetcher) {
+ DCHECK(resources_from_previous_fetcher_.IsEmpty());
+ for (Resource* resource : old_fetcher->document_resources_) {
+ if (GetMemoryCache()->Contains(resource))
+ resources_from_previous_fetcher_.insert(resource);
+ }
+}
+
+void ResourceFetcher::ClearResourcesFromPreviousFetcher() {
+ resources_from_previous_fetcher_.clear();
+}
+
+bool ResourceFetcher::IsControlledByServiceWorker() const {
+ return Context().IsControlledByServiceWorker();
+}
+
+bool ResourceFetcher::ResourceNeedsLoad(Resource* resource,
+ const FetchParameters& params,
+ RevalidationPolicy policy) {
+ // Defer a font load until it is actually needed unless this is a link
+ // preload.
+ if (resource->GetType() == Resource::kFont && !params.IsLinkPreload())
+ return false;
+
+ // Defer loading images either when:
+ // - images are disabled
+ // - instructed to defer loading images from network
+ if (resource->GetType() == Resource::kImage &&
+ ShouldDeferImageLoad(resource->Url()))
+ return false;
+
+ return policy != kUse || resource->StillNeedsLoad();
+}
+
+void ResourceFetcher::RequestLoadStarted(unsigned long identifier,
+ Resource* resource,
+ const FetchParameters& params,
+ RevalidationPolicy policy,
+ bool is_static_data) {
+ KURL url = MemoryCache::RemoveFragmentIdentifierIfNeeded(params.Url());
+ if (policy == kUse && resource->GetStatus() == ResourceStatus::kCached &&
+ !cached_resources_map_.Contains(url)) {
+ // Loaded from MemoryCache.
+ DidLoadResourceFromMemoryCache(identifier, resource,
+ params.GetResourceRequest());
+ }
+
+ if (is_static_data)
+ return;
+
+ if (policy == kUse && !resource->StillNeedsLoad() &&
+ !cached_resources_map_.Contains(url)) {
+ // Resources loaded from memory cache should be reported the first time
+ // they're used.
+ scoped_refptr<ResourceTimingInfo> info = ResourceTimingInfo::Create(
+ params.Options().initiator_info.name, CurrentTimeTicksInSeconds(),
+ resource->GetType() == Resource::kMainResource);
+ PopulateTimingInfo(info.get(), resource);
+ info->ClearLoadTimings();
+ info->SetLoadFinishTime(info->InitialTime());
+ scheduled_resource_timing_reports_.push_back(std::move(info));
+ if (!resource_timing_report_timer_.IsActive())
+ resource_timing_report_timer_.StartOneShot(TimeDelta(), FROM_HERE);
+ }
+}
+
+void ResourceFetcher::DidLoadResourceFromMemoryCache(
+ unsigned long identifier,
+ Resource* resource,
+ const ResourceRequest& original_resource_request) {
+ ResourceRequest resource_request(resource->Url());
+ resource_request.SetFrameType(original_resource_request.GetFrameType());
+ resource_request.SetRequestContext(
+ original_resource_request.GetRequestContext());
+ if (original_resource_request.IsAdResource())
+ resource_request.SetIsAdResource();
+
+ Context().DispatchDidLoadResourceFromMemoryCache(identifier, resource_request,
+ resource->GetResponse());
+ Context().DispatchWillSendRequest(
+ identifier, resource_request, ResourceResponse() /* redirects */,
+ resource->GetType(), resource->Options().initiator_info);
+ Context().DispatchDidReceiveResponse(
+ identifier, resource->GetResponse(), resource_request.GetFrameType(),
+ resource_request.GetRequestContext(), resource,
+ FetchContext::ResourceResponseType::kFromMemoryCache);
+
+ if (resource->EncodedSize() > 0) {
+ Context().DispatchDidReceiveData(identifier, nullptr,
+ resource->EncodedSize());
+ }
+
+ Context().DispatchDidFinishLoading(
+ identifier, 0, 0, resource->GetResponse().DecodedBodyLength(), false);
+}
+
+static std::unique_ptr<TracedValue> UrlForTraceEvent(const KURL& url) {
+ std::unique_ptr<TracedValue> value = TracedValue::Create();
+ value->SetString("url", url.GetString());
+ return value;
+}
+
+Resource* ResourceFetcher::ResourceForStaticData(
+ const FetchParameters& params,
+ const ResourceFactory& factory,
+ const SubstituteData& substitute_data) {
+ const KURL& url = params.GetResourceRequest().Url();
+ DCHECK(url.ProtocolIsData() || substitute_data.IsValid() || archive_);
+
+ // TODO(japhet): We only send main resource data: urls through WebURLLoader
+ // for the benefit of a service worker test
+ // (RenderViewImplTest.ServiceWorkerNetworkProviderSetup), which is at a layer
+ // where it isn't easy to mock out a network load. It uses data: urls to
+ // emulate the behavior it wants to test, which would otherwise be reserved
+ // for network loads.
+ if (!archive_ && !substitute_data.IsValid() &&
+ (factory.GetType() == Resource::kMainResource ||
+ factory.GetType() == Resource::kRaw))
+ return nullptr;
+
+ const String cache_identifier = GetCacheIdentifier();
+ if (Resource* old_resource =
+ GetMemoryCache()->ResourceForURL(url, cache_identifier)) {
+ // There's no reason to re-parse if we saved the data from the previous
+ // parse.
+ if (params.Options().data_buffering_policy != kDoNotBufferData)
+ return old_resource;
+ GetMemoryCache()->Remove(old_resource);
+ }
+
+ ResourceResponse response;
+ scoped_refptr<SharedBuffer> data;
+ if (substitute_data.IsValid()) {
+ data = substitute_data.Content();
+ response.SetURL(url);
+ response.SetMimeType(substitute_data.MimeType());
+ response.SetExpectedContentLength(data->size());
+ response.SetTextEncodingName(substitute_data.TextEncoding());
+ } else if (url.ProtocolIsData()) {
+ data = NetworkUtils::ParseDataURLAndPopulateResponse(url, response);
+ if (!data)
+ return nullptr;
+ // |response| is modified by parseDataURLAndPopulateResponse() and is
+ // ready to be used.
+ } else {
+ ArchiveResource* archive_resource =
+ archive_->SubresourceForURL(params.Url());
+ // The archive doesn't contain the resource, the request must be aborted.
+ if (!archive_resource)
+ return nullptr;
+ data = archive_resource->Data();
+ response.SetURL(url);
+ response.SetMimeType(archive_resource->MimeType());
+ response.SetExpectedContentLength(data->size());
+ response.SetTextEncodingName(archive_resource->TextEncoding());
+ }
+
+ Resource* resource = factory.Create(
+ params.GetResourceRequest(), params.Options(), params.DecoderOptions());
+ // FIXME: We should provide a body stream here.
+ resource->SetStatus(ResourceStatus::kPending);
+ resource->NotifyStartLoad();
+ resource->ResponseReceived(response, nullptr);
+ resource->SetDataBufferingPolicy(kBufferData);
+ if (data->size())
+ resource->SetResourceBuffer(data);
+ resource->SetIdentifier(CreateUniqueIdentifier());
+ resource->SetCacheIdentifier(cache_identifier);
+ resource->SetSourceOrigin(GetSourceOrigin(params.Options()));
+ resource->Finish(0.0, Context().GetLoadingTaskRunner().get());
+
+ if (!substitute_data.IsValid())
+ AddToMemoryCacheIfNeeded(params, resource);
+
+ return resource;
+}
+
+Resource* ResourceFetcher::ResourceForBlockedRequest(
+ const FetchParameters& params,
+ const ResourceFactory& factory,
+ ResourceRequestBlockedReason blocked_reason) {
+ Resource* resource = factory.Create(
+ params.GetResourceRequest(), params.Options(), params.DecoderOptions());
+ resource->SetStatus(ResourceStatus::kPending);
+ resource->NotifyStartLoad();
+ resource->SetSourceOrigin(GetSourceOrigin(params.Options()));
+ resource->FinishAsError(ResourceError::CancelledDueToAccessCheckError(
+ params.Url(), blocked_reason),
+ Context().GetLoadingTaskRunner().get());
+ return resource;
+}
+
+void ResourceFetcher::MakePreloadedResourceBlockOnloadIfNeeded(
+ Resource* resource,
+ const FetchParameters& params) {
+ // TODO(yoav): Test that non-blocking resources (video/audio/track) continue
+ // to not-block even after being preloaded and discovered.
+ if (resource && resource->Loader() &&
+ resource->IsLoadEventBlockingResourceType() &&
+ resource->IsLinkPreload() && !params.IsLinkPreload() &&
+ non_blocking_loaders_.Contains(resource->Loader())) {
+ non_blocking_loaders_.erase(resource->Loader());
+ loaders_.insert(resource->Loader());
+ }
+}
+
+void ResourceFetcher::UpdateMemoryCacheStats(Resource* resource,
+ RevalidationPolicy policy,
+ const FetchParameters& params,
+ const ResourceFactory& factory,
+ bool is_static_data) const {
+ if (is_static_data)
+ return;
+
+ if (params.IsSpeculativePreload() || params.IsLinkPreload()) {
+ DEFINE_RESOURCE_HISTOGRAM("Preload.");
+ } else {
+ DEFINE_RESOURCE_HISTOGRAM("");
+ }
+
+ // Aims to count Resource only referenced from MemoryCache (i.e. what would be
+ // dead if MemoryCache holds weak references to Resource). Currently we check
+ // references to Resource from ResourceClient and |m_preloads| only, because
+ // they are major sources of references.
+ if (resource && !resource->IsAlive() && !ContainsAsPreload(resource)) {
+ DEFINE_RESOURCE_HISTOGRAM("Dead.");
+ }
+}
+
+bool ResourceFetcher::ContainsAsPreload(Resource* resource) const {
+ auto it = preloads_.find(PreloadKey(resource->Url(), resource->GetType()));
+ return it != preloads_.end() && it->value == resource;
+}
+
+void ResourceFetcher::RemovePreload(Resource* resource) {
+ auto it = preloads_.find(PreloadKey(resource->Url(), resource->GetType()));
+ if (it == preloads_.end())
+ return;
+ if (it->value == resource)
+ preloads_.erase(it);
+}
+
+ResourceRequestBlockedReason ResourceFetcher::PrepareRequest(
+ FetchParameters& params,
+ const ResourceFactory& factory,
+ const SubstituteData& substitute_data,
+ unsigned long identifier) {
+ ResourceRequest& resource_request = params.MutableResourceRequest();
+ Resource::Type resource_type = factory.GetType();
+ const ResourceLoaderOptions& options = params.Options();
+
+ DCHECK(options.synchronous_policy == kRequestAsynchronously ||
+ resource_type == Resource::kRaw ||
+ resource_type == Resource::kXSLStyleSheet);
+
+ params.OverrideContentType(factory.ContentType());
+
+ // Don't send security violation reports for speculative preloads.
+ SecurityViolationReportingPolicy reporting_policy =
+ params.IsSpeculativePreload()
+ ? SecurityViolationReportingPolicy::kSuppressReporting
+ : SecurityViolationReportingPolicy::kReport;
+
+ // Note that resource_request.GetRedirectStatus() may return kFollowedRedirect
+ // here since e.g. DocumentThreadableLoader may create a new Resource from
+ // a ResourceRequest that originates from the ResourceRequest passed to
+ // the redirect handling callback.
+
+ // Before modifying the request for CSP, evaluate report-only headers. This
+ // allows site owners to learn about requests that are being modified
+ // (e.g. mixed content that is being upgraded by upgrade-insecure-requests).
+ Context().CheckCSPForRequest(
+ resource_request.GetRequestContext(),
+ MemoryCache::RemoveFragmentIdentifierIfNeeded(params.Url()), options,
+ reporting_policy, resource_request.GetRedirectStatus());
+
+ // This may modify params.Url() (via the resource_request argument).
+ Context().PopulateResourceRequest(
+ resource_type, params.GetClientHintsPreferences(),
+ params.GetResourceWidth(), resource_request);
+
+ if (!params.Url().IsValid())
+ return ResourceRequestBlockedReason::kOther;
+
+ resource_request.SetPriority(ComputeLoadPriority(
+ resource_type, params.GetResourceRequest(), ResourcePriority::kNotVisible,
+ params.Defer(), params.GetSpeculativePreloadType(),
+ params.IsLinkPreload()));
+ if (resource_request.GetCacheMode() == mojom::FetchCacheMode::kDefault) {
+ resource_request.SetCacheMode(Context().ResourceRequestCachePolicy(
+ resource_request, resource_type, params.Defer()));
+ }
+ if (resource_request.GetRequestContext() ==
+ WebURLRequest::kRequestContextUnspecified) {
+ resource_request.SetRequestContext(DetermineRequestContext(
+ resource_type, kImageNotImageSet, Context().IsMainFrame()));
+ }
+ if (resource_type == Resource::kLinkPrefetch)
+ resource_request.SetHTTPHeaderField(HTTPNames::Purpose, "prefetch");
+
+ Context().AddAdditionalRequestHeaders(
+ resource_request, (resource_type == Resource::kMainResource)
+ ? kFetchMainResource
+ : kFetchSubresource);
+
+ network_instrumentation::ResourcePrioritySet(identifier,
+ resource_request.Priority());
+
+ KURL url = MemoryCache::RemoveFragmentIdentifierIfNeeded(params.Url());
+ ResourceRequestBlockedReason blocked_reason = Context().CanRequest(
+ resource_type, resource_request, url, options, reporting_policy,
+ params.GetOriginRestriction(), resource_request.GetRedirectStatus());
+
+ if (Context().IsAdResource(url, resource_type,
+ resource_request.GetRequestContext())) {
+ resource_request.SetIsAdResource();
+ }
+
+ if (blocked_reason != ResourceRequestBlockedReason::kNone)
+ return blocked_reason;
+
+ const scoped_refptr<const SecurityOrigin>& origin = options.security_origin;
+ if (origin && !origin->IsUnique() &&
+ !origin->IsSameSchemeHostPort(Context().GetSecurityOrigin())) {
+ // |options.security_origin| may differ from the document's origin if
+ // this is a fetch initiated by an isolated world execution context, with a
+ // different SecurityOrigin. In this case, plumb it through as the
+ // RequestorOrigin so that the browser process can make policy decisions for
+ // this request, based on any special permissions the isolated world may
+ // have been granted.
+ // TODO(nick, dcheng): Find a way to formalize the isolated world origin
+ // check in https://crbug.com/792154.
+ resource_request.SetRequestorOrigin(origin);
+ }
+
+ // For initial requests, call prepareRequest() here before revalidation
+ // policy is determined.
+ Context().PrepareRequest(resource_request,
+ FetchContext::RedirectType::kNotForRedirect);
+
+ if (!params.Url().IsValid())
+ return ResourceRequestBlockedReason::kOther;
+
+ params.MutableOptions().cors_flag =
+ !origin || !origin->CanRequest(params.Url());
+
+ if (options.cors_handling_by_resource_fetcher ==
+ kEnableCORSHandlingByResourceFetcher) {
+ bool allow_stored_credentials = false;
+ switch (resource_request.GetFetchCredentialsMode()) {
+ case network::mojom::FetchCredentialsMode::kOmit:
+ break;
+ case network::mojom::FetchCredentialsMode::kSameOrigin:
+ allow_stored_credentials = !params.Options().cors_flag;
+ break;
+ case network::mojom::FetchCredentialsMode::kInclude:
+ allow_stored_credentials = true;
+ break;
+ }
+ resource_request.SetAllowStoredCredentials(allow_stored_credentials);
+ }
+
+ return ResourceRequestBlockedReason::kNone;
+}
+
+Resource* ResourceFetcher::RequestResource(
+ FetchParameters& params,
+ const ResourceFactory& factory,
+ ResourceClient* client,
+ const SubstituteData& substitute_data) {
+ // Only async requests get ResourceClient callbacks, so sync requests
+ // shouldn't provide a client.
+ DCHECK(!client ||
+ params.Options().synchronous_policy == kRequestAsynchronously);
+ Resource* resource =
+ RequestResourceInternal(params, factory, substitute_data);
+ DCHECK(resource);
+ if (client)
+ client->SetResource(resource, Context().GetLoadingTaskRunner().get());
+ return resource;
+}
+
+Resource* ResourceFetcher::RequestResourceInternal(
+ FetchParameters& params,
+ const ResourceFactory& factory,
+ const SubstituteData& substitute_data) {
+ unsigned long identifier = CreateUniqueIdentifier();
+ ResourceRequest& resource_request = params.MutableResourceRequest();
+ network_instrumentation::ScopedResourceLoadTracker
+ scoped_resource_load_tracker(identifier, resource_request);
+ SCOPED_BLINK_UMA_HISTOGRAM_TIMER_THREAD_SAFE(
+ "Blink.Fetch.RequestResourceTime");
+ // TODO(dproy): Remove this. http://crbug.com/659666
+ TRACE_EVENT1("blink", "ResourceFetcher::requestResource", "url",
+ UrlForTraceEvent(params.Url()));
+
+ // TODO(crbug.com/123004): Remove once we have enough stats on data URIs that
+ // contain fragments ('#' characters).
+ //
+ // TODO(crbug.com/796173): This call happens before commit for iframes that
+ // have data URI sources, which causes UKM to miss the metric recording.
+ if (context_) {
+ const KURL& url = params.Url();
+ if (url.HasFragmentIdentifier() && url.ProtocolIsData()) {
+ context_->RecordDataUriWithOctothorpe();
+ }
+ }
+
+ ResourceRequestBlockedReason blocked_reason =
+ PrepareRequest(params, factory, substitute_data, identifier);
+ if (blocked_reason != ResourceRequestBlockedReason::kNone)
+ return ResourceForBlockedRequest(params, factory, blocked_reason);
+
+ Resource::Type resource_type = factory.GetType();
+
+ if (!params.IsSpeculativePreload()) {
+ // Only log if it's not for speculative preload.
+ Context().RecordLoadingActivity(resource_request, resource_type,
+ params.Options().initiator_info.name);
+ }
+
+ Resource* resource = nullptr;
+ RevalidationPolicy policy = kLoad;
+
+ bool is_data_url = resource_request.Url().ProtocolIsData();
+ bool is_static_data = is_data_url || substitute_data.IsValid() || archive_;
+ if (is_static_data) {
+ resource = ResourceForStaticData(params, factory, substitute_data);
+ if (resource) {
+ policy =
+ DetermineRevalidationPolicy(resource_type, params, *resource, true);
+ } else if (!is_data_url && archive_) {
+ // Abort the request if the archive doesn't contain the resource, except
+ // in the case of data URLs which might have resources such as fonts that
+ // need to be decoded only on demand. These data URLs are allowed to be
+ // processed using the normal ResourceFetcher machinery.
+ return ResourceForBlockedRequest(params, factory,
+ ResourceRequestBlockedReason::kOther);
+ }
+ }
+
+ if (!resource) {
+ resource = MatchPreload(params, resource_type);
+ if (resource) {
+ policy = kUse;
+ // If |params| is for a blocking resource and a preloaded resource is
+ // found, we may need to make it block the onload event.
+ MakePreloadedResourceBlockOnloadIfNeeded(resource, params);
+ } else if (IsMainThread()) {
+ resource =
+ GetMemoryCache()->ResourceForURL(params.Url(), GetCacheIdentifier());
+ if (resource) {
+ policy = DetermineRevalidationPolicy(resource_type, params, *resource,
+ is_static_data);
+ }
+ }
+ }
+
+ UpdateMemoryCacheStats(resource, policy, params, factory, is_static_data);
+
+ switch (policy) {
+ case kReload:
+ GetMemoryCache()->Remove(resource);
+ FALLTHROUGH;
+ case kLoad:
+ resource = CreateResourceForLoading(params, factory);
+ break;
+ case kRevalidate:
+ InitializeRevalidation(resource_request, resource);
+ break;
+ case kUse:
+ if (resource->IsLinkPreload() && !params.IsLinkPreload())
+ resource->SetLinkPreload(false);
+ break;
+ }
+ DCHECK(resource);
+ // TODO(yoav): turn to a DCHECK. See https://crbug.com/690632
+ CHECK_EQ(resource->GetType(), resource_type);
+
+ if (policy != kUse)
+ resource->SetIdentifier(identifier);
+
+ // TODO(yoav): It is not clear why preloads are exempt from this check. Can we
+ // remove the exemption?
+ if (!params.IsSpeculativePreload() || policy != kUse) {
+ // When issuing another request for a resource that is already in-flight
+ // make sure to not demote the priority of the in-flight request. If the new
+ // request isn't at the same priority as the in-flight request, only allow
+ // promotions. This can happen when a visible image's priority is increased
+ // and then another reference to the image is parsed (which would be at a
+ // lower priority).
+ if (resource_request.Priority() > resource->GetResourceRequest().Priority())
+ resource->DidChangePriority(resource_request.Priority(), 0);
+ // TODO(yoav): I'd expect the stated scenario to not go here, as its policy
+ // would be Use.
+ }
+
+ // If only the fragment identifiers differ, it is the same resource.
+ DCHECK(EqualIgnoringFragmentIdentifier(resource->Url(), params.Url()));
+ RequestLoadStarted(identifier, resource, params, policy, is_static_data);
+ cached_resources_map_.Set(
+ MemoryCache::RemoveFragmentIdentifierIfNeeded(params.Url()), resource);
+ document_resources_.insert(resource);
+
+ // Returns with an existing resource if the resource does not need to start
+ // loading immediately. If revalidation policy was determined as |Revalidate|,
+ // the resource was already initialized for the revalidation here, but won't
+ // start loading.
+ if (ResourceNeedsLoad(resource, params, policy)) {
+ if (StartLoad(resource)) {
+ scoped_resource_load_tracker.ResourceLoadContinuesBeyondScope();
+ } else {
+ resource->FinishAsError(ResourceError::CancelledError(params.Url()),
+ Context().GetLoadingTaskRunner().get());
+ }
+ }
+
+ if (policy != kUse)
+ InsertAsPreloadIfNecessary(resource, params, resource_type);
+
+ return resource;
+}
+
+void ResourceFetcher::ResourceTimingReportTimerFired(TimerBase* timer) {
+ DCHECK_EQ(timer, &resource_timing_report_timer_);
+ Vector<scoped_refptr<ResourceTimingInfo>> timing_reports;
+ timing_reports.swap(scheduled_resource_timing_reports_);
+ for (const auto& timing_info : timing_reports)
+ Context().AddResourceTiming(*timing_info);
+}
+
+void ResourceFetcher::InitializeRevalidation(
+ ResourceRequest& revalidating_request,
+ Resource* resource) {
+ DCHECK(resource);
+ DCHECK(GetMemoryCache()->Contains(resource));
+ DCHECK(resource->IsLoaded());
+ DCHECK(resource->CanUseCacheValidator());
+ DCHECK(!resource->IsCacheValidator());
+ DCHECK(!Context().IsControlledByServiceWorker());
+ // RawResource doesn't support revalidation.
+ CHECK(!IsRawResource(*resource));
+
+ const AtomicString& last_modified =
+ resource->GetResponse().HttpHeaderField(HTTPNames::Last_Modified);
+ const AtomicString& e_tag =
+ resource->GetResponse().HttpHeaderField(HTTPNames::ETag);
+ if (!last_modified.IsEmpty() || !e_tag.IsEmpty()) {
+ DCHECK_NE(mojom::FetchCacheMode::kBypassCache,
+ revalidating_request.GetCacheMode());
+ if (revalidating_request.GetCacheMode() ==
+ mojom::FetchCacheMode::kValidateCache) {
+ revalidating_request.SetHTTPHeaderField(HTTPNames::Cache_Control,
+ "max-age=0");
+ }
+ }
+ if (!last_modified.IsEmpty()) {
+ revalidating_request.SetHTTPHeaderField(HTTPNames::If_Modified_Since,
+ last_modified);
+ }
+ if (!e_tag.IsEmpty())
+ revalidating_request.SetHTTPHeaderField(HTTPNames::If_None_Match, e_tag);
+
+ resource->SetRevalidatingRequest(revalidating_request);
+}
+
+scoped_refptr<const SecurityOrigin> ResourceFetcher::GetSourceOrigin(
+ const ResourceLoaderOptions& options) const {
+ if (options.security_origin)
+ return options.security_origin;
+
+ return Context().GetSecurityOrigin();
+}
+
+void ResourceFetcher::AddToMemoryCacheIfNeeded(const FetchParameters& params,
+ Resource* resource) {
+ if (!ShouldResourceBeAddedToMemoryCache(params, resource))
+ return;
+
+ GetMemoryCache()->Add(resource);
+}
+
+Resource* ResourceFetcher::CreateResourceForLoading(
+ const FetchParameters& params,
+ const ResourceFactory& factory) {
+ const String cache_identifier = GetCacheIdentifier();
+ DCHECK(!IsMainThread() ||
+ !GetMemoryCache()->ResourceForURL(params.GetResourceRequest().Url(),
+ cache_identifier));
+
+ RESOURCE_LOADING_DVLOG(1) << "Loading Resource for "
+ << params.GetResourceRequest().Url().ElidedString();
+
+ Resource* resource = factory.Create(
+ params.GetResourceRequest(), params.Options(), params.DecoderOptions());
+ resource->SetLinkPreload(params.IsLinkPreload());
+ if (params.IsSpeculativePreload()) {
+ resource->SetPreloadDiscoveryTime(params.PreloadDiscoveryTime());
+ }
+ resource->SetCacheIdentifier(cache_identifier);
+ resource->SetSourceOrigin(GetSourceOrigin(params.Options()));
+
+ AddToMemoryCacheIfNeeded(params, resource);
+ return resource;
+}
+
+void ResourceFetcher::StorePerformanceTimingInitiatorInformation(
+ Resource* resource) {
+ const AtomicString& fetch_initiator = resource->Options().initiator_info.name;
+ if (fetch_initiator == FetchInitiatorTypeNames::internal)
+ return;
+
+ bool is_main_resource = resource->GetType() == Resource::kMainResource;
+
+ // The request can already be fetched in a previous navigation. Thus
+ // startTime must be set accordingly.
+ double start_time = resource->GetResourceRequest().NavigationStartTime()
+ ? resource->GetResourceRequest().NavigationStartTime()
+ : CurrentTimeTicksInSeconds();
+
+ // This buffer is created and populated for providing transferSize
+ // and redirect timing opt-in information.
+ if (is_main_resource) {
+ DCHECK(!navigation_timing_info_);
+ navigation_timing_info_ = ResourceTimingInfo::Create(
+ fetch_initiator, start_time, is_main_resource);
+ }
+
+ scoped_refptr<ResourceTimingInfo> info =
+ ResourceTimingInfo::Create(fetch_initiator, start_time, is_main_resource);
+
+ if (resource->IsCacheValidator()) {
+ const AtomicString& timing_allow_origin =
+ resource->GetResponse().HttpHeaderField(HTTPNames::Timing_Allow_Origin);
+ if (!timing_allow_origin.IsEmpty())
+ info->SetOriginalTimingAllowOrigin(timing_allow_origin);
+ }
+
+ if (!is_main_resource ||
+ Context().UpdateTimingInfoForIFrameNavigation(info.get())) {
+ resource_timing_info_map_.insert(resource, std::move(info));
+ }
+}
+
+void ResourceFetcher::RecordResourceTimingOnRedirect(
+ Resource* resource,
+ const ResourceResponse& redirect_response,
+ bool cross_origin) {
+ ResourceTimingInfoMap::iterator it = resource_timing_info_map_.find(resource);
+ if (it != resource_timing_info_map_.end()) {
+ it->value->AddRedirect(redirect_response, cross_origin);
+ }
+
+ if (resource->GetType() == Resource::kMainResource) {
+ DCHECK(navigation_timing_info_);
+ navigation_timing_info_->AddRedirect(redirect_response, cross_origin);
+ }
+}
+
+static bool IsDownloadOrStreamRequest(const ResourceRequest& request) {
+ // Never use cache entries for DownloadToFile / UseStreamOnResponse requests.
+ // The data will be delivered through other paths.
+ return request.DownloadToFile() || request.DownloadToBlob() ||
+ request.UseStreamOnResponse();
+}
+
+Resource* ResourceFetcher::MatchPreload(const FetchParameters& params,
+ Resource::Type type) {
+ auto it = preloads_.find(PreloadKey(params.Url(), type));
+ if (it == preloads_.end())
+ return nullptr;
+
+ Resource* resource = it->value;
+
+ if (resource->MustRefetchDueToIntegrityMetadata(params))
+ return nullptr;
+
+ if (params.IsSpeculativePreload())
+ return resource;
+ if (params.IsLinkPreload()) {
+ resource->SetLinkPreload(true);
+ return resource;
+ }
+
+ const ResourceRequest& request = params.GetResourceRequest();
+ if (request.DownloadToFile() || request.DownloadToBlob())
+ return nullptr;
+
+ if (IsImageResourceDisallowedToBeReused(*resource) ||
+ !resource->CanReuse(params, GetSourceOrigin(params.Options())))
+ return nullptr;
+
+ if (!resource->MatchPreload(params, Context().GetLoadingTaskRunner().get()))
+ return nullptr;
+ preloads_.erase(it);
+ matched_preloads_.push_back(resource);
+ return resource;
+}
+
+void ResourceFetcher::InsertAsPreloadIfNecessary(Resource* resource,
+ const FetchParameters& params,
+ Resource::Type type) {
+ if (!params.IsSpeculativePreload() && !params.IsLinkPreload())
+ return;
+ // CSP layout tests verify that preloads are subject to access checks by
+ // seeing if they are in the `preload started` list. Therefore do not add
+ // them to the list if the load is immediately denied.
+ if (resource->LoadFailedOrCanceled() &&
+ resource->GetResourceError().IsAccessCheck()) {
+ return;
+ }
+ PreloadKey key(params.Url(), type);
+ if (preloads_.find(key) != preloads_.end())
+ return;
+
+ preloads_.insert(key, resource);
+ resource->MarkAsPreload();
+ if (preloaded_urls_for_test_)
+ preloaded_urls_for_test_->insert(resource->Url().GetString());
+}
+
+bool ResourceFetcher::IsImageResourceDisallowedToBeReused(
+ const Resource& existing_resource) const {
+ // When images are disabled, don't ever load images, even if the image is
+ // cached or it is a data: url. In this case:
+ // - remove the image from the memory cache, and
+ // - create a new resource but defer loading (this is done by
+ // ResourceNeedsLoad()).
+ //
+ // This condition must be placed before the condition on |is_static_data| to
+ // prevent loading a data: URL.
+ //
+ // TODO(japhet): Can we get rid of one of these settings?
+
+ if (existing_resource.GetType() != Resource::kImage)
+ return false;
+
+ return !Context().AllowImage(images_enabled_, existing_resource.Url());
+}
+
+ResourceFetcher::RevalidationPolicy
+ResourceFetcher::DetermineRevalidationPolicy(
+ Resource::Type type,
+ const FetchParameters& fetch_params,
+ const Resource& existing_resource,
+ bool is_static_data) const {
+ RevalidationPolicy policy = DetermineRevalidationPolicyInternal(
+ type, fetch_params, existing_resource, is_static_data);
+
+ TRACE_EVENT_INSTANT1("blink", "ResourceFetcher::DetermineRevalidationPolicy",
+ TRACE_EVENT_SCOPE_THREAD, "revalidationPolicy", policy);
+
+ return policy;
+}
+
+ResourceFetcher::RevalidationPolicy
+ResourceFetcher::DetermineRevalidationPolicyInternal(
+ Resource::Type type,
+ const FetchParameters& fetch_params,
+ const Resource& existing_resource,
+ bool is_static_data) const {
+ const ResourceRequest& request = fetch_params.GetResourceRequest();
+
+ if (IsDownloadOrStreamRequest(request))
+ return kReload;
+
+ if (IsImageResourceDisallowedToBeReused(existing_resource))
+ return kReload;
+
+ // If the existing resource is loading and the associated fetcher is not equal
+ // to |this|, we must not use the resource. Otherwise, CSP violation may
+ // happen in redirect handling.
+ if (existing_resource.Loader() &&
+ existing_resource.Loader()->Fetcher() != this) {
+ return kReload;
+ }
+
+ // It's hard to share a not-yet-referenced preloads via MemoryCache correctly.
+ // A not-yet-matched preloads made by a foreign ResourceFetcher and stored in
+ // the memory cache could be used without this block.
+ if ((fetch_params.IsLinkPreload() || fetch_params.IsSpeculativePreload()) &&
+ existing_resource.IsUnusedPreload()) {
+ return kReload;
+ }
+
+ // Checks if the resource has an explicit policy about integrity metadata.
+ //
+ // This is necessary because ScriptResource and CSSStyleSheetResource objects
+ // do not keep the raw data around after the source is accessed once, so if
+ // the resource is accessed from the MemoryCache for a second time, there is
+ // no way to redo an integrity check.
+ //
+ // Thus, Blink implements a scheme where it caches the integrity information
+ // for those resources after the first time it is checked, and if there is
+ // another request for that resource, with the same integrity metadata, Blink
+ // skips the integrity calculation. However, if the integrity metadata is a
+ // mismatch, the MemoryCache must be skipped here, and a new request for the
+ // resource must be made to get the raw data. This is expected to be an
+ // uncommon case, however, as it implies two same-origin requests to the same
+ // resource, but with different integrity metadata.
+ if (existing_resource.MustRefetchDueToIntegrityMetadata(fetch_params)) {
+ return kReload;
+ }
+
+ // If the same URL has been loaded as a different type, we need to reload.
+ if (existing_resource.GetType() != type) {
+ // FIXME: If existingResource is a Preload and the new type is LinkPrefetch
+ // We really should discard the new prefetch since the preload has more
+ // specific type information! crbug.com/379893
+ // fast/dom/HTMLLinkElement/link-and-subresource-test hits this case.
+ RESOURCE_LOADING_DVLOG(1) << "ResourceFetcher::DetermineRevalidationPolicy "
+ "reloading due to type mismatch.";
+ return kReload;
+ }
+
+ // If resource was populated from a SubstituteData load or data: url, use it.
+ // This doesn't necessarily mean that |resource| was just created by using
+ // ResourceForStaticData().
+ if (is_static_data)
+ return kUse;
+
+ if (!existing_resource.CanReuse(fetch_params,
+ GetSourceOrigin(fetch_params.Options()))) {
+ RESOURCE_LOADING_DVLOG(1) << "ResourceFetcher::DetermineRevalidationPolicy "
+ "reloading due to Resource::CanReuse() "
+ "returning false.";
+ return kReload;
+ }
+
+ // Don't reload resources while pasting.
+ if (allow_stale_resources_)
+ return kUse;
+
+ // FORCE_CACHE uses the cache no matter what.
+ if (request.GetCacheMode() == mojom::FetchCacheMode::kForceCache)
+ return kUse;
+
+ // Don't reuse resources with Cache-control: no-store.
+ if (existing_resource.HasCacheControlNoStoreHeader()) {
+ RESOURCE_LOADING_DVLOG(1) << "ResourceFetcher::DetermineRevalidationPolicy "
+ "reloading due to Cache-control: no-store.";
+ return kReload;
+ }
+
+ // During the initial load, avoid loading the same resource multiple times for
+ // a single document, even if the cache policies would tell us to. We also
+ // group loads of the same resource together. Raw resources are exempted, as
+ // XHRs fall into this category and may have user-set Cache-Control: headers
+ // or other factors that require separate requests.
+ if (type != Resource::kRaw) {
+ if (!Context().IsLoadComplete() &&
+ cached_resources_map_.Contains(
+ MemoryCache::RemoveFragmentIdentifierIfNeeded(
+ existing_resource.Url())))
+ return kUse;
+ if (existing_resource.IsLoading())
+ return kUse;
+ }
+
+ // RELOAD always reloads
+ if (request.GetCacheMode() == mojom::FetchCacheMode::kBypassCache) {
+ RESOURCE_LOADING_DVLOG(1) << "ResourceFetcher::DetermineRevalidationPolicy "
+ "reloading due to "
+ "FetchCacheMode::kBypassCache";
+ return kReload;
+ }
+
+ // We'll try to reload the resource if it failed last time.
+ if (existing_resource.ErrorOccurred()) {
+ RESOURCE_LOADING_DVLOG(1) << "ResourceFetcher::DetermineRevalidationPolicy "
+ "reloading due to resource being in the error "
+ "state";
+ return kReload;
+ }
+
+ // List of available images logic allows images to be re-used without cache
+ // validation. We restrict this only to images from memory cache which are the
+ // same as the version in the current document.
+ if (type == Resource::kImage &&
+ &existing_resource == CachedResource(request.Url())) {
+ return kUse;
+ }
+
+ if (existing_resource.MustReloadDueToVaryHeader(request))
+ return kReload;
+
+ // If any of the redirects in the chain to loading the resource were not
+ // cacheable, we cannot reuse our cached resource.
+ if (!existing_resource.CanReuseRedirectChain()) {
+ RESOURCE_LOADING_DVLOG(1) << "ResourceFetcher::DetermineRevalidationPolicy "
+ "reloading due to an uncacheable redirect";
+ return kReload;
+ }
+
+ // Check if the cache headers requires us to revalidate (cache expiration for
+ // example).
+ if (request.GetCacheMode() == mojom::FetchCacheMode::kValidateCache ||
+ existing_resource.MustRevalidateDueToCacheHeaders() ||
+ request.CacheControlContainsNoCache()) {
+ // Revalidation is harmful for non-matched preloads because it may lead to
+ // sharing one preloaded resource among multiple ResourceFetchers.
+ if (existing_resource.IsUnusedPreload())
+ return kReload;
+
+ // See if the resource has usable ETag or Last-modified headers. If the page
+ // is controlled by the ServiceWorker, we choose the Reload policy because
+ // the revalidation headers should not be exposed to the
+ // ServiceWorker.(crbug.com/429570)
+ if (existing_resource.CanUseCacheValidator() &&
+ !Context().IsControlledByServiceWorker()) {
+ // If the resource is already a cache validator but not started yet, the
+ // |Use| policy should be applied to subsequent requests.
+ if (existing_resource.IsCacheValidator()) {
+ DCHECK(existing_resource.StillNeedsLoad());
+ return kUse;
+ }
+ return kRevalidate;
+ }
+
+ // No, must reload.
+ RESOURCE_LOADING_DVLOG(1) << "ResourceFetcher::DetermineRevalidationPolicy "
+ "reloading due to missing cache validators.";
+ return kReload;
+ }
+
+ return kUse;
+}
+
+void ResourceFetcher::SetAutoLoadImages(bool enable) {
+ if (enable == auto_load_images_)
+ return;
+
+ auto_load_images_ = enable;
+
+ if (!auto_load_images_)
+ return;
+
+ ReloadImagesIfNotDeferred();
+}
+
+void ResourceFetcher::SetImagesEnabled(bool enable) {
+ if (enable == images_enabled_)
+ return;
+
+ images_enabled_ = enable;
+
+ if (!images_enabled_)
+ return;
+
+ ReloadImagesIfNotDeferred();
+}
+
+bool ResourceFetcher::ShouldDeferImageLoad(const KURL& url) const {
+ return !Context().AllowImage(images_enabled_, url) || !auto_load_images_;
+}
+
+void ResourceFetcher::ReloadImagesIfNotDeferred() {
+ for (Resource* resource : document_resources_) {
+ if (resource->GetType() == Resource::kImage && resource->StillNeedsLoad() &&
+ !ShouldDeferImageLoad(resource->Url()))
+ StartLoad(resource);
+ }
+}
+
+void ResourceFetcher::ClearContext() {
+ DCHECK(resources_from_previous_fetcher_.IsEmpty());
+ scheduler_->Shutdown();
+ ClearPreloads(ResourceFetcher::kClearAllPreloads);
+ context_ = Context().Detach();
+
+ // Make sure the only requests still going are keepalive requests.
+ // Callers of ClearContext() should be calling StopFetching() prior
+ // to this, but it's possible for additional requests to start during
+ // StopFetching() (e.g., fallback fonts that only trigger when the
+ // first choice font failed to load).
+ StopFetching();
+
+ if (!loaders_.IsEmpty() || !non_blocking_loaders_.IsEmpty()) {
+ // There are some keepalive requests.
+ // The use of WrapPersistent creates a reference cycle intentionally,
+ // to keep the ResourceFetcher and ResourceLoaders alive until the requests
+ // complete or the timer fires.
+ keepalive_loaders_task_handle_ = PostDelayedCancellableTask(
+ *Context().GetLoadingTaskRunner(), FROM_HERE,
+ WTF::Bind(&ResourceFetcher::StopFetchingIncludingKeepaliveLoaders,
+ WrapPersistent(this)),
+ kKeepaliveLoadersTimeout);
+ }
+}
+
+int ResourceFetcher::BlockingRequestCount() const {
+ return loaders_.size();
+}
+
+int ResourceFetcher::NonblockingRequestCount() const {
+ return non_blocking_loaders_.size();
+}
+
+int ResourceFetcher::ActiveRequestCount() const {
+ return loaders_.size() + non_blocking_loaders_.size();
+}
+
+void ResourceFetcher::EnableIsPreloadedForTest() {
+ if (preloaded_urls_for_test_)
+ return;
+ preloaded_urls_for_test_ = std::make_unique<HashSet<String>>();
+
+ for (const auto& pair : preloads_) {
+ Resource* resource = pair.value;
+ preloaded_urls_for_test_->insert(resource->Url().GetString());
+ }
+}
+
+bool ResourceFetcher::IsPreloadedForTest(const KURL& url) const {
+ DCHECK(preloaded_urls_for_test_);
+ return preloaded_urls_for_test_->Contains(url.GetString());
+}
+
+void ResourceFetcher::ClearPreloads(ClearPreloadsPolicy policy) {
+ Vector<PreloadKey> keys_to_be_removed;
+ for (const auto& pair : preloads_) {
+ Resource* resource = pair.value;
+ if (policy == kClearAllPreloads || !resource->IsLinkPreload()) {
+ GetMemoryCache()->Remove(resource);
+ keys_to_be_removed.push_back(pair.key);
+ }
+ }
+ preloads_.RemoveAll(keys_to_be_removed);
+
+ matched_preloads_.clear();
+}
+
+void ResourceFetcher::WarnUnusedPreloads() {
+ for (const auto& pair : preloads_) {
+ Resource* resource = pair.value;
+ if (resource && resource->IsLinkPreload() && resource->IsUnusedPreload()) {
+ Context().AddWarningConsoleMessage(
+ "The resource " + resource->Url().GetString() +
+ " was preloaded using link preload but not used within a few " +
+ "seconds from the window's load event. Please make sure it has " +
+ "an appropriate `as` value and it is preloaded intentionally.",
+ FetchContext::kJSSource);
+ }
+ }
+}
+
+ArchiveResource* ResourceFetcher::CreateArchive(Resource* resource) {
+ // Only the top-frame can load MHTML.
+ if (!Context().IsMainFrame()) {
+ Context().AddErrorConsoleMessage(
+ "Attempted to load a multipart archive into an subframe: " +
+ resource->Url().GetString(),
+ FetchContext::kJSSource);
+ return nullptr;
+ }
+
+ archive_ = MHTMLArchive::Create(resource->Url(), resource->ResourceBuffer());
+ if (!archive_) {
+ // Log if attempting to load an invalid archive resource.
+ Context().AddErrorConsoleMessage(
+ "Malformed multipart archive: " + resource->Url().GetString(),
+ FetchContext::kJSSource);
+ return nullptr;
+ }
+
+ return archive_->MainResource();
+}
+
+ResourceTimingInfo* ResourceFetcher::GetNavigationTimingInfo() {
+ return navigation_timing_info_.get();
+}
+
+void ResourceFetcher::HandleLoadCompletion(Resource* resource) {
+ Context().DidLoadResource(resource);
+
+ resource->ReloadIfLoFiOrPlaceholderImage(this, Resource::kReloadIfNeeded);
+}
+
+void ResourceFetcher::HandleLoaderFinish(Resource* resource,
+ double finish_time,
+ LoaderFinishType type,
+ uint32_t inflight_keepalive_bytes,
+ bool blocked_cross_site_document) {
+ DCHECK(resource);
+
+ DCHECK_LE(inflight_keepalive_bytes, inflight_keepalive_bytes_);
+ inflight_keepalive_bytes_ -= inflight_keepalive_bytes;
+
+ ResourceLoader* loader = resource->Loader();
+ if (type == kDidFinishFirstPartInMultipart) {
+ // When loading a multipart resource, make the loader non-block when
+ // finishing loading the first part.
+ MoveResourceLoaderToNonBlocking(loader);
+ } else {
+ RemoveResourceLoader(loader);
+ DCHECK(!non_blocking_loaders_.Contains(loader));
+ }
+ DCHECK(!loaders_.Contains(loader));
+
+ const int64_t encoded_data_length =
+ resource->GetResponse().EncodedDataLength();
+
+ if (resource->GetType() == Resource::kMainResource) {
+ DCHECK(navigation_timing_info_);
+ // Store redirect responses that were packed inside the final response.
+ AddRedirectsToTimingInfo(resource, navigation_timing_info_.get());
+ if (resource->GetResponse().IsHTTP()) {
+ PopulateTimingInfo(navigation_timing_info_.get(), resource);
+ navigation_timing_info_->AddFinalTransferSize(
+ encoded_data_length == -1 ? 0 : encoded_data_length);
+ }
+ }
+ if (scoped_refptr<ResourceTimingInfo> info =
+ resource_timing_info_map_.Take(resource)) {
+ // Store redirect responses that were packed inside the final response.
+ AddRedirectsToTimingInfo(resource, info.get());
+
+ if (resource->GetResponse().IsHTTP() &&
+ resource->GetResponse().HttpStatusCode() < 400) {
+ PopulateTimingInfo(info.get(), resource);
+ info->SetLoadFinishTime(finish_time);
+ // encodedDataLength == -1 means "not available".
+ // TODO(ricea): Find cases where it is not available but the
+ // PerformanceResourceTiming spec requires it to be available and fix
+ // them.
+ info->AddFinalTransferSize(
+ encoded_data_length == -1 ? 0 : encoded_data_length);
+
+ if (resource->Options().request_initiator_context == kDocumentContext)
+ Context().AddResourceTiming(*info);
+ resource->ReportResourceTimingToClients(*info);
+ }
+ }
+
+ resource->VirtualTimePauser().UnpauseVirtualTime();
+ Context().DispatchDidFinishLoading(
+ resource->Identifier(), finish_time, encoded_data_length,
+ resource->GetResponse().DecodedBodyLength(), blocked_cross_site_document);
+
+ if (type == kDidFinishLoading)
+ resource->Finish(finish_time, Context().GetLoadingTaskRunner().get());
+
+ HandleLoadCompletion(resource);
+}
+
+void ResourceFetcher::HandleLoaderError(Resource* resource,
+ const ResourceError& error,
+ uint32_t inflight_keepalive_bytes) {
+ DCHECK(resource);
+
+ DCHECK_LE(inflight_keepalive_bytes, inflight_keepalive_bytes_);
+ inflight_keepalive_bytes_ -= inflight_keepalive_bytes;
+
+ RemoveResourceLoader(resource->Loader());
+
+ resource_timing_info_map_.Take(resource);
+
+ bool is_internal_request = resource->Options().initiator_info.name ==
+ FetchInitiatorTypeNames::internal;
+
+ resource->VirtualTimePauser().UnpauseVirtualTime();
+ Context().DispatchDidFail(
+ resource->LastResourceRequest().Url(), resource->Identifier(), error,
+ resource->GetResponse().EncodedDataLength(), is_internal_request);
+
+ if (error.IsCancellation())
+ RemovePreload(resource);
+ resource->FinishAsError(error, Context().GetLoadingTaskRunner().get());
+
+ HandleLoadCompletion(resource);
+}
+
+void ResourceFetcher::MoveResourceLoaderToNonBlocking(ResourceLoader* loader) {
+ DCHECK(loader);
+ // TODO(yoav): Convert CHECK to DCHECK if no crash reports come in.
+ CHECK(loaders_.Contains(loader));
+ non_blocking_loaders_.insert(loader);
+ loaders_.erase(loader);
+}
+
+bool ResourceFetcher::StartLoad(Resource* resource) {
+ DCHECK(resource);
+ DCHECK(resource->StillNeedsLoad());
+
+ ResourceRequest request(resource->GetResourceRequest());
+ ResourceLoader* loader = nullptr;
+
+ {
+ // Forbids JavaScript/revalidation until start()
+ // to prevent unintended state transitions.
+ Resource::RevalidationStartForbiddenScope
+ revalidation_start_forbidden_scope(resource);
+ ScriptForbiddenScope script_forbidden_scope;
+
+ if (!Context().ShouldLoadNewResource(resource->GetType()) &&
+ IsMainThread()) {
+ GetMemoryCache()->Remove(resource);
+ return false;
+ }
+
+ ResourceResponse response;
+
+ blink::probe::PlatformSendRequest probe(&Context(), resource->Identifier(),
+ request, response,
+ resource->Options().initiator_info);
+
+ if (Context().GetFrameScheduler()) {
+ WebScopedVirtualTimePauser virtual_time_pauser =
+ Context().GetFrameScheduler()->CreateWebScopedVirtualTimePauser(
+ resource->Url().GetString(),
+ WebScopedVirtualTimePauser::VirtualTaskDuration::kNonInstant);
+ virtual_time_pauser.PauseVirtualTime();
+ resource->VirtualTimePauser() = std::move(virtual_time_pauser);
+ }
+ Context().DispatchWillSendRequest(resource->Identifier(), request, response,
+ resource->GetType(),
+ resource->Options().initiator_info);
+
+ // TODO(shaochuan): Saving modified ResourceRequest back to |resource|,
+ // remove once dispatchWillSendRequest() takes const ResourceRequest.
+ // crbug.com/632580
+ resource->SetResourceRequest(request);
+
+ using QuotaType = decltype(inflight_keepalive_bytes_);
+ QuotaType size = 0;
+ if (request.GetKeepalive() && request.HttpBody()) {
+ auto original_size = request.HttpBody()->SizeInBytes();
+ DCHECK_LE(inflight_keepalive_bytes_, kKeepaliveInflightBytesQuota);
+ if (original_size > std::numeric_limits<QuotaType>::max())
+ return false;
+ size = static_cast<QuotaType>(original_size);
+ if (kKeepaliveInflightBytesQuota - inflight_keepalive_bytes_ < size)
+ return false;
+
+ inflight_keepalive_bytes_ += size;
+ }
+
+ loader = ResourceLoader::Create(this, scheduler_, resource, size);
+ if (resource->ShouldBlockLoadEvent())
+ loaders_.insert(loader);
+ else
+ non_blocking_loaders_.insert(loader);
+
+ StorePerformanceTimingInitiatorInformation(resource);
+
+ // NotifyStartLoad() shouldn't cause AddClient/RemoveClient().
+ Resource::ProhibitAddRemoveClientInScope
+ prohibit_add_remove_client_in_scope(resource);
+
+ resource->NotifyStartLoad();
+ }
+
+ loader->Start();
+ return true;
+}
+
+void ResourceFetcher::RemoveResourceLoader(ResourceLoader* loader) {
+ DCHECK(loader);
+ if (loaders_.Contains(loader))
+ loaders_.erase(loader);
+ else if (non_blocking_loaders_.Contains(loader))
+ non_blocking_loaders_.erase(loader);
+ else
+ NOTREACHED();
+
+ if (loaders_.IsEmpty() && non_blocking_loaders_.IsEmpty())
+ keepalive_loaders_task_handle_.Cancel();
+}
+
+void ResourceFetcher::StopFetching() {
+ StopFetchingInternal(StopFetchingTarget::kExcludingKeepaliveLoaders);
+}
+
+void ResourceFetcher::SetDefersLoading(bool defers) {
+ for (const auto& loader : non_blocking_loaders_)
+ loader->SetDefersLoading(defers);
+ for (const auto& loader : loaders_)
+ loader->SetDefersLoading(defers);
+}
+
+void ResourceFetcher::UpdateAllImageResourcePriorities() {
+ TRACE_EVENT0(
+ "blink",
+ "ResourceLoadPriorityOptimizer::updateAllImageResourcePriorities");
+ for (Resource* resource : document_resources_) {
+ if (!resource || resource->GetType() != Resource::kImage ||
+ !resource->IsLoading())
+ continue;
+
+ ResourcePriority resource_priority = resource->PriorityFromObservers();
+ ResourceLoadPriority resource_load_priority =
+ ComputeLoadPriority(Resource::kImage, resource->GetResourceRequest(),
+ resource_priority.visibility);
+ if (resource_load_priority == resource->GetResourceRequest().Priority())
+ continue;
+
+ resource->DidChangePriority(resource_load_priority,
+ resource_priority.intra_priority_value);
+ network_instrumentation::ResourcePrioritySet(resource->Identifier(),
+ resource_load_priority);
+ Context().DispatchDidChangeResourcePriority(
+ resource->Identifier(), resource_load_priority,
+ resource_priority.intra_priority_value);
+ }
+}
+
+void ResourceFetcher::ReloadLoFiImages() {
+ for (Resource* resource : document_resources_) {
+ if (resource)
+ resource->ReloadIfLoFiOrPlaceholderImage(this, Resource::kReloadAlways);
+ }
+}
+
+String ResourceFetcher::GetCacheIdentifier() const {
+ if (Context().IsControlledByServiceWorker())
+ return String::Number(Context().ServiceWorkerID());
+ return MemoryCache::DefaultCacheIdentifier();
+}
+
+void ResourceFetcher::EmulateLoadStartedForInspector(
+ Resource* resource,
+ const KURL& url,
+ WebURLRequest::RequestContext request_context,
+ const AtomicString& initiator_name) {
+ if (CachedResource(url))
+ return;
+ ResourceRequest resource_request(url);
+ resource_request.SetRequestContext(request_context);
+ ResourceLoaderOptions options = resource->Options();
+ options.initiator_info.name = initiator_name;
+ FetchParameters params(resource_request, options);
+ Context().CanRequest(resource->GetType(), resource->LastResourceRequest(),
+ resource->LastResourceRequest().Url(), params.Options(),
+ SecurityViolationReportingPolicy::kReport,
+ params.GetOriginRestriction(),
+ resource->LastResourceRequest().GetRedirectStatus());
+ RequestLoadStarted(resource->Identifier(), resource, params, kUse);
+}
+
+void ResourceFetcher::PrepareForLeakDetection() {
+ // Stop loaders including keepalive ones that may persist after page
+ // navigation and thus affect instance counters of leak detection.
+ StopFetchingIncludingKeepaliveLoaders();
+}
+
+void ResourceFetcher::StopFetchingInternal(StopFetchingTarget target) {
+ // TODO(toyoshim): May want to suspend scheduler while canceling loaders so
+ // that the cancellations below do not awake unnecessary scheduling.
+
+ HeapVector<Member<ResourceLoader>> loaders_to_cancel;
+ for (const auto& loader : non_blocking_loaders_) {
+ if (target == StopFetchingTarget::kIncludingKeepaliveLoaders ||
+ !loader->ShouldBeKeptAliveWhenDetached()) {
+ loaders_to_cancel.push_back(loader);
+ }
+ }
+ for (const auto& loader : loaders_) {
+ if (target == StopFetchingTarget::kIncludingKeepaliveLoaders ||
+ !loader->ShouldBeKeptAliveWhenDetached()) {
+ loaders_to_cancel.push_back(loader);
+ }
+ }
+
+ for (const auto& loader : loaders_to_cancel) {
+ if (loaders_.Contains(loader) || non_blocking_loaders_.Contains(loader))
+ loader->Cancel();
+ }
+}
+
+void ResourceFetcher::StopFetchingIncludingKeepaliveLoaders() {
+ StopFetchingInternal(StopFetchingTarget::kIncludingKeepaliveLoaders);
+}
+
+void ResourceFetcher::Trace(blink::Visitor* visitor) {
+ visitor->Trace(context_);
+ visitor->Trace(scheduler_);
+ visitor->Trace(archive_);
+ visitor->Trace(loaders_);
+ visitor->Trace(non_blocking_loaders_);
+ visitor->Trace(cached_resources_map_);
+ visitor->Trace(document_resources_);
+ visitor->Trace(resources_from_previous_fetcher_);
+ visitor->Trace(preloads_);
+ visitor->Trace(matched_preloads_);
+ visitor->Trace(resource_timing_info_map_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
new file mode 100644
index 00000000000..134349299fb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
@@ -0,0 +1,345 @@
+/*
+ Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
+ Copyright (C) 2001 Dirk Mueller <mueller@kde.org>
+ Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All
+ rights reserved.
+ Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ This class provides all functionality needed for loading images, style
+ sheets and html pages from the web. It has a memory cache for these objects.
+*/
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_FETCHER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_FETCHER_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_context.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_initiator_info.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h"
+#include "third_party/blink/renderer/platform/loader/fetch/preload_key.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_error.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_load_priority.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
+#include "third_party/blink/renderer/platform/loader/fetch/substitute_data.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/timer.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+
+namespace blink {
+
+class ArchiveResource;
+class MHTMLArchive;
+class KURL;
+class ResourceTimingInfo;
+
+// The ResourceFetcher provides a per-context interface to the MemoryCache and
+// enforces a bunch of security checks and rules for resource revalidation. Its
+// lifetime is roughly per-DocumentLoader, in that it is generally created in
+// the DocumentLoader constructor and loses its ability to generate network
+// requests when the DocumentLoader is destroyed. Documents also hold a pointer
+// to ResourceFetcher for their lifetime (and will create one if they are
+// initialized without a LocalFrame), so a Document can keep a ResourceFetcher
+// alive past detach if scripts still reference the Document.
+class PLATFORM_EXPORT ResourceFetcher
+ : public GarbageCollectedFinalized<ResourceFetcher> {
+ WTF_MAKE_NONCOPYABLE(ResourceFetcher);
+ USING_PRE_FINALIZER(ResourceFetcher, ClearPreloads);
+
+ public:
+ static ResourceFetcher* Create(FetchContext* context) {
+ return new ResourceFetcher(context);
+ }
+ virtual ~ResourceFetcher();
+ virtual void Trace(blink::Visitor*);
+
+ // Triggers a fetch based on the given FetchParameters (if there isn't a
+ // suitable Resource already cached) and registers the given ResourceClient
+ // with the Resource. Guaranteed to return a non-null Resource of the subtype
+ // specified by ResourceFactory::GetType().
+ Resource* RequestResource(FetchParameters&,
+ const ResourceFactory&,
+ ResourceClient*,
+ const SubstituteData& = SubstituteData());
+
+ Resource* CachedResource(const KURL&) const;
+
+ using DocumentResourceMap = HeapHashMap<String, WeakMember<Resource>>;
+ const DocumentResourceMap& AllResources() const {
+ return cached_resources_map_;
+ }
+
+ void HoldResourcesFromPreviousFetcher(ResourceFetcher*);
+ void ClearResourcesFromPreviousFetcher();
+
+ // Binds the given Resource instance to this ResourceFetcher instance to
+ // start loading the Resource actually.
+ // Usually, RequestResource() calls this method internally, but needs to
+ // call this method explicitly on cases such as ResourceNeedsLoad() returning
+ // false.
+ bool StartLoad(Resource*);
+
+ void SetAutoLoadImages(bool);
+ void SetImagesEnabled(bool);
+
+ FetchContext& Context() const {
+ return context_ ? *context_.Get() : FetchContext::NullInstance();
+ }
+ void ClearContext();
+
+ int BlockingRequestCount() const;
+ int NonblockingRequestCount() const;
+ int ActiveRequestCount() const;
+
+ enum ClearPreloadsPolicy {
+ kClearAllPreloads,
+ kClearSpeculativeMarkupPreloads
+ };
+
+ void EnableIsPreloadedForTest();
+ bool IsPreloadedForTest(const KURL&) const;
+
+ int CountPreloads() const { return preloads_.size(); }
+ void ClearPreloads(ClearPreloadsPolicy = kClearAllPreloads);
+ void LogPreloadStats(ClearPreloadsPolicy);
+ void WarnUnusedPreloads();
+
+ MHTMLArchive* Archive() const { return archive_.Get(); }
+ ArchiveResource* CreateArchive(Resource*);
+
+ void SetDefersLoading(bool);
+ void StopFetching();
+
+ bool ShouldDeferImageLoad(const KURL&) const;
+
+ void RecordResourceTimingOnRedirect(Resource*, const ResourceResponse&, bool);
+
+ enum LoaderFinishType { kDidFinishLoading, kDidFinishFirstPartInMultipart };
+ void HandleLoaderFinish(Resource*,
+ double finish_time,
+ LoaderFinishType,
+ uint32_t inflight_keepalive_bytes,
+ bool blocked_cross_site_document);
+ void HandleLoaderError(Resource*,
+ const ResourceError&,
+ uint32_t inflight_keepalive_bytes);
+ bool IsControlledByServiceWorker() const;
+
+ String GetCacheIdentifier() const;
+
+ enum IsImageSet { kImageNotImageSet, kImageIsImageSet };
+
+ WARN_UNUSED_RESULT static WebURLRequest::RequestContext
+ DetermineRequestContext(Resource::Type, IsImageSet, bool is_main_frame);
+
+ void UpdateAllImageResourcePriorities();
+
+ void ReloadLoFiImages();
+
+ // Calling this method before main document resource is fetched is invalid.
+ ResourceTimingInfo* GetNavigationTimingInfo();
+
+ // Returns whether the given resource is contained as a preloaded resource.
+ bool ContainsAsPreload(Resource*) const;
+
+ void RemovePreload(Resource*);
+
+ void LoosenLoadThrottlingPolicy() { scheduler_->LoosenThrottlingPolicy(); }
+ void OnNetworkQuiet() { scheduler_->OnNetworkQuiet(); }
+
+ // Workaround for https://crbug.com/666214.
+ // TODO(hiroshige): Remove this hack.
+ void EmulateLoadStartedForInspector(Resource*,
+ const KURL&,
+ WebURLRequest::RequestContext,
+ const AtomicString& initiator_name);
+
+ // This is called from leak detectors (Real-world leak detector & layout test
+ // leak detector) to clean up loaders after page navigation before instance
+ // counting.
+ void PrepareForLeakDetection();
+
+ private:
+ friend class ResourceCacheValidationSuppressor;
+ enum class StopFetchingTarget {
+ kExcludingKeepaliveLoaders,
+ kIncludingKeepaliveLoaders,
+ };
+
+ ResourceFetcher(FetchContext*);
+
+ void InitializeRevalidation(ResourceRequest&, Resource*);
+ // When |security_origin| of the ResourceLoaderOptions is not a nullptr, it'll
+ // be used instead of the associated FetchContext's SecurityOrigin.
+ scoped_refptr<const SecurityOrigin> GetSourceOrigin(
+ const ResourceLoaderOptions&) const;
+ void AddToMemoryCacheIfNeeded(const FetchParameters&, Resource*);
+ Resource* CreateResourceForLoading(const FetchParameters&,
+ const ResourceFactory&);
+ void StorePerformanceTimingInitiatorInformation(Resource*);
+ ResourceLoadPriority ComputeLoadPriority(
+ Resource::Type,
+ const ResourceRequest&,
+ ResourcePriority::VisibilityStatus,
+ FetchParameters::DeferOption = FetchParameters::kNoDefer,
+ FetchParameters::SpeculativePreloadType =
+ FetchParameters::SpeculativePreloadType::kNotSpeculative,
+ bool is_link_preload = false);
+
+ Resource* RequestResourceInternal(FetchParameters&,
+ const ResourceFactory&,
+ const SubstituteData&);
+ ResourceRequestBlockedReason PrepareRequest(FetchParameters&,
+ const ResourceFactory&,
+ const SubstituteData&,
+ unsigned long identifier);
+
+ Resource* ResourceForStaticData(const FetchParameters&,
+ const ResourceFactory&,
+ const SubstituteData&);
+ Resource* ResourceForBlockedRequest(const FetchParameters&,
+ const ResourceFactory&,
+ ResourceRequestBlockedReason);
+
+ Resource* MatchPreload(const FetchParameters& params, Resource::Type);
+ void InsertAsPreloadIfNecessary(Resource*,
+ const FetchParameters& params,
+ Resource::Type);
+
+ bool IsImageResourceDisallowedToBeReused(const Resource&) const;
+
+ void StopFetchingInternal(StopFetchingTarget);
+ void StopFetchingIncludingKeepaliveLoaders();
+
+ // RevalidationPolicy enum values are used in UMAs https://crbug.com/579496.
+ enum RevalidationPolicy { kUse, kRevalidate, kReload, kLoad };
+
+ // A wrapper just for placing a trace_event macro.
+ RevalidationPolicy DetermineRevalidationPolicy(
+ Resource::Type,
+ const FetchParameters&,
+ const Resource& existing_resource,
+ bool is_static_data) const;
+ // Determines a RevalidationPolicy given a FetchParameters and an existing
+ // resource retrieved from the memory cache (can be a newly constructed one
+ // for a static data).
+ RevalidationPolicy DetermineRevalidationPolicyInternal(
+ Resource::Type,
+ const FetchParameters&,
+ const Resource& existing_resource,
+ bool is_static_data) const;
+
+ void MakePreloadedResourceBlockOnloadIfNeeded(Resource*,
+ const FetchParameters&);
+ void MoveResourceLoaderToNonBlocking(ResourceLoader*);
+ void RemoveResourceLoader(ResourceLoader*);
+ void HandleLoadCompletion(Resource*);
+
+ void RequestLoadStarted(unsigned long identifier,
+ Resource*,
+ const FetchParameters&,
+ RevalidationPolicy,
+ bool is_static_data = false);
+
+ void DidLoadResourceFromMemoryCache(unsigned long identifier,
+ Resource*,
+ const ResourceRequest&);
+
+ bool ResourceNeedsLoad(Resource*, const FetchParameters&, RevalidationPolicy);
+
+ void ResourceTimingReportTimerFired(TimerBase*);
+
+ void ReloadImagesIfNotDeferred();
+
+ void UpdateMemoryCacheStats(Resource*,
+ RevalidationPolicy,
+ const FetchParameters&,
+ const ResourceFactory&,
+ bool is_static_data) const;
+
+ Member<FetchContext> context_;
+ Member<ResourceLoadScheduler> scheduler_;
+
+ DocumentResourceMap cached_resources_map_;
+ HeapHashSet<WeakMember<Resource>> document_resources_;
+
+ // When populated, forces Resources to remain alive across a navigation, to
+ // increase the odds the next document will be able to reuse resources from
+ // the previous page. Unpopulated unless experiment is enabled.
+ HeapHashSet<Member<Resource>> resources_from_previous_fetcher_;
+
+ HeapHashMap<PreloadKey, Member<Resource>> preloads_;
+ HeapVector<Member<Resource>> matched_preloads_;
+ Member<MHTMLArchive> archive_;
+
+ TaskRunnerTimer<ResourceFetcher> resource_timing_report_timer_;
+
+ using ResourceTimingInfoMap =
+ HeapHashMap<Member<Resource>, scoped_refptr<ResourceTimingInfo>>;
+ ResourceTimingInfoMap resource_timing_info_map_;
+
+ scoped_refptr<ResourceTimingInfo> navigation_timing_info_;
+
+ Vector<scoped_refptr<ResourceTimingInfo>> scheduled_resource_timing_reports_;
+
+ HeapHashSet<Member<ResourceLoader>> loaders_;
+ HeapHashSet<Member<ResourceLoader>> non_blocking_loaders_;
+
+ std::unique_ptr<HashSet<String>> preloaded_urls_for_test_;
+
+ // Timeout timer for keepalive requests.
+ TaskHandle keepalive_loaders_task_handle_;
+
+ uint32_t inflight_keepalive_bytes_ = 0;
+
+ // 28 bits left
+ bool auto_load_images_ : 1;
+ bool images_enabled_ : 1;
+ bool allow_stale_resources_ : 1;
+ bool image_fetched_ : 1;
+
+ static constexpr uint32_t kKeepaliveInflightBytesQuota = 64 * 1024;
+};
+
+class ResourceCacheValidationSuppressor {
+ WTF_MAKE_NONCOPYABLE(ResourceCacheValidationSuppressor);
+ STACK_ALLOCATED();
+
+ public:
+ explicit ResourceCacheValidationSuppressor(ResourceFetcher* loader)
+ : loader_(loader), previous_state_(false) {
+ if (loader_) {
+ previous_state_ = loader_->allow_stale_resources_;
+ loader_->allow_stale_resources_ = true;
+ }
+ }
+ ~ResourceCacheValidationSuppressor() {
+ if (loader_)
+ loader_->allow_stale_resources_ = previous_state_;
+ }
+
+ private:
+ Member<ResourceFetcher> loader_;
+ bool previous_state_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_FETCHER_H_
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc
new file mode 100644
index 00000000000..679e35ddf20
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc
@@ -0,0 +1,852 @@
+/*
+ * Copyright (c) 2013, 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 "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
+
+#include <memory>
+#include "services/network/public/mojom/request_context_frame_type.mojom-shared.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/modules/fetch/fetch_api_request.mojom-shared.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_url_loader.h"
+#include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
+#include "third_party/blink/public/platform/web_url_response.h"
+#include "third_party/blink/renderer/platform/exported/wrapped_resource_response.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/heap/heap_allocator.h"
+#include "third_party/blink/renderer/platform/heap/member.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_initiator_info.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_initiator_type_names.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h"
+#include "third_party/blink/renderer/platform/loader/fetch/memory_cache.h"
+#include "third_party/blink/renderer/platform/loader/fetch/raw_resource.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_error.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loader.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h"
+#include "third_party/blink/renderer/platform/loader/testing/fetch_testing_platform_support.h"
+#include "third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h"
+#include "third_party/blink/renderer/platform/loader/testing/mock_resource.h"
+#include "third_party/blink/renderer/platform/loader/testing/mock_resource_client.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
+#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/weburl_loader_mock.h"
+#include "third_party/blink/renderer/platform/testing/weburl_loader_mock_factory_impl.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+
+namespace blink {
+
+namespace {
+
+constexpr char kTestResourceFilename[] = "white-1x1.png";
+constexpr char kTestResourceMimeType[] = "image/png";
+constexpr int kTestResourceSize = 103; // size of white-1x1.png
+
+void RegisterMockedURLLoadWithCustomResponse(const KURL& url,
+ const ResourceResponse& response) {
+ URLTestHelpers::RegisterMockedURLLoadWithCustomResponse(
+ url, test::PlatformTestDataPath(kTestResourceFilename),
+ WrappedResourceResponse(response));
+}
+
+void RegisterMockedURLLoad(const KURL& url) {
+ URLTestHelpers::RegisterMockedURLLoad(
+ url, test::PlatformTestDataPath(kTestResourceFilename),
+ kTestResourceMimeType);
+}
+
+} // namespace
+
+class ResourceFetcherTest : public testing::Test {
+ public:
+ ResourceFetcherTest() = default;
+ ~ResourceFetcherTest() override { GetMemoryCache()->EvictResources(); }
+
+ protected:
+ MockFetchContext* Context() { return platform_->Context(); }
+ void AddResourceToMemoryCache(
+ Resource* resource,
+ scoped_refptr<const SecurityOrigin> source_origin) {
+ resource->SetSourceOrigin(source_origin);
+ GetMemoryCache()->Add(resource);
+ }
+
+ ScopedTestingPlatformSupport<FetchTestingPlatformSupport> platform_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ResourceFetcherTest);
+};
+
+TEST_F(ResourceFetcherTest, StartLoadAfterFrameDetach) {
+ KURL secure_url("https://secureorigin.test/image.png");
+ // Try to request a url. The request should fail, and a resource in an error
+ // state should be returned, and no resource should be present in the cache.
+ ResourceFetcher* fetcher =
+ ResourceFetcher::Create(&FetchContext::NullInstance());
+
+ ResourceRequest resource_request(secure_url);
+ resource_request.SetRequestContext(WebURLRequest::kRequestContextInternal);
+ FetchParameters fetch_params(resource_request);
+ Resource* resource = RawResource::Fetch(fetch_params, fetcher, nullptr);
+ ASSERT_TRUE(resource);
+ EXPECT_TRUE(resource->ErrorOccurred());
+ EXPECT_TRUE(resource->GetResourceError().IsAccessCheck());
+ EXPECT_FALSE(GetMemoryCache()->ResourceForURL(secure_url));
+
+ // Start by calling StartLoad() directly, rather than via RequestResource().
+ // This shouldn't crash.
+ fetcher->StartLoad(RawResource::CreateForTest(secure_url, Resource::kRaw));
+}
+
+TEST_F(ResourceFetcherTest, UseExistingResource) {
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+
+ KURL url("http://127.0.0.1:8000/foo.html");
+ ResourceResponse response(url);
+ response.SetHTTPStatusCode(200);
+ response.SetHTTPHeaderField(HTTPNames::Cache_Control, "max-age=3600");
+ RegisterMockedURLLoadWithCustomResponse(url, response);
+
+ FetchParameters fetch_params{ResourceRequest(url)};
+ Resource* resource = MockResource::Fetch(fetch_params, fetcher, nullptr);
+ ASSERT_TRUE(resource);
+ platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+ EXPECT_TRUE(resource->IsLoaded());
+ EXPECT_TRUE(GetMemoryCache()->Contains(resource));
+
+ Resource* new_resource = MockResource::Fetch(fetch_params, fetcher, nullptr);
+ EXPECT_EQ(resource, new_resource);
+}
+
+// Verify that the ad bit is copied to WillSendRequest's request when the
+// response is served from the memory cache.
+TEST_F(ResourceFetcherTest, WillSendRequestAdBit) {
+ // Add a resource to the memory cache.
+ scoped_refptr<const SecurityOrigin> source_origin =
+ SecurityOrigin::CreateUnique();
+ Context()->SetSecurityOrigin(source_origin);
+ KURL url("http://127.0.0.1:8000/foo.html");
+ Resource* resource = RawResource::CreateForTest(url, Resource::kRaw);
+ AddResourceToMemoryCache(resource, source_origin);
+ ResourceResponse response(url);
+ response.SetHTTPStatusCode(200);
+ response.SetHTTPHeaderField(HTTPNames::Cache_Control, "max-age=3600");
+ resource->ResponseReceived(response, nullptr);
+ resource->FinishForTest();
+
+ // Fetch the cached resource. The request to DispatchWillSendRequest should
+ // preserve the ad bit.
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+ ResourceRequest resource_request(url);
+ resource_request.SetIsAdResource();
+ resource_request.SetRequestContext(WebURLRequest::kRequestContextInternal);
+ FetchParameters fetch_params(resource_request);
+ platform_->GetURLLoaderMockFactory()->RegisterURL(url, WebURLResponse(), "");
+ Resource* new_resource = RawResource::Fetch(fetch_params, fetcher, nullptr);
+
+ EXPECT_EQ(resource, new_resource);
+ WTF::Optional<ResourceRequest> new_request =
+ Context()->RequestFromWillSendRequest();
+ EXPECT_TRUE(new_request.has_value());
+ EXPECT_TRUE(new_request.value().IsAdResource());
+}
+
+TEST_F(ResourceFetcherTest, Vary) {
+ scoped_refptr<const SecurityOrigin> source_origin =
+ SecurityOrigin::CreateUnique();
+ Context()->SetSecurityOrigin(source_origin);
+
+ KURL url("http://127.0.0.1:8000/foo.html");
+ Resource* resource = RawResource::CreateForTest(url, Resource::kRaw);
+ AddResourceToMemoryCache(resource, source_origin);
+
+ ResourceResponse response(url);
+ response.SetHTTPStatusCode(200);
+ response.SetHTTPHeaderField(HTTPNames::Cache_Control, "max-age=3600");
+ response.SetHTTPHeaderField(HTTPNames::Vary, "*");
+ resource->ResponseReceived(response, nullptr);
+ resource->FinishForTest();
+ ASSERT_TRUE(resource->MustReloadDueToVaryHeader(ResourceRequest(url)));
+
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+ ResourceRequest resource_request(url);
+ resource_request.SetRequestContext(WebURLRequest::kRequestContextInternal);
+ FetchParameters fetch_params(resource_request);
+ platform_->GetURLLoaderMockFactory()->RegisterURL(url, WebURLResponse(), "");
+ Resource* new_resource = RawResource::Fetch(fetch_params, fetcher, nullptr);
+ EXPECT_NE(resource, new_resource);
+ new_resource->Loader()->Cancel();
+}
+
+TEST_F(ResourceFetcherTest, NavigationTimingInfo) {
+ KURL url("http://127.0.0.1:8000/foo.html");
+ ResourceResponse response(url);
+ response.SetHTTPStatusCode(200);
+
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+ ResourceRequest resource_request(url);
+ resource_request.SetFrameType(
+ network::mojom::RequestContextFrameType::kNested);
+ resource_request.SetRequestContext(WebURLRequest::kRequestContextForm);
+ FetchParameters fetch_params(resource_request);
+ platform_->GetURLLoaderMockFactory()->RegisterURL(url, WebURLResponse(), "");
+ Resource* resource = RawResource::FetchMainResource(
+ fetch_params, fetcher, nullptr, SubstituteData());
+ resource->ResponseReceived(response, nullptr);
+ EXPECT_EQ(resource->GetType(), Resource::kMainResource);
+
+ ResourceTimingInfo* navigation_timing_info =
+ fetcher->GetNavigationTimingInfo();
+ ASSERT_TRUE(navigation_timing_info);
+ long long encoded_data_length = 123;
+ resource->Loader()->DidFinishLoading(0.0, encoded_data_length, 0, 0, false);
+ EXPECT_EQ(navigation_timing_info->TransferSize(), encoded_data_length);
+
+ // When there are redirects.
+ KURL redirect_url("http://127.0.0.1:8000/redirect.html");
+ ResourceResponse redirect_response(redirect_url);
+ redirect_response.SetHTTPStatusCode(200);
+ long long redirect_encoded_data_length = 123;
+ redirect_response.SetEncodedDataLength(redirect_encoded_data_length);
+ ResourceRequest redirect_resource_request(url);
+ fetcher->RecordResourceTimingOnRedirect(resource, redirect_response, false);
+ EXPECT_EQ(navigation_timing_info->TransferSize(),
+ encoded_data_length + redirect_encoded_data_length);
+}
+
+TEST_F(ResourceFetcherTest, VaryOnBack) {
+ scoped_refptr<const SecurityOrigin> source_origin =
+ SecurityOrigin::CreateUnique();
+ Context()->SetSecurityOrigin(source_origin);
+
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+
+ KURL url("http://127.0.0.1:8000/foo.html");
+ Resource* resource = RawResource::CreateForTest(url, Resource::kRaw);
+ AddResourceToMemoryCache(resource, source_origin);
+
+ ResourceResponse response(url);
+ response.SetHTTPStatusCode(200);
+ response.SetHTTPHeaderField(HTTPNames::Cache_Control, "max-age=3600");
+ response.SetHTTPHeaderField(HTTPNames::Vary, "*");
+ resource->ResponseReceived(response, nullptr);
+ resource->FinishForTest();
+ ASSERT_TRUE(resource->MustReloadDueToVaryHeader(ResourceRequest(url)));
+
+ ResourceRequest resource_request(url);
+ resource_request.SetCacheMode(mojom::FetchCacheMode::kForceCache);
+ resource_request.SetRequestContext(WebURLRequest::kRequestContextInternal);
+ FetchParameters fetch_params(resource_request);
+ Resource* new_resource = RawResource::Fetch(fetch_params, fetcher, nullptr);
+ EXPECT_EQ(resource, new_resource);
+}
+
+TEST_F(ResourceFetcherTest, VaryResource) {
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+
+ KURL url("http://127.0.0.1:8000/foo.html");
+ ResourceResponse response(url);
+ response.SetHTTPStatusCode(200);
+ response.SetHTTPHeaderField(HTTPNames::Cache_Control, "max-age=3600");
+ response.SetHTTPHeaderField(HTTPNames::Vary, "*");
+ RegisterMockedURLLoadWithCustomResponse(url, response);
+
+ FetchParameters fetch_params_original{ResourceRequest(url)};
+ Resource* resource =
+ MockResource::Fetch(fetch_params_original, fetcher, nullptr);
+ ASSERT_TRUE(resource);
+ platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+ ASSERT_TRUE(resource->MustReloadDueToVaryHeader(ResourceRequest(url)));
+
+ FetchParameters fetch_params{ResourceRequest(url)};
+ Resource* new_resource = MockResource::Fetch(fetch_params, fetcher, nullptr);
+ EXPECT_EQ(resource, new_resource);
+}
+
+class RequestSameResourceOnComplete
+ : public GarbageCollectedFinalized<RequestSameResourceOnComplete>,
+ public RawResourceClient {
+ USING_GARBAGE_COLLECTED_MIXIN(RequestSameResourceOnComplete);
+
+ public:
+ explicit RequestSameResourceOnComplete(FetchParameters& params,
+ ResourceFetcher* fetcher)
+ : notify_finished_called_(false),
+ source_origin_(fetcher->Context().GetSecurityOrigin()) {
+ MockResource::Fetch(params, fetcher, this);
+ }
+
+ void NotifyFinished(Resource* resource) override {
+ EXPECT_EQ(GetResource(), resource);
+ MockFetchContext* context =
+ MockFetchContext::Create(MockFetchContext::kShouldLoadNewResource);
+ context->SetSecurityOrigin(source_origin_);
+ ResourceFetcher* fetcher2 = ResourceFetcher::Create(context);
+ ResourceRequest resource_request2(GetResource()->Url());
+ resource_request2.SetCacheMode(mojom::FetchCacheMode::kValidateCache);
+ FetchParameters fetch_params2(resource_request2);
+ Resource* resource2 = MockResource::Fetch(fetch_params2, fetcher2, nullptr);
+ EXPECT_EQ(GetResource(), resource2);
+ notify_finished_called_ = true;
+ ClearResource();
+ }
+ bool NotifyFinishedCalled() const { return notify_finished_called_; }
+
+ void Trace(blink::Visitor* visitor) override {
+ RawResourceClient::Trace(visitor);
+ }
+
+ String DebugName() const override { return "RequestSameResourceOnComplete"; }
+
+ private:
+ bool notify_finished_called_;
+ scoped_refptr<const SecurityOrigin> source_origin_;
+};
+
+TEST_F(ResourceFetcherTest, RevalidateWhileFinishingLoading) {
+ scoped_refptr<const SecurityOrigin> source_origin =
+ SecurityOrigin::CreateUnique();
+ Context()->SetSecurityOrigin(source_origin);
+
+ KURL url("http://127.0.0.1:8000/foo.png");
+
+ ResourceResponse response(url);
+ response.SetHTTPStatusCode(200);
+ response.SetHTTPHeaderField(HTTPNames::Cache_Control, "max-age=3600");
+ response.SetHTTPHeaderField(HTTPNames::ETag, "1234567890");
+ RegisterMockedURLLoadWithCustomResponse(url, response);
+
+ ResourceFetcher* fetcher1 = ResourceFetcher::Create(Context());
+ ResourceRequest request1(url);
+ request1.SetHTTPHeaderField(HTTPNames::Cache_Control, "no-cache");
+ FetchParameters fetch_params1(request1);
+ Persistent<RequestSameResourceOnComplete> client =
+ new RequestSameResourceOnComplete(fetch_params1, fetcher1);
+ platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+ EXPECT_TRUE(client->NotifyFinishedCalled());
+}
+
+TEST_F(ResourceFetcherTest, DontReuseMediaDataUrl) {
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+ ResourceRequest request(KURL("data:text/html,foo"));
+ request.SetRequestContext(WebURLRequest::kRequestContextVideo);
+ request.SetFetchCredentialsMode(network::mojom::FetchCredentialsMode::kOmit);
+ ResourceLoaderOptions options;
+ options.data_buffering_policy = kDoNotBufferData;
+ options.initiator_info.name = FetchInitiatorTypeNames::internal;
+ FetchParameters fetch_params(request, options);
+ Resource* resource1 = RawResource::FetchMedia(fetch_params, fetcher, nullptr);
+ Resource* resource2 = RawResource::FetchMedia(fetch_params, fetcher, nullptr);
+ EXPECT_NE(resource1, resource2);
+}
+
+class ServeRequestsOnCompleteClient final
+ : public GarbageCollectedFinalized<ServeRequestsOnCompleteClient>,
+ public RawResourceClient {
+ USING_GARBAGE_COLLECTED_MIXIN(ServeRequestsOnCompleteClient);
+
+ public:
+ void NotifyFinished(Resource*) override {
+ Platform::Current()->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+ ClearResource();
+ }
+
+ // No callbacks should be received except for the NotifyFinished() triggered
+ // by ResourceLoader::Cancel().
+ void DataSent(Resource*, unsigned long long, unsigned long long) override {
+ ASSERT_TRUE(false);
+ }
+ void ResponseReceived(Resource*,
+ const ResourceResponse&,
+ std::unique_ptr<WebDataConsumerHandle>) override {
+ ASSERT_TRUE(false);
+ }
+ void SetSerializedCachedMetadata(Resource*, const char*, size_t) override {
+ ASSERT_TRUE(false);
+ }
+ void DataReceived(Resource*, const char*, size_t) override {
+ ASSERT_TRUE(false);
+ }
+ bool RedirectReceived(Resource*,
+ const ResourceRequest&,
+ const ResourceResponse&) override {
+ ADD_FAILURE();
+ return true;
+ }
+ void DataDownloaded(Resource*, int) override { ASSERT_TRUE(false); }
+ void DidReceiveResourceTiming(Resource*, const ResourceTimingInfo&) override {
+ ASSERT_TRUE(false);
+ }
+
+ void Trace(blink::Visitor* visitor) override {
+ RawResourceClient::Trace(visitor);
+ }
+
+ String DebugName() const override { return "ServeRequestsOnCompleteClient"; }
+};
+
+// Regression test for http://crbug.com/594072.
+// This emulates a modal dialog triggering a nested run loop inside
+// ResourceLoader::Cancel(). If the ResourceLoader doesn't promptly cancel its
+// WebURLLoader before notifying its clients, a nested run loop may send a
+// network response, leading to an invalid state transition in ResourceLoader.
+TEST_F(ResourceFetcherTest, ResponseOnCancel) {
+ KURL url("http://127.0.0.1:8000/foo.png");
+ RegisterMockedURLLoad(url);
+
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+ ResourceRequest resource_request(url);
+ resource_request.SetRequestContext(WebURLRequest::kRequestContextInternal);
+ FetchParameters fetch_params(resource_request);
+ Persistent<ServeRequestsOnCompleteClient> client =
+ new ServeRequestsOnCompleteClient();
+ Resource* resource = RawResource::Fetch(fetch_params, fetcher, client);
+ resource->Loader()->Cancel();
+}
+
+class ScopedMockRedirectRequester {
+ STACK_ALLOCATED();
+ WTF_MAKE_NONCOPYABLE(ScopedMockRedirectRequester);
+
+ public:
+ explicit ScopedMockRedirectRequester(MockFetchContext* context)
+ : context_(context) {}
+
+ void RegisterRedirect(const WebString& from_url, const WebString& to_url) {
+ KURL redirect_url(from_url);
+ WebURLResponse redirect_response;
+ redirect_response.SetURL(redirect_url);
+ redirect_response.SetHTTPStatusCode(301);
+ redirect_response.SetHTTPHeaderField(HTTPNames::Location, to_url);
+ redirect_response.SetEncodedDataLength(kRedirectResponseOverheadBytes);
+ Platform::Current()->GetURLLoaderMockFactory()->RegisterURL(
+ redirect_url, redirect_response, "");
+ }
+
+ void RegisterFinalResource(const WebString& url) {
+ KURL final_url(url);
+ RegisterMockedURLLoad(final_url);
+ }
+
+ void Request(const WebString& url) {
+ ResourceFetcher* fetcher = ResourceFetcher::Create(context_);
+ ResourceRequest resource_request(url);
+ resource_request.SetRequestContext(WebURLRequest::kRequestContextInternal);
+ FetchParameters fetch_params(resource_request);
+ RawResource::Fetch(fetch_params, fetcher, nullptr);
+ Platform::Current()->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+ }
+
+ private:
+ Member<MockFetchContext> context_;
+};
+
+TEST_F(ResourceFetcherTest, SameOriginRedirect) {
+ const char kRedirectURL[] = "http://127.0.0.1:8000/redirect.html";
+ const char kFinalURL[] = "http://127.0.0.1:8000/final.html";
+ ScopedMockRedirectRequester requester(Context());
+ requester.RegisterRedirect(kRedirectURL, kFinalURL);
+ requester.RegisterFinalResource(kFinalURL);
+ requester.Request(kRedirectURL);
+
+ EXPECT_EQ(kRedirectResponseOverheadBytes + kTestResourceSize,
+ Context()->GetTransferSize());
+}
+
+TEST_F(ResourceFetcherTest, CrossOriginRedirect) {
+ const char kRedirectURL[] = "http://otherorigin.test/redirect.html";
+ const char kFinalURL[] = "http://127.0.0.1:8000/final.html";
+ ScopedMockRedirectRequester requester(Context());
+ requester.RegisterRedirect(kRedirectURL, kFinalURL);
+ requester.RegisterFinalResource(kFinalURL);
+ requester.Request(kRedirectURL);
+
+ EXPECT_EQ(kTestResourceSize, Context()->GetTransferSize());
+}
+
+TEST_F(ResourceFetcherTest, ComplexCrossOriginRedirect) {
+ const char kRedirectURL1[] = "http://127.0.0.1:8000/redirect1.html";
+ const char kRedirectURL2[] = "http://otherorigin.test/redirect2.html";
+ const char kRedirectURL3[] = "http://127.0.0.1:8000/redirect3.html";
+ const char kFinalURL[] = "http://127.0.0.1:8000/final.html";
+ ScopedMockRedirectRequester requester(Context());
+ requester.RegisterRedirect(kRedirectURL1, kRedirectURL2);
+ requester.RegisterRedirect(kRedirectURL2, kRedirectURL3);
+ requester.RegisterRedirect(kRedirectURL3, kFinalURL);
+ requester.RegisterFinalResource(kFinalURL);
+ requester.Request(kRedirectURL1);
+
+ EXPECT_EQ(kTestResourceSize, Context()->GetTransferSize());
+}
+
+TEST_F(ResourceFetcherTest, SynchronousRequest) {
+ KURL url("http://127.0.0.1:8000/foo.png");
+ RegisterMockedURLLoad(url);
+
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+ ResourceRequest resource_request(url);
+ resource_request.SetRequestContext(WebURLRequest::kRequestContextInternal);
+ FetchParameters fetch_params(resource_request);
+ fetch_params.MakeSynchronous();
+ Resource* resource = RawResource::Fetch(fetch_params, fetcher, nullptr);
+ EXPECT_TRUE(resource->IsLoaded());
+ EXPECT_EQ(ResourceLoadPriority::kHighest,
+ resource->GetResourceRequest().Priority());
+}
+
+TEST_F(ResourceFetcherTest, PingPriority) {
+ KURL url("http://127.0.0.1:8000/foo.png");
+ RegisterMockedURLLoad(url);
+
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+ ResourceRequest resource_request(url);
+ resource_request.SetRequestContext(WebURLRequest::kRequestContextPing);
+ FetchParameters fetch_params(resource_request);
+ Resource* resource = RawResource::Fetch(fetch_params, fetcher, nullptr);
+ EXPECT_EQ(ResourceLoadPriority::kVeryLow,
+ resource->GetResourceRequest().Priority());
+}
+
+TEST_F(ResourceFetcherTest, PreloadResourceTwice) {
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+
+ KURL url("http://127.0.0.1:8000/foo.png");
+ RegisterMockedURLLoad(url);
+
+ FetchParameters fetch_params_original{ResourceRequest(url)};
+ fetch_params_original.SetLinkPreload(true);
+ Resource* resource =
+ MockResource::Fetch(fetch_params_original, fetcher, nullptr);
+ ASSERT_TRUE(resource);
+ EXPECT_TRUE(resource->IsLinkPreload());
+ EXPECT_TRUE(fetcher->ContainsAsPreload(resource));
+ platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+
+ FetchParameters fetch_params{ResourceRequest(url)};
+ fetch_params.SetLinkPreload(true);
+ Resource* new_resource = MockResource::Fetch(fetch_params, fetcher, nullptr);
+ EXPECT_EQ(resource, new_resource);
+ EXPECT_TRUE(fetcher->ContainsAsPreload(resource));
+
+ fetcher->ClearPreloads(ResourceFetcher::kClearAllPreloads);
+ EXPECT_FALSE(fetcher->ContainsAsPreload(resource));
+ EXPECT_FALSE(GetMemoryCache()->Contains(resource));
+ EXPECT_TRUE(resource->IsUnusedPreload());
+}
+
+TEST_F(ResourceFetcherTest, LinkPreloadResourceAndUse) {
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+
+ KURL url("http://127.0.0.1:8000/foo.png");
+ RegisterMockedURLLoad(url);
+
+ // Link preload preload scanner
+ FetchParameters fetch_params_original{ResourceRequest(url)};
+ fetch_params_original.SetLinkPreload(true);
+ Resource* resource =
+ MockResource::Fetch(fetch_params_original, fetcher, nullptr);
+ ASSERT_TRUE(resource);
+ EXPECT_TRUE(resource->IsLinkPreload());
+ platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+
+ // Resource created by preload scanner
+ FetchParameters fetch_params_preload_scanner{ResourceRequest(url)};
+ Resource* preload_scanner_resource =
+ MockResource::Fetch(fetch_params_preload_scanner, fetcher, nullptr);
+ EXPECT_EQ(resource, preload_scanner_resource);
+ EXPECT_FALSE(resource->IsLinkPreload());
+
+ // Resource created by parser
+ FetchParameters fetch_params{ResourceRequest(url)};
+ Persistent<MockResourceClient> client = new MockResourceClient;
+ Resource* new_resource = MockResource::Fetch(fetch_params, fetcher, client);
+ EXPECT_EQ(resource, new_resource);
+ EXPECT_FALSE(resource->IsLinkPreload());
+
+ // DCL reached
+ fetcher->ClearPreloads(ResourceFetcher::kClearSpeculativeMarkupPreloads);
+ EXPECT_TRUE(GetMemoryCache()->Contains(resource));
+ EXPECT_FALSE(resource->IsUnusedPreload());
+}
+
+TEST_F(ResourceFetcherTest, PreloadMatchWithBypassingCache) {
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+ KURL url("http://127.0.0.1:8000/foo.png");
+ RegisterMockedURLLoad(url);
+
+ FetchParameters fetch_params_original{ResourceRequest(url)};
+ fetch_params_original.SetLinkPreload(true);
+ Resource* resource =
+ MockResource::Fetch(fetch_params_original, fetcher, nullptr);
+ ASSERT_TRUE(resource);
+ EXPECT_TRUE(resource->IsLinkPreload());
+ platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+
+ FetchParameters fetch_params_second{ResourceRequest(url)};
+ fetch_params_second.MutableResourceRequest().SetCacheMode(
+ mojom::FetchCacheMode::kBypassCache);
+ Resource* second_resource =
+ MockResource::Fetch(fetch_params_second, fetcher, nullptr);
+ EXPECT_EQ(resource, second_resource);
+ EXPECT_FALSE(resource->IsLinkPreload());
+}
+
+TEST_F(ResourceFetcherTest, CrossFramePreloadMatchIsNotAllowed) {
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+ ResourceFetcher* fetcher2 = ResourceFetcher::Create(Context());
+
+ KURL url("http://127.0.0.1:8000/foo.png");
+ RegisterMockedURLLoad(url);
+
+ FetchParameters fetch_params_original{ResourceRequest(url)};
+ fetch_params_original.SetLinkPreload(true);
+ Resource* resource =
+ MockResource::Fetch(fetch_params_original, fetcher, nullptr);
+ ASSERT_TRUE(resource);
+ EXPECT_TRUE(resource->IsLinkPreload());
+ platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+
+ FetchParameters fetch_params_second{ResourceRequest(url)};
+ fetch_params_second.MutableResourceRequest().SetCacheMode(
+ mojom::FetchCacheMode::kBypassCache);
+ Resource* second_resource =
+ MockResource::Fetch(fetch_params_second, fetcher2, nullptr);
+
+ EXPECT_NE(resource, second_resource);
+ EXPECT_TRUE(resource->IsLinkPreload());
+}
+
+TEST_F(ResourceFetcherTest, RepetitiveLinkPreloadShouldBeMerged) {
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+
+ KURL url("http://127.0.0.1:8000/foo.png");
+ RegisterMockedURLLoad(url);
+
+ FetchParameters fetch_params_for_request{ResourceRequest(url)};
+ FetchParameters fetch_params_for_preload{ResourceRequest(url)};
+ fetch_params_for_preload.SetLinkPreload(true);
+
+ Resource* resource1 =
+ MockResource::Fetch(fetch_params_for_preload, fetcher, nullptr);
+ ASSERT_TRUE(resource1);
+ EXPECT_TRUE(resource1->IsUnusedPreload());
+ EXPECT_TRUE(fetcher->ContainsAsPreload(resource1));
+ platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+
+ // The second preload fetch returns the first preload.
+ Resource* resource2 =
+ MockResource::Fetch(fetch_params_for_preload, fetcher, nullptr);
+ EXPECT_TRUE(fetcher->ContainsAsPreload(resource1));
+ EXPECT_TRUE(resource1->IsUnusedPreload());
+ EXPECT_EQ(resource1, resource2);
+
+ // preload matching
+ Resource* resource3 =
+ MockResource::Fetch(fetch_params_for_request, fetcher, nullptr);
+ EXPECT_EQ(resource1, resource3);
+ EXPECT_FALSE(fetcher->ContainsAsPreload(resource1));
+ EXPECT_FALSE(resource1->IsUnusedPreload());
+}
+
+TEST_F(ResourceFetcherTest, RepetitiveSpeculativePreloadShouldBeMerged) {
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+
+ KURL url("http://127.0.0.1:8000/foo.png");
+ RegisterMockedURLLoad(url);
+
+ FetchParameters fetch_params_for_request{ResourceRequest(url)};
+ FetchParameters fetch_params_for_preload{ResourceRequest(url)};
+ fetch_params_for_preload.SetSpeculativePreloadType(
+ FetchParameters::SpeculativePreloadType::kInDocument);
+
+ Resource* resource1 =
+ MockResource::Fetch(fetch_params_for_preload, fetcher, nullptr);
+ ASSERT_TRUE(resource1);
+ EXPECT_TRUE(resource1->IsUnusedPreload());
+ EXPECT_TRUE(fetcher->ContainsAsPreload(resource1));
+ platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+
+ // The second preload fetch returns the first preload.
+ Resource* resource2 =
+ MockResource::Fetch(fetch_params_for_preload, fetcher, nullptr);
+ EXPECT_TRUE(fetcher->ContainsAsPreload(resource1));
+ EXPECT_TRUE(resource1->IsUnusedPreload());
+ EXPECT_EQ(resource1, resource2);
+
+ // preload matching
+ Resource* resource3 =
+ MockResource::Fetch(fetch_params_for_request, fetcher, nullptr);
+ EXPECT_EQ(resource1, resource3);
+ EXPECT_FALSE(fetcher->ContainsAsPreload(resource1));
+ EXPECT_FALSE(resource1->IsUnusedPreload());
+}
+
+TEST_F(ResourceFetcherTest, SpeculativePreloadShouldBePromotedToLinkePreload) {
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+
+ KURL url("http://127.0.0.1:8000/foo.png");
+ RegisterMockedURLLoad(url);
+
+ FetchParameters fetch_params_for_request{ResourceRequest(url)};
+ FetchParameters fetch_params_for_speculative_preload{ResourceRequest(url)};
+ fetch_params_for_speculative_preload.SetSpeculativePreloadType(
+ FetchParameters::SpeculativePreloadType::kInDocument);
+ FetchParameters fetch_params_for_link_preload{ResourceRequest(url)};
+ fetch_params_for_link_preload.SetLinkPreload(true);
+
+ Resource* resource1 = MockResource::Fetch(
+ fetch_params_for_speculative_preload, fetcher, nullptr);
+ ASSERT_TRUE(resource1);
+ EXPECT_TRUE(resource1->IsUnusedPreload());
+ EXPECT_FALSE(resource1->IsLinkPreload());
+ EXPECT_TRUE(fetcher->ContainsAsPreload(resource1));
+ platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+
+ // The second preload fetch returns the first preload.
+ Resource* resource2 =
+ MockResource::Fetch(fetch_params_for_link_preload, fetcher, nullptr);
+ EXPECT_TRUE(fetcher->ContainsAsPreload(resource1));
+ EXPECT_TRUE(resource1->IsUnusedPreload());
+ EXPECT_TRUE(resource1->IsLinkPreload());
+ EXPECT_EQ(resource1, resource2);
+
+ // preload matching
+ Resource* resource3 =
+ MockResource::Fetch(fetch_params_for_request, fetcher, nullptr);
+ EXPECT_EQ(resource1, resource3);
+ EXPECT_FALSE(fetcher->ContainsAsPreload(resource1));
+ EXPECT_FALSE(resource1->IsUnusedPreload());
+ EXPECT_FALSE(resource1->IsLinkPreload());
+}
+
+TEST_F(ResourceFetcherTest, Revalidate304) {
+ scoped_refptr<const SecurityOrigin> source_origin =
+ SecurityOrigin::CreateUnique();
+ Context()->SetSecurityOrigin(source_origin);
+
+ KURL url("http://127.0.0.1:8000/foo.html");
+ Resource* resource = RawResource::CreateForTest(url, Resource::kRaw);
+ AddResourceToMemoryCache(resource, source_origin);
+
+ ResourceResponse response(url);
+ response.SetHTTPStatusCode(304);
+ response.SetHTTPHeaderField("etag", "1234567890");
+ resource->ResponseReceived(response, nullptr);
+ resource->FinishForTest();
+
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+ ResourceRequest resource_request(url);
+ resource_request.SetRequestContext(WebURLRequest::kRequestContextInternal);
+ FetchParameters fetch_params(resource_request);
+ platform_->GetURLLoaderMockFactory()->RegisterURL(url, WebURLResponse(), "");
+ Resource* new_resource = RawResource::Fetch(fetch_params, fetcher, nullptr);
+ fetcher->StopFetching();
+
+ EXPECT_NE(resource, new_resource);
+}
+
+TEST_F(ResourceFetcherTest, LinkPreloadResourceMultipleFetchersAndMove) {
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+ ResourceFetcher* fetcher2 = ResourceFetcher::Create(Context());
+
+ KURL url("http://127.0.0.1:8000/foo.png");
+ RegisterMockedURLLoad(url);
+
+ FetchParameters fetch_params_original{ResourceRequest(url)};
+ fetch_params_original.SetLinkPreload(true);
+ Resource* resource =
+ MockResource::Fetch(fetch_params_original, fetcher, nullptr);
+ ASSERT_TRUE(resource);
+ EXPECT_TRUE(resource->IsLinkPreload());
+ EXPECT_EQ(0, fetcher->BlockingRequestCount());
+
+ // Resource created by parser on the second fetcher
+ FetchParameters fetch_params2{ResourceRequest(url)};
+ Persistent<MockResourceClient> client2 = new MockResourceClient;
+ Resource* new_resource2 =
+ MockResource::Fetch(fetch_params2, fetcher2, client2);
+ EXPECT_NE(resource, new_resource2);
+ EXPECT_EQ(0, fetcher2->BlockingRequestCount());
+ platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+}
+
+TEST_F(ResourceFetcherTest, ContentTypeDataURL) {
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+ FetchParameters fetch_params{ResourceRequest("data:text/testmimetype,foo")};
+ Resource* resource = MockResource::Fetch(fetch_params, fetcher, nullptr);
+ ASSERT_TRUE(resource);
+ EXPECT_EQ(ResourceStatus::kCached, resource->GetStatus());
+ EXPECT_EQ("text/testmimetype", resource->GetResponse().MimeType());
+ EXPECT_EQ("text/testmimetype", resource->GetResponse().HttpContentType());
+}
+
+// Request with the Content-ID scheme must not be canceled, even if there is no
+// MHTMLArchive to serve them.
+// Note: Not blocking it is important because there are some embedders of
+// Android WebView that are intercepting Content-ID URLs and serve their own
+// resources. Please see https://crbug.com/739658.
+TEST_F(ResourceFetcherTest, ContentIdURL) {
+ KURL url("cid:0123456789@example.com");
+ ResourceResponse response(url);
+ response.SetHTTPStatusCode(200);
+ RegisterMockedURLLoadWithCustomResponse(url, response);
+
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+
+ // Main resource case.
+ {
+ ResourceRequest resource_request(url);
+ resource_request.SetRequestContext(WebURLRequest::kRequestContextIframe);
+ resource_request.SetFrameType(
+ network::mojom::RequestContextFrameType::kNested);
+ FetchParameters fetch_params(resource_request);
+ RawResource* resource = RawResource::FetchMainResource(
+ fetch_params, fetcher, nullptr, SubstituteData());
+ ASSERT_NE(nullptr, resource);
+ EXPECT_FALSE(resource->ErrorOccurred());
+ }
+
+ // Subresource case.
+ {
+ ResourceRequest resource_request(url);
+ resource_request.SetRequestContext(WebURLRequest::kRequestContextVideo);
+ FetchParameters fetch_params(resource_request);
+ RawResource* resource =
+ RawResource::FetchMedia(fetch_params, fetcher, nullptr);
+ ASSERT_NE(nullptr, resource);
+ EXPECT_FALSE(resource->ErrorOccurred());
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_finish_observer.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_finish_observer.h
new file mode 100644
index 00000000000..72cfbda8334
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_finish_observer.h
@@ -0,0 +1,38 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_FINISH_OBSERVER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_FINISH_OBSERVER_H_
+
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+// ResourceFinishObserver is different from ResourceClient in several ways.
+// - NotifyFinished is dispatched asynchronously.
+// - ResourceFinishObservers will be removed from Resource when the load
+// finishes. - This class is not intended to be "subclassed" per each Resource
+// subclass.
+// There is no ImageResourceFinishObserver, for example.
+// ResourceFinishObserver should be quite simple. All notifications must be
+// notified AFTER the loading finishes.
+class PLATFORM_EXPORT ResourceFinishObserver : public GarbageCollectedMixin {
+ public:
+ virtual ~ResourceFinishObserver() = default;
+
+ // Called asynchronously when loading finishes.
+ // Note that this can be dispatched after removing |this| client from a
+ // Resource, because of the asynchronicity.
+ virtual void NotifyFinished() = 0;
+ // Name for debugging
+ virtual String DebugName() const = 0;
+
+ void Trace(blink::Visitor* visitor) override {}
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_FINISH_OBSERVER_H_
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_info.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_info.h
new file mode 100644
index 00000000000..db23bdd7a80
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_info.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2010 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_LOAD_INFO_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_LOAD_INFO_H_
+
+#include "third_party/blink/renderer/platform/network/http_header_map.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+
+namespace blink {
+
+struct ResourceLoadInfo : RefCounted<ResourceLoadInfo> {
+ ResourceLoadInfo() : http_status_code(0) {}
+
+ int http_status_code;
+ String http_status_text;
+ HTTPHeaderMap request_headers;
+ HTTPHeaderMap response_headers;
+ String request_headers_text;
+ String response_headers_text;
+ String npn_negotiated_protocol;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_priority.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_priority.h
new file mode 100644
index 00000000000..e5806041157
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_priority.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2010 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_LOAD_PRIORITY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_LOAD_PRIORITY_H_
+
+#include "third_party/blink/public/platform/web_url_request.h"
+
+namespace blink {
+
+using ResourceLoadPriority = WebURLRequest::Priority;
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.cc
new file mode 100644
index 00000000000..5e0f33f2aa3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.cc
@@ -0,0 +1,722 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h"
+
+#include "base/metrics/field_trial_params.h"
+#include "base/metrics/histogram.h"
+#include "base/strings/string_number_conversions.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/histogram.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_context.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/scheduler/renderer/frame_status.h"
+#include "third_party/blink/renderer/platform/scheduler/util/aggregated_metric_reporter.h"
+
+namespace blink {
+
+namespace {
+
+// Field trial name.
+const char kResourceLoadSchedulerTrial[] = "ResourceLoadScheduler";
+
+// Field trial parameter names.
+// Note: bg_limit is supported on m61+, but bg_sub_limit is only on m63+.
+// If bg_sub_limit param is not found, we should use bg_limit to make the
+// study result statistically correct.
+const char kOutstandingLimitForBackgroundMainFrameName[] = "bg_limit";
+const char kOutstandingLimitForBackgroundSubFrameName[] = "bg_sub_limit";
+
+// Field trial default parameters.
+constexpr size_t kOutstandingLimitForBackgroundFrameDefault = 16u;
+
+// Maximum request count that request count metrics assume.
+constexpr base::HistogramBase::Sample kMaximumReportSize10K = 10000;
+
+// Maximum traffic bytes that traffic metrics assume.
+constexpr base::HistogramBase::Sample kMaximumReportSize1G =
+ 1 * 1000 * 1000 * 1000;
+
+// Bucket count for metrics.
+constexpr int32_t kReportBucketCount = 25;
+
+constexpr char kRendererSideResourceScheduler[] =
+ "RendererSideResourceScheduler";
+
+// These values are copied from resource_scheduler.cc, but the meaning is a bit
+// different because ResourceScheduler counts the running delayable requests
+// while ResourceLoadScheduler counts all the running requests.
+constexpr size_t kTightLimitForRendererSideResourceScheduler = 1u;
+constexpr size_t kLimitForRendererSideResourceScheduler = 10u;
+
+constexpr char kTightLimitForRendererSideResourceSchedulerName[] =
+ "tight_limit";
+constexpr char kLimitForRendererSideResourceSchedulerName[] = "limit";
+
+// Represents a resource load circumstance, e.g. from main frame vs sub-frames,
+// or on throttled state vs on not-throttled state.
+// Used to report histograms. Do not reorder or insert new items.
+enum class ReportCircumstance {
+ kMainframeThrottled,
+ kMainframeNotThrottled,
+ kSubframeThrottled,
+ kSubframeNotThrottled,
+ // Append new items here.
+ kNumOfCircumstances,
+};
+
+base::HistogramBase::Sample ToSample(ReportCircumstance circumstance) {
+ return static_cast<base::HistogramBase::Sample>(circumstance);
+}
+
+uint32_t GetFieldTrialUint32Param(const char* trial_name,
+ const char* parameter_name,
+ uint32_t default_param) {
+ std::map<std::string, std::string> trial_params;
+ bool result = base::GetFieldTrialParams(trial_name, &trial_params);
+ if (!result)
+ return default_param;
+
+ const auto& found = trial_params.find(parameter_name);
+ if (found == trial_params.end())
+ return default_param;
+
+ uint32_t param;
+ if (!base::StringToUint(found->second, &param))
+ return default_param;
+
+ return param;
+}
+
+size_t GetOutstandingThrottledLimit(FetchContext* context) {
+ DCHECK(context);
+
+ if (!RuntimeEnabledFeatures::ResourceLoadSchedulerEnabled())
+ return ResourceLoadScheduler::kOutstandingUnlimited;
+
+ uint32_t main_frame_limit = GetFieldTrialUint32Param(
+ kResourceLoadSchedulerTrial, kOutstandingLimitForBackgroundMainFrameName,
+ kOutstandingLimitForBackgroundFrameDefault);
+ if (context->IsMainFrame())
+ return main_frame_limit;
+
+ // We do not have a fixed default limit for sub-frames, but use the limit for
+ // the main frame so that it works as how previous versions that haven't
+ // consider sub-frames' specific limit work.
+ return GetFieldTrialUint32Param(kResourceLoadSchedulerTrial,
+ kOutstandingLimitForBackgroundSubFrameName,
+ main_frame_limit);
+}
+
+int TakeWholeKilobytes(int64_t& bytes) {
+ int kilobytes = bytes / 1024;
+ bytes %= 1024;
+ return kilobytes;
+}
+
+} // namespace
+
+// A class to gather throttling and traffic information to report histograms.
+class ResourceLoadScheduler::TrafficMonitor {
+ public:
+ explicit TrafficMonitor(FetchContext*);
+ ~TrafficMonitor();
+
+ // Notified when the ThrottlingState is changed.
+ void OnThrottlingStateChanged(FrameScheduler::ThrottlingState);
+
+ // Reports resource request completion.
+ void Report(const ResourceLoadScheduler::TrafficReportHints&);
+
+ // Reports per-frame reports.
+ void ReportAll();
+
+ private:
+ const bool is_main_frame_;
+
+ const WeakPersistent<FetchContext> context_; // NOT OWNED
+
+ FrameScheduler::ThrottlingState current_state_ =
+ FrameScheduler::ThrottlingState::kStopped;
+
+ size_t total_throttled_request_count_ = 0;
+ size_t total_throttled_traffic_bytes_ = 0;
+ size_t total_throttled_decoded_bytes_ = 0;
+ size_t total_not_throttled_request_count_ = 0;
+ size_t total_not_throttled_traffic_bytes_ = 0;
+ size_t total_not_throttled_decoded_bytes_ = 0;
+ size_t throttling_state_change_count_ = 0;
+ bool report_all_is_called_ = false;
+
+ scheduler::AggregatedMetricReporter<scheduler::FrameStatus, int64_t>
+ traffic_kilobytes_per_frame_status_;
+ scheduler::AggregatedMetricReporter<scheduler::FrameStatus, int64_t>
+ decoded_kilobytes_per_frame_status_;
+};
+
+ResourceLoadScheduler::TrafficMonitor::TrafficMonitor(FetchContext* context)
+ : is_main_frame_(context->IsMainFrame()),
+ context_(context),
+ traffic_kilobytes_per_frame_status_(
+ "Blink.ResourceLoadScheduler.TrafficBytes.KBPerFrameStatus",
+ &TakeWholeKilobytes),
+ decoded_kilobytes_per_frame_status_(
+ "Blink.ResourceLoadScheduler.DecodedBytes.KBPerFrameStatus",
+ &TakeWholeKilobytes) {
+ DCHECK(context_);
+}
+
+ResourceLoadScheduler::TrafficMonitor::~TrafficMonitor() {
+ ReportAll();
+}
+
+void ResourceLoadScheduler::TrafficMonitor::OnThrottlingStateChanged(
+ FrameScheduler::ThrottlingState state) {
+ current_state_ = state;
+ throttling_state_change_count_++;
+}
+
+void ResourceLoadScheduler::TrafficMonitor::Report(
+ const ResourceLoadScheduler::TrafficReportHints& hints) {
+ // Currently we only care about stats from frames.
+ if (!IsMainThread())
+ return;
+ if (!hints.IsValid())
+ return;
+
+ DEFINE_STATIC_LOCAL(EnumerationHistogram, request_count_by_circumstance,
+ ("Blink.ResourceLoadScheduler.RequestCount",
+ ToSample(ReportCircumstance::kNumOfCircumstances)));
+
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, main_frame_throttled_traffic_bytes,
+ ("Blink.ResourceLoadScheduler.TrafficBytes.MainframeThrottled", 0,
+ kMaximumReportSize1G, kReportBucketCount));
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, main_frame_not_throttled_traffic_bytes,
+ ("Blink.ResourceLoadScheduler.TrafficBytes.MainframeNotThrottled", 0,
+ kMaximumReportSize1G, kReportBucketCount));
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, sub_frame_throttled_traffic_bytes,
+ ("Blink.ResourceLoadScheduler.TrafficBytes.SubframeThrottled", 0,
+ kMaximumReportSize1G, kReportBucketCount));
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, sub_frame_not_throttled_traffic_bytes,
+ ("Blink.ResourceLoadScheduler.TrafficBytes.SubframeNotThrottled", 0,
+ kMaximumReportSize1G, kReportBucketCount));
+
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, main_frame_throttled_decoded_bytes,
+ ("Blink.ResourceLoadScheduler.DecodedBytes.MainframeThrottled", 0,
+ kMaximumReportSize1G, kReportBucketCount));
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, main_frame_not_throttled_decoded_bytes,
+ ("Blink.ResourceLoadScheduler.DecodedBytes.MainframeNotThrottled", 0,
+ kMaximumReportSize1G, kReportBucketCount));
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, sub_frame_throttled_decoded_bytes,
+ ("Blink.ResourceLoadScheduler.DecodedBytes.SubframeThrottled", 0,
+ kMaximumReportSize1G, kReportBucketCount));
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, sub_frame_not_throttled_decoded_bytes,
+ ("Blink.ResourceLoadScheduler.DecodedBytes.SubframeNotThrottled", 0,
+ kMaximumReportSize1G, kReportBucketCount));
+
+ switch (current_state_) {
+ case FrameScheduler::ThrottlingState::kThrottled:
+ if (is_main_frame_) {
+ request_count_by_circumstance.Count(
+ ToSample(ReportCircumstance::kMainframeThrottled));
+ main_frame_throttled_traffic_bytes.Count(hints.encoded_data_length());
+ main_frame_throttled_decoded_bytes.Count(hints.decoded_body_length());
+ } else {
+ request_count_by_circumstance.Count(
+ ToSample(ReportCircumstance::kSubframeThrottled));
+ sub_frame_throttled_traffic_bytes.Count(hints.encoded_data_length());
+ sub_frame_throttled_decoded_bytes.Count(hints.decoded_body_length());
+ }
+ total_throttled_request_count_++;
+ total_throttled_traffic_bytes_ += hints.encoded_data_length();
+ total_throttled_decoded_bytes_ += hints.decoded_body_length();
+ break;
+ case FrameScheduler::ThrottlingState::kNotThrottled:
+ if (is_main_frame_) {
+ request_count_by_circumstance.Count(
+ ToSample(ReportCircumstance::kMainframeNotThrottled));
+ main_frame_not_throttled_traffic_bytes.Count(
+ hints.encoded_data_length());
+ main_frame_not_throttled_decoded_bytes.Count(
+ hints.decoded_body_length());
+ } else {
+ request_count_by_circumstance.Count(
+ ToSample(ReportCircumstance::kSubframeNotThrottled));
+ sub_frame_not_throttled_traffic_bytes.Count(
+ hints.encoded_data_length());
+ sub_frame_not_throttled_decoded_bytes.Count(
+ hints.decoded_body_length());
+ }
+ total_not_throttled_request_count_++;
+ total_not_throttled_traffic_bytes_ += hints.encoded_data_length();
+ total_not_throttled_decoded_bytes_ += hints.decoded_body_length();
+ break;
+ case FrameScheduler::ThrottlingState::kStopped:
+ break;
+ }
+
+ // Report kilobytes instead of bytes to avoid overflows.
+ size_t encoded_kilobytes = hints.encoded_data_length() / 1024;
+ size_t decoded_kilobytes = hints.decoded_body_length() / 1024;
+
+ if (encoded_kilobytes) {
+ traffic_kilobytes_per_frame_status_.RecordTask(
+ scheduler::GetFrameStatus(context_->GetFrameScheduler()),
+ encoded_kilobytes);
+ }
+ if (decoded_kilobytes) {
+ decoded_kilobytes_per_frame_status_.RecordTask(
+ scheduler::GetFrameStatus(context_->GetFrameScheduler()),
+ decoded_kilobytes);
+ }
+}
+
+void ResourceLoadScheduler::TrafficMonitor::ReportAll() {
+ // Currently we only care about stats from frames.
+ if (!IsMainThread())
+ return;
+
+ // Blink has several cases to create DocumentLoader not for an actual page
+ // load use. I.e., per a XMLHttpRequest in "document" type response.
+ // We just ignore such uninteresting cases in following metrics.
+ if (!total_throttled_request_count_ && !total_not_throttled_request_count_)
+ return;
+
+ if (report_all_is_called_)
+ return;
+ report_all_is_called_ = true;
+
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, main_frame_total_throttled_request_count,
+ ("Blink.ResourceLoadScheduler.TotalRequestCount.MainframeThrottled", 0,
+ kMaximumReportSize10K, kReportBucketCount));
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, main_frame_total_not_throttled_request_count,
+ ("Blink.ResourceLoadScheduler.TotalRequestCount.MainframeNotThrottled", 0,
+ kMaximumReportSize10K, kReportBucketCount));
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, sub_frame_total_throttled_request_count,
+ ("Blink.ResourceLoadScheduler.TotalRequestCount.SubframeThrottled", 0,
+ kMaximumReportSize10K, kReportBucketCount));
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, sub_frame_total_not_throttled_request_count,
+ ("Blink.ResourceLoadScheduler.TotalRequestCount.SubframeNotThrottled", 0,
+ kMaximumReportSize10K, kReportBucketCount));
+
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, main_frame_total_throttled_traffic_bytes,
+ ("Blink.ResourceLoadScheduler.TotalTrafficBytes.MainframeThrottled", 0,
+ kMaximumReportSize1G, kReportBucketCount));
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, main_frame_total_not_throttled_traffic_bytes,
+ ("Blink.ResourceLoadScheduler.TotalTrafficBytes.MainframeNotThrottled", 0,
+ kMaximumReportSize1G, kReportBucketCount));
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, sub_frame_total_throttled_traffic_bytes,
+ ("Blink.ResourceLoadScheduler.TotalTrafficBytes.SubframeThrottled", 0,
+ kMaximumReportSize1G, kReportBucketCount));
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, sub_frame_total_not_throttled_traffic_bytes,
+ ("Blink.ResourceLoadScheduler.TotalTrafficBytes.SubframeNotThrottled", 0,
+ kMaximumReportSize1G, kReportBucketCount));
+
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, main_frame_total_throttled_decoded_bytes,
+ ("Blink.ResourceLoadScheduler.TotalDecodedBytes.MainframeThrottled", 0,
+ kMaximumReportSize1G, kReportBucketCount));
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, main_frame_total_not_throttled_decoded_bytes,
+ ("Blink.ResourceLoadScheduler.TotalDecodedBytes.MainframeNotThrottled", 0,
+ kMaximumReportSize1G, kReportBucketCount));
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, sub_frame_total_throttled_decoded_bytes,
+ ("Blink.ResourceLoadScheduler.TotalDecodedBytes.SubframeThrottled", 0,
+ kMaximumReportSize1G, kReportBucketCount));
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, sub_frame_total_not_throttled_decoded_bytes,
+ ("Blink.ResourceLoadScheduler.TotalDecodedBytes.SubframeNotThrottled", 0,
+ kMaximumReportSize1G, kReportBucketCount));
+
+ DEFINE_STATIC_LOCAL(CustomCountHistogram, throttling_state_change_count,
+ ("Blink.ResourceLoadScheduler.ThrottlingStateChangeCount",
+ 0, 100, kReportBucketCount));
+
+ if (is_main_frame_) {
+ main_frame_total_throttled_request_count.Count(
+ total_throttled_request_count_);
+ main_frame_total_not_throttled_request_count.Count(
+ total_not_throttled_request_count_);
+ main_frame_total_throttled_traffic_bytes.Count(
+ total_throttled_traffic_bytes_);
+ main_frame_total_not_throttled_traffic_bytes.Count(
+ total_not_throttled_traffic_bytes_);
+ main_frame_total_throttled_decoded_bytes.Count(
+ total_throttled_decoded_bytes_);
+ main_frame_total_not_throttled_decoded_bytes.Count(
+ total_not_throttled_decoded_bytes_);
+ } else {
+ sub_frame_total_throttled_request_count.Count(
+ total_throttled_request_count_);
+ sub_frame_total_not_throttled_request_count.Count(
+ total_not_throttled_request_count_);
+ sub_frame_total_throttled_traffic_bytes.Count(
+ total_throttled_traffic_bytes_);
+ sub_frame_total_not_throttled_traffic_bytes.Count(
+ total_not_throttled_traffic_bytes_);
+ sub_frame_total_throttled_decoded_bytes.Count(
+ total_throttled_decoded_bytes_);
+ sub_frame_total_not_throttled_decoded_bytes.Count(
+ total_not_throttled_decoded_bytes_);
+ }
+
+ throttling_state_change_count.Count(throttling_state_change_count_);
+}
+
+constexpr ResourceLoadScheduler::ClientId
+ ResourceLoadScheduler::kInvalidClientId;
+
+ResourceLoadScheduler::ResourceLoadScheduler(FetchContext* context)
+ : outstanding_limit_for_throttled_frame_scheduler_(
+ GetOutstandingThrottledLimit(context)),
+ context_(context) {
+ traffic_monitor_ =
+ std::make_unique<ResourceLoadScheduler::TrafficMonitor>(context_);
+
+ if (!RuntimeEnabledFeatures::ResourceLoadSchedulerEnabled() &&
+ !Platform::Current()->IsRendererSideResourceSchedulerEnabled()) {
+ // Initialize TrafficMonitor's state to be |kNotThrottled| so that it
+ // reports metrics in a reasonable state group.
+ traffic_monitor_->OnThrottlingStateChanged(
+ FrameScheduler::ThrottlingState::kNotThrottled);
+ return;
+ }
+
+ auto* scheduler = context->GetFrameScheduler();
+ if (!scheduler)
+ return;
+
+ if (Platform::Current()->IsRendererSideResourceSchedulerEnabled()) {
+ policy_ = context->InitialLoadThrottlingPolicy();
+ normal_outstanding_limit_ =
+ GetFieldTrialUint32Param(kRendererSideResourceScheduler,
+ kLimitForRendererSideResourceSchedulerName,
+ kLimitForRendererSideResourceScheduler);
+ tight_outstanding_limit_ = GetFieldTrialUint32Param(
+ kRendererSideResourceScheduler,
+ kTightLimitForRendererSideResourceSchedulerName,
+ kTightLimitForRendererSideResourceScheduler);
+ }
+
+ is_enabled_ = true;
+ scheduler_observer_handle_ = scheduler->AddThrottlingObserver(
+ FrameScheduler::ObserverType::kLoader, this);
+}
+
+ResourceLoadScheduler* ResourceLoadScheduler::Create(FetchContext* context) {
+ return new ResourceLoadScheduler(context ? context
+ : &FetchContext::NullInstance());
+}
+
+ResourceLoadScheduler::~ResourceLoadScheduler() = default;
+
+void ResourceLoadScheduler::Trace(blink::Visitor* visitor) {
+ visitor->Trace(pending_request_map_);
+ visitor->Trace(context_);
+}
+
+void ResourceLoadScheduler::LoosenThrottlingPolicy() {
+ switch (policy_) {
+ case ThrottlingPolicy::kTight:
+ break;
+ case ThrottlingPolicy::kNormal:
+ return;
+ }
+ policy_ = ThrottlingPolicy::kNormal;
+ MaybeRun();
+}
+
+void ResourceLoadScheduler::Shutdown() {
+ // Do nothing if the feature is not enabled, or Shutdown() was already called.
+ if (is_shutdown_)
+ return;
+ is_shutdown_ = true;
+
+ if (traffic_monitor_)
+ traffic_monitor_.reset();
+
+ scheduler_observer_handle_.reset();
+}
+
+void ResourceLoadScheduler::Request(ResourceLoadSchedulerClient* client,
+ ThrottleOption option,
+ ResourceLoadPriority priority,
+ int intra_priority,
+ ResourceLoadScheduler::ClientId* id) {
+ *id = GenerateClientId();
+ if (is_shutdown_)
+ return;
+
+ if (!Platform::Current()->IsRendererSideResourceSchedulerEnabled()) {
+ // Prioritization is effectively disabled as we use the constant priority.
+ priority = ResourceLoadPriority::kMedium;
+ intra_priority = 0;
+ }
+
+ if (!is_enabled_ || option == ThrottleOption::kCanNotBeThrottled ||
+ !IsThrottablePriority(priority)) {
+ Run(*id, client, false);
+ return;
+ }
+
+ pending_requests_.emplace(*id, priority, intra_priority);
+ pending_request_map_.insert(
+ *id, new ClientWithPriority(client, priority, intra_priority));
+ MaybeRun();
+}
+
+void ResourceLoadScheduler::SetPriority(ClientId client_id,
+ ResourceLoadPriority priority,
+ int intra_priority) {
+ if (!Platform::Current()->IsRendererSideResourceSchedulerEnabled())
+ return;
+
+ auto client_it = pending_request_map_.find(client_id);
+ if (client_it == pending_request_map_.end())
+ return;
+
+ auto it = pending_requests_.find(ClientIdWithPriority(
+ client_id, client_it->value->priority, client_it->value->intra_priority));
+
+ DCHECK(it != pending_requests_.end());
+ pending_requests_.erase(it);
+
+ client_it->value->priority = priority;
+ client_it->value->intra_priority = intra_priority;
+
+ pending_requests_.emplace(client_id, priority, intra_priority);
+ MaybeRun();
+}
+
+bool ResourceLoadScheduler::Release(
+ ResourceLoadScheduler::ClientId id,
+ ResourceLoadScheduler::ReleaseOption option,
+ const ResourceLoadScheduler::TrafficReportHints& hints) {
+ // Check kInvalidClientId that can not be passed to the HashSet.
+ if (id == kInvalidClientId)
+ return false;
+
+ if (running_requests_.find(id) != running_requests_.end()) {
+ running_requests_.erase(id);
+ running_throttlable_requests_.erase(id);
+
+ if (traffic_monitor_)
+ traffic_monitor_->Report(hints);
+
+ if (option == ReleaseOption::kReleaseAndSchedule)
+ MaybeRun();
+ return true;
+ }
+ auto found = pending_request_map_.find(id);
+ if (found != pending_request_map_.end()) {
+ pending_request_map_.erase(found);
+ // Intentionally does not remove it from |pending_requests_|.
+
+ // Didn't release any running requests, but the outstanding limit might be
+ // changed to allow another request.
+ if (option == ReleaseOption::kReleaseAndSchedule)
+ MaybeRun();
+ return true;
+ }
+ return false;
+}
+
+void ResourceLoadScheduler::SetOutstandingLimitForTesting(size_t tight_limit,
+ size_t normal_limit) {
+ tight_outstanding_limit_ = tight_limit;
+ normal_outstanding_limit_ = normal_limit;
+ MaybeRun();
+}
+
+void ResourceLoadScheduler::OnNetworkQuiet() {
+ DCHECK(IsMainThread());
+
+ // Flush out all traffic reports here for safety.
+ traffic_monitor_->ReportAll();
+
+ if (maximum_running_requests_seen_ == 0)
+ return;
+
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, main_frame_throttled,
+ ("Blink.ResourceLoadScheduler.PeakRequests.MainframeThrottled", 0,
+ kMaximumReportSize10K, kReportBucketCount));
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, main_frame_not_throttled,
+ ("Blink.ResourceLoadScheduler.PeakRequests.MainframeNotThrottled", 0,
+ kMaximumReportSize10K, kReportBucketCount));
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, sub_frame_throttled,
+ ("Blink.ResourceLoadScheduler.PeakRequests.SubframeThrottled", 0,
+ kMaximumReportSize10K, kReportBucketCount));
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, sub_frame_not_throttled,
+ ("Blink.ResourceLoadScheduler.PeakRequests.SubframeNotThrottled", 0,
+ kMaximumReportSize10K, kReportBucketCount));
+
+ switch (throttling_history_) {
+ case ThrottlingHistory::kInitial:
+ case ThrottlingHistory::kNotThrottled:
+ if (context_->IsMainFrame())
+ main_frame_not_throttled.Count(maximum_running_requests_seen_);
+ else
+ sub_frame_not_throttled.Count(maximum_running_requests_seen_);
+ break;
+ case ThrottlingHistory::kThrottled:
+ if (context_->IsMainFrame())
+ main_frame_throttled.Count(maximum_running_requests_seen_);
+ else
+ sub_frame_throttled.Count(maximum_running_requests_seen_);
+ break;
+ case ThrottlingHistory::kPartiallyThrottled:
+ break;
+ case ThrottlingHistory::kStopped:
+ break;
+ }
+}
+
+bool ResourceLoadScheduler::IsThrottablePriority(
+ ResourceLoadPriority priority) const {
+ if (!Platform::Current()->IsRendererSideResourceSchedulerEnabled())
+ return true;
+
+ if (RuntimeEnabledFeatures::ResourceLoadSchedulerEnabled()) {
+ // If this scheduler is throttled by the associated FrameScheduler,
+ // consider every prioritiy as throttlable.
+ const auto state = frame_scheduler_throttling_state_;
+ if (state == FrameScheduler::ThrottlingState::kThrottled ||
+ state == FrameScheduler::ThrottlingState::kStopped) {
+ return true;
+ }
+ }
+
+ return priority < ResourceLoadPriority::kHigh;
+}
+
+void ResourceLoadScheduler::OnThrottlingStateChanged(
+ FrameScheduler::ThrottlingState state) {
+ if (traffic_monitor_)
+ traffic_monitor_->OnThrottlingStateChanged(state);
+
+ frame_scheduler_throttling_state_ = state;
+
+ switch (state) {
+ case FrameScheduler::ThrottlingState::kThrottled:
+ if (throttling_history_ == ThrottlingHistory::kInitial)
+ throttling_history_ = ThrottlingHistory::kThrottled;
+ else if (throttling_history_ == ThrottlingHistory::kNotThrottled)
+ throttling_history_ = ThrottlingHistory::kPartiallyThrottled;
+ break;
+ case FrameScheduler::ThrottlingState::kNotThrottled:
+ if (throttling_history_ == ThrottlingHistory::kInitial)
+ throttling_history_ = ThrottlingHistory::kNotThrottled;
+ else if (throttling_history_ == ThrottlingHistory::kThrottled)
+ throttling_history_ = ThrottlingHistory::kPartiallyThrottled;
+ break;
+ case FrameScheduler::ThrottlingState::kStopped:
+ throttling_history_ = ThrottlingHistory::kStopped;
+ break;
+ }
+ MaybeRun();
+}
+
+ResourceLoadScheduler::ClientId ResourceLoadScheduler::GenerateClientId() {
+ ClientId id = ++current_id_;
+ CHECK_NE(0u, id);
+ return id;
+}
+
+void ResourceLoadScheduler::MaybeRun() {
+ // Requests for keep-alive loaders could be remained in the pending queue,
+ // but ignore them once Shutdown() is called.
+ if (is_shutdown_)
+ return;
+
+ while (!pending_requests_.empty()) {
+ // TODO(yhirano): Consider using a unified value.
+ const auto num_requests =
+ frame_scheduler_throttling_state_ ==
+ FrameScheduler::ThrottlingState::kNotThrottled
+ ? running_throttlable_requests_.size()
+ : running_requests_.size();
+
+ const bool has_enough_running_requets =
+ num_requests >= GetOutstandingLimit();
+
+ if (IsThrottablePriority(pending_requests_.begin()->priority) &&
+ has_enough_running_requets) {
+ break;
+ }
+ if (IsThrottablePriority(pending_requests_.begin()->priority) &&
+ has_enough_running_requets) {
+ break;
+ }
+
+ ClientId id = pending_requests_.begin()->client_id;
+ pending_requests_.erase(pending_requests_.begin());
+ auto found = pending_request_map_.find(id);
+ if (found == pending_request_map_.end())
+ continue; // Already released.
+ ResourceLoadSchedulerClient* client = found->value->client;
+ pending_request_map_.erase(found);
+ Run(id, client, true);
+ }
+}
+
+void ResourceLoadScheduler::Run(ResourceLoadScheduler::ClientId id,
+ ResourceLoadSchedulerClient* client,
+ bool throttlable) {
+ running_requests_.insert(id);
+ if (throttlable)
+ running_throttlable_requests_.insert(id);
+ if (running_requests_.size() > maximum_running_requests_seen_) {
+ maximum_running_requests_seen_ = running_requests_.size();
+ }
+ client->Run();
+}
+
+size_t ResourceLoadScheduler::GetOutstandingLimit() const {
+ size_t limit = kOutstandingUnlimited;
+
+ switch (frame_scheduler_throttling_state_) {
+ case FrameScheduler::ThrottlingState::kThrottled:
+ limit = std::min(limit, outstanding_limit_for_throttled_frame_scheduler_);
+ break;
+ case FrameScheduler::ThrottlingState::kNotThrottled:
+ break;
+ case FrameScheduler::ThrottlingState::kStopped:
+ if (RuntimeEnabledFeatures::ResourceLoadSchedulerEnabled())
+ limit = 0;
+ break;
+ }
+
+ switch (policy_) {
+ case ThrottlingPolicy::kTight:
+ limit = std::min(limit, tight_outstanding_limit_);
+ break;
+ case ThrottlingPolicy::kNormal:
+ limit = std::min(limit, normal_outstanding_limit_);
+ break;
+ }
+ return limit;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h
new file mode 100644
index 00000000000..403276a26e4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h
@@ -0,0 +1,318 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_LOAD_SCHEDULER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_LOAD_SCHEDULER_H_
+
+#include <set>
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/heap/heap_allocator.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
+#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+
+namespace blink {
+
+class FetchContext;
+
+// Client interface to use the throttling/scheduling functionality that
+// ResourceLoadScheduler provides.
+class PLATFORM_EXPORT ResourceLoadSchedulerClient
+ : public GarbageCollectedMixin {
+ public:
+ // Called when the request is granted to run.
+ virtual void Run() = 0;
+
+ void Trace(blink::Visitor* visitor) override {}
+};
+
+// ResourceLoadScheduler provides a unified per-frame infrastructure to schedule
+// loading requests. When Request() is called with a
+// ResourceLoadSchedulerClient |client|, it calls |client|'s Run() method
+// synchronously or asynchronously to notify that |client| can start loading.
+//
+// A ResourceLoadScheduler may initiate a new resource loading in the following
+// cases:
+// - When Request() is called
+// - When LoosenThrottlingPolicy() is called
+// - When SetPriority() is called
+// - When Release() is called with kReleaseAndSchedule
+// - When OnThrottlingStateChanged() is called
+//
+// A ResourceLoadScheduler determines if a request can be throttable or not, and
+// keeps track of pending throttable requests with priority information (i.e.,
+// ResourceLoadPriority accompanied with an integer called "intra-priority").
+// Here are the general principles:
+// - A ResourceLoadScheduler does not throttle requests that cannot be
+// throttable. It will call client's Run() method as soon as possible.
+// - A ResourceLoadScheduler determines whether a request can be throttable by
+// seeing Request()'s ThrottleOption argument and requests' priority
+// information. Requests' priority information can be modified via
+// SetPriority().
+// - A ResourceLoadScheulder won't initiate a new resource loading which can
+// be throttable when there are active resource loading activities more than
+// its internal threshold (i.e., what GetOutstandingLimit() returns)".
+//
+// By default, ResourceLoadScheduler is disabled, which means it doesn't
+// throttle any resource loading requests.
+//
+// Here are running experiments (as of M65):
+// - "ResourceLoadScheduler"
+// - Resource loading requests are not at throttled when the frame is in
+// the foreground tab.
+// - Resource loading requests are throttled when the frame is in a
+// background tab. It has different thresholds for the main frame
+// and sub frames. When the frame has been background for more than five
+// minutes, all throttable resource loading requests are throttled
+// indefinitely (i.e., threshold is zero in such a circumstance).
+// - RendererSideResourceScheduler
+// ResourceLoadScheduler has two modes each of which has its own threshold.
+// - Tight mode (used until the frame sees a <body> element):
+// ResourceLoadScheduler considers a request throttable if its priority
+// is less than |kHigh|.
+// - Normal mode:
+// ResourceLoadScheduler considers a request throttable if its priority
+// is less than |kMedium|.
+class PLATFORM_EXPORT ResourceLoadScheduler final
+ : public GarbageCollectedFinalized<ResourceLoadScheduler>,
+ public FrameScheduler::Observer {
+ WTF_MAKE_NONCOPYABLE(ResourceLoadScheduler);
+
+ public:
+ // An option to use in calling Request(). If kCanNotBeThrottled is specified,
+ // the request should be granted and Run() should be called synchronously.
+ // Otherwise, OnRequestGranted() could be called later when other outstanding
+ // requests are finished.
+ enum class ThrottleOption { kCanBeThrottled, kCanNotBeThrottled };
+
+ // An option to use in calling Release(). If kReleaseOnly is specified,
+ // the specified request should be released, but no other requests should
+ // be scheduled within the call.
+ enum class ReleaseOption { kReleaseOnly, kReleaseAndSchedule };
+
+ // A class to pass traffic report hints on calling Release().
+ class TrafficReportHints {
+ public:
+ // |encoded_data_length| is payload size in bytes sent over the network.
+ // |decoded_body_length| is received resource data size in bytes.
+ TrafficReportHints(int64_t encoded_data_length, int64_t decoded_body_length)
+ : valid_(true),
+ encoded_data_length_(encoded_data_length),
+ decoded_body_length_(decoded_body_length) {}
+
+ // Returns the instance that represents an invalid report, which can be
+ // used when a caller don't want to report traffic, i.e. on a failure.
+ static PLATFORM_EXPORT TrafficReportHints InvalidInstance() {
+ return TrafficReportHints();
+ }
+
+ bool IsValid() const { return valid_; }
+
+ int64_t encoded_data_length() const {
+ DCHECK(valid_);
+ return encoded_data_length_;
+ }
+ int64_t decoded_body_length() const {
+ DCHECK(valid_);
+ return decoded_body_length_;
+ }
+
+ private:
+ // Default constructor makes an invalid instance that won't be recorded.
+ TrafficReportHints() = default;
+
+ bool valid_ = false;
+ int64_t encoded_data_length_ = 0;
+ int64_t decoded_body_length_ = 0;
+ };
+
+ // ResourceLoadScheduler has two policies: |kTight| and |kNormal|. Currently
+ // this is used to support aggressive throttling while the corresponding frame
+ // is in layout-blocking phase. There is only one state transition,
+ // |kTight| => |kNormal|, which is done by |LoosenThrottlingPolicy|.
+ enum class ThrottlingPolicy { kTight, kNormal };
+
+ // Returned on Request(). Caller should need to return it via Release().
+ using ClientId = uint64_t;
+
+ static constexpr ClientId kInvalidClientId = 0u;
+
+ static constexpr size_t kOutstandingUnlimited =
+ std::numeric_limits<size_t>::max();
+
+ static ResourceLoadScheduler* Create(FetchContext* = nullptr);
+ ~ResourceLoadScheduler();
+
+ void Trace(blink::Visitor*);
+
+ // Changes the policy from |kTight| to |kNormal|. This function can be called
+ // multiple times, and does nothing when the scheduler is already working with
+ // the normal policy. This function may initiate a new resource loading.
+ void LoosenThrottlingPolicy();
+
+ // Stops all operations including observing throttling signals.
+ // ResourceLoadSchedulerClient::Run() will not be called once this method is
+ // called. This method can be called multiple times safely.
+ void Shutdown();
+
+ // Makes a request. This may synchronously call
+ // ResourceLoadSchedulerClient::Run(), but it is guaranteed that ClientId is
+ // populated before ResourceLoadSchedulerClient::Run() is called, so that the
+ // caller can call Release() with the assigned ClientId correctly.
+ void Request(ResourceLoadSchedulerClient*,
+ ThrottleOption,
+ ResourceLoadPriority,
+ int intra_priority,
+ ClientId*);
+
+ // Updates the priority information of the given client. This function may
+ // initiate a new resource loading.
+ void SetPriority(ClientId, ResourceLoadPriority, int intra_priority);
+
+ // ResourceLoadSchedulerClient should call this method when the loading is
+ // finished, or canceled. This method can be called in a pre-finalization
+ // step, bug the ReleaseOption must be kReleaseOnly in such a case.
+ // TrafficReportHints is for reporting histograms.
+ // TrafficReportHints::InvalidInstance() can be used to omit reporting.
+ bool Release(ClientId, ReleaseOption, const TrafficReportHints&);
+
+ // Checks if the specified client was already scheduled to call Run(), but
+ // haven't call Release() yet.
+ bool IsRunning(ClientId id) { return running_requests_.Contains(id); }
+
+ // Sets outstanding limit for testing.
+ void SetOutstandingLimitForTesting(size_t limit) {
+ SetOutstandingLimitForTesting(limit, limit);
+ }
+ void SetOutstandingLimitForTesting(size_t tight_limit, size_t normal_limit);
+
+ void OnNetworkQuiet();
+
+ // Returns whether we can throttle a request with the given priority.
+ // This function returns false when RendererSideResourceScheduler is disabled.
+ bool IsThrottablePriority(ResourceLoadPriority) const;
+
+ // FrameScheduler::Observer overrides:
+ void OnThrottlingStateChanged(FrameScheduler::ThrottlingState) override;
+
+ private:
+ class TrafficMonitor;
+
+ class ClientIdWithPriority {
+ public:
+ struct Compare {
+ bool operator()(const ClientIdWithPriority& x,
+ const ClientIdWithPriority& y) const {
+ if (x.priority != y.priority)
+ return x.priority > y.priority;
+ if (x.intra_priority != y.intra_priority)
+ return x.intra_priority > y.intra_priority;
+ return x.client_id < y.client_id;
+ }
+ };
+
+ ClientIdWithPriority(ClientId client_id,
+ WebURLRequest::Priority priority,
+ int intra_priority)
+ : client_id(client_id),
+ priority(priority),
+ intra_priority(intra_priority) {}
+
+ const ClientId client_id;
+ const WebURLRequest::Priority priority;
+ const int intra_priority;
+ };
+
+ struct ClientWithPriority : public GarbageCollected<ClientWithPriority> {
+ ClientWithPriority(ResourceLoadSchedulerClient* client,
+ ResourceLoadPriority priority,
+ int intra_priority)
+ : client(client), priority(priority), intra_priority(intra_priority) {}
+
+ void Trace(blink::Visitor* visitor) { visitor->Trace(client); }
+
+ Member<ResourceLoadSchedulerClient> client;
+ ResourceLoadPriority priority;
+ int intra_priority;
+ };
+
+ ResourceLoadScheduler(FetchContext*);
+
+ // Generates the next ClientId.
+ ClientId GenerateClientId();
+
+ // Picks up one client if there is a budget and route it to run.
+ void MaybeRun();
+
+ // Grants a client to run,
+ void Run(ClientId, ResourceLoadSchedulerClient*, bool throttlable);
+
+ size_t GetOutstandingLimit() const;
+
+ // A flag to indicate an internal running state.
+ // TODO(toyoshim): We may want to use enum once we start to have more states.
+ bool is_shutdown_ = false;
+
+ // A mutable flag to indicate if the throttling and scheduling are enabled.
+ // Can be modified by field trial flags or for testing.
+ bool is_enabled_ = false;
+
+ ThrottlingPolicy policy_ = ThrottlingPolicy::kNormal;
+
+ // ResourceLoadScheduler threshold values for various circumstances. Some
+ // conditions can overlap, and ResourceLoadScheduler chooses the smallest
+ // value in such cases.
+
+ // Used when |policy_| is |kTight|.
+ size_t tight_outstanding_limit_ = kOutstandingUnlimited;
+
+ // Used when |policy_| is |kNormal|.
+ size_t normal_outstanding_limit_ = kOutstandingUnlimited;
+
+ // Used when |frame_scheduler_throttling_state_| is |kThrottled|.
+ const size_t outstanding_limit_for_throttled_frame_scheduler_;
+
+ // The last used ClientId to calculate the next.
+ ClientId current_id_ = kInvalidClientId;
+
+ // Holds clients that were granted and are running.
+ HashSet<ClientId> running_requests_;
+
+ HashSet<ClientId> running_throttlable_requests_;
+
+ // Largest number of running requests seen so far.
+ unsigned maximum_running_requests_seen_ = 0;
+
+ enum class ThrottlingHistory {
+ kInitial,
+ kThrottled,
+ kNotThrottled,
+ kPartiallyThrottled,
+ kStopped,
+ };
+ ThrottlingHistory throttling_history_ = ThrottlingHistory::kInitial;
+ FrameScheduler::ThrottlingState frame_scheduler_throttling_state_ =
+ FrameScheduler::ThrottlingState::kNotThrottled;
+
+ // Holds clients that haven't been granted, and are waiting for a grant.
+ HeapHashMap<ClientId, Member<ClientWithPriority>> pending_request_map_;
+ // We use std::set here because WTF doesn't have its counterpart.
+ std::set<ClientIdWithPriority, ClientIdWithPriority::Compare>
+ pending_requests_;
+
+ // Holds an internal class instance to monitor and report traffic.
+ std::unique_ptr<TrafficMonitor> traffic_monitor_;
+
+ // Holds FetchContext reference to contact FrameScheduler.
+ Member<FetchContext> context_;
+
+ // Handle to throttling observer.
+ std::unique_ptr<FrameScheduler::ThrottlingObserverHandle>
+ scheduler_observer_handle_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler_test.cc
new file mode 100644
index 00000000000..059d772e417
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler_test.cc
@@ -0,0 +1,520 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
+
+namespace blink {
+namespace {
+
+class MockClient final : public GarbageCollectedFinalized<MockClient>,
+ public ResourceLoadSchedulerClient {
+ USING_GARBAGE_COLLECTED_MIXIN(MockClient);
+
+ public:
+ ~MockClient() = default;
+
+ void Run() override {
+ EXPECT_FALSE(was_run_);
+ was_run_ = true;
+ }
+ bool WasRun() { return was_run_; }
+
+ void Trace(blink::Visitor* visitor) override {
+ ResourceLoadSchedulerClient::Trace(visitor);
+ }
+
+ private:
+ bool was_run_ = false;
+};
+
+class ResourceLoadSchedulerTest : public testing::Test {
+ public:
+ using ThrottleOption = ResourceLoadScheduler::ThrottleOption;
+ void SetUp() override {
+ DCHECK(RuntimeEnabledFeatures::ResourceLoadSchedulerEnabled());
+ scheduler_ = ResourceLoadScheduler::Create(
+ MockFetchContext::Create(MockFetchContext::kShouldNotLoadNewResource));
+ Scheduler()->SetOutstandingLimitForTesting(1);
+ }
+ void TearDown() override { Scheduler()->Shutdown(); }
+
+ ResourceLoadScheduler* Scheduler() { return scheduler_; }
+
+ bool Release(ResourceLoadScheduler::ClientId client) {
+ return Scheduler()->Release(
+ client, ResourceLoadScheduler::ReleaseOption::kReleaseOnly,
+ ResourceLoadScheduler::TrafficReportHints::InvalidInstance());
+ }
+ bool ReleaseAndSchedule(ResourceLoadScheduler::ClientId client) {
+ return Scheduler()->Release(
+ client, ResourceLoadScheduler::ReleaseOption::kReleaseAndSchedule,
+ ResourceLoadScheduler::TrafficReportHints::InvalidInstance());
+ }
+
+ private:
+ Persistent<ResourceLoadScheduler> scheduler_;
+};
+
+class RendererSideResourceSchedulerTest : public testing::Test {
+ public:
+ using ThrottleOption = ResourceLoadScheduler::ThrottleOption;
+ class TestingPlatformSupport : public ::blink::TestingPlatformSupport {
+ public:
+ bool IsRendererSideResourceSchedulerEnabled() const override {
+ return true;
+ }
+ };
+
+ void SetUp() override {
+ DCHECK(RuntimeEnabledFeatures::ResourceLoadSchedulerEnabled());
+ scheduler_ = ResourceLoadScheduler::Create(
+ MockFetchContext::Create(MockFetchContext::kShouldNotLoadNewResource));
+ Scheduler()->SetOutstandingLimitForTesting(1);
+ }
+ void TearDown() override { Scheduler()->Shutdown(); }
+
+ ResourceLoadScheduler* Scheduler() { return scheduler_; }
+
+ bool Release(ResourceLoadScheduler::ClientId client) {
+ return Scheduler()->Release(
+ client, ResourceLoadScheduler::ReleaseOption::kReleaseOnly,
+ ResourceLoadScheduler::TrafficReportHints::InvalidInstance());
+ }
+
+ private:
+ ScopedTestingPlatformSupport<TestingPlatformSupport>
+ testing_platform_support_;
+ Persistent<ResourceLoadScheduler> scheduler_;
+};
+
+TEST_F(ResourceLoadSchedulerTest, Bypass) {
+ // A request that disallows throttling should be ran synchronously.
+ MockClient* client1 = new MockClient;
+ ResourceLoadScheduler::ClientId id1 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client1, ThrottleOption::kCanNotBeThrottled,
+ ResourceLoadPriority::kMedium, 0 /* intra_priority */,
+ &id1);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id1);
+ EXPECT_TRUE(client1->WasRun());
+
+ // Another request that disallows throttling also should be ran even it makes
+ // the outstanding number reaches to the limit.
+ MockClient* client2 = new MockClient;
+ ResourceLoadScheduler::ClientId id2 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client2, ThrottleOption::kCanNotBeThrottled,
+ ResourceLoadPriority::kMedium, 0 /* intra_priority */,
+ &id2);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id2);
+ EXPECT_TRUE(client2->WasRun());
+
+ // Call Release() with different options just in case.
+ EXPECT_TRUE(Release(id1));
+ EXPECT_TRUE(ReleaseAndSchedule(id2));
+
+ // Should not succeed to call with the same ID twice.
+ EXPECT_FALSE(Release(id1));
+
+ // Should not succeed to call with the invalid ID or unused ID.
+ EXPECT_FALSE(Release(ResourceLoadScheduler::kInvalidClientId));
+
+ EXPECT_FALSE(Release(static_cast<ResourceLoadScheduler::ClientId>(774)));
+}
+
+TEST_F(ResourceLoadSchedulerTest, Throttled) {
+ // The first request should be ran synchronously.
+ MockClient* client1 = new MockClient;
+ ResourceLoadScheduler::ClientId id1 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client1, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kMedium, 0 /* intra_priority */,
+ &id1);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id1);
+ EXPECT_TRUE(client1->WasRun());
+
+ // Another request should be throttled until the first request calls Release.
+ MockClient* client2 = new MockClient;
+ ResourceLoadScheduler::ClientId id2 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client2, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kMedium, 0 /* intra_priority */,
+ &id2);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id2);
+ EXPECT_FALSE(client2->WasRun());
+
+ // Two more requests.
+ MockClient* client3 = new MockClient;
+ ResourceLoadScheduler::ClientId id3 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client3, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kMedium, 0 /* intra_priority */,
+ &id3);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id3);
+ EXPECT_FALSE(client3->WasRun());
+
+ MockClient* client4 = new MockClient;
+ ResourceLoadScheduler::ClientId id4 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client4, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kMedium, 0 /* intra_priority */,
+ &id4);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id4);
+ EXPECT_FALSE(client4->WasRun());
+
+ // Call Release() to run the second request.
+ EXPECT_TRUE(ReleaseAndSchedule(id1));
+ EXPECT_TRUE(client2->WasRun());
+
+ // Call Release() with kReleaseOnly should not run the third and the fourth
+ // requests.
+ EXPECT_TRUE(Release(id2));
+ EXPECT_FALSE(client3->WasRun());
+ EXPECT_FALSE(client4->WasRun());
+
+ // Should be able to call Release() for a client that hasn't run yet. This
+ // should run another scheduling to run the fourth request.
+ EXPECT_TRUE(ReleaseAndSchedule(id3));
+ EXPECT_TRUE(client4->WasRun());
+}
+
+TEST_F(ResourceLoadSchedulerTest, Unthrottle) {
+ // Push three requests.
+ MockClient* client1 = new MockClient;
+ ResourceLoadScheduler::ClientId id1 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client1, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kMedium, 0 /* intra_priority */,
+ &id1);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id1);
+ EXPECT_TRUE(client1->WasRun());
+
+ MockClient* client2 = new MockClient;
+ ResourceLoadScheduler::ClientId id2 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client2, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kMedium, 0 /* intra_priority */,
+ &id2);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id2);
+ EXPECT_FALSE(client2->WasRun());
+
+ MockClient* client3 = new MockClient;
+ ResourceLoadScheduler::ClientId id3 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client3, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kMedium, 0 /* intra_priority */,
+ &id3);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id3);
+ EXPECT_FALSE(client3->WasRun());
+
+ // Allows to pass all requests.
+ Scheduler()->SetOutstandingLimitForTesting(3);
+ EXPECT_TRUE(client2->WasRun());
+ EXPECT_TRUE(client3->WasRun());
+
+ // Release all.
+ EXPECT_TRUE(Release(id3));
+ EXPECT_TRUE(Release(id2));
+ EXPECT_TRUE(Release(id1));
+}
+
+TEST_F(ResourceLoadSchedulerTest, Stopped) {
+ // Push three requests.
+ MockClient* client1 = new MockClient;
+ ResourceLoadScheduler::ClientId id1 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client1, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kMedium, 0 /* intra_priority */,
+ &id1);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id1);
+ EXPECT_TRUE(client1->WasRun());
+
+ MockClient* client2 = new MockClient;
+ ResourceLoadScheduler::ClientId id2 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client2, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kMedium, 0 /* intra_priority */,
+ &id2);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id2);
+ EXPECT_FALSE(client2->WasRun());
+
+ MockClient* client3 = new MockClient;
+ ResourceLoadScheduler::ClientId id3 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client3, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kMedium, 0 /* intra_priority */,
+ &id3);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id3);
+ EXPECT_FALSE(client3->WasRun());
+
+ // Setting outstanding_limit_ to 0 in ThrottlingState::kStopped, prevents
+ // further requests.
+ Scheduler()->SetOutstandingLimitForTesting(0);
+ EXPECT_FALSE(client2->WasRun());
+ EXPECT_FALSE(client3->WasRun());
+
+ // Calling Release() still does not run the second request.
+ EXPECT_TRUE(ReleaseAndSchedule(id1));
+ EXPECT_FALSE(client2->WasRun());
+ EXPECT_FALSE(client3->WasRun());
+
+ // Release all.
+ EXPECT_TRUE(Release(id3));
+ EXPECT_TRUE(Release(id2));
+}
+
+TEST_F(ResourceLoadSchedulerTest, PriotrityIsNotConsidered) {
+ // Push three requests.
+ MockClient* client1 = new MockClient;
+
+ Scheduler()->SetOutstandingLimitForTesting(0);
+
+ ResourceLoadScheduler::ClientId id1 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client1, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kLowest, 10 /* intra_priority */,
+ &id1);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id1);
+
+ MockClient* client2 = new MockClient;
+ ResourceLoadScheduler::ClientId id2 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client2, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kLow, 1 /* intra_priority */,
+ &id2);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id2);
+
+ MockClient* client3 = new MockClient;
+ ResourceLoadScheduler::ClientId id3 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client3, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kLow, 3 /* intra_priority */,
+ &id3);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id3);
+
+ EXPECT_FALSE(client1->WasRun());
+ EXPECT_FALSE(client2->WasRun());
+ EXPECT_FALSE(client3->WasRun());
+
+ Scheduler()->SetOutstandingLimitForTesting(1);
+
+ EXPECT_TRUE(client1->WasRun());
+ EXPECT_FALSE(client2->WasRun());
+ EXPECT_FALSE(client3->WasRun());
+
+ Scheduler()->SetOutstandingLimitForTesting(2);
+
+ EXPECT_TRUE(client1->WasRun());
+ EXPECT_TRUE(client2->WasRun());
+ EXPECT_FALSE(client3->WasRun());
+
+ // Release all.
+ EXPECT_TRUE(Release(id3));
+ EXPECT_TRUE(Release(id2));
+ EXPECT_TRUE(Release(id1));
+}
+
+TEST_F(RendererSideResourceSchedulerTest, PriorityIsConsidered) {
+ // Push three requests.
+ MockClient* client1 = new MockClient;
+
+ Scheduler()->SetOutstandingLimitForTesting(0);
+
+ ResourceLoadScheduler::ClientId id1 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client1, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kLowest, 10 /* intra_priority */,
+ &id1);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id1);
+
+ MockClient* client2 = new MockClient;
+ ResourceLoadScheduler::ClientId id2 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client2, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kLow, 1 /* intra_priority */,
+ &id2);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id2);
+
+ MockClient* client3 = new MockClient;
+ ResourceLoadScheduler::ClientId id3 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client3, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kLow, 3 /* intra_priority */,
+ &id3);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id3);
+
+ MockClient* client4 = new MockClient;
+ ResourceLoadScheduler::ClientId id4 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client4, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kHigh, 0 /* intra_priority */,
+ &id4);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id4);
+
+ EXPECT_FALSE(client1->WasRun());
+ EXPECT_FALSE(client2->WasRun());
+ EXPECT_FALSE(client3->WasRun());
+ EXPECT_TRUE(client4->WasRun());
+
+ Scheduler()->SetOutstandingLimitForTesting(1);
+
+ EXPECT_FALSE(client1->WasRun());
+ EXPECT_FALSE(client2->WasRun());
+ EXPECT_TRUE(client3->WasRun());
+ EXPECT_TRUE(client4->WasRun());
+
+ Scheduler()->SetOutstandingLimitForTesting(2);
+
+ EXPECT_FALSE(client1->WasRun());
+ EXPECT_TRUE(client2->WasRun());
+ EXPECT_TRUE(client3->WasRun());
+ EXPECT_TRUE(client4->WasRun());
+
+ // Release all.
+ EXPECT_TRUE(Release(id4));
+ EXPECT_TRUE(Release(id3));
+ EXPECT_TRUE(Release(id2));
+ EXPECT_TRUE(Release(id1));
+}
+
+TEST_F(RendererSideResourceSchedulerTest, IsThrottablePriority) {
+ EXPECT_TRUE(
+ Scheduler()->IsThrottablePriority(ResourceLoadPriority::kVeryLow));
+ EXPECT_TRUE(Scheduler()->IsThrottablePriority(ResourceLoadPriority::kLow));
+ EXPECT_TRUE(Scheduler()->IsThrottablePriority(ResourceLoadPriority::kMedium));
+ EXPECT_FALSE(Scheduler()->IsThrottablePriority(ResourceLoadPriority::kHigh));
+ EXPECT_FALSE(
+ Scheduler()->IsThrottablePriority(ResourceLoadPriority::kVeryHigh));
+
+ Scheduler()->LoosenThrottlingPolicy();
+
+ EXPECT_TRUE(
+ Scheduler()->IsThrottablePriority(ResourceLoadPriority::kVeryLow));
+ EXPECT_TRUE(Scheduler()->IsThrottablePriority(ResourceLoadPriority::kLow));
+ EXPECT_TRUE(Scheduler()->IsThrottablePriority(ResourceLoadPriority::kMedium));
+ EXPECT_FALSE(Scheduler()->IsThrottablePriority(ResourceLoadPriority::kHigh));
+ EXPECT_FALSE(
+ Scheduler()->IsThrottablePriority(ResourceLoadPriority::kVeryHigh));
+}
+
+TEST_F(RendererSideResourceSchedulerTest, SetPriority) {
+ // Start with the normal scheduling policy.
+ Scheduler()->LoosenThrottlingPolicy();
+ // Push three requests.
+ MockClient* client1 = new MockClient;
+
+ Scheduler()->SetOutstandingLimitForTesting(0);
+
+ ResourceLoadScheduler::ClientId id1 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client1, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kLowest, 0 /* intra_priority */,
+ &id1);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id1);
+
+ MockClient* client2 = new MockClient;
+ ResourceLoadScheduler::ClientId id2 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client2, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kLow, 5 /* intra_priority */,
+ &id2);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id2);
+
+ MockClient* client3 = new MockClient;
+ ResourceLoadScheduler::ClientId id3 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client3, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kLow, 10 /* intra_priority */,
+ &id3);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id3);
+
+ EXPECT_FALSE(client1->WasRun());
+ EXPECT_FALSE(client2->WasRun());
+ EXPECT_FALSE(client3->WasRun());
+
+ Scheduler()->SetPriority(id1, ResourceLoadPriority::kHigh, 0);
+
+ EXPECT_TRUE(client1->WasRun());
+ EXPECT_FALSE(client2->WasRun());
+ EXPECT_FALSE(client3->WasRun());
+
+ Scheduler()->SetPriority(id3, ResourceLoadPriority::kLow, 2);
+
+ EXPECT_TRUE(client1->WasRun());
+ EXPECT_FALSE(client2->WasRun());
+ EXPECT_FALSE(client3->WasRun());
+
+ Scheduler()->SetOutstandingLimitForTesting(2);
+
+ EXPECT_TRUE(client1->WasRun());
+ EXPECT_TRUE(client2->WasRun());
+ EXPECT_FALSE(client3->WasRun());
+
+ // Release all.
+ EXPECT_TRUE(Release(id3));
+ EXPECT_TRUE(Release(id2));
+ EXPECT_TRUE(Release(id1));
+}
+
+TEST_F(RendererSideResourceSchedulerTest, LoosenThrottlingPolicy) {
+ MockClient* client1 = new MockClient;
+
+ Scheduler()->SetOutstandingLimitForTesting(0, 0);
+
+ ResourceLoadScheduler::ClientId id1 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client1, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kLowest, 0 /* intra_priority */,
+ &id1);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id1);
+
+ MockClient* client2 = new MockClient;
+ ResourceLoadScheduler::ClientId id2 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client2, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kLowest, 0 /* intra_priority */,
+ &id2);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id2);
+
+ MockClient* client3 = new MockClient;
+ ResourceLoadScheduler::ClientId id3 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client3, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kLowest, 0 /* intra_priority */,
+ &id3);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id3);
+
+ MockClient* client4 = new MockClient;
+ ResourceLoadScheduler::ClientId id4 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client4, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kLowest, 0 /* intra_priority */,
+ &id4);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id4);
+
+ Scheduler()->SetPriority(id2, ResourceLoadPriority::kLow, 0);
+ Scheduler()->SetPriority(id3, ResourceLoadPriority::kLow, 0);
+ Scheduler()->SetPriority(id4, ResourceLoadPriority::kMedium, 0);
+
+ // As the policy is |kTight|, |kMedium| is throttled.
+ EXPECT_FALSE(client1->WasRun());
+ EXPECT_FALSE(client2->WasRun());
+ EXPECT_FALSE(client3->WasRun());
+ EXPECT_FALSE(client4->WasRun());
+
+ Scheduler()->SetOutstandingLimitForTesting(0, 2);
+
+ // MockFetchContext's initial scheduling policy is |kTight|, setting the
+ // outstanding limit for the normal mode doesn't take effect.
+ EXPECT_FALSE(client1->WasRun());
+ EXPECT_FALSE(client2->WasRun());
+ EXPECT_FALSE(client3->WasRun());
+ EXPECT_FALSE(client4->WasRun());
+
+ // Now let's tighten the limit again.
+ Scheduler()->SetOutstandingLimitForTesting(0, 0);
+
+ // ...and change the scheduling policy to |kNormal|.
+ Scheduler()->LoosenThrottlingPolicy();
+
+ EXPECT_FALSE(client1->WasRun());
+ EXPECT_FALSE(client2->WasRun());
+ EXPECT_FALSE(client3->WasRun());
+ EXPECT_FALSE(client4->WasRun());
+
+ Scheduler()->SetOutstandingLimitForTesting(0, 2);
+
+ EXPECT_FALSE(client1->WasRun());
+ EXPECT_TRUE(client2->WasRun());
+ EXPECT_FALSE(client3->WasRun());
+ EXPECT_TRUE(client4->WasRun());
+
+ // Release all.
+ EXPECT_TRUE(Release(id4));
+ EXPECT_TRUE(Release(id3));
+ EXPECT_TRUE(Release(id2));
+ EXPECT_TRUE(Release(id1));
+}
+
+} // namespace
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_timing.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_timing.cc
new file mode 100644
index 00000000000..ac8c07f7469
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_timing.cc
@@ -0,0 +1,126 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/loader/fetch/resource_load_timing.h"
+
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+
+namespace blink {
+
+ResourceLoadTiming::ResourceLoadTiming() {}
+
+scoped_refptr<ResourceLoadTiming> ResourceLoadTiming::Create() {
+ return base::AdoptRef(new ResourceLoadTiming);
+}
+
+scoped_refptr<ResourceLoadTiming> ResourceLoadTiming::DeepCopy() {
+ scoped_refptr<ResourceLoadTiming> timing = Create();
+ timing->request_time_ = request_time_;
+ timing->proxy_start_ = proxy_start_;
+ timing->proxy_end_ = proxy_end_;
+ timing->dns_start_ = dns_start_;
+ timing->dns_end_ = dns_end_;
+ timing->connect_start_ = connect_start_;
+ timing->connect_end_ = connect_end_;
+ timing->worker_start_ = worker_start_;
+ timing->worker_ready_ = worker_ready_;
+ timing->send_start_ = send_start_;
+ timing->send_end_ = send_end_;
+ timing->receive_headers_end_ = receive_headers_end_;
+ timing->ssl_start_ = ssl_start_;
+ timing->ssl_end_ = ssl_end_;
+ timing->push_start_ = push_start_;
+ timing->push_end_ = push_end_;
+ return timing;
+}
+
+bool ResourceLoadTiming::operator==(const ResourceLoadTiming& other) const {
+ return request_time_ == other.request_time_ &&
+ proxy_start_ == other.proxy_start_ && proxy_end_ == other.proxy_end_ &&
+ dns_start_ == other.dns_start_ && dns_end_ == other.dns_end_ &&
+ connect_start_ == other.connect_start_ &&
+ connect_end_ == other.connect_end_ &&
+ worker_start_ == other.worker_start_ &&
+ worker_ready_ == other.worker_ready_ &&
+ send_start_ == other.send_start_ && send_end_ == other.send_end_ &&
+ receive_headers_end_ == other.receive_headers_end_ &&
+ ssl_start_ == other.ssl_start_ && ssl_end_ == other.ssl_end_ &&
+ push_start_ == other.push_start_ && push_end_ == other.push_end_;
+}
+
+bool ResourceLoadTiming::operator!=(const ResourceLoadTiming& other) const {
+ return !(*this == other);
+}
+
+void ResourceLoadTiming::SetDnsStart(TimeTicks dns_start) {
+ dns_start_ = dns_start;
+}
+
+void ResourceLoadTiming::SetRequestTime(TimeTicks request_time) {
+ request_time_ = request_time;
+}
+
+void ResourceLoadTiming::SetProxyStart(TimeTicks proxy_start) {
+ proxy_start_ = proxy_start;
+}
+
+void ResourceLoadTiming::SetProxyEnd(TimeTicks proxy_end) {
+ proxy_end_ = proxy_end;
+}
+
+void ResourceLoadTiming::SetDnsEnd(TimeTicks dns_end) {
+ dns_end_ = dns_end;
+}
+
+void ResourceLoadTiming::SetConnectStart(TimeTicks connect_start) {
+ connect_start_ = connect_start;
+}
+
+void ResourceLoadTiming::SetConnectEnd(TimeTicks connect_end) {
+ connect_end_ = connect_end;
+}
+
+void ResourceLoadTiming::SetWorkerStart(TimeTicks worker_start) {
+ worker_start_ = worker_start;
+}
+
+void ResourceLoadTiming::SetWorkerReady(TimeTicks worker_ready) {
+ worker_ready_ = worker_ready;
+}
+
+void ResourceLoadTiming::SetSendStart(TimeTicks send_start) {
+ TRACE_EVENT_MARK_WITH_TIMESTAMP0("blink.user_timing", "requestStart",
+ send_start);
+ send_start_ = send_start;
+}
+
+void ResourceLoadTiming::SetSendEnd(TimeTicks send_end) {
+ send_end_ = send_end;
+}
+
+void ResourceLoadTiming::SetReceiveHeadersEnd(TimeTicks receive_headers_end) {
+ receive_headers_end_ = receive_headers_end;
+}
+
+void ResourceLoadTiming::SetSslStart(TimeTicks ssl_start) {
+ ssl_start_ = ssl_start;
+}
+
+void ResourceLoadTiming::SetSslEnd(TimeTicks ssl_end) {
+ ssl_end_ = ssl_end;
+}
+
+void ResourceLoadTiming::SetPushStart(TimeTicks push_start) {
+ push_start_ = push_start;
+}
+
+void ResourceLoadTiming::SetPushEnd(TimeTicks push_end) {
+ push_end_ = push_end;
+}
+
+double ResourceLoadTiming::CalculateMillisecondDelta(TimeTicks time) const {
+ return time.is_null() ? -1 : (time - request_time_).InMillisecondsF();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_timing.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_timing.h
new file mode 100644
index 00000000000..b5016d9beb8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_timing.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2010 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_LOAD_TIMING_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_LOAD_TIMING_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT ResourceLoadTiming
+ : public RefCounted<ResourceLoadTiming> {
+ public:
+ static scoped_refptr<ResourceLoadTiming> Create();
+
+ scoped_refptr<ResourceLoadTiming> DeepCopy();
+ bool operator==(const ResourceLoadTiming&) const;
+ bool operator!=(const ResourceLoadTiming&) const;
+
+ void SetDnsStart(TimeTicks);
+ void SetRequestTime(TimeTicks);
+ void SetProxyStart(TimeTicks);
+ void SetProxyEnd(TimeTicks);
+ void SetDnsEnd(TimeTicks);
+ void SetConnectStart(TimeTicks);
+ void SetConnectEnd(TimeTicks);
+ void SetWorkerStart(TimeTicks);
+ void SetWorkerReady(TimeTicks);
+ void SetSendStart(TimeTicks);
+ void SetSendEnd(TimeTicks);
+ void SetReceiveHeadersEnd(TimeTicks);
+ void SetSslStart(TimeTicks);
+ void SetSslEnd(TimeTicks);
+ void SetPushStart(TimeTicks);
+ void SetPushEnd(TimeTicks);
+
+ TimeTicks DnsStart() const { return dns_start_; }
+ TimeTicks RequestTime() const { return request_time_; }
+ TimeTicks ProxyStart() const { return proxy_start_; }
+ TimeTicks ProxyEnd() const { return proxy_end_; }
+ TimeTicks DnsEnd() const { return dns_end_; }
+ TimeTicks ConnectStart() const { return connect_start_; }
+ TimeTicks ConnectEnd() const { return connect_end_; }
+ TimeTicks WorkerStart() const { return worker_start_; }
+ TimeTicks WorkerReady() const { return worker_ready_; }
+ TimeTicks SendStart() const { return send_start_; }
+ TimeTicks SendEnd() const { return send_end_; }
+ TimeTicks ReceiveHeadersEnd() const { return receive_headers_end_; }
+ TimeTicks SslStart() const { return ssl_start_; }
+ TimeTicks SslEnd() const { return ssl_end_; }
+ TimeTicks PushStart() const { return push_start_; }
+ TimeTicks PushEnd() const { return push_end_; }
+
+ double CalculateMillisecondDelta(TimeTicks) const;
+
+ private:
+ ResourceLoadTiming();
+
+ // We want to present a unified timeline to Javascript. Using walltime is
+ // problematic, because the clock may skew while resources load. To prevent
+ // that skew, we record a single reference walltime when root document
+ // navigation begins. All other times are recorded using
+ // monotonicallyIncreasingTime(). When a time needs to be presented to
+ // Javascript, we build a pseudo-walltime using the following equation
+ // (m_requestTime as example):
+ // pseudo time = document wall reference +
+ // (m_requestTime - document monotonic reference).
+
+ // All values from monotonicallyIncreasingTime(), in WTF::TimeTicks.
+ TimeTicks request_time_;
+ TimeTicks proxy_start_;
+ TimeTicks proxy_end_;
+ TimeTicks dns_start_;
+ TimeTicks dns_end_;
+ TimeTicks connect_start_;
+ TimeTicks connect_end_;
+ TimeTicks worker_start_;
+ TimeTicks worker_ready_;
+ TimeTicks send_start_;
+ TimeTicks send_end_;
+ TimeTicks receive_headers_end_;
+ TimeTicks ssl_start_;
+ TimeTicks ssl_end_;
+ TimeTicks push_start_;
+ TimeTicks push_end_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
new file mode 100644
index 00000000000..7800884770f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
@@ -0,0 +1,873 @@
+/*
+ * Copyright (C) 2006, 2007, 2010, 2011 Apple Inc. All rights reserved.
+ * (C) 2007 Graham Dennis (graham.dennis@gmail.com)
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/loader/fetch/resource_loader.h"
+
+#include "services/network/public/mojom/fetch_api.mojom-blink.h"
+#include "third_party/blink/public/mojom/blob/blob_registry.mojom-blink.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_cors.h"
+#include "third_party/blink/public/platform/web_data.h"
+#include "third_party/blink/public/platform/web_url_error.h"
+#include "third_party/blink/public/platform/web_url_request.h"
+#include "third_party/blink/public/platform/web_url_response.h"
+#include "third_party/blink/renderer/platform/exported/wrapped_resource_request.h"
+#include "third_party/blink/renderer/platform/exported/wrapped_resource_response.h"
+#include "third_party/blink/renderer/platform/loader/cors/cors.h"
+#include "third_party/blink/renderer/platform/loader/cors/cors_error_string.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_context.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_error.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
+#include "third_party/blink/renderer/platform/network/network_instrumentation.h"
+#include "third_party/blink/renderer/platform/scheduler/child/web_scheduler.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/weborigin/security_violation_reporting_policy.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+ResourceLoader* ResourceLoader::Create(ResourceFetcher* fetcher,
+ ResourceLoadScheduler* scheduler,
+ Resource* resource,
+ uint32_t inflight_keepalive_bytes) {
+ return new ResourceLoader(fetcher, scheduler, resource,
+ inflight_keepalive_bytes);
+}
+
+ResourceLoader::ResourceLoader(ResourceFetcher* fetcher,
+ ResourceLoadScheduler* scheduler,
+ Resource* resource,
+ uint32_t inflight_keepalive_bytes)
+ : scheduler_client_id_(ResourceLoadScheduler::kInvalidClientId),
+ fetcher_(fetcher),
+ scheduler_(scheduler),
+ resource_(resource),
+ inflight_keepalive_bytes_(inflight_keepalive_bytes),
+ is_cache_aware_loading_activated_(false),
+ progress_binding_(this),
+ cancel_timer_(Context().GetLoadingTaskRunner(),
+ this,
+ &ResourceLoader::CancelTimerFired) {
+ DCHECK(resource_);
+ DCHECK(fetcher_);
+
+ resource_->SetLoader(this);
+}
+
+ResourceLoader::~ResourceLoader() = default;
+
+void ResourceLoader::Trace(blink::Visitor* visitor) {
+ visitor->Trace(fetcher_);
+ visitor->Trace(scheduler_);
+ visitor->Trace(resource_);
+ ResourceLoadSchedulerClient::Trace(visitor);
+}
+
+void ResourceLoader::Start() {
+ const ResourceRequest& request = resource_->GetResourceRequest();
+ ActivateCacheAwareLoadingIfNeeded(request);
+ loader_ = Context().CreateURLLoader(request, Context().GetLoadingTaskRunner(),
+ resource_->Options());
+ DCHECK_EQ(ResourceLoadScheduler::kInvalidClientId, scheduler_client_id_);
+ auto throttle_option = ResourceLoadScheduler::ThrottleOption::kCanBeThrottled;
+
+ // Synchronous requests should not work with a throttling. Also, tentatively
+ // disables throttling for fetch requests that could keep on holding an active
+ // connection until data is read by JavaScript.
+ if (resource_->Options().synchronous_policy == kRequestSynchronously ||
+ request.GetRequestContext() == WebURLRequest::kRequestContextFetch) {
+ throttle_option = ResourceLoadScheduler::ThrottleOption::kCanNotBeThrottled;
+ }
+
+ scheduler_->Request(this, throttle_option, request.Priority(),
+ request.IntraPriorityValue(), &scheduler_client_id_);
+}
+
+void ResourceLoader::Run() {
+ StartWith(resource_->GetResourceRequest());
+}
+
+void ResourceLoader::StartWith(const ResourceRequest& request) {
+ DCHECK_NE(ResourceLoadScheduler::kInvalidClientId, scheduler_client_id_);
+ DCHECK(loader_);
+
+ if (resource_->Options().synchronous_policy == kRequestSynchronously &&
+ Context().DefersLoading()) {
+ Cancel();
+ return;
+ }
+
+ is_downloading_to_blob_ = request.DownloadToBlob();
+
+ loader_->SetDefersLoading(Context().DefersLoading());
+
+ if (is_cache_aware_loading_activated_) {
+ // Override cache policy for cache-aware loading. If this request fails, a
+ // reload with original request will be triggered in DidFail().
+ ResourceRequest cache_aware_request(request);
+ cache_aware_request.SetCacheMode(
+ mojom::FetchCacheMode::kUnspecifiedOnlyIfCachedStrict);
+ loader_->LoadAsynchronously(WrappedResourceRequest(cache_aware_request),
+ this);
+ return;
+ }
+
+ if (resource_->Options().synchronous_policy == kRequestSynchronously)
+ RequestSynchronously(request);
+ else
+ loader_->LoadAsynchronously(WrappedResourceRequest(request), this);
+}
+
+void ResourceLoader::Release(
+ ResourceLoadScheduler::ReleaseOption option,
+ const ResourceLoadScheduler::TrafficReportHints& hints) {
+ DCHECK_NE(ResourceLoadScheduler::kInvalidClientId, scheduler_client_id_);
+ bool released = scheduler_->Release(scheduler_client_id_, option, hints);
+ DCHECK(released);
+ scheduler_client_id_ = ResourceLoadScheduler::kInvalidClientId;
+}
+
+void ResourceLoader::Restart(const ResourceRequest& request) {
+ CHECK_EQ(resource_->Options().synchronous_policy, kRequestAsynchronously);
+
+ loader_ = Context().CreateURLLoader(request, Context().GetLoadingTaskRunner(),
+ resource_->Options());
+ StartWith(request);
+}
+
+void ResourceLoader::SetDefersLoading(bool defers) {
+ DCHECK(loader_);
+ loader_->SetDefersLoading(defers);
+ if (defers) {
+ resource_->VirtualTimePauser().UnpauseVirtualTime();
+ } else {
+ resource_->VirtualTimePauser().PauseVirtualTime();
+ }
+}
+
+void ResourceLoader::DidChangePriority(ResourceLoadPriority load_priority,
+ int intra_priority_value) {
+ if (scheduler_->IsRunning(scheduler_client_id_)) {
+ DCHECK(loader_);
+ DCHECK_NE(ResourceLoadScheduler::kInvalidClientId, scheduler_client_id_);
+ loader_->DidChangePriority(
+ static_cast<WebURLRequest::Priority>(load_priority),
+ intra_priority_value);
+ } else {
+ scheduler_->SetPriority(scheduler_client_id_, load_priority,
+ intra_priority_value);
+ }
+}
+
+void ResourceLoader::ScheduleCancel() {
+ if (!cancel_timer_.IsActive())
+ cancel_timer_.StartOneShot(TimeDelta(), FROM_HERE);
+}
+
+void ResourceLoader::CancelTimerFired(TimerBase*) {
+ if (loader_ && !resource_->HasClientsOrObservers())
+ Cancel();
+}
+
+void ResourceLoader::Cancel() {
+ HandleError(
+ ResourceError::CancelledError(resource_->LastResourceRequest().Url()));
+}
+
+void ResourceLoader::CancelForRedirectAccessCheckError(
+ const KURL& new_url,
+ ResourceRequestBlockedReason blocked_reason) {
+ resource_->WillNotFollowRedirect();
+
+ if (loader_) {
+ HandleError(
+ ResourceError::CancelledDueToAccessCheckError(new_url, blocked_reason));
+ }
+}
+
+static bool IsManualRedirectFetchRequest(const ResourceRequest& request) {
+ return request.GetFetchRedirectMode() ==
+ network::mojom::FetchRedirectMode::kManual &&
+ request.GetRequestContext() == WebURLRequest::kRequestContextFetch;
+}
+
+bool ResourceLoader::WillFollowRedirect(
+ const WebURL& new_url,
+ const WebURL& new_site_for_cookies,
+ const WebString& new_referrer,
+ WebReferrerPolicy new_referrer_policy,
+ const WebString& new_method,
+ const WebURLResponse& passed_redirect_response,
+ bool& report_raw_headers) {
+ DCHECK(!passed_redirect_response.IsNull());
+
+ if (is_cache_aware_loading_activated_) {
+ // Fail as cache miss if cached response is a redirect.
+ HandleError(
+ ResourceError::CacheMissError(resource_->LastResourceRequest().Url()));
+ return false;
+ }
+
+ std::unique_ptr<ResourceRequest> new_request =
+ resource_->LastResourceRequest().CreateRedirectRequest(
+ new_url, new_method, new_site_for_cookies, new_referrer,
+ static_cast<ReferrerPolicy>(new_referrer_policy),
+ !passed_redirect_response.WasFetchedViaServiceWorker());
+
+ Resource::Type resource_type = resource_->GetType();
+
+ const ResourceRequest& initial_request = resource_->GetResourceRequest();
+ // The following parameters never change during the lifetime of a request.
+ WebURLRequest::RequestContext request_context =
+ initial_request.GetRequestContext();
+ network::mojom::RequestContextFrameType frame_type =
+ initial_request.GetFrameType();
+ network::mojom::FetchRequestMode fetch_request_mode =
+ initial_request.GetFetchRequestMode();
+ network::mojom::FetchCredentialsMode fetch_credentials_mode =
+ initial_request.GetFetchCredentialsMode();
+
+ const ResourceLoaderOptions& options = resource_->Options();
+
+ const ResourceResponse& redirect_response(
+ passed_redirect_response.ToResourceResponse());
+
+ if (!IsManualRedirectFetchRequest(initial_request)) {
+ bool unused_preload = resource_->IsUnusedPreload();
+
+ // Don't send security violation reports for unused preloads.
+ SecurityViolationReportingPolicy reporting_policy =
+ unused_preload ? SecurityViolationReportingPolicy::kSuppressReporting
+ : SecurityViolationReportingPolicy::kReport;
+
+ // CanRequest() checks only enforced CSP, so check report-only here to
+ // ensure that violations are sent.
+ Context().CheckCSPForRequest(
+ request_context, new_url, options, reporting_policy,
+ ResourceRequest::RedirectStatus::kFollowedRedirect);
+
+ ResourceRequestBlockedReason blocked_reason = Context().CanRequest(
+ resource_type, *new_request, new_url, options, reporting_policy,
+ FetchParameters::kUseDefaultOriginRestrictionForType,
+ ResourceRequest::RedirectStatus::kFollowedRedirect);
+
+ if (Context().IsAdResource(new_url, resource_type,
+ new_request->GetRequestContext())) {
+ new_request->SetIsAdResource();
+ }
+
+ if (blocked_reason != ResourceRequestBlockedReason::kNone) {
+ CancelForRedirectAccessCheckError(new_url, blocked_reason);
+ return false;
+ }
+
+ if (options.cors_handling_by_resource_fetcher ==
+ kEnableCORSHandlingByResourceFetcher &&
+ fetch_request_mode == network::mojom::FetchRequestMode::kCORS) {
+ scoped_refptr<const SecurityOrigin> source_origin = GetSourceOrigin();
+ WebSecurityOrigin source_web_origin(source_origin.get());
+ WrappedResourceRequest new_request_wrapper(*new_request);
+ WTF::Optional<network::mojom::CORSError> cors_error =
+ WebCORS::HandleRedirect(
+ source_web_origin, new_request_wrapper, redirect_response.Url(),
+ redirect_response.HttpStatusCode(),
+ redirect_response.HttpHeaderFields(), fetch_credentials_mode,
+ resource_->MutableOptions());
+ if (cors_error) {
+ resource_->SetCORSStatus(CORSStatus::kFailed);
+
+ if (!unused_preload) {
+ Context().AddErrorConsoleMessage(
+ CORS::GetErrorString(CORS::ErrorParameter::Create(
+ *cors_error, redirect_response.Url(), new_url,
+ redirect_response.HttpStatusCode(),
+ redirect_response.HttpHeaderFields(), *source_origin.get(),
+ resource_->LastResourceRequest().GetRequestContext())),
+ FetchContext::kJSSource);
+ }
+
+ CancelForRedirectAccessCheckError(new_url,
+ ResourceRequestBlockedReason::kOther);
+ return false;
+ }
+
+ source_origin = source_web_origin;
+ }
+ if (resource_type == Resource::kImage &&
+ fetcher_->ShouldDeferImageLoad(new_url)) {
+ CancelForRedirectAccessCheckError(new_url,
+ ResourceRequestBlockedReason::kOther);
+ return false;
+ }
+ }
+
+ bool cross_origin =
+ !SecurityOrigin::AreSameSchemeHostPort(redirect_response.Url(), new_url);
+ fetcher_->RecordResourceTimingOnRedirect(resource_.Get(), redirect_response,
+ cross_origin);
+
+ if (options.cors_handling_by_resource_fetcher ==
+ kEnableCORSHandlingByResourceFetcher &&
+ fetch_request_mode == network::mojom::FetchRequestMode::kCORS) {
+ bool allow_stored_credentials = false;
+ switch (fetch_credentials_mode) {
+ case network::mojom::FetchCredentialsMode::kOmit:
+ break;
+ case network::mojom::FetchCredentialsMode::kSameOrigin:
+ allow_stored_credentials = !options.cors_flag;
+ break;
+ case network::mojom::FetchCredentialsMode::kInclude:
+ allow_stored_credentials = true;
+ break;
+ }
+ new_request->SetAllowStoredCredentials(allow_stored_credentials);
+ }
+
+ // The following two calls may rewrite the new_request.Url() to
+ // something else not for rejecting redirect but for other reasons.
+ // E.g. WebFrameTestClient::WillSendRequest() and
+ // RenderFrameImpl::WillSendRequest(). We should reflect the
+ // rewriting but currently we cannot. So, compare new_request.Url() and
+ // new_url after calling them, and return false to make the redirect fail on
+ // mismatch.
+
+ Context().PrepareRequest(*new_request,
+ FetchContext::RedirectType::kForRedirect);
+ if (Context().GetFrameScheduler()) {
+ WebScopedVirtualTimePauser virtual_time_pauser =
+ Context().GetFrameScheduler()->CreateWebScopedVirtualTimePauser(
+ resource_->Url().GetString(),
+ WebScopedVirtualTimePauser::VirtualTaskDuration::kNonInstant);
+ virtual_time_pauser.PauseVirtualTime();
+ resource_->VirtualTimePauser() = std::move(virtual_time_pauser);
+ }
+ Context().DispatchWillSendRequest(resource_->Identifier(), *new_request,
+ redirect_response, resource_->GetType(),
+ options.initiator_info);
+
+ // First-party cookie logic moved from DocumentLoader in Blink to
+ // net::URLRequest in the browser. Assert that Blink didn't try to change it
+ // to something else.
+ DCHECK(KURL(new_site_for_cookies) == new_request->SiteForCookies());
+
+ // The following parameters never change during the lifetime of a request.
+ DCHECK_EQ(new_request->GetRequestContext(), request_context);
+ DCHECK_EQ(new_request->GetFrameType(), frame_type);
+ DCHECK_EQ(new_request->GetFetchRequestMode(), fetch_request_mode);
+ DCHECK_EQ(new_request->GetFetchCredentialsMode(), fetch_credentials_mode);
+
+ if (new_request->Url() != KURL(new_url)) {
+ CancelForRedirectAccessCheckError(new_request->Url(),
+ ResourceRequestBlockedReason::kOther);
+ return false;
+ }
+
+ if (!resource_->WillFollowRedirect(*new_request, redirect_response)) {
+ CancelForRedirectAccessCheckError(new_request->Url(),
+ ResourceRequestBlockedReason::kOther);
+ return false;
+ }
+
+ report_raw_headers = new_request->ReportRawHeaders();
+
+ return true;
+}
+
+void ResourceLoader::DidReceiveCachedMetadata(const char* data, int length) {
+ resource_->SetSerializedCachedMetadata(data, length);
+}
+
+void ResourceLoader::DidSendData(unsigned long long bytes_sent,
+ unsigned long long total_bytes_to_be_sent) {
+ resource_->DidSendData(bytes_sent, total_bytes_to_be_sent);
+}
+
+FetchContext& ResourceLoader::Context() const {
+ return fetcher_->Context();
+}
+
+scoped_refptr<const SecurityOrigin> ResourceLoader::GetSourceOrigin() const {
+ scoped_refptr<const SecurityOrigin> origin =
+ resource_->Options().security_origin;
+ if (origin)
+ return origin;
+
+ return Context().GetSecurityOrigin();
+}
+
+CORSStatus ResourceLoader::DetermineCORSStatus(const ResourceResponse& response,
+ StringBuilder& error_msg) const {
+ // Service workers handle CORS separately.
+ if (response.WasFetchedViaServiceWorker()) {
+ switch (response.ResponseTypeViaServiceWorker()) {
+ case network::mojom::FetchResponseType::kBasic:
+ case network::mojom::FetchResponseType::kCORS:
+ case network::mojom::FetchResponseType::kDefault:
+ case network::mojom::FetchResponseType::kError:
+ return CORSStatus::kServiceWorkerSuccessful;
+ case network::mojom::FetchResponseType::kOpaque:
+ case network::mojom::FetchResponseType::kOpaqueRedirect:
+ return CORSStatus::kServiceWorkerOpaque;
+ }
+ NOTREACHED();
+ }
+
+ if (resource_->GetType() == Resource::Type::kMainResource)
+ return CORSStatus::kNotApplicable;
+
+ scoped_refptr<const SecurityOrigin> source_origin = GetSourceOrigin();
+ DCHECK(source_origin);
+
+ if (source_origin->CanRequest(response.Url()))
+ return CORSStatus::kSameOrigin;
+
+ // RequestContext, FetchRequestMode and FetchCredentialsMode never change
+ // during the lifetime of a request.
+ const ResourceRequest& initial_request = resource_->GetResourceRequest();
+
+ if (resource_->Options().cors_handling_by_resource_fetcher !=
+ kEnableCORSHandlingByResourceFetcher ||
+ initial_request.GetFetchRequestMode() !=
+ network::mojom::FetchRequestMode::kCORS) {
+ return CORSStatus::kNotApplicable;
+ }
+
+ // Use the original response instead of the 304 response for a successful
+ // revalidation.
+ const ResourceResponse& response_for_access_control =
+ (resource_->IsCacheValidator() && response.HttpStatusCode() == 304)
+ ? resource_->GetResponse()
+ : response;
+
+ base::Optional<network::mojom::CORSError> cors_error = CORS::CheckAccess(
+ response_for_access_control.Url(),
+ response_for_access_control.HttpStatusCode(),
+ response_for_access_control.HttpHeaderFields(),
+ initial_request.GetFetchCredentialsMode(), *source_origin);
+
+ if (!cors_error)
+ return CORSStatus::kSuccessful;
+
+ String resource_type = Resource::ResourceTypeToString(
+ resource_->GetType(), resource_->Options().initiator_info.name);
+ error_msg.Append("Access to ");
+ error_msg.Append(resource_type);
+ error_msg.Append(" at '");
+ error_msg.Append(response.Url().GetString());
+ error_msg.Append("' from origin '");
+ error_msg.Append(source_origin->ToString());
+ error_msg.Append("' has been blocked by CORS policy: ");
+ error_msg.Append(CORS::GetErrorString(CORS::ErrorParameter::Create(
+ *cors_error, initial_request.Url(), KURL(),
+ response_for_access_control.HttpStatusCode(),
+ response_for_access_control.HttpHeaderFields(), *source_origin,
+ initial_request.GetRequestContext())));
+
+ return CORSStatus::kFailed;
+}
+
+void ResourceLoader::DidReceiveResponse(
+ const WebURLResponse& web_url_response,
+ std::unique_ptr<WebDataConsumerHandle> handle) {
+ DCHECK(!web_url_response.IsNull());
+
+ if (Context().IsDetached()) {
+ // If the fetch context is already detached, we don't need further signals,
+ // so let's cancel the request.
+ HandleError(ResourceError::CancelledError(web_url_response.Url()));
+ return;
+ }
+
+ Resource::Type resource_type = resource_->GetType();
+
+ const ResourceRequest& initial_request = resource_->GetResourceRequest();
+ // The following parameters never change during the lifetime of a request.
+ WebURLRequest::RequestContext request_context =
+ initial_request.GetRequestContext();
+ network::mojom::FetchRequestMode fetch_request_mode =
+ initial_request.GetFetchRequestMode();
+
+ const ResourceLoaderOptions& options = resource_->Options();
+
+ const ResourceResponse& response = web_url_response.ToResourceResponse();
+
+ // Later, CORS results should already be in the response we get from the
+ // browser at this point.
+ StringBuilder cors_error_msg;
+ resource_->SetCORSStatus(DetermineCORSStatus(response, cors_error_msg));
+
+ // Perform 'nosniff' checks against the original response instead of the 304
+ // response for a successful revalidation.
+ const ResourceResponse& nosniffed_response =
+ (resource_->IsCacheValidator() && response.HttpStatusCode() == 304)
+ ? resource_->GetResponse()
+ : response;
+ ResourceRequestBlockedReason blocked_reason =
+ Context().CheckResponseNosniff(request_context, nosniffed_response);
+ if (blocked_reason != ResourceRequestBlockedReason::kNone) {
+ HandleError(ResourceError::CancelledDueToAccessCheckError(response.Url(),
+ blocked_reason));
+ return;
+ }
+
+ if (response.WasFetchedViaServiceWorker()) {
+ if (options.cors_handling_by_resource_fetcher ==
+ kEnableCORSHandlingByResourceFetcher &&
+ fetch_request_mode == network::mojom::FetchRequestMode::kCORS &&
+ response.WasFallbackRequiredByServiceWorker()) {
+ ResourceRequest last_request = resource_->LastResourceRequest();
+ DCHECK(!last_request.GetSkipServiceWorker());
+ // This code handles the case when a controlling service worker doesn't
+ // handle a cross origin request.
+ if (!Context().ShouldLoadNewResource(resource_type)) {
+ // Cancel the request if we should not trigger a reload now.
+ HandleError(ResourceError::CancelledError(response.Url()));
+ return;
+ }
+ last_request.SetSkipServiceWorker(true);
+ Restart(last_request);
+ return;
+ }
+
+ // If the response is fetched via ServiceWorker, the original URL of the
+ // response could be different from the URL of the request. We check the URL
+ // not to load the resources which are forbidden by the page CSP.
+ // https://w3c.github.io/webappsec-csp/#should-block-response
+ const KURL& original_url = response.OriginalURLViaServiceWorker();
+ if (!original_url.IsEmpty()) {
+ // CanRequest() below only checks enforced policies: check report-only
+ // here to ensure violations are sent.
+ Context().CheckCSPForRequest(
+ request_context, original_url, options,
+ SecurityViolationReportingPolicy::kReport,
+ ResourceRequest::RedirectStatus::kFollowedRedirect);
+
+ ResourceRequestBlockedReason blocked_reason = Context().CanRequest(
+ resource_type, initial_request, original_url, options,
+ SecurityViolationReportingPolicy::kReport,
+ FetchParameters::kUseDefaultOriginRestrictionForType,
+ ResourceRequest::RedirectStatus::kFollowedRedirect);
+ if (blocked_reason != ResourceRequestBlockedReason::kNone) {
+ HandleError(ResourceError::CancelledDueToAccessCheckError(
+ original_url, blocked_reason));
+ return;
+ }
+ }
+ } else if (options.cors_handling_by_resource_fetcher ==
+ kEnableCORSHandlingByResourceFetcher &&
+ fetch_request_mode == network::mojom::FetchRequestMode::kCORS) {
+ if (!resource_->IsSameOriginOrCORSSuccessful()) {
+ if (!resource_->IsUnusedPreload())
+ Context().AddErrorConsoleMessage(cors_error_msg.ToString(),
+ FetchContext::kJSSource);
+
+ // Redirects can change the response URL different from one of request.
+ HandleError(ResourceError::CancelledDueToAccessCheckError(
+ response.Url(), ResourceRequestBlockedReason::kOther));
+ return;
+ }
+ }
+
+ // FrameType never changes during the lifetime of a request.
+ Context().DispatchDidReceiveResponse(
+ resource_->Identifier(), response, initial_request.GetFrameType(),
+ request_context, resource_,
+ FetchContext::ResourceResponseType::kNotFromMemoryCache);
+
+ resource_->ResponseReceived(response, std::move(handle));
+ if (!resource_->Loader())
+ return;
+
+ if (response.HttpStatusCode() >= 400 &&
+ !resource_->ShouldIgnoreHTTPStatusCodeErrors())
+ HandleError(ResourceError::CancelledError(response.Url()));
+}
+
+void ResourceLoader::DidReceiveResponse(const WebURLResponse& response) {
+ DidReceiveResponse(response, nullptr);
+}
+
+void ResourceLoader::DidStartLoadingResponseBody(
+ mojo::ScopedDataPipeConsumerHandle body) {
+ DCHECK(is_downloading_to_blob_);
+ DCHECK(!blob_response_started_);
+ blob_response_started_ = true;
+
+ const ResourceResponse& response = resource_->GetResponse();
+ AtomicString mime_type = response.MimeType();
+
+ mojom::blink::ProgressClientAssociatedPtrInfo progress_client_ptr;
+ progress_binding_.Bind(MakeRequest(&progress_client_ptr));
+
+ // Callback is bound to a WeakPersistent, as ResourceLoader is kept alive by
+ // ResourceFetcher as long as we still care about the result of the load.
+ mojom::blink::BlobRegistry* blob_registry = BlobDataHandle::GetBlobRegistry();
+ blob_registry->RegisterFromStream(
+ mime_type.IsNull() ? g_empty_string : mime_type.LowerASCII(), "",
+ std::max(0ll, response.ExpectedContentLength()), std::move(body),
+ std::move(progress_client_ptr),
+ WTF::Bind(&ResourceLoader::FinishedCreatingBlob,
+ WrapWeakPersistent(this)));
+}
+
+void ResourceLoader::DidDownloadData(int length, int encoded_data_length) {
+ Context().DispatchDidDownloadData(resource_->Identifier(), length,
+ encoded_data_length);
+ resource_->DidDownloadData(length);
+}
+
+void ResourceLoader::DidReceiveData(const char* data, int length) {
+ CHECK_GE(length, 0);
+
+ Context().DispatchDidReceiveData(resource_->Identifier(), data, length);
+ resource_->AppendData(data, length);
+}
+
+void ResourceLoader::DidReceiveTransferSizeUpdate(int transfer_size_diff) {
+ DCHECK_GT(transfer_size_diff, 0);
+ Context().DispatchDidReceiveEncodedData(resource_->Identifier(),
+ transfer_size_diff);
+}
+
+void ResourceLoader::DidFinishLoadingFirstPartInMultipart() {
+ network_instrumentation::EndResourceLoad(
+ resource_->Identifier(),
+ network_instrumentation::RequestOutcome::kSuccess);
+
+ fetcher_->HandleLoaderFinish(resource_.Get(), 0,
+ ResourceFetcher::kDidFinishFirstPartInMultipart,
+ 0, false);
+}
+
+void ResourceLoader::DidFinishLoading(double finish_time,
+ int64_t encoded_data_length,
+ int64_t encoded_body_length,
+ int64_t decoded_body_length,
+ bool blocked_cross_site_document) {
+ resource_->SetEncodedDataLength(encoded_data_length);
+ resource_->SetEncodedBodyLength(encoded_body_length);
+ resource_->SetDecodedBodyLength(decoded_body_length);
+
+ if (is_downloading_to_blob_ && !blob_finished_ && blob_response_started_) {
+ load_did_finish_before_blob_ =
+ DeferedFinishLoadingInfo{finish_time, blocked_cross_site_document};
+ return;
+ }
+
+ Release(ResourceLoadScheduler::ReleaseOption::kReleaseAndSchedule,
+ ResourceLoadScheduler::TrafficReportHints(encoded_data_length,
+ decoded_body_length));
+ loader_.reset();
+
+ network_instrumentation::EndResourceLoad(
+ resource_->Identifier(),
+ network_instrumentation::RequestOutcome::kSuccess);
+
+ fetcher_->HandleLoaderFinish(
+ resource_.Get(), finish_time, ResourceFetcher::kDidFinishLoading,
+ inflight_keepalive_bytes_, blocked_cross_site_document);
+}
+
+void ResourceLoader::DidFail(const WebURLError& error,
+ int64_t encoded_data_length,
+ int64_t encoded_body_length,
+ int64_t decoded_body_length) {
+ resource_->SetEncodedDataLength(encoded_data_length);
+ resource_->SetEncodedBodyLength(encoded_body_length);
+ resource_->SetDecodedBodyLength(decoded_body_length);
+ HandleError(error);
+}
+
+void ResourceLoader::HandleError(const ResourceError& error) {
+ if (is_cache_aware_loading_activated_ && error.IsCacheMiss() &&
+ Context().ShouldLoadNewResource(resource_->GetType())) {
+ resource_->WillReloadAfterDiskCacheMiss();
+ is_cache_aware_loading_activated_ = false;
+ Restart(resource_->GetResourceRequest());
+ return;
+ }
+
+ Release(ResourceLoadScheduler::ReleaseOption::kReleaseAndSchedule,
+ ResourceLoadScheduler::TrafficReportHints::InvalidInstance());
+ loader_.reset();
+
+ network_instrumentation::EndResourceLoad(
+ resource_->Identifier(), network_instrumentation::RequestOutcome::kFail);
+
+ fetcher_->HandleLoaderError(resource_.Get(), error,
+ inflight_keepalive_bytes_);
+}
+
+void ResourceLoader::RequestSynchronously(const ResourceRequest& request) {
+ DCHECK(loader_);
+ DCHECK_EQ(request.Priority(), ResourceLoadPriority::kHighest);
+
+ WrappedResourceRequest request_in(request);
+ WebURLResponse response_out;
+ WTF::Optional<WebURLError> error_out;
+ WebData data_out;
+ int64_t encoded_data_length = WebURLLoaderClient::kUnknownEncodedDataLength;
+ int64_t encoded_body_length = 0;
+ base::Optional<int64_t> downloaded_file_length;
+ WebBlobInfo downloaded_blob;
+ loader_->LoadSynchronously(request_in, response_out, error_out, data_out,
+ encoded_data_length, encoded_body_length,
+ downloaded_file_length, downloaded_blob);
+
+ // A message dispatched while synchronously fetching the resource
+ // can bring about the cancellation of this load.
+ if (!loader_)
+ return;
+ int64_t decoded_body_length = data_out.size();
+ if (error_out) {
+ DidFail(*error_out, encoded_data_length, encoded_body_length,
+ decoded_body_length);
+ return;
+ }
+ DidReceiveResponse(response_out);
+ if (!loader_)
+ return;
+ DCHECK_GE(response_out.ToResourceResponse().EncodedBodyLength(), 0);
+
+ // Follow the async case convention of not calling DidReceiveData or
+ // appending data to m_resource if the response body is empty. Copying the
+ // empty buffer is a noop in most cases, but is destructive in the case of
+ // a 304, where it will overwrite the cached data we should be reusing.
+ if (data_out.size()) {
+ data_out.ForEachSegment([this](const char* segment, size_t segment_size,
+ size_t segment_offset) {
+ Context().DispatchDidReceiveData(resource_->Identifier(), segment,
+ segment_size);
+ return true;
+ });
+ resource_->SetResourceBuffer(data_out);
+ }
+
+ if (downloaded_file_length) {
+ DCHECK(request.DownloadToFile());
+ DidDownloadData(*downloaded_file_length, encoded_body_length);
+ }
+ if (request.DownloadToBlob()) {
+ auto blob = downloaded_blob.GetBlobHandle();
+ if (blob) {
+ Context().DispatchDidReceiveData(resource_->Identifier(), nullptr,
+ blob->size());
+ resource_->DidDownloadData(blob->size());
+ }
+ Context().DispatchDidDownloadToBlob(resource_->Identifier(), blob.get());
+ resource_->DidDownloadToBlob(blob);
+ }
+ DidFinishLoading(CurrentTimeTicksInSeconds(), encoded_data_length,
+ encoded_body_length, decoded_body_length, false);
+}
+
+void ResourceLoader::Dispose() {
+ loader_ = nullptr;
+
+ // Release() should be called to release |scheduler_client_id_| beforehand in
+ // DidFinishLoading() or DidFail(), but when a timer to call Cancel() is
+ // ignored due to GC, this case happens. We just release here because we can
+ // not schedule another request safely. See crbug.com/675947.
+ if (scheduler_client_id_ != ResourceLoadScheduler::kInvalidClientId) {
+ Release(ResourceLoadScheduler::ReleaseOption::kReleaseOnly,
+ ResourceLoadScheduler::TrafficReportHints::InvalidInstance());
+ }
+}
+
+void ResourceLoader::ActivateCacheAwareLoadingIfNeeded(
+ const ResourceRequest& request) {
+ DCHECK(!is_cache_aware_loading_activated_);
+
+ if (resource_->Options().cache_aware_loading_enabled !=
+ kIsCacheAwareLoadingEnabled)
+ return;
+
+ // Synchronous requests are not supported.
+ if (resource_->Options().synchronous_policy == kRequestSynchronously)
+ return;
+
+ // Don't activate on Resource revalidation.
+ if (resource_->IsCacheValidator())
+ return;
+
+ // Don't activate if cache policy is explicitly set.
+ if (request.GetCacheMode() != mojom::FetchCacheMode::kDefault)
+ return;
+
+ // Don't activate if the page is controlled by service worker.
+ if (fetcher_->IsControlledByServiceWorker())
+ return;
+
+ is_cache_aware_loading_activated_ = true;
+}
+
+bool ResourceLoader::ShouldBeKeptAliveWhenDetached() const {
+ return resource_->GetResourceRequest().GetKeepalive() &&
+ resource_->GetResponse().IsNull();
+}
+
+scoped_refptr<base::SingleThreadTaskRunner>
+ResourceLoader::GetLoadingTaskRunner() {
+ return Context().GetLoadingTaskRunner();
+}
+
+void ResourceLoader::OnProgress(uint64_t delta) {
+ DCHECK(!blob_finished_);
+
+ if (scheduler_client_id_ == ResourceLoadScheduler::kInvalidClientId)
+ return;
+
+ Context().DispatchDidReceiveData(resource_->Identifier(), nullptr, delta);
+ resource_->DidDownloadData(delta);
+}
+
+void ResourceLoader::FinishedCreatingBlob(
+ const scoped_refptr<BlobDataHandle>& blob) {
+ DCHECK(!blob_finished_);
+
+ if (scheduler_client_id_ == ResourceLoadScheduler::kInvalidClientId)
+ return;
+
+ Context().DispatchDidDownloadToBlob(resource_->Identifier(), blob.get());
+ resource_->DidDownloadToBlob(blob);
+
+ blob_finished_ = true;
+ if (load_did_finish_before_blob_) {
+ const ResourceResponse& response = resource_->GetResponse();
+ DidFinishLoading(load_did_finish_before_blob_->finish_time,
+ response.EncodedDataLength(), response.EncodedBodyLength(),
+ response.DecodedBodyLength(),
+ load_did_finish_before_blob_->blocked_cross_site_document);
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader.h
new file mode 100644
index 00000000000..cc8c44ea9a6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader.h
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2005, 2006, 2011 Apple 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_LOADER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_LOADER_H_
+
+#include <memory>
+#include "base/gtest_prod_util.h"
+#include "mojo/public/cpp/bindings/associated_binding.h"
+#include "third_party/blink/public/mojom/blob/blob_registry.mojom-blink.h"
+#include "third_party/blink/public/platform/web_url_loader.h"
+#include "third_party/blink/public/platform/web_url_loader_client.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+class FetchContext;
+class ResourceError;
+class ResourceFetcher;
+
+// A ResourceLoader is created for each Resource by the ResourceFetcher when it
+// needs to load the specified resource. A ResourceLoader creates a
+// WebURLLoader and loads the resource using it. Any per-load logic should be
+// implemented in this class basically.
+class PLATFORM_EXPORT ResourceLoader final
+ : public GarbageCollectedFinalized<ResourceLoader>,
+ public ResourceLoadSchedulerClient,
+ protected WebURLLoaderClient,
+ protected mojom::blink::ProgressClient {
+ USING_GARBAGE_COLLECTED_MIXIN(ResourceLoader);
+ USING_PRE_FINALIZER(ResourceLoader, Dispose);
+
+ public:
+ static ResourceLoader* Create(ResourceFetcher*,
+ ResourceLoadScheduler*,
+ Resource*,
+ uint32_t inflight_keepalive_bytes = 0);
+ ~ResourceLoader() override;
+ void Trace(blink::Visitor*) override;
+
+ void Start();
+
+ void ScheduleCancel();
+ void Cancel();
+
+ void SetDefersLoading(bool);
+
+ void DidChangePriority(ResourceLoadPriority, int intra_priority_value);
+
+ // Called before start() to activate cache-aware loading if enabled in
+ // |m_resource->options()| and applicable.
+ void ActivateCacheAwareLoadingIfNeeded(const ResourceRequest&);
+
+ bool IsCacheAwareLoadingActivated() const {
+ return is_cache_aware_loading_activated_;
+ }
+
+ ResourceFetcher* Fetcher() { return fetcher_; }
+ bool ShouldBeKeptAliveWhenDetached() const;
+
+ // WebURLLoaderClient
+ //
+ // A succesful load will consist of:
+ // 0+ WillFollowRedirect()
+ // 0+ DidSendData()
+ // 1 DidReceiveResponse()
+ // 0-1 DidReceiveCachedMetadata()
+ // 0+ DidReceiveData() or DidDownloadData(), but never both
+ // 1 DidFinishLoading()
+ // A failed load is indicated by 1 DidFail(), which can occur at any time
+ // before DidFinishLoading(), including synchronous inside one of the other
+ // callbacks via ResourceLoader::cancel()
+ bool WillFollowRedirect(const WebURL& new_url,
+ const WebURL& new_site_for_cookies,
+ const WebString& new_referrer,
+ WebReferrerPolicy new_referrer_policy,
+ const WebString& new_method,
+ const WebURLResponse& passed_redirect_response,
+ bool& report_raw_headers) override;
+ void DidSendData(unsigned long long bytes_sent,
+ unsigned long long total_bytes_to_be_sent) override;
+ void DidReceiveResponse(const WebURLResponse&) override;
+ void DidReceiveResponse(const WebURLResponse&,
+ std::unique_ptr<WebDataConsumerHandle>) override;
+ void DidReceiveCachedMetadata(const char* data, int length) override;
+ void DidReceiveData(const char*, int) override;
+ void DidReceiveTransferSizeUpdate(int transfer_size_diff) override;
+ void DidStartLoadingResponseBody(
+ mojo::ScopedDataPipeConsumerHandle body) override;
+ void DidDownloadData(int, int) override;
+ void DidFinishLoading(double finish_time,
+ int64_t encoded_data_length,
+ int64_t encoded_body_length,
+ int64_t decoded_body_length,
+ bool blocked_cross_site_document) override;
+ void DidFail(const WebURLError&,
+ int64_t encoded_data_length,
+ int64_t encoded_body_length,
+ int64_t decoded_body_length) override;
+
+ void HandleError(const ResourceError&);
+
+ void DidFinishLoadingFirstPartInMultipart();
+
+ // ResourceLoadSchedulerClient.
+ void Run() override;
+
+ scoped_refptr<base::SingleThreadTaskRunner> GetLoadingTaskRunner();
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(ResourceLoaderTest, DetermineCORSStatus);
+
+ friend class SubresourceIntegrityTest;
+
+ // Assumes ResourceFetcher and Resource are non-null.
+ ResourceLoader(ResourceFetcher*,
+ ResourceLoadScheduler*,
+ Resource*,
+ uint32_t inflight_keepalive_bytes);
+
+ void StartWith(const ResourceRequest&);
+
+ void Release(ResourceLoadScheduler::ReleaseOption,
+ const ResourceLoadScheduler::TrafficReportHints&);
+
+ // This method is currently only used for service worker fallback request and
+ // cache-aware loading, other users should be careful not to break
+ // ResourceLoader state.
+ void Restart(const ResourceRequest&);
+
+ FetchContext& Context() const;
+ scoped_refptr<const SecurityOrigin> GetSourceOrigin() const;
+
+ CORSStatus DetermineCORSStatus(const ResourceResponse&, StringBuilder&) const;
+
+ void CancelForRedirectAccessCheckError(const KURL&,
+ ResourceRequestBlockedReason);
+ void RequestSynchronously(const ResourceRequest&);
+ void Dispose();
+
+ void CancelTimerFired(TimerBase*);
+
+ void OnProgress(uint64_t delta) override;
+ void FinishedCreatingBlob(const scoped_refptr<BlobDataHandle>&);
+
+ std::unique_ptr<WebURLLoader> loader_;
+ ResourceLoadScheduler::ClientId scheduler_client_id_;
+ Member<ResourceFetcher> fetcher_;
+ Member<ResourceLoadScheduler> scheduler_;
+ Member<Resource> resource_;
+
+ uint32_t inflight_keepalive_bytes_;
+ bool is_cache_aware_loading_activated_;
+
+ bool is_downloading_to_blob_ = false;
+ mojo::AssociatedBinding<mojom::blink::ProgressClient> progress_binding_;
+ bool blob_finished_ = false;
+ bool blob_response_started_ = false;
+ // If DidFinishLoading is called while downloading to a blob before the blob
+ // is finished, we might have to defer actually handling the event. This
+ // struct is used to store the information needed to refire DidFinishLoading
+ // when the blob is finished too.
+ struct DeferedFinishLoadingInfo {
+ double finish_time;
+ bool blocked_cross_site_document;
+ };
+ Optional<DeferedFinishLoadingInfo> load_did_finish_before_blob_;
+
+ TaskRunnerTimer<ResourceLoader> cancel_timer_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h
new file mode 100644
index 00000000000..0907407eedf
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_LOADER_OPTIONS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_LOADER_OPTIONS_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "services/network/public/mojom/url_loader_factory.mojom-blink.h"
+#include "third_party/blink/renderer/platform/cross_thread_copier.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_initiator_info.h"
+#include "third_party/blink/renderer/platform/loader/fetch/integrity_metadata.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+enum DataBufferingPolicy : uint8_t { kBufferData, kDoNotBufferData };
+
+enum ContentSecurityPolicyDisposition : uint8_t {
+ kCheckContentSecurityPolicy,
+ kDoNotCheckContentSecurityPolicy
+};
+
+enum RequestInitiatorContext : uint8_t {
+ kDocumentContext,
+ kWorkerContext,
+};
+
+enum SynchronousPolicy : uint8_t {
+ kRequestSynchronously,
+ kRequestAsynchronously
+};
+
+// Used by the DocumentThreadableLoader to turn off part of the CORS handling
+// logic in the ResourceFetcher to use its own CORS handling logic.
+enum CORSHandlingByResourceFetcher {
+ kDisableCORSHandlingByResourceFetcher,
+ kEnableCORSHandlingByResourceFetcher,
+};
+
+// Was the request generated from a "parser-inserted" element?
+// https://html.spec.whatwg.org/multipage/scripting.html#parser-inserted
+enum ParserDisposition : uint8_t { kParserInserted, kNotParserInserted };
+
+enum CacheAwareLoadingEnabled : uint8_t {
+ kNotCacheAwareLoadingEnabled,
+ kIsCacheAwareLoadingEnabled
+};
+
+struct ResourceLoaderOptions {
+ USING_FAST_MALLOC(ResourceLoaderOptions);
+
+ public:
+ ResourceLoaderOptions()
+ : data_buffering_policy(kBufferData),
+ content_security_policy_option(kCheckContentSecurityPolicy),
+ request_initiator_context(kDocumentContext),
+ synchronous_policy(kRequestAsynchronously),
+ cors_handling_by_resource_fetcher(kEnableCORSHandlingByResourceFetcher),
+ cors_flag(false),
+ parser_disposition(kParserInserted),
+ cache_aware_loading_enabled(kNotCacheAwareLoadingEnabled) {}
+
+ FetchInitiatorInfo initiator_info;
+
+ // ATTENTION: When adding members, update
+ // CrossThreadResourceLoaderOptionsData, too.
+
+ DataBufferingPolicy data_buffering_policy;
+
+ ContentSecurityPolicyDisposition content_security_policy_option;
+ RequestInitiatorContext request_initiator_context;
+ SynchronousPolicy synchronous_policy;
+
+ // When set to kDisableCORSHandlingByResourceFetcher, the ResourceFetcher
+ // suppresses part of its CORS handling logic.
+ // Used by DocumentThreadableLoader which does CORS handling by itself.
+ CORSHandlingByResourceFetcher cors_handling_by_resource_fetcher;
+
+ // Corresponds to the CORS flag in the Fetch spec.
+ bool cors_flag;
+
+ scoped_refptr<const SecurityOrigin> security_origin;
+ String content_security_policy_nonce;
+ IntegrityMetadataSet integrity_metadata;
+ ParserDisposition parser_disposition;
+ CacheAwareLoadingEnabled cache_aware_loading_enabled;
+
+ // If not null, this URLLoaderFactory should be used to load this resource
+ // rather than whatever factory the system might otherwise use.
+ // Used for example for loading blob: URLs and for prefetch loading.
+ scoped_refptr<
+ base::RefCountedData<network::mojom::blink::URLLoaderFactoryPtr>>
+ url_loader_factory;
+};
+
+// Encode AtomicString (in FetchInitiatorInfo) as String to cross threads.
+struct CrossThreadResourceLoaderOptionsData {
+ DISALLOW_NEW();
+ explicit CrossThreadResourceLoaderOptionsData(
+ const ResourceLoaderOptions& options)
+ : data_buffering_policy(options.data_buffering_policy),
+ content_security_policy_option(options.content_security_policy_option),
+ initiator_info(options.initiator_info),
+ request_initiator_context(options.request_initiator_context),
+ synchronous_policy(options.synchronous_policy),
+ cors_handling_by_resource_fetcher(
+ options.cors_handling_by_resource_fetcher),
+ cors_flag(options.cors_flag),
+ security_origin(options.security_origin
+ ? options.security_origin->IsolatedCopy()
+ : nullptr),
+ content_security_policy_nonce(
+ options.content_security_policy_nonce.IsolatedCopy()),
+ integrity_metadata(options.integrity_metadata),
+ parser_disposition(options.parser_disposition),
+ cache_aware_loading_enabled(options.cache_aware_loading_enabled) {
+ if (options.url_loader_factory) {
+ DCHECK(options.url_loader_factory->data.is_bound());
+ url_loader_factory = base::MakeRefCounted<base::RefCountedData<
+ network::mojom::blink::URLLoaderFactoryPtrInfo>>();
+ options.url_loader_factory->data->Clone(
+ MakeRequest(&url_loader_factory->data));
+ }
+ }
+
+ operator ResourceLoaderOptions() const {
+ ResourceLoaderOptions options;
+ options.data_buffering_policy = data_buffering_policy;
+ options.content_security_policy_option = content_security_policy_option;
+ options.initiator_info = initiator_info;
+ options.request_initiator_context = request_initiator_context;
+ options.synchronous_policy = synchronous_policy;
+ options.cors_handling_by_resource_fetcher =
+ cors_handling_by_resource_fetcher;
+ options.cors_flag = cors_flag;
+ options.security_origin = security_origin;
+ options.content_security_policy_nonce = content_security_policy_nonce;
+ options.integrity_metadata = integrity_metadata;
+ options.parser_disposition = parser_disposition;
+ options.cache_aware_loading_enabled = cache_aware_loading_enabled;
+ if (url_loader_factory) {
+ DCHECK(url_loader_factory->data.is_valid());
+ options.url_loader_factory = base::MakeRefCounted<
+ base::RefCountedData<network::mojom::blink::URLLoaderFactoryPtr>>(
+ network::mojom::blink::URLLoaderFactoryPtr(
+ std::move(url_loader_factory->data)));
+ }
+ return options;
+ }
+
+ DataBufferingPolicy data_buffering_policy;
+ ContentSecurityPolicyDisposition content_security_policy_option;
+ CrossThreadFetchInitiatorInfoData initiator_info;
+ RequestInitiatorContext request_initiator_context;
+ SynchronousPolicy synchronous_policy;
+
+ CORSHandlingByResourceFetcher cors_handling_by_resource_fetcher;
+ bool cors_flag;
+ scoped_refptr<const SecurityOrigin> security_origin;
+
+ String content_security_policy_nonce;
+ IntegrityMetadataSet integrity_metadata;
+ ParserDisposition parser_disposition;
+ CacheAwareLoadingEnabled cache_aware_loading_enabled;
+ scoped_refptr<
+ base::RefCountedData<network::mojom::blink::URLLoaderFactoryPtrInfo>>
+ url_loader_factory;
+};
+
+template <>
+struct CrossThreadCopier<ResourceLoaderOptions> {
+ using Type = CrossThreadResourceLoaderOptionsData;
+ static Type Copy(const ResourceLoaderOptions& options) {
+ return CrossThreadResourceLoaderOptionsData(options);
+ }
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_LOADER_OPTIONS_H_
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_options_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_options_test.cc
new file mode 100644
index 00000000000..9fd9edd4160
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_options_test.cc
@@ -0,0 +1,108 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include <type_traits>
+
+namespace blink {
+
+namespace {
+
+TEST(ResourceLoaderOptionsTest, DeepCopy) {
+ // Check that the fields of ResourceLoaderOptions are enums, except for
+ // initiatorInfo and securityOrigin.
+ static_assert(std::is_enum<DataBufferingPolicy>::value,
+ "DataBufferingPolicy should be an enum");
+ static_assert(std::is_enum<ContentSecurityPolicyDisposition>::value,
+ "ContentSecurityPolicyDisposition should be an enum");
+ static_assert(std::is_enum<RequestInitiatorContext>::value,
+ "RequestInitiatorContext should be an enum");
+ static_assert(std::is_enum<SynchronousPolicy>::value,
+ "SynchronousPolicy should be an enum");
+ static_assert(std::is_enum<CORSHandlingByResourceFetcher>::value,
+ "CORSHandlingByResourceFetcher should be an enum");
+
+ ResourceLoaderOptions original;
+ scoped_refptr<const SecurityOrigin> security_origin =
+ SecurityOrigin::CreateFromString("http://www.google.com");
+ original.security_origin = security_origin;
+ original.initiator_info.name = AtomicString("xmlhttprequest");
+
+ CrossThreadResourceLoaderOptionsData copy_data =
+ CrossThreadCopier<ResourceLoaderOptions>::Copy(original);
+ ResourceLoaderOptions copy = copy_data;
+
+ // Check that contents are correctly copied to |copyData|
+ EXPECT_EQ(original.data_buffering_policy, copy_data.data_buffering_policy);
+ EXPECT_EQ(original.content_security_policy_option,
+ copy_data.content_security_policy_option);
+ EXPECT_EQ(original.initiator_info.name, copy_data.initiator_info.name);
+ EXPECT_EQ(original.initiator_info.position,
+ copy_data.initiator_info.position);
+ EXPECT_EQ(original.initiator_info.start_time,
+ copy_data.initiator_info.start_time);
+ EXPECT_EQ(original.request_initiator_context,
+ copy_data.request_initiator_context);
+ EXPECT_EQ(original.synchronous_policy, copy_data.synchronous_policy);
+ EXPECT_EQ(original.cors_handling_by_resource_fetcher,
+ copy_data.cors_handling_by_resource_fetcher);
+ EXPECT_EQ(original.security_origin->Protocol(),
+ copy_data.security_origin->Protocol());
+ EXPECT_EQ(original.security_origin->Host(),
+ copy_data.security_origin->Host());
+ EXPECT_EQ(original.security_origin->Domain(),
+ copy_data.security_origin->Domain());
+
+ // Check that pointers are different between |original| and |copyData|
+ EXPECT_NE(original.initiator_info.name.Impl(),
+ copy_data.initiator_info.name.Impl());
+ EXPECT_NE(original.security_origin.get(), copy_data.security_origin.get());
+ EXPECT_NE(original.security_origin->Protocol().Impl(),
+ copy_data.security_origin->Protocol().Impl());
+ EXPECT_NE(original.security_origin->Host().Impl(),
+ copy_data.security_origin->Host().Impl());
+ EXPECT_NE(original.security_origin->Domain().Impl(),
+ copy_data.security_origin->Domain().Impl());
+
+ // Check that contents are correctly copied to |copy|
+ EXPECT_EQ(original.data_buffering_policy, copy.data_buffering_policy);
+ EXPECT_EQ(original.content_security_policy_option,
+ copy.content_security_policy_option);
+ EXPECT_EQ(original.initiator_info.name, copy.initiator_info.name);
+ EXPECT_EQ(original.initiator_info.position, copy.initiator_info.position);
+ EXPECT_EQ(original.initiator_info.start_time, copy.initiator_info.start_time);
+ EXPECT_EQ(original.request_initiator_context, copy.request_initiator_context);
+ EXPECT_EQ(original.synchronous_policy, copy.synchronous_policy);
+ EXPECT_EQ(original.cors_handling_by_resource_fetcher,
+ copy.cors_handling_by_resource_fetcher);
+ EXPECT_EQ(original.security_origin->Protocol(),
+ copy.security_origin->Protocol());
+ EXPECT_EQ(original.security_origin->Host(), copy.security_origin->Host());
+ EXPECT_EQ(original.security_origin->Domain(), copy.security_origin->Domain());
+
+ // Check that pointers are different between |original| and |copy|
+ // FIXME: When |original| and |copy| are in different threads, then
+ // EXPECT_NE(original.initiatorInfo.name.impl(),
+ // copy.initiatorInfo.name.impl());
+ // should pass. However, in the unit test here, these two pointers are the
+ // same, because initiatorInfo.name is AtomicString.
+ EXPECT_NE(original.security_origin.get(), copy.security_origin.get());
+ EXPECT_NE(original.security_origin->Protocol().Impl(),
+ copy.security_origin->Protocol().Impl());
+ EXPECT_NE(original.security_origin->Host().Impl(),
+ copy.security_origin->Host().Impl());
+ EXPECT_NE(original.security_origin->Domain().Impl(),
+ copy.security_origin->Domain().Impl());
+
+ // FIXME: The checks for content equality/pointer inequality for
+ // securityOrigin here is not complete (i.e. m_filePath is not checked). A
+ // unit test for SecurityOrigin::isolatedCopy() that covers these checks
+ // should be added.
+}
+
+} // namespace
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_test.cc
new file mode 100644
index 00000000000..0336dc93bc3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_test.cc
@@ -0,0 +1,155 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/loader/fetch/raw_resource.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loader.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
+#include "third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+class ResourceLoaderTest : public testing::Test {
+ DISALLOW_COPY_AND_ASSIGN(ResourceLoaderTest);
+
+ public:
+ ResourceLoaderTest()
+ : foo_url_("https://foo.test"), bar_url_("https://bar.test"){};
+
+ void SetUp() override {
+ context_ =
+ MockFetchContext::Create(MockFetchContext::kShouldLoadNewResource);
+ }
+
+ protected:
+ enum ServiceWorkerMode { kNoSW, kSWOpaque, kSWClear };
+
+ struct TestCase {
+ const KURL& origin;
+ const KURL& target;
+ const KURL* allow_origin_url;
+ const ServiceWorkerMode service_worker;
+ const Resource::Type resource_type;
+ const CORSStatus expectation;
+ };
+
+ Persistent<MockFetchContext> context_;
+
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform_;
+
+ const KURL foo_url_;
+ const KURL bar_url_;
+};
+
+TEST_F(ResourceLoaderTest, DetermineCORSStatus) {
+ TestCase cases[] = {
+ // No CORS status for main resources:
+ {foo_url_, foo_url_, nullptr, kNoSW, Resource::Type::kMainResource,
+ CORSStatus::kNotApplicable},
+
+ // Same origin:
+ {foo_url_, foo_url_, nullptr, kNoSW, Resource::Type::kRaw,
+ CORSStatus::kSameOrigin},
+
+ // Cross origin CORS successful:
+ {foo_url_, bar_url_, &foo_url_, kNoSW, Resource::Type::kRaw,
+ CORSStatus::kSuccessful},
+
+ // Cross origin not in CORS mode:
+ {foo_url_, bar_url_, nullptr, kNoSW, Resource::Type::kRaw,
+ CORSStatus::kNotApplicable},
+
+ // Cross origin CORS failed:
+ {foo_url_, bar_url_, &bar_url_, kNoSW, Resource::Type::kRaw,
+ CORSStatus::kFailed},
+
+ // CORS handled by service worker
+ {foo_url_, foo_url_, nullptr, kSWClear, Resource::Type::kRaw,
+ CORSStatus::kServiceWorkerSuccessful},
+ {foo_url_, foo_url_, &foo_url_, kSWClear, Resource::Type::kRaw,
+ CORSStatus::kServiceWorkerSuccessful},
+ {foo_url_, bar_url_, nullptr, kSWClear, Resource::Type::kRaw,
+ CORSStatus::kServiceWorkerSuccessful},
+ {foo_url_, bar_url_, &foo_url_, kSWClear, Resource::Type::kRaw,
+ CORSStatus::kServiceWorkerSuccessful},
+
+ // Opaque response by service worker
+ {foo_url_, foo_url_, nullptr, kSWOpaque, Resource::Type::kRaw,
+ CORSStatus::kServiceWorkerOpaque},
+ {foo_url_, bar_url_, nullptr, kSWOpaque, Resource::Type::kRaw,
+ CORSStatus::kServiceWorkerOpaque},
+ {foo_url_, bar_url_, &foo_url_, kSWOpaque, Resource::Type::kRaw,
+ CORSStatus::kServiceWorkerOpaque},
+ };
+
+ ResourceLoadScheduler* scheduler = ResourceLoadScheduler::Create();
+
+ for (const auto& test : cases) {
+ SCOPED_TRACE(testing::Message()
+ << "Origin: " << test.origin.GetString()
+ << ", target: " << test.target.GetString()
+ << ", CORS access-control-allow-origin header: "
+ << (test.allow_origin_url ? test.allow_origin_url->GetString()
+ : "-")
+ << ", service worker: "
+ << (test.service_worker == kNoSW
+ ? "no"
+ : (test.service_worker == kSWClear
+ ? "clear response"
+ : "opaque response"))
+ << ", expected CORSStatus == "
+ << static_cast<unsigned>(test.expectation));
+
+ context_->SetSecurityOrigin(SecurityOrigin::Create(test.origin));
+ ResourceFetcher* fetcher = ResourceFetcher::Create(context_);
+
+ Resource* resource =
+ RawResource::CreateForTest(test.target, test.resource_type);
+ ResourceLoader* loader =
+ ResourceLoader::Create(fetcher, scheduler, resource);
+
+ ResourceRequest request;
+ request.SetURL(test.target);
+
+ ResourceResponse response(test.target);
+ response.SetHTTPStatusCode(200);
+
+ if (test.allow_origin_url) {
+ request.SetFetchRequestMode(network::mojom::FetchRequestMode::kCORS);
+ resource->MutableOptions().cors_handling_by_resource_fetcher =
+ kEnableCORSHandlingByResourceFetcher;
+ response.SetHTTPHeaderField(
+ "access-control-allow-origin",
+ SecurityOrigin::Create(*test.allow_origin_url)->ToAtomicString());
+ response.SetHTTPHeaderField("access-control-allow-credentials", "true");
+ }
+
+ resource->SetResourceRequest(request);
+
+ if (test.service_worker != kNoSW) {
+ response.SetWasFetchedViaServiceWorker(true);
+
+ if (test.service_worker == kSWOpaque) {
+ response.SetResponseTypeViaServiceWorker(
+ network::mojom::FetchResponseType::kOpaque);
+ } else {
+ response.SetResponseTypeViaServiceWorker(
+ network::mojom::FetchResponseType::kDefault);
+ }
+ }
+
+ StringBuilder cors_error_msg;
+ CORSStatus cors_status =
+ loader->DetermineCORSStatus(response, cors_error_msg);
+
+ EXPECT_EQ(cors_status, test.expectation);
+ }
+}
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loading_log.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loading_log.h
new file mode 100644
index 00000000000..62435966a4b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loading_log.h
@@ -0,0 +1,21 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_LOADING_LOG_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_LOADING_LOG_H_
+
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+#if DCHECK_IS_ON()
+// We can see logs with |--v=N| or |--vmodule=ResourceLoadingLog=N| where N is a
+// verbose level.
+#define RESOURCE_LOADING_DVLOG(verbose_level) \
+ LAZY_STREAM( \
+ VLOG_STREAM(verbose_level), \
+ ((verbose_level) <= ::logging::GetVlogLevel("ResourceLoadingLog.h")))
+#else
+#define RESOURCE_LOADING_DVLOG(verbose_level) EAT_STREAM_PARAMETERS
+#endif
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_LOADING_LOG_H_
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_priority.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_priority.h
new file mode 100644
index 00000000000..7b4ecebf224
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_priority.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2010 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_PRIORITY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_PRIORITY_H_
+
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+struct ResourcePriority final {
+ STACK_ALLOCATED();
+
+ public:
+ enum VisibilityStatus {
+ kNotVisible,
+ kVisible,
+ };
+
+ ResourcePriority() : ResourcePriority(kNotVisible, 0) {}
+ ResourcePriority(VisibilityStatus status, int intra_value)
+ : visibility(status), intra_priority_value(intra_value) {}
+
+ VisibilityStatus visibility;
+ int intra_priority_value;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_request.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_request.cc
new file mode 100644
index 00000000000..fab47ed61e5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_request.cc
@@ -0,0 +1,469 @@
+/*
+ * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2009, 2012 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
+
+#include <memory>
+
+#include "third_party/blink/public/platform/web_url_request.h"
+#include "third_party/blink/renderer/platform/network/http_names.h"
+#include "third_party/blink/renderer/platform/network/network_utils.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+
+namespace blink {
+
+double ResourceRequest::default_timeout_interval_ = INT_MAX;
+
+ResourceRequest::ResourceRequest() : ResourceRequest(NullURL()) {}
+
+ResourceRequest::ResourceRequest(const String& url_string)
+ : ResourceRequest(KURL(url_string)) {}
+
+ResourceRequest::ResourceRequest(const KURL& url)
+ : url_(url),
+ timeout_interval_(default_timeout_interval_),
+ requestor_origin_(nullptr),
+ http_method_(HTTPNames::GET),
+ allow_stored_credentials_(true),
+ report_upload_progress_(false),
+ report_raw_headers_(false),
+ has_user_gesture_(false),
+ download_to_file_(false),
+ download_to_blob_(false),
+ use_stream_on_response_(false),
+ keepalive_(false),
+ should_reset_app_cache_(false),
+ cache_mode_(mojom::FetchCacheMode::kDefault),
+ skip_service_worker_(false),
+ priority_(ResourceLoadPriority::kLowest),
+ intra_priority_value_(0),
+ requestor_id_(0),
+ plugin_child_id_(-1),
+ app_cache_host_id_(0),
+ previews_state_(WebURLRequest::kPreviewsUnspecified),
+ request_context_(WebURLRequest::kRequestContextUnspecified),
+ frame_type_(network::mojom::RequestContextFrameType::kNone),
+ fetch_request_mode_(network::mojom::FetchRequestMode::kNoCORS),
+ fetch_credentials_mode_(network::mojom::FetchCredentialsMode::kInclude),
+ fetch_redirect_mode_(network::mojom::FetchRedirectMode::kFollow),
+ referrer_policy_(kReferrerPolicyDefault),
+ did_set_http_referrer_(false),
+ check_for_browser_side_navigation_(true),
+ was_discarded_(false),
+ ui_start_time_(0),
+ is_external_request_(false),
+ cors_preflight_policy_(
+ network::mojom::CORSPreflightPolicy::kConsiderPreflight),
+ is_same_document_navigation_(false),
+ input_perf_metric_report_policy_(
+ InputToLoadPerfMetricReportPolicy::kNoReport),
+ redirect_status_(RedirectStatus::kNoRedirect) {}
+
+ResourceRequest::ResourceRequest(CrossThreadResourceRequestData* data)
+ : ResourceRequest(data->url_) {
+ SetTimeoutInterval(data->timeout_interval_);
+ SetSiteForCookies(data->site_for_cookies_);
+ SetRequestorOrigin(data->requestor_origin_);
+ SetHTTPMethod(AtomicString(data->http_method_));
+ SetPriority(data->priority_, data->intra_priority_value_);
+
+ http_header_fields_.Adopt(std::move(data->http_headers_));
+
+ SetHTTPBody(data->http_body_);
+ SetAllowStoredCredentials(data->allow_stored_credentials_);
+ SetReportUploadProgress(data->report_upload_progress_);
+ SetHasUserGesture(data->has_user_gesture_);
+ SetDownloadToFile(data->download_to_file_);
+ SetDownloadToBlob(data->download_to_blob_);
+ SetUseStreamOnResponse(data->use_stream_on_response_);
+ SetKeepalive(data->keepalive_);
+ SetCacheMode(data->cache_mode_);
+ SetSkipServiceWorker(data->skip_service_worker_);
+ SetShouldResetAppCache(data->should_reset_app_cache_);
+ SetRequestorID(data->requestor_id_);
+ SetPluginChildID(data->plugin_child_id_);
+ SetAppCacheHostID(data->app_cache_host_id_);
+ SetPreviewsState(data->previews_state_);
+ SetRequestContext(data->request_context_);
+ SetFrameType(data->frame_type_);
+ SetFetchRequestMode(data->fetch_request_mode_);
+ SetFetchCredentialsMode(data->fetch_credentials_mode_);
+ SetFetchRedirectMode(data->fetch_redirect_mode_);
+ SetFetchIntegrity(data->fetch_integrity_.IsolatedCopy());
+ referrer_policy_ = data->referrer_policy_;
+ did_set_http_referrer_ = data->did_set_http_referrer_;
+ check_for_browser_side_navigation_ = data->check_for_browser_side_navigation_;
+ ui_start_time_ = data->ui_start_time_;
+ is_external_request_ = data->is_external_request_;
+ cors_preflight_policy_ = data->cors_preflight_policy_;
+ input_perf_metric_report_policy_ = data->input_perf_metric_report_policy_;
+ redirect_status_ = data->redirect_status_;
+ suggested_filename_ = data->suggested_filename_;
+ is_ad_resource_ = data->is_ad_resource_;
+}
+
+ResourceRequest::ResourceRequest(const ResourceRequest&) = default;
+
+ResourceRequest& ResourceRequest::operator=(const ResourceRequest&) = default;
+
+std::unique_ptr<ResourceRequest> ResourceRequest::CreateRedirectRequest(
+ const KURL& new_url,
+ const AtomicString& new_method,
+ const KURL& new_site_for_cookies,
+ const String& new_referrer,
+ ReferrerPolicy new_referrer_policy,
+ bool skip_service_worker) const {
+ std::unique_ptr<ResourceRequest> request =
+ std::make_unique<ResourceRequest>(new_url);
+ request->SetHTTPMethod(new_method);
+ request->SetSiteForCookies(new_site_for_cookies);
+ String referrer =
+ new_referrer.IsEmpty() ? Referrer::NoReferrer() : String(new_referrer);
+ request->SetHTTPReferrer(
+ Referrer(referrer, static_cast<ReferrerPolicy>(new_referrer_policy)));
+ request->SetSkipServiceWorker(skip_service_worker);
+ request->SetRedirectStatus(RedirectStatus::kFollowedRedirect);
+
+ // Copy from parameters for |this|.
+ request->SetDownloadToFile(DownloadToFile());
+ request->SetDownloadToBlob(DownloadToBlob());
+ request->SetUseStreamOnResponse(UseStreamOnResponse());
+ request->SetRequestContext(GetRequestContext());
+ request->SetFrameType(GetFrameType());
+ request->SetShouldResetAppCache(ShouldResetAppCache());
+ request->SetFetchRequestMode(GetFetchRequestMode());
+ request->SetFetchCredentialsMode(GetFetchCredentialsMode());
+ request->SetKeepalive(GetKeepalive());
+ request->SetPriority(Priority());
+
+ if (request->HttpMethod() == HttpMethod())
+ request->SetHTTPBody(HttpBody());
+ request->SetCheckForBrowserSideNavigation(CheckForBrowserSideNavigation());
+ request->SetWasDiscarded(WasDiscarded());
+ request->SetCORSPreflightPolicy(CORSPreflightPolicy());
+ if (IsAdResource())
+ request->SetIsAdResource();
+
+ return request;
+}
+
+std::unique_ptr<CrossThreadResourceRequestData> ResourceRequest::CopyData()
+ const {
+ std::unique_ptr<CrossThreadResourceRequestData> data =
+ std::make_unique<CrossThreadResourceRequestData>();
+ data->url_ = Url().Copy();
+ data->timeout_interval_ = TimeoutInterval();
+ data->site_for_cookies_ = SiteForCookies().Copy();
+ data->requestor_origin_ =
+ RequestorOrigin() ? RequestorOrigin()->IsolatedCopy() : nullptr;
+ data->http_method_ = HttpMethod().GetString().IsolatedCopy();
+ data->http_headers_ = HttpHeaderFields().CopyData();
+ data->priority_ = Priority();
+ data->intra_priority_value_ = intra_priority_value_;
+
+ if (http_body_)
+ data->http_body_ = http_body_->DeepCopy();
+ data->allow_stored_credentials_ = allow_stored_credentials_;
+ data->report_upload_progress_ = report_upload_progress_;
+ data->has_user_gesture_ = has_user_gesture_;
+ data->download_to_file_ = download_to_file_;
+ data->download_to_blob_ = download_to_blob_;
+ data->use_stream_on_response_ = use_stream_on_response_;
+ data->keepalive_ = keepalive_;
+ data->cache_mode_ = GetCacheMode();
+ data->skip_service_worker_ = skip_service_worker_;
+ data->should_reset_app_cache_ = should_reset_app_cache_;
+ data->requestor_id_ = requestor_id_;
+ data->plugin_child_id_ = plugin_child_id_;
+ data->app_cache_host_id_ = app_cache_host_id_;
+ data->previews_state_ = previews_state_;
+ data->request_context_ = request_context_;
+ data->frame_type_ = frame_type_;
+ data->fetch_request_mode_ = fetch_request_mode_;
+ data->fetch_credentials_mode_ = fetch_credentials_mode_;
+ data->fetch_redirect_mode_ = fetch_redirect_mode_;
+ data->fetch_integrity_ = fetch_integrity_.IsolatedCopy();
+ data->referrer_policy_ = referrer_policy_;
+ data->did_set_http_referrer_ = did_set_http_referrer_;
+ data->check_for_browser_side_navigation_ = check_for_browser_side_navigation_;
+ data->ui_start_time_ = ui_start_time_;
+ data->is_external_request_ = is_external_request_;
+ data->cors_preflight_policy_ = cors_preflight_policy_;
+ data->input_perf_metric_report_policy_ = input_perf_metric_report_policy_;
+ data->redirect_status_ = redirect_status_;
+ data->suggested_filename_ = suggested_filename_;
+ data->is_ad_resource_ = is_ad_resource_;
+ return data;
+}
+
+bool ResourceRequest::IsNull() const {
+ return url_.IsNull();
+}
+
+const KURL& ResourceRequest::Url() const {
+ return url_;
+}
+
+void ResourceRequest::SetURL(const KURL& url) {
+ url_ = url;
+}
+
+void ResourceRequest::RemoveUserAndPassFromURL() {
+ if (url_.User().IsEmpty() && url_.Pass().IsEmpty())
+ return;
+
+ url_.SetUser(String());
+ url_.SetPass(String());
+}
+
+mojom::FetchCacheMode ResourceRequest::GetCacheMode() const {
+ return cache_mode_;
+}
+
+void ResourceRequest::SetCacheMode(mojom::FetchCacheMode cache_mode) {
+ cache_mode_ = cache_mode;
+}
+
+double ResourceRequest::TimeoutInterval() const {
+ return timeout_interval_;
+}
+
+void ResourceRequest::SetTimeoutInterval(double timout_interval_seconds) {
+ timeout_interval_ = timout_interval_seconds;
+}
+
+const KURL& ResourceRequest::SiteForCookies() const {
+ return site_for_cookies_;
+}
+
+void ResourceRequest::SetSiteForCookies(const KURL& site_for_cookies) {
+ site_for_cookies_ = site_for_cookies;
+}
+
+scoped_refptr<const SecurityOrigin> ResourceRequest::RequestorOrigin() const {
+ return requestor_origin_;
+}
+
+void ResourceRequest::SetRequestorOrigin(
+ scoped_refptr<const SecurityOrigin> requestor_origin) {
+ requestor_origin_ = std::move(requestor_origin);
+}
+
+const AtomicString& ResourceRequest::HttpMethod() const {
+ return http_method_;
+}
+
+void ResourceRequest::SetHTTPMethod(const AtomicString& http_method) {
+ http_method_ = http_method;
+}
+
+const HTTPHeaderMap& ResourceRequest::HttpHeaderFields() const {
+ return http_header_fields_;
+}
+
+const AtomicString& ResourceRequest::HttpHeaderField(
+ const AtomicString& name) const {
+ return http_header_fields_.Get(name);
+}
+
+void ResourceRequest::SetHTTPHeaderField(const AtomicString& name,
+ const AtomicString& value) {
+ http_header_fields_.Set(name, value);
+}
+
+void ResourceRequest::SetHTTPReferrer(const Referrer& referrer) {
+ if (referrer.referrer.IsEmpty())
+ http_header_fields_.Remove(HTTPNames::Referer);
+ else
+ SetHTTPHeaderField(HTTPNames::Referer, referrer.referrer);
+ referrer_policy_ = referrer.referrer_policy;
+ did_set_http_referrer_ = true;
+}
+
+void ResourceRequest::ClearHTTPReferrer() {
+ http_header_fields_.Remove(HTTPNames::Referer);
+ referrer_policy_ = kReferrerPolicyDefault;
+ did_set_http_referrer_ = false;
+}
+
+void ResourceRequest::SetHTTPOrigin(const SecurityOrigin* origin) {
+ SetHTTPHeaderField(HTTPNames::Origin, origin->ToAtomicString());
+}
+
+void ResourceRequest::ClearHTTPOrigin() {
+ http_header_fields_.Remove(HTTPNames::Origin);
+}
+
+void ResourceRequest::SetHTTPOriginIfNeeded(const SecurityOrigin* origin) {
+ if (NeedsHTTPOrigin())
+ SetHTTPOrigin(origin);
+}
+
+void ResourceRequest::SetHTTPOriginToMatchReferrerIfNeeded() {
+ if (NeedsHTTPOrigin()) {
+ SetHTTPOrigin(
+ SecurityOrigin::CreateFromString(HttpHeaderField(HTTPNames::Referer))
+ .get());
+ }
+}
+
+void ResourceRequest::ClearHTTPUserAgent() {
+ http_header_fields_.Remove(HTTPNames::User_Agent);
+}
+
+EncodedFormData* ResourceRequest::HttpBody() const {
+ return http_body_.get();
+}
+
+void ResourceRequest::SetHTTPBody(scoped_refptr<EncodedFormData> http_body) {
+ http_body_ = std::move(http_body);
+}
+
+bool ResourceRequest::AllowStoredCredentials() const {
+ return allow_stored_credentials_;
+}
+
+void ResourceRequest::SetAllowStoredCredentials(bool allow_credentials) {
+ allow_stored_credentials_ = allow_credentials;
+}
+
+ResourceLoadPriority ResourceRequest::Priority() const {
+ return priority_;
+}
+
+int ResourceRequest::IntraPriorityValue() const {
+ return intra_priority_value_;
+}
+
+void ResourceRequest::SetPriority(ResourceLoadPriority priority,
+ int intra_priority_value) {
+ priority_ = priority;
+ intra_priority_value_ = intra_priority_value;
+}
+
+void ResourceRequest::AddHTTPHeaderField(const AtomicString& name,
+ const AtomicString& value) {
+ HTTPHeaderMap::AddResult result = http_header_fields_.Add(name, value);
+ if (!result.is_new_entry)
+ result.stored_value->value = result.stored_value->value + ", " + value;
+}
+
+void ResourceRequest::AddHTTPHeaderFields(const HTTPHeaderMap& header_fields) {
+ HTTPHeaderMap::const_iterator end = header_fields.end();
+ for (HTTPHeaderMap::const_iterator it = header_fields.begin(); it != end;
+ ++it)
+ AddHTTPHeaderField(it->key, it->value);
+}
+
+void ResourceRequest::ClearHTTPHeaderField(const AtomicString& name) {
+ http_header_fields_.Remove(name);
+}
+
+void ResourceRequest::SetExternalRequestStateFromRequestorAddressSpace(
+ mojom::IPAddressSpace requestor_space) {
+ static_assert(mojom::IPAddressSpace::kLocal < mojom::IPAddressSpace::kPrivate,
+ "Local is inside Private");
+ static_assert(mojom::IPAddressSpace::kLocal < mojom::IPAddressSpace::kPublic,
+ "Local is inside Public");
+ static_assert(
+ mojom::IPAddressSpace::kPrivate < mojom::IPAddressSpace::kPublic,
+ "Private is inside Public");
+
+ // TODO(mkwst): This only checks explicit IP addresses. We'll have to move all
+ // this up to //net and //content in order to have any real impact on gateway
+ // attacks. That turns out to be a TON of work. https://crbug.com/378566
+ if (!RuntimeEnabledFeatures::CorsRFC1918Enabled()) {
+ is_external_request_ = false;
+ return;
+ }
+
+ mojom::IPAddressSpace target_space = mojom::IPAddressSpace::kPublic;
+ if (NetworkUtils::IsReservedIPAddress(url_.Host()))
+ target_space = mojom::IPAddressSpace::kPrivate;
+ if (SecurityOrigin::Create(url_)->IsLocalhost())
+ target_space = mojom::IPAddressSpace::kLocal;
+
+ is_external_request_ = requestor_space > target_space;
+}
+
+void ResourceRequest::SetNavigationStartTime(double navigation_start) {
+ navigation_start_ = navigation_start;
+}
+
+bool ResourceRequest::IsConditional() const {
+ return (http_header_fields_.Contains(HTTPNames::If_Match) ||
+ http_header_fields_.Contains(HTTPNames::If_Modified_Since) ||
+ http_header_fields_.Contains(HTTPNames::If_None_Match) ||
+ http_header_fields_.Contains(HTTPNames::If_Range) ||
+ http_header_fields_.Contains(HTTPNames::If_Unmodified_Since));
+}
+
+void ResourceRequest::SetHasUserGesture(bool has_user_gesture) {
+ has_user_gesture_ |= has_user_gesture;
+}
+
+const CacheControlHeader& ResourceRequest::GetCacheControlHeader() const {
+ if (!cache_control_header_cache_.parsed) {
+ cache_control_header_cache_ = ParseCacheControlDirectives(
+ http_header_fields_.Get(HTTPNames::Cache_Control),
+ http_header_fields_.Get(HTTPNames::Pragma));
+ }
+ return cache_control_header_cache_;
+}
+
+bool ResourceRequest::CacheControlContainsNoCache() const {
+ return GetCacheControlHeader().contains_no_cache;
+}
+
+bool ResourceRequest::CacheControlContainsNoStore() const {
+ return GetCacheControlHeader().contains_no_store;
+}
+
+bool ResourceRequest::HasCacheValidatorFields() const {
+ return !http_header_fields_.Get(HTTPNames::Last_Modified).IsEmpty() ||
+ !http_header_fields_.Get(HTTPNames::ETag).IsEmpty();
+}
+
+bool ResourceRequest::NeedsHTTPOrigin() const {
+ if (!HttpOrigin().IsEmpty())
+ return false; // Request already has an Origin header.
+
+ // Don't send an Origin header for GET or HEAD to avoid privacy issues.
+ // For example, if an intranet page has a hyperlink to an external web
+ // site, we don't want to include the Origin of the request because it
+ // will leak the internal host name. Similar privacy concerns have lead
+ // to the widespread suppression of the Referer header at the network
+ // layer.
+ if (HttpMethod() == HTTPNames::GET || HttpMethod() == HTTPNames::HEAD)
+ return false;
+
+ // For non-GET and non-HEAD methods, always send an Origin header so the
+ // server knows we support this feature.
+ return true;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_request.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_request.h
new file mode 100644
index 00000000000..194138f38f3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_request.h
@@ -0,0 +1,498 @@
+/*
+ * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com>
+ * Copyright (C) 2009, 2012 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_REQUEST_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_REQUEST_H_
+
+#include <memory>
+#include "services/network/public/mojom/cors.mojom-blink.h"
+#include "services/network/public/mojom/fetch_api.mojom-blink.h"
+#include "services/network/public/mojom/request_context_frame_type.mojom-shared.h"
+#include "third_party/blink/public/mojom/net/ip_address_space.mojom-blink.h"
+#include "third_party/blink/public/platform/modules/fetch/fetch_api_request.mojom-shared.h"
+#include "third_party/blink/public/platform/web_url_request.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_load_priority.h"
+#include "third_party/blink/renderer/platform/network/encoded_form_data.h"
+#include "third_party/blink/renderer/platform/network/http_header_map.h"
+#include "third_party/blink/renderer/platform/network/http_names.h"
+#include "third_party/blink/renderer/platform/network/http_parsers.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/weborigin/referrer.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+
+namespace blink {
+
+enum class ResourceRequestBlockedReason {
+ kCSP,
+ kMixedContent,
+ kOrigin,
+ kInspector,
+ kSubresourceFilter,
+ kOther,
+ kContentType,
+ kNone
+};
+
+enum InputToLoadPerfMetricReportPolicy : uint8_t {
+ kNoReport, // Don't report metrics for this ResourceRequest.
+ kReportLink, // Report metrics for this request as initiated by a link click.
+ kReportIntent, // Report metrics for this request as initiated by an intent.
+};
+
+struct CrossThreadResourceRequestData;
+
+// A ResourceRequest is a "request" object for ResourceLoader. Conceptually
+// it is https://fetch.spec.whatwg.org/#concept-request, but it contains
+// a lot of blink specific fields. WebURLRequest is the "public version"
+// of this class and WebURLLoader needs it. See WebURLRequest and
+// WrappedResourceRequest.
+//
+// There are cases where we need to copy a request across threads, and
+// CrossThreadResourceRequestData is a struct for the purpose. When you add a
+// member variable to this class, do not forget to add the corresponding
+// one in CrossThreadResourceRequestData and write copying logic.
+class PLATFORM_EXPORT ResourceRequest final {
+ USING_FAST_MALLOC(ResourceRequest);
+
+ public:
+ enum class RedirectStatus : uint8_t { kFollowedRedirect, kNoRedirect };
+
+ ResourceRequest();
+ explicit ResourceRequest(const String& url_string);
+ explicit ResourceRequest(const KURL&);
+ explicit ResourceRequest(CrossThreadResourceRequestData*);
+
+ // TODO(toyoshim): Use std::unique_ptr as much as possible, and hopefully
+ // make ResourceRequest WTF_MAKE_NONCOPYABLE. See crbug.com/787704.
+ ResourceRequest(const ResourceRequest&);
+ ResourceRequest& operator=(const ResourceRequest&);
+
+ // Constructs a new ResourceRequest for a redirect from this instance.
+ std::unique_ptr<ResourceRequest> CreateRedirectRequest(
+ const KURL& new_url,
+ const AtomicString& new_method,
+ const KURL& new_site_for_cookies,
+ const String& new_referrer,
+ ReferrerPolicy new_referrer_policy,
+ bool skip_service_worker) const;
+
+ // Gets a copy of the data suitable for passing to another thread.
+ std::unique_ptr<CrossThreadResourceRequestData> CopyData() const;
+
+ bool IsNull() const;
+
+ const KURL& Url() const;
+ void SetURL(const KURL&);
+
+ void RemoveUserAndPassFromURL();
+
+ mojom::FetchCacheMode GetCacheMode() const;
+ void SetCacheMode(mojom::FetchCacheMode);
+
+ double TimeoutInterval() const; // May return 0 when using platform default.
+ void SetTimeoutInterval(double);
+
+ const KURL& SiteForCookies() const;
+ void SetSiteForCookies(const KURL&);
+
+ scoped_refptr<const SecurityOrigin> RequestorOrigin() const;
+ void SetRequestorOrigin(scoped_refptr<const SecurityOrigin>);
+
+ const AtomicString& HttpMethod() const;
+ void SetHTTPMethod(const AtomicString&);
+
+ const HTTPHeaderMap& HttpHeaderFields() const;
+ const AtomicString& HttpHeaderField(const AtomicString& name) const;
+ void SetHTTPHeaderField(const AtomicString& name, const AtomicString& value);
+ void AddHTTPHeaderField(const AtomicString& name, const AtomicString& value);
+ void AddHTTPHeaderFields(const HTTPHeaderMap& header_fields);
+ void ClearHTTPHeaderField(const AtomicString& name);
+
+ const AtomicString& HttpContentType() const {
+ return HttpHeaderField(HTTPNames::Content_Type);
+ }
+ void SetHTTPContentType(const AtomicString& http_content_type) {
+ SetHTTPHeaderField(HTTPNames::Content_Type, http_content_type);
+ }
+
+ bool DidSetHTTPReferrer() const { return did_set_http_referrer_; }
+ const AtomicString& HttpReferrer() const {
+ return HttpHeaderField(HTTPNames::Referer);
+ }
+ ReferrerPolicy GetReferrerPolicy() const { return referrer_policy_; }
+ void SetHTTPReferrer(const Referrer&);
+ void ClearHTTPReferrer();
+
+ const AtomicString& HttpOrigin() const {
+ return HttpHeaderField(HTTPNames::Origin);
+ }
+ void SetHTTPOrigin(const SecurityOrigin*);
+ void ClearHTTPOrigin();
+ void SetHTTPOriginIfNeeded(const SecurityOrigin*);
+ void SetHTTPOriginToMatchReferrerIfNeeded();
+
+ void SetHTTPUserAgent(const AtomicString& http_user_agent) {
+ SetHTTPHeaderField(HTTPNames::User_Agent, http_user_agent);
+ }
+ void ClearHTTPUserAgent();
+
+ void SetHTTPAccept(const AtomicString& http_accept) {
+ SetHTTPHeaderField(HTTPNames::Accept, http_accept);
+ }
+
+ EncodedFormData* HttpBody() const;
+ void SetHTTPBody(scoped_refptr<EncodedFormData>);
+
+ bool AllowStoredCredentials() const;
+ void SetAllowStoredCredentials(bool allow_credentials);
+
+ // TODO(yhirano): Describe what Priority and IntraPriorityValue are.
+ ResourceLoadPriority Priority() const;
+ int IntraPriorityValue() const;
+ void SetPriority(ResourceLoadPriority, int intra_priority_value = 0);
+
+ bool IsConditional() const;
+
+ // Whether the associated ResourceHandleClient needs to be notified of
+ // upload progress made for that resource.
+ bool ReportUploadProgress() const { return report_upload_progress_; }
+ void SetReportUploadProgress(bool report_upload_progress) {
+ report_upload_progress_ = report_upload_progress;
+ }
+
+ // Whether actual headers being sent/received should be collected and reported
+ // for the request.
+ bool ReportRawHeaders() const { return report_raw_headers_; }
+ void SetReportRawHeaders(bool report_raw_headers) {
+ report_raw_headers_ = report_raw_headers;
+ }
+
+ // Allows the request to be matched up with its requestor.
+ int RequestorID() const { return requestor_id_; }
+ void SetRequestorID(int requestor_id) { requestor_id_ = requestor_id; }
+
+ // The unique child id (not PID) of the process from which this request
+ // originated. In the case of out-of-process plugins, this allows to link back
+ // the request to the plugin process (as it is processed through a render view
+ // process).
+ int GetPluginChildID() const { return plugin_child_id_; }
+ void SetPluginChildID(int plugin_child_id) {
+ plugin_child_id_ = plugin_child_id;
+ }
+
+ // Allows the request to be matched up with its app cache host.
+ int AppCacheHostID() const { return app_cache_host_id_; }
+ void SetAppCacheHostID(int id) { app_cache_host_id_ = id; }
+
+ // True if request was user initiated.
+ bool HasUserGesture() const { return has_user_gesture_; }
+ void SetHasUserGesture(bool);
+
+ // True if request should be downloaded to file.
+ bool DownloadToFile() const { return download_to_file_; }
+ void SetDownloadToFile(bool download_to_file) {
+ download_to_file_ = download_to_file;
+ }
+
+ // True if request shuold be downloaded to blob.
+ bool DownloadToBlob() const { return download_to_blob_; }
+ void SetDownloadToBlob(bool download_to_blob) {
+ download_to_blob_ = download_to_blob;
+ }
+
+ // True if the requestor wants to receive a response body as
+ // WebDataConsumerHandle.
+ bool UseStreamOnResponse() const { return use_stream_on_response_; }
+ void SetUseStreamOnResponse(bool use_stream_on_response) {
+ use_stream_on_response_ = use_stream_on_response;
+ }
+
+ // True if the request can work after the fetch group is terminated.
+ bool GetKeepalive() const { return keepalive_; }
+ void SetKeepalive(bool keepalive) { keepalive_ = keepalive; }
+
+ // True if service workers should not get events for the request.
+ bool GetSkipServiceWorker() const { return skip_service_worker_; }
+ void SetSkipServiceWorker(bool skip_service_worker) {
+ skip_service_worker_ = skip_service_worker;
+ }
+
+ // True if corresponding AppCache group should be resetted.
+ bool ShouldResetAppCache() const { return should_reset_app_cache_; }
+ void SetShouldResetAppCache(bool should_reset_app_cache) {
+ should_reset_app_cache_ = should_reset_app_cache;
+ }
+
+ // Extra data associated with this request.
+ WebURLRequest::ExtraData* GetExtraData() const {
+ return sharable_extra_data_ ? sharable_extra_data_->data.get() : nullptr;
+ }
+ void SetExtraData(std::unique_ptr<WebURLRequest::ExtraData> extra_data) {
+ if (extra_data) {
+ sharable_extra_data_ =
+ base::MakeRefCounted<SharableExtraData>(std::move(extra_data));
+ } else {
+ sharable_extra_data_ = nullptr;
+ }
+ }
+
+ WebURLRequest::RequestContext GetRequestContext() const {
+ return request_context_;
+ }
+ void SetRequestContext(WebURLRequest::RequestContext context) {
+ request_context_ = context;
+ }
+
+ network::mojom::RequestContextFrameType GetFrameType() const {
+ return frame_type_;
+ }
+ void SetFrameType(network::mojom::RequestContextFrameType frame_type) {
+ frame_type_ = frame_type;
+ }
+
+ network::mojom::FetchRequestMode GetFetchRequestMode() const {
+ return fetch_request_mode_;
+ }
+ void SetFetchRequestMode(network::mojom::FetchRequestMode mode) {
+ fetch_request_mode_ = mode;
+ }
+
+ network::mojom::FetchCredentialsMode GetFetchCredentialsMode() const {
+ return fetch_credentials_mode_;
+ }
+ void SetFetchCredentialsMode(network::mojom::FetchCredentialsMode mode) {
+ fetch_credentials_mode_ = mode;
+ }
+
+ network::mojom::FetchRedirectMode GetFetchRedirectMode() const {
+ return fetch_redirect_mode_;
+ }
+ void SetFetchRedirectMode(network::mojom::FetchRedirectMode redirect) {
+ fetch_redirect_mode_ = redirect;
+ }
+
+ const String& GetFetchIntegrity() const { return fetch_integrity_; }
+ void SetFetchIntegrity(const String& integrity) {
+ fetch_integrity_ = integrity;
+ }
+
+ WebURLRequest::PreviewsState GetPreviewsState() const {
+ return previews_state_;
+ }
+ void SetPreviewsState(WebURLRequest::PreviewsState previews_state) {
+ previews_state_ = previews_state;
+ }
+
+ bool CacheControlContainsNoCache() const;
+ bool CacheControlContainsNoStore() const;
+ bool HasCacheValidatorFields() const;
+
+ bool CheckForBrowserSideNavigation() const {
+ return check_for_browser_side_navigation_;
+ }
+ void SetCheckForBrowserSideNavigation(bool check) {
+ check_for_browser_side_navigation_ = check;
+ }
+
+ bool WasDiscarded() const { return was_discarded_; }
+ void SetWasDiscarded(bool was_discarded) { was_discarded_ = was_discarded; }
+
+ double UiStartTime() const { return ui_start_time_; }
+ void SetUIStartTime(double ui_start_time_seconds) {
+ ui_start_time_ = ui_start_time_seconds;
+ }
+
+ // https://wicg.github.io/cors-rfc1918/#external-request
+ bool IsExternalRequest() const { return is_external_request_; }
+ void SetExternalRequestStateFromRequestorAddressSpace(mojom::IPAddressSpace);
+
+ network::mojom::CORSPreflightPolicy CORSPreflightPolicy() const {
+ return cors_preflight_policy_;
+ }
+ void SetCORSPreflightPolicy(network::mojom::CORSPreflightPolicy policy) {
+ cors_preflight_policy_ = policy;
+ }
+
+ InputToLoadPerfMetricReportPolicy InputPerfMetricReportPolicy() const {
+ return input_perf_metric_report_policy_;
+ }
+ void SetInputPerfMetricReportPolicy(
+ InputToLoadPerfMetricReportPolicy input_perf_metric_report_policy) {
+ input_perf_metric_report_policy_ = input_perf_metric_report_policy;
+ }
+
+ void SetRedirectStatus(RedirectStatus status) { redirect_status_ = status; }
+ RedirectStatus GetRedirectStatus() const { return redirect_status_; }
+
+ void SetSuggestedFilename(const WTF::Optional<String>& suggested_filename) {
+ suggested_filename_ = suggested_filename;
+ }
+ const WTF::Optional<String>& GetSuggestedFilename() const {
+ return suggested_filename_;
+ }
+
+ void SetNavigationStartTime(double);
+ double NavigationStartTime() const { return navigation_start_; }
+
+ void SetIsSameDocumentNavigation(bool is_same_document) {
+ is_same_document_navigation_ = is_same_document;
+ }
+ bool IsSameDocumentNavigation() const { return is_same_document_navigation_; }
+
+ void SetIsAdResource() { is_ad_resource_ = true; }
+ bool IsAdResource() const { return is_ad_resource_; }
+
+ private:
+ using SharableExtraData =
+ base::RefCountedData<std::unique_ptr<WebURLRequest::ExtraData>>;
+
+ const CacheControlHeader& GetCacheControlHeader() const;
+
+ bool NeedsHTTPOrigin() const;
+
+ KURL url_;
+ double timeout_interval_; // 0 is a magic value for platform default on
+ // platforms that have one.
+ KURL site_for_cookies_;
+
+ // The SecurityOrigin specified by the ResourceLoaderOptions in case e.g.
+ // when the fetching was initiated in an isolated world. Set by
+ // ResourceFetcher but only when needed.
+ //
+ // TODO(crbug.com/811669): Merge with some of the other origin variables.
+ scoped_refptr<const SecurityOrigin> requestor_origin_;
+
+ AtomicString http_method_;
+ HTTPHeaderMap http_header_fields_;
+ scoped_refptr<EncodedFormData> http_body_;
+ bool allow_stored_credentials_ : 1;
+ bool report_upload_progress_ : 1;
+ bool report_raw_headers_ : 1;
+ bool has_user_gesture_ : 1;
+ bool download_to_file_ : 1;
+ bool download_to_blob_ : 1;
+ bool use_stream_on_response_ : 1;
+ bool keepalive_ : 1;
+ bool should_reset_app_cache_ : 1;
+ mojom::FetchCacheMode cache_mode_;
+ bool skip_service_worker_ : 1;
+ ResourceLoadPriority priority_;
+ int intra_priority_value_;
+ int requestor_id_;
+ int plugin_child_id_;
+ int app_cache_host_id_;
+ WebURLRequest::PreviewsState previews_state_;
+ scoped_refptr<SharableExtraData> sharable_extra_data_;
+ WebURLRequest::RequestContext request_context_;
+ network::mojom::RequestContextFrameType frame_type_;
+ network::mojom::FetchRequestMode fetch_request_mode_;
+ network::mojom::FetchCredentialsMode fetch_credentials_mode_;
+ network::mojom::FetchRedirectMode fetch_redirect_mode_;
+ String fetch_integrity_;
+ ReferrerPolicy referrer_policy_;
+ bool did_set_http_referrer_;
+ bool check_for_browser_side_navigation_;
+ bool was_discarded_;
+ double ui_start_time_;
+ bool is_external_request_;
+ network::mojom::CORSPreflightPolicy cors_preflight_policy_;
+ bool is_same_document_navigation_;
+ InputToLoadPerfMetricReportPolicy input_perf_metric_report_policy_;
+ RedirectStatus redirect_status_;
+ WTF::Optional<String> suggested_filename_;
+
+ mutable CacheControlHeader cache_control_header_cache_;
+
+ static double default_timeout_interval_;
+
+ double navigation_start_ = 0;
+
+ bool is_ad_resource_ = false;
+};
+
+// This class is needed to copy a ResourceRequest across threads, because it
+// has some members which cannot be transferred across threads (AtomicString
+// for example).
+// There are some rules / restrictions:
+// - This struct cannot contain an object that cannot be transferred across
+// threads (e.g., AtomicString)
+// - Non-simple members need explicit copying (e.g., String::IsolatedCopy,
+// KURL::Copy) rather than the copy constructor or the assignment operator.
+struct CrossThreadResourceRequestData {
+ WTF_MAKE_NONCOPYABLE(CrossThreadResourceRequestData);
+ USING_FAST_MALLOC(CrossThreadResourceRequestData);
+
+ public:
+ CrossThreadResourceRequestData() = default;
+ KURL url_;
+
+ mojom::FetchCacheMode cache_mode_;
+ double timeout_interval_;
+ KURL site_for_cookies_;
+ scoped_refptr<const SecurityOrigin> requestor_origin_;
+
+ String http_method_;
+ std::unique_ptr<CrossThreadHTTPHeaderMapData> http_headers_;
+ scoped_refptr<EncodedFormData> http_body_;
+ bool allow_stored_credentials_;
+ bool report_upload_progress_;
+ bool has_user_gesture_;
+ bool download_to_file_;
+ bool download_to_blob_;
+ bool skip_service_worker_;
+ bool use_stream_on_response_;
+ bool keepalive_;
+ bool should_reset_app_cache_;
+ ResourceLoadPriority priority_;
+ int intra_priority_value_;
+ int requestor_id_;
+ int plugin_child_id_;
+ int app_cache_host_id_;
+ WebURLRequest::RequestContext request_context_;
+ network::mojom::RequestContextFrameType frame_type_;
+ network::mojom::FetchRequestMode fetch_request_mode_;
+ network::mojom::FetchCredentialsMode fetch_credentials_mode_;
+ network::mojom::FetchRedirectMode fetch_redirect_mode_;
+ String fetch_integrity_;
+ WebURLRequest::PreviewsState previews_state_;
+ ReferrerPolicy referrer_policy_;
+ bool did_set_http_referrer_;
+ bool check_for_browser_side_navigation_;
+ double ui_start_time_;
+ bool is_external_request_;
+ network::mojom::CORSPreflightPolicy cors_preflight_policy_;
+ InputToLoadPerfMetricReportPolicy input_perf_metric_report_policy_;
+ ResourceRequest::RedirectStatus redirect_status_;
+ base::Optional<String> suggested_filename_;
+ bool is_ad_resource_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_REQUEST_H_
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_request_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_request_test.cc
new file mode 100644
index 00000000000..0c00adaf85c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_request_test.cc
@@ -0,0 +1,161 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
+
+#include <memory>
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/web_url_request.h"
+#include "third_party/blink/renderer/platform/network/encoded_form_data.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/weborigin/referrer.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+
+namespace blink {
+
+TEST(ResourceRequestTest, CrossThreadResourceRequestData) {
+ ResourceRequest original;
+ original.SetURL(KURL("http://www.example.com/test.htm"));
+ original.SetCacheMode(mojom::FetchCacheMode::kDefault);
+ original.SetTimeoutInterval(10);
+ original.SetSiteForCookies(KURL("http://www.example.com/first_party.htm"));
+ original.SetRequestorOrigin(
+ SecurityOrigin::Create(KURL("http://www.example.com/first_party.htm")));
+ original.SetHTTPMethod(HTTPNames::GET);
+ original.SetHTTPHeaderField(AtomicString("Foo"), AtomicString("Bar"));
+ original.SetHTTPHeaderField(AtomicString("Piyo"), AtomicString("Fuga"));
+ original.SetPriority(ResourceLoadPriority::kLow, 20);
+
+ scoped_refptr<EncodedFormData> original_body(
+ EncodedFormData::Create("Test Body"));
+ original.SetHTTPBody(original_body);
+ original.SetAllowStoredCredentials(false);
+ original.SetReportUploadProgress(false);
+ original.SetHasUserGesture(false);
+ original.SetDownloadToFile(false);
+ original.SetSkipServiceWorker(false);
+ original.SetFetchRequestMode(network::mojom::FetchRequestMode::kCORS);
+ original.SetFetchCredentialsMode(
+ network::mojom::FetchCredentialsMode::kSameOrigin);
+ original.SetRequestorID(30);
+ original.SetPluginChildID(40);
+ original.SetAppCacheHostID(50);
+ original.SetRequestContext(WebURLRequest::kRequestContextAudio);
+ original.SetFrameType(network::mojom::RequestContextFrameType::kNested);
+ original.SetHTTPReferrer(
+ Referrer("http://www.example.com/referrer.htm", kReferrerPolicyDefault));
+
+ EXPECT_STREQ("http://www.example.com/test.htm",
+ original.Url().GetString().Utf8().data());
+ EXPECT_EQ(mojom::FetchCacheMode::kDefault, original.GetCacheMode());
+ EXPECT_EQ(10, original.TimeoutInterval());
+ EXPECT_STREQ("http://www.example.com/first_party.htm",
+ original.SiteForCookies().GetString().Utf8().data());
+ EXPECT_STREQ("www.example.com",
+ original.RequestorOrigin()->Host().Utf8().data());
+ EXPECT_STREQ("GET", original.HttpMethod().Utf8().data());
+ EXPECT_STREQ("Bar", original.HttpHeaderFields().Get("Foo").Utf8().data());
+ EXPECT_STREQ("Fuga", original.HttpHeaderFields().Get("Piyo").Utf8().data());
+ EXPECT_EQ(ResourceLoadPriority::kLow, original.Priority());
+ EXPECT_STREQ("Test Body",
+ original.HttpBody()->FlattenToString().Utf8().data());
+ EXPECT_FALSE(original.AllowStoredCredentials());
+ EXPECT_FALSE(original.ReportUploadProgress());
+ EXPECT_FALSE(original.HasUserGesture());
+ EXPECT_FALSE(original.DownloadToFile());
+ EXPECT_FALSE(original.GetSkipServiceWorker());
+ EXPECT_EQ(network::mojom::FetchRequestMode::kCORS,
+ original.GetFetchRequestMode());
+ EXPECT_EQ(network::mojom::FetchCredentialsMode::kSameOrigin,
+ original.GetFetchCredentialsMode());
+ EXPECT_EQ(30, original.RequestorID());
+ EXPECT_EQ(40, original.GetPluginChildID());
+ EXPECT_EQ(50, original.AppCacheHostID());
+ EXPECT_EQ(WebURLRequest::kRequestContextAudio, original.GetRequestContext());
+ EXPECT_EQ(network::mojom::RequestContextFrameType::kNested,
+ original.GetFrameType());
+ EXPECT_STREQ("http://www.example.com/referrer.htm",
+ original.HttpReferrer().Utf8().data());
+ EXPECT_EQ(kReferrerPolicyDefault, original.GetReferrerPolicy());
+
+ std::unique_ptr<CrossThreadResourceRequestData> data1(original.CopyData());
+ ResourceRequest copy1(data1.get());
+
+ EXPECT_STREQ("http://www.example.com/test.htm",
+ copy1.Url().GetString().Utf8().data());
+ EXPECT_EQ(mojom::FetchCacheMode::kDefault, copy1.GetCacheMode());
+ EXPECT_EQ(10, copy1.TimeoutInterval());
+ EXPECT_STREQ("http://www.example.com/first_party.htm",
+ copy1.SiteForCookies().GetString().Utf8().data());
+ EXPECT_STREQ("www.example.com",
+ copy1.RequestorOrigin()->Host().Utf8().data());
+ EXPECT_STREQ("GET", copy1.HttpMethod().Utf8().data());
+ EXPECT_STREQ("Bar", copy1.HttpHeaderFields().Get("Foo").Utf8().data());
+ EXPECT_EQ(ResourceLoadPriority::kLow, copy1.Priority());
+ EXPECT_STREQ("Test Body", copy1.HttpBody()->FlattenToString().Utf8().data());
+ EXPECT_FALSE(copy1.AllowStoredCredentials());
+ EXPECT_FALSE(copy1.ReportUploadProgress());
+ EXPECT_FALSE(copy1.HasUserGesture());
+ EXPECT_FALSE(copy1.DownloadToFile());
+ EXPECT_FALSE(copy1.GetSkipServiceWorker());
+ EXPECT_EQ(network::mojom::FetchRequestMode::kCORS,
+ copy1.GetFetchRequestMode());
+ EXPECT_EQ(network::mojom::FetchCredentialsMode::kSameOrigin,
+ copy1.GetFetchCredentialsMode());
+ EXPECT_EQ(30, copy1.RequestorID());
+ EXPECT_EQ(40, copy1.GetPluginChildID());
+ EXPECT_EQ(50, copy1.AppCacheHostID());
+ EXPECT_EQ(WebURLRequest::kRequestContextAudio, copy1.GetRequestContext());
+ EXPECT_EQ(network::mojom::RequestContextFrameType::kNested,
+ copy1.GetFrameType());
+ EXPECT_STREQ("http://www.example.com/referrer.htm",
+ copy1.HttpReferrer().Utf8().data());
+ EXPECT_EQ(kReferrerPolicyDefault, copy1.GetReferrerPolicy());
+
+ copy1.SetAllowStoredCredentials(true);
+ copy1.SetReportUploadProgress(true);
+ copy1.SetHasUserGesture(true);
+ copy1.SetDownloadToFile(true);
+ copy1.SetSkipServiceWorker(true);
+ copy1.SetFetchRequestMode(network::mojom::FetchRequestMode::kNoCORS);
+ copy1.SetFetchCredentialsMode(network::mojom::FetchCredentialsMode::kInclude);
+
+ std::unique_ptr<CrossThreadResourceRequestData> data2(copy1.CopyData());
+ ResourceRequest copy2(data2.get());
+ EXPECT_TRUE(copy2.AllowStoredCredentials());
+ EXPECT_TRUE(copy2.ReportUploadProgress());
+ EXPECT_TRUE(copy2.HasUserGesture());
+ EXPECT_TRUE(copy2.DownloadToFile());
+ EXPECT_TRUE(copy2.GetSkipServiceWorker());
+ EXPECT_EQ(network::mojom::FetchRequestMode::kNoCORS,
+ copy1.GetFetchRequestMode());
+ EXPECT_EQ(network::mojom::FetchCredentialsMode::kInclude,
+ copy1.GetFetchCredentialsMode());
+}
+
+TEST(ResourceRequestTest, SetHasUserGesture) {
+ ResourceRequest original;
+ EXPECT_FALSE(original.HasUserGesture());
+ original.SetHasUserGesture(true);
+ EXPECT_TRUE(original.HasUserGesture());
+ original.SetHasUserGesture(false);
+ EXPECT_TRUE(original.HasUserGesture());
+}
+
+TEST(ResourceRequestTest, SetIsAdResource) {
+ ResourceRequest original;
+ EXPECT_FALSE(original.IsAdResource());
+ original.SetIsAdResource();
+ EXPECT_TRUE(original.IsAdResource());
+
+ // Should persist across redirects.
+ std::unique_ptr<ResourceRequest> redirect_request =
+ original.CreateRedirectRequest(
+ KURL("https://example.test/redirect"), original.HttpMethod(),
+ original.SiteForCookies(), original.HttpReferrer(),
+ original.GetReferrerPolicy(), original.GetSkipServiceWorker());
+ EXPECT_TRUE(redirect_request->IsAdResource());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_response.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_response.cc
new file mode 100644
index 00000000000..6e6281a18b5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_response.cc
@@ -0,0 +1,625 @@
+/*
+ * Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
+
+#include <algorithm>
+#include <limits>
+#include <memory>
+#include <string>
+
+#include "third_party/blink/public/platform/web_url_response.h"
+#include "third_party/blink/renderer/platform/network/http_names.h"
+#include "third_party/blink/renderer/platform/network/http_parsers.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+namespace {
+
+template <typename Interface>
+Vector<Interface> IsolatedCopy(const Vector<Interface>& src) {
+ Vector<Interface> result;
+ result.ReserveCapacity(src.size());
+ for (const auto& timestamp : src) {
+ result.push_back(timestamp.IsolatedCopy());
+ }
+ return result;
+}
+
+static const char kCacheControlHeader[] = "cache-control";
+static const char kPragmaHeader[] = "pragma";
+
+} // namespace
+
+ResourceResponse::SignedCertificateTimestamp::SignedCertificateTimestamp(
+ const blink::WebURLResponse::SignedCertificateTimestamp& sct)
+ : status_(sct.status),
+ origin_(sct.origin),
+ log_description_(sct.log_description),
+ log_id_(sct.log_id),
+ timestamp_(sct.timestamp),
+ hash_algorithm_(sct.hash_algorithm),
+ signature_algorithm_(sct.signature_algorithm),
+ signature_data_(sct.signature_data) {}
+
+ResourceResponse::SignedCertificateTimestamp
+ResourceResponse::SignedCertificateTimestamp::IsolatedCopy() const {
+ return SignedCertificateTimestamp(
+ status_.IsolatedCopy(), origin_.IsolatedCopy(),
+ log_description_.IsolatedCopy(), log_id_.IsolatedCopy(), timestamp_,
+ hash_algorithm_.IsolatedCopy(), signature_algorithm_.IsolatedCopy(),
+ signature_data_.IsolatedCopy());
+}
+
+ResourceResponse::ResourceResponse()
+ : expected_content_length_(0), is_null_(true) {}
+
+ResourceResponse::ResourceResponse(const KURL& url,
+ const AtomicString& mime_type,
+ long long expected_length,
+ const AtomicString& text_encoding_name)
+ : url_(url),
+ mime_type_(mime_type),
+ expected_content_length_(expected_length),
+ text_encoding_name_(text_encoding_name),
+ is_null_(false) {}
+
+ResourceResponse::ResourceResponse(CrossThreadResourceResponseData* data)
+ : ResourceResponse() {
+ SetURL(data->url_);
+ SetMimeType(AtomicString(data->mime_type_));
+ SetExpectedContentLength(data->expected_content_length_);
+ SetTextEncodingName(AtomicString(data->text_encoding_name_));
+
+ SetHTTPStatusCode(data->http_status_code_);
+ SetHTTPStatusText(AtomicString(data->http_status_text_));
+
+ http_header_fields_.Adopt(std::move(data->http_headers_));
+ SetResourceLoadTiming(std::move(data->resource_load_timing_));
+ remote_ip_address_ = AtomicString(data->remote_ip_address_);
+ remote_port_ = data->remote_port_;
+ has_major_certificate_errors_ = data->has_major_certificate_errors_;
+ ct_policy_compliance_ = data->ct_policy_compliance_;
+ is_legacy_symantec_cert_ = data->is_legacy_symantec_cert_;
+ cert_validity_start_ = data->cert_validity_start_;
+ was_fetched_via_spdy_ = data->was_fetched_via_spdy_;
+ was_fetched_via_proxy_ = data->was_fetched_via_proxy_;
+ was_fetched_via_service_worker_ = data->was_fetched_via_service_worker_;
+ was_fallback_required_by_service_worker_ =
+ data->was_fallback_required_by_service_worker_;
+ did_service_worker_navigation_preload_ =
+ data->did_service_worker_navigation_preload_;
+ response_type_via_service_worker_ = data->response_type_via_service_worker_;
+ security_style_ = data->security_style_;
+ security_details_.protocol = data->security_details_.protocol;
+ security_details_.cipher = data->security_details_.cipher;
+ security_details_.key_exchange = data->security_details_.key_exchange;
+ security_details_.key_exchange_group =
+ data->security_details_.key_exchange_group;
+ security_details_.mac = data->security_details_.mac;
+ security_details_.subject_name = data->security_details_.subject_name;
+ security_details_.san_list = data->security_details_.san_list;
+ security_details_.issuer = data->security_details_.issuer;
+ security_details_.valid_from = data->security_details_.valid_from;
+ security_details_.valid_to = data->security_details_.valid_to;
+ for (auto& cert : data->certificate_)
+ security_details_.certificate.push_back(AtomicString(cert));
+ security_details_.sct_list = data->security_details_.sct_list;
+ http_version_ = data->http_version_;
+ app_cache_id_ = data->app_cache_id_;
+ app_cache_manifest_url_ = data->app_cache_manifest_url_.Copy();
+ multipart_boundary_ = data->multipart_boundary_;
+ url_list_via_service_worker_ = data->url_list_via_service_worker_;
+ cache_storage_cache_name_ = data->cache_storage_cache_name_;
+ response_time_ = data->response_time_;
+ encoded_data_length_ = data->encoded_data_length_;
+ encoded_body_length_ = data->encoded_body_length_;
+ decoded_body_length_ = data->decoded_body_length_;
+ downloaded_file_path_ = data->downloaded_file_path_;
+ downloaded_file_handle_ = data->downloaded_file_handle_;
+
+ // Bug https://bugs.webkit.org/show_bug.cgi?id=60397 this doesn't support
+ // whatever values may be present in the opaque m_extraData structure.
+}
+
+ResourceResponse::ResourceResponse(const ResourceResponse&) = default;
+ResourceResponse& ResourceResponse::operator=(const ResourceResponse&) =
+ default;
+
+std::unique_ptr<CrossThreadResourceResponseData> ResourceResponse::CopyData()
+ const {
+ std::unique_ptr<CrossThreadResourceResponseData> data =
+ std::make_unique<CrossThreadResourceResponseData>();
+ data->url_ = Url().Copy();
+ data->mime_type_ = MimeType().GetString().IsolatedCopy();
+ data->expected_content_length_ = ExpectedContentLength();
+ data->text_encoding_name_ = TextEncodingName().GetString().IsolatedCopy();
+ data->http_status_code_ = HttpStatusCode();
+ data->http_status_text_ = HttpStatusText().GetString().IsolatedCopy();
+ data->http_headers_ = HttpHeaderFields().CopyData();
+ if (resource_load_timing_)
+ data->resource_load_timing_ = resource_load_timing_->DeepCopy();
+ data->remote_ip_address_ = remote_ip_address_.GetString().IsolatedCopy();
+ data->remote_port_ = remote_port_;
+ data->has_major_certificate_errors_ = has_major_certificate_errors_;
+ data->ct_policy_compliance_ = ct_policy_compliance_;
+ data->is_legacy_symantec_cert_ = is_legacy_symantec_cert_;
+ data->cert_validity_start_ = cert_validity_start_;
+ data->was_fetched_via_spdy_ = was_fetched_via_spdy_;
+ data->was_fetched_via_proxy_ = was_fetched_via_proxy_;
+ data->was_fetched_via_service_worker_ = was_fetched_via_service_worker_;
+ data->was_fallback_required_by_service_worker_ =
+ was_fallback_required_by_service_worker_;
+ data->did_service_worker_navigation_preload_ =
+ did_service_worker_navigation_preload_;
+ data->response_type_via_service_worker_ = response_type_via_service_worker_;
+ data->security_style_ = security_style_;
+ data->security_details_.protocol = security_details_.protocol.IsolatedCopy();
+ data->security_details_.cipher = security_details_.cipher.IsolatedCopy();
+ data->security_details_.key_exchange =
+ security_details_.key_exchange.IsolatedCopy();
+ data->security_details_.key_exchange_group =
+ security_details_.key_exchange_group.IsolatedCopy();
+ data->security_details_.mac = security_details_.mac.IsolatedCopy();
+ data->security_details_.subject_name =
+ security_details_.subject_name.IsolatedCopy();
+ data->security_details_.san_list = IsolatedCopy(security_details_.san_list);
+ data->security_details_.issuer = security_details_.issuer.IsolatedCopy();
+ data->security_details_.valid_from = security_details_.valid_from;
+ data->security_details_.valid_to = security_details_.valid_to;
+ for (auto& cert : security_details_.certificate)
+ data->certificate_.push_back(cert.GetString().IsolatedCopy());
+ data->security_details_.sct_list = IsolatedCopy(security_details_.sct_list);
+ data->http_version_ = http_version_;
+ data->app_cache_id_ = app_cache_id_;
+ data->app_cache_manifest_url_ = app_cache_manifest_url_.Copy();
+ data->multipart_boundary_ = multipart_boundary_;
+ data->url_list_via_service_worker_.resize(
+ url_list_via_service_worker_.size());
+ std::transform(url_list_via_service_worker_.begin(),
+ url_list_via_service_worker_.end(),
+ data->url_list_via_service_worker_.begin(),
+ [](const KURL& url) { return url.Copy(); });
+ data->cache_storage_cache_name_ = CacheStorageCacheName().IsolatedCopy();
+ data->response_time_ = response_time_;
+ data->encoded_data_length_ = encoded_data_length_;
+ data->encoded_body_length_ = encoded_body_length_;
+ data->decoded_body_length_ = decoded_body_length_;
+ data->downloaded_file_path_ = downloaded_file_path_.IsolatedCopy();
+ data->downloaded_file_handle_ = downloaded_file_handle_;
+
+ // Bug https://bugs.webkit.org/show_bug.cgi?id=60397 this doesn't support
+ // whatever values may be present in the opaque m_extraData structure.
+
+ return data;
+}
+
+bool ResourceResponse::IsHTTP() const {
+ return url_.ProtocolIsInHTTPFamily();
+}
+
+const KURL& ResourceResponse::Url() const {
+ return url_;
+}
+
+void ResourceResponse::SetURL(const KURL& url) {
+ is_null_ = false;
+
+ url_ = url;
+}
+
+const AtomicString& ResourceResponse::MimeType() const {
+ return mime_type_;
+}
+
+void ResourceResponse::SetMimeType(const AtomicString& mime_type) {
+ is_null_ = false;
+
+ // FIXME: MIME type is determined by HTTP Content-Type header. We should
+ // update the header, so that it doesn't disagree with m_mimeType.
+ mime_type_ = mime_type;
+}
+
+long long ResourceResponse::ExpectedContentLength() const {
+ return expected_content_length_;
+}
+
+void ResourceResponse::SetExpectedContentLength(
+ long long expected_content_length) {
+ is_null_ = false;
+
+ // FIXME: Content length is determined by HTTP Content-Length header. We
+ // should update the header, so that it doesn't disagree with
+ // m_expectedContentLength.
+ expected_content_length_ = expected_content_length;
+}
+
+const AtomicString& ResourceResponse::TextEncodingName() const {
+ return text_encoding_name_;
+}
+
+void ResourceResponse::SetTextEncodingName(const AtomicString& encoding_name) {
+ is_null_ = false;
+
+ // FIXME: Text encoding is determined by HTTP Content-Type header. We should
+ // update the header, so that it doesn't disagree with m_textEncodingName.
+ text_encoding_name_ = encoding_name;
+}
+
+int ResourceResponse::HttpStatusCode() const {
+ return http_status_code_;
+}
+
+void ResourceResponse::SetHTTPStatusCode(int status_code) {
+ http_status_code_ = status_code;
+}
+
+const AtomicString& ResourceResponse::HttpStatusText() const {
+ return http_status_text_;
+}
+
+void ResourceResponse::SetHTTPStatusText(const AtomicString& status_text) {
+ http_status_text_ = status_text;
+}
+
+const AtomicString& ResourceResponse::HttpHeaderField(
+ const AtomicString& name) const {
+ return http_header_fields_.Get(name);
+}
+
+void ResourceResponse::UpdateHeaderParsedState(const AtomicString& name) {
+ static const char kAgeHeader[] = "age";
+ static const char kDateHeader[] = "date";
+ static const char kExpiresHeader[] = "expires";
+ static const char kLastModifiedHeader[] = "last-modified";
+
+ if (DeprecatedEqualIgnoringCase(name, kAgeHeader))
+ have_parsed_age_header_ = false;
+ else if (DeprecatedEqualIgnoringCase(name, kCacheControlHeader) ||
+ DeprecatedEqualIgnoringCase(name, kPragmaHeader))
+ cache_control_header_ = CacheControlHeader();
+ else if (DeprecatedEqualIgnoringCase(name, kDateHeader))
+ have_parsed_date_header_ = false;
+ else if (DeprecatedEqualIgnoringCase(name, kExpiresHeader))
+ have_parsed_expires_header_ = false;
+ else if (DeprecatedEqualIgnoringCase(name, kLastModifiedHeader))
+ have_parsed_last_modified_header_ = false;
+}
+
+void ResourceResponse::SetSecurityDetails(
+ const String& protocol,
+ const String& key_exchange,
+ const String& key_exchange_group,
+ const String& cipher,
+ const String& mac,
+ const String& subject_name,
+ const Vector<String>& san_list,
+ const String& issuer,
+ time_t valid_from,
+ time_t valid_to,
+ const Vector<AtomicString>& certificate,
+ const SignedCertificateTimestampList& sct_list) {
+ security_details_.protocol = protocol;
+ security_details_.key_exchange = key_exchange;
+ security_details_.key_exchange_group = key_exchange_group;
+ security_details_.cipher = cipher;
+ security_details_.mac = mac;
+ security_details_.subject_name = subject_name;
+ security_details_.san_list = san_list;
+ security_details_.issuer = issuer;
+ security_details_.valid_from = valid_from;
+ security_details_.valid_to = valid_to;
+ security_details_.certificate = certificate;
+ security_details_.sct_list = sct_list;
+}
+
+void ResourceResponse::SetHTTPHeaderField(const AtomicString& name,
+ const AtomicString& value) {
+ UpdateHeaderParsedState(name);
+
+ http_header_fields_.Set(name, value);
+}
+
+void ResourceResponse::AddHTTPHeaderField(const AtomicString& name,
+ const AtomicString& value) {
+ UpdateHeaderParsedState(name);
+
+ HTTPHeaderMap::AddResult result = http_header_fields_.Add(name, value);
+ if (!result.is_new_entry)
+ result.stored_value->value = result.stored_value->value + ", " + value;
+}
+
+void ResourceResponse::ClearHTTPHeaderField(const AtomicString& name) {
+ http_header_fields_.Remove(name);
+}
+
+const HTTPHeaderMap& ResourceResponse::HttpHeaderFields() const {
+ return http_header_fields_;
+}
+
+bool ResourceResponse::CacheControlContainsNoCache() const {
+ if (!cache_control_header_.parsed) {
+ cache_control_header_ = ParseCacheControlDirectives(
+ http_header_fields_.Get(kCacheControlHeader),
+ http_header_fields_.Get(kPragmaHeader));
+ }
+ return cache_control_header_.contains_no_cache;
+}
+
+bool ResourceResponse::CacheControlContainsNoStore() const {
+ if (!cache_control_header_.parsed) {
+ cache_control_header_ = ParseCacheControlDirectives(
+ http_header_fields_.Get(kCacheControlHeader),
+ http_header_fields_.Get(kPragmaHeader));
+ }
+ return cache_control_header_.contains_no_store;
+}
+
+bool ResourceResponse::CacheControlContainsMustRevalidate() const {
+ if (!cache_control_header_.parsed) {
+ cache_control_header_ = ParseCacheControlDirectives(
+ http_header_fields_.Get(kCacheControlHeader),
+ http_header_fields_.Get(kPragmaHeader));
+ }
+ return cache_control_header_.contains_must_revalidate;
+}
+
+bool ResourceResponse::HasCacheValidatorFields() const {
+ static const char kLastModifiedHeader[] = "last-modified";
+ static const char kETagHeader[] = "etag";
+ return !http_header_fields_.Get(kLastModifiedHeader).IsEmpty() ||
+ !http_header_fields_.Get(kETagHeader).IsEmpty();
+}
+
+double ResourceResponse::CacheControlMaxAge() const {
+ if (!cache_control_header_.parsed) {
+ cache_control_header_ = ParseCacheControlDirectives(
+ http_header_fields_.Get(kCacheControlHeader),
+ http_header_fields_.Get(kPragmaHeader));
+ }
+ return cache_control_header_.max_age;
+}
+
+static double ParseDateValueInHeader(const HTTPHeaderMap& headers,
+ const AtomicString& header_name) {
+ const AtomicString& header_value = headers.Get(header_name);
+ if (header_value.IsEmpty())
+ return std::numeric_limits<double>::quiet_NaN();
+ // This handles all date formats required by RFC2616:
+ // Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
+ // Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
+ // Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
+ double date_in_milliseconds = ParseDate(header_value);
+ if (!std::isfinite(date_in_milliseconds))
+ return std::numeric_limits<double>::quiet_NaN();
+ return date_in_milliseconds / 1000;
+}
+
+double ResourceResponse::Date() const {
+ if (!have_parsed_date_header_) {
+ static const char kHeaderName[] = "date";
+ date_ = ParseDateValueInHeader(http_header_fields_, kHeaderName);
+ have_parsed_date_header_ = true;
+ }
+ return date_;
+}
+
+double ResourceResponse::Age() const {
+ if (!have_parsed_age_header_) {
+ static const char kHeaderName[] = "age";
+ const AtomicString& header_value = http_header_fields_.Get(kHeaderName);
+ bool ok;
+ age_ = header_value.ToDouble(&ok);
+ if (!ok)
+ age_ = std::numeric_limits<double>::quiet_NaN();
+ have_parsed_age_header_ = true;
+ }
+ return age_;
+}
+
+double ResourceResponse::Expires() const {
+ if (!have_parsed_expires_header_) {
+ static const char kHeaderName[] = "expires";
+ expires_ = ParseDateValueInHeader(http_header_fields_, kHeaderName);
+ have_parsed_expires_header_ = true;
+ }
+ return expires_;
+}
+
+double ResourceResponse::LastModified() const {
+ if (!have_parsed_last_modified_header_) {
+ static const char kHeaderName[] = "last-modified";
+ last_modified_ = ParseDateValueInHeader(http_header_fields_, kHeaderName);
+ have_parsed_last_modified_header_ = true;
+ }
+ return last_modified_;
+}
+
+bool ResourceResponse::IsAttachment() const {
+ static const char kAttachmentString[] = "attachment";
+ String value = http_header_fields_.Get(HTTPNames::Content_Disposition);
+ size_t loc = value.find(';');
+ if (loc != kNotFound)
+ value = value.Left(loc);
+ value = value.StripWhiteSpace();
+ return DeprecatedEqualIgnoringCase(value, kAttachmentString);
+}
+
+AtomicString ResourceResponse::HttpContentType() const {
+ return ExtractMIMETypeFromMediaType(
+ HttpHeaderField(HTTPNames::Content_Type).DeprecatedLower());
+}
+
+bool ResourceResponse::WasCached() const {
+ return was_cached_;
+}
+
+void ResourceResponse::SetWasCached(bool value) {
+ was_cached_ = value;
+}
+
+bool ResourceResponse::ConnectionReused() const {
+ return connection_reused_;
+}
+
+void ResourceResponse::SetConnectionReused(bool connection_reused) {
+ connection_reused_ = connection_reused;
+}
+
+unsigned ResourceResponse::ConnectionID() const {
+ return connection_id_;
+}
+
+void ResourceResponse::SetConnectionID(unsigned connection_id) {
+ connection_id_ = connection_id;
+}
+
+ResourceLoadTiming* ResourceResponse::GetResourceLoadTiming() const {
+ return resource_load_timing_.get();
+}
+
+void ResourceResponse::SetResourceLoadTiming(
+ scoped_refptr<ResourceLoadTiming> resource_load_timing) {
+ resource_load_timing_ = std::move(resource_load_timing);
+}
+
+scoped_refptr<ResourceLoadInfo> ResourceResponse::GetResourceLoadInfo() const {
+ return resource_load_info_.get();
+}
+
+void ResourceResponse::SetResourceLoadInfo(
+ scoped_refptr<ResourceLoadInfo> load_info) {
+ resource_load_info_ = std::move(load_info);
+}
+
+void ResourceResponse::SetCTPolicyCompliance(CTPolicyCompliance compliance) {
+ ct_policy_compliance_ = compliance;
+}
+
+bool ResourceResponse::IsOpaqueResponseFromServiceWorker() const {
+ switch (response_type_via_service_worker_) {
+ case network::mojom::FetchResponseType::kBasic:
+ case network::mojom::FetchResponseType::kCORS:
+ case network::mojom::FetchResponseType::kDefault:
+ case network::mojom::FetchResponseType::kError:
+ return false;
+ case network::mojom::FetchResponseType::kOpaque:
+ case network::mojom::FetchResponseType::kOpaqueRedirect:
+ return true;
+ }
+ NOTREACHED();
+ return false;
+}
+
+KURL ResourceResponse::OriginalURLViaServiceWorker() const {
+ if (url_list_via_service_worker_.IsEmpty())
+ return KURL();
+ return url_list_via_service_worker_.back();
+}
+
+AtomicString ResourceResponse::ConnectionInfoString() const {
+ std::string connection_info_string =
+ net::HttpResponseInfo::ConnectionInfoToString(connection_info_);
+ return AtomicString(
+ reinterpret_cast<const LChar*>(connection_info_string.data()),
+ connection_info_string.length());
+}
+
+void ResourceResponse::SetEncodedDataLength(long long value) {
+ encoded_data_length_ = value;
+}
+
+void ResourceResponse::SetEncodedBodyLength(long long value) {
+ encoded_body_length_ = value;
+}
+
+void ResourceResponse::SetDecodedBodyLength(long long value) {
+ decoded_body_length_ = value;
+}
+
+void ResourceResponse::SetDownloadedFilePath(
+ const String& downloaded_file_path) {
+ downloaded_file_path_ = downloaded_file_path;
+ if (downloaded_file_path_.IsEmpty()) {
+ downloaded_file_handle_ = nullptr;
+ return;
+ }
+ // TODO(dmurph): Investigate whether we need the mimeType on this blob.
+ std::unique_ptr<BlobData> blob_data =
+ BlobData::CreateForFileWithUnknownSize(downloaded_file_path_);
+ blob_data->DetachFromCurrentThread();
+ downloaded_file_handle_ = BlobDataHandle::Create(std::move(blob_data), -1);
+}
+
+void ResourceResponse::AppendRedirectResponse(
+ const ResourceResponse& response) {
+ redirect_responses_.push_back(response);
+}
+
+bool ResourceResponse::Compare(const ResourceResponse& a,
+ const ResourceResponse& b) {
+ if (a.IsNull() != b.IsNull())
+ return false;
+ if (a.Url() != b.Url())
+ return false;
+ if (a.MimeType() != b.MimeType())
+ return false;
+ if (a.ExpectedContentLength() != b.ExpectedContentLength())
+ return false;
+ if (a.TextEncodingName() != b.TextEncodingName())
+ return false;
+ if (a.HttpStatusCode() != b.HttpStatusCode())
+ return false;
+ if (a.HttpStatusText() != b.HttpStatusText())
+ return false;
+ if (a.HttpHeaderFields() != b.HttpHeaderFields())
+ return false;
+ if (a.GetResourceLoadTiming() && b.GetResourceLoadTiming() &&
+ *a.GetResourceLoadTiming() == *b.GetResourceLoadTiming())
+ return true;
+ if (a.GetResourceLoadTiming() != b.GetResourceLoadTiming())
+ return false;
+ if (a.EncodedBodyLength() != b.EncodedBodyLength())
+ return false;
+ if (a.DecodedBodyLength() != b.DecodedBodyLength())
+ return false;
+ return true;
+}
+
+STATIC_ASSERT_ENUM(WebURLResponse::kHTTPVersionUnknown,
+ ResourceResponse::kHTTPVersionUnknown);
+STATIC_ASSERT_ENUM(WebURLResponse::kHTTPVersion_0_9,
+ ResourceResponse::kHTTPVersion_0_9);
+STATIC_ASSERT_ENUM(WebURLResponse::kHTTPVersion_1_0,
+ ResourceResponse::kHTTPVersion_1_0);
+STATIC_ASSERT_ENUM(WebURLResponse::kHTTPVersion_1_1,
+ ResourceResponse::kHTTPVersion_1_1);
+STATIC_ASSERT_ENUM(WebURLResponse::kHTTPVersion_2_0,
+ ResourceResponse::kHTTPVersion_2_0);
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_response.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_response.h
new file mode 100644
index 00000000000..d25f83dfaba
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_response.h
@@ -0,0 +1,625 @@
+/*
+ * Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_RESPONSE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_RESPONSE_H_
+
+#include <memory>
+#include <utility>
+
+#include "base/memory/scoped_refptr.h"
+#include "base/time/time.h"
+#include "services/network/public/mojom/fetch_api.mojom-blink.h"
+#include "third_party/blink/public/platform/web_url_response.h"
+#include "third_party/blink/renderer/platform/blob/blob_data.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_load_info.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_load_timing.h"
+#include "third_party/blink/renderer/platform/network/http_header_map.h"
+#include "third_party/blink/renderer/platform/network/http_parsers.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+struct CrossThreadResourceResponseData;
+
+// A ResourceResponse is a "response" object used in blink. Conceptually
+// it is https://fetch.spec.whatwg.org/#concept-response, but it contains
+// a lot of blink specific fields. WebURLResponse is the "public version"
+// of this class and public classes (i.e., classes in public/platform) use it.
+//
+// There are cases where we need to copy a response across threads, and
+// CrossThreadResourceResponseData is a struct for the purpose. When you add a
+// member variable to this class, do not forget to add the corresponding
+// one in CrossThreadResourceResponseData and write copying logic.
+class PLATFORM_EXPORT ResourceResponse final {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ enum HTTPVersion : uint8_t {
+ kHTTPVersionUnknown,
+ kHTTPVersion_0_9,
+ kHTTPVersion_1_0,
+ kHTTPVersion_1_1,
+ kHTTPVersion_2_0
+ };
+ enum SecurityStyle : uint8_t {
+ kSecurityStyleUnknown,
+ kSecurityStyleUnauthenticated,
+ kSecurityStyleAuthenticationBroken,
+ kSecurityStyleAuthenticated
+ };
+
+ enum CTPolicyCompliance {
+ kCTPolicyComplianceDetailsNotAvailable,
+ kCTPolicyComplies,
+ kCTPolicyDoesNotComply
+ };
+
+ class PLATFORM_EXPORT SignedCertificateTimestamp final {
+ public:
+ SignedCertificateTimestamp(String status,
+ String origin,
+ String log_description,
+ String log_id,
+ int64_t timestamp,
+ String hash_algorithm,
+ String signature_algorithm,
+ String signature_data)
+ : status_(status),
+ origin_(origin),
+ log_description_(log_description),
+ log_id_(log_id),
+ timestamp_(timestamp),
+ hash_algorithm_(hash_algorithm),
+ signature_algorithm_(signature_algorithm),
+ signature_data_(signature_data) {}
+ explicit SignedCertificateTimestamp(
+ const struct blink::WebURLResponse::SignedCertificateTimestamp&);
+ SignedCertificateTimestamp IsolatedCopy() const;
+
+ String status_;
+ String origin_;
+ String log_description_;
+ String log_id_;
+ int64_t timestamp_;
+ String hash_algorithm_;
+ String signature_algorithm_;
+ String signature_data_;
+ };
+
+ using SignedCertificateTimestampList =
+ WTF::Vector<SignedCertificateTimestamp>;
+
+ struct SecurityDetails {
+ DISALLOW_NEW();
+ SecurityDetails() : valid_from(0), valid_to(0) {}
+ // All strings are human-readable values.
+ String protocol;
+ // keyExchange is the empty string if not applicable for the connection's
+ // protocol.
+ String key_exchange;
+ // keyExchangeGroup is the empty string if not applicable for the
+ // connection's key exchange.
+ String key_exchange_group;
+ String cipher;
+ // mac is the empty string when the connection cipher suite does not
+ // have a separate MAC value (i.e. if the cipher suite is AEAD).
+ String mac;
+ String subject_name;
+ Vector<String> san_list;
+ String issuer;
+ time_t valid_from;
+ time_t valid_to;
+ // DER-encoded X509Certificate certificate chain.
+ Vector<AtomicString> certificate;
+ SignedCertificateTimestampList sct_list;
+ };
+
+ class ExtraData : public RefCounted<ExtraData> {
+ public:
+ virtual ~ExtraData() = default;
+ };
+
+ explicit ResourceResponse(CrossThreadResourceResponseData*);
+
+ // Gets a copy of the data suitable for passing to another thread.
+ std::unique_ptr<CrossThreadResourceResponseData> CopyData() const;
+
+ ResourceResponse();
+ explicit ResourceResponse(
+ const KURL&,
+ const AtomicString& mime_type = g_null_atom,
+ long long expected_length = 0,
+ const AtomicString& text_encoding_name = g_null_atom);
+ ResourceResponse(const ResourceResponse&);
+ ResourceResponse& operator=(const ResourceResponse&);
+
+ bool IsNull() const { return is_null_; }
+ bool IsHTTP() const;
+
+ // The URL of the resource. Note that if a service worker responded to the
+ // request for this resource, it may have fetched an entirely different URL
+ // and responded with that resource. wasFetchedViaServiceWorker() and
+ // originalURLViaServiceWorker() can be used to determine whether and how a
+ // service worker responded to the request. Example service worker code:
+ //
+ // onfetch = (event => {
+ // if (event.request.url == 'https://abc.com')
+ // event.respondWith(fetch('https://def.com'));
+ // });
+ //
+ // If this service worker responds to an "https://abc.com" request, then for
+ // the resulting ResourceResponse, url() is "https://abc.com",
+ // wasFetchedViaServiceWorker() is true, and originalURLViaServiceWorker() is
+ // "https://def.com".
+ const KURL& Url() const;
+ void SetURL(const KURL&);
+
+ const AtomicString& MimeType() const;
+ void SetMimeType(const AtomicString&);
+
+ long long ExpectedContentLength() const;
+ void SetExpectedContentLength(long long);
+
+ const AtomicString& TextEncodingName() const;
+ void SetTextEncodingName(const AtomicString&);
+
+ int HttpStatusCode() const;
+ void SetHTTPStatusCode(int);
+
+ const AtomicString& HttpStatusText() const;
+ void SetHTTPStatusText(const AtomicString&);
+
+ const AtomicString& HttpHeaderField(const AtomicString& name) const;
+ void SetHTTPHeaderField(const AtomicString& name, const AtomicString& value);
+ void AddHTTPHeaderField(const AtomicString& name, const AtomicString& value);
+ void ClearHTTPHeaderField(const AtomicString& name);
+ const HTTPHeaderMap& HttpHeaderFields() const;
+
+ bool IsMultipart() const { return MimeType() == "multipart/x-mixed-replace"; }
+
+ bool IsAttachment() const;
+
+ AtomicString HttpContentType() const;
+
+ // These functions return parsed values of the corresponding response headers.
+ // NaN means that the header was not present or had invalid value.
+ bool CacheControlContainsNoCache() const;
+ bool CacheControlContainsNoStore() const;
+ bool CacheControlContainsMustRevalidate() const;
+ bool HasCacheValidatorFields() const;
+ double CacheControlMaxAge() const;
+ double Date() const;
+ double Age() const;
+ double Expires() const;
+ double LastModified() const;
+
+ unsigned ConnectionID() const;
+ void SetConnectionID(unsigned);
+
+ bool ConnectionReused() const;
+ void SetConnectionReused(bool);
+
+ bool WasCached() const;
+ void SetWasCached(bool);
+
+ ResourceLoadTiming* GetResourceLoadTiming() const;
+ void SetResourceLoadTiming(scoped_refptr<ResourceLoadTiming>);
+
+ scoped_refptr<ResourceLoadInfo> GetResourceLoadInfo() const;
+ void SetResourceLoadInfo(scoped_refptr<ResourceLoadInfo>);
+
+ HTTPVersion HttpVersion() const { return http_version_; }
+ void SetHTTPVersion(HTTPVersion version) { http_version_ = version; }
+
+ bool HasMajorCertificateErrors() const {
+ return has_major_certificate_errors_;
+ }
+ void SetHasMajorCertificateErrors(bool has_major_certificate_errors) {
+ has_major_certificate_errors_ = has_major_certificate_errors;
+ }
+
+ CTPolicyCompliance GetCTPolicyCompliance() const {
+ return ct_policy_compliance_;
+ }
+ void SetCTPolicyCompliance(CTPolicyCompliance);
+
+ bool IsLegacySymantecCert() const { return is_legacy_symantec_cert_; }
+ void SetIsLegacySymantecCert(bool is_legacy_symantec_cert) {
+ is_legacy_symantec_cert_ = is_legacy_symantec_cert;
+ }
+
+ SecurityStyle GetSecurityStyle() const { return security_style_; }
+ void SetSecurityStyle(SecurityStyle security_style) {
+ security_style_ = security_style;
+ }
+
+ const SecurityDetails* GetSecurityDetails() const {
+ return &security_details_;
+ }
+ void SetSecurityDetails(const String& protocol,
+ const String& key_exchange,
+ const String& key_exchange_group,
+ const String& cipher,
+ const String& mac,
+ const String& subject_name,
+ const Vector<String>& san_list,
+ const String& issuer,
+ time_t valid_from,
+ time_t valid_to,
+ const Vector<AtomicString>& certificate,
+ const SignedCertificateTimestampList& sct_list);
+
+ long long AppCacheID() const { return app_cache_id_; }
+ void SetAppCacheID(long long id) { app_cache_id_ = id; }
+
+ const KURL& AppCacheManifestURL() const { return app_cache_manifest_url_; }
+ void SetAppCacheManifestURL(const KURL& url) {
+ app_cache_manifest_url_ = url;
+ }
+
+ bool WasFetchedViaSPDY() const { return was_fetched_via_spdy_; }
+ void SetWasFetchedViaSPDY(bool value) { was_fetched_via_spdy_ = value; }
+
+ // See ServiceWorkerResponseInfo::was_fetched_via_service_worker.
+ bool WasFetchedViaServiceWorker() const {
+ return was_fetched_via_service_worker_;
+ }
+ void SetWasFetchedViaServiceWorker(bool value) {
+ was_fetched_via_service_worker_ = value;
+ }
+
+ // See ServiceWorkerResponseInfo::was_fallback_required.
+ bool WasFallbackRequiredByServiceWorker() const {
+ return was_fallback_required_by_service_worker_;
+ }
+ void SetWasFallbackRequiredByServiceWorker(bool value) {
+ was_fallback_required_by_service_worker_ = value;
+ }
+
+ network::mojom::FetchResponseType ResponseTypeViaServiceWorker() const {
+ return response_type_via_service_worker_;
+ }
+ void SetResponseTypeViaServiceWorker(
+ network::mojom::FetchResponseType value) {
+ response_type_via_service_worker_ = value;
+ }
+ bool IsOpaqueResponseFromServiceWorker() const;
+
+ // See ServiceWorkerResponseInfo::url_list_via_service_worker.
+ const Vector<KURL>& UrlListViaServiceWorker() const {
+ return url_list_via_service_worker_;
+ }
+ void SetURLListViaServiceWorker(const Vector<KURL>& url_list) {
+ url_list_via_service_worker_ = url_list;
+ }
+
+ // Returns the last URL of urlListViaServiceWorker if exists. Otherwise
+ // returns an empty URL.
+ KURL OriginalURLViaServiceWorker() const;
+
+ const Vector<char>& MultipartBoundary() const { return multipart_boundary_; }
+ void SetMultipartBoundary(const char* bytes, size_t size) {
+ multipart_boundary_.clear();
+ multipart_boundary_.Append(bytes, size);
+ }
+
+ const String& CacheStorageCacheName() const {
+ return cache_storage_cache_name_;
+ }
+ void SetCacheStorageCacheName(const String& cache_storage_cache_name) {
+ cache_storage_cache_name_ = cache_storage_cache_name;
+ }
+
+ const Vector<String>& CorsExposedHeaderNames() const {
+ return cors_exposed_header_names_;
+ }
+ void SetCorsExposedHeaderNames(const Vector<String>& header_names) {
+ cors_exposed_header_names_ = header_names;
+ }
+
+ bool DidServiceWorkerNavigationPreload() const {
+ return did_service_worker_navigation_preload_;
+ }
+ void SetDidServiceWorkerNavigationPreload(bool value) {
+ did_service_worker_navigation_preload_ = value;
+ }
+
+ Time ResponseTime() const { return response_time_; }
+ void SetResponseTime(Time response_time) { response_time_ = response_time; }
+
+ const AtomicString& RemoteIPAddress() const { return remote_ip_address_; }
+ void SetRemoteIPAddress(const AtomicString& value) {
+ remote_ip_address_ = value;
+ }
+
+ unsigned short RemotePort() const { return remote_port_; }
+ void SetRemotePort(unsigned short value) { remote_port_ = value; }
+
+ const AtomicString& AlpnNegotiatedProtocol() const {
+ return alpn_negotiated_protocol_;
+ }
+ void SetAlpnNegotiatedProtocol(const AtomicString& value) {
+ alpn_negotiated_protocol_ = value;
+ }
+
+ net::HttpResponseInfo::ConnectionInfo ConnectionInfo() const {
+ return connection_info_;
+ }
+ void SetConnectionInfo(net::HttpResponseInfo::ConnectionInfo value) {
+ connection_info_ = value;
+ }
+
+ AtomicString ConnectionInfoString() const;
+
+ long long EncodedDataLength() const { return encoded_data_length_; }
+ void SetEncodedDataLength(long long value);
+
+ long long EncodedBodyLength() const { return encoded_body_length_; }
+ void SetEncodedBodyLength(long long value);
+
+ long long DecodedBodyLength() const { return decoded_body_length_; }
+ void SetDecodedBodyLength(long long value);
+
+ const String& DownloadedFilePath() const { return downloaded_file_path_; }
+ void SetDownloadedFilePath(const String&);
+
+ // Extra data associated with this response.
+ ExtraData* GetExtraData() const { return extra_data_.get(); }
+ void SetExtraData(scoped_refptr<ExtraData> extra_data) {
+ extra_data_ = std::move(extra_data);
+ }
+
+ unsigned MemoryUsage() const {
+ // average size, mostly due to URL and Header Map strings
+ return 1280;
+ }
+
+ // PlzNavigate: Even if there is redirections, only one
+ // ResourceResponse is built: the final response.
+ // The redirect response chain can be accessed by this function.
+ const Vector<ResourceResponse>& RedirectResponses() const {
+ return redirect_responses_;
+ }
+ void AppendRedirectResponse(const ResourceResponse&);
+
+ // This method doesn't compare the all members.
+ static bool Compare(const ResourceResponse&, const ResourceResponse&);
+
+ private:
+ void UpdateHeaderParsedState(const AtomicString& name);
+
+ KURL url_;
+ AtomicString mime_type_;
+ long long expected_content_length_;
+ AtomicString text_encoding_name_;
+
+ unsigned connection_id_ = 0;
+ int http_status_code_ = 0;
+ AtomicString http_status_text_;
+ HTTPHeaderMap http_header_fields_;
+
+ // Remote IP address of the socket which fetched this resource.
+ AtomicString remote_ip_address_;
+
+ // Remote port number of the socket which fetched this resource.
+ unsigned short remote_port_ = 0;
+
+ bool was_cached_ = false;
+ bool connection_reused_ = false;
+ bool is_null_;
+ mutable bool have_parsed_age_header_ = false;
+ mutable bool have_parsed_date_header_ = false;
+ mutable bool have_parsed_expires_header_ = false;
+ mutable bool have_parsed_last_modified_header_ = false;
+
+ // True if the resource was retrieved by the embedder in spite of
+ // certificate errors.
+ bool has_major_certificate_errors_ = false;
+
+ // The Certificate Transparency policy compliance status of the resource.
+ CTPolicyCompliance ct_policy_compliance_ =
+ kCTPolicyComplianceDetailsNotAvailable;
+
+ // True if the resource was retrieved with a legacy Symantec certificate which
+ // is slated for distrust in future.
+ bool is_legacy_symantec_cert_ = false;
+
+ // The time at which the resource's certificate expires. Null if there was no
+ // certificate.
+ base::Time cert_validity_start_;
+
+ // Was the resource fetched over SPDY. See http://dev.chromium.org/spdy
+ bool was_fetched_via_spdy_ = false;
+
+ // Was the resource fetched over an explicit proxy (HTTP, SOCKS, etc).
+ bool was_fetched_via_proxy_ = false;
+
+ // Was the resource fetched over a ServiceWorker.
+ bool was_fetched_via_service_worker_ = false;
+
+ // Was the fallback request with skip service worker flag required.
+ bool was_fallback_required_by_service_worker_ = false;
+
+ // True if service worker navigation preload was performed due to
+ // the request for this resource.
+ bool did_service_worker_navigation_preload_ = false;
+
+ // The type of the response which was returned by the ServiceWorker.
+ network::mojom::FetchResponseType response_type_via_service_worker_ =
+ network::mojom::FetchResponseType::kDefault;
+
+ // HTTP version used in the response, if known.
+ HTTPVersion http_version_ = kHTTPVersionUnknown;
+
+ // The security style of the resource.
+ // This only contains a valid value when the DevTools Network domain is
+ // enabled. (Otherwise, it contains a default value of Unknown.)
+ SecurityStyle security_style_ = kSecurityStyleUnknown;
+
+ // Security details of this request's connection.
+ // If m_securityStyle is Unknown or Unauthenticated, this does not contain
+ // valid data.
+ SecurityDetails security_details_;
+
+ scoped_refptr<ResourceLoadTiming> resource_load_timing_;
+ scoped_refptr<ResourceLoadInfo> resource_load_info_;
+
+ mutable CacheControlHeader cache_control_header_;
+
+ mutable double age_ = 0.0;
+ mutable double date_ = 0.0;
+ mutable double expires_ = 0.0;
+ mutable double last_modified_ = 0.0;
+
+ // The id of the appcache this response was retrieved from, or zero if
+ // the response was not retrieved from an appcache.
+ long long app_cache_id_ = 0;
+
+ // The manifest url of the appcache this response was retrieved from, if any.
+ // Note: only valid for main resource responses.
+ KURL app_cache_manifest_url_;
+
+ // The multipart boundary of this response.
+ Vector<char> multipart_boundary_;
+
+ // The URL list of the response which was fetched by the ServiceWorker.
+ // This is empty if the response was created inside the ServiceWorker.
+ Vector<KURL> url_list_via_service_worker_;
+
+ // The cache name of the CacheStorage from where the response is served via
+ // the ServiceWorker. Null if the response isn't from the CacheStorage.
+ String cache_storage_cache_name_;
+
+ // The headers that should be exposed according to CORS. Only guaranteed
+ // to be set if the response was fetched by a ServiceWorker.
+ Vector<String> cors_exposed_header_names_;
+
+ // The time at which the response headers were received. For cached
+ // responses, this time could be "far" in the past.
+ Time response_time_;
+
+ // ALPN negotiated protocol of the socket which fetched this resource.
+ AtomicString alpn_negotiated_protocol_;
+
+ // Information about the type of connection used to fetch this resource.
+ net::HttpResponseInfo::ConnectionInfo connection_info_ =
+ net::HttpResponseInfo::ConnectionInfo::CONNECTION_INFO_UNKNOWN;
+
+ // Size of the response in bytes prior to decompression.
+ long long encoded_data_length_ = 0;
+
+ // Size of the response body in bytes prior to decompression.
+ long long encoded_body_length_ = 0;
+
+ // Sizes of the response body in bytes after any content-encoding is
+ // removed.
+ long long decoded_body_length_ = 0;
+
+ // The downloaded file path if the load streamed to a file.
+ String downloaded_file_path_;
+
+ // The handle to the downloaded file to ensure the underlying file will not
+ // be deleted.
+ scoped_refptr<BlobDataHandle> downloaded_file_handle_;
+
+ // ExtraData associated with the response.
+ scoped_refptr<ExtraData> extra_data_;
+
+ // PlzNavigate: the redirect responses are transmitted
+ // inside the final response.
+ Vector<ResourceResponse> redirect_responses_;
+};
+
+inline bool operator==(const ResourceResponse& a, const ResourceResponse& b) {
+ return ResourceResponse::Compare(a, b);
+}
+inline bool operator!=(const ResourceResponse& a, const ResourceResponse& b) {
+ return !(a == b);
+}
+
+// This class is needed to copy a ResourceResponse across threads, because it
+// has some members which cannot be transferred across threads (AtomicString
+// for example).
+// There are some rules / restrictions:
+// - This struct cannot contain an object that cannot be transferred across
+// threads (e.g., AtomicString)
+// - Non-simple members need explicit copying (e.g., String::IsolatedCopy,
+// KURL::Copy) rather than the copy constructor or the assignment operator.
+struct CrossThreadResourceResponseData {
+ WTF_MAKE_NONCOPYABLE(CrossThreadResourceResponseData);
+ USING_FAST_MALLOC(CrossThreadResourceResponseData);
+
+ public:
+ CrossThreadResourceResponseData() = default;
+ KURL url_;
+ String mime_type_;
+ long long expected_content_length_;
+ String text_encoding_name_;
+ int http_status_code_;
+ String http_status_text_;
+ std::unique_ptr<CrossThreadHTTPHeaderMapData> http_headers_;
+ scoped_refptr<ResourceLoadTiming> resource_load_timing_;
+ bool has_major_certificate_errors_;
+ ResourceResponse::CTPolicyCompliance ct_policy_compliance_;
+ bool is_legacy_symantec_cert_;
+ base::Time cert_validity_start_;
+ ResourceResponse::SecurityStyle security_style_;
+ ResourceResponse::SecurityDetails security_details_;
+ // This is |certificate| from SecurityDetails since that structure should
+ // use an AtomicString but this temporary structure is sent across threads.
+ Vector<String> certificate_;
+ ResourceResponse::HTTPVersion http_version_;
+ long long app_cache_id_;
+ KURL app_cache_manifest_url_;
+ Vector<char> multipart_boundary_;
+ bool was_fetched_via_spdy_;
+ bool was_fetched_via_proxy_;
+ bool was_fetched_via_service_worker_;
+ bool was_fallback_required_by_service_worker_;
+ network::mojom::FetchResponseType response_type_via_service_worker_;
+ Vector<KURL> url_list_via_service_worker_;
+ String cache_storage_cache_name_;
+ bool did_service_worker_navigation_preload_;
+ Time response_time_;
+ String remote_ip_address_;
+ unsigned short remote_port_;
+ long long encoded_data_length_;
+ long long encoded_body_length_;
+ long long decoded_body_length_;
+ String downloaded_file_path_;
+ scoped_refptr<BlobDataHandle> downloaded_file_handle_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_RESPONSE_H_
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_response_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_response_test.cc
new file mode 100644
index 00000000000..c22bfd36a9e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_response_test.cc
@@ -0,0 +1,87 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_thread.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
+#include "third_party/blink/renderer/platform/web_task_runner.h"
+
+namespace blink {
+
+namespace {
+
+ResourceResponse CreateTestResponse() {
+ ResourceResponse response;
+ response.AddHTTPHeaderField("age", "0");
+ response.AddHTTPHeaderField("cache-control", "no-cache");
+ response.AddHTTPHeaderField("date", "Tue, 17 Jan 2017 04:01:00 GMT");
+ response.AddHTTPHeaderField("expires", "Tue, 17 Jan 2017 04:11:00 GMT");
+ response.AddHTTPHeaderField("last-modified", "Tue, 17 Jan 2017 04:00:00 GMT");
+ response.AddHTTPHeaderField("pragma", "public");
+ response.AddHTTPHeaderField("etag", "abc");
+ response.AddHTTPHeaderField("content-disposition",
+ "attachment; filename=a.txt");
+ return response;
+}
+
+void RunHeaderRelatedTest(const ResourceResponse& response) {
+ EXPECT_EQ(0, response.Age());
+ EXPECT_NE(0, response.Date());
+ EXPECT_NE(0, response.Expires());
+ EXPECT_NE(0, response.LastModified());
+ EXPECT_EQ(true, response.CacheControlContainsNoCache());
+}
+
+void RunInThread() {
+ ResourceResponse response(CreateTestResponse());
+ RunHeaderRelatedTest(response);
+}
+
+} // namespace
+
+TEST(ResourceResponseTest, SignedCertificateTimestampIsolatedCopy) {
+ ResourceResponse::SignedCertificateTimestamp src(
+ "status", "origin", "logDescription", "logId", 7, "hashAlgorithm",
+ "signatureAlgorithm", "signatureData");
+
+ ResourceResponse::SignedCertificateTimestamp dest = src.IsolatedCopy();
+
+ EXPECT_EQ(src.status_, dest.status_);
+ EXPECT_NE(src.status_.Impl(), dest.status_.Impl());
+ EXPECT_EQ(src.origin_, dest.origin_);
+ EXPECT_NE(src.origin_.Impl(), dest.origin_.Impl());
+ EXPECT_EQ(src.log_description_, dest.log_description_);
+ EXPECT_NE(src.log_description_.Impl(), dest.log_description_.Impl());
+ EXPECT_EQ(src.log_id_, dest.log_id_);
+ EXPECT_NE(src.log_id_.Impl(), dest.log_id_.Impl());
+ EXPECT_EQ(src.timestamp_, dest.timestamp_);
+ EXPECT_EQ(src.hash_algorithm_, dest.hash_algorithm_);
+ EXPECT_NE(src.hash_algorithm_.Impl(), dest.hash_algorithm_.Impl());
+ EXPECT_EQ(src.signature_algorithm_, dest.signature_algorithm_);
+ EXPECT_NE(src.signature_algorithm_.Impl(), dest.signature_algorithm_.Impl());
+ EXPECT_EQ(src.signature_data_, dest.signature_data_);
+ EXPECT_NE(src.signature_data_.Impl(), dest.signature_data_.Impl());
+}
+
+// This test checks that AtomicStrings in ResourceResponse doesn't cause the
+// failure of ThreadRestrictionVerifier check.
+TEST(ResourceResponseTest, CrossThreadAtomicStrings) {
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform;
+
+ ResourceResponse response(CreateTestResponse());
+ RunHeaderRelatedTest(response);
+ std::unique_ptr<WebThread> thread = Platform::Current()->CreateThread(
+ WebThreadCreationParams(WebThreadType::kTestThread)
+ .SetThreadNameForTest("WorkerThread"));
+ PostCrossThreadTask(*thread->GetTaskRunner(), FROM_HERE,
+ CrossThreadBind(&RunInThread));
+ thread.reset();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_status.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_status.h
new file mode 100644
index 00000000000..28773d48bf2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_status.h
@@ -0,0 +1,20 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_STATUS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_STATUS_H_
+
+namespace blink {
+
+enum class ResourceStatus : uint8_t {
+ kNotStarted,
+ kPending, // load in progress
+ kCached, // load completed successfully
+ kLoadError,
+ kDecodeError
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_test.cc
new file mode 100644
index 00000000000..453c1c32137
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_test.cc
@@ -0,0 +1,424 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/loader/fetch/resource.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/loader/fetch/memory_cache.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
+#include "third_party/blink/renderer/platform/loader/testing/mock_resource.h"
+#include "third_party/blink/renderer/platform/loader/testing/mock_resource_client.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
+#include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+namespace {
+
+class MockPlatform final : public TestingPlatformSupportWithMockScheduler {
+ public:
+ MockPlatform() = default;
+ ~MockPlatform() override = default;
+
+ // From blink::Platform:
+ void CacheMetadata(const WebURL& url, Time, const char*, size_t) override {
+ cached_urls_.push_back(url);
+ }
+
+ const Vector<WebURL>& CachedURLs() const { return cached_urls_; }
+
+ private:
+ Vector<WebURL> cached_urls_;
+};
+
+ResourceResponse CreateTestResourceResponse() {
+ ResourceResponse response(URLTestHelpers::ToKURL("https://example.com/"));
+ response.SetHTTPStatusCode(200);
+ return response;
+}
+
+void CreateTestResourceAndSetCachedMetadata(const ResourceResponse& response) {
+ const char kTestData[] = "test data";
+ MockResource* resource = MockResource::Create(response.Url());
+ resource->SetResponse(response);
+ resource->SendCachedMetadata(kTestData, sizeof(kTestData));
+ return;
+}
+
+} // anonymous namespace
+
+TEST(ResourceTest, SetCachedMetadata_SendsMetadataToPlatform) {
+ ScopedTestingPlatformSupport<MockPlatform> mock;
+ ResourceResponse response(CreateTestResourceResponse());
+ CreateTestResourceAndSetCachedMetadata(response);
+ EXPECT_EQ(1u, mock->CachedURLs().size());
+}
+
+TEST(
+ ResourceTest,
+ SetCachedMetadata_DoesNotSendMetadataToPlatformWhenFetchedViaServiceWorker) {
+ ScopedTestingPlatformSupport<MockPlatform> mock;
+ ResourceResponse response(CreateTestResourceResponse());
+ response.SetWasFetchedViaServiceWorker(true);
+ CreateTestResourceAndSetCachedMetadata(response);
+ EXPECT_EQ(0u, mock->CachedURLs().size());
+}
+
+TEST(ResourceTest, RevalidateWithFragment) {
+ ScopedTestingPlatformSupport<MockPlatform> mock;
+ KURL url("http://127.0.0.1:8000/foo.html");
+ ResourceResponse response(url);
+ response.SetHTTPStatusCode(200);
+ MockResource* resource = MockResource::Create(url);
+ resource->ResponseReceived(response, nullptr);
+ resource->FinishForTest();
+
+ // Revalidating with a url that differs by only the fragment
+ // shouldn't trigger a securiy check.
+ url.SetFragmentIdentifier("bar");
+ resource->SetRevalidatingRequest(ResourceRequest(url));
+ ResourceResponse revalidating_response(url);
+ revalidating_response.SetHTTPStatusCode(304);
+ resource->ResponseReceived(revalidating_response, nullptr);
+}
+
+TEST(ResourceTest, Vary) {
+ ScopedTestingPlatformSupport<MockPlatform> mock;
+ const KURL url("http://127.0.0.1:8000/foo.html");
+ ResourceResponse response(url);
+ response.SetHTTPStatusCode(200);
+
+ MockResource* resource = MockResource::Create(url);
+ resource->ResponseReceived(response, nullptr);
+ resource->FinishForTest();
+
+ ResourceRequest new_request(url);
+ EXPECT_FALSE(resource->MustReloadDueToVaryHeader(new_request));
+
+ response.SetHTTPHeaderField(HTTPNames::Vary, "*");
+ resource->SetResponse(response);
+ EXPECT_TRUE(resource->MustReloadDueToVaryHeader(new_request));
+
+ // Irrelevant header
+ response.SetHTTPHeaderField(HTTPNames::Vary, "definitelynotarealheader");
+ resource->SetResponse(response);
+ EXPECT_FALSE(resource->MustReloadDueToVaryHeader(new_request));
+
+ // Header present on new but not old
+ new_request.SetHTTPHeaderField(HTTPNames::User_Agent, "something");
+ response.SetHTTPHeaderField(HTTPNames::Vary, HTTPNames::User_Agent);
+ resource->SetResponse(response);
+ EXPECT_TRUE(resource->MustReloadDueToVaryHeader(new_request));
+ new_request.ClearHTTPHeaderField(HTTPNames::User_Agent);
+
+ ResourceRequest old_request(url);
+ old_request.SetHTTPHeaderField(HTTPNames::User_Agent, "something");
+ old_request.SetHTTPHeaderField(HTTPNames::Referer, "http://foo.com");
+ resource = MockResource::Create(old_request);
+ resource->ResponseReceived(response, nullptr);
+ resource->FinishForTest();
+
+ // Header present on old but not new
+ new_request.ClearHTTPHeaderField(HTTPNames::User_Agent);
+ response.SetHTTPHeaderField(HTTPNames::Vary, HTTPNames::User_Agent);
+ resource->SetResponse(response);
+ EXPECT_TRUE(resource->MustReloadDueToVaryHeader(new_request));
+
+ // Header present on both
+ new_request.SetHTTPHeaderField(HTTPNames::User_Agent, "something");
+ EXPECT_FALSE(resource->MustReloadDueToVaryHeader(new_request));
+
+ // One matching, one mismatching
+ response.SetHTTPHeaderField(HTTPNames::Vary, "User-Agent, Referer");
+ resource->SetResponse(response);
+ EXPECT_TRUE(resource->MustReloadDueToVaryHeader(new_request));
+
+ // Two matching
+ new_request.SetHTTPHeaderField(HTTPNames::Referer, "http://foo.com");
+ EXPECT_FALSE(resource->MustReloadDueToVaryHeader(new_request));
+}
+
+TEST(ResourceTest, RevalidationFailed) {
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform_;
+ const KURL url("http://test.example.com/");
+ MockResource* resource = MockResource::Create(ResourceRequest(url));
+ ResourceResponse response(url);
+ response.SetHTTPStatusCode(200);
+ resource->ResponseReceived(response, nullptr);
+ const char kData[5] = "abcd";
+ resource->AppendData(kData, 4);
+ resource->FinishForTest();
+ GetMemoryCache()->Add(resource);
+
+ MockCacheHandler* original_cache_handler = resource->CacheHandler();
+ EXPECT_TRUE(original_cache_handler);
+
+ // Simulate revalidation start.
+ resource->SetRevalidatingRequest(ResourceRequest(url));
+
+ EXPECT_EQ(original_cache_handler, resource->CacheHandler());
+
+ Persistent<MockResourceClient> client = new MockResourceClient;
+ resource->AddClient(client, nullptr);
+
+ ResourceResponse revalidating_response(url);
+ revalidating_response.SetHTTPStatusCode(200);
+ resource->ResponseReceived(revalidating_response, nullptr);
+
+ EXPECT_FALSE(resource->IsCacheValidator());
+ EXPECT_EQ(200, resource->GetResponse().HttpStatusCode());
+ EXPECT_FALSE(resource->ResourceBuffer());
+ EXPECT_TRUE(resource->CacheHandler());
+ EXPECT_NE(original_cache_handler, resource->CacheHandler());
+ EXPECT_EQ(resource, GetMemoryCache()->ResourceForURL(url));
+
+ resource->AppendData(kData, 4);
+
+ EXPECT_FALSE(client->NotifyFinishedCalled());
+
+ resource->FinishForTest();
+
+ EXPECT_TRUE(client->NotifyFinishedCalled());
+
+ resource->RemoveClient(client);
+ EXPECT_FALSE(resource->IsAlive());
+}
+
+TEST(ResourceTest, RevalidationSucceeded) {
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform_;
+ const KURL url("http://test.example.com/");
+ MockResource* resource = MockResource::Create(ResourceRequest(url));
+ ResourceResponse response(url);
+ response.SetHTTPStatusCode(200);
+ resource->ResponseReceived(response, nullptr);
+ const char kData[5] = "abcd";
+ resource->AppendData(kData, 4);
+ resource->FinishForTest();
+ GetMemoryCache()->Add(resource);
+
+ MockCacheHandler* original_cache_handler = resource->CacheHandler();
+ EXPECT_TRUE(original_cache_handler);
+
+ // Simulate a successful revalidation.
+ resource->SetRevalidatingRequest(ResourceRequest(url));
+
+ EXPECT_EQ(original_cache_handler, resource->CacheHandler());
+
+ Persistent<MockResourceClient> client = new MockResourceClient;
+ resource->AddClient(client, nullptr);
+
+ ResourceResponse revalidating_response(url);
+ revalidating_response.SetHTTPStatusCode(304);
+ resource->ResponseReceived(revalidating_response, nullptr);
+
+ EXPECT_FALSE(resource->IsCacheValidator());
+ EXPECT_EQ(200, resource->GetResponse().HttpStatusCode());
+ EXPECT_EQ(4u, resource->ResourceBuffer()->size());
+ EXPECT_EQ(original_cache_handler, resource->CacheHandler());
+ EXPECT_EQ(resource, GetMemoryCache()->ResourceForURL(url));
+
+ GetMemoryCache()->Remove(resource);
+
+ resource->RemoveClient(client);
+ EXPECT_FALSE(resource->IsAlive());
+ EXPECT_FALSE(client->NotifyFinishedCalled());
+}
+
+TEST(ResourceTest, RevalidationSucceededForResourceWithoutBody) {
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform_;
+ const KURL url("http://test.example.com/");
+ Resource* resource = MockResource::Create(ResourceRequest(url));
+ ResourceResponse response(url);
+ response.SetHTTPStatusCode(200);
+ resource->ResponseReceived(response, nullptr);
+ resource->FinishForTest();
+ GetMemoryCache()->Add(resource);
+
+ // Simulate a successful revalidation.
+ resource->SetRevalidatingRequest(ResourceRequest(url));
+
+ Persistent<MockResourceClient> client = new MockResourceClient;
+ resource->AddClient(client, nullptr);
+
+ ResourceResponse revalidating_response(url);
+ revalidating_response.SetHTTPStatusCode(304);
+ resource->ResponseReceived(revalidating_response, nullptr);
+ EXPECT_FALSE(resource->IsCacheValidator());
+ EXPECT_EQ(200, resource->GetResponse().HttpStatusCode());
+ EXPECT_FALSE(resource->ResourceBuffer());
+ EXPECT_EQ(resource, GetMemoryCache()->ResourceForURL(url));
+ GetMemoryCache()->Remove(resource);
+
+ resource->RemoveClient(client);
+ EXPECT_FALSE(resource->IsAlive());
+ EXPECT_FALSE(client->NotifyFinishedCalled());
+}
+
+TEST(ResourceTest, RevalidationSucceededUpdateHeaders) {
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform_;
+ const KURL url("http://test.example.com/");
+ Resource* resource = MockResource::Create(ResourceRequest(url));
+ ResourceResponse response(url);
+ response.SetHTTPStatusCode(200);
+ response.AddHTTPHeaderField("keep-alive", "keep-alive value");
+ response.AddHTTPHeaderField("expires", "expires value");
+ response.AddHTTPHeaderField("last-modified", "last-modified value");
+ response.AddHTTPHeaderField("proxy-authenticate", "proxy-authenticate value");
+ response.AddHTTPHeaderField("proxy-connection", "proxy-connection value");
+ response.AddHTTPHeaderField("x-custom", "custom value");
+ resource->ResponseReceived(response, nullptr);
+ resource->FinishForTest();
+ GetMemoryCache()->Add(resource);
+
+ // Simulate a successful revalidation.
+ resource->SetRevalidatingRequest(ResourceRequest(url));
+
+ // Validate that these headers pre-update.
+ EXPECT_EQ("keep-alive value",
+ resource->GetResponse().HttpHeaderField("keep-alive"));
+ EXPECT_EQ("expires value",
+ resource->GetResponse().HttpHeaderField("expires"));
+ EXPECT_EQ("last-modified value",
+ resource->GetResponse().HttpHeaderField("last-modified"));
+ EXPECT_EQ("proxy-authenticate value",
+ resource->GetResponse().HttpHeaderField("proxy-authenticate"));
+ EXPECT_EQ("proxy-authenticate value",
+ resource->GetResponse().HttpHeaderField("proxy-authenticate"));
+ EXPECT_EQ("proxy-connection value",
+ resource->GetResponse().HttpHeaderField("proxy-connection"));
+ EXPECT_EQ("custom value",
+ resource->GetResponse().HttpHeaderField("x-custom"));
+
+ Persistent<MockResourceClient> client = new MockResourceClient;
+ resource->AddClient(client, nullptr);
+
+ // Perform a revalidation step.
+ ResourceResponse revalidating_response(url);
+ revalidating_response.SetHTTPStatusCode(304);
+ // Headers that aren't copied with an 304 code.
+ revalidating_response.AddHTTPHeaderField("keep-alive", "garbage");
+ revalidating_response.AddHTTPHeaderField("expires", "garbage");
+ revalidating_response.AddHTTPHeaderField("last-modified", "garbage");
+ revalidating_response.AddHTTPHeaderField("proxy-authenticate", "garbage");
+ revalidating_response.AddHTTPHeaderField("proxy-connection", "garbage");
+ // Header that is updated with 304 code.
+ revalidating_response.AddHTTPHeaderField("x-custom", "updated");
+ resource->ResponseReceived(revalidating_response, nullptr);
+
+ // Validate the original response.
+ EXPECT_EQ(200, resource->GetResponse().HttpStatusCode());
+
+ // Validate that these headers are not updated.
+ EXPECT_EQ("keep-alive value",
+ resource->GetResponse().HttpHeaderField("keep-alive"));
+ EXPECT_EQ("expires value",
+ resource->GetResponse().HttpHeaderField("expires"));
+ EXPECT_EQ("last-modified value",
+ resource->GetResponse().HttpHeaderField("last-modified"));
+ EXPECT_EQ("proxy-authenticate value",
+ resource->GetResponse().HttpHeaderField("proxy-authenticate"));
+ EXPECT_EQ("proxy-authenticate value",
+ resource->GetResponse().HttpHeaderField("proxy-authenticate"));
+ EXPECT_EQ("proxy-connection value",
+ resource->GetResponse().HttpHeaderField("proxy-connection"));
+ EXPECT_EQ("updated", resource->GetResponse().HttpHeaderField("x-custom"));
+
+ resource->RemoveClient(client);
+ EXPECT_FALSE(resource->IsAlive());
+ EXPECT_FALSE(client->NotifyFinishedCalled());
+}
+
+TEST(ResourceTest, RedirectDuringRevalidation) {
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform_;
+ const KURL url("http://test.example.com/1");
+ const KURL redirect_target_url("http://test.example.com/2");
+
+ MockResource* resource = MockResource::Create(ResourceRequest(url));
+ ResourceResponse response(url);
+ response.SetHTTPStatusCode(200);
+ resource->ResponseReceived(response, nullptr);
+ const char kData[5] = "abcd";
+ resource->AppendData(kData, 4);
+ resource->FinishForTest();
+ GetMemoryCache()->Add(resource);
+
+ EXPECT_FALSE(resource->IsCacheValidator());
+ EXPECT_EQ(url, resource->GetResourceRequest().Url());
+ EXPECT_EQ(url, resource->LastResourceRequest().Url());
+
+ MockCacheHandler* original_cache_handler = resource->CacheHandler();
+ EXPECT_TRUE(original_cache_handler);
+
+ // Simulate a revalidation.
+ resource->SetRevalidatingRequest(ResourceRequest(url));
+ EXPECT_TRUE(resource->IsCacheValidator());
+ EXPECT_EQ(url, resource->GetResourceRequest().Url());
+ EXPECT_EQ(url, resource->LastResourceRequest().Url());
+ EXPECT_EQ(original_cache_handler, resource->CacheHandler());
+
+ Persistent<MockResourceClient> client = new MockResourceClient;
+ resource->AddClient(client, nullptr);
+
+ // The revalidating request is redirected.
+ ResourceResponse redirect_response(url);
+ redirect_response.SetHTTPHeaderField(
+ "location", AtomicString(redirect_target_url.GetString()));
+ redirect_response.SetHTTPStatusCode(308);
+ ResourceRequest redirected_revalidating_request(redirect_target_url);
+ resource->WillFollowRedirect(redirected_revalidating_request,
+ redirect_response);
+ EXPECT_FALSE(resource->IsCacheValidator());
+ EXPECT_EQ(url, resource->GetResourceRequest().Url());
+ EXPECT_EQ(redirect_target_url, resource->LastResourceRequest().Url());
+ EXPECT_FALSE(resource->CacheHandler());
+
+ // The final response is received.
+ ResourceResponse revalidating_response(redirect_target_url);
+ revalidating_response.SetHTTPStatusCode(200);
+ resource->ResponseReceived(revalidating_response, nullptr);
+
+ EXPECT_TRUE(resource->CacheHandler());
+
+ const char kData2[4] = "xyz";
+ resource->AppendData(kData2, 3);
+ resource->FinishForTest();
+ EXPECT_FALSE(resource->IsCacheValidator());
+ EXPECT_EQ(url, resource->GetResourceRequest().Url());
+ EXPECT_EQ(redirect_target_url, resource->LastResourceRequest().Url());
+ EXPECT_FALSE(resource->IsCacheValidator());
+ EXPECT_EQ(200, resource->GetResponse().HttpStatusCode());
+ EXPECT_EQ(3u, resource->ResourceBuffer()->size());
+ EXPECT_EQ(resource, GetMemoryCache()->ResourceForURL(url));
+
+ EXPECT_TRUE(client->NotifyFinishedCalled());
+
+ // Test the case where a client is added after revalidation is completed.
+ Persistent<MockResourceClient> client2 = new MockResourceClient;
+ resource->AddClient(
+ client2, Platform::Current()->CurrentThread()->GetTaskRunner().get());
+
+ // Because the client is added asynchronously,
+ // |runUntilIdle()| is called to make |client2| to be notified.
+ platform_->RunUntilIdle();
+
+ EXPECT_TRUE(client2->NotifyFinishedCalled());
+
+ GetMemoryCache()->Remove(resource);
+
+ resource->RemoveClient(client);
+ resource->RemoveClient(client2);
+ EXPECT_FALSE(resource->IsAlive());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_timing_info.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_timing_info.cc
new file mode 100644
index 00000000000..31574cfc049
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_timing_info.cc
@@ -0,0 +1,61 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h"
+
+#include <memory>
+#include "third_party/blink/renderer/platform/cross_thread_copier.h"
+
+namespace blink {
+
+scoped_refptr<ResourceTimingInfo> ResourceTimingInfo::Adopt(
+ std::unique_ptr<CrossThreadResourceTimingInfoData> data) {
+ scoped_refptr<ResourceTimingInfo> info = ResourceTimingInfo::Create(
+ AtomicString(data->type_), data->initial_time_, data->is_main_resource_);
+ info->original_timing_allow_origin_ =
+ AtomicString(data->original_timing_allow_origin_);
+ info->load_finish_time_ = data->load_finish_time_;
+ info->initial_url_ = data->initial_url_.Copy();
+ info->final_response_ = ResourceResponse(data->final_response_.get());
+ for (auto& response_data : data->redirect_chain_)
+ info->redirect_chain_.push_back(ResourceResponse(response_data.get()));
+ info->transfer_size_ = data->transfer_size_;
+ info->negative_allowed_ = data->negative_allowed_;
+ return info;
+}
+
+std::unique_ptr<CrossThreadResourceTimingInfoData>
+ResourceTimingInfo::CopyData() const {
+ std::unique_ptr<CrossThreadResourceTimingInfoData> data =
+ std::make_unique<CrossThreadResourceTimingInfoData>();
+ data->type_ = type_.GetString().IsolatedCopy();
+ data->original_timing_allow_origin_ =
+ original_timing_allow_origin_.GetString().IsolatedCopy();
+ data->initial_time_ = initial_time_;
+ data->load_finish_time_ = load_finish_time_;
+ data->initial_url_ = initial_url_.Copy();
+ data->final_response_ = final_response_.CopyData();
+ for (const auto& response : redirect_chain_)
+ data->redirect_chain_.push_back(response.CopyData());
+ data->transfer_size_ = transfer_size_;
+ data->is_main_resource_ = is_main_resource_;
+ data->negative_allowed_ = negative_allowed_;
+ return data;
+}
+
+void ResourceTimingInfo::AddRedirect(const ResourceResponse& redirect_response,
+ bool cross_origin) {
+ redirect_chain_.push_back(redirect_response);
+ if (has_cross_origin_redirect_)
+ return;
+ if (cross_origin) {
+ has_cross_origin_redirect_ = true;
+ transfer_size_ = 0;
+ } else {
+ DCHECK_GE(redirect_response.EncodedDataLength(), 0);
+ transfer_size_ += redirect_response.EncodedDataLength();
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h
new file mode 100644
index 00000000000..dd964d8be55
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2013 Intel 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_TIMING_INFO_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_TIMING_INFO_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/cross_thread_copier.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+
+namespace blink {
+
+struct CrossThreadResourceTimingInfoData;
+
+class PLATFORM_EXPORT ResourceTimingInfo
+ : public RefCounted<ResourceTimingInfo> {
+ USING_FAST_MALLOC(ResourceTimingInfo);
+ WTF_MAKE_NONCOPYABLE(ResourceTimingInfo);
+
+ public:
+ static scoped_refptr<ResourceTimingInfo> Create(const AtomicString& type,
+ const double time,
+ bool is_main_resource) {
+ return base::AdoptRef(new ResourceTimingInfo(type, time, is_main_resource));
+ }
+ static scoped_refptr<ResourceTimingInfo> Adopt(
+ std::unique_ptr<CrossThreadResourceTimingInfoData>);
+
+ // Gets a copy of the data suitable for passing to another thread.
+ std::unique_ptr<CrossThreadResourceTimingInfoData> CopyData() const;
+
+ double InitialTime() const { return initial_time_; }
+ bool IsMainResource() const { return is_main_resource_; }
+
+ const AtomicString& InitiatorType() const { return type_; }
+
+ void SetOriginalTimingAllowOrigin(
+ const AtomicString& original_timing_allow_origin) {
+ original_timing_allow_origin_ = original_timing_allow_origin;
+ }
+ const AtomicString& OriginalTimingAllowOrigin() const {
+ return original_timing_allow_origin_;
+ }
+
+ void SetLoadFinishTime(double time) { load_finish_time_ = time; }
+ double LoadFinishTime() const { return load_finish_time_; }
+
+ void SetInitialURL(const KURL& url) { initial_url_ = url; }
+ const KURL& InitialURL() const { return initial_url_; }
+
+ void SetFinalResponse(const ResourceResponse& response) {
+ final_response_ = response;
+ }
+ const ResourceResponse& FinalResponse() const { return final_response_; }
+
+ void AddRedirect(const ResourceResponse& redirect_response,
+ bool cross_origin);
+ const Vector<ResourceResponse>& RedirectChain() const {
+ return redirect_chain_;
+ }
+
+ void AddFinalTransferSize(long long encoded_data_length) {
+ transfer_size_ += encoded_data_length;
+ }
+ long long TransferSize() const { return transfer_size_; }
+
+ void ClearLoadTimings() {
+ final_response_.SetResourceLoadTiming(nullptr);
+ for (ResourceResponse& redirect : redirect_chain_)
+ redirect.SetResourceLoadTiming(nullptr);
+ }
+
+ // The timestamps in PerformanceResourceTiming are measured relative from the
+ // time origin. In most cases these timestamps must be positive value, so we
+ // use 0 for invalid negative values. But the timestamps for Service Worker
+ // navigation preload requests may be negative, because these requests may
+ // be started before the service worker started. We set this flag true, to
+ // support such case.
+ void SetNegativeAllowed(bool negative_allowed) {
+ negative_allowed_ = negative_allowed;
+ }
+ bool NegativeAllowed() const { return negative_allowed_; }
+
+ private:
+ ResourceTimingInfo(const AtomicString& type,
+ const double time,
+ bool is_main_resource)
+ : type_(type), initial_time_(time), is_main_resource_(is_main_resource) {}
+
+ AtomicString type_;
+ AtomicString original_timing_allow_origin_;
+ double initial_time_;
+ double load_finish_time_;
+ KURL initial_url_;
+ ResourceResponse final_response_;
+ Vector<ResourceResponse> redirect_chain_;
+ long long transfer_size_ = 0;
+ bool is_main_resource_;
+ bool has_cross_origin_redirect_ = false;
+ bool negative_allowed_ = false;
+};
+
+struct CrossThreadResourceTimingInfoData {
+ WTF_MAKE_NONCOPYABLE(CrossThreadResourceTimingInfoData);
+ USING_FAST_MALLOC(CrossThreadResourceTimingInfoData);
+
+ public:
+ CrossThreadResourceTimingInfoData() = default;
+
+ String type_;
+ String original_timing_allow_origin_;
+ double initial_time_;
+ double load_finish_time_;
+ KURL initial_url_;
+ std::unique_ptr<CrossThreadResourceResponseData> final_response_;
+ Vector<std::unique_ptr<CrossThreadResourceResponseData>> redirect_chain_;
+ long long transfer_size_;
+ bool is_main_resource_;
+ bool negative_allowed_;
+};
+
+template <>
+struct CrossThreadCopier<ResourceTimingInfo> {
+ typedef WTF::PassedWrapper<std::unique_ptr<CrossThreadResourceTimingInfoData>>
+ Type;
+ static Type Copy(const ResourceTimingInfo& info) {
+ return WTF::Passed(info.CopyData());
+ }
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.cc
new file mode 100644
index 00000000000..7fca9edf686
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.cc
@@ -0,0 +1,69 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/loader/fetch/script_fetch_options.h"
+
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+
+namespace blink {
+
+// https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-classic-script
+FetchParameters ScriptFetchOptions::CreateFetchParameters(
+ const KURL& url,
+ const SecurityOrigin* security_origin,
+ const WTF::TextEncoding& encoding,
+ FetchParameters::DeferOption defer) const {
+ // Step 1. Let request be the result of creating a potential-CORS request
+ // given url, ... [spec text]
+ ResourceRequest resource_request(url);
+
+ // Step 1. ... "script", ... [spec text]
+ ResourceLoaderOptions resource_loader_options;
+ resource_loader_options.initiator_info.name = "script";
+ FetchParameters params(resource_request, resource_loader_options);
+
+ // Step 1. ... and CORS setting. [spec text]
+ //
+ // Instead of using CrossOriginAttributeValue that corresponds to |CORS
+ // setting|, we use ScriptFetchOptions::CredentialsMode().
+ // We shouldn't call SetCrossOriginAccessControl() if CredentialsMode() is
+ // kFetchCredentialsModeOmit, because in that case the request should be
+ // no-cors, while SetCrossOriginAccessControl(kFetchCredentialsModeOmit)
+ // would result in a cors request.
+ if (CredentialsMode() != network::mojom::FetchCredentialsMode::kOmit) {
+ params.SetCrossOriginAccessControl(security_origin, CredentialsMode());
+ }
+
+ // Step 2. Set request's client to settings object. [spec text]
+ // Note: Implemented at ClassicPendingScript::Fetch().
+
+ // Step 3. Set up the classic script request given request and options. [spec
+ // text]
+ //
+ // https://html.spec.whatwg.org/multipage/webappapis.html#set-up-the-classic-script-request
+ // Set request's cryptographic nonce metadata to options's cryptographic
+ // nonce, [spec text]
+ params.SetContentSecurityPolicyNonce(Nonce());
+
+ // its integrity metadata to options's integrity metadata, [spec text]
+ params.SetIntegrityMetadata(GetIntegrityMetadata());
+ params.MutableResourceRequest().SetFetchIntegrity(
+ GetIntegrityAttributeValue());
+
+ // and its parser metadata to options's parser metadata. [spec text]
+ params.SetParserDisposition(ParserState());
+
+ params.SetCharset(encoding);
+
+ // This DeferOption logic is only for classic scripts, as we always set
+ // |kLazyLoad| for module scripts in ModuleScriptLoader.
+ params.SetDefer(defer);
+
+ // Steps 4- are Implemented at ClassicPendingScript::Fetch().
+
+ return params;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.h b/chromium/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.h
new file mode 100644
index 00000000000..57883285a4a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.h
@@ -0,0 +1,82 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_SCRIPT_FETCH_OPTIONS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_SCRIPT_FETCH_OPTIONS_H_
+
+#include "third_party/blink/public/platform/web_url_request.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h"
+#include "third_party/blink/renderer/platform/loader/fetch/integrity_metadata.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class KURL;
+class SecurityOrigin;
+
+// ScriptFetchOptions corresponds to the spec concept "script fetch options".
+// https://html.spec.whatwg.org/multipage/webappapis.html#script-fetch-options
+class PLATFORM_EXPORT ScriptFetchOptions final {
+ public:
+ // https://html.spec.whatwg.org/multipage/webappapis.html#default-classic-script-fetch-options
+ // "The default classic script fetch options are a script fetch options whose
+ // cryptographic nonce is the empty string, integrity metadata is the empty
+ // string, parser metadata is "not-parser-inserted", and credentials mode
+ // is "omit"." [spec text]
+ ScriptFetchOptions()
+ : parser_state_(ParserDisposition::kNotParserInserted),
+ credentials_mode_(network::mojom::FetchCredentialsMode::kOmit) {}
+
+ ScriptFetchOptions(const String& nonce,
+ const IntegrityMetadataSet& integrity_metadata,
+ const String& integrity_attribute,
+ ParserDisposition parser_state,
+ network::mojom::FetchCredentialsMode credentials_mode)
+ : nonce_(nonce),
+ integrity_metadata_(integrity_metadata),
+ integrity_attribute_(integrity_attribute),
+ parser_state_(parser_state),
+ credentials_mode_(credentials_mode) {}
+ ~ScriptFetchOptions() = default;
+
+ const String& Nonce() const { return nonce_; }
+ const IntegrityMetadataSet& GetIntegrityMetadata() const {
+ return integrity_metadata_;
+ }
+ const String& GetIntegrityAttributeValue() const {
+ return integrity_attribute_;
+ }
+ const ParserDisposition& ParserState() const { return parser_state_; }
+ network::mojom::FetchCredentialsMode CredentialsMode() const {
+ return credentials_mode_;
+ }
+
+ // https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-classic-script
+ // Steps 1 and 3.
+ FetchParameters CreateFetchParameters(const KURL&,
+ const SecurityOrigin*,
+ const WTF::TextEncoding&,
+ FetchParameters::DeferOption) const;
+
+ private:
+ // https://html.spec.whatwg.org/multipage/webappapis.html#concept-script-fetch-options-nonce
+ const String nonce_;
+
+ // https://html.spec.whatwg.org/multipage/webappapis.html#concept-script-fetch-options-integrity
+ const IntegrityMetadataSet integrity_metadata_;
+ const String integrity_attribute_;
+
+ // https://html.spec.whatwg.org/multipage/webappapis.html#concept-script-fetch-options-parser
+ const ParserDisposition parser_state_;
+
+ // https://html.spec.whatwg.org/multipage/webappapis.html#concept-script-fetch-options-credentials
+ const network::mojom::FetchCredentialsMode credentials_mode_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.cc
new file mode 100644
index 00000000000..4e4e33a3147
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.cc
@@ -0,0 +1,218 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.h"
+
+#include "base/bit_cast.h"
+#include "third_party/blink/renderer/platform/crypto.h"
+#include "third_party/blink/renderer/platform/loader/fetch/cached_metadata.h"
+#include "third_party/blink/renderer/platform/wtf/string_hasher.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+// Defined here for storage/ODR reasons, but initialized in the header.
+const size_t SourceKeyedCachedMetadataHandler::kKeySize;
+
+class SourceKeyedCachedMetadataHandler::SingleKeyHandler final
+ : public SingleCachedMetadataHandler {
+ public:
+ void Trace(Visitor* visitor) override {
+ visitor->Trace(parent_);
+ SingleCachedMetadataHandler::Trace(visitor);
+ }
+
+ SingleKeyHandler(SourceKeyedCachedMetadataHandler* parent, Key key)
+ : parent_(parent), key_(key) {}
+
+ void SetCachedMetadata(uint32_t data_type_id,
+ const char* data,
+ size_t size,
+ CacheType cache_type) override {
+ DCHECK(!parent_->cached_metadata_map_.Contains(key_));
+ parent_->cached_metadata_map_.insert(
+ key_, CachedMetadata::Create(data_type_id, data, size));
+ if (cache_type == CachedMetadataHandler::kSendToPlatform)
+ parent_->SendToPlatform();
+ }
+
+ void ClearCachedMetadata(CacheType cache_type) override {
+ parent_->cached_metadata_map_.erase(key_);
+ if (cache_type == CachedMetadataHandler::kSendToPlatform)
+ parent_->SendToPlatform();
+ }
+
+ scoped_refptr<CachedMetadata> GetCachedMetadata(
+ uint32_t data_type_id) const override {
+ scoped_refptr<CachedMetadata> cached_metadata =
+ parent_->cached_metadata_map_.at(key_);
+ if (!cached_metadata || cached_metadata->DataTypeID() != data_type_id)
+ return nullptr;
+ return cached_metadata;
+ }
+
+ String Encoding() const override { return parent_->Encoding(); }
+
+ bool IsServedFromCacheStorage() const override {
+ return parent_->IsServedFromCacheStorage();
+ }
+
+ private:
+ Member<SourceKeyedCachedMetadataHandler> parent_;
+ Key key_;
+};
+
+class SourceKeyedCachedMetadataHandler::KeyHash {
+ public:
+ static unsigned GetHash(const Key& key) {
+ return StringHasher::ComputeHash(key.data(), key.size());
+ }
+
+ static bool Equal(const Key& a, const Key& b) { return a == b; }
+
+ static const bool safe_to_compare_to_empty_or_deleted = true;
+};
+
+SingleCachedMetadataHandler* SourceKeyedCachedMetadataHandler::HandlerForSource(
+ const String& source) {
+ DigestValue digest_value;
+
+ if (!ComputeDigest(kHashAlgorithmSha256,
+ static_cast<const char*>(source.Bytes()),
+ source.CharactersSizeInBytes(), digest_value))
+ return nullptr;
+
+ Key key;
+ DCHECK_EQ(digest_value.size(), kKeySize);
+ memcpy(key.data(), digest_value.data(), kKeySize);
+
+ return new SingleKeyHandler(this, key);
+}
+
+void SourceKeyedCachedMetadataHandler::ClearCachedMetadata(
+ CachedMetadataHandler::CacheType cache_type) {
+ cached_metadata_map_.clear();
+ if (cache_type == CachedMetadataHandler::kSendToPlatform)
+ SendToPlatform();
+};
+
+String SourceKeyedCachedMetadataHandler::Encoding() const {
+ return String(encoding_.GetName());
+}
+
+// Encoding of keyed map:
+// - marker: CachedMetadataHandler::kSourceKeyedMap (uint32_t)
+// - num_entries (int)
+// - key 1 (Key type)
+// - len data 1 (size_t)
+// - type data 1
+// - data for key 1
+// ...
+// - key N (Key type)
+// - len data N (size_t)
+// - type data N
+// - data for key N
+
+namespace {
+// Reading a value from a char buffer without using reinterpret cast. This
+// should inline and optimize to the same code as *reinterpret_cast<T>(data),
+// but without the risk of undefined behaviour.
+template <typename T>
+T ReadVal(const char* data) {
+ static_assert(base::is_trivially_copyable<T>::value,
+ "ReadVal requires the value type to be copyable");
+ T ret;
+ memcpy(&ret, data, sizeof(T));
+ return ret;
+}
+} // namespace
+
+void SourceKeyedCachedMetadataHandler::SetSerializedCachedMetadata(
+ const char* data,
+ size_t size) {
+ // We only expect to receive cached metadata from the platform once. If this
+ // triggers, it indicates an efficiency problem which is most likely
+ // unexpected in code designed to improve performance.
+ DCHECK(cached_metadata_map_.IsEmpty());
+
+ // Ensure we have a marker.
+ if (size < sizeof(uint32_t))
+ return;
+ uint32_t marker = ReadVal<uint32_t>(data);
+ // Check for our marker to avoid conflicts with other kinds of cached
+ // metadata.
+ if (marker != CachedMetadataHandler::kSourceKeyedMap) {
+ return;
+ }
+ data += sizeof(uint32_t);
+ size -= sizeof(uint32_t);
+
+ // Ensure we have a length.
+ if (size < sizeof(int))
+ return;
+ int num_entries = ReadVal<int>(data);
+ data += sizeof(int);
+ size -= sizeof(int);
+
+ for (int i = 0; i < num_entries; ++i) {
+ // Ensure we have an entry key and size.
+ if (size < kKeySize + sizeof(size_t)) {
+ cached_metadata_map_.clear();
+ return;
+ }
+
+ Key key;
+ std::copy(data, data + kKeySize, std::begin(key));
+ data += kKeySize;
+ size_t entry_size = ReadVal<size_t>(data);
+ data += sizeof(size_t);
+
+ size -= kKeySize + sizeof(size_t);
+
+ // Ensure we have enough data for this entry.
+ if (size < entry_size) {
+ cached_metadata_map_.clear();
+ return;
+ }
+
+ if (scoped_refptr<CachedMetadata> deserialized_entry =
+ CachedMetadata::CreateFromSerializedData(data, entry_size)) {
+ // Only insert the deserialized entry if it deserialized correctly.
+ cached_metadata_map_.insert(key, std::move(deserialized_entry));
+ }
+ data += entry_size;
+ size -= entry_size;
+ }
+
+ // Ensure we have no more data.
+ if (size > 0) {
+ cached_metadata_map_.clear();
+ }
+};
+
+void SourceKeyedCachedMetadataHandler::SendToPlatform() {
+ if (!sender_)
+ return;
+
+ if (cached_metadata_map_.IsEmpty()) {
+ sender_->Send(nullptr, 0);
+ } else {
+ Vector<char> serialized_data;
+ uint32_t marker = CachedMetadataHandler::kSourceKeyedMap;
+ serialized_data.Append(reinterpret_cast<char*>(&marker), sizeof(marker));
+ int num_entries = cached_metadata_map_.size();
+ serialized_data.Append(reinterpret_cast<char*>(&num_entries),
+ sizeof(num_entries));
+ for (const auto& metadata : cached_metadata_map_) {
+ serialized_data.Append(metadata.key.data(), kKeySize);
+ size_t entry_size = metadata.value->SerializedData().size();
+ serialized_data.Append(reinterpret_cast<const char*>(&entry_size),
+ sizeof(entry_size));
+ serialized_data.AppendVector(metadata.value->SerializedData());
+ }
+ sender_->Send(serialized_data.data(), serialized_data.size());
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.h b/chromium/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.h
new file mode 100644
index 00000000000..b9b7b83a773
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.h
@@ -0,0 +1,81 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_SOURCE_KEYED_CACHED_METADATA_HANDLER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_SOURCE_KEYED_CACHED_METADATA_HANDLER_H_
+
+#include <stdint.h>
+#include <array>
+#include "third_party/blink/renderer/platform/loader/fetch/cached_metadata.h"
+#include "third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+
+namespace blink {
+
+// An implementation of CachedMetadataHandler which can hold multiple
+// CachedMetadata entries. These entries are keyed by a cryptograph hash of the
+// source code which produced them.
+//
+// This is used to store cached metadata for multiple inline scripts on a single
+// HTML document's resource.
+class PLATFORM_EXPORT SourceKeyedCachedMetadataHandler final
+ : public CachedMetadataHandler {
+ public:
+ SourceKeyedCachedMetadataHandler(
+ WTF::TextEncoding encoding,
+ std::unique_ptr<CachedMetadataSender> send_callback)
+ : sender_(std::move(send_callback)), encoding_(encoding) {}
+
+ // Produce a metadata handler for a single cached metadata associated with
+ // the given source code.
+ SingleCachedMetadataHandler* HandlerForSource(const String& source);
+
+ void ClearCachedMetadata(CachedMetadataHandler::CacheType) override;
+ String Encoding() const override;
+ bool IsServedFromCacheStorage() const override {
+ return sender_->IsServedFromCacheStorage();
+ }
+
+ void SetSerializedCachedMetadata(const char*, size_t);
+
+ private:
+ // Keys are SHA-256, which are 256/8 = 32 bytes.
+ static constexpr size_t kKeySize = 32;
+ typedef std::array<uint8_t, kKeySize> Key;
+
+ class SingleKeyHandler;
+ class KeyHash;
+ class KeyHashTraits : public WTF::GenericHashTraits<Key> {
+ public:
+ // Note: This class relies on hashes never being zero or 1 followed by all
+ // zeros. Practically, our hash space is large enough that the risk of such
+ // a collision is infinitesimal.
+
+ typedef Key EmptyValueType;
+ static const bool kEmptyValueIsZero = true;
+ static EmptyValueType EmptyValue() {
+ // Rely on integer value initialization to zero out the key array.
+ return Key{};
+ }
+
+ static void ConstructDeletedValue(Key& slot, bool) {
+ slot = {1}; // Remaining entries are value initialized to 0.
+ }
+ static bool IsDeletedValue(const Key& value) { return value == Key{1}; }
+ };
+
+ void SendToPlatform();
+
+ // TODO(leszeks): Maybe just store the SingleKeyHandlers directly in here?
+ WTF::HashMap<Key, scoped_refptr<CachedMetadata>, KeyHash, KeyHashTraits>
+ cached_metadata_map_;
+ std::unique_ptr<CachedMetadataSender> sender_;
+
+ const WTF::TextEncoding encoding_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_SOURCE_KEYED_CACHED_METADATA_HANDLER_H_
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler_test.cc
new file mode 100644
index 00000000000..b30c1769416
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler_test.cc
@@ -0,0 +1,459 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.h"
+
+#include <array>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/crypto.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
+
+namespace blink {
+
+namespace {
+
+class MockSha256WebCryptoDigestor : public WebCryptoDigestor {
+ public:
+ virtual bool Consume(const unsigned char* data, unsigned data_size) {
+ String key(data, data_size);
+
+ auto it = kMapOfHashes.find(key);
+
+ if (it != kMapOfHashes.end()) {
+ hash_exists_ = true;
+ hash_ = it->value;
+ }
+
+ return hash_exists_;
+ }
+
+ virtual bool Finish(unsigned char*& result_data, unsigned& result_data_size) {
+ if (hash_exists_) {
+ result_data = hash_.data();
+ result_data_size = hash_.size();
+ }
+ return hash_exists_;
+ }
+
+ private:
+ Vector<unsigned char> hash_;
+ bool hash_exists_;
+
+ HashMap<String, Vector<unsigned char>> kMapOfHashes = {
+ {"source1",
+ Vector<unsigned char>{0xc4, 0xd5, 0xe4, 0x35, 0x74, 0x89, 0x3c, 0x3c,
+ 0xc3, 0xd4, 0xba, 0xba, 0x65, 0x58, 0x92, 0x48,
+ 0x47, 0x9a, 0x9f, 0xbf, 0xaf, 0x1f, 0x60, 0x8e,
+ 0xb1, 0x54, 0x1e, 0xc0, 0xc6, 0xfe, 0x63, 0x6f}},
+ {"source2",
+ Vector<unsigned char>{0x99, 0x2f, 0x4e, 0xb2, 0x41, 0xee, 0x6e, 0xef,
+ 0xe4, 0x92, 0x80, 0x25, 0xa2, 0x74, 0x7d, 0xb0,
+ 0x8b, 0x91, 0x98, 0x34, 0xc9, 0x3c, 0x5f, 0x57,
+ 0x41, 0x72, 0x5f, 0xa2, 0x6b, 0x63, 0x38, 0x41}}};
+};
+
+// Mock WebCrypto implementation for digest calculation.
+class MockDigestWebCrypto : public WebCrypto {
+ std::unique_ptr<WebCryptoDigestor> CreateDigestor(
+ WebCryptoAlgorithmId algorithm_id) override {
+ EXPECT_EQ(algorithm_id, WebCryptoAlgorithmId::kWebCryptoAlgorithmIdSha256);
+ return std::make_unique<MockSha256WebCryptoDigestor>();
+ }
+};
+
+// Structure holding cache metadata sent to the platform.
+struct CacheMetadataEntry {
+ CacheMetadataEntry(const WebURL& url,
+ base::Time response_time,
+ const char* data,
+ size_t data_size)
+ : url(url), response_time(response_time) {
+ this->data.Append(data, data_size);
+ }
+
+ WebURL url;
+ base::Time response_time;
+ Vector<char> data;
+};
+
+// Mock Platform implementation that provides basic crypto and caching.
+class SourceKeyedCachedMetadataHandlerMockPlatform final
+ : public TestingPlatformSupportWithMockScheduler {
+ public:
+ SourceKeyedCachedMetadataHandlerMockPlatform() {}
+ ~SourceKeyedCachedMetadataHandlerMockPlatform() override = default;
+
+ WebCrypto* Crypto() override { return &mock_web_crypto_; }
+
+ void CacheMetadata(const WebURL& url,
+ base::Time response_time,
+ const char* data,
+ size_t data_size) override {
+ cache_entries_.emplace_back(url, response_time, data, data_size);
+ }
+
+ bool HasCacheMetadataFor(const WebURL& url) {
+ for (const CacheMetadataEntry& entry : cache_entries_) {
+ if (entry.url == url) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ Vector<CacheMetadataEntry> GetCacheMetadatasFor(const WebURL& url) {
+ Vector<CacheMetadataEntry> url_entries;
+ for (const CacheMetadataEntry& entry : cache_entries_) {
+ if (entry.url == url) {
+ url_entries.push_back(entry);
+ }
+ }
+ return url_entries;
+ }
+
+ private:
+ MockDigestWebCrypto mock_web_crypto_;
+ Vector<CacheMetadataEntry> cache_entries_;
+};
+
+// Mock CachedMetadataSender implementation that forwards data to the platform.
+class MockCachedMetadataSender final : public CachedMetadataSender {
+ public:
+ MockCachedMetadataSender(KURL response_url) : response_url_(response_url) {}
+
+ void Send(const char* data, size_t size) {
+ Platform::Current()->CacheMetadata(response_url_, response_time_, data,
+ size);
+ }
+
+ bool IsServedFromCacheStorage() override { return false; }
+
+ private:
+ const KURL response_url_;
+ const Time response_time_;
+};
+
+template <size_t N>
+::testing::AssertionResult CachedMetadataFailure(
+ const char* failure_msg,
+ const char* actual_expression,
+ const std::array<char, N>& expected,
+ const scoped_refptr<CachedMetadata>& actual) {
+ ::testing::Message msg;
+ msg << failure_msg << " for " << actual_expression;
+ msg << "\n Expected: [" << N << "] { ";
+ for (size_t i = 0; i < N; ++i) {
+ if (i > 0)
+ msg << ", ";
+ msg << std::hex << static_cast<int>(expected[i]);
+ }
+ msg << " }";
+ if (actual) {
+ msg << "\n Actual: [" << actual->size() << "] { ";
+ for (size_t i = 0; i < actual->size(); ++i) {
+ if (i > 0)
+ msg << ", ";
+ msg << std::hex << static_cast<int>(actual->Data()[i]);
+ }
+ msg << " }";
+ } else {
+ msg << "\n Actual: (null)";
+ }
+
+ return testing::AssertionFailure() << msg;
+}
+
+template <size_t N>
+::testing::AssertionResult CachedMetadataEqual(
+ const char* expected_expression,
+ const char* actual_expression,
+ const std::array<char, N>& expected,
+ const scoped_refptr<CachedMetadata>& actual) {
+ if (!actual) {
+ return CachedMetadataFailure("Expected non-null data", actual_expression,
+ expected, actual);
+ }
+ if (actual->size() != N) {
+ return CachedMetadataFailure("Wrong size", actual_expression, expected,
+ actual);
+ }
+ const char* actual_data = actual->Data();
+ for (size_t i = 0; i < N; ++i) {
+ if (actual_data[i] != expected[i]) {
+ return CachedMetadataFailure("Wrong data", actual_expression, expected,
+ actual);
+ }
+ }
+
+ return testing::AssertionSuccess();
+}
+
+#define EXPECT_METADATA(data_array, cached_metadata) \
+ EXPECT_PRED_FORMAT2(CachedMetadataEqual, data_array, cached_metadata)
+
+} // namespace
+
+TEST(SourceKeyedCachedMetadataHandlerTest,
+ HandlerForSource_InitiallyNonNullHandlersWithNullData) {
+ ScopedTestingPlatformSupport<SourceKeyedCachedMetadataHandlerMockPlatform>
+ platform;
+
+ KURL url("http://SourceKeyedCachedMetadataHandlerTest.com");
+ SourceKeyedCachedMetadataHandler* handler =
+ new SourceKeyedCachedMetadataHandler(
+ WTF::TextEncoding(), std::make_unique<MockCachedMetadataSender>(url));
+
+ WTF::String source1("source1");
+ SingleCachedMetadataHandler* source1_handler =
+ handler->HandlerForSource(source1);
+
+ WTF::String source2("source2");
+ SingleCachedMetadataHandler* source2_handler =
+ handler->HandlerForSource(source2);
+
+ EXPECT_NE(nullptr, source1_handler);
+ EXPECT_EQ(nullptr, source1_handler->GetCachedMetadata(0xbeef));
+ EXPECT_NE(nullptr, source2_handler);
+ EXPECT_EQ(nullptr, source2_handler->GetCachedMetadata(0x5eed));
+}
+
+TEST(SourceKeyedCachedMetadataHandlerTest,
+ HandlerForSource_OneHandlerSetOtherNull) {
+ ScopedTestingPlatformSupport<SourceKeyedCachedMetadataHandlerMockPlatform>
+ platform;
+
+ KURL url("http://SourceKeyedCachedMetadataHandlerTest.com");
+ SourceKeyedCachedMetadataHandler* handler =
+ new SourceKeyedCachedMetadataHandler(
+ WTF::TextEncoding(), std::make_unique<MockCachedMetadataSender>(url));
+
+ WTF::String source1("source1");
+ SingleCachedMetadataHandler* source1_handler =
+ handler->HandlerForSource(source1);
+
+ WTF::String source2("source2");
+ SingleCachedMetadataHandler* source2_handler =
+ handler->HandlerForSource(source2);
+
+ std::array<char, 3> data1 = {1, 2, 3};
+ source1_handler->SetCachedMetadata(0xbeef, data1.data(), data1.size());
+
+ EXPECT_NE(nullptr, source1_handler);
+ EXPECT_METADATA(data1, source1_handler->GetCachedMetadata(0xbeef));
+
+ EXPECT_NE(nullptr, source2_handler);
+ EXPECT_EQ(nullptr, source2_handler->GetCachedMetadata(0x5eed));
+}
+
+TEST(SourceKeyedCachedMetadataHandlerTest, HandlerForSource_BothHandlersSet) {
+ ScopedTestingPlatformSupport<SourceKeyedCachedMetadataHandlerMockPlatform>
+ platform;
+
+ KURL url("http://SourceKeyedCachedMetadataHandlerTest.com");
+ SourceKeyedCachedMetadataHandler* handler =
+ new SourceKeyedCachedMetadataHandler(
+ WTF::TextEncoding(), std::make_unique<MockCachedMetadataSender>(url));
+
+ WTF::String source1("source1");
+ SingleCachedMetadataHandler* source1_handler =
+ handler->HandlerForSource(source1);
+
+ WTF::String source2("source2");
+ SingleCachedMetadataHandler* source2_handler =
+ handler->HandlerForSource(source2);
+
+ std::array<char, 3> data1 = {1, 2, 3};
+ source1_handler->SetCachedMetadata(0xbeef, data1.data(), data1.size());
+
+ std::array<char, 4> data2 = {3, 4, 5, 6};
+ source2_handler->SetCachedMetadata(0x5eed, data2.data(), data2.size());
+
+ EXPECT_NE(nullptr, source1_handler);
+ EXPECT_METADATA(data1, source1_handler->GetCachedMetadata(0xbeef));
+
+ EXPECT_NE(nullptr, source2_handler);
+ EXPECT_METADATA(data2, source2_handler->GetCachedMetadata(0x5eed));
+}
+
+TEST(SourceKeyedCachedMetadataHandlerTest, Serialize_EmptyClearDoesSend) {
+ ScopedTestingPlatformSupport<SourceKeyedCachedMetadataHandlerMockPlatform>
+ platform;
+
+ KURL url("http://SourceKeyedCachedMetadataHandlerTest.com");
+ SourceKeyedCachedMetadataHandler* handler =
+ new SourceKeyedCachedMetadataHandler(
+ WTF::TextEncoding(), std::make_unique<MockCachedMetadataSender>(url));
+
+ // Clear and send to the platform
+ handler->ClearCachedMetadata(CachedMetadataHandler::kSendToPlatform);
+
+ // Load from platform
+ Vector<CacheMetadataEntry> cache_metadatas =
+ platform->GetCacheMetadatasFor(url);
+
+ EXPECT_EQ(1u, cache_metadatas.size());
+}
+
+TEST(SourceKeyedCachedMetadataHandlerTest, Serialize_EachSetDoesSend) {
+ ScopedTestingPlatformSupport<SourceKeyedCachedMetadataHandlerMockPlatform>
+ platform;
+
+ KURL url("http://SourceKeyedCachedMetadataHandlerTest.com");
+ SourceKeyedCachedMetadataHandler* handler =
+ new SourceKeyedCachedMetadataHandler(
+ WTF::TextEncoding(), std::make_unique<MockCachedMetadataSender>(url));
+
+ WTF::String source1("source1");
+ SingleCachedMetadataHandler* source1_handler =
+ handler->HandlerForSource(source1);
+
+ WTF::String source2("source2");
+ SingleCachedMetadataHandler* source2_handler =
+ handler->HandlerForSource(source2);
+
+ std::array<char, 3> data1 = {1, 2, 3};
+ source1_handler->SetCachedMetadata(0xbeef, data1.data(), data1.size());
+
+ std::array<char, 4> data2 = {3, 4, 5, 6};
+ source2_handler->SetCachedMetadata(0x5eed, data2.data(), data2.size());
+
+ // Load from platform
+ Vector<CacheMetadataEntry> cache_metadatas =
+ platform->GetCacheMetadatasFor(url);
+
+ EXPECT_EQ(2u, cache_metadatas.size());
+}
+
+TEST(SourceKeyedCachedMetadataHandlerTest, Serialize_SetWithNoSendDoesNotSend) {
+ ScopedTestingPlatformSupport<SourceKeyedCachedMetadataHandlerMockPlatform>
+ platform;
+
+ KURL url("http://SourceKeyedCachedMetadataHandlerTest.com");
+ SourceKeyedCachedMetadataHandler* handler =
+ new SourceKeyedCachedMetadataHandler(
+ WTF::TextEncoding(), std::make_unique<MockCachedMetadataSender>(url));
+
+ WTF::String source1("source1");
+ SingleCachedMetadataHandler* source1_handler =
+ handler->HandlerForSource(source1);
+
+ WTF::String source2("source2");
+ SingleCachedMetadataHandler* source2_handler =
+ handler->HandlerForSource(source2);
+
+ std::array<char, 3> data1 = {1, 2, 3};
+ source1_handler->SetCachedMetadata(0xbeef, data1.data(), data1.size(),
+ CachedMetadataHandler::kCacheLocally);
+
+ std::array<char, 4> data2 = {3, 4, 5, 6};
+ source2_handler->SetCachedMetadata(0x5eed, data2.data(), data2.size());
+
+ // Load from platform
+ Vector<CacheMetadataEntry> cache_metadatas =
+ platform->GetCacheMetadatasFor(url);
+
+ EXPECT_EQ(1u, cache_metadatas.size());
+}
+
+TEST(SourceKeyedCachedMetadataHandlerTest,
+ SerializeAndDeserialize_NoHandlersSet) {
+ ScopedTestingPlatformSupport<SourceKeyedCachedMetadataHandlerMockPlatform>
+ platform;
+
+ KURL url("http://SourceKeyedCachedMetadataHandlerTest.com");
+ WTF::String source1("source1");
+ WTF::String source2("source2");
+ {
+ SourceKeyedCachedMetadataHandler* handler =
+ new SourceKeyedCachedMetadataHandler(
+ WTF::TextEncoding(),
+ std::make_unique<MockCachedMetadataSender>(url));
+
+ // Clear and send to the platform
+ handler->ClearCachedMetadata(CachedMetadataHandler::kSendToPlatform);
+ }
+
+ // Reload from platform
+ {
+ Vector<CacheMetadataEntry> cache_metadatas =
+ platform->GetCacheMetadatasFor(url);
+ // Use the last data received by the platform
+ EXPECT_EQ(1u, cache_metadatas.size());
+ CacheMetadataEntry& last_cache_metadata = cache_metadatas[0];
+
+ SourceKeyedCachedMetadataHandler* handler =
+ new SourceKeyedCachedMetadataHandler(
+ WTF::TextEncoding(),
+ std::make_unique<MockCachedMetadataSender>(url));
+ handler->SetSerializedCachedMetadata(last_cache_metadata.data.data(),
+ last_cache_metadata.data.size());
+
+ SingleCachedMetadataHandler* source1_handler =
+ handler->HandlerForSource(source1);
+ SingleCachedMetadataHandler* source2_handler =
+ handler->HandlerForSource(source2);
+
+ EXPECT_NE(nullptr, source1_handler);
+ EXPECT_EQ(nullptr, source1_handler->GetCachedMetadata(0xbeef));
+
+ EXPECT_NE(nullptr, source2_handler);
+ EXPECT_EQ(nullptr, source2_handler->GetCachedMetadata(0x5eed));
+ }
+}
+
+TEST(SourceKeyedCachedMetadataHandlerTest,
+ SerializeAndDeserialize_BothHandlersSet) {
+ ScopedTestingPlatformSupport<SourceKeyedCachedMetadataHandlerMockPlatform>
+ platform;
+
+ KURL url("http://SourceKeyedCachedMetadataHandlerTest.com");
+ WTF::String source1("source1");
+ WTF::String source2("source2");
+ std::array<char, 3> data1 = {1, 2, 3};
+ std::array<char, 4> data2 = {3, 4, 5, 6};
+ {
+ SourceKeyedCachedMetadataHandler* handler =
+ new SourceKeyedCachedMetadataHandler(
+ WTF::TextEncoding(),
+ std::make_unique<MockCachedMetadataSender>(url));
+
+ SingleCachedMetadataHandler* source1_handler =
+ handler->HandlerForSource(source1);
+ SingleCachedMetadataHandler* source2_handler =
+ handler->HandlerForSource(source2);
+
+ source1_handler->SetCachedMetadata(0xbeef, data1.data(), data1.size());
+ source2_handler->SetCachedMetadata(0x5eed, data2.data(), data2.size());
+ }
+
+ // Reload from platform
+ {
+ Vector<CacheMetadataEntry> cache_metadatas =
+ platform->GetCacheMetadatasFor(url);
+ // Use the last data received by the platform
+ EXPECT_EQ(2u, cache_metadatas.size());
+ CacheMetadataEntry& last_cache_metadata = cache_metadatas[1];
+
+ SourceKeyedCachedMetadataHandler* handler =
+ new SourceKeyedCachedMetadataHandler(
+ WTF::TextEncoding(),
+ std::make_unique<MockCachedMetadataSender>(url));
+ handler->SetSerializedCachedMetadata(last_cache_metadata.data.data(),
+ last_cache_metadata.data.size());
+
+ SingleCachedMetadataHandler* source1_handler =
+ handler->HandlerForSource(source1);
+ SingleCachedMetadataHandler* source2_handler =
+ handler->HandlerForSource(source2);
+
+ EXPECT_NE(nullptr, source1_handler);
+ EXPECT_METADATA(data1, source1_handler->GetCachedMetadata(0xbeef));
+
+ EXPECT_NE(nullptr, source2_handler);
+ EXPECT_METADATA(data2, source2_handler->GetCachedMetadata(0x5eed));
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/substitute_data.h b/chromium/third_party/blink/renderer/platform/loader/fetch/substitute_data.h
new file mode 100644
index 00000000000..2dd436b2711
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/substitute_data.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2007 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_SUBSTITUTE_DATA_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_SUBSTITUTE_DATA_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+class SubstituteData {
+ DISALLOW_NEW();
+
+ public:
+ SubstituteData() = default;
+
+ SubstituteData(scoped_refptr<SharedBuffer> content)
+ : SubstituteData(content, "text/html", "UTF-8", KURL()) {}
+
+ SubstituteData(scoped_refptr<SharedBuffer> content,
+ const AtomicString& mime_type,
+ const AtomicString& text_encoding,
+ const KURL& failing_url)
+ : content_(std::move(content)),
+ mime_type_(mime_type),
+ text_encoding_(text_encoding),
+ failing_url_(failing_url) {}
+
+ bool IsValid() const { return content_.get(); }
+
+ SharedBuffer* Content() const { return content_.get(); }
+ const AtomicString& MimeType() const { return mime_type_; }
+ const AtomicString& TextEncoding() const { return text_encoding_; }
+ const KURL& FailingURL() const { return failing_url_; }
+
+ private:
+ scoped_refptr<SharedBuffer> content_;
+ AtomicString mime_type_;
+ AtomicString text_encoding_;
+ KURL failing_url_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_SUBSTITUTE_DATA_H_
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/text_resource_decoder_options.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/text_resource_decoder_options.cc
new file mode 100644
index 00000000000..b0b42a8bc22
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/text_resource_decoder_options.cc
@@ -0,0 +1,65 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/language.h"
+#include "third_party/blink/renderer/platform/loader/fetch/text_resource_decoder_options.h"
+
+namespace blink {
+
+TextResourceDecoderOptions::TextResourceDecoderOptions(
+ ContentType content_type,
+ const WTF::TextEncoding& default_encoding)
+ : TextResourceDecoderOptions(kUseContentAndBOMBasedDetection,
+ content_type,
+ default_encoding,
+ nullptr,
+ KURL()) {}
+
+TextResourceDecoderOptions
+TextResourceDecoderOptions::CreateAlwaysUseUTF8ForText() {
+ return TextResourceDecoderOptions(kAlwaysUseUTF8ForText, kPlainTextContent,
+ UTF8Encoding(), nullptr, NullURL());
+}
+
+TextResourceDecoderOptions TextResourceDecoderOptions::CreateWithAutoDetection(
+ ContentType content_type,
+ const WTF::TextEncoding& default_encoding,
+ const WTF::TextEncoding& hint_encoding,
+ const KURL& hint_url) {
+ return TextResourceDecoderOptions(kUseAllAutoDetection, content_type,
+ default_encoding, hint_encoding.GetName(),
+ hint_url);
+}
+
+TextResourceDecoderOptions::TextResourceDecoderOptions(
+ EncodingDetectionOption encoding_detection_option,
+ ContentType content_type,
+ const WTF::TextEncoding& default_encoding,
+ const char* hint_encoding,
+ const KURL& hint_url)
+ : encoding_detection_option_(encoding_detection_option),
+ content_type_(content_type),
+ default_encoding_(default_encoding),
+ use_lenient_xml_decoding_(false),
+ hint_encoding_(hint_encoding),
+ hint_url_(hint_url) {
+ hint_language_[0] = 0;
+ if (encoding_detection_option_ == kUseAllAutoDetection) {
+ // Checking empty URL helps unit testing. Providing DefaultLanguage() is
+ // sometimes difficult in tests.
+ if (!hint_url_.IsEmpty()) {
+ // This object is created in the main thread, but used in another thread.
+ // We should not share an AtomicString.
+ AtomicString locale = DefaultLanguage();
+ if (locale.length() >= 2) {
+ // DefaultLanguage() is always an ASCII string.
+ hint_language_[0] = static_cast<char>(locale[0]);
+ hint_language_[1] = static_cast<char>(locale[1]);
+ hint_language_[2] = 0;
+ }
+ }
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/text_resource_decoder_options.h b/chromium/third_party/blink/renderer/platform/loader/fetch/text_resource_decoder_options.h
new file mode 100644
index 00000000000..b38a63a6cf9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/text_resource_decoder_options.h
@@ -0,0 +1,99 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_TEXT_RESOURCE_DECODER_OPTIONS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_TEXT_RESOURCE_DECODER_OPTIONS_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT TextResourceDecoderOptions final {
+ public:
+ enum ContentType {
+ kPlainTextContent,
+ kHTMLContent,
+ kJSONContent,
+ kXMLContent,
+ kCSSContent,
+ kMaxContentType = kCSSContent
+ }; // PlainText only checks for BOM.
+
+ explicit TextResourceDecoderOptions(
+ ContentType,
+ const WTF::TextEncoding& default_encoding = WTF::TextEncoding());
+
+ // Corresponds to utf-8 decode in Encoding spec:
+ // https://encoding.spec.whatwg.org/#utf-8-decode.
+ static TextResourceDecoderOptions CreateAlwaysUseUTF8ForText();
+
+ static TextResourceDecoderOptions CreateWithAutoDetection(
+ ContentType,
+ const WTF::TextEncoding& default_encoding,
+ const WTF::TextEncoding& hint_encoding,
+ const KURL& hint_url);
+
+ void SetUseLenientXMLDecoding() { use_lenient_xml_decoding_ = true; }
+ void OverrideContentType(ContentType content_type) {
+ if (encoding_detection_option_ != kAlwaysUseUTF8ForText)
+ content_type_ = content_type;
+ }
+
+ static ContentType DetermineContentType(const String& mime_type);
+
+ // TextResourceDecoder does three kind of encoding detection:
+ // 1. By BOM,
+ // 2. By Content if |content_type_| is not |kPlainTextContext|
+ // (e.g. <meta> tag for HTML), and
+ // 3. By DetectTextEncoding().
+ enum EncodingDetectionOption {
+ // Use 1. + 2. + 3.
+ kUseAllAutoDetection,
+
+ // Use 1. + 2.
+ kUseContentAndBOMBasedDetection,
+
+ // Use None of them.
+ // |content_type_| must be |kPlainTextContent| and
+ // |default_encoding_| must be UTF8Encoding.
+ // This doesn't change encoding based on BOMs, but still processes
+ // utf-8 BOMs so that utf-8 BOMs don't appear in the decoded result.
+ kAlwaysUseUTF8ForText
+ };
+
+ EncodingDetectionOption GetEncodingDetectionOption() const {
+ return encoding_detection_option_;
+ }
+ ContentType GetContentType() const { return content_type_; }
+ const WTF::TextEncoding& DefaultEncoding() const { return default_encoding_; }
+ bool GetUseLenientXMLDecoding() const { return use_lenient_xml_decoding_; }
+
+ const char* HintEncoding() const { return hint_encoding_; }
+ const KURL& HintURL() const { return hint_url_; }
+ const char* HintLanguage() const { return hint_language_; }
+
+ private:
+ TextResourceDecoderOptions(EncodingDetectionOption,
+ ContentType,
+ const WTF::TextEncoding& default_encoding,
+ const char* hint_encoding,
+ const KURL& hint_url);
+
+ EncodingDetectionOption encoding_detection_option_;
+ ContentType content_type_;
+ WTF::TextEncoding default_encoding_;
+ bool use_lenient_xml_decoding_; // Don't stop on XML decoding errors.
+
+ // Hints for DetectTextEncoding().
+ // Only used when |encoding_detection_option_| == |kUseAllAutoDetection|.
+ const char* hint_encoding_;
+ KURL hint_url_;
+ char hint_language_[3];
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/unique_identifier.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/unique_identifier.cc
new file mode 100644
index 00000000000..67cb66ab375
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/unique_identifier.cc
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2013 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 <atomic>
+
+#include "third_party/blink/renderer/platform/loader/fetch/unique_identifier.h"
+
+namespace blink {
+
+static std::atomic_ulong g_unique_identifier(1);
+
+unsigned long CreateUniqueIdentifier() {
+ return g_unique_identifier.fetch_add(1, std::memory_order_relaxed);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/unique_identifier.h b/chromium/third_party/blink/renderer/platform/loader/fetch/unique_identifier.h
new file mode 100644
index 00000000000..b408454b093
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/unique_identifier.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_UNIQUE_IDENTIFIER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_UNIQUE_IDENTIFIER_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+
+PLATFORM_EXPORT unsigned long CreateUniqueIdentifier();
+}
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_UNIQUE_IDENTIFIER_H_
diff --git a/chromium/third_party/blink/renderer/platform/loader/link_header.cc b/chromium/third_party/blink/renderer/platform/loader/link_header.cc
new file mode 100644
index 00000000000..d314eebcbca
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/link_header.cc
@@ -0,0 +1,102 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/loader/link_header.h"
+
+#include "base/strings/string_util.h"
+#include "components/link_header_util/link_header_util.h"
+#include "third_party/blink/renderer/platform/wtf/text/parsing_utilities.h"
+
+namespace blink {
+
+// Verify that the parameter is a link-extension which according to spec doesn't
+// have to have a value.
+static bool IsExtensionParameter(LinkHeader::LinkParameterName name) {
+ return name >= LinkHeader::kLinkParameterUnknown;
+}
+
+static LinkHeader::LinkParameterName ParameterNameFromString(
+ base::StringPiece name) {
+ if (base::EqualsCaseInsensitiveASCII(name, "rel"))
+ return LinkHeader::kLinkParameterRel;
+ if (base::EqualsCaseInsensitiveASCII(name, "anchor"))
+ return LinkHeader::kLinkParameterAnchor;
+ if (base::EqualsCaseInsensitiveASCII(name, "crossorigin"))
+ return LinkHeader::kLinkParameterCrossOrigin;
+ if (base::EqualsCaseInsensitiveASCII(name, "title"))
+ return LinkHeader::kLinkParameterTitle;
+ if (base::EqualsCaseInsensitiveASCII(name, "media"))
+ return LinkHeader::kLinkParameterMedia;
+ if (base::EqualsCaseInsensitiveASCII(name, "type"))
+ return LinkHeader::kLinkParameterType;
+ if (base::EqualsCaseInsensitiveASCII(name, "rev"))
+ return LinkHeader::kLinkParameterRev;
+ if (base::EqualsCaseInsensitiveASCII(name, "hreflang"))
+ return LinkHeader::kLinkParameterHreflang;
+ if (base::EqualsCaseInsensitiveASCII(name, "as"))
+ return LinkHeader::kLinkParameterAs;
+ if (base::EqualsCaseInsensitiveASCII(name, "nonce"))
+ return LinkHeader::kLinkParameterNonce;
+ if (base::EqualsCaseInsensitiveASCII(name, "integrity"))
+ return LinkHeader::kLinkParameterIntegrity;
+ if (base::EqualsCaseInsensitiveASCII(name, "srcset"))
+ return LinkHeader::kLinkParameterSrcset;
+ if (base::EqualsCaseInsensitiveASCII(name, "imgsizes"))
+ return LinkHeader::kLinkParameterImgsizes;
+ return LinkHeader::kLinkParameterUnknown;
+}
+
+void LinkHeader::SetValue(LinkParameterName name, const String& value) {
+ if (name == kLinkParameterRel && !rel_)
+ rel_ = value.DeprecatedLower();
+ else if (name == kLinkParameterAnchor)
+ is_valid_ = false;
+ else if (name == kLinkParameterCrossOrigin)
+ cross_origin_ = value;
+ else if (name == kLinkParameterAs)
+ as_ = value.DeprecatedLower();
+ else if (name == kLinkParameterType)
+ mime_type_ = value.DeprecatedLower();
+ else if (name == kLinkParameterMedia)
+ media_ = value.DeprecatedLower();
+ else if (name == kLinkParameterNonce)
+ nonce_ = value;
+ else if (name == kLinkParameterIntegrity)
+ integrity_ = value;
+ else if (name == kLinkParameterSrcset)
+ srcset_ = value;
+ else if (name == kLinkParameterImgsizes)
+ imgsizes_ = value;
+}
+
+template <typename Iterator>
+LinkHeader::LinkHeader(Iterator begin, Iterator end) : is_valid_(true) {
+ std::string url;
+ std::unordered_map<std::string, base::Optional<std::string>> params;
+ is_valid_ = link_header_util::ParseLinkHeaderValue(begin, end, &url, &params);
+ if (!is_valid_)
+ return;
+
+ url_ = String(&url[0], url.length());
+ for (const auto& param : params) {
+ LinkParameterName name = ParameterNameFromString(param.first);
+ if (!IsExtensionParameter(name) && !param.second)
+ is_valid_ = false;
+ std::string value = param.second.value_or("");
+ SetValue(name, String(&value[0], value.length()));
+ }
+}
+
+LinkHeaderSet::LinkHeaderSet(const String& header) {
+ if (header.IsNull())
+ return;
+
+ DCHECK(header.Is8Bit()) << "Headers should always be 8 bit";
+ std::string header_string(reinterpret_cast<const char*>(header.Characters8()),
+ header.length());
+ for (const auto& value : link_header_util::SplitLinkHeader(header_string))
+ header_set_.push_back(LinkHeader(value.first, value.second));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/link_header.h b/chromium/third_party/blink/renderer/platform/loader/link_header.h
new file mode 100644
index 00000000000..61d5e41310f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/link_header.h
@@ -0,0 +1,87 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_LINK_HEADER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_LINK_HEADER_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class LinkHeader {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ const String& Url() const { return url_; }
+ const String& Rel() const { return rel_; }
+ const String& As() const { return as_; }
+ const String& MimeType() const { return mime_type_; }
+ const String& Media() const { return media_; }
+ const String& CrossOrigin() const { return cross_origin_; }
+ const String& Nonce() const { return nonce_; }
+ const String& Integrity() const { return integrity_; }
+ const String& Srcset() const { return srcset_; }
+ const String& Imgsizes() const { return imgsizes_; }
+ bool Valid() const { return is_valid_; }
+
+ enum LinkParameterName {
+ kLinkParameterRel,
+ kLinkParameterAnchor,
+ kLinkParameterTitle,
+ kLinkParameterMedia,
+ kLinkParameterType,
+ kLinkParameterRev,
+ kLinkParameterHreflang,
+ // Beyond this point, only link-extension parameters
+ kLinkParameterUnknown,
+ kLinkParameterCrossOrigin,
+ kLinkParameterAs,
+ kLinkParameterNonce,
+ kLinkParameterIntegrity,
+ kLinkParameterSrcset,
+ kLinkParameterImgsizes,
+ };
+
+ private:
+ friend class LinkHeaderSet;
+
+ template <typename Iterator>
+ LinkHeader(Iterator begin, Iterator end);
+ void SetValue(LinkParameterName, const String& value);
+
+ String url_;
+ String rel_;
+ String as_;
+ String mime_type_;
+ String media_;
+ String cross_origin_;
+ String nonce_;
+ String integrity_;
+ String srcset_;
+ String imgsizes_;
+ bool is_valid_;
+};
+
+class PLATFORM_EXPORT LinkHeaderSet {
+ STACK_ALLOCATED();
+
+ public:
+ LinkHeaderSet(const String& header);
+
+ Vector<LinkHeader>::const_iterator begin() const {
+ return header_set_.begin();
+ }
+ Vector<LinkHeader>::const_iterator end() const { return header_set_.end(); }
+ LinkHeader& operator[](size_t i) { return header_set_[i]; }
+ size_t size() { return header_set_.size(); }
+
+ private:
+ Vector<LinkHeader> header_set_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/link_header_test.cc b/chromium/third_party/blink/renderer/platform/loader/link_header_test.cc
new file mode 100644
index 00000000000..a82f7b897fa
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/link_header_test.cc
@@ -0,0 +1,287 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/loader/link_header.h"
+
+#include <base/macros.h>
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+namespace {
+
+TEST(LinkHeaderTest, Empty) {
+ String null_string;
+ LinkHeaderSet null_header_set(null_string);
+ ASSERT_EQ(null_header_set.size(), unsigned(0));
+ String empty_string("");
+ LinkHeaderSet empty_header_set(empty_string);
+ ASSERT_EQ(empty_header_set.size(), unsigned(0));
+}
+
+struct SingleTestCase {
+ const char* header_value;
+ bool valid;
+ const char* url;
+ const char* rel;
+ const char* as;
+ const char* media;
+} g_single_test_cases[] = {
+ {"</images/cat.jpg>; rel=prefetch", true, "/images/cat.jpg", "prefetch", "",
+ ""},
+ {"</images/cat.jpg>;rel=prefetch", true, "/images/cat.jpg", "prefetch", "",
+ ""},
+ {"</images/cat.jpg> ;rel=prefetch", true, "/images/cat.jpg", "prefetch",
+ "", ""},
+ {"</images/cat.jpg> ; rel=prefetch", true, "/images/cat.jpg",
+ "prefetch", "", ""},
+ {"< /images/cat.jpg> ; rel=prefetch", true, "/images/cat.jpg",
+ "prefetch", "", ""},
+ {"</images/cat.jpg > ; rel=prefetch", true, "/images/cat.jpg",
+ "prefetch", "", ""},
+ {"</images/cat.jpg wutwut> ; rel=prefetch", true,
+ "/images/cat.jpg wutwut", "prefetch", "", ""},
+ {"</images/cat.jpg wutwut \t > ; rel=prefetch", true,
+ "/images/cat.jpg wutwut", "prefetch", "", ""},
+ {"</images/cat.jpg>; rel=prefetch ", true, "/images/cat.jpg", "prefetch",
+ "", ""},
+ {"</images/cat.jpg>; Rel=prefetch ", true, "/images/cat.jpg", "prefetch",
+ "", ""},
+ {"</images/cat.jpg>; Rel=PReFetCh ", true, "/images/cat.jpg", "prefetch",
+ "", ""},
+ {"</images/cat.jpg>; rel=prefetch; rel=somethingelse", true,
+ "/images/cat.jpg", "prefetch", "", ""},
+ {" </images/cat.jpg>; rel=prefetch ", true, "/images/cat.jpg",
+ "prefetch", "", ""},
+ {"\t </images/cat.jpg>; rel=prefetch ", true, "/images/cat.jpg",
+ "prefetch", "", ""},
+ {"</images/cat.jpg>\t\t ; \trel=prefetch \t ", true, "/images/cat.jpg",
+ "prefetch", "", ""},
+ {"\f</images/cat.jpg>\t\t ; \trel=prefetch \t ", false},
+ {"</images/cat.jpg>; rel= prefetch", true, "/images/cat.jpg", "prefetch",
+ "", ""},
+ {"<../images/cat.jpg?dog>; rel= prefetch", true, "../images/cat.jpg?dog",
+ "prefetch", "", ""},
+ {"</images/cat.jpg>; rel =prefetch", true, "/images/cat.jpg", "prefetch",
+ "", ""},
+ {"</images/cat.jpg>; rel pel=prefetch", false},
+ {"< /images/cat.jpg>", true, "/images/cat.jpg", "", "", ""},
+ {"</images/cat.jpg>; rel =", false},
+ {"</images/cat.jpg>; wut=sup; rel =prefetch", true, "/images/cat.jpg",
+ "prefetch", "", ""},
+ {"</images/cat.jpg>; wut=sup ; rel =prefetch", true, "/images/cat.jpg",
+ "prefetch", "", ""},
+ {"</images/cat.jpg>; wut=sup ; rel =prefetch \t ;", true,
+ "/images/cat.jpg", "prefetch", "", ""},
+ {"</images/cat.jpg> wut=sup ; rel =prefetch \t ;", false},
+ {"< /images/cat.jpg", false},
+ {"< http://wut.com/ sdfsdf ?sd>; rel=dns-prefetch", true,
+ "http://wut.com/ sdfsdf ?sd", "dns-prefetch", "", ""},
+ {"< http://wut.com/%20%20%3dsdfsdf?sd>; rel=dns-prefetch", true,
+ "http://wut.com/%20%20%3dsdfsdf?sd", "dns-prefetch", "", ""},
+ {"< http://wut.com/dfsdf?sdf=ghj&wer=rty>; rel=prefetch", true,
+ "http://wut.com/dfsdf?sdf=ghj&wer=rty", "prefetch", "", ""},
+ {"< http://wut.com/dfsdf?sdf=ghj&wer=rty>;;;;; rel=prefetch", true,
+ "http://wut.com/dfsdf?sdf=ghj&wer=rty", "prefetch", "", ""},
+ {"< http://wut.com/%20%20%3dsdfsdf?sd>; rel=preload;as=image", true,
+ "http://wut.com/%20%20%3dsdfsdf?sd", "preload", "image", ""},
+ {"< http://wut.com/%20%20%3dsdfsdf?sd>; rel=preload;as=whatever", true,
+ "http://wut.com/%20%20%3dsdfsdf?sd", "preload", "whatever", ""},
+ {"</images/cat.jpg>; anchor=foo; rel=prefetch;", false},
+ {"</images/cat.jpg>; rel=prefetch;anchor=foo ", false},
+ {"</images/cat.jpg>; anchor='foo'; rel=prefetch;", false},
+ {"</images/cat.jpg>; rel=prefetch;anchor='foo' ", false},
+ {"</images/cat.jpg>; rel=prefetch;anchor='' ", false},
+ {"</images/cat.jpg>; rel=prefetch;", true, "/images/cat.jpg", "prefetch",
+ "", ""},
+ {"</images/cat.jpg>; rel=prefetch ;", true, "/images/cat.jpg",
+ "prefetch", "", ""},
+ {"</images/ca,t.jpg>; rel=prefetch ;", true, "/images/ca,t.jpg",
+ "prefetch", "", ""},
+ {"<simple.css>; rel=stylesheet; title=\"title with a DQUOTE and "
+ "backslash\"",
+ true, "simple.css", "stylesheet", "", ""},
+ {"<simple.css>; rel=stylesheet; title=\"title with a DQUOTE \\\" and "
+ "backslash: \\\"",
+ false},
+ {"<simple.css>; title=\"title with a DQUOTE \\\" and backslash: \"; "
+ "rel=stylesheet; ",
+ true, "simple.css", "stylesheet", "", ""},
+ {"<simple.css>; title=\'title with a DQUOTE \\\' and backslash: \'; "
+ "rel=stylesheet; ",
+ true, "simple.css", "stylesheet", "", ""},
+ {"<simple.css>; title=\"title with a DQUOTE \\\" and ;backslash,: \"; "
+ "rel=stylesheet; ",
+ true, "simple.css", "stylesheet", "", ""},
+ {"<simple.css>; title=\"title with a DQUOTE \' and ;backslash,: \"; "
+ "rel=stylesheet; ",
+ true, "simple.css", "stylesheet", "", ""},
+ {"<simple.css>; title=\"\"; rel=stylesheet; ", true, "simple.css",
+ "stylesheet", "", ""},
+ {"<simple.css>; title=\"\"; rel=\"stylesheet\"; ", true, "simple.css",
+ "stylesheet", "", ""},
+ {"<simple.css>; rel=stylesheet; title=\"", false},
+ {"<simple.css>; rel=stylesheet; title=\"\"", true, "simple.css",
+ "stylesheet", "", ""},
+ {"<simple.css>; rel=\"stylesheet\"; title=\"", false},
+ {"<simple.css>; rel=\";style,sheet\"; title=\"", false},
+ {"<simple.css>; rel=\"bla'sdf\"; title=\"", false},
+ {"<simple.css>; rel=\"\"; title=\"\"", true, "simple.css", "", "", ""},
+ {"<simple.css>; rel=''; title=\"\"", true, "simple.css", "''", "", ""},
+ {"<simple.css>; rel=''; title=", false},
+ {"<simple.css>; rel=''; title", false},
+ {"<simple.css>; rel=''; media", false},
+ {"<simple.css>; rel=''; hreflang", false},
+ {"<simple.css>; rel=''; type", false},
+ {"<simple.css>; rel=''; rev", false},
+ {"<simple.css>; rel=''; bla", true, "simple.css", "''", "", ""},
+ {"<simple.css>; rel='prefetch", true, "simple.css", "'prefetch", "", ""},
+ {"<simple.css>; rel=\"prefetch", false},
+ {"<simple.css>; rel=\"", false},
+ {"<http://whatever.com>; rel=preconnect; valid!", true,
+ "http://whatever.com", "preconnect", "", ""},
+ {"<http://whatever.com>; rel=preconnect; valid$", true,
+ "http://whatever.com", "preconnect", "", ""},
+ {"<http://whatever.com>; rel=preconnect; invalid@", false},
+ {"<http://whatever.com>; rel=preconnect; invalid*", false},
+ {"</images/cat.jpg>; rel=prefetch;media='(max-width: 5000px)'", true,
+ "/images/cat.jpg", "prefetch", "", "'(max-width: 5000px)'"},
+ {"</images/cat.jpg>; rel=prefetch;media=\"(max-width: 5000px)\"", true,
+ "/images/cat.jpg", "prefetch", "", "(max-width: 5000px)"},
+ {"</images/cat.jpg>; rel=prefetch;media=(max-width:5000px)", true,
+ "/images/cat.jpg", "prefetch", "", "(max-width:5000px)"},
+};
+
+void PrintTo(const SingleTestCase& test, std::ostream* os) {
+ *os << testing::PrintToString(test.header_value);
+}
+
+class SingleLinkHeaderTest : public testing::TestWithParam<SingleTestCase> {};
+
+// Test the cases with a single header
+TEST_P(SingleLinkHeaderTest, Single) {
+ const SingleTestCase test_case = GetParam();
+ LinkHeaderSet header_set(test_case.header_value);
+ ASSERT_EQ(1u, header_set.size());
+ LinkHeader& header = header_set[0];
+ EXPECT_EQ(test_case.valid, header.Valid());
+ if (test_case.valid) {
+ EXPECT_STREQ(test_case.url, header.Url().Ascii().data());
+ EXPECT_STREQ(test_case.rel, header.Rel().Ascii().data());
+ EXPECT_STREQ(test_case.as, header.As().Ascii().data());
+ EXPECT_STREQ(test_case.media, header.Media().Ascii().data());
+ }
+}
+
+INSTANTIATE_TEST_CASE_P(LinkHeaderTest,
+ SingleLinkHeaderTest,
+ testing::ValuesIn(g_single_test_cases));
+
+struct DoubleTestCase {
+ const char* header_value;
+ const char* url;
+ const char* rel;
+ bool valid;
+ const char* url2;
+ const char* rel2;
+ bool valid2;
+} g_double_test_cases[] = {
+ {"<ybg.css>; rel=stylesheet, <simple.css>; rel=stylesheet", "ybg.css",
+ "stylesheet", true, "simple.css", "stylesheet", true},
+ {"<ybg.css>; rel=stylesheet,<simple.css>; rel=stylesheet", "ybg.css",
+ "stylesheet", true, "simple.css", "stylesheet", true},
+ {"<ybg.css>; rel=stylesheet;crossorigin,<simple.css>; rel=stylesheet",
+ "ybg.css", "stylesheet", true, "simple.css", "stylesheet", true},
+ {"<hel,lo.css>; rel=stylesheet; title=\"foo,bar\", <simple.css>; "
+ "rel=stylesheet; title=\"foo;bar\"",
+ "hel,lo.css", "stylesheet", true, "simple.css", "stylesheet", true},
+};
+
+void PrintTo(const DoubleTestCase& test, std::ostream* os) {
+ *os << testing::PrintToString(test.header_value);
+}
+
+class DoubleLinkHeaderTest : public testing::TestWithParam<DoubleTestCase> {};
+
+TEST_P(DoubleLinkHeaderTest, Double) {
+ const DoubleTestCase test_case = GetParam();
+ LinkHeaderSet header_set(test_case.header_value);
+ ASSERT_EQ(2u, header_set.size());
+ LinkHeader& header1 = header_set[0];
+ LinkHeader& header2 = header_set[1];
+ EXPECT_STREQ(test_case.url, header1.Url().Ascii().data());
+ EXPECT_STREQ(test_case.rel, header1.Rel().Ascii().data());
+ EXPECT_EQ(test_case.valid, header1.Valid());
+ EXPECT_STREQ(test_case.url2, header2.Url().Ascii().data());
+ EXPECT_STREQ(test_case.rel2, header2.Rel().Ascii().data());
+ EXPECT_EQ(test_case.valid2, header2.Valid());
+}
+
+INSTANTIATE_TEST_CASE_P(LinkHeaderTest,
+ DoubleLinkHeaderTest,
+ testing::ValuesIn(g_double_test_cases));
+
+struct CrossOriginTestCase {
+ const char* header_value;
+ const char* url;
+ const char* rel;
+ const char* crossorigin;
+ bool valid;
+} g_cross_origin_test_cases[] = {
+ {"<http://whatever.com>; rel=preconnect", "http://whatever.com",
+ "preconnect", nullptr, true},
+ {"<http://whatever.com>; rel=preconnect; crossorigin=", "", "", "", false},
+ {"<http://whatever.com>; rel=preconnect; crossorigin",
+ "http://whatever.com", "preconnect", "", true},
+ {"<http://whatever.com>; rel=preconnect; crossorigin ",
+ "http://whatever.com", "preconnect", "", true},
+ {"<http://whatever.com>; rel=preconnect; crossorigin;",
+ "http://whatever.com", "preconnect", "", true},
+ {"<http://whatever.com>; rel=preconnect; crossorigin, "
+ "<http://whatever2.com>; rel=preconnect",
+ "http://whatever.com", "preconnect", "", true},
+ {"<http://whatever.com>; rel=preconnect; crossorigin , "
+ "<http://whatever2.com>; rel=preconnect",
+ "http://whatever.com", "preconnect", "", true},
+ {"<http://whatever.com>; rel=preconnect; "
+ "crossorigin,<http://whatever2.com>; rel=preconnect",
+ "http://whatever.com", "preconnect", "", true},
+ {"<http://whatever.com>; rel=preconnect; crossorigin=anonymous",
+ "http://whatever.com", "preconnect", "anonymous", true},
+ {"<http://whatever.com>; rel=preconnect; crossorigin=use-credentials",
+ "http://whatever.com", "preconnect", "use-credentials", true},
+ {"<http://whatever.com>; rel=preconnect; crossorigin=whatever",
+ "http://whatever.com", "preconnect", "whatever", true},
+ {"<http://whatever.com>; rel=preconnect; crossorig|in=whatever",
+ "http://whatever.com", "preconnect", nullptr, true},
+ {"<http://whatever.com>; rel=preconnect; crossorigin|=whatever",
+ "http://whatever.com", "preconnect", nullptr, true},
+};
+
+void PrintTo(const CrossOriginTestCase& test, std::ostream* os) {
+ *os << testing::PrintToString(test.header_value);
+}
+
+class CrossOriginLinkHeaderTest
+ : public testing::TestWithParam<CrossOriginTestCase> {};
+
+TEST_P(CrossOriginLinkHeaderTest, CrossOrigin) {
+ const CrossOriginTestCase test_case = GetParam();
+ LinkHeaderSet header_set(test_case.header_value);
+ ASSERT_GE(header_set.size(), 1u);
+ LinkHeader& header = header_set[0];
+ EXPECT_STREQ(test_case.url, header.Url().Ascii().data());
+ EXPECT_STREQ(test_case.rel, header.Rel().Ascii().data());
+ EXPECT_EQ(test_case.valid, header.Valid());
+ if (!test_case.crossorigin)
+ EXPECT_TRUE(header.CrossOrigin().IsNull());
+ else
+ EXPECT_STREQ(test_case.crossorigin, header.CrossOrigin().Ascii().data());
+}
+
+INSTANTIATE_TEST_CASE_P(LinkHeaderTest,
+ CrossOriginLinkHeaderTest,
+ testing::ValuesIn(g_cross_origin_test_cases));
+
+} // namespace
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/subresource_integrity.cc b/chromium/third_party/blink/renderer/platform/loader/subresource_integrity.cc
new file mode 100644
index 00000000000..7d6141afa47
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/subresource_integrity.cc
@@ -0,0 +1,503 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/loader/subresource_integrity.h"
+
+#include "third_party/blink/public/platform/web_crypto.h"
+#include "third_party/blink/public/platform/web_crypto_algorithm.h"
+#include "third_party/blink/renderer/platform/crypto.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+#include "third_party/blink/renderer/platform/wtf/ascii_ctype.h"
+#include "third_party/blink/renderer/platform/wtf/dtoa/utils.h"
+#include "third_party/blink/renderer/platform/wtf/text/base64.h"
+#include "third_party/blink/renderer/platform/wtf/text/parsing_utilities.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "third_party/boringssl/src/include/openssl/curve25519.h"
+
+namespace blink {
+
+// FIXME: This should probably use common functions with ContentSecurityPolicy.
+static bool IsIntegrityCharacter(UChar c) {
+ // Check if it's a base64 encoded value. We're pretty loose here, as there's
+ // not much risk in it, and it'll make it simpler for developers.
+ return IsASCIIAlphanumeric(c) || c == '_' || c == '-' || c == '+' ||
+ c == '/' || c == '=';
+}
+
+static bool IsValueCharacter(UChar c) {
+ // VCHAR per https://tools.ietf.org/html/rfc5234#appendix-B.1
+ return c >= 0x21 && c <= 0x7e;
+}
+
+static bool DigestsEqual(const DigestValue& digest1,
+ const DigestValue& digest2) {
+ if (digest1.size() != digest2.size())
+ return false;
+
+ for (size_t i = 0; i < digest1.size(); i++) {
+ if (digest1[i] != digest2[i])
+ return false;
+ }
+
+ return true;
+}
+
+inline bool IsSpaceOrComma(UChar c) {
+ return IsASCIISpace(c) || c == ',';
+}
+
+static String DigestToString(const DigestValue& digest) {
+ return Base64Encode(reinterpret_cast<const char*>(digest.data()),
+ digest.size(), kBase64DoNotInsertLFs);
+}
+
+void SubresourceIntegrity::ReportInfo::AddUseCount(UseCounterFeature feature) {
+ use_counts_.push_back(feature);
+}
+
+void SubresourceIntegrity::ReportInfo::AddConsoleErrorMessage(
+ const String& message) {
+ console_error_messages_.push_back(message);
+}
+
+void SubresourceIntegrity::ReportInfo::Clear() {
+ use_counts_.clear();
+ console_error_messages_.clear();
+}
+
+bool SubresourceIntegrity::CheckSubresourceIntegrity(
+ const IntegrityMetadataSet& metadata_set,
+ const char* content,
+ size_t size,
+ const KURL& resource_url,
+ const Resource& resource,
+ ReportInfo& report_info) {
+ if (!resource.IsSameOriginOrCORSSuccessful()) {
+ report_info.AddConsoleErrorMessage(
+ "Subresource Integrity: The resource '" + resource_url.ElidedString() +
+ "' has an integrity attribute, but the resource "
+ "requires the request to be CORS enabled to check "
+ "the integrity, and it is not. The resource has been "
+ "blocked because the integrity cannot be enforced.");
+ report_info.AddUseCount(ReportInfo::UseCounterFeature::
+ kSRIElementIntegrityAttributeButIneligible);
+ return false;
+ }
+
+ return CheckSubresourceIntegrityImpl(
+ metadata_set, content, size, resource_url,
+ resource.GetResponse().HttpHeaderField("Integrity"), report_info);
+}
+
+bool SubresourceIntegrity::CheckSubresourceIntegrity(
+ const String& integrity_metadata,
+ IntegrityFeatures features,
+ const char* content,
+ size_t size,
+ const KURL& resource_url,
+ ReportInfo& report_info) {
+ if (integrity_metadata.IsEmpty())
+ return true;
+
+ IntegrityMetadataSet metadata_set;
+ IntegrityParseResult integrity_parse_result = ParseIntegrityAttribute(
+ integrity_metadata, features, metadata_set, &report_info);
+ if (integrity_parse_result != kIntegrityParseValidResult)
+ return true;
+ // TODO(vogelheim): crbug.com/753349, figure out how deal with Ed25519
+ // checking here.
+ String integrity_header;
+ return CheckSubresourceIntegrityImpl(
+ metadata_set, content, size, resource_url, integrity_header, report_info);
+}
+
+bool SubresourceIntegrity::CheckSubresourceIntegrityImpl(
+ const IntegrityMetadataSet& metadata_set,
+ const char* content,
+ size_t size,
+ const KURL& resource_url,
+ const String integrity_header,
+ ReportInfo& report_info) {
+ if (!metadata_set.size())
+ return true;
+
+ // Check any of the "strongest" integrity constraints.
+ IntegrityAlgorithm max_algorithm = FindBestAlgorithm(metadata_set);
+ CheckFunction checker = GetCheckFunctionForAlgorithm(max_algorithm);
+ bool report_ed25519 = max_algorithm == IntegrityAlgorithm::kEd25519;
+ if (report_ed25519) {
+ report_info.AddUseCount(ReportInfo::UseCounterFeature::kSRISignatureCheck);
+ }
+ for (const IntegrityMetadata& metadata : metadata_set) {
+ if (metadata.Algorithm() == max_algorithm &&
+ (*checker)(metadata, content, size, integrity_header)) {
+ report_info.AddUseCount(ReportInfo::UseCounterFeature::
+ kSRIElementWithMatchingIntegrityAttribute);
+ if (report_ed25519) {
+ report_info.AddUseCount(
+ ReportInfo::UseCounterFeature::kSRISignatureSuccess);
+ }
+ return true;
+ }
+ }
+
+ // If we arrive here, none of the "strongest" constaints have validated
+ // the data we received. Report this fact.
+ DigestValue digest;
+ if (ComputeDigest(kHashAlgorithmSha256, content, size, digest)) {
+ // This message exposes the digest of the resource to the console.
+ // Because this is only to the console, that's okay for now, but we
+ // need to be very careful not to expose this in exceptions or
+ // JavaScript, otherwise it risks exposing information about the
+ // resource cross-origin.
+ report_info.AddConsoleErrorMessage(
+ "Failed to find a valid digest in the 'integrity' attribute for "
+ "resource '" +
+ resource_url.ElidedString() + "' with computed SHA-256 integrity '" +
+ DigestToString(digest) + "'. The resource has been blocked.");
+ } else {
+ report_info.AddConsoleErrorMessage(
+ "There was an error computing an integrity value for resource '" +
+ resource_url.ElidedString() + "'. The resource has been blocked.");
+ }
+ report_info.AddUseCount(ReportInfo::UseCounterFeature::
+ kSRIElementWithNonMatchingIntegrityAttribute);
+ return false;
+}
+
+IntegrityAlgorithm SubresourceIntegrity::FindBestAlgorithm(
+ const IntegrityMetadataSet& metadata_set) {
+ // Find the "strongest" algorithm in the set. (This relies on
+ // IntegrityAlgorithm declaration order matching the "strongest" order, so
+ // make the compiler check this assumption first.)
+ static_assert(IntegrityAlgorithm::kSha256 < IntegrityAlgorithm::kSha384 &&
+ IntegrityAlgorithm::kSha384 < IntegrityAlgorithm::kSha512 &&
+ IntegrityAlgorithm::kSha512 < IntegrityAlgorithm::kEd25519,
+ "IntegrityAlgorithm enum order should match the priority "
+ "of the integrity algorithms.");
+
+ // metadata_set is non-empty, so we are guaranteed to always have a result.
+ // This is effectively an implemenation of std::max_element (C++17).
+ DCHECK(!metadata_set.IsEmpty());
+ auto iter = metadata_set.begin();
+ IntegrityAlgorithm max_algorithm = iter->second;
+ ++iter;
+ for (; iter != metadata_set.end(); ++iter) {
+ max_algorithm = std::max(iter->second, max_algorithm);
+ }
+ return max_algorithm;
+}
+
+SubresourceIntegrity::CheckFunction
+SubresourceIntegrity::GetCheckFunctionForAlgorithm(
+ IntegrityAlgorithm algorithm) {
+ switch (algorithm) {
+ case IntegrityAlgorithm::kSha256:
+ case IntegrityAlgorithm::kSha384:
+ case IntegrityAlgorithm::kSha512:
+ return SubresourceIntegrity::CheckSubresourceIntegrityDigest;
+ case IntegrityAlgorithm::kEd25519:
+ return SubresourceIntegrity::CheckSubresourceIntegritySignature;
+ }
+ NOTREACHED();
+ return nullptr;
+}
+
+bool SubresourceIntegrity::CheckSubresourceIntegrityDigest(
+ const IntegrityMetadata& metadata,
+ const char* content,
+ size_t size,
+ const String& integrity_header) {
+ blink::HashAlgorithm hash_algo = kHashAlgorithmSha256;
+ switch (metadata.Algorithm()) {
+ case IntegrityAlgorithm::kSha256:
+ hash_algo = kHashAlgorithmSha256;
+ break;
+ case IntegrityAlgorithm::kSha384:
+ hash_algo = kHashAlgorithmSha384;
+ break;
+ case IntegrityAlgorithm::kSha512:
+ hash_algo = kHashAlgorithmSha512;
+ break;
+ case IntegrityAlgorithm::kEd25519:
+ NOTREACHED();
+ break;
+ }
+
+ DigestValue digest;
+ if (!ComputeDigest(hash_algo, content, size, digest))
+ return false;
+
+ Vector<char> hash_vector;
+ Base64Decode(metadata.Digest(), hash_vector);
+ DigestValue converted_hash_vector;
+ converted_hash_vector.Append(reinterpret_cast<uint8_t*>(hash_vector.data()),
+ hash_vector.size());
+ return DigestsEqual(digest, converted_hash_vector);
+}
+
+bool SubresourceIntegrity::CheckSubresourceIntegritySignature(
+ const IntegrityMetadata& metadata,
+ const char* content,
+ size_t size,
+ const String& integrity_header) {
+ DCHECK_EQ(IntegrityAlgorithm::kEd25519, metadata.Algorithm());
+
+ Vector<char> pubkey;
+ if (!Base64Decode(metadata.Digest(), pubkey) ||
+ pubkey.size() != ED25519_PUBLIC_KEY_LEN)
+ return false;
+
+ // Parse the Integrity:-header containing the signature(s).
+ Vector<UChar> integrity_header_chars;
+ integrity_header.AppendTo(integrity_header_chars);
+ const UChar* position = integrity_header_chars.begin();
+
+ const UChar* const end_position = integrity_header_chars.end();
+ while (position < end_position) {
+ // We expect substrings of the form "ed25519-<BASE64>* ,".
+ // We'll move all of our UChar* pointers up front (before any early exits
+ // from the loop), since we should cleanly skip the next token in the
+ // header in all cases, even if the current token doesn't validate.
+ SkipWhile<UChar, IsSpaceOrComma>(position, end_position);
+ IntegrityAlgorithm algorithm;
+ bool found_ed25519 =
+ kAlgorithmValid ==
+ ParseIntegrityHeaderAlgorithm(position, end_position, algorithm) &&
+ IntegrityAlgorithm::kEd25519 == algorithm;
+ const UChar* digest_begin = position;
+ SkipUntil<UChar, IsSpaceOrComma>(position, end_position);
+ const UChar* const digest_end = position;
+
+ // Now, algorithm contains the parsed algorithm specifier, the digest is
+ // found at digest_begin..digest_end, and position sits before the next
+ // token.
+
+ if (!found_ed25519)
+ continue;
+
+ String signature_raw;
+ if (!ParseDigest(digest_begin, digest_end, signature_raw))
+ continue;
+
+ Vector<char> signature;
+ Base64Decode(signature_raw, signature);
+ if (signature.size() != ED25519_SIGNATURE_LEN)
+ continue;
+
+ // BoringSSL/OpenSSL functions return 1 for success.
+ if (1 ==
+ ED25519_verify(reinterpret_cast<const uint8_t*>(content), size,
+ reinterpret_cast<const uint8_t*>(&*signature.begin()),
+ reinterpret_cast<const uint8_t*>(&*pubkey.begin()))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+SubresourceIntegrity::AlgorithmParseResult
+SubresourceIntegrity::ParseAttributeAlgorithm(const UChar*& begin,
+ const UChar* end,
+ IntegrityFeatures features,
+ IntegrityAlgorithm& algorithm) {
+ static const AlgorithmPrefixPair kPrefixes[] = {
+ {"sha256", IntegrityAlgorithm::kSha256},
+ {"sha-256", IntegrityAlgorithm::kSha256},
+ {"sha384", IntegrityAlgorithm::kSha384},
+ {"sha-384", IntegrityAlgorithm::kSha384},
+ {"sha512", IntegrityAlgorithm::kSha512},
+ {"sha-512", IntegrityAlgorithm::kSha512},
+ {"ed25519", IntegrityAlgorithm::kEd25519}};
+
+ // The last algorithm prefix is the ed25519 signature algorithm, which should
+ // only be enabled if kSignatures is requested. We'll implement this by
+ // adjusting the last_prefix index into the array.
+ size_t last_prefix = WTF_ARRAY_LENGTH(kPrefixes);
+ if (features != IntegrityFeatures::kSignatures)
+ last_prefix--;
+
+ return ParseAlgorithmPrefix(begin, end, kPrefixes, last_prefix, algorithm);
+}
+
+SubresourceIntegrity::AlgorithmParseResult
+SubresourceIntegrity::ParseIntegrityHeaderAlgorithm(
+ const UChar*& begin,
+ const UChar* end,
+ IntegrityAlgorithm& algorithm) {
+ static const AlgorithmPrefixPair kPrefixes[] = {
+ {"ed25519", IntegrityAlgorithm::kEd25519}};
+ return ParseAlgorithmPrefix(begin, end, kPrefixes,
+ WTF_ARRAY_LENGTH(kPrefixes), algorithm);
+}
+
+SubresourceIntegrity::AlgorithmParseResult
+SubresourceIntegrity::ParseAlgorithmPrefix(
+ const UChar*& string_position,
+ const UChar* string_end,
+ const AlgorithmPrefixPair* prefix_table,
+ size_t prefix_table_size,
+ IntegrityAlgorithm& algorithm) {
+ for (size_t i = 0; i < prefix_table_size; i++) {
+ const UChar* pos = string_position;
+ if (SkipToken<UChar>(pos, string_end, prefix_table[i].first) &&
+ SkipExactly<UChar>(pos, string_end, '-')) {
+ string_position = pos;
+ algorithm = prefix_table[i].second;
+ return kAlgorithmValid;
+ }
+ }
+
+ const UChar* dash_position = string_position;
+ SkipUntil<UChar>(dash_position, string_end, '-');
+ return dash_position < string_end ? kAlgorithmUnknown : kAlgorithmUnparsable;
+}
+
+// Before:
+//
+// [algorithm]-[hash] OR [algorithm]-[hash]?[options]
+// ^ ^ ^ ^
+// position end position end
+//
+// After (if successful: if the method returns false, we make no promises and
+// the caller should exit early):
+//
+// [algorithm]-[hash] OR [algorithm]-[hash]?[options]
+// ^ ^ ^
+// position/end position end
+bool SubresourceIntegrity::ParseDigest(const UChar*& position,
+ const UChar* end,
+ String& digest) {
+ const UChar* begin = position;
+ SkipWhile<UChar, IsIntegrityCharacter>(position, end);
+ if (position == begin || (position != end && *position != '?')) {
+ digest = g_empty_string;
+ return false;
+ }
+
+ // We accept base64url encoding, but normalize to "normal" base64 internally:
+ digest = NormalizeToBase64(String(begin, position - begin));
+ return true;
+}
+
+SubresourceIntegrity::IntegrityParseResult
+SubresourceIntegrity::ParseIntegrityAttribute(
+ const WTF::String& attribute,
+ IntegrityFeatures features,
+ IntegrityMetadataSet& metadata_set) {
+ return ParseIntegrityAttribute(attribute, features, metadata_set, nullptr);
+}
+
+SubresourceIntegrity::IntegrityParseResult
+SubresourceIntegrity::ParseIntegrityAttribute(
+ const WTF::String& attribute,
+ IntegrityFeatures features,
+ IntegrityMetadataSet& metadata_set,
+ ReportInfo* report_info) {
+ // We expect a "clean" metadata_set, since metadata_set should only be filled
+ // once.
+ DCHECK(metadata_set.IsEmpty());
+
+ Vector<UChar> characters;
+ attribute.StripWhiteSpace().AppendTo(characters);
+ const UChar* position = characters.data();
+ const UChar* end = characters.end();
+ const UChar* current_integrity_end;
+
+ bool error = false;
+
+ // The integrity attribute takes the form:
+ // *WSP hash-with-options *( 1*WSP hash-with-options ) *WSP / *WSP
+ // To parse this, break on whitespace, parsing each algorithm/digest/option
+ // in order.
+ while (position < end) {
+ WTF::String digest;
+ IntegrityAlgorithm algorithm;
+
+ SkipWhile<UChar, IsASCIISpace>(position, end);
+ current_integrity_end = position;
+ SkipUntil<UChar, IsASCIISpace>(current_integrity_end, end);
+
+ // Algorithm parsing errors are non-fatal (the subresource should
+ // still be loaded) because strong hash algorithms should be used
+ // without fear of breaking older user agents that don't support
+ // them.
+ AlgorithmParseResult parse_result = ParseAttributeAlgorithm(
+ position, current_integrity_end, features, algorithm);
+ if (parse_result == kAlgorithmUnknown) {
+ // Unknown hash algorithms are treated as if they're not present,
+ // and thus are not marked as an error, they're just skipped.
+ SkipUntil<UChar, IsASCIISpace>(position, end);
+ if (report_info) {
+ report_info->AddConsoleErrorMessage(
+ "Error parsing 'integrity' attribute ('" + attribute +
+ "'). The specified hash algorithm must be one of "
+ "'sha256', 'sha384', or 'sha512'.");
+ report_info->AddUseCount(
+ ReportInfo::UseCounterFeature::
+ kSRIElementWithUnparsableIntegrityAttribute);
+ }
+ continue;
+ }
+
+ if (parse_result == kAlgorithmUnparsable) {
+ error = true;
+ SkipUntil<UChar, IsASCIISpace>(position, end);
+ if (report_info) {
+ report_info->AddConsoleErrorMessage(
+ "Error parsing 'integrity' attribute ('" + attribute +
+ "'). The hash algorithm must be one of 'sha256', "
+ "'sha384', or 'sha512', followed by a '-' "
+ "character.");
+ report_info->AddUseCount(
+ ReportInfo::UseCounterFeature::
+ kSRIElementWithUnparsableIntegrityAttribute);
+ }
+ continue;
+ }
+
+ DCHECK_EQ(parse_result, kAlgorithmValid);
+
+ if (!ParseDigest(position, current_integrity_end, digest)) {
+ error = true;
+ SkipUntil<UChar, IsASCIISpace>(position, end);
+ if (report_info) {
+ report_info->AddConsoleErrorMessage(
+ "Error parsing 'integrity' attribute ('" + attribute +
+ "'). The digest must be a valid, base64-encoded value.");
+ report_info->AddUseCount(
+ ReportInfo::UseCounterFeature::
+ kSRIElementWithUnparsableIntegrityAttribute);
+ }
+ continue;
+ }
+
+ // The spec defines a space in the syntax for options, separated by a
+ // '?' character followed by unbounded VCHARs, but no actual options
+ // have been defined yet. Thus, for forward compatibility, ignore any
+ // options specified.
+ if (SkipExactly<UChar>(position, end, '?')) {
+ const UChar* begin = position;
+ SkipWhile<UChar, IsValueCharacter>(position, end);
+ if (begin != position && report_info) {
+ report_info->AddConsoleErrorMessage(
+ "Ignoring unrecogized 'integrity' attribute option '" +
+ String(begin, position - begin) + "'.");
+ }
+ }
+
+ IntegrityMetadata integrity_metadata(digest, algorithm);
+ metadata_set.insert(integrity_metadata.ToPair());
+ }
+ if (metadata_set.size() == 0 && error)
+ return kIntegrityParseNoValidResult;
+
+ return kIntegrityParseValidResult;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/subresource_integrity.h b/chromium/third_party/blink/renderer/platform/loader/subresource_integrity.h
new file mode 100644
index 00000000000..a5fb3ca2c87
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/subresource_integrity.h
@@ -0,0 +1,152 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_SUBRESOURCE_INTEGRITY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_SUBRESOURCE_INTEGRITY_H_
+
+#include "base/gtest_prod_util.h"
+#include "third_party/blink/renderer/platform/loader/fetch/integrity_metadata.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class KURL;
+class Resource;
+
+class PLATFORM_EXPORT SubresourceIntegrity final {
+ STATIC_ONLY(SubresourceIntegrity);
+
+ public:
+ class PLATFORM_EXPORT ReportInfo final {
+ public:
+ enum class UseCounterFeature {
+ kSRIElementWithMatchingIntegrityAttribute,
+ kSRIElementWithNonMatchingIntegrityAttribute,
+ kSRIElementIntegrityAttributeButIneligible,
+ kSRIElementWithUnparsableIntegrityAttribute,
+ kSRISignatureCheck,
+ kSRISignatureSuccess,
+ };
+
+ void AddUseCount(UseCounterFeature);
+ void AddConsoleErrorMessage(const String&);
+ void Clear();
+
+ const Vector<UseCounterFeature>& UseCounts() const { return use_counts_; }
+ const Vector<String>& ConsoleErrorMessages() const {
+ return console_error_messages_;
+ }
+
+ private:
+ Vector<UseCounterFeature> use_counts_;
+ Vector<String> console_error_messages_;
+ };
+
+ enum IntegrityParseResult {
+ kIntegrityParseValidResult,
+ kIntegrityParseNoValidResult
+ };
+
+ // Determine which SRI features to support when parsing integrity attributes.
+ enum class IntegrityFeatures {
+ kDefault, // Default: All sha* hash codes.
+ kSignatures // Also support the ed25519 signature scheme.
+ };
+
+ // The version with the IntegrityMetadataSet passed as the first argument
+ // assumes that the integrity attribute has already been parsed, and the
+ // IntegrityMetadataSet represents the result of that parsing.
+ static bool CheckSubresourceIntegrity(const IntegrityMetadataSet&,
+ const char* content,
+ size_t content_size,
+ const KURL& resource_url,
+ const Resource&,
+ ReportInfo&);
+ static bool CheckSubresourceIntegrity(const String&,
+ IntegrityFeatures,
+ const char* content,
+ size_t content_size,
+ const KURL& resource_url,
+ ReportInfo&);
+
+ // The IntegrityMetadataSet arguments are out parameters which contain the
+ // set of all valid, parsed metadata from |attribute|.
+ static IntegrityParseResult ParseIntegrityAttribute(
+ const WTF::String& attribute,
+ IntegrityFeatures,
+ IntegrityMetadataSet&);
+ static IntegrityParseResult ParseIntegrityAttribute(
+ const WTF::String& attribute,
+ IntegrityFeatures,
+ IntegrityMetadataSet&,
+ ReportInfo*);
+
+ private:
+ friend class SubresourceIntegrityTest;
+ FRIEND_TEST_ALL_PREFIXES(SubresourceIntegrityTest, Parsing);
+ FRIEND_TEST_ALL_PREFIXES(SubresourceIntegrityTest, ParseAlgorithm);
+ FRIEND_TEST_ALL_PREFIXES(SubresourceIntegrityTest, ParseHeader);
+ FRIEND_TEST_ALL_PREFIXES(SubresourceIntegrityTest, Prioritization);
+ FRIEND_TEST_ALL_PREFIXES(SubresourceIntegrityTest, FindBestAlgorithm);
+ FRIEND_TEST_ALL_PREFIXES(SubresourceIntegrityTest,
+ GetCheckFunctionForAlgorithm);
+
+ // The core implementation for all CheckSubresoureIntegrity functions.
+ static bool CheckSubresourceIntegrityImpl(const IntegrityMetadataSet&,
+ const char*,
+ size_t,
+ const KURL& resource_url,
+ const String integrity_header,
+ ReportInfo&);
+
+ enum AlgorithmParseResult {
+ kAlgorithmValid,
+ kAlgorithmUnparsable,
+ kAlgorithmUnknown
+ };
+
+ static IntegrityAlgorithm FindBestAlgorithm(const IntegrityMetadataSet&);
+
+ typedef bool (*CheckFunction)(const IntegrityMetadata&,
+ const char*,
+ size_t,
+ const String&);
+ static CheckFunction GetCheckFunctionForAlgorithm(IntegrityAlgorithm);
+
+ static bool CheckSubresourceIntegrityDigest(const IntegrityMetadata&,
+ const char*,
+ size_t,
+ const String& integrity_header);
+ static bool CheckSubresourceIntegritySignature(
+ const IntegrityMetadata&,
+ const char*,
+ size_t,
+ const String& integrity_header);
+
+ static AlgorithmParseResult ParseAttributeAlgorithm(const UChar*& begin,
+ const UChar* end,
+ IntegrityFeatures,
+ IntegrityAlgorithm&);
+ static AlgorithmParseResult ParseIntegrityHeaderAlgorithm(
+ const UChar*& begin,
+ const UChar* end,
+ IntegrityAlgorithm&);
+ typedef std::pair<const char*, IntegrityAlgorithm> AlgorithmPrefixPair;
+ static AlgorithmParseResult ParseAlgorithmPrefix(
+ const UChar*& string_position,
+ const UChar* string_end,
+ const AlgorithmPrefixPair* prefix_table,
+ size_t prefix_table_size,
+ IntegrityAlgorithm&);
+ static bool ParseDigest(const UChar*& begin,
+ const UChar* end,
+ String& digest);
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/subresource_integrity_test.cc b/chromium/third_party/blink/renderer/platform/loader/subresource_integrity_test.cc
new file mode 100644
index 00000000000..e0c92673a0a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/subresource_integrity_test.cc
@@ -0,0 +1,713 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/loader/subresource_integrity.h"
+
+#include "base/memory/scoped_refptr.h"
+#include "services/network/public/mojom/fetch_api.mojom-blink.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/crypto.h"
+#include "third_party/blink/renderer/platform/loader/fetch/integrity_metadata.h"
+#include "third_party/blink/renderer/platform/loader/fetch/raw_resource.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loader.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
+#include "third_party/blink/renderer/platform/loader/testing/crypto_testing_platform_support.h"
+#include "third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+#include "third_party/blink/renderer/platform/wtf/dtoa/utils.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+#include <algorithm>
+
+namespace blink {
+
+static const char kBasicScript[] = "alert('test');";
+static unsigned char kSha256Hash[] = {
+ 0x18, 0x01, 0x78, 0xf1, 0x03, 0xa8, 0xc5, 0x1b, 0xee, 0xd2, 0x06,
+ 0x40, 0x99, 0x08, 0xaf, 0x51, 0xd2, 0x4f, 0xc8, 0x16, 0x9c, 0xab,
+ 0x39, 0xc1, 0x01, 0x7c, 0x27, 0x91, 0xfa, 0x66, 0x41, 0x7e};
+static unsigned char kSha384Hash[] = {
+ 0x9d, 0xea, 0x77, 0x5e, 0x9b, 0xe1, 0x53, 0x1a, 0x42, 0x30, 0xe5, 0x57,
+ 0x20, 0x53, 0xde, 0x71, 0x38, 0x40, 0xa9, 0xd6, 0x3f, 0xb9, 0x57, 0xa2,
+ 0x0f, 0x89, 0x17, 0x4a, 0xa5, 0xe9, 0xc7, 0x46, 0x09, 0x51, 0x65, 0x38,
+ 0x7d, 0x34, 0xda, 0x16, 0x07, 0x22, 0x4e, 0xe6, 0x64, 0xed, 0xf9, 0x84};
+static unsigned char kSha512Hash[] = {
+ 0x4d, 0x79, 0x09, 0xc3, 0x5f, 0x0f, 0xaa, 0x55, 0x65, 0x11, 0x45,
+ 0xd7, 0x8d, 0xe5, 0xdb, 0x19, 0xeb, 0x68, 0xa7, 0x54, 0xca, 0x07,
+ 0x7c, 0x18, 0x40, 0x8a, 0x75, 0xfe, 0x28, 0x71, 0x08, 0xe1, 0x46,
+ 0x51, 0xf1, 0xbd, 0x4d, 0x83, 0x9a, 0x03, 0x53, 0x25, 0x92, 0x94,
+ 0xc0, 0xa9, 0x25, 0x7a, 0xc9, 0xa7, 0xaf, 0x2c, 0xef, 0x13, 0x8f,
+ 0x9a, 0x60, 0x1f, 0x52, 0x66, 0x67, 0xef, 0x88, 0xb4};
+static const char kSha256Integrity[] =
+ "sha256-GAF48QOoxRvu0gZAmQivUdJPyBacqznBAXwnkfpmQX4=";
+static const char kSha256IntegrityLenientSyntax[] =
+ "sha256-GAF48QOoxRvu0gZAmQivUdJPyBacqznBAXwnkfpmQX4=";
+static const char kSha256IntegrityWithEmptyOption[] =
+ "sha256-GAF48QOoxRvu0gZAmQivUdJPyBacqznBAXwnkfpmQX4=?";
+static const char kSha256IntegrityWithOption[] =
+ "sha256-GAF48QOoxRvu0gZAmQivUdJPyBacqznBAXwnkfpmQX4=?foo=bar";
+static const char kSha256IntegrityWithOptions[] =
+ "sha256-GAF48QOoxRvu0gZAmQivUdJPyBacqznBAXwnkfpmQX4=?foo=bar?baz=foz";
+static const char kSha256IntegrityWithMimeOption[] =
+ "sha256-GAF48QOoxRvu0gZAmQivUdJPyBacqznBAXwnkfpmQX4=?ct=application/"
+ "javascript";
+static const char kSha384Integrity[] =
+ "sha384-nep3XpvhUxpCMOVXIFPecThAqdY_uVeiD4kXSqXpx0YJUWU4fTTaFgciTuZk7fmE";
+static const char kSha512Integrity[] =
+ "sha512-TXkJw18PqlVlEUXXjeXbGetop1TKB3wYQIp1_"
+ "ihxCOFGUfG9TYOaA1MlkpTAqSV6yaevLO8Tj5pgH1JmZ--ItA==";
+static const char kSha384IntegrityLabeledAs256[] =
+ "sha256-nep3XpvhUxpCMOVXIFPecThAqdY_uVeiD4kXSqXpx0YJUWU4fTTaFgciTuZk7fmE";
+static const char kSha256AndSha384Integrities[] =
+ "sha256-GAF48QOoxRvu0gZAmQivUdJPyBacqznBAXwnkfpmQX4= "
+ "sha384-nep3XpvhUxpCMOVXIFPecThAqdY_uVeiD4kXSqXpx0YJUWU4fTTaFgciTuZk7fmE";
+static const char kBadSha256AndGoodSha384Integrities[] =
+ "sha256-deadbeef "
+ "sha384-nep3XpvhUxpCMOVXIFPecThAqdY_uVeiD4kXSqXpx0YJUWU4fTTaFgciTuZk7fmE";
+static const char kGoodSha256AndBadSha384Integrities[] =
+ "sha256-GAF48QOoxRvu0gZAmQivUdJPyBacqznBAXwnkfpmQX4= sha384-deadbeef";
+static const char kBadSha256AndBadSha384Integrities[] =
+ "sha256-deadbeef sha384-deadbeef";
+static const char kUnsupportedHashFunctionIntegrity[] =
+ "sha1-JfLW308qMPKfb4DaHpUBEESwuPc=";
+
+class SubresourceIntegrityTest : public testing::Test {
+ public:
+ SubresourceIntegrityTest()
+ : sec_url("https://example.test:443"),
+ insec_url("http://example.test:80") {}
+
+ protected:
+ void SetUp() override {
+ context =
+ MockFetchContext::Create(MockFetchContext::kShouldLoadNewResource);
+ }
+
+ SubresourceIntegrity::IntegrityFeatures Features() const {
+ return RuntimeEnabledFeatures::SignatureBasedIntegrityEnabledByRuntimeFlag()
+ ? SubresourceIntegrity::IntegrityFeatures::kSignatures
+ : SubresourceIntegrity::IntegrityFeatures::kDefault;
+ }
+
+ void ExpectAlgorithm(const String& text,
+ IntegrityAlgorithm expected_algorithm) {
+ Vector<UChar> characters;
+ text.AppendTo(characters);
+ const UChar* position = characters.data();
+ const UChar* end = characters.end();
+ IntegrityAlgorithm algorithm;
+
+ EXPECT_EQ(SubresourceIntegrity::kAlgorithmValid,
+ SubresourceIntegrity::ParseAttributeAlgorithm(
+ position, end, Features(), algorithm));
+ EXPECT_EQ(expected_algorithm, algorithm);
+ EXPECT_EQ(end, position);
+ }
+
+ void ExpectAlgorithmFailure(
+ const String& text,
+ SubresourceIntegrity::AlgorithmParseResult expected_result) {
+ Vector<UChar> characters;
+ text.AppendTo(characters);
+ const UChar* position = characters.data();
+ const UChar* begin = characters.data();
+ const UChar* end = characters.end();
+ IntegrityAlgorithm algorithm;
+
+ EXPECT_EQ(expected_result, SubresourceIntegrity::ParseAttributeAlgorithm(
+ position, end, Features(), algorithm));
+ EXPECT_EQ(begin, position);
+ }
+
+ void ExpectDigest(const String& text, const char* expected_digest) {
+ Vector<UChar> characters;
+ text.AppendTo(characters);
+ const UChar* position = characters.data();
+ const UChar* end = characters.end();
+ String digest;
+
+ EXPECT_TRUE(SubresourceIntegrity::ParseDigest(position, end, digest));
+ EXPECT_EQ(expected_digest, digest);
+ }
+
+ void ExpectDigestFailure(const String& text) {
+ Vector<UChar> characters;
+ text.AppendTo(characters);
+ const UChar* position = characters.data();
+ const UChar* end = characters.end();
+ String digest;
+
+ EXPECT_FALSE(SubresourceIntegrity::ParseDigest(position, end, digest));
+ EXPECT_TRUE(digest.IsEmpty());
+ }
+
+ void ExpectParse(const char* integrity_attribute,
+ const char* expected_digest,
+ IntegrityAlgorithm expected_algorithm) {
+ IntegrityMetadataSet metadata_set;
+
+ EXPECT_EQ(SubresourceIntegrity::kIntegrityParseValidResult,
+ SubresourceIntegrity::ParseIntegrityAttribute(
+ integrity_attribute, Features(), metadata_set));
+ EXPECT_EQ(1u, metadata_set.size());
+ if (metadata_set.size() > 0) {
+ IntegrityMetadata metadata = *metadata_set.begin();
+ EXPECT_EQ(expected_digest, metadata.Digest());
+ EXPECT_EQ(expected_algorithm, metadata.Algorithm());
+ }
+ }
+
+ void ExpectParseMultipleHashes(
+ const char* integrity_attribute,
+ const IntegrityMetadata expected_metadata_array[],
+ size_t expected_metadata_array_size) {
+ IntegrityMetadataSet expected_metadata_set;
+ for (size_t i = 0; i < expected_metadata_array_size; i++) {
+ expected_metadata_set.insert(expected_metadata_array[i].ToPair());
+ }
+ IntegrityMetadataSet metadata_set;
+ EXPECT_EQ(SubresourceIntegrity::kIntegrityParseValidResult,
+ SubresourceIntegrity::ParseIntegrityAttribute(
+ integrity_attribute, Features(), metadata_set));
+ EXPECT_TRUE(
+ IntegrityMetadata::SetsEqual(expected_metadata_set, metadata_set));
+ }
+
+ void ExpectParseFailure(const char* integrity_attribute) {
+ IntegrityMetadataSet metadata_set;
+
+ EXPECT_EQ(SubresourceIntegrity::kIntegrityParseNoValidResult,
+ SubresourceIntegrity::ParseIntegrityAttribute(
+ integrity_attribute, Features(), metadata_set));
+ }
+
+ void ExpectEmptyParseResult(const char* integrity_attribute) {
+ IntegrityMetadataSet metadata_set;
+
+ EXPECT_EQ(SubresourceIntegrity::kIntegrityParseValidResult,
+ SubresourceIntegrity::ParseIntegrityAttribute(
+ integrity_attribute, Features(), metadata_set));
+ EXPECT_EQ(0u, metadata_set.size());
+ }
+
+ enum ServiceWorkerMode {
+ kNoServiceWorker,
+ kSWOpaqueResponse,
+ kSWClearResponse
+ };
+
+ enum Expectation { kIntegritySuccess, kIntegrityFailure };
+
+ struct TestCase {
+ const KURL& origin;
+ const KURL& target;
+ const KURL* allow_origin_url;
+ const ServiceWorkerMode service_worker;
+ const Expectation expectation;
+ };
+
+ void CheckExpectedIntegrity(const char* integrity, const TestCase test) {
+ CheckExpectedIntegrity(integrity, test, test.expectation);
+ }
+
+ // Allows to overwrite the test expectation for cases that are always expected
+ // to fail:
+ void CheckExpectedIntegrity(const char* integrity,
+ const TestCase test,
+ Expectation expectation) {
+ context->SetSecurityOrigin(SecurityOrigin::Create(test.origin));
+
+ IntegrityMetadataSet metadata_set;
+ EXPECT_EQ(SubresourceIntegrity::kIntegrityParseValidResult,
+ SubresourceIntegrity::ParseIntegrityAttribute(
+ String(integrity), Features(), metadata_set));
+
+ SubresourceIntegrity::ReportInfo report_info;
+ EXPECT_EQ(expectation == kIntegritySuccess,
+ SubresourceIntegrity::CheckSubresourceIntegrity(
+ metadata_set, kBasicScript, strlen(kBasicScript), test.target,
+ *CreateTestResource(test.target, test.allow_origin_url,
+ test.service_worker),
+ report_info));
+ }
+
+ Resource* CreateTestResource(const KURL& url,
+ const KURL* allow_origin_url,
+ ServiceWorkerMode service_worker_mode) {
+ ResourceFetcher* fetcher = ResourceFetcher::Create(context);
+ ResourceLoadScheduler* scheduler = ResourceLoadScheduler::Create();
+ Resource* resource = RawResource::CreateForTest(url, Resource::kRaw);
+ ResourceLoader* loader =
+ ResourceLoader::Create(fetcher, scheduler, resource);
+
+ ResourceRequest request;
+ request.SetURL(url);
+
+ ResourceResponse response(url);
+ response.SetHTTPStatusCode(200);
+
+ if (allow_origin_url) {
+ request.SetFetchRequestMode(network::mojom::FetchRequestMode::kCORS);
+ resource->MutableOptions().cors_handling_by_resource_fetcher =
+ kEnableCORSHandlingByResourceFetcher;
+ response.SetHTTPHeaderField(
+ "access-control-allow-origin",
+ SecurityOrigin::Create(*allow_origin_url)->ToAtomicString());
+ response.SetHTTPHeaderField("access-control-allow-credentials", "true");
+ }
+
+ resource->SetResourceRequest(request);
+
+ if (service_worker_mode != kNoServiceWorker) {
+ response.SetWasFetchedViaServiceWorker(true);
+
+ if (service_worker_mode == kSWOpaqueResponse) {
+ response.SetResponseTypeViaServiceWorker(
+ network::mojom::FetchResponseType::kOpaque);
+ } else {
+ response.SetResponseTypeViaServiceWorker(
+ network::mojom::FetchResponseType::kDefault);
+ }
+ }
+
+ StringBuilder cors_error_msg;
+ CORSStatus cors_status =
+ loader->DetermineCORSStatus(response, cors_error_msg);
+ resource->SetCORSStatus(cors_status);
+
+ return resource;
+ }
+
+ KURL sec_url;
+ KURL insec_url;
+
+ ScopedTestingPlatformSupport<CryptoTestingPlatformSupport> platform_;
+ Persistent<MockFetchContext> context;
+};
+
+// Test the prioritization (i.e. selecting the "strongest" algorithm.
+// This effectively tests the definition of IntegrityAlgorithm in
+// IntegrityMetadata. The test is here, because SubresourceIntegrity is the
+// class that relies on this working as expected.)
+TEST_F(SubresourceIntegrityTest, Prioritization) {
+ // Check that each algorithm is it's own "strongest".
+ EXPECT_EQ(
+ IntegrityAlgorithm::kSha256,
+ std::max({IntegrityAlgorithm::kSha256, IntegrityAlgorithm::kSha256}));
+ EXPECT_EQ(
+ IntegrityAlgorithm::kSha384,
+ std::max({IntegrityAlgorithm::kSha384, IntegrityAlgorithm::kSha384}));
+
+ EXPECT_EQ(
+ IntegrityAlgorithm::kSha512,
+ std::max({IntegrityAlgorithm::kSha512, IntegrityAlgorithm::kSha512}));
+ EXPECT_EQ(
+ IntegrityAlgorithm::kEd25519,
+ std::max({IntegrityAlgorithm::kEd25519, IntegrityAlgorithm::kEd25519}));
+
+ // Check a mix of algorithms.
+ EXPECT_EQ(IntegrityAlgorithm::kSha384,
+ std::max({IntegrityAlgorithm::kSha256, IntegrityAlgorithm::kSha384,
+ IntegrityAlgorithm::kSha256}));
+ EXPECT_EQ(IntegrityAlgorithm::kSha512,
+ std::max({IntegrityAlgorithm::kSha384, IntegrityAlgorithm::kSha512,
+ IntegrityAlgorithm::kSha256}));
+ EXPECT_EQ(
+ IntegrityAlgorithm::kEd25519,
+ std::max({IntegrityAlgorithm::kSha384, IntegrityAlgorithm::kSha512,
+ IntegrityAlgorithm::kEd25519, IntegrityAlgorithm::kSha512,
+ IntegrityAlgorithm::kSha256, IntegrityAlgorithm::kSha512}));
+}
+
+TEST_F(SubresourceIntegrityTest, ParseAlgorithm) {
+ ExpectAlgorithm("sha256-", IntegrityAlgorithm::kSha256);
+ ExpectAlgorithm("sha384-", IntegrityAlgorithm::kSha384);
+ ExpectAlgorithm("sha512-", IntegrityAlgorithm::kSha512);
+ ExpectAlgorithm("sha-256-", IntegrityAlgorithm::kSha256);
+ ExpectAlgorithm("sha-384-", IntegrityAlgorithm::kSha384);
+ ExpectAlgorithm("sha-512-", IntegrityAlgorithm::kSha512);
+
+ {
+ ScopedSignatureBasedIntegrityForTest signature_based_integrity(true);
+ ExpectAlgorithm("ed25519-", IntegrityAlgorithm::kEd25519);
+ }
+ ScopedSignatureBasedIntegrityForTest signature_based_integrity(false);
+ ExpectAlgorithmFailure("ed25519-", SubresourceIntegrity::kAlgorithmUnknown);
+
+ ExpectAlgorithmFailure("sha1-", SubresourceIntegrity::kAlgorithmUnknown);
+ ExpectAlgorithmFailure("sha-1-", SubresourceIntegrity::kAlgorithmUnknown);
+ ExpectAlgorithmFailure("foobarsha256-",
+ SubresourceIntegrity::kAlgorithmUnknown);
+ ExpectAlgorithmFailure("foobar-", SubresourceIntegrity::kAlgorithmUnknown);
+ ExpectAlgorithmFailure("-", SubresourceIntegrity::kAlgorithmUnknown);
+ ExpectAlgorithmFailure("ed-25519-", SubresourceIntegrity::kAlgorithmUnknown);
+ ExpectAlgorithmFailure("ed25518-", SubresourceIntegrity::kAlgorithmUnknown);
+
+ ExpectAlgorithmFailure("sha256", SubresourceIntegrity::kAlgorithmUnparsable);
+ ExpectAlgorithmFailure("", SubresourceIntegrity::kAlgorithmUnparsable);
+}
+
+TEST_F(SubresourceIntegrityTest, ParseDigest) {
+ ExpectDigest("abcdefg", "abcdefg");
+ ExpectDigest("abcdefg?", "abcdefg");
+ ExpectDigest("ab+de/g", "ab+de/g");
+ ExpectDigest("ab-de_g", "ab+de/g");
+
+ ExpectDigestFailure("?");
+ ExpectDigestFailure("&&&foobar&&&");
+ ExpectDigestFailure("\x01\x02\x03\x04");
+}
+
+//
+// End-to-end parsing tests.
+//
+
+TEST_F(SubresourceIntegrityTest, Parsing) {
+ ExpectParseFailure("not_really_a_valid_anything");
+ ExpectParseFailure("sha256-&&&foobar&&&");
+ ExpectParseFailure("sha256-\x01\x02\x03\x04");
+ ExpectParseFailure("sha256-!!! sha256-!!!");
+
+ ExpectEmptyParseResult("foobar:///sha256-abcdefg");
+ ExpectEmptyParseResult("ni://sha256-abcdefg");
+ ExpectEmptyParseResult("ni:///sha256-abcdefg");
+ ExpectEmptyParseResult("notsha256atall-abcdefg");
+
+ ExpectParse("sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
+ "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
+ IntegrityAlgorithm::kSha256);
+
+ ExpectParse("sha-256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
+ "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
+ IntegrityAlgorithm::kSha256);
+
+ ExpectParse(" sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE= ",
+ "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
+ IntegrityAlgorithm::kSha256);
+
+ ExpectParse(
+ "sha384-XVVXBGoYw6AJOh9J-Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup_tA1v5GPr",
+ "XVVXBGoYw6AJOh9J+Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup/tA1v5GPr",
+ IntegrityAlgorithm::kSha384);
+
+ ExpectParse(
+ "sha-384-XVVXBGoYw6AJOh9J_Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup_"
+ "tA1v5GPr",
+ "XVVXBGoYw6AJOh9J/Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup/tA1v5GPr",
+ IntegrityAlgorithm::kSha384);
+
+ ExpectParse(
+ "sha512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ-"
+ "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
+ "tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
+ "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
+ IntegrityAlgorithm::kSha512);
+
+ ExpectParse(
+ "sha-512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ-"
+ "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
+ "tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
+ "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
+ IntegrityAlgorithm::kSha512);
+
+ ExpectParse(
+ "sha-512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ-"
+ "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==?ct=application/javascript",
+ "tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
+ "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
+ IntegrityAlgorithm::kSha512);
+
+ ExpectParse(
+ "sha-512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ-"
+ "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==?ct=application/xhtml+xml",
+ "tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
+ "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
+ IntegrityAlgorithm::kSha512);
+
+ ExpectParse(
+ "sha-512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ-"
+ "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==?foo=bar?ct=application/xhtml+xml",
+ "tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
+ "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
+ IntegrityAlgorithm::kSha512);
+
+ ExpectParse(
+ "sha-512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ-"
+ "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==?ct=application/xhtml+xml?foo=bar",
+ "tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
+ "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
+ IntegrityAlgorithm::kSha512);
+
+ ExpectParse(
+ "sha-512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ-"
+ "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==?baz=foz?ct=application/"
+ "xhtml+xml?foo=bar",
+ "tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
+ "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
+ IntegrityAlgorithm::kSha512);
+
+ ExpectParseMultipleHashes("", nullptr, 0);
+ ExpectParseMultipleHashes(" ", nullptr, 0);
+
+ const IntegrityMetadata valid_sha384_and_sha512[] = {
+ IntegrityMetadata(
+ "XVVXBGoYw6AJOh9J+Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup/tA1v5GPr",
+ IntegrityAlgorithm::kSha384),
+ IntegrityMetadata("tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
+ "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
+ IntegrityAlgorithm::kSha512),
+ };
+ ExpectParseMultipleHashes(
+ "sha384-XVVXBGoYw6AJOh9J+Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup/tA1v5GPr "
+ "sha512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
+ "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
+ valid_sha384_and_sha512, WTF_ARRAY_LENGTH(valid_sha384_and_sha512));
+
+ const IntegrityMetadata valid_sha256_and_sha256[] = {
+ IntegrityMetadata("BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
+ IntegrityAlgorithm::kSha256),
+ IntegrityMetadata("deadbeef", IntegrityAlgorithm::kSha256),
+ };
+ ExpectParseMultipleHashes(
+ "sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE= sha256-deadbeef",
+ valid_sha256_and_sha256, WTF_ARRAY_LENGTH(valid_sha256_and_sha256));
+
+ const IntegrityMetadata valid_sha256_and_invalid_sha256[] = {
+ IntegrityMetadata("BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
+ IntegrityAlgorithm::kSha256),
+ };
+ ExpectParseMultipleHashes(
+ "sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE= sha256-!!!!",
+ valid_sha256_and_invalid_sha256,
+ WTF_ARRAY_LENGTH(valid_sha256_and_invalid_sha256));
+
+ const IntegrityMetadata invalid_sha256_and_valid_sha256[] = {
+ IntegrityMetadata("BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
+ IntegrityAlgorithm::kSha256),
+ };
+ ExpectParseMultipleHashes(
+ "sha256-!!! sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
+ invalid_sha256_and_valid_sha256,
+ WTF_ARRAY_LENGTH(invalid_sha256_and_valid_sha256));
+
+ ExpectParse("sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?foo=bar",
+ "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
+ IntegrityAlgorithm::kSha256);
+
+ ExpectParse(
+ "sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?foo=bar?baz=foz",
+ "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
+ IntegrityAlgorithm::kSha256);
+
+ ExpectParse("sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?",
+ "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
+ IntegrityAlgorithm::kSha256);
+ ExpectParse("sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?foo=bar",
+ "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
+ IntegrityAlgorithm::kSha256);
+ ExpectParse(
+ "sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?foo=bar?baz=foz",
+ "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
+ IntegrityAlgorithm::kSha256);
+ ExpectParse("sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?foo",
+ "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
+ IntegrityAlgorithm::kSha256);
+ ExpectParse("sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?foo=bar?",
+ "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
+ IntegrityAlgorithm::kSha256);
+ ExpectParse("sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?foo:bar",
+ "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
+ IntegrityAlgorithm::kSha256);
+
+ {
+ ScopedSignatureBasedIntegrityForTest signature_based_integrity(false);
+ ExpectEmptyParseResult("ed25519-xxxx");
+ ExpectEmptyParseResult(
+ "ed25519-qGFmwTxlocg707D1cX4w60iTwtfwbMLf8ITDyfko7s0=");
+ }
+
+ ScopedSignatureBasedIntegrityForTest signature_based_integrity(true);
+ ExpectParse("ed25519-xxxx", "xxxx", IntegrityAlgorithm::kEd25519);
+ ExpectParse("ed25519-qGFmwTxlocg707D1cX4w60iTwtfwbMLf8ITDyfko7s0=",
+ "qGFmwTxlocg707D1cX4w60iTwtfwbMLf8ITDyfko7s0=",
+ IntegrityAlgorithm::kEd25519);
+ ExpectParse("ed25519-qGFmwTxlocg707D1cX4w60iTwtfwbMLf8ITDyfko7s0=?foo=bar",
+ "qGFmwTxlocg707D1cX4w60iTwtfwbMLf8ITDyfko7s0=",
+ IntegrityAlgorithm::kEd25519);
+ ExpectEmptyParseResult("ed-25519-xxx");
+ ExpectEmptyParseResult(
+ "ed-25519-qGFmwTxlocg707D1cX4w60iTwtfwbMLf8ITDyfko7s0=");
+}
+
+TEST_F(SubresourceIntegrityTest, ParsingBase64) {
+ ExpectParse(
+ "sha384-XVVXBGoYw6AJOh9J+Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup/tA1v5GPr",
+ "XVVXBGoYw6AJOh9J+Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup/tA1v5GPr",
+ IntegrityAlgorithm::kSha384);
+}
+
+// Tests that SubresourceIntegrity::CheckSubresourceIntegrity behaves correctly
+// when faced with secure or insecure origins, same origin and cross origin
+// requests, successful and failing CORS checks as well as when the response was
+// handled by a service worker.
+TEST_F(SubresourceIntegrityTest, OriginIntegrity) {
+ TestCase cases[] = {
+ // Secure origin, same origin -> integrity expected:
+ {sec_url, sec_url, nullptr, kNoServiceWorker, kIntegritySuccess},
+ {sec_url, sec_url, nullptr, kSWClearResponse, kIntegritySuccess},
+
+ // Insecure origin, secure target, CORS ok -> integrity expected:
+ {insec_url, sec_url, &insec_url, kNoServiceWorker, kIntegritySuccess},
+ {insec_url, sec_url, &insec_url, kSWClearResponse, kIntegritySuccess},
+ {insec_url, sec_url, nullptr, kSWClearResponse, kIntegritySuccess},
+
+ // Secure origin, insecure target, CORS ok -> no failure expected:
+ {sec_url, insec_url, &sec_url, kNoServiceWorker, kIntegritySuccess},
+ {sec_url, insec_url, &sec_url, kSWClearResponse, kIntegritySuccess},
+ {sec_url, insec_url, nullptr, kSWClearResponse, kIntegritySuccess},
+
+ // Insecure origin, secure target, no CORS headers -> failure expected:
+ {insec_url, sec_url, nullptr, kNoServiceWorker, kIntegrityFailure},
+
+ // Insecure origin, secure target, CORS failure -> failure expected:
+ {insec_url, sec_url, &sec_url, kNoServiceWorker, kIntegrityFailure},
+ {insec_url, sec_url, &sec_url, kSWOpaqueResponse, kIntegrityFailure},
+ {insec_url, sec_url, nullptr, kSWOpaqueResponse, kIntegrityFailure},
+
+ // Secure origin, same origin, opaque response from service worker ->
+ // failure expected:
+ {sec_url, sec_url, &sec_url, kSWOpaqueResponse, kIntegrityFailure},
+
+ // Insecure origin, insecure target, same origin-> failure expected:
+ {sec_url, insec_url, nullptr, kNoServiceWorker, kIntegrityFailure},
+ };
+
+ MockWebCryptoDigestorFactory factory_sha256(
+ kBasicScript, strlen(kBasicScript), kSha256Hash, sizeof(kSha256Hash));
+ MockWebCryptoDigestorFactory factory_sha384(
+ kBasicScript, strlen(kBasicScript), kSha384Hash, sizeof(kSha384Hash));
+ MockWebCryptoDigestorFactory factory_sha512(
+ kBasicScript, strlen(kBasicScript), kSha512Hash, sizeof(kSha512Hash));
+
+ CryptoTestingPlatformSupport::SetMockCryptoScope mock_crypto_scope(
+ *platform_.GetTestingPlatformSupport());
+
+ EXPECT_CALL(mock_crypto_scope.MockCrypto(),
+ CreateDigestorProxy(kWebCryptoAlgorithmIdSha256))
+ .WillRepeatedly(testing::InvokeWithoutArgs(
+ &factory_sha256, &MockWebCryptoDigestorFactory::Create));
+ EXPECT_CALL(mock_crypto_scope.MockCrypto(),
+ CreateDigestorProxy(kWebCryptoAlgorithmIdSha384))
+ .WillRepeatedly(testing::InvokeWithoutArgs(
+ &factory_sha384, &MockWebCryptoDigestorFactory::Create));
+ EXPECT_CALL(mock_crypto_scope.MockCrypto(),
+ CreateDigestorProxy(kWebCryptoAlgorithmIdSha512))
+ .WillRepeatedly(testing::InvokeWithoutArgs(
+ &factory_sha512, &MockWebCryptoDigestorFactory::Create));
+
+ for (const auto& test : cases) {
+ SCOPED_TRACE(
+ testing::Message()
+ << "Origin: " << test.origin.BaseAsString()
+ << ", target: " << test.target.BaseAsString()
+ << ", CORS access-control-allow-origin header: "
+ << (test.allow_origin_url ? test.allow_origin_url->BaseAsString() : "-")
+ << ", service worker: "
+ << (test.service_worker == kNoServiceWorker
+ ? "no"
+ : (test.service_worker == kSWClearResponse ? "clear response"
+ : "opaque response"))
+ << ", expected result: "
+ << (test.expectation == kIntegritySuccess ? "integrity" : "failure"));
+
+ // Verify basic sha256, sha384, and sha512 integrity checks.
+ CheckExpectedIntegrity(kSha256Integrity, test);
+ CheckExpectedIntegrity(kSha256IntegrityLenientSyntax, test);
+ CheckExpectedIntegrity(kSha384Integrity, test);
+ CheckExpectedIntegrity(kSha512Integrity, test);
+
+ // Verify multiple hashes in an attribute.
+ CheckExpectedIntegrity(kSha256AndSha384Integrities, test);
+ CheckExpectedIntegrity(kBadSha256AndGoodSha384Integrities, test);
+
+ // Unsupported hash functions should succeed.
+ CheckExpectedIntegrity(kUnsupportedHashFunctionIntegrity, test);
+
+ // Options should be ignored
+ CheckExpectedIntegrity(kSha256IntegrityWithEmptyOption, test);
+ CheckExpectedIntegrity(kSha256IntegrityWithOption, test);
+ CheckExpectedIntegrity(kSha256IntegrityWithOptions, test);
+ CheckExpectedIntegrity(kSha256IntegrityWithMimeOption, test);
+
+ // The following tests are expected to fail in every scenario:
+
+ // The hash label must match the hash value.
+ CheckExpectedIntegrity(kSha384IntegrityLabeledAs256, test,
+ Expectation::kIntegrityFailure);
+
+ // With multiple values, at least one must match, and it must be the
+ // strongest hash algorithm.
+ CheckExpectedIntegrity(kGoodSha256AndBadSha384Integrities, test,
+ Expectation::kIntegrityFailure);
+ CheckExpectedIntegrity(kBadSha256AndBadSha384Integrities, test,
+ Expectation::kIntegrityFailure);
+ }
+}
+
+TEST_F(SubresourceIntegrityTest, FindBestAlgorithm) {
+ // Each algorithm is its own best.
+ EXPECT_EQ(IntegrityAlgorithm::kSha256,
+ SubresourceIntegrity::FindBestAlgorithm(
+ IntegrityMetadataSet({{"", IntegrityAlgorithm::kSha256}})));
+ EXPECT_EQ(IntegrityAlgorithm::kSha384,
+ SubresourceIntegrity::FindBestAlgorithm(
+ IntegrityMetadataSet({{"", IntegrityAlgorithm::kSha384}})));
+ EXPECT_EQ(IntegrityAlgorithm::kSha512,
+ SubresourceIntegrity::FindBestAlgorithm(
+ IntegrityMetadataSet({{"", IntegrityAlgorithm::kSha512}})));
+ EXPECT_EQ(IntegrityAlgorithm::kEd25519,
+ SubresourceIntegrity::FindBestAlgorithm(
+ IntegrityMetadataSet({{"", IntegrityAlgorithm::kEd25519}})));
+
+ // Test combinations of multiple algorithms.
+ EXPECT_EQ(IntegrityAlgorithm::kSha384,
+ SubresourceIntegrity::FindBestAlgorithm(
+ IntegrityMetadataSet({{"", IntegrityAlgorithm::kSha256},
+ {"", IntegrityAlgorithm::kSha384}})));
+ EXPECT_EQ(IntegrityAlgorithm::kSha512,
+ SubresourceIntegrity::FindBestAlgorithm(
+ IntegrityMetadataSet({{"", IntegrityAlgorithm::kSha256},
+ {"", IntegrityAlgorithm::kSha512},
+ {"", IntegrityAlgorithm::kSha384}})));
+ EXPECT_EQ(IntegrityAlgorithm::kEd25519,
+ SubresourceIntegrity::FindBestAlgorithm(
+ IntegrityMetadataSet({{"", IntegrityAlgorithm::kSha256},
+ {"", IntegrityAlgorithm::kSha512},
+ {"", IntegrityAlgorithm::kEd25519}})));
+}
+
+TEST_F(SubresourceIntegrityTest, GetCheckFunctionForAlgorithm) {
+ EXPECT_TRUE(SubresourceIntegrity::CheckSubresourceIntegrityDigest ==
+ SubresourceIntegrity::GetCheckFunctionForAlgorithm(
+ IntegrityAlgorithm::kSha256));
+ EXPECT_TRUE(SubresourceIntegrity::CheckSubresourceIntegrityDigest ==
+ SubresourceIntegrity::GetCheckFunctionForAlgorithm(
+ IntegrityAlgorithm::kSha384));
+ EXPECT_TRUE(SubresourceIntegrity::CheckSubresourceIntegrityDigest ==
+ SubresourceIntegrity::GetCheckFunctionForAlgorithm(
+ IntegrityAlgorithm::kSha512));
+ EXPECT_TRUE(SubresourceIntegrity::CheckSubresourceIntegritySignature ==
+ SubresourceIntegrity::GetCheckFunctionForAlgorithm(
+ IntegrityAlgorithm::kEd25519));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/testing/crypto_testing_platform_support.h b/chromium/third_party/blink/renderer/platform/loader/testing/crypto_testing_platform_support.h
new file mode 100644
index 00000000000..19ba9b6dfe1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/testing/crypto_testing_platform_support.h
@@ -0,0 +1,51 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_TESTING_CRYPTO_TESTING_PLATFORM_SUPPORT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_TESTING_CRYPTO_TESTING_PLATFORM_SUPPORT_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/loader/testing/fetch_testing_platform_support.h"
+#include "third_party/blink/renderer/platform/testing/mock_web_crypto.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+class CryptoTestingPlatformSupport : public FetchTestingPlatformSupport {
+ public:
+ CryptoTestingPlatformSupport() = default;
+ ~CryptoTestingPlatformSupport() override = default;
+
+ // Platform:
+ WebCrypto* Crypto() override { return mock_web_crypto_.get(); }
+
+ class SetMockCryptoScope final {
+ STACK_ALLOCATED();
+
+ public:
+ explicit SetMockCryptoScope(CryptoTestingPlatformSupport& platform)
+ : platform_(platform) {
+ DCHECK(!platform_.Crypto());
+ platform_.SetMockCrypto(MockWebCrypto::Create());
+ }
+ ~SetMockCryptoScope() { platform_.SetMockCrypto(nullptr); }
+ MockWebCrypto& MockCrypto() { return *platform_.mock_web_crypto_; }
+
+ private:
+ CryptoTestingPlatformSupport& platform_;
+ };
+
+ private:
+ void SetMockCrypto(std::unique_ptr<MockWebCrypto> crypto) {
+ mock_web_crypto_ = std::move(crypto);
+ }
+
+ std::unique_ptr<MockWebCrypto> mock_web_crypto_;
+
+ DISALLOW_COPY_AND_ASSIGN(CryptoTestingPlatformSupport);
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/testing/fetch_testing_platform_support.cc b/chromium/third_party/blink/renderer/platform/loader/testing/fetch_testing_platform_support.cc
new file mode 100644
index 00000000000..ac86a8cca00
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/testing/fetch_testing_platform_support.cc
@@ -0,0 +1,48 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/loader/testing/fetch_testing_platform_support.h"
+
+#include <memory>
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_url.h"
+#include "third_party/blink/public/platform/web_url_loader.h"
+#include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_error.h"
+#include "third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h"
+#include "third_party/blink/renderer/platform/loader/testing/web_url_loader_factory_with_mock.h"
+#include "third_party/blink/renderer/platform/testing/weburl_loader_mock_factory_impl.h"
+
+namespace blink {
+
+FetchTestingPlatformSupport::FetchTestingPlatformSupport()
+ : url_loader_mock_factory_(new WebURLLoaderMockFactoryImpl(this)) {}
+
+FetchTestingPlatformSupport::~FetchTestingPlatformSupport() {
+ // Shutdowns WebURLLoaderMockFactory gracefully, serving all pending requests
+ // first, then flushing all registered URLs.
+ url_loader_mock_factory_->ServeAsynchronousRequests();
+ url_loader_mock_factory_->UnregisterAllURLsAndClearMemoryCache();
+}
+
+MockFetchContext* FetchTestingPlatformSupport::Context() {
+ if (!context_) {
+ context_ =
+ MockFetchContext::Create(MockFetchContext::kShouldLoadNewResource);
+ }
+ return context_;
+}
+
+WebURLLoaderMockFactory*
+FetchTestingPlatformSupport::GetURLLoaderMockFactory() {
+ return url_loader_mock_factory_.get();
+}
+
+std::unique_ptr<WebURLLoaderFactory>
+FetchTestingPlatformSupport::CreateDefaultURLLoaderFactory() {
+ return std::make_unique<WebURLLoaderFactoryWithMock>(
+ url_loader_mock_factory_.get());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/testing/fetch_testing_platform_support.h b/chromium/third_party/blink/renderer/platform/loader/testing/fetch_testing_platform_support.h
new file mode 100644
index 00000000000..10e4c61c532
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/testing/fetch_testing_platform_support.h
@@ -0,0 +1,39 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_TESTING_FETCH_TESTING_PLATFORM_SUPPORT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_TESTING_FETCH_TESTING_PLATFORM_SUPPORT_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/heap/persistent.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
+
+namespace blink {
+
+class MockFetchContext;
+
+class FetchTestingPlatformSupport
+ : public TestingPlatformSupportWithMockScheduler {
+ public:
+ FetchTestingPlatformSupport();
+ ~FetchTestingPlatformSupport() override;
+
+ MockFetchContext* Context();
+
+ // Platform:
+ WebURLLoaderMockFactory* GetURLLoaderMockFactory() override;
+ std::unique_ptr<WebURLLoaderFactory> CreateDefaultURLLoaderFactory() override;
+
+ private:
+ class FetchTestingWebURLLoaderMockFactory;
+
+ Persistent<MockFetchContext> context_;
+ std::unique_ptr<WebURLLoaderMockFactory> url_loader_mock_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(FetchTestingPlatformSupport);
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h b/chromium/third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h
new file mode 100644
index 00000000000..e5f3adb730f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h
@@ -0,0 +1,166 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_TESTING_MOCK_FETCH_CONTEXT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_TESTING_MOCK_FETCH_CONTEXT_H_
+
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_url_loader_factory.h"
+#include "third_party/blink/renderer/platform/exported/wrapped_resource_request.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_context.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h"
+#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
+#include "third_party/blink/renderer/platform/scheduler/test/fake_frame_scheduler.h"
+#include "third_party/blink/renderer/platform/scheduler/test/fake_task_runner.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+
+#include <memory>
+
+namespace blink {
+
+class KURL;
+class ResourceRequest;
+struct ResourceLoaderOptions;
+
+// Mocked FetchContext for testing.
+class MockFetchContext : public FetchContext {
+ public:
+ enum LoadPolicy {
+ kShouldLoadNewResource,
+ kShouldNotLoadNewResource,
+ };
+ static MockFetchContext* Create(LoadPolicy load_policy,
+ scoped_refptr<base::SingleThreadTaskRunner>
+ loading_task_runner = nullptr) {
+ return new MockFetchContext(load_policy, std::move(loading_task_runner));
+ }
+
+ ~MockFetchContext() override = default;
+
+ void SetLoadComplete(bool complete) { complete_ = complete; }
+ long long GetTransferSize() const { return transfer_size_; }
+
+ const SecurityOrigin* GetSecurityOrigin() const override {
+ return security_origin_.get();
+ }
+
+ void SetSecurityOrigin(scoped_refptr<const SecurityOrigin> security_origin) {
+ security_origin_ = security_origin;
+ }
+
+ // The last ResourceRequest passed to DispatchWillSendRequest.
+ WTF::Optional<ResourceRequest> RequestFromWillSendRequest() const {
+ return will_send_request_;
+ }
+
+ // FetchContext:
+ void DispatchWillSendRequest(
+ unsigned long identifier,
+ ResourceRequest& request,
+ const ResourceResponse& redirect_response,
+ Resource::Type,
+ const FetchInitiatorInfo& = FetchInitiatorInfo()) override {
+ will_send_request_ = request;
+ }
+ bool AllowImage(bool images_enabled, const KURL&) const override {
+ return true;
+ }
+ ResourceRequestBlockedReason CanRequest(
+ Resource::Type,
+ const ResourceRequest&,
+ const KURL&,
+ const ResourceLoaderOptions&,
+ SecurityViolationReportingPolicy,
+ FetchParameters::OriginRestriction,
+ ResourceRequest::RedirectStatus redirect_status) const override {
+ return ResourceRequestBlockedReason::kNone;
+ }
+ ResourceRequestBlockedReason CheckCSPForRequest(
+ WebURLRequest::RequestContext,
+ const KURL& url,
+ const ResourceLoaderOptions& options,
+ SecurityViolationReportingPolicy reporting_policy,
+ ResourceRequest::RedirectStatus redirect_status) const override {
+ return ResourceRequestBlockedReason::kNone;
+ }
+ virtual ResourceRequestBlockedReason CheckResponseNosniff(
+ WebURLRequest::RequestContext,
+ const ResourceResponse&) const {
+ return ResourceRequestBlockedReason::kNone;
+ }
+ bool ShouldLoadNewResource(Resource::Type) const override {
+ return load_policy_ == kShouldLoadNewResource;
+ }
+ bool IsLoadComplete() const override { return complete_; }
+ void AddResourceTiming(
+ const ResourceTimingInfo& resource_timing_info) override {
+ transfer_size_ = resource_timing_info.TransferSize();
+ }
+
+ std::unique_ptr<WebURLLoader> CreateURLLoader(
+ const ResourceRequest& request,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+ const ResourceLoaderOptions&) override {
+ if (!url_loader_factory_) {
+ url_loader_factory_ =
+ Platform::Current()->CreateDefaultURLLoaderFactory();
+ }
+ WrappedResourceRequest wrapped(request);
+ return url_loader_factory_->CreateURLLoader(wrapped, task_runner);
+ }
+
+ ResourceLoadScheduler::ThrottlingPolicy InitialLoadThrottlingPolicy()
+ const override {
+ return ResourceLoadScheduler::ThrottlingPolicy::kTight;
+ }
+
+ FrameScheduler* GetFrameScheduler() const override {
+ return frame_scheduler_.get();
+ }
+
+ scoped_refptr<base::SingleThreadTaskRunner> GetLoadingTaskRunner() override {
+ return frame_scheduler_->GetTaskRunner(TaskType::kInternalTest);
+ }
+
+ private:
+ class MockFrameScheduler final : public scheduler::FakeFrameScheduler {
+ public:
+ explicit MockFrameScheduler(
+ scoped_refptr<base::SingleThreadTaskRunner> runner)
+ : runner_(std::move(runner)) {}
+ scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(
+ TaskType) override {
+ return runner_;
+ }
+
+ private:
+ scoped_refptr<base::SingleThreadTaskRunner> runner_;
+ };
+
+ MockFetchContext(
+ LoadPolicy load_policy,
+ scoped_refptr<base::SingleThreadTaskRunner> loading_task_runner)
+ : load_policy_(load_policy),
+ runner_(loading_task_runner
+ ? std::move(loading_task_runner)
+ : base::MakeRefCounted<scheduler::FakeTaskRunner>()),
+ security_origin_(SecurityOrigin::CreateUnique()),
+ frame_scheduler_(new MockFrameScheduler(runner_)),
+ complete_(false),
+ transfer_size_(-1) {}
+
+ enum LoadPolicy load_policy_;
+ scoped_refptr<base::SingleThreadTaskRunner> runner_;
+ scoped_refptr<const SecurityOrigin> security_origin_;
+ std::unique_ptr<FrameScheduler> frame_scheduler_;
+ std::unique_ptr<WebURLLoaderFactory> url_loader_factory_;
+ bool complete_;
+ long long transfer_size_;
+ WTF::Optional<ResourceRequest> will_send_request_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/testing/mock_resource.cc b/chromium/third_party/blink/renderer/platform/loader/testing/mock_resource.cc
new file mode 100644
index 00000000000..367473a7aa0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/testing/mock_resource.cc
@@ -0,0 +1,103 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/loader/testing/mock_resource.h"
+
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
+
+namespace blink {
+
+namespace {
+
+class MockResourceFactory final : public NonTextResourceFactory {
+ public:
+ MockResourceFactory() : NonTextResourceFactory(Resource::kMock) {}
+
+ Resource* Create(const ResourceRequest& request,
+ const ResourceLoaderOptions& options) const override {
+ return new MockResource(request, options);
+ }
+};
+
+} // namespace
+
+// static
+MockResource* MockResource::Fetch(FetchParameters& params,
+ ResourceFetcher* fetcher,
+ ResourceClient* client) {
+ params.SetRequestContext(WebURLRequest::kRequestContextSubresource);
+ return static_cast<MockResource*>(
+ fetcher->RequestResource(params, MockResourceFactory(), client));
+}
+
+// static
+MockResource* MockResource::Create(const ResourceRequest& request) {
+ ResourceLoaderOptions options;
+ return new MockResource(request, options);
+}
+
+MockResource* MockResource::Create(const KURL& url) {
+ ResourceRequest request(url);
+ return Create(request);
+}
+
+MockResource::MockResource(const ResourceRequest& request,
+ const ResourceLoaderOptions& options)
+ : Resource(request, Resource::kMock, options) {}
+
+CachedMetadataHandler* MockResource::CreateCachedMetadataHandler(
+ std::unique_ptr<CachedMetadataSender> send_callback) {
+ return new MockCacheHandler(std::move(send_callback));
+}
+
+void MockResource::SetSerializedCachedMetadata(const char* data, size_t size) {
+ Resource::SetSerializedCachedMetadata(data, size);
+ MockCacheHandler* cache_handler =
+ static_cast<MockCacheHandler*>(Resource::CacheHandler());
+ if (cache_handler) {
+ cache_handler->Set(data, size);
+ }
+}
+
+void MockResource::SendCachedMetadata(const char* data, size_t size) {
+ MockCacheHandler* cache_handler =
+ static_cast<MockCacheHandler*>(Resource::CacheHandler());
+ if (cache_handler) {
+ cache_handler->Set(data, size);
+ cache_handler->Send();
+ }
+}
+
+MockCacheHandler* MockResource::CacheHandler() {
+ return static_cast<MockCacheHandler*>(Resource::CacheHandler());
+}
+
+MockCacheHandler::MockCacheHandler(
+ std::unique_ptr<CachedMetadataSender> send_callback)
+ : send_callback_(std::move(send_callback)) {}
+
+void MockCacheHandler::Set(const char* data, size_t size) {
+ data_.emplace();
+ data_->Append(data, size);
+}
+
+void MockCacheHandler::ClearCachedMetadata(
+ CachedMetadataHandler::CacheType cache_type) {
+ if (cache_type == CachedMetadataHandler::kSendToPlatform) {
+ Send();
+ }
+ data_.reset();
+}
+
+void MockCacheHandler::Send() {
+ if (data_) {
+ send_callback_->Send(data_->data(), data_->size());
+ } else {
+ send_callback_->Send(nullptr, 0);
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/testing/mock_resource.h b/chromium/third_party/blink/renderer/platform/loader/testing/mock_resource.h
new file mode 100644
index 00000000000..69e307efbe4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/testing/mock_resource.h
@@ -0,0 +1,60 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_TESTING_MOCK_RESOURCE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_TESTING_MOCK_RESOURCE_H_
+
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+
+namespace blink {
+
+class FetchParameters;
+class ResourceFetcher;
+struct ResourceLoaderOptions;
+
+// Mocked cache handler class used by MockResource to test the caching behaviour
+// of Resource.
+class MockCacheHandler : public CachedMetadataHandler {
+ public:
+ MockCacheHandler(std::unique_ptr<CachedMetadataSender> send_callback);
+
+ void Set(const char* data, size_t);
+ void ClearCachedMetadata(CachedMetadataHandler::CacheType) override;
+ void Send();
+
+ String Encoding() const override { return "mock encoding"; }
+ bool IsServedFromCacheStorage() const override { return false; }
+
+ private:
+ std::unique_ptr<CachedMetadataSender> send_callback_;
+ base::Optional<Vector<char>> data_;
+};
+
+// Mocked Resource sub-class for testing. MockResource class can pretend a type
+// of Resource sub-class in a simple way. You should not expect anything
+// complicated to emulate actual sub-resources, but you may be able to use this
+// class to verify classes that consume Resource sub-classes in a simple way.
+class MockResource final : public Resource {
+ public:
+ static MockResource* Fetch(FetchParameters&,
+ ResourceFetcher*,
+ ResourceClient*);
+ static MockResource* Create(const ResourceRequest&);
+ static MockResource* Create(const KURL&);
+ MockResource(const ResourceRequest&, const ResourceLoaderOptions&);
+
+ CachedMetadataHandler* CreateCachedMetadataHandler(
+ std::unique_ptr<CachedMetadataSender> send_callback) override;
+ void SetSerializedCachedMetadata(const char*, size_t) override;
+
+ MockCacheHandler* CacheHandler();
+
+ void SendCachedMetadata(const char*, size_t);
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/testing/mock_resource_client.h b/chromium/third_party/blink/renderer/platform/loader/testing/mock_resource_client.h
new file mode 100644
index 00000000000..a9fe34287e3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/testing/mock_resource_client.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2013, 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_TESTING_MOCK_RESOURCE_CLIENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_TESTING_MOCK_RESOURCE_CLIENT_H_
+
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_client.h"
+
+namespace blink {
+
+class MockResourceClient : public GarbageCollectedFinalized<MockResourceClient>,
+ public ResourceClient {
+ USING_GARBAGE_COLLECTED_MIXIN(MockResourceClient);
+
+ public:
+ MockResourceClient() = default;
+ ~MockResourceClient() override = default;
+
+ void NotifyFinished(Resource*) override {
+ CHECK(!notify_finished_called_);
+ notify_finished_called_ = true;
+ }
+ String DebugName() const override { return "MockResourceClient"; }
+ bool NotifyFinishedCalled() const { return notify_finished_called_; }
+ void RemoveAsClient() { ClearResource(); }
+
+ protected:
+ bool notify_finished_called_ = false;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_TESTING_MOCK_RESOURCE_CLIENT_H_
diff --git a/chromium/third_party/blink/renderer/platform/loader/testing/web_url_loader_factory_with_mock.cc b/chromium/third_party/blink/renderer/platform/loader/testing/web_url_loader_factory_with_mock.cc
new file mode 100644
index 00000000000..3297570e76a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/testing/web_url_loader_factory_with_mock.cc
@@ -0,0 +1,24 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/loader/testing/web_url_loader_factory_with_mock.h"
+
+#include "third_party/blink/public/platform/web_url_loader.h"
+#include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
+
+namespace blink {
+
+WebURLLoaderFactoryWithMock::WebURLLoaderFactoryWithMock(
+ WebURLLoaderMockFactory* mock_factory)
+ : mock_factory_(mock_factory) {}
+
+WebURLLoaderFactoryWithMock::~WebURLLoaderFactoryWithMock() = default;
+
+std::unique_ptr<WebURLLoader> WebURLLoaderFactoryWithMock::CreateURLLoader(
+ const WebURLRequest& request,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+ return mock_factory_->CreateURLLoader(nullptr);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/testing/web_url_loader_factory_with_mock.h b/chromium/third_party/blink/renderer/platform/loader/testing/web_url_loader_factory_with_mock.h
new file mode 100644
index 00000000000..c66ba9ff8fd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/testing/web_url_loader_factory_with_mock.h
@@ -0,0 +1,32 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_TESTING_WEB_URL_LOADER_FACTORY_WITH_MOCK_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_TESTING_WEB_URL_LOADER_FACTORY_WITH_MOCK_H_
+
+#include <memory>
+
+#include "third_party/blink/public/platform/web_url_loader_factory.h"
+
+namespace blink {
+
+class WebURLLoaderMockFactory;
+
+class WebURLLoaderFactoryWithMock : public WebURLLoaderFactory {
+ public:
+ explicit WebURLLoaderFactoryWithMock(WebURLLoaderMockFactory*);
+ ~WebURLLoaderFactoryWithMock() override;
+
+ std::unique_ptr<WebURLLoader> CreateURLLoader(
+ const WebURLRequest&,
+ scoped_refptr<base::SingleThreadTaskRunner>) override;
+
+ private:
+ // Not owned. The mock factory should outlive |this|.
+ WebURLLoaderMockFactory* mock_factory_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_TESTING_WEB_URL_LOADER_FACTORY_WITH_MOCK_H_
diff --git a/chromium/third_party/blink/renderer/platform/long_task_detector.cc b/chromium/third_party/blink/renderer/platform/long_task_detector.cc
new file mode 100644
index 00000000000..d56839cee1c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/long_task_detector.cc
@@ -0,0 +1,54 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/long_task_detector.h"
+
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_thread.h"
+
+namespace blink {
+
+// static
+LongTaskDetector& LongTaskDetector::Instance() {
+ DEFINE_STATIC_LOCAL(Persistent<LongTaskDetector>, long_task_detector,
+ (new LongTaskDetector));
+ DCHECK(IsMainThread());
+ return *long_task_detector;
+}
+
+LongTaskDetector::LongTaskDetector() = default;
+
+void LongTaskDetector::RegisterObserver(LongTaskObserver* observer) {
+ DCHECK(IsMainThread());
+ DCHECK(observer);
+ observers_.insert(observer);
+ if (observers_.size() == 1) {
+ // Number of observers just became non-zero.
+ Platform::Current()->CurrentThread()->AddTaskTimeObserver(this);
+ }
+}
+
+void LongTaskDetector::UnregisterObserver(LongTaskObserver* observer) {
+ DCHECK(IsMainThread());
+ observers_.erase(observer);
+ if (observers_.size() == 0) {
+ Platform::Current()->CurrentThread()->RemoveTaskTimeObserver(this);
+ }
+}
+
+void LongTaskDetector::DidProcessTask(double start_time, double end_time) {
+ if ((end_time - start_time) < LongTaskDetector::kLongTaskThresholdSeconds)
+ return;
+
+ for (auto& observer : observers_) {
+ observer->OnLongTaskDetected(TimeTicksFromSeconds(start_time),
+ TimeTicksFromSeconds(end_time));
+ }
+}
+
+void LongTaskDetector::Trace(blink::Visitor* visitor) {
+ visitor->Trace(observers_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/long_task_detector.h b/chromium/third_party/blink/renderer/platform/long_task_detector.h
new file mode 100644
index 00000000000..d465cf6c481
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/long_task_detector.h
@@ -0,0 +1,56 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LONG_TASK_DETECTOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LONG_TASK_DETECTOR_H_
+
+#include "third_party/blink/public/platform/web_thread.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_time_observer.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT LongTaskObserver : public GarbageCollectedMixin {
+ public:
+ virtual ~LongTaskObserver() = default;
+
+ virtual void OnLongTaskDetected(TimeTicks start_time, TimeTicks end_time) = 0;
+};
+
+// LongTaskDetector detects tasks longer than kLongTaskThreshold and notifies
+// observers. When it has non-zero LongTaskObserver, it adds itself as a
+// TaskTimeObserver on the main thread and observes every task. When the number
+// of LongTaskObservers drop to zero it automatically removes itself as a
+// TaskTimeObserver.
+class PLATFORM_EXPORT LongTaskDetector final
+ : public GarbageCollectedFinalized<LongTaskDetector>,
+ public scheduler::TaskTimeObserver {
+ WTF_MAKE_NONCOPYABLE(LongTaskDetector);
+
+ public:
+ static LongTaskDetector& Instance();
+
+ void RegisterObserver(LongTaskObserver*);
+ void UnregisterObserver(LongTaskObserver*);
+
+ void Trace(blink::Visitor*);
+
+ static constexpr double kLongTaskThresholdSeconds = 0.05;
+
+ private:
+ LongTaskDetector();
+
+ // scheduler::TaskTimeObserver implementation
+ void WillProcessTask(double start_time) override {}
+ void DidProcessTask(double start_time, double end_time) override;
+
+ HeapHashSet<WeakMember<LongTaskObserver>> observers_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LONG_TASK_DETECTOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/long_task_detector_test.cc b/chromium/third_party/blink/renderer/platform/long_task_detector_test.cc
new file mode 100644
index 00000000000..ef75642bf52
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/long_task_detector_test.cc
@@ -0,0 +1,100 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/long_task_detector.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
+#include "third_party/blink/renderer/platform/web_task_runner.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+namespace {
+class TestLongTaskObserver :
+ // This has to be garbage collected since LongTaskObserver uses
+ // GarbageCollectedMixin.
+ public GarbageCollectedFinalized<TestLongTaskObserver>,
+ public LongTaskObserver {
+ USING_GARBAGE_COLLECTED_MIXIN(TestLongTaskObserver);
+
+ public:
+ TimeTicks last_long_task_start;
+ TimeTicks last_long_task_end;
+
+ // LongTaskObserver implementation.
+ void OnLongTaskDetected(TimeTicks start_time, TimeTicks end_time) override {
+ last_long_task_start = start_time;
+ last_long_task_end = end_time;
+ }
+}; // Anonymous namespace
+
+} // namespace
+class LongTaskDetectorTest : public testing::Test {
+ public:
+ // Public because it's executed on a task queue.
+ void DummyTaskWithDuration(double duration_seconds) {
+ dummy_task_start_time_ = CurrentTimeTicks();
+ platform_->AdvanceClockSeconds(duration_seconds);
+ dummy_task_end_time_ = CurrentTimeTicks();
+ }
+
+ protected:
+ void SetUp() {
+ // For some reason, platform needs to run for non-zero seconds before we
+ // start posting tasks to it. Otherwise TaskTimeObservers don't get notified
+ // of tasks.
+ platform_->RunForPeriodSeconds(1);
+ }
+ TimeTicks DummyTaskStartTime() { return dummy_task_start_time_; }
+
+ TimeTicks DummyTaskEndTime() { return dummy_task_end_time_; }
+
+ void SimulateTask(double duration_seconds) {
+ PostCrossThreadTask(
+ *platform_->CurrentThread()->GetTaskRunner(), FROM_HERE,
+ CrossThreadBind(&LongTaskDetectorTest::DummyTaskWithDuration,
+ CrossThreadUnretained(this), duration_seconds));
+ platform_->RunUntilIdle();
+ }
+
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform_;
+
+ private:
+ TimeTicks dummy_task_start_time_;
+ TimeTicks dummy_task_end_time_;
+};
+
+TEST_F(LongTaskDetectorTest, DeliversLongTaskNotificationOnlyWhenRegistered) {
+ TestLongTaskObserver* long_task_observer = new TestLongTaskObserver();
+ SimulateTask(LongTaskDetector::kLongTaskThresholdSeconds + 0.01);
+ EXPECT_EQ(long_task_observer->last_long_task_end, TimeTicks());
+
+ LongTaskDetector::Instance().RegisterObserver(long_task_observer);
+ SimulateTask(LongTaskDetector::kLongTaskThresholdSeconds + 0.01);
+ TimeTicks long_task_end_when_registered = DummyTaskEndTime();
+ EXPECT_EQ(long_task_observer->last_long_task_start, DummyTaskStartTime());
+ EXPECT_EQ(long_task_observer->last_long_task_end,
+ long_task_end_when_registered);
+
+ LongTaskDetector::Instance().UnregisterObserver(long_task_observer);
+ SimulateTask(LongTaskDetector::kLongTaskThresholdSeconds + 0.01);
+ // Check that we have a long task after unregistering observer.
+ ASSERT_FALSE(long_task_end_when_registered == DummyTaskEndTime());
+ EXPECT_EQ(long_task_observer->last_long_task_end,
+ long_task_end_when_registered);
+}
+
+TEST_F(LongTaskDetectorTest, DoesNotGetNotifiedOfShortTasks) {
+ TestLongTaskObserver* long_task_observer = new TestLongTaskObserver();
+ LongTaskDetector::Instance().RegisterObserver(long_task_observer);
+ SimulateTask(LongTaskDetector::kLongTaskThresholdSeconds - 0.01);
+ EXPECT_EQ(long_task_observer->last_long_task_end, TimeTicks());
+
+ SimulateTask(LongTaskDetector::kLongTaskThresholdSeconds + 0.01);
+ EXPECT_EQ(long_task_observer->last_long_task_end, DummyTaskEndTime());
+}
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/mac/block_exceptions.h b/chromium/third_party/blink/renderer/platform/mac/block_exceptions.h
new file mode 100644
index 00000000000..8a01389e826
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mac/block_exceptions.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2003 Apple Computer, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_MAC_BLOCK_EXCEPTIONS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MAC_BLOCK_EXCEPTIONS_H_
+
+#import <Foundation/NSException.h>
+#import "third_party/blink/renderer/platform/platform_export.h"
+#import "third_party/blink/renderer/platform/wtf/assertions.h"
+
+PLATFORM_EXPORT void ReportBlockedObjCException(NSException*);
+
+#define BEGIN_BLOCK_OBJC_EXCEPTIONS @try {
+#define END_BLOCK_OBJC_EXCEPTIONS \
+ } \
+ @catch (NSException * localException) { \
+ ReportBlockedObjCException(localException); \
+ }
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MAC_BLOCK_EXCEPTIONS_H_
diff --git a/chromium/third_party/blink/renderer/platform/mac/block_exceptions.mm b/chromium/third_party/blink/renderer/platform/mac/block_exceptions.mm
new file mode 100644
index 00000000000..bac6e5be833
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mac/block_exceptions.mm
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2003, 2007 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/mac/block_exceptions.h"
+
+#import "third_party/blink/renderer/platform/wtf/assertions.h"
+
+void ReportBlockedObjCException(NSException* exception) {
+#if DCHECK_IS_ON()
+ NOTREACHED() << "Uncaught exception - "
+ << [[exception description] UTF8String];
+ // This function is marked as NO_RETURN_DUE_TO_ASSERT, but NOTREACHED() and
+ // DCHECK(false) are not recognized as NO_RETURN.
+ LOG(FATAL);
+#else
+ NSLog(@"*** WebKit discarding exception: <%@> %@", [exception name],
+ [exception reason]);
+#endif
+}
diff --git a/chromium/third_party/blink/renderer/platform/mac/color_mac.h b/chromium/third_party/blink/renderer/platform/mac/color_mac.h
new file mode 100644
index 00000000000..682ffbf007b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mac/color_mac.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2007 Apple 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:
+ * 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 Apple Inc. ("Apple") 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 APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_MAC_COLOR_MAC_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MAC_COLOR_MAC_H_
+
+#include "third_party/blink/renderer/platform/graphics/color.h"
+
+OBJC_CLASS NSColor;
+
+namespace blink {
+
+// This function assumes NSColors are in DeviceRGB colorspace
+PLATFORM_EXPORT NSColor* NsColor(const Color&);
+}
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/mac/color_mac.mm b/chromium/third_party/blink/renderer/platform/mac/color_mac.mm
new file mode 100644
index 00000000000..76810bfbeff
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mac/color_mac.mm
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/mac/color_mac.h"
+
+#import <AppKit/AppKit.h>
+
+#import "third_party/blink/renderer/platform/wtf/retain_ptr.h"
+#import "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+
+namespace blink {
+
+// NSColor calls don't throw, so no need to block Cocoa exceptions in this file
+
+NSColor* NsColor(const Color& color) {
+ RGBA32 c = color.Rgb();
+ switch (c) {
+ case 0: {
+ // Need this to avoid returning nil because cachedRGBAValues will default
+ // to 0.
+ DEFINE_STATIC_LOCAL(
+ RetainPtr<NSColor>, clear_color,
+ ([NSColor colorWithDeviceRed:0 green:0 blue:0 alpha:0]));
+ return clear_color.Get();
+ }
+ case Color::kBlack: {
+ DEFINE_STATIC_LOCAL(
+ RetainPtr<NSColor>, black_color,
+ ([NSColor colorWithDeviceRed:0 green:0 blue:0 alpha:1]));
+ return black_color.Get();
+ }
+ case Color::kWhite: {
+ DEFINE_STATIC_LOCAL(
+ RetainPtr<NSColor>, white_color,
+ ([NSColor colorWithDeviceRed:1 green:1 blue:1 alpha:1]));
+ return white_color.Get();
+ }
+ default: {
+ const int kCacheSize = 32;
+ static unsigned cached_rgba_values[kCacheSize];
+ static RetainPtr<NSColor>* cached_colors =
+ new RetainPtr<NSColor>[kCacheSize];
+
+ for (int i = 0; i != kCacheSize; ++i) {
+ if (cached_rgba_values[i] == c)
+ return cached_colors[i].Get();
+ }
+
+ NSColor* result = [NSColor
+ colorWithDeviceRed:static_cast<CGFloat>(color.Red()) / 255
+ green:static_cast<CGFloat>(color.Green()) / 255
+ blue:static_cast<CGFloat>(color.Blue()) / 255
+ alpha:static_cast<CGFloat>(color.Alpha()) / 255];
+
+ static int cursor;
+ cached_rgba_values[cursor] = c;
+ cached_colors[cursor] = result;
+ if (++cursor == kCacheSize)
+ cursor = 0;
+ return result;
+ }
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/mac/graphics_context_canvas.h b/chromium/third_party/blink/renderer/platform/mac/graphics_context_canvas.h
new file mode 100644
index 00000000000..437044de936
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mac/graphics_context_canvas.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MAC_GRAPHICS_CONTEXT_CANVAS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MAC_GRAPHICS_CONTEXT_CANVAS_H_
+
+#include <ApplicationServices/ApplicationServices.h>
+
+#include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+
+struct SkIRect;
+
+namespace blink {
+
+// Creates a bridge for painting into a PaintCanvas with a CGContext.
+class PLATFORM_EXPORT GraphicsContextCanvas {
+ public:
+ // Internally creates a bitmap the same size |paint_rect|, scaled by
+ // |bitmap_scale_factor|. Painting into the CgContext will go into the
+ // bitmap. Upon destruction, that bitmap will be painted into the
+ // canvas as the rectangle |paint_rect|. Users are expected to
+ // clip |paint_rect| to reasonable sizes to not create giant bitmaps.
+ // The |paint_rect| is in canvas device space. The CgContext is set
+ // up to be in exactly the same space as the canvas is at construction
+ // time.
+ GraphicsContextCanvas(PaintCanvas*,
+ const SkIRect& paint_rect,
+ SkScalar bitmap_scale_factor = 1);
+ ~GraphicsContextCanvas();
+
+ CGContextRef CgContext();
+
+ private:
+ void ReleaseIfNeeded();
+
+ PaintCanvas* canvas_;
+
+ CGContextRef cg_context_;
+ SkBitmap offscreen_;
+ SkScalar bitmap_scale_factor_;
+
+ SkIRect paint_rect_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MAC_GRAPHICS_CONTEXT_CANVAS_H_
diff --git a/chromium/third_party/blink/renderer/platform/mac/graphics_context_canvas.mm b/chromium/third_party/blink/renderer/platform/mac/graphics_context_canvas.mm
new file mode 100644
index 00000000000..73936289674
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mac/graphics_context_canvas.mm
@@ -0,0 +1,80 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/mac/graphics_context_canvas.h"
+
+#import <AppKit/AppKit.h>
+#import <CoreGraphics/CoreGraphics.h>
+
+#include "skia/ext/skia_utils_mac.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/wtf/retain_ptr.h"
+
+namespace blink {
+
+GraphicsContextCanvas::GraphicsContextCanvas(PaintCanvas* canvas,
+ const SkIRect& paint_rect,
+ SkScalar bitmap_scale_factor)
+ : canvas_(canvas),
+ cg_context_(0),
+ bitmap_scale_factor_(bitmap_scale_factor),
+ paint_rect_(paint_rect) {
+ // Callers should just avoid painting at all when this is the case.
+ DCHECK(!paint_rect_.isEmpty());
+}
+
+GraphicsContextCanvas::~GraphicsContextCanvas() {
+ ReleaseIfNeeded();
+}
+
+// This must be called to balance calls to cgContext
+void GraphicsContextCanvas::ReleaseIfNeeded() {
+ if (!cg_context_)
+ return;
+ offscreen_.setImmutable(); // Prevents a defensive copy inside Skia.
+ canvas_->save();
+ canvas_->setMatrix(SkMatrix::I()); // Reset back to device space.
+ canvas_->translate(paint_rect_.x(), paint_rect_.y());
+ canvas_->scale(1.f / bitmap_scale_factor_, 1.f / bitmap_scale_factor_);
+ canvas_->drawBitmap(offscreen_, 0, 0);
+ canvas_->restore();
+
+ CGContextRelease(cg_context_);
+ cg_context_ = 0;
+}
+
+CGContextRef GraphicsContextCanvas::CgContext() {
+ ReleaseIfNeeded(); // This flushes any prior bitmap use
+
+ // Allocate an offscreen and draw into that, relying on the
+ // compositing step to apply skia's clip.
+ WTF::RetainPtr<CGColorSpace> color_space(
+ CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
+
+ bool result = offscreen_.tryAllocN32Pixels(
+ SkScalarCeilToInt(bitmap_scale_factor_ * paint_rect_.width()),
+ SkScalarCeilToInt(bitmap_scale_factor_ * paint_rect_.height()));
+ DCHECK(result);
+ if (!result)
+ return 0;
+ offscreen_.eraseColor(0);
+ int display_height = offscreen_.height();
+ cg_context_ = CGBitmapContextCreate(
+ offscreen_.getPixels(), offscreen_.width(), offscreen_.height(), 8,
+ offscreen_.rowBytes(), color_space.Get(),
+ kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst);
+ DCHECK(cg_context_);
+
+ SkMatrix matrix = canvas_->getTotalMatrix();
+ matrix.postTranslate(-SkIntToScalar(paint_rect_.x()),
+ -SkIntToScalar(paint_rect_.y()));
+ matrix.postScale(bitmap_scale_factor_, -bitmap_scale_factor_);
+ matrix.postTranslate(0, SkIntToScalar(display_height));
+
+ CGContextConcatCTM(cg_context_, skia::SkMatrixToCGAffineTransform(matrix));
+
+ return cg_context_;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/mac/graphics_context_canvas_test.mm b/chromium/third_party/blink/renderer/platform/mac/graphics_context_canvas_test.mm
new file mode 100644
index 00000000000..6c2f820c8bd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mac/graphics_context_canvas_test.mm
@@ -0,0 +1,72 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "skia/ext/skia_utils_mac.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/mac/graphics_context_canvas.h"
+
+namespace blink {
+
+enum TestType {
+ kTestIdentity = 0,
+ kTestTranslate = 1,
+ kTestClip = 2,
+ kTestXClip = kTestTranslate | kTestClip,
+};
+
+void RunTest(TestType test) {
+ const unsigned kWidth = 2;
+ const unsigned kHeight = 2;
+ const unsigned kStorageSize = kWidth * kHeight;
+ const unsigned kOriginal[] = {0xFF333333, 0xFF666666, 0xFF999999, 0xFFCCCCCC};
+ EXPECT_EQ(kStorageSize, sizeof(kOriginal) / sizeof(kOriginal[0]));
+ unsigned bits[kStorageSize];
+ memcpy(bits, kOriginal, sizeof(kOriginal));
+ SkImageInfo info = SkImageInfo::MakeN32Premul(kWidth, kHeight);
+ SkBitmap bitmap;
+ bitmap.installPixels(info, bits, info.minRowBytes());
+
+ SkiaPaintCanvas canvas(bitmap);
+ if (test & kTestTranslate)
+ canvas.translate(kWidth / 2, 0);
+ if (test & kTestClip) {
+ SkRect clip_rect = {0, kHeight / 2, kWidth, kHeight};
+ canvas.clipRect(clip_rect);
+ }
+ {
+ SkIRect clip = SkIRect::MakeWH(kWidth, kHeight);
+ GraphicsContextCanvas bit_locker(&canvas, clip);
+ CGContextRef cg_context = bit_locker.CgContext();
+ CGColorRef test_color = CGColorGetConstantColor(kCGColorWhite);
+ CGContextSetFillColorWithColor(cg_context, test_color);
+ CGRect cg_rect = {{0, 0}, {kWidth, kHeight}};
+ CGContextFillRect(cg_context, cg_rect);
+ }
+ const unsigned kResults[][kStorageSize] = {
+ {0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}, // identity
+ {0xFF333333, 0xFFFFFFFF, 0xFF999999, 0xFFFFFFFF}, // translate
+ {0xFF333333, 0xFF666666, 0xFFFFFFFF, 0xFFFFFFFF}, // clip
+ {0xFF333333, 0xFF666666, 0xFF999999, 0xFFFFFFFF} // translate | clip
+ };
+ for (unsigned index = 0; index < kStorageSize; index++)
+ EXPECT_EQ(kResults[test][index], bits[index]) << "Index: " << index;
+}
+
+TEST(GraphicsContextCanvasTest, Identity) {
+ RunTest(kTestIdentity);
+}
+
+TEST(GraphicsContextCanvasTest, Translate) {
+ RunTest(kTestTranslate);
+}
+
+TEST(GraphicsContextCanvasTest, Clip) {
+ RunTest(kTestClip);
+}
+
+TEST(GraphicsContextCanvasTest, XClip) {
+ RunTest(kTestXClip);
+}
+
+} // namespace
diff --git a/chromium/third_party/blink/renderer/platform/mac/kill_ring_mac.mm b/chromium/third_party/blink/renderer/platform/mac/kill_ring_mac.mm
new file mode 100644
index 00000000000..98dea7c4c3c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mac/kill_ring_mac.mm
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2010 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. 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 "third_party/blink/renderer/platform/kill_ring.h"
+
+namespace blink {
+
+extern "C" {
+
+// Kill ring calls. Would be better to use NSKillRing.h, but that's not
+// available as API or SPI.
+
+void _NSInitializeKillRing();
+void _NSAppendToKillRing(NSString*);
+void _NSPrependToKillRing(NSString*);
+NSString* _NSYankFromKillRing();
+void _NSNewKillRingSequence();
+void _NSSetKillRingToYankedState();
+}
+
+static void InitializeKillRingIfNeeded() {
+ static bool initialized_kill_ring = false;
+ if (!initialized_kill_ring) {
+ initialized_kill_ring = true;
+ _NSInitializeKillRing();
+ }
+}
+
+void KillRing::Append(const String& string) {
+ InitializeKillRingIfNeeded();
+ _NSAppendToKillRing(string);
+}
+
+void KillRing::Prepend(const String& string) {
+ InitializeKillRingIfNeeded();
+ _NSPrependToKillRing(string);
+}
+
+String KillRing::Yank() {
+ InitializeKillRingIfNeeded();
+ return _NSYankFromKillRing();
+}
+
+void KillRing::StartNewSequence() {
+ InitializeKillRingIfNeeded();
+ _NSNewKillRingSequence();
+}
+
+void KillRing::SetToYankedState() {
+ InitializeKillRingIfNeeded();
+ _NSSetKillRingToYankedState();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/mac/local_current_graphics_context.h b/chromium/third_party/blink/renderer/platform/mac/local_current_graphics_context.h
new file mode 100644
index 00000000000..bf4ba51c701
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mac/local_current_graphics_context.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MAC_LOCAL_CURRENT_GRAPHICS_CONTEXT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MAC_LOCAL_CURRENT_GRAPHICS_CONTEXT_H_
+
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
+#include "third_party/blink/renderer/platform/mac/graphics_context_canvas.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+OBJC_CLASS NSGraphicsContext;
+
+namespace blink {
+
+class GraphicsContext;
+
+// This class automatically saves and restores the current NSGraphicsContext for
+// functions which call out into AppKit and rely on the currentContext being set
+class PLATFORM_EXPORT LocalCurrentGraphicsContext {
+ WTF_MAKE_NONCOPYABLE(LocalCurrentGraphicsContext);
+
+ public:
+ LocalCurrentGraphicsContext(GraphicsContext&, const IntRect& dirty_rect);
+ LocalCurrentGraphicsContext(PaintCanvas*,
+ float device_scale_factor,
+ const IntRect& dirty_rect);
+ ~LocalCurrentGraphicsContext();
+ CGContextRef CgContext();
+
+ private:
+ PaintCanvas* saved_canvas_;
+ NSGraphicsContext* saved_ns_graphics_context_;
+ bool did_set_graphics_context_;
+ IntRect inflated_dirty_rect_;
+ GraphicsContextCanvas graphics_context_canvas_;
+};
+}
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MAC_LOCAL_CURRENT_GRAPHICS_CONTEXT_H_
diff --git a/chromium/third_party/blink/renderer/platform/mac/local_current_graphics_context.mm b/chromium/third_party/blink/renderer/platform/mac/local_current_graphics_context.mm
new file mode 100644
index 00000000000..1c9a7900d49
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mac/local_current_graphics_context.mm
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "third_party/blink/renderer/platform/mac/local_current_graphics_context.h"
+
+#include <AppKit/NSGraphicsContext.h>
+#include "skia/ext/platform_canvas.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
+#include "third_party/blink/renderer/platform/mac/theme_mac.h"
+#include "third_party/skia/include/core/SkRegion.h"
+
+namespace blink {
+
+LocalCurrentGraphicsContext::LocalCurrentGraphicsContext(
+ GraphicsContext& graphics_context,
+ const IntRect& dirty_rect)
+ : LocalCurrentGraphicsContext(graphics_context.Canvas(),
+ graphics_context.DeviceScaleFactor(),
+ dirty_rect) {}
+
+static const int kMaxDirtyRectPixelSize = 10000;
+
+static SkIRect LocalToClampedDeviceRect(PaintCanvas* canvas,
+ const IntRect& local) {
+ const SkMatrix& matrix = canvas->getTotalMatrix();
+ SkRect device;
+ if (!matrix.mapRect(&device, local))
+ return SkIRect();
+ // Constrain the maximum size of what we paint to something reasonable. This
+ // accordingly means we will not paint the entirety of truly huge native form
+ // elements, which is deemed an acceptable tradeoff for this simple approach
+ // to manage such an edge case.
+ SkIRect idevice = device.roundOut();
+ idevice.intersect(SkIRect::MakeXYWH(idevice.x(), idevice.y(),
+ kMaxDirtyRectPixelSize,
+ kMaxDirtyRectPixelSize));
+ return idevice;
+}
+
+LocalCurrentGraphicsContext::LocalCurrentGraphicsContext(
+ PaintCanvas* canvas,
+ float device_scale_factor,
+ const IntRect& dirty_rect)
+ : did_set_graphics_context_(false),
+ inflated_dirty_rect_(ThemeMac::InflateRectForAA(dirty_rect)),
+ graphics_context_canvas_(
+ canvas,
+ LocalToClampedDeviceRect(canvas, inflated_dirty_rect_),
+ device_scale_factor) {
+ saved_canvas_ = canvas;
+ canvas->save();
+
+ CGContextRef cg_context = this->CgContext();
+ if (cg_context == [[NSGraphicsContext currentContext] graphicsPort]) {
+ saved_ns_graphics_context_ = 0;
+ return;
+ }
+
+ saved_ns_graphics_context_ = [[NSGraphicsContext currentContext] retain];
+ NSGraphicsContext* new_context =
+ [NSGraphicsContext graphicsContextWithGraphicsPort:cg_context
+ flipped:YES];
+ [NSGraphicsContext setCurrentContext:new_context];
+ did_set_graphics_context_ = true;
+}
+
+LocalCurrentGraphicsContext::~LocalCurrentGraphicsContext() {
+ if (did_set_graphics_context_) {
+ [NSGraphicsContext setCurrentContext:saved_ns_graphics_context_];
+ [saved_ns_graphics_context_ release];
+ }
+
+ saved_canvas_->restore();
+}
+
+CGContextRef LocalCurrentGraphicsContext::CgContext() {
+ // This synchronizes the CGContext to reflect the current SkCanvas state.
+ // The implementation may not return the same CGContext each time.
+ CGContextRef cg_context = graphics_context_canvas_.CgContext();
+
+ return cg_context;
+}
+}
diff --git a/chromium/third_party/blink/renderer/platform/mac/ns_scroller_imp_details.h b/chromium/third_party/blink/renderer/platform/mac/ns_scroller_imp_details.h
new file mode 100644
index 00000000000..c5b7581b5f0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mac/ns_scroller_imp_details.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2011 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 WebCore_NSScrollerImpDetails_h
+#define WebCore_NSScrollerImpDetails_h
+
+#import <AvailabilityMacros.h>
+
+@interface NSObject (ScrollbarPainter)
++ (id)scrollerImpWithStyle:(NSScrollerStyle)newScrollerStyle
+ controlSize:(NSControlSize)newControlSize
+ horizontal:(BOOL)horizontal
+ replacingScrollerImp:(id)previous;
+- (CGFloat)knobAlpha;
+- (void)setKnobAlpha:(CGFloat)knobAlpha;
+- (CGFloat)trackAlpha;
+- (void)setTrackAlpha:(CGFloat)trackAlpha;
+- (void)setEnabled:(BOOL)enabled;
+- (void)setBoundsSize:(NSSize)boundsSize;
+- (void)setDoubleValue:(double)doubleValue;
+- (void)setKnobProportion:(CGFloat)proportion;
+- (void)setKnobStyle:(NSScrollerKnobStyle)knobStyle;
+- (void)setExpanded:(BOOL)expanded;
+- (BOOL)isExpanded;
+- (void)setDelegate:(id)delegate;
+- (void)setUiStateTransitionProgress:(CGFloat)uiStateTransitionProgress;
+- (void)setExpansionTransitionProgress:(CGFloat)expansionTransitionProgress;
+- (BOOL)isHorizontal;
+- (CGFloat)trackWidth;
+- (CGFloat)trackBoxWidth;
+- (CGFloat)knobMinLength;
+- (CGFloat)trackOverlapEndInset;
+- (CGFloat)knobOverlapEndInset;
+- (CGFloat)trackEndInset;
+- (CGFloat)knobEndInset;
+- (CGFloat)uiStateTransitionProgress;
+- (CGFloat)expansionTransitionProgress;
+- (NSRect)rectForPart:(NSScrollerPart)partCode;
+- (void)drawKnobSlotInRect:(NSRect)slotRect
+ highlight:(BOOL)flag
+ alpha:(CGFloat)alpha;
+- (void)drawKnob;
+- (void)mouseEnteredScroller;
+- (void)mouseExitedScroller;
+@end
+
+@interface NSObject (ScrollbarPainterController)
+- (void)setDelegate:(id)delegate;
+- (void)hideOverlayScrollers;
+- (void)flashScrollers;
+- (id)horizontalScrollerImp;
+- (void)setHorizontalScrollerImp:(id)horizontal;
+- (id)verticalScrollerImp;
+- (void)setVerticalScrollerImp:(id)vertical;
+- (NSScrollerStyle)scrollerStyle;
+- (void)setScrollerStyle:(NSScrollerStyle)scrollerStyle;
+- (void)contentAreaScrolled;
+- (void)contentAreaScrolledInDirection:(NSPoint)direction;
+- (void)contentAreaWillDraw;
+- (void)mouseEnteredContentArea;
+- (void)mouseExitedContentArea;
+- (void)mouseMovedInContentArea;
+- (void)startLiveResize;
+- (void)contentAreaDidResize;
+- (void)endLiveResize;
+- (void)windowOrderedIn;
+- (void)windowOrderedOut;
+- (void)beginScrollGesture;
+- (void)endScrollGesture;
+@end
+
+#endif // WebCore_NSScrollerImpDetails_h
diff --git a/chromium/third_party/blink/renderer/platform/mac/scroll_animator_mac.h b/chromium/third_party/blink/renderer/platform/mac/scroll_animator_mac.h
new file mode 100644
index 00000000000..9cc37379c42
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mac/scroll_animator_mac.h
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2010, 2011 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_MAC_SCROLL_ANIMATOR_MAC_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MAC_SCROLL_ANIMATOR_MAC_H_
+
+#include <memory>
+#include "base/single_thread_task_runner.h"
+#include "third_party/blink/renderer/platform/geometry/float_point.h"
+#include "third_party/blink/renderer/platform/geometry/float_size.h"
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/scroll/scroll_animator_base.h"
+#include "third_party/blink/renderer/platform/timer.h"
+#include "third_party/blink/renderer/platform/web_task_runner.h"
+#include "third_party/blink/renderer/platform/wtf/retain_ptr.h"
+
+OBJC_CLASS BlinkScrollAnimationHelperDelegate;
+OBJC_CLASS BlinkScrollbarPainterControllerDelegate;
+OBJC_CLASS BlinkScrollbarPainterDelegate;
+
+typedef id ScrollbarPainterController;
+
+namespace blink {
+
+class Scrollbar;
+
+// ScrollAnimatorMac implements keyboard-triggered scroll offset animations,
+// scrollbar painting, and scrollbar opacity animations by delegating to native
+// Cocoa APIs.
+//
+// Scroll offset animations are also known as "smooth scrolling". For the
+// non-Mac implementation of user input smooth scrolling, see ScrollAnimator.
+// For programmatic (CSSOM) smooth scrolls, see ProgrammaticScrollAnimator.
+//
+// Unlike ScrollAnimator, ScrollAnimatorMac only smooth-scrolls keyboard
+// scrolls, and not mouse wheel scrolls. It also does not use compositor
+// animations or any of the standard Blink animation machinery.
+//
+// This divergence is mostly historical. We could probably switch Mac to use
+// ScrollAnimator for smooth scrolls if we factored out the scrollbar-related
+// logic. See crbug.com/574283 and crbug.com/682209.
+//
+// ScrollAnimatorMac's scroll offset animations are implemented by
+// NSScrollAnimationHelper which invokes a BlinkScrollAnimationHelperDelegate to
+// service an animation frame by performing an immediate scroll to the requested
+// offset (via NotifyOffsetChanged).
+//
+// The "scrollbar painter controller" is an NSScrollerImpPair object, which
+// calls back into Blink via BlinkScrollbarPainterControllerDelegate.
+//
+// The "scrollbar painter" is an NSScrollerImp object, which calls back into
+// Blink via BlinkScrollbarPainterDelegate. The scrollbar painter is registered
+// with ScrollbarThemeMac, so that the ScrollbarTheme painting APIs can call
+// into it.
+//
+// The scrollbar painter initiates an overlay scrollbar fade-out animation by
+// calling animateKnobAlphaTo on the delegate. This starts a timer inside the
+// BlinkScrollbarPartAnimationTimer. Each tick evaluates a cubic bezier
+// function to obtain the current opacity, which is stored in the scrollbar
+// painter with setKnobAlpha.
+//
+// If the scroller is composited, the opacity value stored on the scrollbar
+// painter is subsequently read out through ScrollbarThemeMac::ThumbOpacity and
+// plumbed into PaintedScrollbarLayerImpl::thumb_opacity_.
+//
+// TODO: explain other types of animations (TrackAlpha, UIStateTransition,
+// ExpansionTransition), scrollbar paint timer, plumbing of scrollbar paint
+// invalidations.
+
+class PLATFORM_EXPORT ScrollAnimatorMac : public ScrollAnimatorBase {
+ USING_PRE_FINALIZER(ScrollAnimatorMac, Dispose);
+
+ public:
+ ScrollAnimatorMac(ScrollableArea*);
+ ~ScrollAnimatorMac() override;
+
+ void Dispose() override;
+
+ void ImmediateScrollToOffsetForScrollAnimation(
+ const ScrollOffset& new_offset);
+ bool HaveScrolledSincePageLoad() const {
+ return have_scrolled_since_page_load_;
+ }
+
+ void UpdateScrollerStyle();
+
+ bool ScrollbarPaintTimerIsActive() const;
+ void StartScrollbarPaintTimer();
+ void StopScrollbarPaintTimer();
+
+ void SendContentAreaScrolledSoon(const ScrollOffset& scroll_delta);
+
+ virtual void Trace(blink::Visitor* visitor) {
+ ScrollAnimatorBase::Trace(visitor);
+ }
+
+ private:
+ RetainPtr<id> scroll_animation_helper_;
+ RetainPtr<BlinkScrollAnimationHelperDelegate>
+ scroll_animation_helper_delegate_;
+
+ RetainPtr<ScrollbarPainterController> scrollbar_painter_controller_;
+ RetainPtr<BlinkScrollbarPainterControllerDelegate>
+ scrollbar_painter_controller_delegate_;
+ RetainPtr<BlinkScrollbarPainterDelegate>
+ horizontal_scrollbar_painter_delegate_;
+ RetainPtr<BlinkScrollbarPainterDelegate> vertical_scrollbar_painter_delegate_;
+
+ void InitialScrollbarPaintTask();
+ TaskHandle initial_scrollbar_paint_task_handle_;
+
+ void SendContentAreaScrolledTask();
+ TaskHandle send_content_area_scrolled_task_handle_;
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ ScrollOffset content_area_scrolled_timer_scroll_delta_;
+
+ ScrollResult UserScroll(ScrollGranularity,
+ const ScrollOffset& delta) override;
+ void ScrollToOffsetWithoutAnimation(const ScrollOffset&) override;
+
+ void CancelAnimation() override;
+
+ void ContentAreaWillPaint() const override;
+ void MouseEnteredContentArea() const override;
+ void MouseExitedContentArea() const override;
+ void MouseMovedInContentArea() const override;
+ void MouseEnteredScrollbar(Scrollbar&) const override;
+ void MouseExitedScrollbar(Scrollbar&) const override;
+ void ContentsResized() const override;
+ void ContentAreaDidShow() const override;
+ void ContentAreaDidHide() const override;
+
+ void FinishCurrentScrollAnimations() override;
+
+ void DidAddVerticalScrollbar(Scrollbar&) override;
+ void WillRemoveVerticalScrollbar(Scrollbar&) override;
+ void DidAddHorizontalScrollbar(Scrollbar&) override;
+ void WillRemoveHorizontalScrollbar(Scrollbar&) override;
+
+ void NotifyContentAreaScrolled(const ScrollOffset& delta,
+ ScrollType) override;
+
+ bool SetScrollbarsVisibleForTesting(bool) override;
+
+ ScrollOffset AdjustScrollOffsetIfNecessary(const ScrollOffset&) const;
+
+ void ImmediateScrollTo(const ScrollOffset&);
+
+ bool have_scrolled_since_page_load_;
+ bool needs_scroller_style_update_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MAC_SCROLL_ANIMATOR_MAC_H_
diff --git a/chromium/third_party/blink/renderer/platform/mac/scroll_animator_mac.mm b/chromium/third_party/blink/renderer/platform/mac/scroll_animator_mac.mm
new file mode 100644
index 00000000000..5ad7c8a2aaf
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mac/scroll_animator_mac.mm
@@ -0,0 +1,1100 @@
+/*
+ * Copyright (C) 2010, 2011 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/mac/scroll_animator_mac.h"
+
+#import <AppKit/AppKit.h>
+
+#include <memory>
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/animation/timing_function.h"
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/mac/block_exceptions.h"
+#include "third_party/blink/renderer/platform/mac/ns_scroller_imp_details.h"
+#include "third_party/blink/renderer/platform/scheduler/child/web_scheduler.h"
+#include "third_party/blink/renderer/platform/scroll/scrollable_area.h"
+#include "third_party/blink/renderer/platform/scroll/scrollbar_theme.h"
+#include "third_party/blink/renderer/platform/scroll/scrollbar_theme_mac.h"
+#include "third_party/blink/renderer/platform/timer.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+namespace {
+
+bool SupportsUIStateTransitionProgress() {
+ // FIXME: This is temporary until all platforms that support ScrollbarPainter
+ // support this part of the API.
+ static bool global_supports_ui_state_transition_progress =
+ [NSClassFromString(@"NSScrollerImp")
+ instancesRespondToSelector:@selector(mouseEnteredScroller)];
+ return global_supports_ui_state_transition_progress;
+}
+
+bool SupportsExpansionTransitionProgress() {
+ static bool global_supports_expansion_transition_progress =
+ [NSClassFromString(@"NSScrollerImp")
+ instancesRespondToSelector:@selector(expansionTransitionProgress)];
+ return global_supports_expansion_transition_progress;
+}
+
+bool SupportsContentAreaScrolledInDirection() {
+ static bool global_supports_content_area_scrolled_in_direction =
+ [NSClassFromString(@"NSScrollerImpPair")
+ instancesRespondToSelector:@selector
+ (contentAreaScrolledInDirection:)];
+ return global_supports_content_area_scrolled_in_direction;
+}
+
+blink::ScrollbarThemeMac* MacOverlayScrollbarTheme(
+ blink::ScrollbarTheme& scrollbar_theme) {
+ return !scrollbar_theme.IsMockTheme()
+ ? static_cast<blink::ScrollbarThemeMac*>(&scrollbar_theme)
+ : nil;
+}
+
+ScrollbarPainter ScrollbarPainterForScrollbar(blink::Scrollbar& scrollbar) {
+ if (blink::ScrollbarThemeMac* scrollbar_theme =
+ MacOverlayScrollbarTheme(scrollbar.GetTheme()))
+ return scrollbar_theme->PainterForScrollbar(scrollbar);
+
+ return nil;
+}
+
+} // namespace
+
+@interface NSObject (ScrollAnimationHelperDetails)
+- (id)initWithDelegate:(id)delegate;
+- (void)_stopRun;
+- (BOOL)_isAnimating;
+- (NSPoint)targetOrigin;
+- (CGFloat)_progress;
+@end
+
+@interface BlinkScrollAnimationHelperDelegate : NSObject {
+ blink::ScrollAnimatorMac* _animator;
+}
+- (id)initWithScrollAnimator:(blink::ScrollAnimatorMac*)scrollAnimator;
+@end
+
+static NSSize abs(NSSize size) {
+ NSSize finalSize = size;
+ if (finalSize.width < 0)
+ finalSize.width = -finalSize.width;
+ if (finalSize.height < 0)
+ finalSize.height = -finalSize.height;
+ return finalSize;
+}
+
+@implementation BlinkScrollAnimationHelperDelegate
+
+- (id)initWithScrollAnimator:(blink::ScrollAnimatorMac*)scrollAnimator {
+ self = [super init];
+ if (!self)
+ return nil;
+
+ _animator = scrollAnimator;
+ return self;
+}
+
+- (void)invalidate {
+ _animator = 0;
+}
+
+- (NSRect)bounds {
+ if (!_animator)
+ return NSZeroRect;
+
+ blink::ScrollOffset currentOffset = _animator->CurrentOffset();
+ return NSMakeRect(currentOffset.Width(), currentOffset.Height(), 0, 0);
+}
+
+- (void)_immediateScrollToPoint:(NSPoint)newPosition {
+ if (!_animator)
+ return;
+ _animator->ImmediateScrollToOffsetForScrollAnimation(
+ blink::ToScrollOffset(newPosition));
+}
+
+- (NSPoint)_pixelAlignProposedScrollPosition:(NSPoint)newOrigin {
+ return newOrigin;
+}
+
+- (NSSize)convertSizeToBase:(NSSize)size {
+ return abs(size);
+}
+
+- (NSSize)convertSizeFromBase:(NSSize)size {
+ return abs(size);
+}
+
+- (NSSize)convertSizeToBacking:(NSSize)size {
+ return abs(size);
+}
+
+- (NSSize)convertSizeFromBacking:(NSSize)size {
+ return abs(size);
+}
+
+- (id)superview {
+ return nil;
+}
+
+- (id)documentView {
+ return nil;
+}
+
+- (id)window {
+ return nil;
+}
+
+- (void)_recursiveRecomputeToolTips {
+}
+
+@end
+
+@interface BlinkScrollbarPainterControllerDelegate : NSObject {
+ blink::ScrollableArea* _scrollableArea;
+}
+- (id)initWithScrollableArea:(blink::ScrollableArea*)scrollableArea;
+@end
+
+@implementation BlinkScrollbarPainterControllerDelegate
+
+- (id)initWithScrollableArea:(blink::ScrollableArea*)scrollableArea {
+ self = [super init];
+ if (!self)
+ return nil;
+
+ _scrollableArea = scrollableArea;
+ return self;
+}
+
+- (void)invalidate {
+ _scrollableArea = 0;
+}
+
+- (NSRect)contentAreaRectForScrollerImpPair:(id)scrollerImpPair {
+ if (!_scrollableArea)
+ return NSZeroRect;
+
+ blink::IntSize contentsSize = _scrollableArea->ContentsSize();
+ return NSMakeRect(0, 0, contentsSize.Width(), contentsSize.Height());
+}
+
+- (BOOL)inLiveResizeForScrollerImpPair:(id)scrollerImpPair {
+ return NO;
+}
+
+- (NSPoint)mouseLocationInContentAreaForScrollerImpPair:(id)scrollerImpPair {
+ if (!_scrollableArea)
+ return NSZeroPoint;
+
+ return _scrollableArea->LastKnownMousePosition();
+}
+
+- (NSPoint)scrollerImpPair:(id)scrollerImpPair
+ convertContentPoint:(NSPoint)pointInContentArea
+ toScrollerImp:(id)scrollerImp {
+ if (!_scrollableArea || !scrollerImp)
+ return NSZeroPoint;
+
+ blink::Scrollbar* scrollbar = nil;
+ if ([scrollerImp isHorizontal])
+ scrollbar = _scrollableArea->HorizontalScrollbar();
+ else
+ scrollbar = _scrollableArea->VerticalScrollbar();
+
+ // It is possible to have a null scrollbar here since it is possible for this
+ // delegate
+ // method to be called between the moment when a scrollbar has been set to 0
+ // and the
+ // moment when its destructor has been called. We should probably de-couple
+ // some
+ // of the clean-up work in ScrollbarThemeMac::unregisterScrollbar() to avoid
+ // this
+ // issue.
+ if (!scrollbar)
+ return NSZeroPoint;
+
+ DCHECK_EQ(scrollerImp, ScrollbarPainterForScrollbar(*scrollbar));
+
+ return scrollbar->ConvertFromContainingEmbeddedContentView(
+ blink::IntPoint(pointInContentArea));
+}
+
+- (void)scrollerImpPair:(id)scrollerImpPair
+ setContentAreaNeedsDisplayInRect:(NSRect)rect {
+ if (!_scrollableArea)
+ return;
+
+ if (!_scrollableArea->ScrollbarsCanBeActive())
+ return;
+
+ _scrollableArea->GetScrollAnimator().ContentAreaWillPaint();
+}
+
+- (void)scrollerImpPair:(id)scrollerImpPair
+ updateScrollerStyleForNewRecommendedScrollerStyle:
+ (NSScrollerStyle)newRecommendedScrollerStyle {
+ // Chrome has a single process mode which is used for testing on Mac. In that
+ // mode, WebKit runs on a thread in the
+ // browser process. This notification is called by the OS on the main thread
+ // in the browser process, and not on the
+ // the WebKit thread. Better to not update the style than crash.
+ // http://crbug.com/126514
+ if (!IsMainThread())
+ return;
+
+ if (!_scrollableArea)
+ return;
+
+ [scrollerImpPair setScrollerStyle:newRecommendedScrollerStyle];
+
+ static_cast<blink::ScrollAnimatorMac&>(_scrollableArea->GetScrollAnimator())
+ .UpdateScrollerStyle();
+}
+
+@end
+
+enum FeatureToAnimate {
+ ThumbAlpha,
+ TrackAlpha,
+ UIStateTransition,
+ ExpansionTransition
+};
+
+@class BlinkScrollbarPartAnimation;
+
+namespace blink {
+
+// This class is used to drive the animation timer for
+// BlinkScrollbarPartAnimation
+// objects. This is used instead of NSAnimation because CoreAnimation
+// establishes connections to the WindowServer, which should not be done in a
+// sandboxed renderer process.
+class BlinkScrollbarPartAnimationTimer {
+ public:
+ BlinkScrollbarPartAnimationTimer(BlinkScrollbarPartAnimation* animation,
+ CFTimeInterval duration)
+ : timer_(Platform::Current()
+ ->MainThread()
+ ->Scheduler()
+ ->CompositorTaskRunner(),
+ this,
+ &BlinkScrollbarPartAnimationTimer::TimerFired),
+ start_time_(0.0),
+ duration_(duration),
+ animation_(animation),
+ timing_function_(CubicBezierTimingFunction::Preset(
+ CubicBezierTimingFunction::EaseType::EASE_IN_OUT)) {}
+
+ ~BlinkScrollbarPartAnimationTimer() {}
+
+ void Start() {
+ start_time_ = WTF::CurrentTime();
+ // Set the framerate of the animation. NSAnimation uses a default
+ // framerate of 60 Hz, so use that here.
+ timer_.StartRepeating(TimeDelta::FromSecondsD(1.0 / 60.0), FROM_HERE);
+ }
+
+ void Stop() { timer_.Stop(); }
+
+ void SetDuration(CFTimeInterval duration) { duration_ = duration; }
+
+ private:
+ void TimerFired(TimerBase*) {
+ double current_time = WTF::CurrentTime();
+ double delta = current_time - start_time_;
+
+ if (delta >= duration_)
+ timer_.Stop();
+
+ double fraction = delta / duration_;
+ fraction = clampTo(fraction, 0.0, 1.0);
+ double progress = timing_function_->Evaluate(fraction, 0.001);
+ [animation_ setCurrentProgress:progress];
+ }
+
+ TaskRunnerTimer<BlinkScrollbarPartAnimationTimer> timer_;
+ double start_time_; // In seconds.
+ double duration_; // In seconds.
+ BlinkScrollbarPartAnimation* animation_; // Weak, owns this.
+ scoped_refptr<CubicBezierTimingFunction> timing_function_;
+};
+
+} // namespace blink
+
+@interface BlinkScrollbarPartAnimation : NSObject {
+ blink::Scrollbar* _scrollbar;
+ std::unique_ptr<blink::BlinkScrollbarPartAnimationTimer> _timer;
+ RetainPtr<ScrollbarPainter> _scrollbarPainter;
+ FeatureToAnimate _featureToAnimate;
+ CGFloat _startValue;
+ CGFloat _endValue;
+}
+- (id)initWithScrollbar:(blink::Scrollbar*)scrollbar
+ featureToAnimate:(FeatureToAnimate)featureToAnimate
+ animateFrom:(CGFloat)startValue
+ animateTo:(CGFloat)endValue
+ duration:(NSTimeInterval)duration;
+@end
+
+@implementation BlinkScrollbarPartAnimation
+
+- (id)initWithScrollbar:(blink::Scrollbar*)scrollbar
+ featureToAnimate:(FeatureToAnimate)featureToAnimate
+ animateFrom:(CGFloat)startValue
+ animateTo:(CGFloat)endValue
+ duration:(NSTimeInterval)duration {
+ self = [super init];
+ if (!self)
+ return nil;
+
+ _timer =
+ std::make_unique<blink::BlinkScrollbarPartAnimationTimer>(self, duration);
+ _scrollbar = scrollbar;
+ _featureToAnimate = featureToAnimate;
+ _startValue = startValue;
+ _endValue = endValue;
+
+ return self;
+}
+
+- (void)startAnimation {
+ DCHECK(_scrollbar);
+
+ _scrollbarPainter = ScrollbarPainterForScrollbar(*_scrollbar);
+ _timer->Start();
+}
+
+- (void)stopAnimation {
+ _timer->Stop();
+}
+
+- (void)setDuration:(CFTimeInterval)duration {
+ _timer->SetDuration(duration);
+}
+
+- (void)setStartValue:(CGFloat)startValue {
+ _startValue = startValue;
+}
+
+- (void)setEndValue:(CGFloat)endValue {
+ _endValue = endValue;
+}
+
+- (void)setCurrentProgress:(NSAnimationProgress)progress {
+ DCHECK(_scrollbar);
+
+ CGFloat currentValue;
+ if (_startValue > _endValue)
+ currentValue = 1 - progress;
+ else
+ currentValue = progress;
+
+ blink::ScrollbarPart invalidParts = blink::kNoPart;
+ switch (_featureToAnimate) {
+ case ThumbAlpha:
+ [_scrollbarPainter.Get() setKnobAlpha:currentValue];
+ break;
+ case TrackAlpha:
+ [_scrollbarPainter.Get() setTrackAlpha:currentValue];
+ invalidParts = static_cast<blink::ScrollbarPart>(~blink::kThumbPart);
+ break;
+ case UIStateTransition:
+ [_scrollbarPainter.Get() setUiStateTransitionProgress:currentValue];
+ invalidParts = blink::kAllParts;
+ break;
+ case ExpansionTransition:
+ [_scrollbarPainter.Get() setExpansionTransitionProgress:currentValue];
+ invalidParts = blink::kThumbPart;
+ break;
+ }
+
+ _scrollbar->SetNeedsPaintInvalidation(invalidParts);
+}
+
+- (void)invalidate {
+ BEGIN_BLOCK_OBJC_EXCEPTIONS;
+ [self stopAnimation];
+ END_BLOCK_OBJC_EXCEPTIONS;
+ _scrollbar = 0;
+}
+
+@end
+
+@interface BlinkScrollbarPainterDelegate : NSObject<NSAnimationDelegate> {
+ blink::Scrollbar* _scrollbar;
+
+ RetainPtr<BlinkScrollbarPartAnimation> _knobAlphaAnimation;
+ RetainPtr<BlinkScrollbarPartAnimation> _trackAlphaAnimation;
+ RetainPtr<BlinkScrollbarPartAnimation> _uiStateTransitionAnimation;
+ RetainPtr<BlinkScrollbarPartAnimation> _expansionTransitionAnimation;
+ BOOL _hasExpandedSinceInvisible;
+}
+- (id)initWithScrollbar:(blink::Scrollbar*)scrollbar;
+- (void)updateVisibilityImmediately:(bool)show;
+- (void)cancelAnimations;
+@end
+
+@implementation BlinkScrollbarPainterDelegate
+
+- (id)initWithScrollbar:(blink::Scrollbar*)scrollbar {
+ self = [super init];
+ if (!self)
+ return nil;
+
+ _scrollbar = scrollbar;
+ return self;
+}
+
+- (void)updateVisibilityImmediately:(bool)show {
+ [self cancelAnimations];
+ [ScrollbarPainterForScrollbar(*_scrollbar) setKnobAlpha:(show ? 1.0 : 0.0)];
+}
+
+- (void)cancelAnimations {
+ BEGIN_BLOCK_OBJC_EXCEPTIONS;
+ [_knobAlphaAnimation.Get() stopAnimation];
+ [_trackAlphaAnimation.Get() stopAnimation];
+ [_uiStateTransitionAnimation.Get() stopAnimation];
+ [_expansionTransitionAnimation.Get() stopAnimation];
+ END_BLOCK_OBJC_EXCEPTIONS;
+}
+
+- (blink::ScrollAnimatorMac&)scrollAnimator {
+ return static_cast<blink::ScrollAnimatorMac&>(
+ _scrollbar->GetScrollableArea()->GetScrollAnimator());
+}
+
+- (NSRect)convertRectToBacking:(NSRect)aRect {
+ return aRect;
+}
+
+- (NSRect)convertRectFromBacking:(NSRect)aRect {
+ return aRect;
+}
+
+- (NSPoint)mouseLocationInScrollerForScrollerImp:(id)scrollerImp {
+ if (!_scrollbar)
+ return NSZeroPoint;
+
+ DCHECK_EQ(scrollerImp, ScrollbarPainterForScrollbar(*_scrollbar));
+
+ return _scrollbar->ConvertFromContainingEmbeddedContentView(
+ _scrollbar->GetScrollableArea()->LastKnownMousePosition());
+}
+
+- (void)setUpAlphaAnimation:
+ (RetainPtr<BlinkScrollbarPartAnimation>&)scrollbarPartAnimation
+ scrollerPainter:(ScrollbarPainter)scrollerPainter
+ part:(blink::ScrollbarPart)part
+ animateAlphaTo:(CGFloat)newAlpha
+ duration:(NSTimeInterval)duration {
+ // If the user has scrolled the page, then the scrollbars must be animated
+ // here.
+ // This overrides the early returns.
+ bool mustAnimate = [self scrollAnimator].HaveScrolledSincePageLoad();
+
+ if ([self scrollAnimator].ScrollbarPaintTimerIsActive() && !mustAnimate)
+ return;
+
+ if (_scrollbar->GetScrollableArea()->ShouldSuspendScrollAnimations() &&
+ !mustAnimate) {
+ [self scrollAnimator].StartScrollbarPaintTimer();
+ return;
+ }
+
+ // At this point, we are definitely going to animate now, so stop the timer.
+ [self scrollAnimator].StopScrollbarPaintTimer();
+
+ // If we are currently animating, stop
+ if (scrollbarPartAnimation) {
+ [scrollbarPartAnimation.Get() stopAnimation];
+ scrollbarPartAnimation = nullptr;
+ }
+
+ scrollbarPartAnimation.AdoptNS([[BlinkScrollbarPartAnimation alloc]
+ initWithScrollbar:_scrollbar
+ featureToAnimate:part == blink::kThumbPart ? ThumbAlpha : TrackAlpha
+ animateFrom:part == blink::kThumbPart ? [scrollerPainter knobAlpha]
+ : [scrollerPainter trackAlpha]
+ animateTo:newAlpha
+ duration:duration]);
+ [scrollbarPartAnimation.Get() startAnimation];
+}
+
+- (void)scrollerImp:(id)scrollerImp
+ animateKnobAlphaTo:(CGFloat)newKnobAlpha
+ duration:(NSTimeInterval)duration {
+ if (!_scrollbar)
+ return;
+
+ DCHECK_EQ(scrollerImp, ScrollbarPainterForScrollbar(*_scrollbar));
+
+ ScrollbarPainter scrollerPainter = (ScrollbarPainter)scrollerImp;
+ [self setUpAlphaAnimation:_knobAlphaAnimation
+ scrollerPainter:scrollerPainter
+ part:blink::kThumbPart
+ animateAlphaTo:newKnobAlpha
+ duration:duration];
+}
+
+- (void)scrollerImp:(id)scrollerImp
+ animateTrackAlphaTo:(CGFloat)newTrackAlpha
+ duration:(NSTimeInterval)duration {
+ if (!_scrollbar)
+ return;
+
+ DCHECK_EQ(scrollerImp, ScrollbarPainterForScrollbar(*_scrollbar));
+
+ ScrollbarPainter scrollerPainter = (ScrollbarPainter)scrollerImp;
+ [self setUpAlphaAnimation:_trackAlphaAnimation
+ scrollerPainter:scrollerPainter
+ part:blink::kBackTrackPart
+ animateAlphaTo:newTrackAlpha
+ duration:duration];
+}
+
+- (void)scrollerImp:(id)scrollerImp
+ animateUIStateTransitionWithDuration:(NSTimeInterval)duration {
+ if (!_scrollbar)
+ return;
+
+ if (!SupportsUIStateTransitionProgress())
+ return;
+
+ DCHECK_EQ(scrollerImp, ScrollbarPainterForScrollbar(*_scrollbar));
+
+ ScrollbarPainter scrollbarPainter = (ScrollbarPainter)scrollerImp;
+
+ // UIStateTransition always animates to 1. In case an animation is in progress
+ // this avoids a hard transition.
+ [scrollbarPainter
+ setUiStateTransitionProgress:1 - [scrollerImp uiStateTransitionProgress]];
+
+ if (!_uiStateTransitionAnimation)
+ _uiStateTransitionAnimation.AdoptNS([[BlinkScrollbarPartAnimation alloc]
+ initWithScrollbar:_scrollbar
+ featureToAnimate:UIStateTransition
+ animateFrom:[scrollbarPainter uiStateTransitionProgress]
+ animateTo:1.0
+ duration:duration]);
+ else {
+ // If we don't need to initialize the animation, just reset the values in
+ // case they have changed.
+ [_uiStateTransitionAnimation.Get()
+ setStartValue:[scrollbarPainter uiStateTransitionProgress]];
+ [_uiStateTransitionAnimation.Get() setEndValue:1.0];
+ [_uiStateTransitionAnimation.Get() setDuration:duration];
+ }
+ [_uiStateTransitionAnimation.Get() startAnimation];
+}
+
+- (void)scrollerImp:(id)scrollerImp
+ animateExpansionTransitionWithDuration:(NSTimeInterval)duration {
+ if (!_scrollbar)
+ return;
+
+ if (!SupportsExpansionTransitionProgress())
+ return;
+
+ DCHECK_EQ(scrollerImp, ScrollbarPainterForScrollbar(*_scrollbar));
+
+ ScrollbarPainter scrollbarPainter = (ScrollbarPainter)scrollerImp;
+
+ // ExpansionTransition always animates to 1. In case an animation is in
+ // progress this avoids a hard transition.
+ [scrollbarPainter
+ setExpansionTransitionProgress:1 -
+ [scrollerImp expansionTransitionProgress]];
+
+ if (!_expansionTransitionAnimation) {
+ _expansionTransitionAnimation.AdoptNS([[BlinkScrollbarPartAnimation alloc]
+ initWithScrollbar:_scrollbar
+ featureToAnimate:ExpansionTransition
+ animateFrom:[scrollbarPainter expansionTransitionProgress]
+ animateTo:1.0
+ duration:duration]);
+ } else {
+ // If we don't need to initialize the animation, just reset the values in
+ // case they have changed.
+ [_expansionTransitionAnimation.Get()
+ setStartValue:[scrollbarPainter uiStateTransitionProgress]];
+ [_expansionTransitionAnimation.Get() setEndValue:1.0];
+ [_expansionTransitionAnimation.Get() setDuration:duration];
+ }
+ [_expansionTransitionAnimation.Get() startAnimation];
+}
+
+- (void)scrollerImp:(id)scrollerImp
+ overlayScrollerStateChangedTo:(NSUInteger)newOverlayScrollerState {
+ // The names of these states are based on their observed behavior, and are not
+ // based on documentation.
+ enum {
+ NSScrollerStateInvisible = 0,
+ NSScrollerStateKnob = 1,
+ NSScrollerStateExpanded = 2
+ };
+ // We do not receive notifications about the thumb un-expanding when the
+ // scrollbar fades away. Ensure
+ // that we re-paint the thumb the next time that we transition away from being
+ // invisible, so that
+ // the thumb doesn't stick in an expanded state.
+ if (newOverlayScrollerState == NSScrollerStateExpanded) {
+ _hasExpandedSinceInvisible = YES;
+ } else if (newOverlayScrollerState != NSScrollerStateInvisible &&
+ _hasExpandedSinceInvisible) {
+ _scrollbar->SetNeedsPaintInvalidation(blink::kThumbPart);
+ _hasExpandedSinceInvisible = NO;
+ }
+}
+
+- (void)invalidate {
+ _scrollbar = 0;
+ BEGIN_BLOCK_OBJC_EXCEPTIONS;
+ [_knobAlphaAnimation.Get() invalidate];
+ [_trackAlphaAnimation.Get() invalidate];
+ [_uiStateTransitionAnimation.Get() invalidate];
+ [_expansionTransitionAnimation.Get() invalidate];
+ END_BLOCK_OBJC_EXCEPTIONS;
+}
+
+@end
+
+namespace blink {
+
+ScrollAnimatorBase* ScrollAnimatorBase::Create(
+ blink::ScrollableArea* scrollable_area) {
+ return new ScrollAnimatorMac(scrollable_area);
+}
+
+ScrollAnimatorMac::ScrollAnimatorMac(blink::ScrollableArea* scrollable_area)
+ : ScrollAnimatorBase(scrollable_area),
+ task_runner_(Platform::Current()
+ ->CurrentThread()
+ ->Scheduler()
+ ->CompositorTaskRunner()),
+ have_scrolled_since_page_load_(false),
+ needs_scroller_style_update_(false) {
+ scroll_animation_helper_delegate_.AdoptNS(
+ [[BlinkScrollAnimationHelperDelegate alloc] initWithScrollAnimator:this]);
+ scroll_animation_helper_.AdoptNS(
+ [[NSClassFromString(@"NSScrollAnimationHelper") alloc]
+ initWithDelegate:scroll_animation_helper_delegate_.Get()]);
+
+ scrollbar_painter_controller_delegate_.AdoptNS(
+ [[BlinkScrollbarPainterControllerDelegate alloc]
+ initWithScrollableArea:scrollable_area]);
+ scrollbar_painter_controller_ =
+ [[[NSClassFromString(@"NSScrollerImpPair") alloc] init] autorelease];
+ [scrollbar_painter_controller_.Get()
+ performSelector:@selector(setDelegate:)
+ withObject:scrollbar_painter_controller_delegate_.Get()];
+ [scrollbar_painter_controller_.Get()
+ setScrollerStyle:ScrollbarThemeMac::RecommendedScrollerStyle()];
+}
+
+ScrollAnimatorMac::~ScrollAnimatorMac() {}
+
+void ScrollAnimatorMac::Dispose() {
+ BEGIN_BLOCK_OBJC_EXCEPTIONS;
+ ScrollbarPainter horizontal_scrollbar_painter =
+ [scrollbar_painter_controller_.Get() horizontalScrollerImp];
+ [horizontal_scrollbar_painter setDelegate:nil];
+
+ ScrollbarPainter vertical_scrollbar_painter =
+ [scrollbar_painter_controller_.Get() verticalScrollerImp];
+ [vertical_scrollbar_painter setDelegate:nil];
+
+ [scrollbar_painter_controller_delegate_.Get() invalidate];
+ [scrollbar_painter_controller_.Get() setDelegate:nil];
+ [horizontal_scrollbar_painter_delegate_.Get() invalidate];
+ [vertical_scrollbar_painter_delegate_.Get() invalidate];
+ [scroll_animation_helper_delegate_.Get() invalidate];
+ END_BLOCK_OBJC_EXCEPTIONS;
+
+ initial_scrollbar_paint_task_handle_.Cancel();
+ send_content_area_scrolled_task_handle_.Cancel();
+}
+
+ScrollResult ScrollAnimatorMac::UserScroll(ScrollGranularity granularity,
+ const ScrollOffset& delta) {
+ have_scrolled_since_page_load_ = true;
+
+ if (!scrollable_area_->ScrollAnimatorEnabled())
+ return ScrollAnimatorBase::UserScroll(granularity, delta);
+
+ if (granularity == kScrollByPixel || granularity == kScrollByPrecisePixel)
+ return ScrollAnimatorBase::UserScroll(granularity, delta);
+
+ ScrollOffset consumed_delta = ComputeDeltaToConsume(delta);
+ ScrollOffset new_offset = current_offset_ + consumed_delta;
+ if (current_offset_ == new_offset)
+ return ScrollResult();
+
+ // Prevent clobbering an existing animation on an unscrolled axis.
+ if ([scroll_animation_helper_.Get() _isAnimating]) {
+ NSPoint target_origin = [scroll_animation_helper_.Get() targetOrigin];
+ if (!delta.Width())
+ new_offset.SetWidth(target_origin.x);
+ if (!delta.Height())
+ new_offset.SetHeight(target_origin.y);
+ }
+
+ NSPoint new_point = NSMakePoint(new_offset.Width(), new_offset.Height());
+ [scroll_animation_helper_.Get() scrollToPoint:new_point];
+
+ // TODO(bokan): This has different semantics on ScrollResult than
+ // ScrollAnimator,
+ // which only returns unused delta if there's no animation and we don't start
+ // one.
+ return ScrollResult(consumed_delta.Width(), consumed_delta.Height(),
+ delta.Width() - consumed_delta.Width(),
+ delta.Height() - consumed_delta.Height());
+}
+
+void ScrollAnimatorMac::ScrollToOffsetWithoutAnimation(
+ const ScrollOffset& offset) {
+ [scroll_animation_helper_.Get() _stopRun];
+ ImmediateScrollTo(offset);
+}
+
+ScrollOffset ScrollAnimatorMac::AdjustScrollOffsetIfNecessary(
+ const ScrollOffset& offset) const {
+ ScrollOffset min_offset = scrollable_area_->MinimumScrollOffset();
+ ScrollOffset max_offset = scrollable_area_->MaximumScrollOffset();
+
+ float new_x = clampTo<float, float>(offset.Width(), min_offset.Width(),
+ max_offset.Width());
+ float new_y = clampTo<float, float>(offset.Height(), min_offset.Height(),
+ max_offset.Height());
+
+ return ScrollOffset(new_x, new_y);
+}
+
+void ScrollAnimatorMac::ImmediateScrollTo(const ScrollOffset& new_offset) {
+ ScrollOffset adjusted_offset = AdjustScrollOffsetIfNecessary(new_offset);
+
+ bool offset_changed = adjusted_offset != current_offset_;
+ if (!offset_changed && !GetScrollableArea()->ScrollOriginChanged())
+ return;
+
+ ScrollOffset delta = adjusted_offset - current_offset_;
+
+ current_offset_ = adjusted_offset;
+ NotifyContentAreaScrolled(delta, kUserScroll);
+ NotifyOffsetChanged();
+}
+
+void ScrollAnimatorMac::ImmediateScrollToOffsetForScrollAnimation(
+ const ScrollOffset& new_offset) {
+ DCHECK(scroll_animation_helper_);
+ ImmediateScrollTo(new_offset);
+}
+
+void ScrollAnimatorMac::ContentAreaWillPaint() const {
+ if (!GetScrollableArea()->ScrollbarsCanBeActive())
+ return;
+ [scrollbar_painter_controller_.Get() contentAreaWillDraw];
+}
+
+void ScrollAnimatorMac::MouseEnteredContentArea() const {
+ if (!GetScrollableArea()->ScrollbarsCanBeActive())
+ return;
+ [scrollbar_painter_controller_.Get() mouseEnteredContentArea];
+}
+
+void ScrollAnimatorMac::MouseExitedContentArea() const {
+ if (!GetScrollableArea()->ScrollbarsCanBeActive())
+ return;
+ [scrollbar_painter_controller_.Get() mouseExitedContentArea];
+}
+
+void ScrollAnimatorMac::MouseMovedInContentArea() const {
+ if (!GetScrollableArea()->ScrollbarsCanBeActive())
+ return;
+ [scrollbar_painter_controller_.Get() mouseMovedInContentArea];
+}
+
+void ScrollAnimatorMac::MouseEnteredScrollbar(Scrollbar& scrollbar) const {
+ if (!GetScrollableArea()->ScrollbarsCanBeActive())
+ return;
+
+ if (!SupportsUIStateTransitionProgress())
+ return;
+ if (ScrollbarPainter painter = ScrollbarPainterForScrollbar(scrollbar))
+ [painter mouseEnteredScroller];
+}
+
+void ScrollAnimatorMac::MouseExitedScrollbar(Scrollbar& scrollbar) const {
+ if (!GetScrollableArea()->ScrollbarsCanBeActive())
+ return;
+
+ if (!SupportsUIStateTransitionProgress())
+ return;
+ if (ScrollbarPainter painter = ScrollbarPainterForScrollbar(scrollbar))
+ [painter mouseExitedScroller];
+}
+
+void ScrollAnimatorMac::ContentsResized() const {
+ if (!GetScrollableArea()->ScrollbarsCanBeActive())
+ return;
+ [scrollbar_painter_controller_.Get() contentAreaDidResize];
+}
+
+void ScrollAnimatorMac::ContentAreaDidShow() const {
+ if (!GetScrollableArea()->ScrollbarsCanBeActive())
+ return;
+ [scrollbar_painter_controller_.Get() windowOrderedIn];
+}
+
+void ScrollAnimatorMac::ContentAreaDidHide() const {
+ if (!GetScrollableArea()->ScrollbarsCanBeActive())
+ return;
+ [scrollbar_painter_controller_.Get() windowOrderedOut];
+}
+
+void ScrollAnimatorMac::FinishCurrentScrollAnimations() {
+ [scrollbar_painter_controller_.Get() hideOverlayScrollers];
+}
+
+void ScrollAnimatorMac::DidAddVerticalScrollbar(Scrollbar& scrollbar) {
+ ScrollbarPainter painter = ScrollbarPainterForScrollbar(scrollbar);
+ if (!painter)
+ return;
+
+ DCHECK(!vertical_scrollbar_painter_delegate_);
+ vertical_scrollbar_painter_delegate_.AdoptNS(
+ [[BlinkScrollbarPainterDelegate alloc] initWithScrollbar:&scrollbar]);
+
+ [painter setDelegate:vertical_scrollbar_painter_delegate_.Get()];
+ [scrollbar_painter_controller_.Get() setVerticalScrollerImp:painter];
+}
+
+void ScrollAnimatorMac::WillRemoveVerticalScrollbar(Scrollbar& scrollbar) {
+ ScrollbarPainter painter = ScrollbarPainterForScrollbar(scrollbar);
+ DCHECK_EQ([scrollbar_painter_controller_.Get() verticalScrollerImp], painter);
+ if (!painter)
+ return;
+
+ DCHECK(vertical_scrollbar_painter_delegate_);
+ [painter setDelegate:nil];
+ [vertical_scrollbar_painter_delegate_.Get() invalidate];
+ vertical_scrollbar_painter_delegate_ = nullptr;
+ [scrollbar_painter_controller_.Get() setVerticalScrollerImp:nil];
+}
+
+void ScrollAnimatorMac::DidAddHorizontalScrollbar(Scrollbar& scrollbar) {
+ ScrollbarPainter painter = ScrollbarPainterForScrollbar(scrollbar);
+ if (!painter)
+ return;
+
+ DCHECK(!horizontal_scrollbar_painter_delegate_);
+ horizontal_scrollbar_painter_delegate_.AdoptNS(
+ [[BlinkScrollbarPainterDelegate alloc] initWithScrollbar:&scrollbar]);
+
+ [painter setDelegate:horizontal_scrollbar_painter_delegate_.Get()];
+ [scrollbar_painter_controller_.Get() setHorizontalScrollerImp:painter];
+}
+
+void ScrollAnimatorMac::WillRemoveHorizontalScrollbar(Scrollbar& scrollbar) {
+ ScrollbarPainter painter = ScrollbarPainterForScrollbar(scrollbar);
+ DCHECK_EQ([scrollbar_painter_controller_.Get() horizontalScrollerImp],
+ painter);
+ if (!painter)
+ return;
+
+ DCHECK(horizontal_scrollbar_painter_delegate_);
+ [painter setDelegate:nil];
+ [horizontal_scrollbar_painter_delegate_.Get() invalidate];
+ horizontal_scrollbar_painter_delegate_ = nullptr;
+ [scrollbar_painter_controller_.Get() setHorizontalScrollerImp:nil];
+}
+
+void ScrollAnimatorMac::NotifyContentAreaScrolled(const ScrollOffset& delta,
+ ScrollType scrollType) {
+ // This function is called when a page is going into the page cache, but the
+ // page
+ // isn't really scrolling in that case. We should only pass the message on to
+ // the
+ // ScrollbarPainterController when we're really scrolling on an active page.
+ if (IsExplicitScrollType(scrollType) &&
+ GetScrollableArea()->ScrollbarsCanBeActive())
+ SendContentAreaScrolledSoon(delta);
+}
+
+bool ScrollAnimatorMac::SetScrollbarsVisibleForTesting(bool show) {
+ if (show)
+ [scrollbar_painter_controller_.Get() flashScrollers];
+ else
+ [scrollbar_painter_controller_.Get() hideOverlayScrollers];
+
+ [vertical_scrollbar_painter_delegate_.Get() updateVisibilityImmediately:show];
+ [horizontal_scrollbar_painter_delegate_.Get()
+ updateVisibilityImmediately:show];
+ return true;
+}
+
+void ScrollAnimatorMac::CancelAnimation() {
+ [scroll_animation_helper_.Get() _stopRun];
+ have_scrolled_since_page_load_ = false;
+}
+
+void ScrollAnimatorMac::UpdateScrollerStyle() {
+ if (!GetScrollableArea()->ScrollbarsCanBeActive()) {
+ needs_scroller_style_update_ = true;
+ return;
+ }
+
+ blink::ScrollbarThemeMac* mac_theme =
+ MacOverlayScrollbarTheme(scrollable_area_->GetPageScrollbarTheme());
+ if (!mac_theme) {
+ needs_scroller_style_update_ = false;
+ return;
+ }
+
+ NSScrollerStyle new_style =
+ [scrollbar_painter_controller_.Get() scrollerStyle];
+
+ if (Scrollbar* vertical_scrollbar =
+ GetScrollableArea()->VerticalScrollbar()) {
+ vertical_scrollbar->SetNeedsPaintInvalidation(kAllParts);
+
+ ScrollbarPainter old_vertical_painter =
+ [scrollbar_painter_controller_.Get() verticalScrollerImp];
+ ScrollbarPainter new_vertical_painter = [NSClassFromString(@"NSScrollerImp")
+ scrollerImpWithStyle:new_style
+ controlSize:(NSControlSize)vertical_scrollbar->GetControlSize()
+ horizontal:NO
+ replacingScrollerImp:old_vertical_painter];
+ [old_vertical_painter setDelegate:nil];
+ [new_vertical_painter
+ setDelegate:vertical_scrollbar_painter_delegate_.Get()];
+ [scrollbar_painter_controller_.Get()
+ setVerticalScrollerImp:new_vertical_painter];
+ mac_theme->SetNewPainterForScrollbar(*vertical_scrollbar,
+ new_vertical_painter);
+
+ // The different scrollbar styles have different thicknesses, so we must
+ // re-set the
+ // frameRect to the new thickness, and the re-layout below will ensure the
+ // offset
+ // and length are properly updated.
+ int thickness =
+ mac_theme->ScrollbarThickness(vertical_scrollbar->GetControlSize());
+ vertical_scrollbar->SetFrameRect(IntRect(0, 0, thickness, thickness));
+ }
+
+ if (Scrollbar* horizontal_scrollbar =
+ GetScrollableArea()->HorizontalScrollbar()) {
+ horizontal_scrollbar->SetNeedsPaintInvalidation(kAllParts);
+
+ ScrollbarPainter old_horizontal_painter =
+ [scrollbar_painter_controller_.Get() horizontalScrollerImp];
+ ScrollbarPainter new_horizontal_painter =
+ [NSClassFromString(@"NSScrollerImp")
+ scrollerImpWithStyle:new_style
+ controlSize:(NSControlSize)
+ horizontal_scrollbar->GetControlSize()
+ horizontal:YES
+ replacingScrollerImp:old_horizontal_painter];
+ [old_horizontal_painter setDelegate:nil];
+ [new_horizontal_painter
+ setDelegate:horizontal_scrollbar_painter_delegate_.Get()];
+ [scrollbar_painter_controller_.Get()
+ setHorizontalScrollerImp:new_horizontal_painter];
+ mac_theme->SetNewPainterForScrollbar(*horizontal_scrollbar,
+ new_horizontal_painter);
+
+ // The different scrollbar styles have different thicknesses, so we must
+ // re-set the
+ // frameRect to the new thickness, and the re-layout below will ensure the
+ // offset
+ // and length are properly updated.
+ int thickness =
+ mac_theme->ScrollbarThickness(horizontal_scrollbar->GetControlSize());
+ horizontal_scrollbar->SetFrameRect(IntRect(0, 0, thickness, thickness));
+ }
+
+ // If m_needsScrollerStyleUpdate is true, then the page is restoring from the
+ // page cache, and
+ // a relayout will happen on its own. Otherwise, we must initiate a re-layout
+ // ourselves.
+ if (!needs_scroller_style_update_)
+ GetScrollableArea()->ScrollbarStyleChanged();
+
+ needs_scroller_style_update_ = false;
+}
+
+void ScrollAnimatorMac::StartScrollbarPaintTimer() {
+ // Post a task with 1 ms delay to give a chance to run other immediate tasks
+ // that may cancel this.
+ initial_scrollbar_paint_task_handle_ = PostDelayedCancellableTask(
+ *task_runner_, FROM_HERE,
+ WTF::Bind(&ScrollAnimatorMac::InitialScrollbarPaintTask,
+ WrapWeakPersistent(this)),
+ TimeDelta::FromMilliseconds(1));
+}
+
+bool ScrollAnimatorMac::ScrollbarPaintTimerIsActive() const {
+ return initial_scrollbar_paint_task_handle_.IsActive();
+}
+
+void ScrollAnimatorMac::StopScrollbarPaintTimer() {
+ initial_scrollbar_paint_task_handle_.Cancel();
+}
+
+void ScrollAnimatorMac::InitialScrollbarPaintTask() {
+ // To force the scrollbars to flash, we have to call hide first. Otherwise,
+ // the ScrollbarPainterController
+ // might think that the scrollbars are already showing and bail early.
+ [scrollbar_painter_controller_.Get() hideOverlayScrollers];
+ [scrollbar_painter_controller_.Get() flashScrollers];
+}
+
+void ScrollAnimatorMac::SendContentAreaScrolledSoon(const ScrollOffset& delta) {
+ content_area_scrolled_timer_scroll_delta_ = delta;
+
+ if (send_content_area_scrolled_task_handle_.IsActive())
+ return;
+ send_content_area_scrolled_task_handle_ = PostCancellableTask(
+ *task_runner_, FROM_HERE,
+ WTF::Bind(&ScrollAnimatorMac::SendContentAreaScrolledTask,
+ WrapWeakPersistent(this)));
+}
+
+void ScrollAnimatorMac::SendContentAreaScrolledTask() {
+ if (SupportsContentAreaScrolledInDirection()) {
+ [scrollbar_painter_controller_.Get()
+ contentAreaScrolledInDirection:
+ NSMakePoint(content_area_scrolled_timer_scroll_delta_.Width(),
+ content_area_scrolled_timer_scroll_delta_.Height())];
+ content_area_scrolled_timer_scroll_delta_ = ScrollOffset();
+ } else
+ [scrollbar_painter_controller_.Get() contentAreaScrolled];
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/mac/theme_mac.h b/chromium/third_party/blink/renderer/platform/mac/theme_mac.h
new file mode 100644
index 00000000000..3a974d308ee
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mac/theme_mac.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2008 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_MAC_THEME_MAC_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MAC_THEME_MAC_H_
+
+#import <AppKit/AppKit.h>
+
+#include "third_party/blink/renderer/platform/theme.h"
+
+namespace blink {
+
+class ThemeMac : public Theme {
+ public:
+ ThemeMac() {}
+ ~ThemeMac() override {}
+
+ virtual int BaselinePositionAdjustment(ControlPart) const;
+
+ virtual FontDescription ControlFont(ControlPart,
+ const FontDescription&,
+ float zoom_factor) const;
+
+ virtual LengthSize GetControlSize(ControlPart,
+ const FontDescription&,
+ const LengthSize&,
+ float zoom_factor) const;
+ virtual LengthSize MinimumControlSize(ControlPart,
+ const FontDescription&,
+ float zoom_factor) const;
+
+ virtual LengthBox ControlPadding(ControlPart,
+ const FontDescription&,
+ const Length& zoomed_box_top,
+ const Length& zoomed_box_right,
+ const Length& zoomed_box_bottom,
+ const Length& zoomed_box_left,
+ float zoom_factor) const;
+ virtual LengthBox ControlBorder(ControlPart,
+ const FontDescription&,
+ const LengthBox& zoomed_box,
+ float zoom_factor) const;
+
+ virtual bool ControlRequiresPreWhiteSpace(ControlPart part) const {
+ return part == kPushButtonPart;
+ }
+
+ virtual void AddVisualOverflow(ControlPart,
+ ControlStates,
+ float zoom_factor,
+ IntRect& border_box) const;
+
+ // Inflate an IntRect to accout for specific padding around margins.
+ enum { kTopMargin = 0, kRightMargin = 1, kBottomMargin = 2, kLeftMargin = 3 };
+ static PLATFORM_EXPORT IntRect InflateRect(const IntRect&,
+ const IntSize&,
+ const int* margins,
+ float zoom_level = 1.0f);
+
+ // Inflate an IntRect to account for any bleeding that would happen due to
+ // anti-aliasing.
+ static PLATFORM_EXPORT IntRect InflateRectForAA(const IntRect&);
+
+ // Inflate an IntRect to account for its focus ring.
+ // TODO: Consider using computing the focus ring's bounds with
+ // -[NSCell focusRingMaskBoundsForFrame:inView:]).
+ static PLATFORM_EXPORT IntRect InflateRectForFocusRing(const IntRect&);
+
+ static PLATFORM_EXPORT LengthSize CheckboxSize(const FontDescription&,
+ const LengthSize& zoomed_size,
+ float zoom_factor);
+ static PLATFORM_EXPORT NSButtonCell* Checkbox(ControlStates,
+ const IntRect& zoomed_rect,
+ float zoom_factor);
+ static PLATFORM_EXPORT const IntSize* CheckboxSizes();
+ static PLATFORM_EXPORT const int* CheckboxMargins(NSControlSize);
+ static PLATFORM_EXPORT NSView* EnsuredView(ScrollableArea*);
+
+ static PLATFORM_EXPORT const IntSize* RadioSizes();
+ static PLATFORM_EXPORT const int* RadioMargins(NSControlSize);
+ static PLATFORM_EXPORT LengthSize RadioSize(const FontDescription&,
+ const LengthSize& zoomed_size,
+ float zoom_factor);
+ static PLATFORM_EXPORT NSButtonCell* Radio(ControlStates,
+ const IntRect& zoomed_rect,
+ float zoom_factor);
+
+ static PLATFORM_EXPORT const IntSize* ButtonSizes();
+ static PLATFORM_EXPORT const int* ButtonMargins(NSControlSize);
+ static PLATFORM_EXPORT NSButtonCell* Button(ControlPart,
+ ControlStates,
+ const IntRect& zoomed_rect,
+ float zoom_factor);
+
+ static PLATFORM_EXPORT NSControlSize
+ ControlSizeFromPixelSize(const IntSize* sizes,
+ const IntSize& min_zoomed_size,
+ float zoom_factor);
+ static PLATFORM_EXPORT const IntSize* StepperSizes();
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MAC_THEME_MAC_H_
diff --git a/chromium/third_party/blink/renderer/platform/mac/theme_mac.mm b/chromium/third_party/blink/renderer/platform/mac/theme_mac.mm
new file mode 100644
index 00000000000..2fb60f36bdf
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mac/theme_mac.mm
@@ -0,0 +1,558 @@
+/*
+ * Copyright (C) 2008, 2010, 2011, 2012 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 "third_party/blink/renderer/platform/mac/theme_mac.h"
+
+#import <Carbon/Carbon.h>
+#import "third_party/blink/renderer/platform/graphics/graphics_context_state_saver.h"
+#import "third_party/blink/renderer/platform/mac/block_exceptions.h"
+#import "third_party/blink/renderer/platform/mac/local_current_graphics_context.h"
+#import "third_party/blink/renderer/platform/mac/version_util_mac.h"
+#import "third_party/blink/renderer/platform/mac/web_core_ns_cell_extras.h"
+#import "third_party/blink/renderer/platform/scroll/scrollable_area.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+
+// This is a view whose sole purpose is to tell AppKit that it's flipped.
+@interface BlinkFlippedControl : NSControl
+@end
+
+@implementation BlinkFlippedControl
+
+- (BOOL)isFlipped {
+ return YES;
+}
+
+- (NSText*)currentEditor {
+ return nil;
+}
+
+- (BOOL)_automaticFocusRingDisabled {
+ return YES;
+}
+
+@end
+
+namespace blink {
+
+Theme* PlatformTheme() {
+ DEFINE_STATIC_LOCAL(ThemeMac, theme_mac, ());
+ return &theme_mac;
+}
+
+// Helper functions used by a bunch of different control parts.
+
+static NSControlSize ControlSizeForFont(
+ const FontDescription& font_description) {
+ int font_size = font_description.ComputedPixelSize();
+ if (font_size >= 16)
+ return NSRegularControlSize;
+ if (font_size >= 11)
+ return NSSmallControlSize;
+ return NSMiniControlSize;
+}
+
+static LengthSize SizeFromNSControlSize(NSControlSize ns_control_size,
+ const LengthSize& zoomed_size,
+ float zoom_factor,
+ const IntSize* sizes) {
+ IntSize control_size = sizes[ns_control_size];
+ if (zoom_factor != 1.0f)
+ control_size = IntSize(control_size.Width() * zoom_factor,
+ control_size.Height() * zoom_factor);
+ LengthSize result = zoomed_size;
+ if (zoomed_size.Width().IsIntrinsicOrAuto() && control_size.Width() > 0)
+ result.SetWidth(Length(control_size.Width(), kFixed));
+ if (zoomed_size.Height().IsIntrinsicOrAuto() && control_size.Height() > 0)
+ result.SetHeight(Length(control_size.Height(), kFixed));
+ return result;
+}
+
+static LengthSize SizeFromFont(const FontDescription& font_description,
+ const LengthSize& zoomed_size,
+ float zoom_factor,
+ const IntSize* sizes) {
+ return SizeFromNSControlSize(ControlSizeForFont(font_description),
+ zoomed_size, zoom_factor, sizes);
+}
+
+NSControlSize ThemeMac::ControlSizeFromPixelSize(const IntSize* sizes,
+ const IntSize& min_zoomed_size,
+ float zoom_factor) {
+ if (min_zoomed_size.Width() >=
+ static_cast<int>(sizes[NSRegularControlSize].Width() * zoom_factor) &&
+ min_zoomed_size.Height() >=
+ static_cast<int>(sizes[NSRegularControlSize].Height() * zoom_factor))
+ return NSRegularControlSize;
+ if (min_zoomed_size.Width() >=
+ static_cast<int>(sizes[NSSmallControlSize].Width() * zoom_factor) &&
+ min_zoomed_size.Height() >=
+ static_cast<int>(sizes[NSSmallControlSize].Height() * zoom_factor))
+ return NSSmallControlSize;
+ return NSMiniControlSize;
+}
+
+static void SetControlSize(NSCell* cell,
+ const IntSize* sizes,
+ const IntSize& min_zoomed_size,
+ float zoom_factor) {
+ ControlSize size =
+ ThemeMac::ControlSizeFromPixelSize(sizes, min_zoomed_size, zoom_factor);
+ // Only update if we have to, since AppKit does work even if the size is the
+ // same.
+ if (size != [cell controlSize])
+ [cell setControlSize:(NSControlSize)size];
+}
+
+static void UpdateStates(NSCell* cell, ControlStates states) {
+ // Hover state is not supported by Aqua.
+
+ // Pressed state
+ bool old_pressed = [cell isHighlighted];
+ bool pressed = states & kPressedControlState;
+ if (pressed != old_pressed)
+ [cell setHighlighted:pressed];
+
+ // Enabled state
+ bool old_enabled = [cell isEnabled];
+ bool enabled = states & kEnabledControlState;
+ if (enabled != old_enabled)
+ [cell setEnabled:enabled];
+
+ // Checked and Indeterminate
+ bool old_indeterminate = [cell state] == NSMixedState;
+ bool indeterminate = (states & kIndeterminateControlState);
+ bool checked = states & kCheckedControlState;
+ bool old_checked = [cell state] == NSOnState;
+ if (old_indeterminate != indeterminate || checked != old_checked)
+ [cell setState:indeterminate ? NSMixedState
+ : (checked ? NSOnState : NSOffState)];
+
+ // Window inactive state does not need to be checked explicitly, since we
+ // paint parented to a view in a window whose key state can be detected.
+}
+
+// Return a fake NSView whose sole purpose is to tell AppKit that it's flipped.
+NSView* ThemeMac::EnsuredView(ScrollableArea* scrollable_area) {
+ // Use a fake flipped view.
+ static NSView* flipped_view = [[BlinkFlippedControl alloc] init];
+ [flipped_view
+ setFrameSize:NSSizeFromCGSize(CGSize(scrollable_area->ContentsSize()))];
+
+ return flipped_view;
+}
+
+// static
+IntRect ThemeMac::InflateRect(const IntRect& zoomed_rect,
+ const IntSize& zoomed_size,
+ const int* margins,
+ float zoom_factor) {
+ // Only do the inflation if the available width/height are too small.
+ // Otherwise try to fit the glow/check space into the available box's
+ // width/height.
+ int width_delta = zoomed_rect.Width() -
+ (zoomed_size.Width() + margins[kLeftMargin] * zoom_factor +
+ margins[kRightMargin] * zoom_factor);
+ int height_delta = zoomed_rect.Height() -
+ (zoomed_size.Height() + margins[kTopMargin] * zoom_factor +
+ margins[kBottomMargin] * zoom_factor);
+ IntRect result(zoomed_rect);
+ if (width_delta < 0) {
+ result.SetX(result.X() - margins[kLeftMargin] * zoom_factor);
+ result.SetWidth(result.Width() - width_delta);
+ }
+ if (height_delta < 0) {
+ result.SetY(result.Y() - margins[kTopMargin] * zoom_factor);
+ result.SetHeight(result.Height() - height_delta);
+ }
+ return result;
+}
+
+// static
+IntRect ThemeMac::InflateRectForAA(const IntRect& rect) {
+ const int kMargin = 2;
+ return IntRect(rect.X() - kMargin, rect.Y() - kMargin,
+ rect.Width() + 2 * kMargin, rect.Height() + 2 * kMargin);
+}
+
+// static
+IntRect ThemeMac::InflateRectForFocusRing(const IntRect& rect) {
+ // Just put a margin of 16 units around the rect. The UI elements that use
+ // this don't appropriately scale their focus rings appropriately (e.g, paint
+ // pickers), or switch to non-native widgets when scaled (e.g, check boxes
+ // and radio buttons).
+ const int kMargin = 16;
+ IntRect result;
+ result.SetX(rect.X() - kMargin);
+ result.SetY(rect.Y() - kMargin);
+ result.SetWidth(rect.Width() + 2 * kMargin);
+ result.SetHeight(rect.Height() + 2 * kMargin);
+ return result;
+}
+
+// Checkboxes
+
+const IntSize* ThemeMac::CheckboxSizes() {
+ static const IntSize kSizes[3] = {IntSize(14, 14), IntSize(12, 12),
+ IntSize(10, 10)};
+ return kSizes;
+}
+
+const int* ThemeMac::CheckboxMargins(NSControlSize control_size) {
+ static const int kMargins[3][4] = {
+ {3, 4, 4, 2}, {4, 3, 3, 3}, {4, 3, 3, 3},
+ };
+ return kMargins[control_size];
+}
+
+LengthSize ThemeMac::CheckboxSize(const FontDescription& font_description,
+ const LengthSize& zoomed_size,
+ float zoom_factor) {
+ // If the width and height are both specified, then we have nothing to do.
+ if (!zoomed_size.Width().IsIntrinsicOrAuto() &&
+ !zoomed_size.Height().IsIntrinsicOrAuto())
+ return zoomed_size;
+
+ // Use the font size to determine the intrinsic width of the control.
+ return SizeFromFont(font_description, zoomed_size, zoom_factor,
+ CheckboxSizes());
+}
+
+NSButtonCell* ThemeMac::Checkbox(ControlStates states,
+ const IntRect& zoomed_rect,
+ float zoom_factor) {
+ static NSButtonCell* checkbox_cell;
+ if (!checkbox_cell) {
+ checkbox_cell = [[NSButtonCell alloc] init];
+ [checkbox_cell setButtonType:NSSwitchButton];
+ [checkbox_cell setTitle:nil];
+ [checkbox_cell setAllowsMixedState:YES];
+ [checkbox_cell setFocusRingType:NSFocusRingTypeExterior];
+ }
+
+ // Set the control size based off the rectangle we're painting into.
+ SetControlSize(checkbox_cell, CheckboxSizes(), zoomed_rect.Size(),
+ zoom_factor);
+
+ // Update the various states we respond to.
+ UpdateStates(checkbox_cell, states);
+
+ return checkbox_cell;
+}
+
+const IntSize* ThemeMac::RadioSizes() {
+ static const IntSize kSizes[3] = {IntSize(14, 15), IntSize(12, 13),
+ IntSize(10, 10)};
+ return kSizes;
+}
+
+const int* ThemeMac::RadioMargins(NSControlSize control_size) {
+ static const int kMargins[3][4] = {
+ {2, 2, 4, 2}, {3, 2, 3, 2}, {1, 0, 2, 0},
+ };
+ return kMargins[control_size];
+}
+
+LengthSize ThemeMac::RadioSize(const FontDescription& font_description,
+ const LengthSize& zoomed_size,
+ float zoom_factor) {
+ // If the width and height are both specified, then we have nothing to do.
+ if (!zoomed_size.Width().IsIntrinsicOrAuto() &&
+ !zoomed_size.Height().IsIntrinsicOrAuto())
+ return zoomed_size;
+
+ // Use the font size to determine the intrinsic width of the control.
+ return SizeFromFont(font_description, zoomed_size, zoom_factor, RadioSizes());
+}
+
+NSButtonCell* ThemeMac::Radio(ControlStates states,
+ const IntRect& zoomed_rect,
+ float zoom_factor) {
+ static NSButtonCell* radio_cell;
+ if (!radio_cell) {
+ radio_cell = [[NSButtonCell alloc] init];
+ [radio_cell setButtonType:NSRadioButton];
+ [radio_cell setTitle:nil];
+ [radio_cell setFocusRingType:NSFocusRingTypeExterior];
+ }
+
+ // Set the control size based off the rectangle we're painting into.
+ SetControlSize(radio_cell, RadioSizes(), zoomed_rect.Size(), zoom_factor);
+
+ // Update the various states we respond to.
+ // Cocoa draws NSMixedState NSRadioButton as NSOnState so we don't want that.
+ states &= ~kIndeterminateControlState;
+ UpdateStates(radio_cell, states);
+
+ return radio_cell;
+}
+
+// Buttons really only constrain height. They respect width.
+const IntSize* ThemeMac::ButtonSizes() {
+ static const IntSize kSizes[3] = {IntSize(0, 21), IntSize(0, 18),
+ IntSize(0, 15)};
+ return kSizes;
+}
+
+const int* ThemeMac::ButtonMargins(NSControlSize control_size) {
+ static const int kMargins[3][4] = {
+ {4, 6, 7, 6}, {4, 5, 6, 5}, {0, 1, 1, 1},
+ };
+ return kMargins[control_size];
+}
+
+static void SetUpButtonCell(NSButtonCell* cell,
+ ControlPart part,
+ ControlStates states,
+ const IntRect& zoomed_rect,
+ float zoom_factor) {
+ // Set the control size based off the rectangle we're painting into.
+ const IntSize* sizes = ThemeMac::ButtonSizes();
+ if (part == kSquareButtonPart ||
+ zoomed_rect.Height() >
+ ThemeMac::ButtonSizes()[NSRegularControlSize].Height() *
+ zoom_factor) {
+ // Use the square button
+ if ([cell bezelStyle] != NSShadowlessSquareBezelStyle)
+ [cell setBezelStyle:NSShadowlessSquareBezelStyle];
+ } else if ([cell bezelStyle] != NSRoundedBezelStyle)
+ [cell setBezelStyle:NSRoundedBezelStyle];
+
+ SetControlSize(cell, sizes, zoomed_rect.Size(), zoom_factor);
+
+ // Update the various states we respond to.
+ UpdateStates(cell, states);
+}
+
+NSButtonCell* ThemeMac::Button(ControlPart part,
+ ControlStates states,
+ const IntRect& zoomed_rect,
+ float zoom_factor) {
+ static NSButtonCell* cell = nil;
+ if (!cell) {
+ cell = [[NSButtonCell alloc] init];
+ [cell setTitle:nil];
+ [cell setButtonType:NSMomentaryPushInButton];
+ }
+ SetUpButtonCell(cell, part, states, zoomed_rect, zoom_factor);
+ return cell;
+}
+
+const IntSize* ThemeMac::StepperSizes() {
+ static const IntSize kSizes[3] = {IntSize(19, 27), IntSize(15, 22),
+ IntSize(13, 15)};
+ return kSizes;
+}
+
+// We don't use controlSizeForFont() for steppers because the stepper height
+// should be equal to or less than the corresponding text field height,
+static NSControlSize StepperControlSizeForFont(
+ const FontDescription& font_description) {
+ int font_size = font_description.ComputedPixelSize();
+ if (font_size >= 27)
+ return NSRegularControlSize;
+ if (font_size >= 22)
+ return NSSmallControlSize;
+ return NSMiniControlSize;
+}
+
+// Theme overrides
+
+int ThemeMac::BaselinePositionAdjustment(ControlPart part) const {
+ if (part == kCheckboxPart || part == kRadioPart)
+ return -2;
+ return Theme::BaselinePositionAdjustment(part);
+}
+
+FontDescription ThemeMac::ControlFont(ControlPart part,
+ const FontDescription& font_description,
+ float zoom_factor) const {
+ switch (part) {
+ case kPushButtonPart: {
+ FontDescription result;
+ result.SetIsAbsoluteSize(true);
+ result.SetGenericFamily(FontDescription::kSerifFamily);
+
+ NSFont* ns_font = [NSFont
+ systemFontOfSize:[NSFont systemFontSizeForControlSize:
+ ControlSizeForFont(font_description)]];
+ result.FirstFamily().SetFamily(FontFamilyNames::system_ui);
+ result.SetComputedSize([ns_font pointSize] * zoom_factor);
+ result.SetSpecifiedSize([ns_font pointSize] * zoom_factor);
+ return result;
+ }
+ default:
+ return Theme::ControlFont(part, font_description, zoom_factor);
+ }
+}
+
+LengthSize ThemeMac::GetControlSize(ControlPart part,
+ const FontDescription& font_description,
+ const LengthSize& zoomed_size,
+ float zoom_factor) const {
+ switch (part) {
+ case kCheckboxPart:
+ return CheckboxSize(font_description, zoomed_size, zoom_factor);
+ case kRadioPart:
+ return RadioSize(font_description, zoomed_size, zoom_factor);
+ case kPushButtonPart:
+ // Height is reset to auto so that specified heights can be ignored.
+ return SizeFromFont(font_description,
+ LengthSize(zoomed_size.Width(), Length()),
+ zoom_factor, ButtonSizes());
+ case kInnerSpinButtonPart:
+ if (!zoomed_size.Width().IsIntrinsicOrAuto() &&
+ !zoomed_size.Height().IsIntrinsicOrAuto())
+ return zoomed_size;
+ return SizeFromNSControlSize(StepperControlSizeForFont(font_description),
+ zoomed_size, zoom_factor, StepperSizes());
+ default:
+ return zoomed_size;
+ }
+}
+
+LengthSize ThemeMac::MinimumControlSize(ControlPart part,
+ const FontDescription& font_description,
+ float zoom_factor) const {
+ switch (part) {
+ case kSquareButtonPart:
+ case kButtonPart:
+ return LengthSize(Length(0, kFixed),
+ Length(static_cast<int>(15 * zoom_factor), kFixed));
+ case kInnerSpinButtonPart: {
+ IntSize base = StepperSizes()[NSMiniControlSize];
+ return LengthSize(
+ Length(static_cast<int>(base.Width() * zoom_factor), kFixed),
+ Length(static_cast<int>(base.Height() * zoom_factor), kFixed));
+ }
+ default:
+ return Theme::MinimumControlSize(part, font_description, zoom_factor);
+ }
+}
+
+LengthBox ThemeMac::ControlBorder(ControlPart part,
+ const FontDescription& font_description,
+ const LengthBox& zoomed_box,
+ float zoom_factor) const {
+ switch (part) {
+ case kSquareButtonPart:
+ return LengthBox(0, zoomed_box.Right().Value(), 0,
+ zoomed_box.Left().Value());
+ default:
+ return Theme::ControlBorder(part, font_description, zoomed_box,
+ zoom_factor);
+ }
+}
+
+LengthBox ThemeMac::ControlPadding(ControlPart part,
+ const FontDescription& font_description,
+ const Length& zoomed_box_top,
+ const Length& zoomed_box_right,
+ const Length& zoomed_box_bottom,
+ const Length& zoomed_box_left,
+ float zoom_factor) const {
+ switch (part) {
+ case kPushButtonPart: {
+ // Just use 8px. AppKit wants to use 11px for mini buttons, but that
+ // padding is just too large for real-world Web sites (creating a huge
+ // necessary minimum width for buttons whose space is by definition
+ // constrained, since we select mini only for small cramped environments.
+ // This also guarantees the HTML <button> will match our rendering by
+ // default, since we're using a consistent padding.
+ const int padding = 8 * zoom_factor;
+ return LengthBox(2, padding, 3, padding);
+ }
+ default:
+ return Theme::ControlPadding(part, font_description, zoomed_box_top,
+ zoomed_box_right, zoomed_box_bottom,
+ zoomed_box_left, zoom_factor);
+ }
+}
+
+void ThemeMac::AddVisualOverflow(ControlPart part,
+ ControlStates states,
+ float zoom_factor,
+ IntRect& zoomed_rect) const {
+ BEGIN_BLOCK_OBJC_EXCEPTIONS
+ switch (part) {
+ case kCheckboxPart: {
+ // We inflate the rect as needed to account for padding included in the
+ // cell to accommodate the checkbox shadow" and the check. We don't
+ // consider this part of the bounds of the control in WebKit.
+ NSCell* cell = Checkbox(states, zoomed_rect, zoom_factor);
+ NSControlSize control_size = [cell controlSize];
+ IntSize zoomed_size = CheckboxSizes()[control_size];
+ zoomed_size.SetHeight(zoomed_size.Height() * zoom_factor);
+ zoomed_size.SetWidth(zoomed_size.Width() * zoom_factor);
+ zoomed_rect = InflateRect(zoomed_rect, zoomed_size,
+ CheckboxMargins(control_size), zoom_factor);
+ break;
+ }
+ case kRadioPart: {
+ // We inflate the rect as needed to account for padding included in the
+ // cell to accommodate the radio button shadow". We don't consider this
+ // part of the bounds of the control in WebKit.
+ NSCell* cell = Radio(states, zoomed_rect, zoom_factor);
+ NSControlSize control_size = [cell controlSize];
+ IntSize zoomed_size = RadioSizes()[control_size];
+ zoomed_size.SetHeight(zoomed_size.Height() * zoom_factor);
+ zoomed_size.SetWidth(zoomed_size.Width() * zoom_factor);
+ zoomed_rect = InflateRect(zoomed_rect, zoomed_size,
+ RadioMargins(control_size), zoom_factor);
+ break;
+ }
+ case kPushButtonPart:
+ case kButtonPart: {
+ NSButtonCell* cell = Button(part, states, zoomed_rect, zoom_factor);
+ NSControlSize control_size = [cell controlSize];
+
+ // We inflate the rect as needed to account for the Aqua button's shadow.
+ if ([cell bezelStyle] == NSRoundedBezelStyle) {
+ IntSize zoomed_size = ButtonSizes()[control_size];
+ zoomed_size.SetHeight(zoomed_size.Height() * zoom_factor);
+ // Buttons don't ever constrain width, so the zoomed width can just be
+ // honored.
+ zoomed_size.SetWidth(zoomed_rect.Width());
+ zoomed_rect = InflateRect(zoomed_rect, zoomed_size,
+ ButtonMargins(control_size), zoom_factor);
+ }
+ break;
+ }
+ case kInnerSpinButtonPart: {
+ static const int kStepperMargin[4] = {0, 0, 0, 0};
+ ControlSize control_size = ControlSizeFromPixelSize(
+ StepperSizes(), zoomed_rect.Size(), zoom_factor);
+ IntSize zoomed_size = StepperSizes()[control_size];
+ zoomed_size.SetHeight(zoomed_size.Height() * zoom_factor);
+ zoomed_size.SetWidth(zoomed_size.Width() * zoom_factor);
+ zoomed_rect =
+ InflateRect(zoomed_rect, zoomed_size, kStepperMargin, zoom_factor);
+ break;
+ }
+ default:
+ break;
+ }
+ END_BLOCK_OBJC_EXCEPTIONS
+}
+}
diff --git a/chromium/third_party/blink/renderer/platform/mac/version_util_mac.h b/chromium/third_party/blink/renderer/platform/mac/version_util_mac.h
new file mode 100644
index 00000000000..ea3bcbc3446
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mac/version_util_mac.h
@@ -0,0 +1,32 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MAC_VERSION_UTIL_MAC_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MAC_VERSION_UTIL_MAC_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+#include <AvailabilityMacros.h>
+
+namespace blink {
+
+namespace internal {
+
+PLATFORM_EXPORT int MacOSXMinorVersion();
+
+template <int V, int ID>
+constexpr bool IsOS() {
+ return MAC_OS_X_VERSION_MIN_REQUIRED <= ID && MacOSXMinorVersion() == V;
+}
+
+} // namespace internal
+
+const auto IsOS10_9 = internal::IsOS<9, 1090>;
+const auto IsOS10_10 = internal::IsOS<10, 101000>;
+const auto IsOS10_11 = internal::IsOS<11, 101100>;
+const auto IsOS10_12 = internal::IsOS<12, 101200>;
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MAC_VERSION_UTIL_MAC_H_
diff --git a/chromium/third_party/blink/renderer/platform/mac/version_util_mac.mm b/chromium/third_party/blink/renderer/platform/mac/version_util_mac.mm
new file mode 100644
index 00000000000..2750c6bd566
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mac/version_util_mac.mm
@@ -0,0 +1,56 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "third_party/blink/renderer/platform/mac/version_util_mac.h"
+
+#include <sstream>
+#include <string>
+#include <sys/utsname.h>
+
+namespace {
+
+// Returns the running system's Darwin major version. Don't call this, it's
+// an implementation detail and its result is meant to be cached by
+// MacOSXMinorVersion.
+int DarwinMajorVersionInternal() {
+ // The implementation of this method was copied from Chromium, with minor
+ // modifications to avoid the use of methods in base/. For further details,
+ // see
+ // https://code.google.com/p/chromium/codesearch#chromium/src/base/mac/mac_util.mm
+ struct utsname unameInfo;
+ if (uname(&unameInfo) != 0)
+ return 0;
+
+ if (strcmp(unameInfo.sysname, "Darwin") != 0)
+ return 0;
+
+ std::string releaseString(unameInfo.release);
+ size_t pos = releaseString.find_first_of('.');
+ if (pos == std::string::npos)
+ return 0;
+
+ std::istringstream convert(releaseString.substr(0, pos));
+ int majorVersion;
+ if (!(convert >> majorVersion))
+ return 0;
+
+ return majorVersion;
+}
+
+// Returns the running system's Mac OS X minor version. This is the |y| value
+// in 10.y or 10.y.z. Don't call this, it's an implementation detail and the
+// result is meant to be cached by MacOSXMinorVersion.
+int MacOSXMinorVersionInternal() {
+ int darwinMajorVersion = DarwinMajorVersionInternal();
+ return darwinMajorVersion - 4;
+}
+
+} // namespace
+
+// Returns the running system's Mac OS X minor version. This is the |y| value
+// in 10.y or 10.y.z.
+int blink::internal::MacOSXMinorVersion() {
+ static int minor_version = MacOSXMinorVersionInternal();
+ return minor_version;
+}
diff --git a/chromium/third_party/blink/renderer/platform/mac/version_util_mac_test.mm b/chromium/third_party/blink/renderer/platform/mac/version_util_mac_test.mm
new file mode 100644
index 00000000000..581a9e3b4ca
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mac/version_util_mac_test.mm
@@ -0,0 +1,51 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "third_party/blink/renderer/platform/mac/version_util_mac.h"
+
+#include <AppKit/AppKit.h>
+#include <gtest/gtest.h>
+
+#ifndef NSAppKitVersionNumber10_9
+#define NSAppKitVersionNumber10_9 1265
+#endif
+
+#ifndef NSAppKitVersionNumber10_10
+#define NSAppKitVersionNumber10_10 1343
+#endif
+
+// This number was determined by writing a tiny Cocoa App on 10.10.4.
+#define NSAppKitVersionNumber10_10Max 1348
+
+// This number was measured on OSX 10.11 Beta 15A234d. The 10.11
+// AppKit.framework does not provide an NSAppKitVersionNumber preprocessor
+// definition for OSX 10.11.
+#define NSAppKitVersionNumber10_11Max 1389
+
+// AppKit version is loosely correlated to OSX version. It's still useful as a
+// sanity check in unit tests, though we don't want to rely on it in production
+// code.
+TEST(VersionUtilMac, AppKitVersions) {
+ if (floor(NSAppKitVersionNumber) == NSAppKitVersionNumber10_9) {
+ EXPECT_TRUE(blink::IsOS10_9());
+ EXPECT_FALSE(blink::IsOS10_10());
+ EXPECT_FALSE(blink::IsOS10_11());
+ return;
+ }
+
+ if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_10Max &&
+ floor(NSAppKitVersionNumber) >= NSAppKitVersionNumber10_10) {
+ EXPECT_FALSE(blink::IsOS10_9());
+ EXPECT_TRUE(blink::IsOS10_10());
+ EXPECT_FALSE(blink::IsOS10_11());
+ return;
+ }
+
+ if (floor(NSAppKitVersionNumber) == NSAppKitVersionNumber10_11Max) {
+ EXPECT_FALSE(blink::IsOS10_9());
+ EXPECT_FALSE(blink::IsOS10_10());
+ EXPECT_TRUE(blink::IsOS10_11());
+ return;
+ }
+}
diff --git a/chromium/third_party/blink/renderer/platform/mac/web_core_ns_cell_extras.h b/chromium/third_party/blink/renderer/platform/mac/web_core_ns_cell_extras.h
new file mode 100644
index 00000000000..b5438556046
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mac/web_core_ns_cell_extras.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2011, 2012 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_MAC_WEB_CORE_NS_CELL_EXTRAS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MAC_WEB_CORE_NS_CELL_EXTRAS_H_
+
+#import <AppKit/AppKit.h>
+
+@interface NSCell (BlinkFocusRingDrawing)
+- (void)cr_drawFocusRingWithFrame:(NSRect)cellFrame inView:(NSView*)controlView;
+@end
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MAC_WEB_CORE_NS_CELL_EXTRAS_H_
diff --git a/chromium/third_party/blink/renderer/platform/mac/web_core_ns_cell_extras.mm b/chromium/third_party/blink/renderer/platform/mac/web_core_ns_cell_extras.mm
new file mode 100644
index 00000000000..8b6353dd14f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mac/web_core_ns_cell_extras.mm
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2011, 2012 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/mac/web_core_ns_cell_extras.h"
+
+@interface NSCell (LionSDKDeclarations)
+- (void)drawFocusRingMaskWithFrame:(NSRect)cellFrame
+ inView:(NSView*)controlView;
+@end
+
+@implementation NSCell (BlinkFocusRingDrawing)
+
+- (void)cr_drawFocusRingWithFrame:(NSRect)cellFrame
+ inView:(NSView*)controlView {
+ CGContextRef cgContext =
+ (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
+ CGContextSaveGState(cgContext);
+ NSSetFocusRingStyle(NSFocusRingOnly);
+ CGContextBeginTransparencyLayerWithRect(cgContext, NSRectToCGRect(cellFrame),
+ 0);
+ [self drawFocusRingMaskWithFrame:cellFrame inView:controlView];
+ CGContextEndTransparencyLayer(cgContext);
+ CGContextRestoreGState(cgContext);
+}
+
+@end
diff --git a/chromium/third_party/blink/renderer/platform/media/BUILD.gn b/chromium/third_party/blink/renderer/platform/media/BUILD.gn
new file mode 100644
index 00000000000..59532133dbc
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/media/BUILD.gn
@@ -0,0 +1,17 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//third_party/blink/renderer/platform/platform.gni")
+
+blink_platform_sources("media") {
+ sources = [
+ "resource_bundle_helper.cc",
+ "resource_bundle_helper.h",
+ ]
+
+ deps = [
+ "//third_party/zlib/google:compression_utils",
+ "//ui/base:base",
+ ]
+}
diff --git a/chromium/third_party/blink/renderer/platform/media/DEPS b/chromium/third_party/blink/renderer/platform/media/DEPS
new file mode 100644
index 00000000000..dae35ae2f9c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/media/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+third_party/zlib/google/compression_utils.h",
+ "+ui/base/resource",
+]
diff --git a/chromium/third_party/blink/renderer/platform/media/resource_bundle_helper.cc b/chromium/third_party/blink/renderer/platform/media/resource_bundle_helper.cc
new file mode 100644
index 00000000000..7737f5bf6f6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/media/resource_bundle_helper.cc
@@ -0,0 +1,34 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/media/resource_bundle_helper.h"
+
+#include "third_party/zlib/google/compression_utils.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/resource/resource_handle.h"
+
+namespace {
+
+std::string GetResource(int resource_id) {
+ ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
+ return bundle
+ .GetRawDataResourceForScale(resource_id, bundle.GetMaxScaleFactor())
+ .as_string();
+}
+
+} // namespace.
+
+namespace blink {
+
+String ResourceBundleHelper::GetResourceAsString(int resource_id) {
+ return String::FromUTF8(GetResource(resource_id).c_str());
+};
+
+String ResourceBundleHelper::UncompressResourceAsString(int resource_id) {
+ std::string uncompressed;
+ CHECK(compression::GzipUncompress(GetResource(resource_id), &uncompressed));
+ return String::FromUTF8(uncompressed.c_str());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/media/resource_bundle_helper.h b/chromium/third_party/blink/renderer/platform/media/resource_bundle_helper.h
new file mode 100644
index 00000000000..de115f593ed
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/media/resource_bundle_helper.h
@@ -0,0 +1,30 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIA_RESOURCE_BUNDLE_HELPER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIA_RESOURCE_BUNDLE_HELPER_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+// Provides access to ui::ResourceBundle in Blink. This
+// allows Blink to directly load resources from the bundle.
+class ResourceBundleHelper {
+ STATIC_ONLY(ResourceBundleHelper);
+
+ public:
+ // Returns the contents of a resource as a string specified by the
+ // resource id from Grit.
+ static PLATFORM_EXPORT String GetResourceAsString(int resource_id);
+
+ // Uncompresses a gzipped resource and returns it as a string. The resource
+ // is specified by the resource id from Grit.
+ static PLATFORM_EXPORT String UncompressResourceAsString(int resource_id);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIA_RESOURCE_BUNDLE_HELPER_H_
diff --git a/chromium/third_party/blink/renderer/platform/mediastream/media_stream_center.cc b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_center.cc
new file mode 100644
index 00000000000..8641d27b88b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_center.cc
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2011 Ericsson AB. All rights reserved.
+ * Copyright (C) 2012 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:
+ *
+ * 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 Ericsson 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 "third_party/blink/renderer/platform/mediastream/media_stream_center.h"
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_audio_source_provider.h"
+#include "third_party/blink/public/platform/web_media_stream.h"
+#include "third_party/blink/public/platform/web_media_stream_center.h"
+#include "third_party/blink/public/platform/web_media_stream_source.h"
+#include "third_party/blink/public/platform/web_media_stream_track.h"
+#include "third_party/blink/renderer/platform/mediastream/media_stream_descriptor.h"
+#include "third_party/blink/renderer/platform/mediastream/media_stream_web_audio_source.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+namespace blink {
+
+MediaStreamCenter& MediaStreamCenter::Instance() {
+ DCHECK(IsMainThread());
+ DEFINE_STATIC_LOCAL(MediaStreamCenter, center, ());
+ return center;
+}
+
+MediaStreamCenter::MediaStreamCenter()
+ : private_(Platform::Current()->CreateMediaStreamCenter(this)) {}
+
+MediaStreamCenter::~MediaStreamCenter() = default;
+
+void MediaStreamCenter::DidSetMediaStreamTrackEnabled(
+ MediaStreamComponent* component) {
+ if (private_) {
+ if (component->Enabled()) {
+ private_->DidEnableMediaStreamTrack(component);
+ } else {
+ private_->DidDisableMediaStreamTrack(component);
+ }
+ }
+}
+
+void MediaStreamCenter::DidCreateMediaStreamAndTracks(
+ MediaStreamDescriptor* stream) {
+ if (!private_)
+ return;
+
+ for (size_t i = 0; i < stream->NumberOfAudioComponents(); ++i)
+ DidCreateMediaStreamTrack(stream->AudioComponent(i));
+
+ for (size_t i = 0; i < stream->NumberOfVideoComponents(); ++i)
+ DidCreateMediaStreamTrack(stream->VideoComponent(i));
+}
+
+void MediaStreamCenter::DidCreateMediaStreamTrack(MediaStreamComponent* track) {
+ if (private_)
+ private_->DidCreateMediaStreamTrack(track);
+}
+
+void MediaStreamCenter::DidCloneMediaStreamTrack(MediaStreamComponent* original,
+ MediaStreamComponent* clone) {
+ if (private_)
+ private_->DidCloneMediaStreamTrack(original, clone);
+}
+
+void MediaStreamCenter::DidSetContentHint(MediaStreamComponent* track) {
+ if (private_)
+ private_->DidSetContentHint(track);
+}
+
+std::unique_ptr<AudioSourceProvider>
+MediaStreamCenter::CreateWebAudioSourceFromMediaStreamTrack(
+ MediaStreamComponent* track) {
+ DCHECK(track);
+ if (private_) {
+ return MediaStreamWebAudioSource::Create(base::WrapUnique(
+ private_->CreateWebAudioSourceFromMediaStreamTrack(track)));
+ }
+
+ return nullptr;
+}
+
+void MediaStreamCenter::DidStopMediaStreamSource(MediaStreamSource* source) {
+ if (private_)
+ private_->DidStopMediaStreamSource(source);
+}
+
+void MediaStreamCenter::StopLocalMediaStream(const WebMediaStream& web_stream) {
+ MediaStreamDescriptor* stream = web_stream;
+ MediaStreamDescriptorClient* client = stream->Client();
+ if (client)
+ client->StreamEnded();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/mediastream/media_stream_center.h b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_center.h
new file mode 100644
index 00000000000..d6d8a3bef7c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_center.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2014 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:
+ *
+ * 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 Ericsson 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIASTREAM_MEDIA_STREAM_CENTER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIASTREAM_MEDIA_STREAM_CENTER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/public/platform/web_media_stream_center_client.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class AudioSourceProvider;
+class MediaStreamComponent;
+class MediaStreamDescriptor;
+class MediaStreamSource;
+class WebMediaStream;
+class WebMediaStreamCenter;
+
+class PLATFORM_EXPORT MediaStreamCenter final
+ : public WebMediaStreamCenterClient {
+ USING_FAST_MALLOC(MediaStreamCenter);
+
+ public:
+ ~MediaStreamCenter() override;
+
+ static MediaStreamCenter& Instance();
+
+ void DidCreateMediaStreamTrack(MediaStreamComponent*);
+ void DidCloneMediaStreamTrack(MediaStreamComponent* original,
+ MediaStreamComponent* clone);
+ void DidSetMediaStreamTrackEnabled(MediaStreamComponent*);
+ void DidSetContentHint(MediaStreamComponent*);
+ std::unique_ptr<AudioSourceProvider> CreateWebAudioSourceFromMediaStreamTrack(
+ MediaStreamComponent*);
+
+ void DidCreateMediaStreamAndTracks(MediaStreamDescriptor*);
+
+ void DidStopMediaStreamSource(MediaStreamSource*);
+
+ // blink::WebMediaStreamCenterClient
+ void StopLocalMediaStream(const WebMediaStream&) override;
+
+ private:
+ MediaStreamCenter();
+
+ std::unique_ptr<WebMediaStreamCenter> private_;
+
+ DISALLOW_COPY_AND_ASSIGN(MediaStreamCenter);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIASTREAM_MEDIA_STREAM_CENTER_H_
diff --git a/chromium/third_party/blink/renderer/platform/mediastream/media_stream_component.cc b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_component.cc
new file mode 100644
index 00000000000..257aea2f362
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_component.cc
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2011 Ericsson AB. All rights reserved.
+ * Copyright (C) 2013 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 "third_party/blink/renderer/platform/mediastream/media_stream_component.h"
+
+#include "third_party/blink/public/platform/web_audio_source_provider.h"
+#include "third_party/blink/public/platform/web_media_stream_track.h"
+#include "third_party/blink/renderer/platform/audio/audio_bus.h"
+#include "third_party/blink/renderer/platform/mediastream/media_stream_center.h"
+#include "third_party/blink/renderer/platform/mediastream/media_stream_source.h"
+#include "third_party/blink/renderer/platform/uuid.h"
+
+namespace blink {
+
+namespace {
+
+static int g_unique_media_stream_component_id = 0;
+
+} // namespace
+
+// static
+int MediaStreamComponent::GenerateUniqueId() {
+ return ++g_unique_media_stream_component_id;
+}
+
+MediaStreamComponent* MediaStreamComponent::Create(MediaStreamSource* source) {
+ return new MediaStreamComponent(CreateCanonicalUUIDString(), source);
+}
+
+MediaStreamComponent* MediaStreamComponent::Create(const String& id,
+ MediaStreamSource* source) {
+ return new MediaStreamComponent(id, source);
+}
+
+MediaStreamComponent::MediaStreamComponent(const String& id,
+ MediaStreamSource* source)
+ : source_(source), id_(id), unique_id_(GenerateUniqueId()) {
+ DCHECK(id_.length());
+}
+
+MediaStreamComponent* MediaStreamComponent::Clone() const {
+ MediaStreamComponent* cloned_component =
+ new MediaStreamComponent(CreateCanonicalUUIDString(), Source());
+ cloned_component->SetEnabled(enabled_);
+ cloned_component->SetMuted(muted_);
+ cloned_component->SetContentHint(content_hint_);
+ cloned_component->SetConstraints(constraints_);
+ return cloned_component;
+}
+
+void MediaStreamComponent::Dispose() {
+ track_data_.reset();
+}
+
+void MediaStreamComponent::AudioSourceProviderImpl::Wrap(
+ WebAudioSourceProvider* provider) {
+ MutexLocker locker(provide_input_lock_);
+ web_audio_source_provider_ = provider;
+}
+
+void MediaStreamComponent::GetSettings(
+ WebMediaStreamTrack::Settings& settings) {
+ DCHECK(track_data_);
+ source_->GetSettings(settings);
+ track_data_->GetSettings(settings);
+}
+
+void MediaStreamComponent::SetContentHint(
+ WebMediaStreamTrack::ContentHintType hint) {
+ switch (hint) {
+ case WebMediaStreamTrack::ContentHintType::kNone:
+ break;
+ case WebMediaStreamTrack::ContentHintType::kAudioSpeech:
+ case WebMediaStreamTrack::ContentHintType::kAudioMusic:
+ DCHECK_EQ(MediaStreamSource::kTypeAudio, Source()->GetType());
+ break;
+ case WebMediaStreamTrack::ContentHintType::kVideoMotion:
+ case WebMediaStreamTrack::ContentHintType::kVideoDetail:
+ DCHECK_EQ(MediaStreamSource::kTypeVideo, Source()->GetType());
+ break;
+ }
+ if (hint == content_hint_)
+ return;
+ content_hint_ = hint;
+
+ MediaStreamCenter::Instance().DidSetContentHint(this);
+}
+
+void MediaStreamComponent::AudioSourceProviderImpl::ProvideInput(
+ AudioBus* bus,
+ size_t frames_to_process) {
+ DCHECK(bus);
+ if (!bus)
+ return;
+
+ MutexTryLocker try_locker(provide_input_lock_);
+ if (!try_locker.Locked() || !web_audio_source_provider_) {
+ bus->Zero();
+ return;
+ }
+
+ // Wrap the AudioBus channel data using WebVector.
+ size_t n = bus->NumberOfChannels();
+ WebVector<float*> web_audio_data(n);
+ for (size_t i = 0; i < n; ++i)
+ web_audio_data[i] = bus->Channel(i)->MutableData();
+
+ web_audio_source_provider_->ProvideInput(web_audio_data, frames_to_process);
+}
+
+void MediaStreamComponent::Trace(blink::Visitor* visitor) {
+ visitor->Trace(source_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/mediastream/media_stream_component.h b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_component.h
new file mode 100644
index 00000000000..ed0b1cd2db2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_component.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2011 Ericsson AB. All rights reserved.
+ * Copyright (C) 2013 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:
+ *
+ * 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 Ericsson 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIASTREAM_MEDIA_STREAM_COMPONENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIASTREAM_MEDIA_STREAM_COMPONENT_H_
+
+#include <memory>
+#include "third_party/blink/public/platform/web_media_constraints.h"
+#include "third_party/blink/public/platform/web_media_stream_track.h"
+#include "third_party/blink/renderer/platform/audio/audio_source_provider.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
+
+namespace blink {
+
+class MediaStreamSource;
+class WebAudioSourceProvider;
+
+// A MediaStreamComponent is a MediaStreamTrack.
+// TODO(hta): Consider merging the two classes.
+
+class PLATFORM_EXPORT MediaStreamComponent final
+ : public GarbageCollectedFinalized<MediaStreamComponent> {
+ USING_PRE_FINALIZER(MediaStreamComponent, Dispose);
+
+ private:
+ static int GenerateUniqueId();
+
+ public:
+ // This class represents whatever data the Web layer uses to represent
+ // a track. It needs to be able to answer the getSettings question.
+ class TrackData {
+ USING_FAST_MALLOC(TrackData);
+
+ public:
+ virtual void GetSettings(WebMediaStreamTrack::Settings&) = 0;
+ virtual ~TrackData() = default;
+ };
+
+ static MediaStreamComponent* Create(MediaStreamSource*);
+ static MediaStreamComponent* Create(const String& id, MediaStreamSource*);
+
+ MediaStreamComponent* Clone() const;
+
+ // |m_trackData| may hold pointers to GC objects indirectly, and it may touch
+ // eagerly finalized objects in destruction.
+ // So this class runs pre-finalizer to finalize |m_trackData| promptly.
+ void Dispose();
+
+ MediaStreamSource* Source() const { return source_.Get(); }
+
+ // This is the same as the id of the |MediaStreamTrack|. It is unique in most
+ // contexts but collisions can occur e.g. if tracks are created by different
+ // |RTCPeerConnection|s or a remote track ID is signaled to be added, removed
+ // and then re-added resulting in a new track object the second time around.
+ String Id() const { return id_; }
+ // Uniquely identifies this component.
+ int UniqueId() const { return unique_id_; }
+ bool Enabled() const { return enabled_; }
+ void SetEnabled(bool enabled) { enabled_ = enabled; }
+ bool Muted() const { return muted_; }
+ void SetMuted(bool muted) { muted_ = muted; }
+ WebMediaStreamTrack::ContentHintType ContentHint() { return content_hint_; }
+ void SetContentHint(WebMediaStreamTrack::ContentHintType);
+ const WebMediaConstraints& Constraints() const { return constraints_; }
+ void SetConstraints(const WebMediaConstraints& constraints) {
+ constraints_ = constraints;
+ }
+ AudioSourceProvider* GetAudioSourceProvider() { return &source_provider_; }
+ void SetSourceProvider(WebAudioSourceProvider* provider) {
+ source_provider_.Wrap(provider);
+ }
+
+ TrackData* GetTrackData() const { return track_data_.get(); }
+ void SetTrackData(std::unique_ptr<TrackData> track_data) {
+ track_data_ = std::move(track_data);
+ }
+ void GetSettings(WebMediaStreamTrack::Settings&);
+
+ void Trace(blink::Visitor*);
+
+ private:
+ MediaStreamComponent(const String& id, MediaStreamSource*);
+
+ // AudioSourceProviderImpl wraps a WebAudioSourceProvider::provideInput()
+ // calls into chromium to get a rendered audio stream.
+
+ class PLATFORM_EXPORT AudioSourceProviderImpl final
+ : public AudioSourceProvider {
+ public:
+ AudioSourceProviderImpl() : web_audio_source_provider_(nullptr) {}
+
+ ~AudioSourceProviderImpl() override = default;
+
+ // Wraps the given blink::WebAudioSourceProvider to
+ // blink::AudioSourceProvider.
+ void Wrap(WebAudioSourceProvider*);
+
+ // blink::AudioSourceProvider
+ void ProvideInput(AudioBus*, size_t frames_to_process) override;
+
+ private:
+ WebAudioSourceProvider* web_audio_source_provider_;
+ Mutex provide_input_lock_;
+ };
+
+ AudioSourceProviderImpl source_provider_;
+ Member<MediaStreamSource> source_;
+ String id_;
+ int unique_id_;
+ bool enabled_ = true;
+ bool muted_ = false;
+ WebMediaStreamTrack::ContentHintType content_hint_ =
+ WebMediaStreamTrack::ContentHintType::kNone;
+ WebMediaConstraints constraints_;
+ std::unique_ptr<TrackData> track_data_;
+};
+
+typedef HeapVector<Member<MediaStreamComponent>> MediaStreamComponentVector;
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIASTREAM_MEDIA_STREAM_COMPONENT_H_
diff --git a/chromium/third_party/blink/renderer/platform/mediastream/media_stream_descriptor.cc b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_descriptor.cc
new file mode 100644
index 00000000000..b327c63e9ed
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_descriptor.cc
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2011 Ericsson AB. All rights reserved.
+ * Copyright (C) 2013 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 "third_party/blink/renderer/platform/mediastream/media_stream_descriptor.h"
+
+#include "third_party/blink/public/platform/web_media_stream.h"
+#include "third_party/blink/renderer/platform/uuid.h"
+
+namespace blink {
+
+namespace {
+
+static int g_unique_media_stream_descriptor_id = 0;
+
+} // namespace
+
+// static
+int MediaStreamDescriptor::GenerateUniqueId() {
+ return ++g_unique_media_stream_descriptor_id;
+}
+
+MediaStreamDescriptor* MediaStreamDescriptor::Create(
+ const MediaStreamSourceVector& audio_sources,
+ const MediaStreamSourceVector& video_sources) {
+ return new MediaStreamDescriptor(CreateCanonicalUUIDString(), audio_sources,
+ video_sources);
+}
+
+MediaStreamDescriptor* MediaStreamDescriptor::Create(
+ const MediaStreamComponentVector& audio_components,
+ const MediaStreamComponentVector& video_components) {
+ return new MediaStreamDescriptor(CreateCanonicalUUIDString(),
+ audio_components, video_components);
+}
+
+MediaStreamDescriptor* MediaStreamDescriptor::Create(
+ const String& id,
+ const MediaStreamComponentVector& audio_components,
+ const MediaStreamComponentVector& video_components) {
+ return new MediaStreamDescriptor(id, audio_components, video_components);
+}
+
+void MediaStreamDescriptor::AddComponent(MediaStreamComponent* component) {
+ switch (component->Source()->GetType()) {
+ case MediaStreamSource::kTypeAudio:
+ if (audio_components_.Find(component) == kNotFound)
+ audio_components_.push_back(component);
+ break;
+ case MediaStreamSource::kTypeVideo:
+ if (video_components_.Find(component) == kNotFound)
+ video_components_.push_back(component);
+ break;
+ }
+
+ for (auto& observer : observers_) {
+ observer->TrackAdded(component);
+ }
+}
+
+void MediaStreamDescriptor::RemoveComponent(MediaStreamComponent* component) {
+ size_t pos = kNotFound;
+ switch (component->Source()->GetType()) {
+ case MediaStreamSource::kTypeAudio:
+ pos = audio_components_.Find(component);
+ if (pos != kNotFound)
+ audio_components_.EraseAt(pos);
+ break;
+ case MediaStreamSource::kTypeVideo:
+ pos = video_components_.Find(component);
+ if (pos != kNotFound)
+ video_components_.EraseAt(pos);
+ break;
+ }
+
+ for (auto& observer : observers_) {
+ observer->TrackRemoved(component);
+ }
+}
+
+void MediaStreamDescriptor::AddRemoteTrack(MediaStreamComponent* component) {
+ if (client_)
+ client_->AddTrackByComponent(component);
+ else
+ AddComponent(component);
+}
+
+void MediaStreamDescriptor::RemoveRemoteTrack(MediaStreamComponent* component) {
+ if (client_)
+ client_->RemoveTrackByComponent(component);
+ else
+ RemoveComponent(component);
+}
+
+void MediaStreamDescriptor::AddObserver(WebMediaStreamObserver* observer) {
+ DCHECK_EQ(observers_.Find(observer), kNotFound);
+ observers_.push_back(observer);
+}
+
+void MediaStreamDescriptor::RemoveObserver(WebMediaStreamObserver* observer) {
+ size_t index = observers_.Find(observer);
+ DCHECK(index != kNotFound);
+ observers_.EraseAt(index);
+}
+
+MediaStreamDescriptor::MediaStreamDescriptor(
+ const String& id,
+ const MediaStreamSourceVector& audio_sources,
+ const MediaStreamSourceVector& video_sources)
+ : client_(nullptr), id_(id), unique_id_(GenerateUniqueId()), active_(true) {
+ DCHECK(id_.length());
+ for (size_t i = 0; i < audio_sources.size(); i++)
+ audio_components_.push_back(MediaStreamComponent::Create(audio_sources[i]));
+
+ for (size_t i = 0; i < video_sources.size(); i++)
+ video_components_.push_back(MediaStreamComponent::Create(video_sources[i]));
+}
+
+MediaStreamDescriptor::MediaStreamDescriptor(
+ const String& id,
+ const MediaStreamComponentVector& audio_components,
+ const MediaStreamComponentVector& video_components)
+ : client_(nullptr), id_(id), unique_id_(GenerateUniqueId()), active_(true) {
+ DCHECK(id_.length());
+ for (MediaStreamComponentVector::const_iterator iter =
+ audio_components.begin();
+ iter != audio_components.end(); ++iter)
+ audio_components_.push_back((*iter));
+ for (MediaStreamComponentVector::const_iterator iter =
+ video_components.begin();
+ iter != video_components.end(); ++iter)
+ video_components_.push_back((*iter));
+}
+
+void MediaStreamDescriptor::Trace(blink::Visitor* visitor) {
+ visitor->Trace(audio_components_);
+ visitor->Trace(video_components_);
+ visitor->Trace(client_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/mediastream/media_stream_descriptor.h b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_descriptor.h
new file mode 100644
index 00000000000..41060ba217e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_descriptor.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2011 Ericsson AB. All rights reserved.
+ * Copyright (C) 2012 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:
+ *
+ * 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 Ericsson 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIASTREAM_MEDIA_STREAM_DESCRIPTOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIASTREAM_MEDIA_STREAM_DESCRIPTOR_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/mediastream/media_stream_component.h"
+#include "third_party/blink/renderer/platform/mediastream/media_stream_source.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class WebMediaStreamObserver;
+
+class PLATFORM_EXPORT MediaStreamDescriptorClient
+ : public GarbageCollectedMixin {
+ public:
+ virtual ~MediaStreamDescriptorClient() = default;
+
+ virtual void StreamEnded() = 0;
+ virtual void AddTrackByComponent(MediaStreamComponent*) = 0;
+ virtual void RemoveTrackByComponent(MediaStreamComponent*) = 0;
+ virtual void Trace(blink::Visitor* visitor) {}
+};
+
+class PLATFORM_EXPORT MediaStreamDescriptor final
+ : public GarbageCollectedFinalized<MediaStreamDescriptor> {
+ private:
+ static int GenerateUniqueId();
+
+ public:
+ // Only used for AudioDestinationNode.
+ static MediaStreamDescriptor* Create(
+ const MediaStreamSourceVector& audio_sources,
+ const MediaStreamSourceVector& video_sources);
+
+ static MediaStreamDescriptor* Create(
+ const MediaStreamComponentVector& audio_components,
+ const MediaStreamComponentVector& video_components);
+
+ static MediaStreamDescriptor* Create(
+ const String& id,
+ const MediaStreamComponentVector& audio_components,
+ const MediaStreamComponentVector& video_components);
+
+ MediaStreamDescriptorClient* Client() const { return client_; }
+ void SetClient(MediaStreamDescriptorClient* client) { client_ = client; }
+
+ // This is the same as the id of the |MediaStream|. It is unique in most
+ // contexts but collisions can occur e.g. if streams are created by different
+ // |RTCPeerConnection|s or a remote stream ID is signaled to be added, removed
+ // and then re-added resulting in a new stream object the second time around.
+ String Id() const { return id_; }
+ // Uniquely identifies this descriptor.
+ int UniqueId() const { return unique_id_; }
+
+ unsigned NumberOfAudioComponents() const { return audio_components_.size(); }
+ MediaStreamComponent* AudioComponent(unsigned index) const {
+ return audio_components_[index].Get();
+ }
+
+ unsigned NumberOfVideoComponents() const { return video_components_.size(); }
+ MediaStreamComponent* VideoComponent(unsigned index) const {
+ return video_components_[index].Get();
+ }
+
+ void AddComponent(MediaStreamComponent*);
+ void RemoveComponent(MediaStreamComponent*);
+
+ void AddRemoteTrack(MediaStreamComponent*);
+ void RemoveRemoteTrack(MediaStreamComponent*);
+
+ bool Active() const { return active_; }
+ void SetActive(bool active) { active_ = active; }
+
+ void AddObserver(WebMediaStreamObserver*);
+ void RemoveObserver(WebMediaStreamObserver*);
+
+ // |m_extraData| may hold pointers to GC objects, and it may touch them in
+ // destruction. So this class is eagerly finalized to finalize |m_extraData|
+ // promptly.
+ EAGERLY_FINALIZE();
+ void Trace(blink::Visitor*);
+
+ private:
+ MediaStreamDescriptor(const String& id,
+ const MediaStreamSourceVector& audio_sources,
+ const MediaStreamSourceVector& video_sources);
+ MediaStreamDescriptor(const String& id,
+ const MediaStreamComponentVector& audio_components,
+ const MediaStreamComponentVector& video_components);
+
+ Member<MediaStreamDescriptorClient> client_;
+ String id_;
+ int unique_id_;
+ HeapVector<Member<MediaStreamComponent>> audio_components_;
+ HeapVector<Member<MediaStreamComponent>> video_components_;
+ Vector<WebMediaStreamObserver*> observers_;
+ bool active_;
+};
+
+typedef HeapVector<Member<MediaStreamDescriptor>> MediaStreamDescriptorVector;
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIASTREAM_MEDIA_STREAM_DESCRIPTOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/mediastream/media_stream_source.cc b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_source.cc
new file mode 100644
index 00000000000..282cf749ea6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_source.cc
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ * 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 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 "third_party/blink/renderer/platform/mediastream/media_stream_source.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+namespace blink {
+
+MediaStreamSource* MediaStreamSource::Create(const String& id,
+ StreamType type,
+ const String& name,
+ bool remote,
+ ReadyState ready_state,
+ bool requires_consumer) {
+ return new MediaStreamSource(id, type, name, remote, ready_state,
+ requires_consumer);
+}
+
+MediaStreamSource::MediaStreamSource(const String& id,
+ StreamType type,
+ const String& name,
+ bool remote,
+ ReadyState ready_state,
+ bool requires_consumer)
+ : id_(id),
+ type_(type),
+ name_(name),
+ remote_(remote),
+ ready_state_(ready_state),
+ requires_consumer_(requires_consumer) {}
+
+void MediaStreamSource::SetReadyState(ReadyState ready_state) {
+ if (ready_state_ != kReadyStateEnded && ready_state_ != ready_state) {
+ ready_state_ = ready_state;
+
+ // Observers may dispatch events which create and add new Observers;
+ // take a snapshot so as to safely iterate.
+ HeapVector<Member<Observer>> observers;
+ CopyToVector(observers_, observers);
+ for (auto observer : observers)
+ observer->SourceChangedState();
+
+ // setReadyState() will be invoked via the MediaStreamComponent::dispose()
+ // prefinalizer, allocating |observers|. Which means that |observers| will
+ // live until the next GC (but be unreferenced by other heap objects),
+ // _but_ it will potentially contain references to Observers that were
+ // GCed after the MediaStreamComponent prefinalizer had completed.
+ //
+ // So, if the next GC is a conservative one _and_ it happens to find
+ // a reference to |observers| when scanning the stack, we're in trouble
+ // as it contains references to now-dead objects.
+ //
+ // Work around this by explicitly clearing the vector backing store.
+ //
+ // TODO(sof): consider adding run-time checks that disallows this kind
+ // of dead object revivification by default.
+ for (size_t i = 0; i < observers.size(); ++i)
+ observers[i] = nullptr;
+ }
+}
+
+void MediaStreamSource::AddObserver(MediaStreamSource::Observer* observer) {
+ observers_.insert(observer);
+}
+
+void MediaStreamSource::SetAudioProcessingProperties(bool echo_cancellation,
+ bool auto_gain_control,
+ bool noise_supression) {
+ echo_cancellation_ = echo_cancellation;
+ auto_gain_control_ = auto_gain_control;
+ noise_supression_ = noise_supression;
+}
+
+void MediaStreamSource::AddAudioConsumer(AudioDestinationConsumer* consumer) {
+ DCHECK(requires_consumer_);
+ MutexLocker locker(audio_consumers_lock_);
+ audio_consumers_.insert(consumer);
+}
+
+bool MediaStreamSource::RemoveAudioConsumer(
+ AudioDestinationConsumer* consumer) {
+ DCHECK(requires_consumer_);
+ MutexLocker locker(audio_consumers_lock_);
+ auto it = audio_consumers_.find(consumer);
+ if (it == audio_consumers_.end())
+ return false;
+ audio_consumers_.erase(it);
+ return true;
+}
+
+void MediaStreamSource::GetSettings(WebMediaStreamTrack::Settings& settings) {
+ settings.device_id = Id();
+
+ if (echo_cancellation_)
+ settings.echo_cancellation = *echo_cancellation_;
+ if (auto_gain_control_)
+ settings.auto_gain_control = *auto_gain_control_;
+ if (noise_supression_)
+ settings.noise_supression = *noise_supression_;
+}
+
+void MediaStreamSource::SetAudioFormat(size_t number_of_channels,
+ float sample_rate) {
+ DCHECK(requires_consumer_);
+ MutexLocker locker(audio_consumers_lock_);
+ for (AudioDestinationConsumer* consumer : audio_consumers_)
+ consumer->SetFormat(number_of_channels, sample_rate);
+}
+
+void MediaStreamSource::ConsumeAudio(AudioBus* bus, size_t number_of_frames) {
+ DCHECK(requires_consumer_);
+ MutexLocker locker(audio_consumers_lock_);
+ for (AudioDestinationConsumer* consumer : audio_consumers_)
+ consumer->ConsumeAudio(bus, number_of_frames);
+}
+
+void MediaStreamSource::Trace(blink::Visitor* visitor) {
+ visitor->Trace(observers_);
+}
+
+STATIC_ASSERT_ENUM(WebMediaStreamSource::kTypeAudio,
+ MediaStreamSource::kTypeAudio);
+STATIC_ASSERT_ENUM(WebMediaStreamSource::kTypeVideo,
+ MediaStreamSource::kTypeVideo);
+STATIC_ASSERT_ENUM(WebMediaStreamSource::kReadyStateLive,
+ MediaStreamSource::kReadyStateLive);
+STATIC_ASSERT_ENUM(WebMediaStreamSource::kReadyStateMuted,
+ MediaStreamSource::kReadyStateMuted);
+STATIC_ASSERT_ENUM(WebMediaStreamSource::kReadyStateEnded,
+ MediaStreamSource::kReadyStateEnded);
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/mediastream/media_stream_source.h b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_source.h
new file mode 100644
index 00000000000..1a72cd9acf5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_source.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2011 Ericsson AB. All rights reserved.
+ * Copyright (C) 2012 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:
+ *
+ * 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 Ericsson 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIASTREAM_MEDIA_STREAM_SOURCE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIASTREAM_MEDIA_STREAM_SOURCE_H_
+
+#include <memory>
+#include <utility>
+
+#include "third_party/blink/public/platform/web_media_constraints.h"
+#include "third_party/blink/public/platform/web_media_stream_source.h"
+#include "third_party/blink/public/platform/web_media_stream_track.h"
+#include "third_party/blink/renderer/platform/audio/audio_destination_consumer.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT MediaStreamSource final
+ : public GarbageCollectedFinalized<MediaStreamSource> {
+ public:
+ class PLATFORM_EXPORT Observer : public GarbageCollectedMixin {
+ public:
+ virtual ~Observer() = default;
+ virtual void SourceChangedState() = 0;
+ };
+
+ class ExtraData {
+ USING_FAST_MALLOC(ExtraData);
+
+ public:
+ virtual ~ExtraData() = default;
+ };
+
+ enum StreamType { kTypeAudio, kTypeVideo };
+
+ enum ReadyState {
+ kReadyStateLive = 0,
+ kReadyStateMuted = 1,
+ kReadyStateEnded = 2
+ };
+
+ static MediaStreamSource* Create(const String& id,
+ StreamType,
+ const String& name,
+ bool remote,
+ ReadyState = kReadyStateLive,
+ bool requires_consumer = false);
+
+ const String& Id() const { return id_; }
+ StreamType GetType() const { return type_; }
+ const String& GetName() const { return name_; }
+ bool Remote() const { return remote_; }
+
+ void SetReadyState(ReadyState);
+ ReadyState GetReadyState() const { return ready_state_; }
+
+ void AddObserver(Observer*);
+
+ ExtraData* GetExtraData() const { return extra_data_.get(); }
+ void SetExtraData(std::unique_ptr<ExtraData> extra_data) {
+ extra_data_ = std::move(extra_data);
+ }
+
+ void SetAudioProcessingProperties(bool echo_cancellation,
+ bool auto_gain_control,
+ bool noise_supression);
+
+ void SetConstraints(WebMediaConstraints constraints) {
+ constraints_ = constraints;
+ }
+ WebMediaConstraints Constraints() { return constraints_; }
+ void GetSettings(WebMediaStreamTrack::Settings&);
+
+ const WebMediaStreamSource::Capabilities& GetCapabilities() {
+ return capabilities_;
+ }
+ void SetCapabilities(const WebMediaStreamSource::Capabilities& capabilities) {
+ capabilities_ = capabilities;
+ }
+
+ void SetAudioFormat(size_t number_of_channels, float sample_rate);
+ void ConsumeAudio(AudioBus*, size_t number_of_frames);
+
+ bool RequiresAudioConsumer() const { return requires_consumer_; }
+ void AddAudioConsumer(AudioDestinationConsumer*);
+ bool RemoveAudioConsumer(AudioDestinationConsumer*);
+ const HashSet<AudioDestinationConsumer*>& AudioConsumers() {
+ return audio_consumers_;
+ }
+
+ // |m_extraData| may hold pointers to GC objects, and it may touch them in
+ // destruction. So this class is eagerly finalized to finalize |m_extraData|
+ // promptly.
+ EAGERLY_FINALIZE();
+ void Trace(blink::Visitor*);
+
+ private:
+ MediaStreamSource(const String& id,
+ StreamType,
+ const String& name,
+ bool remote,
+ ReadyState,
+ bool requires_consumer);
+
+ String id_;
+ StreamType type_;
+ String name_;
+ bool remote_;
+ ReadyState ready_state_;
+ bool requires_consumer_;
+ HeapHashSet<WeakMember<Observer>> observers_;
+ Mutex audio_consumers_lock_;
+ HashSet<AudioDestinationConsumer*> audio_consumers_;
+ std::unique_ptr<ExtraData> extra_data_;
+ WebMediaConstraints constraints_;
+ WebMediaStreamSource::Capabilities capabilities_;
+ Optional<bool> echo_cancellation_;
+ Optional<bool> auto_gain_control_;
+ Optional<bool> noise_supression_;
+};
+
+typedef HeapVector<Member<MediaStreamSource>> MediaStreamSourceVector;
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIASTREAM_MEDIA_STREAM_SOURCE_H_
diff --git a/chromium/third_party/blink/renderer/platform/mediastream/media_stream_web_audio_source.cc b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_web_audio_source.cc
new file mode 100644
index 00000000000..b21a41baf7d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_web_audio_source.cc
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2014 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:
+ *
+ * 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 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 <memory>
+#include "third_party/blink/public/platform/web_audio_source_provider.h"
+#include "third_party/blink/renderer/platform/audio/audio_bus.h"
+#include "third_party/blink/renderer/platform/mediastream/media_stream_web_audio_source.h"
+
+namespace blink {
+
+MediaStreamWebAudioSource::MediaStreamWebAudioSource(
+ std::unique_ptr<WebAudioSourceProvider> provider)
+ : web_audio_source_provider_(std::move(provider)) {}
+
+MediaStreamWebAudioSource::~MediaStreamWebAudioSource() = default;
+
+void MediaStreamWebAudioSource::ProvideInput(AudioBus* bus,
+ size_t frames_to_process) {
+ DCHECK(bus);
+ if (!bus)
+ return;
+
+ if (!web_audio_source_provider_) {
+ bus->Zero();
+ return;
+ }
+
+ // Wrap the AudioBus channel data using WebVector.
+ size_t n = bus->NumberOfChannels();
+ WebVector<float*> web_audio_data(n);
+ for (size_t i = 0; i < n; ++i)
+ web_audio_data[i] = bus->Channel(i)->MutableData();
+
+ web_audio_source_provider_->ProvideInput(web_audio_data, frames_to_process);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/mediastream/media_stream_web_audio_source.h b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_web_audio_source.h
new file mode 100644
index 00000000000..ba2ba04b275
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_web_audio_source.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2014 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:
+ *
+ * 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 Ericsson 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIASTREAM_MEDIA_STREAM_WEB_AUDIO_SOURCE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIASTREAM_MEDIA_STREAM_WEB_AUDIO_SOURCE_H_
+
+#include <memory>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/platform/audio/audio_source_provider.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
+
+namespace blink {
+
+class WebAudioSourceProvider;
+
+class MediaStreamWebAudioSource : public AudioSourceProvider {
+ WTF_MAKE_NONCOPYABLE(MediaStreamWebAudioSource);
+
+ public:
+ static std::unique_ptr<MediaStreamWebAudioSource> Create(
+ std::unique_ptr<WebAudioSourceProvider> provider) {
+ return base::WrapUnique(new MediaStreamWebAudioSource(std::move(provider)));
+ }
+
+ ~MediaStreamWebAudioSource() override;
+
+ private:
+ explicit MediaStreamWebAudioSource(std::unique_ptr<WebAudioSourceProvider>);
+
+ // blink::AudioSourceProvider implementation.
+ void ProvideInput(AudioBus*, size_t frames_to_process) override;
+
+ std::unique_ptr<WebAudioSourceProvider> web_audio_source_provider_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIASTREAM_MEDIA_STREAM_WEB_AUDIO_SOURCE_H_
diff --git a/chromium/third_party/blink/renderer/platform/memory_coordinator.cc b/chromium/third_party/blink/renderer/platform/memory_coordinator.cc
new file mode 100644
index 00000000000..67f9647adf1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/memory_coordinator.cc
@@ -0,0 +1,139 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/memory_coordinator.h"
+
+#include "base/sys_info.h"
+#include "build/build_config.h"
+#include "third_party/blink/public/common/device_memory/approximated_device_memory.h"
+#include "third_party/blink/public/platform/web_thread.h"
+#include "third_party/blink/public/web/blink.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/fonts/font_global_context.h"
+#include "third_party/blink/renderer/platform/graphics/image_decoding_store.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/web_task_runner.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
+
+#if defined(OS_ANDROID)
+#include "base/android/sys_utils.h"
+#endif
+
+namespace blink {
+
+// Wrapper function defined in WebKit.h
+void DecommitFreeableMemory() {
+ WTF::Partitions::DecommitFreeableMemory();
+}
+
+// static
+bool MemoryCoordinator::is_low_end_device_ = false;
+
+// static
+bool MemoryCoordinator::IsLowEndDevice() {
+ return is_low_end_device_;
+}
+
+// static
+bool MemoryCoordinator::IsCurrentlyLowMemory() {
+#if defined(OS_ANDROID)
+ return base::android::SysUtils::IsCurrentlyLowMemory();
+#else
+ return false;
+#endif
+}
+
+// static
+void MemoryCoordinator::Initialize() {
+ is_low_end_device_ = ::base::SysInfo::IsLowEndDevice();
+ ApproximatedDeviceMemory::Initialize();
+}
+
+// static
+void MemoryCoordinator::SetIsLowEndDeviceForTesting(bool is_low_end_device) {
+ is_low_end_device_ = is_low_end_device;
+}
+
+// static
+MemoryCoordinator& MemoryCoordinator::Instance() {
+ DEFINE_STATIC_LOCAL(Persistent<MemoryCoordinator>, external,
+ (new MemoryCoordinator));
+ DCHECK(IsMainThread());
+ return *external.Get();
+}
+
+void MemoryCoordinator::RegisterThread(WebThread* thread) {
+ MemoryCoordinator::Instance().web_threads_.insert(thread);
+}
+
+void MemoryCoordinator::UnregisterThread(WebThread* thread) {
+ MemoryCoordinator::Instance().web_threads_.erase(thread);
+}
+
+MemoryCoordinator::MemoryCoordinator() = default;
+
+void MemoryCoordinator::RegisterClient(MemoryCoordinatorClient* client) {
+ DCHECK(IsMainThread());
+ DCHECK(client);
+ DCHECK(!clients_.Contains(client));
+ clients_.insert(client);
+}
+
+void MemoryCoordinator::UnregisterClient(MemoryCoordinatorClient* client) {
+ DCHECK(IsMainThread());
+ clients_.erase(client);
+}
+
+void MemoryCoordinator::OnMemoryPressure(WebMemoryPressureLevel level) {
+ TRACE_EVENT0("blink", "MemoryCoordinator::onMemoryPressure");
+ for (auto& client : clients_)
+ client->OnMemoryPressure(level);
+ if (level == kWebMemoryPressureLevelCritical)
+ ClearMemory();
+ WTF::Partitions::DecommitFreeableMemory();
+}
+
+void MemoryCoordinator::OnMemoryStateChange(MemoryState state) {
+ for (auto& client : clients_)
+ client->OnMemoryStateChange(state);
+}
+
+void MemoryCoordinator::OnPurgeMemory() {
+ for (auto& client : clients_)
+ client->OnPurgeMemory();
+ // Don't call clearMemory() because font cache invalidation always causes full
+ // layout. This increases tab switching cost significantly (e.g.
+ // en.wikipedia.org/wiki/Wikipedia). So we should not invalidate the font
+ // cache in purge+throttle.
+ ImageDecodingStore::Instance().Clear();
+ WTF::Partitions::DecommitFreeableMemory();
+
+ // Thread-specific data never issues a layout, so we are safe here.
+ for (auto thread : web_threads_) {
+ if (!thread->GetTaskRunner())
+ continue;
+
+ PostCrossThreadTask(
+ *thread->GetTaskRunner(), FROM_HERE,
+ CrossThreadBind(MemoryCoordinator::ClearThreadSpecificMemory));
+ }
+}
+
+void MemoryCoordinator::ClearMemory() {
+ // Clear the image cache.
+ // TODO(tasak|bashi): Make ImageDecodingStore and FontCache be
+ // MemoryCoordinatorClients rather than clearing caches here.
+ ImageDecodingStore::Instance().Clear();
+ FontGlobalContext::ClearMemory();
+}
+
+void MemoryCoordinator::ClearThreadSpecificMemory() {
+ FontGlobalContext::ClearMemory();
+}
+
+void MemoryCoordinator::Trace(blink::Visitor* visitor) {
+ visitor->Trace(clients_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/memory_coordinator.h b/chromium/third_party/blink/renderer/platform/memory_coordinator.h
new file mode 100644
index 00000000000..7e0bbf2ccdd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/memory_coordinator.h
@@ -0,0 +1,88 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEMORY_COORDINATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEMORY_COORDINATOR_H_
+
+#include "third_party/blink/public/platform/web_memory_pressure_level.h"
+#include "third_party/blink/public/platform/web_memory_state.h"
+#include "third_party/blink/public/platform/web_thread.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT MemoryCoordinatorClient : public GarbageCollectedMixin {
+ public:
+ virtual ~MemoryCoordinatorClient() = default;
+
+ // TODO(bashi): Deprecating. Remove this when MemoryPressureListener is
+ // gone.
+ virtual void OnMemoryPressure(WebMemoryPressureLevel) {}
+
+ virtual void OnMemoryStateChange(MemoryState) {}
+
+ virtual void OnPurgeMemory() {}
+};
+
+// MemoryCoordinator listens to some events which could be opportunities
+// for reducing memory consumption and notifies its clients.
+class PLATFORM_EXPORT MemoryCoordinator final
+ : public GarbageCollectedFinalized<MemoryCoordinator> {
+ WTF_MAKE_NONCOPYABLE(MemoryCoordinator);
+
+ public:
+ static MemoryCoordinator& Instance();
+
+ // Whether the device Blink runs on is a low-end device.
+ // Can be overridden in layout tests via internals.
+ static bool IsLowEndDevice();
+
+ // Returns true when available memory is low.
+ // This is not cheap and should not be called repeatedly.
+ // TODO(keishi): Remove when MemoryState is ready.
+ static bool IsCurrentlyLowMemory();
+
+ // Caches whether this device is a low-end device and the device physical
+ // memory in static members. instance() is not used as it's a heap allocated
+ // object - meaning it's not thread-safe as well as might break tests counting
+ // the heap size.
+ static void Initialize();
+
+ static void RegisterThread(WebThread*);
+ static void UnregisterThread(WebThread*);
+
+ void RegisterClient(MemoryCoordinatorClient*);
+ void UnregisterClient(MemoryCoordinatorClient*);
+
+ // TODO(bashi): Deprecating. Remove this when MemoryPressureListener is
+ // gone.
+ void OnMemoryPressure(WebMemoryPressureLevel);
+
+ void OnMemoryStateChange(MemoryState);
+
+ void OnPurgeMemory();
+
+ void Trace(blink::Visitor*);
+
+ private:
+ friend class Internals;
+
+ static void SetIsLowEndDeviceForTesting(bool);
+
+ MemoryCoordinator();
+
+ void ClearMemory();
+ static void ClearThreadSpecificMemory();
+
+ static bool is_low_end_device_;
+
+ HeapHashSet<WeakMember<MemoryCoordinatorClient>> clients_;
+ HashSet<WebThread*> web_threads_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEMORY_COORDINATOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/mhtml/archive_resource.cc b/chromium/third_party/blink/renderer/platform/mhtml/archive_resource.cc
new file mode 100644
index 00000000000..9e6003dcbdb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mhtml/archive_resource.cc
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2008, 2010 Apple 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/mhtml/archive_resource.h"
+
+namespace blink {
+
+ArchiveResource::ArchiveResource(scoped_refptr<SharedBuffer> data,
+ const KURL& url,
+ const String& content_id,
+ const AtomicString& mime_type,
+ const AtomicString& text_encoding)
+ : url_(url),
+ content_id_(content_id),
+ data_(std::move(data)),
+ mime_type_(mime_type),
+ text_encoding_(text_encoding) {
+ DCHECK(data_);
+}
+
+ArchiveResource::~ArchiveResource() = default;
+
+ArchiveResource* ArchiveResource::Create(scoped_refptr<SharedBuffer> data,
+ const KURL& url,
+ const String& content_id,
+ const AtomicString& mime_type,
+ const AtomicString& text_encoding) {
+ return new ArchiveResource(std::move(data), url, content_id, mime_type,
+ text_encoding);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/mhtml/archive_resource.h b/chromium/third_party/blink/renderer/platform/mhtml/archive_resource.h
new file mode 100644
index 00000000000..9c012991d36
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mhtml/archive_resource.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2008, 2010 Apple 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_MHTML_ARCHIVE_RESOURCE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MHTML_ARCHIVE_RESOURCE_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT ArchiveResource final
+ : public GarbageCollectedFinalized<ArchiveResource> {
+ public:
+ static ArchiveResource* Create(scoped_refptr<SharedBuffer>,
+ const KURL&,
+ const String& content_id,
+ const AtomicString& mime_type,
+ const AtomicString& text_encoding);
+
+ ~ArchiveResource();
+
+ const KURL& Url() const { return url_; }
+ const String& ContentID() const { return content_id_; }
+ SharedBuffer* Data() const { return data_.get(); }
+ const AtomicString& MimeType() const { return mime_type_; }
+ const AtomicString& TextEncoding() const { return text_encoding_; }
+
+ void Trace(blink::Visitor* visitor) {}
+
+ private:
+ ArchiveResource(scoped_refptr<SharedBuffer>,
+ const KURL&,
+ const String& content_id,
+ const AtomicString& mime_type,
+ const AtomicString& text_encoding);
+
+ KURL url_;
+ String content_id_;
+ scoped_refptr<SharedBuffer> data_;
+ AtomicString mime_type_;
+ AtomicString text_encoding_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MHTML_ARCHIVE_RESOURCE_H_
diff --git a/chromium/third_party/blink/renderer/platform/mhtml/mhtml_archive.cc b/chromium/third_party/blink/renderer/platform/mhtml/mhtml_archive.cc
new file mode 100644
index 00000000000..9319926f6c7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mhtml/mhtml_archive.cc
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/renderer/platform/mhtml/mhtml_archive.h"
+
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/date_components.h"
+#include "third_party/blink/renderer/platform/mhtml/archive_resource.h"
+#include "third_party/blink/renderer/platform/mhtml/mhtml_parser.h"
+#include "third_party/blink/renderer/platform/network/mime/mime_type_registry.h"
+#include "third_party/blink/renderer/platform/serialized_resource.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/text/quoted_printable.h"
+#include "third_party/blink/renderer/platform/weborigin/scheme_registry.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/cryptographically_random_number.h"
+#include "third_party/blink/renderer/platform/wtf/date_math.h"
+#include "third_party/blink/renderer/platform/wtf/text/base64.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+namespace {
+
+const size_t kMaximumLineLength = 76;
+
+const char kRFC2047EncodingPrefix[] = "=?utf-8?Q?";
+const size_t kRFC2047EncodingPrefixLength = 10;
+const char kRFC2047EncodingSuffix[] = "?=";
+const size_t kRFC2047EncodingSuffixLength = 2;
+
+const char kQuotedPrintable[] = "quoted-printable";
+const char kBase64[] = "base64";
+const char kBinary[] = "binary";
+
+} // namespace
+
+// Controls quoted-printable encoding characters in body, per RFC 2045.
+class QuotedPrintableEncodeBodyDelegate : public QuotedPrintableEncodeDelegate {
+ public:
+ QuotedPrintableEncodeBodyDelegate() = default;
+ ~QuotedPrintableEncodeBodyDelegate() override = default;
+
+ size_t GetMaxLineLengthForEncodedContent() const override {
+ return kMaximumLineLength;
+ }
+
+ bool ShouldEncodeWhiteSpaceCharacters(bool end_of_line) const override {
+ // They should be encoded only if they appear at the end of a body line.
+ return end_of_line;
+ }
+
+ void DidStartLine(Vector<char>& out) override {
+ // Nothing to add.
+ }
+
+ void DidFinishLine(bool last_line, Vector<char>& out) override {
+ if (!last_line) {
+ out.push_back('=');
+ out.Append("\r\n", 2);
+ }
+ }
+};
+
+// Controls quoted-printable encoding characters in headers, per RFC 2047.
+class QuotedPrintableEncodeHeaderDelegate
+ : public QuotedPrintableEncodeDelegate {
+ public:
+ QuotedPrintableEncodeHeaderDelegate() = default;
+ ~QuotedPrintableEncodeHeaderDelegate() override = default;
+
+ size_t GetMaxLineLengthForEncodedContent() const override {
+ return kMaximumLineLength - kRFC2047EncodingPrefixLength -
+ kRFC2047EncodingSuffixLength;
+ }
+
+ bool ShouldEncodeWhiteSpaceCharacters(bool end_of_line) const override {
+ // They should always be encoded if they appear anywhere in the header.
+ return true;
+ }
+
+ void DidStartLine(Vector<char>& out) override {
+ out.Append(kRFC2047EncodingPrefix, kRFC2047EncodingPrefixLength);
+ }
+
+ void DidFinishLine(bool last_line, Vector<char>& out) override {
+ out.Append(kRFC2047EncodingSuffix, kRFC2047EncodingSuffixLength);
+ if (!last_line) {
+ out.Append("\r\n", 2);
+ out.push_back(' ');
+ }
+ }
+};
+
+static String ConvertToPrintableCharacters(const String& text) {
+ // If the text contains all printable ASCII characters, no need for encoding.
+ bool found_non_printable_char = false;
+ for (size_t i = 0; i < text.length(); ++i) {
+ if (!IsASCIIPrintable(text[i])) {
+ found_non_printable_char = true;
+ break;
+ }
+ }
+ if (!found_non_printable_char)
+ return text;
+
+ // Encode the text as sequences of printable ASCII characters per RFC 2047
+ // (https://tools.ietf.org/html/rfc2047). Specially, the encoded text will be
+ // as: =?utf-8?Q?encoded_text?=
+ // where, "utf-8" is the chosen charset to represent the text and "Q" is the
+ // Quoted-Printable format to convert to 7-bit printable ASCII characters.
+ CString utf8_text = text.Utf8();
+ QuotedPrintableEncodeHeaderDelegate header_delegate;
+ Vector<char> encoded_text;
+ QuotedPrintableEncode(utf8_text.data(), utf8_text.length(), &header_delegate,
+ encoded_text);
+ return String(encoded_text.data(), encoded_text.size());
+}
+
+MHTMLArchive::MHTMLArchive() = default;
+
+MHTMLArchive* MHTMLArchive::Create(const KURL& url,
+ scoped_refptr<const SharedBuffer> data) {
+ // MHTML pages can only be loaded from local URLs, http/https URLs, and
+ // content URLs(Android specific). The latter is now allowed due to full
+ // sandboxing enforcement on MHTML pages.
+ if (!CanLoadArchive(url))
+ return nullptr;
+
+ MHTMLParser parser(std::move(data));
+ HeapVector<Member<ArchiveResource>> resources = parser.ParseArchive();
+ if (resources.IsEmpty())
+ return nullptr; // Invalid MHTML file.
+
+ MHTMLArchive* archive = new MHTMLArchive;
+ archive->date_ = parser.CreationDate();
+
+ size_t resources_count = resources.size();
+ // The first document suitable resource is the main resource of the top frame.
+ for (size_t i = 0; i < resources_count; ++i) {
+ if (archive->MainResource()) {
+ archive->AddSubresource(resources[i].Get());
+ continue;
+ }
+
+ const AtomicString& mime_type = resources[i]->MimeType();
+ bool is_mime_type_suitable_for_main_resource =
+ MIMETypeRegistry::IsSupportedNonImageMIMEType(mime_type);
+ // Want to allow image-only MHTML archives, but retain behavior for other
+ // documents that have already been created expecting the first HTML page to
+ // be considered the main resource.
+ if (resources_count == 1 &&
+ MIMETypeRegistry::IsSupportedImageResourceMIMEType(mime_type)) {
+ is_mime_type_suitable_for_main_resource = true;
+ }
+ // explicitly disallow JS and CSS as the main resource.
+ if (MIMETypeRegistry::IsSupportedJavaScriptMIMEType(mime_type) ||
+ MIMETypeRegistry::IsSupportedStyleSheetMIMEType(mime_type))
+ is_mime_type_suitable_for_main_resource = false;
+
+ if (is_mime_type_suitable_for_main_resource)
+ archive->SetMainResource(resources[i].Get());
+ else
+ archive->AddSubresource(resources[i].Get());
+ }
+ return archive;
+}
+
+bool MHTMLArchive::CanLoadArchive(const KURL& url) {
+ // MHTML pages can only be loaded from local URLs, http/https URLs, and
+ // content URLs(Android specific). The latter is now allowed due to full
+ // sandboxing enforcement on MHTML pages.
+ if (SchemeRegistry::ShouldTreatURLSchemeAsLocal(url.Protocol()))
+ return true;
+ if (url.ProtocolIsInHTTPFamily())
+ return true;
+#if defined(OS_ANDROID)
+ if (url.ProtocolIs("content"))
+ return true;
+#endif
+ return false;
+}
+
+void MHTMLArchive::GenerateMHTMLHeader(const String& boundary,
+ const KURL& url,
+ const String& title,
+ const String& mime_type,
+ WTF::Time date,
+ Vector<char>& output_buffer) {
+ DCHECK(!boundary.IsEmpty());
+ DCHECK(!mime_type.IsEmpty());
+
+ // TODO(lukasza): Passing individual date/time components seems fragile.
+ base::Time::Exploded date_components;
+ date.UTCExplode(&date_components);
+ String date_string = MakeRFC2822DateString(
+ date_components.day_of_week, date_components.day_of_month,
+ // |month| is 1-based in Exploded, but 0-based in MakeRFC2822DateString.
+ date_components.month - 1, date_components.year, date_components.hour,
+ date_components.minute, date_components.second, 0);
+
+ StringBuilder string_builder;
+ string_builder.Append("From: <Saved by Blink>\r\n");
+
+ // Add the document URL in the MHTML headers in order to avoid complicated
+ // parsing to locate it in the multipart body headers.
+ string_builder.Append("Snapshot-Content-Location: ");
+ string_builder.Append(url.GetString());
+
+ string_builder.Append("\r\nSubject: ");
+ string_builder.Append(ConvertToPrintableCharacters(title));
+ string_builder.Append("\r\nDate: ");
+ string_builder.Append(date_string);
+ string_builder.Append("\r\nMIME-Version: 1.0\r\n");
+ string_builder.Append("Content-Type: multipart/related;\r\n");
+ string_builder.Append("\ttype=\"");
+ string_builder.Append(mime_type);
+ string_builder.Append("\";\r\n");
+ string_builder.Append("\tboundary=\"");
+ string_builder.Append(boundary);
+ string_builder.Append("\"\r\n\r\n");
+
+ // We use utf8() below instead of ascii() as ascii() replaces CRLFs with ??
+ // (we still only have put ASCII characters in it).
+ DCHECK(string_builder.ToString().ContainsOnlyASCII());
+ CString ascii_string = string_builder.ToString().Utf8();
+
+ output_buffer.Append(ascii_string.data(), ascii_string.length());
+}
+
+void MHTMLArchive::GenerateMHTMLPart(const String& boundary,
+ const String& content_id,
+ EncodingPolicy encoding_policy,
+ const SerializedResource& resource,
+ Vector<char>& output_buffer) {
+ DCHECK(!boundary.IsEmpty());
+ DCHECK(content_id.IsEmpty() || content_id[0] == '<');
+
+ StringBuilder string_builder;
+ // Per the spec, the boundary must occur at the beginning of a line.
+ string_builder.Append("\r\n--");
+ string_builder.Append(boundary);
+ string_builder.Append("\r\n");
+
+ string_builder.Append("Content-Type: ");
+ string_builder.Append(resource.mime_type);
+ string_builder.Append("\r\n");
+
+ if (!content_id.IsEmpty()) {
+ string_builder.Append("Content-ID: ");
+ string_builder.Append(content_id);
+ string_builder.Append("\r\n");
+ }
+
+ const char* content_encoding = nullptr;
+ if (encoding_policy == kUseBinaryEncoding)
+ content_encoding = kBinary;
+ else if (MIMETypeRegistry::IsSupportedJavaScriptMIMEType(
+ resource.mime_type) ||
+ MIMETypeRegistry::IsSupportedNonImageMIMEType(resource.mime_type))
+ content_encoding = kQuotedPrintable;
+ else
+ content_encoding = kBase64;
+
+ string_builder.Append("Content-Transfer-Encoding: ");
+ string_builder.Append(content_encoding);
+ string_builder.Append("\r\n");
+
+ if (!resource.url.ProtocolIsAbout()) {
+ string_builder.Append("Content-Location: ");
+ string_builder.Append(resource.url.GetString());
+ string_builder.Append("\r\n");
+ }
+
+ string_builder.Append("\r\n");
+
+ CString ascii_string = string_builder.ToString().Utf8();
+ output_buffer.Append(ascii_string.data(), ascii_string.length());
+
+ if (!strcmp(content_encoding, kBinary)) {
+ const char* data;
+ size_t position = 0;
+ while (size_t length = resource.data->GetSomeData(data, position)) {
+ output_buffer.Append(data, length);
+ position += length;
+ }
+ } else {
+ // FIXME: ideally we would encode the content as a stream without having to
+ // fetch it all.
+ const SharedBuffer::DeprecatedFlatData flat_data(resource.data);
+ const char* data = flat_data.Data();
+ size_t data_length = flat_data.size();
+ Vector<char> encoded_data;
+ if (!strcmp(content_encoding, kQuotedPrintable)) {
+ QuotedPrintableEncodeBodyDelegate body_delegate;
+ QuotedPrintableEncode(data, data_length, &body_delegate, encoded_data);
+ output_buffer.Append(encoded_data.data(), encoded_data.size());
+ } else {
+ DCHECK(!strcmp(content_encoding, kBase64));
+ // We are not specifying insertLFs = true below as it would cut the lines
+ // with LFs and MHTML requires CRLFs.
+ Base64Encode(data, data_length, encoded_data);
+ size_t index = 0;
+ size_t encoded_data_length = encoded_data.size();
+ do {
+ size_t line_length =
+ std::min(encoded_data_length - index, kMaximumLineLength);
+ output_buffer.Append(encoded_data.data() + index, line_length);
+ output_buffer.Append("\r\n", 2u);
+ index += kMaximumLineLength;
+ } while (index < encoded_data_length);
+ }
+ }
+}
+
+void MHTMLArchive::GenerateMHTMLFooterForTesting(const String& boundary,
+ Vector<char>& output_buffer) {
+ DCHECK(!boundary.IsEmpty());
+ CString ascii_string = String("\r\n--" + boundary + "--\r\n").Utf8();
+ output_buffer.Append(ascii_string.data(), ascii_string.length());
+}
+
+void MHTMLArchive::SetMainResource(ArchiveResource* main_resource) {
+ main_resource_ = main_resource;
+}
+
+void MHTMLArchive::AddSubresource(ArchiveResource* resource) {
+ const KURL& url = resource->Url();
+ subresources_.Set(url, resource);
+ KURL cid_uri = MHTMLParser::ConvertContentIDToURI(resource->ContentID());
+ if (cid_uri.IsValid())
+ subresources_.Set(cid_uri, resource);
+}
+
+ArchiveResource* MHTMLArchive::SubresourceForURL(const KURL& url) const {
+ return subresources_.at(url.GetString());
+}
+
+void MHTMLArchive::Trace(blink::Visitor* visitor) {
+ visitor->Trace(main_resource_);
+ visitor->Trace(subresources_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/mhtml/mhtml_archive.h b/chromium/third_party/blink/renderer/platform/mhtml/mhtml_archive.h
new file mode 100644
index 00000000000..da2d415b334
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mhtml/mhtml_archive.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_MHTML_MHTML_ARCHIVE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MHTML_MHTML_ARCHIVE_H_
+
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+// When URIs use the "cid" (Content-ID) scheme, the resource refers to a
+// specific body part in an MHTML multipart/related structure.
+// See RFC n°2557, section-8.3: "Use of the Content-ID header and CID URLs".
+const char kContentIdScheme[] = "cid";
+
+class ArchiveResource;
+class KURL;
+class SharedBuffer;
+
+struct SerializedResource;
+
+class PLATFORM_EXPORT MHTMLArchive final
+ : public GarbageCollected<MHTMLArchive> {
+ public:
+ static MHTMLArchive* Create(const KURL&, scoped_refptr<const SharedBuffer>);
+
+ // Binary encoding results in smaller MHTML files but they might not work in
+ // other browsers.
+ enum EncodingPolicy { kUseDefaultEncoding, kUseBinaryEncoding };
+
+ // Generates an MHTML header and appends it to |outputBuffer|.
+ //
+ // Same |boundary| needs to be used for all generateMHTMLHeader and
+ // generateMHTMLPart and generateMHTMLFooter calls that belong to the same
+ // MHTML document (see also rfc1341, section 7.2.1, "boundary" description).
+ static void GenerateMHTMLHeader(const String& boundary,
+ const KURL&,
+ const String& title,
+ const String& mime_type,
+ WTF::Time date,
+ Vector<char>& output_buffer);
+
+ // Serializes SerializedResource as an MHTML part and appends it in
+ // |outputBuffer|.
+ //
+ // Same |boundary| needs to be used for all generateMHTMLHeader and
+ // generateMHTMLPart and generateMHTMLFooter calls that belong to the same
+ // MHTML document (see also rfc1341, section 7.2.1, "boundary" description).
+ //
+ // If |contentID| is non-empty, then it will be used as a Content-ID header.
+ // See rfc2557 - section 8.3 - "Use of the Content-ID header and CID URLs".
+ static void GenerateMHTMLPart(const String& boundary,
+ const String& content_id,
+ EncodingPolicy,
+ const SerializedResource&,
+ Vector<char>& output_buffer);
+
+ // Generates an MHTML footer and appends it to |outputBuffer| for testing
+ // purposes.
+ //
+ // Same |boundary| needs to be used for all generateMHTMLHeader and
+ // generateMHTMLPart and generateMHTMLFooter calls that belong to the same
+ // MHTML document (see also rfc1341, section 7.2.1, "boundary" description).
+ static void GenerateMHTMLFooterForTesting(const String& boundary,
+ Vector<char>& output_buffer);
+
+ typedef HeapHashMap<String, Member<ArchiveResource>> SubArchiveResources;
+
+ ArchiveResource* MainResource() const { return main_resource_.Get(); }
+ ArchiveResource* SubresourceForURL(const KURL&) const;
+
+ // The purported creation date (as expressed by the Date: header).
+ WTF::Time Date() const { return date_; }
+
+ void Trace(blink::Visitor*);
+
+ private:
+ MHTMLArchive();
+
+ void SetMainResource(ArchiveResource*);
+ void AddSubresource(ArchiveResource*);
+ static bool CanLoadArchive(const KURL&);
+
+ WTF::Time date_;
+ Member<ArchiveResource> main_resource_;
+ SubArchiveResources subresources_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/mhtml/mhtml_fuzzer.cc b/chromium/third_party/blink/renderer/platform/mhtml/mhtml_fuzzer.cc
new file mode 100644
index 00000000000..41aca59a600
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mhtml/mhtml_fuzzer.cc
@@ -0,0 +1,31 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/mhtml/archive_resource.h"
+#include "third_party/blink/renderer/platform/mhtml/mhtml_parser.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.h"
+
+namespace blink {
+
+// Fuzzer for blink::MHTMLParser.
+int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ static BlinkFuzzerTestSupport test_support = BlinkFuzzerTestSupport();
+ MHTMLParser mhtml_parser(SharedBuffer::Create(data, size));
+ HeapVector<Member<ArchiveResource>> mhtml_archives =
+ mhtml_parser.ParseArchive();
+ mhtml_archives.clear();
+ ThreadState::Current()->CollectAllGarbage();
+
+ return 0;
+}
+
+} // namespace blink
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ return blink::LLVMFuzzerTestOneInput(data, size);
+}
diff --git a/chromium/third_party/blink/renderer/platform/mhtml/mhtml_parser.cc b/chromium/third_party/blink/renderer/platform/mhtml/mhtml_parser.cc
new file mode 100644
index 00000000000..be093a307ca
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mhtml/mhtml_parser.cc
@@ -0,0 +1,439 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/renderer/platform/mhtml/mhtml_parser.h"
+
+#include "third_party/blink/renderer/platform/mhtml/archive_resource.h"
+#include "third_party/blink/renderer/platform/network/http_parsers.h"
+#include "third_party/blink/renderer/platform/network/parsed_content_type.h"
+#include "third_party/blink/renderer/platform/text/quoted_printable.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/text/base64.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_concatenate.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+// This class is a limited MIME parser used to parse the MIME headers of MHTML
+// files.
+class MIMEHeader : public GarbageCollectedFinalized<MIMEHeader> {
+ public:
+ static MIMEHeader* Create() { return new MIMEHeader; }
+
+ enum Encoding {
+ kQuotedPrintable,
+ kBase64,
+ kEightBit,
+ kSevenBit,
+ kBinary,
+ kUnknown
+ };
+
+ static MIMEHeader* ParseHeader(SharedBufferChunkReader* cr_lf_line_reader);
+
+ bool IsMultipart() const {
+ return content_type_.StartsWithIgnoringASCIICase("multipart/");
+ }
+
+ String ContentType() const { return content_type_; }
+ String Charset() const { return charset_; }
+ Encoding ContentTransferEncoding() const {
+ return content_transfer_encoding_;
+ }
+ String ContentLocation() const { return content_location_; }
+ String ContentID() const { return content_id_; }
+ WTF::Time Date() const { return date_; }
+
+ // Multi-part type and boundaries are only valid for multipart MIME headers.
+ String MultiPartType() const { return multipart_type_; }
+ String EndOfPartBoundary() const { return end_of_part_boundary_; }
+ String EndOfDocumentBoundary() const { return end_of_document_boundary_; }
+
+ void Trace(blink::Visitor* visitor) {}
+
+ private:
+ MIMEHeader();
+
+ static Encoding ParseContentTransferEncoding(const String&);
+
+ String content_type_;
+ String charset_;
+ Encoding content_transfer_encoding_;
+ String content_location_;
+ String content_id_;
+ WTF::Time date_;
+ String multipart_type_;
+ String end_of_part_boundary_;
+ String end_of_document_boundary_;
+};
+
+typedef HashMap<String, String> KeyValueMap;
+
+static KeyValueMap RetrieveKeyValuePairs(SharedBufferChunkReader* buffer) {
+ KeyValueMap key_value_pairs;
+ String line;
+ String key;
+ StringBuilder value;
+ while (!(line = buffer->NextChunkAsUTF8StringWithLatin1Fallback()).IsNull()) {
+ if (line.IsEmpty())
+ break; // Empty line means end of key/value section.
+ if (line[0] == '\t') {
+ value.Append(line.Substring(1));
+ continue;
+ }
+ // New key/value, store the previous one if any.
+ if (!key.IsEmpty()) {
+ if (key_value_pairs.find(key) != key_value_pairs.end())
+ DVLOG(1) << "Key duplicate found in MIME header. Key is '" << key
+ << "', previous value replaced.";
+ key_value_pairs.insert(key, value.ToString().StripWhiteSpace());
+ key = String();
+ value.Clear();
+ }
+ size_t semi_colon_index = line.find(':');
+ if (semi_colon_index == kNotFound) {
+ // This is not a key value pair, ignore.
+ continue;
+ }
+ key =
+ line.Substring(0, semi_colon_index).DeprecatedLower().StripWhiteSpace();
+ value.Append(line.Substring(semi_colon_index + 1));
+ }
+ // Store the last property if there is one.
+ if (!key.IsEmpty())
+ key_value_pairs.Set(key, value.ToString().StripWhiteSpace());
+ return key_value_pairs;
+}
+
+MIMEHeader* MIMEHeader::ParseHeader(SharedBufferChunkReader* buffer) {
+ MIMEHeader* mime_header = MIMEHeader::Create();
+ KeyValueMap key_value_pairs = RetrieveKeyValuePairs(buffer);
+ KeyValueMap::iterator mime_parameters_iterator =
+ key_value_pairs.find("content-type");
+ if (mime_parameters_iterator != key_value_pairs.end()) {
+ ParsedContentType parsed_content_type(mime_parameters_iterator->value,
+ ParsedContentType::Mode::kRelaxed);
+ mime_header->content_type_ = parsed_content_type.MimeType();
+ if (!mime_header->IsMultipart()) {
+ mime_header->charset_ = parsed_content_type.Charset().StripWhiteSpace();
+ } else {
+ mime_header->multipart_type_ =
+ parsed_content_type.ParameterValueForName("type");
+ mime_header->end_of_part_boundary_ =
+ parsed_content_type.ParameterValueForName("boundary");
+ if (mime_header->end_of_part_boundary_.IsNull()) {
+ DVLOG(1) << "No boundary found in multipart MIME header.";
+ return nullptr;
+ }
+ mime_header->end_of_part_boundary_.insert("--", 0);
+ mime_header->end_of_document_boundary_ =
+ mime_header->end_of_part_boundary_;
+ mime_header->end_of_document_boundary_.append("--");
+ }
+ }
+
+ mime_parameters_iterator = key_value_pairs.find("content-transfer-encoding");
+ if (mime_parameters_iterator != key_value_pairs.end())
+ mime_header->content_transfer_encoding_ =
+ ParseContentTransferEncoding(mime_parameters_iterator->value);
+
+ mime_parameters_iterator = key_value_pairs.find("content-location");
+ if (mime_parameters_iterator != key_value_pairs.end())
+ mime_header->content_location_ = mime_parameters_iterator->value;
+
+ // See rfc2557 - section 8.3 - Use of the Content-ID header and CID URLs.
+ mime_parameters_iterator = key_value_pairs.find("content-id");
+ if (mime_parameters_iterator != key_value_pairs.end())
+ mime_header->content_id_ = mime_parameters_iterator->value;
+
+ mime_parameters_iterator = key_value_pairs.find("date");
+ if (mime_parameters_iterator != key_value_pairs.end()) {
+ WTF::Time parsed_time;
+ // Behave like //net and parse time-valued headers with a default time zone
+ // of UTC.
+ if (WTF::Time::FromUTCString(mime_parameters_iterator->value.Utf8().data(),
+ &parsed_time))
+ mime_header->date_ = parsed_time;
+ }
+
+ return mime_header;
+}
+
+MIMEHeader::Encoding MIMEHeader::ParseContentTransferEncoding(
+ const String& text) {
+ String encoding = text.StripWhiteSpace().DeprecatedLower();
+ if (encoding == "base64")
+ return kBase64;
+ if (encoding == "quoted-printable")
+ return kQuotedPrintable;
+ if (encoding == "8bit")
+ return kEightBit;
+ if (encoding == "7bit")
+ return kSevenBit;
+ if (encoding == "binary")
+ return kBinary;
+ DVLOG(1) << "Unknown encoding '" << text << "' found in MIME header.";
+ return kUnknown;
+}
+
+MIMEHeader::MIMEHeader() : content_transfer_encoding_(kUnknown) {}
+
+static bool SkipLinesUntilBoundaryFound(SharedBufferChunkReader& line_reader,
+ const String& boundary) {
+ String line;
+ while (!(line = line_reader.NextChunkAsUTF8StringWithLatin1Fallback())
+ .IsNull()) {
+ if (line == boundary)
+ return true;
+ }
+ return false;
+}
+
+MHTMLParser::MHTMLParser(scoped_refptr<const SharedBuffer> data)
+ : line_reader_(std::move(data), "\r\n") {}
+
+HeapVector<Member<ArchiveResource>> MHTMLParser::ParseArchive() {
+ MIMEHeader* header = MIMEHeader::ParseHeader(&line_reader_);
+ HeapVector<Member<ArchiveResource>> resources;
+ if (ParseArchiveWithHeader(header, resources)) {
+ creation_date_ = header->Date();
+ } else {
+ resources.clear();
+ }
+ return resources;
+}
+
+WTF::Time MHTMLParser::CreationDate() const {
+ return creation_date_;
+}
+
+bool MHTMLParser::ParseArchiveWithHeader(
+ MIMEHeader* header,
+ HeapVector<Member<ArchiveResource>>& resources) {
+ if (!header) {
+ DVLOG(1) << "Failed to parse MHTML part: no header.";
+ return false;
+ }
+
+ if (!header->IsMultipart()) {
+ // With IE a page with no resource is not multi-part.
+ bool end_of_archive_reached = false;
+ ArchiveResource* resource =
+ ParseNextPart(*header, String(), String(), end_of_archive_reached);
+ if (!resource)
+ return false;
+ resources.push_back(resource);
+ return true;
+ }
+
+ // Skip the message content (it's a generic browser specific message).
+ SkipLinesUntilBoundaryFound(line_reader_, header->EndOfPartBoundary());
+
+ bool end_of_archive = false;
+ while (!end_of_archive) {
+ MIMEHeader* resource_header = MIMEHeader::ParseHeader(&line_reader_);
+ if (!resource_header) {
+ DVLOG(1) << "Failed to parse MHTML, invalid MIME header.";
+ return false;
+ }
+ if (resource_header->ContentType() == "multipart/alternative") {
+ // Ignore IE nesting which makes little sense (IE seems to nest only some
+ // of the frames).
+ if (!ParseArchiveWithHeader(resource_header, resources)) {
+ DVLOG(1) << "Failed to parse MHTML subframe.";
+ return false;
+ }
+ SkipLinesUntilBoundaryFound(line_reader_, header->EndOfPartBoundary());
+ continue;
+ }
+
+ ArchiveResource* resource =
+ ParseNextPart(*resource_header, header->EndOfPartBoundary(),
+ header->EndOfDocumentBoundary(), end_of_archive);
+ if (!resource) {
+ DVLOG(1) << "Failed to parse MHTML part.";
+ return false;
+ }
+ resources.push_back(resource);
+ }
+ return true;
+}
+
+ArchiveResource* MHTMLParser::ParseNextPart(
+ const MIMEHeader& mime_header,
+ const String& end_of_part_boundary,
+ const String& end_of_document_boundary,
+ bool& end_of_archive_reached) {
+ DCHECK_EQ(end_of_part_boundary.IsEmpty(), end_of_document_boundary.IsEmpty());
+
+ // Per the spec, the bondary to separate parts should start with CRLF.
+ // |end_of_part_boundary| passed here does not contain CRLF at the beginning.
+ // The parsing logic below takes care of CRLF handling.
+
+ // If no content transfer encoding is specified, default to binary encoding.
+ MIMEHeader::Encoding content_transfer_encoding =
+ mime_header.ContentTransferEncoding();
+ if (content_transfer_encoding == MIMEHeader::kUnknown)
+ content_transfer_encoding = MIMEHeader::kBinary;
+
+ Vector<char> content;
+ const bool check_boundary = !end_of_part_boundary.IsEmpty();
+ bool end_of_part_reached = false;
+ if (content_transfer_encoding == MIMEHeader::kBinary) {
+ if (!check_boundary) {
+ DVLOG(1) << "Binary contents requires end of part";
+ return nullptr;
+ }
+ // Due to a bug in MHTMLArchive, CRLF was not added to the beginning of the
+ // boundary that is placed after the part encoded as binary. To handle both
+ // cases that CRLF may or may not be at the beginning of the boundary, we
+ // read the part content till reaching the boundary without CRLF. So the
+ // part content may contain CRLF at the end, which will be stripped off
+ // later.
+ line_reader_.SetSeparator(end_of_part_boundary.Utf8().data());
+ if (!line_reader_.NextChunk(content)) {
+ DVLOG(1) << "Binary contents requires end of part";
+ return nullptr;
+ }
+ line_reader_.SetSeparator("\r\n");
+
+ // Strip the CRLF from the end of the content if present.
+ // Note: it may be the case that CRLF stripped off is really part of the
+ // content, instead of part of the boundary.
+ // 1) If the content denotes text or html data, stripping off CRLF will
+ // normally bring no harm.
+ // 2) Otherwise, the content denotes image or other type of binary data.
+ // Usually it doesn't have CRLF at the end.
+ // In order to support parsing the MHTML archive file produced before the
+ // MHTMLArchive bug was fixed, we need to take a risk of stripping off the
+ // CRLF that indeed belongs to the content.
+ if (content.size() >= 2 && content[content.size() - 2] == '\r' &&
+ content[content.size() - 1] == '\n') {
+ content.resize(content.size() - 2);
+ }
+
+ Vector<char> next_chars;
+ if (line_reader_.Peek(next_chars, 2) != 2) {
+ DVLOG(1) << "Invalid seperator.";
+ return nullptr;
+ }
+ end_of_part_reached = true;
+ DCHECK(next_chars.size() == 2);
+ end_of_archive_reached = (next_chars[0] == '-' && next_chars[1] == '-');
+ if (!end_of_archive_reached) {
+ String line = line_reader_.NextChunkAsUTF8StringWithLatin1Fallback();
+ if (!line.IsEmpty()) {
+ DVLOG(1) << "No CRLF at end of binary section.";
+ return nullptr;
+ }
+ }
+ } else {
+ String line;
+ while (!(line = line_reader_.NextChunkAsUTF8StringWithLatin1Fallback())
+ .IsNull()) {
+ end_of_archive_reached = (line == end_of_document_boundary);
+ if (check_boundary &&
+ (line == end_of_part_boundary || end_of_archive_reached)) {
+ end_of_part_reached = true;
+ break;
+ }
+ // Note that we use line.utf8() and not line.ascii() as ascii turns
+ // special characters (such as tab, line-feed...) into '?'.
+ content.Append(line.Utf8().data(), line.length());
+ if (content_transfer_encoding == MIMEHeader::kQuotedPrintable) {
+ // The line reader removes the \r\n, but we need them for the content in
+ // this case as the QuotedPrintable decoder expects CR-LF terminated
+ // lines.
+ content.Append("\r\n", 2u);
+ }
+ }
+ }
+ if (!end_of_part_reached && check_boundary) {
+ DVLOG(1) << "No boundary found for MHTML part.";
+ return nullptr;
+ }
+
+ Vector<char> data;
+ switch (content_transfer_encoding) {
+ case MIMEHeader::kBase64:
+ if (!Base64Decode(content.data(), content.size(), data)) {
+ DVLOG(1) << "Invalid base64 content for MHTML part.";
+ return nullptr;
+ }
+ break;
+ case MIMEHeader::kQuotedPrintable:
+ QuotedPrintableDecode(content.data(), content.size(), data);
+ break;
+ case MIMEHeader::kEightBit:
+ case MIMEHeader::kSevenBit:
+ case MIMEHeader::kBinary:
+ data.Append(content.data(), content.size());
+ break;
+ default:
+ DVLOG(1) << "Invalid encoding for MHTML part.";
+ return nullptr;
+ }
+ scoped_refptr<SharedBuffer> content_buffer = SharedBuffer::AdoptVector(data);
+ // FIXME: the URL in the MIME header could be relative, we should resolve it
+ // if it is. The specs mentions 5 ways to resolve a URL:
+ // http://tools.ietf.org/html/rfc2557#section-5
+ // IE and Firefox (UNMht) seem to generate only absolute URLs.
+ KURL location = KURL(NullURL(), mime_header.ContentLocation());
+ return ArchiveResource::Create(content_buffer, location,
+ mime_header.ContentID(),
+ AtomicString(mime_header.ContentType()),
+ AtomicString(mime_header.Charset()));
+}
+
+// static
+KURL MHTMLParser::ConvertContentIDToURI(const String& content_id) {
+ // This function is based primarily on an example from rfc2557 in section
+ // 9.5, but also based on more normative parts of specs like:
+ // - rfc2557 - MHTML - section 8.3 - "Use of the Content-ID header and CID
+ // URLs"
+ // - rfc1738 - URL - section 4 (reserved scheme names; includes "cid")
+ // - rfc2387 - multipart/related - section 3.4 - "Syntax" (cid := msg-id)
+ // - rfc0822 - msg-id = "<" addr-spec ">"; addr-spec = local-part "@" domain
+
+ if (content_id.length() <= 2)
+ return KURL();
+
+ if (!content_id.StartsWith('<') || !content_id.EndsWith('>'))
+ return KURL();
+
+ StringBuilder uri_builder;
+ uri_builder.Append("cid:");
+ uri_builder.Append(content_id, 1, content_id.length() - 2);
+ return KURL(NullURL(), uri_builder.ToString());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/mhtml/mhtml_parser.h b/chromium/third_party/blink/renderer/platform/mhtml/mhtml_parser.h
new file mode 100644
index 00000000000..e6e3bbacf77
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mhtml/mhtml_parser.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_MHTML_MHTML_PARSER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MHTML_MHTML_PARSER_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/heap/member.h"
+#include "third_party/blink/renderer/platform/shared_buffer_chunk_reader.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace WTF {
+class String;
+}
+
+namespace blink {
+
+class ArchiveResource;
+class MIMEHeader;
+class SharedBuffer;
+
+class PLATFORM_EXPORT MHTMLParser final {
+ STACK_ALLOCATED();
+
+ public:
+ explicit MHTMLParser(scoped_refptr<const SharedBuffer>);
+
+ HeapVector<Member<ArchiveResource>> ParseArchive();
+ WTF::Time CreationDate() const;
+
+ // Translates |contentIDFromMimeHeader| (of the form "<foo@bar.com>")
+ // into a cid-scheme URI (of the form "cid:foo@bar.com").
+ //
+ // Returns KURL() - an invalid URL - if contentID is invalid.
+ //
+ // See rfc2557 - section 8.3 - "Use of the Content-ID header and CID URLs".
+ static KURL ConvertContentIDToURI(const String& content_id);
+
+ private:
+ bool ParseArchiveWithHeader(MIMEHeader*,
+ HeapVector<Member<ArchiveResource>>&);
+ ArchiveResource* ParseNextPart(const MIMEHeader&,
+ const String& end_of_part_boundary,
+ const String& end_of_document_boundary,
+ bool& end_of_archive_reached);
+
+ WTF::Time creation_date_;
+ SharedBufferChunkReader line_reader_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/mhtml/mhtml_parser_test.cc b/chromium/third_party/blink/renderer/platform/mhtml/mhtml_parser_test.cc
new file mode 100644
index 00000000000..4f3e910d034
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mhtml/mhtml_parser_test.cc
@@ -0,0 +1,472 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/mhtml/mhtml_parser.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/mhtml/archive_resource.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+namespace {
+
+std::string GetResourceData(
+ const HeapVector<Member<ArchiveResource>>& resources,
+ size_t index) {
+ return std::string(resources[index]->Data()->Data(),
+ resources[index]->Data()->size());
+}
+
+} // namespace
+
+class MHTMLParserTest : public testing::Test {
+ public:
+ MHTMLParserTest() = default;
+
+ HeapVector<Member<ArchiveResource>> ParseArchive(const char* mhtml_data,
+ size_t size) {
+ scoped_refptr<SharedBuffer> buf = SharedBuffer::Create(mhtml_data, size);
+ MHTMLParser parser(buf);
+ return parser.ParseArchive();
+ }
+
+ WTF::Time ParseArchiveTime(const char* mhtml_data, size_t size) {
+ scoped_refptr<SharedBuffer> buf = SharedBuffer::Create(mhtml_data, size);
+ MHTMLParser parser(buf);
+ EXPECT_GT(parser.ParseArchive().size(), 0U);
+ return parser.CreationDate();
+ }
+};
+
+TEST_F(MHTMLParserTest, MHTMLPartHeaders) {
+ const char mhtml_data[] =
+ "From: <Saved by Blink>\r\n"
+ "Subject: Test Subject\r\n"
+ "MIME-Version: 1.0\r\n"
+ "Content-Type: multipart/related;\r\n"
+ "\ttype=\"text/html\";\r\n"
+ "\tboundary=\"BoUnDaRy\"\r\n"
+ "\r\n"
+ "\r\n"
+ "--BoUnDaRy\r\n"
+ "Content-Location: http://www.example.com/page1\r\n"
+ "Content-Transfer-Encoding: quoted-printable\r\n"
+ "Content-Type: text/html; charset=utf-8\r\n"
+ "\r\n"
+ "single line\r\n"
+ "--BoUnDaRy\r\n"
+ "Content-Location: http://www.example.com/page2\r\n"
+ "Content-ID: <foo-123@mhtml.blink>\r\n"
+ "Content-Transfer-Encoding: binary\r\n"
+ "Content-Type: text/plain\r\n"
+ "\r\n"
+ "bin\0ary\r\n"
+ "--BoUnDaRy\r\n"
+ "Content-Location: http://www.example.com/page3\r\n"
+ "Content-Transfer-Encoding: base64\r\n"
+ "Content-Type: text/css; charset=ascii\r\n"
+ "\r\n"
+ "MTIzYWJj\r\n"
+ "\r\n"
+ "--BoUnDaRy--\r\n";
+
+ HeapVector<Member<ArchiveResource>> resources =
+ ParseArchive(mhtml_data, sizeof(mhtml_data));
+ EXPECT_EQ(3ul, resources.size());
+
+ EXPECT_EQ("http://www.example.com/page1", resources[0]->Url());
+ EXPECT_TRUE(resources[0]->ContentID().IsNull());
+ EXPECT_EQ("text/html", resources[0]->MimeType());
+ EXPECT_EQ("utf-8", resources[0]->TextEncoding());
+
+ EXPECT_EQ("http://www.example.com/page2", resources[1]->Url());
+ EXPECT_EQ("<foo-123@mhtml.blink>", resources[1]->ContentID());
+ EXPECT_EQ("text/plain", resources[1]->MimeType());
+ EXPECT_TRUE(resources[1]->TextEncoding().IsNull());
+
+ EXPECT_EQ("http://www.example.com/page3", resources[2]->Url());
+ EXPECT_TRUE(resources[2]->ContentID().IsNull());
+ EXPECT_EQ("text/css", resources[2]->MimeType());
+ EXPECT_EQ("ascii", resources[2]->TextEncoding());
+}
+
+TEST_F(MHTMLParserTest, QuotedPrintableContentTransferEncoding) {
+ const char mhtml_data[] =
+ "From: <Saved by Blink>\r\n"
+ "Subject: Test Subject\r\n"
+ "MIME-Version: 1.0\r\n"
+ "Content-Type: multipart/related;\r\n"
+ "\ttype=\"text/html\";\r\n"
+ "\tboundary=\"BoUnDaRy\"\r\n"
+ "\r\n"
+ "\r\n"
+ "--BoUnDaRy\r\n"
+ "Content-Location: http://www.example.com/page1\r\n"
+ "Content-Transfer-Encoding: quoted-printable\r\n"
+ "Content-Type: text/html; charset=utf-8\r\n"
+ "\r\n"
+ "single line\r\n"
+ "--BoUnDaRy\r\n"
+ "Content-Location: http://www.example.com/page2\r\n"
+ "Content-Transfer-Encoding: quoted-printable\r\n"
+ "Content-Type: text/plain\r\n"
+ "\r\n"
+ "long line=3Dbar=3D=\r\n"
+ "more\r\n"
+ "--BoUnDaRy\r\n"
+ "Content-Location: http://www.example.com/page3\r\n"
+ "Content-Transfer-Encoding: quoted-printable\r\n"
+ "Content-Type: text/css; charset=ascii\r\n"
+ "\r\n"
+ "first line\r\n"
+ "second line\r\n"
+ "\r\n"
+ "--BoUnDaRy--\r\n";
+
+ HeapVector<Member<ArchiveResource>> resources =
+ ParseArchive(mhtml_data, sizeof(mhtml_data));
+ EXPECT_EQ(3ul, resources.size());
+
+ EXPECT_EQ("single line\r\n", GetResourceData(resources, 0));
+ EXPECT_EQ("long line=bar=more\r\n", GetResourceData(resources, 1));
+ EXPECT_EQ("first line\r\nsecond line\r\n\r\n", GetResourceData(resources, 2));
+}
+
+TEST_F(MHTMLParserTest, Base64ContentTransferEncoding) {
+ const char mhtml_data[] =
+ "From: <Saved by Blink>\r\n"
+ "Subject: Test Subject\r\n"
+ "MIME-Version: 1.0\r\n"
+ "Content-Type: multipart/related;\r\n"
+ "\ttype=\"text/html\";\r\n"
+ "\tboundary=\"BoUnDaRy\"\r\n"
+ "\r\n"
+ "\r\n"
+ "--BoUnDaRy\r\n"
+ "Content-Location: http://www.example.com/page1\r\n"
+ "Content-Transfer-Encoding: base64\r\n"
+ "Content-Type: text/html; charset=utf-8\r\n"
+ "\r\n"
+ "MTIzYWJj\r\n"
+ "\r\n"
+ "--BoUnDaRy\r\n"
+ "Content-Location: http://www.example.com/page2\r\n"
+ "Content-Transfer-Encoding: base64\r\n"
+ "Content-Type: text/html; charset=utf-8\r\n"
+ "\r\n"
+ "MTIzYWJj\r\n"
+ "AQIDDQ4P\r\n"
+ "\r\n"
+ "--BoUnDaRy--\r\n";
+
+ HeapVector<Member<ArchiveResource>> resources =
+ ParseArchive(mhtml_data, sizeof(mhtml_data));
+ EXPECT_EQ(2ul, resources.size());
+
+ EXPECT_EQ("123abc", GetResourceData(resources, 0));
+ EXPECT_EQ(std::string("123abc\x01\x02\x03\x0D\x0E\x0F", 12),
+ GetResourceData(resources, 1));
+}
+
+TEST_F(MHTMLParserTest, EightBitContentTransferEncoding) {
+ const char mhtml_data[] =
+ "From: <Saved by Blink>\r\n"
+ "Subject: Test Subject\r\n"
+ "MIME-Version: 1.0\r\n"
+ "Content-Type: multipart/related;\r\n"
+ "\ttype=\"text/html\";\r\n"
+ "\tboundary=\"BoUnDaRy\"\r\n"
+ "\r\n"
+ "\r\n"
+ "--BoUnDaRy\r\n"
+ "Content-Location: http://www.example.com/page2\r\n"
+ "Content-Transfer-Encoding: 8bit\r\n"
+ "Content-Type: text/html; charset=utf-8\r\n"
+ "\r\n"
+ "123\r\n"
+ "bin\0ary\r\n"
+ "\r\n"
+ "--BoUnDaRy--\r\n";
+
+ HeapVector<Member<ArchiveResource>> resources =
+ ParseArchive(mhtml_data, sizeof(mhtml_data));
+ EXPECT_EQ(1ul, resources.size());
+
+ EXPECT_EQ(std::string("123bin\0ary", 10), GetResourceData(resources, 0));
+}
+
+TEST_F(MHTMLParserTest, SevenBitContentTransferEncoding) {
+ const char mhtml_data[] =
+ "From: <Saved by Blink>\r\n"
+ "Subject: Test Subject\r\n"
+ "MIME-Version: 1.0\r\n"
+ "Content-Type: multipart/related;\r\n"
+ "\ttype=\"text/html\";\r\n"
+ "\tboundary=\"BoUnDaRy\"\r\n"
+ "\r\n"
+ "\r\n"
+ "--BoUnDaRy\r\n"
+ "Content-Location: http://www.example.com/page2\r\n"
+ "Content-Transfer-Encoding: 7bit\r\n"
+ "Content-Type: text/html; charset=utf-8\r\n"
+ "\r\n"
+ "123\r\n"
+ "abcdefg\r\n"
+ "\r\n"
+ "--BoUnDaRy--\r\n";
+
+ HeapVector<Member<ArchiveResource>> resources =
+ ParseArchive(mhtml_data, sizeof(mhtml_data));
+ EXPECT_EQ(1ul, resources.size());
+
+ EXPECT_EQ(std::string("123abcdefg", 10), GetResourceData(resources, 0));
+}
+
+TEST_F(MHTMLParserTest, BinaryContentTransferEncoding) {
+ const char mhtml_data[] =
+ "From: <Saved by Blink>\r\n"
+ "Subject: Test Subject\r\n"
+ "MIME-Version: 1.0\r\n"
+ "Content-Type: multipart/related;\r\n"
+ "\ttype=\"text/html\";\r\n"
+ "\tboundary=\"BoUnDaRy\"\r\n"
+ "\r\n"
+ "\r\n"
+ "--BoUnDaRy\r\n"
+ "Content-Location: http://www.example.com/page1\r\n"
+ "Content-Transfer-Encoding: binary\r\n"
+ "Content-Type: binary/octet-stream\r\n"
+ "\r\n"
+ "bin\0ary\r\n"
+ "--BoUnDaRy\r\n"
+ "Content-Location: http://www.example.com/page2\r\n"
+ "Content-Transfer-Encoding: binary\r\n"
+ "Content-Type: binary/octet-stream\r\n"
+ "\r\n"
+ "bin\0ary"
+ "--BoUnDaRy\r\n"
+ "Content-Location: http://www.example.com/page3\r\n"
+ "Content-Transfer-Encoding: binary\r\n"
+ "Content-Type: binary/octet-stream\r\n"
+ "\r\n"
+ "--BoUnDaRy--\r\n";
+
+ HeapVector<Member<ArchiveResource>> resources =
+ ParseArchive(mhtml_data, sizeof(mhtml_data));
+ EXPECT_EQ(3ul, resources.size());
+
+ EXPECT_EQ(std::string("bin\0ary", 7), GetResourceData(resources, 0));
+ EXPECT_EQ(std::string("bin\0ary", 7), GetResourceData(resources, 1));
+ EXPECT_EQ("", GetResourceData(resources, 2));
+}
+
+TEST_F(MHTMLParserTest, UnknownContentTransferEncoding) {
+ // Unknown encoding is treated as binary.
+ const char mhtml_data[] =
+ "From: <Saved by Blink>\r\n"
+ "Subject: Test Subject\r\n"
+ "MIME-Version: 1.0\r\n"
+ "Content-Type: multipart/related;\r\n"
+ "\ttype=\"text/html\";\r\n"
+ "\tboundary=\"BoUnDaRy\"\r\n"
+ "\r\n"
+ "\r\n"
+ "--BoUnDaRy\r\n"
+ "Content-Location: http://www.example.com/page1\r\n"
+ "Content-Transfer-Encoding: foo\r\n"
+ "Content-Type: binary/octet-stream\r\n"
+ "\r\n"
+ "bin\0ary\r\n"
+ "--BoUnDaRy\r\n"
+ "Content-Location: http://www.example.com/page2\r\n"
+ "Content-Transfer-Encoding: unknown\r\n"
+ "Content-Type: binary/octet-stream\r\n"
+ "\r\n"
+ "bin\0ary"
+ "--BoUnDaRy\r\n"
+ "Content-Location: http://www.example.com/page3\r\n"
+ "Content-Transfer-Encoding: \r\n"
+ "Content-Type: binary/octet-stream\r\n"
+ "\r\n"
+ "--BoUnDaRy--\r\n";
+
+ HeapVector<Member<ArchiveResource>> resources =
+ ParseArchive(mhtml_data, sizeof(mhtml_data));
+ EXPECT_EQ(3ul, resources.size());
+
+ EXPECT_EQ(std::string("bin\0ary", 7), GetResourceData(resources, 0));
+ EXPECT_EQ(std::string("bin\0ary", 7), GetResourceData(resources, 1));
+ EXPECT_EQ("", GetResourceData(resources, 2));
+}
+
+TEST_F(MHTMLParserTest, NoContentTransferEncoding) {
+ // Missing encoding is treated as binary.
+ const char mhtml_data[] =
+ "From: <Saved by Blink>\r\n"
+ "Subject: Test Subject\r\n"
+ "MIME-Version: 1.0\r\n"
+ "Content-Type: multipart/related;\r\n"
+ "\ttype=\"text/html\";\r\n"
+ "\tboundary=\"BoUnDaRy\"\r\n"
+ "\r\n"
+ "\r\n"
+ "--BoUnDaRy\r\n"
+ "Content-Location: http://www.example.com/page1\r\n"
+ "Content-Type: binary/octet-stream\r\n"
+ "\r\n"
+ "bin\0ary\r\n"
+ "--BoUnDaRy\r\n"
+ "Content-Location: http://www.example.com/page2\r\n"
+ "Content-Type: binary/octet-stream\r\n"
+ "\r\n"
+ "bin\0ary"
+ "--BoUnDaRy\r\n"
+ "Content-Location: http://www.example.com/page3\r\n"
+ "Content-Type: binary/octet-stream\r\n"
+ "\r\n"
+ "--BoUnDaRy--\r\n";
+
+ HeapVector<Member<ArchiveResource>> resources =
+ ParseArchive(mhtml_data, sizeof(mhtml_data));
+ EXPECT_EQ(3ul, resources.size());
+
+ EXPECT_EQ(std::string("bin\0ary", 7), GetResourceData(resources, 0));
+ EXPECT_EQ(std::string("bin\0ary", 7), GetResourceData(resources, 1));
+ EXPECT_EQ("", GetResourceData(resources, 2));
+}
+
+TEST_F(MHTMLParserTest, DateParsing_EmptyDate) {
+ // Missing date is ignored.
+ const char mhtml_data[] =
+ "From: <Saved by Blink>\r\n"
+ "Subject: Test Subject\r\n"
+ "MIME-Version: 1.0\r\n"
+ "Content-Type: multipart/related;\r\n"
+ "\ttype=\"text/html\";\r\n"
+ "\tboundary=\"BoUnDaRy\"\r\n"
+ "\r\n"
+ "\r\n"
+ "--BoUnDaRy\r\n"
+ "Content-Location: http://www.example.com/page1\r\n"
+ "Content-Type: binary/octet-stream\r\n"
+ "\r\n"
+ "bin\0ary\r\n"
+ "--BoUnDaRy--\r\n";
+
+ WTF::Time creation_time = ParseArchiveTime(mhtml_data, sizeof(mhtml_data));
+
+ // No header should produce an invalid time.
+ EXPECT_EQ(WTF::Time(), creation_time);
+}
+
+TEST_F(MHTMLParserTest, DateParsing_InvalidDate) {
+ // Invalid date is ignored. Also, Date header within a part should not be
+ // used.
+ const char mhtml_data[] =
+ "From: <Saved by Blink>\r\n"
+ "Subject: Test Subject\r\n"
+ "Date: 123xyz\r\n"
+ "MIME-Version: 1.0\r\n"
+ "Content-Type: multipart/related;\r\n"
+ "\ttype=\"text/html\";\r\n"
+ "\tboundary=\"BoUnDaRy\"\r\n"
+ "\r\n"
+ "\r\n"
+ "--BoUnDaRy\r\n"
+ "Content-Location: http://www.example.com/page1\r\n"
+ "Content-Type: binary/octet-stream\r\n"
+ "Date: Fri, 1 Mar 2017 22:44:17 -0000\r\n"
+ "\r\n"
+ "bin\0ary\r\n"
+ "--BoUnDaRy--\r\n";
+
+ WTF::Time creation_time = ParseArchiveTime(mhtml_data, sizeof(mhtml_data));
+
+ // Invalid header should produce an invalid time.
+ EXPECT_EQ(WTF::Time(), creation_time);
+}
+
+TEST_F(MHTMLParserTest, DateParsing_ValidDate) {
+ // Valid date is used.
+ const char mhtml_data[] =
+ "From: <Saved by Blink>\r\n"
+ "Subject: Test Subject\r\n"
+ "Date: Fri, 1 Mar 2017 22:44:17 -0000\r\n"
+ "MIME-Version: 1.0\r\n"
+ "Content-Type: multipart/related;\r\n"
+ "\ttype=\"text/html\";\r\n"
+ "\tboundary=\"BoUnDaRy\"\r\n"
+ "\r\n"
+ "\r\n"
+ "--BoUnDaRy\r\n"
+ "Content-Location: http://www.example.com/page1\r\n"
+ "Content-Type: binary/octet-stream\r\n"
+ "\r\n"
+ "bin\0ary\r\n"
+ "--BoUnDaRy--\r\n";
+
+ WTF::Time creation_time = ParseArchiveTime(mhtml_data, sizeof(mhtml_data));
+ WTF::Time expected_time;
+ ASSERT_TRUE(WTF::Time::FromUTCExploded(
+ {2017, 3 /* March */, 5 /* Friday */, 1, 22, 44, 17, 0}, &expected_time));
+ EXPECT_EQ(expected_time, creation_time);
+}
+
+TEST_F(MHTMLParserTest, MissingBoundary) {
+ // No "boundary" parameter in the content type header means that parsing will
+ // be a failure and the header will be |nullptr|.
+ const char mhtml_data[] = "Content-Type: multipart/false\r\n";
+
+ HeapVector<Member<ArchiveResource>> resources =
+ ParseArchive(mhtml_data, sizeof(mhtml_data));
+ EXPECT_EQ(0U, resources.size());
+}
+
+TEST_F(MHTMLParserTest, OverflowedDate) {
+ const char mhtml_data[] =
+ "From: <Saved by Blink>\r\n"
+ "Subject: Test Subject\r\n"
+ "Date:May1 922372\r\n"
+ "MIME-Version: 1.0\r\n"
+ "Content-Type: multipart/related;\r\n"
+ "\ttype=\"text/html\";\r\n"
+ "\tboundary=\"BoUnDaRy\"\r\n"
+ "\r\n"
+ "\r\n"
+ "--BoUnDaRy\r\n"
+ "Content-Location: http://www.example.com/page1\r\n"
+ "Content-Type: binary/octet-stream\r\n"
+ "\r\n"
+ "bin\0ary\r\n"
+ "--BoUnDaRy--\r\n";
+
+ WTF::Time creation_time = ParseArchiveTime(mhtml_data, sizeof(mhtml_data));
+ EXPECT_EQ(WTF::Time(), creation_time);
+}
+
+TEST_F(MHTMLParserTest, OverflowedDay) {
+ const char mhtml_data[] =
+ "From: <Saved by Blink>\r\n"
+ "Subject: Test Subject\r\n"
+ "Date:94/3/933720368547\r\n"
+ "MIME-Version: 1.0\r\n"
+ "Content-Type: multipart/related;\r\n"
+ "\ttype=\"text/html\";\r\n"
+ "\tboundary=\"BoUnDaRy\"\r\n"
+ "\r\n"
+ "\r\n"
+ "--BoUnDaRy\r\n"
+ "Content-Location: http://www.example.com/page1\r\n"
+ "Content-Type: binary/octet-stream\r\n"
+ "\r\n"
+ "bin\0ary\r\n"
+ "--BoUnDaRy--\r\n";
+
+ WTF::Time creation_time = ParseArchiveTime(mhtml_data, sizeof(mhtml_data));
+ EXPECT_EQ(WTF::Time(), creation_time);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/mojo/BUILD.gn b/chromium/third_party/blink/renderer/platform/mojo/BUILD.gn
new file mode 100644
index 00000000000..c4f7e396124
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/BUILD.gn
@@ -0,0 +1,15 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("geometry_struct_traits") {
+ sources = [
+ "geometry_struct_traits.cc",
+ "geometry_struct_traits.h",
+ ]
+ public_deps = [
+ "//third_party/blink/public:blink_headers",
+ "//ui/gfx/geometry",
+ "//ui/gfx/geometry/mojo:mojo_shared_cpp_sources",
+ ]
+}
diff --git a/chromium/third_party/blink/renderer/platform/mojo/DEPS b/chromium/third_party/blink/renderer/platform/mojo/DEPS
new file mode 100644
index 00000000000..9278750b675
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/DEPS
@@ -0,0 +1,16 @@
+include_rules = [
+ # To whitelist base/ stuff Blink is allowed to include, we list up all
+ # directories and files instead of writing 'base/'.
+ "+base/callback.h",
+ "+base/containers/span.h",
+ "+base/message_loop/message_loop.h",
+ "+base/observer_list.h",
+ "+base/strings/string16.h",
+ "+mojo/common/big_string.mojom-blink.h",
+ "+mojo/common/test_common_custom_types.mojom-blink.h",
+ "+mojo/public/cpp/base/time_mojom_traits.h",
+ "+mojo/public/cpp/bindings/binding.h",
+ "+mojo/public/mojom/base/string16.mojom-blink.h",
+ "+services/network/public/mojom/fetch_api.mojom-blink.h",
+ "+skia/public/interfaces/bitmap_skbitmap_struct_traits.h"
+]
diff --git a/chromium/third_party/blink/renderer/platform/mojo/OWNERS b/chromium/third_party/blink/renderer/platform/mojo/OWNERS
new file mode 100644
index 00000000000..591a6840fc2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/OWNERS
@@ -0,0 +1,6 @@
+per-file *_mojom_traits*.*=set noparent
+per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS
+per-file *_struct_traits*.*=set noparent
+per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS
+per-file *.typemap=set noparent
+per-file *.typemap=file://ipc/SECURITY_OWNERS
diff --git a/chromium/third_party/blink/renderer/platform/mojo/big_buffer.typemap b/chromium/third_party/blink/renderer/platform/mojo/big_buffer.typemap
new file mode 100644
index 00000000000..32726d5a2ca
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/big_buffer.typemap
@@ -0,0 +1,12 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//mojo/public/mojom/base/big_buffer.mojom"
+public_headers = [ "//mojo/public/cpp/base/big_buffer.h" ]
+traits_headers = [ "//mojo/public/cpp/base/big_buffer_mojom_traits.h" ]
+public_deps = [
+ "//mojo/public/cpp/base",
+ "//mojo/public/cpp/base:shared_typemap_traits",
+]
+type_mappings = [ "mojo_base.mojom.BigBuffer=mojo_base::BigBuffer[move_only]" ]
diff --git a/chromium/third_party/blink/renderer/platform/mojo/big_string.typemap b/chromium/third_party/blink/renderer/platform/mojo/big_string.typemap
new file mode 100644
index 00000000000..5837a5dc0c8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/big_string.typemap
@@ -0,0 +1,11 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//mojo/public/mojom/base/big_string.mojom"
+public_headers =
+ [ "//third_party/blink/renderer/platform/wtf/text/wtf_string.h" ]
+traits_headers =
+ [ "//third_party/blink/renderer/platform/mojo/big_string_mojom_traits.h" ]
+type_mappings =
+ [ "mojo_base.mojom.BigString=WTF::String[nullable_is_same_type]" ]
diff --git a/chromium/third_party/blink/renderer/platform/mojo/big_string_mojom_traits.cc b/chromium/third_party/blink/renderer/platform/mojo/big_string_mojom_traits.cc
new file mode 100644
index 00000000000..1090633ed22
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/big_string_mojom_traits.cc
@@ -0,0 +1,45 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/mojo/big_string_mojom_traits.h"
+
+#include <cstring>
+
+#include "base/containers/span.h"
+#include "mojo/public/cpp/base/big_buffer.h"
+#include "mojo/public/cpp/base/big_buffer_mojom_traits.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
+
+namespace mojo {
+
+// static
+mojo_base::BigBuffer StructTraits<mojo_base::mojom::BigStringDataView,
+ WTF::String>::data(const WTF::String& input) {
+ WTF::StringUTF8Adaptor adaptor(input);
+ return mojo_base::BigBuffer(
+ base::make_span(reinterpret_cast<const uint8_t*>(adaptor.Data()),
+ adaptor.length() * sizeof(char)));
+}
+
+// static
+bool StructTraits<mojo_base::mojom::BigStringDataView, WTF::String>::Read(
+ mojo_base::mojom::BigStringDataView data,
+ WTF::String* out) {
+ mojo_base::BigBuffer buffer;
+ if (!data.ReadData(&buffer))
+ return false;
+ size_t size = buffer.size();
+ if (size % sizeof(char))
+ return false;
+ // An empty |mojo_base::BigBuffer| may have a null |data()| if empty.
+ if (!size) {
+ *out = g_empty_string;
+ } else {
+ *out = WTF::String::FromUTF8(reinterpret_cast<const char*>(buffer.data()),
+ size / sizeof(char));
+ }
+ return true;
+}
+
+} // namespace mojo
diff --git a/chromium/third_party/blink/renderer/platform/mojo/big_string_mojom_traits.h b/chromium/third_party/blink/renderer/platform/mojo/big_string_mojom_traits.h
new file mode 100644
index 00000000000..4ea78d87f13
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/big_string_mojom_traits.h
@@ -0,0 +1,30 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_BIG_STRING_MOJOM_TRAITS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_BIG_STRING_MOJOM_TRAITS_H_
+
+#include "mojo/public/cpp/bindings/struct_traits.h"
+#include "mojo/public/mojom/base/big_string.mojom-blink.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace mojo_base {
+class BigBuffer;
+}
+
+namespace mojo {
+
+template <>
+struct PLATFORM_EXPORT
+ StructTraits<mojo_base::mojom::BigStringDataView, WTF::String> {
+ static bool IsNull(const WTF::String& input) { return input.IsNull(); }
+ static void SetToNull(WTF::String* output) { *output = WTF::String(); }
+
+ static mojo_base::BigBuffer data(const WTF::String& input);
+ static bool Read(mojo_base::mojom::BigStringDataView, WTF::String* out);
+};
+
+} // namespace mojo
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_BIG_STRING_MOJOM_TRAITS_H_
diff --git a/chromium/third_party/blink/renderer/platform/mojo/big_string_mojom_traits_test.cc b/chromium/third_party/blink/renderer/platform/mojo/big_string_mojom_traits_test.cc
new file mode 100644
index 00000000000..4253e2fd899
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/big_string_mojom_traits_test.cc
@@ -0,0 +1,70 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/rand_util.h"
+#include "mojo/common/test_common_custom_types.mojom-blink.h"
+#include "mojo/public/cpp/base/big_buffer_mojom_traits.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
+#include "mojo/public/mojom/base/big_string.mojom-blink.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/mojo/big_string_mojom_traits.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+TEST(BigStringMojomTraitsTest, BigString_Null) {
+ String str;
+ String output;
+
+ ASSERT_TRUE(
+ mojo::test::SerializeAndDeserialize<mojo_base::mojom::blink::BigString>(
+ &str, &output));
+ ASSERT_EQ(str, output);
+}
+
+TEST(BigStringMojomTraitsTest, BigString_Empty) {
+ String str = String::FromUTF8("");
+ String output;
+
+ ASSERT_TRUE(
+ mojo::test::SerializeAndDeserialize<mojo_base::mojom::blink::BigString>(
+ &str, &output));
+ ASSERT_EQ(str, output);
+}
+
+TEST(BigStringMojomTraitsTest, BigString_Short) {
+ String str = String::FromUTF8("hello world");
+ ASSERT_TRUE(str.Is8Bit());
+ String output;
+
+ ASSERT_TRUE(
+ mojo::test::SerializeAndDeserialize<mojo_base::mojom::blink::BigString>(
+ &str, &output));
+ ASSERT_EQ(str, output);
+
+ // Replace the "o"s in "hello world" with "o"s with acute, so that |str| is
+ // 16-bit.
+ str = String::FromUTF8("hell\xC3\xB3 w\xC3\xB3rld");
+ ASSERT_FALSE(str.Is8Bit());
+
+ ASSERT_TRUE(
+ mojo::test::SerializeAndDeserialize<mojo_base::mojom::blink::BigString>(
+ &str, &output));
+ ASSERT_EQ(str, output);
+}
+
+TEST(BigStringMojomTraitsTest, BigString_Long) {
+ WTF::Vector<char> random_latin1_string(1024 * 1024);
+ base::RandBytes(random_latin1_string.data(), random_latin1_string.size());
+
+ String str(random_latin1_string.data(), random_latin1_string.size());
+ String output;
+
+ ASSERT_TRUE(
+ mojo::test::SerializeAndDeserialize<mojo_base::mojom::blink::BigString>(
+ &str, &output));
+ ASSERT_EQ(str, output);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/mojo/blink_typemaps.gni b/chromium/third_party/blink/renderer/platform/mojo/blink_typemaps.gni
new file mode 100644
index 00000000000..6ae19f26328
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/blink_typemaps.gni
@@ -0,0 +1,23 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+typemaps = [
+ "//mojo/public/cpp/base/file_path.typemap",
+ "//mojo/common/values.typemap",
+ "//third_party/blink/renderer/core/messaging/blink_cloneable_message.typemap",
+ "//third_party/blink/renderer/core/messaging/blink_transferable_message.typemap",
+ "//third_party/blink/renderer/platform/blob/serialized_blob.typemap",
+ "//third_party/blink/renderer/platform/mojo/big_buffer.typemap",
+ "//third_party/blink/renderer/platform/mojo/big_string.typemap",
+ "//third_party/blink/renderer/platform/mojo/file.typemap",
+ "//third_party/blink/renderer/platform/mojo/geometry.typemap",
+ "//third_party/blink/renderer/platform/mojo/kurl.typemap",
+ "//third_party/blink/renderer/platform/mojo/referrer.typemap",
+ "//third_party/blink/renderer/platform/mojo/security_origin.typemap",
+ "//third_party/blink/renderer/platform/mojo/string.typemap",
+ "//third_party/blink/renderer/platform/mojo/time.typemap",
+ "//third_party/blink/public/platform/modules/bluetooth/bluetooth.typemap",
+ "//third_party/blink/public/platform/modules/fetch/fetch_api_request.typemap",
+ "//third_party/blink/public/platform/modules/notifications/notification_types.typemap",
+]
diff --git a/chromium/third_party/blink/renderer/platform/mojo/bluetooth_struct_traits.cc b/chromium/third_party/blink/renderer/platform/mojo/bluetooth_struct_traits.cc
new file mode 100644
index 00000000000..f4fa2943088
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/bluetooth_struct_traits.cc
@@ -0,0 +1,34 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/mojo/bluetooth_struct_traits.h"
+
+#include "mojo/public/cpp/bindings/string_traits_wtf.h"
+
+namespace mojo {
+
+// static
+bool StructTraits<::blink::mojom::WebBluetoothDeviceIdDataView, WTF::String>::
+ Read(::blink::mojom::WebBluetoothDeviceIdDataView data,
+ WTF::String* output) {
+ return data.ReadDeviceId(output);
+}
+
+// static
+bool StructTraits<bluetooth::mojom::UUIDDataView, WTF::String>::Read(
+ bluetooth::mojom::UUIDDataView data,
+ WTF::String* output) {
+ return data.ReadUuid(output);
+}
+
+// static
+void StructTraits<bluetooth::mojom::UUIDDataView, WTF::String>::SetToNull(
+ WTF::String* output) {
+ if (output->IsNull())
+ return;
+ WTF::String result;
+ output->swap(result);
+}
+
+} // namespace mojo
diff --git a/chromium/third_party/blink/renderer/platform/mojo/bluetooth_struct_traits.h b/chromium/third_party/blink/renderer/platform/mojo/bluetooth_struct_traits.h
new file mode 100644
index 00000000000..f58c52af0cc
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/bluetooth_struct_traits.h
@@ -0,0 +1,37 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_BLUETOOTH_STRUCT_TRAITS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_BLUETOOTH_STRUCT_TRAITS_H_
+
+#include "device/bluetooth/public/mojom/uuid.mojom-blink.h"
+#include "third_party/blink/public/platform/modules/bluetooth/web_bluetooth.mojom-blink.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace mojo {
+
+template <>
+struct StructTraits<::blink::mojom::WebBluetoothDeviceIdDataView, WTF::String> {
+ static const WTF::String& device_id(const WTF::String& input) {
+ return input;
+ }
+
+ static bool Read(::blink::mojom::WebBluetoothDeviceIdDataView,
+ WTF::String* output);
+};
+
+template <>
+struct StructTraits<bluetooth::mojom::UUIDDataView, WTF::String> {
+ static const WTF::String& uuid(const WTF::String& input) { return input; }
+
+ static bool Read(bluetooth::mojom::UUIDDataView, WTF::String* output);
+
+ static bool IsNull(const WTF::String& input) { return input.IsNull(); }
+
+ static void SetToNull(WTF::String* output);
+};
+
+} // namespace mojo
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_BLUETOOTH_STRUCT_TRAITS_H_
diff --git a/chromium/third_party/blink/renderer/platform/mojo/fetch_api_request_struct_traits.cc b/chromium/third_party/blink/renderer/platform/mojo/fetch_api_request_struct_traits.cc
new file mode 100644
index 00000000000..1614f4f0b4a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/fetch_api_request_struct_traits.cc
@@ -0,0 +1,339 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/mojo/fetch_api_request_struct_traits.h"
+
+#include "mojo/public/cpp/bindings/map_traits_wtf_hash_map.h"
+#include "mojo/public/cpp/bindings/string_traits_wtf.h"
+#include "services/network/public/mojom/fetch_api.mojom-blink.h"
+#include "third_party/blink/public/platform/web_referrer_policy.h"
+#include "third_party/blink/renderer/platform/blob/blob_data.h"
+#include "third_party/blink/renderer/platform/mojo/kurl_struct_traits.h"
+#include "third_party/blink/renderer/platform/mojo/referrer_struct_traits.h"
+#include "third_party/blink/renderer/platform/weborigin/referrer.h"
+
+namespace mojo {
+
+using blink::mojom::RequestContextType;
+
+RequestContextType
+EnumTraits<RequestContextType, blink::WebURLRequest::RequestContext>::ToMojom(
+ blink::WebURLRequest::RequestContext input) {
+ switch (input) {
+ case blink::WebURLRequest::kRequestContextUnspecified:
+ return RequestContextType::UNSPECIFIED;
+ case blink::WebURLRequest::kRequestContextAudio:
+ return RequestContextType::AUDIO;
+ case blink::WebURLRequest::kRequestContextBeacon:
+ return RequestContextType::BEACON;
+ case blink::WebURLRequest::kRequestContextCSPReport:
+ return RequestContextType::CSP_REPORT;
+ case blink::WebURLRequest::kRequestContextDownload:
+ return RequestContextType::DOWNLOAD;
+ case blink::WebURLRequest::kRequestContextEmbed:
+ return RequestContextType::EMBED;
+ case blink::WebURLRequest::kRequestContextEventSource:
+ return RequestContextType::EVENT_SOURCE;
+ case blink::WebURLRequest::kRequestContextFavicon:
+ return RequestContextType::FAVICON;
+ case blink::WebURLRequest::kRequestContextFetch:
+ return RequestContextType::FETCH;
+ case blink::WebURLRequest::kRequestContextFont:
+ return RequestContextType::FONT;
+ case blink::WebURLRequest::kRequestContextForm:
+ return RequestContextType::FORM;
+ case blink::WebURLRequest::kRequestContextFrame:
+ return RequestContextType::FRAME;
+ case blink::WebURLRequest::kRequestContextHyperlink:
+ return RequestContextType::HYPERLINK;
+ case blink::WebURLRequest::kRequestContextIframe:
+ return RequestContextType::IFRAME;
+ case blink::WebURLRequest::kRequestContextImage:
+ return RequestContextType::IMAGE;
+ case blink::WebURLRequest::kRequestContextImageSet:
+ return RequestContextType::IMAGE_SET;
+ case blink::WebURLRequest::kRequestContextImport:
+ return RequestContextType::IMPORT;
+ case blink::WebURLRequest::kRequestContextInternal:
+ return RequestContextType::INTERNAL;
+ case blink::WebURLRequest::kRequestContextLocation:
+ return RequestContextType::LOCATION;
+ case blink::WebURLRequest::kRequestContextManifest:
+ return RequestContextType::MANIFEST;
+ case blink::WebURLRequest::kRequestContextObject:
+ return RequestContextType::OBJECT;
+ case blink::WebURLRequest::kRequestContextPing:
+ return RequestContextType::PING;
+ case blink::WebURLRequest::kRequestContextPlugin:
+ return RequestContextType::PLUGIN;
+ case blink::WebURLRequest::kRequestContextPrefetch:
+ return RequestContextType::PREFETCH;
+ case blink::WebURLRequest::kRequestContextScript:
+ return RequestContextType::SCRIPT;
+ case blink::WebURLRequest::kRequestContextServiceWorker:
+ return RequestContextType::SERVICE_WORKER;
+ case blink::WebURLRequest::kRequestContextSharedWorker:
+ return RequestContextType::SHARED_WORKER;
+ case blink::WebURLRequest::kRequestContextSubresource:
+ return RequestContextType::SUBRESOURCE;
+ case blink::WebURLRequest::kRequestContextStyle:
+ return RequestContextType::STYLE;
+ case blink::WebURLRequest::kRequestContextTrack:
+ return RequestContextType::TRACK;
+ case blink::WebURLRequest::kRequestContextVideo:
+ return RequestContextType::VIDEO;
+ case blink::WebURLRequest::kRequestContextWorker:
+ return RequestContextType::WORKER;
+ case blink::WebURLRequest::kRequestContextXMLHttpRequest:
+ return RequestContextType::XML_HTTP_REQUEST;
+ case blink::WebURLRequest::kRequestContextXSLT:
+ return RequestContextType::XSLT;
+ }
+
+ NOTREACHED();
+ return RequestContextType::UNSPECIFIED;
+}
+
+bool EnumTraits<RequestContextType, blink::WebURLRequest::RequestContext>::
+ FromMojom(RequestContextType input,
+ blink::WebURLRequest::RequestContext* out) {
+ switch (input) {
+ case RequestContextType::UNSPECIFIED:
+ *out = blink::WebURLRequest::kRequestContextUnspecified;
+ return true;
+ case RequestContextType::AUDIO:
+ *out = blink::WebURLRequest::kRequestContextAudio;
+ return true;
+ case RequestContextType::BEACON:
+ *out = blink::WebURLRequest::kRequestContextBeacon;
+ return true;
+ case RequestContextType::CSP_REPORT:
+ *out = blink::WebURLRequest::kRequestContextCSPReport;
+ return true;
+ case RequestContextType::DOWNLOAD:
+ *out = blink::WebURLRequest::kRequestContextDownload;
+ return true;
+ case RequestContextType::EMBED:
+ *out = blink::WebURLRequest::kRequestContextEmbed;
+ return true;
+ case RequestContextType::EVENT_SOURCE:
+ *out = blink::WebURLRequest::kRequestContextEventSource;
+ return true;
+ case RequestContextType::FAVICON:
+ *out = blink::WebURLRequest::kRequestContextFavicon;
+ return true;
+ case RequestContextType::FETCH:
+ *out = blink::WebURLRequest::kRequestContextFetch;
+ return true;
+ case RequestContextType::FONT:
+ *out = blink::WebURLRequest::kRequestContextFont;
+ return true;
+ case RequestContextType::FORM:
+ *out = blink::WebURLRequest::kRequestContextForm;
+ return true;
+ case RequestContextType::FRAME:
+ *out = blink::WebURLRequest::kRequestContextFrame;
+ return true;
+ case RequestContextType::HYPERLINK:
+ *out = blink::WebURLRequest::kRequestContextHyperlink;
+ return true;
+ case RequestContextType::IFRAME:
+ *out = blink::WebURLRequest::kRequestContextIframe;
+ return true;
+ case RequestContextType::IMAGE:
+ *out = blink::WebURLRequest::kRequestContextImage;
+ return true;
+ case RequestContextType::IMAGE_SET:
+ *out = blink::WebURLRequest::kRequestContextImageSet;
+ return true;
+ case RequestContextType::IMPORT:
+ *out = blink::WebURLRequest::kRequestContextImport;
+ return true;
+ case RequestContextType::INTERNAL:
+ *out = blink::WebURLRequest::kRequestContextInternal;
+ return true;
+ case RequestContextType::LOCATION:
+ *out = blink::WebURLRequest::kRequestContextLocation;
+ return true;
+ case RequestContextType::MANIFEST:
+ *out = blink::WebURLRequest::kRequestContextManifest;
+ return true;
+ case RequestContextType::OBJECT:
+ *out = blink::WebURLRequest::kRequestContextObject;
+ return true;
+ case RequestContextType::PING:
+ *out = blink::WebURLRequest::kRequestContextPing;
+ return true;
+ case RequestContextType::PLUGIN:
+ *out = blink::WebURLRequest::kRequestContextPlugin;
+ return true;
+ case RequestContextType::PREFETCH:
+ *out = blink::WebURLRequest::kRequestContextPrefetch;
+ return true;
+ case RequestContextType::SCRIPT:
+ *out = blink::WebURLRequest::kRequestContextScript;
+ return true;
+ case RequestContextType::SERVICE_WORKER:
+ *out = blink::WebURLRequest::kRequestContextServiceWorker;
+ return true;
+ case RequestContextType::SHARED_WORKER:
+ *out = blink::WebURLRequest::kRequestContextSharedWorker;
+ return true;
+ case RequestContextType::SUBRESOURCE:
+ *out = blink::WebURLRequest::kRequestContextSubresource;
+ return true;
+ case RequestContextType::STYLE:
+ *out = blink::WebURLRequest::kRequestContextStyle;
+ return true;
+ case RequestContextType::TRACK:
+ *out = blink::WebURLRequest::kRequestContextTrack;
+ return true;
+ case RequestContextType::VIDEO:
+ *out = blink::WebURLRequest::kRequestContextVideo;
+ return true;
+ case RequestContextType::WORKER:
+ *out = blink::WebURLRequest::kRequestContextWorker;
+ return true;
+ case RequestContextType::XML_HTTP_REQUEST:
+ *out = blink::WebURLRequest::kRequestContextXMLHttpRequest;
+ return true;
+ case RequestContextType::XSLT:
+ *out = blink::WebURLRequest::kRequestContextXSLT;
+ return true;
+ }
+
+ return false;
+}
+
+// static
+blink::KURL StructTraits<blink::mojom::FetchAPIRequestDataView,
+ blink::WebServiceWorkerRequest>::
+ url(const blink::WebServiceWorkerRequest& request) {
+ return request.Url();
+}
+
+// static
+WTF::String StructTraits<blink::mojom::FetchAPIRequestDataView,
+ blink::WebServiceWorkerRequest>::
+ method(const blink::WebServiceWorkerRequest& request) {
+ return request.Method();
+}
+
+// static
+WTF::HashMap<WTF::String, WTF::String>
+StructTraits<blink::mojom::FetchAPIRequestDataView,
+ blink::WebServiceWorkerRequest>::
+ headers(const blink::WebServiceWorkerRequest& request) {
+ WTF::HashMap<WTF::String, WTF::String> header_map;
+ for (const auto& pair : request.Headers())
+ header_map.insert(pair.key, pair.value);
+ return header_map;
+}
+
+// static
+const blink::Referrer& StructTraits<blink::mojom::FetchAPIRequestDataView,
+ blink::WebServiceWorkerRequest>::
+ referrer(const blink::WebServiceWorkerRequest& request) {
+ return request.GetReferrer();
+}
+
+// static
+WTF::String StructTraits<blink::mojom::FetchAPIRequestDataView,
+ blink::WebServiceWorkerRequest>::
+ blob_uuid(const blink::WebServiceWorkerRequest& request) {
+ if (request.GetBlobDataHandle())
+ return request.GetBlobDataHandle()->Uuid();
+
+ return WTF::String();
+}
+
+// static
+uint64_t StructTraits<blink::mojom::FetchAPIRequestDataView,
+ blink::WebServiceWorkerRequest>::
+ blob_size(const blink::WebServiceWorkerRequest& request) {
+ if (request.GetBlobDataHandle())
+ return request.GetBlobDataHandle()->size();
+
+ return 0;
+}
+
+// static
+blink::mojom::blink::BlobPtr StructTraits<blink::mojom::FetchAPIRequestDataView,
+ blink::WebServiceWorkerRequest>::
+ blob(const blink::WebServiceWorkerRequest& request) {
+ if (request.GetBlobDataHandle()) {
+ blink::mojom::blink::BlobPtr result =
+ request.GetBlobDataHandle()->CloneBlobPtr();
+ return result;
+ }
+
+ return nullptr;
+}
+
+// static
+WTF::String StructTraits<blink::mojom::FetchAPIRequestDataView,
+ blink::WebServiceWorkerRequest>::
+ integrity(const blink::WebServiceWorkerRequest& request) {
+ return request.Integrity();
+}
+
+// static
+WTF::String StructTraits<blink::mojom::FetchAPIRequestDataView,
+ blink::WebServiceWorkerRequest>::
+ client_id(const blink::WebServiceWorkerRequest& request) {
+ return request.ClientId();
+}
+
+// static
+bool StructTraits<blink::mojom::FetchAPIRequestDataView,
+ blink::WebServiceWorkerRequest>::
+ Read(blink::mojom::FetchAPIRequestDataView data,
+ blink::WebServiceWorkerRequest* out) {
+ network::mojom::FetchRequestMode mode;
+ blink::WebURLRequest::RequestContext requestContext;
+ network::mojom::RequestContextFrameType frameType;
+ blink::KURL url;
+ WTF::String method;
+ WTF::HashMap<WTF::String, WTF::String> headers;
+ WTF::String blobUuid;
+ blink::mojom::blink::BlobPtr blob;
+ blink::Referrer referrer;
+ network::mojom::FetchCredentialsMode credentialsMode;
+ network::mojom::FetchRedirectMode redirectMode;
+ WTF::String integrity;
+ WTF::String clientId;
+
+ if (!data.ReadMode(&mode) || !data.ReadRequestContextType(&requestContext) ||
+ !data.ReadFrameType(&frameType) || !data.ReadUrl(&url) ||
+ !data.ReadMethod(&method) || !data.ReadHeaders(&headers) ||
+ !data.ReadBlobUuid(&blobUuid) || !data.ReadReferrer(&referrer) ||
+ !data.ReadCredentialsMode(&credentialsMode) ||
+ !data.ReadRedirectMode(&redirectMode) || !data.ReadClientId(&clientId) ||
+ !data.ReadIntegrity(&integrity)) {
+ return false;
+ }
+
+ out->SetMode(mode);
+ out->SetIsMainResourceLoad(data.is_main_resource_load());
+ out->SetRequestContext(requestContext);
+ out->SetFrameType(frameType);
+ out->SetURL(url);
+ out->SetMethod(method);
+ for (const auto& pair : headers)
+ out->SetHeader(pair.key, pair.value);
+ out->SetBlob(blobUuid, static_cast<long long>(data.blob_size()),
+ data.TakeBlob<blink::mojom::blink::BlobPtr>().PassInterface());
+ out->SetReferrer(referrer.referrer, static_cast<blink::WebReferrerPolicy>(
+ referrer.referrer_policy));
+ out->SetCredentialsMode(credentialsMode);
+ out->SetCacheMode(data.cache_mode());
+ out->SetRedirectMode(redirectMode);
+ out->SetIntegrity(integrity);
+ out->SetKeepalive(data.keepalive());
+ out->SetClientId(clientId);
+ out->SetIsReload(data.is_reload());
+ return true;
+}
+
+} // namespace mojo
diff --git a/chromium/third_party/blink/renderer/platform/mojo/fetch_api_request_struct_traits.h b/chromium/third_party/blink/renderer/platform/mojo/fetch_api_request_struct_traits.h
new file mode 100644
index 00000000000..0bfcd453764
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/fetch_api_request_struct_traits.h
@@ -0,0 +1,99 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_FETCH_API_REQUEST_STRUCT_TRAITS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_FETCH_API_REQUEST_STRUCT_TRAITS_H_
+
+#include "third_party/blink/public/platform/modules/fetch/fetch_api_request.mojom-blink.h"
+#include "third_party/blink/public/platform/web_url_request.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+class KURL;
+}
+
+namespace mojo {
+
+template <>
+struct EnumTraits<::blink::mojom::RequestContextType,
+ ::blink::WebURLRequest::RequestContext> {
+ static ::blink::mojom::RequestContextType ToMojom(
+ ::blink::WebURLRequest::RequestContext input);
+
+ static bool FromMojom(::blink::mojom::RequestContextType input,
+ ::blink::WebURLRequest::RequestContext* out);
+};
+
+template <>
+struct StructTraits<::blink::mojom::FetchAPIRequestDataView,
+ ::blink::WebServiceWorkerRequest> {
+ static ::network::mojom::FetchRequestMode mode(
+ const ::blink::WebServiceWorkerRequest& request) {
+ return request.Mode();
+ }
+
+ static bool is_main_resource_load(
+ const ::blink::WebServiceWorkerRequest& request) {
+ return request.IsMainResourceLoad();
+ }
+
+ static ::blink::WebURLRequest::RequestContext request_context_type(
+ const ::blink::WebServiceWorkerRequest& request) {
+ return request.GetRequestContext();
+ }
+
+ static ::network::mojom::RequestContextFrameType frame_type(
+ const ::blink::WebServiceWorkerRequest& request) {
+ return request.GetFrameType();
+ }
+
+ static ::blink::KURL url(const ::blink::WebServiceWorkerRequest&);
+
+ static WTF::String method(const ::blink::WebServiceWorkerRequest&);
+
+ static WTF::HashMap<WTF::String, WTF::String> headers(
+ const ::blink::WebServiceWorkerRequest&);
+
+ static WTF::String blob_uuid(const ::blink::WebServiceWorkerRequest&);
+
+ static uint64_t blob_size(const ::blink::WebServiceWorkerRequest&);
+
+ static blink::mojom::blink::BlobPtr blob(
+ const ::blink::WebServiceWorkerRequest&);
+
+ static const ::blink::Referrer& referrer(
+ const ::blink::WebServiceWorkerRequest&);
+
+ static ::network::mojom::FetchCredentialsMode credentials_mode(
+ const ::blink::WebServiceWorkerRequest& request) {
+ return request.CredentialsMode();
+ }
+
+ static ::blink::mojom::FetchCacheMode cache_mode(
+ const ::blink::WebServiceWorkerRequest& request) {
+ return request.CacheMode();
+ }
+
+ static ::network::mojom::FetchRedirectMode redirect_mode(
+ const ::blink::WebServiceWorkerRequest& request) {
+ return request.RedirectMode();
+ }
+
+ static WTF::String integrity(const ::blink::WebServiceWorkerRequest&);
+ static bool keepalive(const ::blink::WebServiceWorkerRequest& request) {
+ return request.Keepalive();
+ }
+ static WTF::String client_id(const ::blink::WebServiceWorkerRequest&);
+
+ static bool is_reload(const ::blink::WebServiceWorkerRequest& request) {
+ return request.IsReload();
+ }
+
+ static bool Read(::blink::mojom::FetchAPIRequestDataView,
+ ::blink::WebServiceWorkerRequest* output);
+};
+
+} // namespace mojo
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_FETCH_API_REQUEST_STRUCT_TRAITS_H_
diff --git a/chromium/third_party/blink/renderer/platform/mojo/file.typemap b/chromium/third_party/blink/renderer/platform/mojo/file.typemap
new file mode 100644
index 00000000000..3a0759dc284
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/file.typemap
@@ -0,0 +1,12 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//mojo/public/mojom/base/file.mojom"
+public_headers = [ "//base/files/file.h" ]
+traits_headers = [ "//mojo/public/cpp/base/file_mojom_traits.h" ]
+deps = [
+ "//mojo/public/mojom/base",
+]
+type_mappings =
+ [ "mojo_base.mojom.File=base::File[move_only,nullable_is_same_type]" ]
diff --git a/chromium/third_party/blink/renderer/platform/mojo/geometry.typemap b/chromium/third_party/blink/renderer/platform/mojo/geometry.typemap
new file mode 100644
index 00000000000..5a7f96cbd5d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/geometry.typemap
@@ -0,0 +1,32 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//ui/gfx/geometry/mojo/geometry.mojom"
+public_headers = [
+ "//third_party/blink/public/platform/web_float_rect.h",
+ "//third_party/blink/public/platform/web_float_point.h",
+ "//third_party/blink/public/platform/web_point.h",
+ "//third_party/blink/public/platform/web_rect.h",
+ "//third_party/blink/public/platform/web_size.h",
+]
+traits_headers = [
+ "//third_party/blink/renderer/platform/mojo/geometry_struct_traits.h",
+ "//ui/gfx/geometry/mojo/geometry_struct_traits.h",
+]
+
+# Note: consumers of this typemap must themselves depend on platform.
+deps = [
+ "//mojo/public/cpp/bindings",
+ "//third_party/blink/renderer/platform/mojo:geometry_struct_traits",
+]
+
+# TODO(zqzhang): ideally, gfx.mojom.Size should be mapped into ::blink::IntSize.
+# However that introduces an link issue on Windows. See https://crbug.com/653323
+type_mappings = [
+ "gfx.mojom.Point=::blink::WebPoint",
+ "gfx.mojom.PointF=::blink::WebFloatPoint",
+ "gfx.mojom.RectF=::blink::WebFloatRect",
+ "gfx.mojom.Rect=::blink::WebRect",
+ "gfx.mojom.Size=::blink::WebSize",
+]
diff --git a/chromium/third_party/blink/renderer/platform/mojo/geometry_struct_traits.cc b/chromium/third_party/blink/renderer/platform/mojo/geometry_struct_traits.cc
new file mode 100644
index 00000000000..b721a83b93f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/geometry_struct_traits.cc
@@ -0,0 +1,64 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/mojo/geometry_struct_traits.h"
+
+namespace mojo {
+
+// static
+bool StructTraits<gfx::mojom::RectFDataView, ::blink::WebFloatRect>::Read(
+ gfx::mojom::RectFDataView data,
+ ::blink::WebFloatRect* out) {
+ if (data.width() < 0 || data.height() < 0)
+ return false;
+ out->x = data.x();
+ out->y = data.y();
+ out->width = data.width();
+ out->height = data.height();
+ return true;
+}
+
+// static
+bool StructTraits<gfx::mojom::RectDataView, ::blink::WebRect>::Read(
+ gfx::mojom::RectDataView data,
+ ::blink::WebRect* out) {
+ if (data.width() < 0 || data.height() < 0)
+ return false;
+ out->x = data.x();
+ out->y = data.y();
+ out->width = data.width();
+ out->height = data.height();
+ return true;
+}
+
+// static
+bool StructTraits<gfx::mojom::PointDataView, ::blink::WebPoint>::Read(
+ gfx::mojom::PointDataView data,
+ ::blink::WebPoint* out) {
+ out->x = data.x();
+ out->y = data.y();
+ return true;
+}
+
+// static
+bool StructTraits<gfx::mojom::PointFDataView, ::blink::WebFloatPoint>::Read(
+ gfx::mojom::PointFDataView data,
+ ::blink::WebFloatPoint* out) {
+ out->x = data.x();
+ out->y = data.y();
+ return true;
+}
+
+// static
+bool StructTraits<gfx::mojom::SizeDataView, ::blink::WebSize>::Read(
+ gfx::mojom::SizeDataView data,
+ ::blink::WebSize* out) {
+ if (data.width() < 0 || data.height() < 0)
+ return false;
+ out->width = data.width();
+ out->height = data.height();
+ return true;
+}
+
+} // namespace mojo
diff --git a/chromium/third_party/blink/renderer/platform/mojo/geometry_struct_traits.h b/chromium/third_party/blink/renderer/platform/mojo/geometry_struct_traits.h
new file mode 100644
index 00000000000..9dda7b831b4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/geometry_struct_traits.h
@@ -0,0 +1,58 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_GEOMETRY_STRUCT_TRAITS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_GEOMETRY_STRUCT_TRAITS_H_
+
+#include "third_party/blink/public/platform/web_float_point.h"
+#include "third_party/blink/public/platform/web_float_rect.h"
+#include "third_party/blink/public/platform/web_point.h"
+#include "third_party/blink/public/platform/web_rect.h"
+#include "third_party/blink/public/platform/web_size.h"
+#include "ui/gfx/geometry/mojo/geometry.mojom-shared.h"
+
+namespace mojo {
+
+template <>
+struct StructTraits<gfx::mojom::PointDataView, ::blink::WebPoint> {
+ static int x(const ::blink::WebPoint& point) { return point.x; }
+ static int y(const ::blink::WebPoint& point) { return point.y; }
+ static bool Read(gfx::mojom::PointDataView, ::blink::WebPoint* out);
+};
+
+template <>
+struct StructTraits<gfx::mojom::PointFDataView, ::blink::WebFloatPoint> {
+ static float x(const ::blink::WebFloatPoint& point) { return point.x; }
+ static float y(const ::blink::WebFloatPoint& point) { return point.y; }
+ static bool Read(gfx::mojom::PointFDataView, ::blink::WebFloatPoint* out);
+};
+
+template <>
+struct StructTraits<gfx::mojom::RectFDataView, ::blink::WebFloatRect> {
+ static float x(const ::blink::WebFloatRect& rect) { return rect.x; }
+ static float y(const ::blink::WebFloatRect& rect) { return rect.y; }
+ static float width(const ::blink::WebFloatRect& rect) { return rect.width; }
+ static float height(const ::blink::WebFloatRect& rect) { return rect.height; }
+ static bool Read(gfx::mojom::RectFDataView, ::blink::WebFloatRect* out);
+};
+
+template <>
+struct StructTraits<gfx::mojom::RectDataView, ::blink::WebRect> {
+ static int x(const ::blink::WebRect& rect) { return rect.x; }
+ static int y(const ::blink::WebRect& rect) { return rect.y; }
+ static int width(const ::blink::WebRect& rect) { return rect.width; }
+ static int height(const ::blink::WebRect& rect) { return rect.height; }
+ static bool Read(gfx::mojom::RectDataView, ::blink::WebRect* out);
+};
+
+template <>
+struct StructTraits<gfx::mojom::SizeDataView, ::blink::WebSize> {
+ static int width(const ::blink::WebSize& size) { return size.width; }
+ static int height(const ::blink::WebSize& size) { return size.height; }
+ static bool Read(gfx::mojom::SizeDataView, ::blink::WebSize* out);
+};
+
+} // namespace mojo
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_GEOMETRY_STRUCT_TRAITS_H_
diff --git a/chromium/third_party/blink/renderer/platform/mojo/geometry_struct_traits_test.cc b/chromium/third_party/blink/renderer/platform/mojo/geometry_struct_traits_test.cc
new file mode 100644
index 00000000000..fb7c72414df
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/geometry_struct_traits_test.cc
@@ -0,0 +1,144 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <utility>
+
+#include "base/message_loop/message_loop.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/mojo/geometry_traits_test_service.mojom-blink.h"
+
+namespace blink {
+
+namespace {
+
+class GeometryStructTraitsTest
+ : public testing::Test,
+ public gfx::mojom::blink::GeometryTraitsTestService {
+ public:
+ GeometryStructTraitsTest() {}
+
+ protected:
+ gfx::mojom::blink::GeometryTraitsTestServicePtr GetTraitsTestProxy() {
+ gfx::mojom::blink::GeometryTraitsTestServicePtr proxy;
+ traits_test_bindings_.AddBinding(this, mojo::MakeRequest(&proxy));
+ return proxy;
+ }
+
+ private:
+ // GeometryTraitsTestService:
+ void EchoPoint(const WebPoint& p, EchoPointCallback callback) override {
+ std::move(callback).Run(p);
+ }
+
+ void EchoPointF(const WebFloatPoint& p,
+ EchoPointFCallback callback) override {
+ std::move(callback).Run(p);
+ }
+
+ void EchoSize(const WebSize& s, EchoSizeCallback callback) override {
+ std::move(callback).Run(s);
+ }
+
+ void EchoSizeF(gfx::mojom::blink::SizeFPtr, EchoSizeFCallback) override {
+ // The type map is not specified.
+ NOTREACHED();
+ }
+
+ void EchoRect(const WebRect& r, EchoRectCallback callback) override {
+ std::move(callback).Run(r);
+ }
+
+ void EchoRectF(const WebFloatRect& r, EchoRectFCallback callback) override {
+ std::move(callback).Run(r);
+ }
+
+ void EchoInsets(gfx::mojom::blink::InsetsPtr, EchoInsetsCallback) override {
+ // The type map is not specified.
+ NOTREACHED();
+ }
+
+ void EchoInsetsF(gfx::mojom::blink::InsetsFPtr,
+ EchoInsetsFCallback) override {
+ // The type map is not specified.
+ NOTREACHED();
+ }
+
+ void EchoVector2d(gfx::mojom::blink::Vector2dPtr,
+ EchoVector2dCallback) override {
+ // The type map is not specified.
+ NOTREACHED();
+ }
+
+ void EchoVector2dF(gfx::mojom::blink::Vector2dFPtr,
+ EchoVector2dFCallback) override {
+ // The type map is not specified.
+ NOTREACHED();
+ }
+
+ mojo::BindingSet<gfx::mojom::blink::GeometryTraitsTestService>
+ traits_test_bindings_;
+
+ base::MessageLoop message_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(GeometryStructTraitsTest);
+};
+
+} // namespace
+
+TEST_F(GeometryStructTraitsTest, Size) {
+ const int32_t kWidth = 1234;
+ const int32_t kHeight = 5678;
+ WebSize input(kWidth, kHeight);
+ gfx::mojom::blink::GeometryTraitsTestServicePtr proxy = GetTraitsTestProxy();
+ WebSize output;
+ proxy->EchoSize(input, &output);
+ EXPECT_EQ(input, output);
+}
+
+TEST_F(GeometryStructTraitsTest, Point) {
+ const float kX = 1234;
+ const float kY = 5678;
+ WebPoint input(kX, kY);
+ gfx::mojom::blink::GeometryTraitsTestServicePtr proxy = GetTraitsTestProxy();
+ WebPoint output;
+ proxy->EchoPoint(input, &output);
+ EXPECT_EQ(input, output);
+}
+
+TEST_F(GeometryStructTraitsTest, PointF) {
+ const float kX = 1.234;
+ const float kY = 5.678;
+ WebFloatPoint input(kX, kY);
+ gfx::mojom::blink::GeometryTraitsTestServicePtr proxy = GetTraitsTestProxy();
+ WebFloatPoint output;
+ proxy->EchoPointF(input, &output);
+ EXPECT_EQ(input, output);
+}
+
+TEST_F(GeometryStructTraitsTest, Rect) {
+ const float kX = 1;
+ const float kY = 2;
+ const float kWidth = 3;
+ const float kHeight = 4;
+ WebRect input(kX, kY, kWidth, kHeight);
+ gfx::mojom::blink::GeometryTraitsTestServicePtr proxy = GetTraitsTestProxy();
+ WebRect output;
+ proxy->EchoRect(input, &output);
+ EXPECT_EQ(input, output);
+}
+
+TEST_F(GeometryStructTraitsTest, RectF) {
+ const float kX = 1.234;
+ const float kY = 2.345;
+ const float kWidth = 3.456;
+ const float kHeight = 4.567;
+ WebFloatRect input(kX, kY, kWidth, kHeight);
+ gfx::mojom::blink::GeometryTraitsTestServicePtr proxy = GetTraitsTestProxy();
+ WebFloatRect output;
+ proxy->EchoRectF(input, &output);
+ EXPECT_EQ(input, output);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/mojo/interface_invalidator.cc b/chromium/third_party/blink/renderer/platform/mojo/interface_invalidator.cc
new file mode 100644
index 00000000000..126d0221c62
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/interface_invalidator.cc
@@ -0,0 +1,35 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/mojo/interface_invalidator.h"
+
+namespace blink {
+
+InterfaceInvalidator::InterfaceInvalidator() : weak_factory_(this) {}
+
+InterfaceInvalidator::~InterfaceInvalidator() {
+ weak_factory_.InvalidateWeakPtrs();
+ NotifyInvalidate();
+}
+
+void InterfaceInvalidator::AddObserver(Observer* observer) {
+ DCHECK(observer);
+ observers_.AddObserver(observer);
+}
+
+void InterfaceInvalidator::RemoveObserver(const Observer* observer) {
+ DCHECK(observer);
+ observers_.RemoveObserver(observer);
+}
+
+base::WeakPtr<InterfaceInvalidator> InterfaceInvalidator::GetWeakPtr() {
+ return weak_factory_.GetWeakPtr();
+}
+
+void InterfaceInvalidator::NotifyInvalidate() {
+ for (auto& observer : observers_)
+ observer.OnInvalidate();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/mojo/interface_invalidator.h b/chromium/third_party/blink/renderer/platform/mojo/interface_invalidator.h
new file mode 100644
index 00000000000..6a65fe2e8ca
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/interface_invalidator.h
@@ -0,0 +1,43 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_INTERFACE_INVALIDATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_INTERFACE_INVALIDATOR_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+
+// Notifies weak interface bindings to be invalidated when this object is
+// destroyed.
+class PLATFORM_EXPORT InterfaceInvalidator {
+ public:
+ InterfaceInvalidator();
+ ~InterfaceInvalidator();
+
+ class Observer {
+ public:
+ virtual void OnInvalidate() = 0;
+ };
+
+ void AddObserver(Observer*);
+ void RemoveObserver(const Observer*);
+
+ base::WeakPtr<InterfaceInvalidator> GetWeakPtr();
+
+ private:
+ void NotifyInvalidate();
+
+ base::ObserverList<Observer> observers_;
+ base::WeakPtrFactory<InterfaceInvalidator> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(InterfaceInvalidator);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_INTERFACE_INVALIDATOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/mojo/interface_invalidator_test.cc b/chromium/third_party/blink/renderer/platform/mojo/interface_invalidator_test.cc
new file mode 100644
index 00000000000..cc25bde800b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/interface_invalidator_test.cc
@@ -0,0 +1,614 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/test/bind_test_util.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "mojo/public/interfaces/bindings/tests/ping_service.mojom-blink.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/mojo/interface_invalidator.h"
+#include "third_party/blink/renderer/platform/mojo/revocable_binding.h"
+#include "third_party/blink/renderer/platform/mojo/revocable_interface_ptr.h"
+#include "third_party/blink/renderer/platform/mojo/revocable_strong_binding.h"
+
+namespace blink {
+
+namespace {
+
+void DoSetFlag(bool* flag) {
+ *flag = true;
+}
+
+class PingServiceImplBase : public mojo::test::blink::PingService {
+ public:
+ PingServiceImplBase(bool send_response = true)
+ : send_response_(send_response) {}
+ ~PingServiceImplBase() override {}
+
+ // mojo::test::blink::PingService:
+ void Ping(const PingCallback& callback) override {
+ if (ping_handler_)
+ ping_handler_.Run();
+
+ if (send_response_) {
+ callback.Run();
+ } else {
+ saved_callback_ = callback;
+ }
+
+ if (post_ping_handler_)
+ post_ping_handler_.Run();
+ }
+
+ void set_ping_handler(const base::RepeatingClosure& handler) {
+ ping_handler_ = handler;
+ }
+
+ void set_post_ping_handler(const base::RepeatingClosure& handler) {
+ post_ping_handler_ = handler;
+ }
+
+ private:
+ bool send_response_;
+ PingCallback saved_callback_;
+ base::RepeatingClosure ping_handler_;
+ base::RepeatingClosure post_ping_handler_;
+
+ DISALLOW_COPY_AND_ASSIGN(PingServiceImplBase);
+};
+
+class PingServiceImpl : public PingServiceImplBase {
+ public:
+ PingServiceImpl(
+ mojo::InterfaceRequest<mojo::test::blink::PingService> request,
+ bool send_response = true)
+ : PingServiceImplBase(send_response),
+ error_handler_called_(false),
+ binding_(this, std::move(request)) {
+ binding_.set_connection_error_handler(
+ base::BindOnce(DoSetFlag, &error_handler_called_));
+ }
+
+ ~PingServiceImpl() override {}
+
+ bool error_handler_called() { return error_handler_called_; }
+
+ mojo::Binding<mojo::test::blink::PingService>* binding() { return &binding_; }
+
+ private:
+ bool error_handler_called_;
+ mojo::Binding<mojo::test::blink::PingService> binding_;
+
+ DISALLOW_COPY_AND_ASSIGN(PingServiceImpl);
+};
+
+class InterfaceInvalidatorTest : public testing::Test {
+ public:
+ InterfaceInvalidatorTest() {}
+ ~InterfaceInvalidatorTest() override {}
+
+ private:
+ base::MessageLoop message_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(InterfaceInvalidatorTest);
+};
+
+class InterfaceInvalidatorObserver : public InterfaceInvalidator::Observer {
+ public:
+ InterfaceInvalidatorObserver(const base::RepeatingClosure& handler) {
+ invalidate_handler_ = handler;
+ }
+ ~InterfaceInvalidatorObserver() {}
+
+ void OnInvalidate() override { invalidate_handler_.Run(); }
+
+ private:
+ base::RepeatingClosure invalidate_handler_;
+
+ DISALLOW_COPY_AND_ASSIGN(InterfaceInvalidatorObserver);
+};
+
+TEST_F(InterfaceInvalidatorTest, DestroyNotifiesObservers) {
+ int called = 0;
+ auto inc_called_cb = base::BindLambdaForTesting([&] { ++called; });
+ InterfaceInvalidatorObserver observer1(inc_called_cb);
+ InterfaceInvalidatorObserver observer2(inc_called_cb);
+ {
+ InterfaceInvalidator invalidator;
+ invalidator.AddObserver(&observer1);
+ invalidator.AddObserver(&observer2);
+ EXPECT_EQ(called, 0);
+ }
+ EXPECT_EQ(called, 2);
+}
+
+TEST_F(InterfaceInvalidatorTest, DestroyInvalidatesRevocableInterfacePtr) {
+ mojo::test::blink::RevocablePingServicePtr wptr;
+ auto invalidator = std::make_unique<InterfaceInvalidator>();
+ PingServiceImpl impl(MakeRequest(&wptr, invalidator.get()));
+
+ bool ping_called = false;
+ wptr->Ping(base::BindRepeating(DoSetFlag, &ping_called));
+ base::RunLoop().RunUntilIdle();
+ ASSERT_TRUE(ping_called);
+
+ bool error_handler_called = false;
+ wptr.set_connection_error_handler(
+ base::BindOnce(DoSetFlag, &error_handler_called));
+
+ invalidator.reset();
+ impl.set_ping_handler(base::BindRepeating([] { FAIL(); }));
+ wptr->Ping(base::BindRepeating([] { FAIL(); }));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(error_handler_called);
+ EXPECT_TRUE(impl.error_handler_called());
+ EXPECT_TRUE(wptr.encountered_error());
+ EXPECT_TRUE(wptr);
+}
+
+TEST_F(InterfaceInvalidatorTest, InvalidateAfterMessageSent) {
+ mojo::test::blink::RevocablePingServicePtr wptr;
+ auto invalidator = std::make_unique<InterfaceInvalidator>();
+ PingServiceImpl impl(MakeRequest(&wptr, invalidator.get()));
+
+ bool called = false;
+ impl.set_ping_handler(base::BindRepeating(DoSetFlag, &called));
+ // The passed in callback will not be called as the interface is invalidated
+ // before a response can come back.
+ wptr->Ping(base::BindRepeating([] { FAIL(); }));
+ invalidator.reset();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(called);
+ EXPECT_TRUE(impl.error_handler_called());
+}
+
+TEST_F(InterfaceInvalidatorTest, PassInterfaceThenInvalidate) {
+ mojo::test::blink::RevocablePingServicePtr wptr;
+ auto invalidator = std::make_unique<InterfaceInvalidator>();
+ PingServiceImpl impl(MakeRequest(&wptr, invalidator.get()));
+
+ bool impl_called = false;
+ impl.set_ping_handler(base::BindRepeating(DoSetFlag, &impl_called));
+ wptr.set_connection_error_handler(base::BindOnce([] { FAIL(); }));
+
+ mojo::test::blink::PingServicePtr ptr(wptr.PassInterface());
+ invalidator.reset();
+ bool ping_called = false;
+ ptr->Ping(base::BindRepeating(DoSetFlag, &ping_called));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(ping_called);
+ EXPECT_TRUE(impl_called);
+ EXPECT_FALSE(impl.error_handler_called());
+}
+
+TEST_F(InterfaceInvalidatorTest, PassInterfaceOfInvalidatedPtr) {
+ mojo::test::blink::RevocablePingServicePtr wptr;
+ auto invalidator = std::make_unique<InterfaceInvalidator>();
+ PingServiceImpl impl(MakeRequest(&wptr, invalidator.get()));
+
+ impl.set_ping_handler(base::BindRepeating([] { FAIL(); }));
+ bool error_handler_called = false;
+ wptr.set_connection_error_handler(
+ base::BindOnce(DoSetFlag, &error_handler_called));
+
+ // This also destroys the original invalidator.
+ invalidator = std::make_unique<InterfaceInvalidator>();
+ base::RunLoop().RunUntilIdle();
+ ASSERT_TRUE(error_handler_called);
+ ASSERT_TRUE(impl.error_handler_called());
+
+ mojo::test::blink::RevocablePingServicePtr wptr2(wptr.PassInterface(),
+ invalidator.get());
+ wptr2->Ping(base::BindRepeating([] { FAIL(); }));
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(InterfaceInvalidatorTest,
+ PassInterfaceBeforeConnectionErrorNotification) {
+ mojo::test::blink::RevocablePingServicePtr wptr;
+ auto invalidator = std::make_unique<InterfaceInvalidator>();
+ PingServiceImpl impl(MakeRequest(&wptr, invalidator.get()));
+
+ impl.set_ping_handler(base::BindRepeating([] { FAIL(); }));
+ wptr.set_connection_error_handler(base::BindOnce([] { FAIL(); }));
+
+ // This also destroys the original invalidator.
+ invalidator = std::make_unique<InterfaceInvalidator>();
+ mojo::test::blink::RevocablePingServicePtr wptr2(wptr.PassInterface(),
+ invalidator.get());
+ wptr2->Ping(base::BindRepeating([] { FAIL(); }));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(impl.error_handler_called());
+}
+
+TEST_F(InterfaceInvalidatorTest, InvalidateAfterReset) {
+ mojo::test::blink::RevocablePingServicePtr wptr;
+ auto invalidator = std::make_unique<InterfaceInvalidator>();
+ PingServiceImpl impl(MakeRequest(&wptr, invalidator.get()));
+ wptr.set_connection_error_handler(base::BindOnce([] { FAIL(); }));
+
+ wptr.reset();
+ invalidator.reset();
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_FALSE(wptr);
+}
+
+TEST_F(InterfaceInvalidatorTest, ResetInvalidatedRevocableInterfacePtr) {
+ mojo::test::blink::RevocablePingServicePtr wptr;
+ auto invalidator = std::make_unique<InterfaceInvalidator>();
+ PingServiceImpl impl(MakeRequest(&wptr, invalidator.get()));
+ wptr.set_connection_error_handler(base::BindOnce([] { FAIL(); }));
+
+ invalidator.reset();
+ wptr.reset();
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(InterfaceInvalidatorTest, InvalidateErroredPtr) {
+ mojo::test::blink::RevocablePingServicePtr wptr;
+ auto invalidator = std::make_unique<InterfaceInvalidator>();
+ PingServiceImpl impl(MakeRequest(&wptr, invalidator.get()));
+
+ int called = 0;
+ wptr.set_connection_error_handler(
+ base::BindLambdaForTesting([&] { called++; }));
+
+ impl.binding()->Close();
+ base::RunLoop().RunUntilIdle();
+ invalidator.reset();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(called, 1);
+ EXPECT_FALSE(impl.error_handler_called());
+}
+
+// InterfacePtrs do not set up a proxy until they are used for the first
+// time.
+TEST_F(InterfaceInvalidatorTest, InvalidateBeforeProxyConfigured) {
+ mojo::test::blink::RevocablePingServicePtr wptr;
+ auto invalidator = std::make_unique<InterfaceInvalidator>();
+ PingServiceImpl impl(MakeRequest(&wptr, invalidator.get()));
+
+ invalidator.reset();
+ wptr->Ping(base::BindRepeating([] { FAIL(); }));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(impl.error_handler_called());
+}
+
+TEST_F(InterfaceInvalidatorTest, MoveChangesInvalidatorObserver) {
+ mojo::test::blink::RevocablePingServicePtr wptr;
+ auto invalidator = std::make_unique<InterfaceInvalidator>();
+ PingServiceImpl impl(MakeRequest(&wptr, invalidator.get()));
+
+ auto wptr2(std::move(wptr));
+ bool called = false;
+ wptr2.set_connection_error_handler(base::BindOnce(DoSetFlag, &called));
+
+ invalidator.reset();
+ wptr2->Ping(base::BindRepeating([] { FAIL(); }));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(called);
+ EXPECT_TRUE(impl.error_handler_called());
+}
+
+TEST_F(InterfaceInvalidatorTest, MoveInvalidatedPointer) {
+ mojo::test::blink::RevocablePingServicePtr wptr;
+ auto invalidator = std::make_unique<InterfaceInvalidator>();
+ PingServiceImpl impl(MakeRequest(&wptr, invalidator.get()));
+
+ invalidator.reset();
+ auto wptr2(std::move(wptr));
+ wptr2->Ping(base::BindRepeating([] { FAIL(); }));
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(InterfaceInvalidatorTest, InvalidateRevocableInterfacePtrDuringSyncIPC) {
+ mojo::test::blink::RevocablePingServicePtr wptr;
+ auto invalidator = std::make_unique<InterfaceInvalidator>();
+ PingServiceImpl impl(MakeRequest(&wptr, invalidator.get()));
+
+ impl.set_ping_handler(
+ base::BindLambdaForTesting([&]() { invalidator.reset(); }));
+ bool result = wptr->Ping();
+ EXPECT_FALSE(result);
+}
+
+TEST_F(InterfaceInvalidatorTest,
+ InvalidateRevocableInterfacePtrDuringSyncIPCWithoutResponse) {
+ mojo::test::blink::RevocablePingServicePtr wptr;
+ auto invalidator = std::make_unique<InterfaceInvalidator>();
+ PingServiceImpl impl(MakeRequest(&wptr, invalidator.get()),
+ false /* send_response */);
+
+ impl.set_ping_handler(
+ base::BindLambdaForTesting([&]() { invalidator.reset(); }));
+ bool result = wptr->Ping();
+ EXPECT_FALSE(result);
+}
+
+class RevocablePingServiceImpl : public PingServiceImplBase {
+ public:
+ RevocablePingServiceImpl(
+ mojo::InterfaceRequest<mojo::test::blink::PingService> request,
+ InterfaceInvalidator* invalidator,
+ bool send_response = true)
+ : PingServiceImplBase(send_response),
+ error_handler_called_(false),
+ binding_(this, std::move(request), invalidator) {
+ binding_.set_connection_error_handler(
+ base::BindOnce(DoSetFlag, &error_handler_called_));
+ }
+
+ ~RevocablePingServiceImpl() override {}
+
+ bool error_handler_called() { return error_handler_called_; }
+
+ RevocableBinding<mojo::test::blink::PingService>* binding() {
+ return &binding_;
+ }
+
+ private:
+ bool error_handler_called_;
+ RevocableBinding<mojo::test::blink::PingService> binding_;
+
+ DISALLOW_COPY_AND_ASSIGN(RevocablePingServiceImpl);
+};
+
+TEST_F(InterfaceInvalidatorTest, DestroyInvalidatesRevocableBinding) {
+ mojo::test::blink::PingServicePtr ptr;
+ auto invalidator = std::make_unique<InterfaceInvalidator>();
+ RevocablePingServiceImpl impl(MakeRequest(&ptr), invalidator.get());
+
+ bool ping_called = false;
+ ptr->Ping(base::BindRepeating(DoSetFlag, &ping_called));
+ base::RunLoop().RunUntilIdle();
+ ASSERT_TRUE(ping_called);
+
+ bool error_handler_called = false;
+ ptr.set_connection_error_handler(
+ base::BindOnce(DoSetFlag, &error_handler_called));
+
+ invalidator.reset();
+ impl.set_ping_handler(base::BindRepeating([] { FAIL(); }));
+ ptr->Ping(base::BindRepeating([] { FAIL(); }));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(error_handler_called);
+ EXPECT_TRUE(impl.error_handler_called());
+ EXPECT_TRUE(ptr.encountered_error());
+ EXPECT_TRUE(ptr);
+ EXPECT_TRUE(*impl.binding());
+}
+
+TEST_F(InterfaceInvalidatorTest, InvalidateBindingBeforeResponse) {
+ mojo::test::blink::PingServicePtr ptr;
+ auto invalidator = std::make_unique<InterfaceInvalidator>();
+ RevocablePingServiceImpl impl(MakeRequest(&ptr), invalidator.get());
+ impl.set_ping_handler(
+ base::BindLambdaForTesting([&] { invalidator.reset(); }));
+
+ bool ptr_error_handler_called = false;
+ ptr.set_connection_error_handler(
+ base::BindOnce(DoSetFlag, &ptr_error_handler_called));
+ ptr->Ping(base::BindRepeating([] { FAIL(); }));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(ptr_error_handler_called);
+ EXPECT_TRUE(impl.error_handler_called());
+ EXPECT_TRUE(*impl.binding());
+}
+
+TEST_F(InterfaceInvalidatorTest, InvalidateBindingAfterResponse) {
+ mojo::test::blink::PingServicePtr ptr;
+ auto invalidator = std::make_unique<InterfaceInvalidator>();
+ RevocablePingServiceImpl impl(MakeRequest(&ptr), invalidator.get());
+ impl.set_post_ping_handler(base::BindLambdaForTesting([&] {
+ invalidator.reset();
+ impl.set_ping_handler(base::BindRepeating([] { FAIL(); }));
+ }));
+
+ bool ptr_error_handler_called = false;
+ ptr.set_connection_error_handler(
+ base::BindOnce(DoSetFlag, &ptr_error_handler_called));
+ bool ping_called = false;
+ ptr->Ping(base::BindRepeating(DoSetFlag, &ping_called));
+ ptr->Ping(base::BindRepeating([] { FAIL(); }));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(ping_called);
+ EXPECT_TRUE(ptr_error_handler_called);
+ EXPECT_TRUE(impl.error_handler_called());
+ EXPECT_TRUE(*impl.binding());
+}
+
+TEST_F(InterfaceInvalidatorTest, UnbindThenInvalidate) {
+ mojo::test::blink::PingServicePtr ptr;
+ auto invalidator = std::make_unique<InterfaceInvalidator>();
+ RevocablePingServiceImpl impl(MakeRequest(&ptr), invalidator.get());
+ ptr.set_connection_error_handler(base::BindOnce([] { FAIL(); }));
+
+ PingServiceImpl impl2(impl.binding()->Unbind());
+ invalidator.reset();
+ bool ping_called = false;
+ ptr->Ping(base::BindRepeating(DoSetFlag, &ping_called));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(ping_called);
+ EXPECT_FALSE(impl.error_handler_called());
+}
+
+TEST_F(InterfaceInvalidatorTest, UnbindInvalidatedRevocableBinding) {
+ mojo::test::blink::PingServicePtr ptr;
+ auto invalidator = std::make_unique<InterfaceInvalidator>();
+ RevocablePingServiceImpl impl(MakeRequest(&ptr), invalidator.get());
+
+ bool ptr_error_handler_called = false;
+ ptr.set_connection_error_handler(
+ base::BindOnce(DoSetFlag, &ptr_error_handler_called));
+
+ invalidator.reset();
+ base::RunLoop().RunUntilIdle();
+ ASSERT_TRUE(ptr_error_handler_called);
+ ASSERT_TRUE(impl.error_handler_called());
+
+ PingServiceImpl impl2(impl.binding()->Unbind());
+ impl2.set_ping_handler(base::BindRepeating([] { FAIL(); }));
+ ptr->Ping(base::BindRepeating([] { FAIL(); }));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(impl2.error_handler_called());
+}
+
+TEST_F(InterfaceInvalidatorTest, UnbindBeforeConnectionErrorNotification) {
+ mojo::test::blink::PingServicePtr ptr;
+ auto invalidator = std::make_unique<InterfaceInvalidator>();
+ RevocablePingServiceImpl impl(MakeRequest(&ptr), invalidator.get());
+
+ bool ptr_error_handler_called = false;
+ ptr.set_connection_error_handler(
+ base::BindOnce(DoSetFlag, &ptr_error_handler_called));
+
+ invalidator.reset();
+ PingServiceImpl impl2(impl.binding()->Unbind());
+ impl2.set_ping_handler(base::BindRepeating([] { FAIL(); }));
+ ptr->Ping(base::BindRepeating([] { FAIL(); }));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_FALSE(impl.error_handler_called());
+ EXPECT_TRUE(impl2.error_handler_called());
+ EXPECT_TRUE(ptr_error_handler_called);
+}
+
+TEST_F(InterfaceInvalidatorTest, InvalidateClosedRevocableBinding) {
+ mojo::test::blink::PingServicePtr ptr;
+ auto invalidator = std::make_unique<InterfaceInvalidator>();
+ RevocablePingServiceImpl impl(MakeRequest(&ptr), invalidator.get());
+
+ impl.binding()->Close();
+ invalidator.reset();
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_FALSE(impl.error_handler_called());
+ EXPECT_FALSE(*impl.binding());
+}
+
+TEST_F(InterfaceInvalidatorTest, CloseInvalidatedRevocableBinding) {
+ mojo::test::blink::PingServicePtr ptr;
+ auto invalidator = std::make_unique<InterfaceInvalidator>();
+ RevocablePingServiceImpl impl(MakeRequest(&ptr), invalidator.get());
+
+ invalidator.reset();
+ impl.binding()->Close();
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_FALSE(impl.error_handler_called());
+ EXPECT_FALSE(*impl.binding());
+}
+
+TEST_F(InterfaceInvalidatorTest, InvalidateErroredRevocableBinding) {
+ mojo::test::blink::PingServicePtr ptr;
+ auto invalidator = std::make_unique<InterfaceInvalidator>();
+ RevocablePingServiceImpl impl(MakeRequest(&ptr), invalidator.get());
+
+ int called = 0;
+ impl.binding()->set_connection_error_handler(
+ base::BindLambdaForTesting([&] { called++; }));
+
+ ptr.reset();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(1, called);
+ invalidator.reset();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(1, called);
+}
+
+TEST_F(InterfaceInvalidatorTest, InvalidateWhileRevocableBindingPaused) {
+ mojo::test::blink::PingServicePtr ptr;
+ auto invalidator = std::make_unique<InterfaceInvalidator>();
+ RevocablePingServiceImpl impl(MakeRequest(&ptr), invalidator.get());
+
+ impl.binding()->PauseIncomingMethodCallProcessing();
+ invalidator.reset();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(impl.error_handler_called());
+ impl.binding()->ResumeIncomingMethodCallProcessing();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(impl.error_handler_called());
+}
+
+TEST_F(InterfaceInvalidatorTest, InvalidateRevocableBindingDuringSyncIPC) {
+ mojo::test::blink::PingServicePtr ptr;
+ auto invalidator = std::make_unique<InterfaceInvalidator>();
+ RevocablePingServiceImpl impl(MakeRequest(&ptr), invalidator.get());
+
+ impl.set_ping_handler(
+ base::BindLambdaForTesting([&] { invalidator.reset(); }));
+ bool result = ptr->Ping();
+ EXPECT_FALSE(result);
+}
+
+TEST_F(InterfaceInvalidatorTest,
+ InvalidateRevocableBindingDuringSyncIPCWithoutResponse) {
+ mojo::test::blink::PingServicePtr ptr;
+ auto invalidator = std::make_unique<InterfaceInvalidator>();
+ RevocablePingServiceImpl impl(MakeRequest(&ptr), invalidator.get(),
+ false /* send_response */);
+
+ impl.set_ping_handler(
+ base::BindLambdaForTesting([&] { invalidator.reset(); }));
+ bool result = ptr->Ping();
+ EXPECT_FALSE(result);
+}
+
+TEST_F(InterfaceInvalidatorTest, InvalidateStrongBinding) {
+ mojo::test::blink::PingServicePtr ptr;
+ auto invalidator = std::make_unique<InterfaceInvalidator>();
+ auto impl_ptr =
+ MakeRevocableStrongBinding(std::make_unique<PingServiceImplBase>(),
+ MakeRequest(&ptr), invalidator.get());
+ auto* impl = reinterpret_cast<PingServiceImplBase*>(impl_ptr->impl());
+
+ bool impl_called = false;
+ impl->set_ping_handler(base::BindRepeating(DoSetFlag, &impl_called));
+ bool ping_called = false;
+ ptr->Ping(base::BindRepeating(DoSetFlag, &ping_called));
+ base::RunLoop().RunUntilIdle();
+ ASSERT_TRUE(impl_called);
+ ASSERT_TRUE(ping_called);
+
+ impl->set_ping_handler(base::BindRepeating([] { FAIL(); }));
+ invalidator.reset();
+ ptr->Ping(base::BindRepeating([] { FAIL(); }));
+ base::RunLoop().RunUntilIdle();
+
+ ASSERT_FALSE(impl_ptr);
+}
+
+TEST_F(InterfaceInvalidatorTest, InvalidateStrongBindingAfterError) {
+ mojo::test::blink::PingServicePtr ptr;
+ auto invalidator = std::make_unique<InterfaceInvalidator>();
+ auto impl_ptr =
+ MakeRevocableStrongBinding(std::make_unique<PingServiceImplBase>(),
+ MakeRequest(&ptr), invalidator.get());
+ ptr.set_connection_error_handler(base::BindOnce([] { FAIL(); }));
+
+ ptr.reset();
+ base::RunLoop().RunUntilIdle();
+ invalidator.reset();
+ base::RunLoop().RunUntilIdle();
+
+ ASSERT_FALSE(impl_ptr);
+}
+
+} // namespace
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/mojo/kurl.typemap b/chromium/third_party/blink/renderer/platform/mojo/kurl.typemap
new file mode 100644
index 00000000000..868ae80d716
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/kurl.typemap
@@ -0,0 +1,15 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//url/mojom/url.mojom"
+public_headers = [ "//third_party/blink/renderer/platform/weborigin/kurl.h" ]
+traits_headers =
+ [ "//third_party/blink/renderer/platform/mojo/kurl_struct_traits.h" ]
+
+# Note: consumers of this typemap must themselves depend on platform.
+deps = [
+ "//mojo/public/cpp/bindings",
+ "//url",
+]
+type_mappings = [ "url.mojom.Url=::blink::KURL[force_serialize]" ]
diff --git a/chromium/third_party/blink/renderer/platform/mojo/kurl_security_origin_test.cc b/chromium/third_party/blink/renderer/platform/mojo/kurl_security_origin_test.cc
new file mode 100644
index 00000000000..e776e7040ad
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/kurl_security_origin_test.cc
@@ -0,0 +1,89 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/mojom/url_test.mojom-blink.h"
+#include "url/url_constants.h"
+
+namespace blink {
+namespace {
+
+class UrlTestImpl : public url::mojom::blink::UrlTest {
+ public:
+ explicit UrlTestImpl(url::mojom::blink::UrlTestRequest request)
+ : binding_(this, std::move(request)) {}
+
+ // UrlTest:
+ void BounceUrl(const KURL& in, BounceUrlCallback callback) override {
+ std::move(callback).Run(in);
+ }
+
+ void BounceOrigin(const scoped_refptr<const SecurityOrigin>& in,
+ BounceOriginCallback callback) override {
+ std::move(callback).Run(in);
+ }
+
+ private:
+ mojo::Binding<UrlTest> binding_;
+};
+
+} // namespace
+
+// Mojo version of chrome IPC test in url/ipc/url_param_traits_unittest.cc.
+TEST(KURLSecurityOriginStructTraitsTest, Basic) {
+ base::MessageLoop message_loop;
+
+ url::mojom::blink::UrlTestPtr proxy;
+ UrlTestImpl impl(MakeRequest(&proxy));
+
+ const char* serialize_cases[] = {
+ "http://www.google.com/", "http://user:pass@host.com:888/foo;bar?baz#nop",
+ };
+
+ for (const char* test_case : serialize_cases) {
+ KURL input(NullURL(), test_case);
+ KURL output;
+ EXPECT_TRUE(proxy->BounceUrl(input, &output));
+
+ // We want to test each component individually to make sure its range was
+ // correctly serialized and deserialized, not just the spec.
+ EXPECT_EQ(input.GetString(), output.GetString());
+ EXPECT_EQ(input.IsValid(), output.IsValid());
+ EXPECT_EQ(input.Protocol(), output.Protocol());
+ EXPECT_EQ(input.User(), output.User());
+ EXPECT_EQ(input.Pass(), output.Pass());
+ EXPECT_EQ(input.Host(), output.Host());
+ EXPECT_EQ(input.Port(), output.Port());
+ EXPECT_EQ(input.GetPath(), output.GetPath());
+ EXPECT_EQ(input.Query(), output.Query());
+ EXPECT_EQ(input.FragmentIdentifier(), output.FragmentIdentifier());
+ }
+
+ // Test an excessively long GURL.
+ {
+ const std::string url =
+ std::string("http://example.org/").append(url::kMaxURLChars + 1, 'a');
+ KURL input(NullURL(), url.c_str());
+ KURL output;
+ EXPECT_TRUE(proxy->BounceUrl(input, &output));
+ EXPECT_TRUE(output.IsEmpty());
+ }
+
+ // Test basic Origin serialization.
+ scoped_refptr<const SecurityOrigin> non_unique =
+ SecurityOrigin::Create("http", "www.google.com", 80);
+ scoped_refptr<const SecurityOrigin> output;
+ EXPECT_TRUE(proxy->BounceOrigin(non_unique, &output));
+ EXPECT_TRUE(non_unique->IsSameSchemeHostPort(output.get()));
+ EXPECT_FALSE(output->IsUnique());
+
+ scoped_refptr<const SecurityOrigin> unique = SecurityOrigin::CreateUnique();
+ EXPECT_TRUE(proxy->BounceOrigin(unique, &output));
+ EXPECT_TRUE(output->IsUnique());
+}
+
+} // namespace url
diff --git a/chromium/third_party/blink/renderer/platform/mojo/kurl_struct_traits.h b/chromium/third_party/blink/renderer/platform/mojo/kurl_struct_traits.h
new file mode 100644
index 00000000000..f1b87901d35
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/kurl_struct_traits.h
@@ -0,0 +1,42 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_KURL_STRUCT_TRAITS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_KURL_STRUCT_TRAITS_H_
+
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "url/mojom/url.mojom-blink.h"
+#include "url/url_constants.h"
+
+namespace mojo {
+
+template <>
+struct StructTraits<url::mojom::blink::Url::DataView, ::blink::KURL> {
+ static WTF::String url(const ::blink::KURL& blinkUrl) {
+ if (!blinkUrl.IsValid() ||
+ blinkUrl.GetString().length() > url::kMaxURLChars) {
+ return g_empty_string;
+ }
+
+ return blinkUrl.GetString();
+ }
+ static bool Read(url::mojom::blink::Url::DataView data, ::blink::KURL* out) {
+ WTF::String urlString;
+ if (!data.ReadUrl(&urlString))
+ return false;
+
+ if (urlString.length() > url::kMaxURLChars)
+ return false;
+
+ *out = ::blink::KURL(::blink::KURL(), urlString);
+ if (!urlString.IsEmpty() && !out->IsValid())
+ return false;
+
+ return true;
+ }
+};
+}
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_KURL_STRUCT_TRAITS_H_
diff --git a/chromium/third_party/blink/renderer/platform/mojo/mojo_helper.h b/chromium/third_party/blink/renderer/platform/mojo/mojo_helper.h
new file mode 100644
index 00000000000..03f2d40ecea
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/mojo_helper.h
@@ -0,0 +1,22 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_MOJO_HELPER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_MOJO_HELPER_H_
+
+#include "base/message_loop/message_loop.h"
+
+namespace blink {
+
+// Used to get whether message loop is ready for current thread, to help
+// blink::initialize() determining whether can initialize mojo stuff or not.
+// TODO(leonhsl): http://crbug.com/660274 Remove this API by ensuring
+// a message loop before calling blink::initialize().
+inline bool CanInitializeMojo() {
+ return base::MessageLoop::current();
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_MOJO_HELPER_H_
diff --git a/chromium/third_party/blink/renderer/platform/mojo/notification_struct_traits.cc b/chromium/third_party/blink/renderer/platform/mojo/notification_struct_traits.cc
new file mode 100644
index 00000000000..5cd6623fa3a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/notification_struct_traits.cc
@@ -0,0 +1,287 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/mojo/notification_struct_traits.h"
+
+#include <iterator>
+
+namespace mojo {
+
+using blink::mojom::NotificationDirection;
+using blink::mojom::NotificationActionType;
+
+// static
+NotificationDirection
+EnumTraits<NotificationDirection, blink::WebNotificationData::Direction>::
+ ToMojom(blink::WebNotificationData::Direction input) {
+ switch (input) {
+ case blink::WebNotificationData::kDirectionLeftToRight:
+ return NotificationDirection::LEFT_TO_RIGHT;
+ case blink::WebNotificationData::kDirectionRightToLeft:
+ return NotificationDirection::RIGHT_TO_LEFT;
+ case blink::WebNotificationData::kDirectionAuto:
+ return NotificationDirection::AUTO;
+ }
+
+ NOTREACHED();
+ return NotificationDirection::AUTO;
+}
+
+// static
+bool EnumTraits<NotificationDirection, blink::WebNotificationData::Direction>::
+ FromMojom(NotificationDirection input,
+ blink::WebNotificationData::Direction* out) {
+ switch (input) {
+ case NotificationDirection::LEFT_TO_RIGHT:
+ *out = blink::WebNotificationData::kDirectionLeftToRight;
+ return true;
+ case NotificationDirection::RIGHT_TO_LEFT:
+ *out = blink::WebNotificationData::kDirectionRightToLeft;
+ return true;
+ case NotificationDirection::AUTO:
+ *out = blink::WebNotificationData::kDirectionAuto;
+ return true;
+ }
+
+ return false;
+}
+
+// static
+NotificationActionType
+EnumTraits<NotificationActionType, blink::WebNotificationAction::Type>::ToMojom(
+ blink::WebNotificationAction::Type input) {
+ switch (input) {
+ case blink::WebNotificationAction::kButton:
+ return NotificationActionType::BUTTON;
+ case blink::WebNotificationAction::kText:
+ return NotificationActionType::TEXT;
+ }
+
+ NOTREACHED();
+ return NotificationActionType::BUTTON;
+}
+
+// static
+bool EnumTraits<NotificationActionType, blink::WebNotificationAction::Type>::
+ FromMojom(NotificationActionType input,
+ blink::WebNotificationAction::Type* out) {
+ switch (input) {
+ case NotificationActionType::BUTTON:
+ *out = blink::WebNotificationAction::kButton;
+ return true;
+ case NotificationActionType::TEXT:
+ *out = blink::WebNotificationAction::kText;
+ return true;
+ }
+
+ return false;
+}
+
+// static
+WTF::String StructTraits<blink::mojom::NotificationActionDataView,
+ blink::WebNotificationAction>::
+ action(const blink::WebNotificationAction& action) {
+ return action.action;
+}
+
+// static
+WTF::String StructTraits<blink::mojom::NotificationActionDataView,
+ blink::WebNotificationAction>::
+ title(const blink::WebNotificationAction& action) {
+ return action.title;
+}
+
+// static
+blink::KURL StructTraits<blink::mojom::NotificationActionDataView,
+ blink::WebNotificationAction>::
+ icon(const blink::WebNotificationAction& action) {
+ return action.icon;
+}
+
+// static
+WTF::String StructTraits<blink::mojom::NotificationActionDataView,
+ blink::WebNotificationAction>::
+ placeholder(const blink::WebNotificationAction& action) {
+ return action.placeholder;
+}
+
+// static
+bool StructTraits<blink::mojom::NotificationActionDataView,
+ blink::WebNotificationAction>::
+ Read(blink::mojom::NotificationActionDataView notification_action,
+ blink::WebNotificationAction* out) {
+ WTF::String action;
+ WTF::String title;
+ blink::KURL icon;
+ WTF::String placeholder;
+
+ if (!notification_action.ReadType(&out->type) ||
+ !notification_action.ReadTitle(&title) ||
+ !notification_action.ReadAction(&action) ||
+ !notification_action.ReadIcon(&icon) ||
+ !notification_action.ReadPlaceholder(&placeholder)) {
+ return false;
+ }
+
+ out->action = action;
+ out->title = title;
+ out->icon = icon;
+ out->placeholder = placeholder;
+ return true;
+}
+
+// static
+WTF::String StructTraits<
+ blink::mojom::NotificationDataDataView,
+ blink::WebNotificationData>::title(const blink::WebNotificationData& data) {
+ return data.title;
+}
+
+// static
+WTF::String StructTraits<
+ blink::mojom::NotificationDataDataView,
+ blink::WebNotificationData>::lang(const blink::WebNotificationData& data) {
+ return data.lang;
+}
+
+// static
+WTF::String StructTraits<
+ blink::mojom::NotificationDataDataView,
+ blink::WebNotificationData>::body(const blink::WebNotificationData& data) {
+ return data.body;
+}
+
+// static
+WTF::String StructTraits<
+ blink::mojom::NotificationDataDataView,
+ blink::WebNotificationData>::tag(const blink::WebNotificationData& data) {
+ return data.tag;
+}
+
+// static
+blink::KURL StructTraits<
+ blink::mojom::NotificationDataDataView,
+ blink::WebNotificationData>::image(const blink::WebNotificationData& data) {
+ return data.image;
+}
+
+// static
+blink::KURL StructTraits<
+ blink::mojom::NotificationDataDataView,
+ blink::WebNotificationData>::icon(const blink::WebNotificationData& data) {
+ return data.icon;
+}
+
+// static
+blink::KURL StructTraits<
+ blink::mojom::NotificationDataDataView,
+ blink::WebNotificationData>::badge(const blink::WebNotificationData& data) {
+ return data.badge;
+}
+
+// static
+base::span<const int32_t> StructTraits<blink::mojom::NotificationDataDataView,
+ blink::WebNotificationData>::
+ vibration_pattern(const blink::WebNotificationData& data) {
+ // TODO(https://crbug.com/798466): Align data types to avoid this cast.
+ return base::make_span(reinterpret_cast<const int32_t*>(data.vibrate.Data()),
+ data.vibrate.size());
+}
+
+// static
+base::span<const blink::WebNotificationAction> StructTraits<
+ blink::mojom::NotificationDataDataView,
+ blink::WebNotificationData>::actions(const blink::WebNotificationData&
+ data) {
+ return base::make_span(data.actions.Data(), data.actions.size());
+}
+
+// static
+base::span<const int8_t> StructTraits<
+ blink::mojom::NotificationDataDataView,
+ blink::WebNotificationData>::data(const blink::WebNotificationData& data) {
+ // TODO(https://crbug.com/798466): Align data types to avoid this cast.
+ return base::make_span(reinterpret_cast<const int8_t*>(data.data.Data()),
+ data.data.size());
+}
+
+// static
+bool StructTraits<blink::mojom::NotificationDataDataView,
+ blink::WebNotificationData>::
+ Read(blink::mojom::NotificationDataDataView notification_data,
+ blink::WebNotificationData* out) {
+ // We can't read these values to the |out| type directly because it relies
+ // on Web* types, whereas Mojo uses the regular WTF types. This will be
+ // solved when the Web* types are removed as part of the Onion Soup refactor.
+ WTF::String title;
+ WTF::String lang;
+ WTF::String body;
+ WTF::String tag;
+
+ blink::KURL image;
+ blink::KURL icon;
+ blink::KURL badge;
+ Vector<int32_t> vibrate;
+ Vector<int8_t> data;
+ Vector<blink::WebNotificationAction> actions;
+
+ if (!notification_data.ReadTitle(&title) ||
+ !notification_data.ReadDirection(&out->direction) ||
+ !notification_data.ReadLang(&lang) ||
+ !notification_data.ReadBody(&body) || !notification_data.ReadTag(&tag) ||
+ !notification_data.ReadImage(&image) ||
+ !notification_data.ReadIcon(&icon) ||
+ !notification_data.ReadBadge(&badge) ||
+ !notification_data.ReadVibrationPattern(&vibrate) ||
+ !notification_data.ReadData(&data) ||
+ !notification_data.ReadActions(&actions)) {
+ return false;
+ }
+
+ out->title = title;
+ out->lang = lang;
+ out->body = body;
+ out->tag = tag;
+ out->image = image;
+ out->icon = icon;
+ out->badge = badge;
+ out->vibrate = vibrate;
+ out->timestamp = notification_data.timestamp();
+ out->renotify = notification_data.renotify();
+ out->silent = notification_data.silent();
+ out->require_interaction = notification_data.require_interaction();
+ out->data = data;
+ out->actions = actions;
+ return true;
+}
+
+// static
+base::span<const SkBitmap>
+StructTraits<blink::mojom::NotificationResourcesDataView,
+ blink::WebNotificationResources>::
+ action_icons(const blink::WebNotificationResources& resources) {
+ return base::make_span(resources.action_icons.Data(),
+ resources.action_icons.size());
+}
+
+// static
+bool StructTraits<blink::mojom::NotificationResourcesDataView,
+ blink::WebNotificationResources>::
+ Read(blink::mojom::NotificationResourcesDataView notification_resources,
+ blink::WebNotificationResources* out) {
+ // Cannot read to |out| directly because it expects a WebVector (see above).
+ Vector<SkBitmap> action_icons;
+
+ if (!notification_resources.ReadImage(&out->image) ||
+ !notification_resources.ReadIcon(&out->icon) ||
+ !notification_resources.ReadBadge(&out->badge) ||
+ !notification_resources.ReadActionIcons(&action_icons)) {
+ return false;
+ }
+
+ out->action_icons = action_icons;
+ return true;
+}
+
+} // namespace mojo
diff --git a/chromium/third_party/blink/renderer/platform/mojo/notification_struct_traits.h b/chromium/third_party/blink/renderer/platform/mojo/notification_struct_traits.h
new file mode 100644
index 00000000000..ac67fd10220
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/notification_struct_traits.h
@@ -0,0 +1,140 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_NOTIFICATION_STRUCT_TRAITS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_NOTIFICATION_STRUCT_TRAITS_H_
+
+#include "base/containers/span.h"
+#include "mojo/public/cpp/bindings/array_traits_wtf_vector.h"
+#include "mojo/public/cpp/bindings/string_traits_wtf.h"
+#include "skia/public/interfaces/bitmap_skbitmap_struct_traits.h"
+#include "third_party/blink/public/platform/modules/notifications/notification.mojom-blink.h"
+#include "third_party/blink/public/platform/modules/notifications/web_notification_action.h"
+#include "third_party/blink/renderer/platform/mojo/kurl_struct_traits.h"
+#include "third_party/blink/renderer/platform/mojo/string16_mojom_traits.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+
+namespace mojo {
+
+template <>
+struct PLATFORM_EXPORT EnumTraits<blink::mojom::NotificationDirection,
+ blink::WebNotificationData::Direction> {
+ static blink::mojom::NotificationDirection ToMojom(
+ blink::WebNotificationData::Direction input);
+
+ static bool FromMojom(blink::mojom::NotificationDirection input,
+ blink::WebNotificationData::Direction* out);
+};
+
+template <>
+struct PLATFORM_EXPORT EnumTraits<blink::mojom::NotificationActionType,
+ blink::WebNotificationAction::Type> {
+ static blink::mojom::NotificationActionType ToMojom(
+ blink::WebNotificationAction::Type input);
+
+ static bool FromMojom(blink::mojom::NotificationActionType input,
+ blink::WebNotificationAction::Type* out);
+};
+
+template <>
+struct PLATFORM_EXPORT StructTraits<blink::mojom::NotificationActionDataView,
+ blink::WebNotificationAction> {
+ static blink::WebNotificationAction::Type type(
+ const blink::WebNotificationAction& action) {
+ return action.type;
+ }
+
+ static WTF::String action(const blink::WebNotificationAction&);
+
+ static WTF::String title(const blink::WebNotificationAction&);
+
+ static blink::KURL icon(const blink::WebNotificationAction&);
+
+ static WTF::String placeholder(const blink::WebNotificationAction&);
+
+ static bool Read(blink::mojom::NotificationActionDataView,
+ blink::WebNotificationAction* output);
+};
+
+template <>
+struct PLATFORM_EXPORT StructTraits<blink::mojom::NotificationDataDataView,
+ blink::WebNotificationData> {
+ static WTF::String title(const blink::WebNotificationData&);
+
+ static blink::WebNotificationData::Direction direction(
+ const blink::WebNotificationData& data) {
+ return data.direction;
+ }
+
+ static WTF::String lang(const blink::WebNotificationData&);
+
+ static WTF::String body(const blink::WebNotificationData&);
+
+ static WTF::String tag(const blink::WebNotificationData&);
+
+ static blink::KURL image(const blink::WebNotificationData&);
+
+ static blink::KURL icon(const blink::WebNotificationData&);
+
+ static blink::KURL badge(const blink::WebNotificationData&);
+
+ static base::span<const int32_t> vibration_pattern(
+ const blink::WebNotificationData&);
+
+ static double timestamp(const blink::WebNotificationData& data) {
+ return data.timestamp;
+ }
+
+ static bool renotify(const blink::WebNotificationData& data) {
+ return data.renotify;
+ }
+
+ static bool silent(const blink::WebNotificationData& data) {
+ return data.silent;
+ }
+
+ static bool require_interaction(const blink::WebNotificationData& data) {
+ return data.require_interaction;
+ }
+
+ static base::span<const int8_t> data(const blink::WebNotificationData&);
+
+ static base::span<const blink::WebNotificationAction> actions(
+ const blink::WebNotificationData&);
+
+ static bool Read(blink::mojom::NotificationDataDataView,
+ blink::WebNotificationData* output);
+};
+
+template <>
+struct PLATFORM_EXPORT StructTraits<blink::mojom::NotificationResourcesDataView,
+ blink::WebNotificationResources> {
+ static const SkBitmap& image(
+ const blink::WebNotificationResources& resources) {
+ return resources.image;
+ }
+
+ static const SkBitmap& icon(
+ const blink::WebNotificationResources& resources) {
+ return resources.icon;
+ }
+
+ static const SkBitmap& badge(
+ const blink::WebNotificationResources& resources) {
+ return resources.badge;
+ }
+
+ static base::span<const SkBitmap> action_icons(
+ const blink::WebNotificationResources&);
+
+ static bool Read(blink::mojom::NotificationResourcesDataView,
+ blink::WebNotificationResources* output);
+};
+
+} // namespace mojo
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_NOTIFICATION_STRUCT_TRAITS_H_
diff --git a/chromium/third_party/blink/renderer/platform/mojo/notification_struct_traits_test.cc b/chromium/third_party/blink/renderer/platform/mojo/notification_struct_traits_test.cc
new file mode 100644
index 00000000000..6f02d86a1e4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/notification_struct_traits_test.cc
@@ -0,0 +1,175 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/mojo/notification_struct_traits.h"
+
+#include <algorithm>
+
+#include "base/macros.h"
+#include "base/strings/stringprintf.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/modules/notifications/notification.mojom-blink.h"
+#include "third_party/blink/public/platform/modules/notifications/web_notification_data.h"
+#include "third_party/blink/public/platform/modules/notifications/web_notification_resources.h"
+#include "third_party/blink/public/platform/web_url.h"
+#include "third_party/blink/public/platform/web_vector.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkColor.h"
+
+namespace blink {
+
+namespace {
+
+const char kNotificationBaseUrl[] = "https://example.com/directory/";
+
+SkBitmap CreateBitmap(int width, int height, SkColor color) {
+ SkBitmap bitmap;
+ bitmap.allocN32Pixels(width, height);
+ bitmap.eraseColor(color);
+ return bitmap;
+}
+
+// Returns true if |lhs| and |rhs| have the same width and height and the
+// pixel at position (0, 0) is the same color in both.
+bool ImagesShareDimensionsAndColor(const SkBitmap& lhs, const SkBitmap& rhs) {
+ return lhs.width() == rhs.width() && lhs.height() == rhs.height() &&
+ lhs.getColor(0, 0) == rhs.getColor(0, 0);
+}
+
+} // namespace
+
+TEST(NotificationStructTraitsTest, NotificationDataRoundtrip) {
+ WebNotificationData notification_data;
+ notification_data.title = "Notification Title";
+ notification_data.direction = WebNotificationData::kDirectionRightToLeft;
+ notification_data.lang = "foo-lang";
+ notification_data.body = "Notification body...";
+ notification_data.tag = "notificationTag";
+
+ const KURL kBaseUrl(kNotificationBaseUrl);
+ notification_data.image = WebURL(KURL(kBaseUrl, "noti_img.png"));
+ notification_data.icon = WebURL(KURL(kBaseUrl, "noti_icon.png"));
+ notification_data.badge = WebURL(KURL(kBaseUrl, "noti_badge.png"));
+
+ const int vibrate[] = {200, 100, 300};
+ WebVector<char> vibrate_vector(vibrate, arraysize(vibrate));
+ notification_data.vibrate = vibrate_vector;
+
+ notification_data.timestamp = 1513963983000.;
+ notification_data.renotify = true;
+ notification_data.silent = true;
+ notification_data.require_interaction = true;
+
+ const char data[] = "some binary data";
+ WebVector<char> data_vector(data, arraysize(data));
+ notification_data.data = data_vector;
+
+ WebVector<blink::WebNotificationAction> actions(static_cast<size_t>(2));
+
+ actions[0].type = blink::WebNotificationAction::kButton;
+ actions[0].action = "my_button_click";
+ actions[0].title = "Button Title";
+ actions[0].icon = blink::WebURL(KURL(kBaseUrl, "button.png"));
+
+ actions[1].type = blink::WebNotificationAction::kText;
+ actions[1].action = "on_reply";
+ actions[1].title = "Reply";
+ actions[1].icon = blink::WebURL(KURL(kBaseUrl, "replyButton.png"));
+ actions[1].placeholder = "placeholder...";
+
+ notification_data.actions = actions;
+
+ WebNotificationData roundtrip_notification_data;
+
+ ASSERT_TRUE(mojom::blink::NotificationData::Deserialize(
+ mojom::blink::NotificationData::Serialize(&notification_data),
+ &roundtrip_notification_data));
+
+ EXPECT_EQ(notification_data.title, roundtrip_notification_data.title);
+ EXPECT_EQ(notification_data.direction, roundtrip_notification_data.direction);
+ EXPECT_EQ(notification_data.lang, roundtrip_notification_data.lang);
+ EXPECT_EQ(notification_data.tag, roundtrip_notification_data.tag);
+ EXPECT_EQ(notification_data.image, roundtrip_notification_data.image);
+ EXPECT_EQ(notification_data.icon, roundtrip_notification_data.icon);
+ EXPECT_EQ(notification_data.badge, roundtrip_notification_data.badge);
+
+ ASSERT_EQ(notification_data.vibrate.size(),
+ roundtrip_notification_data.vibrate.size());
+ EXPECT_TRUE(std::equal(notification_data.vibrate.begin(),
+ notification_data.vibrate.end(),
+ roundtrip_notification_data.vibrate.begin()));
+
+ EXPECT_EQ(notification_data.timestamp, roundtrip_notification_data.timestamp);
+ EXPECT_EQ(notification_data.renotify, roundtrip_notification_data.renotify);
+ EXPECT_EQ(notification_data.silent, roundtrip_notification_data.silent);
+ EXPECT_EQ(notification_data.require_interaction,
+ roundtrip_notification_data.require_interaction);
+
+ ASSERT_EQ(notification_data.data.size(),
+ roundtrip_notification_data.data.size());
+ EXPECT_TRUE(std::equal(notification_data.data.begin(),
+ notification_data.data.end(),
+ roundtrip_notification_data.data.begin()));
+
+ ASSERT_EQ(notification_data.actions.size(),
+ roundtrip_notification_data.actions.size());
+ for (size_t i = 0; i < notification_data.actions.size(); ++i) {
+ SCOPED_TRACE(base::StringPrintf("Action index: %zd", i));
+ EXPECT_EQ(notification_data.actions[i].type,
+ roundtrip_notification_data.actions[i].type);
+ EXPECT_EQ(notification_data.actions[i].action,
+ roundtrip_notification_data.actions[i].action);
+ EXPECT_EQ(notification_data.actions[i].title,
+ roundtrip_notification_data.actions[i].title);
+ EXPECT_EQ(notification_data.actions[i].icon,
+ roundtrip_notification_data.actions[i].icon);
+ EXPECT_EQ(notification_data.actions[i].placeholder,
+ roundtrip_notification_data.actions[i].placeholder);
+ }
+}
+
+TEST(NotificationStructTraitsTest, NotificationResourcesRoundtrip) {
+ WebNotificationResources resources;
+
+ resources.image = CreateBitmap(300, 100, SK_ColorCYAN);
+ resources.icon = CreateBitmap(80, 100, SK_ColorRED);
+ resources.badge = CreateBitmap(50, 40, SK_ColorGREEN);
+
+ WebVector<SkBitmap> action_icons(static_cast<size_t>(2));
+ action_icons[0] = CreateBitmap(10, 10, SK_ColorLTGRAY);
+ action_icons[1] = CreateBitmap(11, 11, SK_ColorDKGRAY);
+
+ resources.action_icons = action_icons;
+
+ WebNotificationResources roundtrip_resources;
+
+ ASSERT_TRUE(mojom::blink::NotificationResources::Deserialize(
+ mojom::blink::NotificationResources::Serialize(&resources),
+ &roundtrip_resources));
+
+ ASSERT_FALSE(roundtrip_resources.image.empty());
+ EXPECT_TRUE(ImagesShareDimensionsAndColor(resources.image,
+ roundtrip_resources.image));
+
+ ASSERT_FALSE(roundtrip_resources.icon.empty());
+ EXPECT_TRUE(
+ ImagesShareDimensionsAndColor(resources.icon, roundtrip_resources.icon));
+
+ ASSERT_FALSE(roundtrip_resources.badge.empty());
+ EXPECT_TRUE(ImagesShareDimensionsAndColor(resources.badge,
+ roundtrip_resources.badge));
+
+ ASSERT_EQ(resources.action_icons.size(),
+ roundtrip_resources.action_icons.size());
+
+ for (size_t i = 0; i < roundtrip_resources.action_icons.size(); ++i) {
+ SCOPED_TRACE(base::StringPrintf("Action icon index: %zd", i));
+
+ ASSERT_FALSE(roundtrip_resources.action_icons[i].empty());
+ EXPECT_TRUE(ImagesShareDimensionsAndColor(
+ resources.action_icons[i], roundtrip_resources.action_icons[i]));
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/mojo/referrer.typemap b/chromium/third_party/blink/renderer/platform/mojo/referrer.typemap
new file mode 100644
index 00000000000..f151339e76c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/referrer.typemap
@@ -0,0 +1,14 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//third_party/blink/public/platform/referrer.mojom"
+public_headers =
+ [ "//third_party/blink/renderer/platform/weborigin/referrer.h" ]
+traits_headers =
+ [ "//third_party/blink/renderer/platform/mojo/referrer_struct_traits.h" ]
+deps = [
+ "//mojo/public/cpp/bindings",
+ "//url",
+]
+type_mappings = [ "blink.mojom.Referrer=::blink::Referrer" ]
diff --git a/chromium/third_party/blink/renderer/platform/mojo/referrer_struct_traits.h b/chromium/third_party/blink/renderer/platform/mojo/referrer_struct_traits.h
new file mode 100644
index 00000000000..fa898aa3df1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/referrer_struct_traits.h
@@ -0,0 +1,47 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_REFERRER_STRUCT_TRAITS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_REFERRER_STRUCT_TRAITS_H_
+
+#include "third_party/blink/public/platform/referrer_policy_enum_traits.h"
+#include "third_party/blink/public/platform/web_referrer_policy.h"
+#include "third_party/blink/renderer/platform/weborigin/referrer.h"
+#include "third_party/blink/renderer/platform/weborigin/referrer_policy.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+namespace mojo {
+
+template <>
+struct StructTraits<blink::mojom::ReferrerDataView, blink::Referrer> {
+ static blink::KURL url(const blink::Referrer& referrer) {
+ if (referrer.referrer == blink::Referrer::NoReferrer())
+ return blink::KURL();
+
+ return blink::KURL(blink::KURL(), referrer.referrer);
+ }
+
+ // Equality of values is asserted in //Source/web/AssertMatchingEnums.cpp.
+ static blink::WebReferrerPolicy policy(const blink::Referrer& referrer) {
+ return static_cast<blink::WebReferrerPolicy>(referrer.referrer_policy);
+ }
+
+ static bool Read(blink::mojom::ReferrerDataView data, blink::Referrer* out) {
+ blink::KURL referrer;
+ blink::WebReferrerPolicy webReferrerPolicy;
+ if (!data.ReadUrl(&referrer) || !data.ReadPolicy(&webReferrerPolicy))
+ return false;
+
+ out->referrer_policy =
+ static_cast<blink::ReferrerPolicy>(webReferrerPolicy);
+ out->referrer = AtomicString(referrer.GetString());
+
+ // Mimics the DCHECK() done in the blink::Referrer constructor.
+ return referrer.IsValid() || out->referrer == blink::Referrer::NoReferrer();
+ }
+};
+
+} // namespace mojo
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_REFERRER_STRUCT_TRAITS_H_
diff --git a/chromium/third_party/blink/renderer/platform/mojo/revocable_binding.h b/chromium/third_party/blink/renderer/platform/mojo/revocable_binding.h
new file mode 100644
index 00000000000..b8d287b06b0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/revocable_binding.h
@@ -0,0 +1,181 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_REVOCABLE_BINDING_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_REVOCABLE_BINDING_H_
+
+#include <utility>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+#include "mojo/public/cpp/bindings/connection_error_callback.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/lib/binding_state.h"
+#include "mojo/public/cpp/bindings/raw_ptr_impl_ref_traits.h"
+#include "mojo/public/cpp/system/core.h"
+#include "third_party/blink/renderer/platform/mojo/interface_invalidator.h"
+
+namespace blink {
+
+class MessageReceiver;
+
+// RevocableBinding is a wrapper around a Binding that has to be tied to an
+// InterfaceInvalidator when bound to a message pipe. The underlying connection
+// is automatically closed once the InterfaceInvalidator is destroyed, and the
+// binding will behave as if its peer had closed the connection. This is useful
+// for tying the lifetime of mojo interfaces to another object.
+//
+// TODO(austinct): Add set_connection_error_with_reason_handler(),
+// CloseWithReason(), ReportBadMessage() and GetBadMessageCallback() methods if
+// needed. Undesirable for now because of the std::string parameter.
+template <typename Interface,
+ typename ImplRefTraits = mojo::RawPtrImplRefTraits<Interface>>
+class RevocableBinding : public InterfaceInvalidator::Observer {
+ public:
+ using ImplPointerType = typename ImplRefTraits::PointerType;
+
+ // Constructs an incomplete binding that will use the implementation |impl|.
+ // The binding may be completed with a subsequent call to the |Bind| method.
+ // Does not take ownership of |impl|, which must outlive the binding.
+ explicit RevocableBinding(ImplPointerType impl) : binding_(std::move(impl)) {}
+
+ // Constructs a completed binding of |impl| to the message pipe endpoint in
+ // |request|, taking ownership of the endpoint. Does not take ownership of
+ // |impl|, which must outlive the binding. Ties the lifetime of the binding to
+ // |invalidator|.
+ RevocableBinding(ImplPointerType impl,
+ mojo::InterfaceRequest<Interface> request,
+ InterfaceInvalidator* invalidator,
+ scoped_refptr<base::SingleThreadTaskRunner> runner = nullptr)
+ : RevocableBinding(std::move(impl)) {
+ Bind(std::move(request), invalidator, std::move(runner));
+ }
+
+ // Tears down the binding, closing the message pipe and leaving the interface
+ // implementation unbound.
+ ~RevocableBinding() { SetInvalidator(nullptr); }
+
+ // Completes a binding that was constructed with only an interface
+ // implementation by removing the message pipe endpoint from |request| and
+ // binding it to the previously specified implementation. Ties the lifetime of
+ // the binding to |invalidator|.
+ void Bind(mojo::InterfaceRequest<Interface> request,
+ InterfaceInvalidator* invalidator,
+ scoped_refptr<base::SingleThreadTaskRunner> runner = nullptr) {
+ DCHECK(invalidator);
+ binding_.Bind(std::move(request), std::move(runner));
+ SetInvalidator(invalidator);
+ }
+
+ // Adds a message filter to be notified of each incoming message before
+ // dispatch. If a filter returns |false| from Accept(), the message is not
+ // dispatched and the pipe is closed. Filters cannot be removed.
+ void AddFilter(std::unique_ptr<MessageReceiver> filter) {
+ binding_.AddFilter(std::move(filter));
+ }
+
+ // Whether there are any associated interfaces running on the pipe currently.
+ bool HasAssociatedInterfaces() const {
+ return binding_.HasAssociatedInterfaces();
+ }
+
+ // Stops processing incoming messages until
+ // ResumeIncomingMethodCallProcessing().
+ // Outgoing messages are still sent.
+ //
+ // No errors are detected on the message pipe while paused.
+ //
+ // This method may only be called if the object has been bound to a message
+ // pipe and there are no associated interfaces running.
+ void PauseIncomingMethodCallProcessing() {
+ binding_.PauseIncomingMethodCallProcessing();
+ }
+ void ResumeIncomingMethodCallProcessing() {
+ binding_.ResumeIncomingMethodCallProcessing();
+ }
+
+ // Closes the message pipe that was previously bound. Put this object into a
+ // state where it can be rebound to a new pipe.
+ void Close() {
+ SetInvalidator(nullptr);
+ binding_.Close();
+ }
+
+ // Unbinds the underlying pipe from this binding and returns it so it can be
+ // used in another context, such as on another sequence or with a different
+ // implementation. Put this object into a state where it can be rebound to a
+ // new pipe.
+ //
+ // This method may only be called if the object has been bound to a message
+ // pipe and there are no associated interfaces running.
+ //
+ // TODO(yzshen): For now, users need to make sure there is no one holding
+ // on to associated interface endpoint handles at both sides of the
+ // message pipe in order to call this method. We need a way to forcefully
+ // invalidate associated interface endpoint handles.
+ mojo::InterfaceRequest<Interface> Unbind() {
+ SetInvalidator(nullptr);
+ return binding_.Unbind();
+ }
+
+ // Sets an error handler that will be called if a connection error occurs on
+ // the bound message pipe.
+ //
+ // This method may only be called after this RevocableBinding has been bound
+ // to a message pipe. The error handler will be reset when this
+ // RevocableBinding is unbound, closed or invalidated.
+ void set_connection_error_handler(base::OnceClosure error_handler) {
+ binding_.set_connection_error_handler(std::move(error_handler));
+ }
+
+ // Returns the interface implementation that was previously specified. Caller
+ // does not take ownership.
+ Interface* impl() { return binding_.impl(); }
+
+ // Indicates whether the binding has been completed (i.e., whether a message
+ // pipe has been bound to the implementation).
+ explicit operator bool() const { return static_cast<bool>(binding_); }
+
+ // Sends a no-op message on the underlying message pipe and runs the current
+ // message loop until its response is received. This can be used in tests to
+ // verify that no message was sent on a message pipe in response to some
+ // stimulus.
+ void FlushForTesting() { binding_.FlushForTesting(); }
+
+ private:
+ // InterfaceInvalidator::Observer
+ void OnInvalidate() override {
+ if (binding_) {
+ binding_.internal_state()->RaiseError();
+ }
+ if (invalidator_) {
+ invalidator_->RemoveObserver(this);
+ }
+ invalidator_.reset();
+ }
+
+ // Replaces the existing invalidator with a new invalidator and changes the
+ // invalidator being observed.
+ void SetInvalidator(InterfaceInvalidator* invalidator) {
+ if (invalidator_)
+ invalidator_->RemoveObserver(this);
+
+ invalidator_.reset();
+ if (invalidator) {
+ invalidator_ = invalidator->GetWeakPtr();
+ invalidator_->AddObserver(this);
+ }
+ }
+
+ mojo::Binding<Interface, ImplRefTraits> binding_;
+ base::WeakPtr<InterfaceInvalidator> invalidator_;
+
+ DISALLOW_COPY_AND_ASSIGN(RevocableBinding);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_REVOCABLE_BINDING_H_
diff --git a/chromium/third_party/blink/renderer/platform/mojo/revocable_interface_ptr.h b/chromium/third_party/blink/renderer/platform/mojo/revocable_interface_ptr.h
new file mode 100644
index 00000000000..32101602afd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/revocable_interface_ptr.h
@@ -0,0 +1,237 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_REVOCABLE_INTERFACE_PTR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_REVOCABLE_INTERFACE_PTR_H_
+
+#include <stdint.h>
+
+#include <string>
+#include <utility>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
+#include "mojo/public/cpp/bindings/connection_error_callback.h"
+#include "mojo/public/cpp/bindings/interface_ptr_info.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/lib/interface_ptr_state.h"
+#include "third_party/blink/renderer/platform/mojo/interface_invalidator.h"
+
+namespace blink {
+
+// RevocableInterfacePtr is a wrapper around an InterfacePtr that has to be tied
+// to an InterfaceInvalidator when bound to a message pipe. The underlying
+// connection is closed once the InterfaceInvalidator is destroyed and the
+// interface will behave as if its peer had closed the connection. This is
+// useful for tying the lifetime of interface pointers to another object.
+template <typename Interface>
+class RevocableInterfacePtr : public InterfaceInvalidator::Observer {
+ public:
+ using PtrType = mojo::InterfacePtr<Interface>;
+ using PtrInfoType = mojo::InterfacePtrInfo<Interface>;
+ using Proxy = typename Interface::Proxy_;
+
+ // Constructs an unbound RevocableInterfacePtr.
+ RevocableInterfacePtr() {}
+ RevocableInterfacePtr(std::nullptr_t) {}
+
+ // Takes over the binding of another RevocableInterfacePtr.
+ RevocableInterfacePtr(RevocableInterfacePtr&& other) {
+ interface_ptr_ = std::move(other.interface_ptr_);
+ SetInvalidator(other.invalidator_.get());
+ // Reset the other interface ptr to remove it as an observer of the
+ // invalidator.
+ other.reset();
+ }
+
+ RevocableInterfacePtr(PtrInfoType info, InterfaceInvalidator* invalidator) {
+ Bind(std::move(info), invalidator);
+ }
+
+ // Takes over the binding of another RevocableInterfacePtr, and closes any
+ // message pipe already bound to this pointer.
+ RevocableInterfacePtr& operator=(RevocableInterfacePtr&& other) {
+ reset();
+ interface_ptr_ = std::move(other.interface_ptr);
+ SetInvalidator(other.invalidator_.get());
+ // Reset the other interface ptr to remove it as an observer of the
+ // invalidator.
+ other.reset();
+ return *this;
+ }
+
+ // Assigning nullptr to this class causes it to close the currently bound
+ // message pipe (if any) and returns the pointer to the unbound state.
+ RevocableInterfacePtr& operator=(decltype(nullptr)) {
+ reset();
+ return *this;
+ }
+
+ // Closes the bound message pipe (if any) on destruction.
+ ~RevocableInterfacePtr() {
+ if (invalidator_) {
+ invalidator_->RemoveObserver(this);
+ }
+ }
+
+ // Binds the RevocableInterfacePtr to a remote implementation of Interface.
+ //
+ // Calling with an invalid |info| (containing an invalid message pipe handle)
+ // has the same effect as reset(). In this case, the InterfacePtr is not
+ // considered as bound.
+ //
+ // |runner| must belong to the same thread. It will be used to dispatch all
+ // callbacks and connection error notification. It is useful when you attach
+ // multiple task runners to a single thread for the purposes of task
+ // scheduling.
+ void Bind(PtrInfoType info,
+ InterfaceInvalidator* invalidator,
+ scoped_refptr<base::SingleThreadTaskRunner> runner = nullptr) {
+ DCHECK(invalidator);
+ reset();
+ if (info.is_valid()) {
+ interface_ptr_.Bind(std::move(info), std::move(runner));
+ invalidator_ = invalidator->GetWeakPtr();
+ invalidator_->AddObserver(this);
+ }
+ }
+
+ // Returns a raw pointer to the local proxy. Caller does not take ownership.
+ // Note that the local proxy is thread hostile, as stated above.
+ Proxy* get() const { return interface_ptr_.get(); }
+
+ // Functions like a pointer to Interface. Must already be bound.
+ Proxy* operator->() const { return get(); }
+ Proxy& operator*() const { return *get(); }
+
+ // Returns the version number of the interface that the remote side supports.
+ uint32_t version() const { return interface_ptr_.version(); }
+
+ // Queries the max version that the remote side supports. On completion, the
+ // result will be returned as the input of |callback|. The version number of
+ // this interface pointer will also be updated.
+ void QueryVersion(const base::RepeatingCallback<void(uint32_t)>& callback) {
+ interface_ptr_.QueryVersion(callback);
+ }
+
+ // If the remote side doesn't support the specified version, it will close its
+ // end of the message pipe asynchronously. This does nothing if it's already
+ // known that the remote side supports the specified version, i.e., if
+ // |version <= this->version()|.
+ //
+ // After calling RequireVersion() with a version not supported by the remote
+ // side, all subsequent calls to interface methods will be ignored.
+ void RequireVersion(uint32_t version) {
+ interface_ptr_.RequireVersion(version);
+ }
+
+ // Sends a no-op message on the underlying message pipe and runs the current
+ // message loop until its response is received. This can be used in tests to
+ // verify that no message was sent on a message pipe in response to some
+ // stimulus.
+ void FlushForTesting() { interface_ptr_.FlushForTesting(); }
+
+ // Closes the bound message pipe, if any.
+ void reset() {
+ interface_ptr_.reset();
+ SetInvalidator(nullptr);
+ }
+
+ // Similar to the method above, but also specifies a disconnect reason.
+ void ResetWithReason(uint32_t custom_reason, const std::string& description) {
+ interface_ptr_.ResetWithReason(custom_reason, description);
+ SetInvalidator(nullptr);
+ }
+
+ // Whether there are any associated interfaces running on the pipe currently.
+ bool HasAssociatedInterfaces() const {
+ return interface_ptr_.HasAssociatedInterfaces();
+ }
+
+ // Indicates whether the message pipe has encountered an error. If true,
+ // method calls made on this interface will be dropped (and may already have
+ // been dropped).
+ bool encountered_error() const { return interface_ptr_.encountered_error(); }
+
+ // Registers a handler to receive error notifications. The handler will be
+ // called from the sequence that owns this RevocableInterfacePtr.
+ //
+ // This method may only be called after the RevocableInterfacePtr has been
+ // bound to a message pipe.
+ void set_connection_error_handler(base::OnceClosure error_handler) {
+ interface_ptr_.set_connection_error_handler(std::move(error_handler));
+ }
+
+ void set_connection_error_with_reason_handler(
+ mojo::ConnectionErrorWithReasonCallback error_handler) {
+ interface_ptr_.set_connection_error_with_reason_handler(
+ std::move(error_handler));
+ }
+
+ // Unbinds the RevocableInterfacePtr and returns the information which could
+ // be used to setup a RevocableInterfacePtr again. See comments on
+ // InterfacePtr::PassInterface for details.
+ PtrInfoType PassInterface() {
+ SetInvalidator(nullptr);
+ return interface_ptr_.PassInterface();
+ }
+
+ bool operator==(const RevocableInterfacePtr& other) const {
+ if (this == &other)
+ return true;
+
+ // Now that the two refer to different objects, they are equivalent if
+ // and only if they are both null.
+ return !(*this) && !other;
+ }
+
+ // Allow RevocableInterfacePtr<> to be used in boolean expressions.
+ explicit operator bool() const { return static_cast<bool>(interface_ptr_); }
+
+ private:
+ // InterfaceInvalidator::Observer
+ void OnInvalidate() override {
+ interface_ptr_.internal_state()->RaiseError();
+ if (invalidator_) {
+ invalidator_->RemoveObserver(this);
+ }
+ invalidator_.reset();
+ }
+
+ // Replaces the existing invalidator with a new invalidator and changes the
+ // invalidator being observed.
+ void SetInvalidator(InterfaceInvalidator* invalidator) {
+ if (invalidator_)
+ invalidator_->RemoveObserver(this);
+
+ invalidator_.reset();
+ if (invalidator) {
+ invalidator_ = invalidator->GetWeakPtr();
+ invalidator_->AddObserver(this);
+ }
+ }
+
+ PtrType interface_ptr_;
+ base::WeakPtr<InterfaceInvalidator> invalidator_;
+
+ DISALLOW_COPY_AND_ASSIGN(RevocableInterfacePtr);
+};
+
+template <typename Interface>
+mojo::InterfaceRequest<Interface> MakeRequest(
+ RevocableInterfacePtr<Interface>* ptr,
+ InterfaceInvalidator* invalidator,
+ scoped_refptr<base::SingleThreadTaskRunner> runner = nullptr) {
+ mojo::MessagePipe pipe;
+ ptr->Bind(mojo::InterfacePtrInfo<Interface>(std::move(pipe.handle0), 0u),
+ invalidator, std::move(runner));
+ return mojo::InterfaceRequest<Interface>(std::move(pipe.handle1));
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_REVOCABLE_INTERFACE_PTR_H_
diff --git a/chromium/third_party/blink/renderer/platform/mojo/revocable_strong_binding.h b/chromium/third_party/blink/renderer/platform/mojo/revocable_strong_binding.h
new file mode 100644
index 00000000000..69d7b27ed58
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/revocable_strong_binding.h
@@ -0,0 +1,135 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_REVOCABLE_STRONG_BINDING_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_REVOCABLE_STRONG_BINDING_H_
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "mojo/public/cpp/bindings/connection_error_callback.h"
+#include "mojo/public/cpp/bindings/filter_chain.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/message_header_validator.h"
+#include "mojo/public/cpp/system/core.h"
+#include "third_party/blink/renderer/platform/mojo/revocable_binding.h"
+
+namespace blink {
+
+class InterfaceInvalidator;
+
+template <typename Interface>
+class RevocableStrongBinding;
+
+template <typename Interface>
+using RevocableStrongBindingPtr =
+ base::WeakPtr<RevocableStrongBinding<Interface>>;
+
+// This is a wrapper around a StrongBinding that binds it to an interface
+// invalidator. When the invalidator is destroyed or a connection error is
+// detected, the interface implementation is deleted. If the task runner that a
+// RevocableStrongBinding is bound on is stopped, the connection error handler
+// will not be invoked and the implementation will not be deleted.
+//
+// To use, call RevocableStrongBinding<T>::Create() (see below) or the helper
+// MakeRevocableStrongBinding function:
+//
+// MakeRevocableStrongBinding(std::make_unique<FooImpl>(),
+// std::move(foo_request));
+//
+template <typename Interface>
+class RevocableStrongBinding {
+ public:
+ // Create a new RevocableStrongBinding instance. The instance owns itself,
+ // cleaning up only in the event of a pipe connection error or invalidation.
+ // Returns a WeakPtr to the new RevocableStrongBinding instance.
+ static RevocableStrongBindingPtr<Interface> Create(
+ std::unique_ptr<Interface> impl,
+ mojo::InterfaceRequest<Interface> request,
+ InterfaceInvalidator* invalidator) {
+ RevocableStrongBinding* binding = new RevocableStrongBinding(
+ std::move(impl), std::move(request), invalidator);
+ return binding->weak_factory_.GetWeakPtr();
+ }
+
+ // Note: The error handler must not delete the interface implementation.
+ //
+ // This method may only be called after this RevocableStrongBinding has been
+ // bound to a message pipe.
+ void set_connection_error_handler(base::OnceClosure error_handler) {
+ DCHECK(binding_.is_bound());
+ connection_error_handler_ = std::move(error_handler);
+ }
+
+ // Stops processing incoming messages until
+ // ResumeIncomingMethodCallProcessing().
+ // Outgoing messages are still sent.
+ //
+ // No errors are detected on the message pipe while paused.
+ //
+ // This method may only be called if the object has been bound to a message
+ // pipe and there are no associated interfaces running.
+ void PauseIncomingMethodCallProcessing() {
+ binding_.PauseIncomingMethodCallProcessing();
+ }
+ void ResumeIncomingMethodCallProcessing() {
+ binding_.ResumeIncomingMethodCallProcessing();
+ }
+
+ // Forces the binding to close. This destroys the RevocableStrongBinding
+ // instance.
+ void Close() { delete this; }
+
+ Interface* impl() { return impl_.get(); }
+
+ // Sends a message on the underlying message pipe and runs the current
+ // message loop until its response is received. This can be used in tests to
+ // verify that no message was sent on a message pipe in response to some
+ // stimulus.
+ void FlushForTesting() { binding_.FlushForTesting(); }
+
+ private:
+ RevocableStrongBinding(std::unique_ptr<Interface> impl,
+ mojo::InterfaceRequest<Interface> request,
+ InterfaceInvalidator* invalidator)
+ : impl_(std::move(impl)),
+ binding_(impl_.get(), std::move(request), invalidator),
+ weak_factory_(this) {
+ binding_.set_connection_error_handler(base::BindOnce(
+ &RevocableStrongBinding::OnConnectionError, base::Unretained(this)));
+ }
+
+ ~RevocableStrongBinding() = default;
+
+ void OnConnectionError() {
+ if (connection_error_handler_) {
+ std::move(connection_error_handler_).Run();
+ }
+ Close();
+ }
+
+ std::unique_ptr<Interface> impl_;
+ base::OnceClosure connection_error_handler_;
+ RevocableBinding<Interface> binding_;
+ base::WeakPtrFactory<RevocableStrongBinding> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(RevocableStrongBinding);
+};
+
+template <typename Interface, typename Impl>
+RevocableStrongBindingPtr<Interface> MakeRevocableStrongBinding(
+ std::unique_ptr<Impl> impl,
+ mojo::InterfaceRequest<Interface> request,
+ InterfaceInvalidator* invalidator) {
+ return RevocableStrongBinding<Interface>::Create(
+ std::move(impl), std::move(request), invalidator);
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_REVOCABLE_STRONG_BINDING_H_
diff --git a/chromium/third_party/blink/renderer/platform/mojo/security_origin.typemap b/chromium/third_party/blink/renderer/platform/mojo/security_origin.typemap
new file mode 100644
index 00000000000..3b2de4ac9b7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/security_origin.typemap
@@ -0,0 +1,18 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//url/mojom/origin.mojom"
+public_headers =
+ [ "//third_party/blink/renderer/platform/weborigin/security_origin.h" ]
+traits_headers = [
+ "//third_party/blink/renderer/platform/mojo/security_origin_struct_traits.h",
+]
+
+# Note: consumers of this typemap must themselves depend on platform.
+deps = [
+ "//mojo/public/cpp/bindings",
+ "//url",
+]
+type_mappings =
+ [ "url.mojom.Origin=scoped_refptr<const ::blink::SecurityOrigin>" ]
diff --git a/chromium/third_party/blink/renderer/platform/mojo/security_origin_struct_traits.h b/chromium/third_party/blink/renderer/platform/mojo/security_origin_struct_traits.h
new file mode 100644
index 00000000000..5aeaa5fc872
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/security_origin_struct_traits.h
@@ -0,0 +1,56 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_SECURITY_ORIGIN_STRUCT_TRAITS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_SECURITY_ORIGIN_STRUCT_TRAITS_H_
+
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "url/mojom/origin.mojom-blink.h"
+
+namespace mojo {
+
+template <>
+struct StructTraits<url::mojom::blink::Origin::DataView,
+ scoped_refptr<const ::blink::SecurityOrigin>> {
+ static WTF::String scheme(
+ const scoped_refptr<const ::blink::SecurityOrigin>& origin) {
+ return origin->Protocol();
+ }
+ static WTF::String host(
+ const scoped_refptr<const ::blink::SecurityOrigin>& origin) {
+ return origin->Host();
+ }
+ static uint16_t port(
+ const scoped_refptr<const ::blink::SecurityOrigin>& origin) {
+ return origin->EffectivePort();
+ }
+ static bool unique(
+ const scoped_refptr<const ::blink::SecurityOrigin>& origin) {
+ return origin->IsUnique();
+ }
+ static bool Read(url::mojom::blink::Origin::DataView data,
+ scoped_refptr<const ::blink::SecurityOrigin>* out) {
+ if (data.unique()) {
+ *out = ::blink::SecurityOrigin::CreateUnique();
+ } else {
+ WTF::String scheme;
+ WTF::String host;
+ if (!data.ReadScheme(&scheme) || !data.ReadHost(&host))
+ return false;
+
+ *out = ::blink::SecurityOrigin::Create(scheme, host, data.port());
+ }
+
+ // If a unique origin was created, but the unique flag wasn't set, then
+ // the values provided to 'create' were invalid.
+ if (!data.unique() && (*out)->IsUnique())
+ return false;
+
+ return true;
+ }
+};
+}
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_SECURITY_ORIGIN_STRUCT_TRAITS_H_
diff --git a/chromium/third_party/blink/renderer/platform/mojo/string.typemap b/chromium/third_party/blink/renderer/platform/mojo/string.typemap
new file mode 100644
index 00000000000..df022b0e774
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/string.typemap
@@ -0,0 +1,13 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//mojo/public/mojom/base/string16.mojom"
+public_headers =
+ [ "//third_party/blink/renderer/platform/wtf/text/wtf_string.h" ]
+traits_headers =
+ [ "//third_party/blink/renderer/platform/mojo/string16_mojom_traits.h" ]
+type_mappings = [
+ "mojo_base.mojom.BigString16=WTF::String[nullable_is_same_type,force_serialize]",
+ "mojo_base.mojom.String16=WTF::String[nullable_is_same_type,force_serialize]",
+]
diff --git a/chromium/third_party/blink/renderer/platform/mojo/string16_mojom_traits.cc b/chromium/third_party/blink/renderer/platform/mojo/string16_mojom_traits.cc
new file mode 100644
index 00000000000..ca4d5c3190a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/string16_mojom_traits.cc
@@ -0,0 +1,102 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/mojo/string16_mojom_traits.h"
+
+#include <cstring>
+
+#include "base/containers/span.h"
+#include "mojo/public/cpp/base/big_buffer.h"
+#include "mojo/public/cpp/base/big_buffer_mojom_traits.h"
+
+namespace mojo {
+
+// static
+void* StructTraits<mojo_base::mojom::String16DataView,
+ WTF::String>::SetUpContext(const WTF::String& input) {
+ // If it is null (i.e., StructTraits<>::IsNull() returns true), this method is
+ // guaranteed not to be called.
+ DCHECK(!input.IsNull());
+
+ if (!input.Is8Bit())
+ return nullptr;
+
+ return new base::string16(input.Characters8(),
+ input.Characters8() + input.length());
+}
+
+// static
+void StructTraits<mojo_base::mojom::String16DataView,
+ WTF::String>::TearDownContext(const WTF::String& input,
+ void* context) {
+ delete static_cast<base::string16*>(context);
+}
+
+// static
+base::span<const uint16_t>
+StructTraits<mojo_base::mojom::String16DataView, WTF::String>::data(
+ const WTF::String& input,
+ void* context) {
+ auto contextObject = static_cast<base::string16*>(context);
+ DCHECK_EQ(input.Is8Bit(), !!contextObject);
+
+ if (contextObject) {
+ return base::make_span(
+ reinterpret_cast<const uint16_t*>(contextObject->data()),
+ contextObject->size());
+ }
+
+ return base::make_span(
+ reinterpret_cast<const uint16_t*>(input.Characters16()), input.length());
+}
+
+// static
+bool StructTraits<mojo_base::mojom::String16DataView, WTF::String>::Read(
+ mojo_base::mojom::String16DataView data,
+ WTF::String* out) {
+ ArrayDataView<uint16_t> view;
+ data.GetDataDataView(&view);
+ *out = WTF::String(reinterpret_cast<const UChar*>(view.data()), view.size());
+ return true;
+}
+
+// static
+mojo_base::BigBuffer StructTraits<mojo_base::mojom::BigString16DataView,
+ WTF::String>::data(const WTF::String& input) {
+ if (input.Is8Bit()) {
+ base::string16 input16(input.Characters8(),
+ input.Characters8() + input.length());
+ return mojo_base::BigBuffer(
+ base::make_span(reinterpret_cast<const uint8_t*>(input16.data()),
+ input16.size() * sizeof(UChar)));
+ }
+
+ return mojo_base::BigBuffer(
+ base::make_span(reinterpret_cast<const uint8_t*>(input.Characters16()),
+ input.length() * sizeof(UChar)));
+}
+
+// static
+bool StructTraits<mojo_base::mojom::BigString16DataView, WTF::String>::Read(
+ mojo_base::mojom::BigString16DataView data,
+ WTF::String* out) {
+ mojo_base::BigBuffer buffer;
+ if (!data.ReadData(&buffer))
+ return false;
+ size_t size = buffer.size();
+ if (size % sizeof(UChar))
+ return false;
+
+ // An empty |mojo_base::BigBuffer| may have a null |data()| if empty.
+ if (!size) {
+ *out = g_empty_string;
+ } else {
+ *out = WTF::String(reinterpret_cast<const UChar*>(buffer.data()),
+ size / sizeof(UChar));
+ }
+
+ return true;
+}
+
+} // namespace mojo
diff --git a/chromium/third_party/blink/renderer/platform/mojo/string16_mojom_traits.h b/chromium/third_party/blink/renderer/platform/mojo/string16_mojom_traits.h
new file mode 100644
index 00000000000..101e3357edd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/string16_mojom_traits.h
@@ -0,0 +1,46 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_STRING16_MOJOM_TRAITS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_STRING16_MOJOM_TRAITS_H_
+
+#include "base/containers/span.h"
+#include "base/strings/string16.h"
+#include "mojo/public/cpp/bindings/struct_traits.h"
+#include "mojo/public/mojom/base/string16.mojom-blink.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace mojo_base {
+class BigBuffer;
+}
+
+namespace mojo {
+
+template <>
+struct PLATFORM_EXPORT
+ StructTraits<mojo_base::mojom::String16DataView, WTF::String> {
+ static bool IsNull(const WTF::String& input) { return input.IsNull(); }
+ static void SetToNull(WTF::String* output) { *output = WTF::String(); }
+
+ static void* SetUpContext(const WTF::String& input);
+ static void TearDownContext(const WTF::String& input, void* context);
+
+ static base::span<const uint16_t> data(const WTF::String& input,
+ void* context);
+ static bool Read(mojo_base::mojom::String16DataView, WTF::String* out);
+};
+
+template <>
+struct PLATFORM_EXPORT
+ StructTraits<mojo_base::mojom::BigString16DataView, WTF::String> {
+ static bool IsNull(const WTF::String& input) { return input.IsNull(); }
+ static void SetToNull(WTF::String* output) { *output = WTF::String(); }
+
+ static mojo_base::BigBuffer data(const WTF::String& input);
+ static bool Read(mojo_base::mojom::BigString16DataView, WTF::String* out);
+};
+
+} // namespace mojo
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_STRING16_MOJOM_TRAITS_H_
diff --git a/chromium/third_party/blink/renderer/platform/mojo/string16_mojom_traits_test.cc b/chromium/third_party/blink/renderer/platform/mojo/string16_mojom_traits_test.cc
new file mode 100644
index 00000000000..8aa308d79c2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/string16_mojom_traits_test.cc
@@ -0,0 +1,95 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cstdint>
+
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/rand_util.h"
+#include "mojo/common/test_common_custom_types.mojom-blink.h"
+#include "mojo/public/cpp/base/big_buffer_mojom_traits.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
+#include "mojo/public/mojom/base/string16.mojom-blink.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/mojo/string16_mojom_traits.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+TEST(String16MojomTraitsTest, String16) {
+ // |str| is 8-bit.
+ String str = String::FromUTF8("hello world");
+ String output;
+
+ ASSERT_TRUE(
+ mojo::test::SerializeAndDeserialize<mojo_base::mojom::blink::String16>(
+ &str, &output));
+ ASSERT_EQ(str, output);
+
+ // Replace the "o"s in "hello world" with "o"s with acute, so that |str| is
+ // 16-bit.
+ str = String::FromUTF8("hell\xC3\xB3 w\xC3\xB3rld");
+
+ ASSERT_TRUE(
+ mojo::test::SerializeAndDeserialize<mojo_base::mojom::blink::String16>(
+ &str, &output));
+ ASSERT_EQ(str, output);
+}
+
+TEST(String16MojomTraitsTest, EmptyString16) {
+ String str = String::FromUTF8("");
+ String output;
+
+ ASSERT_TRUE(
+ mojo::test::SerializeAndDeserialize<mojo_base::mojom::blink::String16>(
+ &str, &output));
+ ASSERT_EQ(str, output);
+}
+
+TEST(String16MojomTraitsTest, BigString16_Empty) {
+ String str = String::FromUTF8("");
+ String output;
+
+ ASSERT_TRUE(
+ mojo::test::SerializeAndDeserialize<mojo_base::mojom::blink::BigString16>(
+ &str, &output));
+ ASSERT_EQ(str, output);
+}
+
+TEST(String16MojomTraitsTest, BigString16_Short) {
+ String str = String::FromUTF8("hello world");
+ ASSERT_TRUE(str.Is8Bit());
+ String output;
+
+ ASSERT_TRUE(
+ mojo::test::SerializeAndDeserialize<mojo_base::mojom::blink::BigString16>(
+ &str, &output));
+ ASSERT_EQ(str, output);
+
+ // Replace the "o"s in "hello world" with "o"s with acute, so that |str| is
+ // 16-bit.
+ str = String::FromUTF8("hell\xC3\xB3 w\xC3\xB3rld");
+
+ ASSERT_TRUE(
+ mojo::test::SerializeAndDeserialize<mojo_base::mojom::blink::BigString16>(
+ &str, &output));
+ ASSERT_EQ(str, output);
+}
+
+TEST(String16MojomTraitsTest, BigString16_Long) {
+ WTF::Vector<char> random_latin1_string(1024 * 1024);
+ base::RandBytes(random_latin1_string.data(), random_latin1_string.size());
+
+ String str(random_latin1_string.data(), random_latin1_string.size());
+ String output;
+
+ ASSERT_TRUE(
+ mojo::test::SerializeAndDeserialize<mojo_base::mojom::blink::BigString16>(
+ &str, &output));
+ ASSERT_EQ(str, output);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/mojo/time.typemap b/chromium/third_party/blink/renderer/platform/mojo/time.typemap
new file mode 100644
index 00000000000..c02526da6f2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/time.typemap
@@ -0,0 +1,19 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//mojo/public/mojom/base/time.mojom"
+public_headers = [ "//third_party/blink/renderer/platform/wtf/time.h" ]
+traits_headers = [
+ "//ipc/ipc_message_utils.h",
+ "//mojo/public/cpp/base/time_mojom_traits.h",
+]
+public_deps = [
+ "//ipc",
+ "//mojo/public/mojom/base",
+]
+
+type_mappings = [
+ "mojo_base.mojom.Time=WTF::Time[copyable_pass_by_value]",
+ "mojo_base.mojom.TimeDelta=WTF::TimeDelta[copyable_pass_by_value]",
+]
diff --git a/chromium/third_party/blink/renderer/platform/network/BUILD.gn b/chromium/third_party/blink/renderer/platform/network/BUILD.gn
new file mode 100644
index 00000000000..c1d09ff49af
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/BUILD.gn
@@ -0,0 +1,118 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/jumbo.gni")
+import("//third_party/blink/renderer/build/scripts/scripts.gni")
+import("//third_party/blink/renderer/platform/platform.gni")
+import("//third_party/blink/renderer/platform/platform_generated.gni")
+
+make_names("http_names") {
+ in_files = [ "http_names.json5" ]
+ output_dir = "$blink_platform_output_dir/network"
+}
+
+group("make_generated") {
+ visibility = [
+ ":*",
+ "//third_party/blink/renderer/platform:*",
+ ]
+
+ public_deps = [
+ ":http_names",
+ ]
+}
+
+blink_platform_sources("network") {
+ sources = [
+ "content_security_policy_parsers.cc",
+ "content_security_policy_parsers.h",
+ "content_security_policy_response_headers.cc",
+ "content_security_policy_response_headers.h",
+ "encoded_form_data.cc",
+ "encoded_form_data.h",
+ "form_data_encoder.cc",
+ "form_data_encoder.h",
+ "header_field_tokenizer.cc",
+ "header_field_tokenizer.h",
+ "http_header_map.cc",
+ "http_header_map.h",
+ "http_parsers.cc",
+ "http_parsers.h",
+ "mime/content_type.cc",
+ "mime/content_type.h",
+ "mime/mime_type_from_url.cc",
+ "mime/mime_type_from_url.h",
+ "mime/mime_type_registry.cc",
+ "mime/mime_type_registry.h",
+ "network_hints.cc",
+ "network_hints.h",
+ "network_instrumentation.cc",
+ "network_instrumentation.h",
+ "network_log.h",
+ "network_state_notifier.cc",
+ "network_state_notifier.h",
+ "network_utils.cc",
+ "network_utils.h",
+ "parsed_content_disposition.cc",
+ "parsed_content_disposition.h",
+ "parsed_content_header_field_parameters.cc",
+ "parsed_content_header_field_parameters.h",
+ "parsed_content_type.cc",
+ "parsed_content_type.h",
+ "server_timing_header.cc",
+ "server_timing_header.h",
+ "web_socket_handshake_request.cc",
+ "web_socket_handshake_request.h",
+ "web_socket_handshake_response.cc",
+ "web_socket_handshake_response.h",
+ ]
+
+ sources += get_target_outputs(":http_names")
+
+ deps = [
+ "//media",
+ ]
+}
+
+jumbo_source_set("unit_tests") {
+ visibility = [ "//third_party/blink/renderer/platform:*" ]
+ testonly = true
+
+ sources = [
+ "encoded_form_data_test.cc",
+ "http_parsers_test.cc",
+ "mime/mime_type_registry_test.cc",
+ "network_state_notifier_test.cc",
+ "network_utils_test.cc",
+ "parsed_content_disposition_test.cc",
+ "parsed_content_header_field_parameters_test.cc",
+ "parsed_content_type_test.cc",
+ ]
+
+ configs += [ "//third_party/blink/renderer/platform:blink_platform_config" ]
+
+ deps = [
+ "//testing/gtest",
+ "//third_party/blink/renderer/platform:platform",
+ ]
+}
+
+jumbo_source_set("test_support") {
+ visibility = [ "//third_party/blink/renderer/platform:test_support" ]
+ testonly = true
+
+ sources = [
+ "mime/mock_mime_registry.h",
+ ]
+
+ configs += [
+ "//third_party/blink/renderer:non_test_config",
+ "//third_party/blink/renderer/platform:blink_platform_config",
+ ]
+
+ public_deps = [
+ "//net",
+ "//third_party/blink/renderer/platform/blob:generator",
+ ]
+}
diff --git a/chromium/third_party/blink/renderer/platform/network/DEPS b/chromium/third_party/blink/renderer/platform/network/DEPS
new file mode 100644
index 00000000000..531bbba099f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/DEPS
@@ -0,0 +1,11 @@
+include_rules = [
+ "+media",
+ "-media/blink",
+ # net/ includes should be allowed only in a limited set of directories,
+ # so we have separate DEPS from platform's one.
+ "+net/base",
+ "+net/http",
+ "+net/nqe",
+ # For URLRequestDataJob::BuildResponse().
+ "+net/url_request/url_request_data_job.h",
+]
diff --git a/chromium/third_party/blink/renderer/platform/network/HTTPParsersFuzzer.dict b/chromium/third_party/blink/renderer/platform/network/HTTPParsersFuzzer.dict
new file mode 100644
index 00000000000..3516a839150
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/HTTPParsersFuzzer.dict
@@ -0,0 +1,24 @@
+"block"
+"charset"
+"content-length"
+"content-type"
+"desc"
+"description"
+"dur"
+"image/png"
+"iso-8859-1"
+"max-age"
+"metric"
+"metric"
+"mode"
+"must-revalidate"
+"no-cache"
+"no-store"
+"none"
+"nonsense"
+"nosniff"
+"not-nosniff"
+"report"
+"text/html"
+"url"
+"utf-8" \ No newline at end of file
diff --git a/chromium/third_party/blink/renderer/platform/network/content_security_policy_parsers.cc b/chromium/third_party/blink/renderer/platform/network/content_security_policy_parsers.cc
new file mode 100644
index 00000000000..50b795fd45f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/content_security_policy_parsers.cc
@@ -0,0 +1,69 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/network/content_security_policy_parsers.h"
+
+#include "third_party/blink/public/platform/web_content_security_policy.h"
+#include "third_party/blink/renderer/platform/wtf/ascii_ctype.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
+
+namespace blink {
+
+bool IsCSPDirectiveNameCharacter(UChar c) {
+ return IsASCIIAlphanumeric(c) || c == '-';
+}
+
+bool IsCSPDirectiveValueCharacter(UChar c) {
+ return IsASCIISpace(c) || (c >= 0x21 && c <= 0x7e); // Whitespace + VCHAR
+}
+
+// Only checks for general Base64(url) encoded chars, not '=' chars since '=' is
+// positional and may only appear at the end of a Base64 encoded string.
+bool IsBase64EncodedCharacter(UChar c) {
+ return IsASCIIAlphanumeric(c) || c == '+' || c == '/' || c == '-' || c == '_';
+}
+
+bool IsNonceCharacter(UChar c) {
+ return IsBase64EncodedCharacter(c) || c == '=';
+}
+
+bool IsSourceCharacter(UChar c) {
+ return !IsASCIISpace(c);
+}
+
+bool IsPathComponentCharacter(UChar c) {
+ return c != '?' && c != '#';
+}
+
+bool IsHostCharacter(UChar c) {
+ return IsASCIIAlphanumeric(c) || c == '-';
+}
+
+bool IsSchemeContinuationCharacter(UChar c) {
+ return IsASCIIAlphanumeric(c) || c == '+' || c == '-' || c == '.';
+}
+
+bool IsNotASCIISpace(UChar c) {
+ return !IsASCIISpace(c);
+}
+
+bool IsNotColonOrSlash(UChar c) {
+ return c != ':' && c != '/';
+}
+
+bool IsMediaTypeCharacter(UChar c) {
+ return !IsASCIISpace(c) && c != '/';
+}
+
+STATIC_ASSERT_ENUM(kWebContentSecurityPolicyTypeReport,
+ kContentSecurityPolicyHeaderTypeReport);
+STATIC_ASSERT_ENUM(kWebContentSecurityPolicyTypeEnforce,
+ kContentSecurityPolicyHeaderTypeEnforce);
+
+STATIC_ASSERT_ENUM(kWebContentSecurityPolicySourceHTTP,
+ kContentSecurityPolicyHeaderSourceHTTP);
+STATIC_ASSERT_ENUM(kWebContentSecurityPolicySourceMeta,
+ kContentSecurityPolicyHeaderSourceMeta);
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/network/content_security_policy_parsers.h b/chromium/third_party/blink/renderer/platform/network/content_security_policy_parsers.h
new file mode 100644
index 00000000000..0fb0dc51a1e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/content_security_policy_parsers.h
@@ -0,0 +1,51 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_CONTENT_SECURITY_POLICY_PARSERS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_CONTENT_SECURITY_POLICY_PARSERS_H_
+
+#include "third_party/blink/renderer/platform/crypto.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
+
+namespace blink {
+
+typedef std::pair<unsigned, DigestValue> CSPHashValue;
+
+enum ContentSecurityPolicyHeaderType {
+ kContentSecurityPolicyHeaderTypeReport,
+ kContentSecurityPolicyHeaderTypeEnforce
+};
+
+enum ContentSecurityPolicyHeaderSource {
+ kContentSecurityPolicyHeaderSourceHTTP,
+ kContentSecurityPolicyHeaderSourceMeta
+};
+
+enum ContentSecurityPolicyHashAlgorithm {
+ kContentSecurityPolicyHashAlgorithmNone = 0,
+ kContentSecurityPolicyHashAlgorithmSha256 = 1 << 2,
+ kContentSecurityPolicyHashAlgorithmSha384 = 1 << 3,
+ kContentSecurityPolicyHashAlgorithmSha512 = 1 << 4,
+ kContentSecurityPolicyHashAlgorithmEd25519 = 1 << 5,
+};
+
+PLATFORM_EXPORT bool IsCSPDirectiveNameCharacter(UChar);
+PLATFORM_EXPORT bool IsCSPDirectiveValueCharacter(UChar);
+PLATFORM_EXPORT bool IsNonceCharacter(UChar);
+PLATFORM_EXPORT bool IsSourceCharacter(UChar);
+PLATFORM_EXPORT bool IsPathComponentCharacter(UChar);
+PLATFORM_EXPORT bool IsHostCharacter(UChar);
+PLATFORM_EXPORT bool IsSchemeContinuationCharacter(UChar);
+PLATFORM_EXPORT bool IsNotASCIISpace(UChar);
+PLATFORM_EXPORT bool IsNotColonOrSlash(UChar);
+PLATFORM_EXPORT bool IsMediaTypeCharacter(UChar);
+
+// Only checks for general Base64 encoded chars, not '=' chars since '=' is
+// positional and may only appear at the end of a Base64 encoded string.
+PLATFORM_EXPORT bool IsBase64EncodedCharacter(UChar);
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/network/content_security_policy_response_headers.cc b/chromium/third_party/blink/renderer/platform/network/content_security_policy_response_headers.cc
new file mode 100644
index 00000000000..f19a8487e86
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/content_security_policy_response_headers.cc
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2013 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/network/content_security_policy_response_headers.h"
+
+#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
+#include "third_party/blink/renderer/platform/network/http_header_map.h"
+#include "third_party/blink/renderer/platform/network/http_names.h"
+#include "third_party/blink/renderer/platform/weborigin/scheme_registry.h"
+
+namespace blink {
+
+ContentSecurityPolicyResponseHeaders::ContentSecurityPolicyResponseHeaders(
+ const ResourceResponse& response)
+ : ContentSecurityPolicyResponseHeaders(
+ response.HttpHeaderFields(),
+ SchemeRegistry::SchemeSupportsWasmEvalCSP(
+ response.Url().Protocol())) {}
+
+ContentSecurityPolicyResponseHeaders::ContentSecurityPolicyResponseHeaders(
+ const HTTPHeaderMap& headers,
+ bool should_parse_wasm_eval)
+ : content_security_policy_(headers.Get(HTTPNames::Content_Security_Policy)),
+ content_security_policy_report_only_(
+ headers.Get(HTTPNames::Content_Security_Policy_Report_Only)),
+ should_parse_wasm_eval_(should_parse_wasm_eval) {}
+
+ContentSecurityPolicyResponseHeaders
+ContentSecurityPolicyResponseHeaders::IsolatedCopy() const {
+ ContentSecurityPolicyResponseHeaders headers;
+ headers.content_security_policy_ = content_security_policy_.IsolatedCopy();
+ headers.content_security_policy_report_only_ =
+ content_security_policy_report_only_.IsolatedCopy();
+ return headers;
+}
+}
diff --git a/chromium/third_party/blink/renderer/platform/network/content_security_policy_response_headers.h b/chromium/third_party/blink/renderer/platform/network/content_security_policy_response_headers.h
new file mode 100644
index 00000000000..7bab26f49fb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/content_security_policy_response_headers.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2013 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_CONTENT_SECURITY_POLICY_RESPONSE_HEADERS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_CONTENT_SECURITY_POLICY_RESPONSE_HEADERS_H_
+
+#include "third_party/blink/renderer/platform/cross_thread_copier.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class ResourceResponse;
+class HTTPHeaderMap;
+
+class PLATFORM_EXPORT ContentSecurityPolicyResponseHeaders final {
+ public:
+ ContentSecurityPolicyResponseHeaders() = default;
+ explicit ContentSecurityPolicyResponseHeaders(const ResourceResponse&);
+ explicit ContentSecurityPolicyResponseHeaders(
+ const HTTPHeaderMap&,
+ bool should_parse_wasm_eval = false);
+
+ const String& ContentSecurityPolicy() const {
+ return content_security_policy_;
+ }
+ const String& ContentSecurityPolicyReportOnly() const {
+ return content_security_policy_report_only_;
+ }
+
+ ContentSecurityPolicyResponseHeaders IsolatedCopy() const;
+
+ bool ShouldParseWasmEval() const { return should_parse_wasm_eval_; }
+
+ private:
+ String content_security_policy_;
+ String content_security_policy_report_only_;
+
+ const bool should_parse_wasm_eval_ = false;
+};
+
+template <>
+struct CrossThreadCopier<ContentSecurityPolicyResponseHeaders> {
+ STATIC_ONLY(CrossThreadCopier);
+ using Type = ContentSecurityPolicyResponseHeaders;
+ PLATFORM_EXPORT static Type Copy(
+ const ContentSecurityPolicyResponseHeaders& headers) {
+ return headers.IsolatedCopy();
+ }
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/network/encoded_form_data.cc b/chromium/third_party/blink/renderer/platform/network/encoded_form_data.cc
new file mode 100644
index 00000000000..5ae025f2d62
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/encoded_form_data.cc
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2004, 2006, 2008, 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ * Copyright (C) 2012 Digia Plc. and/or its subsidiary(-ies)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "third_party/blink/renderer/platform/network/encoded_form_data.h"
+
+#include "third_party/blink/renderer/platform/file_metadata.h"
+#include "third_party/blink/renderer/platform/network/form_data_encoder.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
+
+namespace blink {
+
+bool FormDataElement::IsSafeToSendToAnotherThread() const {
+ return filename_.IsSafeToSendToAnotherThread() &&
+ blob_uuid_.IsSafeToSendToAnotherThread();
+}
+
+inline EncodedFormData::EncodedFormData()
+ : identifier_(0), contains_password_data_(false) {}
+
+inline EncodedFormData::EncodedFormData(const EncodedFormData& data)
+ : RefCounted<EncodedFormData>(),
+ elements_(data.elements_),
+ identifier_(data.identifier_),
+ contains_password_data_(data.contains_password_data_) {}
+
+EncodedFormData::~EncodedFormData() = default;
+
+scoped_refptr<EncodedFormData> EncodedFormData::Create() {
+ return base::AdoptRef(new EncodedFormData);
+}
+
+scoped_refptr<EncodedFormData> EncodedFormData::Create(const void* data,
+ size_t size) {
+ scoped_refptr<EncodedFormData> result = Create();
+ result->AppendData(data, size);
+ return result;
+}
+
+scoped_refptr<EncodedFormData> EncodedFormData::Create(const CString& string) {
+ scoped_refptr<EncodedFormData> result = Create();
+ result->AppendData(string.data(), string.length());
+ return result;
+}
+
+scoped_refptr<EncodedFormData> EncodedFormData::Create(
+ const Vector<char>& vector) {
+ scoped_refptr<EncodedFormData> result = Create();
+ result->AppendData(vector.data(), vector.size());
+ return result;
+}
+
+scoped_refptr<EncodedFormData> EncodedFormData::Copy() const {
+ return base::AdoptRef(new EncodedFormData(*this));
+}
+
+scoped_refptr<EncodedFormData> EncodedFormData::DeepCopy() const {
+ scoped_refptr<EncodedFormData> form_data(Create());
+
+ form_data->identifier_ = identifier_;
+ form_data->boundary_ = boundary_;
+ form_data->contains_password_data_ = contains_password_data_;
+
+ size_t n = elements_.size();
+ form_data->elements_.ReserveInitialCapacity(n);
+ for (size_t i = 0; i < n; ++i) {
+ const FormDataElement& e = elements_[i];
+ switch (e.type_) {
+ case FormDataElement::kData:
+ form_data->elements_.UncheckedAppend(FormDataElement(e.data_));
+ break;
+ case FormDataElement::kEncodedFile:
+ form_data->elements_.UncheckedAppend(FormDataElement(
+ e.filename_.IsolatedCopy(), e.file_start_, e.file_length_,
+ e.expected_file_modification_time_));
+ break;
+ case FormDataElement::kEncodedBlob:
+ form_data->elements_.UncheckedAppend(FormDataElement(
+ e.blob_uuid_.IsolatedCopy(), e.optional_blob_data_handle_));
+ break;
+ case FormDataElement::kDataPipe:
+ network::mojom::blink::DataPipeGetterPtr data_pipe_getter;
+ (*e.data_pipe_getter_->GetPtr())
+ ->Clone(mojo::MakeRequest(&data_pipe_getter));
+ auto wrapped = base::MakeRefCounted<WrappedDataPipeGetter>(
+ std::move(data_pipe_getter));
+ form_data->elements_.UncheckedAppend(
+ FormDataElement(std::move(wrapped)));
+ break;
+ }
+ }
+ return form_data;
+}
+
+void EncodedFormData::AppendData(const void* data, size_t size) {
+ if (elements_.IsEmpty() || elements_.back().type_ != FormDataElement::kData)
+ elements_.push_back(FormDataElement());
+ FormDataElement& e = elements_.back();
+ size_t old_size = e.data_.size();
+ e.data_.Grow(old_size + size);
+ memcpy(e.data_.data() + old_size, data, size);
+}
+
+void EncodedFormData::AppendFile(const String& filename) {
+ elements_.push_back(
+ FormDataElement(filename, 0, BlobData::kToEndOfFile, InvalidFileTime()));
+}
+
+void EncodedFormData::AppendFileRange(const String& filename,
+ long long start,
+ long long length,
+ double expected_modification_time) {
+ elements_.push_back(
+ FormDataElement(filename, start, length, expected_modification_time));
+}
+
+void EncodedFormData::AppendBlob(
+ const String& uuid,
+ scoped_refptr<BlobDataHandle> optional_handle) {
+ elements_.push_back(FormDataElement(uuid, std::move(optional_handle)));
+}
+
+void EncodedFormData::AppendDataPipe(
+ scoped_refptr<WrappedDataPipeGetter> handle) {
+ elements_.emplace_back(std::move(handle));
+}
+
+void EncodedFormData::Flatten(Vector<char>& data) const {
+ // Concatenate all the byte arrays, but omit everything else.
+ data.clear();
+ size_t n = elements_.size();
+ for (size_t i = 0; i < n; ++i) {
+ const FormDataElement& e = elements_[i];
+ if (e.type_ == FormDataElement::kData)
+ data.Append(e.data_.data(), static_cast<size_t>(e.data_.size()));
+ }
+}
+
+String EncodedFormData::FlattenToString() const {
+ Vector<char> bytes;
+ Flatten(bytes);
+ return Latin1Encoding().Decode(reinterpret_cast<const char*>(bytes.data()),
+ bytes.size());
+}
+
+unsigned long long EncodedFormData::SizeInBytes() const {
+ unsigned size = 0;
+ size_t n = elements_.size();
+ for (size_t i = 0; i < n; ++i) {
+ const FormDataElement& e = elements_[i];
+ switch (e.type_) {
+ case FormDataElement::kData:
+ size += e.data_.size();
+ break;
+ case FormDataElement::kEncodedFile:
+ size += e.file_length_ - e.file_start_;
+ break;
+ case FormDataElement::kEncodedBlob:
+ if (e.optional_blob_data_handle_)
+ size += e.optional_blob_data_handle_->size();
+ break;
+ case FormDataElement::kDataPipe:
+ // We can get the size but it'd be async. Data pipe elements exist only
+ // in EncodedFormData instances that were filled from the content side
+ // using the WebHTTPBody interface, and generally represent blobs.
+ // Since for actual kEncodedBlob elements we ignore their size as well
+ // if the element was created through WebHTTPBody (which never sets
+ // optional_blob_data_handle), we'll ignore the size of these elements
+ // as well.
+ break;
+ }
+ }
+ return size;
+}
+
+bool EncodedFormData::IsSafeToSendToAnotherThread() const {
+ if (!HasOneRef())
+ return false;
+ for (auto& element : elements_) {
+ if (!element.IsSafeToSendToAnotherThread())
+ return false;
+ }
+ return true;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/network/encoded_form_data.h b/chromium/third_party/blink/renderer/platform/network/encoded_form_data.h
new file mode 100644
index 00000000000..83eb456a0d2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/encoded_form_data.h
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2004, 2006, 2008, 2011 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_ENCODED_FORM_DATA_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_ENCODED_FORM_DATA_H_
+
+#include "third_party/blink/renderer/platform/blob/blob_data.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+#include "services/network/public/mojom/data_pipe_getter.mojom-blink.h"
+
+namespace blink {
+
+class BlobDataHandle;
+
+// Refcounted wrapper around a DataPipeGetter to allow sharing the move-only
+// type. This is only needed so EncodedFormData/FormDataElement have a copy
+// constructor.
+class PLATFORM_EXPORT WrappedDataPipeGetter final
+ : public RefCounted<WrappedDataPipeGetter> {
+ public:
+ explicit WrappedDataPipeGetter(
+ network::mojom::blink::DataPipeGetterPtr data_pipe_getter)
+ : data_pipe_getter_(std::move(data_pipe_getter)) {}
+ ~WrappedDataPipeGetter() = default;
+
+ network::mojom::blink::DataPipeGetterPtr* GetPtr() {
+ return &data_pipe_getter_;
+ }
+
+ private:
+ network::mojom::blink::DataPipeGetterPtr data_pipe_getter_;
+};
+
+class PLATFORM_EXPORT FormDataElement final {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ FormDataElement() : type_(kData) {}
+ explicit FormDataElement(const Vector<char>& array)
+ : type_(kData), data_(array) {}
+ FormDataElement(const String& filename,
+ long long file_start,
+ long long file_length,
+ double expected_file_modification_time)
+ : type_(kEncodedFile),
+ filename_(filename),
+ file_start_(file_start),
+ file_length_(file_length),
+ expected_file_modification_time_(expected_file_modification_time) {}
+ FormDataElement(const String& blob_uuid,
+ scoped_refptr<BlobDataHandle> optional_handle)
+ : type_(kEncodedBlob),
+ blob_uuid_(blob_uuid),
+ optional_blob_data_handle_(std::move(optional_handle)) {}
+ explicit FormDataElement(
+ scoped_refptr<WrappedDataPipeGetter> data_pipe_getter)
+ : type_(kDataPipe), data_pipe_getter_(std::move(data_pipe_getter)) {}
+
+ bool IsSafeToSendToAnotherThread() const;
+
+ enum Type { kData, kEncodedFile, kEncodedBlob, kDataPipe } type_;
+ Vector<char> data_;
+ String filename_;
+ String blob_uuid_;
+ scoped_refptr<BlobDataHandle> optional_blob_data_handle_;
+ long long file_start_;
+ long long file_length_;
+ double expected_file_modification_time_;
+ scoped_refptr<WrappedDataPipeGetter> data_pipe_getter_;
+};
+
+inline bool operator==(const FormDataElement& a, const FormDataElement& b) {
+ if (&a == &b)
+ return true;
+
+ if (a.type_ != b.type_)
+ return false;
+ if (a.type_ == FormDataElement::kData)
+ return a.data_ == b.data_;
+ if (a.type_ == FormDataElement::kEncodedFile)
+ return a.filename_ == b.filename_ && a.file_start_ == b.file_start_ &&
+ a.file_length_ == b.file_length_ &&
+ a.expected_file_modification_time_ ==
+ b.expected_file_modification_time_;
+ if (a.type_ == FormDataElement::kEncodedBlob)
+ return a.blob_uuid_ == b.blob_uuid_;
+ if (a.type_ == FormDataElement::kDataPipe)
+ return a.data_pipe_getter_ == b.data_pipe_getter_;
+
+ return true;
+}
+
+inline bool operator!=(const FormDataElement& a, const FormDataElement& b) {
+ return !(a == b);
+}
+
+class PLATFORM_EXPORT EncodedFormData : public RefCounted<EncodedFormData> {
+ public:
+ enum EncodingType {
+ kFormURLEncoded, // for application/x-www-form-urlencoded
+ kTextPlain, // for text/plain
+ kMultipartFormData // for multipart/form-data
+ };
+
+ static scoped_refptr<EncodedFormData> Create();
+ static scoped_refptr<EncodedFormData> Create(const void*, size_t);
+ static scoped_refptr<EncodedFormData> Create(const CString&);
+ static scoped_refptr<EncodedFormData> Create(const Vector<char>&);
+ scoped_refptr<EncodedFormData> Copy() const;
+ scoped_refptr<EncodedFormData> DeepCopy() const;
+ ~EncodedFormData();
+
+ void AppendData(const void* data, size_t);
+ void AppendFile(const String& file_path);
+ void AppendFileRange(const String& filename,
+ long long start,
+ long long length,
+ double expected_modification_time);
+ void AppendBlob(const String& blob_uuid,
+ scoped_refptr<BlobDataHandle> optional_handle);
+ void AppendDataPipe(scoped_refptr<WrappedDataPipeGetter> handle);
+
+ void Flatten(Vector<char>&) const; // omits files
+ String FlattenToString() const; // omits files
+
+ bool IsEmpty() const { return elements_.IsEmpty(); }
+ const Vector<FormDataElement>& Elements() const { return elements_; }
+ Vector<FormDataElement>& MutableElements() { return elements_; }
+
+ const Vector<char>& Boundary() const { return boundary_; }
+ void SetBoundary(Vector<char> boundary) { boundary_ = boundary; }
+
+ // Identifies a particular form submission instance. A value of 0 is used
+ // to indicate an unspecified identifier.
+ void SetIdentifier(int64_t identifier) { identifier_ = identifier; }
+ int64_t Identifier() const { return identifier_; }
+
+ bool ContainsPasswordData() const { return contains_password_data_; }
+ void SetContainsPasswordData(bool contains_password_data) {
+ contains_password_data_ = contains_password_data;
+ }
+
+ static EncodingType ParseEncodingType(const String& type) {
+ if (DeprecatedEqualIgnoringCase(type, "text/plain"))
+ return kTextPlain;
+ if (DeprecatedEqualIgnoringCase(type, "multipart/form-data"))
+ return kMultipartFormData;
+ return kFormURLEncoded;
+ }
+
+ // Size of the elements making up the EncodedFormData.
+ unsigned long long SizeInBytes() const;
+
+ bool IsSafeToSendToAnotherThread() const;
+
+ private:
+ EncodedFormData();
+ EncodedFormData(const EncodedFormData&);
+
+ Vector<FormDataElement> elements_;
+
+ int64_t identifier_;
+ Vector<char> boundary_;
+ bool contains_password_data_;
+};
+
+inline bool operator==(const EncodedFormData& a, const EncodedFormData& b) {
+ return a.Elements() == b.Elements();
+}
+
+inline bool operator!=(const EncodedFormData& a, const EncodedFormData& b) {
+ return !(a == b);
+}
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/network/encoded_form_data_test.cc b/chromium/third_party/blink/renderer/platform/network/encoded_form_data_test.cc
new file mode 100644
index 00000000000..7d8ac9748f8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/encoded_form_data_test.cc
@@ -0,0 +1,99 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/network/encoded_form_data.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+namespace {
+
+class EncodedFormDataTest : public testing::Test {
+ public:
+ void CheckDeepCopied(const String& a, const String& b) {
+ EXPECT_EQ(a, b);
+ if (b.Impl())
+ EXPECT_NE(a.Impl(), b.Impl());
+ }
+
+ void CheckDeepCopied(const KURL& a, const KURL& b) {
+ EXPECT_EQ(a, b);
+ CheckDeepCopied(a.GetString(), b.GetString());
+ if (a.InnerURL() && b.InnerURL())
+ CheckDeepCopied(*a.InnerURL(), *b.InnerURL());
+ }
+
+ void CheckDeepCopied(const FormDataElement& a, const FormDataElement& b) {
+ EXPECT_EQ(a, b);
+ CheckDeepCopied(a.filename_, b.filename_);
+ CheckDeepCopied(a.blob_uuid_, b.blob_uuid_);
+ }
+};
+
+TEST_F(EncodedFormDataTest, DeepCopy) {
+ scoped_refptr<EncodedFormData> original(EncodedFormData::Create());
+ original->AppendData("Foo", 3);
+ original->AppendFileRange("example.txt", 12345, 56789, 9999.0);
+ original->AppendBlob("originalUUID", nullptr);
+
+ Vector<char> boundary_vector;
+ boundary_vector.Append("----boundaryForTest", 19);
+ original->SetIdentifier(45678);
+ original->SetBoundary(boundary_vector);
+ original->SetContainsPasswordData(true);
+
+ scoped_refptr<EncodedFormData> copy = original->DeepCopy();
+
+ // Check that contents are copied (compare the copy with expected values).
+ const Vector<FormDataElement>& original_elements = original->Elements();
+ const Vector<FormDataElement>& copy_elements = copy->Elements();
+ ASSERT_EQ(3ul, copy_elements.size());
+
+ Vector<char> foo_vector;
+ foo_vector.Append("Foo", 3);
+
+ EXPECT_EQ(FormDataElement::kData, copy_elements[0].type_);
+ EXPECT_EQ(foo_vector, copy_elements[0].data_);
+
+ EXPECT_EQ(FormDataElement::kEncodedFile, copy_elements[1].type_);
+ EXPECT_EQ(String("example.txt"), copy_elements[1].filename_);
+ EXPECT_EQ(12345ll, copy_elements[1].file_start_);
+ EXPECT_EQ(56789ll, copy_elements[1].file_length_);
+ EXPECT_EQ(9999.0, copy_elements[1].expected_file_modification_time_);
+
+ EXPECT_EQ(FormDataElement::kEncodedBlob, copy_elements[2].type_);
+ EXPECT_EQ(String("originalUUID"), copy_elements[2].blob_uuid_);
+
+ EXPECT_EQ(45678, copy->Identifier());
+ EXPECT_EQ(boundary_vector, copy->Boundary());
+ EXPECT_EQ(true, copy->ContainsPasswordData());
+
+ // Check that contents are copied (compare the copy with the original).
+ EXPECT_EQ(*original, *copy);
+
+ // Check pointers are different, i.e. deep-copied.
+ ASSERT_NE(original.get(), copy.get());
+
+ for (size_t i = 0; i < 3; ++i) {
+ if (copy_elements[i].filename_.Impl()) {
+ EXPECT_NE(original_elements[i].filename_.Impl(),
+ copy_elements[i].filename_.Impl());
+ EXPECT_TRUE(copy_elements[i].filename_.Impl()->HasOneRef());
+ }
+
+ if (copy_elements[i].blob_uuid_.Impl()) {
+ EXPECT_NE(original_elements[i].blob_uuid_.Impl(),
+ copy_elements[i].blob_uuid_.Impl());
+ EXPECT_TRUE(copy_elements[i].blob_uuid_.Impl()->HasOneRef());
+ }
+
+ // m_optionalBlobDataHandle is not checked, because BlobDataHandle is
+ // ThreadSafeRefCounted.
+ }
+}
+
+} // namespace
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/network/form_data_encoder.cc b/chromium/third_party/blink/renderer/platform/network/form_data_encoder.cc
new file mode 100644
index 00000000000..401ea3591c1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/form_data_encoder.cc
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * (C) 2001 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * (C) 2006 Alexey Proskuryakov (ap@nypop.com)
+ * Copyright (C) 2008 Torch Mobile Inc. All rights reserved.
+ * (http://www.torchmobile.com/)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "third_party/blink/renderer/platform/network/form_data_encoder.h"
+
+#include <limits>
+#include "third_party/blink/renderer/platform/wtf/cryptographically_random_number.h"
+#include "third_party/blink/renderer/platform/wtf/hex_number.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
+
+namespace blink {
+
+// Helper functions
+static inline void Append(Vector<char>& buffer, char string) {
+ buffer.push_back(string);
+}
+
+static inline void Append(Vector<char>& buffer, const char* string) {
+ buffer.Append(string, strlen(string));
+}
+
+static inline void Append(Vector<char>& buffer, const CString& string) {
+ buffer.Append(string.data(), string.length());
+}
+
+static inline void AppendPercentEncoded(Vector<char>& buffer, unsigned char c) {
+ Append(buffer, '%');
+ HexNumber::AppendByteAsHex(c, buffer);
+}
+
+static void AppendQuotedString(Vector<char>& buffer, const CString& string) {
+ // Append a string as a quoted value, escaping quotes and line breaks.
+ // FIXME: Is it correct to use percent escaping here? Other browsers do not
+ // encode these characters yet, so we should test popular servers to find out
+ // if there is an encoding form they can handle.
+ size_t length = string.length();
+ for (size_t i = 0; i < length; ++i) {
+ char c = string.data()[i];
+
+ switch (c) {
+ case 0x0a:
+ Append(buffer, "%0A");
+ break;
+ case 0x0d:
+ Append(buffer, "%0D");
+ break;
+ case '"':
+ Append(buffer, "%22");
+ break;
+ default:
+ Append(buffer, c);
+ }
+ }
+}
+
+WTF::TextEncoding FormDataEncoder::EncodingFromAcceptCharset(
+ const String& accept_charset,
+ const WTF::TextEncoding& fallback_encoding) {
+ DCHECK(fallback_encoding.IsValid());
+
+ String normalized_accept_charset = accept_charset;
+ normalized_accept_charset.Replace(',', ' ');
+
+ Vector<String> charsets;
+ normalized_accept_charset.Split(' ', charsets);
+
+ for (const String& name : charsets) {
+ WTF::TextEncoding encoding(name);
+ if (encoding.IsValid())
+ return encoding;
+ }
+
+ return fallback_encoding;
+}
+
+Vector<char> FormDataEncoder::GenerateUniqueBoundaryString() {
+ Vector<char> boundary;
+
+ // TODO(rsleevi): crbug.com/575779: Follow the spec or fix the spec.
+ // The RFC 2046 spec says the alphanumeric characters plus the
+ // following characters are legal for boundaries: '()+_,-./:=?
+ // However the following characters, though legal, cause some sites
+ // to fail: (),./:=+
+ //
+ // Note that our algorithm makes it twice as much likely for 'A' or 'B'
+ // to appear in the boundary string, because 0x41 and 0x42 are present in
+ // the below array twice.
+ static const char kAlphaNumericEncodingMap[64] = {
+ 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B,
+ 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56,
+ 0x57, 0x58, 0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72,
+ 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x30, 0x31, 0x32,
+ 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42};
+
+ // Start with an informative prefix.
+ Append(boundary, "----WebKitFormBoundary");
+
+ // Append 16 random 7bit ascii AlphaNumeric characters.
+ Vector<char> random_bytes;
+
+ for (unsigned i = 0; i < 4; ++i) {
+ uint32_t randomness = CryptographicallyRandomNumber();
+ random_bytes.push_back(kAlphaNumericEncodingMap[(randomness >> 24) & 0x3F]);
+ random_bytes.push_back(kAlphaNumericEncodingMap[(randomness >> 16) & 0x3F]);
+ random_bytes.push_back(kAlphaNumericEncodingMap[(randomness >> 8) & 0x3F]);
+ random_bytes.push_back(kAlphaNumericEncodingMap[randomness & 0x3F]);
+ }
+
+ boundary.AppendVector(random_bytes);
+ boundary.push_back(
+ 0); // Add a 0 at the end so we can use this as a C-style string.
+ return boundary;
+}
+
+void FormDataEncoder::BeginMultiPartHeader(Vector<char>& buffer,
+ const CString& boundary,
+ const CString& name) {
+ AddBoundaryToMultiPartHeader(buffer, boundary);
+
+ // FIXME: This loses data irreversibly if the input name includes characters
+ // you can't encode in the website's character set.
+ Append(buffer, "Content-Disposition: form-data; name=\"");
+ AppendQuotedString(buffer, name);
+ Append(buffer, '"');
+}
+
+void FormDataEncoder::AddBoundaryToMultiPartHeader(Vector<char>& buffer,
+ const CString& boundary,
+ bool is_last_boundary) {
+ Append(buffer, "--");
+ Append(buffer, boundary);
+
+ if (is_last_boundary)
+ Append(buffer, "--");
+
+ Append(buffer, "\r\n");
+}
+
+void FormDataEncoder::AddFilenameToMultiPartHeader(
+ Vector<char>& buffer,
+ const WTF::TextEncoding& encoding,
+ const String& filename) {
+ // Characters that cannot be encoded using the form's encoding will
+ // be escaped using numeric character references, e.g. &#128514; for
+ // 😂.
+ //
+ // This behavior is intended to match existing Firefox and Edge
+ // behavior.
+ //
+ // This aspect of multipart file upload (how to replace filename
+ // characters not representable in the form charset) is not
+ // currently specified in HTML, though it may be a good candidate
+ // for future standardization. An HTML issue tracker entry has
+ // been added for this: https://github.com/whatwg/html/issues/3223
+ //
+ // This behavior also exactly matches the already-standardized
+ // replacement behavior from HTML for entity names and values in
+ // multipart form data. The HTML standard specifically overrides RFC
+ // 7578 in this case and leaves the actual substitution mechanism
+ // implementation-defined.
+ //
+ // See also:
+ //
+ // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#multipart-form-data
+ // https://www.chromestatus.com/features/5634575908732928
+ // https://crbug.com/661819
+ // https://encoding.spec.whatwg.org/#concept-encoding-process
+ // https://tools.ietf.org/html/rfc7578#section-4.2
+ // https://tools.ietf.org/html/rfc5987#section-3.2
+ Append(buffer, "; filename=\"");
+ AppendQuotedString(buffer,
+ encoding.Encode(filename, WTF::kEntitiesForUnencodables));
+ Append(buffer, '"');
+}
+
+void FormDataEncoder::AddContentTypeToMultiPartHeader(
+ Vector<char>& buffer,
+ const CString& mime_type) {
+ Append(buffer, "\r\nContent-Type: ");
+ Append(buffer, mime_type);
+}
+
+void FormDataEncoder::FinishMultiPartHeader(Vector<char>& buffer) {
+ Append(buffer, "\r\n\r\n");
+}
+
+void FormDataEncoder::AddKeyValuePairAsFormData(
+ Vector<char>& buffer,
+ const CString& key,
+ const CString& value,
+ EncodedFormData::EncodingType encoding_type,
+ Mode mode) {
+ if (encoding_type == EncodedFormData::kTextPlain) {
+ Append(buffer, key);
+ Append(buffer, '=');
+ Append(buffer, value);
+ Append(buffer, "\r\n");
+ } else {
+ if (!buffer.IsEmpty())
+ Append(buffer, '&');
+ EncodeStringAsFormData(buffer, key, mode);
+ Append(buffer, '=');
+ EncodeStringAsFormData(buffer, value, mode);
+ }
+}
+
+void FormDataEncoder::EncodeStringAsFormData(Vector<char>& buffer,
+ const CString& string,
+ Mode mode) {
+ // Same safe characters as Netscape for compatibility.
+ static const char kSafeCharacters[] = "-._*";
+
+ // http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1
+ unsigned length = string.length();
+ for (unsigned i = 0; i < length; ++i) {
+ unsigned char c = string.data()[i];
+
+ if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
+ (c >= '0' && c <= '9') || (c != '\0' && strchr(kSafeCharacters, c))) {
+ Append(buffer, c);
+ } else if (c == ' ') {
+ Append(buffer, '+');
+ } else {
+ if (mode == kNormalizeCRLF) {
+ if (c == '\n' ||
+ (c == '\r' && (i + 1 >= length || string.data()[i + 1] != '\n'))) {
+ Append(buffer, "%0D%0A");
+ } else if (c != '\r') {
+ AppendPercentEncoded(buffer, c);
+ }
+ } else {
+ AppendPercentEncoded(buffer, c);
+ }
+ }
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/network/form_data_encoder.h b/chromium/third_party/blink/renderer/platform/network/form_data_encoder.h
new file mode 100644
index 00000000000..c9345041f4d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/form_data_encoder.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2008 Torch Mobile Inc. All rights reserved.
+ * (http://www.torchmobile.com/)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_FORM_DATA_ENCODER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_FORM_DATA_ENCODER_H_
+
+#include "third_party/blink/renderer/platform/network/encoded_form_data.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace WTF {
+class TextEncoding;
+}
+
+namespace blink {
+
+class PLATFORM_EXPORT FormDataEncoder {
+ STATIC_ONLY(FormDataEncoder);
+
+ public:
+ // Specifies how to handle CRs and LFs. When NormalizeCRLF is passed, the
+ // method replaces the following characters with a CRLF pair:
+ // - a CR not followed by an LF
+ // - an LF not preceded by a CR
+ enum Mode { kNormalizeCRLF, kDoNotNormalizeCRLF };
+
+ static WTF::TextEncoding EncodingFromAcceptCharset(
+ const String& accept_charset,
+ const WTF::TextEncoding& fallback_encoding);
+
+ // Helper functions used by HTMLFormElement for multi-part form data
+ static Vector<char> GenerateUniqueBoundaryString();
+ static void BeginMultiPartHeader(Vector<char>&,
+ const CString& boundary,
+ const CString& name);
+ static void AddBoundaryToMultiPartHeader(Vector<char>&,
+ const CString& boundary,
+ bool is_last_boundary = false);
+ static void AddFilenameToMultiPartHeader(Vector<char>&,
+ const WTF::TextEncoding&,
+ const String& filename);
+ static void AddContentTypeToMultiPartHeader(Vector<char>&,
+ const CString& mime_type);
+ static void FinishMultiPartHeader(Vector<char>&);
+
+ // Helper functions used by HTMLFormElement for non multi-part form data. Mode
+ // argument is not used for TextPlain type.
+ static void AddKeyValuePairAsFormData(
+ Vector<char>&,
+ const CString& key,
+ const CString& value,
+ EncodedFormData::EncodingType = EncodedFormData::kFormURLEncoded,
+ Mode = kNormalizeCRLF);
+ static void EncodeStringAsFormData(Vector<char>&, const CString&, Mode);
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/network/header_field_tokenizer.cc b/chromium/third_party/blink/renderer/platform/network/header_field_tokenizer.cc
new file mode 100644
index 00000000000..7825946bf63
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/header_field_tokenizer.cc
@@ -0,0 +1,154 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/network/header_field_tokenizer.h"
+
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_view.h"
+
+namespace blink {
+
+namespace {
+
+using Mode = HeaderFieldTokenizer::Mode;
+
+bool IsTokenCharacter(Mode mode, UChar c) {
+ // TODO(cvazac) change this to use LChar
+ // TODO(cvazac) Check HTTPArchive for usage and possible deprecation.
+ // According to https://tools.ietf.org/html/rfc7230#appendix-B, the
+ // following characters (ASCII decimal) should not be included in a TOKEN:
+ // 123 ('{')
+ // 125 ('}')
+ // 127 (delete)
+
+ if (c >= 128)
+ return false;
+ if (c < 0x20)
+ return false;
+
+ switch (c) {
+ case ' ':
+ case ';':
+ case '"':
+ return false;
+ case '(':
+ case ')':
+ case '<':
+ case '>':
+ case '@':
+ case ',':
+ case ':':
+ case '\\':
+ case '/':
+ case '[':
+ case ']':
+ case '?':
+ case '=':
+ return mode == Mode::kRelaxed;
+ default:
+ return true;
+ }
+}
+
+} // namespace
+
+HeaderFieldTokenizer::HeaderFieldTokenizer(const String& header_field)
+ : index_(0u), input_(header_field) {
+ SkipSpaces();
+}
+
+HeaderFieldTokenizer::HeaderFieldTokenizer(HeaderFieldTokenizer&&) = default;
+
+bool HeaderFieldTokenizer::Consume(char c) {
+ // TODO(cvazac) change this to use LChar
+ DCHECK_NE(c, ' ');
+
+ if (IsConsumed() || input_[index_] != c)
+ return false;
+
+ ++index_;
+ SkipSpaces();
+ return true;
+}
+
+bool HeaderFieldTokenizer::ConsumeQuotedString(String& output) {
+ StringBuilder builder;
+
+ DCHECK_EQ('"', input_[index_]);
+ ++index_;
+
+ while (!IsConsumed()) {
+ if (input_[index_] == '"') {
+ output = builder.ToString();
+ ++index_;
+ SkipSpaces();
+ return true;
+ }
+ if (input_[index_] == '\\') {
+ ++index_;
+ if (IsConsumed())
+ return false;
+ }
+ builder.Append(input_[index_]);
+ ++index_;
+ }
+ return false;
+}
+
+bool HeaderFieldTokenizer::ConsumeToken(Mode mode, StringView& output) {
+ DCHECK(output.IsNull());
+
+ auto start = index_;
+ while (!IsConsumed() && IsTokenCharacter(mode, input_[index_]))
+ ++index_;
+
+ if (start == index_)
+ return false;
+
+ output = StringView(input_, start, index_ - start);
+ SkipSpaces();
+ return true;
+}
+
+bool HeaderFieldTokenizer::ConsumeTokenOrQuotedString(Mode mode,
+ String& output) {
+ if (IsConsumed())
+ return false;
+
+ if (input_[index_] == '"')
+ return ConsumeQuotedString(output);
+
+ StringView view;
+ if (!ConsumeToken(mode, view))
+ return false;
+ output = view.ToString();
+ return true;
+}
+
+void HeaderFieldTokenizer::SkipSpaces() {
+ // TODO(cvazac) skip tabs, per:
+ // https://tools.ietf.org/html/rfc7230#section-3.2.3
+ while (!IsConsumed() && input_[index_] == ' ')
+ ++index_;
+}
+
+void HeaderFieldTokenizer::ConsumeBeforeAnyCharMatch(Vector<LChar> chars) {
+ // TODO(cvazac) move this to HeaderFieldTokenizer c'tor
+ DCHECK(input_.Is8Bit());
+
+ DCHECK_GT(chars.size(), 0U);
+ DCHECK_LT(chars.size(), 3U);
+
+ while (!IsConsumed()) {
+ for (const auto& c : chars) {
+ if (c == input_[index_]) {
+ return;
+ }
+ }
+
+ ++index_;
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/network/header_field_tokenizer.h b/chromium/third_party/blink/renderer/platform/network/header_field_tokenizer.h
new file mode 100644
index 00000000000..71702553e29
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/header_field_tokenizer.h
@@ -0,0 +1,52 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_HEADER_FIELD_TOKENIZER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_HEADER_FIELD_TOKENIZER_H_
+
+#include "third_party/blink/renderer/platform/network/parsed_content_header_field_parameters.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+// Parses header fields into tokens, quoted strings and separators.
+// Commonly used by ParsedContent* classes.
+class PLATFORM_EXPORT HeaderFieldTokenizer final {
+ STACK_ALLOCATED();
+
+ public:
+ using Mode = ParsedContentHeaderFieldParameters::Mode;
+
+ explicit HeaderFieldTokenizer(const String& header_field);
+ HeaderFieldTokenizer(HeaderFieldTokenizer&&);
+
+ // Try to parse a separator character, a token or either a token or a quoted
+ // string from the |header_field| input. Return |true| on success. Return
+ // |false| if the separator character, the token or the quoted string is
+ // missing or invalid.
+ bool Consume(char);
+ bool ConsumeToken(Mode, StringView& output);
+ bool ConsumeTokenOrQuotedString(Mode, String& output);
+
+ // Consume all characters before (but excluding) any of the characters from
+ // the Vector parameter are found.
+ // Because we potentially have to iterate through the entire Vector for each
+ // character of the base string, the Vector should be small (< 3 members).
+ void ConsumeBeforeAnyCharMatch(Vector<LChar>);
+
+ unsigned Index() const { return index_; }
+ bool IsConsumed() const { return index_ >= input_.length(); }
+
+ private:
+ bool ConsumeQuotedString(String& output);
+ void SkipSpaces();
+
+ unsigned index_;
+ const String input_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/network/http_header_map.cc b/chromium/third_party/blink/renderer/platform/network/http_header_map.cc
new file mode 100644
index 00000000000..5d63fd0fdc0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/http_header_map.cc
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/renderer/platform/network/http_header_map.h"
+
+#include <memory>
+
+namespace blink {
+
+HTTPHeaderMap::HTTPHeaderMap() = default;
+
+HTTPHeaderMap::~HTTPHeaderMap() = default;
+
+std::unique_ptr<CrossThreadHTTPHeaderMapData> HTTPHeaderMap::CopyData() const {
+ std::unique_ptr<CrossThreadHTTPHeaderMapData> data =
+ std::make_unique<CrossThreadHTTPHeaderMapData>();
+ data->ReserveInitialCapacity(size());
+
+ HTTPHeaderMap::const_iterator end_it = end();
+ for (HTTPHeaderMap::const_iterator it = begin(); it != end_it; ++it)
+ data->UncheckedAppend(std::make_pair(it->key.GetString().IsolatedCopy(),
+ it->value.GetString().IsolatedCopy()));
+
+ return data;
+}
+
+void HTTPHeaderMap::Adopt(std::unique_ptr<CrossThreadHTTPHeaderMapData> data) {
+ Clear();
+ size_t data_size = data->size();
+ for (size_t index = 0; index < data_size; ++index) {
+ std::pair<String, String>& header = (*data)[index];
+ Set(AtomicString(header.first), AtomicString(header.second));
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/network/http_header_map.h b/chromium/third_party/blink/renderer/platform/network/http_header_map.h
new file mode 100644
index 00000000000..9c4825e0eaf
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/http_header_map.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_HTTP_HEADER_MAP_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_HTTP_HEADER_MAP_H_
+
+#include <memory>
+#include <utility>
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+typedef Vector<std::pair<String, String>> CrossThreadHTTPHeaderMapData;
+
+// FIXME: Not every header fits into a map. Notably, multiple Set-Cookie header
+// fields are needed to set multiple cookies.
+class PLATFORM_EXPORT HTTPHeaderMap final {
+ DISALLOW_NEW();
+
+ public:
+ HTTPHeaderMap();
+ ~HTTPHeaderMap();
+
+ // Gets a copy of the data suitable for passing to another thread.
+ std::unique_ptr<CrossThreadHTTPHeaderMapData> CopyData() const;
+
+ void Adopt(std::unique_ptr<CrossThreadHTTPHeaderMapData>);
+
+ typedef HashMap<AtomicString, AtomicString, CaseFoldingHash> MapType;
+ typedef MapType::AddResult AddResult;
+ typedef MapType::const_iterator const_iterator;
+
+ size_t size() const { return headers_.size(); }
+ const_iterator begin() const { return headers_.begin(); }
+ const_iterator end() const { return headers_.end(); }
+ const_iterator Find(const AtomicString& k) const { return headers_.find(k); }
+ void Clear() { headers_.clear(); }
+ bool Contains(const AtomicString& k) const { return headers_.Contains(k); }
+ const AtomicString& Get(const AtomicString& k) const {
+ return headers_.at(k);
+ }
+ AddResult Set(const AtomicString& k, const AtomicString& v) {
+ return headers_.Set(k, v);
+ }
+ AddResult Add(const AtomicString& k, const AtomicString& v) {
+ return headers_.insert(k, v);
+ }
+ void Remove(const AtomicString& k) { headers_.erase(k); }
+ bool operator!=(const HTTPHeaderMap& rhs) const {
+ return headers_ != rhs.headers_;
+ }
+ bool operator==(const HTTPHeaderMap& rhs) const {
+ return headers_ == rhs.headers_;
+ }
+
+ private:
+ HashMap<AtomicString, AtomicString, CaseFoldingHash> headers_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_HTTP_HEADER_MAP_H_
diff --git a/chromium/third_party/blink/renderer/platform/network/http_names.json5 b/chromium/third_party/blink/renderer/platform/network/http_names.json5
new file mode 100644
index 00000000000..2ef7f762a8d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/http_names.json5
@@ -0,0 +1,73 @@
+{
+ metadata: {
+ namespace: "HTTP",
+ export: "PLATFORM_EXPORT",
+ },
+
+ data: [
+ "GET",
+ "HEAD",
+ "POST",
+ "OPTIONS",
+ "PUT",
+ "Accept",
+ "Accept-CH",
+ "Accept-CH-Lifetime",
+ "Access-Control-Allow-External",
+ "Access-Control-Allow-Headers",
+ "Access-Control-Allow-Methods",
+ "Access-Control-Allow-Origin",
+ "Access-Control-Allow-Credentials",
+ "Access-Control-Expose-Headers",
+ "Access-Control-Max-Age",
+ "Access-Control-Request-External",
+ "Access-Control-Request-Headers",
+ "Access-Control-Request-Method",
+ "Allow-CSP-From",
+ "Cache-Control",
+ "Content-DPR",
+ "Content-Disposition",
+ "Content-Language",
+ "Content-Security-Policy",
+ "Content-Security-Policy-Report-Only",
+ "Content-Type",
+ "ETag",
+ "Expires",
+ "Date",
+ "Feature-Policy",
+ "If-Match",
+ "If-Modified-Since",
+ "If-None-Match",
+ "If-Range",
+ "If-Unmodified-Since",
+ "Last-Event-ID",
+ "Last-Modified",
+ "Link",
+ "Location",
+ "Origin",
+ "Origin-Trial",
+ "Ping-From",
+ "Ping-To",
+ "Pragma",
+ "Purpose",
+ "Range",
+ "Referer",
+ "Referrer-Policy",
+ "Refresh",
+ "Resource-Freshness",
+ "Save-Data",
+ "Sec-Required-CSP",
+ "Server-Timing",
+ "SourceMap",
+ "Timing-Allow-Origin",
+ "Upgrade-Insecure-Requests",
+ "User-Agent",
+ "Vary",
+ "X-Content-Type-Options",
+ "X-DNS-Prefetch-Control",
+ "X-DevTools-Emulate-Network-Conditions-Client-Id",
+ "X-Frame-Options",
+ "X-SourceMap",
+ "X-XSS-Protection",
+ ],
+}
diff --git a/chromium/third_party/blink/renderer/platform/network/http_parsers.cc b/chromium/third_party/blink/renderer/platform/network/http_parsers.cc
new file mode 100644
index 00000000000..fafbf62abd2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/http_parsers.cc
@@ -0,0 +1,699 @@
+/*
+ * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org)
+ * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ * Copyright (C) 2011 Apple 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/network/http_parsers.h"
+
+#include <memory>
+#include "net/http/http_content_disposition.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_util.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
+#include "third_party/blink/renderer/platform/network/header_field_tokenizer.h"
+#include "third_party/blink/renderer/platform/network/http_names.h"
+#include "third_party/blink/renderer/platform/wtf/date_math.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/text/parsing_utilities.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+namespace {
+
+const Vector<AtomicString>& ReplaceHeaders() {
+ // The list of response headers that we do not copy from the original
+ // response when generating a ResourceResponse for a MIME payload.
+ // Note: this is called only on the main thread.
+ DEFINE_STATIC_LOCAL(Vector<AtomicString>, headers,
+ ({"content-type", "content-length", "content-disposition",
+ "content-range", "range", "set-cookie"}));
+ return headers;
+}
+
+bool IsWhitespace(UChar chr) {
+ return (chr == ' ') || (chr == '\t');
+}
+
+// true if there is more to parse, after incrementing pos past whitespace.
+// Note: Might return pos == str.length()
+// if |matcher| is nullptr, isWhitespace() is used.
+inline bool SkipWhiteSpace(const String& str,
+ unsigned& pos,
+ WTF::CharacterMatchFunctionPtr matcher = nullptr) {
+ unsigned len = str.length();
+
+ if (matcher) {
+ while (pos < len && matcher(str[pos]))
+ ++pos;
+ } else {
+ while (pos < len && IsWhitespace(str[pos]))
+ ++pos;
+ }
+
+ return pos < len;
+}
+
+template <typename CharType>
+inline bool IsASCIILowerAlphaOrDigit(CharType c) {
+ return IsASCIILower(c) || IsASCIIDigit(c);
+}
+
+template <typename CharType>
+inline bool IsASCIILowerAlphaOrDigitOrHyphen(CharType c) {
+ return IsASCIILowerAlphaOrDigit(c) || c == '-';
+}
+
+// Parse a number with ignoring trailing [0-9.].
+// Returns NaN if the source contains invalid characters.
+double ParseRefreshTime(const String& source) {
+ int full_stop_count = 0;
+ unsigned number_end = source.length();
+ for (unsigned i = 0; i < source.length(); ++i) {
+ UChar ch = source[i];
+ if (ch == kFullstopCharacter) {
+ // TODO(tkent): According to the HTML specification, we should support
+ // only integers. However we support fractional numbers.
+ if (++full_stop_count == 2)
+ number_end = i;
+ } else if (!IsASCIIDigit(ch)) {
+ return std::numeric_limits<double>::quiet_NaN();
+ }
+ }
+ bool ok;
+ double time = source.Left(number_end).ToDouble(&ok);
+ return ok ? time : std::numeric_limits<double>::quiet_NaN();
+}
+
+} // namespace
+
+bool IsValidHTTPHeaderValue(const String& name) {
+ // FIXME: This should really match name against
+ // field-value in section 4.2 of RFC 2616.
+
+ return name.ContainsOnlyLatin1() && !name.Contains('\r') &&
+ !name.Contains('\n') && !name.Contains('\0');
+}
+
+// See RFC 7230, Section 3.2.6.
+bool IsValidHTTPToken(const String& characters) {
+ if (characters.IsEmpty())
+ return false;
+ for (unsigned i = 0; i < characters.length(); ++i) {
+ UChar c = characters[i];
+ if (c > 0x7F || !net::HttpUtil::IsTokenChar(c))
+ return false;
+ }
+ return true;
+}
+
+bool IsContentDispositionAttachment(const String& content_disposition) {
+ CString cstring(content_disposition.Utf8());
+ std::string string(cstring.data(), cstring.length());
+ return net::HttpContentDisposition(string, std::string()).is_attachment();
+}
+
+// https://html.spec.whatwg.org/multipage/semantics.html#attr-meta-http-equiv-refresh
+bool ParseHTTPRefresh(const String& refresh,
+ WTF::CharacterMatchFunctionPtr matcher,
+ double& delay,
+ String& url) {
+ unsigned len = refresh.length();
+ unsigned pos = 0;
+ matcher = matcher ? matcher : IsWhitespace;
+
+ if (!SkipWhiteSpace(refresh, pos, matcher))
+ return false;
+
+ while (pos != len && refresh[pos] != ',' && refresh[pos] != ';' &&
+ !matcher(refresh[pos]))
+ ++pos;
+
+ if (pos == len) { // no URL
+ url = String();
+ delay = ParseRefreshTime(refresh.StripWhiteSpace());
+ return std::isfinite(delay);
+ } else {
+ delay = ParseRefreshTime(refresh.Left(pos).StripWhiteSpace());
+ if (!std::isfinite(delay))
+ return false;
+
+ SkipWhiteSpace(refresh, pos, matcher);
+ if (pos < len && (refresh[pos] == ',' || refresh[pos] == ';'))
+ ++pos;
+ SkipWhiteSpace(refresh, pos, matcher);
+ unsigned url_start_pos = pos;
+ if (refresh.FindIgnoringASCIICase("url", url_start_pos) == url_start_pos) {
+ url_start_pos += 3;
+ SkipWhiteSpace(refresh, url_start_pos, matcher);
+ if (refresh[url_start_pos] == '=') {
+ ++url_start_pos;
+ SkipWhiteSpace(refresh, url_start_pos, matcher);
+ } else {
+ url_start_pos = pos; // e.g. "Refresh: 0; url.html"
+ }
+ }
+
+ unsigned url_end_pos = len;
+
+ if (refresh[url_start_pos] == '"' || refresh[url_start_pos] == '\'') {
+ UChar quotation_mark = refresh[url_start_pos];
+ url_start_pos++;
+ while (url_end_pos > url_start_pos) {
+ url_end_pos--;
+ if (refresh[url_end_pos] == quotation_mark)
+ break;
+ }
+
+ // https://bugs.webkit.org/show_bug.cgi?id=27868
+ // Sometimes there is no closing quote for the end of the URL even though
+ // there was an opening quote. If we looped over the entire alleged URL
+ // string back to the opening quote, just go ahead and use everything
+ // after the opening quote instead.
+ if (url_end_pos == url_start_pos)
+ url_end_pos = len;
+ }
+
+ url = refresh.Substring(url_start_pos, url_end_pos - url_start_pos)
+ .StripWhiteSpace();
+ return true;
+ }
+}
+
+double ParseDate(const String& value) {
+ return ParseDateFromNullTerminatedCharacters(value.Utf8().data());
+}
+
+AtomicString ExtractMIMETypeFromMediaType(const AtomicString& media_type) {
+ unsigned length = media_type.length();
+
+ unsigned pos = 0;
+
+ while (pos < length) {
+ UChar c = media_type[pos];
+ if (c != '\t' && c != ' ')
+ break;
+ ++pos;
+ }
+
+ if (pos == length)
+ return media_type;
+
+ unsigned type_start = pos;
+
+ unsigned type_end = pos;
+ while (pos < length) {
+ UChar c = media_type[pos];
+
+ // While RFC 2616 does not allow it, other browsers allow multiple values in
+ // the HTTP media type header field, Content-Type. In such cases, the media
+ // type string passed here may contain the multiple values separated by
+ // commas. For now, this code ignores text after the first comma, which
+ // prevents it from simply failing to parse such types altogether. Later
+ // for better compatibility we could consider using the first or last valid
+ // MIME type instead.
+ // See https://bugs.webkit.org/show_bug.cgi?id=25352 for more discussion.
+ if (c == ',' || c == ';')
+ break;
+
+ if (c != '\t' && c != ' ')
+ type_end = pos + 1;
+
+ ++pos;
+ }
+
+ return AtomicString(
+ media_type.GetString().Substring(type_start, type_end - type_start));
+}
+
+ReflectedXSSDisposition ParseXSSProtectionHeader(const String& header,
+ String& failure_reason,
+ unsigned& failure_position,
+ String& report_url) {
+ DEFINE_STATIC_LOCAL(String, failure_reason_invalid_toggle,
+ ("expected token to be 0 or 1"));
+ DEFINE_STATIC_LOCAL(String, failure_reason_invalid_separator,
+ ("expected semicolon"));
+ DEFINE_STATIC_LOCAL(String, failure_reason_invalid_equals,
+ ("expected equals sign"));
+ DEFINE_STATIC_LOCAL(String, failure_reason_invalid_mode,
+ ("invalid mode directive"));
+ DEFINE_STATIC_LOCAL(String, failure_reason_invalid_report,
+ ("invalid report directive"));
+ DEFINE_STATIC_LOCAL(String, failure_reason_duplicate_mode,
+ ("duplicate mode directive"));
+ DEFINE_STATIC_LOCAL(String, failure_reason_duplicate_report,
+ ("duplicate report directive"));
+ DEFINE_STATIC_LOCAL(String, failure_reason_invalid_directive,
+ ("unrecognized directive"));
+
+ HeaderFieldTokenizer tokenizer(header);
+
+ StringView toggle;
+ if (!tokenizer.ConsumeToken(Mode::kNormal, toggle)) {
+ if (tokenizer.IsConsumed())
+ return kReflectedXSSUnset;
+ }
+
+ if (toggle.length() != 1 || (toggle[0] != '0' && toggle[0] != '1')) {
+ failure_reason = failure_reason_invalid_toggle;
+ return kReflectedXSSInvalid;
+ }
+
+ if (toggle[0] == '0')
+ return kAllowReflectedXSS;
+
+ ReflectedXSSDisposition result = kFilterReflectedXSS;
+ bool mode_directive_seen = false;
+ bool report_directive_seen = false;
+
+ while (!tokenizer.IsConsumed()) {
+ // At end of previous directive: consume whitespace, semicolon, and
+ // whitespace.
+ if (!tokenizer.Consume(';')) {
+ failure_reason = failure_reason_invalid_separator;
+ failure_position = tokenizer.Index();
+ return kReflectedXSSInvalid;
+ }
+
+ // Give a pass to a trailing semicolon.
+ if (tokenizer.IsConsumed())
+ return result;
+
+ // At start of next directive.
+ StringView token;
+ unsigned token_start = tokenizer.Index();
+ if (!tokenizer.ConsumeToken(Mode::kNormal, token)) {
+ failure_reason = failure_reason_invalid_directive;
+ failure_position = token_start;
+ return kReflectedXSSInvalid;
+ }
+ if (EqualIgnoringASCIICase(token, "mode")) {
+ if (mode_directive_seen) {
+ failure_reason = failure_reason_duplicate_mode;
+ failure_position = token_start;
+ return kReflectedXSSInvalid;
+ }
+ mode_directive_seen = true;
+ if (!tokenizer.Consume('=')) {
+ failure_reason = failure_reason_invalid_equals;
+ failure_position = tokenizer.Index();
+ return kReflectedXSSInvalid;
+ }
+ String value;
+ unsigned value_start = tokenizer.Index();
+ if (!tokenizer.ConsumeTokenOrQuotedString(Mode::kNormal, value) ||
+ !EqualIgnoringASCIICase(value, "block")) {
+ failure_reason = failure_reason_invalid_mode;
+ failure_position = value_start;
+ return kReflectedXSSInvalid;
+ }
+ result = kBlockReflectedXSS;
+ } else if (EqualIgnoringASCIICase(token, "report")) {
+ if (report_directive_seen) {
+ failure_reason = failure_reason_duplicate_report;
+ failure_position = token_start;
+ return kReflectedXSSInvalid;
+ }
+ report_directive_seen = true;
+ if (!tokenizer.Consume('=')) {
+ failure_reason = failure_reason_invalid_equals;
+ failure_position = tokenizer.Index();
+ return kReflectedXSSInvalid;
+ }
+ // Set, just in case later semantic check deems unacceptable.
+ failure_position = tokenizer.Index();
+ String value;
+ // Relaxed mode - unquoted URLs contain colons and such.
+ if (!tokenizer.ConsumeTokenOrQuotedString(Mode::kRelaxed, value)) {
+ failure_reason = failure_reason_invalid_report;
+ return kReflectedXSSInvalid;
+ }
+ report_url = value;
+ } else {
+ // Unrecognized directive
+ failure_reason = failure_reason_invalid_directive;
+ failure_position = token_start;
+ return kReflectedXSSInvalid;
+ }
+ }
+
+ return result;
+}
+
+ContentTypeOptionsDisposition ParseContentTypeOptionsHeader(
+ const String& value) {
+ if (value.IsEmpty())
+ return kContentTypeOptionsNone;
+
+ Vector<String> results;
+ value.Split(",", results);
+ if (results[0].StripWhiteSpace().LowerASCII() == "nosniff")
+ return kContentTypeOptionsNosniff;
+ return kContentTypeOptionsNone;
+}
+
+static bool IsCacheHeaderSeparator(UChar c) {
+ // See RFC 2616, Section 2.2
+ switch (c) {
+ case '(':
+ case ')':
+ case '<':
+ case '>':
+ case '@':
+ case ',':
+ case ';':
+ case ':':
+ case '\\':
+ case '"':
+ case '/':
+ case '[':
+ case ']':
+ case '?':
+ case '=':
+ case '{':
+ case '}':
+ case ' ':
+ case '\t':
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool IsControlCharacter(UChar c) {
+ return c < ' ' || c == 127;
+}
+
+static inline String TrimToNextSeparator(const String& str) {
+ return str.Substring(0, str.Find(IsCacheHeaderSeparator));
+}
+
+static void ParseCacheHeader(const String& header,
+ Vector<std::pair<String, String>>& result) {
+ const String safe_header = header.RemoveCharacters(IsControlCharacter);
+ unsigned max = safe_header.length();
+ for (unsigned pos = 0; pos < max; /* pos incremented in loop */) {
+ size_t next_comma_position = safe_header.find(',', pos);
+ size_t next_equal_sign_position = safe_header.find('=', pos);
+ if (next_equal_sign_position != kNotFound &&
+ (next_equal_sign_position < next_comma_position ||
+ next_comma_position == kNotFound)) {
+ // Get directive name, parse right hand side of equal sign, then add to
+ // map
+ String directive = TrimToNextSeparator(
+ safe_header.Substring(pos, next_equal_sign_position - pos)
+ .StripWhiteSpace());
+ pos += next_equal_sign_position - pos + 1;
+
+ String value = safe_header.Substring(pos, max - pos).StripWhiteSpace();
+ if (value[0] == '"') {
+ // The value is a quoted string
+ size_t next_double_quote_position = value.find('"', 1);
+ if (next_double_quote_position != kNotFound) {
+ // Store the value as a quoted string without quotes
+ result.push_back(std::pair<String, String>(
+ directive, value.Substring(1, next_double_quote_position - 1)
+ .StripWhiteSpace()));
+ pos += (safe_header.find('"', pos) - pos) +
+ next_double_quote_position + 1;
+ // Move past next comma, if there is one
+ size_t next_comma_position2 = safe_header.find(',', pos);
+ if (next_comma_position2 != kNotFound)
+ pos += next_comma_position2 - pos + 1;
+ else
+ return; // Parse error if there is anything left with no comma
+ } else {
+ // Parse error; just use the rest as the value
+ result.push_back(std::pair<String, String>(
+ directive,
+ TrimToNextSeparator(
+ value.Substring(1, value.length() - 1).StripWhiteSpace())));
+ return;
+ }
+ } else {
+ // The value is a token until the next comma
+ size_t next_comma_position2 = value.find(',');
+ if (next_comma_position2 != kNotFound) {
+ // The value is delimited by the next comma
+ result.push_back(std::pair<String, String>(
+ directive,
+ TrimToNextSeparator(
+ value.Substring(0, next_comma_position2).StripWhiteSpace())));
+ pos += (safe_header.find(',', pos) - pos) + 1;
+ } else {
+ // The rest is the value; no change to value needed
+ result.push_back(
+ std::pair<String, String>(directive, TrimToNextSeparator(value)));
+ return;
+ }
+ }
+ } else if (next_comma_position != kNotFound &&
+ (next_comma_position < next_equal_sign_position ||
+ next_equal_sign_position == kNotFound)) {
+ // Add directive to map with empty string as value
+ result.push_back(std::pair<String, String>(
+ TrimToNextSeparator(
+ safe_header.Substring(pos, next_comma_position - pos)
+ .StripWhiteSpace()),
+ ""));
+ pos += next_comma_position - pos + 1;
+ } else {
+ // Add last directive to map with empty string as value
+ result.push_back(std::pair<String, String>(
+ TrimToNextSeparator(
+ safe_header.Substring(pos, max - pos).StripWhiteSpace()),
+ ""));
+ return;
+ }
+ }
+}
+
+CacheControlHeader ParseCacheControlDirectives(
+ const AtomicString& cache_control_value,
+ const AtomicString& pragma_value) {
+ CacheControlHeader cache_control_header;
+ cache_control_header.parsed = true;
+ cache_control_header.max_age = std::numeric_limits<double>::quiet_NaN();
+
+ static const char kNoCacheDirective[] = "no-cache";
+ static const char kNoStoreDirective[] = "no-store";
+ static const char kMustRevalidateDirective[] = "must-revalidate";
+ static const char kMaxAgeDirective[] = "max-age";
+
+ if (!cache_control_value.IsEmpty()) {
+ Vector<std::pair<String, String>> directives;
+ ParseCacheHeader(cache_control_value, directives);
+
+ size_t directives_size = directives.size();
+ for (size_t i = 0; i < directives_size; ++i) {
+ // RFC2616 14.9.1: A no-cache directive with a value is only meaningful
+ // for proxy caches. It should be ignored by a browser level cache.
+ if (DeprecatedEqualIgnoringCase(directives[i].first, kNoCacheDirective) &&
+ directives[i].second.IsEmpty()) {
+ cache_control_header.contains_no_cache = true;
+ } else if (DeprecatedEqualIgnoringCase(directives[i].first,
+ kNoStoreDirective)) {
+ cache_control_header.contains_no_store = true;
+ } else if (DeprecatedEqualIgnoringCase(directives[i].first,
+ kMustRevalidateDirective)) {
+ cache_control_header.contains_must_revalidate = true;
+ } else if (DeprecatedEqualIgnoringCase(directives[i].first,
+ kMaxAgeDirective)) {
+ if (!std::isnan(cache_control_header.max_age)) {
+ // First max-age directive wins if there are multiple ones.
+ continue;
+ }
+ bool ok;
+ double max_age = directives[i].second.ToDouble(&ok);
+ if (ok)
+ cache_control_header.max_age = max_age;
+ }
+ }
+ }
+
+ if (!cache_control_header.contains_no_cache) {
+ // Handle Pragma: no-cache
+ // This is deprecated and equivalent to Cache-control: no-cache
+ // Don't bother tokenizing the value, it is not important
+ cache_control_header.contains_no_cache =
+ pragma_value.DeprecatedLower().Contains(kNoCacheDirective);
+ }
+ return cache_control_header;
+}
+
+void ParseCommaDelimitedHeader(const String& header_value,
+ CommaDelimitedHeaderSet& header_set) {
+ Vector<String> results;
+ header_value.Split(",", results);
+ for (auto& value : results)
+ header_set.insert(value.StripWhiteSpace(IsWhitespace));
+}
+
+bool ParseMultipartHeadersFromBody(const char* bytes,
+ size_t size,
+ ResourceResponse* response,
+ size_t* end) {
+ DCHECK(IsMainThread());
+
+ int headers_end_pos =
+ net::HttpUtil::LocateEndOfAdditionalHeaders(bytes, size, 0);
+
+ if (headers_end_pos < 0)
+ return false;
+
+ *end = headers_end_pos;
+
+ // Eat headers and prepend a status line as is required by
+ // HttpResponseHeaders.
+ std::string headers("HTTP/1.1 200 OK\r\n");
+ headers.append(bytes, headers_end_pos);
+
+ scoped_refptr<net::HttpResponseHeaders> response_headers =
+ new net::HttpResponseHeaders(
+ net::HttpUtil::AssembleRawHeaders(headers.data(), headers.length()));
+
+ std::string mime_type, charset;
+ response_headers->GetMimeTypeAndCharset(&mime_type, &charset);
+ response->SetMimeType(WebString::FromUTF8(mime_type));
+ response->SetTextEncodingName(WebString::FromUTF8(charset));
+
+ // Copy headers listed in replaceHeaders to the response.
+ for (const AtomicString& header : ReplaceHeaders()) {
+ std::string value;
+ StringUTF8Adaptor adaptor(header);
+ base::StringPiece header_string_piece(adaptor.AsStringPiece());
+ size_t iterator = 0;
+
+ response->ClearHTTPHeaderField(header);
+ while (response_headers->EnumerateHeader(&iterator, header_string_piece,
+ &value)) {
+ response->AddHTTPHeaderField(header, WebString::FromLatin1(value));
+ }
+ }
+ return true;
+}
+
+bool ParseMultipartFormHeadersFromBody(const char* bytes,
+ size_t size,
+ HTTPHeaderMap* header_fields,
+ size_t* end) {
+ DCHECK_EQ(0u, header_fields->size());
+
+ int headersEndPos =
+ net::HttpUtil::LocateEndOfAdditionalHeaders(bytes, size, 0);
+
+ if (headersEndPos < 0)
+ return false;
+
+ *end = headersEndPos;
+
+ // Eat headers and prepend a status line as is required by
+ // HttpResponseHeaders.
+ std::string headers("HTTP/1.1 200 OK\r\n");
+ headers.append(bytes, headersEndPos);
+
+ scoped_refptr<net::HttpResponseHeaders> responseHeaders =
+ new net::HttpResponseHeaders(
+ net::HttpUtil::AssembleRawHeaders(headers.data(), headers.length()));
+
+ // Copy selected header fields.
+ const AtomicString* const headerNamePointers[] = {
+ &HTTPNames::Content_Disposition, &HTTPNames::Content_Type};
+ for (const AtomicString* headerNamePointer : headerNamePointers) {
+ StringUTF8Adaptor adaptor(*headerNamePointer);
+ size_t iterator = 0;
+ base::StringPiece headerNameStringPiece = adaptor.AsStringPiece();
+ std::string value;
+ while (responseHeaders->EnumerateHeader(&iterator, headerNameStringPiece,
+ &value)) {
+ header_fields->Add(*headerNamePointer, WebString::FromUTF8(value));
+ }
+ }
+
+ return true;
+}
+
+bool ParseContentRangeHeaderFor206(const String& content_range,
+ int64_t* first_byte_position,
+ int64_t* last_byte_position,
+ int64_t* instance_length) {
+ return net::HttpUtil::ParseContentRangeHeaderFor206(
+ StringUTF8Adaptor(content_range).AsStringPiece(), first_byte_position,
+ last_byte_position, instance_length);
+}
+
+std::unique_ptr<ServerTimingHeaderVector> ParseServerTimingHeader(
+ const String& headerValue) {
+ std::unique_ptr<ServerTimingHeaderVector> headers =
+ std::make_unique<ServerTimingHeaderVector>();
+
+ if (!headerValue.IsNull()) {
+ DCHECK(headerValue.Is8Bit());
+
+ HeaderFieldTokenizer tokenizer(headerValue);
+ while (!tokenizer.IsConsumed()) {
+ StringView name;
+ if (!tokenizer.ConsumeToken(Mode::kNormal, name)) {
+ break;
+ }
+
+ ServerTimingHeader header(name.ToString());
+
+ while (tokenizer.Consume(';')) {
+ StringView parameter_name;
+ if (!tokenizer.ConsumeToken(Mode::kNormal, parameter_name)) {
+ break;
+ }
+
+ String value = "";
+ if (tokenizer.Consume('=')) {
+ tokenizer.ConsumeTokenOrQuotedString(Mode::kNormal, value);
+ tokenizer.ConsumeBeforeAnyCharMatch({',', ';'});
+ }
+ header.SetParameter(parameter_name, value);
+ }
+
+ headers->push_back(std::make_unique<ServerTimingHeader>(header));
+
+ if (!tokenizer.Consume(',')) {
+ break;
+ }
+ }
+ }
+ return headers;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/network/http_parsers.h b/chromium/third_party/blink/renderer/platform/network/http_parsers.h
new file mode 100644
index 00000000000..8420bb37b72
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/http_parsers.h
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org)
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ * Copyright (C) 2011 Apple 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_HTTP_PARSERS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_HTTP_PARSERS_H_
+
+#include "third_party/blink/renderer/platform/network/parsed_content_type.h"
+#include "third_party/blink/renderer/platform/network/server_timing_header.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+#include <stdint.h>
+#include <memory>
+
+namespace blink {
+
+class HTTPHeaderMap;
+class ResourceResponse;
+
+enum ContentTypeOptionsDisposition {
+ kContentTypeOptionsNone,
+ kContentTypeOptionsNosniff
+};
+
+// Be aware that some behavior may depend on this enum's ordering, with
+// higher values taking precedence over lower ones.
+enum ReflectedXSSDisposition {
+ kReflectedXSSUnset = 0,
+ kAllowReflectedXSS,
+ kReflectedXSSInvalid,
+ kFilterReflectedXSS,
+ kBlockReflectedXSS
+};
+
+using CommaDelimitedHeaderSet = HashSet<String, CaseFoldingHash>;
+
+struct CacheControlHeader {
+ DISALLOW_NEW();
+ bool parsed : 1;
+ bool contains_no_cache : 1;
+ bool contains_no_store : 1;
+ bool contains_must_revalidate : 1;
+ double max_age;
+
+ CacheControlHeader()
+ : parsed(false),
+ contains_no_cache(false),
+ contains_no_store(false),
+ contains_must_revalidate(false),
+ max_age(0.0) {}
+};
+
+using ServerTimingHeaderVector = Vector<std::unique_ptr<ServerTimingHeader>>;
+
+PLATFORM_EXPORT bool IsContentDispositionAttachment(const String&);
+PLATFORM_EXPORT bool IsValidHTTPHeaderValue(const String&);
+// Checks whether the given string conforms to the |token| ABNF production
+// defined in the RFC 7230 or not.
+//
+// The ABNF is for validating octets, but this method takes a String instance
+// for convenience which consists of Unicode code points. When this method sees
+// non-ASCII characters, it just returns false.
+PLATFORM_EXPORT bool IsValidHTTPToken(const String&);
+// |matcher| specifies a function to check a whitespace character. if |nullptr|
+// is specified, ' ' and '\t' are treated as whitespace characters.
+PLATFORM_EXPORT bool ParseHTTPRefresh(const String& refresh,
+ WTF::CharacterMatchFunctionPtr matcher,
+ double& delay,
+ String& url);
+PLATFORM_EXPORT double ParseDate(const String&);
+
+// Given a Media Type (like "foo/bar; baz=gazonk" - usually from the
+// 'Content-Type' HTTP header), extract and return the "type/subtype" portion
+// ("foo/bar").
+//
+// Note:
+// - This function does not in any way check that the "type/subtype" pair
+// is well-formed.
+// - OWSes at the head and the tail of the region before the first semicolon
+// are trimmed.
+PLATFORM_EXPORT AtomicString ExtractMIMETypeFromMediaType(const AtomicString&);
+
+// Given an X-XSS-Protection value like "1; mode=block; report=/foo", combine
+// the first positional parameter and the "mode" into the result code, and
+// return the "report" as report_url, if present. Return kReflectedXSSInvalid
+// on bad syntax, setting |failure_reason| and |failure_position|, otherwise
+// set |failure_position| to the start of the "report" URL, if present (since
+// it is not validated here, and the caller may need that position information
+// to construct an error message).
+PLATFORM_EXPORT ReflectedXSSDisposition
+ParseXSSProtectionHeader(const String& header,
+ String& failure_reason,
+ unsigned& failure_position,
+ String& report_url);
+
+PLATFORM_EXPORT CacheControlHeader
+ParseCacheControlDirectives(const AtomicString& cache_control_header,
+ const AtomicString& pragma_header);
+PLATFORM_EXPORT void ParseCommaDelimitedHeader(const String& header_value,
+ CommaDelimitedHeaderSet&);
+
+PLATFORM_EXPORT ContentTypeOptionsDisposition
+ParseContentTypeOptionsHeader(const String& header);
+
+// Returns true and stores the position of the end of the headers to |*end|
+// if the headers part ends in |bytes[0..size]|. Returns false otherwise.
+PLATFORM_EXPORT bool ParseMultipartFormHeadersFromBody(
+ const char* bytes,
+ size_t,
+ HTTPHeaderMap* header_fields,
+ size_t* end);
+
+// Returns true and stores the position of the end of the headers to |*end|
+// if the headers part ends in |bytes[0..size]|. Returns false otherwise.
+PLATFORM_EXPORT bool ParseMultipartHeadersFromBody(const char* bytes,
+ size_t,
+ ResourceResponse*,
+ size_t* end);
+
+// Extracts the values in a Content-Range header and returns true if all three
+// values are present and valid for a 206 response; otherwise returns false.
+// The following values will be outputted:
+// |*first_byte_position| = inclusive position of the first byte of the range
+// |*last_byte_position| = inclusive position of the last byte of the range
+// |*instance_length| = size in bytes of the object requested
+// If this method returns false, then all of the outputs will be -1.
+PLATFORM_EXPORT bool ParseContentRangeHeaderFor206(const String& content_range,
+ int64_t* first_byte_position,
+ int64_t* last_byte_position,
+ int64_t* instance_length);
+
+PLATFORM_EXPORT std::unique_ptr<ServerTimingHeaderVector>
+ParseServerTimingHeader(const String&);
+
+using Mode = blink::ParsedContentType::Mode;
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/network/http_parsers_fuzzer.cc b/chromium/third_party/blink/renderer/platform/network/http_parsers_fuzzer.cc
new file mode 100644
index 00000000000..7f8de80b18e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/http_parsers_fuzzer.cc
@@ -0,0 +1,38 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/network/http_parsers.h"
+
+#include <string>
+
+#include "base/logging.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
+#include "third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ static blink::BlinkFuzzerTestSupport test_support;
+
+ blink::CommaDelimitedHeaderSet set;
+ double delay;
+ String url;
+ blink::ResourceResponse response;
+ size_t end;
+ String report_url;
+ String failure_reason;
+ unsigned failure_position = 0;
+
+ std::string terminated(reinterpret_cast<const char*>(data), size);
+ blink::IsValidHTTPToken(terminated.c_str());
+ blink::ParseCacheControlDirectives(terminated.c_str(), AtomicString());
+ blink::ParseCommaDelimitedHeader(terminated.c_str(), set);
+ blink::ParseHTTPRefresh(terminated.c_str(), nullptr, delay, url);
+ blink::ParseMultipartHeadersFromBody(terminated.c_str(), terminated.size(),
+ &response, &end);
+ blink::ParseServerTimingHeader(terminated.c_str());
+ blink::ParseContentTypeOptionsHeader(terminated.c_str());
+ blink::ParseXSSProtectionHeader(terminated.c_str(), failure_reason,
+ failure_position, report_url);
+ return 0;
+}
diff --git a/chromium/third_party/blink/renderer/platform/network/http_parsers_test.cc b/chromium/third_party/blink/renderer/platform/network/http_parsers_test.cc
new file mode 100644
index 00000000000..a15991773c2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/http_parsers_test.cc
@@ -0,0 +1,735 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/network/http_parsers.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
+#include "third_party/blink/renderer/platform/wtf/dtoa/utils.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+
+namespace blink {
+
+TEST(HTTPParsersTest, ParseCacheControl) {
+ CacheControlHeader header;
+
+ header = ParseCacheControlDirectives("no-cache", AtomicString());
+ EXPECT_TRUE(header.parsed);
+ EXPECT_TRUE(header.contains_no_cache);
+ EXPECT_FALSE(header.contains_no_store);
+ EXPECT_FALSE(header.contains_must_revalidate);
+ EXPECT_TRUE(std::isnan(header.max_age));
+
+ header = ParseCacheControlDirectives("no-cache no-store", AtomicString());
+ EXPECT_TRUE(header.parsed);
+ EXPECT_TRUE(header.contains_no_cache);
+ EXPECT_FALSE(header.contains_no_store);
+ EXPECT_FALSE(header.contains_must_revalidate);
+ EXPECT_TRUE(std::isnan(header.max_age));
+
+ header =
+ ParseCacheControlDirectives("no-store must-revalidate", AtomicString());
+ EXPECT_TRUE(header.parsed);
+ EXPECT_FALSE(header.contains_no_cache);
+ EXPECT_TRUE(header.contains_no_store);
+ EXPECT_FALSE(header.contains_must_revalidate);
+ EXPECT_TRUE(std::isnan(header.max_age));
+
+ header = ParseCacheControlDirectives("max-age=0", AtomicString());
+ EXPECT_TRUE(header.parsed);
+ EXPECT_FALSE(header.contains_no_cache);
+ EXPECT_FALSE(header.contains_no_store);
+ EXPECT_FALSE(header.contains_must_revalidate);
+ EXPECT_EQ(0.0, header.max_age);
+
+ header = ParseCacheControlDirectives("max-age", AtomicString());
+ EXPECT_TRUE(header.parsed);
+ EXPECT_FALSE(header.contains_no_cache);
+ EXPECT_FALSE(header.contains_no_store);
+ EXPECT_FALSE(header.contains_must_revalidate);
+ EXPECT_TRUE(std::isnan(header.max_age));
+
+ header = ParseCacheControlDirectives("max-age=0 no-cache", AtomicString());
+ EXPECT_TRUE(header.parsed);
+ EXPECT_FALSE(header.contains_no_cache);
+ EXPECT_FALSE(header.contains_no_store);
+ EXPECT_FALSE(header.contains_must_revalidate);
+ EXPECT_EQ(0.0, header.max_age);
+
+ header = ParseCacheControlDirectives("no-cache=foo", AtomicString());
+ EXPECT_TRUE(header.parsed);
+ EXPECT_FALSE(header.contains_no_cache);
+ EXPECT_FALSE(header.contains_no_store);
+ EXPECT_FALSE(header.contains_must_revalidate);
+ EXPECT_TRUE(std::isnan(header.max_age));
+
+ header = ParseCacheControlDirectives("nonsense", AtomicString());
+ EXPECT_TRUE(header.parsed);
+ EXPECT_FALSE(header.contains_no_cache);
+ EXPECT_FALSE(header.contains_no_store);
+ EXPECT_FALSE(header.contains_must_revalidate);
+ EXPECT_TRUE(std::isnan(header.max_age));
+
+ header = ParseCacheControlDirectives("\rno-cache\n\t\v\0\b", AtomicString());
+ EXPECT_TRUE(header.parsed);
+ EXPECT_TRUE(header.contains_no_cache);
+ EXPECT_FALSE(header.contains_no_store);
+ EXPECT_FALSE(header.contains_must_revalidate);
+ EXPECT_TRUE(std::isnan(header.max_age));
+
+ header = ParseCacheControlDirectives(" no-cache ", AtomicString());
+ EXPECT_TRUE(header.parsed);
+ EXPECT_TRUE(header.contains_no_cache);
+ EXPECT_FALSE(header.contains_no_store);
+ EXPECT_FALSE(header.contains_must_revalidate);
+ EXPECT_TRUE(std::isnan(header.max_age));
+
+ header = ParseCacheControlDirectives(AtomicString(), "no-cache");
+ EXPECT_TRUE(header.parsed);
+ EXPECT_TRUE(header.contains_no_cache);
+ EXPECT_FALSE(header.contains_no_store);
+ EXPECT_FALSE(header.contains_must_revalidate);
+ EXPECT_TRUE(std::isnan(header.max_age));
+}
+
+TEST(HTTPParsersTest, CommaDelimitedHeaderSet) {
+ CommaDelimitedHeaderSet set1;
+ CommaDelimitedHeaderSet set2;
+ ParseCommaDelimitedHeader("dpr, rw, whatever", set1);
+ EXPECT_TRUE(set1.Contains("dpr"));
+ EXPECT_TRUE(set1.Contains("rw"));
+ EXPECT_TRUE(set1.Contains("whatever"));
+ ParseCommaDelimitedHeader("dprw\t , fo\to", set2);
+ EXPECT_FALSE(set2.Contains("dpr"));
+ EXPECT_FALSE(set2.Contains("rw"));
+ EXPECT_FALSE(set2.Contains("whatever"));
+ EXPECT_TRUE(set2.Contains("dprw"));
+ EXPECT_FALSE(set2.Contains("foo"));
+ EXPECT_TRUE(set2.Contains("fo\to"));
+}
+
+TEST(HTTPParsersTest, HTTPToken) {
+ const UChar kHiraganaA[2] = {0x3042, 0};
+ const UChar kLatinCapitalAWithMacron[2] = {0x100, 0};
+
+ EXPECT_TRUE(blink::IsValidHTTPToken("gzip"));
+ EXPECT_TRUE(blink::IsValidHTTPToken("no-cache"));
+ EXPECT_TRUE(blink::IsValidHTTPToken("86400"));
+ EXPECT_TRUE(blink::IsValidHTTPToken("~"));
+ EXPECT_FALSE(blink::IsValidHTTPToken(""));
+ EXPECT_FALSE(blink::IsValidHTTPToken(" "));
+ EXPECT_FALSE(blink::IsValidHTTPToken("\t"));
+ EXPECT_FALSE(blink::IsValidHTTPToken("\x7f"));
+ EXPECT_FALSE(blink::IsValidHTTPToken("\xff"));
+ EXPECT_FALSE(blink::IsValidHTTPToken(String(kLatinCapitalAWithMacron)));
+ EXPECT_FALSE(blink::IsValidHTTPToken("t a"));
+ EXPECT_FALSE(blink::IsValidHTTPToken("()"));
+ EXPECT_FALSE(blink::IsValidHTTPToken("(foobar)"));
+ EXPECT_FALSE(blink::IsValidHTTPToken(String("\0", 1)));
+ EXPECT_FALSE(blink::IsValidHTTPToken(String(kHiraganaA)));
+}
+
+TEST(HTTPParsersTest, ExtractMIMETypeFromMediaType) {
+ const AtomicString text_html("text/html");
+
+ EXPECT_EQ(text_html, ExtractMIMETypeFromMediaType(AtomicString("text/html")));
+ EXPECT_EQ(text_html, ExtractMIMETypeFromMediaType(
+ AtomicString("text/html; charset=iso-8859-1")));
+
+ // Quoted charset parameter
+ EXPECT_EQ(text_html, ExtractMIMETypeFromMediaType(
+ AtomicString("text/html; charset=\"quoted\"")));
+
+ // Multiple parameters
+ EXPECT_EQ(text_html, ExtractMIMETypeFromMediaType(
+ AtomicString("text/html; charset=x; foo=bar")));
+
+ // OWSes are trimmed.
+ EXPECT_EQ(text_html,
+ ExtractMIMETypeFromMediaType(AtomicString(" text/html ")));
+ EXPECT_EQ(text_html,
+ ExtractMIMETypeFromMediaType(AtomicString("\ttext/html \t")));
+ EXPECT_EQ(text_html, ExtractMIMETypeFromMediaType(
+ AtomicString("text/html ; charset=iso-8859-1")));
+
+ // Non-standard multiple type/subtype listing using a comma as a separator
+ // is accepted.
+ EXPECT_EQ(text_html,
+ ExtractMIMETypeFromMediaType(AtomicString("text/html,text/plain")));
+ EXPECT_EQ(text_html, ExtractMIMETypeFromMediaType(
+ AtomicString("text/html , text/plain")));
+ EXPECT_EQ(text_html, ExtractMIMETypeFromMediaType(
+ AtomicString("text/html\t,\ttext/plain")));
+ EXPECT_EQ(text_html, ExtractMIMETypeFromMediaType(AtomicString(
+ "text/html,text/plain;charset=iso-8859-1")));
+
+ // Preserves case.
+ EXPECT_EQ("tExt/hTMl",
+ ExtractMIMETypeFromMediaType(AtomicString("tExt/hTMl")));
+
+ EXPECT_EQ(g_empty_string,
+ ExtractMIMETypeFromMediaType(AtomicString(", text/html")));
+ EXPECT_EQ(g_empty_string,
+ ExtractMIMETypeFromMediaType(AtomicString("; text/html")));
+
+ // If no normalization is required, the same AtomicString should be returned.
+ const AtomicString& passthrough = ExtractMIMETypeFromMediaType(text_html);
+ EXPECT_EQ(text_html.Impl(), passthrough.Impl());
+}
+
+TEST(HTTPParsersTest, ExtractMIMETypeFromMediaTypeInvalidInput) {
+ // extractMIMETypeFromMediaType() returns the string before the first
+ // semicolon after trimming OWSes at the head and the tail even if the
+ // string doesn't conform to the media-type ABNF defined in the RFC 7231.
+
+ // These behaviors could be fixed later when ready.
+
+ // Non-OWS characters meaning space are not trimmed.
+ EXPECT_EQ(AtomicString("\r\ntext/html\r\n"),
+ ExtractMIMETypeFromMediaType(AtomicString("\r\ntext/html\r\n")));
+ // U+2003, EM SPACE (UTF-8: E2 80 83).
+ EXPECT_EQ(AtomicString::FromUTF8("\xE2\x80\x83text/html"),
+ ExtractMIMETypeFromMediaType(
+ AtomicString::FromUTF8("\xE2\x80\x83text/html")));
+
+ // Invalid type/subtype.
+ EXPECT_EQ(AtomicString("a"), ExtractMIMETypeFromMediaType(AtomicString("a")));
+
+ // Invalid parameters.
+ EXPECT_EQ(AtomicString("text/html"),
+ ExtractMIMETypeFromMediaType(AtomicString("text/html;wow")));
+ EXPECT_EQ(AtomicString("text/html"),
+ ExtractMIMETypeFromMediaType(AtomicString("text/html;;;;;;")));
+ EXPECT_EQ(AtomicString("text/html"),
+ ExtractMIMETypeFromMediaType(AtomicString("text/html; = = = ")));
+
+ // Only OWSes at either the beginning or the end of the type/subtype
+ // portion.
+ EXPECT_EQ(AtomicString("text / html"),
+ ExtractMIMETypeFromMediaType(AtomicString("text / html")));
+ EXPECT_EQ(AtomicString("t e x t / h t m l"),
+ ExtractMIMETypeFromMediaType(AtomicString("t e x t / h t m l")));
+
+ EXPECT_EQ(AtomicString("text\r\n/\nhtml"),
+ ExtractMIMETypeFromMediaType(AtomicString("text\r\n/\nhtml")));
+ EXPECT_EQ(AtomicString("text\n/\nhtml"),
+ ExtractMIMETypeFromMediaType(AtomicString("text\n/\nhtml")));
+ EXPECT_EQ(AtomicString::FromUTF8("text\xE2\x80\x83/html"),
+ ExtractMIMETypeFromMediaType(
+ AtomicString::FromUTF8("text\xE2\x80\x83/html")));
+}
+
+TEST(HTTPParsersTest, ParseHTTPRefresh) {
+ double delay;
+ String url;
+ EXPECT_FALSE(ParseHTTPRefresh("", nullptr, delay, url));
+ EXPECT_FALSE(ParseHTTPRefresh(" ", nullptr, delay, url));
+ EXPECT_FALSE(ParseHTTPRefresh("1.3xyz url=foo", nullptr, delay, url));
+ EXPECT_FALSE(ParseHTTPRefresh("1.3.4xyz url=foo", nullptr, delay, url));
+ EXPECT_FALSE(ParseHTTPRefresh("1e1 url=foo", nullptr, delay, url));
+
+ EXPECT_TRUE(ParseHTTPRefresh("123 ", nullptr, delay, url));
+ EXPECT_EQ(123.0, delay);
+ EXPECT_TRUE(url.IsEmpty());
+
+ EXPECT_TRUE(ParseHTTPRefresh("1 ; url=dest", nullptr, delay, url));
+ EXPECT_EQ(1.0, delay);
+ EXPECT_EQ("dest", url);
+ EXPECT_TRUE(
+ ParseHTTPRefresh("1 ;\nurl=dest", IsASCIISpace<UChar>, delay, url));
+ EXPECT_EQ(1.0, delay);
+ EXPECT_EQ("dest", url);
+ EXPECT_TRUE(ParseHTTPRefresh("1 ;\nurl=dest", nullptr, delay, url));
+ EXPECT_EQ(1.0, delay);
+ EXPECT_EQ("url=dest", url);
+
+ EXPECT_TRUE(ParseHTTPRefresh("1 url=dest", nullptr, delay, url));
+ EXPECT_EQ(1.0, delay);
+ EXPECT_EQ("dest", url);
+
+ EXPECT_TRUE(
+ ParseHTTPRefresh("10\nurl=dest", IsASCIISpace<UChar>, delay, url));
+ EXPECT_EQ(10, delay);
+ EXPECT_EQ("dest", url);
+
+ EXPECT_TRUE(
+ ParseHTTPRefresh("1.5; url=dest", IsASCIISpace<UChar>, delay, url));
+ EXPECT_EQ(1.5, delay);
+ EXPECT_EQ("dest", url);
+ EXPECT_TRUE(
+ ParseHTTPRefresh("1.5.9; url=dest", IsASCIISpace<UChar>, delay, url));
+ EXPECT_EQ(1.5, delay);
+ EXPECT_EQ("dest", url);
+ EXPECT_TRUE(
+ ParseHTTPRefresh("7..; url=dest", IsASCIISpace<UChar>, delay, url));
+ EXPECT_EQ(7, delay);
+ EXPECT_EQ("dest", url);
+}
+
+TEST(HTTPParsersTest, ParseMultipartHeadersResult) {
+ struct {
+ const char* data;
+ const bool result;
+ const size_t end;
+ } tests[] = {
+ {"This is junk", false, 0},
+ {"Foo: bar\nBaz:\n\nAfter:\n", true, 15},
+ {"Foo: bar\nBaz:\n", false, 0},
+ {"Foo: bar\r\nBaz:\r\n\r\nAfter:\r\n", true, 18},
+ {"Foo: bar\r\nBaz:\r\n", false, 0},
+ {"Foo: bar\nBaz:\r\n\r\nAfter:\n\n", true, 17},
+ {"Foo: bar\r\nBaz:\n", false, 0},
+ {"\r\n", true, 2},
+ };
+ for (size_t i = 0; i < WTF_ARRAY_LENGTH(tests); ++i) {
+ ResourceResponse response;
+ size_t end = 0;
+ bool result = ParseMultipartHeadersFromBody(
+ tests[i].data, strlen(tests[i].data), &response, &end);
+ EXPECT_EQ(tests[i].result, result);
+ EXPECT_EQ(tests[i].end, end);
+ }
+}
+
+TEST(HTTPParsersTest, ParseMultipartHeaders) {
+ ResourceResponse response;
+ response.AddHTTPHeaderField("foo", "bar");
+ response.AddHTTPHeaderField("range", "piyo");
+ response.AddHTTPHeaderField("content-length", "999");
+
+ const char kData[] = "content-type: image/png\ncontent-length: 10\n\n";
+ size_t end = 0;
+ bool result =
+ ParseMultipartHeadersFromBody(kData, strlen(kData), &response, &end);
+
+ EXPECT_TRUE(result);
+ EXPECT_EQ(strlen(kData), end);
+ EXPECT_EQ("image/png", response.HttpHeaderField("content-type"));
+ EXPECT_EQ("10", response.HttpHeaderField("content-length"));
+ EXPECT_EQ("bar", response.HttpHeaderField("foo"));
+ EXPECT_EQ(AtomicString(), response.HttpHeaderField("range"));
+}
+
+TEST(HTTPParsersTest, ParseMultipartHeadersContentCharset) {
+ ResourceResponse response;
+ const char kData[] = "content-type: text/html; charset=utf-8\n\n";
+ size_t end = 0;
+ bool result =
+ ParseMultipartHeadersFromBody(kData, strlen(kData), &response, &end);
+
+ EXPECT_TRUE(result);
+ EXPECT_EQ(strlen(kData), end);
+ EXPECT_EQ("text/html; charset=utf-8",
+ response.HttpHeaderField("content-type"));
+ EXPECT_EQ("utf-8", response.TextEncodingName());
+}
+
+void testServerTimingHeader(const char* headerValue,
+ Vector<Vector<String>> expectedResults) {
+ std::unique_ptr<ServerTimingHeaderVector> results =
+ ParseServerTimingHeader(headerValue);
+ EXPECT_EQ((*results).size(), expectedResults.size());
+ unsigned i = 0;
+ for (const auto& header : *results) {
+ Vector<String> expectedResult = expectedResults[i++];
+ EXPECT_EQ(header->Name(), expectedResult[0]);
+ EXPECT_EQ(header->Duration(), expectedResult[1].ToDouble());
+ EXPECT_EQ(header->Description(), expectedResult[2]);
+ }
+}
+
+TEST(HTTPParsersTest, ParseServerTimingHeader) {
+ // empty string
+ testServerTimingHeader("", {});
+
+ // name only
+ testServerTimingHeader("metric", {{"metric", "0", ""}});
+
+ // name and duration
+ testServerTimingHeader("metric;dur=123.4", {{"metric", "123.4", ""}});
+ testServerTimingHeader("metric;dur=\"123.4\"", {{"metric", "123.4", ""}});
+
+ // name and description
+ testServerTimingHeader("metric;desc=description",
+ {{"metric", "0", "description"}});
+ testServerTimingHeader("metric;desc=\"description\"",
+ {{"metric", "0", "description"}});
+
+ // name, duration, and description
+ testServerTimingHeader("metric;dur=123.4;desc=description",
+ {{"metric", "123.4", "description"}});
+ testServerTimingHeader("metric;desc=description;dur=123.4",
+ {{"metric", "123.4", "description"}});
+
+ // special chars in name
+ testServerTimingHeader("aB3!#$%&'*+-.^_`|~",
+ {{"aB3!#$%&'*+-.^_`|~", "0", ""}});
+
+ // delimiter chars in quoted description
+ testServerTimingHeader("metric;desc=\"descr;,=iption\";dur=123.4",
+ {{"metric", "123.4", "descr;,=iption"}});
+
+ // spaces
+ testServerTimingHeader("metric ; ", {{"metric", "0", ""}});
+ testServerTimingHeader("metric , ", {{"metric", "0", ""}});
+ testServerTimingHeader("metric ; dur = 123.4 ; desc = description",
+ {{"metric", "123.4", "description"}});
+ testServerTimingHeader("metric ; desc = description ; dur = 123.4",
+ {{"metric", "123.4", "description"}});
+ testServerTimingHeader("metric;desc = \"description\"",
+ {{"metric", "0", "description"}});
+
+ // tabs
+ /* known failures:
+ https://bugs.chromium.org/p/chromium/issues/detail?id=798446
+ testServerTimingHeader("metric\t;\t", {{"metric", "0", ""}});
+ testServerTimingHeader("metric\t,\t", {{"metric", "0", ""}});
+ testServerTimingHeader("metric\t;\tdur\t=\t123.4\t;\tdesc\t=\tdescription",
+ {{"metric", "123.4", "description"}});
+ testServerTimingHeader("metric\t;\tdesc\t=\tdescription\t;\tdur\t=\t123.4",
+ {{"metric", "123.4", "description"}});
+ testServerTimingHeader("metric;desc\t=\t\"description\"", {{"metric", "0",
+ "description"}});
+ */
+
+ // multiple entries
+ testServerTimingHeader(
+ "metric1;dur=12.3;desc=description1,metric2;dur=45.6;desc=description2,"
+ "metric3;dur=78.9;desc=description3",
+ {{"metric1", "12.3", "description1"},
+ {"metric2", "45.6", "description2"},
+ {"metric3", "78.9", "description3"}});
+ testServerTimingHeader("metric1,metric2 ,metric3, metric4 , metric5",
+ {{"metric1", "0", ""},
+ {"metric2", "0", ""},
+ {"metric3", "0", ""},
+ {"metric4", "0", ""},
+ {"metric5", "0", ""}});
+
+ // quoted-strings - happy path
+ testServerTimingHeader("metric;desc=\"description\"",
+ {{"metric", "0", "description"}});
+ testServerTimingHeader("metric;desc=\"\t description \t\"",
+ {{"metric", "0", "\t description \t"}});
+ testServerTimingHeader("metric;desc=\"descr\\\"iption\"",
+ {{"metric", "0", "descr\"iption"}});
+
+ // quoted-strings - others
+ // metric;desc=\ --> ''
+ testServerTimingHeader("metric;desc=\\", {{"metric", "0", ""}});
+ // metric;desc=" --> ''
+ testServerTimingHeader("metric;desc=\"", {{"metric", "0", ""}});
+ // metric;desc=\\ --> ''
+ testServerTimingHeader("metric;desc=\\\\", {{"metric", "0", ""}});
+ // metric;desc=\" --> ''
+ testServerTimingHeader("metric;desc=\\\"", {{"metric", "0", ""}});
+ // metric;desc="\ --> ''
+ testServerTimingHeader("metric;desc=\"\\", {{"metric", "0", ""}});
+ // metric;desc="" --> ''
+ testServerTimingHeader("metric;desc=\"\"", {{"metric", "0", ""}});
+ // metric;desc=\\\ --> ''
+ testServerTimingHeader("metric;desc=\\\\\\", {{"metric", "0", ""}});
+ // metric;desc=\\" --> ''
+ testServerTimingHeader("metric;desc=\\\\\"", {{"metric", "0", ""}});
+ // metric;desc=\"\ --> ''
+ testServerTimingHeader("metric;desc=\\\"\\", {{"metric", "0", ""}});
+ // metric;desc=\"" --> ''
+ testServerTimingHeader("metric;desc=\\\"\"", {{"metric", "0", ""}});
+ // metric;desc="\\ --> ''
+ testServerTimingHeader("metric;desc=\"\\\\", {{"metric", "0", ""}});
+ // metric;desc="\" --> ''
+ testServerTimingHeader("metric;desc=\"\\\"", {{"metric", "0", ""}});
+ // metric;desc=""\ --> ''
+ testServerTimingHeader("metric;desc=\"\"\\", {{"metric", "0", ""}});
+ // metric;desc=""" --> ''
+ testServerTimingHeader("metric;desc=\"\"\"", {{"metric", "0", ""}});
+ // metric;desc=\\\\ --> ''
+ testServerTimingHeader("metric;desc=\\\\\\\\", {{"metric", "0", ""}});
+ // metric;desc=\\\" --> ''
+ testServerTimingHeader("metric;desc=\\\\\\\"", {{"metric", "0", ""}});
+ // metric;desc=\\"\ --> ''
+ testServerTimingHeader("metric;desc=\\\\\"\\", {{"metric", "0", ""}});
+ // metric;desc=\\"" --> ''
+ testServerTimingHeader("metric;desc=\\\\\"\"", {{"metric", "0", ""}});
+ // metric;desc=\"\\ --> ''
+ testServerTimingHeader("metric;desc=\\\"\\\\", {{"metric", "0", ""}});
+ // metric;desc=\"\" --> ''
+ testServerTimingHeader("metric;desc=\\\"\\\"", {{"metric", "0", ""}});
+ // metric;desc=\""\ --> ''
+ testServerTimingHeader("metric;desc=\\\"\"\\", {{"metric", "0", ""}});
+ // metric;desc=\""" --> ''
+ testServerTimingHeader("metric;desc=\\\"\"\"", {{"metric", "0", ""}});
+ // metric;desc="\\\ --> ''
+ testServerTimingHeader("metric;desc=\"\\\\\\", {{"metric", "0", ""}});
+ // metric;desc="\\" --> '\'
+ testServerTimingHeader("metric;desc=\"\\\\\"", {{"metric", "0", "\\"}});
+ // metric;desc="\"\ --> ''
+ testServerTimingHeader("metric;desc=\"\\\"\\", {{"metric", "0", ""}});
+ // metric;desc="\"" --> '"'
+ testServerTimingHeader("metric;desc=\"\\\"\"", {{"metric", "0", "\""}});
+ // metric;desc=""\\ --> ''
+ testServerTimingHeader("metric;desc=\"\"\\\\", {{"metric", "0", ""}});
+ // metric;desc=""\" --> ''
+ testServerTimingHeader("metric;desc=\"\"\\\"", {{"metric", "0", ""}});
+ // metric;desc="""\ --> ''
+ testServerTimingHeader("metric;desc=\"\"\"\\", {{"metric", "0", ""}});
+ // metric;desc="""" --> ''
+ testServerTimingHeader("metric;desc=\"\"\"\"", {{"metric", "0", ""}});
+
+ // duplicate entry names
+ testServerTimingHeader(
+ "metric;dur=12.3;desc=description1,metric;dur=45.6;desc=description2",
+ {{"metric", "12.3", "description1"}, {"metric", "45.6", "description2"}});
+
+ // param name case sensitivity
+ testServerTimingHeader("metric;DuR=123.4;DeSc=description",
+ {{"metric", "123.4", "description"}});
+
+ // non-numeric durations
+ testServerTimingHeader("metric;dur=foo", {{"metric", "0", ""}});
+ testServerTimingHeader("metric;dur=\"foo\"", {{"metric", "0", ""}});
+
+ // unrecognized param names
+ testServerTimingHeader(
+ "metric1;foo=bar;desc=description;foo=bar;dur=123.4;foo=bar,metric2",
+ {{"metric1", "123.4", "description"}, {"metric2", "0", ""}});
+
+ // duplicate param names
+ testServerTimingHeader("metric;dur=123.4;dur=567.8",
+ {{"metric", "123.4", ""}});
+ testServerTimingHeader("metric;dur=foo;dur=567.8", {{"metric", "0", ""}});
+ testServerTimingHeader("metric;desc=description1;desc=description2",
+ {{"metric", "0", "description1"}});
+
+ // incomplete params
+ testServerTimingHeader("metric;dur;dur=123.4;desc=description",
+ {{"metric", "0", "description"}});
+ testServerTimingHeader("metric;dur=;dur=123.4;desc=description",
+ {{"metric", "0", "description"}});
+ testServerTimingHeader("metric;desc;desc=description;dur=123.4",
+ {{"metric", "123.4", ""}});
+ testServerTimingHeader("metric;desc=;desc=description;dur=123.4",
+ {{"metric", "123.4", ""}});
+
+ // extraneous characters after param value as token
+ testServerTimingHeader("metric;desc=d1 d2;dur=123.4",
+ {{"metric", "123.4", "d1"}});
+ testServerTimingHeader("metric1;desc=d1 d2,metric2",
+ {{"metric1", "0", "d1"}, {"metric2", "0", ""}});
+
+ // extraneous characters after param value as quoted-string
+ testServerTimingHeader("metric;desc=\"d1\" d2;dur=123.4",
+ {{"metric", "123.4", "d1"}});
+ testServerTimingHeader("metric1;desc=\"d1\" d2,metric2",
+ {{"metric1", "0", "d1"}, {"metric2", "0", ""}});
+
+ // nonsense - extraneous characters after entry name token
+ testServerTimingHeader("metric== \"\"foo;dur=123.4", {{"metric", "0", ""}});
+ testServerTimingHeader("metric1== \"\"foo,metric2", {{"metric1", "0", ""}});
+
+ // nonsense - extraneous characters after param name token
+ testServerTimingHeader("metric;dur foo=12", {{"metric", "0", ""}});
+ testServerTimingHeader("metric;foo dur=12", {{"metric", "0", ""}});
+
+ // nonsense - return zero entries
+ testServerTimingHeader(" ", {});
+ testServerTimingHeader("=", {});
+ testServerTimingHeader("[", {});
+ testServerTimingHeader("]", {});
+ testServerTimingHeader(";", {});
+ testServerTimingHeader(",", {});
+ testServerTimingHeader("=;", {});
+ testServerTimingHeader(";=", {});
+ testServerTimingHeader("=,", {});
+ testServerTimingHeader(",=", {});
+ testServerTimingHeader(";,", {});
+ testServerTimingHeader(",;", {});
+ testServerTimingHeader("=;,", {});
+
+ // TODO(cvazac) the following tests should actually NOT pass
+ // According to the definition of token/tchar
+ // (https://tools.ietf.org/html/rfc7230#appendix-B),
+ // HeaderFieldTokenizer.IsTokenCharacter is being too permissive for the
+ // following chars (decimal):
+ // 123 '{', 125 '}', and 127 (not defined)
+ testServerTimingHeader("{", {{"{", "0", ""}});
+ testServerTimingHeader("}", {{"}", "0", ""}});
+ testServerTimingHeader("{}", {{"{}", "0", ""}});
+ testServerTimingHeader("{\"foo\":\"bar\"},metric", {{"{", "0", ""}});
+}
+
+TEST(HTTPParsersTest, ParseContentTypeOptionsTest) {
+ struct {
+ const char* value;
+ ContentTypeOptionsDisposition result;
+ } cases[] = {{"nosniff", kContentTypeOptionsNosniff},
+ {"NOSNIFF", kContentTypeOptionsNosniff},
+ {"NOsniFF", kContentTypeOptionsNosniff},
+ {"nosniff, nosniff", kContentTypeOptionsNosniff},
+ {"nosniff, not-nosniff", kContentTypeOptionsNosniff},
+ {"nosniff, none", kContentTypeOptionsNosniff},
+ {" nosniff", kContentTypeOptionsNosniff},
+ {"NOSNIFF ", kContentTypeOptionsNosniff},
+ {" NOsniFF ", kContentTypeOptionsNosniff},
+ {" nosniff, nosniff", kContentTypeOptionsNosniff},
+ {"nosniff , not-nosniff", kContentTypeOptionsNosniff},
+ {" nosniff , none", kContentTypeOptionsNosniff},
+ {"", kContentTypeOptionsNone},
+ {"none", kContentTypeOptionsNone},
+ {"none, nosniff", kContentTypeOptionsNone}};
+ for (const auto& test : cases) {
+ SCOPED_TRACE(test.value);
+ EXPECT_EQ(test.result, ParseContentTypeOptionsHeader(test.value));
+ }
+}
+
+TEST(HTTPParsersTest, ParseXSSProtectionDirectives) {
+ // clang-format off
+ struct {
+ const char* input;
+ ReflectedXSSDisposition expected_result;
+ const char* expected_failure_reason;
+ unsigned expected_failure_position;
+ const char* expected_report_url;
+ unsigned line;
+ } tests[] = {
+
+#undef SUCC__
+#undef FAIL__
+#define SUCC__(a, b, d, e) { a, b, nullptr, d, e, __LINE__ }
+#define FAIL__(a, c, d) {a, kReflectedXSSInvalid, c, d, nullptr, __LINE__}
+
+ // Empty header.
+ SUCC__("", kReflectedXSSUnset, 0, nullptr),
+ SUCC__(" ", kReflectedXSSUnset, 0, nullptr),
+
+ // First positional parameter tests.
+ SUCC__("0", kAllowReflectedXSS, 0, nullptr),
+ SUCC__(" 0 ", kAllowReflectedXSS, 0, nullptr),
+ SUCC__(" 0 ;", kAllowReflectedXSS, 0, nullptr),
+ SUCC__("1", kFilterReflectedXSS, 0, nullptr),
+ SUCC__(" 1 ", kFilterReflectedXSS, 0, nullptr),
+ SUCC__(" 1 ;", kFilterReflectedXSS, 0, nullptr),
+ FAIL__(";", "expected token to be 0 or 1", 0),
+ FAIL__(";;", "expected token to be 0 or 1", 0),
+ FAIL__(";;;", "expected token to be 0 or 1", 0),
+ FAIL__(" ; ", "expected token to be 0 or 1", 0),
+ FAIL__(" ; ; ", "expected token to be 0 or 1", 0),
+ FAIL__("; ; ;", "expected token to be 0 or 1", 0),
+ FAIL__("2", "expected token to be 0 or 1", 0),
+ FAIL__(" 2 ", "expected token to be 0 or 1", 0),
+ FAIL__("-1", "expected token to be 0 or 1", 0),
+ FAIL__(" -1 ", "expected token to be 0 or 1", 0),
+ FAIL__("red", "expected token to be 0 or 1", 0),
+ FAIL__("12345678901234567", "expected token to be 0 or 1", 0),
+ FAIL__("1:", "expected semicolon", 1),
+ FAIL__("1 2", "expected semicolon", 2),
+ FAIL__("1; red", "unrecognized directive", 3),
+
+ // Don't allow quoted strings here.
+ FAIL__("\"", "expected token to be 0 or 1", 0),
+ FAIL__("\"0", "expected token to be 0 or 1", 0),
+ FAIL__("\"0\"", "expected token to be 0 or 1", 0),
+ FAIL__("\" 0 \"", "expected token to be 0 or 1", 0),
+ FAIL__("\" 0\";", "expected token to be 0 or 1", 0),
+ FAIL__("\" 0;\"", "expected token to be 0 or 1", 0),
+ FAIL__("\"1", "expected token to be 0 or 1", 0),
+ FAIL__("\"1\"", "expected token to be 0 or 1", 0),
+ FAIL__("\" 1 \"", "expected token to be 0 or 1", 0),
+ FAIL__("\" 1\";", "expected token to be 0 or 1", 0),
+ FAIL__("\" 1;\"", "expected token to be 0 or 1", 0),
+
+ // No other parameters unless enabled.
+ SUCC__("0; mode=block; report=http://u:p@x.com:n/f.x?q=3&v=%26#tag",
+ kAllowReflectedXSS, 0, nullptr),
+ SUCC__("0; all sorts of nonsense; \"here\"",
+ kAllowReflectedXSS, 0, nullptr),
+
+ // Mode parameter tests.
+ SUCC__("1;mode=block", kBlockReflectedXSS, 0, nullptr),
+ SUCC__("1; mode=block;", kBlockReflectedXSS, 0, nullptr),
+ SUCC__("1; mode=block; ", kBlockReflectedXSS, 0, nullptr),
+ SUCC__("1; mode=block ;", kBlockReflectedXSS, 0, nullptr),
+ SUCC__("1; mode= block;", kBlockReflectedXSS, 0, nullptr),
+ SUCC__("1; mode =block;", kBlockReflectedXSS, 0, nullptr),
+ FAIL__("1; mode", "expected equals sign", 7),
+ FAIL__("1; mode=", "invalid mode directive", 8),
+ FAIL__("1; mode =", "invalid mode directive", 9),
+ FAIL__("1; mode=purple", "invalid mode directive", 8),
+ FAIL__("1; mode=block-a-block-block", "invalid mode directive", 8),
+ FAIL__("1; mode=block=a-block-block", "expected semicolon", 13),
+ FAIL__("1; mode=block; mode=block", "duplicate mode directive", 15),
+ FAIL__("1; mode=block; report=foo; mode=block;",
+ "duplicate mode directive", 27),
+
+ // Quoted strings allowed here.
+ SUCC__("1; mode=\"block\"", kBlockReflectedXSS, 0, nullptr),
+ SUCC__("1; mode=\"block\";", kBlockReflectedXSS, 0, nullptr),
+ FAIL__("1; mode=\"block;\"", "invalid mode directive", 8),
+ FAIL__("1; mode=\"red\"", "invalid mode directive", 8),
+
+ // Report url parameter tests.
+ SUCC__("1;report=http://u:p@x.com:n/f.x?q=3&v=%26#tag",
+ kFilterReflectedXSS, 9, "http://u:p@x.com:n/f.x?q=3&v=%26#tag"),
+ SUCC__("1; report=http://u:p@x.com:n/f.x?q=3&v=%26#tag",
+ kFilterReflectedXSS, 10, "http://u:p@x.com:n/f.x?q=3&v=%26#tag"),
+ SUCC__("1; report=http://u:p@x.com:n/f.x?q=3&v=%26#tag ",
+ kFilterReflectedXSS, 10, "http://u:p@x.com:n/f.x?q=3&v=%26#tag"),
+ SUCC__("1; report= http://u:p@x.com:n/f.x?q=3&v=%26#tag",
+ kFilterReflectedXSS, 11, "http://u:p@x.com:n/f.x?q=3&v=%26#tag"),
+ SUCC__("1; report =http://u:p@x.com:n/f.x?q=3&v=%26#tag",
+ kFilterReflectedXSS, 11, "http://u:p@x.com:n/f.x?q=3&v=%26#tag"),
+ SUCC__("1; report=http://u:p@x.com:n/f.x?q=3&v=%26#tag;",
+ kFilterReflectedXSS, 10, "http://u:p@x.com:n/f.x?q=3&v=%26#tag"),
+ SUCC__("1; mode=block; report=http://u:p@x.com:n/f.x?q=3&v=%26#tag",
+ kBlockReflectedXSS, 22, "http://u:p@x.com:n/f.x?q=3&v=%26#tag"),
+ SUCC__("1; report=http://u:p@x.com:n/f.x?q=3&v=%26#tag; mode=block",
+ kBlockReflectedXSS, 10, "http://u:p@x.com:n/f.x?q=3&v=%26#tag"),
+ FAIL__("1; report", "expected equals sign", 9),
+ FAIL__("1; report=", "invalid report directive", 10),
+ FAIL__("1; report= ;", "invalid report directive", 11),
+ FAIL__("1; report=http://foo.com; mode=block; report=http://foo.com;",
+ "duplicate report directive", 38),
+ FAIL__("1; report=http://foo.com;SEQUID=7", "unrecognized directive", 25),
+ FAIL__("1; mode=block; report=http://foo.com;SEQUID=7",
+ "unrecognized directive", 37),
+
+ // Quoted strings allowed per https://crbug.com/825557
+ SUCC__("1; report=\"http://foo.com\"", kFilterReflectedXSS, 10,
+ "http://foo.com"),
+ SUCC__("1; report=\"http://foo.com;SEQUID=7\"", kFilterReflectedXSS, 10,
+ "http://foo.com;SEQUID=7"),
+ FAIL__("1; report=\"", "invalid report directive", 10),
+ FAIL__("1; report=\"http://foo.com", "invalid report directive", 10),
+ FAIL__("1; report=\"http://foo.com\";SEQUID=7",
+ "unrecognized directive", 27),
+
+#undef SUCC__
+#undef FAIL__
+ };
+ // clang-format on
+
+ for (const auto& test : tests) {
+ String report_url;
+ String failure_reason;
+ unsigned failure_position = 0;
+ ReflectedXSSDisposition result = ParseXSSProtectionHeader(
+ test.input, failure_reason, failure_position, report_url);
+ EXPECT_EQ(test.expected_result, result)
+ << "for testcase at line " << test.line;
+ if (result == kReflectedXSSInvalid) {
+ EXPECT_EQ(test.expected_failure_reason, failure_reason)
+ << "for testcase at line " << test.line;
+ } else {
+ EXPECT_EQ(test.expected_report_url, report_url)
+ << "for testcase at line " << test.line;
+ }
+ EXPECT_EQ(test.expected_failure_position, failure_position)
+ << "for testcase at line " << test.line;
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/network/mime/content_type.cc b/chromium/third_party/blink/renderer/platform/network/mime/content_type.cc
new file mode 100644
index 00000000000..a3ddcda5792
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/mime/content_type.cc
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2008 Torch Mobile Inc. All rights reserved.
+ * (http://www.torchmobile.com/)
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/network/mime/content_type.h"
+
+namespace blink {
+
+ContentType::ContentType(const String& content_type) : type_(content_type) {}
+
+String ContentType::Parameter(const String& parameter_name) const {
+ String parameter_value;
+ String stripped_type = type_.StripWhiteSpace();
+
+ // a MIME type can have one or more "param=value" after a semi-colon, and
+ // separated from each other by semi-colons
+ size_t semi = stripped_type.find(';');
+ if (semi != kNotFound) {
+ size_t start =
+ stripped_type.FindIgnoringASCIICase(parameter_name, semi + 1);
+ if (start != kNotFound) {
+ start = stripped_type.find('=', start + parameter_name.length());
+ if (start != kNotFound) {
+ size_t quote = stripped_type.find('\"', start + 1);
+ size_t end = stripped_type.find('\"', start + 2);
+ if (quote != kNotFound && end != kNotFound) {
+ start = quote;
+ } else {
+ end = stripped_type.find(';', start + 1);
+ if (end == kNotFound)
+ end = stripped_type.length();
+ }
+ parameter_value = stripped_type.Substring(start + 1, end - (start + 1))
+ .StripWhiteSpace();
+ }
+ }
+ }
+
+ return parameter_value;
+}
+
+String ContentType::GetType() const {
+ String stripped_type = type_.StripWhiteSpace();
+
+ // "type" can have parameters after a semi-colon, strip them
+ size_t semi = stripped_type.find(';');
+ if (semi != kNotFound)
+ stripped_type = stripped_type.Left(semi).StripWhiteSpace();
+
+ return stripped_type;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/network/mime/content_type.h b/chromium/third_party/blink/renderer/platform/network/mime/content_type.h
new file mode 100644
index 00000000000..212aece4b51
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/mime/content_type.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2006 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_MIME_CONTENT_TYPE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_MIME_CONTENT_TYPE_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+// Parses the MIME type and parameter values based on RFC 2231.
+class PLATFORM_EXPORT ContentType {
+ STACK_ALLOCATED();
+
+ public:
+ explicit ContentType(const String& type);
+
+ String Parameter(const String& parameter_name) const;
+ String GetType() const;
+ const String& Raw() const { return type_; }
+
+ private:
+ String type_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_MIME_CONTENT_TYPE_H_
diff --git a/chromium/third_party/blink/renderer/platform/network/mime/mime_type_from_url.cc b/chromium/third_party/blink/renderer/platform/network/mime/mime_type_from_url.cc
new file mode 100644
index 00000000000..e1f5c1ae3f8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/mime/mime_type_from_url.cc
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2011, 2012 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/network/mime/mime_type_from_url.h"
+
+#include "third_party/blink/renderer/platform/network/mime/mime_type_registry.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+String MimeTypeFromDataURL(const String& url) {
+ DCHECK(ProtocolIs(url, "data"));
+ size_t index = url.find(';');
+ if (index == kNotFound)
+ index = url.find(',');
+ if (index != kNotFound) {
+ if (index > 5)
+ return url.Substring(5, index - 5).DeprecatedLower();
+ // Data URLs with no MIME type are considered text/plain.
+ return "text/plain";
+ }
+ return "";
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/network/mime/mime_type_from_url.h b/chromium/third_party/blink/renderer/platform/network/mime/mime_type_from_url.h
new file mode 100644
index 00000000000..da0121358e7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/mime/mime_type_from_url.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2011, 2012 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_MIME_MIME_TYPE_FROM_URL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_MIME_MIME_TYPE_FROM_URL_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+PLATFORM_EXPORT String MimeTypeFromDataURL(const String& url);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_MIME_MIME_TYPE_FROM_URL_H_
diff --git a/chromium/third_party/blink/renderer/platform/network/mime/mime_type_registry.cc b/chromium/third_party/blink/renderer/platform/network/mime/mime_type_registry.cc
new file mode 100644
index 00000000000..4b669292989
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/mime/mime_type_registry.cc
@@ -0,0 +1,203 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/network/mime/mime_type_registry.h"
+
+#include "base/files/file_path.h"
+#include "base/strings/string_util.h"
+#include "media/base/mime_util.h"
+#include "media/filters/stream_parser_factory.h"
+#include "net/base/mime_util.h"
+#include "third_party/blink/public/common/mime_util/mime_util.h"
+#include "third_party/blink/public/platform/file_path_conversion.h"
+#include "third_party/blink/public/platform/interface_provider.h"
+#include "third_party/blink/public/platform/mime_registry.mojom-blink.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+namespace {
+
+struct MimeRegistryPtrHolder {
+ public:
+ MimeRegistryPtrHolder() {
+ Platform::Current()->GetInterfaceProvider()->GetInterface(
+ mojo::MakeRequest(&mime_registry));
+ }
+ ~MimeRegistryPtrHolder() = default;
+
+ mojom::blink::MimeRegistryPtr mime_registry;
+};
+
+std::string ToASCIIOrEmpty(const WebString& string) {
+ return string.ContainsOnlyASCII() ? string.Ascii() : std::string();
+}
+
+template <typename CHARTYPE, typename SIZETYPE>
+std::string ToLowerASCIIInternal(CHARTYPE* str, SIZETYPE length) {
+ std::string lower_ascii;
+ lower_ascii.reserve(length);
+ for (CHARTYPE* p = str; p < str + length; p++)
+ lower_ascii.push_back(base::ToLowerASCII(static_cast<char>(*p)));
+ return lower_ascii;
+}
+
+// Does the same as ToASCIIOrEmpty, but also makes the chars lower.
+std::string ToLowerASCIIOrEmpty(const String& str) {
+ if (str.IsEmpty() || !str.ContainsOnlyASCII())
+ return std::string();
+ if (str.Is8Bit())
+ return ToLowerASCIIInternal(str.Characters8(), str.length());
+ return ToLowerASCIIInternal(str.Characters16(), str.length());
+}
+
+STATIC_ASSERT_ENUM(MIMETypeRegistry::kIsNotSupported, media::IsNotSupported);
+STATIC_ASSERT_ENUM(MIMETypeRegistry::kIsSupported, media::IsSupported);
+STATIC_ASSERT_ENUM(MIMETypeRegistry::kMayBeSupported, media::MayBeSupported);
+
+} // namespace
+
+String MIMETypeRegistry::GetMIMETypeForExtension(const String& ext) {
+ // The sandbox restricts our access to the registry, so we need to proxy
+ // these calls over to the browser process.
+ DEFINE_STATIC_LOCAL(MimeRegistryPtrHolder, registry_holder, ());
+ String mime_type;
+ if (!registry_holder.mime_registry->GetMimeTypeFromExtension(
+ ext.IsNull() ? "" : ext, &mime_type)) {
+ return String();
+ }
+ return mime_type;
+}
+
+String MIMETypeRegistry::GetWellKnownMIMETypeForExtension(const String& ext) {
+ // This method must be thread safe and should not consult the OS/registry.
+ std::string mime_type;
+ net::GetWellKnownMimeTypeFromExtension(WebStringToFilePath(ext).value(),
+ &mime_type);
+ return String::FromUTF8(mime_type.data(), mime_type.length());
+}
+
+bool MIMETypeRegistry::IsSupportedMIMEType(const String& mime_type) {
+ return blink::IsSupportedMimeType(ToLowerASCIIOrEmpty(mime_type));
+}
+
+bool MIMETypeRegistry::IsSupportedImageMIMEType(const String& mime_type) {
+ return blink::IsSupportedImageMimeType(ToLowerASCIIOrEmpty(mime_type));
+}
+
+bool MIMETypeRegistry::IsSupportedImageResourceMIMEType(
+ const String& mime_type) {
+ return IsSupportedImageMIMEType(mime_type);
+}
+
+bool MIMETypeRegistry::IsSupportedImagePrefixedMIMEType(
+ const String& mime_type) {
+ std::string ascii_mime_type = ToLowerASCIIOrEmpty(mime_type);
+ return (blink::IsSupportedImageMimeType(ascii_mime_type) ||
+ (base::StartsWith(ascii_mime_type, "image/",
+ base::CompareCase::SENSITIVE) &&
+ blink::IsSupportedNonImageMimeType(ascii_mime_type)));
+}
+
+bool MIMETypeRegistry::IsSupportedImageMIMETypeForEncoding(
+ const String& mime_type) {
+ if (DeprecatedEqualIgnoringCase(mime_type, "image/jpeg") ||
+ DeprecatedEqualIgnoringCase(mime_type, "image/png"))
+ return true;
+ if (DeprecatedEqualIgnoringCase(mime_type, "image/webp"))
+ return true;
+ return false;
+}
+
+bool MIMETypeRegistry::IsSupportedJavaScriptMIMEType(const String& mime_type) {
+ return blink::IsSupportedJavascriptMimeType(ToLowerASCIIOrEmpty(mime_type));
+}
+
+bool MIMETypeRegistry::IsLegacySupportedJavaScriptLanguage(
+ const String& language) {
+ // Mozilla 1.8 accepts javascript1.0 - javascript1.7, but WinIE 7 accepts only
+ // javascript1.1 - javascript1.3.
+ // Mozilla 1.8 and WinIE 7 both accept javascript and livescript.
+ // WinIE 7 accepts ecmascript and jscript, but Mozilla 1.8 doesn't.
+ // Neither Mozilla 1.8 nor WinIE 7 accept leading or trailing whitespace.
+ // We want to accept all the values that either of these browsers accept, but
+ // not other values.
+
+ // FIXME: This function is not HTML5 compliant. These belong in the MIME
+ // registry as "text/javascript<version>" entries.
+ return EqualIgnoringASCIICase(language, "javascript") ||
+ EqualIgnoringASCIICase(language, "javascript1.0") ||
+ EqualIgnoringASCIICase(language, "javascript1.1") ||
+ EqualIgnoringASCIICase(language, "javascript1.2") ||
+ EqualIgnoringASCIICase(language, "javascript1.3") ||
+ EqualIgnoringASCIICase(language, "javascript1.4") ||
+ EqualIgnoringASCIICase(language, "javascript1.5") ||
+ EqualIgnoringASCIICase(language, "javascript1.6") ||
+ EqualIgnoringASCIICase(language, "javascript1.7") ||
+ EqualIgnoringASCIICase(language, "livescript") ||
+ EqualIgnoringASCIICase(language, "ecmascript") ||
+ EqualIgnoringASCIICase(language, "jscript");
+}
+
+bool MIMETypeRegistry::IsSupportedNonImageMIMEType(const String& mime_type) {
+ return blink::IsSupportedNonImageMimeType(ToLowerASCIIOrEmpty(mime_type));
+}
+
+bool MIMETypeRegistry::IsSupportedMediaMIMEType(const String& mime_type,
+ const String& codecs) {
+ return SupportsMediaMIMEType(mime_type, codecs) != kIsNotSupported;
+}
+
+MIMETypeRegistry::SupportsType MIMETypeRegistry::SupportsMediaMIMEType(
+ const String& mime_type,
+ const String& codecs) {
+ const std::string ascii_mime_type = ToLowerASCIIOrEmpty(mime_type);
+ std::vector<std::string> codec_vector;
+ media::SplitCodecsToVector(ToASCIIOrEmpty(codecs), &codec_vector, false);
+ return static_cast<SupportsType>(
+ media::IsSupportedMediaFormat(ascii_mime_type, codec_vector));
+}
+
+bool MIMETypeRegistry::IsSupportedMediaSourceMIMEType(const String& mime_type,
+ const String& codecs) {
+ const std::string ascii_mime_type = ToLowerASCIIOrEmpty(mime_type);
+ if (ascii_mime_type.empty())
+ return false;
+ std::vector<std::string> parsed_codec_ids;
+ media::SplitCodecsToVector(ToASCIIOrEmpty(codecs), &parsed_codec_ids, false);
+ return static_cast<MIMETypeRegistry::SupportsType>(
+ media::StreamParserFactory::IsTypeSupported(ascii_mime_type,
+ parsed_codec_ids));
+}
+
+bool MIMETypeRegistry::IsJavaAppletMIMEType(const String& mime_type) {
+ // Since this set is very limited and is likely to remain so we won't bother
+ // with the overhead of using a hash set. Any of the MIME types below may be
+ // followed by any number of specific versions of the JVM, which is why we use
+ // startsWith()
+ return mime_type.StartsWithIgnoringASCIICase("application/x-java-applet") ||
+ mime_type.StartsWithIgnoringASCIICase("application/x-java-bean") ||
+ mime_type.StartsWithIgnoringASCIICase("application/x-java-vm");
+}
+
+bool MIMETypeRegistry::IsSupportedStyleSheetMIMEType(const String& mime_type) {
+ return DeprecatedEqualIgnoringCase(mime_type, "text/css");
+}
+
+bool MIMETypeRegistry::IsSupportedFontMIMEType(const String& mime_type) {
+ static const unsigned kFontLen = 5;
+ if (!mime_type.StartsWithIgnoringASCIICase("font/"))
+ return false;
+ String sub_type = mime_type.Substring(kFontLen).DeprecatedLower();
+ return sub_type == "woff" || sub_type == "woff2" || sub_type == "otf" ||
+ sub_type == "ttf" || sub_type == "sfnt";
+}
+
+bool MIMETypeRegistry::IsSupportedTextTrackMIMEType(const String& mime_type) {
+ return DeprecatedEqualIgnoringCase(mime_type, "text/vtt");
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/network/mime/mime_type_registry.h b/chromium/third_party/blink/renderer/platform/network/mime/mime_type_registry.h
new file mode 100644
index 00000000000..c487cec6891
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/mime/mime_type_registry.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2006 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_MIME_MIME_TYPE_REGISTRY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_MIME_MIME_TYPE_REGISTRY_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+// Note/reminder: MIME type and parameter names are per-RFC case
+// insensitive (https://www.ietf.org/rfc/rfc2045.txt , section 5.1).
+// The MIMETypeRegistry predicates are all case-insensitive.
+class PLATFORM_EXPORT MIMETypeRegistry {
+ STATIC_ONLY(MIMETypeRegistry);
+
+ public:
+ // For Media MIME type checks.
+ enum SupportsType { kIsNotSupported, kIsSupported, kMayBeSupported };
+
+ static String GetMIMETypeForExtension(const String& extension);
+ static String GetWellKnownMIMETypeForExtension(const String& extension);
+
+ // Checks to see if the given mime type is supported.
+ static bool IsSupportedMIMEType(const String& mime_type);
+
+ // Checks to see if a mime type is suitable for being loaded inline as an
+ // image (e.g., <img> tags).
+ static bool IsSupportedImageMIMEType(const String& mime_type);
+
+ // Checks to see if a mime type is suitable for being loaded as an image
+ // document in a frame.
+ static bool IsSupportedImageResourceMIMEType(const String& mime_type);
+
+ // Checks to see if a mime type is suitable for being displayed as an image.
+ static bool IsSupportedImagePrefixedMIMEType(const String& mime_type);
+
+ // Checks to see if a mime type is suitable for being encoded.
+ static bool IsSupportedImageMIMETypeForEncoding(const String& mime_type);
+
+ // Checks to see if a mime type is suitable for being loaded as a JavaScript
+ // resource.
+ static bool IsSupportedJavaScriptMIMEType(const String& mime_type);
+
+ static bool IsLegacySupportedJavaScriptLanguage(const String& language);
+
+ // Checks to see if a non-image mime type is suitable for being loaded as a
+ // document in a frame. Includes supported JavaScript MIME types.
+ static bool IsSupportedNonImageMIMEType(const String& mime_type);
+
+ // Checks to see if the mime type and codecs are supported media MIME types.
+ static bool IsSupportedMediaMIMEType(const String& mime_type,
+ const String& codecs);
+
+ // Does similar to isSupportedMediaMIMEType, but returns a little more
+ // detailed information in SupportsType enum.
+ static SupportsType SupportsMediaMIMEType(const String& mime_type,
+ const String& codecs);
+
+ // Checks to see if the mime type and codecs are supported by the MediaSource
+ // implementation.
+ static bool IsSupportedMediaSourceMIMEType(const String& mime_type,
+ const String& codecs);
+
+ // Checks to see if a mime type is a valid Java applet mime type
+ static bool IsJavaAppletMIMEType(const String& mime_type);
+
+ // Checks to see if a mime type is suitable for being loaded as a stylesheet.
+ static bool IsSupportedStyleSheetMIMEType(const String& mime_type);
+
+ // Checks to see if a mime type is suitable for being loaded as a font.
+ static bool IsSupportedFontMIMEType(const String& mime_type);
+
+ // Checks to see if a mime type is suitable for being loaded as a text track.
+ static bool IsSupportedTextTrackMIMEType(const String& mime_type);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_MIME_MIME_TYPE_REGISTRY_H_
diff --git a/chromium/third_party/blink/renderer/platform/network/mime/mime_type_registry_test.cc b/chromium/third_party/blink/renderer/platform/network/mime/mime_type_registry_test.cc
new file mode 100644
index 00000000000..5fa398dd155
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/mime/mime_type_registry_test.cc
@@ -0,0 +1,37 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/network/mime/mime_type_registry.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+TEST(MIMETypeRegistryTest, MimeTypeTest) {
+ EXPECT_TRUE(MIMETypeRegistry::IsSupportedImagePrefixedMIMEType("image/gif"));
+ EXPECT_TRUE(MIMETypeRegistry::IsSupportedImageResourceMIMEType("image/gif"));
+ EXPECT_TRUE(MIMETypeRegistry::IsSupportedImagePrefixedMIMEType("Image/Gif"));
+ EXPECT_TRUE(MIMETypeRegistry::IsSupportedImageResourceMIMEType("Image/Gif"));
+ static const UChar kUpper16[] = {0x0049, 0x006d, 0x0061, 0x0067,
+ 0x0065, 0x002f, 0x0067, 0x0069,
+ 0x0066, 0}; // Image/gif in UTF16
+ EXPECT_TRUE(
+ MIMETypeRegistry::IsSupportedImagePrefixedMIMEType(String(kUpper16)));
+ EXPECT_TRUE(
+ MIMETypeRegistry::IsSupportedImagePrefixedMIMEType("image/svg+xml"));
+ EXPECT_FALSE(
+ MIMETypeRegistry::IsSupportedImageResourceMIMEType("image/svg+xml"));
+}
+
+TEST(MIMETypeRegistryTest, PluginMimeTypes) {
+ // Since we've removed MIME type guessing based on plugin-declared file
+ // extensions, ensure that the MIMETypeRegistry already contains
+ // the extensions used by common PPAPI plugins.
+ EXPECT_EQ("application/pdf",
+ MIMETypeRegistry::GetWellKnownMIMETypeForExtension("pdf").Utf8());
+ EXPECT_EQ("application/x-shockwave-flash",
+ MIMETypeRegistry::GetWellKnownMIMETypeForExtension("swf").Utf8());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/network/mime/mock_mime_registry.h b/chromium/third_party/blink/renderer/platform/network/mime/mock_mime_registry.h
new file mode 100644
index 00000000000..0316b92bfe0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/mime/mock_mime_registry.h
@@ -0,0 +1,32 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_MIME_MOCK_MIME_REGISTRY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_MIME_MOCK_MIME_REGISTRY_H_
+
+#include "net/base/mime_util.h"
+#include "third_party/blink/public/platform/file_path_conversion.h"
+#include "third_party/blink/public/platform/mime_registry.mojom-blink.h"
+
+namespace blink {
+
+// Used for unit tests.
+class MockMimeRegistry : public mojom::blink::MimeRegistry {
+ public:
+ MockMimeRegistry() = default;
+ ~MockMimeRegistry() = default;
+
+ void GetMimeTypeFromExtension(
+ const String& ext,
+ GetMimeTypeFromExtensionCallback callback) override {
+ std::string mime_type;
+ net::GetMimeTypeFromExtension(WebStringToFilePath(ext).value(), &mime_type);
+ std::move(callback).Run(
+ String::FromUTF8(mime_type.data(), mime_type.length()));
+ }
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_MIME_MOCK_MIME_REGISTRY_H_
diff --git a/chromium/third_party/blink/renderer/platform/network/network_hints.cc b/chromium/third_party/blink/renderer/platform/network/network_hints.cc
new file mode 100644
index 00000000000..6164e7c1e86
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/network_hints.cc
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2008 Collin Jackson <collinj@webkit.org>
+ * Copyright (C) 2013 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/network/network_hints.h"
+
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_prescient_networking.h"
+
+namespace blink {
+
+void PrefetchDNS(const String& hostname) {
+ if (WebPrescientNetworking* prescient_networking =
+ Platform::Current()->PrescientNetworking())
+ prescient_networking->PrefetchDNS(hostname);
+}
+
+void Preconnect(const KURL& url, const CrossOriginAttributeValue cross_origin) {
+ if (WebPrescientNetworking* prescient_networking =
+ Platform::Current()->PrescientNetworking()) {
+ bool allow_credentials = (cross_origin != kCrossOriginAttributeAnonymous);
+ prescient_networking->Preconnect(url, allow_credentials);
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/network/network_hints.h b/chromium/third_party/blink/renderer/platform/network/network_hints.h
new file mode 100644
index 00000000000..9316ad5cc13
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/network_hints.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2008 Collin Jackson <collinj@webkit.org>
+ * Copyright (C) 2013 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_NETWORK_HINTS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_NETWORK_HINTS_H_
+
+#include "third_party/blink/renderer/platform/cross_origin_attribute_value.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+PLATFORM_EXPORT void PrefetchDNS(const String& hostname);
+PLATFORM_EXPORT void Preconnect(const KURL&, const CrossOriginAttributeValue);
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/network/network_instrumentation.cc b/chromium/third_party/blink/renderer/platform/network/network_instrumentation.cc
new file mode 100644
index 00000000000..1c0e4a23a82
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/network_instrumentation.cc
@@ -0,0 +1,99 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/network/network_instrumentation.h"
+
+#include "base/trace_event/trace_event.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/traced_value.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_load_priority.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
+
+namespace blink {
+namespace network_instrumentation {
+
+using network_instrumentation::RequestOutcome;
+using blink::TracedValue;
+
+const char kBlinkResourceID[] = "BlinkResourceID";
+const char kResourceLoadTitle[] = "ResourceLoad";
+const char kResourcePrioritySetTitle[] = "ResourcePrioritySet";
+const char kNetInstrumentationCategory[] = TRACE_DISABLED_BY_DEFAULT("network");
+
+const char* RequestOutcomeToString(RequestOutcome outcome) {
+ switch (outcome) {
+ case RequestOutcome::kSuccess:
+ return "Success";
+ case RequestOutcome::kFail:
+ return "Fail";
+ default:
+ NOTREACHED();
+ // We need to return something to avoid compiler warning.
+ return "This should never happen";
+ }
+}
+
+// Note: network_instrumentation code should do as much work as possible inside
+// the arguments of trace macros so that very little instrumentation overhead is
+// incurred if the trace category is disabled. See https://crbug.com/669666.
+
+namespace {
+
+std::unique_ptr<TracedValue> ScopedResourceTrackerBeginData(
+ const blink::ResourceRequest& request) {
+ std::unique_ptr<TracedValue> data = TracedValue::Create();
+ data->SetString("url", request.Url().GetString());
+ return data;
+}
+
+std::unique_ptr<TracedValue> ResourcePrioritySetData(
+ blink::ResourceLoadPriority priority) {
+ std::unique_ptr<TracedValue> data = TracedValue::Create();
+ data->SetInteger("priority", static_cast<int>(priority));
+ return data;
+}
+
+std::unique_ptr<TracedValue> EndResourceLoadData(RequestOutcome outcome) {
+ std::unique_ptr<TracedValue> data = TracedValue::Create();
+ data->SetString("outcome", RequestOutcomeToString(outcome));
+ return data;
+}
+
+} // namespace
+
+ScopedResourceLoadTracker::ScopedResourceLoadTracker(
+ unsigned long resource_id,
+ const blink::ResourceRequest& request)
+ : resource_load_continues_beyond_scope_(false), resource_id_(resource_id) {
+ TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(
+ kNetInstrumentationCategory, kResourceLoadTitle,
+ TRACE_ID_WITH_SCOPE(kBlinkResourceID, TRACE_ID_LOCAL(resource_id)),
+ "beginData", ScopedResourceTrackerBeginData(request));
+}
+
+ScopedResourceLoadTracker::~ScopedResourceLoadTracker() {
+ if (!resource_load_continues_beyond_scope_)
+ EndResourceLoad(resource_id_, RequestOutcome::kFail);
+}
+
+void ScopedResourceLoadTracker::ResourceLoadContinuesBeyondScope() {
+ resource_load_continues_beyond_scope_ = true;
+}
+
+void ResourcePrioritySet(unsigned long resource_id,
+ blink::ResourceLoadPriority priority) {
+ TRACE_EVENT_NESTABLE_ASYNC_INSTANT1(
+ kNetInstrumentationCategory, kResourcePrioritySetTitle,
+ TRACE_ID_WITH_SCOPE(kBlinkResourceID, TRACE_ID_LOCAL(resource_id)),
+ "data", ResourcePrioritySetData(priority));
+}
+
+void EndResourceLoad(unsigned long resource_id, RequestOutcome outcome) {
+ TRACE_EVENT_NESTABLE_ASYNC_END1(
+ kNetInstrumentationCategory, kResourceLoadTitle,
+ TRACE_ID_WITH_SCOPE(kBlinkResourceID, TRACE_ID_LOCAL(resource_id)),
+ "endData", EndResourceLoadData(outcome));
+}
+
+} // namespace network_instrumentation
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/network/network_instrumentation.h b/chromium/third_party/blink/renderer/platform/network/network_instrumentation.h
new file mode 100644
index 00000000000..0c4f3873842
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/network_instrumentation.h
@@ -0,0 +1,43 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_NETWORK_INSTRUMENTATION_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_NETWORK_INSTRUMENTATION_H_
+
+#include "third_party/blink/renderer/platform/loader/fetch/resource_load_priority.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+
+class ResourceRequest;
+
+namespace network_instrumentation {
+
+enum RequestOutcome { kSuccess, kFail };
+
+class PLATFORM_EXPORT ScopedResourceLoadTracker {
+ public:
+ ScopedResourceLoadTracker(unsigned long resource_id,
+ const blink::ResourceRequest&);
+ ~ScopedResourceLoadTracker();
+ void ResourceLoadContinuesBeyondScope();
+
+ private:
+ // If this variable is false, close resource load slice at end of scope.
+ bool resource_load_continues_beyond_scope_;
+
+ const unsigned long resource_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedResourceLoadTracker);
+};
+
+void PLATFORM_EXPORT ResourcePrioritySet(unsigned long resource_id,
+ blink::ResourceLoadPriority);
+
+void PLATFORM_EXPORT EndResourceLoad(unsigned long resource_id, RequestOutcome);
+
+} // namespace network_instrumentation
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_NETWORK_INSTRUMENTATION_H_
diff --git a/chromium/third_party/blink/renderer/platform/network/network_log.h b/chromium/third_party/blink/renderer/platform/network/network_log.h
new file mode 100644
index 00000000000..1c0399991fd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/network_log.h
@@ -0,0 +1,20 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_NETWORK_LOG_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_NETWORK_LOG_H_
+
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+#if DCHECK_IS_ON()
+// We can see logs with |--v=N| or |--vmodule=NetworkLog=N| where N is a
+// verbose level.
+#define NETWORK_DVLOG(verbose_level) \
+ LAZY_STREAM(VLOG_STREAM(verbose_level), \
+ ((verbose_level) <= ::logging::GetVlogLevel("NetworkLog.h")))
+#else
+#define NETWORK_DVLOG(verbose_level) EAT_STREAM_PARAMETERS
+#endif
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_NETWORK_LOG_H_
diff --git a/chromium/third_party/blink/renderer/platform/network/network_state_notifier.cc b/chromium/third_party/blink/renderer/platform/network/network_state_notifier.cc
new file mode 100644
index 00000000000..6a36df128fe
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/network_state_notifier.cc
@@ -0,0 +1,450 @@
+/*
+ * Copyright (C) 2008 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 "third_party/blink/renderer/platform/network/network_state_notifier.h"
+
+#include <memory>
+#include "net/nqe/effective_connection_type.h"
+#include "net/nqe/network_quality_estimator_params.h"
+#include "third_party/blink/public/common/client_hints/client_hints.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/web_task_runner.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/threading.h"
+
+namespace blink {
+
+template <>
+struct CrossThreadCopier<NetworkStateNotifier::NetworkState>
+ : public CrossThreadCopierPassThrough<NetworkStateNotifier::NetworkState> {
+ STATIC_ONLY(CrossThreadCopier);
+};
+
+NetworkStateNotifier& GetNetworkStateNotifier() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(NetworkStateNotifier, network_state_notifier,
+ ());
+ return network_state_notifier;
+}
+
+NetworkStateNotifier::ScopedNotifier::ScopedNotifier(
+ NetworkStateNotifier& notifier)
+ : notifier_(notifier) {
+ DCHECK(IsMainThread());
+ before_ = notifier_.has_override_ ? notifier_.override_ : notifier_.state_;
+}
+
+NetworkStateNotifier::ScopedNotifier::~ScopedNotifier() {
+ DCHECK(IsMainThread());
+ const NetworkState& after =
+ notifier_.has_override_ ? notifier_.override_ : notifier_.state_;
+ if ((after.type != before_.type ||
+ after.max_bandwidth_mbps != before_.max_bandwidth_mbps ||
+ after.effective_type != before_.effective_type ||
+ after.http_rtt != before_.http_rtt ||
+ after.transport_rtt != before_.transport_rtt ||
+ after.downlink_throughput_mbps != before_.downlink_throughput_mbps ||
+ after.save_data != before_.save_data) &&
+ before_.connection_initialized) {
+ notifier_.NotifyObservers(notifier_.connection_observers_,
+ ObserverType::kConnectionType, after);
+ }
+ if (after.on_line != before_.on_line && before_.on_line_initialized) {
+ notifier_.NotifyObservers(notifier_.on_line_state_observers_,
+ ObserverType::kOnLineState, after);
+ }
+}
+
+NetworkStateNotifier::NetworkStateObserverHandle::NetworkStateObserverHandle(
+ NetworkStateNotifier* notifier,
+ NetworkStateNotifier::ObserverType type,
+ NetworkStateNotifier::NetworkStateObserver* observer,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+ : notifier_(notifier),
+ type_(type),
+ observer_(observer),
+ task_runner_(std::move(task_runner)) {}
+
+NetworkStateNotifier::NetworkStateObserverHandle::
+ ~NetworkStateObserverHandle() {
+ notifier_->RemoveObserver(type_, observer_, std::move(task_runner_));
+}
+
+void NetworkStateNotifier::SetOnLine(bool on_line) {
+ DCHECK(IsMainThread());
+ ScopedNotifier notifier(*this);
+ {
+ MutexLocker locker(mutex_);
+ state_.on_line_initialized = true;
+ state_.on_line = on_line;
+ }
+}
+
+void NetworkStateNotifier::SetWebConnection(WebConnectionType type,
+ double max_bandwidth_mbps) {
+ DCHECK(IsMainThread());
+ ScopedNotifier notifier(*this);
+ {
+ MutexLocker locker(mutex_);
+ state_.connection_initialized = true;
+ state_.type = type;
+ state_.max_bandwidth_mbps = max_bandwidth_mbps;
+ }
+}
+
+void NetworkStateNotifier::SetNetworkQuality(WebEffectiveConnectionType type,
+ TimeDelta http_rtt,
+ TimeDelta transport_rtt,
+ int downlink_throughput_kbps) {
+ DCHECK(IsMainThread());
+ ScopedNotifier notifier(*this);
+ {
+ MutexLocker locker(mutex_);
+
+ state_.effective_type = type;
+ state_.http_rtt = base::nullopt;
+ state_.transport_rtt = base::nullopt;
+ state_.downlink_throughput_mbps = base::nullopt;
+
+ if (http_rtt.InMilliseconds() >= 0)
+ state_.http_rtt = http_rtt;
+
+ if (transport_rtt.InMilliseconds() >= 0)
+ state_.transport_rtt = transport_rtt;
+
+ if (downlink_throughput_kbps >= 0) {
+ state_.downlink_throughput_mbps =
+ static_cast<double>(downlink_throughput_kbps) / 1000;
+ }
+ }
+}
+
+std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle>
+NetworkStateNotifier::AddConnectionObserver(
+ NetworkStateObserver* observer,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+ AddObserverToMap(connection_observers_, observer, task_runner);
+ return std::make_unique<NetworkStateNotifier::NetworkStateObserverHandle>(
+ this, ObserverType::kConnectionType, observer, task_runner);
+}
+
+void NetworkStateNotifier::SetSaveDataEnabled(bool enabled) {
+ DCHECK(IsMainThread());
+ ScopedNotifier notifier(*this);
+ {
+ MutexLocker locker(mutex_);
+ state_.save_data = enabled;
+ }
+}
+
+std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle>
+NetworkStateNotifier::AddOnLineObserver(
+ NetworkStateObserver* observer,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+ AddObserverToMap(on_line_state_observers_, observer, task_runner);
+ return std::make_unique<NetworkStateNotifier::NetworkStateObserverHandle>(
+ this, ObserverType::kOnLineState, observer, task_runner);
+}
+
+void NetworkStateNotifier::SetNetworkConnectionInfoOverride(
+ bool on_line,
+ WebConnectionType type,
+ Optional<WebEffectiveConnectionType> effective_type,
+ unsigned long http_rtt_msec,
+ double max_bandwidth_mbps) {
+ DCHECK(IsMainThread());
+ ScopedNotifier notifier(*this);
+ {
+ MutexLocker locker(mutex_);
+ has_override_ = true;
+ override_.on_line_initialized = true;
+ override_.on_line = on_line;
+ override_.connection_initialized = true;
+ override_.type = type;
+ override_.max_bandwidth_mbps = max_bandwidth_mbps;
+
+ if (!effective_type && http_rtt_msec > 0) {
+ base::TimeDelta http_rtt(TimeDelta::FromMilliseconds(http_rtt_msec));
+ // Threshold values taken from
+ // net/nqe/network_quality_estimator_params.cc.
+ if (http_rtt >= net::kHttpRttEffectiveConnectionTypeThresholds
+ [net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G]) {
+ effective_type = WebEffectiveConnectionType::kTypeSlow2G;
+ } else if (http_rtt >= net::kHttpRttEffectiveConnectionTypeThresholds
+ [net::EFFECTIVE_CONNECTION_TYPE_2G]) {
+ effective_type = WebEffectiveConnectionType::kType2G;
+ } else if (http_rtt >= net::kHttpRttEffectiveConnectionTypeThresholds
+ [net::EFFECTIVE_CONNECTION_TYPE_3G]) {
+ effective_type = WebEffectiveConnectionType::kType3G;
+ } else {
+ effective_type = WebEffectiveConnectionType::kType4G;
+ }
+ }
+ override_.effective_type = effective_type
+ ? effective_type.value()
+ : WebEffectiveConnectionType::kTypeUnknown;
+ override_.http_rtt = TimeDelta::FromMilliseconds(http_rtt_msec);
+ override_.downlink_throughput_mbps = max_bandwidth_mbps;
+ }
+}
+
+void NetworkStateNotifier::SetSaveDataEnabledOverride(bool enabled) {
+ DCHECK(IsMainThread());
+ ScopedNotifier notifier(*this);
+ {
+ MutexLocker locker(mutex_);
+ has_override_ = true;
+ override_.on_line_initialized = true;
+ override_.connection_initialized = true;
+ override_.save_data = enabled;
+ }
+}
+
+void NetworkStateNotifier::ClearOverride() {
+ DCHECK(IsMainThread());
+ ScopedNotifier notifier(*this);
+ {
+ MutexLocker locker(mutex_);
+ has_override_ = false;
+ }
+}
+
+void NetworkStateNotifier::NotifyObservers(ObserverListMap& map,
+ ObserverType type,
+ const NetworkState& state) {
+ DCHECK(IsMainThread());
+ MutexLocker locker(mutex_);
+ for (const auto& entry : map) {
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner = entry.key;
+ PostCrossThreadTask(
+ *task_runner, FROM_HERE,
+ CrossThreadBind(&NetworkStateNotifier::NotifyObserversOnTaskRunner,
+ CrossThreadUnretained(this),
+ CrossThreadUnretained(&map), type, task_runner, state));
+ }
+}
+
+void NetworkStateNotifier::NotifyObserversOnTaskRunner(
+ ObserverListMap* map,
+ ObserverType type,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+ const NetworkState& state) {
+ ObserverList* observer_list = LockAndFindObserverList(*map, task_runner);
+
+ // The context could have been removed before the notification task got to
+ // run.
+ if (!observer_list)
+ return;
+
+ DCHECK(task_runner->RunsTasksInCurrentSequence());
+
+ observer_list->iterating = true;
+
+ for (size_t i = 0; i < observer_list->observers.size(); ++i) {
+ // Observers removed during iteration are zeroed out, skip them.
+ if (!observer_list->observers[i])
+ continue;
+ switch (type) {
+ case ObserverType::kOnLineState:
+ observer_list->observers[i]->OnLineStateChange(state.on_line);
+ continue;
+ case ObserverType::kConnectionType:
+ observer_list->observers[i]->ConnectionChange(
+ state.type, state.max_bandwidth_mbps, state.effective_type,
+ state.http_rtt, state.transport_rtt, state.downlink_throughput_mbps,
+ state.save_data);
+ continue;
+ }
+ NOTREACHED();
+ }
+
+ observer_list->iterating = false;
+
+ if (!observer_list->zeroed_observers.IsEmpty())
+ CollectZeroedObservers(*map, observer_list, std::move(task_runner));
+}
+
+void NetworkStateNotifier::AddObserverToMap(
+ ObserverListMap& map,
+ NetworkStateObserver* observer,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+ DCHECK(task_runner->RunsTasksInCurrentSequence());
+ DCHECK(observer);
+
+ MutexLocker locker(mutex_);
+ ObserverListMap::AddResult result =
+ map.insert(std::move(task_runner), nullptr);
+ if (result.is_new_entry)
+ result.stored_value->value = std::make_unique<ObserverList>();
+
+ DCHECK(result.stored_value->value->observers.Find(observer) == kNotFound);
+ result.stored_value->value->observers.push_back(observer);
+}
+
+void NetworkStateNotifier::RemoveObserver(
+ ObserverType type,
+ NetworkStateObserver* observer,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+ switch (type) {
+ case ObserverType::kConnectionType:
+ RemoveObserverFromMap(connection_observers_, observer,
+ std::move(task_runner));
+ break;
+ case ObserverType::kOnLineState:
+ RemoveObserverFromMap(on_line_state_observers_, observer,
+ std::move(task_runner));
+ break;
+ }
+}
+
+void NetworkStateNotifier::RemoveObserverFromMap(
+ ObserverListMap& map,
+ NetworkStateObserver* observer,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+ DCHECK(task_runner->RunsTasksInCurrentSequence());
+ DCHECK(observer);
+
+ ObserverList* observer_list = LockAndFindObserverList(map, task_runner);
+ if (!observer_list)
+ return;
+
+ Vector<NetworkStateObserver*>& observers = observer_list->observers;
+ size_t index = observers.Find(observer);
+ if (index != kNotFound) {
+ observers[index] = 0;
+ observer_list->zeroed_observers.push_back(index);
+ }
+
+ if (!observer_list->iterating && !observer_list->zeroed_observers.IsEmpty())
+ CollectZeroedObservers(map, observer_list, std::move(task_runner));
+}
+
+NetworkStateNotifier::ObserverList*
+NetworkStateNotifier::LockAndFindObserverList(
+ ObserverListMap& map,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+ MutexLocker locker(mutex_);
+ ObserverListMap::iterator it = map.find(task_runner);
+ return it == map.end() ? nullptr : it->value.get();
+}
+
+void NetworkStateNotifier::CollectZeroedObservers(
+ ObserverListMap& map,
+ ObserverList* list,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+ DCHECK(task_runner->RunsTasksInCurrentSequence());
+ DCHECK(!list->iterating);
+
+ // If any observers were removed during the iteration they will have
+ // 0 values, clean them up.
+ for (size_t i = 0; i < list->zeroed_observers.size(); ++i)
+ list->observers.EraseAt(list->zeroed_observers[i]);
+
+ list->zeroed_observers.clear();
+
+ if (list->observers.IsEmpty()) {
+ MutexLocker locker(mutex_);
+ map.erase(task_runner); // deletes list
+ }
+}
+
+// static
+String NetworkStateNotifier::EffectiveConnectionTypeToString(
+ WebEffectiveConnectionType type) {
+ DCHECK_GT(kWebEffectiveConnectionTypeMappingCount, static_cast<size_t>(type));
+ return kWebEffectiveConnectionTypeMapping[static_cast<int>(type)];
+}
+
+double NetworkStateNotifier::GetRandomMultiplier(const String& host) const {
+ // The random number should be a function of the hostname to reduce
+ // cross-origin fingerprinting. The random number should also be a function
+ // of randomized salt which is known only to the device. This prevents
+ // origin from removing noise from the estimates.
+ if (!host)
+ return 1.0;
+
+ unsigned hash = StringHash::GetHash(host) + RandomizationSalt();
+ double random_multiplier = 0.9 + static_cast<double>((hash % 21)) * 0.01;
+ DCHECK_LE(0.90, random_multiplier);
+ DCHECK_GE(1.10, random_multiplier);
+ return random_multiplier;
+}
+
+unsigned long NetworkStateNotifier::RoundRtt(
+ const String& host,
+ const Optional<TimeDelta>& rtt) const {
+ // Limit the size of the buckets and the maximum reported value to reduce
+ // fingerprinting.
+ static const size_t kBucketSize = 50;
+ static const double kMaxRttMsec = 3.0 * 1000;
+
+ if (!rtt.has_value()) {
+ // RTT is unavailable. So, return the fastest value.
+ return 0;
+ }
+
+ double rtt_msec = static_cast<double>(rtt.value().InMilliseconds());
+ rtt_msec *= GetRandomMultiplier(host);
+ rtt_msec = std::min(rtt_msec, kMaxRttMsec);
+
+ DCHECK_LE(0, rtt_msec);
+ DCHECK_GE(kMaxRttMsec, rtt_msec);
+
+ // Round down to the nearest kBucketSize msec value.
+ return std::round(rtt_msec / kBucketSize) * kBucketSize;
+}
+
+double NetworkStateNotifier::RoundMbps(
+ const String& host,
+ const Optional<double>& downlink_mbps) const {
+ // Limit the size of the buckets and the maximum reported value to reduce
+ // fingerprinting.
+ static const size_t kBucketSize = 50;
+ static const double kMaxDownlinkKbps = 10.0 * 1000;
+
+ double downlink_kbps = 0;
+ if (!downlink_mbps.has_value()) {
+ // Throughput is unavailable. So, return the fastest value.
+ downlink_kbps = kMaxDownlinkKbps;
+ } else {
+ downlink_kbps = downlink_mbps.value() * 1000;
+ }
+ downlink_kbps *= GetRandomMultiplier(host);
+
+ downlink_kbps = std::min(downlink_kbps, kMaxDownlinkKbps);
+
+ DCHECK_LE(0, downlink_kbps);
+ DCHECK_GE(kMaxDownlinkKbps, downlink_kbps);
+ // Round down to the nearest kBucketSize kbps value.
+ double downlink_kbps_rounded =
+ std::round(downlink_kbps / kBucketSize) * kBucketSize;
+
+ // Convert from Kbps to Mbps.
+ return downlink_kbps_rounded / 1000;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/network/network_state_notifier.h b/chromium/third_party/blink/renderer/platform/network/network_state_notifier.h
new file mode 100644
index 00000000000..f5ccbb9054c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/network_state_notifier.h
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2008 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_NETWORK_STATE_NOTIFIER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_NETWORK_STATE_NOTIFIER_H_
+
+#include <memory>
+
+#include "base/rand_util.h"
+#include "base/single_thread_task_runner.h"
+#include "third_party/blink/public/platform/web_connection_type.h"
+#include "third_party/blink/public/platform/web_effective_connection_type.h"
+#include "third_party/blink/renderer/platform/cross_thread_copier.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT NetworkStateNotifier {
+ WTF_MAKE_NONCOPYABLE(NetworkStateNotifier);
+ USING_FAST_MALLOC(NetworkStateNotifier);
+
+ public:
+ struct NetworkState {
+ static const int kInvalidMaxBandwidth = -1;
+ bool on_line_initialized = false;
+ bool on_line = true;
+ bool connection_initialized = false;
+ WebConnectionType type = kWebConnectionTypeOther;
+ double max_bandwidth_mbps = kInvalidMaxBandwidth;
+ WebEffectiveConnectionType effective_type =
+ WebEffectiveConnectionType::kTypeUnknown;
+ Optional<TimeDelta> http_rtt;
+ Optional<TimeDelta> transport_rtt;
+ Optional<double> downlink_throughput_mbps;
+ bool save_data = false;
+ };
+
+ class NetworkStateObserver {
+ public:
+ // Will be called on the task runner that is passed in add*Observer.
+ virtual void ConnectionChange(
+ WebConnectionType,
+ double max_bandwidth_mbps,
+ WebEffectiveConnectionType,
+ const Optional<TimeDelta>& http_rtt,
+ const Optional<TimeDelta>& transport_rtt,
+ const Optional<double>& downlink_throughput_mbps,
+ bool save_data) {}
+ virtual void OnLineStateChange(bool on_line) {}
+ };
+
+ enum class ObserverType {
+ kOnLineState,
+ kConnectionType,
+ };
+
+ class PLATFORM_EXPORT NetworkStateObserverHandle {
+ USING_FAST_MALLOC(NetworkStateObserverHandle);
+
+ public:
+ NetworkStateObserverHandle(NetworkStateNotifier*,
+ ObserverType,
+ NetworkStateObserver*,
+ scoped_refptr<base::SingleThreadTaskRunner>);
+ ~NetworkStateObserverHandle();
+
+ private:
+ NetworkStateNotifier* notifier_;
+ ObserverType type_;
+ NetworkStateObserver* observer_;
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(NetworkStateObserverHandle);
+ };
+
+ NetworkStateNotifier() : has_override_(false) {}
+
+ ~NetworkStateNotifier() {
+ DCHECK(connection_observers_.IsEmpty());
+ DCHECK(on_line_state_observers_.IsEmpty());
+ }
+
+ // Can be called on any thread.
+ bool OnLine() const {
+ MutexLocker locker(mutex_);
+ const NetworkState& state = has_override_ ? override_ : state_;
+ DCHECK(state.on_line_initialized);
+ return state.on_line;
+ }
+
+ // Returns the current effective connection type, which is the connection type
+ // whose typical performance is most similar to the measured performance of
+ // the network in use.
+ WebEffectiveConnectionType EffectiveType() const {
+ MutexLocker locker(mutex_);
+ const NetworkState& state = has_override_ ? override_ : state_;
+ // TODO (tbansal): Add a DCHECK to check that |state.on_line_initialized| is
+ // true once https://crbug.com/728771 is fixed.
+ return state.effective_type;
+ }
+
+ // Returns the current HTTP RTT estimate. If the estimate is unavailable, the
+ // returned optional value is null.
+ Optional<TimeDelta> HttpRtt() const {
+ MutexLocker locker(mutex_);
+ const NetworkState& state = has_override_ ? override_ : state_;
+ // TODO (tbansal): Add a DCHECK to check that |state.on_line_initialized| is
+ // true once https://crbug.com/728771 is fixed.
+ return state.http_rtt;
+ }
+
+ // Returns the current transport RTT estimate. If the estimate is unavailable,
+ // the returned optional value is null.
+ Optional<TimeDelta> TransportRtt() const {
+ MutexLocker locker(mutex_);
+ const NetworkState& state = has_override_ ? override_ : state_;
+ DCHECK(state.on_line_initialized);
+ return state.transport_rtt;
+ }
+
+ // Returns the current throughput estimate (in megabits per second). If the
+ // estimate is unavailable, the returned optional value is null.
+ Optional<double> DownlinkThroughputMbps() const {
+ MutexLocker locker(mutex_);
+ const NetworkState& state = has_override_ ? override_ : state_;
+ // TODO (tbansal): Add a DCHECK to check that |state.on_line_initialized| is
+ // true once https://crbug.com/728771 is fixed.
+ return state.downlink_throughput_mbps;
+ }
+
+ bool SaveDataEnabled() const {
+ MutexLocker locker(mutex_);
+ const NetworkState& state = has_override_ ? override_ : state_;
+ // TODO (tbansal): Add a DCHECK to check that |state.on_line_initialized| is
+ // true once https://crbug.com/728771 is fixed.
+ return state.save_data;
+ }
+
+ void SetOnLine(bool);
+
+ // Can be called on any thread.
+ WebConnectionType ConnectionType() const {
+ MutexLocker locker(mutex_);
+ const NetworkState& state = has_override_ ? override_ : state_;
+ DCHECK(state.connection_initialized);
+ return state.type;
+ }
+
+ // Can be called on any thread.
+ bool IsCellularConnectionType() const {
+ switch (ConnectionType()) {
+ case kWebConnectionTypeCellular2G:
+ case kWebConnectionTypeCellular3G:
+ case kWebConnectionTypeCellular4G:
+ return true;
+ case kWebConnectionTypeBluetooth:
+ case kWebConnectionTypeEthernet:
+ case kWebConnectionTypeWifi:
+ case kWebConnectionTypeWimax:
+ case kWebConnectionTypeOther:
+ case kWebConnectionTypeNone:
+ case kWebConnectionTypeUnknown:
+ return false;
+ }
+ NOTREACHED();
+ return false;
+ }
+
+ // Can be called on any thread.
+ double MaxBandwidth() const {
+ MutexLocker locker(mutex_);
+ const NetworkState& state = has_override_ ? override_ : state_;
+ DCHECK(state.connection_initialized);
+ return state.max_bandwidth_mbps;
+ }
+
+ void SetWebConnection(WebConnectionType, double max_bandwidth_mbps);
+ void SetNetworkQuality(WebEffectiveConnectionType,
+ TimeDelta http_rtt,
+ TimeDelta transport_rtt,
+ int downlink_throughput_kbps);
+ void SetSaveDataEnabled(bool enabled);
+
+ // When called, successive setWebConnectionType/setOnLine calls are stored,
+ // and supplied overridden values are used instead until clearOverride() is
+ // called. This is used for layout tests (see crbug.com/377736) and inspector
+ // emulation.
+ // If |effective_type| is null, its value is computed using |http_rtt_msec|.
+ // |max_bandwidth_mbps| is used to override both the |max_bandwidth_mbps| and
+ // |downlink_throughput_mbps|.
+ //
+ // Since this class is a singleton, tests must clear override when completed
+ // to avoid indeterminate state across the test harness.
+ void SetNetworkConnectionInfoOverride(
+ bool on_line,
+ WebConnectionType,
+ Optional<WebEffectiveConnectionType> effective_type,
+ unsigned long http_rtt_msec,
+ double max_bandwidth_mbps);
+ void SetSaveDataEnabledOverride(bool enabled);
+ void ClearOverride();
+
+ // Must be called on the given task runner. An added observer must be removed
+ // before the observer or its execution context goes away. It's possible for
+ // an observer to be called twice for the same event if it is first removed
+ // and then added during notification.
+ std::unique_ptr<NetworkStateObserverHandle> AddConnectionObserver(
+ NetworkStateObserver*,
+ scoped_refptr<base::SingleThreadTaskRunner>);
+ std::unique_ptr<NetworkStateObserverHandle> AddOnLineObserver(
+ NetworkStateObserver*,
+ scoped_refptr<base::SingleThreadTaskRunner>);
+
+ // Returns the String equivalent for a given WebEffectiveCOnnectionType.
+ static String EffectiveConnectionTypeToString(WebEffectiveConnectionType);
+
+ // Returns |rtt| after adding host-specific random noise, and rounding it as
+ // per the NetInfo spec to improve privacy.
+ unsigned long RoundRtt(const String& host,
+ const Optional<TimeDelta>& rtt) const;
+
+ // Returns |downlink_mbps| after adding host-specific random noise, and
+ // rounding it as per the NetInfo spec and to improve privacy.
+ double RoundMbps(const String& host,
+ const Optional<double>& downlink_mbps) const;
+
+ // Returns the randomization salt (weak and insecure) that should be used when
+ // adding noise to the network quality metrics. This is known only to the
+ // device, and is generated only once. This makes it possible to add the same
+ // amount of noise for a given origin.
+ uint8_t RandomizationSalt() const { return randomization_salt_; }
+
+ private:
+ friend class NetworkStateObserverHandle;
+
+ struct ObserverList {
+ ObserverList() : iterating(false) {}
+ bool iterating;
+ Vector<NetworkStateObserver*> observers;
+ Vector<size_t> zeroed_observers; // Indices in observers that are 0.
+ };
+
+ // This helper scope issues required notifications when mutating the state if
+ // something has changed. It's only possible to mutate the state on the main
+ // thread. Note that ScopedNotifier must be destroyed when not holding a lock
+ // so that onLine notifications can be dispatched without a deadlock.
+ class ScopedNotifier {
+ public:
+ explicit ScopedNotifier(NetworkStateNotifier&);
+ ~ScopedNotifier();
+
+ private:
+ NetworkStateNotifier& notifier_;
+ NetworkState before_;
+ };
+
+ // The ObserverListMap is cross-thread accessed, adding/removing Observers
+ // running on a task runner.
+ using ObserverListMap = HashMap<scoped_refptr<base::SingleThreadTaskRunner>,
+ std::unique_ptr<ObserverList>>;
+
+ void NotifyObservers(ObserverListMap&, ObserverType, const NetworkState&);
+ void NotifyObserversOnTaskRunner(ObserverListMap*,
+ ObserverType,
+ scoped_refptr<base::SingleThreadTaskRunner>,
+ const NetworkState&);
+
+ void AddObserverToMap(ObserverListMap&,
+ NetworkStateObserver*,
+ scoped_refptr<base::SingleThreadTaskRunner>);
+ void RemoveObserver(ObserverType,
+ NetworkStateObserver*,
+ scoped_refptr<base::SingleThreadTaskRunner>);
+ void RemoveObserverFromMap(ObserverListMap&,
+ NetworkStateObserver*,
+ scoped_refptr<base::SingleThreadTaskRunner>);
+
+ ObserverList* LockAndFindObserverList(
+ ObserverListMap&,
+ scoped_refptr<base::SingleThreadTaskRunner>);
+
+ // Removed observers are nulled out in the list in case the list is being
+ // iterated over. Once done iterating, call this to clean up nulled
+ // observers.
+ void CollectZeroedObservers(ObserverListMap&,
+ ObserverList*,
+ scoped_refptr<base::SingleThreadTaskRunner>);
+
+ // A random number by which the RTT and downlink estimates are multiplied
+ // with. The returned random multiplier is a function of the hostname.
+ // Adding this noise reduces the chances of cross-origin fingerprinting.
+ double GetRandomMultiplier(const String& host) const;
+
+ mutable Mutex mutex_;
+ NetworkState state_;
+ bool has_override_;
+ NetworkState override_;
+
+ ObserverListMap connection_observers_;
+ ObserverListMap on_line_state_observers_;
+
+ const uint8_t randomization_salt_ = base::RandInt(1, 20);
+};
+
+PLATFORM_EXPORT NetworkStateNotifier& GetNetworkStateNotifier();
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_NETWORK_STATE_NOTIFIER_H_
diff --git a/chromium/third_party/blink/renderer/platform/network/network_state_notifier_test.cc b/chromium/third_party/blink/renderer/platform/network/network_state_notifier_test.cc
new file mode 100644
index 00000000000..a059d747ce5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/network_state_notifier_test.cc
@@ -0,0 +1,965 @@
+/*
+ * Copyright (c) 2014, 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 "third_party/blink/renderer/platform/network/network_state_notifier.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_connection_type.h"
+#include "third_party/blink/public/platform/web_effective_connection_type.h"
+#include "third_party/blink/public/platform/web_thread.h"
+#include "third_party/blink/renderer/platform/scheduler/test/fake_task_runner.h"
+#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+using scheduler::FakeTaskRunner;
+
+namespace {
+const double kNoneMaxBandwidthMbps = 0.0;
+const double kBluetoothMaxBandwidthMbps = 1.0;
+const double kEthernetMaxBandwidthMbps = 2.0;
+const Optional<TimeDelta> kEthernetHttpRtt(TimeDelta::FromMilliseconds(50));
+const Optional<TimeDelta> kEthernetTransportRtt(
+ TimeDelta::FromMilliseconds(25));
+const Optional<double> kEthernetThroughputMbps(75.0);
+const Optional<TimeDelta> kUnknownRtt;
+const Optional<double> kUnknownThroughputMbps;
+
+enum class SaveData {
+ kOff = 0,
+ kOn = 1,
+};
+
+} // namespace
+
+class StateObserver : public NetworkStateNotifier::NetworkStateObserver {
+ public:
+ StateObserver()
+ : observed_type_(kWebConnectionTypeNone),
+ observed_max_bandwidth_mbps_(0.0),
+ observed_effective_type_(WebEffectiveConnectionType::kTypeUnknown),
+ observed_http_rtt_(kUnknownRtt),
+ observed_transport_rtt_(kUnknownRtt),
+ observed_downlink_throughput_mbps_(kUnknownThroughputMbps),
+ observed_on_line_state_(false),
+ observed_save_data_(SaveData::kOff),
+ callback_count_(0) {}
+
+ virtual void ConnectionChange(
+ WebConnectionType type,
+ double max_bandwidth_mbps,
+ WebEffectiveConnectionType effective_type,
+ const Optional<TimeDelta>& http_rtt,
+ const Optional<TimeDelta>& transport_rtt,
+ const Optional<double>& downlink_throughput_mbps,
+ bool save_data) {
+ observed_type_ = type;
+ observed_max_bandwidth_mbps_ = max_bandwidth_mbps;
+ observed_effective_type_ = effective_type;
+ observed_http_rtt_ = http_rtt;
+ observed_transport_rtt_ = transport_rtt;
+ observed_downlink_throughput_mbps_ = downlink_throughput_mbps;
+ observed_save_data_ = save_data ? SaveData::kOn : SaveData::kOff;
+ callback_count_ += 1;
+
+ if (closure_)
+ std::move(closure_).Run();
+ }
+
+ virtual void OnLineStateChange(bool on_line) {
+ observed_on_line_state_ = on_line;
+ callback_count_ += 1;
+
+ if (closure_)
+ std::move(closure_).Run();
+ }
+
+ WebConnectionType ObservedType() const { return observed_type_; }
+ double ObservedMaxBandwidth() const { return observed_max_bandwidth_mbps_; }
+ WebEffectiveConnectionType ObservedEffectiveType() const {
+ return observed_effective_type_;
+ }
+ Optional<TimeDelta> ObservedHttpRtt() const { return observed_http_rtt_; }
+ Optional<TimeDelta> ObservedTransportRtt() const {
+ return observed_transport_rtt_;
+ }
+ Optional<double> ObservedDownlinkThroughputMbps() const {
+ return observed_downlink_throughput_mbps_;
+ }
+ bool ObservedOnLineState() const { return observed_on_line_state_; }
+ SaveData ObservedSaveData() const { return observed_save_data_; }
+ int CallbackCount() const { return callback_count_; }
+
+ void AddObserverOnNotification(
+ NetworkStateNotifier* notifier,
+ StateObserver* observer_to_add,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+ closure_ = base::BindOnce(
+ [](StateObserver* observer, NetworkStateNotifier* notifier,
+ StateObserver* observer_to_add,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+ observer->added_handle_ =
+ notifier->AddConnectionObserver(observer_to_add, task_runner);
+ },
+ base::Unretained(this), base::Unretained(notifier),
+ base::Unretained(observer_to_add), task_runner);
+ }
+
+ void RemoveObserverOnNotification(
+ std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle>
+ handle) {
+ closure_ = base::BindOnce(
+ [](std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle>
+ handle) {},
+ std::move(handle));
+ }
+
+ private:
+ base::OnceClosure closure_;
+ WebConnectionType observed_type_;
+ double observed_max_bandwidth_mbps_;
+ WebEffectiveConnectionType observed_effective_type_;
+ Optional<TimeDelta> observed_http_rtt_;
+ Optional<TimeDelta> observed_transport_rtt_;
+ Optional<double> observed_downlink_throughput_mbps_;
+ bool observed_on_line_state_;
+ SaveData observed_save_data_;
+ int callback_count_;
+ std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle>
+ added_handle_;
+};
+
+class NetworkStateNotifierTest : public testing::Test {
+ public:
+ NetworkStateNotifierTest()
+ : task_runner_(base::MakeRefCounted<FakeTaskRunner>()),
+ task_runner2_(base::MakeRefCounted<FakeTaskRunner>()) {
+ // Initialize connection, so that future calls to setWebConnection issue
+ // notifications.
+ notifier_.SetWebConnection(kWebConnectionTypeUnknown, 0.0);
+ notifier_.SetOnLine(false);
+ }
+
+ base::SingleThreadTaskRunner* GetTaskRunner() { return task_runner_.get(); }
+ base::SingleThreadTaskRunner* GetTaskRunner2() { return task_runner2_.get(); }
+
+ void TearDown() override {
+ // NetworkStateNotifier class is a singleton, so clear the override to avoid
+ // indeterminate state across the test harness.
+ notifier_.ClearOverride();
+ RunPendingTasks();
+ task_runner_ = nullptr;
+ task_runner2_ = nullptr;
+ }
+
+ protected:
+ void RunPendingTasks() {
+ task_runner_->RunUntilIdle();
+ task_runner2_->RunUntilIdle();
+ }
+
+ void SetConnection(WebConnectionType type,
+ double max_bandwidth_mbps,
+ WebEffectiveConnectionType effective_type,
+ const Optional<TimeDelta>& http_rtt,
+ const Optional<TimeDelta>& transport_rtt,
+ const Optional<double>& downlink_throughput_mbps,
+ SaveData save_data) {
+ notifier_.SetWebConnection(type, max_bandwidth_mbps);
+ notifier_.SetNetworkQuality(
+ effective_type,
+ http_rtt.has_value() ? http_rtt.value()
+ : base::TimeDelta::FromMilliseconds(-1),
+ transport_rtt.has_value() ? transport_rtt.value()
+ : base::TimeDelta::FromMilliseconds(-1),
+ downlink_throughput_mbps.has_value()
+ ? downlink_throughput_mbps.value() * 1000
+ : -1);
+ notifier_.SetSaveDataEnabled(save_data == SaveData::kOn);
+ RunPendingTasks();
+ }
+ void SetOnLine(bool on_line) {
+ notifier_.SetOnLine(on_line);
+ RunPendingTasks();
+ }
+
+ bool VerifyObservations(const StateObserver& observer,
+ WebConnectionType type,
+ double max_bandwidth_mbps,
+ WebEffectiveConnectionType effective_type,
+ const Optional<TimeDelta>& http_rtt,
+ const Optional<TimeDelta>& transport_rtt,
+ const Optional<double>& downlink_throughput_mbps,
+ SaveData save_data) const {
+ EXPECT_EQ(type, observer.ObservedType());
+ EXPECT_EQ(max_bandwidth_mbps, observer.ObservedMaxBandwidth());
+ EXPECT_EQ(effective_type, observer.ObservedEffectiveType());
+ EXPECT_EQ(http_rtt, observer.ObservedHttpRtt());
+ EXPECT_EQ(transport_rtt, observer.ObservedTransportRtt());
+ EXPECT_EQ(downlink_throughput_mbps,
+ observer.ObservedDownlinkThroughputMbps());
+ EXPECT_EQ(save_data, observer.ObservedSaveData());
+
+ return observer.ObservedType() == type &&
+ observer.ObservedMaxBandwidth() == max_bandwidth_mbps &&
+ observer.ObservedEffectiveType() == effective_type &&
+ observer.ObservedHttpRtt() == http_rtt &&
+ observer.ObservedTransportRtt() == transport_rtt &&
+ observer.ObservedDownlinkThroughputMbps() ==
+ downlink_throughput_mbps &&
+ observer.ObservedSaveData() == save_data;
+ }
+
+ scoped_refptr<FakeTaskRunner> task_runner_;
+ scoped_refptr<FakeTaskRunner> task_runner2_;
+ NetworkStateNotifier notifier_;
+};
+
+TEST_F(NetworkStateNotifierTest, AddObserver) {
+ StateObserver observer;
+ std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle> handle =
+ notifier_.AddConnectionObserver(&observer, GetTaskRunner());
+ EXPECT_TRUE(VerifyObservations(
+ observer, kWebConnectionTypeNone, kNoneMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt, kUnknownRtt,
+ kUnknownThroughputMbps, SaveData::kOff));
+
+ // Change max. bandwidth and the network quality estimates.
+ SetConnection(kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType3G, kEthernetHttpRtt,
+ kEthernetTransportRtt, kEthernetThroughputMbps, SaveData::kOff);
+ EXPECT_TRUE(VerifyObservations(
+ observer, kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType3G, kEthernetHttpRtt,
+ kEthernetTransportRtt, kEthernetThroughputMbps, SaveData::kOff));
+ EXPECT_EQ(observer.CallbackCount(), 2);
+
+ // Only change the connection type.
+ SetConnection(kWebConnectionTypeEthernet, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType3G, kEthernetHttpRtt,
+ kEthernetTransportRtt, kEthernetThroughputMbps, SaveData::kOff);
+ EXPECT_TRUE(VerifyObservations(
+ observer, kWebConnectionTypeEthernet, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType3G, kEthernetHttpRtt,
+ kEthernetTransportRtt, kEthernetThroughputMbps, SaveData::kOff));
+ EXPECT_EQ(observer.CallbackCount(), 3);
+
+ // Only change the max. bandwidth.
+ SetConnection(kWebConnectionTypeEthernet, kEthernetMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType3G, kEthernetHttpRtt,
+ kEthernetTransportRtt, kEthernetThroughputMbps, SaveData::kOff);
+ EXPECT_TRUE(VerifyObservations(
+ observer, kWebConnectionTypeEthernet, kEthernetMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType3G, kEthernetHttpRtt,
+ kEthernetTransportRtt, kEthernetThroughputMbps, SaveData::kOff));
+ EXPECT_EQ(observer.CallbackCount(), 4);
+
+ // Only change the transport RTT.
+ SetConnection(kWebConnectionTypeEthernet, kEthernetMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType3G, kEthernetHttpRtt,
+ kEthernetTransportRtt.value() * 2, kEthernetThroughputMbps,
+ SaveData::kOff);
+ EXPECT_TRUE(VerifyObservations(
+ observer, kWebConnectionTypeEthernet, kEthernetMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType3G, kEthernetHttpRtt,
+ kEthernetTransportRtt.value() * 2, kEthernetThroughputMbps,
+ SaveData::kOff));
+ EXPECT_EQ(observer.CallbackCount(), 5);
+
+ // Only change the effective connection type.
+ SetConnection(kWebConnectionTypeEthernet, kEthernetMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType4G, kEthernetHttpRtt,
+ kEthernetTransportRtt.value() * 2, kEthernetThroughputMbps,
+ SaveData::kOff);
+ EXPECT_TRUE(VerifyObservations(
+ observer, kWebConnectionTypeEthernet, kEthernetMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType4G, kEthernetHttpRtt,
+ kEthernetTransportRtt.value() * 2, kEthernetThroughputMbps,
+ SaveData::kOff));
+ EXPECT_EQ(observer.CallbackCount(), 6);
+
+ // Only change the save data.
+ SetConnection(kWebConnectionTypeEthernet, kEthernetMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType4G, kEthernetHttpRtt,
+ kEthernetTransportRtt.value() * 2, kEthernetThroughputMbps,
+ SaveData::kOn);
+ EXPECT_TRUE(VerifyObservations(
+ observer, kWebConnectionTypeEthernet, kEthernetMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType4G, kEthernetHttpRtt,
+ kEthernetTransportRtt.value() * 2, kEthernetThroughputMbps,
+ SaveData::kOn));
+ EXPECT_EQ(observer.CallbackCount(), 7);
+}
+
+TEST_F(NetworkStateNotifierTest, RemoveObserver) {
+ StateObserver observer1, observer2;
+ std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle> handle1 =
+ notifier_.AddConnectionObserver(&observer1, GetTaskRunner());
+ handle1 = nullptr;
+ std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle> handle2 =
+ notifier_.AddConnectionObserver(&observer2, GetTaskRunner());
+
+ SetConnection(kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType3G, kEthernetHttpRtt,
+ kEthernetTransportRtt, kEthernetThroughputMbps, SaveData::kOff);
+
+ EXPECT_TRUE(VerifyObservations(
+ observer1, kWebConnectionTypeNone, kNoneMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt, kUnknownRtt,
+ kUnknownThroughputMbps, SaveData::kOff));
+ EXPECT_TRUE(VerifyObservations(
+ observer2, kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType3G, kEthernetHttpRtt,
+ kEthernetTransportRtt, kEthernetThroughputMbps, SaveData::kOff));
+}
+
+TEST_F(NetworkStateNotifierTest, RemoveSoleObserver) {
+ StateObserver observer1;
+ std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle> handle =
+ notifier_.AddConnectionObserver(&observer1, GetTaskRunner());
+ handle = nullptr;
+
+ SetConnection(kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType3G, kEthernetHttpRtt,
+ kEthernetTransportRtt, kEthernetThroughputMbps, SaveData::kOff);
+ EXPECT_TRUE(VerifyObservations(
+ observer1, kWebConnectionTypeNone, kNoneMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt, kUnknownRtt,
+ kUnknownThroughputMbps, SaveData::kOff));
+}
+
+TEST_F(NetworkStateNotifierTest, AddObserverWhileNotifying) {
+ StateObserver observer1, observer2;
+ std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle> handle =
+ notifier_.AddConnectionObserver(&observer1, GetTaskRunner());
+ observer1.AddObserverOnNotification(&notifier_, &observer2, GetTaskRunner());
+
+ SetConnection(kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt,
+ kUnknownRtt, kUnknownThroughputMbps, SaveData::kOff);
+ EXPECT_TRUE(VerifyObservations(
+ observer1, kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt, kUnknownRtt,
+ kUnknownThroughputMbps, SaveData::kOff));
+ EXPECT_TRUE(VerifyObservations(
+ observer2, kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt, kUnknownRtt,
+ kUnknownThroughputMbps, SaveData::kOff));
+}
+
+TEST_F(NetworkStateNotifierTest, RemoveSoleObserverWhileNotifying) {
+ StateObserver observer1;
+ std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle> handle =
+ notifier_.AddConnectionObserver(&observer1, GetTaskRunner());
+ observer1.RemoveObserverOnNotification(std::move(handle));
+
+ SetConnection(kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt,
+ kUnknownRtt, kUnknownThroughputMbps, SaveData::kOff);
+ EXPECT_TRUE(VerifyObservations(
+ observer1, kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt, kUnknownRtt,
+ kUnknownThroughputMbps, SaveData::kOff));
+
+ SetConnection(kWebConnectionTypeEthernet, kEthernetMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt,
+ kUnknownRtt, kUnknownThroughputMbps, SaveData::kOff);
+ EXPECT_TRUE(VerifyObservations(
+ observer1, kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt, kUnknownRtt,
+ kUnknownThroughputMbps, SaveData::kOff));
+}
+
+TEST_F(NetworkStateNotifierTest, RemoveCurrentObserverWhileNotifying) {
+ StateObserver observer1, observer2;
+ std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle> handle1 =
+ notifier_.AddConnectionObserver(&observer1, GetTaskRunner());
+ std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle> handle2 =
+ notifier_.AddConnectionObserver(&observer2, GetTaskRunner());
+ observer1.RemoveObserverOnNotification(std::move(handle1));
+
+ SetConnection(kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt,
+ kUnknownRtt, kUnknownThroughputMbps, SaveData::kOff);
+ EXPECT_TRUE(VerifyObservations(
+ observer1, kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt, kUnknownRtt,
+ kUnknownThroughputMbps, SaveData::kOff));
+ EXPECT_TRUE(VerifyObservations(
+ observer2, kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt, kUnknownRtt,
+ kUnknownThroughputMbps, SaveData::kOff));
+
+ SetConnection(kWebConnectionTypeEthernet, kEthernetMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt,
+ kUnknownRtt, kUnknownThroughputMbps, SaveData::kOff);
+ EXPECT_TRUE(VerifyObservations(
+ observer1, kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt, kUnknownRtt,
+ kUnknownThroughputMbps, SaveData::kOff));
+ EXPECT_TRUE(VerifyObservations(
+ observer2, kWebConnectionTypeEthernet, kEthernetMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt, kUnknownRtt,
+ kUnknownThroughputMbps, SaveData::kOff));
+}
+
+TEST_F(NetworkStateNotifierTest, RemovePastObserverWhileNotifying) {
+ StateObserver observer1, observer2;
+ std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle> handle1 =
+ notifier_.AddConnectionObserver(&observer1, GetTaskRunner());
+ std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle> handle2 =
+ notifier_.AddConnectionObserver(&observer2, GetTaskRunner());
+ observer2.RemoveObserverOnNotification(std::move(handle1));
+
+ SetConnection(kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt,
+ kUnknownRtt, kUnknownThroughputMbps, SaveData::kOff);
+ EXPECT_EQ(observer1.ObservedType(), kWebConnectionTypeBluetooth);
+ EXPECT_EQ(observer2.ObservedType(), kWebConnectionTypeBluetooth);
+
+ SetConnection(kWebConnectionTypeEthernet, kEthernetMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt,
+ kUnknownRtt, kUnknownThroughputMbps, SaveData::kOff);
+ EXPECT_TRUE(VerifyObservations(
+ observer1, kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt, kUnknownRtt,
+ kUnknownThroughputMbps, SaveData::kOff));
+ EXPECT_TRUE(VerifyObservations(
+ observer2, kWebConnectionTypeEthernet, kEthernetMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt, kUnknownRtt,
+ kUnknownThroughputMbps, SaveData::kOff));
+}
+
+TEST_F(NetworkStateNotifierTest, RemoveFutureObserverWhileNotifying) {
+ StateObserver observer1, observer2, observer3;
+ std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle> handle1 =
+ notifier_.AddConnectionObserver(&observer1, GetTaskRunner());
+ std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle> handle2 =
+ notifier_.AddConnectionObserver(&observer2, GetTaskRunner());
+ std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle> handle3 =
+ notifier_.AddConnectionObserver(&observer3, GetTaskRunner());
+ observer1.RemoveObserverOnNotification(std::move(handle2));
+
+ SetConnection(kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt,
+ kUnknownRtt, kUnknownThroughputMbps, SaveData::kOff);
+ EXPECT_TRUE(VerifyObservations(
+ observer1, kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt, kUnknownRtt,
+ kUnknownThroughputMbps, SaveData::kOff));
+ EXPECT_TRUE(VerifyObservations(
+ observer2, kWebConnectionTypeNone, kNoneMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt, kUnknownRtt,
+ kUnknownThroughputMbps, SaveData::kOff));
+ EXPECT_TRUE(VerifyObservations(
+ observer3, kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt, kUnknownRtt,
+ kUnknownThroughputMbps, SaveData::kOff));
+}
+
+TEST_F(NetworkStateNotifierTest, MultipleContextsAddObserver) {
+ StateObserver observer1, observer2;
+ std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle> handle1 =
+ notifier_.AddConnectionObserver(&observer1, GetTaskRunner());
+ std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle> handle2 =
+ notifier_.AddConnectionObserver(&observer2, GetTaskRunner2());
+
+ SetConnection(kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType3G, kEthernetHttpRtt,
+ kEthernetTransportRtt, kEthernetThroughputMbps, SaveData::kOff);
+ EXPECT_TRUE(VerifyObservations(
+ observer1, kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType3G, kEthernetHttpRtt,
+ kEthernetTransportRtt, kEthernetThroughputMbps, SaveData::kOff));
+ EXPECT_TRUE(VerifyObservations(
+ observer2, kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType3G, kEthernetHttpRtt,
+ kEthernetTransportRtt, kEthernetThroughputMbps, SaveData::kOff));
+}
+
+TEST_F(NetworkStateNotifierTest, RemoveContext) {
+ StateObserver observer1, observer2;
+ std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle> handle1 =
+ notifier_.AddConnectionObserver(&observer1, GetTaskRunner());
+ std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle> handle2 =
+ notifier_.AddConnectionObserver(&observer2, GetTaskRunner2());
+ handle2 = nullptr;
+
+ SetConnection(kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType3G, kEthernetHttpRtt,
+ kEthernetTransportRtt, kEthernetThroughputMbps, SaveData::kOff);
+ EXPECT_TRUE(VerifyObservations(
+ observer1, kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType3G, kEthernetHttpRtt,
+ kEthernetTransportRtt, kEthernetThroughputMbps, SaveData::kOff));
+ EXPECT_TRUE(VerifyObservations(
+ observer2, kWebConnectionTypeNone, kNoneMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt, kUnknownRtt,
+ kUnknownThroughputMbps, SaveData::kOff));
+}
+
+TEST_F(NetworkStateNotifierTest, RemoveAllContexts) {
+ StateObserver observer1, observer2;
+ std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle> handle1 =
+ notifier_.AddConnectionObserver(&observer1, GetTaskRunner());
+ std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle> handle2 =
+ notifier_.AddConnectionObserver(&observer2, GetTaskRunner2());
+ handle1 = nullptr;
+ handle2 = nullptr;
+
+ SetConnection(kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType3G, kEthernetHttpRtt,
+ kEthernetTransportRtt, kEthernetThroughputMbps, SaveData::kOff);
+ EXPECT_TRUE(VerifyObservations(
+ observer1, kWebConnectionTypeNone, kNoneMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt, kUnknownRtt,
+ kUnknownThroughputMbps, SaveData::kOff));
+ EXPECT_TRUE(VerifyObservations(
+ observer2, kWebConnectionTypeNone, kNoneMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt, kUnknownRtt,
+ kUnknownThroughputMbps, SaveData::kOff));
+}
+
+TEST_F(NetworkStateNotifierTest, SetNetworkConnectionInfoOverride) {
+ StateObserver observer;
+ std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle> handle =
+ notifier_.AddConnectionObserver(&observer, GetTaskRunner());
+
+ notifier_.SetOnLine(true);
+ SetConnection(kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt,
+ kUnknownRtt, kUnknownThroughputMbps, SaveData::kOff);
+ EXPECT_TRUE(VerifyObservations(
+ observer, kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt, kUnknownRtt,
+ kUnknownThroughputMbps, SaveData::kOff));
+ EXPECT_TRUE(notifier_.OnLine());
+ EXPECT_EQ(kWebConnectionTypeBluetooth, notifier_.ConnectionType());
+ EXPECT_EQ(kBluetoothMaxBandwidthMbps, notifier_.MaxBandwidth());
+
+ notifier_.SetNetworkConnectionInfoOverride(
+ true, kWebConnectionTypeEthernet, WebEffectiveConnectionType::kType4G,
+ kEthernetHttpRtt.value().InMilliseconds(), kEthernetMaxBandwidthMbps);
+ RunPendingTasks();
+ EXPECT_TRUE(VerifyObservations(
+ observer, kWebConnectionTypeEthernet, kEthernetMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType4G, kEthernetHttpRtt, kUnknownRtt,
+ kEthernetMaxBandwidthMbps, SaveData::kOff));
+ EXPECT_TRUE(notifier_.OnLine());
+ EXPECT_EQ(kWebConnectionTypeEthernet, notifier_.ConnectionType());
+ EXPECT_EQ(kEthernetMaxBandwidthMbps, notifier_.MaxBandwidth());
+
+ // When override is active, calls to setOnLine and setConnection are temporary
+ // ignored.
+ notifier_.SetOnLine(false);
+ SetConnection(kWebConnectionTypeNone, kNoneMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt,
+ kUnknownRtt, kUnknownThroughputMbps, SaveData::kOff);
+ RunPendingTasks();
+ EXPECT_TRUE(VerifyObservations(
+ observer, kWebConnectionTypeEthernet, kEthernetMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType4G, kEthernetHttpRtt, kUnknownRtt,
+ kEthernetMaxBandwidthMbps, SaveData::kOff));
+ EXPECT_TRUE(notifier_.OnLine());
+ EXPECT_EQ(kWebConnectionTypeEthernet, notifier_.ConnectionType());
+ EXPECT_EQ(kEthernetMaxBandwidthMbps, notifier_.MaxBandwidth());
+
+ notifier_.ClearOverride();
+ RunPendingTasks();
+ EXPECT_TRUE(VerifyObservations(
+ observer, kWebConnectionTypeNone, kNoneMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt, kUnknownRtt,
+ kUnknownThroughputMbps, SaveData::kOff));
+ EXPECT_FALSE(notifier_.OnLine());
+ EXPECT_EQ(kWebConnectionTypeNone, notifier_.ConnectionType());
+ EXPECT_EQ(kNoneMaxBandwidthMbps, notifier_.MaxBandwidth());
+}
+
+TEST_F(NetworkStateNotifierTest, SetNetworkQualityInfoOverride) {
+ StateObserver observer;
+ std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle> handle =
+ notifier_.AddConnectionObserver(&observer, GetTaskRunner());
+
+ notifier_.SetOnLine(true);
+ SetConnection(kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt,
+ kUnknownRtt, kUnknownThroughputMbps, SaveData::kOff);
+ EXPECT_TRUE(VerifyObservations(
+ observer, kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt, kUnknownRtt,
+ kUnknownThroughputMbps, SaveData::kOff));
+ EXPECT_TRUE(notifier_.OnLine());
+ EXPECT_EQ(kWebConnectionTypeBluetooth, notifier_.ConnectionType());
+ EXPECT_EQ(kBluetoothMaxBandwidthMbps, notifier_.MaxBandwidth());
+
+ notifier_.SetNetworkConnectionInfoOverride(
+ true, kWebConnectionTypeOther, WebEffectiveConnectionType::kType3G,
+ kEthernetHttpRtt.value().InMilliseconds(),
+ kEthernetThroughputMbps.value());
+ RunPendingTasks();
+ EXPECT_TRUE(VerifyObservations(
+ observer, kWebConnectionTypeOther, kEthernetThroughputMbps.value(),
+ WebEffectiveConnectionType::kType3G, kEthernetHttpRtt, kUnknownRtt,
+ kEthernetThroughputMbps, SaveData::kOff));
+ EXPECT_TRUE(notifier_.OnLine());
+ EXPECT_EQ(kWebConnectionTypeOther, notifier_.ConnectionType());
+ EXPECT_EQ(kEthernetThroughputMbps, notifier_.MaxBandwidth());
+ EXPECT_EQ(WebEffectiveConnectionType::kType3G, notifier_.EffectiveType());
+ EXPECT_EQ(kEthernetHttpRtt, notifier_.HttpRtt());
+ EXPECT_EQ(kEthernetThroughputMbps, notifier_.DownlinkThroughputMbps());
+
+ // When override is active, calls to SetConnection are temporary ignored.
+ notifier_.SetOnLine(false);
+ SetConnection(kWebConnectionTypeNone, kNoneMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt,
+ kUnknownRtt, kUnknownThroughputMbps, SaveData::kOff);
+ RunPendingTasks();
+ EXPECT_TRUE(VerifyObservations(
+ observer, kWebConnectionTypeOther, kEthernetThroughputMbps.value(),
+ WebEffectiveConnectionType::kType3G, kEthernetHttpRtt, kUnknownRtt,
+ kEthernetThroughputMbps, SaveData::kOff));
+ EXPECT_TRUE(notifier_.OnLine());
+ EXPECT_EQ(kWebConnectionTypeOther, notifier_.ConnectionType());
+ EXPECT_EQ(kEthernetThroughputMbps, notifier_.MaxBandwidth());
+ EXPECT_EQ(WebEffectiveConnectionType::kType3G, notifier_.EffectiveType());
+ EXPECT_EQ(kEthernetHttpRtt, notifier_.HttpRtt());
+ EXPECT_EQ(kEthernetThroughputMbps, notifier_.DownlinkThroughputMbps());
+
+ // Override the network connection info as well.
+ notifier_.SetNetworkConnectionInfoOverride(
+ true, kWebConnectionTypeEthernet, WebEffectiveConnectionType::kType3G,
+ kEthernetHttpRtt.value().InMilliseconds(), kEthernetMaxBandwidthMbps);
+ RunPendingTasks();
+ EXPECT_TRUE(VerifyObservations(
+ observer, kWebConnectionTypeEthernet, kEthernetMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType3G, kEthernetHttpRtt, kUnknownRtt,
+ kEthernetMaxBandwidthMbps, SaveData::kOff));
+ EXPECT_TRUE(notifier_.OnLine());
+ EXPECT_EQ(kWebConnectionTypeEthernet, notifier_.ConnectionType());
+ EXPECT_EQ(kEthernetMaxBandwidthMbps, notifier_.MaxBandwidth());
+ EXPECT_EQ(WebEffectiveConnectionType::kType3G, notifier_.EffectiveType());
+ EXPECT_EQ(kEthernetHttpRtt, notifier_.HttpRtt());
+ EXPECT_EQ(kEthernetMaxBandwidthMbps, notifier_.DownlinkThroughputMbps());
+
+ // Clearing the override should cause the network state to be changed and
+ // notified to observers.
+ notifier_.ClearOverride();
+ RunPendingTasks();
+ EXPECT_TRUE(VerifyObservations(
+ observer, kWebConnectionTypeNone, kNoneMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt, kUnknownRtt,
+ kUnknownThroughputMbps, SaveData::kOff));
+ EXPECT_FALSE(notifier_.OnLine());
+ EXPECT_EQ(kWebConnectionTypeNone, notifier_.ConnectionType());
+ EXPECT_EQ(kNoneMaxBandwidthMbps, notifier_.MaxBandwidth());
+ EXPECT_EQ(WebEffectiveConnectionType::kTypeUnknown,
+ notifier_.EffectiveType());
+ EXPECT_EQ(kUnknownRtt, notifier_.TransportRtt());
+ EXPECT_EQ(kUnknownThroughputMbps, notifier_.DownlinkThroughputMbps());
+}
+
+TEST_F(NetworkStateNotifierTest, SaveDataOverride) {
+ StateObserver observer;
+ std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle> handle =
+ notifier_.AddConnectionObserver(&observer, GetTaskRunner());
+
+ notifier_.SetOnLine(true);
+ // Set save-data attribute to false.
+ SetConnection(kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt,
+ kUnknownRtt, kUnknownThroughputMbps, SaveData::kOff);
+ EXPECT_TRUE(VerifyObservations(
+ observer, kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt, kUnknownRtt,
+ kUnknownThroughputMbps, SaveData::kOff));
+ EXPECT_TRUE(notifier_.OnLine());
+ EXPECT_EQ(kWebConnectionTypeBluetooth, notifier_.ConnectionType());
+ EXPECT_EQ(kBluetoothMaxBandwidthMbps, notifier_.MaxBandwidth());
+ EXPECT_FALSE(notifier_.SaveDataEnabled());
+
+ // Set save-data attribute to true.
+ notifier_.SetSaveDataEnabledOverride(true);
+ RunPendingTasks();
+ EXPECT_TRUE(VerifyObservations(observer, kWebConnectionTypeOther, -1,
+ WebEffectiveConnectionType::kTypeUnknown,
+ kUnknownRtt, kUnknownRtt,
+ kUnknownThroughputMbps, SaveData::kOn));
+ EXPECT_TRUE(notifier_.OnLine());
+ EXPECT_EQ(kWebConnectionTypeOther, notifier_.ConnectionType());
+ EXPECT_EQ(-1, notifier_.MaxBandwidth());
+ EXPECT_TRUE(notifier_.SaveDataEnabled());
+
+ // When override is active, calls to SetConnection are temporary ignored.
+ // save_data is set to false in SetConnection() but would be temporarily
+ // ignored.
+ notifier_.SetOnLine(false);
+ SetConnection(kWebConnectionTypeNone, -1,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt,
+ kUnknownRtt, kUnknownThroughputMbps, SaveData::kOff);
+ RunPendingTasks();
+ EXPECT_TRUE(VerifyObservations(observer, kWebConnectionTypeOther, -1,
+ WebEffectiveConnectionType::kTypeUnknown,
+ kUnknownRtt, kUnknownRtt,
+ kUnknownThroughputMbps, SaveData::kOn));
+ EXPECT_TRUE(notifier_.OnLine());
+ EXPECT_EQ(kWebConnectionTypeOther, notifier_.ConnectionType());
+ EXPECT_EQ(-1, notifier_.MaxBandwidth());
+ EXPECT_TRUE(notifier_.SaveDataEnabled());
+
+ // CLearing the override should cause the network state to be changed and
+ // notified to observers.
+ notifier_.ClearOverride();
+ RunPendingTasks();
+ EXPECT_TRUE(VerifyObservations(observer, kWebConnectionTypeNone, -1,
+ WebEffectiveConnectionType::kTypeUnknown,
+ kUnknownRtt, kUnknownRtt,
+ kUnknownThroughputMbps, SaveData::kOff));
+ EXPECT_FALSE(notifier_.OnLine());
+ EXPECT_EQ(kWebConnectionTypeNone, notifier_.ConnectionType());
+ EXPECT_EQ(-1, notifier_.MaxBandwidth());
+ EXPECT_FALSE(notifier_.SaveDataEnabled());
+
+ // Set save-data attribute to true.
+ SetConnection(kWebConnectionTypeNone, -1,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt,
+ kUnknownRtt, kUnknownThroughputMbps, SaveData::kOn);
+ RunPendingTasks();
+ EXPECT_TRUE(VerifyObservations(observer, kWebConnectionTypeNone, -1,
+ WebEffectiveConnectionType::kTypeUnknown,
+ kUnknownRtt, kUnknownRtt,
+ kUnknownThroughputMbps, SaveData::kOn));
+ EXPECT_FALSE(notifier_.OnLine());
+ EXPECT_EQ(kWebConnectionTypeNone, notifier_.ConnectionType());
+ EXPECT_EQ(-1, notifier_.MaxBandwidth());
+ EXPECT_TRUE(notifier_.SaveDataEnabled());
+}
+
+TEST_F(NetworkStateNotifierTest, NoExtraNotifications) {
+ StateObserver observer;
+ std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle> handle =
+ notifier_.AddConnectionObserver(&observer, GetTaskRunner());
+
+ SetConnection(kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType3G, kEthernetHttpRtt,
+ kEthernetTransportRtt, kEthernetThroughputMbps, SaveData::kOff);
+ EXPECT_TRUE(VerifyObservations(
+ observer, kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType3G, kEthernetHttpRtt,
+ kEthernetTransportRtt, kEthernetThroughputMbps, SaveData::kOff));
+ EXPECT_EQ(observer.CallbackCount(), 2);
+
+ SetConnection(kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType3G, kEthernetHttpRtt,
+ kEthernetTransportRtt, kEthernetThroughputMbps, SaveData::kOff);
+ EXPECT_EQ(observer.CallbackCount(), 2);
+
+ SetConnection(kWebConnectionTypeEthernet, kEthernetMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType4G,
+ kEthernetHttpRtt.value() * 2, kEthernetTransportRtt.value() * 2,
+ kEthernetThroughputMbps.value() * 2, SaveData::kOff);
+ EXPECT_TRUE(VerifyObservations(
+ observer, kWebConnectionTypeEthernet, kEthernetMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType4G, kEthernetHttpRtt.value() * 2,
+ kEthernetTransportRtt.value() * 2, kEthernetThroughputMbps.value() * 2,
+ SaveData::kOff));
+ EXPECT_EQ(observer.CallbackCount(), 4);
+
+ SetConnection(kWebConnectionTypeEthernet, kEthernetMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType4G,
+ kEthernetHttpRtt.value() * 2, kEthernetTransportRtt.value() * 2,
+ kEthernetThroughputMbps.value() * 2, SaveData::kOff);
+ EXPECT_EQ(observer.CallbackCount(), 4);
+
+ SetConnection(kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType3G, kEthernetHttpRtt,
+ kEthernetTransportRtt, kEthernetThroughputMbps, SaveData::kOff);
+ EXPECT_TRUE(VerifyObservations(
+ observer, kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType3G, kEthernetHttpRtt,
+ kEthernetTransportRtt, kEthernetThroughputMbps, SaveData::kOff));
+ EXPECT_EQ(observer.CallbackCount(), 6);
+
+ // Changing the Save-Data attribute should trigger one callback.
+ SetConnection(kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType3G, kEthernetHttpRtt,
+ kEthernetTransportRtt, kEthernetThroughputMbps, SaveData::kOn);
+ EXPECT_TRUE(VerifyObservations(
+ observer, kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType3G, kEthernetHttpRtt,
+ kEthernetTransportRtt, kEthernetThroughputMbps, SaveData::kOn));
+ EXPECT_EQ(observer.CallbackCount(), 7);
+
+ SetConnection(kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType3G, kEthernetHttpRtt,
+ kEthernetTransportRtt, kEthernetThroughputMbps, SaveData::kOn);
+ EXPECT_TRUE(VerifyObservations(
+ observer, kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType3G, kEthernetHttpRtt,
+ kEthernetTransportRtt, kEthernetThroughputMbps, SaveData::kOn));
+ EXPECT_EQ(observer.CallbackCount(), 7);
+}
+
+TEST_F(NetworkStateNotifierTest, NoNotificationOnInitialization) {
+ NetworkStateNotifier notifier;
+ StateObserver observer;
+
+ std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle> handle1 =
+ notifier.AddConnectionObserver(&observer, GetTaskRunner());
+ std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle> handle2 =
+ notifier.AddOnLineObserver(&observer, GetTaskRunner());
+ RunPendingTasks();
+ EXPECT_EQ(observer.CallbackCount(), 0);
+
+ notifier.SetWebConnection(kWebConnectionTypeBluetooth,
+ kBluetoothMaxBandwidthMbps);
+ notifier.SetOnLine(true);
+ RunPendingTasks();
+ EXPECT_EQ(observer.CallbackCount(), 0);
+
+ notifier.SetOnLine(true);
+ notifier.SetWebConnection(kWebConnectionTypeBluetooth,
+ kBluetoothMaxBandwidthMbps);
+ RunPendingTasks();
+ EXPECT_EQ(observer.CallbackCount(), 0);
+
+ notifier.SetWebConnection(kWebConnectionTypeEthernet,
+ kEthernetMaxBandwidthMbps);
+ RunPendingTasks();
+ EXPECT_EQ(observer.CallbackCount(), 1);
+ EXPECT_EQ(observer.ObservedType(), kWebConnectionTypeEthernet);
+ EXPECT_EQ(observer.ObservedMaxBandwidth(), kEthernetMaxBandwidthMbps);
+
+ notifier.SetOnLine(false);
+ RunPendingTasks();
+ EXPECT_EQ(observer.CallbackCount(), 2);
+ EXPECT_FALSE(observer.ObservedOnLineState());
+}
+
+TEST_F(NetworkStateNotifierTest, OnLineNotification) {
+ StateObserver observer;
+ std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle> handle =
+ notifier_.AddOnLineObserver(&observer, GetTaskRunner());
+
+ SetOnLine(true);
+ RunPendingTasks();
+ EXPECT_TRUE(observer.ObservedOnLineState());
+ EXPECT_EQ(observer.CallbackCount(), 1);
+
+ SetOnLine(false);
+ RunPendingTasks();
+ EXPECT_FALSE(observer.ObservedOnLineState());
+ EXPECT_EQ(observer.CallbackCount(), 2);
+}
+
+TEST_F(NetworkStateNotifierTest, MultipleObservers) {
+ StateObserver observer1;
+ StateObserver observer2;
+
+ // Observer1 observes online state, Observer2 observes both.
+ std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle> handle1 =
+ notifier_.AddOnLineObserver(&observer1, GetTaskRunner());
+ std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle> handle2 =
+ notifier_.AddConnectionObserver(&observer2, GetTaskRunner());
+ std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle> handle3 =
+ notifier_.AddOnLineObserver(&observer2, GetTaskRunner());
+
+ notifier_.SetOnLine(true);
+ RunPendingTasks();
+ EXPECT_TRUE(observer1.ObservedOnLineState());
+ EXPECT_TRUE(observer2.ObservedOnLineState());
+ EXPECT_EQ(observer1.CallbackCount(), 1);
+ EXPECT_EQ(observer2.CallbackCount(), 1);
+
+ notifier_.SetOnLine(false);
+ RunPendingTasks();
+ EXPECT_FALSE(observer1.ObservedOnLineState());
+ EXPECT_FALSE(observer2.ObservedOnLineState());
+ EXPECT_EQ(observer1.CallbackCount(), 2);
+ EXPECT_EQ(observer2.CallbackCount(), 2);
+
+ notifier_.SetOnLine(true);
+ SetConnection(kWebConnectionTypeEthernet, kEthernetMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType3G, kEthernetHttpRtt,
+ kEthernetTransportRtt, kEthernetThroughputMbps, SaveData::kOff);
+
+ EXPECT_TRUE(observer1.ObservedOnLineState());
+ EXPECT_TRUE(observer2.ObservedOnLineState());
+ EXPECT_TRUE(VerifyObservations(
+ observer2, kWebConnectionTypeEthernet, kEthernetMaxBandwidthMbps,
+ WebEffectiveConnectionType::kType3G, kEthernetHttpRtt,
+ kEthernetTransportRtt, kEthernetThroughputMbps, SaveData::kOff));
+ EXPECT_EQ(observer1.CallbackCount(), 3);
+ EXPECT_EQ(observer2.CallbackCount(), 5);
+}
+
+TEST_F(NetworkStateNotifierTest, SetNetworkConnectionInfoOverrideGenerateECTs) {
+ StateObserver observer;
+ std::unique_ptr<NetworkStateNotifier::NetworkStateObserverHandle> handle =
+ notifier_.AddConnectionObserver(&observer, GetTaskRunner());
+
+ SetConnection(kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt,
+ kUnknownRtt, kNoneMaxBandwidthMbps, SaveData::kOff);
+ EXPECT_TRUE(VerifyObservations(
+ observer, kWebConnectionTypeBluetooth, kBluetoothMaxBandwidthMbps,
+ WebEffectiveConnectionType::kTypeUnknown, kUnknownRtt, kUnknownRtt,
+ kNoneMaxBandwidthMbps, SaveData::kOff));
+
+ const struct {
+ Optional<TimeDelta> rtt;
+ WebEffectiveConnectionType expected_effective_connection_type;
+ } tests[] = {
+ {TimeDelta::FromMilliseconds(100), WebEffectiveConnectionType::kType4G},
+ {TimeDelta::FromMilliseconds(600), WebEffectiveConnectionType::kType3G},
+ {TimeDelta::FromMilliseconds(1600), WebEffectiveConnectionType::kType2G},
+ {TimeDelta::FromMilliseconds(2800),
+ WebEffectiveConnectionType::kTypeSlow2G},
+ };
+
+ for (const auto& test : tests) {
+ // The effective connection type is set to Unknown.
+ // SetNetworkConnectionInfoOverride() should compute the effective
+ // connection type based on the provided RTT.
+ notifier_.SetNetworkConnectionInfoOverride(
+ true, kWebConnectionTypeEthernet, base::nullopt,
+ test.rtt.value().InMilliseconds(), kNoneMaxBandwidthMbps);
+ RunPendingTasks();
+ EXPECT_TRUE(VerifyObservations(
+ observer, kWebConnectionTypeEthernet, kNoneMaxBandwidthMbps,
+ test.expected_effective_connection_type, test.rtt, kUnknownRtt,
+ kNoneMaxBandwidthMbps, SaveData::kOff));
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/network/network_utils.cc b/chromium/third_party/blink/renderer/platform/network/network_utils.cc
new file mode 100644
index 00000000000..3ac8421c7fe
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/network_utils.cc
@@ -0,0 +1,132 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/network/network_utils.h"
+
+#include "net/base/data_url.h"
+#include "net/base/ip_address.h"
+#include "net/base/net_errors.h"
+#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
+#include "net/base/url_util.h"
+#include "net/http/http_response_headers.h"
+#include "net/url_request/url_request_data_job.h"
+#include "third_party/blink/public/common/mime_util/mime_util.h"
+#include "third_party/blink/public/platform/url_conversion.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "url/gurl.h"
+
+namespace {
+
+net::registry_controlled_domains::PrivateRegistryFilter
+getNetPrivateRegistryFilter(blink::NetworkUtils::PrivateRegistryFilter filter) {
+ switch (filter) {
+ case blink::NetworkUtils::kIncludePrivateRegistries:
+ return net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES;
+ case blink::NetworkUtils::kExcludePrivateRegistries:
+ return net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES;
+ }
+ // There are only two NetworkUtils::PrivateRegistryFilter enum entries, so
+ // we should never reach this point. However, we must have a default return
+ // value to avoid a compiler error.
+ NOTREACHED();
+ return net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES;
+}
+
+} // namespace
+
+namespace blink {
+
+namespace NetworkUtils {
+
+bool IsReservedIPAddress(const String& host) {
+ net::IPAddress address;
+ StringUTF8Adaptor utf8(host);
+ if (!net::ParseURLHostnameToAddress(utf8.AsStringPiece(), &address))
+ return false;
+ return address.IsReserved();
+}
+
+bool IsLocalHostname(const String& host, bool* is_local6) {
+ StringUTF8Adaptor utf8(host);
+ return net::IsLocalHostname(utf8.AsStringPiece(), is_local6);
+}
+
+String GetDomainAndRegistry(const String& host, PrivateRegistryFilter filter) {
+ StringUTF8Adaptor host_utf8(host);
+ std::string domain = net::registry_controlled_domains::GetDomainAndRegistry(
+ host_utf8.AsStringPiece(), getNetPrivateRegistryFilter(filter));
+ return String(domain.data(), domain.length());
+}
+
+scoped_refptr<SharedBuffer> ParseDataURLAndPopulateResponse(
+ const KURL& url,
+ ResourceResponse& response) {
+ // The following code contains duplication of GetInfoFromDataURL() and
+ // WebURLLoaderImpl::PopulateURLResponse() in
+ // content/child/web_url_loader_impl.cc. Merge them once content/child is
+ // moved to platform/.
+ std::string utf8_mime_type;
+ std::string utf8_charset;
+ std::string data_string;
+ scoped_refptr<net::HttpResponseHeaders> headers(
+ new net::HttpResponseHeaders(std::string()));
+
+ int result = net::URLRequestDataJob::BuildResponse(
+ WebStringToGURL(url.GetString()), &utf8_mime_type, &utf8_charset,
+ &data_string, headers.get());
+ if (result != net::OK)
+ return nullptr;
+
+ if (!blink::IsSupportedMimeType(utf8_mime_type))
+ return nullptr;
+
+ scoped_refptr<SharedBuffer> data =
+ SharedBuffer::Create(data_string.data(), data_string.size());
+ response.SetHTTPStatusCode(200);
+ response.SetHTTPStatusText("OK");
+ response.SetURL(url);
+ response.SetMimeType(WebString::FromUTF8(utf8_mime_type));
+ response.SetExpectedContentLength(data->size());
+ response.SetTextEncodingName(WebString::FromUTF8(utf8_charset));
+
+ size_t iter = 0;
+ std::string name;
+ std::string value;
+ while (headers->EnumerateHeaderLines(&iter, &name, &value)) {
+ response.AddHTTPHeaderField(WebString::FromLatin1(name),
+ WebString::FromLatin1(value));
+ }
+ return data;
+}
+
+bool IsDataURLMimeTypeSupported(const KURL& url) {
+ std::string utf8_mime_type;
+ std::string utf8_charset;
+ if (net::DataURL::Parse(WebStringToGURL(url.GetString()), &utf8_mime_type,
+ &utf8_charset, nullptr)) {
+ return blink::IsSupportedMimeType(utf8_mime_type);
+ }
+ return false;
+}
+
+bool IsRedirectResponseCode(int response_code) {
+ return net::HttpResponseHeaders::IsRedirectResponseCode(response_code);
+}
+
+bool IsCertificateTransparencyRequiredError(int error_code) {
+ return error_code == net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED;
+}
+
+bool IsLegacySymantecCertError(int error_code) {
+ return error_code == net::ERR_CERT_SYMANTEC_LEGACY;
+}
+
+} // NetworkUtils
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/network/network_utils.h b/chromium/third_party/blink/renderer/platform/network/network_utils.h
new file mode 100644
index 00000000000..2fc4d824929
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/network_utils.h
@@ -0,0 +1,51 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_NETWORK_UTILS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_NETWORK_UTILS_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+class KURL;
+class SharedBuffer;
+class ResourceResponse;
+
+namespace NetworkUtils {
+
+enum PrivateRegistryFilter {
+ kIncludePrivateRegistries,
+ kExcludePrivateRegistries,
+};
+
+PLATFORM_EXPORT bool IsReservedIPAddress(const String& host);
+
+PLATFORM_EXPORT bool IsLocalHostname(const String& host, bool* is_local6);
+
+PLATFORM_EXPORT String GetDomainAndRegistry(const String& host,
+ PrivateRegistryFilter);
+
+// Returns the decoded data url as ResourceResponse and SharedBuffer
+// if url had a supported mimetype and parsing was successful.
+PLATFORM_EXPORT scoped_refptr<SharedBuffer> ParseDataURLAndPopulateResponse(
+ const KURL&,
+ ResourceResponse&);
+
+// Returns true if the URL is a data URL and its MIME type is in the list of
+// supported/recognized MIME types.
+PLATFORM_EXPORT bool IsDataURLMimeTypeSupported(const KURL&);
+
+PLATFORM_EXPORT bool IsRedirectResponseCode(int);
+
+PLATFORM_EXPORT bool IsCertificateTransparencyRequiredError(int);
+
+PLATFORM_EXPORT bool IsLegacySymantecCertError(int);
+
+} // NetworkUtils
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_NETWORK_UTILS_H_
diff --git a/chromium/third_party/blink/renderer/platform/network/network_utils_test.cc b/chromium/third_party/blink/renderer/platform/network/network_utils_test.cc
new file mode 100644
index 00000000000..75ab7cc8651
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/network_utils_test.cc
@@ -0,0 +1,112 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/network/network_utils.h"
+
+#include "net/base/ip_address.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+TEST(NetworkUtilsTest, IsReservedIPAddress) {
+ // Unreserved IPv4 addresses (in various forms).
+ EXPECT_FALSE(NetworkUtils::IsReservedIPAddress("8.8.8.8"));
+ EXPECT_FALSE(NetworkUtils::IsReservedIPAddress("99.64.0.0"));
+ EXPECT_FALSE(NetworkUtils::IsReservedIPAddress("212.15.0.0"));
+ EXPECT_FALSE(NetworkUtils::IsReservedIPAddress("212.15"));
+ EXPECT_FALSE(NetworkUtils::IsReservedIPAddress("212.15.0"));
+ EXPECT_FALSE(NetworkUtils::IsReservedIPAddress("3557752832"));
+
+ // Reserved IPv4 addresses (in various forms).
+ EXPECT_TRUE(NetworkUtils::IsReservedIPAddress("192.168.0.0"));
+ EXPECT_TRUE(NetworkUtils::IsReservedIPAddress("192.168.0.6"));
+ EXPECT_TRUE(NetworkUtils::IsReservedIPAddress("10.0.0.5"));
+ EXPECT_TRUE(NetworkUtils::IsReservedIPAddress("10.0.0"));
+ EXPECT_TRUE(NetworkUtils::IsReservedIPAddress("10.0"));
+ EXPECT_TRUE(NetworkUtils::IsReservedIPAddress("3232235526"));
+
+ // Unreserved IPv6 addresses.
+ EXPECT_FALSE(NetworkUtils::IsReservedIPAddress(
+ "[FFC0:ba98:7654:3210:FEDC:BA98:7654:3210]"));
+ EXPECT_FALSE(NetworkUtils::IsReservedIPAddress(
+ "[2000:ba98:7654:2301:EFCD:BA98:7654:3210]"));
+
+ // Reserved IPv6 addresses.
+ EXPECT_TRUE(NetworkUtils::IsReservedIPAddress("[::1]"));
+ EXPECT_TRUE(NetworkUtils::IsReservedIPAddress("[::192.9.5.5]"));
+ EXPECT_TRUE(NetworkUtils::IsReservedIPAddress("[FEED::BEEF]"));
+ EXPECT_TRUE(NetworkUtils::IsReservedIPAddress(
+ "[FEC0:ba98:7654:3210:FEDC:BA98:7654:3210]"));
+
+ // Not IP addresses at all.
+ EXPECT_FALSE(NetworkUtils::IsReservedIPAddress("example.com"));
+ EXPECT_FALSE(NetworkUtils::IsReservedIPAddress("127.0.0.1.example.com"));
+
+ // Moar IPv4
+ for (int i = 0; i < 256; i++) {
+ net::IPAddress address(i, 0, 0, 1);
+ std::string address_string = address.ToString();
+ if (i == 0 || i == 10 || i == 127 || i > 223) {
+ EXPECT_TRUE(NetworkUtils::IsReservedIPAddress(
+ String::FromUTF8(address_string.data(), address_string.length())));
+ } else {
+ EXPECT_FALSE(NetworkUtils::IsReservedIPAddress(
+ String::FromUTF8(address_string.data(), address_string.length())));
+ }
+ }
+}
+
+TEST(NetworkUtilsTest, GetDomainAndRegistry) {
+ EXPECT_EQ("", NetworkUtils::GetDomainAndRegistry(
+ "", NetworkUtils::kIncludePrivateRegistries));
+ EXPECT_EQ("", NetworkUtils::GetDomainAndRegistry(
+ ".", NetworkUtils::kIncludePrivateRegistries));
+ EXPECT_EQ("", NetworkUtils::GetDomainAndRegistry(
+ "..", NetworkUtils::kIncludePrivateRegistries));
+ EXPECT_EQ("", NetworkUtils::GetDomainAndRegistry(
+ "com", NetworkUtils::kIncludePrivateRegistries));
+ EXPECT_EQ("", NetworkUtils::GetDomainAndRegistry(
+ ".com", NetworkUtils::kIncludePrivateRegistries));
+ EXPECT_EQ(
+ "", NetworkUtils::GetDomainAndRegistry(
+ "www.example.com:8000", NetworkUtils::kIncludePrivateRegistries));
+
+ EXPECT_EQ("", NetworkUtils::GetDomainAndRegistry(
+ "localhost", NetworkUtils::kIncludePrivateRegistries));
+ EXPECT_EQ("", NetworkUtils::GetDomainAndRegistry(
+ "127.0.0.1", NetworkUtils::kIncludePrivateRegistries));
+
+ EXPECT_EQ("example.com",
+ NetworkUtils::GetDomainAndRegistry(
+ "example.com", NetworkUtils::kIncludePrivateRegistries));
+ EXPECT_EQ("example.com",
+ NetworkUtils::GetDomainAndRegistry(
+ "www.example.com", NetworkUtils::kIncludePrivateRegistries));
+ EXPECT_EQ("example.com",
+ NetworkUtils::GetDomainAndRegistry(
+ "static.example.com", NetworkUtils::kIncludePrivateRegistries));
+ EXPECT_EQ("example.com", NetworkUtils::GetDomainAndRegistry(
+ "multilevel.www.example.com",
+ NetworkUtils::kIncludePrivateRegistries));
+ EXPECT_EQ("example.co.uk",
+ NetworkUtils::GetDomainAndRegistry(
+ "www.example.co.uk", NetworkUtils::kIncludePrivateRegistries));
+
+ // Verify proper handling of 'private registries'.
+ EXPECT_EQ("foo.appspot.com", NetworkUtils::GetDomainAndRegistry(
+ "www.foo.appspot.com",
+ NetworkUtils::kIncludePrivateRegistries));
+ EXPECT_EQ("appspot.com", NetworkUtils::GetDomainAndRegistry(
+ "www.foo.appspot.com",
+ NetworkUtils::kExcludePrivateRegistries));
+
+ // Verify that unknown registries are included.
+ EXPECT_EQ("example.notarealregistry",
+ NetworkUtils::GetDomainAndRegistry(
+ "www.example.notarealregistry",
+ NetworkUtils::kIncludePrivateRegistries));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/network/parsed_content_disposition.cc b/chromium/third_party/blink/renderer/platform/network/parsed_content_disposition.cc
new file mode 100644
index 00000000000..57420497eff
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/parsed_content_disposition.cc
@@ -0,0 +1,31 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/network/parsed_content_disposition.h"
+
+#include "third_party/blink/renderer/platform/network/header_field_tokenizer.h"
+
+namespace blink {
+
+ParsedContentDisposition::ParsedContentDisposition(
+ const String& content_disposition,
+ Mode mode) {
+ HeaderFieldTokenizer tokenizer(content_disposition);
+
+ StringView type;
+ if (!tokenizer.ConsumeToken(Mode::kNormal, type)) {
+ DVLOG(1) << "Failed to find `type' in '" << content_disposition << "'";
+ return;
+ }
+ type_ = type.ToString();
+
+ parameters_ =
+ ParsedContentHeaderFieldParameters::Parse(std::move(tokenizer), mode);
+}
+
+String ParsedContentDisposition::Filename() const {
+ return ParameterValueForName("filename");
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/network/parsed_content_disposition.h b/chromium/third_party/blink/renderer/platform/network/parsed_content_disposition.h
new file mode 100644
index 00000000000..46eb0cd5ca9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/parsed_content_disposition.h
@@ -0,0 +1,44 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_PARSED_CONTENT_DISPOSITION_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_PARSED_CONTENT_DISPOSITION_H_
+
+#include "third_party/blink/renderer/platform/network/parsed_content_header_field_parameters.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+// Parses the content of a Content-Disposition header field into disposition
+// type and parameters and stores them.
+class PLATFORM_EXPORT ParsedContentDisposition final {
+ STACK_ALLOCATED();
+
+ public:
+ using Mode = ParsedContentHeaderFieldParameters::Mode;
+
+ explicit ParsedContentDisposition(const String&, Mode = Mode::kNormal);
+
+ String Type() const { return type_; }
+ String Filename() const;
+
+ // Note that in the case of multiple values for the same name, the last value
+ // is returned.
+ String ParameterValueForName(const String& name) const {
+ return IsValid() ? parameters_->ParameterValueForName(name) : String();
+ }
+ bool IsValid() const { return !!parameters_; }
+
+ private:
+ String type_;
+ WTF::Optional<ParsedContentHeaderFieldParameters> parameters_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/network/parsed_content_disposition_test.cc b/chromium/third_party/blink/renderer/platform/network/parsed_content_disposition_test.cc
new file mode 100644
index 00000000000..0c0a73c5e50
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/parsed_content_disposition_test.cc
@@ -0,0 +1,93 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/network/parsed_content_disposition.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+namespace {
+
+using Mode = ParsedContentDisposition::Mode;
+
+bool IsValidContentDisposition(const String& input, Mode mode = Mode::kNormal) {
+ return ParsedContentDisposition(input, mode).IsValid();
+}
+
+TEST(ParsedContentDispositionTest, TypeWithoutFilename) {
+ ParsedContentDisposition t("attachment");
+
+ EXPECT_TRUE(t.IsValid());
+ EXPECT_EQ("attachment", t.Type());
+ EXPECT_EQ(String(), t.Filename());
+}
+
+TEST(ParsedContentDispositionTest, TypeWithFilename) {
+ ParsedContentDisposition t(" attachment ; x=y; filename = file1 ");
+
+ EXPECT_TRUE(t.IsValid());
+ EXPECT_EQ("attachment", t.Type());
+ EXPECT_EQ("file1", t.Filename());
+}
+
+TEST(ParsedContentDispositionTest, TypeWithQuotedFilename) {
+ ParsedContentDisposition t("attachment; filename=\"x=y;y=\\\"\\pz; ;;\"");
+
+ EXPECT_TRUE(t.IsValid());
+ EXPECT_EQ("attachment", t.Type());
+ EXPECT_EQ("x=y;y=\"pz; ;;", t.Filename());
+}
+
+TEST(ParsedContentDispositionTest, InvalidTypeWithoutFilename) {
+ ParsedContentDisposition t(" ");
+
+ EXPECT_FALSE(t.IsValid());
+ EXPECT_EQ(String(), t.Type());
+ EXPECT_EQ(String(), t.Filename());
+}
+
+TEST(ParsedContentDispositionTest, InvalidTypeWithFilename) {
+ ParsedContentDisposition t("/attachment; filename=file1;");
+
+ EXPECT_FALSE(t.IsValid());
+ EXPECT_EQ(String(), t.Type());
+ EXPECT_EQ(String(), t.Filename());
+}
+
+TEST(ParsedContentDispositionTest, CaseInsensitiveFilename) {
+ ParsedContentDisposition t("attachment; fIlEnAmE=file1");
+
+ EXPECT_TRUE(t.IsValid());
+ EXPECT_EQ("attachment", t.Type());
+ EXPECT_EQ("file1", t.Filename());
+}
+
+TEST(ParsedContentDispositionTest, Validity) {
+ EXPECT_TRUE(IsValidContentDisposition("attachment"));
+ EXPECT_TRUE(IsValidContentDisposition("attachment; filename=file1"));
+ EXPECT_TRUE(
+ IsValidContentDisposition("attachment; filename*=UTF-8'en'file1"));
+ EXPECT_TRUE(IsValidContentDisposition(" attachment ;filename=file1 "));
+ EXPECT_TRUE(IsValidContentDisposition(" attachment "));
+ EXPECT_TRUE(IsValidContentDisposition("unknown-unknown"));
+ EXPECT_TRUE(IsValidContentDisposition("unknown-unknown; unknown=unknown"));
+
+ EXPECT_FALSE(IsValidContentDisposition("A/B"));
+ EXPECT_FALSE(IsValidContentDisposition("attachment\r"));
+ EXPECT_FALSE(IsValidContentDisposition("attachment\n"));
+ EXPECT_FALSE(IsValidContentDisposition("attachment filename=file1"));
+ EXPECT_FALSE(IsValidContentDisposition("attachment;filename=file1;"));
+ EXPECT_FALSE(IsValidContentDisposition(""));
+ EXPECT_FALSE(IsValidContentDisposition(" "));
+ EXPECT_FALSE(IsValidContentDisposition("\"x\""));
+ EXPECT_FALSE(IsValidContentDisposition("attachment;"));
+ EXPECT_FALSE(IsValidContentDisposition("attachment; "));
+ EXPECT_FALSE(IsValidContentDisposition("attachment; filename"));
+ EXPECT_FALSE(IsValidContentDisposition("attachment; filename;"));
+}
+
+} // namespace
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/network/parsed_content_header_field_parameters.cc b/chromium/third_party/blink/renderer/platform/network/parsed_content_header_field_parameters.cc
new file mode 100644
index 00000000000..d1153ab4a3a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/parsed_content_header_field_parameters.cc
@@ -0,0 +1,96 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/network/parsed_content_header_field_parameters.h"
+
+#include "third_party/blink/renderer/platform/network/header_field_tokenizer.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_view.h"
+
+namespace blink {
+
+// parameters := *(";" parameter)
+//
+// From http://tools.ietf.org/html/rfc2045#section-5.1:
+//
+// parameter := attribute "=" value
+//
+// attribute := token
+// ; Matching of attributes
+// ; is ALWAYS case-insensitive.
+//
+// value := token / quoted-string
+//
+// token := 1*<any (US-ASCII) CHAR except SPACE, CTLs,
+// or tspecials>
+//
+// tspecials := "(" / ")" / "<" / ">" / "@" /
+// "," / ";" / ":" / "\" / <">
+// "/" / "[" / "]" / "?" / "="
+// ; Must be in quoted-string,
+// ; to use within parameter values
+WTF::Optional<ParsedContentHeaderFieldParameters>
+ParsedContentHeaderFieldParameters::Parse(HeaderFieldTokenizer tokenizer,
+ Mode mode) {
+ NameValuePairs parameters;
+ while (!tokenizer.IsConsumed()) {
+ if (!tokenizer.Consume(';')) {
+ DVLOG(1) << "Failed to find ';'";
+ return WTF::nullopt;
+ }
+
+ StringView key;
+ String value;
+ if (!tokenizer.ConsumeToken(Mode::kNormal, key)) {
+ DVLOG(1) << "Invalid content parameter name. (at " << tokenizer.Index()
+ << ")";
+ return WTF::nullopt;
+ }
+ if (!tokenizer.Consume('=')) {
+ DVLOG(1) << "Failed to find '='";
+ return WTF::nullopt;
+ }
+ if (!tokenizer.ConsumeTokenOrQuotedString(mode, value)) {
+ DVLOG(1) << "Invalid content parameter value (at " << tokenizer.Index()
+ << ", for '" << key.ToString() << "').";
+ return WTF::nullopt;
+ }
+ parameters.emplace_back(key.ToString(), value);
+ }
+
+ return ParsedContentHeaderFieldParameters(std::move(parameters));
+}
+
+String ParsedContentHeaderFieldParameters::ParameterValueForName(
+ const String& name) const {
+ if (!name.ContainsOnlyASCII())
+ return String();
+ String lower_name = name.LowerASCII();
+
+ for (auto i = rbegin(); i != rend(); ++i) {
+ if (i->name.LowerASCII() == lower_name)
+ return i->value;
+ }
+ return String();
+}
+
+size_t ParsedContentHeaderFieldParameters::ParameterCount() const {
+ return parameters_.size();
+}
+
+bool ParsedContentHeaderFieldParameters::HasDuplicatedNames() const {
+ HashSet<String> names;
+ for (const auto& parameter : parameters_) {
+ const String lowered_name = parameter.name.LowerASCII();
+ if (names.find(lowered_name) != names.end())
+ return true;
+
+ names.insert(lowered_name);
+ }
+ return false;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/network/parsed_content_header_field_parameters.h b/chromium/third_party/blink/renderer/platform/network/parsed_content_header_field_parameters.h
new file mode 100644
index 00000000000..24a4b160510
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/parsed_content_header_field_parameters.h
@@ -0,0 +1,72 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_PARSED_CONTENT_HEADER_FIELD_PARAMETERS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_PARSED_CONTENT_HEADER_FIELD_PARAMETERS_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class HeaderFieldTokenizer;
+
+// Parses content header field parameters as specified in RFC2045 and stores
+// them. It is used internally by ParsedContent* classes.
+// FIXME: add support for comments.
+class PLATFORM_EXPORT ParsedContentHeaderFieldParameters final {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ struct NameValue {
+ NameValue(String name, String value) : name(name), value(value) {}
+
+ String name;
+ String value;
+ };
+
+ using NameValuePairs = Vector<NameValue>;
+ using const_iterator = NameValuePairs::const_iterator;
+ using reverse_const_iterator = NameValuePairs::const_reverse_iterator;
+
+ // When |Relaxed| is specified, the parser parses parameter values in a sloppy
+ // manner, i.e., only ';' and '"' are treated as special characters.
+ // See https://chromiumcodereview.appspot.com/23043002.
+ enum class Mode {
+ kNormal,
+ kRelaxed,
+ };
+
+ // We use base::Optional instead of WTF::Optional which requires its content
+ // type to be fully defined. They are essentially same, so uses of this class
+ // can (and should) use WTF::Optional to store the returned value.
+ static base::Optional<ParsedContentHeaderFieldParameters> Parse(
+ HeaderFieldTokenizer,
+ Mode);
+
+ // Note that in the case of multiple values for the same name, the last value
+ // is returned.
+ String ParameterValueForName(const String&) const;
+ size_t ParameterCount() const;
+ bool HasDuplicatedNames() const;
+
+ const_iterator begin() const { return parameters_.begin(); }
+ const_iterator end() const { return parameters_.end(); }
+
+ reverse_const_iterator rbegin() const { return parameters_.rbegin(); }
+ reverse_const_iterator rend() const { return parameters_.rend(); }
+
+ private:
+ explicit ParsedContentHeaderFieldParameters(NameValuePairs parameters)
+ : parameters_(std::move(parameters)) {}
+
+ NameValuePairs parameters_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/network/parsed_content_header_field_parameters_test.cc b/chromium/third_party/blink/renderer/platform/network/parsed_content_header_field_parameters_test.cc
new file mode 100644
index 00000000000..889d0d972af
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/parsed_content_header_field_parameters_test.cc
@@ -0,0 +1,166 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/network/header_field_tokenizer.h"
+#include "third_party/blink/renderer/platform/network/parsed_content_disposition.h"
+#include "third_party/blink/renderer/platform/network/parsed_content_header_field_parameters.h"
+#include "third_party/blink/renderer/platform/network/parsed_content_type.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+namespace {
+
+using Mode = ParsedContentHeaderFieldParameters::Mode;
+
+void CheckValidity(bool expected,
+ const String& input,
+ Mode mode = Mode::kNormal) {
+ EXPECT_EQ(expected, !!ParsedContentHeaderFieldParameters::Parse(
+ HeaderFieldTokenizer(input), mode))
+ << input;
+
+ const String disposition_input = "attachment" + input;
+ EXPECT_EQ(expected,
+ ParsedContentDisposition(disposition_input, mode).IsValid())
+ << disposition_input;
+
+ const String type_input = "text/plain" + input;
+ EXPECT_EQ(expected, ParsedContentType(type_input, mode).IsValid())
+ << type_input;
+}
+
+TEST(ParsedContentHeaderFieldParametersTest, Validity) {
+ CheckValidity(true, "");
+ CheckValidity(true, " ");
+ CheckValidity(true, " ;p1=v1");
+ CheckValidity(true, "; p1=v1");
+ CheckValidity(true, ";p1=v1 ");
+ CheckValidity(true, ";p1 = v1");
+ CheckValidity(true, ";z=\"ttx&r=z;;\\u\\\"kd==\"");
+ CheckValidity(true, "; z=\"\xff\"");
+
+ CheckValidity(false, "\r");
+ CheckValidity(false, "\n");
+ CheckValidity(false, " p1=v1");
+ CheckValidity(false, ";p1=v1;");
+ CheckValidity(false, ";");
+ CheckValidity(false, "; ");
+ CheckValidity(false, "; p1");
+ CheckValidity(false, "; p1;");
+ CheckValidity(false, ";\"xx");
+ CheckValidity(false, ";\"xx=y");
+ CheckValidity(false, "; \"z\"=u");
+ CheckValidity(false, "; z=\xff");
+
+ CheckValidity(false, ";z=q/t:()<>@,:\\/[]?");
+ CheckValidity(true, ";z=q/t:()<>@,:\\/[]?=", Mode::kRelaxed);
+ CheckValidity(false, ";z=q r", Mode::kRelaxed);
+ CheckValidity(false, ";z=q;r", Mode::kRelaxed);
+ CheckValidity(false, ";z=q\"r", Mode::kRelaxed);
+ CheckValidity(false, "; z=\xff", Mode::kRelaxed);
+}
+
+TEST(ParsedContentHeaderFieldParametersTest, ParameterName) {
+ String input = "; y=z ; y= u ; t=r;k= \"t \\u\\\"x\" ;Q=U;T=S";
+
+ CheckValidity(true, input);
+
+ WTF::Optional<ParsedContentHeaderFieldParameters> t =
+ ParsedContentHeaderFieldParameters::Parse(HeaderFieldTokenizer(input),
+ Mode::kNormal);
+ ASSERT_TRUE(t);
+
+ EXPECT_EQ(6u, t->ParameterCount());
+ EXPECT_TRUE(t->HasDuplicatedNames());
+ EXPECT_EQ(String(), t->ParameterValueForName("a"));
+ EXPECT_EQ(String(), t->ParameterValueForName("x"));
+ EXPECT_EQ("u", t->ParameterValueForName("y"));
+ EXPECT_EQ("S", t->ParameterValueForName("t"));
+ EXPECT_EQ("t u\"x", t->ParameterValueForName("k"));
+ EXPECT_EQ("U", t->ParameterValueForName("Q"));
+ EXPECT_EQ("S", t->ParameterValueForName("T"));
+
+ String kelvin = String::FromUTF8("\xe2\x84\xaa");
+ DCHECK_EQ(kelvin.LowerUnicode(AtomicString()), "k");
+ EXPECT_EQ(String(), t->ParameterValueForName(kelvin));
+}
+
+TEST(ParsedContentHeaderFieldParametersTest, RelaxedParameterName) {
+ String input = "; z=q/t:()<>@,:\\/[]?=;y=u";
+
+ CheckValidity(true, input, Mode::kRelaxed);
+
+ WTF::Optional<ParsedContentHeaderFieldParameters> t =
+ ParsedContentHeaderFieldParameters::Parse(HeaderFieldTokenizer(input),
+ Mode::kRelaxed);
+ ASSERT_TRUE(t);
+ EXPECT_EQ(2u, t->ParameterCount());
+ EXPECT_FALSE(t->HasDuplicatedNames());
+ EXPECT_EQ("q/t:()<>@,:\\/[]?=", t->ParameterValueForName("z"));
+ EXPECT_EQ("u", t->ParameterValueForName("y"));
+}
+
+TEST(ParsedContentHeaderFieldParametersTest, BeginEnd) {
+ String input = "; a=b; a=c; b=d";
+
+ WTF::Optional<ParsedContentHeaderFieldParameters> t =
+ ParsedContentHeaderFieldParameters::Parse(HeaderFieldTokenizer(input),
+ Mode::kNormal);
+ ASSERT_TRUE(t);
+ EXPECT_TRUE(t->HasDuplicatedNames());
+ EXPECT_EQ(3u, t->ParameterCount());
+
+ auto i = t->begin();
+ ASSERT_NE(i, t->end());
+ EXPECT_EQ(i->name, "a");
+ EXPECT_EQ(i->value, "b");
+
+ ++i;
+ ASSERT_NE(i, t->end());
+ EXPECT_EQ(i->name, "a");
+ EXPECT_EQ(i->value, "c");
+
+ ++i;
+ ASSERT_NE(i, t->end());
+ EXPECT_EQ(i->name, "b");
+ EXPECT_EQ(i->value, "d");
+
+ ++i;
+ ASSERT_EQ(i, t->end());
+}
+
+TEST(ParsedContentHeaderFieldParametersTest, RBeginEnd) {
+ String input = "; a=B; A=c; b=d";
+
+ WTF::Optional<ParsedContentHeaderFieldParameters> t =
+ ParsedContentHeaderFieldParameters::Parse(HeaderFieldTokenizer(input),
+ Mode::kNormal);
+ ASSERT_TRUE(t);
+ EXPECT_TRUE(t->HasDuplicatedNames());
+ EXPECT_EQ(3u, t->ParameterCount());
+
+ auto i = t->rbegin();
+ ASSERT_NE(i, t->rend());
+ EXPECT_EQ(i->name, "b");
+ EXPECT_EQ(i->value, "d");
+
+ ++i;
+ ASSERT_NE(i, t->rend());
+ EXPECT_EQ(i->name, "A");
+ EXPECT_EQ(i->value, "c");
+
+ ++i;
+ ASSERT_NE(i, t->rend());
+ EXPECT_EQ(i->name, "a");
+ EXPECT_EQ(i->value, "B");
+
+ ++i;
+ ASSERT_EQ(i, t->rend());
+}
+
+} // namespace
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/network/parsed_content_type.cc b/chromium/third_party/blink/renderer/platform/network/parsed_content_type.cc
new file mode 100644
index 00000000000..a6b44892025
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/parsed_content_type.cc
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ * Copyright (C) 2012 Intel Corporation. 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 "third_party/blink/renderer/platform/network/parsed_content_type.h"
+
+#include "third_party/blink/renderer/platform/network/header_field_tokenizer.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_view.h"
+
+namespace blink {
+
+// From http://tools.ietf.org/html/rfc2045#section-5.1:
+//
+// content := "Content-Type" ":" type "/" subtype
+// *(";" parameter)
+// ; Matching of media type and subtype
+// ; is ALWAYS case-insensitive.
+//
+// type := discrete-type / composite-type
+//
+// discrete-type := "text" / "image" / "audio" / "video" /
+// "application" / extension-token
+//
+// composite-type := "message" / "multipart" / extension-token
+//
+// extension-token := ietf-token / x-token
+//
+// ietf-token := <An extension token defined by a
+// standards-track RFC and registered
+// with IANA.>
+//
+// x-token := <The two characters "X-" or "x-" followed, with
+// no intervening white space, by any token>
+//
+// subtype := extension-token / iana-token
+//
+// iana-token := <A publicly-defined extension token. Tokens
+// of this form must be registered with IANA
+// as specified in RFC 2048.>
+//
+// parameter := attribute "=" value
+//
+// attribute := token
+// ; Matching of attributes
+// ; is ALWAYS case-insensitive.
+//
+// value := token / quoted-string
+//
+// token := 1*<any (US-ASCII) CHAR except SPACE, CTLs,
+// or tspecials>
+//
+// tspecials := "(" / ")" / "<" / ">" / "@" /
+// "," / ";" / ":" / "\" / <">
+// "/" / "[" / "]" / "?" / "="
+// ; Must be in quoted-string,
+// ; to use within parameter values
+ParsedContentType::ParsedContentType(const String& content_type, Mode mode) {
+ HeaderFieldTokenizer tokenizer(content_type);
+
+ StringView type, subtype;
+ if (!tokenizer.ConsumeToken(Mode::kNormal, type)) {
+ DVLOG(1) << "Failed to find `type' in '" << content_type << "'";
+ return;
+ }
+ if (!tokenizer.Consume('/')) {
+ DVLOG(1) << "Failed to find '/' in '" << content_type << "'";
+ return;
+ }
+ if (!tokenizer.ConsumeToken(Mode::kNormal, subtype)) {
+ DVLOG(1) << "Failed to find `subtype' in '" << content_type << "'";
+ return;
+ }
+
+ StringBuilder builder;
+ builder.Append(type);
+ builder.Append('/');
+ builder.Append(subtype);
+ mime_type_ = builder.ToString();
+
+ parameters_ =
+ ParsedContentHeaderFieldParameters::Parse(std::move(tokenizer), mode);
+}
+
+String ParsedContentType::Charset() const {
+ return ParameterValueForName("charset");
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/network/parsed_content_type.h b/chromium/third_party/blink/renderer/platform/network/parsed_content_type.h
new file mode 100644
index 00000000000..1dc43fc8662
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/parsed_content_type.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ * Copyright (C) 2012 Intel Corporation. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_PARSED_CONTENT_TYPE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_PARSED_CONTENT_TYPE_H_
+
+#include "third_party/blink/renderer/platform/network/parsed_content_header_field_parameters.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+// ParsedContentType parses the content of a Content-Type header field as
+// specified in RFC2045 into MIME type and parameters and stores them.
+// FIXME: add support for comments.
+class PLATFORM_EXPORT ParsedContentType final {
+ STACK_ALLOCATED();
+
+ public:
+ using Mode = ParsedContentHeaderFieldParameters::Mode;
+
+ explicit ParsedContentType(const String&, Mode = Mode::kNormal);
+
+ String MimeType() const { return mime_type_; }
+ String Charset() const;
+
+ // Note that in the case of multiple values for the same name, the last value
+ // is returned.
+ String ParameterValueForName(const String& name) const {
+ return IsValid() ? parameters_->ParameterValueForName(name) : String();
+ }
+ const ParsedContentHeaderFieldParameters& GetParameters() const {
+ DCHECK(IsValid());
+ return *parameters_;
+ }
+
+ bool IsValid() const { return !!parameters_; }
+
+ private:
+ String mime_type_;
+ WTF::Optional<ParsedContentHeaderFieldParameters> parameters_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/network/parsed_content_type_test.cc b/chromium/third_party/blink/renderer/platform/network/parsed_content_type_test.cc
new file mode 100644
index 00000000000..f78f33cd111
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/parsed_content_type_test.cc
@@ -0,0 +1,94 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/network/parsed_content_type.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+namespace {
+
+using Mode = ParsedContentType::Mode;
+
+bool IsValidContentType(const String& input, Mode mode = Mode::kNormal) {
+ return ParsedContentType(input, mode).IsValid();
+}
+
+TEST(ParsedContentTypeTest, MimeTypeWithoutCharset) {
+ ParsedContentType t("text/plain");
+
+ EXPECT_TRUE(t.IsValid());
+ EXPECT_EQ("text/plain", t.MimeType());
+ EXPECT_EQ(String(), t.Charset());
+}
+
+TEST(ParsedContentTypeTest, MimeTypeWithCharSet) {
+ ParsedContentType t("text / plain ; x=y; charset = utf-8 ");
+
+ EXPECT_TRUE(t.IsValid());
+ EXPECT_EQ("text/plain", t.MimeType());
+ EXPECT_EQ("utf-8", t.Charset());
+}
+
+TEST(ParsedContentTypeTest, MimeTypeWithQuotedCharSet) {
+ ParsedContentType t("text/plain; charset=\"x=y;y=\\\"\\pz; ;;\"");
+
+ EXPECT_TRUE(t.IsValid());
+ EXPECT_EQ("text/plain", t.MimeType());
+ EXPECT_EQ("x=y;y=\"pz; ;;", t.Charset());
+}
+
+TEST(ParsedContentTypeTest, InvalidMimeTypeWithoutCharset) {
+ ParsedContentType t(" ");
+
+ EXPECT_FALSE(t.IsValid());
+ EXPECT_EQ(String(), t.MimeType());
+ EXPECT_EQ(String(), t.Charset());
+}
+
+TEST(ParsedContentTypeTest, InvalidMimeTypeWithCharset) {
+ ParsedContentType t("text/plain; charset;");
+
+ EXPECT_FALSE(t.IsValid());
+ EXPECT_EQ("text/plain", t.MimeType());
+ EXPECT_EQ(String(), t.Charset());
+}
+
+TEST(ParsedContentTypeTest, CaseInsensitiveCharset) {
+ ParsedContentType t("text/plain; cHaRsEt=utf-8");
+
+ EXPECT_TRUE(t.IsValid());
+ EXPECT_EQ("text/plain", t.MimeType());
+ EXPECT_EQ("utf-8", t.Charset());
+}
+
+TEST(ParsedContentTypeTest, Validity) {
+ EXPECT_TRUE(IsValidContentType("text/plain"));
+ EXPECT_TRUE(IsValidContentType("text/plain; charset=utf-8"));
+ EXPECT_TRUE(IsValidContentType(" text/plain ;charset=utf-8 "));
+ EXPECT_TRUE(IsValidContentType(" text/plain "));
+ EXPECT_TRUE(IsValidContentType("unknown/unknown"));
+ EXPECT_TRUE(IsValidContentType("unknown/unknown; charset=unknown"));
+
+ EXPECT_FALSE(IsValidContentType("A"));
+ EXPECT_FALSE(IsValidContentType("text/plain\r"));
+ EXPECT_FALSE(IsValidContentType("text/plain\n"));
+ EXPECT_FALSE(IsValidContentType("text/plain charset=utf-8"));
+ EXPECT_FALSE(IsValidContentType("text/plain;charset=utf-8;"));
+ EXPECT_FALSE(IsValidContentType(""));
+ EXPECT_FALSE(IsValidContentType(" "));
+ EXPECT_FALSE(IsValidContentType("\"x\""));
+ EXPECT_FALSE(IsValidContentType("\"x\"/\"y\""));
+ EXPECT_FALSE(IsValidContentType("\"x\"/y"));
+ EXPECT_FALSE(IsValidContentType("x/\"y\""));
+ EXPECT_FALSE(IsValidContentType("text/plain;"));
+ EXPECT_FALSE(IsValidContentType("text/plain; "));
+ EXPECT_FALSE(IsValidContentType("text/plain; charset"));
+ EXPECT_FALSE(IsValidContentType("text/plain; charset;"));
+}
+
+} // namespace
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/network/server_timing_header.cc b/chromium/third_party/blink/renderer/platform/network/server_timing_header.cc
new file mode 100644
index 00000000000..17a5bf382d6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/server_timing_header.cc
@@ -0,0 +1,23 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/network/server_timing_header.h"
+
+namespace blink {
+
+void ServerTimingHeader::SetParameter(StringView name, String value) {
+ if (EqualIgnoringASCIICase(name, "dur")) {
+ if (!duration_set_) {
+ duration_ = value.ToDouble();
+ duration_set_ = true;
+ }
+ } else if (EqualIgnoringASCIICase(name, "desc")) {
+ if (!description_set_) {
+ description_ = value;
+ description_set_ = true;
+ }
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/network/server_timing_header.h b/chromium/third_party/blink/renderer/platform/network/server_timing_header.h
new file mode 100644
index 00000000000..c8dd457f4b9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/server_timing_header.h
@@ -0,0 +1,35 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_SERVER_TIMING_HEADER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_SERVER_TIMING_HEADER_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class ServerTimingHeader {
+ public:
+ ServerTimingHeader(const String& name) : name_(name) {}
+
+ const String& Name() const { return name_; }
+ const double& Duration() const { return duration_; }
+ const String& Description() const { return description_; }
+
+ void SetParameter(StringView, String);
+
+ private:
+ String name_;
+ double duration_ = 0.0;
+ String description_ = "";
+
+ bool duration_set_ = false;
+ bool description_set_ = false;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/network/web_socket_handshake_request.cc b/chromium/third_party/blink/renderer/platform/network/web_socket_handshake_request.cc
new file mode 100644
index 00000000000..794c6d00583
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/web_socket_handshake_request.cc
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2010 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 "third_party/blink/renderer/platform/network/web_socket_handshake_request.h"
+
+namespace blink {
+
+WebSocketHandshakeRequest::WebSocketHandshakeRequest(const KURL& url)
+ : url_(url) {}
+
+WebSocketHandshakeRequest::WebSocketHandshakeRequest() = default;
+
+WebSocketHandshakeRequest::WebSocketHandshakeRequest(
+ const WebSocketHandshakeRequest& request)
+ : url_(request.url_),
+ header_fields_(request.header_fields_),
+ headers_text_(request.headers_text_) {}
+
+WebSocketHandshakeRequest::~WebSocketHandshakeRequest() = default;
+
+void WebSocketHandshakeRequest::AddAndMergeHeader(HTTPHeaderMap* map,
+ const AtomicString& name,
+ const AtomicString& value) {
+ HTTPHeaderMap::AddResult result = map->Add(name, value);
+ if (!result.is_new_entry) {
+ // Inspector expects the "\n" separated format.
+ result.stored_value->value =
+ result.stored_value->value + "\n" + String(value);
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/network/web_socket_handshake_request.h b/chromium/third_party/blink/renderer/platform/network/web_socket_handshake_request.h
new file mode 100644
index 00000000000..de485d2265f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/web_socket_handshake_request.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2010 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_WEB_SOCKET_HANDSHAKE_REQUEST_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_WEB_SOCKET_HANDSHAKE_REQUEST_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/network/http_header_map.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class HTTPHeaderMap;
+
+class PLATFORM_EXPORT WebSocketHandshakeRequest final
+ : public RefCounted<WebSocketHandshakeRequest> {
+ public:
+ static scoped_refptr<WebSocketHandshakeRequest> Create(const KURL& url) {
+ return base::AdoptRef(new WebSocketHandshakeRequest(url));
+ }
+ static scoped_refptr<WebSocketHandshakeRequest> Create() {
+ return base::AdoptRef(new WebSocketHandshakeRequest);
+ }
+ static scoped_refptr<WebSocketHandshakeRequest> Create(
+ const WebSocketHandshakeRequest& request) {
+ return base::AdoptRef(new WebSocketHandshakeRequest(request));
+ }
+ virtual ~WebSocketHandshakeRequest();
+
+ void AddAndMergeHeader(const AtomicString& name, const AtomicString& value) {
+ AddAndMergeHeader(&header_fields_, name, value);
+ }
+
+ // Merges the existing value with |value| in |map| if |map| already has
+ // |name|. Associates |value| with |name| in |map| otherwise.
+ // This function builds data for inspector.
+ static void AddAndMergeHeader(HTTPHeaderMap* /* map */,
+ const AtomicString& name,
+ const AtomicString& value);
+
+ void AddHeaderField(const AtomicString& name, const AtomicString& value) {
+ header_fields_.Add(name, value);
+ }
+
+ KURL Url() const { return url_; }
+ void SetURL(const KURL& url) { url_ = url; }
+ const HTTPHeaderMap& HeaderFields() const { return header_fields_; }
+ const String& HeadersText() const { return headers_text_; }
+ void SetHeadersText(const String& text) { headers_text_ = text; }
+
+ private:
+ WebSocketHandshakeRequest(const KURL&);
+ WebSocketHandshakeRequest();
+ WebSocketHandshakeRequest(const WebSocketHandshakeRequest&);
+
+ KURL url_;
+ HTTPHeaderMap header_fields_;
+ String headers_text_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_WEB_SOCKET_HANDSHAKE_REQUEST_H_
diff --git a/chromium/third_party/blink/renderer/platform/network/web_socket_handshake_response.cc b/chromium/third_party/blink/renderer/platform/network/web_socket_handshake_response.cc
new file mode 100644
index 00000000000..c3080a57a8b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/web_socket_handshake_response.cc
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2010 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 "third_party/blink/renderer/platform/network/web_socket_handshake_response.h"
+
+#include "third_party/blink/renderer/platform/network/web_socket_handshake_request.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+
+namespace blink {
+
+WebSocketHandshakeResponse::WebSocketHandshakeResponse() = default;
+
+WebSocketHandshakeResponse::~WebSocketHandshakeResponse() = default;
+
+int WebSocketHandshakeResponse::StatusCode() const {
+ return status_code_;
+}
+
+void WebSocketHandshakeResponse::SetStatusCode(int status_code) {
+ DCHECK_GE(status_code, 100);
+ DCHECK_LT(status_code, 600);
+ status_code_ = status_code;
+}
+
+const String& WebSocketHandshakeResponse::StatusText() const {
+ return status_text_;
+}
+
+void WebSocketHandshakeResponse::SetStatusText(const String& status_text) {
+ status_text_ = status_text;
+}
+
+const HTTPHeaderMap& WebSocketHandshakeResponse::HeaderFields() const {
+ return header_fields_;
+}
+
+void WebSocketHandshakeResponse::AddHeaderField(const AtomicString& name,
+ const AtomicString& value) {
+ WebSocketHandshakeRequest::AddAndMergeHeader(&header_fields_, name, value);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/network/web_socket_handshake_response.h b/chromium/third_party/blink/renderer/platform/network/web_socket_handshake_response.h
new file mode 100644
index 00000000000..6bfa40338a5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/network/web_socket_handshake_response.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2010 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_WEB_SOCKET_HANDSHAKE_RESPONSE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_WEB_SOCKET_HANDSHAKE_RESPONSE_H_
+
+#include "third_party/blink/renderer/platform/network/http_header_map.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT WebSocketHandshakeResponse final {
+ public:
+ WebSocketHandshakeResponse();
+ ~WebSocketHandshakeResponse();
+
+ int StatusCode() const;
+ void SetStatusCode(int);
+ const String& StatusText() const;
+ void SetStatusText(const String&);
+ const HTTPHeaderMap& HeaderFields() const;
+ void AddHeaderField(const AtomicString& name, const AtomicString& value);
+ const String& HeadersText() const { return headers_text_; }
+ void SetHeadersText(const String& text) { headers_text_ = text; }
+
+ private:
+ int status_code_;
+ String status_text_;
+ HTTPHeaderMap header_fields_;
+ String headers_text_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_WEB_SOCKET_HANDSHAKE_RESPONSE_H_
diff --git a/chromium/third_party/blink/renderer/platform/partition_alloc_memory_dump_provider.cc b/chromium/third_party/blink/renderer/platform/partition_alloc_memory_dump_provider.cc
new file mode 100644
index 00000000000..0bb965c7bc1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/partition_alloc_memory_dump_provider.cc
@@ -0,0 +1,211 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/partition_alloc_memory_dump_provider.h"
+
+#include <unordered_map>
+
+#include "base/strings/stringprintf.h"
+#include "base/trace_event/heap_profiler_allocation_context_tracker.h"
+#include "base/trace_event/heap_profiler_allocation_register.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "base/trace_event/trace_event_memory_overhead.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+namespace {
+
+void ReportAllocation(void* address, size_t size, const char* type_name) {
+ PartitionAllocMemoryDumpProvider::Instance()->insert(address, size,
+ type_name);
+}
+
+void ReportFree(void* address) {
+ PartitionAllocMemoryDumpProvider::Instance()->Remove(address);
+}
+
+const char kPartitionAllocDumpName[] = "partition_alloc";
+const char kPartitionsDumpName[] = "partitions";
+
+std::string GetPartitionDumpName(const char* partition_name) {
+ return base::StringPrintf("%s/%s/%s", kPartitionAllocDumpName,
+ kPartitionsDumpName, partition_name);
+}
+
+// This class is used to invert the dependency of PartitionAlloc on the
+// PartitionAllocMemoryDumpProvider. This implements an interface that will
+// be called with memory statistics for each bucket in the allocator.
+class PartitionStatsDumperImpl final : public WTF::PartitionStatsDumper {
+ DISALLOW_NEW();
+ WTF_MAKE_NONCOPYABLE(PartitionStatsDumperImpl);
+
+ public:
+ PartitionStatsDumperImpl(
+ base::trace_event::ProcessMemoryDump* memory_dump,
+ base::trace_event::MemoryDumpLevelOfDetail level_of_detail)
+ : memory_dump_(memory_dump), uid_(0), total_active_bytes_(0) {}
+
+ // PartitionStatsDumper implementation.
+ void PartitionDumpTotals(const char* partition_name,
+ const WTF::PartitionMemoryStats*) override;
+ void PartitionsDumpBucketStats(
+ const char* partition_name,
+ const WTF::PartitionBucketMemoryStats*) override;
+
+ size_t TotalActiveBytes() const { return total_active_bytes_; }
+
+ private:
+ base::trace_event::ProcessMemoryDump* memory_dump_;
+ unsigned long uid_;
+ size_t total_active_bytes_;
+};
+
+void PartitionStatsDumperImpl::PartitionDumpTotals(
+ const char* partition_name,
+ const WTF::PartitionMemoryStats* memory_stats) {
+ total_active_bytes_ += memory_stats->total_active_bytes;
+ std::string dump_name = GetPartitionDumpName(partition_name);
+ base::trace_event::MemoryAllocatorDump* allocator_dump =
+ memory_dump_->CreateAllocatorDump(dump_name);
+ allocator_dump->AddScalar("size", "bytes",
+ memory_stats->total_resident_bytes);
+ allocator_dump->AddScalar("allocated_objects_size", "bytes",
+ memory_stats->total_active_bytes);
+ allocator_dump->AddScalar("virtual_size", "bytes",
+ memory_stats->total_mmapped_bytes);
+ allocator_dump->AddScalar("virtual_committed_size", "bytes",
+ memory_stats->total_committed_bytes);
+ allocator_dump->AddScalar("decommittable_size", "bytes",
+ memory_stats->total_decommittable_bytes);
+ allocator_dump->AddScalar("discardable_size", "bytes",
+ memory_stats->total_discardable_bytes);
+}
+
+void PartitionStatsDumperImpl::PartitionsDumpBucketStats(
+ const char* partition_name,
+ const WTF::PartitionBucketMemoryStats* memory_stats) {
+ DCHECK(memory_stats->is_valid);
+ std::string dump_name = GetPartitionDumpName(partition_name);
+ if (memory_stats->is_direct_map) {
+ dump_name.append(base::StringPrintf("/directMap_%lu", ++uid_));
+ } else {
+ dump_name.append(base::StringPrintf(
+ "/bucket_%u", static_cast<unsigned>(memory_stats->bucket_slot_size)));
+ }
+
+ base::trace_event::MemoryAllocatorDump* allocator_dump =
+ memory_dump_->CreateAllocatorDump(dump_name);
+ allocator_dump->AddScalar("size", "bytes", memory_stats->resident_bytes);
+ allocator_dump->AddScalar("allocated_objects_size", "bytes",
+ memory_stats->active_bytes);
+ allocator_dump->AddScalar("slot_size", "bytes",
+ memory_stats->bucket_slot_size);
+ allocator_dump->AddScalar("decommittable_size", "bytes",
+ memory_stats->decommittable_bytes);
+ allocator_dump->AddScalar("discardable_size", "bytes",
+ memory_stats->discardable_bytes);
+ allocator_dump->AddScalar("total_pages_size", "bytes",
+ memory_stats->allocated_page_size);
+ allocator_dump->AddScalar("active_pages", "objects",
+ memory_stats->num_active_pages);
+ allocator_dump->AddScalar("full_pages", "objects",
+ memory_stats->num_full_pages);
+ allocator_dump->AddScalar("empty_pages", "objects",
+ memory_stats->num_empty_pages);
+ allocator_dump->AddScalar("decommitted_pages", "objects",
+ memory_stats->num_decommitted_pages);
+}
+
+} // namespace
+
+PartitionAllocMemoryDumpProvider* PartitionAllocMemoryDumpProvider::Instance() {
+ DEFINE_STATIC_LOCAL(PartitionAllocMemoryDumpProvider, instance, ());
+ return &instance;
+}
+
+bool PartitionAllocMemoryDumpProvider::OnMemoryDump(
+ const base::trace_event::MemoryDumpArgs& args,
+ base::trace_event::ProcessMemoryDump* memory_dump) {
+ using base::trace_event::MemoryDumpLevelOfDetail;
+
+ MemoryDumpLevelOfDetail level_of_detail = args.level_of_detail;
+ if (allocation_register_.is_enabled()) {
+ // Overhead should always be reported, regardless of light vs. heavy.
+ base::trace_event::TraceEventMemoryOverhead overhead;
+ std::unordered_map<base::trace_event::AllocationContext,
+ base::trace_event::AllocationMetrics>
+ metrics_by_context;
+ // Dump only the overhead estimation in non-detailed dumps.
+ if (level_of_detail == MemoryDumpLevelOfDetail::DETAILED) {
+ allocation_register_.UpdateAndReturnsMetrics(metrics_by_context);
+ }
+ allocation_register_.EstimateTraceMemoryOverhead(&overhead);
+ memory_dump->DumpHeapUsage(metrics_by_context, overhead, "partition_alloc");
+ }
+
+ PartitionStatsDumperImpl partition_stats_dumper(memory_dump, level_of_detail);
+
+ base::trace_event::MemoryAllocatorDump* partitions_dump =
+ memory_dump->CreateAllocatorDump(base::StringPrintf(
+ "%s/%s", kPartitionAllocDumpName, kPartitionsDumpName));
+
+ // This method calls memoryStats.partitionsDumpBucketStats with memory
+ // statistics.
+ WTF::Partitions::DumpMemoryStats(
+ level_of_detail != MemoryDumpLevelOfDetail::DETAILED,
+ &partition_stats_dumper);
+
+ base::trace_event::MemoryAllocatorDump* allocated_objects_dump =
+ memory_dump->CreateAllocatorDump(
+ WTF::Partitions::kAllocatedObjectPoolName);
+ allocated_objects_dump->AddScalar("size", "bytes",
+ partition_stats_dumper.TotalActiveBytes());
+ memory_dump->AddOwnershipEdge(allocated_objects_dump->guid(),
+ partitions_dump->guid());
+
+ return true;
+}
+
+// |m_allocationRegister| should be initialized only when necessary to avoid
+// waste of memory.
+PartitionAllocMemoryDumpProvider::PartitionAllocMemoryDumpProvider() = default;
+
+PartitionAllocMemoryDumpProvider::~PartitionAllocMemoryDumpProvider() = default;
+
+void PartitionAllocMemoryDumpProvider::OnHeapProfilingEnabled(bool enabled) {
+ if (enabled) {
+ allocation_register_.SetEnabled();
+ WTF::PartitionAllocHooks::SetAllocationHook(ReportAllocation);
+ WTF::PartitionAllocHooks::SetFreeHook(ReportFree);
+ } else {
+ WTF::PartitionAllocHooks::SetAllocationHook(nullptr);
+ WTF::PartitionAllocHooks::SetFreeHook(nullptr);
+ allocation_register_.SetDisabled();
+ }
+}
+
+void PartitionAllocMemoryDumpProvider::insert(void* address,
+ size_t size,
+ const char* type_name) {
+ base::trace_event::AllocationContext context;
+ if (!base::trace_event::AllocationContextTracker::
+ GetInstanceForCurrentThread()
+ ->GetContextSnapshot(&context))
+ return;
+
+ context.type_name = type_name;
+ if (!allocation_register_.is_enabled())
+ return;
+ allocation_register_.Insert(address, size, context);
+}
+
+void PartitionAllocMemoryDumpProvider::Remove(void* address) {
+ if (!allocation_register_.is_enabled())
+ return;
+ allocation_register_.Remove(address);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/partition_alloc_memory_dump_provider.h b/chromium/third_party/blink/renderer/platform/partition_alloc_memory_dump_provider.h
new file mode 100644
index 00000000000..75a8397f7bb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/partition_alloc_memory_dump_provider.h
@@ -0,0 +1,42 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_PARTITION_ALLOC_MEMORY_DUMP_PROVIDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_PARTITION_ALLOC_MEMORY_DUMP_PROVIDER_H_
+
+#include "base/trace_event/memory_dump_provider.h"
+#include "base/trace_event/sharded_allocation_register.h"
+#include "third_party/blink/public/platform/web_common.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class BLINK_PLATFORM_EXPORT PartitionAllocMemoryDumpProvider final
+ : public base::trace_event::MemoryDumpProvider {
+ // TODO(tasak): PartitionAllocMemoryDumpProvider should be
+ // USING_FAST_MALLOC. c.f. crbug.com/584196
+ WTF_MAKE_NONCOPYABLE(PartitionAllocMemoryDumpProvider);
+
+ public:
+ static PartitionAllocMemoryDumpProvider* Instance();
+ ~PartitionAllocMemoryDumpProvider() override;
+
+ // MemoryDumpProvider implementation.
+ bool OnMemoryDump(const base::trace_event::MemoryDumpArgs&,
+ base::trace_event::ProcessMemoryDump*) override;
+ void OnHeapProfilingEnabled(bool) override;
+
+ // These methods are called only from PartitionAllocHooks' callbacks.
+ void insert(void*, size_t, const char*);
+ void Remove(void*);
+
+ private:
+ PartitionAllocMemoryDumpProvider();
+
+ base::trace_event::ShardedAllocationRegister allocation_register_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_PARTITION_ALLOC_MEMORY_DUMP_PROVIDER_H_
diff --git a/chromium/third_party/blink/renderer/platform/paste_mode.h b/chromium/third_party/blink/renderer/platform/paste_mode.h
new file mode 100644
index 00000000000..f20b28d7adf
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/paste_mode.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_PASTE_MODE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_PASTE_MODE_H_
+
+namespace blink {
+
+enum class PasteMode {
+ kAllMimeTypes,
+ kPlainTextOnly,
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_answer_options_platform.h b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_answer_options_platform.h
new file mode 100644
index 00000000000..b5eecfd80d2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_answer_options_platform.h
@@ -0,0 +1,32 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_ANSWER_OPTIONS_PLATFORM_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_ANSWER_OPTIONS_PLATFORM_H_
+
+#include "third_party/blink/renderer/platform/heap/handle.h"
+
+namespace blink {
+
+class RTCAnswerOptionsPlatform final
+ : public GarbageCollected<RTCAnswerOptionsPlatform> {
+ public:
+ static RTCAnswerOptionsPlatform* Create(bool voice_activity_detection) {
+ return new RTCAnswerOptionsPlatform(voice_activity_detection);
+ }
+
+ bool VoiceActivityDetection() const { return voice_activity_detection_; }
+
+ void Trace(blink::Visitor* visitor) {}
+
+ private:
+ explicit RTCAnswerOptionsPlatform(bool voice_activity_detection)
+ : voice_activity_detection_(voice_activity_detection) {}
+
+ bool voice_activity_detection_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_ANSWER_OPTIONS_PLATFORM_H_
diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_offer_options_platform.h b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_offer_options_platform.h
new file mode 100644
index 00000000000..169715664ed
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_offer_options_platform.h
@@ -0,0 +1,49 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_OFFER_OPTIONS_PLATFORM_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_OFFER_OPTIONS_PLATFORM_H_
+
+#include "third_party/blink/renderer/platform/heap/handle.h"
+
+namespace blink {
+
+class RTCOfferOptionsPlatform final
+ : public GarbageCollected<RTCOfferOptionsPlatform> {
+ public:
+ static RTCOfferOptionsPlatform* Create(int32_t offer_to_receive_video,
+ int32_t offer_to_receive_audio,
+ bool voice_activity_detection,
+ bool ice_restart) {
+ return new RTCOfferOptionsPlatform(offer_to_receive_video,
+ offer_to_receive_audio,
+ voice_activity_detection, ice_restart);
+ }
+
+ int32_t OfferToReceiveVideo() const { return offer_to_receive_video_; }
+ int32_t OfferToReceiveAudio() const { return offer_to_receive_audio_; }
+ bool VoiceActivityDetection() const { return voice_activity_detection_; }
+ bool IceRestart() const { return ice_restart_; }
+
+ void Trace(blink::Visitor* visitor) {}
+
+ private:
+ RTCOfferOptionsPlatform(int32_t offer_to_receive_video,
+ int32_t offer_to_receive_audio,
+ bool voice_activity_detection,
+ bool ice_restart)
+ : offer_to_receive_video_(offer_to_receive_video),
+ offer_to_receive_audio_(offer_to_receive_audio),
+ voice_activity_detection_(voice_activity_detection),
+ ice_restart_(ice_restart) {}
+
+ int32_t offer_to_receive_video_;
+ int32_t offer_to_receive_audio_;
+ bool voice_activity_detection_;
+ bool ice_restart_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_OFFER_OPTIONS_PLATFORM_H_
diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_session_description_request.h b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_session_description_request.h
new file mode 100644
index 00000000000..f29d0ade191
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_session_description_request.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ * 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 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_SESSION_DESCRIPTION_REQUEST_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_SESSION_DESCRIPTION_REQUEST_H_
+
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+class WebRTCSessionDescription;
+
+class RTCSessionDescriptionRequest
+ : public GarbageCollectedFinalized<RTCSessionDescriptionRequest> {
+ public:
+ virtual ~RTCSessionDescriptionRequest() = default;
+
+ virtual void RequestSucceeded(const WebRTCSessionDescription&) = 0;
+ virtual void RequestFailed(const String& error) = 0;
+
+ virtual void Trace(blink::Visitor* visitor) {}
+
+ protected:
+ RTCSessionDescriptionRequest() = default;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_SESSION_DESCRIPTION_REQUEST_H_
diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_stats_request.h b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_stats_request.h
new file mode 100644
index 00000000000..0651cb89114
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_stats_request.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ * 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 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_STATS_REQUEST_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_STATS_REQUEST_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class MediaStreamComponent;
+class RTCStatsResponseBase;
+
+class RTCStatsRequest : public GarbageCollectedFinalized<RTCStatsRequest> {
+ public:
+ virtual ~RTCStatsRequest() = default;
+
+ virtual RTCStatsResponseBase* CreateResponse() = 0;
+ virtual bool HasSelector() = 0;
+ virtual MediaStreamComponent* Component() = 0;
+ virtual void RequestSucceeded(RTCStatsResponseBase*) = 0;
+
+ virtual void Trace(blink::Visitor* visitor) {}
+
+ protected:
+ RTCStatsRequest() = default;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_STATS_REQUEST_H_
diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_stats_response_base.h b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_stats_response_base.h
new file mode 100644
index 00000000000..7e12d5d69b4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_stats_response_base.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2012 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_STATS_RESPONSE_BASE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_STATS_RESPONSE_BASE_H_
+
+#include "third_party/blink/public/platform/web_rtc_legacy_stats.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+class RTCStatsResponseBase : public ScriptWrappable {
+ public:
+ virtual ~RTCStatsResponseBase() = default;
+
+ virtual void AddStats(const WebRTCLegacyStats&) = 0;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_STATS_RESPONSE_BASE_H_
diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_void_request.h b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_void_request.h
new file mode 100644
index 00000000000..d6bac8af610
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_void_request.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ * 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 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_VOID_REQUEST_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_VOID_REQUEST_H_
+
+#include "third_party/blink/public/platform/web_rtc_error.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+class RTCVoidRequest : public GarbageCollectedFinalized<RTCVoidRequest> {
+ public:
+ virtual ~RTCVoidRequest() = default;
+
+ virtual void RequestSucceeded() = 0;
+ virtual void RequestFailed(const WebRTCError&) = 0;
+
+ virtual void Trace(blink::Visitor* visitor) {}
+
+ protected:
+ RTCVoidRequest() = default;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_VOID_REQUEST_H_
diff --git a/chromium/third_party/blink/renderer/platform/platform.gni b/chromium/third_party/blink/renderer/platform/platform.gni
new file mode 100644
index 00000000000..1afa35e16a2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/platform.gni
@@ -0,0 +1,58 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/jumbo.gni")
+
+platform_config_add = [
+ "//build/config/compiler:no_size_t_to_int_warning",
+ "//build/config/compiler:wexit_time_destructors",
+ "//third_party/blink/renderer:config",
+ "//third_party/blink/renderer:inside_blink",
+ "//third_party/blink/renderer:non_test_config",
+ "//third_party/blink/renderer/platform:blink_platform_config",
+ "//third_party/blink/renderer/platform:blink_platform_implementation",
+ "//third_party/blink/renderer/platform:blink_platform_pch",
+]
+platform_config_remove = []
+
+template("blink_platform_sources") {
+ jumbo_source_set(target_name) {
+ # Only platform can directly depend on this.
+ # Any target outside platform should instead depend on platform.
+ visibility = [ "//third_party/blink/renderer/platform" ]
+
+ assert(
+ !defined(invoker.public_deps),
+ "$target_name's public_deps should be moved to //third_party/blink/renderer/platform:blink_platform_public_deps")
+ deps = [
+ "//third_party/blink/renderer/platform:blink_platform_public_deps",
+ ]
+ if (defined(invoker.deps)) {
+ deps += invoker.deps
+ }
+
+ assert(
+ !defined(invoker.public_configs),
+ "$target_name's public_configs should be moved to //third_party/blink/renderer/platform:blink_platform_public_deps")
+ assert(
+ !defined(invoker.all_dependent_configs),
+ "$target_name's all_dependent_configs should be moved to //third_party/blink/renderer/platform:blink_platform_public_deps")
+ configs = []
+ if (defined(invoker.configs)) {
+ configs += invoker.configs
+ }
+
+ forward_variables_from(invoker,
+ "*",
+ [
+ "deps",
+ "configs",
+ ])
+ }
+}
+
+set_defaults("blink_platform_sources") {
+ configs =
+ default_compiler_configs + platform_config_add - platform_config_remove
+}
diff --git a/chromium/third_party/blink/renderer/platform/platform_chrome_client.h b/chromium/third_party/blink/renderer/platform/platform_chrome_client.h
new file mode 100644
index 00000000000..7e5695cd79a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/platform_chrome_client.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2008 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_PLATFORM_CHROME_CLIENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_PLATFORM_CHROME_CLIENT_H_
+
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/platform_frame_view.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class IntRect;
+
+class PLATFORM_EXPORT PlatformChromeClient
+ : public GarbageCollectedFinalized<PlatformChromeClient> {
+ WTF_MAKE_NONCOPYABLE(PlatformChromeClient);
+
+ public:
+ PlatformChromeClient() = default;
+ virtual ~PlatformChromeClient() = default;
+ virtual void Trace(blink::Visitor* visitor) {}
+
+ // Requests the host invalidate the contents.
+ virtual void InvalidateRect(const IntRect& update_rect) = 0;
+
+ // Converts the rect from the viewport coordinates to screen coordinates.
+ virtual IntRect ViewportToScreen(const IntRect&,
+ const PlatformFrameView*) const = 0;
+
+ // Converts the scalar value from the window coordinates to the viewport
+ // scale.
+ virtual float WindowToViewportScalar(const float) const = 0;
+
+ virtual void ScheduleAnimation(const PlatformFrameView*) = 0;
+
+ virtual bool IsPopup() { return false; }
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_PLATFORM_CHROME_CLIENT_H_
diff --git a/chromium/third_party/blink/renderer/platform/platform_export.h b/chromium/third_party/blink/renderer/platform/platform_export.h
new file mode 100644
index 00000000000..50043a2b2c6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/platform_export.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2013 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 header defines the following macros to export component's symbols.
+//
+// - PLATFORM_EXPORT
+// Exports non-template symbols.
+//
+// - PLATFORM_TEMPLATE_CLASS_EXPORT
+// Exports an entire definition of class template.
+//
+// - PLATFORM_EXTERN_TEMPLATE_EXPORT
+// Applicable to template declarations (except for definitions). The
+// corresponding definition must come along with PLATFORM_TEMPLATE_EXPORT.
+// Template specialization uses this macro to declare that such a
+// specialization exists without providing an actual definition.
+//
+// - PLATFORM_TEMPLATE_EXPORT
+// Applicable to template definitions whose declarations are annotated
+// with PLATFORM_EXTERN_TEMPLATE_EXPORT. Template specialization uses this
+// macro to provide an actual definition.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_PLATFORM_EXPORT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_PLATFORM_EXPORT_H_
+
+#include "build/build_config.h"
+
+//
+// BLINK_PLATFORM_IMPLEMENTATION
+//
+#if !defined(BLINK_PLATFORM_IMPLEMENTATION)
+#define BLINK_PLATFORM_IMPLEMENTATION 0
+#endif
+
+//
+// PLATFORM_EXPORT
+//
+#if !defined(COMPONENT_BUILD)
+#define PLATFORM_EXPORT // No need of export
+#else
+
+#if defined(COMPILER_MSVC)
+#if BLINK_PLATFORM_IMPLEMENTATION
+#define PLATFORM_EXPORT __declspec(dllexport)
+#else
+#define PLATFORM_EXPORT __declspec(dllimport)
+#endif
+#endif // defined(COMPILER_MSVC)
+
+#if defined(COMPILER_GCC)
+#if BLINK_PLATFORM_IMPLEMENTATION
+#define PLATFORM_EXPORT __attribute__((visibility("default")))
+#else
+#define PLATFORM_EXPORT
+#endif
+#endif // defined(COMPILER_GCC)
+
+#endif // !defined(COMPONENT_BUILD)
+
+//
+// PLATFORM_TEMPLATE_CLASS_EXPORT
+// PLATFORM_EXTERN_TEMPLATE_EXPORT
+// PLATFORM_TEMPLATE_EXPORT
+//
+#if BLINK_PLATFORM_IMPLEMENTATION
+
+#if defined(COMPILER_MSVC)
+#define PLATFORM_TEMPLATE_CLASS_EXPORT
+#define PLATFORM_EXTERN_TEMPLATE_EXPORT PLATFORM_EXPORT
+#define PLATFORM_TEMPLATE_EXPORT PLATFORM_EXPORT
+#endif
+
+#if defined(COMPILER_GCC)
+#define PLATFORM_TEMPLATE_CLASS_EXPORT PLATFORM_EXPORT
+#define PLATFORM_EXTERN_TEMPLATE_EXPORT PLATFORM_EXPORT
+#define PLATFORM_TEMPLATE_EXPORT
+#endif
+
+#else // BLINK_PLATFORM_IMPLEMENTATION
+
+#define PLATFORM_TEMPLATE_CLASS_EXPORT
+#define PLATFORM_EXTERN_TEMPLATE_EXPORT PLATFORM_EXPORT
+#define PLATFORM_TEMPLATE_EXPORT
+
+#endif // BLINK_PLATFORM_IMPLEMENTATION
+
+#if defined(COMPILER_MSVC)
+// MSVC Compiler warning C4275:
+// non dll-interface class 'Bar' used as base for dll-interface class 'Foo'.
+// Note that this is intended to be used only when no access to the base class'
+// static data is done through derived classes or inline methods. For more info,
+// see http://msdn.microsoft.com/en-us/library/3tdb471s(VS.80).aspx
+//
+// This pragma will allow exporting a class that inherits from a non-exported
+// base class, anywhere in the Blink platform component. This is only
+// a problem when using the MSVC compiler on Windows.
+#pragma warning(suppress : 4275)
+#endif
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_PLATFORM_EXPORT_H_
diff --git a/chromium/third_party/blink/renderer/platform/platform_frame_view.h b/chromium/third_party/blink/renderer/platform/platform_frame_view.h
new file mode 100644
index 00000000000..78a4136569d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/platform_frame_view.h
@@ -0,0 +1,25 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_PLATFORM_FRAME_VIEW_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_PLATFORM_FRAME_VIEW_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+
+// PlatformFrameView is a base class for core/frame/LocalFrameView.
+// PlatformFrameView is needed to let the platform/ layer access functionalities
+// of LocalFrameView.
+class PLATFORM_EXPORT PlatformFrameView {
+ public:
+ PlatformFrameView() = default;
+ virtual ~PlatformFrameView() = default;
+
+ virtual bool IsLocalFrameView() const { return false; }
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_PLATFORM_FRAME_VIEW_H_
diff --git a/chromium/third_party/blink/renderer/platform/platform_generated.gni b/chromium/third_party/blink/renderer/platform/platform_generated.gni
new file mode 100644
index 00000000000..33edb402051
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/platform_generated.gni
@@ -0,0 +1,5 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+blink_platform_output_dir = "$root_gen_dir/third_party/blink/renderer/platform"
diff --git a/chromium/third_party/blink/renderer/platform/plugins/plugin_data.cc b/chromium/third_party/blink/renderer/platform/plugins/plugin_data.cc
new file mode 100644
index 00000000000..73d10a188b2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/plugins/plugin_data.cc
@@ -0,0 +1,139 @@
+/*
+ Copyright (C) 2000 Harri Porten (porten@kde.org)
+ Copyright (C) 2000 Daniel Molkentin (molkentin@kde.org)
+ Copyright (C) 2000 Stefan Schimanski (schimmi@kde.org)
+ Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All Rights Reserved.
+ Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "third_party/blink/renderer/platform/plugins/plugin_data.h"
+
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_security_origin.h"
+#include "third_party/blink/renderer/platform/plugins/plugin_list_builder.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+
+namespace blink {
+
+void MimeClassInfo::Trace(blink::Visitor* visitor) {
+ visitor->Trace(plugin_);
+}
+
+MimeClassInfo::MimeClassInfo(const String& type,
+ const String& description,
+ PluginInfo& plugin)
+ : type_(type), description_(description), plugin_(&plugin) {}
+
+void PluginInfo::Trace(blink::Visitor* visitor) {
+ visitor->Trace(mimes_);
+}
+
+PluginInfo::PluginInfo(const String& name,
+ const String& filename,
+ const String& description,
+ Color background_color)
+ : name_(name),
+ filename_(filename),
+ description_(description),
+ background_color_(background_color) {}
+
+void PluginInfo::AddMimeType(MimeClassInfo* info) {
+ mimes_.push_back(info);
+}
+
+const MimeClassInfo* PluginInfo::GetMimeClassInfo(size_t index) const {
+ if (index >= mimes_.size())
+ return nullptr;
+ return mimes_[index];
+}
+
+const MimeClassInfo* PluginInfo::GetMimeClassInfo(const String& type) const {
+ for (MimeClassInfo* mime : mimes_) {
+ if (mime->Type() == type)
+ return mime;
+ }
+
+ return nullptr;
+}
+
+size_t PluginInfo::GetMimeClassInfoSize() const {
+ return mimes_.size();
+}
+
+void PluginData::Trace(blink::Visitor* visitor) {
+ visitor->Trace(plugins_);
+ visitor->Trace(mimes_);
+}
+
+// static
+void PluginData::RefreshBrowserSidePluginCache() {
+ PluginListBuilder builder(nullptr);
+ Platform::Current()->GetPluginList(true, WebSecurityOrigin::CreateUnique(),
+ &builder);
+}
+
+void PluginData::UpdatePluginList(const SecurityOrigin* main_frame_origin) {
+ ResetPluginData();
+ main_frame_origin_ = main_frame_origin;
+ PluginListBuilder builder(&plugins_);
+ Platform::Current()->GetPluginList(
+ false, WebSecurityOrigin(main_frame_origin_), &builder);
+
+ for (PluginInfo* plugin_info : plugins_) {
+ for (MimeClassInfo* mime_class_info : plugin_info->mimes_)
+ mimes_.push_back(mime_class_info);
+ }
+
+ std::sort(
+ plugins_.begin(), plugins_.end(),
+ [](const Member<PluginInfo>& lhs, const Member<PluginInfo>& rhs) -> bool {
+ return WTF::CodePointCompareLessThan(lhs->Name(), rhs->Name());
+ });
+ std::sort(mimes_.begin(), mimes_.end(),
+ [](const Member<MimeClassInfo>& lhs,
+ const Member<MimeClassInfo>& rhs) -> bool {
+ return WTF::CodePointCompareLessThan(lhs->Type(), rhs->Type());
+ });
+}
+
+void PluginData::ResetPluginData() {
+ plugins_.clear();
+ mimes_.clear();
+ main_frame_origin_ = nullptr;
+}
+
+bool PluginData::SupportsMimeType(const String& mime_type) const {
+ for (const MimeClassInfo* info : mimes_) {
+ if (info->type_ == mime_type)
+ return true;
+ }
+
+ return false;
+}
+
+Color PluginData::PluginBackgroundColorForMimeType(
+ const String& mime_type) const {
+ for (const MimeClassInfo* info : mimes_) {
+ if (info->type_ == mime_type)
+ return info->Plugin()->BackgroundColor();
+ }
+ NOTREACHED();
+ return Color();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/plugins/plugin_data.h b/chromium/third_party/blink/renderer/platform/plugins/plugin_data.h
new file mode 100644
index 00000000000..f1a78d3e0d5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/plugins/plugin_data.h
@@ -0,0 +1,124 @@
+
+/*
+ Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_PLUGINS_PLUGIN_DATA_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_PLUGINS_PLUGIN_DATA_H_
+
+#include "third_party/blink/renderer/platform/graphics/color.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class PluginInfo;
+
+class PLATFORM_EXPORT MimeClassInfo final
+ : public GarbageCollectedFinalized<MimeClassInfo> {
+ public:
+ void Trace(blink::Visitor*);
+
+ MimeClassInfo(const String& type, const String& desc, PluginInfo&);
+
+ const String& Type() const { return type_; }
+ const String& Description() const { return description_; }
+ const Vector<String>& Extensions() const { return extensions_; }
+ const PluginInfo* Plugin() const { return plugin_; }
+
+ private:
+ friend class PluginData;
+ friend class PluginListBuilder;
+
+ String type_;
+ String description_;
+ Vector<String> extensions_;
+ Member<PluginInfo> plugin_;
+};
+
+class PLATFORM_EXPORT PluginInfo final
+ : public GarbageCollectedFinalized<PluginInfo> {
+ public:
+ void Trace(blink::Visitor*);
+
+ PluginInfo(const String& name,
+ const String& filename,
+ const String& desc,
+ Color background_color);
+
+ void AddMimeType(MimeClassInfo*);
+
+ const HeapVector<Member<MimeClassInfo>>& Mimes() const { return mimes_; }
+ const MimeClassInfo* GetMimeClassInfo(size_t index) const;
+ const MimeClassInfo* GetMimeClassInfo(const String& type) const;
+ size_t GetMimeClassInfoSize() const;
+
+ const String& Name() const { return name_; }
+ const String& Filename() const { return filename_; }
+ const String& Description() const { return description_; }
+ Color BackgroundColor() const { return background_color_; }
+
+ private:
+ friend class MimeClassInfo;
+ friend class PluginData;
+ friend class PluginListBuilder;
+
+ String name_;
+ String filename_;
+ String description_;
+ Color background_color_;
+ HeapVector<Member<MimeClassInfo>> mimes_;
+};
+
+class PLATFORM_EXPORT PluginData final
+ : public GarbageCollectedFinalized<PluginData> {
+ WTF_MAKE_NONCOPYABLE(PluginData);
+
+ public:
+ void Trace(blink::Visitor*);
+
+ static PluginData* Create() { return new PluginData(); }
+
+ const HeapVector<Member<PluginInfo>>& Plugins() const { return plugins_; }
+ const HeapVector<Member<MimeClassInfo>>& Mimes() const { return mimes_; }
+ const SecurityOrigin* Origin() const { return main_frame_origin_.get(); }
+ void UpdatePluginList(const SecurityOrigin* main_frame_origin);
+ void ResetPluginData();
+
+ bool SupportsMimeType(const String& mime_type) const;
+ Color PluginBackgroundColorForMimeType(const String& mime_type) const;
+
+ // refreshBrowserSidePluginCache doesn't update existent instances of
+ // PluginData.
+ static void RefreshBrowserSidePluginCache();
+
+ private:
+ PluginData() = default;
+
+ HeapVector<Member<PluginInfo>> plugins_;
+ HeapVector<Member<MimeClassInfo>> mimes_;
+ scoped_refptr<const SecurityOrigin> main_frame_origin_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/plugins/plugin_list_builder.cc b/chromium/third_party/blink/renderer/platform/plugins/plugin_list_builder.cc
new file mode 100644
index 00000000000..26d6f01a055
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/plugins/plugin_list_builder.cc
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/renderer/platform/plugins/plugin_list_builder.h"
+
+#include "third_party/blink/public/platform/web_string.h"
+
+namespace blink {
+
+void PluginListBuilder::AddPlugin(const WebString& name,
+ const WebString& description,
+ const WebString& file_name,
+ WebColor background_color) {
+ if (results_) {
+ results_->push_back(
+ new PluginInfo(name, file_name, description, background_color));
+ }
+}
+
+void PluginListBuilder::AddMediaTypeToLastPlugin(const WebString& name,
+ const WebString& description) {
+ if (results_) {
+ MimeClassInfo* info =
+ new MimeClassInfo(name, description, *results_->back());
+ results_->back()->AddMimeType(info);
+ }
+}
+
+void PluginListBuilder::AddFileExtensionToLastMediaType(
+ const WebString& extension) {
+ if (results_) {
+ MimeClassInfo& info = *results_->back()->mimes_.back();
+ info.extensions_.push_back(extension);
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/plugins/plugin_list_builder.h b/chromium/third_party/blink/renderer/platform/plugins/plugin_list_builder.h
new file mode 100644
index 00000000000..4578ae8742c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/plugins/plugin_list_builder.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_PLUGINS_PLUGIN_LIST_BUILDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_PLUGINS_PLUGIN_LIST_BUILDER_H_
+
+#include "third_party/blink/public/platform/web_plugin_list_builder.h"
+#include "third_party/blink/renderer/platform/plugins/plugin_data.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class PluginListBuilder final : public WebPluginListBuilder {
+ STACK_ALLOCATED();
+
+ public:
+ PluginListBuilder(HeapVector<Member<PluginInfo>>* results)
+ : results_(results) {}
+
+ // WebPluginListBuilder methods:
+ void AddPlugin(const WebString& name,
+ const WebString& description,
+ const WebString& file_name,
+ WebColor background_color) override;
+ void AddMediaTypeToLastPlugin(const WebString& name,
+ const WebString& description) override;
+ void AddFileExtensionToLastMediaType(const WebString& extension) override;
+
+ private:
+ HeapVector<Member<PluginInfo>>* results_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/plugins/plugin_script_forbidden_scope.cc b/chromium/third_party/blink/renderer/platform/plugins/plugin_script_forbidden_scope.cc
new file mode 100644
index 00000000000..abdcc8b53eb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/plugins/plugin_script_forbidden_scope.cc
@@ -0,0 +1,29 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/plugins/plugin_script_forbidden_scope.h"
+
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+namespace blink {
+
+static unsigned g_plugin_script_forbidden_count = 0;
+
+PluginScriptForbiddenScope::PluginScriptForbiddenScope() {
+ DCHECK(IsMainThread());
+ ++g_plugin_script_forbidden_count;
+}
+
+PluginScriptForbiddenScope::~PluginScriptForbiddenScope() {
+ DCHECK(IsMainThread());
+ DCHECK(g_plugin_script_forbidden_count);
+ --g_plugin_script_forbidden_count;
+}
+
+bool PluginScriptForbiddenScope::IsForbidden() {
+ DCHECK(IsMainThread());
+ return g_plugin_script_forbidden_count > 0;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/plugins/plugin_script_forbidden_scope.h b/chromium/third_party/blink/renderer/platform/plugins/plugin_script_forbidden_scope.h
new file mode 100644
index 00000000000..78b34b447d1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/plugins/plugin_script_forbidden_scope.h
@@ -0,0 +1,54 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_PLUGINS_PLUGIN_SCRIPT_FORBIDDEN_SCOPE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_PLUGINS_PLUGIN_SCRIPT_FORBIDDEN_SCOPE_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+// Similar to ScriptForbiddenScope, but more selective. This is intended to help
+// reduce the number of places where Flash can run a nested run loop as its
+// plugin element is being destroyed. One of the reasons that Flash runs this
+// nested run loop is to allow Flash content to synchronously script the
+// page when the plugin element is destroyed.
+//
+// This is problematic for many reasons: the DOM may not be in a consistent
+// state, since Blink is in the middle of detaching nodes, nested run loops
+// can cause normally impossible conditions to occur (https://crbug.com/367210),
+// etc.
+//
+// When this object is instantiated on the stack, it allows execution of event
+// handlers, etc but blocks attempts by plugins to call back into Blink to
+// execute script.
+//
+// Background:
+// For historical reasons, Flash has allowed synchronous scripting during
+// teardown of the plugin. This is generally problematic, but sites apparently
+// rely on this behavior. Over time, Blink has added restrictions on this
+// synchronous scripting: for example, past a certain point in Frame detach,
+// script execution by Flash is ignored: https://crbug.com/371084.
+//
+// Unfortunately, there are still ways for plugins to synchronously script
+// during Document detach: if an unload handler removes a Flash plugin element,
+// that will run the nested run loop, etc. This scoper is intended to block
+// those usages, with the eventual goal that Frame detach will never have to run
+// a nested run loop.
+class PLATFORM_EXPORT PluginScriptForbiddenScope final {
+ STACK_ALLOCATED();
+ WTF_MAKE_NONCOPYABLE(PluginScriptForbiddenScope);
+
+ public:
+ PluginScriptForbiddenScope();
+ ~PluginScriptForbiddenScope();
+
+ static bool IsForbidden();
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_PLUGINS_PLUGIN_SCRIPT_FORBIDDEN_SCOPE_H_
diff --git a/chromium/third_party/blink/renderer/platform/png_fuzzer.cc b/chromium/third_party/blink/renderer/platform/png_fuzzer.cc
new file mode 100644
index 00000000000..8428ae133c0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/png_fuzzer.cc
@@ -0,0 +1,63 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// TODO (scroggo): Move this to
+// third_party/WebKit/Source/platform/image-decoders ?
+
+// Compile with:
+// gn gen out/Fuzz '--args=use_libfuzzer=true is_asan=true
+// is_debug=false is_ubsan_security=true' --check
+// ninja -C out/Fuzz blink_png_decoder_fuzzer
+//
+// Run with:
+// ./out/Fuzz/blink_png_decoder_fuzzer
+// third_party/WebKit/LayoutTests/images/resources/pngfuzz
+//
+// Alternatively, it can be run with:
+// ./out/Fuzz/blink_png_decoder_fuzzer ~/another_dir_to_store_corpus
+// third_party/WebKit/LayoutTests/images/resources/pngfuzz
+//
+// so the fuzzer will read both directories passed, but all new generated
+// testcases will go into ~/another_dir_to_store_corpus
+//
+// For more details, see
+// https://chromium.googlesource.com/chromium/src/+/master/testing/libfuzzer/README.md
+
+#include <memory>
+
+#include "third_party/blink/renderer/platform/image-decoders/png/png_image_decoder.h"
+#include "third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.h"
+
+namespace blink {
+
+std::unique_ptr<ImageDecoder> CreateDecoder(
+ ImageDecoder::AlphaOption alpha_option) {
+ return std::make_unique<PNGImageDecoder>(
+ alpha_option, ColorBehavior::TransformToSRGB(),
+ ImageDecoder::kNoDecodedImageByteLimit);
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ static BlinkFuzzerTestSupport test_support = BlinkFuzzerTestSupport();
+ auto buffer = SharedBuffer::Create(data, size);
+ // TODO (scroggo): Also test ImageDecoder::AlphaNotPremultiplied?
+ auto decoder = CreateDecoder(ImageDecoder::kAlphaPremultiplied);
+ const bool kAllDataReceived = true;
+ decoder->SetData(buffer.get(), kAllDataReceived);
+ decoder->FrameCount();
+ if (decoder->Failed())
+ return 0;
+ for (size_t frame = 0; frame < decoder->FrameCount(); frame++) {
+ decoder->DecodeFrameBufferAtIndex(frame);
+ if (decoder->Failed())
+ return 0;
+ }
+ return 0;
+}
+
+} // namespace blink
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ return blink::LLVMFuzzerTestOneInput(data, size);
+}
diff --git a/chromium/third_party/blink/renderer/platform/pod_arena.h b/chromium/third_party/blink/renderer/platform/pod_arena.h
new file mode 100644
index 00000000000..75a08c1dd79
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/pod_arena.h
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_POD_ARENA_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_POD_ARENA_H_
+
+#include <stdint.h>
+#include <memory>
+#include <utility>
+
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+// An arena which allocates only Plain Old Data (POD), or classes and
+// structs bottoming out in Plain Old Data. NOTE: the constructors of
+// the objects allocated in this arena are called, but _not_ their
+// destructors.
+
+class PODArena final : public RefCounted<PODArena> {
+ public:
+ // The arena is configured with an allocator, which is responsible
+ // for allocating and freeing chunks of memory at a time.
+ class Allocator : public RefCounted<Allocator> {
+ public:
+ virtual void* Allocate(size_t size) = 0;
+ virtual void Free(void* ptr) = 0;
+
+ protected:
+ virtual ~Allocator() = default;
+ friend class WTF::RefCounted<Allocator>;
+ };
+
+ // The Arena's default allocator, which uses fastMalloc and
+ // fastFree to allocate chunks of storage.
+ class FastMallocAllocator : public Allocator {
+ public:
+ static scoped_refptr<FastMallocAllocator> Create() {
+ return base::AdoptRef(new FastMallocAllocator);
+ }
+
+ void* Allocate(size_t size) override {
+ return WTF::Partitions::FastMalloc(size,
+ WTF_HEAP_PROFILER_TYPE_NAME(PODArena));
+ }
+ void Free(void* ptr) override { WTF::Partitions::FastFree(ptr); }
+
+ protected:
+ FastMallocAllocator() = default;
+ };
+
+ // Creates a new PODArena configured with a FastMallocAllocator.
+ static scoped_refptr<PODArena> Create() {
+ return base::AdoptRef(new PODArena);
+ }
+
+ // Creates a new PODArena configured with the given Allocator.
+ static scoped_refptr<PODArena> Create(scoped_refptr<Allocator> allocator) {
+ return base::AdoptRef(new PODArena(std::move(allocator)));
+ }
+
+ // Allocates an object from the arena.
+ template <class T>
+ T* AllocateObject() {
+ return new (AllocateBase<T>()) T();
+ }
+
+ // Allocates an object from the arena, calling a single-argument constructor.
+ template <class T, class Argument1Type>
+ T* AllocateObject(const Argument1Type& argument1) {
+ return new (AllocateBase<T>()) T(argument1);
+ }
+
+ // The initial size of allocated chunks; increases as necessary to
+ // satisfy large allocations. Mainly public for unit tests.
+ enum { kDefaultChunkSize = 16384 };
+
+ protected:
+ friend class WTF::RefCounted<PODArena>;
+
+ PODArena()
+ : allocator_(FastMallocAllocator::Create()),
+ current_(nullptr),
+ current_chunk_size_(kDefaultChunkSize) {}
+
+ explicit PODArena(scoped_refptr<Allocator> allocator)
+ : allocator_(std::move(allocator)),
+ current_(nullptr),
+ current_chunk_size_(kDefaultChunkSize) {}
+
+ // Returns the alignment requirement for classes and structs on the
+ // current platform.
+ template <class T>
+ static size_t MinAlignment() {
+ return WTF_ALIGN_OF(T);
+ }
+
+ template <class T>
+ void* AllocateBase() {
+ void* ptr = nullptr;
+ size_t rounded_size = RoundUp(sizeof(T), MinAlignment<T>());
+ if (current_)
+ ptr = current_->Allocate(rounded_size);
+
+ if (!ptr) {
+ if (rounded_size > current_chunk_size_)
+ current_chunk_size_ = rounded_size;
+ chunks_.push_back(
+ std::make_unique<Chunk>(allocator_.get(), current_chunk_size_));
+ current_ = chunks_.back().get();
+ ptr = current_->Allocate(rounded_size);
+ }
+ return ptr;
+ }
+
+ // Rounds up the given allocation size to the specified alignment.
+ size_t RoundUp(size_t size, size_t alignment) {
+ DCHECK(!(alignment % 2));
+ return (size + alignment - 1) & ~(alignment - 1);
+ }
+
+ // Manages a chunk of memory and individual allocations out of it.
+ class Chunk final {
+ USING_FAST_MALLOC(Chunk);
+ WTF_MAKE_NONCOPYABLE(Chunk);
+
+ public:
+ // Allocates a block of memory of the given size from the passed
+ // Allocator.
+ Chunk(Allocator* allocator, size_t size)
+ : allocator_(allocator), size_(size), current_offset_(0) {
+ base_ = static_cast<uint8_t*>(allocator_->Allocate(size));
+ }
+
+ // Frees the memory allocated from the Allocator in the
+ // constructor.
+ ~Chunk() { allocator_->Free(base_); }
+
+ // Returns a pointer to "size" bytes of storage, or 0 if this
+ // Chunk could not satisfy the allocation.
+ void* Allocate(size_t size) {
+ // Check for overflow
+ if (current_offset_ + size < current_offset_)
+ return nullptr;
+
+ if (current_offset_ + size > size_)
+ return nullptr;
+
+ void* result = base_ + current_offset_;
+ current_offset_ += size;
+ return result;
+ }
+
+ protected:
+ Allocator* allocator_;
+ uint8_t* base_;
+ size_t size_;
+ size_t current_offset_;
+ };
+
+ scoped_refptr<Allocator> allocator_;
+ Chunk* current_;
+ size_t current_chunk_size_;
+ Vector<std::unique_ptr<Chunk>> chunks_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_POD_ARENA_H_
diff --git a/chromium/third_party/blink/renderer/platform/pod_arena_test.cc b/chromium/third_party/blink/renderer/platform/pod_arena_test.cc
new file mode 100644
index 00000000000..c1f07840576
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/pod_arena_test.cc
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/pod_arena.h"
+
+#include <algorithm>
+#include "base/memory/scoped_refptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/testing/arena_test_helpers.h"
+
+namespace blink {
+
+using ArenaTestHelpers::TrackedAllocator;
+
+namespace {
+
+// A couple of simple structs to allocate.
+struct TestClassXYZW {
+ TestClassXYZW() : x(0), y(0), z(0), w(1) {}
+
+ float x, y, z, w;
+};
+
+struct TestClassABCD {
+ TestClassABCD() : a(1), b(2), c(3), d(4) {}
+
+ float a, b, c, d;
+};
+
+} // anonymous namespace
+
+class PODArenaTest : public testing::Test {};
+
+// Make sure the arena can successfully allocate from more than one
+// region.
+TEST_F(PODArenaTest, CanAllocateFromMoreThanOneRegion) {
+ scoped_refptr<TrackedAllocator> allocator = TrackedAllocator::Create();
+ scoped_refptr<PODArena> arena = PODArena::Create(allocator);
+ int num_iterations = 10 * PODArena::kDefaultChunkSize / sizeof(TestClassXYZW);
+ for (int i = 0; i < num_iterations; ++i)
+ arena->AllocateObject<TestClassXYZW>();
+ EXPECT_GT(allocator->NumRegions(), 1);
+}
+
+// Make sure the arena frees all allocated regions during destruction.
+TEST_F(PODArenaTest, FreesAllAllocatedRegions) {
+ scoped_refptr<TrackedAllocator> allocator = TrackedAllocator::Create();
+ {
+ scoped_refptr<PODArena> arena = PODArena::Create(allocator);
+ for (int i = 0; i < 3; i++)
+ arena->AllocateObject<TestClassXYZW>();
+ EXPECT_GT(allocator->NumRegions(), 0);
+ }
+ EXPECT_TRUE(allocator->IsEmpty());
+}
+
+// Make sure the arena runs constructors of the objects allocated within.
+TEST_F(PODArenaTest, RunsConstructors) {
+ scoped_refptr<PODArena> arena = PODArena::Create();
+ for (int i = 0; i < 10000; i++) {
+ TestClassXYZW* tc1 = arena->AllocateObject<TestClassXYZW>();
+ EXPECT_EQ(0, tc1->x);
+ EXPECT_EQ(0, tc1->y);
+ EXPECT_EQ(0, tc1->z);
+ EXPECT_EQ(1, tc1->w);
+ TestClassABCD* tc2 = arena->AllocateObject<TestClassABCD>();
+ EXPECT_EQ(1, tc2->a);
+ EXPECT_EQ(2, tc2->b);
+ EXPECT_EQ(3, tc2->c);
+ EXPECT_EQ(4, tc2->d);
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/pod_free_list_arena.h b/chromium/third_party/blink/renderer/platform/pod_free_list_arena.h
new file mode 100644
index 00000000000..3cab486191f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/pod_free_list_arena.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2011 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_POD_FREE_LIST_ARENA_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_POD_FREE_LIST_ARENA_H_
+
+#include "third_party/blink/renderer/platform/pod_arena.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+
+namespace blink {
+
+template <class T>
+class PODFreeListArena : public RefCounted<PODFreeListArena<T>> {
+ public:
+ static scoped_refptr<PODFreeListArena> Create() {
+ return base::AdoptRef(new PODFreeListArena);
+ }
+
+ // Creates a new PODFreeListArena configured with the given Allocator.
+ static scoped_refptr<PODFreeListArena> Create(
+ scoped_refptr<PODArena::Allocator> allocator) {
+ return base::AdoptRef(new PODFreeListArena(std::move(allocator)));
+ }
+
+ // Allocates an object from the arena.
+ T* AllocateObject() {
+ void* ptr = AllocateFromFreeList();
+
+ if (ptr) {
+ // Use placement operator new to allocate a T at this location.
+ new (ptr) T();
+ return static_cast<T*>(ptr);
+ }
+
+ // PODArena::allocateObject calls T's constructor.
+ return static_cast<T*>(arena_->AllocateObject<T>());
+ }
+
+ template <class Argument1Type>
+ T* AllocateObject(const Argument1Type& argument1) {
+ void* ptr = AllocateFromFreeList();
+
+ if (ptr) {
+ // Use placement operator new to allocate a T at this location.
+ new (ptr) T(argument1);
+ return static_cast<T*>(ptr);
+ }
+
+ // PODArena::allocateObject calls T's constructor.
+ return static_cast<T*>(arena_->AllocateObject<T>(argument1));
+ }
+
+ void FreeObject(T* ptr) {
+ FixedSizeMemoryChunk* old_free_list = free_list_;
+
+ free_list_ = reinterpret_cast<FixedSizeMemoryChunk*>(ptr);
+ free_list_->next = old_free_list;
+ }
+
+ private:
+ PODFreeListArena() : arena_(PODArena::Create()), free_list_(nullptr) {}
+
+ explicit PODFreeListArena(scoped_refptr<PODArena::Allocator> allocator)
+ : arena_(PODArena::Create(std::move(allocator))), free_list_(nullptr) {}
+
+ ~PODFreeListArena() = default;
+
+ void* AllocateFromFreeList() {
+ if (free_list_) {
+ void* memory = free_list_;
+ free_list_ = free_list_->next;
+ return memory;
+ }
+ return nullptr;
+ }
+
+ int GetFreeListSizeForTesting() const {
+ int total = 0;
+ for (FixedSizeMemoryChunk* cur = free_list_; cur; cur = cur->next) {
+ total++;
+ }
+ return total;
+ }
+
+ scoped_refptr<PODArena> arena_;
+
+ // This free list contains pointers within every chunk that's been allocated
+ // so far. None of the individual chunks can be freed until the arena is
+ // destroyed.
+ struct FixedSizeMemoryChunk {
+ DISALLOW_NEW();
+ FixedSizeMemoryChunk* next;
+ };
+ FixedSizeMemoryChunk* free_list_;
+
+ static_assert(sizeof(T) >= sizeof(FixedSizeMemoryChunk),
+ "PODFreeListArena type should be larger");
+
+ friend class WTF::RefCounted<PODFreeListArena>;
+ friend class PODFreeListArenaTest;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/pod_free_list_arena_test.cc b/chromium/third_party/blink/renderer/platform/pod_free_list_arena_test.cc
new file mode 100644
index 00000000000..69fee7819e0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/pod_free_list_arena_test.cc
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2013 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:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/pod_free_list_arena.h"
+
+#include "base/memory/scoped_refptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/testing/arena_test_helpers.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+using ArenaTestHelpers::TrackedAllocator;
+
+namespace {
+
+// A couple of simple structs to allocate.
+struct TestClass1 {
+ TestClass1() : x(0), y(0), z(0), w(1) {}
+
+ float x, y, z, w;
+};
+
+struct TestClass2 {
+ TestClass2() : padding(0) {
+ static int test_ids = 0;
+ id = test_ids++;
+ }
+ int id;
+ int padding;
+};
+
+} // anonymous namespace
+
+class PODFreeListArenaTest : public testing::Test {
+ protected:
+ int GetFreeListSize(scoped_refptr<PODFreeListArena<TestClass1>> arena) const {
+ return arena->GetFreeListSizeForTesting();
+ }
+};
+
+// Make sure the arena can successfully allocate from more than one
+// region.
+TEST_F(PODFreeListArenaTest, CanAllocateFromMoreThanOneRegion) {
+ scoped_refptr<TrackedAllocator> allocator = TrackedAllocator::Create();
+ scoped_refptr<PODFreeListArena<TestClass1>> arena =
+ PODFreeListArena<TestClass1>::Create(allocator);
+ int num_iterations = 10 * PODArena::kDefaultChunkSize / sizeof(TestClass1);
+ for (int i = 0; i < num_iterations; ++i)
+ arena->AllocateObject();
+ EXPECT_GT(allocator->NumRegions(), 1);
+}
+
+// Make sure the arena frees all allocated regions during destruction.
+TEST_F(PODFreeListArenaTest, FreesAllAllocatedRegions) {
+ scoped_refptr<TrackedAllocator> allocator = TrackedAllocator::Create();
+ {
+ scoped_refptr<PODFreeListArena<TestClass1>> arena =
+ PODFreeListArena<TestClass1>::Create(allocator);
+ for (int i = 0; i < 3; i++)
+ arena->AllocateObject();
+ EXPECT_GT(allocator->NumRegions(), 0);
+ }
+ EXPECT_TRUE(allocator->IsEmpty());
+}
+
+// Make sure the arena runs constructors of the objects allocated within.
+TEST_F(PODFreeListArenaTest, RunsConstructorsOnNewObjects) {
+ scoped_refptr<PODFreeListArena<TestClass1>> arena =
+ PODFreeListArena<TestClass1>::Create();
+ for (int i = 0; i < 10000; i++) {
+ TestClass1* tc1 = arena->AllocateObject();
+ EXPECT_EQ(0, tc1->x);
+ EXPECT_EQ(0, tc1->y);
+ EXPECT_EQ(0, tc1->z);
+ EXPECT_EQ(1, tc1->w);
+ }
+}
+
+// Make sure the arena runs constructors of the objects allocated within.
+TEST_F(PODFreeListArenaTest, RunsConstructorsOnReusedObjects) {
+ std::set<TestClass1*> objects;
+ scoped_refptr<PODFreeListArena<TestClass1>> arena =
+ PODFreeListArena<TestClass1>::Create();
+ for (int i = 0; i < 100; i++) {
+ TestClass1* tc1 = arena->AllocateObject();
+ tc1->x = 100;
+ tc1->y = 101;
+ tc1->z = 102;
+ tc1->w = 103;
+
+ objects.insert(tc1);
+ }
+ for (std::set<TestClass1*>::iterator it = objects.begin();
+ it != objects.end(); ++it) {
+ arena->FreeObject(*it);
+ }
+ for (int i = 0; i < 100; i++) {
+ TestClass1* cur = arena->AllocateObject();
+ EXPECT_TRUE(objects.find(cur) != objects.end());
+ EXPECT_EQ(0, cur->x);
+ EXPECT_EQ(0, cur->y);
+ EXPECT_EQ(0, cur->z);
+ EXPECT_EQ(1, cur->w);
+
+ objects.erase(cur);
+ }
+}
+
+// Make sure freeObject puts the object in the free list.
+TEST_F(PODFreeListArenaTest, AddsFreedObjectsToFreedList) {
+ Vector<TestClass1*, 100> objects;
+ scoped_refptr<PODFreeListArena<TestClass1>> arena =
+ PODFreeListArena<TestClass1>::Create();
+ for (int i = 0; i < 100; i++) {
+ objects.push_back(arena->AllocateObject());
+ }
+ for (auto* object : objects) {
+ arena->FreeObject(object);
+ }
+ EXPECT_EQ(100, GetFreeListSize(arena));
+}
+
+// Make sure allocations use previously freed memory.
+TEST_F(PODFreeListArenaTest, ReusesPreviouslyFreedObjects) {
+ std::set<TestClass2*> objects;
+ scoped_refptr<PODFreeListArena<TestClass2>> arena =
+ PODFreeListArena<TestClass2>::Create();
+ for (int i = 0; i < 100; i++) {
+ objects.insert(arena->AllocateObject());
+ }
+ for (std::set<TestClass2*>::iterator it = objects.begin();
+ it != objects.end(); ++it) {
+ arena->FreeObject(*it);
+ }
+ for (int i = 0; i < 100; i++) {
+ TestClass2* cur = arena->AllocateObject();
+ EXPECT_TRUE(objects.find(cur) != objects.end());
+ EXPECT_TRUE(cur->id >= 100 && cur->id < 200);
+ objects.erase(cur);
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/pod_interval.h b/chromium/third_party/blink/renderer/platform/pod_interval.h
new file mode 100644
index 00000000000..48f8065ef33
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/pod_interval.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_POD_INTERVAL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_POD_INTERVAL_H_
+
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#ifndef NDEBUG
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+#endif
+
+namespace blink {
+
+// Class representing a closed interval which can hold an arbitrary
+// Plain Old Datatype (POD) as its endpoints and a piece of user
+// data. An important characteristic for the algorithms we use is that
+// if two intervals have identical endpoints but different user data,
+// they are not considered to be equal. This situation can arise when
+// representing the vertical extents of bounding boxes of overlapping
+// triangles, where the pointer to the triangle is the user data of
+// the interval.
+//
+// *Note* that the destructors of type T and UserData will *not* be
+// called by this class. They must not allocate any memory that is
+// required to be cleaned up in their destructors.
+//
+// The following constructors and operators must be implemented on
+// type T:
+//
+// - Copy constructor (if user data is desired)
+// - operator<
+// - operator==
+// - operator=
+//
+// If the UserData type is specified, it must support a copy
+// constructor and assignment operator.
+//
+// In debug mode, printing of intervals and the data they contain is
+// enabled. This requires the following template specializations to be
+// available:
+//
+// template<> struct ValueToString<T> {
+// static String toString(const T& t);
+// };
+// template<> struct ValueToString<UserData> {
+// static String toString(const UserData& t);
+// };
+//
+// Note that this class requires a copy constructor and assignment
+// operator in order to be stored in the red-black tree.
+
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+#ifndef NDEBUG
+template <class T>
+struct ValueToString;
+#endif
+
+template <class T, class UserData = void*>
+class PODInterval {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ // Constructor from endpoints. This constructor only works when the
+ // UserData type is a pointer or other type which can be initialized
+ // with 0.
+ PODInterval(const T& low, const T& high)
+ : low_(low), high_(high), data_(0), max_high_(high) {}
+
+ // Constructor from two endpoints plus explicit user data.
+ PODInterval(const T& low, const T& high, const UserData data)
+ : low_(low), high_(high), data_(data), max_high_(high) {}
+
+ const T& Low() const { return low_; }
+ const T& High() const { return high_; }
+ const UserData& Data() const { return data_; }
+
+ bool Overlaps(const T& low, const T& high) const {
+ if (this->High() < low)
+ return false;
+ if (high < this->Low())
+ return false;
+ return true;
+ }
+
+ bool Overlaps(const PODInterval& other) const {
+ return Overlaps(other.Low(), other.High());
+ }
+
+ // Returns true if this interval is "less" than the other. The
+ // comparison is performed on the low endpoints of the intervals.
+ bool operator<(const PODInterval& other) const { return Low() < other.Low(); }
+
+ // Returns true if this interval is strictly equal to the other,
+ // including comparison of the user data.
+ bool operator==(const PODInterval& other) const {
+ return (Low() == other.Low() && High() == other.High() &&
+ Data() == other.Data());
+ }
+
+ const T& MaxHigh() const { return max_high_; }
+ void SetMaxHigh(const T& max_high) { max_high_ = max_high; }
+
+#ifndef NDEBUG
+ // Support for printing PODIntervals.
+ String ToString() const {
+ StringBuilder builder;
+ builder.Append("[PODInterval (");
+ builder.Append(ValueToString<T>::ToString(Low()));
+ builder.Append(", ");
+ builder.Append(ValueToString<T>::ToString(High()));
+ builder.Append("), data=");
+ builder.Append(ValueToString<UserData>::ToString(Data()));
+ builder.Append(", maxHigh=");
+ builder.Append(ValueToString<T>::ToString(MaxHigh()));
+ builder.Append(']');
+ return builder.ToString();
+ }
+#endif
+
+ private:
+ T low_;
+ T high_;
+ GC_PLUGIN_IGNORE("crbug.com/513116")
+ UserData data_;
+ T max_high_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_POD_INTERVAL_H_
diff --git a/chromium/third_party/blink/renderer/platform/pod_interval_tree.h b/chromium/third_party/blink/renderer/platform/pod_interval_tree.h
new file mode 100644
index 00000000000..ad4e865d55f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/pod_interval_tree.h
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_POD_INTERVAL_TREE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_POD_INTERVAL_TREE_H_
+
+#include "third_party/blink/renderer/platform/pod_arena.h"
+#include "third_party/blink/renderer/platform/pod_interval.h"
+#include "third_party/blink/renderer/platform/pod_red_black_tree.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+#ifndef NDEBUG
+template <class T>
+struct ValueToString;
+#endif
+
+template <class T, class UserData = void*>
+class PODIntervalSearchAdapter {
+ DISALLOW_NEW();
+
+ public:
+ typedef PODInterval<T, UserData> IntervalType;
+
+ PODIntervalSearchAdapter(Vector<IntervalType>& result,
+ const T& low_value,
+ const T& high_value)
+ : result_(result), low_value_(low_value), high_value_(high_value) {}
+
+ const T& LowValue() const { return low_value_; }
+ const T& HighValue() const { return high_value_; }
+ void CollectIfNeeded(const IntervalType& data) const {
+ if (data.Overlaps(low_value_, high_value_))
+ result_.push_back(data);
+ }
+
+ private:
+ Vector<IntervalType>& result_;
+ T low_value_;
+ T high_value_;
+};
+
+// An interval tree, which is a form of augmented red-black tree. It
+// supports efficient (O(lg n)) insertion, removal and querying of
+// intervals in the tree.
+template <class T, class UserData = void*>
+class PODIntervalTree final : public PODRedBlackTree<PODInterval<T, UserData>> {
+ WTF_MAKE_NONCOPYABLE(PODIntervalTree);
+
+ public:
+ // Typedef to reduce typing when declaring intervals to be stored in
+ // this tree.
+ typedef PODInterval<T, UserData> IntervalType;
+ typedef PODIntervalSearchAdapter<T, UserData> IntervalSearchAdapterType;
+
+ PODIntervalTree(UninitializedTreeEnum unitialized_tree)
+ : PODRedBlackTree<IntervalType>(unitialized_tree) {
+ Init();
+ }
+
+ PODIntervalTree() : PODRedBlackTree<IntervalType>() { Init(); }
+
+ explicit PODIntervalTree(scoped_refptr<PODArena> arena)
+ : PODRedBlackTree<IntervalType>(arena) {
+ Init();
+ }
+
+ // Returns all intervals in the tree which overlap the given query
+ // interval. The returned intervals are sorted by increasing low
+ // endpoint.
+ Vector<IntervalType> AllOverlaps(const IntervalType& interval) const {
+ Vector<IntervalType> result;
+ AllOverlaps(interval, result);
+ return result;
+ }
+
+ // Returns all intervals in the tree which overlap the given query
+ // interval. The returned intervals are sorted by increasing low
+ // endpoint.
+ void AllOverlaps(const IntervalType& interval,
+ Vector<IntervalType>& result) const {
+ // Explicit dereference of "this" required because of
+ // inheritance rules in template classes.
+ IntervalSearchAdapterType adapter(result, interval.Low(), interval.High());
+ SearchForOverlapsFrom<IntervalSearchAdapterType>(this->Root(), adapter);
+ }
+
+ template <class AdapterType>
+ void AllOverlapsWithAdapter(AdapterType& adapter) const {
+ // Explicit dereference of "this" required because of
+ // inheritance rules in template classes.
+ SearchForOverlapsFrom<AdapterType>(this->Root(), adapter);
+ }
+
+ // Helper to create interval objects.
+ static IntervalType CreateInterval(const T& low,
+ const T& high,
+ const UserData data = nullptr) {
+ return IntervalType(low, high, data);
+ }
+
+ bool CheckInvariants() const override {
+ if (!PODRedBlackTree<IntervalType>::CheckInvariants())
+ return false;
+ if (!this->Root())
+ return true;
+ return CheckInvariantsFromNode(this->Root(), nullptr);
+ }
+
+ private:
+ typedef typename PODRedBlackTree<IntervalType>::Node IntervalNode;
+
+ // Initializes the tree.
+ void Init() {
+ // Explicit dereference of "this" required because of
+ // inheritance rules in template classes.
+ this->SetNeedsFullOrderingComparisons(true);
+ }
+
+ // Starting from the given node, adds all overlaps with the given
+ // interval to the result vector. The intervals are sorted by
+ // increasing low endpoint.
+ template <class AdapterType>
+ DISABLE_CFI_PERF void SearchForOverlapsFrom(IntervalNode* node,
+ AdapterType& adapter) const {
+ if (!node)
+ return;
+
+ // Because the intervals are sorted by left endpoint, inorder
+ // traversal produces results sorted as desired.
+
+ // See whether we need to traverse the left subtree.
+ IntervalNode* left = node->Left();
+ if (left
+ // This is phrased this way to avoid the need for operator
+ // <= on type T.
+ && !(left->Data().MaxHigh() < adapter.LowValue()))
+ SearchForOverlapsFrom<AdapterType>(left, adapter);
+
+ // Check for overlap with current node.
+ adapter.CollectIfNeeded(node->Data());
+
+ // See whether we need to traverse the right subtree.
+ // This is phrased this way to avoid the need for operator <=
+ // on type T.
+ if (!(adapter.HighValue() < node->Data().Low()))
+ SearchForOverlapsFrom<AdapterType>(node->Right(), adapter);
+ }
+
+ bool UpdateNode(IntervalNode* node) override {
+ // Would use const T&, but need to reassign this reference in this
+ // function.
+ const T* cur_max = &node->Data().High();
+ IntervalNode* left = node->Left();
+ if (left) {
+ if (*cur_max < left->Data().MaxHigh())
+ cur_max = &left->Data().MaxHigh();
+ }
+ IntervalNode* right = node->Right();
+ if (right) {
+ if (*cur_max < right->Data().MaxHigh())
+ cur_max = &right->Data().MaxHigh();
+ }
+ // This is phrased like this to avoid needing operator!= on type T.
+ if (!(*cur_max == node->Data().MaxHigh())) {
+ node->Data().SetMaxHigh(*cur_max);
+ return true;
+ }
+ return false;
+ }
+
+ bool CheckInvariantsFromNode(IntervalNode* node, T* current_max_value) const {
+ // These assignments are only done in order to avoid requiring
+ // a default constructor on type T.
+ T left_max_value(node->Data().MaxHigh());
+ T right_max_value(node->Data().MaxHigh());
+ IntervalNode* left = node->Left();
+ IntervalNode* right = node->Right();
+ if (left) {
+ if (!CheckInvariantsFromNode(left, &left_max_value))
+ return false;
+ }
+ if (right) {
+ if (!CheckInvariantsFromNode(right, &right_max_value))
+ return false;
+ }
+ if (!left && !right) {
+ // Base case.
+ if (current_max_value)
+ *current_max_value = node->Data().High();
+ return (node->Data().High() == node->Data().MaxHigh());
+ }
+ T local_max_value(node->Data().MaxHigh());
+ if (!left || !right) {
+ if (left)
+ local_max_value = left_max_value;
+ else
+ local_max_value = right_max_value;
+ } else {
+ local_max_value =
+ (left_max_value < right_max_value) ? right_max_value : left_max_value;
+ }
+ if (local_max_value < node->Data().High())
+ local_max_value = node->Data().High();
+ if (!(local_max_value == node->Data().MaxHigh())) {
+#ifndef NDEBUG
+ String local_max_value_string =
+ ValueToString<T>::ToString(local_max_value);
+ DLOG(ERROR) << "PODIntervalTree verification failed at node " << node
+ << ": localMaxValue=" << local_max_value_string
+ << " and data=" << node->Data().ToString();
+#endif
+ return false;
+ }
+ if (current_max_value)
+ *current_max_value = local_max_value;
+ return true;
+ }
+};
+
+#ifndef NDEBUG
+// Support for printing PODIntervals at the PODRedBlackTree level.
+template <class T, class UserData>
+struct ValueToString<PODInterval<T, UserData>> {
+ static String ToString(const PODInterval<T, UserData>& interval) {
+ return interval.ToString();
+ }
+};
+#endif
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_POD_INTERVAL_TREE_H_
diff --git a/chromium/third_party/blink/renderer/platform/pod_interval_tree_test.cc b/chromium/third_party/blink/renderer/platform/pod_interval_tree_test.cc
new file mode 100644
index 00000000000..ad20eb0b7e6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/pod_interval_tree_test.cc
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 the interval tree class.
+
+#include "third_party/blink/renderer/platform/pod_interval_tree.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/geometry/float_polygon.h"
+#include "third_party/blink/renderer/platform/testing/tree_test_helpers.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+using TreeTestHelpers::InitRandom;
+using TreeTestHelpers::NextRandom;
+
+#ifndef NDEBUG
+template <>
+struct ValueToString<void*> {
+ static String ToString(void* const& value) {
+ return String::Format("0x%p", value);
+ }
+};
+#endif
+
+TEST(PODIntervalTreeTest, TestInsertion) {
+ PODIntervalTree<float> tree;
+ tree.Add(PODInterval<float>(2, 4));
+ ASSERT_TRUE(tree.CheckInvariants());
+}
+
+TEST(PODIntervalTreeTest, TestInsertionAndQuery) {
+ PODIntervalTree<float> tree;
+ tree.Add(PODInterval<float>(2, 4));
+ ASSERT_TRUE(tree.CheckInvariants());
+ Vector<PODInterval<float>> result =
+ tree.AllOverlaps(PODInterval<float>(1, 3));
+ EXPECT_EQ(1U, result.size());
+ EXPECT_EQ(2, result[0].Low());
+ EXPECT_EQ(4, result[0].High());
+}
+
+TEST(PODIntervalTreeTest, TestQueryAgainstZeroSizeInterval) {
+ PODIntervalTree<float> tree;
+ tree.Add(PODInterval<float>(1, 2.5));
+ tree.Add(PODInterval<float>(3.5, 5));
+ tree.Add(PODInterval<float>(2, 4));
+ ASSERT_TRUE(tree.CheckInvariants());
+ Vector<PODInterval<float>> result =
+ tree.AllOverlaps(PODInterval<float>(3, 3));
+ EXPECT_EQ(1U, result.size());
+ EXPECT_EQ(2, result[0].Low());
+ EXPECT_EQ(4, result[0].High());
+}
+
+#ifndef NDEBUG
+template <>
+struct ValueToString<int*> {
+ static String ToString(int* const& value) {
+ return String::Format("0x%p", value);
+ }
+};
+#endif
+
+TEST(PODIntervalTreeTest, TestDuplicateElementInsertion) {
+ PODIntervalTree<float, int*> tree;
+ int tmp1 = 1;
+ int tmp2 = 2;
+ typedef PODIntervalTree<float, int*>::IntervalType IntervalType;
+ IntervalType interval1(1, 3, &tmp1);
+ IntervalType interval2(1, 3, &tmp2);
+ tree.Add(interval1);
+ tree.Add(interval2);
+ ASSERT_TRUE(tree.CheckInvariants());
+ EXPECT_TRUE(tree.Contains(interval1));
+ EXPECT_TRUE(tree.Contains(interval2));
+ EXPECT_TRUE(tree.Remove(interval1));
+ EXPECT_TRUE(tree.Contains(interval2));
+ EXPECT_FALSE(tree.Contains(interval1));
+ EXPECT_TRUE(tree.Remove(interval2));
+ EXPECT_EQ(0, tree.size());
+}
+
+namespace {
+
+struct UserData1 {
+ public:
+ UserData1() : a(0), b(1) {}
+
+ float a;
+ int b;
+};
+
+} // anonymous namespace
+
+#ifndef NDEBUG
+template <>
+struct ValueToString<UserData1> {
+ static String ToString(const UserData1& value) {
+ return String("[UserData1 a=") + String::Number(value.a) +
+ " b=" + String::Number(value.b) + "]";
+ }
+};
+#endif
+
+TEST(PODIntervalTreeTest, TestInsertionOfComplexUserData) {
+ PODIntervalTree<float, UserData1> tree;
+ UserData1 data1;
+ data1.a = 5;
+ data1.b = 6;
+ tree.Add(tree.CreateInterval(2, 4, data1));
+ ASSERT_TRUE(tree.CheckInvariants());
+}
+
+TEST(PODIntervalTreeTest, TestQueryingOfComplexUserData) {
+ PODIntervalTree<float, UserData1> tree;
+ UserData1 data1;
+ data1.a = 5;
+ data1.b = 6;
+ tree.Add(tree.CreateInterval(2, 4, data1));
+ ASSERT_TRUE(tree.CheckInvariants());
+ Vector<PODInterval<float, UserData1>> overlaps =
+ tree.AllOverlaps(tree.CreateInterval(3, 5, data1));
+ EXPECT_EQ(1U, overlaps.size());
+ EXPECT_EQ(5, overlaps[0].Data().a);
+ EXPECT_EQ(6, overlaps[0].Data().b);
+}
+
+namespace {
+
+class EndpointType1 {
+ public:
+ explicit EndpointType1(int value) : value_(value) {}
+
+ int Value() const { return value_; }
+
+ bool operator<(const EndpointType1& other) const {
+ return value_ < other.value_;
+ }
+ bool operator==(const EndpointType1& other) const {
+ return value_ == other.value_;
+ }
+
+ private:
+ int value_;
+ // These operators should not be called by the interval tree.
+ bool operator>(const EndpointType1& other);
+ bool operator<=(const EndpointType1& other);
+ bool operator>=(const EndpointType1& other);
+ bool operator!=(const EndpointType1& other);
+};
+
+} // anonymous namespace
+
+#ifndef NDEBUG
+template <>
+struct ValueToString<EndpointType1> {
+ static String ToString(const EndpointType1& value) {
+ return String("[EndpointType1 value=") + String::Number(value.Value()) +
+ "]";
+ }
+};
+#endif
+
+TEST(PODIntervalTreeTest, TestTreeDoesNotRequireMostOperators) {
+ PODIntervalTree<EndpointType1> tree;
+ tree.Add(tree.CreateInterval(EndpointType1(1), EndpointType1(2)));
+ ASSERT_TRUE(tree.CheckInvariants());
+}
+
+// Uncomment to debug a failure of the insertion and deletion test. Won't work
+// in release builds.
+// #define DEBUG_INSERTION_AND_DELETION_TEST
+
+#ifndef NDEBUG
+template <>
+struct ValueToString<int> {
+ static String ToString(const int& value) { return String::Number(value); }
+};
+#endif
+
+namespace {
+
+void TreeInsertionAndDeletionTest(int32_t seed, int tree_size) {
+ InitRandom(seed);
+ int maximum_value = tree_size;
+ // Build the tree
+ PODIntervalTree<int> tree;
+ Vector<PODInterval<int>> added_elements;
+ Vector<PODInterval<int>> removed_elements;
+ for (int i = 0; i < tree_size; i++) {
+ int left = NextRandom(maximum_value);
+ int length = NextRandom(maximum_value);
+ PODInterval<int> interval(left, left + length);
+ tree.Add(interval);
+#ifdef DEBUG_INSERTION_AND_DELETION_TEST
+ DLOG(ERROR) << "*** Adding element "
+ << ValueToString<PODInterval<int>>::string(interval);
+#endif
+ added_elements.push_back(interval);
+ }
+ // Churn the tree's contents.
+ // First remove half of the elements in random order.
+ for (int i = 0; i < tree_size / 2; i++) {
+ int index = NextRandom(added_elements.size());
+#ifdef DEBUG_INSERTION_AND_DELETION_TEST
+ DLOG(ERROR) << "*** Removing element "
+ << ValueToString<PODInterval<int>>::string(
+ addedElements[index]);
+#endif
+ ASSERT_TRUE(tree.Contains(added_elements[index]))
+ << "Test failed for seed " << seed;
+ tree.Remove(added_elements[index]);
+ removed_elements.push_back(added_elements[index]);
+ added_elements.EraseAt(index);
+ ASSERT_TRUE(tree.CheckInvariants()) << "Test failed for seed " << seed;
+ }
+ // Now randomly add or remove elements.
+ for (int i = 0; i < 2 * tree_size; i++) {
+ bool add = false;
+ if (!added_elements.size())
+ add = true;
+ else if (!removed_elements.size())
+ add = false;
+ else
+ add = (NextRandom(2) == 1);
+ if (add) {
+ int index = NextRandom(removed_elements.size());
+#ifdef DEBUG_INSERTION_AND_DELETION_TEST
+ DLOG(ERROR) << "*** Adding element "
+ << ValueToString<PODInterval<int>>::string(
+ removedElements[index]);
+#endif
+ tree.Add(removed_elements[index]);
+ added_elements.push_back(removed_elements[index]);
+ removed_elements.EraseAt(index);
+ } else {
+ int index = NextRandom(added_elements.size());
+#ifdef DEBUG_INSERTION_AND_DELETION_TEST
+ DLOG(ERROR) << "*** Removing element "
+ << ValueToString<PODInterval<int>>::string(
+ addedElements[index]);
+#endif
+ ASSERT_TRUE(tree.Contains(added_elements[index]))
+ << "Test failed for seed " << seed;
+ ASSERT_TRUE(tree.Remove(added_elements[index]))
+ << "Test failed for seed " << seed;
+ removed_elements.push_back(added_elements[index]);
+ added_elements.EraseAt(index);
+ }
+ ASSERT_TRUE(tree.CheckInvariants()) << "Test failed for seed " << seed;
+ }
+}
+
+} // anonymous namespace
+
+TEST(PODIntervalTreeTest, RandomDeletionAndInsertionRegressionTest1) {
+ TreeInsertionAndDeletionTest(13972, 100);
+}
+
+TEST(PODIntervalTreeTest, RandomDeletionAndInsertionRegressionTest2) {
+ TreeInsertionAndDeletionTest(1283382113, 10);
+}
+
+TEST(PODIntervalTreeTest, RandomDeletionAndInsertionRegressionTest3) {
+ // This is the sequence of insertions and deletions that triggered
+ // the failure in RandomDeletionAndInsertionRegressionTest2.
+ PODIntervalTree<int> tree;
+ tree.Add(tree.CreateInterval(0, 5));
+ ASSERT_TRUE(tree.CheckInvariants());
+ tree.Add(tree.CreateInterval(4, 5));
+ ASSERT_TRUE(tree.CheckInvariants());
+ tree.Add(tree.CreateInterval(8, 9));
+ ASSERT_TRUE(tree.CheckInvariants());
+ tree.Add(tree.CreateInterval(1, 4));
+ ASSERT_TRUE(tree.CheckInvariants());
+ tree.Add(tree.CreateInterval(3, 5));
+ ASSERT_TRUE(tree.CheckInvariants());
+ tree.Add(tree.CreateInterval(4, 12));
+ ASSERT_TRUE(tree.CheckInvariants());
+ tree.Add(tree.CreateInterval(0, 2));
+ ASSERT_TRUE(tree.CheckInvariants());
+ tree.Add(tree.CreateInterval(0, 2));
+ ASSERT_TRUE(tree.CheckInvariants());
+ tree.Add(tree.CreateInterval(9, 13));
+ ASSERT_TRUE(tree.CheckInvariants());
+ tree.Add(tree.CreateInterval(0, 1));
+ ASSERT_TRUE(tree.CheckInvariants());
+ tree.Remove(tree.CreateInterval(0, 2));
+ ASSERT_TRUE(tree.CheckInvariants());
+ tree.Remove(tree.CreateInterval(9, 13));
+ ASSERT_TRUE(tree.CheckInvariants());
+ tree.Remove(tree.CreateInterval(0, 2));
+ ASSERT_TRUE(tree.CheckInvariants());
+ tree.Remove(tree.CreateInterval(0, 1));
+ ASSERT_TRUE(tree.CheckInvariants());
+ tree.Remove(tree.CreateInterval(4, 5));
+ ASSERT_TRUE(tree.CheckInvariants());
+ tree.Remove(tree.CreateInterval(4, 12));
+ ASSERT_TRUE(tree.CheckInvariants());
+}
+
+TEST(PODIntervalTreeTest, RandomDeletionAndInsertionRegressionTest4) {
+ // Even further reduced test case for
+ // RandomDeletionAndInsertionRegressionTest3.
+ PODIntervalTree<int> tree;
+ tree.Add(tree.CreateInterval(0, 5));
+ ASSERT_TRUE(tree.CheckInvariants());
+ tree.Add(tree.CreateInterval(8, 9));
+ ASSERT_TRUE(tree.CheckInvariants());
+ tree.Add(tree.CreateInterval(1, 4));
+ ASSERT_TRUE(tree.CheckInvariants());
+ tree.Add(tree.CreateInterval(3, 5));
+ ASSERT_TRUE(tree.CheckInvariants());
+ tree.Add(tree.CreateInterval(4, 12));
+ ASSERT_TRUE(tree.CheckInvariants());
+ tree.Remove(tree.CreateInterval(4, 12));
+ ASSERT_TRUE(tree.CheckInvariants());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/pod_red_black_tree.h b/chromium/third_party/blink/renderer/platform/pod_red_black_tree.h
new file mode 100644
index 00000000000..b03309aa1f6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/pod_red_black_tree.h
@@ -0,0 +1,799 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 red-black tree, which is a form of a balanced binary tree. It
+// supports efficient insertion, deletion and queries of comparable
+// elements. The same element may be inserted multiple times. The
+// algorithmic complexity of common operations is:
+//
+// Insertion: O(lg(n))
+// Deletion: O(lg(n))
+// Querying: O(lg(n))
+//
+// The data type T that is stored in this red-black tree must be only
+// Plain Old Data (POD), or bottom out into POD. It must _not_ rely on
+// having its destructor called. This implementation internally
+// allocates storage in large chunks and does not call the destructor
+// on each object.
+//
+// Type T must supply a default constructor, a copy constructor, and
+// the "<" and "==" operators.
+//
+// In debug mode, printing of the data contained in the tree is
+// enabled. This requires the template specialization to be available:
+//
+// template<> struct ValueToString<T> {
+// static String toString(const T& t);
+// };
+//
+// Note that when complex types are stored in this red/black tree, it
+// is possible that single invocations of the "<" and "==" operators
+// will be insufficient to describe the ordering of elements in the
+// tree during queries. As a concrete example, consider the case where
+// intervals are stored in the tree sorted by low endpoint. The "<"
+// operator on the Interval class only compares the low endpoint, but
+// the "==" operator takes into account the high endpoint as well.
+// This makes the necessary logic for querying and deletion somewhat
+// more complex. In order to properly handle such situations, the
+// property "needsFullOrderingComparisons" must be set to true on
+// the tree.
+//
+// This red-black tree is designed to be _augmented_; subclasses can
+// add additional and summary information to each node to efficiently
+// store and index more complex data structures. A concrete example is
+// the IntervalTree, which extends each node with a summary statistic
+// to efficiently store one-dimensional intervals.
+//
+// The design of this red-black tree comes from Cormen, Leiserson,
+// and Rivest, _Introduction to Algorithms_, MIT Press, 1990.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_POD_RED_BLACK_TREE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_POD_RED_BLACK_TREE_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/pod_free_list_arena.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#ifndef NDEBUG
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#endif
+
+namespace blink {
+
+#ifndef NDEBUG
+template <class T>
+struct ValueToString;
+#endif
+
+enum UninitializedTreeEnum { kUninitializedTree };
+
+template <class T>
+class PODRedBlackTree {
+ DISALLOW_NEW();
+
+ public:
+ class Node;
+
+ // Visitor interface for walking all of the tree's elements.
+ class Visitor {
+ public:
+ virtual void Visit(const T& data) = 0;
+
+ protected:
+ virtual ~Visitor() = default;
+ };
+
+ // Constructs a new red-black tree without allocating an arena.
+ // isInitialized will return false in this case. initIfNeeded can be used
+ // to init the structure. This constructor is usefull for creating
+ // lazy initialized tree.
+ explicit PODRedBlackTree(UninitializedTreeEnum)
+ : root_(nullptr),
+ needs_full_ordering_comparisons_(false)
+#ifndef NDEBUG
+ ,
+ verbose_debugging_(false)
+#endif
+ {
+ }
+
+ // Constructs a new red-black tree, allocating temporary objects
+ // from a newly constructed PODFreeListArena.
+ PODRedBlackTree()
+ : arena_(PODFreeListArena<Node>::Create()),
+ root_(nullptr),
+ needs_full_ordering_comparisons_(false)
+#ifndef NDEBUG
+ ,
+ verbose_debugging_(false)
+#endif
+ {
+ }
+
+ // Constructs a new red-black tree, allocating temporary objects
+ // from the given PODArena.
+ explicit PODRedBlackTree(scoped_refptr<PODFreeListArena<Node>> arena)
+ : arena_(std::move(arena)),
+ root_(nullptr),
+ needs_full_ordering_comparisons_(false)
+#ifndef NDEBUG
+ ,
+ verbose_debugging_(false)
+#endif
+ {
+ }
+
+ virtual ~PODRedBlackTree() = default;
+
+ // Clearing will delete the contents of the tree. After this call
+ // isInitialized will return false.
+ void Clear() {
+ MarkFree(root_);
+ arena_ = nullptr;
+ root_ = nullptr;
+ }
+
+ bool IsInitialized() const { return arena_.get(); }
+
+ void InitIfNeeded() {
+ if (!arena_)
+ arena_ = PODFreeListArena<Node>::Create();
+ }
+
+ void InitIfNeeded(PODFreeListArena<Node>* arena) {
+ if (!arena_)
+ arena_ = arena;
+ }
+
+ void Add(const T& data) {
+ DCHECK(IsInitialized());
+ Node* node = arena_->template AllocateObject<T>(data);
+ InsertNode(node);
+ }
+
+ // Returns true if the datum was found in the tree.
+ bool Remove(const T& data) {
+ DCHECK(IsInitialized());
+ Node* node = TreeSearch(data);
+ if (node) {
+ DeleteNode(node);
+ return true;
+ }
+ return false;
+ }
+
+ bool Contains(const T& data) const {
+ DCHECK(IsInitialized());
+ return TreeSearch(data);
+ }
+
+ void VisitInorder(Visitor* visitor) const {
+ DCHECK(IsInitialized());
+ if (!root_)
+ return;
+ VisitInorderImpl(root_, visitor);
+ }
+
+ int size() const {
+ DCHECK(IsInitialized());
+ Counter counter;
+ VisitInorder(&counter);
+ return counter.Count();
+ }
+
+ // See the class documentation for an explanation of this property.
+ void SetNeedsFullOrderingComparisons(bool needs_full_ordering_comparisons) {
+ needs_full_ordering_comparisons_ = needs_full_ordering_comparisons;
+ }
+
+ virtual bool CheckInvariants() const {
+ DCHECK(IsInitialized());
+ int black_count;
+ return CheckInvariantsFromNode(root_, &black_count);
+ }
+
+#ifndef NDEBUG
+ // Dumps the tree's contents to the logging info stream for
+ // debugging purposes.
+ void Dump() const {
+ if (arena_)
+ DumpFromNode(root_, 0);
+ }
+
+ // Turns on or off verbose debugging of the tree, causing many
+ // messages to be logged during insertion and other operations in
+ // debug mode.
+ void SetVerboseDebugging(bool verbose_debugging) {
+ verbose_debugging_ = verbose_debugging;
+ }
+#endif
+
+ enum NodeColor { kRed = 1, kBlack };
+
+ // The base Node class which is stored in the tree. Nodes are only
+ // an internal concept; users of the tree deal only with the data
+ // they store in it.
+ class Node {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ WTF_MAKE_NONCOPYABLE(Node);
+
+ public:
+ // Constructor. Newly-created nodes are colored red.
+ explicit Node(const T& data)
+ : left_(nullptr),
+ right_(nullptr),
+ parent_(nullptr),
+ color_(kRed),
+ data_(data) {}
+
+ virtual ~Node() = default;
+
+ NodeColor GetColor() const { return color_; }
+ void SetColor(NodeColor color) { color_ = color; }
+
+ // Fetches the user data.
+ T& Data() { return data_; }
+
+ // Copies all user-level fields from the source node, but not
+ // internal fields. For example, the base implementation of this
+ // method copies the "m_data" field, but not the child or parent
+ // fields. Any augmentation information also does not need to be
+ // copied, as it will be recomputed. Subclasses must call the
+ // superclass implementation.
+ virtual void CopyFrom(Node* src) { data_ = src->Data(); }
+
+ Node* Left() const { return left_; }
+ void SetLeft(Node* node) { left_ = node; }
+
+ Node* Right() const { return right_; }
+ void SetRight(Node* node) { right_ = node; }
+
+ Node* Parent() const { return parent_; }
+ void SetParent(Node* node) { parent_ = node; }
+
+ private:
+ Node* left_;
+ Node* right_;
+ Node* parent_;
+ NodeColor color_;
+ T data_;
+ };
+
+ protected:
+ // Returns the root of the tree, which is needed by some subclasses.
+ Node* Root() const { return root_; }
+
+ private:
+ // This virtual method is the hook that subclasses should use when
+ // augmenting the red-black tree with additional per-node summary
+ // information. For example, in the case of an interval tree, this
+ // is used to compute the maximum endpoint of the subtree below the
+ // given node based on the values in the left and right children. It
+ // is guaranteed that this will be called in the correct order to
+ // properly update such summary information based only on the values
+ // in the left and right children. This method should return true if
+ // the node's summary information changed.
+ virtual bool UpdateNode(Node*) { return false; }
+
+ //----------------------------------------------------------------------
+ // Generic binary search tree operations
+ //
+
+ // Searches the tree for the given datum.
+ Node* TreeSearch(const T& data) const {
+ if (needs_full_ordering_comparisons_)
+ return TreeSearchFullComparisons(root_, data);
+
+ return TreeSearchNormal(root_, data);
+ }
+
+ // Searches the tree using the normal comparison operations,
+ // suitable for simple data types such as numbers.
+ Node* TreeSearchNormal(Node* current, const T& data) const {
+ while (current) {
+ if (current->Data() == data)
+ return current;
+ if (data < current->Data())
+ current = current->Left();
+ else
+ current = current->Right();
+ }
+ return nullptr;
+ }
+
+ // Searches the tree using multiple comparison operations, required
+ // for data types with more complex behavior such as intervals.
+ Node* TreeSearchFullComparisons(Node* current, const T& data) const {
+ if (!current)
+ return nullptr;
+ if (data < current->Data())
+ return TreeSearchFullComparisons(current->Left(), data);
+ if (current->Data() < data)
+ return TreeSearchFullComparisons(current->Right(), data);
+ if (data == current->Data())
+ return current;
+
+ // We may need to traverse both the left and right subtrees.
+ Node* result = TreeSearchFullComparisons(current->Left(), data);
+ if (!result)
+ result = TreeSearchFullComparisons(current->Right(), data);
+ return result;
+ }
+
+ void TreeInsert(Node* z) {
+ Node* y = nullptr;
+ Node* x = root_;
+ while (x) {
+ y = x;
+ if (z->Data() < x->Data())
+ x = x->Left();
+ else
+ x = x->Right();
+ }
+ z->SetParent(y);
+ if (!y) {
+ root_ = z;
+ } else {
+ if (z->Data() < y->Data())
+ y->SetLeft(z);
+ else
+ y->SetRight(z);
+ }
+ }
+
+ // Finds the node following the given one in sequential ordering of
+ // their data, or null if none exists.
+ Node* TreeSuccessor(Node* x) {
+ if (x->Right())
+ return TreeMinimum(x->Right());
+ Node* y = x->Parent();
+ while (y && x == y->Right()) {
+ x = y;
+ y = y->Parent();
+ }
+ return y;
+ }
+
+ // Finds the minimum element in the sub-tree rooted at the given
+ // node.
+ Node* TreeMinimum(Node* x) {
+ while (x->Left())
+ x = x->Left();
+ return x;
+ }
+
+ // Helper for maintaining the augmented red-black tree.
+ void PropagateUpdates(Node* start) {
+ bool should_continue = true;
+ while (start && should_continue) {
+ should_continue = UpdateNode(start);
+ start = start->Parent();
+ }
+ }
+
+ //----------------------------------------------------------------------
+ // Red-Black tree operations
+ //
+
+ // Left-rotates the subtree rooted at x.
+ // Returns the new root of the subtree (x's right child).
+ Node* LeftRotate(Node* x) {
+ // Set y.
+ Node* y = x->Right();
+
+ // Turn y's left subtree into x's right subtree.
+ x->SetRight(y->Left());
+ if (y->Left())
+ y->Left()->SetParent(x);
+
+ // Link x's parent to y.
+ y->SetParent(x->Parent());
+ if (!x->Parent()) {
+ root_ = y;
+ } else {
+ if (x == x->Parent()->Left())
+ x->Parent()->SetLeft(y);
+ else
+ x->Parent()->SetRight(y);
+ }
+
+ // Put x on y's left.
+ y->SetLeft(x);
+ x->SetParent(y);
+
+ // Update nodes lowest to highest.
+ UpdateNode(x);
+ UpdateNode(y);
+ return y;
+ }
+
+ // Right-rotates the subtree rooted at y.
+ // Returns the new root of the subtree (y's left child).
+ Node* RightRotate(Node* y) {
+ // Set x.
+ Node* x = y->Left();
+
+ // Turn x's right subtree into y's left subtree.
+ y->SetLeft(x->Right());
+ if (x->Right())
+ x->Right()->SetParent(y);
+
+ // Link y's parent to x.
+ x->SetParent(y->Parent());
+ if (!y->Parent()) {
+ root_ = x;
+ } else {
+ if (y == y->Parent()->Left())
+ y->Parent()->SetLeft(x);
+ else
+ y->Parent()->SetRight(x);
+ }
+
+ // Put y on x's right.
+ x->SetRight(y);
+ y->SetParent(x);
+
+ // Update nodes lowest to highest.
+ UpdateNode(y);
+ UpdateNode(x);
+ return x;
+ }
+
+ // Inserts the given node into the tree.
+ void InsertNode(Node* x) {
+ TreeInsert(x);
+ x->SetColor(kRed);
+ UpdateNode(x);
+
+ LogIfVerbose(" PODRedBlackTree::InsertNode");
+
+ // The node from which to start propagating updates upwards.
+ Node* update_start = x->Parent();
+
+ while (x != root_ && x->Parent()->GetColor() == kRed) {
+ if (x->Parent() == x->Parent()->Parent()->Left()) {
+ Node* y = x->Parent()->Parent()->Right();
+ if (y && y->GetColor() == kRed) {
+ // Case 1
+ LogIfVerbose(" Case 1/1");
+ x->Parent()->SetColor(kBlack);
+ y->SetColor(kBlack);
+ x->Parent()->Parent()->SetColor(kRed);
+ UpdateNode(x->Parent());
+ x = x->Parent()->Parent();
+ UpdateNode(x);
+ update_start = x->Parent();
+ } else {
+ if (x == x->Parent()->Right()) {
+ LogIfVerbose(" Case 1/2");
+ // Case 2
+ x = x->Parent();
+ LeftRotate(x);
+ }
+ // Case 3
+ LogIfVerbose(" Case 1/3");
+ x->Parent()->SetColor(kBlack);
+ x->Parent()->Parent()->SetColor(kRed);
+ Node* new_sub_tree_root = RightRotate(x->Parent()->Parent());
+ update_start = new_sub_tree_root->Parent();
+ }
+ } else {
+ // Same as "then" clause with "right" and "left" exchanged.
+ Node* y = x->Parent()->Parent()->Left();
+ if (y && y->GetColor() == kRed) {
+ // Case 1
+ LogIfVerbose(" Case 2/1");
+ x->Parent()->SetColor(kBlack);
+ y->SetColor(kBlack);
+ x->Parent()->Parent()->SetColor(kRed);
+ UpdateNode(x->Parent());
+ x = x->Parent()->Parent();
+ UpdateNode(x);
+ update_start = x->Parent();
+ } else {
+ if (x == x->Parent()->Left()) {
+ // Case 2
+ LogIfVerbose(" Case 2/2");
+ x = x->Parent();
+ RightRotate(x);
+ }
+ // Case 3
+ LogIfVerbose(" Case 2/3");
+ x->Parent()->SetColor(kBlack);
+ x->Parent()->Parent()->SetColor(kRed);
+ Node* new_sub_tree_root = LeftRotate(x->Parent()->Parent());
+ update_start = new_sub_tree_root->Parent();
+ }
+ }
+ }
+
+ PropagateUpdates(update_start);
+
+ root_->SetColor(kBlack);
+ }
+
+ // Restores the red-black property to the tree after splicing out
+ // a node. Note that x may be null, which is why xParent must be
+ // supplied.
+ void DeleteFixup(Node* x, Node* x_parent) {
+ while (x != root_ && (!x || x->GetColor() == kBlack)) {
+ if (x == x_parent->Left()) {
+ // Note: the text points out that w can not be null.
+ // The reason is not obvious from simply looking at
+ // the code; it comes about from the properties of the
+ // red-black tree.
+ Node* w = x_parent->Right();
+ DCHECK(w); // x's sibling should not be null.
+ if (w->GetColor() == kRed) {
+ // Case 1
+ w->SetColor(kBlack);
+ x_parent->SetColor(kRed);
+ LeftRotate(x_parent);
+ w = x_parent->Right();
+ }
+ if ((!w->Left() || w->Left()->GetColor() == kBlack) &&
+ (!w->Right() || w->Right()->GetColor() == kBlack)) {
+ // Case 2
+ w->SetColor(kRed);
+ x = x_parent;
+ x_parent = x->Parent();
+ } else {
+ if (!w->Right() || w->Right()->GetColor() == kBlack) {
+ // Case 3
+ w->Left()->SetColor(kBlack);
+ w->SetColor(kRed);
+ RightRotate(w);
+ w = x_parent->Right();
+ }
+ // Case 4
+ w->SetColor(x_parent->GetColor());
+ x_parent->SetColor(kBlack);
+ if (w->Right())
+ w->Right()->SetColor(kBlack);
+ LeftRotate(x_parent);
+ x = root_;
+ x_parent = x->Parent();
+ }
+ } else {
+ // Same as "then" clause with "right" and "left"
+ // exchanged.
+
+ // Note: the text points out that w can not be null.
+ // The reason is not obvious from simply looking at
+ // the code; it comes about from the properties of the
+ // red-black tree.
+ Node* w = x_parent->Left();
+ DCHECK(w); // x's sibling should not be null.
+ if (w->GetColor() == kRed) {
+ // Case 1
+ w->SetColor(kBlack);
+ x_parent->SetColor(kRed);
+ RightRotate(x_parent);
+ w = x_parent->Left();
+ }
+ if ((!w->Right() || w->Right()->GetColor() == kBlack) &&
+ (!w->Left() || w->Left()->GetColor() == kBlack)) {
+ // Case 2
+ w->SetColor(kRed);
+ x = x_parent;
+ x_parent = x->Parent();
+ } else {
+ if (!w->Left() || w->Left()->GetColor() == kBlack) {
+ // Case 3
+ w->Right()->SetColor(kBlack);
+ w->SetColor(kRed);
+ LeftRotate(w);
+ w = x_parent->Left();
+ }
+ // Case 4
+ w->SetColor(x_parent->GetColor());
+ x_parent->SetColor(kBlack);
+ if (w->Left())
+ w->Left()->SetColor(kBlack);
+ RightRotate(x_parent);
+ x = root_;
+ x_parent = x->Parent();
+ }
+ }
+ }
+ if (x)
+ x->SetColor(kBlack);
+ }
+
+ // Deletes the given node from the tree. Note that this
+ // particular node may not actually be removed from the tree;
+ // instead, another node might be removed and its contents
+ // copied into z.
+ void DeleteNode(Node* z) {
+ // Y is the node to be unlinked from the tree.
+ Node* y;
+ if (!z->Left() || !z->Right())
+ y = z;
+ else
+ y = TreeSuccessor(z);
+
+ // Y is guaranteed to be non-null at this point.
+ Node* x;
+ if (y->Left())
+ x = y->Left();
+ else
+ x = y->Right();
+
+ // X is the child of y which might potentially replace y in
+ // the tree. X might be null at this point.
+ Node* x_parent;
+ if (x) {
+ x->SetParent(y->Parent());
+ x_parent = x->Parent();
+ } else {
+ x_parent = y->Parent();
+ }
+ if (!y->Parent()) {
+ root_ = x;
+ } else {
+ if (y == y->Parent()->Left())
+ y->Parent()->SetLeft(x);
+ else
+ y->Parent()->SetRight(x);
+ }
+ if (y != z) {
+ z->CopyFrom(y);
+ // This node has changed location in the tree and must be updated.
+ UpdateNode(z);
+ // The parent and its parents may now be out of date.
+ PropagateUpdates(z->Parent());
+ }
+
+ // If we haven't already updated starting from xParent, do so now.
+ if (x_parent && x_parent != y && x_parent != z)
+ PropagateUpdates(x_parent);
+ if (y->GetColor() == kBlack)
+ DeleteFixup(x, x_parent);
+
+ arena_->FreeObject(y);
+ }
+
+ // Visits the subtree rooted at the given node in order.
+ void VisitInorderImpl(Node* node, Visitor* visitor) const {
+ if (node->Left())
+ VisitInorderImpl(node->Left(), visitor);
+ visitor->Visit(node->Data());
+ if (node->Right())
+ VisitInorderImpl(node->Right(), visitor);
+ }
+
+ void MarkFree(Node* node) {
+ if (!node)
+ return;
+
+ if (node->Left())
+ MarkFree(node->Left());
+ if (node->Right())
+ MarkFree(node->Right());
+ arena_->FreeObject(node);
+ }
+
+ //----------------------------------------------------------------------
+ // Helper class for size()
+
+ // A Visitor which simply counts the number of visited elements.
+ class Counter final : public Visitor {
+ DISALLOW_NEW();
+ WTF_MAKE_NONCOPYABLE(Counter);
+
+ public:
+ Counter() : count_(0) {}
+
+ virtual void Visit(const T&) { ++count_; }
+ int Count() const { return count_; }
+
+ private:
+ int count_;
+ };
+
+ //----------------------------------------------------------------------
+ // Verification and debugging routines
+ //
+
+ // Returns in the "blackCount" parameter the number of black
+ // children along all paths to all leaves of the given node.
+ bool CheckInvariantsFromNode(Node* node, int* black_count) const {
+ // Base case is a leaf node.
+ if (!node) {
+ *black_count = 1;
+ return true;
+ }
+
+ // Each node is either red or black.
+ if (!(node->GetColor() == kRed || node->GetColor() == kBlack))
+ return false;
+
+ // Every leaf (or null) is black.
+
+ if (node->GetColor() == kRed) {
+ // Both of its children are black.
+ if (!((!node->Left() || node->Left()->GetColor() == kBlack)))
+ return false;
+ if (!((!node->Right() || node->Right()->GetColor() == kBlack)))
+ return false;
+ }
+
+ // Every simple path to a leaf node contains the same number of
+ // black nodes.
+ int left_count = 0, right_count = 0;
+ bool left_valid = CheckInvariantsFromNode(node->Left(), &left_count);
+ bool right_valid = CheckInvariantsFromNode(node->Right(), &right_count);
+ if (!left_valid || !right_valid)
+ return false;
+ *black_count = left_count + (node->GetColor() == kBlack ? 1 : 0);
+ return left_count == right_count;
+ }
+
+#ifdef NDEBUG
+ void LogIfVerbose(const char*) const {}
+#else
+ void LogIfVerbose(const char* output) const {
+ if (verbose_debugging_)
+ DLOG(ERROR) << output;
+ }
+#endif
+
+#ifndef NDEBUG
+ // Dumps the subtree rooted at the given node.
+ void DumpFromNode(Node* node, int indentation) const {
+ StringBuilder builder;
+ for (int i = 0; i < indentation; i++)
+ builder.Append(' ');
+ builder.Append('-');
+ if (node) {
+ builder.Append(' ');
+ builder.Append(ValueToString<T>::GetString(node->Data()));
+ builder.Append((node->GetColor() == kBlack) ? " (black)" : " (red)");
+ }
+ DLOG(ERROR) << builder.ToString();
+ if (node) {
+ DumpFromNode(node->Left(), indentation + 2);
+ DumpFromNode(node->Right(), indentation + 2);
+ }
+ }
+#endif
+
+ //----------------------------------------------------------------------
+ // Data members
+
+ scoped_refptr<PODFreeListArena<Node>> arena_;
+ Node* root_;
+ bool needs_full_ordering_comparisons_;
+#ifndef NDEBUG
+ bool verbose_debugging_;
+#endif
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_POD_RED_BLACK_TREE_H_
diff --git a/chromium/third_party/blink/renderer/platform/pod_red_black_tree_test.cc b/chromium/third_party/blink/renderer/platform/pod_red_black_tree_test.cc
new file mode 100644
index 00000000000..cb2ad971e4c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/pod_red_black_tree_test.cc
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 the red-black tree class.
+
+#include "third_party/blink/renderer/platform/pod_red_black_tree.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/testing/arena_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/tree_test_helpers.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+using ArenaTestHelpers::TrackedAllocator;
+using TreeTestHelpers::InitRandom;
+using TreeTestHelpers::NextRandom;
+
+TEST(PODRedBlackTreeTest, TestTreeAllocatesFromArena) {
+ scoped_refptr<TrackedAllocator> allocator = TrackedAllocator::Create();
+ {
+ typedef PODFreeListArena<PODRedBlackTree<int>::Node> PODIntegerArena;
+ scoped_refptr<PODIntegerArena> arena = PODIntegerArena::Create(allocator);
+ PODRedBlackTree<int> tree(arena);
+ int num_additions = 2 * PODArena::kDefaultChunkSize / sizeof(int);
+ for (int i = 0; i < num_additions; ++i)
+ tree.Add(i);
+ EXPECT_GT(allocator->NumRegions(), 1);
+ }
+ EXPECT_EQ(allocator->NumRegions(), 0);
+}
+
+TEST(PODRedBlackTreeTest, TestSingleElementInsertion) {
+ PODRedBlackTree<int> tree;
+ tree.Add(5);
+ ASSERT_TRUE(tree.CheckInvariants());
+ EXPECT_TRUE(tree.Contains(5));
+}
+
+TEST(PODRedBlackTreeTest, TestMultipleElementInsertion) {
+ PODRedBlackTree<int> tree;
+ tree.Add(4);
+ ASSERT_TRUE(tree.CheckInvariants());
+ EXPECT_TRUE(tree.Contains(4));
+ tree.Add(3);
+ ASSERT_TRUE(tree.CheckInvariants());
+ EXPECT_TRUE(tree.Contains(3));
+ tree.Add(5);
+ ASSERT_TRUE(tree.CheckInvariants());
+ EXPECT_TRUE(tree.Contains(5));
+ EXPECT_TRUE(tree.Contains(4));
+ EXPECT_TRUE(tree.Contains(3));
+}
+
+TEST(PODRedBlackTreeTest, TestDuplicateElementInsertion) {
+ PODRedBlackTree<int> tree;
+ tree.Add(3);
+ ASSERT_TRUE(tree.CheckInvariants());
+ tree.Add(3);
+ ASSERT_TRUE(tree.CheckInvariants());
+ tree.Add(3);
+ ASSERT_TRUE(tree.CheckInvariants());
+ EXPECT_EQ(3, tree.size());
+ EXPECT_TRUE(tree.Contains(3));
+}
+
+TEST(PODRedBlackTreeTest, TestSingleElementInsertionAndDeletion) {
+ PODRedBlackTree<int> tree;
+ tree.Add(5);
+ ASSERT_TRUE(tree.CheckInvariants());
+ EXPECT_TRUE(tree.Contains(5));
+ tree.Remove(5);
+ ASSERT_TRUE(tree.CheckInvariants());
+ EXPECT_FALSE(tree.Contains(5));
+}
+
+TEST(PODRedBlackTreeTest, TestMultipleElementInsertionAndDeletion) {
+ PODRedBlackTree<int> tree;
+ tree.Add(4);
+ ASSERT_TRUE(tree.CheckInvariants());
+ EXPECT_TRUE(tree.Contains(4));
+ tree.Add(3);
+ ASSERT_TRUE(tree.CheckInvariants());
+ EXPECT_TRUE(tree.Contains(3));
+ tree.Add(5);
+ ASSERT_TRUE(tree.CheckInvariants());
+ EXPECT_TRUE(tree.Contains(5));
+ EXPECT_TRUE(tree.Contains(4));
+ EXPECT_TRUE(tree.Contains(3));
+ tree.Remove(4);
+ ASSERT_TRUE(tree.CheckInvariants());
+ EXPECT_TRUE(tree.Contains(3));
+ EXPECT_FALSE(tree.Contains(4));
+ EXPECT_TRUE(tree.Contains(5));
+ tree.Remove(5);
+ ASSERT_TRUE(tree.CheckInvariants());
+ EXPECT_TRUE(tree.Contains(3));
+ EXPECT_FALSE(tree.Contains(4));
+ EXPECT_FALSE(tree.Contains(5));
+ EXPECT_EQ(1, tree.size());
+}
+
+TEST(PODRedBlackTreeTest, TestDuplicateElementInsertionAndDeletion) {
+ PODRedBlackTree<int> tree;
+ tree.Add(3);
+ ASSERT_TRUE(tree.CheckInvariants());
+ tree.Add(3);
+ ASSERT_TRUE(tree.CheckInvariants());
+ tree.Add(3);
+ ASSERT_TRUE(tree.CheckInvariants());
+ EXPECT_EQ(3, tree.size());
+ EXPECT_TRUE(tree.Contains(3));
+ tree.Remove(3);
+ ASSERT_TRUE(tree.CheckInvariants());
+ tree.Remove(3);
+ ASSERT_TRUE(tree.CheckInvariants());
+ EXPECT_EQ(1, tree.size());
+ EXPECT_TRUE(tree.Contains(3));
+ tree.Remove(3);
+ ASSERT_TRUE(tree.CheckInvariants());
+ EXPECT_EQ(0, tree.size());
+ EXPECT_FALSE(tree.Contains(3));
+}
+
+TEST(PODRedBlackTreeTest, FailingInsertionRegressionTest1) {
+ // These numbers came from a previously-failing randomized test run.
+ PODRedBlackTree<int> tree;
+ tree.Add(5113);
+ ASSERT_TRUE(tree.CheckInvariants());
+ tree.Add(4517);
+ ASSERT_TRUE(tree.CheckInvariants());
+ tree.Add(3373);
+ ASSERT_TRUE(tree.CheckInvariants());
+ tree.Add(9307);
+ ASSERT_TRUE(tree.CheckInvariants());
+ tree.Add(7077);
+ ASSERT_TRUE(tree.CheckInvariants());
+}
+
+namespace {
+void InsertionAndDeletionTest(const int32_t seed, const int tree_size) {
+ InitRandom(seed);
+ const int maximum_value = tree_size;
+ // Build the tree.
+ PODRedBlackTree<int> tree;
+ Vector<int> values;
+ for (int i = 0; i < tree_size; i++) {
+ int value = NextRandom(maximum_value);
+ tree.Add(value);
+ ASSERT_TRUE(tree.CheckInvariants()) << "Test failed for seed " << seed;
+ values.push_back(value);
+ }
+ // Churn the tree's contents.
+ for (int i = 0; i < tree_size; i++) {
+ // Pick a random value to remove.
+ int index = NextRandom(tree_size);
+ int value = values[index];
+ // Remove this value.
+ tree.Remove(value);
+ ASSERT_TRUE(tree.CheckInvariants()) << "Test failed for seed " << seed;
+ // Replace it with a new one.
+ value = NextRandom(maximum_value);
+ values[index] = value;
+ tree.Add(value);
+ ASSERT_TRUE(tree.CheckInvariants()) << "Test failed for seed " << seed;
+ }
+}
+} // anonymous namespace
+
+TEST(PODRedBlackTreeTest, RandomDeletionAndInsertionRegressionTest1) {
+ InsertionAndDeletionTest(12311, 100);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/precompile_platform.h b/chromium/third_party/blink/renderer/platform/precompile_platform.h
new file mode 100644
index 00000000000..432604ee6e0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/precompile_platform.h
@@ -0,0 +1,21 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#if defined(PrecompilePlatform_h_)
+#error You shouldn't include the precompiled header file more than once.
+#endif
+
+#define PrecompilePlatform_h_
+
+#if defined(_MSC_VER)
+#include "build/win/precompile.h"
+#elif defined(__APPLE__)
+#include "build/mac/prefix.h"
+#else
+#error implement
+#endif
+
+// Include Oilpan's Handle.h by default, as it is included by a significant
+// portion of platform/ source files.
+#include "third_party/blink/renderer/platform/heap/handle.h"
diff --git a/chromium/third_party/blink/renderer/platform/prerender.cc b/chromium/third_party/blink/renderer/platform/prerender.cc
new file mode 100644
index 00000000000..610275eb82c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/prerender.cc
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2012 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 "third_party/blink/renderer/platform/prerender.h"
+
+#include "third_party/blink/public/platform/web_prerender.h"
+#include "third_party/blink/public/platform/web_prerendering_support.h"
+#include "third_party/blink/renderer/platform/prerender_client.h"
+
+namespace blink {
+
+Prerender::Prerender(PrerenderClient* client,
+ const KURL& url,
+ const unsigned rel_types,
+ const Referrer& referrer)
+ : client_(client), url_(url), rel_types_(rel_types), referrer_(referrer) {}
+
+Prerender::~Prerender() = default;
+
+void Prerender::Trace(blink::Visitor* visitor) {
+ visitor->Trace(client_);
+}
+
+void Prerender::Dispose() {
+ client_ = nullptr;
+ extra_data_ = nullptr;
+}
+
+void Prerender::Add() {
+ if (WebPrerenderingSupport* platform = WebPrerenderingSupport::Current())
+ platform->Add(WebPrerender(this));
+}
+
+void Prerender::Cancel() {
+ if (WebPrerenderingSupport* platform = WebPrerenderingSupport::Current())
+ platform->Cancel(WebPrerender(this));
+}
+
+void Prerender::Abandon() {
+ if (WebPrerenderingSupport* platform = WebPrerenderingSupport::Current())
+ platform->Abandon(WebPrerender(this));
+}
+
+void Prerender::DidStartPrerender() {
+ if (client_)
+ client_->DidStartPrerender();
+}
+
+void Prerender::DidStopPrerender() {
+ if (client_)
+ client_->DidStopPrerender();
+}
+
+void Prerender::DidSendLoadForPrerender() {
+ if (client_)
+ client_->DidSendLoadForPrerender();
+}
+
+void Prerender::DidSendDOMContentLoadedForPrerender() {
+ if (client_)
+ client_->DidSendDOMContentLoadedForPrerender();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/prerender.h b/chromium/third_party/blink/renderer/platform/prerender.h
new file mode 100644
index 00000000000..7cccea02b27
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/prerender.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2012 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_PRERENDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_PRERENDER_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/weborigin/referrer.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class PrerenderClient;
+
+class PLATFORM_EXPORT Prerender final
+ : public GarbageCollectedFinalized<Prerender> {
+ DISALLOW_COPY_AND_ASSIGN(Prerender);
+
+ public:
+ class ExtraData : public RefCounted<ExtraData> {
+ public:
+ virtual ~ExtraData() = default;
+ };
+
+ static Prerender* Create(PrerenderClient* client,
+ const KURL& url,
+ unsigned rel_types,
+ const Referrer& referrer) {
+ return new Prerender(client, url, rel_types, referrer);
+ }
+
+ ~Prerender();
+ void Trace(blink::Visitor*);
+
+ void Dispose();
+
+ void Add();
+ void Cancel();
+ void Abandon();
+
+ const KURL& Url() const { return url_; }
+ unsigned RelTypes() const { return rel_types_; }
+ const String& GetReferrer() const { return referrer_.referrer; }
+ ReferrerPolicy GetReferrerPolicy() const { return referrer_.referrer_policy; }
+
+ void SetExtraData(scoped_refptr<ExtraData> extra_data) {
+ extra_data_ = std::move(extra_data);
+ }
+ ExtraData* GetExtraData() { return extra_data_.get(); }
+
+ void DidStartPrerender();
+ void DidStopPrerender();
+ void DidSendLoadForPrerender();
+ void DidSendDOMContentLoadedForPrerender();
+
+ private:
+ Prerender(PrerenderClient*, const KURL&, unsigned rel_types, const Referrer&);
+
+ // The embedder's prerendering support holds on to pending Prerender objects;
+ // those references should not keep the PrerenderClient alive -- if the client
+ // becomes otherwise unreachable it should be GCed (at which point it will
+ // abandon this Prerender object.)
+ WeakMember<PrerenderClient> client_;
+
+ const KURL url_;
+ const unsigned rel_types_;
+ const Referrer referrer_;
+
+ scoped_refptr<ExtraData> extra_data_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_PRERENDER_H_
diff --git a/chromium/third_party/blink/renderer/platform/prerender_client.h b/chromium/third_party/blink/renderer/platform/prerender_client.h
new file mode 100644
index 00000000000..81108f88b12
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/prerender_client.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2012 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_PRERENDER_CLIENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_PRERENDER_CLIENT_H_
+
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT PrerenderClient : public GarbageCollectedMixin {
+ public:
+ virtual ~PrerenderClient() = default;
+
+ virtual void DidStartPrerender() = 0;
+ virtual void DidStopPrerender() = 0;
+ virtual void DidSendLoadForPrerender() = 0;
+ virtual void DidSendDOMContentLoadedForPrerender() = 0;
+
+ virtual void Trace(blink::Visitor* visitor) {}
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/probe/OWNERS b/chromium/third_party/blink/renderer/platform/probe/OWNERS
new file mode 100644
index 00000000000..a5b1e783065
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/probe/OWNERS
@@ -0,0 +1,2 @@
+alph@chromium.org
+caseq@chromium.org
diff --git a/chromium/third_party/blink/renderer/platform/probe/PlatformProbes.json5 b/chromium/third_party/blink/renderer/platform/probe/PlatformProbes.json5
new file mode 100644
index 00000000000..5bef0f3daea
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/probe/PlatformProbes.json5
@@ -0,0 +1,19 @@
+{
+ settings: {
+ export_header: "platform/platform_export.h",
+ export_symbol: "PLATFORM_EXPORT",
+ include_path: "platform/probe",
+ includes: [
+ "platform/PlatformProbeSink.h",
+ "platform/probe/platform_probes.h",
+ ]
+ },
+ observers: {
+ PlatformTraceEvents: {
+ class: "PlatformTraceEventsAgent",
+ probes: [
+ "PlatformSendRequest",
+ ]
+ }
+ }
+}
diff --git a/chromium/third_party/blink/renderer/platform/probe/PlatformProbes.pidl b/chromium/third_party/blink/renderer/platform/probe/PlatformProbes.pidl
new file mode 100644
index 00000000000..d40e3614738
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/probe/PlatformProbes.pidl
@@ -0,0 +1,12 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+interface PlatformProbes {
+ class FetchContext;
+ class ResourceRequest;
+ class ResourceResponse;
+ struct FetchInitiatorInfo;
+
+ PlatformSendRequest([Keep] FetchContext*, unsigned long identifier, ResourceRequest&, const ResourceResponse& redirectResponse, const FetchInitiatorInfo&);
+}
diff --git a/chromium/third_party/blink/renderer/platform/probe/PlatformTraceEventsAgent.cpp b/chromium/third_party/blink/renderer/platform/probe/PlatformTraceEventsAgent.cpp
new file mode 100644
index 00000000000..a5ac31a4dd8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/probe/PlatformTraceEventsAgent.cpp
@@ -0,0 +1,33 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/probe/PlatformTraceEventsAgent.h"
+
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/traced_value.h"
+#include "third_party/blink/renderer/platform/probe/platform_probes.h"
+
+namespace blink {
+
+namespace {
+
+std::unique_ptr<TracedValue> BuildData(
+ const probe::PlatformSendRequest& probe) {
+ std::unique_ptr<TracedValue> value = TracedValue::Create();
+ value->SetString("id", String::Number(probe.identifier));
+ return value;
+}
+
+} // namespace
+
+void PlatformTraceEventsAgent::Will(const probe::PlatformSendRequest& probe) {
+ TRACE_EVENT_BEGIN1("devtools.timeline", "PlatformResourceSendRequest", "data",
+ BuildData(probe));
+}
+
+void PlatformTraceEventsAgent::Did(const probe::PlatformSendRequest& probe) {
+ TRACE_EVENT_END0("devtools.timeline", "PlatformResourceSendRequest");
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/probe/PlatformTraceEventsAgent.h b/chromium/third_party/blink/renderer/platform/probe/PlatformTraceEventsAgent.h
new file mode 100644
index 00000000000..1332a584992
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/probe/PlatformTraceEventsAgent.h
@@ -0,0 +1,28 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_PROBE_PLATFORMTRACEEVENTSAGENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_PROBE_PLATFORMTRACEEVENTSAGENT_H_
+
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+
+namespace probe {
+class PlatformSendRequest;
+}
+
+class PLATFORM_EXPORT PlatformTraceEventsAgent
+ : public GarbageCollected<PlatformTraceEventsAgent> {
+ public:
+ void Trace(blink::Visitor* visitor) {}
+
+ void Will(const probe::PlatformSendRequest&);
+ void Did(const probe::PlatformSendRequest&);
+};
+
+} // namespace blink
+
+#endif // !defined(PlatformTraceEventsAgent_h)
diff --git a/chromium/third_party/blink/renderer/platform/probe/platform_probes.cc b/chromium/third_party/blink/renderer/platform/probe/platform_probes.cc
new file mode 100644
index 00000000000..406fb6adc3c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/probe/platform_probes.cc
@@ -0,0 +1,28 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/probe/platform_probes.h"
+
+namespace blink {
+namespace probe {
+
+TimeTicks ProbeBase::CaptureStartTime() const {
+ if (start_time_.is_null())
+ start_time_ = CurrentTimeTicks();
+ return start_time_;
+}
+
+TimeTicks ProbeBase::CaptureEndTime() const {
+ if (end_time_.is_null())
+ end_time_ = CurrentTimeTicks();
+ return end_time_;
+}
+
+TimeDelta ProbeBase::Duration() const {
+ DCHECK(!start_time_.is_null());
+ return CaptureEndTime() - start_time_;
+}
+
+} // namespace probe
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/probe/platform_probes.h b/chromium/third_party/blink/renderer/platform/probe/platform_probes.h
new file mode 100644
index 00000000000..e2913b7ecd7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/probe/platform_probes.h
@@ -0,0 +1,41 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_PROBE_PLATFORM_PROBES_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_PROBE_PLATFORM_PROBES_H_
+
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_context.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+class FetchContext;
+class PlatformProbeSink;
+
+namespace probe {
+
+class PLATFORM_EXPORT ProbeBase {
+ STACK_ALLOCATED();
+
+ public:
+ TimeTicks CaptureStartTime() const;
+ TimeTicks CaptureEndTime() const;
+ TimeDelta Duration() const;
+
+ private:
+ mutable TimeTicks start_time_;
+ mutable TimeTicks end_time_;
+};
+
+inline PlatformProbeSink* ToPlatformProbeSink(FetchContext* context) {
+ return context->GetPlatformProbeSink();
+}
+
+} // namespace probe
+} // namespace blink
+
+#include "third_party/blink/renderer/platform/PlatformProbesInl.h"
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_PROBE_PLATFORM_PROBES_H_
diff --git a/chromium/third_party/blink/renderer/platform/resolution_units.h b/chromium/third_party/blink/renderer/platform/resolution_units.h
new file mode 100644
index 00000000000..24fa95f4782
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/resolution_units.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * Copyright (C) 2009 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_RESOLUTION_UNITS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_RESOLUTION_UNITS_H_
+
+namespace blink {
+
+const double kMillimetersPerCentimeter = 10;
+const double kQuarterMillimetersPerCentimeter = 40;
+const double kCentimetersPerInch = 2.54;
+const double kMillimetersPerInch = 25.4;
+const double kQuarterMillimetersPerInch = 101.6;
+const double kPointsPerInch = 72;
+const double kPicasPerInch = 6;
+
+// The constant CSS pixels per inch value is needed in platform/ for font size
+// calculations.
+const double kCssPixelsPerInch = 96;
+const double kCssPixelsPerPoint = kCssPixelsPerInch / kPointsPerInch;
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/chromium/third_party/blink/renderer/platform/runtime_enabled_features.json5
new file mode 100644
index 00000000000..bd7e3cabb87
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -0,0 +1,1366 @@
+{
+ // http://dev.chromium.org/blink/runtime-enabled-features
+ //
+ // This list is used to generate runtime_enabled_features.h/cc which contains
+ // a class that stores static enablers for all experimental features.
+
+ parameters: {
+ // Each feature can be assigned a "status":
+ // * status=stable: Enable this in all Blink configurations. We are
+ // committed to these APIs indefinitely.
+ // * status=experimental: In-progress features, Web Developers might play
+ // with, but are not on by default in stable.
+ // * status=test: Enabled in ContentShell for testing, otherwise off.
+ // Features without a status are not enabled anywhere by default.
+ //
+ // "stable" features listed here should be rare, as anything which we've
+ // shipped stable can have its runtime flag removed soon after.
+ status: {
+ valid_values: ["stable", "experimental", "test"],
+ },
+
+ // "implied_by" or "depends_on" specifies relationship to other features:
+ // * implied_by: ["feature1","feature2",...]
+ // The feature is automatically enabled if any implied_by features is
+ // enabled;
+ // * depends_on: ["feature1","feature2",...]
+ // The feature is enabled only if all depends_on features are enabled.
+ // Only one of "implied_by" and "depends_on" can be specified.
+ implied_by: {
+ default: [],
+ valid_type: "list",
+ },
+ depends_on: {
+ default: [],
+ valid_type: "list",
+ },
+
+ // origin_trial_feature_name: "FEATURE_NAME" is used to integrate the
+ // feature with the Origin Trials framework. The framework allows the
+ // feature to be enabled at runtime on a per-page basis through a signed
+ // token for the corresponding feature name. Declaring the
+ // origin_trial_feature_name will cause a static method to be generated in
+ // origin_trials.h/cpp. This static method allows the feature implementation
+ // to check if it is enabled for the current context.
+ origin_trial_feature_name: {
+ },
+ origin_trial_os: {
+ default: [],
+ valid_type: "list",
+ },
+
+ // settable_from_internals specifies whether a feature can be set from
+ // internals.runtimeFlags, with the default being false.
+ settable_from_internals: {
+ valid_type: "bool",
+ },
+
+ // Set to true to have customised {feature}Enabled() method implementation.
+ custom: {
+ valid_type: "bool",
+ },
+
+ // Feature policy IDL extended attribute (see crrev.com/2247923004).
+ feature_policy: {
+ },
+ },
+
+ data: [
+ {
+ name: "Accelerated2dCanvas",
+ settable_from_internals: true,
+ status: "stable",
+ },
+ {
+ name: "AccessibilityObjectModel",
+ status: "experimental",
+ },
+ {
+ name: "AllowActivationDelegationAttr",
+ },
+ {
+ name: "AllowContentInitiatedDataUrlNavigations",
+ status: "stable",
+ },
+ {
+ name: "AnimationWorklet",
+ origin_trial_feature_name: "AnimationWorklet",
+ status: "experimental",
+ },
+ {
+ name: "AsyncClipboard",
+ status: "experimental",
+ },
+ {
+ name: "AsyncCookies",
+ status: "experimental",
+ },
+ {
+ name: "AudioOutputDevices",
+ status: "stable",
+ },
+ {
+ name: "AudioVideoTracks",
+ status: "experimental",
+ },
+ {
+ name: "AudioWorklet",
+ depends_on: ["Worklet"],
+ status: "stable",
+ },
+ {
+ name: "AutomationControlled",
+ settable_from_internals: true,
+ },
+ {
+ name: "AutoplayMutedVideos",
+ settable_from_internals: true,
+ },
+ {
+ name: "BackgroundFetch",
+ status: "experimental",
+ },
+ {
+ name: "BackgroundVideoTrackOptimization",
+ status: "stable",
+ },
+ {
+ name: "BlinkRuntimeCallStats",
+ },
+ {
+ name: "BlockCredentialedSubresources",
+ status: "stable",
+ },
+ {
+ name: "BlockingDownloadsInSandbox",
+ },
+ {
+ name: "BlockMetaSetCookie",
+ status: "stable"
+ },
+ {
+ name: "Budget",
+ status: "stable",
+ },
+ {
+ name: "BudgetQuery",
+ origin_trial_feature_name: "BudgetQuery",
+ status: "experimental",
+ },
+ {
+ name: "CacheInlineScriptCode"
+ },
+ {
+ name: "CacheStyleSheetWithMediaQueries",
+ status: "experimental",
+ },
+ {
+ name: "Canvas2dContextLostRestored",
+ status: "experimental",
+ },
+ {
+ name: "Canvas2dFixedRenderingMode",
+ status: "test",
+ },
+ {
+ name: "Canvas2dImageChromium",
+ },
+ {
+ name: "Canvas2dScrollPathIntoView",
+ status: "experimental",
+ },
+ {
+ name: "CanvasColorManagement",
+ status: "experimental",
+ },
+ {
+ name: "CanvasHitRegion",
+ status: "experimental",
+ },
+ {
+ name: "CanvasImageSmoothing",
+ status: "experimental",
+ },
+ {
+ name: "CanvasTransformations",
+ status: "experimental",
+ },
+ {
+ name: "ClickRetargetting",
+ status: "experimental",
+ },
+ {
+ name: "ClientHintsPersistent",
+ status: "stable",
+ },
+ {
+ name: "ClientPlaceholdersForServerLoFi",
+ },
+ {
+ name: "CodeCacheAfterExecute"
+ },
+ {
+ name: "CompositedSelectionUpdate",
+ },
+ {
+ name: "CompositeOpaqueFixedPosition",
+ settable_from_internals: true,
+ status: "experimental",
+ },
+ {
+ name: "CompositeOpaqueScrollers",
+ settable_from_internals: true,
+ status: "stable",
+ },
+ {
+ name: "CompositorTouchAction",
+ status: "test",
+ },
+ {
+ name: "ComputedAccessibilityInfo",
+ status: "experimental",
+ },
+ {
+ name: "ConicGradient",
+ status: "experimental",
+ },
+ {
+ name: "ConstructableStylesheets",
+ status: "experimental",
+ },
+ {
+ name: "ContextMenu",
+ status: "experimental",
+ },
+ {
+ name: "CorsRFC1918",
+ },
+ {
+ name: "CSS3Text",
+ status: "experimental",
+ },
+ {
+ name: "CSSAdditiveAnimations",
+ depends_on: ["StackedCSSPropertyAnimations"],
+ status: "experimental",
+ },
+ {
+ name: "CSSBackdropFilter",
+ status: "experimental",
+ },
+ {
+ name: "CSSDisplayContents",
+ status: "stable",
+ },
+ {
+ name: "CSSFocusVisible",
+ status: "experimental",
+ },
+ {
+ name: "CSSFontSizeAdjust",
+ status: "experimental",
+ },
+ {
+ name: "CSSHexAlphaColor",
+ status: "stable",
+ },
+ {
+ name: "CSSInBodyDoesNotBlockPaint",
+ status: "experimental",
+ },
+ {
+ name: "CSSIndependentTransformProperties",
+ status: "experimental",
+ },
+ {
+ name: "CSSLayoutAPI",
+ status: "experimental",
+ },
+ {
+ name: "CSSMaskSourceType",
+ status: "experimental",
+ },
+ {
+ name: "CSSMatches",
+ status: "experimental",
+ },
+ {
+ name: "CSSOffsetPathRay",
+ status: "experimental",
+ },
+ {
+ name: "CSSOffsetPathRayContain",
+ status: "experimental",
+ },
+ {
+ name: "CSSOffsetPositionAnchor",
+ status: "experimental",
+ },
+ {
+ name: "CSSOMSmoothScroll",
+ status: "stable",
+ },
+ {
+ name: "CSSOverscrollBehavior",
+ status: "stable",
+ },
+ {
+ name: "CSSPaintAPI",
+ depends_on: ["Worklet"],
+ status: "stable",
+ },
+ {
+ name: "CSSPaintAPIArguments",
+ depends_on: ["CSSPaintAPI"],
+ status: "experimental",
+ },
+ {
+ name: "CSSPartPseudoElement",
+ status: "test",
+ },
+ {
+ name: "CSSScrollSnapPoints",
+ status: "experimental",
+ },
+ {
+ name: "CSSSnapSize",
+ status: "experimental",
+ },
+ {
+ name: "CSSTransformBox",
+ status: "stable",
+ },
+ {
+ name: "CSSTypedOM",
+ status: "stable",
+ },
+ {
+ name: "CSSVariables2",
+ status: "experimental",
+ },
+ {
+ name: "CSSViewport",
+ status: "experimental",
+ },
+ {
+ name: "CustomElementDefaultStyle",
+ status: "experimental",
+ },
+ {
+ name: "CustomElementsBuiltin",
+ status: "stable",
+ },
+ {
+ name: "CustomUserTiming",
+ },
+ {
+ name: "Database",
+ status: "stable",
+ },
+ {
+ name: "DecodeToYUV",
+ status: "experimental",
+ },
+ {
+ name: "DeprecationReporting",
+ status: "experimental",
+ },
+ {
+ name: "DesktopCaptureDisableLocalEchoControl",
+ status: "experimental",
+ },
+ {
+ name: "DeviceMemoryHeader",
+ status: "stable",
+ },
+ {
+ name: "DisableHardwareNoiseSuppression",
+ origin_trial_feature_name: "DisableHardwareNoiseSuppression",
+ status: "experimental",
+ },
+ {
+ name: "DisablePaintChunksToCcLayer",
+ },
+ {
+ name: "DisableRasterInvalidation",
+ },
+ {
+ name: "DisplayNoneIFrameCreatesNoLayoutObject",
+ status: "stable",
+ },
+ {
+ name: "DocumentCookie",
+ },
+ {
+ name: "DocumentDomain",
+ },
+ {
+ name: "DocumentWrite",
+ },
+ {
+ name: "EmbedderCSPEnforcement",
+ status: "stable",
+ },
+ {
+ name: "EncryptedMediaHdcpPolicyCheck",
+ status: "test",
+ },
+ {
+ name: "ExecCommandInJavaScript",
+ status: "test",
+ },
+ {
+ name: "ExpensiveBackgroundTimerThrottling",
+ status: "stable",
+ },
+ {
+ name: "ExperimentalContentSecurityPolicyFeatures",
+ status: "experimental",
+ },
+ {
+ name: "ExperimentalHardwareEchoCancellation",
+ origin_trial_feature_name: "ExperimentalHardwareEchoCancellation",
+ status: "experimental",
+ },
+ {
+ name: "ExperimentalV8Extras",
+ status: "experimental",
+ },
+ {
+ name: "ExtendedImageBitmapOptions",
+ status: "experimental",
+ },
+ {
+ name: "ExtendedTextMetrics",
+ status: "experimental",
+ },
+ {
+ name: "ExtraWebGLVideoTextureMetadata",
+ status: "experimental",
+ },
+ {
+ name: "FastMobileScrolling",
+ },
+ {
+ name: "FeaturePolicyAutoplayFeature",
+ status: "stable"
+ },
+ {
+ name: "FeaturePolicyExperimentalFeatures",
+ status: "experimental"
+ },
+ {
+ name: "FeaturePolicyForPermissions",
+ status: "stable"
+ },
+ {
+ name: "FeaturePolicyJavaScriptInterface",
+ status: "experimental"
+ },
+ {
+ name: "FeaturePolicyVibrateFeature"
+ },
+ {
+ name: "FetchRequestCache",
+ status: "stable",
+ },
+ {
+ name: "FetchRequestKeepalive",
+ status: "stable",
+ },
+ {
+ name: "FetchRequestSignal",
+ status: "stable",
+ },
+ {
+ name: "FileSystem",
+ status: "stable",
+ },
+ {
+ name: "FocusOptions",
+ status: "stable",
+ },
+ {
+ name: "FontCacheScaling",
+ status: "test",
+ },
+ // For simulating Android's overlay fullscreen video in layout tests on Linux.
+ {
+ name: "ForceOverlayFullscreenVideo",
+ },
+ {
+ name: "ForceTallerSelectPopup",
+ },
+ {
+ name: "FormDataEvent",
+ status: "experimental",
+ },
+ {
+ name: "FractionalMouseEvent",
+ },
+ {
+ name: "FractionalMouseTypePointerEvent",
+ status: "stable",
+ },
+ {
+ name: "FractionalScrollOffsets",
+ },
+ {
+ name: "FramebustingNeedsSameOriginOrUserGesture",
+ settable_from_internals: true,
+ status: "experimental",
+ },
+ {
+ name: "FramesTimingFunction",
+ status: "experimental",
+ },
+ {
+ name: "FullscreenUnprefixed",
+ settable_from_internals: true,
+ status: "experimental",
+ },
+ {
+ name: "GamepadExtensions",
+ origin_trial_feature_name: "WebVR1.1M62",
+ status: "experimental",
+ },
+ {
+ name: "GeometryInterfaces",
+ status: "stable",
+ },
+ {
+ name: "GetMatchedCSSRules",
+ },
+ {
+ name: "HeapCompaction",
+ status: "stable",
+ },
+ {
+ name: "HeapIncrementalMarking",
+ },
+ // https://crbug.com/766694 for testing disabling the feature.
+ {
+ name: "HTMLImports",
+ status: "stable",
+ },
+ // https://crbug.com/523952 for testing disabling the feature.
+ {
+ name: "HTMLImportsStyleApplication",
+ status: "stable",
+ },
+ {
+ name: "IDBObserver",
+ status: "experimental",
+ },
+ {
+ name: "IdleTimeColdModeSpellChecking",
+ status: "test",
+ },
+ {
+ name: "ImageDecodingAttribute",
+ status: "stable",
+ },
+ {
+ name: "ImageOrientation",
+ status: "test",
+ },
+ {
+ name: "ImplicitRootScroller",
+ status: "experimental",
+ settable_from_internals: true,
+ },
+ {
+ name: "IncrementalShadowDOM",
+ },
+ {
+ name: "InertAttribute",
+ status: "experimental",
+ },
+ {
+ name: "InputModeAttribute",
+ status: "stable",
+ },
+ {
+ name: "InputMultipleFieldsUI",
+ status: "stable",
+ },
+ {
+ name: "InstalledApp",
+ origin_trial_feature_name: "InstalledApp",
+ status: "experimental",
+ },
+ {
+ name: "JSImageDecode",
+ status: "stable",
+ },
+ {
+ name: "KeyboardLock",
+ status: "test",
+ },
+ {
+ name: "LangAttributeAwareFormControlUI",
+ },
+ {
+ name: "LayoutNG",
+ implied_by: ["LayoutNGBlockFragmentation", "LayoutNGFlexBox"],
+ },
+ {
+ name: "LayoutNGBlockFragmentation",
+ },
+ {
+ name: "LayoutNGFlexBox",
+ },
+ {
+ name: "LayoutNGFragmentCaching",
+ },
+ {
+ name: "LazyFrameLoading",
+ },
+ {
+ name: "LazyInitializeMediaControls",
+ // This is enabled by features::kLazyInitializeMediaControls.
+ },
+ {
+ name: "LazyParseCSS",
+ status: "experimental",
+ },
+ {
+ name: "LoadingWithMojo",
+ status: "stable",
+ },
+ {
+ name: "LongTaskObserver",
+ status: "stable",
+ },
+ {
+ name: "LongTaskV2",
+ },
+ {
+ name: "LowLatencyCanvas",
+ origin_trial_feature_name: "LowLatencyCanvas",
+ status: "experimental",
+ },
+ {
+ name: "MediaCapabilities",
+ origin_trial_feature_name: "MediaCapabilities",
+ status: "stable",
+ },
+ {
+ name: "MediaCapabilitiesEncodingInfo",
+ status: "experimental",
+ },
+ {
+ name: "MediaCapture",
+ },
+ {
+ name: "MediaCaptureDepth",
+ status: "experimental",
+ },
+ {
+ name: "MediaCaptureDepthVideoKind",
+ status: "experimental",
+ },
+ {
+ name: "MediaCaptureFromVideo",
+ status: "stable",
+ },
+ // Set to reflect the MediaCastOverlayButton feature.
+ {
+ name: "MediaCastOverlayButton",
+ },
+ {
+ name: "MediaControlsOverlayPlayButton",
+ settable_from_internals: true,
+ },
+ {
+ name: "MediaDocumentDownloadButton",
+ },
+ // Set to reflect the kMediaEngagementBypassAutoplayPolicies feature.
+ {
+ name: "MediaEngagementBypassAutoplayPolicies",
+ },
+ {
+ name: "MediaQueryShape",
+ status: "experimental",
+ },
+ // MediaSession is enabled by default on Android only.
+ // TODO(rbyers): Add parameter to specify platform.
+ {
+ name: "MediaSession",
+ status: "test",
+ },
+ {
+ name: "MediaSourceExperimental",
+ status: "experimental",
+ },
+ {
+ name: "MediaSourceNewAbortAndDuration",
+ status: "experimental",
+ },
+ {
+ name: "MediaStreamTrackContentHint",
+ status: "experimental",
+ },
+ // This is enabled by default on Windows only. The only part that's
+ // "experimental" is the support on other platforms.
+ {
+ name: "MiddleClickAutoscroll",
+ status: "test",
+ },
+ {
+ name: "MobileLayoutTheme",
+ },
+ {
+ name: "ModernMediaControls",
+ },
+ {
+ name: "ModuleDedicatedWorker",
+ status: "experimental",
+ },
+ {
+ name: "ModulePreload",
+ status: "stable",
+ },
+ {
+ name: "ModuleScriptsDynamicImport",
+ },
+ {
+ name: "ModuleScriptsImportMetaUrl",
+ },
+ {
+ name: "MojoBlobURLs",
+ },
+ {
+ name: "MojoJS",
+ status: "test",
+ },
+ // MojoJSTest is used exclusively in testing environments, whereas MojoJS
+ // may also be used elsewhere.
+ {
+ name: "MojoJSTest",
+ status: "test",
+ },
+ {
+ name: "MultipleColorStopPositions",
+ status: "experimental",
+ },
+ {
+ name: "NavigatorContentUtils",
+ },
+ {
+ name: "NavigatorDeviceMemory",
+ status: "stable",
+ },
+ {
+ name: "NetInfoDownlink",
+ status: "stable",
+ },
+ {
+ name: "NetInfoDownlinkHeader",
+ status: "experimental",
+ },
+ {
+ name: "NetInfoDownlinkMax",
+ status: "stable",
+ },
+ {
+ name: "NetInfoEffectiveType",
+ status: "stable",
+ },
+ {
+ name: "NetInfoEffectiveTypeHeader",
+ status: "experimental",
+ },
+ {
+ name: "NetInfoRtt",
+ status: "stable",
+ },
+ {
+ name: "NetInfoRttHeader",
+ status: "experimental",
+ },
+ {
+ name: "NetInfoSaveData",
+ status: "stable",
+ },
+ {
+ name: "NetworkService",
+ },
+ // Not a web exposed feature, enabled from the command line.
+ {
+ name: "NewRemotePlaybackPipeline",
+ },
+ {
+ name: "NotificationConstructor",
+ status: "stable",
+ },
+ // NotificationContentImage is not available in all platforms
+ {
+ name: "NotificationContentImage",
+ status: "test",
+ },
+ {
+ name: "NotificationInlineReplies",
+ status: "experimental",
+ },
+ {
+ name: "Notifications",
+ status: "stable",
+ },
+ {
+ name: "NotificationsWithMojo",
+ },
+ {
+ name: "NullableDocumentDomain",
+ status: "experimental",
+ },
+ {
+ name: "OffMainThreadWebSocket",
+ },
+ {
+ name: "OffscreenCanvas",
+ status: "experimental",
+ },
+ {
+ name: "OffscreenCanvasText",
+ status: "experimental",
+ },
+ {
+ name: "OnDeviceChange",
+ status: "stable",
+ },
+ {
+ name: "OrientationEvent",
+ },
+ {
+ name: "OriginManifest",
+ status: "test",
+ },
+ {
+ name: "OriginTrials",
+ status: "stable",
+ },
+ // Define a sample API for testing integration with the Origin Trials
+ // Framework. The sample API is used in both unit and layout tests for the
+ // Origin Trials Framework. Do not change this flag to stable, as it exists
+ // solely to generate code used by the sample API implementation.
+ {
+ name: "OriginTrialsSampleAPI",
+ origin_trial_feature_name: "Frobulate",
+ },
+ // Define a sample API for testing integration with the Origin Trials
+ // Framework. The sample API is used in both unit and layout tests for the
+ // Origin Trials Framework. Do not change this flag to stable, as it exists
+ // solely to generate code used by the sample API implementation.
+ {
+ name: "OriginTrialsSampleAPIImplied",
+ origin_trial_feature_name: "FrobulateImplied",
+ implied_by: ["OriginTrialsSampleAPI"],
+ },
+ {
+ name: "OutOfBlinkCORS",
+ },
+ {
+ name: "OverflowIconsForMediaControls",
+ },
+ {
+ name: "OverlayScrollbars",
+ settable_from_internals: true,
+ },
+ {
+ name: "PageLifecycle",
+ status: "experimental",
+ },
+ {
+ name: "PagePopup",
+ status: "stable",
+ },
+ {
+ name: "PaintUnderInvalidationChecking",
+ settable_from_internals: true,
+ },
+ {
+ name: "PartialRasterInvalidation",
+ },
+ {
+ name: "PassiveDocumentEventListeners",
+ status: "stable",
+ },
+ {
+ name: "PassPaintVisualRectToCompositor",
+ },
+ {
+ name: "PaymentApp",
+ status: "experimental",
+ },
+ {
+ name: "PaymentDetailsModifierData",
+ status: "stable",
+ },
+ // PaymentRequest is enabled by default on Android
+ {
+ name: "PaymentRequest",
+ status: "experimental",
+ },
+ {
+ name: "PaymentRequestBasicCard",
+ status: "stable",
+ },
+ {
+ name: "PerformanceNavigationTiming2",
+ status: "stable",
+ },
+ {
+ name: "PerformancePaintTiming",
+ status: "stable",
+ },
+ {
+ name: "PermissionDelegation",
+ status: "test",
+ },
+ {
+ name: "Permissions",
+ status: "stable",
+ },
+ {
+ name: "PermissionsRequestRevoke",
+ status: "experimental",
+ },
+ {
+ name: "PictureInPicture",
+ settable_from_internals: true,
+ },
+ {
+ name: "PictureInPictureAPI",
+ status: "test",
+ },
+ {
+ name: "PreciseMemoryInfo",
+ },
+ {
+ name: "PreferredImageRasterBounds",
+ settable_from_internals: true,
+ },
+ // This feature is deprecated and we are evangelizing affected sites.
+ // See https://crbug.com/346236 for current status.
+ {
+ name: "PrefixedVideoFullscreen",
+ status: "stable",
+ },
+ {
+ name: "PreloadDefaultIsMetadata",
+ status: "stable",
+ },
+ {
+ name: "PreloadImageSrcSet",
+ status: "experimental",
+ },
+ {
+ name: "Presentation",
+ status: "stable",
+ },
+ {
+ name: "PrintBrowser",
+ },
+ {
+ name: "PushMessaging",
+ status: "stable",
+ },
+ {
+ name: "PWAFullCodeCache"
+ },
+ {
+ name: "RasterInducingScroll",
+ status: "experimental",
+ },
+ {
+ name: "ReducedReferrerGranularity",
+ },
+ {
+ name: "RemotePlayback",
+ status: "stable",
+ },
+ // Whether or not the Remote Playback API backend is implemented.
+ // Not web-exposed, set per platform. For LayoutTests, disabled by
+ // default.
+ {
+ name: "RemotePlaybackBackend",
+ settable_from_internals: true,
+ },
+ {
+ name: "RenderingPipelineThrottling",
+ status: "stable",
+ },
+ {
+ name: "RenderingPipelineThrottlingLoadingIframes",
+ status: "stable",
+ },
+ {
+ name: "RenderUnicodeControlCharacters",
+ status: "stable",
+ },
+ {
+ name: "ReportingObserver",
+ status: "experimental",
+ },
+ {
+ name: "RequireCSSExtensionForFile",
+ status: "stable",
+ },
+ {
+ name: "ResizeObserver",
+ status: "stable",
+ },
+ {
+ name: "ResourceLoadScheduler",
+ status: "experimental",
+ },
+ {
+ name: "RestrictAppCacheToSecureContexts",
+ status: "experimental",
+ },
+ {
+ name: "RestrictCanRequestURLCharacterSet",
+ status: "stable",
+ },
+ // Handles frame scrolling via the root PaintLayer instead of the FrameView.
+ // The master bug for the root layer scrolling project is crbug.com/417782.
+ // This REF is enabled iff features::kRootLayerScrolling is on or
+ // experimental web platform features are enabled.
+ {
+ name: "RootLayerScrolling",
+ status: "stable",
+ },
+ {
+ name: "RtcPeerConnectionId",
+ origin_trial_feature_name: "RtcPeerConnectionId",
+ status: "experimental",
+ },
+ {
+ name: "RTCRtpSenderParameters",
+ status: "experimental",
+ },
+ // Enables the use of |RTCConfiguration::sdpSemantics| to override the
+ // default SDP semantics at RTCPeerConnection construction.
+ {
+ name: "RTCUnifiedPlan",
+ status: "experimental",
+ },
+ // Overrides the default SDP semantics to be Unified Plan at
+ // RTCPeerConnection construction (unless otherwise specified).
+ {
+ name: "RTCUnifiedPlanByDefault",
+ },
+ {
+ name: "ScriptedSpeech",
+ status: "stable",
+ },
+ // Scrolls to compensate for layout movements (bit.ly/scroll-anchoring).
+ {
+ name: "ScrollAnchoring",
+ settable_from_internals: true,
+ status: "experimental",
+ },
+ // Serialize and restore scroll anchors.
+ {
+ name: "ScrollAnchorSerialization",
+ },
+ {
+ name: "ScrollCustomization",
+ },
+ // Implements documentElement.scrollTop/Left and bodyElement.scrollTop/Left
+ // as per the spec, matching other Web engines.
+ {
+ name: "ScrollTopLeftInterop",
+ status: "stable",
+ },
+ {
+ name: "SendBeaconThrowForBlobWithNonSimpleType",
+ status: "stable",
+ },
+ {
+ name: "SendMouseEventsDisabledFormControls",
+ status: "experimental",
+ },
+ {
+ name: "Sensor",
+ origin_trial_feature_name: "GenericSensor",
+ status: "experimental",
+ },
+ {
+ name: "SensorExtraClasses",
+ depends_on: ["Sensor"],
+ status: "experimental",
+ },
+ {
+ name: "ServerTiming",
+ status: "stable",
+ },
+ {
+ name: "ServiceWorkerScriptFullCodeCache",
+ },
+ {
+ name: "ServiceWorkerUpdateViaCache",
+ status: "experimental",
+ },
+ {
+ name: "SetRootScroller",
+ status: "experimental",
+ },
+ // Introduced this flag as stable so web developers can test their sites
+ // without native Shadow DOM v0 support
+ {
+ name: "ShadowDOMV0",
+ status: "stable",
+ },
+ {
+ name: "ShadowPiercingDescendantCombinator",
+ status: "experimental",
+ },
+ {
+ name: "ShapeDetection",
+ status: "experimental",
+ },
+ {
+ name: "SharedArrayBuffer",
+ status: "stable",
+ },
+ {
+ name: "SharedWorker",
+ status: "stable",
+ },
+ {
+ name: "SignatureBasedIntegrity",
+ origin_trial_feature_name: "SignatureBasedIntegrity",
+ status: "experimental",
+ },
+ {
+ name: "SlimmingPaintV175",
+ status: "stable",
+ implied_by: ["SlimmingPaintV2", "LayoutNG"],
+ },
+ {
+ name: "SlimmingPaintV2",
+ },
+ {
+ name: "SlotInFlatTree",
+ status: "stable",
+ },
+ {
+ name: "SMIL",
+ status: "stable",
+ },
+ {
+ name: "SmoothScrollJSIntervention",
+ status: "stable",
+ },
+ // Used as argument in attribute of stable-release functions/interfaces
+ // where a runtime-enabled feature name is required for correct IDL syntax.
+ // This is a global flag; do not change its status.
+ {
+ name: "StableBlinkFeatures",
+ status: "stable",
+ },
+ {
+ name: "StackedCSSPropertyAnimations",
+ status: "experimental",
+ },
+ {
+ name: "StopInBackground",
+ status: "test",
+ },
+ {
+ name: "StopLoadingInBackground",
+ status: "test",
+ },
+ {
+ name: "StopNonTimersInBackground",
+ status: "test",
+ },
+ {
+ name: "TimerThrottlingForBackgroundTabs",
+ status: "stable",
+ },
+ {
+ name: "TimerThrottlingForHiddenFrames",
+ status: "stable",
+ },
+ {
+ name: "TopNavByUserActivationInSandbox",
+ status: "stable",
+ },
+ // Many websites disable mouse support when touch APIs are available. We'd
+ // like to enable this always but can't until more websites fix this bug.
+ // Chromium sets this conditionally (eg. based on the presence of a
+ // touchscreen) in ApplyWebPreferences. "Touch events" themselves are always
+ // enabled since they're a feature always supported by Chrome.
+ {
+ name: "TouchEventFeatureDetection",
+ origin_trial_feature_name: "ForceTouchEventFeatureDetectionForInspector",
+ status: "stable",
+ },
+ // This is set conditionally in SetRuntimeFeaturesDefaultsAndUpdateFromArgs
+ // and will eventually go away when the touchpad/wheel scroll latching is
+ // enabled on all platforms (http://crbug.com/526463).
+ {
+ name: "TouchpadAndWheelScrollLatching",
+ },
+ // When enabled, tracks the number of times each LayoutBlock
+ // goes through layout. For testing purposes only.
+ {
+ name: "TrackLayoutPassesPerBlock",
+ },
+ {
+ name: "TrustedDOMTypes",
+ status: "experimental",
+ },
+ {
+ name: "TrustedEventsDefaultAction",
+ status: "stable",
+ },
+ {
+ name: "TurnOff2DAndOpacityCompositorAnimations",
+ },
+ {
+ name: "UnclosedFormControlIsInvalid",
+ status: "experimental",
+ },
+ {
+ name: "UnifiedTouchAdjustment",
+ status: "experimental",
+ },
+ {
+ name: "UpdateHoverPostLayout",
+ status: "stable",
+ },
+ {
+ name: "UserActivationV2",
+ },
+ {
+ name: "V8ContextSnapshot",
+ status: "test",
+ },
+ {
+ name: "V8IdleTasks",
+ },
+ {
+ name: "VideoFullscreenDetection",
+ },
+ {
+ name: "VideoFullscreenOrientationLock",
+ },
+ {
+ name: "VideoRotateToFullscreen",
+ },
+ {
+ name: "VisibilityCollapseColumn",
+ status: "experimental",
+ },
+ {
+ name: "VisibilityCollapseRow",
+ status: "stable",
+ },
+ {
+ name: "VisualViewportAPI",
+ status: "stable",
+ },
+ {
+ name: "WakeLock",
+ status: "experimental",
+ },
+ {
+ name: "WebAnimationsAPI",
+ status: "experimental",
+ },
+ {
+ name: "WebAnimationsSVG",
+ status: "experimental",
+ },
+ {
+ name: "WebAssemblyStreaming",
+ status: "experimental",
+ },
+ {
+ name: "WebAuth",
+ status: "experimental",
+ },
+ // WebBluetooth is enabled by default on Android, ChromeOS and Mac.
+ {
+ name: "WebBluetooth",
+ status: "experimental",
+ },
+ {
+ name: "WebFontsCacheAwareTimeoutAdaptation",
+ status: "experimental",
+ },
+ {
+ name: "WebGLDraftExtensions",
+ status: "experimental",
+ },
+ {
+ name: "WebGLImageChromium",
+ },
+ {
+ name: "WebLocksAPI",
+ origin_trial_feature_name: "WebLocksAPI",
+ status: "experimental",
+ },
+ {
+ name: "WebNFC",
+ status: "experimental",
+ },
+ // WebShare is enabled by default on Android.
+ {
+ name: "WebShare",
+ status: "experimental",
+ },
+ {
+ name: "WebUSB",
+ status: "stable",
+ },
+ {
+ name: "WebVR",
+ origin_trial_feature_name: "WebVR1.1M62",
+ status: "experimental",
+ },
+ {
+ name: "WebVTTRegions",
+ status: "experimental",
+ },
+ {
+ name: "WebXR",
+ origin_trial_feature_name: "WebXRDevice",
+ status: "experimental",
+ },
+ // Subset of the GamepadExtensions wanted for WebXR, which expose VR controller
+ // functionality as Gamepads.
+ {
+ name: "WebXRGamepadSupport",
+ origin_trial_feature_name: "WebXRGamepadSupport",
+ // This feature is a strict subset of GamepadExtensions.
+ implied_by: ["GamepadExtensions"],
+ status: "experimental",
+ },
+ {
+ name: "WorkerNosniffBlock",
+ status: "test",
+ },
+ {
+ name: "WorkerNosniffWarn",
+ status: "test",
+ implied_by: ["WorkerNosniffBlock"],
+ },
+ {
+ name: "Worklet",
+ status: "stable",
+ },
+ {
+ name: "WorkStealingInScriptRunner",
+ status: "experimental",
+ },
+ {
+ name: "XSLT",
+ status: "stable",
+ },
+ ],
+}
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/BUILD.gn b/chromium/third_party/blink/renderer/platform/scheduler/BUILD.gn
new file mode 100644
index 00000000000..c8ee15c3a53
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/BUILD.gn
@@ -0,0 +1,258 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/jumbo.gni")
+import("//third_party/blink/renderer/platform/platform.gni")
+
+blink_platform_sources("scheduler") {
+ sources = [
+ "base/enqueue_order.cc",
+ "base/enqueue_order.h",
+ "base/graceful_queue_shutdown_helper.cc",
+ "base/graceful_queue_shutdown_helper.h",
+ "base/intrusive_heap.h",
+ "base/lazy_now.cc",
+ "base/lazy_now.h",
+ "base/moveable_auto_lock.h",
+ "base/real_time_domain.cc",
+ "base/real_time_domain.h",
+ "base/sequenced_task_source.h",
+ "base/task_queue.cc",
+ "base/task_queue.h",
+ "base/task_queue_impl.cc",
+ "base/task_queue_impl.h",
+ "base/task_queue_manager.h",
+ "base/task_queue_manager_impl.cc",
+ "base/task_queue_manager_impl.h",
+ "base/task_queue_selector.cc",
+ "base/task_queue_selector.h",
+ "base/task_queue_selector_logic.h",
+ "base/task_time_observer.h",
+ "base/thread_controller.h",
+ "base/thread_controller_impl.cc",
+ "base/thread_controller_impl.h",
+ "base/time_domain.cc",
+ "base/time_domain.h",
+ "base/virtual_time_domain.cc",
+ "base/virtual_time_domain.h",
+ "base/work_queue.cc",
+ "base/work_queue.h",
+ "base/work_queue_sets.cc",
+ "base/work_queue_sets.h",
+ "child/cancelable_closure_holder.cc",
+ "child/cancelable_closure_holder.h",
+ "child/compositor_metrics_helper.cc",
+ "child/compositor_metrics_helper.h",
+ "child/default_params.h",
+ "child/features.h",
+ "child/idle_canceled_delayed_task_sweeper.cc",
+ "child/idle_canceled_delayed_task_sweeper.h",
+ "child/idle_helper.cc",
+ "child/idle_helper.h",
+ "child/metrics_helper.cc",
+ "child/metrics_helper.h",
+ "child/page_visibility_state.cc",
+ "child/page_visibility_state.h",
+ "child/pollable_thread_safe_flag.cc",
+ "child/pollable_thread_safe_flag.h",
+ "child/process_state.cc",
+ "child/process_state.h",
+ "common/scheduler_helper.cc",
+ "common/scheduler_helper.h",
+ "child/single_thread_idle_task_runner.cc",
+ "child/task_runner_impl.cc",
+ "child/task_runner_impl.h",
+ "child/web_scheduler.h",
+ "child/web_scheduler_impl.cc",
+ "child/web_scheduler_impl.h",
+ "child/webthread_base.cc",
+ "child/webthread_impl_for_worker_scheduler.cc",
+ "child/webthread_impl_for_worker_scheduler.h",
+ "child/worker_global_scope_scheduler.cc",
+ "child/worker_global_scope_scheduler.h",
+ "child/worker_metrics_helper.cc",
+ "child/worker_metrics_helper.h",
+ "worker/worker_scheduler_helper.cc",
+ "worker/worker_scheduler_helper.h",
+ "child/worker_scheduler_proxy.cc",
+ "child/worker_scheduler_proxy.h",
+ "child/worker_task_queue.cc",
+ "child/worker_task_queue.h",
+ "common/throttling/budget_pool.cc",
+ "common/throttling/budget_pool.h",
+ "common/throttling/cpu_time_budget_pool.cc",
+ "common/throttling/cpu_time_budget_pool.h",
+ "common/throttling/task_queue_throttler.cc",
+ "common/throttling/task_queue_throttler.h",
+ "common/throttling/throttled_time_domain.cc",
+ "common/throttling/throttled_time_domain.h",
+ "common/throttling/wake_up_budget_pool.cc",
+ "common/throttling/wake_up_budget_pool.h",
+ "main_thread/frame_origin_type.cc",
+ "main_thread/frame_origin_type.h",
+ "main_thread/frame_scheduler_impl.cc",
+ "main_thread/frame_scheduler_impl.h",
+ "main_thread/main_thread_scheduler_impl.cc",
+ "main_thread/main_thread_scheduler_impl.h",
+ "main_thread/page_scheduler_impl.cc",
+ "main_thread/page_scheduler_impl.h",
+ "main_thread/web_main_thread_scheduler.cc",
+ "main_thread/web_render_widget_scheduling_state.cc",
+ "public/frame_or_worker_global_scope_scheduler.h",
+ "public/frame_scheduler.h",
+ "public/non_main_thread_scheduler.h",
+ "public/page_scheduler.h",
+ "public/web_main_thread_scheduler.h",
+ "renderer/auto_advancing_virtual_time_domain.cc",
+ "renderer/auto_advancing_virtual_time_domain.h",
+ "renderer/deadline_task_runner.cc",
+ "renderer/deadline_task_runner.h",
+ "renderer/frame_status.cc",
+ "renderer/frame_status.h",
+ "renderer/idle_time_estimator.cc",
+ "renderer/idle_time_estimator.h",
+ "main_thread/main_thread_scheduler_helper.cc",
+ "main_thread/main_thread_scheduler_helper.h",
+ "renderer/main_thread_task_queue.cc",
+ "renderer/main_thread_task_queue.h",
+ "renderer/queueing_time_estimator.cc",
+ "renderer/queueing_time_estimator.h",
+ "renderer/render_widget_signals.cc",
+ "renderer/render_widget_signals.h",
+ "renderer/renderer_metrics_helper.cc",
+ "renderer/renderer_metrics_helper.h",
+ "renderer/renderer_web_scheduler_impl.cc",
+ "renderer/renderer_web_scheduler_impl.h",
+ "renderer/task_cost_estimator.cc",
+ "renderer/task_cost_estimator.h",
+ "renderer/user_model.cc",
+ "renderer/user_model.h",
+ "renderer/web_scoped_virtual_time_pauser.cc",
+ "renderer/webthread_impl_for_renderer_scheduler.cc",
+ "renderer/webthread_impl_for_renderer_scheduler.h",
+ "util/aggregated_metric_reporter.h",
+ "util/task_duration_metric_reporter.cc",
+ "util/task_duration_metric_reporter.h",
+ "util/thread_cpu_throttler.cc",
+ "util/thread_cpu_throttler.h",
+ "util/thread_load_tracker.cc",
+ "util/thread_load_tracker.h",
+ "util/thread_type.h",
+ "util/tracing_helper.cc",
+ "util/tracing_helper.h",
+ "utility/webthread_impl_for_utility_thread.cc",
+ "utility/webthread_impl_for_utility_thread.h",
+ "worker/compositor_thread_scheduler.cc",
+ "worker/compositor_thread_scheduler.h",
+ "worker/non_main_thread_scheduler.cc",
+ "worker/worker_thread_scheduler.cc",
+ "worker/worker_thread_scheduler.h",
+ ]
+
+ deps = [
+ "//base",
+ "//cc",
+ "//device/base/synchronization",
+ "//services/metrics/public/cpp:ukm_builders",
+ "//third_party/blink/renderer/platform:make_platform_generated",
+ "//third_party/blink/renderer/platform/wtf",
+ ]
+}
+
+jumbo_source_set("test_support") {
+ testonly = true
+
+ sources = [
+ "base/test_task_time_observer.h",
+ "test/fake_frame_scheduler.h",
+ "test/fake_page_scheduler.h",
+ "test/fake_renderer_scheduler.cc",
+ "test/fake_task_runner.cc",
+ "test/fake_task_runner.h",
+ "test/lazy_thread_controller_for_test.cc",
+ "test/lazy_thread_controller_for_test.h",
+ "test/renderer_scheduler_test_support.cc",
+ "test/task_queue_manager_for_test.cc",
+ "test/task_queue_manager_for_test.h",
+ "test/test_task_queue.cc",
+ "test/test_task_queue.h",
+ ]
+
+ deps = [
+ "//base",
+ "//base/test:test_support",
+ ]
+
+ configs += [ "//third_party/blink/renderer/platform:blink_platform_config" ]
+}
+
+jumbo_source_set("unit_tests") {
+ testonly = true
+
+ sources = [
+ "base/intrusive_heap_unittest.cc",
+ "base/task_queue_manager_impl_unittest.cc",
+ "base/task_queue_selector_unittest.cc",
+ "base/test_count_uses_time_source.cc",
+ "base/test_count_uses_time_source.h",
+ "base/time_domain_unittest.cc",
+ "base/work_queue_sets_unittest.cc",
+ "base/work_queue_unittest.cc",
+ "child/idle_canceled_delayed_task_sweeper_unittest.cc",
+ "child/idle_helper_unittest.cc",
+ "child/metrics_helper_unittest.cc",
+ "common/scheduler_helper_unittest.cc",
+ "child/webthread_impl_for_worker_scheduler_unittest.cc",
+ "child/worker_global_scope_scheduler_unittest.cc",
+ "child/worker_scheduler_proxy_unittest.cc",
+ "common/throttling/budget_pool_unittest.cc",
+ "common/throttling/task_queue_throttler_unittest.cc",
+ "main_thread/frame_scheduler_impl_unittest.cc",
+ "main_thread/main_thread_scheduler_impl_unittest.cc",
+ "main_thread/page_scheduler_impl_unittest.cc",
+ "renderer/auto_advancing_virtual_time_domain_unittest.cc",
+ "renderer/deadline_task_runner_unittest.cc",
+ "renderer/idle_time_estimator_unittest.cc",
+ "renderer/queueing_time_estimator_unittest.cc",
+ "renderer/render_widget_signals_unittest.cc",
+ "renderer/renderer_metrics_helper_unittest.cc",
+ "renderer/task_cost_estimator_unittest.cc",
+ "renderer/user_model_unittest.cc",
+ "renderer/webthread_impl_for_renderer_scheduler_unittest.cc",
+ "util/task_duration_metric_reporter_unittest.cc",
+ "util/thread_load_tracker_unittest.cc",
+ "util/tracing_helper_unittest.cc",
+ "worker/worker_thread_scheduler_unittest.cc",
+ ]
+
+ deps = [
+ "//base",
+ "//base/test:test_support",
+ "//components/viz/common",
+ "//components/viz/test:test_support",
+ "//testing/gmock",
+ "//testing/gtest",
+ "//third_party/blink/public:test_support",
+ ]
+
+ configs += [ "//third_party/blink/renderer/platform:blink_platform_config" ]
+}
+
+source_set("perf_tests") {
+ testonly = true
+
+ sources = [
+ "base/task_queue_manager_perftest.cc",
+ ]
+
+ deps = [
+ "//base",
+ "//base/test:test_support",
+ "//testing/gmock",
+ "//testing/gtest",
+ "//testing/perf",
+ ]
+
+ configs += [ "//third_party/blink/renderer/platform:blink_platform_config" ]
+}
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/DEPS b/chromium/third_party/blink/renderer/platform/scheduler/DEPS
new file mode 100644
index 00000000000..a011b9d769a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/DEPS
@@ -0,0 +1,41 @@
+include_rules = [
+ "+base/atomic_sequence_num.h",
+ "+base/atomicops.h",
+ "+base/bind_helpers.h",
+ "+base/callback.h",
+ "+base/cancelable_callback.h",
+ "+base/command_line.h",
+ "+base/compiler_specific.h",
+ "+base/containers/small_map.h",
+ "+base/feature_list.h",
+ "+base/format_macros.h",
+ "+base/gtest_prod_util.h",
+ "+base/logging.h",
+ "+base/message_loop/message_loop.h",
+ "+base/metrics/field_trial.h",
+ "+base/metrics/field_trial_params.h",
+ "+base/metrics/histogram_functions.h",
+ "+base/metrics/histogram_macros.h",
+ "+base/observer_list.h",
+ "+base/optional.h",
+ "+base/pending_task.h",
+ "+base/run_loop.h",
+ "+base/sequence_checker.h",
+ "+base/single_thread_task_runner.h",
+ "+base/strings/string_number_conversions.h",
+ "+base/synchronization/atomic_flag.h",
+ "+base/synchronization/cancellation_flag.h",
+ "+base/synchronization/lock.h",
+ "+base/threading/platform_thread.h",
+ "+base/threading/sequenced_task_runner_handle.h",
+ "+base/threading/thread.h",
+ "+base/threading/thread_checker.h",
+ "+services/metrics",
+]
+
+specific_include_rules = {
+ ".*test\.cc": [
+ "+base/metrics/field_trial_param_associator.h",
+ "+testing",
+ ],
+}
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/OWNERS b/chromium/third_party/blink/renderer/platform/scheduler/OWNERS
new file mode 100644
index 00000000000..8423b497efb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/OWNERS
@@ -0,0 +1,7 @@
+altimin@chromium.org
+alexclarke@chromium.org
+rmcilroy@chromium.org
+skyostil@chromium.org
+
+# TEAM: scheduler-dev@chromium.org
+# COMPONENT: Blink>Scheduling
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/README.md b/chromium/third_party/blink/renderer/platform/scheduler/README.md
new file mode 100644
index 00000000000..610e26ff86b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/README.md
@@ -0,0 +1,96 @@
+# Blink Scheduler
+
+This directory contains the Blink Scheduler, which coordinates task execution
+in renderer processes. The main subdirectories are:
+
+- `base/` -- basic scheduling primitives such as `TaskQueue` and
+ `TaskQueueManager`.
+- `child/` -- contains the `ChildScheduler` which is the base class for all
+ thread schedulers, as well as a `WorkerScheduler` for worker threads.
+- `utility/` -- a small scheduler for utility processes.
+- `renderer/` -- `RendererScheduler` for the renderer process.
+
+The scheduler exposes an API at `public/platform/scheduler`.
+
+# Documentation
+
+The following is a collection of scheduling-related documentation about the
+Blink Scheduler as well as other schedulers in Chrome.
+
+
+## 2017
+* [Improved load time scheduling](https://docs.google.com/document/d/1q5uPIKyUP0X7KaQRyxWXmIzMvKF3fx1j6QPCWhjI82o/edit)
+* [Wake-up based throttling](https://docs.google.com/document/d/1A87Ci3_USDyQEdlmXTO1spQxUcR_ML5zqiCsaow4NGM/edit)
+* [Background tabs & offscreen frames](https://docs.google.com/document/d/18_sX-KGRaHcV3xe5Xk_l6NNwXoxm-23IOepgMx4OlE4/edit)
+* [BeginFrame sequence numbers + acknowledgements](https://docs.google.com/document/d/1nxaunQ0cYWxhtS6Zzfwa99nae74F7gxanbuT5JRpI6Y/edit)
+* [Background tab use cases](https://docs.google.com/document/d/16-QGneIkYNbNleoXbdD-mRMYdZAG2JIjMcTVxSC3ZWc/edit)
+* [Activity traits](https://docs.google.com/document/d/1BaJpx08vbPz_1LCj9tehnatZNqh1eLPeE9xoUnWdlW4/edit#heading=h.nwhgpfhlxswr)
+
+## 2016
+
+* [Time-based renderer task throttling](https://drive.google.com/open?id=1vCUeGfr2xzZ67SFt2yZjNeaIcXGp2Td6KHN7bI02ySo)
+* [kV8 Performance Mode](https://drive.google.com/open?id=1bRVAP08qNBvnEm_vO4hW1-NqQC9-lQZjUH29_vwfYRY)
+* [Isolating performance of third-party iframes](https://docs.google.com/document/d/1CEggurHQGXenhu_GQT7KnRvtSuowuenXpxVzYSeRxSY/edit)
+* [Folly of Scheduling (BlinkOn 6)](https://drive.google.com/open?id=1ZMxbnSn1R1o2-NGztP0mVyOOoQg24bLSqWE1SWXnQ_E)
+* [Rendering pipeline throttling (BlinkOn 6)](https://docs.google.com/presentation/d/1aPZzH7J0O29sqA_FzsuWQNDwK6CoNcAcpMvJexsO6Vg/edit)
+* [Power usage impact of render pipeline throttling](https://docs.google.com/document/d/1jMuvRYWptZfP5zpvWmPJPRL-iowtgBVX45rSvew0VH4)
+* [The future of TaskRunnerHandles](https://docs.google.com/document/d/1A_LRKyTOCzhRPOY4Q3RsePuw4UCsvxuFYx6D18BaYk0/edit#heading=h.xgjl2srtytjt)
+* [Improved policy for blocking expensive tasks](https://docs.google.com/document/d/14VdbqN-ehgpNC4KYVpPQFiQpfxOQiVtJgYjXUJGI4f0/edit#)
+* [scheduler-dev performance metrics](https://docs.google.com/document/d/15CIJ4eMnwOneshhjFxVjz3FCV7ja9lrlQOEZGWLZdgA/edit)
+* [FrameBlamer](https://docs.google.com/document/d/15BB-suCb9j-nFt55yCFJBJCGzLg2qUm3WaSOPb8APtI/edit)
+* [Virtual time in Blink](https://drive.google.com/open?id=1y9KDT_ZEzT7pBeY6uzVt1dgKlwc1OB_vY4NZO1zBQmo)
+* [Browser I/O scheduler (Lucky Luke)](https://docs.google.com/document/d/1S2AAeoo1xa_vsLbDYBsDHCqhrkfiMgoIPlyRi6kxa5k/edit)
+
+## 2015
+
+* [Virtual time in Headless Chrome](https://docs.google.com/document/d/1dIMHIl1xutUXqXWRXqXrDd3bo9hachIt_ZkPK_BshUs/edit)
+* [Task traits](https://docs.google.com/document/d/1d6t7CTobtXLj1gXiBE8SVl_fxJjEazATxYHYGp5ppvE)
+* [Compositor and Display Scheduling presentation from scheduling summit](https://docs.google.com/presentation/d/1FpTy5DpIGKt8r2t785y6yrHETkg8v7JfJ26zUxaNDUg/edit?usp=sharing)
+* [Simplifying Task Management in Chromium](https://docs.google.com/document/d/1fn0AmFsY7gWvStShOh7dUxOfHXjKgcfsr9I1ETbNe2I/edit)
+* [Outline of known work needed to fix resize](https://docs.google.com/a/chromium.org/document/d/1POLDq-L_T9iZ_Ul39sjOMiOO-yvLnmb1WFsH4JfIyVU/edit?usp=sharing_eid)
+* [Blink spatial scheduling](https://docs.google.com/document/d/1k9fL01wwRliVzZW_ibPT8-B9BADz7I87hpFoXFG37aI/edit?pli=1#)
+* [Throttling Blink's rendering pipeline for hidden content](https://docs.google.com/document/d/1Dd4qi1b_iX-OCZpelvXxizjq6dDJ76XNtk37SZEoTYQ/edit)
+* [Scheduling to avoid checkerboard in Chrome](https://docs.google.com/document/d/1OLp7x06CjBY-0J3TBQzXw8sALHznIx4rYixvnBTbUUA/edit#heading=h.9i2v5u7um22b)
+* [State of GPU scheduling](https://docs.google.com/document/d/15gbHgXPyhSlNu1Ku9HF-of8BNOpvsziV1F8IE-kRax4/edit#heading=h.jermw4ib9rwc)
+* [Scheduling architecture diagram](https://docs.google.com/drawings/d/1xcHpqhdcIsX0b_sGPuU1SZCsY87UTqJ67qvvbF29oLM/edit)
+* [Event dispatch diagram](https://docs.google.com/drawings/d/1bUukRm-DV34sM7rL2_bSdxaQkZVMQ_5vOa7nzDnmnx8/edit)
+* [Proxying MessageLoop tasks to the Scheduler](https://docs.google.com/a/chromium.org/document/d/1qxdh2I61_aB_Uzh1QgNqvdWFBCL_E65G2smoSySw7KU/edit#heading=h.vit0krths7ns)
+* [Scheduling JS timer execution](https://docs.google.com/a/chromium.org/document/d/163ow-1wjd6L0rAN3V_U6t12eqVkq4mXDDjVaA4OuvCA/edit?usp=sharing_eid)
+* [PSA: How to write reliable layout tests](https://docs.google.com/a/chromium.org/document/d/1Yl4SnTLBWmY1O99_BTtQvuoffP8YM9HZx2YPkEsaduQ/edit#heading=h.ui2te0d6ongo)
+* [Long Idle Tasks: Coupling wagons to the Blink Midnight Train](https://docs.google.com/a/chromium.org/document/d/1yBlUdYW8VTIfB-DqhvQqUeP0kf-Ap1W4cao2yQq58Do/edit?pli=1#heading=h.g9y2fheuia8t)
+* [Cooperative scheduling in Javascript](https://docs.google.com/a/chromium.org/document/d/1Jb0DRcIeHHFldlI8wkQJ4uAyTZLzNOvH161VBJUF_Oc/edit#)
+
+## 2014
+
+* [Blink Scheduler talk at BlinkOn 3](https://docs.google.com/presentation/d/1V09Qq08_jOucvOFs-C7P4Hz2Vsswa6imqLxAf7ONomQ/edit#slide=id.p)
+* [Blink Scheduler](https://docs.google.com/a/chromium.org/document/d/11N2WTV3M0IkZ-kQlKWlBcwkOkKTCuLXGVNylK5E2zvc/edit#heading=h.3ay9sj44f0zd)
+* [Blink Scheduler refactoring](https://docs.google.com/a/chromium.org/document/d/16f_RIhZa47uEK_OdtTgzWdRU0RFMTQWMpEWyWXIpXUo/edit#) (moving from Blink to content)
+* [Idle Tasks in the Blink Scheduler](https://docs.google.com/a/chromium.org/document/d/1bXcZ45iCr9NPP6UDbY57RCKgSndgaBt9tSgwxV0sg1o/edit)
+* [Resource loading tasks and the Blink Scheduler](https://docs.google.com/a/chromium.org/document/d/1kLdtb718AEetE64gL-MmM0YRh7kAkxPpWDRa7OI-scI/edit?usp=sharing_eid)
+* [Blink Scheduler friendly HTMLDocumentParser](https://docs.google.com/a/chromium.org/document/d/1Ofil50mhU9IuDkmEdbde18uxquA3WsEdI3vCyYzzDyc/edit#heading=h.fr9ldspsaw6g)
+* [Trustable Future Sync Points](https://docs.google.com/document/d/1qqu8c5Gp1faY-AY4CgER-GKs0w7GXlR5YJ-BaIZ4auo/edit?usp=sharing)
+* [Unified VSync Scheduling](https://docs.google.com/document/d/13xtO-_NSSnNZRRS1Xq3xGNKZawKc8HQxOid5boBUyX8/edit?usp=sharing)
+* [VSync-Aligned Buffered Input](https://docs.google.com/document/d/1L2JTgYMksmXgujKxxhyV45xL8jNhbCh60NQHoueKyS4/edit?usp=sharing)
+* [London Perf Summit - Chrome Scheduling](https://docs.google.com/presentation/d/1I105Uk7nlH_Kj4UaqC7Ygkw3eNuDINQXPtYYSusW8Ho/edit?usp=sharing)
+* [GPU Service Scheduling Latency](https://docs.google.com/document/d/1hjVckIpb9WBE7A9HUxAmutRJEgSkZO_JAFfgwOE-8NE/edit?usp=sharing)
+* Related
+ * [Sync Point Shader Arguments](https://docs.google.com/document/d/1GlnjZI0jDNPXZIlhdcU135BGnsP7T3WY0UR4IwjEILU/edit?usp=sharing)
+ * [Asynchronous Shader Input from the CPU](https://docs.google.com/document/d/1daXOSiYUHvDcG5dR9OUQWx6rllW127mRcxiVUTK9DdM/edit?usp=sharing)
+ * [T-Sync Display Interface](https://docs.google.com/document/d/1ZoH6a-Pxsnh9Xu_2rtF5jss2d352Klpu_23urKghaH0/edit?usp=sharing)
+ * [Surfaces](https://docs.google.com/a/chromium.org/document/d/1RxbffpK_GxPtZscXgIEN0N9ZT7IC8BObnbx9ynw92qg/edit?pli=1)
+ * [Better Video Frame Scheduling](https://docs.google.com/a/chromium.org/document/d/1xauQd5Tt2MuM82MAwIqIW7IEkVj4VnjWBB-zUODfERQ)
+
+## 2013
+
+* [Synthetic Scheduler Tests](https://docs.google.com/a/chromium.org/document/d/17yhE5Po9By0sCdM1yZT3LiUECaUr_94rQt9j-4tOQIM)
+* [Chrome Frame Synchronization](https://docs.google.com/presentation/d/1q2WU0LusCyQFKDMjOSWLj3xGeOxMWmLzConrC8euJpA/edit?usp=sharing)
+* [Chrome Scheduling Overhaul Phase 1](https://docs.google.com/document/d/1LUFA8MDpJcDHE0_L2EHvrcwqOMJhzl5dqb0AlBSqHOY/edit?usp=sharing)
+* [Chrome Scheduling Overhaul Phase 2](https://docs.google.com/document/d/1VJf2busac85FRQYXhn8hdc-x4yp77JUroTrY-_sj5Ck/edit?usp=sharing)
+* [Improved vsync scheduling for Chrome on Android](https://docs.google.com/a/chromium.org/document/d/16822du6DLKDZ1vQVNWI3gDVYoSqCSezgEmWZ0arvkP8/edit)
+* [ZilCh](https://docs.google.com/document/d/1HmS0YQtWg2ToY67fE8A33PJUyPSwGUwUCLMk_zjK7ik/edit?usp=sharing)
+
+## Miscellaneous
+
+* [Rendering for "Glass Time"](https://docs.google.com/a/google.com/presentation/d/1oKEunkaeiTwznGaIX_yIhe6HPfZBXhtv8r5J5hz52UI/edit#slide=id.g2b8380fec_0129)
+* [Motion Vectors: An interface between Render and Input Systems](http://www.google.com/url?q=http%3A%2F%2Fgo%2Finput-motion-vectors&sa=D&sntz=1&usg=AFQjCNF0sC31c9FLCscR8HtXiz_kP5EaPw)
+* [Begin Frame / VSync Design Diagram](https://docs.google.com/a/chromium.org/drawings/d/1WEj-6A-8FmJNIMbd9hvkvxAuOOTwQvkSjbKR79YCt-c/edit)
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/DEPS b/chromium/third_party/blink/renderer/platform/scheduler/base/DEPS
new file mode 100644
index 00000000000..53701134c2e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/DEPS
@@ -0,0 +1,5 @@
+specific_include_rules = {
+ ".*test\.cc": [
+ "+components/viz/test",
+ ],
+}
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/enqueue_order.cc b/chromium/third_party/blink/renderer/platform/scheduler/base/enqueue_order.cc
new file mode 100644
index 00000000000..5d262f4956a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/enqueue_order.cc
@@ -0,0 +1,25 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/base/enqueue_order.h"
+
+namespace blink {
+namespace scheduler {
+namespace internal {
+
+// Note we set the first |enqueue_order_| to a specific non-zero value, because
+// first N values of EnqueueOrder have special meaning (see EnqueueOrderValues).
+EnqueueOrderGenerator::EnqueueOrderGenerator()
+ : enqueue_order_(static_cast<EnqueueOrder>(EnqueueOrderValues::kFirst)) {}
+
+EnqueueOrderGenerator::~EnqueueOrderGenerator() = default;
+
+EnqueueOrder EnqueueOrderGenerator::GenerateNext() {
+ base::AutoLock lock(lock_);
+ return enqueue_order_++;
+}
+
+} // namespace internal
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/enqueue_order.h b/chromium/third_party/blink/renderer/platform/scheduler/base/enqueue_order.h
new file mode 100644
index 00000000000..9caea897ade
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/enqueue_order.h
@@ -0,0 +1,52 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_ENQUEUE_ORDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_ENQUEUE_ORDER_H_
+
+#include <stdint.h>
+
+#include "base/synchronization/lock.h"
+
+namespace blink {
+namespace scheduler {
+namespace internal {
+
+using EnqueueOrder = uint64_t;
+
+// TODO(scheduler-dev): Remove explicit casts when c++17 comes.
+enum class EnqueueOrderValues : EnqueueOrder {
+ // Invalid EnqueueOrder.
+ kNone = 0,
+
+ // Earliest possible EnqueueOrder, to be used for fence blocking.
+ kBlockingFence = 1,
+ kFirst = 2,
+};
+
+// A 64bit integer used to provide ordering of tasks. NOTE The scheduler assumes
+// these values will not overflow.
+class EnqueueOrderGenerator {
+ public:
+ EnqueueOrderGenerator();
+ ~EnqueueOrderGenerator();
+
+ // Returns a monotonically increasing integer, starting from one. Can be
+ // called from any thread.
+ EnqueueOrder GenerateNext();
+
+ static bool IsValidEnqueueOrder(EnqueueOrder enqueue_order) {
+ return enqueue_order != 0ull;
+ }
+
+ private:
+ base::Lock lock_;
+ EnqueueOrder enqueue_order_;
+};
+
+} // namespace internal
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_ENQUEUE_ORDER_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/graceful_queue_shutdown_helper.cc b/chromium/third_party/blink/renderer/platform/scheduler/base/graceful_queue_shutdown_helper.cc
new file mode 100644
index 00000000000..c2fc97aafdf
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/graceful_queue_shutdown_helper.cc
@@ -0,0 +1,40 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl.h"
+
+namespace blink {
+namespace scheduler {
+namespace internal {
+
+GracefulQueueShutdownHelper::GracefulQueueShutdownHelper()
+ : task_queue_manager_deleted_(false) {}
+
+GracefulQueueShutdownHelper::~GracefulQueueShutdownHelper() = default;
+
+void GracefulQueueShutdownHelper::GracefullyShutdownTaskQueue(
+ std::unique_ptr<internal::TaskQueueImpl> task_queue) {
+ base::AutoLock lock(lock_);
+ if (task_queue_manager_deleted_)
+ return;
+ queues_.push_back(std::move(task_queue));
+}
+
+void GracefulQueueShutdownHelper::OnTaskQueueManagerDeleted() {
+ base::AutoLock lock(lock_);
+ task_queue_manager_deleted_ = true;
+ queues_.clear();
+}
+
+std::vector<std::unique_ptr<internal::TaskQueueImpl>>
+GracefulQueueShutdownHelper::TakeQueues() {
+ base::AutoLock lock(lock_);
+ std::vector<std::unique_ptr<internal::TaskQueueImpl>> result;
+ result.swap(queues_);
+ return result;
+}
+
+} // namespace internal
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/graceful_queue_shutdown_helper.h b/chromium/third_party/blink/renderer/platform/scheduler/base/graceful_queue_shutdown_helper.h
new file mode 100644
index 00000000000..976feb1ae5d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/graceful_queue_shutdown_helper.h
@@ -0,0 +1,47 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_GRACEFUL_QUEUE_SHUTDOWN_HELPER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_GRACEFUL_QUEUE_SHUTDOWN_HELPER_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+
+namespace blink {
+namespace scheduler {
+namespace internal {
+
+class TaskQueueImpl;
+
+// Thread-safe helper to shutdown queues from any thread.
+class GracefulQueueShutdownHelper
+ : public base::RefCountedThreadSafe<GracefulQueueShutdownHelper> {
+ public:
+ GracefulQueueShutdownHelper();
+ ~GracefulQueueShutdownHelper();
+
+ void GracefullyShutdownTaskQueue(
+ std::unique_ptr<internal::TaskQueueImpl> queue);
+
+ void OnTaskQueueManagerDeleted();
+
+ std::vector<std::unique_ptr<internal::TaskQueueImpl>> TakeQueues();
+
+ private:
+ base::Lock lock_;
+ bool task_queue_manager_deleted_;
+ std::vector<std::unique_ptr<internal::TaskQueueImpl>> queues_;
+
+ DISALLOW_COPY_AND_ASSIGN(GracefulQueueShutdownHelper);
+};
+
+} // namespace internal
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_GRACEFUL_QUEUE_SHUTDOWN_HELPER_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/intrusive_heap.h b/chromium/third_party/blink/renderer/platform/scheduler/base/intrusive_heap.h
new file mode 100644
index 00000000000..5d5acabdcc5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/intrusive_heap.h
@@ -0,0 +1,227 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_INTRUSIVE_HEAP_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_INTRUSIVE_HEAP_H_
+
+#include <algorithm>
+#include <vector>
+
+#include "base/logging.h"
+
+namespace blink {
+namespace scheduler {
+
+template <typename T>
+class IntrusiveHeap;
+
+// Intended as an opaque wrapper around |index_|.
+class HeapHandle {
+ public:
+ HeapHandle() : index_(0u) {}
+
+ bool IsValid() const { return index_ != 0u; }
+
+ private:
+ template <typename T>
+ friend class IntrusiveHeap;
+
+ HeapHandle(size_t index) : index_(index) {}
+
+ size_t index_;
+};
+
+// A standard min-heap with the following assumptions:
+// 1. T has operator <=
+// 2. T has method void SetHeapHandle(HeapHandle handle)
+// 3. T has method void ClearHeapHandle()
+// 4. T is moveable
+// 5. T is default constructible
+// 6. The heap size never gets terribly big so reclaiming memory on pop/erase
+// isn't a priority.
+//
+// The reason IntrusiveHeap exists is to provide similar performance to
+// std::priority_queue while allowing removal of arbitrary elements.
+template <typename T>
+class IntrusiveHeap {
+ public:
+ IntrusiveHeap() : nodes_(kMinimumHeapSize), size_(0) {}
+
+ ~IntrusiveHeap() {
+ for (size_t i = 1; i <= size_; i++) {
+ MakeHole(i);
+ }
+ }
+
+ bool empty() const { return size_ == 0; }
+
+ size_t size() const { return size_; }
+
+ void Clear() {
+ for (size_t i = 1; i <= size_; i++) {
+ MakeHole(i);
+ }
+ nodes_.resize(kMinimumHeapSize);
+ size_ = 0;
+ }
+
+ const T& Min() const {
+ DCHECK_GE(size_, 1u);
+ return nodes_[1];
+ }
+
+ void Pop() {
+ DCHECK_GE(size_, 1u);
+ MakeHole(1u);
+ size_t top_index = size_--;
+ if (!empty())
+ MoveHoleDownAndFillWithLeafElement(1u, std::move(nodes_[top_index]));
+ }
+
+ void insert(T&& element) {
+ size_++;
+ if (size_ >= nodes_.size())
+ nodes_.resize(nodes_.size() * 2);
+ // Notionally we have a hole in the tree at index |size_|, move this up
+ // to find the right insertion point.
+ MoveHoleUpAndFillWithElement(size_, std::move(element));
+ }
+
+ void erase(HeapHandle handle) {
+ DCHECK_GT(handle.index_, 0u);
+ DCHECK_LE(handle.index_, size_);
+ MakeHole(handle.index_);
+ size_t top_index = size_--;
+ if (empty() || top_index == handle.index_)
+ return;
+ if (nodes_[handle.index_] <= nodes_[top_index]) {
+ MoveHoleDownAndFillWithLeafElement(handle.index_,
+ std::move(nodes_[top_index]));
+ } else {
+ MoveHoleUpAndFillWithElement(handle.index_, std::move(nodes_[top_index]));
+ }
+ }
+
+ void ReplaceMin(T&& element) {
+ // Note |element| might not be a leaf node so we can't use
+ // MoveHoleDownAndFillWithLeafElement.
+ MoveHoleDownAndFillWithElement(1u, std::move(element));
+ }
+
+ void ChangeKey(HeapHandle handle, T&& element) {
+ if (nodes_[handle.index_] <= element) {
+ MoveHoleDownAndFillWithLeafElement(handle.index_, std::move(element));
+ } else {
+ MoveHoleUpAndFillWithElement(handle.index_, std::move(element));
+ }
+ }
+
+ // Caution mutating the heap invalidates the iterators.
+ const T* begin() const { return &nodes_[1u]; }
+ const T* end() const { return begin() + size_; }
+
+ private:
+ enum {
+ // The majority of sets in the scheduler have 0-3 items in them (a few will
+ // have perhaps up to 100), so this means we usually only have to allocate
+ // memory once.
+ kMinimumHeapSize = 4u
+ };
+
+ friend class IntrusiveHeapTest;
+
+ size_t MoveHole(size_t new_hole_pos, size_t old_hole_pos) {
+ DCHECK_GT(new_hole_pos, 0u);
+ DCHECK_LE(new_hole_pos, size_);
+ DCHECK_GT(new_hole_pos, 0u);
+ DCHECK_LE(new_hole_pos, size_);
+ DCHECK_NE(old_hole_pos, new_hole_pos);
+ nodes_[old_hole_pos] = std::move(nodes_[new_hole_pos]);
+ nodes_[old_hole_pos].SetHeapHandle(HeapHandle(old_hole_pos));
+ return new_hole_pos;
+ }
+
+ // Notionally creates a hole in the tree at |index|.
+ void MakeHole(size_t index) {
+ DCHECK_GT(index, 0u);
+ DCHECK_LE(index, size_);
+ nodes_[index].ClearHeapHandle();
+ }
+
+ void FillHole(size_t hole, T&& element) {
+ DCHECK_GT(hole, 0u);
+ DCHECK_LE(hole, size_);
+ nodes_[hole] = std::move(element);
+ nodes_[hole].SetHeapHandle(HeapHandle(hole));
+ DCHECK(std::is_heap(begin(), end(), CompareNodes));
+ }
+
+ // is_heap requires a strict comparator.
+ static bool CompareNodes(const T& a, const T& b) { return !(a <= b); }
+
+ // Moves the |hole| up the tree and when the right position has been found
+ // |element| is moved in.
+ void MoveHoleUpAndFillWithElement(size_t hole, T&& element) {
+ DCHECK_GT(hole, 0u);
+ DCHECK_LE(hole, size_);
+ while (hole >= 2u) {
+ size_t parent_pos = hole / 2;
+ if (nodes_[parent_pos] <= element)
+ break;
+
+ hole = MoveHole(parent_pos, hole);
+ }
+ FillHole(hole, std::move(element));
+ }
+
+ // Moves the |hole| down the tree and when the right position has been found
+ // |element| is moved in.
+ void MoveHoleDownAndFillWithElement(size_t hole, T&& element) {
+ DCHECK_GT(hole, 0u);
+ DCHECK_LE(hole, size_);
+ size_t child_pos = hole * 2;
+ while (child_pos < size_) {
+ if (nodes_[child_pos + 1] <= nodes_[child_pos])
+ child_pos++;
+
+ if (element <= nodes_[child_pos])
+ break;
+
+ hole = MoveHole(child_pos, hole);
+ child_pos *= 2;
+ }
+ if (child_pos == size_ && !(element <= nodes_[child_pos]))
+ hole = MoveHole(child_pos, hole);
+ FillHole(hole, std::move(element));
+ }
+
+ // Moves the |hole| down the tree and when the right position has been found
+ // |leaf_element| is moved in. Faster than MoveHoleDownAndFillWithElement
+ // (it does one key comparison per level instead of two) but only valid for
+ // leaf elements (i.e. one of the max values).
+ void MoveHoleDownAndFillWithLeafElement(size_t hole, T&& leaf_element) {
+ DCHECK_GT(hole, 0u);
+ DCHECK_LE(hole, size_);
+ size_t child_pos = hole * 2;
+ while (child_pos < size_) {
+ size_t second_child = child_pos + 1;
+ if (nodes_[second_child] <= nodes_[child_pos])
+ child_pos = second_child;
+
+ hole = MoveHole(child_pos, hole);
+ child_pos *= 2;
+ }
+ if (child_pos == size_)
+ hole = MoveHole(child_pos, hole);
+ MoveHoleUpAndFillWithElement(hole, std::move(leaf_element));
+ }
+
+ std::vector<T> nodes_; // NOTE we use 1-based indexing
+ size_t size_;
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_INTRUSIVE_HEAP_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/intrusive_heap_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/base/intrusive_heap_unittest.cc
new file mode 100644
index 00000000000..d64250ffaf7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/intrusive_heap_unittest.cc
@@ -0,0 +1,374 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/base/intrusive_heap.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+namespace scheduler {
+namespace {
+
+struct TestElement {
+ int key;
+ HeapHandle* handle;
+
+ bool operator<=(const TestElement& other) const { return key <= other.key; }
+
+ void SetHeapHandle(HeapHandle h) {
+ if (handle)
+ *handle = h;
+ }
+
+ void ClearHeapHandle() {
+ if (handle)
+ *handle = HeapHandle();
+ }
+};
+
+} // namespace
+
+class IntrusiveHeapTest : public testing::Test {
+ protected:
+ static bool CompareNodes(const TestElement& a, const TestElement& b) {
+ return IntrusiveHeap<TestElement>::CompareNodes(a, b);
+ }
+};
+
+TEST_F(IntrusiveHeapTest, Basic) {
+ IntrusiveHeap<TestElement> heap;
+
+ EXPECT_TRUE(heap.empty());
+ EXPECT_EQ(0u, heap.size());
+}
+
+TEST_F(IntrusiveHeapTest, Clear) {
+ IntrusiveHeap<TestElement> heap;
+ HeapHandle index1;
+
+ heap.insert({11, &index1});
+ EXPECT_EQ(1u, heap.size());
+ EXPECT_TRUE(index1.IsValid());
+
+ heap.Clear();
+ EXPECT_EQ(0u, heap.size());
+ EXPECT_FALSE(index1.IsValid());
+}
+
+TEST_F(IntrusiveHeapTest, Destructor) {
+ HeapHandle index1;
+
+ {
+ IntrusiveHeap<TestElement> heap;
+
+ heap.insert({11, &index1});
+ EXPECT_EQ(1u, heap.size());
+ EXPECT_TRUE(index1.IsValid());
+ }
+
+ EXPECT_FALSE(index1.IsValid());
+}
+
+TEST_F(IntrusiveHeapTest, Min) {
+ IntrusiveHeap<TestElement> heap;
+
+ heap.insert({9, nullptr});
+ heap.insert({10, nullptr});
+ heap.insert({8, nullptr});
+ heap.insert({2, nullptr});
+ heap.insert({7, nullptr});
+ heap.insert({15, nullptr});
+ heap.insert({22, nullptr});
+ heap.insert({3, nullptr});
+
+ EXPECT_FALSE(heap.empty());
+ EXPECT_EQ(8u, heap.size());
+ EXPECT_EQ(2, heap.Min().key);
+}
+
+TEST_F(IntrusiveHeapTest, InsertAscending) {
+ IntrusiveHeap<TestElement> heap;
+ HeapHandle index1;
+
+ for (int i = 0; i < 50; i++)
+ heap.insert({i, nullptr});
+
+ EXPECT_EQ(0, heap.Min().key);
+ EXPECT_EQ(50u, heap.size());
+}
+
+TEST_F(IntrusiveHeapTest, InsertDescending) {
+ IntrusiveHeap<TestElement> heap;
+
+ for (int i = 0; i < 50; i++)
+ heap.insert({50 - i, nullptr});
+
+ EXPECT_EQ(1, heap.Min().key);
+ EXPECT_EQ(50u, heap.size());
+}
+
+TEST_F(IntrusiveHeapTest, HeapIndex) {
+ HeapHandle index5;
+ HeapHandle index4;
+ HeapHandle index3;
+ HeapHandle index2;
+ HeapHandle index1;
+ IntrusiveHeap<TestElement> heap;
+
+ EXPECT_FALSE(index1.IsValid());
+ EXPECT_FALSE(index2.IsValid());
+ EXPECT_FALSE(index3.IsValid());
+ EXPECT_FALSE(index4.IsValid());
+ EXPECT_FALSE(index5.IsValid());
+
+ heap.insert({15, &index5});
+ heap.insert({14, &index4});
+ heap.insert({13, &index3});
+ heap.insert({12, &index2});
+ heap.insert({11, &index1});
+
+ EXPECT_TRUE(index1.IsValid());
+ EXPECT_TRUE(index2.IsValid());
+ EXPECT_TRUE(index3.IsValid());
+ EXPECT_TRUE(index4.IsValid());
+ EXPECT_TRUE(index5.IsValid());
+
+ EXPECT_FALSE(heap.empty());
+}
+
+TEST_F(IntrusiveHeapTest, Pop) {
+ IntrusiveHeap<TestElement> heap;
+ HeapHandle index1;
+ HeapHandle index2;
+
+ heap.insert({11, &index1});
+ heap.insert({12, &index2});
+ EXPECT_EQ(2u, heap.size());
+ EXPECT_TRUE(index1.IsValid());
+ EXPECT_TRUE(index2.IsValid());
+
+ heap.Pop();
+ EXPECT_EQ(1u, heap.size());
+ EXPECT_FALSE(index1.IsValid());
+ EXPECT_TRUE(index2.IsValid());
+
+ heap.Pop();
+ EXPECT_EQ(0u, heap.size());
+ EXPECT_FALSE(index1.IsValid());
+ EXPECT_FALSE(index2.IsValid());
+}
+
+TEST_F(IntrusiveHeapTest, PopMany) {
+ IntrusiveHeap<TestElement> heap;
+
+ for (int i = 0; i < 500; i++)
+ heap.insert({i, nullptr});
+
+ EXPECT_FALSE(heap.empty());
+ EXPECT_EQ(500u, heap.size());
+ for (int i = 0; i < 500; i++) {
+ EXPECT_EQ(i, heap.Min().key);
+ heap.Pop();
+ }
+ EXPECT_TRUE(heap.empty());
+}
+
+TEST_F(IntrusiveHeapTest, Erase) {
+ IntrusiveHeap<TestElement> heap;
+
+ HeapHandle index12;
+
+ heap.insert({15, nullptr});
+ heap.insert({14, nullptr});
+ heap.insert({13, nullptr});
+ heap.insert({12, &index12});
+ heap.insert({11, nullptr});
+
+ EXPECT_EQ(5u, heap.size());
+ EXPECT_TRUE(index12.IsValid());
+ heap.erase(index12);
+ EXPECT_EQ(4u, heap.size());
+ EXPECT_FALSE(index12.IsValid());
+
+ EXPECT_EQ(11, heap.Min().key);
+ heap.Pop();
+ EXPECT_EQ(13, heap.Min().key);
+ heap.Pop();
+ EXPECT_EQ(14, heap.Min().key);
+ heap.Pop();
+ EXPECT_EQ(15, heap.Min().key);
+ heap.Pop();
+ EXPECT_TRUE(heap.empty());
+}
+
+TEST_F(IntrusiveHeapTest, ReplaceMin) {
+ IntrusiveHeap<TestElement> heap;
+
+ for (int i = 0; i < 500; i++)
+ heap.insert({500 - i, nullptr});
+
+ EXPECT_EQ(1, heap.Min().key);
+
+ for (int i = 0; i < 500; i++)
+ heap.ReplaceMin({1000 + i, nullptr});
+
+ EXPECT_EQ(1000, heap.Min().key);
+}
+
+TEST_F(IntrusiveHeapTest, ReplaceMinWithNonLeafNode) {
+ IntrusiveHeap<TestElement> heap;
+
+ for (int i = 0; i < 50; i++) {
+ heap.insert({i, nullptr});
+ heap.insert({200 + i, nullptr});
+ }
+
+ EXPECT_EQ(0, heap.Min().key);
+
+ for (int i = 0; i < 50; i++)
+ heap.ReplaceMin({100 + i, nullptr});
+
+ for (int i = 0; i < 50; i++) {
+ EXPECT_EQ((100 + i), heap.Min().key);
+ heap.Pop();
+ }
+ for (int i = 0; i < 50; i++) {
+ EXPECT_EQ((200 + i), heap.Min().key);
+ heap.Pop();
+ }
+ EXPECT_TRUE(heap.empty());
+}
+
+TEST_F(IntrusiveHeapTest, ReplaceMinCheckAllFinalPositions) {
+ HeapHandle index[100];
+
+ for (int j = -1; j <= 201; j += 2) {
+ IntrusiveHeap<TestElement> heap;
+ for (size_t i = 0; i < 100; i++) {
+ heap.insert({static_cast<int>(i) * 2, &index[i]});
+ }
+
+ heap.ReplaceMin({j, &index[40]});
+
+ int prev = -2;
+ while (!heap.empty()) {
+ DCHECK_GT(heap.Min().key, prev);
+ DCHECK(heap.Min().key == j || (heap.Min().key % 2) == 0);
+ DCHECK_NE(heap.Min().key, 0);
+ prev = heap.Min().key;
+ heap.Pop();
+ }
+ }
+}
+
+TEST_F(IntrusiveHeapTest, ChangeKeyUp) {
+ IntrusiveHeap<TestElement> heap;
+ HeapHandle index[10];
+
+ for (size_t i = 0; i < 10; i++) {
+ heap.insert({static_cast<int>(i) * 2, &index[i]});
+ }
+
+ heap.ChangeKey(index[5], {17, &index[5]});
+
+ std::vector<int> results;
+ while (!heap.empty()) {
+ results.push_back(heap.Min().key);
+ heap.Pop();
+ }
+
+ EXPECT_THAT(results, testing::ElementsAre(0, 2, 4, 6, 8, 12, 14, 16, 17, 18));
+}
+
+TEST_F(IntrusiveHeapTest, ChangeKeyUpButDoesntMove) {
+ IntrusiveHeap<TestElement> heap;
+ HeapHandle index[10];
+
+ for (size_t i = 0; i < 10; i++) {
+ heap.insert({static_cast<int>(i) * 2, &index[i]});
+ }
+
+ heap.ChangeKey(index[5], {11, &index[5]});
+
+ std::vector<int> results;
+ while (!heap.empty()) {
+ results.push_back(heap.Min().key);
+ heap.Pop();
+ }
+
+ EXPECT_THAT(results, testing::ElementsAre(0, 2, 4, 6, 8, 11, 12, 14, 16, 18));
+}
+
+TEST_F(IntrusiveHeapTest, ChangeKeyDown) {
+ IntrusiveHeap<TestElement> heap;
+ HeapHandle index[10];
+
+ for (size_t i = 0; i < 10; i++) {
+ heap.insert({static_cast<int>(i) * 2, &index[i]});
+ }
+
+ heap.ChangeKey(index[5], {1, &index[5]});
+
+ std::vector<int> results;
+ while (!heap.empty()) {
+ results.push_back(heap.Min().key);
+ heap.Pop();
+ }
+
+ EXPECT_THAT(results, testing::ElementsAre(0, 1, 2, 4, 6, 8, 12, 14, 16, 18));
+}
+
+TEST_F(IntrusiveHeapTest, ChangeKeyDownButDoesntMove) {
+ IntrusiveHeap<TestElement> heap;
+ HeapHandle index[10];
+
+ for (size_t i = 0; i < 10; i++) {
+ heap.insert({static_cast<int>(i) * 2, &index[i]});
+ }
+
+ heap.ChangeKey(index[5], {9, &index[5]});
+
+ std::vector<int> results;
+ while (!heap.empty()) {
+ results.push_back(heap.Min().key);
+ heap.Pop();
+ }
+
+ EXPECT_THAT(results, testing::ElementsAre(0, 2, 4, 6, 8, 9, 12, 14, 16, 18));
+}
+
+TEST_F(IntrusiveHeapTest, ChangeKeyCheckAllFinalPositions) {
+ HeapHandle index[100];
+
+ for (int j = -1; j <= 201; j += 2) {
+ IntrusiveHeap<TestElement> heap;
+ for (size_t i = 0; i < 100; i++) {
+ heap.insert({static_cast<int>(i) * 2, &index[i]});
+ }
+
+ heap.ChangeKey(index[40], {j, &index[40]});
+
+ int prev = -2;
+ while (!heap.empty()) {
+ DCHECK_GT(heap.Min().key, prev);
+ DCHECK(heap.Min().key == j || (heap.Min().key % 2) == 0);
+ DCHECK_NE(heap.Min().key, 80);
+ prev = heap.Min().key;
+ heap.Pop();
+ }
+ }
+}
+
+TEST_F(IntrusiveHeapTest, CompareNodes) {
+ TestElement five{5, nullptr}, six{6, nullptr};
+
+ // This is the stdlibc++ assertion that fails in http://crbug.com/661080
+ EXPECT_FALSE(IntrusiveHeapTest::CompareNodes(six, six));
+
+ EXPECT_FALSE(IntrusiveHeapTest::CompareNodes(five, six));
+ EXPECT_TRUE(IntrusiveHeapTest::CompareNodes(six, five));
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/lazy_now.cc b/chromium/third_party/blink/renderer/platform/scheduler/base/lazy_now.cc
new file mode 100644
index 00000000000..4ac96989417
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/lazy_now.cc
@@ -0,0 +1,18 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/base/lazy_now.h"
+
+#include "base/time/tick_clock.h"
+
+namespace blink {
+namespace scheduler {
+base::TimeTicks LazyNow::Now() {
+ if (!now_)
+ now_ = tick_clock_->NowTicks();
+ return now_.value();
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/lazy_now.h b/chromium/third_party/blink/renderer/platform/scheduler/base/lazy_now.h
new file mode 100644
index 00000000000..c841f20c78a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/lazy_now.h
@@ -0,0 +1,40 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_LAZY_NOW_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_LAZY_NOW_H_
+
+#include "base/optional.h"
+#include "base/time/time.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace base {
+class TickClock;
+}
+
+namespace blink {
+namespace scheduler {
+
+// Now() is somewhat expensive so it makes sense not to call Now() unless we
+// really need to.
+class PLATFORM_EXPORT LazyNow {
+ public:
+ explicit LazyNow(base::TimeTicks now) : tick_clock_(nullptr), now_(now) {
+ }
+
+ explicit LazyNow(const base::TickClock* tick_clock)
+ : tick_clock_(tick_clock) {}
+
+ // Result will not be updated on any subsesequent calls.
+ base::TimeTicks Now();
+
+ private:
+ const base::TickClock* tick_clock_; // NOT OWNED
+ base::Optional<base::TimeTicks> now_;
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_LAZY_NOW_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/moveable_auto_lock.h b/chromium/third_party/blink/renderer/platform/scheduler/base/moveable_auto_lock.h
new file mode 100644
index 00000000000..60412fbcf25
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/moveable_auto_lock.h
@@ -0,0 +1,41 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_MOVEABLE_AUTO_LOCK_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_MOVEABLE_AUTO_LOCK_H_
+
+#include "base/synchronization/lock.h"
+
+namespace blink {
+namespace scheduler {
+
+class MoveableAutoLock {
+ public:
+ explicit MoveableAutoLock(base::Lock& lock) : lock_(lock), moved_(false) {
+ lock_.Acquire();
+ }
+
+ MoveableAutoLock(MoveableAutoLock&& other)
+ : lock_(other.lock_), moved_(other.moved_) {
+ lock_.AssertAcquired();
+ other.moved_ = true;
+ }
+
+ ~MoveableAutoLock() {
+ if (moved_)
+ return;
+ lock_.AssertAcquired();
+ lock_.Release();
+ }
+
+ private:
+ base::Lock& lock_;
+ bool moved_;
+ DISALLOW_COPY_AND_ASSIGN(MoveableAutoLock);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_MOVEABLE_AUTO_LOCK_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/real_time_domain.cc b/chromium/third_party/blink/renderer/platform/scheduler/base/real_time_domain.cc
new file mode 100644
index 00000000000..ccdce6fabf1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/real_time_domain.cc
@@ -0,0 +1,70 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/base/real_time_domain.h"
+
+#include "base/bind.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_manager_impl.h"
+
+namespace blink {
+namespace scheduler {
+
+RealTimeDomain::RealTimeDomain() : task_queue_manager_(nullptr) {}
+
+RealTimeDomain::~RealTimeDomain() = default;
+
+void RealTimeDomain::OnRegisterWithTaskQueueManager(
+ TaskQueueManagerImpl* task_queue_manager) {
+ task_queue_manager_ = task_queue_manager;
+ DCHECK(task_queue_manager_);
+}
+
+LazyNow RealTimeDomain::CreateLazyNow() const {
+ return task_queue_manager_->CreateLazyNow();
+}
+
+base::TimeTicks RealTimeDomain::Now() const {
+ return task_queue_manager_->NowTicks();
+}
+
+void RealTimeDomain::RequestWakeUpAt(base::TimeTicks now,
+ base::TimeTicks run_time) {
+ // NOTE this is only called if the scheduled runtime is sooner than any
+ // previously scheduled runtime, or there is no (outstanding) previously
+ // scheduled runtime.
+ task_queue_manager_->MaybeScheduleDelayedWork(FROM_HERE, this, now, run_time);
+}
+
+void RealTimeDomain::CancelWakeUpAt(base::TimeTicks run_time) {
+ task_queue_manager_->CancelDelayedWork(this, run_time);
+}
+
+base::Optional<base::TimeDelta> RealTimeDomain::DelayTillNextTask(
+ LazyNow* lazy_now) {
+ base::TimeTicks next_run_time;
+ if (!NextScheduledRunTime(&next_run_time))
+ return base::nullopt;
+
+ base::TimeTicks now = lazy_now->Now();
+ if (now >= next_run_time)
+ return base::TimeDelta(); // Makes DoWork post an immediate continuation.
+
+ base::TimeDelta delay = next_run_time - now;
+ TRACE_EVENT1("renderer.scheduler", "RealTimeDomain::DelayTillNextTask",
+ "delay_ms", delay.InMillisecondsF());
+
+ // The next task is sometime in the future. DoWork will make sure it gets
+ // run at the right time.
+ return delay;
+}
+
+void RealTimeDomain::AsValueIntoInternal(
+ base::trace_event::TracedValue* state) const {}
+
+const char* RealTimeDomain::GetName() const {
+ return "RealTimeDomain";
+}
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/real_time_domain.h b/chromium/third_party/blink/renderer/platform/scheduler/base/real_time_domain.h
new file mode 100644
index 00000000000..57e079c2d71
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/real_time_domain.h
@@ -0,0 +1,45 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_REAL_TIME_DOMAIN_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_REAL_TIME_DOMAIN_H_
+
+#include <set>
+
+#include "base/macros.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/scheduler/base/time_domain.h"
+
+namespace blink {
+namespace scheduler {
+
+class PLATFORM_EXPORT RealTimeDomain : public TimeDomain {
+ public:
+ RealTimeDomain();
+ ~RealTimeDomain() override;
+
+ // TimeDomain implementation:
+ LazyNow CreateLazyNow() const override;
+ base::TimeTicks Now() const override;
+ base::Optional<base::TimeDelta> DelayTillNextTask(LazyNow* lazy_now) override;
+ const char* GetName() const override;
+
+ protected:
+ void OnRegisterWithTaskQueueManager(
+ TaskQueueManagerImpl* task_queue_manager) override;
+ void RequestWakeUpAt(base::TimeTicks now, base::TimeTicks run_time) override;
+ void CancelWakeUpAt(base::TimeTicks run_time) override;
+ void AsValueIntoInternal(
+ base::trace_event::TracedValue* state) const override;
+
+ private:
+ TaskQueueManagerImpl* task_queue_manager_; // NOT OWNED
+
+ DISALLOW_COPY_AND_ASSIGN(RealTimeDomain);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_REAL_TIME_DOMAIN_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/sequenced_task_source.h b/chromium/third_party/blink/renderer/platform/scheduler/base/sequenced_task_source.h
new file mode 100644
index 00000000000..323e0868988
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/sequenced_task_source.h
@@ -0,0 +1,40 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_SEQUENCED_TASK_SOURCE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_SEQUENCED_TASK_SOURCE_H_
+
+#include "base/optional.h"
+#include "base/pending_task.h"
+
+namespace blink {
+namespace scheduler {
+class LazyNow;
+
+namespace internal {
+
+// This is temporary interface for ThreadController to be able to run tasks
+// from TaskQueueManager.
+class SequencedTaskSource {
+ public:
+ // TODO(alexclarke): Move this enum elsewhere.
+ enum class WorkType { kImmediate, kDelayed };
+
+ // Take a next task to run from a sequence.
+ // TODO(altimin): Do not pass |work_type| here.
+ virtual base::Optional<base::PendingTask> TakeTask() = 0;
+
+ // Notify a sequence that a taken task has been completed.
+ virtual void DidRunTask() = 0;
+
+ // Returns the delay till the next task, or base::TimeDelta::Max() if there
+ // isn't one.
+ virtual base::TimeDelta DelayTillNextTask(LazyNow* lazy_now) = 0;
+};
+
+} // namespace internal
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_SEQUENCED_TASK_SOURCE_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue.cc b/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue.cc
new file mode 100644
index 00000000000..d1dd378e3a8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue.cc
@@ -0,0 +1,279 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue.h"
+
+#include "base/bind_helpers.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_manager_impl.h"
+
+namespace blink {
+namespace scheduler {
+
+TaskQueue::TaskQueue(std::unique_ptr<internal::TaskQueueImpl> impl,
+ const TaskQueue::Spec& spec)
+ : impl_(std::move(impl)),
+ thread_id_(base::PlatformThread::CurrentId()),
+ task_queue_manager_(impl_ ? impl_->GetTaskQueueManagerWeakPtr()
+ : nullptr),
+ graceful_queue_shutdown_helper_(
+ impl_ ? impl_->GetGracefulQueueShutdownHelper() : nullptr) {}
+
+TaskQueue::~TaskQueue() {
+ // scoped_refptr guarantees us that this object isn't used.
+ if (!impl_)
+ return;
+ if (impl_->IsUnregistered())
+ return;
+ graceful_queue_shutdown_helper_->GracefullyShutdownTaskQueue(
+ TakeTaskQueueImpl());
+}
+
+TaskQueue::Task::Task(TaskQueue::PostedTask task,
+ base::TimeTicks desired_run_time)
+ : PendingTask(task.posted_from,
+ std::move(task.callback),
+ desired_run_time,
+ task.nestable),
+ task_type_(task.task_type) {}
+
+TaskQueue::PostedTask::PostedTask(base::OnceClosure callback,
+ base::Location posted_from,
+ base::TimeDelta delay,
+ base::Nestable nestable,
+ int task_type)
+ : callback(std::move(callback)),
+ posted_from(posted_from),
+ delay(delay),
+ nestable(nestable),
+ task_type(task_type) {}
+
+void TaskQueue::ShutdownTaskQueue() {
+ DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
+ base::AutoLock lock(impl_lock_);
+ if (!impl_)
+ return;
+ if (!task_queue_manager_) {
+ impl_.reset();
+ return;
+ }
+ impl_->SetBlameContext(nullptr);
+ impl_->SetOnTaskStartedHandler(
+ internal::TaskQueueImpl::OnTaskStartedHandler());
+ impl_->SetOnTaskCompletedHandler(
+ internal::TaskQueueImpl::OnTaskCompletedHandler());
+ task_queue_manager_->UnregisterTaskQueueImpl(TakeTaskQueueImpl());
+}
+
+bool TaskQueue::RunsTasksInCurrentSequence() const {
+ return IsOnMainThread();
+}
+
+bool TaskQueue::PostDelayedTask(const base::Location& from_here,
+ base::OnceClosure task,
+ base::TimeDelta delay) {
+ internal::TaskQueueImpl::PostTaskResult result;
+ {
+ auto lock = AcquireImplReadLockIfNeeded();
+ if (!impl_)
+ return false;
+ result = impl_->PostDelayedTask(PostedTask(
+ std::move(task), from_here, delay, base::Nestable::kNestable));
+ }
+ return result.success;
+}
+
+bool TaskQueue::PostNonNestableDelayedTask(const base::Location& from_here,
+ base::OnceClosure task,
+ base::TimeDelta delay) {
+ internal::TaskQueueImpl::PostTaskResult result;
+ {
+ auto lock = AcquireImplReadLockIfNeeded();
+ if (!impl_)
+ return false;
+ result = impl_->PostDelayedTask(PostedTask(
+ std::move(task), from_here, delay, base::Nestable::kNonNestable));
+ }
+ return result.success;
+}
+
+bool TaskQueue::PostTaskWithMetadata(PostedTask task) {
+ internal::TaskQueueImpl::PostTaskResult result;
+ {
+ auto lock = AcquireImplReadLockIfNeeded();
+ if (!impl_)
+ return false;
+ result = impl_->PostDelayedTask(std::move(task));
+ }
+ return result.success;
+}
+
+std::unique_ptr<TaskQueue::QueueEnabledVoter>
+TaskQueue::CreateQueueEnabledVoter() {
+ DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
+ if (!impl_)
+ return nullptr;
+ return impl_->CreateQueueEnabledVoter(this);
+}
+
+bool TaskQueue::IsQueueEnabled() const {
+ DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
+ if (!impl_)
+ return false;
+ return impl_->IsQueueEnabled();
+}
+
+bool TaskQueue::IsEmpty() const {
+ DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
+ if (!impl_)
+ return true;
+ return impl_->IsEmpty();
+}
+
+size_t TaskQueue::GetNumberOfPendingTasks() const {
+ DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
+ if (!impl_)
+ return 0;
+ return impl_->GetNumberOfPendingTasks();
+}
+
+bool TaskQueue::HasTaskToRunImmediately() const {
+ DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
+ if (!impl_)
+ return false;
+ return impl_->HasTaskToRunImmediately();
+}
+
+base::Optional<base::TimeTicks> TaskQueue::GetNextScheduledWakeUp() {
+ DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
+ if (!impl_)
+ return base::nullopt;
+ return impl_->GetNextScheduledWakeUp();
+}
+
+void TaskQueue::SetQueuePriority(TaskQueue::QueuePriority priority) {
+ DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
+ if (!impl_)
+ return;
+ impl_->SetQueuePriority(priority);
+}
+
+TaskQueue::QueuePriority TaskQueue::GetQueuePriority() const {
+ DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
+ if (!impl_)
+ return TaskQueue::QueuePriority::kLowPriority;
+ return impl_->GetQueuePriority();
+}
+
+void TaskQueue::AddTaskObserver(
+ base::MessageLoop::TaskObserver* task_observer) {
+ DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
+ if (!impl_)
+ return;
+ impl_->AddTaskObserver(task_observer);
+}
+
+void TaskQueue::RemoveTaskObserver(
+ base::MessageLoop::TaskObserver* task_observer) {
+ DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
+ if (!impl_)
+ return;
+ impl_->RemoveTaskObserver(task_observer);
+}
+
+void TaskQueue::SetTimeDomain(TimeDomain* time_domain) {
+ DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
+ if (!impl_)
+ return;
+ impl_->SetTimeDomain(time_domain);
+}
+
+TimeDomain* TaskQueue::GetTimeDomain() const {
+ DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
+ if (!impl_)
+ return nullptr;
+ return impl_->GetTimeDomain();
+}
+
+void TaskQueue::SetBlameContext(
+ base::trace_event::BlameContext* blame_context) {
+ DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
+ if (!impl_)
+ return;
+ impl_->SetBlameContext(blame_context);
+}
+
+void TaskQueue::InsertFence(InsertFencePosition position) {
+ DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
+ if (!impl_)
+ return;
+ impl_->InsertFence(position);
+}
+
+void TaskQueue::InsertFenceAt(base::TimeTicks time) {
+ impl_->InsertFenceAt(time);
+}
+
+void TaskQueue::RemoveFence() {
+ DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
+ if (!impl_)
+ return;
+ impl_->RemoveFence();
+}
+
+bool TaskQueue::HasActiveFence() {
+ DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
+ if (!impl_)
+ return false;
+ return impl_->HasActiveFence();
+}
+
+bool TaskQueue::BlockedByFence() const {
+ DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
+ if (!impl_)
+ return false;
+ return impl_->BlockedByFence();
+}
+
+const char* TaskQueue::GetName() const {
+ auto lock = AcquireImplReadLockIfNeeded();
+ if (!impl_)
+ return "";
+ return impl_->GetName();
+}
+
+void TaskQueue::SetObserver(Observer* observer) {
+ DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
+ if (!impl_)
+ return;
+ if (observer) {
+ // Observer is guaranteed to outlive TaskQueue and TaskQueueImpl lifecycle
+ // is controlled by |this|.
+ impl_->SetOnNextWakeUpChangedCallback(base::BindRepeating(
+ &TaskQueue::Observer::OnQueueNextWakeUpChanged,
+ base::Unretained(observer), base::Unretained(this)));
+ } else {
+ impl_->SetOnNextWakeUpChangedCallback(
+ base::RepeatingCallback<void(base::TimeTicks)>());
+ }
+}
+
+bool TaskQueue::IsOnMainThread() const {
+ return thread_id_ == base::PlatformThread::CurrentId();
+}
+
+base::Optional<MoveableAutoLock> TaskQueue::AcquireImplReadLockIfNeeded()
+ const {
+ if (IsOnMainThread())
+ return base::nullopt;
+ return MoveableAutoLock(impl_lock_);
+}
+
+std::unique_ptr<internal::TaskQueueImpl> TaskQueue::TakeTaskQueueImpl() {
+ DCHECK(impl_);
+ return std::move(impl_);
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue.h b/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue.h
new file mode 100644
index 00000000000..031204f5581
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue.h
@@ -0,0 +1,307 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TASK_QUEUE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TASK_QUEUE_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/optional.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/platform_thread.h"
+#include "base/time/time.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/scheduler/base/graceful_queue_shutdown_helper.h"
+#include "third_party/blink/renderer/platform/scheduler/base/moveable_auto_lock.h"
+
+namespace base {
+namespace trace_event {
+class BlameContext;
+}
+} // namespace base
+
+namespace blink {
+namespace scheduler {
+namespace task_queue_throttler_unittest {
+class TaskQueueThrottlerTest;
+}
+namespace internal {
+class TaskQueueImpl;
+}
+
+class TimeDomain;
+class TaskQueueManagerImpl;
+
+class PLATFORM_EXPORT TaskQueue : public base::SingleThreadTaskRunner {
+ public:
+ class PLATFORM_EXPORT Observer {
+ public:
+ virtual ~Observer() = default;
+
+ // Notify observer that the time at which this queue wants to run
+ // the next task has changed. |next_wakeup| can be in the past
+ // (e.g. base::TimeTicks() can be used to notify about immediate work).
+ // Can be called on any thread
+ // All methods but SetObserver, SetTimeDomain and GetTimeDomain can be
+ // called on |queue|.
+ //
+ // TODO(altimin): Make it base::Optional<base::TimeTicks> to tell
+ // observer about cancellations.
+ virtual void OnQueueNextWakeUpChanged(TaskQueue* queue,
+ base::TimeTicks next_wake_up) = 0;
+ };
+
+ // A wrapper around base::OnceClosure with additional metadata to be passed
+ // to PostTask and plumbed until PendingTask is created.
+ struct PLATFORM_EXPORT PostedTask {
+ PostedTask(base::OnceClosure callback,
+ base::Location posted_from,
+ base::TimeDelta delay = base::TimeDelta(),
+ base::Nestable nestable = base::Nestable::kNestable,
+ int task_type = 0);
+
+ base::OnceClosure callback;
+ base::Location posted_from;
+ base::TimeDelta delay;
+ base::Nestable nestable;
+ int task_type;
+ };
+
+ // Unregisters the task queue after which no tasks posted to it will run and
+ // the TaskQueueManagerImpl's reference to it will be released soon.
+ virtual void ShutdownTaskQueue();
+
+ enum QueuePriority {
+ // Queues with control priority will run before any other queue, and will
+ // explicitly starve other queues. Typically this should only be used for
+ // private queues which perform control operations.
+ kControlPriority,
+
+ // The selector will prioritize highest over high, normal and low; and
+ // high over normal and low; and normal over low. However it will ensure
+ // neither of the lower priority queues can be completely starved by higher
+ // priority tasks. All three of these queues will always take priority over
+ // and can starve the best effort queue.
+ kHighestPriority,
+
+ kHighPriority,
+
+ // Queues with normal priority are the default.
+ kNormalPriority,
+ kLowPriority,
+
+ // Queues with best effort priority will only be run if all other queues are
+ // empty. They can be starved by the other queues.
+ kBestEffortPriority,
+ // Must be the last entry.
+ kQueuePriorityCount,
+ kFirstQueuePriority = kControlPriority,
+ };
+
+ // Can be called on any thread.
+ static const char* PriorityToString(QueuePriority priority);
+
+ // Options for constructing a TaskQueue.
+ struct Spec {
+ explicit Spec(const char* name)
+ : name(name),
+ should_monitor_quiescence(false),
+ time_domain(nullptr),
+ should_notify_observers(true) {}
+
+ Spec SetShouldMonitorQuiescence(bool should_monitor) {
+ should_monitor_quiescence = should_monitor;
+ return *this;
+ }
+
+ Spec SetShouldNotifyObservers(bool run_observers) {
+ should_notify_observers = run_observers;
+ return *this;
+ }
+
+ Spec SetTimeDomain(TimeDomain* domain) {
+ time_domain = domain;
+ return *this;
+ }
+
+ const char* name;
+ bool should_monitor_quiescence;
+ TimeDomain* time_domain;
+ bool should_notify_observers;
+ };
+
+ // Interface to pass per-task metadata to RendererScheduler.
+ class PLATFORM_EXPORT Task : public base::PendingTask {
+ public:
+ Task(PostedTask posted_task, base::TimeTicks desired_run_time);
+
+ int task_type() const { return task_type_; }
+
+ private:
+ int task_type_;
+ };
+
+ // An interface that lets the owner vote on whether or not the associated
+ // TaskQueue should be enabled.
+ class QueueEnabledVoter {
+ public:
+ QueueEnabledVoter() = default;
+ virtual ~QueueEnabledVoter() = default;
+
+ // Votes to enable or disable the associated TaskQueue. The TaskQueue will
+ // only be enabled if all the voters agree it should be enabled, or if there
+ // are no voters.
+ // NOTE this must be called on the thread the associated TaskQueue was
+ // created on.
+ virtual void SetQueueEnabled(bool enabled) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QueueEnabledVoter);
+ };
+
+ // Returns an interface that allows the caller to vote on whether or not this
+ // TaskQueue is enabled. The TaskQueue will be enabled if there are no voters
+ // or if all agree it should be enabled.
+ // NOTE this must be called on the thread this TaskQueue was created by.
+ std::unique_ptr<QueueEnabledVoter> CreateQueueEnabledVoter();
+
+ // NOTE this must be called on the thread this TaskQueue was created by.
+ bool IsQueueEnabled() const;
+
+ // Returns true if the queue is completely empty.
+ bool IsEmpty() const;
+
+ // Returns the number of pending tasks in the queue.
+ size_t GetNumberOfPendingTasks() const;
+
+ // Returns true if the queue has work that's ready to execute now.
+ // NOTE: this must be called on the thread this TaskQueue was created by.
+ bool HasTaskToRunImmediately() const;
+
+ // Returns requested run time of next scheduled wake-up for a delayed task
+ // which is not ready to run. If there are no such tasks or the queue is
+ // disabled (by a QueueEnabledVoter) it returns base::nullopt.
+ // NOTE: this must be called on the thread this TaskQueue was created by.
+ base::Optional<base::TimeTicks> GetNextScheduledWakeUp();
+
+ // Can be called on any thread.
+ virtual const char* GetName() const;
+
+ // Set the priority of the queue to |priority|. NOTE this must be called on
+ // the thread this TaskQueue was created by.
+ void SetQueuePriority(QueuePriority priority);
+
+ // Returns the current queue priority.
+ QueuePriority GetQueuePriority() const;
+
+ // These functions can only be called on the same thread that the task queue
+ // manager executes its tasks on.
+ void AddTaskObserver(base::MessageLoop::TaskObserver* task_observer);
+ void RemoveTaskObserver(base::MessageLoop::TaskObserver* task_observer);
+
+ // Set the blame context which is entered and left while executing tasks from
+ // this task queue. |blame_context| must be null or outlive this task queue.
+ // Must be called on the thread this TaskQueue was created by.
+ void SetBlameContext(base::trace_event::BlameContext* blame_context);
+
+ // Removes the task queue from the previous TimeDomain and adds it to
+ // |domain|. This is a moderately expensive operation.
+ void SetTimeDomain(TimeDomain* domain);
+
+ // Returns the queue's current TimeDomain. Can be called from any thread.
+ TimeDomain* GetTimeDomain() const;
+
+ enum class InsertFencePosition {
+ kNow, // Tasks posted on the queue up till this point further may run.
+ // All further tasks are blocked.
+ kBeginningOfTime, // No tasks posted on this queue may run.
+ };
+
+ // Inserts a barrier into the task queue which prevents tasks with an enqueue
+ // order greater than the fence from running until either the fence has been
+ // removed or a subsequent fence has unblocked some tasks within the queue.
+ // Note: delayed tasks get their enqueue order set once their delay has
+ // expired, and non-delayed tasks get their enqueue order set when posted.
+ //
+ // Fences come in three flavours:
+ // - Regular (InsertFence(NOW)) - all tasks posted after this moment
+ // are blocked.
+ // - Fully blocking (InsertFence(kBeginningOfTime)) - all tasks including
+ // already posted are blocked.
+ // - Delayed (InsertFenceAt(timestamp)) - blocks all tasks posted after given
+ // point in time (must be in the future).
+ //
+ // Only one fence can be scheduled at a time. Inserting a new fence
+ // will automatically remove the previous one, regardless of fence type.
+ void InsertFence(InsertFencePosition position);
+ void InsertFenceAt(base::TimeTicks time);
+
+ // Removes any previously added fence and unblocks execution of any tasks
+ // blocked by it.
+ void RemoveFence();
+
+ bool HasActiveFence();
+
+ // Returns true if the queue has a fence which is blocking execution of tasks.
+ bool BlockedByFence() const;
+
+ void SetObserver(Observer* observer);
+
+ // base::SingleThreadTaskRunner implementation
+ bool RunsTasksInCurrentSequence() const override;
+ bool PostDelayedTask(const base::Location& from_here,
+ base::OnceClosure task,
+ base::TimeDelta delay) override;
+ bool PostNonNestableDelayedTask(const base::Location& from_here,
+ base::OnceClosure task,
+ base::TimeDelta delay) override;
+
+ bool PostTaskWithMetadata(PostedTask task);
+
+ protected:
+ TaskQueue(std::unique_ptr<internal::TaskQueueImpl> impl,
+ const TaskQueue::Spec& spec);
+ ~TaskQueue() override;
+
+ internal::TaskQueueImpl* GetTaskQueueImpl() const { return impl_.get(); }
+
+ private:
+ friend class internal::TaskQueueImpl;
+ friend class TaskQueueManagerImpl;
+
+ friend class task_queue_throttler_unittest::TaskQueueThrottlerTest;
+
+ bool IsOnMainThread() const;
+
+ base::Optional<MoveableAutoLock> AcquireImplReadLockIfNeeded() const;
+
+ // Take |impl_| and untie it from the enclosing task queue.
+ std::unique_ptr<internal::TaskQueueImpl> TakeTaskQueueImpl();
+
+ // |impl_| can be written to on the main thread but can be read from
+ // any thread.
+ // |impl_lock_| must be acquired when writing to |impl_| or when accessing
+ // it from non-main thread. Reading from the main thread does not require
+ // a lock.
+ mutable base::Lock impl_lock_;
+ std::unique_ptr<internal::TaskQueueImpl> impl_;
+
+ const base::PlatformThreadId thread_id_;
+
+ const base::WeakPtr<TaskQueueManagerImpl> task_queue_manager_;
+
+ const scoped_refptr<internal::GracefulQueueShutdownHelper>
+ graceful_queue_shutdown_helper_;
+
+ THREAD_CHECKER(main_thread_checker_);
+
+ DISALLOW_COPY_AND_ASSIGN(TaskQueue);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TASK_QUEUE_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_impl.cc b/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_impl.cc
new file mode 100644
index 00000000000..d53960530ce
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_impl.cc
@@ -0,0 +1,1039 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/time/time.h"
+#include "base/trace_event/blame_context.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_manager_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/base/time_domain.h"
+#include "third_party/blink/renderer/platform/scheduler/base/work_queue.h"
+#include "third_party/blink/renderer/platform/scheduler/util/tracing_helper.h"
+
+namespace blink {
+namespace scheduler {
+
+// static
+const char* TaskQueue::PriorityToString(TaskQueue::QueuePriority priority) {
+ switch (priority) {
+ case kControlPriority:
+ return "control";
+ case kHighestPriority:
+ return "highest";
+ case kHighPriority:
+ return "high";
+ case kNormalPriority:
+ return "normal";
+ case kLowPriority:
+ return "low";
+ case kBestEffortPriority:
+ return "best_effort";
+ default:
+ NOTREACHED();
+ return nullptr;
+ }
+}
+
+namespace internal {
+
+TaskQueueImpl::TaskQueueImpl(TaskQueueManagerImpl* task_queue_manager,
+ TimeDomain* time_domain,
+ const TaskQueue::Spec& spec)
+ : name_(spec.name),
+ thread_id_(base::PlatformThread::CurrentId()),
+ any_thread_(task_queue_manager, time_domain),
+ main_thread_only_(task_queue_manager, this, time_domain),
+ should_monitor_quiescence_(spec.should_monitor_quiescence),
+ should_notify_observers_(spec.should_notify_observers) {
+ DCHECK(time_domain);
+ time_domain->RegisterQueue(this);
+}
+
+TaskQueueImpl::~TaskQueueImpl() {
+#if DCHECK_IS_ON()
+ base::AutoLock lock(any_thread_lock_);
+ // NOTE this check shouldn't fire because |TaskQueueManagerImpl::queues_|
+ // contains a strong reference to this TaskQueueImpl and the
+ // TaskQueueManagerImpl destructor calls UnregisterTaskQueue on all task
+ // queues.
+ DCHECK(!any_thread().task_queue_manager)
+ << "UnregisterTaskQueue must be called first!";
+#endif
+}
+
+TaskQueueImpl::PostTaskResult::PostTaskResult()
+ : task(base::OnceClosure(), base::Location()) {}
+
+TaskQueueImpl::PostTaskResult::PostTaskResult(bool success,
+ TaskQueue::PostedTask task)
+ : success(success), task(std::move(task)) {}
+
+TaskQueueImpl::PostTaskResult TaskQueueImpl::PostTaskResult::Success() {
+ return PostTaskResult(
+ true, TaskQueue::PostedTask(base::OnceClosure(), base::Location()));
+}
+
+TaskQueueImpl::PostTaskResult TaskQueueImpl::PostTaskResult::Fail(
+ TaskQueue::PostedTask task) {
+ return PostTaskResult(false, std::move(task));
+}
+
+TaskQueueImpl::Task::Task(TaskQueue::PostedTask task,
+ base::TimeTicks desired_run_time,
+ EnqueueOrder sequence_number)
+ : TaskQueue::Task(std::move(task), desired_run_time),
+#ifndef NDEBUG
+ enqueue_order_set_(false),
+#endif
+ enqueue_order_(0) {
+ sequence_num = sequence_number;
+}
+
+TaskQueueImpl::Task::Task(TaskQueue::PostedTask task,
+ base::TimeTicks desired_run_time,
+ EnqueueOrder sequence_number,
+ EnqueueOrder enqueue_order)
+ : TaskQueue::Task(std::move(task), desired_run_time),
+#ifndef NDEBUG
+ enqueue_order_set_(true),
+#endif
+ enqueue_order_(enqueue_order) {
+ sequence_num = sequence_number;
+}
+
+TaskQueueImpl::AnyThread::AnyThread(TaskQueueManagerImpl* task_queue_manager,
+ TimeDomain* time_domain)
+ : task_queue_manager(task_queue_manager), time_domain(time_domain) {}
+
+TaskQueueImpl::AnyThread::~AnyThread() = default;
+
+TaskQueueImpl::MainThreadOnly::MainThreadOnly(
+ TaskQueueManagerImpl* task_queue_manager,
+ TaskQueueImpl* task_queue,
+ TimeDomain* time_domain)
+ : task_queue_manager(task_queue_manager),
+ time_domain(time_domain),
+ delayed_work_queue(
+ new WorkQueue(task_queue, "delayed", WorkQueue::QueueType::kDelayed)),
+ immediate_work_queue(new WorkQueue(task_queue,
+ "immediate",
+ WorkQueue::QueueType::kImmediate)),
+ set_index(0),
+ is_enabled_refcount(0),
+ voter_refcount(0),
+ blame_context(nullptr),
+ current_fence(0),
+ is_enabled_for_test(true) {}
+
+TaskQueueImpl::MainThreadOnly::~MainThreadOnly() = default;
+
+void TaskQueueImpl::UnregisterTaskQueue() {
+ TaskDeque immediate_incoming_queue;
+
+ {
+ base::AutoLock lock(any_thread_lock_);
+ base::AutoLock immediate_incoming_queue_lock(
+ immediate_incoming_queue_lock_);
+
+ if (main_thread_only().time_domain)
+ main_thread_only().time_domain->UnregisterQueue(this);
+
+ if (!any_thread().task_queue_manager)
+ return;
+
+ main_thread_only().on_task_completed_handler = OnTaskCompletedHandler();
+ any_thread().time_domain = nullptr;
+ main_thread_only().time_domain = nullptr;
+
+ any_thread().task_queue_manager = nullptr;
+ main_thread_only().task_queue_manager = nullptr;
+ any_thread().on_next_wake_up_changed_callback =
+ OnNextWakeUpChangedCallback();
+ main_thread_only().on_next_wake_up_changed_callback =
+ OnNextWakeUpChangedCallback();
+ immediate_incoming_queue.Swap(immediate_incoming_queue_);
+ }
+
+ // It is possible for a task to hold a scoped_refptr to this, which
+ // will lead to TaskQueueImpl destructor being called when deleting a task.
+ // To avoid use-after-free, we need to clear all fields of a task queue
+ // before starting to delete the tasks.
+ // All work queues and priority queues containing tasks should be moved to
+ // local variables on stack (std::move for unique_ptrs and swap for queues)
+ // before clearing them and deleting tasks.
+
+ // Flush the queues outside of the lock because TSAN complains about a lock
+ // order inversion for tasks that are posted from within a lock, with a
+ // destructor that acquires the same lock.
+
+ std::priority_queue<Task> delayed_incoming_queue;
+ delayed_incoming_queue.swap(main_thread_only().delayed_incoming_queue);
+
+ std::unique_ptr<WorkQueue> immediate_work_queue =
+ std::move(main_thread_only().immediate_work_queue);
+ std::unique_ptr<WorkQueue> delayed_work_queue =
+ std::move(main_thread_only().delayed_work_queue);
+}
+
+const char* TaskQueueImpl::GetName() const {
+ return name_;
+}
+
+bool TaskQueueImpl::RunsTasksInCurrentSequence() const {
+ return base::PlatformThread::CurrentId() == thread_id_;
+}
+
+TaskQueueImpl::PostTaskResult TaskQueueImpl::PostDelayedTask(
+ TaskQueue::PostedTask task) {
+ if (task.delay.is_zero())
+ return PostImmediateTaskImpl(std::move(task));
+
+ return PostDelayedTaskImpl(std::move(task));
+}
+
+TaskQueueImpl::PostTaskResult TaskQueueImpl::PostImmediateTaskImpl(
+ TaskQueue::PostedTask task) {
+ // Use CHECK instead of DCHECK to crash earlier. See http://crbug.com/711167
+ // for details.
+ CHECK(task.callback);
+ base::AutoLock lock(any_thread_lock_);
+ if (!any_thread().task_queue_manager)
+ return PostTaskResult::Fail(std::move(task));
+
+ EnqueueOrder sequence_number =
+ any_thread().task_queue_manager->GetNextSequenceNumber();
+
+ PushOntoImmediateIncomingQueueLocked(Task(std::move(task),
+ any_thread().time_domain->Now(),
+ sequence_number, sequence_number));
+ return PostTaskResult::Success();
+}
+
+TaskQueueImpl::PostTaskResult TaskQueueImpl::PostDelayedTaskImpl(
+ TaskQueue::PostedTask task) {
+ // Use CHECK instead of DCHECK to crash earlier. See http://crbug.com/711167
+ // for details.
+ CHECK(task.callback);
+ DCHECK_GT(task.delay, base::TimeDelta());
+ if (base::PlatformThread::CurrentId() == thread_id_) {
+ // Lock-free fast path for delayed tasks posted from the main thread.
+ if (!main_thread_only().task_queue_manager)
+ return PostTaskResult::Fail(std::move(task));
+
+ EnqueueOrder sequence_number =
+ main_thread_only().task_queue_manager->GetNextSequenceNumber();
+
+ base::TimeTicks time_domain_now = main_thread_only().time_domain->Now();
+ base::TimeTicks time_domain_delayed_run_time = time_domain_now + task.delay;
+ PushOntoDelayedIncomingQueueFromMainThread(
+ Task(std::move(task), time_domain_delayed_run_time, sequence_number),
+ time_domain_now);
+ } else {
+ // NOTE posting a delayed task from a different thread is not expected to
+ // be common. This pathway is less optimal than perhaps it could be
+ // because it causes two main thread tasks to be run. Should this
+ // assumption prove to be false in future, we may need to revisit this.
+ base::AutoLock lock(any_thread_lock_);
+ if (!any_thread().task_queue_manager)
+ return PostTaskResult::Fail(std::move(task));
+
+ EnqueueOrder sequence_number =
+ any_thread().task_queue_manager->GetNextSequenceNumber();
+
+ base::TimeTicks time_domain_now = any_thread().time_domain->Now();
+ base::TimeTicks time_domain_delayed_run_time = time_domain_now + task.delay;
+ PushOntoDelayedIncomingQueueLocked(
+ Task(std::move(task), time_domain_delayed_run_time, sequence_number));
+ }
+ return PostTaskResult::Success();
+}
+
+void TaskQueueImpl::PushOntoDelayedIncomingQueueFromMainThread(
+ Task pending_task,
+ base::TimeTicks now) {
+ main_thread_only().task_queue_manager->DidQueueTask(pending_task);
+ main_thread_only().delayed_incoming_queue.push(std::move(pending_task));
+
+ LazyNow lazy_now = main_thread_only().time_domain->CreateLazyNow();
+ UpdateDelayedWakeUp(&lazy_now);
+
+ TraceQueueSize();
+}
+
+void TaskQueueImpl::PushOntoDelayedIncomingQueueLocked(Task pending_task) {
+ any_thread().task_queue_manager->DidQueueTask(pending_task);
+
+ int thread_hop_task_sequence_number =
+ any_thread().task_queue_manager->GetNextSequenceNumber();
+ // TODO(altimin): Add a copy method to Task to capture metadata here.
+ PushOntoImmediateIncomingQueueLocked(
+ Task(TaskQueue::PostedTask(
+ base::BindOnce(&TaskQueueImpl::ScheduleDelayedWorkTask,
+ base::Unretained(this), std::move(pending_task)),
+ FROM_HERE, base::TimeDelta(), base::Nestable::kNonNestable,
+ pending_task.task_type()),
+ base::TimeTicks(), thread_hop_task_sequence_number,
+ thread_hop_task_sequence_number));
+}
+
+void TaskQueueImpl::ScheduleDelayedWorkTask(Task pending_task) {
+ DCHECK(main_thread_checker_.CalledOnValidThread());
+ base::TimeTicks delayed_run_time = pending_task.delayed_run_time;
+ base::TimeTicks time_domain_now = main_thread_only().time_domain->Now();
+ if (delayed_run_time <= time_domain_now) {
+ // If |delayed_run_time| is in the past then push it onto the work queue
+ // immediately. To ensure the right task ordering we need to temporarily
+ // push it onto the |delayed_incoming_queue|.
+ delayed_run_time = time_domain_now;
+ pending_task.delayed_run_time = time_domain_now;
+ main_thread_only().delayed_incoming_queue.push(std::move(pending_task));
+ LazyNow lazy_now(time_domain_now);
+ WakeUpForDelayedWork(&lazy_now);
+ } else {
+ // If |delayed_run_time| is in the future we can queue it as normal.
+ PushOntoDelayedIncomingQueueFromMainThread(std::move(pending_task),
+ time_domain_now);
+ }
+ TraceQueueSize();
+}
+
+void TaskQueueImpl::PushOntoImmediateIncomingQueueLocked(Task task) {
+ // If the |immediate_incoming_queue| is empty we need a DoWork posted to make
+ // it run.
+ bool was_immediate_incoming_queue_empty;
+
+ EnqueueOrder sequence_number = task.sequence_num;
+ base::TimeTicks desired_run_time = task.delayed_run_time;
+
+ {
+ base::AutoLock lock(immediate_incoming_queue_lock_);
+ was_immediate_incoming_queue_empty = immediate_incoming_queue().empty();
+ immediate_incoming_queue().push_back(std::move(task));
+ any_thread().task_queue_manager->DidQueueTask(
+ immediate_incoming_queue().back());
+ }
+
+ if (was_immediate_incoming_queue_empty) {
+ // However there's no point posting a DoWork for a blocked queue. NB we can
+ // only tell if it's disabled from the main thread.
+ bool queue_is_blocked =
+ RunsTasksInCurrentSequence() &&
+ (!IsQueueEnabled() || main_thread_only().current_fence);
+ any_thread().task_queue_manager->OnQueueHasIncomingImmediateWork(
+ this, sequence_number, queue_is_blocked);
+ if (!any_thread().on_next_wake_up_changed_callback.is_null())
+ any_thread().on_next_wake_up_changed_callback.Run(desired_run_time);
+ }
+
+ TraceQueueSize();
+}
+
+void TaskQueueImpl::ReloadImmediateWorkQueueIfEmpty() {
+ if (!main_thread_only().immediate_work_queue->Empty())
+ return;
+
+ main_thread_only().immediate_work_queue->ReloadEmptyImmediateQueue();
+}
+
+TaskQueueImpl::TaskDeque TaskQueueImpl::TakeImmediateIncomingQueue() {
+ base::AutoLock immediate_incoming_queue_lock(immediate_incoming_queue_lock_);
+ TaskQueueImpl::TaskDeque queue;
+ queue.Swap(immediate_incoming_queue());
+
+ // Activate delayed fence if necessary. This is ideologically similar to
+ // ActivateDelayedFenceIfNeeded, but due to immediate tasks being posted
+ // from any thread we can't generate an enqueue order for the fence there,
+ // so we have to check all immediate tasks and use their enqueue order for
+ // a fence.
+ if (main_thread_only().delayed_fence) {
+ for (const Task& task : queue) {
+ if (task.delayed_run_time >= main_thread_only().delayed_fence.value()) {
+ main_thread_only().delayed_fence = base::nullopt;
+ DCHECK_EQ(main_thread_only().current_fence,
+ static_cast<EnqueueOrder>(EnqueueOrderValues::kNone));
+ main_thread_only().current_fence = task.enqueue_order();
+ // Do not trigger WorkQueueSets notification when taking incoming
+ // immediate queue.
+ main_thread_only().immediate_work_queue->InsertFenceSilently(
+ main_thread_only().current_fence);
+ main_thread_only().delayed_work_queue->InsertFenceSilently(
+ main_thread_only().current_fence);
+ break;
+ }
+ }
+ }
+
+ return queue;
+}
+
+bool TaskQueueImpl::IsEmpty() const {
+ if (!main_thread_only().delayed_work_queue->Empty() ||
+ !main_thread_only().delayed_incoming_queue.empty() ||
+ !main_thread_only().immediate_work_queue->Empty()) {
+ return false;
+ }
+
+ base::AutoLock lock(immediate_incoming_queue_lock_);
+ return immediate_incoming_queue().empty();
+}
+
+size_t TaskQueueImpl::GetNumberOfPendingTasks() const {
+ size_t task_count = 0;
+ task_count += main_thread_only().delayed_work_queue->Size();
+ task_count += main_thread_only().delayed_incoming_queue.size();
+ task_count += main_thread_only().immediate_work_queue->Size();
+
+ base::AutoLock lock(immediate_incoming_queue_lock_);
+ task_count += immediate_incoming_queue().size();
+ return task_count;
+}
+
+bool TaskQueueImpl::HasTaskToRunImmediately() const {
+ // Any work queue tasks count as immediate work.
+ if (!main_thread_only().delayed_work_queue->Empty() ||
+ !main_thread_only().immediate_work_queue->Empty()) {
+ return true;
+ }
+
+ // Tasks on |delayed_incoming_queue| that could run now, count as
+ // immediate work.
+ if (!main_thread_only().delayed_incoming_queue.empty() &&
+ main_thread_only().delayed_incoming_queue.top().delayed_run_time <=
+ main_thread_only().time_domain->CreateLazyNow().Now()) {
+ return true;
+ }
+
+ // Finally tasks on |immediate_incoming_queue| count as immediate work.
+ base::AutoLock lock(immediate_incoming_queue_lock_);
+ return !immediate_incoming_queue().empty();
+}
+
+base::Optional<TaskQueueImpl::DelayedWakeUp>
+TaskQueueImpl::GetNextScheduledWakeUpImpl() {
+ // Note we don't scheduled a wake-up for disabled queues.
+ if (main_thread_only().delayed_incoming_queue.empty() || !IsQueueEnabled())
+ return base::nullopt;
+
+ return main_thread_only().delayed_incoming_queue.top().delayed_wake_up();
+}
+
+base::Optional<base::TimeTicks> TaskQueueImpl::GetNextScheduledWakeUp() {
+ base::Optional<DelayedWakeUp> wake_up = GetNextScheduledWakeUpImpl();
+ if (!wake_up)
+ return base::nullopt;
+ return wake_up->time;
+}
+
+void TaskQueueImpl::WakeUpForDelayedWork(LazyNow* lazy_now) {
+ // Enqueue all delayed tasks that should be running now, skipping any that
+ // have been canceled.
+ while (!main_thread_only().delayed_incoming_queue.empty()) {
+ Task& task =
+ const_cast<Task&>(main_thread_only().delayed_incoming_queue.top());
+ if (!task.task || task.task.IsCancelled()) {
+ main_thread_only().delayed_incoming_queue.pop();
+ continue;
+ }
+ if (task.delayed_run_time > lazy_now->Now())
+ break;
+ ActivateDelayedFenceIfNeeded(task.delayed_run_time);
+ task.set_enqueue_order(
+ main_thread_only().task_queue_manager->GetNextSequenceNumber());
+ main_thread_only().delayed_work_queue->Push(std::move(task));
+ main_thread_only().delayed_incoming_queue.pop();
+
+ // Normally WakeUpForDelayedWork is called inside DoWork, but it also
+ // can be called elsewhere (e.g. tests and fast-path for posting
+ // delayed tasks). Ensure that there is a DoWork posting. No-op inside
+ // existing DoWork due to DoWork deduplication.
+ if (IsQueueEnabled() || !main_thread_only().current_fence) {
+ main_thread_only().task_queue_manager->MaybeScheduleImmediateWork(
+ FROM_HERE);
+ }
+ }
+
+ UpdateDelayedWakeUp(lazy_now);
+}
+
+void TaskQueueImpl::TraceQueueSize() const {
+ bool is_tracing;
+ TRACE_EVENT_CATEGORY_GROUP_ENABLED(
+ TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), &is_tracing);
+ if (!is_tracing)
+ return;
+
+ // It's only safe to access the work queues from the main thread.
+ // TODO(alexclarke): We should find another way of tracing this
+ if (base::PlatformThread::CurrentId() != thread_id_)
+ return;
+
+ base::AutoLock lock(immediate_incoming_queue_lock_);
+ TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), GetName(),
+ immediate_incoming_queue().size() +
+ main_thread_only().immediate_work_queue->Size() +
+ main_thread_only().delayed_work_queue->Size() +
+ main_thread_only().delayed_incoming_queue.size());
+}
+
+void TaskQueueImpl::SetQueuePriority(TaskQueue::QueuePriority priority) {
+ if (!main_thread_only().task_queue_manager || priority == GetQueuePriority())
+ return;
+ main_thread_only()
+ .task_queue_manager->main_thread_only()
+ .selector.SetQueuePriority(this, priority);
+}
+
+TaskQueue::QueuePriority TaskQueueImpl::GetQueuePriority() const {
+ size_t set_index = immediate_work_queue()->work_queue_set_index();
+ DCHECK_EQ(set_index, delayed_work_queue()->work_queue_set_index());
+ return static_cast<TaskQueue::QueuePriority>(set_index);
+}
+
+void TaskQueueImpl::AsValueInto(base::TimeTicks now,
+ base::trace_event::TracedValue* state) const {
+ base::AutoLock lock(any_thread_lock_);
+ base::AutoLock immediate_incoming_queue_lock(immediate_incoming_queue_lock_);
+ state->BeginDictionary();
+ state->SetString("name", GetName());
+ if (!main_thread_only().task_queue_manager) {
+ state->SetBoolean("unregistered", true);
+ state->EndDictionary();
+ return;
+ }
+ DCHECK(main_thread_only().time_domain);
+ DCHECK(main_thread_only().delayed_work_queue);
+ DCHECK(main_thread_only().immediate_work_queue);
+
+ state->SetString("task_queue_id", PointerToString(this));
+ state->SetBoolean("enabled", IsQueueEnabled());
+ state->SetString("time_domain_name",
+ main_thread_only().time_domain->GetName());
+ state->SetInteger("immediate_incoming_queue_size",
+ immediate_incoming_queue().size());
+ state->SetInteger("delayed_incoming_queue_size",
+ main_thread_only().delayed_incoming_queue.size());
+ state->SetInteger("immediate_work_queue_size",
+ main_thread_only().immediate_work_queue->Size());
+ state->SetInteger("delayed_work_queue_size",
+ main_thread_only().delayed_work_queue->Size());
+ if (!main_thread_only().delayed_incoming_queue.empty()) {
+ base::TimeDelta delay_to_next_task =
+ (main_thread_only().delayed_incoming_queue.top().delayed_run_time -
+ main_thread_only().time_domain->CreateLazyNow().Now());
+ state->SetDouble("delay_to_next_task_ms",
+ delay_to_next_task.InMillisecondsF());
+ }
+ if (main_thread_only().current_fence)
+ state->SetInteger("current_fence", main_thread_only().current_fence);
+ if (main_thread_only().delayed_fence) {
+ state->SetDouble(
+ "delayed_fence_seconds_from_now",
+ (main_thread_only().delayed_fence.value() - now).InSecondsF());
+ }
+ if (AreVerboseSnapshotsEnabled()) {
+ state->BeginArray("immediate_incoming_queue");
+ QueueAsValueInto(immediate_incoming_queue(), now, state);
+ state->EndArray();
+ state->BeginArray("delayed_work_queue");
+ main_thread_only().delayed_work_queue->AsValueInto(now, state);
+ state->EndArray();
+ state->BeginArray("immediate_work_queue");
+ main_thread_only().immediate_work_queue->AsValueInto(now, state);
+ state->EndArray();
+ state->BeginArray("delayed_incoming_queue");
+ QueueAsValueInto(main_thread_only().delayed_incoming_queue, now, state);
+ state->EndArray();
+ }
+ state->SetString("priority", TaskQueue::PriorityToString(GetQueuePriority()));
+ state->EndDictionary();
+}
+
+void TaskQueueImpl::AddTaskObserver(
+ base::MessageLoop::TaskObserver* task_observer) {
+ main_thread_only().task_observers.AddObserver(task_observer);
+}
+
+void TaskQueueImpl::RemoveTaskObserver(
+ base::MessageLoop::TaskObserver* task_observer) {
+ main_thread_only().task_observers.RemoveObserver(task_observer);
+}
+
+void TaskQueueImpl::NotifyWillProcessTask(
+ const base::PendingTask& pending_task) {
+ DCHECK(should_notify_observers_);
+ if (main_thread_only().blame_context)
+ main_thread_only().blame_context->Enter();
+ for (auto& observer : main_thread_only().task_observers)
+ observer.WillProcessTask(pending_task);
+}
+
+void TaskQueueImpl::NotifyDidProcessTask(
+ const base::PendingTask& pending_task) {
+ DCHECK(should_notify_observers_);
+ for (auto& observer : main_thread_only().task_observers)
+ observer.DidProcessTask(pending_task);
+ if (main_thread_only().blame_context)
+ main_thread_only().blame_context->Leave();
+}
+
+void TaskQueueImpl::SetTimeDomain(TimeDomain* time_domain) {
+ {
+ base::AutoLock lock(any_thread_lock_);
+ DCHECK(time_domain);
+ // NOTE this is similar to checking |any_thread().task_queue_manager| but
+ // the TaskQueueSelectorTests constructs TaskQueueImpl directly with a null
+ // task_queue_manager. Instead we check |any_thread().time_domain| which is
+ // another way of asserting that UnregisterTaskQueue has not been called.
+ DCHECK(any_thread().time_domain);
+ if (!any_thread().time_domain)
+ return;
+ DCHECK(main_thread_checker_.CalledOnValidThread());
+ if (time_domain == main_thread_only().time_domain)
+ return;
+
+ any_thread().time_domain = time_domain;
+ }
+
+ main_thread_only().time_domain->UnregisterQueue(this);
+ main_thread_only().time_domain = time_domain;
+ time_domain->RegisterQueue(this);
+
+ LazyNow lazy_now = time_domain->CreateLazyNow();
+ // Clear scheduled wake up to ensure that new notifications are issued
+ // correctly.
+ // TODO(altimin): Remove this when we won't have to support changing time
+ // domains.
+ main_thread_only().scheduled_wake_up = base::nullopt;
+ UpdateDelayedWakeUp(&lazy_now);
+}
+
+TimeDomain* TaskQueueImpl::GetTimeDomain() const {
+ if (base::PlatformThread::CurrentId() == thread_id_)
+ return main_thread_only().time_domain;
+
+ base::AutoLock lock(any_thread_lock_);
+ return any_thread().time_domain;
+}
+
+void TaskQueueImpl::SetBlameContext(
+ base::trace_event::BlameContext* blame_context) {
+ main_thread_only().blame_context = blame_context;
+}
+
+void TaskQueueImpl::InsertFence(TaskQueue::InsertFencePosition position) {
+ if (!main_thread_only().task_queue_manager)
+ return;
+
+ // Only one fence may be present at a time.
+ main_thread_only().delayed_fence = base::nullopt;
+
+ EnqueueOrder previous_fence = main_thread_only().current_fence;
+ EnqueueOrder current_fence =
+ position == TaskQueue::InsertFencePosition::kNow
+ ? main_thread_only().task_queue_manager->GetNextSequenceNumber()
+ : static_cast<EnqueueOrder>(EnqueueOrderValues::kBlockingFence);
+
+ // Tasks posted after this point will have a strictly higher enqueue order
+ // and will be blocked from running.
+ main_thread_only().current_fence = current_fence;
+ bool task_unblocked =
+ main_thread_only().immediate_work_queue->InsertFence(current_fence);
+ task_unblocked |=
+ main_thread_only().delayed_work_queue->InsertFence(current_fence);
+
+ if (!task_unblocked && previous_fence && previous_fence < current_fence) {
+ base::AutoLock lock(immediate_incoming_queue_lock_);
+ if (!immediate_incoming_queue().empty() &&
+ immediate_incoming_queue().front().enqueue_order() > previous_fence &&
+ immediate_incoming_queue().front().enqueue_order() < current_fence) {
+ task_unblocked = true;
+ }
+ }
+
+ if (IsQueueEnabled() && task_unblocked) {
+ main_thread_only().task_queue_manager->MaybeScheduleImmediateWork(
+ FROM_HERE);
+ }
+}
+
+void TaskQueueImpl::InsertFenceAt(base::TimeTicks time) {
+ // Task queue can have only one fence, delayed or not.
+ RemoveFence();
+ main_thread_only().delayed_fence = time;
+}
+
+void TaskQueueImpl::RemoveFence() {
+ if (!main_thread_only().task_queue_manager)
+ return;
+
+ EnqueueOrder previous_fence = main_thread_only().current_fence;
+ main_thread_only().current_fence = 0;
+ main_thread_only().delayed_fence = base::nullopt;
+
+ bool task_unblocked = main_thread_only().immediate_work_queue->RemoveFence();
+ task_unblocked |= main_thread_only().delayed_work_queue->RemoveFence();
+
+ if (!task_unblocked && previous_fence) {
+ base::AutoLock lock(immediate_incoming_queue_lock_);
+ if (!immediate_incoming_queue().empty() &&
+ immediate_incoming_queue().front().enqueue_order() > previous_fence) {
+ task_unblocked = true;
+ }
+ }
+
+ if (IsQueueEnabled() && task_unblocked) {
+ main_thread_only().task_queue_manager->MaybeScheduleImmediateWork(
+ FROM_HERE);
+ }
+}
+
+bool TaskQueueImpl::BlockedByFence() const {
+ if (!main_thread_only().current_fence)
+ return false;
+
+ if (!main_thread_only().immediate_work_queue->BlockedByFence() ||
+ !main_thread_only().delayed_work_queue->BlockedByFence()) {
+ return false;
+ }
+
+ base::AutoLock lock(immediate_incoming_queue_lock_);
+ if (immediate_incoming_queue().empty())
+ return true;
+
+ return immediate_incoming_queue().front().enqueue_order() >
+ main_thread_only().current_fence;
+}
+
+bool TaskQueueImpl::HasActiveFence() {
+ if (main_thread_only().delayed_fence &&
+ main_thread_only().time_domain->Now() >
+ main_thread_only().delayed_fence.value()) {
+ return true;
+ }
+ return !!main_thread_only().current_fence;
+}
+
+bool TaskQueueImpl::CouldTaskRun(EnqueueOrder enqueue_order) const {
+ if (!IsQueueEnabled())
+ return false;
+
+ if (!main_thread_only().current_fence)
+ return true;
+
+ return enqueue_order < main_thread_only().current_fence;
+}
+
+EnqueueOrder TaskQueueImpl::GetFenceForTest() const {
+ return main_thread_only().current_fence;
+}
+
+// static
+void TaskQueueImpl::QueueAsValueInto(const TaskDeque& queue,
+ base::TimeTicks now,
+ base::trace_event::TracedValue* state) {
+ for (const Task& task : queue) {
+ TaskAsValueInto(task, now, state);
+ }
+}
+
+// static
+void TaskQueueImpl::QueueAsValueInto(const std::priority_queue<Task>& queue,
+ base::TimeTicks now,
+ base::trace_event::TracedValue* state) {
+ // Remove const to search |queue| in the destructive manner. Restore the
+ // content from |visited| later.
+ std::priority_queue<Task>* mutable_queue =
+ const_cast<std::priority_queue<Task>*>(&queue);
+ std::priority_queue<Task> visited;
+ while (!mutable_queue->empty()) {
+ TaskAsValueInto(mutable_queue->top(), now, state);
+ visited.push(std::move(const_cast<Task&>(mutable_queue->top())));
+ mutable_queue->pop();
+ }
+ *mutable_queue = std::move(visited);
+}
+
+// static
+void TaskQueueImpl::TaskAsValueInto(const Task& task,
+ base::TimeTicks now,
+ base::trace_event::TracedValue* state) {
+ state->BeginDictionary();
+ state->SetString("posted_from", task.posted_from.ToString());
+#ifndef NDEBUG
+ if (task.enqueue_order_set())
+ state->SetInteger("enqueue_order", task.enqueue_order());
+#else
+ state->SetInteger("enqueue_order", task.enqueue_order());
+#endif
+ state->SetInteger("sequence_num", task.sequence_num);
+ state->SetBoolean("nestable", task.nestable == base::Nestable::kNestable);
+ state->SetBoolean("is_high_res", task.is_high_res);
+ state->SetBoolean("is_cancelled", task.task.IsCancelled());
+ state->SetDouble(
+ "delayed_run_time",
+ (task.delayed_run_time - base::TimeTicks()).InMillisecondsF());
+ state->SetDouble("delayed_run_time_milliseconds_from_now",
+ (task.delayed_run_time - now).InMillisecondsF());
+ state->EndDictionary();
+}
+
+TaskQueueImpl::QueueEnabledVoterImpl::QueueEnabledVoterImpl(
+ scoped_refptr<TaskQueue> task_queue)
+ : task_queue_(task_queue), enabled_(true) {}
+
+TaskQueueImpl::QueueEnabledVoterImpl::~QueueEnabledVoterImpl() {
+ if (task_queue_->GetTaskQueueImpl())
+ task_queue_->GetTaskQueueImpl()->RemoveQueueEnabledVoter(this);
+}
+
+void TaskQueueImpl::QueueEnabledVoterImpl::SetQueueEnabled(bool enabled) {
+ if (enabled_ == enabled)
+ return;
+
+ task_queue_->GetTaskQueueImpl()->OnQueueEnabledVoteChanged(enabled);
+ enabled_ = enabled;
+}
+
+void TaskQueueImpl::RemoveQueueEnabledVoter(
+ const QueueEnabledVoterImpl* voter) {
+ // Bail out if we're being called from TaskQueueImpl::UnregisterTaskQueue.
+ if (!main_thread_only().time_domain)
+ return;
+
+ bool was_enabled = IsQueueEnabled();
+ if (voter->enabled_) {
+ main_thread_only().is_enabled_refcount--;
+ DCHECK_GE(main_thread_only().is_enabled_refcount, 0);
+ }
+
+ main_thread_only().voter_refcount--;
+ DCHECK_GE(main_thread_only().voter_refcount, 0);
+
+ bool is_enabled = IsQueueEnabled();
+ if (was_enabled != is_enabled)
+ EnableOrDisableWithSelector(is_enabled);
+}
+
+bool TaskQueueImpl::IsQueueEnabled() const {
+ // By default is_enabled_refcount and voter_refcount both equal zero.
+ return (main_thread_only().is_enabled_refcount ==
+ main_thread_only().voter_refcount) &&
+ main_thread_only().is_enabled_for_test;
+}
+
+void TaskQueueImpl::OnQueueEnabledVoteChanged(bool enabled) {
+ bool was_enabled = IsQueueEnabled();
+ if (enabled) {
+ main_thread_only().is_enabled_refcount++;
+ DCHECK_LE(main_thread_only().is_enabled_refcount,
+ main_thread_only().voter_refcount);
+ } else {
+ main_thread_only().is_enabled_refcount--;
+ DCHECK_GE(main_thread_only().is_enabled_refcount, 0);
+ }
+
+ bool is_enabled = IsQueueEnabled();
+ if (was_enabled != is_enabled)
+ EnableOrDisableWithSelector(is_enabled);
+}
+
+void TaskQueueImpl::EnableOrDisableWithSelector(bool enable) {
+ if (!main_thread_only().task_queue_manager)
+ return;
+
+ LazyNow lazy_now = main_thread_only().time_domain->CreateLazyNow();
+ UpdateDelayedWakeUp(&lazy_now);
+
+ if (enable) {
+ if (HasPendingImmediateWork() &&
+ !main_thread_only().on_next_wake_up_changed_callback.is_null()) {
+ // Delayed work notification will be issued via time domain.
+ main_thread_only().on_next_wake_up_changed_callback.Run(
+ base::TimeTicks());
+ }
+
+ // Note the selector calls TaskQueueManager::OnTaskQueueEnabled which posts
+ // a DoWork if needed.
+ main_thread_only()
+ .task_queue_manager->main_thread_only()
+ .selector.EnableQueue(this);
+ } else {
+ main_thread_only()
+ .task_queue_manager->main_thread_only()
+ .selector.DisableQueue(this);
+ }
+}
+
+std::unique_ptr<TaskQueue::QueueEnabledVoter>
+TaskQueueImpl::CreateQueueEnabledVoter(scoped_refptr<TaskQueue> task_queue) {
+ DCHECK_EQ(task_queue->GetTaskQueueImpl(), this);
+ main_thread_only().voter_refcount++;
+ main_thread_only().is_enabled_refcount++;
+ return std::make_unique<QueueEnabledVoterImpl>(task_queue);
+}
+
+void TaskQueueImpl::SweepCanceledDelayedTasks(base::TimeTicks now) {
+ if (main_thread_only().delayed_incoming_queue.empty())
+ return;
+
+ // Remove canceled tasks.
+ std::priority_queue<Task> remaining_tasks;
+ while (!main_thread_only().delayed_incoming_queue.empty()) {
+ if (!main_thread_only().delayed_incoming_queue.top().task.IsCancelled()) {
+ remaining_tasks.push(std::move(
+ const_cast<Task&>(main_thread_only().delayed_incoming_queue.top())));
+ }
+ main_thread_only().delayed_incoming_queue.pop();
+ }
+
+ main_thread_only().delayed_incoming_queue = std::move(remaining_tasks);
+
+ LazyNow lazy_now(now);
+ UpdateDelayedWakeUp(&lazy_now);
+}
+
+void TaskQueueImpl::PushImmediateIncomingTaskForTest(
+ TaskQueueImpl::Task&& task) {
+ base::AutoLock lock(immediate_incoming_queue_lock_);
+ immediate_incoming_queue().push_back(std::move(task));
+}
+
+void TaskQueueImpl::RequeueDeferredNonNestableTask(
+ TaskQueueImpl::Task&& task,
+ SequencedTaskSource::WorkType work_type) {
+ DCHECK(task.nestable == base::Nestable::kNonNestable);
+ // The re-queued tasks have to be pushed onto the front because we'd otherwise
+ // violate the strict monotonically increasing enqueue order within the
+ // WorkQueue. We can't assign them a new enqueue order here because that will
+ // not behave correctly with fences and things will break (e.g Idle TQ).
+ if (work_type == SequencedTaskSource::WorkType::kDelayed) {
+ main_thread_only().delayed_work_queue->PushNonNestableTaskToFront(
+ std::move(task));
+ } else {
+ main_thread_only().immediate_work_queue->PushNonNestableTaskToFront(
+ std::move(task));
+ }
+}
+
+void TaskQueueImpl::SetOnNextWakeUpChangedCallback(
+ TaskQueueImpl::OnNextWakeUpChangedCallback callback) {
+#if DCHECK_IS_ON()
+ if (callback) {
+ DCHECK(main_thread_only().on_next_wake_up_changed_callback.is_null())
+ << "Can't assign two different observers to "
+ "blink::scheduler::TaskQueue";
+ }
+#endif
+ base::AutoLock lock(any_thread_lock_);
+ any_thread().on_next_wake_up_changed_callback = callback;
+ main_thread_only().on_next_wake_up_changed_callback = callback;
+}
+
+void TaskQueueImpl::UpdateDelayedWakeUp(LazyNow* lazy_now) {
+ return UpdateDelayedWakeUpImpl(lazy_now, GetNextScheduledWakeUpImpl());
+}
+
+void TaskQueueImpl::UpdateDelayedWakeUpImpl(
+ LazyNow* lazy_now,
+ base::Optional<TaskQueueImpl::DelayedWakeUp> wake_up) {
+ if (main_thread_only().scheduled_wake_up == wake_up)
+ return;
+ main_thread_only().scheduled_wake_up = wake_up;
+
+ if (wake_up &&
+ !main_thread_only().on_next_wake_up_changed_callback.is_null() &&
+ !HasPendingImmediateWork()) {
+ main_thread_only().on_next_wake_up_changed_callback.Run(wake_up->time);
+ }
+
+ main_thread_only().time_domain->ScheduleWakeUpForQueue(this, wake_up,
+ lazy_now);
+}
+
+void TaskQueueImpl::SetDelayedWakeUpForTesting(
+ base::Optional<TaskQueueImpl::DelayedWakeUp> wake_up) {
+ LazyNow lazy_now = main_thread_only().time_domain->CreateLazyNow();
+ UpdateDelayedWakeUpImpl(&lazy_now, wake_up);
+}
+
+bool TaskQueueImpl::HasPendingImmediateWork() {
+ // Any work queue tasks count as immediate work.
+ if (!main_thread_only().delayed_work_queue->Empty() ||
+ !main_thread_only().immediate_work_queue->Empty()) {
+ return true;
+ }
+
+ // Finally tasks on |immediate_incoming_queue| count as immediate work.
+ base::AutoLock lock(immediate_incoming_queue_lock_);
+ return !immediate_incoming_queue().empty();
+}
+
+void TaskQueueImpl::SetOnTaskStartedHandler(
+ TaskQueueImpl::OnTaskStartedHandler handler) {
+ main_thread_only().on_task_started_handler = std::move(handler);
+}
+
+void TaskQueueImpl::OnTaskStarted(const TaskQueue::Task& task,
+ base::TimeTicks start) {
+ if (!main_thread_only().on_task_started_handler.is_null())
+ main_thread_only().on_task_started_handler.Run(task, start);
+}
+
+void TaskQueueImpl::SetOnTaskCompletedHandler(
+ TaskQueueImpl::OnTaskCompletedHandler handler) {
+ main_thread_only().on_task_completed_handler = std::move(handler);
+}
+
+void TaskQueueImpl::OnTaskCompleted(
+ const TaskQueue::Task& task,
+ base::TimeTicks start,
+ base::TimeTicks end,
+ base::Optional<base::TimeDelta> thread_time) {
+ if (!main_thread_only().on_task_completed_handler.is_null()) {
+ main_thread_only().on_task_completed_handler.Run(task, start, end,
+ thread_time);
+ }
+}
+
+bool TaskQueueImpl::RequiresTaskTiming() const {
+ return !main_thread_only().on_task_started_handler.is_null() ||
+ !main_thread_only().on_task_completed_handler.is_null();
+}
+
+bool TaskQueueImpl::IsUnregistered() const {
+ base::AutoLock lock(any_thread_lock_);
+ return !any_thread().task_queue_manager;
+}
+
+base::WeakPtr<TaskQueueManagerImpl>
+TaskQueueImpl::GetTaskQueueManagerWeakPtr() {
+ return main_thread_only().task_queue_manager->GetWeakPtr();
+}
+
+scoped_refptr<GracefulQueueShutdownHelper>
+TaskQueueImpl::GetGracefulQueueShutdownHelper() {
+ return main_thread_only()
+ .task_queue_manager->GetGracefulQueueShutdownHelper();
+}
+
+void TaskQueueImpl::SetQueueEnabledForTest(bool enabled) {
+ main_thread_only().is_enabled_for_test = enabled;
+ EnableOrDisableWithSelector(IsQueueEnabled());
+}
+
+void TaskQueueImpl::ActivateDelayedFenceIfNeeded(base::TimeTicks now) {
+ if (!main_thread_only().delayed_fence)
+ return;
+ if (main_thread_only().delayed_fence.value() > now)
+ return;
+ InsertFence(TaskQueue::InsertFencePosition::kNow);
+ main_thread_only().delayed_fence = base::nullopt;
+}
+
+} // namespace internal
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_impl.h b/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_impl.h
new file mode 100644
index 00000000000..0e1a62c1a9d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_impl.h
@@ -0,0 +1,451 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TASK_QUEUE_IMPL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TASK_QUEUE_IMPL_H_
+
+#include <stddef.h>
+
+#include <memory>
+#include <set>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/pending_task.h"
+#include "base/threading/thread_checker.h"
+#include "base/trace_event/trace_event.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "third_party/blink/renderer/platform/scheduler/base/enqueue_order.h"
+#include "third_party/blink/renderer/platform/scheduler/base/graceful_queue_shutdown_helper.h"
+#include "third_party/blink/renderer/platform/scheduler/base/intrusive_heap.h"
+#include "third_party/blink/renderer/platform/scheduler/base/sequenced_task_source.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue.h"
+#include "third_party/blink/renderer/platform/wtf/deque.h"
+
+namespace blink {
+namespace scheduler {
+class LazyNow;
+class TimeDomain;
+class TaskQueueManagerImpl;
+
+namespace internal {
+class WorkQueue;
+class WorkQueueSets;
+
+// TaskQueueImpl has four main queues:
+//
+// Immediate (non-delayed) tasks:
+// immediate_incoming_queue - PostTask enqueues tasks here
+// immediate_work_queue
+//
+// Delayed tasks
+// delayed_incoming_queue - PostDelayedTask enqueues tasks here
+// delayed_work_queue
+//
+// The immediate_incoming_queue can be accessed from any thread, the other
+// queues are main-thread only. To reduce the overhead of locking,
+// immediate_work_queue is swapped with immediate_incoming_queue when
+// immediate_work_queue becomes empty.
+//
+// Delayed tasks are initially posted to delayed_incoming_queue and a wake-up
+// is scheduled with the TimeDomain. When the delay has elapsed, the TimeDomain
+// calls UpdateDelayedWorkQueue and ready delayed tasks are moved into the
+// delayed_work_queue. Note the EnqueueOrder (used for ordering) for a delayed
+// task is not set until it's moved into the delayed_work_queue.
+//
+// TaskQueueImpl uses the WorkQueueSets and the TaskQueueSelector to implement
+// prioritization. Task selection is done by the TaskQueueSelector and when a
+// queue is selected, it round-robins between the immediate_work_queue and
+// delayed_work_queue. The reason for this is we want to make sure delayed
+// tasks (normally the most common type) don't starve out immediate work.
+class PLATFORM_EXPORT TaskQueueImpl {
+ public:
+ TaskQueueImpl(TaskQueueManagerImpl* task_queue_manager,
+ TimeDomain* time_domain,
+ const TaskQueue::Spec& spec);
+
+ ~TaskQueueImpl();
+
+ // Represents a time at which a task wants to run. Tasks scheduled for the
+ // same point in time will be ordered by their sequence numbers.
+ struct DelayedWakeUp {
+ base::TimeTicks time;
+ int sequence_num;
+
+ bool operator!=(const DelayedWakeUp& other) const {
+ return time != other.time || other.sequence_num != sequence_num;
+ }
+
+ bool operator==(const DelayedWakeUp& other) const {
+ return !(*this != other);
+ }
+
+ bool operator<=(const DelayedWakeUp& other) const {
+ if (time == other.time) {
+ // Debug gcc builds can compare an element against itself.
+ DCHECK(sequence_num != other.sequence_num || this == &other);
+ return (sequence_num - other.sequence_num) < 0;
+ }
+ return time < other.time;
+ }
+ };
+
+ class PLATFORM_EXPORT Task : public TaskQueue::Task {
+ public:
+ Task(TaskQueue::PostedTask task,
+ base::TimeTicks desired_run_time,
+ EnqueueOrder sequence_number);
+
+ Task(TaskQueue::PostedTask task,
+ base::TimeTicks desired_run_time,
+ EnqueueOrder sequence_number,
+ EnqueueOrder enqueue_order);
+
+ DelayedWakeUp delayed_wake_up() const {
+ return DelayedWakeUp{delayed_run_time, sequence_num};
+ }
+
+ EnqueueOrder enqueue_order() const {
+#ifndef NDEBUG
+ DCHECK(enqueue_order_set_);
+#endif
+ return enqueue_order_;
+ }
+
+ void set_enqueue_order(EnqueueOrder enqueue_order) {
+#ifndef NDEBUG
+ DCHECK(!enqueue_order_set_);
+ enqueue_order_set_ = true;
+#endif
+ enqueue_order_ = enqueue_order;
+ }
+
+#ifndef NDEBUG
+ bool enqueue_order_set() const { return enqueue_order_set_; }
+#endif
+
+ private:
+#ifndef NDEBUG
+ bool enqueue_order_set_;
+#endif
+ // Similar to sequence number, but ultimately the |enqueue_order_| is what
+ // the scheduler uses for task ordering. For immediate tasks |enqueue_order|
+ // is set when posted, but for delayed tasks it's not defined until they are
+ // enqueued on the |delayed_work_queue_|. This is because otherwise delayed
+ // tasks could run before an immediate task posted after the delayed task.
+ EnqueueOrder enqueue_order_;
+ };
+
+ // A result retuned by PostDelayedTask. When scheduler failed to post a task
+ // due to being shutdown a task is returned to be destroyed outside the lock.
+ struct PostTaskResult {
+ PostTaskResult();
+ PostTaskResult(bool success, TaskQueue::PostedTask task);
+
+ static PostTaskResult Success();
+ static PostTaskResult Fail(TaskQueue::PostedTask task);
+
+ bool success = false;
+ TaskQueue::PostedTask task;
+ };
+
+ using OnNextWakeUpChangedCallback =
+ base::RepeatingCallback<void(base::TimeTicks)>;
+ using OnTaskStartedHandler =
+ base::RepeatingCallback<void(const TaskQueue::Task&, base::TimeTicks)>;
+ using OnTaskCompletedHandler =
+ base::RepeatingCallback<void(const TaskQueue::Task&,
+ base::TimeTicks,
+ base::TimeTicks,
+ base::Optional<base::TimeDelta>)>;
+
+ // TaskQueue implementation.
+ const char* GetName() const;
+ bool RunsTasksInCurrentSequence() const;
+ PostTaskResult PostDelayedTask(TaskQueue::PostedTask task);
+ // Require a reference to enclosing task queue for lifetime control.
+ std::unique_ptr<TaskQueue::QueueEnabledVoter> CreateQueueEnabledVoter(
+ scoped_refptr<TaskQueue> owning_task_queue);
+ bool IsQueueEnabled() const;
+ bool IsEmpty() const;
+ size_t GetNumberOfPendingTasks() const;
+ bool HasTaskToRunImmediately() const;
+ base::Optional<base::TimeTicks> GetNextScheduledWakeUp();
+ base::Optional<DelayedWakeUp> GetNextScheduledWakeUpImpl();
+ void SetQueuePriority(TaskQueue::QueuePriority priority);
+ TaskQueue::QueuePriority GetQueuePriority() const;
+ void AddTaskObserver(base::MessageLoop::TaskObserver* task_observer);
+ void RemoveTaskObserver(base::MessageLoop::TaskObserver* task_observer);
+ void SetTimeDomain(TimeDomain* time_domain);
+ TimeDomain* GetTimeDomain() const;
+ void SetBlameContext(base::trace_event::BlameContext* blame_context);
+ void InsertFence(TaskQueue::InsertFencePosition position);
+ void InsertFenceAt(base::TimeTicks time);
+ void RemoveFence();
+ bool HasActiveFence();
+ bool BlockedByFence() const;
+ // Implementation of TaskQueue::SetObserver.
+ void SetOnNextWakeUpChangedCallback(OnNextWakeUpChangedCallback callback);
+
+ void UnregisterTaskQueue();
+
+ // Returns true if a (potentially hypothetical) task with the specified
+ // |enqueue_order| could run on the queue. Must be called from the main
+ // thread.
+ bool CouldTaskRun(EnqueueOrder enqueue_order) const;
+
+ // Must only be called from the thread this task queue was created on.
+ void ReloadImmediateWorkQueueIfEmpty();
+
+ void AsValueInto(base::TimeTicks now,
+ base::trace_event::TracedValue* state) const;
+
+ bool GetQuiescenceMonitored() const { return should_monitor_quiescence_; }
+ bool GetShouldNotifyObservers() const { return should_notify_observers_; }
+
+ void NotifyWillProcessTask(const base::PendingTask& pending_task);
+ void NotifyDidProcessTask(const base::PendingTask& pending_task);
+
+ // Check for available tasks in immediate work queues.
+ // Used to check if we need to generate notifications about delayed work.
+ bool HasPendingImmediateWork();
+
+ WorkQueue* delayed_work_queue() {
+ return main_thread_only().delayed_work_queue.get();
+ }
+
+ const WorkQueue* delayed_work_queue() const {
+ return main_thread_only().delayed_work_queue.get();
+ }
+
+ WorkQueue* immediate_work_queue() {
+ return main_thread_only().immediate_work_queue.get();
+ }
+
+ const WorkQueue* immediate_work_queue() const {
+ return main_thread_only().immediate_work_queue.get();
+ }
+
+ // Enqueues any delayed tasks which should be run now on the
+ // |delayed_work_queue|.
+ // Must be called from the main thread.
+ void WakeUpForDelayedWork(LazyNow* lazy_now);
+
+ HeapHandle heap_handle() const { return main_thread_only().heap_handle; }
+
+ void set_heap_handle(HeapHandle heap_handle) {
+ main_thread_only().heap_handle = heap_handle;
+ }
+
+ // Pushes |task| onto the front of the specified work queue. Caution must be
+ // taken with this API because you could easily starve out other work.
+ void RequeueDeferredNonNestableTask(TaskQueueImpl::Task&& task,
+ SequencedTaskSource::WorkType work_type);
+
+ void PushImmediateIncomingTaskForTest(TaskQueueImpl::Task&& task);
+ EnqueueOrder GetFenceForTest() const;
+
+ class QueueEnabledVoterImpl : public TaskQueue::QueueEnabledVoter {
+ public:
+ explicit QueueEnabledVoterImpl(scoped_refptr<TaskQueue> task_queue);
+ ~QueueEnabledVoterImpl() override;
+
+ // QueueEnabledVoter implementation.
+ void SetQueueEnabled(bool enabled) override;
+
+ TaskQueueImpl* GetTaskQueueForTest() const {
+ return task_queue_->GetTaskQueueImpl();
+ }
+
+ private:
+ friend class TaskQueueImpl;
+
+ scoped_refptr<TaskQueue> task_queue_;
+ bool enabled_;
+ };
+
+ // Iterates over |delayed_incoming_queue| removing canceled tasks.
+ void SweepCanceledDelayedTasks(base::TimeTicks now);
+
+ // Allows wrapping TaskQueue to set a handler to subscribe for notifications
+ // about started and completed tasks.
+ void SetOnTaskStartedHandler(OnTaskStartedHandler handler);
+ void OnTaskStarted(const TaskQueue::Task& task, base::TimeTicks start);
+ void SetOnTaskCompletedHandler(OnTaskCompletedHandler handler);
+ void OnTaskCompleted(const TaskQueue::Task& task,
+ base::TimeTicks start,
+ base::TimeTicks end,
+ base::Optional<base::TimeDelta> thread_time);
+ bool RequiresTaskTiming() const;
+
+ base::WeakPtr<TaskQueueManagerImpl> GetTaskQueueManagerWeakPtr();
+
+ scoped_refptr<GracefulQueueShutdownHelper> GetGracefulQueueShutdownHelper();
+
+ // Returns true if this queue is unregistered or task queue manager is deleted
+ // and this queue can be safely deleted on any thread.
+ bool IsUnregistered() const;
+
+ // Disables queue for testing purposes, when a QueueEnabledVoter can't be
+ // constructed due to not having TaskQueue.
+ void SetQueueEnabledForTest(bool enabled);
+
+ protected:
+ void SetDelayedWakeUpForTesting(base::Optional<DelayedWakeUp> wake_up);
+
+ private:
+ friend class WorkQueue;
+ friend class WorkQueueTest;
+
+ struct AnyThread {
+ AnyThread(TaskQueueManagerImpl* task_queue_manager,
+ TimeDomain* time_domain);
+ ~AnyThread();
+
+ // TaskQueueManagerImpl, TimeDomain and Observer are maintained in two
+ // copies: inside AnyThread and inside MainThreadOnly. They can be changed
+ // only from main thread, so it should be locked before accessing from other
+ // threads.
+ TaskQueueManagerImpl* task_queue_manager;
+ TimeDomain* time_domain;
+ // Callback corresponding to TaskQueue::Observer::OnQueueNextChanged.
+ OnNextWakeUpChangedCallback on_next_wake_up_changed_callback;
+ };
+
+ struct MainThreadOnly {
+ MainThreadOnly(TaskQueueManagerImpl* task_queue_manager,
+ TaskQueueImpl* task_queue,
+ TimeDomain* time_domain);
+ ~MainThreadOnly();
+
+ // Another copy of TaskQueueManagerImpl, TimeDomain and Observer
+ // for lock-free access from the main thread.
+ // See description inside struct AnyThread for details.
+ TaskQueueManagerImpl* task_queue_manager;
+ TimeDomain* time_domain;
+ // Callback corresponding to TaskQueue::Observer::OnQueueNextChanged.
+ OnNextWakeUpChangedCallback on_next_wake_up_changed_callback;
+
+ std::unique_ptr<WorkQueue> delayed_work_queue;
+ std::unique_ptr<WorkQueue> immediate_work_queue;
+ std::priority_queue<Task> delayed_incoming_queue;
+ base::ObserverList<base::MessageLoop::TaskObserver> task_observers;
+ size_t set_index;
+ HeapHandle heap_handle;
+ int is_enabled_refcount;
+ int voter_refcount;
+ base::trace_event::BlameContext* blame_context; // Not owned.
+ EnqueueOrder current_fence;
+ base::Optional<base::TimeTicks> delayed_fence;
+ OnTaskStartedHandler on_task_started_handler;
+ OnTaskCompletedHandler on_task_completed_handler;
+ // Last reported wake up, used only in UpdateWakeUp to avoid
+ // excessive calls.
+ base::Optional<DelayedWakeUp> scheduled_wake_up;
+ // If false, queue will be disabled. Used only for tests.
+ bool is_enabled_for_test;
+ };
+
+ PostTaskResult PostImmediateTaskImpl(TaskQueue::PostedTask task);
+ PostTaskResult PostDelayedTaskImpl(TaskQueue::PostedTask task);
+
+ // Push the task onto the |delayed_incoming_queue|. Lock-free main thread
+ // only fast path.
+ void PushOntoDelayedIncomingQueueFromMainThread(Task pending_task,
+ base::TimeTicks now);
+
+ // Push the task onto the |delayed_incoming_queue|. Slow path from other
+ // threads.
+ void PushOntoDelayedIncomingQueueLocked(Task pending_task);
+
+ void ScheduleDelayedWorkTask(Task pending_task);
+
+ void MoveReadyImmediateTasksToImmediateWorkQueueLocked();
+
+ // Push the task onto the |immediate_incoming_queue| and for auto pumped
+ // queues it calls MaybePostDoWorkOnMainRunner if the Incoming queue was
+ // empty.
+ void PushOntoImmediateIncomingQueueLocked(Task task);
+
+ // We reserve an inline capacity of 8 tasks to try and reduce the load on
+ // PartitionAlloc.
+ using TaskDeque = WTF::Deque<Task, 8>;
+
+ // Extracts all the tasks from the immediate incoming queue and clears it.
+ // Can be called from any thread.
+ TaskDeque TakeImmediateIncomingQueue();
+
+ void TraceQueueSize() const;
+ static void QueueAsValueInto(const TaskDeque& queue,
+ base::TimeTicks now,
+ base::trace_event::TracedValue* state);
+ static void QueueAsValueInto(const std::priority_queue<Task>& queue,
+ base::TimeTicks now,
+ base::trace_event::TracedValue* state);
+ static void TaskAsValueInto(const Task& task,
+ base::TimeTicks now,
+ base::trace_event::TracedValue* state);
+
+ void RemoveQueueEnabledVoter(const QueueEnabledVoterImpl* voter);
+ void OnQueueEnabledVoteChanged(bool enabled);
+ void EnableOrDisableWithSelector(bool enable);
+
+ // Schedules delayed work on time domain and calls the observer.
+ void UpdateDelayedWakeUp(LazyNow* lazy_now);
+ void UpdateDelayedWakeUpImpl(LazyNow* lazy_now,
+ base::Optional<DelayedWakeUp> wake_up);
+
+ // Activate a delayed fence if a time has come.
+ void ActivateDelayedFenceIfNeeded(base::TimeTicks now);
+
+ const char* name_;
+
+ const base::PlatformThreadId thread_id_;
+
+ mutable base::Lock any_thread_lock_;
+ AnyThread any_thread_;
+ struct AnyThread& any_thread() {
+ any_thread_lock_.AssertAcquired();
+ return any_thread_;
+ }
+ const struct AnyThread& any_thread() const {
+ any_thread_lock_.AssertAcquired();
+ return any_thread_;
+ }
+
+ base::ThreadChecker main_thread_checker_;
+ MainThreadOnly main_thread_only_;
+ MainThreadOnly& main_thread_only() {
+ DCHECK(main_thread_checker_.CalledOnValidThread());
+ return main_thread_only_;
+ }
+ const MainThreadOnly& main_thread_only() const {
+ DCHECK(main_thread_checker_.CalledOnValidThread());
+ return main_thread_only_;
+ }
+
+ mutable base::Lock immediate_incoming_queue_lock_;
+ TaskDeque immediate_incoming_queue_;
+ TaskDeque& immediate_incoming_queue() {
+ immediate_incoming_queue_lock_.AssertAcquired();
+ return immediate_incoming_queue_;
+ }
+ const TaskDeque& immediate_incoming_queue() const {
+ immediate_incoming_queue_lock_.AssertAcquired();
+ return immediate_incoming_queue_;
+ }
+
+ const bool should_monitor_quiescence_;
+ const bool should_notify_observers_;
+
+ DISALLOW_COPY_AND_ASSIGN(TaskQueueImpl);
+};
+
+} // namespace internal
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TASK_QUEUE_IMPL_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_manager.h b/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_manager.h
new file mode 100644
index 00000000000..77fecacb9b4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_manager.h
@@ -0,0 +1,103 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TASK_QUEUE_MANAGER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TASK_QUEUE_MANAGER_H_
+
+#include "base/message_loop/message_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/scheduler/base/real_time_domain.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_time_observer.h"
+#include "third_party/blink/renderer/platform/scheduler/base/time_domain.h"
+
+namespace blink {
+namespace scheduler {
+
+class PLATFORM_EXPORT TaskQueueManager {
+ public:
+ // Keep TaskQueueManagerImpl in sync with this interface.
+ // The general rule is not to expose methods only used in scheduler/base.
+ // Try to keep interface as lean as possible.
+
+ // Observer class. Always called back on the main thread.
+ class PLATFORM_EXPORT Observer {
+ public:
+ virtual ~Observer() {}
+ virtual void OnBeginNestedRunLoop() = 0;
+ virtual void OnExitNestedRunLoop() = 0;
+ };
+
+ virtual ~TaskQueueManager() = default;
+
+ // Forwards to TaskQueueManagerImpl::TakeOverCurrentThread.
+ // TODO(kraynov): Any way to make it truly agnostic of TaskQueueManagerImpl?
+ static std::unique_ptr<TaskQueueManager> TakeOverCurrentThread();
+
+ // Should be called once, on main thread only.
+ // If |null| is passed, no callbacks will occur.
+ // Note: |observer| is expected to outlive the SchedulerHelper.
+ // TODO(kraynov): Review these lifetime assumptions.
+ virtual void SetObserver(Observer* observer) = 0;
+
+ // These functions can only be called on the same thread that the task queue
+ // manager executes its tasks on.
+ virtual void AddTaskObserver(
+ base::MessageLoop::TaskObserver* task_observer) = 0;
+ virtual void RemoveTaskObserver(
+ base::MessageLoop::TaskObserver* task_observer) = 0;
+ virtual void AddTaskTimeObserver(TaskTimeObserver* task_time_observer) = 0;
+ virtual void RemoveTaskTimeObserver(TaskTimeObserver* task_time_observer) = 0;
+
+ // Time domains must be registered for the task queues to get updated.
+ virtual void RegisterTimeDomain(TimeDomain* time_domain) = 0;
+ virtual void UnregisterTimeDomain(TimeDomain* time_domain) = 0;
+ virtual RealTimeDomain* GetRealTimeDomain() const = 0;
+
+ virtual const base::TickClock* GetClock() const = 0;
+ virtual base::TimeTicks NowTicks() const = 0;
+
+ // Sets the SingleThreadTaskRunner that will be returned by
+ // ThreadTaskRunnerHandle::Get and MessageLoop::current().task_runner() on the
+ // thread associated with this TaskQueueManager.
+ virtual void SetDefaultTaskRunner(
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner) = 0;
+
+ // Removes all canceled delayed tasks.
+ virtual void SweepCanceledDelayedTasks() = 0;
+
+ // Returns true if any task from a monitored task queue was was run since the
+ // last call to GetAndClearSystemIsQuiescentBit.
+ virtual bool GetAndClearSystemIsQuiescentBit() = 0;
+
+ // Set the number of tasks executed in a single invocation of the task queue
+ // manager. Increasing the batch size can reduce the overhead of yielding
+ // back to the main message loop -- at the cost of potentially delaying other
+ // tasks posted to the main loop. The batch size is 1 by default.
+ virtual void SetWorkBatchSize(int work_batch_size) = 0;
+
+ virtual void EnableCrashKeys(const char* file_name_crash_key,
+ const char* function_name_crash_key) = 0;
+
+ // Creates a task queue with the given type, |spec| and args. Must be called
+ // on the thread this class was created on.
+ // TODO(altimin): TaskQueueManager should not create TaskQueues.
+ template <typename TaskQueueType, typename... Args>
+ scoped_refptr<TaskQueueType> CreateTaskQueue(const TaskQueue::Spec& spec,
+ Args&&... args) {
+ scoped_refptr<TaskQueueType> task_queue(new TaskQueueType(
+ CreateTaskQueueImpl(spec), spec, std::forward<Args>(args)...));
+ return task_queue;
+ }
+
+ protected:
+ virtual std::unique_ptr<internal::TaskQueueImpl> CreateTaskQueueImpl(
+ const TaskQueue::Spec& spec) = 0;
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TASK_QUEUE_MANAGER_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_manager_delegate_for_test.cc b/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_manager_delegate_for_test.cc
new file mode 100644
index 00000000000..e24c8827fdb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_manager_delegate_for_test.cc
@@ -0,0 +1,65 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_manager_delegate_for_test.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+
+namespace blink {
+namespace scheduler {
+
+// static
+scoped_refptr<TaskQueueManagerDelegateForTest>
+TaskQueueManagerDelegateForTest::Create(
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+ const base::TickClock* time_source) {
+ return base::WrapRefCounted(
+ new TaskQueueManagerDelegateForTest(task_runner, time_source));
+}
+
+TaskQueueManagerDelegateForTest::TaskQueueManagerDelegateForTest(
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+ const base::TickClock* time_source)
+ : task_runner_(task_runner), time_source_(time_source) {}
+
+TaskQueueManagerDelegateForTest::~TaskQueueManagerDelegateForTest() {}
+
+bool TaskQueueManagerDelegateForTest::PostDelayedTask(
+ const base::Location& from_here,
+ base::OnceClosure task,
+ base::TimeDelta delay) {
+ return task_runner_->PostDelayedTask(from_here, std::move(task), delay);
+}
+
+bool TaskQueueManagerDelegateForTest::PostNonNestableDelayedTask(
+ const base::Location& from_here,
+ base::OnceClosure task,
+ base::TimeDelta delay) {
+ return task_runner_->PostNonNestableDelayedTask(from_here, std::move(task),
+ delay);
+}
+
+bool TaskQueueManagerDelegateForTest::RunsTasksInCurrentSequence() const {
+ return task_runner_->RunsTasksInCurrentSequence();
+}
+
+bool TaskQueueManagerDelegateForTest::IsNested() const {
+ return false;
+}
+
+void TaskQueueManagerDelegateForTest::AddNestingObserver(
+ base::RunLoop::NestingObserver* observer) {}
+
+void TaskQueueManagerDelegateForTest::RemoveNestingObserver(
+ base::RunLoop::NestingObserver* observer) {}
+
+base::TimeTicks TaskQueueManagerDelegateForTest::NowTicks() const {
+ return time_source_->NowTicks();
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_manager_delegate_for_test.h b/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_manager_delegate_for_test.h
new file mode 100644
index 00000000000..d448f792c3e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_manager_delegate_for_test.h
@@ -0,0 +1,59 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TASK_QUEUE_MANAGER_DELEGATE_FOR_TEST_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TASK_QUEUE_MANAGER_DELEGATE_FOR_TEST_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/time/tick_clock.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_manager_delegate.h"
+
+namespace blink {
+namespace scheduler {
+
+class TaskQueueManagerDelegateForTest : public TaskQueueManagerDelegate {
+ public:
+ static scoped_refptr<TaskQueueManagerDelegateForTest> Create(
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+ const base::TickClock* time_source);
+
+ // SingleThreadTaskRunner:
+ bool PostDelayedTask(const base::Location& from_here,
+ base::OnceClosure task,
+ base::TimeDelta delay) override;
+ bool PostNonNestableDelayedTask(const base::Location& from_here,
+ base::OnceClosure task,
+ base::TimeDelta delay) override;
+ bool RunsTasksInCurrentSequence() const override;
+
+ // TaskQueueManagerDelegate:
+ bool IsNested() const override;
+ void AddNestingObserver(base::RunLoop::NestingObserver* observer) override;
+ void RemoveNestingObserver(base::RunLoop::NestingObserver* observer) override;
+
+ // TickClock:
+ base::TimeTicks NowTicks() const override;
+
+ protected:
+ ~TaskQueueManagerDelegateForTest() override;
+ TaskQueueManagerDelegateForTest(
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+ const base::TickClock* time_source);
+
+ private:
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ const base::TickClock* time_source_;
+
+ DISALLOW_COPY_AND_ASSIGN(TaskQueueManagerDelegateForTest);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TASK_QUEUE_MANAGER_DELEGATE_FOR_TEST_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_manager_impl.cc b/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_manager_impl.cc
new file mode 100644
index 00000000000..5c7b2968477
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_manager_impl.cc
@@ -0,0 +1,669 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_manager_impl.h"
+
+#include <memory>
+#include <queue>
+#include <set>
+
+#include "base/bind.h"
+#include "base/bit_cast.h"
+#include "base/compiler_specific.h"
+#include "base/debug/crash_logging.h"
+#include "base/rand_util.h"
+#include "base/time/default_tick_clock.h"
+#include "base/time/tick_clock.h"
+#include "base/trace_event/trace_event.h"
+#include "third_party/blink/renderer/platform/scheduler/base/real_time_domain.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_selector.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_time_observer.h"
+#include "third_party/blink/renderer/platform/scheduler/base/thread_controller.h"
+#include "third_party/blink/renderer/platform/scheduler/base/thread_controller_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/base/work_queue.h"
+#include "third_party/blink/renderer/platform/scheduler/base/work_queue_sets.h"
+
+namespace blink {
+namespace scheduler {
+
+namespace {
+
+const double kLongTaskTraceEventThreshold = 0.05;
+const double kSamplingRateForRecordingCPUTime = 0.01;
+
+double MonotonicTimeInSeconds(base::TimeTicks time_ticks) {
+ return (time_ticks - base::TimeTicks()).InSecondsF();
+}
+
+// Magic value to protect against memory corruption and bail out
+// early when detected.
+constexpr int kMemoryCorruptionSentinelValue = 0xdeadbeef;
+
+void SweepCanceledDelayedTasksInQueue(
+ internal::TaskQueueImpl* queue,
+ std::map<TimeDomain*, base::TimeTicks>* time_domain_now) {
+ TimeDomain* time_domain = queue->GetTimeDomain();
+ if (time_domain_now->find(time_domain) == time_domain_now->end())
+ time_domain_now->insert(std::make_pair(time_domain, time_domain->Now()));
+ queue->SweepCanceledDelayedTasks(time_domain_now->at(time_domain));
+}
+
+} // namespace
+
+// static
+std::unique_ptr<TaskQueueManager> TaskQueueManager::TakeOverCurrentThread() {
+ return TaskQueueManagerImpl::TakeOverCurrentThread();
+}
+
+TaskQueueManagerImpl::TaskQueueManagerImpl(
+ std::unique_ptr<internal::ThreadController> controller)
+ : graceful_shutdown_helper_(new internal::GracefulQueueShutdownHelper()),
+ controller_(std::move(controller)),
+ memory_corruption_sentinel_(kMemoryCorruptionSentinelValue),
+ weak_factory_(this) {
+ // TODO(altimin): Create a sequence checker here.
+ DCHECK(controller_->RunsTasksInCurrentSequence());
+ TRACE_EVENT_OBJECT_CREATED_WITH_ID(
+ TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "TaskQueueManager",
+ this);
+ main_thread_only().selector.SetTaskQueueSelectorObserver(this);
+
+ RegisterTimeDomain(main_thread_only().real_time_domain.get());
+
+ controller_->SetSequencedTaskSource(this);
+ controller_->AddNestingObserver(this);
+}
+
+TaskQueueManagerImpl::~TaskQueueManagerImpl() {
+ TRACE_EVENT_OBJECT_DELETED_WITH_ID(
+ TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "TaskQueueManager",
+ this);
+
+ // TODO(altimin): restore default task runner automatically when
+ // ThreadController is destroyed.
+ controller_->RestoreDefaultTaskRunner();
+
+ for (internal::TaskQueueImpl* queue : main_thread_only().active_queues) {
+ main_thread_only().selector.RemoveQueue(queue);
+ queue->UnregisterTaskQueue();
+ }
+
+ main_thread_only().active_queues.clear();
+ main_thread_only().queues_to_gracefully_shutdown.clear();
+
+ graceful_shutdown_helper_->OnTaskQueueManagerDeleted();
+
+ main_thread_only().selector.SetTaskQueueSelectorObserver(nullptr);
+ controller_->RemoveNestingObserver(this);
+}
+
+TaskQueueManagerImpl::MainThreadOnly::MainThreadOnly()
+ : random_generator(base::RandUint64()),
+ uniform_distribution(0.0, 1.0),
+ real_time_domain(new RealTimeDomain()) {}
+
+std::unique_ptr<TaskQueueManagerImpl>
+TaskQueueManagerImpl::TakeOverCurrentThread() {
+ return std::unique_ptr<TaskQueueManagerImpl>(
+ new TaskQueueManagerImpl(internal::ThreadControllerImpl::Create(
+ base::MessageLoop::current(),
+ base::DefaultTickClock::GetInstance())));
+}
+
+void TaskQueueManagerImpl::RegisterTimeDomain(TimeDomain* time_domain) {
+ main_thread_only().time_domains.insert(time_domain);
+ time_domain->OnRegisterWithTaskQueueManager(this);
+}
+
+void TaskQueueManagerImpl::UnregisterTimeDomain(TimeDomain* time_domain) {
+ main_thread_only().time_domains.erase(time_domain);
+}
+
+RealTimeDomain* TaskQueueManagerImpl::GetRealTimeDomain() const {
+ return main_thread_only().real_time_domain.get();
+}
+
+std::unique_ptr<internal::TaskQueueImpl>
+TaskQueueManagerImpl::CreateTaskQueueImpl(const TaskQueue::Spec& spec) {
+ DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
+ TimeDomain* time_domain = spec.time_domain
+ ? spec.time_domain
+ : main_thread_only().real_time_domain.get();
+ DCHECK(main_thread_only().time_domains.find(time_domain) !=
+ main_thread_only().time_domains.end());
+ std::unique_ptr<internal::TaskQueueImpl> task_queue =
+ std::make_unique<internal::TaskQueueImpl>(this, time_domain, spec);
+ main_thread_only().active_queues.insert(task_queue.get());
+ main_thread_only().selector.AddQueue(task_queue.get());
+ return task_queue;
+}
+
+void TaskQueueManagerImpl::SetObserver(Observer* observer) {
+ main_thread_only().observer = observer;
+}
+
+void TaskQueueManagerImpl::UnregisterTaskQueueImpl(
+ std::unique_ptr<internal::TaskQueueImpl> task_queue) {
+ TRACE_EVENT1("renderer.scheduler",
+ "TaskQueueManagerImpl::UnregisterTaskQueue", "queue_name",
+ task_queue->GetName());
+ DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
+
+ main_thread_only().selector.RemoveQueue(task_queue.get());
+
+ {
+ base::AutoLock lock(any_thread_lock_);
+ any_thread().has_incoming_immediate_work.erase(task_queue.get());
+ }
+
+ task_queue->UnregisterTaskQueue();
+
+ // Add |task_queue| to |main_thread_only().queues_to_delete| so we can prevent
+ // it from being freed while any of our structures hold hold a raw pointer to
+ // it.
+ main_thread_only().active_queues.erase(task_queue.get());
+ main_thread_only().queues_to_delete[task_queue.get()] = std::move(task_queue);
+}
+
+void TaskQueueManagerImpl::ReloadEmptyWorkQueues(
+ const IncomingImmediateWorkMap& queues_to_reload) const {
+ // There are two cases where a queue needs reloading. First, it might be
+ // completely empty and we've just posted a task (this method handles that
+ // case). Secondly if the work queue becomes empty in when calling
+ // WorkQueue::TakeTaskFromWorkQueue (handled there).
+ for (const auto& pair : queues_to_reload) {
+ pair.first->ReloadImmediateWorkQueueIfEmpty();
+ }
+}
+
+void TaskQueueManagerImpl::WakeUpReadyDelayedQueues(LazyNow* lazy_now) {
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "TaskQueueManagerImpl::WakeUpReadyDelayedQueues");
+
+ for (TimeDomain* time_domain : main_thread_only().time_domains) {
+ if (time_domain == main_thread_only().real_time_domain.get()) {
+ time_domain->WakeUpReadyDelayedQueues(lazy_now);
+ } else {
+ LazyNow time_domain_lazy_now = time_domain->CreateLazyNow();
+ time_domain->WakeUpReadyDelayedQueues(&time_domain_lazy_now);
+ }
+ }
+}
+
+void TaskQueueManagerImpl::OnBeginNestedRunLoop() {
+ // We just entered a nested run loop, make sure there's a DoWork posted or
+ // the system will grind to a halt.
+ main_thread_only().nesting_depth++;
+ if (main_thread_only().observer && main_thread_only().nesting_depth == 1)
+ main_thread_only().observer->OnBeginNestedRunLoop();
+}
+
+void TaskQueueManagerImpl::OnExitNestedRunLoop() {
+ main_thread_only().nesting_depth--;
+ DCHECK_GE(main_thread_only().nesting_depth, 0);
+ if (main_thread_only().nesting_depth == 0) {
+ // While we were nested some non-nestable tasks may have become eligible to
+ // run. We push them back onto the front of their original work queues.
+ while (!main_thread_only().non_nestable_task_queue.empty()) {
+ NonNestableTask& non_nestable_task =
+ *main_thread_only().non_nestable_task_queue.begin();
+ non_nestable_task.task_queue->RequeueDeferredNonNestableTask(
+ std::move(non_nestable_task.task), non_nestable_task.work_type);
+ main_thread_only().non_nestable_task_queue.pop_front();
+ }
+ if (main_thread_only().observer)
+ main_thread_only().observer->OnExitNestedRunLoop();
+ }
+}
+
+void TaskQueueManagerImpl::OnQueueHasIncomingImmediateWork(
+ internal::TaskQueueImpl* queue,
+ internal::EnqueueOrder enqueue_order,
+ bool queue_is_blocked) {
+ {
+ base::AutoLock lock(any_thread_lock_);
+ any_thread().has_incoming_immediate_work.insert(
+ std::make_pair(queue, enqueue_order));
+ }
+
+ if (!queue_is_blocked)
+ controller_->ScheduleWork();
+}
+
+void TaskQueueManagerImpl::MaybeScheduleImmediateWork(
+ const base::Location& from_here) {
+ controller_->ScheduleWork();
+}
+
+void TaskQueueManagerImpl::MaybeScheduleDelayedWork(
+ const base::Location& from_here,
+ TimeDomain* requesting_time_domain,
+ base::TimeTicks now,
+ base::TimeTicks run_time) {
+ controller_->ScheduleDelayedWork(now, run_time);
+}
+
+void TaskQueueManagerImpl::CancelDelayedWork(TimeDomain* requesting_time_domain,
+ base::TimeTicks run_time) {
+ controller_->CancelDelayedWork(run_time);
+}
+
+base::Optional<base::PendingTask> TaskQueueManagerImpl::TakeTask() {
+ CHECK(Validate());
+
+ DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
+ TRACE_EVENT0("renderer.scheduler", "TaskQueueManagerImpl::TakeTask");
+
+ IncomingImmediateWorkMap queues_to_reload;
+
+ {
+ base::AutoLock lock(any_thread_lock_);
+ std::swap(queues_to_reload, any_thread().has_incoming_immediate_work);
+ }
+
+ // It's important we call ReloadEmptyWorkQueues out side of the lock to
+ // avoid a lock order inversion.
+ ReloadEmptyWorkQueues(queues_to_reload);
+ LazyNow lazy_now(main_thread_only().real_time_domain->CreateLazyNow());
+ WakeUpReadyDelayedQueues(&lazy_now);
+
+ while (true) {
+ internal::WorkQueue* work_queue = nullptr;
+ bool should_run =
+ main_thread_only().selector.SelectWorkQueueToService(&work_queue);
+ TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
+ TRACE_DISABLED_BY_DEFAULT("renderer.scheduler.debug"),
+ "TaskQueueManager", this,
+ AsValueWithSelectorResult(should_run, work_queue));
+
+ if (!should_run)
+ return base::nullopt;
+
+ // If the head task was canceled, remove it and run the selector again.
+ if (work_queue->RemoveAllCanceledTasksFromFront())
+ continue;
+
+ if (work_queue->GetFrontTask()->nestable == base::Nestable::kNonNestable &&
+ main_thread_only().nesting_depth > 0) {
+ // Defer non-nestable work. NOTE these tasks can be arbitrarily delayed so
+ // the additional delay should not be a problem.
+ // Note because we don't delete queues while nested, it's perfectly OK to
+ // store the raw pointer for |queue| here.
+ NonNestableTask deferred_task{work_queue->TakeTaskFromWorkQueue(),
+ work_queue->task_queue(),
+ work_queue->queue_type()};
+ // We push these tasks onto the front to make sure that when requeued they
+ // are pushed in the right order.
+ main_thread_only().non_nestable_task_queue.push_front(
+ std::move(deferred_task));
+ continue;
+ }
+
+ // Due to nested message loops we need to maintain a stack of currently
+ // executing tasks so in TaskQueueManagerImpl::DidRunTask we can run the
+ // right observers.
+ main_thread_only().task_execution_stack.emplace_back(
+ work_queue->TakeTaskFromWorkQueue(), work_queue->task_queue());
+ ExecutingTask& executing_task =
+ *main_thread_only().task_execution_stack.rbegin();
+ NotifyWillProcessTask(&executing_task, &lazy_now);
+ return std::move(executing_task.pending_task);
+ }
+}
+
+void TaskQueueManagerImpl::DidRunTask() {
+ LazyNow lazy_now(main_thread_only().real_time_domain->CreateLazyNow());
+ ExecutingTask& executing_task =
+ *main_thread_only().task_execution_stack.rbegin();
+ NotifyDidProcessTask(executing_task, &lazy_now);
+ main_thread_only().task_execution_stack.pop_back();
+
+ if (main_thread_only().nesting_depth == 0)
+ CleanUpQueues();
+}
+
+base::TimeDelta TaskQueueManagerImpl::DelayTillNextTask(LazyNow* lazy_now) {
+ DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
+
+ // If the selector has non-empty queues we trivially know there is immediate
+ // work to be done.
+ if (!main_thread_only().selector.AllEnabledWorkQueuesAreEmpty())
+ return base::TimeDelta();
+
+ // Its possible the selectors state is dirty because ReloadEmptyWorkQueues
+ // hasn't been called yet. This check catches the case of fresh incoming work.
+ {
+ base::AutoLock lock(any_thread_lock_);
+ for (const auto& pair : any_thread().has_incoming_immediate_work) {
+ if (pair.first->CouldTaskRun(pair.second))
+ return base::TimeDelta();
+ }
+ }
+
+ // Otherwise we need to find the shortest delay, if any. NB we don't need to
+ // call WakeUpReadyDelayedQueues because it's assumed DelayTillNextTask will
+ // return base::TimeDelta>() if the delayed task is due to run now.
+ base::TimeDelta delay_till_next_task = base::TimeDelta::Max();
+ for (TimeDomain* time_domain : main_thread_only().time_domains) {
+ base::Optional<base::TimeDelta> delay =
+ time_domain->DelayTillNextTask(lazy_now);
+ if (!delay)
+ continue;
+
+ if (*delay < delay_till_next_task)
+ delay_till_next_task = *delay;
+ }
+ return delay_till_next_task;
+}
+
+void TaskQueueManagerImpl::DidQueueTask(
+ const internal::TaskQueueImpl::Task& pending_task) {
+ controller_->DidQueueTask(pending_task);
+}
+
+void TaskQueueManagerImpl::NotifyWillProcessTask(ExecutingTask* executing_task,
+ LazyNow* time_before_task) {
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "TaskQueueManagerImpl::NotifyWillProcessTaskObservers");
+ if (executing_task->task_queue->GetQuiescenceMonitored())
+ main_thread_only().task_was_run_on_quiescence_monitored_queue = true;
+
+ base::debug::SetCrashKeyString(
+ main_thread_only().file_name_crash_key,
+ executing_task->pending_task.posted_from.file_name());
+ base::debug::SetCrashKeyString(
+ main_thread_only().function_name_crash_key,
+ executing_task->pending_task.posted_from.function_name());
+
+ if (executing_task->task_queue->GetShouldNotifyObservers()) {
+ {
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "TaskQueueManager.WillProcessTaskObservers");
+ for (auto& observer : main_thread_only().task_observers)
+ observer.WillProcessTask(executing_task->pending_task);
+ }
+
+ {
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "TaskQueueManager.QueueNotifyWillProcessTask");
+ executing_task->task_queue->NotifyWillProcessTask(
+ executing_task->pending_task);
+ }
+
+ bool notify_time_observers =
+ main_thread_only().nesting_depth == 0 &&
+ (main_thread_only().task_time_observers.might_have_observers() ||
+ executing_task->task_queue->RequiresTaskTiming());
+ if (notify_time_observers) {
+ executing_task->task_start_time = time_before_task->Now();
+ double task_start_time_sec =
+ MonotonicTimeInSeconds(executing_task->task_start_time);
+
+ {
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "TaskQueueManager.WillProcessTaskTimeObservers");
+ for (auto& observer : main_thread_only().task_time_observers)
+ observer.WillProcessTask(task_start_time_sec);
+ }
+
+ {
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "TaskQueueManager.QueueOnTaskStarted");
+ executing_task->task_queue->OnTaskStarted(
+ executing_task->pending_task, executing_task->task_start_time);
+ }
+ }
+ }
+
+ executing_task->should_record_thread_time = ShouldRecordCPUTimeForTask();
+ if (executing_task->should_record_thread_time)
+ executing_task->task_start_thread_time = base::ThreadTicks::Now();
+}
+
+void TaskQueueManagerImpl::NotifyDidProcessTask(
+ const ExecutingTask& executing_task,
+ LazyNow* time_after_task) {
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "TaskQueueManagerImpl::NotifyDidProcessTaskObservers");
+
+ base::ThreadTicks task_end_thread_time;
+ if (executing_task.should_record_thread_time)
+ task_end_thread_time = base::ThreadTicks::Now();
+
+ if (!executing_task.task_queue->GetShouldNotifyObservers())
+ return;
+
+ double task_start_time_sec =
+ MonotonicTimeInSeconds(executing_task.task_start_time);
+ double task_end_time_sec = 0;
+
+ if (task_start_time_sec) {
+ task_end_time_sec = MonotonicTimeInSeconds(time_after_task->Now());
+
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "TaskQueueManager.DidProcessTaskTimeObservers");
+ for (auto& observer : main_thread_only().task_time_observers)
+ observer.DidProcessTask(task_start_time_sec, task_end_time_sec);
+ }
+
+ {
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "TaskQueueManager.DidProcessTaskObservers");
+ for (auto& observer : main_thread_only().task_observers)
+ observer.DidProcessTask(executing_task.pending_task);
+ }
+
+ {
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "TaskQueueManager.QueueNotifyDidProcessTask");
+ executing_task.task_queue->NotifyDidProcessTask(
+ executing_task.pending_task);
+ }
+
+ {
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "TaskQueueManager.QueueOnTaskCompleted");
+ if (task_start_time_sec && task_end_time_sec) {
+ executing_task.task_queue->OnTaskCompleted(
+ executing_task.pending_task, executing_task.task_start_time,
+ time_after_task->Now(),
+ task_end_thread_time - executing_task.task_start_thread_time);
+ }
+ }
+
+ if (task_start_time_sec && task_end_time_sec &&
+ task_end_time_sec - task_start_time_sec > kLongTaskTraceEventThreshold) {
+ TRACE_EVENT_INSTANT1("blink", "LongTask", TRACE_EVENT_SCOPE_THREAD,
+ "duration", task_end_time_sec - task_start_time_sec);
+ }
+}
+
+void TaskQueueManagerImpl::SetWorkBatchSize(int work_batch_size) {
+ DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
+ DCHECK_GE(work_batch_size, 1);
+ controller_->SetWorkBatchSize(work_batch_size);
+}
+
+void TaskQueueManagerImpl::AddTaskObserver(
+ base::MessageLoop::TaskObserver* task_observer) {
+ DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
+ main_thread_only().task_observers.AddObserver(task_observer);
+}
+
+void TaskQueueManagerImpl::RemoveTaskObserver(
+ base::MessageLoop::TaskObserver* task_observer) {
+ DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
+ main_thread_only().task_observers.RemoveObserver(task_observer);
+}
+
+void TaskQueueManagerImpl::AddTaskTimeObserver(
+ TaskTimeObserver* task_time_observer) {
+ DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
+ main_thread_only().task_time_observers.AddObserver(task_time_observer);
+}
+
+void TaskQueueManagerImpl::RemoveTaskTimeObserver(
+ TaskTimeObserver* task_time_observer) {
+ DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
+ main_thread_only().task_time_observers.RemoveObserver(task_time_observer);
+}
+
+bool TaskQueueManagerImpl::GetAndClearSystemIsQuiescentBit() {
+ bool task_was_run =
+ main_thread_only().task_was_run_on_quiescence_monitored_queue;
+ main_thread_only().task_was_run_on_quiescence_monitored_queue = false;
+ return !task_was_run;
+}
+
+internal::EnqueueOrder TaskQueueManagerImpl::GetNextSequenceNumber() {
+ return enqueue_order_generator_.GenerateNext();
+}
+
+LazyNow TaskQueueManagerImpl::CreateLazyNow() const {
+ return LazyNow(controller_->GetClock());
+}
+
+std::unique_ptr<base::trace_event::ConvertableToTraceFormat>
+TaskQueueManagerImpl::AsValueWithSelectorResult(
+ bool should_run,
+ internal::WorkQueue* selected_work_queue) const {
+ DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
+ std::unique_ptr<base::trace_event::TracedValue> state(
+ new base::trace_event::TracedValue());
+ base::TimeTicks now =
+ main_thread_only().real_time_domain->CreateLazyNow().Now();
+ state->BeginArray("active_queues");
+ for (auto& queue : main_thread_only().active_queues)
+ queue->AsValueInto(now, state.get());
+ state->EndArray();
+ state->BeginArray("queues_to_gracefully_shutdown");
+ for (const auto& pair : main_thread_only().queues_to_gracefully_shutdown)
+ pair.first->AsValueInto(now, state.get());
+ state->EndArray();
+ state->BeginArray("queues_to_delete");
+ for (const auto& pair : main_thread_only().queues_to_delete)
+ pair.first->AsValueInto(now, state.get());
+ state->EndArray();
+ state->BeginDictionary("selector");
+ main_thread_only().selector.AsValueInto(state.get());
+ state->EndDictionary();
+ if (should_run) {
+ state->SetString("selected_queue",
+ selected_work_queue->task_queue()->GetName());
+ state->SetString("work_queue_name", selected_work_queue->name());
+ }
+
+ state->BeginArray("time_domains");
+ for (auto* time_domain : main_thread_only().time_domains)
+ time_domain->AsValueInto(state.get());
+ state->EndArray();
+ {
+ base::AutoLock lock(any_thread_lock_);
+ state->BeginArray("has_incoming_immediate_work");
+ for (const auto& pair : any_thread().has_incoming_immediate_work) {
+ state->AppendString(pair.first->GetName());
+ }
+ state->EndArray();
+ }
+ return std::move(state);
+}
+
+void TaskQueueManagerImpl::OnTaskQueueEnabled(internal::TaskQueueImpl* queue) {
+ DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
+ DCHECK(queue->IsQueueEnabled());
+ // Only schedule DoWork if there's something to do.
+ if (queue->HasTaskToRunImmediately() && !queue->BlockedByFence())
+ MaybeScheduleImmediateWork(FROM_HERE);
+}
+
+void TaskQueueManagerImpl::SweepCanceledDelayedTasks() {
+ std::map<TimeDomain*, base::TimeTicks> time_domain_now;
+ for (const auto& queue : main_thread_only().active_queues)
+ SweepCanceledDelayedTasksInQueue(queue, &time_domain_now);
+ for (const auto& pair : main_thread_only().queues_to_gracefully_shutdown)
+ SweepCanceledDelayedTasksInQueue(pair.first, &time_domain_now);
+}
+
+void TaskQueueManagerImpl::TakeQueuesToGracefullyShutdownFromHelper() {
+ std::vector<std::unique_ptr<internal::TaskQueueImpl>> queues =
+ graceful_shutdown_helper_->TakeQueues();
+ for (std::unique_ptr<internal::TaskQueueImpl>& queue : queues) {
+ main_thread_only().queues_to_gracefully_shutdown[queue.get()] =
+ std::move(queue);
+ }
+}
+
+void TaskQueueManagerImpl::CleanUpQueues() {
+ TakeQueuesToGracefullyShutdownFromHelper();
+
+ for (auto it = main_thread_only().queues_to_gracefully_shutdown.begin();
+ it != main_thread_only().queues_to_gracefully_shutdown.end();) {
+ if (it->first->IsEmpty()) {
+ UnregisterTaskQueueImpl(std::move(it->second));
+ main_thread_only().active_queues.erase(it->first);
+ main_thread_only().queues_to_gracefully_shutdown.erase(it++);
+ } else {
+ ++it;
+ }
+ }
+ main_thread_only().queues_to_delete.clear();
+}
+
+scoped_refptr<internal::GracefulQueueShutdownHelper>
+TaskQueueManagerImpl::GetGracefulQueueShutdownHelper() const {
+ return graceful_shutdown_helper_;
+}
+
+base::WeakPtr<TaskQueueManagerImpl> TaskQueueManagerImpl::GetWeakPtr() {
+ return weak_factory_.GetWeakPtr();
+}
+
+void TaskQueueManagerImpl::SetDefaultTaskRunner(
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+ controller_->SetDefaultTaskRunner(task_runner);
+}
+
+const base::TickClock* TaskQueueManagerImpl::GetClock() const {
+ return controller_->GetClock();
+}
+
+base::TimeTicks TaskQueueManagerImpl::NowTicks() const {
+ return controller_->GetClock()->NowTicks();
+}
+
+bool TaskQueueManagerImpl::ShouldRecordCPUTimeForTask() {
+ return base::ThreadTicks::IsSupported() &&
+ main_thread_only().uniform_distribution(
+ main_thread_only().random_generator) <
+ kSamplingRateForRecordingCPUTime;
+}
+
+MSVC_DISABLE_OPTIMIZE()
+bool TaskQueueManagerImpl::Validate() {
+ return memory_corruption_sentinel_ == kMemoryCorruptionSentinelValue;
+}
+MSVC_ENABLE_OPTIMIZE()
+
+void TaskQueueManagerImpl::EnableCrashKeys(
+ const char* file_name_crash_key_name,
+ const char* function_name_crash_key_name) {
+ DCHECK(!main_thread_only().file_name_crash_key);
+ DCHECK(!main_thread_only().function_name_crash_key);
+ main_thread_only().file_name_crash_key = base::debug::AllocateCrashKeyString(
+ file_name_crash_key_name, base::debug::CrashKeySize::Size64);
+ main_thread_only().function_name_crash_key =
+ base::debug::AllocateCrashKeyString(function_name_crash_key_name,
+ base::debug::CrashKeySize::Size64);
+}
+
+internal::TaskQueueImpl* TaskQueueManagerImpl::currently_executing_task_queue()
+ const {
+ if (main_thread_only().task_execution_stack.empty())
+ return nullptr;
+ return main_thread_only().task_execution_stack.rbegin()->task_queue;
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_manager_impl.h b/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_manager_impl.h
new file mode 100644
index 00000000000..dde60bee6b7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_manager_impl.h
@@ -0,0 +1,334 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TASK_QUEUE_MANAGER_IMPL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TASK_QUEUE_MANAGER_IMPL_H_
+
+#include <map>
+#include <random>
+
+#include "base/atomic_sequence_num.h"
+#include "base/cancelable_callback.h"
+#include "base/debug/task_annotator.h"
+#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/pending_task.h"
+#include "base/run_loop.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/thread_checker.h"
+#include "third_party/blink/renderer/platform/scheduler/base/enqueue_order.h"
+#include "third_party/blink/renderer/platform/scheduler/base/graceful_queue_shutdown_helper.h"
+#include "third_party/blink/renderer/platform/scheduler/base/moveable_auto_lock.h"
+#include "third_party/blink/renderer/platform/scheduler/base/sequenced_task_source.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_manager.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_selector.h"
+
+namespace base {
+namespace debug {
+struct CrashKeyString;
+} // namespace debug
+
+namespace trace_event {
+class ConvertableToTraceFormat;
+} // namespace trace_event
+} // namespace base
+
+namespace blink {
+namespace scheduler {
+
+namespace internal {
+class TaskQueueImpl;
+class ThreadController;
+} // namespace internal
+
+class LazyNow;
+class RealTimeDomain;
+class TaskQueue;
+class TaskTimeObserver;
+class TimeDomain;
+
+// The task queue manager provides N task queues and a selector interface for
+// choosing which task queue to service next. Each task queue consists of two
+// sub queues:
+//
+// 1. Incoming task queue. Tasks that are posted get immediately appended here.
+// When a task is appended into an empty incoming queue, the task manager
+// work function (DoWork()) is scheduled to run on the main task runner.
+//
+// 2. Work queue. If a work queue is empty when DoWork() is entered, tasks from
+// the incoming task queue (if any) are moved here. The work queues are
+// registered with the selector as input to the scheduling decision.
+//
+class PLATFORM_EXPORT TaskQueueManagerImpl
+ : public TaskQueueManager,
+ public internal::SequencedTaskSource,
+ public internal::TaskQueueSelector::Observer,
+ public base::RunLoop::NestingObserver {
+ public:
+ // Keep public methods in sync with TaskQueueManager interface.
+ // The general rule is to keep methods only used in scheduler/base just here
+ // and not to define them in the interface.
+
+ using Observer = TaskQueueManager::Observer;
+
+ ~TaskQueueManagerImpl() override;
+
+ // Assume direct control over current thread and create a TaskQueueManager.
+ // This function should be called only once per thread.
+ // This function assumes that a MessageLoop is initialized for current
+ // thread.
+ static std::unique_ptr<TaskQueueManagerImpl> TakeOverCurrentThread();
+
+ // TaskQueueManager implementation:
+ void SetObserver(Observer* observer) override;
+ void AddTaskObserver(base::MessageLoop::TaskObserver* task_observer) override;
+ void RemoveTaskObserver(
+ base::MessageLoop::TaskObserver* task_observer) override;
+ void AddTaskTimeObserver(TaskTimeObserver* task_time_observer) override;
+ void RemoveTaskTimeObserver(TaskTimeObserver* task_time_observer) override;
+ void RegisterTimeDomain(TimeDomain* time_domain) override;
+ void UnregisterTimeDomain(TimeDomain* time_domain) override;
+ RealTimeDomain* GetRealTimeDomain() const override;
+ const base::TickClock* GetClock() const override;
+ base::TimeTicks NowTicks() const override;
+ void SetDefaultTaskRunner(
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner) override;
+ void SweepCanceledDelayedTasks() override;
+ bool GetAndClearSystemIsQuiescentBit() override;
+ void SetWorkBatchSize(int work_batch_size) override;
+ void EnableCrashKeys(const char* file_name_crash_key,
+ const char* function_name_crash_key) override;
+
+ // Implementation of SequencedTaskSource:
+ base::Optional<base::PendingTask> TakeTask() override;
+ void DidRunTask() override;
+ base::TimeDelta DelayTillNextTask(LazyNow* lazy_now) override;
+
+ // Requests that a task to process work is posted on the main task runner.
+ // These tasks are de-duplicated in two buckets: main-thread and all other
+ // threads. This distinction is done to reduce the overhead from locks, we
+ // assume the main-thread path will be hot.
+ void MaybeScheduleImmediateWork(const base::Location& from_here);
+
+ // Requests that a delayed task to process work is posted on the main task
+ // runner. These delayed tasks are de-duplicated. Must be called on the thread
+ // this class was created on.
+ void MaybeScheduleDelayedWork(const base::Location& from_here,
+ TimeDomain* requesting_time_domain,
+ base::TimeTicks now,
+ base::TimeTicks run_time);
+
+ // Cancels a delayed task to process work at |run_time|, previously requested
+ // with MaybeScheduleDelayedWork.
+ void CancelDelayedWork(TimeDomain* requesting_time_domain,
+ base::TimeTicks run_time);
+
+ LazyNow CreateLazyNow() const;
+
+ // Returns the currently executing TaskQueue if any. Must be called on the
+ // thread this class was created on.
+ internal::TaskQueueImpl* currently_executing_task_queue() const;
+
+ // Unregisters a TaskQueue previously created by |NewTaskQueue()|.
+ // No tasks will run on this queue after this call.
+ void UnregisterTaskQueueImpl(
+ std::unique_ptr<internal::TaskQueueImpl> task_queue);
+
+ scoped_refptr<internal::GracefulQueueShutdownHelper>
+ GetGracefulQueueShutdownHelper() const;
+
+ base::WeakPtr<TaskQueueManagerImpl> GetWeakPtr();
+
+ protected:
+ // Create a task queue manager where |controller| controls the thread
+ // on which the tasks are eventually run.
+ explicit TaskQueueManagerImpl(
+ std::unique_ptr<internal::ThreadController> controller);
+
+ friend class internal::TaskQueueImpl;
+ friend class TaskQueueManagerForTest;
+
+ private:
+ enum class ProcessTaskResult {
+ kDeferred,
+ kExecuted,
+ kTaskQueueManagerDeleted,
+ };
+
+ using IncomingImmediateWorkMap =
+ std::unordered_map<internal::TaskQueueImpl*, internal::EnqueueOrder>;
+
+ struct AnyThread {
+ AnyThread() = default;
+
+ // Task queues with newly available work on the incoming queue.
+ IncomingImmediateWorkMap has_incoming_immediate_work;
+ };
+
+ // TODO(scheduler-dev): Review if we really need non-nestable tasks at all.
+ struct NonNestableTask {
+ internal::TaskQueueImpl::Task task;
+ internal::TaskQueueImpl* task_queue;
+ WorkType work_type;
+ };
+ using NonNestableTaskDeque = WTF::Deque<NonNestableTask, 8>;
+
+ // We have to track rentrancy because we support nested runloops but the
+ // selector interface is unaware of those. This struct keeps track off all
+ // task related state needed to make pairs of TakeTask() / DidRunTask() work.
+ struct ExecutingTask {
+ ExecutingTask()
+ : pending_task(
+ TaskQueue::PostedTask(base::OnceClosure(), base::Location()),
+ base::TimeTicks(),
+ 0) {}
+
+ ExecutingTask(internal::TaskQueueImpl::Task&& pending_task,
+ internal::TaskQueueImpl* task_queue)
+ : pending_task(std::move(pending_task)), task_queue(task_queue) {}
+
+ internal::TaskQueueImpl::Task pending_task;
+ internal::TaskQueueImpl* task_queue = nullptr;
+ base::TimeTicks task_start_time;
+ base::ThreadTicks task_start_thread_time;
+ bool should_record_thread_time = false;
+ };
+
+ struct MainThreadOnly {
+ MainThreadOnly();
+
+ int nesting_depth = 0;
+ NonNestableTaskDeque non_nestable_task_queue;
+ // TODO(altimin): Switch to instruction pointer crash key when it's
+ // available.
+ base::debug::CrashKeyString* file_name_crash_key = nullptr;
+ base::debug::CrashKeyString* function_name_crash_key = nullptr;
+
+ std::mt19937_64 random_generator;
+ std::uniform_real_distribution<double> uniform_distribution;
+
+ internal::TaskQueueSelector selector;
+ base::ObserverList<base::MessageLoop::TaskObserver> task_observers;
+ base::ObserverList<TaskTimeObserver> task_time_observers;
+ std::set<TimeDomain*> time_domains;
+ std::unique_ptr<RealTimeDomain> real_time_domain;
+
+ // List of task queues managed by this TaskQueueManager.
+ // - active_queues contains queues that are still running tasks.
+ // Most often they are owned by relevant TaskQueues, but
+ // queues_to_gracefully_shutdown_ are included here too.
+ // - queues_to_gracefully_shutdown contains queues which should be deleted
+ // when they become empty.
+ // - queues_to_delete contains soon-to-be-deleted queues, because some
+ // internal scheduling code does not expect queues to be pulled
+ // from underneath.
+
+ std::set<internal::TaskQueueImpl*> active_queues;
+ std::map<internal::TaskQueueImpl*, std::unique_ptr<internal::TaskQueueImpl>>
+ queues_to_gracefully_shutdown;
+ std::map<internal::TaskQueueImpl*, std::unique_ptr<internal::TaskQueueImpl>>
+ queues_to_delete;
+
+ bool task_was_run_on_quiescence_monitored_queue = false;
+
+ // Due to nested runloops more than one task can be executing concurrently.
+ std::list<ExecutingTask> task_execution_stack;
+
+ Observer* observer = nullptr; // NOT OWNED
+ };
+
+ // TaskQueueSelector::Observer:
+ void OnTaskQueueEnabled(internal::TaskQueueImpl* queue) override;
+
+ // base::RunLoop::NestingObserver:
+ void OnBeginNestedRunLoop() override;
+ void OnExitNestedRunLoop() override;
+
+ // Called by the task queue to register a new pending task.
+ void DidQueueTask(const internal::TaskQueueImpl::Task& pending_task);
+
+ // Delayed Tasks with run_times <= Now() are enqueued onto the work queue and
+ // reloads any empty work queues.
+ void WakeUpReadyDelayedQueues(LazyNow* lazy_now);
+
+ void NotifyWillProcessTask(ExecutingTask* task, LazyNow* time_before_task);
+ void NotifyDidProcessTask(const ExecutingTask& task,
+ LazyNow* time_after_task);
+
+ internal::EnqueueOrder GetNextSequenceNumber();
+
+ std::unique_ptr<base::trace_event::ConvertableToTraceFormat>
+ AsValueWithSelectorResult(bool should_run,
+ internal::WorkQueue* selected_work_queue) const;
+
+ // Adds |queue| to |any_thread().has_incoming_immediate_work_| and if
+ // |queue_is_blocked| is false it makes sure a DoWork is posted.
+ // Can be called from any thread.
+ void OnQueueHasIncomingImmediateWork(internal::TaskQueueImpl* queue,
+ internal::EnqueueOrder enqueue_order,
+ bool queue_is_blocked);
+
+ // Calls |ReloadImmediateWorkQueueIfEmpty| on all queues in
+ // |queues_to_reload|.
+ void ReloadEmptyWorkQueues(
+ const IncomingImmediateWorkMap& queues_to_reload) const;
+
+ std::unique_ptr<internal::TaskQueueImpl> CreateTaskQueueImpl(
+ const TaskQueue::Spec& spec) override;
+
+ void TakeQueuesToGracefullyShutdownFromHelper();
+
+ // Deletes queues marked for deletion and empty queues marked for shutdown.
+ void CleanUpQueues();
+
+ bool ShouldRecordCPUTimeForTask();
+
+ const scoped_refptr<internal::GracefulQueueShutdownHelper>
+ graceful_shutdown_helper_;
+
+ internal::EnqueueOrderGenerator enqueue_order_generator_;
+
+ std::unique_ptr<internal::ThreadController> controller_;
+
+ mutable base::Lock any_thread_lock_;
+ AnyThread any_thread_;
+
+ struct AnyThread& any_thread() {
+ any_thread_lock_.AssertAcquired();
+ return any_thread_;
+ }
+ const struct AnyThread& any_thread() const {
+ any_thread_lock_.AssertAcquired();
+ return any_thread_;
+ }
+
+ // A check to bail out early during memory corruption.
+ // crbug.com/757940
+ bool Validate();
+
+ int32_t memory_corruption_sentinel_;
+
+ THREAD_CHECKER(main_thread_checker_);
+ MainThreadOnly main_thread_only_;
+ MainThreadOnly& main_thread_only() {
+ DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
+ return main_thread_only_;
+ }
+ const MainThreadOnly& main_thread_only() const {
+ DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
+ return main_thread_only_;
+ }
+
+ base::WeakPtrFactory<TaskQueueManagerImpl> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(TaskQueueManagerImpl);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TASK_QUEUE_MANAGER_IMPL_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_manager_impl_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_manager_impl_unittest.cc
new file mode 100644
index 00000000000..303dc435830
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_manager_impl_unittest.cc
@@ -0,0 +1,3312 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_manager_impl.h"
+
+#include <stddef.h>
+#include <memory>
+#include <utility>
+
+#include "base/location.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "base/test/trace_event_analyzer.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/trace_event/blame_context.h"
+#include "components/viz/test/ordered_simple_task_runner.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
+#include "third_party/blink/renderer/platform/scheduler/base/real_time_domain.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_selector.h"
+#include "third_party/blink/renderer/platform/scheduler/base/test_count_uses_time_source.h"
+#include "third_party/blink/renderer/platform/scheduler/base/test_task_time_observer.h"
+#include "third_party/blink/renderer/platform/scheduler/base/thread_controller_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/base/virtual_time_domain.h"
+#include "third_party/blink/renderer/platform/scheduler/base/work_queue.h"
+#include "third_party/blink/renderer/platform/scheduler/base/work_queue_sets.h"
+#include "third_party/blink/renderer/platform/scheduler/test/task_queue_manager_for_test.h"
+#include "third_party/blink/renderer/platform/scheduler/test/test_task_queue.h"
+
+using testing::AnyNumber;
+using testing::Contains;
+using testing::ElementsAre;
+using testing::ElementsAreArray;
+using testing::Mock;
+using testing::Not;
+using testing::_;
+using blink::scheduler::internal::EnqueueOrder;
+
+namespace blink {
+namespace scheduler {
+
+class TaskQueueManagerTest : public testing::Test {
+ public:
+ TaskQueueManagerTest() = default;
+ void DeleteTaskQueueManager() { manager_.reset(); }
+
+ protected:
+ void TearDown() { manager_.reset(); }
+
+ scoped_refptr<TestTaskQueue> CreateTaskQueueWithSpec(TaskQueue::Spec spec) {
+ return manager_->CreateTaskQueue<TestTaskQueue>(spec);
+ }
+
+ scoped_refptr<TestTaskQueue> CreateTaskQueue() {
+ return CreateTaskQueueWithSpec(TaskQueue::Spec("test"));
+ }
+
+ scoped_refptr<TestTaskQueue> CreateTaskQueueWithMonitoredQuiescence() {
+ return CreateTaskQueueWithSpec(
+ TaskQueue::Spec("test").SetShouldMonitorQuiescence(true));
+ }
+
+ void Initialize(size_t num_queues) {
+ now_src_.Advance(base::TimeDelta::FromMicroseconds(1000));
+
+ test_task_runner_ =
+ base::WrapRefCounted(new cc::OrderedSimpleTaskRunner(&now_src_, false));
+
+ manager_ = TaskQueueManagerForTest::Create(nullptr, test_task_runner_.get(),
+ &now_src_);
+
+ for (size_t i = 0; i < num_queues; i++)
+ runners_.push_back(CreateTaskQueue());
+ }
+
+ void InitializeWithRealMessageLoop(size_t num_queues) {
+ message_loop_.reset(new base::MessageLoop());
+ original_message_loop_task_runner_ = message_loop_->task_runner();
+ // A null clock triggers some assertions.
+ now_src_.Advance(base::TimeDelta::FromMicroseconds(1000));
+ manager_ = TaskQueueManagerForTest::Create(
+ message_loop_.get(), GetSingleThreadTaskRunnerForTesting(), &now_src_);
+
+ for (size_t i = 0; i < num_queues; i++)
+ runners_.push_back(CreateTaskQueue());
+ }
+
+ void WakeUpReadyDelayedQueues(LazyNow lazy_now) {
+ manager_->WakeUpReadyDelayedQueues(&lazy_now);
+ }
+
+ EnqueueOrder GetNextSequenceNumber() const {
+ return manager_->GetNextSequenceNumber();
+ }
+
+ void MaybeScheduleImmediateWork(const base::Location& from_here) {
+ manager_->MaybeScheduleImmediateWork(from_here);
+ }
+
+ // Runs all immediate tasks until there is no more work to do and advances
+ // time if there is a pending delayed task. |per_run_time_callback| is called
+ // when the clock advances.
+ void RunUntilIdle(base::RepeatingClosure per_run_time_callback) {
+ for (;;) {
+ // Advance time if we've run out of immediate work to do.
+ if (!manager_->HasImmediateWork()) {
+ base::TimeTicks run_time;
+ if (manager_->GetRealTimeDomain()->NextScheduledRunTime(&run_time)) {
+ now_src_.SetNowTicks(run_time);
+ per_run_time_callback.Run();
+ } else {
+ break;
+ }
+ }
+
+ test_task_runner_->RunPendingTasks();
+ }
+ }
+
+ base::TimeTicks Now() { return now_src_.NowTicks(); }
+
+ std::unique_ptr<base::MessageLoop> message_loop_;
+ scoped_refptr<base::SingleThreadTaskRunner>
+ original_message_loop_task_runner_;
+ base::SimpleTestTickClock now_src_;
+ scoped_refptr<cc::OrderedSimpleTaskRunner> test_task_runner_;
+ std::unique_ptr<TaskQueueManagerForTest> manager_;
+ std::vector<scoped_refptr<TestTaskQueue>> runners_;
+ TestTaskTimeObserver test_task_time_observer_;
+};
+
+void PostFromNestedRunloop(
+ base::MessageLoop* message_loop,
+ base::SingleThreadTaskRunner* runner,
+ std::vector<std::pair<base::OnceClosure, bool>>* tasks) {
+ base::MessageLoop::ScopedNestableTaskAllower allow(message_loop);
+ for (std::pair<base::OnceClosure, bool>& pair : *tasks) {
+ if (pair.second) {
+ runner->PostTask(FROM_HERE, std::move(pair.first));
+ } else {
+ runner->PostNonNestableTask(FROM_HERE, std::move(pair.first));
+ }
+ }
+ base::RunLoop().RunUntilIdle();
+}
+
+void NopTask() {}
+
+TEST_F(TaskQueueManagerTest,
+ NowCalledMinimumNumberOfTimesToComputeTaskDurations) {
+ message_loop_.reset(new base::MessageLoop());
+ // This memory is managed by the TaskQueueManager, but we need to hold a
+ // pointer to this object to read out how many times Now was called.
+ TestCountUsesTimeSource test_count_uses_time_source;
+
+ manager_ = TaskQueueManagerForTest::Create(
+ nullptr, GetSingleThreadTaskRunnerForTesting(),
+ &test_count_uses_time_source);
+ manager_->SetWorkBatchSize(6);
+ manager_->AddTaskTimeObserver(&test_task_time_observer_);
+
+ for (size_t i = 0; i < 3; i++)
+ runners_.push_back(CreateTaskQueue());
+
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&NopTask));
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&NopTask));
+ runners_[1]->PostTask(FROM_HERE, base::BindOnce(&NopTask));
+ runners_[1]->PostTask(FROM_HERE, base::BindOnce(&NopTask));
+ runners_[2]->PostTask(FROM_HERE, base::BindOnce(&NopTask));
+ runners_[2]->PostTask(FROM_HERE, base::BindOnce(&NopTask));
+
+ base::RunLoop().RunUntilIdle();
+ // Now is called each time a task is queued, when first task is started
+ // running, and when a task is completed. 6 * 3 = 18 calls.
+ EXPECT_EQ(18, test_count_uses_time_source.now_calls_count());
+}
+
+TEST_F(TaskQueueManagerTest, NowNotCalledForNestedTasks) {
+ message_loop_.reset(new base::MessageLoop());
+ // This memory is managed by the TaskQueueManager, but we need to hold a
+ // pointer to this object to read out how many times Now was called.
+ TestCountUsesTimeSource test_count_uses_time_source;
+
+ manager_ = TaskQueueManagerForTest::Create(message_loop_.get(),
+ message_loop_->task_runner(),
+ &test_count_uses_time_source);
+ manager_->AddTaskTimeObserver(&test_task_time_observer_);
+
+ runners_.push_back(CreateTaskQueue());
+
+ std::vector<std::pair<base::OnceClosure, bool>>
+ tasks_to_post_from_nested_loop;
+ for (int i = 0; i < 7; ++i) {
+ tasks_to_post_from_nested_loop.push_back(
+ std::make_pair(base::BindOnce(&NopTask), true));
+ }
+
+ runners_[0]->PostTask(
+ FROM_HERE,
+ base::BindOnce(&PostFromNestedRunloop, message_loop_.get(),
+ base::RetainedRef(runners_[0]),
+ base::Unretained(&tasks_to_post_from_nested_loop)));
+
+ base::RunLoop().RunUntilIdle();
+ // We need to call Now twice, to measure the start and end of the outermost
+ // task. We shouldn't call it for any of the nested tasks.
+ // Also Now is called when a task is scheduled (8 times).
+ // That brings expected call count for Now() to 2 + 8 = 10
+ EXPECT_EQ(10, test_count_uses_time_source.now_calls_count());
+}
+
+void NullTask() {}
+
+void TestTask(EnqueueOrder value, std::vector<EnqueueOrder>* out_result) {
+ out_result->push_back(value);
+}
+
+void DisableQueueTestTask(EnqueueOrder value,
+ std::vector<EnqueueOrder>* out_result,
+ TaskQueue::QueueEnabledVoter* voter) {
+ out_result->push_back(value);
+ voter->SetQueueEnabled(false);
+}
+
+TEST_F(TaskQueueManagerTest, SingleQueuePosting) {
+ Initialize(1u);
+
+ std::vector<EnqueueOrder> run_order;
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 1, &run_order));
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 2, &run_order));
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 3, &run_order));
+
+ test_task_runner_->RunUntilIdle();
+ EXPECT_THAT(run_order, ElementsAre(1, 2, 3));
+}
+
+TEST_F(TaskQueueManagerTest, MultiQueuePosting) {
+ Initialize(3u);
+
+ std::vector<EnqueueOrder> run_order;
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 1, &run_order));
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 2, &run_order));
+ runners_[1]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 3, &run_order));
+ runners_[1]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 4, &run_order));
+ runners_[2]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 5, &run_order));
+ runners_[2]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 6, &run_order));
+
+ test_task_runner_->RunUntilIdle();
+ EXPECT_THAT(run_order, ElementsAre(1, 2, 3, 4, 5, 6));
+}
+
+TEST_F(TaskQueueManagerTest, NonNestableTaskPosting) {
+ InitializeWithRealMessageLoop(1u);
+
+ std::vector<EnqueueOrder> run_order;
+ runners_[0]->PostNonNestableTask(FROM_HERE,
+ base::BindOnce(&TestTask, 1, &run_order));
+
+ base::RunLoop().RunUntilIdle();
+ EXPECT_THAT(run_order, ElementsAre(1));
+}
+
+TEST_F(TaskQueueManagerTest, NonNestableTaskExecutesInExpectedOrder) {
+ InitializeWithRealMessageLoop(1u);
+
+ std::vector<EnqueueOrder> run_order;
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 1, &run_order));
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 2, &run_order));
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 3, &run_order));
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 4, &run_order));
+ runners_[0]->PostNonNestableTask(FROM_HERE,
+ base::BindOnce(&TestTask, 5, &run_order));
+
+ base::RunLoop().RunUntilIdle();
+ EXPECT_THAT(run_order, ElementsAre(1, 2, 3, 4, 5));
+}
+
+TEST_F(TaskQueueManagerTest, NonNestableTasksDoesntExecuteInNestedLoop) {
+ InitializeWithRealMessageLoop(1u);
+
+ std::vector<EnqueueOrder> run_order;
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 1, &run_order));
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 2, &run_order));
+
+ std::vector<std::pair<base::OnceClosure, bool>>
+ tasks_to_post_from_nested_loop;
+ tasks_to_post_from_nested_loop.push_back(
+ std::make_pair(base::BindOnce(&TestTask, 3, &run_order), false));
+ tasks_to_post_from_nested_loop.push_back(
+ std::make_pair(base::BindOnce(&TestTask, 4, &run_order), false));
+ tasks_to_post_from_nested_loop.push_back(
+ std::make_pair(base::BindOnce(&TestTask, 5, &run_order), true));
+ tasks_to_post_from_nested_loop.push_back(
+ std::make_pair(base::BindOnce(&TestTask, 6, &run_order), true));
+
+ runners_[0]->PostTask(
+ FROM_HERE,
+ base::BindOnce(&PostFromNestedRunloop, message_loop_.get(),
+ base::RetainedRef(runners_[0]),
+ base::Unretained(&tasks_to_post_from_nested_loop)));
+
+ base::RunLoop().RunUntilIdle();
+ // Note we expect tasks 3 & 4 to run last because they're non-nestable.
+ EXPECT_THAT(run_order, ElementsAre(1, 2, 5, 6, 3, 4));
+}
+
+namespace {
+
+void InsertFenceAndPostTestTask(EnqueueOrder id,
+ std::vector<EnqueueOrder>* run_order,
+ scoped_refptr<TestTaskQueue> task_queue) {
+ run_order->push_back(id);
+ task_queue->InsertFence(TaskQueue::InsertFencePosition::kNow);
+ task_queue->PostTask(FROM_HERE, base::BindOnce(&TestTask, id + 1, run_order));
+
+ // Force reload of immediate work queue. In real life the same effect can be
+ // achieved with cross-thread posting.
+ task_queue->GetTaskQueueImpl()->ReloadImmediateWorkQueueIfEmpty();
+}
+
+} // namespace
+
+TEST_F(TaskQueueManagerTest, TaskQueueDisabledFromNestedLoop) {
+ InitializeWithRealMessageLoop(1u);
+ std::vector<EnqueueOrder> run_order;
+
+ std::vector<std::pair<base::OnceClosure, bool>>
+ tasks_to_post_from_nested_loop;
+
+ tasks_to_post_from_nested_loop.push_back(
+ std::make_pair(base::BindOnce(&TestTask, 1, &run_order), false));
+ tasks_to_post_from_nested_loop.push_back(std::make_pair(
+ base::BindOnce(&InsertFenceAndPostTestTask, 2, &run_order, runners_[0]),
+ true));
+
+ runners_[0]->PostTask(
+ FROM_HERE,
+ base::BindOnce(&PostFromNestedRunloop, message_loop_.get(),
+ base::RetainedRef(runners_[0]),
+ base::Unretained(&tasks_to_post_from_nested_loop)));
+ base::RunLoop().RunUntilIdle();
+
+ // Task 1 shouldn't run first due to it being non-nestable and queue gets
+ // blocked after task 2. Task 1 runs after existing nested message loop
+ // due to being posted before inserting a fence.
+ // This test checks that breaks when nestable task is pushed into a redo
+ // queue.
+ EXPECT_THAT(run_order, ElementsAre(2, 1));
+
+ runners_[0]->RemoveFence();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_THAT(run_order, ElementsAre(2, 1, 3));
+}
+
+TEST_F(TaskQueueManagerTest, HasPendingImmediateWork_ImmediateTask) {
+ Initialize(1u);
+
+ std::vector<EnqueueOrder> run_order;
+ EXPECT_FALSE(runners_[0]->HasTaskToRunImmediately());
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 1, &run_order));
+ EXPECT_TRUE(runners_[0]->HasTaskToRunImmediately());
+
+ // Move the task into the |immediate_work_queue|.
+ EXPECT_TRUE(runners_[0]->GetTaskQueueImpl()->immediate_work_queue()->Empty());
+ std::unique_ptr<TaskQueue::QueueEnabledVoter> voter =
+ runners_[0]->CreateQueueEnabledVoter();
+ voter->SetQueueEnabled(false);
+ test_task_runner_->RunUntilIdle();
+ EXPECT_FALSE(
+ runners_[0]->GetTaskQueueImpl()->immediate_work_queue()->Empty());
+ EXPECT_TRUE(runners_[0]->HasTaskToRunImmediately());
+
+ // Run the task, making the queue empty.
+ voter->SetQueueEnabled(true);
+ test_task_runner_->RunUntilIdle();
+ EXPECT_FALSE(runners_[0]->HasTaskToRunImmediately());
+}
+
+TEST_F(TaskQueueManagerTest, HasPendingImmediateWork_DelayedTask) {
+ Initialize(1u);
+
+ std::vector<EnqueueOrder> run_order;
+ base::TimeDelta delay(base::TimeDelta::FromMilliseconds(10));
+ runners_[0]->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&TestTask, 1, &run_order), delay);
+ EXPECT_FALSE(runners_[0]->HasTaskToRunImmediately());
+ now_src_.Advance(delay);
+ EXPECT_TRUE(runners_[0]->HasTaskToRunImmediately());
+
+ // Move the task into the |delayed_work_queue|.
+ WakeUpReadyDelayedQueues(LazyNow(&now_src_));
+ EXPECT_FALSE(runners_[0]->GetTaskQueueImpl()->delayed_work_queue()->Empty());
+ EXPECT_TRUE(runners_[0]->HasTaskToRunImmediately());
+
+ // Run the task, making the queue empty.
+ test_task_runner_->RunUntilIdle();
+ EXPECT_FALSE(runners_[0]->HasTaskToRunImmediately());
+}
+
+TEST_F(TaskQueueManagerTest, DelayedTaskPosting) {
+ Initialize(1u);
+
+ std::vector<EnqueueOrder> run_order;
+ base::TimeDelta delay(base::TimeDelta::FromMilliseconds(10));
+ runners_[0]->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&TestTask, 1, &run_order), delay);
+ EXPECT_EQ(delay, test_task_runner_->DelayToNextTaskTime());
+ EXPECT_FALSE(runners_[0]->HasTaskToRunImmediately());
+ EXPECT_TRUE(run_order.empty());
+
+ // The task doesn't run before the delay has completed.
+ test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(9));
+ EXPECT_TRUE(run_order.empty());
+
+ // After the delay has completed, the task runs normally.
+ test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(1));
+ EXPECT_THAT(run_order, ElementsAre(1));
+ EXPECT_FALSE(runners_[0]->HasTaskToRunImmediately());
+}
+
+bool MessageLoopTaskCounter(size_t* count) {
+ *count = *count + 1;
+ return true;
+}
+
+TEST_F(TaskQueueManagerTest, DelayedTaskExecutedInOneMessageLoopTask) {
+ Initialize(1u);
+
+ base::TimeDelta delay(base::TimeDelta::FromMilliseconds(10));
+ runners_[0]->PostDelayedTask(FROM_HERE, base::BindOnce(&NopTask), delay);
+
+ size_t task_count = 0;
+ test_task_runner_->RunTasksWhile(
+ base::BindRepeating(&MessageLoopTaskCounter, &task_count));
+ EXPECT_EQ(1u, task_count);
+}
+
+TEST_F(TaskQueueManagerTest, DelayedTaskPosting_MultipleTasks_DecendingOrder) {
+ Initialize(1u);
+
+ std::vector<EnqueueOrder> run_order;
+ runners_[0]->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&TestTask, 1, &run_order),
+ base::TimeDelta::FromMilliseconds(10));
+
+ runners_[0]->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&TestTask, 2, &run_order),
+ base::TimeDelta::FromMilliseconds(8));
+
+ runners_[0]->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&TestTask, 3, &run_order),
+ base::TimeDelta::FromMilliseconds(5));
+
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(5),
+ test_task_runner_->DelayToNextTaskTime());
+
+ test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(5));
+ EXPECT_THAT(run_order, ElementsAre(3));
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(3),
+ test_task_runner_->DelayToNextTaskTime());
+
+ test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(3));
+ EXPECT_THAT(run_order, ElementsAre(3, 2));
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(2),
+ test_task_runner_->DelayToNextTaskTime());
+
+ test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(2));
+ EXPECT_THAT(run_order, ElementsAre(3, 2, 1));
+}
+
+TEST_F(TaskQueueManagerTest, DelayedTaskPosting_MultipleTasks_AscendingOrder) {
+ Initialize(1u);
+
+ std::vector<EnqueueOrder> run_order;
+ runners_[0]->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&TestTask, 1, &run_order),
+ base::TimeDelta::FromMilliseconds(1));
+
+ runners_[0]->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&TestTask, 2, &run_order),
+ base::TimeDelta::FromMilliseconds(5));
+
+ runners_[0]->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&TestTask, 3, &run_order),
+ base::TimeDelta::FromMilliseconds(10));
+
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(1),
+ test_task_runner_->DelayToNextTaskTime());
+
+ test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(1));
+ EXPECT_THAT(run_order, ElementsAre(1));
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(4),
+ test_task_runner_->DelayToNextTaskTime());
+
+ test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(4));
+ EXPECT_THAT(run_order, ElementsAre(1, 2));
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(5),
+ test_task_runner_->DelayToNextTaskTime());
+
+ test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(5));
+ EXPECT_THAT(run_order, ElementsAre(1, 2, 3));
+}
+
+TEST_F(TaskQueueManagerTest, PostDelayedTask_SharesUnderlyingDelayedTasks) {
+ Initialize(1u);
+
+ std::vector<EnqueueOrder> run_order;
+ base::TimeDelta delay(base::TimeDelta::FromMilliseconds(10));
+ runners_[0]->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&TestTask, 1, &run_order), delay);
+ runners_[0]->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&TestTask, 2, &run_order), delay);
+ runners_[0]->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&TestTask, 3, &run_order), delay);
+
+ EXPECT_EQ(1u, test_task_runner_->NumPendingTasks());
+}
+
+class TestObject {
+ public:
+ ~TestObject() { destructor_count__++; }
+
+ void Run() { FAIL() << "TestObject::Run should not be called"; }
+
+ static int destructor_count__;
+};
+
+int TestObject::destructor_count__ = 0;
+
+TEST_F(TaskQueueManagerTest, PendingDelayedTasksRemovedOnShutdown) {
+ Initialize(1u);
+
+ TestObject::destructor_count__ = 0;
+
+ base::TimeDelta delay(base::TimeDelta::FromMilliseconds(10));
+ runners_[0]->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&TestObject::Run, base::Owned(new TestObject())), delay);
+ runners_[0]->PostTask(
+ FROM_HERE,
+ base::BindOnce(&TestObject::Run, base::Owned(new TestObject())));
+
+ manager_.reset();
+
+ EXPECT_EQ(2, TestObject::destructor_count__);
+}
+
+TEST_F(TaskQueueManagerTest, InsertAndRemoveFence) {
+ Initialize(1u);
+ runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow);
+
+ std::vector<EnqueueOrder> run_order;
+ // Posting a task when pumping is disabled doesn't result in work getting
+ // posted.
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 1, &run_order));
+ EXPECT_FALSE(test_task_runner_->HasPendingTasks());
+
+ // However polling still works.
+ EXPECT_TRUE(runners_[0]->HasTaskToRunImmediately());
+
+ // After removing the fence the task runs normally.
+ runners_[0]->RemoveFence();
+ EXPECT_TRUE(test_task_runner_->HasPendingTasks());
+ test_task_runner_->RunUntilIdle();
+ EXPECT_THAT(run_order, ElementsAre(1));
+}
+
+TEST_F(TaskQueueManagerTest, RemovingFenceForDisabledQueueDoesNotPostDoWork) {
+ Initialize(1u);
+
+ std::vector<EnqueueOrder> run_order;
+ std::unique_ptr<TaskQueue::QueueEnabledVoter> voter =
+ runners_[0]->CreateQueueEnabledVoter();
+ voter->SetQueueEnabled(false);
+ runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow);
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 1, &run_order));
+
+ runners_[0]->RemoveFence();
+ EXPECT_FALSE(test_task_runner_->HasPendingTasks());
+}
+
+TEST_F(TaskQueueManagerTest, EnablingFencedQueueDoesNotPostDoWork) {
+ Initialize(1u);
+
+ std::vector<EnqueueOrder> run_order;
+ std::unique_ptr<TaskQueue::QueueEnabledVoter> voter =
+ runners_[0]->CreateQueueEnabledVoter();
+ voter->SetQueueEnabled(false);
+ runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow);
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 1, &run_order));
+
+ voter->SetQueueEnabled(true);
+ EXPECT_FALSE(test_task_runner_->HasPendingTasks());
+}
+
+TEST_F(TaskQueueManagerTest, DenyRunning_BeforePosting) {
+ Initialize(1u);
+
+ std::vector<EnqueueOrder> run_order;
+ std::unique_ptr<TaskQueue::QueueEnabledVoter> voter =
+ runners_[0]->CreateQueueEnabledVoter();
+ voter->SetQueueEnabled(false);
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 1, &run_order));
+ EXPECT_FALSE(test_task_runner_->HasPendingTasks());
+
+ test_task_runner_->RunUntilIdle();
+ EXPECT_TRUE(run_order.empty());
+
+ voter->SetQueueEnabled(true);
+ test_task_runner_->RunUntilIdle();
+ EXPECT_THAT(run_order, ElementsAre(1));
+}
+
+TEST_F(TaskQueueManagerTest, DenyRunning_AfterPosting) {
+ Initialize(1u);
+
+ std::vector<EnqueueOrder> run_order;
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 1, &run_order));
+ std::unique_ptr<TaskQueue::QueueEnabledVoter> voter =
+ runners_[0]->CreateQueueEnabledVoter();
+ EXPECT_TRUE(test_task_runner_->HasPendingTasks());
+ voter->SetQueueEnabled(false);
+
+ test_task_runner_->RunUntilIdle();
+ EXPECT_TRUE(run_order.empty());
+
+ voter->SetQueueEnabled(true);
+ test_task_runner_->RunUntilIdle();
+ EXPECT_THAT(run_order, ElementsAre(1));
+}
+
+TEST_F(TaskQueueManagerTest, DenyRunning_AfterRemovingFence) {
+ Initialize(1u);
+
+ std::vector<EnqueueOrder> run_order;
+ runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow);
+ std::unique_ptr<TaskQueue::QueueEnabledVoter> voter =
+ runners_[0]->CreateQueueEnabledVoter();
+ voter->SetQueueEnabled(false);
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 1, &run_order));
+
+ test_task_runner_->RunUntilIdle();
+ EXPECT_TRUE(run_order.empty());
+
+ runners_[0]->RemoveFence();
+ voter->SetQueueEnabled(true);
+ test_task_runner_->RunUntilIdle();
+ EXPECT_THAT(run_order, ElementsAre(1));
+}
+
+TEST_F(TaskQueueManagerTest, RemovingFenceWithDelayedTask) {
+ Initialize(1u);
+ runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow);
+
+ std::vector<EnqueueOrder> run_order;
+ // Posting a delayed task when fenced will apply the delay, but won't cause
+ // work to executed afterwards.
+ base::TimeDelta delay(base::TimeDelta::FromMilliseconds(10));
+ runners_[0]->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&TestTask, 1, &run_order), delay);
+
+ // The task does not run even though it's delay is up.
+ test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(10));
+ EXPECT_TRUE(run_order.empty());
+
+ // Removing the fence causes the task to run.
+ runners_[0]->RemoveFence();
+ EXPECT_TRUE(test_task_runner_->HasPendingTasks());
+ test_task_runner_->RunPendingTasks();
+ EXPECT_THAT(run_order, ElementsAre(1));
+}
+
+TEST_F(TaskQueueManagerTest, RemovingFenceWithMultipleDelayedTasks) {
+ Initialize(1u);
+ runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow);
+
+ std::vector<EnqueueOrder> run_order;
+ // Posting a delayed task when fenced will apply the delay, but won't cause
+ // work to executed afterwards.
+ base::TimeDelta delay1(base::TimeDelta::FromMilliseconds(1));
+ base::TimeDelta delay2(base::TimeDelta::FromMilliseconds(10));
+ base::TimeDelta delay3(base::TimeDelta::FromMilliseconds(20));
+ runners_[0]->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, 1, &run_order), delay1);
+ runners_[0]->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, 2, &run_order), delay2);
+ runners_[0]->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, 3, &run_order), delay3);
+
+ now_src_.Advance(base::TimeDelta::FromMilliseconds(15));
+ test_task_runner_->RunUntilIdle();
+ EXPECT_TRUE(run_order.empty());
+
+ // Removing the fence causes the ready tasks to run.
+ runners_[0]->RemoveFence();
+ test_task_runner_->RunUntilIdle();
+ EXPECT_THAT(run_order, ElementsAre(1, 2));
+}
+
+TEST_F(TaskQueueManagerTest, InsertFencePreventsDelayedTasksFromRunning) {
+ Initialize(1u);
+ runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow);
+
+ std::vector<EnqueueOrder> run_order;
+ base::TimeDelta delay(base::TimeDelta::FromMilliseconds(10));
+ runners_[0]->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&TestTask, 1, &run_order), delay);
+
+ test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(10));
+ EXPECT_TRUE(run_order.empty());
+}
+
+TEST_F(TaskQueueManagerTest, MultipleFences) {
+ Initialize(1u);
+
+ std::vector<EnqueueOrder> run_order;
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 1, &run_order));
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 2, &run_order));
+ runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow);
+
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 3, &run_order));
+ test_task_runner_->RunUntilIdle();
+ EXPECT_THAT(run_order, ElementsAre(1, 2));
+
+ runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow);
+ // Subsequent tasks should be blocked.
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 4, &run_order));
+ test_task_runner_->RunUntilIdle();
+ EXPECT_THAT(run_order, ElementsAre(1, 2, 3));
+}
+
+TEST_F(TaskQueueManagerTest, InsertFenceThenImmediatlyRemoveDoesNotBlock) {
+ Initialize(1u);
+ runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow);
+ runners_[0]->RemoveFence();
+
+ std::vector<EnqueueOrder> run_order;
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 1, &run_order));
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 2, &run_order));
+
+ test_task_runner_->RunUntilIdle();
+ EXPECT_THAT(run_order, ElementsAre(1, 2));
+}
+
+TEST_F(TaskQueueManagerTest, InsertFencePostThenRemoveDoesNotBlock) {
+ Initialize(1u);
+ runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow);
+
+ std::vector<EnqueueOrder> run_order;
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 1, &run_order));
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 2, &run_order));
+ runners_[0]->RemoveFence();
+
+ test_task_runner_->RunUntilIdle();
+ EXPECT_THAT(run_order, ElementsAre(1, 2));
+}
+
+TEST_F(TaskQueueManagerTest, MultipleFencesWithInitiallyEmptyQueue) {
+ Initialize(1u);
+ runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow);
+
+ std::vector<EnqueueOrder> run_order;
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 1, &run_order));
+ runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow);
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 2, &run_order));
+ test_task_runner_->RunUntilIdle();
+ EXPECT_THAT(run_order, ElementsAre(1));
+}
+
+TEST_F(TaskQueueManagerTest, BlockedByFence) {
+ Initialize(1u);
+ EXPECT_FALSE(runners_[0]->BlockedByFence());
+
+ runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow);
+ EXPECT_TRUE(runners_[0]->BlockedByFence());
+
+ runners_[0]->RemoveFence();
+ EXPECT_FALSE(runners_[0]->BlockedByFence());
+
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&NopTask));
+ runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow);
+ EXPECT_FALSE(runners_[0]->BlockedByFence());
+
+ test_task_runner_->RunUntilIdle();
+ EXPECT_TRUE(runners_[0]->BlockedByFence());
+
+ runners_[0]->RemoveFence();
+ EXPECT_FALSE(runners_[0]->BlockedByFence());
+}
+
+TEST_F(TaskQueueManagerTest, BlockedByFence_BothTypesOfFence) {
+ Initialize(1u);
+
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&NopTask));
+
+ runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow);
+ EXPECT_FALSE(runners_[0]->BlockedByFence());
+
+ runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kBeginningOfTime);
+ EXPECT_TRUE(runners_[0]->BlockedByFence());
+}
+
+namespace {
+
+void RecordTimeTask(std::vector<base::TimeTicks>* run_times,
+ base::SimpleTestTickClock* clock) {
+ run_times->push_back(clock->NowTicks());
+}
+
+void RecordTimeAndQueueTask(
+ std::vector<std::pair<scoped_refptr<TestTaskQueue>, base::TimeTicks>>*
+ run_times,
+ scoped_refptr<TestTaskQueue> task_queue,
+ base::SimpleTestTickClock* clock) {
+ run_times->emplace_back(task_queue, clock->NowTicks());
+}
+
+} // namespace
+
+TEST_F(TaskQueueManagerTest, DelayedFence_DelayedTasks) {
+ Initialize(1u);
+ test_task_runner_->SetAutoAdvanceNowToPendingTasks(true);
+
+ std::vector<base::TimeTicks> run_times;
+ runners_[0]->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&RecordTimeTask, &run_times, &now_src_),
+ base::TimeDelta::FromMilliseconds(100));
+ runners_[0]->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&RecordTimeTask, &run_times, &now_src_),
+ base::TimeDelta::FromMilliseconds(200));
+ runners_[0]->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&RecordTimeTask, &run_times, &now_src_),
+ base::TimeDelta::FromMilliseconds(300));
+
+ runners_[0]->InsertFenceAt(Now() + base::TimeDelta::FromMilliseconds(250));
+ EXPECT_FALSE(runners_[0]->HasActiveFence());
+
+ test_task_runner_->RunUntilIdle();
+
+ EXPECT_TRUE(runners_[0]->HasActiveFence());
+ EXPECT_THAT(
+ run_times,
+ ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(101),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(201)));
+ run_times.clear();
+
+ runners_[0]->RemoveFence();
+ test_task_runner_->RunUntilIdle();
+
+ EXPECT_FALSE(runners_[0]->HasActiveFence());
+ EXPECT_THAT(run_times, ElementsAre(base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(301)));
+}
+
+TEST_F(TaskQueueManagerTest, DelayedFence_ImmediateTasks) {
+ Initialize(1u);
+ test_task_runner_->SetAutoAdvanceNowToPendingTasks(true);
+
+ std::vector<base::TimeTicks> run_times;
+ runners_[0]->InsertFenceAt(Now() + base::TimeDelta::FromMilliseconds(250));
+
+ for (int i = 0; i < 5; ++i) {
+ runners_[0]->PostTask(
+ FROM_HERE, base::BindOnce(&RecordTimeTask, &run_times, &now_src_));
+ test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(100));
+ if (i < 2) {
+ EXPECT_FALSE(runners_[0]->HasActiveFence());
+ } else {
+ EXPECT_TRUE(runners_[0]->HasActiveFence());
+ }
+ }
+
+ EXPECT_THAT(
+ run_times,
+ ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(1),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(101),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(201)));
+ run_times.clear();
+
+ runners_[0]->RemoveFence();
+ test_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(
+ run_times,
+ ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(501),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(501)));
+}
+
+TEST_F(TaskQueueManagerTest, DelayedFence_RemovedFenceDoesNotActivate) {
+ Initialize(1u);
+ test_task_runner_->SetAutoAdvanceNowToPendingTasks(true);
+
+ std::vector<base::TimeTicks> run_times;
+ runners_[0]->InsertFenceAt(Now() + base::TimeDelta::FromMilliseconds(250));
+
+ for (int i = 0; i < 3; ++i) {
+ runners_[0]->PostTask(
+ FROM_HERE, base::BindOnce(&RecordTimeTask, &run_times, &now_src_));
+ EXPECT_FALSE(runners_[0]->HasActiveFence());
+ test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(100));
+ }
+
+ EXPECT_TRUE(runners_[0]->HasActiveFence());
+ runners_[0]->RemoveFence();
+
+ for (int i = 0; i < 2; ++i) {
+ runners_[0]->PostTask(
+ FROM_HERE, base::BindOnce(&RecordTimeTask, &run_times, &now_src_));
+ test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(100));
+ EXPECT_FALSE(runners_[0]->HasActiveFence());
+ }
+
+ EXPECT_THAT(
+ run_times,
+ ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(1),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(101),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(201),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(301),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(401)));
+}
+
+TEST_F(TaskQueueManagerTest, DelayedFence_TakeIncomingImmediateQueue) {
+ // This test checks that everything works correctly when a work queue
+ // is swapped with an immediate incoming queue and a delayed fence
+ // is activated, forcing a different queue to become active.
+ Initialize(2u);
+ test_task_runner_->SetAutoAdvanceNowToPendingTasks(true);
+
+ scoped_refptr<TestTaskQueue> queue1 = runners_[0];
+ scoped_refptr<TestTaskQueue> queue2 = runners_[1];
+
+ std::vector<std::pair<scoped_refptr<TestTaskQueue>, base::TimeTicks>>
+ run_times;
+
+ // Fence ensures that the task posted after advancing time is blocked.
+ queue1->InsertFenceAt(Now() + base::TimeDelta::FromMilliseconds(250));
+
+ // This task should not be blocked and should run immediately after
+ // advancing time at 301ms.
+ queue1->PostTask(FROM_HERE, base::BindOnce(&RecordTimeAndQueueTask,
+ &run_times, queue1, &now_src_));
+ // Force reload of immediate work queue. In real life the same effect can be
+ // achieved with cross-thread posting.
+ queue1->GetTaskQueueImpl()->ReloadImmediateWorkQueueIfEmpty();
+
+ now_src_.Advance(base::TimeDelta::FromMilliseconds(300));
+
+ // This task should be blocked.
+ queue1->PostTask(FROM_HERE, base::BindOnce(&RecordTimeAndQueueTask,
+ &run_times, queue1, &now_src_));
+ // This task on a different runner should run as expected.
+ queue2->PostTask(FROM_HERE, base::BindOnce(&RecordTimeAndQueueTask,
+ &run_times, queue2, &now_src_));
+
+ test_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(
+ run_times,
+ ElementsAre(
+ std::make_pair(queue1, base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(301)),
+ std::make_pair(queue2, base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(301))));
+}
+
+namespace {
+
+void ReentrantTestTask(scoped_refptr<base::SingleThreadTaskRunner> runner,
+ int countdown,
+ std::vector<EnqueueOrder>* out_result) {
+ out_result->push_back(countdown);
+ if (--countdown) {
+ runner->PostTask(
+ FROM_HERE, BindOnce(&ReentrantTestTask, runner, countdown, out_result));
+ }
+}
+
+} // namespace
+
+TEST_F(TaskQueueManagerTest, ReentrantPosting) {
+ Initialize(1u);
+
+ std::vector<EnqueueOrder> run_order;
+ runners_[0]->PostTask(
+ FROM_HERE, BindOnce(&ReentrantTestTask, runners_[0], 3, &run_order));
+
+ test_task_runner_->RunUntilIdle();
+ EXPECT_THAT(run_order, ElementsAre(3, 2, 1));
+}
+
+TEST_F(TaskQueueManagerTest, NoTasksAfterShutdown) {
+ Initialize(1u);
+
+ std::vector<EnqueueOrder> run_order;
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 1, &run_order));
+ manager_.reset();
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 1, &run_order));
+
+ test_task_runner_->RunUntilIdle();
+ EXPECT_TRUE(run_order.empty());
+}
+
+void PostTaskToRunner(scoped_refptr<base::SingleThreadTaskRunner> runner,
+ std::vector<EnqueueOrder>* run_order) {
+ runner->PostTask(FROM_HERE, base::BindOnce(&TestTask, 1, run_order));
+}
+
+TEST_F(TaskQueueManagerTest, PostFromThread) {
+ InitializeWithRealMessageLoop(1u);
+
+ std::vector<EnqueueOrder> run_order;
+ base::Thread thread("TestThread");
+ thread.Start();
+ thread.task_runner()->PostTask(
+ FROM_HERE, base::BindOnce(&PostTaskToRunner, runners_[0], &run_order));
+ thread.Stop();
+
+ base::RunLoop().RunUntilIdle();
+ EXPECT_THAT(run_order, ElementsAre(1));
+}
+
+void RePostingTestTask(scoped_refptr<base::SingleThreadTaskRunner> runner,
+ int* run_count) {
+ (*run_count)++;
+ runner->PostTask(
+ FROM_HERE,
+ BindOnce(&RePostingTestTask, base::Unretained(runner.get()), run_count));
+}
+
+TEST_F(TaskQueueManagerTest, DoWorkCantPostItselfMultipleTimes) {
+ Initialize(1u);
+
+ int run_count = 0;
+ runners_[0]->PostTask(
+ FROM_HERE, base::BindOnce(&RePostingTestTask, runners_[0], &run_count));
+
+ test_task_runner_->RunPendingTasks();
+ // NOTE without the executing_task_ check in MaybeScheduleDoWork there
+ // will be two tasks here.
+ EXPECT_EQ(1u, test_task_runner_->NumPendingTasks());
+ EXPECT_EQ(1, run_count);
+}
+
+TEST_F(TaskQueueManagerTest, PostFromNestedRunloop) {
+ InitializeWithRealMessageLoop(1u);
+
+ std::vector<EnqueueOrder> run_order;
+ std::vector<std::pair<base::OnceClosure, bool>>
+ tasks_to_post_from_nested_loop;
+ tasks_to_post_from_nested_loop.push_back(
+ std::make_pair(base::BindOnce(&TestTask, 1, &run_order), true));
+
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 0, &run_order));
+ runners_[0]->PostTask(
+ FROM_HERE,
+ base::BindOnce(&PostFromNestedRunloop, message_loop_.get(),
+ base::RetainedRef(runners_[0]),
+ base::Unretained(&tasks_to_post_from_nested_loop)));
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 2, &run_order));
+
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_THAT(run_order, ElementsAre(0, 2, 1));
+}
+
+TEST_F(TaskQueueManagerTest, WorkBatching) {
+ Initialize(1u);
+
+ manager_->SetWorkBatchSize(2);
+
+ std::vector<EnqueueOrder> run_order;
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 1, &run_order));
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 2, &run_order));
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 3, &run_order));
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 4, &run_order));
+
+ // Running one task in the host message loop should cause two posted tasks to
+ // get executed.
+ EXPECT_EQ(test_task_runner_->NumPendingTasks(), 1u);
+ test_task_runner_->RunPendingTasks();
+ EXPECT_THAT(run_order, ElementsAre(1, 2));
+
+ // The second task runs the remaining two posted tasks.
+ EXPECT_EQ(test_task_runner_->NumPendingTasks(), 1u);
+ test_task_runner_->RunPendingTasks();
+ EXPECT_THAT(run_order, ElementsAre(1, 2, 3, 4));
+}
+
+class MockTaskObserver : public base::MessageLoop::TaskObserver {
+ public:
+ MOCK_METHOD1(DidProcessTask, void(const base::PendingTask& task));
+ MOCK_METHOD1(WillProcessTask, void(const base::PendingTask& task));
+};
+
+TEST_F(TaskQueueManagerTest, TaskObserverAdding) {
+ InitializeWithRealMessageLoop(1u);
+ MockTaskObserver observer;
+
+ manager_->SetWorkBatchSize(2);
+ manager_->AddTaskObserver(&observer);
+
+ std::vector<EnqueueOrder> run_order;
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 1, &run_order));
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 2, &run_order));
+
+ EXPECT_CALL(observer, WillProcessTask(_)).Times(2);
+ EXPECT_CALL(observer, DidProcessTask(_)).Times(2);
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(TaskQueueManagerTest, TaskObserverRemoving) {
+ InitializeWithRealMessageLoop(1u);
+ MockTaskObserver observer;
+ manager_->SetWorkBatchSize(2);
+ manager_->AddTaskObserver(&observer);
+ manager_->RemoveTaskObserver(&observer);
+
+ std::vector<EnqueueOrder> run_order;
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 1, &run_order));
+
+ EXPECT_CALL(observer, WillProcessTask(_)).Times(0);
+ EXPECT_CALL(observer, DidProcessTask(_)).Times(0);
+
+ base::RunLoop().RunUntilIdle();
+}
+
+void RemoveObserverTask(TaskQueueManagerImpl* manager,
+ base::MessageLoop::TaskObserver* observer) {
+ manager->RemoveTaskObserver(observer);
+}
+
+TEST_F(TaskQueueManagerTest, TaskObserverRemovingInsideTask) {
+ InitializeWithRealMessageLoop(1u);
+ MockTaskObserver observer;
+ manager_->SetWorkBatchSize(3);
+ manager_->AddTaskObserver(&observer);
+
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&RemoveObserverTask,
+ manager_.get(), &observer));
+
+ EXPECT_CALL(observer, WillProcessTask(_)).Times(1);
+ EXPECT_CALL(observer, DidProcessTask(_)).Times(0);
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(TaskQueueManagerTest, QueueTaskObserverAdding) {
+ InitializeWithRealMessageLoop(2u);
+ MockTaskObserver observer;
+
+ manager_->SetWorkBatchSize(2);
+ runners_[0]->AddTaskObserver(&observer);
+
+ std::vector<EnqueueOrder> run_order;
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 1, &run_order));
+ runners_[1]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 2, &run_order));
+
+ EXPECT_CALL(observer, WillProcessTask(_)).Times(1);
+ EXPECT_CALL(observer, DidProcessTask(_)).Times(1);
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(TaskQueueManagerTest, QueueTaskObserverRemoving) {
+ InitializeWithRealMessageLoop(1u);
+ MockTaskObserver observer;
+ manager_->SetWorkBatchSize(2);
+ runners_[0]->AddTaskObserver(&observer);
+ runners_[0]->RemoveTaskObserver(&observer);
+
+ std::vector<EnqueueOrder> run_order;
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 1, &run_order));
+
+ EXPECT_CALL(observer, WillProcessTask(_)).Times(0);
+ EXPECT_CALL(observer, DidProcessTask(_)).Times(0);
+
+ base::RunLoop().RunUntilIdle();
+}
+
+void RemoveQueueObserverTask(scoped_refptr<TaskQueue> queue,
+ base::MessageLoop::TaskObserver* observer) {
+ queue->RemoveTaskObserver(observer);
+}
+
+TEST_F(TaskQueueManagerTest, QueueTaskObserverRemovingInsideTask) {
+ InitializeWithRealMessageLoop(1u);
+ MockTaskObserver observer;
+ runners_[0]->AddTaskObserver(&observer);
+
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&RemoveQueueObserverTask,
+ runners_[0], &observer));
+
+ EXPECT_CALL(observer, WillProcessTask(_)).Times(1);
+ EXPECT_CALL(observer, DidProcessTask(_)).Times(0);
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(TaskQueueManagerTest, ThreadCheckAfterTermination) {
+ Initialize(1u);
+ EXPECT_TRUE(runners_[0]->RunsTasksInCurrentSequence());
+ manager_.reset();
+ EXPECT_TRUE(runners_[0]->RunsTasksInCurrentSequence());
+}
+
+TEST_F(TaskQueueManagerTest, TimeDomain_NextScheduledRunTime) {
+ Initialize(2u);
+ now_src_.Advance(base::TimeDelta::FromMicroseconds(10000));
+
+ // With no delayed tasks.
+ base::TimeTicks run_time;
+ EXPECT_FALSE(manager_->GetRealTimeDomain()->NextScheduledRunTime(&run_time));
+
+ // With a non-delayed task.
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&NopTask));
+ EXPECT_FALSE(manager_->GetRealTimeDomain()->NextScheduledRunTime(&run_time));
+
+ // With a delayed task.
+ base::TimeDelta expected_delay = base::TimeDelta::FromMilliseconds(50);
+ runners_[0]->PostDelayedTask(FROM_HERE, base::BindOnce(&NopTask),
+ expected_delay);
+ EXPECT_TRUE(manager_->GetRealTimeDomain()->NextScheduledRunTime(&run_time));
+ EXPECT_EQ(now_src_.NowTicks() + expected_delay, run_time);
+
+ // With another delayed task in the same queue with a longer delay.
+ runners_[0]->PostDelayedTask(FROM_HERE, base::BindOnce(&NopTask),
+ base::TimeDelta::FromMilliseconds(100));
+ EXPECT_TRUE(manager_->GetRealTimeDomain()->NextScheduledRunTime(&run_time));
+ EXPECT_EQ(now_src_.NowTicks() + expected_delay, run_time);
+
+ // With another delayed task in the same queue with a shorter delay.
+ expected_delay = base::TimeDelta::FromMilliseconds(20);
+ runners_[0]->PostDelayedTask(FROM_HERE, base::BindOnce(&NopTask),
+ expected_delay);
+ EXPECT_TRUE(manager_->GetRealTimeDomain()->NextScheduledRunTime(&run_time));
+ EXPECT_EQ(now_src_.NowTicks() + expected_delay, run_time);
+
+ // With another delayed task in a different queue with a shorter delay.
+ expected_delay = base::TimeDelta::FromMilliseconds(10);
+ runners_[1]->PostDelayedTask(FROM_HERE, base::BindOnce(&NopTask),
+ expected_delay);
+ EXPECT_TRUE(manager_->GetRealTimeDomain()->NextScheduledRunTime(&run_time));
+ EXPECT_EQ(now_src_.NowTicks() + expected_delay, run_time);
+
+ // Test it updates as time progresses
+ now_src_.Advance(expected_delay);
+ EXPECT_TRUE(manager_->GetRealTimeDomain()->NextScheduledRunTime(&run_time));
+ EXPECT_EQ(now_src_.NowTicks(), run_time);
+}
+
+TEST_F(TaskQueueManagerTest, TimeDomain_NextScheduledRunTime_MultipleQueues) {
+ Initialize(3u);
+
+ base::TimeDelta delay1 = base::TimeDelta::FromMilliseconds(50);
+ base::TimeDelta delay2 = base::TimeDelta::FromMilliseconds(5);
+ base::TimeDelta delay3 = base::TimeDelta::FromMilliseconds(10);
+ runners_[0]->PostDelayedTask(FROM_HERE, base::BindOnce(&NopTask), delay1);
+ runners_[1]->PostDelayedTask(FROM_HERE, base::BindOnce(&NopTask), delay2);
+ runners_[2]->PostDelayedTask(FROM_HERE, base::BindOnce(&NopTask), delay3);
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&NopTask));
+
+ base::TimeTicks run_time;
+ EXPECT_TRUE(manager_->GetRealTimeDomain()->NextScheduledRunTime(&run_time));
+ EXPECT_EQ(now_src_.NowTicks() + delay2, run_time);
+}
+
+TEST_F(TaskQueueManagerTest, DeleteTaskQueueManagerInsideATask) {
+ Initialize(1u);
+
+ runners_[0]->PostTask(
+ FROM_HERE, base::BindOnce(&TaskQueueManagerTest::DeleteTaskQueueManager,
+ base::Unretained(this)));
+
+ // This should not crash, assuming DoWork detects the TaskQueueManager has
+ // been deleted.
+ test_task_runner_->RunUntilIdle();
+}
+
+TEST_F(TaskQueueManagerTest, GetAndClearSystemIsQuiescentBit) {
+ Initialize(3u);
+
+ scoped_refptr<TaskQueue> queue0 = CreateTaskQueueWithMonitoredQuiescence();
+ scoped_refptr<TaskQueue> queue1 = CreateTaskQueueWithMonitoredQuiescence();
+ scoped_refptr<TaskQueue> queue2 = CreateTaskQueue();
+
+ EXPECT_TRUE(manager_->GetAndClearSystemIsQuiescentBit());
+
+ queue0->PostTask(FROM_HERE, base::BindOnce(&NopTask));
+ test_task_runner_->RunUntilIdle();
+ EXPECT_FALSE(manager_->GetAndClearSystemIsQuiescentBit());
+ EXPECT_TRUE(manager_->GetAndClearSystemIsQuiescentBit());
+
+ queue1->PostTask(FROM_HERE, base::BindOnce(&NopTask));
+ test_task_runner_->RunUntilIdle();
+ EXPECT_FALSE(manager_->GetAndClearSystemIsQuiescentBit());
+ EXPECT_TRUE(manager_->GetAndClearSystemIsQuiescentBit());
+
+ queue2->PostTask(FROM_HERE, base::BindOnce(&NopTask));
+ test_task_runner_->RunUntilIdle();
+ EXPECT_TRUE(manager_->GetAndClearSystemIsQuiescentBit());
+
+ queue0->PostTask(FROM_HERE, base::BindOnce(&NopTask));
+ queue1->PostTask(FROM_HERE, base::BindOnce(&NopTask));
+ test_task_runner_->RunUntilIdle();
+ EXPECT_FALSE(manager_->GetAndClearSystemIsQuiescentBit());
+ EXPECT_TRUE(manager_->GetAndClearSystemIsQuiescentBit());
+}
+
+TEST_F(TaskQueueManagerTest, HasPendingImmediateWork) {
+ Initialize(1u);
+
+ EXPECT_FALSE(runners_[0]->HasTaskToRunImmediately());
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(NullTask));
+ EXPECT_TRUE(runners_[0]->HasTaskToRunImmediately());
+
+ test_task_runner_->RunUntilIdle();
+ EXPECT_FALSE(runners_[0]->HasTaskToRunImmediately());
+}
+
+TEST_F(TaskQueueManagerTest, HasPendingImmediateWork_DelayedTasks) {
+ Initialize(1u);
+
+ EXPECT_FALSE(runners_[0]->HasTaskToRunImmediately());
+ runners_[0]->PostDelayedTask(FROM_HERE, base::BindOnce(NullTask),
+ base::TimeDelta::FromMilliseconds(12));
+ EXPECT_FALSE(runners_[0]->HasTaskToRunImmediately());
+
+ // Move time forwards until just before the delayed task should run.
+ now_src_.Advance(base::TimeDelta::FromMilliseconds(10));
+ WakeUpReadyDelayedQueues(LazyNow(&now_src_));
+ EXPECT_FALSE(runners_[0]->HasTaskToRunImmediately());
+
+ // Force the delayed task onto the work queue.
+ now_src_.Advance(base::TimeDelta::FromMilliseconds(2));
+ WakeUpReadyDelayedQueues(LazyNow(&now_src_));
+ EXPECT_TRUE(runners_[0]->HasTaskToRunImmediately());
+
+ test_task_runner_->RunUntilIdle();
+ EXPECT_FALSE(runners_[0]->HasTaskToRunImmediately());
+}
+
+void ExpensiveTestTask(int value,
+ base::SimpleTestTickClock* clock,
+ std::vector<EnqueueOrder>* out_result) {
+ out_result->push_back(value);
+ clock->Advance(base::TimeDelta::FromMilliseconds(1));
+}
+
+TEST_F(TaskQueueManagerTest, ImmediateAndDelayedTaskInterleaving) {
+ Initialize(1u);
+
+ std::vector<EnqueueOrder> run_order;
+ base::TimeDelta delay = base::TimeDelta::FromMilliseconds(10);
+ for (int i = 10; i < 19; i++) {
+ runners_[0]->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&ExpensiveTestTask, i, &now_src_, &run_order),
+ delay);
+ }
+
+ test_task_runner_->RunForPeriod(delay);
+
+ for (int i = 0; i < 9; i++) {
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&ExpensiveTestTask, i,
+ &now_src_, &run_order));
+ }
+
+ test_task_runner_->SetAutoAdvanceNowToPendingTasks(true);
+ test_task_runner_->RunUntilIdle();
+
+ // Delayed tasks are not allowed to starve out immediate work which is why
+ // some of the immediate tasks run out of order.
+ int expected_run_order[] = {10, 11, 12, 13, 0, 14, 15, 16, 1,
+ 17, 18, 2, 3, 4, 5, 6, 7, 8};
+ EXPECT_THAT(run_order, ElementsAreArray(expected_run_order));
+}
+
+TEST_F(TaskQueueManagerTest,
+ DelayedTaskDoesNotSkipAHeadOfNonDelayedTask_SameQueue) {
+ Initialize(1u);
+
+ std::vector<EnqueueOrder> run_order;
+ base::TimeDelta delay = base::TimeDelta::FromMilliseconds(10);
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 2, &run_order));
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 3, &run_order));
+ runners_[0]->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&TestTask, 1, &run_order), delay);
+
+ now_src_.Advance(delay * 2);
+ test_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(run_order, ElementsAre(2, 3, 1));
+}
+
+TEST_F(TaskQueueManagerTest,
+ DelayedTaskDoesNotSkipAHeadOfNonDelayedTask_DifferentQueues) {
+ Initialize(2u);
+
+ std::vector<EnqueueOrder> run_order;
+ base::TimeDelta delay = base::TimeDelta::FromMilliseconds(10);
+ runners_[1]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 2, &run_order));
+ runners_[1]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 3, &run_order));
+ runners_[0]->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&TestTask, 1, &run_order), delay);
+
+ now_src_.Advance(delay * 2);
+ test_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(run_order, ElementsAre(2, 3, 1));
+}
+
+TEST_F(TaskQueueManagerTest, DelayedTaskDoesNotSkipAHeadOfShorterDelayedTask) {
+ Initialize(2u);
+
+ std::vector<EnqueueOrder> run_order;
+ base::TimeDelta delay1 = base::TimeDelta::FromMilliseconds(10);
+ base::TimeDelta delay2 = base::TimeDelta::FromMilliseconds(5);
+ runners_[0]->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, 1, &run_order), delay1);
+ runners_[1]->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, 2, &run_order), delay2);
+
+ now_src_.Advance(delay1 * 2);
+ test_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(run_order, ElementsAre(2, 1));
+}
+
+void CheckIsNested(bool* is_nested) {
+ *is_nested = base::RunLoop::IsNestedOnCurrentThread();
+}
+
+void PostAndQuitFromNestedRunloop(base::RunLoop* run_loop,
+ base::SingleThreadTaskRunner* runner,
+ bool* was_nested) {
+ base::MessageLoop::ScopedNestableTaskAllower allow(
+ base::MessageLoop::current());
+ runner->PostTask(FROM_HERE, run_loop->QuitClosure());
+ runner->PostTask(FROM_HERE, base::BindOnce(&CheckIsNested, was_nested));
+ run_loop->Run();
+}
+
+TEST_F(TaskQueueManagerTest, QuitWhileNested) {
+ // This test makes sure we don't continue running a work batch after a nested
+ // run loop has been exited in the middle of the batch.
+ InitializeWithRealMessageLoop(1u);
+ manager_->SetWorkBatchSize(2);
+
+ bool was_nested = true;
+ base::RunLoop run_loop;
+ runners_[0]->PostTask(
+ FROM_HERE,
+ base::BindOnce(&PostAndQuitFromNestedRunloop, base::Unretained(&run_loop),
+ base::RetainedRef(runners_[0]),
+ base::Unretained(&was_nested)));
+
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(was_nested);
+}
+
+class SequenceNumberCapturingTaskObserver
+ : public base::MessageLoop::TaskObserver {
+ public:
+ // MessageLoop::TaskObserver overrides.
+ void WillProcessTask(const base::PendingTask& pending_task) override {}
+ void DidProcessTask(const base::PendingTask& pending_task) override {
+ sequence_numbers_.push_back(pending_task.sequence_num);
+ }
+
+ const std::vector<EnqueueOrder>& sequence_numbers() const {
+ return sequence_numbers_;
+ }
+
+ private:
+ std::vector<EnqueueOrder> sequence_numbers_;
+};
+
+TEST_F(TaskQueueManagerTest, SequenceNumSetWhenTaskIsPosted) {
+ Initialize(1u);
+
+ SequenceNumberCapturingTaskObserver observer;
+ manager_->AddTaskObserver(&observer);
+
+ // Register four tasks that will run in reverse order.
+ std::vector<EnqueueOrder> run_order;
+ runners_[0]->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&TestTask, 1, &run_order),
+ base::TimeDelta::FromMilliseconds(30));
+ runners_[0]->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&TestTask, 2, &run_order),
+ base::TimeDelta::FromMilliseconds(20));
+ runners_[0]->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&TestTask, 3, &run_order),
+ base::TimeDelta::FromMilliseconds(10));
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 4, &run_order));
+
+ test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(40));
+ ASSERT_THAT(run_order, ElementsAre(4, 3, 2, 1));
+
+ // The sequence numbers are a one-based monotonically incrememting counter
+ // which should be set when the task is posted rather than when it's enqueued
+ // onto the Incoming queue. This counter starts with 2.
+ EXPECT_THAT(observer.sequence_numbers(), ElementsAre(5, 4, 3, 2));
+
+ manager_->RemoveTaskObserver(&observer);
+}
+
+TEST_F(TaskQueueManagerTest, NewTaskQueues) {
+ Initialize(1u);
+
+ scoped_refptr<TaskQueue> queue1 = CreateTaskQueue();
+ scoped_refptr<TaskQueue> queue2 = CreateTaskQueue();
+ scoped_refptr<TaskQueue> queue3 = CreateTaskQueue();
+
+ ASSERT_NE(queue1, queue2);
+ ASSERT_NE(queue1, queue3);
+ ASSERT_NE(queue2, queue3);
+
+ std::vector<EnqueueOrder> run_order;
+ queue1->PostTask(FROM_HERE, base::BindOnce(&TestTask, 1, &run_order));
+ queue2->PostTask(FROM_HERE, base::BindOnce(&TestTask, 2, &run_order));
+ queue3->PostTask(FROM_HERE, base::BindOnce(&TestTask, 3, &run_order));
+ test_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(run_order, ElementsAre(1, 2, 3));
+}
+
+TEST_F(TaskQueueManagerTest, ShutdownTaskQueue) {
+ Initialize(1u);
+
+ scoped_refptr<TaskQueue> queue1 = CreateTaskQueue();
+ scoped_refptr<TaskQueue> queue2 = CreateTaskQueue();
+ scoped_refptr<TaskQueue> queue3 = CreateTaskQueue();
+
+ ASSERT_NE(queue1, queue2);
+ ASSERT_NE(queue1, queue3);
+ ASSERT_NE(queue2, queue3);
+
+ std::vector<EnqueueOrder> run_order;
+ queue1->PostTask(FROM_HERE, base::BindOnce(&TestTask, 1, &run_order));
+ queue2->PostTask(FROM_HERE, base::BindOnce(&TestTask, 2, &run_order));
+ queue3->PostTask(FROM_HERE, base::BindOnce(&TestTask, 3, &run_order));
+
+ queue2->ShutdownTaskQueue();
+ test_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(run_order, ElementsAre(1, 3));
+}
+
+TEST_F(TaskQueueManagerTest, ShutdownTaskQueue_WithDelayedTasks) {
+ Initialize(2u);
+
+ // Register three delayed tasks
+ std::vector<EnqueueOrder> run_order;
+ runners_[0]->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&TestTask, 1, &run_order),
+ base::TimeDelta::FromMilliseconds(10));
+ runners_[1]->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&TestTask, 2, &run_order),
+ base::TimeDelta::FromMilliseconds(20));
+ runners_[0]->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&TestTask, 3, &run_order),
+ base::TimeDelta::FromMilliseconds(30));
+
+ runners_[1]->ShutdownTaskQueue();
+ test_task_runner_->RunUntilIdle();
+
+ test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(40));
+ ASSERT_THAT(run_order, ElementsAre(1, 3));
+}
+
+namespace {
+void ShutdownQueue(scoped_refptr<TaskQueue> queue) {
+ queue->ShutdownTaskQueue();
+}
+} // namespace
+
+TEST_F(TaskQueueManagerTest, ShutdownTaskQueue_InTasks) {
+ Initialize(3u);
+
+ std::vector<EnqueueOrder> run_order;
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 1, &run_order));
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&ShutdownQueue, runners_[1]));
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&ShutdownQueue, runners_[2]));
+ runners_[1]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 2, &run_order));
+ runners_[2]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 3, &run_order));
+
+ test_task_runner_->RunUntilIdle();
+ ASSERT_THAT(run_order, ElementsAre(1));
+}
+
+namespace {
+
+class MockObserver : public TaskQueueManager::Observer {
+ public:
+ MOCK_METHOD0(OnTriedToExecuteBlockedTask, void());
+ MOCK_METHOD0(OnBeginNestedRunLoop, void());
+ MOCK_METHOD0(OnExitNestedRunLoop, void());
+};
+
+} // namespace
+
+TEST_F(TaskQueueManagerTest, ShutdownTaskQueueInNestedLoop) {
+ InitializeWithRealMessageLoop(1u);
+
+ // We retain a reference to the task queue even when the manager has deleted
+ // its reference.
+ scoped_refptr<TaskQueue> task_queue = CreateTaskQueue();
+
+ std::vector<bool> log;
+ std::vector<std::pair<base::OnceClosure, bool>>
+ tasks_to_post_from_nested_loop;
+
+ // Inside a nested run loop, call task_queue->ShutdownTaskQueue, bookended
+ // by calls to HasOneRefTask to make sure the manager doesn't release its
+ // reference until the nested run loop exits.
+ // NB: This first HasOneRefTask is a sanity check.
+ tasks_to_post_from_nested_loop.push_back(
+ std::make_pair(base::BindOnce(&NopTask), true));
+ tasks_to_post_from_nested_loop.push_back(
+ std::make_pair(base::BindOnce(&TaskQueue::ShutdownTaskQueue,
+ base::Unretained(task_queue.get())),
+ true));
+ tasks_to_post_from_nested_loop.push_back(
+ std::make_pair(base::BindOnce(&NopTask), true));
+ runners_[0]->PostTask(
+ FROM_HERE,
+ base::BindOnce(&PostFromNestedRunloop, message_loop_.get(),
+ base::RetainedRef(runners_[0]),
+ base::Unretained(&tasks_to_post_from_nested_loop)));
+ base::RunLoop().RunUntilIdle();
+
+ // Just make sure that we don't crash.
+}
+
+TEST_F(TaskQueueManagerTest, TimeDomainsAreIndependant) {
+ Initialize(2u);
+
+ base::TimeTicks start_time_ticks = manager_->NowTicks();
+ std::unique_ptr<VirtualTimeDomain> domain_a(
+ new VirtualTimeDomain(start_time_ticks));
+ std::unique_ptr<VirtualTimeDomain> domain_b(
+ new VirtualTimeDomain(start_time_ticks));
+ manager_->RegisterTimeDomain(domain_a.get());
+ manager_->RegisterTimeDomain(domain_b.get());
+ runners_[0]->SetTimeDomain(domain_a.get());
+ runners_[1]->SetTimeDomain(domain_b.get());
+
+ std::vector<EnqueueOrder> run_order;
+ runners_[0]->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&TestTask, 1, &run_order),
+ base::TimeDelta::FromMilliseconds(10));
+ runners_[0]->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&TestTask, 2, &run_order),
+ base::TimeDelta::FromMilliseconds(20));
+ runners_[0]->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&TestTask, 3, &run_order),
+ base::TimeDelta::FromMilliseconds(30));
+
+ runners_[1]->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&TestTask, 4, &run_order),
+ base::TimeDelta::FromMilliseconds(10));
+ runners_[1]->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&TestTask, 5, &run_order),
+ base::TimeDelta::FromMilliseconds(20));
+ runners_[1]->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&TestTask, 6, &run_order),
+ base::TimeDelta::FromMilliseconds(30));
+
+ domain_b->AdvanceNowTo(start_time_ticks +
+ base::TimeDelta::FromMilliseconds(50));
+ manager_->MaybeScheduleImmediateWork(FROM_HERE);
+
+ test_task_runner_->RunUntilIdle();
+ EXPECT_THAT(run_order, ElementsAre(4, 5, 6));
+
+ domain_a->AdvanceNowTo(start_time_ticks +
+ base::TimeDelta::FromMilliseconds(50));
+ manager_->MaybeScheduleImmediateWork(FROM_HERE);
+
+ test_task_runner_->RunUntilIdle();
+ EXPECT_THAT(run_order, ElementsAre(4, 5, 6, 1, 2, 3));
+
+ runners_[0]->ShutdownTaskQueue();
+ runners_[1]->ShutdownTaskQueue();
+
+ manager_->UnregisterTimeDomain(domain_a.get());
+ manager_->UnregisterTimeDomain(domain_b.get());
+}
+
+TEST_F(TaskQueueManagerTest, TimeDomainMigration) {
+ Initialize(1u);
+
+ base::TimeTicks start_time_ticks = manager_->NowTicks();
+ std::unique_ptr<VirtualTimeDomain> domain_a(
+ new VirtualTimeDomain(start_time_ticks));
+ manager_->RegisterTimeDomain(domain_a.get());
+ runners_[0]->SetTimeDomain(domain_a.get());
+
+ std::vector<EnqueueOrder> run_order;
+ runners_[0]->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&TestTask, 1, &run_order),
+ base::TimeDelta::FromMilliseconds(10));
+ runners_[0]->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&TestTask, 2, &run_order),
+ base::TimeDelta::FromMilliseconds(20));
+ runners_[0]->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&TestTask, 3, &run_order),
+ base::TimeDelta::FromMilliseconds(30));
+ runners_[0]->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&TestTask, 4, &run_order),
+ base::TimeDelta::FromMilliseconds(40));
+
+ domain_a->AdvanceNowTo(start_time_ticks +
+ base::TimeDelta::FromMilliseconds(20));
+ manager_->MaybeScheduleImmediateWork(FROM_HERE);
+ test_task_runner_->RunUntilIdle();
+ EXPECT_THAT(run_order, ElementsAre(1, 2));
+
+ std::unique_ptr<VirtualTimeDomain> domain_b(
+ new VirtualTimeDomain(start_time_ticks));
+ manager_->RegisterTimeDomain(domain_b.get());
+ runners_[0]->SetTimeDomain(domain_b.get());
+
+ domain_b->AdvanceNowTo(start_time_ticks +
+ base::TimeDelta::FromMilliseconds(50));
+ manager_->MaybeScheduleImmediateWork(FROM_HERE);
+
+ test_task_runner_->RunUntilIdle();
+ EXPECT_THAT(run_order, ElementsAre(1, 2, 3, 4));
+
+ runners_[0]->ShutdownTaskQueue();
+
+ manager_->UnregisterTimeDomain(domain_a.get());
+ manager_->UnregisterTimeDomain(domain_b.get());
+}
+
+TEST_F(TaskQueueManagerTest, TimeDomainMigrationWithIncomingImmediateTasks) {
+ Initialize(1u);
+
+ base::TimeTicks start_time_ticks = manager_->NowTicks();
+ std::unique_ptr<VirtualTimeDomain> domain_a(
+ new VirtualTimeDomain(start_time_ticks));
+ std::unique_ptr<VirtualTimeDomain> domain_b(
+ new VirtualTimeDomain(start_time_ticks));
+ manager_->RegisterTimeDomain(domain_a.get());
+ manager_->RegisterTimeDomain(domain_b.get());
+
+ runners_[0]->SetTimeDomain(domain_a.get());
+ std::vector<EnqueueOrder> run_order;
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 1, &run_order));
+ runners_[0]->SetTimeDomain(domain_b.get());
+
+ test_task_runner_->RunUntilIdle();
+ EXPECT_THAT(run_order, ElementsAre(1));
+
+ runners_[0]->ShutdownTaskQueue();
+
+ manager_->UnregisterTimeDomain(domain_a.get());
+ manager_->UnregisterTimeDomain(domain_b.get());
+}
+
+TEST_F(TaskQueueManagerTest,
+ PostDelayedTasksReverseOrderAlternatingTimeDomains) {
+ Initialize(1u);
+
+ std::vector<EnqueueOrder> run_order;
+
+ std::unique_ptr<RealTimeDomain> domain_a(new RealTimeDomain());
+ std::unique_ptr<RealTimeDomain> domain_b(new RealTimeDomain());
+ manager_->RegisterTimeDomain(domain_a.get());
+ manager_->RegisterTimeDomain(domain_b.get());
+
+ runners_[0]->SetTimeDomain(domain_a.get());
+ runners_[0]->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&TestTask, 1, &run_order),
+ base::TimeDelta::FromMilliseconds(40));
+
+ runners_[0]->SetTimeDomain(domain_b.get());
+ runners_[0]->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&TestTask, 2, &run_order),
+ base::TimeDelta::FromMilliseconds(30));
+
+ runners_[0]->SetTimeDomain(domain_a.get());
+ runners_[0]->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&TestTask, 3, &run_order),
+ base::TimeDelta::FromMilliseconds(20));
+
+ runners_[0]->SetTimeDomain(domain_b.get());
+ runners_[0]->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&TestTask, 4, &run_order),
+ base::TimeDelta::FromMilliseconds(10));
+
+ test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(40));
+ EXPECT_THAT(run_order, ElementsAre(4, 3, 2, 1));
+
+ runners_[0]->ShutdownTaskQueue();
+
+ manager_->UnregisterTimeDomain(domain_a.get());
+ manager_->UnregisterTimeDomain(domain_b.get());
+}
+
+namespace {
+
+class MockTaskQueueObserver : public TaskQueue::Observer {
+ public:
+ ~MockTaskQueueObserver() override = default;
+
+ MOCK_METHOD2(OnQueueNextWakeUpChanged, void(TaskQueue*, base::TimeTicks));
+};
+
+} // namespace
+
+TEST_F(TaskQueueManagerTest, TaskQueueObserver_ImmediateTask) {
+ Initialize(1u);
+
+ MockTaskQueueObserver observer;
+ runners_[0]->SetObserver(&observer);
+
+ // We should get a notification when a task is posted on an empty queue.
+ EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[0].get(), _));
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&NopTask));
+ Mock::VerifyAndClearExpectations(&observer);
+
+ // But not subsequently.
+ EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _)).Times(0);
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&NopTask));
+ Mock::VerifyAndClearExpectations(&observer);
+
+ // Unless the immediate work queue is emptied.
+ runners_[0]->GetTaskQueueImpl()->ReloadImmediateWorkQueueIfEmpty();
+ EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[0].get(), _));
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&NopTask));
+
+ // Tidy up.
+ runners_[0]->ShutdownTaskQueue();
+}
+
+TEST_F(TaskQueueManagerTest, TaskQueueObserver_DelayedTask) {
+ Initialize(1u);
+
+ base::TimeTicks start_time = manager_->NowTicks();
+ base::TimeDelta delay10s(base::TimeDelta::FromSeconds(10));
+ base::TimeDelta delay100s(base::TimeDelta::FromSeconds(100));
+ base::TimeDelta delay1s(base::TimeDelta::FromSeconds(1));
+
+ MockTaskQueueObserver observer;
+ runners_[0]->SetObserver(&observer);
+
+ // We should get a notification when a delayed task is posted on an empty
+ // queue.
+ EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[0].get(),
+ start_time + delay10s));
+ runners_[0]->PostDelayedTask(FROM_HERE, base::BindOnce(&NopTask), delay10s);
+ Mock::VerifyAndClearExpectations(&observer);
+
+ // We should not get a notification for a longer delay.
+ EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _)).Times(0);
+ runners_[0]->PostDelayedTask(FROM_HERE, base::BindOnce(&NopTask), delay100s);
+ Mock::VerifyAndClearExpectations(&observer);
+
+ // We should get a notification for a shorter delay.
+ EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[0].get(),
+ start_time + delay1s));
+ runners_[0]->PostDelayedTask(FROM_HERE, base::BindOnce(&NopTask), delay1s);
+ Mock::VerifyAndClearExpectations(&observer);
+
+ std::unique_ptr<TaskQueue::QueueEnabledVoter> voter =
+ runners_[0]->CreateQueueEnabledVoter();
+ voter->SetQueueEnabled(false);
+ Mock::VerifyAndClearExpectations(&observer);
+
+ // When a queue has been enabled, we may get a notification if the
+ // TimeDomain's next scheduled wake-up has changed.
+ EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[0].get(),
+ start_time + delay1s));
+ voter->SetQueueEnabled(true);
+ Mock::VerifyAndClearExpectations(&observer);
+
+ // Tidy up.
+ runners_[0]->ShutdownTaskQueue();
+}
+
+TEST_F(TaskQueueManagerTest, TaskQueueObserver_DelayedTaskMultipleQueues) {
+ Initialize(2u);
+
+ MockTaskQueueObserver observer;
+ runners_[0]->SetObserver(&observer);
+ runners_[1]->SetObserver(&observer);
+
+ base::TimeTicks start_time = manager_->NowTicks();
+ base::TimeDelta delay1s(base::TimeDelta::FromSeconds(1));
+ base::TimeDelta delay10s(base::TimeDelta::FromSeconds(10));
+
+ EXPECT_CALL(observer,
+ OnQueueNextWakeUpChanged(runners_[0].get(), start_time + delay1s))
+ .Times(1);
+ EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[1].get(),
+ start_time + delay10s))
+ .Times(1);
+ runners_[0]->PostDelayedTask(FROM_HERE, base::BindOnce(&NopTask), delay1s);
+ runners_[1]->PostDelayedTask(FROM_HERE, base::BindOnce(&NopTask), delay10s);
+ testing::Mock::VerifyAndClearExpectations(&observer);
+
+ std::unique_ptr<TaskQueue::QueueEnabledVoter> voter0 =
+ runners_[0]->CreateQueueEnabledVoter();
+ std::unique_ptr<TaskQueue::QueueEnabledVoter> voter1 =
+ runners_[1]->CreateQueueEnabledVoter();
+
+ // Disabling a queue should not trigger a notification.
+ EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _)).Times(0);
+ voter0->SetQueueEnabled(false);
+ Mock::VerifyAndClearExpectations(&observer);
+
+ // Re-enabling it should should also trigger a notification.
+ EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[0].get(),
+ start_time + delay1s));
+ voter0->SetQueueEnabled(true);
+ Mock::VerifyAndClearExpectations(&observer);
+
+ // Disabling a queue should not trigger a notification.
+ EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _)).Times(0);
+ voter1->SetQueueEnabled(false);
+ Mock::VerifyAndClearExpectations(&observer);
+
+ // Re-enabling it should should trigger a notification.
+ EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[1].get(),
+ start_time + delay10s));
+ voter1->SetQueueEnabled(true);
+ Mock::VerifyAndClearExpectations(&observer);
+
+ // Tidy up.
+ EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _)).Times(AnyNumber());
+ runners_[0]->ShutdownTaskQueue();
+ runners_[1]->ShutdownTaskQueue();
+}
+
+TEST_F(TaskQueueManagerTest, TaskQueueObserver_DelayedWorkWhichCanRunNow) {
+ // This test checks that when delayed work becomes available
+ // the notification still fires. This usually happens when time advances
+ // and task becomes available in the middle of the scheduling code.
+ // For this test we rely on the fact that notification dispatching code
+ // is the same in all conditions and just change a time domain to
+ // trigger notification.
+
+ Initialize(1u);
+
+ base::TimeDelta delay1s(base::TimeDelta::FromSeconds(1));
+ base::TimeDelta delay10s(base::TimeDelta::FromSeconds(10));
+
+ MockTaskQueueObserver observer;
+ runners_[0]->SetObserver(&observer);
+
+ // We should get a notification when a delayed task is posted on an empty
+ // queue.
+ EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _));
+ runners_[0]->PostDelayedTask(FROM_HERE, base::BindOnce(&NopTask), delay1s);
+ Mock::VerifyAndClearExpectations(&observer);
+
+ std::unique_ptr<TimeDomain> mock_time_domain =
+ std::make_unique<RealTimeDomain>();
+ manager_->RegisterTimeDomain(mock_time_domain.get());
+
+ now_src_.Advance(delay10s);
+
+ EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _));
+ runners_[0]->SetTimeDomain(mock_time_domain.get());
+ Mock::VerifyAndClearExpectations(&observer);
+
+ // Tidy up.
+ runners_[0]->ShutdownTaskQueue();
+}
+
+class CancelableTask {
+ public:
+ explicit CancelableTask(const base::TickClock* clock)
+ : clock_(clock), weak_factory_(this) {}
+
+ void RecordTimeTask(std::vector<base::TimeTicks>* run_times) {
+ run_times->push_back(clock_->NowTicks());
+ }
+
+ const base::TickClock* clock_;
+ base::WeakPtrFactory<CancelableTask> weak_factory_;
+};
+
+TEST_F(TaskQueueManagerTest, TaskQueueObserver_SweepCanceledDelayedTasks) {
+ Initialize(1u);
+
+ MockTaskQueueObserver observer;
+ runners_[0]->SetObserver(&observer);
+
+ base::TimeTicks start_time = manager_->NowTicks();
+ base::TimeDelta delay1(base::TimeDelta::FromSeconds(5));
+ base::TimeDelta delay2(base::TimeDelta::FromSeconds(10));
+
+ EXPECT_CALL(observer,
+ OnQueueNextWakeUpChanged(runners_[0].get(), start_time + delay1))
+ .Times(1);
+
+ CancelableTask task1(&now_src_);
+ CancelableTask task2(&now_src_);
+ std::vector<base::TimeTicks> run_times;
+ runners_[0]->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&CancelableTask::RecordTimeTask,
+ task1.weak_factory_.GetWeakPtr(), &run_times),
+ delay1);
+ runners_[0]->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&CancelableTask::RecordTimeTask,
+ task2.weak_factory_.GetWeakPtr(), &run_times),
+ delay2);
+
+ task1.weak_factory_.InvalidateWeakPtrs();
+
+ // Sweeping away canceled delayed tasks should trigger a notification.
+ EXPECT_CALL(observer,
+ OnQueueNextWakeUpChanged(runners_[0].get(), start_time + delay2))
+ .Times(1);
+ manager_->SweepCanceledDelayedTasks();
+}
+
+namespace {
+void ChromiumRunloopInspectionTask(
+ scoped_refptr<cc::OrderedSimpleTaskRunner> test_task_runner) {
+ EXPECT_EQ(1u, test_task_runner->NumPendingTasks());
+}
+} // namespace
+
+TEST_F(TaskQueueManagerTest, NumberOfPendingTasksOnChromiumRunLoop) {
+ Initialize(1u);
+
+ // NOTE because tasks posted to the chromiumrun loop are not cancellable, we
+ // will end up with a lot more tasks posted if the delayed tasks were posted
+ // in the reverse order.
+ // TODO(alexclarke): Consider talking to the message pump directly.
+ test_task_runner_->SetAutoAdvanceNowToPendingTasks(true);
+ for (int i = 1; i < 100; i++) {
+ runners_[0]->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&ChromiumRunloopInspectionTask, test_task_runner_),
+ base::TimeDelta::FromMilliseconds(i));
+ }
+ test_task_runner_->RunUntilIdle();
+}
+
+namespace {
+
+class QuadraticTask {
+ public:
+ QuadraticTask(scoped_refptr<TaskQueue> task_queue,
+ base::TimeDelta delay,
+ base::SimpleTestTickClock* now_src)
+ : count_(0), task_queue_(task_queue), delay_(delay), now_src_(now_src) {}
+
+ void SetShouldExit(base::RepeatingCallback<bool()> should_exit) {
+ should_exit_ = should_exit;
+ }
+
+ void Run() {
+ if (should_exit_.Run())
+ return;
+ count_++;
+ task_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&QuadraticTask::Run, base::Unretained(this)),
+ delay_);
+ task_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&QuadraticTask::Run, base::Unretained(this)),
+ delay_);
+ now_src_->Advance(base::TimeDelta::FromMilliseconds(5));
+ }
+
+ int Count() const { return count_; }
+
+ private:
+ int count_;
+ scoped_refptr<TaskQueue> task_queue_;
+ base::TimeDelta delay_;
+ base::RepeatingCallback<bool()> should_exit_;
+ base::SimpleTestTickClock* now_src_;
+};
+
+class LinearTask {
+ public:
+ LinearTask(scoped_refptr<TaskQueue> task_queue,
+ base::TimeDelta delay,
+ base::SimpleTestTickClock* now_src)
+ : count_(0), task_queue_(task_queue), delay_(delay), now_src_(now_src) {}
+
+ void SetShouldExit(base::RepeatingCallback<bool()> should_exit) {
+ should_exit_ = should_exit;
+ }
+
+ void Run() {
+ if (should_exit_.Run())
+ return;
+ count_++;
+ task_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&LinearTask::Run, base::Unretained(this)),
+ delay_);
+ now_src_->Advance(base::TimeDelta::FromMilliseconds(5));
+ }
+
+ int Count() const { return count_; }
+
+ private:
+ int count_;
+ scoped_refptr<TaskQueue> task_queue_;
+ base::TimeDelta delay_;
+ base::RepeatingCallback<bool()> should_exit_;
+ base::SimpleTestTickClock* now_src_;
+};
+
+bool ShouldExit(QuadraticTask* quadratic_task, LinearTask* linear_task) {
+ return quadratic_task->Count() == 1000 || linear_task->Count() == 1000;
+}
+
+} // namespace
+
+TEST_F(TaskQueueManagerTest,
+ DelayedTasksDontBadlyStarveNonDelayedWork_SameQueue) {
+ Initialize(1u);
+
+ QuadraticTask quadratic_delayed_task(
+ runners_[0], base::TimeDelta::FromMilliseconds(10), &now_src_);
+ LinearTask linear_immediate_task(runners_[0], base::TimeDelta(), &now_src_);
+ base::RepeatingCallback<bool()> should_exit = base::BindRepeating(
+ ShouldExit, &quadratic_delayed_task, &linear_immediate_task);
+ quadratic_delayed_task.SetShouldExit(should_exit);
+ linear_immediate_task.SetShouldExit(should_exit);
+
+ quadratic_delayed_task.Run();
+ linear_immediate_task.Run();
+
+ test_task_runner_->SetAutoAdvanceNowToPendingTasks(true);
+ test_task_runner_->RunUntilIdle();
+
+ double ratio = static_cast<double>(linear_immediate_task.Count()) /
+ static_cast<double>(quadratic_delayed_task.Count());
+
+ EXPECT_GT(ratio, 0.333);
+ EXPECT_LT(ratio, 1.1);
+}
+
+TEST_F(TaskQueueManagerTest, ImmediateWorkCanStarveDelayedTasks_SameQueue) {
+ Initialize(1u);
+
+ QuadraticTask quadratic_immediate_task(runners_[0], base::TimeDelta(),
+ &now_src_);
+ LinearTask linear_delayed_task(
+ runners_[0], base::TimeDelta::FromMilliseconds(10), &now_src_);
+ base::RepeatingCallback<bool()> should_exit = base::BindRepeating(
+ &ShouldExit, &quadratic_immediate_task, &linear_delayed_task);
+
+ quadratic_immediate_task.SetShouldExit(should_exit);
+ linear_delayed_task.SetShouldExit(should_exit);
+
+ quadratic_immediate_task.Run();
+ linear_delayed_task.Run();
+
+ test_task_runner_->SetAutoAdvanceNowToPendingTasks(true);
+ test_task_runner_->RunUntilIdle();
+
+ double ratio = static_cast<double>(linear_delayed_task.Count()) /
+ static_cast<double>(quadratic_immediate_task.Count());
+
+ // This is by design, we want to enforce a strict ordering in task execution
+ // where by delayed tasks can not skip ahead of non-delayed work.
+ EXPECT_GT(ratio, 0.0);
+ EXPECT_LT(ratio, 0.1);
+}
+
+TEST_F(TaskQueueManagerTest,
+ DelayedTasksDontBadlyStarveNonDelayedWork_DifferentQueue) {
+ Initialize(2u);
+
+ QuadraticTask quadratic_delayed_task(
+ runners_[0], base::TimeDelta::FromMilliseconds(10), &now_src_);
+ LinearTask linear_immediate_task(runners_[1], base::TimeDelta(), &now_src_);
+ base::RepeatingCallback<bool()> should_exit = base::BindRepeating(
+ ShouldExit, &quadratic_delayed_task, &linear_immediate_task);
+ quadratic_delayed_task.SetShouldExit(should_exit);
+ linear_immediate_task.SetShouldExit(should_exit);
+
+ quadratic_delayed_task.Run();
+ linear_immediate_task.Run();
+
+ test_task_runner_->SetAutoAdvanceNowToPendingTasks(true);
+ test_task_runner_->RunUntilIdle();
+
+ double ratio = static_cast<double>(linear_immediate_task.Count()) /
+ static_cast<double>(quadratic_delayed_task.Count());
+
+ EXPECT_GT(ratio, 0.333);
+ EXPECT_LT(ratio, 1.1);
+}
+
+TEST_F(TaskQueueManagerTest,
+ ImmediateWorkCanStarveDelayedTasks_DifferentQueue) {
+ Initialize(2u);
+
+ QuadraticTask quadratic_immediate_task(runners_[0], base::TimeDelta(),
+ &now_src_);
+ LinearTask linear_delayed_task(
+ runners_[1], base::TimeDelta::FromMilliseconds(10), &now_src_);
+ base::RepeatingCallback<bool()> should_exit = base::BindRepeating(
+ &ShouldExit, &quadratic_immediate_task, &linear_delayed_task);
+
+ quadratic_immediate_task.SetShouldExit(should_exit);
+ linear_delayed_task.SetShouldExit(should_exit);
+
+ quadratic_immediate_task.Run();
+ linear_delayed_task.Run();
+
+ test_task_runner_->SetAutoAdvanceNowToPendingTasks(true);
+ test_task_runner_->RunUntilIdle();
+
+ double ratio = static_cast<double>(linear_delayed_task.Count()) /
+ static_cast<double>(quadratic_immediate_task.Count());
+
+ // This is by design, we want to enforce a strict ordering in task execution
+ // where by delayed tasks can not skip ahead of non-delayed work.
+ EXPECT_GT(ratio, 0.0);
+ EXPECT_LT(ratio, 0.1);
+}
+
+TEST_F(TaskQueueManagerTest, CurrentlyExecutingTaskQueue_NoTaskRunning) {
+ Initialize(1u);
+
+ EXPECT_EQ(nullptr, manager_->currently_executing_task_queue());
+}
+
+namespace {
+void CurrentlyExecutingTaskQueueTestTask(
+ TaskQueueManagerImpl* task_queue_manager,
+ std::vector<internal::TaskQueueImpl*>* task_sources) {
+ task_sources->push_back(task_queue_manager->currently_executing_task_queue());
+}
+} // namespace
+
+TEST_F(TaskQueueManagerTest, CurrentlyExecutingTaskQueue_TaskRunning) {
+ Initialize(2u);
+
+ TestTaskQueue* queue0 = runners_[0].get();
+ TestTaskQueue* queue1 = runners_[1].get();
+
+ std::vector<internal::TaskQueueImpl*> task_sources;
+ queue0->PostTask(FROM_HERE,
+ base::BindOnce(&CurrentlyExecutingTaskQueueTestTask,
+ manager_.get(), &task_sources));
+ queue1->PostTask(FROM_HERE,
+ base::BindOnce(&CurrentlyExecutingTaskQueueTestTask,
+ manager_.get(), &task_sources));
+ test_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(task_sources, ElementsAre(queue0->GetTaskQueueImpl(),
+ queue1->GetTaskQueueImpl()));
+ EXPECT_EQ(nullptr, manager_->currently_executing_task_queue());
+}
+
+namespace {
+void RunloopCurrentlyExecutingTaskQueueTestTask(
+ base::MessageLoop* message_loop,
+ TaskQueueManagerImpl* task_queue_manager,
+ std::vector<internal::TaskQueueImpl*>* task_sources,
+ std::vector<std::pair<base::OnceClosure, TestTaskQueue*>>* tasks) {
+ base::MessageLoop::ScopedNestableTaskAllower allow(message_loop);
+ task_sources->push_back(task_queue_manager->currently_executing_task_queue());
+
+ for (std::pair<base::OnceClosure, TestTaskQueue*>& pair : *tasks) {
+ pair.second->PostTask(FROM_HERE, std::move(pair.first));
+ }
+
+ base::RunLoop().RunUntilIdle();
+ task_sources->push_back(task_queue_manager->currently_executing_task_queue());
+}
+} // namespace
+
+TEST_F(TaskQueueManagerTest, CurrentlyExecutingTaskQueue_NestedLoop) {
+ InitializeWithRealMessageLoop(3u);
+
+ TestTaskQueue* queue0 = runners_[0].get();
+ TestTaskQueue* queue1 = runners_[1].get();
+ TestTaskQueue* queue2 = runners_[2].get();
+
+ std::vector<internal::TaskQueueImpl*> task_sources;
+ std::vector<std::pair<base::OnceClosure, TestTaskQueue*>>
+ tasks_to_post_from_nested_loop;
+ tasks_to_post_from_nested_loop.push_back(
+ std::make_pair(base::BindOnce(&CurrentlyExecutingTaskQueueTestTask,
+ manager_.get(), &task_sources),
+ queue1));
+ tasks_to_post_from_nested_loop.push_back(
+ std::make_pair(base::BindOnce(&CurrentlyExecutingTaskQueueTestTask,
+ manager_.get(), &task_sources),
+ queue2));
+
+ queue0->PostTask(
+ FROM_HERE,
+ base::BindOnce(&RunloopCurrentlyExecutingTaskQueueTestTask,
+ message_loop_.get(), manager_.get(), &task_sources,
+ &tasks_to_post_from_nested_loop));
+
+ base::RunLoop().RunUntilIdle();
+ EXPECT_THAT(
+ task_sources,
+ ElementsAre(queue0->GetTaskQueueImpl(), queue1->GetTaskQueueImpl(),
+ queue2->GetTaskQueueImpl(), queue0->GetTaskQueueImpl()));
+ EXPECT_EQ(nullptr, manager_->currently_executing_task_queue());
+}
+
+TEST_F(TaskQueueManagerTest, BlameContextAttribution) {
+ using trace_analyzer::Query;
+
+ InitializeWithRealMessageLoop(1u);
+ TestTaskQueue* queue = runners_[0].get();
+
+ trace_analyzer::Start("*");
+ {
+ base::trace_event::BlameContext blame_context("cat", "name", "type",
+ "scope", 0, nullptr);
+ blame_context.Initialize();
+ queue->SetBlameContext(&blame_context);
+ queue->PostTask(FROM_HERE, base::BindOnce(&NopTask));
+ base::RunLoop().RunUntilIdle();
+ }
+ auto analyzer = trace_analyzer::Stop();
+
+ trace_analyzer::TraceEventVector events;
+ Query q = Query::EventPhaseIs(TRACE_EVENT_PHASE_ENTER_CONTEXT) ||
+ Query::EventPhaseIs(TRACE_EVENT_PHASE_LEAVE_CONTEXT);
+ analyzer->FindEvents(q, &events);
+
+ EXPECT_EQ(2u, events.size());
+}
+
+TEST_F(TaskQueueManagerTest, NoWakeUpsForCanceledDelayedTasks) {
+ Initialize(1u);
+
+ base::TimeTicks start_time = manager_->NowTicks();
+
+ CancelableTask task1(&now_src_);
+ CancelableTask task2(&now_src_);
+ CancelableTask task3(&now_src_);
+ CancelableTask task4(&now_src_);
+ base::TimeDelta delay1(base::TimeDelta::FromSeconds(5));
+ base::TimeDelta delay2(base::TimeDelta::FromSeconds(10));
+ base::TimeDelta delay3(base::TimeDelta::FromSeconds(15));
+ base::TimeDelta delay4(base::TimeDelta::FromSeconds(30));
+ std::vector<base::TimeTicks> run_times;
+ runners_[0]->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&CancelableTask::RecordTimeTask,
+ task1.weak_factory_.GetWeakPtr(), &run_times),
+ delay1);
+ runners_[0]->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&CancelableTask::RecordTimeTask,
+ task2.weak_factory_.GetWeakPtr(), &run_times),
+ delay2);
+ runners_[0]->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&CancelableTask::RecordTimeTask,
+ task3.weak_factory_.GetWeakPtr(), &run_times),
+ delay3);
+ runners_[0]->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&CancelableTask::RecordTimeTask,
+ task4.weak_factory_.GetWeakPtr(), &run_times),
+ delay4);
+
+ task2.weak_factory_.InvalidateWeakPtrs();
+ task3.weak_factory_.InvalidateWeakPtrs();
+
+ std::set<base::TimeTicks> wake_up_times;
+
+ RunUntilIdle(base::BindRepeating(
+ [](std::set<base::TimeTicks>* wake_up_times,
+ base::SimpleTestTickClock* clock) {
+ wake_up_times->insert(clock->NowTicks());
+ },
+ &wake_up_times, &now_src_));
+
+ EXPECT_THAT(wake_up_times,
+ ElementsAre(start_time + delay1, start_time + delay4));
+ EXPECT_THAT(run_times, ElementsAre(start_time + delay1, start_time + delay4));
+}
+
+TEST_F(TaskQueueManagerTest, NoWakeUpsForCanceledDelayedTasksReversePostOrder) {
+ Initialize(1u);
+
+ base::TimeTicks start_time = manager_->NowTicks();
+
+ CancelableTask task1(&now_src_);
+ CancelableTask task2(&now_src_);
+ CancelableTask task3(&now_src_);
+ CancelableTask task4(&now_src_);
+ base::TimeDelta delay1(base::TimeDelta::FromSeconds(5));
+ base::TimeDelta delay2(base::TimeDelta::FromSeconds(10));
+ base::TimeDelta delay3(base::TimeDelta::FromSeconds(15));
+ base::TimeDelta delay4(base::TimeDelta::FromSeconds(30));
+ std::vector<base::TimeTicks> run_times;
+ runners_[0]->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&CancelableTask::RecordTimeTask,
+ task4.weak_factory_.GetWeakPtr(), &run_times),
+ delay4);
+ runners_[0]->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&CancelableTask::RecordTimeTask,
+ task3.weak_factory_.GetWeakPtr(), &run_times),
+ delay3);
+ runners_[0]->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&CancelableTask::RecordTimeTask,
+ task2.weak_factory_.GetWeakPtr(), &run_times),
+ delay2);
+ runners_[0]->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&CancelableTask::RecordTimeTask,
+ task1.weak_factory_.GetWeakPtr(), &run_times),
+ delay1);
+
+ task2.weak_factory_.InvalidateWeakPtrs();
+ task3.weak_factory_.InvalidateWeakPtrs();
+
+ std::set<base::TimeTicks> wake_up_times;
+
+ RunUntilIdle(base::BindRepeating(
+ [](std::set<base::TimeTicks>* wake_up_times,
+ base::SimpleTestTickClock* clock) {
+ wake_up_times->insert(clock->NowTicks());
+ },
+ &wake_up_times, &now_src_));
+
+ EXPECT_THAT(wake_up_times,
+ ElementsAre(start_time + delay1, start_time + delay4));
+ EXPECT_THAT(run_times, ElementsAre(start_time + delay1, start_time + delay4));
+}
+
+TEST_F(TaskQueueManagerTest, TimeDomainWakeUpOnlyCancelledIfAllUsesCancelled) {
+ Initialize(1u);
+
+ base::TimeTicks start_time = manager_->NowTicks();
+
+ CancelableTask task1(&now_src_);
+ CancelableTask task2(&now_src_);
+ CancelableTask task3(&now_src_);
+ CancelableTask task4(&now_src_);
+ base::TimeDelta delay1(base::TimeDelta::FromSeconds(5));
+ base::TimeDelta delay2(base::TimeDelta::FromSeconds(10));
+ base::TimeDelta delay3(base::TimeDelta::FromSeconds(15));
+ base::TimeDelta delay4(base::TimeDelta::FromSeconds(30));
+ std::vector<base::TimeTicks> run_times;
+ runners_[0]->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&CancelableTask::RecordTimeTask,
+ task1.weak_factory_.GetWeakPtr(), &run_times),
+ delay1);
+ runners_[0]->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&CancelableTask::RecordTimeTask,
+ task2.weak_factory_.GetWeakPtr(), &run_times),
+ delay2);
+ runners_[0]->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&CancelableTask::RecordTimeTask,
+ task3.weak_factory_.GetWeakPtr(), &run_times),
+ delay3);
+ runners_[0]->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&CancelableTask::RecordTimeTask,
+ task4.weak_factory_.GetWeakPtr(), &run_times),
+ delay4);
+
+ // Post a non-canceled task with |delay3|. So we should still get a wake-up at
+ // |delay3| even though we cancel |task3|.
+ runners_[0]->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&CancelableTask::RecordTimeTask, base::Unretained(&task3),
+ &run_times),
+ delay3);
+
+ task2.weak_factory_.InvalidateWeakPtrs();
+ task3.weak_factory_.InvalidateWeakPtrs();
+ task1.weak_factory_.InvalidateWeakPtrs();
+
+ std::set<base::TimeTicks> wake_up_times;
+
+ RunUntilIdle(base::BindRepeating(
+ [](std::set<base::TimeTicks>* wake_up_times,
+ base::SimpleTestTickClock* clock) {
+ wake_up_times->insert(clock->NowTicks());
+ },
+ &wake_up_times, &now_src_));
+
+ EXPECT_THAT(wake_up_times,
+ ElementsAre(start_time + delay1, start_time + delay3,
+ start_time + delay4));
+
+ EXPECT_THAT(run_times, ElementsAre(start_time + delay3, start_time + delay4));
+}
+
+TEST_F(TaskQueueManagerTest, TaskQueueVoters) {
+ Initialize(1u);
+
+ // The task queue should be initially enabled.
+ EXPECT_TRUE(runners_[0]->IsQueueEnabled());
+
+ std::unique_ptr<TaskQueue::QueueEnabledVoter> voter1 =
+ runners_[0]->CreateQueueEnabledVoter();
+ std::unique_ptr<TaskQueue::QueueEnabledVoter> voter2 =
+ runners_[0]->CreateQueueEnabledVoter();
+ std::unique_ptr<TaskQueue::QueueEnabledVoter> voter3 =
+ runners_[0]->CreateQueueEnabledVoter();
+ std::unique_ptr<TaskQueue::QueueEnabledVoter> voter4 =
+ runners_[0]->CreateQueueEnabledVoter();
+
+ // Voters should initially vote for the queue to be enabled.
+ EXPECT_TRUE(runners_[0]->IsQueueEnabled());
+
+ // If any voter wants to disable, the queue is disabled.
+ voter1->SetQueueEnabled(false);
+ EXPECT_FALSE(runners_[0]->IsQueueEnabled());
+
+ // If the voter is deleted then the queue should be re-enabled.
+ voter1.reset();
+ EXPECT_TRUE(runners_[0]->IsQueueEnabled());
+
+ // If any of the remaining voters wants to disable, the queue should be
+ // disabled.
+ voter2->SetQueueEnabled(false);
+ EXPECT_FALSE(runners_[0]->IsQueueEnabled());
+
+ // If another queue votes to disable, nothing happens because it's already
+ // disabled.
+ voter3->SetQueueEnabled(false);
+ EXPECT_FALSE(runners_[0]->IsQueueEnabled());
+
+ // There are two votes to disable, so one of them voting to enable does
+ // nothing.
+ voter2->SetQueueEnabled(true);
+ EXPECT_FALSE(runners_[0]->IsQueueEnabled());
+
+ // IF all queues vote to enable then the queue is enabled.
+ voter3->SetQueueEnabled(true);
+ EXPECT_TRUE(runners_[0]->IsQueueEnabled());
+}
+
+TEST_F(TaskQueueManagerTest, ShutdownQueueBeforeEnabledVoterDeleted) {
+ Initialize(1u);
+
+ scoped_refptr<TaskQueue> queue = CreateTaskQueue();
+
+ std::unique_ptr<TaskQueue::QueueEnabledVoter> voter =
+ queue->CreateQueueEnabledVoter();
+
+ voter->SetQueueEnabled(true); // NOP
+ queue->ShutdownTaskQueue();
+
+ // This should complete without DCHECKing.
+ voter.reset();
+}
+
+TEST_F(TaskQueueManagerTest, ShutdownQueueBeforeDisabledVoterDeleted) {
+ Initialize(1u);
+
+ scoped_refptr<TaskQueue> queue = CreateTaskQueue();
+
+ std::unique_ptr<TaskQueue::QueueEnabledVoter> voter =
+ queue->CreateQueueEnabledVoter();
+
+ voter->SetQueueEnabled(false);
+ queue->ShutdownTaskQueue();
+
+ // This should complete without DCHECKing.
+ voter.reset();
+}
+
+TEST_F(TaskQueueManagerTest, SweepCanceledDelayedTasks) {
+ Initialize(1u);
+
+ CancelableTask task1(&now_src_);
+ CancelableTask task2(&now_src_);
+ CancelableTask task3(&now_src_);
+ CancelableTask task4(&now_src_);
+ base::TimeDelta delay1(base::TimeDelta::FromSeconds(5));
+ base::TimeDelta delay2(base::TimeDelta::FromSeconds(10));
+ base::TimeDelta delay3(base::TimeDelta::FromSeconds(15));
+ base::TimeDelta delay4(base::TimeDelta::FromSeconds(30));
+ std::vector<base::TimeTicks> run_times;
+ runners_[0]->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&CancelableTask::RecordTimeTask,
+ task1.weak_factory_.GetWeakPtr(), &run_times),
+ delay1);
+ runners_[0]->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&CancelableTask::RecordTimeTask,
+ task2.weak_factory_.GetWeakPtr(), &run_times),
+ delay2);
+ runners_[0]->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&CancelableTask::RecordTimeTask,
+ task3.weak_factory_.GetWeakPtr(), &run_times),
+ delay3);
+ runners_[0]->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&CancelableTask::RecordTimeTask,
+ task4.weak_factory_.GetWeakPtr(), &run_times),
+ delay4);
+
+ EXPECT_EQ(4u, runners_[0]->GetNumberOfPendingTasks());
+ task2.weak_factory_.InvalidateWeakPtrs();
+ task3.weak_factory_.InvalidateWeakPtrs();
+ EXPECT_EQ(4u, runners_[0]->GetNumberOfPendingTasks());
+
+ manager_->SweepCanceledDelayedTasks();
+ EXPECT_EQ(2u, runners_[0]->GetNumberOfPendingTasks());
+
+ task1.weak_factory_.InvalidateWeakPtrs();
+ task4.weak_factory_.InvalidateWeakPtrs();
+
+ manager_->SweepCanceledDelayedTasks();
+ EXPECT_EQ(0u, runners_[0]->GetNumberOfPendingTasks());
+}
+
+TEST_F(TaskQueueManagerTest, DelayTillNextTask) {
+ Initialize(2u);
+
+ LazyNow lazy_now(&now_src_);
+ EXPECT_EQ(base::TimeDelta::Max(), manager_->DelayTillNextTask(&lazy_now));
+
+ runners_[0]->PostDelayedTask(FROM_HERE, base::BindOnce(&NopTask),
+ base::TimeDelta::FromSeconds(10));
+
+ EXPECT_EQ(base::TimeDelta::FromSeconds(10),
+ manager_->DelayTillNextTask(&lazy_now));
+
+ runners_[1]->PostDelayedTask(FROM_HERE, base::BindOnce(&NopTask),
+ base::TimeDelta::FromSeconds(15));
+
+ EXPECT_EQ(base::TimeDelta::FromSeconds(10),
+ manager_->DelayTillNextTask(&lazy_now));
+
+ runners_[1]->PostDelayedTask(FROM_HERE, base::BindOnce(&NopTask),
+ base::TimeDelta::FromSeconds(5));
+
+ EXPECT_EQ(base::TimeDelta::FromSeconds(5),
+ manager_->DelayTillNextTask(&lazy_now));
+
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&NopTask));
+
+ EXPECT_EQ(base::TimeDelta(), manager_->DelayTillNextTask(&lazy_now));
+}
+
+TEST_F(TaskQueueManagerTest, DelayTillNextTask_Disabled) {
+ Initialize(1u);
+
+ std::unique_ptr<TaskQueue::QueueEnabledVoter> voter =
+ runners_[0]->CreateQueueEnabledVoter();
+ voter->SetQueueEnabled(false);
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&NopTask));
+
+ LazyNow lazy_now(&now_src_);
+ EXPECT_EQ(base::TimeDelta::Max(), manager_->DelayTillNextTask(&lazy_now));
+}
+
+TEST_F(TaskQueueManagerTest, DelayTillNextTask_Fence) {
+ Initialize(1u);
+
+ runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow);
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&NopTask));
+
+ LazyNow lazy_now(&now_src_);
+ EXPECT_EQ(base::TimeDelta::Max(), manager_->DelayTillNextTask(&lazy_now));
+}
+
+TEST_F(TaskQueueManagerTest, DelayTillNextTask_FenceUnblocking) {
+ Initialize(1u);
+
+ runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow);
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&NopTask));
+ runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow);
+
+ LazyNow lazy_now(&now_src_);
+ EXPECT_EQ(base::TimeDelta(), manager_->DelayTillNextTask(&lazy_now));
+}
+
+TEST_F(TaskQueueManagerTest, DelayTillNextTask_DelayedTaskReady) {
+ Initialize(1u);
+
+ runners_[0]->PostDelayedTask(FROM_HERE, base::BindOnce(&NopTask),
+ base::TimeDelta::FromSeconds(1));
+
+ now_src_.Advance(base::TimeDelta::FromSeconds(10));
+
+ LazyNow lazy_now(&now_src_);
+ EXPECT_EQ(base::TimeDelta(), manager_->DelayTillNextTask(&lazy_now));
+}
+
+namespace {
+void MessageLoopTaskWithDelayedQuit(base::MessageLoop* message_loop,
+ base::SimpleTestTickClock* now_src,
+ scoped_refptr<TaskQueue> task_queue) {
+ base::MessageLoop::ScopedNestableTaskAllower allow(message_loop);
+ base::RunLoop run_loop;
+ task_queue->PostDelayedTask(FROM_HERE, run_loop.QuitClosure(),
+ base::TimeDelta::FromMilliseconds(100));
+ now_src->Advance(base::TimeDelta::FromMilliseconds(200));
+ run_loop.Run();
+}
+} // namespace
+
+TEST_F(TaskQueueManagerTest, DelayedTaskRunsInNestedMessageLoop) {
+ InitializeWithRealMessageLoop(1u);
+ base::RunLoop run_loop;
+ runners_[0]->PostTask(
+ FROM_HERE,
+ base::BindOnce(&MessageLoopTaskWithDelayedQuit, message_loop_.get(),
+ &now_src_, base::RetainedRef(runners_[0])));
+ run_loop.RunUntilIdle();
+}
+
+namespace {
+void MessageLoopTaskWithImmediateQuit(base::MessageLoop* message_loop,
+ base::OnceClosure non_nested_quit_closure,
+ scoped_refptr<TaskQueue> task_queue) {
+ base::MessageLoop::ScopedNestableTaskAllower allow(message_loop);
+
+ base::RunLoop run_loop;
+ // Needed because entering the nested run loop causes a DoWork to get
+ // posted.
+ task_queue->PostTask(FROM_HERE, base::BindOnce(&NopTask));
+ task_queue->PostTask(FROM_HERE, run_loop.QuitClosure());
+ run_loop.Run();
+ std::move(non_nested_quit_closure).Run();
+}
+} // namespace
+
+TEST_F(TaskQueueManagerTest,
+ DelayedNestedMessageLoopDoesntPreventTasksRunning) {
+ InitializeWithRealMessageLoop(1u);
+ base::RunLoop run_loop;
+ runners_[0]->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&MessageLoopTaskWithImmediateQuit, message_loop_.get(),
+ run_loop.QuitClosure(), base::RetainedRef(runners_[0])),
+ base::TimeDelta::FromMilliseconds(100));
+
+ now_src_.Advance(base::TimeDelta::FromMilliseconds(200));
+ run_loop.Run();
+}
+
+TEST_F(TaskQueueManagerTest, CouldTaskRun_DisableAndReenable) {
+ Initialize(1u);
+
+ EnqueueOrder enqueue_order = GetNextSequenceNumber();
+ EXPECT_TRUE(runners_[0]->GetTaskQueueImpl()->CouldTaskRun(enqueue_order));
+
+ std::unique_ptr<TaskQueue::QueueEnabledVoter> voter =
+ runners_[0]->CreateQueueEnabledVoter();
+ voter->SetQueueEnabled(false);
+ EXPECT_FALSE(runners_[0]->GetTaskQueueImpl()->CouldTaskRun(enqueue_order));
+
+ voter->SetQueueEnabled(true);
+ EXPECT_TRUE(runners_[0]->GetTaskQueueImpl()->CouldTaskRun(enqueue_order));
+}
+
+TEST_F(TaskQueueManagerTest, CouldTaskRun_Fence) {
+ Initialize(1u);
+
+ EnqueueOrder enqueue_order = GetNextSequenceNumber();
+ EXPECT_TRUE(runners_[0]->GetTaskQueueImpl()->CouldTaskRun(enqueue_order));
+
+ runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow);
+ EXPECT_TRUE(runners_[0]->GetTaskQueueImpl()->CouldTaskRun(enqueue_order));
+
+ runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kBeginningOfTime);
+ EXPECT_FALSE(runners_[0]->GetTaskQueueImpl()->CouldTaskRun(enqueue_order));
+
+ runners_[0]->RemoveFence();
+ EXPECT_TRUE(runners_[0]->GetTaskQueueImpl()->CouldTaskRun(enqueue_order));
+}
+
+TEST_F(TaskQueueManagerTest, CouldTaskRun_FenceBeforeThenAfter) {
+ Initialize(1u);
+
+ runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow);
+
+ EnqueueOrder enqueue_order = GetNextSequenceNumber();
+ EXPECT_FALSE(runners_[0]->GetTaskQueueImpl()->CouldTaskRun(enqueue_order));
+
+ runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow);
+ EXPECT_TRUE(runners_[0]->GetTaskQueueImpl()->CouldTaskRun(enqueue_order));
+}
+
+TEST_F(TaskQueueManagerTest, DelayedDoWorkNotPostedForDisabledQueue) {
+ Initialize(1u);
+
+ runners_[0]->PostDelayedTask(FROM_HERE, base::BindOnce(&NopTask),
+ base::TimeDelta::FromMilliseconds(1));
+ ASSERT_TRUE(test_task_runner_->HasPendingTasks());
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(1),
+ test_task_runner_->DelayToNextTaskTime());
+
+ std::unique_ptr<TaskQueue::QueueEnabledVoter> voter =
+ runners_[0]->CreateQueueEnabledVoter();
+ voter->SetQueueEnabled(false);
+
+ EXPECT_TRUE(test_task_runner_->HasPendingTasks());
+ test_task_runner_->RemoveCancelledTasks();
+ EXPECT_FALSE(test_task_runner_->HasPendingTasks());
+
+ voter->SetQueueEnabled(true);
+ ASSERT_TRUE(test_task_runner_->HasPendingTasks());
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(1),
+ test_task_runner_->DelayToNextTaskTime());
+}
+
+TEST_F(TaskQueueManagerTest, DisablingQueuesChangesDelayTillNextDoWork) {
+ Initialize(3u);
+ runners_[0]->PostDelayedTask(FROM_HERE, base::BindOnce(&NopTask),
+ base::TimeDelta::FromMilliseconds(1));
+ runners_[1]->PostDelayedTask(FROM_HERE, base::BindOnce(&NopTask),
+ base::TimeDelta::FromMilliseconds(10));
+ runners_[2]->PostDelayedTask(FROM_HERE, base::BindOnce(&NopTask),
+ base::TimeDelta::FromMilliseconds(100));
+
+ std::unique_ptr<TaskQueue::QueueEnabledVoter> voter0 =
+ runners_[0]->CreateQueueEnabledVoter();
+ std::unique_ptr<TaskQueue::QueueEnabledVoter> voter1 =
+ runners_[1]->CreateQueueEnabledVoter();
+ std::unique_ptr<TaskQueue::QueueEnabledVoter> voter2 =
+ runners_[2]->CreateQueueEnabledVoter();
+
+ ASSERT_TRUE(test_task_runner_->HasPendingTasks());
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(1),
+ test_task_runner_->DelayToNextTaskTime());
+
+ voter0->SetQueueEnabled(false);
+ test_task_runner_->RemoveCancelledTasks();
+ ASSERT_TRUE(test_task_runner_->HasPendingTasks());
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(10),
+ test_task_runner_->DelayToNextTaskTime());
+
+ voter1->SetQueueEnabled(false);
+ test_task_runner_->RemoveCancelledTasks();
+ ASSERT_TRUE(test_task_runner_->HasPendingTasks());
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(100),
+ test_task_runner_->DelayToNextTaskTime());
+
+ voter2->SetQueueEnabled(false);
+ test_task_runner_->RemoveCancelledTasks();
+ EXPECT_FALSE(test_task_runner_->HasPendingTasks());
+}
+
+TEST_F(TaskQueueManagerTest, GetNextScheduledWakeUp) {
+ Initialize(1u);
+
+ EXPECT_EQ(base::nullopt, runners_[0]->GetNextScheduledWakeUp());
+
+ base::TimeTicks start_time = manager_->NowTicks();
+ base::TimeDelta delay1 = base::TimeDelta::FromMilliseconds(10);
+ base::TimeDelta delay2 = base::TimeDelta::FromMilliseconds(2);
+
+ runners_[0]->PostDelayedTask(FROM_HERE, base::BindOnce(&NopTask), delay1);
+ EXPECT_EQ(start_time + delay1, runners_[0]->GetNextScheduledWakeUp());
+
+ runners_[0]->PostDelayedTask(FROM_HERE, base::BindOnce(&NopTask), delay2);
+ EXPECT_EQ(start_time + delay2, runners_[0]->GetNextScheduledWakeUp());
+
+ // We don't have wake-ups scheduled for disabled queues.
+ std::unique_ptr<TaskQueue::QueueEnabledVoter> voter =
+ runners_[0]->CreateQueueEnabledVoter();
+ voter->SetQueueEnabled(false);
+ EXPECT_EQ(base::nullopt, runners_[0]->GetNextScheduledWakeUp());
+
+ voter->SetQueueEnabled(true);
+ EXPECT_EQ(start_time + delay2, runners_[0]->GetNextScheduledWakeUp());
+
+ // Immediate tasks shouldn't make any difference.
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&NopTask));
+ EXPECT_EQ(start_time + delay2, runners_[0]->GetNextScheduledWakeUp());
+
+ // Neither should fences.
+ runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kBeginningOfTime);
+ EXPECT_EQ(start_time + delay2, runners_[0]->GetNextScheduledWakeUp());
+}
+
+TEST_F(TaskQueueManagerTest, SetTimeDomainForDisabledQueue) {
+ Initialize(1u);
+
+ MockTaskQueueObserver observer;
+ runners_[0]->SetObserver(&observer);
+
+ runners_[0]->PostDelayedTask(FROM_HERE, base::BindOnce(&NopTask),
+ base::TimeDelta::FromMilliseconds(1));
+
+ std::unique_ptr<TaskQueue::QueueEnabledVoter> voter =
+ runners_[0]->CreateQueueEnabledVoter();
+ voter->SetQueueEnabled(false);
+
+ // We should not get a notification for a disabled queue.
+ EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _)).Times(0);
+
+ std::unique_ptr<VirtualTimeDomain> domain(
+ new VirtualTimeDomain(manager_->NowTicks()));
+ manager_->RegisterTimeDomain(domain.get());
+ runners_[0]->SetTimeDomain(domain.get());
+
+ // Tidy up.
+ runners_[0]->ShutdownTaskQueue();
+ manager_->UnregisterTimeDomain(domain.get());
+}
+
+namespace {
+void SetOnTaskHandlers(scoped_refptr<TestTaskQueue> task_queue,
+ int* start_counter,
+ int* complete_counter) {
+ task_queue->GetTaskQueueImpl()->SetOnTaskStartedHandler(
+ base::BindRepeating([](int* counter, const TaskQueue::Task& task,
+ base::TimeTicks start) { ++(*counter); },
+ start_counter));
+ task_queue->GetTaskQueueImpl()->SetOnTaskCompletedHandler(base::BindRepeating(
+ [](int* counter, const TaskQueue::Task& task, base::TimeTicks start,
+ base::TimeTicks end,
+ base::Optional<base::TimeDelta> thread_time) { ++(*counter); },
+ complete_counter));
+}
+
+void UnsetOnTaskHandlers(scoped_refptr<TestTaskQueue> task_queue) {
+ task_queue->GetTaskQueueImpl()->SetOnTaskStartedHandler(
+ base::RepeatingCallback<void(const TaskQueue::Task& task,
+ base::TimeTicks start)>());
+ task_queue->GetTaskQueueImpl()->SetOnTaskCompletedHandler(
+ base::RepeatingCallback<void(
+ const TaskQueue::Task& task, base::TimeTicks start,
+ base::TimeTicks end, base::Optional<base::TimeDelta> thread_time)>());
+}
+} // namespace
+
+TEST_F(TaskQueueManagerTest, ProcessTasksWithoutTaskTimeObservers) {
+ Initialize(1u);
+ int start_counter = 0;
+ int complete_counter = 0;
+ std::vector<EnqueueOrder> run_order;
+ SetOnTaskHandlers(runners_[0], &start_counter, &complete_counter);
+ EXPECT_TRUE(runners_[0]->GetTaskQueueImpl()->RequiresTaskTiming());
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 1, &run_order));
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 2, &run_order));
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 3, &run_order));
+ test_task_runner_->RunUntilIdle();
+ EXPECT_EQ(start_counter, 3);
+ EXPECT_EQ(complete_counter, 3);
+ EXPECT_THAT(run_order, ElementsAre(1, 2, 3));
+
+ UnsetOnTaskHandlers(runners_[0]);
+ EXPECT_FALSE(runners_[0]->GetTaskQueueImpl()->RequiresTaskTiming());
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 4, &run_order));
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 5, &run_order));
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 6, &run_order));
+ test_task_runner_->RunUntilIdle();
+ EXPECT_EQ(start_counter, 3);
+ EXPECT_EQ(complete_counter, 3);
+ EXPECT_THAT(run_order, ElementsAre(1, 2, 3, 4, 5, 6));
+}
+
+TEST_F(TaskQueueManagerTest, ProcessTasksWithTaskTimeObservers) {
+ Initialize(1u);
+ int start_counter = 0;
+ int complete_counter = 0;
+
+ manager_->AddTaskTimeObserver(&test_task_time_observer_);
+ SetOnTaskHandlers(runners_[0], &start_counter, &complete_counter);
+ EXPECT_TRUE(runners_[0]->GetTaskQueueImpl()->RequiresTaskTiming());
+ std::vector<EnqueueOrder> run_order;
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 1, &run_order));
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 2, &run_order));
+ test_task_runner_->RunUntilIdle();
+ EXPECT_EQ(start_counter, 2);
+ EXPECT_EQ(complete_counter, 2);
+ EXPECT_THAT(run_order, ElementsAre(1, 2));
+
+ UnsetOnTaskHandlers(runners_[0]);
+ EXPECT_FALSE(runners_[0]->GetTaskQueueImpl()->RequiresTaskTiming());
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 3, &run_order));
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 4, &run_order));
+ test_task_runner_->RunUntilIdle();
+ EXPECT_EQ(start_counter, 2);
+ EXPECT_EQ(complete_counter, 2);
+ EXPECT_THAT(run_order, ElementsAre(1, 2, 3, 4));
+
+ manager_->RemoveTaskTimeObserver(&test_task_time_observer_);
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 5, &run_order));
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 6, &run_order));
+ test_task_runner_->RunUntilIdle();
+ EXPECT_EQ(start_counter, 2);
+ EXPECT_EQ(complete_counter, 2);
+ EXPECT_FALSE(runners_[0]->GetTaskQueueImpl()->RequiresTaskTiming());
+ EXPECT_THAT(run_order, ElementsAre(1, 2, 3, 4, 5, 6));
+
+ SetOnTaskHandlers(runners_[0], &start_counter, &complete_counter);
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 7, &run_order));
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 8, &run_order));
+ test_task_runner_->RunUntilIdle();
+ EXPECT_EQ(start_counter, 4);
+ EXPECT_EQ(complete_counter, 4);
+ EXPECT_TRUE(runners_[0]->GetTaskQueueImpl()->RequiresTaskTiming());
+ EXPECT_THAT(run_order, ElementsAre(1, 2, 3, 4, 5, 6, 7, 8));
+ UnsetOnTaskHandlers(runners_[0]);
+}
+
+TEST_F(TaskQueueManagerTest, GracefulShutdown) {
+ Initialize(0u);
+ test_task_runner_->SetAutoAdvanceNowToPendingTasks(true);
+
+ std::vector<base::TimeTicks> run_times;
+ scoped_refptr<TestTaskQueue> main_tq = CreateTaskQueue();
+ base::WeakPtr<TestTaskQueue> main_tq_weak_ptr = main_tq->GetWeakPtr();
+
+ EXPECT_EQ(1u, manager_->ActiveQueuesCount());
+ EXPECT_EQ(0u, manager_->QueuesToShutdownCount());
+ EXPECT_EQ(0u, manager_->QueuesToDeleteCount());
+
+ for (int i = 1; i <= 5; ++i) {
+ main_tq->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&RecordTimeTask, &run_times, &now_src_),
+ base::TimeDelta::FromMilliseconds(i * 100));
+ }
+ test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(250));
+
+ main_tq = nullptr;
+ // Ensure that task queue went away.
+ EXPECT_FALSE(main_tq_weak_ptr.get());
+
+ test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(1));
+
+ EXPECT_EQ(1u, manager_->ActiveQueuesCount());
+ EXPECT_EQ(1u, manager_->QueuesToShutdownCount());
+ EXPECT_EQ(0u, manager_->QueuesToDeleteCount());
+
+ test_task_runner_->RunUntilIdle();
+
+ // Even with TaskQueue gone, tasks are executed.
+ EXPECT_THAT(
+ run_times,
+ ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(101),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(201),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(301),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(401),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(501)));
+
+ EXPECT_EQ(0u, manager_->ActiveQueuesCount());
+ EXPECT_EQ(0u, manager_->QueuesToShutdownCount());
+ EXPECT_EQ(0u, manager_->QueuesToDeleteCount());
+}
+
+TEST_F(TaskQueueManagerTest, GracefulShutdown_ManagerDeletedInFlight) {
+ Initialize(0u);
+ test_task_runner_->SetAutoAdvanceNowToPendingTasks(true);
+
+ std::vector<base::TimeTicks> run_times;
+ scoped_refptr<TestTaskQueue> control_tq = CreateTaskQueue();
+ std::vector<scoped_refptr<TestTaskQueue>> main_tqs;
+ std::vector<base::WeakPtr<TestTaskQueue>> main_tq_weak_ptrs;
+
+ // There might be a race condition - async task queues should be unregistered
+ // first. Increase the number of task queues to surely detect that.
+ // The problem is that pointers are compared in a set and generally for
+ // a small number of allocations value of the pointers increases
+ // monotonically. 100 is large enough to force allocations from different
+ // pages.
+ const int N = 100;
+ for (int i = 0; i < N; ++i) {
+ scoped_refptr<TestTaskQueue> tq = CreateTaskQueue();
+ main_tq_weak_ptrs.push_back(tq->GetWeakPtr());
+ main_tqs.push_back(std::move(tq));
+ }
+
+ for (int i = 1; i <= 5; ++i) {
+ main_tqs[0]->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&RecordTimeTask, &run_times, &now_src_),
+ base::TimeDelta::FromMilliseconds(i * 100));
+ }
+ test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(250));
+
+ main_tqs.clear();
+ // Ensure that task queues went away.
+ for (int i = 0; i < N; ++i) {
+ EXPECT_FALSE(main_tq_weak_ptrs[i].get());
+ }
+
+ // No leaks should occur when TQM was destroyed before processing
+ // shutdown task and TaskQueueImpl should be safely deleted on a correct
+ // thread.
+ manager_.reset();
+
+ test_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(
+ run_times,
+ ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(101),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(201)));
+}
+
+TEST_F(TaskQueueManagerTest,
+ GracefulShutdown_ManagerDeletedWithQueuesToShutdown) {
+ Initialize(0u);
+ test_task_runner_->SetAutoAdvanceNowToPendingTasks(true);
+
+ std::vector<base::TimeTicks> run_times;
+ scoped_refptr<TestTaskQueue> main_tq = CreateTaskQueue();
+ base::WeakPtr<TestTaskQueue> main_tq_weak_ptr = main_tq->GetWeakPtr();
+
+ EXPECT_EQ(1u, manager_->ActiveQueuesCount());
+ EXPECT_EQ(0u, manager_->QueuesToShutdownCount());
+ EXPECT_EQ(0u, manager_->QueuesToDeleteCount());
+
+ for (int i = 1; i <= 5; ++i) {
+ main_tq->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&RecordTimeTask, &run_times, &now_src_),
+ base::TimeDelta::FromMilliseconds(i * 100));
+ }
+ test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(250));
+
+ main_tq = nullptr;
+ // Ensure that task queue went away.
+ EXPECT_FALSE(main_tq_weak_ptr.get());
+
+ test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(1));
+
+ EXPECT_EQ(1u, manager_->ActiveQueuesCount());
+ EXPECT_EQ(1u, manager_->QueuesToShutdownCount());
+ EXPECT_EQ(0u, manager_->QueuesToDeleteCount());
+
+ // Ensure that all queues-to-gracefully-shutdown are properly unregistered.
+ manager_.reset();
+
+ test_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(
+ run_times,
+ ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(101),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(201)));
+}
+
+TEST_F(TaskQueueManagerTest, DefaultTaskRunnerSupport) {
+ base::MessageLoop message_loop;
+ scoped_refptr<base::SingleThreadTaskRunner> original_task_runner =
+ message_loop.task_runner();
+ scoped_refptr<base::SingleThreadTaskRunner> custom_task_runner =
+ base::MakeRefCounted<base::TestSimpleTaskRunner>();
+ {
+ std::unique_ptr<TaskQueueManagerForTest> manager =
+ TaskQueueManagerForTest::Create(&message_loop,
+ message_loop.task_runner(), nullptr);
+ manager->SetDefaultTaskRunner(custom_task_runner);
+ DCHECK_EQ(custom_task_runner, message_loop.task_runner());
+ }
+ DCHECK_EQ(original_task_runner, message_loop.task_runner());
+}
+
+TEST_F(TaskQueueManagerTest, CanceledTasksInQueueCantMakeOtherTasksSkipAhead) {
+ Initialize(2u);
+
+ CancelableTask task1(&now_src_);
+ CancelableTask task2(&now_src_);
+ std::vector<base::TimeTicks> run_times;
+
+ runners_[0]->PostTask(
+ FROM_HERE, base::BindOnce(&CancelableTask::RecordTimeTask,
+ task1.weak_factory_.GetWeakPtr(), &run_times));
+ runners_[0]->PostTask(
+ FROM_HERE, base::BindOnce(&CancelableTask::RecordTimeTask,
+ task2.weak_factory_.GetWeakPtr(), &run_times));
+
+ std::vector<EnqueueOrder> run_order;
+ runners_[1]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 1, &run_order));
+
+ runners_[0]->PostTask(FROM_HERE, base::BindOnce(&TestTask, 2, &run_order));
+
+ task1.weak_factory_.InvalidateWeakPtrs();
+ task2.weak_factory_.InvalidateWeakPtrs();
+ test_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(run_order, ElementsAre(1, 2));
+}
+
+TEST_F(TaskQueueManagerTest, TaskQueueDeletedOnAnotherThread) {
+ Initialize(0u);
+ test_task_runner_->SetAutoAdvanceNowToPendingTasks(true);
+
+ std::vector<base::TimeTicks> run_times;
+ scoped_refptr<TestTaskQueue> main_tq = CreateTaskQueue();
+
+ int start_counter = 0;
+ int complete_counter = 0;
+ SetOnTaskHandlers(main_tq, &start_counter, &complete_counter);
+
+ EXPECT_EQ(1u, manager_->ActiveQueuesCount());
+ EXPECT_EQ(0u, manager_->QueuesToShutdownCount());
+ EXPECT_EQ(0u, manager_->QueuesToDeleteCount());
+
+ for (int i = 1; i <= 5; ++i) {
+ main_tq->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&RecordTimeTask, &run_times, &now_src_),
+ base::TimeDelta::FromMilliseconds(i * 100));
+ }
+
+ // TODO(altimin): do not do this after switching to weak pointer-based
+ // task handlers.
+ UnsetOnTaskHandlers(main_tq);
+
+ base::WaitableEvent task_queue_deleted(
+ base::WaitableEvent::ResetPolicy::MANUAL,
+ base::WaitableEvent::InitialState::NOT_SIGNALED);
+ std::unique_ptr<base::Thread> thread =
+ std::make_unique<base::Thread>("test thread");
+ thread->StartAndWaitForTesting();
+
+ thread->task_runner()->PostTask(
+ FROM_HERE, base::BindOnce(
+ [](scoped_refptr<base::SingleThreadTaskRunner> task_queue,
+ base::WaitableEvent* task_queue_deleted) {
+ task_queue = nullptr;
+ task_queue_deleted->Signal();
+ },
+ std::move(main_tq), &task_queue_deleted));
+ task_queue_deleted.Wait();
+
+ EXPECT_EQ(1u, manager_->ActiveQueuesCount());
+ EXPECT_EQ(1u, manager_->QueuesToShutdownCount());
+ EXPECT_EQ(0u, manager_->QueuesToDeleteCount());
+
+ test_task_runner_->RunUntilIdle();
+
+ // Even with TaskQueue gone, tasks are executed.
+ EXPECT_THAT(
+ run_times,
+ ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(101),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(201),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(301),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(401),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(501)));
+
+ EXPECT_EQ(0u, manager_->ActiveQueuesCount());
+ EXPECT_EQ(0u, manager_->QueuesToShutdownCount());
+ EXPECT_EQ(0u, manager_->QueuesToDeleteCount());
+
+ thread->Stop();
+}
+
+namespace {
+
+void DoNothing() {}
+
+class PostTaskInDestructor {
+ public:
+ explicit PostTaskInDestructor(scoped_refptr<TaskQueue> task_queue)
+ : task_queue_(task_queue) {}
+
+ ~PostTaskInDestructor() {
+ task_queue_->PostTask(FROM_HERE, base::BindOnce(&DoNothing));
+ }
+
+ void Do() {}
+
+ private:
+ scoped_refptr<TaskQueue> task_queue_;
+};
+
+} // namespace
+
+TEST_F(TaskQueueManagerTest, TaskQueueUsedInTaskDestructorAfterShutdown) {
+ // This test checks that when a task is posted to a shutdown queue and
+ // destroyed, it can try to post a task to the same queue without deadlocks.
+ Initialize(0u);
+ test_task_runner_->SetAutoAdvanceNowToPendingTasks(true);
+
+ scoped_refptr<TestTaskQueue> main_tq = CreateTaskQueue();
+
+ base::WaitableEvent test_executed(
+ base::WaitableEvent::ResetPolicy::MANUAL,
+ base::WaitableEvent::InitialState::NOT_SIGNALED);
+ std::unique_ptr<base::Thread> thread =
+ std::make_unique<base::Thread>("test thread");
+ thread->StartAndWaitForTesting();
+
+ manager_.reset();
+
+ thread->task_runner()->PostTask(
+ FROM_HERE, base::BindOnce(
+ [](scoped_refptr<base::SingleThreadTaskRunner> task_queue,
+ std::unique_ptr<PostTaskInDestructor> test_object,
+ base::WaitableEvent* test_executed) {
+ task_queue->PostTask(
+ FROM_HERE, base::BindOnce(&PostTaskInDestructor::Do,
+ std::move(test_object)));
+ test_executed->Signal();
+ },
+ main_tq, std::make_unique<PostTaskInDestructor>(main_tq),
+ &test_executed));
+ test_executed.Wait();
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_manager_perftest.cc b/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_manager_perftest.cc
new file mode 100644
index 00000000000..acaf725976b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_manager_perftest.cc
@@ -0,0 +1,230 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_manager.h"
+
+#include <stddef.h>
+#include <memory>
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/stringprintf.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/default_tick_clock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/perf/perf_test.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_selector.h"
+#include "third_party/blink/renderer/platform/scheduler/base/test_task_time_observer.h"
+#include "third_party/blink/renderer/platform/scheduler/base/virtual_time_domain.h"
+#include "third_party/blink/renderer/platform/scheduler/base/work_queue_sets.h"
+#include "third_party/blink/renderer/platform/scheduler/test/task_queue_manager_for_test.h"
+#include "third_party/blink/renderer/platform/scheduler/test/test_task_queue.h"
+
+namespace blink {
+namespace scheduler {
+
+// To reduce noise related to the OS timer, we use a virtual time domain to
+// fast forward the timers.
+class PerfTestTimeDomain : public VirtualTimeDomain {
+ public:
+ PerfTestTimeDomain() : VirtualTimeDomain(base::TimeTicks::Now()) {}
+ ~PerfTestTimeDomain() override = default;
+
+ base::Optional<base::TimeDelta> DelayTillNextTask(
+ LazyNow* lazy_now) override {
+ base::TimeTicks run_time;
+ if (!NextScheduledRunTime(&run_time))
+ return base::Optional<base::TimeDelta>();
+
+ AdvanceNowTo(run_time);
+ return base::TimeDelta(); // Makes DoWork post an immediate continuation.
+ }
+
+ void RequestWakeUpAt(base::TimeTicks now, base::TimeTicks run_time) override {
+ // De-dupe DoWorks.
+ if (NumberOfScheduledWakeUps() == 1u)
+ RequestDoWork();
+ }
+
+ void CancelWakeUpAt(base::TimeTicks run_time) override {
+ // We didn't post a delayed task in RequestWakeUpAt so there's no need to do
+ // anything here.
+ }
+
+ const char* GetName() const override { return "PerfTestTimeDomain"; }
+
+ DISALLOW_COPY_AND_ASSIGN(PerfTestTimeDomain);
+};
+
+class TaskQueueManagerPerfTest : public testing::Test {
+ public:
+ TaskQueueManagerPerfTest()
+ : num_queues_(0),
+ max_tasks_in_flight_(0),
+ num_tasks_in_flight_(0),
+ num_tasks_to_post_(0),
+ num_tasks_to_run_(0) {}
+
+ void SetUp() override {
+ if (base::ThreadTicks::IsSupported())
+ base::ThreadTicks::WaitUntilInitialized();
+ }
+
+ void TearDown() override {
+ queues_.clear();
+ manager_->UnregisterTimeDomain(virtual_time_domain_.get());
+ manager_.reset();
+ }
+
+ void Initialize(size_t num_queues) {
+ num_queues_ = num_queues;
+ message_loop_.reset(new base::MessageLoop());
+ manager_ = TaskQueueManagerForTest::Create(
+ message_loop_.get(), message_loop_->task_runner(),
+ base::DefaultTickClock::GetInstance());
+ manager_->AddTaskTimeObserver(&test_task_time_observer_);
+
+ virtual_time_domain_.reset(new PerfTestTimeDomain());
+ manager_->RegisterTimeDomain(virtual_time_domain_.get());
+
+ for (size_t i = 0; i < num_queues; i++) {
+ queues_.push_back(manager_->CreateTaskQueue<TestTaskQueue>(
+ TaskQueue::Spec("test").SetTimeDomain(virtual_time_domain_.get())));
+ }
+ }
+
+ void TestDelayedTask() {
+ if (--num_tasks_to_run_ == 0) {
+ run_loop_->QuitWhenIdle();
+ return;
+ }
+
+ num_tasks_in_flight_--;
+ // NOTE there are only up to max_tasks_in_flight_ pending delayed tasks at
+ // any one time. Thanks to the lower_num_tasks_to_post going to zero if
+ // there are a lot of tasks in flight, the total number of task in flight at
+ // any one time is very variable.
+ unsigned int lower_num_tasks_to_post =
+ num_tasks_in_flight_ < (max_tasks_in_flight_ / 2) ? 1 : 0;
+ unsigned int max_tasks_to_post =
+ num_tasks_to_post_ % 2 ? lower_num_tasks_to_post : 10;
+ for (unsigned int i = 0;
+ i < max_tasks_to_post && num_tasks_in_flight_ < max_tasks_in_flight_ &&
+ num_tasks_to_post_ > 0;
+ i++) {
+ // Choose a queue weighted towards queue 0.
+ unsigned int queue = num_tasks_to_post_ % (num_queues_ + 1);
+ if (queue == num_queues_) {
+ queue = 0;
+ }
+ // Simulate a mix of short and longer delays.
+ unsigned int delay =
+ num_tasks_to_post_ % 2 ? 1 : (10 + num_tasks_to_post_ % 10);
+ queues_[queue]->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&TaskQueueManagerPerfTest::TestDelayedTask,
+ base::Unretained(this)),
+ base::TimeDelta::FromMilliseconds(delay));
+ num_tasks_in_flight_++;
+ num_tasks_to_post_--;
+ }
+ }
+
+ void ResetAndCallTestDelayedTask(unsigned int num_tasks_to_run) {
+ num_tasks_in_flight_ = 1;
+ num_tasks_to_post_ = num_tasks_to_run;
+ num_tasks_to_run_ = num_tasks_to_run;
+ TestDelayedTask();
+ }
+
+ void Benchmark(const std::string& trace,
+ const base::RepeatingClosure& test_task) {
+ base::ThreadTicks start = base::ThreadTicks::Now();
+ base::ThreadTicks now;
+ unsigned long long num_iterations = 0;
+ do {
+ test_task.Run();
+ run_loop_.reset(new base::RunLoop());
+ run_loop_->Run();
+ now = base::ThreadTicks::Now();
+ num_iterations++;
+ } while (now - start < base::TimeDelta::FromSeconds(5));
+ perf_test::PrintResult(
+ "task", "", trace,
+ (now - start).InMicroseconds() / static_cast<double>(num_iterations),
+ "us/run", true);
+ }
+
+ size_t num_queues_;
+ unsigned int max_tasks_in_flight_;
+ unsigned int num_tasks_in_flight_;
+ unsigned int num_tasks_to_post_;
+ unsigned int num_tasks_to_run_;
+ std::unique_ptr<base::MessageLoop> message_loop_;
+ std::unique_ptr<TaskQueueManager> manager_;
+ std::unique_ptr<base::RunLoop> run_loop_;
+ std::unique_ptr<VirtualTimeDomain> virtual_time_domain_;
+ std::vector<scoped_refptr<base::SingleThreadTaskRunner>> queues_;
+ // TODO(alexclarke): parameterize so we can measure with and without a
+ // TaskTimeObserver.
+ TestTaskTimeObserver test_task_time_observer_;
+};
+
+TEST_F(TaskQueueManagerPerfTest, RunTenThousandDelayedTasks_OneQueue) {
+ if (!base::ThreadTicks::IsSupported())
+ return;
+ Initialize(1u);
+
+ max_tasks_in_flight_ = 200;
+ Benchmark("run 10000 delayed tasks with one queue",
+ base::BindRepeating(
+ &TaskQueueManagerPerfTest::ResetAndCallTestDelayedTask,
+ base::Unretained(this), 10000));
+}
+
+TEST_F(TaskQueueManagerPerfTest, RunTenThousandDelayedTasks_FourQueues) {
+ if (!base::ThreadTicks::IsSupported())
+ return;
+ Initialize(4u);
+
+ max_tasks_in_flight_ = 200;
+ Benchmark("run 10000 delayed tasks with four queues",
+ base::BindRepeating(
+ &TaskQueueManagerPerfTest::ResetAndCallTestDelayedTask,
+ base::Unretained(this), 10000));
+}
+
+TEST_F(TaskQueueManagerPerfTest, RunTenThousandDelayedTasks_EightQueues) {
+ if (!base::ThreadTicks::IsSupported())
+ return;
+ Initialize(8u);
+
+ max_tasks_in_flight_ = 200;
+ Benchmark("run 10000 delayed tasks with eight queues",
+ base::BindRepeating(
+ &TaskQueueManagerPerfTest::ResetAndCallTestDelayedTask,
+ base::Unretained(this), 10000));
+}
+
+TEST_F(TaskQueueManagerPerfTest, RunTenThousandDelayedTasks_ThirtyTwoQueues) {
+ if (!base::ThreadTicks::IsSupported())
+ return;
+ Initialize(32u);
+
+ max_tasks_in_flight_ = 200;
+ Benchmark("run 10000 delayed tasks with eight queues",
+ base::BindRepeating(
+ &TaskQueueManagerPerfTest::ResetAndCallTestDelayedTask,
+ base::Unretained(this), 10000));
+}
+
+// TODO(alexclarke): Add additional tests with different mixes of non-delayed vs
+// delayed tasks.
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_selector.cc b/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_selector.cc
new file mode 100644
index 00000000000..46883b04c24
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_selector.cc
@@ -0,0 +1,388 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_selector.h"
+
+#include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/base/work_queue.h"
+
+namespace blink {
+namespace scheduler {
+namespace internal {
+
+namespace {
+
+TaskQueueSelectorLogic QueuePriorityToSelectorLogic(
+ TaskQueue::QueuePriority priority) {
+ switch (priority) {
+ case TaskQueue::kControlPriority:
+ return TaskQueueSelectorLogic::kControlPriorityLogic;
+ case TaskQueue::kHighestPriority:
+ return TaskQueueSelectorLogic::kHighestPriorityLogic;
+ case TaskQueue::kHighPriority:
+ return TaskQueueSelectorLogic::kHighPriorityLogic;
+ case TaskQueue::kNormalPriority:
+ return TaskQueueSelectorLogic::kNormalPriorityLogic;
+ case TaskQueue::kLowPriority:
+ return TaskQueueSelectorLogic::kLowPriorityLogic;
+ case TaskQueue::kBestEffortPriority:
+ return TaskQueueSelectorLogic::kBestEffortPriorityLogic;
+ default:
+ NOTREACHED();
+ return TaskQueueSelectorLogic::kCount;
+ }
+}
+
+// Helper function used to report the number of times a selector logic is
+// trigerred. This will create a histogram for the enumerated data.
+void ReportTaskSelectionLogic(TaskQueueSelectorLogic selector_logic) {
+ UMA_HISTOGRAM_ENUMERATION("TaskQueueSelector.TaskServicedPerSelectorLogic",
+ selector_logic, TaskQueueSelectorLogic::kCount);
+}
+
+} // namespace
+
+TaskQueueSelector::TaskQueueSelector()
+ : prioritizing_selector_(this, "enabled"),
+ immediate_starvation_count_(0),
+ high_priority_starvation_score_(0),
+ normal_priority_starvation_score_(0),
+ low_priority_starvation_score_(0),
+ task_queue_selector_observer_(nullptr) {}
+
+TaskQueueSelector::~TaskQueueSelector() = default;
+
+void TaskQueueSelector::AddQueue(internal::TaskQueueImpl* queue) {
+ DCHECK(main_thread_checker_.CalledOnValidThread());
+ DCHECK(queue->IsQueueEnabled());
+ prioritizing_selector_.AddQueue(queue, TaskQueue::kNormalPriority);
+}
+
+void TaskQueueSelector::RemoveQueue(internal::TaskQueueImpl* queue) {
+ DCHECK(main_thread_checker_.CalledOnValidThread());
+ if (queue->IsQueueEnabled()) {
+ prioritizing_selector_.RemoveQueue(queue);
+ }
+}
+
+void TaskQueueSelector::EnableQueue(internal::TaskQueueImpl* queue) {
+ DCHECK(main_thread_checker_.CalledOnValidThread());
+ DCHECK(queue->IsQueueEnabled());
+ prioritizing_selector_.AddQueue(queue, queue->GetQueuePriority());
+ if (task_queue_selector_observer_)
+ task_queue_selector_observer_->OnTaskQueueEnabled(queue);
+}
+
+void TaskQueueSelector::DisableQueue(internal::TaskQueueImpl* queue) {
+ DCHECK(main_thread_checker_.CalledOnValidThread());
+ DCHECK(!queue->IsQueueEnabled());
+ prioritizing_selector_.RemoveQueue(queue);
+}
+
+void TaskQueueSelector::SetQueuePriority(internal::TaskQueueImpl* queue,
+ TaskQueue::QueuePriority priority) {
+ DCHECK_LT(priority, TaskQueue::kQueuePriorityCount);
+ DCHECK(main_thread_checker_.CalledOnValidThread());
+ if (queue->IsQueueEnabled()) {
+ prioritizing_selector_.ChangeSetIndex(queue, priority);
+ } else {
+ // Disabled queue is not in any set so we can't use ChangeSetIndex here
+ // and have to assign priority for the queue itself.
+ queue->delayed_work_queue()->AssignSetIndex(priority);
+ queue->immediate_work_queue()->AssignSetIndex(priority);
+ }
+ DCHECK_EQ(priority, queue->GetQueuePriority());
+}
+
+TaskQueue::QueuePriority TaskQueueSelector::NextPriority(
+ TaskQueue::QueuePriority priority) {
+ DCHECK(priority < TaskQueue::kQueuePriorityCount);
+ return static_cast<TaskQueue::QueuePriority>(static_cast<int>(priority) + 1);
+}
+
+TaskQueueSelector::PrioritizingSelector::PrioritizingSelector(
+ TaskQueueSelector* task_queue_selector,
+ const char* name)
+ : task_queue_selector_(task_queue_selector),
+ delayed_work_queue_sets_(TaskQueue::kQueuePriorityCount, name),
+ immediate_work_queue_sets_(TaskQueue::kQueuePriorityCount, name) {}
+
+void TaskQueueSelector::PrioritizingSelector::AddQueue(
+ internal::TaskQueueImpl* queue,
+ TaskQueue::QueuePriority priority) {
+#if DCHECK_IS_ON()
+ DCHECK(!CheckContainsQueueForTest(queue));
+#endif
+ delayed_work_queue_sets_.AddQueue(queue->delayed_work_queue(), priority);
+ immediate_work_queue_sets_.AddQueue(queue->immediate_work_queue(), priority);
+#if DCHECK_IS_ON()
+ DCHECK(CheckContainsQueueForTest(queue));
+#endif
+}
+
+void TaskQueueSelector::PrioritizingSelector::ChangeSetIndex(
+ internal::TaskQueueImpl* queue,
+ TaskQueue::QueuePriority priority) {
+#if DCHECK_IS_ON()
+ DCHECK(CheckContainsQueueForTest(queue));
+#endif
+ delayed_work_queue_sets_.ChangeSetIndex(queue->delayed_work_queue(),
+ priority);
+ immediate_work_queue_sets_.ChangeSetIndex(queue->immediate_work_queue(),
+ priority);
+#if DCHECK_IS_ON()
+ DCHECK(CheckContainsQueueForTest(queue));
+#endif
+}
+
+void TaskQueueSelector::PrioritizingSelector::RemoveQueue(
+ internal::TaskQueueImpl* queue) {
+#if DCHECK_IS_ON()
+ DCHECK(CheckContainsQueueForTest(queue));
+#endif
+ delayed_work_queue_sets_.RemoveQueue(queue->delayed_work_queue());
+ immediate_work_queue_sets_.RemoveQueue(queue->immediate_work_queue());
+
+#if DCHECK_IS_ON()
+ DCHECK(!CheckContainsQueueForTest(queue));
+#endif
+}
+
+bool TaskQueueSelector::PrioritizingSelector::
+ ChooseOldestImmediateTaskWithPriority(TaskQueue::QueuePriority priority,
+ WorkQueue** out_work_queue) const {
+ return immediate_work_queue_sets_.GetOldestQueueInSet(priority,
+ out_work_queue);
+}
+
+bool TaskQueueSelector::PrioritizingSelector::
+ ChooseOldestDelayedTaskWithPriority(TaskQueue::QueuePriority priority,
+ WorkQueue** out_work_queue) const {
+ return delayed_work_queue_sets_.GetOldestQueueInSet(priority, out_work_queue);
+}
+
+bool TaskQueueSelector::PrioritizingSelector::
+ ChooseOldestImmediateOrDelayedTaskWithPriority(
+ TaskQueue::QueuePriority priority,
+ bool* out_chose_delayed_over_immediate,
+ WorkQueue** out_work_queue) const {
+ WorkQueue* immediate_queue;
+ DCHECK_EQ(*out_chose_delayed_over_immediate, false);
+ EnqueueOrder immediate_enqueue_order;
+ if (immediate_work_queue_sets_.GetOldestQueueAndEnqueueOrderInSet(
+ priority, &immediate_queue, &immediate_enqueue_order)) {
+ WorkQueue* delayed_queue;
+ EnqueueOrder delayed_enqueue_order;
+ if (delayed_work_queue_sets_.GetOldestQueueAndEnqueueOrderInSet(
+ priority, &delayed_queue, &delayed_enqueue_order)) {
+ if (immediate_enqueue_order < delayed_enqueue_order) {
+ *out_work_queue = immediate_queue;
+ } else {
+ *out_chose_delayed_over_immediate = true;
+ *out_work_queue = delayed_queue;
+ }
+ } else {
+ *out_work_queue = immediate_queue;
+ }
+ return true;
+ }
+ return delayed_work_queue_sets_.GetOldestQueueInSet(priority, out_work_queue);
+}
+
+bool TaskQueueSelector::PrioritizingSelector::ChooseOldestWithPriority(
+ TaskQueue::QueuePriority priority,
+ bool* out_chose_delayed_over_immediate,
+ WorkQueue** out_work_queue) const {
+ // Select an immediate work queue if we are starving immediate tasks.
+ if (task_queue_selector_->immediate_starvation_count_ >=
+ kMaxDelayedStarvationTasks) {
+ if (ChooseOldestImmediateTaskWithPriority(priority, out_work_queue))
+ return true;
+ return ChooseOldestDelayedTaskWithPriority(priority, out_work_queue);
+ }
+ return ChooseOldestImmediateOrDelayedTaskWithPriority(
+ priority, out_chose_delayed_over_immediate, out_work_queue);
+}
+
+bool TaskQueueSelector::PrioritizingSelector::SelectWorkQueueToService(
+ TaskQueue::QueuePriority max_priority,
+ WorkQueue** out_work_queue,
+ bool* out_chose_delayed_over_immediate) {
+ DCHECK(task_queue_selector_->main_thread_checker_.CalledOnValidThread());
+ DCHECK_EQ(*out_chose_delayed_over_immediate, false);
+
+ // Always service the control queue if it has any work.
+ if (max_priority > TaskQueue::kControlPriority &&
+ ChooseOldestWithPriority(TaskQueue::kControlPriority,
+ out_chose_delayed_over_immediate,
+ out_work_queue)) {
+ ReportTaskSelectionLogic(TaskQueueSelectorLogic::kControlPriorityLogic);
+ return true;
+ }
+
+ // Select from the low priority queue if we are starving it.
+ if (max_priority > TaskQueue::kLowPriority &&
+ task_queue_selector_->low_priority_starvation_score_ >=
+ kMaxLowPriorityStarvationScore &&
+ ChooseOldestWithPriority(TaskQueue::kLowPriority,
+ out_chose_delayed_over_immediate,
+ out_work_queue)) {
+ ReportTaskSelectionLogic(
+ TaskQueueSelectorLogic::kLowPriorityStarvationLogic);
+ return true;
+ }
+
+ // Select from the normal priority queue if we are starving it.
+ if (max_priority > TaskQueue::kNormalPriority &&
+ task_queue_selector_->normal_priority_starvation_score_ >=
+ kMaxNormalPriorityStarvationScore &&
+ ChooseOldestWithPriority(TaskQueue::kNormalPriority,
+ out_chose_delayed_over_immediate,
+ out_work_queue)) {
+ ReportTaskSelectionLogic(
+ TaskQueueSelectorLogic::kNormalPriorityStarvationLogic);
+ return true;
+ }
+
+ // Select from the high priority queue if we are starving it.
+ if (max_priority > TaskQueue::kHighPriority &&
+ task_queue_selector_->high_priority_starvation_score_ >=
+ kMaxHighPriorityStarvationScore &&
+ ChooseOldestWithPriority(TaskQueue::kHighPriority,
+ out_chose_delayed_over_immediate,
+ out_work_queue)) {
+ ReportTaskSelectionLogic(
+ TaskQueueSelectorLogic::kHighPriorityStarvationLogic);
+ return true;
+ }
+
+ // Otherwise choose in priority order.
+ for (TaskQueue::QueuePriority priority = TaskQueue::kHighestPriority;
+ priority < max_priority; priority = NextPriority(priority)) {
+ if (ChooseOldestWithPriority(priority, out_chose_delayed_over_immediate,
+ out_work_queue)) {
+ ReportTaskSelectionLogic(QueuePriorityToSelectorLogic(priority));
+ return true;
+ }
+ }
+ return false;
+}
+
+#if DCHECK_IS_ON() || !defined(NDEBUG)
+bool TaskQueueSelector::PrioritizingSelector::CheckContainsQueueForTest(
+ const internal::TaskQueueImpl* queue) const {
+ bool contains_delayed_work_queue =
+ delayed_work_queue_sets_.ContainsWorkQueueForTest(
+ queue->delayed_work_queue());
+
+ bool contains_immediate_work_queue =
+ immediate_work_queue_sets_.ContainsWorkQueueForTest(
+ queue->immediate_work_queue());
+
+ DCHECK_EQ(contains_delayed_work_queue, contains_immediate_work_queue);
+ return contains_delayed_work_queue;
+}
+#endif
+
+bool TaskQueueSelector::SelectWorkQueueToService(WorkQueue** out_work_queue) {
+ DCHECK(main_thread_checker_.CalledOnValidThread());
+ bool chose_delayed_over_immediate = false;
+ bool found_queue = prioritizing_selector_.SelectWorkQueueToService(
+ TaskQueue::kQueuePriorityCount, out_work_queue,
+ &chose_delayed_over_immediate);
+ if (!found_queue)
+ return false;
+
+ // We could use |(*out_work_queue)->task_queue()->GetQueuePriority()| here but
+ // for re-queued non-nestable tasks |task_queue()| returns null.
+ DidSelectQueueWithPriority(static_cast<TaskQueue::QueuePriority>(
+ (*out_work_queue)->work_queue_set_index()),
+ chose_delayed_over_immediate);
+ return true;
+}
+
+void TaskQueueSelector::DidSelectQueueWithPriority(
+ TaskQueue::QueuePriority priority,
+ bool chose_delayed_over_immediate) {
+ switch (priority) {
+ case TaskQueue::kControlPriority:
+ break;
+ case TaskQueue::kHighestPriority:
+ low_priority_starvation_score_ +=
+ kSmallScoreIncrementForLowPriorityStarvation;
+ normal_priority_starvation_score_ +=
+ kSmallScoreIncrementForNormalPriorityStarvation;
+ high_priority_starvation_score_ +=
+ kSmallScoreIncrementForHighPriorityStarvation;
+ break;
+ case TaskQueue::kHighPriority:
+ low_priority_starvation_score_ +=
+ kLargeScoreIncrementForLowPriorityStarvation;
+ normal_priority_starvation_score_ +=
+ kLargeScoreIncrementForNormalPriorityStarvation;
+ high_priority_starvation_score_ = 0;
+ break;
+ case TaskQueue::kNormalPriority:
+ low_priority_starvation_score_ +=
+ kLargeScoreIncrementForLowPriorityStarvation;
+ normal_priority_starvation_score_ = 0;
+ break;
+ case TaskQueue::kLowPriority:
+ case TaskQueue::kBestEffortPriority:
+ low_priority_starvation_score_ = 0;
+ high_priority_starvation_score_ = 0;
+ normal_priority_starvation_score_ = 0;
+ break;
+ default:
+ NOTREACHED();
+ }
+ if (chose_delayed_over_immediate) {
+ immediate_starvation_count_++;
+ } else {
+ immediate_starvation_count_ = 0;
+ }
+}
+
+void TaskQueueSelector::AsValueInto(
+ base::trace_event::TracedValue* state) const {
+ DCHECK(main_thread_checker_.CalledOnValidThread());
+ state->SetInteger("high_priority_starvation_score",
+ high_priority_starvation_score_);
+ state->SetInteger("normal_priority_starvation_score",
+ normal_priority_starvation_score_);
+ state->SetInteger("low_priority_starvation_score",
+ low_priority_starvation_score_);
+ state->SetInteger("immediate_starvation_count", immediate_starvation_count_);
+}
+
+void TaskQueueSelector::SetTaskQueueSelectorObserver(Observer* observer) {
+ task_queue_selector_observer_ = observer;
+}
+
+bool TaskQueueSelector::AllEnabledWorkQueuesAreEmpty() const {
+ DCHECK(main_thread_checker_.CalledOnValidThread());
+ for (TaskQueue::QueuePriority priority = TaskQueue::kControlPriority;
+ priority < TaskQueue::kQueuePriorityCount;
+ priority = NextPriority(priority)) {
+ if (!prioritizing_selector_.delayed_work_queue_sets()->IsSetEmpty(
+ priority) ||
+ !prioritizing_selector_.immediate_work_queue_sets()->IsSetEmpty(
+ priority)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void TaskQueueSelector::SetImmediateStarvationCountForTest(
+ size_t immediate_starvation_count) {
+ immediate_starvation_count_ = immediate_starvation_count;
+}
+
+} // namespace internal
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_selector.h b/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_selector.h
new file mode 100644
index 00000000000..8f39be19b6c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_selector.h
@@ -0,0 +1,225 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TASK_QUEUE_SELECTOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TASK_QUEUE_SELECTOR_H_
+
+#include <stddef.h>
+
+#include <set>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/pending_task.h"
+#include "base/threading/thread_checker.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_selector_logic.h"
+#include "third_party/blink/renderer/platform/scheduler/base/work_queue_sets.h"
+
+namespace blink {
+namespace scheduler {
+namespace internal {
+
+// TaskQueueSelector is used by the SchedulerHelper to enable prioritization
+// of particular task queues.
+class PLATFORM_EXPORT TaskQueueSelector {
+ public:
+ TaskQueueSelector();
+ ~TaskQueueSelector();
+
+ // Called to register a queue that can be selected. This function is called
+ // on the main thread.
+ void AddQueue(internal::TaskQueueImpl* queue);
+
+ // The specified work will no longer be considered for selection. This
+ // function is called on the main thread.
+ void RemoveQueue(internal::TaskQueueImpl* queue);
+
+ // Make |queue| eligible for selection. This function is called on the main
+ // thread. Must only be called if |queue| is disabled.
+ void EnableQueue(internal::TaskQueueImpl* queue);
+
+ // Disable selection from |queue|. Must only be called if |queue| is enabled.
+ void DisableQueue(internal::TaskQueueImpl* queue);
+
+ // Called get or set the priority of |queue|.
+ void SetQueuePriority(internal::TaskQueueImpl* queue,
+ TaskQueue::QueuePriority priority);
+
+ // Called to choose the work queue from which the next task should be taken
+ // and run. Return true if |out_work_queue| indicates the queue to service or
+ // false to avoid running any task.
+ //
+ // This function is called on the main thread.
+ bool SelectWorkQueueToService(WorkQueue** out_work_queue);
+
+ // Serialize the selector state for tracing.
+ void AsValueInto(base::trace_event::TracedValue* state) const;
+
+ class PLATFORM_EXPORT Observer {
+ public:
+ virtual ~Observer() = default;
+
+ // Called when |queue| transitions from disabled to enabled.
+ virtual void OnTaskQueueEnabled(internal::TaskQueueImpl* queue) = 0;
+ };
+
+ // Called once to set the Observer. This function is called
+ // on the main thread. If |observer| is null, then no callbacks will occur.
+ void SetTaskQueueSelectorObserver(Observer* observer);
+
+ // Returns true if all the enabled work queues are empty. Returns false
+ // otherwise.
+ bool AllEnabledWorkQueuesAreEmpty() const;
+
+ protected:
+ class PLATFORM_EXPORT PrioritizingSelector {
+ public:
+ PrioritizingSelector(TaskQueueSelector* task_queue_selector,
+ const char* name);
+
+ void ChangeSetIndex(internal::TaskQueueImpl* queue,
+ TaskQueue::QueuePriority priority);
+ void AddQueue(internal::TaskQueueImpl* queue,
+ TaskQueue::QueuePriority priority);
+ void RemoveQueue(internal::TaskQueueImpl* queue);
+
+ bool SelectWorkQueueToService(TaskQueue::QueuePriority max_priority,
+ WorkQueue** out_work_queue,
+ bool* out_chose_delayed_over_immediate);
+
+ WorkQueueSets* delayed_work_queue_sets() {
+ return &delayed_work_queue_sets_;
+ }
+ WorkQueueSets* immediate_work_queue_sets() {
+ return &immediate_work_queue_sets_;
+ }
+
+ const WorkQueueSets* delayed_work_queue_sets() const {
+ return &delayed_work_queue_sets_;
+ }
+ const WorkQueueSets* immediate_work_queue_sets() const {
+ return &immediate_work_queue_sets_;
+ }
+
+ bool ChooseOldestWithPriority(TaskQueue::QueuePriority priority,
+ bool* out_chose_delayed_over_immediate,
+ WorkQueue** out_work_queue) const;
+
+#if DCHECK_IS_ON() || !defined(NDEBUG)
+ bool CheckContainsQueueForTest(const internal::TaskQueueImpl* queue) const;
+#endif
+
+ private:
+ bool ChooseOldestImmediateTaskWithPriority(
+ TaskQueue::QueuePriority priority,
+ WorkQueue** out_work_queue) const;
+
+ bool ChooseOldestDelayedTaskWithPriority(TaskQueue::QueuePriority priority,
+ WorkQueue** out_work_queue) const;
+
+ // Return true if |out_queue| contains the queue with the oldest pending
+ // task from the set of queues of |priority|, or false if all queues of that
+ // priority are empty. In addition |out_chose_delayed_over_immediate| is set
+ // to true iff we chose a delayed work queue in favour of an immediate work
+ // queue.
+ bool ChooseOldestImmediateOrDelayedTaskWithPriority(
+ TaskQueue::QueuePriority priority,
+ bool* out_chose_delayed_over_immediate,
+ WorkQueue** out_work_queue) const;
+
+ const TaskQueueSelector* task_queue_selector_;
+ WorkQueueSets delayed_work_queue_sets_;
+ WorkQueueSets immediate_work_queue_sets_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrioritizingSelector);
+ };
+
+ // Return true if |out_queue| contains the queue with the oldest pending task
+ // from the set of queues of |priority|, or false if all queues of that
+ // priority are empty. In addition |out_chose_delayed_over_immediate| is set
+ // to true iff we chose a delayed work queue in favour of an immediate work
+ // queue. This method will force select an immediate task if those are being
+ // starved by delayed tasks.
+ void SetImmediateStarvationCountForTest(size_t immediate_starvation_count);
+
+ PrioritizingSelector* prioritizing_selector_for_test() {
+ return &prioritizing_selector_;
+ }
+
+ private:
+ // Returns the priority which is next after |priority|.
+ static TaskQueue::QueuePriority NextPriority(
+ TaskQueue::QueuePriority priority);
+
+ bool SelectWorkQueueToServiceInternal(WorkQueue** out_work_queue);
+
+ // Called whenever the selector chooses a task queue for execution with the
+ // priority |priority|.
+ void DidSelectQueueWithPriority(TaskQueue::QueuePriority priority,
+ bool chose_delayed_over_immediate);
+
+ // Maximum score to accumulate before high priority tasks are run even in
+ // the presence of highest priority tasks.
+ static const size_t kMaxHighPriorityStarvationScore = 3;
+
+ // Increment to be applied to the high priority starvation score when a task
+ // should have only a small effect on the score. E.g. A number of highest
+ // priority tasks must run before the high priority queue is considered
+ // starved.
+ static const size_t kSmallScoreIncrementForHighPriorityStarvation = 1;
+
+ // Maximum score to accumulate before normal priority tasks are run even in
+ // the presence of higher priority tasks i.e. highest and high priority tasks.
+ static const size_t kMaxNormalPriorityStarvationScore = 5;
+
+ // Increment to be applied to the normal priority starvation score when a task
+ // should have a large effect on the score. E.g Only a few high priority
+ // priority tasks must run before the normal priority queue is considered
+ // starved.
+ static const size_t kLargeScoreIncrementForNormalPriorityStarvation = 2;
+
+ // Increment to be applied to the normal priority starvation score when a task
+ // should have only a small effect on the score. E.g. A number of highest
+ // priority tasks must run before the normal priority queue is considered
+ // starved.
+ static const size_t kSmallScoreIncrementForNormalPriorityStarvation = 1;
+
+ // Maximum score to accumulate before low priority tasks are run even in the
+ // presence of highest, high, or normal priority tasks.
+ static const size_t kMaxLowPriorityStarvationScore = 25;
+
+ // Increment to be applied to the low priority starvation score when a task
+ // should have a large effect on the score. E.g. Only a few normal/high
+ // priority tasks must run before the low priority queue is considered
+ // starved.
+ static const size_t kLargeScoreIncrementForLowPriorityStarvation = 5;
+
+ // Increment to be applied to the low priority starvation score when a task
+ // should have only a small effect on the score. E.g. A lot of highest
+ // priority tasks must run before the low priority queue is considered
+ // starved.
+ static const size_t kSmallScoreIncrementForLowPriorityStarvation = 1;
+
+ // Maximum number of delayed tasks tasks which can be run while there's a
+ // waiting non-delayed task.
+ static const size_t kMaxDelayedStarvationTasks = 3;
+
+ private:
+ base::ThreadChecker main_thread_checker_;
+
+ PrioritizingSelector prioritizing_selector_;
+ size_t immediate_starvation_count_;
+ size_t high_priority_starvation_score_;
+ size_t normal_priority_starvation_score_;
+ size_t low_priority_starvation_score_;
+
+ Observer* task_queue_selector_observer_; // NOT OWNED
+ DISALLOW_COPY_AND_ASSIGN(TaskQueueSelector);
+};
+
+} // namespace internal
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TASK_QUEUE_SELECTOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_selector_logic.h b/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_selector_logic.h
new file mode 100644
index 00000000000..e29f25788f3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_selector_logic.h
@@ -0,0 +1,35 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TASK_QUEUE_SELECTOR_LOGIC_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TASK_QUEUE_SELECTOR_LOGIC_H_
+
+namespace blink {
+namespace scheduler {
+
+// Used to describe the logic trigerred when a task queue is selected to
+// service.
+// This enum is used for histograms and should not be renumbered.
+enum class TaskQueueSelectorLogic {
+
+ // Selected due to priority rules.
+ kControlPriorityLogic = 0,
+ kHighestPriorityLogic = 1,
+ kHighPriorityLogic = 2,
+ kNormalPriorityLogic = 3,
+ kLowPriorityLogic = 4,
+ kBestEffortPriorityLogic = 5,
+
+ // Selected due to starvation logic.
+ kHighPriorityStarvationLogic = 6,
+ kNormalPriorityStarvationLogic = 7,
+ kLowPriorityStarvationLogic = 8,
+
+ kCount = 9,
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_selector_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_selector_unittest.cc
new file mode 100644
index 00000000000..46a59f25aa5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/task_queue_selector_unittest.cc
@@ -0,0 +1,714 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_selector.h"
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/pending_task.h"
+#include "base/test/histogram_tester.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/base/virtual_time_domain.h"
+#include "third_party/blink/renderer/platform/scheduler/base/work_queue.h"
+#include "third_party/blink/renderer/platform/scheduler/base/work_queue_sets.h"
+
+using testing::_;
+
+namespace blink {
+namespace scheduler {
+namespace internal {
+// To avoid symbol collisions in jumbo builds.
+namespace task_queue_selector_unittest {
+
+class MockObserver : public TaskQueueSelector::Observer {
+ public:
+ MockObserver() = default;
+ virtual ~MockObserver() = default;
+
+ MOCK_METHOD1(OnTaskQueueEnabled, void(internal::TaskQueueImpl*));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockObserver);
+};
+
+class TaskQueueSelectorForTest : public TaskQueueSelector {
+ public:
+ using TaskQueueSelector::prioritizing_selector_for_test;
+ using TaskQueueSelector::PrioritizingSelector;
+ using TaskQueueSelector::SetImmediateStarvationCountForTest;
+};
+
+class TaskQueueSelectorTest : public testing::Test {
+ public:
+ TaskQueueSelectorTest()
+ : test_closure_(
+ base::BindRepeating(&TaskQueueSelectorTest::TestFunction)) {}
+ ~TaskQueueSelectorTest() override = default;
+
+ TaskQueueSelectorForTest::PrioritizingSelector* prioritizing_selector() {
+ return selector_.prioritizing_selector_for_test();
+ }
+
+ WorkQueueSets* delayed_work_queue_sets() {
+ return prioritizing_selector()->delayed_work_queue_sets();
+ }
+ WorkQueueSets* immediate_work_queue_sets() {
+ return prioritizing_selector()->immediate_work_queue_sets();
+ }
+
+ void PushTasks(const size_t queue_indices[], size_t num_tasks) {
+ std::set<size_t> changed_queue_set;
+ for (size_t i = 0; i < num_tasks; i++) {
+ changed_queue_set.insert(queue_indices[i]);
+ task_queues_[queue_indices[i]]->immediate_work_queue()->Push(
+ TaskQueueImpl::Task(TaskQueue::PostedTask(test_closure_, FROM_HERE),
+ base::TimeTicks(), 0, i));
+ }
+ }
+
+ void PushTasksWithEnqueueOrder(const size_t queue_indices[],
+ const size_t enqueue_orders[],
+ size_t num_tasks) {
+ std::set<size_t> changed_queue_set;
+ for (size_t i = 0; i < num_tasks; i++) {
+ changed_queue_set.insert(queue_indices[i]);
+ task_queues_[queue_indices[i]]->immediate_work_queue()->Push(
+ TaskQueueImpl::Task(TaskQueue::PostedTask(test_closure_, FROM_HERE),
+ base::TimeTicks(), 0, enqueue_orders[i]));
+ }
+ }
+
+ std::vector<size_t> PopTasks() {
+ std::vector<size_t> order;
+ WorkQueue* chosen_work_queue;
+ while (selector_.SelectWorkQueueToService(&chosen_work_queue)) {
+ size_t chosen_queue_index =
+ queue_to_index_map_.find(chosen_work_queue->task_queue())->second;
+ order.push_back(chosen_queue_index);
+ chosen_work_queue->PopTaskForTesting();
+ immediate_work_queue_sets()->OnPopQueue(chosen_work_queue);
+ }
+ return order;
+ }
+
+ static void TestFunction() {}
+
+ protected:
+ void SetUp() final {
+ virtual_time_domain_ = base::WrapUnique<VirtualTimeDomain>(
+ new VirtualTimeDomain(base::TimeTicks()));
+ for (size_t i = 0; i < kTaskQueueCount; i++) {
+ std::unique_ptr<TaskQueueImpl> task_queue =
+ std::make_unique<TaskQueueImpl>(nullptr, virtual_time_domain_.get(),
+ TaskQueue::Spec("test"));
+ selector_.AddQueue(task_queue.get());
+ task_queues_.push_back(std::move(task_queue));
+ }
+ for (size_t i = 0; i < kTaskQueueCount; i++) {
+ EXPECT_EQ(TaskQueue::kNormalPriority, task_queues_[i]->GetQueuePriority())
+ << i;
+ queue_to_index_map_.insert(std::make_pair(task_queues_[i].get(), i));
+ }
+ histogram_tester_.reset(new base::HistogramTester());
+ }
+
+ void TearDown() final {
+ for (std::unique_ptr<TaskQueueImpl>& task_queue : task_queues_) {
+ // Note since this test doesn't have a TaskQueueManager we need to
+ // manually remove |task_queue| from the |selector_|. Normally
+ // UnregisterTaskQueue would do that.
+ selector_.RemoveQueue(task_queue.get());
+ task_queue->UnregisterTaskQueue();
+ }
+ }
+
+ std::unique_ptr<TaskQueueImpl> NewTaskQueueWithBlockReporting() {
+ return std::make_unique<TaskQueueImpl>(nullptr, virtual_time_domain_.get(),
+ TaskQueue::Spec("test"));
+ }
+
+ const size_t kTaskQueueCount = 5;
+ base::RepeatingClosure test_closure_;
+ TaskQueueSelectorForTest selector_;
+ std::unique_ptr<VirtualTimeDomain> virtual_time_domain_;
+ std::vector<std::unique_ptr<TaskQueueImpl>> task_queues_;
+ std::map<TaskQueueImpl*, size_t> queue_to_index_map_;
+ std::unique_ptr<base::HistogramTester> histogram_tester_;
+};
+
+TEST_F(TaskQueueSelectorTest, TestDefaultPriority) {
+ size_t queue_order[] = {4, 3, 2, 1, 0};
+ PushTasks(queue_order, 5);
+ EXPECT_THAT(PopTasks(), testing::ElementsAre(4, 3, 2, 1, 0));
+ EXPECT_EQ(histogram_tester_->GetBucketCount(
+ "TaskQueueSelector.TaskServicedPerSelectorLogic",
+ static_cast<int>(TaskQueueSelectorLogic::kNormalPriorityLogic)),
+ 5);
+}
+
+TEST_F(TaskQueueSelectorTest, TestHighestPriority) {
+ size_t queue_order[] = {0, 1, 2, 3, 4};
+ PushTasks(queue_order, 5);
+ selector_.SetQueuePriority(task_queues_[2].get(),
+ TaskQueue::kHighestPriority);
+ EXPECT_THAT(PopTasks(), ::testing::ElementsAre(2, 0, 1, 3, 4));
+ EXPECT_EQ(
+ histogram_tester_->GetBucketCount(
+ "TaskQueueSelector.TaskServicedPerSelectorLogic",
+ static_cast<int>(TaskQueueSelectorLogic::kHighestPriorityLogic)),
+ 1);
+}
+
+TEST_F(TaskQueueSelectorTest, TestHighPriority) {
+ size_t queue_order[] = {0, 1, 2, 3, 4};
+ PushTasks(queue_order, 5);
+ selector_.SetQueuePriority(task_queues_[2].get(),
+ TaskQueue::kHighestPriority);
+ selector_.SetQueuePriority(task_queues_[1].get(), TaskQueue::kHighPriority);
+ selector_.SetQueuePriority(task_queues_[0].get(), TaskQueue::kLowPriority);
+ EXPECT_THAT(PopTasks(), ::testing::ElementsAre(2, 1, 3, 4, 0));
+ EXPECT_EQ(histogram_tester_->GetBucketCount(
+ "TaskQueueSelector.TaskServicedPerSelectorLogic",
+ static_cast<int>(TaskQueueSelectorLogic::kHighPriorityLogic)),
+ 1);
+}
+
+TEST_F(TaskQueueSelectorTest, TestLowPriority) {
+ size_t queue_order[] = {0, 1, 2, 3, 4};
+ PushTasks(queue_order, 5);
+ selector_.SetQueuePriority(task_queues_[2].get(), TaskQueue::kLowPriority);
+ EXPECT_THAT(PopTasks(), testing::ElementsAre(0, 1, 3, 4, 2));
+ EXPECT_EQ(histogram_tester_->GetBucketCount(
+ "TaskQueueSelector.TaskServicedPerSelectorLogic",
+ static_cast<int>(TaskQueueSelectorLogic::kLowPriorityLogic)),
+ 1);
+}
+
+TEST_F(TaskQueueSelectorTest, TestBestEffortPriority) {
+ size_t queue_order[] = {0, 1, 2, 3, 4};
+ PushTasks(queue_order, 5);
+ selector_.SetQueuePriority(task_queues_[0].get(),
+ TaskQueue::kBestEffortPriority);
+ selector_.SetQueuePriority(task_queues_[2].get(), TaskQueue::kLowPriority);
+ selector_.SetQueuePriority(task_queues_[3].get(),
+ TaskQueue::kHighestPriority);
+ EXPECT_THAT(PopTasks(), ::testing::ElementsAre(3, 1, 4, 2, 0));
+ EXPECT_EQ(
+ histogram_tester_->GetBucketCount(
+ "TaskQueueSelector.TaskServicedPerSelectorLogic",
+ static_cast<int>(TaskQueueSelectorLogic::kBestEffortPriorityLogic)),
+ 1);
+}
+
+TEST_F(TaskQueueSelectorTest, TestControlPriority) {
+ size_t queue_order[] = {0, 1, 2, 3, 4};
+ PushTasks(queue_order, 5);
+ selector_.SetQueuePriority(task_queues_[4].get(),
+ TaskQueue::kControlPriority);
+ EXPECT_EQ(TaskQueue::kControlPriority, task_queues_[4]->GetQueuePriority());
+ selector_.SetQueuePriority(task_queues_[2].get(),
+ TaskQueue::kHighestPriority);
+ EXPECT_EQ(TaskQueue::kHighestPriority, task_queues_[2]->GetQueuePriority());
+ EXPECT_THAT(PopTasks(), ::testing::ElementsAre(4, 2, 0, 1, 3));
+ EXPECT_EQ(
+ histogram_tester_->GetBucketCount(
+ "TaskQueueSelector.TaskServicedPerSelectorLogic",
+ static_cast<int>(TaskQueueSelectorLogic::kControlPriorityLogic)),
+ 1);
+}
+
+TEST_F(TaskQueueSelectorTest, TestObserverWithEnabledQueue) {
+ task_queues_[1]->SetQueueEnabledForTest(false);
+ selector_.DisableQueue(task_queues_[1].get());
+ MockObserver mock_observer;
+ selector_.SetTaskQueueSelectorObserver(&mock_observer);
+ EXPECT_CALL(mock_observer, OnTaskQueueEnabled(_)).Times(1);
+ task_queues_[1]->SetQueueEnabledForTest(true);
+ selector_.EnableQueue(task_queues_[1].get());
+}
+
+TEST_F(TaskQueueSelectorTest,
+ TestObserverWithSetQueuePriorityAndQueueAlreadyEnabled) {
+ selector_.SetQueuePriority(task_queues_[1].get(),
+ TaskQueue::kHighestPriority);
+ MockObserver mock_observer;
+ selector_.SetTaskQueueSelectorObserver(&mock_observer);
+ EXPECT_CALL(mock_observer, OnTaskQueueEnabled(_)).Times(0);
+ selector_.SetQueuePriority(task_queues_[1].get(), TaskQueue::kNormalPriority);
+}
+
+TEST_F(TaskQueueSelectorTest, TestDisableEnable) {
+ MockObserver mock_observer;
+ selector_.SetTaskQueueSelectorObserver(&mock_observer);
+
+ size_t queue_order[] = {0, 1, 2, 3, 4};
+ PushTasks(queue_order, 5);
+ task_queues_[2]->SetQueueEnabledForTest(false);
+ selector_.DisableQueue(task_queues_[2].get());
+ task_queues_[4]->SetQueueEnabledForTest(false);
+ selector_.DisableQueue(task_queues_[4].get());
+ // Disabling a queue should not affect its priority.
+ EXPECT_EQ(TaskQueue::kNormalPriority, task_queues_[2]->GetQueuePriority());
+ EXPECT_EQ(TaskQueue::kNormalPriority, task_queues_[4]->GetQueuePriority());
+ EXPECT_THAT(PopTasks(), testing::ElementsAre(0, 1, 3));
+
+ EXPECT_CALL(mock_observer, OnTaskQueueEnabled(_)).Times(2);
+ task_queues_[2]->SetQueueEnabledForTest(true);
+ selector_.EnableQueue(task_queues_[2].get());
+ selector_.SetQueuePriority(task_queues_[2].get(),
+ TaskQueue::kBestEffortPriority);
+ EXPECT_THAT(PopTasks(), testing::ElementsAre(2));
+ task_queues_[4]->SetQueueEnabledForTest(true);
+ selector_.EnableQueue(task_queues_[4].get());
+ EXPECT_THAT(PopTasks(), testing::ElementsAre(4));
+}
+
+TEST_F(TaskQueueSelectorTest, TestDisableChangePriorityThenEnable) {
+ EXPECT_TRUE(task_queues_[2]->delayed_work_queue()->Empty());
+ EXPECT_TRUE(task_queues_[2]->immediate_work_queue()->Empty());
+
+ task_queues_[2]->SetQueueEnabledForTest(false);
+ selector_.SetQueuePriority(task_queues_[2].get(),
+ TaskQueue::kHighestPriority);
+
+ size_t queue_order[] = {0, 1, 2, 3, 4};
+ PushTasks(queue_order, 5);
+
+ EXPECT_TRUE(task_queues_[2]->delayed_work_queue()->Empty());
+ EXPECT_FALSE(task_queues_[2]->immediate_work_queue()->Empty());
+ task_queues_[2]->SetQueueEnabledForTest(true);
+
+ EXPECT_EQ(TaskQueue::kHighestPriority, task_queues_[2]->GetQueuePriority());
+ EXPECT_THAT(PopTasks(), ::testing::ElementsAre(2, 0, 1, 3, 4));
+}
+
+TEST_F(TaskQueueSelectorTest, TestEmptyQueues) {
+ WorkQueue* chosen_work_queue = nullptr;
+ EXPECT_FALSE(selector_.SelectWorkQueueToService(&chosen_work_queue));
+
+ // Test only disabled queues.
+ size_t queue_order[] = {0};
+ PushTasks(queue_order, 1);
+ task_queues_[0]->SetQueueEnabledForTest(false);
+ selector_.DisableQueue(task_queues_[0].get());
+ EXPECT_FALSE(selector_.SelectWorkQueueToService(&chosen_work_queue));
+
+ // These tests are unusual since there's no TQM. To avoid a later DCHECK when
+ // deleting the task queue, we re-enable the queue here so the selector
+ // doesn't get out of sync.
+ task_queues_[0]->SetQueueEnabledForTest(true);
+ selector_.EnableQueue(task_queues_[0].get());
+}
+
+TEST_F(TaskQueueSelectorTest, TestAge) {
+ size_t enqueue_order[] = {10, 1, 2, 9, 4};
+ size_t queue_order[] = {0, 1, 2, 3, 4};
+ PushTasksWithEnqueueOrder(queue_order, enqueue_order, 5);
+ EXPECT_THAT(PopTasks(), testing::ElementsAre(1, 2, 4, 3, 0));
+}
+
+TEST_F(TaskQueueSelectorTest, TestControlStarvesOthers) {
+ size_t queue_order[] = {0, 1, 2, 3};
+ PushTasks(queue_order, 4);
+ selector_.SetQueuePriority(task_queues_[3].get(),
+ TaskQueue::kControlPriority);
+ selector_.SetQueuePriority(task_queues_[2].get(),
+ TaskQueue::kHighestPriority);
+ selector_.SetQueuePriority(task_queues_[1].get(),
+ TaskQueue::kBestEffortPriority);
+ for (int i = 0; i < 100; i++) {
+ WorkQueue* chosen_work_queue = nullptr;
+ ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
+ EXPECT_EQ(task_queues_[3].get(), chosen_work_queue->task_queue());
+ // Don't remove task from queue to simulate all queues still being full.
+ }
+}
+
+TEST_F(TaskQueueSelectorTest, TestHighestPriorityDoesNotStarveHigh) {
+ size_t queue_order[] = {0, 1};
+ PushTasks(queue_order, 2);
+ selector_.SetQueuePriority(task_queues_[0].get(),
+ TaskQueue::kHighestPriority);
+ selector_.SetQueuePriority(task_queues_[1].get(), TaskQueue::kHighPriority);
+
+ size_t counts[] = {0, 0};
+ for (int i = 0; i < 100; i++) {
+ WorkQueue* chosen_work_queue = nullptr;
+ ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
+ size_t chosen_queue_index =
+ queue_to_index_map_.find(chosen_work_queue->task_queue())->second;
+ counts[chosen_queue_index]++;
+ // Don't remove task from queue to simulate all queues still being full.
+ }
+ EXPECT_GT(counts[1], 0ul); // Check highest doesn't starve high.
+ EXPECT_GT(counts[0], counts[1]); // Check highest gets more chance to run.
+}
+
+TEST_F(TaskQueueSelectorTest, TestHighestPriorityDoesNotStarveHighOrNormal) {
+ size_t queue_order[] = {0, 1, 2};
+ PushTasks(queue_order, 3);
+ selector_.SetQueuePriority(task_queues_[0].get(),
+ TaskQueue::kHighestPriority);
+ selector_.SetQueuePriority(task_queues_[1].get(), TaskQueue::kHighPriority);
+
+ size_t counts[] = {0, 0, 0};
+ for (int i = 0; i < 100; i++) {
+ WorkQueue* chosen_work_queue = nullptr;
+ ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
+ size_t chosen_queue_index =
+ queue_to_index_map_.find(chosen_work_queue->task_queue())->second;
+ counts[chosen_queue_index]++;
+ // Don't remove task from queue to simulate all queues still being full.
+ }
+
+ // Check highest runs more frequently then high.
+ EXPECT_GT(counts[0], counts[1]);
+
+ // Check high runs at least as frequently as normal.
+ EXPECT_GE(counts[1], counts[2]);
+
+ // Check normal isn't starved.
+ EXPECT_GT(counts[2], 0ul);
+}
+
+TEST_F(TaskQueueSelectorTest,
+ TestHighestPriorityDoesNotStarveHighOrNormalOrLow) {
+ size_t queue_order[] = {0, 1, 2, 3};
+ PushTasks(queue_order, 4);
+ selector_.SetQueuePriority(task_queues_[0].get(),
+ TaskQueue::kHighestPriority);
+ selector_.SetQueuePriority(task_queues_[1].get(), TaskQueue::kHighPriority);
+ selector_.SetQueuePriority(task_queues_[3].get(), TaskQueue::kLowPriority);
+
+ size_t counts[] = {0, 0, 0, 0};
+ for (int i = 0; i < 100; i++) {
+ WorkQueue* chosen_work_queue = nullptr;
+ ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
+ size_t chosen_queue_index =
+ queue_to_index_map_.find(chosen_work_queue->task_queue())->second;
+ counts[chosen_queue_index]++;
+ // Don't remove task from queue to simulate all queues still being full.
+ }
+
+ // Check highest runs more frequently then high.
+ EXPECT_GT(counts[0], counts[1]);
+
+ // Check high runs at least as frequently as normal.
+ EXPECT_GE(counts[1], counts[2]);
+
+ // Check normal runs more frequently than low.
+ EXPECT_GT(counts[2], counts[3]);
+
+ // Check low isn't starved.
+ EXPECT_GT(counts[3], 0ul);
+}
+
+TEST_F(TaskQueueSelectorTest, TestHighPriorityDoesNotStarveNormal) {
+ size_t queue_order[] = {0, 1};
+ PushTasks(queue_order, 2);
+
+ selector_.SetQueuePriority(task_queues_[0].get(), TaskQueue::kHighPriority);
+
+ size_t counts[] = {0, 0, 0, 0};
+ for (int i = 0; i < 100; i++) {
+ WorkQueue* chosen_work_queue = nullptr;
+ ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
+ size_t chosen_queue_index =
+ queue_to_index_map_.find(chosen_work_queue->task_queue())->second;
+ counts[chosen_queue_index]++;
+ // Don't remove task from queue to simulate all queues still being full.
+ }
+
+ // Check high runs more frequently then normal.
+ EXPECT_GT(counts[0], counts[1]);
+
+ // Check low isn't starved.
+ EXPECT_GT(counts[1], 0ul);
+}
+
+TEST_F(TaskQueueSelectorTest, TestHighPriorityDoesNotStarveNormalOrLow) {
+ size_t queue_order[] = {0, 1, 2};
+ PushTasks(queue_order, 3);
+ selector_.SetQueuePriority(task_queues_[0].get(), TaskQueue::kHighPriority);
+ selector_.SetQueuePriority(task_queues_[2].get(), TaskQueue::kLowPriority);
+
+ size_t counts[] = {0, 0, 0};
+ for (int i = 0; i < 100; i++) {
+ WorkQueue* chosen_work_queue = nullptr;
+ ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
+ size_t chosen_queue_index =
+ queue_to_index_map_.find(chosen_work_queue->task_queue())->second;
+ counts[chosen_queue_index]++;
+ // Don't remove task from queue to simulate all queues still being full.
+ }
+
+ // Check high runs more frequently than normal.
+ EXPECT_GT(counts[0], counts[1]);
+
+ // Check normal runs more frequently than low.
+ EXPECT_GT(counts[1], counts[2]);
+
+ // Check low isn't starved.
+ EXPECT_GT(counts[2], 0ul);
+}
+
+TEST_F(TaskQueueSelectorTest, TestNormalPriorityDoesNotStarveLow) {
+ size_t queue_order[] = {0, 1, 2};
+ PushTasks(queue_order, 3);
+ selector_.SetQueuePriority(task_queues_[0].get(), TaskQueue::kLowPriority);
+ selector_.SetQueuePriority(task_queues_[1].get(),
+ TaskQueue::kBestEffortPriority);
+ size_t counts[] = {0, 0, 0};
+ for (int i = 0; i < 100; i++) {
+ WorkQueue* chosen_work_queue = nullptr;
+ ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
+ size_t chosen_queue_index =
+ queue_to_index_map_.find(chosen_work_queue->task_queue())->second;
+ counts[chosen_queue_index]++;
+ // Don't remove task from queue to simulate all queues still being full.
+ }
+ EXPECT_GT(counts[0], 0ul); // Check normal doesn't starve low.
+ EXPECT_GT(counts[2], counts[0]); // Check normal gets more chance to run.
+ EXPECT_EQ(0ul, counts[1]); // Check best effort is starved.
+}
+
+TEST_F(TaskQueueSelectorTest, TestBestEffortGetsStarved) {
+ size_t queue_order[] = {0, 1};
+ PushTasks(queue_order, 2);
+ selector_.SetQueuePriority(task_queues_[0].get(),
+ TaskQueue::kBestEffortPriority);
+ EXPECT_EQ(TaskQueue::kNormalPriority, task_queues_[1]->GetQueuePriority());
+
+ // Check that normal priority tasks starve best effort.
+ WorkQueue* chosen_work_queue = nullptr;
+ for (int i = 0; i < 100; i++) {
+ ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
+ EXPECT_EQ(task_queues_[1].get(), chosen_work_queue->task_queue());
+ // Don't remove task from queue to simulate all queues still being full.
+ }
+
+ // Check that highest priority tasks starve best effort.
+ selector_.SetQueuePriority(task_queues_[1].get(),
+ TaskQueue::kHighestPriority);
+ for (int i = 0; i < 100; i++) {
+ ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
+ EXPECT_EQ(task_queues_[1].get(), chosen_work_queue->task_queue());
+ // Don't remove task from queue to simulate all queues still being full.
+ }
+
+ // Check that high priority tasks starve best effort.
+ selector_.SetQueuePriority(task_queues_[1].get(), TaskQueue::kHighPriority);
+ for (int i = 0; i < 100; i++) {
+ ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
+ EXPECT_EQ(task_queues_[1].get(), chosen_work_queue->task_queue());
+ // Don't remove task from queue to simulate all queues still being full.
+ }
+
+ // Check that low priority tasks starve best effort.
+ selector_.SetQueuePriority(task_queues_[1].get(), TaskQueue::kLowPriority);
+ for (int i = 0; i < 100; i++) {
+ ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
+ EXPECT_EQ(task_queues_[1].get(), chosen_work_queue->task_queue());
+ // Don't remove task from queue to simulate all queues still being full.
+ }
+
+ // Check that control priority tasks starve best effort.
+ selector_.SetQueuePriority(task_queues_[1].get(),
+ TaskQueue::kControlPriority);
+ for (int i = 0; i < 100; i++) {
+ ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
+ EXPECT_EQ(task_queues_[1].get(), chosen_work_queue->task_queue());
+ // Don't remove task from queue to simulate all queues still being full.
+ }
+}
+
+TEST_F(TaskQueueSelectorTest, AllEnabledWorkQueuesAreEmpty) {
+ EXPECT_TRUE(selector_.AllEnabledWorkQueuesAreEmpty());
+ size_t queue_order[] = {0, 1};
+ PushTasks(queue_order, 2);
+
+ EXPECT_FALSE(selector_.AllEnabledWorkQueuesAreEmpty());
+ PopTasks();
+ EXPECT_TRUE(selector_.AllEnabledWorkQueuesAreEmpty());
+}
+
+TEST_F(TaskQueueSelectorTest, AllEnabledWorkQueuesAreEmpty_ControlPriority) {
+ size_t queue_order[] = {0};
+ PushTasks(queue_order, 1);
+
+ selector_.SetQueuePriority(task_queues_[0].get(),
+ TaskQueue::kControlPriority);
+
+ EXPECT_FALSE(selector_.AllEnabledWorkQueuesAreEmpty());
+}
+
+TEST_F(TaskQueueSelectorTest, ChooseOldestWithPriority_Empty) {
+ WorkQueue* chosen_work_queue = nullptr;
+ bool chose_delayed_over_immediate = false;
+ EXPECT_FALSE(prioritizing_selector()->ChooseOldestWithPriority(
+ TaskQueue::kNormalPriority, &chose_delayed_over_immediate,
+ &chosen_work_queue));
+ EXPECT_FALSE(chose_delayed_over_immediate);
+}
+
+TEST_F(TaskQueueSelectorTest, ChooseOldestWithPriority_OnlyDelayed) {
+ task_queues_[0]->delayed_work_queue()->Push(
+ TaskQueueImpl::Task(TaskQueue::PostedTask(test_closure_, FROM_HERE),
+ base::TimeTicks(), 0, 0));
+
+ WorkQueue* chosen_work_queue = nullptr;
+ bool chose_delayed_over_immediate = false;
+ EXPECT_TRUE(prioritizing_selector()->ChooseOldestWithPriority(
+ TaskQueue::kNormalPriority, &chose_delayed_over_immediate,
+ &chosen_work_queue));
+ EXPECT_EQ(chosen_work_queue, task_queues_[0]->delayed_work_queue());
+ EXPECT_FALSE(chose_delayed_over_immediate);
+}
+
+TEST_F(TaskQueueSelectorTest, ChooseOldestWithPriority_OnlyImmediate) {
+ task_queues_[0]->immediate_work_queue()->Push(
+ TaskQueueImpl::Task(TaskQueue::PostedTask(test_closure_, FROM_HERE),
+ base::TimeTicks(), 0, 0));
+
+ WorkQueue* chosen_work_queue = nullptr;
+ bool chose_delayed_over_immediate = false;
+ EXPECT_TRUE(prioritizing_selector()->ChooseOldestWithPriority(
+ TaskQueue::kNormalPriority, &chose_delayed_over_immediate,
+ &chosen_work_queue));
+ EXPECT_EQ(chosen_work_queue, task_queues_[0]->immediate_work_queue());
+ EXPECT_FALSE(chose_delayed_over_immediate);
+}
+
+TEST_F(TaskQueueSelectorTest, TestObserverWithOneBlockedQueue) {
+ TaskQueueSelectorForTest selector;
+ MockObserver mock_observer;
+ selector.SetTaskQueueSelectorObserver(&mock_observer);
+
+ EXPECT_CALL(mock_observer, OnTaskQueueEnabled(_)).Times(1);
+
+ std::unique_ptr<TaskQueueImpl> task_queue(NewTaskQueueWithBlockReporting());
+ selector.AddQueue(task_queue.get());
+
+ task_queue->SetQueueEnabledForTest(false);
+ selector.DisableQueue(task_queue.get());
+
+ TaskQueueImpl::Task task(TaskQueue::PostedTask(test_closure_, FROM_HERE),
+ base::TimeTicks(), 0);
+ task.set_enqueue_order(0);
+ task_queue->immediate_work_queue()->Push(std::move(task));
+
+ WorkQueue* chosen_work_queue;
+ EXPECT_FALSE(selector.SelectWorkQueueToService(&chosen_work_queue));
+
+ task_queue->SetQueueEnabledForTest(true);
+ selector.EnableQueue(task_queue.get());
+ selector.RemoveQueue(task_queue.get());
+ task_queue->UnregisterTaskQueue();
+}
+
+TEST_F(TaskQueueSelectorTest, TestObserverWithTwoBlockedQueues) {
+ TaskQueueSelectorForTest selector;
+ MockObserver mock_observer;
+ selector.SetTaskQueueSelectorObserver(&mock_observer);
+
+ std::unique_ptr<TaskQueueImpl> task_queue(NewTaskQueueWithBlockReporting());
+ std::unique_ptr<TaskQueueImpl> task_queue2(NewTaskQueueWithBlockReporting());
+ selector.AddQueue(task_queue.get());
+ selector.AddQueue(task_queue2.get());
+
+ task_queue->SetQueueEnabledForTest(false);
+ task_queue2->SetQueueEnabledForTest(false);
+ selector.DisableQueue(task_queue.get());
+ selector.DisableQueue(task_queue2.get());
+
+ selector.SetQueuePriority(task_queue2.get(), TaskQueue::kControlPriority);
+
+ TaskQueueImpl::Task task1(TaskQueue::PostedTask(test_closure_, FROM_HERE),
+ base::TimeTicks(), 0);
+ TaskQueueImpl::Task task2(TaskQueue::PostedTask(test_closure_, FROM_HERE),
+ base::TimeTicks(), 1);
+ task1.set_enqueue_order(0);
+ task2.set_enqueue_order(1);
+ task_queue->immediate_work_queue()->Push(std::move(task1));
+ task_queue2->immediate_work_queue()->Push(std::move(task2));
+
+ WorkQueue* chosen_work_queue;
+ EXPECT_FALSE(selector.SelectWorkQueueToService(&chosen_work_queue));
+ testing::Mock::VerifyAndClearExpectations(&mock_observer);
+
+ EXPECT_CALL(mock_observer, OnTaskQueueEnabled(_)).Times(2);
+
+ task_queue->SetQueueEnabledForTest(true);
+ selector.EnableQueue(task_queue.get());
+
+ selector.RemoveQueue(task_queue.get());
+ task_queue->UnregisterTaskQueue();
+ EXPECT_FALSE(selector.SelectWorkQueueToService(&chosen_work_queue));
+
+ task_queue2->SetQueueEnabledForTest(true);
+ selector.EnableQueue(task_queue2.get());
+ selector.RemoveQueue(task_queue2.get());
+ task_queue2->UnregisterTaskQueue();
+}
+
+struct ChooseOldestWithPriorityTestParam {
+ int delayed_task_enqueue_order;
+ int immediate_task_enqueue_order;
+ int immediate_starvation_count;
+ const char* expected_work_queue_name;
+ bool expected_did_starve_immediate_queue;
+};
+
+static const ChooseOldestWithPriorityTestParam
+ kChooseOldestWithPriorityTestCases[] = {
+ {1, 2, 0, "delayed", true}, {1, 2, 1, "delayed", true},
+ {1, 2, 2, "delayed", true}, {1, 2, 3, "immediate", false},
+ {1, 2, 4, "immediate", false}, {2, 1, 4, "immediate", false},
+ {2, 1, 4, "immediate", false},
+};
+
+class ChooseOldestWithPriorityTest
+ : public TaskQueueSelectorTest,
+ public testing::WithParamInterface<ChooseOldestWithPriorityTestParam> {};
+
+TEST_P(ChooseOldestWithPriorityTest, RoundRobinTest) {
+ task_queues_[0]->immediate_work_queue()->Push(TaskQueueImpl::Task(
+ TaskQueue::PostedTask(test_closure_, FROM_HERE), base::TimeTicks(),
+ GetParam().immediate_task_enqueue_order,
+ GetParam().immediate_task_enqueue_order));
+
+ task_queues_[0]->delayed_work_queue()->Push(TaskQueueImpl::Task(
+ TaskQueue::PostedTask(test_closure_, FROM_HERE), base::TimeTicks(),
+ GetParam().delayed_task_enqueue_order,
+ GetParam().delayed_task_enqueue_order));
+
+ selector_.SetImmediateStarvationCountForTest(
+ GetParam().immediate_starvation_count);
+
+ WorkQueue* chosen_work_queue = nullptr;
+ bool chose_delayed_over_immediate = false;
+ EXPECT_TRUE(prioritizing_selector()->ChooseOldestWithPriority(
+ TaskQueue::kNormalPriority, &chose_delayed_over_immediate,
+ &chosen_work_queue));
+ EXPECT_EQ(chosen_work_queue->task_queue(), task_queues_[0].get());
+ EXPECT_STREQ(chosen_work_queue->name(), GetParam().expected_work_queue_name);
+ EXPECT_EQ(chose_delayed_over_immediate,
+ GetParam().expected_did_starve_immediate_queue);
+}
+
+INSTANTIATE_TEST_CASE_P(ChooseOldestWithPriorityTest,
+ ChooseOldestWithPriorityTest,
+ testing::ValuesIn(kChooseOldestWithPriorityTestCases));
+
+} // namespace task_queue_selector_unittest
+} // namespace internal
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/task_time_observer.h b/chromium/third_party/blink/renderer/platform/scheduler/base/task_time_observer.h
new file mode 100644
index 00000000000..cde2e569708
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/task_time_observer.h
@@ -0,0 +1,36 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TASK_TIME_OBSERVER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TASK_TIME_OBSERVER_H_
+
+#include "base/time/time.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+namespace scheduler {
+
+// TaskTimeObserver provides an API for observing completion of renderer tasks.
+class PLATFORM_EXPORT TaskTimeObserver {
+ public:
+ TaskTimeObserver() = default;
+ virtual ~TaskTimeObserver() = default;
+
+ // Callback to be called when task is about to start.
+ // |start_time| - time in seconds when task started to run,
+ virtual void WillProcessTask(double start_time) = 0;
+
+ // Callback to be called when task is completed.
+ // |start_time| - time in seconds when task started to run,
+ // |end_time| - time in seconds when task was completed.
+ virtual void DidProcessTask(double start_time, double end_time) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TaskTimeObserver);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TASK_TIME_OBSERVER_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/test_count_uses_time_source.cc b/chromium/third_party/blink/renderer/platform/scheduler/base/test_count_uses_time_source.cc
new file mode 100644
index 00000000000..9b6e17ad504
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/test_count_uses_time_source.cc
@@ -0,0 +1,22 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/base/test_count_uses_time_source.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+namespace scheduler {
+
+TestCountUsesTimeSource::TestCountUsesTimeSource() : now_calls_count_(0) {}
+
+TestCountUsesTimeSource::~TestCountUsesTimeSource() = default;
+
+base::TimeTicks TestCountUsesTimeSource::NowTicks() const {
+ now_calls_count_++;
+ // Don't return 0, as it triggers some assertions.
+ return base::TimeTicks() + base::TimeDelta::FromSeconds(1);
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/test_count_uses_time_source.h b/chromium/third_party/blink/renderer/platform/scheduler/base/test_count_uses_time_source.h
new file mode 100644
index 00000000000..c953ab16c55
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/test_count_uses_time_source.h
@@ -0,0 +1,31 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TEST_COUNT_USES_TIME_SOURCE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TEST_COUNT_USES_TIME_SOURCE_H_
+
+#include "base/macros.h"
+#include "base/time/tick_clock.h"
+
+namespace blink {
+namespace scheduler {
+
+class TestCountUsesTimeSource : public base::TickClock {
+ public:
+ explicit TestCountUsesTimeSource();
+ ~TestCountUsesTimeSource() override;
+
+ base::TimeTicks NowTicks() const override;
+ int now_calls_count() const { return now_calls_count_; }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestCountUsesTimeSource);
+
+ mutable int now_calls_count_;
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TEST_COUNT_USES_TIME_SOURCE_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/test_task_time_observer.h b/chromium/third_party/blink/renderer/platform/scheduler/base/test_task_time_observer.h
new file mode 100644
index 00000000000..62a4db3b58f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/test_task_time_observer.h
@@ -0,0 +1,23 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TEST_TASK_TIME_OBSERVER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TEST_TASK_TIME_OBSERVER_H_
+
+#include "base/time/time.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_time_observer.h"
+
+namespace blink {
+namespace scheduler {
+
+class TestTaskTimeObserver : public TaskTimeObserver {
+ public:
+ void WillProcessTask(double start_time) override {}
+ void DidProcessTask(double start_time, double end_time) override {}
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TEST_TASK_TIME_OBSERVER_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/thread_controller.h b/chromium/third_party/blink/renderer/platform/scheduler/base/thread_controller.h
new file mode 100644
index 00000000000..cfae477eb76
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/thread_controller.h
@@ -0,0 +1,94 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_THREAD_CONTROLLER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_THREAD_CONTROLLER_H_
+
+#include "base/location.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/time/time.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace base {
+class TickClock;
+struct PendingTask;
+};
+
+namespace blink {
+namespace scheduler {
+namespace internal {
+
+class SequencedTaskSource;
+
+// Interface for TaskQueueManager to schedule work to be run.
+class PLATFORM_EXPORT ThreadController {
+ public:
+ virtual ~ThreadController() = default;
+
+ // Set the number of tasks executed in a single invocation of DoWork.
+ // Increasing the batch size can reduce the overhead of yielding back to the
+ // main message loop. The batch size is 1 by default.
+ virtual void SetWorkBatchSize(int work_batch_size) = 0;
+
+ // Notifies that |pending_task| was enqueued. Needed for tracing purposes.
+ virtual void DidQueueTask(const base::PendingTask& pending_task) = 0;
+
+ // Notify the controller that its associated sequence has immediate work
+ // to run. Shortly after this is called, the thread associated with this
+ // controller will run a task returned by sequence->TakeTask(). Can be called
+ // from any sequence.
+ //
+ // TODO(altimin): Change this to "the thread associated with this
+ // controller will run tasks returned by sequence->TakeTask() until it
+ // returns null or sequence->DidRunTask() returns false" once the
+ // code is changed to work that way.
+ virtual void ScheduleWork() = 0;
+
+ // Notify the controller that its associated sequence will have
+ // delayed work to run at |run_time|. The thread associated with this
+ // controller will run a task returned by sequence->TakeTask() at that time.
+ // This call cancels any previously scheduled delayed work. Will be called
+ // from the main sequence.
+ //
+ // TODO(altimin): Change this to "the thread associated with this
+ // controller will run tasks returned by sequence->TakeTask() until
+ // it returns null or sequence->DidRunTask() returns false" once the
+ // code is changed to work that way.
+ virtual void ScheduleDelayedWork(base::TimeTicks now,
+ base::TimeTicks run_time) = 0;
+
+ // Notify thread controller that sequence no longer has delayed work at
+ // |run_time| and previously scheduled callbacks should be cancelled.
+ virtual void CancelDelayedWork(base::TimeTicks run_time) = 0;
+
+ // Sets the sequenced task source from which to take tasks after
+ // a Schedule*Work() call is made.
+ // Must be called before the first call to Schedule*Work().
+ virtual void SetSequencedTaskSource(SequencedTaskSource*) = 0;
+
+ // TODO(altimin): Get rid of the methods below.
+ // These methods exist due to current integration of TaskQueueManager
+ // with MessageLoop.
+
+ virtual bool RunsTasksInCurrentSequence() = 0;
+
+ virtual const base::TickClock* GetClock() = 0;
+
+ virtual void SetDefaultTaskRunner(
+ scoped_refptr<base::SingleThreadTaskRunner>) = 0;
+
+ virtual void RestoreDefaultTaskRunner() = 0;
+
+ virtual void AddNestingObserver(base::RunLoop::NestingObserver* observer) = 0;
+
+ virtual void RemoveNestingObserver(
+ base::RunLoop::NestingObserver* observer) = 0;
+};
+
+} // namespace internal
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_THREAD_CONTROLLER_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/thread_controller_impl.cc b/chromium/third_party/blink/renderer/platform/scheduler/base/thread_controller_impl.cc
new file mode 100644
index 00000000000..196cd5c5312
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/thread_controller_impl.cc
@@ -0,0 +1,255 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/base/thread_controller_impl.h"
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/time/tick_clock.h"
+#include "base/trace_event/trace_event.h"
+#include "third_party/blink/renderer/platform/scheduler/base/lazy_now.h"
+
+namespace blink {
+namespace scheduler {
+namespace internal {
+
+ThreadControllerImpl::ThreadControllerImpl(
+ base::MessageLoop* message_loop,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+ const base::TickClock* time_source)
+ : message_loop_(message_loop),
+ task_runner_(task_runner),
+ message_loop_task_runner_(message_loop ? message_loop->task_runner()
+ : nullptr),
+ time_source_(time_source),
+ weak_factory_(this) {
+ immediate_do_work_closure_ = base::BindRepeating(
+ &ThreadControllerImpl::DoWork, weak_factory_.GetWeakPtr(),
+ SequencedTaskSource::WorkType::kImmediate);
+ delayed_do_work_closure_ = base::BindRepeating(
+ &ThreadControllerImpl::DoWork, weak_factory_.GetWeakPtr(),
+ SequencedTaskSource::WorkType::kDelayed);
+}
+
+ThreadControllerImpl::~ThreadControllerImpl() = default;
+
+std::unique_ptr<ThreadControllerImpl> ThreadControllerImpl::Create(
+ base::MessageLoop* message_loop,
+ const base::TickClock* time_source) {
+ return base::WrapUnique(new ThreadControllerImpl(
+ message_loop, message_loop->task_runner(), time_source));
+}
+
+void ThreadControllerImpl::SetSequencedTaskSource(
+ SequencedTaskSource* sequence) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(sequence);
+ DCHECK(!sequence_);
+ sequence_ = sequence;
+}
+
+void ThreadControllerImpl::ScheduleWork() {
+ DCHECK(sequence_);
+ base::AutoLock lock(any_sequence_lock_);
+ // Don't post a DoWork if there's an immediate DoWork in flight or if we're
+ // inside a top level DoWork. We can rely on a continuation being posted as
+ // needed.
+ if (any_sequence().immediate_do_work_posted ||
+ (any_sequence().do_work_running_count > any_sequence().nesting_depth)) {
+ return;
+ }
+ any_sequence().immediate_do_work_posted = true;
+
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "ThreadControllerImpl::ScheduleWork::PostTask");
+ task_runner_->PostTask(FROM_HERE, immediate_do_work_closure_);
+}
+
+void ThreadControllerImpl::ScheduleDelayedWork(base::TimeTicks now,
+ base::TimeTicks run_time) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(sequence_);
+
+ // If there's a delayed DoWork scheduled to run sooner, we don't need to do
+ // anything because a delayed continuation will be posted as needed.
+ if (main_sequence_only().next_delayed_do_work <= run_time)
+ return;
+
+ // If DoWork is running then we don't need to do anything because it will post
+ // a continuation as needed. Bailing out here is by far the most common case.
+ if (main_sequence_only().do_work_running_count >
+ main_sequence_only().nesting_depth) {
+ return;
+ }
+
+ // If DoWork is about to run then we also don't need to do anything.
+ {
+ base::AutoLock lock(any_sequence_lock_);
+ if (any_sequence().immediate_do_work_posted)
+ return;
+ }
+
+ base::TimeDelta delay = std::max(base::TimeDelta(), run_time - now);
+ TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "ThreadControllerImpl::ScheduleDelayedWork::PostDelayedTask",
+ "delay_ms", delay.InMillisecondsF());
+
+ main_sequence_only().next_delayed_do_work = run_time;
+ cancelable_delayed_do_work_closure_.Reset(delayed_do_work_closure_);
+ task_runner_->PostDelayedTask(
+ FROM_HERE, cancelable_delayed_do_work_closure_.callback(), delay);
+}
+
+void ThreadControllerImpl::CancelDelayedWork(base::TimeTicks run_time) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(sequence_);
+ if (main_sequence_only().next_delayed_do_work != run_time)
+ return;
+
+ cancelable_delayed_do_work_closure_.Cancel();
+ main_sequence_only().next_delayed_do_work = base::TimeTicks::Max();
+}
+
+bool ThreadControllerImpl::RunsTasksInCurrentSequence() {
+ return task_runner_->RunsTasksInCurrentSequence();
+}
+
+const base::TickClock* ThreadControllerImpl::GetClock() {
+ return time_source_;
+}
+
+void ThreadControllerImpl::SetDefaultTaskRunner(
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+ if (!message_loop_)
+ return;
+ message_loop_->SetTaskRunner(task_runner);
+}
+
+void ThreadControllerImpl::RestoreDefaultTaskRunner() {
+ if (!message_loop_)
+ return;
+ message_loop_->SetTaskRunner(message_loop_task_runner_);
+}
+
+void ThreadControllerImpl::DidQueueTask(const base::PendingTask& pending_task) {
+ task_annotator_.DidQueueTask("TaskQueueManager::PostTask", pending_task);
+}
+
+void ThreadControllerImpl::DoWork(SequencedTaskSource::WorkType work_type) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(sequence_);
+
+ {
+ base::AutoLock lock(any_sequence_lock_);
+ if (work_type == SequencedTaskSource::WorkType::kImmediate)
+ any_sequence().immediate_do_work_posted = false;
+ any_sequence().do_work_running_count++;
+ }
+
+ main_sequence_only().do_work_running_count++;
+
+ base::WeakPtr<ThreadControllerImpl> weak_ptr = weak_factory_.GetWeakPtr();
+ // TODO(scheduler-dev): Consider moving to a time based work batch instead.
+ for (int i = 0; i < main_sequence_only().work_batch_size_; i++) {
+ base::Optional<base::PendingTask> task = sequence_->TakeTask();
+ if (!task)
+ break;
+
+ TRACE_TASK_EXECUTION("ThreadControllerImpl::DoWork", *task);
+ task_annotator_.RunTask("ThreadControllerImpl::DoWork", &*task);
+
+ if (!weak_ptr)
+ return;
+
+ sequence_->DidRunTask();
+
+ // TODO(alexclarke): Find out why this is needed.
+ if (main_sequence_only().nesting_depth > 0)
+ break;
+ }
+
+ main_sequence_only().do_work_running_count--;
+
+ {
+ base::AutoLock lock(any_sequence_lock_);
+ any_sequence().do_work_running_count--;
+ DCHECK_GE(any_sequence().do_work_running_count, 0);
+ LazyNow lazy_now(time_source_);
+ base::TimeDelta delay_till_next_task =
+ sequence_->DelayTillNextTask(&lazy_now);
+ if (delay_till_next_task <= base::TimeDelta()) {
+ // The next task needs to run immediately, post a continuation if needed.
+ if (!any_sequence().immediate_do_work_posted) {
+ any_sequence().immediate_do_work_posted = true;
+ task_runner_->PostTask(FROM_HERE, immediate_do_work_closure_);
+ }
+ } else if (delay_till_next_task < base::TimeDelta::Max()) {
+ // The next task needs to run after a delay, post a continuation if
+ // needed.
+ base::TimeTicks next_task_at = lazy_now.Now() + delay_till_next_task;
+ if (next_task_at != main_sequence_only().next_delayed_do_work) {
+ main_sequence_only().next_delayed_do_work = next_task_at;
+ cancelable_delayed_do_work_closure_.Reset(delayed_do_work_closure_);
+ task_runner_->PostDelayedTask(
+ FROM_HERE, cancelable_delayed_do_work_closure_.callback(),
+ delay_till_next_task);
+ }
+ } else {
+ // There is no next task scheduled.
+ main_sequence_only().next_delayed_do_work = base::TimeTicks::Max();
+ }
+ }
+}
+
+void ThreadControllerImpl::AddNestingObserver(
+ base::RunLoop::NestingObserver* observer) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ nesting_observer_ = observer;
+ base::RunLoop::AddNestingObserverOnCurrentThread(this);
+}
+
+void ThreadControllerImpl::RemoveNestingObserver(
+ base::RunLoop::NestingObserver* observer) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK_EQ(observer, nesting_observer_);
+ nesting_observer_ = nullptr;
+ base::RunLoop::RemoveNestingObserverOnCurrentThread(this);
+}
+
+void ThreadControllerImpl::OnBeginNestedRunLoop() {
+ main_sequence_only().nesting_depth++;
+ {
+ base::AutoLock lock(any_sequence_lock_);
+ any_sequence().nesting_depth++;
+ if (!any_sequence().immediate_do_work_posted) {
+ any_sequence().immediate_do_work_posted = true;
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "ThreadControllerImpl::OnBeginNestedRunLoop::PostTask");
+ task_runner_->PostTask(FROM_HERE, immediate_do_work_closure_);
+ }
+ }
+ if (nesting_observer_)
+ nesting_observer_->OnBeginNestedRunLoop();
+}
+
+void ThreadControllerImpl::OnExitNestedRunLoop() {
+ main_sequence_only().nesting_depth--;
+ {
+ base::AutoLock lock(any_sequence_lock_);
+ any_sequence().nesting_depth--;
+ DCHECK_GE(any_sequence().nesting_depth, 0);
+ }
+ if (nesting_observer_)
+ nesting_observer_->OnExitNestedRunLoop();
+}
+
+void ThreadControllerImpl::SetWorkBatchSize(int work_batch_size) {
+ main_sequence_only().work_batch_size_ = work_batch_size;
+}
+
+} // namespace internal
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/thread_controller_impl.h b/chromium/third_party/blink/renderer/platform/scheduler/base/thread_controller_impl.h
new file mode 100644
index 00000000000..bff48060c17
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/thread_controller_impl.h
@@ -0,0 +1,132 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_THREAD_CONTROLLER_IMPL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_THREAD_CONTROLLER_IMPL_H_
+
+#include "third_party/blink/renderer/platform/scheduler/base/thread_controller.h"
+
+#include "base/cancelable_callback.h"
+#include "base/debug/task_annotator.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/run_loop.h"
+#include "base/sequence_checker.h"
+#include "base/single_thread_task_runner.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/scheduler/base/sequenced_task_source.h"
+
+namespace base {
+class MessageLoop;
+class TickClock;
+} // namespace base
+
+namespace blink {
+namespace scheduler {
+namespace internal {
+
+class PLATFORM_EXPORT ThreadControllerImpl
+ : public ThreadController,
+ public base::RunLoop::NestingObserver {
+ public:
+ ~ThreadControllerImpl() override;
+
+ static std::unique_ptr<ThreadControllerImpl> Create(
+ base::MessageLoop* message_loop,
+ const base::TickClock* time_source);
+
+ // ThreadController:
+ void SetWorkBatchSize(int work_batch_size) override;
+ void DidQueueTask(const base::PendingTask& pending_task) override;
+ void ScheduleWork() override;
+ void ScheduleDelayedWork(base::TimeTicks now,
+ base::TimeTicks run_timy) override;
+ void CancelDelayedWork(base::TimeTicks run_time) override;
+ void SetSequencedTaskSource(SequencedTaskSource* sequence) override;
+ bool RunsTasksInCurrentSequence() override;
+ const base::TickClock* GetClock() override;
+ void SetDefaultTaskRunner(
+ scoped_refptr<base::SingleThreadTaskRunner>) override;
+ void RestoreDefaultTaskRunner() override;
+ void AddNestingObserver(base::RunLoop::NestingObserver* observer) override;
+ void RemoveNestingObserver(base::RunLoop::NestingObserver* observer) override;
+
+ // base::RunLoop::NestingObserver:
+ void OnBeginNestedRunLoop() override;
+ void OnExitNestedRunLoop() override;
+
+ protected:
+ ThreadControllerImpl(base::MessageLoop* message_loop,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+ const base::TickClock* time_source);
+
+ // TODO(altimin): Make these const. Blocked on removing
+ // lazy initialisation support.
+ base::MessageLoop* message_loop_;
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+ base::RunLoop::NestingObserver* nesting_observer_ = nullptr;
+
+ private:
+ void DoWork(SequencedTaskSource::WorkType work_type);
+
+ struct AnySequence {
+ AnySequence() = default;
+
+ int do_work_running_count = 0;
+ int nesting_depth = 0;
+ bool immediate_do_work_posted = false;
+ };
+
+ mutable base::Lock any_sequence_lock_;
+ AnySequence any_sequence_;
+
+ struct AnySequence& any_sequence() {
+ any_sequence_lock_.AssertAcquired();
+ return any_sequence_;
+ }
+ const struct AnySequence& any_sequence() const {
+ any_sequence_lock_.AssertAcquired();
+ return any_sequence_;
+ }
+
+ struct MainSequenceOnly {
+ MainSequenceOnly() = default;
+
+ int do_work_running_count = 0;
+ int nesting_depth = 0;
+ int work_batch_size_ = 1;
+
+ base::TimeTicks next_delayed_do_work = base::TimeTicks::Max();
+ };
+
+ SEQUENCE_CHECKER(sequence_checker_);
+ MainSequenceOnly main_sequence_only_;
+ MainSequenceOnly& main_sequence_only() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ return main_sequence_only_;
+ }
+ const MainSequenceOnly& main_sequence_only() const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ return main_sequence_only_;
+ }
+
+ scoped_refptr<base::SingleThreadTaskRunner> message_loop_task_runner_;
+ const base::TickClock* time_source_;
+ base::RepeatingClosure immediate_do_work_closure_;
+ base::RepeatingClosure delayed_do_work_closure_;
+ base::CancelableClosure cancelable_delayed_do_work_closure_;
+ SequencedTaskSource* sequence_ = nullptr; // NOT OWNED
+ base::debug::TaskAnnotator task_annotator_;
+
+ base::WeakPtrFactory<ThreadControllerImpl> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadControllerImpl);
+};
+
+} // namespace internal
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_THREAD_CONTROLLER_IMPL_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/time_domain.cc b/chromium/third_party/blink/renderer/platform/scheduler/base/time_domain.cc
new file mode 100644
index 00000000000..b835c150451
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/time_domain.cc
@@ -0,0 +1,119 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/base/time_domain.h"
+
+#include <set>
+
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/base/work_queue.h"
+
+namespace blink {
+namespace scheduler {
+
+TimeDomain::TimeDomain() = default;
+
+TimeDomain::~TimeDomain() {
+ DCHECK(main_thread_checker_.CalledOnValidThread());
+}
+
+void TimeDomain::RegisterQueue(internal::TaskQueueImpl* queue) {
+ DCHECK(main_thread_checker_.CalledOnValidThread());
+ DCHECK_EQ(queue->GetTimeDomain(), this);
+}
+
+void TimeDomain::UnregisterQueue(internal::TaskQueueImpl* queue) {
+ DCHECK(main_thread_checker_.CalledOnValidThread());
+ DCHECK_EQ(queue->GetTimeDomain(), this);
+
+ LazyNow lazy_now = CreateLazyNow();
+ ScheduleWakeUpForQueue(queue, base::nullopt, &lazy_now);
+}
+
+void TimeDomain::ScheduleWakeUpForQueue(
+ internal::TaskQueueImpl* queue,
+ base::Optional<internal::TaskQueueImpl::DelayedWakeUp> wake_up,
+ LazyNow* lazy_now) {
+ DCHECK(main_thread_checker_.CalledOnValidThread());
+ DCHECK_EQ(queue->GetTimeDomain(), this);
+ DCHECK(queue->IsQueueEnabled() || !wake_up);
+
+ base::Optional<base::TimeTicks> previous_wake_up;
+ if (!delayed_wake_up_queue_.empty())
+ previous_wake_up = delayed_wake_up_queue_.Min().wake_up.time;
+
+ if (wake_up) {
+ // Insert a new wake-up into the heap.
+ if (queue->heap_handle().IsValid()) {
+ // O(log n)
+ delayed_wake_up_queue_.ChangeKey(queue->heap_handle(),
+ {wake_up.value(), queue});
+ } else {
+ // O(log n)
+ delayed_wake_up_queue_.insert({wake_up.value(), queue});
+ }
+ } else {
+ // Remove a wake-up from heap if present.
+ if (queue->heap_handle().IsValid())
+ delayed_wake_up_queue_.erase(queue->heap_handle());
+ }
+
+ base::Optional<base::TimeTicks> new_wake_up;
+ if (!delayed_wake_up_queue_.empty())
+ new_wake_up = delayed_wake_up_queue_.Min().wake_up.time;
+
+ if (previous_wake_up == new_wake_up)
+ return;
+
+ if (previous_wake_up)
+ CancelWakeUpAt(previous_wake_up.value());
+ if (new_wake_up)
+ RequestWakeUpAt(lazy_now->Now(), new_wake_up.value());
+}
+
+void TimeDomain::WakeUpReadyDelayedQueues(LazyNow* lazy_now) {
+ DCHECK(main_thread_checker_.CalledOnValidThread());
+ // Wake up any queues with pending delayed work. Note std::multimap stores
+ // the elements sorted by key, so the begin() iterator points to the earliest
+ // queue to wake-up.
+ while (!delayed_wake_up_queue_.empty() &&
+ delayed_wake_up_queue_.Min().wake_up.time <= lazy_now->Now()) {
+ internal::TaskQueueImpl* queue = delayed_wake_up_queue_.Min().queue;
+ queue->WakeUpForDelayedWork(lazy_now);
+ }
+}
+
+bool TimeDomain::NextScheduledRunTime(base::TimeTicks* out_time) const {
+ DCHECK(main_thread_checker_.CalledOnValidThread());
+ if (delayed_wake_up_queue_.empty())
+ return false;
+
+ *out_time = delayed_wake_up_queue_.Min().wake_up.time;
+ return true;
+}
+
+bool TimeDomain::NextScheduledTaskQueue(
+ internal::TaskQueueImpl** out_task_queue) const {
+ DCHECK(main_thread_checker_.CalledOnValidThread());
+ if (delayed_wake_up_queue_.empty())
+ return false;
+
+ *out_task_queue = delayed_wake_up_queue_.Min().queue;
+ return true;
+}
+
+void TimeDomain::AsValueInto(base::trace_event::TracedValue* state) const {
+ state->BeginDictionary();
+ state->SetString("name", GetName());
+ state->SetInteger("registered_delay_count", delayed_wake_up_queue_.size());
+ if (!delayed_wake_up_queue_.empty()) {
+ base::TimeDelta delay = delayed_wake_up_queue_.Min().wake_up.time - Now();
+ state->SetDouble("next_delay_ms", delay.InMillisecondsF());
+ }
+ AsValueIntoInternal(state);
+ state->EndDictionary();
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/time_domain.h b/chromium/third_party/blink/renderer/platform/scheduler/base/time_domain.h
new file mode 100644
index 00000000000..5ee0d850ea6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/time_domain.h
@@ -0,0 +1,142 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TIME_DOMAIN_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TIME_DOMAIN_H_
+
+#include <map>
+
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "third_party/blink/renderer/platform/scheduler/base/intrusive_heap.h"
+#include "third_party/blink/renderer/platform/scheduler/base/lazy_now.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl.h"
+
+namespace blink {
+namespace scheduler {
+namespace internal {
+class TaskQueueImpl;
+} // internal
+class TaskQueueManager;
+
+// The TimeDomain's job is to wake task queues up when their next delayed tasks
+// are due to fire. TaskQueues request a wake up via ScheduleDelayedWork, when
+// the wake up is due the TimeDomain calls TaskQueue::WakeUpForDelayedWork.
+// The TimeDomain communicates with the TaskQueueManager to actually schedule
+// the wake-ups on the underlying base::MessageLoop. Various levels of de-duping
+// are employed to prevent unnecessary posting of TaskQueueManager::DoWork.
+//
+// Note the TimeDomain only knows about the first wake-up per queue, it's the
+// responsibility of TaskQueueImpl to keep the time domain up to date if this
+// changes.
+class PLATFORM_EXPORT TimeDomain {
+ public:
+ TimeDomain();
+ virtual ~TimeDomain();
+
+ // Returns a LazyNow that evaluate this TimeDomain's Now. Can be called from
+ // any thread.
+ // TODO(alexclarke): Make this main thread only.
+ virtual LazyNow CreateLazyNow() const = 0;
+
+ // Evaluate this TimeDomain's Now. Can be called from any thread.
+ virtual base::TimeTicks Now() const = 0;
+
+ // Computes the delay until the next task the TimeDomain is aware of, if any.
+ // Note virtual time domains may return base::TimeDelta() if they have any
+ // delayed tasks they deem eligible to run. Virtual time domains are allowed
+ // to advance their internal clock when this method is called.
+ virtual base::Optional<base::TimeDelta> DelayTillNextTask(
+ LazyNow* lazy_now) = 0;
+
+ // Returns the name of this time domain for tracing.
+ virtual const char* GetName() const = 0;
+
+ // If there is a scheduled delayed task, |out_time| is set to the scheduled
+ // runtime for the next one and it returns true. Returns false otherwise.
+ bool NextScheduledRunTime(base::TimeTicks* out_time) const;
+
+ protected:
+ friend class internal::TaskQueueImpl;
+ friend class TaskQueueManagerImpl;
+
+ void AsValueInto(base::trace_event::TracedValue* state) const;
+
+ // If there is a scheduled delayed task, |out_task_queue| is set to the queue
+ // the next task was posted to and it returns true. Returns false otherwise.
+ bool NextScheduledTaskQueue(internal::TaskQueueImpl** out_task_queue) const;
+
+ void ScheduleWakeUpForQueue(
+ internal::TaskQueueImpl* queue,
+ base::Optional<internal::TaskQueueImpl::DelayedWakeUp> wake_up,
+ LazyNow* lazy_now);
+
+ // Registers the |queue|.
+ void RegisterQueue(internal::TaskQueueImpl* queue);
+
+ // Removes |queue| from all internal data structures.
+ void UnregisterQueue(internal::TaskQueueImpl* queue);
+
+ // Called by the TaskQueueManager when the TimeDomain is registered.
+ virtual void OnRegisterWithTaskQueueManager(
+ TaskQueueManagerImpl* task_queue_manager) = 0;
+
+ // The implementation will schedule task processing to run at time |run_time|
+ // within the TimeDomain's time line. Only called from the main thread.
+ // NOTE this is only called by ScheduleDelayedWork if the scheduled runtime
+ // is sooner than any previously sheduled work or if there is no other
+ // scheduled work.
+ virtual void RequestWakeUpAt(base::TimeTicks now,
+ base::TimeTicks run_time) = 0;
+
+ // The implementation will cancel a wake up previously requested by
+ // RequestWakeUpAt. It's expected this will be a NOP for most virtual time
+ // domains.
+ virtual void CancelWakeUpAt(base::TimeTicks run_time) = 0;
+
+ // For implementation specific tracing.
+ virtual void AsValueIntoInternal(
+ base::trace_event::TracedValue* state) const = 0;
+
+ // Call TaskQueueImpl::UpdateDelayedWorkQueue for each queue where the delay
+ // has elapsed.
+ void WakeUpReadyDelayedQueues(LazyNow* lazy_now);
+
+ size_t NumberOfScheduledWakeUps() const {
+ return delayed_wake_up_queue_.size();
+ }
+
+ private:
+ struct ScheduledDelayedWakeUp {
+ internal::TaskQueueImpl::DelayedWakeUp wake_up;
+ internal::TaskQueueImpl* queue;
+
+ bool operator<=(const ScheduledDelayedWakeUp& other) const {
+ return wake_up <= other.wake_up;
+ }
+
+ void SetHeapHandle(HeapHandle handle) {
+ DCHECK(handle.IsValid());
+ queue->set_heap_handle(handle);
+ }
+
+ void ClearHeapHandle() {
+ DCHECK(queue->heap_handle().IsValid());
+ queue->set_heap_handle(HeapHandle());
+ }
+ };
+
+ IntrusiveHeap<ScheduledDelayedWakeUp> delayed_wake_up_queue_;
+
+ base::ThreadChecker main_thread_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(TimeDomain);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_TIME_DOMAIN_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/time_domain_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/base/time_domain_unittest.cc
new file mode 100644
index 00000000000..a16adba9f08
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/time_domain_unittest.cc
@@ -0,0 +1,348 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/base/time_domain.h"
+
+#include <memory>
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "components/viz/test/ordered_simple_task_runner.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_manager.h"
+#include "third_party/blink/renderer/platform/scheduler/base/work_queue.h"
+
+using testing::_;
+using testing::AnyNumber;
+using testing::Mock;
+
+namespace blink {
+namespace scheduler {
+
+class TaskQueueImplForTest : public internal::TaskQueueImpl {
+ public:
+ TaskQueueImplForTest(TaskQueueManagerImpl* task_queue_manager,
+ TimeDomain* time_domain,
+ const TaskQueue::Spec& spec)
+ : TaskQueueImpl(task_queue_manager, time_domain, spec) {}
+ ~TaskQueueImplForTest() {}
+
+ using TaskQueueImpl::SetDelayedWakeUpForTesting;
+};
+
+class MockTimeDomain : public TimeDomain {
+ public:
+ MockTimeDomain()
+ : now_(base::TimeTicks() + base::TimeDelta::FromSeconds(1)) {}
+
+ ~MockTimeDomain() override = default;
+
+ using TimeDomain::NextScheduledRunTime;
+ using TimeDomain::NextScheduledTaskQueue;
+ using TimeDomain::UnregisterQueue;
+ using TimeDomain::WakeUpReadyDelayedQueues;
+
+ // TimeSource implementation:
+ LazyNow CreateLazyNow() const override { return LazyNow(now_); }
+ base::TimeTicks Now() const override { return now_; }
+
+ void AsValueIntoInternal(
+ base::trace_event::TracedValue* state) const override {}
+
+ base::Optional<base::TimeDelta> DelayTillNextTask(
+ LazyNow* lazy_now) override {
+ return base::Optional<base::TimeDelta>();
+ }
+ const char* GetName() const override { return "Test"; }
+ void OnRegisterWithTaskQueueManager(
+ TaskQueueManagerImpl* task_queue_manager) override {}
+
+ MOCK_METHOD2(RequestWakeUpAt,
+ void(base::TimeTicks now, base::TimeTicks run_time));
+
+ MOCK_METHOD1(CancelWakeUpAt, void(base::TimeTicks run_time));
+
+ void SetNow(base::TimeTicks now) { now_ = now; }
+
+ private:
+ base::TimeTicks now_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockTimeDomain);
+};
+
+class TimeDomainTest : public testing::Test {
+ public:
+ void SetUp() final {
+ time_domain_ = base::WrapUnique(CreateMockTimeDomain());
+ task_queue_ = std::make_unique<TaskQueueImplForTest>(
+ nullptr, time_domain_.get(), TaskQueue::Spec("test"));
+ }
+
+ void TearDown() final {
+ if (task_queue_)
+ task_queue_->UnregisterTaskQueue();
+ }
+
+ virtual MockTimeDomain* CreateMockTimeDomain() {
+ return new MockTimeDomain();
+ }
+
+ std::unique_ptr<MockTimeDomain> time_domain_;
+ std::unique_ptr<TaskQueueImplForTest> task_queue_;
+};
+
+TEST_F(TimeDomainTest, ScheduleWakeUpForQueue) {
+ base::TimeDelta delay = base::TimeDelta::FromMilliseconds(10);
+ base::TimeTicks delayed_runtime = time_domain_->Now() + delay;
+ EXPECT_CALL(*time_domain_.get(), RequestWakeUpAt(_, delayed_runtime));
+ base::TimeTicks now = time_domain_->Now();
+ LazyNow lazy_now(now);
+ task_queue_->SetDelayedWakeUpForTesting(
+ internal::TaskQueueImpl::DelayedWakeUp{now + delay, 0});
+
+ base::TimeTicks next_scheduled_runtime;
+ EXPECT_TRUE(time_domain_->NextScheduledRunTime(&next_scheduled_runtime));
+ EXPECT_EQ(delayed_runtime, next_scheduled_runtime);
+
+ internal::TaskQueueImpl* next_task_queue;
+ EXPECT_TRUE(time_domain_->NextScheduledTaskQueue(&next_task_queue));
+ EXPECT_EQ(task_queue_.get(), next_task_queue);
+ Mock::VerifyAndClearExpectations(time_domain_.get());
+
+ EXPECT_CALL(*time_domain_.get(), CancelWakeUpAt(_)).Times(AnyNumber());
+}
+
+TEST_F(TimeDomainTest, ScheduleWakeUpForQueueSupersedesPreviousWakeUp) {
+ base::TimeDelta delay1 = base::TimeDelta::FromMilliseconds(10);
+ base::TimeDelta delay2 = base::TimeDelta::FromMilliseconds(100);
+ base::TimeTicks delayed_runtime1 = time_domain_->Now() + delay1;
+ base::TimeTicks delayed_runtime2 = time_domain_->Now() + delay2;
+ EXPECT_CALL(*time_domain_.get(), RequestWakeUpAt(_, delayed_runtime1));
+ base::TimeTicks now = time_domain_->Now();
+ LazyNow lazy_now(now);
+ task_queue_->SetDelayedWakeUpForTesting(
+ internal::TaskQueueImpl::DelayedWakeUp{delayed_runtime1, 0});
+
+ base::TimeTicks next_scheduled_runtime;
+ EXPECT_TRUE(time_domain_->NextScheduledRunTime(&next_scheduled_runtime));
+ EXPECT_EQ(delayed_runtime1, next_scheduled_runtime);
+
+ Mock::VerifyAndClearExpectations(time_domain_.get());
+
+ // Now schedule a later wake_up, which should replace the previously
+ // requested one.
+ EXPECT_CALL(*time_domain_.get(), RequestWakeUpAt(_, delayed_runtime2));
+ task_queue_->SetDelayedWakeUpForTesting(
+ internal::TaskQueueImpl::DelayedWakeUp{delayed_runtime2, 0});
+
+ EXPECT_TRUE(time_domain_->NextScheduledRunTime(&next_scheduled_runtime));
+ EXPECT_EQ(delayed_runtime2, next_scheduled_runtime);
+ Mock::VerifyAndClearExpectations(time_domain_.get());
+
+ EXPECT_CALL(*time_domain_.get(), CancelWakeUpAt(_)).Times(AnyNumber());
+}
+
+TEST_F(TimeDomainTest, RequestWakeUpAt_OnlyCalledForEarlierTasks) {
+ std::unique_ptr<TaskQueueImplForTest> task_queue2 =
+ std::make_unique<TaskQueueImplForTest>(nullptr, time_domain_.get(),
+ TaskQueue::Spec("test"));
+
+ std::unique_ptr<TaskQueueImplForTest> task_queue3 =
+ std::make_unique<TaskQueueImplForTest>(nullptr, time_domain_.get(),
+ TaskQueue::Spec("test"));
+
+ std::unique_ptr<TaskQueueImplForTest> task_queue4 =
+ std::make_unique<TaskQueueImplForTest>(nullptr, time_domain_.get(),
+ TaskQueue::Spec("test"));
+
+ base::TimeDelta delay1 = base::TimeDelta::FromMilliseconds(10);
+ base::TimeDelta delay2 = base::TimeDelta::FromMilliseconds(20);
+ base::TimeDelta delay3 = base::TimeDelta::FromMilliseconds(30);
+ base::TimeDelta delay4 = base::TimeDelta::FromMilliseconds(1);
+
+ // RequestWakeUpAt should always be called if there are no other wake-ups.
+ base::TimeTicks now = time_domain_->Now();
+ LazyNow lazy_now(now);
+ EXPECT_CALL(*time_domain_.get(), RequestWakeUpAt(_, now + delay1));
+ task_queue_->SetDelayedWakeUpForTesting(
+ internal::TaskQueueImpl::DelayedWakeUp{now + delay1, 0});
+
+ Mock::VerifyAndClearExpectations(time_domain_.get());
+
+ // RequestWakeUpAt should not be called when scheduling later tasks.
+ EXPECT_CALL(*time_domain_.get(), RequestWakeUpAt(_, _)).Times(0);
+ task_queue2->SetDelayedWakeUpForTesting(
+ internal::TaskQueueImpl::DelayedWakeUp{now + delay2, 0});
+ task_queue3->SetDelayedWakeUpForTesting(
+ internal::TaskQueueImpl::DelayedWakeUp{now + delay3, 0});
+
+ // RequestWakeUpAt should be called when scheduling earlier tasks.
+ Mock::VerifyAndClearExpectations(time_domain_.get());
+ EXPECT_CALL(*time_domain_.get(), RequestWakeUpAt(_, now + delay4));
+ task_queue4->SetDelayedWakeUpForTesting(
+ internal::TaskQueueImpl::DelayedWakeUp{now + delay4, 0});
+
+ Mock::VerifyAndClearExpectations(time_domain_.get());
+
+ EXPECT_CALL(*time_domain_.get(), RequestWakeUpAt(_, _));
+ EXPECT_CALL(*time_domain_.get(), CancelWakeUpAt(_)).Times(2);
+ task_queue2->UnregisterTaskQueue();
+ task_queue3->UnregisterTaskQueue();
+ task_queue4->UnregisterTaskQueue();
+}
+
+TEST_F(TimeDomainTest, UnregisterQueue) {
+ std::unique_ptr<TaskQueueImplForTest> task_queue2_ =
+ std::make_unique<TaskQueueImplForTest>(nullptr, time_domain_.get(),
+ TaskQueue::Spec("test"));
+
+ base::TimeTicks now = time_domain_->Now();
+ LazyNow lazy_now(now);
+ base::TimeTicks wake_up1 = now + base::TimeDelta::FromMilliseconds(10);
+ EXPECT_CALL(*time_domain_.get(), RequestWakeUpAt(_, wake_up1)).Times(1);
+ task_queue_->SetDelayedWakeUpForTesting(
+ internal::TaskQueueImpl::DelayedWakeUp{wake_up1, 0});
+ base::TimeTicks wake_up2 = now + base::TimeDelta::FromMilliseconds(100);
+ task_queue2_->SetDelayedWakeUpForTesting(
+ internal::TaskQueueImpl::DelayedWakeUp{wake_up2, 0});
+
+ internal::TaskQueueImpl* next_task_queue;
+ EXPECT_TRUE(time_domain_->NextScheduledTaskQueue(&next_task_queue));
+ EXPECT_EQ(task_queue_.get(), next_task_queue);
+
+ testing::Mock::VerifyAndClearExpectations(time_domain_.get());
+
+ EXPECT_CALL(*time_domain_.get(), CancelWakeUpAt(wake_up1)).Times(1);
+ EXPECT_CALL(*time_domain_.get(), RequestWakeUpAt(_, wake_up2)).Times(1);
+
+ time_domain_->UnregisterQueue(task_queue_.get());
+ task_queue_ = std::unique_ptr<TaskQueueImplForTest>();
+ EXPECT_TRUE(time_domain_->NextScheduledTaskQueue(&next_task_queue));
+ EXPECT_EQ(task_queue2_.get(), next_task_queue);
+
+ testing::Mock::VerifyAndClearExpectations(time_domain_.get());
+
+ EXPECT_CALL(*time_domain_.get(), CancelWakeUpAt(wake_up2)).Times(1);
+
+ time_domain_->UnregisterQueue(task_queue2_.get());
+ EXPECT_FALSE(time_domain_->NextScheduledTaskQueue(&next_task_queue));
+}
+
+TEST_F(TimeDomainTest, WakeUpReadyDelayedQueues) {
+ base::TimeDelta delay = base::TimeDelta::FromMilliseconds(50);
+ base::TimeTicks now = time_domain_->Now();
+ LazyNow lazy_now(now);
+ base::TimeTicks delayed_runtime = now + delay;
+ EXPECT_CALL(*time_domain_.get(), RequestWakeUpAt(_, delayed_runtime));
+ task_queue_->SetDelayedWakeUpForTesting(
+ internal::TaskQueueImpl::DelayedWakeUp{delayed_runtime, 0});
+
+ base::TimeTicks next_run_time;
+ ASSERT_TRUE(time_domain_->NextScheduledRunTime(&next_run_time));
+ EXPECT_EQ(delayed_runtime, next_run_time);
+
+ time_domain_->WakeUpReadyDelayedQueues(&lazy_now);
+ ASSERT_TRUE(time_domain_->NextScheduledRunTime(&next_run_time));
+ EXPECT_EQ(delayed_runtime, next_run_time);
+
+ time_domain_->SetNow(delayed_runtime);
+ lazy_now = time_domain_->CreateLazyNow();
+ time_domain_->WakeUpReadyDelayedQueues(&lazy_now);
+ ASSERT_FALSE(time_domain_->NextScheduledRunTime(&next_run_time));
+}
+
+TEST_F(TimeDomainTest, WakeUpReadyDelayedQueuesWithIdenticalRuntimes) {
+ int sequence_num = 0;
+ base::TimeDelta delay = base::TimeDelta::FromMilliseconds(50);
+ base::TimeTicks now = time_domain_->Now();
+ LazyNow lazy_now(now);
+ base::TimeTicks delayed_runtime = now + delay;
+ EXPECT_CALL(*time_domain_.get(), RequestWakeUpAt(_, delayed_runtime));
+ EXPECT_CALL(*time_domain_.get(), CancelWakeUpAt(delayed_runtime));
+
+ std::unique_ptr<TaskQueueImplForTest> task_queue2 =
+ std::make_unique<TaskQueueImplForTest>(nullptr, time_domain_.get(),
+ TaskQueue::Spec("test"));
+
+ task_queue2->SetDelayedWakeUpForTesting(
+ internal::TaskQueueImpl::DelayedWakeUp{delayed_runtime, ++sequence_num});
+ task_queue_->SetDelayedWakeUpForTesting(
+ internal::TaskQueueImpl::DelayedWakeUp{delayed_runtime, ++sequence_num});
+
+ time_domain_->WakeUpReadyDelayedQueues(&lazy_now);
+
+ // The second task queue should wake up first since it has a lower sequence
+ // number.
+ internal::TaskQueueImpl* next_task_queue;
+ EXPECT_TRUE(time_domain_->NextScheduledTaskQueue(&next_task_queue));
+ EXPECT_EQ(task_queue2.get(), next_task_queue);
+
+ task_queue2->UnregisterTaskQueue();
+}
+
+TEST_F(TimeDomainTest, CancelDelayedWork) {
+ base::TimeTicks now = time_domain_->Now();
+ LazyNow lazy_now(now);
+ base::TimeTicks run_time = now + base::TimeDelta::FromMilliseconds(20);
+
+ EXPECT_CALL(*time_domain_.get(), RequestWakeUpAt(_, run_time));
+ task_queue_->SetDelayedWakeUpForTesting(
+ internal::TaskQueueImpl::DelayedWakeUp{run_time, 0});
+
+ internal::TaskQueueImpl* next_task_queue;
+ EXPECT_TRUE(time_domain_->NextScheduledTaskQueue(&next_task_queue));
+ EXPECT_EQ(task_queue_.get(), next_task_queue);
+
+ EXPECT_CALL(*time_domain_.get(), CancelWakeUpAt(run_time));
+ task_queue_->SetDelayedWakeUpForTesting(base::nullopt);
+ EXPECT_FALSE(time_domain_->NextScheduledTaskQueue(&next_task_queue));
+}
+
+TEST_F(TimeDomainTest, CancelDelayedWork_TwoQueues) {
+ std::unique_ptr<TaskQueueImplForTest> task_queue2 =
+ std::make_unique<TaskQueueImplForTest>(nullptr, time_domain_.get(),
+ TaskQueue::Spec("test"));
+
+ base::TimeTicks now = time_domain_->Now();
+ LazyNow lazy_now(now);
+ base::TimeTicks run_time1 = now + base::TimeDelta::FromMilliseconds(20);
+ base::TimeTicks run_time2 = now + base::TimeDelta::FromMilliseconds(40);
+ EXPECT_CALL(*time_domain_.get(), RequestWakeUpAt(_, run_time1));
+ task_queue_->SetDelayedWakeUpForTesting(
+ internal::TaskQueueImpl::DelayedWakeUp{run_time1, 0});
+ Mock::VerifyAndClearExpectations(time_domain_.get());
+
+ EXPECT_CALL(*time_domain_.get(), RequestWakeUpAt(_, _)).Times(0);
+ task_queue2->SetDelayedWakeUpForTesting(
+ internal::TaskQueueImpl::DelayedWakeUp{run_time2, 0});
+ Mock::VerifyAndClearExpectations(time_domain_.get());
+
+ internal::TaskQueueImpl* next_task_queue;
+ EXPECT_TRUE(time_domain_->NextScheduledTaskQueue(&next_task_queue));
+ EXPECT_EQ(task_queue_.get(), next_task_queue);
+
+ base::TimeTicks next_run_time;
+ ASSERT_TRUE(time_domain_->NextScheduledRunTime(&next_run_time));
+ EXPECT_EQ(run_time1, next_run_time);
+
+ EXPECT_CALL(*time_domain_.get(), CancelWakeUpAt(run_time1));
+ EXPECT_CALL(*time_domain_.get(), RequestWakeUpAt(_, run_time2));
+ task_queue_->SetDelayedWakeUpForTesting(base::nullopt);
+ EXPECT_TRUE(time_domain_->NextScheduledTaskQueue(&next_task_queue));
+ EXPECT_EQ(task_queue2.get(), next_task_queue);
+
+ ASSERT_TRUE(time_domain_->NextScheduledRunTime(&next_run_time));
+ EXPECT_EQ(run_time2, next_run_time);
+
+ Mock::VerifyAndClearExpectations(time_domain_.get());
+ EXPECT_CALL(*time_domain_.get(), RequestWakeUpAt(_, _)).Times(AnyNumber());
+ EXPECT_CALL(*time_domain_.get(), CancelWakeUpAt(_)).Times(AnyNumber());
+
+ // Tidy up.
+ task_queue2->UnregisterTaskQueue();
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/virtual_time_domain.cc b/chromium/third_party/blink/renderer/platform/scheduler/base/virtual_time_domain.cc
new file mode 100644
index 00000000000..55d425b4dde
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/virtual_time_domain.cc
@@ -0,0 +1,69 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/base/virtual_time_domain.h"
+
+#include "base/bind.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_manager_impl.h"
+
+namespace blink {
+namespace scheduler {
+
+VirtualTimeDomain::VirtualTimeDomain(base::TimeTicks initial_time_ticks)
+ : now_ticks_(initial_time_ticks), task_queue_manager_(nullptr) {}
+
+VirtualTimeDomain::~VirtualTimeDomain() = default;
+
+void VirtualTimeDomain::OnRegisterWithTaskQueueManager(
+ TaskQueueManagerImpl* task_queue_manager) {
+ task_queue_manager_ = task_queue_manager;
+ DCHECK(task_queue_manager_);
+}
+
+LazyNow VirtualTimeDomain::CreateLazyNow() const {
+ base::AutoLock lock(lock_);
+ return LazyNow(now_ticks_);
+}
+
+base::TimeTicks VirtualTimeDomain::Now() const {
+ base::AutoLock lock(lock_);
+ return now_ticks_;
+}
+
+void VirtualTimeDomain::RequestWakeUpAt(base::TimeTicks now,
+ base::TimeTicks run_time) {
+ // We don't need to do anything here because the caller of AdvanceTo is
+ // responsible for calling TaskQueueManagerImpl::MaybeScheduleImmediateWork if
+ // needed.
+}
+
+void VirtualTimeDomain::CancelWakeUpAt(base::TimeTicks run_time) {
+ // We ignore this because RequestWakeUpAt is a NOP.
+}
+
+base::Optional<base::TimeDelta> VirtualTimeDomain::DelayTillNextTask(
+ LazyNow* lazy_now) {
+ return base::nullopt;
+}
+
+void VirtualTimeDomain::AsValueIntoInternal(
+ base::trace_event::TracedValue* state) const {}
+
+void VirtualTimeDomain::AdvanceNowTo(base::TimeTicks now) {
+ base::AutoLock lock(lock_);
+ DCHECK_GE(now, now_ticks_);
+ now_ticks_ = now;
+}
+
+void VirtualTimeDomain::RequestDoWork() {
+ task_queue_manager_->MaybeScheduleImmediateWork(FROM_HERE);
+}
+
+const char* VirtualTimeDomain::GetName() const {
+ return "VirtualTimeDomain";
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/virtual_time_domain.h b/chromium/third_party/blink/renderer/platform/scheduler/base/virtual_time_domain.h
new file mode 100644
index 00000000000..c4f37699830
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/virtual_time_domain.h
@@ -0,0 +1,55 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_VIRTUAL_TIME_DOMAIN_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_VIRTUAL_TIME_DOMAIN_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/threading/thread_checker.h"
+#include "third_party/blink/renderer/platform/scheduler/base/time_domain.h"
+
+namespace blink {
+namespace scheduler {
+
+class PLATFORM_EXPORT VirtualTimeDomain : public TimeDomain {
+ public:
+ explicit VirtualTimeDomain(base::TimeTicks initial_time_ticks);
+ ~VirtualTimeDomain() override;
+
+ // TimeDomain implementation:
+ LazyNow CreateLazyNow() const override;
+ base::TimeTicks Now() const override;
+ base::Optional<base::TimeDelta> DelayTillNextTask(LazyNow* lazy_now) override;
+ const char* GetName() const override;
+
+ // Advances this time domain to |now|. NOTE |now| is supposed to be
+ // monotonically increasing. NOTE it's the responsibility of the caller to
+ // call TaskQueueManager::MaybeScheduleImmediateWork if needed.
+ void AdvanceNowTo(base::TimeTicks now);
+
+ protected:
+ void OnRegisterWithTaskQueueManager(
+ TaskQueueManagerImpl* task_queue_manager) override;
+ void RequestWakeUpAt(base::TimeTicks now, base::TimeTicks run_time) override;
+ void CancelWakeUpAt(base::TimeTicks run_time) override;
+ void AsValueIntoInternal(
+ base::trace_event::TracedValue* state) const override;
+
+ void RequestDoWork();
+
+ private:
+ mutable base::Lock lock_; // Protects |now_ticks_|
+ base::TimeTicks now_ticks_;
+
+ TaskQueueManagerImpl* task_queue_manager_; // NOT OWNED
+ base::Closure do_work_closure_;
+
+ DISALLOW_COPY_AND_ASSIGN(VirtualTimeDomain);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_VIRTUAL_TIME_DOMAIN_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/work_queue.cc b/chromium/third_party/blink/renderer/platform/scheduler/base/work_queue.cc
new file mode 100644
index 00000000000..462d5c07eb2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/work_queue.cc
@@ -0,0 +1,234 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/base/work_queue.h"
+
+#include "third_party/blink/renderer/platform/scheduler/base/work_queue_sets.h"
+
+namespace blink {
+namespace scheduler {
+namespace internal {
+
+WorkQueue::WorkQueue(TaskQueueImpl* task_queue,
+ const char* name,
+ QueueType queue_type)
+ : task_queue_(task_queue), name_(name), queue_type_(queue_type) {}
+
+void WorkQueue::AsValueInto(base::TimeTicks now,
+ base::trace_event::TracedValue* state) const {
+ for (const TaskQueueImpl::Task& task : tasks_) {
+ TaskQueueImpl::TaskAsValueInto(task, now, state);
+ }
+}
+
+WorkQueue::~WorkQueue() {
+ DCHECK(!work_queue_sets_) << task_queue_->GetName() << " : "
+ << work_queue_sets_->GetName() << " : " << name_;
+}
+
+const TaskQueueImpl::Task* WorkQueue::GetFrontTask() const {
+ if (tasks_.empty())
+ return nullptr;
+ return &tasks_.front();
+}
+
+const TaskQueueImpl::Task* WorkQueue::GetBackTask() const {
+ if (tasks_.empty())
+ return nullptr;
+ return &tasks_.back();
+}
+
+bool WorkQueue::BlockedByFence() const {
+ if (!fence_)
+ return false;
+
+ // If the queue is empty then any future tasks will have a higher enqueue
+ // order and will be blocked. The queue is also blocked if the head is past
+ // the fence.
+ return tasks_.empty() || tasks_.front().enqueue_order() >= fence_;
+}
+
+bool WorkQueue::GetFrontTaskEnqueueOrder(EnqueueOrder* enqueue_order) const {
+ if (tasks_.empty() || BlockedByFence())
+ return false;
+ // Quick sanity check.
+ DCHECK_LE(tasks_.front().enqueue_order(), tasks_.back().enqueue_order())
+ << task_queue_->GetName() << " : " << work_queue_sets_->GetName() << " : "
+ << name_;
+ *enqueue_order = tasks_.front().enqueue_order();
+ return true;
+}
+
+void WorkQueue::Push(TaskQueueImpl::Task task) {
+ bool was_empty = tasks_.empty();
+#ifndef NDEBUG
+ DCHECK(task.enqueue_order_set());
+#endif
+
+ // Make sure the |enqueue_order()| is monotonically increasing.
+ DCHECK(was_empty || tasks_.rbegin()->enqueue_order() < task.enqueue_order());
+
+ // Amoritized O(1).
+ tasks_.push_back(std::move(task));
+
+ if (!was_empty)
+ return;
+
+ // If we hit the fence, pretend to WorkQueueSets that we're empty.
+ if (work_queue_sets_ && !BlockedByFence())
+ work_queue_sets_->OnTaskPushedToEmptyQueue(this);
+}
+
+void WorkQueue::PushNonNestableTaskToFront(TaskQueueImpl::Task task) {
+ DCHECK(task.nestable == base::Nestable::kNonNestable);
+
+ bool was_empty = tasks_.empty();
+ bool was_blocked = BlockedByFence();
+#ifndef NDEBUG
+ DCHECK(task.enqueue_order_set());
+#endif
+
+ if (!was_empty) {
+ // Make sure the |enqueue_order()| is monotonically increasing.
+ DCHECK_LE(task.enqueue_order(), tasks_.front().enqueue_order())
+ << task_queue_->GetName() << " : " << work_queue_sets_->GetName()
+ << " : " << name_;
+ }
+
+ // Amoritized O(1).
+ tasks_.push_front(std::move(task));
+
+ if (!work_queue_sets_)
+ return;
+
+ // Pretend to WorkQueueSets that nothing has changed if we're blocked.
+ if (BlockedByFence())
+ return;
+
+ // Pushing task to front may unblock the fence.
+ if (was_empty || was_blocked) {
+ work_queue_sets_->OnTaskPushedToEmptyQueue(this);
+ } else {
+ work_queue_sets_->OnFrontTaskChanged(this);
+ }
+}
+
+void WorkQueue::ReloadEmptyImmediateQueue() {
+ DCHECK(tasks_.empty());
+
+ tasks_ = task_queue_->TakeImmediateIncomingQueue();
+ if (tasks_.empty())
+ return;
+
+ // If we hit the fence, pretend to WorkQueueSets that we're empty.
+ if (work_queue_sets_ && !BlockedByFence())
+ work_queue_sets_->OnTaskPushedToEmptyQueue(this);
+}
+
+TaskQueueImpl::Task WorkQueue::TakeTaskFromWorkQueue() {
+ DCHECK(work_queue_sets_);
+ DCHECK(!tasks_.empty());
+
+ TaskQueueImpl::Task pending_task = tasks_.TakeFirst();
+ // NB immediate tasks have a different pipeline to delayed ones.
+ if (queue_type_ == QueueType::kImmediate && tasks_.empty()) {
+ // Short-circuit the queue reload so that OnPopQueue does the right thing.
+ tasks_ = task_queue_->TakeImmediateIncomingQueue();
+ }
+ // OnPopQueue calls GetFrontTaskEnqueueOrder which checks BlockedByFence() so
+ // we don't need to here.
+ work_queue_sets_->OnPopQueue(this);
+ task_queue_->TraceQueueSize();
+ return pending_task;
+}
+
+bool WorkQueue::RemoveAllCanceledTasksFromFront() {
+ DCHECK(work_queue_sets_);
+ bool task_removed = false;
+ while (!tasks_.empty() &&
+ (!tasks_.front().task || tasks_.front().task.IsCancelled())) {
+ tasks_.pop_front();
+ task_removed = true;
+ }
+ if (task_removed) {
+ // NB immediate tasks have a different pipeline to delayed ones.
+ if (queue_type_ == QueueType::kImmediate && tasks_.empty()) {
+ // Short-circuit the queue reload so that OnPopQueue does the right thing.
+ tasks_ = task_queue_->TakeImmediateIncomingQueue();
+ }
+ work_queue_sets_->OnPopQueue(this);
+ task_queue_->TraceQueueSize();
+ }
+ return task_removed;
+}
+
+void WorkQueue::AssignToWorkQueueSets(WorkQueueSets* work_queue_sets) {
+ work_queue_sets_ = work_queue_sets;
+}
+
+void WorkQueue::AssignSetIndex(size_t work_queue_set_index) {
+ work_queue_set_index_ = work_queue_set_index;
+}
+
+bool WorkQueue::InsertFenceImpl(EnqueueOrder fence) {
+ DCHECK_NE(fence, 0u);
+ DCHECK(fence >= fence_ || fence == 1u);
+ bool was_blocked_by_fence = BlockedByFence();
+ fence_ = fence;
+ return was_blocked_by_fence;
+}
+
+void WorkQueue::InsertFenceSilently(EnqueueOrder fence) {
+ // Ensure that there is no fence present or a new one blocks queue completely.
+ DCHECK(fence_ == 0u || fence == 1u);
+ InsertFenceImpl(fence);
+}
+
+bool WorkQueue::InsertFence(EnqueueOrder fence) {
+ bool was_blocked_by_fence = InsertFenceImpl(fence);
+
+ // Moving the fence forward may unblock some tasks.
+ if (work_queue_sets_ && !tasks_.empty() && was_blocked_by_fence &&
+ !BlockedByFence()) {
+ work_queue_sets_->OnTaskPushedToEmptyQueue(this);
+ return true;
+ }
+ // Fence insertion may have blocked all tasks in this work queue.
+ if (BlockedByFence())
+ work_queue_sets_->OnQueueBlocked(this);
+ return false;
+}
+
+bool WorkQueue::RemoveFence() {
+ bool was_blocked_by_fence = BlockedByFence();
+ fence_ = 0;
+ if (work_queue_sets_ && !tasks_.empty() && was_blocked_by_fence) {
+ work_queue_sets_->OnTaskPushedToEmptyQueue(this);
+ return true;
+ }
+ return false;
+}
+
+bool WorkQueue::ShouldRunBefore(const WorkQueue* other_queue) const {
+ DCHECK(!tasks_.empty());
+ DCHECK(!other_queue->tasks_.empty());
+ EnqueueOrder enqueue_order = 0;
+ EnqueueOrder other_enqueue_order = 0;
+ bool have_task = GetFrontTaskEnqueueOrder(&enqueue_order);
+ bool have_other_task =
+ other_queue->GetFrontTaskEnqueueOrder(&other_enqueue_order);
+ DCHECK(have_task);
+ DCHECK(have_other_task);
+ return enqueue_order < other_enqueue_order;
+}
+
+void WorkQueue::PopTaskForTesting() {
+ if (tasks_.empty())
+ return;
+ tasks_.pop_front();
+}
+
+} // namespace internal
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/work_queue.h b/chromium/third_party/blink/renderer/platform/scheduler/base/work_queue.h
new file mode 100644
index 00000000000..9b3b8010aec
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/work_queue.h
@@ -0,0 +1,156 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_WORK_QUEUE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_WORK_QUEUE_H_
+
+#include <stddef.h>
+
+#include <set>
+
+#include "base/trace_event/trace_event.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "third_party/blink/renderer/platform/scheduler/base/enqueue_order.h"
+#include "third_party/blink/renderer/platform/scheduler/base/intrusive_heap.h"
+#include "third_party/blink/renderer/platform/scheduler/base/sequenced_task_source.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl.h"
+
+namespace blink {
+namespace scheduler {
+namespace internal {
+
+class WorkQueueSets;
+
+// This class keeps track of immediate and delayed tasks which are due to run
+// now. It interfaces deeply with WorkQueueSets which keeps track of which queue
+// (with a given priority) contains the oldest task.
+//
+// If a fence is inserted, WorkQueue behaves normally up until
+// TakeTaskFromWorkQueue reaches or exceeds the fence. At that point it the
+// API subset used by WorkQueueSets pretends the WorkQueue is empty until the
+// fence is removed. This functionality is a primitive intended for use by
+// throttling mechanisms.
+class PLATFORM_EXPORT WorkQueue {
+ public:
+ using QueueType = SequencedTaskSource::WorkType;
+
+ // Note |task_queue| can be null if queue_type is kNonNestable.
+ WorkQueue(TaskQueueImpl* task_queue, const char* name, QueueType queue_type);
+ ~WorkQueue();
+
+ // Associates this work queue with the given work queue sets. This must be
+ // called before any tasks can be inserted into this work queue.
+ void AssignToWorkQueueSets(WorkQueueSets* work_queue_sets);
+
+ // Assigns the current set index.
+ void AssignSetIndex(size_t work_queue_set_index);
+
+ void AsValueInto(base::TimeTicks now,
+ base::trace_event::TracedValue* state) const;
+
+ // Returns true if the |tasks_| is empty. This method ignores any fences.
+ bool Empty() const { return tasks_.empty(); }
+
+ // If the |tasks_| isn't empty and a fence hasn't been reached,
+ // |enqueue_order| gets set to the enqueue order of the front task and the
+ // function returns true. Otherwise the function returns false.
+ bool GetFrontTaskEnqueueOrder(EnqueueOrder* enqueue_order) const;
+
+ // Returns the first task in this queue or null if the queue is empty. This
+ // method ignores any fences.
+ const TaskQueueImpl::Task* GetFrontTask() const;
+
+ // Returns the last task in this queue or null if the queue is empty. This
+ // method ignores any fences.
+ const TaskQueueImpl::Task* GetBackTask() const;
+
+ // Pushes the task onto the |tasks_| and if a fence hasn't been reached
+ // it informs the WorkQueueSets if the head changed.
+ void Push(TaskQueueImpl::Task task);
+
+ // Pushes the task onto the front of the |tasks_| and if it's before any
+ // fence it informs the WorkQueueSets the head changed. Use with caution this
+ // API can easily lead to task starvation if misused.
+ void PushNonNestableTaskToFront(TaskQueueImpl::Task task);
+
+ // Reloads the empty |tasks_| with
+ // |task_queue_->TakeImmediateIncomingQueue| and if a fence hasn't been
+ // reached it informs the WorkQueueSets if the head changed.
+ void ReloadEmptyImmediateQueue();
+
+ size_t Size() const { return tasks_.size(); }
+
+ // Pulls a task off the |tasks_| and informs the WorkQueueSets. If the
+ // task removed had an enqueue order >= the current fence then WorkQueue
+ // pretends to be empty as far as the WorkQueueSets is concerned.
+ TaskQueueImpl::Task TakeTaskFromWorkQueue();
+
+ // Removes all canceled tasks from the head of the list. Returns true if any
+ // tasks were removed.
+ bool RemoveAllCanceledTasksFromFront();
+
+ const char* name() const { return name_; }
+
+ TaskQueueImpl* task_queue() const { return task_queue_; }
+
+ WorkQueueSets* work_queue_sets() const { return work_queue_sets_; }
+
+ size_t work_queue_set_index() const { return work_queue_set_index_; }
+
+ HeapHandle heap_handle() const { return heap_handle_; }
+
+ void set_heap_handle(HeapHandle handle) { heap_handle_ = handle; }
+
+ QueueType queue_type() const { return queue_type_; }
+
+ // Returns true if the front task in this queue has an older enqueue order
+ // than the front task of |other_queue|. Both queue are assumed to be
+ // non-empty. This method ignores any fences.
+ bool ShouldRunBefore(const WorkQueue* other_queue) const;
+
+ // Submit a fence. When TakeTaskFromWorkQueue encounters a task whose
+ // enqueue_order is >= |fence| then the WorkQueue will start pretending to be.
+ // empty.
+ // Inserting a fence may supersede a previous one and unblock some tasks.
+ // Returns true if any tasks where unblocked, returns false otherwise.
+ bool InsertFence(EnqueueOrder fence);
+
+ // Submit a fence without triggering a WorkQueueSets notification.
+ // Caller must ensure that WorkQueueSets are properly updated.
+ // This method should not be called when a fence is already present.
+ void InsertFenceSilently(EnqueueOrder fence);
+
+ // Removes any fences that where added and if WorkQueue was pretending to be
+ // empty, then the real value is reported to WorkQueueSets. Returns true if
+ // any tasks where unblocked.
+ bool RemoveFence();
+
+ // Returns true if any tasks are blocked by the fence. Returns true if the
+ // queue is empty and fence has been set (i.e. future tasks would be blocked).
+ // Otherwise returns false.
+ bool BlockedByFence() const;
+
+ // Test support function. This should not be used in production code.
+ void PopTaskForTesting();
+
+ private:
+ bool InsertFenceImpl(EnqueueOrder fence);
+
+ TaskQueueImpl::TaskDeque tasks_;
+ WorkQueueSets* work_queue_sets_ = nullptr; // NOT OWNED.
+ TaskQueueImpl* const task_queue_; // NOT OWNED.
+ size_t work_queue_set_index_ = 0;
+ HeapHandle heap_handle_;
+ const char* const name_;
+ EnqueueOrder fence_ = 0;
+ const QueueType queue_type_;
+
+ DISALLOW_COPY_AND_ASSIGN(WorkQueue);
+};
+
+} // namespace internal
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_WORK_QUEUE_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/work_queue_sets.cc b/chromium/third_party/blink/renderer/platform/scheduler/base/work_queue_sets.cc
new file mode 100644
index 00000000000..04f64aaa021
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/work_queue_sets.cc
@@ -0,0 +1,172 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/base/work_queue_sets.h"
+
+#include "base/logging.h"
+
+namespace blink {
+namespace scheduler {
+namespace internal {
+
+WorkQueueSets::WorkQueueSets(size_t num_sets, const char* name)
+ : work_queue_heaps_(num_sets), name_(name) {}
+
+WorkQueueSets::~WorkQueueSets() = default;
+
+void WorkQueueSets::AddQueue(WorkQueue* work_queue, size_t set_index) {
+ DCHECK(!work_queue->work_queue_sets());
+ DCHECK_LT(set_index, work_queue_heaps_.size());
+ EnqueueOrder enqueue_order;
+ bool has_enqueue_order = work_queue->GetFrontTaskEnqueueOrder(&enqueue_order);
+ work_queue->AssignToWorkQueueSets(this);
+ work_queue->AssignSetIndex(set_index);
+ if (!has_enqueue_order)
+ return;
+ work_queue_heaps_[set_index].insert({enqueue_order, work_queue});
+}
+
+void WorkQueueSets::RemoveQueue(WorkQueue* work_queue) {
+ DCHECK_EQ(this, work_queue->work_queue_sets());
+ work_queue->AssignToWorkQueueSets(nullptr);
+ HeapHandle heap_handle = work_queue->heap_handle();
+ if (!heap_handle.IsValid())
+ return;
+ size_t set_index = work_queue->work_queue_set_index();
+ DCHECK_LT(set_index, work_queue_heaps_.size());
+ work_queue_heaps_[set_index].erase(heap_handle);
+}
+
+void WorkQueueSets::ChangeSetIndex(WorkQueue* work_queue, size_t set_index) {
+ DCHECK_EQ(this, work_queue->work_queue_sets());
+ DCHECK_LT(set_index, work_queue_heaps_.size());
+ EnqueueOrder enqueue_order;
+ bool has_enqueue_order = work_queue->GetFrontTaskEnqueueOrder(&enqueue_order);
+ size_t old_set = work_queue->work_queue_set_index();
+ DCHECK_LT(old_set, work_queue_heaps_.size());
+ DCHECK_NE(old_set, set_index);
+ work_queue->AssignSetIndex(set_index);
+ if (!has_enqueue_order)
+ return;
+ work_queue_heaps_[old_set].erase(work_queue->heap_handle());
+ work_queue_heaps_[set_index].insert({enqueue_order, work_queue});
+}
+
+void WorkQueueSets::OnFrontTaskChanged(WorkQueue* work_queue) {
+ EnqueueOrder enqueue_order;
+ bool has_enqueue_order = work_queue->GetFrontTaskEnqueueOrder(&enqueue_order);
+ DCHECK(has_enqueue_order);
+ size_t set = work_queue->work_queue_set_index();
+ work_queue_heaps_[set].ChangeKey(work_queue->heap_handle(),
+ {enqueue_order, work_queue});
+}
+
+void WorkQueueSets::OnTaskPushedToEmptyQueue(WorkQueue* work_queue) {
+ // NOTE if this function changes, we need to keep |WorkQueueSets::AddQueue| in
+ // sync.
+ DCHECK_EQ(this, work_queue->work_queue_sets());
+ EnqueueOrder enqueue_order;
+ bool has_enqueue_order = work_queue->GetFrontTaskEnqueueOrder(&enqueue_order);
+ DCHECK(has_enqueue_order);
+ size_t set_index = work_queue->work_queue_set_index();
+ DCHECK_LT(set_index, work_queue_heaps_.size()) << " set_index = "
+ << set_index;
+ // |work_queue| should not be in work_queue_heaps_[set_index].
+ DCHECK(!work_queue->heap_handle().IsValid());
+ work_queue_heaps_[set_index].insert({enqueue_order, work_queue});
+}
+
+void WorkQueueSets::OnPopQueue(WorkQueue* work_queue) {
+ // Assume that |work_queue| contains the lowest enqueue_order.
+ size_t set_index = work_queue->work_queue_set_index();
+ DCHECK_EQ(this, work_queue->work_queue_sets());
+ DCHECK_LT(set_index, work_queue_heaps_.size());
+ DCHECK(!work_queue_heaps_[set_index].empty()) << " set_index = " << set_index;
+ DCHECK_EQ(work_queue_heaps_[set_index].Min().value, work_queue)
+ << " set_index = " << set_index;
+ DCHECK(work_queue->heap_handle().IsValid());
+ EnqueueOrder enqueue_order;
+ if (work_queue->GetFrontTaskEnqueueOrder(&enqueue_order)) {
+ // O(log n)
+ work_queue_heaps_[set_index].ReplaceMin({enqueue_order, work_queue});
+ } else {
+ // O(log n)
+ work_queue_heaps_[set_index].Pop();
+ DCHECK(work_queue_heaps_[set_index].empty() ||
+ work_queue_heaps_[set_index].Min().value != work_queue);
+ }
+}
+
+void WorkQueueSets::OnQueueBlocked(WorkQueue* work_queue) {
+ DCHECK_EQ(this, work_queue->work_queue_sets());
+ HeapHandle heap_handle = work_queue->heap_handle();
+ if (!heap_handle.IsValid())
+ return;
+ size_t set_index = work_queue->work_queue_set_index();
+ DCHECK_LT(set_index, work_queue_heaps_.size());
+ work_queue_heaps_[set_index].erase(heap_handle);
+}
+
+bool WorkQueueSets::GetOldestQueueInSet(size_t set_index,
+ WorkQueue** out_work_queue) const {
+ DCHECK_LT(set_index, work_queue_heaps_.size());
+ if (work_queue_heaps_[set_index].empty())
+ return false;
+ *out_work_queue = work_queue_heaps_[set_index].Min().value;
+ DCHECK_EQ(set_index, (*out_work_queue)->work_queue_set_index());
+ DCHECK((*out_work_queue)->heap_handle().IsValid());
+ return true;
+}
+
+bool WorkQueueSets::GetOldestQueueAndEnqueueOrderInSet(
+ size_t set_index,
+ WorkQueue** out_work_queue,
+ EnqueueOrder* out_enqueue_order) const {
+ DCHECK_LT(set_index, work_queue_heaps_.size());
+ if (work_queue_heaps_[set_index].empty())
+ return false;
+ const OldestTaskEnqueueOrder& oldest = work_queue_heaps_[set_index].Min();
+ *out_work_queue = oldest.value;
+ *out_enqueue_order = oldest.key;
+ EnqueueOrder enqueue_order;
+ DCHECK(oldest.value->GetFrontTaskEnqueueOrder(&enqueue_order) &&
+ oldest.key == enqueue_order);
+ return true;
+}
+
+bool WorkQueueSets::IsSetEmpty(size_t set_index) const {
+ DCHECK_LT(set_index, work_queue_heaps_.size()) << " set_index = "
+ << set_index;
+ return work_queue_heaps_[set_index].empty();
+}
+
+#if DCHECK_IS_ON() || !defined(NDEBUG)
+bool WorkQueueSets::ContainsWorkQueueForTest(
+ const WorkQueue* work_queue) const {
+ EnqueueOrder enqueue_order;
+ bool has_enqueue_order = work_queue->GetFrontTaskEnqueueOrder(&enqueue_order);
+
+ for (const IntrusiveHeap<OldestTaskEnqueueOrder>& heap : work_queue_heaps_) {
+ for (const OldestTaskEnqueueOrder& heap_value_pair : heap) {
+ if (heap_value_pair.value == work_queue) {
+ DCHECK(has_enqueue_order);
+ DCHECK_EQ(heap_value_pair.key, enqueue_order);
+ DCHECK_EQ(this, work_queue->work_queue_sets());
+ return true;
+ }
+ }
+ }
+
+ if (work_queue->work_queue_sets() == this) {
+ DCHECK(!has_enqueue_order);
+ return true;
+ }
+
+ return false;
+}
+#endif
+
+} // namespace internal
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/work_queue_sets.h b/chromium/third_party/blink/renderer/platform/scheduler/base/work_queue_sets.h
new file mode 100644
index 00000000000..aa51cacf170
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/work_queue_sets.h
@@ -0,0 +1,104 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_WORK_QUEUE_SETS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_WORK_QUEUE_SETS_H_
+
+#include <stddef.h>
+
+#include <map>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/scheduler/base/intrusive_heap.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/base/work_queue.h"
+
+namespace blink {
+namespace scheduler {
+namespace internal {
+
+// There is a WorkQueueSet for each scheduler priority and each WorkQueueSet
+// uses a EnqueueOrderToWorkQueueMap to keep track of which queue in the set has
+// the oldest task (i.e. the one that should be run next if the
+// TaskQueueSelector chooses to run a task a given priority). The reason this
+// works is because std::map is a tree based associative container and all the
+// values are kept in sorted order.
+class PLATFORM_EXPORT WorkQueueSets {
+ public:
+ WorkQueueSets(size_t num_sets, const char* name);
+ ~WorkQueueSets();
+
+ // O(log num queues)
+ void AddQueue(WorkQueue* queue, size_t set_index);
+
+ // O(log num queues)
+ void RemoveQueue(WorkQueue* work_queue);
+
+ // O(log num queues)
+ void ChangeSetIndex(WorkQueue* queue, size_t set_index);
+
+ // O(log num queues)
+ void OnFrontTaskChanged(WorkQueue* queue);
+
+ // O(log num queues)
+ void OnTaskPushedToEmptyQueue(WorkQueue* work_queue);
+
+ // If empty it's O(1) amortized, otherwise it's O(log num queues)
+ // Assumes |work_queue| contains the lowest enqueue order in the set.
+ void OnPopQueue(WorkQueue* work_queue);
+
+ // O(log num queues)
+ void OnQueueBlocked(WorkQueue* work_queue);
+
+ // O(1)
+ bool GetOldestQueueInSet(size_t set_index, WorkQueue** out_work_queue) const;
+
+ // O(1)
+ bool GetOldestQueueAndEnqueueOrderInSet(
+ size_t set_index,
+ WorkQueue** out_work_queue,
+ EnqueueOrder* out_enqueue_order) const;
+
+ // O(1)
+ bool IsSetEmpty(size_t set_index) const;
+
+#if DCHECK_IS_ON() || !defined(NDEBUG)
+ // Note this iterates over everything in |work_queue_heaps_|.
+ // It's intended for use with DCHECKS and for testing
+ bool ContainsWorkQueueForTest(const WorkQueue* queue) const;
+#endif
+
+ const char* GetName() const { return name_; }
+
+ private:
+ struct OldestTaskEnqueueOrder {
+ EnqueueOrder key;
+ WorkQueue* value;
+
+ bool operator<=(const OldestTaskEnqueueOrder& other) const {
+ return key <= other.key;
+ }
+
+ void SetHeapHandle(HeapHandle handle) { value->set_heap_handle(handle); }
+
+ void ClearHeapHandle() { value->set_heap_handle(HeapHandle()); }
+ };
+
+ // For each set |work_queue_heaps_| has a queue of WorkQueue ordered by the
+ // oldest task in each WorkQueue.
+ std::vector<IntrusiveHeap<OldestTaskEnqueueOrder>> work_queue_heaps_;
+ const char* const name_;
+
+ DISALLOW_COPY_AND_ASSIGN(WorkQueueSets);
+};
+
+} // namespace internal
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_WORK_QUEUE_SETS_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/work_queue_sets_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/base/work_queue_sets_unittest.cc
new file mode 100644
index 00000000000..fe4794da7b9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/work_queue_sets_unittest.cc
@@ -0,0 +1,329 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/base/work_queue_sets.h"
+
+#include <stddef.h>
+
+#include "base/memory/ptr_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "third_party/blink/renderer/platform/scheduler/base/work_queue.h"
+
+namespace blink {
+namespace scheduler {
+class TimeDomain;
+
+namespace internal {
+
+class WorkQueueSetsTest : public testing::Test {
+ public:
+ void SetUp() override {
+ work_queue_sets_.reset(new WorkQueueSets(kNumSets, "test"));
+ }
+
+ void TearDown() override {
+ for (std::unique_ptr<WorkQueue>& work_queue : work_queues_) {
+ if (work_queue->work_queue_sets())
+ work_queue_sets_->RemoveQueue(work_queue.get());
+ }
+ }
+
+ protected:
+ enum {
+ kNumSets = 5 // An arbitary choice.
+ };
+
+ WorkQueue* NewTaskQueue(const char* queue_name) {
+ WorkQueue* queue =
+ new WorkQueue(nullptr, "test", WorkQueue::QueueType::kImmediate);
+ work_queues_.push_back(base::WrapUnique(queue));
+ work_queue_sets_->AddQueue(queue, TaskQueue::kControlPriority);
+ return queue;
+ }
+
+ TaskQueueImpl::Task FakeTaskWithEnqueueOrder(int enqueue_order) {
+ TaskQueueImpl::Task fake_task(
+ TaskQueue::PostedTask(base::BindOnce([] {}), FROM_HERE),
+ base::TimeTicks(), 0);
+ fake_task.set_enqueue_order(enqueue_order);
+ return fake_task;
+ }
+
+ TaskQueueImpl::Task FakeNonNestableTaskWithEnqueueOrder(int enqueue_order) {
+ TaskQueueImpl::Task fake_task(
+ TaskQueue::PostedTask(base::BindOnce([] {}), FROM_HERE),
+ base::TimeTicks(), 0);
+ fake_task.set_enqueue_order(enqueue_order);
+ fake_task.nestable = base::Nestable::kNonNestable;
+ return fake_task;
+ }
+
+ std::vector<std::unique_ptr<WorkQueue>> work_queues_;
+ std::unique_ptr<WorkQueueSets> work_queue_sets_;
+};
+
+TEST_F(WorkQueueSetsTest, ChangeSetIndex) {
+ WorkQueue* work_queue = NewTaskQueue("queue");
+ size_t set = TaskQueue::kNormalPriority;
+ work_queue_sets_->ChangeSetIndex(work_queue, set);
+
+ EXPECT_EQ(set, work_queue->work_queue_set_index());
+}
+
+TEST_F(WorkQueueSetsTest, GetOldestQueueInSet_QueueEmpty) {
+ WorkQueue* work_queue = NewTaskQueue("queue");
+ size_t set = TaskQueue::kNormalPriority;
+ work_queue_sets_->ChangeSetIndex(work_queue, set);
+
+ WorkQueue* selected_work_queue;
+ EXPECT_FALSE(
+ work_queue_sets_->GetOldestQueueInSet(set, &selected_work_queue));
+}
+
+TEST_F(WorkQueueSetsTest, OnTaskPushedToEmptyQueue) {
+ WorkQueue* work_queue = NewTaskQueue("queue");
+ size_t set = TaskQueue::kNormalPriority;
+ work_queue_sets_->ChangeSetIndex(work_queue, set);
+
+ WorkQueue* selected_work_queue;
+ EXPECT_FALSE(
+ work_queue_sets_->GetOldestQueueInSet(set, &selected_work_queue));
+
+ // Calls OnTaskPushedToEmptyQueue.
+ work_queue->Push(FakeTaskWithEnqueueOrder(10));
+
+ EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(set, &selected_work_queue));
+ EXPECT_EQ(work_queue, selected_work_queue);
+}
+
+TEST_F(WorkQueueSetsTest, GetOldestQueueInSet_SingleTaskInSet) {
+ WorkQueue* work_queue = NewTaskQueue("queue");
+ work_queue->Push(FakeTaskWithEnqueueOrder(10));
+ size_t set = 1;
+ work_queue_sets_->ChangeSetIndex(work_queue, set);
+
+ WorkQueue* selected_work_queue;
+ EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(set, &selected_work_queue));
+ EXPECT_EQ(work_queue, selected_work_queue);
+}
+
+TEST_F(WorkQueueSetsTest, GetOldestQueueAndEnqueueOrderInSet) {
+ WorkQueue* work_queue = NewTaskQueue("queue");
+ work_queue->Push(FakeTaskWithEnqueueOrder(10));
+ size_t set = 1;
+ work_queue_sets_->ChangeSetIndex(work_queue, set);
+
+ WorkQueue* selected_work_queue;
+ EnqueueOrder enqueue_order;
+ EXPECT_TRUE(work_queue_sets_->GetOldestQueueAndEnqueueOrderInSet(
+ set, &selected_work_queue, &enqueue_order));
+ EXPECT_EQ(work_queue, selected_work_queue);
+ EXPECT_EQ(10u, enqueue_order);
+}
+
+TEST_F(WorkQueueSetsTest, GetOldestQueueInSet_MultipleAgesInSet) {
+ WorkQueue* queue1 = NewTaskQueue("queue1");
+ WorkQueue* queue2 = NewTaskQueue("queue2");
+ WorkQueue* queue3 = NewTaskQueue("queue2");
+ queue1->Push(FakeTaskWithEnqueueOrder(6));
+ queue2->Push(FakeTaskWithEnqueueOrder(5));
+ queue3->Push(FakeTaskWithEnqueueOrder(4));
+ size_t set = 2;
+ work_queue_sets_->ChangeSetIndex(queue1, set);
+ work_queue_sets_->ChangeSetIndex(queue2, set);
+ work_queue_sets_->ChangeSetIndex(queue3, set);
+
+ WorkQueue* selected_work_queue;
+ EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(set, &selected_work_queue));
+ EXPECT_EQ(queue3, selected_work_queue);
+}
+
+TEST_F(WorkQueueSetsTest, OnPopQueue) {
+ WorkQueue* queue1 = NewTaskQueue("queue1");
+ WorkQueue* queue2 = NewTaskQueue("queue2");
+ WorkQueue* queue3 = NewTaskQueue("queue3");
+ queue1->Push(FakeTaskWithEnqueueOrder(6));
+ queue2->Push(FakeTaskWithEnqueueOrder(1));
+ queue2->Push(FakeTaskWithEnqueueOrder(3));
+ queue3->Push(FakeTaskWithEnqueueOrder(4));
+ size_t set = 3;
+ work_queue_sets_->ChangeSetIndex(queue1, set);
+ work_queue_sets_->ChangeSetIndex(queue2, set);
+ work_queue_sets_->ChangeSetIndex(queue3, set);
+
+ WorkQueue* selected_work_queue;
+ EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(set, &selected_work_queue));
+ EXPECT_EQ(queue2, selected_work_queue);
+
+ queue2->PopTaskForTesting();
+ work_queue_sets_->OnPopQueue(queue2);
+
+ EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(set, &selected_work_queue));
+ EXPECT_EQ(queue2, selected_work_queue);
+}
+
+TEST_F(WorkQueueSetsTest, OnPopQueue_QueueBecomesEmpty) {
+ WorkQueue* queue1 = NewTaskQueue("queue1");
+ WorkQueue* queue2 = NewTaskQueue("queue2");
+ WorkQueue* queue3 = NewTaskQueue("queue3");
+ queue1->Push(FakeTaskWithEnqueueOrder(6));
+ queue2->Push(FakeTaskWithEnqueueOrder(5));
+ queue3->Push(FakeTaskWithEnqueueOrder(4));
+ size_t set = 4;
+ work_queue_sets_->ChangeSetIndex(queue1, set);
+ work_queue_sets_->ChangeSetIndex(queue2, set);
+ work_queue_sets_->ChangeSetIndex(queue3, set);
+
+ WorkQueue* selected_work_queue;
+ EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(set, &selected_work_queue));
+ EXPECT_EQ(queue3, selected_work_queue);
+
+ queue3->PopTaskForTesting();
+ work_queue_sets_->OnPopQueue(queue3);
+
+ EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(set, &selected_work_queue));
+ EXPECT_EQ(queue2, selected_work_queue);
+}
+
+TEST_F(WorkQueueSetsTest,
+ GetOldestQueueInSet_MultipleAgesInSetIntegerRollover) {
+ WorkQueue* queue1 = NewTaskQueue("queue1");
+ WorkQueue* queue2 = NewTaskQueue("queue2");
+ WorkQueue* queue3 = NewTaskQueue("queue3");
+ queue1->Push(FakeTaskWithEnqueueOrder(0x7ffffff1));
+ queue2->Push(FakeTaskWithEnqueueOrder(0x7ffffff0));
+ queue3->Push(FakeTaskWithEnqueueOrder(-0x7ffffff1));
+ size_t set = 1;
+ work_queue_sets_->ChangeSetIndex(queue1, set);
+ work_queue_sets_->ChangeSetIndex(queue2, set);
+ work_queue_sets_->ChangeSetIndex(queue3, set);
+
+ WorkQueue* selected_work_queue;
+ EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(set, &selected_work_queue));
+ EXPECT_EQ(queue2, selected_work_queue);
+}
+
+TEST_F(WorkQueueSetsTest, GetOldestQueueInSet_MultipleAgesInSet_RemoveQueue) {
+ WorkQueue* queue1 = NewTaskQueue("queue1");
+ WorkQueue* queue2 = NewTaskQueue("queue2");
+ WorkQueue* queue3 = NewTaskQueue("queue3");
+ queue1->Push(FakeTaskWithEnqueueOrder(6));
+ queue2->Push(FakeTaskWithEnqueueOrder(5));
+ queue3->Push(FakeTaskWithEnqueueOrder(4));
+ size_t set = 1;
+ work_queue_sets_->ChangeSetIndex(queue1, set);
+ work_queue_sets_->ChangeSetIndex(queue2, set);
+ work_queue_sets_->ChangeSetIndex(queue3, set);
+ work_queue_sets_->RemoveQueue(queue3);
+
+ WorkQueue* selected_work_queue;
+ EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(set, &selected_work_queue));
+ EXPECT_EQ(queue2, selected_work_queue);
+}
+
+TEST_F(WorkQueueSetsTest, ChangeSetIndex_Complex) {
+ WorkQueue* queue1 = NewTaskQueue("queue1");
+ WorkQueue* queue2 = NewTaskQueue("queue2");
+ WorkQueue* queue3 = NewTaskQueue("queue3");
+ WorkQueue* queue4 = NewTaskQueue("queue4");
+ queue1->Push(FakeTaskWithEnqueueOrder(6));
+ queue2->Push(FakeTaskWithEnqueueOrder(5));
+ queue3->Push(FakeTaskWithEnqueueOrder(4));
+ queue4->Push(FakeTaskWithEnqueueOrder(3));
+ size_t set1 = 1;
+ size_t set2 = 2;
+ work_queue_sets_->ChangeSetIndex(queue1, set1);
+ work_queue_sets_->ChangeSetIndex(queue2, set1);
+ work_queue_sets_->ChangeSetIndex(queue3, set2);
+ work_queue_sets_->ChangeSetIndex(queue4, set2);
+
+ WorkQueue* selected_work_queue;
+ EXPECT_TRUE(
+ work_queue_sets_->GetOldestQueueInSet(set1, &selected_work_queue));
+ EXPECT_EQ(queue2, selected_work_queue);
+
+ EXPECT_TRUE(
+ work_queue_sets_->GetOldestQueueInSet(set2, &selected_work_queue));
+ EXPECT_EQ(queue4, selected_work_queue);
+
+ work_queue_sets_->ChangeSetIndex(queue4, set1);
+
+ EXPECT_TRUE(
+ work_queue_sets_->GetOldestQueueInSet(set1, &selected_work_queue));
+ EXPECT_EQ(queue4, selected_work_queue);
+
+ EXPECT_TRUE(
+ work_queue_sets_->GetOldestQueueInSet(set2, &selected_work_queue));
+ EXPECT_EQ(queue3, selected_work_queue);
+}
+
+TEST_F(WorkQueueSetsTest, IsSetEmpty_NoWork) {
+ size_t set = 2;
+ EXPECT_TRUE(work_queue_sets_->IsSetEmpty(set));
+
+ WorkQueue* work_queue = NewTaskQueue("queue");
+ work_queue_sets_->ChangeSetIndex(work_queue, set);
+ EXPECT_TRUE(work_queue_sets_->IsSetEmpty(set));
+}
+
+TEST_F(WorkQueueSetsTest, IsSetEmpty_Work) {
+ size_t set = 2;
+ EXPECT_TRUE(work_queue_sets_->IsSetEmpty(set));
+
+ WorkQueue* work_queue = NewTaskQueue("queue");
+ work_queue->Push(FakeTaskWithEnqueueOrder(1));
+ work_queue_sets_->ChangeSetIndex(work_queue, set);
+ EXPECT_FALSE(work_queue_sets_->IsSetEmpty(set));
+
+ work_queue->PopTaskForTesting();
+ work_queue_sets_->OnPopQueue(work_queue);
+ EXPECT_TRUE(work_queue_sets_->IsSetEmpty(set));
+}
+
+TEST_F(WorkQueueSetsTest, BlockQueuesByFence) {
+ WorkQueue* queue1 = NewTaskQueue("queue1");
+ WorkQueue* queue2 = NewTaskQueue("queue2");
+
+ queue1->Push(FakeTaskWithEnqueueOrder(6));
+ queue2->Push(FakeTaskWithEnqueueOrder(7));
+ queue1->Push(FakeTaskWithEnqueueOrder(8));
+ queue2->Push(FakeTaskWithEnqueueOrder(9));
+
+ size_t set = TaskQueue::kControlPriority;
+
+ WorkQueue* selected_work_queue;
+ EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(set, &selected_work_queue));
+ EXPECT_EQ(selected_work_queue, queue1);
+
+ queue1->InsertFence(1);
+
+ EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(set, &selected_work_queue));
+ EXPECT_EQ(selected_work_queue, queue2);
+}
+
+TEST_F(WorkQueueSetsTest, PushNonNestableTaskToFront) {
+ WorkQueue* queue1 = NewTaskQueue("queue1");
+ WorkQueue* queue2 = NewTaskQueue("queue2");
+ WorkQueue* queue3 = NewTaskQueue("queue3");
+ queue1->Push(FakeTaskWithEnqueueOrder(6));
+ queue2->Push(FakeTaskWithEnqueueOrder(5));
+ queue3->Push(FakeTaskWithEnqueueOrder(4));
+ size_t set = 4;
+ work_queue_sets_->ChangeSetIndex(queue1, set);
+ work_queue_sets_->ChangeSetIndex(queue2, set);
+ work_queue_sets_->ChangeSetIndex(queue3, set);
+
+ WorkQueue* selected_work_queue;
+ EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(set, &selected_work_queue));
+ EXPECT_EQ(queue3, selected_work_queue);
+
+ queue1->PushNonNestableTaskToFront(FakeNonNestableTaskWithEnqueueOrder(2));
+
+ EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(set, &selected_work_queue));
+ EXPECT_EQ(queue1, selected_work_queue);
+}
+
+} // namespace internal
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/base/work_queue_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/base/work_queue_unittest.cc
new file mode 100644
index 00000000000..91624b2a65b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/base/work_queue_unittest.cc
@@ -0,0 +1,474 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/base/work_queue.h"
+
+#include <stddef.h>
+#include <memory>
+
+#include "base/bind.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "third_party/blink/renderer/platform/scheduler/base/real_time_domain.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/base/work_queue_sets.h"
+
+namespace blink {
+namespace scheduler {
+namespace internal {
+namespace {
+void NopTask() {}
+
+struct Cancelable {
+ Cancelable() : weak_ptr_factory(this) {}
+
+ void NopTask() {}
+
+ base::WeakPtrFactory<Cancelable> weak_ptr_factory;
+};
+} // namespace
+
+class WorkQueueTest : public testing::Test {
+ public:
+ void SetUp() override {
+ time_domain_.reset(new RealTimeDomain());
+ task_queue_ = std::make_unique<TaskQueueImpl>(nullptr, time_domain_.get(),
+ TaskQueue::Spec("test"));
+
+ work_queue_.reset(new WorkQueue(task_queue_.get(), "test",
+ WorkQueue::QueueType::kImmediate));
+ work_queue_sets_.reset(new WorkQueueSets(1, "test"));
+ work_queue_sets_->AddQueue(work_queue_.get(), 0);
+ }
+
+ void TearDown() override { work_queue_sets_->RemoveQueue(work_queue_.get()); }
+
+ protected:
+ TaskQueueImpl::Task FakeCancelableTaskWithEnqueueOrder(
+ int enqueue_order,
+ base::WeakPtr<Cancelable> weak_ptr) {
+ TaskQueueImpl::Task fake_task(
+ TaskQueue::PostedTask(base::BindOnce(&Cancelable::NopTask, weak_ptr),
+ FROM_HERE),
+ base::TimeTicks(), 0);
+ fake_task.set_enqueue_order(enqueue_order);
+ return fake_task;
+ }
+
+ TaskQueueImpl::Task FakeTaskWithEnqueueOrder(int enqueue_order) {
+ TaskQueueImpl::Task fake_task(
+ TaskQueue::PostedTask(base::BindOnce(&NopTask), FROM_HERE),
+ base::TimeTicks(), 0);
+ fake_task.set_enqueue_order(enqueue_order);
+ return fake_task;
+ }
+
+ TaskQueueImpl::Task FakeNonNestableTaskWithEnqueueOrder(int enqueue_order) {
+ TaskQueueImpl::Task fake_task(
+ TaskQueue::PostedTask(base::BindOnce(&NopTask), FROM_HERE),
+ base::TimeTicks(), 0);
+ fake_task.set_enqueue_order(enqueue_order);
+ fake_task.nestable = base::Nestable::kNonNestable;
+ return fake_task;
+ }
+
+ std::unique_ptr<RealTimeDomain> time_domain_;
+ std::unique_ptr<TaskQueueImpl> task_queue_;
+ std::unique_ptr<WorkQueue> work_queue_;
+ std::unique_ptr<WorkQueueSets> work_queue_sets_;
+ std::unique_ptr<TaskQueueImpl::TaskDeque> incoming_queue_;
+};
+
+TEST_F(WorkQueueTest, Empty) {
+ EXPECT_TRUE(work_queue_->Empty());
+ work_queue_->Push(FakeTaskWithEnqueueOrder(1));
+ EXPECT_FALSE(work_queue_->Empty());
+}
+
+TEST_F(WorkQueueTest, Empty_IgnoresFences) {
+ work_queue_->Push(FakeTaskWithEnqueueOrder(1));
+ work_queue_->InsertFence(1);
+ EXPECT_FALSE(work_queue_->Empty());
+}
+
+TEST_F(WorkQueueTest, GetFrontTaskEnqueueOrderQueueEmpty) {
+ EnqueueOrder enqueue_order;
+ EXPECT_FALSE(work_queue_->GetFrontTaskEnqueueOrder(&enqueue_order));
+}
+
+TEST_F(WorkQueueTest, GetFrontTaskEnqueueOrder) {
+ work_queue_->Push(FakeTaskWithEnqueueOrder(2));
+ work_queue_->Push(FakeTaskWithEnqueueOrder(3));
+ work_queue_->Push(FakeTaskWithEnqueueOrder(4));
+
+ EnqueueOrder enqueue_order;
+ EXPECT_TRUE(work_queue_->GetFrontTaskEnqueueOrder(&enqueue_order));
+ EXPECT_EQ(2ull, enqueue_order);
+}
+
+TEST_F(WorkQueueTest, GetFrontTaskQueueEmpty) {
+ EXPECT_EQ(nullptr, work_queue_->GetFrontTask());
+}
+
+TEST_F(WorkQueueTest, GetFrontTask) {
+ work_queue_->Push(FakeTaskWithEnqueueOrder(2));
+ work_queue_->Push(FakeTaskWithEnqueueOrder(3));
+ work_queue_->Push(FakeTaskWithEnqueueOrder(4));
+
+ ASSERT_NE(nullptr, work_queue_->GetFrontTask());
+ EXPECT_EQ(2ull, work_queue_->GetFrontTask()->enqueue_order());
+}
+
+TEST_F(WorkQueueTest, GetBackTask_Empty) {
+ EXPECT_EQ(nullptr, work_queue_->GetBackTask());
+}
+
+TEST_F(WorkQueueTest, GetBackTask) {
+ work_queue_->Push(FakeTaskWithEnqueueOrder(2));
+ work_queue_->Push(FakeTaskWithEnqueueOrder(3));
+ work_queue_->Push(FakeTaskWithEnqueueOrder(4));
+
+ ASSERT_NE(nullptr, work_queue_->GetBackTask());
+ EXPECT_EQ(4ull, work_queue_->GetBackTask()->enqueue_order());
+}
+
+TEST_F(WorkQueueTest, Push) {
+ WorkQueue* work_queue;
+ EXPECT_FALSE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue));
+
+ work_queue_->Push(FakeTaskWithEnqueueOrder(2));
+ EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue));
+ EXPECT_EQ(work_queue_.get(), work_queue);
+}
+
+TEST_F(WorkQueueTest, PushAfterFenceHit) {
+ work_queue_->InsertFence(1);
+ WorkQueue* work_queue;
+ EXPECT_FALSE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue));
+
+ work_queue_->Push(FakeTaskWithEnqueueOrder(2));
+ EXPECT_FALSE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue));
+}
+
+TEST_F(WorkQueueTest, PushNonNestableTaskToFront) {
+ WorkQueue* work_queue;
+ EXPECT_FALSE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue));
+
+ work_queue_->PushNonNestableTaskToFront(
+ FakeNonNestableTaskWithEnqueueOrder(3));
+ EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue));
+ EXPECT_EQ(work_queue_.get(), work_queue);
+
+ work_queue_->PushNonNestableTaskToFront(
+ FakeNonNestableTaskWithEnqueueOrder(2));
+
+ EXPECT_EQ(2ull, work_queue_->GetFrontTask()->enqueue_order());
+ EXPECT_EQ(3ull, work_queue_->GetBackTask()->enqueue_order());
+}
+
+TEST_F(WorkQueueTest, PushNonNestableTaskToFrontAfterFenceHit) {
+ work_queue_->InsertFence(1);
+ WorkQueue* work_queue;
+ EXPECT_FALSE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue));
+
+ work_queue_->PushNonNestableTaskToFront(
+ FakeNonNestableTaskWithEnqueueOrder(2));
+ EXPECT_FALSE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue));
+}
+
+TEST_F(WorkQueueTest, PushNonNestableTaskToFrontBeforeFenceHit) {
+ work_queue_->InsertFence(3);
+ WorkQueue* work_queue;
+ EXPECT_FALSE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue));
+
+ work_queue_->PushNonNestableTaskToFront(
+ FakeNonNestableTaskWithEnqueueOrder(2));
+ EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue));
+}
+
+TEST_F(WorkQueueTest, ReloadEmptyImmediateQueue) {
+ task_queue_->PushImmediateIncomingTaskForTest(FakeTaskWithEnqueueOrder(2));
+ task_queue_->PushImmediateIncomingTaskForTest(FakeTaskWithEnqueueOrder(3));
+ task_queue_->PushImmediateIncomingTaskForTest(FakeTaskWithEnqueueOrder(4));
+
+ WorkQueue* work_queue;
+ EXPECT_FALSE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue));
+ EXPECT_TRUE(work_queue_->Empty());
+ work_queue_->ReloadEmptyImmediateQueue();
+
+ EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue));
+ EXPECT_FALSE(work_queue_->Empty());
+
+ ASSERT_NE(nullptr, work_queue_->GetFrontTask());
+ EXPECT_EQ(2ull, work_queue_->GetFrontTask()->enqueue_order());
+
+ ASSERT_NE(nullptr, work_queue_->GetBackTask());
+ EXPECT_EQ(4ull, work_queue_->GetBackTask()->enqueue_order());
+}
+
+TEST_F(WorkQueueTest, ReloadEmptyImmediateQueueAfterFenceHit) {
+ work_queue_->InsertFence(1);
+ task_queue_->PushImmediateIncomingTaskForTest(FakeTaskWithEnqueueOrder(2));
+ task_queue_->PushImmediateIncomingTaskForTest(FakeTaskWithEnqueueOrder(3));
+ task_queue_->PushImmediateIncomingTaskForTest(FakeTaskWithEnqueueOrder(4));
+
+ WorkQueue* work_queue;
+ EXPECT_FALSE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue));
+ EXPECT_TRUE(work_queue_->Empty());
+ work_queue_->ReloadEmptyImmediateQueue();
+
+ EXPECT_FALSE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue));
+ EXPECT_FALSE(work_queue_->Empty());
+
+ ASSERT_NE(nullptr, work_queue_->GetFrontTask());
+ EXPECT_EQ(2ull, work_queue_->GetFrontTask()->enqueue_order());
+
+ ASSERT_NE(nullptr, work_queue_->GetBackTask());
+ EXPECT_EQ(4ull, work_queue_->GetBackTask()->enqueue_order());
+}
+
+TEST_F(WorkQueueTest, TakeTaskFromWorkQueue) {
+ work_queue_->Push(FakeTaskWithEnqueueOrder(2));
+ work_queue_->Push(FakeTaskWithEnqueueOrder(3));
+ work_queue_->Push(FakeTaskWithEnqueueOrder(4));
+
+ WorkQueue* work_queue;
+ EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue));
+ EXPECT_FALSE(work_queue_->Empty());
+
+ EXPECT_EQ(2ull, work_queue_->TakeTaskFromWorkQueue().enqueue_order());
+ EXPECT_EQ(3ull, work_queue_->TakeTaskFromWorkQueue().enqueue_order());
+ EXPECT_EQ(4ull, work_queue_->TakeTaskFromWorkQueue().enqueue_order());
+
+ EXPECT_FALSE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue));
+ EXPECT_TRUE(work_queue_->Empty());
+}
+
+TEST_F(WorkQueueTest, TakeTaskFromWorkQueue_HitFence) {
+ work_queue_->InsertFence(3);
+ work_queue_->Push(FakeTaskWithEnqueueOrder(2));
+ work_queue_->Push(FakeTaskWithEnqueueOrder(4));
+ EXPECT_FALSE(work_queue_->BlockedByFence());
+
+ WorkQueue* work_queue;
+ EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue));
+ EXPECT_FALSE(work_queue_->Empty());
+ EXPECT_FALSE(work_queue_->BlockedByFence());
+
+ EXPECT_EQ(2ull, work_queue_->TakeTaskFromWorkQueue().enqueue_order());
+ EXPECT_FALSE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue));
+ EXPECT_FALSE(work_queue_->Empty());
+ EXPECT_TRUE(work_queue_->BlockedByFence());
+}
+
+TEST_F(WorkQueueTest, InsertFenceBeforeEnqueueing) {
+ EXPECT_FALSE(work_queue_->InsertFence(1));
+ EXPECT_TRUE(work_queue_->BlockedByFence());
+
+ work_queue_->Push(FakeTaskWithEnqueueOrder(2));
+ work_queue_->Push(FakeTaskWithEnqueueOrder(3));
+ work_queue_->Push(FakeTaskWithEnqueueOrder(4));
+
+ EnqueueOrder enqueue_order;
+ EXPECT_FALSE(work_queue_->GetFrontTaskEnqueueOrder(&enqueue_order));
+}
+
+TEST_F(WorkQueueTest, InsertFenceAfterEnqueueingNonBlocking) {
+ work_queue_->Push(FakeTaskWithEnqueueOrder(2));
+ work_queue_->Push(FakeTaskWithEnqueueOrder(3));
+ work_queue_->Push(FakeTaskWithEnqueueOrder(4));
+
+ EXPECT_FALSE(work_queue_->InsertFence(5));
+ EXPECT_FALSE(work_queue_->BlockedByFence());
+
+ EnqueueOrder enqueue_order;
+ EXPECT_TRUE(work_queue_->GetFrontTaskEnqueueOrder(&enqueue_order));
+ EXPECT_EQ(2ull, work_queue_->TakeTaskFromWorkQueue().enqueue_order());
+}
+
+TEST_F(WorkQueueTest, InsertFenceAfterEnqueueing) {
+ work_queue_->Push(FakeTaskWithEnqueueOrder(2));
+ work_queue_->Push(FakeTaskWithEnqueueOrder(3));
+ work_queue_->Push(FakeTaskWithEnqueueOrder(4));
+
+ // NB in reality a fence will always be greater than any currently enqueued
+ // tasks.
+ EXPECT_FALSE(work_queue_->InsertFence(1));
+ EXPECT_TRUE(work_queue_->BlockedByFence());
+
+ EnqueueOrder enqueue_order;
+ EXPECT_FALSE(work_queue_->GetFrontTaskEnqueueOrder(&enqueue_order));
+}
+
+TEST_F(WorkQueueTest, InsertNewFence) {
+ work_queue_->Push(FakeTaskWithEnqueueOrder(2));
+ work_queue_->Push(FakeTaskWithEnqueueOrder(4));
+ work_queue_->Push(FakeTaskWithEnqueueOrder(5));
+
+ EXPECT_FALSE(work_queue_->InsertFence(3));
+ EXPECT_FALSE(work_queue_->BlockedByFence());
+
+ // Note until TakeTaskFromWorkQueue() is called we don't hit the fence.
+ EnqueueOrder enqueue_order;
+ EXPECT_TRUE(work_queue_->GetFrontTaskEnqueueOrder(&enqueue_order));
+ EXPECT_EQ(2ull, enqueue_order);
+
+ EXPECT_EQ(2ull, work_queue_->TakeTaskFromWorkQueue().enqueue_order());
+ EXPECT_FALSE(work_queue_->GetFrontTaskEnqueueOrder(&enqueue_order));
+ EXPECT_TRUE(work_queue_->BlockedByFence());
+
+ // Inserting the new fence should temporarily unblock the queue until the new
+ // one is hit.
+ EXPECT_TRUE(work_queue_->InsertFence(6));
+ EXPECT_FALSE(work_queue_->BlockedByFence());
+
+ EXPECT_TRUE(work_queue_->GetFrontTaskEnqueueOrder(&enqueue_order));
+ EXPECT_EQ(4ull, enqueue_order);
+ EXPECT_EQ(4ull, work_queue_->TakeTaskFromWorkQueue().enqueue_order());
+ EXPECT_TRUE(work_queue_->GetFrontTaskEnqueueOrder(&enqueue_order));
+ EXPECT_FALSE(work_queue_->BlockedByFence());
+}
+
+TEST_F(WorkQueueTest, PushWithNonEmptyQueueDoesNotHitFence) {
+ work_queue_->Push(FakeTaskWithEnqueueOrder(1));
+ EXPECT_FALSE(work_queue_->InsertFence(2));
+ work_queue_->Push(FakeTaskWithEnqueueOrder(3));
+ EXPECT_FALSE(work_queue_->BlockedByFence());
+}
+
+TEST_F(WorkQueueTest, RemoveFence) {
+ work_queue_->Push(FakeTaskWithEnqueueOrder(2));
+ work_queue_->Push(FakeTaskWithEnqueueOrder(4));
+ work_queue_->Push(FakeTaskWithEnqueueOrder(5));
+ work_queue_->InsertFence(3);
+
+ WorkQueue* work_queue;
+ EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue));
+ EXPECT_FALSE(work_queue_->Empty());
+
+ EXPECT_EQ(2ull, work_queue_->TakeTaskFromWorkQueue().enqueue_order());
+ EXPECT_FALSE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue));
+ EXPECT_FALSE(work_queue_->Empty());
+ EXPECT_TRUE(work_queue_->BlockedByFence());
+
+ EXPECT_TRUE(work_queue_->RemoveFence());
+ EXPECT_EQ(4ull, work_queue_->TakeTaskFromWorkQueue().enqueue_order());
+ EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue));
+ EXPECT_FALSE(work_queue_->BlockedByFence());
+}
+
+TEST_F(WorkQueueTest, RemoveFenceButNoFence) {
+ EXPECT_FALSE(work_queue_->RemoveFence());
+}
+
+TEST_F(WorkQueueTest, RemoveFenceNothingUnblocked) {
+ EXPECT_FALSE(work_queue_->InsertFence(1));
+ EXPECT_TRUE(work_queue_->BlockedByFence());
+
+ EXPECT_FALSE(work_queue_->RemoveFence());
+ EXPECT_FALSE(work_queue_->BlockedByFence());
+}
+
+TEST_F(WorkQueueTest, BlockedByFence) {
+ EXPECT_FALSE(work_queue_->BlockedByFence());
+ EXPECT_FALSE(work_queue_->InsertFence(1));
+ EXPECT_TRUE(work_queue_->BlockedByFence());
+}
+
+TEST_F(WorkQueueTest, BlockedByFencePopBecomesEmpty) {
+ work_queue_->Push(FakeTaskWithEnqueueOrder(1));
+ EXPECT_FALSE(work_queue_->InsertFence(2));
+ EXPECT_FALSE(work_queue_->BlockedByFence());
+
+ EXPECT_EQ(1ull, work_queue_->TakeTaskFromWorkQueue().enqueue_order());
+ EXPECT_TRUE(work_queue_->BlockedByFence());
+}
+
+TEST_F(WorkQueueTest, BlockedByFencePop) {
+ work_queue_->Push(FakeTaskWithEnqueueOrder(1));
+ EXPECT_FALSE(work_queue_->InsertFence(2));
+ EXPECT_FALSE(work_queue_->BlockedByFence());
+
+ work_queue_->Push(FakeTaskWithEnqueueOrder(3));
+ EXPECT_FALSE(work_queue_->BlockedByFence());
+
+ EXPECT_EQ(1ull, work_queue_->TakeTaskFromWorkQueue().enqueue_order());
+ EXPECT_TRUE(work_queue_->BlockedByFence());
+}
+
+TEST_F(WorkQueueTest, InitiallyEmptyBlockedByFenceNewFenceUnblocks) {
+ EXPECT_FALSE(work_queue_->InsertFence(1));
+ EXPECT_TRUE(work_queue_->BlockedByFence());
+
+ work_queue_->Push(FakeTaskWithEnqueueOrder(2));
+ EXPECT_TRUE(work_queue_->InsertFence(3));
+ EXPECT_FALSE(work_queue_->BlockedByFence());
+}
+
+TEST_F(WorkQueueTest, BlockedByFenceNewFenceUnblocks) {
+ work_queue_->Push(FakeTaskWithEnqueueOrder(1));
+ EXPECT_FALSE(work_queue_->InsertFence(2));
+ EXPECT_FALSE(work_queue_->BlockedByFence());
+
+ work_queue_->Push(FakeTaskWithEnqueueOrder(3));
+ EXPECT_FALSE(work_queue_->BlockedByFence());
+
+ EXPECT_EQ(1ull, work_queue_->TakeTaskFromWorkQueue().enqueue_order());
+ EXPECT_TRUE(work_queue_->BlockedByFence());
+
+ EXPECT_TRUE(work_queue_->InsertFence(4));
+ EXPECT_FALSE(work_queue_->BlockedByFence());
+}
+
+TEST_F(WorkQueueTest, InsertFenceAfterEnqueuing) {
+ work_queue_->Push(FakeTaskWithEnqueueOrder(2));
+ work_queue_->Push(FakeTaskWithEnqueueOrder(3));
+ work_queue_->Push(FakeTaskWithEnqueueOrder(4));
+ EXPECT_FALSE(work_queue_->BlockedByFence());
+
+ EXPECT_FALSE(work_queue_->InsertFence(1));
+ EXPECT_TRUE(work_queue_->BlockedByFence());
+
+ EnqueueOrder enqueue_order;
+ EXPECT_FALSE(work_queue_->GetFrontTaskEnqueueOrder(&enqueue_order));
+}
+
+TEST_F(WorkQueueTest, RemoveAllCanceledTasksFromFront) {
+ {
+ Cancelable cancelable;
+ work_queue_->Push(FakeCancelableTaskWithEnqueueOrder(
+ 2, cancelable.weak_ptr_factory.GetWeakPtr()));
+ work_queue_->Push(FakeCancelableTaskWithEnqueueOrder(
+ 3, cancelable.weak_ptr_factory.GetWeakPtr()));
+ work_queue_->Push(FakeCancelableTaskWithEnqueueOrder(
+ 4, cancelable.weak_ptr_factory.GetWeakPtr()));
+ work_queue_->Push(FakeTaskWithEnqueueOrder(5));
+ }
+ EXPECT_TRUE(work_queue_->RemoveAllCanceledTasksFromFront());
+
+ EnqueueOrder enqueue_order;
+ EXPECT_TRUE(work_queue_->GetFrontTaskEnqueueOrder(&enqueue_order));
+ EXPECT_EQ(5ull, enqueue_order);
+}
+
+TEST_F(WorkQueueTest, RemoveAllCanceledTasksFromFrontTasksNotCanceled) {
+ {
+ Cancelable cancelable;
+ work_queue_->Push(FakeCancelableTaskWithEnqueueOrder(
+ 2, cancelable.weak_ptr_factory.GetWeakPtr()));
+ work_queue_->Push(FakeCancelableTaskWithEnqueueOrder(
+ 3, cancelable.weak_ptr_factory.GetWeakPtr()));
+ work_queue_->Push(FakeCancelableTaskWithEnqueueOrder(
+ 4, cancelable.weak_ptr_factory.GetWeakPtr()));
+ work_queue_->Push(FakeTaskWithEnqueueOrder(5));
+ EXPECT_FALSE(work_queue_->RemoveAllCanceledTasksFromFront());
+
+ EnqueueOrder enqueue_order;
+ EXPECT_TRUE(work_queue_->GetFrontTaskEnqueueOrder(&enqueue_order));
+ EXPECT_EQ(2ull, enqueue_order);
+ }
+}
+
+} // namespace internal
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/DEPS b/chromium/third_party/blink/renderer/platform/scheduler/child/DEPS
new file mode 100644
index 00000000000..aadaadfbcab
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/DEPS
@@ -0,0 +1,10 @@
+include_rules = [
+ "+third_party/blink/public/platform/scheduler/base",
+ "+third_party/blink/renderer/platform/scheduler/base",
+]
+
+specific_include_rules = {
+ ".*test\.cc": [
+ "+components/viz/test",
+ ],
+}
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/OWNERS b/chromium/third_party/blink/renderer/platform/scheduler/child/OWNERS
new file mode 100644
index 00000000000..8423b497efb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/OWNERS
@@ -0,0 +1,7 @@
+altimin@chromium.org
+alexclarke@chromium.org
+rmcilroy@chromium.org
+skyostil@chromium.org
+
+# TEAM: scheduler-dev@chromium.org
+# COMPONENT: Blink>Scheduling
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/cancelable_closure_holder.cc b/chromium/third_party/blink/renderer/platform/scheduler/child/cancelable_closure_holder.cc
new file mode 100644
index 00000000000..9264b364afe
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/cancelable_closure_holder.cc
@@ -0,0 +1,30 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/child/cancelable_closure_holder.h"
+
+namespace blink {
+namespace scheduler {
+
+CancelableClosureHolder::CancelableClosureHolder() = default;
+
+CancelableClosureHolder::~CancelableClosureHolder() = default;
+
+void CancelableClosureHolder::Reset(const base::Closure& callback) {
+ callback_ = callback;
+ cancelable_callback_.Reset(callback_);
+}
+
+void CancelableClosureHolder::Cancel() {
+ DCHECK(!callback_.is_null());
+ cancelable_callback_.Reset(callback_);
+}
+
+base::Closure CancelableClosureHolder::GetCallback() const {
+ DCHECK(!callback_.is_null());
+ return cancelable_callback_.callback();
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/cancelable_closure_holder.h b/chromium/third_party/blink/renderer/platform/scheduler/child/cancelable_closure_holder.h
new file mode 100644
index 00000000000..a8b484c3ebe
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/cancelable_closure_holder.h
@@ -0,0 +1,42 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_CANCELABLE_CLOSURE_HOLDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_CANCELABLE_CLOSURE_HOLDER_H_
+
+#include "base/cancelable_callback.h"
+#include "base/macros.h"
+
+namespace blink {
+namespace scheduler {
+
+// A CancelableClosureHolder is a CancelableCallback which resets its wrapped
+// callback with a cached closure whenever it is canceled.
+class CancelableClosureHolder {
+ public:
+ CancelableClosureHolder();
+ ~CancelableClosureHolder();
+
+ // Resets the closure to be wrapped by the cancelable callback. Cancels any
+ // outstanding callbacks.
+ void Reset(const base::Closure& callback);
+
+ // Cancels any outstanding closures returned by callback().
+ void Cancel();
+
+ // Returns a callback that will be disabled by calling Cancel(). Callback
+ // must have been set using Reset() before calling this function.
+ base::Closure GetCallback() const;
+
+ private:
+ base::Closure callback_;
+ base::CancelableClosure cancelable_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(CancelableClosureHolder);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_CANCELABLE_CLOSURE_HOLDER_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/compositor_metrics_helper.cc b/chromium/third_party/blink/renderer/platform/scheduler/child/compositor_metrics_helper.cc
new file mode 100644
index 00000000000..37757604c31
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/compositor_metrics_helper.cc
@@ -0,0 +1,29 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/child/compositor_metrics_helper.h"
+
+namespace blink {
+namespace scheduler {
+
+CompositorMetricsHelper::CompositorMetricsHelper()
+ : MetricsHelper(WebThreadType::kCompositorThread) {}
+
+CompositorMetricsHelper::~CompositorMetricsHelper() {}
+
+void CompositorMetricsHelper::RecordTaskMetrics(
+ WorkerTaskQueue* queue,
+ const TaskQueue::Task& task,
+ base::TimeTicks start_time,
+ base::TimeTicks end_time,
+ base::Optional<base::TimeDelta> thread_time) {
+ if (ShouldDiscardTask(queue, task, start_time, end_time, thread_time))
+ return;
+
+ MetricsHelper::RecordCommonTaskMetrics(queue, task, start_time, end_time,
+ thread_time);
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/compositor_metrics_helper.h b/chromium/third_party/blink/renderer/platform/scheduler/child/compositor_metrics_helper.h
new file mode 100644
index 00000000000..a758e587cfd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/compositor_metrics_helper.h
@@ -0,0 +1,32 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_COMPOSITOR_METRICS_HELPER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_COMPOSITOR_METRICS_HELPER_H_
+
+#include "third_party/blink/renderer/platform/scheduler/child/metrics_helper.h"
+#include "third_party/blink/renderer/platform/scheduler/child/worker_task_queue.h"
+
+namespace blink {
+namespace scheduler {
+
+class PLATFORM_EXPORT CompositorMetricsHelper : public MetricsHelper {
+ public:
+ CompositorMetricsHelper();
+ ~CompositorMetricsHelper();
+
+ void RecordTaskMetrics(WorkerTaskQueue* queue,
+ const TaskQueue::Task& task,
+ base::TimeTicks start_time,
+ base::TimeTicks end_time,
+ base::Optional<base::TimeDelta> thread_time);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CompositorMetricsHelper);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_COMPOSITOR_METRICS_HELPER_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/default_params.h b/chromium/third_party/blink/renderer/platform/scheduler/child/default_params.h
new file mode 100644
index 00000000000..0db12d608d6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/default_params.h
@@ -0,0 +1,19 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_DEFAULT_PARAMS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_DEFAULT_PARAMS_H_
+
+#include "third_party/blink/renderer/platform/scheduler/child/page_visibility_state.h"
+
+namespace blink {
+namespace scheduler {
+
+constexpr PageVisibilityState kDefaultPageVisibility =
+ PageVisibilityState::kVisible;
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_DEFAULT_PARAMS_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/features.h b/chromium/third_party/blink/renderer/platform/scheduler/child/features.h
new file mode 100644
index 00000000000..a2951a685a7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/features.h
@@ -0,0 +1,19 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_FEATURES_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_FEATURES_H_
+
+#include "base/feature_list.h"
+
+namespace blink {
+namespace scheduler {
+
+const base::Feature kHighPriorityInput{"BlinkSchedulerHighPriorityInput",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_FEATURES_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/idle_canceled_delayed_task_sweeper.cc b/chromium/third_party/blink/renderer/platform/scheduler/child/idle_canceled_delayed_task_sweeper.cc
new file mode 100644
index 00000000000..2d6a8164915
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/idle_canceled_delayed_task_sweeper.cc
@@ -0,0 +1,40 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/child/idle_canceled_delayed_task_sweeper.h"
+
+#include "base/bind.h"
+
+namespace blink {
+namespace scheduler {
+
+namespace {
+const int kDelayedTaskSweepIntervalSeconds = 30;
+}
+
+IdleCanceledDelayedTaskSweeper::IdleCanceledDelayedTaskSweeper(
+ SchedulerHelper* scheduler_helper,
+ scoped_refptr<SingleThreadIdleTaskRunner> idle_task_runner)
+ : scheduler_helper_(scheduler_helper),
+ idle_task_runner_(idle_task_runner),
+ weak_factory_(this) {
+ PostIdleTask();
+}
+
+void IdleCanceledDelayedTaskSweeper::PostIdleTask() {
+ idle_task_runner_->PostDelayedIdleTask(
+ FROM_HERE, base::TimeDelta::FromSeconds(kDelayedTaskSweepIntervalSeconds),
+ base::BindOnce(&IdleCanceledDelayedTaskSweeper::SweepIdleTask,
+ weak_factory_.GetWeakPtr()));
+}
+
+void IdleCanceledDelayedTaskSweeper::SweepIdleTask(base::TimeTicks deadline) {
+ TRACE_EVENT0("renderer.scheduler",
+ "IdleCanceledDelayedTaskSweeper::SweepIdleTask");
+ scheduler_helper_->SweepCanceledDelayedTasks();
+ PostIdleTask();
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/idle_canceled_delayed_task_sweeper.h b/chromium/third_party/blink/renderer/platform/scheduler/child/idle_canceled_delayed_task_sweeper.h
new file mode 100644
index 00000000000..2e7da89d924
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/idle_canceled_delayed_task_sweeper.h
@@ -0,0 +1,38 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_IDLE_CANCELED_DELAYED_TASK_SWEEPER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_IDLE_CANCELED_DELAYED_TASK_SWEEPER_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "third_party/blink/public/platform/scheduler/single_thread_idle_task_runner.h"
+#include "third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h"
+
+namespace blink {
+namespace scheduler {
+
+// This class periodically sweeps away canceled delayed tasks, which helps
+// reduce memory consumption.
+class PLATFORM_EXPORT IdleCanceledDelayedTaskSweeper {
+ public:
+ IdleCanceledDelayedTaskSweeper(
+ SchedulerHelper* scheduler_helper,
+ scoped_refptr<SingleThreadIdleTaskRunner> idle_task_runner);
+
+ private:
+ void PostIdleTask();
+ void SweepIdleTask(base::TimeTicks deadline);
+
+ SchedulerHelper* scheduler_helper_; // NOT OWNED
+ scoped_refptr<SingleThreadIdleTaskRunner> idle_task_runner_;
+ base::WeakPtrFactory<IdleCanceledDelayedTaskSweeper> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(IdleCanceledDelayedTaskSweeper);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_IDLE_CANCELED_DELAYED_TASK_SWEEPER_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/idle_canceled_delayed_task_sweeper_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/child/idle_canceled_delayed_task_sweeper_unittest.cc
new file mode 100644
index 00000000000..776ca644891
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/idle_canceled_delayed_task_sweeper_unittest.cc
@@ -0,0 +1,132 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/child/idle_canceled_delayed_task_sweeper.h"
+
+#include "base/test/simple_test_tick_clock.h"
+#include "components/viz/test/ordered_simple_task_runner.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/scheduler/base/lazy_now.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue.h"
+#include "third_party/blink/renderer/platform/scheduler/child/idle_helper.h"
+#include "third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_helper.h"
+#include "third_party/blink/renderer/platform/scheduler/test/task_queue_manager_for_test.h"
+
+namespace blink {
+namespace scheduler {
+
+class TestClass {
+ public:
+ TestClass() : weak_factory_(this) {}
+
+ void NopTask() {}
+
+ base::WeakPtrFactory<TestClass> weak_factory_;
+};
+
+class IdleCanceledDelayedTaskSweeperTest : public testing::Test,
+ public IdleHelper::Delegate {
+ public:
+ IdleCanceledDelayedTaskSweeperTest()
+ : mock_task_runner_(new cc::OrderedSimpleTaskRunner(&clock_, true)),
+ scheduler_helper_(new MainThreadSchedulerHelper(
+ TaskQueueManagerForTest::Create(nullptr,
+ mock_task_runner_,
+ &clock_),
+ nullptr)),
+ idle_helper_(
+ new IdleHelper(scheduler_helper_.get(),
+ this,
+ "test",
+ base::TimeDelta::FromSeconds(30),
+ scheduler_helper_->NewTaskQueue(
+ MainThreadTaskQueue::QueueCreationParams(
+ MainThreadTaskQueue::QueueType::kTest)))),
+ idle_canceled_delayed_taks_sweeper_(
+ new IdleCanceledDelayedTaskSweeper(scheduler_helper_.get(),
+ idle_helper_->IdleTaskRunner())),
+ default_task_queue_(scheduler_helper_->DefaultMainThreadTaskQueue()) {
+ clock_.Advance(base::TimeDelta::FromMicroseconds(5000));
+ }
+
+ ~IdleCanceledDelayedTaskSweeperTest() override = default;
+
+ void TearDown() override {
+ // Check that all tests stop posting tasks.
+ mock_task_runner_->SetAutoAdvanceNowToPendingTasks(true);
+ while (mock_task_runner_->RunUntilIdle()) {
+ }
+ }
+
+ // IdleHelper::Delegate implementation:
+ bool CanEnterLongIdlePeriod(
+ base::TimeTicks now,
+ base::TimeDelta* next_long_idle_period_delay_out) override {
+ return true;
+ }
+ void IsNotQuiescent() override {}
+ void OnIdlePeriodStarted() override {}
+ void OnIdlePeriodEnded() override {}
+ void OnPendingTasksChanged(bool has_tasks) {}
+
+ protected:
+ base::SimpleTestTickClock clock_;
+ scoped_refptr<cc::OrderedSimpleTaskRunner> mock_task_runner_;
+
+ std::unique_ptr<MainThreadSchedulerHelper> scheduler_helper_;
+ std::unique_ptr<IdleHelper> idle_helper_;
+ std::unique_ptr<IdleCanceledDelayedTaskSweeper>
+ idle_canceled_delayed_taks_sweeper_;
+ scoped_refptr<TaskQueue> default_task_queue_;
+
+ DISALLOW_COPY_AND_ASSIGN(IdleCanceledDelayedTaskSweeperTest);
+};
+
+TEST_F(IdleCanceledDelayedTaskSweeperTest, TestSweep) {
+ TestClass class1;
+ TestClass class2;
+
+ // Post one task we won't cancel.
+ default_task_queue_->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&TestClass::NopTask, class1.weak_factory_.GetWeakPtr()),
+ base::TimeDelta::FromSeconds(100));
+
+ // And a bunch we will.
+ default_task_queue_->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&TestClass::NopTask, class2.weak_factory_.GetWeakPtr()),
+ base::TimeDelta::FromSeconds(101));
+
+ default_task_queue_->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&TestClass::NopTask, class2.weak_factory_.GetWeakPtr()),
+ base::TimeDelta::FromSeconds(102));
+
+ default_task_queue_->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&TestClass::NopTask, class2.weak_factory_.GetWeakPtr()),
+ base::TimeDelta::FromSeconds(103));
+
+ default_task_queue_->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&TestClass::NopTask, class2.weak_factory_.GetWeakPtr()),
+ base::TimeDelta::FromSeconds(104));
+
+ // Cancel the last four tasks.
+ class2.weak_factory_.InvalidateWeakPtrs();
+
+ // Give the IdleCanceledDelayedTaskSweeper a chance to run but don't let
+ // the first non canceled delayed task run. This is important because the
+ // canceled tasks would get removed by TaskQueueImpl::WakeUpForDelayedWork.
+ clock_.Advance(base::TimeDelta::FromSeconds(40));
+ idle_helper_->EnableLongIdlePeriod();
+ mock_task_runner_->RunForPeriod(base::TimeDelta::FromSeconds(40));
+
+ EXPECT_EQ(1u, default_task_queue_->GetNumberOfPendingTasks());
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/idle_helper.cc b/chromium/third_party/blink/renderer/platform/scheduler/child/idle_helper.cc
new file mode 100644
index 00000000000..56025c7c53f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/idle_helper.cc
@@ -0,0 +1,497 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/child/idle_helper.h"
+
+#include "base/time/time.h"
+#include "base/trace_event/trace_event.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "third_party/blink/renderer/platform/scheduler/base/real_time_domain.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_manager.h"
+#include "third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h"
+
+namespace blink {
+namespace scheduler {
+
+IdleHelper::IdleHelper(
+ SchedulerHelper* helper,
+ Delegate* delegate,
+ const char* idle_period_tracing_name,
+ base::TimeDelta required_quiescence_duration_before_long_idle_period,
+ scoped_refptr<TaskQueue> idle_queue)
+ : helper_(helper),
+ delegate_(delegate),
+ idle_queue_(std::move(idle_queue)),
+ state_(helper, delegate, idle_period_tracing_name),
+ required_quiescence_duration_before_long_idle_period_(
+ required_quiescence_duration_before_long_idle_period),
+ is_shutdown_(false),
+ weak_factory_(this) {
+ weak_idle_helper_ptr_ = weak_factory_.GetWeakPtr();
+ enable_next_long_idle_period_closure_.Reset(base::BindRepeating(
+ &IdleHelper::EnableLongIdlePeriod, weak_idle_helper_ptr_));
+ on_idle_task_posted_closure_.Reset(base::BindRepeating(
+ &IdleHelper::OnIdleTaskPostedOnMainThread, weak_idle_helper_ptr_));
+
+ idle_task_runner_ =
+ base::MakeRefCounted<SingleThreadIdleTaskRunner>(idle_queue_, this);
+
+ // This fence will block any idle tasks from running.
+ idle_queue_->InsertFence(TaskQueue::InsertFencePosition::kBeginningOfTime);
+ idle_queue_->SetQueuePriority(TaskQueue::kBestEffortPriority);
+}
+
+IdleHelper::~IdleHelper() {
+ Shutdown();
+}
+
+void IdleHelper::Shutdown() {
+ if (is_shutdown_)
+ return;
+
+ EndIdlePeriod();
+ is_shutdown_ = true;
+ weak_factory_.InvalidateWeakPtrs();
+ // Belt & braces, might not be needed.
+ idle_queue_->ShutdownTaskQueue();
+}
+
+IdleHelper::Delegate::Delegate() = default;
+
+IdleHelper::Delegate::~Delegate() = default;
+
+scoped_refptr<SingleThreadIdleTaskRunner> IdleHelper::IdleTaskRunner() {
+ helper_->CheckOnValidThread();
+ return idle_task_runner_;
+}
+
+IdleHelper::IdlePeriodState IdleHelper::ComputeNewLongIdlePeriodState(
+ const base::TimeTicks now,
+ base::TimeDelta* next_long_idle_period_delay_out) {
+ helper_->CheckOnValidThread();
+
+ if (!delegate_->CanEnterLongIdlePeriod(now,
+ next_long_idle_period_delay_out)) {
+ return IdlePeriodState::kNotInIdlePeriod;
+ }
+
+ base::TimeTicks next_pending_delayed_task;
+ base::TimeDelta max_long_idle_period_duration =
+ base::TimeDelta::FromMilliseconds(kMaximumIdlePeriodMillis);
+ base::TimeDelta long_idle_period_duration;
+ if (helper_->real_time_domain()->NextScheduledRunTime(
+ &next_pending_delayed_task)) {
+ // Limit the idle period duration to be before the next pending task.
+ long_idle_period_duration = std::min(next_pending_delayed_task - now,
+ max_long_idle_period_duration);
+ } else {
+ long_idle_period_duration = max_long_idle_period_duration;
+ }
+
+ if (long_idle_period_duration >=
+ base::TimeDelta::FromMilliseconds(kMinimumIdlePeriodDurationMillis)) {
+ *next_long_idle_period_delay_out = long_idle_period_duration;
+ if (!idle_queue_->HasTaskToRunImmediately())
+ return IdlePeriodState::kInLongIdlePeriodPaused;
+ if (long_idle_period_duration == max_long_idle_period_duration)
+ return IdlePeriodState::kInLongIdlePeriodWithMaxDeadline;
+ return IdlePeriodState::kInLongIdlePeriod;
+ } else {
+ // If we can't start the idle period yet then try again after wake-up.
+ *next_long_idle_period_delay_out = base::TimeDelta::FromMilliseconds(
+ kRetryEnableLongIdlePeriodDelayMillis);
+ return IdlePeriodState::kNotInIdlePeriod;
+ }
+}
+
+bool IdleHelper::ShouldWaitForQuiescence() {
+ helper_->CheckOnValidThread();
+
+ if (required_quiescence_duration_before_long_idle_period_ ==
+ base::TimeDelta()) {
+ return false;
+ }
+
+ bool system_is_quiescent = helper_->GetAndClearSystemIsQuiescentBit();
+ TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "ShouldWaitForQuiescence", "system_is_quiescent",
+ system_is_quiescent);
+ return !system_is_quiescent;
+}
+
+void IdleHelper::EnableLongIdlePeriod() {
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "EnableLongIdlePeriod");
+ helper_->CheckOnValidThread();
+ if (is_shutdown_)
+ return;
+
+ // End any previous idle period.
+ EndIdlePeriod();
+
+ if (ShouldWaitForQuiescence()) {
+ helper_->ControlTaskQueue()->PostDelayedTask(
+ FROM_HERE, enable_next_long_idle_period_closure_.GetCallback(),
+ required_quiescence_duration_before_long_idle_period_);
+ delegate_->IsNotQuiescent();
+ return;
+ }
+
+ base::TimeTicks now(helper_->NowTicks());
+ base::TimeDelta next_long_idle_period_delay;
+ IdlePeriodState new_idle_period_state =
+ ComputeNewLongIdlePeriodState(now, &next_long_idle_period_delay);
+ if (IsInIdlePeriod(new_idle_period_state)) {
+ StartIdlePeriod(new_idle_period_state, now,
+ now + next_long_idle_period_delay);
+ } else {
+ // Otherwise wait for the next long idle period delay before trying again.
+ helper_->ControlTaskQueue()->PostDelayedTask(
+ FROM_HERE, enable_next_long_idle_period_closure_.GetCallback(),
+ next_long_idle_period_delay);
+ }
+}
+
+void IdleHelper::StartIdlePeriod(IdlePeriodState new_state,
+ base::TimeTicks now,
+ base::TimeTicks idle_period_deadline) {
+ DCHECK(!is_shutdown_);
+ DCHECK_GT(idle_period_deadline, now);
+ helper_->CheckOnValidThread();
+ DCHECK(IsInIdlePeriod(new_state));
+
+ // Allow any ready delayed idle tasks to run.
+ idle_task_runner_->EnqueueReadyDelayedIdleTasks();
+
+ base::TimeDelta idle_period_duration(idle_period_deadline - now);
+ if (idle_period_duration <
+ base::TimeDelta::FromMilliseconds(kMinimumIdlePeriodDurationMillis)) {
+ TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "NotStartingIdlePeriodBecauseDeadlineIsTooClose",
+ "idle_period_duration_ms",
+ idle_period_duration.InMillisecondsF());
+ return;
+ }
+
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "StartIdlePeriod");
+ if (!IsInIdlePeriod(state_.idle_period_state()))
+ helper_->AddTaskObserver(this);
+
+ // Use a fence to make sure any idle tasks posted after this point do not run
+ // until the next idle period and unblock existing tasks.
+ idle_queue_->InsertFence(TaskQueue::InsertFencePosition::kNow);
+
+ state_.UpdateState(new_state, idle_period_deadline, now);
+}
+
+void IdleHelper::EndIdlePeriod() {
+ if (is_shutdown_)
+ return;
+
+ helper_->CheckOnValidThread();
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "EndIdlePeriod");
+
+ enable_next_long_idle_period_closure_.Cancel();
+ on_idle_task_posted_closure_.Cancel();
+
+ // If we weren't already within an idle period then early-out.
+ if (!IsInIdlePeriod(state_.idle_period_state()))
+ return;
+
+ helper_->RemoveTaskObserver(this);
+
+ // This fence will block any idle tasks from running.
+ idle_queue_->InsertFence(TaskQueue::InsertFencePosition::kBeginningOfTime);
+ state_.UpdateState(IdlePeriodState::kNotInIdlePeriod, base::TimeTicks(),
+ base::TimeTicks());
+}
+
+void IdleHelper::WillProcessTask(const base::PendingTask& pending_task) {
+ DCHECK(!is_shutdown_);
+}
+
+void IdleHelper::DidProcessTask(const base::PendingTask& pending_task) {
+ helper_->CheckOnValidThread();
+ DCHECK(!is_shutdown_);
+ DCHECK(IsInIdlePeriod(state_.idle_period_state()));
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "DidProcessTask");
+ if (state_.idle_period_state() != IdlePeriodState::kInLongIdlePeriodPaused &&
+ helper_->NowTicks() >= state_.idle_period_deadline()) {
+ // If the idle period deadline has now been reached, either end the idle
+ // period or trigger a new long-idle period.
+ if (IsInLongIdlePeriod(state_.idle_period_state())) {
+ EnableLongIdlePeriod();
+ } else {
+ DCHECK(IdlePeriodState::kInShortIdlePeriod == state_.idle_period_state());
+ EndIdlePeriod();
+ }
+ }
+}
+
+void IdleHelper::UpdateLongIdlePeriodStateAfterIdleTask() {
+ helper_->CheckOnValidThread();
+ DCHECK(!is_shutdown_);
+ DCHECK(IsInLongIdlePeriod(state_.idle_period_state()));
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "UpdateLongIdlePeriodStateAfterIdleTask");
+
+ if (!idle_queue_->HasTaskToRunImmediately()) {
+ // If there are no more idle tasks then pause long idle period ticks until a
+ // new idle task is posted.
+ state_.UpdateState(IdlePeriodState::kInLongIdlePeriodPaused,
+ state_.idle_period_deadline(), base::TimeTicks());
+ } else if (idle_queue_->BlockedByFence()) {
+ // If there is still idle work to do then just start the next idle period.
+ base::TimeDelta next_long_idle_period_delay;
+ // Ensure that we kick the scheduler at the right time to
+ // initiate the next idle period.
+ next_long_idle_period_delay = std::max(
+ base::TimeDelta(), state_.idle_period_deadline() - helper_->NowTicks());
+ if (next_long_idle_period_delay.is_zero()) {
+ EnableLongIdlePeriod();
+ } else {
+ helper_->ControlTaskQueue()->PostDelayedTask(
+ FROM_HERE, enable_next_long_idle_period_closure_.GetCallback(),
+ next_long_idle_period_delay);
+ }
+ }
+}
+
+base::TimeTicks IdleHelper::CurrentIdleTaskDeadline() const {
+ helper_->CheckOnValidThread();
+ return state_.idle_period_deadline();
+}
+
+void IdleHelper::OnIdleTaskPosted() {
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "OnIdleTaskPosted");
+ if (is_shutdown_)
+ return;
+ if (idle_task_runner_->RunsTasksInCurrentSequence()) {
+ OnIdleTaskPostedOnMainThread();
+ } else {
+ helper_->ControlTaskQueue()->PostTask(
+ FROM_HERE, on_idle_task_posted_closure_.GetCallback());
+ }
+}
+
+void IdleHelper::OnIdleTaskPostedOnMainThread() {
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "OnIdleTaskPostedOnMainThread");
+ if (is_shutdown_)
+ return;
+ delegate_->OnPendingTasksChanged(true);
+ if (state_.idle_period_state() == IdlePeriodState::kInLongIdlePeriodPaused) {
+ // Restart long idle period ticks.
+ helper_->ControlTaskQueue()->PostTask(
+ FROM_HERE, enable_next_long_idle_period_closure_.GetCallback());
+ }
+}
+
+base::TimeTicks IdleHelper::WillProcessIdleTask() {
+ helper_->CheckOnValidThread();
+ DCHECK(!is_shutdown_);
+ state_.TraceIdleIdleTaskStart();
+ return CurrentIdleTaskDeadline();
+}
+
+void IdleHelper::DidProcessIdleTask() {
+ helper_->CheckOnValidThread();
+ if (is_shutdown_)
+ return;
+ state_.TraceIdleIdleTaskEnd();
+ if (IsInLongIdlePeriod(state_.idle_period_state())) {
+ UpdateLongIdlePeriodStateAfterIdleTask();
+ }
+ delegate_->OnPendingTasksChanged(idle_queue_->GetNumberOfPendingTasks() > 0);
+}
+
+base::TimeTicks IdleHelper::NowTicks() {
+ return helper_->NowTicks();
+}
+
+// static
+bool IdleHelper::IsInIdlePeriod(IdlePeriodState state) {
+ return state != IdlePeriodState::kNotInIdlePeriod;
+}
+
+// static
+bool IdleHelper::IsInLongIdlePeriod(IdlePeriodState state) {
+ return state == IdlePeriodState::kInLongIdlePeriod ||
+ state == IdlePeriodState::kInLongIdlePeriodWithMaxDeadline ||
+ state == IdlePeriodState::kInLongIdlePeriodPaused;
+}
+
+bool IdleHelper::CanExceedIdleDeadlineIfRequired() const {
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "CanExceedIdleDeadlineIfRequired");
+ helper_->CheckOnValidThread();
+ return state_.idle_period_state() ==
+ IdlePeriodState::kInLongIdlePeriodWithMaxDeadline;
+}
+
+IdleHelper::IdlePeriodState IdleHelper::SchedulerIdlePeriodState() const {
+ return state_.idle_period_state();
+}
+
+IdleHelper::State::State(SchedulerHelper* helper,
+ Delegate* delegate,
+ const char* idle_period_tracing_name)
+ : helper_(helper),
+ delegate_(delegate),
+ idle_period_state_(IdlePeriodState::kNotInIdlePeriod),
+ idle_period_trace_event_started_(false),
+ running_idle_task_for_tracing_(false),
+ idle_period_tracing_name_(idle_period_tracing_name) {}
+
+IdleHelper::State::~State() = default;
+
+IdleHelper::IdlePeriodState IdleHelper::State::idle_period_state() const {
+ helper_->CheckOnValidThread();
+ return idle_period_state_;
+}
+
+base::TimeTicks IdleHelper::State::idle_period_deadline() const {
+ helper_->CheckOnValidThread();
+ return idle_period_deadline_;
+}
+
+void IdleHelper::State::UpdateState(IdlePeriodState new_state,
+ base::TimeTicks new_deadline,
+ base::TimeTicks optional_now) {
+ IdlePeriodState old_idle_period_state = idle_period_state_;
+
+ helper_->CheckOnValidThread();
+ if (new_state == idle_period_state_) {
+ DCHECK_EQ(new_deadline, idle_period_deadline_);
+ return;
+ }
+
+ bool is_tracing;
+ TRACE_EVENT_CATEGORY_GROUP_ENABLED("renderer.scheduler", &is_tracing);
+ if (is_tracing) {
+ base::TimeTicks now(optional_now.is_null() ? helper_->NowTicks()
+ : optional_now);
+ TraceEventIdlePeriodStateChange(new_state, running_idle_task_for_tracing_,
+ new_deadline, now);
+ }
+
+ idle_period_state_ = new_state;
+ idle_period_deadline_ = new_deadline;
+
+ // Inform the delegate if we are starting or ending an idle period.
+ if (IsInIdlePeriod(new_state) && !IsInIdlePeriod(old_idle_period_state)) {
+ delegate_->OnIdlePeriodStarted();
+ } else if (!IsInIdlePeriod(new_state) &&
+ IsInIdlePeriod(old_idle_period_state)) {
+ delegate_->OnIdlePeriodEnded();
+ }
+}
+
+void IdleHelper::State::TraceIdleIdleTaskStart() {
+ helper_->CheckOnValidThread();
+
+ bool is_tracing;
+ TRACE_EVENT_CATEGORY_GROUP_ENABLED("renderer.scheduler", &is_tracing);
+ if (is_tracing) {
+ TraceEventIdlePeriodStateChange(idle_period_state_, true,
+ idle_period_deadline_,
+ base::TimeTicks::Now());
+ }
+}
+
+void IdleHelper::State::TraceIdleIdleTaskEnd() {
+ helper_->CheckOnValidThread();
+
+ bool is_tracing;
+ TRACE_EVENT_CATEGORY_GROUP_ENABLED("renderer.scheduler", &is_tracing);
+ if (is_tracing) {
+ TraceEventIdlePeriodStateChange(idle_period_state_, false,
+ idle_period_deadline_,
+ base::TimeTicks::Now());
+ }
+}
+
+void IdleHelper::State::TraceEventIdlePeriodStateChange(
+ IdlePeriodState new_state,
+ bool new_running_idle_task,
+ base::TimeTicks new_deadline,
+ base::TimeTicks now) {
+ TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "SetIdlePeriodState", "old_state",
+ IdleHelper::IdlePeriodStateToString(idle_period_state_),
+ "new_state", IdleHelper::IdlePeriodStateToString(new_state));
+
+ if (idle_period_trace_event_started_ && running_idle_task_for_tracing_ &&
+ !new_running_idle_task) {
+ running_idle_task_for_tracing_ = false;
+ if (!idle_period_deadline_.is_null() && now > idle_period_deadline_) {
+ TRACE_EVENT_ASYNC_STEP_INTO_WITH_TIMESTAMP0(
+ "renderer.scheduler", idle_period_tracing_name_, this,
+ "DeadlineOverrun",
+ std::max(idle_period_deadline_, last_idle_task_trace_time_));
+ }
+ }
+
+ if (IsInIdlePeriod(new_state)) {
+ if (!idle_period_trace_event_started_) {
+ idle_period_trace_event_started_ = true;
+ TRACE_EVENT_ASYNC_BEGIN1("renderer.scheduler", idle_period_tracing_name_,
+ this, "idle_period_length_ms",
+ (new_deadline - now).InMillisecondsF());
+ }
+
+ if (new_running_idle_task) {
+ last_idle_task_trace_time_ = now;
+ running_idle_task_for_tracing_ = true;
+ TRACE_EVENT_ASYNC_STEP_INTO0("renderer.scheduler",
+ idle_period_tracing_name_, this,
+ "RunningIdleTask");
+ } else if (new_state == IdlePeriodState::kInShortIdlePeriod) {
+ TRACE_EVENT_ASYNC_STEP_INTO0("renderer.scheduler",
+ idle_period_tracing_name_, this,
+ "ShortIdlePeriod");
+ } else if (IsInLongIdlePeriod(new_state) &&
+ new_state != IdlePeriodState::kInLongIdlePeriodPaused) {
+ TRACE_EVENT_ASYNC_STEP_INTO0("renderer.scheduler",
+ idle_period_tracing_name_, this,
+ "LongIdlePeriod");
+ } else if (new_state == IdlePeriodState::kInLongIdlePeriodPaused) {
+ TRACE_EVENT_ASYNC_STEP_INTO0("renderer.scheduler",
+ idle_period_tracing_name_, this,
+ "LongIdlePeriodPaused");
+ }
+ } else if (idle_period_trace_event_started_) {
+ idle_period_trace_event_started_ = false;
+ TRACE_EVENT_ASYNC_END0("renderer.scheduler", idle_period_tracing_name_,
+ this);
+ }
+}
+
+// static
+const char* IdleHelper::IdlePeriodStateToString(
+ IdlePeriodState idle_period_state) {
+ switch (idle_period_state) {
+ case IdlePeriodState::kNotInIdlePeriod:
+ return "not_in_idle_period";
+ case IdlePeriodState::kInShortIdlePeriod:
+ return "in_short_idle_period";
+ case IdlePeriodState::kInLongIdlePeriod:
+ return "in_long_idle_period";
+ case IdlePeriodState::kInLongIdlePeriodWithMaxDeadline:
+ return "in_long_idle_period_with_max_deadline";
+ case IdlePeriodState::kInLongIdlePeriodPaused:
+ return "in_long_idle_period_paused";
+ default:
+ NOTREACHED();
+ return nullptr;
+ }
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/idle_helper.h b/chromium/third_party/blink/renderer/platform/scheduler/child/idle_helper.h
new file mode 100644
index 00000000000..8f0ec9a79c5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/idle_helper.h
@@ -0,0 +1,249 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_IDLE_HELPER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_IDLE_HELPER_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "third_party/blink/public/platform/scheduler/single_thread_idle_task_runner.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_selector.h"
+#include "third_party/blink/renderer/platform/scheduler/child/cancelable_closure_holder.h"
+#include "third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h"
+
+namespace blink {
+namespace scheduler {
+namespace idle_helper_unittest {
+class BaseIdleHelperTest;
+class IdleHelperTest;
+} // namespace idle_helper_unittest
+
+class SchedulerHelper;
+
+// The job of the IdleHelper is to run idle tasks when the system is otherwise
+// idle. Idle tasks should be optional work, with no guarantee they will be run
+// at all. Idle tasks are subject to three levels of throttling:
+//
+// 1. Both idle queues are run a BEST_EFFORT priority (i.e. only selected if
+// there is nothing else to do.
+// 2. The idle queues are only enabled during an idle period.
+// 3. Idle tasks posted from within an idle task run in the next idle period.
+// This is achieved by inserting a fence into the queue.
+//
+// There are two types of idle periods:
+// 1. Short idle period - typically less than 10ms run after begin main frame
+// has finished, with the idle period ending at the compositor provided
+// deadline.
+// 2. Long idle periods - typically up to 50ms when no frames are being
+// produced.
+//
+// Idle tasks are supplied a deadline, and should endeavor to finished before it
+// ends to avoid jank.
+class PLATFORM_EXPORT IdleHelper : public base::MessageLoop::TaskObserver,
+ public SingleThreadIdleTaskRunner::Delegate {
+ public:
+ // Used to by scheduler implementations to customize idle behaviour.
+ class PLATFORM_EXPORT Delegate {
+ public:
+ Delegate();
+ virtual ~Delegate();
+
+ // If it's ok to enter a long idle period, return true. Otherwise return
+ // false and set next_long_idle_period_delay_out so we know when to try
+ // again.
+ virtual bool CanEnterLongIdlePeriod(
+ base::TimeTicks now,
+ base::TimeDelta* next_long_idle_period_delay_out) = 0;
+
+ // Signals that the Long Idle Period hasn't started yet because the system
+ // isn't quiescent.
+ virtual void IsNotQuiescent() = 0;
+
+ // Signals that we have started an Idle Period.
+ virtual void OnIdlePeriodStarted() = 0;
+
+ // Signals that we have finished an Idle Period.
+ virtual void OnIdlePeriodEnded() = 0;
+
+ // Signals that the task list has changed.
+ virtual void OnPendingTasksChanged(bool has_tasks) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Delegate);
+ };
+
+ // Keep IdleHelper::IdlePeriodStateToString in sync with this enum.
+ enum class IdlePeriodState {
+ kNotInIdlePeriod,
+ kInShortIdlePeriod,
+ kInLongIdlePeriod,
+ kInLongIdlePeriodWithMaxDeadline,
+ kInLongIdlePeriodPaused,
+ // Must be the last entry.
+ kIdlePeriodStateCount,
+ kFirstIdlePeriodState = kNotInIdlePeriod,
+ };
+
+ // The maximum length of an idle period.
+ static const int kMaximumIdlePeriodMillis = 50;
+
+ // |helper| and |delegate| are not owned by IdleHelper object and must
+ // outlive it.
+ IdleHelper(
+ SchedulerHelper* helper,
+ Delegate* delegate,
+ const char* idle_period_tracing_name,
+ base::TimeDelta required_quiescence_duration_before_long_idle_period,
+ scoped_refptr<TaskQueue> idle_queue);
+ ~IdleHelper() override;
+
+ // Prevents any further idle tasks from running.
+ void Shutdown();
+
+ // Returns the idle task runner. Tasks posted to this runner may be reordered
+ // relative to other task types and may be starved for an arbitrarily long
+ // time if no idle time is available.
+ scoped_refptr<SingleThreadIdleTaskRunner> IdleTaskRunner();
+
+ // If |required_quiescence_duration_before_long_idle_period_| is zero then
+ // immediately initiate a long idle period, otherwise check if any tasks have
+ // run recently and if so, check again after a delay of
+ // |required_quiescence_duration_before_long_idle_period_|.
+ // Calling this function will end any previous idle period immediately, and
+ // potentially again later if
+ // |required_quiescence_duration_before_long_idle_period_| is non-zero.
+ // NOTE EndIdlePeriod will disable the long idle periods.
+ void EnableLongIdlePeriod();
+
+ // Start an idle period with a given idle period deadline.
+ void StartIdlePeriod(IdlePeriodState new_idle_period_state,
+ base::TimeTicks now,
+ base::TimeTicks idle_period_deadline);
+
+ // This will end an idle period either started with StartIdlePeriod or
+ // EnableLongIdlePeriod.
+ void EndIdlePeriod();
+
+ // Returns true if a currently running idle task could exceed its deadline
+ // without impacting user experience too much. This should only be used if
+ // there is a task which cannot be pre-empted and is likely to take longer
+ // than the largest expected idle task deadline. It should NOT be polled to
+ // check whether more work can be performed on the current idle task after
+ // its deadline has expired - post a new idle task for the continuation of the
+ // work in this case.
+ // Must be called from the thread this class was created on.
+ bool CanExceedIdleDeadlineIfRequired() const;
+
+ // Returns the deadline for the current idle task.
+ base::TimeTicks CurrentIdleTaskDeadline() const;
+
+ // SingleThreadIdleTaskRunner::Delegate implementation:
+ void OnIdleTaskPosted() override;
+ base::TimeTicks WillProcessIdleTask() override;
+ void DidProcessIdleTask() override;
+ base::TimeTicks NowTicks() override;
+
+ // base::MessageLoop::TaskObserver implementation:
+ void WillProcessTask(const base::PendingTask& pending_task) override;
+ void DidProcessTask(const base::PendingTask& pending_task) override;
+
+ IdlePeriodState SchedulerIdlePeriodState() const;
+ static const char* IdlePeriodStateToString(IdlePeriodState state);
+
+ private:
+ friend class idle_helper_unittest::BaseIdleHelperTest;
+ friend class idle_helper_unittest::IdleHelperTest;
+
+ const scoped_refptr<TaskQueue>& idle_queue() const { return idle_queue_; }
+
+ class State {
+ public:
+ State(SchedulerHelper* helper,
+ Delegate* delegate,
+ const char* idle_period_tracing_name);
+ virtual ~State();
+
+ void UpdateState(IdlePeriodState new_state,
+ base::TimeTicks new_deadline,
+ base::TimeTicks optional_now);
+ bool IsIdlePeriodPaused() const;
+
+ IdlePeriodState idle_period_state() const;
+ base::TimeTicks idle_period_deadline() const;
+
+ void TraceIdleIdleTaskStart();
+ void TraceIdleIdleTaskEnd();
+
+ private:
+ void TraceEventIdlePeriodStateChange(IdlePeriodState new_state,
+ bool new_running_idle_task,
+ base::TimeTicks new_deadline,
+ base::TimeTicks optional_now);
+
+ SchedulerHelper* helper_; // NOT OWNED
+ Delegate* delegate_; // NOT OWNED
+
+ IdlePeriodState idle_period_state_;
+ base::TimeTicks idle_period_deadline_;
+
+ base::TimeTicks last_idle_task_trace_time_;
+ bool idle_period_trace_event_started_;
+ bool running_idle_task_for_tracing_;
+ const char* idle_period_tracing_name_;
+
+ DISALLOW_COPY_AND_ASSIGN(State);
+ };
+
+ // The minimum duration of an idle period.
+ static const int kMinimumIdlePeriodDurationMillis = 1;
+
+ // The minimum delay to wait between retrying to initiate a long idle time.
+ static const int kRetryEnableLongIdlePeriodDelayMillis = 1;
+
+ // Returns the new idle period state for the next long idle period. Fills in
+ // |next_long_idle_period_delay_out| with the next time we should try to
+ // initiate the next idle period.
+ IdlePeriodState ComputeNewLongIdlePeriodState(
+ const base::TimeTicks now,
+ base::TimeDelta* next_long_idle_period_delay_out);
+
+ bool ShouldWaitForQuiescence();
+ void OnIdleTaskPostedOnMainThread();
+ void UpdateLongIdlePeriodStateAfterIdleTask();
+
+ void SetIdlePeriodState(IdlePeriodState new_state,
+ base::TimeTicks new_deadline,
+ base::TimeTicks optional_now);
+
+ // Returns true if |state| represents being within an idle period state.
+ static bool IsInIdlePeriod(IdlePeriodState state);
+ // Returns true if |state| represents being within a long idle period state.
+ static bool IsInLongIdlePeriod(IdlePeriodState state);
+
+ SchedulerHelper* helper_; // NOT OWNED
+ Delegate* delegate_; // NOT OWNED
+ scoped_refptr<TaskQueue> idle_queue_;
+ scoped_refptr<SingleThreadIdleTaskRunner> idle_task_runner_;
+
+ CancelableClosureHolder enable_next_long_idle_period_closure_;
+ CancelableClosureHolder on_idle_task_posted_closure_;
+
+ State state_;
+
+ base::TimeDelta required_quiescence_duration_before_long_idle_period_;
+
+ bool is_shutdown_;
+
+ base::WeakPtr<IdleHelper> weak_idle_helper_ptr_;
+ base::WeakPtrFactory<IdleHelper> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(IdleHelper);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_IDLE_HELPER_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/idle_helper_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/child/idle_helper_unittest.cc
new file mode 100644
index 00000000000..98b4c60d9cc
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/idle_helper_unittest.cc
@@ -0,0 +1,1190 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/child/idle_helper.h"
+
+#include <utility>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "components/viz/test/ordered_simple_task_runner.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/scheduler/base/real_time_domain.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_manager.h"
+#include "third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h"
+#include "third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_helper.h"
+#include "third_party/blink/renderer/platform/scheduler/test/task_queue_manager_for_test.h"
+
+using testing::_;
+using testing::AnyNumber;
+using testing::AtLeast;
+using testing::Exactly;
+using testing::Invoke;
+using testing::Return;
+
+namespace blink {
+namespace scheduler {
+// To avoid symbol collisions in jumbo builds.
+namespace idle_helper_unittest {
+
+void AppendToVectorTestTask(std::vector<std::string>* vector,
+ std::string value) {
+ vector->push_back(value);
+}
+
+void AppendToVectorIdleTestTask(std::vector<std::string>* vector,
+ std::string value,
+ base::TimeTicks deadline) {
+ AppendToVectorTestTask(vector, value);
+}
+
+void NullTask() {}
+
+void NullIdleTask(base::TimeTicks deadline) {}
+
+void AppendToVectorReentrantTask(base::SingleThreadTaskRunner* task_runner,
+ std::vector<int>* vector,
+ int* reentrant_count,
+ int max_reentrant_count) {
+ vector->push_back((*reentrant_count)++);
+ if (*reentrant_count < max_reentrant_count) {
+ task_runner->PostTask(FROM_HERE,
+ base::BindOnce(AppendToVectorReentrantTask,
+ base::Unretained(task_runner), vector,
+ reentrant_count, max_reentrant_count));
+ }
+}
+
+void IdleTestTask(int* run_count,
+ base::TimeTicks* deadline_out,
+ base::TimeTicks deadline) {
+ (*run_count)++;
+ *deadline_out = deadline;
+}
+
+int g_max_idle_task_reposts = 2;
+
+void RepostingIdleTestTask(SingleThreadIdleTaskRunner* idle_task_runner,
+ int* run_count,
+ base::TimeTicks* deadline_out,
+ base::TimeTicks deadline) {
+ if ((*run_count + 1) < g_max_idle_task_reposts) {
+ idle_task_runner->PostIdleTask(
+ FROM_HERE, base::BindOnce(&RepostingIdleTestTask,
+ base::Unretained(idle_task_runner), run_count,
+ deadline_out));
+ }
+ *deadline_out = deadline;
+ (*run_count)++;
+}
+
+void RepostingUpdateClockIdleTestTask(
+ SingleThreadIdleTaskRunner* idle_task_runner,
+ int* run_count,
+ base::SimpleTestTickClock* clock,
+ base::TimeDelta advance_time,
+ std::vector<base::TimeTicks>* deadlines,
+ base::TimeTicks deadline) {
+ if ((*run_count + 1) < g_max_idle_task_reposts) {
+ idle_task_runner->PostIdleTask(
+ FROM_HERE, base::BindOnce(&RepostingUpdateClockIdleTestTask,
+ base::Unretained(idle_task_runner), run_count,
+ clock, advance_time, deadlines));
+ }
+ deadlines->push_back(deadline);
+ (*run_count)++;
+ clock->Advance(advance_time);
+}
+
+void RepeatingTask(base::SingleThreadTaskRunner* task_runner,
+ int num_repeats,
+ base::TimeDelta delay) {
+ if (num_repeats > 1) {
+ task_runner->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&RepeatingTask, base::Unretained(task_runner),
+ num_repeats - 1, delay),
+ delay);
+ }
+}
+
+void UpdateClockIdleTestTask(base::SimpleTestTickClock* clock,
+ int* run_count,
+ base::TimeTicks set_time,
+ base::TimeTicks deadline) {
+ clock->Advance(set_time - clock->NowTicks());
+ (*run_count)++;
+}
+
+void UpdateClockToDeadlineIdleTestTask(base::SimpleTestTickClock* clock,
+ int* run_count,
+ base::TimeTicks deadline) {
+ UpdateClockIdleTestTask(clock, run_count, deadline, deadline);
+}
+
+void EndIdlePeriodIdleTask(IdleHelper* idle_helper, base::TimeTicks deadline) {
+ idle_helper->EndIdlePeriod();
+}
+
+void ShutdownIdleTask(IdleHelper* helper,
+ bool* shutdown_task_run,
+ base::TimeTicks deadline) {
+ *shutdown_task_run = true;
+ helper->Shutdown();
+}
+
+// RAII helper class to enable auto advancing of time inside mock task runner.
+// Automatically disables auto-advancement when destroyed.
+class ScopedAutoAdvanceNowEnabler {
+ public:
+ ScopedAutoAdvanceNowEnabler(
+ scoped_refptr<cc::OrderedSimpleTaskRunner> task_runner)
+ : task_runner_(task_runner) {
+ if (task_runner_)
+ task_runner_->SetAutoAdvanceNowToPendingTasks(true);
+ }
+
+ ~ScopedAutoAdvanceNowEnabler() {
+ if (task_runner_)
+ task_runner_->SetAutoAdvanceNowToPendingTasks(false);
+ }
+
+ private:
+ scoped_refptr<cc::OrderedSimpleTaskRunner> task_runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedAutoAdvanceNowEnabler);
+};
+
+class IdleHelperForTest : public IdleHelper, public IdleHelper::Delegate {
+ public:
+ explicit IdleHelperForTest(
+ SchedulerHelper* scheduler_helper,
+ base::TimeDelta required_quiescence_duration_before_long_idle_period,
+ scoped_refptr<TaskQueue> idle_task_runner)
+ : IdleHelper(scheduler_helper,
+ this,
+ "TestSchedulerIdlePeriod",
+ required_quiescence_duration_before_long_idle_period,
+ idle_task_runner) {}
+
+ ~IdleHelperForTest() override = default;
+
+ // IdleHelper::Delegate implementation:
+ MOCK_METHOD2(CanEnterLongIdlePeriod,
+ bool(base::TimeTicks now,
+ base::TimeDelta* next_long_idle_period_delay_out));
+
+ MOCK_METHOD0(IsNotQuiescent, void());
+ MOCK_METHOD0(OnIdlePeriodStarted, void());
+ MOCK_METHOD0(OnIdlePeriodEnded, void());
+ MOCK_METHOD1(OnPendingTasksChanged, void(bool has_tasks));
+};
+
+class BaseIdleHelperTest : public testing::Test {
+ public:
+ BaseIdleHelperTest(
+ base::MessageLoop* message_loop,
+ base::TimeDelta required_quiescence_duration_before_long_idle_period)
+ : mock_task_runner_(
+ message_loop ? nullptr
+ : new cc::OrderedSimpleTaskRunner(&clock_, false)),
+ message_loop_(message_loop) {
+ std::unique_ptr<TaskQueueManager> task_queue_manager =
+ TaskQueueManagerForTest::Create(
+ message_loop,
+ message_loop ? message_loop->task_runner() : mock_task_runner_,
+ &clock_);
+ task_queue_manager_ = task_queue_manager.get();
+ scheduler_helper_ = std::make_unique<WorkerSchedulerHelper>(
+ std::move(task_queue_manager), nullptr);
+ idle_helper_ = std::make_unique<IdleHelperForTest>(
+ scheduler_helper_.get(),
+ required_quiescence_duration_before_long_idle_period,
+ scheduler_helper_->NewTaskQueue(TaskQueue::Spec("idle_test")));
+ default_task_runner_ = scheduler_helper_->DefaultWorkerTaskQueue();
+ idle_task_runner_ = idle_helper_->IdleTaskRunner();
+ clock_.Advance(base::TimeDelta::FromMicroseconds(5000));
+ }
+
+ ~BaseIdleHelperTest() override = default;
+
+ void SetUp() override {
+ EXPECT_CALL(*idle_helper_, OnIdlePeriodStarted()).Times(AnyNumber());
+ EXPECT_CALL(*idle_helper_, OnIdlePeriodEnded()).Times(AnyNumber());
+ EXPECT_CALL(*idle_helper_, CanEnterLongIdlePeriod(_, _))
+ .Times(AnyNumber())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*idle_helper_, OnPendingTasksChanged(_)).Times(AnyNumber());
+ }
+
+ void TearDown() override {
+ EXPECT_CALL(*idle_helper_, OnIdlePeriodEnded()).Times(AnyNumber());
+ idle_helper_->Shutdown();
+ DCHECK(!mock_task_runner_.get() || !message_loop_.get());
+ if (mock_task_runner_.get()) {
+ // Check that all tests stop posting tasks.
+ mock_task_runner_->SetAutoAdvanceNowToPendingTasks(true);
+ while (mock_task_runner_->RunUntilIdle()) {
+ }
+ } else {
+ base::RunLoop().RunUntilIdle();
+ }
+ }
+
+ TaskQueueManager* task_queue_manager() const { return task_queue_manager_; }
+
+ void RunUntilIdle() {
+ // Only one of mock_task_runner_ or message_loop_ should be set.
+ DCHECK(!mock_task_runner_.get() || !message_loop_.get());
+ if (mock_task_runner_.get()) {
+ mock_task_runner_->RunUntilIdle();
+ } else {
+ base::RunLoop().RunUntilIdle();
+ }
+ }
+
+ template <typename E>
+ static void CallForEachEnumValue(E first,
+ E last,
+ const char* (*function)(E)) {
+ for (E val = first; val < last;
+ val = static_cast<E>(static_cast<int>(val) + 1)) {
+ (*function)(val);
+ }
+ }
+
+ static void CheckAllTaskQueueIdToString() {
+ CallForEachEnumValue<IdleHelper::IdlePeriodState>(
+ IdleHelper::IdlePeriodState::kFirstIdlePeriodState,
+ IdleHelper::IdlePeriodState::kIdlePeriodStateCount,
+ &IdleHelper::IdlePeriodStateToString);
+ }
+
+ bool IsInIdlePeriod() const {
+ return idle_helper_->IsInIdlePeriod(
+ idle_helper_->SchedulerIdlePeriodState());
+ }
+
+ protected:
+ static base::TimeDelta maximum_idle_period_duration() {
+ return base::TimeDelta::FromMilliseconds(
+ IdleHelper::kMaximumIdlePeriodMillis);
+ }
+
+ static base::TimeDelta retry_enable_long_idle_period_delay() {
+ return base::TimeDelta::FromMilliseconds(
+ IdleHelper::kRetryEnableLongIdlePeriodDelayMillis);
+ }
+
+ static base::TimeDelta minimum_idle_period_duration() {
+ return base::TimeDelta::FromMilliseconds(
+ IdleHelper::kMinimumIdlePeriodDurationMillis);
+ }
+
+ base::TimeTicks CurrentIdleTaskDeadline() {
+ return idle_helper_->CurrentIdleTaskDeadline();
+ }
+
+ void CheckIdlePeriodStateIs(const char* expected) {
+ EXPECT_STREQ(expected, IdleHelper::IdlePeriodStateToString(
+ idle_helper_->SchedulerIdlePeriodState()));
+ }
+
+ const scoped_refptr<TaskQueue>& idle_queue() const {
+ return idle_helper_->idle_queue_;
+ }
+
+ base::SimpleTestTickClock clock_;
+ // Only one of mock_task_runner_ or message_loop_ will be set.
+ scoped_refptr<cc::OrderedSimpleTaskRunner> mock_task_runner_;
+ std::unique_ptr<base::MessageLoop> message_loop_;
+
+ std::unique_ptr<WorkerSchedulerHelper> scheduler_helper_;
+ TaskQueueManager* task_queue_manager_; // Owned by scheduler_helper_.
+ std::unique_ptr<IdleHelperForTest> idle_helper_;
+ scoped_refptr<base::SingleThreadTaskRunner> default_task_runner_;
+ scoped_refptr<SingleThreadIdleTaskRunner> idle_task_runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(BaseIdleHelperTest);
+};
+
+class IdleHelperTest : public BaseIdleHelperTest {
+ public:
+ IdleHelperTest() : BaseIdleHelperTest(nullptr, base::TimeDelta()) {}
+
+ ~IdleHelperTest() override = default;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(IdleHelperTest);
+};
+
+TEST_F(IdleHelperTest, TestPostIdleTask) {
+ int run_count = 0;
+ base::TimeTicks expected_deadline =
+ clock_.NowTicks() + base::TimeDelta::FromMilliseconds(2300);
+ base::TimeTicks deadline_in_task;
+
+ clock_.Advance(base::TimeDelta::FromMilliseconds(100));
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE, base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task));
+
+ RunUntilIdle();
+ EXPECT_EQ(0, run_count);
+
+ idle_helper_->StartIdlePeriod(IdleHelper::IdlePeriodState::kInShortIdlePeriod,
+ clock_.NowTicks(), expected_deadline);
+ RunUntilIdle();
+ EXPECT_EQ(1, run_count);
+ EXPECT_EQ(expected_deadline, deadline_in_task);
+}
+
+TEST_F(IdleHelperTest, TestPostIdleTask_EndIdlePeriod) {
+ int run_count = 0;
+ base::TimeTicks deadline_in_task;
+
+ clock_.Advance(base::TimeDelta::FromMilliseconds(100));
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE, base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task));
+
+ RunUntilIdle();
+ EXPECT_EQ(0, run_count);
+
+ idle_helper_->StartIdlePeriod(
+ IdleHelper::IdlePeriodState::kInShortIdlePeriod, clock_.NowTicks(),
+ clock_.NowTicks() + base::TimeDelta::FromMilliseconds(10));
+ idle_helper_->EndIdlePeriod();
+ RunUntilIdle();
+ EXPECT_EQ(0, run_count);
+}
+
+TEST_F(IdleHelperTest, TestRepostingIdleTask) {
+ base::TimeTicks actual_deadline;
+ int run_count = 0;
+
+ g_max_idle_task_reposts = 2;
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE, base::BindOnce(&RepostingIdleTestTask,
+ base::RetainedRef(idle_task_runner_),
+ &run_count, &actual_deadline));
+ idle_helper_->StartIdlePeriod(
+ IdleHelper::IdlePeriodState::kInShortIdlePeriod, clock_.NowTicks(),
+ clock_.NowTicks() + base::TimeDelta::FromMilliseconds(10));
+ RunUntilIdle();
+ EXPECT_EQ(1, run_count);
+
+ // Reposted tasks shouldn't run until next idle period.
+ RunUntilIdle();
+ EXPECT_EQ(1, run_count);
+
+ idle_helper_->StartIdlePeriod(
+ IdleHelper::IdlePeriodState::kInShortIdlePeriod, clock_.NowTicks(),
+ clock_.NowTicks() + base::TimeDelta::FromMilliseconds(10));
+ RunUntilIdle();
+ EXPECT_EQ(2, run_count);
+}
+
+TEST_F(IdleHelperTest, TestIdleTaskExceedsDeadline) {
+ int run_count = 0;
+
+ // Post two UpdateClockToDeadlineIdleTestTask tasks.
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE,
+ base::BindOnce(&UpdateClockToDeadlineIdleTestTask, &clock_, &run_count));
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE,
+ base::BindOnce(&UpdateClockToDeadlineIdleTestTask, &clock_, &run_count));
+
+ idle_helper_->StartIdlePeriod(
+ IdleHelper::IdlePeriodState::kInShortIdlePeriod, clock_.NowTicks(),
+ clock_.NowTicks() + base::TimeDelta::FromMilliseconds(10));
+ RunUntilIdle();
+ // Only the first idle task should execute since it's used up the deadline.
+ EXPECT_EQ(1, run_count);
+
+ idle_helper_->EndIdlePeriod();
+ idle_helper_->StartIdlePeriod(
+ IdleHelper::IdlePeriodState::kInShortIdlePeriod, clock_.NowTicks(),
+ clock_.NowTicks() + base::TimeDelta::FromMilliseconds(10));
+ RunUntilIdle();
+ // Second task should be run on the next idle period.
+ EXPECT_EQ(2, run_count);
+}
+
+class IdleHelperTestWithIdlePeriodObserver : public BaseIdleHelperTest {
+ public:
+ IdleHelperTestWithIdlePeriodObserver()
+ : BaseIdleHelperTest(nullptr, base::TimeDelta()) {}
+
+ ~IdleHelperTestWithIdlePeriodObserver() override = default;
+
+ void SetUp() override {
+ // Don't set expectations on IdleHelper::Delegate.
+ }
+
+ void ExpectIdlePeriodStartsButNeverEnds() {
+ EXPECT_CALL(*idle_helper_, OnIdlePeriodStarted()).Times(1);
+ EXPECT_CALL(*idle_helper_, OnIdlePeriodEnded()).Times(0);
+ }
+
+ void ExpectIdlePeriodStartsAndEnds(const testing::Cardinality& cardinality) {
+ EXPECT_CALL(*idle_helper_, OnIdlePeriodStarted()).Times(cardinality);
+ EXPECT_CALL(*idle_helper_, OnIdlePeriodEnded()).Times(cardinality);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(IdleHelperTestWithIdlePeriodObserver);
+};
+
+TEST_F(IdleHelperTestWithIdlePeriodObserver, TestEnterButNotExitIdlePeriod) {
+ ExpectIdlePeriodStartsButNeverEnds();
+
+ idle_helper_->StartIdlePeriod(
+ IdleHelper::IdlePeriodState::kInShortIdlePeriod, clock_.NowTicks(),
+ clock_.NowTicks() + base::TimeDelta::FromMilliseconds(10));
+}
+
+TEST_F(IdleHelperTestWithIdlePeriodObserver, TestEnterAndExitIdlePeriod) {
+ BaseIdleHelperTest* fixture = this;
+ ON_CALL(*idle_helper_, OnIdlePeriodStarted())
+ .WillByDefault(
+ Invoke([fixture]() { EXPECT_TRUE(fixture->IsInIdlePeriod()); }));
+ ON_CALL(*idle_helper_, OnIdlePeriodEnded())
+ .WillByDefault(
+ Invoke([fixture]() { EXPECT_FALSE(fixture->IsInIdlePeriod()); }));
+
+ ExpectIdlePeriodStartsAndEnds(Exactly(1));
+
+ idle_helper_->StartIdlePeriod(
+ IdleHelper::IdlePeriodState::kInShortIdlePeriod, clock_.NowTicks(),
+ clock_.NowTicks() + base::TimeDelta::FromMilliseconds(10));
+ idle_helper_->EndIdlePeriod();
+}
+
+class IdleHelperWithMessageLoopTest : public BaseIdleHelperTest {
+ public:
+ IdleHelperWithMessageLoopTest()
+ : BaseIdleHelperTest(new base::MessageLoop(), base::TimeDelta()) {}
+ ~IdleHelperWithMessageLoopTest() override = default;
+
+ void PostFromNestedRunloop(
+ std::vector<std::pair<SingleThreadIdleTaskRunner::IdleTask, bool>>*
+ tasks) {
+ base::MessageLoop::ScopedNestableTaskAllower allow(message_loop_.get());
+ for (std::pair<SingleThreadIdleTaskRunner::IdleTask, bool>& pair : *tasks) {
+ if (pair.second) {
+ idle_task_runner_->PostIdleTask(FROM_HERE, std::move(pair.first));
+ } else {
+ idle_task_runner_->PostNonNestableIdleTask(FROM_HERE,
+ std::move(pair.first));
+ }
+ }
+ idle_helper_->StartIdlePeriod(
+ IdleHelper::IdlePeriodState::kInShortIdlePeriod, clock_.NowTicks(),
+ clock_.NowTicks() + base::TimeDelta::FromMilliseconds(10));
+ base::RunLoop().RunUntilIdle();
+ }
+
+ void SetUp() override {
+ EXPECT_CALL(*idle_helper_, OnIdlePeriodStarted()).Times(AnyNumber());
+ EXPECT_CALL(*idle_helper_, OnIdlePeriodEnded()).Times(AnyNumber());
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(IdleHelperWithMessageLoopTest);
+};
+
+TEST_F(IdleHelperWithMessageLoopTest,
+ NonNestableIdleTaskDoesntExecuteInNestedLoop) {
+ std::vector<std::string> order;
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE,
+ base::BindOnce(&AppendToVectorIdleTestTask, &order, std::string("1")));
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE,
+ base::BindOnce(&AppendToVectorIdleTestTask, &order, std::string("2")));
+
+ std::vector<std::pair<SingleThreadIdleTaskRunner::IdleTask, bool>>
+ tasks_to_post_from_nested_loop;
+ tasks_to_post_from_nested_loop.push_back(std::make_pair(
+ base::BindOnce(&AppendToVectorIdleTestTask, &order, std::string("3")),
+ false));
+ tasks_to_post_from_nested_loop.push_back(std::make_pair(
+ base::BindOnce(&AppendToVectorIdleTestTask, &order, std::string("4")),
+ true));
+ tasks_to_post_from_nested_loop.push_back(std::make_pair(
+ base::BindOnce(&AppendToVectorIdleTestTask, &order, std::string("5")),
+ true));
+
+ default_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&IdleHelperWithMessageLoopTest::PostFromNestedRunloop,
+ base::Unretained(this),
+ base::Unretained(&tasks_to_post_from_nested_loop)));
+
+ idle_helper_->StartIdlePeriod(
+ IdleHelper::IdlePeriodState::kInShortIdlePeriod, clock_.NowTicks(),
+ clock_.NowTicks() + base::TimeDelta::FromMilliseconds(10));
+ RunUntilIdle();
+ // Note we expect task 3 to run last because it's non-nestable.
+ EXPECT_THAT(order, testing::ElementsAre(std::string("1"), std::string("2"),
+ std::string("4"), std::string("5"),
+ std::string("3")));
+}
+
+TEST_F(IdleHelperTestWithIdlePeriodObserver, TestLongIdlePeriod) {
+ base::TimeTicks expected_deadline =
+ clock_.NowTicks() + maximum_idle_period_duration();
+ base::TimeTicks deadline_in_task;
+ int run_count = 0;
+
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE, base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task));
+
+ EXPECT_CALL(*idle_helper_, CanEnterLongIdlePeriod(_, _))
+ .Times(1)
+ .WillRepeatedly(Return(true));
+ ExpectIdlePeriodStartsButNeverEnds();
+
+ RunUntilIdle();
+ EXPECT_EQ(0, run_count); // Shouldn't run yet as no idle period.
+
+ idle_helper_->EnableLongIdlePeriod();
+ RunUntilIdle();
+ EXPECT_EQ(1, run_count); // Should have run in a long idle time.
+ EXPECT_EQ(expected_deadline, deadline_in_task);
+}
+
+TEST_F(IdleHelperTest, TestLongIdlePeriodWithPendingDelayedTask) {
+ base::TimeDelta pending_task_delay = base::TimeDelta::FromMilliseconds(30);
+ base::TimeTicks expected_deadline = clock_.NowTicks() + pending_task_delay;
+ base::TimeTicks deadline_in_task;
+ int run_count = 0;
+
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE, base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task));
+ default_task_runner_->PostDelayedTask(FROM_HERE, base::BindOnce(&NullTask),
+ pending_task_delay);
+
+ idle_helper_->EnableLongIdlePeriod();
+ RunUntilIdle();
+ EXPECT_EQ(1, run_count); // Should have run in a long idle time.
+ EXPECT_EQ(expected_deadline, deadline_in_task);
+}
+
+TEST_F(IdleHelperTest, TestLongIdlePeriodWithLatePendingDelayedTask) {
+ base::TimeDelta pending_task_delay = base::TimeDelta::FromMilliseconds(10);
+ base::TimeTicks deadline_in_task;
+ int run_count = 0;
+
+ default_task_runner_->PostDelayedTask(FROM_HERE, base::BindOnce(&NullTask),
+ pending_task_delay);
+
+ // Advance clock until after delayed task was meant to be run.
+ clock_.Advance(base::TimeDelta::FromMilliseconds(20));
+
+ // Post an idle task and then EnableLongIdlePeriod. Since there is a late
+ // pending delayed task this shouldn't actually start an idle period.
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE, base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task));
+ idle_helper_->EnableLongIdlePeriod();
+ RunUntilIdle();
+ EXPECT_EQ(0, run_count);
+
+ // After the delayed task has been run we should trigger an idle period.
+ clock_.Advance(maximum_idle_period_duration());
+ RunUntilIdle();
+ EXPECT_EQ(1, run_count);
+}
+
+TEST_F(IdleHelperTestWithIdlePeriodObserver, TestLongIdlePeriodRepeating) {
+ mock_task_runner_->SetAutoAdvanceNowToPendingTasks(true);
+ std::vector<base::TimeTicks> actual_deadlines;
+ int run_count = 0;
+
+ EXPECT_CALL(*idle_helper_, CanEnterLongIdlePeriod(_, _))
+ .Times(4)
+ .WillRepeatedly(Return(true));
+ ExpectIdlePeriodStartsAndEnds(AtLeast(2));
+
+ g_max_idle_task_reposts = 3;
+ base::TimeTicks clock_before(clock_.NowTicks());
+ base::TimeDelta idle_task_runtime(base::TimeDelta::FromMilliseconds(10));
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE,
+ base::BindOnce(&RepostingUpdateClockIdleTestTask,
+ base::RetainedRef(idle_task_runner_), &run_count, &clock_,
+ idle_task_runtime, &actual_deadlines));
+
+ // Check each idle task runs in their own idle period.
+ idle_helper_->EnableLongIdlePeriod();
+ RunUntilIdle();
+ EXPECT_EQ(3, run_count);
+ EXPECT_THAT(
+ actual_deadlines,
+ testing::ElementsAre(clock_before + maximum_idle_period_duration(),
+ clock_before + 2 * maximum_idle_period_duration(),
+ clock_before + 3 * maximum_idle_period_duration()));
+
+ g_max_idle_task_reposts = 5;
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE,
+ base::BindOnce(&RepostingUpdateClockIdleTestTask,
+ base::RetainedRef(idle_task_runner_), &run_count, &clock_,
+ idle_task_runtime, &actual_deadlines));
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE, base::BindOnce(&EndIdlePeriodIdleTask,
+ base::Unretained(idle_helper_.get())));
+
+ // Ensure that reposting tasks stop after EndIdlePeriod is called.
+ RunUntilIdle();
+ EXPECT_EQ(4, run_count);
+}
+
+TEST_F(IdleHelperTestWithIdlePeriodObserver,
+ TestLongIdlePeriodWhenNotCanEnterLongIdlePeriod) {
+ base::TimeDelta delay = base::TimeDelta::FromMilliseconds(1000);
+ base::TimeDelta half_delay = base::TimeDelta::FromMilliseconds(500);
+ base::TimeTicks delay_over = clock_.NowTicks() + delay;
+ base::TimeTicks deadline_in_task;
+ int run_count = 0;
+
+ ON_CALL(*idle_helper_, CanEnterLongIdlePeriod(_, _))
+ .WillByDefault(
+ Invoke([delay, delay_over](
+ base::TimeTicks now,
+ base::TimeDelta* next_long_idle_period_delay_out) {
+ if (now >= delay_over)
+ return true;
+ *next_long_idle_period_delay_out = delay;
+ return false;
+ }));
+
+ EXPECT_CALL(*idle_helper_, CanEnterLongIdlePeriod(_, _)).Times(2);
+ EXPECT_CALL(*idle_helper_, OnIdlePeriodStarted()).Times(AnyNumber());
+
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE, base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task));
+
+ // Make sure Idle tasks don't run until the delay has occurred.
+ idle_helper_->EnableLongIdlePeriod();
+ RunUntilIdle();
+ EXPECT_EQ(0, run_count);
+
+ clock_.Advance(half_delay);
+ RunUntilIdle();
+ EXPECT_EQ(0, run_count);
+
+ // Delay is finished, idle task should run.
+ clock_.Advance(half_delay);
+ RunUntilIdle();
+ EXPECT_EQ(1, run_count);
+}
+
+TEST_F(IdleHelperTest,
+ TestLongIdlePeriodDoesNotImmediatelyRestartIfMaxDeadline) {
+ ScopedAutoAdvanceNowEnabler advance_now(mock_task_runner_);
+
+ std::vector<base::TimeTicks> actual_deadlines;
+ int run_count = 0;
+
+ base::TimeTicks clock_before(clock_.NowTicks());
+ base::TimeDelta idle_task_runtime(base::TimeDelta::FromMilliseconds(10));
+
+ // The second idle period should happen immediately after the first the
+ // they have max deadlines.
+ g_max_idle_task_reposts = 2;
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE,
+ base::BindOnce(&RepostingUpdateClockIdleTestTask,
+ base::RetainedRef(idle_task_runner_), &run_count, &clock_,
+ idle_task_runtime, &actual_deadlines));
+
+ idle_helper_->EnableLongIdlePeriod();
+ RunUntilIdle();
+ EXPECT_EQ(2, run_count);
+ EXPECT_THAT(
+ actual_deadlines,
+ testing::ElementsAre(clock_before + maximum_idle_period_duration(),
+ clock_before + 2 * maximum_idle_period_duration()));
+}
+
+TEST_F(IdleHelperTest, TestLongIdlePeriodRestartWaitsIfNotMaxDeadline) {
+ base::TimeTicks actual_deadline;
+ int run_count = 0;
+
+ base::TimeDelta pending_task_delay(base::TimeDelta::FromMilliseconds(20));
+ base::TimeDelta idle_task_duration(base::TimeDelta::FromMilliseconds(10));
+ base::TimeTicks expected_deadline(clock_.NowTicks() + pending_task_delay +
+ maximum_idle_period_duration() +
+ retry_enable_long_idle_period_delay());
+
+ // Post delayed task to ensure idle period doesn't have a max deadline.
+ default_task_runner_->PostDelayedTask(FROM_HERE, base::BindOnce(&NullTask),
+ pending_task_delay);
+
+ g_max_idle_task_reposts = 2;
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE, base::BindOnce(&RepostingIdleTestTask,
+ base::RetainedRef(idle_task_runner_),
+ &run_count, &actual_deadline));
+ idle_helper_->EnableLongIdlePeriod();
+ RunUntilIdle();
+ EXPECT_EQ(1, run_count);
+ clock_.Advance(idle_task_duration);
+
+ // Next idle period shouldn't happen until the pending task has been run.
+ RunUntilIdle();
+ EXPECT_EQ(1, run_count);
+
+ // Once the pending task is run the new idle period should start.
+ clock_.Advance(pending_task_delay - idle_task_duration);
+
+ // Since the idle period tried to start before the pending task ran we have to
+ // wait for the idle helper to retry starting the long idle period.
+ clock_.Advance(retry_enable_long_idle_period_delay());
+ RunUntilIdle();
+
+ EXPECT_EQ(2, run_count);
+ EXPECT_EQ(expected_deadline, actual_deadline);
+}
+
+TEST_F(IdleHelperTest, TestLongIdlePeriodPaused) {
+ mock_task_runner_->SetAutoAdvanceNowToPendingTasks(true);
+ std::vector<base::TimeTicks> actual_deadlines;
+ int run_count = 0;
+
+ // If there are no idle tasks posted we should start in the paused state.
+ idle_helper_->EnableLongIdlePeriod();
+ CheckIdlePeriodStateIs("in_long_idle_period_paused");
+ // There shouldn't be any delayed tasks posted by the idle helper when paused.
+ base::TimeTicks next_pending_delayed_task;
+ EXPECT_FALSE(scheduler_helper_->real_time_domain()->NextScheduledRunTime(
+ &next_pending_delayed_task));
+
+ // Posting a task should transition us to the an active state.
+ g_max_idle_task_reposts = 2;
+ base::TimeTicks clock_before(clock_.NowTicks());
+ base::TimeDelta idle_task_runtime(base::TimeDelta::FromMilliseconds(10));
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE,
+ base::BindOnce(&RepostingUpdateClockIdleTestTask,
+ base::RetainedRef(idle_task_runner_), &run_count, &clock_,
+ idle_task_runtime, &actual_deadlines));
+ RunUntilIdle();
+ EXPECT_EQ(2, run_count);
+ EXPECT_THAT(
+ actual_deadlines,
+ testing::ElementsAre(clock_before + maximum_idle_period_duration(),
+ clock_before + 2 * maximum_idle_period_duration()));
+
+ // Once all task have been run we should go back to the paused state.
+ CheckIdlePeriodStateIs("in_long_idle_period_paused");
+ EXPECT_FALSE(scheduler_helper_->real_time_domain()->NextScheduledRunTime(
+ &next_pending_delayed_task));
+
+ idle_helper_->EndIdlePeriod();
+ CheckIdlePeriodStateIs("not_in_idle_period");
+}
+
+TEST_F(IdleHelperTest, TestLongIdlePeriodWhenShutdown) {
+ base::TimeTicks deadline_in_task;
+ int run_count = 0;
+
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE, base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task));
+ idle_helper_->Shutdown();
+
+ // We shouldn't be able to enter a long idle period when shutdown
+ idle_helper_->EnableLongIdlePeriod();
+ RunUntilIdle();
+ CheckIdlePeriodStateIs("not_in_idle_period");
+ EXPECT_EQ(0, run_count);
+}
+
+void TestCanExceedIdleDeadlineIfRequiredTask(IdleHelperForTest* idle_helper,
+ bool* can_exceed_idle_deadline_out,
+ int* run_count,
+ base::TimeTicks deadline) {
+ *can_exceed_idle_deadline_out =
+ idle_helper->CanExceedIdleDeadlineIfRequired();
+ (*run_count)++;
+}
+
+TEST_F(IdleHelperTest, CanExceedIdleDeadlineIfRequired) {
+ int run_count = 0;
+ bool can_exceed_idle_deadline = false;
+
+ // Should return false if not in an idle period.
+ EXPECT_FALSE(idle_helper_->CanExceedIdleDeadlineIfRequired());
+
+ // Should return false for short idle periods.
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE, base::BindOnce(&TestCanExceedIdleDeadlineIfRequiredTask,
+ idle_helper_.get(), &can_exceed_idle_deadline,
+ &run_count));
+ idle_helper_->StartIdlePeriod(
+ IdleHelper::IdlePeriodState::kInShortIdlePeriod, clock_.NowTicks(),
+ clock_.NowTicks() + base::TimeDelta::FromMilliseconds(10));
+ RunUntilIdle();
+ EXPECT_EQ(1, run_count);
+ EXPECT_FALSE(can_exceed_idle_deadline);
+
+ // Should return false for a long idle period which is shortened due to a
+ // pending delayed task.
+ default_task_runner_->PostDelayedTask(FROM_HERE, base::BindOnce(&NullTask),
+ base::TimeDelta::FromMilliseconds(10));
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE, base::BindOnce(&TestCanExceedIdleDeadlineIfRequiredTask,
+ idle_helper_.get(), &can_exceed_idle_deadline,
+ &run_count));
+ idle_helper_->EnableLongIdlePeriod();
+ RunUntilIdle();
+ EXPECT_EQ(2, run_count);
+ EXPECT_FALSE(can_exceed_idle_deadline);
+
+ // Next long idle period will be for the maximum time, so
+ // CanExceedIdleDeadlineIfRequired should return true.
+ clock_.Advance(maximum_idle_period_duration());
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE, base::BindOnce(&TestCanExceedIdleDeadlineIfRequiredTask,
+ idle_helper_.get(), &can_exceed_idle_deadline,
+ &run_count));
+ RunUntilIdle();
+ EXPECT_EQ(3, run_count);
+ EXPECT_TRUE(can_exceed_idle_deadline);
+}
+
+class IdleHelperWithQuiescencePeriodTest : public BaseIdleHelperTest {
+ public:
+ enum {
+ kQuiescenceDelayMs = 100,
+ kLongIdlePeriodMs = 50,
+ };
+
+ IdleHelperWithQuiescencePeriodTest()
+ : BaseIdleHelperTest(
+ nullptr,
+ base::TimeDelta::FromMilliseconds(kQuiescenceDelayMs)) {}
+
+ ~IdleHelperWithQuiescencePeriodTest() override = default;
+
+ void SetUp() override {
+ EXPECT_CALL(*idle_helper_, OnIdlePeriodStarted()).Times(AnyNumber());
+ EXPECT_CALL(*idle_helper_, OnIdlePeriodEnded()).Times(AnyNumber());
+ EXPECT_CALL(*idle_helper_, CanEnterLongIdlePeriod(_, _))
+ .Times(AnyNumber())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*idle_helper_, IsNotQuiescent()).Times(AnyNumber());
+ }
+
+ void MakeNonQuiescent() {
+ // Run an arbitrary task so we're deemed to be not quiescent.
+ default_task_runner_->PostTask(FROM_HERE, base::BindOnce(NullTask));
+ RunUntilIdle();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(IdleHelperWithQuiescencePeriodTest);
+};
+
+class IdleHelperWithQuiescencePeriodTestWithIdlePeriodObserver
+ : public IdleHelperWithQuiescencePeriodTest {
+ public:
+ IdleHelperWithQuiescencePeriodTestWithIdlePeriodObserver()
+ : IdleHelperWithQuiescencePeriodTest() {}
+
+ ~IdleHelperWithQuiescencePeriodTestWithIdlePeriodObserver() override =
+ default;
+
+ void SetUp() override {
+ // Don't set expectations on IdleHelper::Delegate.
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(
+ IdleHelperWithQuiescencePeriodTestWithIdlePeriodObserver);
+};
+
+TEST_F(IdleHelperWithQuiescencePeriodTest,
+ LongIdlePeriodStartsImmediatelyIfQuiescent) {
+ base::TimeTicks actual_deadline;
+ int run_count = 0;
+ g_max_idle_task_reposts = 1;
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE, base::BindOnce(&RepostingIdleTestTask,
+ base::RetainedRef(idle_task_runner_),
+ &run_count, &actual_deadline));
+
+ idle_helper_->EnableLongIdlePeriod();
+ RunUntilIdle();
+
+ EXPECT_EQ(1, run_count);
+}
+
+TEST_F(IdleHelperWithQuiescencePeriodTestWithIdlePeriodObserver,
+ LongIdlePeriodDoesNotStartsImmediatelyIfBusy) {
+ MakeNonQuiescent();
+ EXPECT_CALL(*idle_helper_, OnIdlePeriodStarted()).Times(0);
+ EXPECT_CALL(*idle_helper_, OnIdlePeriodEnded()).Times(0);
+ EXPECT_CALL(*idle_helper_, CanEnterLongIdlePeriod(_, _)).Times(0);
+ EXPECT_CALL(*idle_helper_, IsNotQuiescent()).Times(AtLeast(1));
+
+ base::TimeTicks actual_deadline;
+ int run_count = 0;
+ g_max_idle_task_reposts = 1;
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE, base::BindOnce(&RepostingIdleTestTask,
+ base::RetainedRef(idle_task_runner_),
+ &run_count, &actual_deadline));
+
+ idle_helper_->EnableLongIdlePeriod();
+ RunUntilIdle();
+
+ EXPECT_EQ(0, run_count);
+
+ scheduler_helper_->Shutdown();
+}
+
+TEST_F(IdleHelperWithQuiescencePeriodTest,
+ LongIdlePeriodStartsAfterQuiescence) {
+ MakeNonQuiescent();
+ mock_task_runner_->SetAutoAdvanceNowToPendingTasks(true);
+
+ // Run a repeating task so we're deemed to be busy for the next 400ms.
+ default_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&RepeatingTask,
+ base::Unretained(default_task_runner_.get()),
+ 10, base::TimeDelta::FromMilliseconds(40)));
+
+ int run_count = 0;
+ // In this scenario EnableLongIdlePeriod deems us not to be quiescent 5x in
+ // a row.
+ base::TimeTicks expected_deadline =
+ clock_.NowTicks() + base::TimeDelta::FromMilliseconds(
+ 5 * kQuiescenceDelayMs + kLongIdlePeriodMs);
+ base::TimeTicks deadline_in_task;
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE, base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task));
+
+ idle_helper_->EnableLongIdlePeriod();
+ RunUntilIdle();
+ EXPECT_EQ(1, run_count);
+ EXPECT_EQ(expected_deadline, deadline_in_task);
+}
+
+TEST_F(IdleHelperWithQuiescencePeriodTest,
+ QuescienceCheckedForAfterLongIdlePeriodEnds) {
+ mock_task_runner_->SetAutoAdvanceNowToPendingTasks(true);
+
+ idle_task_runner_->PostIdleTask(FROM_HERE, base::BindOnce(&NullIdleTask));
+ idle_helper_->EnableLongIdlePeriod();
+ RunUntilIdle();
+
+ // Post a normal task to make the scheduler non-quiescent.
+ default_task_runner_->PostTask(FROM_HERE, base::BindOnce(&NullTask));
+ RunUntilIdle();
+
+ // Post an idle task. The idle task won't run initially because the system is
+ // not judged to be quiescent, but should be run after the quiescence delay.
+ int run_count = 0;
+ base::TimeTicks deadline_in_task;
+ base::TimeTicks expected_deadline =
+ clock_.NowTicks() +
+ base::TimeDelta::FromMilliseconds(kQuiescenceDelayMs + kLongIdlePeriodMs);
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE, base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task));
+ idle_helper_->EnableLongIdlePeriod();
+ RunUntilIdle();
+
+ EXPECT_EQ(1, run_count);
+ EXPECT_EQ(expected_deadline, deadline_in_task);
+}
+
+TEST_F(IdleHelperTest, NoShortIdlePeriodWhenDeadlineTooClose) {
+ int run_count = 0;
+ base::TimeTicks deadline_in_task;
+
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE, base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task));
+
+ base::TimeDelta half_a_ms(base::TimeDelta::FromMicroseconds(50));
+ base::TimeTicks less_than_min_deadline(
+ clock_.NowTicks() + minimum_idle_period_duration() - half_a_ms);
+ base::TimeTicks more_than_min_deadline(
+ clock_.NowTicks() + minimum_idle_period_duration() + half_a_ms);
+
+ idle_helper_->StartIdlePeriod(IdleHelper::IdlePeriodState::kInShortIdlePeriod,
+ clock_.NowTicks(), less_than_min_deadline);
+ RunUntilIdle();
+ EXPECT_EQ(0, run_count);
+
+ idle_helper_->StartIdlePeriod(IdleHelper::IdlePeriodState::kInShortIdlePeriod,
+ clock_.NowTicks(), more_than_min_deadline);
+ RunUntilIdle();
+ EXPECT_EQ(1, run_count);
+}
+
+TEST_F(IdleHelperTest, NoLongIdlePeriodWhenDeadlineTooClose) {
+ int run_count = 0;
+ base::TimeTicks deadline_in_task;
+
+ base::TimeDelta half_a_ms(base::TimeDelta::FromMicroseconds(50));
+ base::TimeDelta less_than_min_deadline_duration(
+ minimum_idle_period_duration() - half_a_ms);
+ base::TimeDelta more_than_min_deadline_duration(
+ minimum_idle_period_duration() + half_a_ms);
+
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE, base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task));
+ default_task_runner_->PostDelayedTask(FROM_HERE, base::BindOnce(&NullTask),
+ less_than_min_deadline_duration);
+
+ idle_helper_->EnableLongIdlePeriod();
+ RunUntilIdle();
+ EXPECT_EQ(0, run_count);
+
+ idle_helper_->EndIdlePeriod();
+ clock_.Advance(maximum_idle_period_duration());
+ RunUntilIdle();
+ EXPECT_EQ(0, run_count);
+
+ default_task_runner_->PostDelayedTask(FROM_HERE, base::BindOnce(&NullTask),
+ more_than_min_deadline_duration);
+ idle_helper_->EnableLongIdlePeriod();
+ RunUntilIdle();
+ EXPECT_EQ(1, run_count);
+}
+
+TEST_F(IdleHelperWithQuiescencePeriodTest,
+ PendingEnableLongIdlePeriodNotRunAfterShutdown) {
+ MakeNonQuiescent();
+
+ bool shutdown_task_run = false;
+ int run_count = 0;
+ base::TimeTicks deadline_in_task;
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE,
+ base::BindOnce(&ShutdownIdleTask, base::Unretained(idle_helper_.get()),
+ &shutdown_task_run));
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE, base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task));
+
+ // Delayed call to IdleHelper::EnableLongIdlePeriod enables idle tasks.
+ idle_helper_->EnableLongIdlePeriod();
+ clock_.Advance(maximum_idle_period_duration() * 2.0);
+ mock_task_runner_->RunPendingTasks();
+ EXPECT_TRUE(shutdown_task_run);
+ EXPECT_EQ(0, run_count);
+
+ // Shutdown immediately after idle period started should prevent the idle
+ // task from running.
+ idle_helper_->Shutdown();
+ mock_task_runner_->RunUntilIdle();
+ EXPECT_EQ(0, run_count);
+}
+
+TEST_F(IdleHelperTest, TestPostDelayedIdleTask) {
+ int run_count = 0;
+ base::TimeTicks expected_deadline =
+ clock_.NowTicks() + base::TimeDelta::FromMilliseconds(2300);
+ base::TimeTicks deadline_in_task;
+
+ // Posting a delayed idle task should not post anything on the underlying
+ // task queue until the delay is up.
+ idle_task_runner_->PostDelayedIdleTask(
+ FROM_HERE, base::TimeDelta::FromMilliseconds(200),
+ base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task));
+ EXPECT_EQ(0u, idle_queue()->GetNumberOfPendingTasks());
+
+ clock_.Advance(base::TimeDelta::FromMilliseconds(100));
+
+ // It shouldn't run until the delay is over even though we went idle.
+ idle_helper_->StartIdlePeriod(IdleHelper::IdlePeriodState::kInShortIdlePeriod,
+ clock_.NowTicks(), expected_deadline);
+ EXPECT_EQ(0u, idle_queue()->GetNumberOfPendingTasks());
+ RunUntilIdle();
+ EXPECT_EQ(0, run_count);
+
+ clock_.Advance(base::TimeDelta::FromMilliseconds(100));
+ idle_helper_->StartIdlePeriod(IdleHelper::IdlePeriodState::kInShortIdlePeriod,
+ clock_.NowTicks(), expected_deadline);
+ EXPECT_EQ(1u, idle_queue()->GetNumberOfPendingTasks());
+ RunUntilIdle();
+
+ EXPECT_EQ(1, run_count);
+ EXPECT_EQ(expected_deadline, deadline_in_task);
+}
+
+// Tests that the OnPendingTasksChanged callback is called once when the idle
+// queue becomes non-empty and again when it becomes empty.
+TEST_F(IdleHelperTest, OnPendingTasksChanged) {
+ int run_count = 0;
+ base::TimeTicks expected_deadline =
+ clock_.NowTicks() + base::TimeDelta::FromMilliseconds(2300);
+ base::TimeTicks deadline_in_task;
+
+ {
+ testing::InSequence dummy;
+ // This will be called once. I.e when the one and only task is posted.
+ EXPECT_CALL(*idle_helper_, OnPendingTasksChanged(true)).Times(1);
+ // This will be called once. I.e when the one and only task completes.
+ EXPECT_CALL(*idle_helper_, OnPendingTasksChanged(false)).Times(1);
+ }
+
+ clock_.Advance(base::TimeDelta::FromMilliseconds(100));
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE, base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task));
+
+ RunUntilIdle();
+ EXPECT_EQ(0, run_count);
+
+ idle_helper_->StartIdlePeriod(IdleHelper::IdlePeriodState::kInShortIdlePeriod,
+ clock_.NowTicks(), expected_deadline);
+ RunUntilIdle();
+ EXPECT_EQ(1, run_count);
+ EXPECT_EQ(expected_deadline, deadline_in_task);
+}
+
+// Tests that the OnPendingTasksChanged callback is still only called once
+// with false despite there being two idle tasks posted.
+TEST_F(IdleHelperTest, OnPendingTasksChanged_TwoTasksAtTheSameTime) {
+ int run_count = 0;
+ base::TimeTicks expected_deadline =
+ clock_.NowTicks() + base::TimeDelta::FromMilliseconds(2300);
+ base::TimeTicks deadline_in_task;
+
+ {
+ testing::InSequence dummy;
+ // This will be called 3 times. I.e when T1 and T2 are posted and when T1
+ // completes.
+ EXPECT_CALL(*idle_helper_, OnPendingTasksChanged(true)).Times(3);
+ // This will be called once. I.e when T2 completes.
+ EXPECT_CALL(*idle_helper_, OnPendingTasksChanged(false)).Times(1);
+ }
+
+ clock_.Advance(base::TimeDelta::FromMilliseconds(100));
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE, base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task));
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE, base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task));
+
+ RunUntilIdle();
+ EXPECT_EQ(0, run_count);
+
+ idle_helper_->StartIdlePeriod(IdleHelper::IdlePeriodState::kInShortIdlePeriod,
+ clock_.NowTicks(), expected_deadline);
+ RunUntilIdle();
+ EXPECT_EQ(2, run_count);
+ EXPECT_EQ(expected_deadline, deadline_in_task);
+}
+
+} // namespace idle_helper_unittest
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/metrics_helper.cc b/chromium/third_party/blink/renderer/platform/scheduler/child/metrics_helper.cc
new file mode 100644
index 00000000000..de044878b78
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/metrics_helper.cc
@@ -0,0 +1,84 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/child/metrics_helper.h"
+
+#include "third_party/blink/renderer/platform/scheduler/child/process_state.h"
+
+namespace blink {
+namespace scheduler {
+
+namespace {
+
+// Threshold for discarding ultra-long tasks. It is assumed that ultra-long
+// tasks are reporting glitches (e.g. system falling asleep on the middle of the
+// task).
+constexpr base::TimeDelta kLongTaskDiscardingThreshold =
+ base::TimeDelta::FromSeconds(30);
+
+} // namespace
+
+MetricsHelper::MetricsHelper(WebThreadType thread_type)
+ : thread_type_(thread_type),
+ thread_task_duration_reporter_(
+ "RendererScheduler.TaskDurationPerThreadType"),
+ thread_task_cpu_duration_reporter_(
+ "RendererScheduler.TaskCPUDurationPerThreadType"),
+ foreground_thread_task_duration_reporter_(
+ "RendererScheduler.TaskDurationPerThreadType.Foreground"),
+ foreground_thread_task_cpu_duration_reporter_(
+ "RendererScheduler.TaskCPUDurationPerThreadType.Foreground"),
+ background_thread_task_duration_reporter_(
+ "RendererScheduler.TaskDurationPerThreadType.Background"),
+ background_thread_task_cpu_duration_reporter_(
+ "RendererScheduler.TaskCPUDurationPerThreadType.Background") {}
+
+MetricsHelper::~MetricsHelper() {}
+
+bool MetricsHelper::ShouldDiscardTask(
+ TaskQueue* queue,
+ const TaskQueue::Task& task,
+ base::TimeTicks start_time,
+ base::TimeTicks end_time,
+ base::Optional<base::TimeDelta> thread_time) {
+ // TODO(altimin): Investigate the relationship between thread time and
+ // wall time for discarded tasks.
+ return end_time - start_time > kLongTaskDiscardingThreshold;
+}
+
+void MetricsHelper::RecordCommonTaskMetrics(
+ TaskQueue* queue,
+ const TaskQueue::Task& task,
+ base::TimeTicks start_time,
+ base::TimeTicks end_time,
+ base::Optional<base::TimeDelta> thread_time) {
+ base::TimeDelta wall_time = end_time - start_time;
+
+ thread_task_duration_reporter_.RecordTask(thread_type_, wall_time);
+
+ bool backgrounded = internal::ProcessState::Get()->is_process_backgrounded;
+
+ if (backgrounded) {
+ background_thread_task_duration_reporter_.RecordTask(thread_type_,
+ wall_time);
+ } else {
+ foreground_thread_task_duration_reporter_.RecordTask(thread_type_,
+ wall_time);
+ }
+
+ if (!thread_time)
+ return;
+ thread_task_cpu_duration_reporter_.RecordTask(thread_type_,
+ thread_time.value());
+ if (backgrounded) {
+ background_thread_task_cpu_duration_reporter_.RecordTask(
+ thread_type_, thread_time.value());
+ } else {
+ foreground_thread_task_cpu_duration_reporter_.RecordTask(
+ thread_type_, thread_time.value());
+ }
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/metrics_helper.h b/chromium/third_party/blink/renderer/platform/scheduler/child/metrics_helper.h
new file mode 100644
index 00000000000..83794f48527
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/metrics_helper.h
@@ -0,0 +1,68 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_METRICS_HELPER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_METRICS_HELPER_H_
+
+#include "base/optional.h"
+#include "base/time/time.h"
+#include "third_party/blink/public/platform/web_thread_type.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue.h"
+#include "third_party/blink/renderer/platform/scheduler/util/task_duration_metric_reporter.h"
+
+namespace blink {
+namespace scheduler {
+
+class TaskQueue;
+
+// Helper class to take care of task metrics shared between main thread
+// and worker threads of the renderer process, including per-thread
+// task metrics.
+//
+// Each thread-specific scheduler should have its own subclass of MetricsHelper
+// (RendererMetricsHelper, WorkerMetricsHelper, etc) and should call
+// RecordCommonTaskMetrics manually.
+// Note that this is code reuse, not data reuse -- each thread should have its
+// own instantiation of this class.
+class PLATFORM_EXPORT MetricsHelper {
+ public:
+ explicit MetricsHelper(WebThreadType thread_type);
+ ~MetricsHelper();
+
+ protected:
+ bool ShouldDiscardTask(TaskQueue* queue,
+ const TaskQueue::Task& task,
+ base::TimeTicks start_time,
+ base::TimeTicks end_time,
+ base::Optional<base::TimeDelta> thread_time);
+
+ // Record task metrics which are shared between threads.
+ void RecordCommonTaskMetrics(TaskQueue* queue,
+ const TaskQueue::Task& task,
+ base::TimeTicks start_time,
+ base::TimeTicks end_time,
+ base::Optional<base::TimeDelta> thread_time);
+
+ protected:
+ WebThreadType thread_type_;
+
+ private:
+ TaskDurationMetricReporter<WebThreadType> thread_task_duration_reporter_;
+ TaskDurationMetricReporter<WebThreadType> thread_task_cpu_duration_reporter_;
+ TaskDurationMetricReporter<WebThreadType>
+ foreground_thread_task_duration_reporter_;
+ TaskDurationMetricReporter<WebThreadType>
+ foreground_thread_task_cpu_duration_reporter_;
+ TaskDurationMetricReporter<WebThreadType>
+ background_thread_task_duration_reporter_;
+ TaskDurationMetricReporter<WebThreadType>
+ background_thread_task_cpu_duration_reporter_;
+
+ DISALLOW_COPY_AND_ASSIGN(MetricsHelper);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_METRICS_HELPER_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/metrics_helper_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/child/metrics_helper_unittest.cc
new file mode 100644
index 00000000000..f232dc424ce
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/metrics_helper_unittest.cc
@@ -0,0 +1,78 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/child/metrics_helper.h"
+#include "base/test/histogram_tester.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+namespace scheduler {
+
+namespace {
+
+class MetricsHelperForTest : public MetricsHelper {
+ public:
+ MetricsHelperForTest(WebThreadType thread_type)
+ : MetricsHelper(thread_type) {}
+ ~MetricsHelperForTest() = default;
+
+ using MetricsHelper::RecordCommonTaskMetrics;
+};
+
+} // namespace
+
+TEST(MetricsHelperTest, TaskDurationPerThreadType) {
+ base::HistogramTester histogram_tester;
+
+ MetricsHelperForTest main_thread_metrics(WebThreadType::kMainThread);
+ MetricsHelperForTest compositor_metrics(WebThreadType::kCompositorThread);
+ MetricsHelperForTest worker_metrics(WebThreadType::kUnspecifiedWorkerThread);
+
+ TaskQueue::Task fake_task(
+ (TaskQueue::PostedTask(base::OnceClosure(), base::Location())),
+ base::TimeTicks());
+
+ main_thread_metrics.RecordCommonTaskMetrics(
+ nullptr, fake_task,
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(10),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(50),
+ base::TimeDelta::FromMilliseconds(15));
+ compositor_metrics.RecordCommonTaskMetrics(
+ nullptr, fake_task,
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(10),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(80),
+ base::TimeDelta::FromMilliseconds(5));
+ compositor_metrics.RecordCommonTaskMetrics(
+ nullptr, fake_task,
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(100),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(200),
+ base::nullopt);
+ worker_metrics.RecordCommonTaskMetrics(
+ nullptr, fake_task,
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(10),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(125),
+ base::TimeDelta::FromMilliseconds(25));
+
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples(
+ "RendererScheduler.TaskDurationPerThreadType"),
+ testing::UnorderedElementsAre(
+ base::Bucket(static_cast<int>(WebThreadType::kMainThread), 40),
+ base::Bucket(static_cast<int>(WebThreadType::kCompositorThread), 170),
+ base::Bucket(
+ static_cast<int>(WebThreadType::kUnspecifiedWorkerThread), 115)));
+
+ EXPECT_THAT(
+ histogram_tester.GetAllSamples(
+ "RendererScheduler.TaskCPUDurationPerThreadType"),
+ testing::UnorderedElementsAre(
+ base::Bucket(static_cast<int>(WebThreadType::kMainThread), 15),
+ base::Bucket(static_cast<int>(WebThreadType::kCompositorThread), 5),
+ base::Bucket(
+ static_cast<int>(WebThreadType::kUnspecifiedWorkerThread), 25)));
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/page_visibility_state.cc b/chromium/third_party/blink/renderer/platform/scheduler/child/page_visibility_state.cc
new file mode 100644
index 00000000000..af00b25482d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/page_visibility_state.cc
@@ -0,0 +1,22 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/child/page_visibility_state.h"
+
+namespace blink {
+namespace scheduler {
+
+const char* PageVisibilityStateToString(PageVisibilityState visibility) {
+ switch (visibility) {
+ case PageVisibilityState::kVisible:
+ return "visible";
+ case PageVisibilityState::kHidden:
+ return "hidden";
+ }
+ // Keep MSVC happy.
+ return nullptr;
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/page_visibility_state.h b/chromium/third_party/blink/renderer/platform/scheduler/child/page_visibility_state.h
new file mode 100644
index 00000000000..11b6fe8bc6e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/page_visibility_state.h
@@ -0,0 +1,19 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_PAGE_VISIBILITY_STATE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_PAGE_VISIBILITY_STATE_H_
+
+namespace blink {
+namespace scheduler {
+
+// TODO(altimin): Move to core/.
+enum class PageVisibilityState { kVisible, kHidden };
+
+const char* PageVisibilityStateToString(PageVisibilityState visibility);
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_PAGE_VISIBILITY_STATE_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/pollable_thread_safe_flag.cc b/chromium/third_party/blink/renderer/platform/scheduler/child/pollable_thread_safe_flag.cc
new file mode 100644
index 00000000000..409d276a10c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/pollable_thread_safe_flag.cc
@@ -0,0 +1,19 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/child/pollable_thread_safe_flag.h"
+
+PollableThreadSafeFlag::PollableThreadSafeFlag(base::Lock* write_lock_)
+ : flag_(false), write_lock_(write_lock_) {}
+
+PollableThreadSafeFlag::~PollableThreadSafeFlag() = default;
+
+void PollableThreadSafeFlag::SetWhileLocked(bool value) {
+ write_lock_->AssertAcquired();
+ base::subtle::Release_Store(&flag_, value);
+}
+
+bool PollableThreadSafeFlag::IsSet() const {
+ return base::subtle::Acquire_Load(&flag_);
+}
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/pollable_thread_safe_flag.h b/chromium/third_party/blink/renderer/platform/scheduler/child/pollable_thread_safe_flag.h
new file mode 100644
index 00000000000..d3e4a531858
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/pollable_thread_safe_flag.h
@@ -0,0 +1,36 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_POLLABLE_THREAD_SAFE_FLAG_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_POLLABLE_THREAD_SAFE_FLAG_H_
+
+#include "base/atomicops.h"
+#include "base/macros.h"
+#include "base/synchronization/lock.h"
+
+// A PollableThreadSafeFlag can be polled without requiring a lock, but can only
+// be updated if a lock is held. This enables lock-free checking as to whether a
+// condition has changed, while protecting operations which update the condition
+// with a lock. You must ensure that the flag is only updated within the same
+// lock-protected critical section as any other variables on which the condition
+// depends.
+class PollableThreadSafeFlag {
+ public:
+ explicit PollableThreadSafeFlag(base::Lock* write_lock);
+ ~PollableThreadSafeFlag();
+
+ // Set the flag. May only be called if |write_lock| is held.
+ void SetWhileLocked(bool value);
+
+ // Returns true iff the flag is set to true.
+ bool IsSet() const;
+
+ private:
+ base::subtle::Atomic32 flag_;
+ base::Lock* write_lock_; // Not owned.
+
+ DISALLOW_COPY_AND_ASSIGN(PollableThreadSafeFlag);
+};
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_POLLABLE_THREAD_SAFE_FLAG_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/process_state.cc b/chromium/third_party/blink/renderer/platform/scheduler/child/process_state.cc
new file mode 100644
index 00000000000..e62069f67de
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/process_state.cc
@@ -0,0 +1,26 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/child/process_state.h"
+
+#include "base/lazy_instance.h"
+
+namespace blink {
+namespace scheduler {
+namespace internal {
+
+namespace {
+
+base::LazyInstance<ProcessState>::Leaky g_process_state;
+
+} // namespace
+
+// static
+ProcessState* ProcessState::Get() {
+ return g_process_state.Pointer();
+}
+
+} // namespace internal
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/process_state.h b/chromium/third_party/blink/renderer/platform/scheduler/child/process_state.h
new file mode 100644
index 00000000000..5b5899d6603
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/process_state.h
@@ -0,0 +1,27 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_PROCESS_STATE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_PROCESS_STATE_H_
+
+#include <atomic>
+
+namespace blink {
+namespace scheduler {
+namespace internal {
+
+// Helper lock-free struct to share main state of the process between threads
+// for recording methods.
+// This class should not be used for synchronization between threads.
+struct ProcessState {
+ static ProcessState* Get();
+
+ std::atomic_bool is_process_backgrounded;
+};
+
+} // namespace internal
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_PROCESS_STATE_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/single_thread_idle_task_runner.cc b/chromium/third_party/blink/renderer/platform/scheduler/child/single_thread_idle_task_runner.cc
new file mode 100644
index 00000000000..fae4dab6c0a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/single_thread_idle_task_runner.cc
@@ -0,0 +1,100 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/scheduler/single_thread_idle_task_runner.h"
+
+#include "base/location.h"
+#include "base/single_thread_task_runner.h"
+#include "base/trace_event/blame_context.h"
+#include "base/trace_event/trace_event.h"
+
+namespace blink {
+namespace scheduler {
+
+SingleThreadIdleTaskRunner::SingleThreadIdleTaskRunner(
+ scoped_refptr<base::SingleThreadTaskRunner> idle_priority_task_runner,
+ Delegate* delegate)
+ : idle_priority_task_runner_(idle_priority_task_runner),
+ delegate_(delegate),
+ blame_context_(nullptr),
+ weak_factory_(this) {
+ DCHECK(!idle_priority_task_runner_ ||
+ idle_priority_task_runner_->RunsTasksInCurrentSequence());
+ weak_scheduler_ptr_ = weak_factory_.GetWeakPtr();
+}
+
+SingleThreadIdleTaskRunner::~SingleThreadIdleTaskRunner() = default;
+
+SingleThreadIdleTaskRunner::Delegate::Delegate() = default;
+
+SingleThreadIdleTaskRunner::Delegate::~Delegate() = default;
+
+bool SingleThreadIdleTaskRunner::RunsTasksInCurrentSequence() const {
+ return idle_priority_task_runner_->RunsTasksInCurrentSequence();
+}
+
+void SingleThreadIdleTaskRunner::PostIdleTask(const base::Location& from_here,
+ IdleTask idle_task) {
+ delegate_->OnIdleTaskPosted();
+ idle_priority_task_runner_->PostTask(
+ from_here, base::BindOnce(&SingleThreadIdleTaskRunner::RunTask,
+ weak_scheduler_ptr_, std::move(idle_task)));
+}
+
+void SingleThreadIdleTaskRunner::PostDelayedIdleTask(
+ const base::Location& from_here,
+ const base::TimeDelta delay,
+ IdleTask idle_task) {
+ base::TimeTicks first_run_time = delegate_->NowTicks() + delay;
+ delayed_idle_tasks_.insert(std::make_pair(
+ first_run_time,
+ std::make_pair(
+ from_here,
+ base::BindOnce(&SingleThreadIdleTaskRunner::RunTask,
+ weak_scheduler_ptr_, std::move(idle_task)))));
+}
+
+void SingleThreadIdleTaskRunner::PostNonNestableIdleTask(
+ const base::Location& from_here,
+ IdleTask idle_task) {
+ delegate_->OnIdleTaskPosted();
+ idle_priority_task_runner_->PostNonNestableTask(
+ from_here, base::BindOnce(&SingleThreadIdleTaskRunner::RunTask,
+ weak_scheduler_ptr_, std::move(idle_task)));
+}
+
+void SingleThreadIdleTaskRunner::EnqueueReadyDelayedIdleTasks() {
+ if (delayed_idle_tasks_.empty())
+ return;
+
+ base::TimeTicks now = delegate_->NowTicks();
+ while (!delayed_idle_tasks_.empty() &&
+ delayed_idle_tasks_.begin()->first <= now) {
+ idle_priority_task_runner_->PostTask(
+ delayed_idle_tasks_.begin()->second.first,
+ std::move(delayed_idle_tasks_.begin()->second.second));
+ delayed_idle_tasks_.erase(delayed_idle_tasks_.begin());
+ }
+}
+
+void SingleThreadIdleTaskRunner::RunTask(IdleTask idle_task) {
+ base::TimeTicks deadline = delegate_->WillProcessIdleTask();
+ TRACE_EVENT1("renderer.scheduler", "SingleThreadIdleTaskRunner::RunTask",
+ "allotted_time_ms",
+ (deadline - base::TimeTicks::Now()).InMillisecondsF());
+ if (blame_context_)
+ blame_context_->Enter();
+ std::move(idle_task).Run(deadline);
+ if (blame_context_)
+ blame_context_->Leave();
+ delegate_->DidProcessIdleTask();
+}
+
+void SingleThreadIdleTaskRunner::SetBlameContext(
+ base::trace_event::BlameContext* blame_context) {
+ blame_context_ = blame_context;
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/task_runner_impl.cc b/chromium/third_party/blink/renderer/platform/scheduler/child/task_runner_impl.cc
new file mode 100644
index 00000000000..56ec26c2728
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/task_runner_impl.cc
@@ -0,0 +1,50 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/child/task_runner_impl.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue.h"
+
+namespace blink {
+namespace scheduler {
+
+scoped_refptr<TaskRunnerImpl> TaskRunnerImpl::Create(
+ scoped_refptr<TaskQueue> task_queue,
+ TaskType task_type) {
+ return base::WrapRefCounted(
+ new TaskRunnerImpl(std::move(task_queue), task_type));
+}
+
+bool TaskRunnerImpl::RunsTasksInCurrentSequence() const {
+ return task_queue_->RunsTasksInCurrentSequence();
+}
+
+TaskRunnerImpl::TaskRunnerImpl(scoped_refptr<TaskQueue> task_queue,
+ TaskType task_type)
+ : task_queue_(std::move(task_queue)), task_type_(task_type) {}
+
+TaskRunnerImpl::~TaskRunnerImpl() = default;
+
+bool TaskRunnerImpl::PostDelayedTask(const base::Location& location,
+ base::OnceClosure task,
+ base::TimeDelta delay) {
+ return task_queue_->PostTaskWithMetadata(TaskQueue::PostedTask(
+ std::move(task), location, delay, base::Nestable::kNestable,
+ static_cast<int>(task_type_)));
+}
+
+bool TaskRunnerImpl::PostNonNestableDelayedTask(const base::Location& location,
+ base::OnceClosure task,
+ base::TimeDelta delay) {
+ return task_queue_->PostTaskWithMetadata(TaskQueue::PostedTask(
+ std::move(task), location, delay, base::Nestable::kNonNestable,
+ static_cast<int>(task_type_)));
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/task_runner_impl.h b/chromium/third_party/blink/renderer/platform/scheduler/child/task_runner_impl.h
new file mode 100644
index 00000000000..e7025d30388
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/task_runner_impl.h
@@ -0,0 +1,54 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_TASK_RUNNER_IMPL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_TASK_RUNNER_IMPL_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/single_thread_task_runner.h"
+#include "base/time/time.h"
+#include "third_party/blink/public/platform/task_type.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+namespace scheduler {
+class TaskQueue;
+
+class PLATFORM_EXPORT TaskRunnerImpl : public base::SingleThreadTaskRunner {
+ public:
+ static scoped_refptr<TaskRunnerImpl> Create(
+ scoped_refptr<TaskQueue> task_queue,
+ TaskType task_type);
+
+ // base::SingleThreadTaskRunner implementation:
+ bool RunsTasksInCurrentSequence() const override;
+
+ TaskQueue* GetTaskQueue() const { return task_queue_.get(); }
+
+ protected:
+ bool PostDelayedTask(const base::Location&,
+ base::OnceClosure,
+ base::TimeDelta) override;
+ bool PostNonNestableDelayedTask(const base::Location&,
+ base::OnceClosure,
+ base::TimeDelta) override;
+
+ private:
+ TaskRunnerImpl(scoped_refptr<TaskQueue> task_queue, TaskType task_type);
+ ~TaskRunnerImpl() override;
+
+ scoped_refptr<TaskQueue> task_queue_;
+ TaskType task_type_;
+
+ DISALLOW_COPY_AND_ASSIGN(TaskRunnerImpl);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_TASK_RUNNER_IMPL_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/web_scheduler.h b/chromium/third_party/blink/renderer/platform/scheduler/child/web_scheduler.h
new file mode 100644
index 00000000000..9d6a22cdcce
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/web_scheduler.h
@@ -0,0 +1,105 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_WEB_SCHEDULER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_WEB_SCHEDULER_H_
+
+#include <memory>
+#include "base/location.h"
+#include "base/single_thread_task_runner.h"
+#include "base/time/time.h"
+#include "third_party/blink/public/platform/scheduler/web_main_thread_scheduler.h"
+#include "third_party/blink/public/platform/web_thread.h"
+#include "third_party/blink/renderer/platform/scheduler/public/page_scheduler.h"
+
+namespace blink {
+
+// This class is used to submit tasks and pass other information from Blink to
+// the platform's scheduler.
+// TODO(skyostil): Replace this class with WebMainThreadScheduler.
+class PLATFORM_EXPORT WebScheduler {
+ public:
+ using RendererPauseHandle =
+ scheduler::WebMainThreadScheduler::RendererPauseHandle;
+
+ virtual ~WebScheduler() = default;
+
+ // Called to prevent any more pending tasks from running. Must be called on
+ // the associated WebThread.
+ virtual void Shutdown() = 0;
+
+ // Returns true if there is high priority work pending on the associated
+ // WebThread and the caller should yield to let the scheduler service that
+ // work. Must be called on the associated WebThread.
+ virtual bool ShouldYieldForHighPriorityWork() = 0;
+
+ // Returns true if a currently running idle task could exceed its deadline
+ // without impacting user experience too much. This should only be used if
+ // there is a task which cannot be pre-empted and is likely to take longer
+ // than the largest expected idle task deadline. It should NOT be polled to
+ // check whether more work can be performed on the current idle task after
+ // its deadline has expired - post a new idle task for the continuation of
+ // the work in this case.
+ // Must be called from the associated WebThread.
+ virtual bool CanExceedIdleDeadlineIfRequired() = 0;
+
+ // Schedule an idle task to run the associated WebThread. For non-critical
+ // tasks which may be reordered relative to other task types and may be
+ // starved for an arbitrarily long time if no idle time is available.
+ // Takes ownership of |IdleTask|. Can be called from any thread.
+ virtual void PostIdleTask(const base::Location&, WebThread::IdleTask) = 0;
+
+ // Like postIdleTask but guarantees that the posted task will not run
+ // nested within an already-running task. Posting an idle task as
+ // non-nestable may not affect when the task gets run, or it could
+ // make it run later than it normally would, but it won't make it
+ // run earlier than it normally would.
+ virtual void PostNonNestableIdleTask(const base::Location&,
+ WebThread::IdleTask) = 0;
+
+ // Returns a task runner for kV8 tasks. Can be called from any thread.
+ virtual base::SingleThreadTaskRunner* V8TaskRunner() = 0;
+
+ // Returns a task runner for compositor tasks. This is intended only to be
+ // used by specific animation and rendering related tasks (e.g. animated GIFS)
+ // and should not generally be used.
+ virtual base::SingleThreadTaskRunner* CompositorTaskRunner() = 0;
+
+ // Creates a new PageScheduler for a given Page. Must be called from the
+ // associated WebThread.
+ virtual std::unique_ptr<PageScheduler> CreatePageScheduler(
+ PageScheduler::Delegate*) = 0;
+
+ // Pauses the scheduler. See WebMainThreadScheduler::PauseRenderer for
+ // details. May only be called from the main thread.
+ virtual std::unique_ptr<RendererPauseHandle> PauseScheduler()
+ WARN_UNUSED_RESULT = 0;
+
+ // Tells the scheduler that a navigation task is pending.
+ // TODO(alexclarke): Long term should this be a task trait?
+ virtual void AddPendingNavigation(
+ scheduler::WebMainThreadScheduler::NavigatingFrameType) = 0;
+
+ // Tells the scheduler that a navigation task is no longer pending.
+ virtual void RemovePendingNavigation(
+ scheduler::WebMainThreadScheduler::NavigatingFrameType) = 0;
+
+ // Returns the current time recognized by the scheduler, which may perhaps
+ // be based on a real or virtual time domain. Used by Timer.
+ virtual base::TimeTicks MonotonicallyIncreasingVirtualTime() const = 0;
+
+ // Test helpers.
+
+ // Return a reference to an underlying WebMainThreadScheduler object.
+ // Can be null if there is no underlying WebMainThreadScheduler
+ // (e.g. worker threads).
+ virtual scheduler::WebMainThreadScheduler*
+ GetWebMainThreadSchedulerForTest() {
+ return nullptr;
+ }
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_WEB_SCHEDULER_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/web_scheduler_impl.cc b/chromium/third_party/blink/renderer/platform/scheduler/child/web_scheduler_impl.cc
new file mode 100644
index 00000000000..eb441b529bc
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/web_scheduler_impl.cc
@@ -0,0 +1,87 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/child/web_scheduler_impl.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/single_thread_task_runner.h"
+#include "third_party/blink/renderer/platform/scheduler/child/task_runner_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/public/non_main_thread_scheduler.h"
+#include "third_party/blink/renderer/platform/scheduler/public/page_scheduler.h"
+
+namespace blink {
+namespace scheduler {
+
+// TODO(kraynov): Ditch kDeprecatedNone here.
+WebSchedulerImpl::WebSchedulerImpl(
+ WebThreadScheduler* thread_scheduler,
+ scoped_refptr<SingleThreadIdleTaskRunner> idle_task_runner,
+ scoped_refptr<TaskQueue> v8_task_runner)
+ : thread_scheduler_(thread_scheduler),
+ idle_task_runner_(idle_task_runner),
+ v8_task_runner_(TaskRunnerImpl::Create(std::move(v8_task_runner),
+ TaskType::kDeprecatedNone)) {}
+
+WebSchedulerImpl::~WebSchedulerImpl() = default;
+
+void WebSchedulerImpl::Shutdown() {
+ thread_scheduler_->Shutdown();
+}
+
+bool WebSchedulerImpl::ShouldYieldForHighPriorityWork() {
+ return thread_scheduler_->ShouldYieldForHighPriorityWork();
+}
+
+bool WebSchedulerImpl::CanExceedIdleDeadlineIfRequired() {
+ return thread_scheduler_->CanExceedIdleDeadlineIfRequired();
+}
+
+void WebSchedulerImpl::RunIdleTask(blink::WebThread::IdleTask task,
+ base::TimeTicks deadline) {
+ std::move(task).Run((deadline - base::TimeTicks()).InSecondsF());
+}
+
+void WebSchedulerImpl::PostIdleTask(const base::Location& location,
+ blink::WebThread::IdleTask task) {
+ DCHECK(idle_task_runner_);
+ idle_task_runner_->PostIdleTask(
+ location,
+ base::BindOnce(&WebSchedulerImpl::RunIdleTask, std::move(task)));
+}
+
+void WebSchedulerImpl::PostNonNestableIdleTask(
+ const base::Location& location,
+ blink::WebThread::IdleTask task) {
+ DCHECK(idle_task_runner_);
+ idle_task_runner_->PostNonNestableIdleTask(
+ location,
+ base::BindOnce(&WebSchedulerImpl::RunIdleTask, std::move(task)));
+}
+
+base::SingleThreadTaskRunner* WebSchedulerImpl::V8TaskRunner() {
+ return v8_task_runner_.get();
+}
+
+base::SingleThreadTaskRunner* WebSchedulerImpl::CompositorTaskRunner() {
+ return nullptr;
+}
+
+std::unique_ptr<blink::PageScheduler> WebSchedulerImpl::CreatePageScheduler(
+ PageScheduler::Delegate* delegate) {
+ NOTREACHED();
+ return nullptr;
+}
+
+std::unique_ptr<WebSchedulerImpl::RendererPauseHandle>
+WebSchedulerImpl::PauseScheduler() {
+ return nullptr;
+}
+
+base::TimeTicks WebSchedulerImpl::MonotonicallyIncreasingVirtualTime() const {
+ return base::TimeTicks::Now();
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/web_scheduler_impl.h b/chromium/third_party/blink/renderer/platform/scheduler/child/web_scheduler_impl.h
new file mode 100644
index 00000000000..174b7174821
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/web_scheduler_impl.h
@@ -0,0 +1,67 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_WEB_SCHEDULER_IMPL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_WEB_SCHEDULER_IMPL_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/time/time.h"
+#include "third_party/blink/public/platform/web_thread.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue.h"
+#include "third_party/blink/renderer/platform/scheduler/child/web_scheduler.h"
+
+namespace blink {
+namespace scheduler {
+
+class SingleThreadIdleTaskRunner;
+class TaskRunnerImpl;
+class WebThreadScheduler;
+
+class PLATFORM_EXPORT WebSchedulerImpl : public WebScheduler {
+ public:
+ WebSchedulerImpl(WebThreadScheduler* thread_scheduler,
+ scoped_refptr<SingleThreadIdleTaskRunner> idle_task_runner,
+ scoped_refptr<TaskQueue> v8_task_runner);
+ ~WebSchedulerImpl() override;
+
+ // WebScheduler implementation:
+ void Shutdown() override;
+ bool ShouldYieldForHighPriorityWork() override;
+ bool CanExceedIdleDeadlineIfRequired() override;
+ void PostIdleTask(const base::Location& location,
+ WebThread::IdleTask task) override;
+ void PostNonNestableIdleTask(const base::Location& location,
+ WebThread::IdleTask task) override;
+ base::SingleThreadTaskRunner* V8TaskRunner() override;
+ base::SingleThreadTaskRunner* CompositorTaskRunner() override;
+ std::unique_ptr<PageScheduler> CreatePageScheduler(
+ PageScheduler::Delegate*) override;
+ std::unique_ptr<RendererPauseHandle> PauseScheduler() override
+ WARN_UNUSED_RESULT;
+ void AddPendingNavigation(
+ scheduler::WebMainThreadScheduler::NavigatingFrameType type) override {}
+ void RemovePendingNavigation(
+ scheduler::WebMainThreadScheduler::NavigatingFrameType type) override {}
+
+ // Returns TimeTicks::Now() by default.
+ base::TimeTicks MonotonicallyIncreasingVirtualTime() const override;
+
+ private:
+ static void RunIdleTask(WebThread::IdleTask task, base::TimeTicks deadline);
+
+ WebThreadScheduler* thread_scheduler_; // NOT OWNED
+ scoped_refptr<SingleThreadIdleTaskRunner> idle_task_runner_;
+ scoped_refptr<TaskRunnerImpl> v8_task_runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebSchedulerImpl);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_WEB_SCHEDULER_IMPL_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/webthread_base.cc b/chromium/third_party/blink/renderer/platform/scheduler/child/webthread_base.cc
new file mode 100644
index 00000000000..ad1ce16ce72
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/webthread_base.cc
@@ -0,0 +1,144 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// An implementation of WebThread in terms of base::MessageLoop and
+// base::Thread
+
+#include "third_party/blink/public/platform/scheduler/child/webthread_base.h"
+
+#include <memory>
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/location.h"
+#include "base/message_loop/message_loop.h"
+#include "base/pending_task.h"
+#include "base/threading/platform_thread.h"
+#include "third_party/blink/public/platform/scheduler/single_thread_idle_task_runner.h"
+#include "third_party/blink/renderer/platform/scheduler/child/webthread_impl_for_worker_scheduler.h"
+#include "third_party/blink/renderer/platform/scheduler/utility/webthread_impl_for_utility_thread.h"
+#include "third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.h"
+
+namespace blink {
+namespace scheduler {
+
+class WebThreadBase::TaskObserverAdapter
+ : public base::MessageLoop::TaskObserver {
+ public:
+ explicit TaskObserverAdapter(WebThread::TaskObserver* observer)
+ : observer_(observer) {}
+
+ void WillProcessTask(const base::PendingTask& pending_task) override {
+ observer_->WillProcessTask();
+ }
+
+ void DidProcessTask(const base::PendingTask& pending_task) override {
+ observer_->DidProcessTask();
+ }
+
+ private:
+ WebThread::TaskObserver* observer_;
+};
+
+WebThreadBase::WebThreadBase() = default;
+
+WebThreadBase::~WebThreadBase() {
+ for (auto& observer_entry : task_observer_map_) {
+ delete observer_entry.second;
+ }
+}
+
+void WebThreadBase::AddTaskObserver(TaskObserver* observer) {
+ CHECK(IsCurrentThread());
+ std::pair<TaskObserverMap::iterator, bool> result =
+ task_observer_map_.insert(std::make_pair(observer, nullptr));
+ if (result.second)
+ result.first->second = new TaskObserverAdapter(observer);
+ AddTaskObserverInternal(result.first->second);
+}
+
+void WebThreadBase::RemoveTaskObserver(TaskObserver* observer) {
+ CHECK(IsCurrentThread());
+ TaskObserverMap::iterator iter = task_observer_map_.find(observer);
+ if (iter == task_observer_map_.end())
+ return;
+ RemoveTaskObserverInternal(iter->second);
+ delete iter->second;
+ task_observer_map_.erase(iter);
+}
+
+void WebThreadBase::AddTaskTimeObserver(TaskTimeObserver* task_time_observer) {
+ AddTaskTimeObserverInternal(task_time_observer);
+}
+
+void WebThreadBase::RemoveTaskTimeObserver(
+ TaskTimeObserver* task_time_observer) {
+ RemoveTaskTimeObserverInternal(task_time_observer);
+}
+
+void WebThreadBase::AddTaskObserverInternal(
+ base::MessageLoop::TaskObserver* observer) {
+ base::MessageLoop::current()->AddTaskObserver(observer);
+}
+
+void WebThreadBase::RemoveTaskObserverInternal(
+ base::MessageLoop::TaskObserver* observer) {
+ base::MessageLoop::current()->RemoveTaskObserver(observer);
+}
+
+// static
+void WebThreadBase::RunWebThreadIdleTask(blink::WebThread::IdleTask idle_task,
+ base::TimeTicks deadline) {
+ std::move(idle_task).Run((deadline - base::TimeTicks()).InSecondsF());
+}
+
+void WebThreadBase::PostIdleTask(const base::Location& location,
+ IdleTask idle_task) {
+ GetIdleTaskRunner()->PostIdleTask(
+ location, base::BindOnce(&WebThreadBase::RunWebThreadIdleTask,
+ std::move(idle_task)));
+}
+
+bool WebThreadBase::IsCurrentThread() const {
+ return GetTaskRunner()->BelongsToCurrentThread();
+}
+
+namespace {
+
+class WebThreadForCompositor : public WebThreadImplForWorkerScheduler {
+ public:
+ explicit WebThreadForCompositor(const WebThreadCreationParams& params)
+ : WebThreadImplForWorkerScheduler(params) {
+ Init();
+ }
+ ~WebThreadForCompositor() override = default;
+
+ private:
+ // WebThreadImplForWorkerScheduler:
+ std::unique_ptr<blink::scheduler::NonMainThreadScheduler>
+ CreateNonMainThreadScheduler() override {
+ return std::make_unique<CompositorThreadScheduler>(
+ GetThread(), TaskQueueManager::TakeOverCurrentThread());
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(WebThreadForCompositor);
+};
+
+} // namespace
+
+std::unique_ptr<WebThreadBase> WebThreadBase::CreateWorkerThread(
+ const WebThreadCreationParams& params) {
+ return std::make_unique<WebThreadImplForWorkerScheduler>(params);
+}
+
+std::unique_ptr<WebThreadBase> WebThreadBase::CreateCompositorThread(
+ const WebThreadCreationParams& params) {
+ return std::make_unique<WebThreadForCompositor>(params);
+}
+
+std::unique_ptr<WebThreadBase> WebThreadBase::InitializeUtilityThread() {
+ return std::make_unique<WebThreadImplForUtilityThread>();
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/webthread_impl_for_worker_scheduler.cc b/chromium/third_party/blink/renderer/platform/scheduler/child/webthread_impl_for_worker_scheduler.cc
new file mode 100644
index 00000000000..8b1777f7f69
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/webthread_impl_for_worker_scheduler.cc
@@ -0,0 +1,128 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/child/webthread_impl_for_worker_scheduler.h"
+
+#include <memory>
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/time/default_tick_clock.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue.h"
+#include "third_party/blink/renderer/platform/scheduler/child/web_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/child/worker_scheduler_proxy.h"
+#include "third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h"
+
+namespace blink {
+namespace scheduler {
+
+WebThreadImplForWorkerScheduler::WebThreadImplForWorkerScheduler(
+ const WebThreadCreationParams& params)
+ : thread_(new base::Thread(params.name ? params.name : std::string())),
+ thread_type_(params.thread_type),
+ worker_scheduler_proxy_(
+ params.frame_scheduler
+ ? std::make_unique<WorkerSchedulerProxy>(params.frame_scheduler)
+ : nullptr) {
+ bool started = thread_->StartWithOptions(params.thread_options);
+ CHECK(started);
+ thread_task_runner_ = thread_->task_runner();
+}
+
+void WebThreadImplForWorkerScheduler::Init() {
+ base::WaitableEvent completion(
+ base::WaitableEvent::ResetPolicy::AUTOMATIC,
+ base::WaitableEvent::InitialState::NOT_SIGNALED);
+ thread_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&WebThreadImplForWorkerScheduler::InitOnThread,
+ base::Unretained(this), &completion));
+ completion.Wait();
+}
+
+WebThreadImplForWorkerScheduler::~WebThreadImplForWorkerScheduler() {
+ // We want to avoid blocking main thread when the thread was already
+ // shut down, but calling ShutdownOnThread twice does not cause any problems.
+ if (!was_shutdown_on_thread_.IsSet()) {
+ base::WaitableEvent completion(
+ base::WaitableEvent::ResetPolicy::AUTOMATIC,
+ base::WaitableEvent::InitialState::NOT_SIGNALED);
+ thread_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&WebThreadImplForWorkerScheduler::ShutdownOnThread,
+ base::Unretained(this), &completion));
+ completion.Wait();
+ }
+ thread_->Stop();
+}
+
+void WebThreadImplForWorkerScheduler::InitOnThread(
+ base::WaitableEvent* completion) {
+ // TODO(alexclarke): Do we need to unify virtual time for workers and the
+ // main thread?
+ non_main_thread_scheduler_ = CreateNonMainThreadScheduler();
+ non_main_thread_scheduler_->Init();
+ task_queue_ = non_main_thread_scheduler_->DefaultTaskQueue();
+ idle_task_runner_ = non_main_thread_scheduler_->IdleTaskRunner();
+ web_scheduler_.reset(
+ new WebSchedulerImpl(non_main_thread_scheduler_.get(),
+ non_main_thread_scheduler_->IdleTaskRunner(),
+ non_main_thread_scheduler_->DefaultTaskQueue()));
+ base::MessageLoop::current()->AddDestructionObserver(this);
+ completion->Signal();
+}
+
+void WebThreadImplForWorkerScheduler::ShutdownOnThread(
+ base::WaitableEvent* completion) {
+ was_shutdown_on_thread_.Set();
+
+ task_queue_ = nullptr;
+ idle_task_runner_ = nullptr;
+ web_scheduler_ = nullptr;
+ non_main_thread_scheduler_ = nullptr;
+
+ if (completion)
+ completion->Signal();
+}
+
+std::unique_ptr<NonMainThreadScheduler>
+WebThreadImplForWorkerScheduler::CreateNonMainThreadScheduler() {
+ return NonMainThreadScheduler::Create(thread_type_,
+ worker_scheduler_proxy_.get());
+}
+
+void WebThreadImplForWorkerScheduler::WillDestroyCurrentMessageLoop() {
+ ShutdownOnThread(nullptr);
+}
+
+blink::PlatformThreadId WebThreadImplForWorkerScheduler::ThreadId() const {
+ return thread_->GetThreadId();
+}
+
+blink::WebScheduler* WebThreadImplForWorkerScheduler::Scheduler() const {
+ return web_scheduler_.get();
+}
+
+SingleThreadIdleTaskRunner* WebThreadImplForWorkerScheduler::GetIdleTaskRunner()
+ const {
+ return idle_task_runner_.get();
+}
+
+scoped_refptr<base::SingleThreadTaskRunner>
+WebThreadImplForWorkerScheduler::GetTaskRunner() const {
+ return task_queue_;
+}
+
+void WebThreadImplForWorkerScheduler::AddTaskObserverInternal(
+ base::MessageLoop::TaskObserver* observer) {
+ non_main_thread_scheduler_->AddTaskObserver(observer);
+}
+
+void WebThreadImplForWorkerScheduler::RemoveTaskObserverInternal(
+ base::MessageLoop::TaskObserver* observer) {
+ non_main_thread_scheduler_->RemoveTaskObserver(observer);
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/webthread_impl_for_worker_scheduler.h b/chromium/third_party/blink/renderer/platform/scheduler/child/webthread_impl_for_worker_scheduler.h
new file mode 100644
index 00000000000..3643ac566c5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/webthread_impl_for_worker_scheduler.h
@@ -0,0 +1,88 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_WEBTHREAD_IMPL_FOR_WORKER_SCHEDULER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_WEBTHREAD_IMPL_FOR_WORKER_SCHEDULER_H_
+
+#include "base/message_loop/message_loop.h"
+#include "base/synchronization/atomic_flag.h"
+#include "base/threading/thread.h"
+#include "third_party/blink/public/platform/scheduler/child/webthread_base.h"
+#include "third_party/blink/public/platform/web_private_ptr.h"
+
+namespace base {
+class WaitableEvent;
+}
+
+namespace blink {
+class WebScheduler;
+}
+
+namespace blink {
+namespace scheduler {
+class SingleThreadIdleTaskRunner;
+class TaskQueue;
+class WebSchedulerImpl;
+class NonMainThreadScheduler;
+class WorkerSchedulerProxy;
+
+class PLATFORM_EXPORT WebThreadImplForWorkerScheduler
+ : public WebThreadBase,
+ public base::MessageLoop::DestructionObserver {
+ public:
+ explicit WebThreadImplForWorkerScheduler(
+ const WebThreadCreationParams& params);
+ ~WebThreadImplForWorkerScheduler() override;
+
+ // WebThread implementation.
+ WebScheduler* Scheduler() const override;
+ PlatformThreadId ThreadId() const override;
+ scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() const override;
+
+ // WebThreadBase implementation.
+ scheduler::SingleThreadIdleTaskRunner* GetIdleTaskRunner() const override;
+ void Init() override;
+
+ // base::MessageLoop::DestructionObserver implementation.
+ void WillDestroyCurrentMessageLoop() override;
+
+ scheduler::NonMainThreadScheduler* GetNonMainThreadScheduler() {
+ return non_main_thread_scheduler_.get();
+ }
+
+ protected:
+ virtual std::unique_ptr<NonMainThreadScheduler>
+ CreateNonMainThreadScheduler();
+
+ base::Thread* GetThread() const { return thread_.get(); }
+
+ scheduler::WorkerSchedulerProxy* worker_scheduler_proxy() const {
+ return worker_scheduler_proxy_.get();
+ }
+
+ private:
+ void AddTaskObserverInternal(
+ base::MessageLoop::TaskObserver* observer) override;
+ void RemoveTaskObserverInternal(
+ base::MessageLoop::TaskObserver* observer) override;
+
+ void InitOnThread(base::WaitableEvent* completion);
+ void ShutdownOnThread(base::WaitableEvent* completion);
+
+ std::unique_ptr<base::Thread> thread_;
+ const WebThreadType thread_type_;
+ std::unique_ptr<scheduler::WorkerSchedulerProxy> worker_scheduler_proxy_;
+ std::unique_ptr<scheduler::NonMainThreadScheduler> non_main_thread_scheduler_;
+ std::unique_ptr<scheduler::WebSchedulerImpl> web_scheduler_;
+ scoped_refptr<base::SingleThreadTaskRunner> thread_task_runner_;
+ scoped_refptr<TaskQueue> task_queue_;
+ scoped_refptr<scheduler::SingleThreadIdleTaskRunner> idle_task_runner_;
+
+ base::AtomicFlag was_shutdown_on_thread_;
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_WEBTHREAD_IMPL_FOR_WORKER_SCHEDULER_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/webthread_impl_for_worker_scheduler_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/child/webthread_impl_for_worker_scheduler_unittest.cc
new file mode 100644
index 00000000000..c7c44701b78
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/webthread_impl_for_worker_scheduler_unittest.cc
@@ -0,0 +1,205 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/child/webthread_impl_for_worker_scheduler.h"
+
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/synchronization/waitable_event.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/scheduler/child/web_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h"
+#include "third_party/blink/renderer/platform/web_task_runner.h"
+
+using testing::_;
+using testing::AnyOf;
+using testing::ElementsAre;
+using testing::Invoke;
+
+namespace blink {
+namespace scheduler {
+namespace webthread_impl_for_worker_scheduler_unittest {
+
+class MockTask {
+ public:
+ MOCK_METHOD0(Run, void());
+};
+
+class MockIdleTask {
+ public:
+ MOCK_METHOD1(Run, void(double deadline));
+};
+
+class TestObserver : public blink::WebThread::TaskObserver {
+ public:
+ explicit TestObserver(std::string* calls) : calls_(calls) {}
+
+ ~TestObserver() override = default;
+
+ void WillProcessTask() override { calls_->append(" willProcessTask"); }
+
+ void DidProcessTask() override { calls_->append(" didProcessTask"); }
+
+ private:
+ std::string* calls_; // NOT OWNED
+};
+
+void RunTestTask(std::string* calls) {
+ calls->append(" run");
+}
+
+void AddTaskObserver(WebThreadImplForWorkerScheduler* thread,
+ TestObserver* observer) {
+ thread->AddTaskObserver(observer);
+}
+
+void RemoveTaskObserver(WebThreadImplForWorkerScheduler* thread,
+ TestObserver* observer) {
+ thread->RemoveTaskObserver(observer);
+}
+
+void ShutdownOnThread(WebThreadImplForWorkerScheduler* thread) {
+ WebSchedulerImpl* web_scheduler_impl =
+ static_cast<WebSchedulerImpl*>(thread->Scheduler());
+ web_scheduler_impl->Shutdown();
+}
+
+class WebThreadImplForWorkerSchedulerTest : public testing::Test {
+ public:
+ WebThreadImplForWorkerSchedulerTest() = default;
+
+ ~WebThreadImplForWorkerSchedulerTest() override = default;
+
+ void SetUp() override {
+ thread_.reset(new WebThreadImplForWorkerScheduler(
+ WebThreadCreationParams(WebThreadType::kTestThread)));
+ thread_->Init();
+ }
+
+ void RunOnWorkerThread(const base::Location& from_here,
+ base::OnceClosure task) {
+ base::WaitableEvent completion(
+ base::WaitableEvent::ResetPolicy::AUTOMATIC,
+ base::WaitableEvent::InitialState::NOT_SIGNALED);
+ thread_->GetTaskRunner()->PostTask(
+ from_here,
+ base::BindOnce(
+ &WebThreadImplForWorkerSchedulerTest::RunOnWorkerThreadTask,
+ base::Unretained(this), std::move(task), &completion));
+ completion.Wait();
+ }
+
+ protected:
+ void RunOnWorkerThreadTask(base::OnceClosure task,
+ base::WaitableEvent* completion) {
+ std::move(task).Run();
+ completion->Signal();
+ }
+
+ std::unique_ptr<WebThreadImplForWorkerScheduler> thread_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebThreadImplForWorkerSchedulerTest);
+};
+
+TEST_F(WebThreadImplForWorkerSchedulerTest, TestDefaultTask) {
+ MockTask task;
+ base::WaitableEvent completion(
+ base::WaitableEvent::ResetPolicy::AUTOMATIC,
+ base::WaitableEvent::InitialState::NOT_SIGNALED);
+
+ EXPECT_CALL(task, Run());
+ ON_CALL(task, Run()).WillByDefault(Invoke([&completion]() {
+ completion.Signal();
+ }));
+
+ PostCrossThreadTask(
+ *thread_->GetTaskRunner(), FROM_HERE,
+ CrossThreadBind(&MockTask::Run, WTF::CrossThreadUnretained(&task)));
+ completion.Wait();
+}
+
+TEST_F(WebThreadImplForWorkerSchedulerTest,
+ TestTaskExecutedBeforeThreadDeletion) {
+ MockTask task;
+ base::WaitableEvent completion(
+ base::WaitableEvent::ResetPolicy::AUTOMATIC,
+ base::WaitableEvent::InitialState::NOT_SIGNALED);
+
+ EXPECT_CALL(task, Run());
+ ON_CALL(task, Run()).WillByDefault(Invoke([&completion]() {
+ completion.Signal();
+ }));
+
+ PostCrossThreadTask(
+ *thread_->GetTaskRunner(), FROM_HERE,
+ CrossThreadBind(&MockTask::Run, WTF::CrossThreadUnretained(&task)));
+ thread_.reset();
+}
+
+TEST_F(WebThreadImplForWorkerSchedulerTest, TestIdleTask) {
+ MockIdleTask task;
+ base::WaitableEvent completion(
+ base::WaitableEvent::ResetPolicy::AUTOMATIC,
+ base::WaitableEvent::InitialState::NOT_SIGNALED);
+
+ EXPECT_CALL(task, Run(_));
+ ON_CALL(task, Run(_)).WillByDefault(Invoke([&completion](double) {
+ completion.Signal();
+ }));
+
+ thread_->PostIdleTask(
+ FROM_HERE, base::BindOnce(&MockIdleTask::Run, WTF::Unretained(&task)));
+ // We need to post a wake-up task or idle work will never happen.
+ PostDelayedCrossThreadTask(*thread_->GetTaskRunner(), FROM_HERE,
+ CrossThreadBind([] {}),
+ TimeDelta::FromMilliseconds(50));
+
+ completion.Wait();
+}
+
+TEST_F(WebThreadImplForWorkerSchedulerTest, TestTaskObserver) {
+ std::string calls;
+ TestObserver observer(&calls);
+
+ RunOnWorkerThread(FROM_HERE,
+ base::BindOnce(&AddTaskObserver, thread_.get(), &observer));
+ PostCrossThreadTask(
+ *thread_->GetTaskRunner(), FROM_HERE,
+ CrossThreadBind(&RunTestTask, WTF::CrossThreadUnretained(&calls)));
+ RunOnWorkerThread(
+ FROM_HERE, base::BindOnce(&RemoveTaskObserver, thread_.get(), &observer));
+
+ // We need to be careful what we test here. We want to make sure the
+ // observers are un in the expected order before and after the task.
+ // Sometimes we get an internal scheduler task running before or after
+ // TestTask as well. This is not a bug, and we need to make sure the test
+ // doesn't fail when that happens.
+ EXPECT_THAT(calls, testing::HasSubstr("willProcessTask run didProcessTask"));
+}
+
+TEST_F(WebThreadImplForWorkerSchedulerTest, TestShutdown) {
+ MockTask task;
+ MockTask delayed_task;
+
+ EXPECT_CALL(task, Run()).Times(0);
+ EXPECT_CALL(delayed_task, Run()).Times(0);
+
+ RunOnWorkerThread(FROM_HERE,
+ base::BindOnce(&ShutdownOnThread, thread_.get()));
+ PostCrossThreadTask(
+ *thread_->GetTaskRunner(), FROM_HERE,
+ CrossThreadBind(&MockTask::Run, WTF::CrossThreadUnretained(&task)));
+ PostDelayedCrossThreadTask(
+ *thread_->GetTaskRunner(), FROM_HERE,
+ CrossThreadBind(&MockTask::Run,
+ WTF::CrossThreadUnretained(&delayed_task)),
+ TimeDelta::FromMilliseconds(50));
+ thread_.reset();
+}
+
+} // namespace webthread_impl_for_worker_scheduler_unittest
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/worker_global_scope_scheduler.cc b/chromium/third_party/blink/renderer/platform/scheduler/child/worker_global_scope_scheduler.cc
new file mode 100644
index 00000000000..891a95f1162
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/worker_global_scope_scheduler.cc
@@ -0,0 +1,88 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/child/worker_global_scope_scheduler.h"
+
+#include "third_party/blink/renderer/platform/scheduler/child/task_runner_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/public/non_main_thread_scheduler.h"
+
+namespace blink {
+namespace scheduler {
+
+WorkerGlobalScopeScheduler::WorkerGlobalScopeScheduler(
+ NonMainThreadScheduler* non_main_thread_scheduler) {
+ task_queue_ = non_main_thread_scheduler->CreateTaskRunner();
+}
+
+WorkerGlobalScopeScheduler::~WorkerGlobalScopeScheduler() {
+#if DCHECK_IS_ON()
+ DCHECK(is_disposed_);
+#endif
+}
+
+std::unique_ptr<FrameOrWorkerGlobalScopeScheduler::ActiveConnectionHandle>
+WorkerGlobalScopeScheduler::OnActiveConnectionCreated() {
+ return nullptr;
+}
+
+void WorkerGlobalScopeScheduler::Dispose() {
+ task_queue_->ShutdownTaskQueue();
+#if DCHECK_IS_ON()
+ is_disposed_ = true;
+#endif
+}
+
+scoped_refptr<base::SingleThreadTaskRunner>
+WorkerGlobalScopeScheduler::GetTaskRunner(TaskType type) const {
+ switch (type) {
+ case TaskType::kDeprecatedNone:
+ case TaskType::kDOMManipulation:
+ case TaskType::kUserInteraction:
+ case TaskType::kNetworking:
+ case TaskType::kNetworkingControl:
+ case TaskType::kHistoryTraversal:
+ case TaskType::kEmbed:
+ case TaskType::kMediaElementEvent:
+ case TaskType::kCanvasBlobSerialization:
+ case TaskType::kMicrotask:
+ case TaskType::kJavascriptTimer:
+ case TaskType::kRemoteEvent:
+ case TaskType::kWebSocket:
+ case TaskType::kPostedMessage:
+ case TaskType::kUnshippedPortMessage:
+ case TaskType::kFileReading:
+ case TaskType::kDatabaseAccess:
+ case TaskType::kPresentation:
+ case TaskType::kSensor:
+ case TaskType::kPerformanceTimeline:
+ case TaskType::kWebGL:
+ case TaskType::kIdleTask:
+ case TaskType::kMiscPlatformAPI:
+ case TaskType::kUnspecedTimer:
+ case TaskType::kInternalLoading:
+ case TaskType::kUnthrottled:
+ case TaskType::kInternalTest:
+ case TaskType::kInternalWebCrypto:
+ case TaskType::kInternalIndexedDB:
+ case TaskType::kInternalMedia:
+ case TaskType::kInternalMediaRealTime:
+ case TaskType::kInternalIPC:
+ case TaskType::kInternalUserInteraction:
+ case TaskType::kInternalInspector:
+ case TaskType::kInternalAnimation:
+ // UnthrottledTaskRunner is generally discouraged in future.
+ // TODO(nhiroki): Identify which tasks can be throttled / suspendable and
+ // move them into other task runners. See also comments in
+ // Get(LocalFrame). (https://crbug.com/670534)
+ return TaskRunnerImpl::Create(task_queue_, type);
+ case TaskType::kCount:
+ NOTREACHED();
+ break;
+ }
+ NOTREACHED();
+ return nullptr;
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/worker_global_scope_scheduler.h b/chromium/third_party/blink/renderer/platform/scheduler/child/worker_global_scope_scheduler.h
new file mode 100644
index 00000000000..4493e189c6d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/worker_global_scope_scheduler.h
@@ -0,0 +1,55 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_WORKER_GLOBAL_SCOPE_SCHEDULER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_WORKER_GLOBAL_SCOPE_SCHEDULER_H_
+
+#include "base/single_thread_task_runner.h"
+#include "third_party/blink/public/platform/task_type.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue.h"
+#include "third_party/blink/renderer/platform/scheduler/public/frame_or_worker_global_scope_scheduler.h"
+
+namespace blink {
+
+namespace scheduler {
+
+class NonMainThreadScheduler;
+
+// A scheduler provides per-global-scope task queues. This is constructed when a
+// global scope is created and destructed when it's closed.
+//
+// Unless stated otherwise, all methods must be called on the worker thread.
+class PLATFORM_EXPORT WorkerGlobalScopeScheduler
+ : public FrameOrWorkerGlobalScopeScheduler {
+ public:
+ explicit WorkerGlobalScopeScheduler(
+ NonMainThreadScheduler* non_main_thread_scheduler);
+ ~WorkerGlobalScopeScheduler() override;
+
+ std::unique_ptr<ActiveConnectionHandle> OnActiveConnectionCreated() override;
+
+ // Unregisters the task queues and cancels tasks in them.
+ void Dispose();
+
+ // Returns a task runner that is suitable with the given task type. This can
+ // be called from any thread.
+ //
+ // This must be called only from WorkerThread::GetTaskRunner().
+ scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(TaskType) const;
+
+ // TODO(nhiroki): Add mechanism to throttle/suspend tasks in response to the
+ // state of the parent document (https://crbug.com/670534).
+
+ private:
+ scoped_refptr<TaskQueue> task_queue_;
+
+#if DCHECK_IS_ON()
+ bool is_disposed_ = false;
+#endif
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_WORKER_GLOBAL_SCOPE_SCHEDULER_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/worker_global_scope_scheduler_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/child/worker_global_scope_scheduler_unittest.cc
new file mode 100644
index 00000000000..a38cde67c5a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/worker_global_scope_scheduler_unittest.cc
@@ -0,0 +1,91 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/child/worker_global_scope_scheduler.h"
+
+#include <memory>
+#include "base/macros.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "base/test/test_simple_task_runner.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/scheduler/test/task_queue_manager_for_test.h"
+#include "third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+
+using testing::ElementsAreArray;
+
+namespace blink {
+namespace scheduler {
+// To avoid symbol collisions in jumbo builds.
+namespace worker_global_scope_scheduler_unittest {
+
+void AppendToVectorTestTask(std::vector<std::string>* vector,
+ std::string value) {
+ vector->push_back(value);
+}
+
+class WorkerGlobalScopeSchedulerTest : public testing::Test {
+ public:
+ WorkerGlobalScopeSchedulerTest()
+ : mock_task_runner_(new base::TestSimpleTaskRunner()),
+ scheduler_(new WorkerThreadScheduler(
+ WebThreadType::kTestThread,
+ TaskQueueManagerForTest::Create(nullptr,
+ mock_task_runner_,
+ &clock_),
+ nullptr /* proxy */)) {
+ clock_.Advance(base::TimeDelta::FromMicroseconds(5000));
+ }
+
+ ~WorkerGlobalScopeSchedulerTest() override = default;
+
+ void SetUp() override {
+ scheduler_->Init();
+ global_scope_scheduler_ =
+ std::make_unique<WorkerGlobalScopeScheduler>(scheduler_.get());
+ }
+
+ void RunUntilIdle() { mock_task_runner_->RunUntilIdle(); }
+
+ // Helper for posting a task.
+ void PostTestTask(std::vector<std::string>* run_order,
+ const std::string& task_descriptor) {
+ global_scope_scheduler_->GetTaskRunner(TaskType::kInternalTest)
+ ->PostTask(FROM_HERE,
+ WTF::Bind(&AppendToVectorTestTask,
+ WTF::Unretained(run_order), task_descriptor));
+ }
+
+ protected:
+ base::SimpleTestTickClock clock_;
+ scoped_refptr<base::TestSimpleTaskRunner> mock_task_runner_;
+
+ std::unique_ptr<WorkerThreadScheduler> scheduler_;
+ std::unique_ptr<WorkerGlobalScopeScheduler> global_scope_scheduler_;
+
+ DISALLOW_COPY_AND_ASSIGN(WorkerGlobalScopeSchedulerTest);
+};
+
+TEST_F(WorkerGlobalScopeSchedulerTest, TestPostTasks) {
+ std::vector<std::string> run_order;
+ PostTestTask(&run_order, "T1");
+ PostTestTask(&run_order, "T2");
+ RunUntilIdle();
+ PostTestTask(&run_order, "T3");
+ RunUntilIdle();
+ EXPECT_THAT(run_order, testing::ElementsAre("T1", "T2", "T3"));
+
+ // Tasks should not run after the scheduler is disposed of.
+ global_scope_scheduler_->Dispose();
+ run_order.clear();
+ PostTestTask(&run_order, "T4");
+ PostTestTask(&run_order, "T5");
+ RunUntilIdle();
+ EXPECT_TRUE(run_order.empty());
+}
+
+} // namespace worker_global_scope_scheduler_unittest
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/worker_metrics_helper.cc b/chromium/third_party/blink/renderer/platform/scheduler/child/worker_metrics_helper.cc
new file mode 100644
index 00000000000..f9ea5be00ef
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/worker_metrics_helper.cc
@@ -0,0 +1,66 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/child/worker_metrics_helper.h"
+
+#include "third_party/blink/renderer/platform/scheduler/child/process_state.h"
+
+namespace blink {
+namespace scheduler {
+
+WorkerMetricsHelper::WorkerMetricsHelper(WebThreadType thread_type)
+ : MetricsHelper(thread_type),
+ dedicated_worker_per_task_type_duration_reporter_(
+ "RendererScheduler.TaskDurationPerTaskType.DedicatedWorker"),
+ dedicated_worker_per_task_type_cpu_duration_reporter_(
+ "RendererScheduler.TaskCPUDurationPerTaskType.DedicatedWorker"),
+ dedicated_worker_per_parent_frame_status_duration_reporter_(
+ "RendererScheduler.TaskDurationPerFrameOriginType.DedicatedWorker"),
+ background_dedicated_worker_per_parent_frame_status_duration_reporter_(
+ "RendererScheduler.TaskDurationPerFrameOriginType.DedicatedWorker."
+ "Background") {}
+
+WorkerMetricsHelper::~WorkerMetricsHelper() {}
+
+void WorkerMetricsHelper::SetParentFrameType(FrameOriginType frame_type) {
+ parent_frame_type_ = frame_type;
+}
+
+void WorkerMetricsHelper::RecordTaskMetrics(
+ WorkerTaskQueue* queue,
+ const TaskQueue::Task& task,
+ base::TimeTicks start_time,
+ base::TimeTicks end_time,
+ base::Optional<base::TimeDelta> thread_time) {
+ if (ShouldDiscardTask(queue, task, start_time, end_time, thread_time))
+ return;
+
+ MetricsHelper::RecordCommonTaskMetrics(queue, task, start_time, end_time,
+ thread_time);
+
+ bool backgrounded = internal::ProcessState::Get()->is_process_backgrounded;
+
+ if (thread_type_ == WebThreadType::kDedicatedWorkerThread) {
+ TaskType task_type = static_cast<TaskType>(task.task_type());
+ dedicated_worker_per_task_type_duration_reporter_.RecordTask(
+ task_type, end_time - start_time);
+ if (thread_time) {
+ dedicated_worker_per_task_type_cpu_duration_reporter_.RecordTask(
+ task_type, thread_time.value());
+ }
+
+ if (parent_frame_type_) {
+ dedicated_worker_per_parent_frame_status_duration_reporter_.RecordTask(
+ parent_frame_type_.value(), end_time - start_time);
+
+ if (backgrounded) {
+ background_dedicated_worker_per_parent_frame_status_duration_reporter_
+ .RecordTask(parent_frame_type_.value(), end_time - start_time);
+ }
+ }
+ }
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/worker_metrics_helper.h b/chromium/third_party/blink/renderer/platform/scheduler/child/worker_metrics_helper.h
new file mode 100644
index 00000000000..b2837920399
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/worker_metrics_helper.h
@@ -0,0 +1,48 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_WORKER_METRICS_HELPER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_WORKER_METRICS_HELPER_H_
+
+#include "third_party/blink/public/platform/task_type.h"
+#include "third_party/blink/renderer/platform/scheduler/child/metrics_helper.h"
+#include "third_party/blink/renderer/platform/scheduler/child/worker_task_queue.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/frame_origin_type.h"
+#include "third_party/blink/renderer/platform/scheduler/util/thread_load_tracker.h"
+
+namespace blink {
+namespace scheduler {
+
+class PLATFORM_EXPORT WorkerMetricsHelper : public MetricsHelper {
+ public:
+ explicit WorkerMetricsHelper(WebThreadType thread_type);
+ ~WorkerMetricsHelper();
+
+ void RecordTaskMetrics(WorkerTaskQueue* queue,
+ const TaskQueue::Task& task,
+ base::TimeTicks start_time,
+ base::TimeTicks end_time,
+ base::Optional<base::TimeDelta> thread_time);
+
+ void SetParentFrameType(FrameOriginType frame_type);
+
+ private:
+ TaskDurationMetricReporter<TaskType>
+ dedicated_worker_per_task_type_duration_reporter_;
+ TaskDurationMetricReporter<TaskType>
+ dedicated_worker_per_task_type_cpu_duration_reporter_;
+ TaskDurationMetricReporter<FrameOriginType>
+ dedicated_worker_per_parent_frame_status_duration_reporter_;
+ TaskDurationMetricReporter<FrameOriginType>
+ background_dedicated_worker_per_parent_frame_status_duration_reporter_;
+
+ base::Optional<FrameOriginType> parent_frame_type_;
+
+ DISALLOW_COPY_AND_ASSIGN(WorkerMetricsHelper);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_WORKER_METRICS_HELPER_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/worker_scheduler_proxy.cc b/chromium/third_party/blink/renderer/platform/scheduler/child/worker_scheduler_proxy.cc
new file mode 100644
index 00000000000..0bdfdf5a4fb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/worker_scheduler_proxy.cc
@@ -0,0 +1,51 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/child/worker_scheduler_proxy.h"
+
+#include "third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h"
+
+namespace blink {
+namespace scheduler {
+
+WorkerSchedulerProxy::WorkerSchedulerProxy(FrameScheduler* frame_scheduler) {
+ throttling_observer_handle_ = frame_scheduler->AddThrottlingObserver(
+ FrameScheduler::ObserverType::kWorkerScheduler, this);
+ parent_frame_type_ = GetFrameOriginType(frame_scheduler);
+}
+
+WorkerSchedulerProxy::~WorkerSchedulerProxy() {
+ DCHECK(IsMainThread());
+}
+
+void WorkerSchedulerProxy::OnWorkerSchedulerCreated(
+ base::WeakPtr<WorkerThreadScheduler> worker_scheduler) {
+ DCHECK(!IsMainThread())
+ << "OnWorkerSchedulerCreated should be called from the worker thread";
+ DCHECK(!worker_scheduler_) << "OnWorkerSchedulerCreated is called twice";
+ DCHECK(worker_scheduler) << "WorkerScheduler is expected to exist";
+ worker_scheduler_ = std::move(worker_scheduler);
+ worker_thread_task_runner_ = worker_scheduler_->ControlTaskQueue();
+ initialized_ = true;
+}
+
+void WorkerSchedulerProxy::OnThrottlingStateChanged(
+ FrameScheduler::ThrottlingState throttling_state) {
+ DCHECK(IsMainThread());
+ if (throttling_state_ == throttling_state)
+ return;
+ throttling_state_ = throttling_state;
+
+ if (!initialized_)
+ return;
+
+ worker_thread_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&WorkerThreadScheduler::OnThrottlingStateChanged,
+ worker_scheduler_, throttling_state));
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/worker_scheduler_proxy.h b/chromium/third_party/blink/renderer/platform/scheduler/child/worker_scheduler_proxy.h
new file mode 100644
index 00000000000..4188c5b474f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/worker_scheduler_proxy.h
@@ -0,0 +1,74 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_WORKER_SCHEDULER_PROXY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_WORKER_SCHEDULER_PROXY_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/scheduler/child/page_visibility_state.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/frame_origin_type.h"
+#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
+#include "third_party/blink/renderer/platform/wtf/wtf.h"
+
+namespace blink {
+namespace scheduler {
+class WorkerThreadScheduler;
+
+// Helper class for communication between frame scheduler (main thread) and
+// worker scheduler (worker thread).
+//
+// It's owned by DedicatedWorkerThread and is created and destroyed
+// on the main thread. It's passed to WorkerScheduler during its construction.
+// Given that DedicatedWorkerThread object outlives worker thread, this class
+// outlives worker thread too.
+class PLATFORM_EXPORT WorkerSchedulerProxy : public FrameScheduler::Observer {
+ public:
+ explicit WorkerSchedulerProxy(FrameScheduler* scheduler);
+ ~WorkerSchedulerProxy() override;
+
+ void OnWorkerSchedulerCreated(
+ base::WeakPtr<WorkerThreadScheduler> worker_scheduler);
+
+ void OnThrottlingStateChanged(
+ FrameScheduler::ThrottlingState throttling_state) override;
+
+ // Should be accessed only from the main thread or during init.
+ FrameScheduler::ThrottlingState throttling_state() const {
+ DCHECK(IsMainThread() || !initialized_);
+ return throttling_state_;
+ }
+
+ FrameOriginType parent_frame_type() const {
+ DCHECK(IsMainThread() || !initialized_);
+ return parent_frame_type_;
+ }
+
+ private:
+ // Can be accessed only from the worker thread.
+ base::WeakPtr<WorkerThreadScheduler> worker_scheduler_;
+
+ // Const after init on the worker thread.
+ scoped_refptr<base::SingleThreadTaskRunner> worker_thread_task_runner_;
+
+ FrameScheduler::ThrottlingState throttling_state_ =
+ FrameScheduler::ThrottlingState::kNotThrottled;
+
+ std::unique_ptr<FrameScheduler::ThrottlingObserverHandle>
+ throttling_observer_handle_;
+
+ bool initialized_ = false;
+
+ FrameOriginType parent_frame_type_;
+
+ DISALLOW_COPY_AND_ASSIGN(WorkerSchedulerProxy);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_WORKER_SCHEDULER_PROXY_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/worker_scheduler_proxy_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/child/worker_scheduler_proxy_unittest.cc
new file mode 100644
index 00000000000..ebd392fe224
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/worker_scheduler_proxy_unittest.cc
@@ -0,0 +1,187 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/child/worker_scheduler_proxy.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "components/viz/test/ordered_simple_task_runner.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/scheduler/child/webthread_impl_for_worker_scheduler.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/test/task_queue_manager_for_test.h"
+#include "third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h"
+#include "third_party/blink/renderer/platform/waitable_event.h"
+
+namespace blink {
+namespace scheduler {
+
+namespace {
+
+class WorkerThreadSchedulerForTest : public WorkerThreadScheduler {
+ public:
+ WorkerThreadSchedulerForTest(std::unique_ptr<TaskQueueManager> manager,
+ WorkerSchedulerProxy* proxy,
+ WaitableEvent* throtting_state_changed)
+ : WorkerThreadScheduler(WebThreadType::kTestThread,
+ std::move(manager),
+ proxy),
+ throtting_state_changed_(throtting_state_changed) {}
+
+ void OnThrottlingStateChanged(
+ FrameScheduler::ThrottlingState throttling_state) override {
+ WorkerThreadScheduler::OnThrottlingStateChanged(throttling_state);
+
+ throtting_state_changed_->Signal();
+ }
+
+ using WorkerThreadScheduler::throttling_state;
+
+ private:
+ WaitableEvent* throtting_state_changed_;
+};
+
+class WebThreadImplForWorkerSchedulerForTest
+ : public WebThreadImplForWorkerScheduler {
+ public:
+ WebThreadImplForWorkerSchedulerForTest(FrameScheduler* frame_scheduler,
+ WaitableEvent* throtting_state_changed)
+ : WebThreadImplForWorkerScheduler(
+ WebThreadCreationParams(WebThreadType::kTestThread)
+ .SetFrameScheduler(frame_scheduler)),
+ throtting_state_changed_(throtting_state_changed) {}
+
+ std::unique_ptr<NonMainThreadScheduler> CreateNonMainThreadScheduler()
+ override {
+ auto scheduler = std::make_unique<WorkerThreadSchedulerForTest>(
+ TaskQueueManager::TakeOverCurrentThread(), worker_scheduler_proxy(),
+ throtting_state_changed_);
+ scheduler_ = scheduler.get();
+ return scheduler;
+ }
+
+ WorkerThreadSchedulerForTest* GetWorkerScheduler() { return scheduler_; }
+
+ private:
+ WaitableEvent* throtting_state_changed_; // NOT OWNED
+ WorkerThreadSchedulerForTest* scheduler_ = nullptr; // NOT OWNED
+};
+
+std::unique_ptr<WebThreadImplForWorkerSchedulerForTest> CreateWorkerThread(
+ FrameScheduler* frame_scheduler,
+ WaitableEvent* throtting_state_changed) {
+ std::unique_ptr<WebThreadImplForWorkerSchedulerForTest> thread =
+ std::make_unique<WebThreadImplForWorkerSchedulerForTest>(
+ frame_scheduler, throtting_state_changed);
+ thread->Init();
+ return thread;
+}
+
+} // namespace
+
+class WorkerSchedulerProxyTest : public testing::Test {
+ public:
+ WorkerSchedulerProxyTest()
+ : mock_main_thread_task_runner_(
+ new cc::OrderedSimpleTaskRunner(&clock_, true)),
+ main_thread_scheduler_(std::make_unique<MainThreadSchedulerImpl>(
+ TaskQueueManagerForTest::Create(nullptr,
+ mock_main_thread_task_runner_,
+ &clock_),
+ base::nullopt)),
+ page_scheduler_(
+ std::make_unique<PageSchedulerImpl>(nullptr,
+ main_thread_scheduler_.get(),
+ false)),
+ frame_scheduler_(page_scheduler_->CreateFrameSchedulerImpl(
+ nullptr,
+ FrameScheduler::FrameType::kMainFrame)) {}
+
+ ~WorkerSchedulerProxyTest() {
+ frame_scheduler_.reset();
+ page_scheduler_.reset();
+ main_thread_scheduler_->Shutdown();
+ }
+
+ protected:
+ base::SimpleTestTickClock clock_;
+ scoped_refptr<cc::OrderedSimpleTaskRunner> mock_main_thread_task_runner_;
+
+ std::unique_ptr<MainThreadSchedulerImpl> main_thread_scheduler_;
+ std::unique_ptr<PageSchedulerImpl> page_scheduler_;
+ std::unique_ptr<FrameSchedulerImpl> frame_scheduler_;
+};
+
+TEST_F(WorkerSchedulerProxyTest, VisibilitySignalReceived) {
+ WaitableEvent throtting_state_changed;
+
+ auto worker_thread =
+ CreateWorkerThread(frame_scheduler_.get(), &throtting_state_changed);
+
+ DCHECK(worker_thread->GetWorkerScheduler()->throttling_state() ==
+ FrameScheduler::ThrottlingState::kNotThrottled);
+
+ page_scheduler_->SetPageVisible(false);
+ throtting_state_changed.Wait();
+ DCHECK(worker_thread->GetWorkerScheduler()->throttling_state() ==
+ FrameScheduler::ThrottlingState::kThrottled);
+
+ page_scheduler_->SetPageVisible(true);
+ throtting_state_changed.Wait();
+ DCHECK(worker_thread->GetWorkerScheduler()->throttling_state() ==
+ FrameScheduler::ThrottlingState::kNotThrottled);
+
+ mock_main_thread_task_runner_->RunUntilIdle();
+}
+
+// Tests below check that no crashes occur during different shutdown sequences.
+
+TEST_F(WorkerSchedulerProxyTest, FrameSchedulerDestroyed) {
+ WaitableEvent throtting_state_changed;
+
+ auto worker_thread =
+ CreateWorkerThread(frame_scheduler_.get(), &throtting_state_changed);
+
+ DCHECK(worker_thread->GetWorkerScheduler()->throttling_state() ==
+ FrameScheduler::ThrottlingState::kNotThrottled);
+
+ page_scheduler_->SetPageVisible(false);
+ throtting_state_changed.Wait();
+ DCHECK(worker_thread->GetWorkerScheduler()->throttling_state() ==
+ FrameScheduler::ThrottlingState::kThrottled);
+
+ frame_scheduler_.reset();
+ mock_main_thread_task_runner_->RunUntilIdle();
+
+ worker_thread.reset();
+ mock_main_thread_task_runner_->RunUntilIdle();
+}
+
+TEST_F(WorkerSchedulerProxyTest, ThreadDestroyed) {
+ WaitableEvent throtting_state_changed;
+
+ auto worker_thread =
+ CreateWorkerThread(frame_scheduler_.get(), &throtting_state_changed);
+
+ DCHECK(worker_thread->GetWorkerScheduler()->throttling_state() ==
+ FrameScheduler::ThrottlingState::kNotThrottled);
+
+ page_scheduler_->SetPageVisible(false);
+ throtting_state_changed.Wait();
+ DCHECK(worker_thread->GetWorkerScheduler()->throttling_state() ==
+ FrameScheduler::ThrottlingState::kThrottled);
+
+ worker_thread.reset();
+ mock_main_thread_task_runner_->RunUntilIdle();
+
+ page_scheduler_->SetPageVisible(true);
+ mock_main_thread_task_runner_->RunUntilIdle();
+
+ frame_scheduler_.reset();
+ mock_main_thread_task_runner_->RunUntilIdle();
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/worker_task_queue.cc b/chromium/third_party/blink/renderer/platform/scheduler/child/worker_task_queue.cc
new file mode 100644
index 00000000000..58898d5c1b0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/worker_task_queue.cc
@@ -0,0 +1,41 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/child/worker_task_queue.h"
+
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/public/non_main_thread_scheduler.h"
+
+namespace blink {
+namespace scheduler {
+
+WorkerTaskQueue::WorkerTaskQueue(
+ std::unique_ptr<internal::TaskQueueImpl> impl,
+ const TaskQueue::Spec& spec,
+ NonMainThreadScheduler* non_main_thread_scheduler)
+ : TaskQueue(std::move(impl), spec),
+ non_main_thread_scheduler_(non_main_thread_scheduler) {
+ if (GetTaskQueueImpl()) {
+ // TaskQueueImpl may be null for tests.
+ GetTaskQueueImpl()->SetOnTaskCompletedHandler(base::BindRepeating(
+ &WorkerTaskQueue::OnTaskCompleted, base::Unretained(this)));
+ }
+}
+
+WorkerTaskQueue::~WorkerTaskQueue() = default;
+
+void WorkerTaskQueue::OnTaskCompleted(
+ const TaskQueue::Task& task,
+ base::TimeTicks start,
+ base::TimeTicks end,
+ base::Optional<base::TimeDelta> thread_time) {
+ // |non_main_thread_scheduler_| can be nullptr in tests.
+ if (non_main_thread_scheduler_) {
+ non_main_thread_scheduler_->OnTaskCompleted(this, task, start, end,
+ thread_time);
+ }
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/child/worker_task_queue.h b/chromium/third_party/blink/renderer/platform/scheduler/child/worker_task_queue.h
new file mode 100644
index 00000000000..049adb10eff
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/child/worker_task_queue.h
@@ -0,0 +1,35 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_WORKER_TASK_QUEUE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_WORKER_TASK_QUEUE_H_
+
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue.h"
+
+namespace blink {
+namespace scheduler {
+
+class NonMainThreadScheduler;
+
+class PLATFORM_EXPORT WorkerTaskQueue : public TaskQueue {
+ public:
+ WorkerTaskQueue(std::unique_ptr<internal::TaskQueueImpl> impl,
+ const Spec& spec,
+ NonMainThreadScheduler* non_main_thread_scheduler);
+ ~WorkerTaskQueue() override;
+
+ void OnTaskCompleted(const TaskQueue::Task& task,
+ base::TimeTicks start,
+ base::TimeTicks end,
+ base::Optional<base::TimeDelta> thread_time);
+
+ private:
+ // Not owned.
+ NonMainThreadScheduler* non_main_thread_scheduler_;
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_CHILD_WORKER_TASK_QUEUE_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/DEPS b/chromium/third_party/blink/renderer/platform/scheduler/common/DEPS
new file mode 100644
index 00000000000..a3018fec64f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/common/DEPS
@@ -0,0 +1,6 @@
+specific_include_rules = {
+ ".*test\.cc": [
+ "+cc/test",
+ "+components/viz/test",
+ ],
+}
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.cc b/chromium/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.cc
new file mode 100644
index 00000000000..d905bfc6327
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.cc
@@ -0,0 +1,133 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h"
+
+#include "base/time/default_tick_clock.h"
+#include "base/trace_event/trace_event.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl.h"
+
+namespace blink {
+namespace scheduler {
+
+SchedulerHelper::SchedulerHelper(
+ std::unique_ptr<TaskQueueManager> task_queue_manager)
+ : task_queue_manager_(std::move(task_queue_manager)), observer_(nullptr) {
+ task_queue_manager_->SetWorkBatchSize(4);
+}
+
+void SchedulerHelper::InitDefaultQueues(
+ scoped_refptr<TaskQueue> default_task_queue,
+ scoped_refptr<TaskQueue> control_task_queue) {
+ control_task_queue->SetQueuePriority(TaskQueue::kControlPriority);
+
+ DCHECK(task_queue_manager_);
+ task_queue_manager_->SetDefaultTaskRunner(default_task_queue);
+}
+
+SchedulerHelper::~SchedulerHelper() {
+ Shutdown();
+}
+
+void SchedulerHelper::Shutdown() {
+ CheckOnValidThread();
+ if (!task_queue_manager_)
+ return;
+ task_queue_manager_->SetObserver(nullptr);
+ task_queue_manager_.reset();
+}
+
+void SchedulerHelper::SetWorkBatchSizeForTesting(size_t work_batch_size) {
+ CheckOnValidThread();
+ DCHECK(task_queue_manager_.get());
+ task_queue_manager_->SetWorkBatchSize(work_batch_size);
+}
+
+bool SchedulerHelper::GetAndClearSystemIsQuiescentBit() {
+ CheckOnValidThread();
+ DCHECK(task_queue_manager_.get());
+ return task_queue_manager_->GetAndClearSystemIsQuiescentBit();
+}
+
+void SchedulerHelper::AddTaskObserver(
+ base::MessageLoop::TaskObserver* task_observer) {
+ CheckOnValidThread();
+ if (task_queue_manager_)
+ task_queue_manager_->AddTaskObserver(task_observer);
+}
+
+void SchedulerHelper::RemoveTaskObserver(
+ base::MessageLoop::TaskObserver* task_observer) {
+ CheckOnValidThread();
+ if (task_queue_manager_)
+ task_queue_manager_->RemoveTaskObserver(task_observer);
+}
+
+void SchedulerHelper::AddTaskTimeObserver(
+ TaskTimeObserver* task_time_observer) {
+ if (task_queue_manager_)
+ task_queue_manager_->AddTaskTimeObserver(task_time_observer);
+}
+
+void SchedulerHelper::RemoveTaskTimeObserver(
+ TaskTimeObserver* task_time_observer) {
+ if (task_queue_manager_)
+ task_queue_manager_->RemoveTaskTimeObserver(task_time_observer);
+}
+
+void SchedulerHelper::SetObserver(Observer* observer) {
+ CheckOnValidThread();
+ observer_ = observer;
+ DCHECK(task_queue_manager_);
+ task_queue_manager_->SetObserver(this);
+}
+
+void SchedulerHelper::SweepCanceledDelayedTasks() {
+ CheckOnValidThread();
+ DCHECK(task_queue_manager_);
+ task_queue_manager_->SweepCanceledDelayedTasks();
+}
+
+RealTimeDomain* SchedulerHelper::real_time_domain() const {
+ CheckOnValidThread();
+ DCHECK(task_queue_manager_);
+ return task_queue_manager_->GetRealTimeDomain();
+}
+
+void SchedulerHelper::RegisterTimeDomain(TimeDomain* time_domain) {
+ CheckOnValidThread();
+ DCHECK(task_queue_manager_);
+ task_queue_manager_->RegisterTimeDomain(time_domain);
+}
+
+void SchedulerHelper::UnregisterTimeDomain(TimeDomain* time_domain) {
+ CheckOnValidThread();
+ if (task_queue_manager_)
+ task_queue_manager_->UnregisterTimeDomain(time_domain);
+}
+
+void SchedulerHelper::OnBeginNestedRunLoop() {
+ if (observer_)
+ observer_->OnBeginNestedRunLoop();
+}
+
+void SchedulerHelper::OnExitNestedRunLoop() {
+ if (observer_)
+ observer_->OnExitNestedRunLoop();
+}
+
+const base::TickClock* SchedulerHelper::GetClock() const {
+ return task_queue_manager_->GetClock();
+}
+
+base::TimeTicks SchedulerHelper::NowTicks() const {
+ if (task_queue_manager_)
+ return task_queue_manager_->NowTicks();
+ // We may need current time for tracing when shutting down worker thread.
+ return base::TimeTicks::Now();
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h b/chromium/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h
new file mode 100644
index 00000000000..4e365b2f5fa
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h
@@ -0,0 +1,108 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_COMMON_SCHEDULER_HELPER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_COMMON_SCHEDULER_HELPER_H_
+
+#include <stddef.h>
+
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/time/tick_clock.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_manager.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_selector.h"
+
+namespace blink {
+namespace scheduler {
+
+// Common scheduler functionality for default tasks.
+class PLATFORM_EXPORT SchedulerHelper : public TaskQueueManager::Observer {
+ public:
+ explicit SchedulerHelper(
+ std::unique_ptr<TaskQueueManager> task_queue_manager);
+ ~SchedulerHelper() override;
+
+ // TaskQueueManager::Observer implementation:
+ void OnBeginNestedRunLoop() override;
+ void OnExitNestedRunLoop() override;
+
+ const base::TickClock* GetClock() const;
+ base::TimeTicks NowTicks() const;
+
+ // Returns the default task queue.
+ virtual scoped_refptr<TaskQueue> DefaultTaskQueue() = 0;
+
+ // Returns the control task queue. Tasks posted to this queue are executed
+ // with the highest priority. Care must be taken to avoid starvation of other
+ // task queues.
+ virtual scoped_refptr<TaskQueue> ControlTaskQueue() = 0;
+
+ // Adds or removes a task observer from the scheduler. The observer will be
+ // notified before and after every executed task. These functions can only be
+ // called on the thread this class was created on.
+ void AddTaskObserver(base::MessageLoop::TaskObserver* task_observer);
+ void RemoveTaskObserver(base::MessageLoop::TaskObserver* task_observer);
+
+ void AddTaskTimeObserver(TaskTimeObserver* task_time_observer);
+ void RemoveTaskTimeObserver(TaskTimeObserver* task_time_observer);
+
+ // Shuts down the scheduler by dropping any remaining pending work in the work
+ // queues. After this call any work posted to the task queue will be
+ // silently dropped.
+ void Shutdown();
+
+ // Returns true if Shutdown() has been called. Otherwise returns false.
+ bool IsShutdown() const { return !task_queue_manager_.get(); }
+
+ inline void CheckOnValidThread() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ }
+
+ class PLATFORM_EXPORT Observer {
+ public:
+ virtual ~Observer() = default;
+
+ // Called when scheduler executes task with nested run loop.
+ virtual void OnBeginNestedRunLoop() = 0;
+
+ // Called when the scheduler spots we've exited a nested run loop.
+ virtual void OnExitNestedRunLoop() = 0;
+ };
+
+ // Called once to set the Observer. This function is called on the main
+ // thread. If |observer| is null, then no callbacks will occur.
+ // Note |observer| is expected to outlive the SchedulerHelper.
+ void SetObserver(Observer* observer);
+
+ // Remove all canceled delayed tasks.
+ void SweepCanceledDelayedTasks();
+
+ // Accessor methods.
+ RealTimeDomain* real_time_domain() const;
+ void RegisterTimeDomain(TimeDomain* time_domain);
+ void UnregisterTimeDomain(TimeDomain* time_domain);
+ bool GetAndClearSystemIsQuiescentBit();
+
+ // Test helpers.
+ void SetWorkBatchSizeForTesting(size_t work_batch_size);
+
+ protected:
+ void InitDefaultQueues(scoped_refptr<TaskQueue> default_task_queue,
+ scoped_refptr<TaskQueue> control_task_queue);
+
+ base::ThreadChecker thread_checker_;
+ std::unique_ptr<TaskQueueManager> task_queue_manager_;
+
+ private:
+ friend class SchedulerHelperTest;
+
+ Observer* observer_; // NOT OWNED
+
+ DISALLOW_COPY_AND_ASSIGN(SchedulerHelper);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_COMMON_SCHEDULER_HELPER_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/scheduler_helper_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/common/scheduler_helper_unittest.cc
new file mode 100644
index 00000000000..c5e3720d208
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/common/scheduler_helper_unittest.cc
@@ -0,0 +1,180 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h"
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "components/viz/test/ordered_simple_task_runner.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/scheduler/base/lazy_now.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue.h"
+#include "third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_helper.h"
+#include "third_party/blink/renderer/platform/scheduler/test/task_queue_manager_for_test.h"
+
+using testing::_;
+using testing::AnyNumber;
+using testing::Invoke;
+using testing::Return;
+
+namespace blink {
+namespace scheduler {
+namespace scheduler_helper_unittest {
+
+namespace {
+void AppendToVectorTestTask(std::vector<std::string>* vector,
+ std::string value) {
+ vector->push_back(value);
+}
+
+void AppendToVectorReentrantTask(base::SingleThreadTaskRunner* task_runner,
+ std::vector<int>* vector,
+ int* reentrant_count,
+ int max_reentrant_count) {
+ vector->push_back((*reentrant_count)++);
+ if (*reentrant_count < max_reentrant_count) {
+ task_runner->PostTask(FROM_HERE,
+ base::BindOnce(AppendToVectorReentrantTask,
+ base::Unretained(task_runner), vector,
+ reentrant_count, max_reentrant_count));
+ }
+}
+
+}; // namespace
+
+class SchedulerHelperTest : public testing::Test {
+ public:
+ SchedulerHelperTest()
+ : mock_task_runner_(new cc::OrderedSimpleTaskRunner(&clock_, false)) {
+ std::unique_ptr<TaskQueueManagerForTest> task_queue_manager =
+ TaskQueueManagerForTest::Create(nullptr, mock_task_runner_, &clock_);
+ task_queue_manager_ = task_queue_manager.get();
+ scheduler_helper_ = std::make_unique<WorkerSchedulerHelper>(
+ std::move(task_queue_manager), nullptr);
+ default_task_runner_ = scheduler_helper_->DefaultWorkerTaskQueue();
+ clock_.Advance(base::TimeDelta::FromMicroseconds(5000));
+ }
+
+ ~SchedulerHelperTest() override = default;
+
+ void TearDown() override {
+ // Check that all tests stop posting tasks.
+ mock_task_runner_->SetAutoAdvanceNowToPendingTasks(true);
+ while (mock_task_runner_->RunUntilIdle()) {
+ }
+ }
+
+ void RunUntilIdle() { mock_task_runner_->RunUntilIdle(); }
+
+ template <typename E>
+ static void CallForEachEnumValue(E first,
+ E last,
+ const char* (*function)(E)) {
+ for (E val = first; val < last;
+ val = static_cast<E>(static_cast<int>(val) + 1)) {
+ (*function)(val);
+ }
+ }
+
+ protected:
+ base::SimpleTestTickClock clock_;
+ scoped_refptr<cc::OrderedSimpleTaskRunner> mock_task_runner_;
+
+ std::unique_ptr<WorkerSchedulerHelper> scheduler_helper_;
+ TaskQueueManagerForTest* task_queue_manager_; // Owned by scheduler_helper.
+ scoped_refptr<base::SingleThreadTaskRunner> default_task_runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(SchedulerHelperTest);
+};
+
+TEST_F(SchedulerHelperTest, TestPostDefaultTask) {
+ std::vector<std::string> run_order;
+ default_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&AppendToVectorTestTask, &run_order, "D1"));
+ default_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&AppendToVectorTestTask, &run_order, "D2"));
+ default_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&AppendToVectorTestTask, &run_order, "D3"));
+ default_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&AppendToVectorTestTask, &run_order, "D4"));
+
+ RunUntilIdle();
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("D1"), std::string("D2"),
+ std::string("D3"), std::string("D4")));
+}
+
+TEST_F(SchedulerHelperTest, TestRentrantTask) {
+ int count = 0;
+ std::vector<int> run_order;
+ default_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(AppendToVectorReentrantTask,
+ base::RetainedRef(default_task_runner_),
+ &run_order, &count, 5));
+ RunUntilIdle();
+
+ EXPECT_THAT(run_order, testing::ElementsAre(0, 1, 2, 3, 4));
+}
+
+TEST_F(SchedulerHelperTest, IsShutdown) {
+ EXPECT_FALSE(scheduler_helper_->IsShutdown());
+
+ scheduler_helper_->Shutdown();
+ EXPECT_TRUE(scheduler_helper_->IsShutdown());
+}
+
+TEST_F(SchedulerHelperTest, GetNumberOfPendingTasks) {
+ std::vector<std::string> run_order;
+ scheduler_helper_->DefaultWorkerTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&AppendToVectorTestTask, &run_order, "D1"));
+ scheduler_helper_->DefaultWorkerTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&AppendToVectorTestTask, &run_order, "D2"));
+ scheduler_helper_->ControlWorkerTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&AppendToVectorTestTask, &run_order, "C1"));
+ EXPECT_EQ(3U, task_queue_manager_->PendingTasksCount());
+ RunUntilIdle();
+ EXPECT_EQ(0U, task_queue_manager_->PendingTasksCount());
+}
+
+namespace {
+class MockTaskObserver : public base::MessageLoop::TaskObserver {
+ public:
+ MOCK_METHOD1(DidProcessTask, void(const base::PendingTask& task));
+ MOCK_METHOD1(WillProcessTask, void(const base::PendingTask& task));
+};
+
+void NopTask() {}
+} // namespace
+
+TEST_F(SchedulerHelperTest, ObserversNotifiedFor_DefaultTaskRunner) {
+ MockTaskObserver observer;
+ scheduler_helper_->AddTaskObserver(&observer);
+
+ scheduler_helper_->DefaultWorkerTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&NopTask));
+
+ EXPECT_CALL(observer, WillProcessTask(_)).Times(1);
+ EXPECT_CALL(observer, DidProcessTask(_)).Times(1);
+ RunUntilIdle();
+}
+
+TEST_F(SchedulerHelperTest, ObserversNotNotifiedFor_ControlTaskQueue) {
+ MockTaskObserver observer;
+ scheduler_helper_->AddTaskObserver(&observer);
+
+ scheduler_helper_->ControlWorkerTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&NopTask));
+
+ EXPECT_CALL(observer, WillProcessTask(_)).Times(0);
+ EXPECT_CALL(observer, DidProcessTask(_)).Times(0);
+ RunUntilIdle();
+}
+
+} // namespace scheduler_helper_unittest
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.cc b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.cc
new file mode 100644
index 00000000000..df387275201
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.cc
@@ -0,0 +1,95 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h"
+
+#include <cstdint>
+
+#include "base/optional.h"
+#include "third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h"
+
+namespace blink {
+namespace scheduler {
+
+BudgetPool::BudgetPool(const char* name,
+ BudgetPoolController* budget_pool_controller)
+ : name_(name),
+ budget_pool_controller_(budget_pool_controller),
+ is_enabled_(true) {}
+
+BudgetPool::~BudgetPool() = default;
+
+const char* BudgetPool::Name() const {
+ return name_;
+}
+
+void BudgetPool::AddQueue(base::TimeTicks now, TaskQueue* queue) {
+ budget_pool_controller_->AddQueueToBudgetPool(queue, this);
+ associated_task_queues_.insert(queue);
+
+ if (!is_enabled_)
+ return;
+ budget_pool_controller_->UpdateQueueThrottlingState(now, queue);
+}
+
+void BudgetPool::UnregisterQueue(TaskQueue* queue) {
+ DissociateQueue(queue);
+}
+
+void BudgetPool::RemoveQueue(base::TimeTicks now, TaskQueue* queue) {
+ DissociateQueue(queue);
+
+ if (!is_enabled_)
+ return;
+
+ budget_pool_controller_->UpdateQueueThrottlingState(now, queue);
+}
+
+void BudgetPool::DissociateQueue(TaskQueue* queue) {
+ budget_pool_controller_->RemoveQueueFromBudgetPool(queue, this);
+ associated_task_queues_.erase(queue);
+}
+
+void BudgetPool::EnableThrottling(LazyNow* lazy_now) {
+ if (is_enabled_)
+ return;
+ is_enabled_ = true;
+
+ TRACE_EVENT0("renderer.scheduler", "BudgetPool_EnableThrottling");
+
+ BlockThrottledQueues(lazy_now->Now());
+}
+
+void BudgetPool::DisableThrottling(LazyNow* lazy_now) {
+ if (!is_enabled_)
+ return;
+ is_enabled_ = false;
+
+ TRACE_EVENT0("renderer.scheduler", "BudgetPool_DisableThrottling");
+
+ for (TaskQueue* queue : associated_task_queues_) {
+ budget_pool_controller_->UpdateQueueThrottlingState(lazy_now->Now(), queue);
+ }
+
+ // TODO(altimin): We need to disable TimeBudgetQueues here or they will
+ // regenerate extra time budget when they are disabled.
+}
+
+bool BudgetPool::IsThrottlingEnabled() const {
+ return is_enabled_;
+}
+
+void BudgetPool::Close() {
+ DCHECK_EQ(0u, associated_task_queues_.size());
+
+ budget_pool_controller_->UnregisterBudgetPool(this);
+}
+
+void BudgetPool::BlockThrottledQueues(base::TimeTicks now) {
+ for (TaskQueue* queue : associated_task_queues_)
+ budget_pool_controller_->UpdateQueueThrottlingState(now, queue);
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h
new file mode 100644
index 00000000000..617444431c8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h
@@ -0,0 +1,119 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_COMMON_THROTTLING_BUDGET_POOL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_COMMON_THROTTLING_BUDGET_POOL_H_
+
+#include <unordered_set>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/optional.h"
+#include "base/time/time.h"
+#include "third_party/blink/renderer/platform/scheduler/base/lazy_now.h"
+
+namespace base {
+namespace trace_event {
+class TracedValue;
+}
+} // namespace base
+
+namespace blink {
+namespace scheduler {
+
+class TaskQueue;
+class BudgetPoolController;
+enum class QueueBlockType;
+
+// BudgetPool represents a group of task queues which share a limit
+// on a resource. This limit applies when task queues are already throttled
+// by TaskQueueThrottler.
+class PLATFORM_EXPORT BudgetPool {
+ public:
+ virtual ~BudgetPool();
+
+ const char* Name() const;
+
+ // Report task run time to the budget pool.
+ virtual void RecordTaskRunTime(TaskQueue* queue,
+ base::TimeTicks start_time,
+ base::TimeTicks end_time) = 0;
+
+ // Returns the earliest time when the next pump can be scheduled to run
+ // new tasks.
+ virtual base::TimeTicks GetNextAllowedRunTime(
+ base::TimeTicks desired_run_time) const = 0;
+
+ // Returns true if a task can run at the given time.
+ virtual bool CanRunTasksAt(base::TimeTicks moment, bool is_wake_up) const = 0;
+
+ // Returns a point in time until which tasks are allowed to run.
+ // base::nullopt means that there are no known limits.
+ virtual base::Optional<base::TimeTicks> GetTimeTasksCanRunUntil(
+ base::TimeTicks now,
+ bool is_wake_up) const = 0;
+
+ // Notifies budget pool that queue has work with desired run time.
+ virtual void OnQueueNextWakeUpChanged(TaskQueue* queue,
+ base::TimeTicks now,
+ base::TimeTicks desired_run_time) = 0;
+
+ // Notifies budget pool that wakeup has happened.
+ virtual void OnWakeUp(base::TimeTicks now) = 0;
+
+ // Specify how this budget pool should block affected queues.
+ virtual QueueBlockType GetBlockType() const = 0;
+
+ // Returns state for tracing.
+ virtual void AsValueInto(base::trace_event::TracedValue* state,
+ base::TimeTicks now) const = 0;
+
+ // Adds |queue| to given pool. If the pool restriction does not allow
+ // a task to be run immediately and |queue| is throttled, |queue| becomes
+ // disabled.
+ void AddQueue(base::TimeTicks now, TaskQueue* queue);
+
+ // Removes |queue| from given pool. If it is throttled, it does not
+ // become enabled immediately, but a wake-up is scheduled if needed.
+ void RemoveQueue(base::TimeTicks now, TaskQueue* queue);
+
+ // Unlike RemoveQueue, does not schedule a new wake-up for the queue.
+ void UnregisterQueue(TaskQueue* queue);
+
+ // Enables this time budget pool. Queues from this pool will be
+ // throttled based on their run time.
+ void EnableThrottling(LazyNow* now);
+
+ // Disables with time budget pool. Queues from this pool will not be
+ // throttled based on their run time. A call to |PumpThrottledTasks|
+ // will be scheduled to enable this queues back again and respect
+ // timer alignment. Internal budget level will not regenerate with time.
+ void DisableThrottling(LazyNow* now);
+
+ bool IsThrottlingEnabled() const;
+
+ // All queues should be removed before calling Close().
+ void Close();
+
+ // Block all associated queues and schedule them to run when appropriate.
+ void BlockThrottledQueues(base::TimeTicks now);
+
+ protected:
+ BudgetPool(const char* name, BudgetPoolController* budget_pool_controller);
+
+ const char* name_; // NOT OWNED
+
+ BudgetPoolController* budget_pool_controller_;
+
+ std::unordered_set<TaskQueue*> associated_task_queues_;
+ bool is_enabled_;
+
+ private:
+ void DissociateQueue(TaskQueue* queue);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_COMMON_THROTTLING_BUDGET_POOL_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool_unittest.cc
new file mode 100644
index 00000000000..09b4f14de2c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool_unittest.cc
@@ -0,0 +1,186 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h"
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "components/viz/test/ordered_simple_task_runner.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h"
+#include "third_party/blink/renderer/platform/scheduler/common/throttling/cpu_time_budget_pool.h"
+#include "third_party/blink/renderer/platform/scheduler/common/throttling/wake_up_budget_pool.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/test/task_queue_manager_for_test.h"
+
+namespace blink {
+namespace scheduler {
+
+class BudgetPoolTest : public testing::Test {
+ public:
+ BudgetPoolTest() = default;
+ ~BudgetPoolTest() override = default;
+
+ void SetUp() override {
+ clock_.Advance(base::TimeDelta::FromMicroseconds(5000));
+ mock_task_runner_ =
+ base::MakeRefCounted<cc::OrderedSimpleTaskRunner>(&clock_, true);
+ scheduler_.reset(new MainThreadSchedulerImpl(
+ TaskQueueManagerForTest::Create(nullptr, mock_task_runner_, &clock_),
+ base::nullopt));
+ task_queue_throttler_ = scheduler_->task_queue_throttler();
+ start_time_ = clock_.NowTicks();
+ }
+
+ void TearDown() override {
+ scheduler_->Shutdown();
+ scheduler_.reset();
+ }
+
+ base::TimeTicks MillisecondsAfterStart(int milliseconds) {
+ return start_time_ + base::TimeDelta::FromMilliseconds(milliseconds);
+ }
+
+ base::TimeTicks SecondsAfterStart(int seconds) {
+ return start_time_ + base::TimeDelta::FromSeconds(seconds);
+ }
+
+ protected:
+ base::SimpleTestTickClock clock_;
+ scoped_refptr<cc::OrderedSimpleTaskRunner> mock_task_runner_;
+ std::unique_ptr<MainThreadSchedulerImpl> scheduler_;
+ TaskQueueThrottler* task_queue_throttler_; // NOT OWNED
+ base::TimeTicks start_time_;
+
+ DISALLOW_COPY_AND_ASSIGN(BudgetPoolTest);
+};
+
+TEST_F(BudgetPoolTest, CPUTimeBudgetPool) {
+ CPUTimeBudgetPool* pool =
+ task_queue_throttler_->CreateCPUTimeBudgetPool("test");
+
+ pool->SetTimeBudgetRecoveryRate(SecondsAfterStart(0), 0.1);
+
+ EXPECT_TRUE(pool->CanRunTasksAt(SecondsAfterStart(0), false));
+ EXPECT_EQ(SecondsAfterStart(0),
+ pool->GetNextAllowedRunTime(SecondsAfterStart(0)));
+
+ // Run an expensive task and make sure that we're throttled.
+ pool->RecordTaskRunTime(nullptr, SecondsAfterStart(0),
+ MillisecondsAfterStart(100));
+
+ EXPECT_FALSE(pool->CanRunTasksAt(MillisecondsAfterStart(500), false));
+ EXPECT_EQ(MillisecondsAfterStart(1000),
+ pool->GetNextAllowedRunTime(SecondsAfterStart(0)));
+ EXPECT_TRUE(pool->CanRunTasksAt(MillisecondsAfterStart(1000), false));
+
+ // Run a cheap task and make sure that it doesn't affect anything.
+ EXPECT_TRUE(pool->CanRunTasksAt(MillisecondsAfterStart(2000), false));
+ pool->RecordTaskRunTime(nullptr, MillisecondsAfterStart(2000),
+ MillisecondsAfterStart(2020));
+ EXPECT_TRUE(pool->CanRunTasksAt(MillisecondsAfterStart(2020), false));
+ EXPECT_EQ(MillisecondsAfterStart(2020),
+ pool->GetNextAllowedRunTime(SecondsAfterStart(0)));
+
+ pool->Close();
+}
+
+TEST_F(BudgetPoolTest, CPUTimeBudgetPoolMinBudgetLevelToRun) {
+ CPUTimeBudgetPool* pool =
+ task_queue_throttler_->CreateCPUTimeBudgetPool("test");
+
+ pool->SetMinBudgetLevelToRun(SecondsAfterStart(0),
+ base::TimeDelta::FromMilliseconds(10));
+ pool->SetTimeBudgetRecoveryRate(SecondsAfterStart(0), 0.1);
+
+ EXPECT_TRUE(pool->CanRunTasksAt(SecondsAfterStart(0), false));
+ EXPECT_EQ(SecondsAfterStart(0),
+ pool->GetNextAllowedRunTime(SecondsAfterStart(0)));
+
+ pool->RecordTaskRunTime(nullptr, SecondsAfterStart(0),
+ MillisecondsAfterStart(10));
+ EXPECT_FALSE(pool->CanRunTasksAt(MillisecondsAfterStart(15), false));
+ EXPECT_FALSE(pool->CanRunTasksAt(MillisecondsAfterStart(150), false));
+ // We need to wait extra 100ms to get budget of 10ms.
+ EXPECT_EQ(MillisecondsAfterStart(200),
+ pool->GetNextAllowedRunTime(SecondsAfterStart(0)));
+
+ pool->RecordTaskRunTime(nullptr, MillisecondsAfterStart(200),
+ MillisecondsAfterStart(205));
+ // We can run when budget is non-negative even when it less than 10ms.
+ EXPECT_EQ(MillisecondsAfterStart(205),
+ pool->GetNextAllowedRunTime(SecondsAfterStart(0)));
+
+ pool->RecordTaskRunTime(nullptr, MillisecondsAfterStart(205),
+ MillisecondsAfterStart(215));
+ EXPECT_EQ(MillisecondsAfterStart(350),
+ pool->GetNextAllowedRunTime(SecondsAfterStart(0)));
+}
+
+TEST_F(BudgetPoolTest, WakeUpBudgetPool) {
+ WakeUpBudgetPool* pool =
+ task_queue_throttler_->CreateWakeUpBudgetPool("test");
+
+ scoped_refptr<TaskQueue> queue = scheduler_->NewTimerTaskQueue(
+ MainThreadTaskQueue::QueueType::kFrameThrottleable);
+
+ pool->SetWakeUpRate(0.1);
+ pool->SetWakeUpDuration(base::TimeDelta::FromMilliseconds(10));
+
+ // Can't run tasks until a wake-up.
+ EXPECT_FALSE(pool->CanRunTasksAt(MillisecondsAfterStart(0), false));
+ EXPECT_FALSE(pool->CanRunTasksAt(MillisecondsAfterStart(5), false));
+ EXPECT_FALSE(pool->CanRunTasksAt(MillisecondsAfterStart(9), false));
+ EXPECT_FALSE(pool->CanRunTasksAt(MillisecondsAfterStart(10), false));
+ EXPECT_FALSE(pool->CanRunTasksAt(MillisecondsAfterStart(11), false));
+
+ pool->OnWakeUp(MillisecondsAfterStart(0));
+
+ EXPECT_TRUE(pool->CanRunTasksAt(MillisecondsAfterStart(0), false));
+ EXPECT_TRUE(pool->CanRunTasksAt(MillisecondsAfterStart(5), false));
+ EXPECT_TRUE(pool->CanRunTasksAt(MillisecondsAfterStart(9), false));
+ EXPECT_FALSE(pool->CanRunTasksAt(MillisecondsAfterStart(10), false));
+ EXPECT_FALSE(pool->CanRunTasksAt(MillisecondsAfterStart(11), false));
+
+ // GetNextAllowedRunTime should return the desired time when in the
+ // wakeup window and return the next wakeup otherwise.
+ EXPECT_EQ(start_time_, pool->GetNextAllowedRunTime(start_time_));
+ EXPECT_EQ(base::TimeTicks() + base::TimeDelta::FromSeconds(10),
+ pool->GetNextAllowedRunTime(MillisecondsAfterStart(15)));
+
+ pool->RecordTaskRunTime(queue.get(), MillisecondsAfterStart(5),
+ MillisecondsAfterStart(7));
+
+ // Make sure that nothing changes after a task inside wakeup window.
+ EXPECT_TRUE(pool->CanRunTasksAt(MillisecondsAfterStart(0), false));
+ EXPECT_TRUE(pool->CanRunTasksAt(MillisecondsAfterStart(5), false));
+ EXPECT_TRUE(pool->CanRunTasksAt(MillisecondsAfterStart(9), false));
+ EXPECT_FALSE(pool->CanRunTasksAt(MillisecondsAfterStart(10), false));
+ EXPECT_FALSE(pool->CanRunTasksAt(MillisecondsAfterStart(11), false));
+ EXPECT_EQ(start_time_, pool->GetNextAllowedRunTime(start_time_));
+ EXPECT_EQ(base::TimeTicks() + base::TimeDelta::FromSeconds(10),
+ pool->GetNextAllowedRunTime(MillisecondsAfterStart(15)));
+
+ pool->OnWakeUp(MillisecondsAfterStart(12005));
+ pool->RecordTaskRunTime(queue.get(), MillisecondsAfterStart(12005),
+ MillisecondsAfterStart(12007));
+
+ EXPECT_TRUE(pool->CanRunTasksAt(MillisecondsAfterStart(12005), false));
+ EXPECT_TRUE(pool->CanRunTasksAt(MillisecondsAfterStart(12007), false));
+ EXPECT_TRUE(pool->CanRunTasksAt(MillisecondsAfterStart(12014), false));
+ EXPECT_FALSE(pool->CanRunTasksAt(MillisecondsAfterStart(12015), false));
+ EXPECT_FALSE(pool->CanRunTasksAt(MillisecondsAfterStart(12016), false));
+ EXPECT_EQ(base::TimeTicks() + base::TimeDelta::FromSeconds(20),
+ pool->GetNextAllowedRunTime(SecondsAfterStart(13)));
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/cpu_time_budget_pool.cc b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/cpu_time_budget_pool.cc
new file mode 100644
index 00000000000..6eade22fc88
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/cpu_time_budget_pool.cc
@@ -0,0 +1,186 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/common/throttling/cpu_time_budget_pool.h"
+
+#include <cstdint>
+
+#include "base/logging.h"
+#include "base/optional.h"
+#include "third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h"
+
+namespace blink {
+namespace scheduler {
+
+CPUTimeBudgetPool::CPUTimeBudgetPool(
+ const char* name,
+ BudgetPoolController* budget_pool_controller,
+ TraceableVariableController* tracing_controller,
+ base::TimeTicks now)
+ : BudgetPool(name, budget_pool_controller),
+ current_budget_level_(base::TimeDelta(),
+ "RendererScheduler.BackgroundBudgetMs",
+ budget_pool_controller,
+ tracing_controller,
+ TimeDeltaToMilliseconds),
+ last_checkpoint_(now),
+ cpu_percentage_(1) {}
+
+CPUTimeBudgetPool::~CPUTimeBudgetPool() = default;
+
+QueueBlockType CPUTimeBudgetPool::GetBlockType() const {
+ return QueueBlockType::kAllTasks;
+}
+
+void CPUTimeBudgetPool::SetMaxBudgetLevel(
+ base::TimeTicks now,
+ base::Optional<base::TimeDelta> max_budget_level) {
+ Advance(now);
+ max_budget_level_ = max_budget_level;
+ EnforceBudgetLevelRestrictions();
+}
+
+void CPUTimeBudgetPool::SetMaxThrottlingDelay(
+ base::TimeTicks now,
+ base::Optional<base::TimeDelta> max_throttling_delay) {
+ Advance(now);
+ max_throttling_delay_ = max_throttling_delay;
+ EnforceBudgetLevelRestrictions();
+}
+
+void CPUTimeBudgetPool::SetMinBudgetLevelToRun(
+ base::TimeTicks now,
+ base::TimeDelta min_budget_level_to_run) {
+ Advance(now);
+ min_budget_level_to_run_ = min_budget_level_to_run;
+}
+
+void CPUTimeBudgetPool::SetTimeBudgetRecoveryRate(base::TimeTicks now,
+ double cpu_percentage) {
+ Advance(now);
+ cpu_percentage_ = cpu_percentage;
+ EnforceBudgetLevelRestrictions();
+}
+
+void CPUTimeBudgetPool::GrantAdditionalBudget(base::TimeTicks now,
+ base::TimeDelta budget_level) {
+ Advance(now);
+ current_budget_level_ += budget_level;
+ EnforceBudgetLevelRestrictions();
+}
+
+void CPUTimeBudgetPool::SetReportingCallback(
+ base::RepeatingCallback<void(base::TimeDelta)> reporting_callback) {
+ reporting_callback_ = reporting_callback;
+}
+
+bool CPUTimeBudgetPool::CanRunTasksAt(base::TimeTicks moment,
+ bool is_wake_up) const {
+ return moment >= GetNextAllowedRunTime(moment);
+}
+
+base::Optional<base::TimeTicks> CPUTimeBudgetPool::GetTimeTasksCanRunUntil(
+ base::TimeTicks now,
+ bool is_wake_up) const {
+ if (CanRunTasksAt(now, is_wake_up))
+ return base::nullopt;
+ return base::TimeTicks();
+}
+
+base::TimeTicks CPUTimeBudgetPool::GetNextAllowedRunTime(
+ base::TimeTicks desired_run_time) const {
+ if (!is_enabled_ || current_budget_level_->InMicroseconds() >= 0)
+ return last_checkpoint_;
+ // Subtract because current_budget is negative.
+ return last_checkpoint_ +
+ (-current_budget_level_ + min_budget_level_to_run_) / cpu_percentage_;
+}
+
+void CPUTimeBudgetPool::RecordTaskRunTime(TaskQueue* queue,
+ base::TimeTicks start_time,
+ base::TimeTicks end_time) {
+ DCHECK_LE(start_time, end_time);
+ Advance(end_time);
+ if (is_enabled_) {
+ base::TimeDelta old_budget_level = current_budget_level_;
+ current_budget_level_ -= (end_time - start_time);
+ EnforceBudgetLevelRestrictions();
+
+ if (!reporting_callback_.is_null() && old_budget_level.InSecondsF() > 0 &&
+ current_budget_level_->InSecondsF() < 0) {
+ reporting_callback_.Run(-current_budget_level_ / cpu_percentage_);
+ }
+ }
+
+ if (current_budget_level_->InSecondsF() < 0)
+ BlockThrottledQueues(end_time);
+}
+
+void CPUTimeBudgetPool::OnQueueNextWakeUpChanged(
+ TaskQueue* queue,
+ base::TimeTicks now,
+ base::TimeTicks desired_run_time) {
+ budget_pool_controller_->UpdateQueueThrottlingState(now, queue);
+}
+
+void CPUTimeBudgetPool::OnWakeUp(base::TimeTicks now) {}
+
+void CPUTimeBudgetPool::AsValueInto(base::trace_event::TracedValue* state,
+ base::TimeTicks now) const {
+ current_budget_level_.Trace();
+ state->BeginDictionary(name_);
+
+ state->SetString("name", name_);
+ state->SetDouble("time_budget", cpu_percentage_);
+ state->SetDouble("time_budget_level_in_seconds",
+ current_budget_level_->InSecondsF());
+ state->SetDouble("last_checkpoint_seconds_ago",
+ (now - last_checkpoint_).InSecondsF());
+ state->SetBoolean("is_enabled", is_enabled_);
+ state->SetDouble("min_budget_level_to_run_in_seconds",
+ min_budget_level_to_run_.InSecondsF());
+
+ if (max_throttling_delay_) {
+ state->SetDouble("max_throttling_delay_in_seconds",
+ max_throttling_delay_.value().InSecondsF());
+ }
+ if (max_budget_level_) {
+ state->SetDouble("max_budget_level_in_seconds",
+ max_budget_level_.value().InSecondsF());
+ }
+
+ state->BeginArray("task_queues");
+ for (TaskQueue* queue : associated_task_queues_) {
+ state->AppendString(PointerToString(queue));
+ }
+ state->EndArray();
+
+ state->EndDictionary();
+}
+
+void CPUTimeBudgetPool::Advance(base::TimeTicks now) {
+ if (now > last_checkpoint_) {
+ if (is_enabled_) {
+ current_budget_level_ += cpu_percentage_ * (now - last_checkpoint_);
+ EnforceBudgetLevelRestrictions();
+ }
+ last_checkpoint_ = now;
+ }
+}
+
+void CPUTimeBudgetPool::EnforceBudgetLevelRestrictions() {
+ if (max_budget_level_) {
+ current_budget_level_ =
+ std::min(current_budget_level_.value(), max_budget_level_.value());
+ }
+ if (max_throttling_delay_) {
+ // Current budget level may be negative.
+ current_budget_level_ =
+ std::max(current_budget_level_.value(),
+ -max_throttling_delay_.value() * cpu_percentage_);
+ }
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/cpu_time_budget_pool.h b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/cpu_time_budget_pool.h
new file mode 100644
index 00000000000..0f21f438267
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/cpu_time_budget_pool.h
@@ -0,0 +1,133 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_COMMON_THROTTLING_CPU_TIME_BUDGET_POOL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_COMMON_THROTTLING_CPU_TIME_BUDGET_POOL_H_
+
+#include "third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h"
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/optional.h"
+#include "base/time/time.h"
+#include "third_party/blink/renderer/platform/scheduler/base/lazy_now.h"
+#include "third_party/blink/renderer/platform/scheduler/util/tracing_helper.h"
+
+namespace blink {
+namespace scheduler {
+
+// CPUTimeBudgetPool represents a collection of task queues which share a limit
+// on total cpu time.
+class PLATFORM_EXPORT CPUTimeBudgetPool : public BudgetPool {
+ public:
+ CPUTimeBudgetPool(const char* name,
+ BudgetPoolController* budget_pool_controller,
+ TraceableVariableController* tracing_controller,
+ base::TimeTicks now);
+
+ ~CPUTimeBudgetPool();
+
+ // Set max budget level, base::nullopt represent absence of max level.
+ // Max budget level prevents accumulating arbitrary large budgets when
+ // page is inactive for a very long time.
+ void SetMaxBudgetLevel(base::TimeTicks now,
+ base::Optional<base::TimeDelta> max_budget_level);
+
+ // Set max throttling duration, base::nullopt represents absense of it.
+ // Max throttling duration prevents page from being throttled for
+ // a very long period after a single long task.
+ void SetMaxThrottlingDelay(
+ base::TimeTicks now,
+ base::Optional<base::TimeDelta> max_throttling_delay);
+
+ // Set minimal budget level required to run a task. If budget pool was
+ // exhausted, it needs to accumulate at least |min_budget_to_run| time units
+ // to unblock and run tasks again. When unblocked, it still can run tasks
+ // when budget is positive but less than this level until being blocked
+ // until being blocked when budget reaches zero.
+ // This is needed for integration with WakeUpBudgetPool to prevent a situation
+ // when wake-up happened but time budget pool allows only one task to run at
+ // the moment.
+ // It is recommended to use the same value for this and WakeUpBudgetPool's
+ // wake-up window length.
+ // NOTE: This does not have an immediate effect and does not call
+ // BudgetPoolController::UnblockQueue.
+ void SetMinBudgetLevelToRun(base::TimeTicks now,
+ base::TimeDelta min_budget_level_to_run);
+
+ // Throttle task queues from this time budget pool if tasks are running
+ // for more than |cpu_percentage| per cent of wall time.
+ // This function does not affect internal time budget level.
+ void SetTimeBudgetRecoveryRate(base::TimeTicks now, double cpu_percentage);
+
+ // Increase budget level by given value. This function DOES NOT unblock
+ // queues even if they are allowed to run with increased budget level.
+ void GrantAdditionalBudget(base::TimeTicks now, base::TimeDelta budget_level);
+
+ // Set callback which will be called every time when this budget pool
+ // is throttled. Throttling duration (time until the queue is allowed
+ // to run again) is passed as a parameter to callback.
+ void SetReportingCallback(
+ base::RepeatingCallback<void(base::TimeDelta)> reporting_callback);
+
+ // BudgetPool implementation:
+ void RecordTaskRunTime(TaskQueue* queue,
+ base::TimeTicks start_time,
+ base::TimeTicks end_time) final;
+ bool CanRunTasksAt(base::TimeTicks moment, bool is_wake_up) const final;
+ base::Optional<base::TimeTicks> GetTimeTasksCanRunUntil(
+ base::TimeTicks now,
+ bool is_wake_up) const final;
+ base::TimeTicks GetNextAllowedRunTime(
+ base::TimeTicks desired_run_time) const final;
+ void OnQueueNextWakeUpChanged(TaskQueue* queue,
+ base::TimeTicks now,
+ base::TimeTicks desired_run_time) final;
+ void OnWakeUp(base::TimeTicks now) final;
+ void AsValueInto(base::trace_event::TracedValue* state,
+ base::TimeTicks now) const final;
+
+ protected:
+ QueueBlockType GetBlockType() const final;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(TaskQueueThrottlerTest, CPUTimeBudgetPool);
+
+ // Advances |last_checkpoint_| to |now| if needed and recalculates
+ // budget level.
+ void Advance(base::TimeTicks now);
+
+ // Increase |current_budget_level_| to satisfy max throttling duration
+ // condition if necessary.
+ // Decrease |current_budget_level_| to satisfy max budget level
+ // condition if necessary.
+ void EnforceBudgetLevelRestrictions();
+
+ // Max budget level which we can accrue.
+ // Tasks will be allowed to run for this time before being throttled
+ // after a very long period of inactivity.
+ base::Optional<base::TimeDelta> max_budget_level_;
+ // Max throttling delay places a lower limit on time budget level,
+ // ensuring that one long task does not cause extremely long throttling.
+ // Note that this is not a guarantee that every task will run
+ // after desired run time + max throttling duration, but a guarantee
+ // that at least one task will be run every max_throttling_delay.
+ base::Optional<base::TimeDelta> max_throttling_delay_;
+ // See CPUTimeBudgetPool::SetMinBudgetLevelToRun.
+ base::TimeDelta min_budget_level_to_run_;
+
+ TraceableCounter<base::TimeDelta, kTracingCategoryNameInfo>
+ current_budget_level_;
+ base::TimeTicks last_checkpoint_;
+ double cpu_percentage_;
+
+ base::RepeatingCallback<void(base::TimeDelta)> reporting_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(CPUTimeBudgetPool);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_COMMON_THROTTLING_CPU_TIME_BUDGET_POOL_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.cc b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.cc
new file mode 100644
index 00000000000..e6d2c4c17ac
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.cc
@@ -0,0 +1,578 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h"
+
+#include <cstdint>
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/optional.h"
+#include "third_party/blink/renderer/platform/scheduler/base/real_time_domain.h"
+#include "third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h"
+#include "third_party/blink/renderer/platform/scheduler/common/throttling/throttled_time_domain.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
+
+namespace blink {
+namespace scheduler {
+
+namespace {
+
+base::Optional<base::TimeTicks> NextTaskRunTime(LazyNow* lazy_now,
+ TaskQueue* queue) {
+ if (queue->HasTaskToRunImmediately())
+ return lazy_now->Now();
+ return queue->GetNextScheduledWakeUp();
+}
+
+template <class T>
+T Min(const base::Optional<T>& optional, const T& value) {
+ if (!optional) {
+ return value;
+ }
+ return std::min(optional.value(), value);
+}
+
+template <class T>
+base::Optional<T> Min(const base::Optional<T>& a, const base::Optional<T>& b) {
+ if (!b)
+ return a;
+ if (!a)
+ return b;
+ return std::min(a.value(), b.value());
+}
+
+template <class T>
+T Max(const base::Optional<T>& optional, const T& value) {
+ if (!optional)
+ return value;
+ return std::max(optional.value(), value);
+}
+
+template <class T>
+base::Optional<T> Max(const base::Optional<T>& a, const base::Optional<T>& b) {
+ if (!b)
+ return a;
+ if (!a)
+ return b;
+ return std::max(a.value(), b.value());
+}
+
+} // namespace
+
+TaskQueueThrottler::TaskQueueThrottler(
+ MainThreadSchedulerImpl* main_thread_scheduler,
+ TraceableVariableController* tracing_controller)
+ : control_task_queue_(main_thread_scheduler->ControlTaskQueue()),
+ main_thread_scheduler_(main_thread_scheduler),
+ tracing_controller_(tracing_controller),
+ tick_clock_(main_thread_scheduler->tick_clock()),
+ time_domain_(new ThrottledTimeDomain()),
+ allow_throttling_(true),
+ weak_factory_(this) {
+ pump_throttled_tasks_closure_.Reset(base::BindRepeating(
+ &TaskQueueThrottler::PumpThrottledTasks, weak_factory_.GetWeakPtr()));
+ forward_immediate_work_callback_ =
+ base::BindRepeating(&TaskQueueThrottler::OnQueueNextWakeUpChanged,
+ weak_factory_.GetWeakPtr());
+
+ main_thread_scheduler_->RegisterTimeDomain(time_domain_.get());
+}
+
+TaskQueueThrottler::~TaskQueueThrottler() {
+ // It's possible for queues to be still throttled, so we need to tidy up
+ // before unregistering the time domain.
+ for (const TaskQueueMap::value_type& map_entry : queue_details_) {
+ TaskQueue* task_queue = map_entry.first;
+ if (IsThrottled(task_queue)) {
+ task_queue->SetTimeDomain(main_thread_scheduler_->GetActiveTimeDomain());
+ task_queue->RemoveFence();
+ }
+ if (map_entry.second.throttling_ref_count != 0)
+ task_queue->SetObserver(nullptr);
+ }
+
+ main_thread_scheduler_->UnregisterTimeDomain(time_domain_.get());
+}
+
+void TaskQueueThrottler::IncreaseThrottleRefCount(TaskQueue* task_queue) {
+ DCHECK_NE(task_queue, control_task_queue_.get());
+
+ std::pair<TaskQueueMap::iterator, bool> insert_result =
+ queue_details_.insert(std::make_pair(task_queue, Metadata()));
+ insert_result.first->second.throttling_ref_count++;
+
+ // If ref_count is 1, the task queue is newly throttled.
+ if (insert_result.first->second.throttling_ref_count != 1)
+ return;
+
+ TRACE_EVENT1("renderer.scheduler", "TaskQueueThrottler_TaskQueueThrottled",
+ "task_queue", task_queue);
+
+ task_queue->SetObserver(this);
+
+ if (!allow_throttling_)
+ return;
+
+ task_queue->SetTimeDomain(time_domain_.get());
+ // This blocks any tasks from |task_queue| until PumpThrottledTasks() to
+ // enforce task alignment.
+ task_queue->InsertFence(TaskQueue::InsertFencePosition::kBeginningOfTime);
+
+ if (!task_queue->IsQueueEnabled())
+ return;
+
+ if (!task_queue->IsEmpty()) {
+ LazyNow lazy_now(tick_clock_);
+ OnQueueNextWakeUpChanged(task_queue,
+ NextTaskRunTime(&lazy_now, task_queue).value());
+ }
+}
+
+void TaskQueueThrottler::DecreaseThrottleRefCount(TaskQueue* task_queue) {
+ TaskQueueMap::iterator iter = queue_details_.find(task_queue);
+
+ if (iter == queue_details_.end())
+ return;
+ if (iter->second.throttling_ref_count == 0)
+ return;
+ if (--iter->second.throttling_ref_count != 0)
+ return;
+
+ TRACE_EVENT1("renderer.scheduler", "TaskQueueThrottler_TaskQueueUnthrottled",
+ "task_queue", task_queue);
+
+ task_queue->SetObserver(nullptr);
+
+ MaybeDeleteQueueMetadata(iter);
+
+ if (!allow_throttling_)
+ return;
+
+ task_queue->SetTimeDomain(main_thread_scheduler_->GetActiveTimeDomain());
+ task_queue->RemoveFence();
+}
+
+bool TaskQueueThrottler::IsThrottled(TaskQueue* task_queue) const {
+ if (!allow_throttling_)
+ return false;
+
+ auto find_it = queue_details_.find(task_queue);
+ if (find_it == queue_details_.end())
+ return false;
+ return find_it->second.throttling_ref_count > 0;
+}
+
+void TaskQueueThrottler::ShutdownTaskQueue(TaskQueue* task_queue) {
+ auto find_it = queue_details_.find(task_queue);
+ if (find_it == queue_details_.end())
+ return;
+
+ // Reset a time domain reference to a valid domain, otherwise it's possible
+ // to get a stale reference when deleting queue.
+ task_queue->SetTimeDomain(main_thread_scheduler_->GetActiveTimeDomain());
+ task_queue->RemoveFence();
+
+ std::unordered_set<BudgetPool*> budget_pools = find_it->second.budget_pools;
+ for (BudgetPool* budget_pool : budget_pools) {
+ budget_pool->UnregisterQueue(task_queue);
+ }
+
+ // Iterator may have been deleted by BudgetPool::RemoveQueue, so don't
+ // use it here.
+ queue_details_.erase(task_queue);
+
+ // NOTE: Observer is automatically unregistered when unregistering task queue.
+}
+
+void TaskQueueThrottler::OnQueueNextWakeUpChanged(
+ TaskQueue* queue,
+ base::TimeTicks next_wake_up) {
+ if (!control_task_queue_->RunsTasksInCurrentSequence()) {
+ control_task_queue_->PostTask(
+ FROM_HERE, base::BindOnce(forward_immediate_work_callback_,
+ base::RetainedRef(queue), next_wake_up));
+ return;
+ }
+
+ TRACE_EVENT0("renderer.scheduler",
+ "TaskQueueThrottler::OnQueueNextWakeUpChanged");
+
+ // We don't expect this to get called for disabled queues, but we can't DCHECK
+ // because of the above thread hop. Just bail out if the queue is disabled.
+ if (!queue->IsQueueEnabled())
+ return;
+
+ base::TimeTicks now = tick_clock_->NowTicks();
+ next_wake_up = std::max(now, next_wake_up);
+
+ auto find_it = queue_details_.find(queue);
+ if (find_it == queue_details_.end())
+ return;
+
+ for (BudgetPool* budget_pool : find_it->second.budget_pools) {
+ budget_pool->OnQueueNextWakeUpChanged(queue, now, next_wake_up);
+ }
+
+ // TODO(altimin): This probably can be removed —- budget pools should
+ // schedule this.
+ base::TimeTicks next_allowed_run_time =
+ GetNextAllowedRunTime(queue, next_wake_up);
+ MaybeSchedulePumpThrottledTasks(
+ FROM_HERE, now, std::max(next_wake_up, next_allowed_run_time));
+}
+
+void TaskQueueThrottler::PumpThrottledTasks() {
+ TRACE_EVENT0("renderer.scheduler", "TaskQueueThrottler::PumpThrottledTasks");
+ pending_pump_throttled_tasks_runtime_.reset();
+
+ LazyNow lazy_now(tick_clock_);
+
+ for (const auto& pair : budget_pools_)
+ pair.first->OnWakeUp(lazy_now.Now());
+
+ for (const TaskQueueMap::value_type& map_entry : queue_details_) {
+ TaskQueue* task_queue = map_entry.first;
+ UpdateQueueThrottlingStateInternal(lazy_now.Now(), task_queue, true);
+ }
+}
+
+/* static */
+base::TimeTicks TaskQueueThrottler::AlignedThrottledRunTime(
+ base::TimeTicks unthrottled_runtime) {
+ const base::TimeDelta one_second = base::TimeDelta::FromSeconds(1);
+ return unthrottled_runtime + one_second -
+ ((unthrottled_runtime - base::TimeTicks()) % one_second);
+}
+
+void TaskQueueThrottler::MaybeSchedulePumpThrottledTasks(
+ const base::Location& from_here,
+ base::TimeTicks now,
+ base::TimeTicks unaligned_runtime) {
+ if (!allow_throttling_)
+ return;
+
+ // TODO(altimin): Consider removing alignment here.
+ base::TimeTicks runtime =
+ std::max(now, unaligned_runtime)
+ .SnappedToNextTick(base::TimeTicks(),
+ base::TimeDelta::FromSeconds(1));
+ DCHECK_LE(now, runtime);
+
+ // If there is a pending call to PumpThrottledTasks and it's sooner than
+ // |runtime| then return.
+ if (pending_pump_throttled_tasks_runtime_ &&
+ runtime >= pending_pump_throttled_tasks_runtime_.value()) {
+ return;
+ }
+
+ pending_pump_throttled_tasks_runtime_ = runtime;
+
+ pump_throttled_tasks_closure_.Cancel();
+
+ base::TimeDelta delay = pending_pump_throttled_tasks_runtime_.value() - now;
+ TRACE_EVENT1("renderer.scheduler",
+ "TaskQueueThrottler::MaybeSchedulePumpThrottledTasks",
+ "delay_till_next_pump_ms", delay.InMilliseconds());
+ control_task_queue_->PostDelayedTask(
+ from_here, pump_throttled_tasks_closure_.GetCallback(), delay);
+}
+
+CPUTimeBudgetPool* TaskQueueThrottler::CreateCPUTimeBudgetPool(
+ const char* name) {
+ CPUTimeBudgetPool* time_budget_pool = new CPUTimeBudgetPool(
+ name, this, tracing_controller_, tick_clock_->NowTicks());
+ budget_pools_[time_budget_pool] = base::WrapUnique(time_budget_pool);
+ return time_budget_pool;
+}
+
+WakeUpBudgetPool* TaskQueueThrottler::CreateWakeUpBudgetPool(const char* name) {
+ WakeUpBudgetPool* wake_up_budget_pool =
+ new WakeUpBudgetPool(name, this, tick_clock_->NowTicks());
+ budget_pools_[wake_up_budget_pool] = base::WrapUnique(wake_up_budget_pool);
+ return wake_up_budget_pool;
+}
+
+void TaskQueueThrottler::OnTaskRunTimeReported(TaskQueue* task_queue,
+ base::TimeTicks start_time,
+ base::TimeTicks end_time) {
+ if (!IsThrottled(task_queue))
+ return;
+
+ auto find_it = queue_details_.find(task_queue);
+ if (find_it == queue_details_.end())
+ return;
+
+ for (BudgetPool* budget_pool : find_it->second.budget_pools) {
+ budget_pool->RecordTaskRunTime(task_queue, start_time, end_time);
+ }
+}
+
+void TaskQueueThrottler::UpdateQueueThrottlingState(base::TimeTicks now,
+ TaskQueue* queue) {
+ UpdateQueueThrottlingStateInternal(now, queue, false);
+}
+
+void TaskQueueThrottler::UpdateQueueThrottlingStateInternal(base::TimeTicks now,
+ TaskQueue* queue,
+ bool is_wake_up) {
+ if (!queue->IsQueueEnabled() || !IsThrottled(queue)) {
+ return;
+ }
+
+ LazyNow lazy_now(now);
+
+ base::Optional<base::TimeTicks> next_desired_run_time =
+ NextTaskRunTime(&lazy_now, queue);
+
+ if (CanRunTasksAt(queue, now, is_wake_up)) {
+ // Unblock queue if we can run tasks immediately.
+ base::Optional<base::TimeTicks> unblock_until =
+ GetTimeTasksCanRunUntil(queue, now, is_wake_up);
+ DCHECK(unblock_until);
+ if (!unblock_until || unblock_until.value() > now) {
+ queue->InsertFenceAt(unblock_until.value());
+ } else if (unblock_until.value() == now) {
+ queue->InsertFence(TaskQueue::InsertFencePosition::kNow);
+ } else {
+ DCHECK_GE(unblock_until.value(), now);
+ }
+
+ // Throttled time domain does not schedule wake-ups without explicitly
+ // being told so.
+ if (next_desired_run_time && next_desired_run_time.value() != now &&
+ next_desired_run_time.value() < unblock_until) {
+ time_domain_->SetNextTaskRunTime(next_desired_run_time.value());
+ }
+
+ base::Optional<base::TimeTicks> next_wake_up =
+ queue->GetNextScheduledWakeUp();
+ // TODO(altimin, crbug.com/813218): Find a testcase to repro freezes
+ // mentioned in the bug.
+ if (next_wake_up) {
+ MaybeSchedulePumpThrottledTasks(
+ FROM_HERE, now, GetNextAllowedRunTime(queue, next_wake_up.value()));
+ }
+
+ return;
+ }
+
+ if (!next_desired_run_time)
+ return;
+
+ base::TimeTicks next_run_time =
+ GetNextAllowedRunTime(queue, next_desired_run_time.value());
+
+ // Insert a fence of an approriate type.
+ base::Optional<QueueBlockType> block_type = GetQueueBlockType(now, queue);
+ DCHECK(block_type);
+
+ switch (block_type.value()) {
+ case QueueBlockType::kAllTasks:
+ queue->InsertFence(TaskQueue::InsertFencePosition::kBeginningOfTime);
+
+ {
+ // Braces limit the scope for a declared variable. Does not compile
+ // otherwise.
+ TRACE_EVENT1(
+ "renderer.scheduler",
+ "TaskQueueThrottler::PumpThrottledTasks_ExpensiveTaskThrottled",
+ "throttle_time_in_seconds",
+ (next_run_time - next_desired_run_time.value()).InSecondsF());
+ }
+ break;
+ case QueueBlockType::kNewTasksOnly:
+ if (!queue->HasActiveFence()) {
+ // Insert a new non-fully blocking fence only when there is no fence
+ // already in order avoid undesired unblocking of old tasks.
+ queue->InsertFence(TaskQueue::InsertFencePosition::kNow);
+ }
+ break;
+ }
+
+ // Schedule a pump.
+ MaybeSchedulePumpThrottledTasks(FROM_HERE, now, next_run_time);
+}
+
+base::Optional<QueueBlockType> TaskQueueThrottler::GetQueueBlockType(
+ base::TimeTicks now,
+ TaskQueue* queue) {
+ auto find_it = queue_details_.find(queue);
+ if (find_it == queue_details_.end())
+ return base::nullopt;
+
+ bool has_new_tasks_only_block = false;
+
+ for (BudgetPool* budget_pool : find_it->second.budget_pools) {
+ if (!budget_pool->CanRunTasksAt(now, false)) {
+ if (budget_pool->GetBlockType() == QueueBlockType::kAllTasks)
+ return QueueBlockType::kAllTasks;
+ DCHECK_EQ(budget_pool->GetBlockType(), QueueBlockType::kNewTasksOnly);
+ has_new_tasks_only_block = true;
+ }
+ }
+
+ if (has_new_tasks_only_block)
+ return QueueBlockType::kNewTasksOnly;
+ return base::nullopt;
+}
+
+void TaskQueueThrottler::AsValueInto(base::trace_event::TracedValue* state,
+ base::TimeTicks now) const {
+ if (pending_pump_throttled_tasks_runtime_) {
+ state->SetDouble(
+ "next_throttled_tasks_pump_in_seconds",
+ (pending_pump_throttled_tasks_runtime_.value() - now).InSecondsF());
+ }
+
+ state->SetBoolean("allow_throttling", allow_throttling_);
+
+ state->BeginDictionary("time_budget_pools");
+ for (const auto& map_entry : budget_pools_) {
+ BudgetPool* pool = map_entry.first;
+ pool->AsValueInto(state, now);
+ }
+ state->EndDictionary();
+
+ state->BeginDictionary("queue_details");
+ for (const auto& map_entry : queue_details_) {
+ state->BeginDictionaryWithCopiedName(PointerToString(map_entry.first));
+ state->SetInteger("throttling_ref_count",
+ map_entry.second.throttling_ref_count);
+ state->EndDictionary();
+ }
+ state->EndDictionary();
+}
+
+void TaskQueueThrottler::AddQueueToBudgetPool(TaskQueue* queue,
+ BudgetPool* budget_pool) {
+ std::pair<TaskQueueMap::iterator, bool> insert_result =
+ queue_details_.insert(std::make_pair(queue, Metadata()));
+
+ Metadata& metadata = insert_result.first->second;
+
+ DCHECK(metadata.budget_pools.find(budget_pool) ==
+ metadata.budget_pools.end());
+
+ metadata.budget_pools.insert(budget_pool);
+}
+
+void TaskQueueThrottler::RemoveQueueFromBudgetPool(TaskQueue* queue,
+ BudgetPool* budget_pool) {
+ auto find_it = queue_details_.find(queue);
+ DCHECK(find_it != queue_details_.end() &&
+ find_it->second.budget_pools.find(budget_pool) !=
+ find_it->second.budget_pools.end());
+
+ find_it->second.budget_pools.erase(budget_pool);
+
+ MaybeDeleteQueueMetadata(find_it);
+}
+
+void TaskQueueThrottler::UnregisterBudgetPool(BudgetPool* budget_pool) {
+ budget_pools_.erase(budget_pool);
+}
+
+base::TimeTicks TaskQueueThrottler::GetNextAllowedRunTime(
+ TaskQueue* queue,
+ base::TimeTicks desired_run_time) {
+ base::TimeTicks next_run_time = desired_run_time;
+
+ auto find_it = queue_details_.find(queue);
+ if (find_it == queue_details_.end())
+ return next_run_time;
+
+ for (BudgetPool* budget_pool : find_it->second.budget_pools) {
+ next_run_time = std::max(
+ next_run_time, budget_pool->GetNextAllowedRunTime(desired_run_time));
+ }
+
+ return next_run_time;
+}
+
+bool TaskQueueThrottler::CanRunTasksAt(TaskQueue* queue,
+ base::TimeTicks moment,
+ bool is_wake_up) {
+ auto find_it = queue_details_.find(queue);
+ if (find_it == queue_details_.end())
+ return true;
+
+ for (BudgetPool* budget_pool : find_it->second.budget_pools) {
+ if (!budget_pool->CanRunTasksAt(moment, is_wake_up))
+ return false;
+ }
+
+ return true;
+}
+
+base::Optional<base::TimeTicks> TaskQueueThrottler::GetTimeTasksCanRunUntil(
+ TaskQueue* queue,
+ base::TimeTicks now,
+ bool is_wake_up) const {
+ base::Optional<base::TimeTicks> result;
+ auto find_it = queue_details_.find(queue);
+ if (find_it == queue_details_.end())
+ return result;
+
+ for (BudgetPool* budget_pool : find_it->second.budget_pools) {
+ result = Min(result, budget_pool->GetTimeTasksCanRunUntil(now, is_wake_up));
+ }
+
+ return result;
+}
+
+void TaskQueueThrottler::MaybeDeleteQueueMetadata(TaskQueueMap::iterator it) {
+ if (it->second.throttling_ref_count == 0 && it->second.budget_pools.empty())
+ queue_details_.erase(it);
+}
+
+void TaskQueueThrottler::DisableThrottling() {
+ if (!allow_throttling_)
+ return;
+
+ allow_throttling_ = false;
+
+ for (const auto& map_entry : queue_details_) {
+ if (map_entry.second.throttling_ref_count == 0)
+ continue;
+
+ TaskQueue* queue = map_entry.first;
+
+ queue->SetTimeDomain(main_thread_scheduler_->GetActiveTimeDomain());
+ queue->RemoveFence();
+ }
+
+ pump_throttled_tasks_closure_.Cancel();
+ pending_pump_throttled_tasks_runtime_ = base::nullopt;
+
+ TRACE_EVENT0("renderer.scheduler", "TaskQueueThrottler_DisableThrottling");
+}
+
+void TaskQueueThrottler::EnableThrottling() {
+ if (allow_throttling_)
+ return;
+
+ allow_throttling_ = true;
+
+ LazyNow lazy_now(tick_clock_);
+
+ for (const auto& map_entry : queue_details_) {
+ if (map_entry.second.throttling_ref_count == 0)
+ continue;
+
+ TaskQueue* queue = map_entry.first;
+
+ // Throttling is enabled and task queue should be blocked immediately
+ // to enforce task alignment.
+ queue->InsertFence(TaskQueue::InsertFencePosition::kBeginningOfTime);
+ queue->SetTimeDomain(time_domain_.get());
+ UpdateQueueThrottlingState(lazy_now.Now(), queue);
+ }
+
+ TRACE_EVENT0("renderer.scheduler", "TaskQueueThrottler_EnableThrottling");
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h
new file mode 100644
index 00000000000..ce3a9dda0bb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h
@@ -0,0 +1,222 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_COMMON_THROTTLING_TASK_QUEUE_THROTTLER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_COMMON_THROTTLING_TASK_QUEUE_THROTTLER_H_
+
+#include <set>
+#include <unordered_map>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "base/threading/thread_checker.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/scheduler/base/time_domain.h"
+#include "third_party/blink/renderer/platform/scheduler/child/cancelable_closure_holder.h"
+#include "third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h"
+#include "third_party/blink/renderer/platform/scheduler/common/throttling/cpu_time_budget_pool.h"
+#include "third_party/blink/renderer/platform/scheduler/common/throttling/wake_up_budget_pool.h"
+#include "third_party/blink/renderer/platform/scheduler/util/tracing_helper.h"
+
+namespace base {
+namespace trace_event {
+class TracedValue;
+}
+} // namespace base
+
+namespace blink {
+namespace scheduler {
+
+class BudgetPool;
+class MainThreadSchedulerImpl;
+class ThrottledTimeDomain;
+class CPUTimeBudgetPool;
+class WakeUpBudgetPool;
+
+// kNewTasksOnly prevents new tasks from running (old tasks can run normally),
+// kAllTasks block queue completely.
+// kAllTasks-type block always blocks the queue completely.
+// kNewTasksOnly-type block does nothing when queue is already blocked by
+// kAllTasks, and overrides previous kNewTasksOnly block if any, which may
+// unblock some tasks.
+enum class QueueBlockType { kAllTasks, kNewTasksOnly };
+
+// Interface for BudgetPool to interact with TaskQueueThrottler.
+class PLATFORM_EXPORT BudgetPoolController {
+ public:
+ virtual ~BudgetPoolController() = default;
+
+ // To be used by BudgetPool only, use BudgetPool::{Add,Remove}Queue
+ // methods instead.
+ virtual void AddQueueToBudgetPool(TaskQueue* queue,
+ BudgetPool* budget_pool) = 0;
+ virtual void RemoveQueueFromBudgetPool(TaskQueue* queue,
+ BudgetPool* budget_pool) = 0;
+
+ // Deletes the budget pool.
+ virtual void UnregisterBudgetPool(BudgetPool* budget_pool) = 0;
+
+ // Ensure that an appropriate type of the fence is installed and schedule
+ // a pump for this queue when needed.
+ virtual void UpdateQueueThrottlingState(base::TimeTicks now,
+ TaskQueue* queue) = 0;
+
+ // Returns true if the |queue| is throttled (i.e. added to TaskQueueThrottler
+ // and throttling is not disabled).
+ virtual bool IsThrottled(TaskQueue* queue) const = 0;
+};
+
+// The job of the TaskQueueThrottler is to control when tasks posted on
+// throttled queues get run. The TaskQueueThrottler:
+// - runs throttled tasks once per second,
+// - controls time budget for task queues grouped in CPUTimeBudgetPools.
+//
+// This is done by disabling throttled queues and running
+// a special "heart beat" function |PumpThrottledTasks| which when run
+// temporarily enables throttled queues and inserts a fence to ensure tasks
+// posted from a throttled task run next time the queue is pumped.
+//
+// Of course the TaskQueueThrottler isn't the only sub-system that wants to
+// enable or disable queues. E.g. MainThreadSchedulerImpl also does this for
+// policy reasons. To prevent the systems from fighting, clients of
+// TaskQueueThrottler must use SetQueueEnabled rather than calling the function
+// directly on the queue.
+//
+// There may be more than one system that wishes to throttle a queue (e.g.
+// renderer suspension vs tab level suspension) so the TaskQueueThrottler keeps
+// a count of the number of systems that wish a queue to be throttled.
+// See IncreaseThrottleRefCount & DecreaseThrottleRefCount.
+//
+// This class is main-thread only.
+class PLATFORM_EXPORT TaskQueueThrottler : public TaskQueue::Observer,
+ public BudgetPoolController {
+ public:
+ // We use tracing controller from MainThreadSchedulerImpl because an instance
+ // of this class is always its member, so has the same lifetime.
+ TaskQueueThrottler(MainThreadSchedulerImpl* main_thread_scheduler,
+ TraceableVariableController* tracing_controller);
+
+ ~TaskQueueThrottler() override;
+
+ // TaskQueue::Observer implementation:
+ void OnQueueNextWakeUpChanged(TaskQueue* queue,
+ base::TimeTicks wake_up) override;
+
+ // BudgetPoolController implementation:
+ void AddQueueToBudgetPool(TaskQueue* queue, BudgetPool* budget_pool) override;
+ void RemoveQueueFromBudgetPool(TaskQueue* queue,
+ BudgetPool* budget_pool) override;
+ void UnregisterBudgetPool(BudgetPool* budget_pool) override;
+ void UpdateQueueThrottlingState(base::TimeTicks now,
+ TaskQueue* queue) override;
+ bool IsThrottled(TaskQueue* queue) const override;
+
+ // Increments the throttled refcount and causes |task_queue| to be throttled
+ // if its not already throttled.
+ void IncreaseThrottleRefCount(TaskQueue* task_queue);
+
+ // If the refcouint is non-zero it's decremented. If the throttled refcount
+ // becomes zero then |task_queue| is unthrottled. If the refcount was already
+ // zero this function does nothing.
+ void DecreaseThrottleRefCount(TaskQueue* task_queue);
+
+ // Removes |task_queue| from |queue_details| and from appropriate budget pool.
+ void ShutdownTaskQueue(TaskQueue* task_queue);
+
+ // Disable throttling for all queues, this setting takes precedence over
+ // all other throttling settings. Designed to be used when a global event
+ // disabling throttling happens (e.g. audio is playing).
+ void DisableThrottling();
+
+ // Enable back global throttling.
+ void EnableThrottling();
+
+ const ThrottledTimeDomain* time_domain() const { return time_domain_.get(); }
+
+ // TODO(altimin): Remove it.
+ static base::TimeTicks AlignedThrottledRunTime(
+ base::TimeTicks unthrottled_runtime);
+
+ const scoped_refptr<TaskQueue>& task_queue() const {
+ return control_task_queue_;
+ }
+
+ // Returned object is owned by |TaskQueueThrottler|.
+ CPUTimeBudgetPool* CreateCPUTimeBudgetPool(const char* name);
+ WakeUpBudgetPool* CreateWakeUpBudgetPool(const char* name);
+
+ // Accounts for given task for cpu-based throttling needs.
+ void OnTaskRunTimeReported(TaskQueue* task_queue,
+ base::TimeTicks start_time,
+ base::TimeTicks end_time);
+
+ void AsValueInto(base::trace_event::TracedValue* state,
+ base::TimeTicks now) const;
+
+ private:
+ struct Metadata {
+ Metadata() : throttling_ref_count(0) {}
+
+ size_t throttling_ref_count;
+
+ std::unordered_set<BudgetPool*> budget_pools;
+ };
+ using TaskQueueMap = std::unordered_map<TaskQueue*, Metadata>;
+
+ void PumpThrottledTasks();
+
+ // Note |unthrottled_runtime| might be in the past. When this happens we
+ // compute the delay to the next runtime based on now rather than
+ // unthrottled_runtime.
+ void MaybeSchedulePumpThrottledTasks(const base::Location& from_here,
+ base::TimeTicks now,
+ base::TimeTicks runtime);
+
+ // Return next possible time when queue is allowed to run in accordance
+ // with throttling policy.
+ base::TimeTicks GetNextAllowedRunTime(TaskQueue* queue,
+ base::TimeTicks desired_run_time);
+
+ bool CanRunTasksAt(TaskQueue* queue, base::TimeTicks moment, bool is_wake_up);
+
+ base::Optional<base::TimeTicks> GetTimeTasksCanRunUntil(
+ TaskQueue* queue,
+ base::TimeTicks now,
+ bool is_wake_up) const;
+
+ void MaybeDeleteQueueMetadata(TaskQueueMap::iterator it);
+
+ void UpdateQueueThrottlingStateInternal(base::TimeTicks now,
+ TaskQueue* queue,
+ bool is_wake_up);
+
+ base::Optional<QueueBlockType> GetQueueBlockType(base::TimeTicks now,
+ TaskQueue* queue);
+
+ TaskQueueMap queue_details_;
+ base::RepeatingCallback<void(TaskQueue*, base::TimeTicks)>
+ forward_immediate_work_callback_;
+ scoped_refptr<TaskQueue> control_task_queue_;
+ MainThreadSchedulerImpl* main_thread_scheduler_; // NOT OWNED
+ TraceableVariableController* tracing_controller_; // NOT OWNED
+ const base::TickClock* tick_clock_; // NOT OWNED
+ std::unique_ptr<ThrottledTimeDomain> time_domain_;
+
+ CancelableClosureHolder pump_throttled_tasks_closure_;
+ base::Optional<base::TimeTicks> pending_pump_throttled_tasks_runtime_;
+ bool allow_throttling_;
+
+ std::unordered_map<BudgetPool*, std::unique_ptr<BudgetPool>> budget_pools_;
+
+ base::WeakPtrFactory<TaskQueueThrottler> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(TaskQueueThrottler);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_COMMON_THROTTLING_TASK_QUEUE_THROTTLER_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler_unittest.cc
new file mode 100644
index 00000000000..e429542157e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler_unittest.cc
@@ -0,0 +1,1406 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h"
+
+#include <stddef.h>
+
+#include <deque>
+#include <memory>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "components/viz/test/ordered_simple_task_runner.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/scheduler/base/real_time_domain.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_manager.h"
+#include "third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain.h"
+#include "third_party/blink/renderer/platform/scheduler/test/task_queue_manager_for_test.h"
+
+using testing::ElementsAre;
+
+namespace blink {
+namespace scheduler {
+// To avoid symbol collisions in jumbo builds.
+namespace task_queue_throttler_unittest {
+
+bool MessageLoopTaskCounter(size_t* count) {
+ *count = *count + 1;
+ return true;
+}
+
+void NopTask() {}
+
+void AddOneTask(size_t* count) {
+ (*count)++;
+}
+
+void RunTenTimesTask(size_t* count, scoped_refptr<TaskQueue> timer_queue) {
+ if (++(*count) < 10) {
+ timer_queue->PostTask(FROM_HERE,
+ base::BindOnce(&RunTenTimesTask, count, timer_queue));
+ }
+}
+
+// Test clock which simulates passage of time by automatically
+// advancing time with each call to Now().
+class AutoAdvancingTestClock : public base::SimpleTestTickClock {
+ public:
+ AutoAdvancingTestClock(base::TimeDelta interval)
+ : advancing_interval_(interval) {}
+ ~AutoAdvancingTestClock() override = default;
+
+ base::TimeTicks NowTicks() const override {
+ const_cast<AutoAdvancingTestClock*>(this)->Advance(advancing_interval_);
+ return SimpleTestTickClock::NowTicks();
+ }
+
+ base::TimeTicks GetNowTicksWithoutAdvancing() {
+ return SimpleTestTickClock::NowTicks();
+ }
+
+ private:
+ base::TimeDelta advancing_interval_;
+};
+
+class TaskQueueThrottlerTest : public testing::Test {
+ public:
+ TaskQueueThrottlerTest() = default;
+ ~TaskQueueThrottlerTest() override = default;
+
+ void SetUp() override {
+ clock_ = CreateClock();
+ clock_->Advance(base::TimeDelta::FromMicroseconds(5000));
+ mock_task_runner_ =
+ base::MakeRefCounted<cc::OrderedSimpleTaskRunner>(clock_.get(), true);
+ scheduler_.reset(new MainThreadSchedulerImpl(
+ TaskQueueManagerForTest::Create(nullptr, mock_task_runner_,
+ clock_.get()),
+ base::nullopt));
+ task_queue_throttler_ = scheduler_->task_queue_throttler();
+ timer_queue_ = scheduler_->NewTimerTaskQueue(
+ MainThreadTaskQueue::QueueType::kFrameThrottleable);
+ }
+
+ void TearDown() override {
+ scheduler_->Shutdown();
+ scheduler_.reset();
+ }
+
+ void ExpectThrottled(scoped_refptr<TaskQueue> timer_queue) {
+ size_t count = 0;
+ timer_queue->PostTask(
+ FROM_HERE, base::BindOnce(&RunTenTimesTask, &count, timer_queue));
+
+ mock_task_runner_->RunForPeriod(base::TimeDelta::FromSeconds(1));
+ EXPECT_LE(count, 1u);
+
+ // Make sure the rest of the tasks run or we risk a UAF on |count|.
+ mock_task_runner_->RunForPeriod(base::TimeDelta::FromSeconds(10));
+ EXPECT_EQ(10u, count);
+ }
+
+ void ExpectUnthrottled(scoped_refptr<TaskQueue> timer_queue) {
+ size_t count = 0;
+ timer_queue->PostTask(
+ FROM_HERE, base::BindOnce(&RunTenTimesTask, &count, timer_queue));
+
+ mock_task_runner_->RunForPeriod(base::TimeDelta::FromSeconds(1));
+ EXPECT_EQ(10u, count);
+ mock_task_runner_->RunUntilIdle();
+ }
+
+ bool IsQueueBlocked(TaskQueue* task_queue) {
+ internal::TaskQueueImpl* task_queue_impl = task_queue->GetTaskQueueImpl();
+ if (!task_queue_impl->IsQueueEnabled())
+ return true;
+ return task_queue_impl->GetFenceForTest() ==
+ static_cast<internal::EnqueueOrder>(
+ internal::EnqueueOrderValues::kBlockingFence);
+ }
+
+ protected:
+ virtual std::unique_ptr<AutoAdvancingTestClock> CreateClock() {
+ return std::make_unique<AutoAdvancingTestClock>(base::TimeDelta());
+ }
+
+ std::unique_ptr<AutoAdvancingTestClock> clock_;
+ scoped_refptr<cc::OrderedSimpleTaskRunner> mock_task_runner_;
+ std::unique_ptr<MainThreadSchedulerImpl> scheduler_;
+ scoped_refptr<TaskQueue> timer_queue_;
+ TaskQueueThrottler* task_queue_throttler_; // NOT OWNED
+
+ DISALLOW_COPY_AND_ASSIGN(TaskQueueThrottlerTest);
+};
+
+class TaskQueueThrottlerWithAutoAdvancingTimeTest
+ : public TaskQueueThrottlerTest,
+ public testing::WithParamInterface<bool> {
+ public:
+ TaskQueueThrottlerWithAutoAdvancingTimeTest()
+ : auto_advance_time_interval_(GetParam()
+ ? base::TimeDelta::FromMicroseconds(1)
+ : base::TimeDelta()) {}
+ ~TaskQueueThrottlerWithAutoAdvancingTimeTest() override = default;
+
+ protected:
+ std::unique_ptr<AutoAdvancingTestClock> CreateClock() override {
+ return std::make_unique<AutoAdvancingTestClock>(
+ auto_advance_time_interval_);
+ }
+
+ base::TimeDelta auto_advance_time_interval_;
+
+ DISALLOW_COPY_AND_ASSIGN(TaskQueueThrottlerWithAutoAdvancingTimeTest);
+};
+
+INSTANTIATE_TEST_CASE_P(All,
+ TaskQueueThrottlerWithAutoAdvancingTimeTest,
+ testing::Bool());
+
+TEST_F(TaskQueueThrottlerTest, ThrottledTasksReportRealTime) {
+ EXPECT_EQ(timer_queue_->GetTimeDomain()->Now(),
+ clock_->GetNowTicksWithoutAdvancing());
+
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+ EXPECT_EQ(timer_queue_->GetTimeDomain()->Now(),
+ clock_->GetNowTicksWithoutAdvancing());
+
+ clock_->Advance(base::TimeDelta::FromMilliseconds(250));
+ // Make sure the throttled time domain's Now() reports the same as the
+ // underlying clock.
+ EXPECT_EQ(timer_queue_->GetTimeDomain()->Now(),
+ clock_->GetNowTicksWithoutAdvancing());
+}
+
+TEST_F(TaskQueueThrottlerTest, AlignedThrottledRunTime) {
+ EXPECT_EQ(base::TimeTicks() + base::TimeDelta::FromSecondsD(1.0),
+ TaskQueueThrottler::AlignedThrottledRunTime(
+ base::TimeTicks() + base::TimeDelta::FromSecondsD(0.0)));
+
+ EXPECT_EQ(base::TimeTicks() + base::TimeDelta::FromSecondsD(1.0),
+ TaskQueueThrottler::AlignedThrottledRunTime(
+ base::TimeTicks() + base::TimeDelta::FromSecondsD(0.1)));
+
+ EXPECT_EQ(base::TimeTicks() + base::TimeDelta::FromSecondsD(1.0),
+ TaskQueueThrottler::AlignedThrottledRunTime(
+ base::TimeTicks() + base::TimeDelta::FromSecondsD(0.2)));
+
+ EXPECT_EQ(base::TimeTicks() + base::TimeDelta::FromSecondsD(1.0),
+ TaskQueueThrottler::AlignedThrottledRunTime(
+ base::TimeTicks() + base::TimeDelta::FromSecondsD(0.5)));
+
+ EXPECT_EQ(base::TimeTicks() + base::TimeDelta::FromSecondsD(1.0),
+ TaskQueueThrottler::AlignedThrottledRunTime(
+ base::TimeTicks() + base::TimeDelta::FromSecondsD(0.8)));
+
+ EXPECT_EQ(base::TimeTicks() + base::TimeDelta::FromSecondsD(1.0),
+ TaskQueueThrottler::AlignedThrottledRunTime(
+ base::TimeTicks() + base::TimeDelta::FromSecondsD(0.9)));
+
+ EXPECT_EQ(base::TimeTicks() + base::TimeDelta::FromSecondsD(2.0),
+ TaskQueueThrottler::AlignedThrottledRunTime(
+ base::TimeTicks() + base::TimeDelta::FromSecondsD(1.0)));
+
+ EXPECT_EQ(base::TimeTicks() + base::TimeDelta::FromSecondsD(2.0),
+ TaskQueueThrottler::AlignedThrottledRunTime(
+ base::TimeTicks() + base::TimeDelta::FromSecondsD(1.1)));
+
+ EXPECT_EQ(base::TimeTicks() + base::TimeDelta::FromSecondsD(9.0),
+ TaskQueueThrottler::AlignedThrottledRunTime(
+ base::TimeTicks() + base::TimeDelta::FromSecondsD(8.0)));
+
+ EXPECT_EQ(base::TimeTicks() + base::TimeDelta::FromSecondsD(9.0),
+ TaskQueueThrottler::AlignedThrottledRunTime(
+ base::TimeTicks() + base::TimeDelta::FromSecondsD(8.1)));
+}
+
+namespace {
+
+// Round up time to milliseconds to deal with autoadvancing time.
+// TODO(altimin): round time only when autoadvancing time is enabled.
+base::TimeDelta RoundTimeToMilliseconds(base::TimeDelta time) {
+ return time - time % base::TimeDelta::FromMilliseconds(1);
+}
+
+base::TimeTicks RoundTimeToMilliseconds(base::TimeTicks time) {
+ return base::TimeTicks() + RoundTimeToMilliseconds(time - base::TimeTicks());
+}
+
+void TestTask(std::vector<base::TimeTicks>* run_times,
+ AutoAdvancingTestClock* clock) {
+ run_times->push_back(
+ RoundTimeToMilliseconds(clock->GetNowTicksWithoutAdvancing()));
+}
+
+void ExpensiveTestTask(std::vector<base::TimeTicks>* run_times,
+ AutoAdvancingTestClock* clock) {
+ run_times->push_back(
+ RoundTimeToMilliseconds(clock->GetNowTicksWithoutAdvancing()));
+ clock->Advance(base::TimeDelta::FromMilliseconds(250));
+}
+
+void RecordThrottling(std::vector<base::TimeDelta>* reported_throttling_times,
+ base::TimeDelta throttling_duration) {
+ reported_throttling_times->push_back(
+ RoundTimeToMilliseconds(throttling_duration));
+}
+} // namespace
+
+TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest, TimerAlignment) {
+ std::vector<base::TimeTicks> run_times;
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, clock_.get()),
+ base::TimeDelta::FromMilliseconds(200.0));
+
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, clock_.get()),
+ base::TimeDelta::FromMilliseconds(800.0));
+
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, clock_.get()),
+ base::TimeDelta::FromMilliseconds(1200.0));
+
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, clock_.get()),
+ base::TimeDelta::FromMilliseconds(8300.0));
+
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+
+ mock_task_runner_->RunUntilIdle();
+
+ // Times are aligned to a multiple of 1000 milliseconds.
+ EXPECT_THAT(
+ run_times,
+ ElementsAre(
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(1000.0),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(1000.0),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(2000.0),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(9000.0)));
+}
+
+TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
+ TimerAlignment_Unthrottled) {
+ std::vector<base::TimeTicks> run_times;
+ base::TimeTicks start_time = clock_->GetNowTicksWithoutAdvancing();
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, clock_.get()),
+ base::TimeDelta::FromMilliseconds(200.0));
+
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, clock_.get()),
+ base::TimeDelta::FromMilliseconds(800.0));
+
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, clock_.get()),
+ base::TimeDelta::FromMilliseconds(1200.0));
+
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, clock_.get()),
+ base::TimeDelta::FromMilliseconds(8300.0));
+
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+ task_queue_throttler_->DecreaseThrottleRefCount(timer_queue_.get());
+
+ mock_task_runner_->RunUntilIdle();
+
+ // Times are not aligned.
+ EXPECT_THAT(
+ run_times,
+ ElementsAre(RoundTimeToMilliseconds(
+ start_time + base::TimeDelta::FromMilliseconds(200.0)),
+ RoundTimeToMilliseconds(
+ start_time + base::TimeDelta::FromMilliseconds(800.0)),
+ RoundTimeToMilliseconds(
+ start_time + base::TimeDelta::FromMilliseconds(1200.0)),
+ RoundTimeToMilliseconds(
+ start_time + base::TimeDelta::FromMilliseconds(8300.0))));
+}
+
+TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest, Refcount) {
+ ExpectUnthrottled(timer_queue_.get());
+
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+ ExpectThrottled(timer_queue_);
+
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+ ExpectThrottled(timer_queue_);
+
+ task_queue_throttler_->DecreaseThrottleRefCount(timer_queue_.get());
+ ExpectThrottled(timer_queue_);
+
+ task_queue_throttler_->DecreaseThrottleRefCount(timer_queue_.get());
+ ExpectUnthrottled(timer_queue_);
+
+ // Should be a NOP.
+ task_queue_throttler_->DecreaseThrottleRefCount(timer_queue_.get());
+ ExpectUnthrottled(timer_queue_);
+
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+ ExpectThrottled(timer_queue_);
+}
+
+TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
+ ThrotlingAnEmptyQueueDoesNotPostPumpThrottledTasksLocked) {
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+
+ EXPECT_TRUE(task_queue_throttler_->task_queue()->IsEmpty());
+}
+
+TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
+ OnTimeDomainHasImmediateWork_EnabledQueue) {
+ task_queue_throttler_->OnQueueNextWakeUpChanged(timer_queue_.get(),
+ base::TimeTicks());
+ // Check PostPumpThrottledTasksLocked was called.
+ EXPECT_FALSE(task_queue_throttler_->task_queue()->IsEmpty());
+}
+
+TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
+ OnTimeDomainHasImmediateWork_DisabledQueue) {
+ std::unique_ptr<TaskQueue::QueueEnabledVoter> voter =
+ timer_queue_->CreateQueueEnabledVoter();
+ voter->SetQueueEnabled(false);
+
+ task_queue_throttler_->OnQueueNextWakeUpChanged(timer_queue_.get(),
+ base::TimeTicks());
+ // Check PostPumpThrottledTasksLocked was not called.
+ EXPECT_TRUE(task_queue_throttler_->task_queue()->IsEmpty());
+}
+
+TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
+ ThrottlingADisabledQueueDoesNotPostPumpThrottledTasks) {
+ timer_queue_->PostTask(FROM_HERE, base::BindOnce(&NopTask));
+
+ std::unique_ptr<TaskQueue::QueueEnabledVoter> voter =
+ timer_queue_->CreateQueueEnabledVoter();
+ voter->SetQueueEnabled(false);
+
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+ EXPECT_TRUE(task_queue_throttler_->task_queue()->IsEmpty());
+
+ // Enabling it should trigger a call to PostPumpThrottledTasksLocked.
+ voter->SetQueueEnabled(true);
+ EXPECT_FALSE(task_queue_throttler_->task_queue()->IsEmpty());
+}
+
+TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
+ ThrottlingADisabledQueueDoesNotPostPumpThrottledTasks_DelayedTask) {
+ timer_queue_->PostDelayedTask(FROM_HERE, base::BindOnce(&NopTask),
+ base::TimeDelta::FromMilliseconds(1));
+
+ std::unique_ptr<TaskQueue::QueueEnabledVoter> voter =
+ timer_queue_->CreateQueueEnabledVoter();
+ voter->SetQueueEnabled(false);
+
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+ EXPECT_TRUE(task_queue_throttler_->task_queue()->IsEmpty());
+
+ // Enabling it should trigger a call to PostPumpThrottledTasksLocked.
+ voter->SetQueueEnabled(true);
+ EXPECT_FALSE(task_queue_throttler_->task_queue()->IsEmpty());
+}
+
+TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest, WakeUpForNonDelayedTask) {
+ std::vector<base::TimeTicks> run_times;
+
+ // Nothing is posted on timer_queue_ so PumpThrottledTasks will not tick.
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+
+ // Posting a task should trigger the pump.
+ timer_queue_->PostTask(FROM_HERE,
+ base::BindOnce(&TestTask, &run_times, clock_.get()));
+
+ mock_task_runner_->RunUntilIdle();
+ EXPECT_THAT(run_times,
+ ElementsAre(base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(1000.0)));
+}
+
+TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest, WakeUpForDelayedTask) {
+ std::vector<base::TimeTicks> run_times;
+
+ // Nothing is posted on timer_queue_ so PumpThrottledTasks will not tick.
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+
+ // Posting a task should trigger the pump.
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, clock_.get()),
+ base::TimeDelta::FromMilliseconds(1200.0));
+
+ mock_task_runner_->RunUntilIdle();
+ EXPECT_THAT(run_times,
+ ElementsAre(base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(2000.0)));
+}
+
+TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
+ SingleThrottledTaskPumpedAndRunWithNoExtraneousMessageLoopTasks) {
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+
+ base::TimeDelta delay(base::TimeDelta::FromMilliseconds(10));
+ timer_queue_->PostDelayedTask(FROM_HERE, base::BindOnce(&NopTask), delay);
+
+ size_t task_count = 0;
+ mock_task_runner_->RunTasksWhile(
+ base::BindRepeating(&MessageLoopTaskCounter, &task_count));
+
+ // Run the task.
+ EXPECT_EQ(1u, task_count);
+}
+
+TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
+ SingleFutureThrottledTaskPumpedAndRunWithNoExtraneousMessageLoopTasks) {
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+
+ base::TimeDelta delay(base::TimeDelta::FromSecondsD(15.5));
+ timer_queue_->PostDelayedTask(FROM_HERE, base::BindOnce(&NopTask), delay);
+
+ size_t task_count = 0;
+ mock_task_runner_->RunTasksWhile(
+ base::BindRepeating(&MessageLoopTaskCounter, &task_count));
+
+ // Run the delayed task.
+ EXPECT_EQ(1u, task_count);
+}
+
+TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
+ TwoFutureThrottledTaskPumpedAndRunWithNoExtraneousMessageLoopTasks) {
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+ std::vector<base::TimeTicks> run_times;
+
+ base::TimeDelta delay(base::TimeDelta::FromSecondsD(15.5));
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, clock_.get()), delay);
+
+ base::TimeDelta delay2(base::TimeDelta::FromSecondsD(5.5));
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, clock_.get()), delay2);
+
+ size_t task_count = 0;
+ mock_task_runner_->RunTasksWhile(
+ base::BindRepeating(&MessageLoopTaskCounter, &task_count));
+
+ // Run both delayed tasks.
+ EXPECT_EQ(2u, task_count);
+
+ EXPECT_THAT(
+ run_times,
+ ElementsAre(base::TimeTicks() + base::TimeDelta::FromSeconds(6),
+ base::TimeTicks() + base::TimeDelta::FromSeconds(16)));
+}
+
+TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
+ TaskDelayIsBasedOnRealTime) {
+ std::vector<base::TimeTicks> run_times;
+
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+
+ // Post an initial task that should run at the first aligned time period.
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, clock_.get()),
+ base::TimeDelta::FromMilliseconds(900.0));
+
+ mock_task_runner_->RunUntilIdle();
+
+ // Advance realtime.
+ clock_->Advance(base::TimeDelta::FromMilliseconds(250));
+
+ // Post a task that due to real time + delay must run in the third aligned
+ // time period.
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, clock_.get()),
+ base::TimeDelta::FromMilliseconds(900.0));
+
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(
+ run_times,
+ ElementsAre(
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(1000.0),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(3000.0)));
+}
+
+TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest, TaskQueueDisabledTillPump) {
+ size_t count = 0;
+ timer_queue_->PostTask(FROM_HERE, base::BindOnce(&AddOneTask, &count));
+
+ EXPECT_FALSE(IsQueueBlocked(timer_queue_.get()));
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+ EXPECT_TRUE(IsQueueBlocked(timer_queue_.get()));
+
+ mock_task_runner_->RunUntilIdle(); // Wait until the pump.
+ EXPECT_EQ(1u, count); // The task got run
+ EXPECT_FALSE(IsQueueBlocked(timer_queue_.get()));
+}
+
+TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
+ DoubleIncrementDoubleDecrement) {
+ timer_queue_->PostTask(FROM_HERE, base::BindOnce(&NopTask));
+
+ EXPECT_FALSE(IsQueueBlocked(timer_queue_.get()));
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+ EXPECT_TRUE(IsQueueBlocked(timer_queue_.get()));
+ task_queue_throttler_->DecreaseThrottleRefCount(timer_queue_.get());
+ task_queue_throttler_->DecreaseThrottleRefCount(timer_queue_.get());
+ EXPECT_FALSE(IsQueueBlocked(timer_queue_.get()));
+}
+
+TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
+ EnableVirtualTimeThenIncrement) {
+ timer_queue_->PostTask(FROM_HERE, base::BindOnce(&NopTask));
+
+ scheduler_->EnableVirtualTime(
+ MainThreadSchedulerImpl::BaseTimeOverridePolicy::DO_NOT_OVERRIDE);
+ EXPECT_EQ(timer_queue_->GetTimeDomain(), scheduler_->GetVirtualTimeDomain());
+
+ EXPECT_FALSE(IsQueueBlocked(timer_queue_.get()));
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+ EXPECT_FALSE(IsQueueBlocked(timer_queue_.get()));
+ EXPECT_EQ(timer_queue_->GetTimeDomain(), scheduler_->GetVirtualTimeDomain());
+}
+
+TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
+ IncrementThenEnableVirtualTime) {
+ timer_queue_->PostTask(FROM_HERE, base::BindOnce(&NopTask));
+
+ EXPECT_FALSE(IsQueueBlocked(timer_queue_.get()));
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+ EXPECT_TRUE(IsQueueBlocked(timer_queue_.get()));
+
+ scheduler_->EnableVirtualTime(
+ MainThreadSchedulerImpl::BaseTimeOverridePolicy::DO_NOT_OVERRIDE);
+ EXPECT_FALSE(IsQueueBlocked(timer_queue_.get()));
+ EXPECT_EQ(timer_queue_->GetTimeDomain(), scheduler_->GetVirtualTimeDomain());
+}
+
+TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest, TimeBasedThrottling) {
+ std::vector<base::TimeTicks> run_times;
+
+ CPUTimeBudgetPool* pool =
+ task_queue_throttler_->CreateCPUTimeBudgetPool("test");
+
+ pool->SetTimeBudgetRecoveryRate(base::TimeTicks(), 0.1);
+ pool->AddQueue(base::TimeTicks(), timer_queue_.get());
+
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+
+ // Submit two tasks. They should be aligned, and second one should be
+ // throttled.
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&ExpensiveTestTask, &run_times, clock_.get()),
+ base::TimeDelta::FromMilliseconds(200));
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&ExpensiveTestTask, &run_times, clock_.get()),
+ base::TimeDelta::FromMilliseconds(200));
+
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(run_times,
+ ElementsAre(base::TimeTicks() + base::TimeDelta::FromSeconds(1),
+ base::TimeTicks() + base::TimeDelta::FromSeconds(3)));
+
+ pool->RemoveQueue(clock_->GetNowTicksWithoutAdvancing(), timer_queue_.get());
+ run_times.clear();
+
+ // Queue was removed from CPUTimeBudgetPool, only timer alignment should be
+ // active now.
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&ExpensiveTestTask, &run_times, clock_.get()),
+ base::TimeDelta::FromMilliseconds(200));
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&ExpensiveTestTask, &run_times, clock_.get()),
+ base::TimeDelta::FromMilliseconds(200));
+
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(
+ run_times,
+ ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(4000),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(4250)));
+
+ task_queue_throttler_->DecreaseThrottleRefCount(timer_queue_.get());
+ pool->Close();
+}
+
+TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
+ EnableAndDisableCPUTimeBudgetPool) {
+ std::vector<base::TimeTicks> run_times;
+
+ CPUTimeBudgetPool* pool =
+ task_queue_throttler_->CreateCPUTimeBudgetPool("test");
+ EXPECT_TRUE(pool->IsThrottlingEnabled());
+
+ pool->SetTimeBudgetRecoveryRate(base::TimeTicks(), 0.1);
+ pool->AddQueue(base::TimeTicks(), timer_queue_.get());
+
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+
+ // Post an expensive task. Pool is now throttled.
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&ExpensiveTestTask, &run_times, clock_.get()),
+ base::TimeDelta::FromMilliseconds(200));
+
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(run_times, ElementsAre(base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(1000)));
+ run_times.clear();
+
+ LazyNow lazy_now(clock_.get());
+ pool->DisableThrottling(&lazy_now);
+ EXPECT_FALSE(pool->IsThrottlingEnabled());
+
+ // Pool should not be throttled now.
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&ExpensiveTestTask, &run_times, clock_.get()),
+ base::TimeDelta::FromMilliseconds(200));
+
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(run_times, ElementsAre(base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(2000)));
+ run_times.clear();
+
+ lazy_now = LazyNow(clock_.get());
+ pool->EnableThrottling(&lazy_now);
+ EXPECT_TRUE(pool->IsThrottlingEnabled());
+
+ // Because time pool was disabled, time budget level did not replenish
+ // and queue is throttled.
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&ExpensiveTestTask, &run_times, clock_.get()),
+ base::TimeDelta::FromMilliseconds(200));
+
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(run_times, ElementsAre(base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(4000)));
+ run_times.clear();
+
+ task_queue_throttler_->DecreaseThrottleRefCount(timer_queue_.get());
+
+ pool->RemoveQueue(clock_->GetNowTicksWithoutAdvancing(), timer_queue_.get());
+ pool->Close();
+}
+
+TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
+ ImmediateTasksTimeBudgetThrottling) {
+ std::vector<base::TimeTicks> run_times;
+
+ CPUTimeBudgetPool* pool =
+ task_queue_throttler_->CreateCPUTimeBudgetPool("test");
+
+ pool->SetTimeBudgetRecoveryRate(base::TimeTicks(), 0.1);
+ pool->AddQueue(base::TimeTicks(), timer_queue_.get());
+
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+
+ // Submit two tasks. They should be aligned, and second one should be
+ // throttled.
+ timer_queue_->PostTask(
+ FROM_HERE, base::BindOnce(&ExpensiveTestTask, &run_times, clock_.get()));
+ timer_queue_->PostTask(
+ FROM_HERE, base::BindOnce(&ExpensiveTestTask, &run_times, clock_.get()));
+
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(run_times,
+ ElementsAre(base::TimeTicks() + base::TimeDelta::FromSeconds(1),
+ base::TimeTicks() + base::TimeDelta::FromSeconds(3)));
+
+ pool->RemoveQueue(clock_->GetNowTicksWithoutAdvancing(), timer_queue_.get());
+ run_times.clear();
+
+ // Queue was removed from CPUTimeBudgetPool, only timer alignment should be
+ // active now.
+ timer_queue_->PostTask(
+ FROM_HERE, base::BindOnce(&ExpensiveTestTask, &run_times, clock_.get()));
+ timer_queue_->PostTask(
+ FROM_HERE, base::BindOnce(&ExpensiveTestTask, &run_times, clock_.get()));
+
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(
+ run_times,
+ ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(4000),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(4250)));
+
+ task_queue_throttler_->DecreaseThrottleRefCount(timer_queue_.get());
+ pool->Close();
+}
+
+TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
+ TwoQueuesTimeBudgetThrottling) {
+ std::vector<base::TimeTicks> run_times;
+
+ scoped_refptr<TaskQueue> second_queue = scheduler_->NewTimerTaskQueue(
+ MainThreadTaskQueue::QueueType::kFrameThrottleable);
+
+ CPUTimeBudgetPool* pool =
+ task_queue_throttler_->CreateCPUTimeBudgetPool("test");
+
+ pool->SetTimeBudgetRecoveryRate(base::TimeTicks(), 0.1);
+ pool->AddQueue(base::TimeTicks(), timer_queue_.get());
+ pool->AddQueue(base::TimeTicks(), second_queue.get());
+
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+ task_queue_throttler_->IncreaseThrottleRefCount(second_queue.get());
+
+ timer_queue_->PostTask(
+ FROM_HERE, base::BindOnce(&ExpensiveTestTask, &run_times, clock_.get()));
+ second_queue->PostTask(
+ FROM_HERE, base::BindOnce(&ExpensiveTestTask, &run_times, clock_.get()));
+
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(run_times,
+ ElementsAre(base::TimeTicks() + base::TimeDelta::FromSeconds(1),
+ base::TimeTicks() + base::TimeDelta::FromSeconds(3)));
+
+ task_queue_throttler_->DecreaseThrottleRefCount(timer_queue_.get());
+ task_queue_throttler_->DecreaseThrottleRefCount(second_queue.get());
+
+ pool->RemoveQueue(clock_->GetNowTicksWithoutAdvancing(), timer_queue_.get());
+ pool->RemoveQueue(clock_->GetNowTicksWithoutAdvancing(), second_queue.get());
+
+ pool->Close();
+}
+
+TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
+ DisabledTimeBudgetDoesNotAffectThrottledQueues) {
+ std::vector<base::TimeTicks> run_times;
+ LazyNow lazy_now(clock_.get());
+
+ CPUTimeBudgetPool* pool =
+ task_queue_throttler_->CreateCPUTimeBudgetPool("test");
+ pool->SetTimeBudgetRecoveryRate(lazy_now.Now(), 0.1);
+ pool->DisableThrottling(&lazy_now);
+
+ pool->AddQueue(lazy_now.Now(), timer_queue_.get());
+
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&ExpensiveTestTask, &run_times, clock_.get()),
+ base::TimeDelta::FromMilliseconds(100));
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&ExpensiveTestTask, &run_times, clock_.get()),
+ base::TimeDelta::FromMilliseconds(100));
+
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(
+ run_times,
+ ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(1000),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(1250)));
+}
+
+TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
+ TimeBudgetThrottlingDoesNotAffectUnthrottledQueues) {
+ std::vector<base::TimeTicks> run_times;
+
+ CPUTimeBudgetPool* pool =
+ task_queue_throttler_->CreateCPUTimeBudgetPool("test");
+ pool->SetTimeBudgetRecoveryRate(base::TimeTicks(), 0.1);
+
+ LazyNow lazy_now(clock_.get());
+ pool->DisableThrottling(&lazy_now);
+
+ pool->AddQueue(clock_->GetNowTicksWithoutAdvancing(), timer_queue_.get());
+
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&ExpensiveTestTask, &run_times, clock_.get()),
+ base::TimeDelta::FromMilliseconds(100));
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&ExpensiveTestTask, &run_times, clock_.get()),
+ base::TimeDelta::FromMilliseconds(100));
+
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(
+ run_times,
+ ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(105),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(355)));
+}
+
+TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest, MaxThrottlingDelay) {
+ std::vector<base::TimeTicks> run_times;
+
+ CPUTimeBudgetPool* pool =
+ task_queue_throttler_->CreateCPUTimeBudgetPool("test");
+
+ pool->SetMaxThrottlingDelay(base::TimeTicks(),
+ base::TimeDelta::FromMinutes(1));
+
+ pool->SetTimeBudgetRecoveryRate(base::TimeTicks(), 0.001);
+ pool->AddQueue(base::TimeTicks(), timer_queue_.get());
+
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+
+ for (int i = 0; i < 5; ++i) {
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&ExpensiveTestTask, &run_times, clock_.get()),
+ base::TimeDelta::FromMilliseconds(200));
+ }
+
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(
+ run_times,
+ ElementsAre(base::TimeTicks() + base::TimeDelta::FromSeconds(1),
+ base::TimeTicks() + base::TimeDelta::FromSeconds(62),
+ base::TimeTicks() + base::TimeDelta::FromSeconds(123),
+ base::TimeTicks() + base::TimeDelta::FromSeconds(184),
+ base::TimeTicks() + base::TimeDelta::FromSeconds(245)));
+}
+
+TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
+ EnableAndDisableThrottling) {
+ std::vector<base::TimeTicks> run_times;
+
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, clock_.get()),
+ base::TimeDelta::FromMilliseconds(200));
+
+ mock_task_runner_->RunUntilTime(base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(300));
+
+ // Disable throttling - task should run immediately.
+ task_queue_throttler_->DisableThrottling();
+
+ mock_task_runner_->RunUntilTime(base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(500));
+
+ EXPECT_THAT(run_times, ElementsAre(base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(300)));
+ run_times.clear();
+
+ // Schedule a task at 900ms. It should proceed as normal.
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, clock_.get()),
+ base::TimeDelta::FromMilliseconds(400));
+
+ // Schedule a task at 1200ms. It should proceed as normal.
+ // PumpThrottledTasks was scheduled at 1000ms, so it needs to be checked
+ // that it was cancelled and it does not interfere with tasks posted before
+ // 1s mark and scheduled to run after 1s mark.
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, clock_.get()),
+ base::TimeDelta::FromMilliseconds(700));
+
+ mock_task_runner_->RunUntilTime(base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(1300));
+
+ EXPECT_THAT(
+ run_times,
+ ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(900),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(1200)));
+ run_times.clear();
+
+ // Schedule a task at 1500ms. It should be throttled because of enabled
+ // throttling.
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, clock_.get()),
+ base::TimeDelta::FromMilliseconds(200));
+
+ mock_task_runner_->RunUntilTime(base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(1400));
+
+ // Throttling is enabled and new task should be aligned.
+ task_queue_throttler_->EnableThrottling();
+
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(run_times, ElementsAre(base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(2000)));
+}
+
+TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest, ReportThrottling) {
+ std::vector<base::TimeTicks> run_times;
+ std::vector<base::TimeDelta> reported_throttling_times;
+
+ CPUTimeBudgetPool* pool =
+ task_queue_throttler_->CreateCPUTimeBudgetPool("test");
+
+ pool->SetTimeBudgetRecoveryRate(base::TimeTicks(), 0.1);
+ pool->AddQueue(base::TimeTicks(), timer_queue_.get());
+
+ pool->SetReportingCallback(
+ base::BindRepeating(&RecordThrottling, &reported_throttling_times));
+
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, clock_.get()),
+ base::TimeDelta::FromMilliseconds(200));
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&ExpensiveTestTask, &run_times, clock_.get()),
+ base::TimeDelta::FromMilliseconds(200));
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&ExpensiveTestTask, &run_times, clock_.get()),
+ base::TimeDelta::FromMilliseconds(200));
+
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(run_times,
+ ElementsAre(base::TimeTicks() + base::TimeDelta::FromSeconds(1),
+ base::TimeTicks() + base::TimeDelta::FromSeconds(1),
+ base::TimeTicks() + base::TimeDelta::FromSeconds(3)));
+
+ EXPECT_THAT(reported_throttling_times,
+ ElementsAre(base::TimeDelta::FromMilliseconds(1255),
+ base::TimeDelta::FromMilliseconds(1755)));
+
+ pool->RemoveQueue(clock_->GetNowTicksWithoutAdvancing(), timer_queue_.get());
+ task_queue_throttler_->DecreaseThrottleRefCount(timer_queue_.get());
+ pool->Close();
+}
+
+TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest, GrantAdditionalBudget) {
+ std::vector<base::TimeTicks> run_times;
+
+ CPUTimeBudgetPool* pool =
+ task_queue_throttler_->CreateCPUTimeBudgetPool("test");
+
+ pool->SetTimeBudgetRecoveryRate(base::TimeTicks(), 0.1);
+ pool->AddQueue(base::TimeTicks(), timer_queue_.get());
+ pool->GrantAdditionalBudget(base::TimeTicks(),
+ base::TimeDelta::FromMilliseconds(500));
+
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+
+ // Submit five tasks. First three will not be throttled because they have
+ // budget to run.
+ for (int i = 0; i < 5; ++i) {
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&ExpensiveTestTask, &run_times, clock_.get()),
+ base::TimeDelta::FromMilliseconds(200));
+ }
+
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(
+ run_times,
+ ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(1000),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(1250),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(1500),
+ base::TimeTicks() + base::TimeDelta::FromSeconds(3),
+ base::TimeTicks() + base::TimeDelta::FromSeconds(6)));
+
+ pool->RemoveQueue(clock_->GetNowTicksWithoutAdvancing(), timer_queue_.get());
+ task_queue_throttler_->DecreaseThrottleRefCount(timer_queue_.get());
+ pool->Close();
+}
+
+TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
+ EnableAndDisableThrottlingAndTimeBudgets) {
+ // This test checks that if time budget pool is enabled when throttling
+ // is disabled, it does not throttle the queue.
+ std::vector<base::TimeTicks> run_times;
+
+ task_queue_throttler_->DisableThrottling();
+
+ CPUTimeBudgetPool* pool =
+ task_queue_throttler_->CreateCPUTimeBudgetPool("test");
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+
+ LazyNow lazy_now(clock_.get());
+ pool->DisableThrottling(&lazy_now);
+
+ pool->AddQueue(base::TimeTicks(), timer_queue_.get());
+
+ mock_task_runner_->RunUntilTime(base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(100));
+
+ lazy_now = LazyNow(clock_.get());
+ pool->EnableThrottling(&lazy_now);
+
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, clock_.get()),
+ base::TimeDelta::FromMilliseconds(200));
+
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(run_times, ElementsAre(base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(300)));
+}
+
+TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
+ AddQueueToBudgetPoolWhenThrottlingDisabled) {
+ // This test checks that a task queue is added to time budget pool
+ // when throttling is disabled, is does not throttle queue.
+ std::vector<base::TimeTicks> run_times;
+
+ task_queue_throttler_->DisableThrottling();
+
+ CPUTimeBudgetPool* pool =
+ task_queue_throttler_->CreateCPUTimeBudgetPool("test");
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+
+ mock_task_runner_->RunUntilTime(base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(100));
+
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, clock_.get()),
+ base::TimeDelta::FromMilliseconds(200));
+
+ pool->AddQueue(base::TimeTicks(), timer_queue_.get());
+
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(run_times, ElementsAre(base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(300)));
+}
+
+TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
+ DisabledQueueThenEnabledQueue) {
+ std::vector<base::TimeTicks> run_times;
+
+ scoped_refptr<MainThreadTaskQueue> second_queue =
+ scheduler_->NewTimerTaskQueue(
+ MainThreadTaskQueue::QueueType::kFrameThrottleable);
+
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+ task_queue_throttler_->IncreaseThrottleRefCount(second_queue.get());
+
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, clock_.get()),
+ base::TimeDelta::FromMilliseconds(100));
+ second_queue->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, clock_.get()),
+ base::TimeDelta::FromMilliseconds(200));
+
+ std::unique_ptr<TaskQueue::QueueEnabledVoter> voter =
+ timer_queue_->CreateQueueEnabledVoter();
+ voter->SetQueueEnabled(false);
+
+ clock_->Advance(base::TimeDelta::FromMilliseconds(250));
+
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(run_times, ElementsAre(base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(1000)));
+
+ voter->SetQueueEnabled(true);
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(
+ run_times,
+ ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(1000),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(2000)));
+}
+
+TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest, TwoBudgetPools) {
+ std::vector<base::TimeTicks> run_times;
+
+ scoped_refptr<TaskQueue> second_queue = scheduler_->NewTimerTaskQueue(
+ MainThreadTaskQueue::QueueType::kFrameThrottleable);
+
+ CPUTimeBudgetPool* pool1 =
+ task_queue_throttler_->CreateCPUTimeBudgetPool("test");
+ pool1->SetTimeBudgetRecoveryRate(base::TimeTicks(), 0.1);
+ pool1->AddQueue(base::TimeTicks(), timer_queue_.get());
+ pool1->AddQueue(base::TimeTicks(), second_queue.get());
+
+ CPUTimeBudgetPool* pool2 =
+ task_queue_throttler_->CreateCPUTimeBudgetPool("test");
+ pool2->SetTimeBudgetRecoveryRate(base::TimeTicks(), 0.01);
+ pool2->AddQueue(base::TimeTicks(), timer_queue_.get());
+
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+ task_queue_throttler_->IncreaseThrottleRefCount(second_queue.get());
+
+ timer_queue_->PostTask(
+ FROM_HERE, base::BindOnce(&ExpensiveTestTask, &run_times, clock_.get()));
+ second_queue->PostTask(
+ FROM_HERE, base::BindOnce(&ExpensiveTestTask, &run_times, clock_.get()));
+ timer_queue_->PostTask(
+ FROM_HERE, base::BindOnce(&ExpensiveTestTask, &run_times, clock_.get()));
+ second_queue->PostTask(
+ FROM_HERE, base::BindOnce(&ExpensiveTestTask, &run_times, clock_.get()));
+
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(
+ run_times,
+ ElementsAre(
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(1000),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(3000),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(6000),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(26000)));
+}
+
+namespace {
+
+void RunChainedTask(std::deque<base::TimeDelta> task_durations,
+ scoped_refptr<TaskQueue> queue,
+ AutoAdvancingTestClock* clock,
+ std::vector<base::TimeTicks>* run_times,
+ base::TimeDelta delay) {
+ if (task_durations.empty())
+ return;
+
+ run_times->push_back(
+ RoundTimeToMilliseconds(clock->GetNowTicksWithoutAdvancing()));
+ clock->Advance(task_durations.front());
+ task_durations.pop_front();
+
+ queue->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&RunChainedTask, std::move(task_durations), queue, clock,
+ run_times, delay),
+ delay);
+}
+} // namespace
+
+TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
+ WakeUpBasedThrottling_ChainedTasks_Instantaneous) {
+ scheduler_->GetWakeUpBudgetPoolForTesting()->SetWakeUpDuration(
+ base::TimeDelta::FromMilliseconds(10));
+ std::vector<base::TimeTicks> run_times;
+
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+
+ timer_queue_->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&RunChainedTask,
+ std::deque<base::TimeDelta>(10, base::TimeDelta()),
+ timer_queue_, clock_.get(), &run_times, base::TimeDelta()),
+ base::TimeDelta::FromMilliseconds(100));
+
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(run_times,
+ ElementsAre(base::TimeTicks() + base::TimeDelta::FromSeconds(1),
+ base::TimeTicks() + base::TimeDelta::FromSeconds(1),
+ base::TimeTicks() + base::TimeDelta::FromSeconds(1),
+ base::TimeTicks() + base::TimeDelta::FromSeconds(1),
+ base::TimeTicks() + base::TimeDelta::FromSeconds(1),
+ base::TimeTicks() + base::TimeDelta::FromSeconds(1),
+ base::TimeTicks() + base::TimeDelta::FromSeconds(1),
+ base::TimeTicks() + base::TimeDelta::FromSeconds(1),
+ base::TimeTicks() + base::TimeDelta::FromSeconds(1),
+ base::TimeTicks() + base::TimeDelta::FromSeconds(1)));
+}
+
+TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
+ WakeUpBasedThrottling_ImmediateTasks_Fast) {
+ scheduler_->GetWakeUpBudgetPoolForTesting()->SetWakeUpDuration(
+ base::TimeDelta::FromMilliseconds(10));
+ std::vector<base::TimeTicks> run_times;
+
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+
+ timer_queue_->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(
+ &RunChainedTask,
+ std::deque<base::TimeDelta>(10, base::TimeDelta::FromMilliseconds(3)),
+ timer_queue_, clock_.get(), &run_times, base::TimeDelta()),
+ base::TimeDelta::FromMilliseconds(100));
+
+ mock_task_runner_->RunUntilIdle();
+
+ // TODO(altimin): Add fence mechanism to block immediate tasks.
+ EXPECT_THAT(
+ run_times,
+ ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(1000),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(1003),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(1006),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(1009),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(2000),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(2003),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(2006),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(2009),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(3000),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(3003)));
+}
+
+TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
+ WakeUpBasedThrottling_DelayedTasks) {
+ scheduler_->GetWakeUpBudgetPoolForTesting()->SetWakeUpDuration(
+ base::TimeDelta::FromMilliseconds(10));
+ std::vector<base::TimeTicks> run_times;
+
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+
+ timer_queue_->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&RunChainedTask,
+ std::deque<base::TimeDelta>(10, base::TimeDelta()),
+ timer_queue_, clock_.get(), &run_times,
+ base::TimeDelta::FromMilliseconds(3)),
+ base::TimeDelta::FromMilliseconds(100));
+
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(
+ run_times,
+ ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(1000),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(1003),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(1006),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(1009),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(2000),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(2003),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(2006),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(2009),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(3000),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(3003)));
+}
+
+TEST_F(TaskQueueThrottlerTest, WakeUpBasedThrottlingWithCPUBudgetThrottling) {
+ scheduler_->GetWakeUpBudgetPoolForTesting()->SetWakeUpDuration(
+ base::TimeDelta::FromMilliseconds(10));
+
+ CPUTimeBudgetPool* pool =
+ task_queue_throttler_->CreateCPUTimeBudgetPool("test");
+
+ pool->SetTimeBudgetRecoveryRate(base::TimeTicks(), 0.1);
+ pool->AddQueue(base::TimeTicks(), timer_queue_.get());
+
+ std::vector<base::TimeTicks> run_times;
+
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+
+ timer_queue_->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(
+ &RunChainedTask,
+ std::deque<base::TimeDelta>{base::TimeDelta::FromMilliseconds(250),
+ base::TimeDelta(), base::TimeDelta(),
+ base::TimeDelta::FromMilliseconds(250),
+ base::TimeDelta(), base::TimeDelta(),
+ base::TimeDelta::FromMilliseconds(250),
+ base::TimeDelta(), base::TimeDelta()},
+ timer_queue_, clock_.get(), &run_times, base::TimeDelta()),
+ base::TimeDelta::FromMilliseconds(100));
+
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(
+ run_times,
+ ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(1000),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(3000),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(3000),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(3000),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(6000),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(6000),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(6000),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(8000),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(8000)));
+}
+
+TEST_F(TaskQueueThrottlerTest,
+ WakeUpBasedThrottlingWithCPUBudgetThrottling_OnAndOff) {
+ scheduler_->GetWakeUpBudgetPoolForTesting()->SetWakeUpDuration(
+ base::TimeDelta::FromMilliseconds(10));
+
+ CPUTimeBudgetPool* pool =
+ task_queue_throttler_->CreateCPUTimeBudgetPool("test");
+
+ pool->SetTimeBudgetRecoveryRate(base::TimeTicks(), 0.1);
+ pool->AddQueue(base::TimeTicks(), timer_queue_.get());
+
+ std::vector<base::TimeTicks> run_times;
+
+ bool is_throttled = false;
+
+ for (int i = 0; i < 5; ++i) {
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&ExpensiveTestTask, &run_times, clock_.get()),
+ base::TimeDelta::FromMilliseconds(200));
+ timer_queue_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, clock_.get()),
+ base::TimeDelta::FromMilliseconds(300));
+
+ if (is_throttled) {
+ task_queue_throttler_->DecreaseThrottleRefCount(timer_queue_.get());
+ is_throttled = false;
+ } else {
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+ is_throttled = true;
+ }
+
+ mock_task_runner_->RunUntilIdle();
+ }
+
+ EXPECT_THAT(run_times,
+ ElementsAre(
+ // Throttled due to cpu budget.
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(1000),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(3000),
+ // Unthrottled.
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(3200),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(3450),
+ // Throttled due to wake-up budget. Old tasks still run.
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(5000),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(5250),
+ // Unthrottled.
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(6200),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(6450),
+ // Throttled due to wake-up budget. Old tasks still run.
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(8000),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(8250)));
+}
+
+TEST_F(TaskQueueThrottlerTest,
+ WakeUpBasedThrottlingWithCPUBudgetThrottling_ChainedFastTasks) {
+ // This test checks that a new task should run during the wake-up window
+ // when time budget allows that and should be blocked when time budget is
+ // exhausted.
+ scheduler_->GetWakeUpBudgetPoolForTesting()->SetWakeUpDuration(
+ base::TimeDelta::FromMilliseconds(10));
+
+ CPUTimeBudgetPool* pool =
+ task_queue_throttler_->CreateCPUTimeBudgetPool("test");
+
+ pool->SetTimeBudgetRecoveryRate(base::TimeTicks(), 0.01);
+ pool->AddQueue(base::TimeTicks(), timer_queue_.get());
+
+ std::vector<base::TimeTicks> run_times;
+
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+
+ timer_queue_->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(
+ &RunChainedTask,
+ std::deque<base::TimeDelta>(10, base::TimeDelta::FromMilliseconds(7)),
+ timer_queue_, clock_.get(), &run_times, base::TimeDelta()),
+ base::TimeDelta::FromMilliseconds(100));
+
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(run_times,
+ ElementsAre(
+ // Time budget is ~10ms and we can run two 7ms tasks.
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(1000),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(1007),
+ // Time budget is ~6ms and we can run one 7ms task.
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(2000),
+ // Time budget is ~8ms and we can run two 7ms tasks.
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(3000),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(3007),
+ // Time budget is ~5ms and we can run one 7ms task.
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(4000),
+ // Time budget is ~8ms and we can run two 7ms tasks.
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(5000),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(5007),
+ // Time budget is ~4ms and we can run one 7ms task.
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(6000),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(7000)));
+}
+
+} // namespace task_queue_throttler_unittest
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/throttled_time_domain.cc b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/throttled_time_domain.cc
new file mode 100644
index 00000000000..70aa1b8ba8f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/throttled_time_domain.cc
@@ -0,0 +1,51 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/common/throttling/throttled_time_domain.h"
+
+namespace blink {
+namespace scheduler {
+
+ThrottledTimeDomain::ThrottledTimeDomain() : RealTimeDomain() {}
+
+ThrottledTimeDomain::~ThrottledTimeDomain() = default;
+
+const char* ThrottledTimeDomain::GetName() const {
+ return "ThrottledTimeDomain";
+}
+
+void ThrottledTimeDomain::RequestWakeUpAt(base::TimeTicks now,
+ base::TimeTicks run_time) {
+ // We assume the owner (i.e. TaskQueueThrottler) will manage wake-ups on our
+ // behalf.
+}
+
+void ThrottledTimeDomain::CancelWakeUpAt(base::TimeTicks run_time) {
+ // We ignore this because RequestWakeUpAt is a NOP.
+}
+
+void ThrottledTimeDomain::SetNextTaskRunTime(base::TimeTicks run_time) {
+ next_task_run_time_ = run_time;
+}
+
+base::Optional<base::TimeDelta> ThrottledTimeDomain::DelayTillNextTask(
+ LazyNow* lazy_now) {
+ if (next_task_run_time_ && next_task_run_time_ > lazy_now->Now())
+ return next_task_run_time_.value() - lazy_now->Now();
+
+ base::TimeTicks next_run_time;
+ if (!NextScheduledRunTime(&next_run_time))
+ return base::nullopt;
+
+ base::TimeTicks now = lazy_now->Now();
+ if (now >= next_run_time)
+ return base::TimeDelta(); // Makes DoWork post an immediate continuation.
+
+ // We assume the owner (i.e. TaskQueueThrottler) will manage wake-ups on our
+ // behalf.
+ return base::nullopt;
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/throttled_time_domain.h b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/throttled_time_domain.h
new file mode 100644
index 00000000000..4cc739ef187
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/throttled_time_domain.h
@@ -0,0 +1,42 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_COMMON_THROTTLING_THROTTLED_TIME_DOMAIN_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_COMMON_THROTTLING_THROTTLED_TIME_DOMAIN_H_
+
+#include "base/macros.h"
+#include "third_party/blink/renderer/platform/scheduler/base/real_time_domain.h"
+
+namespace blink {
+namespace scheduler {
+
+// A time domain for throttled tasks. behaves like an RealTimeDomain except it
+// relies on the owner (TaskQueueThrottler) to schedule wake-ups.
+class PLATFORM_EXPORT ThrottledTimeDomain : public RealTimeDomain {
+ public:
+ ThrottledTimeDomain();
+ ~ThrottledTimeDomain() override;
+
+ void SetNextTaskRunTime(base::TimeTicks run_time);
+
+ // TimeDomain implementation:
+ const char* GetName() const override;
+ void RequestWakeUpAt(base::TimeTicks now, base::TimeTicks run_time) override;
+ void CancelWakeUpAt(base::TimeTicks run_time) override;
+ base::Optional<base::TimeDelta> DelayTillNextTask(LazyNow* lazy_now) override;
+
+ using TimeDomain::WakeUpReadyDelayedQueues;
+
+ private:
+ // Next task run time provided by task queue throttler. Note that it does not
+ // get reset, so it is valid only when in the future.
+ base::Optional<base::TimeTicks> next_task_run_time_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThrottledTimeDomain);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_COMMON_THROTTLING_THROTTLED_TIME_DOMAIN_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/wake_up_budget_pool.cc b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/wake_up_budget_pool.cc
new file mode 100644
index 00000000000..aa6f5a21548
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/wake_up_budget_pool.cc
@@ -0,0 +1,125 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/common/throttling/wake_up_budget_pool.h"
+
+#include <cstdint>
+
+#include "third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h"
+#include "third_party/blink/renderer/platform/scheduler/util/tracing_helper.h"
+
+namespace blink {
+namespace scheduler {
+
+WakeUpBudgetPool::WakeUpBudgetPool(const char* name,
+ BudgetPoolController* budget_pool_controller,
+ base::TimeTicks now)
+ : BudgetPool(name, budget_pool_controller),
+ wake_up_interval_(base::TimeDelta::FromSecondsD(1.0)) {}
+
+WakeUpBudgetPool::~WakeUpBudgetPool() = default;
+
+QueueBlockType WakeUpBudgetPool::GetBlockType() const {
+ return QueueBlockType::kNewTasksOnly;
+}
+
+void WakeUpBudgetPool::SetWakeUpRate(double wake_ups_per_second) {
+ wake_up_interval_ = base::TimeDelta::FromSecondsD(1 / wake_ups_per_second);
+}
+
+void WakeUpBudgetPool::SetWakeUpDuration(base::TimeDelta duration) {
+ wake_up_duration_ = duration;
+}
+
+void WakeUpBudgetPool::RecordTaskRunTime(TaskQueue* queue,
+ base::TimeTicks start_time,
+ base::TimeTicks end_time) {
+ budget_pool_controller_->UpdateQueueThrottlingState(end_time, queue);
+}
+
+bool WakeUpBudgetPool::CanRunTasksAt(base::TimeTicks moment,
+ bool is_wake_up) const {
+ if (!last_wake_up_)
+ return false;
+ // |is_wake_up| flag means that we're in the beginning of the wake-up and
+ // |OnWakeUp| has just been called. This is needed to support backwards
+ // compability with old throttling mechanism (when |wake_up_duration| is zero)
+ // and allow only one task to run.
+ if (last_wake_up_ == moment && is_wake_up)
+ return true;
+ return moment < last_wake_up_.value() + wake_up_duration_;
+}
+
+base::Optional<base::TimeTicks> WakeUpBudgetPool::GetTimeTasksCanRunUntil(
+ base::TimeTicks now,
+ bool is_wake_up) const {
+ if (!last_wake_up_)
+ return base::TimeTicks();
+ if (!CanRunTasksAt(now, is_wake_up))
+ return base::TimeTicks();
+ return last_wake_up_.value() + wake_up_duration_;
+}
+
+namespace {
+
+// Wrapper around base::TimeTicks::SnappedToNextTick which ensures that
+// the returned point is strictly in the future.
+base::TimeTicks SnapToNextTickStrict(base::TimeTicks moment,
+ base::TimeDelta interval) {
+ base::TimeTicks snapped =
+ moment.SnappedToNextTick(base::TimeTicks(), interval);
+ if (snapped == moment)
+ return moment + interval;
+ return snapped;
+}
+
+} // namespace
+
+base::TimeTicks WakeUpBudgetPool::GetNextAllowedRunTime(
+ base::TimeTicks desired_run_time) const {
+ if (!last_wake_up_)
+ return SnapToNextTickStrict(desired_run_time, wake_up_interval_);
+ if (desired_run_time < last_wake_up_.value() + wake_up_duration_)
+ return desired_run_time;
+ return SnapToNextTickStrict(std::max(desired_run_time, last_wake_up_.value()),
+ wake_up_interval_);
+}
+
+void WakeUpBudgetPool::OnQueueNextWakeUpChanged(
+ TaskQueue* queue,
+ base::TimeTicks now,
+ base::TimeTicks desired_run_time) {
+ budget_pool_controller_->UpdateQueueThrottlingState(now, queue);
+}
+
+void WakeUpBudgetPool::OnWakeUp(base::TimeTicks now) {
+ last_wake_up_ = now;
+}
+
+void WakeUpBudgetPool::AsValueInto(base::trace_event::TracedValue* state,
+ base::TimeTicks now) const {
+ state->BeginDictionary(name_);
+
+ state->SetString("name", name_);
+ state->SetDouble("wake_up_interval_in_seconds",
+ wake_up_interval_.InSecondsF());
+ state->SetDouble("wake_up_duration_in_seconds",
+ wake_up_duration_.InSecondsF());
+ if (last_wake_up_) {
+ state->SetDouble("last_wake_up_seconds_ago",
+ (now - last_wake_up_.value()).InSecondsF());
+ }
+ state->SetBoolean("is_enabled", is_enabled_);
+
+ state->BeginArray("task_queues");
+ for (TaskQueue* queue : associated_task_queues_) {
+ state->AppendString(PointerToString(queue));
+ }
+ state->EndArray();
+
+ state->EndDictionary();
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/wake_up_budget_pool.h b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/wake_up_budget_pool.h
new file mode 100644
index 00000000000..a4879975781
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/wake_up_budget_pool.h
@@ -0,0 +1,67 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_COMMON_THROTTLING_WAKE_UP_BUDGET_POOL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_COMMON_THROTTLING_WAKE_UP_BUDGET_POOL_H_
+
+#include "third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h"
+
+#include "base/macros.h"
+#include "base/optional.h"
+#include "base/time/time.h"
+#include "third_party/blink/renderer/platform/scheduler/base/lazy_now.h"
+
+namespace blink {
+namespace scheduler {
+
+// WakeUpBudgetPool represents a collection of task queues which share a limit
+// on total cpu time.
+class PLATFORM_EXPORT WakeUpBudgetPool : public BudgetPool {
+ public:
+ WakeUpBudgetPool(const char* name,
+ BudgetPoolController* budget_pool_controller,
+ base::TimeTicks now);
+ ~WakeUpBudgetPool() override;
+
+ // Note: this does not have an immediate effect and should be called only
+ // during initialization of a WakeUpBudgetPool.
+ void SetWakeUpRate(double wake_ups_per_second);
+
+ // Note: this does not have an immediate effect and should be called only
+ // during initialization of a WakeUpBudgetPool.
+ void SetWakeUpDuration(base::TimeDelta duration);
+
+ // BudgetPool implementation:
+ void RecordTaskRunTime(TaskQueue* queue,
+ base::TimeTicks start_time,
+ base::TimeTicks end_time) final;
+ bool CanRunTasksAt(base::TimeTicks moment, bool is_wake_up) const final;
+ base::Optional<base::TimeTicks> GetTimeTasksCanRunUntil(
+ base::TimeTicks now,
+ bool is_wake_up) const final;
+ base::TimeTicks GetNextAllowedRunTime(
+ base::TimeTicks desired_run_time) const final;
+ void OnQueueNextWakeUpChanged(TaskQueue* queue,
+ base::TimeTicks now,
+ base::TimeTicks desired_run_time) final;
+ void OnWakeUp(base::TimeTicks now) final;
+ void AsValueInto(base::trace_event::TracedValue* state,
+ base::TimeTicks now) const final;
+
+ protected:
+ QueueBlockType GetBlockType() const final;
+
+ private:
+ base::TimeDelta wake_up_interval_;
+ base::TimeDelta wake_up_duration_;
+
+ base::Optional<base::TimeTicks> last_wake_up_;
+
+ DISALLOW_COPY_AND_ASSIGN(WakeUpBudgetPool);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_COMMON_THROTTLING_WAKE_UP_BUDGET_POOL_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/DEPS b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/DEPS
new file mode 100644
index 00000000000..0308a8627ef
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/DEPS
@@ -0,0 +1,10 @@
+include_rules = [
+ "+base/metrics/single_sample_metrics.h",
+ "+components/viz/common",
+]
+
+specific_include_rules = {
+ ".*test\.cc": [
+ "+components/viz/test",
+ ],
+}
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_origin_type.cc b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_origin_type.cc
new file mode 100644
index 00000000000..fa4f2fc7761
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_origin_type.cc
@@ -0,0 +1,42 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/main_thread/frame_origin_type.h"
+
+#include "base/macros.h"
+#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
+
+namespace blink {
+namespace scheduler {
+
+FrameOriginType GetFrameOriginType(FrameScheduler* scheduler) {
+ DCHECK(scheduler);
+
+ if (scheduler->GetFrameType() == FrameScheduler::FrameType::kMainFrame)
+ return FrameOriginType::kMainFrame;
+
+ if (scheduler->IsCrossOrigin()) {
+ return FrameOriginType::kCrossOriginFrame;
+ } else {
+ return FrameOriginType::kSameOriginFrame;
+ }
+}
+
+const char* FrameOriginTypeToString(FrameOriginType origin) {
+ switch (origin) {
+ case FrameOriginType::kMainFrame:
+ return "main-frame";
+ case FrameOriginType::kSameOriginFrame:
+ return "same-origin";
+ case FrameOriginType::kCrossOriginFrame:
+ return "cross-origin";
+ case FrameOriginType::kCount:
+ NOTREACHED();
+ }
+ NOTREACHED();
+ return nullptr;
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_origin_type.h b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_origin_type.h
new file mode 100644
index 00000000000..d16357c794f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_origin_type.h
@@ -0,0 +1,30 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_FRAME_ORIGIN_TYPE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_FRAME_ORIGIN_TYPE_H_
+
+namespace blink {
+class FrameScheduler;
+
+namespace scheduler {
+
+// This enum is used for a histogram (RendererSchedulerFrameOriginType)
+// and should not be renumbered.
+enum class FrameOriginType {
+ kMainFrame = 0,
+ kSameOriginFrame = 1,
+ kCrossOriginFrame = 2,
+
+ kCount = 3
+};
+
+FrameOriginType GetFrameOriginType(FrameScheduler* frame_scheduler);
+
+const char* FrameOriginTypeToString(FrameOriginType origin);
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_FRAME_ORIGIN_TYPE_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
new file mode 100644
index 00000000000..11f92d93458
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
@@ -0,0 +1,647 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h"
+
+#include <memory>
+#include "base/metrics/histogram_macros.h"
+#include "base/trace_event/blame_context.h"
+#include "third_party/blink/public/platform/blame_context.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/scheduler/base/real_time_domain.h"
+#include "third_party/blink/renderer/platform/scheduler/base/virtual_time_domain.h"
+#include "third_party/blink/renderer/platform/scheduler/child/default_params.h"
+#include "third_party/blink/renderer/platform/scheduler/child/page_visibility_state.h"
+#include "third_party/blink/renderer/platform/scheduler/child/task_runner_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/child/worker_scheduler_proxy.h"
+#include "third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain.h"
+#include "third_party/blink/renderer/platform/scheduler/util/tracing_helper.h"
+
+namespace blink {
+namespace scheduler {
+
+namespace {
+
+const char* VisibilityStateToString(bool is_visible) {
+ if (is_visible) {
+ return "visible";
+ } else {
+ return "hidden";
+ }
+}
+
+const char* PausedStateToString(bool is_paused) {
+ if (is_paused) {
+ return "paused";
+ } else {
+ return "running";
+ }
+}
+
+const char* FrozenStateToString(bool is_frozen) {
+ if (is_frozen) {
+ return "frozen";
+ } else {
+ return "running";
+ }
+}
+
+const char* KeepActiveStateToString(bool keep_active) {
+ if (keep_active) {
+ return "keep_active";
+ } else {
+ return "no_keep_active";
+ }
+}
+
+} // namespace
+
+FrameSchedulerImpl::ActiveConnectionHandleImpl::ActiveConnectionHandleImpl(
+ FrameSchedulerImpl* frame_scheduler)
+ : frame_scheduler_(frame_scheduler->GetWeakPtr()) {
+ frame_scheduler->DidOpenActiveConnection();
+}
+
+FrameSchedulerImpl::ActiveConnectionHandleImpl::~ActiveConnectionHandleImpl() {
+ if (frame_scheduler_)
+ frame_scheduler_->DidCloseActiveConnection();
+}
+
+FrameSchedulerImpl::ThrottlingObserverHandleImpl::ThrottlingObserverHandleImpl(
+ FrameSchedulerImpl* frame_scheduler,
+ Observer* observer)
+ : frame_scheduler_(frame_scheduler->GetWeakPtr()), observer_(observer) {}
+
+FrameSchedulerImpl::ThrottlingObserverHandleImpl::
+ ~ThrottlingObserverHandleImpl() {
+ if (frame_scheduler_)
+ frame_scheduler_->RemoveThrottlingObserver(observer_);
+}
+
+FrameSchedulerImpl::FrameSchedulerImpl(
+ MainThreadSchedulerImpl* main_thread_scheduler,
+ PageSchedulerImpl* parent_page_scheduler,
+ base::trace_event::BlameContext* blame_context,
+ FrameScheduler::FrameType frame_type)
+ : frame_type_(frame_type),
+ main_thread_scheduler_(main_thread_scheduler),
+ parent_page_scheduler_(parent_page_scheduler),
+ blame_context_(blame_context),
+ throttling_state_(FrameScheduler::ThrottlingState::kNotThrottled),
+ frame_visible_(true,
+ "FrameScheduler.FrameVisible",
+ this,
+ &tracing_controller_,
+ VisibilityStateToString),
+ page_visibility_(kDefaultPageVisibility,
+ "FrameScheduler.PageVisibility",
+ this,
+ &tracing_controller_,
+ PageVisibilityStateToString),
+ page_frozen_(false,
+ "FrameScheduler.PageFrozen",
+ this,
+ &tracing_controller_,
+ FrozenStateToString),
+ keep_active_(main_thread_scheduler->SchedulerKeepActive(),
+ "FrameScheduler.KeepActive",
+ this,
+ &tracing_controller_,
+ KeepActiveStateToString),
+ frame_paused_(false,
+ "FrameScheduler.FramePaused",
+ this,
+ &tracing_controller_,
+ PausedStateToString),
+ frame_origin_type_(frame_type == FrameType::kMainFrame
+ ? FrameOriginType::kMainFrame
+ : FrameOriginType::kSameOriginFrame,
+ "FrameScheduler.Origin",
+ this,
+ &tracing_controller_,
+ FrameOriginTypeToString),
+ url_tracer_("FrameScheduler.URL", this),
+ task_queue_throttled_(false,
+ "FrameScheduler.TaskQueueThrottled",
+ this,
+ &tracing_controller_,
+ YesNoStateToString),
+ active_connection_count_(0),
+ has_active_connection_(false,
+ "FrameScheduler.HasActiveConnection",
+ this,
+ &tracing_controller_,
+ YesNoStateToString),
+ weak_factory_(this) {
+ DCHECK_EQ(throttling_state_, CalculateThrottlingState());
+}
+
+namespace {
+
+void CleanUpQueue(MainThreadTaskQueue* queue) {
+ if (!queue)
+ return;
+ queue->DetachFromMainThreadScheduler();
+ queue->SetFrameScheduler(nullptr);
+ queue->SetBlameContext(nullptr);
+ queue->SetQueuePriority(TaskQueue::QueuePriority::kLowPriority);
+}
+
+} // namespace
+
+FrameSchedulerImpl::~FrameSchedulerImpl() {
+ weak_factory_.InvalidateWeakPtrs();
+
+ RemoveThrottleableQueueFromBackgroundCPUTimeBudgetPool();
+
+ CleanUpQueue(loading_task_queue_.get());
+ CleanUpQueue(loading_control_task_queue_.get());
+ CleanUpQueue(throttleable_task_queue_.get());
+ CleanUpQueue(deferrable_task_queue_.get());
+ CleanUpQueue(pausable_task_queue_.get());
+ CleanUpQueue(unpausable_task_queue_.get());
+
+ if (parent_page_scheduler_) {
+ parent_page_scheduler_->Unregister(this);
+
+ if (has_active_connection())
+ parent_page_scheduler_->OnConnectionUpdated();
+ }
+}
+
+void FrameSchedulerImpl::DetachFromPageScheduler() {
+ RemoveThrottleableQueueFromBackgroundCPUTimeBudgetPool();
+
+ parent_page_scheduler_ = nullptr;
+}
+
+void FrameSchedulerImpl::
+ RemoveThrottleableQueueFromBackgroundCPUTimeBudgetPool() {
+ if (!throttleable_task_queue_)
+ return;
+
+ if (!parent_page_scheduler_)
+ return;
+
+ CPUTimeBudgetPool* time_budget_pool =
+ parent_page_scheduler_->BackgroundCPUTimeBudgetPool();
+
+ if (!time_budget_pool)
+ return;
+
+ time_budget_pool->RemoveQueue(
+ main_thread_scheduler_->tick_clock()->NowTicks(),
+ throttleable_task_queue_.get());
+}
+
+std::unique_ptr<FrameScheduler::ThrottlingObserverHandle>
+FrameSchedulerImpl::AddThrottlingObserver(ObserverType type,
+ Observer* observer) {
+ DCHECK(observer);
+ observer->OnThrottlingStateChanged(CalculateThrottlingState());
+ loader_observers_.insert(observer);
+ return std::make_unique<ThrottlingObserverHandleImpl>(this, observer);
+}
+
+void FrameSchedulerImpl::RemoveThrottlingObserver(Observer* observer) {
+ DCHECK(observer);
+ const auto found = loader_observers_.find(observer);
+ DCHECK(loader_observers_.end() != found);
+ loader_observers_.erase(found);
+}
+
+void FrameSchedulerImpl::SetFrameVisible(bool frame_visible) {
+ DCHECK(parent_page_scheduler_);
+ if (frame_visible_ == frame_visible)
+ return;
+ UMA_HISTOGRAM_BOOLEAN("RendererScheduler.IPC.FrameVisibility", frame_visible);
+ frame_visible_ = frame_visible;
+ UpdateTaskQueueThrottling();
+}
+
+bool FrameSchedulerImpl::IsFrameVisible() const {
+ return frame_visible_;
+}
+
+void FrameSchedulerImpl::SetCrossOrigin(bool cross_origin) {
+ DCHECK(parent_page_scheduler_);
+ if (frame_origin_type_ == FrameOriginType::kMainFrame) {
+ DCHECK(!cross_origin);
+ return;
+ }
+ if (cross_origin) {
+ frame_origin_type_ = FrameOriginType::kCrossOriginFrame;
+ } else {
+ frame_origin_type_ = FrameOriginType::kSameOriginFrame;
+ }
+ UpdateTaskQueueThrottling();
+}
+
+bool FrameSchedulerImpl::IsCrossOrigin() const {
+ return frame_origin_type_ == FrameOriginType::kCrossOriginFrame;
+}
+
+void FrameSchedulerImpl::TraceUrlChange(const String& url) {
+ url_tracer_.TraceString(url);
+}
+
+FrameScheduler::FrameType FrameSchedulerImpl::GetFrameType() const {
+ return frame_type_;
+}
+
+scoped_refptr<base::SingleThreadTaskRunner> FrameSchedulerImpl::GetTaskRunner(
+ TaskType type) {
+ // TODO(haraken): Optimize the mapping from TaskTypes to task runners.
+ switch (type) {
+ case TaskType::kJavascriptTimer:
+ return TaskRunnerImpl::Create(ThrottleableTaskQueue(), type);
+ case TaskType::kInternalLoading:
+ case TaskType::kNetworking:
+ return TaskRunnerImpl::Create(LoadingTaskQueue(), type);
+ case TaskType::kNetworkingControl:
+ return TaskRunnerImpl::Create(LoadingControlTaskQueue(), type);
+ // Throttling following tasks may break existing web pages, so tentatively
+ // these are unthrottled.
+ // TODO(nhiroki): Throttle them again after we're convinced that it's safe
+ // or provide a mechanism that web pages can opt-out it if throttling is not
+ // desirable.
+ case TaskType::kDatabaseAccess:
+ case TaskType::kDOMManipulation:
+ case TaskType::kHistoryTraversal:
+ case TaskType::kEmbed:
+ case TaskType::kCanvasBlobSerialization:
+ case TaskType::kRemoteEvent:
+ case TaskType::kWebSocket:
+ case TaskType::kMicrotask:
+ case TaskType::kUnshippedPortMessage:
+ case TaskType::kFileReading:
+ case TaskType::kPresentation:
+ case TaskType::kSensor:
+ case TaskType::kPerformanceTimeline:
+ case TaskType::kWebGL:
+ case TaskType::kIdleTask:
+ case TaskType::kUnspecedTimer:
+ case TaskType::kMiscPlatformAPI:
+ // TODO(altimin): Move appropriate tasks to throttleable task queue.
+ return TaskRunnerImpl::Create(DeferrableTaskQueue(), type);
+ // PostedMessage can be used for navigation, so we shouldn't defer it
+ // when expecting a user gesture.
+ case TaskType::kPostedMessage:
+ // UserInteraction tasks should be run even when expecting a user gesture.
+ case TaskType::kUserInteraction:
+ // Media events should not be deferred to ensure that media playback is
+ // smooth.
+ case TaskType::kMediaElementEvent:
+ case TaskType::kInternalIndexedDB:
+ case TaskType::kInternalMedia:
+ case TaskType::kInternalMediaRealTime:
+ case TaskType::kInternalUserInteraction:
+ case TaskType::kInternalAnimation:
+ return TaskRunnerImpl::Create(PausableTaskQueue(), type);
+ case TaskType::kUnthrottled:
+ case TaskType::kInternalTest:
+ case TaskType::kInternalWebCrypto:
+ case TaskType::kInternalIPC:
+ // The TaskType of Inspector tasks needs to be unpausable because they need
+ // to run even on a paused page.
+ case TaskType::kInternalInspector:
+ return TaskRunnerImpl::Create(UnpausableTaskQueue(), type);
+ case TaskType::kDeprecatedNone:
+ case TaskType::kCount:
+ NOTREACHED();
+ break;
+ }
+ NOTREACHED();
+ return nullptr;
+}
+
+scoped_refptr<TaskQueue> FrameSchedulerImpl::LoadingTaskQueue() {
+ DCHECK(parent_page_scheduler_);
+ if (!loading_task_queue_) {
+ // TODO(panicker): Avoid adding this queue in RS task_runners_.
+ loading_task_queue_ = main_thread_scheduler_->NewLoadingTaskQueue(
+ MainThreadTaskQueue::QueueType::kFrameLoading);
+ loading_task_queue_->SetBlameContext(blame_context_);
+ loading_task_queue_->SetFrameScheduler(this);
+ loading_queue_enabled_voter_ =
+ loading_task_queue_->CreateQueueEnabledVoter();
+ loading_queue_enabled_voter_->SetQueueEnabled(!frame_paused_);
+ }
+ return loading_task_queue_;
+}
+
+scoped_refptr<TaskQueue> FrameSchedulerImpl::LoadingControlTaskQueue() {
+ DCHECK(parent_page_scheduler_);
+ if (!loading_control_task_queue_) {
+ loading_control_task_queue_ = main_thread_scheduler_->NewLoadingTaskQueue(
+ MainThreadTaskQueue::QueueType::kFrameLoadingControl);
+ loading_control_task_queue_->SetBlameContext(blame_context_);
+ loading_control_task_queue_->SetFrameScheduler(this);
+ loading_control_queue_enabled_voter_ =
+ loading_control_task_queue_->CreateQueueEnabledVoter();
+ loading_control_queue_enabled_voter_->SetQueueEnabled(!frame_paused_);
+ }
+ return loading_control_task_queue_;
+}
+
+scoped_refptr<TaskQueue> FrameSchedulerImpl::ThrottleableTaskQueue() {
+ DCHECK(parent_page_scheduler_);
+ if (!throttleable_task_queue_) {
+ // TODO(panicker): Avoid adding this queue in RS task_runners_.
+ throttleable_task_queue_ = main_thread_scheduler_->NewTaskQueue(
+ MainThreadTaskQueue::QueueCreationParams(
+ MainThreadTaskQueue::QueueType::kFrameThrottleable)
+ .SetCanBeThrottled(true)
+ .SetCanBeStopped(true)
+ .SetFreezeWhenKeepActive(true)
+ .SetCanBeDeferred(true)
+ .SetCanBePaused(true));
+ throttleable_task_queue_->SetBlameContext(blame_context_);
+ throttleable_task_queue_->SetFrameScheduler(this);
+ throttleable_queue_enabled_voter_ =
+ throttleable_task_queue_->CreateQueueEnabledVoter();
+ throttleable_queue_enabled_voter_->SetQueueEnabled(!frame_paused_);
+
+ CPUTimeBudgetPool* time_budget_pool =
+ parent_page_scheduler_->BackgroundCPUTimeBudgetPool();
+ if (time_budget_pool) {
+ time_budget_pool->AddQueue(
+ main_thread_scheduler_->tick_clock()->NowTicks(),
+ throttleable_task_queue_.get());
+ }
+ UpdateTaskQueueThrottling();
+ }
+ return throttleable_task_queue_;
+}
+
+scoped_refptr<TaskQueue> FrameSchedulerImpl::DeferrableTaskQueue() {
+ DCHECK(parent_page_scheduler_);
+ if (!deferrable_task_queue_) {
+ deferrable_task_queue_ = main_thread_scheduler_->NewTaskQueue(
+ MainThreadTaskQueue::QueueCreationParams(
+ MainThreadTaskQueue::QueueType::kFrameDeferrable)
+ .SetCanBeDeferred(true)
+ .SetCanBeStopped(
+ RuntimeEnabledFeatures::StopNonTimersInBackgroundEnabled())
+ .SetCanBePaused(true));
+ deferrable_task_queue_->SetBlameContext(blame_context_);
+ deferrable_task_queue_->SetFrameScheduler(this);
+ deferrable_queue_enabled_voter_ =
+ deferrable_task_queue_->CreateQueueEnabledVoter();
+ deferrable_queue_enabled_voter_->SetQueueEnabled(!frame_paused_);
+ }
+ return deferrable_task_queue_;
+}
+
+scoped_refptr<TaskQueue> FrameSchedulerImpl::PausableTaskQueue() {
+ DCHECK(parent_page_scheduler_);
+ if (!pausable_task_queue_) {
+ pausable_task_queue_ = main_thread_scheduler_->NewTaskQueue(
+ MainThreadTaskQueue::QueueCreationParams(
+ MainThreadTaskQueue::QueueType::kFramePausable)
+ .SetCanBeStopped(
+ RuntimeEnabledFeatures::StopNonTimersInBackgroundEnabled())
+ .SetCanBePaused(true));
+ pausable_task_queue_->SetBlameContext(blame_context_);
+ pausable_task_queue_->SetFrameScheduler(this);
+ pausable_queue_enabled_voter_ =
+ pausable_task_queue_->CreateQueueEnabledVoter();
+ pausable_queue_enabled_voter_->SetQueueEnabled(!frame_paused_);
+ }
+ return pausable_task_queue_;
+}
+
+scoped_refptr<TaskQueue> FrameSchedulerImpl::UnpausableTaskQueue() {
+ DCHECK(parent_page_scheduler_);
+ if (!unpausable_task_queue_) {
+ unpausable_task_queue_ = main_thread_scheduler_->NewTaskQueue(
+ MainThreadTaskQueue::QueueCreationParams(
+ MainThreadTaskQueue::QueueType::kFrameUnpausable));
+ unpausable_task_queue_->SetBlameContext(blame_context_);
+ unpausable_task_queue_->SetFrameScheduler(this);
+ }
+ return unpausable_task_queue_;
+}
+
+scoped_refptr<TaskQueue> FrameSchedulerImpl::ControlTaskQueue() {
+ DCHECK(parent_page_scheduler_);
+ return main_thread_scheduler_->ControlTaskQueue();
+}
+
+blink::PageScheduler* FrameSchedulerImpl::GetPageScheduler() const {
+ return parent_page_scheduler_;
+}
+
+void FrameSchedulerImpl::DidStartProvisionalLoad(bool is_main_frame) {
+ main_thread_scheduler_->DidStartProvisionalLoad(is_main_frame);
+}
+
+void FrameSchedulerImpl::DidCommitProvisionalLoad(
+ bool is_web_history_inert_commit,
+ bool is_reload,
+ bool is_main_frame) {
+ main_thread_scheduler_->DidCommitProvisionalLoad(is_web_history_inert_commit,
+ is_reload, is_main_frame);
+}
+
+WebScopedVirtualTimePauser FrameSchedulerImpl::CreateWebScopedVirtualTimePauser(
+ const WTF::String& name,
+ WebScopedVirtualTimePauser::VirtualTaskDuration duration) {
+ return WebScopedVirtualTimePauser(main_thread_scheduler_, duration, name);
+}
+
+void FrameSchedulerImpl::DidOpenActiveConnection() {
+ ++active_connection_count_;
+ has_active_connection_ = static_cast<bool>(active_connection_count_);
+ if (parent_page_scheduler_)
+ parent_page_scheduler_->OnConnectionUpdated();
+}
+
+void FrameSchedulerImpl::DidCloseActiveConnection() {
+ DCHECK_GT(active_connection_count_, 0);
+ --active_connection_count_;
+ has_active_connection_ = static_cast<bool>(active_connection_count_);
+ if (parent_page_scheduler_)
+ parent_page_scheduler_->OnConnectionUpdated();
+}
+
+void FrameSchedulerImpl::AsValueInto(
+ base::trace_event::TracedValue* state) const {
+ state->SetBoolean("frame_visible", frame_visible_);
+ state->SetBoolean("page_visible",
+ page_visibility_ == PageVisibilityState::kVisible);
+ state->SetBoolean("cross_origin", IsCrossOrigin());
+ state->SetString("frame_type",
+ frame_type_ == FrameScheduler::FrameType::kMainFrame
+ ? "MainFrame"
+ : "Subframe");
+ if (loading_task_queue_) {
+ state->SetString("loading_task_queue",
+ PointerToString(loading_task_queue_.get()));
+ }
+ if (loading_control_task_queue_) {
+ state->SetString("loading_control_task_queue",
+ PointerToString(loading_control_task_queue_.get()));
+ }
+ if (throttleable_task_queue_) {
+ state->SetString("throttleable_task_queue",
+ PointerToString(throttleable_task_queue_.get()));
+ }
+ if (deferrable_task_queue_) {
+ state->SetString("deferrable_task_queue",
+ PointerToString(deferrable_task_queue_.get()));
+ }
+ if (pausable_task_queue_) {
+ state->SetString("pausable_task_queue",
+ PointerToString(pausable_task_queue_.get()));
+ }
+ if (unpausable_task_queue_) {
+ state->SetString("unpausable_task_queue",
+ PointerToString(unpausable_task_queue_.get()));
+ }
+ if (blame_context_) {
+ state->BeginDictionary("blame_context");
+ state->SetString(
+ "id_ref",
+ PointerToString(reinterpret_cast<void*>(blame_context_->id())));
+ state->SetString("scope", blame_context_->scope());
+ state->EndDictionary();
+ }
+}
+
+void FrameSchedulerImpl::SetPageVisibility(
+ PageVisibilityState page_visibility) {
+ DCHECK(parent_page_scheduler_);
+ if (page_visibility_ == page_visibility)
+ return;
+ page_visibility_ = page_visibility;
+ if (page_visibility_ == PageVisibilityState::kVisible)
+ page_frozen_ = false; // visible page must not be frozen.
+ UpdateTaskQueues();
+ UpdateTaskQueueThrottling();
+}
+
+bool FrameSchedulerImpl::IsPageVisible() const {
+ return page_visibility_ == PageVisibilityState::kVisible;
+}
+
+void FrameSchedulerImpl::SetPaused(bool frame_paused) {
+ DCHECK(parent_page_scheduler_);
+ if (frame_paused_ == frame_paused)
+ return;
+
+ frame_paused_ = frame_paused;
+ UpdateTaskQueues();
+}
+
+void FrameSchedulerImpl::SetPageFrozen(bool frozen) {
+ DCHECK(!frozen || page_visibility_ == PageVisibilityState::kHidden);
+ page_frozen_ = frozen;
+ UpdateTaskQueues();
+}
+
+void FrameSchedulerImpl::SetKeepActive(bool keep_active) {
+ keep_active_ = keep_active;
+ UpdateTaskQueues();
+}
+
+void FrameSchedulerImpl::UpdateTaskQueues() {
+ // Per-frame (stoppable) task queues will be stopped after 5mins in
+ // background. They will be resumed when the page is visible.
+ UpdateTaskQueue(throttleable_task_queue_,
+ throttleable_queue_enabled_voter_.get());
+ UpdateTaskQueue(loading_task_queue_, loading_queue_enabled_voter_.get());
+ UpdateTaskQueue(loading_control_task_queue_,
+ loading_control_queue_enabled_voter_.get());
+ UpdateTaskQueue(deferrable_task_queue_,
+ deferrable_queue_enabled_voter_.get());
+ UpdateTaskQueue(pausable_task_queue_, pausable_queue_enabled_voter_.get());
+ UpdateThrottlingState();
+}
+
+void FrameSchedulerImpl::UpdateTaskQueue(
+ const scoped_refptr<MainThreadTaskQueue>& queue,
+ TaskQueue::QueueEnabledVoter* voter) {
+ if (!queue || !voter)
+ return;
+ bool queue_paused = frame_paused_ && queue->CanBePaused();
+ bool queue_frozen = page_frozen_ && queue->CanBeStopped();
+ // Override freezing if keep-active is true.
+ if (queue_frozen && !queue->FreezeWhenKeepActive())
+ queue_frozen = !keep_active_;
+ voter->SetQueueEnabled(!queue_paused && !queue_frozen);
+}
+
+void FrameSchedulerImpl::UpdateThrottlingState() {
+ FrameScheduler::ThrottlingState throttling_state = CalculateThrottlingState();
+ if (throttling_state == throttling_state_)
+ return;
+ throttling_state_ = throttling_state;
+ for (auto observer : loader_observers_)
+ observer->OnThrottlingStateChanged(throttling_state_);
+}
+
+FrameScheduler::ThrottlingState FrameSchedulerImpl::CalculateThrottlingState()
+ const {
+ if (RuntimeEnabledFeatures::StopLoadingInBackgroundEnabled() &&
+ page_frozen_ && !keep_active_) {
+ DCHECK(page_visibility_ == PageVisibilityState::kHidden);
+ return FrameScheduler::ThrottlingState::kStopped;
+ }
+ if (page_visibility_ == PageVisibilityState::kHidden)
+ return FrameScheduler::ThrottlingState::kThrottled;
+ return FrameScheduler::ThrottlingState::kNotThrottled;
+}
+
+void FrameSchedulerImpl::OnFirstMeaningfulPaint() {
+ main_thread_scheduler_->OnFirstMeaningfulPaint();
+}
+
+std::unique_ptr<FrameScheduler::ActiveConnectionHandle>
+FrameSchedulerImpl::OnActiveConnectionCreated() {
+ return std::make_unique<FrameSchedulerImpl::ActiveConnectionHandleImpl>(this);
+}
+
+bool FrameSchedulerImpl::ShouldThrottleTimers() const {
+ if (page_visibility_ == PageVisibilityState::kHidden)
+ return true;
+ return RuntimeEnabledFeatures::TimerThrottlingForHiddenFramesEnabled() &&
+ !frame_visible_ && IsCrossOrigin();
+}
+
+void FrameSchedulerImpl::UpdateTaskQueueThrottling() {
+ // Before we initialize a trottleable task queue, |task_queue_throttled_|
+ // stays false and this function ensures it indicates whether are we holding
+ // a queue reference for throttler or not.
+ // Don't modify that value neither amend the reference counter anywhere else.
+ if (!throttleable_task_queue_)
+ return;
+ bool should_throttle = ShouldThrottleTimers();
+ if (task_queue_throttled_ == should_throttle)
+ return;
+ task_queue_throttled_ = should_throttle;
+
+ if (should_throttle) {
+ main_thread_scheduler_->task_queue_throttler()->IncreaseThrottleRefCount(
+ throttleable_task_queue_.get());
+ } else {
+ main_thread_scheduler_->task_queue_throttler()->DecreaseThrottleRefCount(
+ throttleable_task_queue_.get());
+ }
+}
+
+base::WeakPtr<FrameSchedulerImpl> FrameSchedulerImpl::GetWeakPtr() {
+ return weak_factory_.GetWeakPtr();
+}
+
+bool FrameSchedulerImpl::IsExemptFromBudgetBasedThrottling() const {
+ return has_active_connection();
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h
new file mode 100644
index 00000000000..91df102ab76
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h
@@ -0,0 +1,192 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_FRAME_SCHEDULER_IMPL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_FRAME_SCHEDULER_IMPL_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/trace_event/trace_event.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue.h"
+#include "third_party/blink/renderer/platform/scheduler/child/page_visibility_state.h"
+#include "third_party/blink/renderer/platform/scheduler/child/worker_scheduler_proxy.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/frame_origin_type.h"
+#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
+#include "third_party/blink/renderer/platform/scheduler/util/tracing_helper.h"
+
+namespace base {
+namespace trace_event {
+class BlameContext;
+class TracedValue;
+} // namespace trace_event
+} // namespace base
+
+namespace blink {
+namespace scheduler {
+
+class MainThreadSchedulerImpl;
+class MainThreadTaskQueue;
+class PageSchedulerImpl;
+class TaskQueue;
+
+namespace main_thread_scheduler_impl_unittest {
+class MainThreadSchedulerImplTest;
+}
+
+namespace frame_scheduler_impl_unittest {
+class FrameSchedulerImplTest;
+}
+
+namespace page_scheduler_impl_unittest {
+class PageSchedulerImplTest;
+}
+
+class PLATFORM_EXPORT FrameSchedulerImpl : public FrameScheduler {
+ public:
+ FrameSchedulerImpl(MainThreadSchedulerImpl* main_thread_scheduler,
+ PageSchedulerImpl* parent_page_scheduler,
+ base::trace_event::BlameContext* blame_context,
+ FrameScheduler::FrameType frame_type);
+
+ ~FrameSchedulerImpl() override;
+
+ // FrameScheduler implementation:
+ std::unique_ptr<ThrottlingObserverHandle> AddThrottlingObserver(
+ ObserverType,
+ Observer*) override;
+ void SetFrameVisible(bool frame_visible) override;
+ bool IsFrameVisible() const override;
+ bool IsPageVisible() const override;
+ void SetPaused(bool frame_paused) override;
+ void SetPageFrozen(bool) override;
+ void SetKeepActive(bool) override;
+
+ void SetCrossOrigin(bool cross_origin) override;
+ bool IsCrossOrigin() const override;
+ void TraceUrlChange(const String& url) override;
+ FrameScheduler::FrameType GetFrameType() const override;
+ scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(TaskType) override;
+ PageScheduler* GetPageScheduler() const override;
+ void DidStartProvisionalLoad(bool is_main_frame) override;
+ void DidCommitProvisionalLoad(bool is_web_history_inert_commit,
+ bool is_reload,
+ bool is_main_frame) override;
+ WebScopedVirtualTimePauser CreateWebScopedVirtualTimePauser(
+ const WTF::String& name,
+ WebScopedVirtualTimePauser::VirtualTaskDuration duration) override;
+ void OnFirstMeaningfulPaint() override;
+ std::unique_ptr<ActiveConnectionHandle> OnActiveConnectionCreated() override;
+ void AsValueInto(base::trace_event::TracedValue* state) const;
+ bool IsExemptFromBudgetBasedThrottling() const override;
+
+ scoped_refptr<TaskQueue> ControlTaskQueue();
+ void SetPageVisibility(PageVisibilityState page_visibility);
+
+ bool has_active_connection() const { return has_active_connection_; }
+
+ void OnTraceLogEnabled() { tracing_controller_.OnTraceLogEnabled(); }
+
+ private:
+ friend class PageSchedulerImpl;
+ friend class main_thread_scheduler_impl_unittest::MainThreadSchedulerImplTest;
+ friend class frame_scheduler_impl_unittest::FrameSchedulerImplTest;
+ friend class page_scheduler_impl_unittest::PageSchedulerImplTest;
+
+ class ActiveConnectionHandleImpl : public ActiveConnectionHandle {
+ public:
+ ActiveConnectionHandleImpl(FrameSchedulerImpl* frame_scheduler);
+ ~ActiveConnectionHandleImpl() override;
+
+ private:
+ base::WeakPtr<FrameSchedulerImpl> frame_scheduler_;
+
+ DISALLOW_COPY_AND_ASSIGN(ActiveConnectionHandleImpl);
+ };
+
+ class ThrottlingObserverHandleImpl : public ThrottlingObserverHandle {
+ public:
+ ThrottlingObserverHandleImpl(FrameSchedulerImpl* frame_scheduler,
+ Observer* observer);
+ ~ThrottlingObserverHandleImpl() override;
+
+ private:
+ base::WeakPtr<FrameSchedulerImpl> frame_scheduler_;
+ Observer* observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThrottlingObserverHandleImpl);
+ };
+
+ void DetachFromPageScheduler();
+ void RemoveThrottleableQueueFromBackgroundCPUTimeBudgetPool();
+ void ApplyPolicyToThrottleableQueue();
+ bool ShouldThrottleTimers() const;
+ void UpdateTaskQueueThrottling();
+ FrameScheduler::ThrottlingState CalculateThrottlingState() const;
+ void UpdateThrottlingState();
+ void RemoveThrottlingObserver(Observer* observer);
+ void UpdateTaskQueues();
+ void UpdateTaskQueue(const scoped_refptr<MainThreadTaskQueue>& queue,
+ TaskQueue::QueueEnabledVoter* voter);
+
+ void DidOpenActiveConnection();
+ void DidCloseActiveConnection();
+
+ scoped_refptr<TaskQueue> LoadingTaskQueue();
+ scoped_refptr<TaskQueue> LoadingControlTaskQueue();
+ scoped_refptr<TaskQueue> ThrottleableTaskQueue();
+ scoped_refptr<TaskQueue> DeferrableTaskQueue();
+ scoped_refptr<TaskQueue> PausableTaskQueue();
+ scoped_refptr<TaskQueue> UnpausableTaskQueue();
+
+ base::WeakPtr<FrameSchedulerImpl> GetWeakPtr();
+
+ const FrameScheduler::FrameType frame_type_;
+
+ TraceableVariableController tracing_controller_;
+ scoped_refptr<MainThreadTaskQueue> loading_task_queue_;
+ scoped_refptr<MainThreadTaskQueue> loading_control_task_queue_;
+ scoped_refptr<MainThreadTaskQueue> throttleable_task_queue_;
+ scoped_refptr<MainThreadTaskQueue> deferrable_task_queue_;
+ scoped_refptr<MainThreadTaskQueue> pausable_task_queue_;
+ scoped_refptr<MainThreadTaskQueue> unpausable_task_queue_;
+ std::unique_ptr<TaskQueue::QueueEnabledVoter> loading_queue_enabled_voter_;
+ std::unique_ptr<TaskQueue::QueueEnabledVoter>
+ loading_control_queue_enabled_voter_;
+ std::unique_ptr<TaskQueue::QueueEnabledVoter>
+ throttleable_queue_enabled_voter_;
+ std::unique_ptr<TaskQueue::QueueEnabledVoter> deferrable_queue_enabled_voter_;
+ std::unique_ptr<TaskQueue::QueueEnabledVoter> pausable_queue_enabled_voter_;
+ MainThreadSchedulerImpl* main_thread_scheduler_; // NOT OWNED
+ PageSchedulerImpl* parent_page_scheduler_; // NOT OWNED
+ base::trace_event::BlameContext* blame_context_; // NOT OWNED
+ std::set<Observer*> loader_observers_; // NOT OWNED
+ FrameScheduler::ThrottlingState throttling_state_;
+ TraceableState<bool, kTracingCategoryNameInfo> frame_visible_;
+ TraceableState<PageVisibilityState, kTracingCategoryNameInfo>
+ page_visibility_;
+ TraceableState<bool, kTracingCategoryNameInfo> page_frozen_;
+ TraceableState<bool, kTracingCategoryNameInfo> keep_active_;
+ TraceableState<bool, kTracingCategoryNameInfo> frame_paused_;
+ TraceableState<FrameOriginType, kTracingCategoryNameInfo> frame_origin_type_;
+ StateTracer<kTracingCategoryNameInfo> url_tracer_;
+ // |task_queue_throttled_| is false if |throttleable_task_queue_| is absent.
+ TraceableState<bool, kTracingCategoryNameInfo> task_queue_throttled_;
+ // TODO(kraynov): https://crbug.com/827113
+ // Trace active connection count.
+ int active_connection_count_;
+ TraceableState<bool, kTracingCategoryNameInfo> has_active_connection_;
+
+ base::WeakPtrFactory<FrameSchedulerImpl> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(FrameSchedulerImpl);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_FRAME_SCHEDULER_IMPL_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
new file mode 100644
index 00000000000..763ac9e2e76
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
@@ -0,0 +1,480 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h"
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "components/viz/test/ordered_simple_task_runner.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/test/task_queue_manager_for_test.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+#include "third_party/blink/renderer/platform/web_task_runner.h"
+
+namespace blink {
+namespace scheduler {
+// To avoid symbol collisions in jumbo builds.
+namespace frame_scheduler_impl_unittest {
+
+class FrameSchedulerImplTest : public testing::Test {
+ public:
+ FrameSchedulerImplTest() = default;
+ ~FrameSchedulerImplTest() override = default;
+
+ void SetUp() override {
+ clock_.Advance(base::TimeDelta::FromMicroseconds(5000));
+ mock_task_runner_ =
+ base::MakeRefCounted<cc::OrderedSimpleTaskRunner>(&clock_, true);
+ scheduler_.reset(new MainThreadSchedulerImpl(
+ TaskQueueManagerForTest::Create(nullptr, mock_task_runner_, &clock_),
+ base::nullopt));
+ page_scheduler_.reset(
+ new PageSchedulerImpl(nullptr, scheduler_.get(), false));
+ frame_scheduler_ = page_scheduler_->CreateFrameSchedulerImpl(
+ nullptr, FrameScheduler::FrameType::kSubframe);
+ }
+
+ void TearDown() override {
+ frame_scheduler_.reset();
+ page_scheduler_.reset();
+ scheduler_->Shutdown();
+ scheduler_.reset();
+ }
+
+ protected:
+ scoped_refptr<TaskQueue> throttleable_task_queue() {
+ return frame_scheduler_->throttleable_task_queue_;
+ }
+
+ void LazyInitThrottleableTaskQueue() {
+ EXPECT_FALSE(throttleable_task_queue());
+ frame_scheduler_->ThrottleableTaskQueue();
+ EXPECT_TRUE(throttleable_task_queue());
+ }
+
+ scoped_refptr<TaskQueue> ThrottleableTaskQueue() {
+ return frame_scheduler_->ThrottleableTaskQueue();
+ }
+
+ scoped_refptr<TaskQueue> LoadingTaskQueue() {
+ return frame_scheduler_->LoadingTaskQueue();
+ }
+
+ scoped_refptr<TaskQueue> DeferrableTaskQueue() {
+ return frame_scheduler_->DeferrableTaskQueue();
+ }
+
+ scoped_refptr<TaskQueue> PausableTaskQueue() {
+ return frame_scheduler_->PausableTaskQueue();
+ }
+
+ scoped_refptr<TaskQueue> UnpausableTaskQueue() {
+ return frame_scheduler_->UnpausableTaskQueue();
+ }
+
+ bool IsThrottled() {
+ EXPECT_TRUE(throttleable_task_queue());
+ return scheduler_->task_queue_throttler()->IsThrottled(
+ throttleable_task_queue().get());
+ }
+
+ base::SimpleTestTickClock clock_;
+ scoped_refptr<cc::OrderedSimpleTaskRunner> mock_task_runner_;
+ std::unique_ptr<MainThreadSchedulerImpl> scheduler_;
+ std::unique_ptr<PageSchedulerImpl> page_scheduler_;
+ std::unique_ptr<FrameSchedulerImpl> frame_scheduler_;
+};
+
+namespace {
+
+class MockThrottlingObserver final : public FrameScheduler::Observer {
+ public:
+ MockThrottlingObserver()
+ : throttled_count_(0u), not_throttled_count_(0u), stopped_count_(0u) {}
+
+ void CheckObserverState(size_t throttled_count_expectation,
+ size_t not_throttled_count_expectation,
+ size_t stopped_count_expectation) {
+ EXPECT_EQ(throttled_count_expectation, throttled_count_);
+ EXPECT_EQ(not_throttled_count_expectation, not_throttled_count_);
+ EXPECT_EQ(stopped_count_expectation, stopped_count_);
+ }
+
+ void OnThrottlingStateChanged(
+ FrameScheduler::ThrottlingState state) override {
+ switch (state) {
+ case FrameScheduler::ThrottlingState::kThrottled:
+ throttled_count_++;
+ break;
+ case FrameScheduler::ThrottlingState::kNotThrottled:
+ not_throttled_count_++;
+ break;
+ case FrameScheduler::ThrottlingState::kStopped:
+ stopped_count_++;
+ break;
+ // We should not have another state, and compiler checks it.
+ }
+ }
+
+ private:
+ size_t throttled_count_;
+ size_t not_throttled_count_;
+ size_t stopped_count_;
+};
+
+void IncrementCounter(int* counter) {
+ ++*counter;
+}
+
+} // namespace
+
+// Throttleable task queue is initialized lazily, so there're two scenarios:
+// - Task queue created first and throttling decision made later;
+// - Scheduler receives relevant signals to make a throttling decision but
+// applies one once task queue gets created.
+// We test both (ExplicitInit/LazyInit) of them.
+
+TEST_F(FrameSchedulerImplTest, PageVisible) {
+ ScopedTimerThrottlingForHiddenFramesForTest throttle_hidden_frames(true);
+ EXPECT_FALSE(throttleable_task_queue());
+ LazyInitThrottleableTaskQueue();
+ EXPECT_FALSE(IsThrottled());
+}
+
+TEST_F(FrameSchedulerImplTest, PageHidden_ExplicitInit) {
+ ScopedTimerThrottlingForHiddenFramesForTest throttle_hidden_frames(true);
+ LazyInitThrottleableTaskQueue();
+ EXPECT_FALSE(IsThrottled());
+ page_scheduler_->SetPageVisible(false);
+ EXPECT_TRUE(IsThrottled());
+}
+
+TEST_F(FrameSchedulerImplTest, PageHidden_LazyInit) {
+ ScopedTimerThrottlingForHiddenFramesForTest throttle_hidden_frames(false);
+ page_scheduler_->SetPageVisible(false);
+ LazyInitThrottleableTaskQueue();
+ EXPECT_TRUE(IsThrottled());
+}
+
+TEST_F(FrameSchedulerImplTest, PageHiddenThenVisible_ExplicitInit) {
+ ScopedTimerThrottlingForHiddenFramesForTest throttle_hidden_frames(false);
+ LazyInitThrottleableTaskQueue();
+ EXPECT_FALSE(IsThrottled());
+ page_scheduler_->SetPageVisible(false);
+ EXPECT_TRUE(IsThrottled());
+ page_scheduler_->SetPageVisible(true);
+ EXPECT_FALSE(IsThrottled());
+ page_scheduler_->SetPageVisible(false);
+ EXPECT_TRUE(IsThrottled());
+}
+
+TEST_F(FrameSchedulerImplTest,
+ FrameHiddenThenVisible_CrossOrigin_ExplicitInit) {
+ ScopedTimerThrottlingForHiddenFramesForTest throttle_hidden_frames(true);
+ LazyInitThrottleableTaskQueue();
+ EXPECT_FALSE(IsThrottled());
+ frame_scheduler_->SetFrameVisible(false);
+ frame_scheduler_->SetCrossOrigin(true);
+ frame_scheduler_->SetCrossOrigin(false);
+ EXPECT_FALSE(IsThrottled());
+ frame_scheduler_->SetCrossOrigin(true);
+ EXPECT_TRUE(IsThrottled());
+ frame_scheduler_->SetFrameVisible(true);
+ EXPECT_FALSE(IsThrottled());
+ frame_scheduler_->SetFrameVisible(false);
+ EXPECT_TRUE(IsThrottled());
+}
+
+TEST_F(FrameSchedulerImplTest, FrameHidden_CrossOrigin_LazyInit) {
+ ScopedTimerThrottlingForHiddenFramesForTest throttle_hidden_frames(true);
+ frame_scheduler_->SetFrameVisible(false);
+ frame_scheduler_->SetCrossOrigin(true);
+ LazyInitThrottleableTaskQueue();
+ EXPECT_TRUE(IsThrottled());
+}
+
+TEST_F(FrameSchedulerImplTest,
+ FrameHidden_CrossOrigin_NoThrottling_ExplicitInit) {
+ ScopedTimerThrottlingForHiddenFramesForTest throttle_hidden_frames(false);
+ LazyInitThrottleableTaskQueue();
+ EXPECT_FALSE(IsThrottled());
+ frame_scheduler_->SetFrameVisible(false);
+ frame_scheduler_->SetCrossOrigin(true);
+ EXPECT_FALSE(IsThrottled());
+}
+
+TEST_F(FrameSchedulerImplTest, FrameHidden_CrossOrigin_NoThrottling_LazyInit) {
+ ScopedTimerThrottlingForHiddenFramesForTest throttle_hidden_frames(false);
+ frame_scheduler_->SetFrameVisible(false);
+ frame_scheduler_->SetCrossOrigin(true);
+ LazyInitThrottleableTaskQueue();
+ EXPECT_FALSE(IsThrottled());
+}
+
+TEST_F(FrameSchedulerImplTest, FrameHidden_SameOrigin_ExplicitInit) {
+ ScopedTimerThrottlingForHiddenFramesForTest throttle_hidden_frames(true);
+ LazyInitThrottleableTaskQueue();
+ EXPECT_FALSE(IsThrottled());
+ frame_scheduler_->SetFrameVisible(false);
+ EXPECT_FALSE(IsThrottled());
+}
+
+TEST_F(FrameSchedulerImplTest, FrameHidden_SameOrigin_LazyInit) {
+ ScopedTimerThrottlingForHiddenFramesForTest throttle_hidden_frames(true);
+ frame_scheduler_->SetFrameVisible(false);
+ LazyInitThrottleableTaskQueue();
+ EXPECT_FALSE(IsThrottled());
+}
+
+TEST_F(FrameSchedulerImplTest, FrameVisible_CrossOrigin_ExplicitInit) {
+ ScopedTimerThrottlingForHiddenFramesForTest throttle_hidden_frames(true);
+ LazyInitThrottleableTaskQueue();
+ EXPECT_FALSE(IsThrottled());
+ EXPECT_TRUE(throttleable_task_queue());
+ frame_scheduler_->SetFrameVisible(true);
+ EXPECT_FALSE(IsThrottled());
+ frame_scheduler_->SetCrossOrigin(true);
+ EXPECT_FALSE(IsThrottled());
+}
+
+TEST_F(FrameSchedulerImplTest, FrameVisible_CrossOrigin_LazyInit) {
+ ScopedTimerThrottlingForHiddenFramesForTest throttle_hidden_frames(true);
+ frame_scheduler_->SetFrameVisible(true);
+ frame_scheduler_->SetCrossOrigin(true);
+ LazyInitThrottleableTaskQueue();
+ EXPECT_FALSE(IsThrottled());
+}
+
+TEST_F(FrameSchedulerImplTest, PauseAndResume) {
+ int counter = 0;
+ LoadingTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+ ThrottleableTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+ DeferrableTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+ PausableTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+ UnpausableTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+
+ frame_scheduler_->SetPaused(true);
+
+ EXPECT_EQ(0, counter);
+ mock_task_runner_->RunUntilIdle();
+ EXPECT_EQ(1, counter);
+
+ frame_scheduler_->SetPaused(false);
+
+ EXPECT_EQ(1, counter);
+ mock_task_runner_->RunUntilIdle();
+ EXPECT_EQ(5, counter);
+}
+
+TEST_F(FrameSchedulerImplTest, PageFreezeAndUnfreezeFlagEnabled) {
+ ScopedStopLoadingInBackgroundForTest stop_loading_enabler(true);
+ ScopedStopNonTimersInBackgroundForTest stop_non_timers_enabler(true);
+ int counter = 0;
+ LoadingTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+ ThrottleableTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+ DeferrableTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+ PausableTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+ UnpausableTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+
+ frame_scheduler_->SetPageVisibility(PageVisibilityState::kHidden);
+ frame_scheduler_->SetPageFrozen(true);
+
+ EXPECT_EQ(0, counter);
+ mock_task_runner_->RunUntilIdle();
+ // unpausable tasks continue to run.
+ EXPECT_EQ(1, counter);
+
+ frame_scheduler_->SetPageFrozen(false);
+
+ EXPECT_EQ(1, counter);
+ mock_task_runner_->RunUntilIdle();
+ EXPECT_EQ(5, counter);
+}
+
+TEST_F(FrameSchedulerImplTest, PageFreezeAndUnfreezeFlagDisabled) {
+ ScopedStopLoadingInBackgroundForTest stop_loading_enabler(false);
+ ScopedStopNonTimersInBackgroundForTest stop_non_timers_enabler(false);
+ int counter = 0;
+ LoadingTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+ ThrottleableTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+ DeferrableTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+ PausableTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+ UnpausableTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+
+ frame_scheduler_->SetPageVisibility(PageVisibilityState::kHidden);
+ frame_scheduler_->SetPageFrozen(true);
+
+ EXPECT_EQ(0, counter);
+ mock_task_runner_->RunUntilIdle();
+ // throttleable tasks are frozen, other tasks continue to run.
+ EXPECT_EQ(4, counter);
+
+ frame_scheduler_->SetPageFrozen(false);
+
+ EXPECT_EQ(4, counter);
+ mock_task_runner_->RunUntilIdle();
+ EXPECT_EQ(5, counter);
+}
+
+TEST_F(FrameSchedulerImplTest, PageFreezeWithKeepActive) {
+ ScopedStopLoadingInBackgroundForTest stop_loading_enabler(true);
+ ScopedStopNonTimersInBackgroundForTest stop_non_timers_enabler(false);
+ int counter = 0;
+ LoadingTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+ ThrottleableTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+ DeferrableTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+ PausableTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+ UnpausableTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+
+ frame_scheduler_->SetKeepActive(true); // say we have a Service Worker
+ frame_scheduler_->SetPageVisibility(PageVisibilityState::kHidden);
+ frame_scheduler_->SetPageFrozen(true);
+
+ EXPECT_EQ(0, counter);
+ mock_task_runner_->RunUntilIdle();
+ // Everything runs except throttleable tasks (timers)
+ EXPECT_EQ(4, counter);
+
+ LoadingTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+
+ EXPECT_EQ(4, counter);
+ mock_task_runner_->RunUntilIdle();
+ EXPECT_EQ(5, counter); // loading task runs
+
+ LoadingTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+ // KeepActive is false when Service Worker stops.
+ frame_scheduler_->SetKeepActive(false);
+ EXPECT_EQ(5, counter);
+ mock_task_runner_->RunUntilIdle();
+ EXPECT_EQ(5, counter); // loading task does not run
+
+ frame_scheduler_->SetKeepActive(true);
+ EXPECT_EQ(5, counter);
+ mock_task_runner_->RunUntilIdle();
+ EXPECT_EQ(6, counter); // loading task runs
+}
+
+TEST_F(FrameSchedulerImplTest, PageFreezeAndPageVisible) {
+ ScopedStopLoadingInBackgroundForTest stop_loading_enabler(true);
+ ScopedStopNonTimersInBackgroundForTest stop_non_timers_enabler(true);
+ int counter = 0;
+ LoadingTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+ ThrottleableTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+ DeferrableTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+ PausableTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+ UnpausableTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+
+ frame_scheduler_->SetPageVisibility(PageVisibilityState::kHidden);
+ frame_scheduler_->SetPageFrozen(true);
+
+ EXPECT_EQ(0, counter);
+ mock_task_runner_->RunUntilIdle();
+ EXPECT_EQ(1, counter);
+
+ // Making the page visible should cause frozen queues to resume.
+ frame_scheduler_->SetPageVisibility(PageVisibilityState::kVisible);
+
+ EXPECT_EQ(1, counter);
+ mock_task_runner_->RunUntilIdle();
+ EXPECT_EQ(5, counter);
+}
+
+// Tests if throttling observer interfaces work.
+TEST_F(FrameSchedulerImplTest, ThrottlingObserver) {
+ std::unique_ptr<MockThrottlingObserver> observer =
+ std::make_unique<MockThrottlingObserver>();
+
+ size_t throttled_count = 0u;
+ size_t not_throttled_count = 0u;
+ size_t stopped_count = 0u;
+
+ observer->CheckObserverState(throttled_count, not_throttled_count,
+ stopped_count);
+
+ auto observer_handle = frame_scheduler_->AddThrottlingObserver(
+ FrameScheduler::ObserverType::kLoader, observer.get());
+
+ // Initial state should be synchronously notified here.
+ // We assume kNotThrottled is notified as an initial state, but it could
+ // depend on implementation details and can be changed.
+ observer->CheckObserverState(throttled_count, ++not_throttled_count,
+ stopped_count);
+
+ // Once the page gets to be invisible, it should notify the observer of
+ // kThrottled synchronously.
+ page_scheduler_->SetPageVisible(false);
+ observer->CheckObserverState(++throttled_count, not_throttled_count,
+ stopped_count);
+
+ // When no state has changed, observers are not called.
+ page_scheduler_->SetPageVisible(false);
+ observer->CheckObserverState(throttled_count, not_throttled_count,
+ stopped_count);
+
+ // Setting background page to STOPPED, notifies observers of kStopped.
+ page_scheduler_->SetPageFrozen(true);
+ observer->CheckObserverState(throttled_count, not_throttled_count,
+ ++stopped_count);
+
+ // When page is not in the STOPPED state, then page visibility is used,
+ // notifying observer of kThrottled.
+ page_scheduler_->SetPageFrozen(false);
+ observer->CheckObserverState(++throttled_count, not_throttled_count,
+ stopped_count);
+
+ // Going back to visible state should notify the observer of kNotThrottled
+ // synchronously.
+ page_scheduler_->SetPageVisible(true);
+ observer->CheckObserverState(throttled_count, ++not_throttled_count,
+ stopped_count);
+
+ // Remove from the observer list, and see if any other callback should not be
+ // invoked when the condition is changed.
+ observer_handle.reset();
+ page_scheduler_->SetPageVisible(false);
+
+ // Wait 100 secs virtually and run pending tasks just in case.
+ clock_.Advance(base::TimeDelta::FromSeconds(100));
+ mock_task_runner_->RunUntilIdle();
+
+ observer->CheckObserverState(throttled_count, not_throttled_count,
+ stopped_count);
+}
+
+} // namespace frame_scheduler_impl_unittest
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_helper.cc b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_helper.cc
new file mode 100644
index 00000000000..93df1b7da10
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_helper.cc
@@ -0,0 +1,64 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_helper.h"
+
+#include "third_party/blink/renderer/platform/scheduler/renderer/main_thread_task_queue.h"
+
+namespace blink {
+namespace scheduler {
+
+MainThreadSchedulerHelper::MainThreadSchedulerHelper(
+ std::unique_ptr<TaskQueueManager> task_queue_manager,
+ MainThreadSchedulerImpl* main_thread_scheduler)
+ : SchedulerHelper(std::move(task_queue_manager)),
+ main_thread_scheduler_(main_thread_scheduler),
+ default_task_queue_(
+ NewTaskQueue(MainThreadTaskQueue::QueueCreationParams(
+ MainThreadTaskQueue::QueueType::kDefault)
+ .SetShouldMonitorQuiescence(true))),
+ control_task_queue_(
+ NewTaskQueue(MainThreadTaskQueue::QueueCreationParams(
+ MainThreadTaskQueue::QueueType::kControl)
+ .SetShouldNotifyObservers(false))) {
+ InitDefaultQueues(default_task_queue_, control_task_queue_);
+ task_queue_manager_->EnableCrashKeys("blink_scheduler_task_file_name",
+ "blink_scheduler_task_function_name");
+}
+
+MainThreadSchedulerHelper::~MainThreadSchedulerHelper() {
+ control_task_queue_->ShutdownTaskQueue();
+ default_task_queue_->ShutdownTaskQueue();
+}
+
+scoped_refptr<MainThreadTaskQueue>
+MainThreadSchedulerHelper::DefaultMainThreadTaskQueue() {
+ return default_task_queue_;
+}
+
+scoped_refptr<TaskQueue> MainThreadSchedulerHelper::DefaultTaskQueue() {
+ return default_task_queue_;
+}
+
+scoped_refptr<MainThreadTaskQueue>
+MainThreadSchedulerHelper::ControlMainThreadTaskQueue() {
+ return control_task_queue_;
+}
+
+scoped_refptr<TaskQueue> MainThreadSchedulerHelper::ControlTaskQueue() {
+ return control_task_queue_;
+}
+
+scoped_refptr<MainThreadTaskQueue> MainThreadSchedulerHelper::NewTaskQueue(
+ const MainThreadTaskQueue::QueueCreationParams& params) {
+ scoped_refptr<MainThreadTaskQueue> task_queue =
+ task_queue_manager_->CreateTaskQueue<MainThreadTaskQueue>(
+ params.spec, params, main_thread_scheduler_);
+ if (params.used_for_important_tasks)
+ task_queue->SetQueuePriority(TaskQueue::QueuePriority::kHighestPriority);
+ return task_queue;
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_helper.h b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_helper.h
new file mode 100644
index 00000000000..f82acb3aa0b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_helper.h
@@ -0,0 +1,46 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_MAIN_THREAD_SCHEDULER_HELPER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_MAIN_THREAD_SCHEDULER_HELPER_H_
+
+#include "third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h"
+
+#include "third_party/blink/renderer/platform/scheduler/renderer/main_thread_task_queue.h"
+
+namespace blink {
+namespace scheduler {
+
+class MainThreadSchedulerImpl;
+
+class PLATFORM_EXPORT MainThreadSchedulerHelper : public SchedulerHelper {
+ public:
+ MainThreadSchedulerHelper(
+ std::unique_ptr<TaskQueueManager> task_queue_manager,
+ MainThreadSchedulerImpl* main_thread_scheduler);
+ ~MainThreadSchedulerHelper() override;
+
+ scoped_refptr<MainThreadTaskQueue> NewTaskQueue(
+ const MainThreadTaskQueue::QueueCreationParams& params);
+
+ scoped_refptr<MainThreadTaskQueue> DefaultMainThreadTaskQueue();
+ scoped_refptr<MainThreadTaskQueue> ControlMainThreadTaskQueue();
+
+ protected:
+ scoped_refptr<TaskQueue> DefaultTaskQueue() override;
+ scoped_refptr<TaskQueue> ControlTaskQueue() override;
+
+ private:
+ MainThreadSchedulerImpl* main_thread_scheduler_; // NOT OWNED
+
+ const scoped_refptr<MainThreadTaskQueue> default_task_queue_;
+ const scoped_refptr<MainThreadTaskQueue> control_task_queue_;
+
+ DISALLOW_COPY_AND_ASSIGN(MainThreadSchedulerHelper);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_MAIN_THREAD_SCHEDULER_HELPER_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
new file mode 100644
index 00000000000..17e1f3cb036
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
@@ -0,0 +1,2792 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/debug/stack_trace.h"
+#include "base/feature_list.h"
+#include "base/logging.h"
+#include "base/metrics/field_trial_params.h"
+#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/trace_event/trace_event.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "build/build_config.h"
+#include "components/viz/common/frame_sinks/begin_frame_args.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
+#include "third_party/blink/public/common/page/launching_process_state.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/scheduler/renderer_process_type.h"
+#include "third_party/blink/renderer/platform/instrumentation/resource_coordinator/blink_resource_coordinator_base.h"
+#include "third_party/blink/renderer/platform/instrumentation/resource_coordinator/renderer_resource_coordinator.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/scheduler/base/real_time_domain.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_selector.h"
+#include "third_party/blink/renderer/platform/scheduler/base/virtual_time_domain.h"
+#include "third_party/blink/renderer/platform/scheduler/child/features.h"
+#include "third_party/blink/renderer/platform/scheduler/child/process_state.h"
+#include "third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain.h"
+#include "third_party/blink/renderer/platform/scheduler/renderer/webthread_impl_for_renderer_scheduler.h"
+
+namespace blink {
+namespace scheduler {
+namespace {
+// The run time of loading tasks is strongly bimodal. The vast majority are
+// very cheap, but there are usually a handful of very expensive tasks (e.g ~1
+// second on a mobile device) so we take a very pessimistic view when estimating
+// the cost of loading tasks.
+const int kLoadingTaskEstimationSampleCount = 1000;
+const double kLoadingTaskEstimationPercentile = 99;
+const int kTimerTaskEstimationSampleCount = 1000;
+const double kTimerTaskEstimationPercentile = 99;
+const int kShortIdlePeriodDurationSampleCount = 10;
+const double kShortIdlePeriodDurationPercentile = 50;
+// Amount of idle time left in a frame (as a ratio of the vsync interval) above
+// which main thread compositing can be considered fast.
+const double kFastCompositingIdleTimeThreshold = .2;
+// We do not throttle anything while audio is played and shortly after that.
+constexpr base::TimeDelta kThrottlingDelayAfterAudioIsPlayed =
+ base::TimeDelta::FromSeconds(5);
+constexpr base::TimeDelta kQueueingTimeWindowDuration =
+ base::TimeDelta::FromSeconds(1);
+const double kSamplingRateForTaskUkm = 0.0001;
+
+// Field trial name.
+const char kWakeUpThrottlingTrial[] = "RendererSchedulerWakeUpThrottling";
+const char kWakeUpDurationParam[] = "wake_up_duration_ms";
+
+constexpr base::TimeDelta kDefaultWakeUpDuration = base::TimeDelta();
+
+base::TimeDelta GetWakeUpDuration() {
+ int duration_ms;
+ if (!base::StringToInt(base::GetFieldTrialParamValue(kWakeUpThrottlingTrial,
+ kWakeUpDurationParam),
+ &duration_ms))
+ return kDefaultWakeUpDuration;
+ return base::TimeDelta::FromMilliseconds(duration_ms);
+}
+
+const char* BackgroundStateToString(bool is_backgrounded) {
+ if (is_backgrounded) {
+ return "renderer_backgrounded";
+ } else {
+ return "renderer_visible";
+ }
+}
+
+const char* HiddenStateToString(bool is_hidden) {
+ if (is_hidden) {
+ return "hidden";
+ } else {
+ return "visible";
+ }
+}
+
+const char* AudioPlayingStateToString(bool is_audio_playing) {
+ if (is_audio_playing) {
+ return "playing";
+ } else {
+ return "silent";
+ }
+}
+
+const char* RendererProcessTypeToString(RendererProcessType process_type) {
+ switch (process_type) {
+ case RendererProcessType::kRenderer:
+ return "normal";
+ case RendererProcessType::kExtensionRenderer:
+ return "extension";
+ }
+ NOTREACHED();
+ return ""; // MSVC needs that.
+}
+
+const char* TaskTypeToString(TaskType task_type) {
+ switch (task_type) {
+ case TaskType::kDeprecatedNone:
+ return "None";
+ case TaskType::kDOMManipulation:
+ return "DOMManipultion";
+ case TaskType::kUserInteraction:
+ return "UserInteraction";
+ case TaskType::kNetworking:
+ return "Networking";
+ case TaskType::kNetworkingControl:
+ return "NetworkingControl";
+ case TaskType::kHistoryTraversal:
+ return "HistoryTraversal";
+ case TaskType::kEmbed:
+ return "Embed";
+ case TaskType::kMediaElementEvent:
+ return "MediaElementEvent";
+ case TaskType::kCanvasBlobSerialization:
+ return "CanvasBlobSerialization";
+ case TaskType::kMicrotask:
+ return "Microtask";
+ case TaskType::kJavascriptTimer:
+ return "JavascriptTimer";
+ case TaskType::kRemoteEvent:
+ return "RemoteEvent";
+ case TaskType::kWebSocket:
+ return "WebSocket";
+ case TaskType::kPostedMessage:
+ return "PostedMessage";
+ case TaskType::kUnshippedPortMessage:
+ return "UnshipedPortMessage";
+ case TaskType::kFileReading:
+ return "FileReading";
+ case TaskType::kDatabaseAccess:
+ return "DatabaseAccess";
+ case TaskType::kPresentation:
+ return "Presentation";
+ case TaskType::kSensor:
+ return "Sensor";
+ case TaskType::kPerformanceTimeline:
+ return "PerformanceTimeline";
+ case TaskType::kWebGL:
+ return "WebGL";
+ case TaskType::kIdleTask:
+ return "IdleTask";
+ case TaskType::kMiscPlatformAPI:
+ return "MiscPlatformAPI";
+ case TaskType::kUnspecedTimer:
+ return "UnspecedTimer";
+ case TaskType::kInternalLoading:
+ return "UnspecedLoading";
+ case TaskType::kUnthrottled:
+ return "Unthrottled";
+ case TaskType::kInternalTest:
+ return "InternalTest";
+ case TaskType::kInternalWebCrypto:
+ return "InternalWebCrypto";
+ case TaskType::kInternalIndexedDB:
+ return "InternalIndexedDB";
+ case TaskType::kInternalMedia:
+ return "InternalMedia";
+ case TaskType::kInternalMediaRealTime:
+ return "InternalMediaRealTime";
+ case TaskType::kInternalIPC:
+ return "InternalIPC";
+ case TaskType::kInternalUserInteraction:
+ return "InternalUserInteraction";
+ case TaskType::kInternalInspector:
+ return "InternalInspector";
+ case TaskType::kInternalAnimation:
+ return "InternalAnimation";
+ case TaskType::kCount:
+ return "Count";
+ }
+ NOTREACHED();
+ return "";
+}
+
+const char* OptionalTaskDescriptionToString(
+ base::Optional<MainThreadSchedulerImpl::TaskDescriptionForTracing> desc) {
+ if (!desc)
+ return nullptr;
+ if (desc->task_type != TaskType::kDeprecatedNone)
+ return TaskTypeToString(desc->task_type);
+ if (!desc->queue_type)
+ return "detached_tq";
+ return MainThreadTaskQueue::NameForQueueType(desc->queue_type.value());
+}
+
+bool IsUnconditionalHighPriorityInputEnabled() {
+ return base::FeatureList::IsEnabled(kHighPriorityInput);
+}
+
+} // namespace
+
+MainThreadSchedulerImpl::MainThreadSchedulerImpl(
+ std::unique_ptr<TaskQueueManager> task_queue_manager,
+ base::Optional<base::Time> initial_virtual_time)
+ : helper_(std::move(task_queue_manager), this),
+ idle_helper_(
+ &helper_,
+ this,
+ "MainThreadSchedulerIdlePeriod",
+ base::TimeDelta(),
+ helper_.NewTaskQueue(MainThreadTaskQueue::QueueCreationParams(
+ MainThreadTaskQueue::QueueType::kIdle))),
+ idle_canceled_delayed_task_sweeper_(&helper_,
+ idle_helper_.IdleTaskRunner()),
+ render_widget_scheduler_signals_(this),
+ control_task_queue_(helper_.ControlMainThreadTaskQueue()),
+ compositor_task_queue_(
+ helper_.NewTaskQueue(MainThreadTaskQueue::QueueCreationParams(
+ MainThreadTaskQueue::QueueType::kCompositor)
+ .SetShouldMonitorQuiescence(true))),
+ input_task_queue_(helper_.NewTaskQueue(
+ MainThreadTaskQueue::QueueCreationParams(
+ MainThreadTaskQueue::QueueType::kInput)
+ .SetShouldMonitorQuiescence(true)
+ .SetUsedForImportantTasks(
+ IsUnconditionalHighPriorityInputEnabled()))),
+ compositor_task_queue_enabled_voter_(
+ compositor_task_queue_->CreateQueueEnabledVoter()),
+ input_task_queue_enabled_voter_(
+ input_task_queue_->CreateQueueEnabledVoter()),
+ delayed_update_policy_runner_(
+ base::BindRepeating(&MainThreadSchedulerImpl::UpdatePolicy,
+ base::Unretained(this)),
+ helper_.ControlMainThreadTaskQueue()),
+ seqlock_queueing_time_estimator_(
+ QueueingTimeEstimator(this, kQueueingTimeWindowDuration, 20)),
+ main_thread_only_(this,
+ compositor_task_queue_,
+ helper_.GetClock(),
+ helper_.NowTicks()),
+ any_thread_(this),
+ policy_may_need_update_(&any_thread_lock_),
+ weak_factory_(this) {
+ task_queue_throttler_.reset(
+ new TaskQueueThrottler(this, &tracing_controller_));
+ update_policy_closure_ = base::BindRepeating(
+ &MainThreadSchedulerImpl::UpdatePolicy, weak_factory_.GetWeakPtr());
+ end_renderer_hidden_idle_period_closure_.Reset(base::BindRepeating(
+ &MainThreadSchedulerImpl::EndIdlePeriod, weak_factory_.GetWeakPtr()));
+
+ // Compositor task queue and default task queue should be managed by
+ // WebMainThreadScheduler. Control task queue should not.
+ task_runners_.insert(
+ std::make_pair(helper_.DefaultMainThreadTaskQueue(), nullptr));
+ task_runners_.insert(
+ std::make_pair(compositor_task_queue_,
+ compositor_task_queue_->CreateQueueEnabledVoter()));
+ task_runners_.insert(std::make_pair(
+ input_task_queue_, input_task_queue_->CreateQueueEnabledVoter()));
+
+ v8_task_queue_ = NewTaskQueue(MainThreadTaskQueue::QueueCreationParams(
+ MainThreadTaskQueue::QueueType::kV8));
+ ipc_task_queue_ = NewTaskQueue(MainThreadTaskQueue::QueueCreationParams(
+ MainThreadTaskQueue::QueueType::kIPC));
+
+ TRACE_EVENT_OBJECT_CREATED_WITH_ID(
+ TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "MainThreadScheduler",
+ this);
+
+ helper_.SetObserver(this);
+
+ // Register a tracing state observer unless we're running in a test without a
+ // task runner. Note that it's safe to remove a non-existent observer.
+ if (base::ThreadTaskRunnerHandle::IsSet()) {
+ base::trace_event::TraceLog::GetInstance()->AddAsyncEnabledStateObserver(
+ weak_factory_.GetWeakPtr());
+ }
+
+ int32_t delay_for_background_tab_stopping_millis;
+ if (!base::StringToInt(
+ base::GetFieldTrialParamValue("BackgroundTabStopping",
+ "DelayForBackgroundTabStoppingMills"),
+ &delay_for_background_tab_stopping_millis)) {
+ delay_for_background_tab_stopping_millis =
+ kDelayForBackgroundTabStoppingMillis;
+ }
+ delay_for_background_tab_stopping_ = base::TimeDelta::FromMilliseconds(
+ delay_for_background_tab_stopping_millis);
+
+ internal::ProcessState::Get()->is_process_backgrounded =
+ main_thread_only().renderer_backgrounded;
+
+ if (initial_virtual_time) {
+ main_thread_only().initial_virtual_time = *initial_virtual_time;
+ // The real uptime of the machine is irrelevant if we're using virtual time
+ // we choose an arbitrary initial offset.
+ main_thread_only().initial_virtual_time_ticks =
+ base::TimeTicks() + base::TimeDelta::FromSeconds(10);
+ EnableVirtualTime(BaseTimeOverridePolicy::OVERRIDE);
+ SetVirtualTimePolicy(VirtualTimePolicy::kPause);
+ }
+}
+
+MainThreadSchedulerImpl::~MainThreadSchedulerImpl() {
+ TRACE_EVENT_OBJECT_DELETED_WITH_ID(
+ TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "MainThreadScheduler",
+ this);
+
+ for (auto& pair : task_runners_) {
+ TaskCostEstimator* observer = nullptr;
+ switch (pair.first->queue_class()) {
+ case MainThreadTaskQueue::QueueClass::kLoading:
+ observer = &main_thread_only().loading_task_cost_estimator;
+ break;
+ case MainThreadTaskQueue::QueueClass::kTimer:
+ observer = &main_thread_only().timer_task_cost_estimator;
+ break;
+ default:
+ observer = nullptr;
+ }
+
+ if (observer)
+ pair.first->RemoveTaskObserver(observer);
+
+ pair.first->ShutdownTaskQueue();
+ }
+
+ if (virtual_time_domain_)
+ UnregisterTimeDomain(virtual_time_domain_.get());
+
+ if (virtual_time_control_task_queue_)
+ virtual_time_control_task_queue_->ShutdownTaskQueue();
+
+ base::trace_event::TraceLog::GetInstance()->RemoveAsyncEnabledStateObserver(
+ this);
+
+ // Ensure the renderer scheduler was shut down explicitly, because otherwise
+ // we could end up having stale pointers to the Blink heap which has been
+ // terminated by this point.
+ DCHECK(was_shutdown_);
+}
+
+MainThreadSchedulerImpl::MainThreadOnly::MainThreadOnly(
+ MainThreadSchedulerImpl* main_thread_scheduler_impl,
+ const scoped_refptr<MainThreadTaskQueue>& compositor_task_runner,
+ const base::TickClock* time_source,
+ base::TimeTicks now)
+ : loading_task_cost_estimator(time_source,
+ kLoadingTaskEstimationSampleCount,
+ kLoadingTaskEstimationPercentile),
+ timer_task_cost_estimator(time_source,
+ kTimerTaskEstimationSampleCount,
+ kTimerTaskEstimationPercentile),
+ idle_time_estimator(compositor_task_runner,
+ time_source,
+ kShortIdlePeriodDurationSampleCount,
+ kShortIdlePeriodDurationPercentile),
+ current_use_case(UseCase::kNone,
+ "MainThreadScheduler.UseCase",
+ main_thread_scheduler_impl,
+ &main_thread_scheduler_impl->tracing_controller_,
+ UseCaseToString),
+ longest_jank_free_task_duration(
+ base::TimeDelta(),
+ "MainThreadScheduler.LongestJankFreeTaskDuration",
+ main_thread_scheduler_impl,
+ &main_thread_scheduler_impl->tracing_controller_,
+ TimeDeltaToMilliseconds),
+ renderer_pause_count(0,
+ "MainThreadScheduler.PauseCount",
+ main_thread_scheduler_impl,
+ &main_thread_scheduler_impl->tracing_controller_),
+ navigation_task_expected_count(
+ 0,
+ "MainThreadScheduler.NavigationTaskExpectedCount",
+ main_thread_scheduler_impl,
+ &main_thread_scheduler_impl->tracing_controller_),
+ expensive_task_policy(ExpensiveTaskPolicy::kRun,
+ "MainThreadScheduler.ExpensiveTaskPolicy",
+ main_thread_scheduler_impl,
+ &main_thread_scheduler_impl->tracing_controller_,
+ ExpensiveTaskPolicyToString),
+ rail_mode_for_tracing(current_policy.rail_mode(),
+ "MainThreadScheduler.RAILMode",
+ main_thread_scheduler_impl,
+ &main_thread_scheduler_impl->tracing_controller_,
+ RAILModeToString),
+ renderer_hidden(false,
+ "MainThreadScheduler.Hidden",
+ main_thread_scheduler_impl,
+ &main_thread_scheduler_impl->tracing_controller_,
+ HiddenStateToString),
+ renderer_backgrounded(kLaunchingProcessIsBackgrounded,
+ "RendererVisibility",
+ main_thread_scheduler_impl,
+ &main_thread_scheduler_impl->tracing_controller_,
+ BackgroundStateToString),
+ keep_active_fetch_or_worker(
+ false,
+ "MainThreadScheduler.KeepRendererActive",
+ main_thread_scheduler_impl,
+ &main_thread_scheduler_impl->tracing_controller_,
+ YesNoStateToString),
+ stopping_when_backgrounded_enabled(
+ false,
+ "MainThreadScheduler.StoppingWhenBackgroundedEnabled",
+ main_thread_scheduler_impl,
+ &main_thread_scheduler_impl->tracing_controller_,
+ YesNoStateToString),
+ stopped_when_backgrounded(
+ false,
+ "MainThreadScheduler.StoppedWhenBackgrounded",
+ main_thread_scheduler_impl,
+ &main_thread_scheduler_impl->tracing_controller_,
+ YesNoStateToString),
+ loading_task_estimated_cost(
+ base::TimeDelta(),
+ "MainThreadScheduler.LoadingTaskEstimatedCostMs",
+ main_thread_scheduler_impl,
+ &main_thread_scheduler_impl->tracing_controller_,
+ TimeDeltaToMilliseconds),
+ timer_task_estimated_cost(
+ base::TimeDelta(),
+ "MainThreadScheduler.TimerTaskEstimatedCostMs",
+ main_thread_scheduler_impl,
+ &main_thread_scheduler_impl->tracing_controller_,
+ TimeDeltaToMilliseconds),
+ loading_tasks_seem_expensive(
+ false,
+ "MainThreadScheduler.LoadingTasksSeemExpensive",
+ main_thread_scheduler_impl,
+ &main_thread_scheduler_impl->tracing_controller_,
+ YesNoStateToString),
+ timer_tasks_seem_expensive(
+ false,
+ "MainThreadScheduler.TimerTasksSeemExpensive",
+ main_thread_scheduler_impl,
+ &main_thread_scheduler_impl->tracing_controller_,
+ YesNoStateToString),
+ touchstart_expected_soon(false,
+ "MainThreadScheduler.TouchstartExpectedSoon",
+ main_thread_scheduler_impl,
+ &main_thread_scheduler_impl->tracing_controller_,
+ YesNoStateToString),
+ have_seen_a_begin_main_frame(
+ false,
+ "MainThreadScheduler.HasSeenBeginMainFrame",
+ main_thread_scheduler_impl,
+ &main_thread_scheduler_impl->tracing_controller_,
+ YesNoStateToString),
+ have_reported_blocking_intervention_in_current_policy(
+ false,
+ "MainThreadScheduler.HasReportedBlockingInterventionInCurrentPolicy",
+ main_thread_scheduler_impl,
+ &main_thread_scheduler_impl->tracing_controller_,
+ YesNoStateToString),
+ have_reported_blocking_intervention_since_navigation(
+ false,
+ "MainThreadScheduler.HasReportedBlockingInterventionSinceNavigation",
+ main_thread_scheduler_impl,
+ &main_thread_scheduler_impl->tracing_controller_,
+ YesNoStateToString),
+ has_visible_render_widget_with_touch_handler(
+ false,
+ "MainThreadScheduler.HasVisibleRenderWidgetWithTouchHandler",
+ main_thread_scheduler_impl,
+ &main_thread_scheduler_impl->tracing_controller_,
+ YesNoStateToString),
+ begin_frame_not_expected_soon(
+ false,
+ "MainThreadScheduler.BeginFrameNotExpectedSoon",
+ main_thread_scheduler_impl,
+ &main_thread_scheduler_impl->tracing_controller_,
+ YesNoStateToString),
+ in_idle_period_for_testing(
+ false,
+ "MainThreadScheduler.InIdlePeriod",
+ main_thread_scheduler_impl,
+ &main_thread_scheduler_impl->tracing_controller_,
+ YesNoStateToString),
+ use_virtual_time(false,
+ "MainThreadScheduler.UseVirtualTime",
+ main_thread_scheduler_impl,
+ &main_thread_scheduler_impl->tracing_controller_,
+ YesNoStateToString),
+ is_audio_playing(false,
+ "RendererAudioState",
+ main_thread_scheduler_impl,
+ &main_thread_scheduler_impl->tracing_controller_,
+ AudioPlayingStateToString),
+ compositor_will_send_main_frame_not_expected(
+ false,
+ "MainThreadScheduler.CompositorWillSendMainFrameNotExpected",
+ main_thread_scheduler_impl,
+ &main_thread_scheduler_impl->tracing_controller_,
+ YesNoStateToString),
+ has_navigated(false,
+ "MainThreadScheduler.HasNavigated",
+ main_thread_scheduler_impl,
+ &main_thread_scheduler_impl->tracing_controller_,
+ YesNoStateToString),
+ pause_timers_for_webview(false,
+ "MainThreadScheduler.PauseTimersForWebview",
+ main_thread_scheduler_impl,
+ &main_thread_scheduler_impl->tracing_controller_,
+ YesNoStateToString),
+ background_status_changed_at(now),
+ rail_mode_observer(nullptr),
+ wake_up_budget_pool(nullptr),
+ metrics_helper(main_thread_scheduler_impl, now, renderer_backgrounded),
+ process_type(RendererProcessType::kRenderer,
+ "RendererProcessType",
+ main_thread_scheduler_impl,
+ &main_thread_scheduler_impl->tracing_controller_,
+ RendererProcessTypeToString),
+ task_description_for_tracing(
+ base::nullopt,
+ "MainThreadScheduler.MainThreadTask",
+ main_thread_scheduler_impl,
+ &main_thread_scheduler_impl->tracing_controller_,
+ OptionalTaskDescriptionToString),
+ virtual_time_policy(VirtualTimePolicy::kAdvance),
+ virtual_time_pause_count(0),
+ max_virtual_time_task_starvation_count(0),
+ virtual_time_stopped(false),
+ nested_runloop(false),
+ uniform_distribution(0.0f, 1.0f) {}
+
+MainThreadSchedulerImpl::MainThreadOnly::~MainThreadOnly() = default;
+
+MainThreadSchedulerImpl::AnyThread::AnyThread(
+ MainThreadSchedulerImpl* main_thread_scheduler_impl)
+ : awaiting_touch_start_response(
+ false,
+ "MainThreadScheduler.AwaitingTouchstartResponse",
+ main_thread_scheduler_impl,
+ &main_thread_scheduler_impl->tracing_controller_,
+ YesNoStateToString),
+ in_idle_period(false,
+ "MainThreadScheduler.InIdlePeriod",
+ main_thread_scheduler_impl,
+ &main_thread_scheduler_impl->tracing_controller_,
+ YesNoStateToString),
+ begin_main_frame_on_critical_path(
+ false,
+ "MainThreadScheduler.BeginMainFrameOnCriticalPath",
+ main_thread_scheduler_impl,
+ &main_thread_scheduler_impl->tracing_controller_,
+ YesNoStateToString),
+ last_gesture_was_compositor_driven(
+ false,
+ "MainThreadScheduler.LastGestureWasCompositorDriven",
+ main_thread_scheduler_impl,
+ &main_thread_scheduler_impl->tracing_controller_,
+ YesNoStateToString),
+ default_gesture_prevented(
+ true,
+ "MainThreadScheduler.DefaultGesturePrevented",
+ main_thread_scheduler_impl,
+ &main_thread_scheduler_impl->tracing_controller_,
+ YesNoStateToString),
+ have_seen_a_potentially_blocking_gesture(
+ false,
+ "MainThreadScheduler.HaveSeenPotentiallyBlockingGesture",
+ main_thread_scheduler_impl,
+ &main_thread_scheduler_impl->tracing_controller_,
+ YesNoStateToString),
+ waiting_for_meaningful_paint(
+ false,
+ "MainThreadScheduler.WaitingForMeaningfulPaint",
+ main_thread_scheduler_impl,
+ &main_thread_scheduler_impl->tracing_controller_,
+ YesNoStateToString),
+ have_seen_input_since_navigation(
+ false,
+ "MainThreadScheduler.HaveSeenInputSinceNavigation",
+ main_thread_scheduler_impl,
+ &main_thread_scheduler_impl->tracing_controller_,
+ YesNoStateToString) {}
+
+MainThreadSchedulerImpl::AnyThread::~AnyThread() = default;
+
+MainThreadSchedulerImpl::CompositorThreadOnly::CompositorThreadOnly()
+ : last_input_type(blink::WebInputEvent::kUndefined),
+ main_thread_seems_unresponsive(false) {}
+
+MainThreadSchedulerImpl::CompositorThreadOnly::~CompositorThreadOnly() =
+ default;
+
+MainThreadSchedulerImpl::RendererPauseHandleImpl::RendererPauseHandleImpl(
+ MainThreadSchedulerImpl* scheduler)
+ : scheduler_(scheduler) {
+ scheduler_->PauseRendererImpl();
+}
+
+MainThreadSchedulerImpl::RendererPauseHandleImpl::~RendererPauseHandleImpl() {
+ scheduler_->ResumeRendererImpl();
+}
+
+void MainThreadSchedulerImpl::Shutdown() {
+ if (was_shutdown_)
+ return;
+
+ base::TimeTicks now = tick_clock()->NowTicks();
+ main_thread_only().metrics_helper.OnRendererShutdown(now);
+
+ task_queue_throttler_.reset();
+ idle_helper_.Shutdown();
+ helper_.Shutdown();
+ main_thread_only().rail_mode_observer = nullptr;
+ was_shutdown_ = true;
+}
+
+std::unique_ptr<blink::WebThread> MainThreadSchedulerImpl::CreateMainThread() {
+ return std::make_unique<WebThreadImplForRendererScheduler>(this);
+}
+
+scoped_refptr<base::SingleThreadTaskRunner>
+MainThreadSchedulerImpl::DefaultTaskRunner() {
+ return helper_.DefaultMainThreadTaskQueue();
+}
+
+scoped_refptr<base::SingleThreadTaskRunner>
+MainThreadSchedulerImpl::CompositorTaskRunner() {
+ helper_.CheckOnValidThread();
+ return compositor_task_queue_;
+}
+
+scoped_refptr<base::SingleThreadTaskRunner>
+MainThreadSchedulerImpl::InputTaskRunner() {
+ helper_.CheckOnValidThread();
+ return input_task_queue_;
+}
+
+scoped_refptr<SingleThreadIdleTaskRunner>
+MainThreadSchedulerImpl::IdleTaskRunner() {
+ return idle_helper_.IdleTaskRunner();
+}
+
+scoped_refptr<base::SingleThreadTaskRunner>
+MainThreadSchedulerImpl::IPCTaskRunner() {
+ return ipc_task_queue_;
+}
+
+scoped_refptr<MainThreadTaskQueue> MainThreadSchedulerImpl::DefaultTaskQueue() {
+ return helper_.DefaultMainThreadTaskQueue();
+}
+
+scoped_refptr<MainThreadTaskQueue>
+MainThreadSchedulerImpl::CompositorTaskQueue() {
+ helper_.CheckOnValidThread();
+ return compositor_task_queue_;
+}
+
+scoped_refptr<MainThreadTaskQueue> MainThreadSchedulerImpl::InputTaskQueue() {
+ helper_.CheckOnValidThread();
+ return input_task_queue_;
+}
+
+scoped_refptr<MainThreadTaskQueue> MainThreadSchedulerImpl::V8TaskQueue() {
+ helper_.CheckOnValidThread();
+ return v8_task_queue_;
+}
+
+scoped_refptr<MainThreadTaskQueue> MainThreadSchedulerImpl::ControlTaskQueue() {
+ helper_.CheckOnValidThread();
+ return helper_.ControlMainThreadTaskQueue();
+}
+
+scoped_refptr<MainThreadTaskQueue>
+MainThreadSchedulerImpl::VirtualTimeControlTaskQueue() {
+ helper_.CheckOnValidThread();
+ return virtual_time_control_task_queue_;
+}
+
+scoped_refptr<MainThreadTaskQueue> MainThreadSchedulerImpl::NewTaskQueue(
+ const MainThreadTaskQueue::QueueCreationParams& params) {
+ helper_.CheckOnValidThread();
+ scoped_refptr<MainThreadTaskQueue> task_queue(helper_.NewTaskQueue(params));
+
+ std::unique_ptr<TaskQueue::QueueEnabledVoter> voter;
+ if (params.can_be_blocked || params.can_be_paused || params.can_be_stopped)
+ voter = task_queue->CreateQueueEnabledVoter();
+
+ auto insert_result =
+ task_runners_.insert(std::make_pair(task_queue, std::move(voter)));
+ auto queue_class = task_queue->queue_class();
+ if (queue_class == MainThreadTaskQueue::QueueClass::kTimer) {
+ task_queue->AddTaskObserver(&main_thread_only().timer_task_cost_estimator);
+ } else if (queue_class == MainThreadTaskQueue::QueueClass::kLoading) {
+ task_queue->AddTaskObserver(
+ &main_thread_only().loading_task_cost_estimator);
+ }
+
+ ApplyTaskQueuePolicy(
+ task_queue.get(), insert_result.first->second.get(), TaskQueuePolicy(),
+ main_thread_only().current_policy.GetQueuePolicy(queue_class));
+
+ if (task_queue->CanBeThrottled())
+ AddQueueToWakeUpBudgetPool(task_queue.get());
+
+ // If this is a timer queue, and virtual time is enabled and paused, it should
+ // be suspended by adding a fence to prevent immediate tasks from running when
+ // they're not supposed to.
+ if (queue_class == MainThreadTaskQueue::QueueClass::kTimer &&
+ main_thread_only().virtual_time_stopped &&
+ main_thread_only().use_virtual_time) {
+ task_queue->InsertFence(TaskQueue::InsertFencePosition::kNow);
+ }
+
+ return task_queue;
+}
+
+scoped_refptr<MainThreadTaskQueue> MainThreadSchedulerImpl::NewLoadingTaskQueue(
+ MainThreadTaskQueue::QueueType queue_type) {
+ DCHECK_EQ(MainThreadTaskQueue::QueueClassForQueueType(queue_type),
+ MainThreadTaskQueue::QueueClass::kLoading);
+ return NewTaskQueue(
+ MainThreadTaskQueue::QueueCreationParams(queue_type)
+ .SetCanBePaused(true)
+ .SetCanBeStopped(
+ RuntimeEnabledFeatures::StopLoadingInBackgroundEnabled())
+ .SetCanBeDeferred(true)
+ .SetUsedForImportantTasks(
+ queue_type ==
+ MainThreadTaskQueue::QueueType::kFrameLoadingControl));
+}
+
+scoped_refptr<MainThreadTaskQueue> MainThreadSchedulerImpl::NewTimerTaskQueue(
+ MainThreadTaskQueue::QueueType queue_type) {
+ DCHECK_EQ(MainThreadTaskQueue::QueueClassForQueueType(queue_type),
+ MainThreadTaskQueue::QueueClass::kTimer);
+ return NewTaskQueue(MainThreadTaskQueue::QueueCreationParams(queue_type)
+ .SetCanBePaused(true)
+ .SetCanBeStopped(true)
+ .SetCanBeDeferred(true)
+ .SetCanBeThrottled(true));
+}
+
+std::unique_ptr<WebRenderWidgetSchedulingState>
+MainThreadSchedulerImpl::NewRenderWidgetSchedulingState() {
+ return render_widget_scheduler_signals_.NewRenderWidgetSchedulingState();
+}
+
+void MainThreadSchedulerImpl::OnShutdownTaskQueue(
+ const scoped_refptr<MainThreadTaskQueue>& task_queue) {
+ if (was_shutdown_)
+ return;
+
+ if (task_queue_throttler_)
+ task_queue_throttler_->ShutdownTaskQueue(task_queue.get());
+
+ if (task_runners_.erase(task_queue)) {
+ switch (task_queue->queue_class()) {
+ case MainThreadTaskQueue::QueueClass::kTimer:
+ task_queue->RemoveTaskObserver(
+ &main_thread_only().timer_task_cost_estimator);
+ break;
+ case MainThreadTaskQueue::QueueClass::kLoading:
+ task_queue->RemoveTaskObserver(
+ &main_thread_only().loading_task_cost_estimator);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+bool MainThreadSchedulerImpl::CanExceedIdleDeadlineIfRequired() const {
+ return idle_helper_.CanExceedIdleDeadlineIfRequired();
+}
+
+void MainThreadSchedulerImpl::AddTaskObserver(
+ base::MessageLoop::TaskObserver* task_observer) {
+ helper_.AddTaskObserver(task_observer);
+}
+
+void MainThreadSchedulerImpl::RemoveTaskObserver(
+ base::MessageLoop::TaskObserver* task_observer) {
+ helper_.RemoveTaskObserver(task_observer);
+}
+
+void MainThreadSchedulerImpl::WillBeginFrame(const viz::BeginFrameArgs& args) {
+ TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "MainThreadSchedulerImpl::WillBeginFrame", "args",
+ args.AsValue());
+ helper_.CheckOnValidThread();
+ if (helper_.IsShutdown())
+ return;
+
+ EndIdlePeriod();
+ main_thread_only().estimated_next_frame_begin =
+ args.frame_time + args.interval;
+ main_thread_only().have_seen_a_begin_main_frame = true;
+ main_thread_only().begin_frame_not_expected_soon = false;
+ main_thread_only().compositor_frame_interval = args.interval;
+ {
+ base::AutoLock lock(any_thread_lock_);
+ any_thread().begin_main_frame_on_critical_path = args.on_critical_path;
+ }
+}
+
+void MainThreadSchedulerImpl::DidCommitFrameToCompositor() {
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "MainThreadSchedulerImpl::DidCommitFrameToCompositor");
+ helper_.CheckOnValidThread();
+ if (helper_.IsShutdown())
+ return;
+
+ base::TimeTicks now(helper_.NowTicks());
+ if (now < main_thread_only().estimated_next_frame_begin) {
+ // TODO(rmcilroy): Consider reducing the idle period based on the runtime of
+ // the next pending delayed tasks (as currently done in for long idle times)
+ idle_helper_.StartIdlePeriod(
+ IdleHelper::IdlePeriodState::kInShortIdlePeriod, now,
+ main_thread_only().estimated_next_frame_begin);
+ }
+
+ main_thread_only().idle_time_estimator.DidCommitFrameToCompositor();
+}
+
+void MainThreadSchedulerImpl::BeginFrameNotExpectedSoon() {
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "MainThreadSchedulerImpl::BeginFrameNotExpectedSoon");
+ helper_.CheckOnValidThread();
+ if (helper_.IsShutdown())
+ return;
+
+ main_thread_only().begin_frame_not_expected_soon = true;
+ idle_helper_.EnableLongIdlePeriod();
+ {
+ base::AutoLock lock(any_thread_lock_);
+ any_thread().begin_main_frame_on_critical_path = false;
+ }
+}
+
+void MainThreadSchedulerImpl::BeginMainFrameNotExpectedUntil(
+ base::TimeTicks time) {
+ helper_.CheckOnValidThread();
+ if (helper_.IsShutdown())
+ return;
+
+ base::TimeTicks now(helper_.NowTicks());
+ TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "MainThreadSchedulerImpl::BeginMainFrameNotExpectedUntil",
+ "time_remaining", (time - now).InMillisecondsF());
+
+ if (now < time) {
+ // End any previous idle period.
+ EndIdlePeriod();
+
+ // TODO(rmcilroy): Consider reducing the idle period based on the runtime of
+ // the next pending delayed tasks (as currently done in for long idle times)
+ idle_helper_.StartIdlePeriod(
+ IdleHelper::IdlePeriodState::kInShortIdlePeriod, now, time);
+ }
+}
+
+void MainThreadSchedulerImpl::SetAllRenderWidgetsHidden(bool hidden) {
+ TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "MainThreadSchedulerImpl::SetAllRenderWidgetsHidden", "hidden",
+ hidden);
+
+ helper_.CheckOnValidThread();
+
+ if (helper_.IsShutdown() || main_thread_only().renderer_hidden == hidden)
+ return;
+
+ end_renderer_hidden_idle_period_closure_.Cancel();
+
+ if (hidden) {
+ idle_helper_.EnableLongIdlePeriod();
+
+ // Ensure that we stop running idle tasks after a few seconds of being
+ // hidden.
+ base::TimeDelta end_idle_when_hidden_delay =
+ base::TimeDelta::FromMilliseconds(kEndIdleWhenHiddenDelayMillis);
+ control_task_queue_->PostDelayedTask(
+ FROM_HERE, end_renderer_hidden_idle_period_closure_.GetCallback(),
+ end_idle_when_hidden_delay);
+ main_thread_only().renderer_hidden = true;
+ } else {
+ main_thread_only().renderer_hidden = false;
+ EndIdlePeriod();
+ }
+
+ // TODO(alexclarke): Should we update policy here?
+ CreateTraceEventObjectSnapshot();
+}
+
+void MainThreadSchedulerImpl::SetHasVisibleRenderWidgetWithTouchHandler(
+ bool has_visible_render_widget_with_touch_handler) {
+ helper_.CheckOnValidThread();
+ if (has_visible_render_widget_with_touch_handler ==
+ main_thread_only().has_visible_render_widget_with_touch_handler)
+ return;
+
+ main_thread_only().has_visible_render_widget_with_touch_handler =
+ has_visible_render_widget_with_touch_handler;
+
+ base::AutoLock lock(any_thread_lock_);
+ UpdatePolicyLocked(UpdateType::kForceUpdate);
+}
+
+void MainThreadSchedulerImpl::SetRendererHidden(bool hidden) {
+ if (hidden) {
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "MainThreadSchedulerImpl::OnRendererHidden");
+ } else {
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "MainThreadSchedulerImpl::OnRendererVisible");
+ }
+ helper_.CheckOnValidThread();
+ main_thread_only().renderer_hidden = hidden;
+}
+
+void MainThreadSchedulerImpl::SetRendererBackgrounded(bool backgrounded) {
+ helper_.CheckOnValidThread();
+ if (helper_.IsShutdown() ||
+ main_thread_only().renderer_backgrounded == backgrounded)
+ return;
+ if (backgrounded) {
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "MainThreadSchedulerImpl::OnRendererBackgrounded");
+ RendererMetricsHelper::RecordBackgroundedTransition(
+ BackgroundedRendererTransition::kBackgrounded);
+ } else {
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "MainThreadSchedulerImpl::OnRendererForegrounded");
+ RendererMetricsHelper::RecordBackgroundedTransition(
+ BackgroundedRendererTransition::kForegrounded);
+ }
+
+ main_thread_only().renderer_backgrounded = backgrounded;
+ internal::ProcessState::Get()->is_process_backgrounded = backgrounded;
+
+ main_thread_only().background_status_changed_at = tick_clock()->NowTicks();
+ seqlock_queueing_time_estimator_.seqlock.WriteBegin();
+ seqlock_queueing_time_estimator_.data.OnRendererStateChanged(
+ backgrounded, main_thread_only().background_status_changed_at);
+ seqlock_queueing_time_estimator_.seqlock.WriteEnd();
+
+ UpdatePolicy();
+
+ base::TimeTicks now = tick_clock()->NowTicks();
+ if (backgrounded) {
+ main_thread_only().metrics_helper.OnRendererBackgrounded(now);
+ } else {
+ main_thread_only().metrics_helper.OnRendererForegrounded(now);
+ }
+}
+
+void MainThreadSchedulerImpl::SetSchedulerKeepActive(bool keep_active) {
+ main_thread_only().keep_active_fetch_or_worker = keep_active;
+ for (PageSchedulerImpl* page_scheduler : main_thread_only().page_schedulers) {
+ page_scheduler->SetKeepActive(keep_active);
+ }
+}
+
+bool MainThreadSchedulerImpl::SchedulerKeepActive() {
+ return main_thread_only().keep_active_fetch_or_worker;
+}
+
+#if defined(OS_ANDROID)
+void MainThreadSchedulerImpl::PauseTimersForAndroidWebView() {
+ main_thread_only().pause_timers_for_webview = true;
+ UpdatePolicy();
+}
+
+void MainThreadSchedulerImpl::ResumeTimersForAndroidWebView() {
+ main_thread_only().pause_timers_for_webview = false;
+ UpdatePolicy();
+}
+#endif
+
+void MainThreadSchedulerImpl::OnAudioStateChanged() {
+ bool is_audio_playing = false;
+ for (PageSchedulerImpl* page_scheduler : main_thread_only().page_schedulers) {
+ is_audio_playing = is_audio_playing || page_scheduler->IsPlayingAudio();
+ }
+
+ if (is_audio_playing == main_thread_only().is_audio_playing)
+ return;
+
+ main_thread_only().last_audio_state_change = helper_.NowTicks();
+ main_thread_only().is_audio_playing = is_audio_playing;
+
+ UpdatePolicy();
+}
+
+std::unique_ptr<MainThreadSchedulerImpl::RendererPauseHandle>
+MainThreadSchedulerImpl::PauseRenderer() {
+ return std::make_unique<RendererPauseHandleImpl>(this);
+}
+
+void MainThreadSchedulerImpl::PauseRendererImpl() {
+ helper_.CheckOnValidThread();
+ if (helper_.IsShutdown())
+ return;
+
+ ++main_thread_only().renderer_pause_count;
+ UpdatePolicy();
+}
+
+void MainThreadSchedulerImpl::ResumeRendererImpl() {
+ helper_.CheckOnValidThread();
+ if (helper_.IsShutdown())
+ return;
+ --main_thread_only().renderer_pause_count;
+ DCHECK_GE(main_thread_only().renderer_pause_count.value(), 0);
+ UpdatePolicy();
+}
+
+void MainThreadSchedulerImpl::EndIdlePeriod() {
+ if (main_thread_only().in_idle_period_for_testing)
+ return;
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "MainThreadSchedulerImpl::EndIdlePeriod");
+ helper_.CheckOnValidThread();
+ idle_helper_.EndIdlePeriod();
+}
+
+void MainThreadSchedulerImpl::EndIdlePeriodForTesting(
+ const base::Closure& callback,
+ base::TimeTicks time_remaining) {
+ main_thread_only().in_idle_period_for_testing = false;
+ EndIdlePeriod();
+ callback.Run();
+}
+
+bool MainThreadSchedulerImpl::PolicyNeedsUpdateForTesting() {
+ return policy_may_need_update_.IsSet();
+}
+
+// static
+bool MainThreadSchedulerImpl::ShouldPrioritizeInputEvent(
+ const blink::WebInputEvent& web_input_event) {
+ // We regard MouseMove events with the left mouse button down as a signal
+ // that the user is doing something requiring a smooth frame rate.
+ if ((web_input_event.GetType() == blink::WebInputEvent::kMouseDown ||
+ web_input_event.GetType() == blink::WebInputEvent::kMouseMove) &&
+ (web_input_event.GetModifiers() &
+ blink::WebInputEvent::kLeftButtonDown)) {
+ return true;
+ }
+ // Ignore all other mouse events because they probably don't signal user
+ // interaction needing a smooth framerate. NOTE isMouseEventType returns false
+ // for mouse wheel events, hence we regard them as user input.
+ // Ignore keyboard events because it doesn't really make sense to enter
+ // compositor priority for them.
+ if (blink::WebInputEvent::IsMouseEventType(web_input_event.GetType()) ||
+ blink::WebInputEvent::IsKeyboardEventType(web_input_event.GetType())) {
+ return false;
+ }
+ return true;
+}
+
+void MainThreadSchedulerImpl::DidHandleInputEventOnCompositorThread(
+ const blink::WebInputEvent& web_input_event,
+ InputEventState event_state) {
+ TRACE_EVENT0(
+ TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "MainThreadSchedulerImpl::DidHandleInputEventOnCompositorThread");
+ if (!ShouldPrioritizeInputEvent(web_input_event))
+ return;
+
+ UpdateForInputEventOnCompositorThread(web_input_event.GetType(), event_state);
+}
+
+void MainThreadSchedulerImpl::DidAnimateForInputOnCompositorThread() {
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "MainThreadSchedulerImpl::DidAnimateForInputOnCompositorThread");
+ base::AutoLock lock(any_thread_lock_);
+ any_thread().fling_compositor_escalation_deadline =
+ helper_.NowTicks() +
+ base::TimeDelta::FromMilliseconds(kFlingEscalationLimitMillis);
+}
+
+void MainThreadSchedulerImpl::UpdateForInputEventOnCompositorThread(
+ blink::WebInputEvent::Type type,
+ InputEventState input_event_state) {
+ base::AutoLock lock(any_thread_lock_);
+ base::TimeTicks now = helper_.NowTicks();
+
+ // TODO(alexclarke): Move WebInputEventTraits where we can access it from here
+ // and record the name rather than the integer representation.
+ TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "MainThreadSchedulerImpl::UpdateForInputEventOnCompositorThread",
+ "type", static_cast<int>(type), "input_event_state",
+ InputEventStateToString(input_event_state));
+
+ base::TimeDelta unused_policy_duration;
+ UseCase previous_use_case =
+ ComputeCurrentUseCase(now, &unused_policy_duration);
+ bool was_awaiting_touch_start_response =
+ any_thread().awaiting_touch_start_response;
+
+ any_thread().user_model.DidStartProcessingInputEvent(type, now);
+ any_thread().have_seen_input_since_navigation = true;
+
+ if (input_event_state == InputEventState::EVENT_CONSUMED_BY_COMPOSITOR)
+ any_thread().user_model.DidFinishProcessingInputEvent(now);
+
+ switch (type) {
+ case blink::WebInputEvent::kTouchStart:
+ any_thread().awaiting_touch_start_response = true;
+ // This is just a fail-safe to reset the state of
+ // |last_gesture_was_compositor_driven| to the default. We don't know
+ // yet where the gesture will run.
+ any_thread().last_gesture_was_compositor_driven = false;
+ any_thread().have_seen_a_potentially_blocking_gesture = true;
+ // Assume the default gesture is prevented until we see evidence
+ // otherwise.
+ any_thread().default_gesture_prevented = true;
+ break;
+
+ case blink::WebInputEvent::kTouchMove:
+ // Observation of consecutive touchmoves is a strong signal that the
+ // page is consuming the touch sequence, in which case touchstart
+ // response prioritization is no longer necessary. Otherwise, the
+ // initial touchmove should preserve the touchstart response pending
+ // state.
+ if (any_thread().awaiting_touch_start_response &&
+ GetCompositorThreadOnly().last_input_type ==
+ blink::WebInputEvent::kTouchMove) {
+ any_thread().awaiting_touch_start_response = false;
+ }
+ break;
+
+ case blink::WebInputEvent::kGesturePinchUpdate:
+ case blink::WebInputEvent::kGestureScrollUpdate:
+ // If we see events for an established gesture, we can lock it to the
+ // appropriate thread as the gesture can no longer be cancelled.
+ any_thread().last_gesture_was_compositor_driven =
+ input_event_state == InputEventState::EVENT_CONSUMED_BY_COMPOSITOR;
+ any_thread().awaiting_touch_start_response = false;
+ any_thread().default_gesture_prevented = false;
+ break;
+
+ case blink::WebInputEvent::kGestureFlingCancel:
+ any_thread().fling_compositor_escalation_deadline = base::TimeTicks();
+ break;
+
+ case blink::WebInputEvent::kGestureTapDown:
+ case blink::WebInputEvent::kGestureShowPress:
+ case blink::WebInputEvent::kGestureScrollEnd:
+ // With no observable effect, these meta events do not indicate a
+ // meaningful touchstart response and should not impact task priority.
+ break;
+
+ case blink::WebInputEvent::kMouseDown:
+ // Reset tracking state at the start of a new mouse drag gesture.
+ any_thread().last_gesture_was_compositor_driven = false;
+ any_thread().default_gesture_prevented = true;
+ break;
+
+ case blink::WebInputEvent::kMouseMove:
+ // Consider mouse movement with the left button held down (see
+ // ShouldPrioritizeInputEvent) similarly to a touch gesture.
+ any_thread().last_gesture_was_compositor_driven =
+ input_event_state == InputEventState::EVENT_CONSUMED_BY_COMPOSITOR;
+ any_thread().awaiting_touch_start_response = false;
+ break;
+
+ case blink::WebInputEvent::kMouseWheel:
+ any_thread().last_gesture_was_compositor_driven =
+ input_event_state == InputEventState::EVENT_CONSUMED_BY_COMPOSITOR;
+ any_thread().awaiting_touch_start_response = false;
+ any_thread().have_seen_a_potentially_blocking_gesture = true;
+ // If the event was sent to the main thread, assume the default gesture is
+ // prevented until we see evidence otherwise.
+ any_thread().default_gesture_prevented =
+ !any_thread().last_gesture_was_compositor_driven;
+ break;
+
+ case blink::WebInputEvent::kUndefined:
+ break;
+
+ default:
+ any_thread().awaiting_touch_start_response = false;
+ break;
+ }
+
+ // Avoid unnecessary policy updates if the use case did not change.
+ UseCase use_case = ComputeCurrentUseCase(now, &unused_policy_duration);
+
+ if (use_case != previous_use_case ||
+ was_awaiting_touch_start_response !=
+ any_thread().awaiting_touch_start_response) {
+ EnsureUrgentPolicyUpdatePostedOnMainThread(FROM_HERE);
+ }
+ GetCompositorThreadOnly().last_input_type = type;
+}
+
+void MainThreadSchedulerImpl::DidHandleInputEventOnMainThread(
+ const WebInputEvent& web_input_event,
+ WebInputEventResult result) {
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "MainThreadSchedulerImpl::DidHandleInputEventOnMainThread");
+ helper_.CheckOnValidThread();
+ if (ShouldPrioritizeInputEvent(web_input_event)) {
+ base::AutoLock lock(any_thread_lock_);
+ any_thread().user_model.DidFinishProcessingInputEvent(helper_.NowTicks());
+
+ // If we were waiting for a touchstart response and the main thread has
+ // prevented the default gesture, consider the gesture established. This
+ // ensures single-event gestures such as button presses are promptly
+ // detected.
+ if (any_thread().awaiting_touch_start_response &&
+ result == WebInputEventResult::kHandledApplication) {
+ any_thread().awaiting_touch_start_response = false;
+ any_thread().default_gesture_prevented = true;
+ UpdatePolicyLocked(UpdateType::kMayEarlyOutIfPolicyUnchanged);
+ }
+ }
+}
+
+base::TimeDelta MainThreadSchedulerImpl::MostRecentExpectedQueueingTime() {
+ return main_thread_only().most_recent_expected_queueing_time;
+}
+
+bool MainThreadSchedulerImpl::IsHighPriorityWorkAnticipated() {
+ helper_.CheckOnValidThread();
+ if (helper_.IsShutdown())
+ return false;
+
+ MaybeUpdatePolicy();
+ // The touchstart, synchronized gesture and main-thread gesture use cases
+ // indicate a strong likelihood of high-priority work in the near future.
+ UseCase use_case = main_thread_only().current_use_case;
+ return main_thread_only().touchstart_expected_soon ||
+ use_case == UseCase::kTouchstart ||
+ use_case == UseCase::kMainThreadGesture ||
+ use_case == UseCase::kMainThreadCustomInputHandling ||
+ use_case == UseCase::kSynchronizedGesture;
+}
+
+bool MainThreadSchedulerImpl::ShouldYieldForHighPriorityWork() {
+ helper_.CheckOnValidThread();
+ if (helper_.IsShutdown())
+ return false;
+
+ MaybeUpdatePolicy();
+ // We only yield if there's a urgent task to be run now, or we are expecting
+ // one soon (touch start).
+ // Note: even though the control queue has the highest priority we don't yield
+ // for it since these tasks are not user-provided work and they are only
+ // intended to run before the next task, not interrupt the tasks.
+ switch (main_thread_only().current_use_case) {
+ case UseCase::kCompositorGesture:
+ case UseCase::kNone:
+ return main_thread_only().touchstart_expected_soon;
+
+ case UseCase::kMainThreadGesture:
+ case UseCase::kMainThreadCustomInputHandling:
+ case UseCase::kSynchronizedGesture:
+ return compositor_task_queue_->HasTaskToRunImmediately() ||
+ main_thread_only().touchstart_expected_soon;
+
+ case UseCase::kTouchstart:
+ return true;
+
+ case UseCase::kLoading:
+ return false;
+
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
+base::TimeTicks MainThreadSchedulerImpl::CurrentIdleTaskDeadlineForTesting()
+ const {
+ return idle_helper_.CurrentIdleTaskDeadline();
+}
+
+void MainThreadSchedulerImpl::RunIdleTasksForTesting(
+ const base::Closure& callback) {
+ main_thread_only().in_idle_period_for_testing = true;
+ IdleTaskRunner()->PostIdleTask(
+ FROM_HERE,
+ base::BindOnce(&MainThreadSchedulerImpl::EndIdlePeriodForTesting,
+ weak_factory_.GetWeakPtr(), callback));
+ idle_helper_.EnableLongIdlePeriod();
+}
+
+void MainThreadSchedulerImpl::MaybeUpdatePolicy() {
+ helper_.CheckOnValidThread();
+ if (policy_may_need_update_.IsSet()) {
+ UpdatePolicy();
+ }
+}
+
+void MainThreadSchedulerImpl::EnsureUrgentPolicyUpdatePostedOnMainThread(
+ const base::Location& from_here) {
+ // TODO(scheduler-dev): Check that this method isn't called from the main
+ // thread.
+ any_thread_lock_.AssertAcquired();
+ if (!policy_may_need_update_.IsSet()) {
+ policy_may_need_update_.SetWhileLocked(true);
+ control_task_queue_->PostTask(from_here, update_policy_closure_);
+ }
+}
+
+void MainThreadSchedulerImpl::UpdatePolicy() {
+ base::AutoLock lock(any_thread_lock_);
+ UpdatePolicyLocked(UpdateType::kMayEarlyOutIfPolicyUnchanged);
+}
+
+void MainThreadSchedulerImpl::ForceUpdatePolicy() {
+ base::AutoLock lock(any_thread_lock_);
+ UpdatePolicyLocked(UpdateType::kForceUpdate);
+}
+
+namespace {
+
+void UpdatePolicyDuration(base::TimeTicks now,
+ base::TimeTicks policy_expiration,
+ base::TimeDelta* policy_duration) {
+ if (policy_expiration <= now)
+ return;
+
+ if (policy_duration->is_zero()) {
+ *policy_duration = policy_expiration - now;
+ return;
+ }
+
+ *policy_duration = std::min(*policy_duration, policy_expiration - now);
+}
+
+} // namespace
+
+void MainThreadSchedulerImpl::UpdatePolicyLocked(UpdateType update_type) {
+ helper_.CheckOnValidThread();
+ any_thread_lock_.AssertAcquired();
+ if (helper_.IsShutdown())
+ return;
+
+ base::TimeTicks now = helper_.NowTicks();
+ policy_may_need_update_.SetWhileLocked(false);
+
+ base::TimeDelta expected_use_case_duration;
+ UseCase use_case = ComputeCurrentUseCase(now, &expected_use_case_duration);
+ main_thread_only().current_use_case = use_case;
+
+ base::TimeDelta touchstart_expected_flag_valid_for_duration;
+ // TODO(skyostil): Consider handlers for all types of blocking gestures (e.g.,
+ // mouse wheel) instead of just touchstart.
+ bool touchstart_expected_soon = false;
+ if (main_thread_only().has_visible_render_widget_with_touch_handler) {
+ touchstart_expected_soon = any_thread().user_model.IsGestureExpectedSoon(
+ now, &touchstart_expected_flag_valid_for_duration);
+ }
+ main_thread_only().touchstart_expected_soon = touchstart_expected_soon;
+
+ base::TimeDelta longest_jank_free_task_duration =
+ EstimateLongestJankFreeTaskDuration();
+ main_thread_only().longest_jank_free_task_duration =
+ longest_jank_free_task_duration;
+
+ main_thread_only().loading_task_estimated_cost =
+ main_thread_only().loading_task_cost_estimator.expected_task_duration();
+ bool loading_tasks_seem_expensive =
+ main_thread_only().loading_task_estimated_cost >
+ longest_jank_free_task_duration;
+
+ main_thread_only().timer_task_estimated_cost =
+ main_thread_only().timer_task_cost_estimator.expected_task_duration();
+ bool timer_tasks_seem_expensive =
+ main_thread_only().timer_task_estimated_cost >
+ longest_jank_free_task_duration;
+
+ main_thread_only().timer_tasks_seem_expensive = timer_tasks_seem_expensive;
+ main_thread_only().loading_tasks_seem_expensive =
+ loading_tasks_seem_expensive;
+
+ // The |new_policy_duration| is the minimum of |expected_use_case_duration|
+ // and |touchstart_expected_flag_valid_for_duration| unless one is zero in
+ // which case we choose the other.
+ base::TimeDelta new_policy_duration = expected_use_case_duration;
+ if (new_policy_duration.is_zero() ||
+ (touchstart_expected_flag_valid_for_duration > base::TimeDelta() &&
+ new_policy_duration > touchstart_expected_flag_valid_for_duration)) {
+ new_policy_duration = touchstart_expected_flag_valid_for_duration;
+ }
+
+ // Do not throttle while audio is playing or for a short period after that
+ // to make sure that pages playing short audio clips powered by timers
+ // work.
+ if (main_thread_only().last_audio_state_change &&
+ !main_thread_only().is_audio_playing) {
+ UpdatePolicyDuration(now,
+ main_thread_only().last_audio_state_change.value() +
+ kThrottlingDelayAfterAudioIsPlayed,
+ &new_policy_duration);
+ }
+
+ bool previously_stopped_when_backgrounded =
+ main_thread_only().stopped_when_backgrounded;
+ bool newly_stopped = false;
+ if (main_thread_only().renderer_backgrounded &&
+ main_thread_only().stopping_when_backgrounded_enabled) {
+ base::TimeTicks stop_at = main_thread_only().background_status_changed_at +
+ delay_for_background_tab_stopping_;
+
+ newly_stopped = !main_thread_only().stopped_when_backgrounded;
+ main_thread_only().stopped_when_backgrounded = now >= stop_at;
+ newly_stopped &= main_thread_only().stopped_when_backgrounded;
+
+ if (!main_thread_only().stopped_when_backgrounded)
+ UpdatePolicyDuration(now, stop_at, &new_policy_duration);
+ } else {
+ main_thread_only().stopped_when_backgrounded = false;
+ }
+
+ if (new_policy_duration > base::TimeDelta()) {
+ main_thread_only().current_policy_expiration_time =
+ now + new_policy_duration;
+ delayed_update_policy_runner_.SetDeadline(FROM_HERE, new_policy_duration,
+ now);
+ } else {
+ main_thread_only().current_policy_expiration_time = base::TimeTicks();
+ }
+
+ // Avoid prioritizing main thread compositing (e.g., rAF) if it is extremely
+ // slow, because that can cause starvation in other task sources.
+ bool main_thread_compositing_is_fast =
+ main_thread_only().idle_time_estimator.GetExpectedIdleDuration(
+ main_thread_only().compositor_frame_interval) >
+ main_thread_only().compositor_frame_interval *
+ kFastCompositingIdleTimeThreshold;
+
+ Policy new_policy;
+ ExpensiveTaskPolicy expensive_task_policy = ExpensiveTaskPolicy::kRun;
+ new_policy.rail_mode() = v8::PERFORMANCE_ANIMATION;
+
+ switch (use_case) {
+ case UseCase::kCompositorGesture:
+ if (touchstart_expected_soon) {
+ new_policy.rail_mode() = v8::PERFORMANCE_RESPONSE;
+ expensive_task_policy = ExpensiveTaskPolicy::kBlock;
+ new_policy.compositor_queue_policy().priority =
+ TaskQueue::kHighestPriority;
+ } else {
+ // What we really want to do is priorize loading tasks, but that doesn't
+ // seem to be safe. Instead we do that by proxy by deprioritizing
+ // compositor tasks. This should be safe since we've already gone to the
+ // pain of fixing ordering issues with them.
+ new_policy.compositor_queue_policy().priority = TaskQueue::kLowPriority;
+ }
+ break;
+
+ case UseCase::kSynchronizedGesture:
+ new_policy.compositor_queue_policy().priority =
+ main_thread_compositing_is_fast ? TaskQueue::kHighestPriority
+ : TaskQueue::kNormalPriority;
+ if (touchstart_expected_soon) {
+ new_policy.rail_mode() = v8::PERFORMANCE_RESPONSE;
+ expensive_task_policy = ExpensiveTaskPolicy::kBlock;
+ } else {
+ expensive_task_policy = ExpensiveTaskPolicy::kThrottle;
+ }
+ break;
+
+ case UseCase::kMainThreadCustomInputHandling:
+ // In main thread input handling scenarios we don't have perfect knowledge
+ // about which things we should be prioritizing, so we don't attempt to
+ // block expensive tasks because we don't know whether they were integral
+ // to the page's functionality or not.
+ new_policy.compositor_queue_policy().priority =
+ main_thread_compositing_is_fast ? TaskQueue::kHighestPriority
+ : TaskQueue::kNormalPriority;
+ break;
+
+ case UseCase::kMainThreadGesture:
+ // A main thread gesture is for example a scroll gesture which is handled
+ // by the main thread. Since we know the established gesture type, we can
+ // be a little more aggressive about prioritizing compositing and input
+ // handling over other tasks.
+ new_policy.compositor_queue_policy().priority =
+ TaskQueue::kHighestPriority;
+ if (touchstart_expected_soon) {
+ new_policy.rail_mode() = v8::PERFORMANCE_RESPONSE;
+ expensive_task_policy = ExpensiveTaskPolicy::kBlock;
+ } else {
+ expensive_task_policy = ExpensiveTaskPolicy::kThrottle;
+ }
+ break;
+
+ case UseCase::kTouchstart:
+ new_policy.rail_mode() = v8::PERFORMANCE_RESPONSE;
+ new_policy.compositor_queue_policy().priority =
+ TaskQueue::kHighestPriority;
+ new_policy.loading_queue_policy().is_blocked = true;
+ new_policy.timer_queue_policy().is_blocked = true;
+ // NOTE this is a nop due to the above.
+ expensive_task_policy = ExpensiveTaskPolicy::kBlock;
+ break;
+
+ case UseCase::kNone:
+ // It's only safe to block tasks that if we are expecting a compositor
+ // driven gesture.
+ if (touchstart_expected_soon &&
+ any_thread().last_gesture_was_compositor_driven) {
+ new_policy.rail_mode() = v8::PERFORMANCE_RESPONSE;
+ expensive_task_policy = ExpensiveTaskPolicy::kBlock;
+ }
+ break;
+
+ case UseCase::kLoading:
+ new_policy.rail_mode() = v8::PERFORMANCE_LOAD;
+ // TODO(skyostil): Experiment with increasing loading and default queue
+ // priorities and throttling rendering frame rate.
+ break;
+
+ default:
+ NOTREACHED();
+ }
+
+ // TODO(skyostil): Add an idle state for foreground tabs too.
+ if (main_thread_only().renderer_hidden)
+ new_policy.rail_mode() = v8::PERFORMANCE_IDLE;
+
+ if (expensive_task_policy == ExpensiveTaskPolicy::kBlock &&
+ (!main_thread_only().have_seen_a_begin_main_frame ||
+ main_thread_only().navigation_task_expected_count > 0)) {
+ expensive_task_policy = ExpensiveTaskPolicy::kRun;
+ }
+
+ switch (expensive_task_policy) {
+ case ExpensiveTaskPolicy::kRun:
+ break;
+
+ case ExpensiveTaskPolicy::kBlock:
+ if (loading_tasks_seem_expensive)
+ new_policy.loading_queue_policy().is_blocked = true;
+ if (timer_tasks_seem_expensive)
+ new_policy.timer_queue_policy().is_blocked = true;
+ break;
+
+ case ExpensiveTaskPolicy::kThrottle:
+ if (loading_tasks_seem_expensive) {
+ new_policy.loading_queue_policy().is_throttled = true;
+ }
+ if (timer_tasks_seem_expensive) {
+ new_policy.timer_queue_policy().is_throttled = true;
+ }
+ break;
+ }
+ main_thread_only().expensive_task_policy = expensive_task_policy;
+
+ if (main_thread_only().stopped_when_backgrounded) {
+ // TODO(panicker): Remove this, as it is controlled at
+ // FrameScheduler. This is currently needed to avoid early out.
+ new_policy.timer_queue_policy().is_stopped = true;
+ }
+
+ if (main_thread_only().renderer_pause_count != 0) {
+ new_policy.loading_queue_policy().is_paused = true;
+ new_policy.timer_queue_policy().is_paused = true;
+ }
+ if (main_thread_only().pause_timers_for_webview) {
+ new_policy.timer_queue_policy().is_paused = true;
+ }
+
+ if (main_thread_only().renderer_backgrounded &&
+ RuntimeEnabledFeatures::TimerThrottlingForBackgroundTabsEnabled()) {
+ new_policy.timer_queue_policy().is_throttled = true;
+ }
+
+ if (main_thread_only().use_virtual_time) {
+ new_policy.compositor_queue_policy().use_virtual_time = true;
+ new_policy.default_queue_policy().use_virtual_time = true;
+ new_policy.loading_queue_policy().use_virtual_time = true;
+ new_policy.timer_queue_policy().use_virtual_time = true;
+ }
+
+ new_policy.should_disable_throttling() =
+ ShouldDisableThrottlingBecauseOfAudio(now) ||
+ main_thread_only().use_virtual_time;
+
+ // Tracing is done before the early out check, because it's quite possible we
+ // will otherwise miss this information in traces.
+ CreateTraceEventObjectSnapshotLocked();
+
+ // TODO(alexclarke): Can we get rid of force update now?
+ if (update_type == UpdateType::kMayEarlyOutIfPolicyUnchanged &&
+ new_policy == main_thread_only().current_policy) {
+ return;
+ }
+
+ for (const auto& pair : task_runners_) {
+ MainThreadTaskQueue::QueueClass queue_class = pair.first->queue_class();
+
+ ApplyTaskQueuePolicy(
+ pair.first.get(), pair.second.get(),
+ main_thread_only().current_policy.GetQueuePolicy(queue_class),
+ new_policy.GetQueuePolicy(queue_class));
+ }
+
+ main_thread_only().rail_mode_for_tracing = new_policy.rail_mode();
+ if (main_thread_only().rail_mode_observer &&
+ new_policy.rail_mode() != main_thread_only().current_policy.rail_mode()) {
+ main_thread_only().rail_mode_observer->OnRAILModeChanged(
+ new_policy.rail_mode());
+ }
+
+ // TODO(skyostil): send these notifications after releasing the scheduler
+ // lock.
+ if (main_thread_only().stopping_when_backgrounded_enabled) {
+ if (main_thread_only().stopped_when_backgrounded !=
+ previously_stopped_when_backgrounded) {
+ SetStoppedInBackground(main_thread_only().stopped_when_backgrounded);
+ RendererMetricsHelper::RecordBackgroundedTransition(
+ main_thread_only().stopped_when_backgrounded
+ ? BackgroundedRendererTransition::kStoppedAfterDelay
+ : BackgroundedRendererTransition::kResumed);
+ }
+ }
+
+ if (new_policy.should_disable_throttling() !=
+ main_thread_only().current_policy.should_disable_throttling()) {
+ if (new_policy.should_disable_throttling()) {
+ task_queue_throttler()->DisableThrottling();
+ } else {
+ task_queue_throttler()->EnableThrottling();
+ }
+ }
+
+ DCHECK(compositor_task_queue_->IsQueueEnabled());
+ main_thread_only().current_policy = new_policy;
+
+ if (newly_stopped)
+ Platform::Current()->RequestPurgeMemory();
+}
+
+void MainThreadSchedulerImpl::ApplyTaskQueuePolicy(
+ MainThreadTaskQueue* task_queue,
+ TaskQueue::QueueEnabledVoter* task_queue_enabled_voter,
+ const TaskQueuePolicy& old_task_queue_policy,
+ const TaskQueuePolicy& new_task_queue_policy) const {
+ DCHECK(old_task_queue_policy.IsQueueEnabled(task_queue) ||
+ task_queue_enabled_voter);
+ if (task_queue_enabled_voter) {
+ task_queue_enabled_voter->SetQueueEnabled(
+ new_task_queue_policy.IsQueueEnabled(task_queue));
+ }
+
+ // Make sure if there's no voter that the task queue is enabled.
+ DCHECK(task_queue_enabled_voter ||
+ old_task_queue_policy.IsQueueEnabled(task_queue));
+
+ task_queue->SetQueuePriority(new_task_queue_policy.GetPriority(task_queue));
+
+ TimeDomainType old_time_domain_type =
+ old_task_queue_policy.GetTimeDomainType(task_queue);
+ TimeDomainType new_time_domain_type =
+ new_task_queue_policy.GetTimeDomainType(task_queue);
+
+ if (old_time_domain_type != new_time_domain_type) {
+ if (old_time_domain_type == TimeDomainType::kThrottled) {
+ task_queue_throttler_->DecreaseThrottleRefCount(task_queue);
+ } else if (new_time_domain_type == TimeDomainType::kThrottled) {
+ task_queue_throttler_->IncreaseThrottleRefCount(task_queue);
+ }
+ if (new_time_domain_type == TimeDomainType::kVirtual) {
+ DCHECK(virtual_time_domain_);
+ task_queue->SetTimeDomain(virtual_time_domain_.get());
+ } else {
+ task_queue->SetTimeDomain(real_time_domain());
+ }
+ }
+}
+
+UseCase MainThreadSchedulerImpl::ComputeCurrentUseCase(
+ base::TimeTicks now,
+ base::TimeDelta* expected_use_case_duration) const {
+ any_thread_lock_.AssertAcquired();
+ // Special case for flings. This is needed because we don't get notification
+ // of a fling ending (although we do for cancellation).
+ if (any_thread().fling_compositor_escalation_deadline > now &&
+ !any_thread().awaiting_touch_start_response) {
+ *expected_use_case_duration =
+ any_thread().fling_compositor_escalation_deadline - now;
+ return UseCase::kCompositorGesture;
+ }
+ // Above all else we want to be responsive to user input.
+ *expected_use_case_duration =
+ any_thread().user_model.TimeLeftInUserGesture(now);
+ if (*expected_use_case_duration > base::TimeDelta()) {
+ // Has a gesture been fully established?
+ if (any_thread().awaiting_touch_start_response) {
+ // No, so arrange for compositor tasks to be run at the highest priority.
+ return UseCase::kTouchstart;
+ }
+
+ // Yes a gesture has been established. Based on how the gesture is handled
+ // we need to choose between one of four use cases:
+ // 1. kCompositorGesture where the gesture is processed only on the
+ // compositor thread.
+ // 2. MAIN_THREAD_GESTURE where the gesture is processed only on the main
+ // thread.
+ // 3. MAIN_THREAD_CUSTOM_INPUT_HANDLING where the main thread processes a
+ // stream of input events and has prevented a default gesture from being
+ // started.
+ // 4. SYNCHRONIZED_GESTURE where the gesture is processed on both threads.
+ if (any_thread().last_gesture_was_compositor_driven) {
+ if (any_thread().begin_main_frame_on_critical_path) {
+ return UseCase::kSynchronizedGesture;
+ } else {
+ return UseCase::kCompositorGesture;
+ }
+ }
+ if (any_thread().default_gesture_prevented) {
+ return UseCase::kMainThreadCustomInputHandling;
+ } else {
+ return UseCase::kMainThreadGesture;
+ }
+ }
+
+ // Occasionally the meaningful paint fails to be detected, so as a fallback we
+ // treat the presence of input as an indirect signal that there is meaningful
+ // content on the page.
+ if (any_thread().waiting_for_meaningful_paint &&
+ !any_thread().have_seen_input_since_navigation) {
+ return UseCase::kLoading;
+ }
+ return UseCase::kNone;
+}
+
+base::TimeDelta MainThreadSchedulerImpl::EstimateLongestJankFreeTaskDuration()
+ const {
+ switch (main_thread_only().current_use_case) {
+ case UseCase::kTouchstart:
+ case UseCase::kCompositorGesture:
+ case UseCase::kLoading:
+ case UseCase::kNone:
+ return base::TimeDelta::FromMilliseconds(kRailsResponseTimeMillis);
+
+ case UseCase::kMainThreadCustomInputHandling:
+ case UseCase::kMainThreadGesture:
+ case UseCase::kSynchronizedGesture:
+ return main_thread_only().idle_time_estimator.GetExpectedIdleDuration(
+ main_thread_only().compositor_frame_interval);
+
+ default:
+ NOTREACHED();
+ return base::TimeDelta::FromMilliseconds(kRailsResponseTimeMillis);
+ }
+}
+
+bool MainThreadSchedulerImpl::CanEnterLongIdlePeriod(
+ base::TimeTicks now,
+ base::TimeDelta* next_long_idle_period_delay_out) {
+ helper_.CheckOnValidThread();
+
+ MaybeUpdatePolicy();
+ if (main_thread_only().current_use_case == UseCase::kTouchstart) {
+ // Don't start a long idle task in touch start priority, try again when
+ // the policy is scheduled to end.
+ *next_long_idle_period_delay_out =
+ std::max(base::TimeDelta(),
+ main_thread_only().current_policy_expiration_time - now);
+ return false;
+ }
+ return true;
+}
+
+void MainThreadSchedulerImpl::SetStoppedInBackground(bool stopped) const {
+ for (PageSchedulerImpl* page_scheduler : main_thread_only().page_schedulers) {
+ // This moves the page to FROZEN lifecycle state.
+ page_scheduler->SetPageFrozen(stopped);
+ }
+}
+
+MainThreadSchedulerHelper*
+MainThreadSchedulerImpl::GetSchedulerHelperForTesting() {
+ return &helper_;
+}
+
+TaskCostEstimator*
+MainThreadSchedulerImpl::GetLoadingTaskCostEstimatorForTesting() {
+ return &main_thread_only().loading_task_cost_estimator;
+}
+
+TaskCostEstimator*
+MainThreadSchedulerImpl::GetTimerTaskCostEstimatorForTesting() {
+ return &main_thread_only().timer_task_cost_estimator;
+}
+
+IdleTimeEstimator* MainThreadSchedulerImpl::GetIdleTimeEstimatorForTesting() {
+ return &main_thread_only().idle_time_estimator;
+}
+
+WakeUpBudgetPool* MainThreadSchedulerImpl::GetWakeUpBudgetPoolForTesting() {
+ return main_thread_only().wake_up_budget_pool;
+}
+
+base::TimeTicks MainThreadSchedulerImpl::EnableVirtualTime(
+ BaseTimeOverridePolicy policy) {
+ if (main_thread_only().use_virtual_time)
+ return main_thread_only().initial_virtual_time_ticks;
+ main_thread_only().use_virtual_time = true;
+ DCHECK(!virtual_time_domain_);
+ if (main_thread_only().initial_virtual_time.is_null())
+ main_thread_only().initial_virtual_time = base::Time::Now();
+ if (main_thread_only().initial_virtual_time_ticks.is_null())
+ main_thread_only().initial_virtual_time_ticks = tick_clock()->NowTicks();
+ virtual_time_domain_.reset(new AutoAdvancingVirtualTimeDomain(
+ main_thread_only().initial_virtual_time +
+ main_thread_only().initial_virtual_time_offset,
+ main_thread_only().initial_virtual_time_ticks +
+ main_thread_only().initial_virtual_time_offset,
+ &helper_, policy));
+ RegisterTimeDomain(virtual_time_domain_.get());
+ virtual_time_domain_->SetObserver(this);
+
+ DCHECK(!virtual_time_control_task_queue_);
+ virtual_time_control_task_queue_ =
+ helper_.NewTaskQueue(MainThreadTaskQueue::QueueCreationParams(
+ MainThreadTaskQueue::QueueType::kControl));
+ virtual_time_control_task_queue_->SetQueuePriority(
+ TaskQueue::kControlPriority);
+ virtual_time_control_task_queue_->SetTimeDomain(virtual_time_domain_.get());
+
+ main_thread_only().use_virtual_time = true;
+ ForceUpdatePolicy();
+
+ virtual_time_domain_->SetCanAdvanceVirtualTime(
+ !main_thread_only().virtual_time_stopped);
+
+ if (main_thread_only().virtual_time_stopped)
+ VirtualTimePaused();
+ return main_thread_only().initial_virtual_time_ticks;
+}
+
+bool MainThreadSchedulerImpl::IsVirtualTimeEnabled() const {
+ return main_thread_only().use_virtual_time;
+}
+
+void MainThreadSchedulerImpl::DisableVirtualTimeForTesting() {
+ if (!main_thread_only().use_virtual_time)
+ return;
+ // Reset virtual time and all tasks queues back to their initial state.
+ main_thread_only().use_virtual_time = false;
+
+ if (main_thread_only().virtual_time_stopped) {
+ main_thread_only().virtual_time_stopped = false;
+ VirtualTimeResumed();
+ }
+
+ ForceUpdatePolicy();
+
+ virtual_time_control_task_queue_->ShutdownTaskQueue();
+ virtual_time_control_task_queue_ = nullptr;
+ UnregisterTimeDomain(virtual_time_domain_.get());
+ virtual_time_domain_.reset();
+ virtual_time_control_task_queue_ = nullptr;
+ ApplyVirtualTimePolicy();
+
+ // Reset the MetricsHelper because it gets confused by time going backwards.
+ base::TimeTicks now = tick_clock()->NowTicks();
+ main_thread_only().metrics_helper.ResetForTest(now);
+}
+
+void MainThreadSchedulerImpl::SetVirtualTimeStopped(bool virtual_time_stopped) {
+ if (main_thread_only().virtual_time_stopped == virtual_time_stopped)
+ return;
+ main_thread_only().virtual_time_stopped = virtual_time_stopped;
+
+ if (!main_thread_only().use_virtual_time)
+ return;
+
+ virtual_time_domain_->SetCanAdvanceVirtualTime(!virtual_time_stopped);
+
+ if (virtual_time_stopped) {
+ VirtualTimePaused();
+ } else {
+ VirtualTimeResumed();
+ }
+}
+
+void MainThreadSchedulerImpl::VirtualTimePaused() {
+ for (const auto& pair : task_runners_) {
+ if (pair.first->queue_class() == MainThreadTaskQueue::QueueClass::kTimer) {
+ DCHECK(!task_queue_throttler_->IsThrottled(pair.first.get()));
+ pair.first->InsertFence(TaskQueue::InsertFencePosition::kNow);
+ }
+ }
+ for (auto& observer : main_thread_only().virtual_time_observers) {
+ observer.OnVirtualTimePaused(virtual_time_domain_->Now() -
+ main_thread_only().initial_virtual_time_ticks);
+ }
+}
+
+void MainThreadSchedulerImpl::VirtualTimeResumed() {
+ for (const auto& pair : task_runners_) {
+ if (pair.first->queue_class() == MainThreadTaskQueue::QueueClass::kTimer) {
+ DCHECK(!task_queue_throttler_->IsThrottled(pair.first.get()));
+ DCHECK(pair.first->HasActiveFence());
+ pair.first->RemoveFence();
+ }
+ }
+}
+
+bool MainThreadSchedulerImpl::VirtualTimeAllowedToAdvance() const {
+ return !main_thread_only().virtual_time_stopped;
+}
+
+base::TimeTicks MainThreadSchedulerImpl::IncrementVirtualTimePauseCount() {
+ main_thread_only().virtual_time_pause_count++;
+ ApplyVirtualTimePolicy();
+
+ if (virtual_time_domain_)
+ return virtual_time_domain_->Now();
+ return tick_clock()->NowTicks();
+}
+
+void MainThreadSchedulerImpl::DecrementVirtualTimePauseCount() {
+ main_thread_only().virtual_time_pause_count--;
+ DCHECK_GE(main_thread_only().virtual_time_pause_count, 0);
+ ApplyVirtualTimePolicy();
+}
+
+void MainThreadSchedulerImpl::MaybeAdvanceVirtualTime(
+ base::TimeTicks new_virtual_time) {
+ if (virtual_time_domain_)
+ virtual_time_domain_->MaybeAdvanceVirtualTime(new_virtual_time);
+}
+
+void MainThreadSchedulerImpl::SetVirtualTimePolicy(VirtualTimePolicy policy) {
+ main_thread_only().virtual_time_policy = policy;
+ ApplyVirtualTimePolicy();
+}
+
+void MainThreadSchedulerImpl::SetInitialVirtualTimeOffset(
+ base::TimeDelta offset) {
+ main_thread_only().initial_virtual_time_offset = offset;
+}
+
+void MainThreadSchedulerImpl::AddVirtualTimeObserver(
+ VirtualTimeObserver* observer) {
+ main_thread_only().virtual_time_observers.AddObserver(observer);
+}
+
+void MainThreadSchedulerImpl::RemoveVirtualTimeObserver(
+ VirtualTimeObserver* observer) {
+ main_thread_only().virtual_time_observers.RemoveObserver(observer);
+}
+
+void MainThreadSchedulerImpl::OnVirtualTimeAdvanced() {
+ for (auto& observer : main_thread_only().virtual_time_observers) {
+ observer.OnVirtualTimeAdvanced(
+ virtual_time_domain_->Now() -
+ main_thread_only().initial_virtual_time_ticks);
+ }
+}
+
+void MainThreadSchedulerImpl::ApplyVirtualTimePolicy() {
+ switch (main_thread_only().virtual_time_policy) {
+ case VirtualTimePolicy::kAdvance:
+ if (virtual_time_domain_) {
+ virtual_time_domain_->SetMaxVirtualTimeTaskStarvationCount(
+ main_thread_only().nested_runloop
+ ? 0
+ : main_thread_only().max_virtual_time_task_starvation_count);
+ virtual_time_domain_->SetVirtualTimeFence(base::TimeTicks());
+ }
+ SetVirtualTimeStopped(false);
+ break;
+ case VirtualTimePolicy::kPause:
+ if (virtual_time_domain_) {
+ virtual_time_domain_->SetMaxVirtualTimeTaskStarvationCount(0);
+ virtual_time_domain_->SetVirtualTimeFence(virtual_time_domain_->Now());
+ }
+ SetVirtualTimeStopped(true);
+ break;
+ case VirtualTimePolicy::kDeterministicLoading:
+ if (virtual_time_domain_) {
+ virtual_time_domain_->SetMaxVirtualTimeTaskStarvationCount(
+ main_thread_only().nested_runloop
+ ? 0
+ : main_thread_only().max_virtual_time_task_starvation_count);
+ }
+
+ // We pause virtual time while the run loop is nested because that implies
+ // something modal is happening such as the DevTools debugger pausing the
+ // system. We also pause while the renderer is waiting for various
+ // asynchronous things e.g. resource load or navigation.
+ SetVirtualTimeStopped(main_thread_only().virtual_time_pause_count != 0 ||
+ main_thread_only().nested_runloop);
+ break;
+ }
+}
+
+void MainThreadSchedulerImpl::SetMaxVirtualTimeTaskStarvationCount(
+ int max_task_starvation_count) {
+ main_thread_only().max_virtual_time_task_starvation_count =
+ max_task_starvation_count;
+ ApplyVirtualTimePolicy();
+}
+
+void MainThreadSchedulerImpl::SetStoppingWhenBackgroundedEnabled(bool enabled) {
+ // Note that this will only take effect for the next backgrounded signal.
+ main_thread_only().stopping_when_backgrounded_enabled = enabled;
+}
+
+std::unique_ptr<base::trace_event::ConvertableToTraceFormat>
+MainThreadSchedulerImpl::AsValue(base::TimeTicks optional_now) const {
+ base::AutoLock lock(any_thread_lock_);
+ return AsValueLocked(optional_now);
+}
+
+void MainThreadSchedulerImpl::CreateTraceEventObjectSnapshot() const {
+ TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
+ TRACE_DISABLED_BY_DEFAULT("renderer.scheduler.debug"),
+ "MainThreadScheduler", this, AsValue(helper_.NowTicks()));
+}
+
+void MainThreadSchedulerImpl::CreateTraceEventObjectSnapshotLocked() const {
+ TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
+ TRACE_DISABLED_BY_DEFAULT("renderer.scheduler.debug"),
+ "MainThreadScheduler", this, AsValueLocked(helper_.NowTicks()));
+}
+
+// static
+const char* MainThreadSchedulerImpl::ExpensiveTaskPolicyToString(
+ ExpensiveTaskPolicy expensive_task_policy) {
+ switch (expensive_task_policy) {
+ case ExpensiveTaskPolicy::kRun:
+ return "run";
+ case ExpensiveTaskPolicy::kBlock:
+ return "block";
+ case ExpensiveTaskPolicy::kThrottle:
+ return "throttle";
+ default:
+ NOTREACHED();
+ return nullptr;
+ }
+}
+
+std::unique_ptr<base::trace_event::ConvertableToTraceFormat>
+MainThreadSchedulerImpl::AsValueLocked(base::TimeTicks optional_now) const {
+ helper_.CheckOnValidThread();
+ any_thread_lock_.AssertAcquired();
+
+ if (optional_now.is_null())
+ optional_now = helper_.NowTicks();
+ std::unique_ptr<base::trace_event::TracedValue> state(
+ new base::trace_event::TracedValue());
+ state->SetBoolean(
+ "has_visible_render_widget_with_touch_handler",
+ main_thread_only().has_visible_render_widget_with_touch_handler);
+ state->SetString("current_use_case",
+ UseCaseToString(main_thread_only().current_use_case));
+ state->SetBoolean("loading_tasks_seem_expensive",
+ main_thread_only().loading_tasks_seem_expensive);
+ state->SetBoolean("timer_tasks_seem_expensive",
+ main_thread_only().timer_tasks_seem_expensive);
+ state->SetBoolean("begin_frame_not_expected_soon",
+ main_thread_only().begin_frame_not_expected_soon);
+ state->SetBoolean(
+ "compositor_will_send_main_frame_not_expected",
+ main_thread_only().compositor_will_send_main_frame_not_expected);
+ state->SetBoolean("touchstart_expected_soon",
+ main_thread_only().touchstart_expected_soon);
+ state->SetString("idle_period_state",
+ IdleHelper::IdlePeriodStateToString(
+ idle_helper_.SchedulerIdlePeriodState()));
+ state->SetBoolean("renderer_hidden", main_thread_only().renderer_hidden);
+ state->SetBoolean("have_seen_a_begin_main_frame",
+ main_thread_only().have_seen_a_begin_main_frame);
+ state->SetBoolean("waiting_for_meaningful_paint",
+ any_thread().waiting_for_meaningful_paint);
+ state->SetBoolean("have_seen_input_since_navigation",
+ any_thread().have_seen_input_since_navigation);
+ state->SetBoolean(
+ "have_reported_blocking_intervention_in_current_policy",
+ main_thread_only().have_reported_blocking_intervention_in_current_policy);
+ state->SetBoolean(
+ "have_reported_blocking_intervention_since_navigation",
+ main_thread_only().have_reported_blocking_intervention_since_navigation);
+ state->SetBoolean("renderer_backgrounded",
+ main_thread_only().renderer_backgrounded);
+ state->SetBoolean("keep_active_fetch_or_worker",
+ main_thread_only().keep_active_fetch_or_worker);
+ state->SetBoolean("stopped_when_backgrounded",
+ main_thread_only().stopped_when_backgrounded);
+ state->SetDouble("now", (optional_now - base::TimeTicks()).InMillisecondsF());
+ state->SetDouble(
+ "fling_compositor_escalation_deadline",
+ (any_thread().fling_compositor_escalation_deadline - base::TimeTicks())
+ .InMillisecondsF());
+ state->SetInteger("navigation_task_expected_count",
+ main_thread_only().navigation_task_expected_count);
+ state->SetDouble("last_idle_period_end_time",
+ (any_thread().last_idle_period_end_time - base::TimeTicks())
+ .InMillisecondsF());
+ state->SetBoolean("awaiting_touch_start_response",
+ any_thread().awaiting_touch_start_response);
+ state->SetBoolean("begin_main_frame_on_critical_path",
+ any_thread().begin_main_frame_on_critical_path);
+ state->SetBoolean("last_gesture_was_compositor_driven",
+ any_thread().last_gesture_was_compositor_driven);
+ state->SetBoolean("default_gesture_prevented",
+ any_thread().default_gesture_prevented);
+ state->SetDouble("expected_loading_task_duration",
+ main_thread_only()
+ .loading_task_cost_estimator.expected_task_duration()
+ .InMillisecondsF());
+ state->SetDouble("expected_timer_task_duration",
+ main_thread_only()
+ .timer_task_cost_estimator.expected_task_duration()
+ .InMillisecondsF());
+ state->SetBoolean("is_audio_playing", main_thread_only().is_audio_playing);
+ state->SetBoolean("virtual_time_stopped",
+ main_thread_only().virtual_time_stopped);
+ state->SetDouble("virtual_time_pause_count",
+ main_thread_only().virtual_time_pause_count);
+ state->SetString(
+ "virtual_time_policy",
+ VirtualTimePolicyToString(main_thread_only().virtual_time_policy));
+ state->SetBoolean("virtual_time", main_thread_only().use_virtual_time);
+
+ state->BeginDictionary("page_schedulers");
+ for (PageSchedulerImpl* page_scheduler : main_thread_only().page_schedulers) {
+ state->BeginDictionaryWithCopiedName(PointerToString(page_scheduler));
+ page_scheduler->AsValueInto(state.get());
+ state->EndDictionary();
+ }
+ state->EndDictionary();
+
+ state->BeginDictionary("policy");
+ main_thread_only().current_policy.AsValueInto(state.get());
+ state->EndDictionary();
+
+ // TODO(skyostil): Can we somehow trace how accurate these estimates were?
+ state->SetDouble(
+ "longest_jank_free_task_duration",
+ main_thread_only().longest_jank_free_task_duration->InMillisecondsF());
+ state->SetDouble(
+ "compositor_frame_interval",
+ main_thread_only().compositor_frame_interval.InMillisecondsF());
+ state->SetDouble(
+ "estimated_next_frame_begin",
+ (main_thread_only().estimated_next_frame_begin - base::TimeTicks())
+ .InMillisecondsF());
+ state->SetBoolean("in_idle_period", any_thread().in_idle_period);
+
+ state->SetString(
+ "expensive_task_policy",
+ ExpensiveTaskPolicyToString(main_thread_only().expensive_task_policy));
+
+ any_thread().user_model.AsValueInto(state.get());
+ render_widget_scheduler_signals_.AsValueInto(state.get());
+
+ state->BeginDictionary("task_queue_throttler");
+ task_queue_throttler_->AsValueInto(state.get(), optional_now);
+ state->EndDictionary();
+
+ return std::move(state);
+}
+
+bool MainThreadSchedulerImpl::TaskQueuePolicy::IsQueueEnabled(
+ MainThreadTaskQueue* task_queue) const {
+ if (!is_enabled)
+ return false;
+ if (is_paused && task_queue->CanBePaused())
+ return false;
+ if (is_blocked && task_queue->CanBeDeferred())
+ return false;
+ // TODO(panicker): Remove this, as it is redundant as we stop per-frame
+ // task_queues in WebFrameScheduler
+ if (is_stopped && task_queue->CanBeStopped())
+ return false;
+ return true;
+}
+
+TaskQueue::QueuePriority MainThreadSchedulerImpl::TaskQueuePolicy::GetPriority(
+ MainThreadTaskQueue* task_queue) const {
+ return task_queue->UsedForImportantTasks() ? TaskQueue::kHighestPriority
+ : priority;
+}
+
+MainThreadSchedulerImpl::TimeDomainType
+MainThreadSchedulerImpl::TaskQueuePolicy::GetTimeDomainType(
+ MainThreadTaskQueue* task_queue) const {
+ if (use_virtual_time)
+ return TimeDomainType::kVirtual;
+ if (is_throttled && task_queue->CanBeThrottled())
+ return TimeDomainType::kThrottled;
+ return TimeDomainType::kReal;
+}
+
+void MainThreadSchedulerImpl::TaskQueuePolicy::AsValueInto(
+ base::trace_event::TracedValue* state) const {
+ state->SetBoolean("is_enabled", is_enabled);
+ state->SetBoolean("is_paused", is_paused);
+ state->SetBoolean("is_throttled", is_throttled);
+ state->SetBoolean("is_blocked", is_blocked);
+ state->SetBoolean("is_stopped", is_stopped);
+ state->SetBoolean("use_virtual_time", use_virtual_time);
+ state->SetString("priority", TaskQueue::PriorityToString(priority));
+}
+
+void MainThreadSchedulerImpl::Policy::AsValueInto(
+ base::trace_event::TracedValue* state) const {
+ state->BeginDictionary("compositor_queue_policy");
+ compositor_queue_policy().AsValueInto(state);
+ state->EndDictionary();
+
+ state->BeginDictionary("loading_queue_policy");
+ loading_queue_policy().AsValueInto(state);
+ state->EndDictionary();
+
+ state->BeginDictionary("timer_queue_policy");
+ timer_queue_policy().AsValueInto(state);
+ state->EndDictionary();
+
+ state->BeginDictionary("default_queue_policy");
+ default_queue_policy().AsValueInto(state);
+ state->EndDictionary();
+
+ state->SetString("rail_mode", RAILModeToString(rail_mode()));
+ state->SetBoolean("should_disable_throttling", should_disable_throttling());
+}
+
+void MainThreadSchedulerImpl::OnIdlePeriodStarted() {
+ base::AutoLock lock(any_thread_lock_);
+ any_thread().in_idle_period = true;
+ UpdatePolicyLocked(UpdateType::kMayEarlyOutIfPolicyUnchanged);
+}
+
+void MainThreadSchedulerImpl::OnIdlePeriodEnded() {
+ base::AutoLock lock(any_thread_lock_);
+ any_thread().last_idle_period_end_time = helper_.NowTicks();
+ any_thread().in_idle_period = false;
+ UpdatePolicyLocked(UpdateType::kMayEarlyOutIfPolicyUnchanged);
+}
+
+void MainThreadSchedulerImpl::OnPendingTasksChanged(bool has_tasks) {
+ if (has_tasks ==
+ main_thread_only().compositor_will_send_main_frame_not_expected.get())
+ return;
+
+ // Dispatch RequestBeginMainFrameNotExpectedSoon notifications asynchronously.
+ // This is needed because idle task can be posted (and OnPendingTasksChanged
+ // called) at any moment, including in the middle of allocating an object,
+ // when state is not consistent. Posting a task to dispatch notifications
+ // minimizes the amount of code that runs and sees an inconsistent state .
+ control_task_queue_->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &MainThreadSchedulerImpl::DispatchRequestBeginMainFrameNotExpected,
+ weak_factory_.GetWeakPtr(), has_tasks));
+}
+
+void MainThreadSchedulerImpl::DispatchRequestBeginMainFrameNotExpected(
+ bool has_tasks) {
+ if (has_tasks ==
+ main_thread_only().compositor_will_send_main_frame_not_expected.get())
+ return;
+ main_thread_only().compositor_will_send_main_frame_not_expected = has_tasks;
+
+ TRACE_EVENT1(
+ TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "MainThreadSchedulerImpl::DispatchRequestBeginMainFrameNotExpected",
+ "has_tasks", has_tasks);
+ for (PageSchedulerImpl* page_scheduler : main_thread_only().page_schedulers) {
+ page_scheduler->RequestBeginMainFrameNotExpected(has_tasks);
+ }
+}
+
+void MainThreadSchedulerImpl::AddPendingNavigation(NavigatingFrameType type) {
+ helper_.CheckOnValidThread();
+ if (type == NavigatingFrameType::kMainFrame) {
+ main_thread_only().navigation_task_expected_count++;
+ UpdatePolicy();
+ }
+}
+
+void MainThreadSchedulerImpl::RemovePendingNavigation(
+ NavigatingFrameType type) {
+ helper_.CheckOnValidThread();
+ DCHECK_GT(main_thread_only().navigation_task_expected_count, 0);
+ if (type == NavigatingFrameType::kMainFrame &&
+ main_thread_only().navigation_task_expected_count > 0) {
+ main_thread_only().navigation_task_expected_count--;
+ UpdatePolicy();
+ }
+}
+
+std::unique_ptr<base::SingleSampleMetric>
+MainThreadSchedulerImpl::CreateMaxQueueingTimeMetric() {
+ return base::SingleSampleMetricsFactory::Get()->CreateCustomCountsMetric(
+ "RendererScheduler.MaxQueueingTime", 1, 10000, 50);
+}
+
+void MainThreadSchedulerImpl::DidStartProvisionalLoad(bool is_main_frame) {
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "MainThreadSchedulerImpl::DidStartProvisionalLoad");
+ if (is_main_frame) {
+ base::AutoLock lock(any_thread_lock_);
+ ResetForNavigationLocked();
+ }
+}
+
+void MainThreadSchedulerImpl::DidCommitProvisionalLoad(
+ bool is_web_history_inert_commit,
+ bool is_reload,
+ bool is_main_frame) {
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "MainThreadSchedulerImpl::DidCommitProvisionalLoad");
+ // Initialize |max_queueing_time_metric| lazily so that
+ // |SingleSampleMetricsFactory::SetFactory()| is called before
+ // |SingleSampleMetricsFactory::Get()|
+ if (!main_thread_only().max_queueing_time_metric) {
+ main_thread_only().max_queueing_time_metric = CreateMaxQueueingTimeMetric();
+ }
+ main_thread_only().max_queueing_time_metric.reset();
+ main_thread_only().max_queueing_time = base::TimeDelta();
+ main_thread_only().has_navigated = true;
+
+ // If this either isn't a history inert commit or it's a reload then we must
+ // reset the task cost estimators.
+ if (is_main_frame && (!is_web_history_inert_commit || is_reload)) {
+ base::AutoLock lock(any_thread_lock_);
+ ResetForNavigationLocked();
+ }
+}
+
+void MainThreadSchedulerImpl::OnFirstMeaningfulPaint() {
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "MainThreadSchedulerImpl::OnFirstMeaningfulPaint");
+ base::AutoLock lock(any_thread_lock_);
+ any_thread().waiting_for_meaningful_paint = false;
+ UpdatePolicyLocked(UpdateType::kMayEarlyOutIfPolicyUnchanged);
+}
+
+void MainThreadSchedulerImpl::ResetForNavigationLocked() {
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "MainThreadSchedulerImpl::ResetForNavigationLocked");
+ helper_.CheckOnValidThread();
+ any_thread_lock_.AssertAcquired();
+ any_thread().user_model.Reset(helper_.NowTicks());
+ any_thread().have_seen_a_potentially_blocking_gesture = false;
+ any_thread().waiting_for_meaningful_paint = true;
+ any_thread().have_seen_input_since_navigation = false;
+ main_thread_only().loading_task_cost_estimator.Clear();
+ main_thread_only().timer_task_cost_estimator.Clear();
+ main_thread_only().idle_time_estimator.Clear();
+ main_thread_only().have_seen_a_begin_main_frame = false;
+ main_thread_only().have_reported_blocking_intervention_since_navigation =
+ false;
+ for (PageSchedulerImpl* page_scheduler : main_thread_only().page_schedulers) {
+ page_scheduler->OnNavigation();
+ }
+ UpdatePolicyLocked(UpdateType::kMayEarlyOutIfPolicyUnchanged);
+
+ UMA_HISTOGRAM_COUNTS_100("RendererScheduler.WebViewsPerScheduler",
+ main_thread_only().page_schedulers.size());
+
+ size_t frame_count = 0;
+ for (PageSchedulerImpl* page_scheduler : main_thread_only().page_schedulers) {
+ frame_count += page_scheduler->FrameCount();
+ }
+ UMA_HISTOGRAM_COUNTS_100("RendererScheduler.WebFramesPerScheduler",
+ frame_count);
+}
+
+void MainThreadSchedulerImpl::SetTopLevelBlameContext(
+ base::trace_event::BlameContext* blame_context) {
+ // Any task that runs in the default task runners belongs to the context of
+ // all frames (as opposed to a particular frame). Note that the task itself
+ // may still enter a more specific blame context if necessary.
+ //
+ // Per-frame task runners (loading, timers, etc.) are configured with a more
+ // specific blame context by FrameSchedulerImpl.
+ //
+ // TODO(altimin): automatically enter top-level for all task queues associated
+ // with renderer scheduler which do not have a corresponding frame.
+ control_task_queue_->SetBlameContext(blame_context);
+ DefaultTaskQueue()->SetBlameContext(blame_context);
+ compositor_task_queue_->SetBlameContext(blame_context);
+ idle_helper_.IdleTaskRunner()->SetBlameContext(blame_context);
+ v8_task_queue_->SetBlameContext(blame_context);
+ ipc_task_queue_->SetBlameContext(blame_context);
+}
+
+void MainThreadSchedulerImpl::SetRAILModeObserver(RAILModeObserver* observer) {
+ main_thread_only().rail_mode_observer = observer;
+}
+
+bool MainThreadSchedulerImpl::MainThreadSeemsUnresponsive(
+ base::TimeDelta main_thread_responsiveness_threshold) {
+ base::TimeTicks now = tick_clock()->NowTicks();
+ base::TimeDelta estimated_queueing_time;
+
+ bool can_read = false;
+
+ base::subtle::Atomic32 version;
+ seqlock_queueing_time_estimator_.seqlock.TryRead(&can_read, &version);
+
+ // If we fail to determine if the main thread is busy, assume whether or not
+ // it's busy hasn't change since the last time we asked.
+ if (!can_read)
+ return GetCompositorThreadOnly().main_thread_seems_unresponsive;
+
+ QueueingTimeEstimator::State queueing_time_estimator_state =
+ seqlock_queueing_time_estimator_.data.GetState();
+
+ // If we fail to determine if the main thread is busy, assume whether or not
+ // it's busy hasn't change since the last time we asked.
+ if (seqlock_queueing_time_estimator_.seqlock.ReadRetry(version))
+ return GetCompositorThreadOnly().main_thread_seems_unresponsive;
+
+ QueueingTimeEstimator queueing_time_estimator(queueing_time_estimator_state);
+
+ estimated_queueing_time =
+ queueing_time_estimator.EstimateQueueingTimeIncludingCurrentTask(now);
+
+ bool main_thread_seems_unresponsive =
+ estimated_queueing_time > main_thread_responsiveness_threshold;
+ GetCompositorThreadOnly().main_thread_seems_unresponsive =
+ main_thread_seems_unresponsive;
+
+ return main_thread_seems_unresponsive;
+}
+
+void MainThreadSchedulerImpl::SetRendererProcessType(RendererProcessType type) {
+ main_thread_only().process_type = type;
+}
+
+WebScopedVirtualTimePauser
+MainThreadSchedulerImpl::CreateWebScopedVirtualTimePauser(
+ const char* name,
+ WebScopedVirtualTimePauser::VirtualTaskDuration duration) {
+ return WebScopedVirtualTimePauser(this, duration,
+ WebString(WTF::String(name)));
+}
+
+void MainThreadSchedulerImpl::RegisterTimeDomain(TimeDomain* time_domain) {
+ helper_.RegisterTimeDomain(time_domain);
+}
+
+void MainThreadSchedulerImpl::UnregisterTimeDomain(TimeDomain* time_domain) {
+ helper_.UnregisterTimeDomain(time_domain);
+}
+
+const base::TickClock* MainThreadSchedulerImpl::tick_clock() const {
+ return helper_.GetClock();
+}
+
+void MainThreadSchedulerImpl::AddPageScheduler(
+ PageSchedulerImpl* page_scheduler) {
+ main_thread_only().page_schedulers.insert(page_scheduler);
+}
+
+void MainThreadSchedulerImpl::RemovePageScheduler(
+ PageSchedulerImpl* page_scheduler) {
+ DCHECK(main_thread_only().page_schedulers.find(page_scheduler) !=
+ main_thread_only().page_schedulers.end());
+ main_thread_only().page_schedulers.erase(page_scheduler);
+}
+
+void MainThreadSchedulerImpl::BroadcastIntervention(
+ const std::string& message) {
+ helper_.CheckOnValidThread();
+ for (auto* page_scheduler : main_thread_only().page_schedulers)
+ page_scheduler->ReportIntervention(message);
+}
+
+void MainThreadSchedulerImpl::OnTaskStarted(MainThreadTaskQueue* queue,
+ const TaskQueue::Task& task,
+ base::TimeTicks start) {
+ main_thread_only().current_task_start_time = start;
+ seqlock_queueing_time_estimator_.seqlock.WriteBegin();
+ seqlock_queueing_time_estimator_.data.OnTopLevelTaskStarted(start, queue);
+ seqlock_queueing_time_estimator_.seqlock.WriteEnd();
+ main_thread_only().task_description_for_tracing = TaskDescriptionForTracing{
+ static_cast<TaskType>(task.task_type()),
+ queue
+ ? base::Optional<MainThreadTaskQueue::QueueType>(queue->queue_type())
+ : base::nullopt};
+}
+
+void MainThreadSchedulerImpl::OnTaskCompleted(
+ MainThreadTaskQueue* queue,
+ const TaskQueue::Task& task,
+ base::TimeTicks start,
+ base::TimeTicks end,
+ base::Optional<base::TimeDelta> thread_time) {
+ DCHECK_LE(start, end);
+ seqlock_queueing_time_estimator_.seqlock.WriteBegin();
+ seqlock_queueing_time_estimator_.data.OnTopLevelTaskCompleted(end);
+ seqlock_queueing_time_estimator_.seqlock.WriteEnd();
+
+ if (queue)
+ task_queue_throttler()->OnTaskRunTimeReported(queue, start, end);
+
+ // TODO(altimin): Per-page metrics should also be considered.
+ main_thread_only().metrics_helper.RecordTaskMetrics(queue, task, start, end,
+ thread_time);
+ main_thread_only().task_description_for_tracing = base::nullopt;
+
+ RecordTaskUkm(queue, task, start, end, thread_time);
+}
+
+void MainThreadSchedulerImpl::RecordTaskUkm(
+ MainThreadTaskQueue* queue,
+ const TaskQueue::Task& task,
+ base::TimeTicks start,
+ base::TimeTicks end,
+ base::Optional<base::TimeDelta> thread_time) {
+ if (!ShouldRecordTaskUkm())
+ return;
+
+ if (queue && queue->GetFrameScheduler()) {
+ RecordTaskUkmImpl(queue, task, start, end, thread_time,
+ static_cast<PageSchedulerImpl*>(
+ queue->GetFrameScheduler()->GetPageScheduler()),
+ 1);
+ return;
+ }
+
+ for (PageSchedulerImpl* page_scheduler : main_thread_only().page_schedulers) {
+ RecordTaskUkmImpl(queue, task, start, end, thread_time, page_scheduler,
+ main_thread_only().page_schedulers.size());
+ }
+}
+
+void MainThreadSchedulerImpl::RecordTaskUkmImpl(
+ MainThreadTaskQueue* queue,
+ const TaskQueue::Task& task,
+ base::TimeTicks start,
+ base::TimeTicks end,
+ base::Optional<base::TimeDelta> thread_time,
+ PageSchedulerImpl* page_scheduler,
+ size_t page_schedulers_to_attribute) {
+ // Skip tasks which have deleted the page scheduler.
+ if (!page_scheduler)
+ return;
+
+ ukm::UkmRecorder* ukm_recorder = page_scheduler->GetUkmRecorder();
+ // OOPIFs are not supported.
+ if (!ukm_recorder)
+ return;
+
+ ukm::builders::RendererSchedulerTask builder(
+ page_scheduler->GetUkmSourceId());
+
+ builder.SetPageSchedulers(page_schedulers_to_attribute);
+ builder.SetRendererBackgrounded(main_thread_only().renderer_backgrounded);
+ builder.SetRendererHidden(main_thread_only().renderer_hidden);
+ builder.SetRendererAudible(main_thread_only().is_audio_playing);
+ builder.SetUseCase(
+ static_cast<int>(main_thread_only().current_use_case.get()));
+ builder.SetTaskType(task.task_type());
+ builder.SetQueueType(static_cast<int>(
+ queue ? queue->queue_type() : MainThreadTaskQueue::QueueType::kDetached));
+ builder.SetFrameStatus(static_cast<int>(
+ GetFrameStatus(queue ? queue->GetFrameScheduler() : nullptr)));
+ builder.SetTaskDuration((end - start).InMicroseconds());
+
+ if (thread_time) {
+ builder.SetTaskCPUDuration(thread_time->InMicroseconds());
+ }
+
+ builder.Record(ukm_recorder);
+}
+
+void MainThreadSchedulerImpl::OnBeginNestedRunLoop() {
+ seqlock_queueing_time_estimator_.seqlock.WriteBegin();
+ seqlock_queueing_time_estimator_.data.OnBeginNestedRunLoop();
+ seqlock_queueing_time_estimator_.seqlock.WriteEnd();
+
+ main_thread_only().nested_runloop = true;
+ ApplyVirtualTimePolicy();
+}
+
+void MainThreadSchedulerImpl::OnExitNestedRunLoop() {
+ main_thread_only().nested_runloop = false;
+ ApplyVirtualTimePolicy();
+}
+
+void MainThreadSchedulerImpl::AddTaskTimeObserver(
+ TaskTimeObserver* task_time_observer) {
+ helper_.AddTaskTimeObserver(task_time_observer);
+}
+
+void MainThreadSchedulerImpl::RemoveTaskTimeObserver(
+ TaskTimeObserver* task_time_observer) {
+ helper_.RemoveTaskTimeObserver(task_time_observer);
+}
+
+bool MainThreadSchedulerImpl::ContainsLocalMainFrame() {
+ for (auto* page_scheduler : main_thread_only().page_schedulers) {
+ if (page_scheduler->IsMainFrameLocal())
+ return true;
+ }
+ return false;
+}
+
+void MainThreadSchedulerImpl::OnQueueingTimeForWindowEstimated(
+ base::TimeDelta queueing_time,
+ bool is_disjoint_window) {
+ main_thread_only().most_recent_expected_queueing_time = queueing_time;
+
+ if (main_thread_only().has_navigated) {
+ if (main_thread_only().max_queueing_time < queueing_time) {
+ if (!main_thread_only().max_queueing_time_metric) {
+ main_thread_only().max_queueing_time_metric =
+ CreateMaxQueueingTimeMetric();
+ }
+ main_thread_only().max_queueing_time_metric->SetSample(
+ queueing_time.InMilliseconds());
+ main_thread_only().max_queueing_time = queueing_time;
+ }
+ }
+
+ if (!is_disjoint_window || !ContainsLocalMainFrame())
+ return;
+
+ UMA_HISTOGRAM_TIMES("RendererScheduler.ExpectedTaskQueueingDuration",
+ queueing_time);
+ UMA_HISTOGRAM_CUSTOM_COUNTS(
+ "RendererScheduler.ExpectedTaskQueueingDuration3",
+ queueing_time.InMicroseconds(), kMinExpectedQueueingTimeBucket,
+ kMaxExpectedQueueingTimeBucket, kNumberExpectedQueueingTimeBuckets);
+ TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "estimated_queueing_time_for_window",
+ queueing_time.InMillisecondsF());
+
+ if (::resource_coordinator::IsResourceCoordinatorEnabled()) {
+ RendererResourceCoordinator::Get().SetExpectedTaskQueueingDuration(
+ queueing_time);
+ }
+}
+
+void MainThreadSchedulerImpl::OnReportFineGrainedExpectedQueueingTime(
+ const char* split_description,
+ base::TimeDelta queueing_time) {
+ if (!ContainsLocalMainFrame())
+ return;
+
+ base::UmaHistogramCustomCounts(
+ split_description, queueing_time.InMicroseconds(),
+ kMinExpectedQueueingTimeBucket, kMaxExpectedQueueingTimeBucket,
+ kNumberExpectedQueueingTimeBuckets);
+}
+
+AutoAdvancingVirtualTimeDomain*
+MainThreadSchedulerImpl::GetVirtualTimeDomain() {
+ return virtual_time_domain_.get();
+}
+
+bool MainThreadSchedulerImpl::ShouldDisableThrottlingBecauseOfAudio(
+ base::TimeTicks now) {
+ if (!main_thread_only().last_audio_state_change)
+ return false;
+
+ if (main_thread_only().is_audio_playing)
+ return true;
+
+ return main_thread_only().last_audio_state_change.value() +
+ kThrottlingDelayAfterAudioIsPlayed >
+ now;
+}
+
+void MainThreadSchedulerImpl::AddQueueToWakeUpBudgetPool(
+ MainThreadTaskQueue* queue) {
+ if (!main_thread_only().wake_up_budget_pool) {
+ main_thread_only().wake_up_budget_pool =
+ task_queue_throttler()->CreateWakeUpBudgetPool("renderer_wake_up_pool");
+ main_thread_only().wake_up_budget_pool->SetWakeUpRate(1);
+ main_thread_only().wake_up_budget_pool->SetWakeUpDuration(
+ GetWakeUpDuration());
+ }
+ main_thread_only().wake_up_budget_pool->AddQueue(tick_clock()->NowTicks(),
+ queue);
+}
+
+TimeDomain* MainThreadSchedulerImpl::GetActiveTimeDomain() {
+ if (main_thread_only().use_virtual_time) {
+ return GetVirtualTimeDomain();
+ } else {
+ return real_time_domain();
+ }
+}
+
+void MainThreadSchedulerImpl::OnTraceLogEnabled() {
+ CreateTraceEventObjectSnapshot();
+ tracing_controller_.OnTraceLogEnabled();
+ for (PageSchedulerImpl* page_scheduler : main_thread_only().page_schedulers) {
+ page_scheduler->OnTraceLogEnabled();
+ }
+}
+
+void MainThreadSchedulerImpl::OnTraceLogDisabled() {}
+
+base::WeakPtr<MainThreadSchedulerImpl> MainThreadSchedulerImpl::GetWeakPtr() {
+ return weak_factory_.GetWeakPtr();
+}
+
+bool MainThreadSchedulerImpl::ShouldRecordTaskUkm() {
+ // This function returns true with probability of kSamplingRateForTaskUkm.
+ return main_thread_only().uniform_distribution(
+ main_thread_only().random_generator) < kSamplingRateForTaskUkm;
+}
+
+// static
+const char* MainThreadSchedulerImpl::UseCaseToString(UseCase use_case) {
+ switch (use_case) {
+ case UseCase::kNone:
+ return "none";
+ case UseCase::kCompositorGesture:
+ return "compositor_gesture";
+ case UseCase::kMainThreadCustomInputHandling:
+ return "main_thread_custom_input_handling";
+ case UseCase::kSynchronizedGesture:
+ return "synchronized_gesture";
+ case UseCase::kTouchstart:
+ return "touchstart";
+ case UseCase::kLoading:
+ return "loading";
+ case UseCase::kMainThreadGesture:
+ return "main_thread_gesture";
+ default:
+ NOTREACHED();
+ return nullptr;
+ }
+}
+
+// static
+const char* MainThreadSchedulerImpl::RAILModeToString(v8::RAILMode rail_mode) {
+ switch (rail_mode) {
+ case v8::PERFORMANCE_RESPONSE:
+ return "response";
+ case v8::PERFORMANCE_ANIMATION:
+ return "animation";
+ case v8::PERFORMANCE_IDLE:
+ return "idle";
+ case v8::PERFORMANCE_LOAD:
+ return "load";
+ default:
+ NOTREACHED();
+ return nullptr;
+ }
+}
+
+// static
+const char* MainThreadSchedulerImpl::TimeDomainTypeToString(
+ TimeDomainType domain_type) {
+ switch (domain_type) {
+ case TimeDomainType::kReal:
+ return "real";
+ case TimeDomainType::kThrottled:
+ return "throttled";
+ case TimeDomainType::kVirtual:
+ return "virtual";
+ default:
+ NOTREACHED();
+ return nullptr;
+ }
+}
+
+// static
+const char* MainThreadSchedulerImpl::VirtualTimePolicyToString(
+ VirtualTimePolicy virtual_time_policy) {
+ switch (virtual_time_policy) {
+ case VirtualTimePolicy::kAdvance:
+ return "ADVANCE";
+ case VirtualTimePolicy::kPause:
+ return "PAUSE";
+ case VirtualTimePolicy::kDeterministicLoading:
+ return "DETERMINISTIC_LOADING";
+ default:
+ NOTREACHED();
+ return nullptr;
+ }
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
new file mode 100644
index 00000000000..34b1aa1e53c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
@@ -0,0 +1,832 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_MAIN_THREAD_SCHEDULER_IMPL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_MAIN_THREAD_SCHEDULER_IMPL_H_
+
+#include <random>
+
+#include "base/atomicops.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/metrics/single_sample_metrics.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/lock.h"
+#include "base/trace_event/trace_log.h"
+#include "build/build_config.h"
+#include "device/base/synchronization/shared_memory_seqlock_buffer.h"
+#include "third_party/blink/public/platform/scheduler/web_main_thread_scheduler.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_time_observer.h"
+#include "third_party/blink/renderer/platform/scheduler/child/idle_canceled_delayed_task_sweeper.h"
+#include "third_party/blink/renderer/platform/scheduler/child/idle_helper.h"
+#include "third_party/blink/renderer/platform/scheduler/child/pollable_thread_safe_flag.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain.h"
+#include "third_party/blink/renderer/platform/scheduler/renderer/deadline_task_runner.h"
+#include "third_party/blink/renderer/platform/scheduler/renderer/idle_time_estimator.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_helper.h"
+#include "third_party/blink/renderer/platform/scheduler/renderer/main_thread_task_queue.h"
+#include "third_party/blink/renderer/platform/scheduler/renderer/queueing_time_estimator.h"
+#include "third_party/blink/renderer/platform/scheduler/renderer/render_widget_signals.h"
+#include "third_party/blink/renderer/platform/scheduler/renderer/renderer_metrics_helper.h"
+#include "third_party/blink/renderer/platform/scheduler/renderer/task_cost_estimator.h"
+#include "third_party/blink/renderer/platform/scheduler/renderer/use_case.h"
+#include "third_party/blink/renderer/platform/scheduler/renderer/user_model.h"
+#include "third_party/blink/renderer/platform/scheduler/util/tracing_helper.h"
+
+namespace base {
+namespace trace_event {
+class ConvertableToTraceFormat;
+}
+} // namespace base
+
+namespace blink {
+namespace scheduler {
+namespace main_thread_scheduler_impl_unittest {
+class MainThreadSchedulerImplForTest;
+class MainThreadSchedulerImplTest;
+FORWARD_DECLARE_TEST(MainThreadSchedulerImplTest, Tracing);
+} // namespace main_thread_scheduler_impl_unittest
+class PageSchedulerImpl;
+class TaskQueueThrottler;
+class WebRenderWidgetSchedulingState;
+
+class PLATFORM_EXPORT MainThreadSchedulerImpl
+ : public WebMainThreadScheduler,
+ public IdleHelper::Delegate,
+ public MainThreadSchedulerHelper::Observer,
+ public RenderWidgetSignals::Observer,
+ public QueueingTimeEstimator::Client,
+ public base::trace_event::TraceLog::AsyncEnabledStateObserver,
+ public AutoAdvancingVirtualTimeDomain::Observer {
+ public:
+ // Don't use except for tracing.
+ struct TaskDescriptionForTracing {
+ TaskType task_type;
+ base::Optional<MainThreadTaskQueue::QueueType> queue_type;
+
+ // Required in order to wrap in TraceableState.
+ constexpr bool operator!=(const TaskDescriptionForTracing& rhs) const {
+ return task_type != rhs.task_type || queue_type != rhs.queue_type;
+ }
+ };
+
+ static const char* UseCaseToString(UseCase use_case);
+ static const char* RAILModeToString(v8::RAILMode rail_mode);
+ static const char* VirtualTimePolicyToString(
+ PageScheduler::VirtualTimePolicy);
+ // The lowest bucket for fine-grained Expected Queueing Time reporting.
+ static const int kMinExpectedQueueingTimeBucket = 1;
+ // The highest bucket for fine-grained Expected Queueing Time reporting, in
+ // microseconds.
+ static const int kMaxExpectedQueueingTimeBucket = 30 * 1000 * 1000;
+ // The number of buckets for fine-grained Expected Queueing Time reporting.
+ static const int kNumberExpectedQueueingTimeBuckets = 50;
+
+ // If |initial_virtual_time| is specified then the scheduler will be created
+ // with virtual time enabled and paused with base::Time will be overridden to
+ // start at |initial_virtual_time|.
+ MainThreadSchedulerImpl(std::unique_ptr<TaskQueueManager> task_queue_manager,
+ base::Optional<base::Time> initial_virtual_time);
+
+ ~MainThreadSchedulerImpl() override;
+
+ // WebMainThreadSchedulerScheduler implementation:
+ std::unique_ptr<WebThread> CreateMainThread() override;
+ scoped_refptr<SingleThreadIdleTaskRunner> IdleTaskRunner() override;
+ scoped_refptr<base::SingleThreadTaskRunner> IPCTaskRunner() override;
+ std::unique_ptr<WebRenderWidgetSchedulingState>
+ NewRenderWidgetSchedulingState() override;
+ void WillBeginFrame(const viz::BeginFrameArgs& args) override;
+ void BeginFrameNotExpectedSoon() override;
+ void BeginMainFrameNotExpectedUntil(base::TimeTicks time) override;
+ void DidCommitFrameToCompositor() override;
+ void DidHandleInputEventOnCompositorThread(
+ const WebInputEvent& web_input_event,
+ InputEventState event_state) override;
+ void DidHandleInputEventOnMainThread(const WebInputEvent& web_input_event,
+ WebInputEventResult result) override;
+ base::TimeDelta MostRecentExpectedQueueingTime() override;
+ void DidAnimateForInputOnCompositorThread() override;
+ void SetRendererHidden(bool hidden) override;
+ void SetRendererBackgrounded(bool backgrounded) override;
+ void SetSchedulerKeepActive(bool keep_active) override;
+ bool SchedulerKeepActive();
+#if defined(OS_ANDROID)
+ void PauseTimersForAndroidWebView();
+ void ResumeTimersForAndroidWebView();
+#endif
+ std::unique_ptr<RendererPauseHandle> PauseRenderer() override
+ WARN_UNUSED_RESULT;
+ void AddPendingNavigation(NavigatingFrameType type) override;
+ void RemovePendingNavigation(NavigatingFrameType type) override;
+ bool IsHighPriorityWorkAnticipated() override;
+ bool ShouldYieldForHighPriorityWork() override;
+ bool CanExceedIdleDeadlineIfRequired() const override;
+ void AddTaskObserver(base::MessageLoop::TaskObserver* task_observer) override;
+ void RemoveTaskObserver(
+ base::MessageLoop::TaskObserver* task_observer) override;
+ void Shutdown() override;
+ void SetStoppingWhenBackgroundedEnabled(bool enabled) override;
+ void SetTopLevelBlameContext(
+ base::trace_event::BlameContext* blame_context) override;
+ void SetRAILModeObserver(RAILModeObserver* observer) override;
+ bool MainThreadSeemsUnresponsive(
+ base::TimeDelta main_thread_responsiveness_threshold) override;
+ void SetRendererProcessType(RendererProcessType type) override;
+ WebScopedVirtualTimePauser CreateWebScopedVirtualTimePauser(
+ const char* name,
+ WebScopedVirtualTimePauser::VirtualTaskDuration duration) override;
+
+ // AutoAdvancingVirtualTimeDomain::Observer implementation:
+ void OnVirtualTimeAdvanced() override;
+
+ // RenderWidgetSignals::Observer implementation:
+ void SetAllRenderWidgetsHidden(bool hidden) override;
+ void SetHasVisibleRenderWidgetWithTouchHandler(
+ bool has_visible_render_widget_with_touch_handler) override;
+
+ // SchedulerHelper::Observer implementation:
+ void OnBeginNestedRunLoop() override;
+ void OnExitNestedRunLoop() override;
+
+ // QueueingTimeEstimator::Client implementation:
+ void OnQueueingTimeForWindowEstimated(base::TimeDelta queueing_time,
+ bool is_disjoint_window) override;
+ void OnReportFineGrainedExpectedQueueingTime(
+ const char* split_description,
+ base::TimeDelta queueing_time) override;
+
+ scoped_refptr<MainThreadTaskQueue> DefaultTaskQueue();
+ scoped_refptr<MainThreadTaskQueue> CompositorTaskQueue();
+ scoped_refptr<MainThreadTaskQueue> InputTaskQueue();
+ scoped_refptr<MainThreadTaskQueue> V8TaskQueue();
+
+ // Returns a new task queue created with given params.
+ scoped_refptr<MainThreadTaskQueue> NewTaskQueue(
+ const MainThreadTaskQueue::QueueCreationParams& params);
+
+ // Returns a new loading task queue. This queue is intended for tasks related
+ // to resource dispatch, foreground HTML parsing, etc...
+ // Note: Tasks posted to kFrameLoadingControl queues must execute quickly.
+ scoped_refptr<MainThreadTaskQueue> NewLoadingTaskQueue(
+ MainThreadTaskQueue::QueueType queue_type);
+
+ // Returns a new timer task queue. This queue is intended for DOM Timers.
+ scoped_refptr<MainThreadTaskQueue> NewTimerTaskQueue(
+ MainThreadTaskQueue::QueueType queue_type);
+
+ // Returns a task queue where tasks run at the highest possible priority.
+ scoped_refptr<MainThreadTaskQueue> ControlTaskQueue();
+
+ // A control task queue which also respects virtual time. Only available if
+ // virtual time has been enabled.
+ scoped_refptr<MainThreadTaskQueue> VirtualTimeControlTaskQueue();
+
+ void RegisterTimeDomain(TimeDomain* time_domain);
+ void UnregisterTimeDomain(TimeDomain* time_domain);
+
+ using VirtualTimePolicy = PageScheduler::VirtualTimePolicy;
+ using VirtualTimeObserver = PageScheduler::VirtualTimeObserver;
+
+ using BaseTimeOverridePolicy =
+ AutoAdvancingVirtualTimeDomain::BaseTimeOverridePolicy;
+
+ // Tells the scheduler that all TaskQueues should use virtual time. Returns
+ // the TimeTicks that virtual time offsets will be relative to.
+ base::TimeTicks EnableVirtualTime(BaseTimeOverridePolicy policy);
+ bool IsVirtualTimeEnabled() const;
+
+ // Migrates all task queues to real time.
+ void DisableVirtualTimeForTesting();
+
+ // Returns true if virtual time is not paused.
+ bool VirtualTimeAllowedToAdvance() const;
+ void SetVirtualTimePolicy(VirtualTimePolicy virtual_time_policy);
+ void SetInitialVirtualTimeOffset(base::TimeDelta offset);
+ void SetMaxVirtualTimeTaskStarvationCount(int max_task_starvation_count);
+ void AddVirtualTimeObserver(VirtualTimeObserver*);
+ void RemoveVirtualTimeObserver(VirtualTimeObserver*);
+ base::TimeTicks IncrementVirtualTimePauseCount();
+ void DecrementVirtualTimePauseCount();
+ void MaybeAdvanceVirtualTime(base::TimeTicks new_virtual_time);
+
+ void AddPageScheduler(PageSchedulerImpl*);
+ void RemovePageScheduler(PageSchedulerImpl*);
+
+ void AddTaskTimeObserver(TaskTimeObserver*);
+ void RemoveTaskTimeObserver(TaskTimeObserver*);
+
+ // Snapshots this MainThreadSchedulerImpl for tracing.
+ void CreateTraceEventObjectSnapshot() const;
+
+ // Called when one of associated page schedulers has changed audio state.
+ void OnAudioStateChanged();
+
+ // Tells the scheduler that a provisional load has committed. Must be called
+ // from the main thread.
+ void DidStartProvisionalLoad(bool is_main_frame);
+
+ // Tells the scheduler that a provisional load has committed. The scheduler
+ // may reset the task cost estimators and the UserModel. Must be called from
+ // the main thread.
+ void DidCommitProvisionalLoad(bool is_web_history_inert_commit,
+ bool is_reload,
+ bool is_main_frame);
+
+ // Test helpers.
+ MainThreadSchedulerHelper* GetSchedulerHelperForTesting();
+ TaskCostEstimator* GetLoadingTaskCostEstimatorForTesting();
+ TaskCostEstimator* GetTimerTaskCostEstimatorForTesting();
+ IdleTimeEstimator* GetIdleTimeEstimatorForTesting();
+ base::TimeTicks CurrentIdleTaskDeadlineForTesting() const;
+ void RunIdleTasksForTesting(const base::Closure& callback);
+ void EndIdlePeriodForTesting(const base::Closure& callback,
+ base::TimeTicks time_remaining);
+ bool PolicyNeedsUpdateForTesting();
+ WakeUpBudgetPool* GetWakeUpBudgetPoolForTesting();
+
+ const base::TickClock* tick_clock() const;
+
+ RealTimeDomain* real_time_domain() const {
+ return helper_.real_time_domain();
+ }
+
+ AutoAdvancingVirtualTimeDomain* GetVirtualTimeDomain();
+
+ TimeDomain* GetActiveTimeDomain();
+
+ TaskQueueThrottler* task_queue_throttler() const {
+ return task_queue_throttler_.get();
+ }
+
+ void OnFirstMeaningfulPaint();
+
+ void OnShutdownTaskQueue(const scoped_refptr<MainThreadTaskQueue>& queue);
+
+ void OnTaskStarted(MainThreadTaskQueue* queue,
+ const TaskQueue::Task& task,
+ base::TimeTicks start);
+
+ void OnTaskCompleted(MainThreadTaskQueue* queue,
+ const TaskQueue::Task& task,
+ base::TimeTicks start,
+ base::TimeTicks end,
+ base::Optional<base::TimeDelta> thread_time);
+
+ // base::trace_event::TraceLog::EnabledStateObserver implementation:
+ void OnTraceLogEnabled() override;
+ void OnTraceLogDisabled() override;
+
+ base::WeakPtr<MainThreadSchedulerImpl> GetWeakPtr();
+
+ protected:
+ // WebMainThreadScheduler implementation.
+ // Use *TaskQueue internally.
+ scoped_refptr<base::SingleThreadTaskRunner> DefaultTaskRunner() override;
+ scoped_refptr<base::SingleThreadTaskRunner> CompositorTaskRunner() override;
+ scoped_refptr<base::SingleThreadTaskRunner> InputTaskRunner() override;
+
+ // `current_use_case` will be overwritten by the next call to UpdatePolicy.
+ // Thus, this function should be only used for testing purposes.
+ void SetCurrentUseCaseForTest(UseCase use_case) {
+ main_thread_only().current_use_case = use_case;
+ }
+
+ private:
+ friend class WebRenderWidgetSchedulingState;
+ friend class RendererMetricsHelper;
+
+ friend class RendererMetricsHelperTest;
+ friend class main_thread_scheduler_impl_unittest::
+ MainThreadSchedulerImplForTest;
+ friend class main_thread_scheduler_impl_unittest::MainThreadSchedulerImplTest;
+ FRIEND_TEST_ALL_PREFIXES(
+ main_thread_scheduler_impl_unittest::MainThreadSchedulerImplTest,
+ Tracing);
+
+ enum class ExpensiveTaskPolicy { kRun, kBlock, kThrottle };
+
+ enum class TimeDomainType {
+ kReal,
+ kThrottled,
+ kVirtual,
+ };
+
+ static const char* TimeDomainTypeToString(TimeDomainType domain_type);
+
+ void SetStoppedInBackground(bool) const;
+
+ bool ContainsLocalMainFrame();
+
+ struct TaskQueuePolicy {
+ // Default constructor of TaskQueuePolicy should match behaviour of a
+ // newly-created task queue.
+ TaskQueuePolicy()
+ : is_enabled(true),
+ is_paused(false),
+ is_throttled(false),
+ is_blocked(false),
+ is_stopped(false),
+ use_virtual_time(false),
+ priority(TaskQueue::kNormalPriority) {}
+
+ bool is_enabled;
+ bool is_paused;
+ bool is_throttled;
+ bool is_blocked;
+ bool is_stopped;
+ bool use_virtual_time;
+ TaskQueue::QueuePriority priority;
+
+ bool IsQueueEnabled(MainThreadTaskQueue* task_queue) const;
+
+ TaskQueue::QueuePriority GetPriority(MainThreadTaskQueue* task_queue) const;
+
+ TimeDomainType GetTimeDomainType(MainThreadTaskQueue* task_queue) const;
+
+ bool operator==(const TaskQueuePolicy& other) const {
+ return is_enabled == other.is_enabled && is_paused == other.is_paused &&
+ is_throttled == other.is_throttled &&
+ is_blocked == other.is_blocked && is_stopped == other.is_stopped &&
+ use_virtual_time == other.use_virtual_time &&
+ priority == other.priority;
+ }
+
+ void AsValueInto(base::trace_event::TracedValue* state) const;
+ };
+
+ class Policy {
+ public:
+ Policy()
+ : rail_mode_(v8::PERFORMANCE_ANIMATION),
+ should_disable_throttling_(false) {}
+ ~Policy() = default;
+
+ TaskQueuePolicy& compositor_queue_policy() {
+ return policies_[static_cast<size_t>(
+ MainThreadTaskQueue::QueueClass::kCompositor)];
+ }
+ const TaskQueuePolicy& compositor_queue_policy() const {
+ return policies_[static_cast<size_t>(
+ MainThreadTaskQueue::QueueClass::kCompositor)];
+ }
+
+ TaskQueuePolicy& loading_queue_policy() {
+ return policies_[static_cast<size_t>(
+ MainThreadTaskQueue::QueueClass::kLoading)];
+ }
+ const TaskQueuePolicy& loading_queue_policy() const {
+ return policies_[static_cast<size_t>(
+ MainThreadTaskQueue::QueueClass::kLoading)];
+ }
+
+ TaskQueuePolicy& timer_queue_policy() {
+ return policies_[static_cast<size_t>(
+ MainThreadTaskQueue::QueueClass::kTimer)];
+ }
+ const TaskQueuePolicy& timer_queue_policy() const {
+ return policies_[static_cast<size_t>(
+ MainThreadTaskQueue::QueueClass::kTimer)];
+ }
+
+ TaskQueuePolicy& default_queue_policy() {
+ return policies_[static_cast<size_t>(
+ MainThreadTaskQueue::QueueClass::kNone)];
+ }
+ const TaskQueuePolicy& default_queue_policy() const {
+ return policies_[static_cast<size_t>(
+ MainThreadTaskQueue::QueueClass::kNone)];
+ }
+
+ const TaskQueuePolicy& GetQueuePolicy(
+ MainThreadTaskQueue::QueueClass queue_class) const {
+ return policies_[static_cast<size_t>(queue_class)];
+ }
+
+ v8::RAILMode& rail_mode() { return rail_mode_; }
+ v8::RAILMode rail_mode() const { return rail_mode_; }
+
+ bool& should_disable_throttling() { return should_disable_throttling_; }
+ bool should_disable_throttling() const {
+ return should_disable_throttling_;
+ }
+
+ bool operator==(const Policy& other) const {
+ return policies_ == other.policies_ && rail_mode_ == other.rail_mode_ &&
+ should_disable_throttling_ == other.should_disable_throttling_;
+ }
+
+ void AsValueInto(base::trace_event::TracedValue* state) const;
+
+ private:
+ v8::RAILMode rail_mode_;
+ bool should_disable_throttling_;
+
+ std::array<TaskQueuePolicy,
+ static_cast<size_t>(MainThreadTaskQueue::QueueClass::kCount)>
+ policies_;
+ };
+
+ class PollableNeedsUpdateFlag {
+ public:
+ PollableNeedsUpdateFlag(base::Lock* write_lock);
+ ~PollableNeedsUpdateFlag();
+
+ // Set the flag. May only be called if |write_lock| is held.
+ void SetWhileLocked(bool value);
+
+ // Returns true iff the flag is set to true.
+ bool IsSet() const;
+
+ private:
+ base::subtle::Atomic32 flag_;
+ base::Lock* write_lock_; // Not owned.
+
+ DISALLOW_COPY_AND_ASSIGN(PollableNeedsUpdateFlag);
+ };
+
+ class TaskDurationMetricTracker;
+
+ class RendererPauseHandleImpl : public RendererPauseHandle {
+ public:
+ explicit RendererPauseHandleImpl(MainThreadSchedulerImpl* scheduler);
+ ~RendererPauseHandleImpl() override;
+
+ private:
+ MainThreadSchedulerImpl* scheduler_; // NOT OWNED
+ };
+
+ // IdleHelper::Delegate implementation:
+ bool CanEnterLongIdlePeriod(
+ base::TimeTicks now,
+ base::TimeDelta* next_long_idle_period_delay_out) override;
+ void IsNotQuiescent() override {}
+ void OnIdlePeriodStarted() override;
+ void OnIdlePeriodEnded() override;
+
+ void OnPendingTasksChanged(bool has_tasks) override;
+ void DispatchRequestBeginMainFrameNotExpected(bool has_tasks);
+
+ void EndIdlePeriod();
+
+ // Returns the serialized scheduler state for tracing.
+ std::unique_ptr<base::trace_event::ConvertableToTraceFormat> AsValue(
+ base::TimeTicks optional_now) const;
+ std::unique_ptr<base::trace_event::ConvertableToTraceFormat> AsValueLocked(
+ base::TimeTicks optional_now) const;
+ void CreateTraceEventObjectSnapshotLocked() const;
+
+ static bool ShouldPrioritizeInputEvent(const WebInputEvent& web_input_event);
+
+ // The amount of time which idle periods can continue being scheduled when the
+ // renderer has been hidden, before going to sleep for good.
+ static const int kEndIdleWhenHiddenDelayMillis = 10000;
+
+ // The amount of time in milliseconds we have to respond to user input as
+ // defined by RAILS.
+ static const int kRailsResponseTimeMillis = 50;
+
+ // The amount of time to wait before suspending shared timers, and loading
+ // etc. after the renderer has been backgrounded. This is used only if
+ // background suspension is enabled.
+ static const int kDelayForBackgroundTabStoppingMillis = 5 * 60 * 1000;
+
+ // The time we should stay in a priority-escalated mode after a call to
+ // DidAnimateForInputOnCompositorThread().
+ static const int kFlingEscalationLimitMillis = 100;
+
+ // Schedules an immediate PolicyUpdate, if there isn't one already pending and
+ // sets |policy_may_need_update_|. Note |any_thread_lock_| must be
+ // locked.
+ void EnsureUrgentPolicyUpdatePostedOnMainThread(
+ const base::Location& from_here);
+
+ // Update the policy if a new signal has arrived. Must be called from the main
+ // thread.
+ void MaybeUpdatePolicy();
+
+ // Locks |any_thread_lock_| and updates the scheduler policy. May early
+ // out if the policy is unchanged. Must be called from the main thread.
+ void UpdatePolicy();
+
+ // Like UpdatePolicy, except it doesn't early out.
+ void ForceUpdatePolicy();
+
+ enum class UpdateType {
+ kMayEarlyOutIfPolicyUnchanged,
+ kForceUpdate,
+ };
+
+ // The implelemtation of UpdatePolicy & ForceUpdatePolicy. It is allowed to
+ // early out if |update_type| is kMayEarlyOutIfPolicyUnchanged.
+ virtual void UpdatePolicyLocked(UpdateType update_type);
+
+ // Helper for computing the use case. |expected_usecase_duration| will be
+ // filled with the amount of time after which the use case should be updated
+ // again. If the duration is zero, a new use case update should not be
+ // scheduled. Must be called with |any_thread_lock_| held. Can be called from
+ // any thread.
+ UseCase ComputeCurrentUseCase(
+ base::TimeTicks now,
+ base::TimeDelta* expected_use_case_duration) const;
+
+ std::unique_ptr<base::SingleSampleMetric> CreateMaxQueueingTimeMetric();
+
+ // An input event of some sort happened, the policy may need updating.
+ void UpdateForInputEventOnCompositorThread(WebInputEvent::Type type,
+ InputEventState input_event_state);
+
+ // The task cost estimators and the UserModel need to be reset upon page
+ // nagigation. This function does that. Must be called from the main thread.
+ void ResetForNavigationLocked();
+
+ // Estimates the maximum task length that won't cause a jank based on the
+ // current system state. Must be called from the main thread.
+ base::TimeDelta EstimateLongestJankFreeTaskDuration() const;
+
+ // Report an intervention to all WebViews in this process.
+ void BroadcastIntervention(const std::string& message);
+
+ void ApplyTaskQueuePolicy(
+ MainThreadTaskQueue* task_queue,
+ TaskQueue::QueueEnabledVoter* task_queue_enabled_voter,
+ const TaskQueuePolicy& old_task_queue_policy,
+ const TaskQueuePolicy& new_task_queue_policy) const;
+
+ static const char* ExpensiveTaskPolicyToString(
+ ExpensiveTaskPolicy expensive_task_policy);
+
+ bool ShouldDisableThrottlingBecauseOfAudio(base::TimeTicks now);
+
+ void AddQueueToWakeUpBudgetPool(MainThreadTaskQueue* queue);
+
+ void PauseRendererImpl();
+ void ResumeRendererImpl();
+
+ void NotifyVirtualTimePaused();
+ void SetVirtualTimeStopped(bool virtual_time_stopped);
+ void ApplyVirtualTimePolicy();
+
+ // Pauses the timer queues by inserting a fence that blocks any tasks posted
+ // after this point from running. Orthogonal to PauseTimerQueue. Care must
+ // be taken when using this API to avoid fighting with the TaskQueueThrottler.
+ void VirtualTimePaused();
+
+ // Removes the fence added by VirtualTimePaused allowing timers to execute
+ // normally. Care must be taken when using this API to avoid fighting with the
+ // TaskQueueThrottler.
+ void VirtualTimeResumed();
+
+ bool ShouldRecordTaskUkm();
+
+ // Probabilistically record all task metadata for the current task.
+ // If task belongs to a per-frame queue, this task is attributed to
+ // a particular Page, otherwise it's attributed to all Pages in the process.
+ void RecordTaskUkm(MainThreadTaskQueue* queue,
+ const TaskQueue::Task& task,
+ base::TimeTicks start,
+ base::TimeTicks end,
+ base::Optional<base::TimeDelta> thread_time);
+
+ void RecordTaskUkmImpl(MainThreadTaskQueue* queue,
+ const TaskQueue::Task& task,
+ base::TimeTicks start,
+ base::TimeTicks end,
+ base::Optional<base::TimeDelta> thread_time,
+ PageSchedulerImpl* page_scheduler,
+ size_t page_schedulers_to_attribute);
+
+ // Indicates that scheduler has been shutdown.
+ // It should be accessed only on the main thread, but couldn't be a member
+ // of MainThreadOnly struct because last might be destructed before we
+ // have to check this flag during scheduler's destruction.
+ bool was_shutdown_ = false;
+
+ // This controller should be initialized before any TraceableVariables
+ // because they require one to initialize themselves.
+ TraceableVariableController tracing_controller_;
+
+ MainThreadSchedulerHelper helper_;
+ IdleHelper idle_helper_;
+ IdleCanceledDelayedTaskSweeper idle_canceled_delayed_task_sweeper_;
+ std::unique_ptr<TaskQueueThrottler> task_queue_throttler_;
+ RenderWidgetSignals render_widget_scheduler_signals_;
+
+ const scoped_refptr<MainThreadTaskQueue> control_task_queue_;
+ const scoped_refptr<MainThreadTaskQueue> compositor_task_queue_;
+ const scoped_refptr<MainThreadTaskQueue> input_task_queue_;
+ scoped_refptr<MainThreadTaskQueue> virtual_time_control_task_queue_;
+ std::unique_ptr<TaskQueue::QueueEnabledVoter>
+ compositor_task_queue_enabled_voter_;
+ std::unique_ptr<TaskQueue::QueueEnabledVoter> input_task_queue_enabled_voter_;
+
+ using TaskQueueVoterMap =
+ std::map<scoped_refptr<MainThreadTaskQueue>,
+ std::unique_ptr<TaskQueue::QueueEnabledVoter>>;
+
+ TaskQueueVoterMap task_runners_;
+
+ scoped_refptr<MainThreadTaskQueue> v8_task_queue_;
+ scoped_refptr<MainThreadTaskQueue> ipc_task_queue_;
+
+ // Note |virtual_time_domain_| is lazily created.
+ std::unique_ptr<AutoAdvancingVirtualTimeDomain> virtual_time_domain_;
+
+ base::Closure update_policy_closure_;
+ DeadlineTaskRunner delayed_update_policy_runner_;
+ CancelableClosureHolder end_renderer_hidden_idle_period_closure_;
+
+ using SeqLockQueueingTimeEstimator =
+ device::SharedMemorySeqLockBuffer<QueueingTimeEstimator>;
+
+ SeqLockQueueingTimeEstimator seqlock_queueing_time_estimator_;
+
+ base::TimeDelta delay_for_background_tab_stopping_;
+
+ // We have decided to improve thread safety at the cost of some boilerplate
+ // (the accessors) for the following data members.
+
+ struct MainThreadOnly {
+ MainThreadOnly(
+ MainThreadSchedulerImpl* main_thread_scheduler_impl,
+ const scoped_refptr<MainThreadTaskQueue>& compositor_task_runner,
+ const base::TickClock* time_source,
+ base::TimeTicks now);
+ ~MainThreadOnly();
+
+ TaskCostEstimator loading_task_cost_estimator;
+ TaskCostEstimator timer_task_cost_estimator;
+ IdleTimeEstimator idle_time_estimator;
+ TraceableState<UseCase, kTracingCategoryNameDefault> current_use_case;
+ Policy current_policy;
+ base::TimeTicks current_policy_expiration_time;
+ base::TimeTicks estimated_next_frame_begin;
+ base::TimeTicks current_task_start_time;
+ base::TimeDelta most_recent_expected_queueing_time;
+ base::TimeDelta compositor_frame_interval;
+ TraceableCounter<base::TimeDelta, kTracingCategoryNameDebug>
+ longest_jank_free_task_duration;
+ base::Optional<base::TimeTicks> last_audio_state_change;
+ TraceableCounter<int, kTracingCategoryNameInfo>
+ renderer_pause_count; // Renderer is paused if non-zero.
+ TraceableCounter<int, kTracingCategoryNameDebug>
+ navigation_task_expected_count;
+ TraceableState<ExpensiveTaskPolicy, kTracingCategoryNameInfo>
+ expensive_task_policy;
+ TraceableState<v8::RAILMode, kTracingCategoryNameInfo>
+ rail_mode_for_tracing; // Don't use except for tracing.
+ TraceableState<bool, kTracingCategoryNameDebug> renderer_hidden;
+ TraceableState<bool, kTracingCategoryNameTopLevel> renderer_backgrounded;
+ TraceableState<bool, kTracingCategoryNameDefault>
+ keep_active_fetch_or_worker;
+ TraceableState<bool, kTracingCategoryNameInfo>
+ stopping_when_backgrounded_enabled;
+ TraceableState<bool, kTracingCategoryNameInfo> stopped_when_backgrounded;
+ TraceableCounter<base::TimeDelta, kTracingCategoryNameInfo>
+ loading_task_estimated_cost;
+ TraceableCounter<base::TimeDelta, kTracingCategoryNameInfo>
+ timer_task_estimated_cost;
+ TraceableState<bool, kTracingCategoryNameInfo> loading_tasks_seem_expensive;
+ TraceableState<bool, kTracingCategoryNameInfo> timer_tasks_seem_expensive;
+ TraceableState<bool, kTracingCategoryNameDefault> touchstart_expected_soon;
+ TraceableState<bool, kTracingCategoryNameDebug>
+ have_seen_a_begin_main_frame;
+ TraceableState<bool, kTracingCategoryNameDebug>
+ have_reported_blocking_intervention_in_current_policy;
+ TraceableState<bool, kTracingCategoryNameDebug>
+ have_reported_blocking_intervention_since_navigation;
+ TraceableState<bool, kTracingCategoryNameDebug>
+ has_visible_render_widget_with_touch_handler;
+ TraceableState<bool, kTracingCategoryNameDebug>
+ begin_frame_not_expected_soon;
+ TraceableState<bool, kTracingCategoryNameDebug> in_idle_period_for_testing;
+ TraceableState<bool, kTracingCategoryNameInfo> use_virtual_time;
+ TraceableState<bool, kTracingCategoryNameTopLevel> is_audio_playing;
+ TraceableState<bool, kTracingCategoryNameDebug>
+ compositor_will_send_main_frame_not_expected;
+ TraceableState<bool, kTracingCategoryNameDebug> has_navigated;
+ TraceableState<bool, kTracingCategoryNameDebug> pause_timers_for_webview;
+ std::unique_ptr<base::SingleSampleMetric> max_queueing_time_metric;
+ base::TimeDelta max_queueing_time;
+ base::TimeTicks background_status_changed_at;
+ std::set<PageSchedulerImpl*> page_schedulers; // Not owned.
+ RAILModeObserver* rail_mode_observer; // Not owned.
+ WakeUpBudgetPool* wake_up_budget_pool; // Not owned.
+ RendererMetricsHelper metrics_helper;
+ TraceableState<RendererProcessType, kTracingCategoryNameTopLevel>
+ process_type;
+ TraceableState<base::Optional<TaskDescriptionForTracing>,
+ kTracingCategoryNameInfo>
+ task_description_for_tracing; // Don't use except for tracing.
+ base::ObserverList<VirtualTimeObserver> virtual_time_observers;
+ base::Time initial_virtual_time;
+ base::TimeTicks initial_virtual_time_ticks;
+
+ // This is used for cross origin navigations to account for virtual time
+ // advancing in the previous renderer.
+ base::TimeDelta initial_virtual_time_offset;
+ VirtualTimePolicy virtual_time_policy;
+
+ // In VirtualTimePolicy::kDeterministicLoading virtual time is only allowed
+ // to advance if this is zero.
+ int virtual_time_pause_count;
+
+ // The maximum number amount of delayed task starvation we will allow in
+ // VirtualTimePolicy::kAdvance or VirtualTimePolicy::kDeterministicLoading
+ // unless the run_loop is nested (in which case infinite starvation is
+ // allowed). NB a value of 0 allows infinite starvation.
+ int max_virtual_time_task_starvation_count;
+ bool virtual_time_stopped;
+ bool nested_runloop;
+
+ std::mt19937_64 random_generator;
+ std::uniform_real_distribution<double> uniform_distribution;
+ };
+
+ struct AnyThread {
+ explicit AnyThread(MainThreadSchedulerImpl* main_thread_scheduler_impl);
+ ~AnyThread();
+
+ base::TimeTicks last_idle_period_end_time;
+ base::TimeTicks fling_compositor_escalation_deadline;
+ UserModel user_model;
+ TraceableState<bool, kTracingCategoryNameInfo>
+ awaiting_touch_start_response;
+ TraceableState<bool, kTracingCategoryNameInfo> in_idle_period;
+ TraceableState<bool, kTracingCategoryNameInfo>
+ begin_main_frame_on_critical_path;
+ TraceableState<bool, kTracingCategoryNameInfo>
+ last_gesture_was_compositor_driven;
+ TraceableState<bool, kTracingCategoryNameInfo> default_gesture_prevented;
+ TraceableState<bool, kTracingCategoryNameInfo>
+ have_seen_a_potentially_blocking_gesture;
+ TraceableState<bool, kTracingCategoryNameInfo> waiting_for_meaningful_paint;
+ TraceableState<bool, kTracingCategoryNameInfo>
+ have_seen_input_since_navigation;
+ };
+
+ struct CompositorThreadOnly {
+ CompositorThreadOnly();
+ ~CompositorThreadOnly();
+
+ WebInputEvent::Type last_input_type;
+ bool main_thread_seems_unresponsive;
+ std::unique_ptr<base::ThreadChecker> compositor_thread_checker;
+
+ void CheckOnValidThread() {
+#if DCHECK_IS_ON()
+ // We don't actually care which thread this called from, just so long as
+ // its consistent.
+ if (!compositor_thread_checker)
+ compositor_thread_checker.reset(new base::ThreadChecker());
+ DCHECK(compositor_thread_checker->CalledOnValidThread());
+#endif
+ }
+ };
+
+ // Don't access main_thread_only_, instead use MainThreadOnly().
+ MainThreadOnly main_thread_only_;
+ MainThreadOnly& main_thread_only() {
+ helper_.CheckOnValidThread();
+ return main_thread_only_;
+ }
+ const struct MainThreadOnly& main_thread_only() const {
+ helper_.CheckOnValidThread();
+ return main_thread_only_;
+ }
+
+ mutable base::Lock any_thread_lock_;
+ // Don't access any_thread_, instead use AnyThread().
+ AnyThread any_thread_;
+ AnyThread& any_thread() {
+ any_thread_lock_.AssertAcquired();
+ return any_thread_;
+ }
+ const struct AnyThread& any_thread() const {
+ any_thread_lock_.AssertAcquired();
+ return any_thread_;
+ }
+
+ // Don't access compositor_thread_only_, instead use CompositorThreadOnly().
+ CompositorThreadOnly compositor_thread_only_;
+ CompositorThreadOnly& GetCompositorThreadOnly() {
+ compositor_thread_only_.CheckOnValidThread();
+ return compositor_thread_only_;
+ }
+
+ PollableThreadSafeFlag policy_may_need_update_;
+
+ base::WeakPtrFactory<MainThreadSchedulerImpl> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(MainThreadSchedulerImpl);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_MAIN_THREAD_SCHEDULER_IMPL_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
new file mode 100644
index 00000000000..a9bd594baa0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
@@ -0,0 +1,4116 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/test/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "build/build_config.h"
+#include "components/viz/common/frame_sinks/begin_frame_args.h"
+#include "components/viz/test/ordered_simple_task_runner.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/page/launching_process_state.h"
+#include "third_party/blink/renderer/platform/scheduler/base/real_time_domain.h"
+#include "third_party/blink/renderer/platform/scheduler/child/features.h"
+#include "third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain.h"
+#include "third_party/blink/renderer/platform/scheduler/test/task_queue_manager_for_test.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+
+namespace blink {
+namespace scheduler {
+// To avoid symbol collisions in jumbo builds.
+namespace main_thread_scheduler_impl_unittest {
+
+using testing::Mock;
+using InputEventState = WebMainThreadScheduler::InputEventState;
+
+class FakeInputEvent : public blink::WebInputEvent {
+ public:
+ explicit FakeInputEvent(blink::WebInputEvent::Type event_type,
+ int modifiers = WebInputEvent::kNoModifiers)
+ : WebInputEvent(sizeof(FakeInputEvent),
+ event_type,
+ modifiers,
+ WebInputEvent::GetStaticTimeStampForTests()) {}
+};
+
+void AppendToVectorTestTask(std::vector<std::string>* vector,
+ std::string value) {
+ vector->push_back(value);
+}
+
+void AppendToVectorIdleTestTask(std::vector<std::string>* vector,
+ std::string value,
+ base::TimeTicks deadline) {
+ AppendToVectorTestTask(vector, value);
+}
+
+void NullTask() {}
+
+void AppendToVectorReentrantTask(base::SingleThreadTaskRunner* task_runner,
+ std::vector<int>* vector,
+ int* reentrant_count,
+ int max_reentrant_count) {
+ vector->push_back((*reentrant_count)++);
+ if (*reentrant_count < max_reentrant_count) {
+ task_runner->PostTask(FROM_HERE,
+ base::BindOnce(AppendToVectorReentrantTask,
+ base::Unretained(task_runner), vector,
+ reentrant_count, max_reentrant_count));
+ }
+}
+
+void IdleTestTask(int* run_count,
+ base::TimeTicks* deadline_out,
+ base::TimeTicks deadline) {
+ (*run_count)++;
+ *deadline_out = deadline;
+}
+
+int g_max_idle_task_reposts = 2;
+
+void RepostingIdleTestTask(SingleThreadIdleTaskRunner* idle_task_runner,
+ int* run_count,
+ base::TimeTicks deadline) {
+ if ((*run_count + 1) < g_max_idle_task_reposts) {
+ idle_task_runner->PostIdleTask(
+ FROM_HERE,
+ base::BindOnce(&RepostingIdleTestTask,
+ base::Unretained(idle_task_runner), run_count));
+ }
+ (*run_count)++;
+}
+
+void RepostingUpdateClockIdleTestTask(
+ SingleThreadIdleTaskRunner* idle_task_runner,
+ int* run_count,
+ base::SimpleTestTickClock* clock,
+ base::TimeDelta advance_time,
+ std::vector<base::TimeTicks>* deadlines,
+ base::TimeTicks deadline) {
+ if ((*run_count + 1) < g_max_idle_task_reposts) {
+ idle_task_runner->PostIdleTask(
+ FROM_HERE, base::BindOnce(&RepostingUpdateClockIdleTestTask,
+ base::Unretained(idle_task_runner), run_count,
+ clock, advance_time, deadlines));
+ }
+ deadlines->push_back(deadline);
+ (*run_count)++;
+ clock->Advance(advance_time);
+}
+
+void WillBeginFrameIdleTask(WebMainThreadScheduler* scheduler,
+ uint64_t sequence_number,
+ base::SimpleTestTickClock* clock,
+ base::TimeTicks deadline) {
+ scheduler->WillBeginFrame(viz::BeginFrameArgs::Create(
+ BEGINFRAME_FROM_HERE, 0, sequence_number, clock->NowTicks(),
+ base::TimeTicks(), base::TimeDelta::FromMilliseconds(1000),
+ viz::BeginFrameArgs::NORMAL));
+}
+
+void UpdateClockToDeadlineIdleTestTask(base::SimpleTestTickClock* clock,
+ int* run_count,
+ base::TimeTicks deadline) {
+ clock->Advance(deadline - clock->NowTicks());
+ (*run_count)++;
+}
+
+void PostingYieldingTestTask(MainThreadSchedulerImpl* scheduler,
+ base::SingleThreadTaskRunner* task_runner,
+ bool simulate_input,
+ bool* should_yield_before,
+ bool* should_yield_after) {
+ *should_yield_before = scheduler->ShouldYieldForHighPriorityWork();
+ task_runner->PostTask(FROM_HERE, base::BindOnce(NullTask));
+ if (simulate_input) {
+ scheduler->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchMove),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+ }
+ *should_yield_after = scheduler->ShouldYieldForHighPriorityWork();
+}
+
+enum class SimulateInputType {
+ kNone,
+ kTouchStart,
+ kTouchEnd,
+ kGestureScrollBegin,
+ kGestureScrollEnd
+};
+
+void AnticipationTestTask(MainThreadSchedulerImpl* scheduler,
+ SimulateInputType simulate_input,
+ bool* is_anticipated_before,
+ bool* is_anticipated_after) {
+ *is_anticipated_before = scheduler->IsHighPriorityWorkAnticipated();
+ switch (simulate_input) {
+ case SimulateInputType::kNone:
+ break;
+
+ case SimulateInputType::kTouchStart:
+ scheduler->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchStart),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+ break;
+
+ case SimulateInputType::kTouchEnd:
+ scheduler->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchEnd),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+ break;
+
+ case SimulateInputType::kGestureScrollBegin:
+ scheduler->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureScrollBegin),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+ break;
+
+ case SimulateInputType::kGestureScrollEnd:
+ scheduler->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureScrollEnd),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+ break;
+ }
+ *is_anticipated_after = scheduler->IsHighPriorityWorkAnticipated();
+}
+
+// RAII helper class to enable auto advancing of time inside mock task runner.
+// Automatically disables auto-advancement when destroyed.
+class ScopedAutoAdvanceNowEnabler {
+ public:
+ ScopedAutoAdvanceNowEnabler(
+ scoped_refptr<cc::OrderedSimpleTaskRunner> task_runner)
+ : task_runner_(task_runner) {
+ task_runner_->SetAutoAdvanceNowToPendingTasks(true);
+ }
+
+ ~ScopedAutoAdvanceNowEnabler() {
+ task_runner_->SetAutoAdvanceNowToPendingTasks(false);
+ }
+
+ private:
+ scoped_refptr<cc::OrderedSimpleTaskRunner> task_runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedAutoAdvanceNowEnabler);
+};
+
+class MainThreadSchedulerImplForTest : public MainThreadSchedulerImpl {
+ public:
+ using MainThreadSchedulerImpl::EstimateLongestJankFreeTaskDuration;
+ using MainThreadSchedulerImpl::OnIdlePeriodEnded;
+ using MainThreadSchedulerImpl::OnIdlePeriodStarted;
+ using MainThreadSchedulerImpl::OnPendingTasksChanged;
+
+ MainThreadSchedulerImplForTest(
+ std::unique_ptr<TaskQueueManager> manager,
+ base::Optional<base::Time> initial_virtual_time)
+ : MainThreadSchedulerImpl(std::move(manager), initial_virtual_time),
+ update_policy_count_(0) {}
+
+ void UpdatePolicyLocked(UpdateType update_type) override {
+ update_policy_count_++;
+ MainThreadSchedulerImpl::UpdatePolicyLocked(update_type);
+
+ std::string use_case = MainThreadSchedulerImpl::UseCaseToString(
+ main_thread_only().current_use_case);
+ if (main_thread_only().touchstart_expected_soon) {
+ use_cases_.push_back(use_case + " touchstart expected");
+ } else {
+ use_cases_.push_back(use_case);
+ }
+ }
+
+ void EnsureUrgentPolicyUpdatePostedOnMainThread() {
+ base::AutoLock lock(any_thread_lock_);
+ MainThreadSchedulerImpl::EnsureUrgentPolicyUpdatePostedOnMainThread(
+ FROM_HERE);
+ }
+
+ void ScheduleDelayedPolicyUpdate(base::TimeTicks now, base::TimeDelta delay) {
+ delayed_update_policy_runner_.SetDeadline(FROM_HERE, delay, now);
+ }
+
+ bool BeginMainFrameOnCriticalPath() {
+ base::AutoLock lock(any_thread_lock_);
+ return any_thread().begin_main_frame_on_critical_path;
+ }
+
+ bool waiting_for_meaningful_paint() const {
+ base::AutoLock lock(any_thread_lock_);
+ return any_thread().waiting_for_meaningful_paint;
+ }
+
+ VirtualTimePolicy virtual_time_policy() const {
+ return main_thread_only().virtual_time_policy;
+ }
+
+ int update_policy_count_;
+ std::vector<std::string> use_cases_;
+};
+
+// Lets gtest print human readable Policy values.
+::std::ostream& operator<<(::std::ostream& os, const UseCase& use_case) {
+ return os << MainThreadSchedulerImpl::UseCaseToString(use_case);
+}
+
+class MainThreadSchedulerImplTest : public testing::Test {
+ public:
+ MainThreadSchedulerImplTest()
+ : fake_task_(TaskQueue::PostedTask(base::BindOnce([] {}), FROM_HERE),
+ base::TimeTicks()) {
+ feature_list_.InitAndEnableFeature(kHighPriorityInput);
+ clock_.Advance(base::TimeDelta::FromMicroseconds(5000));
+ }
+
+ MainThreadSchedulerImplTest(base::MessageLoop* message_loop)
+ : fake_task_(TaskQueue::PostedTask(base::BindOnce([] {}), FROM_HERE),
+ base::TimeTicks()),
+ message_loop_(message_loop) {
+ clock_.Advance(base::TimeDelta::FromMicroseconds(5000));
+ }
+
+ ~MainThreadSchedulerImplTest() override = default;
+
+ void SetUp() override {
+ if (!message_loop_) {
+ mock_task_runner_ =
+ base::MakeRefCounted<cc::OrderedSimpleTaskRunner>(&clock_, false);
+ }
+ Initialize(std::make_unique<MainThreadSchedulerImplForTest>(
+ TaskQueueManagerForTest::Create(
+ message_loop_.get(),
+ message_loop_ ? message_loop_->task_runner() : mock_task_runner_,
+ &clock_),
+ base::nullopt));
+ }
+
+ void Initialize(std::unique_ptr<MainThreadSchedulerImplForTest> scheduler) {
+ scheduler_ = std::move(scheduler);
+ if (kLaunchingProcessIsBackgrounded) {
+ scheduler_->SetRendererBackgrounded(false);
+ // Reset the policy count as foregrounding would force an initial update.
+ scheduler_->update_policy_count_ = 0;
+ scheduler_->use_cases_.clear();
+ }
+ default_task_runner_ = scheduler_->DefaultTaskQueue();
+ compositor_task_runner_ = scheduler_->CompositorTaskQueue();
+ input_task_runner_ = scheduler_->InputTaskQueue();
+ loading_task_runner_ = scheduler_->NewLoadingTaskQueue(
+ MainThreadTaskQueue::QueueType::kFrameLoading);
+ loading_control_task_runner_ = scheduler_->NewLoadingTaskQueue(
+ MainThreadTaskQueue::QueueType::kFrameLoadingControl);
+ idle_task_runner_ = scheduler_->IdleTaskRunner();
+ timer_task_runner_ = scheduler_->NewTimerTaskQueue(
+ MainThreadTaskQueue::QueueType::kFrameThrottleable);
+ v8_task_runner_ = scheduler_->V8TaskQueue();
+ fake_queue_ = scheduler_->NewLoadingTaskQueue(
+ MainThreadTaskQueue::QueueType::kFrameLoading);
+ }
+
+ void TearDown() override {
+ DCHECK(!mock_task_runner_.get() || !message_loop_.get());
+ scheduler_->Shutdown();
+ if (mock_task_runner_.get()) {
+ // Check that all tests stop posting tasks.
+ mock_task_runner_->SetAutoAdvanceNowToPendingTasks(true);
+ while (mock_task_runner_->RunUntilIdle()) {
+ }
+ } else {
+ base::RunLoop().RunUntilIdle();
+ }
+ scheduler_.reset();
+ }
+
+ void RunUntilIdle() {
+ // Only one of mock_task_runner_ or message_loop_ should be set.
+ DCHECK(!mock_task_runner_.get() || !message_loop_.get());
+ if (mock_task_runner_.get()) {
+ mock_task_runner_->RunUntilIdle();
+ } else {
+ base::RunLoop().RunUntilIdle();
+ }
+ }
+
+ void DoMainFrame() {
+ viz::BeginFrameArgs begin_frame_args = viz::BeginFrameArgs::Create(
+ BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_.NowTicks(),
+ base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
+ viz::BeginFrameArgs::NORMAL);
+ begin_frame_args.on_critical_path = false;
+ scheduler_->WillBeginFrame(begin_frame_args);
+ scheduler_->DidCommitFrameToCompositor();
+ }
+
+ void DoMainFrameOnCriticalPath() {
+ viz::BeginFrameArgs begin_frame_args = viz::BeginFrameArgs::Create(
+ BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_.NowTicks(),
+ base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
+ viz::BeginFrameArgs::NORMAL);
+ begin_frame_args.on_critical_path = true;
+ scheduler_->WillBeginFrame(begin_frame_args);
+ }
+
+ void ForceTouchStartToBeExpectedSoon() {
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureScrollUpdate),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureScrollEnd),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+ clock_.Advance(priority_escalation_after_input_duration() * 2);
+ scheduler_->ForceUpdatePolicy();
+ }
+
+ void SimulateExpensiveTasks(
+ const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) {
+ // RunUntilIdle won't actually run all of the SimpleTestTickClock::Advance
+ // tasks unless we set AutoAdvanceNow to true :/
+ ScopedAutoAdvanceNowEnabler enable_auto_advance_now(mock_task_runner_);
+
+ // Simulate a bunch of expensive tasks
+ for (int i = 0; i < 10; i++) {
+ task_runner->PostTask(
+ FROM_HERE, base::BindOnce(&base::SimpleTestTickClock::Advance,
+ base::Unretained(&clock_),
+ base::TimeDelta::FromMilliseconds(500)));
+ }
+
+ RunUntilIdle();
+ }
+
+ enum class TouchEventPolicy {
+ kSendTouchStart,
+ kDontSendTouchStart,
+ };
+
+ void SimulateCompositorGestureStart(TouchEventPolicy touch_event_policy) {
+ if (touch_event_policy == TouchEventPolicy::kSendTouchStart) {
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchStart),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchMove),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchMove),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ }
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureScrollBegin),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureScrollUpdate),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+ }
+
+ // Simulate a gesture where there is an active compositor scroll, but no
+ // scroll updates are generated. Instead, the main thread handles
+ // non-canceleable touch events, making this an effectively main thread
+ // driven gesture.
+ void SimulateMainThreadGestureWithoutScrollUpdates() {
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchStart),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchMove),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureScrollBegin),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchMove),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ }
+
+ // Simulate a gesture where the main thread handles touch events but does not
+ // preventDefault(), allowing the gesture to turn into a compositor driven
+ // gesture. This function also verifies the necessary policy updates are
+ // scheduled.
+ void SimulateMainThreadGestureWithoutPreventDefault() {
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchStart),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+
+ // Touchstart policy update.
+ EXPECT_TRUE(scheduler_->PolicyNeedsUpdateForTesting());
+ EXPECT_EQ(UseCase::kTouchstart, ForceUpdatePolicyAndGetCurrentUseCase());
+ EXPECT_FALSE(scheduler_->PolicyNeedsUpdateForTesting());
+
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchMove),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureTapCancel),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureScrollBegin),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+
+ // Main thread gesture policy update.
+ EXPECT_TRUE(scheduler_->PolicyNeedsUpdateForTesting());
+ EXPECT_EQ(UseCase::kMainThreadCustomInputHandling,
+ ForceUpdatePolicyAndGetCurrentUseCase());
+ EXPECT_FALSE(scheduler_->PolicyNeedsUpdateForTesting());
+
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureScrollUpdate),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchScrollStarted),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchMove),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+
+ // Compositor thread gesture policy update.
+ EXPECT_TRUE(scheduler_->PolicyNeedsUpdateForTesting());
+ EXPECT_EQ(UseCase::kCompositorGesture,
+ ForceUpdatePolicyAndGetCurrentUseCase());
+ EXPECT_FALSE(scheduler_->PolicyNeedsUpdateForTesting());
+ }
+
+ void SimulateMainThreadGestureStart(TouchEventPolicy touch_event_policy,
+ blink::WebInputEvent::Type gesture_type) {
+ if (touch_event_policy == TouchEventPolicy::kSendTouchStart) {
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchStart),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ scheduler_->DidHandleInputEventOnMainThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchStart),
+ WebInputEventResult::kHandledSystem);
+
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchMove),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ scheduler_->DidHandleInputEventOnMainThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchMove),
+ WebInputEventResult::kHandledSystem);
+
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchMove),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ scheduler_->DidHandleInputEventOnMainThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchMove),
+ WebInputEventResult::kHandledSystem);
+ }
+ if (gesture_type != blink::WebInputEvent::kUndefined) {
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(gesture_type),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ scheduler_->DidHandleInputEventOnMainThread(
+ FakeInputEvent(gesture_type), WebInputEventResult::kHandledSystem);
+ }
+ }
+
+ void SimulateMainThreadInputHandlingCompositorTask(
+ base::TimeDelta begin_main_frame_duration) {
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchMove),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ clock_.Advance(begin_main_frame_duration);
+ scheduler_->DidHandleInputEventOnMainThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchMove),
+ WebInputEventResult::kHandledApplication);
+ scheduler_->DidCommitFrameToCompositor();
+ }
+
+ void SimulateMainThreadCompositorTask(
+ base::TimeDelta begin_main_frame_duration) {
+ clock_.Advance(begin_main_frame_duration);
+ scheduler_->DidCommitFrameToCompositor();
+ simulate_compositor_task_ran_ = true;
+ }
+
+ bool SimulatedCompositorTaskPending() const {
+ return !simulate_compositor_task_ran_;
+ }
+
+ void SimulateTimerTask(base::TimeDelta duration) {
+ clock_.Advance(duration);
+ simulate_timer_task_ran_ = true;
+ }
+
+ void EnableIdleTasks() { DoMainFrame(); }
+
+ UseCase CurrentUseCase() {
+ return scheduler_->main_thread_only().current_use_case;
+ }
+
+ UseCase ForceUpdatePolicyAndGetCurrentUseCase() {
+ scheduler_->ForceUpdatePolicy();
+ return scheduler_->main_thread_only().current_use_case;
+ }
+
+ v8::RAILMode GetRAILMode() {
+ return scheduler_->main_thread_only().current_policy.rail_mode();
+ }
+
+ bool BeginFrameNotExpectedSoon() {
+ return scheduler_->main_thread_only().begin_frame_not_expected_soon;
+ }
+
+ bool TouchStartExpectedSoon() {
+ return scheduler_->main_thread_only().touchstart_expected_soon;
+ }
+
+ bool HaveSeenABeginMainframe() {
+ return scheduler_->main_thread_only().have_seen_a_begin_main_frame;
+ }
+
+ bool LoadingTasksSeemExpensive() {
+ return scheduler_->main_thread_only().loading_tasks_seem_expensive;
+ }
+
+ bool TimerTasksSeemExpensive() {
+ return scheduler_->main_thread_only().timer_tasks_seem_expensive;
+ }
+
+ base::TimeTicks EstimatedNextFrameBegin() {
+ return scheduler_->main_thread_only().estimated_next_frame_begin;
+ }
+
+ int NavigationTaskExpectedCount() {
+ return scheduler_->main_thread_only().navigation_task_expected_count;
+ }
+
+ void AdvanceTimeWithTask(double duration) {
+ base::TimeTicks start = clock_.NowTicks();
+ scheduler_->OnTaskStarted(fake_queue_.get(), fake_task_, start);
+ clock_.Advance(base::TimeDelta::FromSecondsD(duration));
+ base::TimeTicks end = clock_.NowTicks();
+ scheduler_->OnTaskCompleted(fake_queue_.get(), fake_task_, start, end,
+ base::nullopt);
+ }
+
+ void GetQueueingTimeEstimatorLock() {
+ scheduler_->seqlock_queueing_time_estimator_.seqlock.WriteBegin();
+ }
+
+ void DropQueueingTimeEstimatorLock() {
+ scheduler_->seqlock_queueing_time_estimator_.seqlock.WriteEnd();
+ }
+
+ void RunSlowCompositorTask() {
+ // Run a long compositor task so that compositor tasks appear to be running
+ // slow and thus compositor tasks will not be prioritized.
+ compositor_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &MainThreadSchedulerImplTest::SimulateMainThreadCompositorTask,
+ base::Unretained(this), base::TimeDelta::FromMilliseconds(1000)));
+ RunUntilIdle();
+ }
+
+ // Helper for posting several tasks of specific types. |task_descriptor| is a
+ // string with space delimited task identifiers. The first letter of each
+ // task identifier specifies the task type:
+ // - 'D': Default task
+ // - 'C': Compositor task
+ // - 'P': Input task
+ // - 'L': Loading task
+ // - 'M': Loading Control task
+ // - 'I': Idle task
+ // - 'T': Timer task
+ // - 'V': kV8 task
+ void PostTestTasks(std::vector<std::string>* run_order,
+ const std::string& task_descriptor) {
+ std::istringstream stream(task_descriptor);
+ while (!stream.eof()) {
+ std::string task;
+ stream >> task;
+ switch (task[0]) {
+ case 'D':
+ default_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&AppendToVectorTestTask, run_order, task));
+ break;
+ case 'C':
+ compositor_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&AppendToVectorTestTask, run_order, task));
+ break;
+ case 'P':
+ input_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&AppendToVectorTestTask, run_order, task));
+ break;
+ case 'L':
+ loading_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&AppendToVectorTestTask, run_order, task));
+ break;
+ case 'M':
+ loading_control_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&AppendToVectorTestTask, run_order, task));
+ break;
+ case 'I':
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE,
+ base::BindOnce(&AppendToVectorIdleTestTask, run_order, task));
+ break;
+ case 'T':
+ timer_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&AppendToVectorTestTask, run_order, task));
+ break;
+ case 'V':
+ v8_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&AppendToVectorTestTask, run_order, task));
+ break;
+ default:
+ NOTREACHED();
+ }
+ }
+ }
+
+ protected:
+ static base::TimeDelta priority_escalation_after_input_duration() {
+ return base::TimeDelta::FromMilliseconds(
+ UserModel::kGestureEstimationLimitMillis);
+ }
+
+ static base::TimeDelta subsequent_input_expected_after_input_duration() {
+ return base::TimeDelta::FromMilliseconds(
+ UserModel::kExpectSubsequentGestureMillis);
+ }
+
+ static base::TimeDelta maximum_idle_period_duration() {
+ return base::TimeDelta::FromMilliseconds(
+ IdleHelper::kMaximumIdlePeriodMillis);
+ }
+
+ static base::TimeDelta end_idle_when_hidden_delay() {
+ return base::TimeDelta::FromMilliseconds(
+ MainThreadSchedulerImpl::kEndIdleWhenHiddenDelayMillis);
+ }
+
+ static base::TimeDelta delay_for_background_tab_stopping() {
+ return base::TimeDelta::FromMilliseconds(
+ MainThreadSchedulerImpl::kDelayForBackgroundTabStoppingMillis);
+ }
+
+ static base::TimeDelta rails_response_time() {
+ return base::TimeDelta::FromMilliseconds(
+ MainThreadSchedulerImpl::kRailsResponseTimeMillis);
+ }
+
+ static base::TimeDelta responsiveness_threshold() {
+ return base::TimeDelta::FromMilliseconds(200);
+ }
+
+ template <typename E>
+ static void CallForEachEnumValue(E first,
+ E last,
+ const char* (*function)(E)) {
+ for (E val = first; val < last;
+ val = static_cast<E>(static_cast<int>(val) + 1)) {
+ (*function)(val);
+ }
+ }
+
+ static void CheckAllUseCaseToString() {
+ CallForEachEnumValue<UseCase>(UseCase::kFirstUseCase, UseCase::kCount,
+ &MainThreadSchedulerImpl::UseCaseToString);
+ }
+
+ static scoped_refptr<TaskQueue> ThrottableTaskQueue(
+ FrameSchedulerImpl* scheduler) {
+ return scheduler->ThrottleableTaskQueue();
+ }
+
+ base::test::ScopedFeatureList feature_list_;
+ base::SimpleTestTickClock clock_;
+ TaskQueue::Task fake_task_;
+ scoped_refptr<MainThreadTaskQueue> fake_queue_;
+ // Only one of mock_task_runner_ or message_loop_ will be set.
+ scoped_refptr<cc::OrderedSimpleTaskRunner> mock_task_runner_;
+ std::unique_ptr<base::MessageLoop> message_loop_;
+
+ std::unique_ptr<MainThreadSchedulerImplForTest> scheduler_;
+ scoped_refptr<base::SingleThreadTaskRunner> default_task_runner_;
+ scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner_;
+ scoped_refptr<base::SingleThreadTaskRunner> input_task_runner_;
+ scoped_refptr<TaskQueue> loading_task_runner_;
+ scoped_refptr<base::SingleThreadTaskRunner> loading_control_task_runner_;
+ scoped_refptr<SingleThreadIdleTaskRunner> idle_task_runner_;
+ scoped_refptr<TaskQueue> timer_task_runner_;
+ scoped_refptr<base::SingleThreadTaskRunner> v8_task_runner_;
+ bool simulate_timer_task_ran_;
+ bool simulate_compositor_task_ran_;
+ uint64_t next_begin_frame_number_ = viz::BeginFrameArgs::kStartingFrameNumber;
+
+ DISALLOW_COPY_AND_ASSIGN(MainThreadSchedulerImplTest);
+};
+
+TEST_F(MainThreadSchedulerImplTest, TestPostDefaultTask) {
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "D1 D2 D3 D4");
+
+ RunUntilIdle();
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("D1"), std::string("D2"),
+ std::string("D3"), std::string("D4")));
+}
+
+TEST_F(MainThreadSchedulerImplTest, TestPostDefaultAndCompositor) {
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "D1 C1 P1");
+ RunUntilIdle();
+ EXPECT_THAT(run_order, testing::Contains("D1"));
+ EXPECT_THAT(run_order, testing::Contains("C1"));
+ EXPECT_THAT(run_order, testing::Contains("P1"));
+}
+
+TEST_F(MainThreadSchedulerImplTest, TestRentrantTask) {
+ int count = 0;
+ std::vector<int> run_order;
+ default_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(AppendToVectorReentrantTask,
+ base::RetainedRef(default_task_runner_),
+ &run_order, &count, 5));
+ RunUntilIdle();
+
+ EXPECT_THAT(run_order, testing::ElementsAre(0, 1, 2, 3, 4));
+}
+
+TEST_F(MainThreadSchedulerImplTest, TestPostIdleTask) {
+ int run_count = 0;
+ base::TimeTicks expected_deadline =
+ clock_.NowTicks() + base::TimeDelta::FromMilliseconds(2300);
+ base::TimeTicks deadline_in_task;
+
+ clock_.Advance(base::TimeDelta::FromMilliseconds(100));
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE, base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task));
+
+ RunUntilIdle();
+ EXPECT_EQ(0, run_count); // Shouldn't run yet as no WillBeginFrame.
+
+ scheduler_->WillBeginFrame(viz::BeginFrameArgs::Create(
+ BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_.NowTicks(),
+ base::TimeTicks(), base::TimeDelta::FromMilliseconds(1000),
+ viz::BeginFrameArgs::NORMAL));
+ RunUntilIdle();
+ EXPECT_EQ(0, run_count); // Shouldn't run as no DidCommitFrameToCompositor.
+
+ clock_.Advance(base::TimeDelta::FromMilliseconds(1200));
+ scheduler_->DidCommitFrameToCompositor();
+ RunUntilIdle();
+ EXPECT_EQ(0, run_count); // We missed the deadline.
+
+ scheduler_->WillBeginFrame(viz::BeginFrameArgs::Create(
+ BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_.NowTicks(),
+ base::TimeTicks(), base::TimeDelta::FromMilliseconds(1000),
+ viz::BeginFrameArgs::NORMAL));
+ clock_.Advance(base::TimeDelta::FromMilliseconds(800));
+ scheduler_->DidCommitFrameToCompositor();
+ RunUntilIdle();
+ EXPECT_EQ(1, run_count);
+ EXPECT_EQ(expected_deadline, deadline_in_task);
+}
+
+TEST_F(MainThreadSchedulerImplTest, TestRepostingIdleTask) {
+ int run_count = 0;
+
+ g_max_idle_task_reposts = 2;
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE,
+ base::BindOnce(&RepostingIdleTestTask,
+ base::RetainedRef(idle_task_runner_), &run_count));
+ EnableIdleTasks();
+ RunUntilIdle();
+ EXPECT_EQ(1, run_count);
+
+ // Reposted tasks shouldn't run until next idle period.
+ RunUntilIdle();
+ EXPECT_EQ(1, run_count);
+
+ EnableIdleTasks();
+ RunUntilIdle();
+ EXPECT_EQ(2, run_count);
+}
+
+TEST_F(MainThreadSchedulerImplTest, TestIdleTaskExceedsDeadline) {
+ ScopedAutoAdvanceNowEnabler enable_auto_advance_now(mock_task_runner_);
+ int run_count = 0;
+
+ // Post two UpdateClockToDeadlineIdleTestTask tasks.
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE,
+ base::BindOnce(&UpdateClockToDeadlineIdleTestTask, &clock_, &run_count));
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE,
+ base::BindOnce(&UpdateClockToDeadlineIdleTestTask, &clock_, &run_count));
+
+ EnableIdleTasks();
+ RunUntilIdle();
+ // Only the first idle task should execute since it's used up the deadline.
+ EXPECT_EQ(1, run_count);
+
+ EnableIdleTasks();
+ RunUntilIdle();
+ // Second task should be run on the next idle period.
+ EXPECT_EQ(2, run_count);
+}
+
+TEST_F(MainThreadSchedulerImplTest, TestDelayedEndIdlePeriodCanceled) {
+ int run_count = 0;
+
+ base::TimeTicks deadline_in_task;
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE, base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task));
+
+ // Trigger the beginning of an idle period for 1000ms.
+ scheduler_->WillBeginFrame(viz::BeginFrameArgs::Create(
+ BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_.NowTicks(),
+ base::TimeTicks(), base::TimeDelta::FromMilliseconds(1000),
+ viz::BeginFrameArgs::NORMAL));
+ DoMainFrame();
+
+ // End the idle period early (after 500ms), and send a WillBeginFrame which
+ // specifies that the next idle period should end 1000ms from now.
+ clock_.Advance(base::TimeDelta::FromMilliseconds(500));
+ scheduler_->WillBeginFrame(viz::BeginFrameArgs::Create(
+ BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_.NowTicks(),
+ base::TimeTicks(), base::TimeDelta::FromMilliseconds(1000),
+ viz::BeginFrameArgs::NORMAL));
+
+ RunUntilIdle();
+ EXPECT_EQ(0, run_count); // Not currently in an idle period.
+
+ // Trigger the start of the idle period before the task to end the previous
+ // idle period has been triggered.
+ clock_.Advance(base::TimeDelta::FromMilliseconds(400));
+ scheduler_->DidCommitFrameToCompositor();
+
+ // Post a task which simulates running until after the previous end idle
+ // period delayed task was scheduled for
+ scheduler_->DefaultTaskQueue()->PostTask(FROM_HERE, base::BindOnce(NullTask));
+ clock_.Advance(base::TimeDelta::FromMilliseconds(300));
+
+ RunUntilIdle();
+ EXPECT_EQ(1, run_count); // We should still be in the new idle period.
+}
+
+TEST_F(MainThreadSchedulerImplTest, TestDefaultPolicy) {
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "L1 I1 D1 P1 C1 D2 P2 C2");
+
+ EnableIdleTasks();
+ RunUntilIdle();
+ // High-priority input is enabled and input tasks are processed first.
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("P1"), std::string("P2"),
+ std::string("L1"), std::string("D1"),
+ std::string("C1"), std::string("D2"),
+ std::string("C2"), std::string("I1")));
+ EXPECT_EQ(UseCase::kNone, CurrentUseCase());
+}
+
+TEST_F(MainThreadSchedulerImplTest, TestDefaultPolicyWithSlowCompositor) {
+ RunSlowCompositorTask();
+
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "L1 I1 D1 C1 P1 D2 C2");
+
+ EnableIdleTasks();
+ RunUntilIdle();
+ // Even with slow compositor input tasks are handled first.
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("P1"), std::string("L1"),
+ std::string("D1"), std::string("C1"),
+ std::string("D2"), std::string("C2"),
+ std::string("I1")));
+ EXPECT_EQ(UseCase::kNone, CurrentUseCase());
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ TestCompositorPolicy_CompositorHandlesInput_WithTouchHandler) {
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "L1 I1 D1 C1 D2 C2");
+
+ scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
+ EnableIdleTasks();
+ SimulateCompositorGestureStart(TouchEventPolicy::kSendTouchStart);
+ RunUntilIdle();
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("L1"), std::string("D1"),
+ std::string("D2"), std::string("C1"),
+ std::string("C2"), std::string("I1")));
+ EXPECT_EQ(UseCase::kCompositorGesture, CurrentUseCase());
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ TestCompositorPolicy_MainThreadHandlesInput_WithoutScrollUpdates) {
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "L1 I1 D1 C1 D2 C2");
+
+ scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
+ EnableIdleTasks();
+ SimulateMainThreadGestureWithoutScrollUpdates();
+ RunUntilIdle();
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("C1"), std::string("C2"),
+ std::string("L1"), std::string("D1"),
+ std::string("D2"), std::string("I1")));
+ EXPECT_EQ(UseCase::kMainThreadCustomInputHandling, CurrentUseCase());
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ TestCompositorPolicy_MainThreadHandlesInput_WithoutPreventDefault) {
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "L1 I1 D1 C1 D2 C2");
+
+ scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
+ EnableIdleTasks();
+ SimulateMainThreadGestureWithoutPreventDefault();
+ RunUntilIdle();
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("L1"), std::string("D1"),
+ std::string("D2"), std::string("C1"),
+ std::string("C2"), std::string("I1")));
+ EXPECT_EQ(UseCase::kCompositorGesture, CurrentUseCase());
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ TestCompositorPolicy_CompositorHandlesInput_LongGestureDuration) {
+ scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
+ EnableIdleTasks();
+ SimulateCompositorGestureStart(TouchEventPolicy::kSendTouchStart);
+
+ base::TimeTicks loop_end_time =
+ clock_.NowTicks() + base::TimeDelta::FromMilliseconds(
+ UserModel::kMedianGestureDurationMillis * 2);
+
+ // The UseCase::kCompositorGesture usecase initially deprioritizes
+ // compositor tasks (see
+ // TestCompositorPolicy_CompositorHandlesInput_WithTouchHandler) but if the
+ // gesture is long enough, compositor tasks get prioritized again.
+ while (clock_.NowTicks() < loop_end_time) {
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchMove),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+ clock_.Advance(base::TimeDelta::FromMilliseconds(16));
+ RunUntilIdle();
+ }
+
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "L1 I1 D1 C1 D2 C2");
+
+ RunUntilIdle();
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("C1"), std::string("C2"),
+ std::string("L1"), std::string("D1"),
+ std::string("D2")));
+ EXPECT_EQ(UseCase::kCompositorGesture, CurrentUseCase());
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ TestCompositorPolicy_CompositorHandlesInput_WithoutTouchHandler) {
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "L1 I1 D1 C1 D2 C2");
+
+ EnableIdleTasks();
+ SimulateCompositorGestureStart(TouchEventPolicy::kDontSendTouchStart);
+ RunUntilIdle();
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("L1"), std::string("D1"),
+ std::string("D2"), std::string("C1"),
+ std::string("C2"), std::string("I1")));
+ EXPECT_EQ(UseCase::kCompositorGesture, CurrentUseCase());
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ TestCompositorPolicy_MainThreadHandlesInput_WithTouchHandler) {
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "L1 I1 D1 C1 D2 C2");
+
+ scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
+ EnableIdleTasks();
+ SimulateMainThreadGestureStart(TouchEventPolicy::kSendTouchStart,
+ blink::WebInputEvent::kGestureScrollBegin);
+ RunUntilIdle();
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("C1"), std::string("C2"),
+ std::string("L1"), std::string("D1"),
+ std::string("D2"), std::string("I1")));
+ EXPECT_EQ(UseCase::kMainThreadCustomInputHandling, CurrentUseCase());
+ scheduler_->DidHandleInputEventOnMainThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureFlingStart),
+ WebInputEventResult::kHandledSystem);
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ TestCompositorPolicy_MainThreadHandlesInput_WithoutTouchHandler) {
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "L1 I1 D1 C1 D2 C2");
+
+ EnableIdleTasks();
+ SimulateMainThreadGestureStart(TouchEventPolicy::kDontSendTouchStart,
+ blink::WebInputEvent::kGestureScrollBegin);
+ RunUntilIdle();
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("C1"), std::string("C2"),
+ std::string("L1"), std::string("D1"),
+ std::string("D2"), std::string("I1")));
+ EXPECT_EQ(UseCase::kMainThreadCustomInputHandling, CurrentUseCase());
+ scheduler_->DidHandleInputEventOnMainThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureFlingStart),
+ WebInputEventResult::kHandledSystem);
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ TestCompositorPolicy_MainThreadHandlesInput_SingleEvent_PreventDefault) {
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "L1 I1 D1 C1 D2 C2");
+
+ scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
+ EnableIdleTasks();
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchStart),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ scheduler_->DidHandleInputEventOnMainThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchStart),
+ WebInputEventResult::kHandledApplication);
+ RunUntilIdle();
+ // Because the main thread is performing custom input handling, we let all
+ // tasks run. However compositing tasks are still given priority.
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("C1"), std::string("C2"),
+ std::string("L1"), std::string("D1"),
+ std::string("D2"), std::string("I1")));
+ EXPECT_EQ(UseCase::kMainThreadCustomInputHandling, CurrentUseCase());
+}
+
+TEST_F(
+ MainThreadSchedulerImplTest,
+ TestCompositorPolicy_MainThreadHandlesInput_SingleEvent_NoPreventDefault) {
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "L1 I1 D1 C1 D2 C2");
+
+ scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
+ EnableIdleTasks();
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchStart),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ scheduler_->DidHandleInputEventOnMainThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchStart),
+ WebInputEventResult::kHandledSystem);
+ RunUntilIdle();
+ // Because we are still waiting for the touchstart to be processed,
+ // non-essential tasks like loading tasks are blocked.
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("C1"), std::string("C2"),
+ std::string("D1"), std::string("D2"),
+ std::string("I1")));
+ EXPECT_EQ(UseCase::kTouchstart, CurrentUseCase());
+}
+
+TEST_F(MainThreadSchedulerImplTest, TestCompositorPolicy_DidAnimateForInput) {
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "I1 D1 C1 D2 C2");
+
+ scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
+ scheduler_->DidAnimateForInputOnCompositorThread();
+ // Note DidAnimateForInputOnCompositorThread does not by itself trigger a
+ // policy update.
+ EXPECT_EQ(UseCase::kCompositorGesture,
+ ForceUpdatePolicyAndGetCurrentUseCase());
+ EnableIdleTasks();
+ RunUntilIdle();
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("D1"), std::string("D2"),
+ std::string("C1"), std::string("C2"),
+ std::string("I1")));
+ EXPECT_EQ(UseCase::kCompositorGesture, CurrentUseCase());
+}
+
+TEST_F(MainThreadSchedulerImplTest, Navigation_ResetsTaskCostEstimations) {
+ std::vector<std::string> run_order;
+
+ scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
+ SimulateExpensiveTasks(timer_task_runner_);
+ DoMainFrame();
+ // A navigation occurs which creates a new Document thus resetting the task
+ // cost estimations.
+ scheduler_->DidStartProvisionalLoad(true);
+ SimulateMainThreadGestureStart(TouchEventPolicy::kSendTouchStart,
+ blink::WebInputEvent::kGestureScrollUpdate);
+
+ PostTestTasks(&run_order, "C1 T1");
+
+ RunUntilIdle();
+
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("C1"), std::string("T1")));
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ ExpensiveTimersDontRunWhenMainThreadScrolling) {
+ std::vector<std::string> run_order;
+
+ scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
+ SimulateExpensiveTasks(timer_task_runner_);
+ DoMainFrame();
+ SimulateMainThreadGestureStart(TouchEventPolicy::kSendTouchStart,
+ blink::WebInputEvent::kGestureScrollUpdate);
+
+ PostTestTasks(&run_order, "C1 T1");
+
+ RunUntilIdle();
+ EXPECT_FALSE(TouchStartExpectedSoon());
+ EXPECT_EQ(UseCase::kMainThreadGesture, CurrentUseCase());
+
+ EXPECT_THAT(run_order, testing::ElementsAre(std::string("C1")));
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ ExpensiveTimersDoRunWhenMainThreadInputHandling) {
+ std::vector<std::string> run_order;
+
+ scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
+ SimulateExpensiveTasks(timer_task_runner_);
+ DoMainFrame();
+ SimulateMainThreadGestureStart(TouchEventPolicy::kSendTouchStart,
+ blink::WebInputEvent::kUndefined);
+
+ PostTestTasks(&run_order, "C1 T1");
+
+ RunUntilIdle();
+ EXPECT_FALSE(TouchStartExpectedSoon());
+ EXPECT_EQ(UseCase::kMainThreadCustomInputHandling, CurrentUseCase());
+
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("C1"), std::string("T1")));
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ ExpensiveTimersDoRunWhenMainThreadScrolling_AndOnCriticalPath) {
+ std::vector<std::string> run_order;
+
+ scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
+ SimulateExpensiveTasks(timer_task_runner_);
+ DoMainFrameOnCriticalPath();
+ SimulateMainThreadGestureStart(TouchEventPolicy::kSendTouchStart,
+ blink::WebInputEvent::kGestureScrollBegin);
+
+ PostTestTasks(&run_order, "C1 T1");
+
+ RunUntilIdle();
+ EXPECT_FALSE(TouchStartExpectedSoon());
+ EXPECT_EQ(UseCase::kMainThreadCustomInputHandling, CurrentUseCase());
+
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("C1"), std::string("T1")));
+}
+
+TEST_F(MainThreadSchedulerImplTest, TestTouchstartPolicy_Compositor) {
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "L1 D1 C1 D2 C2 T1 T2");
+
+ // Observation of touchstart should defer execution of timer, idle and loading
+ // tasks.
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchStart),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+ EnableIdleTasks();
+ RunUntilIdle();
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("C1"), std::string("C2"),
+ std::string("D1"), std::string("D2")));
+
+ // Animation or meta events like TapDown/FlingCancel shouldn't affect the
+ // priority.
+ run_order.clear();
+ scheduler_->DidAnimateForInputOnCompositorThread();
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureFlingCancel),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureTapDown),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+ RunUntilIdle();
+ EXPECT_THAT(run_order, testing::ElementsAre());
+
+ // Action events like ScrollBegin will kick us back into compositor priority,
+ // allowing service of the timer, loading and idle queues.
+ run_order.clear();
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureScrollBegin),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+ RunUntilIdle();
+
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("L1"), std::string("T1"),
+ std::string("T2")));
+}
+
+TEST_F(MainThreadSchedulerImplTest, TestTouchstartPolicy_MainThread) {
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "L1 D1 C1 D2 C2 T1 T2");
+
+ // Observation of touchstart should defer execution of timer, idle and loading
+ // tasks.
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchStart),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ scheduler_->DidHandleInputEventOnMainThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchStart),
+ WebInputEventResult::kHandledSystem);
+ EnableIdleTasks();
+ RunUntilIdle();
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("C1"), std::string("C2"),
+ std::string("D1"), std::string("D2")));
+
+ // Meta events like TapDown/FlingCancel shouldn't affect the priority.
+ run_order.clear();
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureFlingCancel),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ scheduler_->DidHandleInputEventOnMainThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureFlingCancel),
+ WebInputEventResult::kHandledSystem);
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureTapDown),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ scheduler_->DidHandleInputEventOnMainThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureTapDown),
+ WebInputEventResult::kHandledSystem);
+ RunUntilIdle();
+ EXPECT_THAT(run_order, testing::ElementsAre());
+
+ // Action events like ScrollBegin will kick us back into compositor priority,
+ // allowing service of the timer, loading and idle queues.
+ run_order.clear();
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureScrollBegin),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ scheduler_->DidHandleInputEventOnMainThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureScrollBegin),
+ WebInputEventResult::kHandledSystem);
+ RunUntilIdle();
+
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("L1"), std::string("T1"),
+ std::string("T2")));
+}
+
+// TODO(alexclarke): Reenable once we've reinstaed the Loading
+// UseCase.
+TEST_F(MainThreadSchedulerImplTest, DISABLED_LoadingUseCase) {
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "I1 D1 C1 T1 L1 D2 C2 T2 L2");
+
+ scheduler_->DidStartProvisionalLoad(true);
+ EnableIdleTasks();
+ RunUntilIdle();
+
+ // In loading policy, loading tasks are prioritized other others.
+ std::string loading_policy_expected[] = {
+ std::string("D1"), std::string("L1"), std::string("D2"),
+ std::string("L2"), std::string("C1"), std::string("T1"),
+ std::string("C2"), std::string("T2"), std::string("I1")};
+ EXPECT_THAT(run_order, testing::ElementsAreArray(loading_policy_expected));
+ EXPECT_EQ(UseCase::kLoading, CurrentUseCase());
+
+ // Advance 15s and try again, the loading policy should have ended and the
+ // task order should return to the NONE use case where loading tasks are no
+ // longer prioritized.
+ clock_.Advance(base::TimeDelta::FromMilliseconds(150000));
+ run_order.clear();
+ PostTestTasks(&run_order, "I1 D1 C1 T1 L1 D2 C2 T2 L2");
+ EnableIdleTasks();
+ RunUntilIdle();
+
+ std::string default_order_expected[] = {
+ std::string("D1"), std::string("C1"), std::string("T1"),
+ std::string("L1"), std::string("D2"), std::string("C2"),
+ std::string("T2"), std::string("L2"), std::string("I1")};
+ EXPECT_THAT(run_order, testing::ElementsAreArray(default_order_expected));
+ EXPECT_EQ(UseCase::kNone, CurrentUseCase());
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ EventConsumedOnCompositorThread_IgnoresMouseMove_WhenMouseUp) {
+ RunSlowCompositorTask();
+
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "I1 D1 C1 D2 C2");
+
+ EnableIdleTasks();
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kMouseMove),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+ RunUntilIdle();
+ // Note compositor tasks are not prioritized.
+ EXPECT_EQ(UseCase::kNone, CurrentUseCase());
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("D1"), std::string("C1"),
+ std::string("D2"), std::string("C2"),
+ std::string("I1")));
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ EventForwardedToMainThread_IgnoresMouseMove_WhenMouseUp) {
+ RunSlowCompositorTask();
+
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "I1 D1 C1 D2 C2");
+
+ EnableIdleTasks();
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kMouseMove),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ RunUntilIdle();
+ // Note compositor tasks are not prioritized.
+ EXPECT_EQ(UseCase::kNone, CurrentUseCase());
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("D1"), std::string("C1"),
+ std::string("D2"), std::string("C2"),
+ std::string("I1")));
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ EventConsumedOnCompositorThread_MouseMove_WhenMouseDown) {
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "I1 D1 C1 D2 C2");
+
+ // Note that currently the compositor will never consume mouse move events,
+ // but this test reflects what should happen if that was the case.
+ EnableIdleTasks();
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kMouseMove,
+ blink::WebInputEvent::kLeftButtonDown),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+ RunUntilIdle();
+ // Note compositor tasks deprioritized.
+ EXPECT_EQ(UseCase::kCompositorGesture, CurrentUseCase());
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("D1"), std::string("D2"),
+ std::string("C1"), std::string("C2"),
+ std::string("I1")));
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ EventForwardedToMainThread_MouseMove_WhenMouseDown) {
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "I1 D1 C1 D2 C2");
+
+ EnableIdleTasks();
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kMouseMove,
+ blink::WebInputEvent::kLeftButtonDown),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ RunUntilIdle();
+ // Note compositor tasks are prioritized.
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("C1"), std::string("C2"),
+ std::string("D1"), std::string("D2"),
+ std::string("I1")));
+ scheduler_->DidHandleInputEventOnMainThread(
+ FakeInputEvent(blink::WebInputEvent::kMouseMove,
+ blink::WebInputEvent::kLeftButtonDown),
+ WebInputEventResult::kHandledSystem);
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ EventForwardedToMainThread_MouseMove_WhenMouseDown_AfterMouseWheel) {
+ // Simulate a main thread driven mouse wheel scroll gesture.
+ SimulateMainThreadGestureStart(TouchEventPolicy::kSendTouchStart,
+ blink::WebInputEvent::kGestureScrollUpdate);
+ RunUntilIdle();
+ EXPECT_FALSE(TouchStartExpectedSoon());
+ EXPECT_EQ(UseCase::kMainThreadGesture, CurrentUseCase());
+
+ // Now start a main thread mouse touch gesture. It should be detected as main
+ // thread custom input handling.
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "I1 D1 C1 D2 C2");
+ EnableIdleTasks();
+
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kMouseDown,
+ blink::WebInputEvent::kLeftButtonDown),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kMouseMove,
+ blink::WebInputEvent::kLeftButtonDown),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ RunUntilIdle();
+
+ EXPECT_EQ(UseCase::kMainThreadCustomInputHandling, CurrentUseCase());
+
+ // Note compositor tasks are prioritized.
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("C1"), std::string("C2"),
+ std::string("D1"), std::string("D2"),
+ std::string("I1")));
+}
+
+TEST_F(MainThreadSchedulerImplTest, EventForwardedToMainThread_MouseClick) {
+ // A mouse click should be detected as main thread input handling, which means
+ // we won't try to defer expensive tasks because of one. We can, however,
+ // prioritize compositing/input handling.
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "I1 D1 C1 D2 C2");
+ EnableIdleTasks();
+
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kMouseDown,
+ blink::WebInputEvent::kLeftButtonDown),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kMouseUp,
+ blink::WebInputEvent::kLeftButtonDown),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ RunUntilIdle();
+
+ EXPECT_EQ(UseCase::kMainThreadCustomInputHandling, CurrentUseCase());
+
+ // Note compositor tasks are prioritized.
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("C1"), std::string("C2"),
+ std::string("D1"), std::string("D2"),
+ std::string("I1")));
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ EventConsumedOnCompositorThread_MouseWheel) {
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "I1 D1 C1 D2 C2");
+
+ EnableIdleTasks();
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kMouseWheel),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+ RunUntilIdle();
+ // Note compositor tasks are not prioritized.
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("D1"), std::string("D2"),
+ std::string("C1"), std::string("C2"),
+ std::string("I1")));
+ EXPECT_EQ(UseCase::kCompositorGesture, CurrentUseCase());
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ EventForwardedToMainThread_MouseWheel_PreventDefault) {
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "I1 D1 C1 D2 C2");
+
+ EnableIdleTasks();
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kMouseWheel),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ RunUntilIdle();
+ // Note compositor tasks are prioritized (since they are fast).
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("C1"), std::string("C2"),
+ std::string("D1"), std::string("D2"),
+ std::string("I1")));
+ EXPECT_EQ(UseCase::kMainThreadCustomInputHandling, CurrentUseCase());
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ EventForwardedToMainThread_NoPreventDefault) {
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "I1 D1 C1 D2 C2");
+
+ EnableIdleTasks();
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kMouseWheel),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureScrollBegin),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureScrollUpdate),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureScrollUpdate),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ RunUntilIdle();
+ // Note compositor tasks are prioritized.
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("C1"), std::string("C2"),
+ std::string("D1"), std::string("D2"),
+ std::string("I1")));
+ EXPECT_EQ(UseCase::kMainThreadGesture, CurrentUseCase());
+}
+
+TEST_F(
+ MainThreadSchedulerImplTest,
+ EventForwardedToMainThreadAndBackToCompositor_MouseWheel_NoPreventDefault) {
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "I1 D1 C1 D2 C2");
+
+ EnableIdleTasks();
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kMouseWheel),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureScrollBegin),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureScrollUpdate),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureScrollUpdate),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+ RunUntilIdle();
+ // Note compositor tasks are not prioritized.
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("D1"), std::string("D2"),
+ std::string("C1"), std::string("C2"),
+ std::string("I1")));
+ EXPECT_EQ(UseCase::kCompositorGesture, CurrentUseCase());
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ EventConsumedOnCompositorThread_IgnoresKeyboardEvents) {
+ RunSlowCompositorTask();
+
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "I1 D1 C1 D2 C2");
+
+ EnableIdleTasks();
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kKeyDown),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+ RunUntilIdle();
+ // Note compositor tasks are not prioritized.
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("D1"), std::string("C1"),
+ std::string("D2"), std::string("C2"),
+ std::string("I1")));
+ EXPECT_EQ(UseCase::kNone, CurrentUseCase());
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ EventForwardedToMainThread_IgnoresKeyboardEvents) {
+ RunSlowCompositorTask();
+
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "I1 D1 C1 D2 C2");
+
+ EnableIdleTasks();
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kKeyDown),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ RunUntilIdle();
+ // Note compositor tasks are not prioritized.
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("D1"), std::string("C1"),
+ std::string("D2"), std::string("C2"),
+ std::string("I1")));
+ EXPECT_EQ(UseCase::kNone, CurrentUseCase());
+ // Note compositor tasks are not prioritized.
+ scheduler_->DidHandleInputEventOnMainThread(
+ FakeInputEvent(blink::WebInputEvent::kKeyDown),
+ WebInputEventResult::kHandledSystem);
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ TestMainthreadScrollingUseCaseDoesNotStarveDefaultTasks) {
+ SimulateMainThreadGestureStart(TouchEventPolicy::kDontSendTouchStart,
+ blink::WebInputEvent::kGestureScrollBegin);
+ EnableIdleTasks();
+
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "D1 C1");
+
+ for (int i = 0; i < 20; i++) {
+ compositor_task_runner_->PostTask(FROM_HERE, base::BindOnce(&NullTask));
+ }
+ PostTestTasks(&run_order, "C2");
+
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureFlingStart),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+ RunUntilIdle();
+ // Ensure that the default D1 task gets to run at some point before the final
+ // C2 compositor task.
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("C1"), std::string("D1"),
+ std::string("C2")));
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ TestCompositorPolicyEnds_CompositorHandlesInput) {
+ SimulateCompositorGestureStart(TouchEventPolicy::kDontSendTouchStart);
+ EXPECT_EQ(UseCase::kCompositorGesture,
+ ForceUpdatePolicyAndGetCurrentUseCase());
+
+ clock_.Advance(base::TimeDelta::FromMilliseconds(1000));
+ EXPECT_EQ(UseCase::kNone, ForceUpdatePolicyAndGetCurrentUseCase());
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ TestCompositorPolicyEnds_MainThreadHandlesInput) {
+ SimulateMainThreadGestureStart(TouchEventPolicy::kDontSendTouchStart,
+ blink::WebInputEvent::kGestureScrollBegin);
+ EXPECT_EQ(UseCase::kMainThreadCustomInputHandling,
+ ForceUpdatePolicyAndGetCurrentUseCase());
+
+ clock_.Advance(base::TimeDelta::FromMilliseconds(1000));
+ EXPECT_EQ(UseCase::kNone, ForceUpdatePolicyAndGetCurrentUseCase());
+}
+
+TEST_F(MainThreadSchedulerImplTest, TestTouchstartPolicyEndsAfterTimeout) {
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "L1 D1 C1 D2 C2");
+
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchStart),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+ RunUntilIdle();
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("C1"), std::string("C2"),
+ std::string("D1"), std::string("D2")));
+
+ run_order.clear();
+ clock_.Advance(base::TimeDelta::FromMilliseconds(1000));
+
+ // Don't post any compositor tasks to simulate a very long running event
+ // handler.
+ PostTestTasks(&run_order, "D1 D2");
+
+ // Touchstart policy mode should have ended now that the clock has advanced.
+ RunUntilIdle();
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("L1"), std::string("D1"),
+ std::string("D2")));
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ TestTouchstartPolicyEndsAfterConsecutiveTouchmoves) {
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "L1 D1 C1 D2 C2");
+
+ // Observation of touchstart should defer execution of idle and loading tasks.
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchStart),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+ RunUntilIdle();
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("C1"), std::string("C2"),
+ std::string("D1"), std::string("D2")));
+
+ // Receiving the first touchmove will not affect scheduler priority.
+ run_order.clear();
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchMove),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+ RunUntilIdle();
+ EXPECT_THAT(run_order, testing::ElementsAre());
+
+ // Receiving the second touchmove will kick us back into compositor priority.
+ run_order.clear();
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchMove),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+ RunUntilIdle();
+ EXPECT_THAT(run_order, testing::ElementsAre(std::string("L1")));
+}
+
+TEST_F(MainThreadSchedulerImplTest, TestIsHighPriorityWorkAnticipated) {
+ bool is_anticipated_before = false;
+ bool is_anticipated_after = false;
+
+ scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
+ default_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&AnticipationTestTask, scheduler_.get(),
+ SimulateInputType::kNone,
+ &is_anticipated_before, &is_anticipated_after));
+ RunUntilIdle();
+ // In its default state, without input receipt, the scheduler should indicate
+ // that no high-priority is anticipated.
+ EXPECT_FALSE(is_anticipated_before);
+ EXPECT_FALSE(is_anticipated_after);
+
+ default_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&AnticipationTestTask, scheduler_.get(),
+ SimulateInputType::kTouchStart,
+ &is_anticipated_before, &is_anticipated_after));
+ bool dummy;
+ default_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&AnticipationTestTask, scheduler_.get(),
+ SimulateInputType::kTouchEnd, &dummy, &dummy));
+ default_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&AnticipationTestTask, scheduler_.get(),
+ SimulateInputType::kGestureScrollBegin, &dummy, &dummy));
+ default_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&AnticipationTestTask, scheduler_.get(),
+ SimulateInputType::kGestureScrollEnd, &dummy, &dummy));
+
+ RunUntilIdle();
+ // When input is received, the scheduler should indicate that high-priority
+ // work is anticipated.
+ EXPECT_FALSE(is_anticipated_before);
+ EXPECT_TRUE(is_anticipated_after);
+
+ clock_.Advance(priority_escalation_after_input_duration() * 2);
+ default_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&AnticipationTestTask, scheduler_.get(),
+ SimulateInputType::kNone,
+ &is_anticipated_before, &is_anticipated_after));
+ RunUntilIdle();
+ // Without additional input, the scheduler should go into NONE
+ // use case but with scrolling expected where high-priority work is still
+ // anticipated.
+ EXPECT_EQ(UseCase::kNone, CurrentUseCase());
+ EXPECT_TRUE(TouchStartExpectedSoon());
+ EXPECT_TRUE(is_anticipated_before);
+ EXPECT_TRUE(is_anticipated_after);
+
+ clock_.Advance(subsequent_input_expected_after_input_duration() * 2);
+ default_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&AnticipationTestTask, scheduler_.get(),
+ SimulateInputType::kNone,
+ &is_anticipated_before, &is_anticipated_after));
+ RunUntilIdle();
+ // Eventually the scheduler should go into the default use case where
+ // high-priority work is no longer anticipated.
+ EXPECT_EQ(UseCase::kNone, CurrentUseCase());
+ EXPECT_FALSE(TouchStartExpectedSoon());
+ EXPECT_FALSE(is_anticipated_before);
+ EXPECT_FALSE(is_anticipated_after);
+}
+
+TEST_F(MainThreadSchedulerImplTest, TestShouldYield) {
+ bool should_yield_before = false;
+ bool should_yield_after = false;
+
+ default_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&PostingYieldingTestTask, scheduler_.get(),
+ base::RetainedRef(default_task_runner_), false,
+ &should_yield_before, &should_yield_after));
+ RunUntilIdle();
+ // Posting to default runner shouldn't cause yielding.
+ EXPECT_FALSE(should_yield_before);
+ EXPECT_FALSE(should_yield_after);
+
+ default_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&PostingYieldingTestTask, scheduler_.get(),
+ base::RetainedRef(compositor_task_runner_), false,
+ &should_yield_before, &should_yield_after));
+ RunUntilIdle();
+ // Posting while not mainthread scrolling shouldn't cause yielding.
+ EXPECT_FALSE(should_yield_before);
+ EXPECT_FALSE(should_yield_after);
+
+ default_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&PostingYieldingTestTask, scheduler_.get(),
+ base::RetainedRef(compositor_task_runner_), true,
+ &should_yield_before, &should_yield_after));
+ RunUntilIdle();
+ // We should be able to switch to compositor priority mid-task.
+ EXPECT_FALSE(should_yield_before);
+ EXPECT_TRUE(should_yield_after);
+}
+
+TEST_F(MainThreadSchedulerImplTest, TestShouldYield_TouchStart) {
+ // Receiving a touchstart should immediately trigger yielding, even if
+ // there's no immediately pending work in the compositor queue.
+ EXPECT_FALSE(scheduler_->ShouldYieldForHighPriorityWork());
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchStart),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+ EXPECT_TRUE(scheduler_->ShouldYieldForHighPriorityWork());
+ RunUntilIdle();
+}
+
+TEST_F(MainThreadSchedulerImplTest, SlowMainThreadInputEvent) {
+ EXPECT_EQ(UseCase::kNone, CurrentUseCase());
+
+ // An input event should bump us into input priority.
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureFlingStart),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ RunUntilIdle();
+ EXPECT_EQ(UseCase::kMainThreadCustomInputHandling, CurrentUseCase());
+
+ // Simulate the input event being queued for a very long time. The compositor
+ // task we post here represents the enqueued input task.
+ clock_.Advance(priority_escalation_after_input_duration() * 2);
+ scheduler_->DidHandleInputEventOnMainThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureFlingStart),
+ WebInputEventResult::kHandledSystem);
+ RunUntilIdle();
+
+ // Even though we exceeded the input priority escalation period, we should
+ // still be in main thread gesture since the input remains queued.
+ EXPECT_EQ(UseCase::kMainThreadCustomInputHandling, CurrentUseCase());
+
+ // After the escalation period ends we should go back into normal mode.
+ clock_.Advance(priority_escalation_after_input_duration() * 2);
+ RunUntilIdle();
+ EXPECT_EQ(UseCase::kNone, CurrentUseCase());
+}
+
+class MainThreadSchedulerImplWithMockSchedulerTest
+ : public MainThreadSchedulerImplTest {
+ public:
+ void SetUp() override {
+ mock_task_runner_ =
+ base::MakeRefCounted<cc::OrderedSimpleTaskRunner>(&clock_, false);
+ mock_scheduler_ = new MainThreadSchedulerImplForTest(
+ TaskQueueManagerForTest::Create(nullptr, mock_task_runner_, &clock_),
+ base::nullopt);
+ Initialize(base::WrapUnique(mock_scheduler_));
+ }
+
+ protected:
+ MainThreadSchedulerImplForTest* mock_scheduler_;
+};
+
+TEST_F(MainThreadSchedulerImplWithMockSchedulerTest,
+ OnlyOnePendingUrgentPolicyUpdatey) {
+ mock_scheduler_->EnsureUrgentPolicyUpdatePostedOnMainThread();
+ mock_scheduler_->EnsureUrgentPolicyUpdatePostedOnMainThread();
+ mock_scheduler_->EnsureUrgentPolicyUpdatePostedOnMainThread();
+ mock_scheduler_->EnsureUrgentPolicyUpdatePostedOnMainThread();
+
+ RunUntilIdle();
+
+ EXPECT_EQ(1, mock_scheduler_->update_policy_count_);
+}
+
+TEST_F(MainThreadSchedulerImplWithMockSchedulerTest,
+ OnePendingDelayedAndOneUrgentUpdatePolicy) {
+ ScopedAutoAdvanceNowEnabler enable_auto_advance_now(mock_task_runner_);
+
+ mock_scheduler_->ScheduleDelayedPolicyUpdate(
+ clock_.NowTicks(), base::TimeDelta::FromMilliseconds(1));
+ mock_scheduler_->EnsureUrgentPolicyUpdatePostedOnMainThread();
+
+ RunUntilIdle();
+
+ // We expect both the urgent and the delayed updates to run.
+ EXPECT_EQ(2, mock_scheduler_->update_policy_count_);
+}
+
+TEST_F(MainThreadSchedulerImplWithMockSchedulerTest,
+ OneUrgentAndOnePendingDelayedUpdatePolicy) {
+ ScopedAutoAdvanceNowEnabler enable_auto_advance_now(mock_task_runner_);
+
+ mock_scheduler_->EnsureUrgentPolicyUpdatePostedOnMainThread();
+ mock_scheduler_->ScheduleDelayedPolicyUpdate(
+ clock_.NowTicks(), base::TimeDelta::FromMilliseconds(1));
+
+ RunUntilIdle();
+
+ // We expect both the urgent and the delayed updates to run.
+ EXPECT_EQ(2, mock_scheduler_->update_policy_count_);
+}
+
+TEST_F(MainThreadSchedulerImplWithMockSchedulerTest,
+ UpdatePolicyCountTriggeredByOneInputEvent) {
+ // We expect DidHandleInputEventOnCompositorThread to post an urgent policy
+ // update.
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchStart),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ EXPECT_EQ(0, mock_scheduler_->update_policy_count_);
+ mock_task_runner_->RunPendingTasks();
+ EXPECT_EQ(1, mock_scheduler_->update_policy_count_);
+
+ scheduler_->DidHandleInputEventOnMainThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchStart),
+ WebInputEventResult::kHandledSystem);
+ EXPECT_EQ(1, mock_scheduler_->update_policy_count_);
+
+ clock_.Advance(base::TimeDelta::FromMilliseconds(1000));
+ RunUntilIdle();
+
+ // We finally expect a delayed policy update 100ms later.
+ EXPECT_EQ(2, mock_scheduler_->update_policy_count_);
+}
+
+TEST_F(MainThreadSchedulerImplWithMockSchedulerTest,
+ UpdatePolicyCountTriggeredByThreeInputEvents) {
+ // We expect DidHandleInputEventOnCompositorThread to post an urgent policy
+ // update.
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchStart),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ EXPECT_EQ(0, mock_scheduler_->update_policy_count_);
+ mock_task_runner_->RunPendingTasks();
+ EXPECT_EQ(1, mock_scheduler_->update_policy_count_);
+
+ scheduler_->DidHandleInputEventOnMainThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchStart),
+ WebInputEventResult::kHandledSystem);
+ EXPECT_EQ(1, mock_scheduler_->update_policy_count_);
+
+ // The second call to DidHandleInputEventOnCompositorThread should not post a
+ // policy update because we are already in compositor priority.
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchMove),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ mock_task_runner_->RunPendingTasks();
+ EXPECT_EQ(1, mock_scheduler_->update_policy_count_);
+
+ // We expect DidHandleInputEvent to trigger a policy update.
+ scheduler_->DidHandleInputEventOnMainThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchMove),
+ WebInputEventResult::kHandledSystem);
+ EXPECT_EQ(1, mock_scheduler_->update_policy_count_);
+
+ // The third call to DidHandleInputEventOnCompositorThread should post a
+ // policy update because the awaiting_touch_start_response_ flag changed.
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchMove),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ EXPECT_EQ(1, mock_scheduler_->update_policy_count_);
+ mock_task_runner_->RunPendingTasks();
+ EXPECT_EQ(2, mock_scheduler_->update_policy_count_);
+
+ // We expect DidHandleInputEvent to trigger a policy update.
+ scheduler_->DidHandleInputEventOnMainThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchMove),
+ WebInputEventResult::kHandledSystem);
+ EXPECT_EQ(2, mock_scheduler_->update_policy_count_);
+
+ clock_.Advance(base::TimeDelta::FromMilliseconds(1000));
+ RunUntilIdle();
+
+ // We finally expect a delayed policy update.
+ EXPECT_EQ(3, mock_scheduler_->update_policy_count_);
+}
+
+TEST_F(MainThreadSchedulerImplWithMockSchedulerTest,
+ UpdatePolicyCountTriggeredByTwoInputEventsWithALongSeparatingDelay) {
+ // We expect DidHandleInputEventOnCompositorThread to post an urgent policy
+ // update.
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchStart),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ EXPECT_EQ(0, mock_scheduler_->update_policy_count_);
+ mock_task_runner_->RunPendingTasks();
+ EXPECT_EQ(1, mock_scheduler_->update_policy_count_);
+
+ scheduler_->DidHandleInputEventOnMainThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchStart),
+ WebInputEventResult::kHandledSystem);
+ EXPECT_EQ(1, mock_scheduler_->update_policy_count_);
+
+ clock_.Advance(base::TimeDelta::FromMilliseconds(1000));
+ RunUntilIdle();
+ // We expect a delayed policy update.
+ EXPECT_EQ(2, mock_scheduler_->update_policy_count_);
+
+ // We expect the second call to DidHandleInputEventOnCompositorThread to post
+ // an urgent policy update because we are no longer in compositor priority.
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchMove),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ EXPECT_EQ(2, mock_scheduler_->update_policy_count_);
+ mock_task_runner_->RunPendingTasks();
+ EXPECT_EQ(3, mock_scheduler_->update_policy_count_);
+
+ scheduler_->DidHandleInputEventOnMainThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchMove),
+ WebInputEventResult::kHandledSystem);
+ EXPECT_EQ(3, mock_scheduler_->update_policy_count_);
+
+ clock_.Advance(base::TimeDelta::FromMilliseconds(1000));
+ RunUntilIdle();
+
+ // We finally expect a delayed policy update.
+ EXPECT_EQ(4, mock_scheduler_->update_policy_count_);
+}
+
+TEST_F(MainThreadSchedulerImplWithMockSchedulerTest,
+ EnsureUpdatePolicyNotTriggeredTooOften) {
+ ScopedAutoAdvanceNowEnabler enable_auto_advance_now(mock_task_runner_);
+
+ EXPECT_EQ(0, mock_scheduler_->update_policy_count_);
+ scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
+ EXPECT_EQ(1, mock_scheduler_->update_policy_count_);
+
+ SimulateCompositorGestureStart(TouchEventPolicy::kSendTouchStart);
+
+ // We expect the first call to IsHighPriorityWorkAnticipated to be called
+ // after receiving an input event (but before the UpdateTask was processed) to
+ // call UpdatePolicy.
+ EXPECT_EQ(1, mock_scheduler_->update_policy_count_);
+ scheduler_->IsHighPriorityWorkAnticipated();
+ EXPECT_EQ(2, mock_scheduler_->update_policy_count_);
+ // Subsequent calls should not call UpdatePolicy.
+ scheduler_->IsHighPriorityWorkAnticipated();
+ scheduler_->IsHighPriorityWorkAnticipated();
+ scheduler_->IsHighPriorityWorkAnticipated();
+ scheduler_->ShouldYieldForHighPriorityWork();
+ scheduler_->ShouldYieldForHighPriorityWork();
+ scheduler_->ShouldYieldForHighPriorityWork();
+ scheduler_->ShouldYieldForHighPriorityWork();
+
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureScrollEnd),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchEnd),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+
+ scheduler_->DidHandleInputEventOnMainThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchStart),
+ WebInputEventResult::kHandledSystem);
+ scheduler_->DidHandleInputEventOnMainThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchMove),
+ WebInputEventResult::kHandledSystem);
+ scheduler_->DidHandleInputEventOnMainThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchMove),
+ WebInputEventResult::kHandledSystem);
+ scheduler_->DidHandleInputEventOnMainThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchEnd),
+ WebInputEventResult::kHandledSystem);
+
+ EXPECT_EQ(2, mock_scheduler_->update_policy_count_);
+
+ // We expect both the urgent and the delayed updates to run in addition to the
+ // earlier updated cause by IsHighPriorityWorkAnticipated, a final update
+ // transitions from 'not_scrolling touchstart expected' to 'not_scrolling'.
+ RunUntilIdle();
+ EXPECT_THAT(
+ mock_scheduler_->use_cases_,
+ testing::ElementsAre(
+ std::string("none"), std::string("compositor_gesture"),
+ std::string("compositor_gesture touchstart expected"),
+ std::string("none touchstart expected"), std::string("none")));
+}
+
+class MainThreadSchedulerImplWithMessageLoopTest
+ : public MainThreadSchedulerImplTest {
+ public:
+ MainThreadSchedulerImplWithMessageLoopTest()
+ : MainThreadSchedulerImplTest(new base::MessageLoop()) {}
+ ~MainThreadSchedulerImplWithMessageLoopTest() override = default;
+
+ void PostFromNestedRunloop(
+ std::vector<std::pair<SingleThreadIdleTaskRunner::IdleTask, bool>>*
+ tasks) {
+ base::MessageLoop::ScopedNestableTaskAllower allow(message_loop_.get());
+ for (std::pair<SingleThreadIdleTaskRunner::IdleTask, bool>& pair : *tasks) {
+ if (pair.second) {
+ idle_task_runner_->PostIdleTask(FROM_HERE, std::move(pair.first));
+ } else {
+ idle_task_runner_->PostNonNestableIdleTask(FROM_HERE,
+ std::move(pair.first));
+ }
+ }
+ EnableIdleTasks();
+ base::RunLoop().RunUntilIdle();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MainThreadSchedulerImplWithMessageLoopTest);
+};
+
+TEST_F(MainThreadSchedulerImplWithMessageLoopTest,
+ NonNestableIdleTaskDoesntExecuteInNestedLoop) {
+ std::vector<std::string> order;
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE,
+ base::BindOnce(&AppendToVectorIdleTestTask, &order, std::string("1")));
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE,
+ base::BindOnce(&AppendToVectorIdleTestTask, &order, std::string("2")));
+
+ std::vector<std::pair<SingleThreadIdleTaskRunner::IdleTask, bool>>
+ tasks_to_post_from_nested_loop;
+ tasks_to_post_from_nested_loop.push_back(std::make_pair(
+ base::BindOnce(&AppendToVectorIdleTestTask, &order, std::string("3")),
+ false));
+ tasks_to_post_from_nested_loop.push_back(std::make_pair(
+ base::BindOnce(&AppendToVectorIdleTestTask, &order, std::string("4")),
+ true));
+ tasks_to_post_from_nested_loop.push_back(std::make_pair(
+ base::BindOnce(&AppendToVectorIdleTestTask, &order, std::string("5")),
+ true));
+
+ default_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &MainThreadSchedulerImplWithMessageLoopTest::PostFromNestedRunloop,
+ base::Unretained(this),
+ base::Unretained(&tasks_to_post_from_nested_loop)));
+
+ EnableIdleTasks();
+ RunUntilIdle();
+ // Note we expect task 3 to run last because it's non-nestable.
+ EXPECT_THAT(order, testing::ElementsAre(std::string("1"), std::string("2"),
+ std::string("4"), std::string("5"),
+ std::string("3")));
+}
+
+TEST_F(MainThreadSchedulerImplTest, TestBeginMainFrameNotExpectedUntil) {
+ base::TimeDelta ten_millis(base::TimeDelta::FromMilliseconds(10));
+ base::TimeTicks expected_deadline = clock_.NowTicks() + ten_millis;
+ base::TimeTicks deadline_in_task;
+ int run_count = 0;
+
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE, base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task));
+
+ RunUntilIdle();
+ EXPECT_EQ(0, run_count); // Shouldn't run yet as no idle period.
+
+ base::TimeTicks now = clock_.NowTicks();
+ base::TimeTicks frame_time = now + ten_millis;
+ // No main frame is expected until frame_time, so short idle work can be
+ // scheduled in the mean time.
+ scheduler_->BeginMainFrameNotExpectedUntil(frame_time);
+ RunUntilIdle();
+ EXPECT_EQ(1, run_count); // Should have run in a long idle time.
+ EXPECT_EQ(expected_deadline, deadline_in_task);
+}
+
+TEST_F(MainThreadSchedulerImplTest, TestLongIdlePeriod) {
+ base::TimeTicks expected_deadline =
+ clock_.NowTicks() + maximum_idle_period_duration();
+ base::TimeTicks deadline_in_task;
+ int run_count = 0;
+
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE, base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task));
+
+ RunUntilIdle();
+ EXPECT_EQ(0, run_count); // Shouldn't run yet as no idle period.
+
+ scheduler_->BeginFrameNotExpectedSoon();
+ RunUntilIdle();
+ EXPECT_EQ(1, run_count); // Should have run in a long idle time.
+ EXPECT_EQ(expected_deadline, deadline_in_task);
+}
+
+TEST_F(MainThreadSchedulerImplTest, TestLongIdlePeriodWithPendingDelayedTask) {
+ base::TimeDelta pending_task_delay = base::TimeDelta::FromMilliseconds(30);
+ base::TimeTicks expected_deadline = clock_.NowTicks() + pending_task_delay;
+ base::TimeTicks deadline_in_task;
+ int run_count = 0;
+
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE, base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task));
+ default_task_runner_->PostDelayedTask(FROM_HERE, base::BindOnce(&NullTask),
+ pending_task_delay);
+
+ scheduler_->BeginFrameNotExpectedSoon();
+ RunUntilIdle();
+ EXPECT_EQ(1, run_count); // Should have run in a long idle time.
+ EXPECT_EQ(expected_deadline, deadline_in_task);
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ TestLongIdlePeriodWithLatePendingDelayedTask) {
+ base::TimeDelta pending_task_delay = base::TimeDelta::FromMilliseconds(10);
+ base::TimeTicks deadline_in_task;
+ int run_count = 0;
+
+ default_task_runner_->PostDelayedTask(FROM_HERE, base::BindOnce(&NullTask),
+ pending_task_delay);
+
+ // Advance clock until after delayed task was meant to be run.
+ clock_.Advance(base::TimeDelta::FromMilliseconds(20));
+
+ // Post an idle task and BeginFrameNotExpectedSoon to initiate a long idle
+ // period. Since there is a late pending delayed task this shouldn't actually
+ // start an idle period.
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE, base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task));
+ scheduler_->BeginFrameNotExpectedSoon();
+ RunUntilIdle();
+ EXPECT_EQ(0, run_count);
+
+ // After the delayed task has been run we should trigger an idle period.
+ clock_.Advance(maximum_idle_period_duration());
+ RunUntilIdle();
+ EXPECT_EQ(1, run_count);
+}
+
+TEST_F(MainThreadSchedulerImplTest, TestLongIdlePeriodRepeating) {
+ ScopedAutoAdvanceNowEnabler enable_auto_advance_now(mock_task_runner_);
+ std::vector<base::TimeTicks> actual_deadlines;
+ int run_count = 0;
+
+ g_max_idle_task_reposts = 3;
+ base::TimeTicks clock_before(clock_.NowTicks());
+ base::TimeDelta idle_task_runtime(base::TimeDelta::FromMilliseconds(10));
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE,
+ base::BindOnce(&RepostingUpdateClockIdleTestTask,
+ base::RetainedRef(idle_task_runner_), &run_count, &clock_,
+ idle_task_runtime, &actual_deadlines));
+ scheduler_->BeginFrameNotExpectedSoon();
+ RunUntilIdle();
+ EXPECT_EQ(3, run_count);
+ EXPECT_THAT(
+ actual_deadlines,
+ testing::ElementsAre(clock_before + maximum_idle_period_duration(),
+ clock_before + 2 * maximum_idle_period_duration(),
+ clock_before + 3 * maximum_idle_period_duration()));
+
+ // Check that idle tasks don't run after the idle period ends with a
+ // new BeginMainFrame.
+ g_max_idle_task_reposts = 5;
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE,
+ base::BindOnce(&RepostingUpdateClockIdleTestTask,
+ base::RetainedRef(idle_task_runner_), &run_count, &clock_,
+ idle_task_runtime, &actual_deadlines));
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE, base::BindOnce(&WillBeginFrameIdleTask,
+ base::Unretained(scheduler_.get()),
+ next_begin_frame_number_++, &clock_));
+ RunUntilIdle();
+ EXPECT_EQ(4, run_count);
+}
+
+TEST_F(MainThreadSchedulerImplTest, TestLongIdlePeriodInTouchStartPolicy) {
+ base::TimeTicks deadline_in_task;
+ int run_count = 0;
+
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE, base::BindOnce(&IdleTestTask, &run_count, &deadline_in_task));
+
+ // Observation of touchstart should defer the start of the long idle period.
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchStart),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+ scheduler_->BeginFrameNotExpectedSoon();
+ RunUntilIdle();
+ EXPECT_EQ(0, run_count);
+
+ // The long idle period should start after the touchstart policy has finished.
+ clock_.Advance(priority_escalation_after_input_duration());
+ RunUntilIdle();
+ EXPECT_EQ(1, run_count);
+}
+
+void TestCanExceedIdleDeadlineIfRequiredTask(WebMainThreadScheduler* scheduler,
+ bool* can_exceed_idle_deadline_out,
+ int* run_count,
+ base::TimeTicks deadline) {
+ *can_exceed_idle_deadline_out = scheduler->CanExceedIdleDeadlineIfRequired();
+ (*run_count)++;
+}
+
+TEST_F(MainThreadSchedulerImplTest, CanExceedIdleDeadlineIfRequired) {
+ int run_count = 0;
+ bool can_exceed_idle_deadline = false;
+
+ // Should return false if not in an idle period.
+ EXPECT_FALSE(scheduler_->CanExceedIdleDeadlineIfRequired());
+
+ // Should return false for short idle periods.
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE,
+ base::BindOnce(&TestCanExceedIdleDeadlineIfRequiredTask, scheduler_.get(),
+ &can_exceed_idle_deadline, &run_count));
+ EnableIdleTasks();
+ RunUntilIdle();
+ EXPECT_EQ(1, run_count);
+ EXPECT_FALSE(can_exceed_idle_deadline);
+
+ // Should return false for a long idle period which is shortened due to a
+ // pending delayed task.
+ default_task_runner_->PostDelayedTask(FROM_HERE, base::BindOnce(&NullTask),
+ base::TimeDelta::FromMilliseconds(10));
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE,
+ base::BindOnce(&TestCanExceedIdleDeadlineIfRequiredTask, scheduler_.get(),
+ &can_exceed_idle_deadline, &run_count));
+ scheduler_->BeginFrameNotExpectedSoon();
+ RunUntilIdle();
+ EXPECT_EQ(2, run_count);
+ EXPECT_FALSE(can_exceed_idle_deadline);
+
+ // Next long idle period will be for the maximum time, so
+ // CanExceedIdleDeadlineIfRequired should return true.
+ clock_.Advance(maximum_idle_period_duration());
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE,
+ base::BindOnce(&TestCanExceedIdleDeadlineIfRequiredTask, scheduler_.get(),
+ &can_exceed_idle_deadline, &run_count));
+ RunUntilIdle();
+ EXPECT_EQ(3, run_count);
+ EXPECT_TRUE(can_exceed_idle_deadline);
+
+ // Next long idle period will be for the maximum time, so
+ // CanExceedIdleDeadlineIfRequired should return true.
+ scheduler_->WillBeginFrame(viz::BeginFrameArgs::Create(
+ BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_.NowTicks(),
+ base::TimeTicks(), base::TimeDelta::FromMilliseconds(1000),
+ viz::BeginFrameArgs::NORMAL));
+ EXPECT_FALSE(scheduler_->CanExceedIdleDeadlineIfRequired());
+}
+
+TEST_F(MainThreadSchedulerImplTest, TestRendererHiddenIdlePeriod) {
+ ScopedAutoAdvanceNowEnabler enable_auto_advance_now(mock_task_runner_);
+
+ int run_count = 0;
+
+ g_max_idle_task_reposts = 2;
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE,
+ base::BindOnce(&RepostingIdleTestTask,
+ base::RetainedRef(idle_task_runner_), &run_count));
+
+ // Renderer should start in visible state.
+ RunUntilIdle();
+ EXPECT_EQ(0, run_count);
+
+ // When we hide the renderer it should start a max deadline idle period, which
+ // will run an idle task and then immediately start a new idle period, which
+ // runs the second idle task.
+ scheduler_->SetAllRenderWidgetsHidden(true);
+ RunUntilIdle();
+ EXPECT_EQ(2, run_count);
+
+ // Advance time by amount of time by the maximum amount of time we execute
+ // idle tasks when hidden (plus some slack) - idle period should have ended.
+ g_max_idle_task_reposts = 3;
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE,
+ base::BindOnce(&RepostingIdleTestTask,
+ base::RetainedRef(idle_task_runner_), &run_count));
+ clock_.Advance(end_idle_when_hidden_delay() +
+ base::TimeDelta::FromMilliseconds(10));
+ RunUntilIdle();
+ EXPECT_EQ(2, run_count);
+}
+
+TEST_F(MainThreadSchedulerImplTest, TimerQueueEnabledByDefault) {
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "T1 T2");
+ RunUntilIdle();
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("T1"), std::string("T2")));
+}
+
+TEST_F(MainThreadSchedulerImplTest, StopAndResumeRenderer) {
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "T1 T2");
+
+ auto pause_handle = scheduler_->PauseRenderer();
+ RunUntilIdle();
+ EXPECT_THAT(run_order, testing::ElementsAre());
+
+ pause_handle.reset();
+ RunUntilIdle();
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("T1"), std::string("T2")));
+}
+
+TEST_F(MainThreadSchedulerImplTest, StopAndThrottleTimerQueue) {
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "T1 T2");
+
+ auto pause_handle = scheduler_->PauseRenderer();
+ RunUntilIdle();
+ scheduler_->task_queue_throttler()->IncreaseThrottleRefCount(
+ static_cast<TaskQueue*>(timer_task_runner_.get()));
+ RunUntilIdle();
+ EXPECT_THAT(run_order, testing::ElementsAre());
+}
+
+TEST_F(MainThreadSchedulerImplTest, ThrottleAndPauseRenderer) {
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "T1 T2");
+
+ scheduler_->task_queue_throttler()->IncreaseThrottleRefCount(
+ static_cast<TaskQueue*>(timer_task_runner_.get()));
+ RunUntilIdle();
+ auto pause_handle = scheduler_->PauseRenderer();
+ RunUntilIdle();
+ EXPECT_THAT(run_order, testing::ElementsAre());
+}
+
+TEST_F(MainThreadSchedulerImplTest, MultipleStopsNeedMultipleResumes) {
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "T1 T2");
+
+ auto pause_handle1 = scheduler_->PauseRenderer();
+ auto pause_handle2 = scheduler_->PauseRenderer();
+ auto pause_handle3 = scheduler_->PauseRenderer();
+ RunUntilIdle();
+ EXPECT_THAT(run_order, testing::ElementsAre());
+
+ pause_handle1.reset();
+ RunUntilIdle();
+ EXPECT_THAT(run_order, testing::ElementsAre());
+
+ pause_handle2.reset();
+ RunUntilIdle();
+ EXPECT_THAT(run_order, testing::ElementsAre());
+
+ pause_handle3.reset();
+ RunUntilIdle();
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("T1"), std::string("T2")));
+}
+
+TEST_F(MainThreadSchedulerImplTest, PauseRenderer) {
+ // Tasks in some queues don't fire when the renderer is paused.
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "D1 C1 L1 I1 T1");
+ auto pause_handle = scheduler_->PauseRenderer();
+ EnableIdleTasks();
+ RunUntilIdle();
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("D1"), std::string("C1"),
+ std::string("I1")));
+
+ // Tasks are executed when renderer is resumed.
+ run_order.clear();
+ pause_handle.reset();
+ RunUntilIdle();
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("L1"), std::string("T1")));
+}
+
+TEST_F(MainThreadSchedulerImplTest, UseCaseToString) {
+ CheckAllUseCaseToString();
+}
+
+TEST_F(MainThreadSchedulerImplTest, MismatchedDidHandleInputEventOnMainThread) {
+ // This should not DCHECK because there was no corresponding compositor side
+ // call to DidHandleInputEventOnCompositorThread with
+ // INPUT_EVENT_ACK_STATE_NOT_CONSUMED. There are legitimate reasons for the
+ // compositor to not be there and we don't want to make debugging impossible.
+ scheduler_->DidHandleInputEventOnMainThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureFlingStart),
+ WebInputEventResult::kHandledSystem);
+}
+
+TEST_F(MainThreadSchedulerImplTest, BeginMainFrameOnCriticalPath) {
+ ASSERT_FALSE(scheduler_->BeginMainFrameOnCriticalPath());
+
+ viz::BeginFrameArgs begin_frame_args = viz::BeginFrameArgs::Create(
+ BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_.NowTicks(),
+ base::TimeTicks(), base::TimeDelta::FromMilliseconds(1000),
+ viz::BeginFrameArgs::NORMAL);
+ scheduler_->WillBeginFrame(begin_frame_args);
+ ASSERT_TRUE(scheduler_->BeginMainFrameOnCriticalPath());
+
+ begin_frame_args.on_critical_path = false;
+ scheduler_->WillBeginFrame(begin_frame_args);
+ ASSERT_FALSE(scheduler_->BeginMainFrameOnCriticalPath());
+}
+
+TEST_F(MainThreadSchedulerImplTest, ShutdownPreventsPostingOfNewTasks) {
+ scheduler_->Shutdown();
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "D1 C1");
+ RunUntilIdle();
+ EXPECT_THAT(run_order, testing::ElementsAre());
+}
+
+TEST_F(MainThreadSchedulerImplTest, TestRendererBackgroundedTimerSuspension) {
+ scheduler_->SetStoppingWhenBackgroundedEnabled(true);
+
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "T1 T2");
+
+ base::TimeTicks now;
+
+ // The background signal will not immediately suspend the timer queue.
+ scheduler_->SetRendererBackgrounded(true);
+ now += base::TimeDelta::FromMilliseconds(1100);
+ clock_.SetNowTicks(now);
+ RunUntilIdle();
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("T1"), std::string("T2")));
+
+ run_order.clear();
+ PostTestTasks(&run_order, "T3");
+
+ now += base::TimeDelta::FromSeconds(1);
+ clock_.SetNowTicks(now);
+ RunUntilIdle();
+ EXPECT_THAT(run_order, testing::ElementsAre(std::string("T3")));
+
+ // Advance the time until after the scheduled timer queue suspension.
+ now = base::TimeTicks() + delay_for_background_tab_stopping() +
+ base::TimeDelta::FromMilliseconds(10);
+ run_order.clear();
+ clock_.SetNowTicks(now);
+ RunUntilIdle();
+ ASSERT_TRUE(run_order.empty());
+
+ // Timer tasks should be paused until the foregrounded signal.
+ PostTestTasks(&run_order, "T4 T5 V1");
+ now += base::TimeDelta::FromSeconds(10);
+ clock_.SetNowTicks(now);
+ RunUntilIdle();
+ EXPECT_THAT(run_order, testing::ElementsAre(std::string("V1")));
+
+ run_order.clear();
+ scheduler_->SetRendererBackgrounded(false);
+ RunUntilIdle();
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("T4"), std::string("T5")));
+
+ // Subsequent timer tasks should fire as usual.
+ run_order.clear();
+ PostTestTasks(&run_order, "T6");
+ RunUntilIdle();
+ EXPECT_THAT(run_order, testing::ElementsAre(std::string("T6")));
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ ExpensiveLoadingTasksNotBlockedTillFirstBeginMainFrame) {
+ std::vector<std::string> run_order;
+
+ scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
+ SimulateExpensiveTasks(loading_task_runner_);
+ ForceTouchStartToBeExpectedSoon();
+ PostTestTasks(&run_order, "L1 D1");
+ RunUntilIdle();
+
+ EXPECT_EQ(UseCase::kNone, ForceUpdatePolicyAndGetCurrentUseCase());
+ EXPECT_FALSE(HaveSeenABeginMainframe());
+ EXPECT_TRUE(LoadingTasksSeemExpensive());
+ EXPECT_FALSE(TimerTasksSeemExpensive());
+ EXPECT_TRUE(TouchStartExpectedSoon());
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("L1"), std::string("D1")));
+
+ // Emit a BeginMainFrame, and the loading task should get blocked.
+ DoMainFrame();
+ run_order.clear();
+
+ PostTestTasks(&run_order, "L1 D1");
+ RunUntilIdle();
+
+ EXPECT_EQ(UseCase::kNone, CurrentUseCase());
+ EXPECT_TRUE(HaveSeenABeginMainframe());
+ EXPECT_TRUE(LoadingTasksSeemExpensive());
+ EXPECT_FALSE(TimerTasksSeemExpensive());
+ EXPECT_TRUE(TouchStartExpectedSoon());
+ EXPECT_THAT(run_order, testing::ElementsAre(std::string("D1")));
+ EXPECT_EQ(v8::PERFORMANCE_RESPONSE, GetRAILMode());
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ ExpensiveLoadingTasksNotBlockedIfNoTouchHandler) {
+ std::vector<std::string> run_order;
+
+ scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(false);
+ DoMainFrame();
+ SimulateExpensiveTasks(loading_task_runner_);
+ ForceTouchStartToBeExpectedSoon();
+ PostTestTasks(&run_order, "L1 D1");
+ RunUntilIdle();
+
+ EXPECT_EQ(UseCase::kNone, ForceUpdatePolicyAndGetCurrentUseCase());
+ EXPECT_TRUE(HaveSeenABeginMainframe());
+ EXPECT_TRUE(LoadingTasksSeemExpensive());
+ EXPECT_FALSE(TimerTasksSeemExpensive());
+ EXPECT_FALSE(TouchStartExpectedSoon());
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("L1"), std::string("D1")));
+ EXPECT_EQ(v8::PERFORMANCE_ANIMATION, GetRAILMode());
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ ExpensiveTimerTaskBlocked_UseCase_NONE_PreviousCompositorGesture) {
+ std::vector<std::string> run_order;
+
+ scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
+ DoMainFrame();
+ SimulateExpensiveTasks(timer_task_runner_);
+ ForceTouchStartToBeExpectedSoon();
+
+ PostTestTasks(&run_order, "T1 D1");
+ RunUntilIdle();
+
+ EXPECT_EQ(UseCase::kNone, ForceUpdatePolicyAndGetCurrentUseCase());
+ EXPECT_TRUE(HaveSeenABeginMainframe());
+ EXPECT_FALSE(LoadingTasksSeemExpensive());
+ EXPECT_TRUE(TimerTasksSeemExpensive());
+ EXPECT_TRUE(TouchStartExpectedSoon());
+ EXPECT_THAT(run_order, testing::ElementsAre(std::string("D1")));
+ EXPECT_EQ(v8::PERFORMANCE_RESPONSE, GetRAILMode());
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ ExpensiveTimerTaskNotBlocked_UseCase_NONE_PreviousMainThreadGesture) {
+ std::vector<std::string> run_order;
+
+ scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
+ DoMainFrame();
+ SimulateExpensiveTasks(timer_task_runner_);
+
+ SimulateMainThreadGestureStart(TouchEventPolicy::kSendTouchStart,
+ blink::WebInputEvent::kGestureScrollBegin);
+ EXPECT_EQ(UseCase::kMainThreadCustomInputHandling,
+ ForceUpdatePolicyAndGetCurrentUseCase());
+
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchEnd),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ scheduler_->DidHandleInputEventOnMainThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchEnd),
+ WebInputEventResult::kHandledSystem);
+
+ clock_.Advance(priority_escalation_after_input_duration() * 2);
+ EXPECT_EQ(UseCase::kNone, ForceUpdatePolicyAndGetCurrentUseCase());
+
+ PostTestTasks(&run_order, "T1 D1");
+ RunUntilIdle();
+
+ EXPECT_EQ(UseCase::kNone, ForceUpdatePolicyAndGetCurrentUseCase());
+ EXPECT_TRUE(HaveSeenABeginMainframe());
+ EXPECT_FALSE(LoadingTasksSeemExpensive());
+ EXPECT_TRUE(TimerTasksSeemExpensive());
+ EXPECT_TRUE(TouchStartExpectedSoon());
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("T1"), std::string("D1")));
+ EXPECT_EQ(v8::PERFORMANCE_ANIMATION, GetRAILMode());
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ ExpensiveTimerTaskBlocked_UseCase_kCompositorGesture) {
+ std::vector<std::string> run_order;
+
+ scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
+ DoMainFrame();
+ SimulateExpensiveTasks(timer_task_runner_);
+ ForceTouchStartToBeExpectedSoon();
+ scheduler_->DidAnimateForInputOnCompositorThread();
+
+ PostTestTasks(&run_order, "T1 D1");
+ RunUntilIdle();
+
+ EXPECT_EQ(UseCase::kCompositorGesture,
+ ForceUpdatePolicyAndGetCurrentUseCase());
+ EXPECT_TRUE(HaveSeenABeginMainframe());
+ EXPECT_FALSE(LoadingTasksSeemExpensive());
+ EXPECT_TRUE(TimerTasksSeemExpensive());
+ EXPECT_TRUE(TouchStartExpectedSoon());
+ EXPECT_THAT(run_order, testing::ElementsAre(std::string("D1")));
+ EXPECT_EQ(v8::PERFORMANCE_RESPONSE, GetRAILMode());
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ ExpensiveTimerTaskBlocked_EvenIfBeginMainFrameNotExpectedSoon) {
+ std::vector<std::string> run_order;
+
+ scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
+ DoMainFrame();
+ SimulateExpensiveTasks(timer_task_runner_);
+ ForceTouchStartToBeExpectedSoon();
+ scheduler_->BeginFrameNotExpectedSoon();
+
+ PostTestTasks(&run_order, "T1 D1");
+ RunUntilIdle();
+
+ EXPECT_EQ(UseCase::kNone, ForceUpdatePolicyAndGetCurrentUseCase());
+ EXPECT_TRUE(HaveSeenABeginMainframe());
+ EXPECT_FALSE(LoadingTasksSeemExpensive());
+ EXPECT_TRUE(TimerTasksSeemExpensive());
+ EXPECT_TRUE(TouchStartExpectedSoon());
+ EXPECT_THAT(run_order, testing::ElementsAre(std::string("D1")));
+ EXPECT_EQ(v8::PERFORMANCE_RESPONSE, GetRAILMode());
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ ExpensiveLoadingTasksBlockedIfChildFrameNavigationExpected) {
+ std::vector<std::string> run_order;
+
+ DoMainFrame();
+ scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
+ SimulateExpensiveTasks(loading_task_runner_);
+ ForceTouchStartToBeExpectedSoon();
+ scheduler_->AddPendingNavigation(
+ scheduler::WebMainThreadScheduler::NavigatingFrameType::kChildFrame);
+
+ PostTestTasks(&run_order, "L1 D1");
+ RunUntilIdle();
+
+ // The expensive loading task gets blocked.
+ EXPECT_THAT(run_order, testing::ElementsAre(std::string("D1")));
+ EXPECT_EQ(v8::PERFORMANCE_RESPONSE, GetRAILMode());
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ ExpensiveLoadingTasksNotBlockedIfMainFrameNavigationExpected) {
+ std::vector<std::string> run_order;
+
+ DoMainFrame();
+ scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
+ SimulateExpensiveTasks(loading_task_runner_);
+ ForceTouchStartToBeExpectedSoon();
+ scheduler_->AddPendingNavigation(
+ scheduler::WebMainThreadScheduler::NavigatingFrameType::kMainFrame);
+
+ PostTestTasks(&run_order, "L1 D1");
+ RunUntilIdle();
+
+ EXPECT_EQ(UseCase::kNone, ForceUpdatePolicyAndGetCurrentUseCase());
+ EXPECT_TRUE(HaveSeenABeginMainframe());
+ EXPECT_TRUE(LoadingTasksSeemExpensive());
+ EXPECT_FALSE(TimerTasksSeemExpensive());
+ EXPECT_TRUE(TouchStartExpectedSoon());
+ EXPECT_EQ(1, NavigationTaskExpectedCount());
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("L1"), std::string("D1")));
+
+ // After the nagigation has been cancelled, the expensive loading tasks should
+ // get blocked.
+ scheduler_->RemovePendingNavigation(
+ scheduler::WebMainThreadScheduler::NavigatingFrameType::kMainFrame);
+ run_order.clear();
+
+ PostTestTasks(&run_order, "L1 D1");
+ RunUntilIdle();
+
+ EXPECT_EQ(UseCase::kNone, CurrentUseCase());
+ EXPECT_TRUE(HaveSeenABeginMainframe());
+ EXPECT_TRUE(LoadingTasksSeemExpensive());
+ EXPECT_FALSE(TimerTasksSeemExpensive());
+ EXPECT_TRUE(TouchStartExpectedSoon());
+ EXPECT_EQ(0, NavigationTaskExpectedCount());
+ EXPECT_THAT(run_order, testing::ElementsAre(std::string("D1")));
+ EXPECT_EQ(v8::PERFORMANCE_RESPONSE, GetRAILMode());
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ ExpensiveLoadingTasksNotBlockedIfMainFrameNavigationExpected_Multiple) {
+ std::vector<std::string> run_order;
+
+ DoMainFrame();
+ scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
+ SimulateExpensiveTasks(loading_task_runner_);
+ ForceTouchStartToBeExpectedSoon();
+ scheduler_->AddPendingNavigation(
+ scheduler::WebMainThreadScheduler::NavigatingFrameType::kMainFrame);
+ scheduler_->AddPendingNavigation(
+ scheduler::WebMainThreadScheduler::NavigatingFrameType::kMainFrame);
+
+ PostTestTasks(&run_order, "L1 D1");
+ RunUntilIdle();
+
+ EXPECT_EQ(UseCase::kNone, ForceUpdatePolicyAndGetCurrentUseCase());
+ EXPECT_TRUE(HaveSeenABeginMainframe());
+ EXPECT_TRUE(LoadingTasksSeemExpensive());
+ EXPECT_FALSE(TimerTasksSeemExpensive());
+ EXPECT_TRUE(TouchStartExpectedSoon());
+ EXPECT_EQ(2, NavigationTaskExpectedCount());
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("L1"), std::string("D1")));
+
+ run_order.clear();
+ scheduler_->RemovePendingNavigation(
+ scheduler::WebMainThreadScheduler::NavigatingFrameType::kMainFrame);
+ // Navigation task expected ref count non-zero so expensive tasks still not
+ // blocked.
+ PostTestTasks(&run_order, "L1 D1");
+ RunUntilIdle();
+
+ EXPECT_EQ(UseCase::kNone, ForceUpdatePolicyAndGetCurrentUseCase());
+ EXPECT_TRUE(HaveSeenABeginMainframe());
+ EXPECT_TRUE(LoadingTasksSeemExpensive());
+ EXPECT_FALSE(TimerTasksSeemExpensive());
+ EXPECT_TRUE(TouchStartExpectedSoon());
+ EXPECT_EQ(1, NavigationTaskExpectedCount());
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("L1"), std::string("D1")));
+
+ run_order.clear();
+ scheduler_->RemovePendingNavigation(
+ scheduler::WebMainThreadScheduler::NavigatingFrameType::kMainFrame);
+ // Navigation task expected ref count is now zero, the expensive loading tasks
+ // should get blocked.
+ PostTestTasks(&run_order, "L1 D1");
+ RunUntilIdle();
+
+ EXPECT_EQ(UseCase::kNone, CurrentUseCase());
+ EXPECT_TRUE(HaveSeenABeginMainframe());
+ EXPECT_TRUE(LoadingTasksSeemExpensive());
+ EXPECT_FALSE(TimerTasksSeemExpensive());
+ EXPECT_TRUE(TouchStartExpectedSoon());
+ EXPECT_EQ(0, NavigationTaskExpectedCount());
+ EXPECT_THAT(run_order, testing::ElementsAre(std::string("D1")));
+ EXPECT_EQ(v8::PERFORMANCE_RESPONSE, GetRAILMode());
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ ExpensiveLoadingTasksNotBlockedDuringMainThreadGestures) {
+ std::vector<std::string> run_order;
+
+ SimulateExpensiveTasks(loading_task_runner_);
+
+ // Loading tasks should not be disabled during main thread user interactions.
+ PostTestTasks(&run_order, "C1 L1");
+
+ // Trigger main_thread_gesture UseCase
+ SimulateMainThreadGestureStart(TouchEventPolicy::kSendTouchStart,
+ blink::WebInputEvent::kGestureScrollBegin);
+ RunUntilIdle();
+ EXPECT_EQ(UseCase::kMainThreadCustomInputHandling, CurrentUseCase());
+
+ EXPECT_TRUE(LoadingTasksSeemExpensive());
+ EXPECT_FALSE(TimerTasksSeemExpensive());
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("C1"), std::string("L1")));
+ EXPECT_EQ(v8::PERFORMANCE_ANIMATION, GetRAILMode());
+}
+
+TEST_F(MainThreadSchedulerImplTest, ModeratelyExpensiveTimer_NotBlocked) {
+ scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
+ SimulateMainThreadGestureStart(TouchEventPolicy::kSendTouchStart,
+ blink::WebInputEvent::kTouchMove);
+ RunUntilIdle();
+ for (int i = 0; i < 20; i++) {
+ simulate_timer_task_ran_ = false;
+
+ viz::BeginFrameArgs begin_frame_args = viz::BeginFrameArgs::Create(
+ BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_.NowTicks(),
+ base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
+ viz::BeginFrameArgs::NORMAL);
+ begin_frame_args.on_critical_path = false;
+ scheduler_->WillBeginFrame(begin_frame_args);
+
+ compositor_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&MainThreadSchedulerImplTest::
+ SimulateMainThreadInputHandlingCompositorTask,
+ base::Unretained(this),
+ base::TimeDelta::FromMilliseconds(8)));
+ timer_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&MainThreadSchedulerImplTest::SimulateTimerTask,
+ base::Unretained(this),
+ base::TimeDelta::FromMilliseconds(4)));
+
+ RunUntilIdle();
+ EXPECT_TRUE(simulate_timer_task_ran_) << " i = " << i;
+ EXPECT_EQ(UseCase::kMainThreadCustomInputHandling, CurrentUseCase())
+ << " i = " << i;
+ EXPECT_FALSE(LoadingTasksSeemExpensive()) << " i = " << i;
+ EXPECT_FALSE(TimerTasksSeemExpensive()) << " i = " << i;
+
+ base::TimeDelta time_till_next_frame =
+ EstimatedNextFrameBegin() - clock_.NowTicks();
+ if (time_till_next_frame > base::TimeDelta())
+ clock_.Advance(time_till_next_frame);
+ }
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ FourtyMsTimer_NotBlocked_CompositorScrolling) {
+ scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
+ RunUntilIdle();
+ for (int i = 0; i < 20; i++) {
+ simulate_timer_task_ran_ = false;
+
+ viz::BeginFrameArgs begin_frame_args = viz::BeginFrameArgs::Create(
+ BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_.NowTicks(),
+ base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
+ viz::BeginFrameArgs::NORMAL);
+ begin_frame_args.on_critical_path = false;
+ scheduler_->WillBeginFrame(begin_frame_args);
+ scheduler_->DidAnimateForInputOnCompositorThread();
+
+ compositor_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &MainThreadSchedulerImplTest::SimulateMainThreadCompositorTask,
+ base::Unretained(this), base::TimeDelta::FromMilliseconds(8)));
+ timer_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&MainThreadSchedulerImplTest::SimulateTimerTask,
+ base::Unretained(this),
+ base::TimeDelta::FromMilliseconds(40)));
+
+ RunUntilIdle();
+ EXPECT_TRUE(simulate_timer_task_ran_) << " i = " << i;
+ EXPECT_EQ(UseCase::kCompositorGesture, CurrentUseCase()) << " i = " << i;
+ EXPECT_FALSE(LoadingTasksSeemExpensive()) << " i = " << i;
+ EXPECT_FALSE(TimerTasksSeemExpensive()) << " i = " << i;
+
+ base::TimeDelta time_till_next_frame =
+ EstimatedNextFrameBegin() - clock_.NowTicks();
+ if (time_till_next_frame > base::TimeDelta())
+ clock_.Advance(time_till_next_frame);
+ }
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ ExpensiveTimer_NotBlocked_UseCase_MAIN_THREAD_CUSTOM_INPUT_HANDLING) {
+ scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
+ SimulateMainThreadGestureStart(TouchEventPolicy::kSendTouchStart,
+ blink::WebInputEvent::kTouchMove);
+ RunUntilIdle();
+ for (int i = 0; i < 20; i++) {
+ simulate_timer_task_ran_ = false;
+
+ viz::BeginFrameArgs begin_frame_args = viz::BeginFrameArgs::Create(
+ BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_.NowTicks(),
+ base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
+ viz::BeginFrameArgs::NORMAL);
+ begin_frame_args.on_critical_path = false;
+ scheduler_->WillBeginFrame(begin_frame_args);
+
+ compositor_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&MainThreadSchedulerImplTest::
+ SimulateMainThreadInputHandlingCompositorTask,
+ base::Unretained(this),
+ base::TimeDelta::FromMilliseconds(8)));
+ timer_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&MainThreadSchedulerImplTest::SimulateTimerTask,
+ base::Unretained(this),
+ base::TimeDelta::FromMilliseconds(10)));
+
+ RunUntilIdle();
+ EXPECT_EQ(UseCase::kMainThreadCustomInputHandling, CurrentUseCase())
+ << " i = " << i;
+ EXPECT_FALSE(LoadingTasksSeemExpensive()) << " i = " << i;
+ if (i == 0) {
+ EXPECT_FALSE(TimerTasksSeemExpensive()) << " i = " << i;
+ } else {
+ EXPECT_TRUE(TimerTasksSeemExpensive()) << " i = " << i;
+ }
+ EXPECT_TRUE(simulate_timer_task_ran_) << " i = " << i;
+
+ base::TimeDelta time_till_next_frame =
+ EstimatedNextFrameBegin() - clock_.NowTicks();
+ if (time_till_next_frame > base::TimeDelta())
+ clock_.Advance(time_till_next_frame);
+ }
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ EstimateLongestJankFreeTaskDuration_UseCase_NONE) {
+ EXPECT_EQ(UseCase::kNone, CurrentUseCase());
+ EXPECT_EQ(rails_response_time(),
+ scheduler_->EstimateLongestJankFreeTaskDuration());
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ EstimateLongestJankFreeTaskDuration_UseCase_kCompositorGesture) {
+ SimulateCompositorGestureStart(TouchEventPolicy::kDontSendTouchStart);
+ EXPECT_EQ(UseCase::kCompositorGesture,
+ ForceUpdatePolicyAndGetCurrentUseCase());
+ EXPECT_EQ(rails_response_time(),
+ scheduler_->EstimateLongestJankFreeTaskDuration());
+}
+
+// TODO(alexclarke): Reenable once we've reinstaed the Loading
+// UseCase.
+TEST_F(MainThreadSchedulerImplTest,
+ DISABLED_EstimateLongestJankFreeTaskDuration_UseCase_) {
+ scheduler_->DidStartProvisionalLoad(true);
+ EXPECT_EQ(UseCase::kLoading, ForceUpdatePolicyAndGetCurrentUseCase());
+ EXPECT_EQ(rails_response_time(),
+ scheduler_->EstimateLongestJankFreeTaskDuration());
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ EstimateLongestJankFreeTaskDuration_UseCase_MAIN_THREAD_GESTURE) {
+ SimulateMainThreadGestureStart(TouchEventPolicy::kSendTouchStart,
+ blink::WebInputEvent::kGestureScrollUpdate);
+ viz::BeginFrameArgs begin_frame_args = viz::BeginFrameArgs::Create(
+ BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_.NowTicks(),
+ base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
+ viz::BeginFrameArgs::NORMAL);
+ begin_frame_args.on_critical_path = false;
+ scheduler_->WillBeginFrame(begin_frame_args);
+
+ compositor_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&MainThreadSchedulerImplTest::
+ SimulateMainThreadInputHandlingCompositorTask,
+ base::Unretained(this),
+ base::TimeDelta::FromMilliseconds(5)));
+
+ RunUntilIdle();
+ EXPECT_EQ(UseCase::kMainThreadGesture, CurrentUseCase());
+
+ // 16ms frame - 5ms compositor work = 11ms for other stuff.
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(11),
+ scheduler_->EstimateLongestJankFreeTaskDuration());
+}
+
+TEST_F(
+ MainThreadSchedulerImplTest,
+ EstimateLongestJankFreeTaskDuration_UseCase_MAIN_THREAD_CUSTOM_INPUT_HANDLING) {
+ viz::BeginFrameArgs begin_frame_args = viz::BeginFrameArgs::Create(
+ BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_.NowTicks(),
+ base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
+ viz::BeginFrameArgs::NORMAL);
+ begin_frame_args.on_critical_path = false;
+ scheduler_->WillBeginFrame(begin_frame_args);
+
+ compositor_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&MainThreadSchedulerImplTest::
+ SimulateMainThreadInputHandlingCompositorTask,
+ base::Unretained(this),
+ base::TimeDelta::FromMilliseconds(5)));
+
+ RunUntilIdle();
+ EXPECT_EQ(UseCase::kMainThreadCustomInputHandling, CurrentUseCase());
+
+ // 16ms frame - 5ms compositor work = 11ms for other stuff.
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(11),
+ scheduler_->EstimateLongestJankFreeTaskDuration());
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ EstimateLongestJankFreeTaskDuration_UseCase_SYNCHRONIZED_GESTURE) {
+ SimulateCompositorGestureStart(TouchEventPolicy::kDontSendTouchStart);
+
+ viz::BeginFrameArgs begin_frame_args = viz::BeginFrameArgs::Create(
+ BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_.NowTicks(),
+ base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
+ viz::BeginFrameArgs::NORMAL);
+ begin_frame_args.on_critical_path = true;
+ scheduler_->WillBeginFrame(begin_frame_args);
+
+ compositor_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &MainThreadSchedulerImplTest::SimulateMainThreadCompositorTask,
+ base::Unretained(this), base::TimeDelta::FromMilliseconds(5)));
+
+ RunUntilIdle();
+ EXPECT_EQ(UseCase::kSynchronizedGesture, CurrentUseCase());
+
+ // 16ms frame - 5ms compositor work = 11ms for other stuff.
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(11),
+ scheduler_->EstimateLongestJankFreeTaskDuration());
+}
+
+class PageSchedulerImplForTest : public PageSchedulerImpl {
+ public:
+ explicit PageSchedulerImplForTest(MainThreadSchedulerImpl* scheduler)
+ : PageSchedulerImpl(nullptr, scheduler, false) {}
+ ~PageSchedulerImplForTest() override = default;
+
+ void ReportIntervention(const std::string& message) override {
+ interventions_.push_back(message);
+ }
+
+ const std::vector<std::string>& Interventions() const {
+ return interventions_;
+ }
+
+ MOCK_METHOD1(RequestBeginMainFrameNotExpected, void(bool));
+
+ private:
+ std::vector<std::string> interventions_;
+
+ DISALLOW_COPY_AND_ASSIGN(PageSchedulerImplForTest);
+};
+
+namespace {
+void SlowCountingTask(size_t* count,
+ base::SimpleTestTickClock* clock,
+ int task_duration,
+ scoped_refptr<base::SingleThreadTaskRunner> timer_queue) {
+ clock->Advance(base::TimeDelta::FromMilliseconds(task_duration));
+ if (++(*count) < 500) {
+ timer_queue->PostTask(
+ FROM_HERE, base::BindOnce(SlowCountingTask, count, clock, task_duration,
+ timer_queue));
+ }
+}
+} // namespace
+
+TEST_F(MainThreadSchedulerImplTest,
+ SYNCHRONIZED_GESTURE_TimerTaskThrottling_task_expensive) {
+ SimulateCompositorGestureStart(TouchEventPolicy::kSendTouchStart);
+
+ base::TimeTicks first_throttled_run_time =
+ TaskQueueThrottler::AlignedThrottledRunTime(clock_.NowTicks());
+
+ size_t count = 0;
+ // With the compositor task taking 10ms, there is not enough time to run this
+ // 7ms timer task in the 16ms frame.
+ timer_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(SlowCountingTask, &count, &clock_, 7, timer_task_runner_));
+
+ for (int i = 0; i < 1000; i++) {
+ viz::BeginFrameArgs begin_frame_args = viz::BeginFrameArgs::Create(
+ BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_.NowTicks(),
+ base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
+ viz::BeginFrameArgs::NORMAL);
+ begin_frame_args.on_critical_path = true;
+ scheduler_->WillBeginFrame(begin_frame_args);
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureScrollUpdate),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+
+ simulate_compositor_task_ran_ = false;
+ compositor_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &MainThreadSchedulerImplTest::SimulateMainThreadCompositorTask,
+ base::Unretained(this), base::TimeDelta::FromMilliseconds(10)));
+
+ mock_task_runner_->RunTasksWhile(base::BindRepeating(
+ &MainThreadSchedulerImplTest::SimulatedCompositorTaskPending,
+ base::Unretained(this)));
+ EXPECT_EQ(UseCase::kSynchronizedGesture, CurrentUseCase()) << "i = " << i;
+
+ // We expect the queue to get throttled on the second iteration which is
+ // when the system realizes the task is expensive.
+ bool expect_queue_throttled = (i > 0);
+ EXPECT_EQ(expect_queue_throttled,
+ scheduler_->task_queue_throttler()->IsThrottled(
+ timer_task_runner_.get()))
+ << "i = " << i;
+
+ if (expect_queue_throttled) {
+ EXPECT_GE(count, 2u);
+ } else {
+ EXPECT_LE(count, 2u);
+ }
+
+ // The task runs twice before the system realizes it's too expensive.
+ bool throttled_task_has_run = count > 2;
+ bool throttled_task_expected_to_have_run =
+ (clock_.NowTicks() > first_throttled_run_time);
+ EXPECT_EQ(throttled_task_expected_to_have_run, throttled_task_has_run)
+ << "i = " << i << " count = " << count;
+ }
+
+ // Task is throttled but not completely blocked.
+ EXPECT_EQ(12u, count);
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ SYNCHRONIZED_GESTURE_TimerTaskThrottling_TimersStopped) {
+ SimulateCompositorGestureStart(TouchEventPolicy::kSendTouchStart);
+
+ base::TimeTicks first_throttled_run_time =
+ TaskQueueThrottler::AlignedThrottledRunTime(clock_.NowTicks());
+
+ size_t count = 0;
+ // With the compositor task taking 10ms, there is not enough time to run this
+ // 7ms timer task in the 16ms frame.
+ timer_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(SlowCountingTask, &count, &clock_, 7, timer_task_runner_));
+
+ std::unique_ptr<WebMainThreadScheduler::RendererPauseHandle> paused;
+ for (int i = 0; i < 1000; i++) {
+ viz::BeginFrameArgs begin_frame_args = viz::BeginFrameArgs::Create(
+ BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_.NowTicks(),
+ base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
+ viz::BeginFrameArgs::NORMAL);
+ begin_frame_args.on_critical_path = true;
+ scheduler_->WillBeginFrame(begin_frame_args);
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureScrollUpdate),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+
+ simulate_compositor_task_ran_ = false;
+ compositor_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &MainThreadSchedulerImplTest::SimulateMainThreadCompositorTask,
+ base::Unretained(this), base::TimeDelta::FromMilliseconds(10)));
+
+ mock_task_runner_->RunTasksWhile(base::BindRepeating(
+ &MainThreadSchedulerImplTest::SimulatedCompositorTaskPending,
+ base::Unretained(this)));
+ EXPECT_EQ(UseCase::kSynchronizedGesture, CurrentUseCase()) << "i = " << i;
+
+ // Before the policy is updated the queue will be enabled. Subsequently it
+ // will be disabled until the throttled queue is pumped.
+ bool expect_queue_enabled =
+ (i == 0) || (clock_.NowTicks() > first_throttled_run_time);
+ if (paused)
+ expect_queue_enabled = false;
+ EXPECT_EQ(expect_queue_enabled, timer_task_runner_->IsQueueEnabled())
+ << "i = " << i;
+
+ // After we've run any expensive tasks suspend the queue. The throttling
+ // helper should /not/ re-enable this queue under any circumstances while
+ // timers are paused.
+ if (count > 0 && !paused) {
+ EXPECT_EQ(2u, count);
+ paused = scheduler_->PauseRenderer();
+ }
+ }
+
+ // Make sure the timer queue stayed paused!
+ EXPECT_EQ(2u, count);
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ SYNCHRONIZED_GESTURE_TimerTaskThrottling_task_not_expensive) {
+ SimulateCompositorGestureStart(TouchEventPolicy::kSendTouchStart);
+
+ size_t count = 0;
+ // With the compositor task taking 10ms, there is enough time to run this 6ms
+ // timer task in the 16ms frame.
+ timer_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(SlowCountingTask, &count, &clock_, 6, timer_task_runner_));
+
+ for (int i = 0; i < 1000; i++) {
+ viz::BeginFrameArgs begin_frame_args = viz::BeginFrameArgs::Create(
+ BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_.NowTicks(),
+ base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
+ viz::BeginFrameArgs::NORMAL);
+ begin_frame_args.on_critical_path = true;
+ scheduler_->WillBeginFrame(begin_frame_args);
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureScrollUpdate),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+
+ simulate_compositor_task_ran_ = false;
+ compositor_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &MainThreadSchedulerImplTest::SimulateMainThreadCompositorTask,
+ base::Unretained(this), base::TimeDelta::FromMilliseconds(10)));
+
+ mock_task_runner_->RunTasksWhile(base::BindRepeating(
+ &MainThreadSchedulerImplTest::SimulatedCompositorTaskPending,
+ base::Unretained(this)));
+ EXPECT_EQ(UseCase::kSynchronizedGesture, CurrentUseCase()) << "i = " << i;
+ EXPECT_TRUE(timer_task_runner_->IsQueueEnabled()) << "i = " << i;
+ }
+
+ // Task is not throttled.
+ EXPECT_EQ(500u, count);
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ ExpensiveTimerTaskBlocked_SYNCHRONIZED_GESTURE_TouchStartExpected) {
+ SimulateExpensiveTasks(timer_task_runner_);
+ SimulateCompositorGestureStart(TouchEventPolicy::kSendTouchStart);
+ scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
+ ForceTouchStartToBeExpectedSoon();
+
+ // Bump us into SYNCHRONIZED_GESTURE.
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureScrollUpdate),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+
+ viz::BeginFrameArgs begin_frame_args = viz::BeginFrameArgs::Create(
+ BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_.NowTicks(),
+ base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
+ viz::BeginFrameArgs::NORMAL);
+ begin_frame_args.on_critical_path = true;
+ scheduler_->WillBeginFrame(begin_frame_args);
+
+ EXPECT_EQ(UseCase::kSynchronizedGesture,
+ ForceUpdatePolicyAndGetCurrentUseCase());
+
+ EXPECT_TRUE(TimerTasksSeemExpensive());
+ EXPECT_TRUE(TouchStartExpectedSoon());
+ EXPECT_FALSE(timer_task_runner_->IsQueueEnabled());
+}
+
+TEST_F(MainThreadSchedulerImplTest, DenyLongIdleDuringTouchStart) {
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchStart),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+ EXPECT_EQ(UseCase::kTouchstart, ForceUpdatePolicyAndGetCurrentUseCase());
+
+ // First check that long idle is denied during the TOUCHSTART use case.
+ IdleHelper::Delegate* idle_delegate = scheduler_.get();
+ base::TimeTicks now;
+ base::TimeDelta next_time_to_check;
+ EXPECT_FALSE(idle_delegate->CanEnterLongIdlePeriod(now, &next_time_to_check));
+ EXPECT_GE(next_time_to_check, base::TimeDelta());
+
+ // Check again at a time past the TOUCHSTART expiration. We should still get a
+ // non-negative delay to when to check again.
+ now += base::TimeDelta::FromMilliseconds(500);
+ EXPECT_FALSE(idle_delegate->CanEnterLongIdlePeriod(now, &next_time_to_check));
+ EXPECT_GE(next_time_to_check, base::TimeDelta());
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ TestCompositorPolicy_TouchStartDuringFling) {
+ scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
+ scheduler_->DidAnimateForInputOnCompositorThread();
+ // Note DidAnimateForInputOnCompositorThread does not by itself trigger a
+ // policy update.
+ EXPECT_EQ(UseCase::kCompositorGesture,
+ ForceUpdatePolicyAndGetCurrentUseCase());
+
+ // Make sure TouchStart causes a policy change.
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchStart),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+ EXPECT_EQ(UseCase::kTouchstart, ForceUpdatePolicyAndGetCurrentUseCase());
+}
+
+TEST_F(MainThreadSchedulerImplTest, SYNCHRONIZED_GESTURE_CompositingExpensive) {
+ SimulateCompositorGestureStart(TouchEventPolicy::kSendTouchStart);
+
+ // With the compositor task taking 20ms, there is not enough time to run
+ // other tasks in the same 16ms frame. To avoid starvation, compositing tasks
+ // should therefore not get prioritized.
+ std::vector<std::string> run_order;
+ for (int i = 0; i < 1000; i++)
+ PostTestTasks(&run_order, "T1");
+
+ for (int i = 0; i < 100; i++) {
+ viz::BeginFrameArgs begin_frame_args = viz::BeginFrameArgs::Create(
+ BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_.NowTicks(),
+ base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
+ viz::BeginFrameArgs::NORMAL);
+ begin_frame_args.on_critical_path = true;
+ scheduler_->WillBeginFrame(begin_frame_args);
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureScrollUpdate),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+
+ simulate_compositor_task_ran_ = false;
+ compositor_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &MainThreadSchedulerImplTest::SimulateMainThreadCompositorTask,
+ base::Unretained(this), base::TimeDelta::FromMilliseconds(20)));
+
+ mock_task_runner_->RunTasksWhile(base::BindRepeating(
+ &MainThreadSchedulerImplTest::SimulatedCompositorTaskPending,
+ base::Unretained(this)));
+ EXPECT_EQ(UseCase::kSynchronizedGesture, CurrentUseCase()) << "i = " << i;
+ }
+
+ // Timer tasks should not have been starved by the expensive compositor
+ // tasks.
+ EXPECT_EQ(TaskQueue::kNormalPriority,
+ scheduler_->CompositorTaskQueue()->GetQueuePriority());
+ EXPECT_EQ(1000u, run_order.size());
+}
+
+TEST_F(MainThreadSchedulerImplTest, MAIN_THREAD_CUSTOM_INPUT_HANDLING) {
+ SimulateMainThreadGestureStart(TouchEventPolicy::kSendTouchStart,
+ blink::WebInputEvent::kGestureScrollBegin);
+
+ // With the compositor task taking 20ms, there is not enough time to run
+ // other tasks in the same 16ms frame. To avoid starvation, compositing tasks
+ // should therefore not get prioritized.
+ std::vector<std::string> run_order;
+ for (int i = 0; i < 1000; i++)
+ PostTestTasks(&run_order, "T1");
+
+ for (int i = 0; i < 100; i++) {
+ viz::BeginFrameArgs begin_frame_args = viz::BeginFrameArgs::Create(
+ BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_.NowTicks(),
+ base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
+ viz::BeginFrameArgs::NORMAL);
+ begin_frame_args.on_critical_path = true;
+ scheduler_->WillBeginFrame(begin_frame_args);
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kTouchMove),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+
+ simulate_compositor_task_ran_ = false;
+ compositor_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &MainThreadSchedulerImplTest::SimulateMainThreadCompositorTask,
+ base::Unretained(this), base::TimeDelta::FromMilliseconds(20)));
+
+ mock_task_runner_->RunTasksWhile(base::BindRepeating(
+ &MainThreadSchedulerImplTest::SimulatedCompositorTaskPending,
+ base::Unretained(this)));
+ EXPECT_EQ(UseCase::kMainThreadCustomInputHandling, CurrentUseCase())
+ << "i = " << i;
+ }
+
+ // Timer tasks should not have been starved by the expensive compositor
+ // tasks.
+ EXPECT_EQ(TaskQueue::kNormalPriority,
+ scheduler_->CompositorTaskQueue()->GetQueuePriority());
+ EXPECT_EQ(1000u, run_order.size());
+}
+
+TEST_F(MainThreadSchedulerImplTest, MAIN_THREAD_GESTURE) {
+ SimulateMainThreadGestureStart(TouchEventPolicy::kDontSendTouchStart,
+ blink::WebInputEvent::kGestureScrollBegin);
+
+ // With the compositor task taking 20ms, there is not enough time to run
+ // other tasks in the same 16ms frame. However because this is a main thread
+ // gesture instead of custom main thread input handling, we allow the timer
+ // tasks to be starved.
+ std::vector<std::string> run_order;
+ for (int i = 0; i < 1000; i++)
+ PostTestTasks(&run_order, "T1");
+
+ for (int i = 0; i < 100; i++) {
+ viz::BeginFrameArgs begin_frame_args = viz::BeginFrameArgs::Create(
+ BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_.NowTicks(),
+ base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
+ viz::BeginFrameArgs::NORMAL);
+ begin_frame_args.on_critical_path = true;
+ scheduler_->WillBeginFrame(begin_frame_args);
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureScrollUpdate),
+ InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
+
+ simulate_compositor_task_ran_ = false;
+ compositor_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &MainThreadSchedulerImplTest::SimulateMainThreadCompositorTask,
+ base::Unretained(this), base::TimeDelta::FromMilliseconds(20)));
+
+ mock_task_runner_->RunTasksWhile(base::BindRepeating(
+ &MainThreadSchedulerImplTest::SimulatedCompositorTaskPending,
+ base::Unretained(this)));
+ EXPECT_EQ(UseCase::kMainThreadGesture, CurrentUseCase()) << "i = " << i;
+ }
+
+ EXPECT_EQ(TaskQueue::kHighestPriority,
+ scheduler_->CompositorTaskQueue()->GetQueuePriority());
+ EXPECT_EQ(279u, run_order.size());
+}
+
+class MockRAILModeObserver : public WebMainThreadScheduler::RAILModeObserver {
+ public:
+ MOCK_METHOD1(OnRAILModeChanged, void(v8::RAILMode rail_mode));
+};
+
+TEST_F(MainThreadSchedulerImplTest, TestResponseRAILMode) {
+ MockRAILModeObserver observer;
+ scheduler_->SetRAILModeObserver(&observer);
+ EXPECT_CALL(observer, OnRAILModeChanged(v8::PERFORMANCE_RESPONSE));
+
+ scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
+ ForceTouchStartToBeExpectedSoon();
+ EXPECT_EQ(UseCase::kNone, ForceUpdatePolicyAndGetCurrentUseCase());
+ EXPECT_EQ(v8::PERFORMANCE_RESPONSE, GetRAILMode());
+ scheduler_->SetRAILModeObserver(nullptr);
+}
+
+TEST_F(MainThreadSchedulerImplTest, TestAnimateRAILMode) {
+ MockRAILModeObserver observer;
+ scheduler_->SetRAILModeObserver(&observer);
+ EXPECT_CALL(observer, OnRAILModeChanged(v8::PERFORMANCE_ANIMATION)).Times(0);
+
+ EXPECT_FALSE(BeginFrameNotExpectedSoon());
+ EXPECT_EQ(UseCase::kNone, ForceUpdatePolicyAndGetCurrentUseCase());
+ EXPECT_EQ(v8::PERFORMANCE_ANIMATION, GetRAILMode());
+ scheduler_->SetRAILModeObserver(nullptr);
+}
+
+TEST_F(MainThreadSchedulerImplTest, TestIdleRAILMode) {
+ MockRAILModeObserver observer;
+ scheduler_->SetRAILModeObserver(&observer);
+ EXPECT_CALL(observer, OnRAILModeChanged(v8::PERFORMANCE_ANIMATION));
+ EXPECT_CALL(observer, OnRAILModeChanged(v8::PERFORMANCE_IDLE));
+
+ scheduler_->SetAllRenderWidgetsHidden(true);
+ EXPECT_EQ(UseCase::kNone, ForceUpdatePolicyAndGetCurrentUseCase());
+ EXPECT_EQ(v8::PERFORMANCE_IDLE, GetRAILMode());
+ scheduler_->SetAllRenderWidgetsHidden(false);
+ EXPECT_EQ(UseCase::kNone, ForceUpdatePolicyAndGetCurrentUseCase());
+ EXPECT_EQ(v8::PERFORMANCE_ANIMATION, GetRAILMode());
+ scheduler_->SetRAILModeObserver(nullptr);
+}
+
+TEST_F(MainThreadSchedulerImplTest, TestLoadRAILMode) {
+ MockRAILModeObserver observer;
+ scheduler_->SetRAILModeObserver(&observer);
+ EXPECT_CALL(observer, OnRAILModeChanged(v8::PERFORMANCE_ANIMATION));
+ EXPECT_CALL(observer, OnRAILModeChanged(v8::PERFORMANCE_LOAD));
+
+ scheduler_->DidStartProvisionalLoad(true);
+ EXPECT_EQ(v8::PERFORMANCE_LOAD, GetRAILMode());
+ EXPECT_EQ(UseCase::kLoading, ForceUpdatePolicyAndGetCurrentUseCase());
+ scheduler_->OnFirstMeaningfulPaint();
+ EXPECT_EQ(UseCase::kNone, ForceUpdatePolicyAndGetCurrentUseCase());
+ EXPECT_EQ(v8::PERFORMANCE_ANIMATION, GetRAILMode());
+ scheduler_->SetRAILModeObserver(nullptr);
+}
+
+TEST_F(MainThreadSchedulerImplTest, InputTerminatesLoadRAILMode) {
+ MockRAILModeObserver observer;
+ scheduler_->SetRAILModeObserver(&observer);
+ EXPECT_CALL(observer, OnRAILModeChanged(v8::PERFORMANCE_ANIMATION));
+ EXPECT_CALL(observer, OnRAILModeChanged(v8::PERFORMANCE_LOAD));
+
+ scheduler_->DidStartProvisionalLoad(true);
+ EXPECT_EQ(v8::PERFORMANCE_LOAD, GetRAILMode());
+ EXPECT_EQ(UseCase::kLoading, ForceUpdatePolicyAndGetCurrentUseCase());
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureScrollBegin),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureScrollUpdate),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+ EXPECT_EQ(UseCase::kCompositorGesture,
+ ForceUpdatePolicyAndGetCurrentUseCase());
+ EXPECT_EQ(v8::PERFORMANCE_ANIMATION, GetRAILMode());
+ scheduler_->SetRAILModeObserver(nullptr);
+}
+
+TEST_F(MainThreadSchedulerImplTest, UnthrottledTaskRunner) {
+ // Ensure neither suspension nor timer task throttling affects an unthrottled
+ // task runner.
+ SimulateCompositorGestureStart(TouchEventPolicy::kSendTouchStart);
+ scoped_refptr<TaskQueue> unthrottled_task_runner =
+ scheduler_->NewTaskQueue(MainThreadTaskQueue::QueueCreationParams(
+ MainThreadTaskQueue::QueueType::kUnthrottled));
+
+ size_t timer_count = 0;
+ size_t unthrottled_count = 0;
+ timer_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(SlowCountingTask, &timer_count, &clock_, 7,
+ timer_task_runner_));
+ unthrottled_task_runner->PostTask(
+ FROM_HERE, base::BindOnce(SlowCountingTask, &unthrottled_count, &clock_,
+ 7, unthrottled_task_runner));
+ auto handle = scheduler_->PauseRenderer();
+
+ for (int i = 0; i < 1000; i++) {
+ viz::BeginFrameArgs begin_frame_args = viz::BeginFrameArgs::Create(
+ BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, clock_.NowTicks(),
+ base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
+ viz::BeginFrameArgs::NORMAL);
+ begin_frame_args.on_critical_path = true;
+ scheduler_->WillBeginFrame(begin_frame_args);
+ scheduler_->DidHandleInputEventOnCompositorThread(
+ FakeInputEvent(blink::WebInputEvent::kGestureScrollUpdate),
+ InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
+
+ simulate_compositor_task_ran_ = false;
+ compositor_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &MainThreadSchedulerImplTest::SimulateMainThreadCompositorTask,
+ base::Unretained(this), base::TimeDelta::FromMilliseconds(10)));
+
+ mock_task_runner_->RunTasksWhile(base::BindRepeating(
+ &MainThreadSchedulerImplTest::SimulatedCompositorTaskPending,
+ base::Unretained(this)));
+ EXPECT_EQ(UseCase::kSynchronizedGesture, CurrentUseCase()) << "i = " << i;
+ }
+
+ EXPECT_EQ(0u, timer_count);
+ EXPECT_EQ(500u, unthrottled_count);
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ VirtualTimePolicyDoesNotAffectNewTimerTaskQueueIfVirtualTimeNotEnabled) {
+ scheduler_->SetVirtualTimePolicy(
+ PageSchedulerImpl::VirtualTimePolicy::kPause);
+ scoped_refptr<MainThreadTaskQueue> timer_tq = scheduler_->NewTimerTaskQueue(
+ MainThreadTaskQueue::QueueType::kFrameThrottleable);
+ EXPECT_FALSE(timer_tq->HasActiveFence());
+}
+
+TEST_F(MainThreadSchedulerImplTest, EnableVirtualTime) {
+ EXPECT_FALSE(scheduler_->IsVirtualTimeEnabled());
+ scheduler_->EnableVirtualTime(
+ MainThreadSchedulerImpl::BaseTimeOverridePolicy::DO_NOT_OVERRIDE);
+ EXPECT_TRUE(scheduler_->IsVirtualTimeEnabled());
+ scoped_refptr<MainThreadTaskQueue> loading_tq =
+ scheduler_->NewLoadingTaskQueue(
+ MainThreadTaskQueue::QueueType::kFrameLoading);
+ scoped_refptr<TaskQueue> loading_control_tq = scheduler_->NewLoadingTaskQueue(
+ MainThreadTaskQueue::QueueType::kFrameLoadingControl);
+ scoped_refptr<MainThreadTaskQueue> timer_tq = scheduler_->NewTimerTaskQueue(
+ MainThreadTaskQueue::QueueType::kFrameThrottleable);
+ scoped_refptr<MainThreadTaskQueue> unthrottled_tq =
+ scheduler_->NewTaskQueue(MainThreadTaskQueue::QueueCreationParams(
+ MainThreadTaskQueue::QueueType::kUnthrottled));
+
+ EXPECT_EQ(scheduler_->DefaultTaskQueue()->GetTimeDomain(),
+ scheduler_->GetVirtualTimeDomain());
+ EXPECT_EQ(scheduler_->CompositorTaskQueue()->GetTimeDomain(),
+ scheduler_->GetVirtualTimeDomain());
+ EXPECT_EQ(loading_task_runner_->GetTimeDomain(),
+ scheduler_->GetVirtualTimeDomain());
+ EXPECT_EQ(timer_task_runner_->GetTimeDomain(),
+ scheduler_->GetVirtualTimeDomain());
+ EXPECT_EQ(scheduler_->VirtualTimeControlTaskQueue()->GetTimeDomain(),
+ scheduler_->GetVirtualTimeDomain());
+ EXPECT_EQ(scheduler_->V8TaskQueue()->GetTimeDomain(),
+ scheduler_->GetVirtualTimeDomain());
+
+ // The main control task queue remains in the real time domain.
+ EXPECT_EQ(scheduler_->ControlTaskQueue()->GetTimeDomain(),
+ scheduler_->real_time_domain());
+
+ EXPECT_EQ(loading_tq->GetTimeDomain(), scheduler_->GetVirtualTimeDomain());
+ EXPECT_EQ(loading_control_tq->GetTimeDomain(),
+ scheduler_->GetVirtualTimeDomain());
+ EXPECT_EQ(timer_tq->GetTimeDomain(), scheduler_->GetVirtualTimeDomain());
+ EXPECT_EQ(unthrottled_tq->GetTimeDomain(),
+ scheduler_->GetVirtualTimeDomain());
+
+ EXPECT_EQ(
+ scheduler_
+ ->NewLoadingTaskQueue(MainThreadTaskQueue::QueueType::kFrameLoading)
+ ->GetTimeDomain(),
+ scheduler_->GetVirtualTimeDomain());
+ EXPECT_EQ(scheduler_
+ ->NewTimerTaskQueue(
+ MainThreadTaskQueue::QueueType::kFrameThrottleable)
+ ->GetTimeDomain(),
+ scheduler_->GetVirtualTimeDomain());
+ EXPECT_EQ(scheduler_
+ ->NewTaskQueue(MainThreadTaskQueue::QueueCreationParams(
+ MainThreadTaskQueue::QueueType::kUnthrottled))
+ ->GetTimeDomain(),
+ scheduler_->GetVirtualTimeDomain());
+ EXPECT_EQ(scheduler_
+ ->NewTaskQueue(MainThreadTaskQueue::QueueCreationParams(
+ MainThreadTaskQueue::QueueType::kTest))
+ ->GetTimeDomain(),
+ scheduler_->GetVirtualTimeDomain());
+}
+
+TEST_F(MainThreadSchedulerImplTest, EnableVirtualTimeAfterThrottling) {
+ std::unique_ptr<PageSchedulerImpl> page_scheduler = base::WrapUnique(
+ new PageSchedulerImpl(nullptr, scheduler_.get(),
+ false /* disable_background_timer_throttling */));
+ scheduler_->AddPageScheduler(page_scheduler.get());
+
+ std::unique_ptr<FrameSchedulerImpl> frame_scheduler =
+ page_scheduler->CreateFrameSchedulerImpl(
+ nullptr, FrameScheduler::FrameType::kSubframe);
+
+ TaskQueue* timer_tq = ThrottableTaskQueue(frame_scheduler.get()).get();
+
+ frame_scheduler->SetCrossOrigin(true);
+ frame_scheduler->SetFrameVisible(false);
+ EXPECT_TRUE(scheduler_->task_queue_throttler()->IsThrottled(timer_tq));
+
+ scheduler_->EnableVirtualTime(
+ MainThreadSchedulerImpl::BaseTimeOverridePolicy::DO_NOT_OVERRIDE);
+ EXPECT_EQ(timer_tq->GetTimeDomain(), scheduler_->GetVirtualTimeDomain());
+ EXPECT_FALSE(scheduler_->task_queue_throttler()->IsThrottled(timer_tq));
+}
+
+TEST_F(MainThreadSchedulerImplTest, DisableVirtualTimeForTesting) {
+ scheduler_->EnableVirtualTime(
+ MainThreadSchedulerImpl::BaseTimeOverridePolicy::DO_NOT_OVERRIDE);
+
+ scoped_refptr<MainThreadTaskQueue> timer_tq = scheduler_->NewTimerTaskQueue(
+ MainThreadTaskQueue::QueueType::kFrameThrottleable);
+ scoped_refptr<MainThreadTaskQueue> unthrottled_tq =
+ scheduler_->NewTaskQueue(MainThreadTaskQueue::QueueCreationParams(
+ MainThreadTaskQueue::QueueType::kUnthrottled));
+
+ scheduler_->DisableVirtualTimeForTesting();
+ EXPECT_EQ(scheduler_->DefaultTaskQueue()->GetTimeDomain(),
+ scheduler_->real_time_domain());
+ EXPECT_EQ(scheduler_->CompositorTaskQueue()->GetTimeDomain(),
+ scheduler_->real_time_domain());
+ EXPECT_EQ(loading_task_runner_->GetTimeDomain(),
+ scheduler_->real_time_domain());
+ EXPECT_EQ(timer_task_runner_->GetTimeDomain(),
+ scheduler_->real_time_domain());
+ EXPECT_EQ(scheduler_->ControlTaskQueue()->GetTimeDomain(),
+ scheduler_->real_time_domain());
+ EXPECT_EQ(scheduler_->V8TaskQueue()->GetTimeDomain(),
+ scheduler_->real_time_domain());
+ EXPECT_FALSE(scheduler_->VirtualTimeControlTaskQueue());
+}
+
+TEST_F(MainThreadSchedulerImplTest, VirtualTimePauser) {
+ scheduler_->EnableVirtualTime(
+ MainThreadSchedulerImpl::BaseTimeOverridePolicy::DO_NOT_OVERRIDE);
+ scheduler_->SetVirtualTimePolicy(
+ PageSchedulerImpl::VirtualTimePolicy::kDeterministicLoading);
+
+ WebScopedVirtualTimePauser pauser =
+ scheduler_->CreateWebScopedVirtualTimePauser(
+ "test", WebScopedVirtualTimePauser::VirtualTaskDuration::kInstant);
+
+ base::TimeTicks before = scheduler_->GetVirtualTimeDomain()->Now();
+ EXPECT_TRUE(scheduler_->VirtualTimeAllowedToAdvance());
+ pauser.PauseVirtualTime();
+ EXPECT_FALSE(scheduler_->VirtualTimeAllowedToAdvance());
+
+ pauser.UnpauseVirtualTime();
+ EXPECT_TRUE(scheduler_->VirtualTimeAllowedToAdvance());
+ base::TimeTicks after = scheduler_->GetVirtualTimeDomain()->Now();
+ EXPECT_EQ(after, before);
+}
+
+TEST_F(MainThreadSchedulerImplTest, VirtualTimePauserNonInstantTask) {
+ scheduler_->EnableVirtualTime(
+ MainThreadSchedulerImpl::BaseTimeOverridePolicy::DO_NOT_OVERRIDE);
+ scheduler_->SetVirtualTimePolicy(
+ PageSchedulerImpl::VirtualTimePolicy::kDeterministicLoading);
+
+ WebScopedVirtualTimePauser pauser =
+ scheduler_->CreateWebScopedVirtualTimePauser(
+ "test", WebScopedVirtualTimePauser::VirtualTaskDuration::kNonInstant);
+
+ base::TimeTicks before = scheduler_->GetVirtualTimeDomain()->Now();
+ pauser.PauseVirtualTime();
+ pauser.UnpauseVirtualTime();
+ base::TimeTicks after = scheduler_->GetVirtualTimeDomain()->Now();
+ EXPECT_GT(after, before);
+}
+
+TEST_F(MainThreadSchedulerImplTest, Tracing) {
+ // This test sets renderer scheduler to some non-trivial state
+ // (by posting tasks, creating child schedulers, etc) and converts it into a
+ // traced value. This test checks that no internal checks fire during this.
+
+ std::unique_ptr<PageSchedulerImpl> page_scheduler1 =
+ base::WrapUnique(new PageSchedulerImpl(nullptr, scheduler_.get(), false));
+ scheduler_->AddPageScheduler(page_scheduler1.get());
+
+ std::unique_ptr<FrameSchedulerImpl> frame_scheduler =
+ page_scheduler1->CreateFrameSchedulerImpl(
+ nullptr, FrameScheduler::FrameType::kSubframe);
+
+ std::unique_ptr<PageSchedulerImpl> page_scheduler2 =
+ base::WrapUnique(new PageSchedulerImpl(nullptr, scheduler_.get(), false));
+ scheduler_->AddPageScheduler(page_scheduler2.get());
+
+ CPUTimeBudgetPool* time_budget_pool =
+ scheduler_->task_queue_throttler()->CreateCPUTimeBudgetPool("test");
+
+ time_budget_pool->AddQueue(base::TimeTicks(), timer_task_runner_.get());
+
+ timer_task_runner_->PostTask(FROM_HERE, base::BindOnce(NullTask));
+
+ loading_task_runner_->PostDelayedTask(FROM_HERE, base::BindOnce(NullTask),
+ base::TimeDelta::FromMilliseconds(10));
+
+ std::unique_ptr<base::trace_event::ConvertableToTraceFormat> value =
+ scheduler_->AsValue(base::TimeTicks());
+ EXPECT_TRUE(value);
+}
+
+void RecordingTimeTestTask(std::vector<base::TimeTicks>* run_times,
+ base::SimpleTestTickClock* clock) {
+ run_times->push_back(clock->NowTicks());
+}
+
+// TODO(altimin@): Re-enable after splitting the timer policy into separate
+// policies.
+TEST_F(MainThreadSchedulerImplTest,
+ DISABLED_DefaultTimerTasksAreThrottledWhenBackgrounded) {
+ ScopedAutoAdvanceNowEnabler enable_auto_advance_now(mock_task_runner_);
+
+ scheduler_->SetRendererBackgrounded(true);
+
+ std::vector<base::TimeTicks> run_times;
+
+ timer_task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&RecordingTimeTestTask, &run_times, &clock_));
+
+ mock_task_runner_->RunUntilTime(base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(1100));
+
+ EXPECT_THAT(run_times, testing::ElementsAre(base::TimeTicks() +
+ base::TimeDelta::FromSeconds(1)));
+ run_times.clear();
+
+ timer_task_runner_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&RecordingTimeTestTask, &run_times, &clock_),
+ base::TimeDelta::FromMilliseconds(200));
+
+ scheduler_->SetRendererBackgrounded(false);
+
+ mock_task_runner_->RunUntilTime(base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(1500));
+
+ EXPECT_THAT(run_times,
+ testing::ElementsAre(base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(1300)));
+}
+
+TEST_F(MainThreadSchedulerImplTest, UnresponsiveMainThread) {
+ EXPECT_FALSE(
+ scheduler_->MainThreadSeemsUnresponsive(responsiveness_threshold()));
+
+ // Add one second long task.
+ AdvanceTimeWithTask(1);
+ EXPECT_TRUE(
+ scheduler_->MainThreadSeemsUnresponsive(responsiveness_threshold()));
+
+ // Wait a second.
+ clock_.Advance(base::TimeDelta::FromSecondsD(2));
+
+ AdvanceTimeWithTask(0.5);
+ EXPECT_FALSE(
+ scheduler_->MainThreadSeemsUnresponsive(responsiveness_threshold()));
+}
+
+// As |responsiveness_threshold| == expected queueing time threshold == 0.2s,
+// for a task shorter than the length of the window (1s), the critical value of
+// the length of task x can be calculated by (x/2) * (x/1) = 0.2, in which x =
+// 0.6324.
+TEST_F(MainThreadSchedulerImplTest, UnresponsiveMainThreadAboveThreshold) {
+ EXPECT_FALSE(
+ scheduler_->MainThreadSeemsUnresponsive(responsiveness_threshold()));
+
+ AdvanceTimeWithTask(0.64);
+ EXPECT_TRUE(
+ scheduler_->MainThreadSeemsUnresponsive(responsiveness_threshold()));
+}
+
+// As |responsiveness_threshold| == expected queueing time threshold == 0.2s,
+// for a task shorter than the length of the window (1s), the critical value of
+// the length of task x can be calculated by (x/2) * (x/1) = 0.2, in which x =
+// 0.6324.
+TEST_F(MainThreadSchedulerImplTest, ResponsiveMainThreadBelowThreshold) {
+ EXPECT_FALSE(
+ scheduler_->MainThreadSeemsUnresponsive(responsiveness_threshold()));
+
+ AdvanceTimeWithTask(0.63);
+ EXPECT_FALSE(
+ scheduler_->MainThreadSeemsUnresponsive(responsiveness_threshold()));
+}
+
+TEST_F(MainThreadSchedulerImplTest, ResponsiveMainThreadDuringTask) {
+ EXPECT_FALSE(
+ scheduler_->MainThreadSeemsUnresponsive(responsiveness_threshold()));
+ clock_.Advance(base::TimeDelta::FromSecondsD(2));
+ scheduler_->OnTaskStarted(fake_queue_.get(), fake_task_, clock_.NowTicks());
+ EXPECT_FALSE(
+ scheduler_->MainThreadSeemsUnresponsive(responsiveness_threshold()));
+}
+
+TEST_F(MainThreadSchedulerImplTest, UnresponsiveMainThreadWithContention) {
+ // Process a long task, lock the queueing time estimator, and check that we
+ // still report the main thread is unresponsive.
+ AdvanceTimeWithTask(1);
+ EXPECT_TRUE(
+ scheduler_->MainThreadSeemsUnresponsive(responsiveness_threshold()));
+ GetQueueingTimeEstimatorLock();
+ EXPECT_TRUE(
+ scheduler_->MainThreadSeemsUnresponsive(responsiveness_threshold()));
+
+ // Advance the clock, so that in the last second, we were responsive.
+ clock_.Advance(base::TimeDelta::FromSecondsD(2));
+ // While the queueing time estimator is locked, we believe the thread to still
+ // be unresponsive.
+ EXPECT_TRUE(
+ scheduler_->MainThreadSeemsUnresponsive(responsiveness_threshold()));
+ // Once we've dropped the lock, we realize the main thread is responsive.
+ DropQueueingTimeEstimatorLock();
+ EXPECT_FALSE(
+ scheduler_->MainThreadSeemsUnresponsive(responsiveness_threshold()));
+}
+
+// Nav Start Nav Start assert
+// | | |
+// v v v
+// ------------------------------------------------------------>
+// |---long task---|---1s task---|-----long task ----|
+//
+// (---MaxEQT1---)
+// (---MaxEQT2---)
+//
+// --- EQT untracked---| |---EQT unflushed-----
+//
+// MaxEQT1 = 500ms is recorded and observed in histogram.
+// MaxEQT2 is recorded but not yet in histogram for not being flushed.
+TEST_F(MainThreadSchedulerImplTest,
+ MaxQueueingTimeMetricRecordedOnlyDuringNavigation) {
+ base::HistogramTester tester;
+ // Start with a long task whose queueing time will be ignored.
+ AdvanceTimeWithTask(10);
+ // Navigation start.
+ scheduler_->DidCommitProvisionalLoad(false, false, false);
+ // The max queueing time of the following task will be recorded.
+ AdvanceTimeWithTask(1);
+ // The smaller queuing time will be ignored.
+ AdvanceTimeWithTask(0.5);
+ scheduler_->DidCommitProvisionalLoad(false, false, false);
+ // Add another long task after navigation start but without navigation end.
+ // This value won't be recorded as there is not navigation.
+ AdvanceTimeWithTask(10);
+ // The expected queueing time of 1s task in 1s window is 500ms.
+ tester.ExpectUniqueSample("RendererScheduler.MaxQueueingTime", 500, 1);
+}
+
+// Only the max of all the queueing times is recorded.
+TEST_F(MainThreadSchedulerImplTest, MaxQueueingTimeMetricRecordTheMax) {
+ base::HistogramTester tester;
+ scheduler_->DidCommitProvisionalLoad(false, false, false);
+ // The smaller queuing time will be ignored.
+ AdvanceTimeWithTask(0.5);
+ // The max queueing time of the following task will be recorded.
+ AdvanceTimeWithTask(1);
+ // The smaller queuing time will be ignored.
+ AdvanceTimeWithTask(0.5);
+ scheduler_->DidCommitProvisionalLoad(false, false, false);
+ tester.ExpectUniqueSample("RendererScheduler.MaxQueueingTime", 500, 1);
+}
+
+TEST_F(MainThreadSchedulerImplTest, DidCommitProvisionalLoad) {
+ scheduler_->OnFirstMeaningfulPaint();
+ EXPECT_FALSE(scheduler_->waiting_for_meaningful_paint());
+
+ // Check that we only clear state for main frame navigations that are either
+ // not history inert or are reloads.
+ scheduler_->DidCommitProvisionalLoad(false /* is_web_history_inert_commit */,
+ false /* is_reload */,
+ false /* is_main_frame */);
+ EXPECT_FALSE(scheduler_->waiting_for_meaningful_paint());
+
+ scheduler_->OnFirstMeaningfulPaint();
+ scheduler_->DidCommitProvisionalLoad(false /* is_web_history_inert_commit */,
+ false /* is_reload */,
+ true /* is_main_frame */);
+ EXPECT_TRUE(scheduler_->waiting_for_meaningful_paint()); // State cleared.
+
+ scheduler_->OnFirstMeaningfulPaint();
+ scheduler_->DidCommitProvisionalLoad(false /* is_web_history_inert_commit */,
+ true /* is_reload */,
+ false /* is_main_frame */);
+ EXPECT_FALSE(scheduler_->waiting_for_meaningful_paint());
+
+ scheduler_->OnFirstMeaningfulPaint();
+ scheduler_->DidCommitProvisionalLoad(false /* is_web_history_inert_commit */,
+ true /* is_reload */,
+ true /* is_main_frame */);
+ EXPECT_TRUE(scheduler_->waiting_for_meaningful_paint()); // State cleared.
+
+ scheduler_->OnFirstMeaningfulPaint();
+ scheduler_->DidCommitProvisionalLoad(true /* is_web_history_inert_commit */,
+ false /* is_reload */,
+ false /* is_main_frame */);
+ EXPECT_FALSE(scheduler_->waiting_for_meaningful_paint());
+
+ scheduler_->OnFirstMeaningfulPaint();
+ scheduler_->DidCommitProvisionalLoad(true /* is_web_history_inert_commit */,
+ false /* is_reload */,
+ true /* is_main_frame */);
+ EXPECT_FALSE(scheduler_->waiting_for_meaningful_paint());
+
+ scheduler_->OnFirstMeaningfulPaint();
+ scheduler_->DidCommitProvisionalLoad(true /* is_web_history_inert_commit */,
+ true /* is_reload */,
+ false /* is_main_frame */);
+ EXPECT_FALSE(scheduler_->waiting_for_meaningful_paint());
+
+ scheduler_->OnFirstMeaningfulPaint();
+ scheduler_->DidCommitProvisionalLoad(true /* is_web_history_inert_commit */,
+ true /* is_reload */,
+ true /* is_main_frame */);
+ EXPECT_TRUE(scheduler_->waiting_for_meaningful_paint()); // State cleared.
+}
+
+TEST_F(MainThreadSchedulerImplTest, LoadingControlTasks) {
+ // Expect control loading tasks (M) to jump ahead of any regular loading
+ // tasks (L).
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "L1 L2 M1 L3 L4 M2 L5 L6");
+ RunUntilIdle();
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("M1"), std::string("M2"),
+ std::string("L1"), std::string("L2"),
+ std::string("L3"), std::string("L4"),
+ std::string("L5"), std::string("L6")));
+}
+
+TEST_F(MainThreadSchedulerImplTest, RequestBeginMainFrameNotExpected) {
+ std::unique_ptr<PageSchedulerImplForTest> page_scheduler =
+ std::make_unique<PageSchedulerImplForTest>(scheduler_.get());
+ scheduler_->AddPageScheduler(page_scheduler.get());
+
+ scheduler_->OnPendingTasksChanged(true);
+ EXPECT_CALL(*page_scheduler, RequestBeginMainFrameNotExpected(true)).Times(1);
+ RunUntilIdle();
+
+ Mock::VerifyAndClearExpectations(page_scheduler.get());
+
+ scheduler_->OnPendingTasksChanged(false);
+ EXPECT_CALL(*page_scheduler, RequestBeginMainFrameNotExpected(false))
+ .Times(1);
+ RunUntilIdle();
+
+ Mock::VerifyAndClearExpectations(page_scheduler.get());
+}
+
+TEST_F(MainThreadSchedulerImplTest,
+ RequestBeginMainFrameNotExpected_MultipleCalls) {
+ std::unique_ptr<PageSchedulerImplForTest> page_scheduler =
+ std::make_unique<PageSchedulerImplForTest>(scheduler_.get());
+ scheduler_->AddPageScheduler(page_scheduler.get());
+
+ scheduler_->OnPendingTasksChanged(true);
+ scheduler_->OnPendingTasksChanged(true);
+ // Multiple calls should result in only one call.
+ EXPECT_CALL(*page_scheduler, RequestBeginMainFrameNotExpected(true)).Times(1);
+ RunUntilIdle();
+
+ Mock::VerifyAndClearExpectations(page_scheduler.get());
+}
+
+#if defined(OS_ANDROID)
+TEST_F(MainThreadSchedulerImplTest, PauseTimersForAndroidWebView) {
+ ScopedAutoAdvanceNowEnabler enable_auto_advance_now(mock_task_runner_);
+ // Tasks in some queues don't fire when the timers are paused.
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "D1 C1 L1 I1 T1");
+ scheduler_->PauseTimersForAndroidWebView();
+ EnableIdleTasks();
+ RunUntilIdle();
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("D1"), std::string("C1"),
+ std::string("L1"), std::string("I1")));
+ // The rest queued tasks fire when the timers are resumed.
+ run_order.clear();
+ scheduler_->ResumeTimersForAndroidWebView();
+ RunUntilIdle();
+ EXPECT_THAT(run_order, testing::ElementsAre(std::string("T1")));
+}
+#endif // defined(OS_ANDROID)
+
+class MainThreadSchedulerImplWithInitalVirtualTimeTest
+ : public MainThreadSchedulerImplTest {
+ public:
+ void SetUp() override {
+ if (!message_loop_) {
+ mock_task_runner_ =
+ base::MakeRefCounted<cc::OrderedSimpleTaskRunner>(&clock_, false);
+ }
+ Initialize(std::make_unique<MainThreadSchedulerImplForTest>(
+ TaskQueueManagerForTest::Create(
+ message_loop_.get(),
+ message_loop_ ? message_loop_->task_runner() : mock_task_runner_,
+ &clock_),
+ base::Time::FromJsTime(1000000.0)));
+ }
+};
+
+TEST_F(MainThreadSchedulerImplWithInitalVirtualTimeTest, VirtualTimeOverride) {
+ EXPECT_TRUE(scheduler_->IsVirtualTimeEnabled());
+ EXPECT_EQ(PageSchedulerImpl::VirtualTimePolicy::kPause,
+ scheduler_->virtual_time_policy());
+ EXPECT_EQ(base::Time::Now(), base::Time::FromJsTime(1000000.0));
+}
+
+} // namespace main_thread_scheduler_impl_unittest
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
new file mode 100644
index 00000000000..c105a49f2f9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
@@ -0,0 +1,400 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h"
+
+#include "base/logging.h"
+#include "base/metrics/field_trial_params.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/scheduler/base/virtual_time_domain.h"
+#include "third_party/blink/renderer/platform/scheduler/child/default_params.h"
+#include "third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
+#include "third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain.h"
+
+namespace blink {
+namespace scheduler {
+
+namespace {
+
+constexpr double kDefaultBackgroundBudgetAsCPUFraction = .01;
+constexpr double kDefaultMaxBackgroundBudgetLevelInSeconds = 3;
+constexpr double kDefaultInitialBackgroundBudgetInSeconds = 1;
+constexpr double kDefaultMaxBackgroundThrottlingDelayInSeconds = 0;
+
+// Given that we already align timers to 1Hz, do not report throttling if
+// it is under 3s.
+constexpr base::TimeDelta kMinimalBackgroundThrottlingDurationToReport =
+ base::TimeDelta::FromSeconds(3);
+
+// Values coming from the field trial config are interpreted as follows:
+// -1 is "not set". Scheduler should use a reasonable default.
+// 0 corresponds to base::nullopt.
+// Other values are left without changes.
+
+struct BackgroundThrottlingSettings {
+ double budget_recovery_rate;
+ base::Optional<base::TimeDelta> max_budget_level;
+ base::Optional<base::TimeDelta> max_throttling_delay;
+ base::Optional<base::TimeDelta> initial_budget;
+};
+
+double GetDoubleParameterFromMap(
+ const std::map<std::string, std::string>& settings,
+ const std::string& setting_name,
+ double default_value) {
+ const auto& find_it = settings.find(setting_name);
+ if (find_it == settings.end())
+ return default_value;
+ double parsed_value;
+ if (!base::StringToDouble(find_it->second, &parsed_value))
+ return default_value;
+ if (parsed_value == -1)
+ return default_value;
+ return parsed_value;
+}
+
+base::Optional<base::TimeDelta> DoubleToOptionalTime(double value) {
+ if (value == 0)
+ return base::nullopt;
+ return base::TimeDelta::FromSecondsD(value);
+}
+
+BackgroundThrottlingSettings GetBackgroundThrottlingSettings() {
+ std::map<std::string, std::string> background_throttling_settings;
+ base::GetFieldTrialParams("ExpensiveBackgroundTimerThrottling",
+ &background_throttling_settings);
+
+ BackgroundThrottlingSettings settings;
+
+ settings.budget_recovery_rate =
+ GetDoubleParameterFromMap(background_throttling_settings, "cpu_budget",
+ kDefaultBackgroundBudgetAsCPUFraction);
+
+ settings.max_budget_level = DoubleToOptionalTime(
+ GetDoubleParameterFromMap(background_throttling_settings, "max_budget",
+ kDefaultMaxBackgroundBudgetLevelInSeconds));
+
+ settings.max_throttling_delay = DoubleToOptionalTime(
+ GetDoubleParameterFromMap(background_throttling_settings, "max_delay",
+ kDefaultMaxBackgroundThrottlingDelayInSeconds));
+
+ settings.initial_budget = DoubleToOptionalTime(GetDoubleParameterFromMap(
+ background_throttling_settings, "initial_budget",
+ kDefaultInitialBackgroundBudgetInSeconds));
+
+ return settings;
+}
+
+} // namespace
+
+PageSchedulerImpl::PageSchedulerImpl(
+ PageScheduler::Delegate* delegate,
+ MainThreadSchedulerImpl* main_thread_scheduler,
+ bool disable_background_timer_throttling)
+ : main_thread_scheduler_(main_thread_scheduler),
+ page_visibility_(kDefaultPageVisibility),
+ disable_background_timer_throttling_(disable_background_timer_throttling),
+ is_audio_playing_(false),
+ is_frozen_(false),
+ reported_background_throttling_since_navigation_(false),
+ has_active_connection_(false),
+ nested_runloop_(false),
+ is_main_frame_local_(false),
+ background_time_budget_pool_(nullptr),
+ delegate_(delegate),
+ weak_factory_(this) {
+ main_thread_scheduler->AddPageScheduler(this);
+}
+
+PageSchedulerImpl::~PageSchedulerImpl() {
+ // TODO(alexclarke): Find out why we can't rely on the web view outliving the
+ // frame.
+ for (FrameSchedulerImpl* frame_scheduler : frame_schedulers_) {
+ frame_scheduler->DetachFromPageScheduler();
+ }
+ main_thread_scheduler_->RemovePageScheduler(this);
+
+ if (background_time_budget_pool_)
+ background_time_budget_pool_->Close();
+}
+
+void PageSchedulerImpl::SetPageVisible(bool page_visible) {
+ PageVisibilityState page_visibility = page_visible
+ ? PageVisibilityState::kVisible
+ : PageVisibilityState::kHidden;
+
+ if (disable_background_timer_throttling_ ||
+ page_visibility_ == page_visibility)
+ return;
+
+ page_visibility_ = page_visibility;
+
+ UpdateBackgroundThrottlingState();
+
+ // Visible pages should not be frozen.
+ if (page_visibility_ == PageVisibilityState::kVisible && is_frozen_)
+ SetPageFrozen(false);
+}
+
+void PageSchedulerImpl::SetPageFrozen(bool frozen) {
+ if (is_frozen_ == frozen)
+ return;
+ is_frozen_ = frozen;
+ for (FrameSchedulerImpl* frame_scheduler : frame_schedulers_)
+ frame_scheduler->SetPageFrozen(frozen);
+ if (delegate_)
+ delegate_->SetPageFrozen(frozen);
+}
+
+void PageSchedulerImpl::SetKeepActive(bool keep_active) {
+ for (FrameSchedulerImpl* frame_scheduler : frame_schedulers_)
+ frame_scheduler->SetKeepActive(keep_active);
+}
+
+bool PageSchedulerImpl::IsMainFrameLocal() const {
+ return is_main_frame_local_;
+}
+
+void PageSchedulerImpl::SetIsMainFrameLocal(bool is_local) {
+ is_main_frame_local_ = is_local;
+}
+
+std::unique_ptr<FrameSchedulerImpl> PageSchedulerImpl::CreateFrameSchedulerImpl(
+ base::trace_event::BlameContext* blame_context,
+ FrameScheduler::FrameType frame_type) {
+ MaybeInitializeBackgroundCPUTimeBudgetPool();
+ std::unique_ptr<FrameSchedulerImpl> frame_scheduler(new FrameSchedulerImpl(
+ main_thread_scheduler_, this, blame_context, frame_type));
+ frame_scheduler->SetPageVisibility(page_visibility_);
+ frame_schedulers_.insert(frame_scheduler.get());
+ return frame_scheduler;
+}
+
+std::unique_ptr<blink::FrameScheduler> PageSchedulerImpl::CreateFrameScheduler(
+ blink::BlameContext* blame_context,
+ FrameScheduler::FrameType frame_type) {
+ return CreateFrameSchedulerImpl(blame_context, frame_type);
+}
+
+void PageSchedulerImpl::Unregister(FrameSchedulerImpl* frame_scheduler) {
+ DCHECK(frame_schedulers_.find(frame_scheduler) != frame_schedulers_.end());
+ frame_schedulers_.erase(frame_scheduler);
+}
+
+void PageSchedulerImpl::OnNavigation() {
+ reported_background_throttling_since_navigation_ = false;
+}
+
+void PageSchedulerImpl::ReportIntervention(const std::string& message) {
+ delegate_->ReportIntervention(String::FromUTF8(message.c_str()));
+}
+
+base::TimeTicks PageSchedulerImpl::EnableVirtualTime() {
+ return main_thread_scheduler_->EnableVirtualTime(
+ MainThreadSchedulerImpl::BaseTimeOverridePolicy::DO_NOT_OVERRIDE);
+}
+
+void PageSchedulerImpl::DisableVirtualTimeForTesting() {
+ main_thread_scheduler_->DisableVirtualTimeForTesting();
+}
+
+void PageSchedulerImpl::SetVirtualTimePolicy(VirtualTimePolicy policy) {
+ main_thread_scheduler_->SetVirtualTimePolicy(policy);
+}
+
+void PageSchedulerImpl::SetInitialVirtualTimeOffset(base::TimeDelta offset) {
+ main_thread_scheduler_->SetInitialVirtualTimeOffset(offset);
+}
+
+bool PageSchedulerImpl::VirtualTimeAllowedToAdvance() const {
+ return main_thread_scheduler_->VirtualTimeAllowedToAdvance();
+}
+
+void PageSchedulerImpl::GrantVirtualTimeBudget(
+ base::TimeDelta budget,
+ base::OnceClosure budget_exhausted_callback) {
+ main_thread_scheduler_->VirtualTimeControlTaskQueue()->PostDelayedTask(
+ FROM_HERE, std::move(budget_exhausted_callback), budget);
+ // This can shift time forwards if there's a pending MaybeAdvanceVirtualTime,
+ // so it's important this is called second.
+ main_thread_scheduler_->GetVirtualTimeDomain()->SetVirtualTimeFence(
+ main_thread_scheduler_->GetVirtualTimeDomain()->Now() + budget);
+}
+
+void PageSchedulerImpl::AddVirtualTimeObserver(VirtualTimeObserver* observer) {
+ main_thread_scheduler_->AddVirtualTimeObserver(observer);
+}
+
+void PageSchedulerImpl::RemoveVirtualTimeObserver(
+ VirtualTimeObserver* observer) {
+ main_thread_scheduler_->RemoveVirtualTimeObserver(observer);
+}
+
+void PageSchedulerImpl::AudioStateChanged(bool is_audio_playing) {
+ is_audio_playing_ = is_audio_playing;
+ main_thread_scheduler_->OnAudioStateChanged();
+}
+
+bool PageSchedulerImpl::IsExemptFromBudgetBasedThrottling() const {
+ return has_active_connection_;
+}
+
+bool PageSchedulerImpl::HasActiveConnectionForTest() const {
+ return has_active_connection_;
+}
+
+void PageSchedulerImpl::RequestBeginMainFrameNotExpected(bool new_state) {
+ delegate_->RequestBeginMainFrameNotExpected(new_state);
+}
+
+bool PageSchedulerImpl::IsPlayingAudio() const {
+ return is_audio_playing_;
+}
+
+bool PageSchedulerImpl::IsFrozen() const {
+ return is_frozen_;
+}
+
+void PageSchedulerImpl::OnConnectionUpdated() {
+ bool has_active_connection = false;
+ for (FrameSchedulerImpl* frame_scheduler : frame_schedulers_) {
+ has_active_connection |= frame_scheduler->has_active_connection();
+ }
+
+ if (has_active_connection_ != has_active_connection) {
+ has_active_connection_ = has_active_connection;
+ UpdateBackgroundThrottlingState();
+ }
+}
+
+void PageSchedulerImpl::OnTraceLogEnabled() {
+ tracing_controller_.OnTraceLogEnabled();
+ for (FrameSchedulerImpl* frame_scheduler : frame_schedulers_) {
+ frame_scheduler->OnTraceLogEnabled();
+ }
+}
+
+void PageSchedulerImpl::AsValueInto(
+ base::trace_event::TracedValue* state) const {
+ state->SetBoolean("page_visible",
+ page_visibility_ == PageVisibilityState::kVisible);
+ state->SetBoolean("disable_background_timer_throttling",
+ disable_background_timer_throttling_);
+ state->SetBoolean("is_audio_playing", is_audio_playing_);
+ state->SetBoolean("is_frozen", is_frozen_);
+ state->SetBoolean("reported_background_throttling_since_navigation",
+ reported_background_throttling_since_navigation_);
+
+ state->BeginDictionary("frame_schedulers");
+ for (FrameSchedulerImpl* frame_scheduler : frame_schedulers_) {
+ state->BeginDictionaryWithCopiedName(PointerToString(frame_scheduler));
+ frame_scheduler->AsValueInto(state);
+ state->EndDictionary();
+ }
+ state->EndDictionary();
+}
+
+CPUTimeBudgetPool* PageSchedulerImpl::BackgroundCPUTimeBudgetPool() {
+ MaybeInitializeBackgroundCPUTimeBudgetPool();
+ return background_time_budget_pool_;
+}
+
+void PageSchedulerImpl::MaybeInitializeBackgroundCPUTimeBudgetPool() {
+ if (background_time_budget_pool_)
+ return;
+
+ if (!RuntimeEnabledFeatures::ExpensiveBackgroundTimerThrottlingEnabled())
+ return;
+
+ background_time_budget_pool_ =
+ main_thread_scheduler_->task_queue_throttler()->CreateCPUTimeBudgetPool(
+ "background");
+ LazyNow lazy_now(main_thread_scheduler_->tick_clock());
+
+ BackgroundThrottlingSettings settings = GetBackgroundThrottlingSettings();
+
+ background_time_budget_pool_->SetMaxBudgetLevel(lazy_now.Now(),
+ settings.max_budget_level);
+ background_time_budget_pool_->SetMaxThrottlingDelay(
+ lazy_now.Now(), settings.max_throttling_delay);
+
+ UpdateBackgroundThrottlingState();
+
+ background_time_budget_pool_->SetTimeBudgetRecoveryRate(
+ lazy_now.Now(), settings.budget_recovery_rate);
+
+ if (settings.initial_budget) {
+ background_time_budget_pool_->GrantAdditionalBudget(
+ lazy_now.Now(), settings.initial_budget.value());
+ }
+}
+
+void PageSchedulerImpl::OnThrottlingReported(
+ base::TimeDelta throttling_duration) {
+ if (throttling_duration < kMinimalBackgroundThrottlingDurationToReport)
+ return;
+
+ if (reported_background_throttling_since_navigation_)
+ return;
+ reported_background_throttling_since_navigation_ = true;
+
+ std::string message = base::StringPrintf(
+ "Timer tasks have taken too much time while the page was in the "
+ "background. "
+ "As a result, they have been deferred for %.3f seconds. "
+ "See https://www.chromestatus.com/feature/6172836527865856 "
+ "for more details",
+ throttling_duration.InSecondsF());
+
+ delegate_->ReportIntervention(String::FromUTF8(message.c_str()));
+}
+
+void PageSchedulerImpl::UpdateBackgroundThrottlingState() {
+ for (FrameSchedulerImpl* frame_scheduler : frame_schedulers_)
+ frame_scheduler->SetPageVisibility(page_visibility_);
+ UpdateBackgroundBudgetPoolThrottlingState();
+}
+
+void PageSchedulerImpl::UpdateBackgroundBudgetPoolThrottlingState() {
+ if (!background_time_budget_pool_)
+ return;
+
+ LazyNow lazy_now(main_thread_scheduler_->tick_clock());
+ if (page_visibility_ == PageVisibilityState::kVisible ||
+ has_active_connection_) {
+ background_time_budget_pool_->DisableThrottling(&lazy_now);
+ } else {
+ background_time_budget_pool_->EnableThrottling(&lazy_now);
+ }
+}
+
+size_t PageSchedulerImpl::FrameCount() const {
+ return frame_schedulers_.size();
+}
+
+void PageSchedulerImpl::SetMaxVirtualTimeTaskStarvationCount(
+ int max_task_starvation_count) {
+ main_thread_scheduler_->SetMaxVirtualTimeTaskStarvationCount(
+ max_task_starvation_count);
+}
+
+ukm::UkmRecorder* PageSchedulerImpl::GetUkmRecorder() {
+ if (!delegate_)
+ return nullptr;
+ return delegate_->GetUkmRecorder();
+}
+
+int64_t PageSchedulerImpl::GetUkmSourceId() {
+ if (!delegate_)
+ return 0;
+ return delegate_->GetUkmSourceId();
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h
new file mode 100644
index 00000000000..beee564fefd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h
@@ -0,0 +1,140 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_PAGE_SCHEDULER_IMPL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_PAGE_SCHEDULER_IMPL_H_
+
+#include <memory>
+#include <set>
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue.h"
+#include "third_party/blink/renderer/platform/scheduler/child/page_visibility_state.h"
+#include "third_party/blink/renderer/platform/scheduler/child/web_scheduler.h"
+#include "third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h"
+#include "third_party/blink/renderer/platform/scheduler/public/page_scheduler.h"
+#include "third_party/blink/renderer/platform/scheduler/util/tracing_helper.h"
+
+namespace base {
+namespace trace_event {
+class BlameContext;
+class TracedValue;
+} // namespace trace_event
+} // namespace base
+
+namespace blink {
+namespace scheduler {
+
+class CPUTimeBudgetPool;
+class FrameSchedulerImpl;
+class MainThreadSchedulerImpl;
+
+class PLATFORM_EXPORT PageSchedulerImpl : public PageScheduler {
+ public:
+ PageSchedulerImpl(PageScheduler::Delegate*,
+ MainThreadSchedulerImpl*,
+ bool disable_background_timer_throttling);
+
+ ~PageSchedulerImpl() override;
+
+ // PageScheduler implementation:
+ void SetPageVisible(bool page_visible) override;
+ void SetPageFrozen(bool) override;
+ void SetKeepActive(bool) override;
+ bool IsMainFrameLocal() const override;
+ void SetIsMainFrameLocal(bool is_local) override;
+
+ std::unique_ptr<FrameScheduler> CreateFrameScheduler(
+ BlameContext*,
+ FrameScheduler::FrameType) override;
+ base::TimeTicks EnableVirtualTime() override;
+ void DisableVirtualTimeForTesting() override;
+ bool VirtualTimeAllowedToAdvance() const override;
+ void SetVirtualTimePolicy(VirtualTimePolicy) override;
+ void SetInitialVirtualTimeOffset(base::TimeDelta offset) override;
+ void GrantVirtualTimeBudget(
+ base::TimeDelta budget,
+ base::OnceClosure budget_exhausted_callback) override;
+ void SetMaxVirtualTimeTaskStarvationCount(
+ int max_task_starvation_count) override;
+ void AudioStateChanged(bool is_audio_playing) override;
+ bool IsPlayingAudio() const override;
+ bool IsExemptFromBudgetBasedThrottling() const override;
+ bool HasActiveConnectionForTest() const override;
+ void RequestBeginMainFrameNotExpected(bool new_state) override;
+ void AddVirtualTimeObserver(VirtualTimeObserver*) override;
+ void RemoveVirtualTimeObserver(VirtualTimeObserver*) override;
+
+ // Virtual for testing.
+ virtual void ReportIntervention(const std::string& message);
+
+ bool IsFrozen() const;
+
+ std::unique_ptr<FrameSchedulerImpl> CreateFrameSchedulerImpl(
+ base::trace_event::BlameContext*,
+ FrameScheduler::FrameType);
+
+ void Unregister(FrameSchedulerImpl*);
+ void OnNavigation();
+
+ void OnConnectionUpdated();
+
+ void OnTraceLogEnabled();
+
+ // Return a number of child web frame schedulers for this PageScheduler.
+ size_t FrameCount() const;
+
+ void AsValueInto(base::trace_event::TracedValue* state) const;
+
+ ukm::UkmRecorder* GetUkmRecorder();
+ int64_t GetUkmSourceId();
+
+ base::WeakPtr<PageSchedulerImpl> GetWeakPtr() {
+ return weak_factory_.GetWeakPtr();
+ }
+
+ private:
+ friend class FrameSchedulerImpl;
+
+ CPUTimeBudgetPool* BackgroundCPUTimeBudgetPool();
+ void MaybeInitializeBackgroundCPUTimeBudgetPool();
+
+ void OnThrottlingReported(base::TimeDelta throttling_duration);
+
+ // Depending on page visibility, either turns throttling off, or schedules a
+ // call to enable it after a grace period.
+ void UpdateBackgroundThrottlingState();
+
+ // As a part of UpdateBackgroundThrottlingState set correct
+ // background_time_budget_pool_ state depending on page visibility and
+ // number of active connections.
+ void UpdateBackgroundBudgetPoolThrottlingState();
+
+ TraceableVariableController tracing_controller_;
+ std::set<FrameSchedulerImpl*> frame_schedulers_;
+ MainThreadSchedulerImpl* main_thread_scheduler_;
+
+ PageVisibilityState page_visibility_;
+ bool disable_background_timer_throttling_;
+ bool is_audio_playing_;
+ bool is_frozen_;
+ bool reported_background_throttling_since_navigation_;
+ bool has_active_connection_;
+ bool nested_runloop_;
+ bool is_main_frame_local_;
+ CPUTimeBudgetPool* background_time_budget_pool_; // Not owned.
+ PageScheduler::Delegate* delegate_; // Not owned.
+ base::WeakPtrFactory<PageSchedulerImpl> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(PageSchedulerImpl);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_PAGE_SCHEDULER_IMPL_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc
new file mode 100644
index 00000000000..0b5260dbb29
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc
@@ -0,0 +1,1174 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h"
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/metrics/field_trial.h"
+#include "base/metrics/field_trial_param_associator.h"
+#include "base/metrics/field_trial_params.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "components/viz/test/ordered_simple_task_runner.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/scheduler/child/task_runner_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/test/task_queue_manager_for_test.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+
+using testing::ElementsAre;
+using VirtualTimePolicy = blink::PageScheduler::VirtualTimePolicy;
+
+namespace blink {
+namespace scheduler {
+// To avoid symbol collisions in jumbo builds.
+namespace page_scheduler_impl_unittest {
+
+class PageSchedulerImplTest : public testing::Test {
+ public:
+ PageSchedulerImplTest() = default;
+ ~PageSchedulerImplTest() override = default;
+
+ void SetUp() override {
+ clock_.Advance(base::TimeDelta::FromMicroseconds(5000));
+ mock_task_runner_ =
+ base::MakeRefCounted<cc::OrderedSimpleTaskRunner>(&clock_, true);
+ scheduler_.reset(new MainThreadSchedulerImpl(
+ TaskQueueManagerForTest::Create(nullptr, mock_task_runner_, &clock_),
+ base::nullopt));
+ page_scheduler_.reset(new PageSchedulerImpl(
+ nullptr, scheduler_.get(), DisableBackgroundTimerThrottling()));
+ frame_scheduler_ = page_scheduler_->CreateFrameSchedulerImpl(
+ nullptr, FrameScheduler::FrameType::kSubframe);
+ }
+
+ void TearDown() override {
+ frame_scheduler_.reset();
+ page_scheduler_.reset();
+ scheduler_->Shutdown();
+ scheduler_.reset();
+ }
+
+ virtual bool DisableBackgroundTimerThrottling() const { return false; }
+
+ protected:
+ static scoped_refptr<TaskQueue> ThrottleableTaskQueueForScheduler(
+ FrameSchedulerImpl* scheduler) {
+ return scheduler->ThrottleableTaskQueue();
+ }
+
+ scoped_refptr<base::SingleThreadTaskRunner> ThrottleableTaskRunner() {
+ return TaskRunnerImpl::Create(ThrottleableTaskQueue(),
+ TaskType::kInternalTest);
+ }
+
+ scoped_refptr<base::SingleThreadTaskRunner> LoadingTaskRunner() {
+ return TaskRunnerImpl::Create(LoadingTaskQueue(), TaskType::kInternalTest);
+ }
+
+ scoped_refptr<TaskQueue> ThrottleableTaskQueue() {
+ return frame_scheduler_->ThrottleableTaskQueue();
+ }
+
+ scoped_refptr<TaskQueue> LoadingTaskQueue() {
+ return frame_scheduler_->LoadingTaskQueue();
+ }
+
+ scoped_refptr<TaskQueue> DeferrableTaskQueue() {
+ return frame_scheduler_->DeferrableTaskQueue();
+ }
+
+ scoped_refptr<TaskQueue> PausableTaskQueue() {
+ return frame_scheduler_->PausableTaskQueue();
+ }
+
+ scoped_refptr<TaskQueue> UnpausableTaskQueue() {
+ return frame_scheduler_->UnpausableTaskQueue();
+ }
+
+ base::SimpleTestTickClock clock_;
+ scoped_refptr<cc::OrderedSimpleTaskRunner> mock_task_runner_;
+ std::unique_ptr<MainThreadSchedulerImpl> scheduler_;
+ std::unique_ptr<PageSchedulerImpl> page_scheduler_;
+ std::unique_ptr<FrameSchedulerImpl> frame_scheduler_;
+};
+
+TEST_F(PageSchedulerImplTest, TestDestructionOfFrameSchedulersBefore) {
+ std::unique_ptr<blink::FrameScheduler> frame1(
+ page_scheduler_->CreateFrameScheduler(
+ nullptr, FrameScheduler::FrameType::kSubframe));
+ std::unique_ptr<blink::FrameScheduler> frame2(
+ page_scheduler_->CreateFrameScheduler(
+ nullptr, FrameScheduler::FrameType::kSubframe));
+}
+
+TEST_F(PageSchedulerImplTest, TestDestructionOfFrameSchedulersAfter) {
+ std::unique_ptr<blink::FrameScheduler> frame1(
+ page_scheduler_->CreateFrameScheduler(
+ nullptr, FrameScheduler::FrameType::kSubframe));
+ std::unique_ptr<blink::FrameScheduler> frame2(
+ page_scheduler_->CreateFrameScheduler(
+ nullptr, FrameScheduler::FrameType::kSubframe));
+ page_scheduler_.reset();
+}
+
+namespace {
+
+void RunRepeatingTask(scoped_refptr<TaskQueue>, int* run_count);
+
+base::OnceClosure MakeRepeatingTask(scoped_refptr<TaskQueue> task_queue,
+ int* run_count) {
+ return base::BindOnce(&RunRepeatingTask, std::move(task_queue),
+ base::Unretained(run_count));
+}
+
+void RunRepeatingTask(scoped_refptr<TaskQueue> task_queue, int* run_count) {
+ ++*run_count;
+ TaskQueue* task_queue_ptr = task_queue.get();
+ task_queue_ptr->PostDelayedTask(
+ FROM_HERE, MakeRepeatingTask(std::move(task_queue_ptr), run_count),
+ base::TimeDelta::FromMilliseconds(1));
+}
+
+} // namespace
+
+TEST_F(PageSchedulerImplTest, RepeatingTimer_PageInForeground) {
+ page_scheduler_->SetPageVisible(true);
+
+ int run_count = 0;
+ ThrottleableTaskQueue()->PostDelayedTask(
+ FROM_HERE, MakeRepeatingTask(ThrottleableTaskQueue(), &run_count),
+ base::TimeDelta::FromMilliseconds(1));
+
+ mock_task_runner_->RunForPeriod(base::TimeDelta::FromSeconds(1));
+ EXPECT_EQ(1000, run_count);
+}
+
+TEST_F(PageSchedulerImplTest, RepeatingTimer_PageInBackgroundThenForeground) {
+ page_scheduler_->SetPageVisible(false);
+
+ int run_count = 0;
+ ThrottleableTaskQueue()->PostDelayedTask(
+ FROM_HERE, MakeRepeatingTask(ThrottleableTaskQueue(), &run_count),
+ base::TimeDelta::FromMilliseconds(1));
+
+ mock_task_runner_->RunForPeriod(base::TimeDelta::FromSeconds(1));
+ EXPECT_EQ(1, run_count);
+
+ // Make sure there's no delay in throttling being removed for pages that have
+ // become visible.
+ page_scheduler_->SetPageVisible(true);
+
+ run_count = 0;
+ mock_task_runner_->RunForPeriod(base::TimeDelta::FromSeconds(1));
+ EXPECT_EQ(1001, run_count); // Note we end up running 1001 here because the
+ // task was posted while throttled with a delay of 1ms so the first task was
+ // due to run before the 1s period started.
+}
+
+TEST_F(PageSchedulerImplTest, RepeatingLoadingTask_PageInBackground) {
+ page_scheduler_->SetPageVisible(false);
+
+ int run_count = 0;
+ LoadingTaskQueue()->PostDelayedTask(
+ FROM_HERE, MakeRepeatingTask(LoadingTaskQueue(), &run_count),
+ base::TimeDelta::FromMilliseconds(1));
+
+ mock_task_runner_->RunForPeriod(base::TimeDelta::FromSeconds(1));
+ EXPECT_EQ(1000, run_count); // Loading tasks should not be throttled
+}
+
+TEST_F(PageSchedulerImplTest, RepeatingTimers_OneBackgroundOneForeground) {
+ std::unique_ptr<PageSchedulerImpl> page_scheduler2(
+ new PageSchedulerImpl(nullptr, scheduler_.get(), false));
+ std::unique_ptr<FrameSchedulerImpl> frame_scheduler2 =
+ page_scheduler2->CreateFrameSchedulerImpl(
+ nullptr, FrameScheduler::FrameType::kSubframe);
+
+ page_scheduler_->SetPageVisible(true);
+ page_scheduler2->SetPageVisible(false);
+
+ int run_count1 = 0;
+ int run_count2 = 0;
+ ThrottleableTaskQueue()->PostDelayedTask(
+ FROM_HERE, MakeRepeatingTask(ThrottleableTaskQueue(), &run_count1),
+ base::TimeDelta::FromMilliseconds(1));
+ ThrottleableTaskQueueForScheduler(frame_scheduler2.get())
+ ->PostDelayedTask(FROM_HERE,
+ MakeRepeatingTask(ThrottleableTaskQueueForScheduler(
+ frame_scheduler2.get()),
+ &run_count2),
+ base::TimeDelta::FromMilliseconds(1));
+
+ mock_task_runner_->RunForPeriod(base::TimeDelta::FromSeconds(1));
+ EXPECT_EQ(1000, run_count1);
+ EXPECT_EQ(1, run_count2);
+}
+
+namespace {
+
+void RunVirtualTimeRecorderTask(
+ base::SimpleTestTickClock* clock,
+ MainThreadSchedulerImpl* scheduler,
+ std::vector<base::TimeTicks>* out_real_times,
+ std::vector<base::TimeTicks>* out_virtual_times) {
+ out_real_times->push_back(clock->NowTicks());
+ out_virtual_times->push_back(scheduler->GetVirtualTimeDomain()->Now());
+}
+
+base::OnceClosure MakeVirtualTimeRecorderTask(
+ base::SimpleTestTickClock* clock,
+ MainThreadSchedulerImpl* scheduler,
+ std::vector<base::TimeTicks>* out_real_times,
+ std::vector<base::TimeTicks>* out_virtual_times) {
+ return WTF::Bind(&RunVirtualTimeRecorderTask, WTF::Unretained(clock),
+ WTF::Unretained(scheduler), WTF::Unretained(out_real_times),
+ WTF::Unretained(out_virtual_times));
+}
+} // namespace
+
+TEST_F(PageSchedulerImplTest, VirtualTime_TimerFastForwarding) {
+ std::vector<base::TimeTicks> real_times;
+ std::vector<base::TimeTicks> virtual_times;
+
+ page_scheduler_->EnableVirtualTime();
+
+ base::TimeTicks initial_real_time = scheduler_->tick_clock()->NowTicks();
+ base::TimeTicks initial_virtual_time =
+ scheduler_->GetVirtualTimeDomain()->Now();
+
+ ThrottleableTaskRunner()->PostDelayedTask(
+ FROM_HERE,
+ MakeVirtualTimeRecorderTask(&clock_, scheduler_.get(), &real_times,
+ &virtual_times),
+ base::TimeDelta::FromMilliseconds(2));
+
+ ThrottleableTaskRunner()->PostDelayedTask(
+ FROM_HERE,
+ MakeVirtualTimeRecorderTask(&clock_, scheduler_.get(), &real_times,
+ &virtual_times),
+ base::TimeDelta::FromMilliseconds(20));
+
+ ThrottleableTaskRunner()->PostDelayedTask(
+ FROM_HERE,
+ MakeVirtualTimeRecorderTask(&clock_, scheduler_.get(), &real_times,
+ &virtual_times),
+ base::TimeDelta::FromMilliseconds(200));
+
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(real_times, ElementsAre(initial_real_time, initial_real_time,
+ initial_real_time));
+ EXPECT_THAT(
+ virtual_times,
+ ElementsAre(
+ initial_virtual_time + base::TimeDelta::FromMilliseconds(2),
+ initial_virtual_time + base::TimeDelta::FromMilliseconds(20),
+ initial_virtual_time + base::TimeDelta::FromMilliseconds(200)));
+}
+
+TEST_F(PageSchedulerImplTest, VirtualTime_LoadingTaskFastForwarding) {
+ std::vector<base::TimeTicks> real_times;
+ std::vector<base::TimeTicks> virtual_times;
+
+ page_scheduler_->EnableVirtualTime();
+
+ base::TimeTicks initial_real_time = scheduler_->tick_clock()->NowTicks();
+ base::TimeTicks initial_virtual_time =
+ scheduler_->GetVirtualTimeDomain()->Now();
+
+ LoadingTaskRunner()->PostDelayedTask(
+ FROM_HERE,
+ MakeVirtualTimeRecorderTask(&clock_, scheduler_.get(), &real_times,
+ &virtual_times),
+ base::TimeDelta::FromMilliseconds(2));
+
+ LoadingTaskRunner()->PostDelayedTask(
+ FROM_HERE,
+ MakeVirtualTimeRecorderTask(&clock_, scheduler_.get(), &real_times,
+ &virtual_times),
+ base::TimeDelta::FromMilliseconds(20));
+
+ LoadingTaskRunner()->PostDelayedTask(
+ FROM_HERE,
+ MakeVirtualTimeRecorderTask(&clock_, scheduler_.get(), &real_times,
+ &virtual_times),
+ base::TimeDelta::FromMilliseconds(200));
+
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(real_times, ElementsAre(initial_real_time, initial_real_time,
+ initial_real_time));
+ EXPECT_THAT(
+ virtual_times,
+ ElementsAre(
+ initial_virtual_time + base::TimeDelta::FromMilliseconds(2),
+ initial_virtual_time + base::TimeDelta::FromMilliseconds(20),
+ initial_virtual_time + base::TimeDelta::FromMilliseconds(200)));
+}
+
+TEST_F(PageSchedulerImplTest,
+ RepeatingTimer_PageInBackground_MeansNothingForVirtualTime) {
+ page_scheduler_->EnableVirtualTime();
+ page_scheduler_->SetPageVisible(false);
+ scheduler_->GetSchedulerHelperForTesting()->SetWorkBatchSizeForTesting(1);
+ base::TimeTicks initial_real_time = scheduler_->tick_clock()->NowTicks();
+
+ int run_count = 0;
+ ThrottleableTaskQueue()->PostDelayedTask(
+ FROM_HERE, MakeRepeatingTask(ThrottleableTaskQueue(), &run_count),
+ base::TimeDelta::FromMilliseconds(1));
+
+ mock_task_runner_->RunTasksWhile(mock_task_runner_->TaskRunCountBelow(2000));
+ // Virtual time means page visibility is ignored.
+ EXPECT_EQ(1999, run_count);
+
+ // The global tick clock has not moved, yet we ran a large number of "delayed"
+ // tasks despite calling setPageVisible(false).
+ EXPECT_EQ(initial_real_time, scheduler_->tick_clock()->NowTicks());
+}
+
+namespace {
+
+void RunOrderTask(int index, std::vector<int>* out_run_order) {
+ out_run_order->push_back(index);
+}
+
+void DelayedRunOrderTask(int index,
+ scoped_refptr<TaskQueue> task_queue,
+ std::vector<int>* out_run_order) {
+ out_run_order->push_back(index);
+ task_queue->PostTask(FROM_HERE,
+ base::BindOnce(&RunOrderTask, index + 1,
+ base::Unretained(out_run_order)));
+}
+} // namespace
+
+TEST_F(PageSchedulerImplTest, VirtualTime_NotAllowedToAdvance) {
+ std::vector<int> run_order;
+
+ page_scheduler_->SetVirtualTimePolicy(VirtualTimePolicy::kPause);
+ page_scheduler_->EnableVirtualTime();
+
+ ThrottleableTaskQueue()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&RunOrderTask, 0, base::Unretained(&run_order)));
+
+ ThrottleableTaskQueue()->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&DelayedRunOrderTask, 1, ThrottleableTaskQueue(),
+ base::Unretained(&run_order)),
+ base::TimeDelta::FromMilliseconds(2));
+
+ ThrottleableTaskQueue()->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&DelayedRunOrderTask, 3, ThrottleableTaskQueue(),
+ base::Unretained(&run_order)),
+ base::TimeDelta::FromMilliseconds(4));
+
+ mock_task_runner_->RunUntilIdle();
+
+ // No timer tasks are allowed to run.
+ EXPECT_THAT(run_order, ElementsAre());
+}
+
+TEST_F(PageSchedulerImplTest, VirtualTime_AllowedToAdvance) {
+ std::vector<int> run_order;
+
+ page_scheduler_->SetVirtualTimePolicy(VirtualTimePolicy::kAdvance);
+ page_scheduler_->EnableVirtualTime();
+
+ ThrottleableTaskQueue()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&RunOrderTask, 0, base::Unretained(&run_order)));
+
+ ThrottleableTaskQueue()->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&DelayedRunOrderTask, 1, ThrottleableTaskQueue(),
+ base::Unretained(&run_order)),
+ base::TimeDelta::FromMilliseconds(2));
+
+ ThrottleableTaskQueue()->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&DelayedRunOrderTask, 3, ThrottleableTaskQueue(),
+ base::Unretained(&run_order)),
+ base::TimeDelta::FromMilliseconds(4));
+
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(run_order, ElementsAre(0, 1, 2, 3, 4));
+}
+
+class PageSchedulerImplTestWithDisabledBackgroundTimerThrottling
+ : public PageSchedulerImplTest {
+ public:
+ PageSchedulerImplTestWithDisabledBackgroundTimerThrottling() = default;
+ ~PageSchedulerImplTestWithDisabledBackgroundTimerThrottling() override =
+ default;
+
+ bool DisableBackgroundTimerThrottling() const override { return true; }
+};
+
+TEST_F(PageSchedulerImplTestWithDisabledBackgroundTimerThrottling,
+ RepeatingTimer_PageInBackground) {
+ page_scheduler_->SetPageVisible(false);
+
+ int run_count = 0;
+ ThrottleableTaskQueue()->PostDelayedTask(
+ FROM_HERE, MakeRepeatingTask(ThrottleableTaskQueue(), &run_count),
+ base::TimeDelta::FromMilliseconds(1));
+
+ mock_task_runner_->RunForPeriod(base::TimeDelta::FromSeconds(1));
+ EXPECT_EQ(1000, run_count);
+}
+
+TEST_F(PageSchedulerImplTest, VirtualTimeSettings_NewFrameScheduler) {
+ std::vector<int> run_order;
+
+ page_scheduler_->SetVirtualTimePolicy(VirtualTimePolicy::kPause);
+ page_scheduler_->EnableVirtualTime();
+
+ std::unique_ptr<FrameSchedulerImpl> frame_scheduler =
+ page_scheduler_->CreateFrameSchedulerImpl(
+ nullptr, FrameScheduler::FrameType::kSubframe);
+
+ ThrottleableTaskQueueForScheduler(frame_scheduler.get())
+ ->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&RunOrderTask, 1, base::Unretained(&run_order)),
+ base::TimeDelta::FromMilliseconds(1));
+
+ mock_task_runner_->RunUntilIdle();
+ EXPECT_TRUE(run_order.empty());
+
+ page_scheduler_->SetVirtualTimePolicy(VirtualTimePolicy::kAdvance);
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(run_order, ElementsAre(1));
+}
+
+namespace {
+
+template <typename T>
+base::OnceClosure MakeDeletionTask(T* obj) {
+ return base::BindOnce([](T* obj) { delete obj; }, base::Unretained(obj));
+}
+
+} // namespace
+
+TEST_F(PageSchedulerImplTest, DeleteFrameSchedulers_InTask) {
+ for (int i = 0; i < 10; i++) {
+ FrameSchedulerImpl* frame_scheduler =
+ page_scheduler_
+ ->CreateFrameSchedulerImpl(nullptr,
+ FrameScheduler::FrameType::kSubframe)
+ .release();
+ ThrottleableTaskQueueForScheduler(frame_scheduler)
+ ->PostDelayedTask(FROM_HERE, MakeDeletionTask(frame_scheduler),
+ base::TimeDelta::FromMilliseconds(1));
+ }
+ mock_task_runner_->RunUntilIdle();
+}
+
+TEST_F(PageSchedulerImplTest, DeletePageScheduler_InTask) {
+ ThrottleableTaskQueue()->PostTask(
+ FROM_HERE, MakeDeletionTask(page_scheduler_.release()));
+ mock_task_runner_->RunUntilIdle();
+}
+
+TEST_F(PageSchedulerImplTest, DeleteThrottledQueue_InTask) {
+ page_scheduler_->SetPageVisible(false);
+
+ FrameSchedulerImpl* frame_scheduler =
+ page_scheduler_
+ ->CreateFrameSchedulerImpl(nullptr,
+ FrameScheduler::FrameType::kSubframe)
+ .release();
+ scoped_refptr<TaskQueue> timer_task_queue =
+ ThrottleableTaskQueueForScheduler(frame_scheduler);
+
+ int run_count = 0;
+ timer_task_queue->PostDelayedTask(
+ FROM_HERE, MakeRepeatingTask(timer_task_queue, &run_count),
+ base::TimeDelta::FromMilliseconds(1));
+
+ // Note this will run at time t = 10s since we start at time t = 5000us.
+ // However, we still should run all tasks after frame scheduler deletion.
+ timer_task_queue->PostDelayedTask(FROM_HERE,
+ MakeDeletionTask(frame_scheduler),
+ base::TimeDelta::FromMilliseconds(9990));
+
+ mock_task_runner_->RunForPeriod(base::TimeDelta::FromSeconds(100));
+ EXPECT_EQ(90015, run_count);
+}
+
+TEST_F(PageSchedulerImplTest, VirtualTimePauseCount_DETERMINISTIC_LOADING) {
+ page_scheduler_->SetVirtualTimePolicy(
+ VirtualTimePolicy::kDeterministicLoading);
+ EXPECT_TRUE(scheduler_->VirtualTimeAllowedToAdvance());
+
+ scheduler_->IncrementVirtualTimePauseCount();
+ EXPECT_FALSE(scheduler_->VirtualTimeAllowedToAdvance());
+
+ scheduler_->IncrementVirtualTimePauseCount();
+ EXPECT_FALSE(scheduler_->VirtualTimeAllowedToAdvance());
+
+ scheduler_->DecrementVirtualTimePauseCount();
+ EXPECT_FALSE(scheduler_->VirtualTimeAllowedToAdvance());
+
+ scheduler_->DecrementVirtualTimePauseCount();
+ EXPECT_TRUE(scheduler_->VirtualTimeAllowedToAdvance());
+
+ scheduler_->IncrementVirtualTimePauseCount();
+ EXPECT_FALSE(scheduler_->VirtualTimeAllowedToAdvance());
+
+ scheduler_->DecrementVirtualTimePauseCount();
+ EXPECT_TRUE(scheduler_->VirtualTimeAllowedToAdvance());
+}
+
+TEST_F(PageSchedulerImplTest,
+ WebScopedVirtualTimePauser_DETERMINISTIC_LOADING) {
+ page_scheduler_->SetVirtualTimePolicy(
+ VirtualTimePolicy::kDeterministicLoading);
+
+ std::unique_ptr<FrameSchedulerImpl> frame_scheduler =
+ page_scheduler_->CreateFrameSchedulerImpl(
+ nullptr, FrameScheduler::FrameType::kSubframe);
+
+ {
+ WebScopedVirtualTimePauser virtual_time_pauser =
+ frame_scheduler->CreateWebScopedVirtualTimePauser(
+ "test",
+ WebScopedVirtualTimePauser::VirtualTaskDuration::kNonInstant);
+ EXPECT_TRUE(scheduler_->VirtualTimeAllowedToAdvance());
+
+ virtual_time_pauser.PauseVirtualTime();
+ EXPECT_FALSE(scheduler_->VirtualTimeAllowedToAdvance());
+
+ virtual_time_pauser.UnpauseVirtualTime();
+ EXPECT_TRUE(scheduler_->VirtualTimeAllowedToAdvance());
+
+ virtual_time_pauser.PauseVirtualTime();
+ EXPECT_FALSE(scheduler_->VirtualTimeAllowedToAdvance());
+ }
+
+ EXPECT_TRUE(scheduler_->VirtualTimeAllowedToAdvance());
+}
+
+namespace {
+
+void RecordVirtualTime(MainThreadSchedulerImpl* scheduler,
+ base::TimeTicks* out) {
+ *out = scheduler->GetVirtualTimeDomain()->Now();
+}
+
+void PauseAndUnpauseVirtualTime(MainThreadSchedulerImpl* scheduler,
+ FrameSchedulerImpl* frame_scheduler,
+ base::TimeTicks* paused,
+ base::TimeTicks* unpaused) {
+ *paused = scheduler->GetVirtualTimeDomain()->Now();
+
+ {
+ WebScopedVirtualTimePauser virtual_time_pauser =
+ frame_scheduler->CreateWebScopedVirtualTimePauser(
+ "test",
+ WebScopedVirtualTimePauser::VirtualTaskDuration::kNonInstant);
+ virtual_time_pauser.PauseVirtualTime();
+ }
+
+ *unpaused = scheduler->GetVirtualTimeDomain()->Now();
+}
+
+} // namespace
+
+TEST_F(PageSchedulerImplTest,
+ WebScopedVirtualTimePauserWithInterleavedTasks_DETERMINISTIC_LOADING) {
+ // Make task queue manager ask the virtual time domain for the next task delay
+ // after each task.
+ scheduler_->GetSchedulerHelperForTesting()->SetWorkBatchSizeForTesting(1);
+
+ page_scheduler_->EnableVirtualTime();
+ page_scheduler_->SetVirtualTimePolicy(
+ VirtualTimePolicy::kDeterministicLoading);
+
+ base::TimeTicks initial_virtual_time =
+ scheduler_->GetVirtualTimeDomain()->Now();
+
+ base::TimeTicks time_paused;
+ base::TimeTicks time_unpaused;
+ base::TimeTicks time_second_task;
+
+ std::unique_ptr<FrameSchedulerImpl> frame_scheduler =
+ page_scheduler_->CreateFrameSchedulerImpl(
+ nullptr, FrameScheduler::FrameType::kSubframe);
+
+ // Pauses and unpauses virtual time, thereby advancing virtual time by an
+ // additional 10ms due to WebScopedVirtualTimePauser's delay.
+ ThrottleableTaskRunner()->PostDelayedTask(
+ FROM_HERE,
+ WTF::Bind(&PauseAndUnpauseVirtualTime, WTF::Unretained(scheduler_.get()),
+ WTF::Unretained(frame_scheduler.get()),
+ WTF::Unretained(&time_paused), WTF::Unretained(&time_unpaused)),
+ base::TimeDelta::FromMilliseconds(3));
+
+ // Will run after the first task has advanced virtual time past 5ms.
+ ThrottleableTaskRunner()->PostDelayedTask(
+ FROM_HERE,
+ WTF::Bind(&RecordVirtualTime, WTF::Unretained(scheduler_.get()),
+ WTF::Unretained(&time_second_task)),
+ base::TimeDelta::FromMilliseconds(5));
+
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_EQ(time_paused,
+ initial_virtual_time + base::TimeDelta::FromMilliseconds(3));
+ EXPECT_EQ(time_unpaused,
+ initial_virtual_time + base::TimeDelta::FromMilliseconds(13));
+ EXPECT_EQ(time_second_task,
+ initial_virtual_time + base::TimeDelta::FromMilliseconds(13));
+}
+
+TEST_F(PageSchedulerImplTest,
+ MultipleWebScopedVirtualTimePausers_DETERMINISTIC_LOADING) {
+ page_scheduler_->SetVirtualTimePolicy(
+ VirtualTimePolicy::kDeterministicLoading);
+
+ std::unique_ptr<FrameSchedulerImpl> frame_scheduler =
+ page_scheduler_->CreateFrameSchedulerImpl(
+ nullptr, FrameScheduler::FrameType::kSubframe);
+
+ WebScopedVirtualTimePauser virtual_time_pauser1 =
+ frame_scheduler->CreateWebScopedVirtualTimePauser(
+ "test", WebScopedVirtualTimePauser::VirtualTaskDuration::kNonInstant);
+ WebScopedVirtualTimePauser virtual_time_pauser2 =
+ frame_scheduler->CreateWebScopedVirtualTimePauser(
+ "test", WebScopedVirtualTimePauser::VirtualTaskDuration::kNonInstant);
+
+ EXPECT_TRUE(scheduler_->VirtualTimeAllowedToAdvance());
+
+ virtual_time_pauser1.PauseVirtualTime();
+ virtual_time_pauser2.PauseVirtualTime();
+ EXPECT_FALSE(scheduler_->VirtualTimeAllowedToAdvance());
+
+ virtual_time_pauser2.UnpauseVirtualTime();
+ EXPECT_FALSE(scheduler_->VirtualTimeAllowedToAdvance());
+
+ virtual_time_pauser1.UnpauseVirtualTime();
+ EXPECT_TRUE(scheduler_->VirtualTimeAllowedToAdvance());
+}
+
+TEST_F(PageSchedulerImplTest, NestedMessageLoop_DETERMINISTIC_LOADING) {
+ page_scheduler_->SetVirtualTimePolicy(
+ VirtualTimePolicy::kDeterministicLoading);
+ EXPECT_TRUE(scheduler_->VirtualTimeAllowedToAdvance());
+
+ scheduler_->OnBeginNestedRunLoop();
+ EXPECT_FALSE(scheduler_->VirtualTimeAllowedToAdvance());
+
+ scheduler_->OnExitNestedRunLoop();
+ EXPECT_TRUE(scheduler_->VirtualTimeAllowedToAdvance());
+}
+
+TEST_F(PageSchedulerImplTest, PauseTimersWhileVirtualTimeIsPaused) {
+ std::vector<int> run_order;
+
+ std::unique_ptr<FrameSchedulerImpl> frame_scheduler =
+ page_scheduler_->CreateFrameSchedulerImpl(
+ nullptr, FrameScheduler::FrameType::kSubframe);
+ page_scheduler_->SetVirtualTimePolicy(VirtualTimePolicy::kPause);
+ page_scheduler_->EnableVirtualTime();
+
+ ThrottleableTaskQueueForScheduler(frame_scheduler.get())
+ ->PostTask(FROM_HERE, base::BindOnce(&RunOrderTask, 1,
+ base::Unretained(&run_order)));
+
+ mock_task_runner_->RunUntilIdle();
+ EXPECT_TRUE(run_order.empty());
+
+ page_scheduler_->SetVirtualTimePolicy(VirtualTimePolicy::kAdvance);
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(run_order, ElementsAre(1));
+}
+
+TEST_F(PageSchedulerImplTest, VirtualTimeBudgetExhaustedCallback) {
+ std::vector<base::TimeTicks> real_times;
+ std::vector<base::TimeTicks> virtual_times;
+
+ page_scheduler_->EnableVirtualTime();
+
+ base::TimeTicks initial_real_time = scheduler_->tick_clock()->NowTicks();
+ base::TimeTicks initial_virtual_time =
+ scheduler_->GetVirtualTimeDomain()->Now();
+
+ ThrottleableTaskRunner()->PostDelayedTask(
+ FROM_HERE,
+ MakeVirtualTimeRecorderTask(&clock_, scheduler_.get(), &real_times,
+ &virtual_times),
+ base::TimeDelta::FromMilliseconds(1));
+
+ ThrottleableTaskRunner()->PostDelayedTask(
+ FROM_HERE,
+ MakeVirtualTimeRecorderTask(&clock_, scheduler_.get(), &real_times,
+ &virtual_times),
+ base::TimeDelta::FromMilliseconds(2));
+
+ ThrottleableTaskRunner()->PostDelayedTask(
+ FROM_HERE,
+ MakeVirtualTimeRecorderTask(&clock_, scheduler_.get(), &real_times,
+ &virtual_times),
+ base::TimeDelta::FromMilliseconds(5));
+
+ ThrottleableTaskRunner()->PostDelayedTask(
+ FROM_HERE,
+ MakeVirtualTimeRecorderTask(&clock_, scheduler_.get(), &real_times,
+ &virtual_times),
+ base::TimeDelta::FromMilliseconds(7));
+
+ page_scheduler_->GrantVirtualTimeBudget(
+ base::TimeDelta::FromMilliseconds(5),
+ WTF::Bind(
+ [](PageScheduler* scheduler) {
+ scheduler->SetVirtualTimePolicy(VirtualTimePolicy::kPause);
+ },
+ WTF::Unretained(page_scheduler_.get())));
+
+ mock_task_runner_->RunUntilIdle();
+
+ // The timer that is scheduled for the exact point in time when virtual time
+ // expires will not run.
+ EXPECT_THAT(real_times, ElementsAre(initial_real_time, initial_real_time,
+ initial_real_time));
+ EXPECT_THAT(
+ virtual_times,
+ ElementsAre(initial_virtual_time + base::TimeDelta::FromMilliseconds(1),
+ initial_virtual_time + base::TimeDelta::FromMilliseconds(2),
+ initial_virtual_time + base::TimeDelta::FromMilliseconds(5)));
+}
+
+namespace {
+class MockObserver : public PageScheduler::VirtualTimeObserver {
+ public:
+ ~MockObserver() override = default;
+
+ void OnVirtualTimeAdvanced(base::TimeDelta virtual_time_offset) override {
+ virtual_time_log_.push_back(base::StringPrintf(
+ "Advanced to %dms",
+ static_cast<int>(virtual_time_offset.InMilliseconds())));
+ }
+
+ void OnVirtualTimePaused(base::TimeDelta virtual_time_offset) override {
+ virtual_time_log_.push_back(base::StringPrintf(
+ "Paused at %dms",
+ static_cast<int>(virtual_time_offset.InMilliseconds())));
+ }
+
+ const std::vector<std::string>& virtual_time_log() const {
+ return virtual_time_log_;
+ }
+
+ private:
+ std::vector<std::string> virtual_time_log_;
+};
+
+void NopTask() {}
+} // namespace
+
+TEST_F(PageSchedulerImplTest, VirtualTimeObserver) {
+ MockObserver mock_observer;
+ page_scheduler_->AddVirtualTimeObserver(&mock_observer);
+ page_scheduler_->EnableVirtualTime();
+
+ ThrottleableTaskQueue()->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&NopTask),
+ base::TimeDelta::FromMilliseconds(200));
+
+ ThrottleableTaskQueue()->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&NopTask),
+ base::TimeDelta::FromMilliseconds(20));
+
+ ThrottleableTaskQueue()->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&NopTask),
+ base::TimeDelta::FromMilliseconds(2));
+
+ page_scheduler_->GrantVirtualTimeBudget(
+ base::TimeDelta::FromMilliseconds(1000),
+ WTF::Bind(
+ [](PageScheduler* scheduler) {
+ scheduler->SetVirtualTimePolicy(VirtualTimePolicy::kPause);
+ },
+ WTF::Unretained(page_scheduler_.get())));
+
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_THAT(
+ mock_observer.virtual_time_log(),
+ ElementsAre("Advanced to 2ms", "Advanced to 20ms", "Advanced to 200ms",
+ "Advanced to 1000ms", "Paused at 1000ms"));
+ page_scheduler_->RemoveVirtualTimeObserver(&mock_observer);
+}
+
+namespace {
+void RepostingTask(scoped_refptr<TaskQueue> task_queue,
+ int max_count,
+ int* count) {
+ if (++(*count) >= max_count)
+ return;
+
+ task_queue->PostTask(FROM_HERE,
+ base::BindOnce(&RepostingTask, task_queue, max_count,
+ base::Unretained(count)));
+}
+
+void DelayedTask(int* count_in, int* count_out) {
+ *count_out = *count_in;
+}
+
+} // namespace
+
+TEST_F(PageSchedulerImplTest, MaxVirtualTimeTaskStarvationCountOneHundred) {
+ page_scheduler_->EnableVirtualTime();
+ page_scheduler_->SetMaxVirtualTimeTaskStarvationCount(100);
+ page_scheduler_->SetVirtualTimePolicy(VirtualTimePolicy::kAdvance);
+
+ int count = 0;
+ int delayed_task_run_at_count = 0;
+ RepostingTask(ThrottleableTaskQueue(), 1000, &count);
+ ThrottleableTaskQueue()->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(DelayedTask, base::Unretained(&count),
+ base::Unretained(&delayed_task_run_at_count)),
+ base::TimeDelta::FromMilliseconds(10));
+
+ page_scheduler_->GrantVirtualTimeBudget(
+ base::TimeDelta::FromMilliseconds(1000),
+ WTF::Bind(
+ [](PageScheduler* scheduler) {
+ scheduler->SetVirtualTimePolicy(VirtualTimePolicy::kPause);
+ },
+ WTF::Unretained(page_scheduler_.get())));
+
+ mock_task_runner_->RunUntilIdle();
+
+ // Two delayed tasks with a run of 100 tasks, plus initial call.
+ EXPECT_EQ(201, count);
+ EXPECT_EQ(102, delayed_task_run_at_count);
+}
+
+TEST_F(PageSchedulerImplTest,
+ MaxVirtualTimeTaskStarvationCountOneHundredNestedMessageLoop) {
+ page_scheduler_->EnableVirtualTime();
+ page_scheduler_->SetMaxVirtualTimeTaskStarvationCount(100);
+ page_scheduler_->SetVirtualTimePolicy(VirtualTimePolicy::kAdvance);
+ scheduler_->OnBeginNestedRunLoop();
+
+ int count = 0;
+ int delayed_task_run_at_count = 0;
+ RepostingTask(ThrottleableTaskQueue(), 1000, &count);
+ ThrottleableTaskQueue()->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(DelayedTask, WTF::Unretained(&count),
+ WTF::Unretained(&delayed_task_run_at_count)),
+ base::TimeDelta::FromMilliseconds(10));
+
+ page_scheduler_->GrantVirtualTimeBudget(
+ base::TimeDelta::FromMilliseconds(1000),
+ WTF::Bind(
+ [](PageScheduler* scheduler) {
+ scheduler->SetVirtualTimePolicy(VirtualTimePolicy::kPause);
+ },
+ WTF::Unretained(page_scheduler_.get())));
+
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_EQ(1000, count);
+ EXPECT_EQ(1000, delayed_task_run_at_count);
+}
+
+TEST_F(PageSchedulerImplTest, MaxVirtualTimeTaskStarvationCountZero) {
+ page_scheduler_->EnableVirtualTime();
+ page_scheduler_->SetMaxVirtualTimeTaskStarvationCount(0);
+ page_scheduler_->SetVirtualTimePolicy(VirtualTimePolicy::kAdvance);
+
+ int count = 0;
+ int delayed_task_run_at_count = 0;
+ RepostingTask(ThrottleableTaskQueue(), 1000, &count);
+ ThrottleableTaskQueue()->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(DelayedTask, WTF::Unretained(&count),
+ WTF::Unretained(&delayed_task_run_at_count)),
+ base::TimeDelta::FromMilliseconds(10));
+
+ page_scheduler_->GrantVirtualTimeBudget(
+ base::TimeDelta::FromMilliseconds(1000),
+ WTF::Bind(
+ [](PageScheduler* scheduler) {
+ scheduler->SetVirtualTimePolicy(VirtualTimePolicy::kPause);
+ },
+ WTF::Unretained(page_scheduler_.get())));
+
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_EQ(1000, count);
+ // If the initial count had been higher, the delayed task could have been
+ // arbitrarily delayed.
+ EXPECT_EQ(1000, delayed_task_run_at_count);
+}
+
+namespace {
+
+void ExpensiveTestTask(base::SimpleTestTickClock* clock,
+ std::vector<base::TimeTicks>* run_times) {
+ run_times->push_back(clock->NowTicks());
+ clock->Advance(base::TimeDelta::FromMilliseconds(250));
+}
+
+void InitializeTrialParams() {
+ std::map<std::string, std::string> params = {{"cpu_budget", "0.01"},
+ {"max_budget", "0.0"},
+ {"initial_budget", "0.0"},
+ {"max_delay", "0.0"}};
+ const char kParamName[] = "ExpensiveBackgroundTimerThrottling";
+ const char kGroupName[] = "Enabled";
+ EXPECT_TRUE(base::AssociateFieldTrialParams(kParamName, kGroupName, params));
+ EXPECT_TRUE(base::FieldTrialList::CreateFieldTrial(kParamName, kGroupName));
+
+ std::map<std::string, std::string> actual_params;
+ base::GetFieldTrialParams(kParamName, &actual_params);
+ EXPECT_EQ(actual_params, params);
+}
+
+} // namespace
+
+TEST_F(PageSchedulerImplTest, BackgroundTimerThrottling) {
+ ScopedExpensiveBackgroundTimerThrottlingForTest
+ budget_background_throttling_enabler(true);
+
+ std::unique_ptr<base::FieldTrialList> field_trial_list =
+ std::make_unique<base::FieldTrialList>(nullptr);
+ InitializeTrialParams();
+ page_scheduler_.reset(
+ new PageSchedulerImpl(nullptr, scheduler_.get(), false));
+
+ std::vector<base::TimeTicks> run_times;
+ frame_scheduler_ = page_scheduler_->CreateFrameSchedulerImpl(
+ nullptr, FrameScheduler::FrameType::kSubframe);
+ page_scheduler_->SetPageVisible(true);
+
+ mock_task_runner_->RunUntilTime(base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(2500));
+
+ ThrottleableTaskQueue()->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&ExpensiveTestTask, &clock_, &run_times),
+ base::TimeDelta::FromMilliseconds(1));
+ ThrottleableTaskQueue()->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&ExpensiveTestTask, &clock_, &run_times),
+ base::TimeDelta::FromMilliseconds(1));
+
+ mock_task_runner_->RunUntilTime(base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(3500));
+
+ // Check that these tasks are aligned, but are not subject to budget-based
+ // throttling.
+ EXPECT_THAT(
+ run_times,
+ ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(2501),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(2751)));
+ run_times.clear();
+
+ page_scheduler_->SetPageVisible(false);
+
+ ThrottleableTaskQueue()->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&ExpensiveTestTask, &clock_, &run_times),
+ base::TimeDelta::FromMicroseconds(1));
+ ThrottleableTaskQueue()->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&ExpensiveTestTask, &clock_, &run_times),
+ base::TimeDelta::FromMicroseconds(1));
+
+ mock_task_runner_->RunUntilIdle();
+
+ // Check that tasks are aligned and throttled.
+ EXPECT_THAT(
+ run_times,
+ ElementsAre(base::TimeTicks() + base::TimeDelta::FromSeconds(4),
+ base::TimeTicks() + base::TimeDelta::FromSeconds(26)));
+
+ base::FieldTrialParamAssociator::GetInstance()->ClearAllParamsForTesting();
+}
+
+TEST_F(PageSchedulerImplTest, OpenWebSocketExemptsFromBudgetThrottling) {
+ ScopedExpensiveBackgroundTimerThrottlingForTest
+ budget_background_throttling_enabler(true);
+
+ std::unique_ptr<base::FieldTrialList> field_trial_list =
+ std::make_unique<base::FieldTrialList>(nullptr);
+ InitializeTrialParams();
+ std::unique_ptr<PageSchedulerImpl> page_scheduler(
+ new PageSchedulerImpl(nullptr, scheduler_.get(), false));
+
+ std::vector<base::TimeTicks> run_times;
+
+ std::unique_ptr<FrameSchedulerImpl> frame_scheduler1 =
+ page_scheduler->CreateFrameSchedulerImpl(
+ nullptr, FrameScheduler::FrameType::kSubframe);
+ std::unique_ptr<FrameSchedulerImpl> frame_scheduler2 =
+ page_scheduler->CreateFrameSchedulerImpl(
+ nullptr, FrameScheduler::FrameType::kSubframe);
+
+ page_scheduler->SetPageVisible(false);
+
+ // Wait for 20s to avoid initial throttling delay.
+ mock_task_runner_->RunUntilTime(base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(20500));
+
+ for (size_t i = 0; i < 3; ++i) {
+ ThrottleableTaskQueueForScheduler(frame_scheduler1.get())
+ ->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&ExpensiveTestTask, &clock_, &run_times),
+ base::TimeDelta::FromMilliseconds(1));
+ }
+
+ mock_task_runner_->RunUntilTime(base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(55500));
+
+ // Check that tasks are throttled.
+ EXPECT_THAT(
+ run_times,
+ ElementsAre(base::TimeTicks() + base::TimeDelta::FromSeconds(21),
+ base::TimeTicks() + base::TimeDelta::FromSeconds(26),
+ base::TimeTicks() + base::TimeDelta::FromSeconds(51)));
+ run_times.clear();
+
+ std::unique_ptr<FrameScheduler::ActiveConnectionHandle> websocket_connection =
+ frame_scheduler1->OnActiveConnectionCreated();
+
+ for (size_t i = 0; i < 3; ++i) {
+ ThrottleableTaskQueueForScheduler(frame_scheduler1.get())
+ ->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&ExpensiveTestTask, &clock_, &run_times),
+ base::TimeDelta::FromMilliseconds(1));
+ }
+
+ mock_task_runner_->RunUntilTime(base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(58500));
+
+ // Check that the timer task queue from the first frame is aligned,
+ // but not throttled.
+ EXPECT_THAT(
+ run_times,
+ ElementsAre(
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(56000),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(56250),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(56500)));
+ run_times.clear();
+
+ for (size_t i = 0; i < 3; ++i) {
+ ThrottleableTaskQueueForScheduler(frame_scheduler2.get())
+ ->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&ExpensiveTestTask, &clock_, &run_times),
+ base::TimeDelta::FromMilliseconds(1));
+ }
+
+ mock_task_runner_->RunUntilTime(base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(59500));
+
+ // Check that the second frame scheduler becomes unthrottled.
+ EXPECT_THAT(
+ run_times,
+ ElementsAre(
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(59000),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(59250),
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(59500)));
+ run_times.clear();
+
+ websocket_connection.reset();
+
+ // Wait for 10s to enable throttling back.
+ mock_task_runner_->RunUntilTime(base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(70500));
+
+ for (size_t i = 0; i < 3; ++i) {
+ ThrottleableTaskQueueForScheduler(frame_scheduler1.get())
+ ->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&ExpensiveTestTask, &clock_, &run_times),
+ base::TimeDelta::FromMilliseconds(1));
+ }
+
+ mock_task_runner_->RunUntilIdle();
+
+ // WebSocket is closed, budget-based throttling now applies.
+ EXPECT_THAT(
+ run_times,
+ ElementsAre(base::TimeTicks() + base::TimeDelta::FromSeconds(84),
+ base::TimeTicks() + base::TimeDelta::FromSeconds(109),
+ base::TimeTicks() + base::TimeDelta::FromSeconds(134)));
+
+ base::FieldTrialParamAssociator::GetInstance()->ClearAllParamsForTesting();
+}
+
+namespace {
+void IncrementCounter(int* counter) {
+ ++*counter;
+}
+} // namespace
+
+TEST_F(PageSchedulerImplTest, PageFreeze) {
+ ScopedStopLoadingInBackgroundForTest stop_loading_enabler(true);
+ ScopedStopNonTimersInBackgroundForTest stop_non_timers_enabler(true);
+
+ int counter = 0;
+ LoadingTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+ ThrottleableTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+ DeferrableTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+ PausableTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+ UnpausableTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+
+ page_scheduler_->SetPageVisible(false);
+ EXPECT_EQ(false, page_scheduler_->IsFrozen());
+
+ // In a backgrounded active page, all queues should run.
+ mock_task_runner_->RunUntilIdle();
+ EXPECT_EQ(5, counter);
+
+ LoadingTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+ ThrottleableTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+ DeferrableTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+ PausableTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+ UnpausableTaskQueue()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+ counter = 0;
+
+ page_scheduler_->SetPageFrozen(true);
+ EXPECT_EQ(true, page_scheduler_->IsFrozen());
+
+ // In a backgrounded frozen page, only Unpausable queue should run.
+ mock_task_runner_->RunUntilIdle();
+ EXPECT_EQ(1, counter);
+
+ // A visible page should not be frozen.
+ page_scheduler_->SetPageVisible(true);
+ EXPECT_EQ(false, page_scheduler_->IsFrozen());
+
+ // Once the page is unfrozen, the rest of the queues should run.
+ mock_task_runner_->RunUntilIdle();
+ EXPECT_EQ(5, counter);
+}
+
+} // namespace page_scheduler_impl_unittest
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/web_main_thread_scheduler.cc b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/web_main_thread_scheduler.cc
new file mode 100644
index 00000000000..c04c216cf39
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/web_main_thread_scheduler.cc
@@ -0,0 +1,55 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/scheduler/web_main_thread_scheduler.h"
+
+#include <memory>
+#include "base/command_line.h"
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/metrics/field_trial.h"
+#include "base/time/default_tick_clock.h"
+#include "base/trace_event/trace_event.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/util/tracing_helper.h"
+
+namespace blink {
+namespace scheduler {
+
+WebMainThreadScheduler::WebMainThreadScheduler() = default;
+
+WebMainThreadScheduler::~WebMainThreadScheduler() = default;
+
+WebMainThreadScheduler::RAILModeObserver::~RAILModeObserver() = default;
+
+// static
+std::unique_ptr<WebMainThreadScheduler> WebMainThreadScheduler::Create(
+ base::Optional<base::Time> initial_virtual_time) {
+ // Ensure categories appear as an option in chrome://tracing.
+ WarmupTracingCategories();
+ // Workers might be short-lived, so placing warmup here.
+ TRACE_EVENT_WARMUP_CATEGORY(TRACE_DISABLED_BY_DEFAULT("worker.scheduler"));
+
+ std::unique_ptr<MainThreadSchedulerImpl> scheduler(
+ new MainThreadSchedulerImpl(TaskQueueManager::TakeOverCurrentThread(),
+ initial_virtual_time));
+ return base::WrapUnique<WebMainThreadScheduler>(scheduler.release());
+}
+
+// static
+const char* WebMainThreadScheduler::InputEventStateToString(
+ InputEventState input_event_state) {
+ switch (input_event_state) {
+ case InputEventState::EVENT_CONSUMED_BY_COMPOSITOR:
+ return "event_consumed_by_compositor";
+ case InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD:
+ return "event_forwarded_to_main_thread";
+ default:
+ NOTREACHED();
+ return nullptr;
+ }
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/web_render_widget_scheduling_state.cc b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/web_render_widget_scheduling_state.cc
new file mode 100644
index 00000000000..2cfea4a953d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/web_render_widget_scheduling_state.cc
@@ -0,0 +1,68 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/scheduler/web_render_widget_scheduling_state.h"
+
+#include "third_party/blink/renderer/platform/scheduler/renderer/render_widget_signals.h"
+
+namespace blink {
+namespace scheduler {
+
+WebRenderWidgetSchedulingState::WebRenderWidgetSchedulingState(
+ RenderWidgetSignals* render_widget_scheduling_signals)
+ : render_widget_signals_(render_widget_scheduling_signals),
+ hidden_(false),
+ has_touch_handler_(false) {
+ render_widget_signals_->IncNumVisibleRenderWidgets();
+}
+
+WebRenderWidgetSchedulingState::~WebRenderWidgetSchedulingState() {
+ if (hidden_)
+ return;
+
+ render_widget_signals_->DecNumVisibleRenderWidgets();
+
+ if (has_touch_handler_) {
+ render_widget_signals_->DecNumVisibleRenderWidgetsWithTouchHandlers();
+ }
+}
+
+void WebRenderWidgetSchedulingState::SetHidden(bool hidden) {
+ if (hidden_ == hidden)
+ return;
+
+ hidden_ = hidden;
+
+ if (hidden_) {
+ render_widget_signals_->DecNumVisibleRenderWidgets();
+ if (has_touch_handler_) {
+ render_widget_signals_->DecNumVisibleRenderWidgetsWithTouchHandlers();
+ }
+ } else {
+ render_widget_signals_->IncNumVisibleRenderWidgets();
+ if (has_touch_handler_) {
+ render_widget_signals_->IncNumVisibleRenderWidgetsWithTouchHandlers();
+ }
+ }
+}
+
+void WebRenderWidgetSchedulingState::SetHasTouchHandler(
+ bool has_touch_handler) {
+ if (has_touch_handler_ == has_touch_handler)
+ return;
+
+ has_touch_handler_ = has_touch_handler;
+
+ if (hidden_)
+ return;
+
+ if (has_touch_handler_) {
+ render_widget_signals_->IncNumVisibleRenderWidgetsWithTouchHandlers();
+ } else {
+ render_widget_signals_->DecNumVisibleRenderWidgetsWithTouchHandlers();
+ }
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/public/frame_or_worker_global_scope_scheduler.h b/chromium/third_party/blink/renderer/platform/scheduler/public/frame_or_worker_global_scope_scheduler.h
new file mode 100644
index 00000000000..0de37b8308b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/public/frame_or_worker_global_scope_scheduler.h
@@ -0,0 +1,37 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_FRAME_OR_WORKER_GLOBAL_SCOPE_SCHEDULER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_FRAME_OR_WORKER_GLOBAL_SCOPE_SCHEDULER_H_
+
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+// This is the base class of FrameScheduler and WorkerGlobalScopeScheduler.
+class FrameOrWorkerGlobalScopeScheduler {
+ USING_FAST_MALLOC(FrameOrWorkerGlobalScopeScheduler);
+
+ public:
+ virtual ~FrameOrWorkerGlobalScopeScheduler() = default;
+
+ class ActiveConnectionHandle {
+ public:
+ ActiveConnectionHandle() = default;
+ virtual ~ActiveConnectionHandle() = default;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ActiveConnectionHandle);
+ };
+
+ // Notifies scheduler that this execution context has established an active
+ // real time connection (websocket, webrtc, etc). When connection is closed
+ // this handle must be destroyed.
+ virtual std::unique_ptr<ActiveConnectionHandle>
+ OnActiveConnectionCreated() = 0;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_FRAME_OR_WORKER_GLOBAL_SCOPE_SCHEDULER_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h b/chromium/third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h
new file mode 100644
index 00000000000..edebfd1e745
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h
@@ -0,0 +1,172 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_FRAME_SCHEDULER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_FRAME_SCHEDULER_H_
+
+#include <memory>
+
+#include "base/memory/scoped_refptr.h"
+#include "base/single_thread_task_runner.h"
+#include "third_party/blink/public/platform/task_type.h"
+#include "third_party/blink/public/platform/web_scoped_virtual_time_pauser.h"
+#include "third_party/blink/renderer/platform/scheduler/public/frame_or_worker_global_scope_scheduler.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class PageScheduler;
+
+class FrameScheduler : public FrameOrWorkerGlobalScopeScheduler {
+ public:
+ virtual ~FrameScheduler() = default;
+
+ // Observer type that regulates conditions to invoke callbacks.
+ enum class ObserverType { kLoader, kWorkerScheduler };
+
+ // Represents throttling state.
+ enum class ThrottlingState {
+ kThrottled,
+ kNotThrottled,
+ kStopped,
+ };
+
+ // Represents the type of frame: main (top-level) vs not.
+ enum class FrameType {
+ kMainFrame,
+ kSubframe,
+ };
+
+ // Observer interface to receive scheduling policy change events.
+ class Observer {
+ public:
+ virtual ~Observer() = default;
+
+ // Notified when throttling state is changed.
+ virtual void OnThrottlingStateChanged(ThrottlingState) = 0;
+ };
+
+ class ThrottlingObserverHandle {
+ public:
+ ThrottlingObserverHandle() = default;
+ virtual ~ThrottlingObserverHandle() = default;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ThrottlingObserverHandle);
+ };
+
+ // Adds an Observer instance to be notified on scheduling policy changed.
+ // When an Observer is added, the initial state will be notified synchronously
+ // through the Observer interface.
+ // A RAII handle is returned and observer is unregistered when the handle is
+ // destroyed.
+ virtual std::unique_ptr<ThrottlingObserverHandle> AddThrottlingObserver(
+ ObserverType,
+ Observer*) = 0;
+
+ // The scheduler may throttle tasks associated with offscreen frames.
+ virtual void SetFrameVisible(bool) = 0;
+ virtual bool IsFrameVisible() const = 0;
+
+ // Query the page visibility state for the page associated with this frame.
+ // The scheduler may throttle tasks associated with pages that are not
+ // visible.
+ virtual bool IsPageVisible() const = 0;
+
+ // Set whether this frame is suspended. Only unthrottledTaskRunner tasks are
+ // allowed to run on a suspended frame.
+ virtual void SetPaused(bool) = 0;
+
+ // Notifies observers of transitioning to and from FROZEN state in
+ // background.
+ virtual void SetPageFrozen(bool) {}
+
+ // Tells the scheduler about "keep-alive" state which can be due to:
+ // service workers, shared workers, or fetch keep-alive.
+ // If true, then the scheduler should not freeze relevant task queues.
+ virtual void SetKeepActive(bool) {}
+
+ // Set whether this frame is cross origin w.r.t. the top level frame. Cross
+ // origin frames may use a different scheduling policy from same origin
+ // frames.
+ virtual void SetCrossOrigin(bool) = 0;
+ virtual bool IsCrossOrigin() const = 0;
+ virtual void TraceUrlChange(const String&) = 0;
+
+ // Returns the frame type, which currently determines whether this frame is
+ // the top level frame, i.e. a main frame.
+ virtual FrameType GetFrameType() const = 0;
+
+ // The tasks runners below are listed in increasing QoS order.
+ // - throttleable task queue. Designed for custom user-provided javascript
+ // tasks. Lowest guarantees. Can be paused, blocked during user gesture,
+ // throttled when backgrounded or stopped completely after some time in
+ // background.
+ // - deferrable task queue. These tasks can be deferred for a small period
+ // (several seconds) when high-priority work is anticipated. These tasks
+ // can be paused.
+ // - pausable task queue. Default queue for high-priority javascript tasks.
+ // They can be paused according to the spec during javascript alert
+ // dialogs, printing windows and devtools debugging. Otherwise scheduler
+ // does not tamper with their execution.
+ // - unpausable task queue. Should be used for control tasks which should
+ // run when the context is paused. Usage should be extremely rare.
+ // Please consult scheduler-dev@ before using it. Running javascript
+ // on it is strictly verboten and can lead to hard-to-diagnose errors.
+ //
+ //
+ // These queues below are separate due to special handling for their
+ // priorities.
+ // - loading task queue. Similar to deferrable task queue. Throttling might
+ // be considered in the future.
+ // - loading control task queue. Loading task queue with increased priority
+ // to run small loading tasks which schedule other loading tasks.
+
+ // Note: old-style timer task runner corresponds to throttleable task runner
+ // and unthrottled task runner corresponds to pausable task runner.
+
+ // Returns a task runner that is suitable with the given task type.
+ virtual scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(
+ TaskType) = 0;
+
+ // Returns the parent PageScheduler.
+ virtual PageScheduler* GetPageScheduler() const = 0;
+
+ // Returns a WebScopedVirtualTimePauser which can be used to vote for pausing
+ // virtual time. Virtual time will be paused if any WebScopedVirtualTimePauser
+ // votes to pause it, and only unpaused only if all
+ // WebScopedVirtualTimePausers are either destroyed or vote to unpause. Note
+ // the WebScopedVirtualTimePauser returned by this method is initially
+ // unpaused.
+ virtual WebScopedVirtualTimePauser CreateWebScopedVirtualTimePauser(
+ const String& name,
+ WebScopedVirtualTimePauser::VirtualTaskDuration) = 0;
+
+ // Tells the scheduler that a provisional load has started, the scheduler may
+ // reset the task cost estimators and the UserModel. Must be called from the
+ // main thread.
+ virtual void DidStartProvisionalLoad(bool is_main_frame) = 0;
+
+ // Tells the scheduler that a provisional load has committed, the scheduler
+ // may reset the task cost estimators and the UserModel. Must be called from
+ // the main thread.
+ virtual void DidCommitProvisionalLoad(bool is_web_history_inert_commit,
+ bool is_reload,
+ bool is_main_frame) = 0;
+
+ // Tells the scheduler that the first meaningful paint has occured for this
+ // frame.
+ virtual void OnFirstMeaningfulPaint() = 0;
+
+ // Returns true if this frame is should not throttled (e.g. due to an active
+ // connection).
+ // Note that this only applies to the current frame,
+ // use GetPageScheduler()->IsExemptFromBudgetBasedThrottling for the status
+ // of the page.
+ virtual bool IsExemptFromBudgetBasedThrottling() const = 0;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_FRAME_SCHEDULER_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/public/non_main_thread_scheduler.h b/chromium/third_party/blink/renderer/platform/scheduler/public/non_main_thread_scheduler.h
new file mode 100644
index 00000000000..a9aca3e3f92
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/public/non_main_thread_scheduler.h
@@ -0,0 +1,60 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_NON_MAIN_THREAD_SCHEDULER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_NON_MAIN_THREAD_SCHEDULER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "third_party/blink/public/platform/scheduler/single_thread_idle_task_runner.h"
+#include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h"
+#include "third_party/blink/public/platform/web_thread_type.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue.h"
+#include "third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_helper.h"
+#include "third_party/blink/renderer/platform/scheduler/child/worker_task_queue.h"
+
+namespace blink {
+namespace scheduler {
+class WorkerSchedulerProxy;
+
+class PLATFORM_EXPORT NonMainThreadScheduler : public WebThreadScheduler {
+ public:
+ ~NonMainThreadScheduler() override;
+
+ static std::unique_ptr<NonMainThreadScheduler> Create(
+ WebThreadType thread_type,
+ WorkerSchedulerProxy* proxy);
+
+ // Blink should use NonMainThreadScheduler::DefaultTaskQueue instead of
+ // WebThreadScheduler::DefaultTaskRunner.
+ virtual scoped_refptr<WorkerTaskQueue> DefaultTaskQueue() = 0;
+
+ // Must be called before the scheduler can be used. Does any post construction
+ // initialization needed such as initializing idle period detection.
+ virtual void Init() = 0;
+
+ virtual void OnTaskCompleted(WorkerTaskQueue* worker_task_queue,
+ const TaskQueue::Task& task,
+ base::TimeTicks start,
+ base::TimeTicks end,
+ base::Optional<base::TimeDelta> thread_time) = 0;
+
+ scoped_refptr<WorkerTaskQueue> CreateTaskRunner();
+
+ protected:
+ explicit NonMainThreadScheduler(
+ std::unique_ptr<WorkerSchedulerHelper> helper);
+
+ std::unique_ptr<WorkerSchedulerHelper> helper_;
+
+ DISALLOW_COPY_AND_ASSIGN(NonMainThreadScheduler);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_NON_MAIN_THREAD_SCHEDULER_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/public/page_scheduler.h b/chromium/third_party/blink/renderer/platform/scheduler/public/page_scheduler.h
new file mode 100644
index 00000000000..fdec4b71f7f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/public/page_scheduler.h
@@ -0,0 +1,152 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_PAGE_SCHEDULER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_PAGE_SCHEDULER_H_
+
+#include <memory>
+#include "third_party/blink/public/platform/blame_context.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace ukm {
+class UkmRecorder;
+}
+
+namespace blink {
+
+class PLATFORM_EXPORT PageScheduler {
+ public:
+ class PLATFORM_EXPORT Delegate {
+ public:
+ virtual ~Delegate() = default;
+
+ virtual void ReportIntervention(const WTF::String& message) = 0;
+ virtual void RequestBeginMainFrameNotExpected(bool new_state) = 0;
+ virtual void SetPageFrozen(bool frozen) = 0;
+ virtual ukm::UkmRecorder* GetUkmRecorder() = 0;
+ virtual int64_t GetUkmSourceId() = 0;
+ };
+
+ virtual ~PageScheduler() = default;
+
+ // The scheduler may throttle tasks associated with background pages.
+ virtual void SetPageVisible(bool) = 0;
+ // The scheduler transitions app to and from STOPPED state in background.
+ virtual void SetPageFrozen(bool) = 0;
+ // Tells the scheduler about "keep-alive" state which can be due to:
+ // service workers, shared workers, or fetch keep-alive.
+ // If true, then the scheduler should not freeze relevant task queues.
+ virtual void SetKeepActive(bool) = 0;
+ // Whether the main frame of this page is local or not (remote).
+ virtual bool IsMainFrameLocal() const = 0;
+ virtual void SetIsMainFrameLocal(bool) = 0;
+
+ // Creates a new FrameScheduler. The caller is responsible for deleting
+ // it. All tasks executed by the frame scheduler will be attributed to
+ // |blame_context|.
+ virtual std::unique_ptr<FrameScheduler> CreateFrameScheduler(
+ BlameContext*,
+ FrameScheduler::FrameType) = 0;
+
+ // Instructs this PageScheduler to use virtual time. When virtual time is
+ // enabled the system doesn't actually sleep for the delays between tasks
+ // before executing them. Returns the TimeTicks that virtual time offsets will
+ // be relative to.
+ //
+ // E.g: A-E are delayed tasks
+ //
+ // | A B C D E (normal)
+ // |-----------------------------> time
+ //
+ // |ABCDE (virtual time)
+ // |-----------------------------> time
+ virtual base::TimeTicks EnableVirtualTime() = 0;
+
+ // Disables virtual time. Note that this is only used for testing, because
+ // there's no reason to do this in production.
+ virtual void DisableVirtualTimeForTesting() = 0;
+
+ // Returns true if virtual time is currently allowed to advance.
+ virtual bool VirtualTimeAllowedToAdvance() const = 0;
+
+ enum class VirtualTimePolicy {
+ // In this policy virtual time is allowed to advance. If the blink scheduler
+ // runs out of immediate work, the virtual timebase will be incremented so
+ // that the next sceduled timer may fire. NOTE Tasks will be run in time
+ // order (as usual).
+ kAdvance,
+
+ // In this policy virtual time is not allowed to advance. Delayed tasks
+ // posted to task runners owned by any child FrameSchedulers will be
+ // paused, unless their scheduled run time is less than or equal to the
+ // current virtual time. Note non-delayed tasks will run as normal.
+ kPause,
+
+ // In this policy virtual time is allowed to advance unless there are
+ // pending network fetches associated any child FrameScheduler, or a
+ // document is being parsed on a background thread. Initially virtual time
+ // is not allowed to advance until we have seen at least one load. The aim
+ // being to try and make loading (more) deterministic.
+ kDeterministicLoading,
+ };
+
+ virtual void SetInitialVirtualTimeOffset(base::TimeDelta offset) = 0;
+
+ // Sets the virtual time policy, which is applied imemdiatly to all child
+ // FrameSchedulers.
+ virtual void SetVirtualTimePolicy(VirtualTimePolicy) = 0;
+
+ class PLATFORM_EXPORT VirtualTimeObserver {
+ public:
+ virtual ~VirtualTimeObserver() = default;
+
+ // Called when virtual time advances. |virtual_time_offset| is the offset
+ // between the current virtual time and the initial virtual time when
+ // EnableVirtualTime() was called.
+ virtual void OnVirtualTimeAdvanced(base::TimeDelta virtual_time_offset) = 0;
+
+ // Called when virtual time pauses for any reason. |virtual_time_offset| is
+ // the offset between the current virtual time and the initial virtual time
+ // when EnableVirtualTime() was called.
+ virtual void OnVirtualTimePaused(base::TimeDelta virtual_time_offset) = 0;
+ };
+
+ // Adds a VirtualTimeObserver instance to be notified when virtual time has
+ // been paused.
+ virtual void AddVirtualTimeObserver(VirtualTimeObserver*) = 0;
+ virtual void RemoveVirtualTimeObserver(VirtualTimeObserver*) = 0;
+
+ // Set the remaining virtual time budget to |budget|. Once the budget runs
+ // out, |budget_exhausted_callback| is called. Note that the virtual time
+ // policy is not affected when the budget expires.
+ virtual void GrantVirtualTimeBudget(
+ base::TimeDelta budget,
+ base::OnceClosure budget_exhausted_callback) = 0;
+
+ // It's possible for pages to send infinite messages which can arbitrarily
+ // block virtual time. We can prevent this by setting an upper limit on the
+ // number of tasks that can run before virtual time is advanced.
+ // NB this anti-starvation logic doesn't apply to VirtualTimePolicy::kPause.
+ virtual void SetMaxVirtualTimeTaskStarvationCount(
+ int max_task_starvation_count) = 0;
+
+ virtual void AudioStateChanged(bool is_audio_playing) = 0;
+
+ virtual bool IsPlayingAudio() const = 0;
+
+ // Returns true if the page should be exempted from aggressive throttling
+ // (e.g. due to a page maintaining an active connection).
+ virtual bool IsExemptFromBudgetBasedThrottling() const = 0;
+
+ virtual bool HasActiveConnectionForTest() const = 0;
+
+ virtual void RequestBeginMainFrameNotExpected(bool new_state) = 0;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_PAGE_SCHEDULER_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/renderer/DEPS b/chromium/third_party/blink/renderer/platform/scheduler/renderer/DEPS
new file mode 100644
index 00000000000..37007ef0655
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/renderer/DEPS
@@ -0,0 +1,15 @@
+include_rules = [
+ "+base/metrics/single_sample_metrics.h",
+ "+cc",
+ "+components/viz/common",
+ "+services/resource_coordinator/public/cpp/resource_coordinator_features.h",
+ "+third_party/blink/renderer/platform/scheduler/base",
+ "+third_party/blink/renderer/platform/scheduler/child",
+]
+
+specific_include_rules = {
+ ".*test\.cc": [
+ "+cc/test",
+ "+components/viz/test",
+ ],
+}
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain.cc b/chromium/third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain.cc
new file mode 100644
index 00000000000..92390abd805
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain.cc
@@ -0,0 +1,172 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain.h"
+
+#include "base/time/time_override.h"
+#include "third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h"
+
+namespace blink {
+namespace scheduler {
+
+AutoAdvancingVirtualTimeDomain::AutoAdvancingVirtualTimeDomain(
+ base::Time initial_time,
+ base::TimeTicks initial_time_ticks,
+ SchedulerHelper* helper,
+ BaseTimeOverridePolicy policy)
+ : VirtualTimeDomain(initial_time_ticks),
+ task_starvation_count_(0),
+ max_task_starvation_count_(0),
+ can_advance_virtual_time_(true),
+ observer_(nullptr),
+ helper_(helper),
+ initial_time_ticks_(initial_time_ticks),
+ initial_time_(initial_time),
+ previous_time_(initial_time),
+ time_overrides_(
+ policy == BaseTimeOverridePolicy::OVERRIDE
+ ? std::make_unique<base::subtle::ScopedTimeClockOverrides>(
+ &AutoAdvancingVirtualTimeDomain::GetVirtualTime,
+ &AutoAdvancingVirtualTimeDomain::GetVirtualTimeTicks,
+ nullptr)
+ : nullptr) {
+ helper_->AddTaskObserver(this);
+ DCHECK_EQ(AutoAdvancingVirtualTimeDomain::g_time_domain_, nullptr);
+ AutoAdvancingVirtualTimeDomain::g_time_domain_ = this;
+}
+
+AutoAdvancingVirtualTimeDomain::~AutoAdvancingVirtualTimeDomain() {
+ helper_->RemoveTaskObserver(this);
+ DCHECK_EQ(AutoAdvancingVirtualTimeDomain::g_time_domain_, this);
+ AutoAdvancingVirtualTimeDomain::g_time_domain_ = nullptr;
+}
+
+base::Optional<base::TimeDelta>
+AutoAdvancingVirtualTimeDomain::DelayTillNextTask(LazyNow* lazy_now) {
+ base::TimeTicks run_time;
+ if (!NextScheduledRunTime(&run_time))
+ return base::nullopt;
+
+ // We may have advanced virtual time past the next task when a
+ // WebScopedVirtualTimePauser unpauses.
+ if (run_time <= Now())
+ return base::TimeDelta();
+
+ if (!can_advance_virtual_time_)
+ return base::nullopt;
+
+ if (MaybeAdvanceVirtualTime(run_time)) {
+ task_starvation_count_ = 0;
+ return base::TimeDelta(); // Makes DoWork post an immediate continuation.
+ }
+
+ return base::nullopt;
+}
+
+void AutoAdvancingVirtualTimeDomain::RequestWakeUpAt(base::TimeTicks now,
+ base::TimeTicks run_time) {
+ // Avoid posting pointless DoWorks. I.e. if the time domain has more then one
+ // scheduled wake up then we don't need to do anything.
+ if (can_advance_virtual_time_ && NumberOfScheduledWakeUps() == 1u)
+ RequestDoWork();
+}
+
+void AutoAdvancingVirtualTimeDomain::CancelWakeUpAt(base::TimeTicks run_time) {
+ // We ignore this because RequestWakeUpAt doesn't post a delayed task.
+}
+
+void AutoAdvancingVirtualTimeDomain::SetObserver(Observer* observer) {
+ observer_ = observer;
+}
+
+void AutoAdvancingVirtualTimeDomain::SetCanAdvanceVirtualTime(
+ bool can_advance_virtual_time) {
+ can_advance_virtual_time_ = can_advance_virtual_time;
+ if (can_advance_virtual_time_)
+ RequestDoWork();
+}
+
+void AutoAdvancingVirtualTimeDomain::SetMaxVirtualTimeTaskStarvationCount(
+ int max_task_starvation_count) {
+ max_task_starvation_count_ = max_task_starvation_count;
+ if (max_task_starvation_count_ == 0)
+ task_starvation_count_ = 0;
+}
+
+void AutoAdvancingVirtualTimeDomain::SetVirtualTimeFence(
+ base::TimeTicks virtual_time_fence) {
+ virtual_time_fence_ = virtual_time_fence;
+ if (!requested_next_virtual_time_.is_null())
+ MaybeAdvanceVirtualTime(requested_next_virtual_time_);
+}
+
+bool AutoAdvancingVirtualTimeDomain::MaybeAdvanceVirtualTime(
+ base::TimeTicks new_virtual_time) {
+ // If set, don't advance past the end of |virtual_time_fence_|.
+ if (!virtual_time_fence_.is_null() &&
+ new_virtual_time > virtual_time_fence_) {
+ requested_next_virtual_time_ = new_virtual_time;
+ new_virtual_time = virtual_time_fence_;
+ } else {
+ requested_next_virtual_time_ = base::TimeTicks();
+ }
+
+ if (new_virtual_time <= Now())
+ return false;
+
+ AdvanceNowTo(new_virtual_time);
+
+ if (observer_)
+ observer_->OnVirtualTimeAdvanced();
+
+ return true;
+}
+
+const char* AutoAdvancingVirtualTimeDomain::GetName() const {
+ return "AutoAdvancingVirtualTimeDomain";
+}
+
+void AutoAdvancingVirtualTimeDomain::WillProcessTask(
+ const base::PendingTask& pending_task) {}
+
+void AutoAdvancingVirtualTimeDomain::DidProcessTask(
+ const base::PendingTask& pending_task) {
+ if (max_task_starvation_count_ == 0 ||
+ ++task_starvation_count_ < max_task_starvation_count_) {
+ return;
+ }
+
+ // Delayed tasks are being excessively starved, so allow virtual time to
+ // advance.
+ base::TimeTicks run_time;
+ if (NextScheduledRunTime(&run_time) && MaybeAdvanceVirtualTime(run_time))
+ task_starvation_count_ = 0;
+}
+
+base::Time AutoAdvancingVirtualTimeDomain::Date() const {
+ base::TimeDelta offset = Now() - initial_time_ticks_;
+ return initial_time_ + offset;
+}
+
+AutoAdvancingVirtualTimeDomain* AutoAdvancingVirtualTimeDomain::g_time_domain_ =
+ nullptr;
+
+// static
+base::TimeTicks AutoAdvancingVirtualTimeDomain::GetVirtualTimeTicks() {
+ DCHECK(AutoAdvancingVirtualTimeDomain::g_time_domain_);
+ return AutoAdvancingVirtualTimeDomain::g_time_domain_->Now();
+}
+
+// static
+base::Time AutoAdvancingVirtualTimeDomain::GetVirtualTime() {
+ DCHECK(AutoAdvancingVirtualTimeDomain::g_time_domain_);
+ return AutoAdvancingVirtualTimeDomain::g_time_domain_->Date();
+}
+
+AutoAdvancingVirtualTimeDomain::Observer::Observer() = default;
+
+AutoAdvancingVirtualTimeDomain::Observer::~Observer() = default;
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain.h b/chromium/third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain.h
new file mode 100644
index 00000000000..e32dea00005
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain.h
@@ -0,0 +1,120 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_AUTO_ADVANCING_VIRTUAL_TIME_DOMAIN_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_AUTO_ADVANCING_VIRTUAL_TIME_DOMAIN_H_
+
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/time/time_override.h"
+#include "third_party/blink/renderer/platform/scheduler/base/virtual_time_domain.h"
+
+namespace blink {
+namespace scheduler {
+class SchedulerHelper;
+
+// A time domain that runs tasks sequentially in time order but doesn't sleep
+// between delayed tasks.
+//
+// KEY: A-E are delayed tasks
+// | A B C D E (Execution with RealTimeDomain)
+// |-----------------------------> time
+//
+// |ABCDE (Execution with AutoAdvancingVirtualTimeDomain)
+// |-----------------------------> time
+class PLATFORM_EXPORT AutoAdvancingVirtualTimeDomain
+ : public VirtualTimeDomain,
+ public base::MessageLoop::TaskObserver {
+ public:
+ enum class BaseTimeOverridePolicy { OVERRIDE, DO_NOT_OVERRIDE };
+
+ AutoAdvancingVirtualTimeDomain(base::Time initial_time,
+ base::TimeTicks initial_time_ticks,
+ SchedulerHelper* helper,
+ BaseTimeOverridePolicy policy);
+ ~AutoAdvancingVirtualTimeDomain() override;
+
+ // TimeDomain implementation:
+ base::Optional<base::TimeDelta> DelayTillNextTask(LazyNow* lazy_now) override;
+ void RequestWakeUpAt(base::TimeTicks now, base::TimeTicks run_time) override;
+ void CancelWakeUpAt(base::TimeTicks run_time) override;
+ const char* GetName() const override;
+
+ class PLATFORM_EXPORT Observer {
+ public:
+ Observer();
+ virtual ~Observer();
+
+ // Notification received when the virtual time advances.
+ virtual void OnVirtualTimeAdvanced() = 0;
+ };
+
+ // Note its assumed that |observer| will either remove itself or last at least
+ // as long as this AutoAdvancingVirtualTimeDomain.
+ void SetObserver(Observer* observer);
+
+ // Controls whether or not virtual time is allowed to advance, when the
+ // TaskQueueManager runs out of immediate work to do.
+ void SetCanAdvanceVirtualTime(bool can_advance_virtual_time);
+
+ // If non-null, virtual time may not advance past |virtual_time_fence|.
+ void SetVirtualTimeFence(base::TimeTicks virtual_time_fence);
+
+ // The maximum number amount of delayed task starvation we will allow.
+ // NB a value of 0 allows infinite starvation. A reasonable value for this in
+ // practice is around 1000 tasks, which should only affect rendering of the
+ // heaviest pages.
+ void SetMaxVirtualTimeTaskStarvationCount(int max_task_starvation_count);
+
+ // Updates to min(NextDelayedTaskTime, |new_virtual_time|) if thats ahead of
+ // the current virtual time. Returns true if time was advanced.
+ bool MaybeAdvanceVirtualTime(base::TimeTicks new_virtual_time);
+
+ // base::PendingTask implementation:
+ void WillProcessTask(const base::PendingTask& pending_task) override;
+ void DidProcessTask(const base::PendingTask& pending_task) override;
+
+ int task_starvation_count() const { return task_starvation_count_; }
+
+ private:
+ // Can be called on any thread.
+ base::Time Date() const;
+
+ static base::TimeTicks GetVirtualTimeTicks();
+ static base::Time GetVirtualTime();
+ static AutoAdvancingVirtualTimeDomain* g_time_domain_;
+
+ // The number of tasks that have been run since the last time VirtualTime
+ // advanced. Used to detect excessive starvation of delayed tasks.
+ int task_starvation_count_;
+
+ // The maximum number amount of delayed task starvation we will allow.
+ // NB a value of 0 allows infinite starvation.
+ int max_task_starvation_count_;
+
+ bool can_advance_virtual_time_;
+ Observer* observer_; // NOT OWNED
+ SchedulerHelper* helper_; // NOT OWNED
+
+ // VirtualTime is usually doled out in 100ms intervals using fences and this
+ // variable let us honor a request to MaybeAdvanceVirtualTime that straddles
+ // one of these boundaries.
+ base::TimeTicks requested_next_virtual_time_;
+
+ // Upper limit on how far virtual time is allowed to advance.
+ base::TimeTicks virtual_time_fence_;
+
+ const base::TimeTicks initial_time_ticks_;
+ const base::Time initial_time_;
+ base::Time previous_time_;
+
+ std::unique_ptr<base::subtle::ScopedTimeClockOverrides> time_overrides_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutoAdvancingVirtualTimeDomain);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_AUTO_ADVANCING_VIRTUAL_TIME_DOMAIN_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain_unittest.cc
new file mode 100644
index 00000000000..146cb812329
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain_unittest.cc
@@ -0,0 +1,248 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain.h"
+
+#include <memory>
+#include "base/test/simple_test_tick_clock.h"
+#include "components/viz/test/ordered_simple_task_runner.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_manager.h"
+#include "third_party/blink/renderer/platform/scheduler/base/test_task_time_observer.h"
+#include "third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_helper.h"
+#include "third_party/blink/renderer/platform/scheduler/test/task_queue_manager_for_test.h"
+#include "third_party/blink/renderer/platform/scheduler/test/test_task_queue.h"
+
+namespace blink {
+namespace scheduler {
+// Namespace to avoid symbol collisions in jumbo builds.
+namespace auto_advancing_virtual_time_domain_unittest {
+
+class AutoAdvancingVirtualTimeDomainTest : public testing::Test {
+ public:
+ AutoAdvancingVirtualTimeDomainTest() = default;
+ ~AutoAdvancingVirtualTimeDomainTest() override = default;
+
+ void SetUp() override {
+ clock_.Advance(base::TimeDelta::FromMicroseconds(5000));
+
+ mock_task_runner_ =
+ base::MakeRefCounted<cc::OrderedSimpleTaskRunner>(&clock_, false);
+
+ scheduler_helper_.reset(new WorkerSchedulerHelper(
+ TaskQueueManagerForTest::Create(nullptr, mock_task_runner_, &clock_),
+ nullptr));
+
+ scheduler_helper_->AddTaskTimeObserver(&test_task_time_observer_);
+ task_queue_ = scheduler_helper_->DefaultWorkerTaskQueue();
+ initial_time_ = base::Time::FromJsTime(100000.0);
+ initial_time_ticks_ = clock_.NowTicks();
+ auto_advancing_time_domain_.reset(new AutoAdvancingVirtualTimeDomain(
+ initial_time_, initial_time_ticks_, scheduler_helper_.get(),
+ AutoAdvancingVirtualTimeDomain::BaseTimeOverridePolicy::OVERRIDE));
+ scheduler_helper_->RegisterTimeDomain(auto_advancing_time_domain_.get());
+ task_queue_->SetTimeDomain(auto_advancing_time_domain_.get());
+ }
+
+ void TearDown() override {
+ task_queue_->ShutdownTaskQueue();
+ scheduler_helper_->UnregisterTimeDomain(auto_advancing_time_domain_.get());
+ }
+
+ base::Time initial_time_;
+ base::TimeTicks initial_time_ticks_;
+ base::SimpleTestTickClock clock_;
+ scoped_refptr<cc::OrderedSimpleTaskRunner> mock_task_runner_;
+ std::unique_ptr<WorkerSchedulerHelper> scheduler_helper_;
+ scoped_refptr<TaskQueue> task_queue_;
+ std::unique_ptr<AutoAdvancingVirtualTimeDomain> auto_advancing_time_domain_;
+ TestTaskTimeObserver test_task_time_observer_;
+};
+
+namespace {
+void NopTask(bool* task_run) {
+ *task_run = true;
+}
+
+class MockObserver : public AutoAdvancingVirtualTimeDomain::Observer {
+ public:
+ MOCK_METHOD0(OnVirtualTimeAdvanced, void());
+};
+
+} // namesapce
+
+TEST_F(AutoAdvancingVirtualTimeDomainTest, VirtualTimeAdvances) {
+ MockObserver mock_observer;
+ auto_advancing_time_domain_->SetObserver(&mock_observer);
+
+ base::TimeDelta delay = base::TimeDelta::FromMilliseconds(10);
+ bool task_run = false;
+ task_queue_->PostDelayedTask(FROM_HERE, base::BindOnce(NopTask, &task_run),
+ delay);
+
+ EXPECT_CALL(mock_observer, OnVirtualTimeAdvanced());
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_EQ(initial_time_ticks_, clock_.NowTicks());
+ EXPECT_EQ(initial_time_ticks_ + delay,
+ auto_advancing_time_domain_->CreateLazyNow().Now());
+ EXPECT_TRUE(task_run);
+
+ auto_advancing_time_domain_->SetObserver(nullptr);
+}
+
+TEST_F(AutoAdvancingVirtualTimeDomainTest, VirtualTimeDoesNotAdvance) {
+ MockObserver mock_observer;
+ auto_advancing_time_domain_->SetObserver(&mock_observer);
+
+ base::TimeDelta delay = base::TimeDelta::FromMilliseconds(10);
+ bool task_run = false;
+ task_queue_->PostDelayedTask(FROM_HERE, base::BindOnce(NopTask, &task_run),
+ delay);
+
+ auto_advancing_time_domain_->SetCanAdvanceVirtualTime(false);
+
+ EXPECT_CALL(mock_observer, OnVirtualTimeAdvanced()).Times(0);
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_EQ(initial_time_ticks_, clock_.NowTicks());
+ EXPECT_EQ(initial_time_ticks_,
+ auto_advancing_time_domain_->CreateLazyNow().Now());
+ EXPECT_FALSE(task_run);
+
+ auto_advancing_time_domain_->SetObserver(nullptr);
+}
+
+namespace {
+void RepostingTask(scoped_refptr<TaskQueue> task_queue,
+ int max_count,
+ int* count) {
+ if (++(*count) >= max_count)
+ return;
+
+ task_queue->PostTask(
+ FROM_HERE, base::BindOnce(&RepostingTask, task_queue, max_count, count));
+}
+
+void DelayedTask(int* count_in, int* count_out) {
+ *count_out = *count_in;
+}
+
+} // namespace
+
+TEST_F(AutoAdvancingVirtualTimeDomainTest,
+ MaxVirtualTimeTaskStarvationCountOneHundred) {
+ auto_advancing_time_domain_->SetCanAdvanceVirtualTime(true);
+ auto_advancing_time_domain_->SetMaxVirtualTimeTaskStarvationCount(100);
+
+ int count = 0;
+ int delayed_task_run_at_count = 0;
+ RepostingTask(task_queue_, 1000, &count);
+ task_queue_->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(DelayedTask, &count, &delayed_task_run_at_count),
+ base::TimeDelta::FromMilliseconds(10));
+
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_EQ(1000, count);
+ EXPECT_EQ(102, delayed_task_run_at_count);
+}
+
+TEST_F(AutoAdvancingVirtualTimeDomainTest,
+ MaxVirtualTimeTaskStarvationCountZero) {
+ auto_advancing_time_domain_->SetCanAdvanceVirtualTime(true);
+ auto_advancing_time_domain_->SetMaxVirtualTimeTaskStarvationCount(0);
+
+ int count = 0;
+ int delayed_task_run_at_count = 0;
+ RepostingTask(task_queue_, 1000, &count);
+ task_queue_->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(DelayedTask, &count, &delayed_task_run_at_count),
+ base::TimeDelta::FromMilliseconds(10));
+
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_EQ(1000, count);
+ // If the initial count had been higher, the delayed task could have been
+ // arbitrarily delayed.
+ EXPECT_EQ(1000, delayed_task_run_at_count);
+}
+
+TEST_F(AutoAdvancingVirtualTimeDomainTest, TaskStarvationCountIncrements) {
+ auto_advancing_time_domain_->SetMaxVirtualTimeTaskStarvationCount(100);
+ EXPECT_EQ(0, auto_advancing_time_domain_->task_starvation_count());
+ base::PendingTask fake_task(FROM_HERE, base::OnceClosure());
+ auto_advancing_time_domain_->DidProcessTask(fake_task);
+ EXPECT_EQ(1, auto_advancing_time_domain_->task_starvation_count());
+}
+
+TEST_F(AutoAdvancingVirtualTimeDomainTest, TaskStarvationCountNotIncrements) {
+ auto_advancing_time_domain_->SetMaxVirtualTimeTaskStarvationCount(0);
+ EXPECT_EQ(0, auto_advancing_time_domain_->task_starvation_count());
+ base::PendingTask fake_task(FROM_HERE, base::OnceClosure());
+ auto_advancing_time_domain_->DidProcessTask(fake_task);
+ EXPECT_EQ(0, auto_advancing_time_domain_->task_starvation_count());
+}
+
+TEST_F(AutoAdvancingVirtualTimeDomainTest, TaskStarvationCountResets) {
+ auto_advancing_time_domain_->SetMaxVirtualTimeTaskStarvationCount(100);
+ base::PendingTask fake_task(FROM_HERE, base::OnceClosure());
+ auto_advancing_time_domain_->DidProcessTask(fake_task);
+ EXPECT_EQ(1, auto_advancing_time_domain_->task_starvation_count());
+ auto_advancing_time_domain_->SetMaxVirtualTimeTaskStarvationCount(0);
+ EXPECT_EQ(0, auto_advancing_time_domain_->task_starvation_count());
+}
+
+TEST_F(AutoAdvancingVirtualTimeDomainTest, BaseTimeOverriden) {
+ base::Time initial_time = base::Time::FromJsTime(100000.0);
+ EXPECT_EQ(base::Time::Now(), initial_time);
+
+ // Make time advance.
+ base::TimeDelta delay = base::TimeDelta::FromMilliseconds(10);
+ bool task_run = false;
+ task_queue_->PostDelayedTask(FROM_HERE, base::BindOnce(NopTask, &task_run),
+ delay);
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_EQ(base::Time::Now(), initial_time + delay);
+}
+
+TEST_F(AutoAdvancingVirtualTimeDomainTest, BaseTimeTicksOverriden) {
+ base::TimeTicks initial_time = clock_.NowTicks();
+ EXPECT_EQ(base::TimeTicks::Now(), initial_time);
+
+ // Make time advance.
+ base::TimeDelta delay = base::TimeDelta::FromMilliseconds(20);
+ bool task_run = false;
+ task_queue_->PostDelayedTask(FROM_HERE, base::BindOnce(NopTask, &task_run),
+ delay);
+ mock_task_runner_->RunUntilIdle();
+
+ EXPECT_EQ(base::TimeTicks::Now(), initial_time + delay);
+}
+
+TEST_F(AutoAdvancingVirtualTimeDomainTest,
+ DelayTillNextTaskHandlesPastRunTime) {
+ base::TimeTicks initial_time = clock_.NowTicks();
+
+ // Post a task for t+10ms.
+ bool task_run = false;
+ task_queue_->PostDelayedTask(FROM_HERE, base::BindOnce(NopTask, &task_run),
+ base::TimeDelta::FromMilliseconds(10));
+
+ // Advance virtual time past task time to t+100ms.
+ auto_advancing_time_domain_->MaybeAdvanceVirtualTime(
+ initial_time + base::TimeDelta::FromMilliseconds(100));
+
+ // Task at t+10ms should be run immediately.
+ EXPECT_EQ(base::TimeDelta(),
+ auto_advancing_time_domain_->DelayTillNextTask(nullptr));
+}
+
+} // namespace auto_advancing_virtual_time_domain_unittest
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/renderer/deadline_task_runner.cc b/chromium/third_party/blink/renderer/platform/scheduler/renderer/deadline_task_runner.cc
new file mode 100644
index 00000000000..e945595a3d5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/renderer/deadline_task_runner.cc
@@ -0,0 +1,41 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/renderer/deadline_task_runner.h"
+
+#include "base/bind.h"
+
+namespace blink {
+namespace scheduler {
+
+DeadlineTaskRunner::DeadlineTaskRunner(
+ const base::RepeatingClosure& callback,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+ : callback_(callback), task_runner_(task_runner) {
+ cancelable_run_internal_.Reset(base::BindRepeating(
+ &DeadlineTaskRunner::RunInternal, base::Unretained(this)));
+}
+
+DeadlineTaskRunner::~DeadlineTaskRunner() = default;
+
+void DeadlineTaskRunner::SetDeadline(const base::Location& from_here,
+ base::TimeDelta delay,
+ base::TimeTicks now) {
+ DCHECK(delay > base::TimeDelta());
+ base::TimeTicks deadline = now + delay;
+ if (deadline_.is_null() || deadline < deadline_) {
+ deadline_ = deadline;
+ cancelable_run_internal_.Cancel();
+ task_runner_->PostDelayedTask(
+ from_here, cancelable_run_internal_.GetCallback(), delay);
+ }
+}
+
+void DeadlineTaskRunner::RunInternal() {
+ deadline_ = base::TimeTicks();
+ callback_.Run();
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/renderer/deadline_task_runner.h b/chromium/third_party/blink/renderer/platform/scheduler/renderer/deadline_task_runner.h
new file mode 100644
index 00000000000..c37ec85dcc8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/renderer/deadline_task_runner.h
@@ -0,0 +1,51 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_DEADLINE_TASK_RUNNER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_DEADLINE_TASK_RUNNER_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/single_thread_task_runner.h"
+#include "base/time/time.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/scheduler/child/cancelable_closure_holder.h"
+
+namespace blink {
+namespace scheduler {
+
+// Runs a posted task at latest by a given deadline, but possibly sooner.
+class PLATFORM_EXPORT DeadlineTaskRunner {
+ public:
+ DeadlineTaskRunner(const base::RepeatingClosure& callback,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+
+ ~DeadlineTaskRunner();
+
+ // If there is no outstanding task then a task is posted to run after |delay|.
+ // If there is an outstanding task which is scheduled to run:
+ // a) sooner - then this is a NOP.
+ // b) later - then the outstanding task is cancelled and a new task is
+ // posted to run after |delay|.
+ //
+ // Once the deadline task has run, we reset.
+ void SetDeadline(const base::Location& from_here,
+ base::TimeDelta delay,
+ base::TimeTicks now);
+
+ private:
+ void RunInternal();
+
+ CancelableClosureHolder cancelable_run_internal_;
+ base::RepeatingClosure callback_;
+ base::TimeTicks deadline_;
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeadlineTaskRunner);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_DEADLINE_TASK_RUNNER_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/renderer/deadline_task_runner_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/renderer/deadline_task_runner_unittest.cc
new file mode 100644
index 00000000000..7cf087aa033
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/renderer/deadline_task_runner_unittest.cc
@@ -0,0 +1,104 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/renderer/deadline_task_runner.h"
+
+#include <memory>
+
+#include "base/test/simple_test_tick_clock.h"
+#include "components/viz/test/ordered_simple_task_runner.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+namespace scheduler {
+
+class DeadlineTaskRunnerTest : public testing::Test {
+ public:
+ DeadlineTaskRunnerTest() = default;
+ ~DeadlineTaskRunnerTest() override = default;
+
+ void SetUp() override {
+ clock_.reset(new base::SimpleTestTickClock());
+ clock_->Advance(base::TimeDelta::FromMicroseconds(5000));
+ mock_task_runner_ = new cc::OrderedSimpleTaskRunner(clock_.get(), true);
+ deadline_task_runner_.reset(new DeadlineTaskRunner(
+ base::BindRepeating(&DeadlineTaskRunnerTest::TestTask,
+ base::Unretained(this)),
+ mock_task_runner_));
+ run_times_.clear();
+ }
+
+ bool RunUntilIdle() { return mock_task_runner_->RunUntilIdle(); }
+
+ void TestTask() { run_times_.push_back(clock_->NowTicks()); }
+
+ std::unique_ptr<base::SimpleTestTickClock> clock_;
+ scoped_refptr<cc::OrderedSimpleTaskRunner> mock_task_runner_;
+ std::unique_ptr<DeadlineTaskRunner> deadline_task_runner_;
+ std::vector<base::TimeTicks> run_times_;
+};
+
+TEST_F(DeadlineTaskRunnerTest, RunOnce) {
+ base::TimeTicks start_time = clock_->NowTicks();
+ base::TimeDelta delay = base::TimeDelta::FromMilliseconds(10);
+ deadline_task_runner_->SetDeadline(FROM_HERE, delay, clock_->NowTicks());
+ RunUntilIdle();
+
+ EXPECT_THAT(run_times_, testing::ElementsAre(start_time + delay));
+};
+
+TEST_F(DeadlineTaskRunnerTest, RunTwice) {
+ base::TimeDelta delay1 = base::TimeDelta::FromMilliseconds(10);
+ base::TimeTicks deadline1 = clock_->NowTicks() + delay1;
+ deadline_task_runner_->SetDeadline(FROM_HERE, delay1, clock_->NowTicks());
+ RunUntilIdle();
+
+ base::TimeDelta delay2 = base::TimeDelta::FromMilliseconds(100);
+ base::TimeTicks deadline2 = clock_->NowTicks() + delay2;
+ deadline_task_runner_->SetDeadline(FROM_HERE, delay2, clock_->NowTicks());
+ RunUntilIdle();
+
+ EXPECT_THAT(run_times_, testing::ElementsAre(deadline1, deadline2));
+};
+
+TEST_F(DeadlineTaskRunnerTest, EarlierDeadlinesTakePrecidence) {
+ base::TimeTicks start_time = clock_->NowTicks();
+ base::TimeDelta delay1 = base::TimeDelta::FromMilliseconds(1);
+ base::TimeDelta delay10 = base::TimeDelta::FromMilliseconds(10);
+ base::TimeDelta delay100 = base::TimeDelta::FromMilliseconds(100);
+ deadline_task_runner_->SetDeadline(FROM_HERE, delay100, clock_->NowTicks());
+ deadline_task_runner_->SetDeadline(FROM_HERE, delay10, clock_->NowTicks());
+ deadline_task_runner_->SetDeadline(FROM_HERE, delay1, clock_->NowTicks());
+
+ RunUntilIdle();
+
+ EXPECT_THAT(run_times_, testing::ElementsAre(start_time + delay1));
+};
+
+TEST_F(DeadlineTaskRunnerTest, LaterDeadlinesIgnored) {
+ base::TimeTicks start_time = clock_->NowTicks();
+ base::TimeDelta delay100 = base::TimeDelta::FromMilliseconds(100);
+ base::TimeDelta delay10000 = base::TimeDelta::FromMilliseconds(10000);
+ deadline_task_runner_->SetDeadline(FROM_HERE, delay100, clock_->NowTicks());
+ deadline_task_runner_->SetDeadline(FROM_HERE, delay10000, clock_->NowTicks());
+
+ RunUntilIdle();
+
+ EXPECT_THAT(run_times_, testing::ElementsAre(start_time + delay100));
+};
+
+TEST_F(DeadlineTaskRunnerTest, DeleteDeadlineTaskRunnerAfterPosting) {
+ deadline_task_runner_->SetDeadline(
+ FROM_HERE, base::TimeDelta::FromMilliseconds(10), clock_->NowTicks());
+
+ // Deleting the pending task should cancel it.
+ deadline_task_runner_.reset(nullptr);
+ RunUntilIdle();
+
+ EXPECT_TRUE(run_times_.empty());
+};
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/renderer/frame_status.cc b/chromium/third_party/blink/renderer/platform/scheduler/renderer/frame_status.cc
new file mode 100644
index 00000000000..c4ef05aafe5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/renderer/frame_status.cc
@@ -0,0 +1,84 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/renderer/frame_status.h"
+
+#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
+#include "third_party/blink/renderer/platform/scheduler/public/page_scheduler.h"
+
+namespace blink {
+namespace scheduler {
+
+namespace {
+
+enum class FrameThrottlingState {
+ kVisible = 0,
+ kVisibleService = 1,
+ kHidden = 2,
+ kHiddenService = 3,
+ kBackground = 4,
+ kBackgroundExemptSelf = 5,
+ kBackgroundExemptOther = 6,
+
+ kCount = 7
+};
+
+enum class FrameOriginState {
+ kMainFrame = 0,
+ kSameOrigin = 1,
+ kCrossOrigin = 2,
+
+ kCount = 3
+};
+
+FrameThrottlingState GetFrameThrottlingState(
+ const FrameScheduler& frame_scheduler) {
+ if (frame_scheduler.IsPageVisible()) {
+ if (frame_scheduler.IsFrameVisible())
+ return FrameThrottlingState::kVisible;
+ return FrameThrottlingState::kHidden;
+ }
+
+ PageScheduler* page_scheduler = frame_scheduler.GetPageScheduler();
+ if (page_scheduler && page_scheduler->IsPlayingAudio()) {
+ if (frame_scheduler.IsFrameVisible())
+ return FrameThrottlingState::kVisibleService;
+ return FrameThrottlingState::kHiddenService;
+ }
+
+ if (frame_scheduler.IsExemptFromBudgetBasedThrottling())
+ return FrameThrottlingState::kBackgroundExemptSelf;
+
+ if (page_scheduler && page_scheduler->IsExemptFromBudgetBasedThrottling())
+ return FrameThrottlingState::kBackgroundExemptOther;
+
+ return FrameThrottlingState::kBackground;
+}
+
+FrameOriginState GetFrameOriginState(const FrameScheduler& frame_scheduler) {
+ if (frame_scheduler.GetFrameType() == FrameScheduler::FrameType::kMainFrame) {
+ return FrameOriginState::kMainFrame;
+ }
+ if (frame_scheduler.IsCrossOrigin())
+ return FrameOriginState::kCrossOrigin;
+ return FrameOriginState::kSameOrigin;
+}
+
+} // namespace
+
+FrameStatus GetFrameStatus(FrameScheduler* frame_scheduler) {
+ if (!frame_scheduler)
+ return FrameStatus::kNone;
+ FrameThrottlingState throttling_state =
+ GetFrameThrottlingState(*frame_scheduler);
+ FrameOriginState origin_state = GetFrameOriginState(*frame_scheduler);
+ return static_cast<FrameStatus>(
+ static_cast<int>(FrameStatus::kSpecialCasesCount) +
+ static_cast<int>(origin_state) *
+ static_cast<int>(FrameThrottlingState::kCount) +
+ static_cast<int>(throttling_state));
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/renderer/frame_status.h b/chromium/third_party/blink/renderer/platform/scheduler/renderer/frame_status.h
new file mode 100644
index 00000000000..29c98f89891
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/renderer/frame_status.h
@@ -0,0 +1,85 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_FRAME_STATUS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_FRAME_STATUS_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+class FrameScheduler;
+
+namespace scheduler {
+
+// This enum is used for histogram and should not be renumbered.
+// This enum should be kept in sync with FrameThrottlingState and
+// FrameOriginState.
+//
+// There are three main states:
+// VISIBLE describes frames which are visible to the user (both page and frame
+// are visible).
+// Without this service frame would have had kBackgrounded state.
+// HIDDEN describes frames which are out of viewport but the page is visible
+// to the user.
+// BACKGROUND describes frames in background pages.
+//
+// There are four auxillary states:
+// VISIBLE_SERVICE describes frames which are treated as visible to the user
+// but it is a service (e.g. audio) which forces the page to be foregrounded.
+// HIDDEN_SERVICE describes offscreen frames in pages which are treated as
+// foregrounded due to a presence of a service (e.g. audio playing).
+// BACKGROUND_EXEMPT_SELF describes background frames which are
+// exempted from background throttling due to a special conditions being met
+// for this frame.
+// BACKGROUND_EXEMPT_kOther describes background frames which are exempted from
+// background throttling due to other frames granting an exemption for
+// the whole page.
+//
+// Note that all these seven states are disjoint, e.g, when calculating
+// a metric for background BACKGROUND, BACKGROUND_EXEMPT_SELF and
+// BACKGROUND_EXEMPT_kOther should be added together.
+enum class FrameStatus {
+ // Used to describe a task queue which doesn't have a frame associated
+ // (e.g. global task queue).
+ kNone = 0,
+
+ // This frame was detached and does not have origin or visibility status
+ // anymore.
+ kDetached = 1,
+
+ kSpecialCasesCount = 2,
+
+ kMainFrameVisible = 2,
+ kMainFrameVisibleService = 3,
+ kMainFrameHidden = 4,
+ kMainFrameHiddenService = 5,
+ kMainFrameBackground = 6,
+ kMainFrameBackgroundExemptSelf = 7,
+ kMainFrameBackgroundExemptOther = 8,
+
+ kSameOriginVisible = 9,
+ kSameOriginVisibleService = 10,
+ kSameOriginHidden = 11,
+ kSameOriginHiddenService = 12,
+ kSameOriginBackground = 13,
+ kSameOriginBackgroundExemptSelf = 14,
+ kSameOriginBackgroundExemptOther = 15,
+
+ kCrossOriginVisible = 16,
+ kCrossOriginVisibleService = 17,
+ kCrossOriginHidden = 18,
+ kCrossOriginHiddenService = 19,
+ kCrossOriginBackground = 20,
+ kCrossOriginBackgroundExemptSelf = 21,
+ kCrossOriginBackgroundExemptOther = 22,
+
+ kCount = 23
+};
+
+PLATFORM_EXPORT FrameStatus GetFrameStatus(FrameScheduler* frame_scheduler);
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_FRAME_STATUS_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/renderer/idle_time_estimator.cc b/chromium/third_party/blink/renderer/platform/scheduler/renderer/idle_time_estimator.cc
new file mode 100644
index 00000000000..4ee87d3e6cb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/renderer/idle_time_estimator.cc
@@ -0,0 +1,76 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/renderer/idle_time_estimator.h"
+
+#include "base/time/default_tick_clock.h"
+
+namespace blink {
+namespace scheduler {
+
+IdleTimeEstimator::IdleTimeEstimator(
+ const scoped_refptr<TaskQueue>& compositor_task_runner,
+ const base::TickClock* time_source,
+ int sample_count,
+ double estimation_percentile)
+ : compositor_task_queue_(compositor_task_runner),
+ per_frame_compositor_task_runtime_(sample_count),
+ time_source_(time_source),
+ estimation_percentile_(estimation_percentile),
+ nesting_level_(0),
+ did_commit_(false) {
+ compositor_task_queue_->AddTaskObserver(this);
+}
+
+IdleTimeEstimator::~IdleTimeEstimator() {
+ compositor_task_queue_->RemoveTaskObserver(this);
+}
+
+base::TimeDelta IdleTimeEstimator::GetExpectedIdleDuration(
+ base::TimeDelta compositor_frame_interval) const {
+ base::TimeDelta expected_compositor_task_runtime_ =
+ per_frame_compositor_task_runtime_.Percentile(estimation_percentile_);
+ return std::max(base::TimeDelta(), compositor_frame_interval -
+ expected_compositor_task_runtime_);
+}
+
+void IdleTimeEstimator::DidCommitFrameToCompositor() {
+ // This will run inside of a WillProcessTask / DidProcessTask pair, let
+ // DidProcessTask know a frame was comitted.
+ if (nesting_level_ == 1)
+ did_commit_ = true;
+}
+
+void IdleTimeEstimator::Clear() {
+ task_start_time_ = base::TimeTicks();
+ prev_commit_time_ = base::TimeTicks();
+ cumulative_compositor_runtime_ = base::TimeDelta();
+ per_frame_compositor_task_runtime_.Clear();
+ did_commit_ = false;
+}
+
+void IdleTimeEstimator::WillProcessTask(const base::PendingTask& pending_task) {
+ nesting_level_++;
+ if (nesting_level_ == 1)
+ task_start_time_ = time_source_->NowTicks();
+}
+
+void IdleTimeEstimator::DidProcessTask(const base::PendingTask& pending_task) {
+ nesting_level_--;
+ DCHECK_GE(nesting_level_, 0);
+ if (nesting_level_ != 0)
+ return;
+
+ cumulative_compositor_runtime_ += time_source_->NowTicks() - task_start_time_;
+
+ if (did_commit_) {
+ per_frame_compositor_task_runtime_.InsertSample(
+ cumulative_compositor_runtime_);
+ cumulative_compositor_runtime_ = base::TimeDelta();
+ did_commit_ = false;
+ }
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/renderer/idle_time_estimator.h b/chromium/third_party/blink/renderer/platform/scheduler/renderer/idle_time_estimator.h
new file mode 100644
index 00000000000..43cdbcb8bdb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/renderer/idle_time_estimator.h
@@ -0,0 +1,60 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_IDLE_TIME_ESTIMATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_IDLE_TIME_ESTIMATOR_H_
+
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/time/tick_clock.h"
+#include "cc/base/rolling_time_delta_history.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue.h"
+
+namespace blink {
+namespace scheduler {
+
+// Estimates how much idle time there is available. Ignores nested tasks.
+class PLATFORM_EXPORT IdleTimeEstimator
+ : public base::MessageLoop::TaskObserver {
+ public:
+ IdleTimeEstimator(const scoped_refptr<TaskQueue>& compositor_task_runner,
+ const base::TickClock* time_source,
+ int sample_count,
+ double estimation_percentile);
+
+ ~IdleTimeEstimator() override;
+
+ // Expected Idle time is defined as: |compositor_frame_interval| minus
+ // expected compositor task duration.
+ base::TimeDelta GetExpectedIdleDuration(
+ base::TimeDelta compositor_frame_interval) const;
+
+ void DidCommitFrameToCompositor();
+
+ void Clear();
+
+ // TaskObserver implementation:
+ void WillProcessTask(const base::PendingTask& pending_task) override;
+ void DidProcessTask(const base::PendingTask& pending_task) override;
+
+ private:
+ scoped_refptr<TaskQueue> compositor_task_queue_;
+ cc::RollingTimeDeltaHistory per_frame_compositor_task_runtime_;
+ const base::TickClock* time_source_; // NOT OWNED
+ double estimation_percentile_;
+
+ base::TimeTicks task_start_time_;
+ base::TimeTicks prev_commit_time_;
+ base::TimeDelta cumulative_compositor_runtime_;
+ int nesting_level_;
+ bool did_commit_;
+
+ DISALLOW_COPY_AND_ASSIGN(IdleTimeEstimator);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_IDLE_TIME_ESTIMATOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/renderer/idle_time_estimator_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/renderer/idle_time_estimator_unittest.cc
new file mode 100644
index 00000000000..326bdccfd1a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/renderer/idle_time_estimator_unittest.cc
@@ -0,0 +1,165 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/renderer/idle_time_estimator.h"
+
+#include <memory>
+#include "base/memory/scoped_refptr.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "components/viz/test/ordered_simple_task_runner.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_manager.h"
+#include "third_party/blink/renderer/platform/scheduler/base/test_task_time_observer.h"
+#include "third_party/blink/renderer/platform/scheduler/test/task_queue_manager_for_test.h"
+#include "third_party/blink/renderer/platform/scheduler/test/test_task_queue.h"
+
+namespace blink {
+namespace scheduler {
+
+class IdleTimeEstimatorForTest : public IdleTimeEstimator {
+ public:
+ IdleTimeEstimatorForTest(
+ const scoped_refptr<TaskQueue>& compositor_task_runner,
+ const base::TickClock* clock,
+ int sample_count,
+ double estimation_percentile)
+ : IdleTimeEstimator(compositor_task_runner,
+ clock,
+ sample_count,
+ estimation_percentile) {}
+};
+
+class IdleTimeEstimatorTest : public testing::Test {
+ public:
+ IdleTimeEstimatorTest()
+ : frame_length_(base::TimeDelta::FromMilliseconds(16)) {}
+
+ ~IdleTimeEstimatorTest() override = default;
+
+ void SetUp() override {
+ clock_.Advance(base::TimeDelta::FromMicroseconds(5000));
+ mock_task_runner_ =
+ base::MakeRefCounted<cc::OrderedSimpleTaskRunner>(&clock_, false);
+ manager_ =
+ TaskQueueManagerForTest::Create(nullptr, mock_task_runner_, &clock_);
+ compositor_task_queue_ =
+ manager_->CreateTaskQueue<TestTaskQueue>(TaskQueue::Spec("test_tq"));
+ estimator_.reset(
+ new IdleTimeEstimatorForTest(compositor_task_queue_, &clock_, 10, 50));
+ }
+
+ void SimulateFrameWithOneCompositorTask(int compositor_time) {
+ base::TimeDelta non_idle_time =
+ base::TimeDelta::FromMilliseconds(compositor_time);
+ base::PendingTask task(FROM_HERE, base::Closure());
+ estimator_->WillProcessTask(task);
+ clock_.Advance(non_idle_time);
+ estimator_->DidCommitFrameToCompositor();
+ estimator_->DidProcessTask(task);
+ if (non_idle_time < frame_length_)
+ clock_.Advance(frame_length_ - non_idle_time);
+ }
+
+ void SimulateFrameWithTwoCompositorTasks(int compositor_time1,
+ int compositor_time2) {
+ base::TimeDelta non_idle_time1 =
+ base::TimeDelta::FromMilliseconds(compositor_time1);
+ base::TimeDelta non_idle_time2 =
+ base::TimeDelta::FromMilliseconds(compositor_time2);
+ base::PendingTask task(FROM_HERE, base::Closure());
+ estimator_->WillProcessTask(task);
+ clock_.Advance(non_idle_time1);
+ estimator_->DidProcessTask(task);
+
+ estimator_->WillProcessTask(task);
+ clock_.Advance(non_idle_time2);
+ estimator_->DidCommitFrameToCompositor();
+ estimator_->DidProcessTask(task);
+
+ base::TimeDelta idle_time = frame_length_ - non_idle_time1 - non_idle_time2;
+ clock_.Advance(idle_time);
+ }
+
+ base::SimpleTestTickClock clock_;
+ scoped_refptr<cc::OrderedSimpleTaskRunner> mock_task_runner_;
+ std::unique_ptr<TaskQueueManager> manager_;
+ scoped_refptr<TaskQueue> compositor_task_queue_;
+ std::unique_ptr<IdleTimeEstimatorForTest> estimator_;
+ const base::TimeDelta frame_length_;
+ TestTaskTimeObserver test_task_time_observer_;
+};
+
+TEST_F(IdleTimeEstimatorTest, InitialTimeEstimateWithNoData) {
+ EXPECT_EQ(frame_length_, estimator_->GetExpectedIdleDuration(frame_length_));
+}
+
+TEST_F(IdleTimeEstimatorTest, BasicEstimation_SteadyState) {
+ SimulateFrameWithOneCompositorTask(5);
+ SimulateFrameWithOneCompositorTask(5);
+
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(11),
+ estimator_->GetExpectedIdleDuration(frame_length_));
+}
+
+TEST_F(IdleTimeEstimatorTest, BasicEstimation_Variable) {
+ SimulateFrameWithOneCompositorTask(5);
+ SimulateFrameWithOneCompositorTask(6);
+ SimulateFrameWithOneCompositorTask(7);
+ SimulateFrameWithOneCompositorTask(7);
+ SimulateFrameWithOneCompositorTask(7);
+ SimulateFrameWithOneCompositorTask(8);
+
+ // We expect it to return the median.
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(9),
+ estimator_->GetExpectedIdleDuration(frame_length_));
+}
+
+TEST_F(IdleTimeEstimatorTest, NoIdleTime) {
+ SimulateFrameWithOneCompositorTask(100);
+ SimulateFrameWithOneCompositorTask(100);
+
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(0),
+ estimator_->GetExpectedIdleDuration(frame_length_));
+}
+
+TEST_F(IdleTimeEstimatorTest, Clear) {
+ SimulateFrameWithOneCompositorTask(5);
+ SimulateFrameWithOneCompositorTask(5);
+
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(11),
+ estimator_->GetExpectedIdleDuration(frame_length_));
+ estimator_->Clear();
+
+ EXPECT_EQ(frame_length_, estimator_->GetExpectedIdleDuration(frame_length_));
+}
+
+TEST_F(IdleTimeEstimatorTest, Estimation_MultipleTasks) {
+ SimulateFrameWithTwoCompositorTasks(1, 4);
+ SimulateFrameWithTwoCompositorTasks(1, 4);
+
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(11),
+ estimator_->GetExpectedIdleDuration(frame_length_));
+}
+
+TEST_F(IdleTimeEstimatorTest, IgnoresNestedTasks) {
+ SimulateFrameWithOneCompositorTask(5);
+ SimulateFrameWithOneCompositorTask(5);
+
+ base::PendingTask task(FROM_HERE, base::Closure());
+ estimator_->WillProcessTask(task);
+ SimulateFrameWithTwoCompositorTasks(4, 4);
+ SimulateFrameWithTwoCompositorTasks(4, 4);
+ SimulateFrameWithTwoCompositorTasks(4, 4);
+ SimulateFrameWithTwoCompositorTasks(4, 4);
+ estimator_->DidCommitFrameToCompositor();
+ estimator_->DidProcessTask(task);
+
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(11),
+ estimator_->GetExpectedIdleDuration(frame_length_));
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/renderer/main_thread_task_queue.cc b/chromium/third_party/blink/renderer/platform/scheduler/renderer/main_thread_task_queue.cc
new file mode 100644
index 00000000000..5aa213126a9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/renderer/main_thread_task_queue.cc
@@ -0,0 +1,177 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/renderer/main_thread_task_queue.h"
+
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
+
+namespace blink {
+namespace scheduler {
+
+// static
+const char* MainThreadTaskQueue::NameForQueueType(
+ MainThreadTaskQueue::QueueType queue_type) {
+ switch (queue_type) {
+ case MainThreadTaskQueue::QueueType::kControl:
+ return "control_tq";
+ case MainThreadTaskQueue::QueueType::kDefault:
+ return "default_tq";
+ case MainThreadTaskQueue::QueueType::kUnthrottled:
+ return "unthrottled_tq";
+ case MainThreadTaskQueue::QueueType::kFrameLoading:
+ return "frame_loading_tq";
+ case MainThreadTaskQueue::QueueType::kFrameThrottleable:
+ return "frame_throttleable_tq";
+ case MainThreadTaskQueue::QueueType::kFrameDeferrable:
+ return "frame_deferrable_tq";
+ case MainThreadTaskQueue::QueueType::kFramePausable:
+ return "frame_pausable_tq";
+ case MainThreadTaskQueue::QueueType::kFrameUnpausable:
+ return "frame_unpausable_tq";
+ case MainThreadTaskQueue::QueueType::kCompositor:
+ return "compositor_tq";
+ case MainThreadTaskQueue::QueueType::kIdle:
+ return "idle_tq";
+ case MainThreadTaskQueue::QueueType::kTest:
+ return "test_tq";
+ case MainThreadTaskQueue::QueueType::kFrameLoadingControl:
+ return "frame_loading_control_tq";
+ case MainThreadTaskQueue::QueueType::kV8:
+ return "v8_tq";
+ case MainThreadTaskQueue::QueueType::kIPC:
+ return "ipc_tq";
+ case MainThreadTaskQueue::QueueType::kInput:
+ return "input_tq";
+ case MainThreadTaskQueue::QueueType::kDetached:
+ return "detached_tq";
+ case MainThreadTaskQueue::QueueType::kOther:
+ return "other_tq";
+ case MainThreadTaskQueue::QueueType::kCount:
+ NOTREACHED();
+ return nullptr;
+ }
+ NOTREACHED();
+ return nullptr;
+}
+
+MainThreadTaskQueue::QueueClass MainThreadTaskQueue::QueueClassForQueueType(
+ QueueType type) {
+ switch (type) {
+ case QueueType::kControl:
+ case QueueType::kDefault:
+ case QueueType::kIdle:
+ case QueueType::kTest:
+ case QueueType::kV8:
+ case QueueType::kIPC:
+ return QueueClass::kNone;
+ case QueueType::kFrameLoading:
+ case QueueType::kFrameLoadingControl:
+ return QueueClass::kLoading;
+ case QueueType::kUnthrottled:
+ case QueueType::kFrameThrottleable:
+ case QueueType::kFrameDeferrable:
+ case QueueType::kFramePausable:
+ case QueueType::kFrameUnpausable:
+ return QueueClass::kTimer;
+ case QueueType::kCompositor:
+ case QueueType::kInput:
+ return QueueClass::kCompositor;
+ case QueueType::kDetached:
+ case QueueType::kOther:
+ case QueueType::kCount:
+ DCHECK(false);
+ return QueueClass::kCount;
+ }
+ NOTREACHED();
+ return QueueClass::kNone;
+}
+
+MainThreadTaskQueue::MainThreadTaskQueue(
+ std::unique_ptr<internal::TaskQueueImpl> impl,
+ const TaskQueue::Spec& spec,
+ const QueueCreationParams& params,
+ MainThreadSchedulerImpl* main_thread_scheduler)
+ : TaskQueue(std::move(impl), spec),
+ queue_type_(params.queue_type),
+ queue_class_(QueueClassForQueueType(params.queue_type)),
+ can_be_blocked_(params.can_be_blocked),
+ can_be_throttled_(params.can_be_throttled),
+ can_be_paused_(params.can_be_paused),
+ can_be_stopped_(params.can_be_stopped),
+ freeze_when_keep_active_(params.freeze_when_keep_active),
+ used_for_important_tasks_(params.used_for_important_tasks),
+ main_thread_scheduler_(main_thread_scheduler),
+ frame_scheduler_(nullptr) {
+ if (GetTaskQueueImpl()) {
+ // TaskQueueImpl may be null for tests.
+ // TODO(scheduler-dev): Consider mapping directly to
+ // MainThreadSchedulerImpl::OnTaskStarted/Completed. At the moment this
+ // is not possible due to task queue being created inside
+ // MainThreadScheduler's constructor.
+ GetTaskQueueImpl()->SetOnTaskStartedHandler(base::BindRepeating(
+ &MainThreadTaskQueue::OnTaskStarted, base::Unretained(this)));
+ GetTaskQueueImpl()->SetOnTaskCompletedHandler(base::BindRepeating(
+ &MainThreadTaskQueue::OnTaskCompleted, base::Unretained(this)));
+ }
+}
+
+MainThreadTaskQueue::~MainThreadTaskQueue() = default;
+
+void MainThreadTaskQueue::OnTaskStarted(const TaskQueue::Task& task,
+ base::TimeTicks start) {
+ if (main_thread_scheduler_)
+ main_thread_scheduler_->OnTaskStarted(this, task, start);
+}
+
+void MainThreadTaskQueue::OnTaskCompleted(
+ const TaskQueue::Task& task,
+ base::TimeTicks start,
+ base::TimeTicks end,
+ base::Optional<base::TimeDelta> thread_time) {
+ if (main_thread_scheduler_) {
+ main_thread_scheduler_->OnTaskCompleted(this, task, start, end,
+ thread_time);
+ }
+}
+
+void MainThreadTaskQueue::DetachFromMainThreadScheduler() {
+ // Frame has already been detached.
+ if (!main_thread_scheduler_)
+ return;
+
+ if (GetTaskQueueImpl()) {
+ GetTaskQueueImpl()->SetOnTaskStartedHandler(
+ base::BindRepeating(&MainThreadSchedulerImpl::OnTaskStarted,
+ main_thread_scheduler_->GetWeakPtr(), nullptr));
+ GetTaskQueueImpl()->SetOnTaskCompletedHandler(
+ base::BindRepeating(&MainThreadSchedulerImpl::OnTaskCompleted,
+ main_thread_scheduler_->GetWeakPtr(), nullptr));
+ }
+
+ ClearReferencesToSchedulers();
+}
+
+void MainThreadTaskQueue::ShutdownTaskQueue() {
+ ClearReferencesToSchedulers();
+ TaskQueue::ShutdownTaskQueue();
+}
+
+void MainThreadTaskQueue::ClearReferencesToSchedulers() {
+ if (main_thread_scheduler_)
+ main_thread_scheduler_->OnShutdownTaskQueue(this);
+ main_thread_scheduler_ = nullptr;
+ frame_scheduler_ = nullptr;
+}
+
+FrameScheduler* MainThreadTaskQueue::GetFrameScheduler() const {
+ return frame_scheduler_;
+}
+
+void MainThreadTaskQueue::SetFrameScheduler(FrameScheduler* frame) {
+ frame_scheduler_ = frame;
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/renderer/main_thread_task_queue.h b/chromium/third_party/blink/renderer/platform/scheduler/renderer/main_thread_task_queue.h
new file mode 100644
index 00000000000..6563c32549b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/renderer/main_thread_task_queue.h
@@ -0,0 +1,212 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_MAIN_THREAD_TASK_QUEUE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_MAIN_THREAD_TASK_QUEUE_H_
+
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl.h"
+
+namespace blink {
+
+class FrameScheduler;
+
+namespace scheduler {
+
+class MainThreadSchedulerImpl;
+
+class PLATFORM_EXPORT MainThreadTaskQueue : public TaskQueue {
+ public:
+ enum class QueueType {
+ // Keep MainThreadTaskQueue::NameForQueueType in sync.
+ // This enum is used for a histogram and it should not be re-numbered.
+ // TODO(altimin): Clean up obsolete names and use a new histogram when
+ // the situation settles.
+ kControl = 0,
+ kDefault = 1,
+
+ // 2 was used for default loading task runner but this was deprecated.
+
+ // 3 was used for default timer task runner but this was deprecated.
+
+ kUnthrottled = 4,
+ kFrameLoading = 5,
+ // 6 : kFrameThrottleable, replaced with FRAME_THROTTLEABLE.
+ // 7 : kFramePausable, replaced with kFramePausable
+ kCompositor = 8,
+ kIdle = 9,
+ kTest = 10,
+ kFrameLoadingControl = 11,
+ kFrameThrottleable = 12,
+ kFrameDeferrable = 13,
+ kFramePausable = 14,
+ kFrameUnpausable = 15,
+ kV8 = 16,
+ kIPC = 17,
+ kInput = 18,
+
+ // Detached is used in histograms for tasks which are run after frame
+ // is detached and task queue is gracefully shutdown.
+ // TODO(altimin): Move to the top when histogram is renumbered.
+ kDetached = 19,
+
+ // Used to group multiple types when calculating Expected Queueing Time.
+ kOther = 20,
+ kCount = 21
+ };
+
+ // Returns name of the given queue type. Returned string has application
+ // lifetime.
+ static const char* NameForQueueType(QueueType queue_type);
+
+ // High-level category used by MainThreadScheduler to make scheduling
+ // decisions.
+ enum class QueueClass {
+ kNone = 0,
+ kLoading = 1,
+ kTimer = 2,
+ kCompositor = 4,
+
+ kCount = 5,
+ };
+
+ static QueueClass QueueClassForQueueType(QueueType type);
+
+ struct QueueCreationParams {
+ explicit QueueCreationParams(QueueType queue_type)
+ : queue_type(queue_type),
+ spec(NameForQueueType(queue_type)),
+ can_be_blocked(false),
+ can_be_throttled(false),
+ can_be_paused(false),
+ can_be_stopped(false),
+ freeze_when_keep_active(false),
+ used_for_important_tasks(false) {}
+
+ QueueCreationParams SetCanBeDeferred(bool value) {
+ can_be_blocked = value;
+ return *this;
+ }
+
+ QueueCreationParams SetCanBeThrottled(bool value) {
+ can_be_throttled = value;
+ return *this;
+ }
+
+ QueueCreationParams SetCanBePaused(bool value) {
+ can_be_paused = value;
+ return *this;
+ }
+
+ QueueCreationParams SetCanBeStopped(bool value) {
+ can_be_stopped = value;
+ return *this;
+ }
+
+ QueueCreationParams SetFreezeWhenKeepActive(bool value) {
+ freeze_when_keep_active = value;
+ return *this;
+ }
+
+ QueueCreationParams SetUsedForImportantTasks(bool value) {
+ used_for_important_tasks = value;
+ return *this;
+ }
+
+ // Forwarded calls to |spec|.
+
+ QueueCreationParams SetShouldMonitorQuiescence(bool should_monitor) {
+ spec = spec.SetShouldMonitorQuiescence(should_monitor);
+ return *this;
+ }
+
+ QueueCreationParams SetShouldNotifyObservers(bool run_observers) {
+ spec = spec.SetShouldNotifyObservers(run_observers);
+ return *this;
+ }
+
+ QueueCreationParams SetTimeDomain(TimeDomain* domain) {
+ spec = spec.SetTimeDomain(domain);
+ return *this;
+ }
+
+ QueueType queue_type;
+ TaskQueue::Spec spec;
+ FrameScheduler* frame_;
+ bool can_be_blocked;
+ bool can_be_throttled;
+ bool can_be_paused;
+ bool can_be_stopped;
+ bool freeze_when_keep_active;
+ bool used_for_important_tasks;
+ };
+
+ ~MainThreadTaskQueue() override;
+
+ QueueType queue_type() const { return queue_type_; }
+
+ QueueClass queue_class() const { return queue_class_; }
+
+ bool CanBeDeferred() const { return can_be_blocked_; }
+
+ bool CanBeThrottled() const { return can_be_throttled_; }
+
+ bool CanBePaused() const { return can_be_paused_; }
+
+ bool CanBeStopped() const { return can_be_stopped_; }
+
+ bool FreezeWhenKeepActive() const { return freeze_when_keep_active_; }
+
+ bool UsedForImportantTasks() const { return used_for_important_tasks_; }
+
+ void OnTaskStarted(const TaskQueue::Task& task, base::TimeTicks start);
+
+ void OnTaskCompleted(const TaskQueue::Task& task,
+ base::TimeTicks start,
+ base::TimeTicks end,
+ base::Optional<base::TimeDelta> thread_time);
+
+ void DetachFromMainThreadScheduler();
+
+ // Override base method to notify MainThreadScheduler about shutdown queue.
+ void ShutdownTaskQueue() override;
+
+ FrameScheduler* GetFrameScheduler() const;
+ void SetFrameScheduler(FrameScheduler* frame);
+
+ protected:
+ MainThreadTaskQueue(std::unique_ptr<internal::TaskQueueImpl> impl,
+ const Spec& spec,
+ const QueueCreationParams& params,
+ MainThreadSchedulerImpl* main_thread_scheduler);
+
+ private:
+ friend class TaskQueueManager;
+
+ // Clear references to main thread scheduler and frame scheduler and dispatch
+ // appropriate notifications. This is the common part of ShutdownTaskQueue and
+ // DetachFromMainThreadScheduler.
+ void ClearReferencesToSchedulers();
+
+ QueueType queue_type_;
+ QueueClass queue_class_;
+ const bool can_be_blocked_;
+ const bool can_be_throttled_;
+ const bool can_be_paused_;
+ const bool can_be_stopped_;
+ const bool freeze_when_keep_active_;
+ const bool used_for_important_tasks_;
+
+ // Needed to notify renderer scheduler about completed tasks.
+ MainThreadSchedulerImpl* main_thread_scheduler_; // NOT OWNED
+
+ FrameScheduler* frame_scheduler_; // NOT OWNED
+
+ DISALLOW_COPY_AND_ASSIGN(MainThreadTaskQueue);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_MAIN_THREAD_TASK_QUEUE_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/renderer/queueing_time_estimator.cc b/chromium/third_party/blink/renderer/platform/scheduler/renderer/queueing_time_estimator.cc
new file mode 100644
index 00000000000..75a6ed7b9b0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/renderer/queueing_time_estimator.cc
@@ -0,0 +1,411 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/renderer/queueing_time_estimator.h"
+
+#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
+
+#include <algorithm>
+#include <map>
+
+namespace blink {
+namespace scheduler {
+
+namespace {
+
+#define FRAME_STATUS_PREFIX \
+ "RendererScheduler.ExpectedQueueingTimeByFrameStatus2."
+#define TASK_QUEUE_PREFIX "RendererScheduler.ExpectedQueueingTimeByTaskQueue2."
+
+// On Windows, when a computer sleeps, we may end up getting extremely long
+// tasks or idling. We'll ignore tasks longer than |kInvalidPeriodThreshold|.
+constexpr base::TimeDelta kInvalidPeriodThreshold =
+ base::TimeDelta::FromSecondsD(30);
+
+// This method computes the expected queueing time of a randomly distributed
+// task R within a window containing a single task T. Let T' be the time range
+// for which T overlaps the window. We first compute the probability that R will
+// start within T'. We then compute the expected queueing duration if R does
+// start within this range. Since the start time of R is uniformly distributed
+// within the window, this is equal to the average of the queueing times if R
+// started at the beginning or end of T'. The expected queueing time of T is the
+// probability that R will start within T', multiplied by the expected queueing
+// duration if R does fall in this range.
+base::TimeDelta ExpectedQueueingTimeFromTask(base::TimeTicks task_start,
+ base::TimeTicks task_end,
+ base::TimeTicks step_start,
+ base::TimeTicks step_end) {
+ DCHECK_LE(task_start, task_end);
+ DCHECK_LE(task_start, step_end);
+ DCHECK_LT(step_start, step_end);
+ // Because we skip steps when the renderer is backgrounded, we may have gone
+ // into the future, and in that case we ignore this task completely.
+ if (task_end < step_start)
+ return base::TimeDelta();
+
+ base::TimeTicks task_in_step_start_time = std::max(task_start, step_start);
+ base::TimeTicks task_in_step_end_time = std::min(task_end, step_end);
+ DCHECK_LE(task_in_step_end_time, task_in_step_end_time);
+
+ double probability_of_this_task =
+ static_cast<double>(
+ (task_in_step_end_time - task_in_step_start_time).InMicroseconds()) /
+ (step_end - step_start).InMicroseconds();
+
+ base::TimeDelta expected_queueing_duration_within_task =
+ ((task_end - task_in_step_start_time) +
+ (task_end - task_in_step_end_time)) /
+ 2;
+
+ return base::TimeDelta::FromMillisecondsD(
+ probability_of_this_task *
+ expected_queueing_duration_within_task.InMillisecondsF());
+}
+
+} // namespace
+
+QueueingTimeEstimator::QueueingTimeEstimator(
+ QueueingTimeEstimator::Client* client,
+ base::TimeDelta window_duration,
+ int steps_per_window)
+ : client_(client), state_(steps_per_window) {
+ DCHECK_GE(steps_per_window, 1);
+ state_.window_step_width = window_duration / steps_per_window;
+}
+
+QueueingTimeEstimator::QueueingTimeEstimator(const State& state)
+ : client_(nullptr), state_(state) {}
+
+void QueueingTimeEstimator::OnTopLevelTaskStarted(
+ base::TimeTicks task_start_time,
+ MainThreadTaskQueue* queue) {
+ state_.OnTopLevelTaskStarted(client_, task_start_time, queue);
+}
+
+void QueueingTimeEstimator::OnTopLevelTaskCompleted(
+ base::TimeTicks task_end_time) {
+ state_.OnTopLevelTaskCompleted(client_, task_end_time);
+}
+
+void QueueingTimeEstimator::OnBeginNestedRunLoop() {
+ state_.OnBeginNestedRunLoop();
+}
+
+void QueueingTimeEstimator::OnRendererStateChanged(
+ bool backgrounded,
+ base::TimeTicks transition_time) {
+ state_.OnRendererStateChanged(client_, backgrounded, transition_time);
+}
+
+QueueingTimeEstimator::Calculator::Calculator(int steps_per_window)
+ : steps_per_window_(steps_per_window),
+ step_queueing_times_(steps_per_window) {}
+
+// static
+const char* QueueingTimeEstimator::Calculator::GetReportingMessageFromQueueType(
+ MainThreadTaskQueue::QueueType queue_type) {
+ switch (queue_type) {
+ case MainThreadTaskQueue::QueueType::kDefault:
+ return TASK_QUEUE_PREFIX "Default";
+ case MainThreadTaskQueue::QueueType::kUnthrottled:
+ return TASK_QUEUE_PREFIX "Unthrottled";
+ case MainThreadTaskQueue::QueueType::kFrameLoading:
+ return TASK_QUEUE_PREFIX "FrameLoading";
+ case MainThreadTaskQueue::QueueType::kCompositor:
+ return TASK_QUEUE_PREFIX "Compositor";
+ case MainThreadTaskQueue::QueueType::kFrameThrottleable:
+ return TASK_QUEUE_PREFIX "FrameThrottleable";
+ case MainThreadTaskQueue::QueueType::kFramePausable:
+ return TASK_QUEUE_PREFIX "FramePausable";
+ case MainThreadTaskQueue::QueueType::kControl:
+ case MainThreadTaskQueue::QueueType::kIdle:
+ case MainThreadTaskQueue::QueueType::kTest:
+ case MainThreadTaskQueue::QueueType::kFrameLoadingControl:
+ case MainThreadTaskQueue::QueueType::kFrameDeferrable:
+ case MainThreadTaskQueue::QueueType::kFrameUnpausable:
+ case MainThreadTaskQueue::QueueType::kV8:
+ case MainThreadTaskQueue::QueueType::kOther:
+ case MainThreadTaskQueue::QueueType::kCount:
+ // Using default here as well because there are some values less than COUNT
+ // that have been removed and do not correspond to any QueueType.
+ default:
+ return TASK_QUEUE_PREFIX "Other";
+ }
+}
+
+// static
+const char*
+QueueingTimeEstimator::Calculator::GetReportingMessageFromFrameStatus(
+ FrameStatus frame_status) {
+ switch (frame_status) {
+ case FrameStatus::kMainFrameVisible:
+ case FrameStatus::kMainFrameVisibleService:
+ return FRAME_STATUS_PREFIX "MainFrameVisible";
+ case FrameStatus::kMainFrameHidden:
+ case FrameStatus::kMainFrameHiddenService:
+ return FRAME_STATUS_PREFIX "MainFrameHidden";
+ case FrameStatus::kMainFrameBackground:
+ case FrameStatus::kMainFrameBackgroundExemptSelf:
+ case FrameStatus::kMainFrameBackgroundExemptOther:
+ return FRAME_STATUS_PREFIX "MainFrameBackground";
+ case FrameStatus::kSameOriginVisible:
+ case FrameStatus::kSameOriginVisibleService:
+ return FRAME_STATUS_PREFIX "SameOriginVisible";
+ case FrameStatus::kSameOriginHidden:
+ case FrameStatus::kSameOriginHiddenService:
+ return FRAME_STATUS_PREFIX "SameOriginHidden";
+ case FrameStatus::kSameOriginBackground:
+ case FrameStatus::kSameOriginBackgroundExemptSelf:
+ case FrameStatus::kSameOriginBackgroundExemptOther:
+ return FRAME_STATUS_PREFIX "SameOriginBackground";
+ case FrameStatus::kCrossOriginVisible:
+ case FrameStatus::kCrossOriginVisibleService:
+ return FRAME_STATUS_PREFIX "CrossOriginVisible";
+ case FrameStatus::kCrossOriginHidden:
+ case FrameStatus::kCrossOriginHiddenService:
+ return FRAME_STATUS_PREFIX "CrossOriginHidden";
+ case FrameStatus::kCrossOriginBackground:
+ case FrameStatus::kCrossOriginBackgroundExemptSelf:
+ case FrameStatus::kCrossOriginBackgroundExemptOther:
+ return FRAME_STATUS_PREFIX "CrossOriginBackground";
+ case FrameStatus::kNone:
+ case FrameStatus::kDetached:
+ return FRAME_STATUS_PREFIX "Other";
+ case FrameStatus::kCount:
+ NOTREACHED();
+ return "";
+ }
+ NOTREACHED();
+ return "";
+}
+
+void QueueingTimeEstimator::Calculator::UpdateStatusFromTaskQueue(
+ MainThreadTaskQueue* queue) {
+ current_queue_type_ =
+ queue ? queue->queue_type() : MainThreadTaskQueue::QueueType::kOther;
+ FrameScheduler* scheduler = queue ? queue->GetFrameScheduler() : nullptr;
+ current_frame_status_ =
+ scheduler ? GetFrameStatus(scheduler) : FrameStatus::kNone;
+}
+
+void QueueingTimeEstimator::Calculator::AddQueueingTime(
+ base::TimeDelta queueing_time) {
+ step_expected_queueing_time_ += queueing_time;
+ eqt_by_queue_type_[static_cast<int>(current_queue_type_)] += queueing_time;
+ eqt_by_frame_status_[static_cast<int>(current_frame_status_)] +=
+ queueing_time;
+}
+
+void QueueingTimeEstimator::Calculator::EndStep(
+ QueueingTimeEstimator::Client* client) {
+ step_queueing_times_.Add(step_expected_queueing_time_);
+
+ // RendererScheduler reports the queueing time once per disjoint window.
+ // |stepEQT|stepEQT|stepEQT|stepEQT|stepEQT|stepEQT|
+ // Report: |-------window EQT------|
+ // Discard: |-------window EQT------|
+ // Discard: |-------window EQT------|
+ // Report: |-------window EQT------|
+ client->OnQueueingTimeForWindowEstimated(step_queueing_times_.GetAverage(),
+ step_queueing_times_.IndexIsZero());
+ ResetStep();
+ if (!step_queueing_times_.IndexIsZero())
+ return;
+
+ std::map<const char*, base::TimeDelta> delta_by_message;
+ for (int i = 0; i < static_cast<int>(MainThreadTaskQueue::QueueType::kCount);
+ ++i) {
+ delta_by_message[GetReportingMessageFromQueueType(
+ static_cast<MainThreadTaskQueue::QueueType>(i))] +=
+ eqt_by_queue_type_[i];
+ }
+ for (int i = 0; i < static_cast<int>(FrameStatus::kCount); ++i) {
+ delta_by_message[GetReportingMessageFromFrameStatus(
+ static_cast<FrameStatus>(i))] += eqt_by_frame_status_[i];
+ }
+ for (auto it : delta_by_message) {
+ client->OnReportFineGrainedExpectedQueueingTime(
+ it.first, it.second / steps_per_window_);
+ }
+ std::fill(eqt_by_queue_type_.begin(), eqt_by_queue_type_.end(),
+ base::TimeDelta());
+ std::fill(eqt_by_frame_status_.begin(), eqt_by_frame_status_.end(),
+ base::TimeDelta());
+}
+
+void QueueingTimeEstimator::Calculator::ResetStep() {
+ step_expected_queueing_time_ = base::TimeDelta();
+}
+
+QueueingTimeEstimator::State::State(int steps_per_window)
+ : calculator_(steps_per_window) {}
+
+void QueueingTimeEstimator::State::OnTopLevelTaskStarted(
+ QueueingTimeEstimator::Client* client,
+ base::TimeTicks task_start_time,
+ MainThreadTaskQueue* queue) {
+ AdvanceTime(client, task_start_time);
+ current_task_start_time = task_start_time;
+ processing_task = true;
+ calculator_.UpdateStatusFromTaskQueue(queue);
+}
+
+void QueueingTimeEstimator::State::OnTopLevelTaskCompleted(
+ QueueingTimeEstimator::Client* client,
+ base::TimeTicks task_end_time) {
+ DCHECK(processing_task);
+ AdvanceTime(client, task_end_time);
+ processing_task = false;
+ current_task_start_time = base::TimeTicks();
+ in_nested_message_loop_ = false;
+}
+
+void QueueingTimeEstimator::State::OnBeginNestedRunLoop() {
+ in_nested_message_loop_ = true;
+}
+
+void QueueingTimeEstimator::State::OnRendererStateChanged(
+ QueueingTimeEstimator::Client* client,
+ bool backgrounded,
+ base::TimeTicks transition_time) {
+ DCHECK_NE(backgrounded, renderer_backgrounded);
+ if (!processing_task)
+ AdvanceTime(client, transition_time);
+ renderer_backgrounded = backgrounded;
+}
+
+void QueueingTimeEstimator::State::AdvanceTime(
+ QueueingTimeEstimator::Client* client,
+ base::TimeTicks current_time) {
+ if (step_start_time.is_null()) {
+ // Ignore any time before the first task.
+ if (!processing_task)
+ return;
+
+ step_start_time = current_task_start_time;
+ }
+ base::TimeTicks reference_time =
+ processing_task ? current_task_start_time : step_start_time;
+ if (in_nested_message_loop_ || renderer_backgrounded ||
+ current_time - reference_time > kInvalidPeriodThreshold) {
+ // Skip steps when the renderer was backgrounded, when we are at a nested
+ // message loop, when a task took too long, or when we remained idle for
+ // too long. May cause |step_start_time| to go slightly into the future.
+ // TODO(npm): crbug.com/776013. Base skipping long tasks/idling on a signal
+ // that we've been suspended.
+ step_start_time =
+ current_time.SnappedToNextTick(step_start_time, window_step_width);
+ calculator_.ResetStep();
+ return;
+ }
+ while (TimePastStepEnd(current_time)) {
+ if (processing_task) {
+ // Include the current task in this window.
+ calculator_.AddQueueingTime(ExpectedQueueingTimeFromTask(
+ current_task_start_time, current_time, step_start_time,
+ step_start_time + window_step_width));
+ }
+ calculator_.EndStep(client);
+ step_start_time += window_step_width;
+ }
+ if (processing_task) {
+ calculator_.AddQueueingTime(ExpectedQueueingTimeFromTask(
+ current_task_start_time, current_time, step_start_time,
+ step_start_time + window_step_width));
+ }
+}
+
+bool QueueingTimeEstimator::State::TimePastStepEnd(base::TimeTicks time) {
+ return time >= step_start_time + window_step_width;
+}
+
+QueueingTimeEstimator::RunningAverage::RunningAverage(int size) {
+ circular_buffer_.resize(size);
+ index_ = 0;
+}
+
+int QueueingTimeEstimator::RunningAverage::GetStepsPerWindow() const {
+ return circular_buffer_.size();
+}
+
+void QueueingTimeEstimator::RunningAverage::Add(base::TimeDelta bin_value) {
+ running_sum_ -= circular_buffer_[index_];
+ circular_buffer_[index_] = bin_value;
+ running_sum_ += bin_value;
+ index_ = (index_ + 1) % circular_buffer_.size();
+}
+
+base::TimeDelta QueueingTimeEstimator::RunningAverage::GetAverage() const {
+ return running_sum_ / circular_buffer_.size();
+}
+
+bool QueueingTimeEstimator::RunningAverage::IndexIsZero() const {
+ return index_ == 0;
+}
+
+// Keeps track of the queueing time.
+class RecordQueueingTimeClient : public QueueingTimeEstimator::Client {
+ public:
+ // QueueingTimeEstimator::Client implementation:
+ void OnQueueingTimeForWindowEstimated(base::TimeDelta queueing_time,
+ bool is_disjoint_window) override {
+ queueing_time_ = queueing_time;
+ }
+
+ void OnReportFineGrainedExpectedQueueingTime(
+ const char* split_description,
+ base::TimeDelta queueing_time) override {}
+
+ base::TimeDelta queueing_time() { return queueing_time_; }
+
+ RecordQueueingTimeClient() = default;
+ ~RecordQueueingTimeClient() override = default;
+
+ private:
+ base::TimeDelta queueing_time_;
+ DISALLOW_COPY_AND_ASSIGN(RecordQueueingTimeClient);
+};
+
+base::TimeDelta QueueingTimeEstimator::EstimateQueueingTimeIncludingCurrentTask(
+ base::TimeTicks now) const {
+ RecordQueueingTimeClient record_queueing_time_client;
+
+ // Make a copy of this QueueingTimeEstimator. We'll use it to evaluate the
+ // estimated input latency, assuming that any active task ends now.
+ QueueingTimeEstimator::State temporary_queueing_time_estimator_state(state_);
+
+ // If there's a task in progress, pretend it ends now, and include it in the
+ // computation. If there's no task in progress, add an empty task to flush any
+ // stale windows.
+ if (temporary_queueing_time_estimator_state.current_task_start_time
+ .is_null()) {
+ temporary_queueing_time_estimator_state.OnTopLevelTaskStarted(
+ &record_queueing_time_client, now, nullptr);
+ }
+ temporary_queueing_time_estimator_state.OnTopLevelTaskCompleted(
+ &record_queueing_time_client, now);
+
+ // Report the max of the queueing time for the last window, or the on-going
+ // window (tmp window in chart) which includes the current task.
+ //
+ // Estimate
+ // |
+ // v
+ // Actual Task |-------------------------...
+ // Assumed Task |----------------|
+ // Time |---o---o---o---o---o---o-------->
+ // 0 1 2 3 4 5 6
+ // | s | s | s | s | s | s |
+ // |----last window----|
+ // |----tmp window-----|
+ base::TimeDelta last_window_queueing_time =
+ record_queueing_time_client.queueing_time();
+ temporary_queueing_time_estimator_state.calculator_.EndStep(
+ &record_queueing_time_client);
+ return std::max(last_window_queueing_time,
+ record_queueing_time_client.queueing_time());
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/renderer/queueing_time_estimator.h b/chromium/third_party/blink/renderer/platform/scheduler/renderer/queueing_time_estimator.h
new file mode 100644
index 00000000000..b31312639d2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/renderer/queueing_time_estimator.h
@@ -0,0 +1,167 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_QUEUEING_TIME_ESTIMATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_QUEUEING_TIME_ESTIMATOR_H_
+
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "third_party/blink/public/common/page/launching_process_state.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/scheduler/renderer/main_thread_task_queue.h"
+#include "third_party/blink/renderer/platform/scheduler/renderer/renderer_metrics_helper.h"
+
+#include <array>
+#include <vector>
+
+namespace blink {
+namespace scheduler {
+
+// Records the expected queueing time for a high priority task occurring
+// randomly during each interval of length equal to window's duration.
+class PLATFORM_EXPORT QueueingTimeEstimator {
+ public:
+ class PLATFORM_EXPORT Client {
+ public:
+ virtual void OnQueueingTimeForWindowEstimated(base::TimeDelta queueing_time,
+ bool is_disjoint_window) = 0;
+ virtual void OnReportFineGrainedExpectedQueueingTime(
+ const char* split_description,
+ base::TimeDelta queueing_time) = 0;
+ Client() = default;
+ virtual ~Client() = default;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Client);
+ };
+
+ class RunningAverage {
+ public:
+ explicit RunningAverage(int steps_per_window);
+ int GetStepsPerWindow() const;
+ void Add(base::TimeDelta bin_value);
+ base::TimeDelta GetAverage() const;
+ bool IndexIsZero() const;
+
+ private:
+ size_t index_;
+ std::vector<base::TimeDelta> circular_buffer_;
+ base::TimeDelta running_sum_;
+ };
+
+ class PLATFORM_EXPORT Calculator {
+ public:
+ explicit Calculator(int steps_per_window);
+ static const char* GetReportingMessageFromQueueType(
+ MainThreadTaskQueue::QueueType queue_type);
+ static const char* GetReportingMessageFromFrameStatus(
+ FrameStatus frame_status);
+
+ void UpdateStatusFromTaskQueue(MainThreadTaskQueue* queue);
+ void AddQueueingTime(base::TimeDelta queuing_time);
+ void EndStep(Client* client);
+ void ResetStep();
+
+ private:
+ // Variables to compute the total Expected Queueing Time.
+ // |steps_per_window_| is the ratio of window duration to the sliding
+ // window's step width. It is an integer since the window must be a integer
+ // multiple of the step's width. This parameter is used for deciding the
+ // sliding window's step width, and the number of bins of the circular
+ // buffer.
+ const int steps_per_window_;
+
+ // |step_expected_queueing_time_| is the expected queuing time of a
+ // smaller window of a step's width. By combining these step EQTs through a
+ // running average, we can get window EQTs of a bigger window.
+ //
+ // ^ Instantaneous queuing time
+ // |
+ // |
+ // | |\ .
+ // | | \ |\ |\ .
+ // | | \ | \ |\ | \ .
+ // | | \ |\ | \ | \ | \ .
+ // | | \ | \ | \ | \ | \ .
+ // ------------------------------------------------> Time
+ //
+ // |stepEQT|stepEQT|stepEQT|stepEQT|stepEQT|stepEQT|
+ //
+ // |------windowEQT_1------|
+ // |------windowEQT_2------|
+ // |------windowEQT_3------|
+ //
+ // In this case:
+ // |steps_per_window_| = 3, because each window is the length of 3 steps.
+ base::TimeDelta step_expected_queueing_time_;
+ RunningAverage step_queueing_times_;
+
+ // Variables to split Expected Queueing Time by task queue type.
+ std::array<base::TimeDelta,
+ static_cast<int>(MainThreadTaskQueue::QueueType::kCount)>
+ eqt_by_queue_type_;
+ MainThreadTaskQueue::QueueType current_queue_type_ =
+ MainThreadTaskQueue::QueueType::kOther;
+
+ // Variables to split Expected Queueing Time by frame type.
+ std::array<base::TimeDelta, static_cast<int>(FrameStatus::kCount)>
+ eqt_by_frame_status_;
+ FrameStatus current_frame_status_ = FrameStatus::kNone;
+ };
+
+ class State {
+ public:
+ explicit State(int steps_per_window);
+ void OnTopLevelTaskStarted(Client* client,
+ base::TimeTicks task_start_time,
+ MainThreadTaskQueue* queue);
+ void OnTopLevelTaskCompleted(Client* client, base::TimeTicks task_end_time);
+ void OnBeginNestedRunLoop();
+ void OnRendererStateChanged(Client* client,
+ bool backgrounded,
+ base::TimeTicks transition_time);
+
+ base::TimeDelta window_step_width;
+ base::TimeTicks step_start_time;
+ base::TimeTicks current_task_start_time;
+ // |renderer_backgrounded| is the renderer's current status.
+ bool renderer_backgrounded = kLaunchingProcessIsBackgrounded;
+ bool processing_task = false;
+ Calculator calculator_;
+
+ private:
+ void AdvanceTime(Client* client, base::TimeTicks current_time);
+ bool TimePastStepEnd(base::TimeTicks task_end_time);
+ bool in_nested_message_loop_ = false;
+ };
+
+ QueueingTimeEstimator(Client* client,
+ base::TimeDelta window_duration,
+ int steps_per_window);
+ explicit QueueingTimeEstimator(const State& state);
+
+ void OnTopLevelTaskStarted(base::TimeTicks task_start_time,
+ MainThreadTaskQueue* queue);
+ void OnTopLevelTaskCompleted(base::TimeTicks task_end_time);
+ void OnBeginNestedRunLoop();
+ void OnRendererStateChanged(bool backgrounded,
+ base::TimeTicks transition_time);
+
+ // Returns all state except for the current |client_|.
+ const State& GetState() const { return state_; }
+
+ base::TimeDelta EstimateQueueingTimeIncludingCurrentTask(
+ base::TimeTicks now) const;
+
+ private:
+ Client* client_; // NOT OWNED.
+ State state_;
+
+ DISALLOW_ASSIGN(QueueingTimeEstimator);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_QUEUEING_TIME_ESTIMATOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/renderer/queueing_time_estimator_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/renderer/queueing_time_estimator_unittest.cc
new file mode 100644
index 00000000000..3e2d5412da0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/renderer/queueing_time_estimator_unittest.cc
@@ -0,0 +1,1230 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/renderer/queueing_time_estimator.h"
+#include "base/logging.h"
+#include "base/metrics/histogram_functions.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/page/launching_process_state.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/test/fake_frame_scheduler.h"
+#include "third_party/blink/renderer/platform/testing/histogram_tester.h"
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace blink {
+namespace scheduler {
+
+using QueueType = MainThreadTaskQueue::QueueType;
+
+namespace {
+
+class TestQueueingTimeEstimatorClient : public QueueingTimeEstimator::Client {
+ public:
+ void OnQueueingTimeForWindowEstimated(base::TimeDelta queueing_time,
+ bool is_disjoint_window) override {
+ expected_queueing_times_.push_back(queueing_time);
+ // Mimic RendererSchedulerImpl::OnQueueingTimeForWindowEstimated.
+ if (is_disjoint_window) {
+ UMA_HISTOGRAM_TIMES("RendererScheduler.ExpectedTaskQueueingDuration",
+ queueing_time);
+ UMA_HISTOGRAM_CUSTOM_COUNTS(
+ "RendererScheduler.ExpectedTaskQueueingDuration3",
+ queueing_time.InMicroseconds(),
+ MainThreadSchedulerImpl::kMinExpectedQueueingTimeBucket,
+ MainThreadSchedulerImpl::kMaxExpectedQueueingTimeBucket,
+ MainThreadSchedulerImpl::kNumberExpectedQueueingTimeBuckets);
+ }
+ }
+ void OnReportFineGrainedExpectedQueueingTime(const char* split_description,
+ base::TimeDelta queueing_time) {
+ if (split_eqts_.find(split_description) == split_eqts_.end())
+ split_eqts_[split_description] = std::vector<base::TimeDelta>();
+ split_eqts_[split_description].push_back(queueing_time);
+ // Mimic MainThreadSchedulerImpl::OnReportFineGrainedExpectedQueueingTime.
+ base::UmaHistogramCustomCounts(
+ split_description, queueing_time.InMicroseconds(),
+ MainThreadSchedulerImpl::kMinExpectedQueueingTimeBucket,
+ MainThreadSchedulerImpl::kMaxExpectedQueueingTimeBucket,
+ MainThreadSchedulerImpl::kNumberExpectedQueueingTimeBuckets);
+ }
+ const std::vector<base::TimeDelta>& expected_queueing_times() {
+ return expected_queueing_times_;
+ }
+ const std::map<const char*, std::vector<base::TimeDelta>>& split_eqts() {
+ return split_eqts_;
+ }
+ const std::vector<base::TimeDelta>& QueueTypeValues(QueueType queue_type) {
+ return split_eqts_[QueueingTimeEstimator::Calculator::
+ GetReportingMessageFromQueueType(queue_type)];
+ }
+ const std::vector<base::TimeDelta>& FrameStatusValues(
+ FrameStatus frame_status) {
+ return split_eqts_[QueueingTimeEstimator::Calculator::
+ GetReportingMessageFromFrameStatus(frame_status)];
+ }
+
+ private:
+ std::vector<base::TimeDelta> expected_queueing_times_;
+ std::map<const char*, std::vector<base::TimeDelta>> split_eqts_;
+};
+
+class QueueingTimeEstimatorForTest : public QueueingTimeEstimator {
+ public:
+ QueueingTimeEstimatorForTest(TestQueueingTimeEstimatorClient* client,
+ base::TimeDelta window_duration,
+ int steps_per_window,
+ base::TimeTicks time)
+ : QueueingTimeEstimator(client, window_duration, steps_per_window) {
+ // If initial state is not foregrounded, foreground.
+ if (kLaunchingProcessIsBackgrounded) {
+ this->OnRendererStateChanged(false, time);
+ }
+ }
+};
+
+struct BucketExpectation {
+ int sample;
+ int count;
+};
+
+} // namespace
+
+class QueueingTimeEstimatorTest : public testing::Test {
+ protected:
+ static std::vector<BucketExpectation> GetFineGrained(
+ const std::vector<BucketExpectation>& expected) {
+ std::vector<BucketExpectation> fine_grained(expected.size());
+ for (size_t i = 0; i < expected.size(); ++i) {
+ fine_grained[i].sample = expected[i].sample * 1000;
+ fine_grained[i].count = expected[i].count;
+ }
+ return fine_grained;
+ }
+
+ void TestHistogram(const std::string& name,
+ int total,
+ const std::vector<BucketExpectation>& expectations) {
+ histogram_tester.ExpectTotalCount(name, total);
+ int sum = 0;
+ for (const auto& expected : expectations) {
+ histogram_tester.ExpectBucketCount(name, expected.sample, expected.count);
+ sum += expected.count;
+ }
+ EXPECT_EQ(total, sum);
+ }
+
+ void TestSplitSumsTotal(base::TimeDelta* expected_sums, int num_windows) {
+ for (int window = 1; window < num_windows; ++window) {
+ base::TimeDelta sum;
+ // Add up the reported split EQTs for that window.
+ for (const auto& entry : client.split_eqts())
+ sum += entry.second[window - 1];
+ // Divide sum by two because we're also adding the split by frame type.
+ sum /= 2.0;
+ // Compare the split sum and the reported EQT for the disjoint window.
+ EXPECT_EQ(expected_sums[window - 1], sum);
+ EXPECT_EQ(expected_sums[window - 1],
+ client.expected_queueing_times()[5 * window - 1]);
+ }
+ }
+
+ HistogramTester histogram_tester;
+ base::TimeTicks time;
+ TestQueueingTimeEstimatorClient client;
+};
+
+// Three tasks of one second each, all within a 5 second window. Expected
+// queueing time is the probability of falling into one of these tasks (3/5),
+// multiplied by the expected queueing time within a task (0.5 seconds). Thus we
+// expect a queueing time of 0.3 seconds.
+TEST_F(QueueingTimeEstimatorTest, AllTasksWithinWindow) {
+ QueueingTimeEstimatorForTest estimator(
+ &client, base::TimeDelta::FromSeconds(5), 1, time);
+ for (int i = 0; i < 3; ++i) {
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ time += base::TimeDelta::FromMilliseconds(1000);
+ estimator.OnTopLevelTaskCompleted(time);
+ }
+
+ // Flush the data by adding a task in the next window.
+ time += base::TimeDelta::FromMilliseconds(5000);
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ time += base::TimeDelta::FromMilliseconds(500);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ EXPECT_THAT(client.expected_queueing_times(),
+ testing::ElementsAre(base::TimeDelta::FromMilliseconds(300)));
+ std::vector<BucketExpectation> expected = {{300, 1}};
+ TestHistogram("RendererScheduler.ExpectedTaskQueueingDuration", 1, expected);
+ std::vector<BucketExpectation> fine_grained = GetFineGrained(expected);
+ TestHistogram("RendererScheduler.ExpectedTaskQueueingDuration3", 1,
+ fine_grained);
+}
+
+// One 20 second long task, starting 3 seconds into the first window.
+// Window 1: Probability of being within task = 2/5. Expected delay within task:
+// avg(20, 18). Total expected queueing time = 7.6s.
+// Window 2: Probability of being within task = 1. Expected delay within task:
+// avg(18, 13). Total expected queueing time = 15.5s.
+// Window 5: Probability of being within task = 3/5. Expected delay within task:
+// avg(3, 0). Total expected queueing time = 0.9s.
+TEST_F(QueueingTimeEstimatorTest, MultiWindowTask) {
+ QueueingTimeEstimatorForTest estimator(
+ &client, base::TimeDelta::FromSeconds(5), 1, time);
+ time += base::TimeDelta::FromMilliseconds(5000);
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ time += base::TimeDelta::FromMilliseconds(3000);
+
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ time += base::TimeDelta::FromMilliseconds(20000);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ // Flush the data by adding a task in the next window.
+ time += base::TimeDelta::FromMilliseconds(5000);
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ time += base::TimeDelta::FromMilliseconds(500);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ EXPECT_THAT(client.expected_queueing_times(),
+ testing::ElementsAre(base::TimeDelta::FromMilliseconds(7600),
+ base::TimeDelta::FromMilliseconds(15500),
+ base::TimeDelta::FromMilliseconds(10500),
+ base::TimeDelta::FromMilliseconds(5500),
+ base::TimeDelta::FromMilliseconds(900)));
+ std::vector<BucketExpectation> expected = {
+ {900, 1}, {5500, 1}, {7600, 1}, {10500, 2}};
+ TestHistogram("RendererScheduler.ExpectedTaskQueueingDuration", 5, expected);
+ // Split here is different: only 7600 and 10500 get grouped up.
+ std::vector<BucketExpectation> fine_grained = {
+ {900 * 1000, 1}, {5500 * 1000, 1}, {7600 * 1000, 2}, {15500 * 1000, 1}};
+ TestHistogram("RendererScheduler.ExpectedTaskQueueingDuration3", 5,
+ fine_grained);
+}
+
+// The main thread is considered unresponsive during a single long task. In this
+// case, the single long task is 3 seconds long.
+// Probability of being with the task = 3/5. Expected delay within task:
+// avg(0, 3). Total expected queueing time = 3/5 * 3/2 = 0.9s.
+// In this example, the queueing time comes from the current, incomplete window.
+TEST_F(QueueingTimeEstimatorTest,
+ EstimateQueueingTimeDuringSingleLongTaskIncompleteWindow) {
+ QueueingTimeEstimatorForTest estimator(
+ &client, base::TimeDelta::FromSeconds(5), 1, time);
+ time += base::TimeDelta::FromMilliseconds(5000);
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ base::TimeTicks start_time = time;
+ estimator.OnTopLevelTaskStarted(start_time, nullptr);
+
+ time += base::TimeDelta::FromMilliseconds(3000);
+
+ base::TimeDelta estimated_queueing_time =
+ estimator.EstimateQueueingTimeIncludingCurrentTask(time);
+
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(900), estimated_queueing_time);
+ // Window time was not completed, so no UMA should be recorded.
+ histogram_tester.ExpectTotalCount(
+ "RendererScheduler.ExpectedTaskQueueingDuration", 0);
+ histogram_tester.ExpectTotalCount("ExpectedTaskQueueingDuration2", 0);
+}
+
+// The main thread is considered unresponsive during a single long task, which
+// exceeds the size of one window. We report the queueing time of the most
+// recent window. Probability of being within the task = 100%, as the task
+// fills the whole window. Expected delay within this task = avg(8, 3) = 5.5.
+TEST_F(QueueingTimeEstimatorTest,
+ EstimateQueueingTimeDuringSingleLongTaskExceedingWindow) {
+ QueueingTimeEstimatorForTest estimator(
+ &client, base::TimeDelta::FromSeconds(5), 1, time);
+ time += base::TimeDelta::FromMilliseconds(5000);
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ base::TimeTicks start_time = time;
+ estimator.OnTopLevelTaskStarted(start_time, nullptr);
+
+ time += base::TimeDelta::FromMilliseconds(13000);
+
+ base::TimeDelta estimated_queueing_time =
+ estimator.EstimateQueueingTimeIncludingCurrentTask(time);
+
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(5500), estimated_queueing_time);
+}
+
+// Estimate
+// |
+// v
+// Task|------------------------------...
+// Time|---o---o---o---o---o---o-------->
+// 0 1 2 3 4 5 6
+// | s | s | s | s | s |
+// |--------win1-------|
+// |--------win2-------|
+//
+// s: step window
+// win1: The last full window.
+// win2: The partial window.
+//
+// EQT(win1) = (0.5 + 5.5) / 2 * (5 / 5) = 3
+// EQT(win2) = (4.5 + 0) / 2 * (4.5 / 5) = 2.025
+// So EQT = max(EQT(win1), EQT(win2)) = 3
+TEST_F(QueueingTimeEstimatorTest,
+ SlidingWindowEstimateQueueingTimeFullWindowLargerThanPartial) {
+ QueueingTimeEstimatorForTest estimator(
+ &client, base::TimeDelta::FromSeconds(5), 5, time);
+ time += base::TimeDelta::FromMilliseconds(5000);
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ base::TimeTicks start_time = time;
+ estimator.OnTopLevelTaskStarted(start_time, nullptr);
+
+ time += base::TimeDelta::FromMilliseconds(5500);
+
+ base::TimeDelta estimated_queueing_time =
+ estimator.EstimateQueueingTimeIncludingCurrentTask(time);
+
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(3000), estimated_queueing_time);
+ // Only EstimateQueueingTimeIncludingCurrentTask has been called after window
+ // completion, so UMA should not have been reported yet.
+ histogram_tester.ExpectTotalCount(
+ "RendererScheduler.ExpectedTaskQueueingDuration", 0);
+ histogram_tester.ExpectTotalCount(
+ "RendererScheduler.ExpectedTaskQueueingDuration3", 0);
+}
+
+// Estimate
+// |
+// v
+// Task |----------...
+// Time|---o---o---o---o---o---o-------->
+// 0 1 2 3 4 5 6
+// | s | s | s | s | s |
+// |--------win1-------|
+// |--------win2-------|
+//
+// s: step window
+// win1: The last full window.
+// win2: The partial window.
+//
+// EQT(win1) = 0
+// EQT(win2) = (0 + 0.5) / 2 * (0.5 / 2) = 0.025
+// So EQT = max(EQT(win1), EQT(win2)) = 0.025
+TEST_F(QueueingTimeEstimatorTest,
+ SlidingWindowEstimateQueueingTimePartialWindowLargerThanFull) {
+ QueueingTimeEstimatorForTest estimator(
+ &client, base::TimeDelta::FromSeconds(5), 5, time);
+ time += base::TimeDelta::FromMilliseconds(5000);
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ time += base::TimeDelta::FromMilliseconds(5000);
+ base::TimeTicks start_time = time;
+ estimator.OnTopLevelTaskStarted(start_time, nullptr);
+ time += base::TimeDelta::FromMilliseconds(500);
+
+ base::TimeDelta estimated_queueing_time =
+ estimator.EstimateQueueingTimeIncludingCurrentTask(time);
+
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(25), estimated_queueing_time);
+ std::vector<BucketExpectation> expected = {{0, 1}};
+ TestHistogram("RendererScheduler.ExpectedTaskQueueingDuration", 1, expected);
+ TestHistogram("RendererScheduler.ExpectedTaskQueueingDuration3", 1, expected);
+}
+
+// Tasks containing nested run loops may be extremely long without
+// negatively impacting user experience. Ignore such tasks.
+TEST_F(QueueingTimeEstimatorTest, IgnoresTasksWithNestedMessageLoops) {
+ QueueingTimeEstimatorForTest estimator(
+ &client, base::TimeDelta::FromSeconds(5), 1, time);
+ time += base::TimeDelta::FromMilliseconds(5000);
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ time += base::TimeDelta::FromMilliseconds(5000);
+
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ time += base::TimeDelta::FromMilliseconds(20000);
+ estimator.OnBeginNestedRunLoop();
+ estimator.OnTopLevelTaskCompleted(time);
+
+ // Perform an additional task after the nested run loop. A 1 second task
+ // in a 5 second window results in a 100ms expected queueing time.
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ time += base::TimeDelta::FromMilliseconds(1000);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ // Flush the data by adding a task in the next window.
+ time += base::TimeDelta::FromMilliseconds(5000);
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ time += base::TimeDelta::FromMilliseconds(500);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ EXPECT_THAT(client.expected_queueing_times(),
+ testing::ElementsAre(base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(100)));
+ std::vector<BucketExpectation> expected = {{0, 1}, {100, 1}};
+ TestHistogram("RendererScheduler.ExpectedTaskQueueingDuration", 2, expected);
+ std::vector<BucketExpectation> fine_grained = GetFineGrained(expected);
+ TestHistogram("RendererScheduler.ExpectedTaskQueueingDuration3", 2,
+ fine_grained);
+}
+
+// If a task is too long, we assume it's invalid. Perhaps the user's machine
+// went to sleep during a task, resulting in an extremely long task. Ignore
+// these long tasks completely.
+TEST_F(QueueingTimeEstimatorTest, IgnoreExtremelyLongTasks) {
+ QueueingTimeEstimatorForTest estimator(
+ &client, base::TimeDelta::FromSeconds(5), 1, time);
+ time += base::TimeDelta::FromMilliseconds(5000);
+ // Start with a 1 second task.
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ time += base::TimeDelta::FromMilliseconds(1000);
+ estimator.OnTopLevelTaskCompleted(time);
+ time += base::TimeDelta::FromMilliseconds(4000);
+
+ // Now perform an invalid task. This will cause the windows involving this
+ // task to be ignored.
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ time += base::TimeDelta::FromMilliseconds(35000);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ // Perform another 1 second task.
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ time += base::TimeDelta::FromMilliseconds(1000);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ // Add a task in the next window.
+ time += base::TimeDelta::FromMilliseconds(5000);
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ time += base::TimeDelta::FromMilliseconds(500);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ // Now perform another invalid task. This will cause the windows involving
+ // this task to be ignored. Therefore, the previous task is ignored.
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ time += base::TimeDelta::FromMilliseconds(35000);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ // Flush by adding a task.
+ time += base::TimeDelta::FromMilliseconds(5000);
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ EXPECT_THAT(client.expected_queueing_times(),
+ testing::ElementsAre(base::TimeDelta::FromMilliseconds(100),
+ base::TimeDelta::FromMilliseconds(100)));
+ std::vector<BucketExpectation> expected = {{100, 2}};
+ TestHistogram("RendererScheduler.ExpectedTaskQueueingDuration", 2, expected);
+ std::vector<BucketExpectation> fine_grained = GetFineGrained(expected);
+ TestHistogram("RendererScheduler.ExpectedTaskQueueingDuration3", 2,
+ fine_grained);
+}
+
+// If we idle for too long, ignore idling time, even if the renderer is on the
+// foreground. Perhaps the user's machine went to sleep while we were idling.
+TEST_F(QueueingTimeEstimatorTest, IgnoreExtremelyLongIdlePeriods) {
+ QueueingTimeEstimatorForTest estimator(
+ &client, base::TimeDelta::FromSeconds(5), 1, time);
+ time += base::TimeDelta::FromMilliseconds(5000);
+ // Start with a 1 second task.
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ time += base::TimeDelta::FromMilliseconds(1000);
+ estimator.OnTopLevelTaskCompleted(time);
+ time += base::TimeDelta::FromMilliseconds(4000);
+ // Dummy task to ensure this window is reported.
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ // Now go idle for long. This will cause the windows involving this
+ // time to be ignored.
+ time += base::TimeDelta::FromMilliseconds(35000);
+
+ // Perform another 1 second task.
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ time += base::TimeDelta::FromMilliseconds(1000);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ // Add a task in the next window.
+ time += base::TimeDelta::FromMilliseconds(5000);
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ time += base::TimeDelta::FromMilliseconds(500);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ // Now go idle again. This will cause the windows involving this idle period
+ // to be ignored. Therefore, the previous task is ignored.
+ time += base::TimeDelta::FromMilliseconds(35000);
+
+ // Flush by adding a task.
+ time += base::TimeDelta::FromMilliseconds(5000);
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ EXPECT_THAT(client.expected_queueing_times(),
+ testing::ElementsAre(base::TimeDelta::FromMilliseconds(100),
+ base::TimeDelta::FromMilliseconds(100)));
+ std::vector<BucketExpectation> expected = {{100, 2}};
+ TestHistogram("RendererScheduler.ExpectedTaskQueueingDuration", 2, expected);
+ std::vector<BucketExpectation> fine_grained = GetFineGrained(expected);
+ TestHistogram("RendererScheduler.ExpectedTaskQueueingDuration3", 2,
+ fine_grained);
+}
+
+// ^ Instantaneous queuing time
+// |
+// |
+// | |\ .
+// | | \ .
+// | | \ .
+// | | \ .
+// | | \ | .
+// ------------------------------------------------> Time
+// |s|s|s|s|s|
+// |---win---|
+// |---win---|
+// |---win---|
+TEST_F(QueueingTimeEstimatorTest, SlidingWindowOverOneTask) {
+ QueueingTimeEstimatorForTest estimator(
+ &client, base::TimeDelta::FromSeconds(5), 5, time);
+ time += base::TimeDelta::FromMilliseconds(1000);
+
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ time += base::TimeDelta::FromMilliseconds(5000);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ time += base::TimeDelta::FromMilliseconds(6000);
+
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ std::vector<base::TimeDelta> expected_durations = {
+ base::TimeDelta::FromMilliseconds(900),
+ base::TimeDelta::FromMilliseconds(1600),
+ base::TimeDelta::FromMilliseconds(2100),
+ base::TimeDelta::FromMilliseconds(2400),
+ base::TimeDelta::FromMilliseconds(2500),
+ base::TimeDelta::FromMilliseconds(1600),
+ base::TimeDelta::FromMilliseconds(900),
+ base::TimeDelta::FromMilliseconds(400),
+ base::TimeDelta::FromMilliseconds(100),
+ base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(0)};
+ EXPECT_THAT(client.expected_queueing_times(),
+ testing::ElementsAreArray(expected_durations));
+ // UMA reported only on disjoint windows.
+ std::vector<BucketExpectation> expected = {{0, 1}, {2500, 1}};
+ TestHistogram("RendererScheduler.ExpectedTaskQueueingDuration", 2, expected);
+}
+
+// ^ Instantaneous queuing time
+// |
+// |
+// | |\ .
+// | | \ .
+// | | \ .
+// | | \ |\ .
+// | | \ | \ | .
+// ------------------------------------------------> Time
+// |s|s|s|s|s|
+// |---win---|
+// |---win---|
+// |---win---|
+TEST_F(QueueingTimeEstimatorTest, SlidingWindowOverTwoTasksWithinFirstWindow) {
+ QueueingTimeEstimatorForTest estimator(
+ &client, base::TimeDelta::FromSeconds(5), 5, time);
+ time += base::TimeDelta::FromMilliseconds(1000);
+
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ time += base::TimeDelta::FromMilliseconds(2500);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ time += base::TimeDelta::FromMilliseconds(500);
+
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ time += base::TimeDelta::FromMilliseconds(1000);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ time += base::TimeDelta::FromMilliseconds(6000);
+
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ std::vector<base::TimeDelta> expected_durations = {
+ base::TimeDelta::FromMilliseconds(400),
+ base::TimeDelta::FromMilliseconds(600),
+ base::TimeDelta::FromMilliseconds(625),
+ base::TimeDelta::FromMilliseconds(725),
+ base::TimeDelta::FromMilliseconds(725),
+ base::TimeDelta::FromMilliseconds(325),
+ base::TimeDelta::FromMilliseconds(125),
+ base::TimeDelta::FromMilliseconds(100),
+ base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(0)};
+ EXPECT_THAT(client.expected_queueing_times(),
+ testing::ElementsAreArray(expected_durations));
+ std::vector<BucketExpectation> expected = {{0, 1}, {725, 1}};
+ TestHistogram("RendererScheduler.ExpectedTaskQueueingDuration", 2, expected);
+ std::vector<BucketExpectation> fine_grained = GetFineGrained(expected);
+ TestHistogram("RendererScheduler.ExpectedTaskQueueingDuration3", 2,
+ fine_grained);
+}
+
+// ^ Instantaneous queuing time
+// |
+// |
+// | |\ .
+// | | \ .
+// | | \ .
+// | | \ |\ .
+// | | \| \ | .
+// ------------------------------------------------> Time
+// |s|s|s|s|s|
+// |---win---|
+// |---win---|
+// |---win---|
+TEST_F(QueueingTimeEstimatorTest,
+ SlidingWindowOverTwoTasksSpanningSeveralWindows) {
+ QueueingTimeEstimatorForTest estimator(
+ &client, base::TimeDelta::FromSeconds(5), 5, time);
+ time += base::TimeDelta::FromMilliseconds(1000);
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ time += base::TimeDelta::FromMilliseconds(4000);
+
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ time += base::TimeDelta::FromMilliseconds(2500);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ time += base::TimeDelta::FromMilliseconds(1000);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ time += base::TimeDelta::FromMilliseconds(6000);
+
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ std::vector<base::TimeDelta> expected_durations = {
+ base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(400),
+ base::TimeDelta::FromMilliseconds(600),
+ base::TimeDelta::FromMilliseconds(700),
+ base::TimeDelta::FromMilliseconds(725),
+ base::TimeDelta::FromMilliseconds(725),
+ base::TimeDelta::FromMilliseconds(325),
+ base::TimeDelta::FromMilliseconds(125),
+ base::TimeDelta::FromMilliseconds(25),
+ base::TimeDelta::FromMilliseconds(0)};
+
+ EXPECT_THAT(client.expected_queueing_times(),
+ testing::ElementsAreArray(expected_durations));
+ std::vector<BucketExpectation> expected = {{325, 1}, {400, 1}};
+ TestHistogram("RendererScheduler.ExpectedTaskQueueingDuration", 2, expected);
+ // The two values get grouped under the same bucket in the microsecond
+ // version.
+ expected = {{325 * 1000, 2}};
+ TestHistogram("RendererScheduler.ExpectedTaskQueueingDuration3", 2, expected);
+}
+
+// There are multiple windows, but some of the EQTs are not reported due to
+// backgrounded renderer. EQT(win1) = 0. EQT(win3) = (1500+500)/2 = 1000.
+// EQT(win4) = 1/2*500/2 = 250. EQT(win7) = 1/5*200/2 = 20.
+TEST_F(QueueingTimeEstimatorTest, BackgroundedEQTsWithSingleStepPerWindow) {
+ QueueingTimeEstimatorForTest estimator(
+ &client, base::TimeDelta::FromSeconds(1), 1, time);
+ time += base::TimeDelta::FromMilliseconds(1000);
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ estimator.OnTopLevelTaskCompleted(time);
+ time += base::TimeDelta::FromMilliseconds(1001);
+
+ // Second window should not be reported.
+ estimator.OnRendererStateChanged(true, time);
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ time += base::TimeDelta::FromMilliseconds(456);
+ estimator.OnTopLevelTaskCompleted(time);
+ time += base::TimeDelta::FromMilliseconds(200);
+ estimator.OnRendererStateChanged(false, time);
+ time += base::TimeDelta::FromMilliseconds(343);
+
+ // Third, fourth windows should be reported
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ time += base::TimeDelta::FromMilliseconds(1500);
+ estimator.OnTopLevelTaskCompleted(time);
+ time += base::TimeDelta::FromMilliseconds(501);
+
+ // Fifth, sixth task should not be reported
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ time += base::TimeDelta::FromMilliseconds(800);
+ estimator.OnTopLevelTaskCompleted(time);
+ estimator.OnRendererStateChanged(true, time);
+ time += base::TimeDelta::FromMilliseconds(200);
+ estimator.OnRendererStateChanged(false, time);
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ time += base::TimeDelta::FromMilliseconds(999);
+
+ // Seventh task should be reported.
+ time += base::TimeDelta::FromMilliseconds(200);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ time += base::TimeDelta::FromMilliseconds(1000);
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ EXPECT_THAT(client.expected_queueing_times(),
+ testing::ElementsAre(base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(1000),
+ base::TimeDelta::FromMilliseconds(125),
+ base::TimeDelta::FromMilliseconds(20)));
+ std::vector<BucketExpectation> expected = {
+ {0, 1}, {20, 1}, {125, 1}, {1000, 1}};
+ TestHistogram("RendererScheduler.ExpectedTaskQueueingDuration", 4, expected);
+ std::vector<BucketExpectation> fine_grained = GetFineGrained(expected);
+ TestHistogram("RendererScheduler.ExpectedTaskQueueingDuration3", 4,
+ fine_grained);
+}
+
+// We only ignore steps that contain some part that is backgrounded. Thus a
+// window could be made up of non-contiguous steps. The following are EQTs, with
+// time deltas with respect to the end of the first, 0-time task:
+// Win1: [0-1000]. EQT of step [0-1000]: 500/2*1/2 = 125. EQT(win1) = 125/5 =
+// 25.
+// Win2: [0-1000],[2000-3000]. EQT of [2000-3000]: (1000+200)/2*4/5 = 480.
+// EQT(win2) = (125+480)/5 = 121.
+// Win3: [0-1000],[2000-3000],[11000-12000]. EQT of [11000-12000]: 0. EQT(win3)
+// = 121.
+// Win4: [0-1000],[2000-3000],[11000-13000]. EQT of [12000-13000]:
+// (1500+1400)/2*1/10 = 145. EQT(win4) = (125+480+0+145)/5 = 150.
+// Win5: [0-1000],[2000-3000],[11000-14000]. EQT of [13000-14000]: (1400+400)/2
+// = 900. EQT(win5) = (125+480+0+145+900)/5 = 330.
+// Win6: [2000-3000],[11000-15000]. EQT of [14000-15000]: 400/2*2/5 = 80.
+// EQT(win6) = (480+0+145+900+80)/5 = 321.
+// Win7: [11000-16000]. EQT of [15000-16000]: (2500+1700)/2*4/5 = 1680.
+// EQT(win7) = (0+145+900+80+1680)/5 = 561.
+// Win8: [12000-17000]. EQT of [16000-17000]: (1700+700)/2 = 1200. EQT(win8) =
+// (145+900+80+1680+1200)/5 = 801.
+TEST_F(QueueingTimeEstimatorTest, BackgroundedEQTsWithMutipleStepsPerWindow) {
+ QueueingTimeEstimatorForTest estimator(
+ &client, base::TimeDelta::FromSeconds(5), 5, time);
+ time += base::TimeDelta::FromMilliseconds(5000);
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ time += base::TimeDelta::FromMilliseconds(500);
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ time += base::TimeDelta::FromMilliseconds(500);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ estimator.OnRendererStateChanged(true, time);
+ // This task should be ignored.
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ time += base::TimeDelta::FromMilliseconds(800);
+ estimator.OnTopLevelTaskCompleted(time);
+ estimator.OnRendererStateChanged(false, time);
+
+ time += base::TimeDelta::FromMilliseconds(400);
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ time += base::TimeDelta::FromMilliseconds(1000);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ time += base::TimeDelta::FromMilliseconds(300);
+ estimator.OnRendererStateChanged(true, time);
+ time += base::TimeDelta::FromMilliseconds(2000);
+ // These tasks should be ignored.
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ time += base::TimeDelta::FromMilliseconds(2000);
+ estimator.OnTopLevelTaskCompleted(time);
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ time += base::TimeDelta::FromMilliseconds(3400);
+ estimator.OnTopLevelTaskCompleted(time);
+ estimator.OnRendererStateChanged(false, time);
+
+ time += base::TimeDelta::FromMilliseconds(2000);
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ time += base::TimeDelta::FromMilliseconds(1500);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ time += base::TimeDelta::FromMilliseconds(800);
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ time += base::TimeDelta::FromMilliseconds(2500);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ // Window with last step should not be reported.
+ estimator.OnRendererStateChanged(true, time);
+ time += base::TimeDelta::FromMilliseconds(1000);
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ EXPECT_THAT(client.expected_queueing_times(),
+ testing::ElementsAre(base::TimeDelta::FromMilliseconds(25),
+ base::TimeDelta::FromMilliseconds(121),
+ base::TimeDelta::FromMilliseconds(121),
+ base::TimeDelta::FromMilliseconds(150),
+ base::TimeDelta::FromMilliseconds(330),
+ base::TimeDelta::FromMilliseconds(321),
+ base::TimeDelta::FromMilliseconds(561),
+ base::TimeDelta::FromMilliseconds(801)));
+}
+
+// Split ExpectedQueueingTime only reports once per disjoint window. The
+// following is a detailed explanation of EQT per window and task queue:
+// Window 1: A 3000ms default queue task contributes 900 to that EQT.
+// Window 2: After 3000ms, the first 2000ms from a 3000ms default task: 800 EQT
+// for that.
+// Window 3: The remaining 100 EQT for default type. Also 1000ms tasks (which
+// contribute 100) for FrameLoading, FrameThrottleable, and Unthrottled.
+// Window 4: 600 ms tasks (which contribute 36) for each of the buckets except
+// other. Two 300 ms (each contributing 9) and one 200 ms tasks (contributes 4)
+// for the other bucket.
+TEST_F(QueueingTimeEstimatorTest, SplitEQTByTaskQueueType) {
+ QueueingTimeEstimatorForTest estimator(
+ &client, base::TimeDelta::FromSeconds(5), 5, time);
+ time += base::TimeDelta::FromMilliseconds(5000);
+ // Dummy task to initialize the estimator.
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ // Beginning of window 1.
+ time += base::TimeDelta::FromMilliseconds(500);
+ scoped_refptr<MainThreadTaskQueueForTest> default_queue(
+ new MainThreadTaskQueueForTest(QueueType::kDefault));
+ estimator.OnTopLevelTaskStarted(time, default_queue.get());
+ time += base::TimeDelta::FromMilliseconds(3000);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ time += base::TimeDelta::FromMilliseconds(1500);
+
+ // Beginning of window 2.
+ time += base::TimeDelta::FromMilliseconds(3000);
+ estimator.OnTopLevelTaskStarted(time, default_queue.get());
+ time += base::TimeDelta::FromMilliseconds(3000);
+ // 1000 ms after beginning of window 3.
+ estimator.OnTopLevelTaskCompleted(time);
+
+ time += base::TimeDelta::FromMilliseconds(1000);
+ scoped_refptr<MainThreadTaskQueueForTest> frame_loading_queue(
+ new MainThreadTaskQueueForTest(QueueType::kFrameLoading));
+ scoped_refptr<MainThreadTaskQueueForTest> frame_throttleable_queue(
+ new MainThreadTaskQueueForTest(QueueType::kFrameThrottleable));
+ scoped_refptr<MainThreadTaskQueueForTest> unthrottled_queue(
+ new MainThreadTaskQueueForTest(QueueType::kUnthrottled));
+ MainThreadTaskQueue* queues_for_thousand[] = {frame_loading_queue.get(),
+ frame_throttleable_queue.get(),
+ unthrottled_queue.get()};
+ for (auto queue : queues_for_thousand) {
+ estimator.OnTopLevelTaskStarted(time, queue);
+ time += base::TimeDelta::FromMilliseconds(1000);
+ estimator.OnTopLevelTaskCompleted(time);
+ }
+
+ // Beginning of window 4.
+ scoped_refptr<MainThreadTaskQueueForTest> frame_pausable_queue(
+ new MainThreadTaskQueueForTest(QueueType::kFramePausable));
+ scoped_refptr<MainThreadTaskQueueForTest> compositor_queue(
+ new MainThreadTaskQueueForTest(QueueType::kCompositor));
+ MainThreadTaskQueue* queues_for_six_hundred[] = {
+ default_queue.get(),
+ frame_loading_queue.get(),
+ frame_throttleable_queue.get(),
+ frame_pausable_queue.get(),
+ unthrottled_queue.get(),
+ compositor_queue.get()};
+ for (auto queue : queues_for_six_hundred) {
+ estimator.OnTopLevelTaskStarted(time, queue);
+ time += base::TimeDelta::FromMilliseconds(600);
+ estimator.OnTopLevelTaskCompleted(time);
+ }
+ time += base::TimeDelta::FromMilliseconds(600);
+
+ // The following task contributes to "Other" because kControl is not a
+ // supported queue type.
+ scoped_refptr<MainThreadTaskQueueForTest> control_queue(
+ new MainThreadTaskQueueForTest(QueueType::kControl));
+ estimator.OnTopLevelTaskStarted(time, control_queue.get());
+ time += base::TimeDelta::FromMilliseconds(300);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ // The following task contributes to "Other" because kTest is not a supported
+ // queue type.
+ scoped_refptr<MainThreadTaskQueueForTest> test_queue(
+ new MainThreadTaskQueueForTest(QueueType::kTest));
+ estimator.OnTopLevelTaskStarted(time, test_queue.get());
+ time += base::TimeDelta::FromMilliseconds(300);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ // The following task contributes to "Other" because there is no task queue.
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ time += base::TimeDelta::FromMilliseconds(200);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ // End of window 4. Now check the vectors per task queue type.
+ EXPECT_THAT(client.QueueTypeValues(QueueType::kDefault),
+ testing::ElementsAre(base::TimeDelta::FromMilliseconds(900),
+ base::TimeDelta::FromMilliseconds(800),
+ base::TimeDelta::FromMilliseconds(100),
+ base::TimeDelta::FromMilliseconds(36)));
+ // The 800 and 900 values get grouped into a single bucket.
+ std::vector<BucketExpectation> expected = {{36, 1}, {100, 1}, {800, 2}};
+ TestHistogram("RendererScheduler.ExpectedQueueingTimeByTaskQueue2.Default", 4,
+ GetFineGrained(expected));
+
+ EXPECT_THAT(client.QueueTypeValues(QueueType::kFrameLoading),
+ testing::ElementsAre(base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(100),
+ base::TimeDelta::FromMilliseconds(36)));
+ expected = {{0, 2}, {36, 1}, {100, 1}};
+ TestHistogram(
+ "RendererScheduler.ExpectedQueueingTimeByTaskQueue2.FrameLoading", 4,
+ GetFineGrained(expected));
+
+ EXPECT_THAT(client.QueueTypeValues(QueueType::kFrameThrottleable),
+ testing::ElementsAre(base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(100),
+ base::TimeDelta::FromMilliseconds(36)));
+ expected = {{0, 2}, {36, 1}, {100, 1}};
+ TestHistogram(
+ "RendererScheduler.ExpectedQueueingTimeByTaskQueue2.FrameThrottleable", 4,
+ GetFineGrained(expected));
+
+ EXPECT_THAT(client.QueueTypeValues(QueueType::kFramePausable),
+ testing::ElementsAre(base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(36)));
+ expected = {{0, 3}, {36, 1}};
+ TestHistogram(
+ "RendererScheduler.ExpectedQueueingTimeByTaskQueue2.FramePausable", 4,
+ GetFineGrained(expected));
+
+ EXPECT_THAT(client.QueueTypeValues(QueueType::kUnthrottled),
+ testing::ElementsAre(base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(100),
+ base::TimeDelta::FromMilliseconds(36)));
+ expected = {{0, 2}, {36, 1}, {100, 1}};
+ TestHistogram(
+ "RendererScheduler.ExpectedQueueingTimeByTaskQueue2.Unthrottled", 4,
+ GetFineGrained(expected));
+
+ EXPECT_THAT(client.QueueTypeValues(QueueType::kCompositor),
+ testing::ElementsAre(base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(36)));
+ expected = {{0, 3}, {36, 1}};
+ TestHistogram("RendererScheduler.ExpectedQueueingTimeByTaskQueue2.Compositor",
+ 4, GetFineGrained(expected));
+
+ EXPECT_THAT(client.QueueTypeValues(QueueType::kOther),
+ testing::ElementsAre(base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(22)));
+ expected = {{0, 3}, {22, 1}};
+ TestHistogram("RendererScheduler.ExpectedQueueingTimeByTaskQueue2.Other", 4,
+ GetFineGrained(expected));
+
+ // Check that the sum of split EQT equals the total EQT for each window.
+ base::TimeDelta expected_sums[] = {base::TimeDelta::FromMilliseconds(900),
+ base::TimeDelta::FromMilliseconds(800),
+ base::TimeDelta::FromMilliseconds(400),
+ base::TimeDelta::FromMilliseconds(238)};
+ EXPECT_THAT(client.FrameStatusValues(FrameStatus::kNone),
+ testing::ElementsAreArray(expected_sums));
+ expected = {{238, 1}, {400, 1}, {800, 1}, {900, 1}};
+ // The 800 and 900 values end up grouped up in the fine-grained version.
+ std::vector<BucketExpectation> fine_grained = {
+ {238 * 1000, 1}, {400 * 1000, 1}, {800 * 1000, 2}};
+ TestHistogram("RendererScheduler.ExpectedQueueingTimeByFrameStatus2.Other", 4,
+ fine_grained);
+ TestHistogram("RendererScheduler.ExpectedTaskQueueingDuration", 4, expected);
+ TestHistogram("RendererScheduler.ExpectedTaskQueueingDuration3", 4,
+ fine_grained);
+ TestSplitSumsTotal(expected_sums, 5);
+}
+
+// Split ExpectedQueueingTime only reports once per disjoint window. The
+// following is a detailed explanation of EQT per window and frame type:
+// Window 1: A 3000ms task in a background main frame contributes 900 to that
+// EQT.
+// Window 2: Two 2000ms tasks in a visible main frame: 400 each, total 800
+// EQT.
+// Window 3: 3000ms task in a visible main frame: 900 EQT for that type. Also,
+// the first 2000ms from a 3000ms task in a background main frame: 800 EQT for
+// that.
+// Window 4: The remaining 100 EQT for background main frame. Also 1000ms
+// tasks (which contribute 100) for kSameOriginVisible, kSameOriginHidden,
+// and kCrossOriginVisible.
+// Window 5: 400 ms tasks (which contribute 16) for each of the buckets except
+// other. Two 300 ms (each contributing 9) and one 800 ms tasks (contributes
+// 64) for the other bucket.
+TEST_F(QueueingTimeEstimatorTest, SplitEQTByFrameStatus) {
+ QueueingTimeEstimatorForTest estimator(
+ &client, base::TimeDelta::FromSeconds(5), 5, time);
+ time += base::TimeDelta::FromMilliseconds(5000);
+ // Dummy task to initialize the estimator.
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ estimator.OnTopLevelTaskCompleted(time);
+ scoped_refptr<MainThreadTaskQueueForTest> queue1(
+ new MainThreadTaskQueueForTest(QueueType::kTest));
+
+ // Beginning of window 1.
+ time += base::TimeDelta::FromMilliseconds(500);
+ // Scheduler with frame type: MAIN_FRAME_BACKGROUND.
+ std::unique_ptr<FakeFrameScheduler> frame1 =
+ FakeFrameScheduler::Builder()
+ .SetFrameType(FrameScheduler::FrameType::kMainFrame)
+ .Build();
+ queue1->SetFrameScheduler(frame1.get());
+ estimator.OnTopLevelTaskStarted(time, queue1.get());
+ time += base::TimeDelta::FromMilliseconds(3000);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ time += base::TimeDelta::FromMilliseconds(1500);
+ // Beginning of window 2.
+ // Scheduler with frame type: MAIN_FRAME_VISIBLE.
+ std::unique_ptr<FakeFrameScheduler> frame2 =
+ FakeFrameScheduler::Builder()
+ .SetFrameType(FrameScheduler::FrameType::kMainFrame)
+ .SetIsPageVisible(true)
+ .SetIsFrameVisible(true)
+ .Build();
+ queue1->SetFrameScheduler(frame2.get());
+ estimator.OnTopLevelTaskStarted(time, queue1.get());
+ time += base::TimeDelta::FromMilliseconds(2000);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ scoped_refptr<MainThreadTaskQueueForTest> queue2(
+ new MainThreadTaskQueueForTest(QueueType::kTest));
+ queue2->SetFrameScheduler(frame2.get());
+ time += base::TimeDelta::FromMilliseconds(1000);
+ estimator.OnTopLevelTaskStarted(time, queue2.get());
+ time += base::TimeDelta::FromMilliseconds(2000);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ // Beginning of window 3.
+ // Scheduler with frame type: MAIN_FRAME_VISIBLE.
+ std::unique_ptr<FakeFrameScheduler> frame3 =
+ FakeFrameScheduler::Builder()
+ .SetFrameType(FrameScheduler::FrameType::kMainFrame)
+ .SetIsPageVisible(true)
+ .SetIsFrameVisible(true)
+ .SetIsExemptFromThrottling(true)
+ .Build();
+ queue1->SetFrameScheduler(frame3.get());
+ estimator.OnTopLevelTaskStarted(time, queue1.get());
+ time += base::TimeDelta::FromMilliseconds(3000);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ // Scheduler with frame type: MAIN_FRAME_BACKGROUND.
+ std::unique_ptr<FakeFrameScheduler> frame4 =
+ FakeFrameScheduler::Builder()
+ .SetFrameType(FrameScheduler::FrameType::kMainFrame)
+ .SetIsFrameVisible(true)
+ .SetIsExemptFromThrottling(true)
+ .Build();
+ queue1->SetFrameScheduler(frame4.get());
+ estimator.OnTopLevelTaskStarted(time, queue1.get());
+ time += base::TimeDelta::FromMilliseconds(3000);
+ // 1000 ms after beginning of window 4.
+ estimator.OnTopLevelTaskCompleted(time);
+
+ time += base::TimeDelta::FromMilliseconds(1000);
+ // Scheduler with frame type: SAME_ORIGIN_VISIBLE.
+ std::unique_ptr<FakeFrameScheduler> frame5 =
+ FakeFrameScheduler::Builder()
+ .SetFrameType(FrameScheduler::FrameType::kSubframe)
+ .SetIsPageVisible(true)
+ .SetIsFrameVisible(true)
+ .Build();
+ // Scheduler with frame type: SAME_ORIGIN_HIDDEN.
+ std::unique_ptr<FakeFrameScheduler> frame6 =
+ FakeFrameScheduler::Builder()
+ .SetFrameType(FrameScheduler::FrameType::kSubframe)
+ .SetIsPageVisible(true)
+ .Build();
+ // Scheduler with frame type: CROSS_ORIGIN_VISIBLE.
+ std::unique_ptr<FakeFrameScheduler> frame7 =
+ FakeFrameScheduler::Builder()
+ .SetFrameType(FrameScheduler::FrameType::kSubframe)
+ .SetIsPageVisible(true)
+ .SetIsFrameVisible(true)
+ .SetIsCrossOrigin(true)
+ .Build();
+ FakeFrameScheduler* schedulers_for_thousand[] = {frame5.get(), frame6.get(),
+ frame7.get()};
+ for (auto scheduler : schedulers_for_thousand) {
+ queue1->SetFrameScheduler(scheduler);
+ estimator.OnTopLevelTaskStarted(time, queue1.get());
+ time += base::TimeDelta::FromMilliseconds(1000);
+ estimator.OnTopLevelTaskCompleted(time);
+ }
+
+ // Beginning of window 5.
+ // Scheduler with frame type: MAIN_FRAME_HIDDEN.
+ std::unique_ptr<FakeFrameScheduler> frame8 =
+ FakeFrameScheduler::Builder()
+ .SetFrameType(FrameScheduler::FrameType::kMainFrame)
+ .SetIsPageVisible(true)
+ .Build();
+ // Scheduler with frame type: SAME_ORIGIN_BACKGROUND.
+ std::unique_ptr<FakeFrameScheduler> frame9 =
+ FakeFrameScheduler::Builder()
+ .SetFrameType(FrameScheduler::FrameType::kSubframe)
+ .Build();
+ // Scheduler with frame type: CROSS_ORIGIN_HIDDEN.
+ std::unique_ptr<FakeFrameScheduler> frame10 =
+ FakeFrameScheduler::Builder()
+ .SetFrameType(FrameScheduler::FrameType::kSubframe)
+ .SetIsPageVisible(true)
+ .SetIsCrossOrigin(true)
+ .Build();
+ // Scheduler with frame type: CROSS_ORIGIN_BACKGROUND.
+ std::unique_ptr<FakeFrameScheduler> frame11 =
+ FakeFrameScheduler::Builder()
+ .SetFrameType(FrameScheduler::FrameType::kSubframe)
+ .SetIsCrossOrigin(true)
+ .Build();
+ // One scheduler per supported frame type, excluding "Other".
+ FakeFrameScheduler* schedulers_for_four_hundred[] = {
+ frame2.get(), frame1.get(), frame8.get(), frame5.get(), frame6.get(),
+ frame9.get(), frame7.get(), frame10.get(), frame11.get()};
+ for (auto scheduler : schedulers_for_four_hundred) {
+ queue1->SetFrameScheduler(scheduler);
+ estimator.OnTopLevelTaskStarted(time, queue1.get());
+ time += base::TimeDelta::FromMilliseconds(400);
+ estimator.OnTopLevelTaskCompleted(time);
+ }
+
+ // The following tasks contribute to "Other" because there is no frame.
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ time += base::TimeDelta::FromMilliseconds(300);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ queue1->SetFrameScheduler(nullptr);
+ estimator.OnTopLevelTaskStarted(time, queue1.get());
+ time += base::TimeDelta::FromMilliseconds(300);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ estimator.OnTopLevelTaskStarted(time, nullptr);
+ time += base::TimeDelta::FromMilliseconds(800);
+ estimator.OnTopLevelTaskCompleted(time);
+
+ // End of window 5. Now check the vectors per frame type.
+ EXPECT_THAT(client.FrameStatusValues(FrameStatus::kMainFrameBackground),
+ testing::ElementsAre(base::TimeDelta::FromMilliseconds(900),
+ base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(800),
+ base::TimeDelta::FromMilliseconds(100),
+ base::TimeDelta::FromMilliseconds(16)));
+ std::vector<BucketExpectation> expected = {
+ {0, 1}, {16, 1}, {100, 1}, {800, 2}};
+ TestHistogram(
+ "RendererScheduler.ExpectedQueueingTimeByFrameStatus2."
+ "MainFrameBackground",
+ 5, GetFineGrained(expected));
+
+ EXPECT_THAT(client.FrameStatusValues(FrameStatus::kMainFrameVisible),
+ testing::ElementsAre(base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(800),
+ base::TimeDelta::FromMilliseconds(900),
+ base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(16)));
+ expected = {{0, 2}, {16, 1}, {800, 2}};
+ TestHistogram(
+ "RendererScheduler.ExpectedQueueingTimeByFrameStatus2.MainFrameVisible",
+ 5, GetFineGrained(expected));
+
+ struct FrameExpectation {
+ FrameStatus frame_status;
+ std::string name;
+ };
+ FrameExpectation three_expected[] = {
+ {FrameStatus::kSameOriginVisible,
+ "RendererScheduler.ExpectedQueueingTimeByFrameStatus2."
+ "SameOriginVisible"},
+ {FrameStatus::kSameOriginHidden,
+ "RendererScheduler.ExpectedQueueingTimeByFrameStatus2.SameOriginHidden"},
+ {FrameStatus::kCrossOriginVisible,
+ "RendererScheduler.ExpectedQueueingTimeByFrameStatus2."
+ "CrossOriginVisible"},
+ };
+ for (const auto& frame_expectation : three_expected) {
+ EXPECT_THAT(client.FrameStatusValues(frame_expectation.frame_status),
+ testing::ElementsAre(base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(100),
+ base::TimeDelta::FromMilliseconds(16)));
+ expected = {{0, 3}, {16, 1}, {100, 1}};
+ TestHistogram(frame_expectation.name, 5, GetFineGrained(expected));
+ }
+
+ FrameExpectation more_expected[] = {
+ {FrameStatus::kMainFrameHidden,
+ "RendererScheduler.ExpectedQueueingTimeByFrameStatus2."
+ "MainFrameHidden"},
+ {FrameStatus::kSameOriginBackground,
+ "RendererScheduler.ExpectedQueueingTimeByFrameStatus2."
+ "SameOriginBackground"},
+ {FrameStatus::kCrossOriginHidden,
+ "RendererScheduler.ExpectedQueueingTimeByFrameStatus2."
+ "CrossOriginHidden"},
+ {FrameStatus::kCrossOriginBackground,
+ "RendererScheduler.ExpectedQueueingTimeByFrameStatus2."
+ "CrossOriginBackground"}};
+ for (const auto& frame_expectation : more_expected) {
+ EXPECT_THAT(client.FrameStatusValues(frame_expectation.frame_status),
+ testing::ElementsAre(base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(16)));
+ expected = {{0, 4}, {16, 1}};
+ TestHistogram(frame_expectation.name, 5, GetFineGrained(expected));
+ }
+
+ EXPECT_THAT(client.FrameStatusValues(FrameStatus::kNone),
+ testing::ElementsAre(base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(0),
+ base::TimeDelta::FromMilliseconds(82)));
+ expected = {{0, 4}, {82, 1}};
+ TestHistogram("RendererScheduler.ExpectedQueueingTimeByFrameStatus2.Other", 5,
+ GetFineGrained(expected));
+
+ // Check that the sum of split EQT equals the total EQT for each window.
+ base::TimeDelta expected_sums[] = {base::TimeDelta::FromMilliseconds(900),
+ base::TimeDelta::FromMilliseconds(800),
+ base::TimeDelta::FromMilliseconds(1700),
+ base::TimeDelta::FromMilliseconds(400),
+ base::TimeDelta::FromMilliseconds(226)};
+ EXPECT_THAT(client.QueueTypeValues(QueueType::kOther),
+ testing::ElementsAreArray(expected_sums));
+ expected = {{226, 1}, {400, 1}, {800, 1}, {900, 1}, {1700, 1}};
+ std::vector<BucketExpectation> fine_grained = {
+ {226 * 1000, 1}, {400 * 1000, 1}, {800 * 1000, 2}, {1700 * 1000, 1}};
+ TestHistogram("RendererScheduler.ExpectedQueueingTimeByTaskQueue2.Other", 5,
+ fine_grained);
+ TestHistogram("RendererScheduler.ExpectedTaskQueueingDuration", 5, expected);
+ TestHistogram("RendererScheduler.ExpectedTaskQueueingDuration3", 5,
+ fine_grained);
+ TestSplitSumsTotal(expected_sums, 6);
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/renderer/render_widget_signals.cc b/chromium/third_party/blink/renderer/platform/scheduler/renderer/render_widget_signals.cc
new file mode 100644
index 00000000000..fc1f0031564
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/renderer/render_widget_signals.cc
@@ -0,0 +1,64 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/renderer/render_widget_signals.h"
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/public/platform/scheduler/web_render_widget_scheduling_state.h"
+
+namespace blink {
+namespace scheduler {
+
+RenderWidgetSignals::RenderWidgetSignals(Observer* observer)
+ : observer_(observer),
+ num_visible_render_widgets_(0),
+ num_visible_render_widgets_with_touch_handlers_(0) {}
+
+std::unique_ptr<WebRenderWidgetSchedulingState>
+RenderWidgetSignals::NewRenderWidgetSchedulingState() {
+ return base::WrapUnique(new WebRenderWidgetSchedulingState(this));
+}
+
+void RenderWidgetSignals::IncNumVisibleRenderWidgets() {
+ num_visible_render_widgets_++;
+
+ if (num_visible_render_widgets_ == 1)
+ observer_->SetAllRenderWidgetsHidden(false);
+}
+
+void RenderWidgetSignals::DecNumVisibleRenderWidgets() {
+ num_visible_render_widgets_--;
+ DCHECK_GE(num_visible_render_widgets_, 0);
+
+ if (num_visible_render_widgets_ == 0)
+ observer_->SetAllRenderWidgetsHidden(true);
+}
+
+void RenderWidgetSignals::IncNumVisibleRenderWidgetsWithTouchHandlers() {
+ num_visible_render_widgets_with_touch_handlers_++;
+
+ if (num_visible_render_widgets_with_touch_handlers_ == 1)
+ observer_->SetHasVisibleRenderWidgetWithTouchHandler(true);
+}
+
+void RenderWidgetSignals::DecNumVisibleRenderWidgetsWithTouchHandlers() {
+ num_visible_render_widgets_with_touch_handlers_--;
+ DCHECK_GE(num_visible_render_widgets_with_touch_handlers_, 0);
+
+ if (num_visible_render_widgets_with_touch_handlers_ == 0)
+ observer_->SetHasVisibleRenderWidgetWithTouchHandler(false);
+}
+
+void RenderWidgetSignals::AsValueInto(
+ base::trace_event::TracedValue* state) const {
+ state->BeginDictionary("renderer_widget_signals");
+ state->SetInteger("num_visible_render_widgets", num_visible_render_widgets_);
+ state->SetInteger("num_visible_render_widgets_with_touch_handlers",
+ num_visible_render_widgets_with_touch_handlers_);
+ state->EndDictionary();
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/renderer/render_widget_signals.h b/chromium/third_party/blink/renderer/platform/scheduler/renderer/render_widget_signals.h
new file mode 100644
index 00000000000..e19b2133a9d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/renderer/render_widget_signals.h
@@ -0,0 +1,61 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_RENDER_WIDGET_SIGNALS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_RENDER_WIDGET_SIGNALS_H_
+
+#include <memory>
+
+#include "base/trace_event/trace_event.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+namespace scheduler {
+
+class WebRenderWidgetSchedulingState;
+
+class PLATFORM_EXPORT RenderWidgetSignals {
+ public:
+ class PLATFORM_EXPORT Observer {
+ public:
+ virtual ~Observer() = default;
+
+ // If |hidden| is true then all render widgets managed by this renderer
+ // process have been hidden.
+ // If |hidden| is false at least one render widget managed by this renderer
+ // process has become visible and the renderer is no longer hidden.
+ // Will be called on the main thread.
+ virtual void SetAllRenderWidgetsHidden(bool hidden) = 0;
+
+ // Tells the observer whether or not we have at least one touch handler on
+ // a visible render widget. Will be called on the main thread.
+ virtual void SetHasVisibleRenderWidgetWithTouchHandler(
+ bool has_visible_render_widget_with_touch_handler) = 0;
+ };
+
+ explicit RenderWidgetSignals(Observer* observer);
+
+ std::unique_ptr<WebRenderWidgetSchedulingState>
+ NewRenderWidgetSchedulingState();
+
+ void AsValueInto(base::trace_event::TracedValue* state) const;
+
+ private:
+ friend class WebRenderWidgetSchedulingState;
+
+ void IncNumVisibleRenderWidgets();
+ void DecNumVisibleRenderWidgets();
+ void IncNumVisibleRenderWidgetsWithTouchHandlers();
+ void DecNumVisibleRenderWidgetsWithTouchHandlers();
+
+ Observer* observer_; // NOT OWNED
+ int num_visible_render_widgets_;
+ int num_visible_render_widgets_with_touch_handlers_;
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_RENDER_WIDGET_SIGNALS_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/renderer/render_widget_signals_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/renderer/render_widget_signals_unittest.cc
new file mode 100644
index 00000000000..6e55af040dd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/renderer/render_widget_signals_unittest.cc
@@ -0,0 +1,269 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/renderer/render_widget_signals.h"
+
+#include "base/macros.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/scheduler/web_render_widget_scheduling_state.h"
+
+using testing::AnyNumber;
+using testing::Mock;
+using testing::_;
+
+namespace blink {
+namespace scheduler {
+// To avoid symbol collisions in jumbo builds.
+namespace render_widget_signals_unittest {
+
+class MockObserver : public RenderWidgetSignals::Observer {
+ public:
+ MockObserver() = default;
+ virtual ~MockObserver() = default;
+
+ MOCK_METHOD1(SetAllRenderWidgetsHidden, void(bool hidden));
+ MOCK_METHOD1(SetHasVisibleRenderWidgetWithTouchHandler,
+ void(bool has_visible_render_widget_with_touch_handler));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockObserver);
+};
+
+class RenderWidgetSignalsTest : public testing::Test {
+ public:
+ RenderWidgetSignalsTest() = default;
+ ~RenderWidgetSignalsTest() override = default;
+
+ void SetUp() override {
+ mock_observer_.reset(new MockObserver());
+ render_widget_signals_.reset(new RenderWidgetSignals(mock_observer_.get()));
+ }
+
+ void IgnoreWidgetCreationCallbacks() {
+ EXPECT_CALL(*mock_observer_, SetAllRenderWidgetsHidden(false))
+ .Times(AnyNumber());
+ }
+
+ void IgnoreWidgetDestructionCallbacks() {
+ EXPECT_CALL(*mock_observer_, SetAllRenderWidgetsHidden(true))
+ .Times(AnyNumber());
+ }
+
+ std::unique_ptr<MockObserver> mock_observer_;
+ std::unique_ptr<RenderWidgetSignals> render_widget_signals_;
+};
+
+TEST_F(RenderWidgetSignalsTest, RenderWidgetSchedulingStateLifeCycle) {
+ EXPECT_CALL(*mock_observer_, SetAllRenderWidgetsHidden(false)).Times(1);
+ std::unique_ptr<WebRenderWidgetSchedulingState> widget1_state =
+ render_widget_signals_->NewRenderWidgetSchedulingState();
+ Mock::VerifyAndClearExpectations(mock_observer_.get());
+
+ EXPECT_CALL(*mock_observer_, SetAllRenderWidgetsHidden(true)).Times(1);
+}
+
+TEST_F(RenderWidgetSignalsTest, RenderWidget_Hidden) {
+ IgnoreWidgetCreationCallbacks();
+ std::unique_ptr<WebRenderWidgetSchedulingState> widget1_state =
+ render_widget_signals_->NewRenderWidgetSchedulingState();
+ Mock::VerifyAndClearExpectations(mock_observer_.get());
+
+ EXPECT_CALL(*mock_observer_, SetAllRenderWidgetsHidden(true)).Times(1);
+ widget1_state->SetHidden(true);
+ Mock::VerifyAndClearExpectations(mock_observer_.get());
+
+ IgnoreWidgetDestructionCallbacks();
+}
+
+TEST_F(RenderWidgetSignalsTest, RenderWidget_HiddenThreeTimesShownOnce) {
+ IgnoreWidgetCreationCallbacks();
+ std::unique_ptr<WebRenderWidgetSchedulingState> widget1_state =
+ render_widget_signals_->NewRenderWidgetSchedulingState();
+ Mock::VerifyAndClearExpectations(mock_observer_.get());
+
+ EXPECT_CALL(*mock_observer_, SetAllRenderWidgetsHidden(true)).Times(1);
+ widget1_state->SetHidden(true);
+ widget1_state->SetHidden(true);
+ widget1_state->SetHidden(true);
+ Mock::VerifyAndClearExpectations(mock_observer_.get());
+
+ EXPECT_CALL(*mock_observer_, SetAllRenderWidgetsHidden(false)).Times(1);
+ widget1_state->SetHidden(false);
+ Mock::VerifyAndClearExpectations(mock_observer_.get());
+
+ IgnoreWidgetDestructionCallbacks();
+}
+
+TEST_F(RenderWidgetSignalsTest, MultipleRenderWidgetsBecomeHiddenThenVisible) {
+ IgnoreWidgetCreationCallbacks();
+ std::unique_ptr<WebRenderWidgetSchedulingState> widget1_state =
+ render_widget_signals_->NewRenderWidgetSchedulingState();
+ std::unique_ptr<WebRenderWidgetSchedulingState> widget2_state =
+ render_widget_signals_->NewRenderWidgetSchedulingState();
+ std::unique_ptr<WebRenderWidgetSchedulingState> widget3_state =
+ render_widget_signals_->NewRenderWidgetSchedulingState();
+ Mock::VerifyAndClearExpectations(mock_observer_.get());
+
+ // Widgets are initially assumed to be visible so start hiding them, we should
+ // not get any calls to SetAllRenderWidgetsHidden till the last one is hidden.
+ EXPECT_CALL(*mock_observer_, SetAllRenderWidgetsHidden(_)).Times(0);
+ widget1_state->SetHidden(true);
+ widget2_state->SetHidden(true);
+ Mock::VerifyAndClearExpectations(mock_observer_.get());
+
+ EXPECT_CALL(*mock_observer_, SetAllRenderWidgetsHidden(true)).Times(1);
+ widget3_state->SetHidden(true);
+ Mock::VerifyAndClearExpectations(mock_observer_.get());
+
+ // We should get a call back once the first widget is unhidden and no more
+ // after that.
+ EXPECT_CALL(*mock_observer_, SetAllRenderWidgetsHidden(false)).Times(1);
+ widget1_state->SetHidden(false);
+ Mock::VerifyAndClearExpectations(mock_observer_.get());
+
+ EXPECT_CALL(*mock_observer_, SetAllRenderWidgetsHidden(_)).Times(0);
+ widget2_state->SetHidden(false);
+ widget3_state->SetHidden(false);
+ Mock::VerifyAndClearExpectations(mock_observer_.get());
+
+ IgnoreWidgetDestructionCallbacks();
+}
+
+TEST_F(RenderWidgetSignalsTest, TouchHandlerAddedAndRemoved_VisibleWidget) {
+ IgnoreWidgetCreationCallbacks();
+
+ std::unique_ptr<WebRenderWidgetSchedulingState> widget_state =
+ render_widget_signals_->NewRenderWidgetSchedulingState();
+ Mock::VerifyAndClearExpectations(mock_observer_.get());
+
+ EXPECT_CALL(*mock_observer_, SetHasVisibleRenderWidgetWithTouchHandler(true))
+ .Times(1);
+ widget_state->SetHasTouchHandler(true);
+ Mock::VerifyAndClearExpectations(mock_observer_.get());
+
+ EXPECT_CALL(*mock_observer_, SetHasVisibleRenderWidgetWithTouchHandler(false))
+ .Times(1);
+ widget_state->SetHasTouchHandler(false);
+ Mock::VerifyAndClearExpectations(mock_observer_.get());
+
+ IgnoreWidgetDestructionCallbacks();
+}
+
+TEST_F(RenderWidgetSignalsTest,
+ TouchHandlerAddedThriceAndRemovedOnce_VisibleWidget) {
+ IgnoreWidgetCreationCallbacks();
+
+ std::unique_ptr<WebRenderWidgetSchedulingState> widget_state =
+ render_widget_signals_->NewRenderWidgetSchedulingState();
+ Mock::VerifyAndClearExpectations(mock_observer_.get());
+
+ EXPECT_CALL(*mock_observer_, SetHasVisibleRenderWidgetWithTouchHandler(true))
+ .Times(1);
+ widget_state->SetHasTouchHandler(true);
+ widget_state->SetHasTouchHandler(true);
+ widget_state->SetHasTouchHandler(true);
+ Mock::VerifyAndClearExpectations(mock_observer_.get());
+
+ EXPECT_CALL(*mock_observer_, SetHasVisibleRenderWidgetWithTouchHandler(false))
+ .Times(1);
+ widget_state->SetHasTouchHandler(false);
+ Mock::VerifyAndClearExpectations(mock_observer_.get());
+
+ IgnoreWidgetDestructionCallbacks();
+}
+
+TEST_F(RenderWidgetSignalsTest, TouchHandlerAddedAndRemoved_HiddenWidget) {
+ IgnoreWidgetCreationCallbacks();
+
+ std::unique_ptr<WebRenderWidgetSchedulingState> widget_state =
+ render_widget_signals_->NewRenderWidgetSchedulingState();
+ EXPECT_CALL(*mock_observer_, SetAllRenderWidgetsHidden(true)).Times(1);
+ widget_state->SetHidden(true);
+ Mock::VerifyAndClearExpectations(mock_observer_.get());
+
+ EXPECT_CALL(*mock_observer_, SetHasVisibleRenderWidgetWithTouchHandler(_))
+ .Times(0);
+ widget_state->SetHasTouchHandler(true);
+ widget_state->SetHasTouchHandler(false);
+ Mock::VerifyAndClearExpectations(mock_observer_.get());
+
+ IgnoreWidgetDestructionCallbacks();
+}
+
+TEST_F(RenderWidgetSignalsTest,
+ MultipleTouchHandlerAddedAndRemoved_VisibleWidgets) {
+ IgnoreWidgetCreationCallbacks();
+
+ std::unique_ptr<WebRenderWidgetSchedulingState> widget1_state =
+ render_widget_signals_->NewRenderWidgetSchedulingState();
+ std::unique_ptr<WebRenderWidgetSchedulingState> widget2_state =
+ render_widget_signals_->NewRenderWidgetSchedulingState();
+ std::unique_ptr<WebRenderWidgetSchedulingState> widget3_state =
+ render_widget_signals_->NewRenderWidgetSchedulingState();
+ Mock::VerifyAndClearExpectations(mock_observer_.get());
+
+ // We should only get a callback for the first widget with a touch handler.
+ EXPECT_CALL(*mock_observer_, SetHasVisibleRenderWidgetWithTouchHandler(true))
+ .Times(1);
+ widget1_state->SetHasTouchHandler(true);
+ Mock::VerifyAndClearExpectations(mock_observer_.get());
+
+ EXPECT_CALL(*mock_observer_, SetHasVisibleRenderWidgetWithTouchHandler(_))
+ .Times(0);
+ widget2_state->SetHasTouchHandler(true);
+ widget3_state->SetHasTouchHandler(true);
+ Mock::VerifyAndClearExpectations(mock_observer_.get());
+
+ // We should only get a callback when the last touch handler is removed.
+ EXPECT_CALL(*mock_observer_, SetHasVisibleRenderWidgetWithTouchHandler(_))
+ .Times(0);
+ widget1_state->SetHasTouchHandler(false);
+ widget2_state->SetHasTouchHandler(false);
+ Mock::VerifyAndClearExpectations(mock_observer_.get());
+
+ EXPECT_CALL(*mock_observer_, SetHasVisibleRenderWidgetWithTouchHandler(false))
+ .Times(1);
+ widget3_state->SetHasTouchHandler(false);
+ Mock::VerifyAndClearExpectations(mock_observer_.get());
+
+ IgnoreWidgetDestructionCallbacks();
+}
+
+TEST_F(RenderWidgetSignalsTest,
+ TouchHandlerAddedThenWigetDeleted_VisibleWidget) {
+ IgnoreWidgetCreationCallbacks();
+
+ std::unique_ptr<WebRenderWidgetSchedulingState> widget_state =
+ render_widget_signals_->NewRenderWidgetSchedulingState();
+ Mock::VerifyAndClearExpectations(mock_observer_.get());
+
+ EXPECT_CALL(*mock_observer_, SetHasVisibleRenderWidgetWithTouchHandler(true))
+ .Times(1);
+ widget_state->SetHasTouchHandler(true);
+ Mock::VerifyAndClearExpectations(mock_observer_.get());
+
+ EXPECT_CALL(*mock_observer_, SetHasVisibleRenderWidgetWithTouchHandler(false))
+ .Times(1);
+ IgnoreWidgetDestructionCallbacks();
+}
+
+TEST_F(RenderWidgetSignalsTest,
+ TouchHandlerAddedThenWigetDeleted_HiddenWidget) {
+ IgnoreWidgetCreationCallbacks();
+
+ std::unique_ptr<WebRenderWidgetSchedulingState> widget_state =
+ render_widget_signals_->NewRenderWidgetSchedulingState();
+ EXPECT_CALL(*mock_observer_, SetAllRenderWidgetsHidden(true)).Times(1);
+ widget_state->SetHidden(true);
+ Mock::VerifyAndClearExpectations(mock_observer_.get());
+
+ EXPECT_CALL(*mock_observer_, SetHasVisibleRenderWidgetWithTouchHandler(_))
+ .Times(0);
+ IgnoreWidgetDestructionCallbacks();
+}
+
+} // namespace render_widget_signals_unittest
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/renderer/renderer_metrics_helper.cc b/chromium/third_party/blink/renderer/platform/scheduler/renderer/renderer_metrics_helper.cc
new file mode 100644
index 00000000000..859fb62b089
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/renderer/renderer_metrics_helper.cc
@@ -0,0 +1,501 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/renderer/renderer_metrics_helper.h"
+
+#include "base/bind.h"
+#include "base/metrics/histogram_macros.h"
+#include "services/resource_coordinator/public/cpp/resource_coordinator_features.h"
+#include "third_party/blink/public/platform/scheduler/renderer_process_type.h"
+#include "third_party/blink/renderer/platform/instrumentation/resource_coordinator/renderer_resource_coordinator.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
+
+namespace blink {
+namespace scheduler {
+
+#define DURATION_PER_QUEUE_TYPE_METRIC_NAME \
+ "RendererScheduler.TaskDurationPerQueueType2"
+#define COUNT_PER_QUEUE_TYPE_METRIC_NAME \
+ "RendererScheduler.TaskCountPerQueueType"
+#define MAIN_THREAD_LOAD_METRIC_NAME "RendererScheduler.RendererMainThreadLoad5"
+#define EXTENSIONS_MAIN_THREAD_LOAD_METRIC_NAME \
+ MAIN_THREAD_LOAD_METRIC_NAME ".Extension"
+#define DURATION_PER_FRAME_TYPE_METRIC_NAME \
+ "RendererScheduler.TaskDurationPerFrameType2"
+#define DURATION_PER_TASK_TYPE_METRIC_NAME \
+ "RendererScheduler.TaskDurationPerTaskType"
+#define COUNT_PER_FRAME_METRIC_NAME "RendererScheduler.TaskCountPerFrameType"
+#define DURATION_PER_TASK_USE_CASE_NAME \
+ "RendererScheduler.TaskDurationPerUseCase"
+
+enum class MainThreadTaskLoadState { kLow, kHigh, kUnknown };
+
+namespace {
+
+constexpr base::TimeDelta kThreadLoadTrackerReportingInterval =
+ base::TimeDelta::FromSeconds(1);
+constexpr base::TimeDelta kLongIdlePeriodDiscardingThreshold =
+ base::TimeDelta::FromMinutes(3);
+
+} // namespace
+
+RendererMetricsHelper::PerQueueTypeDurationReporters::
+ PerQueueTypeDurationReporters()
+ : overall(DURATION_PER_QUEUE_TYPE_METRIC_NAME),
+ foreground(DURATION_PER_QUEUE_TYPE_METRIC_NAME ".Foreground"),
+ foreground_first_minute(DURATION_PER_QUEUE_TYPE_METRIC_NAME
+ ".Foreground.FirstMinute"),
+ foreground_second_minute(DURATION_PER_QUEUE_TYPE_METRIC_NAME
+ ".Foreground.SecondMinute"),
+ foreground_third_minute(DURATION_PER_QUEUE_TYPE_METRIC_NAME
+ ".Foreground.ThirdMinute"),
+ foreground_after_third_minute(DURATION_PER_QUEUE_TYPE_METRIC_NAME
+ ".Foreground.AfterThirdMinute"),
+ background(DURATION_PER_QUEUE_TYPE_METRIC_NAME ".Background"),
+ background_first_minute(DURATION_PER_QUEUE_TYPE_METRIC_NAME
+ ".Background.FirstMinute"),
+ background_second_minute(DURATION_PER_QUEUE_TYPE_METRIC_NAME
+ ".Background.SecondMinute"),
+ background_third_minute(DURATION_PER_QUEUE_TYPE_METRIC_NAME
+ ".Background.ThirdMinute"),
+ background_fourth_minute(DURATION_PER_QUEUE_TYPE_METRIC_NAME
+ ".Background.FourthMinute"),
+ background_fifth_minute(DURATION_PER_QUEUE_TYPE_METRIC_NAME
+ ".Background.FifthMinute"),
+ background_after_fifth_minute(DURATION_PER_QUEUE_TYPE_METRIC_NAME
+ ".Background.AfterFifthMinute"),
+ background_keep_active_after_fifth_minute(
+ DURATION_PER_QUEUE_TYPE_METRIC_NAME
+ ".Background.KeepAlive.AfterFifthMinute"),
+ hidden(DURATION_PER_QUEUE_TYPE_METRIC_NAME ".Hidden"),
+ visible(DURATION_PER_QUEUE_TYPE_METRIC_NAME ".Visible"),
+ hidden_music(DURATION_PER_QUEUE_TYPE_METRIC_NAME ".HiddenMusic") {}
+
+RendererMetricsHelper::RendererMetricsHelper(
+ MainThreadSchedulerImpl* main_thread_scheduler,
+ base::TimeTicks now,
+ bool renderer_backgrounded)
+ : MetricsHelper(WebThreadType::kMainThread),
+ main_thread_scheduler_(main_thread_scheduler),
+ main_thread_load_tracker_(
+ now,
+ base::BindRepeating(&RendererMetricsHelper::RecordMainThreadTaskLoad,
+ base::Unretained(this)),
+ kThreadLoadTrackerReportingInterval),
+ background_main_thread_load_tracker_(
+ now,
+ base::BindRepeating(
+ &RendererMetricsHelper::RecordBackgroundMainThreadTaskLoad,
+ base::Unretained(this)),
+ kThreadLoadTrackerReportingInterval),
+ foreground_main_thread_load_tracker_(
+ now,
+ base::BindRepeating(
+ &RendererMetricsHelper::RecordForegroundMainThreadTaskLoad,
+ base::Unretained(this)),
+ kThreadLoadTrackerReportingInterval),
+ per_frame_status_duration_reporter_(DURATION_PER_FRAME_TYPE_METRIC_NAME),
+ per_task_type_duration_reporter_(DURATION_PER_TASK_TYPE_METRIC_NAME),
+ no_use_case_per_task_type_duration_reporter_(
+ DURATION_PER_TASK_TYPE_METRIC_NAME ".UseCaseNone"),
+ loading_per_task_type_duration_reporter_(
+ DURATION_PER_TASK_TYPE_METRIC_NAME ".UseCaseLoading"),
+ input_handling_per_task_type_duration_reporter_(
+ DURATION_PER_TASK_TYPE_METRIC_NAME ".UseCaseInputHandling"),
+ foreground_per_task_type_duration_reporter_(
+ DURATION_PER_TASK_TYPE_METRIC_NAME ".Foreground"),
+ background_per_task_type_duration_reporter_(
+ DURATION_PER_TASK_TYPE_METRIC_NAME ".Background"),
+ per_task_use_case_duration_reporter_(DURATION_PER_TASK_USE_CASE_NAME),
+ main_thread_task_load_state_(MainThreadTaskLoadState::kUnknown) {
+ main_thread_load_tracker_.Resume(now);
+ if (renderer_backgrounded) {
+ background_main_thread_load_tracker_.Resume(now);
+ } else {
+ foreground_main_thread_load_tracker_.Resume(now);
+ }
+}
+
+RendererMetricsHelper::~RendererMetricsHelper() = default;
+
+void RendererMetricsHelper::OnRendererForegrounded(base::TimeTicks now) {
+ foreground_main_thread_load_tracker_.Resume(now);
+ background_main_thread_load_tracker_.Pause(now);
+}
+
+void RendererMetricsHelper::OnRendererBackgrounded(base::TimeTicks now) {
+ foreground_main_thread_load_tracker_.Pause(now);
+ background_main_thread_load_tracker_.Resume(now);
+}
+
+void RendererMetricsHelper::OnRendererShutdown(base::TimeTicks now) {
+ foreground_main_thread_load_tracker_.RecordIdle(now);
+ background_main_thread_load_tracker_.RecordIdle(now);
+ main_thread_load_tracker_.RecordIdle(now);
+}
+
+void RendererMetricsHelper::ResetForTest(base::TimeTicks now) {
+ main_thread_load_tracker_ = ThreadLoadTracker(
+ now,
+ base::BindRepeating(&RendererMetricsHelper::RecordMainThreadTaskLoad,
+ base::Unretained(this)),
+ kThreadLoadTrackerReportingInterval);
+
+ background_main_thread_load_tracker_ = ThreadLoadTracker(
+ now,
+ base::BindRepeating(
+ &RendererMetricsHelper::RecordBackgroundMainThreadTaskLoad,
+ base::Unretained(this)),
+ kThreadLoadTrackerReportingInterval);
+
+ foreground_main_thread_load_tracker_ = ThreadLoadTracker(
+ now,
+ base::BindRepeating(
+ &RendererMetricsHelper::RecordForegroundMainThreadTaskLoad,
+ base::Unretained(this)),
+ kThreadLoadTrackerReportingInterval);
+}
+
+namespace {
+
+// Calculates the length of the intersection of two given time intervals.
+base::TimeDelta DurationOfIntervalOverlap(base::TimeTicks start1,
+ base::TimeTicks end1,
+ base::TimeTicks start2,
+ base::TimeTicks end2) {
+ DCHECK_LE(start1, end1);
+ DCHECK_LE(start2, end2);
+ return std::max(std::min(end1, end2) - std::max(start1, start2),
+ base::TimeDelta());
+}
+
+} // namespace
+
+void RendererMetricsHelper::RecordTaskMetrics(
+ MainThreadTaskQueue* queue,
+ const TaskQueue::Task& task,
+ base::TimeTicks start_time,
+ base::TimeTicks end_time,
+ base::Optional<base::TimeDelta> thread_time) {
+ if (ShouldDiscardTask(queue, task, start_time, end_time, thread_time))
+ return;
+
+ MetricsHelper::RecordCommonTaskMetrics(queue, task, start_time, end_time,
+ thread_time);
+
+ MainThreadTaskQueue::QueueType queue_type =
+ queue ? queue->queue_type() : MainThreadTaskQueue::QueueType::kDetached;
+ base::TimeDelta duration = end_time - start_time;
+
+ // Discard anomalously long idle periods.
+ if (last_reported_task_ && start_time - last_reported_task_.value() >
+ kLongIdlePeriodDiscardingThreshold) {
+ main_thread_load_tracker_.Reset(end_time);
+ foreground_main_thread_load_tracker_.Reset(end_time);
+ background_main_thread_load_tracker_.Reset(end_time);
+ return;
+ }
+
+ last_reported_task_ = end_time;
+
+ UMA_HISTOGRAM_CUSTOM_COUNTS("RendererScheduler.TaskTime2",
+ duration.InMicroseconds(), 1, 1000 * 1000, 50);
+
+ // We want to measure thread time here, but for efficiency reasons
+ // we stick with wall time.
+ main_thread_load_tracker_.RecordTaskTime(start_time, end_time);
+ foreground_main_thread_load_tracker_.RecordTaskTime(start_time, end_time);
+ background_main_thread_load_tracker_.RecordTaskTime(start_time, end_time);
+
+ UMA_HISTOGRAM_ENUMERATION(COUNT_PER_QUEUE_TYPE_METRIC_NAME, queue_type,
+ MainThreadTaskQueue::QueueType::kCount);
+
+ if (duration >= base::TimeDelta::FromMilliseconds(16)) {
+ UMA_HISTOGRAM_ENUMERATION(
+ COUNT_PER_QUEUE_TYPE_METRIC_NAME ".LongerThan16ms", queue_type,
+ MainThreadTaskQueue::QueueType::kCount);
+ }
+
+ if (duration >= base::TimeDelta::FromMilliseconds(50)) {
+ UMA_HISTOGRAM_ENUMERATION(
+ COUNT_PER_QUEUE_TYPE_METRIC_NAME ".LongerThan50ms", queue_type,
+ MainThreadTaskQueue::QueueType::kCount);
+ }
+
+ if (duration >= base::TimeDelta::FromMilliseconds(100)) {
+ UMA_HISTOGRAM_ENUMERATION(
+ COUNT_PER_QUEUE_TYPE_METRIC_NAME ".LongerThan100ms", queue_type,
+ MainThreadTaskQueue::QueueType::kCount);
+ }
+
+ if (duration >= base::TimeDelta::FromMilliseconds(150)) {
+ UMA_HISTOGRAM_ENUMERATION(
+ COUNT_PER_QUEUE_TYPE_METRIC_NAME ".LongerThan150ms", queue_type,
+ MainThreadTaskQueue::QueueType::kCount);
+ }
+
+ if (duration >= base::TimeDelta::FromSeconds(1)) {
+ UMA_HISTOGRAM_ENUMERATION(COUNT_PER_QUEUE_TYPE_METRIC_NAME ".LongerThan1s",
+ queue_type,
+ MainThreadTaskQueue::QueueType::kCount);
+ }
+
+ per_queue_type_reporters_.overall.RecordTask(queue_type, duration);
+
+ TaskType task_type = static_cast<TaskType>(task.task_type());
+ per_task_type_duration_reporter_.RecordTask(task_type, duration);
+
+ if (main_thread_scheduler_->main_thread_only().renderer_backgrounded) {
+ per_queue_type_reporters_.background.RecordTask(queue_type, duration);
+
+ // Collect detailed breakdown for first five minutes given that we stop
+ // timers on mobile after five minutes.
+ base::TimeTicks backgrounded_at =
+ main_thread_scheduler_->main_thread_only().background_status_changed_at;
+
+ per_queue_type_reporters_.background_first_minute.RecordTask(
+ queue_type, DurationOfIntervalOverlap(
+ start_time, end_time, backgrounded_at,
+ backgrounded_at + base::TimeDelta::FromMinutes(1)));
+
+ per_queue_type_reporters_.background_second_minute.RecordTask(
+ queue_type, DurationOfIntervalOverlap(
+ start_time, end_time,
+ backgrounded_at + base::TimeDelta::FromMinutes(1),
+ backgrounded_at + base::TimeDelta::FromMinutes(2)));
+
+ per_queue_type_reporters_.background_third_minute.RecordTask(
+ queue_type, DurationOfIntervalOverlap(
+ start_time, end_time,
+ backgrounded_at + base::TimeDelta::FromMinutes(2),
+ backgrounded_at + base::TimeDelta::FromMinutes(3)));
+
+ per_queue_type_reporters_.background_fourth_minute.RecordTask(
+ queue_type, DurationOfIntervalOverlap(
+ start_time, end_time,
+ backgrounded_at + base::TimeDelta::FromMinutes(3),
+ backgrounded_at + base::TimeDelta::FromMinutes(4)));
+
+ per_queue_type_reporters_.background_fifth_minute.RecordTask(
+ queue_type, DurationOfIntervalOverlap(
+ start_time, end_time,
+ backgrounded_at + base::TimeDelta::FromMinutes(4),
+ backgrounded_at + base::TimeDelta::FromMinutes(5)));
+
+ per_queue_type_reporters_.background_after_fifth_minute.RecordTask(
+ queue_type,
+ DurationOfIntervalOverlap(
+ start_time, end_time,
+ backgrounded_at + base::TimeDelta::FromMinutes(5),
+ std::max(backgrounded_at + base::TimeDelta::FromMinutes(5),
+ end_time)));
+
+ if (main_thread_scheduler_->main_thread_only()
+ .keep_active_fetch_or_worker) {
+ per_queue_type_reporters_.background_keep_active_after_fifth_minute
+ .RecordTask(
+ queue_type,
+ DurationOfIntervalOverlap(
+ start_time, end_time,
+ backgrounded_at + base::TimeDelta::FromMinutes(5),
+ std::max(backgrounded_at + base::TimeDelta::FromMinutes(5),
+ end_time)));
+ }
+
+ background_per_task_type_duration_reporter_.RecordTask(task_type, duration);
+ } else {
+ per_queue_type_reporters_.foreground.RecordTask(queue_type, duration);
+
+ // For foreground tabs we do not expect such a notable difference as it is
+ // the case with background tabs, so we limit breakdown to three minutes.
+ base::TimeTicks foregrounded_at =
+ main_thread_scheduler_->main_thread_only().background_status_changed_at;
+
+ per_queue_type_reporters_.foreground_first_minute.RecordTask(
+ queue_type, DurationOfIntervalOverlap(
+ start_time, end_time, foregrounded_at,
+ foregrounded_at + base::TimeDelta::FromMinutes(1)));
+
+ per_queue_type_reporters_.foreground_second_minute.RecordTask(
+ queue_type, DurationOfIntervalOverlap(
+ start_time, end_time,
+ foregrounded_at + base::TimeDelta::FromMinutes(1),
+ foregrounded_at + base::TimeDelta::FromMinutes(2)));
+
+ per_queue_type_reporters_.foreground_third_minute.RecordTask(
+ queue_type, DurationOfIntervalOverlap(
+ start_time, end_time,
+ foregrounded_at + base::TimeDelta::FromMinutes(2),
+ foregrounded_at + base::TimeDelta::FromMinutes(3)));
+
+ per_queue_type_reporters_.foreground_after_third_minute.RecordTask(
+ queue_type,
+ DurationOfIntervalOverlap(
+ start_time, end_time,
+ foregrounded_at + base::TimeDelta::FromMinutes(3),
+ std::max(foregrounded_at + base::TimeDelta::FromMinutes(3),
+ end_time)));
+
+ foreground_per_task_type_duration_reporter_.RecordTask(task_type, duration);
+ }
+
+ if (main_thread_scheduler_->main_thread_only().renderer_hidden) {
+ per_queue_type_reporters_.hidden.RecordTask(queue_type, duration);
+
+ if (main_thread_scheduler_->ShouldDisableThrottlingBecauseOfAudio(
+ start_time)) {
+ per_queue_type_reporters_.hidden_music.RecordTask(queue_type, duration);
+ }
+ } else {
+ per_queue_type_reporters_.visible.RecordTask(queue_type, duration);
+ }
+
+ FrameStatus frame_status =
+ GetFrameStatus(queue ? queue->GetFrameScheduler() : nullptr);
+ per_frame_status_duration_reporter_.RecordTask(frame_status, duration);
+ UMA_HISTOGRAM_ENUMERATION(COUNT_PER_FRAME_METRIC_NAME, frame_status,
+ FrameStatus::kCount);
+ if (duration >= base::TimeDelta::FromMilliseconds(16)) {
+ UMA_HISTOGRAM_ENUMERATION(COUNT_PER_FRAME_METRIC_NAME ".LongerThan16ms",
+ frame_status, FrameStatus::kCount);
+ }
+
+ if (duration >= base::TimeDelta::FromMilliseconds(50)) {
+ UMA_HISTOGRAM_ENUMERATION(COUNT_PER_FRAME_METRIC_NAME ".LongerThan50ms",
+ frame_status, FrameStatus::kCount);
+ }
+
+ if (duration >= base::TimeDelta::FromMilliseconds(100)) {
+ UMA_HISTOGRAM_ENUMERATION(COUNT_PER_FRAME_METRIC_NAME ".LongerThan100ms",
+ frame_status, FrameStatus::kCount);
+ }
+
+ if (duration >= base::TimeDelta::FromMilliseconds(150)) {
+ UMA_HISTOGRAM_ENUMERATION(COUNT_PER_FRAME_METRIC_NAME ".LongerThan150ms",
+ frame_status, FrameStatus::kCount);
+ }
+
+ if (duration >= base::TimeDelta::FromSeconds(1)) {
+ UMA_HISTOGRAM_ENUMERATION(COUNT_PER_FRAME_METRIC_NAME ".LongerThan1s",
+ frame_status, FrameStatus::kCount);
+ }
+
+ UseCase use_case =
+ main_thread_scheduler_->main_thread_only().current_use_case;
+ per_task_use_case_duration_reporter_.RecordTask(use_case, duration);
+ if (use_case == UseCase::kNone) {
+ no_use_case_per_task_type_duration_reporter_.RecordTask(task_type,
+ duration);
+ } else if (use_case == UseCase::kLoading) {
+ loading_per_task_type_duration_reporter_.RecordTask(task_type, duration);
+ } else {
+ input_handling_per_task_type_duration_reporter_.RecordTask(task_type,
+ duration);
+ }
+}
+
+void RendererMetricsHelper::RecordMainThreadTaskLoad(base::TimeTicks time,
+ double load) {
+ int load_percentage = static_cast<int>(load * 100);
+ DCHECK_LE(load_percentage, 100);
+
+ if (::resource_coordinator::IsPageAlmostIdleSignalEnabled()) {
+ static const int main_thread_task_load_low_threshold =
+ ::resource_coordinator::GetMainThreadTaskLoadLowThreshold();
+
+ // Avoid sending duplicate IPCs when the state doesn't change.
+ if (load_percentage <= main_thread_task_load_low_threshold &&
+ main_thread_task_load_state_ != MainThreadTaskLoadState::kLow) {
+ RendererResourceCoordinator::Get().SetMainThreadTaskLoadIsLow(true);
+ main_thread_task_load_state_ = MainThreadTaskLoadState::kLow;
+ } else if (load_percentage > main_thread_task_load_low_threshold &&
+ main_thread_task_load_state_ != MainThreadTaskLoadState::kHigh) {
+ RendererResourceCoordinator::Get().SetMainThreadTaskLoadIsLow(false);
+ main_thread_task_load_state_ = MainThreadTaskLoadState::kHigh;
+ }
+ }
+
+ UMA_HISTOGRAM_PERCENTAGE(MAIN_THREAD_LOAD_METRIC_NAME, load_percentage);
+
+ if (main_thread_scheduler_->main_thread_only().process_type ==
+ RendererProcessType::kExtensionRenderer) {
+ UMA_HISTOGRAM_PERCENTAGE(EXTENSIONS_MAIN_THREAD_LOAD_METRIC_NAME,
+ load_percentage);
+ }
+
+ TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "MainThreadScheduler.RendererMainThreadLoad", load_percentage);
+}
+
+void RendererMetricsHelper::RecordForegroundMainThreadTaskLoad(
+ base::TimeTicks time,
+ double load) {
+ int load_percentage = static_cast<int>(load * 100);
+ DCHECK_LE(load_percentage, 100);
+
+ switch (main_thread_scheduler_->main_thread_only().process_type) {
+ case RendererProcessType::kExtensionRenderer:
+ UMA_HISTOGRAM_PERCENTAGE(EXTENSIONS_MAIN_THREAD_LOAD_METRIC_NAME
+ ".Foreground",
+ load_percentage);
+ break;
+ case RendererProcessType::kRenderer:
+ UMA_HISTOGRAM_PERCENTAGE(MAIN_THREAD_LOAD_METRIC_NAME ".Foreground",
+ load_percentage);
+
+ if (time - main_thread_scheduler_->main_thread_only()
+ .background_status_changed_at >
+ base::TimeDelta::FromMinutes(1)) {
+ UMA_HISTOGRAM_PERCENTAGE(MAIN_THREAD_LOAD_METRIC_NAME
+ ".Foreground.AfterFirstMinute",
+ load_percentage);
+ }
+ break;
+ }
+
+ TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "MainThreadScheduler.RendererMainThreadLoad.Foreground",
+ load_percentage);
+}
+
+void RendererMetricsHelper::RecordBackgroundMainThreadTaskLoad(
+ base::TimeTicks time,
+ double load) {
+ int load_percentage = static_cast<int>(load * 100);
+ DCHECK_LE(load_percentage, 100);
+
+ switch (main_thread_scheduler_->main_thread_only().process_type) {
+ case RendererProcessType::kExtensionRenderer:
+ UMA_HISTOGRAM_PERCENTAGE(EXTENSIONS_MAIN_THREAD_LOAD_METRIC_NAME
+ ".Background",
+ load_percentage);
+ break;
+ case RendererProcessType::kRenderer:
+ UMA_HISTOGRAM_PERCENTAGE(MAIN_THREAD_LOAD_METRIC_NAME ".Background",
+ load_percentage);
+
+ if (time - main_thread_scheduler_->main_thread_only()
+ .background_status_changed_at >
+ base::TimeDelta::FromMinutes(1)) {
+ UMA_HISTOGRAM_PERCENTAGE(MAIN_THREAD_LOAD_METRIC_NAME
+ ".Background.AfterFirstMinute",
+ load_percentage);
+ }
+ break;
+ }
+
+ TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "MainThreadScheduler.RendererMainThreadLoad.Background",
+ load_percentage);
+}
+
+// static
+void RendererMetricsHelper::RecordBackgroundedTransition(
+ BackgroundedRendererTransition transition) {
+ UMA_HISTOGRAM_ENUMERATION("RendererScheduler.BackgroundedRendererTransition",
+ transition, BackgroundedRendererTransition::kCount);
+}
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/renderer/renderer_metrics_helper.h b/chromium/third_party/blink/renderer/platform/scheduler/renderer/renderer_metrics_helper.h
new file mode 100644
index 00000000000..c8dd67a9f83
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/renderer/renderer_metrics_helper.h
@@ -0,0 +1,144 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_RENDERER_METRICS_HELPER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_RENDERER_METRICS_HELPER_H_
+
+#include "base/macros.h"
+#include "base/optional.h"
+#include "base/time/time.h"
+#include "third_party/blink/public/platform/task_type.h"
+#include "third_party/blink/public/platform/web_thread_type.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/scheduler/child/metrics_helper.h"
+#include "third_party/blink/renderer/platform/scheduler/renderer/frame_status.h"
+#include "third_party/blink/renderer/platform/scheduler/renderer/main_thread_task_queue.h"
+#include "third_party/blink/renderer/platform/scheduler/renderer/use_case.h"
+#include "third_party/blink/renderer/platform/scheduler/util/task_duration_metric_reporter.h"
+#include "third_party/blink/renderer/platform/scheduler/util/thread_load_tracker.h"
+
+namespace blink {
+namespace scheduler {
+
+enum class MainThreadTaskLoadState;
+class MainThreadTaskQueue;
+class MainThreadSchedulerImpl;
+
+// This enum is used for histogram and should not be renumbered.
+// It tracks the following possible transitions:
+// -> kBackgrounded (-> [STOPPED_* -> kResumed])? -> kForegrounded
+enum class BackgroundedRendererTransition {
+ // Renderer is backgrounded
+ kBackgrounded = 0,
+ // Renderer is stopped after being backgrounded for a while
+ kStoppedAfterDelay = 1,
+ // Renderer is stopped due to critical resources, reserved for future use.
+ kStoppedDueToCriticalResources = 2,
+ // Renderer is resumed after being stopped
+ kResumed = 3,
+ // Renderer is foregrounded
+ kForegrounded = 4,
+
+ kCount = 5
+};
+
+// Helper class to take care of metrics on behalf of MainThreadScheduler.
+// This class should be used only on the main thread.
+class PLATFORM_EXPORT RendererMetricsHelper : public MetricsHelper {
+ public:
+ static void RecordBackgroundedTransition(
+ BackgroundedRendererTransition transition);
+
+ RendererMetricsHelper(MainThreadSchedulerImpl* main_thread_scheduler,
+ base::TimeTicks now,
+ bool renderer_backgrounded);
+ ~RendererMetricsHelper();
+
+ void RecordTaskMetrics(MainThreadTaskQueue* queue,
+ const TaskQueue::Task& task,
+ base::TimeTicks start_time,
+ base::TimeTicks end_time,
+ base::Optional<base::TimeDelta> thread_time);
+
+ void OnRendererForegrounded(base::TimeTicks now);
+ void OnRendererBackgrounded(base::TimeTicks now);
+ void OnRendererShutdown(base::TimeTicks now);
+
+ void RecordMainThreadTaskLoad(base::TimeTicks time, double load);
+ void RecordForegroundMainThreadTaskLoad(base::TimeTicks time, double load);
+ void RecordBackgroundMainThreadTaskLoad(base::TimeTicks time, double load);
+
+ void ResetForTest(base::TimeTicks now);
+
+ private:
+ MainThreadSchedulerImpl* main_thread_scheduler_; // NOT OWNED
+
+ base::Optional<base::TimeTicks> last_reported_task_;
+
+ ThreadLoadTracker main_thread_load_tracker_;
+ ThreadLoadTracker background_main_thread_load_tracker_;
+ ThreadLoadTracker foreground_main_thread_load_tracker_;
+
+ using TaskDurationPerQueueTypeMetricReporter =
+ TaskDurationMetricReporter<MainThreadTaskQueue::QueueType>;
+
+ struct PerQueueTypeDurationReporters {
+ PerQueueTypeDurationReporters();
+
+ TaskDurationPerQueueTypeMetricReporter overall;
+ TaskDurationPerQueueTypeMetricReporter foreground;
+ TaskDurationPerQueueTypeMetricReporter foreground_first_minute;
+ TaskDurationPerQueueTypeMetricReporter foreground_second_minute;
+ TaskDurationPerQueueTypeMetricReporter foreground_third_minute;
+ TaskDurationPerQueueTypeMetricReporter foreground_after_third_minute;
+ TaskDurationPerQueueTypeMetricReporter background;
+ TaskDurationPerQueueTypeMetricReporter background_first_minute;
+ TaskDurationPerQueueTypeMetricReporter background_second_minute;
+ TaskDurationPerQueueTypeMetricReporter background_third_minute;
+ TaskDurationPerQueueTypeMetricReporter background_fourth_minute;
+ TaskDurationPerQueueTypeMetricReporter background_fifth_minute;
+ TaskDurationPerQueueTypeMetricReporter background_after_fifth_minute;
+ TaskDurationPerQueueTypeMetricReporter
+ background_keep_active_after_fifth_minute;
+ TaskDurationPerQueueTypeMetricReporter hidden;
+ TaskDurationPerQueueTypeMetricReporter visible;
+ TaskDurationPerQueueTypeMetricReporter hidden_music;
+ };
+
+ PerQueueTypeDurationReporters per_queue_type_reporters_;
+
+ TaskDurationMetricReporter<FrameStatus> per_frame_status_duration_reporter_;
+
+ using TaskDurationPerTaskTypeMetricReporter =
+ TaskDurationMetricReporter<TaskType>;
+
+ TaskDurationPerTaskTypeMetricReporter per_task_type_duration_reporter_;
+
+ // The next three reporters are used to report the duration per task type
+ // split by renderer scheduler use case (check use_case.h for reference):
+ // None, Loading, and User Input (aggregation of multiple input-handling
+ // related use cases).
+ TaskDurationPerTaskTypeMetricReporter
+ no_use_case_per_task_type_duration_reporter_;
+ TaskDurationPerTaskTypeMetricReporter
+ loading_per_task_type_duration_reporter_;
+ TaskDurationPerTaskTypeMetricReporter
+ input_handling_per_task_type_duration_reporter_;
+
+ TaskDurationPerTaskTypeMetricReporter
+ foreground_per_task_type_duration_reporter_;
+ TaskDurationPerTaskTypeMetricReporter
+ background_per_task_type_duration_reporter_;
+
+ TaskDurationMetricReporter<UseCase> per_task_use_case_duration_reporter_;
+
+ MainThreadTaskLoadState main_thread_task_load_state_;
+
+ DISALLOW_COPY_AND_ASSIGN(RendererMetricsHelper);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_RENDERER_METRICS_HELPER_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/renderer/renderer_metrics_helper_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/renderer/renderer_metrics_helper_unittest.cc
new file mode 100644
index 00000000000..34f40ea9c16
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/renderer/renderer_metrics_helper_unittest.cc
@@ -0,0 +1,607 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/renderer/renderer_metrics_helper.h"
+
+#include <memory>
+#include "base/macros.h"
+#include "base/test/histogram_tester.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "components/viz/test/ordered_simple_task_runner.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/page/launching_process_state.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
+#include "third_party/blink/renderer/platform/scheduler/test/fake_frame_scheduler.h"
+#include "third_party/blink/renderer/platform/scheduler/test/fake_page_scheduler.h"
+#include "third_party/blink/renderer/platform/scheduler/test/task_queue_manager_for_test.h"
+
+namespace blink {
+namespace scheduler {
+
+namespace {
+class MainThreadSchedulerImplForTest : public MainThreadSchedulerImpl {
+ public:
+ MainThreadSchedulerImplForTest(
+ std::unique_ptr<TaskQueueManager> task_queue_manager,
+ base::Optional<base::Time> initial_virtual_time)
+ : MainThreadSchedulerImpl(std::move(task_queue_manager),
+ initial_virtual_time){};
+
+ using MainThreadSchedulerImpl::SetCurrentUseCaseForTest;
+};
+} // namespace
+
+using QueueType = MainThreadTaskQueue::QueueType;
+using base::Bucket;
+using testing::ElementsAre;
+using testing::UnorderedElementsAre;
+
+class RendererMetricsHelperTest : public testing::Test {
+ public:
+ RendererMetricsHelperTest() = default;
+ ~RendererMetricsHelperTest() = default;
+
+ void SetUp() {
+ histogram_tester_.reset(new base::HistogramTester());
+ mock_task_runner_ =
+ base::MakeRefCounted<cc::OrderedSimpleTaskRunner>(&clock_, true);
+ scheduler_ = std::make_unique<MainThreadSchedulerImplForTest>(
+ TaskQueueManagerForTest::Create(nullptr, mock_task_runner_, &clock_),
+ base::nullopt);
+ metrics_helper_ = &scheduler_->main_thread_only().metrics_helper;
+ }
+
+ void TearDown() {
+ scheduler_->Shutdown();
+ scheduler_.reset();
+ }
+
+ void RunTask(MainThreadTaskQueue::QueueType queue_type,
+ base::TimeTicks start,
+ base::TimeDelta duration) {
+ DCHECK_LE(clock_.NowTicks(), start);
+ clock_.SetNowTicks(start + duration);
+ scoped_refptr<MainThreadTaskQueueForTest> queue;
+ if (queue_type != MainThreadTaskQueue::QueueType::kDetached) {
+ queue = scoped_refptr<MainThreadTaskQueueForTest>(
+ new MainThreadTaskQueueForTest(queue_type));
+ }
+
+ // Pass an empty task for recording.
+ TaskQueue::PostedTask posted_task(base::OnceClosure(), FROM_HERE);
+ TaskQueue::Task task(std::move(posted_task), base::TimeTicks());
+ metrics_helper_->RecordTaskMetrics(queue.get(), task, start,
+ start + duration, base::nullopt);
+ }
+
+ void RunTask(FrameScheduler* scheduler,
+ base::TimeTicks start,
+ base::TimeDelta duration) {
+ DCHECK_LE(clock_.NowTicks(), start);
+ clock_.SetNowTicks(start + duration);
+ scoped_refptr<MainThreadTaskQueueForTest> queue(
+ new MainThreadTaskQueueForTest(QueueType::kDefault));
+ queue->SetFrameScheduler(scheduler);
+ // Pass an empty task for recording.
+ TaskQueue::PostedTask posted_task(base::OnceClosure(), FROM_HERE);
+ TaskQueue::Task task(std::move(posted_task), base::TimeTicks());
+ metrics_helper_->RecordTaskMetrics(queue.get(), task, start,
+ start + duration, base::nullopt);
+ }
+
+ void RunTask(UseCase use_case,
+ base::TimeTicks start,
+ base::TimeDelta duration) {
+ DCHECK_LE(clock_.NowTicks(), start);
+ clock_.SetNowTicks(start + duration);
+ scoped_refptr<MainThreadTaskQueueForTest> queue(
+ new MainThreadTaskQueueForTest(QueueType::kDefault));
+ scheduler_->SetCurrentUseCaseForTest(use_case);
+ // Pass an empty task for recording.
+ TaskQueue::PostedTask posted_task(base::OnceClosure(), FROM_HERE);
+ TaskQueue::Task task(std::move(posted_task), base::TimeTicks());
+ metrics_helper_->RecordTaskMetrics(queue.get(), task, start,
+ start + duration, base::nullopt);
+ }
+
+ base::TimeTicks Milliseconds(int milliseconds) {
+ return base::TimeTicks() + base::TimeDelta::FromMilliseconds(milliseconds);
+ }
+
+ void ForceUpdatePolicy() { scheduler_->ForceUpdatePolicy(); }
+
+ std::unique_ptr<FakeFrameScheduler> CreateFakeFrameSchedulerWithType(
+ FrameStatus frame_status) {
+ FakeFrameScheduler::Builder builder;
+ switch (frame_status) {
+ case FrameStatus::kNone:
+ case FrameStatus::kDetached:
+ return nullptr;
+ case FrameStatus::kMainFrameVisible:
+ builder.SetFrameType(FrameScheduler::FrameType::kMainFrame)
+ .SetIsPageVisible(true)
+ .SetIsFrameVisible(true);
+ break;
+ case FrameStatus::kMainFrameVisibleService:
+ builder.SetFrameType(FrameScheduler::FrameType::kMainFrame)
+ .SetPageScheduler(playing_view_.get())
+ .SetIsFrameVisible(true);
+ break;
+ case FrameStatus::kMainFrameHidden:
+ builder.SetFrameType(FrameScheduler::FrameType::kMainFrame)
+ .SetIsPageVisible(true);
+ break;
+ case FrameStatus::kMainFrameHiddenService:
+ builder.SetFrameType(FrameScheduler::FrameType::kMainFrame)
+ .SetPageScheduler(playing_view_.get());
+ break;
+ case FrameStatus::kMainFrameBackground:
+ builder.SetFrameType(FrameScheduler::FrameType::kMainFrame);
+ break;
+ case FrameStatus::kMainFrameBackgroundExemptSelf:
+ builder.SetFrameType(FrameScheduler::FrameType::kMainFrame)
+ .SetIsExemptFromThrottling(true);
+ break;
+ case FrameStatus::kMainFrameBackgroundExemptOther:
+ builder.SetFrameType(FrameScheduler::FrameType::kMainFrame)
+ .SetPageScheduler(throtting_exempt_view_.get());
+ break;
+ case FrameStatus::kSameOriginVisible:
+ builder.SetFrameType(FrameScheduler::FrameType::kSubframe)
+ .SetIsPageVisible(true)
+ .SetIsFrameVisible(true);
+ break;
+ case FrameStatus::kSameOriginVisibleService:
+ builder.SetFrameType(FrameScheduler::FrameType::kSubframe)
+ .SetPageScheduler(playing_view_.get())
+ .SetIsFrameVisible(true);
+ break;
+ case FrameStatus::kSameOriginHidden:
+ builder.SetFrameType(FrameScheduler::FrameType::kSubframe)
+ .SetIsPageVisible(true);
+ break;
+ case FrameStatus::kSameOriginHiddenService:
+ builder.SetFrameType(FrameScheduler::FrameType::kSubframe)
+ .SetPageScheduler(playing_view_.get());
+ break;
+ case FrameStatus::kSameOriginBackground:
+ builder.SetFrameType(FrameScheduler::FrameType::kSubframe);
+ break;
+ case FrameStatus::kSameOriginBackgroundExemptSelf:
+ builder.SetFrameType(FrameScheduler::FrameType::kSubframe)
+ .SetIsExemptFromThrottling(true);
+ break;
+ case FrameStatus::kSameOriginBackgroundExemptOther:
+ builder.SetFrameType(FrameScheduler::FrameType::kSubframe)
+ .SetPageScheduler(throtting_exempt_view_.get());
+ break;
+ case FrameStatus::kCrossOriginVisible:
+ builder.SetFrameType(FrameScheduler::FrameType::kSubframe)
+ .SetIsCrossOrigin(true)
+ .SetIsPageVisible(true)
+ .SetIsFrameVisible(true);
+ break;
+ case FrameStatus::kCrossOriginVisibleService:
+ builder.SetFrameType(FrameScheduler::FrameType::kSubframe)
+ .SetIsCrossOrigin(true)
+ .SetPageScheduler(playing_view_.get())
+ .SetIsFrameVisible(true);
+ break;
+ case FrameStatus::kCrossOriginHidden:
+ builder.SetFrameType(FrameScheduler::FrameType::kSubframe)
+ .SetIsCrossOrigin(true)
+ .SetIsPageVisible(true);
+ break;
+ case FrameStatus::kCrossOriginHiddenService:
+ builder.SetFrameType(FrameScheduler::FrameType::kSubframe)
+ .SetIsCrossOrigin(true)
+ .SetPageScheduler(playing_view_.get());
+ break;
+ case FrameStatus::kCrossOriginBackground:
+ builder.SetFrameType(FrameScheduler::FrameType::kSubframe)
+ .SetIsCrossOrigin(true);
+ break;
+ case FrameStatus::kCrossOriginBackgroundExemptSelf:
+ builder.SetFrameType(FrameScheduler::FrameType::kSubframe)
+ .SetIsCrossOrigin(true)
+ .SetIsExemptFromThrottling(true);
+ break;
+ case FrameStatus::kCrossOriginBackgroundExemptOther:
+ builder.SetFrameType(FrameScheduler::FrameType::kSubframe)
+ .SetIsCrossOrigin(true)
+ .SetPageScheduler(throtting_exempt_view_.get());
+ break;
+ case FrameStatus::kCount:
+ NOTREACHED();
+ return nullptr;
+ }
+ return builder.Build();
+ }
+
+ base::SimpleTestTickClock clock_;
+ scoped_refptr<cc::OrderedSimpleTaskRunner> mock_task_runner_;
+ std::unique_ptr<MainThreadSchedulerImplForTest> scheduler_;
+ RendererMetricsHelper* metrics_helper_; // NOT OWNED
+ std::unique_ptr<base::HistogramTester> histogram_tester_;
+ std::unique_ptr<FakePageScheduler> playing_view_ =
+ FakePageScheduler::Builder().SetIsPlayingAudio(true).Build();
+ std::unique_ptr<FakePageScheduler> throtting_exempt_view_ =
+ FakePageScheduler::Builder().SetIsThrottlingExempt(true).Build();
+
+ DISALLOW_COPY_AND_ASSIGN(RendererMetricsHelperTest);
+};
+
+TEST_F(RendererMetricsHelperTest, Metrics_PerQueueType) {
+ // QueueType::kDefault is checking sub-millisecond task aggregation,
+ // FRAME_* tasks are checking normal task aggregation and other
+ // queue types have a single task.
+
+ // Make sure that it starts in a foregrounded state.
+ if (kLaunchingProcessIsBackgrounded)
+ scheduler_->SetRendererBackgrounded(false);
+
+ RunTask(QueueType::kDefault, Milliseconds(1),
+ base::TimeDelta::FromMicroseconds(700));
+ RunTask(QueueType::kDefault, Milliseconds(2),
+ base::TimeDelta::FromMicroseconds(700));
+ RunTask(QueueType::kDefault, Milliseconds(3),
+ base::TimeDelta::FromMicroseconds(700));
+
+ RunTask(QueueType::kControl, Milliseconds(400),
+ base::TimeDelta::FromMilliseconds(30));
+ RunTask(QueueType::kFrameLoading, Milliseconds(800),
+ base::TimeDelta::FromMilliseconds(70));
+ RunTask(QueueType::kFramePausable, Milliseconds(1000),
+ base::TimeDelta::FromMilliseconds(20));
+ RunTask(QueueType::kCompositor, Milliseconds(1200),
+ base::TimeDelta::FromMilliseconds(25));
+ RunTask(QueueType::kTest, Milliseconds(1600),
+ base::TimeDelta::FromMilliseconds(85));
+
+ scheduler_->SetRendererBackgrounded(true);
+
+ RunTask(QueueType::kControl, Milliseconds(2000),
+ base::TimeDelta::FromMilliseconds(25));
+ RunTask(QueueType::kFrameThrottleable, Milliseconds(2600),
+ base::TimeDelta::FromMilliseconds(175));
+ RunTask(QueueType::kUnthrottled, Milliseconds(2800),
+ base::TimeDelta::FromMilliseconds(25));
+ RunTask(QueueType::kFrameLoading, Milliseconds(3000),
+ base::TimeDelta::FromMilliseconds(35));
+ RunTask(QueueType::kFrameThrottleable, Milliseconds(3200),
+ base::TimeDelta::FromMilliseconds(5));
+ RunTask(QueueType::kCompositor, Milliseconds(3400),
+ base::TimeDelta::FromMilliseconds(20));
+ RunTask(QueueType::kIdle, Milliseconds(3600),
+ base::TimeDelta::FromMilliseconds(50));
+ RunTask(QueueType::kFrameLoadingControl, Milliseconds(4000),
+ base::TimeDelta::FromMilliseconds(5));
+ RunTask(QueueType::kControl, Milliseconds(4200),
+ base::TimeDelta::FromMilliseconds(20));
+ RunTask(QueueType::kFrameThrottleable, Milliseconds(4400),
+ base::TimeDelta::FromMilliseconds(115));
+ RunTask(QueueType::kFramePausable, Milliseconds(4600),
+ base::TimeDelta::FromMilliseconds(175));
+ RunTask(QueueType::kIdle, Milliseconds(5000),
+ base::TimeDelta::FromMilliseconds(1600));
+
+ RunTask(QueueType::kDetached, Milliseconds(8000),
+ base::TimeDelta::FromMilliseconds(150));
+
+ std::vector<base::Bucket> expected_samples = {
+ {static_cast<int>(QueueType::kControl), 75},
+ {static_cast<int>(QueueType::kDefault), 2},
+ {static_cast<int>(QueueType::kUnthrottled), 25},
+ {static_cast<int>(QueueType::kFrameLoading), 105},
+ {static_cast<int>(QueueType::kCompositor), 45},
+ {static_cast<int>(QueueType::kIdle), 1650},
+ {static_cast<int>(QueueType::kTest), 85},
+ {static_cast<int>(QueueType::kFrameLoadingControl), 5},
+ {static_cast<int>(QueueType::kFrameThrottleable), 295},
+ {static_cast<int>(QueueType::kFramePausable), 195},
+ {static_cast<int>(QueueType::kDetached), 150},
+ };
+ EXPECT_THAT(histogram_tester_->GetAllSamples(
+ "RendererScheduler.TaskDurationPerQueueType2"),
+ testing::ContainerEq(expected_samples));
+
+ EXPECT_THAT(histogram_tester_->GetAllSamples(
+ "RendererScheduler.TaskDurationPerQueueType2.Foreground"),
+ UnorderedElementsAre(
+ Bucket(static_cast<int>(QueueType::kControl), 30),
+ Bucket(static_cast<int>(QueueType::kDefault), 2),
+ Bucket(static_cast<int>(QueueType::kFrameLoading), 70),
+ Bucket(static_cast<int>(QueueType::kCompositor), 25),
+ Bucket(static_cast<int>(QueueType::kTest), 85),
+ Bucket(static_cast<int>(QueueType::kFramePausable), 20)));
+
+ EXPECT_THAT(histogram_tester_->GetAllSamples(
+ "RendererScheduler.TaskDurationPerQueueType2.Background"),
+ UnorderedElementsAre(
+ Bucket(static_cast<int>(QueueType::kControl), 45),
+ Bucket(static_cast<int>(QueueType::kUnthrottled), 25),
+ Bucket(static_cast<int>(QueueType::kFrameLoading), 35),
+ Bucket(static_cast<int>(QueueType::kFrameThrottleable), 295),
+ Bucket(static_cast<int>(QueueType::kFramePausable), 175),
+ Bucket(static_cast<int>(QueueType::kCompositor), 20),
+ Bucket(static_cast<int>(QueueType::kIdle), 1650),
+ Bucket(static_cast<int>(QueueType::kFrameLoadingControl), 5),
+ Bucket(static_cast<int>(QueueType::kDetached), 150)));
+}
+
+TEST_F(RendererMetricsHelperTest, Metrics_PerUseCase) {
+ RunTask(UseCase::kNone, Milliseconds(500),
+ base::TimeDelta::FromMilliseconds(4000));
+
+ RunTask(UseCase::kTouchstart, Milliseconds(7000),
+ base::TimeDelta::FromMilliseconds(25));
+ RunTask(UseCase::kTouchstart, Milliseconds(7050),
+ base::TimeDelta::FromMilliseconds(25));
+ RunTask(UseCase::kTouchstart, Milliseconds(7100),
+ base::TimeDelta::FromMilliseconds(25));
+
+ RunTask(UseCase::kCompositorGesture, Milliseconds(7150),
+ base::TimeDelta::FromMilliseconds(5));
+ RunTask(UseCase::kCompositorGesture, Milliseconds(7200),
+ base::TimeDelta::FromMilliseconds(30));
+
+ RunTask(UseCase::kMainThreadCustomInputHandling, Milliseconds(7300),
+ base::TimeDelta::FromMilliseconds(2));
+ RunTask(UseCase::kSynchronizedGesture, Milliseconds(7400),
+ base::TimeDelta::FromMilliseconds(250));
+ RunTask(UseCase::kMainThreadCustomInputHandling, Milliseconds(7700),
+ base::TimeDelta::FromMilliseconds(150));
+ RunTask(UseCase::kLoading, Milliseconds(7900),
+ base::TimeDelta::FromMilliseconds(50));
+ RunTask(UseCase::kMainThreadGesture, Milliseconds(8000),
+ base::TimeDelta::FromMilliseconds(60));
+ EXPECT_THAT(
+ histogram_tester_->GetAllSamples(
+ "RendererScheduler.TaskDurationPerUseCase"),
+ UnorderedElementsAre(
+ Bucket(static_cast<int>(UseCase::kNone), 4000),
+ Bucket(static_cast<int>(UseCase::kCompositorGesture), 35),
+ Bucket(static_cast<int>(UseCase::kMainThreadCustomInputHandling),
+ 152),
+ Bucket(static_cast<int>(UseCase::kSynchronizedGesture), 250),
+ Bucket(static_cast<int>(UseCase::kTouchstart), 75),
+ Bucket(static_cast<int>(UseCase::kLoading), 50),
+ Bucket(static_cast<int>(UseCase::kMainThreadGesture), 60)));
+}
+
+TEST_F(RendererMetricsHelperTest, GetFrameStatusTest) {
+ DCHECK_EQ(GetFrameStatus(nullptr), FrameStatus::kNone);
+
+ FrameStatus frame_statuses_tested[] = {
+ FrameStatus::kMainFrameVisible,
+ FrameStatus::kSameOriginHidden,
+ FrameStatus::kCrossOriginHidden,
+ FrameStatus::kSameOriginBackground,
+ FrameStatus::kMainFrameBackgroundExemptSelf,
+ FrameStatus::kSameOriginVisibleService,
+ FrameStatus::kCrossOriginHiddenService,
+ FrameStatus::kMainFrameBackgroundExemptOther};
+ for (FrameStatus frame_status : frame_statuses_tested) {
+ std::unique_ptr<FakeFrameScheduler> frame =
+ CreateFakeFrameSchedulerWithType(frame_status);
+ EXPECT_EQ(GetFrameStatus(frame.get()), frame_status);
+ }
+}
+
+TEST_F(RendererMetricsHelperTest, BackgroundedRendererTransition) {
+ scheduler_->SetStoppingWhenBackgroundedEnabled(true);
+ typedef BackgroundedRendererTransition Transition;
+
+ int backgrounding_transitions = 0;
+ int foregrounding_transitions = 0;
+ if (!kLaunchingProcessIsBackgrounded) {
+ scheduler_->SetRendererBackgrounded(true);
+ backgrounding_transitions++;
+ EXPECT_THAT(
+ histogram_tester_->GetAllSamples(
+ "RendererScheduler.BackgroundedRendererTransition"),
+ UnorderedElementsAre(Bucket(static_cast<int>(Transition::kBackgrounded),
+ backgrounding_transitions)));
+ scheduler_->SetRendererBackgrounded(false);
+ foregrounding_transitions++;
+ EXPECT_THAT(
+ histogram_tester_->GetAllSamples(
+ "RendererScheduler.BackgroundedRendererTransition"),
+ UnorderedElementsAre(Bucket(static_cast<int>(Transition::kBackgrounded),
+ backgrounding_transitions),
+ Bucket(static_cast<int>(Transition::kForegrounded),
+ foregrounding_transitions)));
+ } else {
+ scheduler_->SetRendererBackgrounded(false);
+ foregrounding_transitions++;
+ EXPECT_THAT(
+ histogram_tester_->GetAllSamples(
+ "RendererScheduler.BackgroundedRendererTransition"),
+ UnorderedElementsAre(Bucket(static_cast<int>(Transition::kForegrounded),
+ foregrounding_transitions)));
+ }
+
+ scheduler_->SetRendererBackgrounded(true);
+ backgrounding_transitions++;
+ EXPECT_THAT(
+ histogram_tester_->GetAllSamples(
+ "RendererScheduler.BackgroundedRendererTransition"),
+ UnorderedElementsAre(Bucket(static_cast<int>(Transition::kBackgrounded),
+ backgrounding_transitions),
+ Bucket(static_cast<int>(Transition::kForegrounded),
+ foregrounding_transitions)));
+
+ // Waste 5+ minutes so that the delayed stop is triggered
+ RunTask(QueueType::kDefault, Milliseconds(1),
+ base::TimeDelta::FromSeconds(5 * 61));
+ // Firing ForceUpdatePolicy multiple times to make sure that the
+ // metric is only recorded upon an actual change.
+ ForceUpdatePolicy();
+ ForceUpdatePolicy();
+ ForceUpdatePolicy();
+ EXPECT_THAT(histogram_tester_->GetAllSamples(
+ "RendererScheduler.BackgroundedRendererTransition"),
+ UnorderedElementsAre(
+ Bucket(static_cast<int>(Transition::kBackgrounded),
+ backgrounding_transitions),
+ Bucket(static_cast<int>(Transition::kForegrounded),
+ foregrounding_transitions),
+ Bucket(static_cast<int>(Transition::kStoppedAfterDelay), 1)));
+
+ scheduler_->SetRendererBackgrounded(false);
+ foregrounding_transitions++;
+ ForceUpdatePolicy();
+ ForceUpdatePolicy();
+ EXPECT_THAT(histogram_tester_->GetAllSamples(
+ "RendererScheduler.BackgroundedRendererTransition"),
+ UnorderedElementsAre(
+ Bucket(static_cast<int>(Transition::kBackgrounded),
+ backgrounding_transitions),
+ Bucket(static_cast<int>(Transition::kForegrounded),
+ foregrounding_transitions),
+ Bucket(static_cast<int>(Transition::kStoppedAfterDelay), 1),
+ Bucket(static_cast<int>(Transition::kResumed), 1)));
+}
+
+TEST_F(RendererMetricsHelperTest, TaskCountPerFrameStatus) {
+ int task_count = 0;
+ struct CountPerFrameStatus {
+ FrameStatus frame_status;
+ int count;
+ };
+ CountPerFrameStatus test_data[] = {
+ {FrameStatus::kNone, 4},
+ {FrameStatus::kMainFrameVisible, 8},
+ {FrameStatus::kMainFrameBackgroundExemptSelf, 5},
+ {FrameStatus::kCrossOriginHidden, 3},
+ {FrameStatus::kCrossOriginHiddenService, 7},
+ {FrameStatus::kCrossOriginVisible, 1},
+ {FrameStatus::kMainFrameBackgroundExemptOther, 2},
+ {FrameStatus::kSameOriginVisible, 10},
+ {FrameStatus::kSameOriginBackground, 9},
+ {FrameStatus::kSameOriginVisibleService, 6}};
+
+ for (const auto& data : test_data) {
+ std::unique_ptr<FakeFrameScheduler> frame =
+ CreateFakeFrameSchedulerWithType(data.frame_status);
+ for (int i = 0; i < data.count; ++i) {
+ RunTask(frame.get(), Milliseconds(++task_count),
+ base::TimeDelta::FromMicroseconds(100));
+ }
+ }
+
+ EXPECT_THAT(
+ histogram_tester_->GetAllSamples(
+ "RendererScheduler.TaskCountPerFrameType"),
+ UnorderedElementsAre(
+ Bucket(static_cast<int>(FrameStatus::kNone), 4),
+ Bucket(static_cast<int>(FrameStatus::kMainFrameVisible), 8),
+ Bucket(static_cast<int>(FrameStatus::kMainFrameBackgroundExemptSelf),
+ 5),
+ Bucket(static_cast<int>(FrameStatus::kMainFrameBackgroundExemptOther),
+ 2),
+ Bucket(static_cast<int>(FrameStatus::kSameOriginVisible), 10),
+ Bucket(static_cast<int>(FrameStatus::kSameOriginVisibleService), 6),
+ Bucket(static_cast<int>(FrameStatus::kSameOriginBackground), 9),
+ Bucket(static_cast<int>(FrameStatus::kCrossOriginVisible), 1),
+ Bucket(static_cast<int>(FrameStatus::kCrossOriginHidden), 3),
+ Bucket(static_cast<int>(FrameStatus::kCrossOriginHiddenService), 7)));
+}
+
+TEST_F(RendererMetricsHelperTest, TaskCountPerFrameTypeLongerThan) {
+ int total_duration = 0;
+ struct TasksPerFrameStatus {
+ FrameStatus frame_status;
+ std::vector<int> durations;
+ };
+ TasksPerFrameStatus test_data[] = {
+ {FrameStatus::kSameOriginHidden,
+ {2, 15, 16, 20, 25, 30, 49, 50, 73, 99, 100, 110, 140, 150, 800, 1000,
+ 1200}},
+ {FrameStatus::kCrossOriginVisibleService,
+ {5, 10, 18, 19, 20, 55, 75, 220}},
+ {FrameStatus::kMainFrameBackground,
+ {21, 31, 41, 51, 61, 71, 81, 91, 101, 1001}},
+ };
+
+ for (const auto& data : test_data) {
+ std::unique_ptr<FakeFrameScheduler> frame =
+ CreateFakeFrameSchedulerWithType(data.frame_status);
+ for (size_t i = 0; i < data.durations.size(); ++i) {
+ RunTask(frame.get(), Milliseconds(++total_duration),
+ base::TimeDelta::FromMilliseconds(data.durations[i]));
+ total_duration += data.durations[i];
+ }
+ }
+
+ EXPECT_THAT(
+ histogram_tester_->GetAllSamples(
+ "RendererScheduler.TaskCountPerFrameType"),
+ UnorderedElementsAre(
+ Bucket(static_cast<int>(FrameStatus::kMainFrameBackground), 10),
+ Bucket(static_cast<int>(FrameStatus::kSameOriginHidden), 17),
+ Bucket(static_cast<int>(FrameStatus::kCrossOriginVisibleService),
+ 8)));
+
+ EXPECT_THAT(
+ histogram_tester_->GetAllSamples(
+ "RendererScheduler.TaskCountPerFrameType."
+ "LongerThan16ms"),
+ UnorderedElementsAre(
+ Bucket(static_cast<int>(FrameStatus::kMainFrameBackground), 10),
+ Bucket(static_cast<int>(FrameStatus::kSameOriginHidden), 15),
+ Bucket(static_cast<int>(FrameStatus::kCrossOriginVisibleService),
+ 6)));
+
+ EXPECT_THAT(
+ histogram_tester_->GetAllSamples(
+ "RendererScheduler.TaskCountPerFrameType."
+ "LongerThan50ms"),
+ UnorderedElementsAre(
+ Bucket(static_cast<int>(FrameStatus::kMainFrameBackground), 7),
+ Bucket(static_cast<int>(FrameStatus::kSameOriginHidden), 10),
+ Bucket(static_cast<int>(FrameStatus::kCrossOriginVisibleService),
+ 3)));
+
+ EXPECT_THAT(
+ histogram_tester_->GetAllSamples(
+ "RendererScheduler.TaskCountPerFrameType."
+ "LongerThan100ms"),
+ UnorderedElementsAre(
+ Bucket(static_cast<int>(FrameStatus::kMainFrameBackground), 2),
+ Bucket(static_cast<int>(FrameStatus::kSameOriginHidden), 7),
+ Bucket(static_cast<int>(FrameStatus::kCrossOriginVisibleService),
+ 1)));
+
+ EXPECT_THAT(
+ histogram_tester_->GetAllSamples(
+ "RendererScheduler.TaskCountPerFrameType."
+ "LongerThan150ms"),
+ UnorderedElementsAre(
+ Bucket(static_cast<int>(FrameStatus::kMainFrameBackground), 1),
+ Bucket(static_cast<int>(FrameStatus::kSameOriginHidden), 4),
+ Bucket(static_cast<int>(FrameStatus::kCrossOriginVisibleService),
+ 1)));
+
+ EXPECT_THAT(
+ histogram_tester_->GetAllSamples(
+ "RendererScheduler.TaskCountPerFrameType.LongerThan1s"),
+ UnorderedElementsAre(
+ Bucket(static_cast<int>(FrameStatus::kMainFrameBackground), 1),
+ Bucket(static_cast<int>(FrameStatus::kSameOriginHidden), 2)));
+}
+
+// TODO(crbug.com/754656): Add tests for NthMinute and
+// AfterNthMinute histograms.
+
+// TODO(crbug.com/754656): Add tests for
+// TaskDuration.Hidden/Visible histograms.
+
+// TODO(crbug.com/754656): Add tests for non-TaskDuration
+// histograms.
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/renderer/renderer_web_scheduler_impl.cc b/chromium/third_party/blink/renderer/platform/scheduler/renderer/renderer_web_scheduler_impl.cc
new file mode 100644
index 00000000000..4e0fa795827
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/renderer/renderer_web_scheduler_impl.cc
@@ -0,0 +1,61 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/renderer/renderer_web_scheduler_impl.h"
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue.h"
+#include "third_party/blink/renderer/platform/scheduler/child/task_runner_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h"
+
+namespace blink {
+namespace scheduler {
+
+// TODO(kraynov): Ditch kDeprecatedNone here.
+RendererWebSchedulerImpl::RendererWebSchedulerImpl(
+ MainThreadSchedulerImpl* main_thread_scheduler)
+ : WebSchedulerImpl(main_thread_scheduler,
+ main_thread_scheduler->IdleTaskRunner(),
+ main_thread_scheduler->V8TaskQueue()),
+ main_thread_scheduler_(main_thread_scheduler),
+ compositor_task_runner_(
+ TaskRunnerImpl::Create(main_thread_scheduler_->CompositorTaskQueue(),
+ TaskType::kDeprecatedNone)) {}
+
+RendererWebSchedulerImpl::~RendererWebSchedulerImpl() = default;
+
+base::SingleThreadTaskRunner* RendererWebSchedulerImpl::CompositorTaskRunner() {
+ return compositor_task_runner_.get();
+}
+
+std::unique_ptr<RendererWebSchedulerImpl::RendererPauseHandle>
+RendererWebSchedulerImpl::PauseScheduler() {
+ return main_thread_scheduler_->PauseRenderer();
+}
+
+std::unique_ptr<blink::PageScheduler>
+RendererWebSchedulerImpl::CreatePageScheduler(
+ PageScheduler::Delegate* delegate) {
+ return base::WrapUnique(
+ new PageSchedulerImpl(delegate, main_thread_scheduler_,
+ !blink::RuntimeEnabledFeatures::
+ TimerThrottlingForBackgroundTabsEnabled()));
+}
+
+base::TimeTicks RendererWebSchedulerImpl::MonotonicallyIncreasingVirtualTime()
+ const {
+ return main_thread_scheduler_->GetActiveTimeDomain()->Now();
+}
+
+WebMainThreadScheduler*
+RendererWebSchedulerImpl::GetWebMainThreadSchedulerForTest() {
+ return main_thread_scheduler_;
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/renderer/renderer_web_scheduler_impl.h b/chromium/third_party/blink/renderer/platform/scheduler/renderer/renderer_web_scheduler_impl.h
new file mode 100644
index 00000000000..d0cffbd4c2b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/renderer/renderer_web_scheduler_impl.h
@@ -0,0 +1,41 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_RENDERER_WEB_SCHEDULER_IMPL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_RENDERER_WEB_SCHEDULER_IMPL_H_
+
+#include "third_party/blink/renderer/platform/scheduler/child/web_scheduler_impl.h"
+
+namespace blink {
+namespace scheduler {
+
+class MainThreadSchedulerImpl;
+
+class PLATFORM_EXPORT RendererWebSchedulerImpl : public WebSchedulerImpl {
+ public:
+ explicit RendererWebSchedulerImpl(
+ MainThreadSchedulerImpl* main_thread_scheduler);
+
+ ~RendererWebSchedulerImpl() override;
+
+ // WebScheduler implementation:
+ base::SingleThreadTaskRunner* CompositorTaskRunner() override;
+ std::unique_ptr<RendererPauseHandle> PauseScheduler() override
+ WARN_UNUSED_RESULT;
+ std::unique_ptr<PageScheduler> CreatePageScheduler(
+ PageScheduler::Delegate*) override;
+
+ base::TimeTicks MonotonicallyIncreasingVirtualTime() const override;
+
+ WebMainThreadScheduler* GetWebMainThreadSchedulerForTest() override;
+
+ private:
+ MainThreadSchedulerImpl* main_thread_scheduler_; // NOT OWNED
+ scoped_refptr<TaskRunnerImpl> compositor_task_runner_;
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_RENDERER_WEB_SCHEDULER_IMPL_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/renderer/task_cost_estimator.cc b/chromium/third_party/blink/renderer/platform/scheduler/renderer/task_cost_estimator.cc
new file mode 100644
index 00000000000..f9ce0c49a66
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/renderer/task_cost_estimator.cc
@@ -0,0 +1,45 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/renderer/task_cost_estimator.h"
+
+#include "base/time/default_tick_clock.h"
+
+namespace blink {
+namespace scheduler {
+
+TaskCostEstimator::TaskCostEstimator(const base::TickClock* time_source,
+ int sample_count,
+ double estimation_percentile)
+ : rolling_time_delta_history_(sample_count),
+ time_source_(time_source),
+ outstanding_task_count_(0),
+ estimation_percentile_(estimation_percentile) {}
+
+TaskCostEstimator::~TaskCostEstimator() = default;
+
+void TaskCostEstimator::WillProcessTask(const base::PendingTask& pending_task) {
+ // Avoid measuring the duration in nested run loops.
+ if (++outstanding_task_count_ == 1)
+ task_start_time_ = time_source_->NowTicks();
+}
+
+void TaskCostEstimator::DidProcessTask(const base::PendingTask& pending_task) {
+ if (--outstanding_task_count_ == 0) {
+ base::TimeDelta duration = time_source_->NowTicks() - task_start_time_;
+ rolling_time_delta_history_.InsertSample(duration);
+ }
+}
+
+base::TimeDelta TaskCostEstimator::expected_task_duration() const {
+ return rolling_time_delta_history_.Percentile(estimation_percentile_);
+}
+
+void TaskCostEstimator::Clear() {
+ rolling_time_delta_history_.Clear();
+ expected_task_duration_ = base::TimeDelta();
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/renderer/task_cost_estimator.h b/chromium/third_party/blink/renderer/platform/scheduler/renderer/task_cost_estimator.h
new file mode 100644
index 00000000000..2214c772114
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/renderer/task_cost_estimator.h
@@ -0,0 +1,52 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_TASK_COST_ESTIMATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_TASK_COST_ESTIMATOR_H_
+
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/time/time.h"
+#include "cc/base/rolling_time_delta_history.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace base {
+class TickClock;
+}
+
+namespace blink {
+namespace scheduler {
+
+// Estimates the cost of running tasks based on historical timing data.
+class PLATFORM_EXPORT TaskCostEstimator
+ : public base::MessageLoop::TaskObserver {
+ public:
+ TaskCostEstimator(const base::TickClock* time_source,
+ int sample_count,
+ double estimation_percentile);
+ ~TaskCostEstimator() override;
+
+ base::TimeDelta expected_task_duration() const;
+
+ // TaskObserver implementation:
+ void WillProcessTask(const base::PendingTask& pending_task) override;
+ void DidProcessTask(const base::PendingTask& pending_task) override;
+
+ void Clear();
+
+ private:
+ cc::RollingTimeDeltaHistory rolling_time_delta_history_;
+ const base::TickClock* time_source_; // NOT OWNED
+ int outstanding_task_count_;
+ double estimation_percentile_;
+ base::TimeTicks task_start_time_;
+ base::TimeDelta expected_task_duration_;
+
+ DISALLOW_COPY_AND_ASSIGN(TaskCostEstimator);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_TASK_COST_ESTIMATOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/renderer/task_cost_estimator_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/renderer/task_cost_estimator_unittest.cc
new file mode 100644
index 00000000000..45ff499a9e3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/renderer/task_cost_estimator_unittest.cc
@@ -0,0 +1,74 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/renderer/task_cost_estimator.h"
+
+#include <memory>
+
+#include "base/test/simple_test_tick_clock.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+namespace scheduler {
+
+class TaskCostEstimatorTest : public testing::Test {
+ public:
+ TaskCostEstimatorTest() = default;
+ ~TaskCostEstimatorTest() override = default;
+
+ base::SimpleTestTickClock clock_;
+};
+
+class TaskCostEstimatorForTest : public TaskCostEstimator {
+ public:
+ TaskCostEstimatorForTest(const base::TickClock* clock,
+ int sample_count,
+ double estimation_percentile)
+ : TaskCostEstimator(clock, sample_count, estimation_percentile) {}
+};
+
+TEST_F(TaskCostEstimatorTest, BasicEstimation) {
+ TaskCostEstimatorForTest estimator(&clock_, 1, 100);
+ base::PendingTask task(FROM_HERE, base::Closure());
+
+ estimator.WillProcessTask(task);
+ clock_.Advance(base::TimeDelta::FromMilliseconds(500));
+ estimator.DidProcessTask(task);
+
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(500),
+ estimator.expected_task_duration());
+}
+
+TEST_F(TaskCostEstimatorTest, Clear) {
+ TaskCostEstimatorForTest estimator(&clock_, 1, 100);
+ base::PendingTask task(FROM_HERE, base::Closure());
+
+ estimator.WillProcessTask(task);
+ clock_.Advance(base::TimeDelta::FromMilliseconds(500));
+ estimator.DidProcessTask(task);
+
+ estimator.Clear();
+
+ EXPECT_EQ(base::TimeDelta(), estimator.expected_task_duration());
+}
+
+TEST_F(TaskCostEstimatorTest, NestedRunLoop) {
+ TaskCostEstimatorForTest estimator(&clock_, 1, 100);
+ base::PendingTask task(FROM_HERE, base::Closure());
+
+ // Make sure we ignore the tasks inside the nested run loop.
+ estimator.WillProcessTask(task);
+ estimator.WillProcessTask(task);
+ clock_.Advance(base::TimeDelta::FromMilliseconds(500));
+ estimator.DidProcessTask(task);
+ clock_.Advance(base::TimeDelta::FromMilliseconds(500));
+ estimator.DidProcessTask(task);
+
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(1000),
+ estimator.expected_task_duration());
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/renderer/use_case.h b/chromium/third_party/blink/renderer/platform/scheduler/renderer/use_case.h
new file mode 100644
index 00000000000..ce4ad48ebdb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/renderer/use_case.h
@@ -0,0 +1,42 @@
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_USE_CASE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_USE_CASE_H_
+
+namespace blink {
+namespace scheduler {
+
+// Keep RendererScheduler::UseCaseToString in sync with this enum.
+// This enum is used for histograms and should not be renumbered.
+enum class UseCase {
+ // No active use case detected.
+ kNone = 0,
+ // A continuous gesture (e.g., scroll, pinch) which is being driven by the
+ // compositor thread.
+ kCompositorGesture = 1,
+ // An unspecified touch gesture which is being handled by the main thread.
+ // Note that since we don't have a full view of the use case, we should be
+ // careful to prioritize all work equally.
+ kMainThreadCustomInputHandling = 2,
+ // A continuous gesture (e.g., scroll, pinch) which is being driven by the
+ // compositor thread but also observed by the main thread. An example is
+ // synchronized scrolling where a scroll listener on the main thread changes
+ // page layout based on the current scroll position.
+ kSynchronizedGesture = 3,
+ // A gesture has recently started and we are about to run main thread touch
+ // listeners to find out the actual gesture type. To minimize touch latency,
+ // only input handling work should run in this state.
+ kTouchstart = 4,
+ // A page is loading.
+ kLoading = 5,
+ // A continuous gesture (e.g., scroll) which is being handled by the main
+ // thread.
+ kMainThreadGesture = 6,
+ kFirstUseCase = kNone,
+
+ // Must be the last entry.
+ kCount = 7,
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/renderer/user_model.cc b/chromium/third_party/blink/renderer/platform/scheduler/renderer/user_model.cc
new file mode 100644
index 00000000000..aeb7ba5ce40
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/renderer/user_model.cc
@@ -0,0 +1,168 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/renderer/user_model.h"
+
+namespace blink {
+namespace scheduler {
+
+UserModel::UserModel()
+ : pending_input_event_count_(0),
+ is_gesture_active_(false),
+ is_gesture_expected_(false) {}
+UserModel::~UserModel() = default;
+
+void UserModel::DidStartProcessingInputEvent(blink::WebInputEvent::Type type,
+ const base::TimeTicks now) {
+ last_input_signal_time_ = now;
+ if (type == blink::WebInputEvent::kTouchStart ||
+ type == blink::WebInputEvent::kGestureScrollBegin ||
+ type == blink::WebInputEvent::kGesturePinchBegin) {
+ // Only update stats once per gesture.
+ if (!is_gesture_active_)
+ last_gesture_start_time_ = now;
+
+ is_gesture_active_ = true;
+ }
+
+ // We need to track continuous gestures seperatly for scroll detection
+ // because taps should not be confused with scrolls.
+ if (type == blink::WebInputEvent::kGestureScrollBegin ||
+ type == blink::WebInputEvent::kGestureScrollEnd ||
+ type == blink::WebInputEvent::kGestureScrollUpdate ||
+ type == blink::WebInputEvent::kGestureFlingStart ||
+ type == blink::WebInputEvent::kGestureFlingCancel ||
+ type == blink::WebInputEvent::kGesturePinchBegin ||
+ type == blink::WebInputEvent::kGesturePinchEnd ||
+ type == blink::WebInputEvent::kGesturePinchUpdate) {
+ last_continuous_gesture_time_ = now;
+ }
+
+ // If the gesture has ended, clear |is_gesture_active_| and record a UMA
+ // metric that tracks its duration.
+ if (type == blink::WebInputEvent::kGestureScrollEnd ||
+ type == blink::WebInputEvent::kGesturePinchEnd ||
+ type == blink::WebInputEvent::kGestureFlingStart ||
+ type == blink::WebInputEvent::kTouchEnd) {
+ is_gesture_active_ = false;
+ }
+
+ TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+ "is_gesture_active", is_gesture_active_);
+
+ pending_input_event_count_++;
+}
+
+void UserModel::DidFinishProcessingInputEvent(const base::TimeTicks now) {
+ last_input_signal_time_ = now;
+ if (pending_input_event_count_ > 0)
+ pending_input_event_count_--;
+}
+
+base::TimeDelta UserModel::TimeLeftInUserGesture(base::TimeTicks now) const {
+ base::TimeDelta escalated_priority_duration =
+ base::TimeDelta::FromMilliseconds(kGestureEstimationLimitMillis);
+
+ // If the input event is still pending, go into input prioritized policy and
+ // check again later.
+ if (pending_input_event_count_ > 0)
+ return escalated_priority_duration;
+ if (last_input_signal_time_.is_null() ||
+ last_input_signal_time_ + escalated_priority_duration < now) {
+ return base::TimeDelta();
+ }
+ return last_input_signal_time_ + escalated_priority_duration - now;
+}
+
+bool UserModel::IsGestureExpectedSoon(
+ const base::TimeTicks now,
+ base::TimeDelta* prediction_valid_duration) {
+ bool was_gesture_expected = is_gesture_expected_;
+ is_gesture_expected_ =
+ IsGestureExpectedSoonImpl(now, prediction_valid_duration);
+
+ // Track when we start expecting a gesture so we can work out later if a
+ // gesture actually happened.
+ if (!was_gesture_expected && is_gesture_expected_)
+ last_gesture_expected_start_time_ = now;
+ return is_gesture_expected_;
+}
+
+bool UserModel::IsGestureExpectedSoonImpl(
+ const base::TimeTicks now,
+ base::TimeDelta* prediction_valid_duration) const {
+ if (is_gesture_active_) {
+ if (IsGestureExpectedToContinue(now, prediction_valid_duration))
+ return false;
+ *prediction_valid_duration =
+ base::TimeDelta::FromMilliseconds(kExpectSubsequentGestureMillis);
+ return true;
+ } else {
+ // If we've have a finished a gesture then a subsequent gesture is deemed
+ // likely.
+ base::TimeDelta expect_subsequent_gesture_for =
+ base::TimeDelta::FromMilliseconds(kExpectSubsequentGestureMillis);
+ if (last_continuous_gesture_time_.is_null() ||
+ last_continuous_gesture_time_ + expect_subsequent_gesture_for <= now) {
+ return false;
+ }
+ *prediction_valid_duration =
+ last_continuous_gesture_time_ + expect_subsequent_gesture_for - now;
+ return true;
+ }
+}
+
+bool UserModel::IsGestureExpectedToContinue(
+ const base::TimeTicks now,
+ base::TimeDelta* prediction_valid_duration) const {
+ if (!is_gesture_active_)
+ return false;
+
+ base::TimeDelta median_gesture_duration =
+ base::TimeDelta::FromMilliseconds(kMedianGestureDurationMillis);
+ base::TimeTicks expected_gesture_end_time =
+ last_gesture_start_time_ + median_gesture_duration;
+
+ if (expected_gesture_end_time > now) {
+ *prediction_valid_duration = expected_gesture_end_time - now;
+ return true;
+ }
+ return false;
+}
+
+void UserModel::Reset(base::TimeTicks now) {
+ last_input_signal_time_ = base::TimeTicks();
+ last_gesture_start_time_ = base::TimeTicks();
+ last_continuous_gesture_time_ = base::TimeTicks();
+ last_gesture_expected_start_time_ = base::TimeTicks();
+ last_reset_time_ = now;
+ is_gesture_active_ = false;
+ is_gesture_expected_ = false;
+ pending_input_event_count_ = 0;
+}
+
+void UserModel::AsValueInto(base::trace_event::TracedValue* state) const {
+ state->BeginDictionary("user_model");
+ state->SetInteger("pending_input_event_count", pending_input_event_count_);
+ state->SetDouble(
+ "last_input_signal_time",
+ (last_input_signal_time_ - base::TimeTicks()).InMillisecondsF());
+ state->SetDouble(
+ "last_gesture_start_time",
+ (last_gesture_start_time_ - base::TimeTicks()).InMillisecondsF());
+ state->SetDouble(
+ "last_continuous_gesture_time",
+ (last_continuous_gesture_time_ - base::TimeTicks()).InMillisecondsF());
+ state->SetDouble("last_gesture_expected_start_time",
+ (last_gesture_expected_start_time_ - base::TimeTicks())
+ .InMillisecondsF());
+ state->SetDouble("last_reset_time",
+ (last_reset_time_ - base::TimeTicks()).InMillisecondsF());
+ state->SetBoolean("is_gesture_expected", is_gesture_expected_);
+ state->SetBoolean("is_gesture_active", is_gesture_active_);
+ state->EndDictionary();
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/renderer/user_model.h b/chromium/third_party/blink/renderer/platform/scheduler/renderer/user_model.h
new file mode 100644
index 00000000000..247acf43847
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/renderer/user_model.h
@@ -0,0 +1,86 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_USER_MODEL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_USER_MODEL_H_
+
+#include "base/macros.h"
+#include "base/trace_event/trace_event.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "third_party/blink/public/platform/scheduler/web_main_thread_scheduler.h"
+#include "third_party/blink/public/platform/web_input_event.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+namespace scheduler {
+
+class PLATFORM_EXPORT UserModel {
+ public:
+ UserModel();
+ ~UserModel();
+
+ // Tells us that the system started processing an input event. Must be paired
+ // with a call to DidFinishProcessingInputEvent.
+ void DidStartProcessingInputEvent(WebInputEvent::Type type,
+ const base::TimeTicks now);
+
+ // Tells us that the system finished processing an input event.
+ void DidFinishProcessingInputEvent(const base::TimeTicks now);
+
+ // Returns the estimated amount of time left in the current user gesture, to a
+ // maximum of |kGestureEstimationLimitMillis|. After that time has elapased
+ // this function should be called again.
+ base::TimeDelta TimeLeftInUserGesture(base::TimeTicks now) const;
+
+ // Tries to guess if a user gesture is expected soon. Currently this is
+ // very simple, but one day I hope to do something more sophisticated here.
+ // The prediction may change after |prediction_valid_duration| has elapsed.
+ bool IsGestureExpectedSoon(const base::TimeTicks now,
+ base::TimeDelta* prediction_valid_duration);
+
+ // Returns true if a gesture has been in progress for less than the median
+ // gesture duration. The prediction may change after
+ // |prediction_valid_duration| has elapsed.
+ bool IsGestureExpectedToContinue(
+ const base::TimeTicks now,
+ base::TimeDelta* prediction_valid_duration) const;
+
+ void AsValueInto(base::trace_event::TracedValue* state) const;
+
+ // The time we should stay in a priority-escalated mode after an input event.
+ static const int kGestureEstimationLimitMillis = 100;
+
+ // This is based on two weeks of Android usage data.
+ static const int kMedianGestureDurationMillis = 300;
+
+ // We consider further gesture start events to be likely if the user has
+ // interacted with the device in the past two seconds.
+ // Based on Android usage data, 2000ms between gestures is the 75th percentile
+ // with 700ms being the 50th.
+ static const int kExpectSubsequentGestureMillis = 2000;
+
+ // Clears input signals.
+ void Reset(base::TimeTicks now);
+
+ private:
+ bool IsGestureExpectedSoonImpl(
+ const base::TimeTicks now,
+ base::TimeDelta* prediction_valid_duration) const;
+
+ int pending_input_event_count_;
+ base::TimeTicks last_input_signal_time_;
+ base::TimeTicks last_gesture_start_time_;
+ base::TimeTicks last_continuous_gesture_time_; // Doesn't include Taps.
+ base::TimeTicks last_gesture_expected_start_time_;
+ base::TimeTicks last_reset_time_;
+ bool is_gesture_active_; // This typically means the user's finger is down.
+ bool is_gesture_expected_;
+
+ DISALLOW_COPY_AND_ASSIGN(UserModel);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_USER_MODEL_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/renderer/user_model_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/renderer/user_model_unittest.cc
new file mode 100644
index 00000000000..6d75270e750
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/renderer/user_model_unittest.cc
@@ -0,0 +1,266 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/renderer/user_model.h"
+
+#include "base/test/simple_test_tick_clock.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+namespace scheduler {
+
+class UserModelTest : public testing::Test {
+ public:
+ UserModelTest() = default;
+ ~UserModelTest() override = default;
+
+ void SetUp() override {
+ clock_.reset(new base::SimpleTestTickClock());
+ clock_->Advance(base::TimeDelta::FromMicroseconds(5000));
+
+ user_model_.reset(new UserModel());
+ }
+
+ protected:
+ static base::TimeDelta priority_escalation_after_input_duration() {
+ return base::TimeDelta::FromMilliseconds(
+ UserModel::kGestureEstimationLimitMillis);
+ }
+
+ static base::TimeDelta subsequent_input_expected_after_input_duration() {
+ return base::TimeDelta::FromMilliseconds(
+ UserModel::kExpectSubsequentGestureMillis);
+ }
+
+ std::unique_ptr<base::SimpleTestTickClock> clock_;
+ std::unique_ptr<UserModel> user_model_;
+};
+
+TEST_F(UserModelTest, TimeLeftInUserGesture_NoInput) {
+ EXPECT_EQ(base::TimeDelta(),
+ user_model_->TimeLeftInUserGesture(clock_->NowTicks()));
+}
+
+TEST_F(UserModelTest, TimeLeftInUserGesture_ImmediatelyAfterInput) {
+ user_model_->DidStartProcessingInputEvent(
+ blink::WebInputEvent::Type::kTouchStart, clock_->NowTicks());
+ user_model_->DidFinishProcessingInputEvent(clock_->NowTicks());
+ EXPECT_EQ(priority_escalation_after_input_duration(),
+ user_model_->TimeLeftInUserGesture(clock_->NowTicks()));
+}
+
+TEST_F(UserModelTest, TimeLeftInUserGesture_ShortlyAfterInput) {
+ user_model_->DidStartProcessingInputEvent(
+ blink::WebInputEvent::Type::kTouchStart, clock_->NowTicks());
+ user_model_->DidFinishProcessingInputEvent(clock_->NowTicks());
+ base::TimeDelta delta(base::TimeDelta::FromMilliseconds(10));
+ clock_->Advance(delta);
+ EXPECT_EQ(priority_escalation_after_input_duration() - delta,
+ user_model_->TimeLeftInUserGesture(clock_->NowTicks()));
+}
+
+TEST_F(UserModelTest, TimeLeftInUserGesture_LongAfterInput) {
+ user_model_->DidStartProcessingInputEvent(
+ blink::WebInputEvent::Type::kTouchStart, clock_->NowTicks());
+ user_model_->DidFinishProcessingInputEvent(clock_->NowTicks());
+ clock_->Advance(priority_escalation_after_input_duration() * 2);
+ EXPECT_EQ(base::TimeDelta(),
+ user_model_->TimeLeftInUserGesture(clock_->NowTicks()));
+}
+
+TEST_F(UserModelTest, DidFinishProcessingInputEvent_Delayed) {
+ user_model_->DidStartProcessingInputEvent(
+ blink::WebInputEvent::Type::kTouchStart, clock_->NowTicks());
+ clock_->Advance(priority_escalation_after_input_duration() * 10);
+
+ EXPECT_EQ(priority_escalation_after_input_duration(),
+ user_model_->TimeLeftInUserGesture(clock_->NowTicks()));
+
+ user_model_->DidFinishProcessingInputEvent(clock_->NowTicks());
+ base::TimeDelta delta(base::TimeDelta::FromMilliseconds(10));
+ clock_->Advance(delta);
+
+ EXPECT_EQ(priority_escalation_after_input_duration() - delta,
+ user_model_->TimeLeftInUserGesture(clock_->NowTicks()));
+}
+
+TEST_F(UserModelTest, GestureExpectedSoon_NoRecentInput) {
+ base::TimeDelta prediction_valid_duration;
+ EXPECT_FALSE(user_model_->IsGestureExpectedSoon(clock_->NowTicks(),
+ &prediction_valid_duration));
+ EXPECT_EQ(base::TimeDelta(), prediction_valid_duration);
+}
+
+TEST_F(UserModelTest, GestureExpectedSoon_ShortlyAfter_GestureScrollBegin) {
+ user_model_->DidStartProcessingInputEvent(
+ blink::WebInputEvent::Type::kGestureScrollBegin, clock_->NowTicks());
+ user_model_->DidFinishProcessingInputEvent(clock_->NowTicks());
+
+ base::TimeDelta delta(base::TimeDelta::FromMilliseconds(10));
+ clock_->Advance(delta);
+
+ base::TimeDelta prediction_valid_duration;
+ EXPECT_FALSE(user_model_->IsGestureExpectedSoon(clock_->NowTicks(),
+ &prediction_valid_duration));
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(
+ UserModel::kMedianGestureDurationMillis) -
+ delta,
+ prediction_valid_duration);
+}
+
+TEST_F(UserModelTest, GestureExpectedSoon_LongAfter_GestureScrollBegin) {
+ user_model_->DidStartProcessingInputEvent(
+ blink::WebInputEvent::Type::kGestureScrollBegin, clock_->NowTicks());
+ user_model_->DidFinishProcessingInputEvent(clock_->NowTicks());
+
+ base::TimeDelta delta(base::TimeDelta::FromMilliseconds(
+ UserModel::kMedianGestureDurationMillis * 2));
+ clock_->Advance(delta);
+
+ base::TimeDelta prediction_valid_duration;
+ EXPECT_TRUE(user_model_->IsGestureExpectedSoon(clock_->NowTicks(),
+ &prediction_valid_duration));
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(
+ UserModel::kExpectSubsequentGestureMillis),
+ prediction_valid_duration);
+}
+
+TEST_F(UserModelTest, GestureExpectedSoon_ImmediatelyAfter_GestureScrollEnd) {
+ user_model_->DidStartProcessingInputEvent(
+ blink::WebInputEvent::Type::kGestureScrollEnd, clock_->NowTicks());
+ user_model_->DidFinishProcessingInputEvent(clock_->NowTicks());
+
+ base::TimeDelta prediction_valid_duration;
+ EXPECT_TRUE(user_model_->IsGestureExpectedSoon(clock_->NowTicks(),
+ &prediction_valid_duration));
+ EXPECT_EQ(subsequent_input_expected_after_input_duration(),
+ prediction_valid_duration);
+}
+
+TEST_F(UserModelTest, GestureExpectedSoon_ShortlyAfter_GestureScrollEnd) {
+ user_model_->DidStartProcessingInputEvent(
+ blink::WebInputEvent::Type::kGestureScrollEnd, clock_->NowTicks());
+ user_model_->DidFinishProcessingInputEvent(clock_->NowTicks());
+
+ base::TimeDelta delta(base::TimeDelta::FromMilliseconds(10));
+ clock_->Advance(delta);
+
+ base::TimeDelta prediction_valid_duration;
+ EXPECT_TRUE(user_model_->IsGestureExpectedSoon(clock_->NowTicks(),
+ &prediction_valid_duration));
+ EXPECT_EQ(subsequent_input_expected_after_input_duration() - delta,
+ prediction_valid_duration);
+}
+
+TEST_F(UserModelTest, GestureExpectedSoon_LongAfter_GestureScrollEnd) {
+ user_model_->DidStartProcessingInputEvent(
+ blink::WebInputEvent::Type::kGestureScrollEnd, clock_->NowTicks());
+ user_model_->DidFinishProcessingInputEvent(clock_->NowTicks());
+ clock_->Advance(subsequent_input_expected_after_input_duration() * 2);
+
+ base::TimeDelta prediction_valid_duration;
+ EXPECT_FALSE(user_model_->IsGestureExpectedSoon(clock_->NowTicks(),
+ &prediction_valid_duration));
+ EXPECT_EQ(base::TimeDelta(), prediction_valid_duration);
+}
+
+TEST_F(UserModelTest, GestureExpectedSoon_ShortlyAfter_GesturePinchEnd) {
+ user_model_->DidStartProcessingInputEvent(
+ blink::WebInputEvent::Type::kGesturePinchEnd, clock_->NowTicks());
+ user_model_->DidFinishProcessingInputEvent(clock_->NowTicks());
+
+ base::TimeDelta delta(base::TimeDelta::FromMilliseconds(10));
+ clock_->Advance(delta);
+
+ base::TimeDelta prediction_valid_duration;
+ EXPECT_TRUE(user_model_->IsGestureExpectedSoon(clock_->NowTicks(),
+ &prediction_valid_duration));
+ EXPECT_EQ(subsequent_input_expected_after_input_duration() - delta,
+ prediction_valid_duration);
+}
+
+TEST_F(UserModelTest, GestureExpectedSoon_ShortlyAfterInput_GestureTap) {
+ user_model_->DidStartProcessingInputEvent(
+ blink::WebInputEvent::Type::kGestureTap, clock_->NowTicks());
+ user_model_->DidFinishProcessingInputEvent(clock_->NowTicks());
+
+ base::TimeDelta delta(base::TimeDelta::FromMilliseconds(10));
+ clock_->Advance(delta);
+
+ base::TimeDelta prediction_valid_duration;
+ EXPECT_FALSE(user_model_->IsGestureExpectedSoon(clock_->NowTicks(),
+ &prediction_valid_duration));
+ EXPECT_EQ(base::TimeDelta(), prediction_valid_duration);
+}
+
+TEST_F(UserModelTest, IsGestureExpectedToContinue_NoGesture) {
+ base::TimeDelta prediction_valid_duration;
+ EXPECT_FALSE(user_model_->IsGestureExpectedToContinue(
+ clock_->NowTicks(), &prediction_valid_duration));
+ EXPECT_EQ(base::TimeDelta(), prediction_valid_duration);
+}
+
+TEST_F(UserModelTest, IsGestureExpectedToContinue_GestureJustStarted) {
+ user_model_->DidStartProcessingInputEvent(
+ blink::WebInputEvent::Type::kGestureScrollBegin, clock_->NowTicks());
+ base::TimeDelta prediction_valid_duration;
+ EXPECT_TRUE(user_model_->IsGestureExpectedToContinue(
+ clock_->NowTicks(), &prediction_valid_duration));
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(
+ UserModel::kMedianGestureDurationMillis),
+ prediction_valid_duration);
+}
+
+TEST_F(UserModelTest, IsGestureExpectedToContinue_GestureJustEnded) {
+ user_model_->DidStartProcessingInputEvent(
+ blink::WebInputEvent::Type::kGestureScrollEnd, clock_->NowTicks());
+ base::TimeDelta prediction_valid_duration;
+ EXPECT_FALSE(user_model_->IsGestureExpectedToContinue(
+ clock_->NowTicks(), &prediction_valid_duration));
+ EXPECT_EQ(base::TimeDelta(), prediction_valid_duration);
+}
+
+TEST_F(UserModelTest, IsGestureExpectedToContinue_ShortlyAfterGestureStarted) {
+ user_model_->DidStartProcessingInputEvent(
+ blink::WebInputEvent::Type::kGestureScrollBegin, clock_->NowTicks());
+
+ base::TimeDelta delta(base::TimeDelta::FromMilliseconds(10));
+ clock_->Advance(delta);
+
+ base::TimeDelta prediction_valid_duration;
+ EXPECT_TRUE(user_model_->IsGestureExpectedToContinue(
+ clock_->NowTicks(), &prediction_valid_duration));
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(
+ UserModel::kMedianGestureDurationMillis) -
+ delta,
+ prediction_valid_duration);
+}
+
+TEST_F(UserModelTest, IsGestureExpectedToContinue_LongAfterGestureStarted) {
+ user_model_->DidStartProcessingInputEvent(
+ blink::WebInputEvent::Type::kGestureScrollBegin, clock_->NowTicks());
+
+ base::TimeDelta delta(base::TimeDelta::FromMilliseconds(
+ UserModel::kMedianGestureDurationMillis * 2));
+ clock_->Advance(delta);
+
+ base::TimeDelta prediction_valid_duration;
+ EXPECT_FALSE(user_model_->IsGestureExpectedToContinue(
+ clock_->NowTicks(), &prediction_valid_duration));
+ EXPECT_EQ(base::TimeDelta(), prediction_valid_duration);
+}
+
+TEST_F(UserModelTest, ResetPendingInputCount) {
+ user_model_->DidStartProcessingInputEvent(
+ blink::WebInputEvent::Type::kGestureScrollBegin, clock_->NowTicks());
+ EXPECT_EQ(priority_escalation_after_input_duration(),
+ user_model_->TimeLeftInUserGesture(clock_->NowTicks()));
+ user_model_->Reset(clock_->NowTicks());
+ EXPECT_EQ(base::TimeDelta(),
+ user_model_->TimeLeftInUserGesture(clock_->NowTicks()));
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/renderer/web_scoped_virtual_time_pauser.cc b/chromium/third_party/blink/renderer/platform/scheduler/renderer/web_scoped_virtual_time_pauser.cc
new file mode 100644
index 00000000000..b3bd2a7e4ec
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/renderer/web_scoped_virtual_time_pauser.cc
@@ -0,0 +1,87 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_scoped_virtual_time_pauser.h"
+
+#include "base/trace_event/trace_event.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/traced_value.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
+
+namespace blink {
+
+WebScopedVirtualTimePauser::WebScopedVirtualTimePauser()
+ : scheduler_(nullptr) {}
+
+WebScopedVirtualTimePauser::WebScopedVirtualTimePauser(
+ scheduler::MainThreadSchedulerImpl* scheduler,
+ VirtualTaskDuration duration,
+ const WebString& name)
+ : duration_(duration),
+ scheduler_(scheduler),
+ debug_name_(name),
+ trace_id_(WebScopedVirtualTimePauser::next_trace_id_++) {}
+
+WebScopedVirtualTimePauser::~WebScopedVirtualTimePauser() {
+ if (paused_ && scheduler_)
+ DecrementVirtualTimePauseCount();
+}
+
+WebScopedVirtualTimePauser::WebScopedVirtualTimePauser(
+ WebScopedVirtualTimePauser&& other) {
+ virtual_time_when_paused_ = other.virtual_time_when_paused_;
+ paused_ = other.paused_;
+ duration_ = other.duration_;
+ scheduler_ = std::move(other.scheduler_);
+ debug_name_ = std::move(other.debug_name_);
+ other.scheduler_ = nullptr;
+ trace_id_ = other.trace_id_;
+}
+
+WebScopedVirtualTimePauser& WebScopedVirtualTimePauser::operator=(
+ WebScopedVirtualTimePauser&& other) {
+ if (scheduler_ && paused_)
+ DecrementVirtualTimePauseCount();
+ virtual_time_when_paused_ = other.virtual_time_when_paused_;
+ paused_ = other.paused_;
+ duration_ = other.duration_;
+ scheduler_ = std::move(other.scheduler_);
+ debug_name_ = std::move(other.debug_name_);
+ trace_id_ = other.trace_id_;
+ other.scheduler_ = nullptr;
+ return *this;
+}
+
+void WebScopedVirtualTimePauser::PauseVirtualTime() {
+ if (paused_ || !scheduler_)
+ return;
+
+ paused_ = true;
+ TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(
+ "renderer.scheduler", "WebScopedVirtualTimePauser::PauseVirtualTime",
+ trace_id_, "name", debug_name_.Latin1());
+ virtual_time_when_paused_ = scheduler_->IncrementVirtualTimePauseCount();
+}
+
+void WebScopedVirtualTimePauser::UnpauseVirtualTime() {
+ if (!paused_ || !scheduler_)
+ return;
+
+ paused_ = false;
+ TRACE_EVENT_NESTABLE_ASYNC_END0(
+ "renderer.scheduler", "WebScopedVirtualTimePauser::PauseVirtualTime",
+ trace_id_);
+ DecrementVirtualTimePauseCount();
+}
+
+void WebScopedVirtualTimePauser::DecrementVirtualTimePauseCount() {
+ scheduler_->DecrementVirtualTimePauseCount();
+ if (duration_ == VirtualTaskDuration::kNonInstant) {
+ scheduler_->MaybeAdvanceVirtualTime(virtual_time_when_paused_ +
+ base::TimeDelta::FromMilliseconds(10));
+ }
+}
+
+int WebScopedVirtualTimePauser::next_trace_id_ = 0;
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/renderer/webthread_impl_for_renderer_scheduler.cc b/chromium/third_party/blink/renderer/platform/scheduler/renderer/webthread_impl_for_renderer_scheduler.cc
new file mode 100644
index 00000000000..087c9c2e138
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/renderer/webthread_impl_for_renderer_scheduler.cc
@@ -0,0 +1,67 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/renderer/webthread_impl_for_renderer_scheduler.h"
+
+#include "base/location.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/renderer/renderer_web_scheduler_impl.h"
+
+namespace blink {
+namespace scheduler {
+
+WebThreadImplForRendererScheduler::WebThreadImplForRendererScheduler(
+ MainThreadSchedulerImpl* scheduler)
+ : web_scheduler_(new RendererWebSchedulerImpl(scheduler)),
+ task_runner_(scheduler->DefaultTaskQueue()),
+ idle_task_runner_(scheduler->IdleTaskRunner()),
+ scheduler_(scheduler),
+ thread_id_(base::PlatformThread::CurrentId()) {}
+
+WebThreadImplForRendererScheduler::~WebThreadImplForRendererScheduler() =
+ default;
+
+blink::PlatformThreadId WebThreadImplForRendererScheduler::ThreadId() const {
+ return thread_id_;
+}
+
+blink::WebScheduler* WebThreadImplForRendererScheduler::Scheduler() const {
+ return web_scheduler_.get();
+}
+
+SingleThreadIdleTaskRunner*
+WebThreadImplForRendererScheduler::GetIdleTaskRunner() const {
+ return idle_task_runner_.get();
+}
+
+scoped_refptr<base::SingleThreadTaskRunner>
+WebThreadImplForRendererScheduler::GetTaskRunner() const {
+ return task_runner_;
+}
+
+void WebThreadImplForRendererScheduler::AddTaskObserverInternal(
+ base::MessageLoop::TaskObserver* observer) {
+ scheduler_->AddTaskObserver(observer);
+}
+
+void WebThreadImplForRendererScheduler::RemoveTaskObserverInternal(
+ base::MessageLoop::TaskObserver* observer) {
+ scheduler_->RemoveTaskObserver(observer);
+}
+
+void WebThreadImplForRendererScheduler::AddTaskTimeObserverInternal(
+ TaskTimeObserver* task_time_observer) {
+ scheduler_->AddTaskTimeObserver(task_time_observer);
+}
+
+void WebThreadImplForRendererScheduler::RemoveTaskTimeObserverInternal(
+ TaskTimeObserver* task_time_observer) {
+ scheduler_->RemoveTaskTimeObserver(task_time_observer);
+}
+
+void WebThreadImplForRendererScheduler::Init() {}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/renderer/webthread_impl_for_renderer_scheduler.h b/chromium/third_party/blink/renderer/platform/scheduler/renderer/webthread_impl_for_renderer_scheduler.h
new file mode 100644
index 00000000000..cb637c5a7df
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/renderer/webthread_impl_for_renderer_scheduler.h
@@ -0,0 +1,56 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_WEBTHREAD_IMPL_FOR_RENDERER_SCHEDULER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_WEBTHREAD_IMPL_FOR_RENDERER_SCHEDULER_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "base/message_loop/message_loop.h"
+#include "third_party/blink/public/platform/scheduler/child/webthread_base.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+class WebScheduler;
+};
+
+namespace blink {
+namespace scheduler {
+class MainThreadSchedulerImpl;
+class WebSchedulerImpl;
+
+class PLATFORM_EXPORT WebThreadImplForRendererScheduler : public WebThreadBase {
+ public:
+ explicit WebThreadImplForRendererScheduler(
+ MainThreadSchedulerImpl* scheduler);
+ ~WebThreadImplForRendererScheduler() override;
+
+ // WebThread implementation.
+ WebScheduler* Scheduler() const override;
+ PlatformThreadId ThreadId() const override;
+ scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() const override;
+
+ // WebThreadBase implementation.
+ SingleThreadIdleTaskRunner* GetIdleTaskRunner() const override;
+ void Init() override;
+
+ private:
+ void AddTaskObserverInternal(
+ base::MessageLoop::TaskObserver* observer) override;
+ void RemoveTaskObserverInternal(
+ base::MessageLoop::TaskObserver* observer) override;
+
+ void AddTaskTimeObserverInternal(TaskTimeObserver*) override;
+ void RemoveTaskTimeObserverInternal(TaskTimeObserver*) override;
+
+ std::unique_ptr<WebSchedulerImpl> web_scheduler_;
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ scoped_refptr<SingleThreadIdleTaskRunner> idle_task_runner_;
+ MainThreadSchedulerImpl* scheduler_; // Not owned.
+ PlatformThreadId thread_id_;
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_WEBTHREAD_IMPL_FOR_RENDERER_SCHEDULER_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/renderer/webthread_impl_for_renderer_scheduler_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/renderer/webthread_impl_for_renderer_scheduler_unittest.cc
new file mode 100644
index 00000000000..f10992d66b4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/renderer/webthread_impl_for_renderer_scheduler_unittest.cc
@@ -0,0 +1,205 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/renderer/webthread_impl_for_renderer_scheduler.h"
+
+#include <stddef.h>
+#include <memory>
+
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/test/task_queue_manager_for_test.h"
+
+namespace blink {
+namespace scheduler {
+// To avoid symbol collisions in jumbo builds.
+namespace webthread_impl_for_renderer_scheduler_unittest {
+
+const int kWorkBatchSize = 2;
+
+class MockTask {
+ public:
+ MOCK_METHOD0(Run, void());
+};
+
+class MockTaskObserver : public blink::WebThread::TaskObserver {
+ public:
+ MOCK_METHOD0(WillProcessTask, void());
+ MOCK_METHOD0(DidProcessTask, void());
+};
+
+class WebThreadImplForRendererSchedulerTest : public testing::Test {
+ public:
+ WebThreadImplForRendererSchedulerTest() = default;
+
+ void SetUp() override {
+ clock_.Advance(base::TimeDelta::FromMicroseconds(5000));
+ scheduler_.reset(new MainThreadSchedulerImpl(
+ TaskQueueManagerForTest::Create(&message_loop_,
+ message_loop_.task_runner(), &clock_),
+ base::nullopt));
+ default_task_runner_ = scheduler_->DefaultTaskQueue();
+ thread_ = scheduler_->CreateMainThread();
+ }
+
+ ~WebThreadImplForRendererSchedulerTest() override = default;
+
+ void SetWorkBatchSizeForTesting(size_t work_batch_size) {
+ scheduler_->GetSchedulerHelperForTesting()->SetWorkBatchSizeForTesting(
+ work_batch_size);
+ }
+
+ void TearDown() override { scheduler_->Shutdown(); }
+
+ protected:
+ base::MessageLoop message_loop_;
+ base::SimpleTestTickClock clock_;
+ std::unique_ptr<MainThreadSchedulerImpl> scheduler_;
+ scoped_refptr<base::SingleThreadTaskRunner> default_task_runner_;
+ std::unique_ptr<blink::WebThread> thread_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebThreadImplForRendererSchedulerTest);
+};
+
+TEST_F(WebThreadImplForRendererSchedulerTest, TestTaskObserver) {
+ MockTaskObserver observer;
+ thread_->AddTaskObserver(&observer);
+ MockTask task;
+
+ {
+ testing::InSequence sequence;
+ EXPECT_CALL(observer, WillProcessTask());
+ EXPECT_CALL(task, Run());
+ EXPECT_CALL(observer, DidProcessTask());
+ }
+
+ thread_->GetTaskRunner()->PostTask(
+ FROM_HERE, WTF::Bind(&MockTask::Run, WTF::Unretained(&task)));
+ base::RunLoop().RunUntilIdle();
+ thread_->RemoveTaskObserver(&observer);
+}
+
+TEST_F(WebThreadImplForRendererSchedulerTest, TestWorkBatchWithOneTask) {
+ MockTaskObserver observer;
+ thread_->AddTaskObserver(&observer);
+ MockTask task;
+
+ SetWorkBatchSizeForTesting(kWorkBatchSize);
+ {
+ testing::InSequence sequence;
+ EXPECT_CALL(observer, WillProcessTask());
+ EXPECT_CALL(task, Run());
+ EXPECT_CALL(observer, DidProcessTask());
+ }
+
+ thread_->GetTaskRunner()->PostTask(
+ FROM_HERE, WTF::Bind(&MockTask::Run, WTF::Unretained(&task)));
+ base::RunLoop().RunUntilIdle();
+ thread_->RemoveTaskObserver(&observer);
+}
+
+TEST_F(WebThreadImplForRendererSchedulerTest, TestWorkBatchWithTwoTasks) {
+ MockTaskObserver observer;
+ thread_->AddTaskObserver(&observer);
+ MockTask task1;
+ MockTask task2;
+
+ SetWorkBatchSizeForTesting(kWorkBatchSize);
+ {
+ testing::InSequence sequence;
+ EXPECT_CALL(observer, WillProcessTask());
+ EXPECT_CALL(task1, Run());
+ EXPECT_CALL(observer, DidProcessTask());
+
+ EXPECT_CALL(observer, WillProcessTask());
+ EXPECT_CALL(task2, Run());
+ EXPECT_CALL(observer, DidProcessTask());
+ }
+
+ thread_->GetTaskRunner()->PostTask(
+ FROM_HERE, WTF::Bind(&MockTask::Run, WTF::Unretained(&task1)));
+ thread_->GetTaskRunner()->PostTask(
+ FROM_HERE, WTF::Bind(&MockTask::Run, WTF::Unretained(&task2)));
+ base::RunLoop().RunUntilIdle();
+ thread_->RemoveTaskObserver(&observer);
+}
+
+TEST_F(WebThreadImplForRendererSchedulerTest, TestWorkBatchWithThreeTasks) {
+ MockTaskObserver observer;
+ thread_->AddTaskObserver(&observer);
+ MockTask task1;
+ MockTask task2;
+ MockTask task3;
+
+ SetWorkBatchSizeForTesting(kWorkBatchSize);
+ {
+ testing::InSequence sequence;
+ EXPECT_CALL(observer, WillProcessTask());
+ EXPECT_CALL(task1, Run());
+ EXPECT_CALL(observer, DidProcessTask());
+
+ EXPECT_CALL(observer, WillProcessTask());
+ EXPECT_CALL(task2, Run());
+ EXPECT_CALL(observer, DidProcessTask());
+
+ EXPECT_CALL(observer, WillProcessTask());
+ EXPECT_CALL(task3, Run());
+ EXPECT_CALL(observer, DidProcessTask());
+ }
+
+ thread_->GetTaskRunner()->PostTask(
+ FROM_HERE, WTF::Bind(&MockTask::Run, WTF::Unretained(&task1)));
+ thread_->GetTaskRunner()->PostTask(
+ FROM_HERE, WTF::Bind(&MockTask::Run, WTF::Unretained(&task2)));
+ thread_->GetTaskRunner()->PostTask(
+ FROM_HERE, WTF::Bind(&MockTask::Run, WTF::Unretained(&task3)));
+ base::RunLoop().RunUntilIdle();
+ thread_->RemoveTaskObserver(&observer);
+}
+
+void EnterRunLoop(base::MessageLoop* message_loop, blink::WebThread* thread) {
+ // Note: WebThreads do not support nested run loops, which is why we use a
+ // run loop directly.
+ base::RunLoop run_loop;
+ thread->GetTaskRunner()->PostTask(
+ FROM_HERE, WTF::Bind(&base::RunLoop::Quit, WTF::Unretained(&run_loop)));
+ message_loop->SetNestableTasksAllowed(true);
+ run_loop.Run();
+}
+
+TEST_F(WebThreadImplForRendererSchedulerTest, TestNestedRunLoop) {
+ MockTaskObserver observer;
+ thread_->AddTaskObserver(&observer);
+
+ {
+ testing::InSequence sequence;
+
+ // One callback for EnterRunLoop.
+ EXPECT_CALL(observer, WillProcessTask());
+
+ // A pair for ExitRunLoopTask.
+ EXPECT_CALL(observer, WillProcessTask());
+ EXPECT_CALL(observer, DidProcessTask());
+
+ // A final callback for EnterRunLoop.
+ EXPECT_CALL(observer, DidProcessTask());
+ }
+
+ message_loop_.task_runner()->PostTask(
+ FROM_HERE, base::BindOnce(&EnterRunLoop, base::Unretained(&message_loop_),
+ base::Unretained(thread_.get())));
+ base::RunLoop().RunUntilIdle();
+ thread_->RemoveTaskObserver(&observer);
+}
+
+} // namespace webthread_impl_for_renderer_scheduler_unittest
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/util/aggregated_metric_reporter.h b/chromium/third_party/blink/renderer/platform/scheduler/util/aggregated_metric_reporter.h
new file mode 100644
index 00000000000..9e4e423e954
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/util/aggregated_metric_reporter.h
@@ -0,0 +1,89 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_UTIL_AGGREGATED_METRIC_REPORTER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_UTIL_AGGREGATED_METRIC_REPORTER_H_
+
+#include <array>
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/metrics/histogram.h"
+#include "base/threading/thread_checker.h"
+#include "base/time/time.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace base {
+class HistogramBase;
+}
+
+namespace blink {
+namespace scheduler {
+
+// A helper class to report metrics split by a specific type.
+// This class aggregates smaller value and report when it's over threshold
+// to avoid overflows.
+//
+// |TaskClass| is an enum which should have kCount field.
+// All values reported to RecordTask should have lower values.
+template <class TaskClass, class ValueType>
+class AggregatedMetricReporter {
+ public:
+ // Aggregation function: takes ValueType, returns the integer value to return
+ // to histogram and modifies the passed value.
+ // Example: aggregate(time) {
+ // return time.InMilliseconds();
+ // time %= TimeDelta::FromMilliseconds(1);
+ // }
+ using AggregatorFuncPtr = int (*)(ValueType&);
+
+ AggregatedMetricReporter(const char* metric_name,
+ AggregatorFuncPtr aggregator)
+ : AggregatedMetricReporter(
+ base::Histogram::FactoryGet(
+ metric_name,
+ 1,
+ static_cast<int>(TaskClass::kCount),
+ static_cast<int>(TaskClass::kCount) + 1,
+ base::HistogramBase::kUmaTargetedHistogramFlag),
+ aggregator) {}
+
+ ~AggregatedMetricReporter() {}
+
+ void RecordTask(TaskClass task_class, ValueType value) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ DCHECK_LT(static_cast<int>(task_class),
+ static_cast<int>(TaskClass::kCount));
+
+ ValueType& unreported_value =
+ unreported_values_[static_cast<int>(task_class)];
+ unreported_value += value;
+
+ int value_to_report = aggregator_(unreported_value);
+ if (value_to_report > 0) {
+ value_per_type_histogram_->AddCount(static_cast<int>(task_class),
+ value_to_report);
+ }
+ }
+
+ protected:
+ AggregatedMetricReporter(base::HistogramBase* histogram,
+ AggregatorFuncPtr aggregator)
+ : value_per_type_histogram_(histogram), aggregator_(aggregator) {}
+
+ std::array<ValueType, static_cast<size_t>(TaskClass::kCount)>
+ unreported_values_ = {};
+ base::HistogramBase* value_per_type_histogram_;
+ AggregatorFuncPtr aggregator_;
+
+ THREAD_CHECKER(thread_checker_);
+
+ DISALLOW_COPY_AND_ASSIGN(AggregatedMetricReporter);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_UTIL_AGGREGATED_METRIC_REPORTER_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/util/task_duration_metric_reporter.cc b/chromium/third_party/blink/renderer/platform/scheduler/util/task_duration_metric_reporter.cc
new file mode 100644
index 00000000000..37acbae342a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/util/task_duration_metric_reporter.cc
@@ -0,0 +1,19 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/util/task_duration_metric_reporter.h"
+
+namespace blink {
+namespace scheduler {
+namespace internal {
+
+int TakeFullMilliseconds(base::TimeDelta& duration) {
+ int milliseconds = static_cast<int>(duration.InMilliseconds());
+ duration = duration % base::TimeDelta::FromMilliseconds(1);
+ return milliseconds;
+}
+
+} // namespace internal
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/util/task_duration_metric_reporter.h b/chromium/third_party/blink/renderer/platform/scheduler/util/task_duration_metric_reporter.h
new file mode 100644
index 00000000000..f01fe8b4888
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/util/task_duration_metric_reporter.h
@@ -0,0 +1,57 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_UTIL_TASK_DURATION_METRIC_REPORTER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_UTIL_TASK_DURATION_METRIC_REPORTER_H_
+
+#include <array>
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/metrics/histogram.h"
+#include "base/time/time.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/scheduler/util/aggregated_metric_reporter.h"
+
+namespace base {
+class HistogramBase;
+}
+
+namespace blink {
+namespace scheduler {
+namespace internal {
+PLATFORM_EXPORT int TakeFullMilliseconds(base::TimeDelta& duration);
+} // namespace internal
+
+// A helper class to report task duration split by a specific type.
+// Aggregates small tasks internally and reports only whole milliseconds.
+//
+// |TaskClass| is an enum which should have COUNT field.
+// All values reported to RecordTask should have lower values.
+template <class TaskClass>
+class TaskDurationMetricReporter
+ : public AggregatedMetricReporter<TaskClass, base::TimeDelta> {
+ public:
+ explicit TaskDurationMetricReporter(const char* metric_name)
+ : AggregatedMetricReporter<TaskClass, base::TimeDelta>(
+ metric_name,
+ &internal::TakeFullMilliseconds) {}
+
+ ~TaskDurationMetricReporter() = default;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(TaskDurationMetricReporterTest, Test);
+
+ TaskDurationMetricReporter(base::HistogramBase* histogram)
+ : AggregatedMetricReporter<TaskClass, base::TimeDelta>(
+ histogram,
+ &internal::TakeFullMilliseconds) {}
+
+ DISALLOW_COPY_AND_ASSIGN(TaskDurationMetricReporter);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_UTIL_TASK_DURATION_METRIC_REPORTER_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/util/task_duration_metric_reporter_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/util/task_duration_metric_reporter_unittest.cc
new file mode 100644
index 00000000000..9b1e8763044
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/util/task_duration_metric_reporter_unittest.cc
@@ -0,0 +1,87 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/util/task_duration_metric_reporter.h"
+
+#include "base/metrics/histogram_base.h"
+#include "base/metrics/histogram_samples.h"
+#include "base/values.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+namespace scheduler {
+
+namespace {
+
+using testing::_;
+using testing::Mock;
+
+class FakeHistogram : public base::HistogramBase {
+ public:
+ FakeHistogram() : base::HistogramBase("fake") {}
+ ~FakeHistogram() override = default;
+
+ MOCK_METHOD2(AddCount, void(base::HistogramBase::Sample, int));
+ MOCK_CONST_METHOD0(name_hash, uint64_t());
+ MOCK_CONST_METHOD0(GetHistogramType, base::HistogramType());
+ MOCK_CONST_METHOD3(HasConstructionArguments,
+ bool(base::HistogramBase::Sample,
+ base::HistogramBase::Sample,
+ uint32_t));
+ MOCK_METHOD1(Add, void(base::HistogramBase::Sample));
+ MOCK_METHOD1(AddSamples, void(const base::HistogramSamples&));
+ MOCK_CONST_METHOD0(SnapshotSamples,
+ std::unique_ptr<base::HistogramSamples>());
+ MOCK_METHOD0(SnapshotDelta, std::unique_ptr<base::HistogramSamples>());
+ MOCK_CONST_METHOD0(SnapshotFinalDelta,
+ std::unique_ptr<base::HistogramSamples>());
+ MOCK_METHOD1(AddSamplesFromPickle, bool(base::PickleIterator*));
+ MOCK_CONST_METHOD1(WriteHTMLGraph, void(std::string*));
+ MOCK_CONST_METHOD1(WriteAscii, void(std::string*));
+ MOCK_CONST_METHOD1(SerializeInfoImpl, void(base::Pickle*));
+ MOCK_CONST_METHOD1(GetParameters, void(base::DictionaryValue*));
+ MOCK_CONST_METHOD3(GetCountAndBucketData,
+ void(base::HistogramBase::Count*,
+ int64_t*,
+ base::ListValue*));
+};
+
+enum class FakeTaskQueueType {
+ kFakeType0 = 0,
+ kFakeType1 = 1,
+ kFakeType2 = 2,
+ kCount = 3
+};
+
+} // namespace
+
+TEST(TaskDurationMetricReporterTest, Test) {
+ FakeHistogram histogram;
+
+ TaskDurationMetricReporter<FakeTaskQueueType> metric_reporter(&histogram);
+
+ EXPECT_CALL(histogram, AddCount(2, 3));
+ metric_reporter.RecordTask(static_cast<FakeTaskQueueType>(2),
+ base::TimeDelta::FromMicroseconds(3400));
+ Mock::VerifyAndClearExpectations(&histogram);
+
+ EXPECT_CALL(histogram, AddCount(_, _)).Times(0);
+ metric_reporter.RecordTask(static_cast<FakeTaskQueueType>(2),
+ base::TimeDelta::FromMicroseconds(300));
+ Mock::VerifyAndClearExpectations(&histogram);
+
+ EXPECT_CALL(histogram, AddCount(2, 1));
+ metric_reporter.RecordTask(static_cast<FakeTaskQueueType>(2),
+ base::TimeDelta::FromMicroseconds(800));
+ Mock::VerifyAndClearExpectations(&histogram);
+
+ EXPECT_CALL(histogram, AddCount(2, 16));
+ metric_reporter.RecordTask(static_cast<FakeTaskQueueType>(2),
+ base::TimeDelta::FromMicroseconds(15600));
+ Mock::VerifyAndClearExpectations(&histogram);
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/util/thread_cpu_throttler.cc b/chromium/third_party/blink/renderer/platform/scheduler/util/thread_cpu_throttler.cc
new file mode 100644
index 00000000000..2023865282a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/util/thread_cpu_throttler.cc
@@ -0,0 +1,221 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/util/thread_cpu_throttler.h"
+
+#include "base/atomicops.h"
+#include "base/macros.h"
+#include "base/memory/singleton.h"
+#include "base/synchronization/cancellation_flag.h"
+#include "base/threading/platform_thread.h"
+#include "build/build_config.h"
+
+#if defined(OS_POSIX) && !defined(OS_FUCHSIA)
+#include <signal.h>
+#define USE_SIGNALS 1
+#elif defined(OS_WIN)
+#include <windows.h>
+#endif
+
+using base::subtle::Atomic32;
+using base::subtle::Acquire_Load;
+using base::subtle::Release_Store;
+
+namespace blink {
+namespace scheduler {
+
+class ThreadCPUThrottler::ThrottlingThread final
+ : public base::PlatformThread::Delegate {
+ public:
+ explicit ThrottlingThread(double rate);
+ ~ThrottlingThread() override;
+
+ void SetThrottlingRate(double rate);
+
+ private:
+ void ThreadMain() override;
+
+ void Start();
+ void Stop();
+ void Throttle();
+
+ static void SuspendThread(base::PlatformThreadHandle thread_handle);
+ static void ResumeThread(base::PlatformThreadHandle thread_handle);
+ static void Sleep(base::TimeDelta duration);
+
+#ifdef USE_SIGNALS
+ void InstallSignalHandler();
+ void RestoreSignalHandler();
+ static void HandleSignal(int signal);
+
+ static bool signal_handler_installed_;
+ static struct sigaction old_signal_handler_;
+#endif
+ static Atomic32 thread_exists_;
+ static Atomic32 throttling_rate_percent_;
+
+ base::PlatformThreadHandle throttled_thread_handle_;
+ base::PlatformThreadHandle throttling_thread_handle_;
+ base::CancellationFlag cancellation_flag_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThrottlingThread);
+};
+
+#ifdef USE_SIGNALS
+bool ThreadCPUThrottler::ThrottlingThread::signal_handler_installed_;
+struct sigaction ThreadCPUThrottler::ThrottlingThread::old_signal_handler_;
+#endif
+Atomic32 ThreadCPUThrottler::ThrottlingThread::throttling_rate_percent_;
+Atomic32 ThreadCPUThrottler::ThrottlingThread::thread_exists_;
+
+ThreadCPUThrottler::ThrottlingThread::ThrottlingThread(double rate)
+#ifdef OS_WIN
+ : throttled_thread_handle_(
+ ::OpenThread(THREAD_SUSPEND_RESUME, false, ::GetCurrentThreadId())) {
+#else
+ : throttled_thread_handle_(base::PlatformThread::CurrentHandle()) {
+#endif
+ SetThrottlingRate(rate);
+ CHECK_EQ(base::subtle::NoBarrier_AtomicExchange(&thread_exists_, 1), 0);
+ Start();
+}
+
+ThreadCPUThrottler::ThrottlingThread::~ThrottlingThread() {
+ Stop();
+ CHECK_EQ(base::subtle::NoBarrier_AtomicExchange(&thread_exists_, 0), 1);
+}
+
+void ThreadCPUThrottler::ThrottlingThread::SetThrottlingRate(double rate) {
+ Release_Store(&throttling_rate_percent_, static_cast<Atomic32>(rate * 100));
+}
+
+void ThreadCPUThrottler::ThrottlingThread::ThreadMain() {
+ base::PlatformThread::SetName("CPUThrottlingThread");
+ while (!cancellation_flag_.IsSet()) {
+ Throttle();
+ }
+}
+
+#ifdef USE_SIGNALS
+
+// static
+void ThreadCPUThrottler::ThrottlingThread::InstallSignalHandler() {
+ // There must be the only one!
+ DCHECK(!signal_handler_installed_);
+ struct sigaction sa;
+ sa.sa_handler = &HandleSignal;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+ signal_handler_installed_ =
+ (sigaction(SIGUSR2, &sa, &old_signal_handler_) == 0);
+}
+
+// static
+void ThreadCPUThrottler::ThrottlingThread::RestoreSignalHandler() {
+ if (!signal_handler_installed_)
+ return;
+ sigaction(SIGUSR2, &old_signal_handler_, nullptr);
+ signal_handler_installed_ = false;
+}
+
+// static
+void ThreadCPUThrottler::ThrottlingThread::HandleSignal(int signal) {
+ if (signal != SIGUSR2)
+ return;
+ static base::TimeTicks lastResumeTime;
+ base::TimeTicks now = base::TimeTicks::Now();
+ base::TimeDelta run_duration = now - lastResumeTime;
+ uint32_t throttling_rate_percent = Acquire_Load(&throttling_rate_percent_);
+ // Limit the observed run duration to 1000μs to deal with the first entrance
+ // to the signal handler.
+ uint32_t run_duration_us = static_cast<uint32_t>(
+ std::min(run_duration.InMicroseconds(), static_cast<int64_t>(1000)));
+ uint32_t sleep_duration_us =
+ run_duration_us * throttling_rate_percent / 100 - run_duration_us;
+ base::TimeTicks wake_up_time =
+ now + base::TimeDelta::FromMicroseconds(sleep_duration_us);
+ do {
+ now = base::TimeTicks::Now();
+ } while (now < wake_up_time);
+ lastResumeTime = now;
+}
+
+#endif // USE_SIGNALS
+
+void ThreadCPUThrottler::ThrottlingThread::Throttle() {
+ const int quant_time_us = 200;
+#ifdef USE_SIGNALS
+ pthread_kill(throttled_thread_handle_.platform_handle(), SIGUSR2);
+ Sleep(base::TimeDelta::FromMicroseconds(quant_time_us));
+#elif defined(OS_WIN)
+ double rate = Acquire_Load(&throttling_rate_percent_) / 100.;
+ base::TimeDelta run_duration =
+ base::TimeDelta::FromMicroseconds(static_cast<int>(quant_time_us / rate));
+ base::TimeDelta sleep_duration =
+ base::TimeDelta::FromMicroseconds(quant_time_us) - run_duration;
+ Sleep(run_duration);
+ ::SuspendThread(throttled_thread_handle_.platform_handle());
+ Sleep(sleep_duration);
+ ::ResumeThread(throttled_thread_handle_.platform_handle());
+#else
+ ALLOW_UNUSED_LOCAL(quant_time_us);
+#endif
+}
+
+void ThreadCPUThrottler::ThrottlingThread::Start() {
+#ifdef USE_SIGNALS
+ InstallSignalHandler();
+#elif !defined(OS_WIN)
+ LOG(ERROR) << "CPU throttling is not supported.";
+ return;
+#endif
+ if (!base::PlatformThread::Create(0, this, &throttling_thread_handle_)) {
+ LOG(ERROR) << "Failed to create throttling thread.";
+ }
+}
+
+void ThreadCPUThrottler::ThrottlingThread::Sleep(base::TimeDelta duration) {
+#if defined(OS_WIN)
+ // We cannot rely on ::Sleep function as it's precision is not enough for
+ // the purpose. Could be up to 16ms jitter.
+ base::TimeTicks wakeup_time = base::TimeTicks::Now() + duration;
+ while (base::TimeTicks::Now() < wakeup_time) {
+ }
+#else
+ base::PlatformThread::Sleep(duration);
+#endif
+}
+
+void ThreadCPUThrottler::ThrottlingThread::Stop() {
+ cancellation_flag_.Set();
+ base::PlatformThread::Join(throttling_thread_handle_);
+#ifdef USE_SIGNALS
+ RestoreSignalHandler();
+#endif
+}
+
+ThreadCPUThrottler::ThreadCPUThrottler() = default;
+ThreadCPUThrottler::~ThreadCPUThrottler() = default;
+
+void ThreadCPUThrottler::SetThrottlingRate(double rate) {
+ if (rate <= 1) {
+ if (throttling_thread_) {
+ throttling_thread_.reset();
+ }
+ return;
+ }
+ if (throttling_thread_) {
+ throttling_thread_->SetThrottlingRate(rate);
+ } else {
+ throttling_thread_.reset(new ThrottlingThread(rate));
+ }
+}
+
+// static
+ThreadCPUThrottler* ThreadCPUThrottler::GetInstance() {
+ return base::Singleton<ThreadCPUThrottler>::get();
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/util/thread_cpu_throttler.h b/chromium/third_party/blink/renderer/platform/scheduler/util/thread_cpu_throttler.h
new file mode 100644
index 00000000000..95c5c4ca1f5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/util/thread_cpu_throttler.h
@@ -0,0 +1,49 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_UTIL_THREAD_CPU_THROTTLER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_UTIL_THREAD_CPU_THROTTLER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace base {
+template <typename T>
+struct DefaultSingletonTraits;
+};
+
+namespace blink {
+namespace scheduler {
+
+// This class is used to slow down the main thread for
+// inspector "cpu throttling". It does it by spawning an
+// additional thread which frequently interrupts main thread
+// and sleeps.
+class PLATFORM_EXPORT ThreadCPUThrottler final {
+ public:
+ static ThreadCPUThrottler* GetInstance();
+
+ // |rate| is a slow-down factor - passing 2.0 will make
+ // everything two times slower.
+ // Any rate less or equal to 1.0 disables throttling and
+ // cleans up helper thread.
+ void SetThrottlingRate(double rate);
+
+ private:
+ ThreadCPUThrottler();
+ ~ThreadCPUThrottler();
+ friend struct base::DefaultSingletonTraits<ThreadCPUThrottler>;
+
+ class ThrottlingThread;
+ std::unique_ptr<ThrottlingThread> throttling_thread_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadCPUThrottler);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_UTIL_THREAD_CPU_THROTTLER_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/util/thread_load_tracker.cc b/chromium/third_party/blink/renderer/platform/scheduler/util/thread_load_tracker.cc
new file mode 100644
index 00000000000..f84529ea118
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/util/thread_load_tracker.cc
@@ -0,0 +1,125 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/util/thread_load_tracker.h"
+
+#include <algorithm>
+
+namespace blink {
+namespace scheduler {
+
+ThreadLoadTracker::ThreadLoadTracker(base::TimeTicks now,
+ const Callback& callback,
+ base::TimeDelta reporting_interval)
+ : time_(now),
+ thread_state_(ThreadState::kPaused),
+ last_state_change_time_(now),
+ reporting_interval_(reporting_interval),
+ callback_(callback) {
+ next_reporting_time_ = now + reporting_interval_;
+}
+
+ThreadLoadTracker::~ThreadLoadTracker() = default;
+
+void ThreadLoadTracker::Pause(base::TimeTicks now) {
+ Advance(now, TaskState::kIdle);
+ thread_state_ = ThreadState::kPaused;
+
+ Reset(now);
+}
+
+void ThreadLoadTracker::Resume(base::TimeTicks now) {
+ Advance(now, TaskState::kIdle);
+ thread_state_ = ThreadState::kActive;
+
+ Reset(now);
+}
+
+void ThreadLoadTracker::Reset(base::TimeTicks now) {
+ last_state_change_time_ = now;
+ next_reporting_time_ = now + reporting_interval_;
+ run_time_inside_window_ = base::TimeDelta();
+}
+
+void ThreadLoadTracker::RecordTaskTime(base::TimeTicks start_time,
+ base::TimeTicks end_time) {
+ start_time = std::max(last_state_change_time_, start_time);
+ end_time = std::max(last_state_change_time_, end_time);
+
+ Advance(start_time, TaskState::kIdle);
+ Advance(end_time, TaskState::kTaskRunning);
+}
+
+void ThreadLoadTracker::RecordIdle(base::TimeTicks now) {
+ Advance(now, TaskState::kIdle);
+}
+
+namespace {
+
+// Calculates length of intersection of two time intervals.
+base::TimeDelta Intersection(base::TimeTicks left1,
+ base::TimeTicks right1,
+ base::TimeTicks left2,
+ base::TimeTicks right2) {
+ DCHECK_LT(left1, right1);
+ DCHECK_LT(left2, right2);
+ base::TimeTicks left = std::max(left1, left2);
+ base::TimeTicks right = std::min(right1, right2);
+
+ if (left <= right)
+ return right - left;
+
+ return base::TimeDelta();
+}
+
+} // namespace
+
+void ThreadLoadTracker::Advance(base::TimeTicks now, TaskState task_state) {
+ // This function advances |time_| to now and calls |callback_|
+ // when appropriate.
+ DCHECK_LE(time_, now);
+
+ if (thread_state_ == ThreadState::kPaused) {
+ // If the load tracker is paused, bail out early.
+ time_ = now;
+ return;
+ }
+
+ while (time_ < now) {
+ // Advance time_ to the earliest of following:
+ // a) time to call |callback_|
+ // b) requested time to forward (|now|).
+ base::TimeTicks next_current_time = std::min(next_reporting_time_, now);
+
+ base::TimeDelta delta = next_current_time - time_;
+
+ // Keep a running total of the time spent running tasks within the window
+ // and the total time.
+ if (task_state == TaskState::kTaskRunning) {
+ run_time_inside_window_ +=
+ Intersection(next_reporting_time_ - reporting_interval_,
+ next_reporting_time_, time_, time_ + delta);
+ }
+
+ time_ = next_current_time;
+
+ if (time_ == next_reporting_time_) {
+ // Call |callback_| if need and update next callback time.
+ if (thread_state_ == ThreadState::kActive) {
+ callback_.Run(time_, Load());
+ DCHECK_EQ(thread_state_, ThreadState::kActive);
+ }
+ next_reporting_time_ += reporting_interval_;
+ run_time_inside_window_ = base::TimeDelta();
+ }
+ }
+}
+
+double ThreadLoadTracker::Load() {
+ return run_time_inside_window_.InSecondsF() /
+ reporting_interval_.InSecondsF();
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/util/thread_load_tracker.h b/chromium/third_party/blink/renderer/platform/scheduler/util/thread_load_tracker.h
new file mode 100644
index 00000000000..1cd7ee89188
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/util/thread_load_tracker.h
@@ -0,0 +1,73 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_UTIL_THREAD_LOAD_TRACKER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_UTIL_THREAD_LOAD_TRACKER_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+namespace scheduler {
+
+// This class tracks thread load level, i.e. percentage of wall time spent
+// running tasks.
+// In order to avoid bias it reports load level at regular intervals.
+// Every |reporting_interval_| time units, it reports the average thread load
+// level computed using a sliding window of width |reporting_interval_|.
+class PLATFORM_EXPORT ThreadLoadTracker {
+ public:
+ // Callback is called with (current_time, load_level) parameters.
+ using Callback = base::RepeatingCallback<void(base::TimeTicks, double)>;
+
+ ThreadLoadTracker(base::TimeTicks now,
+ const Callback& callback,
+ base::TimeDelta reporting_interval);
+ ~ThreadLoadTracker();
+
+ void Pause(base::TimeTicks now);
+ void Resume(base::TimeTicks now);
+
+ // Note: this does not change |thread_state_|.
+ void Reset(base::TimeTicks now);
+
+ void RecordTaskTime(base::TimeTicks start_time, base::TimeTicks end_time);
+
+ void RecordIdle(base::TimeTicks now);
+
+ // TODO(altimin): Count wake-ups.
+
+ private:
+ enum class ThreadState { kActive, kPaused };
+
+ enum class TaskState { kTaskRunning, kIdle };
+
+ // This function advances |time_| to |now|, calling |callback_|
+ // in the process (multiple times if needed).
+ void Advance(base::TimeTicks now, TaskState task_state);
+
+ double Load();
+
+ // |time_| is the last timestamp LoadTracker knows about.
+ base::TimeTicks time_;
+ base::TimeTicks next_reporting_time_;
+
+ ThreadState thread_state_;
+ base::TimeTicks last_state_change_time_;
+
+ base::TimeDelta reporting_interval_;
+
+ // Recorded run time in window
+ // [next_reporting_time - reporting_interval, next_reporting_time].
+ base::TimeDelta run_time_inside_window_;
+
+ Callback callback_;
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_UTIL_THREAD_LOAD_TRACKER_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/util/thread_load_tracker_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/util/thread_load_tracker_unittest.cc
new file mode 100644
index 00000000000..7f015763b7f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/util/thread_load_tracker_unittest.cc
@@ -0,0 +1,148 @@
+#include "third_party/blink/renderer/platform/scheduler/util/thread_load_tracker.h"
+
+#include "base/bind.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::ElementsAre;
+
+namespace blink {
+namespace scheduler {
+
+namespace {
+
+void AddToVector(std::vector<std::pair<base::TimeTicks, double>>* vector,
+ base::TimeTicks time,
+ double load) {
+ vector->push_back({time, load});
+}
+
+base::TimeTicks SecondsToTime(int seconds) {
+ return base::TimeTicks() + base::TimeDelta::FromSeconds(seconds);
+}
+
+base::TimeTicks MillisecondsToTime(int milliseconds) {
+ return base::TimeTicks() + base::TimeDelta::FromMilliseconds(milliseconds);
+}
+
+} // namespace
+
+TEST(ThreadLoadTrackerTest, RecordTasks) {
+ std::vector<std::pair<base::TimeTicks, double>> result;
+
+ ThreadLoadTracker thread_load_tracker(
+ SecondsToTime(1),
+ base::BindRepeating(&AddToVector, base::Unretained(&result)),
+ base::TimeDelta::FromSeconds(1));
+ thread_load_tracker.Resume(SecondsToTime(1));
+
+ thread_load_tracker.RecordTaskTime(SecondsToTime(1), SecondsToTime(3));
+
+ thread_load_tracker.RecordTaskTime(MillisecondsToTime(4300),
+ MillisecondsToTime(4400));
+
+ thread_load_tracker.RecordTaskTime(MillisecondsToTime(5900),
+ MillisecondsToTime(6100));
+
+ thread_load_tracker.RecordTaskTime(MillisecondsToTime(6700),
+ MillisecondsToTime(6800));
+
+ thread_load_tracker.RecordTaskTime(MillisecondsToTime(7500),
+ MillisecondsToTime(8500));
+
+ thread_load_tracker.RecordIdle(MillisecondsToTime(10500));
+
+ EXPECT_THAT(result, ElementsAre(std::make_pair(SecondsToTime(2), 1.0),
+ std::make_pair(SecondsToTime(3), 1.0),
+ std::make_pair(SecondsToTime(4), 0),
+ std::make_pair(SecondsToTime(5), 0.1),
+ std::make_pair(SecondsToTime(6), 0.1),
+ std::make_pair(SecondsToTime(7), 0.2),
+ std::make_pair(SecondsToTime(8), 0.5),
+ std::make_pair(SecondsToTime(9), 0.5),
+ std::make_pair(SecondsToTime(10), 0)));
+}
+
+TEST(ThreadLoadTrackerTest, PauseAndResume) {
+ std::vector<std::pair<base::TimeTicks, double>> result;
+
+ ThreadLoadTracker thread_load_tracker(
+ SecondsToTime(1),
+ base::BindRepeating(&AddToVector, base::Unretained(&result)),
+ base::TimeDelta::FromSeconds(1));
+ thread_load_tracker.Resume(SecondsToTime(1));
+
+ thread_load_tracker.RecordTaskTime(SecondsToTime(2), SecondsToTime(3));
+ thread_load_tracker.Pause(SecondsToTime(5));
+ thread_load_tracker.RecordTaskTime(SecondsToTime(6), SecondsToTime(7));
+ thread_load_tracker.Resume(SecondsToTime(9));
+ thread_load_tracker.RecordTaskTime(MillisecondsToTime(10900),
+ MillisecondsToTime(11100));
+
+ thread_load_tracker.Pause(SecondsToTime(12));
+
+ thread_load_tracker.RecordTaskTime(MillisecondsToTime(12100),
+ MillisecondsToTime(12200));
+
+ thread_load_tracker.Resume(SecondsToTime(13));
+
+ thread_load_tracker.RecordTaskTime(MillisecondsToTime(13100),
+ MillisecondsToTime(13400));
+
+ thread_load_tracker.RecordIdle(SecondsToTime(14));
+
+ EXPECT_THAT(result, ElementsAre(std::make_pair(SecondsToTime(2), 0),
+ std::make_pair(SecondsToTime(3), 1.0),
+ std::make_pair(SecondsToTime(4), 0),
+ std::make_pair(SecondsToTime(5), 0),
+ std::make_pair(SecondsToTime(10), 0),
+ std::make_pair(SecondsToTime(11), 0.1),
+ std::make_pair(SecondsToTime(12), 0.1),
+ std::make_pair(SecondsToTime(14), 0.3)));
+}
+
+TEST(ThreadLoadTrackerTest, DisabledByDefault) {
+ std::vector<std::pair<base::TimeTicks, double>> result;
+ ThreadLoadTracker thread_load_tracker(
+ SecondsToTime(1),
+ base::BindRepeating(&AddToVector, base::Unretained(&result)),
+ base::TimeDelta::FromSeconds(1));
+
+ // ThreadLoadTracker should be disabled and these tasks should be
+ // ignored.
+ thread_load_tracker.RecordTaskTime(SecondsToTime(1), SecondsToTime(3));
+ thread_load_tracker.RecordTaskTime(SecondsToTime(4), SecondsToTime(7));
+
+ thread_load_tracker.Resume(SecondsToTime(8));
+
+ thread_load_tracker.RecordTaskTime(SecondsToTime(9), SecondsToTime(10));
+
+ EXPECT_THAT(result, ElementsAre(std::make_pair(SecondsToTime(9), 0),
+ std::make_pair(SecondsToTime(10), 1)));
+}
+
+TEST(ThreadLoadTrackerTest, Reset) {
+ std::vector<std::pair<base::TimeTicks, double>> result;
+ ThreadLoadTracker thread_load_tracker(
+ SecondsToTime(1),
+ base::BindRepeating(&AddToVector, base::Unretained(&result)),
+ base::TimeDelta::FromSeconds(1));
+
+ thread_load_tracker.Resume(SecondsToTime(1));
+
+ thread_load_tracker.RecordTaskTime(MillisecondsToTime(1500),
+ MillisecondsToTime(4500));
+
+ thread_load_tracker.Reset(SecondsToTime(100));
+
+ thread_load_tracker.RecordTaskTime(SecondsToTime(101), SecondsToTime(102));
+
+ EXPECT_THAT(result, ElementsAre(std::make_pair(SecondsToTime(2), 0.5),
+ std::make_pair(SecondsToTime(3), 1.0),
+ std::make_pair(SecondsToTime(4), 1.0),
+ std::make_pair(SecondsToTime(101), 0),
+ std::make_pair(SecondsToTime(102), 1)));
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/util/tracing_helper.cc b/chromium/third_party/blink/renderer/platform/scheduler/util/tracing_helper.cc
new file mode 100644
index 00000000000..d8df0ceae74
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/util/tracing_helper.cc
@@ -0,0 +1,101 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/util/tracing_helper.h"
+
+#include "base/format_macros.h"
+#include "base/strings/stringprintf.h"
+
+namespace blink {
+namespace scheduler {
+
+const char kTracingCategoryNameTopLevel[] = "toplevel";
+const char kTracingCategoryNameDefault[] = "renderer.scheduler";
+const char kTracingCategoryNameInfo[] =
+ TRACE_DISABLED_BY_DEFAULT("renderer.scheduler");
+const char kTracingCategoryNameDebug[] =
+ TRACE_DISABLED_BY_DEFAULT("renderer.scheduler.debug");
+
+namespace {
+
+// No trace events should be created with this category.
+const char kTracingCategoryNameVerboseSnapshots[] =
+ TRACE_DISABLED_BY_DEFAULT("renderer.scheduler.enable_verbose_snapshots");
+
+} // namespace
+
+namespace internal {
+
+void ValidateTracingCategory(const char* category) {
+ // Category must be a constant defined in tracing helper because there's no
+ // portable way to use string literals as a template argument.
+ // Unfortunately, static_assert won't work with templates either because
+ // inequality (!=) of linker symbols is undefined in compile-time.
+ DCHECK(category == kTracingCategoryNameTopLevel ||
+ category == kTracingCategoryNameDefault ||
+ category == kTracingCategoryNameInfo ||
+ category == kTracingCategoryNameDebug);
+}
+
+} // namespace internal
+
+bool AreVerboseSnapshotsEnabled() {
+ bool result = false;
+ TRACE_EVENT_CATEGORY_GROUP_ENABLED(kTracingCategoryNameVerboseSnapshots,
+ &result);
+ return result;
+}
+
+void WarmupTracingCategories() {
+ // No need to warm-up toplevel category here.
+ TRACE_EVENT_WARMUP_CATEGORY(kTracingCategoryNameDefault);
+ TRACE_EVENT_WARMUP_CATEGORY(kTracingCategoryNameInfo);
+ TRACE_EVENT_WARMUP_CATEGORY(kTracingCategoryNameDebug);
+ TRACE_EVENT_WARMUP_CATEGORY(kTracingCategoryNameVerboseSnapshots);
+}
+
+std::string PointerToString(const void* pointer) {
+ return base::StringPrintf(
+ "0x%" PRIx64,
+ static_cast<uint64_t>(reinterpret_cast<uintptr_t>(pointer)));
+}
+
+double TimeDeltaToMilliseconds(const base::TimeDelta& value) {
+ return value.InMillisecondsF();
+}
+
+const char* YesNoStateToString(bool is_yes) {
+ if (is_yes) {
+ return "yes";
+ } else {
+ return "no";
+ }
+}
+
+TraceableVariableController::TraceableVariableController() {
+}
+
+TraceableVariableController::~TraceableVariableController() {
+ // Controller should have very same lifetime as their tracers.
+ DCHECK(traceable_variables_.empty());
+}
+
+void TraceableVariableController::RegisterTraceableVariable(
+ TraceableVariable* traceable_variable) {
+ traceable_variables_.insert(traceable_variable);
+}
+
+void TraceableVariableController::DeregisterTraceableVariable(
+ TraceableVariable* traceable_variable) {
+ traceable_variables_.erase(traceable_variable);
+}
+
+void TraceableVariableController::OnTraceLogEnabled() {
+ for (auto tracer : traceable_variables_) {
+ tracer->OnTraceLogEnabled();
+ }
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/util/tracing_helper.h b/chromium/third_party/blink/renderer/platform/scheduler/util/tracing_helper.h
new file mode 100644
index 00000000000..f9d4647b013
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/util/tracing_helper.h
@@ -0,0 +1,367 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_UTIL_TRACING_HELPER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_UTIL_TRACING_HELPER_H_
+
+#include <string>
+#include <unordered_set>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "base/trace_event/trace_event.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+namespace scheduler {
+
+// DISCLAIMER
+// Using these constants in TRACE_EVENTs is discouraged nor should you pass any
+// non-literal string as a category, unless familiar with tracing internals.
+PLATFORM_EXPORT extern const char kTracingCategoryNameTopLevel[];
+PLATFORM_EXPORT extern const char kTracingCategoryNameDefault[];
+PLATFORM_EXPORT extern const char kTracingCategoryNameInfo[];
+PLATFORM_EXPORT extern const char kTracingCategoryNameDebug[];
+
+namespace internal {
+
+PLATFORM_EXPORT void ValidateTracingCategory(const char* category);
+
+} // namespace internal
+
+PLATFORM_EXPORT void WarmupTracingCategories();
+
+PLATFORM_EXPORT bool AreVerboseSnapshotsEnabled();
+
+PLATFORM_EXPORT std::string PointerToString(const void* pointer);
+
+PLATFORM_EXPORT double TimeDeltaToMilliseconds(const base::TimeDelta& value);
+
+PLATFORM_EXPORT const char* YesNoStateToString(bool is_yes);
+
+class TraceableVariable;
+
+// Unfortunately, using |base::trace_event::TraceLog::EnabledStateObserver|
+// wouldn't be helpful in our case because removing one takes linear time
+// and tracers may be created and disposed frequently.
+class PLATFORM_EXPORT TraceableVariableController {
+ public:
+ TraceableVariableController();
+ ~TraceableVariableController();
+
+ // Not thread safe.
+ void RegisterTraceableVariable(TraceableVariable* traceable_variable);
+ void DeregisterTraceableVariable(TraceableVariable* traceable_variable);
+
+ void OnTraceLogEnabled();
+
+ private:
+ std::unordered_set<TraceableVariable*> traceable_variables_;
+};
+
+class TraceableVariable {
+ public:
+ TraceableVariable(TraceableVariableController* controller)
+ : controller_(controller) {
+ controller_->RegisterTraceableVariable(this);
+ }
+
+ virtual ~TraceableVariable() {
+ controller_->DeregisterTraceableVariable(this);
+ }
+
+ virtual void OnTraceLogEnabled() = 0;
+
+ private:
+ TraceableVariableController* const controller_; // Not owned.
+};
+
+// TRACE_EVENT macros define static variable to cache a pointer to the state
+// of category. Hence, we need distinct version for each category in order to
+// prevent unintended leak of state.
+
+template <const char* category>
+class StateTracer {
+ public:
+ StateTracer(const char* name, const void* object)
+ : name_(name), object_(object), slice_is_open_(false) {
+ internal::ValidateTracingCategory(category);
+ }
+
+ ~StateTracer() {
+ if (slice_is_open_)
+ TRACE_EVENT_ASYNC_END0(category, name_, object_);
+ }
+
+ // String will be copied before leaving this function.
+ void TraceString(const String& state) {
+ TraceImpl(state.Utf8().data(), true);
+ }
+
+ // Trace compile-time defined const string, so no copy needed.
+ // Null may be passed to indicate the absence of state.
+ void TraceCompileTimeString(const char* state) {
+ TraceImpl(state, false);
+ }
+
+ protected:
+ bool is_enabled() const {
+ bool result = false;
+ TRACE_EVENT_CATEGORY_GROUP_ENABLED(category, &result); // Cached.
+ return result;
+ }
+
+ private:
+ void TraceImpl(const char* state, bool need_copy) {
+ if (slice_is_open_) {
+ TRACE_EVENT_ASYNC_END0(category, name_, object_);
+ slice_is_open_ = false;
+ }
+ if (!state || !is_enabled())
+ return;
+
+ // Trace viewer logic relies on subslice starting at the exact same time
+ // as the async event.
+ base::TimeTicks now = TRACE_TIME_TICKS_NOW();
+ TRACE_EVENT_ASYNC_BEGIN_WITH_TIMESTAMP0(category, name_, object_, now);
+ if (need_copy) {
+ TRACE_EVENT_ASYNC_STEP_INTO_WITH_TIMESTAMP0(category, name_, object_,
+ TRACE_STR_COPY(state), now);
+ } else {
+ TRACE_EVENT_ASYNC_STEP_INTO_WITH_TIMESTAMP0(category, name_, object_,
+ state, now);
+ }
+ slice_is_open_ = true;
+ }
+
+ const char* const name_; // Not owned.
+ const void* const object_; // Not owned.
+
+ // We have to track whether slice is open to avoid confusion since assignment,
+ // "absent" state and OnTraceLogEnabled can happen anytime.
+ bool slice_is_open_;
+
+ DISALLOW_COPY_AND_ASSIGN(StateTracer);
+};
+
+// TODO(kraynov): Rename to something less generic and reflecting
+// the enum nature of such variables.
+template <typename T, const char* category>
+class TraceableState : public TraceableVariable, private StateTracer<category> {
+ public:
+ // Converter must return compile-time defined const strings because tracing
+ // will not make a copy of them.
+ using ConverterFuncPtr = const char* (*)(T);
+
+ TraceableState(T initial_state,
+ const char* name,
+ const void* object,
+ TraceableVariableController* controller,
+ ConverterFuncPtr converter)
+ : TraceableVariable(controller),
+ StateTracer<category>(name, object),
+ converter_(converter),
+ state_(initial_state) {
+ Trace();
+ }
+
+ ~TraceableState() override = default;
+
+ TraceableState& operator =(const T& value) {
+ Assign(value);
+ return *this;
+ }
+ TraceableState& operator =(const TraceableState& another) {
+ Assign(another.state_);
+ return *this;
+ }
+
+ operator T() const {
+ return state_;
+ }
+ const T& get() const {
+ return state_;
+ }
+
+ void OnTraceLogEnabled() final {
+ Trace();
+ }
+
+ protected:
+ void Assign(T new_state) {
+ if (state_ != new_state) {
+ state_ = new_state;
+ Trace();
+ }
+ }
+
+ void (*mock_trace_for_test_)(const char*) = nullptr;
+
+ private:
+ void Trace() {
+ if (UNLIKELY(mock_trace_for_test_)) {
+ mock_trace_for_test_(converter_(state_));
+ return;
+ }
+
+ // Null state string means the absence of state.
+ const char* state_str = nullptr;
+ if (StateTracer<category>::is_enabled()) {
+ state_str = converter_(state_);
+ }
+
+ // We have to be explicit to deal with two-phase name lookup in templates:
+ // http://blog.llvm.org/2009/12/dreaded-two-phase-name-lookup.html
+ StateTracer<category>::TraceCompileTimeString(state_str);
+ }
+
+ const ConverterFuncPtr converter_;
+ T state_;
+
+ DISALLOW_COPY(TraceableState);
+};
+
+template <typename T, const char* category>
+class TraceableCounter : public TraceableVariable {
+ public:
+ using ConverterFuncPtr = double (*)(const T&);
+
+ TraceableCounter(T initial_value,
+ const char* name,
+ const void* object,
+ TraceableVariableController* controller,
+ ConverterFuncPtr converter)
+ : TraceableVariable(controller),
+ name_(name),
+ object_(object),
+ converter_(converter),
+ value_(initial_value) {
+ internal::ValidateTracingCategory(category);
+ Trace();
+ }
+
+ TraceableCounter(T initial_value,
+ const char* name,
+ const void* object,
+ TraceableVariableController* controller)
+ : TraceableVariable(controller),
+ name_(name),
+ object_(object),
+ converter_([](const T& value) { return static_cast<double>(value); }),
+ value_(initial_value) {
+ internal::ValidateTracingCategory(category);
+ Trace();
+ }
+
+ TraceableCounter& operator =(const T& value) {
+ value_ = value;
+ Trace();
+ return *this;
+ }
+ TraceableCounter& operator =(const TraceableCounter& another) {
+ value_ = another.value_;
+ Trace();
+ return *this;
+ }
+
+ TraceableCounter& operator +=(const T& value) {
+ value_ += value;
+ Trace();
+ return *this;
+ }
+ TraceableCounter& operator -=(const T& value) {
+ value_ -= value;
+ Trace();
+ return *this;
+ }
+
+ const T& value() const {
+ return value_;
+ }
+ const T* operator ->() const {
+ return &value_;
+ }
+ operator T() const {
+ return value_;
+ }
+
+ void OnTraceLogEnabled() final {
+ Trace();
+ }
+
+ void Trace() const {
+ TRACE_COUNTER_ID1(category, name_, object_, converter_(value_));
+ }
+
+ private:
+ const char* const name_; // Not owned.
+ const void* const object_; // Not owned.
+ const ConverterFuncPtr converter_;
+
+ T value_;
+ DISALLOW_COPY(TraceableCounter);
+};
+
+// Add operators when it's needed.
+
+template <typename T, const char* category>
+constexpr T operator -(const TraceableCounter<T, category>& counter) {
+ return -counter.value();
+}
+
+template <typename T, const char* category>
+constexpr T operator /(const TraceableCounter<T, category>& lhs, const T& rhs) {
+ return lhs.value() / rhs;
+}
+
+template <typename T, const char* category>
+constexpr bool operator >(
+ const TraceableCounter<T, category>& lhs, const T& rhs) {
+ return lhs.value() > rhs;
+}
+
+template <typename T, const char* category>
+constexpr bool operator <(
+ const TraceableCounter<T, category>& lhs, const T& rhs) {
+ return lhs.value() < rhs;
+}
+
+template <typename T, const char* category>
+constexpr bool operator !=(
+ const TraceableCounter<T, category>& lhs, const T& rhs) {
+ return lhs.value() != rhs;
+}
+
+template <typename T, const char* category>
+constexpr T operator ++(TraceableCounter<T, category>& counter) {
+ counter = counter.value() + 1;
+ return counter.value();
+}
+
+template <typename T, const char* category>
+constexpr T operator --(TraceableCounter<T, category>& counter) {
+ counter = counter.value() - 1;
+ return counter.value();
+}
+
+template <typename T, const char* category>
+constexpr T operator ++(TraceableCounter<T, category>& counter, int) {
+ T value = counter.value();
+ counter = value + 1;
+ return value;
+}
+
+template <typename T, const char* category>
+constexpr T operator --(TraceableCounter<T, category>& counter, int) {
+ T value = counter.value();
+ counter = value - 1;
+ return value;
+}
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_UTIL_TRACING_HELPER_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/util/tracing_helper_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/util/tracing_helper_unittest.cc
new file mode 100644
index 00000000000..61931729e01
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/util/tracing_helper_unittest.cc
@@ -0,0 +1,98 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/util/tracing_helper.h"
+
+#include <unordered_set>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+namespace scheduler {
+
+namespace {
+
+const char* g_last_state = nullptr;
+
+void ExpectTraced(const char* state) {
+ EXPECT_TRUE(state);
+ EXPECT_TRUE(g_last_state);
+ EXPECT_STREQ(state, g_last_state);
+ g_last_state = nullptr;
+}
+
+void ExpectNotTraced() {
+ EXPECT_FALSE(g_last_state);
+}
+
+const char* SignOfInt(int value) {
+ if (value > 0)
+ return "positive";
+ if (value < 0)
+ return "negative";
+ return "zero";
+}
+
+class TraceableStateForTest
+ : public TraceableState<int, kTracingCategoryNameDefault> {
+ public:
+ TraceableStateForTest(TraceableVariableController* controller)
+ : TraceableState(0, "State", controller, controller, SignOfInt) {
+ // We shouldn't expect trace in constructor here because mock isn't set yet.
+ mock_trace_for_test_ = &MockTrace;
+ }
+
+ TraceableStateForTest& operator =(const int& value) {
+ Assign(value);
+ return *this;
+ }
+
+ static void MockTrace(const char* state) {
+ EXPECT_TRUE(state);
+ EXPECT_FALSE(g_last_state); // No unexpected traces.
+ g_last_state = state;
+ }
+};
+
+} // namespace
+
+// TODO(kraynov): TraceableCounter tests.
+
+TEST(TracingHelperTest, TraceableState) {
+ TraceableVariableController controller;
+ TraceableStateForTest state(&controller);
+ controller.OnTraceLogEnabled();
+ ExpectTraced("zero");
+ state = 0;
+ ExpectNotTraced();
+ state = 1;
+ ExpectTraced("positive");
+ state = -1;
+ ExpectTraced("negative");
+}
+
+TEST(TracingHelperTest, TraceableStateOperators) {
+ TraceableVariableController controller;
+ TraceableState<int, kTracingCategoryNameDebug> x(
+ -1, "X", &controller, &controller, SignOfInt);
+ TraceableState<int, kTracingCategoryNameDebug> y(
+ 1, "Y", &controller, &controller, SignOfInt);
+ EXPECT_EQ(0, x + y);
+ EXPECT_FALSE(x == y);
+ EXPECT_TRUE(x != y);
+ x = 1;
+ EXPECT_EQ(0, y - x);
+ EXPECT_EQ(2, x + y);
+ EXPECT_EQ(x, y);
+ EXPECT_FALSE(x != y);
+ EXPECT_NE(x + y, 3);
+ EXPECT_EQ(2 - y + 1 + x, 3);
+ x = 3;
+ y = 2;
+ int z = x = y;
+ EXPECT_EQ(2, z);
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/utility/webthread_impl_for_utility_thread.cc b/chromium/third_party/blink/renderer/platform/scheduler/utility/webthread_impl_for_utility_thread.cc
new file mode 100644
index 00000000000..1d1d37ee582
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/utility/webthread_impl_for_utility_thread.cc
@@ -0,0 +1,41 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/utility/webthread_impl_for_utility_thread.h"
+
+#include "base/threading/thread_task_runner_handle.h"
+
+namespace blink {
+namespace scheduler {
+
+WebThreadImplForUtilityThread::WebThreadImplForUtilityThread()
+ : task_runner_(base::ThreadTaskRunnerHandle::Get()),
+ thread_id_(base::PlatformThread::CurrentId()) {}
+
+WebThreadImplForUtilityThread::~WebThreadImplForUtilityThread() = default;
+
+blink::WebScheduler* WebThreadImplForUtilityThread::Scheduler() const {
+ NOTIMPLEMENTED();
+ return nullptr;
+}
+
+blink::PlatformThreadId WebThreadImplForUtilityThread::ThreadId() const {
+ return thread_id_;
+}
+
+scoped_refptr<base::SingleThreadTaskRunner>
+WebThreadImplForUtilityThread::GetTaskRunner() const {
+ return task_runner_;
+}
+
+scheduler::SingleThreadIdleTaskRunner*
+WebThreadImplForUtilityThread::GetIdleTaskRunner() const {
+ NOTIMPLEMENTED();
+ return nullptr;
+}
+
+void WebThreadImplForUtilityThread::Init() {}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/utility/webthread_impl_for_utility_thread.h b/chromium/third_party/blink/renderer/platform/scheduler/utility/webthread_impl_for_utility_thread.h
new file mode 100644
index 00000000000..b6316416bfc
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/utility/webthread_impl_for_utility_thread.h
@@ -0,0 +1,41 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_UTILITY_WEBTHREAD_IMPL_FOR_UTILITY_THREAD_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_UTILITY_WEBTHREAD_IMPL_FOR_UTILITY_THREAD_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/public/platform/scheduler/child/webthread_base.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+namespace scheduler {
+
+class PLATFORM_EXPORT WebThreadImplForUtilityThread
+ : public scheduler::WebThreadBase {
+ public:
+ WebThreadImplForUtilityThread();
+ ~WebThreadImplForUtilityThread() override;
+
+ // WebThread implementation.
+ WebScheduler* Scheduler() const override;
+ PlatformThreadId ThreadId() const override;
+ scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() const override;
+
+ // WebThreadBase implementation.
+ scheduler::SingleThreadIdleTaskRunner* GetIdleTaskRunner() const override;
+ void Init() override;
+
+ private:
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ PlatformThreadId thread_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebThreadImplForUtilityThread);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_UTILITY_WEBTHREAD_IMPL_FOR_UTILITY_THREAD_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/worker/DEPS b/chromium/third_party/blink/renderer/platform/scheduler/worker/DEPS
new file mode 100644
index 00000000000..53701134c2e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/worker/DEPS
@@ -0,0 +1,5 @@
+specific_include_rules = {
+ ".*test\.cc": [
+ "+components/viz/test",
+ ],
+}
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.cc b/chromium/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.cc
new file mode 100644
index 00000000000..4e7ea136d11
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.cc
@@ -0,0 +1,102 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/callback.h"
+#include "base/message_loop/message_loop.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue.h"
+#include "third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h"
+
+namespace blink {
+namespace scheduler {
+
+CompositorThreadScheduler::CompositorThreadScheduler(
+ base::Thread* thread,
+ std::unique_ptr<TaskQueueManager> task_queue_manager)
+ : NonMainThreadScheduler(
+ std::make_unique<WorkerSchedulerHelper>(std::move(task_queue_manager),
+ this)),
+ thread_(thread) {}
+
+CompositorThreadScheduler::~CompositorThreadScheduler() = default;
+
+scoped_refptr<WorkerTaskQueue> CompositorThreadScheduler::DefaultTaskQueue() {
+ return helper_->DefaultWorkerTaskQueue();
+}
+
+void CompositorThreadScheduler::Init() {}
+
+void CompositorThreadScheduler::OnTaskCompleted(
+ WorkerTaskQueue* worker_task_queue,
+ const TaskQueue::Task& task,
+ base::TimeTicks start,
+ base::TimeTicks end,
+ base::Optional<base::TimeDelta> thread_time) {
+ compositor_metrics_helper_.RecordTaskMetrics(worker_task_queue, task, start,
+ end, thread_time);
+}
+
+scoped_refptr<base::SingleThreadTaskRunner>
+CompositorThreadScheduler::DefaultTaskRunner() {
+ return DefaultTaskQueue();
+}
+
+scoped_refptr<scheduler::SingleThreadIdleTaskRunner>
+CompositorThreadScheduler::IdleTaskRunner() {
+ // TODO(flackr): This posts idle tasks as regular tasks. We need to create
+ // an idle task runner with the semantics we want for the compositor thread
+ // which runs them after the current frame has been drawn before the next
+ // vsync. https://crbug.com/609532
+ return base::MakeRefCounted<SingleThreadIdleTaskRunner>(
+ thread_->task_runner(), this);
+}
+
+scoped_refptr<base::SingleThreadTaskRunner>
+CompositorThreadScheduler::IPCTaskRunner() {
+ return base::ThreadTaskRunnerHandle::Get();
+}
+
+bool CompositorThreadScheduler::CanExceedIdleDeadlineIfRequired() const {
+ return false;
+}
+
+bool CompositorThreadScheduler::ShouldYieldForHighPriorityWork() {
+ return false;
+}
+
+void CompositorThreadScheduler::AddTaskObserver(
+ base::MessageLoop::TaskObserver* task_observer) {
+ helper_->AddTaskObserver(task_observer);
+}
+
+void CompositorThreadScheduler::RemoveTaskObserver(
+ base::MessageLoop::TaskObserver* task_observer) {
+ helper_->RemoveTaskObserver(task_observer);
+}
+
+void CompositorThreadScheduler::Shutdown() {}
+
+void CompositorThreadScheduler::OnIdleTaskPosted() {}
+
+base::TimeTicks CompositorThreadScheduler::WillProcessIdleTask() {
+ // TODO(flackr): Return the next frame time as the deadline instead.
+ // TODO(flackr): Ensure that oilpan GC does happen on the compositor thread
+ // even though we will have no long idle periods. https://crbug.com/609531
+ return base::TimeTicks::Now() + base::TimeDelta::FromMillisecondsD(16.7);
+}
+
+void CompositorThreadScheduler::DidProcessIdleTask() {}
+
+base::TimeTicks CompositorThreadScheduler::NowTicks() {
+ return base::TimeTicks::Now();
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.h b/chromium/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.h
new file mode 100644
index 00000000000..8768e3b4af8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.h
@@ -0,0 +1,73 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_WORKER_COMPOSITOR_THREAD_SCHEDULER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_WORKER_COMPOSITOR_THREAD_SCHEDULER_H_
+
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "third_party/blink/public/platform/scheduler/single_thread_idle_task_runner.h"
+#include "third_party/blink/public/platform/web_thread_type.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/scheduler/child/compositor_metrics_helper.h"
+#include "third_party/blink/renderer/platform/scheduler/public/non_main_thread_scheduler.h"
+#include "third_party/blink/renderer/platform/scheduler/util/task_duration_metric_reporter.h"
+
+namespace base {
+class Thread;
+}
+
+namespace blink {
+namespace scheduler {
+
+class PLATFORM_EXPORT CompositorThreadScheduler
+ : public NonMainThreadScheduler,
+ public SingleThreadIdleTaskRunner::Delegate {
+ public:
+ CompositorThreadScheduler(
+ base::Thread* thread,
+ std::unique_ptr<TaskQueueManager> task_queue_manager);
+
+ ~CompositorThreadScheduler() override;
+
+ // WorkerScheduler:
+ scoped_refptr<WorkerTaskQueue> DefaultTaskQueue() override;
+ void Init() override;
+ void OnTaskCompleted(WorkerTaskQueue* worker_task_queue,
+ const TaskQueue::Task& task,
+ base::TimeTicks start,
+ base::TimeTicks end,
+ base::Optional<base::TimeDelta> thread_time) override;
+
+ // ChildScheduler:
+ scoped_refptr<base::SingleThreadTaskRunner> DefaultTaskRunner() override;
+ scoped_refptr<scheduler::SingleThreadIdleTaskRunner> IdleTaskRunner()
+ override;
+ scoped_refptr<base::SingleThreadTaskRunner> IPCTaskRunner() override;
+ bool ShouldYieldForHighPriorityWork() override;
+ bool CanExceedIdleDeadlineIfRequired() const override;
+ void AddTaskObserver(base::MessageLoop::TaskObserver* task_observer) override;
+ void RemoveTaskObserver(
+ base::MessageLoop::TaskObserver* task_observer) override;
+ void Shutdown() override;
+
+ // SingleThreadIdleTaskRunner::Delegate:
+ void OnIdleTaskPosted() override;
+ base::TimeTicks WillProcessIdleTask() override;
+ void DidProcessIdleTask() override;
+ base::TimeTicks NowTicks() override;
+
+ private:
+ base::Thread* thread_;
+
+ CompositorMetricsHelper compositor_metrics_helper_;
+
+ DISALLOW_COPY_AND_ASSIGN(CompositorThreadScheduler);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_WORKER_COMPOSITOR_THREAD_SCHEDULER_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/worker/non_main_thread_scheduler.cc b/chromium/third_party/blink/renderer/platform/scheduler/worker/non_main_thread_scheduler.cc
new file mode 100644
index 00000000000..506fda0a632
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/worker/non_main_thread_scheduler.cc
@@ -0,0 +1,37 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/public/non_main_thread_scheduler.h"
+
+#include <utility>
+
+#include "base/message_loop/message_loop.h"
+#include "third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h"
+
+namespace blink {
+namespace scheduler {
+
+NonMainThreadScheduler::NonMainThreadScheduler(
+ std::unique_ptr<WorkerSchedulerHelper> helper)
+ : helper_(std::move(helper)) {}
+
+NonMainThreadScheduler::~NonMainThreadScheduler() = default;
+
+// static
+std::unique_ptr<NonMainThreadScheduler> NonMainThreadScheduler::Create(
+ WebThreadType thread_type,
+ WorkerSchedulerProxy* proxy) {
+ return std::make_unique<WorkerThreadScheduler>(
+ thread_type, TaskQueueManager::TakeOverCurrentThread(), proxy);
+}
+
+scoped_refptr<WorkerTaskQueue> NonMainThreadScheduler::CreateTaskRunner() {
+ helper_->CheckOnValidThread();
+ return helper_->NewTaskQueue(TaskQueue::Spec("worker_tq")
+ .SetShouldMonitorQuiescence(true)
+ .SetTimeDomain(nullptr));
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_helper.cc b/chromium/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_helper.cc
new file mode 100644
index 00000000000..e66380e2f93
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_helper.cc
@@ -0,0 +1,52 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_helper.h"
+
+#include "third_party/blink/renderer/platform/scheduler/child/worker_task_queue.h"
+
+namespace blink {
+namespace scheduler {
+
+WorkerSchedulerHelper::WorkerSchedulerHelper(
+ std::unique_ptr<TaskQueueManager> task_queue_manager,
+ NonMainThreadScheduler* non_main_thread_scheduler)
+ : SchedulerHelper(std::move(task_queue_manager)),
+ non_main_thread_scheduler_(non_main_thread_scheduler),
+ default_task_queue_(NewTaskQueue(TaskQueue::Spec("worker_default_tq")
+ .SetShouldMonitorQuiescence(true))),
+ control_task_queue_(NewTaskQueue(TaskQueue::Spec("worker_control_tq")
+ .SetShouldNotifyObservers(false))) {
+ InitDefaultQueues(default_task_queue_, control_task_queue_);
+}
+
+WorkerSchedulerHelper::~WorkerSchedulerHelper() {
+ control_task_queue_->ShutdownTaskQueue();
+ default_task_queue_->ShutdownTaskQueue();
+}
+
+scoped_refptr<WorkerTaskQueue> WorkerSchedulerHelper::DefaultWorkerTaskQueue() {
+ return default_task_queue_;
+}
+
+scoped_refptr<TaskQueue> WorkerSchedulerHelper::DefaultTaskQueue() {
+ return default_task_queue_;
+}
+
+scoped_refptr<WorkerTaskQueue> WorkerSchedulerHelper::ControlWorkerTaskQueue() {
+ return control_task_queue_;
+}
+
+scoped_refptr<TaskQueue> WorkerSchedulerHelper::ControlTaskQueue() {
+ return control_task_queue_;
+}
+
+scoped_refptr<WorkerTaskQueue> WorkerSchedulerHelper::NewTaskQueue(
+ const TaskQueue::Spec& spec) {
+ return task_queue_manager_->CreateTaskQueue<WorkerTaskQueue>(
+ spec, non_main_thread_scheduler_);
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_helper.h b/chromium/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_helper.h
new file mode 100644
index 00000000000..895fbd44f97
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_helper.h
@@ -0,0 +1,43 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_WORKER_WORKER_SCHEDULER_HELPER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_WORKER_WORKER_SCHEDULER_HELPER_H_
+
+#include "third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h"
+
+#include "third_party/blink/renderer/platform/scheduler/child/worker_task_queue.h"
+
+namespace blink {
+namespace scheduler {
+
+class NonMainThreadScheduler;
+
+class PLATFORM_EXPORT WorkerSchedulerHelper : public SchedulerHelper {
+ public:
+ WorkerSchedulerHelper(std::unique_ptr<TaskQueueManager> manager,
+ NonMainThreadScheduler* non_main_thread_scheduler);
+ ~WorkerSchedulerHelper() override;
+
+ scoped_refptr<WorkerTaskQueue> NewTaskQueue(const TaskQueue::Spec& spec);
+
+ scoped_refptr<WorkerTaskQueue> DefaultWorkerTaskQueue();
+ scoped_refptr<WorkerTaskQueue> ControlWorkerTaskQueue();
+
+ protected:
+ scoped_refptr<TaskQueue> DefaultTaskQueue() override;
+ scoped_refptr<TaskQueue> ControlTaskQueue() override;
+
+ private:
+ NonMainThreadScheduler* non_main_thread_scheduler_; // NOT OWNED
+ const scoped_refptr<WorkerTaskQueue> default_task_queue_;
+ const scoped_refptr<WorkerTaskQueue> control_task_queue_;
+
+ DISALLOW_COPY_AND_ASSIGN(WorkerSchedulerHelper);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_WORKER_WORKER_SCHEDULER_HELPER_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.cc b/chromium/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.cc
new file mode 100644
index 00000000000..ad0fb1866db
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.cc
@@ -0,0 +1,201 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/trace_event/trace_event.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "third_party/blink/renderer/platform/histogram.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_manager.h"
+#include "third_party/blink/renderer/platform/scheduler/child/default_params.h"
+#include "third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_helper.h"
+#include "third_party/blink/renderer/platform/scheduler/child/worker_scheduler_proxy.h"
+
+namespace blink {
+namespace scheduler {
+
+namespace {
+// Workers could be short-lived, set a shorter interval than
+// the renderer thread.
+constexpr base::TimeDelta kUnspecifiedWorkerThreadLoadTrackerReportingInterval =
+ base::TimeDelta::FromSeconds(1);
+
+void ReportWorkerTaskLoad(base::TimeTicks time, double load) {
+ int load_percentage = static_cast<int>(load * 100);
+ DCHECK_LE(load_percentage, 100);
+ // TODO(kinuko): Maybe we also want to separately log when the associated
+ // tab is in foreground and when not.
+ UMA_HISTOGRAM_PERCENTAGE("WorkerScheduler.WorkerThreadLoad", load_percentage);
+}
+
+// TODO(scheduler-dev): Remove conversions when Blink starts using
+// base::TimeTicks instead of doubles for time.
+base::TimeTicks MonotonicTimeInSecondsToTimeTicks(
+ double monotonic_time_in_seconds) {
+ return base::TimeTicks() +
+ base::TimeDelta::FromSecondsD(monotonic_time_in_seconds);
+}
+
+} // namespace
+
+WorkerThreadScheduler::WorkerThreadScheduler(
+ WebThreadType thread_type,
+ std::unique_ptr<TaskQueueManager> task_queue_manager,
+ WorkerSchedulerProxy* proxy)
+ : NonMainThreadScheduler(
+ std::make_unique<WorkerSchedulerHelper>(std::move(task_queue_manager),
+ this)),
+ idle_helper_(helper_.get(),
+ this,
+ "WorkerSchedulerIdlePeriod",
+ base::TimeDelta::FromMilliseconds(300),
+ helper_->NewTaskQueue(TaskQueue::Spec("worker_idle_tq"))),
+ idle_canceled_delayed_task_sweeper_(helper_.get(),
+ idle_helper_.IdleTaskRunner()),
+ load_tracker_(helper_->NowTicks(),
+ base::BindRepeating(&ReportWorkerTaskLoad),
+ kUnspecifiedWorkerThreadLoadTrackerReportingInterval),
+ throttling_state_(proxy ? proxy->throttling_state()
+ : FrameScheduler::ThrottlingState::kNotThrottled),
+ worker_metrics_helper_(thread_type),
+ weak_factory_(this) {
+ thread_start_time_ = helper_->NowTicks();
+ load_tracker_.Resume(thread_start_time_);
+ helper_->AddTaskTimeObserver(this);
+
+ if (proxy) {
+ worker_metrics_helper_.SetParentFrameType(proxy->parent_frame_type());
+ proxy->OnWorkerSchedulerCreated(GetWeakPtr());
+ }
+
+ TRACE_EVENT_OBJECT_CREATED_WITH_ID(
+ TRACE_DISABLED_BY_DEFAULT("worker.scheduler"), "WorkerScheduler", this);
+}
+
+WorkerThreadScheduler::~WorkerThreadScheduler() {
+ TRACE_EVENT_OBJECT_DELETED_WITH_ID(
+ TRACE_DISABLED_BY_DEFAULT("worker.scheduler"), "WorkerScheduler", this);
+
+ helper_->RemoveTaskTimeObserver(this);
+}
+
+scoped_refptr<base::SingleThreadTaskRunner>
+WorkerThreadScheduler::DefaultTaskRunner() {
+ DCHECK(initialized_);
+ return helper_->DefaultWorkerTaskQueue();
+}
+
+scoped_refptr<SingleThreadIdleTaskRunner>
+WorkerThreadScheduler::IdleTaskRunner() {
+ DCHECK(initialized_);
+ return idle_helper_.IdleTaskRunner();
+}
+
+scoped_refptr<base::SingleThreadTaskRunner>
+WorkerThreadScheduler::IPCTaskRunner() {
+ return base::ThreadTaskRunnerHandle::Get();
+}
+
+bool WorkerThreadScheduler::CanExceedIdleDeadlineIfRequired() const {
+ DCHECK(initialized_);
+ return idle_helper_.CanExceedIdleDeadlineIfRequired();
+}
+
+bool WorkerThreadScheduler::ShouldYieldForHighPriorityWork() {
+ // We don't consider any work as being high priority on workers.
+ return false;
+}
+
+void WorkerThreadScheduler::AddTaskObserver(
+ base::MessageLoop::TaskObserver* task_observer) {
+ DCHECK(initialized_);
+ helper_->AddTaskObserver(task_observer);
+}
+
+void WorkerThreadScheduler::RemoveTaskObserver(
+ base::MessageLoop::TaskObserver* task_observer) {
+ DCHECK(initialized_);
+ helper_->RemoveTaskObserver(task_observer);
+}
+
+void WorkerThreadScheduler::Shutdown() {
+ DCHECK(initialized_);
+ load_tracker_.RecordIdle(helper_->NowTicks());
+ base::TimeTicks end_time = helper_->NowTicks();
+ base::TimeDelta delta = end_time - thread_start_time_;
+
+ // The lifetime could be radically different for different workers,
+ // some workers could be short-lived (but last at least 1 sec in
+ // Service Workers case) or could be around as long as the tab is open.
+ UMA_HISTOGRAM_CUSTOM_TIMES(
+ "WorkerThread.Runtime", delta, base::TimeDelta::FromSeconds(1),
+ base::TimeDelta::FromDays(1), 50 /* bucket count */);
+ helper_->Shutdown();
+}
+
+scoped_refptr<WorkerTaskQueue> WorkerThreadScheduler::DefaultTaskQueue() {
+ DCHECK(initialized_);
+ return helper_->DefaultWorkerTaskQueue();
+}
+
+void WorkerThreadScheduler::Init() {
+ initialized_ = true;
+ idle_helper_.EnableLongIdlePeriod();
+}
+
+void WorkerThreadScheduler::OnTaskCompleted(
+ WorkerTaskQueue* worker_task_queue,
+ const TaskQueue::Task& task,
+ base::TimeTicks start,
+ base::TimeTicks end,
+ base::Optional<base::TimeDelta> thread_time) {
+ worker_metrics_helper_.RecordTaskMetrics(worker_task_queue, task, start, end,
+ thread_time);
+}
+
+SchedulerHelper* WorkerThreadScheduler::GetSchedulerHelperForTesting() {
+ return helper_.get();
+}
+
+bool WorkerThreadScheduler::CanEnterLongIdlePeriod(base::TimeTicks,
+ base::TimeDelta*) {
+ return true;
+}
+
+base::TimeTicks WorkerThreadScheduler::CurrentIdleTaskDeadlineForTesting()
+ const {
+ return idle_helper_.CurrentIdleTaskDeadline();
+}
+
+void WorkerThreadScheduler::WillProcessTask(double start_time) {}
+
+void WorkerThreadScheduler::DidProcessTask(double start_time, double end_time) {
+ base::TimeTicks start_time_ticks =
+ MonotonicTimeInSecondsToTimeTicks(start_time);
+ base::TimeTicks end_time_ticks = MonotonicTimeInSecondsToTimeTicks(end_time);
+
+ load_tracker_.RecordTaskTime(start_time_ticks, end_time_ticks);
+}
+
+void WorkerThreadScheduler::OnThrottlingStateChanged(
+ FrameScheduler::ThrottlingState throttling_state) {
+ throttling_state_ = throttling_state;
+}
+
+scoped_refptr<WorkerTaskQueue> WorkerThreadScheduler::ControlTaskQueue() {
+ return helper_->ControlWorkerTaskQueue();
+}
+
+base::WeakPtr<WorkerThreadScheduler> WorkerThreadScheduler::GetWeakPtr() {
+ return weak_factory_.GetWeakPtr();
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h b/chromium/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h
new file mode 100644
index 00000000000..7391e257bfe
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h
@@ -0,0 +1,110 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_WORKER_WORKER_THREAD_SCHEDULER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_WORKER_WORKER_THREAD_SCHEDULER_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "third_party/blink/public/platform/web_thread_type.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_time_observer.h"
+#include "third_party/blink/renderer/platform/scheduler/child/idle_canceled_delayed_task_sweeper.h"
+#include "third_party/blink/renderer/platform/scheduler/child/idle_helper.h"
+#include "third_party/blink/renderer/platform/scheduler/child/worker_metrics_helper.h"
+#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
+#include "third_party/blink/renderer/platform/scheduler/public/non_main_thread_scheduler.h"
+#include "third_party/blink/renderer/platform/scheduler/util/task_duration_metric_reporter.h"
+#include "third_party/blink/renderer/platform/scheduler/util/thread_load_tracker.h"
+
+namespace blink {
+namespace scheduler {
+
+class TaskQueueManager;
+class WorkerSchedulerProxy;
+
+class PLATFORM_EXPORT WorkerThreadScheduler : public NonMainThreadScheduler,
+ public IdleHelper::Delegate,
+ public TaskTimeObserver {
+ public:
+ WorkerThreadScheduler(WebThreadType thread_type,
+ std::unique_ptr<TaskQueueManager> task_queue_manager,
+ WorkerSchedulerProxy* proxy);
+ ~WorkerThreadScheduler() override;
+
+ // WebThreadScheduler implementation:
+ scoped_refptr<base::SingleThreadTaskRunner> DefaultTaskRunner() override;
+ scoped_refptr<SingleThreadIdleTaskRunner> IdleTaskRunner() override;
+ scoped_refptr<base::SingleThreadTaskRunner> IPCTaskRunner() override;
+ bool ShouldYieldForHighPriorityWork() override;
+ bool CanExceedIdleDeadlineIfRequired() const override;
+ void AddTaskObserver(base::MessageLoop::TaskObserver* task_observer) override;
+ void RemoveTaskObserver(
+ base::MessageLoop::TaskObserver* task_observer) override;
+ void Shutdown() override;
+
+ // NonMainThreadScheduler implementation:
+ scoped_refptr<WorkerTaskQueue> DefaultTaskQueue() override;
+ void Init() override;
+ void OnTaskCompleted(WorkerTaskQueue* worker_task_queue,
+ const TaskQueue::Task& task,
+ base::TimeTicks start,
+ base::TimeTicks end,
+ base::Optional<base::TimeDelta> thread_time) override;
+
+ // TaskTimeObserver implementation:
+ void WillProcessTask(double start_time) override;
+ void DidProcessTask(double start_time, double end_time) override;
+
+ SchedulerHelper* GetSchedulerHelperForTesting();
+ base::TimeTicks CurrentIdleTaskDeadlineForTesting() const;
+
+ // Virtual for test.
+ virtual void OnThrottlingStateChanged(
+ FrameScheduler::ThrottlingState throttling_state);
+
+ // Returns the control task queue. Tasks posted to this queue are executed
+ // with the highest priority. Care must be taken to avoid starvation of other
+ // task queues.
+ scoped_refptr<WorkerTaskQueue> ControlTaskQueue();
+
+ protected:
+ // IdleHelper::Delegate implementation:
+ bool CanEnterLongIdlePeriod(
+ base::TimeTicks now,
+ base::TimeDelta* next_long_idle_period_delay_out) override;
+ void IsNotQuiescent() override {}
+ void OnIdlePeriodStarted() override {}
+ void OnIdlePeriodEnded() override {}
+ void OnPendingTasksChanged(bool new_state) override {}
+
+ FrameScheduler::ThrottlingState throttling_state() const {
+ return throttling_state_;
+ }
+
+ private:
+ void MaybeStartLongIdlePeriod();
+
+ base::WeakPtr<WorkerThreadScheduler> GetWeakPtr();
+
+ IdleHelper idle_helper_;
+ IdleCanceledDelayedTaskSweeper idle_canceled_delayed_task_sweeper_;
+ ThreadLoadTracker load_tracker_;
+ bool initialized_;
+ base::TimeTicks thread_start_time_;
+ scoped_refptr<WorkerTaskQueue> control_task_queue_;
+ FrameScheduler::ThrottlingState throttling_state_;
+
+ WorkerMetricsHelper worker_metrics_helper_;
+
+ base::WeakPtrFactory<WorkerThreadScheduler> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(WorkerThreadScheduler);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_WORKER_WORKER_THREAD_SCHEDULER_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler_unittest.cc
new file mode 100644
index 00000000000..fe4fb13d42a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler_unittest.cc
@@ -0,0 +1,407 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h"
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "components/viz/test/ordered_simple_task_runner.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/scheduler/test/task_queue_manager_for_test.h"
+
+using testing::ElementsAreArray;
+
+namespace blink {
+namespace scheduler {
+// To avoid symbol collisions in jumbo builds.
+namespace worker_thread_scheduler_unittest {
+
+void NopTask() {}
+
+int TimeTicksToIntMs(const base::TimeTicks& time) {
+ return static_cast<int>((time - base::TimeTicks()).InMilliseconds());
+}
+
+void RecordTimelineTask(std::vector<std::string>* timeline,
+ base::SimpleTestTickClock* clock) {
+ timeline->push_back(base::StringPrintf("run RecordTimelineTask @ %d",
+ TimeTicksToIntMs(clock->NowTicks())));
+}
+
+void AppendToVectorTestTask(std::vector<std::string>* vector,
+ std::string value) {
+ vector->push_back(value);
+}
+
+void AppendToVectorIdleTestTask(std::vector<std::string>* vector,
+ std::string value,
+ base::TimeTicks deadline) {
+ AppendToVectorTestTask(vector, value);
+}
+
+void TimelineIdleTestTask(std::vector<std::string>* timeline,
+ base::TimeTicks deadline) {
+ timeline->push_back(base::StringPrintf("run TimelineIdleTestTask deadline %d",
+ TimeTicksToIntMs(deadline)));
+}
+
+class WorkerThreadSchedulerForTest : public WorkerThreadScheduler {
+ public:
+ WorkerThreadSchedulerForTest(std::unique_ptr<TaskQueueManager> manager,
+ base::SimpleTestTickClock* clock_)
+ : WorkerThreadScheduler(WebThreadType::kTestThread,
+ std::move(manager),
+ nullptr),
+ clock_(clock_),
+ timeline_(nullptr) {}
+
+ void RecordTimelineEvents(std::vector<std::string>* timeline) {
+ timeline_ = timeline;
+ }
+
+ private:
+ bool CanEnterLongIdlePeriod(
+ base::TimeTicks now,
+ base::TimeDelta* next_long_idle_period_delay_out) override {
+ if (timeline_) {
+ timeline_->push_back(base::StringPrintf("CanEnterLongIdlePeriod @ %d",
+ TimeTicksToIntMs(now)));
+ }
+ return WorkerThreadScheduler::CanEnterLongIdlePeriod(
+ now, next_long_idle_period_delay_out);
+ }
+
+ void IsNotQuiescent() override {
+ if (timeline_) {
+ timeline_->push_back(base::StringPrintf(
+ "IsNotQuiescent @ %d", TimeTicksToIntMs(clock_->NowTicks())));
+ }
+ WorkerThreadScheduler::IsNotQuiescent();
+ }
+
+ base::SimpleTestTickClock* clock_; // NOT OWNED
+ std::vector<std::string>* timeline_; // NOT OWNED
+};
+
+class WorkerThreadSchedulerTest : public testing::Test {
+ public:
+ WorkerThreadSchedulerTest()
+ : mock_task_runner_(new cc::OrderedSimpleTaskRunner(&clock_, true)),
+ scheduler_(new WorkerThreadSchedulerForTest(
+ TaskQueueManagerForTest::Create(nullptr,
+ mock_task_runner_,
+ &clock_),
+ &clock_)),
+ timeline_(nullptr) {
+ clock_.Advance(base::TimeDelta::FromMicroseconds(5000));
+ }
+
+ ~WorkerThreadSchedulerTest() override = default;
+
+ void TearDown() override {
+ // Check that all tests stop posting tasks.
+ while (mock_task_runner_->RunUntilIdle()) {
+ }
+ }
+
+ void Init() {
+ scheduler_->Init();
+ default_task_runner_ = scheduler_->DefaultTaskRunner();
+ idle_task_runner_ = scheduler_->IdleTaskRunner();
+ timeline_ = nullptr;
+ }
+
+ void RecordTimelineEvents(std::vector<std::string>* timeline) {
+ timeline_ = timeline;
+ scheduler_->RecordTimelineEvents(timeline);
+ }
+
+ void RunUntilIdle() {
+ if (timeline_) {
+ timeline_->push_back(base::StringPrintf(
+ "RunUntilIdle begin @ %d", TimeTicksToIntMs(clock_.NowTicks())));
+ }
+ mock_task_runner_->RunUntilIdle();
+ if (timeline_) {
+ timeline_->push_back(base::StringPrintf(
+ "RunUntilIdle end @ %d", TimeTicksToIntMs(clock_.NowTicks())));
+ }
+ }
+
+ // Helper for posting several tasks of specific types. |task_descriptor| is a
+ // string with space delimited task identifiers. The first letter of each
+ // task identifier specifies the task type:
+ // - 'D': Default task
+ // - 'I': Idle task
+ void PostTestTasks(std::vector<std::string>* run_order,
+ const std::string& task_descriptor) {
+ std::istringstream stream(task_descriptor);
+ while (!stream.eof()) {
+ std::string task;
+ stream >> task;
+ switch (task[0]) {
+ case 'D':
+ default_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&AppendToVectorTestTask, run_order, task));
+ break;
+ case 'I':
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE,
+ base::BindOnce(&AppendToVectorIdleTestTask, run_order, task));
+ break;
+ default:
+ NOTREACHED();
+ }
+ }
+ }
+
+ static base::TimeDelta maximum_idle_period_duration() {
+ return base::TimeDelta::FromMilliseconds(
+ IdleHelper::kMaximumIdlePeriodMillis);
+ }
+
+ protected:
+ base::SimpleTestTickClock clock_;
+ // Only one of mock_task_runner_ or message_loop_ will be set.
+ scoped_refptr<cc::OrderedSimpleTaskRunner> mock_task_runner_;
+
+ std::unique_ptr<WorkerThreadSchedulerForTest> scheduler_;
+ scoped_refptr<base::SingleThreadTaskRunner> default_task_runner_;
+ scoped_refptr<SingleThreadIdleTaskRunner> idle_task_runner_;
+ std::vector<std::string>* timeline_; // NOT OWNED
+
+ DISALLOW_COPY_AND_ASSIGN(WorkerThreadSchedulerTest);
+};
+
+TEST_F(WorkerThreadSchedulerTest, TestPostDefaultTask) {
+ Init();
+
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "D1 D2 D3 D4");
+
+ RunUntilIdle();
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("D1"), std::string("D2"),
+ std::string("D3"), std::string("D4")));
+}
+
+TEST_F(WorkerThreadSchedulerTest, TestPostIdleTask) {
+ Init();
+
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "I1");
+
+ RunUntilIdle();
+ EXPECT_THAT(run_order, testing::ElementsAre(std::string("I1")));
+}
+
+TEST_F(WorkerThreadSchedulerTest, TestPostDefaultAndIdleTasks) {
+ Init();
+
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "I1 D2 D3 D4");
+
+ RunUntilIdle();
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("D2"), std::string("D3"),
+ std::string("D4"), std::string("I1")));
+}
+
+TEST_F(WorkerThreadSchedulerTest, TestPostDefaultDelayedAndIdleTasks) {
+ Init();
+
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "I1 D2 D3 D4");
+
+ default_task_runner_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&AppendToVectorTestTask, &run_order, "DELAYED"),
+ base::TimeDelta::FromMilliseconds(1000));
+
+ RunUntilIdle();
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("D2"), std::string("D3"),
+ std::string("D4"), std::string("I1"),
+ std::string("DELAYED")));
+}
+
+TEST_F(WorkerThreadSchedulerTest, TestIdleTaskWhenIsNotQuiescent) {
+ std::vector<std::string> timeline;
+ RecordTimelineEvents(&timeline);
+ Init();
+
+ timeline.push_back("Post default task");
+ // Post a delayed task timed to occur mid way during the long idle period.
+ default_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&RecordTimelineTask, base::Unretained(&timeline),
+ base::Unretained(&clock_)));
+ RunUntilIdle();
+
+ timeline.push_back("Post idle task");
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE, base::BindOnce(&TimelineIdleTestTask, &timeline));
+
+ RunUntilIdle();
+
+ std::string expected_timeline[] = {"CanEnterLongIdlePeriod @ 5",
+ "Post default task",
+ "run RecordTimelineTask @ 5",
+ "Post idle task",
+ "IsNotQuiescent @ 5",
+ "CanEnterLongIdlePeriod @ 305",
+ "run TimelineIdleTestTask deadline 355"};
+
+ EXPECT_THAT(timeline, ElementsAreArray(expected_timeline));
+}
+
+TEST_F(WorkerThreadSchedulerTest, TestIdleDeadlineWithPendingDelayedTask) {
+ std::vector<std::string> timeline;
+ RecordTimelineEvents(&timeline);
+ Init();
+
+ timeline.push_back("Post delayed and idle tasks");
+ // Post a delayed task timed to occur mid way during the long idle period.
+ default_task_runner_->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&RecordTimelineTask, base::Unretained(&timeline),
+ base::Unretained(&clock_)),
+ base::TimeDelta::FromMilliseconds(20));
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE, base::BindOnce(&TimelineIdleTestTask, &timeline));
+
+ RunUntilIdle();
+
+ std::string expected_timeline[] = {
+ "CanEnterLongIdlePeriod @ 5", "Post delayed and idle tasks",
+ "CanEnterLongIdlePeriod @ 5",
+ "run TimelineIdleTestTask deadline 25", // Note the short 20ms deadline.
+ "run RecordTimelineTask @ 25"};
+
+ EXPECT_THAT(timeline, ElementsAreArray(expected_timeline));
+}
+
+TEST_F(WorkerThreadSchedulerTest,
+ TestIdleDeadlineWithPendingDelayedTaskFarInTheFuture) {
+ std::vector<std::string> timeline;
+ RecordTimelineEvents(&timeline);
+ Init();
+
+ timeline.push_back("Post delayed and idle tasks");
+ // Post a delayed task timed to occur well after the long idle period.
+ default_task_runner_->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&RecordTimelineTask, base::Unretained(&timeline),
+ base::Unretained(&clock_)),
+ base::TimeDelta::FromMilliseconds(500));
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE, base::BindOnce(&TimelineIdleTestTask, &timeline));
+
+ RunUntilIdle();
+
+ std::string expected_timeline[] = {
+ "CanEnterLongIdlePeriod @ 5", "Post delayed and idle tasks",
+ "CanEnterLongIdlePeriod @ 5",
+ "run TimelineIdleTestTask deadline 55", // Note the full 50ms deadline.
+ "run RecordTimelineTask @ 505"};
+
+ EXPECT_THAT(timeline, ElementsAreArray(expected_timeline));
+}
+
+TEST_F(WorkerThreadSchedulerTest, TestPostIdleTaskAfterRunningUntilIdle) {
+ Init();
+
+ default_task_runner_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&NopTask),
+ base::TimeDelta::FromMilliseconds(1000));
+ RunUntilIdle();
+
+ std::vector<std::string> run_order;
+ PostTestTasks(&run_order, "I1 I2 D3");
+
+ RunUntilIdle();
+ EXPECT_THAT(run_order,
+ testing::ElementsAre(std::string("D3"), std::string("I1"),
+ std::string("I2")));
+}
+
+void PostIdleTask(std::vector<std::string>* timeline,
+ base::SimpleTestTickClock* clock,
+ SingleThreadIdleTaskRunner* idle_task_runner) {
+ timeline->push_back(base::StringPrintf("run PostIdleTask @ %d",
+ TimeTicksToIntMs(clock->NowTicks())));
+
+ idle_task_runner->PostIdleTask(
+ FROM_HERE, base::BindOnce(&TimelineIdleTestTask, timeline));
+}
+
+TEST_F(WorkerThreadSchedulerTest, TestLongIdlePeriodTimeline) {
+ Init();
+
+ std::vector<std::string> timeline;
+ RecordTimelineEvents(&timeline);
+
+ // The scheduler should not run the initiate_next_long_idle_period task if
+ // there are no idle tasks and no other task woke up the scheduler, thus
+ // the idle period deadline shouldn't update at the end of the current long
+ // idle period.
+ base::TimeTicks idle_period_deadline =
+ scheduler_->CurrentIdleTaskDeadlineForTesting();
+ clock_.Advance(maximum_idle_period_duration());
+ RunUntilIdle();
+
+ base::TimeTicks new_idle_period_deadline =
+ scheduler_->CurrentIdleTaskDeadlineForTesting();
+ EXPECT_EQ(idle_period_deadline, new_idle_period_deadline);
+
+ // Post a task to post an idle task. Because the system is non-quiescent a
+ // 300ms pause will occur before the next long idle period is initiated and
+ // the idle task run.
+ default_task_runner_->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&PostIdleTask, base::Unretained(&timeline),
+ base::Unretained(&clock_),
+ base::Unretained(idle_task_runner_.get())),
+ base::TimeDelta::FromMilliseconds(30));
+
+ timeline.push_back("PostFirstIdleTask");
+ idle_task_runner_->PostIdleTask(
+ FROM_HERE, base::BindOnce(&TimelineIdleTestTask, &timeline));
+ RunUntilIdle();
+ new_idle_period_deadline = scheduler_->CurrentIdleTaskDeadlineForTesting();
+
+ // Running a normal task will mark the system as non-quiescent.
+ timeline.push_back("Post RecordTimelineTask");
+ default_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&RecordTimelineTask, base::Unretained(&timeline),
+ base::Unretained(&clock_)));
+ RunUntilIdle();
+
+ std::string expected_timeline[] = {"RunUntilIdle begin @ 55",
+ "RunUntilIdle end @ 55",
+ "PostFirstIdleTask",
+ "RunUntilIdle begin @ 55",
+ "CanEnterLongIdlePeriod @ 55",
+ "run TimelineIdleTestTask deadline 85",
+ "run PostIdleTask @ 85",
+ "IsNotQuiescent @ 85",
+ "CanEnterLongIdlePeriod @ 385",
+ "run TimelineIdleTestTask deadline 435",
+ "RunUntilIdle end @ 385",
+ "Post RecordTimelineTask",
+ "RunUntilIdle begin @ 385",
+ "run RecordTimelineTask @ 385",
+ "RunUntilIdle end @ 385"};
+
+ EXPECT_THAT(timeline, ElementsAreArray(expected_timeline));
+}
+
+} // namespace worker_scheduler_impl_unittest
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scoped_orientation_change_indicator.cc b/chromium/third_party/blink/renderer/platform/scoped_orientation_change_indicator.cc
new file mode 100644
index 00000000000..281c68dd94b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scoped_orientation_change_indicator.cc
@@ -0,0 +1,33 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scoped_orientation_change_indicator.h"
+
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+namespace blink {
+
+ScopedOrientationChangeIndicator::State
+ ScopedOrientationChangeIndicator::state_ =
+ ScopedOrientationChangeIndicator::State::kNotProcessing;
+
+ScopedOrientationChangeIndicator::ScopedOrientationChangeIndicator() {
+ DCHECK(IsMainThread());
+
+ previous_state_ = state_;
+ state_ = State::kProcessing;
+}
+
+ScopedOrientationChangeIndicator::~ScopedOrientationChangeIndicator() {
+ DCHECK(IsMainThread());
+ state_ = previous_state_;
+}
+
+// static
+bool ScopedOrientationChangeIndicator::ProcessingOrientationChange() {
+ DCHECK(IsMainThread());
+ return state_ == State::kProcessing;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scoped_orientation_change_indicator.h b/chromium/third_party/blink/renderer/platform/scoped_orientation_change_indicator.h
new file mode 100644
index 00000000000..e25e19f8c44
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scoped_orientation_change_indicator.h
@@ -0,0 +1,37 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCOPED_ORIENTATION_CHANGE_INDICATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCOPED_ORIENTATION_CHANGE_INDICATOR_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT ScopedOrientationChangeIndicator final {
+ STACK_ALLOCATED();
+ WTF_MAKE_NONCOPYABLE(ScopedOrientationChangeIndicator);
+
+ public:
+ static bool ProcessingOrientationChange();
+
+ explicit ScopedOrientationChangeIndicator();
+ ~ScopedOrientationChangeIndicator();
+
+ private:
+ enum class State {
+ kProcessing,
+ kNotProcessing,
+ };
+
+ static State state_;
+
+ State previous_state_ = State::kNotProcessing;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/scoped_orientation_change_indicator_test.cc b/chromium/third_party/blink/renderer/platform/scoped_orientation_change_indicator_test.cc
new file mode 100644
index 00000000000..3485a9fba5b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scoped_orientation_change_indicator_test.cc
@@ -0,0 +1,44 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scoped_orientation_change_indicator.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+TEST(ScopedOrientationChangeIndicatorTest, InitialState) {
+ EXPECT_FALSE(ScopedOrientationChangeIndicator::ProcessingOrientationChange());
+}
+
+TEST(ScopedOrientationChangeIndicatorTest, ConstructOneIndicatorWithGesture) {
+ ScopedOrientationChangeIndicator indicator;
+
+ EXPECT_TRUE(ScopedOrientationChangeIndicator::ProcessingOrientationChange());
+}
+
+TEST(ScopedOrientationChangeIndicatorTest, MultipleIndicatorInTheSameScope) {
+ ScopedOrientationChangeIndicator indicator1;
+
+ EXPECT_TRUE(ScopedOrientationChangeIndicator::ProcessingOrientationChange());
+
+ ScopedOrientationChangeIndicator indicator2;
+
+ EXPECT_TRUE(ScopedOrientationChangeIndicator::ProcessingOrientationChange());
+}
+
+TEST(ScopedOrientationChangeIndicatorTest, DestructResetsStateUsingGesture) {
+ { ScopedOrientationChangeIndicator indicator; }
+
+ EXPECT_FALSE(ScopedOrientationChangeIndicator::ProcessingOrientationChange());
+}
+
+TEST(ScopedOrientationChangeIndicatorTest, DestructResetsStateUsingNoGesture) {
+ ScopedOrientationChangeIndicator indicator;
+ { ScopedOrientationChangeIndicator indicator; }
+
+ EXPECT_TRUE(ScopedOrientationChangeIndicator::ProcessingOrientationChange());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scroll/DEPS b/chromium/third_party/blink/renderer/platform/scroll/DEPS
new file mode 100644
index 00000000000..8d825ff202e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+cc",
+ "-cc/blink",
+]
diff --git a/chromium/third_party/blink/renderer/platform/scroll/main_thread_scrolling_reason.h b/chromium/third_party/blink/renderer/platform/scroll/main_thread_scrolling_reason.h
new file mode 100644
index 00000000000..9b813d5d89b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/main_thread_scrolling_reason.h
@@ -0,0 +1,17 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_MAIN_THREAD_SCROLLING_REASON_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_MAIN_THREAD_SCROLLING_REASON_H_
+
+#include "cc/input/main_thread_scrolling_reason.h"
+
+namespace blink {
+
+// A wrapper around cc's structure to expose it to core.
+struct MainThreadScrollingReason : public cc::MainThreadScrollingReason {};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/scroll/programmatic_scroll_animator.cc b/chromium/third_party/blink/renderer/platform/scroll/programmatic_scroll_animator.cc
new file mode 100644
index 00000000000..95f4ff79908
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/programmatic_scroll_animator.cc
@@ -0,0 +1,205 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scroll/programmatic_scroll_animator.h"
+
+#include <memory>
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_compositor_support.h"
+#include "third_party/blink/renderer/platform/animation/compositor_keyframe_model.h"
+#include "third_party/blink/renderer/platform/animation/compositor_scroll_offset_animation_curve.h"
+#include "third_party/blink/renderer/platform/geometry/int_size.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
+#include "third_party/blink/renderer/platform/scroll/scrollable_area.h"
+#include "third_party/blink/renderer/platform/scroll/smooth_scroll_sequencer.h"
+
+namespace blink {
+
+ProgrammaticScrollAnimator::ProgrammaticScrollAnimator(
+ ScrollableArea* scrollable_area)
+ : scrollable_area_(scrollable_area), start_time_(0.0) {}
+
+ProgrammaticScrollAnimator::~ProgrammaticScrollAnimator() = default;
+
+void ProgrammaticScrollAnimator::ResetAnimationState() {
+ ScrollAnimatorCompositorCoordinator::ResetAnimationState();
+ animation_curve_.reset();
+ start_time_ = 0.0;
+}
+
+void ProgrammaticScrollAnimator::NotifyOffsetChanged(
+ const ScrollOffset& offset) {
+ ScrollType scroll_type =
+ is_sequenced_scroll_ ? kSequencedScroll : kProgrammaticScroll;
+ ScrollOffsetChanged(offset, scroll_type);
+}
+
+void ProgrammaticScrollAnimator::ScrollToOffsetWithoutAnimation(
+ const ScrollOffset& offset,
+ bool is_sequenced_scroll) {
+ CancelAnimation();
+ is_sequenced_scroll_ = is_sequenced_scroll;
+ NotifyOffsetChanged(offset);
+ is_sequenced_scroll_ = false;
+ if (SmoothScrollSequencer* sequencer =
+ GetScrollableArea()->GetSmoothScrollSequencer())
+ sequencer->RunQueuedAnimations();
+}
+
+void ProgrammaticScrollAnimator::AnimateToOffset(const ScrollOffset& offset,
+ bool is_sequenced_scroll) {
+ if (run_state_ == RunState::kPostAnimationCleanup)
+ ResetAnimationState();
+
+ start_time_ = 0.0;
+ target_offset_ = offset;
+ is_sequenced_scroll_ = is_sequenced_scroll;
+ animation_curve_ = CompositorScrollOffsetAnimationCurve::Create(
+ CompositorOffsetFromBlinkOffset(target_offset_),
+ CompositorScrollOffsetAnimationCurve::kScrollDurationDeltaBased);
+
+ scrollable_area_->RegisterForAnimation();
+ if (!scrollable_area_->ScheduleAnimation()) {
+ ResetAnimationState();
+ NotifyOffsetChanged(offset);
+ }
+ run_state_ = RunState::kWaitingToSendToCompositor;
+}
+
+void ProgrammaticScrollAnimator::CancelAnimation() {
+ DCHECK_NE(run_state_, RunState::kRunningOnCompositorButNeedsUpdate);
+ ScrollAnimatorCompositorCoordinator::CancelAnimation();
+}
+
+void ProgrammaticScrollAnimator::TickAnimation(double monotonic_time) {
+ if (run_state_ != RunState::kRunningOnMainThread)
+ return;
+
+ if (!start_time_)
+ start_time_ = monotonic_time;
+ double elapsed_time = monotonic_time - start_time_;
+ bool is_finished = (elapsed_time > animation_curve_->Duration());
+ ScrollOffset offset =
+ BlinkOffsetFromCompositorOffset(animation_curve_->GetValue(elapsed_time));
+ NotifyOffsetChanged(offset);
+
+ if (is_finished) {
+ run_state_ = RunState::kPostAnimationCleanup;
+ AnimationFinished();
+ } else if (!scrollable_area_->ScheduleAnimation()) {
+ NotifyOffsetChanged(offset);
+ ResetAnimationState();
+ }
+}
+
+void ProgrammaticScrollAnimator::UpdateCompositorAnimations() {
+ if (run_state_ == RunState::kPostAnimationCleanup) {
+ // No special cleanup, simply reset animation state. We have this state
+ // here because the state machine is shared with ScrollAnimator which
+ // has to do some cleanup that requires the compositing state to be clean.
+ return ResetAnimationState();
+ }
+
+ if (compositor_animation_id_ &&
+ run_state_ != RunState::kRunningOnCompositor) {
+ // If the current run state is WaitingToSendToCompositor but we have a
+ // non-zero compositor animation id, there's a currently running
+ // compositor animation that needs to be removed here before the new
+ // animation is added below.
+ DCHECK(run_state_ == RunState::kWaitingToCancelOnCompositor ||
+ run_state_ == RunState::kWaitingToSendToCompositor);
+
+ RemoveAnimation();
+
+ compositor_animation_id_ = 0;
+ compositor_animation_group_id_ = 0;
+ if (run_state_ == RunState::kWaitingToCancelOnCompositor) {
+ ResetAnimationState();
+ return;
+ }
+ }
+
+ if (run_state_ == RunState::kWaitingToSendToCompositor) {
+ if (!element_id_)
+ ReattachCompositorAnimationIfNeeded(
+ GetScrollableArea()->GetCompositorAnimationTimeline());
+
+ bool sent_to_compositor = false;
+
+ // TODO(sunyunjia): Sequenced Smooth Scroll should also be able to
+ // scroll on the compositor thread. We should send the ScrollType
+ // information to the compositor thread.
+ // crbug.com/730705
+ if (!scrollable_area_->ShouldScrollOnMainThread() &&
+ !is_sequenced_scroll_) {
+ std::unique_ptr<CompositorKeyframeModel> animation =
+ CompositorKeyframeModel::Create(
+ *animation_curve_, CompositorTargetProperty::SCROLL_OFFSET, 0, 0);
+
+ int animation_id = animation->Id();
+ int animation_group_id = animation->Group();
+
+ if (AddAnimation(std::move(animation))) {
+ sent_to_compositor = true;
+ run_state_ = RunState::kRunningOnCompositor;
+ compositor_animation_id_ = animation_id;
+ compositor_animation_group_id_ = animation_group_id;
+ }
+ }
+
+ if (!sent_to_compositor) {
+ run_state_ = RunState::kRunningOnMainThread;
+ animation_curve_->SetInitialValue(
+ CompositorOffsetFromBlinkOffset(scrollable_area_->GetScrollOffset()));
+ if (!scrollable_area_->ScheduleAnimation()) {
+ NotifyOffsetChanged(target_offset_);
+ ResetAnimationState();
+ }
+ }
+ }
+}
+
+void ProgrammaticScrollAnimator::LayerForCompositedScrollingDidChange(
+ CompositorAnimationTimeline* timeline) {
+ ReattachCompositorAnimationIfNeeded(timeline);
+
+ // If the composited scrolling layer is lost during a composited animation,
+ // continue the animation on the main thread.
+ if (run_state_ == RunState::kRunningOnCompositor &&
+ !scrollable_area_->LayerForScrolling()) {
+ run_state_ = RunState::kRunningOnMainThread;
+ compositor_animation_id_ = 0;
+ compositor_animation_group_id_ = 0;
+ animation_curve_->SetInitialValue(
+ CompositorOffsetFromBlinkOffset(scrollable_area_->GetScrollOffset()));
+ scrollable_area_->RegisterForAnimation();
+ if (!scrollable_area_->ScheduleAnimation()) {
+ ResetAnimationState();
+ NotifyOffsetChanged(target_offset_);
+ }
+ }
+}
+
+void ProgrammaticScrollAnimator::NotifyCompositorAnimationFinished(
+ int group_id) {
+ DCHECK_NE(run_state_, RunState::kRunningOnCompositorButNeedsUpdate);
+ ScrollAnimatorCompositorCoordinator::CompositorAnimationFinished(group_id);
+ AnimationFinished();
+}
+
+void ProgrammaticScrollAnimator::AnimationFinished() {
+ if (is_sequenced_scroll_) {
+ is_sequenced_scroll_ = false;
+ if (SmoothScrollSequencer* sequencer =
+ GetScrollableArea()->GetSmoothScrollSequencer())
+ sequencer->RunQueuedAnimations();
+ }
+}
+
+void ProgrammaticScrollAnimator::Trace(blink::Visitor* visitor) {
+ visitor->Trace(scrollable_area_);
+ ScrollAnimatorCompositorCoordinator::Trace(visitor);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scroll/programmatic_scroll_animator.h b/chromium/third_party/blink/renderer/platform/scroll/programmatic_scroll_animator.h
new file mode 100644
index 00000000000..6a62d95b80c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/programmatic_scroll_animator.h
@@ -0,0 +1,76 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_PROGRAMMATIC_SCROLL_ANIMATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_PROGRAMMATIC_SCROLL_ANIMATOR_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/scroll/scroll_animator_compositor_coordinator.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class ScrollableArea;
+class CompositorAnimationTimeline;
+class CompositorScrollOffsetAnimationCurve;
+
+// ProgrammaticScrollAnimator manages scroll offset animations ("smooth
+// scrolls") triggered by web APIs such as "scroll-behavior: smooth" which are
+// standardized by the CSSOM View Module (https://www.w3.org/TR/cssom-view-1/).
+//
+// For scroll animations triggered by user input, see ScrollAnimator and
+// ScrollAnimatorMac.
+
+class ProgrammaticScrollAnimator : public ScrollAnimatorCompositorCoordinator {
+ WTF_MAKE_NONCOPYABLE(ProgrammaticScrollAnimator);
+
+ public:
+ static ProgrammaticScrollAnimator* Create(ScrollableArea* scrollable_area) {
+ return new ProgrammaticScrollAnimator(scrollable_area);
+ }
+
+ virtual ~ProgrammaticScrollAnimator();
+
+ void ScrollToOffsetWithoutAnimation(const ScrollOffset&,
+ bool is_sequenced_scroll);
+ void AnimateToOffset(const ScrollOffset&, bool is_sequenced_scroll = false);
+
+ // ScrollAnimatorCompositorCoordinator implementation.
+ void ResetAnimationState() override;
+ void CancelAnimation() override;
+ void TakeOverCompositorAnimation() override {}
+ ScrollableArea* GetScrollableArea() const override {
+ return scrollable_area_;
+ }
+ void TickAnimation(double monotonic_time) override;
+ void UpdateCompositorAnimations() override;
+ void NotifyCompositorAnimationFinished(int group_id) override;
+ void NotifyCompositorAnimationAborted(int group_id) override {}
+ void LayerForCompositedScrollingDidChange(
+ CompositorAnimationTimeline*) override;
+
+ void Trace(blink::Visitor*);
+
+ private:
+ explicit ProgrammaticScrollAnimator(ScrollableArea*);
+
+ void NotifyOffsetChanged(const ScrollOffset&);
+ void AnimationFinished();
+
+ Member<ScrollableArea> scrollable_area_;
+ std::unique_ptr<CompositorScrollOffsetAnimationCurve> animation_curve_;
+ ScrollOffset target_offset_;
+ double start_time_;
+ // is_sequenced_scroll_ is true for the entire duration of an animated scroll
+ // as well as during an instant scroll if that scroll is part of a sequence.
+ // It resets to false at the end of the scroll. It controls whether we should
+ // abort the smooth scroll sequence after an instant SetScrollOffset.
+ bool is_sequenced_scroll_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_PROGRAMMATIC_SCROLL_ANIMATOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/scroll/scroll_alignment.cc b/chromium/third_party/blink/renderer/platform/scroll/scroll_alignment.cc
new file mode 100644
index 00000000000..c8544902612
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/scroll_alignment.cc
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
+ *
+ * Portions are Copyright (C) 1998 Netscape Communications Corporation.
+ *
+ * Other contributors:
+ * Robert O'Callahan <roc+@cs.cmu.edu>
+ * David Baron <dbaron@fas.harvard.edu>
+ * Christian Biesinger <cbiesinger@web.de>
+ * Randall Jesup <rjesup@wgate.com>
+ * Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
+ * Josh Soref <timeless@mac.com>
+ * Boris Zbarsky <bzbarsky@mit.edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * Alternatively, the contents of this file may be used under the terms
+ * of either the Mozilla Public License Version 1.1, found at
+ * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
+ * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
+ * (the "GPL"), in which case the provisions of the MPL or the GPL are
+ * applicable instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of one of those two
+ * licenses (the MPL or the GPL) and not to allow others to use your
+ * version of this file under the LGPL, indicate your decision by
+ * deletingthe provisions above and replace them with the notice and
+ * other provisions required by the MPL or the GPL, as the case may be.
+ * If you do not delete the provisions above, a recipient may use your
+ * version of this file under any of the LGPL, the MPL or the GPL.
+ */
+
+#include "third_party/blink/renderer/platform/scroll/scroll_alignment.h"
+
+#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
+
+namespace blink {
+
+const ScrollAlignment ScrollAlignment::kAlignCenterIfNeeded = {
+ kScrollAlignmentNoScroll, kScrollAlignmentCenter,
+ kScrollAlignmentClosestEdge};
+const ScrollAlignment ScrollAlignment::kAlignToEdgeIfNeeded = {
+ kScrollAlignmentNoScroll, kScrollAlignmentClosestEdge,
+ kScrollAlignmentClosestEdge};
+const ScrollAlignment ScrollAlignment::kAlignCenterAlways = {
+ kScrollAlignmentCenter, kScrollAlignmentCenter, kScrollAlignmentCenter};
+const ScrollAlignment ScrollAlignment::kAlignTopAlways = {
+ kScrollAlignmentTop, kScrollAlignmentTop, kScrollAlignmentTop};
+const ScrollAlignment ScrollAlignment::kAlignBottomAlways = {
+ kScrollAlignmentBottom, kScrollAlignmentBottom, kScrollAlignmentBottom};
+const ScrollAlignment ScrollAlignment::kAlignLeftAlways = {
+ kScrollAlignmentLeft, kScrollAlignmentLeft, kScrollAlignmentLeft};
+const ScrollAlignment ScrollAlignment::kAlignRightAlways = {
+ kScrollAlignmentRight, kScrollAlignmentRight, kScrollAlignmentRight};
+
+#define MIN_INTERSECT_FOR_REVEAL 32
+
+ScrollOffset ScrollAlignment::GetScrollOffsetToExpose(
+ const LayoutRect& scroll_snapport_rect,
+ const LayoutRect& expose_rect,
+ const ScrollAlignment& align_x,
+ const ScrollAlignment& align_y,
+ const ScrollOffset& current_scroll_offset) {
+ // Prevent degenerate cases by giving the visible rect a minimum non-0 size.
+ LayoutRect non_zero_visible_rect(scroll_snapport_rect);
+ LayoutUnit minimum_layout_unit;
+ minimum_layout_unit.SetRawValue(1);
+ if (non_zero_visible_rect.Width() == LayoutUnit())
+ non_zero_visible_rect.SetWidth(minimum_layout_unit);
+ if (non_zero_visible_rect.Height() == LayoutUnit())
+ non_zero_visible_rect.SetHeight(minimum_layout_unit);
+
+ // Determine the appropriate X behavior.
+ ScrollAlignmentBehavior scroll_x;
+ LayoutRect expose_rect_x(expose_rect.X(), non_zero_visible_rect.Y(),
+ expose_rect.Width(), non_zero_visible_rect.Height());
+ LayoutUnit intersect_width =
+ Intersection(non_zero_visible_rect, expose_rect_x).Width();
+ if (intersect_width == expose_rect.Width() ||
+ intersect_width >= MIN_INTERSECT_FOR_REVEAL) {
+ // If the rectangle is fully visible, use the specified visible behavior.
+ // If the rectangle is partially visible, but over a certain threshold,
+ // then treat it as fully visible to avoid unnecessary horizontal scrolling
+ scroll_x = GetVisibleBehavior(align_x);
+ } else if (intersect_width == non_zero_visible_rect.Width()) {
+ // If the rect is bigger than the visible area, don't bother trying to
+ // center. Other alignments will work.
+ scroll_x = GetVisibleBehavior(align_x);
+ if (scroll_x == kScrollAlignmentCenter)
+ scroll_x = kScrollAlignmentNoScroll;
+ } else if (intersect_width > 0) {
+ // If the rectangle is partially visible, but not above the minimum
+ // threshold, use the specified partial behavior
+ scroll_x = GetPartialBehavior(align_x);
+ } else {
+ scroll_x = GetHiddenBehavior(align_x);
+ }
+
+ if (scroll_x == kScrollAlignmentClosestEdge) {
+ // Closest edge is the right in two cases:
+ // (1) exposeRect to the right of and smaller than nonZeroVisibleRect
+ // (2) exposeRect to the left of and larger than nonZeroVisibleRect
+ if ((expose_rect.MaxX() > non_zero_visible_rect.MaxX() &&
+ expose_rect.Width() < non_zero_visible_rect.Width()) ||
+ (expose_rect.MaxX() < non_zero_visible_rect.MaxX() &&
+ expose_rect.Width() > non_zero_visible_rect.Width())) {
+ scroll_x = kScrollAlignmentRight;
+ }
+ }
+
+ // Determine the appropriate Y behavior.
+ ScrollAlignmentBehavior scroll_y;
+ LayoutRect expose_rect_y(non_zero_visible_rect.X(), expose_rect.Y(),
+ non_zero_visible_rect.Width(), expose_rect.Height());
+ LayoutUnit intersect_height =
+ Intersection(non_zero_visible_rect, expose_rect_y).Height();
+ if (intersect_height == expose_rect.Height()) {
+ // If the rectangle is fully visible, use the specified visible behavior.
+ scroll_y = GetVisibleBehavior(align_y);
+ } else if (intersect_height == non_zero_visible_rect.Height()) {
+ // If the rect is bigger than the visible area, don't bother trying to
+ // center. Other alignments will work.
+ scroll_y = GetVisibleBehavior(align_y);
+ if (scroll_y == kScrollAlignmentCenter)
+ scroll_y = kScrollAlignmentNoScroll;
+ } else if (intersect_height > 0) {
+ // If the rectangle is partially visible, use the specified partial behavior
+ scroll_y = GetPartialBehavior(align_y);
+ } else {
+ scroll_y = GetHiddenBehavior(align_y);
+ }
+
+ if (scroll_y == kScrollAlignmentClosestEdge) {
+ // Closest edge is the bottom in two cases:
+ // (1) exposeRect below and smaller than nonZeroVisibleRect
+ // (2) exposeRect above and larger than nonZeroVisibleRect
+ if ((expose_rect.MaxY() > non_zero_visible_rect.MaxY() &&
+ expose_rect.Height() < non_zero_visible_rect.Height()) ||
+ (expose_rect.MaxY() < non_zero_visible_rect.MaxY() &&
+ expose_rect.Height() > non_zero_visible_rect.Height())) {
+ scroll_y = kScrollAlignmentBottom;
+ }
+ }
+
+ // We would like calculate the ScrollPosition to move |expose_rect| inside
+ // the scroll_snapport, which is based on the scroll_origin of the scroller.
+ non_zero_visible_rect.Move(LayoutSize(-current_scroll_offset));
+
+ // Given the X behavior, compute the X coordinate.
+ float x;
+ if (scroll_x == kScrollAlignmentNoScroll) {
+ x = current_scroll_offset.Width();
+ } else if (scroll_x == kScrollAlignmentRight) {
+ x = (expose_rect.MaxX() - non_zero_visible_rect.MaxX()).ToFloat();
+ } else if (scroll_x == kScrollAlignmentCenter) {
+ x = ((expose_rect.X() + expose_rect.MaxX() -
+ (non_zero_visible_rect.X() + non_zero_visible_rect.MaxX())) /
+ 2)
+ .ToFloat();
+ } else {
+ x = (expose_rect.X() - non_zero_visible_rect.X()).ToFloat();
+ }
+
+ // Given the Y behavior, compute the Y coordinate.
+ float y;
+ if (scroll_y == kScrollAlignmentNoScroll) {
+ y = current_scroll_offset.Height();
+ } else if (scroll_y == kScrollAlignmentBottom) {
+ y = (expose_rect.MaxY() - non_zero_visible_rect.MaxY()).ToFloat();
+ } else if (scroll_y == kScrollAlignmentCenter) {
+ y = ((expose_rect.Y() + expose_rect.MaxY() -
+ (non_zero_visible_rect.Y() + non_zero_visible_rect.MaxY())) /
+ 2)
+ .ToFloat();
+ } else {
+ y = (expose_rect.Y() - non_zero_visible_rect.Y()).ToFloat();
+ }
+
+ return ScrollOffset(x, y);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scroll/scroll_alignment.h b/chromium/third_party/blink/renderer/platform/scroll/scroll_alignment.h
new file mode 100644
index 00000000000..7a4d5e3a8e8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/scroll_alignment.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2003, 2009 Apple Inc. All rights reserved.
+ *
+ * Portions are Copyright (C) 1998 Netscape Communications Corporation.
+ *
+ * Other contributors:
+ * Robert O'Callahan <roc+@cs.cmu.edu>
+ * David Baron <dbaron@fas.harvard.edu>
+ * Christian Biesinger <cbiesinger@web.de>
+ * Randall Jesup <rjesup@wgate.com>
+ * Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
+ * Josh Soref <timeless@mac.com>
+ * Boris Zbarsky <bzbarsky@mit.edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * Alternatively, the contents of this file may be used under the terms
+ * of either the Mozilla Public License Version 1.1, found at
+ * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
+ * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
+ * (the "GPL"), in which case the provisions of the MPL or the GPL are
+ * applicable instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of one of those two
+ * licenses (the MPL or the GPL) and not to allow others to use your
+ * version of this file under the LGPL, indicate your decision by
+ * deletingthe provisions above and replace them with the notice and
+ * other provisions required by the MPL or the GPL, as the case may be.
+ * If you do not delete the provisions above, a recipient may use your
+ * version of this file under any of the LGPL, the MPL or the GPL.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLL_ALIGNMENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLL_ALIGNMENT_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/scroll/scroll_types.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+enum ScrollAlignmentBehavior {
+ kScrollAlignmentNoScroll,
+ kScrollAlignmentCenter,
+ kScrollAlignmentTop,
+ kScrollAlignmentBottom,
+ kScrollAlignmentLeft,
+ kScrollAlignmentRight,
+ kScrollAlignmentClosestEdge
+};
+
+class LayoutRect;
+
+struct PLATFORM_EXPORT ScrollAlignment {
+ STACK_ALLOCATED();
+ static ScrollAlignmentBehavior GetVisibleBehavior(const ScrollAlignment& s) {
+ return s.rect_visible_;
+ }
+ static ScrollAlignmentBehavior GetPartialBehavior(const ScrollAlignment& s) {
+ return s.rect_partial_;
+ }
+ static ScrollAlignmentBehavior GetHiddenBehavior(const ScrollAlignment& s) {
+ return s.rect_hidden_;
+ }
+
+ // Returns the scroll offset the scroller needs to scroll to in order to put
+ // |expose_rect| into |visible_scroll_snapport_rect| aligned by |align_x| and
+ // |align_y|.
+ // The coordinates for |visible_scroll_snapport_rect|, |expose_rect| and
+ // |current_scroll_position| are based on the scroller's scroll_origin
+ // Note that the |current_scroll_offset| is not the location of
+ // |visible_scroll_snapport_rect|, as |visible_scroll_snapport_rect| is the
+ // visible rect contracted by its scroll-padding.
+ // FIXME: This function should probably go somewhere else but where?
+ static ScrollOffset GetScrollOffsetToExpose(
+ const LayoutRect& visible_scroll_snapport_rect,
+ const LayoutRect& expose_rect,
+ const ScrollAlignment& align_x,
+ const ScrollAlignment& align_y,
+ const ScrollOffset& current_scroll_offset);
+
+ static const ScrollAlignment kAlignCenterIfNeeded;
+ static const ScrollAlignment kAlignToEdgeIfNeeded;
+ static const ScrollAlignment kAlignCenterAlways;
+ static const ScrollAlignment kAlignTopAlways;
+ static const ScrollAlignment kAlignBottomAlways;
+ static const ScrollAlignment kAlignLeftAlways;
+ static const ScrollAlignment kAlignRightAlways;
+
+ ScrollAlignmentBehavior rect_visible_;
+ ScrollAlignmentBehavior rect_hidden_;
+ ScrollAlignmentBehavior rect_partial_;
+};
+
+inline bool PLATFORM_EXPORT operator==(const ScrollAlignment& lhs,
+ const ScrollAlignment& rhs) {
+ return lhs.rect_visible_ == rhs.rect_visible_ &&
+ lhs.rect_hidden_ == rhs.rect_hidden_ &&
+ lhs.rect_partial_ == rhs.rect_partial_;
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLL_ALIGNMENT_H_
diff --git a/chromium/third_party/blink/renderer/platform/scroll/scroll_animator.cc b/chromium/third_party/blink/renderer/platform/scroll/scroll_animator.cc
new file mode 100644
index 00000000000..57675df549e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/scroll_animator.cc
@@ -0,0 +1,449 @@
+/*
+ * Copyright (c) 2011, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/renderer/platform/scroll/scroll_animator.h"
+
+#include <memory>
+#include "base/memory/scoped_refptr.h"
+#include "cc/animation/scroll_offset_animation_curve.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_compositor_support.h"
+#include "third_party/blink/renderer/platform/animation/compositor_keyframe_model.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/scroll/main_thread_scrolling_reason.h"
+#include "third_party/blink/renderer/platform/scroll/scrollable_area.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+namespace {
+
+WebLayer* ToWebLayer(GraphicsLayer* layer) {
+ return layer ? layer->PlatformLayer() : nullptr;
+}
+
+} // namespace
+
+ScrollAnimatorBase* ScrollAnimatorBase::Create(
+ ScrollableArea* scrollable_area) {
+ if (scrollable_area && scrollable_area->ScrollAnimatorEnabled())
+ return new ScrollAnimator(scrollable_area);
+ return new ScrollAnimatorBase(scrollable_area);
+}
+
+ScrollAnimator::ScrollAnimator(ScrollableArea* scrollable_area,
+ WTF::TimeFunction time_function)
+ : ScrollAnimatorBase(scrollable_area),
+ time_function_(time_function),
+ last_granularity_(kScrollByPixel) {}
+
+ScrollAnimator::~ScrollAnimator() = default;
+
+ScrollOffset ScrollAnimator::DesiredTargetOffset() const {
+ if (run_state_ == RunState::kWaitingToCancelOnCompositor)
+ return CurrentOffset();
+ return (animation_curve_ ||
+ run_state_ == RunState::kWaitingToSendToCompositor)
+ ? target_offset_
+ : CurrentOffset();
+}
+
+bool ScrollAnimator::HasRunningAnimation() const {
+ return run_state_ != RunState::kPostAnimationCleanup &&
+ (animation_curve_ ||
+ run_state_ == RunState::kWaitingToSendToCompositor);
+}
+
+ScrollOffset ScrollAnimator::ComputeDeltaToConsume(
+ const ScrollOffset& delta) const {
+ ScrollOffset pos = DesiredTargetOffset();
+ ScrollOffset new_pos = scrollable_area_->ClampScrollOffset(pos + delta);
+ return new_pos - pos;
+}
+
+void ScrollAnimator::ResetAnimationState() {
+ ScrollAnimatorCompositorCoordinator::ResetAnimationState();
+ if (animation_curve_)
+ animation_curve_.reset();
+ start_time_ = 0.0;
+}
+
+ScrollResult ScrollAnimator::UserScroll(ScrollGranularity granularity,
+ const ScrollOffset& delta) {
+ if (!scrollable_area_->ScrollAnimatorEnabled())
+ return ScrollAnimatorBase::UserScroll(granularity, delta);
+
+ TRACE_EVENT0("blink", "ScrollAnimator::scroll");
+
+ if (granularity == kScrollByPrecisePixel) {
+ // Cancel scroll animation because asked to instant scroll.
+ if (HasRunningAnimation())
+ CancelAnimation();
+ return ScrollAnimatorBase::UserScroll(granularity, delta);
+ }
+
+ bool needs_post_animation_cleanup =
+ run_state_ == RunState::kPostAnimationCleanup;
+ if (run_state_ == RunState::kPostAnimationCleanup)
+ ResetAnimationState();
+
+ ScrollOffset consumed_delta = ComputeDeltaToConsume(delta);
+ ScrollOffset target_offset = DesiredTargetOffset();
+ target_offset += consumed_delta;
+
+ if (WillAnimateToOffset(target_offset)) {
+ last_granularity_ = granularity;
+ // Report unused delta only if there is no animation running. See
+ // comment below regarding scroll latching.
+ // TODO(bokan): Need to standardize how ScrollAnimators report
+ // unusedDelta. This differs from ScrollAnimatorMac currently.
+ return ScrollResult(true, true, 0, 0);
+ }
+
+ // If the run state when this method was called was PostAnimationCleanup and
+ // we're not starting an animation, stay in PostAnimationCleanup state so
+ // that the main thread scrolling reason can be removed.
+ if (needs_post_animation_cleanup)
+ run_state_ = RunState::kPostAnimationCleanup;
+
+ // Report unused delta only if there is no animation and we are not
+ // starting one. This ensures we latch for the duration of the
+ // animation rather than animating multiple scrollers at the same time.
+ return ScrollResult(false, false, delta.Width(), delta.Height());
+}
+
+bool ScrollAnimator::WillAnimateToOffset(const ScrollOffset& target_offset) {
+ if (run_state_ == RunState::kPostAnimationCleanup)
+ ResetAnimationState();
+
+ if (run_state_ == RunState::kWaitingToCancelOnCompositor ||
+ run_state_ == RunState::kWaitingToCancelOnCompositorButNewScroll) {
+ DCHECK(animation_curve_);
+ target_offset_ = target_offset;
+ if (RegisterAndScheduleAnimation())
+ run_state_ = RunState::kWaitingToCancelOnCompositorButNewScroll;
+ return true;
+ }
+
+ if (animation_curve_) {
+ if ((target_offset - target_offset_).IsZero())
+ return true;
+
+ target_offset_ = target_offset;
+ DCHECK(run_state_ == RunState::kRunningOnMainThread ||
+ run_state_ == RunState::kRunningOnCompositor ||
+ run_state_ == RunState::kRunningOnCompositorButNeedsUpdate ||
+ run_state_ == RunState::kRunningOnCompositorButNeedsTakeover);
+
+ // Running on the main thread, simply update the target offset instead
+ // of sending to the compositor.
+ if (run_state_ == RunState::kRunningOnMainThread) {
+ animation_curve_->UpdateTarget(
+ time_function_() - start_time_,
+ CompositorOffsetFromBlinkOffset(target_offset));
+ return true;
+ }
+
+ if (RegisterAndScheduleAnimation())
+ run_state_ = RunState::kRunningOnCompositorButNeedsUpdate;
+ return true;
+ }
+
+ if ((target_offset - CurrentOffset()).IsZero())
+ return false;
+
+ target_offset_ = target_offset;
+ start_time_ = time_function_();
+
+ if (RegisterAndScheduleAnimation())
+ run_state_ = RunState::kWaitingToSendToCompositor;
+
+ return true;
+}
+
+void ScrollAnimator::AdjustAnimationAndSetScrollOffset(
+ const ScrollOffset& offset,
+ ScrollType scroll_type) {
+ IntSize adjustment = RoundedIntSize(offset) -
+ RoundedIntSize(scrollable_area_->GetScrollOffset());
+ ScrollOffsetChanged(offset, scroll_type);
+
+ if (run_state_ == RunState::kIdle) {
+ AdjustImplOnlyScrollOffsetAnimation(adjustment);
+ } else if (HasRunningAnimation()) {
+ target_offset_ += ScrollOffset(adjustment);
+ if (animation_curve_) {
+ animation_curve_->ApplyAdjustment(adjustment);
+ if (run_state_ != RunState::kRunningOnMainThread &&
+ RegisterAndScheduleAnimation())
+ run_state_ = RunState::kRunningOnCompositorButNeedsAdjustment;
+ }
+ }
+}
+
+void ScrollAnimator::ScrollToOffsetWithoutAnimation(
+ const ScrollOffset& offset) {
+ current_offset_ = offset;
+
+ ResetAnimationState();
+ NotifyOffsetChanged();
+}
+
+void ScrollAnimator::TickAnimation(double monotonic_time) {
+ if (run_state_ != RunState::kRunningOnMainThread)
+ return;
+
+ TRACE_EVENT0("blink", "ScrollAnimator::tickAnimation");
+ double elapsed_time = monotonic_time - start_time_;
+
+ bool is_finished = (elapsed_time > animation_curve_->Duration());
+ ScrollOffset offset = BlinkOffsetFromCompositorOffset(
+ is_finished ? animation_curve_->TargetValue()
+ : animation_curve_->GetValue(elapsed_time));
+
+ offset = scrollable_area_->ClampScrollOffset(offset);
+
+ current_offset_ = offset;
+
+ if (is_finished)
+ run_state_ = RunState::kPostAnimationCleanup;
+ else
+ GetScrollableArea()->ScheduleAnimation();
+
+ TRACE_EVENT0("blink", "ScrollAnimator::notifyOffsetChanged");
+ NotifyOffsetChanged();
+}
+
+void ScrollAnimator::PostAnimationCleanupAndReset() {
+ // Remove the temporary main thread scrolling reason that was added while
+ // main thread had scheduled an animation.
+ RemoveMainThreadScrollingReason();
+
+ ResetAnimationState();
+}
+
+bool ScrollAnimator::SendAnimationToCompositor() {
+ if (scrollable_area_->ShouldScrollOnMainThread())
+ return false;
+
+ std::unique_ptr<CompositorKeyframeModel> animation =
+ CompositorKeyframeModel::Create(
+ *animation_curve_, CompositorTargetProperty::SCROLL_OFFSET, 0, 0);
+ // Being here means that either there is an animation that needs
+ // to be sent to the compositor, or an animation that needs to
+ // be updated (a new scroll event before the previous animation
+ // is finished). In either case, the start time is when the
+ // first animation was initiated. This re-targets the animation
+ // using the current time on main thread.
+ animation->SetStartTime(start_time_);
+
+ int animation_id = animation->Id();
+ int animation_group_id = animation->Group();
+
+ bool sent_to_compositor = AddAnimation(std::move(animation));
+ if (sent_to_compositor) {
+ run_state_ = RunState::kRunningOnCompositor;
+ compositor_animation_id_ = animation_id;
+ compositor_animation_group_id_ = animation_group_id;
+ }
+
+ return sent_to_compositor;
+}
+
+void ScrollAnimator::CreateAnimationCurve() {
+ DCHECK(!animation_curve_);
+ animation_curve_ = CompositorScrollOffsetAnimationCurve::Create(
+ CompositorOffsetFromBlinkOffset(target_offset_),
+ last_granularity_ == kScrollByPixel
+ ? CompositorScrollOffsetAnimationCurve::kScrollDurationInverseDelta
+ : CompositorScrollOffsetAnimationCurve::kScrollDurationConstant);
+ animation_curve_->SetInitialValue(
+ CompositorOffsetFromBlinkOffset(CurrentOffset()));
+}
+
+void ScrollAnimator::UpdateCompositorAnimations() {
+ ScrollAnimatorCompositorCoordinator::UpdateCompositorAnimations();
+
+ if (run_state_ == RunState::kPostAnimationCleanup) {
+ PostAnimationCleanupAndReset();
+ return;
+ }
+
+ if (run_state_ == RunState::kWaitingToCancelOnCompositor) {
+ DCHECK(compositor_animation_id_);
+ AbortAnimation();
+ PostAnimationCleanupAndReset();
+ return;
+ }
+
+ if (run_state_ == RunState::kRunningOnCompositorButNeedsTakeover) {
+ // The call to ::takeOverCompositorAnimation aborted the animation and
+ // put us in this state. The assumption is that takeOver is called
+ // because a main thread scrolling reason is added, and simply trying
+ // to ::sendAnimationToCompositor will fail and we will run on the main
+ // thread.
+ ResetAnimationIds();
+ run_state_ = RunState::kWaitingToSendToCompositor;
+ }
+
+ if (run_state_ == RunState::kRunningOnCompositorButNeedsUpdate ||
+ run_state_ == RunState::kWaitingToCancelOnCompositorButNewScroll ||
+ run_state_ == RunState::kRunningOnCompositorButNeedsAdjustment) {
+ // Abort the running animation before a new one with an updated
+ // target is added.
+ AbortAnimation();
+ ResetAnimationIds();
+
+ if (run_state_ != RunState::kRunningOnCompositorButNeedsAdjustment) {
+ // When in RunningOnCompositorButNeedsAdjustment, the call to
+ // ::adjustScrollOffsetAnimation should have made the necessary
+ // adjustment to the curve.
+ animation_curve_->UpdateTarget(
+ time_function_() - start_time_,
+ CompositorOffsetFromBlinkOffset(target_offset_));
+ }
+
+ if (run_state_ == RunState::kWaitingToCancelOnCompositorButNewScroll) {
+ animation_curve_->SetInitialValue(
+ CompositorOffsetFromBlinkOffset(CurrentOffset()));
+ }
+
+ run_state_ = RunState::kWaitingToSendToCompositor;
+ }
+
+ if (run_state_ == RunState::kWaitingToSendToCompositor) {
+ if (!element_id_)
+ ReattachCompositorAnimationIfNeeded(
+ GetScrollableArea()->GetCompositorAnimationTimeline());
+
+ if (!animation_curve_)
+ CreateAnimationCurve();
+
+ bool running_on_main_thread = false;
+ bool sent_to_compositor = SendAnimationToCompositor();
+ if (!sent_to_compositor) {
+ running_on_main_thread = RegisterAndScheduleAnimation();
+ if (running_on_main_thread)
+ run_state_ = RunState::kRunningOnMainThread;
+ }
+
+ // Main thread should deal with the scroll animations it started.
+ if (sent_to_compositor || running_on_main_thread)
+ AddMainThreadScrollingReason();
+ else
+ RemoveMainThreadScrollingReason();
+ }
+}
+
+void ScrollAnimator::AddMainThreadScrollingReason() {
+ // Usually main thread scrolling reasons should be updated from
+ // one frame to all its descendants. khandlingScrollFromMainThread
+ // is a special case because its subframes cannot be scrolled
+ // when the reason is set. When the subframes are ready to scroll
+ // the reason has benn reset.
+ if (WebLayer* scroll_layer =
+ ToWebLayer(GetScrollableArea()->LayerForScrolling())) {
+ scroll_layer->AddMainThreadScrollingReasons(
+ MainThreadScrollingReason::kHandlingScrollFromMainThread);
+ }
+}
+
+void ScrollAnimator::RemoveMainThreadScrollingReason() {
+ if (WebLayer* scroll_layer =
+ ToWebLayer(GetScrollableArea()->LayerForScrolling())) {
+ scroll_layer->ClearMainThreadScrollingReasons(
+ MainThreadScrollingReason::kHandlingScrollFromMainThread);
+ }
+}
+
+void ScrollAnimator::NotifyCompositorAnimationAborted(int group_id) {
+ // An animation aborted by the compositor is treated as a finished
+ // animation.
+ ScrollAnimatorCompositorCoordinator::CompositorAnimationFinished(group_id);
+}
+
+void ScrollAnimator::NotifyCompositorAnimationFinished(int group_id) {
+ ScrollAnimatorCompositorCoordinator::CompositorAnimationFinished(group_id);
+}
+
+void ScrollAnimator::NotifyAnimationTakeover(
+ double monotonic_time,
+ double animation_start_time,
+ std::unique_ptr<cc::AnimationCurve> curve) {
+ // If there is already an animation running and the compositor asks to take
+ // over an animation, do nothing to avoid judder.
+ if (HasRunningAnimation())
+ return;
+
+ cc::ScrollOffsetAnimationCurve* scroll_offset_animation_curve =
+ curve->ToScrollOffsetAnimationCurve();
+ ScrollOffset target_value(scroll_offset_animation_curve->target_value().x(),
+ scroll_offset_animation_curve->target_value().y());
+ if (WillAnimateToOffset(target_value)) {
+ animation_curve_ = CompositorScrollOffsetAnimationCurve::Create(
+ scroll_offset_animation_curve);
+ start_time_ = animation_start_time;
+ }
+}
+
+void ScrollAnimator::CancelAnimation() {
+ ScrollAnimatorCompositorCoordinator::CancelAnimation();
+}
+
+void ScrollAnimator::TakeOverCompositorAnimation() {
+ if (run_state_ == RunState::kRunningOnCompositor ||
+ run_state_ == RunState::kRunningOnCompositorButNeedsUpdate)
+ RemoveMainThreadScrollingReason();
+
+ ScrollAnimatorCompositorCoordinator::TakeOverCompositorAnimation();
+}
+
+void ScrollAnimator::LayerForCompositedScrollingDidChange(
+ CompositorAnimationTimeline* timeline) {
+ if (ReattachCompositorAnimationIfNeeded(timeline) && animation_curve_)
+ AddMainThreadScrollingReason();
+}
+
+bool ScrollAnimator::RegisterAndScheduleAnimation() {
+ GetScrollableArea()->RegisterForAnimation();
+ if (!scrollable_area_->ScheduleAnimation()) {
+ ScrollToOffsetWithoutAnimation(target_offset_);
+ ResetAnimationState();
+ return false;
+ }
+ return true;
+}
+
+void ScrollAnimator::Trace(blink::Visitor* visitor) {
+ ScrollAnimatorBase::Trace(visitor);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scroll/scroll_animator.h b/chromium/third_party/blink/renderer/platform/scroll/scroll_animator.h
new file mode 100644
index 00000000000..0ebcf411a9b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/scroll_animator.h
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2011, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLL_ANIMATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLL_ANIMATOR_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/animation/compositor_animation_client.h"
+#include "third_party/blink/renderer/platform/animation/compositor_animation_delegate.h"
+#include "third_party/blink/renderer/platform/animation/compositor_scroll_offset_animation_curve.h"
+#include "third_party/blink/renderer/platform/scroll/scroll_animator_base.h"
+#include "third_party/blink/renderer/platform/timer.h"
+
+namespace blink {
+
+class CompositorAnimationTimeline;
+
+// ScrollAnimator is the Blink-side implementation of user-input scroll offset
+// animations ("smooth scrolling") on all platforms except for Mac.
+//
+// See http://bit.ly/smoothscrolling for general info about user-input smooth
+// scrolling. For the Mac implementation, see ScrollAnimatorMac. For
+// programmatic (CSSOM) smooth scrolls, see ProgrammaticScrollAnimator.
+//
+// When Blink receives an input event that should start or update a scroll
+// animation, it calls ScrollAnimator::UserScroll. This will construct an
+// animation curve object which can report the desired scroll offset as a
+// function of elapsed time. See cc/animation/scroll_offset_animation_curve.h
+// for more info about the animation curve logic (including velocity-matched
+// target updating).
+//
+// Having established an animation curve, the logic for servicing the animation
+// is highly dependent on compositing. There are four scenarios to consider:
+//
+// (1) Scroll animation running on the compositor, scheduled by the compositor
+// (LayerTreeHostImpl::ScrollAnimated) in response to a scroll wheel input
+// event handled by the compositor thread. Blink doesn't know about these.
+//
+// (2) Scroll animation running on the compositor, scheduled by Blink. For
+// example, a keyboard scroll of a composited scroller.
+//
+// (3) Scroll animation of a composited scroller, running on the main thread due
+// to main-thread scrolling reasons (for example, non-composited fixed-
+// position elements that need to be repainted on scroll).
+//
+// (4) Scroll animation of a non-composited scroller, running on the main
+// thread.
+//
+// In scenarios (1) and (2) the animation is created as a cc::Animation with
+// TargetProperty::SCROLL_OFFSET and added to a cc::Animation that is
+// serviced on the compositor thread (in cc::AnimationHost::TickAnimations).
+// This lets the animation play smoothly even if the main thread is janked.
+//
+// In scenarios (3) and (4), we schedule the animation ticks on the main thread
+// using ScrollableArea::ScheduleAnimation, and update the scroll offset during
+// ScrollAnimator::TickAnimation.
+//
+// There is a special main-thread scrolling reason kHandlingScrollFromMainThread
+// set in scenarios (2) and (3) for the duration of the scroll, to prevent
+// interference from events that would otherwise trigger scenario (1).
+//
+// There is a complicated handoff from (1) to (3) in the event that a main-
+// thread scrolling reason is added in the middle of an animation. This is
+// handled by TakeOverCompositorAnimation, which aborts the animation in cc and
+// sends an AnimationEvent::TAKEOVER back to the main thread containing a copy
+// of the curve. That calls back into NotifyAnimationTakeover which starts a
+// new animation on main to play the "remainder" of the curve.
+//
+// The logic for Blink-side scheduling of compositor-serviced scroll offset
+// animations is shared with ProgrammaticScrollAnimator, and lives mostly in the
+// common base class ScrollAnimatorCompositorCoordinator.
+
+class PLATFORM_EXPORT ScrollAnimator : public ScrollAnimatorBase {
+ public:
+ explicit ScrollAnimator(ScrollableArea*,
+ WTF::TimeFunction = WTF::CurrentTimeTicksInSeconds);
+ ~ScrollAnimator() override;
+
+ bool HasRunningAnimation() const override;
+ ScrollOffset ComputeDeltaToConsume(const ScrollOffset& delta) const override;
+
+ ScrollResult UserScroll(ScrollGranularity,
+ const ScrollOffset& delta) override;
+ void ScrollToOffsetWithoutAnimation(const ScrollOffset&) override;
+ ScrollOffset DesiredTargetOffset() const override;
+
+ // ScrollAnimatorCompositorCoordinator implementation.
+ void TickAnimation(double monotonic_time) override;
+ void CancelAnimation() override;
+ void AdjustAnimationAndSetScrollOffset(const ScrollOffset&,
+ ScrollType) override;
+ void TakeOverCompositorAnimation() override;
+ void ResetAnimationState() override;
+ void UpdateCompositorAnimations() override;
+ void NotifyCompositorAnimationFinished(int group_id) override;
+ void NotifyCompositorAnimationAborted(int group_id) override;
+ void LayerForCompositedScrollingDidChange(
+ CompositorAnimationTimeline*) override;
+
+ virtual void Trace(blink::Visitor*);
+
+ protected:
+ // Returns whether or not the animation was sent to the compositor.
+ virtual bool SendAnimationToCompositor();
+
+ void NotifyAnimationTakeover(double monotonic_time,
+ double animation_start_time,
+ std::unique_ptr<cc::AnimationCurve>) override;
+
+ std::unique_ptr<CompositorScrollOffsetAnimationCurve> animation_curve_;
+ double start_time_;
+ WTF::TimeFunction time_function_;
+
+ private:
+ // Returns true if the animation was scheduled successfully. If animation
+ // could not be scheduled (e.g. because the frame is detached), scrolls
+ // immediately to the target and returns false.
+ bool RegisterAndScheduleAnimation();
+
+ void CreateAnimationCurve();
+ void PostAnimationCleanupAndReset();
+
+ void AddMainThreadScrollingReason();
+ void RemoveMainThreadScrollingReason();
+
+ // Returns true if will animate to the given target offset. Returns false
+ // only when there is no animation running and we are not starting one
+ // because we are already at targetPos.
+ bool WillAnimateToOffset(const ScrollOffset& target_pos);
+
+ ScrollOffset target_offset_;
+ ScrollGranularity last_granularity_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLL_ANIMATOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/scroll/scroll_animator_base.cc b/chromium/third_party/blink/renderer/platform/scroll/scroll_animator_base.cc
new file mode 100644
index 00000000000..db194b61fe3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/scroll_animator_base.cc
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2010, 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 "third_party/blink/renderer/platform/scroll/scroll_animator_base.h"
+
+#include "third_party/blink/renderer/platform/scroll/scrollable_area.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+namespace blink {
+
+ScrollAnimatorBase::ScrollAnimatorBase(ScrollableArea* scrollable_area)
+ : scrollable_area_(scrollable_area) {}
+
+ScrollAnimatorBase::~ScrollAnimatorBase() = default;
+
+ScrollOffset ScrollAnimatorBase::ComputeDeltaToConsume(
+ const ScrollOffset& delta) const {
+ ScrollOffset new_pos =
+ scrollable_area_->ClampScrollOffset(current_offset_ + delta);
+ return new_pos - current_offset_;
+}
+
+ScrollResult ScrollAnimatorBase::UserScroll(ScrollGranularity,
+ const ScrollOffset& delta) {
+ ScrollOffset consumed_delta = ComputeDeltaToConsume(delta);
+ ScrollOffset new_pos = current_offset_ + consumed_delta;
+ if (current_offset_ == new_pos)
+ return ScrollResult(false, false, delta.Width(), delta.Height());
+
+ current_offset_ = new_pos;
+
+ NotifyOffsetChanged();
+
+ return ScrollResult(consumed_delta.Width(), consumed_delta.Height(),
+ delta.Width() - consumed_delta.Width(),
+ delta.Height() - consumed_delta.Height());
+}
+
+void ScrollAnimatorBase::ScrollToOffsetWithoutAnimation(
+ const ScrollOffset& offset) {
+ current_offset_ = offset;
+ NotifyOffsetChanged();
+}
+
+void ScrollAnimatorBase::SetCurrentOffset(const ScrollOffset& offset) {
+ current_offset_ = offset;
+}
+
+ScrollOffset ScrollAnimatorBase::CurrentOffset() const {
+ return current_offset_;
+}
+
+void ScrollAnimatorBase::NotifyOffsetChanged() {
+ ScrollOffsetChanged(current_offset_, kUserScroll);
+}
+
+void ScrollAnimatorBase::Trace(blink::Visitor* visitor) {
+ visitor->Trace(scrollable_area_);
+ ScrollAnimatorCompositorCoordinator::Trace(visitor);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scroll/scroll_animator_base.h b/chromium/third_party/blink/renderer/platform/scroll/scroll_animator_base.h
new file mode 100644
index 00000000000..8db422f137d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/scroll_animator_base.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2010, 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLL_ANIMATOR_BASE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLL_ANIMATOR_BASE_H_
+
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/scroll/scroll_animator_compositor_coordinator.h"
+#include "third_party/blink/renderer/platform/scroll/scroll_types.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+class CompositorAnimationTimeline;
+class ScrollableArea;
+class Scrollbar;
+
+// ScrollAnimatorBase is the common base class for all user scroll animators.
+// Every scrollable area has a lazily-created animator for user-input scrolls
+// (ScrollableArea::scroll_animator_).
+//
+// ScrollAnimatorBase is directly instantiated when scroll animations are
+// disabled. In this case, all scrolls are instantaneous.
+
+class PLATFORM_EXPORT ScrollAnimatorBase
+ : public ScrollAnimatorCompositorCoordinator {
+ public:
+ static ScrollAnimatorBase* Create(ScrollableArea*);
+
+ virtual ~ScrollAnimatorBase();
+
+ virtual void Dispose() {}
+
+ // A possibly animated scroll. The base class implementation always scrolls
+ // immediately, never animates. If the scroll is animated and currently the
+ // animator has an in-progress animation, the ScrollResult will always return
+ // no unusedDelta and didScroll=true, i.e. fully consuming the scroll request.
+ // This makes animations latch to a single scroller. Note, the semantics are
+ // currently somewhat different on Mac - see ScrollAnimatorMac.mm.
+ virtual ScrollResult UserScroll(ScrollGranularity, const ScrollOffset& delta);
+
+ virtual void ScrollToOffsetWithoutAnimation(const ScrollOffset&);
+
+ void SetCurrentOffset(const ScrollOffset&);
+ ScrollOffset CurrentOffset() const;
+ virtual ScrollOffset DesiredTargetOffset() const { return CurrentOffset(); }
+
+ // Returns how much of pixelDelta will be used by the underlying scrollable
+ // area.
+ virtual ScrollOffset ComputeDeltaToConsume(const ScrollOffset& delta) const;
+
+ // ScrollAnimatorCompositorCoordinator implementation.
+ ScrollableArea* GetScrollableArea() const override {
+ return scrollable_area_;
+ }
+ void TickAnimation(double monotonic_time) override {}
+ void CancelAnimation() override {}
+ void TakeOverCompositorAnimation() override {}
+ void UpdateCompositorAnimations() override {}
+ void NotifyCompositorAnimationFinished(int group_id) override {}
+ void NotifyCompositorAnimationAborted(int group_id) override {}
+ void LayerForCompositedScrollingDidChange(
+ CompositorAnimationTimeline*) override {}
+
+ virtual void ContentAreaWillPaint() const {}
+ virtual void MouseEnteredContentArea() const {}
+ virtual void MouseExitedContentArea() const {}
+ virtual void MouseMovedInContentArea() const {}
+ virtual void MouseEnteredScrollbar(Scrollbar&) const {}
+ virtual void MouseExitedScrollbar(Scrollbar&) const {}
+ virtual void ContentsResized() const {}
+ virtual void ContentAreaDidShow() const {}
+ virtual void ContentAreaDidHide() const {}
+
+ virtual void FinishCurrentScrollAnimations() {}
+
+ virtual void DidAddVerticalScrollbar(Scrollbar&) {}
+ virtual void WillRemoveVerticalScrollbar(Scrollbar&) {}
+ virtual void DidAddHorizontalScrollbar(Scrollbar&) {}
+ virtual void WillRemoveHorizontalScrollbar(Scrollbar&) {}
+
+ virtual void NotifyContentAreaScrolled(const ScrollOffset&, ScrollType) {}
+
+ virtual bool SetScrollbarsVisibleForTesting(bool) { return false; }
+
+ virtual void Trace(blink::Visitor*);
+
+ protected:
+ explicit ScrollAnimatorBase(ScrollableArea*);
+
+ virtual void NotifyOffsetChanged();
+
+ Member<ScrollableArea> scrollable_area_;
+
+ ScrollOffset current_offset_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLL_ANIMATOR_BASE_H_
diff --git a/chromium/third_party/blink/renderer/platform/scroll/scroll_animator_compositor_coordinator.cc b/chromium/third_party/blink/renderer/platform/scroll/scroll_animator_compositor_coordinator.cc
new file mode 100644
index 00000000000..48cb9f9df65
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/scroll_animator_compositor_coordinator.cc
@@ -0,0 +1,349 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scroll/scroll_animator_compositor_coordinator.h"
+
+#include <memory>
+#include "cc/animation/scroll_offset_animation_curve.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_compositor_support.h"
+#include "third_party/blink/renderer/platform/animation/compositor_animation.h"
+#include "third_party/blink/renderer/platform/animation/compositor_animation_host.h"
+#include "third_party/blink/renderer/platform/animation/compositor_animation_timeline.h"
+#include "third_party/blink/renderer/platform/animation/compositor_keyframe_model.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/scroll/scrollable_area.h"
+
+namespace blink {
+
+ScrollAnimatorCompositorCoordinator::ScrollAnimatorCompositorCoordinator()
+ : element_id_(),
+ run_state_(RunState::kIdle),
+ compositor_animation_id_(0),
+ compositor_animation_group_id_(0),
+ impl_only_animation_takeover_(false) {
+ compositor_animation_ = CompositorAnimation::Create();
+ DCHECK(compositor_animation_);
+ compositor_animation_->SetAnimationDelegate(this);
+}
+
+ScrollAnimatorCompositorCoordinator::~ScrollAnimatorCompositorCoordinator() =
+ default;
+
+void ScrollAnimatorCompositorCoordinator::Dispose() {
+ compositor_animation_->SetAnimationDelegate(nullptr);
+ compositor_animation_.reset();
+}
+
+void ScrollAnimatorCompositorCoordinator::ResetAnimationIds() {
+ compositor_animation_id_ = 0;
+ compositor_animation_group_id_ = 0;
+}
+
+void ScrollAnimatorCompositorCoordinator::ResetAnimationState() {
+ run_state_ = RunState::kIdle;
+ ResetAnimationIds();
+}
+
+bool ScrollAnimatorCompositorCoordinator::HasAnimationThatRequiresService()
+ const {
+ if (HasImplOnlyAnimationUpdate())
+ return true;
+
+ switch (run_state_) {
+ case RunState::kIdle:
+ case RunState::kRunningOnCompositor:
+ return false;
+ case RunState::kWaitingToCancelOnCompositorButNewScroll:
+ case RunState::kPostAnimationCleanup:
+ case RunState::kWaitingToSendToCompositor:
+ case RunState::kRunningOnMainThread:
+ case RunState::kRunningOnCompositorButNeedsUpdate:
+ case RunState::kRunningOnCompositorButNeedsTakeover:
+ case RunState::kRunningOnCompositorButNeedsAdjustment:
+ case RunState::kWaitingToCancelOnCompositor:
+ return true;
+ }
+ NOTREACHED();
+ return false;
+}
+
+bool ScrollAnimatorCompositorCoordinator::AddAnimation(
+ std::unique_ptr<CompositorKeyframeModel> keyframe_model) {
+ if (compositor_animation_->IsElementAttached()) {
+ compositor_animation_->AddKeyframeModel(std::move(keyframe_model));
+ return true;
+ }
+ return false;
+}
+
+void ScrollAnimatorCompositorCoordinator::RemoveAnimation() {
+ if (compositor_animation_->IsElementAttached())
+ compositor_animation_->RemoveKeyframeModel(compositor_animation_id_);
+}
+
+void ScrollAnimatorCompositorCoordinator::AbortAnimation() {
+ if (compositor_animation_->IsElementAttached())
+ compositor_animation_->AbortKeyframeModel(compositor_animation_id_);
+}
+
+void ScrollAnimatorCompositorCoordinator::CancelAnimation() {
+ switch (run_state_) {
+ case RunState::kIdle:
+ case RunState::kWaitingToCancelOnCompositor:
+ case RunState::kPostAnimationCleanup:
+ break;
+ case RunState::kWaitingToSendToCompositor:
+ if (compositor_animation_id_) {
+ // We still have a previous animation running on the compositor.
+ run_state_ = RunState::kWaitingToCancelOnCompositor;
+ } else {
+ ResetAnimationState();
+ }
+ break;
+ case RunState::kRunningOnMainThread:
+ run_state_ = RunState::kPostAnimationCleanup;
+ break;
+ case RunState::kWaitingToCancelOnCompositorButNewScroll:
+ case RunState::kRunningOnCompositorButNeedsAdjustment:
+ case RunState::kRunningOnCompositorButNeedsTakeover:
+ case RunState::kRunningOnCompositorButNeedsUpdate:
+ case RunState::kRunningOnCompositor:
+ run_state_ = RunState::kWaitingToCancelOnCompositor;
+
+ // Get serviced the next time compositor updates are allowed.
+ GetScrollableArea()->RegisterForAnimation();
+ }
+}
+
+void ScrollAnimatorCompositorCoordinator::TakeOverCompositorAnimation() {
+ switch (run_state_) {
+ case RunState::kIdle:
+ TakeOverImplOnlyScrollOffsetAnimation();
+ break;
+ case RunState::kWaitingToCancelOnCompositor:
+ case RunState::kWaitingToCancelOnCompositorButNewScroll:
+ case RunState::kPostAnimationCleanup:
+ case RunState::kRunningOnCompositorButNeedsTakeover:
+ case RunState::kWaitingToSendToCompositor:
+ case RunState::kRunningOnMainThread:
+ break;
+ case RunState::kRunningOnCompositorButNeedsAdjustment:
+ case RunState::kRunningOnCompositorButNeedsUpdate:
+ case RunState::kRunningOnCompositor:
+ // We call abortAnimation that makes changes to the animation running on
+ // the compositor. Thus, this function should only be called when in
+ // CompositingClean state.
+ AbortAnimation();
+
+ run_state_ = RunState::kRunningOnCompositorButNeedsTakeover;
+
+ // Get serviced the next time compositor updates are allowed.
+ GetScrollableArea()->RegisterForAnimation();
+ }
+}
+
+void ScrollAnimatorCompositorCoordinator::CompositorAnimationFinished(
+ int group_id) {
+ if (compositor_animation_group_id_ != group_id)
+ return;
+
+ compositor_animation_id_ = 0;
+ compositor_animation_group_id_ = 0;
+
+ switch (run_state_) {
+ case RunState::kIdle:
+ case RunState::kPostAnimationCleanup:
+ case RunState::kRunningOnMainThread:
+ NOTREACHED();
+ break;
+ case RunState::kWaitingToSendToCompositor:
+ case RunState::kWaitingToCancelOnCompositorButNewScroll:
+ break;
+ case RunState::kRunningOnCompositor:
+ case RunState::kRunningOnCompositorButNeedsAdjustment:
+ case RunState::kRunningOnCompositorButNeedsUpdate:
+ case RunState::kRunningOnCompositorButNeedsTakeover:
+ case RunState::kWaitingToCancelOnCompositor:
+ run_state_ = RunState::kPostAnimationCleanup;
+ // Get serviced the next time compositor updates are allowed.
+ if (GetScrollableArea())
+ GetScrollableArea()->RegisterForAnimation();
+ else
+ ResetAnimationState();
+ }
+}
+
+bool ScrollAnimatorCompositorCoordinator::ReattachCompositorAnimationIfNeeded(
+ CompositorAnimationTimeline* timeline) {
+ bool reattached = false;
+ CompositorElementId element_id = GetScrollElementId();
+ DCHECK(element_id || (RuntimeEnabledFeatures::SlimmingPaintV2Enabled() ||
+ !GetScrollableArea()->LayerForScrolling()));
+
+ if (element_id != element_id_) {
+ if (compositor_animation_ && timeline) {
+ // Detach from old layer (if any).
+ if (element_id_) {
+ if (compositor_animation_->IsElementAttached())
+ compositor_animation_->DetachElement();
+ timeline->AnimationDestroyed(*this);
+ }
+ // Attach to new layer (if any).
+ if (element_id) {
+ DCHECK(!compositor_animation_->IsElementAttached());
+ timeline->AnimationAttached(*this);
+ compositor_animation_->AttachElement(element_id);
+ reattached = true;
+ }
+ element_id_ = element_id;
+ }
+ }
+
+ return reattached;
+}
+
+void ScrollAnimatorCompositorCoordinator::NotifyAnimationStarted(
+ double monotonic_time,
+ int group) {}
+
+void ScrollAnimatorCompositorCoordinator::NotifyAnimationFinished(
+ double monotonic_time,
+ int group) {
+ NotifyCompositorAnimationFinished(group);
+}
+
+void ScrollAnimatorCompositorCoordinator::NotifyAnimationAborted(
+ double monotonic_time,
+ int group) {
+ // An animation aborted by the compositor is treated as a finished
+ // animation.
+ NotifyCompositorAnimationFinished(group);
+}
+
+CompositorAnimation*
+ScrollAnimatorCompositorCoordinator::GetCompositorAnimation() const {
+ return compositor_animation_.get();
+}
+
+FloatPoint ScrollAnimatorCompositorCoordinator::CompositorOffsetFromBlinkOffset(
+ ScrollOffset offset) {
+ return GetScrollableArea()->ScrollOrigin() + offset;
+}
+
+ScrollOffset
+ScrollAnimatorCompositorCoordinator::BlinkOffsetFromCompositorOffset(
+ FloatPoint offset) {
+ return offset - GetScrollableArea()->ScrollOrigin();
+}
+
+bool ScrollAnimatorCompositorCoordinator::HasImplOnlyAnimationUpdate() const {
+ return !impl_only_animation_adjustment_.IsZero() ||
+ impl_only_animation_takeover_;
+}
+
+CompositorElementId ScrollAnimatorCompositorCoordinator::GetScrollElementId()
+ const {
+ if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled())
+ return GetScrollableArea()->GetCompositorElementId();
+
+ GraphicsLayer* layer = GetScrollableArea()->LayerForScrolling();
+ return layer ? layer->PlatformLayer()->GetElementId() : CompositorElementId();
+}
+
+void ScrollAnimatorCompositorCoordinator::UpdateImplOnlyCompositorAnimations() {
+ if (!HasImplOnlyAnimationUpdate())
+ return;
+
+ CompositorAnimationHost* host =
+ GetScrollableArea()->GetCompositorAnimationHost();
+ CompositorElementId element_id = GetScrollElementId();
+ if (host && element_id) {
+ if (!impl_only_animation_adjustment_.IsZero()) {
+ host->AdjustImplOnlyScrollOffsetAnimation(
+ element_id, gfx::Vector2dF(impl_only_animation_adjustment_.Width(),
+ impl_only_animation_adjustment_.Height()));
+ }
+ if (impl_only_animation_takeover_)
+ host->TakeOverImplOnlyScrollOffsetAnimation(element_id);
+ }
+ impl_only_animation_adjustment_ = IntSize();
+ impl_only_animation_takeover_ = false;
+}
+
+void ScrollAnimatorCompositorCoordinator::UpdateCompositorAnimations() {
+ if (!GetScrollableArea()->ScrollAnimatorEnabled())
+ return;
+
+ UpdateImplOnlyCompositorAnimations();
+}
+
+void ScrollAnimatorCompositorCoordinator::ScrollOffsetChanged(
+ const ScrollOffset& offset,
+ ScrollType scroll_type) {
+ GetScrollableArea()->ScrollOffsetChanged(offset, scroll_type);
+}
+
+void ScrollAnimatorCompositorCoordinator::AdjustAnimationAndSetScrollOffset(
+ const ScrollOffset& offset,
+ ScrollType scroll_type) {
+ // Subclasses should override this and adjust the animation as necessary.
+ ScrollOffsetChanged(offset, scroll_type);
+}
+
+void ScrollAnimatorCompositorCoordinator::AdjustImplOnlyScrollOffsetAnimation(
+ const IntSize& adjustment) {
+ if (!GetScrollableArea()->ScrollAnimatorEnabled())
+ return;
+
+ impl_only_animation_adjustment_.Expand(adjustment.Width(),
+ adjustment.Height());
+
+ GetScrollableArea()->RegisterForAnimation();
+}
+
+void ScrollAnimatorCompositorCoordinator::
+ TakeOverImplOnlyScrollOffsetAnimation() {
+ if (!GetScrollableArea()->ScrollAnimatorEnabled())
+ return;
+
+ impl_only_animation_takeover_ = true;
+
+ // Update compositor animations right away to avoid skipping a frame.
+ // This imposes the constraint that this function should only be called
+ // from or after DocumentLifecycle::LifecycleState::CompositingClean state.
+ UpdateImplOnlyCompositorAnimations();
+
+ GetScrollableArea()->RegisterForAnimation();
+}
+
+String ScrollAnimatorCompositorCoordinator::RunStateAsText() const {
+ switch (run_state_) {
+ case RunState::kIdle:
+ return String("Idle");
+ case RunState::kWaitingToSendToCompositor:
+ return String("WaitingToSendToCompositor");
+ case RunState::kRunningOnCompositor:
+ return String("RunningOnCompositor");
+ case RunState::kRunningOnMainThread:
+ return String("RunningOnMainThread");
+ case RunState::kRunningOnCompositorButNeedsUpdate:
+ return String("RunningOnCompositorButNeedsUpdate");
+ case RunState::kWaitingToCancelOnCompositor:
+ return String("WaitingToCancelOnCompositor");
+ case RunState::kPostAnimationCleanup:
+ return String("PostAnimationCleanup");
+ case RunState::kRunningOnCompositorButNeedsTakeover:
+ return String("RunningOnCompositorButNeedsTakeover");
+ case RunState::kWaitingToCancelOnCompositorButNewScroll:
+ return String("WaitingToCancelOnCompositorButNewScroll");
+ case RunState::kRunningOnCompositorButNeedsAdjustment:
+ return String("RunningOnCompositorButNeedsAdjustment");
+ }
+ NOTREACHED();
+ return String();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scroll/scroll_animator_compositor_coordinator.h b/chromium/third_party/blink/renderer/platform/scroll/scroll_animator_compositor_coordinator.h
new file mode 100644
index 00000000000..a40156f6e52
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/scroll_animator_compositor_coordinator.h
@@ -0,0 +1,199 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLL_ANIMATOR_COMPOSITOR_COORDINATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLL_ANIMATOR_COMPOSITOR_COORDINATOR_H_
+
+#include <memory>
+#include "base/gtest_prod_util.h"
+#include "cc/animation/animation_curve.h"
+#include "cc/animation/scroll_offset_animations.h"
+#include "third_party/blink/renderer/platform/animation/compositor_animation_client.h"
+#include "third_party/blink/renderer/platform/animation/compositor_animation_delegate.h"
+#include "third_party/blink/renderer/platform/graphics/compositor_element_id.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/scroll/scroll_types.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class ScrollableArea;
+class CompositorAnimation;
+class CompositorAnimationTimeline;
+class CompositorKeyframeModel;
+
+// ScrollAnimatorCompositorCoordinator is the common base class of user scroll
+// animators and programmatic scroll animators, and holds logic related to
+// scheduling and updating scroll animations running on the compositor.
+//
+// See ScrollAnimator.h for more information about scroll animations.
+
+class PLATFORM_EXPORT ScrollAnimatorCompositorCoordinator
+ : public GarbageCollectedFinalized<ScrollAnimatorCompositorCoordinator>,
+ private CompositorAnimationClient,
+ CompositorAnimationDelegate {
+ WTF_MAKE_NONCOPYABLE(ScrollAnimatorCompositorCoordinator);
+ USING_PRE_FINALIZER(ScrollAnimatorCompositorCoordinator, Dispose);
+
+ public:
+ enum class RunState {
+ // No animation.
+ kIdle,
+
+ // Waiting to send an animation to the compositor. There might also
+ // already be another animation running on the compositor that will need
+ // to be canceled first.
+ kWaitingToSendToCompositor,
+
+ // Running an animation on the compositor.
+ kRunningOnCompositor,
+
+ // Running an animation on the compositor but needs update.
+ kRunningOnCompositorButNeedsUpdate,
+
+ // Running an animation on the main thread.
+ kRunningOnMainThread,
+
+ // Waiting to cancel the animation currently running on the compositor.
+ // There is no pending animation to replace the canceled animation.
+ kWaitingToCancelOnCompositor,
+
+ // Finished an animation that was running on the main thread or the
+ // compositor thread. When in this state, post animation cleanup can
+ // be performed.
+ kPostAnimationCleanup,
+
+ // Running an animation on the compositor but need to continue it
+ // on the main thread. This could happen if a main thread scrolling
+ // reason is added while animating the scroll offset.
+ kRunningOnCompositorButNeedsTakeover,
+
+ // Waiting to cancel the animation currently running on the compositor
+ // while another animation is requested. In this case, the currently
+ // running animation is aborted and an animation to the new target
+ // from the current offset is started.
+ kWaitingToCancelOnCompositorButNewScroll,
+
+ // Running an animation on the compositor but an adjustment to the
+ // scroll offset was made on the main thread and the animation must
+ // be updated.
+ kRunningOnCompositorButNeedsAdjustment,
+ };
+
+ virtual ~ScrollAnimatorCompositorCoordinator();
+
+ bool HasAnimationThatRequiresService() const;
+ void Dispose();
+ String RunStateAsText() const;
+
+ virtual bool HasRunningAnimation() const { return false; }
+
+ virtual void ResetAnimationState();
+ virtual void CancelAnimation();
+ // Aborts the currently running scroll offset animation on the compositor
+ // and continues it on the main thread. This should only be called when in
+ // DocumentLifecycle::LifecycleState::CompositingClean state.
+ virtual void TakeOverCompositorAnimation();
+ // Updates the scroll offset of the animator's ScrollableArea by
+ // adjustment and update the target of an ongoing scroll offset animation.
+ virtual void AdjustAnimationAndSetScrollOffset(const ScrollOffset&,
+ ScrollType);
+ virtual void UpdateCompositorAnimations();
+
+ virtual ScrollableArea* GetScrollableArea() const = 0;
+ virtual void TickAnimation(double monotonic_time) = 0;
+ virtual void NotifyCompositorAnimationFinished(int group_id) = 0;
+ virtual void NotifyCompositorAnimationAborted(int group_id) = 0;
+ virtual void LayerForCompositedScrollingDidChange(
+ CompositorAnimationTimeline*) = 0;
+
+ RunState RunStateForTesting() { return run_state_; }
+
+ virtual void Trace(blink::Visitor* visitor) {}
+
+ protected:
+ explicit ScrollAnimatorCompositorCoordinator();
+
+ void ScrollOffsetChanged(const ScrollOffset&, ScrollType);
+
+ void AdjustImplOnlyScrollOffsetAnimation(const IntSize& adjustment);
+ IntSize ImplOnlyAnimationAdjustmentForTesting() {
+ return impl_only_animation_adjustment_;
+ }
+
+ void ResetAnimationIds();
+ bool AddAnimation(std::unique_ptr<CompositorKeyframeModel>);
+ void RemoveAnimation();
+ virtual void AbortAnimation();
+
+ // "offset" in the cc scrolling code is analagous to "position" in the blink
+ // scrolling code:
+ // they both represent the distance from the top-left of the overflow rect to
+ // the top-left
+ // of the viewport. In blink, "offset" refers to the distance of the viewport
+ // from the
+ // beginning of flow of the contents. In left-to-right flows, blink "offset"
+ // and "position" are
+ // equivalent, but in right-to-left flows (including direction:rtl,
+ // writing-mode:vertical-rl,
+ // and flex-direction:row-reverse), they aren't. See core/layout/README.md
+ // for more info.
+ FloatPoint CompositorOffsetFromBlinkOffset(ScrollOffset);
+ ScrollOffset BlinkOffsetFromCompositorOffset(FloatPoint);
+
+ void CompositorAnimationFinished(int group_id);
+ // Returns true if the compositor animation was attached to a new layer.
+ bool ReattachCompositorAnimationIfNeeded(CompositorAnimationTimeline*);
+
+ // CompositorAnimationDelegate implementation.
+ void NotifyAnimationStarted(double monotonic_time, int group) override;
+ void NotifyAnimationFinished(double monotonic_time, int group) override;
+ void NotifyAnimationAborted(double monotonic_time, int group) override;
+ void NotifyAnimationTakeover(double monotonic_time,
+ double animation_start_time,
+ std::unique_ptr<cc::AnimationCurve>) override {}
+
+ // CompositorAnimationClient implementation.
+ CompositorAnimation* GetCompositorAnimation() const override;
+
+ friend class Internals;
+ // TODO(ymalik): Tests are added as friends to access m_RunState. Use the
+ // runStateForTesting accessor instead.
+ FRIEND_TEST_ALL_PREFIXES(ScrollAnimatorTest, MainThreadStates);
+ FRIEND_TEST_ALL_PREFIXES(ScrollAnimatorTest, AnimatedScrollTakeover);
+ FRIEND_TEST_ALL_PREFIXES(ScrollAnimatorTest, CancellingAnimationResetsState);
+ FRIEND_TEST_ALL_PREFIXES(ScrollAnimatorTest, CancellingCompositorAnimation);
+ FRIEND_TEST_ALL_PREFIXES(ScrollAnimatorTest, ImplOnlyAnimationUpdatesCleared);
+
+ std::unique_ptr<CompositorAnimation> compositor_animation_;
+ // The element id to which the compositor animation is attached when
+ // the animation is present.
+ CompositorElementId element_id_;
+ RunState run_state_;
+ int compositor_animation_id_;
+ int compositor_animation_group_id_;
+
+ // An adjustment to the scroll offset on the main thread that may affect
+ // impl-only scroll offset animations.
+ IntSize impl_only_animation_adjustment_;
+
+ // If set to true, sends a cc::ScrollOffsetAnimationUpdate to cc which will
+ // abort the impl-only scroll offset animation and continue it on main
+ // thread.
+ bool impl_only_animation_takeover_;
+
+ private:
+ CompositorElementId GetScrollElementId() const;
+ bool HasImplOnlyAnimationUpdate() const;
+ void UpdateImplOnlyCompositorAnimations();
+ // Accesses compositing state and should only be called when in or after
+ // DocumentLifecycle::LifecycleState::CompositingClean.
+ void TakeOverImplOnlyScrollOffsetAnimation();
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLL_ANIMATOR_COMPOSITOR_COORDINATOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/scroll/scroll_animator_test.cc b/chromium/third_party/blink/renderer/platform/scroll/scroll_animator_test.cc
new file mode 100644
index 00000000000..617e7d68075
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/scroll_animator_test.cc
@@ -0,0 +1,698 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 the ScrollAnimator class.
+
+#include "third_party/blink/renderer/platform/scroll/scroll_animator.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
+#include "third_party/blink/public/platform/web_thread.h"
+#include "third_party/blink/renderer/platform/geometry/float_point.h"
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/scheduler/child/web_scheduler.h"
+#include "third_party/blink/renderer/platform/scroll/scroll_animator_base.h"
+#include "third_party/blink/renderer/platform/scroll/scrollable_area.h"
+#include "third_party/blink/renderer/platform/scroll/scrollbar_theme.h"
+
+namespace blink {
+
+using testing::AtLeast;
+using testing::Return;
+using testing::_;
+
+static double g_mocked_time = 0.0;
+
+static double GetMockedTime() {
+ return g_mocked_time;
+}
+
+namespace {
+
+class MockScrollableAreaForAnimatorTest
+ : public GarbageCollectedFinalized<MockScrollableAreaForAnimatorTest>,
+ public ScrollableArea {
+ USING_GARBAGE_COLLECTED_MIXIN(MockScrollableAreaForAnimatorTest);
+
+ public:
+ static MockScrollableAreaForAnimatorTest* Create(
+ bool scroll_animator_enabled,
+ const ScrollOffset& min_offset,
+ const ScrollOffset& max_offset) {
+ return new MockScrollableAreaForAnimatorTest(scroll_animator_enabled,
+ min_offset, max_offset);
+ }
+
+ MOCK_CONST_METHOD0(VisualRectForScrollbarParts, LayoutRect());
+ MOCK_CONST_METHOD0(IsActive, bool());
+ MOCK_CONST_METHOD1(ScrollSize, int(ScrollbarOrientation));
+ MOCK_CONST_METHOD0(IsScrollCornerVisible, bool());
+ MOCK_CONST_METHOD0(ScrollCornerRect, IntRect());
+ MOCK_METHOD2(UpdateScrollOffset, void(const ScrollOffset&, ScrollType));
+ MOCK_METHOD0(ScrollControlWasSetNeedsPaintInvalidation, void());
+ MOCK_CONST_METHOD0(EnclosingScrollableArea, ScrollableArea*());
+ MOCK_CONST_METHOD1(VisibleContentRect, IntRect(IncludeScrollbarsInRect));
+ MOCK_CONST_METHOD0(ContentsSize, IntSize());
+ MOCK_CONST_METHOD0(ScrollbarsCanBeActive, bool());
+ MOCK_CONST_METHOD0(ScrollableAreaBoundingBox, IntRect());
+ MOCK_METHOD0(RegisterForAnimation, void());
+ MOCK_METHOD0(ScheduleAnimation, bool());
+
+ bool UserInputScrollable(ScrollbarOrientation) const override { return true; }
+ bool ShouldPlaceVerticalScrollbarOnLeft() const override { return false; }
+ IntSize ScrollOffsetInt() const override { return IntSize(); }
+ int VisibleHeight() const override { return 768; }
+ int VisibleWidth() const override { return 1024; }
+ CompositorElementId GetCompositorElementId() const override {
+ return CompositorElementId();
+ }
+ bool ScrollAnimatorEnabled() const override {
+ return scroll_animator_enabled_;
+ }
+ int PageStep(ScrollbarOrientation) const override { return 0; }
+ IntSize MinimumScrollOffsetInt() const override {
+ return FlooredIntSize(min_offset_);
+ }
+ IntSize MaximumScrollOffsetInt() const override {
+ return FlooredIntSize(max_offset_);
+ }
+
+ void SetScrollAnimator(ScrollAnimator* scroll_animator) {
+ animator = scroll_animator;
+ }
+
+ ScrollOffset GetScrollOffset() const override {
+ if (animator)
+ return animator->CurrentOffset();
+ return ScrollableArea::GetScrollOffset();
+ }
+
+ void SetScrollOffset(const ScrollOffset& offset,
+ ScrollType type,
+ ScrollBehavior behavior = kScrollBehaviorInstant) {
+ if (animator)
+ animator->SetCurrentOffset(offset);
+ ScrollableArea::SetScrollOffset(offset, type, behavior);
+ }
+
+ scoped_refptr<base::SingleThreadTaskRunner> GetTimerTaskRunner() const final {
+ if (!timer_task_runner_) {
+ timer_task_runner_ =
+ blink::scheduler::GetSingleThreadTaskRunnerForTesting();
+ }
+ return timer_task_runner_;
+ }
+
+ ScrollbarTheme& GetPageScrollbarTheme() const override {
+ return ScrollbarTheme::DeprecatedStaticGetTheme();
+ }
+
+ virtual void Trace(blink::Visitor* visitor) {
+ visitor->Trace(animator);
+ ScrollableArea::Trace(visitor);
+ }
+
+ private:
+ explicit MockScrollableAreaForAnimatorTest(bool scroll_animator_enabled,
+ const ScrollOffset& min_offset,
+ const ScrollOffset& max_offset)
+ : scroll_animator_enabled_(scroll_animator_enabled),
+ min_offset_(min_offset),
+ max_offset_(max_offset) {}
+
+ bool scroll_animator_enabled_;
+ ScrollOffset min_offset_;
+ ScrollOffset max_offset_;
+ Member<ScrollAnimator> animator;
+ mutable scoped_refptr<base::SingleThreadTaskRunner> timer_task_runner_;
+};
+
+class TestScrollAnimator : public ScrollAnimator {
+ public:
+ TestScrollAnimator(ScrollableArea* scrollable_area,
+ WTF::TimeFunction timing_function)
+ : ScrollAnimator(scrollable_area, timing_function) {}
+ ~TestScrollAnimator() override = default;
+
+ void SetShouldSendToCompositor(bool send) {
+ should_send_to_compositor_ = send;
+ }
+
+ bool SendAnimationToCompositor() override {
+ if (should_send_to_compositor_) {
+ run_state_ =
+ ScrollAnimatorCompositorCoordinator::RunState::kRunningOnCompositor;
+ compositor_animation_id_ = 1;
+ return true;
+ }
+ return false;
+ }
+
+ protected:
+ void AbortAnimation() override {}
+
+ private:
+ bool should_send_to_compositor_ = false;
+};
+
+} // namespace
+
+static void Reset(ScrollAnimator& scroll_animator) {
+ scroll_animator.ScrollToOffsetWithoutAnimation(ScrollOffset());
+}
+
+// TODO(skobes): Add unit tests for composited scrolling paths.
+
+TEST(ScrollAnimatorTest, MainThreadStates) {
+ MockScrollableAreaForAnimatorTest* scrollable_area =
+ MockScrollableAreaForAnimatorTest::Create(true, ScrollOffset(),
+ ScrollOffset(1000, 1000));
+ ScrollAnimator* scroll_animator =
+ new ScrollAnimator(scrollable_area, GetMockedTime);
+
+ EXPECT_CALL(*scrollable_area, UpdateScrollOffset(_, _)).Times(2);
+ // Once from userScroll, once from updateCompositorAnimations.
+ EXPECT_CALL(*scrollable_area, RegisterForAnimation()).Times(2);
+ EXPECT_CALL(*scrollable_area, ScheduleAnimation())
+ .Times(AtLeast(1))
+ .WillRepeatedly(Return(true));
+
+ // Idle
+ EXPECT_FALSE(scroll_animator->HasAnimationThatRequiresService());
+ EXPECT_EQ(scroll_animator->run_state_,
+ ScrollAnimatorCompositorCoordinator::RunState::kIdle);
+
+ // WaitingToSendToCompositor
+ scroll_animator->UserScroll(kScrollByLine, FloatSize(10, 0));
+ EXPECT_EQ(scroll_animator->run_state_,
+ ScrollAnimatorCompositorCoordinator::RunState::
+ kWaitingToSendToCompositor);
+
+ // RunningOnMainThread
+ g_mocked_time += 0.05;
+ scroll_animator->UpdateCompositorAnimations();
+ EXPECT_EQ(
+ scroll_animator->run_state_,
+ ScrollAnimatorCompositorCoordinator::RunState::kRunningOnMainThread);
+ scroll_animator->TickAnimation(GetMockedTime());
+ EXPECT_EQ(
+ scroll_animator->run_state_,
+ ScrollAnimatorCompositorCoordinator::RunState::kRunningOnMainThread);
+
+ // PostAnimationCleanup
+ scroll_animator->CancelAnimation();
+ EXPECT_EQ(
+ scroll_animator->run_state_,
+ ScrollAnimatorCompositorCoordinator::RunState::kPostAnimationCleanup);
+
+ // Idle
+ scroll_animator->UpdateCompositorAnimations();
+ scroll_animator->TickAnimation(GetMockedTime());
+ EXPECT_EQ(scroll_animator->run_state_,
+ ScrollAnimatorCompositorCoordinator::RunState::kIdle);
+
+ Reset(*scroll_animator);
+
+ // Forced GC in order to finalize objects depending on the mock object.
+ ThreadState::Current()->CollectAllGarbage();
+}
+
+TEST(ScrollAnimatorTest, MainThreadEnabled) {
+ MockScrollableAreaForAnimatorTest* scrollable_area =
+ MockScrollableAreaForAnimatorTest::Create(true, ScrollOffset(),
+ ScrollOffset(1000, 1000));
+ ScrollAnimator* scroll_animator =
+ new ScrollAnimator(scrollable_area, GetMockedTime);
+
+ EXPECT_CALL(*scrollable_area, UpdateScrollOffset(_, _)).Times(9);
+ EXPECT_CALL(*scrollable_area, RegisterForAnimation()).Times(6);
+ EXPECT_CALL(*scrollable_area, ScheduleAnimation())
+ .Times(AtLeast(1))
+ .WillRepeatedly(Return(true));
+
+ EXPECT_FALSE(scroll_animator->HasAnimationThatRequiresService());
+
+ ScrollResult result =
+ scroll_animator->UserScroll(kScrollByLine, FloatSize(-100, 0));
+ EXPECT_FALSE(scroll_animator->HasAnimationThatRequiresService());
+ EXPECT_FALSE(result.did_scroll_x);
+ EXPECT_FLOAT_EQ(-100.0f, result.unused_scroll_delta_x);
+
+ result = scroll_animator->UserScroll(kScrollByLine, FloatSize(100, 0));
+ EXPECT_TRUE(scroll_animator->HasAnimationThatRequiresService());
+ EXPECT_TRUE(result.did_scroll_x);
+ EXPECT_FLOAT_EQ(0.0, result.unused_scroll_delta_x);
+
+ g_mocked_time += 0.05;
+ scroll_animator->UpdateCompositorAnimations();
+ scroll_animator->TickAnimation(GetMockedTime());
+
+ EXPECT_NE(100, scroll_animator->CurrentOffset().Width());
+ EXPECT_NE(0, scroll_animator->CurrentOffset().Width());
+ EXPECT_EQ(0, scroll_animator->CurrentOffset().Height());
+ Reset(*scroll_animator);
+
+ scroll_animator->UserScroll(kScrollByPage, FloatSize(100, 0));
+ EXPECT_TRUE(scroll_animator->HasAnimationThatRequiresService());
+
+ g_mocked_time += 0.05;
+ scroll_animator->UpdateCompositorAnimations();
+ scroll_animator->TickAnimation(GetMockedTime());
+
+ EXPECT_NE(100, scroll_animator->CurrentOffset().Width());
+ EXPECT_NE(0, scroll_animator->CurrentOffset().Width());
+ EXPECT_EQ(0, scroll_animator->CurrentOffset().Height());
+ Reset(*scroll_animator);
+
+ scroll_animator->UserScroll(kScrollByPixel, FloatSize(100, 0));
+ EXPECT_TRUE(scroll_animator->HasAnimationThatRequiresService());
+
+ g_mocked_time += 0.05;
+ scroll_animator->UpdateCompositorAnimations();
+ scroll_animator->TickAnimation(GetMockedTime());
+
+ EXPECT_NE(100, scroll_animator->CurrentOffset().Width());
+ EXPECT_NE(0, scroll_animator->CurrentOffset().Width());
+ EXPECT_EQ(0, scroll_animator->CurrentOffset().Height());
+
+ g_mocked_time += 1.0;
+ scroll_animator->UpdateCompositorAnimations();
+ scroll_animator->TickAnimation(GetMockedTime());
+
+ g_mocked_time += 0.05;
+ scroll_animator->UpdateCompositorAnimations();
+ EXPECT_FALSE(scroll_animator->HasAnimationThatRequiresService());
+ EXPECT_EQ(100, scroll_animator->CurrentOffset().Width());
+
+ Reset(*scroll_animator);
+
+ scroll_animator->UserScroll(kScrollByPrecisePixel, FloatSize(100, 0));
+ EXPECT_FALSE(scroll_animator->HasAnimationThatRequiresService());
+
+ EXPECT_EQ(100, scroll_animator->CurrentOffset().Width());
+ EXPECT_NE(0, scroll_animator->CurrentOffset().Width());
+ EXPECT_EQ(0, scroll_animator->CurrentOffset().Height());
+ Reset(*scroll_animator);
+}
+
+// Test that a smooth scroll offset animation is aborted when followed by a
+// non-smooth scroll offset animation.
+TEST(ScrollAnimatorTest, AnimatedScrollAborted) {
+ MockScrollableAreaForAnimatorTest* scrollable_area =
+ MockScrollableAreaForAnimatorTest::Create(true, ScrollOffset(),
+ ScrollOffset(1000, 1000));
+ ScrollAnimator* scroll_animator =
+ new ScrollAnimator(scrollable_area, GetMockedTime);
+
+ EXPECT_CALL(*scrollable_area, UpdateScrollOffset(_, _)).Times(3);
+ EXPECT_CALL(*scrollable_area, RegisterForAnimation()).Times(2);
+ EXPECT_CALL(*scrollable_area, ScheduleAnimation())
+ .Times(AtLeast(1))
+ .WillRepeatedly(Return(true));
+
+ EXPECT_FALSE(scroll_animator->HasAnimationThatRequiresService());
+
+ // Smooth scroll.
+ ScrollResult result =
+ scroll_animator->UserScroll(kScrollByLine, FloatSize(100, 0));
+ EXPECT_TRUE(scroll_animator->HasAnimationThatRequiresService());
+ EXPECT_TRUE(result.did_scroll_x);
+ EXPECT_FLOAT_EQ(0.0, result.unused_scroll_delta_x);
+ EXPECT_TRUE(scroll_animator->HasRunningAnimation());
+
+ g_mocked_time += 0.05;
+ scroll_animator->UpdateCompositorAnimations();
+ scroll_animator->TickAnimation(GetMockedTime());
+
+ EXPECT_NE(100, scroll_animator->CurrentOffset().Width());
+ EXPECT_NE(0, scroll_animator->CurrentOffset().Width());
+ EXPECT_EQ(0, scroll_animator->CurrentOffset().Height());
+
+ float x = scroll_animator->CurrentOffset().Width();
+
+ // Instant scroll.
+ result =
+ scroll_animator->UserScroll(kScrollByPrecisePixel, FloatSize(100, 0));
+ EXPECT_TRUE(result.did_scroll_x);
+ g_mocked_time += 0.05;
+ scroll_animator->UpdateCompositorAnimations();
+ EXPECT_FALSE(scroll_animator->HasRunningAnimation());
+ EXPECT_EQ(x + 100, scroll_animator->CurrentOffset().Width());
+ EXPECT_EQ(0, scroll_animator->CurrentOffset().Height());
+
+ Reset(*scroll_animator);
+}
+
+// Test that a smooth scroll offset animation running on the compositor is
+// completed on the main thread.
+TEST(ScrollAnimatorTest, AnimatedScrollTakeover) {
+ MockScrollableAreaForAnimatorTest* scrollable_area =
+ MockScrollableAreaForAnimatorTest::Create(true, ScrollOffset(),
+ ScrollOffset(1000, 1000));
+ TestScrollAnimator* scroll_animator =
+ new TestScrollAnimator(scrollable_area, GetMockedTime);
+
+ EXPECT_CALL(*scrollable_area, UpdateScrollOffset(_, _)).Times(2);
+ // Called from userScroll, updateCompositorAnimations, then
+ // takeOverCompositorAnimation (to re-register after RunningOnCompositor).
+ EXPECT_CALL(*scrollable_area, RegisterForAnimation()).Times(3);
+ EXPECT_CALL(*scrollable_area, ScheduleAnimation())
+ .Times(AtLeast(1))
+ .WillRepeatedly(Return(true));
+
+ EXPECT_FALSE(scroll_animator->HasAnimationThatRequiresService());
+
+ // Smooth scroll.
+ ScrollResult result =
+ scroll_animator->UserScroll(kScrollByLine, FloatSize(100, 0));
+ EXPECT_TRUE(scroll_animator->HasAnimationThatRequiresService());
+ EXPECT_TRUE(result.did_scroll_x);
+ EXPECT_FLOAT_EQ(0.0, result.unused_scroll_delta_x);
+ EXPECT_TRUE(scroll_animator->HasRunningAnimation());
+
+ // Update compositor animation.
+ g_mocked_time += 0.05;
+ scroll_animator->SetShouldSendToCompositor(true);
+ scroll_animator->UpdateCompositorAnimations();
+ EXPECT_EQ(
+ scroll_animator->run_state_,
+ ScrollAnimatorCompositorCoordinator::RunState::kRunningOnCompositor);
+
+ // Takeover.
+ scroll_animator->TakeOverCompositorAnimation();
+ EXPECT_EQ(scroll_animator->run_state_,
+ ScrollAnimatorCompositorCoordinator::RunState::
+ kRunningOnCompositorButNeedsTakeover);
+
+ // Animation should now be running on the main thread.
+ scroll_animator->SetShouldSendToCompositor(false);
+ scroll_animator->UpdateCompositorAnimations();
+ EXPECT_EQ(
+ scroll_animator->run_state_,
+ ScrollAnimatorCompositorCoordinator::RunState::kRunningOnMainThread);
+ scroll_animator->TickAnimation(GetMockedTime());
+ EXPECT_NE(100, scroll_animator->CurrentOffset().Width());
+ EXPECT_NE(0, scroll_animator->CurrentOffset().Width());
+ EXPECT_EQ(0, scroll_animator->CurrentOffset().Height());
+ Reset(*scroll_animator);
+}
+
+TEST(ScrollAnimatorTest, Disabled) {
+ MockScrollableAreaForAnimatorTest* scrollable_area =
+ MockScrollableAreaForAnimatorTest::Create(false, ScrollOffset(),
+ ScrollOffset(1000, 1000));
+ ScrollAnimator* scroll_animator =
+ new ScrollAnimator(scrollable_area, GetMockedTime);
+
+ EXPECT_CALL(*scrollable_area, UpdateScrollOffset(_, _)).Times(8);
+ EXPECT_CALL(*scrollable_area, RegisterForAnimation()).Times(0);
+
+ scroll_animator->UserScroll(kScrollByLine, FloatSize(100, 0));
+ EXPECT_EQ(100, scroll_animator->CurrentOffset().Width());
+ EXPECT_EQ(0, scroll_animator->CurrentOffset().Height());
+ Reset(*scroll_animator);
+
+ scroll_animator->UserScroll(kScrollByPage, FloatSize(100, 0));
+ EXPECT_EQ(100, scroll_animator->CurrentOffset().Width());
+ EXPECT_EQ(0, scroll_animator->CurrentOffset().Height());
+ Reset(*scroll_animator);
+
+ scroll_animator->UserScroll(kScrollByDocument, FloatSize(100, 0));
+ EXPECT_EQ(100, scroll_animator->CurrentOffset().Width());
+ EXPECT_EQ(0, scroll_animator->CurrentOffset().Height());
+ Reset(*scroll_animator);
+
+ scroll_animator->UserScroll(kScrollByPixel, FloatSize(100, 0));
+ EXPECT_EQ(100, scroll_animator->CurrentOffset().Width());
+ EXPECT_EQ(0, scroll_animator->CurrentOffset().Height());
+ Reset(*scroll_animator);
+}
+
+// Test that cancelling an animation resets the animation state.
+// See crbug.com/598548.
+TEST(ScrollAnimatorTest, CancellingAnimationResetsState) {
+ MockScrollableAreaForAnimatorTest* scrollable_area =
+ MockScrollableAreaForAnimatorTest::Create(true, ScrollOffset(),
+ ScrollOffset(1000, 1000));
+ ScrollAnimator* scroll_animator =
+ new ScrollAnimator(scrollable_area, GetMockedTime);
+
+ // Called from first userScroll, setCurrentOffset, and second userScroll.
+ EXPECT_CALL(*scrollable_area, UpdateScrollOffset(_, _)).Times(3);
+ // Called from userScroll, updateCompositorAnimations.
+ EXPECT_CALL(*scrollable_area, RegisterForAnimation()).Times(4);
+ EXPECT_CALL(*scrollable_area, ScheduleAnimation())
+ .Times(AtLeast(1))
+ .WillRepeatedly(Return(true));
+
+ EXPECT_EQ(0, scroll_animator->CurrentOffset().Width());
+ EXPECT_EQ(0, scroll_animator->CurrentOffset().Height());
+
+ // WaitingToSendToCompositor
+ scroll_animator->UserScroll(kScrollByLine, FloatSize(10, 0));
+ EXPECT_EQ(scroll_animator->run_state_,
+ ScrollAnimatorCompositorCoordinator::RunState::
+ kWaitingToSendToCompositor);
+
+ // RunningOnMainThread
+ g_mocked_time += 0.05;
+ scroll_animator->UpdateCompositorAnimations();
+ EXPECT_EQ(
+ scroll_animator->run_state_,
+ ScrollAnimatorCompositorCoordinator::RunState::kRunningOnMainThread);
+ scroll_animator->TickAnimation(GetMockedTime());
+ EXPECT_EQ(
+ scroll_animator->run_state_,
+ ScrollAnimatorCompositorCoordinator::RunState::kRunningOnMainThread);
+
+ // Amount scrolled so far.
+ float offset_x = scroll_animator->CurrentOffset().Width();
+
+ // Interrupt user scroll.
+ scroll_animator->CancelAnimation();
+ EXPECT_EQ(
+ scroll_animator->run_state_,
+ ScrollAnimatorCompositorCoordinator::RunState::kPostAnimationCleanup);
+
+ // Another userScroll after modified scroll offset.
+ scroll_animator->SetCurrentOffset(ScrollOffset(offset_x + 15, 0));
+ scroll_animator->UserScroll(kScrollByLine, FloatSize(10, 0));
+ EXPECT_EQ(scroll_animator->run_state_,
+ ScrollAnimatorCompositorCoordinator::RunState::
+ kWaitingToSendToCompositor);
+
+ // Finish scroll animation.
+ g_mocked_time += 1.0;
+ scroll_animator->UpdateCompositorAnimations();
+ scroll_animator->TickAnimation(GetMockedTime());
+ EXPECT_EQ(
+ scroll_animator->run_state_,
+ ScrollAnimatorCompositorCoordinator::RunState::kPostAnimationCleanup);
+
+ EXPECT_EQ(offset_x + 15 + 10, scroll_animator->CurrentOffset().Width());
+ EXPECT_EQ(0, scroll_animator->CurrentOffset().Height());
+ Reset(*scroll_animator);
+}
+
+// Test the behavior when in WaitingToCancelOnCompositor and a new user scroll
+// happens.
+TEST(ScrollAnimatorTest, CancellingCompositorAnimation) {
+ MockScrollableAreaForAnimatorTest* scrollable_area =
+ MockScrollableAreaForAnimatorTest::Create(true, ScrollOffset(),
+ ScrollOffset(1000, 1000));
+ TestScrollAnimator* scroll_animator =
+ new TestScrollAnimator(scrollable_area, GetMockedTime);
+
+ // Called when reset, not setting anywhere else.
+ EXPECT_CALL(*scrollable_area, UpdateScrollOffset(_, _)).Times(1);
+ // Called from userScroll, and first update.
+ EXPECT_CALL(*scrollable_area, RegisterForAnimation()).Times(4);
+ EXPECT_CALL(*scrollable_area, ScheduleAnimation())
+ .Times(AtLeast(1))
+ .WillRepeatedly(Return(true));
+
+ EXPECT_FALSE(scroll_animator->HasAnimationThatRequiresService());
+
+ // First user scroll.
+ ScrollResult result =
+ scroll_animator->UserScroll(kScrollByLine, FloatSize(100, 0));
+ EXPECT_TRUE(scroll_animator->HasAnimationThatRequiresService());
+ EXPECT_TRUE(result.did_scroll_x);
+ EXPECT_FLOAT_EQ(0.0, result.unused_scroll_delta_x);
+ EXPECT_TRUE(scroll_animator->HasRunningAnimation());
+ EXPECT_EQ(100, scroll_animator->DesiredTargetOffset().Width());
+ EXPECT_EQ(0, scroll_animator->DesiredTargetOffset().Height());
+
+ // Update compositor animation.
+ g_mocked_time += 0.05;
+ scroll_animator->SetShouldSendToCompositor(true);
+ scroll_animator->UpdateCompositorAnimations();
+ EXPECT_EQ(
+ scroll_animator->run_state_,
+ ScrollAnimatorCompositorCoordinator::RunState::kRunningOnCompositor);
+
+ // Cancel
+ scroll_animator->CancelAnimation();
+ EXPECT_EQ(scroll_animator->run_state_,
+ ScrollAnimatorCompositorCoordinator::RunState::
+ kWaitingToCancelOnCompositor);
+
+ // Unrelated scroll offset update.
+ scroll_animator->SetCurrentOffset(ScrollOffset(50, 0));
+
+ // Desired target offset should be that of the second scroll.
+ result = scroll_animator->UserScroll(kScrollByLine, FloatSize(100, 0));
+ EXPECT_TRUE(scroll_animator->HasAnimationThatRequiresService());
+ EXPECT_TRUE(result.did_scroll_x);
+ EXPECT_FLOAT_EQ(0.0, result.unused_scroll_delta_x);
+ EXPECT_EQ(scroll_animator->run_state_,
+ ScrollAnimatorCompositorCoordinator::RunState::
+ kWaitingToCancelOnCompositorButNewScroll);
+ EXPECT_EQ(150, scroll_animator->DesiredTargetOffset().Width());
+ EXPECT_EQ(0, scroll_animator->DesiredTargetOffset().Height());
+
+ // Update compositor animation.
+ g_mocked_time += 0.05;
+ scroll_animator->UpdateCompositorAnimations();
+ EXPECT_EQ(
+ scroll_animator->run_state_,
+ ScrollAnimatorCompositorCoordinator::RunState::kRunningOnCompositor);
+
+ // Third user scroll after compositor update updates the target.
+ result = scroll_animator->UserScroll(kScrollByLine, FloatSize(100, 0));
+ EXPECT_TRUE(scroll_animator->HasAnimationThatRequiresService());
+ EXPECT_TRUE(result.did_scroll_x);
+ EXPECT_FLOAT_EQ(0.0, result.unused_scroll_delta_x);
+ EXPECT_EQ(scroll_animator->run_state_,
+ ScrollAnimatorCompositorCoordinator::RunState::
+ kRunningOnCompositorButNeedsUpdate);
+ EXPECT_EQ(250, scroll_animator->DesiredTargetOffset().Width());
+ EXPECT_EQ(0, scroll_animator->DesiredTargetOffset().Height());
+ Reset(*scroll_animator);
+
+ // Forced GC in order to finalize objects depending on the mock object.
+ ThreadState::Current()->CollectAllGarbage();
+}
+
+// This test verifies that impl only animation updates get cleared once they
+// are pushed to compositor animation host.
+TEST(ScrollAnimatorTest, ImplOnlyAnimationUpdatesCleared) {
+ MockScrollableAreaForAnimatorTest* scrollable_area =
+ MockScrollableAreaForAnimatorTest::Create(true, ScrollOffset(),
+ ScrollOffset(1000, 1000));
+ TestScrollAnimator* animator =
+ new TestScrollAnimator(scrollable_area, GetMockedTime);
+
+ // From calls to adjust/takeoverImplOnlyScrollOffsetAnimation.
+ EXPECT_CALL(*scrollable_area, RegisterForAnimation()).Times(3);
+
+ // Verify that the adjustment update is cleared.
+ EXPECT_EQ(animator->run_state_,
+ ScrollAnimatorCompositorCoordinator::RunState::kIdle);
+ EXPECT_FALSE(animator->HasAnimationThatRequiresService());
+ EXPECT_TRUE(animator->ImplOnlyAnimationAdjustmentForTesting().IsZero());
+
+ animator->AdjustImplOnlyScrollOffsetAnimation(IntSize(100, 100));
+ animator->AdjustImplOnlyScrollOffsetAnimation(IntSize(10, -10));
+
+ EXPECT_TRUE(animator->HasAnimationThatRequiresService());
+ EXPECT_EQ(FloatSize(110, 90),
+ animator->ImplOnlyAnimationAdjustmentForTesting());
+
+ animator->UpdateCompositorAnimations();
+
+ EXPECT_EQ(animator->run_state_,
+ ScrollAnimatorCompositorCoordinator::RunState::kIdle);
+ EXPECT_FALSE(animator->HasAnimationThatRequiresService());
+ EXPECT_TRUE(animator->ImplOnlyAnimationAdjustmentForTesting().IsZero());
+
+ // Verify that the takeover update is cleared.
+ animator->TakeOverImplOnlyScrollOffsetAnimation();
+ EXPECT_FALSE(animator->HasAnimationThatRequiresService());
+
+ // Forced GC in order to finalize objects depending on the mock object.
+ ThreadState::Current()->CollectAllGarbage();
+}
+
+TEST(ScrollAnimatorTest, MainThreadAnimationTargetAdjustment) {
+ MockScrollableAreaForAnimatorTest* scrollable_area =
+ MockScrollableAreaForAnimatorTest::Create(true, ScrollOffset(-100, -100),
+ ScrollOffset(1000, 1000));
+ ScrollAnimator* animator = new ScrollAnimator(scrollable_area, GetMockedTime);
+ scrollable_area->SetScrollAnimator(animator);
+
+ // Twice from tickAnimation, once from reset, and twice from
+ // adjustAnimationAndSetScrollOffset.
+ EXPECT_CALL(*scrollable_area, UpdateScrollOffset(_, _)).Times(5);
+ // One from call to userScroll and one from updateCompositorAnimations.
+ EXPECT_CALL(*scrollable_area, RegisterForAnimation()).Times(2);
+ EXPECT_CALL(*scrollable_area, ScheduleAnimation())
+ .Times(AtLeast(1))
+ .WillRepeatedly(Return(true));
+
+ // Idle
+ EXPECT_FALSE(animator->HasAnimationThatRequiresService());
+ EXPECT_EQ(ScrollOffset(), animator->CurrentOffset());
+
+ // WaitingToSendToCompositor
+ animator->UserScroll(kScrollByLine, ScrollOffset(100, 100));
+
+ // RunningOnMainThread
+ g_mocked_time += 0.05;
+ animator->UpdateCompositorAnimations();
+ animator->TickAnimation(GetMockedTime());
+ ScrollOffset offset = animator->CurrentOffset();
+ EXPECT_EQ(ScrollOffset(100, 100), animator->DesiredTargetOffset());
+ EXPECT_GT(offset.Width(), 0);
+ EXPECT_GT(offset.Height(), 0);
+
+ // Adjustment
+ ScrollOffset new_offset = offset + ScrollOffset(10, -10);
+ animator->AdjustAnimationAndSetScrollOffset(new_offset, kAnchoringScroll);
+ EXPECT_EQ(ScrollOffset(110, 90), animator->DesiredTargetOffset());
+
+ // Adjusting after finished animation should do nothing.
+ g_mocked_time += 1.0;
+ animator->UpdateCompositorAnimations();
+ animator->TickAnimation(GetMockedTime());
+ EXPECT_EQ(
+ animator->RunStateForTesting(),
+ ScrollAnimatorCompositorCoordinator::RunState::kPostAnimationCleanup);
+ new_offset = animator->CurrentOffset() + ScrollOffset(10, -10);
+ animator->AdjustAnimationAndSetScrollOffset(new_offset, kAnchoringScroll);
+ EXPECT_EQ(
+ animator->RunStateForTesting(),
+ ScrollAnimatorCompositorCoordinator::RunState::kPostAnimationCleanup);
+ EXPECT_EQ(ScrollOffset(110, 90), animator->DesiredTargetOffset());
+
+ Reset(*animator);
+
+ // Forced GC in order to finalize objects depending on the mock object.
+ ThreadState::Current()->CollectAllGarbage();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scroll/scroll_customization.cc b/chromium/third_party/blink/renderer/platform/scroll/scroll_customization.cc
new file mode 100644
index 00000000000..203b5b63996
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/scroll_customization.cc
@@ -0,0 +1,35 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scroll/scroll_customization.h"
+
+namespace blink {
+namespace ScrollCustomization {
+
+ScrollDirection GetScrollDirectionFromDeltas(double delta_x, double delta_y) {
+ // TODO(ekaramad, tdresser): Find out the right value for kEpsilon here (see
+ // https://crbug.com/510550).
+ const double kEpsilon = 0.1f;
+
+ ScrollDirection direction = kScrollDirectionNone;
+ if (delta_x > kEpsilon)
+ direction |= kScrollDirectionPanRight;
+ if (delta_x < -kEpsilon)
+ direction |= kScrollDirectionPanLeft;
+ if (delta_y > kEpsilon)
+ direction |= kScrollDirectionPanDown;
+ if (delta_y < -kEpsilon)
+ direction |= kScrollDirectionPanUp;
+
+ if (!direction) {
+ // TODO(ekaramad, sahel): Remove this and perhaps replace with a DCHECK when
+ // issue https://crbug.com/728214 is fixed.
+ return kScrollDirectionAuto;
+ }
+
+ return direction;
+}
+
+} // namespace ScrollCustomization
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scroll/scroll_customization.h b/chromium/third_party/blink/renderer/platform/scroll/scroll_customization.h
new file mode 100644
index 00000000000..dfa89edb213
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/scroll_customization.h
@@ -0,0 +1,33 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLL_CUSTOMIZATION_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLL_CUSTOMIZATION_H_
+
+#include <stdint.h>
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+namespace ScrollCustomization {
+using ScrollDirection = uint8_t;
+
+constexpr ScrollDirection kScrollDirectionNone = 0;
+constexpr ScrollDirection kScrollDirectionPanLeft = 1 << 0;
+constexpr ScrollDirection kScrollDirectionPanRight = 1 << 1;
+constexpr ScrollDirection kScrollDirectionPanX =
+ kScrollDirectionPanLeft | kScrollDirectionPanRight;
+constexpr ScrollDirection kScrollDirectionPanUp = 1 << 2;
+constexpr ScrollDirection kScrollDirectionPanDown = 1 << 3;
+constexpr ScrollDirection kScrollDirectionPanY =
+ kScrollDirectionPanUp | kScrollDirectionPanDown;
+constexpr ScrollDirection kScrollDirectionAuto =
+ kScrollDirectionPanX | kScrollDirectionPanY;
+
+PLATFORM_EXPORT ScrollDirection GetScrollDirectionFromDeltas(double delta_x,
+ double delta_y);
+} // namespace ScrollCustomization
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLL_CUSTOMIZATION_H_
diff --git a/chromium/third_party/blink/renderer/platform/scroll/scroll_snap_data.h b/chromium/third_party/blink/renderer/platform/scroll/scroll_snap_data.h
new file mode 100644
index 00000000000..6cc9550b3ab
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/scroll_snap_data.h
@@ -0,0 +1,25 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLL_SNAP_DATA_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLL_SNAP_DATA_H_
+
+#include "cc/input/scroll_snap_data.h"
+
+// This file defines classes and structs used in SnapCoordinator.h
+
+namespace blink {
+
+using SnapAxis = cc::SnapAxis;
+using SearchAxis = cc::SearchAxis;
+using SnapStrictness = cc::SnapStrictness;
+using SnapAlignment = cc::SnapAlignment;
+using ScrollSnapType = cc::ScrollSnapType;
+using ScrollSnapAlign = cc::ScrollSnapAlign;
+using SnapAreaData = cc::SnapAreaData;
+using SnapContainerData = cc::SnapContainerData;
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLL_SNAP_DATA_H_
diff --git a/chromium/third_party/blink/renderer/platform/scroll/scroll_state_data.h b/chromium/third_party/blink/renderer/platform/scroll/scroll_state_data.h
new file mode 100644
index 00000000000..db10c3d0d83
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/scroll_state_data.h
@@ -0,0 +1,19 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLL_STATE_DATA_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLL_STATE_DATA_H_
+
+#include "cc/input/scroll_state.h"
+
+namespace blink {
+
+// A wrapper around cc's structure to expose it to core.
+struct ScrollStateData : public cc::ScrollStateData {
+ ScrollStateData() : cc::ScrollStateData() {}
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/scroll/scroll_types.h b/chromium/third_party/blink/renderer/platform/scroll/scroll_types.h
new file mode 100644
index 00000000000..db12b84734d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/scroll_types.h
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2006 Apple Computer, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLL_TYPES_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLL_TYPES_H_
+
+#include "third_party/blink/public/platform/web_gesture_event.h"
+#include "third_party/blink/renderer/platform/geometry/float_point.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+namespace blink {
+
+// A ScrollOffset represents an offset from the scroll origin of a
+// ScrollableArea. Note that "scroll origin" is not the same as the layout
+// concept of "location", nor is it necessarily coincident with the top/left of
+// the ScrollableArea's overflow rect. See core/layout/README.md for more
+// information.
+typedef FloatSize ScrollOffset;
+
+inline ScrollOffset ToScrollOffset(const FloatPoint& p) {
+ return ScrollOffset(p.X(), p.Y());
+}
+
+// Platform overlay scrollbars are controlled and painted by the operating
+// system (e.g., OSX and Android). CSS overlay scrollbars are created by
+// setting overflow:overlay, and they are painted by chromium.
+enum OverlayScrollbarClipBehavior {
+ kIgnorePlatformOverlayScrollbarSize,
+ kIgnorePlatformAndCSSOverlayScrollbarSize,
+ kExcludeOverlayScrollbarSizeForHitTesting
+};
+
+enum ScrollDirection {
+ kScrollUpIgnoringWritingMode,
+ kScrollDownIgnoringWritingMode,
+ kScrollLeftIgnoringWritingMode,
+ kScrollRightIgnoringWritingMode,
+
+ kScrollBlockDirectionBackward,
+ kScrollBlockDirectionForward,
+ kScrollInlineDirectionBackward,
+ kScrollInlineDirectionForward
+};
+
+enum ScrollDirectionPhysical {
+ kScrollUp,
+ kScrollDown,
+ kScrollLeft,
+ kScrollRight
+};
+
+enum ScrollType {
+ kUserScroll,
+ kProgrammaticScroll,
+ kClampingScroll,
+ kCompositorScroll,
+ kAnchoringScroll,
+ // These are programmatic sequenced scrolls from SmoothScrollSequencer.
+ // SetScrollOffset called with kSequencedScroll should not abort the smooth
+ // scroll sequence.
+ kSequencedScroll
+};
+
+// An explicit scroll is one that was requested by the user or the webpage.
+// An implicit scroll is a side effect of a layout change.
+inline bool IsExplicitScrollType(ScrollType scroll_type) {
+ return scroll_type == kUserScroll || scroll_type == kProgrammaticScroll ||
+ scroll_type == kCompositorScroll || scroll_type == kSequencedScroll;
+}
+
+// Convert logical scroll direction to physical. Physical scroll directions are
+// unaffected.
+inline ScrollDirectionPhysical ToPhysicalDirection(ScrollDirection direction,
+ bool is_vertical,
+ bool is_flipped) {
+ switch (direction) {
+ case kScrollBlockDirectionBackward: {
+ if (is_vertical) {
+ if (!is_flipped)
+ return kScrollUp;
+ return kScrollDown;
+ }
+ if (!is_flipped)
+ return kScrollLeft;
+ return kScrollRight;
+ }
+ case kScrollBlockDirectionForward: {
+ if (is_vertical) {
+ if (!is_flipped)
+ return kScrollDown;
+ return kScrollUp;
+ }
+ if (!is_flipped)
+ return kScrollRight;
+ return kScrollLeft;
+ }
+ case kScrollInlineDirectionBackward: {
+ if (is_vertical) {
+ if (!is_flipped)
+ return kScrollLeft;
+ return kScrollRight;
+ }
+ if (!is_flipped)
+ return kScrollUp;
+ return kScrollDown;
+ }
+ case kScrollInlineDirectionForward: {
+ if (is_vertical) {
+ if (!is_flipped)
+ return kScrollRight;
+ return kScrollLeft;
+ }
+ if (!is_flipped)
+ return kScrollDown;
+ return kScrollUp;
+ }
+ // Direction is already physical
+ case kScrollUpIgnoringWritingMode:
+ return kScrollUp;
+ case kScrollDownIgnoringWritingMode:
+ return kScrollDown;
+ case kScrollLeftIgnoringWritingMode:
+ return kScrollLeft;
+ case kScrollRightIgnoringWritingMode:
+ return kScrollRight;
+ default:
+ NOTREACHED();
+ break;
+ }
+ return kScrollUp;
+}
+
+inline ScrollDirection ToScrollDirection(ScrollDirectionPhysical direction) {
+ switch (direction) {
+ case kScrollUp:
+ return kScrollUpIgnoringWritingMode;
+ case kScrollDown:
+ return kScrollDownIgnoringWritingMode;
+ case kScrollLeft:
+ return kScrollLeftIgnoringWritingMode;
+ case kScrollRight:
+ return kScrollRightIgnoringWritingMode;
+ default:
+ NOTREACHED();
+ break;
+ }
+ return kScrollUpIgnoringWritingMode;
+}
+
+enum ScrollGranularity {
+ kScrollByLine,
+ kScrollByPage,
+ kScrollByDocument,
+ kScrollByPixel,
+ kScrollByPrecisePixel
+};
+
+enum ScrollInertialPhase {
+ kScrollInertialPhaseUnknown,
+ kScrollInertialPhaseNonMomentum,
+ kScrollInertialPhaseMomentum
+};
+
+enum ScrollbarOrientation { kHorizontalScrollbar, kVerticalScrollbar };
+
+enum ScrollOrientation { kHorizontalScroll, kVerticalScroll };
+
+enum ScrollbarMode { kScrollbarAuto, kScrollbarAlwaysOff, kScrollbarAlwaysOn };
+
+enum ScrollbarControlSize { kRegularScrollbar, kSmallScrollbar };
+
+typedef unsigned ScrollbarControlState;
+
+enum ScrollbarControlStateMask {
+ kActiveScrollbarState = 1,
+ kEnabledScrollbarState = 1 << 1,
+ kPressedScrollbarState = 1 << 2
+};
+
+enum ScrollbarPart {
+ kNoPart = 0,
+ kBackButtonStartPart = 1,
+ kForwardButtonStartPart = 1 << 1,
+ kBackTrackPart = 1 << 2,
+ kThumbPart = 1 << 3,
+ kForwardTrackPart = 1 << 4,
+ kBackButtonEndPart = 1 << 5,
+ kForwardButtonEndPart = 1 << 6,
+ kScrollbarBGPart = 1 << 7,
+ kTrackBGPart = 1 << 8,
+ kAllParts = 0xffffffff
+};
+
+enum ScrollbarOverlayColorTheme {
+ kScrollbarOverlayColorThemeDark,
+ kScrollbarOverlayColorThemeLight
+};
+
+enum ScrollBehavior {
+ kScrollBehaviorAuto,
+ kScrollBehaviorInstant,
+ kScrollBehaviorSmooth,
+};
+
+// The result of an attempt to scroll. If didScroll is true, then
+// unusedScrollDelta gives the amount of the scroll delta that was not consumed
+// by scrolling.
+struct ScrollResult {
+ STACK_ALLOCATED();
+ explicit ScrollResult()
+ : did_scroll_x(false),
+ did_scroll_y(false),
+ unused_scroll_delta_x(0),
+ unused_scroll_delta_y(0) {}
+ ScrollResult(bool did_scroll_x,
+ bool did_scroll_y,
+ float unused_scroll_delta_x,
+ float unused_scroll_delta_y)
+ : did_scroll_x(did_scroll_x),
+ did_scroll_y(did_scroll_y),
+ unused_scroll_delta_x(unused_scroll_delta_x),
+ unused_scroll_delta_y(unused_scroll_delta_y) {}
+
+ bool DidScroll() { return did_scroll_x || did_scroll_y; }
+
+ bool did_scroll_x;
+ bool did_scroll_y;
+
+ // In pixels.
+ float unused_scroll_delta_x;
+ float unused_scroll_delta_y;
+};
+
+inline ScrollOffset ToScrollDelta(ScrollbarOrientation orientation,
+ float delta) {
+ return orientation == kHorizontalScrollbar ? ScrollOffset(delta, 0.0f)
+ : ScrollOffset(0.0f, delta);
+}
+
+inline ScrollOffset ToScrollDelta(ScrollDirectionPhysical dir, float delta) {
+ if (dir == kScrollUp || dir == kScrollLeft)
+ delta = -delta;
+
+ return (dir == kScrollLeft || dir == kScrollRight) ? ScrollOffset(delta, 0)
+ : ScrollOffset(0, delta);
+}
+
+inline ScrollGranularity ToPlatformScrollGranularity(
+ WebGestureEvent::ScrollUnits units) {
+ switch (units) {
+ case WebGestureEvent::ScrollUnits::kPrecisePixels:
+ return ScrollGranularity::kScrollByPrecisePixel;
+ case WebGestureEvent::ScrollUnits::kPixels:
+ return ScrollGranularity::kScrollByPixel;
+ case WebGestureEvent::ScrollUnits::kPage:
+ return ScrollGranularity::kScrollByPage;
+ default:
+ NOTREACHED();
+ return ScrollGranularity::kScrollByPrecisePixel;
+ }
+}
+
+inline ScrollOffset ScrollPositionToOffset(FloatPoint position,
+ FloatPoint origin) {
+ return position - origin;
+}
+
+inline FloatPoint ScrollOffsetToPosition(ScrollOffset offset,
+ FloatPoint origin) {
+ return origin + offset;
+}
+
+typedef unsigned ScrollbarControlPartMask;
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/scroll/scrollable_area.cc b/chromium/third_party/blink/renderer/platform/scroll/scrollable_area.cc
new file mode 100644
index 00000000000..c1ca05cb131
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/scrollable_area.cc
@@ -0,0 +1,726 @@
+/*
+ * Copyright (c) 2010, Google Inc. All rights reserved.
+ * Copyright (C) 2008, 2011 Apple 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 "third_party/blink/renderer/platform/scroll/scrollable_area.h"
+
+#include "build/build_config.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/platform_chrome_client.h"
+#include "third_party/blink/renderer/platform/scheduler/child/web_scheduler.h"
+#include "third_party/blink/renderer/platform/scroll/main_thread_scrolling_reason.h"
+#include "third_party/blink/renderer/platform/scroll/programmatic_scroll_animator.h"
+#include "third_party/blink/renderer/platform/scroll/scrollbar_theme.h"
+#include "third_party/blink/renderer/platform/scroll/smooth_scroll_sequencer.h"
+
+static const int kPixelsPerLineStep = 40;
+static const float kMinFractionToStepWhenPaging = 0.875f;
+
+namespace blink {
+
+int ScrollableArea::PixelsPerLineStep(PlatformChromeClient* host) {
+ if (!host)
+ return kPixelsPerLineStep;
+ return host->WindowToViewportScalar(kPixelsPerLineStep);
+}
+
+float ScrollableArea::MinFractionToStepWhenPaging() {
+ return kMinFractionToStepWhenPaging;
+}
+
+int ScrollableArea::MaxOverlapBetweenPages() const {
+ return GetPageScrollbarTheme().MaxOverlapBetweenPages();
+}
+
+// static
+ScrollBehavior ScrollableArea::DetermineScrollBehavior(
+ ScrollBehavior behavior_from_param,
+ ScrollBehavior behavior_from_style) {
+ if (behavior_from_param == kScrollBehaviorSmooth)
+ return kScrollBehaviorSmooth;
+
+ if (behavior_from_param == kScrollBehaviorAuto &&
+ behavior_from_style == kScrollBehaviorSmooth) {
+ return kScrollBehaviorSmooth;
+ }
+
+ return kScrollBehaviorInstant;
+}
+
+ScrollableArea::ScrollableArea()
+ : autosize_vertical_scrollbar_mode_(kScrollbarAuto),
+ autosize_horizontal_scrollbar_mode_(kScrollbarAuto),
+ scrollbar_overlay_color_theme_(kScrollbarOverlayColorThemeDark),
+ scroll_origin_changed_(false),
+ horizontal_scrollbar_needs_paint_invalidation_(false),
+ vertical_scrollbar_needs_paint_invalidation_(false),
+ scroll_corner_needs_paint_invalidation_(false),
+ scrollbars_hidden_if_overlay_(true),
+ scrollbar_captured_(false),
+ mouse_over_scrollbar_(false),
+ needs_show_scrollbar_layers_(false),
+ uses_composited_scrolling_(false) {}
+
+ScrollableArea::~ScrollableArea() = default;
+
+void ScrollableArea::ClearScrollableArea() {
+#if defined(OS_MACOSX)
+ if (scroll_animator_)
+ scroll_animator_->Dispose();
+#endif
+ scroll_animator_.Clear();
+ programmatic_scroll_animator_.Clear();
+ if (fade_overlay_scrollbars_timer_)
+ fade_overlay_scrollbars_timer_->Stop();
+}
+
+ScrollAnimatorBase& ScrollableArea::GetScrollAnimator() const {
+ if (!scroll_animator_)
+ scroll_animator_ =
+ ScrollAnimatorBase::Create(const_cast<ScrollableArea*>(this));
+
+ return *scroll_animator_;
+}
+
+ProgrammaticScrollAnimator& ScrollableArea::GetProgrammaticScrollAnimator()
+ const {
+ if (!programmatic_scroll_animator_)
+ programmatic_scroll_animator_ =
+ ProgrammaticScrollAnimator::Create(const_cast<ScrollableArea*>(this));
+
+ return *programmatic_scroll_animator_;
+}
+
+void ScrollableArea::SetScrollOrigin(const IntPoint& origin) {
+ if (scroll_origin_ != origin) {
+ scroll_origin_ = origin;
+ scroll_origin_changed_ = true;
+ }
+}
+
+GraphicsLayer* ScrollableArea::LayerForContainer() const {
+ return LayerForScrolling() ? LayerForScrolling()->Parent() : nullptr;
+}
+
+ScrollbarOrientation ScrollableArea::ScrollbarOrientationFromDirection(
+ ScrollDirectionPhysical direction) const {
+ return (direction == kScrollUp || direction == kScrollDown)
+ ? kVerticalScrollbar
+ : kHorizontalScrollbar;
+}
+
+float ScrollableArea::ScrollStep(ScrollGranularity granularity,
+ ScrollbarOrientation orientation) const {
+ switch (granularity) {
+ case kScrollByLine:
+ return LineStep(orientation);
+ case kScrollByPage:
+ return PageStep(orientation);
+ case kScrollByDocument:
+ return DocumentStep(orientation);
+ case kScrollByPixel:
+ case kScrollByPrecisePixel:
+ return PixelStep(orientation);
+ default:
+ NOTREACHED();
+ return 0.0f;
+ }
+}
+
+ScrollResult ScrollableArea::UserScroll(ScrollGranularity granularity,
+ const ScrollOffset& delta) {
+ float step_x = ScrollStep(granularity, kHorizontalScrollbar);
+ float step_y = ScrollStep(granularity, kVerticalScrollbar);
+
+ ScrollOffset pixel_delta(delta);
+ pixel_delta.Scale(step_x, step_y);
+
+ ScrollOffset scrollable_axis_delta(
+ UserInputScrollable(kHorizontalScrollbar) ? pixel_delta.Width() : 0,
+ UserInputScrollable(kVerticalScrollbar) ? pixel_delta.Height() : 0);
+
+ if (scrollable_axis_delta.IsZero()) {
+ return ScrollResult(false, false, pixel_delta.Width(),
+ pixel_delta.Height());
+ }
+
+ CancelProgrammaticScrollAnimation();
+ if (SmoothScrollSequencer* sequencer = GetSmoothScrollSequencer())
+ sequencer->AbortAnimations();
+
+ ScrollResult result =
+ GetScrollAnimator().UserScroll(granularity, pixel_delta);
+
+ // Delta that wasn't scrolled because the axis is !userInputScrollable
+ // should count as unusedScrollDelta.
+ ScrollOffset unscrollable_axis_delta = pixel_delta - scrollable_axis_delta;
+ result.unused_scroll_delta_x += unscrollable_axis_delta.Width();
+ result.unused_scroll_delta_y += unscrollable_axis_delta.Height();
+
+ return result;
+}
+
+void ScrollableArea::SetScrollOffset(const ScrollOffset& offset,
+ ScrollType scroll_type,
+ ScrollBehavior behavior) {
+ if (scroll_type != kSequencedScroll && scroll_type != kClampingScroll &&
+ scroll_type != kAnchoringScroll) {
+ if (SmoothScrollSequencer* sequencer = GetSmoothScrollSequencer())
+ sequencer->AbortAnimations();
+ }
+
+ ScrollOffset clamped_offset = ClampScrollOffset(offset);
+ if (clamped_offset == GetScrollOffset())
+ return;
+
+ if (behavior == kScrollBehaviorAuto)
+ behavior = ScrollBehaviorStyle();
+
+ switch (scroll_type) {
+ case kCompositorScroll:
+ case kClampingScroll:
+ ScrollOffsetChanged(clamped_offset, scroll_type);
+ break;
+ case kAnchoringScroll:
+ GetScrollAnimator().AdjustAnimationAndSetScrollOffset(clamped_offset,
+ scroll_type);
+ break;
+ case kProgrammaticScroll:
+ ProgrammaticScrollHelper(clamped_offset, behavior, false);
+ break;
+ case kSequencedScroll:
+ ProgrammaticScrollHelper(clamped_offset, behavior, true);
+ break;
+ case kUserScroll:
+ UserScrollHelper(clamped_offset, behavior);
+ break;
+ default:
+ NOTREACHED();
+ }
+}
+
+void ScrollableArea::ScrollBy(const ScrollOffset& delta,
+ ScrollType type,
+ ScrollBehavior behavior) {
+ SetScrollOffset(GetScrollOffset() + delta, type, behavior);
+}
+
+void ScrollableArea::SetScrollOffsetSingleAxis(ScrollbarOrientation orientation,
+ float offset,
+ ScrollType scroll_type,
+ ScrollBehavior behavior) {
+ ScrollOffset new_offset;
+ if (orientation == kHorizontalScrollbar)
+ new_offset =
+ ScrollOffset(offset, GetScrollAnimator().CurrentOffset().Height());
+ else
+ new_offset =
+ ScrollOffset(GetScrollAnimator().CurrentOffset().Width(), offset);
+
+ // TODO(bokan): Note, this doesn't use the derived class versions since this
+ // method is currently used exclusively by code that adjusts the position by
+ // the scroll origin and the derived class versions differ on whether they
+ // take that into account or not.
+ ScrollableArea::SetScrollOffset(new_offset, scroll_type, behavior);
+}
+
+void ScrollableArea::ProgrammaticScrollHelper(const ScrollOffset& offset,
+ ScrollBehavior scroll_behavior,
+ bool is_sequenced_scroll) {
+ CancelScrollAnimation();
+
+ if (scroll_behavior == kScrollBehaviorSmooth) {
+ GetProgrammaticScrollAnimator().AnimateToOffset(offset,
+ is_sequenced_scroll);
+ } else {
+ GetProgrammaticScrollAnimator().ScrollToOffsetWithoutAnimation(
+ offset, is_sequenced_scroll);
+ }
+}
+
+void ScrollableArea::UserScrollHelper(const ScrollOffset& offset,
+ ScrollBehavior scroll_behavior) {
+ CancelProgrammaticScrollAnimation();
+ if (SmoothScrollSequencer* sequencer = GetSmoothScrollSequencer())
+ sequencer->AbortAnimations();
+
+ float x = UserInputScrollable(kHorizontalScrollbar)
+ ? offset.Width()
+ : GetScrollAnimator().CurrentOffset().Width();
+ float y = UserInputScrollable(kVerticalScrollbar)
+ ? offset.Height()
+ : GetScrollAnimator().CurrentOffset().Height();
+
+ // Smooth user scrolls (keyboard, wheel clicks) are handled via the userScroll
+ // method.
+ // TODO(bokan): The userScroll method should probably be modified to call this
+ // method and ScrollAnimatorBase to have a simpler
+ // animateToOffset method like the ProgrammaticScrollAnimator.
+ DCHECK_EQ(scroll_behavior, kScrollBehaviorInstant);
+ GetScrollAnimator().ScrollToOffsetWithoutAnimation(ScrollOffset(x, y));
+}
+
+LayoutRect ScrollableArea::ScrollIntoView(
+ const LayoutRect& rect_in_absolute,
+ const WebScrollIntoViewParams& params) {
+ // TODO(bokan): This should really be implemented here but ScrollAlignment is
+ // in Core which is a dependency violation.
+ NOTREACHED();
+ return LayoutRect();
+}
+
+void ScrollableArea::ScrollOffsetChanged(const ScrollOffset& offset,
+ ScrollType scroll_type) {
+ TRACE_EVENT0("blink", "ScrollableArea::scrollOffsetChanged");
+
+ ScrollOffset old_offset = GetScrollOffset();
+ ScrollOffset truncated_offset = ShouldUseIntegerScrollOffset()
+ ? ScrollOffset(FlooredIntSize(offset))
+ : offset;
+
+ // Tell the derived class to scroll its contents.
+ UpdateScrollOffset(truncated_offset, scroll_type);
+
+ // If the layout object has been detached as a result of updating the scroll
+ // this object will be cleaned up shortly.
+ if (HasBeenDisposed())
+ return;
+
+ // Tell the scrollbars to update their thumb postions.
+ // If the scrollbar does not have its own layer, it must always be
+ // invalidated to reflect the new thumb offset, even if the theme did not
+ // invalidate any individual part.
+ if (Scrollbar* horizontal_scrollbar = this->HorizontalScrollbar())
+ horizontal_scrollbar->OffsetDidChange();
+ if (Scrollbar* vertical_scrollbar = this->VerticalScrollbar())
+ vertical_scrollbar->OffsetDidChange();
+
+ if (GetScrollOffset() != old_offset) {
+ GetScrollAnimator().NotifyContentAreaScrolled(
+ GetScrollOffset() - old_offset, scroll_type);
+ }
+
+ GetScrollAnimator().SetCurrentOffset(offset);
+}
+
+bool ScrollableArea::ScrollBehaviorFromString(const String& behavior_string,
+ ScrollBehavior& behavior) {
+ if (behavior_string == "auto")
+ behavior = kScrollBehaviorAuto;
+ else if (behavior_string == "instant")
+ behavior = kScrollBehaviorInstant;
+ else if (behavior_string == "smooth")
+ behavior = kScrollBehaviorSmooth;
+ else
+ return false;
+
+ return true;
+}
+
+// NOTE: Only called from Internals for testing.
+void ScrollableArea::UpdateScrollOffsetFromInternals(const IntSize& offset) {
+ ScrollOffsetChanged(ScrollOffset(offset), kProgrammaticScroll);
+}
+
+void ScrollableArea::ContentAreaWillPaint() const {
+ if (ScrollAnimatorBase* scroll_animator = ExistingScrollAnimator())
+ scroll_animator->ContentAreaWillPaint();
+}
+
+void ScrollableArea::MouseEnteredContentArea() const {
+ if (ScrollAnimatorBase* scroll_animator = ExistingScrollAnimator())
+ scroll_animator->MouseEnteredContentArea();
+}
+
+void ScrollableArea::MouseExitedContentArea() const {
+ if (ScrollAnimatorBase* scroll_animator = ExistingScrollAnimator())
+ scroll_animator->MouseEnteredContentArea();
+}
+
+void ScrollableArea::MouseMovedInContentArea() const {
+ if (ScrollAnimatorBase* scroll_animator = ExistingScrollAnimator())
+ scroll_animator->MouseMovedInContentArea();
+}
+
+void ScrollableArea::MouseEnteredScrollbar(Scrollbar& scrollbar) {
+ mouse_over_scrollbar_ = true;
+ GetScrollAnimator().MouseEnteredScrollbar(scrollbar);
+ ShowOverlayScrollbars();
+ if (fade_overlay_scrollbars_timer_)
+ fade_overlay_scrollbars_timer_->Stop();
+}
+
+void ScrollableArea::MouseExitedScrollbar(Scrollbar& scrollbar) {
+ mouse_over_scrollbar_ = false;
+ GetScrollAnimator().MouseExitedScrollbar(scrollbar);
+ if (HasOverlayScrollbars() && !scrollbars_hidden_if_overlay_) {
+ // This will kick off the fade out timer.
+ ShowOverlayScrollbars();
+ }
+}
+
+void ScrollableArea::MouseCapturedScrollbar() {
+ scrollbar_captured_ = true;
+ ShowOverlayScrollbars();
+ if (fade_overlay_scrollbars_timer_)
+ fade_overlay_scrollbars_timer_->Stop();
+}
+
+void ScrollableArea::MouseReleasedScrollbar(ScrollbarOrientation orientation) {
+ scrollbar_captured_ = false;
+ // This will kick off the fade out timer.
+ ShowOverlayScrollbars();
+ SnapAfterScrollbarDragging(orientation);
+}
+
+void ScrollableArea::ContentAreaDidShow() const {
+ if (ScrollAnimatorBase* scroll_animator = ExistingScrollAnimator())
+ scroll_animator->ContentAreaDidShow();
+}
+
+void ScrollableArea::ContentAreaDidHide() const {
+ if (ScrollAnimatorBase* scroll_animator = ExistingScrollAnimator())
+ scroll_animator->ContentAreaDidHide();
+}
+
+void ScrollableArea::FinishCurrentScrollAnimations() const {
+ if (ScrollAnimatorBase* scroll_animator = ExistingScrollAnimator())
+ scroll_animator->FinishCurrentScrollAnimations();
+}
+
+void ScrollableArea::DidAddScrollbar(Scrollbar& scrollbar,
+ ScrollbarOrientation orientation) {
+ if (orientation == kVerticalScrollbar)
+ GetScrollAnimator().DidAddVerticalScrollbar(scrollbar);
+ else
+ GetScrollAnimator().DidAddHorizontalScrollbar(scrollbar);
+
+ // <rdar://problem/9797253> AppKit resets the scrollbar's style when you
+ // attach a scrollbar
+ SetScrollbarOverlayColorTheme(GetScrollbarOverlayColorTheme());
+}
+
+void ScrollableArea::WillRemoveScrollbar(Scrollbar& scrollbar,
+ ScrollbarOrientation orientation) {
+ if (ScrollAnimatorBase* scroll_animator = ExistingScrollAnimator()) {
+ if (orientation == kVerticalScrollbar)
+ scroll_animator->WillRemoveVerticalScrollbar(scrollbar);
+ else
+ scroll_animator->WillRemoveHorizontalScrollbar(scrollbar);
+ }
+}
+
+void ScrollableArea::ContentsResized() {
+ if (ScrollAnimatorBase* scroll_animator = ExistingScrollAnimator())
+ scroll_animator->ContentsResized();
+}
+
+bool ScrollableArea::HasOverlayScrollbars() const {
+ Scrollbar* v_scrollbar = VerticalScrollbar();
+ if (v_scrollbar && v_scrollbar->IsOverlayScrollbar())
+ return true;
+ Scrollbar* h_scrollbar = HorizontalScrollbar();
+ return h_scrollbar && h_scrollbar->IsOverlayScrollbar();
+}
+
+void ScrollableArea::SetScrollbarOverlayColorTheme(
+ ScrollbarOverlayColorTheme overlay_theme) {
+ scrollbar_overlay_color_theme_ = overlay_theme;
+
+ if (Scrollbar* scrollbar = HorizontalScrollbar()) {
+ GetPageScrollbarTheme().UpdateScrollbarOverlayColorTheme(*scrollbar);
+ scrollbar->SetNeedsPaintInvalidation(kAllParts);
+ }
+
+ if (Scrollbar* scrollbar = VerticalScrollbar()) {
+ GetPageScrollbarTheme().UpdateScrollbarOverlayColorTheme(*scrollbar);
+ scrollbar->SetNeedsPaintInvalidation(kAllParts);
+ }
+}
+
+void ScrollableArea::RecalculateScrollbarOverlayColorTheme(
+ Color background_color) {
+ ScrollbarOverlayColorTheme old_overlay_theme =
+ GetScrollbarOverlayColorTheme();
+ ScrollbarOverlayColorTheme overlay_theme = kScrollbarOverlayColorThemeDark;
+
+ // Reduce the background color from RGB to a lightness value
+ // and determine which scrollbar style to use based on a lightness
+ // heuristic.
+ double hue, saturation, lightness;
+ background_color.GetHSL(hue, saturation, lightness);
+ if (lightness <= .5 && background_color.Alpha())
+ overlay_theme = kScrollbarOverlayColorThemeLight;
+
+ if (old_overlay_theme != overlay_theme)
+ SetScrollbarOverlayColorTheme(overlay_theme);
+}
+
+void ScrollableArea::SetScrollbarNeedsPaintInvalidation(
+ ScrollbarOrientation orientation) {
+ if (orientation == kHorizontalScrollbar) {
+ if (GraphicsLayer* graphics_layer = LayerForHorizontalScrollbar()) {
+ graphics_layer->SetNeedsDisplay();
+ graphics_layer->SetContentsNeedsDisplay();
+ }
+ horizontal_scrollbar_needs_paint_invalidation_ = true;
+ } else {
+ if (GraphicsLayer* graphics_layer = LayerForVerticalScrollbar()) {
+ graphics_layer->SetNeedsDisplay();
+ graphics_layer->SetContentsNeedsDisplay();
+ }
+ vertical_scrollbar_needs_paint_invalidation_ = true;
+ }
+
+ ScrollControlWasSetNeedsPaintInvalidation();
+}
+
+void ScrollableArea::SetScrollCornerNeedsPaintInvalidation() {
+ if (GraphicsLayer* graphics_layer = LayerForScrollCorner()) {
+ graphics_layer->SetNeedsDisplay();
+ return;
+ }
+ scroll_corner_needs_paint_invalidation_ = true;
+ ScrollControlWasSetNeedsPaintInvalidation();
+}
+
+bool ScrollableArea::HasLayerForHorizontalScrollbar() const {
+ return LayerForHorizontalScrollbar();
+}
+
+bool ScrollableArea::HasLayerForVerticalScrollbar() const {
+ return LayerForVerticalScrollbar();
+}
+
+bool ScrollableArea::HasLayerForScrollCorner() const {
+ return LayerForScrollCorner();
+}
+
+void ScrollableArea::LayerForScrollingDidChange(
+ CompositorAnimationTimeline* timeline) {
+ if (ProgrammaticScrollAnimator* programmatic_scroll_animator =
+ ExistingProgrammaticScrollAnimator())
+ programmatic_scroll_animator->LayerForCompositedScrollingDidChange(
+ timeline);
+ if (ScrollAnimatorBase* scroll_animator = ExistingScrollAnimator())
+ scroll_animator->LayerForCompositedScrollingDidChange(timeline);
+}
+
+void ScrollableArea::ServiceScrollAnimations(double monotonic_time) {
+ bool requires_animation_service = false;
+ if (ScrollAnimatorBase* scroll_animator = ExistingScrollAnimator()) {
+ scroll_animator->TickAnimation(monotonic_time);
+ if (scroll_animator->HasAnimationThatRequiresService())
+ requires_animation_service = true;
+ }
+ if (ProgrammaticScrollAnimator* programmatic_scroll_animator =
+ ExistingProgrammaticScrollAnimator()) {
+ programmatic_scroll_animator->TickAnimation(monotonic_time);
+ if (programmatic_scroll_animator->HasAnimationThatRequiresService())
+ requires_animation_service = true;
+ }
+ if (!requires_animation_service)
+ DeregisterForAnimation();
+}
+
+void ScrollableArea::UpdateCompositorScrollAnimations() {
+ if (ProgrammaticScrollAnimator* programmatic_scroll_animator =
+ ExistingProgrammaticScrollAnimator())
+ programmatic_scroll_animator->UpdateCompositorAnimations();
+
+ if (ScrollAnimatorBase* scroll_animator = ExistingScrollAnimator())
+ scroll_animator->UpdateCompositorAnimations();
+}
+
+void ScrollableArea::CancelScrollAnimation() {
+ if (ScrollAnimatorBase* scroll_animator = ExistingScrollAnimator())
+ scroll_animator->CancelAnimation();
+}
+
+void ScrollableArea::CancelProgrammaticScrollAnimation() {
+ if (ProgrammaticScrollAnimator* programmatic_scroll_animator =
+ ExistingProgrammaticScrollAnimator())
+ programmatic_scroll_animator->CancelAnimation();
+}
+
+bool ScrollableArea::ShouldScrollOnMainThread() const {
+ if (GraphicsLayer* layer = LayerForScrolling()) {
+ uint32_t reasons = layer->PlatformLayer()->MainThreadScrollingReasons();
+ // Should scroll on main thread unless the reason is the one that is set
+ // by the ScrollAnimator, in which case, the animation can still be
+ // scheduled on the compositor.
+ // TODO(ymalik): We have a non-transient "main thread scrolling reason"
+ // that doesn't actually cause shouldScrollOnMainThread() to be true.
+ // This is confusing and should be cleaned up.
+ return !!(reasons &
+ ~MainThreadScrollingReason::kHandlingScrollFromMainThread);
+ }
+ return true;
+}
+
+bool ScrollableArea::ScrollbarsHiddenIfOverlay() const {
+ return HasOverlayScrollbars() && scrollbars_hidden_if_overlay_;
+}
+
+void ScrollableArea::SetScrollbarsHiddenIfOverlay(bool hidden) {
+ // If scrollable area has been disposed, we can not get the page scrollbar
+ // theme setting. Should early return here.
+ if (HasBeenDisposed())
+ return;
+
+ if (!GetPageScrollbarTheme().UsesOverlayScrollbars())
+ return;
+
+ if (scrollbars_hidden_if_overlay_ == static_cast<unsigned>(hidden))
+ return;
+
+ scrollbars_hidden_if_overlay_ = hidden;
+ ScrollbarVisibilityChanged();
+}
+
+void ScrollableArea::FadeOverlayScrollbarsTimerFired(TimerBase*) {
+ SetScrollbarsHiddenIfOverlay(true);
+}
+
+void ScrollableArea::ShowOverlayScrollbars() {
+ if (!GetPageScrollbarTheme().UsesOverlayScrollbars())
+ return;
+
+ SetScrollbarsHiddenIfOverlay(false);
+ needs_show_scrollbar_layers_ = true;
+
+ const double time_until_disable =
+ GetPageScrollbarTheme().OverlayScrollbarFadeOutDelaySeconds() +
+ GetPageScrollbarTheme().OverlayScrollbarFadeOutDurationSeconds();
+
+ // If the overlay scrollbars don't fade out, don't do anything. This is the
+ // case for the mock overlays used in tests and on Mac, where the fade-out is
+ // animated in ScrollAnimatorMac.
+ // We also don't fade out overlay scrollbar for popup since we don't create
+ // compositor for popup and thus they don't appear on hover so users without
+ // a wheel can't scroll if they fade out.
+ if (!time_until_disable || GetChromeClient()->IsPopup())
+ return;
+
+ if (!fade_overlay_scrollbars_timer_) {
+ fade_overlay_scrollbars_timer_.reset(new TaskRunnerTimer<ScrollableArea>(
+ Platform::Current()->MainThread()->Scheduler()->CompositorTaskRunner(),
+ this, &ScrollableArea::FadeOverlayScrollbarsTimerFired));
+ }
+
+ if (!scrollbar_captured_ && !mouse_over_scrollbar_) {
+ fade_overlay_scrollbars_timer_->StartOneShot(time_until_disable, FROM_HERE);
+ }
+}
+
+IntSize ScrollableArea::ClampScrollOffset(const IntSize& scroll_offset) const {
+ return scroll_offset.ShrunkTo(MaximumScrollOffsetInt())
+ .ExpandedTo(MinimumScrollOffsetInt());
+}
+
+ScrollOffset ScrollableArea::ClampScrollOffset(
+ const ScrollOffset& scroll_offset) const {
+ return scroll_offset.ShrunkTo(MaximumScrollOffset())
+ .ExpandedTo(MinimumScrollOffset());
+}
+
+int ScrollableArea::LineStep(ScrollbarOrientation) const {
+ return PixelsPerLineStep(GetChromeClient());
+}
+
+int ScrollableArea::PageStep(ScrollbarOrientation orientation) const {
+ IntRect visible_rect = VisibleContentRect(kIncludeScrollbars);
+ int length = (orientation == kHorizontalScrollbar) ? visible_rect.Width()
+ : visible_rect.Height();
+ int min_page_step =
+ static_cast<float>(length) * MinFractionToStepWhenPaging();
+ int page_step = std::max(min_page_step, length - MaxOverlapBetweenPages());
+
+ return std::max(page_step, 1);
+}
+
+int ScrollableArea::DocumentStep(ScrollbarOrientation orientation) const {
+ return ScrollSize(orientation);
+}
+
+float ScrollableArea::PixelStep(ScrollbarOrientation) const {
+ return 1;
+}
+
+int ScrollableArea::VerticalScrollbarWidth(
+ OverlayScrollbarClipBehavior behavior) const {
+ DCHECK_EQ(behavior, kIgnorePlatformOverlayScrollbarSize);
+ if (Scrollbar* vertical_bar = VerticalScrollbar())
+ return !vertical_bar->IsOverlayScrollbar() ? vertical_bar->Width() : 0;
+ return 0;
+}
+
+int ScrollableArea::HorizontalScrollbarHeight(
+ OverlayScrollbarClipBehavior behavior) const {
+ DCHECK_EQ(behavior, kIgnorePlatformOverlayScrollbarSize);
+ if (Scrollbar* horizontal_bar = HorizontalScrollbar())
+ return !horizontal_bar->IsOverlayScrollbar() ? horizontal_bar->Height() : 0;
+ return 0;
+}
+
+FloatQuad ScrollableArea::LocalToVisibleContentQuad(const FloatQuad& quad,
+ const LayoutObject*,
+ unsigned) const {
+ FloatQuad result(quad);
+ result.Move(-GetScrollOffset());
+ return result;
+}
+
+IntSize ScrollableArea::ExcludeScrollbars(const IntSize& size) const {
+ return IntSize(std::max(0, size.Width() - VerticalScrollbarWidth()),
+ std::max(0, size.Height() - HorizontalScrollbarHeight()));
+}
+
+void ScrollableArea::DidScroll(const gfx::ScrollOffset& offset) {
+ ScrollOffset new_offset = ScrollOffset(offset.x() - ScrollOrigin().X(),
+ offset.y() - ScrollOrigin().Y());
+ SetScrollOffset(new_offset, kCompositorScroll);
+}
+
+void ScrollableArea::SetAutosizeScrollbarModes(ScrollbarMode vertical,
+ ScrollbarMode horizontal) {
+ DCHECK_EQ(vertical == kScrollbarAuto, horizontal == kScrollbarAuto);
+ autosize_vertical_scrollbar_mode_ = vertical;
+ autosize_horizontal_scrollbar_mode_ = horizontal;
+}
+
+void ScrollableArea::Trace(blink::Visitor* visitor) {
+ visitor->Trace(scroll_animator_);
+ visitor->Trace(programmatic_scroll_animator_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scroll/scrollable_area.h b/chromium/third_party/blink/renderer/platform/scroll/scrollable_area.h
new file mode 100644
index 00000000000..b03b1aa6348
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/scrollable_area.h
@@ -0,0 +1,514 @@
+/*
+ * Copyright (C) 2008, 2011 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLLABLE_AREA_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLLABLE_AREA_H_
+
+#include "third_party/blink/renderer/platform/geometry/float_quad.h"
+#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
+#include "third_party/blink/renderer/platform/graphics/color.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/scroll/scroll_animator_base.h"
+#include "third_party/blink/renderer/platform/scroll/scroll_types.h"
+#include "third_party/blink/renderer/platform/scroll/scrollbar.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class CompositorAnimationHost;
+class CompositorAnimationTimeline;
+class GraphicsLayer;
+class LayoutBox;
+class LayoutObject;
+class PaintLayer;
+class PlatformChromeClient;
+class ProgrammaticScrollAnimator;
+class ScrollAnchor;
+class ScrollAnimatorBase;
+struct SerializedAnchor;
+class SmoothScrollSequencer;
+class CompositorAnimationTimeline;
+struct WebScrollIntoViewParams;
+
+enum IncludeScrollbarsInRect {
+ kExcludeScrollbars,
+ kIncludeScrollbars,
+};
+
+class PLATFORM_EXPORT ScrollableArea : public GarbageCollectedMixin {
+ WTF_MAKE_NONCOPYABLE(ScrollableArea);
+
+ public:
+ static int PixelsPerLineStep(PlatformChromeClient*);
+ static float MinFractionToStepWhenPaging();
+ int MaxOverlapBetweenPages() const;
+
+ // Convert a non-finite scroll value (Infinity, -Infinity, NaN) to 0 as
+ // per https://drafts.csswg.org/cssom-view/#normalize-non-finite-values.
+ static float NormalizeNonFiniteScroll(float value) {
+ return std::isfinite(value) ? value : 0.0;
+ }
+
+ virtual PlatformChromeClient* GetChromeClient() const { return nullptr; }
+
+ virtual SmoothScrollSequencer* GetSmoothScrollSequencer() const {
+ return nullptr;
+ }
+
+ virtual ScrollResult UserScroll(ScrollGranularity, const ScrollOffset&);
+
+ virtual void SetScrollOffset(const ScrollOffset&,
+ ScrollType,
+ ScrollBehavior = kScrollBehaviorInstant);
+ virtual void ScrollBy(const ScrollOffset&,
+ ScrollType,
+ ScrollBehavior = kScrollBehaviorInstant);
+ void SetScrollOffsetSingleAxis(ScrollbarOrientation,
+ float,
+ ScrollType,
+ ScrollBehavior = kScrollBehaviorInstant);
+
+ // Scrolls the area so that the given rect, given in absolute coordinates,
+ // such that it's visible in the area. Returns the new location of the input
+ // rect in absolute coordinates.
+ virtual LayoutRect ScrollIntoView(const LayoutRect&,
+ const WebScrollIntoViewParams&);
+
+ static bool ScrollBehaviorFromString(const String&, ScrollBehavior&);
+
+ void ContentAreaWillPaint() const;
+ void MouseEnteredContentArea() const;
+ void MouseExitedContentArea() const;
+ void MouseMovedInContentArea() const;
+ void MouseEnteredScrollbar(Scrollbar&);
+ void MouseExitedScrollbar(Scrollbar&);
+ void MouseCapturedScrollbar();
+ void MouseReleasedScrollbar(ScrollbarOrientation);
+ void ContentAreaDidShow() const;
+ void ContentAreaDidHide() const;
+
+ virtual void SnapAfterScrollbarDragging(ScrollbarOrientation) {}
+
+ void FinishCurrentScrollAnimations() const;
+
+ virtual void DidAddScrollbar(Scrollbar&, ScrollbarOrientation);
+ virtual void WillRemoveScrollbar(Scrollbar&, ScrollbarOrientation);
+
+ // Called when this ScrollableArea becomes or unbecomes the global root
+ // scroller.
+ virtual void DidChangeGlobalRootScroller() {}
+
+ virtual void ContentsResized();
+
+ bool HasOverlayScrollbars() const;
+ void SetScrollbarOverlayColorTheme(ScrollbarOverlayColorTheme);
+ void RecalculateScrollbarOverlayColorTheme(Color);
+ ScrollbarOverlayColorTheme GetScrollbarOverlayColorTheme() const {
+ return static_cast<ScrollbarOverlayColorTheme>(
+ scrollbar_overlay_color_theme_);
+ }
+
+ // This getter will create a ScrollAnimatorBase if it doesn't already exist.
+ ScrollAnimatorBase& GetScrollAnimator() const;
+
+ // This getter will return null if the ScrollAnimatorBase hasn't been created
+ // yet.
+ ScrollAnimatorBase* ExistingScrollAnimator() const {
+ return scroll_animator_;
+ }
+
+ ProgrammaticScrollAnimator& GetProgrammaticScrollAnimator() const;
+ ProgrammaticScrollAnimator* ExistingProgrammaticScrollAnimator() const {
+ return programmatic_scroll_animator_;
+ }
+
+ virtual CompositorAnimationHost* GetCompositorAnimationHost() const {
+ return nullptr;
+ }
+ virtual CompositorAnimationTimeline* GetCompositorAnimationTimeline() const {
+ return nullptr;
+ }
+
+ // See Source/core/layout/README.md for an explanation of scroll origin.
+ const IntPoint& ScrollOrigin() const { return scroll_origin_; }
+ bool ScrollOriginChanged() const { return scroll_origin_changed_; }
+
+ // This is used to determine whether the incoming fractional scroll offset
+ // should be truncated to integer. Current rule is that if
+ // preferCompositingToLCDTextEnabled() is disabled (which is true on low-dpi
+ // device by default) we should do the truncation. The justification is that
+ // non-composited elements using fractional scroll offsets is causing too much
+ // nasty bugs but does not add too benefit on low-dpi devices.
+ // TODO(szager): Now that scroll offsets are floats everywhere, can we get rid
+ // of this?
+ virtual bool ShouldUseIntegerScrollOffset() const {
+ return !RuntimeEnabledFeatures::FractionalScrollOffsetsEnabled();
+ }
+
+ virtual bool IsActive() const = 0;
+ virtual int ScrollSize(ScrollbarOrientation) const = 0;
+ void SetScrollbarNeedsPaintInvalidation(ScrollbarOrientation);
+ virtual bool IsScrollCornerVisible() const = 0;
+ virtual IntRect ScrollCornerRect() const = 0;
+ void SetScrollCornerNeedsPaintInvalidation();
+ virtual void GetTickmarks(Vector<IntRect>&) const {}
+
+ // Convert points and rects between the scrollbar and its containing
+ // EmbeddedContentView. The client needs to implement these in order to be
+ // aware of layout effects like CSS transforms.
+ virtual IntRect ConvertFromScrollbarToContainingEmbeddedContentView(
+ const Scrollbar& scrollbar,
+ const IntRect& scrollbar_rect) const {
+ IntRect local_rect = scrollbar_rect;
+ local_rect.MoveBy(scrollbar.Location());
+ return local_rect;
+ }
+ virtual IntPoint ConvertFromContainingEmbeddedContentViewToScrollbar(
+ const Scrollbar& scrollbar,
+ const IntPoint& parent_point) const {
+ NOTREACHED();
+ return parent_point;
+ }
+ virtual IntPoint ConvertFromScrollbarToContainingEmbeddedContentView(
+ const Scrollbar& scrollbar,
+ const IntPoint& scrollbar_point) const {
+ NOTREACHED();
+ return scrollbar_point;
+ }
+ virtual IntPoint ConvertFromRootFrame(
+ const IntPoint& point_in_root_frame) const {
+ NOTREACHED();
+ return point_in_root_frame;
+ }
+
+ virtual Scrollbar* HorizontalScrollbar() const { return nullptr; }
+ virtual Scrollbar* VerticalScrollbar() const { return nullptr; }
+ virtual Scrollbar* CreateScrollbar(ScrollbarOrientation) { return nullptr; }
+
+ virtual PaintLayer* Layer() const { return nullptr; }
+
+ // scrollPosition is the location of the top/left of the scroll viewport in
+ // the coordinate system defined by the top/left of the overflow rect.
+ // scrollOffset is the offset of the scroll viewport from its position when
+ // scrolled all the way to the beginning of its content's flow.
+ // For a more detailed explanation of scrollPosition, scrollOffset, and
+ // scrollOrigin, see core/layout/README.md.
+ FloatPoint ScrollPosition() const {
+ return FloatPoint(ScrollOrigin()) + GetScrollOffset();
+ }
+ virtual IntSize ScrollOffsetInt() const = 0;
+ virtual ScrollOffset GetScrollOffset() const {
+ return ScrollOffset(ScrollOffsetInt());
+ }
+ virtual IntSize MinimumScrollOffsetInt() const = 0;
+ virtual ScrollOffset MinimumScrollOffset() const {
+ return ScrollOffset(MinimumScrollOffsetInt());
+ }
+ virtual IntSize MaximumScrollOffsetInt() const = 0;
+ virtual ScrollOffset MaximumScrollOffset() const {
+ return ScrollOffset(MaximumScrollOffsetInt());
+ }
+
+ virtual IntRect VisibleContentRect(
+ IncludeScrollbarsInRect = kExcludeScrollbars) const = 0;
+ virtual int VisibleHeight() const { return VisibleContentRect().Height(); }
+ virtual int VisibleWidth() const { return VisibleContentRect().Width(); }
+ virtual IntSize ContentsSize() const = 0;
+
+ // scroll snapport is the area of the scrollport that is used as the alignment
+ // container for the scroll snap areas when calculating snap positions. It's
+ // the box's scrollport contracted by its scroll-padding.
+ // https://drafts.csswg.org/css-scroll-snap-1/#scroll-padding
+ virtual LayoutRect VisibleScrollSnapportRect() const {
+ return LayoutRect(VisibleContentRect());
+ }
+
+ virtual IntPoint LastKnownMousePosition() const { return IntPoint(); }
+
+ virtual bool ShouldSuspendScrollAnimations() const { return true; }
+ virtual void ScrollbarStyleChanged() {}
+ virtual bool ScrollbarsCanBeActive() const = 0;
+
+ // Returns the bounding box of this scrollable area, in the coordinate system
+ // of the top-level FrameView.
+ virtual IntRect ScrollableAreaBoundingBox() const = 0;
+
+ virtual CompositorElementId GetCompositorElementId() const = 0;
+ virtual bool ScrollAnimatorEnabled() const { return false; }
+
+ // NOTE: Only called from Internals for testing.
+ void UpdateScrollOffsetFromInternals(const IntSize&);
+
+ virtual IntSize ClampScrollOffset(const IntSize&) const;
+ virtual ScrollOffset ClampScrollOffset(const ScrollOffset&) const;
+
+ // Let subclasses provide a way of asking for and servicing scroll
+ // animations.
+ virtual bool ScheduleAnimation() { return false; }
+ virtual void ServiceScrollAnimations(double monotonic_time);
+ virtual void UpdateCompositorScrollAnimations();
+ virtual void RegisterForAnimation() {}
+ virtual void DeregisterForAnimation() {}
+
+ bool UsesCompositedScrolling() const { return uses_composited_scrolling_; }
+ void SetUsesCompositedScrolling(bool uses_composited_scrolling) {
+ uses_composited_scrolling_ = uses_composited_scrolling;
+ }
+ virtual bool ShouldScrollOnMainThread() const;
+
+ // Overlay scrollbars can "fade-out" when inactive.
+ virtual bool ScrollbarsHiddenIfOverlay() const;
+ virtual void SetScrollbarsHiddenIfOverlay(bool);
+
+ // Returns true if the GraphicsLayer tree needs to be rebuilt.
+ virtual bool UpdateAfterCompositingChange() { return false; }
+
+ virtual bool UserInputScrollable(ScrollbarOrientation) const = 0;
+ virtual bool ShouldPlaceVerticalScrollbarOnLeft() const = 0;
+
+ // Convenience functions
+ float MinimumScrollOffset(ScrollbarOrientation orientation) {
+ return orientation == kHorizontalScrollbar ? MinimumScrollOffset().Width()
+ : MinimumScrollOffset().Height();
+ }
+ float MaximumScrollOffset(ScrollbarOrientation orientation) {
+ return orientation == kHorizontalScrollbar ? MaximumScrollOffset().Width()
+ : MaximumScrollOffset().Height();
+ }
+ float ClampScrollOffset(ScrollbarOrientation orientation, float offset) {
+ return clampTo(offset, MinimumScrollOffset(orientation),
+ MaximumScrollOffset(orientation));
+ }
+
+ virtual GraphicsLayer* LayerForContainer() const;
+ virtual GraphicsLayer* LayerForScrolling() const { return nullptr; }
+ virtual GraphicsLayer* LayerForHorizontalScrollbar() const { return nullptr; }
+ virtual GraphicsLayer* LayerForVerticalScrollbar() const { return nullptr; }
+ virtual GraphicsLayer* LayerForScrollCorner() const { return nullptr; }
+ bool HasLayerForHorizontalScrollbar() const;
+ bool HasLayerForVerticalScrollbar() const;
+ bool HasLayerForScrollCorner() const;
+
+ void LayerForScrollingDidChange(CompositorAnimationTimeline*);
+ bool NeedsShowScrollbarLayers() const { return needs_show_scrollbar_layers_; }
+ void DidShowScrollbarLayers() { needs_show_scrollbar_layers_ = false; }
+
+ void CancelScrollAnimation();
+ virtual void CancelProgrammaticScrollAnimation();
+
+ virtual ~ScrollableArea();
+
+ // Called when any of horizontal scrollbar, vertical scrollbar and scroll
+ // corner is setNeedsPaintInvalidation.
+ virtual void ScrollControlWasSetNeedsPaintInvalidation() = 0;
+
+ // Returns the default scroll style this area should scroll with when not
+ // explicitly specified. E.g. The scrolling behavior of an element can be
+ // specified in CSS.
+ virtual ScrollBehavior ScrollBehaviorStyle() const {
+ return kScrollBehaviorInstant;
+ }
+
+ virtual bool IsScrollable() const { return true; }
+
+ // TODO(bokan): FrameView::setScrollOffset uses updateScrollbars to scroll
+ // which bails out early if its already in updateScrollbars, the effect being
+ // that programmatic scrolls (i.e. setScrollOffset) are disabled when in
+ // updateScrollbars. Expose this here to allow RootFrameViewport to match the
+ // semantics for now but it should be cleaned up at the source.
+ virtual bool IsProgrammaticallyScrollable() { return true; }
+
+ // Subtracts space occupied by this ScrollableArea's scrollbars.
+ // Does nothing if overlay scrollbars are enabled.
+ IntSize ExcludeScrollbars(const IntSize&) const;
+
+ virtual int VerticalScrollbarWidth(
+ OverlayScrollbarClipBehavior = kIgnorePlatformOverlayScrollbarSize) const;
+ virtual int HorizontalScrollbarHeight(
+ OverlayScrollbarClipBehavior = kIgnorePlatformOverlayScrollbarSize) const;
+
+ virtual LayoutBox* GetLayoutBox() const { return nullptr; }
+
+ // Maps a quad from the coordinate system of a LayoutObject contained by the
+ // ScrollableArea to the coordinate system of the ScrollableArea's visible
+ // content rect. If the LayoutObject* argument is null, the argument quad is
+ // considered to be in the coordinate space of the overflow rect.
+ virtual FloatQuad LocalToVisibleContentQuad(const FloatQuad&,
+ const LayoutObject*,
+ unsigned = 0) const;
+
+ virtual bool IsLocalFrameView() const { return false; }
+ virtual bool IsPaintLayerScrollableArea() const { return false; }
+ virtual bool IsRootFrameViewport() const { return false; }
+
+ // Returns true if the scroller adjusts the scroll offset to compensate
+ // for layout movements (bit.ly/scroll-anchoring).
+ virtual bool ShouldPerformScrollAnchoring() const { return false; }
+
+ // Need to promptly let go of owned animator objects.
+ EAGERLY_FINALIZE();
+ virtual void Trace(blink::Visitor*);
+
+ virtual void ClearScrollableArea();
+
+ virtual bool RestoreScrollAnchor(const SerializedAnchor&) { return false; }
+ virtual ScrollAnchor* GetScrollAnchor() { return nullptr; }
+
+ virtual void DidScrollWithScrollbar(ScrollbarPart, ScrollbarOrientation) {}
+
+ // Returns the task runner to be used for scrollable area timers.
+ // Ideally a frame-specific throttled one can be used.
+ virtual scoped_refptr<base::SingleThreadTaskRunner> GetTimerTaskRunner()
+ const = 0;
+
+ // Callback for compositor-side scrolling.
+ virtual void DidScroll(const gfx::ScrollOffset&);
+
+ virtual void ScrollbarFrameRectChanged() {}
+
+ virtual ScrollbarTheme& GetPageScrollbarTheme() const = 0;
+
+ // If either direction has a non-auto mode, the other must as well.
+ void SetAutosizeScrollbarModes(ScrollbarMode vertical,
+ ScrollbarMode horizontal);
+
+ protected:
+ // Deduces the ScrollBehavior based on the element style and the parameter set
+ // by programmatic scroll into either instant or smooth scroll.
+ static ScrollBehavior DetermineScrollBehavior(
+ ScrollBehavior behavior_from_style,
+ ScrollBehavior behavior_from_param);
+
+ ScrollableArea();
+
+ ScrollbarOrientation ScrollbarOrientationFromDirection(
+ ScrollDirectionPhysical) const;
+ float ScrollStep(ScrollGranularity, ScrollbarOrientation) const;
+
+ void SetScrollOrigin(const IntPoint&);
+ void ResetScrollOriginChanged() { scroll_origin_changed_ = false; }
+
+ // Needed to let the animators call scrollOffsetChanged.
+ friend class ScrollAnimatorCompositorCoordinator;
+ void ScrollOffsetChanged(const ScrollOffset&, ScrollType);
+
+ bool HorizontalScrollbarNeedsPaintInvalidation() const {
+ return horizontal_scrollbar_needs_paint_invalidation_;
+ }
+ bool VerticalScrollbarNeedsPaintInvalidation() const {
+ return vertical_scrollbar_needs_paint_invalidation_;
+ }
+ bool ScrollCornerNeedsPaintInvalidation() const {
+ return scroll_corner_needs_paint_invalidation_;
+ }
+ void ClearNeedsPaintInvalidationForScrollControls() {
+ horizontal_scrollbar_needs_paint_invalidation_ = false;
+ vertical_scrollbar_needs_paint_invalidation_ = false;
+ scroll_corner_needs_paint_invalidation_ = false;
+ }
+ void ShowOverlayScrollbars();
+
+ // Called when scrollbar hides/shows for overlay scrollbars. This callback
+ // shouldn't do any significant work as it can be called unexpectadly often
+ // on Mac. This happens because painting code has to set alpha to 1, paint,
+ // then reset to alpha, causing spurrious "visibilityChanged" calls.
+ virtual void ScrollbarVisibilityChanged() {}
+
+ ScrollbarMode AutosizeVerticalScrollbarMode() const {
+ return autosize_vertical_scrollbar_mode_;
+ }
+ ScrollbarMode AutosizeHorizontalScrollbarMode() const {
+ return autosize_horizontal_scrollbar_mode_;
+ }
+
+ virtual bool HasBeenDisposed() const { return false; }
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(ScrollableAreaTest,
+ PopupOverlayScrollbarShouldNotFadeOut);
+
+ void ProgrammaticScrollHelper(const ScrollOffset&, ScrollBehavior, bool);
+ void UserScrollHelper(const ScrollOffset&, ScrollBehavior);
+
+ void FadeOverlayScrollbarsTimerFired(TimerBase*);
+
+ // This function should be overriden by subclasses to perform the actual
+ // scroll of the content.
+ virtual void UpdateScrollOffset(const ScrollOffset&, ScrollType) = 0;
+
+ virtual int LineStep(ScrollbarOrientation) const;
+ virtual int PageStep(ScrollbarOrientation) const;
+ virtual int DocumentStep(ScrollbarOrientation) const;
+ virtual float PixelStep(ScrollbarOrientation) const;
+
+ mutable Member<ScrollAnimatorBase> scroll_animator_;
+ mutable Member<ProgrammaticScrollAnimator> programmatic_scroll_animator_;
+
+ std::unique_ptr<TaskRunnerTimer<ScrollableArea>>
+ fade_overlay_scrollbars_timer_;
+
+ // FrameViewAutoSizeInfo controls scrollbar appearance manually rather than
+ // relying on layout. These members are used to override the
+ // ScrollableArea's ScrollbarModes as calculated from style. kScrollbarAuto
+ // disables the override.
+ ScrollbarMode autosize_vertical_scrollbar_mode_;
+ ScrollbarMode autosize_horizontal_scrollbar_mode_;
+
+ unsigned scrollbar_overlay_color_theme_ : 2;
+
+ unsigned scroll_origin_changed_ : 1;
+
+ unsigned horizontal_scrollbar_needs_paint_invalidation_ : 1;
+ unsigned vertical_scrollbar_needs_paint_invalidation_ : 1;
+ unsigned scroll_corner_needs_paint_invalidation_ : 1;
+ unsigned scrollbars_hidden_if_overlay_ : 1;
+ unsigned scrollbar_captured_ : 1;
+ unsigned mouse_over_scrollbar_ : 1;
+
+ // Indicates that the next compositing update needs to call
+ // WebLayer::showScrollbars on our scroll layer. Ignored if not composited.
+ unsigned needs_show_scrollbar_layers_ : 1;
+ unsigned uses_composited_scrolling_ : 1;
+
+ // There are 6 possible combinations of writing mode and direction. Scroll
+ // origin will be non-zero in the x or y axis if there is any reversed
+ // direction or writing-mode. The combinations are:
+ // writing-mode / direction scrollOrigin.x() set scrollOrigin.y() set
+ // horizontal-tb / ltr NO NO
+ // horizontal-tb / rtl YES NO
+ // vertical-lr / ltr NO NO
+ // vertical-lr / rtl NO YES
+ // vertical-rl / ltr YES NO
+ // vertical-rl / rtl YES YES
+ IntPoint scroll_origin_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLLABLE_AREA_H_
diff --git a/chromium/third_party/blink/renderer/platform/scroll/scrollable_area_test.cc b/chromium/third_party/blink/renderer/platform/scroll/scrollable_area_test.cc
new file mode 100644
index 00000000000..56443fae40e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/scrollable_area_test.cc
@@ -0,0 +1,308 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scroll/scrollable_area.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/graphics/color.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
+#include "third_party/blink/renderer/platform/scroll/scrollbar_test_suite.h"
+#include "third_party/blink/renderer/platform/scroll/scrollbar_theme.h"
+#include "third_party/blink/renderer/platform/scroll/scrollbar_theme_mock.h"
+#include "third_party/blink/renderer/platform/scroll/scrollbar_theme_overlay_mock.h"
+#include "third_party/blink/renderer/platform/testing/fake_graphics_layer.h"
+#include "third_party/blink/renderer/platform/testing/fake_graphics_layer_client.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
+
+namespace blink {
+
+namespace {
+
+using testing::_;
+using testing::Return;
+
+class ScrollbarThemeWithMockInvalidation : public ScrollbarThemeMock {
+ public:
+ MOCK_CONST_METHOD0(ShouldRepaintAllPartsOnInvalidation, bool());
+ MOCK_CONST_METHOD3(InvalidateOnThumbPositionChange,
+ ScrollbarPart(const ScrollbarThemeClient&, float, float));
+};
+
+} // namespace
+
+class ScrollableAreaTest : public testing::Test {
+ private:
+ base::MessageLoop message_loop_;
+};
+
+TEST_F(ScrollableAreaTest, ScrollAnimatorCurrentPositionShouldBeSync) {
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform;
+
+ MockScrollableArea* scrollable_area =
+ MockScrollableArea::Create(ScrollOffset(0, 100));
+ scrollable_area->SetScrollOffset(ScrollOffset(0, 10000), kCompositorScroll);
+ EXPECT_EQ(100.0,
+ scrollable_area->GetScrollAnimator().CurrentOffset().Height());
+}
+
+TEST_F(ScrollableAreaTest, ScrollbarTrackAndThumbRepaint) {
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform;
+
+ ScrollbarThemeWithMockInvalidation theme;
+ MockScrollableArea* scrollable_area =
+ MockScrollableArea::Create(ScrollOffset(0, 100));
+ Scrollbar* scrollbar = Scrollbar::CreateForTesting(
+ scrollable_area, kHorizontalScrollbar, kRegularScrollbar, &theme);
+
+ EXPECT_CALL(theme, ShouldRepaintAllPartsOnInvalidation())
+ .WillRepeatedly(Return(true));
+ EXPECT_TRUE(scrollbar->TrackNeedsRepaint());
+ EXPECT_TRUE(scrollbar->ThumbNeedsRepaint());
+ scrollbar->SetNeedsPaintInvalidation(kNoPart);
+ EXPECT_TRUE(scrollbar->TrackNeedsRepaint());
+ EXPECT_TRUE(scrollbar->ThumbNeedsRepaint());
+
+ scrollbar->ClearTrackNeedsRepaint();
+ scrollbar->ClearThumbNeedsRepaint();
+ EXPECT_FALSE(scrollbar->TrackNeedsRepaint());
+ EXPECT_FALSE(scrollbar->ThumbNeedsRepaint());
+ scrollbar->SetNeedsPaintInvalidation(kThumbPart);
+ EXPECT_TRUE(scrollbar->TrackNeedsRepaint());
+ EXPECT_TRUE(scrollbar->ThumbNeedsRepaint());
+
+ // When not all parts are repainted on invalidation,
+ // setNeedsPaintInvalidation sets repaint bits only on the requested parts.
+ EXPECT_CALL(theme, ShouldRepaintAllPartsOnInvalidation())
+ .WillRepeatedly(Return(false));
+ scrollbar->ClearTrackNeedsRepaint();
+ scrollbar->ClearThumbNeedsRepaint();
+ EXPECT_FALSE(scrollbar->TrackNeedsRepaint());
+ EXPECT_FALSE(scrollbar->ThumbNeedsRepaint());
+ scrollbar->SetNeedsPaintInvalidation(kThumbPart);
+ EXPECT_FALSE(scrollbar->TrackNeedsRepaint());
+ EXPECT_TRUE(scrollbar->ThumbNeedsRepaint());
+
+ // Forced GC in order to finalize objects depending on the mock object.
+ ThreadState::Current()->CollectAllGarbage();
+}
+
+TEST_F(ScrollableAreaTest, ScrollbarGraphicsLayerInvalidation) {
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform;
+
+ ScrollbarTheme::SetMockScrollbarsEnabled(true);
+ MockScrollableArea* scrollable_area =
+ MockScrollableArea::Create(ScrollOffset(0, 100));
+ FakeGraphicsLayerClient graphics_layer_client;
+ graphics_layer_client.SetIsTrackingRasterInvalidations(true);
+ FakeGraphicsLayer graphics_layer(graphics_layer_client);
+ graphics_layer.SetDrawsContent(true);
+ graphics_layer.SetSize(FloatSize(111, 222));
+
+ EXPECT_CALL(*scrollable_area, LayerForHorizontalScrollbar())
+ .WillRepeatedly(Return(&graphics_layer));
+
+ Scrollbar* scrollbar = Scrollbar::Create(
+ scrollable_area, kHorizontalScrollbar, kRegularScrollbar, nullptr);
+ graphics_layer.ResetTrackedRasterInvalidations();
+ scrollbar->SetNeedsPaintInvalidation(kNoPart);
+ EXPECT_TRUE(graphics_layer.HasTrackedRasterInvalidations());
+
+ // Forced GC in order to finalize objects depending on the mock object.
+ ThreadState::Current()->CollectAllGarbage();
+}
+
+TEST_F(ScrollableAreaTest, InvalidatesNonCompositedScrollbarsWhenThumbMoves) {
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform;
+
+ ScrollbarThemeWithMockInvalidation theme;
+ MockScrollableArea* scrollable_area =
+ MockScrollableArea::Create(ScrollOffset(100, 100));
+ Scrollbar* horizontal_scrollbar = Scrollbar::CreateForTesting(
+ scrollable_area, kHorizontalScrollbar, kRegularScrollbar, &theme);
+ Scrollbar* vertical_scrollbar = Scrollbar::CreateForTesting(
+ scrollable_area, kVerticalScrollbar, kRegularScrollbar, &theme);
+ EXPECT_CALL(*scrollable_area, HorizontalScrollbar())
+ .WillRepeatedly(Return(horizontal_scrollbar));
+ EXPECT_CALL(*scrollable_area, VerticalScrollbar())
+ .WillRepeatedly(Return(vertical_scrollbar));
+
+ // Regardless of whether the theme invalidates any parts, non-composited
+ // scrollbars have to be repainted if the thumb moves.
+ EXPECT_CALL(*scrollable_area, LayerForHorizontalScrollbar())
+ .WillRepeatedly(Return(nullptr));
+ EXPECT_CALL(*scrollable_area, LayerForVerticalScrollbar())
+ .WillRepeatedly(Return(nullptr));
+ ASSERT_FALSE(scrollable_area->HasLayerForVerticalScrollbar());
+ ASSERT_FALSE(scrollable_area->HasLayerForHorizontalScrollbar());
+ EXPECT_CALL(theme, ShouldRepaintAllPartsOnInvalidation())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(theme, InvalidateOnThumbPositionChange(_, _, _))
+ .WillRepeatedly(Return(kNoPart));
+
+ // A scroll in each direction should only invalidate one scrollbar.
+ scrollable_area->SetScrollOffset(ScrollOffset(0, 50), kProgrammaticScroll);
+ EXPECT_FALSE(scrollable_area->HorizontalScrollbarNeedsPaintInvalidation());
+ EXPECT_TRUE(scrollable_area->VerticalScrollbarNeedsPaintInvalidation());
+ scrollable_area->ClearNeedsPaintInvalidationForScrollControls();
+ scrollable_area->SetScrollOffset(ScrollOffset(50, 50), kProgrammaticScroll);
+ EXPECT_TRUE(scrollable_area->HorizontalScrollbarNeedsPaintInvalidation());
+ EXPECT_FALSE(scrollable_area->VerticalScrollbarNeedsPaintInvalidation());
+ scrollable_area->ClearNeedsPaintInvalidationForScrollControls();
+
+ // Forced GC in order to finalize objects depending on the mock object.
+ ThreadState::Current()->CollectAllGarbage();
+}
+
+TEST_F(ScrollableAreaTest, InvalidatesCompositedScrollbarsIfPartsNeedRepaint) {
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform;
+
+ ScrollbarThemeWithMockInvalidation theme;
+ MockScrollableArea* scrollable_area =
+ MockScrollableArea::Create(ScrollOffset(100, 100));
+ Scrollbar* horizontal_scrollbar = Scrollbar::CreateForTesting(
+ scrollable_area, kHorizontalScrollbar, kRegularScrollbar, &theme);
+ horizontal_scrollbar->ClearTrackNeedsRepaint();
+ horizontal_scrollbar->ClearThumbNeedsRepaint();
+ Scrollbar* vertical_scrollbar = Scrollbar::CreateForTesting(
+ scrollable_area, kVerticalScrollbar, kRegularScrollbar, &theme);
+ vertical_scrollbar->ClearTrackNeedsRepaint();
+ vertical_scrollbar->ClearThumbNeedsRepaint();
+ EXPECT_CALL(*scrollable_area, HorizontalScrollbar())
+ .WillRepeatedly(Return(horizontal_scrollbar));
+ EXPECT_CALL(*scrollable_area, VerticalScrollbar())
+ .WillRepeatedly(Return(vertical_scrollbar));
+
+ // Composited scrollbars only need repainting when parts become invalid
+ // (e.g. if the track changes appearance when the thumb reaches the end).
+ FakeGraphicsLayerClient graphics_layer_client;
+ graphics_layer_client.SetIsTrackingRasterInvalidations(true);
+ FakeGraphicsLayer layer_for_horizontal_scrollbar(graphics_layer_client);
+ layer_for_horizontal_scrollbar.SetDrawsContent(true);
+ layer_for_horizontal_scrollbar.SetSize(FloatSize(10, 10));
+ FakeGraphicsLayer layer_for_vertical_scrollbar(graphics_layer_client);
+ layer_for_vertical_scrollbar.SetDrawsContent(true);
+ layer_for_vertical_scrollbar.SetSize(FloatSize(10, 10));
+ EXPECT_CALL(*scrollable_area, LayerForHorizontalScrollbar())
+ .WillRepeatedly(Return(&layer_for_horizontal_scrollbar));
+ EXPECT_CALL(*scrollable_area, LayerForVerticalScrollbar())
+ .WillRepeatedly(Return(&layer_for_vertical_scrollbar));
+ ASSERT_TRUE(scrollable_area->HasLayerForHorizontalScrollbar());
+ ASSERT_TRUE(scrollable_area->HasLayerForVerticalScrollbar());
+ EXPECT_CALL(theme, ShouldRepaintAllPartsOnInvalidation())
+ .WillRepeatedly(Return(false));
+
+ // First, we'll scroll horizontally, and the theme will require repainting
+ // the back button (i.e. the track).
+ EXPECT_CALL(theme, InvalidateOnThumbPositionChange(_, _, _))
+ .WillOnce(Return(kBackButtonStartPart));
+ scrollable_area->SetScrollOffset(ScrollOffset(50, 0), kProgrammaticScroll);
+ EXPECT_TRUE(layer_for_horizontal_scrollbar.HasTrackedRasterInvalidations());
+ EXPECT_FALSE(layer_for_vertical_scrollbar.HasTrackedRasterInvalidations());
+ EXPECT_TRUE(horizontal_scrollbar->TrackNeedsRepaint());
+ EXPECT_FALSE(horizontal_scrollbar->ThumbNeedsRepaint());
+ layer_for_horizontal_scrollbar.ResetTrackedRasterInvalidations();
+ horizontal_scrollbar->ClearTrackNeedsRepaint();
+
+ // Next, we'll scroll vertically, but invalidate the thumb.
+ EXPECT_CALL(theme, InvalidateOnThumbPositionChange(_, _, _))
+ .WillOnce(Return(kThumbPart));
+ scrollable_area->SetScrollOffset(ScrollOffset(50, 50), kProgrammaticScroll);
+ EXPECT_FALSE(layer_for_horizontal_scrollbar.HasTrackedRasterInvalidations());
+ EXPECT_TRUE(layer_for_vertical_scrollbar.HasTrackedRasterInvalidations());
+ EXPECT_FALSE(vertical_scrollbar->TrackNeedsRepaint());
+ EXPECT_TRUE(vertical_scrollbar->ThumbNeedsRepaint());
+ layer_for_vertical_scrollbar.ResetTrackedRasterInvalidations();
+ vertical_scrollbar->ClearThumbNeedsRepaint();
+
+ // Next we'll scroll in both, but the thumb position moving requires no
+ // invalidations. Nonetheless the GraphicsLayer should be invalidated,
+ // because we still need to update the underlying layer (though no
+ // rasterization will be required).
+ EXPECT_CALL(theme, InvalidateOnThumbPositionChange(_, _, _))
+ .Times(2)
+ .WillRepeatedly(Return(kNoPart));
+ scrollable_area->SetScrollOffset(ScrollOffset(70, 70), kProgrammaticScroll);
+ EXPECT_TRUE(layer_for_horizontal_scrollbar.HasTrackedRasterInvalidations());
+ EXPECT_TRUE(layer_for_vertical_scrollbar.HasTrackedRasterInvalidations());
+ EXPECT_FALSE(horizontal_scrollbar->TrackNeedsRepaint());
+ EXPECT_FALSE(horizontal_scrollbar->ThumbNeedsRepaint());
+ EXPECT_FALSE(vertical_scrollbar->TrackNeedsRepaint());
+ EXPECT_FALSE(vertical_scrollbar->ThumbNeedsRepaint());
+
+ // Forced GC in order to finalize objects depending on the mock object.
+ ThreadState::Current()->CollectAllGarbage();
+}
+
+TEST_F(ScrollableAreaTest, RecalculatesScrollbarOverlayIfBackgroundChanges) {
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform;
+
+ MockScrollableArea* scrollable_area =
+ MockScrollableArea::Create(ScrollOffset(0, 100));
+
+ EXPECT_EQ(kScrollbarOverlayColorThemeDark,
+ scrollable_area->GetScrollbarOverlayColorTheme());
+ scrollable_area->RecalculateScrollbarOverlayColorTheme(Color(34, 85, 51));
+ EXPECT_EQ(kScrollbarOverlayColorThemeLight,
+ scrollable_area->GetScrollbarOverlayColorTheme());
+ scrollable_area->RecalculateScrollbarOverlayColorTheme(Color(236, 143, 185));
+ EXPECT_EQ(kScrollbarOverlayColorThemeDark,
+ scrollable_area->GetScrollbarOverlayColorTheme());
+}
+
+TEST_F(ScrollableAreaTest, ScrollableAreaDidScroll) {
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform;
+
+ MockScrollableArea* scrollable_area =
+ MockScrollableArea::Create(ScrollOffset(100, 100));
+ scrollable_area->SetScrollOrigin(IntPoint(20, 30));
+ scrollable_area->DidScroll(gfx::ScrollOffset(40, 51));
+
+ // After calling didScroll, the new offset should account for scroll origin.
+ EXPECT_EQ(20, scrollable_area->ScrollOffsetInt().Width());
+ EXPECT_EQ(21, scrollable_area->ScrollOffsetInt().Height());
+}
+
+// Scrollbars in popups shouldn't fade out since they aren't composited and thus
+// they don't appear on hover so users without a wheel can't scroll if they fade
+// out.
+TEST_F(ScrollableAreaTest, PopupOverlayScrollbarShouldNotFadeOut) {
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform;
+
+ ScopedOverlayScrollbarsForTest overlay_scrollbars(true);
+ ScrollbarTheme::SetMockScrollbarsEnabled(true);
+
+ MockScrollableArea* scrollable_area =
+ MockScrollableArea::Create(ScrollOffset(0, 100));
+ scrollable_area->SetIsPopup();
+
+ ScrollbarThemeOverlayMock& theme =
+ (ScrollbarThemeOverlayMock&)scrollable_area->GetPageScrollbarTheme();
+ theme.SetOverlayScrollbarFadeOutDelay(1);
+ Scrollbar* scrollbar = Scrollbar::CreateForTesting(
+ scrollable_area, kHorizontalScrollbar, kRegularScrollbar, &theme);
+
+ DCHECK(scrollbar->IsOverlayScrollbar());
+ DCHECK(scrollbar->Enabled());
+
+ scrollable_area->ShowOverlayScrollbars();
+
+ // No fade out animation should be posted.
+ EXPECT_FALSE(scrollable_area->fade_overlay_scrollbars_timer_);
+
+ // Forced GC in order to finalize objects depending on the mock object.
+ ThreadState::Current()->CollectAllGarbage();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scroll/scrollbar.cc b/chromium/third_party/blink/renderer/platform/scroll/scrollbar.cc
new file mode 100644
index 00000000000..ffca17fb668
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/scrollbar.cc
@@ -0,0 +1,671 @@
+/*
+ * Copyright (C) 2004, 2006, 2008 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/scroll/scrollbar.h"
+
+#include <algorithm>
+#include "third_party/blink/public/platform/web_gesture_event.h"
+#include "third_party/blink/public/platform/web_mouse_event.h"
+#include "third_party/blink/public/platform/web_scrollbar.h"
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/graphics/paint/cull_rect.h"
+#include "third_party/blink/renderer/platform/platform_chrome_client.h"
+#include "third_party/blink/renderer/platform/scroll/scroll_animator_base.h"
+#include "third_party/blink/renderer/platform/scroll/scrollable_area.h"
+#include "third_party/blink/renderer/platform/scroll/scrollbar_theme.h"
+
+namespace blink {
+
+Scrollbar::Scrollbar(ScrollableArea* scrollable_area,
+ ScrollbarOrientation orientation,
+ ScrollbarControlSize control_size,
+ PlatformChromeClient* chrome_client,
+ ScrollbarTheme* theme)
+ : scrollable_area_(scrollable_area),
+ orientation_(orientation),
+ control_size_(control_size),
+ theme_(theme ? *theme : scrollable_area->GetPageScrollbarTheme()),
+ chrome_client_(chrome_client),
+ visible_size_(0),
+ total_size_(0),
+ current_pos_(0),
+ drag_origin_(0),
+ hovered_part_(kNoPart),
+ pressed_part_(kNoPart),
+ pressed_pos_(0),
+ scroll_pos_(0),
+ dragging_document_(false),
+ document_drag_pos_(0),
+ enabled_(true),
+ scroll_timer_(scrollable_area->GetTimerTaskRunner(),
+ this,
+ &Scrollbar::AutoscrollTimerFired),
+ elastic_overscroll_(0),
+ track_needs_repaint_(true),
+ thumb_needs_repaint_(true) {
+ theme_.RegisterScrollbar(*this);
+
+ // FIXME: This is ugly and would not be necessary if we fix cross-platform
+ // code to actually query for scrollbar thickness and use it when sizing
+ // scrollbars (rather than leaving one dimension of the scrollbar alone when
+ // sizing).
+ int thickness = theme_.ScrollbarThickness(control_size);
+ theme_scrollbar_thickness_ = thickness;
+ if (chrome_client_)
+ thickness = chrome_client_->WindowToViewportScalar(thickness);
+ frame_rect_ = IntRect(0, 0, thickness, thickness);
+
+ current_pos_ = ScrollableAreaCurrentPos();
+}
+
+Scrollbar::~Scrollbar() {
+ theme_.UnregisterScrollbar(*this);
+}
+
+void Scrollbar::Trace(blink::Visitor* visitor) {
+ visitor->Trace(scrollable_area_);
+ visitor->Trace(chrome_client_);
+}
+
+void Scrollbar::SetFrameRect(const IntRect& frame_rect) {
+ if (frame_rect == frame_rect_)
+ return;
+
+ frame_rect_ = frame_rect;
+ SetNeedsPaintInvalidation(kAllParts);
+ if (scrollable_area_)
+ scrollable_area_->ScrollbarFrameRectChanged();
+}
+
+ScrollbarOverlayColorTheme Scrollbar::GetScrollbarOverlayColorTheme() const {
+ return scrollable_area_ ? scrollable_area_->GetScrollbarOverlayColorTheme()
+ : kScrollbarOverlayColorThemeDark;
+}
+
+void Scrollbar::GetTickmarks(Vector<IntRect>& tickmarks) const {
+ if (scrollable_area_)
+ scrollable_area_->GetTickmarks(tickmarks);
+}
+
+bool Scrollbar::IsScrollableAreaActive() const {
+ return scrollable_area_ && scrollable_area_->IsActive();
+}
+
+bool Scrollbar::IsLeftSideVerticalScrollbar() const {
+ if (orientation_ == kVerticalScrollbar && scrollable_area_)
+ return scrollable_area_->ShouldPlaceVerticalScrollbarOnLeft();
+ return false;
+}
+
+void Scrollbar::OffsetDidChange() {
+ DCHECK(scrollable_area_);
+
+ float position = ScrollableAreaCurrentPos();
+ if (position == current_pos_)
+ return;
+
+ float old_position = current_pos_;
+ int old_thumb_position = GetTheme().ThumbPosition(*this);
+ current_pos_ = position;
+
+ ScrollbarPart invalid_parts =
+ GetTheme().InvalidateOnThumbPositionChange(*this, old_position, position);
+ SetNeedsPaintInvalidation(invalid_parts);
+
+ if (pressed_part_ == kThumbPart)
+ SetPressedPos(pressed_pos_ + GetTheme().ThumbPosition(*this) -
+ old_thumb_position);
+}
+
+void Scrollbar::DisconnectFromScrollableArea() {
+ scrollable_area_ = nullptr;
+}
+
+void Scrollbar::SetProportion(int visible_size, int total_size) {
+ if (visible_size == visible_size_ && total_size == total_size_)
+ return;
+
+ visible_size_ = visible_size;
+ total_size_ = total_size;
+
+ SetNeedsPaintInvalidation(kAllParts);
+}
+
+void Scrollbar::Paint(GraphicsContext& context,
+ const CullRect& cull_rect) const {
+ if (!cull_rect.IntersectsCullRect(FrameRect()))
+ return;
+
+ GetTheme().Paint(*this, context, cull_rect);
+}
+
+void Scrollbar::AutoscrollTimerFired(TimerBase*) {
+ AutoscrollPressedPart(GetTheme().AutoscrollTimerDelay());
+}
+
+bool Scrollbar::ThumbWillBeUnderMouse() const {
+ int thumb_pos = GetTheme().TrackPosition(*this) +
+ GetTheme().ThumbPosition(*this, ScrollableAreaTargetPos());
+ int thumb_length = GetTheme().ThumbLength(*this);
+ return PressedPos() >= thumb_pos && PressedPos() < thumb_pos + thumb_length;
+}
+
+void Scrollbar::AutoscrollPressedPart(double delay) {
+ // Don't do anything for the thumb or if nothing was pressed.
+ if (pressed_part_ == kThumbPart || pressed_part_ == kNoPart)
+ return;
+
+ // Handle the track.
+ if ((pressed_part_ == kBackTrackPart || pressed_part_ == kForwardTrackPart) &&
+ ThumbWillBeUnderMouse()) {
+ SetHoveredPart(kThumbPart);
+ return;
+ }
+
+ // Handle the arrows and track.
+ if (scrollable_area_ &&
+ scrollable_area_
+ ->UserScroll(PressedPartScrollGranularity(),
+ ToScrollDelta(PressedPartScrollDirectionPhysical(), 1))
+ .DidScroll())
+ StartTimerIfNeeded(delay);
+}
+
+void Scrollbar::StartTimerIfNeeded(double delay) {
+ // Don't do anything for the thumb.
+ if (pressed_part_ == kThumbPart)
+ return;
+
+ // Handle the track. We halt track scrolling once the thumb is level
+ // with us.
+ if ((pressed_part_ == kBackTrackPart || pressed_part_ == kForwardTrackPart) &&
+ ThumbWillBeUnderMouse()) {
+ SetHoveredPart(kThumbPart);
+ return;
+ }
+
+ // We can't scroll if we've hit the beginning or end.
+ ScrollDirectionPhysical dir = PressedPartScrollDirectionPhysical();
+ if (dir == kScrollUp || dir == kScrollLeft) {
+ if (current_pos_ == 0)
+ return;
+ } else {
+ if (current_pos_ == Maximum())
+ return;
+ }
+
+ scroll_timer_.StartOneShot(delay, FROM_HERE);
+}
+
+void Scrollbar::StopTimerIfNeeded() {
+ scroll_timer_.Stop();
+}
+
+ScrollDirectionPhysical Scrollbar::PressedPartScrollDirectionPhysical() {
+ if (orientation_ == kHorizontalScrollbar) {
+ if (pressed_part_ == kBackButtonStartPart ||
+ pressed_part_ == kBackButtonEndPart || pressed_part_ == kBackTrackPart)
+ return kScrollLeft;
+ return kScrollRight;
+ } else {
+ if (pressed_part_ == kBackButtonStartPart ||
+ pressed_part_ == kBackButtonEndPart || pressed_part_ == kBackTrackPart)
+ return kScrollUp;
+ return kScrollDown;
+ }
+}
+
+ScrollGranularity Scrollbar::PressedPartScrollGranularity() {
+ if (pressed_part_ == kBackButtonStartPart ||
+ pressed_part_ == kBackButtonEndPart ||
+ pressed_part_ == kForwardButtonStartPart ||
+ pressed_part_ == kForwardButtonEndPart)
+ return kScrollByLine;
+ return kScrollByPage;
+}
+
+void Scrollbar::MoveThumb(int pos, bool dragging_document) {
+ if (!scrollable_area_)
+ return;
+
+ int delta = pos - pressed_pos_;
+
+ if (dragging_document) {
+ if (dragging_document_)
+ delta = pos - document_drag_pos_;
+ dragging_document_ = true;
+ ScrollOffset current_position =
+ scrollable_area_->GetScrollAnimator().CurrentOffset();
+ float destination_position =
+ (orientation_ == kHorizontalScrollbar ? current_position.Width()
+ : current_position.Height()) +
+ delta;
+ destination_position =
+ scrollable_area_->ClampScrollOffset(orientation_, destination_position);
+ scrollable_area_->SetScrollOffsetSingleAxis(
+ orientation_, destination_position, kUserScroll);
+ document_drag_pos_ = pos;
+ return;
+ }
+
+ if (dragging_document_) {
+ delta += pressed_pos_ - document_drag_pos_;
+ dragging_document_ = false;
+ }
+
+ // Drag the thumb.
+ int thumb_pos = GetTheme().ThumbPosition(*this);
+ int thumb_len = GetTheme().ThumbLength(*this);
+ int track_len = GetTheme().TrackLength(*this);
+ DCHECK_LE(thumb_len, track_len);
+ if (thumb_len == track_len)
+ return;
+
+ if (delta > 0)
+ delta = std::min(track_len - thumb_len - thumb_pos, delta);
+ else if (delta < 0)
+ delta = std::max(-thumb_pos, delta);
+
+ float min_offset = scrollable_area_->MinimumScrollOffset(orientation_);
+ float max_offset = scrollable_area_->MaximumScrollOffset(orientation_);
+ if (delta) {
+ float new_offset = static_cast<float>(thumb_pos + delta) *
+ (max_offset - min_offset) / (track_len - thumb_len) +
+ min_offset;
+ scrollable_area_->SetScrollOffsetSingleAxis(orientation_, new_offset,
+ kUserScroll);
+ }
+}
+
+void Scrollbar::SetHoveredPart(ScrollbarPart part) {
+ if (part == hovered_part_)
+ return;
+
+ if (((hovered_part_ == kNoPart || part == kNoPart) &&
+ GetTheme().InvalidateOnMouseEnterExit())
+ // When there's a pressed part, we don't draw a hovered state, so there's
+ // no reason to invalidate.
+ || pressed_part_ == kNoPart)
+ SetNeedsPaintInvalidation(static_cast<ScrollbarPart>(hovered_part_ | part));
+
+ hovered_part_ = part;
+}
+
+void Scrollbar::SetPressedPart(ScrollbarPart part) {
+ if (pressed_part_ != kNoPart
+ // When we no longer have a pressed part, we can start drawing a hovered
+ // state on the hovered part.
+ || hovered_part_ != kNoPart)
+ SetNeedsPaintInvalidation(
+ static_cast<ScrollbarPart>(pressed_part_ | hovered_part_ | part));
+
+ if (GetScrollableArea())
+ GetScrollableArea()->DidScrollWithScrollbar(part, Orientation());
+
+ pressed_part_ = part;
+}
+
+bool Scrollbar::GestureEvent(const WebGestureEvent& evt,
+ bool* should_update_capture) {
+ DCHECK(should_update_capture);
+ switch (evt.GetType()) {
+ case WebInputEvent::kGestureTapDown: {
+ IntPoint position = FlooredIntPoint(evt.PositionInRootFrame());
+ SetPressedPart(GetTheme().HitTest(*this, position));
+ pressed_pos_ = Orientation() == kHorizontalScrollbar
+ ? ConvertFromRootFrame(position).X()
+ : ConvertFromRootFrame(position).Y();
+ *should_update_capture = true;
+ return true;
+ }
+ case WebInputEvent::kGestureTapCancel:
+ if (pressed_part_ != kThumbPart)
+ return false;
+ scroll_pos_ = pressed_pos_;
+ return true;
+ case WebInputEvent::kGestureScrollBegin:
+ switch (evt.SourceDevice()) {
+ case kWebGestureDeviceSyntheticAutoscroll:
+ case kWebGestureDeviceTouchpad:
+ // Update the state on GSB for touchpad since GestureTapDown
+ // is not generated by that device. Touchscreen uses the tap down
+ // gesture since the scrollbar enters a visual active state.
+ SetPressedPart(kNoPart);
+ pressed_pos_ = 0;
+ return false;
+ case kWebGestureDeviceTouchscreen:
+ if (pressed_part_ != kThumbPart)
+ return false;
+ scroll_pos_ = pressed_pos_;
+ return true;
+ default:
+ NOTREACHED();
+ return true;
+ }
+ break;
+ case WebInputEvent::kGestureScrollUpdate:
+ switch (evt.SourceDevice()) {
+ case kWebGestureDeviceSyntheticAutoscroll:
+ case kWebGestureDeviceTouchpad:
+ return false;
+ case kWebGestureDeviceTouchscreen:
+ if (pressed_part_ != kThumbPart)
+ return false;
+ scroll_pos_ += Orientation() == kHorizontalScrollbar
+ ? evt.DeltaXInRootFrame()
+ : evt.DeltaYInRootFrame();
+ MoveThumb(scroll_pos_, false);
+ return true;
+ default:
+ NOTREACHED();
+ return true;
+ }
+ break;
+ case WebInputEvent::kGestureScrollEnd:
+ case WebInputEvent::kGestureLongPress:
+ case WebInputEvent::kGestureFlingStart:
+ scroll_pos_ = 0;
+ pressed_pos_ = 0;
+ SetPressedPart(kNoPart);
+ return false;
+ case WebInputEvent::kGestureTap: {
+ if (pressed_part_ != kThumbPart && pressed_part_ != kNoPart &&
+ scrollable_area_ &&
+ scrollable_area_
+ ->UserScroll(
+ PressedPartScrollGranularity(),
+ ToScrollDelta(PressedPartScrollDirectionPhysical(), 1))
+ .DidScroll()) {
+ return true;
+ }
+ scroll_pos_ = 0;
+ pressed_pos_ = 0;
+ SetPressedPart(kNoPart);
+ return false;
+ }
+ default:
+ // By default, we assume that gestures don't deselect the scrollbar.
+ return true;
+ }
+}
+
+void Scrollbar::MouseMoved(const WebMouseEvent& evt) {
+ IntPoint position = FlooredIntPoint(evt.PositionInRootFrame());
+ if (pressed_part_ == kThumbPart) {
+ if (GetTheme().ShouldSnapBackToDragOrigin(*this, evt)) {
+ if (scrollable_area_) {
+ scrollable_area_->SetScrollOffsetSingleAxis(
+ orientation_,
+ drag_origin_ + scrollable_area_->MinimumScrollOffset(orientation_),
+ kUserScroll);
+ }
+ } else {
+ MoveThumb(orientation_ == kHorizontalScrollbar
+ ? ConvertFromRootFrame(position).X()
+ : ConvertFromRootFrame(position).Y(),
+ GetTheme().ShouldDragDocumentInsteadOfThumb(*this, evt));
+ }
+ return;
+ }
+
+ if (pressed_part_ != kNoPart) {
+ pressed_pos_ = Orientation() == kHorizontalScrollbar
+ ? ConvertFromRootFrame(position).X()
+ : ConvertFromRootFrame(position).Y();
+ }
+
+ ScrollbarPart part = GetTheme().HitTest(*this, position);
+ if (part != hovered_part_) {
+ if (pressed_part_ != kNoPart) {
+ if (part == pressed_part_) {
+ // The mouse is moving back over the pressed part. We
+ // need to start up the timer action again.
+ StartTimerIfNeeded(GetTheme().AutoscrollTimerDelay());
+ } else if (hovered_part_ == pressed_part_) {
+ // The mouse is leaving the pressed part. Kill our timer
+ // if needed.
+ StopTimerIfNeeded();
+ }
+ }
+
+ SetHoveredPart(part);
+ }
+
+ return;
+}
+
+void Scrollbar::MouseEntered() {
+ if (scrollable_area_)
+ scrollable_area_->MouseEnteredScrollbar(*this);
+}
+
+void Scrollbar::MouseExited() {
+ if (scrollable_area_)
+ scrollable_area_->MouseExitedScrollbar(*this);
+ SetHoveredPart(kNoPart);
+}
+
+void Scrollbar::MouseUp(const WebMouseEvent& mouse_event) {
+ bool is_captured = pressed_part_ == kThumbPart;
+ SetPressedPart(kNoPart);
+ pressed_pos_ = 0;
+ dragging_document_ = false;
+ StopTimerIfNeeded();
+
+ if (scrollable_area_) {
+ if (is_captured)
+ scrollable_area_->MouseReleasedScrollbar(orientation_);
+
+ ScrollbarPart part = GetTheme().HitTest(
+ *this, FlooredIntPoint(mouse_event.PositionInRootFrame()));
+ if (part == kNoPart) {
+ SetHoveredPart(kNoPart);
+ scrollable_area_->MouseExitedScrollbar(*this);
+ }
+ }
+}
+
+void Scrollbar::MouseDown(const WebMouseEvent& evt) {
+ // Early exit for right click
+ if (evt.button == WebPointerProperties::Button::kRight)
+ return;
+
+ IntPoint position = FlooredIntPoint(evt.PositionInRootFrame());
+ SetPressedPart(GetTheme().HitTest(*this, position));
+ int pressed_pos = Orientation() == kHorizontalScrollbar
+ ? ConvertFromRootFrame(position).X()
+ : ConvertFromRootFrame(position).Y();
+
+ if ((pressed_part_ == kBackTrackPart || pressed_part_ == kForwardTrackPart) &&
+ GetTheme().ShouldCenterOnThumb(*this, evt)) {
+ SetHoveredPart(kThumbPart);
+ SetPressedPart(kThumbPart);
+ drag_origin_ = current_pos_;
+ int thumb_len = GetTheme().ThumbLength(*this);
+ int desired_pos = pressed_pos;
+ // Set the pressed position to the middle of the thumb so that when we do
+ // the move, the delta will be from the current pixel position of the thumb
+ // to the new desired position for the thumb.
+ pressed_pos_ = GetTheme().TrackPosition(*this) +
+ GetTheme().ThumbPosition(*this) + thumb_len / 2;
+ MoveThumb(desired_pos);
+ return;
+ }
+ if (pressed_part_ == kThumbPart) {
+ drag_origin_ = current_pos_;
+ if (scrollable_area_)
+ scrollable_area_->MouseCapturedScrollbar();
+ }
+
+ pressed_pos_ = pressed_pos;
+
+ AutoscrollPressedPart(GetTheme().InitialAutoscrollTimerDelay());
+}
+
+void Scrollbar::SetScrollbarsHiddenIfOverlay(bool hidden) {
+ if (scrollable_area_)
+ scrollable_area_->SetScrollbarsHiddenIfOverlay(hidden);
+}
+
+void Scrollbar::SetEnabled(bool e) {
+ if (enabled_ == e)
+ return;
+ enabled_ = e;
+ GetTheme().UpdateEnabledState(*this);
+
+ // We can skip thumb/track repaint when hiding an overlay scrollbar, but not
+ // when showing (since the proportions may have changed while hidden).
+ bool skipPartsRepaint = IsOverlayScrollbar() && scrollable_area_ &&
+ scrollable_area_->ScrollbarsHiddenIfOverlay();
+ SetNeedsPaintInvalidation(skipPartsRepaint ? kNoPart : kAllParts);
+}
+
+int Scrollbar::ScrollbarThickness() const {
+ int thickness = Orientation() == kHorizontalScrollbar ? Height() : Width();
+ if (!thickness || !chrome_client_)
+ return thickness;
+ return chrome_client_->WindowToViewportScalar(theme_scrollbar_thickness_);
+}
+
+bool Scrollbar::IsOverlayScrollbar() const {
+ return theme_.UsesOverlayScrollbars();
+}
+
+bool Scrollbar::ShouldParticipateInHitTesting() {
+ // Non-overlay scrollbars should always participate in hit testing.
+ if (!IsOverlayScrollbar())
+ return true;
+ return !scrollable_area_->ScrollbarsHiddenIfOverlay();
+}
+
+bool Scrollbar::IsWindowActive() const {
+ return scrollable_area_ && scrollable_area_->IsActive();
+}
+
+IntPoint Scrollbar::ConvertFromRootFrame(
+ const IntPoint& point_in_root_frame) const {
+ if (scrollable_area_) {
+ IntPoint parent_point =
+ scrollable_area_->ConvertFromRootFrame(point_in_root_frame);
+ return scrollable_area_
+ ->ConvertFromContainingEmbeddedContentViewToScrollbar(*this,
+ parent_point);
+ }
+
+ return point_in_root_frame;
+}
+
+IntRect Scrollbar::ConvertToContainingEmbeddedContentView(
+ const IntRect& local_rect) const {
+ if (scrollable_area_) {
+ return scrollable_area_
+ ->ConvertFromScrollbarToContainingEmbeddedContentView(*this,
+ local_rect);
+ }
+
+ return local_rect;
+}
+
+IntPoint Scrollbar::ConvertFromContainingEmbeddedContentView(
+ const IntPoint& parent_point) const {
+ if (scrollable_area_) {
+ return scrollable_area_
+ ->ConvertFromContainingEmbeddedContentViewToScrollbar(*this,
+ parent_point);
+ }
+
+ return parent_point;
+}
+
+float Scrollbar::ScrollableAreaCurrentPos() const {
+ if (!scrollable_area_)
+ return 0;
+
+ if (orientation_ == kHorizontalScrollbar) {
+ return scrollable_area_->GetScrollOffset().Width() -
+ scrollable_area_->MinimumScrollOffset().Width();
+ }
+
+ return scrollable_area_->GetScrollOffset().Height() -
+ scrollable_area_->MinimumScrollOffset().Height();
+}
+
+float Scrollbar::ScrollableAreaTargetPos() const {
+ if (!scrollable_area_)
+ return 0;
+
+ if (orientation_ == kHorizontalScrollbar) {
+ return scrollable_area_->GetScrollAnimator().DesiredTargetOffset().Width() -
+ scrollable_area_->MinimumScrollOffset().Width();
+ }
+
+ return scrollable_area_->GetScrollAnimator().DesiredTargetOffset().Height() -
+ scrollable_area_->MinimumScrollOffset().Height();
+}
+
+void Scrollbar::SetNeedsPaintInvalidation(ScrollbarPart invalid_parts) {
+ if (theme_.ShouldRepaintAllPartsOnInvalidation())
+ invalid_parts = kAllParts;
+ if (invalid_parts & ~kThumbPart)
+ track_needs_repaint_ = true;
+ if (invalid_parts & kThumbPart)
+ thumb_needs_repaint_ = true;
+ if (scrollable_area_)
+ scrollable_area_->SetScrollbarNeedsPaintInvalidation(Orientation());
+}
+
+STATIC_ASSERT_ENUM(WebScrollbar::ScrollingMode::kAuto, kScrollbarAuto);
+STATIC_ASSERT_ENUM(WebScrollbar::ScrollingMode::kAlwaysOff,
+ kScrollbarAlwaysOff);
+STATIC_ASSERT_ENUM(WebScrollbar::ScrollingMode::kAlwaysOn, kScrollbarAlwaysOn);
+
+STATIC_ASSERT_ENUM(WebScrollbar::kHorizontal, kHorizontalScrollbar);
+STATIC_ASSERT_ENUM(WebScrollbar::kVertical, kVerticalScrollbar);
+
+STATIC_ASSERT_ENUM(WebScrollbar::kScrollByLine, kScrollByLine);
+STATIC_ASSERT_ENUM(WebScrollbar::kScrollByPage, kScrollByPage);
+STATIC_ASSERT_ENUM(WebScrollbar::kScrollByDocument, kScrollByDocument);
+STATIC_ASSERT_ENUM(WebScrollbar::kScrollByPixel, kScrollByPixel);
+
+STATIC_ASSERT_ENUM(WebScrollbar::kRegularScrollbar, kRegularScrollbar);
+STATIC_ASSERT_ENUM(WebScrollbar::kSmallScrollbar, kSmallScrollbar);
+STATIC_ASSERT_ENUM(WebScrollbar::kNoPart, kNoPart);
+STATIC_ASSERT_ENUM(WebScrollbar::kBackButtonStartPart, kBackButtonStartPart);
+STATIC_ASSERT_ENUM(WebScrollbar::kForwardButtonStartPart,
+ kForwardButtonStartPart);
+STATIC_ASSERT_ENUM(WebScrollbar::kBackTrackPart, kBackTrackPart);
+STATIC_ASSERT_ENUM(WebScrollbar::kThumbPart, kThumbPart);
+STATIC_ASSERT_ENUM(WebScrollbar::kForwardTrackPart, kForwardTrackPart);
+STATIC_ASSERT_ENUM(WebScrollbar::kBackButtonEndPart, kBackButtonEndPart);
+STATIC_ASSERT_ENUM(WebScrollbar::kForwardButtonEndPart, kForwardButtonEndPart);
+STATIC_ASSERT_ENUM(WebScrollbar::kScrollbarBGPart, kScrollbarBGPart);
+STATIC_ASSERT_ENUM(WebScrollbar::kTrackBGPart, kTrackBGPart);
+STATIC_ASSERT_ENUM(WebScrollbar::kAllParts, kAllParts);
+STATIC_ASSERT_ENUM(kWebScrollbarOverlayColorThemeDark,
+ kScrollbarOverlayColorThemeDark);
+STATIC_ASSERT_ENUM(kWebScrollbarOverlayColorThemeLight,
+ kScrollbarOverlayColorThemeLight);
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scroll/scrollbar.h b/chromium/third_party/blink/renderer/platform/scroll/scrollbar.h
new file mode 100644
index 00000000000..82099a11f94
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/scrollbar.h
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2004, 2006 Apple Computer, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLLBAR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLLBAR_H_
+
+#include "third_party/blink/renderer/platform/graphics/paint/display_item.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/scroll/scroll_types.h"
+#include "third_party/blink/renderer/platform/scroll/scrollbar_theme_client.h"
+#include "third_party/blink/renderer/platform/timer.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+namespace blink {
+
+class CullRect;
+class GraphicsContext;
+class IntRect;
+class PlatformChromeClient;
+class ScrollableArea;
+class ScrollbarTheme;
+class WebGestureEvent;
+class WebMouseEvent;
+
+class PLATFORM_EXPORT Scrollbar : public GarbageCollectedFinalized<Scrollbar>,
+ public ScrollbarThemeClient,
+ public DisplayItemClient {
+ public:
+ static Scrollbar* Create(ScrollableArea* scrollable_area,
+ ScrollbarOrientation orientation,
+ ScrollbarControlSize size,
+ PlatformChromeClient* chrome_client) {
+ return new Scrollbar(scrollable_area, orientation, size, chrome_client);
+ }
+
+ // Theme object ownership remains with the caller and it must outlive the
+ // scrollbar.
+ static Scrollbar* CreateForTesting(ScrollableArea* scrollable_area,
+ ScrollbarOrientation orientation,
+ ScrollbarControlSize size,
+ ScrollbarTheme* theme) {
+ return new Scrollbar(scrollable_area, orientation, size, nullptr, theme);
+ }
+
+ ~Scrollbar() override;
+
+ // ScrollbarThemeClient implementation.
+ int X() const override { return frame_rect_.X(); }
+ int Y() const override { return frame_rect_.Y(); }
+ int Width() const override { return frame_rect_.Width(); }
+ int Height() const override { return frame_rect_.Height(); }
+ IntSize Size() const override { return frame_rect_.Size(); }
+ IntPoint Location() const override { return frame_rect_.Location(); }
+
+ void SetFrameRect(const IntRect&);
+ IntRect FrameRect() const override { return frame_rect_; }
+
+ ScrollbarOverlayColorTheme GetScrollbarOverlayColorTheme() const override;
+ void GetTickmarks(Vector<IntRect>&) const override;
+ bool IsScrollableAreaActive() const override;
+
+ IntPoint ConvertFromRootFrame(const IntPoint&) const override;
+
+ bool IsCustomScrollbar() const override { return false; }
+ ScrollbarOrientation Orientation() const override { return orientation_; }
+ bool IsLeftSideVerticalScrollbar() const override;
+
+ int Value() const override { return lroundf(current_pos_); }
+ float CurrentPos() const override { return current_pos_; }
+ int VisibleSize() const override { return visible_size_; }
+ int TotalSize() const override { return total_size_; }
+ int Maximum() const override { return total_size_ - visible_size_; }
+ ScrollbarControlSize GetControlSize() const override { return control_size_; }
+
+ ScrollbarPart PressedPart() const override { return pressed_part_; }
+ ScrollbarPart HoveredPart() const override { return hovered_part_; }
+
+ void StyleChanged() override {}
+ void SetScrollbarsHiddenIfOverlay(bool) override;
+ bool Enabled() const override { return enabled_; }
+ void SetEnabled(bool) override;
+
+ // This returns device-scale-factor-aware pixel value.
+ // e.g. 15 in dsf=1.0, 30 in dsf=2.0.
+ // This returns 0 for overlay scrollbars.
+ // See also ScrolbarTheme::scrollbatThickness().
+ int ScrollbarThickness() const;
+
+ // Called by the ScrollableArea when the scroll offset changes.
+ // Will trigger paint invalidation if required.
+ void OffsetDidChange();
+
+ virtual void DisconnectFromScrollableArea();
+ ScrollableArea* GetScrollableArea() const { return scrollable_area_; }
+
+ int PressedPos() const { return pressed_pos_; }
+
+ virtual void SetHoveredPart(ScrollbarPart);
+ virtual void SetPressedPart(ScrollbarPart);
+
+ void SetProportion(int visible_size, int total_size);
+ void SetPressedPos(int p) { pressed_pos_ = p; }
+
+ void Paint(GraphicsContext&, const CullRect&) const;
+
+ bool IsOverlayScrollbar() const override;
+ bool ShouldParticipateInHitTesting();
+
+ bool IsWindowActive() const;
+
+ // Return if the gesture event was handled. |shouldUpdateCapture|
+ // will be set to true if the handler should update the capture
+ // state for this scrollbar.
+ bool GestureEvent(const WebGestureEvent&, bool* should_update_capture);
+
+ // These methods are used for platform scrollbars to give :hover feedback.
+ // They will not get called when the mouse went down in a scrollbar, since it
+ // is assumed the scrollbar will start
+ // grabbing all events in that case anyway.
+ void MouseMoved(const WebMouseEvent&);
+ void MouseEntered();
+ void MouseExited();
+
+ // Used by some platform scrollbars to know when they've been released from
+ // capture.
+ void MouseUp(const WebMouseEvent&);
+ void MouseDown(const WebMouseEvent&);
+
+ ScrollbarTheme& GetTheme() const { return theme_; }
+
+ IntRect ConvertToContainingEmbeddedContentView(const IntRect&) const;
+ IntPoint ConvertFromContainingEmbeddedContentView(const IntPoint&) const;
+
+ void MoveThumb(int pos, bool dragging_document = false);
+
+ float ElasticOverscroll() const override { return elastic_overscroll_; }
+ void SetElasticOverscroll(float elastic_overscroll) override {
+ elastic_overscroll_ = elastic_overscroll;
+ }
+
+ // Use setNeedsPaintInvalidation to cause the scrollbar (or parts thereof)
+ // to repaint.
+ bool TrackNeedsRepaint() const { return track_needs_repaint_; }
+ void ClearTrackNeedsRepaint() { track_needs_repaint_ = false; }
+ bool ThumbNeedsRepaint() const { return thumb_needs_repaint_; }
+ void ClearThumbNeedsRepaint() { thumb_needs_repaint_ = false; }
+
+ // DisplayItemClient methods.
+ String DebugName() const final {
+ return orientation_ == kHorizontalScrollbar ? "HorizontalScrollbar"
+ : "VerticalScrollbar";
+ }
+ LayoutRect VisualRect() const final { return visual_rect_; }
+
+ virtual void SetVisualRect(const LayoutRect& r) { visual_rect_ = r; }
+
+ // Marks the scrollbar as needing to be redrawn.
+ //
+ // If invalid parts are provided, then those parts will also be repainted.
+ // Otherwise, the ScrollableArea may redraw using cached renderings of
+ // individual parts. For instance, if the scrollbar is composited, the thumb
+ // may be cached in a GPU texture (and is only guaranteed to be repainted if
+ // ThumbPart is invalidated).
+ //
+ // Even if no parts are invalidated, the scrollbar may need to be redrawn
+ // if, for instance, the thumb moves without changing the appearance of any
+ // part.
+ void SetNeedsPaintInvalidation(ScrollbarPart invalid_parts);
+
+ // Promptly unregister from the theme manager + run finalizers of derived
+ // Scrollbars.
+ EAGERLY_FINALIZE();
+ DECLARE_EAGER_FINALIZATION_OPERATOR_NEW();
+ virtual void Trace(blink::Visitor*);
+
+ protected:
+ Scrollbar(ScrollableArea*,
+ ScrollbarOrientation,
+ ScrollbarControlSize,
+ PlatformChromeClient* = nullptr,
+ ScrollbarTheme* = nullptr);
+
+ void AutoscrollTimerFired(TimerBase*);
+ void StartTimerIfNeeded(double delay);
+ void StopTimerIfNeeded();
+ void AutoscrollPressedPart(double delay);
+ ScrollDirectionPhysical PressedPartScrollDirectionPhysical();
+ ScrollGranularity PressedPartScrollGranularity();
+
+ Member<ScrollableArea> scrollable_area_;
+ ScrollbarOrientation orientation_;
+ ScrollbarControlSize control_size_;
+ ScrollbarTheme& theme_;
+ Member<PlatformChromeClient> chrome_client_;
+
+ int visible_size_;
+ int total_size_;
+ float current_pos_;
+ float drag_origin_;
+
+ ScrollbarPart hovered_part_;
+ ScrollbarPart pressed_part_;
+ int pressed_pos_;
+ float scroll_pos_;
+ bool dragging_document_;
+ int document_drag_pos_;
+
+ bool enabled_;
+
+ TaskRunnerTimer<Scrollbar> scroll_timer_;
+
+ float elastic_overscroll_;
+
+ private:
+ void Invalidate() override { SetNeedsPaintInvalidation(kAllParts); }
+ void InvalidateRect(const IntRect&) override {
+ SetNeedsPaintInvalidation(kAllParts);
+ }
+
+ float ScrollableAreaCurrentPos() const;
+ float ScrollableAreaTargetPos() const;
+ bool ThumbWillBeUnderMouse() const;
+
+ int theme_scrollbar_thickness_;
+ bool track_needs_repaint_;
+ bool thumb_needs_repaint_;
+ LayoutRect visual_rect_;
+ IntRect frame_rect_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLLBAR_H_
diff --git a/chromium/third_party/blink/renderer/platform/scroll/scrollbar_test_suite.h b/chromium/third_party/blink/renderer/platform/scroll/scrollbar_test_suite.h
new file mode 100644
index 00000000000..0292b6bbc75
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/scrollbar_test_suite.h
@@ -0,0 +1,142 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLLBAR_TEST_SUITE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLLBAR_TEST_SUITE_H_
+
+#include <memory>
+#include "testing/gmock/include/gmock/gmock.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
+#include "third_party/blink/public/platform/web_thread.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/platform_chrome_client.h"
+#include "third_party/blink/renderer/platform/scheduler/child/web_scheduler.h"
+#include "third_party/blink/renderer/platform/scroll/scrollable_area.h"
+#include "third_party/blink/renderer/platform/scroll/scrollbar.h"
+#include "third_party/blink/renderer/platform/scroll/scrollbar_theme_mock.h"
+
+namespace blink {
+
+class MockPlatformChromeClient : public PlatformChromeClient {
+ public:
+ MockPlatformChromeClient() : is_popup_(false) {}
+
+ bool IsPopup() override { return is_popup_; }
+
+ void SetIsPopup(bool is_popup) { is_popup_ = is_popup; }
+
+ void InvalidateRect(const IntRect&) override {}
+
+ IntRect ViewportToScreen(const IntRect&,
+ const PlatformFrameView*) const override {
+ return IntRect();
+ }
+
+ float WindowToViewportScalar(const float) const override { return 0; }
+
+ void ScheduleAnimation(const PlatformFrameView*) override {}
+
+ private:
+ bool is_popup_;
+};
+
+class MockScrollableArea : public GarbageCollectedFinalized<MockScrollableArea>,
+ public ScrollableArea {
+ USING_GARBAGE_COLLECTED_MIXIN(MockScrollableArea);
+
+ public:
+ static MockScrollableArea* Create() { return new MockScrollableArea(); }
+
+ static MockScrollableArea* Create(const ScrollOffset& maximum_scroll_offset) {
+ MockScrollableArea* mock = Create();
+ mock->SetMaximumScrollOffset(maximum_scroll_offset);
+ return mock;
+ }
+
+ MOCK_CONST_METHOD0(VisualRectForScrollbarParts, LayoutRect());
+ MOCK_CONST_METHOD0(IsActive, bool());
+ MOCK_CONST_METHOD1(ScrollSize, int(ScrollbarOrientation));
+ MOCK_CONST_METHOD0(IsScrollCornerVisible, bool());
+ MOCK_CONST_METHOD0(ScrollCornerRect, IntRect());
+ MOCK_CONST_METHOD0(EnclosingScrollableArea, ScrollableArea*());
+ MOCK_CONST_METHOD1(VisibleContentRect, IntRect(IncludeScrollbarsInRect));
+ MOCK_CONST_METHOD0(ContentsSize, IntSize());
+ MOCK_CONST_METHOD0(ScrollableAreaBoundingBox, IntRect());
+ MOCK_CONST_METHOD0(LayerForHorizontalScrollbar, GraphicsLayer*());
+ MOCK_CONST_METHOD0(LayerForVerticalScrollbar, GraphicsLayer*());
+ MOCK_CONST_METHOD0(HorizontalScrollbar, Scrollbar*());
+ MOCK_CONST_METHOD0(VerticalScrollbar, Scrollbar*());
+ MOCK_CONST_METHOD0(ScrollbarsHiddenIfOverlay, bool());
+
+ bool UserInputScrollable(ScrollbarOrientation) const override { return true; }
+ bool ScrollbarsCanBeActive() const override { return true; }
+ bool ShouldPlaceVerticalScrollbarOnLeft() const override { return false; }
+ void UpdateScrollOffset(const ScrollOffset& offset, ScrollType) override {
+ scroll_offset_ = offset.ShrunkTo(maximum_scroll_offset_);
+ }
+ IntSize ScrollOffsetInt() const override {
+ return FlooredIntSize(scroll_offset_);
+ }
+ IntSize MinimumScrollOffsetInt() const override { return IntSize(); }
+ IntSize MaximumScrollOffsetInt() const override {
+ return ExpandedIntSize(maximum_scroll_offset_);
+ }
+ int VisibleHeight() const override { return 768; }
+ int VisibleWidth() const override { return 1024; }
+ CompositorElementId GetCompositorElementId() const override {
+ return CompositorElementId();
+ }
+ bool ScrollAnimatorEnabled() const override { return false; }
+ int PageStep(ScrollbarOrientation) const override { return 0; }
+ void ScrollControlWasSetNeedsPaintInvalidation() {}
+ void SetScrollOrigin(const IntPoint& origin) {
+ ScrollableArea::SetScrollOrigin(origin);
+ }
+
+ scoped_refptr<base::SingleThreadTaskRunner> GetTimerTaskRunner() const final {
+ return blink::scheduler::GetSingleThreadTaskRunnerForTesting();
+ }
+
+ PlatformChromeClient* GetChromeClient() const override {
+ return chrome_client_.Get();
+ }
+
+ void SetIsPopup() { chrome_client_->SetIsPopup(true); }
+
+ ScrollbarTheme& GetPageScrollbarTheme() const override {
+ return ScrollbarTheme::DeprecatedStaticGetTheme();
+ }
+
+ using ScrollableArea::ShowOverlayScrollbars;
+ using ScrollableArea::HorizontalScrollbarNeedsPaintInvalidation;
+ using ScrollableArea::VerticalScrollbarNeedsPaintInvalidation;
+ using ScrollableArea::ClearNeedsPaintInvalidationForScrollControls;
+
+ virtual void Trace(blink::Visitor* visitor) {
+ visitor->Trace(chrome_client_);
+ ScrollableArea::Trace(visitor);
+ }
+
+ protected:
+ explicit MockScrollableArea()
+ : maximum_scroll_offset_(ScrollOffset(0, 100)),
+ chrome_client_(new MockPlatformChromeClient()) {}
+ explicit MockScrollableArea(const ScrollOffset& offset)
+ : maximum_scroll_offset_(offset),
+ chrome_client_(new MockPlatformChromeClient()) {}
+
+ private:
+ void SetMaximumScrollOffset(const ScrollOffset& maximum_scroll_offset) {
+ maximum_scroll_offset_ = maximum_scroll_offset;
+ }
+
+ ScrollOffset scroll_offset_;
+ ScrollOffset maximum_scroll_offset_;
+ Member<MockPlatformChromeClient> chrome_client_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme.cc b/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme.cc
new file mode 100644
index 00000000000..d45f26cf4b0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme.cc
@@ -0,0 +1,435 @@
+/*
+ * Copyright (C) 2011 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 "third_party/blink/renderer/platform/scroll/scrollbar_theme.h"
+
+#include "build/build_config.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_mouse_event.h"
+#include "third_party/blink/public/platform/web_point.h"
+#include "third_party/blink/public/platform/web_rect.h"
+#include "third_party/blink/public/platform/web_scrollbar_behavior.h"
+#include "third_party/blink/renderer/platform/graphics/color.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context_state_saver.h"
+#include "third_party/blink/renderer/platform/graphics/paint/cull_rect.h"
+#include "third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h"
+#include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/scroll/scrollbar.h"
+#include "third_party/blink/renderer/platform/scroll/scrollbar_theme_mock.h"
+#include "third_party/blink/renderer/platform/scroll/scrollbar_theme_overlay_mock.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+
+#if !defined(OS_MACOSX)
+#include "third_party/blink/public/platform/web_theme_engine.h"
+#endif
+
+namespace blink {
+
+bool ScrollbarTheme::g_mock_scrollbars_enabled_ = false;
+
+static inline bool ShouldPaintScrollbarPart(const IntRect& part_rect,
+ const CullRect& cull_rect) {
+ return (!part_rect.IsEmpty()) || cull_rect.IntersectsCullRect(part_rect);
+}
+
+bool ScrollbarTheme::Paint(const Scrollbar& scrollbar,
+ GraphicsContext& graphics_context,
+ const CullRect& cull_rect) {
+ // Create the ScrollbarControlPartMask based on the cullRect
+ ScrollbarControlPartMask scroll_mask = kNoPart;
+
+ IntRect back_button_start_paint_rect;
+ IntRect back_button_end_paint_rect;
+ IntRect forward_button_start_paint_rect;
+ IntRect forward_button_end_paint_rect;
+ if (HasButtons(scrollbar)) {
+ back_button_start_paint_rect =
+ BackButtonRect(scrollbar, kBackButtonStartPart, true);
+ if (ShouldPaintScrollbarPart(back_button_start_paint_rect, cull_rect))
+ scroll_mask |= kBackButtonStartPart;
+ back_button_end_paint_rect =
+ BackButtonRect(scrollbar, kBackButtonEndPart, true);
+ if (ShouldPaintScrollbarPart(back_button_end_paint_rect, cull_rect))
+ scroll_mask |= kBackButtonEndPart;
+ forward_button_start_paint_rect =
+ ForwardButtonRect(scrollbar, kForwardButtonStartPart, true);
+ if (ShouldPaintScrollbarPart(forward_button_start_paint_rect, cull_rect))
+ scroll_mask |= kForwardButtonStartPart;
+ forward_button_end_paint_rect =
+ ForwardButtonRect(scrollbar, kForwardButtonEndPart, true);
+ if (ShouldPaintScrollbarPart(forward_button_end_paint_rect, cull_rect))
+ scroll_mask |= kForwardButtonEndPart;
+ }
+
+ IntRect start_track_rect;
+ IntRect thumb_rect;
+ IntRect end_track_rect;
+ IntRect track_paint_rect = TrackRect(scrollbar, true);
+ scroll_mask |= kTrackBGPart;
+ bool thumb_present = HasThumb(scrollbar);
+ if (thumb_present) {
+ IntRect track = TrackRect(scrollbar);
+ SplitTrack(scrollbar, track, start_track_rect, thumb_rect, end_track_rect);
+ if (ShouldPaintScrollbarPart(thumb_rect, cull_rect))
+ scroll_mask |= kThumbPart;
+ if (ShouldPaintScrollbarPart(start_track_rect, cull_rect))
+ scroll_mask |= kBackTrackPart;
+ if (ShouldPaintScrollbarPart(end_track_rect, cull_rect))
+ scroll_mask |= kForwardTrackPart;
+ }
+
+ // Paint the scrollbar background (only used by custom CSS scrollbars).
+ PaintScrollbarBackground(graphics_context, scrollbar);
+
+ // Paint the back and forward buttons.
+ if (scroll_mask & kBackButtonStartPart)
+ PaintButton(graphics_context, scrollbar, back_button_start_paint_rect,
+ kBackButtonStartPart);
+ if (scroll_mask & kBackButtonEndPart)
+ PaintButton(graphics_context, scrollbar, back_button_end_paint_rect,
+ kBackButtonEndPart);
+ if (scroll_mask & kForwardButtonStartPart)
+ PaintButton(graphics_context, scrollbar, forward_button_start_paint_rect,
+ kForwardButtonStartPart);
+ if (scroll_mask & kForwardButtonEndPart)
+ PaintButton(graphics_context, scrollbar, forward_button_end_paint_rect,
+ kForwardButtonEndPart);
+
+ if (scroll_mask & kTrackBGPart)
+ PaintTrackBackground(graphics_context, scrollbar, track_paint_rect);
+
+ if ((scroll_mask & kForwardTrackPart) || (scroll_mask & kBackTrackPart)) {
+ // Paint the track pieces above and below the thumb.
+ if (scroll_mask & kBackTrackPart)
+ PaintTrackPiece(graphics_context, scrollbar, start_track_rect,
+ kBackTrackPart);
+ if (scroll_mask & kForwardTrackPart)
+ PaintTrackPiece(graphics_context, scrollbar, end_track_rect,
+ kForwardTrackPart);
+
+ PaintTickmarks(graphics_context, scrollbar, track_paint_rect);
+ }
+
+ if (scroll_mask & kThumbPart)
+ PaintThumbWithOpacity(graphics_context, scrollbar, thumb_rect);
+
+ return true;
+}
+
+ScrollbarPart ScrollbarTheme::HitTest(const ScrollbarThemeClient& scrollbar,
+ const IntPoint& position_in_root_frame) {
+ ScrollbarPart result = kNoPart;
+ if (!scrollbar.Enabled())
+ return result;
+
+ IntPoint test_position =
+ scrollbar.ConvertFromRootFrame(position_in_root_frame);
+ test_position.Move(scrollbar.X(), scrollbar.Y());
+
+ if (!scrollbar.FrameRect().Contains(test_position))
+ return kNoPart;
+
+ result = kScrollbarBGPart;
+
+ IntRect track = TrackRect(scrollbar);
+ if (track.Contains(test_position)) {
+ IntRect before_thumb_rect;
+ IntRect thumb_rect;
+ IntRect after_thumb_rect;
+ SplitTrack(scrollbar, track, before_thumb_rect, thumb_rect,
+ after_thumb_rect);
+ if (thumb_rect.Contains(test_position))
+ result = kThumbPart;
+ else if (before_thumb_rect.Contains(test_position))
+ result = kBackTrackPart;
+ else if (after_thumb_rect.Contains(test_position))
+ result = kForwardTrackPart;
+ else
+ result = kTrackBGPart;
+ } else if (BackButtonRect(scrollbar, kBackButtonStartPart)
+ .Contains(test_position)) {
+ result = kBackButtonStartPart;
+ } else if (BackButtonRect(scrollbar, kBackButtonEndPart)
+ .Contains(test_position)) {
+ result = kBackButtonEndPart;
+ } else if (ForwardButtonRect(scrollbar, kForwardButtonStartPart)
+ .Contains(test_position)) {
+ result = kForwardButtonStartPart;
+ } else if (ForwardButtonRect(scrollbar, kForwardButtonEndPart)
+ .Contains(test_position)) {
+ result = kForwardButtonEndPart;
+ }
+ return result;
+}
+
+void ScrollbarTheme::PaintScrollCorner(
+ GraphicsContext& context,
+ const DisplayItemClient& display_item_client,
+ const IntRect& corner_rect) {
+ if (corner_rect.IsEmpty())
+ return;
+
+ if (DrawingRecorder::UseCachedDrawingIfPossible(
+ context, display_item_client, DisplayItem::kScrollbarCorner))
+ return;
+
+ DrawingRecorder recorder(context, display_item_client,
+ DisplayItem::kScrollbarCorner);
+#if defined(OS_MACOSX)
+ context.FillRect(corner_rect, Color::kWhite);
+#else
+ Platform::Current()->ThemeEngine()->Paint(
+ context.Canvas(), WebThemeEngine::kPartScrollbarCorner,
+ WebThemeEngine::kStateNormal, WebRect(corner_rect), nullptr);
+#endif
+}
+
+bool ScrollbarTheme::ShouldCenterOnThumb(const ScrollbarThemeClient& scrollbar,
+ const WebMouseEvent& evt) {
+ return Platform::Current()->ScrollbarBehavior()->ShouldCenterOnThumb(
+ evt.button, evt.GetModifiers() & WebInputEvent::kShiftKey,
+ evt.GetModifiers() & WebInputEvent::kAltKey);
+}
+
+void ScrollbarTheme::PaintTickmarks(GraphicsContext& context,
+ const Scrollbar& scrollbar,
+ const IntRect& rect) {
+// Android paints tickmarks in the browser at FindResultBar.java.
+#if !defined(OS_ANDROID)
+ if (scrollbar.Orientation() != kVerticalScrollbar)
+ return;
+
+ if (rect.Height() <= 0 || rect.Width() <= 0)
+ return;
+
+ // Get the tickmarks for the frameview.
+ Vector<IntRect> tickmarks;
+ scrollbar.GetTickmarks(tickmarks);
+ if (!tickmarks.size())
+ return;
+
+ if (DrawingRecorder::UseCachedDrawingIfPossible(
+ context, scrollbar, DisplayItem::kScrollbarTickmarks))
+ return;
+
+ DrawingRecorder recorder(context, scrollbar,
+ DisplayItem::kScrollbarTickmarks);
+ GraphicsContextStateSaver state_saver(context);
+ context.SetShouldAntialias(false);
+
+ for (Vector<IntRect>::const_iterator i = tickmarks.begin();
+ i != tickmarks.end(); ++i) {
+ // Calculate how far down (in %) the tick-mark should appear.
+ const float percent = static_cast<float>(i->Y()) / scrollbar.TotalSize();
+
+ // Calculate how far down (in pixels) the tick-mark should appear.
+ const int y_pos = rect.Y() + (rect.Height() * percent);
+
+ FloatRect tick_rect(rect.X(), y_pos, rect.Width(), 3);
+ context.FillRect(tick_rect, Color(0xCC, 0xAA, 0x00, 0xFF));
+
+ FloatRect tick_stroke(rect.X() + TickmarkBorderWidth(), y_pos + 1,
+ rect.Width() - 2 * TickmarkBorderWidth(), 1);
+ context.FillRect(tick_stroke, Color(0xFF, 0xDD, 0x00, 0xFF));
+ }
+#endif
+}
+
+bool ScrollbarTheme::ShouldSnapBackToDragOrigin(
+ const ScrollbarThemeClient& scrollbar,
+ const WebMouseEvent& evt) {
+ IntPoint mouse_position = scrollbar.ConvertFromRootFrame(
+ FlooredIntPoint(evt.PositionInRootFrame()));
+ mouse_position.Move(scrollbar.X(), scrollbar.Y());
+ return Platform::Current()->ScrollbarBehavior()->ShouldSnapBackToDragOrigin(
+ mouse_position, TrackRect(scrollbar),
+ scrollbar.Orientation() == kHorizontalScrollbar);
+}
+
+double ScrollbarTheme::OverlayScrollbarFadeOutDelaySeconds() const {
+ // On Mac, fading is controlled by the painting code in ScrollAnimatorMac.
+ return 0.0;
+}
+
+double ScrollbarTheme::OverlayScrollbarFadeOutDurationSeconds() const {
+ // On Mac, fading is controlled by the painting code in ScrollAnimatorMac.
+ return 0.0;
+}
+
+int ScrollbarTheme::ThumbPosition(const ScrollbarThemeClient& scrollbar,
+ float scroll_position) {
+ if (scrollbar.Enabled()) {
+ float size = scrollbar.TotalSize() - scrollbar.VisibleSize();
+ // Avoid doing a floating point divide by zero and return 1 when
+ // usedTotalSize == visibleSize.
+ if (!size)
+ return 0;
+ float pos = std::max(0.0f, scroll_position) *
+ (TrackLength(scrollbar) - ThumbLength(scrollbar)) / size;
+ return (pos < 1 && pos > 0) ? 1 : pos;
+ }
+ return 0;
+}
+
+int ScrollbarTheme::ThumbLength(const ScrollbarThemeClient& scrollbar) {
+ if (!scrollbar.Enabled())
+ return 0;
+
+ float overhang = fabsf(scrollbar.ElasticOverscroll());
+ float proportion = 0.0f;
+ float total_size = scrollbar.TotalSize();
+ if (total_size > 0.0f) {
+ proportion = (scrollbar.VisibleSize() - overhang) / total_size;
+ }
+ int track_len = TrackLength(scrollbar);
+ int length = round(proportion * track_len);
+ length = std::max(length, MinimumThumbLength(scrollbar));
+ if (length > track_len)
+ length = 0; // Once the thumb is below the track length, it just goes away
+ // (to make more room for the track).
+ return length;
+}
+
+int ScrollbarTheme::TrackPosition(const ScrollbarThemeClient& scrollbar) {
+ IntRect constrained_track_rect =
+ ConstrainTrackRectToTrackPieces(scrollbar, TrackRect(scrollbar));
+ return (scrollbar.Orientation() == kHorizontalScrollbar)
+ ? constrained_track_rect.X() - scrollbar.X()
+ : constrained_track_rect.Y() - scrollbar.Y();
+}
+
+int ScrollbarTheme::TrackLength(const ScrollbarThemeClient& scrollbar) {
+ IntRect constrained_track_rect =
+ ConstrainTrackRectToTrackPieces(scrollbar, TrackRect(scrollbar));
+ return (scrollbar.Orientation() == kHorizontalScrollbar)
+ ? constrained_track_rect.Width()
+ : constrained_track_rect.Height();
+}
+
+IntRect ScrollbarTheme::ThumbRect(const ScrollbarThemeClient& scrollbar) {
+ if (!HasThumb(scrollbar))
+ return IntRect();
+
+ IntRect track = TrackRect(scrollbar);
+ IntRect start_track_rect;
+ IntRect thumb_rect;
+ IntRect end_track_rect;
+ SplitTrack(scrollbar, track, start_track_rect, thumb_rect, end_track_rect);
+
+ return thumb_rect;
+}
+
+int ScrollbarTheme::ThumbThickness(const ScrollbarThemeClient& scrollbar) {
+ IntRect track = TrackRect(scrollbar);
+ return scrollbar.Orientation() == kHorizontalScrollbar ? track.Height()
+ : track.Width();
+}
+
+void ScrollbarTheme::SplitTrack(const ScrollbarThemeClient& scrollbar,
+ const IntRect& unconstrained_track_rect,
+ IntRect& before_thumb_rect,
+ IntRect& thumb_rect,
+ IntRect& after_thumb_rect) {
+ // This function won't even get called unless we're big enough to have some
+ // combination of these three rects where at least one of them is non-empty.
+ IntRect track_rect =
+ ConstrainTrackRectToTrackPieces(scrollbar, unconstrained_track_rect);
+ int thumb_pos = ThumbPosition(scrollbar);
+ if (scrollbar.Orientation() == kHorizontalScrollbar) {
+ thumb_rect = IntRect(track_rect.X() + thumb_pos, track_rect.Y(),
+ ThumbLength(scrollbar), scrollbar.Height());
+ before_thumb_rect =
+ IntRect(track_rect.X(), track_rect.Y(),
+ thumb_pos + thumb_rect.Width() / 2, track_rect.Height());
+ after_thumb_rect = IntRect(
+ track_rect.X() + before_thumb_rect.Width(), track_rect.Y(),
+ track_rect.MaxX() - before_thumb_rect.MaxX(), track_rect.Height());
+ } else {
+ thumb_rect = IntRect(track_rect.X(), track_rect.Y() + thumb_pos,
+ scrollbar.Width(), ThumbLength(scrollbar));
+ before_thumb_rect =
+ IntRect(track_rect.X(), track_rect.Y(), track_rect.Width(),
+ thumb_pos + thumb_rect.Height() / 2);
+ after_thumb_rect = IntRect(
+ track_rect.X(), track_rect.Y() + before_thumb_rect.Height(),
+ track_rect.Width(), track_rect.MaxY() - before_thumb_rect.MaxY());
+ }
+}
+
+ScrollbarTheme& ScrollbarTheme::DeprecatedStaticGetTheme() {
+ if (ScrollbarTheme::MockScrollbarsEnabled()) {
+ if (RuntimeEnabledFeatures::OverlayScrollbarsEnabled()) {
+ DEFINE_STATIC_LOCAL(ScrollbarThemeOverlayMock, overlay_mock_theme, ());
+ return overlay_mock_theme;
+ }
+
+ DEFINE_STATIC_LOCAL(ScrollbarThemeMock, mock_theme, ());
+ return mock_theme;
+ }
+ return NativeTheme();
+}
+
+void ScrollbarTheme::SetMockScrollbarsEnabled(bool flag) {
+ g_mock_scrollbars_enabled_ = flag;
+}
+
+bool ScrollbarTheme::MockScrollbarsEnabled() {
+ return g_mock_scrollbars_enabled_;
+}
+
+DisplayItem::Type ScrollbarTheme::ButtonPartToDisplayItemType(
+ ScrollbarPart part) {
+ switch (part) {
+ case kBackButtonStartPart:
+ return DisplayItem::kScrollbarBackButtonStart;
+ case kBackButtonEndPart:
+ return DisplayItem::kScrollbarBackButtonEnd;
+ case kForwardButtonStartPart:
+ return DisplayItem::kScrollbarForwardButtonStart;
+ case kForwardButtonEndPart:
+ return DisplayItem::kScrollbarForwardButtonEnd;
+ default:
+ NOTREACHED();
+ return DisplayItem::kScrollbarBackButtonStart;
+ }
+}
+
+DisplayItem::Type ScrollbarTheme::TrackPiecePartToDisplayItemType(
+ ScrollbarPart part) {
+ switch (part) {
+ case kBackTrackPart:
+ return DisplayItem::kScrollbarBackTrack;
+ case kForwardTrackPart:
+ return DisplayItem::kScrollbarForwardTrack;
+ default:
+ NOTREACHED();
+ return DisplayItem::kScrollbarBackTrack;
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme.h b/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme.h
new file mode 100644
index 00000000000..624ace28340
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme.h
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2008 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLLBAR_THEME_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLLBAR_THEME_H_
+
+#include "third_party/blink/public/platform/web_scrollbar_buttons_placement.h"
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/graphics/paint/display_item.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/scroll/scroll_types.h"
+#include "third_party/blink/renderer/platform/scroll/scrollbar.h"
+
+namespace blink {
+
+class CullRect;
+class GraphicsContext;
+class WebMouseEvent;
+
+class PLATFORM_EXPORT ScrollbarTheme {
+ WTF_MAKE_NONCOPYABLE(ScrollbarTheme);
+ USING_FAST_MALLOC(ScrollbarTheme);
+
+ public:
+ ScrollbarTheme() = default;
+ virtual ~ScrollbarTheme() = default;
+
+ // If true, then scrollbars with this theme will be painted every time
+ // Scrollbar::setNeedsPaintInvalidation is called. If false, then only parts
+ // which are explicitly invalidated will be repainted.
+ virtual bool ShouldRepaintAllPartsOnInvalidation() const { return true; }
+
+ virtual void UpdateEnabledState(const ScrollbarThemeClient&) {}
+
+ virtual bool Paint(const Scrollbar&, GraphicsContext&, const CullRect&);
+
+ virtual ScrollbarPart HitTest(const ScrollbarThemeClient&, const IntPoint&);
+
+ // This returns a fixed value regardless of device-scale-factor.
+ // This returns thickness when scrollbar is painted. i.e. It's not 0 even in
+ // overlay scrollbar mode.
+ // See also Scrollbar::scrollbarThickness().
+ virtual int ScrollbarThickness(ScrollbarControlSize = kRegularScrollbar) {
+ return 0;
+ }
+ virtual int ScrollbarMargin() const { return 0; }
+
+ virtual WebScrollbarButtonsPlacement ButtonsPlacement() const {
+ return kWebScrollbarButtonsPlacementSingle;
+ }
+
+ virtual bool SupportsControlTints() const { return false; }
+ virtual bool UsesOverlayScrollbars() const { return false; }
+ virtual void UpdateScrollbarOverlayColorTheme(const ScrollbarThemeClient&) {}
+
+ // If true, scrollbars that become invisible (i.e. overlay scrollbars that
+ // fade out) should be marked as disabled. This option exists since Mac and
+ // Aura overlays implement the fade out differently, with Mac painting code
+ // fading out the scrollbars. Aura scrollbars require disabling the scrollbar
+ // to prevent painting it.
+ virtual bool ShouldDisableInvisibleScrollbars() const { return true; }
+
+ virtual bool InvalidateOnMouseEnterExit() { return false; }
+ virtual bool InvalidateOnWindowActiveChange() const { return false; }
+
+ // Returns parts of the scrollbar which must be repainted following a change
+ // in the thumb position, given scroll positions before and after.
+ virtual ScrollbarPart InvalidateOnThumbPositionChange(
+ const ScrollbarThemeClient&,
+ float old_position,
+ float new_position) const {
+ return kAllParts;
+ }
+
+ virtual void PaintScrollCorner(GraphicsContext&,
+ const DisplayItemClient&,
+ const IntRect& corner_rect);
+ virtual void PaintTickmarks(GraphicsContext&,
+ const Scrollbar&,
+ const IntRect&);
+
+ virtual bool ShouldCenterOnThumb(const ScrollbarThemeClient&,
+ const WebMouseEvent&);
+ virtual bool ShouldSnapBackToDragOrigin(const ScrollbarThemeClient&,
+ const WebMouseEvent&);
+ virtual bool ShouldDragDocumentInsteadOfThumb(const ScrollbarThemeClient&,
+ const WebMouseEvent&) {
+ return false;
+ }
+
+ // The position of the thumb relative to the track.
+ int ThumbPosition(const ScrollbarThemeClient& scrollbar) {
+ return ThumbPosition(scrollbar, scrollbar.CurrentPos());
+ }
+ virtual double OverlayScrollbarFadeOutDelaySeconds() const;
+ virtual double OverlayScrollbarFadeOutDurationSeconds() const;
+ // The position the thumb would have, relative to the track, at the specified
+ // scroll position.
+ virtual int ThumbPosition(const ScrollbarThemeClient&, float scroll_position);
+ // The length of the thumb along the axis of the scrollbar.
+ virtual int ThumbLength(const ScrollbarThemeClient&);
+ // The position of the track relative to the scrollbar.
+ virtual int TrackPosition(const ScrollbarThemeClient&);
+ // The length of the track along the axis of the scrollbar.
+ virtual int TrackLength(const ScrollbarThemeClient&);
+ // The opacity to be applied to the thumb. A theme overriding ThumbOpacity()
+ // should also override PaintThumbWithOpacity().
+ virtual float ThumbOpacity(const ScrollbarThemeClient&) const { return 1.0f; }
+
+ virtual bool HasButtons(const ScrollbarThemeClient&) = 0;
+ virtual bool HasThumb(const ScrollbarThemeClient&) = 0;
+
+ virtual IntRect BackButtonRect(const ScrollbarThemeClient&,
+ ScrollbarPart,
+ bool painting = false) = 0;
+ virtual IntRect ForwardButtonRect(const ScrollbarThemeClient&,
+ ScrollbarPart,
+ bool painting = false) = 0;
+ virtual IntRect TrackRect(const ScrollbarThemeClient&,
+ bool painting = false) = 0;
+ virtual IntRect ThumbRect(const ScrollbarThemeClient&);
+ virtual int ThumbThickness(const ScrollbarThemeClient&);
+
+ virtual int MinimumThumbLength(const ScrollbarThemeClient&) = 0;
+
+ virtual void SplitTrack(const ScrollbarThemeClient&,
+ const IntRect& track,
+ IntRect& start_track,
+ IntRect& thumb,
+ IntRect& end_track);
+
+ virtual void PaintScrollbarBackground(GraphicsContext&, const Scrollbar&) {}
+ virtual void PaintTrackBackground(GraphicsContext&,
+ const Scrollbar&,
+ const IntRect&) {}
+ virtual void PaintTrackPiece(GraphicsContext&,
+ const Scrollbar&,
+ const IntRect&,
+ ScrollbarPart) {}
+ virtual void PaintButton(GraphicsContext&,
+ const Scrollbar&,
+ const IntRect&,
+ ScrollbarPart) {}
+ virtual void PaintThumb(GraphicsContext&, const Scrollbar&, const IntRect&) {}
+
+ // Paint the thumb with ThumbOpacity() applied.
+ virtual void PaintThumbWithOpacity(GraphicsContext& context,
+ const Scrollbar& scrollbar,
+ const IntRect& rect) {
+ // By default this method just calls PaintThumb(). A theme with custom
+ // ThumbOpacity() should override this method to apply the opacity.
+ DCHECK_EQ(1.0f, ThumbOpacity(scrollbar));
+ PaintThumb(context, scrollbar, rect);
+ }
+
+ virtual int MaxOverlapBetweenPages() {
+ return std::numeric_limits<int>::max();
+ }
+
+ virtual double InitialAutoscrollTimerDelay() { return 0.25; }
+ virtual double AutoscrollTimerDelay() { return 0.05; }
+
+ virtual IntRect ConstrainTrackRectToTrackPieces(const ScrollbarThemeClient&,
+ const IntRect& rect) {
+ return rect;
+ }
+
+ virtual void RegisterScrollbar(ScrollbarThemeClient&) {}
+ virtual void UnregisterScrollbar(ScrollbarThemeClient&) {}
+
+ virtual bool IsMockTheme() const { return false; }
+
+ virtual bool UsesNinePatchThumbResource() const { return false; }
+
+ // For a nine-patch scrollbar, this defines the painting canvas size which the
+ // painting code will use to paint the scrollbar into. The actual scrollbar
+ // dimensions will be ignored for purposes of painting since the resource can
+ // be then resized without a repaint.
+ virtual IntSize NinePatchThumbCanvasSize(const ScrollbarThemeClient&) const {
+ NOTREACHED();
+ return IntSize();
+ }
+
+ // For a nine-patch resource, the aperture defines the center patch that will
+ // be stretched out.
+ virtual IntRect NinePatchThumbAperture(const ScrollbarThemeClient&) const {
+ NOTREACHED();
+ return IntRect();
+ }
+
+ // Warning: Please call Page::GetScrollbarTheme instead of call this method
+ // directly since we support different native scrollbar theme base on page
+ // settings. See crrev.com/c/646727, this function will eventually be removed.
+ static ScrollbarTheme& DeprecatedStaticGetTheme();
+
+ static void SetMockScrollbarsEnabled(bool flag);
+ static bool MockScrollbarsEnabled();
+
+ protected:
+ virtual int TickmarkBorderWidth() { return 0; }
+ static DisplayItem::Type ButtonPartToDisplayItemType(ScrollbarPart);
+ static DisplayItem::Type TrackPiecePartToDisplayItemType(ScrollbarPart);
+
+ private:
+ static ScrollbarTheme&
+ NativeTheme(); // Must be implemented to return the correct theme subclass.
+ static bool g_mock_scrollbars_enabled_;
+};
+
+} // namespace blink
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_android.cc b/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_android.cc
new file mode 100644
index 00000000000..325abb2a37b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_android.cc
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/scroll/scrollbar_theme.h"
+
+#include "third_party/blink/renderer/platform/scroll/scrollbar_theme_overlay.h"
+
+namespace blink {
+
+ScrollbarTheme& ScrollbarTheme::NativeTheme() {
+ return ScrollbarThemeOverlay::MobileTheme();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_aura.cc b/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_aura.cc
new file mode 100644
index 00000000000..1633b0301bf
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_aura.cc
@@ -0,0 +1,364 @@
+/*
+ * Copyright (c) 2008, 2009, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/renderer/platform/scroll/scrollbar_theme_aura.h"
+
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_rect.h"
+#include "third_party/blink/public/platform/web_theme_engine.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
+#include "third_party/blink/renderer/platform/layout_test_support.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/scroll/scrollable_area.h"
+#include "third_party/blink/renderer/platform/scroll/scrollbar.h"
+#include "third_party/blink/renderer/platform/scroll/scrollbar_theme_overlay.h"
+
+namespace blink {
+
+namespace {
+
+static bool UseMockTheme() {
+ return LayoutTestSupport::IsRunningLayoutTest();
+}
+
+// Contains a flag indicating whether WebThemeEngine should paint a UI widget
+// for a scrollbar part, and if so, what part and state apply.
+//
+// If the PartPaintingParams are not affected by a change in the scrollbar
+// state, then the corresponding scrollbar part does not need to be repainted.
+struct PartPaintingParams {
+ PartPaintingParams()
+ : should_paint(false),
+ part(WebThemeEngine::kPartScrollbarDownArrow),
+ state(WebThemeEngine::kStateNormal) {}
+ PartPaintingParams(WebThemeEngine::Part part, WebThemeEngine::State state)
+ : should_paint(true), part(part), state(state) {}
+
+ bool should_paint;
+ WebThemeEngine::Part part;
+ WebThemeEngine::State state;
+};
+
+bool operator==(const PartPaintingParams& a, const PartPaintingParams& b) {
+ return (!a.should_paint && !b.should_paint) ||
+ std::tie(a.should_paint, a.part, a.state) ==
+ std::tie(b.should_paint, b.part, b.state);
+}
+
+bool operator!=(const PartPaintingParams& a, const PartPaintingParams& b) {
+ return !(a == b);
+}
+
+PartPaintingParams ButtonPartPaintingParams(
+ const ScrollbarThemeClient& scrollbar,
+ float position,
+ ScrollbarPart part) {
+ WebThemeEngine::Part paint_part;
+ WebThemeEngine::State state = WebThemeEngine::kStateNormal;
+ bool check_min = false;
+ bool check_max = false;
+
+ if (scrollbar.Orientation() == kHorizontalScrollbar) {
+ if (part == kBackButtonStartPart) {
+ paint_part = WebThemeEngine::kPartScrollbarLeftArrow;
+ check_min = true;
+ } else if (UseMockTheme() && part != kForwardButtonEndPart) {
+ return PartPaintingParams();
+ } else {
+ paint_part = WebThemeEngine::kPartScrollbarRightArrow;
+ check_max = true;
+ }
+ } else {
+ if (part == kBackButtonStartPart) {
+ paint_part = WebThemeEngine::kPartScrollbarUpArrow;
+ check_min = true;
+ } else if (UseMockTheme() && part != kForwardButtonEndPart) {
+ return PartPaintingParams();
+ } else {
+ paint_part = WebThemeEngine::kPartScrollbarDownArrow;
+ check_max = true;
+ }
+ }
+
+ if (UseMockTheme() && !scrollbar.Enabled()) {
+ state = WebThemeEngine::kStateDisabled;
+ } else if (!UseMockTheme() &&
+ ((check_min && (position <= 0)) ||
+ (check_max && position >= scrollbar.Maximum()))) {
+ state = WebThemeEngine::kStateDisabled;
+ } else {
+ if (part == scrollbar.PressedPart())
+ state = WebThemeEngine::kStatePressed;
+ else if (part == scrollbar.HoveredPart())
+ state = WebThemeEngine::kStateHover;
+ }
+
+ return PartPaintingParams(paint_part, state);
+}
+
+static int GetScrollbarThickness() {
+ return Platform::Current()
+ ->ThemeEngine()
+ ->GetSize(WebThemeEngine::kPartScrollbarVerticalThumb)
+ .width;
+}
+
+} // namespace
+
+ScrollbarTheme& ScrollbarTheme::NativeTheme() {
+ if (RuntimeEnabledFeatures::OverlayScrollbarsEnabled()) {
+ DEFINE_STATIC_LOCAL(
+ ScrollbarThemeOverlay, theme,
+ (GetScrollbarThickness(), 0, ScrollbarThemeOverlay::kAllowHitTest));
+ return theme;
+ }
+
+ DEFINE_STATIC_LOCAL(ScrollbarThemeAura, theme, ());
+ return theme;
+}
+
+int ScrollbarThemeAura::ScrollbarThickness(ScrollbarControlSize control_size) {
+ // Horiz and Vert scrollbars are the same thickness.
+ // In unit tests we don't have the mock theme engine (because of layering
+ // violations), so we hard code the size (see bug 327470).
+ if (UseMockTheme())
+ return 15;
+ IntSize scrollbar_size = Platform::Current()->ThemeEngine()->GetSize(
+ WebThemeEngine::kPartScrollbarVerticalTrack);
+ return scrollbar_size.Width();
+}
+
+bool ScrollbarThemeAura::HasThumb(const ScrollbarThemeClient& scrollbar) {
+ // This method is just called as a paint-time optimization to see if
+ // painting the thumb can be skipped. We don't have to be exact here.
+ return ThumbLength(scrollbar) > 0;
+}
+
+IntRect ScrollbarThemeAura::BackButtonRect(
+ const ScrollbarThemeClient& scrollbar,
+ ScrollbarPart part,
+ bool) {
+ // Windows and Linux just have single arrows.
+ if (part == kBackButtonEndPart)
+ return IntRect();
+
+ IntSize size = ButtonSize(scrollbar);
+ return IntRect(scrollbar.X(), scrollbar.Y(), size.Width(), size.Height());
+}
+
+IntRect ScrollbarThemeAura::ForwardButtonRect(
+ const ScrollbarThemeClient& scrollbar,
+ ScrollbarPart part,
+ bool) {
+ // Windows and Linux just have single arrows.
+ if (part == kForwardButtonStartPart)
+ return IntRect();
+
+ IntSize size = ButtonSize(scrollbar);
+ int x, y;
+ if (scrollbar.Orientation() == kHorizontalScrollbar) {
+ x = scrollbar.X() + scrollbar.Width() - size.Width();
+ y = scrollbar.Y();
+ } else {
+ x = scrollbar.X();
+ y = scrollbar.Y() + scrollbar.Height() - size.Height();
+ }
+ return IntRect(x, y, size.Width(), size.Height());
+}
+
+IntRect ScrollbarThemeAura::TrackRect(const ScrollbarThemeClient& scrollbar,
+ bool) {
+ // The track occupies all space between the two buttons.
+ IntSize bs = ButtonSize(scrollbar);
+ if (scrollbar.Orientation() == kHorizontalScrollbar) {
+ if (scrollbar.Width() <= 2 * bs.Width())
+ return IntRect();
+ return IntRect(scrollbar.X() + bs.Width(), scrollbar.Y(),
+ scrollbar.Width() - 2 * bs.Width(), scrollbar.Height());
+ }
+ if (scrollbar.Height() <= 2 * bs.Height())
+ return IntRect();
+ return IntRect(scrollbar.X(), scrollbar.Y() + bs.Height(), scrollbar.Width(),
+ scrollbar.Height() - 2 * bs.Height());
+}
+
+int ScrollbarThemeAura::MinimumThumbLength(
+ const ScrollbarThemeClient& scrollbar) {
+ if (scrollbar.Orientation() == kVerticalScrollbar) {
+ return Platform::Current()
+ ->ThemeEngine()
+ ->GetSize(WebThemeEngine::kPartScrollbarVerticalThumb)
+ .height;
+ }
+
+ return Platform::Current()
+ ->ThemeEngine()
+ ->GetSize(WebThemeEngine::kPartScrollbarHorizontalThumb)
+ .width;
+}
+
+void ScrollbarThemeAura::PaintTrackBackground(GraphicsContext& context,
+ const Scrollbar& scrollbar,
+ const IntRect& rect) {
+ // Just assume a forward track part. We only paint the track as a single piece
+ // when there is no thumb.
+ if (!HasThumb(scrollbar) && !rect.IsEmpty())
+ PaintTrackPiece(context, scrollbar, rect, kForwardTrackPart);
+}
+
+void ScrollbarThemeAura::PaintTrackPiece(GraphicsContext& gc,
+ const Scrollbar& scrollbar,
+ const IntRect& rect,
+ ScrollbarPart part_type) {
+ DisplayItem::Type display_item_type =
+ TrackPiecePartToDisplayItemType(part_type);
+ if (DrawingRecorder::UseCachedDrawingIfPossible(gc, scrollbar,
+ display_item_type))
+ return;
+
+ DrawingRecorder recorder(gc, scrollbar, display_item_type);
+
+ WebThemeEngine::State state = scrollbar.HoveredPart() == part_type
+ ? WebThemeEngine::kStateHover
+ : WebThemeEngine::kStateNormal;
+
+ if (UseMockTheme() && !scrollbar.Enabled())
+ state = WebThemeEngine::kStateDisabled;
+
+ IntRect align_rect = TrackRect(scrollbar, false);
+ WebThemeEngine::ExtraParams extra_params;
+ extra_params.scrollbar_track.is_back = (part_type == kBackTrackPart);
+ extra_params.scrollbar_track.track_x = align_rect.X();
+ extra_params.scrollbar_track.track_y = align_rect.Y();
+ extra_params.scrollbar_track.track_width = align_rect.Width();
+ extra_params.scrollbar_track.track_height = align_rect.Height();
+ Platform::Current()->ThemeEngine()->Paint(
+ gc.Canvas(),
+ scrollbar.Orientation() == kHorizontalScrollbar
+ ? WebThemeEngine::kPartScrollbarHorizontalTrack
+ : WebThemeEngine::kPartScrollbarVerticalTrack,
+ state, WebRect(rect), &extra_params);
+}
+
+void ScrollbarThemeAura::PaintButton(GraphicsContext& gc,
+ const Scrollbar& scrollbar,
+ const IntRect& rect,
+ ScrollbarPart part) {
+ DisplayItem::Type display_item_type = ButtonPartToDisplayItemType(part);
+ if (DrawingRecorder::UseCachedDrawingIfPossible(gc, scrollbar,
+ display_item_type))
+ return;
+ PartPaintingParams params =
+ ButtonPartPaintingParams(scrollbar, scrollbar.CurrentPos(), part);
+ if (!params.should_paint)
+ return;
+ DrawingRecorder recorder(gc, scrollbar, display_item_type);
+ Platform::Current()->ThemeEngine()->Paint(
+ gc.Canvas(), params.part, params.state, WebRect(rect), nullptr);
+}
+
+void ScrollbarThemeAura::PaintThumb(GraphicsContext& gc,
+ const Scrollbar& scrollbar,
+ const IntRect& rect) {
+ if (DrawingRecorder::UseCachedDrawingIfPossible(gc, scrollbar,
+ DisplayItem::kScrollbarThumb))
+ return;
+
+ DrawingRecorder recorder(gc, scrollbar, DisplayItem::kScrollbarThumb);
+
+ WebThemeEngine::State state;
+ WebCanvas* canvas = gc.Canvas();
+ if (scrollbar.PressedPart() == kThumbPart)
+ state = WebThemeEngine::kStatePressed;
+ else if (scrollbar.HoveredPart() == kThumbPart)
+ state = WebThemeEngine::kStateHover;
+ else
+ state = WebThemeEngine::kStateNormal;
+
+ Platform::Current()->ThemeEngine()->Paint(
+ canvas,
+ scrollbar.Orientation() == kHorizontalScrollbar
+ ? WebThemeEngine::kPartScrollbarHorizontalThumb
+ : WebThemeEngine::kPartScrollbarVerticalThumb,
+ state, WebRect(rect), nullptr);
+}
+
+bool ScrollbarThemeAura::ShouldRepaintAllPartsOnInvalidation() const {
+ // This theme can separately handle thumb invalidation.
+ return false;
+}
+
+ScrollbarPart ScrollbarThemeAura::InvalidateOnThumbPositionChange(
+ const ScrollbarThemeClient& scrollbar,
+ float old_position,
+ float new_position) const {
+ ScrollbarPart invalid_parts = kNoPart;
+ DCHECK_EQ(ButtonsPlacement(), kWebScrollbarButtonsPlacementSingle);
+ static const ScrollbarPart kButtonParts[] = {kBackButtonStartPart,
+ kForwardButtonEndPart};
+ for (ScrollbarPart part : kButtonParts) {
+ if (ButtonPartPaintingParams(scrollbar, old_position, part) !=
+ ButtonPartPaintingParams(scrollbar, new_position, part))
+ invalid_parts = static_cast<ScrollbarPart>(invalid_parts | part);
+ }
+ return invalid_parts;
+}
+
+bool ScrollbarThemeAura::HasScrollbarButtons(
+ ScrollbarOrientation orientation) const {
+ WebThemeEngine* theme_engine = Platform::Current()->ThemeEngine();
+ if (orientation == kVerticalScrollbar) {
+ return !theme_engine->GetSize(WebThemeEngine::kPartScrollbarDownArrow)
+ .IsEmpty();
+ }
+ return !theme_engine->GetSize(WebThemeEngine::kPartScrollbarLeftArrow)
+ .IsEmpty();
+};
+
+IntSize ScrollbarThemeAura::ButtonSize(const ScrollbarThemeClient& scrollbar) {
+ if (!HasScrollbarButtons(scrollbar.Orientation()))
+ return IntSize(0, 0);
+
+ if (scrollbar.Orientation() == kVerticalScrollbar) {
+ int square_size = scrollbar.Width();
+ return IntSize(square_size, scrollbar.Height() < 2 * square_size
+ ? scrollbar.Height() / 2
+ : square_size);
+ }
+
+ // HorizontalScrollbar
+ int square_size = scrollbar.Height();
+ return IntSize(
+ scrollbar.Width() < 2 * square_size ? scrollbar.Width() / 2 : square_size,
+ square_size);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_aura.h b/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_aura.h
new file mode 100644
index 00000000000..dfdee4b89b3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_aura.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2009, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLLBAR_THEME_AURA_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLLBAR_THEME_AURA_H_
+
+#include "base/gtest_prod_util.h"
+#include "third_party/blink/renderer/platform/scroll/scrollbar_theme.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT ScrollbarThemeAura : public ScrollbarTheme {
+ public:
+ int ScrollbarThickness(ScrollbarControlSize) override;
+
+ protected:
+ bool HasButtons(const ScrollbarThemeClient&) override { return true; }
+ bool HasThumb(const ScrollbarThemeClient&) override;
+
+ IntRect BackButtonRect(const ScrollbarThemeClient&,
+ ScrollbarPart,
+ bool painting = false) override;
+ IntRect ForwardButtonRect(const ScrollbarThemeClient&,
+ ScrollbarPart,
+ bool painting = false) override;
+ IntRect TrackRect(const ScrollbarThemeClient&,
+ bool painting = false) override;
+ int MinimumThumbLength(const ScrollbarThemeClient&) override;
+
+ void PaintTrackBackground(GraphicsContext&,
+ const Scrollbar&,
+ const IntRect&) override;
+ void PaintTrackPiece(GraphicsContext&,
+ const Scrollbar&,
+ const IntRect&,
+ ScrollbarPart) override;
+ void PaintButton(GraphicsContext&,
+ const Scrollbar&,
+ const IntRect&,
+ ScrollbarPart) override;
+ void PaintThumb(GraphicsContext&, const Scrollbar&, const IntRect&) override;
+
+ bool ShouldRepaintAllPartsOnInvalidation() const override;
+ ScrollbarPart InvalidateOnThumbPositionChange(
+ const ScrollbarThemeClient&,
+ float old_position,
+ float new_position) const override;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(ScrollbarThemeAuraTest, ButtonSizeHorizontal);
+ FRIEND_TEST_ALL_PREFIXES(ScrollbarThemeAuraTest, ButtonSizeVertical);
+ FRIEND_TEST_ALL_PREFIXES(ScrollbarThemeAuraTest, NoButtonsReturnsSize0);
+
+ virtual bool HasScrollbarButtons(ScrollbarOrientation) const;
+ IntSize ButtonSize(const ScrollbarThemeClient&);
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_aura_test.cc b/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_aura_test.cc
new file mode 100644
index 00000000000..35f5ec78232
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_aura_test.cc
@@ -0,0 +1,107 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scroll/scrollbar_theme_aura.h"
+
+#include "third_party/blink/renderer/platform/scroll/scrollbar_test_suite.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
+
+namespace blink {
+
+using testing::Return;
+
+namespace {
+
+class ScrollbarThemeAuraButtonOverride final : public ScrollbarThemeAura {
+ public:
+ ScrollbarThemeAuraButtonOverride() : has_scrollbar_buttons_(true) {}
+
+ void SetHasScrollbarButtons(bool value) { has_scrollbar_buttons_ = value; }
+
+ bool HasScrollbarButtons(ScrollbarOrientation unused) const override {
+ return has_scrollbar_buttons_;
+ }
+
+ private:
+ bool has_scrollbar_buttons_;
+};
+
+} // namespace
+
+class ScrollbarThemeAuraTest : public testing::Test {
+ private:
+ base::MessageLoop message_loop_;
+};
+
+TEST_F(ScrollbarThemeAuraTest, ButtonSizeHorizontal) {
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform;
+
+ MockScrollableArea* mock_scrollable_area = MockScrollableArea::Create();
+ ScrollbarThemeMock mock_theme;
+ Scrollbar* scrollbar =
+ Scrollbar::CreateForTesting(mock_scrollable_area, kHorizontalScrollbar,
+ kRegularScrollbar, &mock_theme);
+ ScrollbarThemeAuraButtonOverride theme;
+
+ IntRect scrollbar_size_normal_dimensions(11, 22, 444, 66);
+ scrollbar->SetFrameRect(scrollbar_size_normal_dimensions);
+ IntSize size1 = theme.ButtonSize(*scrollbar);
+ EXPECT_EQ(66, size1.Width());
+ EXPECT_EQ(66, size1.Height());
+
+ IntRect scrollbar_size_squashed_dimensions(11, 22, 444, 666);
+ scrollbar->SetFrameRect(scrollbar_size_squashed_dimensions);
+ IntSize size2 = theme.ButtonSize(*scrollbar);
+ EXPECT_EQ(222, size2.Width());
+ EXPECT_EQ(666, size2.Height());
+
+ ThreadState::Current()->CollectAllGarbage();
+}
+
+TEST_F(ScrollbarThemeAuraTest, ButtonSizeVertical) {
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform;
+
+ MockScrollableArea* mock_scrollable_area = MockScrollableArea::Create();
+ ScrollbarThemeMock mock_theme;
+ Scrollbar* scrollbar = Scrollbar::CreateForTesting(
+ mock_scrollable_area, kVerticalScrollbar, kRegularScrollbar, &mock_theme);
+ ScrollbarThemeAuraButtonOverride theme;
+
+ IntRect scrollbar_size_normal_dimensions(11, 22, 44, 666);
+ scrollbar->SetFrameRect(scrollbar_size_normal_dimensions);
+ IntSize size1 = theme.ButtonSize(*scrollbar);
+ EXPECT_EQ(44, size1.Width());
+ EXPECT_EQ(44, size1.Height());
+
+ IntRect scrollbar_size_squashed_dimensions(11, 22, 444, 666);
+ scrollbar->SetFrameRect(scrollbar_size_squashed_dimensions);
+ IntSize size2 = theme.ButtonSize(*scrollbar);
+ EXPECT_EQ(444, size2.Width());
+ EXPECT_EQ(333, size2.Height());
+
+ ThreadState::Current()->CollectAllGarbage();
+}
+
+TEST_F(ScrollbarThemeAuraTest, NoButtonsReturnsSize0) {
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform;
+
+ MockScrollableArea* mock_scrollable_area = MockScrollableArea::Create();
+ ScrollbarThemeMock mock_theme;
+ Scrollbar* scrollbar = Scrollbar::CreateForTesting(
+ mock_scrollable_area, kVerticalScrollbar, kRegularScrollbar, &mock_theme);
+ ScrollbarThemeAuraButtonOverride theme;
+ theme.SetHasScrollbarButtons(false);
+
+ scrollbar->SetFrameRect(IntRect(1, 2, 3, 4));
+ IntSize size = theme.ButtonSize(*scrollbar);
+ EXPECT_EQ(0, size.Width());
+ EXPECT_EQ(0, size.Height());
+
+ ThreadState::Current()->CollectAllGarbage();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_client.h b/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_client.h
new file mode 100644
index 00000000000..3bebead8bec
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_client.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2012 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:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLLBAR_THEME_CLIENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLLBAR_THEME_CLIENT_H_
+
+#include "third_party/blink/renderer/platform/geometry/int_point.h"
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/geometry/int_size.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/scroll/scroll_types.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT ScrollbarThemeClient {
+ public:
+ virtual int X() const = 0;
+ virtual int Y() const = 0;
+ virtual int Width() const = 0;
+ virtual int Height() const = 0;
+ virtual IntSize Size() const = 0;
+ virtual IntPoint Location() const = 0;
+
+ virtual void SetFrameRect(const IntRect&) = 0;
+ virtual IntRect FrameRect() const = 0;
+
+ virtual void Invalidate() = 0;
+ virtual void InvalidateRect(const IntRect&) = 0;
+
+ virtual ScrollbarOverlayColorTheme GetScrollbarOverlayColorTheme() const = 0;
+ virtual void GetTickmarks(Vector<IntRect>&) const = 0;
+ virtual bool IsScrollableAreaActive() const = 0;
+
+ virtual IntPoint ConvertFromRootFrame(const IntPoint&) const = 0;
+
+ virtual bool IsCustomScrollbar() const = 0;
+ virtual ScrollbarOrientation Orientation() const = 0;
+ virtual bool IsLeftSideVerticalScrollbar() const = 0;
+
+ virtual int Value() const = 0;
+ virtual float CurrentPos() const = 0;
+ virtual int VisibleSize() const = 0;
+ virtual int TotalSize() const = 0;
+ virtual int Maximum() const = 0;
+ virtual ScrollbarControlSize GetControlSize() const = 0;
+
+ virtual ScrollbarPart PressedPart() const = 0;
+ virtual ScrollbarPart HoveredPart() const = 0;
+
+ virtual void StyleChanged() = 0;
+ virtual void SetScrollbarsHiddenIfOverlay(bool) = 0;
+
+ virtual bool Enabled() const = 0;
+ virtual void SetEnabled(bool) = 0;
+
+ virtual bool IsOverlayScrollbar() const = 0;
+
+ virtual float ElasticOverscroll() const = 0;
+ virtual void SetElasticOverscroll(float) = 0;
+
+ protected:
+ virtual ~ScrollbarThemeClient() = default;
+};
+
+} // namespace blink
+
+#endif // ScollbarThemeClient_h
diff --git a/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_mac.h b/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_mac.h
new file mode 100644
index 00000000000..379ecadc265
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_mac.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2008 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLLBAR_THEME_MAC_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLLBAR_THEME_MAC_H_
+
+#include <AppKit/AppKit.h>
+
+#include "third_party/blink/renderer/platform/mac/ns_scroller_imp_details.h"
+#include "third_party/blink/renderer/platform/scroll/scrollbar_theme.h"
+
+typedef id ScrollbarPainter;
+
+namespace blink {
+
+class Pattern;
+
+class PLATFORM_EXPORT ScrollbarThemeMac : public ScrollbarTheme {
+ public:
+ ~ScrollbarThemeMac() override;
+
+ void RegisterScrollbar(ScrollbarThemeClient&) override;
+ void UnregisterScrollbar(ScrollbarThemeClient&) override;
+ void PreferencesChanged(float initial_button_delay,
+ float autoscroll_button_delay,
+ NSScrollerStyle preferred_scroller_style,
+ bool redraw,
+ WebScrollbarButtonsPlacement);
+
+ bool SupportsControlTints() const override { return true; }
+
+ // On Mac, the painting code itself animates the opacity so there's no need
+ // to disable in order to make the scrollbars invisible. In fact,
+ // disabling/enabling causes invalidations which can cause endless loops as
+ // Mac queues up scrollbar paint timers.
+ bool ShouldDisableInvisibleScrollbars() const override { return false; }
+
+ double InitialAutoscrollTimerDelay() override;
+ double AutoscrollTimerDelay() override;
+
+ void PaintTickmarks(GraphicsContext&,
+ const Scrollbar&,
+ const IntRect&) override;
+
+ bool ShouldRepaintAllPartsOnInvalidation() const override { return false; }
+ ScrollbarPart InvalidateOnThumbPositionChange(
+ const ScrollbarThemeClient&,
+ float old_position,
+ float new_position) const override;
+ void UpdateEnabledState(const ScrollbarThemeClient&) override;
+ int ScrollbarThickness(ScrollbarControlSize = kRegularScrollbar) override;
+ bool UsesOverlayScrollbars() const override;
+ void UpdateScrollbarOverlayColorTheme(const ScrollbarThemeClient&) override;
+ WebScrollbarButtonsPlacement ButtonsPlacement() const override;
+
+ void SetNewPainterForScrollbar(ScrollbarThemeClient&, ScrollbarPainter);
+ ScrollbarPainter PainterForScrollbar(const ScrollbarThemeClient&) const;
+
+ void PaintTrackBackground(GraphicsContext&,
+ const Scrollbar&,
+ const IntRect&) override;
+ void PaintThumb(GraphicsContext& context,
+ const Scrollbar& scrollbar,
+ const IntRect& rect) override {
+ PaintThumbInternal(context, scrollbar, rect, 1.0f);
+ }
+ void PaintThumbWithOpacity(GraphicsContext& context,
+ const Scrollbar& scrollbar,
+ const IntRect& rect) override {
+ PaintThumbInternal(context, scrollbar, rect, ThumbOpacity(scrollbar));
+ }
+
+ float ThumbOpacity(const ScrollbarThemeClient&) const override;
+
+ static NSScrollerStyle RecommendedScrollerStyle();
+
+ protected:
+ int MaxOverlapBetweenPages() override { return 40; }
+
+ bool ShouldDragDocumentInsteadOfThumb(const ScrollbarThemeClient&,
+ const WebMouseEvent&) override;
+ int ScrollbarPartToHIPressedState(ScrollbarPart);
+
+ virtual void UpdateButtonPlacement(WebScrollbarButtonsPlacement) {}
+
+ IntRect TrackRect(const ScrollbarThemeClient&,
+ bool painting = false) override;
+ IntRect BackButtonRect(const ScrollbarThemeClient&,
+ ScrollbarPart,
+ bool painting = false) override;
+ IntRect ForwardButtonRect(const ScrollbarThemeClient&,
+ ScrollbarPart,
+ bool painting = false) override;
+
+ bool HasButtons(const ScrollbarThemeClient&) override { return false; }
+ bool HasThumb(const ScrollbarThemeClient&) override;
+
+ int MinimumThumbLength(const ScrollbarThemeClient&) override;
+
+ int TickmarkBorderWidth() override { return 1; }
+
+ void PaintThumbInternal(GraphicsContext&,
+ const Scrollbar&,
+ const IntRect&,
+ float opacity);
+
+ scoped_refptr<Pattern> overhang_pattern_;
+};
+}
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLLBAR_THEME_MAC_H_
diff --git a/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_mac.mm b/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_mac.mm
new file mode 100644
index 00000000000..ae9ebdfa482
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_mac.mm
@@ -0,0 +1,425 @@
+/*
+ * Copyright (C) 2008, 2011 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 "third_party/blink/renderer/platform/scroll/scrollbar_theme_mac.h"
+
+#include <Carbon/Carbon.h>
+#include "skia/ext/skia_utils_mac.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_mouse_event.h"
+#include "third_party/blink/public/platform/web_rect.h"
+#include "third_party/blink/public/platform/web_theme_engine.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context_state_saver.h"
+#include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
+#include "third_party/blink/renderer/platform/mac/color_mac.h"
+#include "third_party/blink/renderer/platform/mac/local_current_graphics_context.h"
+#include "third_party/blink/renderer/platform/mac/ns_scroller_imp_details.h"
+#include "third_party/blink/renderer/platform/mac/scroll_animator_mac.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/scroll/scrollbar_theme_client.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/retain_ptr.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+
+// FIXME: There are repainting problems due to Aqua scroll bar buttons' visual
+// overflow.
+
+@interface NSColor (WebNSColorDetails)
++ (NSImage*)_linenPatternImage;
+@end
+
+@interface BlinkScrollbarObserver : NSObject {
+ blink::ScrollbarThemeClient* _scrollbar;
+ RetainPtr<ScrollbarPainter> _scrollbarPainter;
+}
+- (id)initWithScrollbar:(blink::ScrollbarThemeClient*)scrollbar
+ painter:(const RetainPtr<ScrollbarPainter>&)painter;
+@end
+
+@implementation BlinkScrollbarObserver
+
+- (id)initWithScrollbar:(blink::ScrollbarThemeClient*)scrollbar
+ painter:(const RetainPtr<ScrollbarPainter>&)painter {
+ if (!(self = [super init]))
+ return nil;
+ _scrollbar = scrollbar;
+ _scrollbarPainter = painter;
+ [_scrollbarPainter.Get() addObserver:self
+ forKeyPath:@"knobAlpha"
+ options:0
+ context:nil];
+ return self;
+}
+
+- (id)painter {
+ return _scrollbarPainter.Get();
+}
+
+- (void)dealloc {
+ [_scrollbarPainter.Get() removeObserver:self forKeyPath:@"knobAlpha"];
+ [super dealloc];
+}
+
+- (void)observeValueForKeyPath:(NSString*)keyPath
+ ofObject:(id)object
+ change:(NSDictionary*)change
+ context:(void*)context {
+ if ([keyPath isEqualToString:@"knobAlpha"]) {
+ BOOL visible = [_scrollbarPainter.Get() knobAlpha] > 0;
+ _scrollbar->SetScrollbarsHiddenIfOverlay(!visible);
+ }
+}
+
+@end
+
+namespace blink {
+
+typedef HashSet<ScrollbarThemeClient*> ScrollbarSet;
+
+static ScrollbarSet& GetScrollbarSet() {
+ DEFINE_STATIC_LOCAL(ScrollbarSet, set, ());
+ return set;
+}
+
+static float g_initial_button_delay = 0.5f;
+static float g_autoscroll_button_delay = 0.05f;
+static NSScrollerStyle g_preferred_scroller_style = NSScrollerStyleLegacy;
+
+typedef HashMap<ScrollbarThemeClient*, RetainPtr<BlinkScrollbarObserver>>
+ ScrollbarPainterMap;
+
+static ScrollbarPainterMap& GetScrollbarPainterMap() {
+ static ScrollbarPainterMap* map = new ScrollbarPainterMap;
+ return *map;
+}
+
+static bool SupportsExpandedScrollbars() {
+ // FIXME: This is temporary until all platforms that support ScrollbarPainter
+ // support this part of the API.
+ static bool global_supports_expanded_scrollbars =
+ [NSClassFromString(@"NSScrollerImp")
+ instancesRespondToSelector:@selector(setExpanded:)];
+ return global_supports_expanded_scrollbars;
+}
+
+ScrollbarTheme& ScrollbarTheme::NativeTheme() {
+ DEFINE_STATIC_LOCAL(ScrollbarThemeMac, overlay_theme, ());
+ return overlay_theme;
+}
+
+void ScrollbarThemeMac::PaintTickmarks(GraphicsContext& context,
+ const Scrollbar& scrollbar,
+ const IntRect& rect) {
+ IntRect tickmark_track_rect = rect;
+ tickmark_track_rect.SetX(tickmark_track_rect.X() + 1);
+ tickmark_track_rect.SetWidth(tickmark_track_rect.Width() - 1);
+ ScrollbarTheme::PaintTickmarks(context, scrollbar, tickmark_track_rect);
+}
+
+ScrollbarThemeMac::~ScrollbarThemeMac() {}
+
+void ScrollbarThemeMac::PreferencesChanged(
+ float initial_button_delay,
+ float autoscroll_button_delay,
+ NSScrollerStyle preferred_scroller_style,
+ bool redraw,
+ WebScrollbarButtonsPlacement button_placement) {
+ UpdateButtonPlacement(button_placement);
+ g_initial_button_delay = initial_button_delay;
+ g_autoscroll_button_delay = autoscroll_button_delay;
+ g_preferred_scroller_style = preferred_scroller_style;
+ if (redraw && !GetScrollbarSet().IsEmpty()) {
+ ScrollbarSet::iterator end = GetScrollbarSet().end();
+ for (ScrollbarSet::iterator it = GetScrollbarSet().begin(); it != end;
+ ++it) {
+ (*it)->StyleChanged();
+ (*it)->Invalidate();
+ }
+ }
+}
+
+double ScrollbarThemeMac::InitialAutoscrollTimerDelay() {
+ return g_initial_button_delay;
+}
+
+double ScrollbarThemeMac::AutoscrollTimerDelay() {
+ return g_autoscroll_button_delay;
+}
+
+bool ScrollbarThemeMac::ShouldDragDocumentInsteadOfThumb(
+ const ScrollbarThemeClient&,
+ const WebMouseEvent& event) {
+ return (event.GetModifiers() & WebInputEvent::Modifiers::kAltKey) != 0;
+}
+
+int ScrollbarThemeMac::ScrollbarPartToHIPressedState(ScrollbarPart part) {
+ switch (part) {
+ case kBackButtonStartPart:
+ return kThemeTopOutsideArrowPressed;
+ case kBackButtonEndPart:
+ // This does not make much sense. For some reason the outside constant
+ // is required.
+ return kThemeTopOutsideArrowPressed;
+ case kForwardButtonStartPart:
+ return kThemeTopInsideArrowPressed;
+ case kForwardButtonEndPart:
+ return kThemeBottomOutsideArrowPressed;
+ case kThumbPart:
+ return kThemeThumbPressed;
+ default:
+ return 0;
+ }
+}
+
+ScrollbarPart ScrollbarThemeMac::InvalidateOnThumbPositionChange(
+ const ScrollbarThemeClient& scrollbar,
+ float old_position,
+ float new_position) const {
+ // ScrollAnimatorMac will invalidate scrollbar parts if necessary.
+ return kNoPart;
+}
+
+void ScrollbarThemeMac::RegisterScrollbar(ScrollbarThemeClient& scrollbar) {
+ GetScrollbarSet().insert(&scrollbar);
+
+ bool is_horizontal = scrollbar.Orientation() == kHorizontalScrollbar;
+ RetainPtr<ScrollbarPainter> scrollbar_painter(
+ kAdoptNS,
+ [[NSClassFromString(@"NSScrollerImp")
+ scrollerImpWithStyle:RecommendedScrollerStyle()
+ controlSize:(NSControlSize)scrollbar.GetControlSize()
+ horizontal:is_horizontal
+ replacingScrollerImp:nil] retain]);
+ RetainPtr<BlinkScrollbarObserver> observer(
+ kAdoptNS,
+ [[BlinkScrollbarObserver alloc] initWithScrollbar:&scrollbar
+ painter:scrollbar_painter]);
+
+ GetScrollbarPainterMap().insert(&scrollbar, observer);
+ UpdateEnabledState(scrollbar);
+ UpdateScrollbarOverlayColorTheme(scrollbar);
+}
+
+void ScrollbarThemeMac::UnregisterScrollbar(ScrollbarThemeClient& scrollbar) {
+ GetScrollbarPainterMap().erase(&scrollbar);
+ GetScrollbarSet().erase(&scrollbar);
+}
+
+void ScrollbarThemeMac::SetNewPainterForScrollbar(
+ ScrollbarThemeClient& scrollbar,
+ ScrollbarPainter new_painter) {
+ RetainPtr<ScrollbarPainter> scrollbar_painter(kAdoptNS, [new_painter retain]);
+ RetainPtr<BlinkScrollbarObserver> observer(
+ kAdoptNS,
+ [[BlinkScrollbarObserver alloc] initWithScrollbar:&scrollbar
+ painter:scrollbar_painter]);
+ GetScrollbarPainterMap().Set(&scrollbar, observer);
+ UpdateEnabledState(scrollbar);
+ UpdateScrollbarOverlayColorTheme(scrollbar);
+}
+
+ScrollbarPainter ScrollbarThemeMac::PainterForScrollbar(
+ const ScrollbarThemeClient& scrollbar) const {
+ return [GetScrollbarPainterMap()
+ .at(const_cast<ScrollbarThemeClient*>(&scrollbar))
+ .Get() painter];
+}
+
+void ScrollbarThemeMac::PaintTrackBackground(GraphicsContext& context,
+ const Scrollbar& scrollbar,
+ const IntRect& rect) {
+ if (DrawingRecorder::UseCachedDrawingIfPossible(
+ context, scrollbar, DisplayItem::kScrollbarTrackBackground))
+ return;
+
+ DrawingRecorder recorder(context, scrollbar,
+ DisplayItem::kScrollbarTrackBackground);
+
+ GraphicsContextStateSaver state_saver(context);
+ context.Translate(rect.X(), rect.Y());
+ LocalCurrentGraphicsContext local_context(context,
+ IntRect(IntPoint(), rect.Size()));
+
+ CGRect frame_rect = CGRect(scrollbar.FrameRect());
+ ScrollbarPainter scrollbar_painter = PainterForScrollbar(scrollbar);
+ [scrollbar_painter setEnabled:scrollbar.Enabled()];
+ [scrollbar_painter setBoundsSize:NSSizeFromCGSize(frame_rect.size)];
+ NSRect track_rect =
+ NSMakeRect(0, 0, frame_rect.size.width, frame_rect.size.height);
+ [scrollbar_painter drawKnobSlotInRect:track_rect highlight:NO];
+}
+
+void ScrollbarThemeMac::PaintThumbInternal(GraphicsContext& context,
+ const Scrollbar& scrollbar,
+ const IntRect& rect,
+ float opacity) {
+ if (DrawingRecorder::UseCachedDrawingIfPossible(context, scrollbar,
+ DisplayItem::kScrollbarThumb))
+ return;
+
+ DrawingRecorder recorder(context, scrollbar, DisplayItem::kScrollbarThumb);
+
+ GraphicsContextStateSaver state_saver(context);
+ context.Translate(rect.X(), rect.Y());
+ IntRect local_rect(IntPoint(), rect.Size());
+
+ if (opacity != 1.0f) {
+ FloatRect float_local_rect(local_rect);
+ context.BeginLayer(opacity, SkBlendMode::kSrcOver, &float_local_rect);
+ }
+
+ {
+ LocalCurrentGraphicsContext local_context(context, local_rect);
+ ScrollbarPainter scrollbar_painter = PainterForScrollbar(scrollbar);
+ [scrollbar_painter setEnabled:scrollbar.Enabled()];
+ // drawKnob aligns the thumb to right side of the draw rect.
+ // If the vertical overlay scrollbar is on the left, use trackWidth instead
+ // of scrollbar width, to avoid the gap on the left side of the thumb.
+ IntRect draw_rect = IntRect(rect);
+ if (UsesOverlayScrollbars() && scrollbar.IsLeftSideVerticalScrollbar()) {
+ int thumb_width = [scrollbar_painter trackWidth];
+ draw_rect.SetWidth(thumb_width);
+ }
+ [scrollbar_painter
+ setBoundsSize:NSSizeFromCGSize(CGSize(draw_rect.Size()))];
+
+ [scrollbar_painter setDoubleValue:0];
+ [scrollbar_painter setKnobProportion:1];
+
+ CGFloat old_knob_alpha = [scrollbar_painter knobAlpha];
+ [scrollbar_painter setKnobAlpha:1];
+
+ if (scrollbar.Enabled())
+ [scrollbar_painter drawKnob];
+
+ // If this state is not set, then moving the cursor over the scrollbar area
+ // will only cause the scrollbar to engorge when moved over the top of the
+ // scrollbar area.
+ [scrollbar_painter
+ setBoundsSize:NSSizeFromCGSize(CGSize(scrollbar.FrameRect().Size()))];
+ [scrollbar_painter setKnobAlpha:old_knob_alpha];
+ }
+
+ if (opacity != 1.0f)
+ context.EndLayer();
+}
+
+int ScrollbarThemeMac::ScrollbarThickness(ScrollbarControlSize control_size) {
+ NSControlSize ns_control_size = static_cast<NSControlSize>(control_size);
+ ScrollbarPainter scrollbar_painter = [NSClassFromString(@"NSScrollerImp")
+ scrollerImpWithStyle:RecommendedScrollerStyle()
+ controlSize:ns_control_size
+ horizontal:NO
+ replacingScrollerImp:nil];
+ BOOL was_expanded = NO;
+ if (SupportsExpandedScrollbars()) {
+ was_expanded = [scrollbar_painter isExpanded];
+ [scrollbar_painter setExpanded:YES];
+ }
+ int thickness = [scrollbar_painter trackBoxWidth];
+ if (SupportsExpandedScrollbars())
+ [scrollbar_painter setExpanded:was_expanded];
+ return thickness;
+}
+
+bool ScrollbarThemeMac::UsesOverlayScrollbars() const {
+ return RecommendedScrollerStyle() == NSScrollerStyleOverlay;
+}
+
+void ScrollbarThemeMac::UpdateScrollbarOverlayColorTheme(
+ const ScrollbarThemeClient& scrollbar) {
+ ScrollbarPainter painter = PainterForScrollbar(scrollbar);
+ switch (scrollbar.GetScrollbarOverlayColorTheme()) {
+ case kScrollbarOverlayColorThemeDark:
+ [painter setKnobStyle:NSScrollerKnobStyleDark];
+ break;
+ case kScrollbarOverlayColorThemeLight:
+ [painter setKnobStyle:NSScrollerKnobStyleLight];
+ break;
+ }
+}
+
+WebScrollbarButtonsPlacement ScrollbarThemeMac::ButtonsPlacement() const {
+ return kWebScrollbarButtonsPlacementNone;
+}
+
+bool ScrollbarThemeMac::HasThumb(const ScrollbarThemeClient& scrollbar) {
+ ScrollbarPainter painter = PainterForScrollbar(scrollbar);
+ int min_length_for_thumb =
+ [painter knobMinLength] + [painter trackOverlapEndInset] +
+ [painter knobOverlapEndInset] +
+ 2 * ([painter trackEndInset] + [painter knobEndInset]);
+ return scrollbar.Enabled() &&
+ (scrollbar.Orientation() == kHorizontalScrollbar
+ ? scrollbar.Width()
+ : scrollbar.Height()) >= min_length_for_thumb;
+}
+
+IntRect ScrollbarThemeMac::BackButtonRect(const ScrollbarThemeClient& scrollbar,
+ ScrollbarPart part,
+ bool painting) {
+ DCHECK_EQ(ButtonsPlacement(), kWebScrollbarButtonsPlacementNone);
+ return IntRect();
+}
+
+IntRect ScrollbarThemeMac::ForwardButtonRect(
+ const ScrollbarThemeClient& scrollbar,
+ ScrollbarPart part,
+ bool painting) {
+ DCHECK_EQ(ButtonsPlacement(), kWebScrollbarButtonsPlacementNone);
+ return IntRect();
+}
+
+IntRect ScrollbarThemeMac::TrackRect(const ScrollbarThemeClient& scrollbar,
+ bool painting) {
+ DCHECK(!HasButtons(scrollbar));
+ return scrollbar.FrameRect();
+}
+
+int ScrollbarThemeMac::MinimumThumbLength(
+ const ScrollbarThemeClient& scrollbar) {
+ return [PainterForScrollbar(scrollbar) knobMinLength];
+}
+
+void ScrollbarThemeMac::UpdateEnabledState(
+ const ScrollbarThemeClient& scrollbar) {
+ [PainterForScrollbar(scrollbar) setEnabled:scrollbar.Enabled()];
+}
+
+float ScrollbarThemeMac::ThumbOpacity(
+ const ScrollbarThemeClient& scrollbar) const {
+ ScrollbarPainter scrollbar_painter = PainterForScrollbar(scrollbar);
+ return [scrollbar_painter knobAlpha];
+}
+
+// static
+NSScrollerStyle ScrollbarThemeMac::RecommendedScrollerStyle() {
+ if (RuntimeEnabledFeatures::OverlayScrollbarsEnabled())
+ return NSScrollerStyleOverlay;
+ return g_preferred_scroller_style;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_mock.cc b/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_mock.cc
new file mode 100644
index 00000000000..6728343cb18
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_mock.cc
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2011 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 "third_party/blink/renderer/platform/scroll/scrollbar_theme_mock.h"
+
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/scroll/scrollbar.h"
+
+namespace blink {
+
+static int g_c_scrollbar_thickness[] = {15, 11};
+
+int ScrollbarThemeMock::ScrollbarThickness(ScrollbarControlSize control_size) {
+ return g_c_scrollbar_thickness[control_size];
+}
+
+bool ScrollbarThemeMock::UsesOverlayScrollbars() const {
+ return RuntimeEnabledFeatures::OverlayScrollbarsEnabled();
+}
+
+IntRect ScrollbarThemeMock::TrackRect(const ScrollbarThemeClient& scrollbar,
+ bool) {
+ return scrollbar.FrameRect();
+}
+
+void ScrollbarThemeMock::PaintTrackBackground(GraphicsContext& context,
+ const Scrollbar& scrollbar,
+ const IntRect& track_rect) {
+ if (DrawingRecorder::UseCachedDrawingIfPossible(
+ context, scrollbar, DisplayItem::kScrollbarTrackBackground))
+ return;
+
+ DrawingRecorder recorder(context, scrollbar,
+ DisplayItem::kScrollbarTrackBackground);
+ context.FillRect(track_rect,
+ scrollbar.Enabled() ? Color::kLightGray : Color(0xFFE0E0E0));
+}
+
+void ScrollbarThemeMock::PaintThumb(GraphicsContext& context,
+ const Scrollbar& scrollbar,
+ const IntRect& thumb_rect) {
+ if (!scrollbar.Enabled())
+ return;
+
+ if (DrawingRecorder::UseCachedDrawingIfPossible(context, scrollbar,
+ DisplayItem::kScrollbarThumb))
+ return;
+
+ DrawingRecorder recorder(context, scrollbar, DisplayItem::kScrollbarThumb);
+ context.FillRect(thumb_rect, Color::kDarkGray);
+}
+
+void ScrollbarThemeMock::PaintScrollCorner(GraphicsContext& context,
+ const DisplayItemClient& scrollbar,
+ const IntRect& corner_rect) {
+ if (DrawingRecorder::UseCachedDrawingIfPossible(
+ context, scrollbar, DisplayItem::kScrollbarCorner))
+ return;
+
+ DrawingRecorder recorder(context, scrollbar, DisplayItem::kScrollbarCorner);
+ context.FillRect(corner_rect, Color::kWhite);
+}
+
+int ScrollbarThemeMock::MinimumThumbLength(
+ const ScrollbarThemeClient& scrollbar) {
+ return ScrollbarThickness(scrollbar.GetControlSize());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_mock.h b/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_mock.h
new file mode 100644
index 00000000000..4118b580bd5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_mock.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2011 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLLBAR_THEME_MOCK_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLLBAR_THEME_MOCK_H_
+
+#include "third_party/blink/renderer/platform/scroll/scrollbar_theme.h"
+
+namespace blink {
+
+// Scrollbar theme used in image snapshots, to eliminate appearance differences
+// between platforms.
+class PLATFORM_EXPORT ScrollbarThemeMock : public ScrollbarTheme {
+ public:
+ int ScrollbarThickness(ScrollbarControlSize = kRegularScrollbar) override;
+ bool UsesOverlayScrollbars() const override;
+
+ protected:
+ bool HasButtons(const ScrollbarThemeClient&) override { return false; }
+ bool HasThumb(const ScrollbarThemeClient&) override { return true; }
+
+ IntRect BackButtonRect(const ScrollbarThemeClient&,
+ ScrollbarPart,
+ bool /*painting*/ = false) override {
+ return IntRect();
+ }
+ IntRect ForwardButtonRect(const ScrollbarThemeClient&,
+ ScrollbarPart,
+ bool /*painting*/ = false) override {
+ return IntRect();
+ }
+ IntRect TrackRect(const ScrollbarThemeClient&,
+ bool painting = false) override;
+
+ void PaintTrackBackground(GraphicsContext&,
+ const Scrollbar&,
+ const IntRect&) override;
+ void PaintThumb(GraphicsContext&, const Scrollbar&, const IntRect&) override;
+
+ void PaintScrollCorner(GraphicsContext&,
+ const DisplayItemClient&,
+ const IntRect& corner_rect) override;
+
+ int MinimumThumbLength(const ScrollbarThemeClient&) override;
+
+ private:
+ bool IsMockTheme() const final { return true; }
+};
+
+} // namespace blink
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLLBAR_THEME_MOCK_H_
diff --git a/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_overlay.cc b/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_overlay.cc
new file mode 100644
index 00000000000..b6f49e26a48
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_overlay.cc
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/scroll/scrollbar_theme_overlay.h"
+
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_rect.h"
+#include "third_party/blink/public/platform/web_theme_engine.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
+#include "third_party/blink/renderer/platform/scroll/scrollbar.h"
+#include "third_party/blink/renderer/platform/transforms/transformation_matrix.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+#include <algorithm>
+
+namespace blink {
+
+ScrollbarThemeOverlay::ScrollbarThemeOverlay(int thumb_thickness,
+ int scrollbar_margin,
+ HitTestBehavior allow_hit_test,
+ Color color)
+ : ScrollbarTheme(),
+ thumb_thickness_(thumb_thickness),
+ scrollbar_margin_(scrollbar_margin),
+ allow_hit_test_(allow_hit_test),
+ color_(color),
+ use_solid_color_(true) {}
+
+ScrollbarThemeOverlay::ScrollbarThemeOverlay(int thumb_thickness,
+ int scrollbar_margin,
+ HitTestBehavior allow_hit_test)
+ : ScrollbarTheme(),
+ thumb_thickness_(thumb_thickness),
+ scrollbar_margin_(scrollbar_margin),
+ allow_hit_test_(allow_hit_test),
+ use_solid_color_(false) {}
+
+bool ScrollbarThemeOverlay::ShouldRepaintAllPartsOnInvalidation() const {
+ return false;
+}
+
+ScrollbarPart ScrollbarThemeOverlay::InvalidateOnThumbPositionChange(
+ const ScrollbarThemeClient&,
+ float old_position,
+ float new_position) const {
+ return kNoPart;
+}
+
+int ScrollbarThemeOverlay::ScrollbarThickness(
+ ScrollbarControlSize control_size) {
+ return thumb_thickness_ + scrollbar_margin_;
+}
+
+int ScrollbarThemeOverlay::ScrollbarMargin() const {
+ return scrollbar_margin_;
+}
+
+bool ScrollbarThemeOverlay::UsesOverlayScrollbars() const {
+ return true;
+}
+
+double ScrollbarThemeOverlay::OverlayScrollbarFadeOutDelaySeconds() const {
+ // TODO(bokan): Unit tests run without a theme engine. This is normally fine
+ // because they expect to use ScrollbarThemeMock which doesn't use a theme
+ // engine. If overlays are turned on though, this class is used even if mock
+ // scrollbars are on. We should either provide mock out a web theme engine for
+ // unit tests or provide a mock version of this class.
+ if (!Platform::Current()->ThemeEngine())
+ return 0.0;
+ WebThemeEngine::ScrollbarStyle style;
+ Platform::Current()->ThemeEngine()->GetOverlayScrollbarStyle(&style);
+ return style.fade_out_delay_seconds;
+}
+
+double ScrollbarThemeOverlay::OverlayScrollbarFadeOutDurationSeconds() const {
+ if (!Platform::Current()->ThemeEngine())
+ return 0.0;
+ WebThemeEngine::ScrollbarStyle style;
+ Platform::Current()->ThemeEngine()->GetOverlayScrollbarStyle(&style);
+ return style.fade_out_duration_seconds;
+}
+
+int ScrollbarThemeOverlay::ThumbLength(const ScrollbarThemeClient& scrollbar) {
+ int track_len = TrackLength(scrollbar);
+
+ if (!scrollbar.TotalSize())
+ return track_len;
+
+ float proportion =
+ static_cast<float>(scrollbar.VisibleSize()) / scrollbar.TotalSize();
+ int length = round(proportion * track_len);
+ int min_len = std::min(MinimumThumbLength(scrollbar), track_len);
+ length = clampTo(length, min_len, track_len);
+ return length;
+}
+
+bool ScrollbarThemeOverlay::HasThumb(const ScrollbarThemeClient& scrollbar) {
+ return true;
+}
+
+IntRect ScrollbarThemeOverlay::BackButtonRect(const ScrollbarThemeClient&,
+ ScrollbarPart,
+ bool) {
+ return IntRect();
+}
+
+IntRect ScrollbarThemeOverlay::ForwardButtonRect(const ScrollbarThemeClient&,
+ ScrollbarPart,
+ bool) {
+ return IntRect();
+}
+
+IntRect ScrollbarThemeOverlay::TrackRect(const ScrollbarThemeClient& scrollbar,
+ bool) {
+ IntRect rect = scrollbar.FrameRect();
+ if (scrollbar.Orientation() == kHorizontalScrollbar)
+ rect.InflateX(-scrollbar_margin_);
+ else
+ rect.InflateY(-scrollbar_margin_);
+ return rect;
+}
+
+int ScrollbarThemeOverlay::ThumbThickness(const ScrollbarThemeClient&) {
+ return thumb_thickness_;
+}
+
+void ScrollbarThemeOverlay::PaintThumb(GraphicsContext& context,
+ const Scrollbar& scrollbar,
+ const IntRect& rect) {
+ if (DrawingRecorder::UseCachedDrawingIfPossible(context, scrollbar,
+ DisplayItem::kScrollbarThumb))
+ return;
+
+ DrawingRecorder recorder(context, scrollbar, DisplayItem::kScrollbarThumb);
+
+ IntRect thumb_rect = rect;
+ if (scrollbar.Orientation() == kHorizontalScrollbar) {
+ thumb_rect.SetHeight(thumb_rect.Height() - scrollbar_margin_);
+ } else {
+ thumb_rect.SetWidth(thumb_rect.Width() - scrollbar_margin_);
+ if (scrollbar.IsLeftSideVerticalScrollbar())
+ thumb_rect.SetX(thumb_rect.X() + scrollbar_margin_);
+ }
+
+ if (use_solid_color_ || !Platform::Current()->ThemeEngine()) {
+ context.FillRect(thumb_rect, color_);
+ return;
+ }
+
+ WebThemeEngine::State state = WebThemeEngine::kStateNormal;
+
+ if (!scrollbar.Enabled())
+ state = WebThemeEngine::kStateDisabled;
+ else if (scrollbar.PressedPart() == kThumbPart)
+ state = WebThemeEngine::kStatePressed;
+ else if (scrollbar.HoveredPart() == kThumbPart)
+ state = WebThemeEngine::kStateHover;
+
+ WebCanvas* canvas = context.Canvas();
+
+ WebThemeEngine::Part part = WebThemeEngine::kPartScrollbarHorizontalThumb;
+ if (scrollbar.Orientation() == kVerticalScrollbar)
+ part = WebThemeEngine::kPartScrollbarVerticalThumb;
+
+ blink::WebThemeEngine::ExtraParams params;
+ params.scrollbar_thumb.scrollbar_theme =
+ static_cast<WebScrollbarOverlayColorTheme>(
+ scrollbar.GetScrollbarOverlayColorTheme());
+
+ // Horizontally flip the canvas if it is left vertical scrollbar.
+ if (scrollbar.IsLeftSideVerticalScrollbar()) {
+ canvas->save();
+ canvas->translate(rect.Width(), 0);
+ canvas->scale(-1, 1);
+ }
+
+ Platform::Current()->ThemeEngine()->Paint(canvas, part, state, WebRect(rect),
+ &params);
+
+ if (scrollbar.IsLeftSideVerticalScrollbar())
+ canvas->restore();
+}
+
+ScrollbarPart ScrollbarThemeOverlay::HitTest(
+ const ScrollbarThemeClient& scrollbar,
+ const IntPoint& position) {
+ if (allow_hit_test_ == kDisallowHitTest)
+ return kNoPart;
+
+ ScrollbarPart part = ScrollbarTheme::HitTest(scrollbar, position);
+ if (part != kThumbPart)
+ return kNoPart;
+
+ return kThumbPart;
+}
+
+// static
+ScrollbarThemeOverlay& ScrollbarThemeOverlay::MobileTheme() {
+ static ScrollbarThemeOverlay* theme;
+ if (!theme) {
+ WebThemeEngine::ScrollbarStyle style = {3, 3, 0x80808080}; // default style
+ if (Platform::Current()->ThemeEngine()) {
+ Platform::Current()->ThemeEngine()->GetOverlayScrollbarStyle(&style);
+ }
+ theme = new ScrollbarThemeOverlay(
+ style.thumb_thickness, style.scrollbar_margin,
+ ScrollbarThemeOverlay::kDisallowHitTest, Color(style.color));
+ theme->is_mobile_theme_ = true;
+ }
+ return *theme;
+}
+
+bool ScrollbarThemeOverlay::IsMobileTheme() const {
+ return is_mobile_theme_;
+}
+
+bool ScrollbarThemeOverlay::UsesNinePatchThumbResource() const {
+ WebThemeEngine* engine = Platform::Current()->ThemeEngine();
+ if (!engine)
+ return false;
+
+ // Thumb orientation doesn't matter here.
+ return engine->SupportsNinePatch(WebThemeEngine::kPartScrollbarVerticalThumb);
+}
+
+IntSize ScrollbarThemeOverlay::NinePatchThumbCanvasSize(
+ const ScrollbarThemeClient& scrollbar) const {
+ DCHECK(UsesNinePatchThumbResource());
+
+ WebThemeEngine::Part part =
+ scrollbar.Orientation() == kVerticalScrollbar
+ ? WebThemeEngine::kPartScrollbarVerticalThumb
+ : WebThemeEngine::kPartScrollbarHorizontalThumb;
+
+ DCHECK(Platform::Current()->ThemeEngine());
+ return Platform::Current()->ThemeEngine()->NinePatchCanvasSize(part);
+}
+
+IntRect ScrollbarThemeOverlay::NinePatchThumbAperture(
+ const ScrollbarThemeClient& scrollbar) const {
+ DCHECK(UsesNinePatchThumbResource());
+
+ WebThemeEngine::Part part = WebThemeEngine::kPartScrollbarHorizontalThumb;
+ if (scrollbar.Orientation() == kVerticalScrollbar)
+ part = WebThemeEngine::kPartScrollbarVerticalThumb;
+
+ DCHECK(Platform::Current()->ThemeEngine());
+ return Platform::Current()->ThemeEngine()->NinePatchAperture(part);
+}
+
+int ScrollbarThemeOverlay::MinimumThumbLength(
+ const ScrollbarThemeClient& scrollbar) {
+ if (scrollbar.Orientation() == kVerticalScrollbar) {
+ return Platform::Current()
+ ->ThemeEngine()
+ ->GetSize(WebThemeEngine::kPartScrollbarVerticalThumb)
+ .height;
+ }
+
+ return Platform::Current()
+ ->ThemeEngine()
+ ->GetSize(WebThemeEngine::kPartScrollbarHorizontalThumb)
+ .width;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_overlay.h b/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_overlay.h
new file mode 100644
index 00000000000..9990461217c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_overlay.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLLBAR_THEME_OVERLAY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLLBAR_THEME_OVERLAY_H_
+
+#include "third_party/blink/renderer/platform/graphics/color.h"
+#include "third_party/blink/renderer/platform/scroll/scrollbar_theme.h"
+
+namespace blink {
+
+// This scrollbar theme is used to get overlay scrollbar for platforms other
+// than Mac. Mac's overlay scrollbars are in ScrollbarThemeMac*.
+class PLATFORM_EXPORT ScrollbarThemeOverlay : public ScrollbarTheme {
+ public:
+ enum HitTestBehavior { kAllowHitTest, kDisallowHitTest };
+
+ ScrollbarThemeOverlay(int thumb_thickness,
+ int scrollbar_margin,
+ HitTestBehavior);
+ ScrollbarThemeOverlay(int thumb_thickness,
+ int scrollbar_margin,
+ HitTestBehavior,
+ Color);
+ ~ScrollbarThemeOverlay() override = default;
+
+ bool ShouldRepaintAllPartsOnInvalidation() const override;
+
+ ScrollbarPart InvalidateOnThumbPositionChange(
+ const ScrollbarThemeClient&,
+ float old_position,
+ float new_position) const override;
+
+ int ScrollbarThickness(ScrollbarControlSize) override;
+ int ScrollbarMargin() const override;
+ bool UsesOverlayScrollbars() const override;
+ double OverlayScrollbarFadeOutDelaySeconds() const override;
+ double OverlayScrollbarFadeOutDurationSeconds() const override;
+
+ int ThumbLength(const ScrollbarThemeClient&) override;
+
+ bool HasButtons(const ScrollbarThemeClient&) override { return false; }
+ bool HasThumb(const ScrollbarThemeClient&) override;
+
+ IntRect BackButtonRect(const ScrollbarThemeClient&,
+ ScrollbarPart,
+ bool painting = false) override;
+ IntRect ForwardButtonRect(const ScrollbarThemeClient&,
+ ScrollbarPart,
+ bool painting = false) override;
+ IntRect TrackRect(const ScrollbarThemeClient&,
+ bool painting = false) override;
+ int ThumbThickness(const ScrollbarThemeClient&) override;
+ int ThumbThickness() { return thumb_thickness_; }
+
+ void PaintThumb(GraphicsContext&, const Scrollbar&, const IntRect&) override;
+ ScrollbarPart HitTest(const ScrollbarThemeClient&, const IntPoint&) override;
+
+ bool UsesNinePatchThumbResource() const override;
+ IntSize NinePatchThumbCanvasSize(const ScrollbarThemeClient&) const override;
+ IntRect NinePatchThumbAperture(const ScrollbarThemeClient&) const override;
+
+ int MinimumThumbLength(const ScrollbarThemeClient&) override;
+
+ bool IsMobileTheme() const;
+
+ static ScrollbarThemeOverlay& MobileTheme();
+
+ private:
+ int thumb_thickness_;
+ int scrollbar_margin_;
+ HitTestBehavior allow_hit_test_;
+ Color color_;
+ bool is_mobile_theme_;
+ const bool use_solid_color_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_overlay_mock.h b/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_overlay_mock.h
new file mode 100644
index 00000000000..fc6e95b05a7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_overlay_mock.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLLBAR_THEME_OVERLAY_MOCK_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLLBAR_THEME_OVERLAY_MOCK_H_
+
+#include "third_party/blink/renderer/platform/scroll/scrollbar_theme_overlay.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT ScrollbarThemeOverlayMock : public ScrollbarThemeOverlay {
+ public:
+ ScrollbarThemeOverlayMock()
+ : ScrollbarThemeOverlay(3, 4, kDisallowHitTest, Color(128, 128, 128)),
+ delay_in_seconds_(0.0) {}
+
+ double OverlayScrollbarFadeOutDelaySeconds() const override {
+ return delay_in_seconds_;
+ }
+ double OverlayScrollbarFadeOutDurationSeconds() const override { return 0.0; }
+
+ void SetOverlayScrollbarFadeOutDelay(double delay_in_seconds) {
+ delay_in_seconds_ = delay_in_seconds;
+ }
+
+ void PaintThumb(GraphicsContext& gc,
+ const Scrollbar& scrollbar,
+ const IntRect& rect) override {
+ if (!scrollbar.Enabled())
+ return;
+ ScrollbarThemeOverlay::PaintThumb(gc, scrollbar, rect);
+ }
+
+ bool ShouldSnapBackToDragOrigin(const ScrollbarThemeClient& scrollbar,
+ const WebMouseEvent& evt) override {
+ return false;
+ }
+
+ int MinimumThumbLength(const ScrollbarThemeClient&) override { return 7; }
+
+ private:
+ double delay_in_seconds_;
+ bool IsMockTheme() const final { return true; }
+};
+
+} // namespace blink
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SCROLLBAR_THEME_OVERLAY_MOCK_H_
diff --git a/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_overlay_test.cc b/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_overlay_test.cc
new file mode 100644
index 00000000000..2eed0f501bb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/scrollbar_theme_overlay_test.cc
@@ -0,0 +1,144 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scroll/scrollbar_theme_overlay.h"
+
+#include "third_party/blink/renderer/platform/scroll/scrollbar_test_suite.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
+
+namespace blink {
+
+using testing::NiceMock;
+using testing::Return;
+
+class ScrollbarThemeOverlayTest : public testing::Test {
+ private:
+ base::MessageLoop message_loop_;
+};
+
+TEST_F(ScrollbarThemeOverlayTest, PaintInvalidation) {
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform;
+
+ NiceMock<MockScrollableArea>* mock_scrollable_area =
+ new NiceMock<MockScrollableArea>(ScrollOffset(100, 100));
+ ScrollbarThemeOverlay theme(14, 0, ScrollbarThemeOverlay::kAllowHitTest);
+
+ Scrollbar* vertical_scrollbar = Scrollbar::CreateForTesting(
+ mock_scrollable_area, kVerticalScrollbar, kRegularScrollbar, &theme);
+ Scrollbar* horizontal_scrollbar = Scrollbar::CreateForTesting(
+ mock_scrollable_area, kHorizontalScrollbar, kRegularScrollbar, &theme);
+ ON_CALL(*mock_scrollable_area, VerticalScrollbar())
+ .WillByDefault(Return(vertical_scrollbar));
+ ON_CALL(*mock_scrollable_area, HorizontalScrollbar())
+ .WillByDefault(Return(horizontal_scrollbar));
+
+ IntRect vertical_rect(1010, 0, 14, 768);
+ IntRect horizontal_rect(0, 754, 1024, 14);
+ vertical_scrollbar->SetFrameRect(vertical_rect);
+ horizontal_scrollbar->SetFrameRect(horizontal_rect);
+
+ ASSERT_EQ(vertical_scrollbar, mock_scrollable_area->VerticalScrollbar());
+ ASSERT_EQ(horizontal_scrollbar, mock_scrollable_area->HorizontalScrollbar());
+
+ vertical_scrollbar->ClearTrackNeedsRepaint();
+ vertical_scrollbar->ClearThumbNeedsRepaint();
+ horizontal_scrollbar->ClearTrackNeedsRepaint();
+ horizontal_scrollbar->ClearThumbNeedsRepaint();
+ mock_scrollable_area->ClearNeedsPaintInvalidationForScrollControls();
+
+ ASSERT_FALSE(vertical_scrollbar->ThumbNeedsRepaint());
+ ASSERT_FALSE(vertical_scrollbar->TrackNeedsRepaint());
+ ASSERT_FALSE(mock_scrollable_area->VerticalScrollbarNeedsPaintInvalidation());
+ ASSERT_FALSE(horizontal_scrollbar->ThumbNeedsRepaint());
+ ASSERT_FALSE(horizontal_scrollbar->TrackNeedsRepaint());
+ ASSERT_FALSE(
+ mock_scrollable_area->HorizontalScrollbarNeedsPaintInvalidation());
+
+ // Changing the scroll offset shouldn't invalide the thumb nor track, but it
+ // should cause a "general" invalidation for non-composited scrollbars.
+ // Ensure the horizontal scrollbar is unaffected.
+ mock_scrollable_area->UpdateScrollOffset(ScrollOffset(0, 5), kUserScroll);
+ vertical_scrollbar->OffsetDidChange();
+ horizontal_scrollbar->OffsetDidChange();
+ EXPECT_FALSE(vertical_scrollbar->ThumbNeedsRepaint());
+ EXPECT_FALSE(vertical_scrollbar->TrackNeedsRepaint());
+ EXPECT_TRUE(mock_scrollable_area->VerticalScrollbarNeedsPaintInvalidation());
+ EXPECT_FALSE(horizontal_scrollbar->ThumbNeedsRepaint());
+ EXPECT_FALSE(horizontal_scrollbar->TrackNeedsRepaint());
+ EXPECT_FALSE(
+ mock_scrollable_area->HorizontalScrollbarNeedsPaintInvalidation());
+
+ // Try the horizontal scrollbar.
+ mock_scrollable_area->ClearNeedsPaintInvalidationForScrollControls();
+ mock_scrollable_area->UpdateScrollOffset(ScrollOffset(5, 5), kUserScroll);
+ horizontal_scrollbar->OffsetDidChange();
+ vertical_scrollbar->OffsetDidChange();
+ EXPECT_FALSE(vertical_scrollbar->ThumbNeedsRepaint());
+ EXPECT_FALSE(vertical_scrollbar->TrackNeedsRepaint());
+ EXPECT_FALSE(mock_scrollable_area->VerticalScrollbarNeedsPaintInvalidation());
+ EXPECT_FALSE(horizontal_scrollbar->ThumbNeedsRepaint());
+ EXPECT_FALSE(horizontal_scrollbar->TrackNeedsRepaint());
+ EXPECT_TRUE(
+ mock_scrollable_area->HorizontalScrollbarNeedsPaintInvalidation());
+
+ mock_scrollable_area->ClearNeedsPaintInvalidationForScrollControls();
+
+ // Move the mouse over the vertical scrollbar's thumb. Ensure the thumb is
+ // invalidated as its state is changed to hover.
+ vertical_scrollbar->SetHoveredPart(kThumbPart);
+ EXPECT_TRUE(vertical_scrollbar->ThumbNeedsRepaint());
+ EXPECT_TRUE(mock_scrollable_area->VerticalScrollbarNeedsPaintInvalidation());
+
+ vertical_scrollbar->ClearThumbNeedsRepaint();
+ mock_scrollable_area->ClearNeedsPaintInvalidationForScrollControls();
+
+ // Pressing down should also cause an invalidation.
+ vertical_scrollbar->SetPressedPart(kThumbPart);
+ EXPECT_TRUE(vertical_scrollbar->ThumbNeedsRepaint());
+ EXPECT_TRUE(mock_scrollable_area->VerticalScrollbarNeedsPaintInvalidation());
+
+ vertical_scrollbar->ClearThumbNeedsRepaint();
+ mock_scrollable_area->ClearNeedsPaintInvalidationForScrollControls();
+
+ // Release should cause invalidation.
+ vertical_scrollbar->SetPressedPart(kNoPart);
+ EXPECT_TRUE(vertical_scrollbar->ThumbNeedsRepaint());
+ EXPECT_TRUE(mock_scrollable_area->VerticalScrollbarNeedsPaintInvalidation());
+
+ vertical_scrollbar->ClearThumbNeedsRepaint();
+ mock_scrollable_area->ClearNeedsPaintInvalidationForScrollControls();
+
+ // Move off should cause invalidation
+ vertical_scrollbar->SetHoveredPart(kNoPart);
+ EXPECT_TRUE(vertical_scrollbar->ThumbNeedsRepaint());
+ EXPECT_TRUE(mock_scrollable_area->VerticalScrollbarNeedsPaintInvalidation());
+
+ vertical_scrollbar->ClearThumbNeedsRepaint();
+ mock_scrollable_area->ClearNeedsPaintInvalidationForScrollControls();
+
+ // Hiding the scrollbar should invalidate the layer (SetNeedsDisplay) but not
+ // trigger repaint of the thumb resouce, since the compositor will give the
+ // entire layer opacity 0.
+ EXPECT_CALL(*mock_scrollable_area, ScrollbarsHiddenIfOverlay())
+ .WillOnce(Return(true));
+ vertical_scrollbar->SetEnabled(false);
+ EXPECT_FALSE(vertical_scrollbar->ThumbNeedsRepaint());
+ EXPECT_TRUE(mock_scrollable_area->VerticalScrollbarNeedsPaintInvalidation());
+
+ mock_scrollable_area->ClearNeedsPaintInvalidationForScrollControls();
+
+ // Showing the scrollbar needs to repaint the thumb resource, since it may
+ // have been repainted in the disabled state while hidden (e.g. from
+ // SetProportion on bounds changes).
+ EXPECT_CALL(*mock_scrollable_area, ScrollbarsHiddenIfOverlay())
+ .WillOnce(Return(false));
+ vertical_scrollbar->SetEnabled(true);
+ EXPECT_TRUE(vertical_scrollbar->ThumbNeedsRepaint());
+ EXPECT_TRUE(mock_scrollable_area->VerticalScrollbarNeedsPaintInvalidation());
+
+ ThreadState::Current()->CollectAllGarbage();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scroll/smooth_scroll_sequencer.cc b/chromium/third_party/blink/renderer/platform/scroll/smooth_scroll_sequencer.cc
new file mode 100644
index 00000000000..31fce84c37a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/smooth_scroll_sequencer.cc
@@ -0,0 +1,59 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scroll/smooth_scroll_sequencer.h"
+
+#include "third_party/blink/renderer/platform/scroll/programmatic_scroll_animator.h"
+#include "third_party/blink/renderer/platform/scroll/scrollable_area.h"
+
+namespace blink {
+
+void SequencedScroll::Trace(blink::Visitor* visitor) {
+ visitor->Trace(scrollable_area);
+}
+
+void SmoothScrollSequencer::QueueAnimation(ScrollableArea* scrollable,
+ ScrollOffset offset,
+ ScrollBehavior behavior) {
+ if (scrollable->ClampScrollOffset(offset) != scrollable->GetScrollOffset())
+ queue_.push_back(new SequencedScroll(scrollable, offset, behavior));
+}
+
+void SmoothScrollSequencer::RunQueuedAnimations() {
+ if (queue_.IsEmpty()) {
+ current_scrollable_ = nullptr;
+ return;
+ }
+ SequencedScroll* sequenced_scroll = queue_.back();
+ queue_.pop_back();
+ current_scrollable_ = sequenced_scroll->scrollable_area;
+ current_scrollable_->SetScrollOffset(sequenced_scroll->scroll_offset,
+ kSequencedScroll,
+ sequenced_scroll->scroll_behavior);
+}
+
+void SmoothScrollSequencer::AbortAnimations() {
+ if (current_scrollable_) {
+ current_scrollable_->CancelProgrammaticScrollAnimation();
+ current_scrollable_ = nullptr;
+ }
+ queue_.clear();
+}
+
+void SmoothScrollSequencer::DidDisposeScrollableArea(
+ const ScrollableArea& area) {
+ for (Member<SequencedScroll>& sequenced_scroll : queue_) {
+ if (sequenced_scroll->scrollable_area.Get() == &area) {
+ AbortAnimations();
+ break;
+ }
+ }
+}
+
+void SmoothScrollSequencer::Trace(blink::Visitor* visitor) {
+ visitor->Trace(queue_);
+ visitor->Trace(current_scrollable_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scroll/smooth_scroll_sequencer.h b/chromium/third_party/blink/renderer/platform/scroll/smooth_scroll_sequencer.h
new file mode 100644
index 00000000000..0af018e513c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/smooth_scroll_sequencer.h
@@ -0,0 +1,64 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SMOOTH_SCROLL_SEQUENCER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SMOOTH_SCROLL_SEQUENCER_H_
+
+#include <utility>
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/scroll/scroll_types.h"
+
+namespace blink {
+
+class ScrollableArea;
+
+struct SequencedScroll final : public GarbageCollected<SequencedScroll> {
+ SequencedScroll();
+
+ SequencedScroll(ScrollableArea* area,
+ ScrollOffset offset,
+ ScrollBehavior behavior)
+ : scrollable_area(area),
+ scroll_offset(offset),
+ scroll_behavior(behavior) {}
+
+ SequencedScroll(const SequencedScroll& other)
+ : scrollable_area(other.scrollable_area),
+ scroll_offset(other.scroll_offset),
+ scroll_behavior(other.scroll_behavior) {}
+
+ Member<ScrollableArea> scrollable_area;
+ ScrollOffset scroll_offset;
+ ScrollBehavior scroll_behavior;
+
+ void Trace(blink::Visitor*);
+};
+
+// A sequencer that queues the nested scrollers from inside to outside,
+// so that they can be animated from outside to inside when smooth scroll
+// is called.
+class PLATFORM_EXPORT SmoothScrollSequencer final
+ : public GarbageCollected<SmoothScrollSequencer> {
+ public:
+ SmoothScrollSequencer() = default;
+ // Add a scroll offset animation to the back of a queue.
+ void QueueAnimation(ScrollableArea*, ScrollOffset, ScrollBehavior);
+
+ // Run the animation at the back of the queue.
+ void RunQueuedAnimations();
+
+ // Abort the currently running animation and all the animations in the queue.
+ void AbortAnimations();
+
+ void DidDisposeScrollableArea(const ScrollableArea&);
+
+ void Trace(blink::Visitor*);
+
+ private:
+ HeapVector<Member<SequencedScroll>> queue_;
+ Member<ScrollableArea> current_scrollable_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCROLL_SMOOTH_SCROLL_SEQUENCER_H_
diff --git a/chromium/third_party/blink/renderer/platform/scroll/web_scroll_into_view_params.cc b/chromium/third_party/blink/renderer/platform/scroll/web_scroll_into_view_params.cc
new file mode 100644
index 00000000000..d766d5c70ea
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/web_scroll_into_view_params.cc
@@ -0,0 +1,122 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_scroll_into_view_params.h"
+
+#include "third_party/blink/renderer/platform/wtf/size_assertions.h"
+
+namespace blink {
+namespace {
+using AlignmentBehavior = WebScrollIntoViewParams::AlignmentBehavior;
+using Alignment = WebScrollIntoViewParams::Alignment;
+using Type = WebScrollIntoViewParams::Type;
+using Behavior = WebScrollIntoViewParams::Behavior;
+
+// Make sure we keep the public enums in sync with the internal ones.
+ASSERT_SIZE(AlignmentBehavior, ScrollAlignmentBehavior)
+STATIC_ASSERT_ENUM(AlignmentBehavior::kNoScroll,
+ ScrollAlignmentBehavior::kScrollAlignmentNoScroll);
+STATIC_ASSERT_ENUM(AlignmentBehavior::kCenter,
+ ScrollAlignmentBehavior::kScrollAlignmentCenter);
+STATIC_ASSERT_ENUM(AlignmentBehavior::kTop,
+ ScrollAlignmentBehavior::kScrollAlignmentTop);
+STATIC_ASSERT_ENUM(AlignmentBehavior::kBottom,
+ ScrollAlignmentBehavior::kScrollAlignmentBottom);
+STATIC_ASSERT_ENUM(AlignmentBehavior::kLeft,
+ ScrollAlignmentBehavior::kScrollAlignmentLeft);
+STATIC_ASSERT_ENUM(AlignmentBehavior::kRight,
+ ScrollAlignmentBehavior::kScrollAlignmentRight);
+STATIC_ASSERT_ENUM(AlignmentBehavior::kClosestEdge,
+ ScrollAlignmentBehavior::kScrollAlignmentClosestEdge);
+
+ASSERT_SIZE(Type, ScrollType)
+STATIC_ASSERT_ENUM(Type::kUser, ScrollType::kUserScroll);
+STATIC_ASSERT_ENUM(Type::kProgrammatic, ScrollType::kProgrammaticScroll);
+STATIC_ASSERT_ENUM(Type::kClamping, ScrollType::kClampingScroll);
+STATIC_ASSERT_ENUM(Type::kAnchoring, ScrollType::kAnchoringScroll);
+STATIC_ASSERT_ENUM(Type::kSequenced, ScrollType::kSequencedScroll);
+
+ASSERT_SIZE(Behavior, ScrollBehavior)
+STATIC_ASSERT_ENUM(Behavior::kAuto, ScrollBehavior::kScrollBehaviorAuto);
+STATIC_ASSERT_ENUM(Behavior::kInstant, ScrollBehavior::kScrollBehaviorInstant);
+STATIC_ASSERT_ENUM(Behavior::kSmooth, ScrollBehavior::kScrollBehaviorSmooth);
+
+AlignmentBehavior ToAlignmentBehavior(
+ ScrollAlignmentBehavior scroll_alignment_behavior) {
+ return static_cast<AlignmentBehavior>(
+ static_cast<int>(scroll_alignment_behavior));
+}
+
+ScrollAlignmentBehavior ToScrollAlignmentBehavior(
+ AlignmentBehavior alignment_behavior) {
+ return static_cast<ScrollAlignmentBehavior>(
+ static_cast<int>(alignment_behavior));
+}
+
+ScrollAlignment ToScrollAlignment(Alignment alignment) {
+ return {ToScrollAlignmentBehavior(alignment.rect_visible),
+ ToScrollAlignmentBehavior(alignment.rect_hidden),
+ ToScrollAlignmentBehavior(alignment.rect_partial)};
+}
+
+Type FromScrollType(ScrollType type) {
+ return static_cast<Type>(static_cast<int>(type));
+}
+
+ScrollType ToScrollType(Type type) {
+ return static_cast<ScrollType>(static_cast<int>(type));
+}
+
+Behavior FromScrollBehavior(ScrollBehavior behavior) {
+ return static_cast<Behavior>(static_cast<int>(behavior));
+}
+
+ScrollBehavior ToScrollBehavior(Behavior behavior) {
+ return static_cast<ScrollBehavior>(static_cast<int>(behavior));
+}
+
+} // namespace
+
+WebScrollIntoViewParams::Alignment::Alignment(
+ const ScrollAlignment& scroll_alignment)
+ : rect_visible(ToAlignmentBehavior(
+ ScrollAlignment::GetVisibleBehavior(scroll_alignment))),
+ rect_hidden(ToAlignmentBehavior(
+ ScrollAlignment::GetHiddenBehavior(scroll_alignment))),
+ rect_partial(ToAlignmentBehavior(
+ ScrollAlignment::GetPartialBehavior(scroll_alignment))) {}
+
+WebScrollIntoViewParams::WebScrollIntoViewParams(
+ ScrollAlignment scroll_alignment_x,
+ ScrollAlignment scroll_alignment_y,
+ ScrollType scroll_type,
+ bool make_visible_in_visual_viewport,
+ ScrollBehavior scroll_behavior,
+ bool is_for_scroll_sequence,
+ bool zoom_into_rect)
+ : align_x(scroll_alignment_x),
+ align_y(scroll_alignment_y),
+ type(FromScrollType(scroll_type)),
+ make_visible_in_visual_viewport(make_visible_in_visual_viewport),
+ behavior(FromScrollBehavior(scroll_behavior)),
+ is_for_scroll_sequence(is_for_scroll_sequence),
+ zoom_into_rect(zoom_into_rect) {}
+
+ScrollAlignment WebScrollIntoViewParams::GetScrollAlignmentX() const {
+ return ToScrollAlignment(align_x);
+}
+
+ScrollAlignment WebScrollIntoViewParams::GetScrollAlignmentY() const {
+ return ToScrollAlignment(align_y);
+}
+
+ScrollType WebScrollIntoViewParams::GetScrollType() const {
+ return ToScrollType(type);
+}
+
+ScrollBehavior WebScrollIntoViewParams::GetScrollBehavior() const {
+ return ToScrollBehavior(behavior);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scroll/web_scrollbar_theme.mm b/chromium/third_party/blink/renderer/platform/scroll/web_scrollbar_theme.mm
new file mode 100644
index 00000000000..4494a7d709d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scroll/web_scrollbar_theme.mm
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/public/platform/mac/web_scrollbar_theme.h"
+
+#import <AppKit/AppKit.h>
+
+#include "third_party/blink/renderer/platform/mac/ns_scroller_imp_details.h"
+#include "third_party/blink/renderer/platform/scroll/scrollbar_theme_mac.h"
+
+namespace blink {
+
+static_assert(static_cast<NSScrollerStyle>(kScrollerStyleLegacy) ==
+ NSScrollerStyleLegacy,
+ "ScrollerStyleLegacy must match NSScrollerStyleLegacy");
+static_assert(static_cast<NSScrollerStyle>(kScrollerStyleOverlay) ==
+ NSScrollerStyleOverlay,
+ "ScrollerStyleOverlay must match NSScrollerStyleOverlay");
+
+void WebScrollbarTheme::UpdateScrollbarsWithNSDefaults(
+ float initial_button_delay,
+ float autoscroll_button_delay,
+ ScrollerStyle preferred_scroller_style,
+ bool redraw,
+ WebScrollbarButtonsPlacement button_placement) {
+ ScrollbarTheme& theme = ScrollbarTheme::DeprecatedStaticGetTheme();
+ if (theme.IsMockTheme())
+ return;
+
+ static_cast<ScrollbarThemeMac&>(theme).PreferencesChanged(
+ initial_button_delay, autoscroll_button_delay,
+ static_cast<NSScrollerStyle>(preferred_scroller_style), redraw,
+ button_placement);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/serialized_resource.h b/chromium/third_party/blink/renderer/platform/serialized_resource.h
new file mode 100644
index 00000000000..27a2f173036
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/serialized_resource.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_SERIALIZED_RESOURCE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SERIALIZED_RESOURCE_H_
+
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+struct SerializedResource {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ KURL url;
+ String mime_type;
+ scoped_refptr<const SharedBuffer> data;
+
+ SerializedResource(const KURL& url,
+ const String& mime_type,
+ scoped_refptr<const SharedBuffer> data)
+ : url(url), mime_type(mime_type), data(std::move(data)) {}
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SERIALIZED_RESOURCE_H_
diff --git a/chromium/third_party/blink/renderer/platform/shared_buffer.cc b/chromium/third_party/blink/renderer/platform/shared_buffer.cc
new file mode 100644
index 00000000000..986899f040d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/shared_buffer.cc
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/shared_buffer.h"
+
+#include "third_party/blink/renderer/platform/instrumentation/tracing/web_process_memory_dump.h"
+#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
+#include "third_party/blink/renderer/platform/wtf/text/utf8.h"
+#include "third_party/skia/include/core/SkData.h"
+
+namespace blink {
+
+static inline size_t SegmentIndex(size_t position) {
+ return position / SharedBuffer::kSegmentSize;
+}
+
+static inline size_t OffsetInSegment(size_t position) {
+ return position % SharedBuffer::kSegmentSize;
+}
+
+static inline char* AllocateSegment() {
+ return static_cast<char*>(WTF::Partitions::FastMalloc(
+ SharedBuffer::kSegmentSize, "blink::SharedBuffer"));
+}
+
+static inline void FreeSegment(char* p) {
+ WTF::Partitions::FastFree(p);
+}
+
+SharedBuffer::SharedBuffer() : size_(0) {}
+
+SharedBuffer::SharedBuffer(size_t size) : size_(size), buffer_(size) {}
+
+SharedBuffer::SharedBuffer(const char* data, size_t size) : size_(0) {
+ AppendInternal(data, size);
+}
+
+SharedBuffer::SharedBuffer(const unsigned char* data, size_t size) : size_(0) {
+ AppendInternal(reinterpret_cast<const char*>(data), size);
+}
+
+SharedBuffer::~SharedBuffer() {
+ Clear();
+}
+
+scoped_refptr<SharedBuffer> SharedBuffer::AdoptVector(Vector<char>& vector) {
+ scoped_refptr<SharedBuffer> buffer = Create();
+ buffer->buffer_.swap(vector);
+ buffer->size_ = buffer->buffer_.size();
+ return buffer;
+}
+
+size_t SharedBuffer::size() const {
+ return size_;
+}
+
+const char* SharedBuffer::Data() const {
+ MergeSegmentsIntoBuffer();
+ return buffer_.data();
+}
+
+void SharedBuffer::Append(const SharedBuffer& data) {
+ const char* segment;
+ size_t position = 0;
+ while (size_t length = data.GetSomeDataInternal(segment, position)) {
+ Append(segment, length);
+ position += length;
+ }
+}
+
+void SharedBuffer::AppendInternal(const char* data, size_t length) {
+ if (!length)
+ return;
+
+ DCHECK_GE(size_, buffer_.size());
+ size_t position_in_segment = OffsetInSegment(size_ - buffer_.size());
+ size_ += length;
+
+ if (size_ <= kSegmentSize) {
+ // No need to use segments for small resource data.
+ buffer_.Append(data, length);
+ return;
+ }
+
+ char* segment;
+ if (!position_in_segment) {
+ segment = AllocateSegment();
+ segments_.push_back(segment);
+ } else
+ segment = segments_.back() + position_in_segment;
+
+ size_t segment_free_space = kSegmentSize - position_in_segment;
+ size_t bytes_to_copy = std::min(length, segment_free_space);
+
+ for (;;) {
+ memcpy(segment, data, bytes_to_copy);
+ if (length == bytes_to_copy)
+ break;
+
+ length -= bytes_to_copy;
+ data += bytes_to_copy;
+ segment = AllocateSegment();
+ segments_.push_back(segment);
+ bytes_to_copy = std::min(length, static_cast<size_t>(kSegmentSize));
+ }
+}
+
+void SharedBuffer::Append(const Vector<char>& data) {
+ Append(data.data(), data.size());
+}
+
+void SharedBuffer::Clear() {
+ for (size_t i = 0; i < segments_.size(); ++i)
+ FreeSegment(segments_[i]);
+
+ segments_.clear();
+ size_ = 0;
+ buffer_.clear();
+}
+
+Vector<char> SharedBuffer::Copy() const {
+ Vector<char> buffer;
+ buffer.ReserveInitialCapacity(size_);
+
+ ForEachSegment([&buffer](const char* segment, size_t segment_size,
+ size_t segment_offset) -> bool {
+ buffer.Append(segment, segment_size);
+ return true;
+ });
+
+ DCHECK_EQ(buffer.size(), size_);
+ return buffer;
+}
+
+void SharedBuffer::MergeSegmentsIntoBuffer() const {
+ size_t buffer_size = buffer_.size();
+ if (size_ > buffer_size) {
+ size_t bytes_left = size_ - buffer_size;
+ for (size_t i = 0; i < segments_.size(); ++i) {
+ size_t bytes_to_copy =
+ std::min(bytes_left, static_cast<size_t>(kSegmentSize));
+ buffer_.Append(segments_[i], bytes_to_copy);
+ bytes_left -= bytes_to_copy;
+ FreeSegment(segments_[i]);
+ }
+ segments_.clear();
+ }
+}
+
+size_t SharedBuffer::GetSomeDataInternal(const char*& some_data,
+ size_t position) const {
+ size_t total_size = size();
+ if (position >= total_size) {
+ some_data = nullptr;
+ return 0;
+ }
+
+ SECURITY_DCHECK(position < size_);
+ size_t consecutive_size = buffer_.size();
+ if (position < consecutive_size) {
+ some_data = buffer_.data() + position;
+ return consecutive_size - position;
+ }
+
+ position -= consecutive_size;
+ size_t segments = segments_.size();
+ size_t max_segmented_size = segments * kSegmentSize;
+ size_t segment = SegmentIndex(position);
+ if (segment < segments) {
+ size_t bytes_left = total_size - consecutive_size;
+ size_t segmented_size = std::min(max_segmented_size, bytes_left);
+
+ size_t position_in_segment = OffsetInSegment(position);
+ some_data = segments_[segment] + position_in_segment;
+ return segment == segments - 1 ? segmented_size - position
+ : kSegmentSize - position_in_segment;
+ }
+ NOTREACHED();
+ return 0;
+}
+
+bool SharedBuffer::GetBytesInternal(void* dest, size_t byte_length) const {
+ if (!dest)
+ return false;
+
+ const char* segment = nullptr;
+ size_t load_position = 0;
+ size_t write_position = 0;
+ while (byte_length > 0) {
+ size_t load_size = GetSomeDataInternal(segment, load_position);
+ if (load_size == 0)
+ break;
+
+ if (byte_length < load_size)
+ load_size = byte_length;
+ memcpy(static_cast<char*>(dest) + write_position, segment, load_size);
+ load_position += load_size;
+ write_position += load_size;
+ byte_length -= load_size;
+ }
+
+ return byte_length == 0;
+}
+
+sk_sp<SkData> SharedBuffer::GetAsSkData() const {
+ size_t buffer_length = size();
+ sk_sp<SkData> data = SkData::MakeUninitialized(buffer_length);
+ char* buffer = static_cast<char*>(data->writable_data());
+ const char* segment = nullptr;
+ size_t position = 0;
+ while (size_t segment_size = GetSomeDataInternal(segment, position)) {
+ memcpy(buffer + position, segment, segment_size);
+ position += segment_size;
+ }
+
+ if (position != buffer_length) {
+ NOTREACHED();
+ // Don't return the incomplete SkData.
+ return nullptr;
+ }
+ return data;
+}
+
+void SharedBuffer::OnMemoryDump(const String& dump_prefix,
+ WebProcessMemoryDump* memory_dump) const {
+ if (buffer_.size()) {
+ WebMemoryAllocatorDump* dump =
+ memory_dump->CreateMemoryAllocatorDump(dump_prefix + "/shared_buffer");
+ dump->AddScalar("size", "bytes", buffer_.size());
+ memory_dump->AddSuballocation(
+ dump->Guid(), String(WTF::Partitions::kAllocatedObjectPoolName));
+ } else {
+ // If there is data in the segments, then it should have been allocated
+ // using fastMalloc.
+ const String data_dump_name = dump_prefix + "/segments";
+ auto dump = memory_dump->CreateMemoryAllocatorDump(data_dump_name);
+ dump->AddScalar("size", "bytes", size_);
+ memory_dump->AddSuballocation(
+ dump->Guid(), String(WTF::Partitions::kAllocatedObjectPoolName));
+ }
+}
+
+SharedBuffer::DeprecatedFlatData::DeprecatedFlatData(
+ scoped_refptr<const SharedBuffer> buffer)
+ : buffer_(std::move(buffer)) {
+ DCHECK(buffer_);
+
+ if (buffer_->size() <= buffer_->buffer_.size()) {
+ // The SharedBuffer is not segmented - just point to its data.
+ data_ = buffer_->buffer_.data();
+ return;
+ }
+
+ // Merge all segments.
+ flat_buffer_.ReserveInitialCapacity(buffer_->size());
+ buffer_->ForEachSegment([this](const char* segment, size_t segment_size,
+ size_t segment_offset) -> bool {
+ flat_buffer_.Append(segment, segment_size);
+ return true;
+ });
+
+ data_ = flat_buffer_.data();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/shared_buffer.h b/chromium/third_party/blink/renderer/platform/shared_buffer.h
new file mode 100644
index 00000000000..c73c303c7c4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/shared_buffer.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_SHARED_BUFFER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SHARED_BUFFER_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+class SkData;
+template <typename T>
+class sk_sp;
+
+namespace blink {
+
+class WebProcessMemoryDump;
+
+class PLATFORM_EXPORT SharedBuffer : public RefCounted<SharedBuffer> {
+ public:
+ enum : unsigned { kSegmentSize = 0x1000 };
+
+ static scoped_refptr<SharedBuffer> Create() {
+ return base::AdoptRef(new SharedBuffer);
+ }
+
+ HAS_STRICTLY_TYPED_ARG
+ static scoped_refptr<SharedBuffer> Create(STRICTLY_TYPED_ARG(size)) {
+ STRICT_ARG_TYPE(size_t);
+ return base::AdoptRef(new SharedBuffer(size));
+ }
+
+ HAS_STRICTLY_TYPED_ARG
+ static scoped_refptr<SharedBuffer> Create(const char* data,
+ STRICTLY_TYPED_ARG(size)) {
+ STRICT_ARG_TYPE(size_t);
+ return base::AdoptRef(new SharedBuffer(data, size));
+ }
+
+ HAS_STRICTLY_TYPED_ARG
+ static scoped_refptr<SharedBuffer> Create(const unsigned char* data,
+ STRICTLY_TYPED_ARG(size)) {
+ STRICT_ARG_TYPE(size_t);
+ return base::AdoptRef(new SharedBuffer(data, size));
+ }
+
+ static scoped_refptr<SharedBuffer> AdoptVector(Vector<char>&);
+
+ ~SharedBuffer();
+
+ // DEPRECATED: use a segment iterator, FlatData or Copy() instead.
+ //
+ // Calling this function will force internal segmented buffers to be merged
+ // into a flat buffer. Use getSomeData() whenever possible for better
+ // performance.
+ const char* Data() const;
+
+ size_t size() const;
+
+ bool IsEmpty() const { return !size(); }
+
+ void Append(const SharedBuffer&);
+
+ HAS_STRICTLY_TYPED_ARG
+ void Append(const char* data, STRICTLY_TYPED_ARG(size)) {
+ ALLOW_NUMERIC_ARG_TYPES_PROMOTABLE_TO(size_t);
+ AppendInternal(data, size);
+ }
+ void Append(const Vector<char>&);
+
+ void Clear();
+
+ // Copies the segmented data into a contiguous buffer. Use GetSomeData() or
+ // ForEachSegment() whenever possible, as they are cheaper.
+ Vector<char> Copy() const;
+
+ // Return the number of consecutive bytes after "position". "data"
+ // points to the first byte.
+ // Return 0 when no more data left.
+ // When extracting all data with getSomeData(), the caller should
+ // repeat calling it until it returns 0.
+ // Usage:
+ // const char* segment;
+ // size_t pos = 0;
+ // while (size_t length = sharedBuffer->getSomeData(segment, pos)) {
+ // // Use the data. for example: decoder->decode(segment, length);
+ // pos += length;
+ // }
+ HAS_STRICTLY_TYPED_ARG
+ size_t GetSomeData(
+ const char*& data,
+ STRICTLY_TYPED_ARG(position) = static_cast<size_t>(0)) const {
+ STRICT_ARG_TYPE(size_t);
+ return GetSomeDataInternal(data, position);
+ }
+
+ // Copies |byteLength| bytes from the beginning of the content data into
+ // |dest| as a flat buffer. Returns true on success, otherwise the content of
+ // |dest| is not guaranteed.
+ HAS_STRICTLY_TYPED_ARG
+ WARN_UNUSED_RESULT
+ bool GetBytes(void* dest, STRICTLY_TYPED_ARG(byte_length)) const {
+ STRICT_ARG_TYPE(size_t);
+ return GetBytesInternal(dest, byte_length);
+ }
+
+ // Creates an SkData and copies this SharedBuffer's contents to that
+ // SkData without merging segmented buffers into a flat buffer.
+ sk_sp<SkData> GetAsSkData() const;
+
+ void OnMemoryDump(const String& dump_prefix, WebProcessMemoryDump*) const;
+
+ // Helper for applying a lambda to all data segments, sequentially:
+ //
+ // bool func(const char* segment, size_t segment_size,
+ // size_t segment_offset);
+ //
+ // The iterator stops early when the lambda returns |false|.
+ //
+ template <typename Func>
+ void ForEachSegment(Func&& func) const {
+ const char* segment;
+ size_t pos = 0;
+
+ while (size_t length = GetSomeData(segment, pos)) {
+ if (!func(segment, length, pos))
+ break;
+ pos += length;
+ }
+ }
+
+ // Helper for providing a contiguous view of the data. If the SharedBuffer is
+ // segmented, this will copy/merge all segments into a temporary buffer.
+ // In general, clients should use the efficient/segmented accessors.
+ class PLATFORM_EXPORT DeprecatedFlatData {
+ STACK_ALLOCATED();
+
+ public:
+ explicit DeprecatedFlatData(scoped_refptr<const SharedBuffer>);
+
+ const char* Data() const { return data_; }
+ size_t size() const { return buffer_->size(); }
+
+ private:
+ scoped_refptr<const SharedBuffer> buffer_;
+ Vector<char> flat_buffer_;
+ const char* data_;
+ };
+
+ private:
+ SharedBuffer();
+ explicit SharedBuffer(size_t);
+ SharedBuffer(const char*, size_t);
+ SharedBuffer(const unsigned char*, size_t);
+
+ // See SharedBuffer::data().
+ void MergeSegmentsIntoBuffer() const;
+
+ void AppendInternal(const char* data, size_t);
+ bool GetBytesInternal(void* dest, size_t) const;
+ size_t GetSomeDataInternal(const char*& data, size_t position) const;
+
+ size_t size_;
+ mutable Vector<char> buffer_;
+ mutable Vector<char*> segments_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SHARED_BUFFER_H_
diff --git a/chromium/third_party/blink/renderer/platform/shared_buffer_chunk_reader.cc b/chromium/third_party/blink/renderer/platform/shared_buffer_chunk_reader.cc
new file mode 100644
index 00000000000..f0664e46752
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/shared_buffer_chunk_reader.cc
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/renderer/platform/shared_buffer_chunk_reader.h"
+
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+
+namespace blink {
+
+SharedBufferChunkReader::SharedBufferChunkReader(
+ scoped_refptr<const SharedBuffer> buffer,
+ const Vector<char>& separator)
+ : buffer_(std::move(buffer)),
+ buffer_position_(0),
+ segment_(nullptr),
+ segment_length_(0),
+ segment_index_(0),
+ reached_end_of_file_(false),
+ separator_(separator),
+ separator_index_(0) {}
+
+SharedBufferChunkReader::SharedBufferChunkReader(
+ scoped_refptr<const SharedBuffer> buffer,
+ const char* separator)
+ : buffer_(std::move(buffer)),
+ buffer_position_(0),
+ segment_(nullptr),
+ segment_length_(0),
+ segment_index_(0),
+ reached_end_of_file_(false),
+ separator_index_(0) {
+ SetSeparator(separator);
+}
+
+void SharedBufferChunkReader::SetSeparator(const Vector<char>& separator) {
+ separator_ = separator;
+}
+
+void SharedBufferChunkReader::SetSeparator(const char* separator) {
+ separator_.clear();
+ separator_.Append(separator, strlen(separator));
+}
+
+bool SharedBufferChunkReader::NextChunk(Vector<char>& chunk,
+ bool include_separator) {
+ if (reached_end_of_file_)
+ return false;
+
+ chunk.clear();
+ while (true) {
+ while (segment_index_ < segment_length_) {
+ char current_character = segment_[segment_index_++];
+ if (current_character != separator_[separator_index_]) {
+ if (separator_index_ > 0) {
+ SECURITY_DCHECK(separator_index_ <= separator_.size());
+ chunk.Append(separator_.data(), separator_index_);
+ separator_index_ = 0;
+ }
+ chunk.push_back(current_character);
+ continue;
+ }
+ separator_index_++;
+ if (separator_index_ == separator_.size()) {
+ if (include_separator)
+ chunk.AppendVector(separator_);
+ separator_index_ = 0;
+ return true;
+ }
+ }
+
+ // Read the next segment.
+ segment_index_ = 0;
+ buffer_position_ += segment_length_;
+ segment_length_ = buffer_->GetSomeData(segment_, buffer_position_);
+ if (!segment_length_) {
+ reached_end_of_file_ = true;
+ if (separator_index_ > 0)
+ chunk.Append(separator_.data(), separator_index_);
+ return !chunk.IsEmpty();
+ }
+ }
+ NOTREACHED();
+ return false;
+}
+
+String SharedBufferChunkReader::NextChunkAsUTF8StringWithLatin1Fallback(
+ bool include_separator) {
+ Vector<char> data;
+ if (!NextChunk(data, include_separator))
+ return String();
+
+ return data.size()
+ ? String::FromUTF8WithLatin1Fallback(data.data(), data.size())
+ : g_empty_string;
+}
+
+size_t SharedBufferChunkReader::Peek(Vector<char>& data,
+ size_t requested_size) {
+ data.clear();
+ if (requested_size <= segment_length_ - segment_index_) {
+ data.Append(segment_ + segment_index_, requested_size);
+ return requested_size;
+ }
+
+ size_t read_bytes_count = segment_length_ - segment_index_;
+ data.Append(segment_ + segment_index_, read_bytes_count);
+
+ size_t buffer_position = buffer_position_ + segment_length_;
+ const char* segment = nullptr;
+ while (size_t segment_length =
+ buffer_->GetSomeData(segment, buffer_position)) {
+ if (requested_size <= read_bytes_count + segment_length) {
+ data.Append(segment, requested_size - read_bytes_count);
+ read_bytes_count += (requested_size - read_bytes_count);
+ break;
+ }
+ data.Append(segment, segment_length);
+ read_bytes_count += segment_length;
+ buffer_position += segment_length;
+ }
+ return read_bytes_count;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/shared_buffer_chunk_reader.h b/chromium/third_party/blink/renderer/platform/shared_buffer_chunk_reader.h
new file mode 100644
index 00000000000..3ebfcacd193
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/shared_buffer_chunk_reader.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_SHARED_BUFFER_CHUNK_READER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SHARED_BUFFER_CHUNK_READER_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class SharedBuffer;
+
+class PLATFORM_EXPORT SharedBufferChunkReader final {
+ DISALLOW_NEW();
+ WTF_MAKE_NONCOPYABLE(SharedBufferChunkReader);
+
+ public:
+ SharedBufferChunkReader(scoped_refptr<const SharedBuffer>,
+ const Vector<char>& separator);
+ SharedBufferChunkReader(scoped_refptr<const SharedBuffer>,
+ const char* separator);
+
+ void SetSeparator(const Vector<char>&);
+ void SetSeparator(const char*);
+
+ // Returns false when the end of the buffer was reached.
+ bool NextChunk(Vector<char>& data, bool include_separator = false);
+
+ // Returns a null string when the end of the buffer has been reached.
+ String NextChunkAsUTF8StringWithLatin1Fallback(
+ bool include_separator = false);
+
+ // Reads size bytes at the current location in the buffer, without changing
+ // the buffer position.
+ // Returns the number of bytes read. That number might be less than the
+ // specified size if the end of the buffer was reached.
+ size_t Peek(Vector<char>&, size_t);
+
+ private:
+ scoped_refptr<const SharedBuffer> buffer_;
+ size_t buffer_position_;
+ const char* segment_;
+ size_t segment_length_;
+ size_t segment_index_;
+ bool reached_end_of_file_;
+ Vector<char> separator_;
+ size_t separator_index_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/shared_buffer_test.cc b/chromium/third_party/blink/renderer/platform/shared_buffer_test.cc
new file mode 100644
index 00000000000..a1124645829
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/shared_buffer_test.cc
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/renderer/platform/shared_buffer.h"
+
+#include <algorithm>
+#include <cstdlib>
+#include <memory>
+#include "base/memory/scoped_refptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+TEST(SharedBufferTest, getAsBytes) {
+ char test_data0[] = "Hello";
+ char test_data1[] = "World";
+ char test_data2[] = "Goodbye";
+
+ scoped_refptr<SharedBuffer> shared_buffer =
+ SharedBuffer::Create(test_data0, strlen(test_data0));
+ shared_buffer->Append(test_data1, strlen(test_data1));
+ shared_buffer->Append(test_data2, strlen(test_data2));
+
+ const size_t size = shared_buffer->size();
+ auto data = std::make_unique<char[]>(size);
+ ASSERT_TRUE(shared_buffer->GetBytes(data.get(), size));
+
+ char expected_concatenation[] = "HelloWorldGoodbye";
+ ASSERT_EQ(strlen(expected_concatenation), size);
+ EXPECT_EQ(0, memcmp(expected_concatenation, data.get(),
+ strlen(expected_concatenation)));
+}
+
+TEST(SharedBufferTest, getPartAsBytes) {
+ char test_data0[] = "Hello";
+ char test_data1[] = "World";
+ char test_data2[] = "Goodbye";
+
+ scoped_refptr<SharedBuffer> shared_buffer =
+ SharedBuffer::Create(test_data0, strlen(test_data0));
+ shared_buffer->Append(test_data1, strlen(test_data1));
+ shared_buffer->Append(test_data2, strlen(test_data2));
+
+ struct TestData {
+ size_t size;
+ const char* expected;
+ } test_data[] = {
+ {17, "HelloWorldGoodbye"}, {7, "HelloWo"}, {3, "Hel"},
+ };
+ for (TestData& test : test_data) {
+ auto data = std::make_unique<char[]>(test.size);
+ ASSERT_TRUE(shared_buffer->GetBytes(data.get(), test.size));
+ EXPECT_EQ(0, memcmp(test.expected, data.get(), test.size));
+ }
+}
+
+TEST(SharedBufferTest, getAsBytesLargeSegments) {
+ Vector<char> vector0(0x4000);
+ for (size_t i = 0; i < vector0.size(); ++i)
+ vector0[i] = 'a';
+ Vector<char> vector1(0x4000);
+ for (size_t i = 0; i < vector1.size(); ++i)
+ vector1[i] = 'b';
+ Vector<char> vector2(0x4000);
+ for (size_t i = 0; i < vector2.size(); ++i)
+ vector2[i] = 'c';
+
+ scoped_refptr<SharedBuffer> shared_buffer =
+ SharedBuffer::AdoptVector(vector0);
+ shared_buffer->Append(vector1);
+ shared_buffer->Append(vector2);
+
+ const size_t size = shared_buffer->size();
+ auto data = std::make_unique<char[]>(size);
+ ASSERT_TRUE(shared_buffer->GetBytes(data.get(), size));
+
+ ASSERT_EQ(0x4000U + 0x4000U + 0x4000U, size);
+ int position = 0;
+ for (int i = 0; i < 0x4000; ++i) {
+ EXPECT_EQ('a', data[position]);
+ ++position;
+ }
+ for (int i = 0; i < 0x4000; ++i) {
+ EXPECT_EQ('b', data[position]);
+ ++position;
+ }
+ for (int i = 0; i < 0x4000; ++i) {
+ EXPECT_EQ('c', data[position]);
+ ++position;
+ }
+}
+
+TEST(SharedBufferTest, copy) {
+ Vector<char> test_data(10000);
+ std::generate(test_data.begin(), test_data.end(), &std::rand);
+
+ size_t length = test_data.size();
+ scoped_refptr<SharedBuffer> shared_buffer =
+ SharedBuffer::Create(test_data.data(), length);
+ shared_buffer->Append(test_data.data(), length);
+ shared_buffer->Append(test_data.data(), length);
+ shared_buffer->Append(test_data.data(), length);
+ // sharedBuffer must contain data more than segmentSize (= 0x1000) to check
+ // copy().
+ ASSERT_EQ(length * 4, shared_buffer->size());
+
+ Vector<char> clone = shared_buffer->Copy();
+ ASSERT_EQ(length * 4, clone.size());
+ const Vector<char> contiguous = shared_buffer->Copy();
+ ASSERT_EQ(contiguous.size(), shared_buffer->size());
+ ASSERT_EQ(0, memcmp(clone.data(), contiguous.data(), clone.size()));
+
+ clone.Append(test_data.data(), length);
+ ASSERT_EQ(length * 5, clone.size());
+}
+
+TEST(SharedBufferTest, constructorWithSizeOnly) {
+ size_t length = 10000;
+ scoped_refptr<SharedBuffer> shared_buffer = SharedBuffer::Create(length);
+ ASSERT_EQ(length, shared_buffer->size());
+
+ // The internal flat buffer should have been resized to |length| therefore
+ // getSomeData() should directly return the full size.
+ const char* data;
+ ASSERT_EQ(length, shared_buffer->GetSomeData(data, static_cast<size_t>(0u)));
+}
+
+TEST(SharedBufferTest, FlatData) {
+ auto check_flat_data = [](scoped_refptr<const SharedBuffer> shared_buffer) {
+ const SharedBuffer::DeprecatedFlatData flat_buffer(shared_buffer);
+
+ EXPECT_EQ(shared_buffer->size(), flat_buffer.size());
+ shared_buffer->ForEachSegment([&flat_buffer](
+ const char* segment, size_t segment_size,
+ size_t segment_offset) -> bool {
+ EXPECT_EQ(
+ memcmp(segment, flat_buffer.Data() + segment_offset, segment_size),
+ 0);
+
+ // If the SharedBuffer is not segmented, FlatData doesn't copy any data.
+ EXPECT_EQ(segment_size == flat_buffer.size(),
+ segment == flat_buffer.Data());
+ return true;
+ });
+ };
+
+ scoped_refptr<SharedBuffer> shared_buffer = SharedBuffer::Create();
+
+ // Add enough data to hit a couple of segments.
+ while (shared_buffer->size() < 10000) {
+ check_flat_data(shared_buffer);
+ shared_buffer->Append("FooBarBaz", 9u);
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/speech/platform_speech_synthesis_utterance.cc b/chromium/third_party/blink/renderer/platform/speech/platform_speech_synthesis_utterance.cc
new file mode 100644
index 00000000000..1c69a40f565
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/speech/platform_speech_synthesis_utterance.cc
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2013 Apple Computer, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/speech/platform_speech_synthesis_utterance.h"
+
+namespace blink {
+
+PlatformSpeechSynthesisUtterance* PlatformSpeechSynthesisUtterance::Create(
+ PlatformSpeechSynthesisUtteranceClient* client) {
+ return new PlatformSpeechSynthesisUtterance(client);
+}
+
+PlatformSpeechSynthesisUtterance::PlatformSpeechSynthesisUtterance(
+ PlatformSpeechSynthesisUtteranceClient* client)
+ : client_(client), volume_(1.0f), rate_(1.0f), pitch_(1.0f) {}
+
+void PlatformSpeechSynthesisUtterance::Trace(blink::Visitor* visitor) {
+ visitor->Trace(client_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/speech/platform_speech_synthesis_utterance.h b/chromium/third_party/blink/renderer/platform/speech/platform_speech_synthesis_utterance.h
new file mode 100644
index 00000000000..229d5e0b33d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/speech/platform_speech_synthesis_utterance.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2013 Apple Computer, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_SPEECH_PLATFORM_SPEECH_SYNTHESIS_UTTERANCE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SPEECH_PLATFORM_SPEECH_SYNTHESIS_UTTERANCE_H_
+
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/speech/platform_speech_synthesis_voice.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class PlatformSpeechSynthesisUtteranceClient : public GarbageCollectedMixin {
+ public:
+ // Implement methods as needed.
+ protected:
+ virtual ~PlatformSpeechSynthesisUtteranceClient() = default;
+};
+
+class PLATFORM_EXPORT PlatformSpeechSynthesisUtterance final
+ : public GarbageCollectedFinalized<PlatformSpeechSynthesisUtterance> {
+ public:
+ static PlatformSpeechSynthesisUtterance* Create(
+ PlatformSpeechSynthesisUtteranceClient*);
+
+ const String& GetText() const { return text_; }
+ void SetText(const String& text) { text_ = text; }
+
+ const String& Lang() const { return lang_; }
+ void SetLang(const String& lang) { lang_ = lang; }
+
+ PlatformSpeechSynthesisVoice* Voice() const { return voice_.get(); }
+ void SetVoice(PlatformSpeechSynthesisVoice* voice) { voice_ = voice; }
+
+ // Range = [0, 1] where 1 is the default.
+ float Volume() const { return volume_; }
+ void SetVolume(float volume) { volume_ = clampTo(volume, 0.0f, 1.0f); }
+
+ // Range = [0.1, 10] where 1 is the default.
+ float Rate() const { return rate_; }
+ void SetRate(float rate) { rate_ = clampTo(rate, 0.1f, 10.0f); }
+
+ // Range = [0, 2] where 1 is the default.
+ float Pitch() const { return pitch_; }
+ void SetPitch(float pitch) { pitch_ = clampTo(pitch, 0.0f, 2.0f); }
+
+ double StartTime() const { return start_time_; }
+ void SetStartTime(double start_time) { start_time_ = start_time; }
+
+ PlatformSpeechSynthesisUtteranceClient* Client() const { return client_; }
+
+ void Trace(blink::Visitor*);
+
+ private:
+ explicit PlatformSpeechSynthesisUtterance(
+ PlatformSpeechSynthesisUtteranceClient*);
+
+ Member<PlatformSpeechSynthesisUtteranceClient> client_;
+ String text_;
+ String lang_;
+ scoped_refptr<PlatformSpeechSynthesisVoice> voice_;
+ float volume_;
+ float rate_;
+ float pitch_;
+ double start_time_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SPEECH_PLATFORM_SPEECH_SYNTHESIS_UTTERANCE_H_
diff --git a/chromium/third_party/blink/renderer/platform/speech/platform_speech_synthesis_voice.cc b/chromium/third_party/blink/renderer/platform/speech/platform_speech_synthesis_voice.cc
new file mode 100644
index 00000000000..533602a9a83
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/speech/platform_speech_synthesis_voice.cc
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2013 Apple Computer, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/speech/platform_speech_synthesis_voice.h"
+
+namespace blink {
+
+scoped_refptr<PlatformSpeechSynthesisVoice>
+PlatformSpeechSynthesisVoice::Create(const String& voice_uri,
+ const String& name,
+ const String& lang,
+ bool local_service,
+ bool is_default) {
+ return base::AdoptRef(new PlatformSpeechSynthesisVoice(
+ voice_uri, name, lang, local_service, is_default));
+}
+
+scoped_refptr<PlatformSpeechSynthesisVoice>
+PlatformSpeechSynthesisVoice::Create() {
+ return base::AdoptRef(new PlatformSpeechSynthesisVoice);
+}
+
+PlatformSpeechSynthesisVoice::PlatformSpeechSynthesisVoice(
+ const String& voice_uri,
+ const String& name,
+ const String& lang,
+ bool local_service,
+ bool is_default)
+ : voice_uri_(voice_uri),
+ name_(name),
+ lang_(lang),
+ local_service_(local_service),
+ default_(is_default) {}
+
+PlatformSpeechSynthesisVoice::PlatformSpeechSynthesisVoice()
+ : local_service_(false), default_(false) {}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/speech/platform_speech_synthesis_voice.h b/chromium/third_party/blink/renderer/platform/speech/platform_speech_synthesis_voice.h
new file mode 100644
index 00000000000..54e2b053daf
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/speech/platform_speech_synthesis_voice.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2013 Apple Computer, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_SPEECH_PLATFORM_SPEECH_SYNTHESIS_VOICE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SPEECH_PLATFORM_SPEECH_SYNTHESIS_VOICE_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT PlatformSpeechSynthesisVoice final
+ : public RefCounted<PlatformSpeechSynthesisVoice> {
+ public:
+ static scoped_refptr<PlatformSpeechSynthesisVoice> Create(
+ const String& voice_uri,
+ const String& name,
+ const String& lang,
+ bool local_service,
+ bool is_default);
+ static scoped_refptr<PlatformSpeechSynthesisVoice> Create();
+
+ const String& VoiceURI() const { return voice_uri_; }
+ void SetVoiceURI(const String& voice_uri) { voice_uri_ = voice_uri; }
+
+ const String& GetName() const { return name_; }
+ void SetName(const String& name) { name_ = name; }
+
+ const String& Lang() const { return lang_; }
+ void SetLang(const String& lang) { lang_ = lang; }
+
+ bool LocalService() const { return local_service_; }
+ void SetLocalService(bool local_service) { local_service_ = local_service; }
+
+ bool IsDefault() const { return default_; }
+ void SetIsDefault(bool is_default) { default_ = is_default; }
+
+ private:
+ PlatformSpeechSynthesisVoice(const String& voice_uri,
+ const String& name,
+ const String& lang,
+ bool local_service,
+ bool is_default);
+ PlatformSpeechSynthesisVoice();
+
+ String voice_uri_;
+ String name_;
+ String lang_;
+ bool local_service_;
+ bool default_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SPEECH_PLATFORM_SPEECH_SYNTHESIS_VOICE_H_
diff --git a/chromium/third_party/blink/renderer/platform/speech/platform_speech_synthesizer.cc b/chromium/third_party/blink/renderer/platform/speech/platform_speech_synthesizer.cc
new file mode 100644
index 00000000000..f56c547e843
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/speech/platform_speech_synthesizer.cc
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2013 Apple Computer, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/speech/platform_speech_synthesizer.h"
+
+#include "build/build_config.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_speech_synthesis_utterance.h"
+#include "third_party/blink/public/platform/web_speech_synthesizer.h"
+#include "third_party/blink/public/platform/web_speech_synthesizer_client.h"
+#include "third_party/blink/renderer/platform/exported/web_speech_synthesizer_client_impl.h"
+#include "third_party/blink/renderer/platform/speech/platform_speech_synthesis_utterance.h"
+
+namespace blink {
+
+PlatformSpeechSynthesizer* PlatformSpeechSynthesizer::Create(
+ PlatformSpeechSynthesizerClient* client) {
+ PlatformSpeechSynthesizer* synthesizer =
+ new PlatformSpeechSynthesizer(client);
+#if defined(OS_ANDROID)
+// On Android devices we don't fetch voices until the object
+// is touched to avoid needlessly binding to TTS service, see
+// https://crbug.com/811929.
+#else
+ synthesizer->InitializeVoiceList();
+#endif
+ return synthesizer;
+}
+
+PlatformSpeechSynthesizer::PlatformSpeechSynthesizer(
+ PlatformSpeechSynthesizerClient* client)
+ : speech_synthesizer_client_(client) {
+ web_speech_synthesizer_client_ =
+ new WebSpeechSynthesizerClientImpl(this, client);
+ web_speech_synthesizer_ = Platform::Current()->CreateSpeechSynthesizer(
+ web_speech_synthesizer_client_);
+}
+
+PlatformSpeechSynthesizer::~PlatformSpeechSynthesizer() = default;
+
+void PlatformSpeechSynthesizer::Speak(
+ PlatformSpeechSynthesisUtterance* utterance) {
+ MaybeInitializeVoiceList();
+ if (web_speech_synthesizer_ && web_speech_synthesizer_client_)
+ web_speech_synthesizer_->Speak(WebSpeechSynthesisUtterance(utterance));
+}
+
+void PlatformSpeechSynthesizer::Pause() {
+ MaybeInitializeVoiceList();
+ if (web_speech_synthesizer_)
+ web_speech_synthesizer_->Pause();
+}
+
+void PlatformSpeechSynthesizer::Resume() {
+ MaybeInitializeVoiceList();
+ if (web_speech_synthesizer_)
+ web_speech_synthesizer_->Resume();
+}
+
+void PlatformSpeechSynthesizer::Cancel() {
+ MaybeInitializeVoiceList();
+ if (web_speech_synthesizer_)
+ web_speech_synthesizer_->Cancel();
+}
+
+const Vector<scoped_refptr<PlatformSpeechSynthesisVoice>>&
+PlatformSpeechSynthesizer::GetVoiceList() {
+ MaybeInitializeVoiceList();
+ return voice_list_;
+}
+
+void PlatformSpeechSynthesizer::SetVoiceList(
+ Vector<scoped_refptr<PlatformSpeechSynthesisVoice>>& voices) {
+ voice_list_ = voices;
+}
+
+void PlatformSpeechSynthesizer::MaybeInitializeVoiceList() {
+ if (!voices_initialized_)
+ InitializeVoiceList();
+}
+
+void PlatformSpeechSynthesizer::InitializeVoiceList() {
+ if (web_speech_synthesizer_) {
+ voices_initialized_ = true;
+ web_speech_synthesizer_->UpdateVoiceList();
+ }
+}
+
+void PlatformSpeechSynthesizer::Trace(blink::Visitor* visitor) {
+ visitor->Trace(speech_synthesizer_client_);
+ visitor->Trace(web_speech_synthesizer_client_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/speech/platform_speech_synthesizer.h b/chromium/third_party/blink/renderer/platform/speech/platform_speech_synthesizer.h
new file mode 100644
index 00000000000..355aa486d35
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/speech/platform_speech_synthesizer.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2013 Apple Computer, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_SPEECH_PLATFORM_SPEECH_SYNTHESIZER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SPEECH_PLATFORM_SPEECH_SYNTHESIZER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/speech/platform_speech_synthesis_voice.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+enum SpeechBoundary { kSpeechWordBoundary, kSpeechSentenceBoundary };
+
+class PlatformSpeechSynthesisUtterance;
+class WebSpeechSynthesizer;
+class WebSpeechSynthesizerClientImpl;
+
+class PLATFORM_EXPORT PlatformSpeechSynthesizerClient
+ : public GarbageCollectedMixin {
+ public:
+ virtual void DidStartSpeaking(PlatformSpeechSynthesisUtterance*) = 0;
+ virtual void DidFinishSpeaking(PlatformSpeechSynthesisUtterance*) = 0;
+ virtual void DidPauseSpeaking(PlatformSpeechSynthesisUtterance*) = 0;
+ virtual void DidResumeSpeaking(PlatformSpeechSynthesisUtterance*) = 0;
+ virtual void SpeakingErrorOccurred(PlatformSpeechSynthesisUtterance*) = 0;
+ virtual void BoundaryEventOccurred(PlatformSpeechSynthesisUtterance*,
+ SpeechBoundary,
+ unsigned char_index) = 0;
+ virtual void VoicesDidChange() = 0;
+
+ protected:
+ virtual ~PlatformSpeechSynthesizerClient() = default;
+};
+
+class PLATFORM_EXPORT PlatformSpeechSynthesizer
+ : public GarbageCollectedFinalized<PlatformSpeechSynthesizer> {
+ public:
+ static PlatformSpeechSynthesizer* Create(PlatformSpeechSynthesizerClient*);
+
+ virtual ~PlatformSpeechSynthesizer();
+
+ virtual void Speak(PlatformSpeechSynthesisUtterance*);
+ virtual void Pause();
+ virtual void Resume();
+ virtual void Cancel();
+
+ PlatformSpeechSynthesizerClient* Client() const {
+ return speech_synthesizer_client_;
+ }
+
+ const Vector<scoped_refptr<PlatformSpeechSynthesisVoice>>& GetVoiceList();
+
+ void SetVoiceList(Vector<scoped_refptr<PlatformSpeechSynthesisVoice>>&);
+
+ // Eager finalization is required to promptly release the owned
+ // WebSpeechSynthesizer.
+ //
+ // If not and delayed until lazily swept, m_webSpeechSynthesizerClient may end
+ // up being lazily swept first (i.e., before this PlatformSpeechSynthesizer),
+ // leaving m_webSpeechSynthesizer with a dangling pointer to a finalized
+ // object -- WebSpeechSynthesizer embedder implementations calling
+ // notification methods in the other directions by way of
+ // m_webSpeechSynthesizerClient. Eagerly releasing WebSpeechSynthesizer
+ // prevents such unsafe accesses.
+ EAGERLY_FINALIZE();
+ virtual void Trace(blink::Visitor*);
+
+ protected:
+ explicit PlatformSpeechSynthesizer(PlatformSpeechSynthesizerClient*);
+
+ virtual void InitializeVoiceList();
+
+ Vector<scoped_refptr<PlatformSpeechSynthesisVoice>> voice_list_;
+ bool voices_initialized_ = false;
+
+ private:
+ void MaybeInitializeVoiceList();
+
+ Member<PlatformSpeechSynthesizerClient> speech_synthesizer_client_;
+
+ std::unique_ptr<WebSpeechSynthesizer> web_speech_synthesizer_;
+ Member<WebSpeechSynthesizerClientImpl> web_speech_synthesizer_client_;
+
+ DISALLOW_COPY_AND_ASSIGN(PlatformSpeechSynthesizer);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SPEECH_PLATFORM_SPEECH_SYNTHESIZER_H_
diff --git a/chromium/third_party/blink/renderer/platform/supplementable.cc b/chromium/third_party/blink/renderer/platform/supplementable.cc
new file mode 100644
index 00000000000..fc2eaebd975
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/supplementable.cc
@@ -0,0 +1,13 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/supplementable.h"
+
+// This Supplementable.cpp, which includes only
+// Supplementable.h, should be in Source/platform,
+// because Supplementable is not compiled without this cpp.
+// So if we don't have this cpp, we will see unresolved symbol error
+// when constructor/destructor's address is required.
+// i.e. error LNK2005: "public: virtual __cdecl
+// blink::SupplementTracing<0>::~SupplementTracing<0>(void)"
diff --git a/chromium/third_party/blink/renderer/platform/supplementable.h b/chromium/third_party/blink/renderer/platform/supplementable.h
new file mode 100644
index 00000000000..be20afad9be
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/supplementable.h
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2012 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_SUPPLEMENTABLE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SUPPLEMENTABLE_H_
+
+#include "third_party/blink/renderer/platform/bindings/trace_wrapper_member.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+#if DCHECK_IS_ON()
+#include "third_party/blink/renderer/platform/wtf/threading.h"
+#endif
+
+namespace blink {
+
+// What you should know about Supplementable and Supplement
+// ========================================================
+// Supplementable and Supplement instances are meant to be thread local. They
+// should only be accessed from within the thread that created them. The
+// 2 classes are not designed for safe access from another thread. Violating
+// this design assumption can result in memory corruption and unpredictable
+// behavior.
+//
+// What you should know about the Supplement keys
+// ==============================================
+// The Supplement is expected to use the same const char* string instance
+// as its key. The Supplementable's SupplementMap will use the address of the
+// string as the key and not the characters themselves. Hence, 2 strings with
+// the same characters will be treated as 2 different keys.
+//
+// In practice, this is mostly hidden. Each Supplement must expose a static
+// const char array which provides a human-readable key. Access to supplements
+// requires passing the Supplement type, so these cannot collide for unequal
+// types.
+//
+// Use extreme caution when deriving a supplementable class, as misuse can cause
+// type confusion.
+//
+// Typical use is expected to look like this:
+//
+// class NavigatorFoo : public Supplement<Navigator> {
+// public:
+// static const char kSupplementName[];
+//
+// NavigatorFoo& From(Navigator&);
+// }
+//
+// // static
+// const char NavigatorFoo::kSupplementName[] = "NavigatorFoo";
+//
+// NavigatorFoo& NavigatorFoo::From(Navigator& navigator)
+// {
+// NavigatorFoo* supplement =
+// Supplement<Navigator>::From<NavigatorFoo>(navigator);
+// if (!supplement) {
+// supplement = new NavigatorFoo(navigator);
+// ProvideTo(navigator, supplement);
+// }
+// return *supplement;
+// }
+//
+// The hash map key will automatically be determined from the supplement type
+// used.
+//
+// What you should know about thread checks
+// ========================================
+// When assertion is enabled this class performs thread-safety check so that
+// supplements are provided to and from the same thread.
+// If you want to provide some value for Workers, this thread check may be too
+// strict, since in you'll be providing the value while worker preparation is
+// being done on the main thread, even before the worker thread has started.
+// If that's the case you can explicitly call reattachThread() when the
+// Supplementable object is passed to the final destination thread (i.e.
+// worker thread). This will allow supplements to be accessed on that thread.
+// Please be extremely careful to use the method though, as randomly calling
+// the method could easily cause racy condition.
+//
+// Note that reattachThread() does nothing if assertion is not enabled.
+
+template <typename T>
+class Supplementable;
+
+// Supplement<T>-specific version of TraceWrapperBase class. In order to support
+// wrapper-tracing from Supplementable<T> to Supplement<T> (especially when
+// crossing core/modules boundary), Supplement<T> needs to be wrapper-traceable.
+// This class provides a common API for all subclasses of Supplement<T> to
+// support wrapper-tracing.
+class PLATFORM_EXPORT TraceWrapperBaseForSupplement {
+ public:
+ virtual void TraceWrappers(const ScriptWrappableVisitor* visitor) const {}
+};
+
+template <typename T>
+class Supplement : public GarbageCollectedMixin,
+ public TraceWrapperBaseForSupplement {
+ public:
+ // TODO(haraken): Remove the default constructor.
+ // All Supplement objects should be instantiated with |supplementable_|.
+ Supplement() {}
+
+ explicit Supplement(T& supplementable) : supplementable_(&supplementable) {}
+
+ // Supplementable and its supplements live and die together.
+ // Thus supplementable() should never return null (if the default constructor
+ // is completely removed).
+ T* GetSupplementable() const { return supplementable_; }
+
+ template <typename SupplementType>
+ static void ProvideTo(Supplementable<T>& supplementable,
+ SupplementType* supplement) {
+ supplementable.ProvideSupplement(supplement);
+ }
+
+ template <typename SupplementType>
+ static SupplementType* From(const Supplementable<T>& supplementable) {
+ return supplementable.template RequireSupplement<SupplementType>();
+ }
+
+ template <typename SupplementType>
+ static SupplementType* From(const Supplementable<T>* supplementable) {
+ return supplementable
+ ? supplementable->template RequireSupplement<SupplementType>()
+ : nullptr;
+ }
+
+ virtual void Trace(blink::Visitor* visitor) {
+ visitor->Trace(supplementable_);
+ }
+
+ private:
+ Member<T> supplementable_;
+};
+
+template <typename T>
+class Supplementable : public GarbageCollectedMixin {
+ WTF_MAKE_NONCOPYABLE(Supplementable);
+
+ public:
+ template <typename SupplementType>
+ void ProvideSupplement(SupplementType* supplement) {
+#if DCHECK_IS_ON()
+ DCHECK_EQ(creation_thread_id_, CurrentThread());
+#endif
+ static_assert(
+ std::is_array<decltype(SupplementType::kSupplementName)>::value,
+ "Declare a const char array kSupplementName. See Supplementable.h for "
+ "details.");
+ this->supplements_.Set(SupplementType::kSupplementName, supplement);
+ }
+
+ template <typename SupplementType>
+ void RemoveSupplement() {
+#if DCHECK_IS_ON()
+ DCHECK_EQ(creation_thread_id_, CurrentThread());
+#endif
+ static_assert(
+ std::is_array<decltype(SupplementType::kSupplementName)>::value,
+ "Declare a const char array kSupplementName. See Supplementable.h for "
+ "details.");
+ this->supplements_.erase(SupplementType::kSupplementName);
+ }
+
+ template <typename SupplementType>
+ SupplementType* RequireSupplement() const {
+#if DCHECK_IS_ON()
+ DCHECK_EQ(attached_thread_id_, CurrentThread());
+#endif
+ static_assert(
+ std::is_array<decltype(SupplementType::kSupplementName)>::value,
+ "Declare a const char array kSupplementName. See Supplementable.h for "
+ "details.");
+ return static_cast<SupplementType*>(
+ this->supplements_.at(SupplementType::kSupplementName));
+ }
+
+ void ReattachThread() {
+#if DCHECK_IS_ON()
+ attached_thread_id_ = CurrentThread();
+#endif
+ }
+
+ virtual void Trace(blink::Visitor* visitor) { visitor->Trace(supplements_); }
+ virtual void TraceWrappers(const ScriptWrappableVisitor* visitor) const {
+ for (const auto& supplement : supplements_.Values())
+ visitor->TraceWrappers(supplement);
+ }
+
+ protected:
+ using SupplementMap = HeapHashMap<const char*,
+ TraceWrapperMember<Supplement<T>>,
+ PtrHash<const char>>;
+ SupplementMap supplements_;
+
+ Supplementable()
+#if DCHECK_IS_ON()
+ : attached_thread_id_(CurrentThread()),
+ creation_thread_id_(CurrentThread())
+#endif
+ {
+ }
+
+#if DCHECK_IS_ON()
+ private:
+ ThreadIdentifier attached_thread_id_;
+ ThreadIdentifier creation_thread_id_;
+#endif
+};
+
+template <typename T>
+struct ThreadingTrait<Supplement<T>> {
+ static const ThreadAffinity kAffinity = ThreadingTrait<T>::kAffinity;
+};
+
+template <typename T>
+struct ThreadingTrait<Supplementable<T>> {
+ static const ThreadAffinity kAffinity = ThreadingTrait<T>::Affinity;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SUPPLEMENTABLE_H_
diff --git a/chromium/third_party/blink/renderer/platform/testing/DEPS b/chromium/third_party/blink/renderer/platform/testing/DEPS
new file mode 100644
index 00000000000..a9fe93b7c6e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/DEPS
@@ -0,0 +1,23 @@
+include_rules = [
+ # To whitelist base/ stuff Blink is allowed to include, we list up all
+ # directories and files instead of writing 'base/'.
+ "+base/at_exit.h",
+ "+base/command_line.h",
+ "+base/i18n/icu_util.h",
+ "+base/path_service.h",
+ "+base/process",
+ "+base/run_loop.h",
+ "+base/message_loop",
+ "+base/metrics/statistics_recorder.h",
+ "+base/test/test_io_thread.h",
+ "+cc",
+ "+components/viz/test",
+ "+mojo/edk/embedder",
+ '+testing',
+]
+
+specific_include_rules = {
+ 'blink_fuzzer_test_support\.cc': [
+ "+content/public/test/blink_test_environment.h",
+ ],
+}
diff --git a/chromium/third_party/blink/renderer/platform/testing/arena_test_helpers.h b/chromium/third_party/blink/renderer/platform/testing/arena_test_helpers.h
new file mode 100644
index 00000000000..4d2cb5e3c25
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/arena_test_helpers.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_ARENA_TEST_HELPERS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_ARENA_TEST_HELPERS_H_
+
+#include "third_party/blink/renderer/platform/pod_arena.h"
+#include "third_party/blink/renderer/platform/wtf/not_found.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+#include <gtest/gtest.h>
+
+namespace blink {
+namespace ArenaTestHelpers {
+
+// An allocator for the PODArena which tracks the regions which have
+// been allocated.
+class TrackedAllocator final : public PODArena::FastMallocAllocator {
+ public:
+ static scoped_refptr<TrackedAllocator> Create() {
+ return base::AdoptRef(new TrackedAllocator);
+ }
+
+ void* Allocate(size_t size) override {
+ void* result = PODArena::FastMallocAllocator::Allocate(size);
+ allocated_regions_.push_back(result);
+ return result;
+ }
+
+ void Free(void* ptr) override {
+ size_t slot = allocated_regions_.Find(ptr);
+ ASSERT_NE(slot, kNotFound);
+ allocated_regions_.EraseAt(slot);
+ PODArena::FastMallocAllocator::Free(ptr);
+ }
+
+ bool IsEmpty() const { return !NumRegions(); }
+
+ int NumRegions() const { return allocated_regions_.size(); }
+
+ private:
+ TrackedAllocator() = default;
+ Vector<void*> allocated_regions_;
+};
+
+} // namespace ArenaTestHelpers
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_ARENA_TEST_HELPERS_H_
diff --git a/chromium/third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.cc b/chromium/third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.cc
new file mode 100644
index 00000000000..219a6870079
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.cc
@@ -0,0 +1,35 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.h"
+
+#include "base/at_exit.h"
+#include "base/command_line.h"
+#include "base/i18n/icu_util.h"
+#include "content/public/test/blink_test_environment.h"
+#include "third_party/blink/renderer/platform/weborigin/scheme_registry.h"
+
+namespace blink {
+
+BlinkFuzzerTestSupport::BlinkFuzzerTestSupport()
+ : BlinkFuzzerTestSupport(0, nullptr) {}
+
+BlinkFuzzerTestSupport::BlinkFuzzerTestSupport(int argc, char** argv) {
+ // Note: we don't tear anything down here after an iteration of the fuzzer
+ // is complete, this is for efficiency. We rerun the fuzzer with the same
+ // environment as the previous iteration.
+ base::AtExitManager at_exit;
+
+ CHECK(base::i18n::InitializeICU());
+
+ base::CommandLine::Init(argc, argv);
+
+ content::SetUpBlinkTestEnvironment();
+
+ blink::SchemeRegistry::Initialize();
+}
+
+BlinkFuzzerTestSupport::~BlinkFuzzerTestSupport() = default;
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.h b/chromium/third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.h
new file mode 100644
index 00000000000..5ba8d5f7ba4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.h
@@ -0,0 +1,24 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_BLINK_FUZZER_TEST_SUPPORT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_BLINK_FUZZER_TEST_SUPPORT_H_
+
+namespace blink {
+
+// Instantiating BlinkFuzzerTestSupport will spin up an environment similar to
+// webkit_unit_tests. It should be statically initialized and leaked in fuzzers.
+class BlinkFuzzerTestSupport {
+ public:
+ // Use this constructor in LLVMFuzzerTestOneInput.
+ BlinkFuzzerTestSupport();
+
+ // Use this constructor in LLVMFuzzerInitialize only if argv is necessary.
+ BlinkFuzzerTestSupport(int argc, char** argv);
+ ~BlinkFuzzerTestSupport();
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_BLINK_FUZZER_TEST_SUPPORT_H_
diff --git a/chromium/third_party/blink/renderer/platform/testing/blink_perf_test_suite.cc b/chromium/third_party/blink/renderer/platform/testing/blink_perf_test_suite.cc
new file mode 100644
index 00000000000..270b7ef0eab
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/blink_perf_test_suite.cc
@@ -0,0 +1,55 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/testing/blink_perf_test_suite.h"
+
+#include "base/command_line.h"
+#include "base/debug/debugger.h"
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+#include "base/process/launch.h"
+#include "base/strings/string_util.h"
+#include "base/test/perf_log.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
+#include "third_party/blink/renderer/platform/wtf/wtf.h"
+
+namespace blink {
+
+BlinkPerfTestSuite::BlinkPerfTestSuite(int argc, char** argv)
+ : base::TestSuite(argc, argv) {}
+
+void BlinkPerfTestSuite::Initialize() {
+ TestSuite::Initialize();
+ WTF::Partitions::Initialize(nullptr);
+ WTF::Initialize(nullptr);
+
+ // Initialize the perf timer log
+ base::FilePath log_path =
+ base::CommandLine::ForCurrentProcess()->GetSwitchValuePath("log-file");
+ if (log_path.empty()) {
+ base::PathService::Get(base::FILE_EXE, &log_path);
+#if defined(OS_ANDROID)
+ base::FilePath tmp_dir;
+ base::PathService::Get(base::DIR_CACHE, &tmp_dir);
+ log_path = tmp_dir.Append(log_path.BaseName());
+#endif
+ log_path = log_path.ReplaceExtension(FILE_PATH_LITERAL("log"));
+ log_path = log_path.InsertBeforeExtension(FILE_PATH_LITERAL("_perf"));
+ }
+ ASSERT_TRUE(base::InitPerfLog(log_path));
+
+ // Raise to high priority to have more precise measurements. Since we don't
+ // aim at 1% precision, it is not necessary to run at realtime level.
+ if (!base::debug::BeingDebugged())
+ base::RaiseProcessToHighPriority();
+}
+
+void BlinkPerfTestSuite::Shutdown() {
+ TestSuite::Shutdown();
+ base::FinalizePerfLog();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/testing/blink_perf_test_suite.h b/chromium/third_party/blink/renderer/platform/testing/blink_perf_test_suite.h
new file mode 100644
index 00000000000..76e5dccd960
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/blink_perf_test_suite.h
@@ -0,0 +1,22 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_BLINK_PERF_TEST_SUITE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_BLINK_PERF_TEST_SUITE_H_
+
+#include "base/test/test_suite.h"
+
+namespace blink {
+
+class BlinkPerfTestSuite : public base::TestSuite {
+ public:
+ BlinkPerfTestSuite(int argc, char** argv);
+
+ void Initialize() override;
+ void Shutdown() override;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_BLINK_PERF_TEST_SUITE_H_
diff --git a/chromium/third_party/blink/renderer/platform/testing/compositor_test.cc b/chromium/third_party/blink/renderer/platform/testing/compositor_test.cc
new file mode 100644
index 00000000000..4cfb88c445c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/compositor_test.cc
@@ -0,0 +1,14 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/testing/compositor_test.h"
+
+namespace blink {
+
+CompositorTest::CompositorTest()
+ : runner_(new base::TestMockTimeTaskRunner), runner_handle_(runner_) {}
+
+CompositorTest::~CompositorTest() = default;
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/testing/compositor_test.h b/chromium/third_party/blink/renderer/platform/testing/compositor_test.h
new file mode 100644
index 00000000000..746d2ca5a25
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/compositor_test.h
@@ -0,0 +1,32 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_COMPOSITOR_TEST_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_COMPOSITOR_TEST_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "base/test/test_mock_time_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class CompositorTest : public testing::Test {
+ WTF_MAKE_NONCOPYABLE(CompositorTest);
+
+ public:
+ CompositorTest();
+ virtual ~CompositorTest();
+
+ protected:
+ // Mock task runner is initialized here because tests create
+ // WebLayerTreeViewImplForTesting which needs the current task runner handle.
+ scoped_refptr<base::TestMockTimeTaskRunner> runner_;
+ base::ThreadTaskRunnerHandle runner_handle_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_COMPOSITOR_TEST_H_
diff --git a/chromium/third_party/blink/renderer/platform/testing/data/Ahem.woff b/chromium/third_party/blink/renderer/platform/testing/data/Ahem.woff
new file mode 100644
index 00000000000..0bc80935288
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/data/Ahem.woff
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/testing/data/AhemSpaceLigature.woff b/chromium/third_party/blink/renderer/platform/testing/data/AhemSpaceLigature.woff
new file mode 100644
index 00000000000..2f576b9a912
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/data/AhemSpaceLigature.woff
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/testing/data/third_party/Amiri/README b/chromium/third_party/blink/renderer/platform/testing/data/third_party/Amiri/README
new file mode 100644
index 00000000000..a7071c15789
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/data/third_party/Amiri/README
@@ -0,0 +1,90 @@
+Amiri is a classical Arabic typeface in Naskh style for typesetting books and other running text. Its design is a revival of the beautiful typeface pioneered in early 20th century by Bulaq Press in Cairo, also known as Amiria Press, after which the font is named.
+Read more about the project at www.amirifont.org
+Updated in July 2017 to v0.109
+
+SIL OPEN FONT LICENSE
+
+Version 1.1 - 26 February 2007
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting — in part or in whole — any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE. \ No newline at end of file
diff --git a/chromium/third_party/blink/renderer/platform/testing/data/third_party/Amiri/amiri_arabic.woff2 b/chromium/third_party/blink/renderer/platform/testing/data/third_party/Amiri/amiri_arabic.woff2
new file mode 100644
index 00000000000..da20321dadf
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/data/third_party/Amiri/amiri_arabic.woff2
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/testing/data/third_party/MEgalopolis/MEgalopolisExtra.woff b/chromium/third_party/blink/renderer/platform/testing/data/third_party/MEgalopolis/MEgalopolisExtra.woff
new file mode 100644
index 00000000000..ee37efffcd1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/data/third_party/MEgalopolis/MEgalopolisExtra.woff
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/testing/data/third_party/MEgalopolis/README b/chromium/third_party/blink/renderer/platform/testing/data/third_party/MEgalopolis/README
new file mode 100644
index 00000000000..8172bbd118f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/data/third_party/MEgalopolis/README
@@ -0,0 +1,37 @@
+MEgalopolis Extra Font
+from http://www.smeltery.net/fonts/megalopolis-extra
+© Jack Usine / SMeltery · All rights reserved.
+
+Font file included as a real-world example for testing fonts with extreme ligatures
+which combine two characters into the dimensions of one glyph,
+for example: fast/text/font-ligatures-linebreak*.html
+
+Permission was granted by Jack Usine to use this font in a test case and include it in the repository.
+
+License Information:
+
+http://www.smeltery.net/SMFFEULA.html (retrieved 01-15-2014):
+
+"SMELTERY FREE FONT END USER LICENSE AGREEMENT!
+SMFF EULA version 1.2, november 2008
+
+By downloading and/or installing a SMeltery Free Font
+you agree to this license.
+
+This font is freeware. You can use it freely for all your personal and commercial work.
+
+The font files may not be modified without written permission from Jack Usine / SMeltery.
+
+This font may not be sold.
+
+This font may not be redistributed, shared, repackaged or included in any online or offline archive, font collection, web site or CD-ROM without written permission from Jack Usine / SMeltery.
+
+Embedding this font in a PDF document is allowed.
+
+Embedding this font in a web page with a @font-face declaration is allowed once you credit SMeltery with a link somewhere on your site.
+
+Except for your right to use this font, all other rights are owned and retained by Jack Usine / SMeltery.
+
+Jack Usine / SMeltery is not liable for any damage resulting from the use of this font.
+
+Thank you!"
diff --git a/chromium/third_party/blink/renderer/platform/testing/data/white-1x1.png b/chromium/third_party/blink/renderer/platform/testing/data/white-1x1.png
new file mode 100644
index 00000000000..ccf7a6ca05b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/data/white-1x1.png
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/testing/empty_web_media_player.cc b/chromium/third_party/blink/renderer/platform/testing/empty_web_media_player.cc
new file mode 100644
index 00000000000..ef1bde3af31
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/empty_web_media_player.cc
@@ -0,0 +1,32 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/testing/empty_web_media_player.h"
+
+#include "third_party/blink/public/platform/web_size.h"
+#include "third_party/blink/public/platform/web_time_range.h"
+
+namespace blink {
+
+WebTimeRanges EmptyWebMediaPlayer::Buffered() const {
+ return WebTimeRanges();
+}
+
+WebTimeRanges EmptyWebMediaPlayer::Seekable() const {
+ return WebTimeRanges();
+}
+
+WebSize EmptyWebMediaPlayer::NaturalSize() const {
+ return WebSize(0, 0);
+}
+
+WebSize EmptyWebMediaPlayer::VisibleRect() const {
+ return WebSize();
+}
+
+WebString EmptyWebMediaPlayer::GetErrorMessage() const {
+ return WebString();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/testing/empty_web_media_player.h b/chromium/third_party/blink/renderer/platform/testing/empty_web_media_player.h
new file mode 100644
index 00000000000..c1d570134c9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/empty_web_media_player.h
@@ -0,0 +1,63 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_media_player.h"
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_EMPTY_WEB_MEDIA_PLAYER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_EMPTY_WEB_MEDIA_PLAYER_H_
+
+namespace blink {
+
+// An empty WebMediaPlayer used only for tests. This class defines the methods
+// of WebMediaPlayer so that mock WebMediaPlayers don't need to redefine them if
+// they don't care their behavior.
+class EmptyWebMediaPlayer : public WebMediaPlayer {
+ public:
+ ~EmptyWebMediaPlayer() override = default;
+
+ void Load(LoadType, const WebMediaPlayerSource&, CORSMode) override {}
+ void Play() override {}
+ void Pause() override {}
+ void Seek(double seconds) override {}
+ void SetRate(double) override {}
+ void SetVolume(double) override {}
+ void EnterPictureInPicture() override {}
+ void ExitPictureInPicture() override {}
+ WebTimeRanges Buffered() const override;
+ WebTimeRanges Seekable() const override;
+ void SetSinkId(const WebString& sink_id,
+ const WebSecurityOrigin&,
+ WebSetSinkIdCallbacks*) override {}
+ bool HasVideo() const override { return false; }
+ bool HasAudio() const override { return false; }
+ WebSize NaturalSize() const override;
+ WebSize VisibleRect() const override;
+ bool Paused() const override { return false; }
+ bool Seeking() const override { return false; }
+ double Duration() const override { return 0.0; }
+ double CurrentTime() const override { return 0.0; }
+ NetworkState GetNetworkState() const override { return kNetworkStateEmpty; }
+ ReadyState GetReadyState() const override { return kReadyStateHaveNothing; }
+ WebString GetErrorMessage() const override;
+ bool DidLoadingProgress() override { return false; }
+ bool DidGetOpaqueResponseFromServiceWorker() const override { return false; }
+ bool HasSingleSecurityOrigin() const override { return true; }
+ bool DidPassCORSAccessCheck() const override { return true; }
+ double MediaTimeForTimeValue(double time_value) const override {
+ return time_value;
+ };
+ unsigned DecodedFrameCount() const override { return 0; }
+ unsigned DroppedFrameCount() const override { return 0; }
+ size_t AudioDecodedByteCount() const override { return 0; }
+ size_t VideoDecodedByteCount() const override { return 0; }
+ void Paint(WebCanvas*,
+ const WebRect&,
+ cc::PaintFlags&,
+ int already_uploaded_id,
+ VideoFrameUploadMetadata*) override {}
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_EMPTY_WEB_MEDIA_PLAYER_H_
diff --git a/chromium/third_party/blink/renderer/platform/testing/fake_display_item_client.h b/chromium/third_party/blink/renderer/platform/testing/fake_display_item_client.h
new file mode 100644
index 00000000000..e097c22aa5a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/fake_display_item_client.h
@@ -0,0 +1,49 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_FAKE_DISPLAY_ITEM_CLIENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_FAKE_DISPLAY_ITEM_CLIENT_H_
+
+#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
+#include "third_party/blink/renderer/platform/graphics/paint/display_item_client.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+// A simple DisplayItemClient implementation suitable for use in unit tests.
+class FakeDisplayItemClient : public DisplayItemClient {
+ public:
+ FakeDisplayItemClient(const String& name = "FakeDisplayItemClient",
+ const LayoutRect& visual_rect = LayoutRect())
+ : name_(name), visual_rect_(visual_rect) {}
+
+ String DebugName() const final { return name_; }
+ LayoutRect VisualRect() const override { return visual_rect_; }
+ LayoutRect PartialInvalidationRect() const override {
+ return partial_invalidation_rect_;
+ }
+ void ClearPartialInvalidationRect() const override {
+ partial_invalidation_rect_ = LayoutRect();
+ }
+
+ void SetVisualRect(const LayoutRect& r) { visual_rect_ = r; }
+ void SetPartialInvalidationRect(const LayoutRect& r) {
+ SetDisplayItemsUncached(PaintInvalidationReason::kRectangle);
+ partial_invalidation_rect_ = r;
+ }
+
+ // This simulates a paint without needing a PaintController.
+ void UpdateCacheGeneration() {
+ SetDisplayItemsCached(CacheGenerationOrInvalidationReason::Next());
+ }
+
+ private:
+ String name_;
+ LayoutRect visual_rect_;
+ mutable LayoutRect partial_invalidation_rect_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_FAKE_DISPLAY_ITEM_CLIENT_H_
diff --git a/chromium/third_party/blink/renderer/platform/testing/fake_graphics_layer.h b/chromium/third_party/blink/renderer/platform/testing/fake_graphics_layer.h
new file mode 100644
index 00000000000..d6cd8f8160c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/fake_graphics_layer.h
@@ -0,0 +1,19 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_FAKE_GRAPHICS_LAYER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_FAKE_GRAPHICS_LAYER_H_
+
+namespace blink {
+
+// A simple GraphicsLayer implementation suitable for use in unit tests.
+class FakeGraphicsLayer : public GraphicsLayer {
+ public:
+ explicit FakeGraphicsLayer(GraphicsLayerClient& client)
+ : GraphicsLayer(client) {}
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_FAKE_GRAPHICS_LAYER_H_
diff --git a/chromium/third_party/blink/renderer/platform/testing/fake_graphics_layer_client.h b/chromium/third_party/blink/renderer/platform/testing/fake_graphics_layer_client.h
new file mode 100644
index 00000000000..4d4e7c98f37
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/fake_graphics_layer_client.h
@@ -0,0 +1,55 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_FAKE_GRAPHICS_LAYER_CLIENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_FAKE_GRAPHICS_LAYER_CLIENT_H_
+
+#include "third_party/blink/renderer/platform/graphics/graphics_layer_client.h"
+
+namespace blink {
+
+// A simple GraphicsLayerClient implementation suitable for use in unit tests.
+class FakeGraphicsLayerClient : public GraphicsLayerClient {
+ public:
+ // GraphicsLayerClient implementation.
+ IntRect ComputeInterestRect(const GraphicsLayer*,
+ const IntRect&) const override {
+ return IntRect();
+ }
+ String DebugName(const GraphicsLayer*) const override { return String(); }
+ bool IsTrackingRasterInvalidations() const override {
+ return is_tracking_raster_invalidations_;
+ }
+ bool NeedsRepaint(const GraphicsLayer&) const override {
+ return needs_repaint_;
+ }
+ void PaintContents(const GraphicsLayer* layer,
+ GraphicsContext& context,
+ GraphicsLayerPaintingPhase phase,
+ const IntRect& rect) const override {
+ if (painter_)
+ painter_(layer, context, phase, rect);
+ }
+
+ void SetIsTrackingRasterInvalidations(bool is_tracking_raster_invalidations) {
+ is_tracking_raster_invalidations_ = is_tracking_raster_invalidations;
+ }
+
+ void SetNeedsRepaint(bool needs_repaint) { needs_repaint_ = needs_repaint; }
+
+ using Painter = std::function<void(const GraphicsLayer*,
+ GraphicsContext&,
+ GraphicsLayerPaintingPhase,
+ const IntRect&)>;
+ void SetPainter(const Painter& painter) { painter_ = painter; }
+
+ private:
+ Painter painter_ = nullptr;
+ bool is_tracking_raster_invalidations_ = false;
+ bool needs_repaint_ = false;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_FAKE_GRAPHICS_LAYER_CLIENT_H_
diff --git a/chromium/third_party/blink/renderer/platform/testing/font_test_helpers.cc b/chromium/third_party/blink/renderer/platform/testing/font_test_helpers.cc
new file mode 100644
index 00000000000..fa18103a622
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/font_test_helpers.cc
@@ -0,0 +1,99 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/testing/font_test_helpers.h"
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/fonts/font.h"
+#include "third_party/blink/renderer/platform/fonts/font_custom_platform_data.h"
+#include "third_party/blink/renderer/platform/fonts/font_selector.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
+
+namespace blink {
+namespace test {
+
+namespace {
+
+class TestFontSelector : public FontSelector {
+ public:
+ static TestFontSelector* Create(const String& path) {
+ scoped_refptr<SharedBuffer> font_buffer = test::ReadFromFile(path);
+ String ots_parse_message;
+ return new TestFontSelector(
+ FontCustomPlatformData::Create(font_buffer.get(), ots_parse_message));
+ }
+
+ ~TestFontSelector() override = default;
+
+ scoped_refptr<FontData> GetFontData(
+ const FontDescription& font_description,
+ const AtomicString& family_name) override {
+ FontSelectionCapabilities normal_capabilities(
+ {NormalWidthValue(), NormalWidthValue()},
+ {NormalSlopeValue(), NormalSlopeValue()},
+ {NormalWeightValue(), NormalWeightValue()});
+ FontPlatformData platform_data = custom_platform_data_->GetFontPlatformData(
+ font_description.EffectiveFontSize(),
+ font_description.IsSyntheticBold(),
+ font_description.IsSyntheticItalic(),
+ font_description.GetFontSelectionRequest(), normal_capabilities,
+ font_description.Orientation());
+ return SimpleFontData::Create(platform_data, CustomFontData::Create());
+ }
+
+ void WillUseFontData(const FontDescription&,
+ const AtomicString& family_name,
+ const String& text) override {}
+ void WillUseRange(const FontDescription&,
+ const AtomicString& family_name,
+ const FontDataForRangeSet&) override {}
+
+ unsigned Version() const override { return 0; }
+ void FontCacheInvalidated() override {}
+ void ReportNotDefGlyph() const override {}
+ ExecutionContext* GetExecutionContext() const override { return nullptr; }
+ FontFaceCache* GetFontFaceCache() override { return nullptr; }
+
+ void RegisterForInvalidationCallbacks(FontSelectorClient*) override {}
+ void UnregisterForInvalidationCallbacks(FontSelectorClient*) override {}
+
+ bool IsPlatformFamilyMatchAvailable(
+ const FontDescription&,
+ const AtomicString& passed_family) override {
+ return false;
+ }
+
+ private:
+ TestFontSelector(scoped_refptr<FontCustomPlatformData> custom_platform_data)
+ : custom_platform_data_(std::move(custom_platform_data)) {
+ DCHECK(custom_platform_data_);
+ }
+
+ scoped_refptr<FontCustomPlatformData> custom_platform_data_;
+};
+
+} // namespace
+
+Font CreateTestFont(const AtomicString& family_name,
+ const String& font_path,
+ float size,
+ const FontDescription::VariantLigatures* ligatures) {
+ FontFamily family;
+ family.SetFamily(family_name);
+
+ FontDescription font_description;
+ font_description.SetFamily(family);
+ font_description.SetSpecifiedSize(size);
+ font_description.SetComputedSize(size);
+ if (ligatures)
+ font_description.SetVariantLigatures(*ligatures);
+
+ Font font(font_description);
+ font.Update(TestFontSelector::Create(font_path));
+ return font;
+}
+
+} // namespace test
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/testing/font_test_helpers.h b/chromium/third_party/blink/renderer/platform/testing/font_test_helpers.h
new file mode 100644
index 00000000000..fc68d0fce72
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/font_test_helpers.h
@@ -0,0 +1,26 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_FONT_TEST_HELPERS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_FONT_TEST_HELPERS_H_
+
+#include "third_party/blink/renderer/platform/fonts/font_description.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+class Font;
+
+namespace test {
+
+// Reads a font from a specified path, for use in unit tests only.
+Font CreateTestFont(const AtomicString& family_name,
+ const String& font_path,
+ float size,
+ const FontDescription::VariantLigatures* = nullptr);
+
+} // namespace test
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_FONT_TEST_HELPERS_H_
diff --git a/chromium/third_party/blink/renderer/platform/testing/fuzzed_data_provider.cc b/chromium/third_party/blink/renderer/platform/testing/fuzzed_data_provider.cc
new file mode 100644
index 00000000000..2cbfb01b084
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/fuzzed_data_provider.cc
@@ -0,0 +1,33 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/testing/fuzzed_data_provider.h"
+
+namespace blink {
+
+FuzzedDataProvider::FuzzedDataProvider(const uint8_t* bytes, size_t num_bytes)
+ : provider_(bytes, num_bytes) {}
+
+CString FuzzedDataProvider::ConsumeBytesInRange(uint32_t min_bytes,
+ uint32_t max_bytes) {
+ size_t num_bytes =
+ static_cast<size_t>(provider_.ConsumeUint32InRange(min_bytes, max_bytes));
+ std::string bytes = provider_.ConsumeBytes(num_bytes);
+ return CString(bytes.data(), bytes.size());
+}
+
+CString FuzzedDataProvider::ConsumeRemainingBytes() {
+ std::string bytes = provider_.ConsumeRemainingBytes();
+ return CString(bytes.data(), bytes.size());
+}
+
+bool FuzzedDataProvider::ConsumeBool() {
+ return provider_.ConsumeBool();
+}
+
+int FuzzedDataProvider::ConsumeInt32InRange(int min, int max) {
+ return provider_.ConsumeInt32InRange(min, max);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/testing/fuzzed_data_provider.h b/chromium/third_party/blink/renderer/platform/testing/fuzzed_data_provider.h
new file mode 100644
index 00000000000..ee8897fa453
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/fuzzed_data_provider.h
@@ -0,0 +1,55 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_FUZZED_DATA_PROVIDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_FUZZED_DATA_PROVIDER_H_
+
+#include "base/test/fuzzed_data_provider.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+
+namespace blink {
+
+// This class simply wraps //base/test/fuzzed_data_provider and vends Blink
+// friendly types.
+class FuzzedDataProvider {
+ WTF_MAKE_NONCOPYABLE(FuzzedDataProvider);
+
+ public:
+ FuzzedDataProvider(const uint8_t* bytes, size_t num_bytes);
+
+ // Returns a string with length between minBytes and maxBytes. If the
+ // length is greater than the length of the remaining data this is
+ // equivalent to ConsumeRemainingBytes().
+ CString ConsumeBytesInRange(uint32_t min_bytes, uint32_t max_bytes);
+
+ // Returns a String containing all remaining bytes of the input data.
+ CString ConsumeRemainingBytes();
+
+ // Returns a bool, or false when no data remains.
+ bool ConsumeBool();
+
+ // Returns a number in the range [min, max] by consuming bytes from the input
+ // data. The value might not be uniformly distributed in the given range. If
+ // there's no input data left, always returns |min|. |min| must be less than
+ // or equal to |max|.
+ int ConsumeInt32InRange(int min, int max);
+
+ // Returns a value from |array|, consuming as many bytes as needed to do so.
+ // |array| must be a fixed-size array.
+ template <typename Type, size_t size>
+ Type PickValueInArray(Type (&array)[size]) {
+ return array[provider_.ConsumeUint32InRange(0, size - 1)];
+ }
+
+ // Reports the remaining bytes available for fuzzed input.
+ size_t RemainingBytes() { return provider_.remaining_bytes(); }
+
+ private:
+ base::FuzzedDataProvider provider_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_FUZZED_DATA_PROVIDER_H_
diff --git a/chromium/third_party/blink/renderer/platform/testing/histogram_tester.cc b/chromium/third_party/blink/renderer/platform/testing/histogram_tester.cc
new file mode 100644
index 00000000000..467ff74112a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/histogram_tester.cc
@@ -0,0 +1,43 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/testing/histogram_tester.h"
+
+#include <string>
+
+#include "base/test/histogram_tester.h"
+
+namespace blink {
+
+HistogramTester::HistogramTester()
+ : histogram_tester_(std::make_unique<base::HistogramTester>()) {}
+
+HistogramTester::~HistogramTester() = default;
+
+void HistogramTester::ExpectUniqueSample(
+ const std::string& name,
+ base::HistogramBase::Sample sample,
+ base::HistogramBase::Count count) const {
+ histogram_tester_->ExpectUniqueSample(name, sample, count);
+}
+
+void HistogramTester::ExpectBucketCount(
+ const std::string& name,
+ base::HistogramBase::Sample sample,
+ base::HistogramBase::Count count) const {
+ histogram_tester_->ExpectBucketCount(name, sample, count);
+}
+
+void HistogramTester::ExpectTotalCount(const std::string& name,
+ base::HistogramBase::Count count) const {
+ histogram_tester_->ExpectTotalCount(name, count);
+}
+
+base::HistogramBase::Count HistogramTester::GetBucketCount(
+ const std::string& name,
+ base::HistogramBase::Sample sample) const {
+ return histogram_tester_->GetBucketCount(name, sample);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/testing/histogram_tester.h b/chromium/third_party/blink/renderer/platform/testing/histogram_tester.h
new file mode 100644
index 00000000000..609a9b79e31
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/histogram_tester.h
@@ -0,0 +1,40 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_HISTOGRAM_TESTER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_HISTOGRAM_TESTER_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/histogram.h"
+
+namespace base {
+class HistogramTester;
+}
+
+namespace blink {
+
+// Blink interface for base::HistogramTester.
+class HistogramTester {
+ public:
+ HistogramTester();
+ ~HistogramTester();
+
+ void ExpectUniqueSample(const std::string& name,
+ base::HistogramBase::Sample,
+ base::HistogramBase::Count) const;
+ void ExpectBucketCount(const std::string& name,
+ base::HistogramBase::Sample,
+ base::HistogramBase::Count) const;
+ void ExpectTotalCount(const std::string& name,
+ base::HistogramBase::Count) const;
+ base::HistogramBase::Count GetBucketCount(const std::string& name,
+ base::HistogramBase::Sample) const;
+
+ private:
+ std::unique_ptr<base::HistogramTester> histogram_tester_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_HISTOGRAM_TESTER_H_
diff --git a/chromium/third_party/blink/renderer/platform/testing/image_decode_bench.cc b/chromium/third_party/blink/renderer/platform/testing/image_decode_bench.cc
new file mode 100644
index 00000000000..223f23bc987
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/image_decode_bench.cc
@@ -0,0 +1,160 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Provides a minimal wrapping of the Blink image decoders. Used to perform
+// a non-threaded, memory-to-memory image decode using micro second accuracy
+// clocks to measure image decode time. Basic usage:
+//
+// % ninja -C out/Release image_decode_bench &&
+// ./out/Release/image_decode_bench file [iterations]
+//
+// TODO(noel): Consider adding md5 checksum support to WTF. Use it to compute
+// the decoded image frame md5 and output that value.
+//
+// TODO(noel): Consider integrating this tool in Chrome telemetry for realz,
+// using the image corpora used to assess Blink image decode performance. See
+// http://crbug.com/398235#c103 and http://crbug.com/258324#c5
+
+#include <fstream>
+
+#include "base/command_line.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+namespace {
+
+scoped_refptr<SharedBuffer> ReadFile(const char* name) {
+ std::ifstream file(name, std::ios::in | std::ios::binary);
+ if (!file) {
+ fprintf(stderr, "Cannot open file %s\n", name);
+ exit(2);
+ }
+
+ file.seekg(0, std::ios::end);
+ int file_size = file.tellg();
+ file.seekg(0, std::ios::beg);
+
+ if (!file || file_size <= 0) {
+ fprintf(stderr, "Error seeking file %s\n", name);
+ exit(2);
+ }
+
+ Vector<char> buffer(file_size);
+ if (!file.read(buffer.data(), file_size)) {
+ fprintf(stderr, "Error reading file %s\n", name);
+ exit(2);
+ }
+
+ return SharedBuffer::AdoptVector(buffer);
+}
+
+struct ImageMeta {
+ char* name;
+ int width;
+ int height;
+ int frames;
+ // Cumulative time in seconds to decode all frames.
+ double time;
+};
+
+void DecodeFailure(ImageMeta* image) {
+ fprintf(stderr, "Failed to decode image %s\n", image->name);
+ exit(3);
+}
+
+void DecodeImageData(SharedBuffer* data, ImageMeta* image) {
+ std::unique_ptr<ImageDecoder> decoder = ImageDecoder::Create(
+ data, true, ImageDecoder::kAlphaPremultiplied, ColorBehavior::Ignore());
+
+ auto start = CurrentTimeTicks();
+
+ bool all_data_received = true;
+ decoder->SetData(data, all_data_received);
+
+ size_t frame_count = decoder->FrameCount();
+ for (size_t index = 0; index < frame_count; ++index) {
+ if (!decoder->DecodeFrameBufferAtIndex(index))
+ DecodeFailure(image);
+ }
+
+ image->time += (CurrentTimeTicks() - start).InSecondsF();
+
+ if (!frame_count || decoder->Failed())
+ DecodeFailure(image);
+
+ image->width = decoder->Size().Width();
+ image->height = decoder->Size().Height();
+ image->frames = frame_count;
+}
+
+} // namespace
+
+int ImageDecodeBenchMain(int argc, char* argv[]) {
+ base::CommandLine::Init(argc, argv);
+ const char* program = argv[0];
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s file [iterations]\n", program);
+ exit(1);
+ }
+
+ // Control bench decode iterations.
+
+ size_t decode_iterations = 1;
+ if (argc >= 3) {
+ char* end = nullptr;
+ decode_iterations = strtol(argv[2], &end, 10);
+ if (*end != '\0' || !decode_iterations) {
+ fprintf(stderr,
+ "Second argument should be number of iterations. "
+ "The default is 1. You supplied %s\n",
+ argv[2]);
+ exit(1);
+ }
+ }
+
+ // Create a web platform. blink::Platform can't be used directly because it
+ // has a protected constructor.
+
+ class WebPlatform : public Platform {};
+ Platform::Initialize(new WebPlatform());
+
+ // Read entire file content into |data| (a contiguous block of memory) then
+ // decode it to verify the image and record its ImageMeta data.
+
+ ImageMeta image = {argv[1], 0, 0, 0, 0};
+ scoped_refptr<SharedBuffer> data = ReadFile(argv[1]);
+ DecodeImageData(data.get(), &image);
+
+ // Image decode bench for decode_iterations.
+
+ double total_time = 0.0;
+ for (size_t i = 0; i < decode_iterations; ++i) {
+ image.time = 0.0;
+ DecodeImageData(data.get(), &image);
+ total_time += image.time;
+ }
+
+ // Results to stdout.
+
+ double average_time = total_time / decode_iterations;
+ printf("%f %f\n", total_time, average_time);
+ return 0;
+}
+
+} // namespace blink
+
+int main(int argc, char* argv[]) {
+ base::MessageLoop message_loop;
+ mojo::edk::Init();
+ return blink::ImageDecodeBenchMain(argc, argv);
+}
diff --git a/chromium/third_party/blink/renderer/platform/testing/message_loop_for_mojo.h b/chromium/third_party/blink/renderer/platform/testing/message_loop_for_mojo.h
new file mode 100644
index 00000000000..cd4a443e2f3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/message_loop_for_mojo.h
@@ -0,0 +1,14 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_MESSAGE_LOOP_FOR_MOJO_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_MESSAGE_LOOP_FOR_MOJO_H_
+
+#include "base/message_loop/message_loop.h"
+
+// This file exists just in order to allow unittests to include
+// "base/message_loop/message_loop.h" indirectly without adding a line of
+// "+base/message_loop" in their DEPS file.
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_MESSAGE_LOOP_FOR_MOJO_H_
diff --git a/chromium/third_party/blink/renderer/platform/testing/mock_web_crypto.cc b/chromium/third_party/blink/renderer/platform/testing/mock_web_crypto.cc
new file mode 100644
index 00000000000..7b0ecee0dab
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/mock_web_crypto.cc
@@ -0,0 +1,70 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/testing/mock_web_crypto.h"
+
+#include <cstring>
+#include <memory>
+#include <string>
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+using testing::_;
+using testing::DoAll;
+using testing::InSequence;
+using testing::Return;
+using testing::SetArgReferee;
+
+// MemEq(p, len) expects memcmp(arg, p, len) == 0, where |arg| is the argument
+// to be matched.
+MATCHER_P2(MemEq,
+ p,
+ len,
+ std::string("pointing to memory") + (negation ? " not" : "") +
+ " equal to \"" + std::string(static_cast<const char*>(p), len) +
+ "\" (length=" + testing::PrintToString(len) + ")") {
+ return memcmp(arg, p, len) == 0;
+}
+
+void MockWebCryptoDigestor::ExpectConsumeAndFinish(const void* input_data,
+ unsigned input_length,
+ void* output_data,
+ unsigned output_length) {
+ InSequence s;
+
+ // Consume should be called with a memory region equal to |input_data|.
+ EXPECT_CALL(*this, Consume(MemEq(input_data, input_length), input_length))
+ .WillOnce(Return(true));
+
+ // Finish(unsigned char*& result_data, unsigned& result_data_size) {
+ // result_data = output_data;
+ // result_data_size = output_length;
+ // return true;
+ // }
+ EXPECT_CALL(*this, Finish(_, _))
+ .WillOnce(
+ DoAll(SetArgReferee<0>(static_cast<unsigned char*>(output_data)),
+ SetArgReferee<1>(output_length), Return(true)));
+}
+
+MockWebCryptoDigestorFactory::MockWebCryptoDigestorFactory(
+ const void* input_data,
+ unsigned input_length,
+ void* output_data,
+ unsigned output_length)
+ : input_data_(input_data),
+ input_length_(input_length),
+ output_data_(output_data),
+ output_length_(output_length) {}
+
+MockWebCryptoDigestor* MockWebCryptoDigestorFactory::Create() {
+ std::unique_ptr<MockWebCryptoDigestor> digestor(
+ MockWebCryptoDigestor::Create());
+ digestor->ExpectConsumeAndFinish(input_data_, input_length_, output_data_,
+ output_length_);
+ return digestor.release();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/testing/mock_web_crypto.h b/chromium/third_party/blink/renderer/platform/testing/mock_web_crypto.h
new file mode 100644
index 00000000000..ce8ca5d4952
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/mock_web_crypto.h
@@ -0,0 +1,170 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_MOCK_WEB_CRYPTO_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_MOCK_WEB_CRYPTO_H_
+
+#include <memory>
+#include "testing/gmock/include/gmock/gmock.h"
+#include "third_party/blink/public/platform/web_crypto.h"
+#include "third_party/blink/public/platform/web_crypto_key_algorithm.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+class MockWebCrypto : public WebCrypto {
+ public:
+ ~MockWebCrypto() override = default;
+
+ static std::unique_ptr<MockWebCrypto> Create() {
+ return std::unique_ptr<MockWebCrypto>(
+ new testing::StrictMock<MockWebCrypto>());
+ }
+
+ MOCK_METHOD5(Encrypt,
+ void(const WebCryptoAlgorithm&,
+ const WebCryptoKey&,
+ WebVector<unsigned char>,
+ WebCryptoResult,
+ scoped_refptr<base::SingleThreadTaskRunner>));
+ MOCK_METHOD5(Decrypt,
+ void(const WebCryptoAlgorithm&,
+ const WebCryptoKey&,
+ WebVector<unsigned char>,
+ WebCryptoResult,
+ scoped_refptr<base::SingleThreadTaskRunner>));
+ MOCK_METHOD5(Sign,
+ void(const WebCryptoAlgorithm&,
+ const WebCryptoKey&,
+ WebVector<unsigned char>,
+ WebCryptoResult,
+ scoped_refptr<base::SingleThreadTaskRunner>));
+ MOCK_METHOD6(VerifySignature,
+ void(const WebCryptoAlgorithm&,
+ const WebCryptoKey&,
+ WebVector<unsigned char>,
+ WebVector<unsigned char>,
+ WebCryptoResult,
+ scoped_refptr<base::SingleThreadTaskRunner>));
+ MOCK_METHOD4(Digest,
+ void(const WebCryptoAlgorithm&,
+ WebVector<unsigned char>,
+ WebCryptoResult,
+ scoped_refptr<base::SingleThreadTaskRunner>));
+ MOCK_METHOD5(GenerateKey,
+ void(const WebCryptoAlgorithm&,
+ bool,
+ WebCryptoKeyUsageMask,
+ WebCryptoResult,
+ scoped_refptr<base::SingleThreadTaskRunner>));
+ MOCK_METHOD7(ImportKey,
+ void(WebCryptoKeyFormat,
+ WebVector<unsigned char>,
+ const WebCryptoAlgorithm&,
+ bool,
+ WebCryptoKeyUsageMask,
+ WebCryptoResult,
+ scoped_refptr<base::SingleThreadTaskRunner>));
+ MOCK_METHOD4(ExportKey,
+ void(WebCryptoKeyFormat,
+ const WebCryptoKey&,
+ WebCryptoResult,
+ scoped_refptr<base::SingleThreadTaskRunner>));
+ MOCK_METHOD6(WrapKey,
+ void(WebCryptoKeyFormat,
+ const WebCryptoKey&,
+ const WebCryptoKey&,
+ const WebCryptoAlgorithm&,
+ WebCryptoResult,
+ scoped_refptr<base::SingleThreadTaskRunner>));
+ MOCK_METHOD9(UnwrapKey,
+ void(WebCryptoKeyFormat,
+ WebVector<unsigned char>,
+ const WebCryptoKey&,
+ const WebCryptoAlgorithm&,
+ const WebCryptoAlgorithm&,
+ bool,
+ WebCryptoKeyUsageMask,
+ WebCryptoResult,
+ scoped_refptr<base::SingleThreadTaskRunner>));
+ MOCK_METHOD5(DeriveBits,
+ void(const WebCryptoAlgorithm&,
+ const WebCryptoKey&,
+ unsigned,
+ WebCryptoResult,
+ scoped_refptr<base::SingleThreadTaskRunner>));
+ MOCK_METHOD8(DeriveKey,
+ void(const WebCryptoAlgorithm&,
+ const WebCryptoKey&,
+ const WebCryptoAlgorithm&,
+ const WebCryptoAlgorithm&,
+ bool,
+ WebCryptoKeyUsageMask,
+ WebCryptoResult,
+ scoped_refptr<base::SingleThreadTaskRunner>));
+ MOCK_METHOD1(CreateDigestorProxy, WebCryptoDigestor*(WebCryptoAlgorithmId));
+ MOCK_METHOD7(DeserializeKeyForClone,
+ bool(const WebCryptoKeyAlgorithm&,
+ WebCryptoKeyType,
+ bool,
+ WebCryptoKeyUsageMask,
+ const unsigned char*,
+ unsigned,
+ WebCryptoKey&));
+ MOCK_METHOD2(SerializeKeyForClone,
+ bool(const WebCryptoKey&, WebVector<unsigned char>&));
+
+ protected:
+ MockWebCrypto() = default;
+
+ std::unique_ptr<WebCryptoDigestor> CreateDigestor(
+ WebCryptoAlgorithmId id) override {
+ return std::unique_ptr<WebCryptoDigestor>(CreateDigestorProxy(id));
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(MockWebCrypto);
+};
+
+class MockWebCryptoDigestor : public WebCryptoDigestor {
+ public:
+ ~MockWebCryptoDigestor() override = default;
+
+ static MockWebCryptoDigestor* Create() {
+ return new testing::StrictMock<MockWebCryptoDigestor>();
+ }
+
+ void ExpectConsumeAndFinish(const void* input_data,
+ unsigned input_length,
+ void* output_data,
+ unsigned output_length);
+
+ MOCK_METHOD2(Consume, bool(const unsigned char*, unsigned));
+ MOCK_METHOD2(Finish, bool(unsigned char*&, unsigned&));
+
+ protected:
+ MockWebCryptoDigestor() = default;
+
+ DISALLOW_COPY_AND_ASSIGN(MockWebCryptoDigestor);
+};
+
+class MockWebCryptoDigestorFactory final {
+ STACK_ALLOCATED();
+
+ public:
+ MockWebCryptoDigestorFactory(const void* input_data,
+ unsigned input_length,
+ void* output_data,
+ unsigned output_length);
+ MockWebCryptoDigestor* Create();
+
+ private:
+ const void* const input_data_;
+ const unsigned input_length_;
+ void* const output_data_;
+ const unsigned output_length_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/testing/paint_property_test_helpers.h b/chromium/third_party/blink/renderer/platform/testing/paint_property_test_helpers.h
new file mode 100644
index 00000000000..92a48c23c4d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/paint_property_test_helpers.h
@@ -0,0 +1,38 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_PAINT_PROPERTY_TEST_HELPERS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_PAINT_PROPERTY_TEST_HELPERS_H_
+
+#include "third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.h"
+#include "third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_chunk_properties.h"
+#include "third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h"
+
+namespace blink {
+namespace test {
+
+static inline scoped_refptr<EffectPaintPropertyNode> CreateOpacityOnlyEffect(
+ scoped_refptr<const EffectPaintPropertyNode> parent,
+ float opacity) {
+ scoped_refptr<TransformPaintPropertyNode> local_transform_space =
+ const_cast<TransformPaintPropertyNode*>(parent->LocalTransformSpace());
+ scoped_refptr<ClipPaintPropertyNode> output_clip =
+ const_cast<ClipPaintPropertyNode*>(parent->OutputClip());
+ return EffectPaintPropertyNode::Create(
+ std::move(parent), std::move(local_transform_space),
+ std::move(output_clip), kColorFilterNone, CompositorFilterOperations(),
+ opacity, SkBlendMode::kSrcOver);
+}
+
+static inline PaintChunkProperties DefaultPaintChunkProperties() {
+ PaintChunkProperties default_properties(PropertyTreeState::Root());
+
+ return default_properties;
+}
+
+} // namespace test
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_PAINT_PROPERTY_TEST_HELPERS_H_
diff --git a/chromium/third_party/blink/renderer/platform/testing/paint_test_configurations.h b/chromium/third_party/blink/renderer/platform/testing/paint_test_configurations.h
new file mode 100644
index 00000000000..0b6bf8f4c5c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/paint_test_configurations.h
@@ -0,0 +1,59 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_PAINT_TEST_CONFIGURATIONS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_PAINT_TEST_CONFIGURATIONS_H_
+
+#include <gtest/gtest.h>
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+
+namespace blink {
+
+enum {
+ kRootLayerScrolling = 1 << 0,
+ kSlimmingPaintV175 = 1 << 1,
+ kSlimmingPaintV2 = 1 << 2,
+ kUnderInvalidationChecking = 1 << 3,
+};
+
+class PaintTestConfigurations
+ : public testing::WithParamInterface<unsigned>,
+ private ScopedRootLayerScrollingForTest,
+ private ScopedSlimmingPaintV175ForTest,
+ private ScopedSlimmingPaintV2ForTest,
+ private ScopedPaintUnderInvalidationCheckingForTest {
+ public:
+ PaintTestConfigurations()
+ : ScopedRootLayerScrollingForTest(GetParam() & kRootLayerScrolling),
+ ScopedSlimmingPaintV175ForTest(GetParam() & kSlimmingPaintV175),
+ ScopedSlimmingPaintV2ForTest(GetParam() & kSlimmingPaintV2),
+ ScopedPaintUnderInvalidationCheckingForTest(
+ GetParam() & kUnderInvalidationChecking) {}
+};
+
+static constexpr unsigned kAllSlimmingPaintTestConfigurations[] = {
+ 0,
+ kSlimmingPaintV175,
+ kSlimmingPaintV2,
+ kRootLayerScrolling,
+ kSlimmingPaintV175 | kRootLayerScrolling,
+ kSlimmingPaintV2 | kRootLayerScrolling,
+};
+
+static constexpr unsigned kSlimmingPaintNonV1TestConfigurations[] = {
+ kSlimmingPaintV175, kSlimmingPaintV175 | kRootLayerScrolling,
+ kSlimmingPaintV2, kSlimmingPaintV2 | kRootLayerScrolling,
+};
+
+static constexpr unsigned kSlimmingPaintV2TestConfigurations[] = {
+ kSlimmingPaintV2, kSlimmingPaintV2 | kRootLayerScrolling,
+};
+
+static constexpr unsigned kSlimmingPaintVersions[] = {
+ 0, kSlimmingPaintV175, kSlimmingPaintV2,
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_PAINT_TEST_CONFIGURATIONS_H_
diff --git a/chromium/third_party/blink/renderer/platform/testing/picture_matchers.cc b/chromium/third_party/blink/renderer/platform/testing/picture_matchers.cc
new file mode 100644
index 00000000000..d38c327be82
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/picture_matchers.cc
@@ -0,0 +1,148 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/testing/picture_matchers.h"
+
+#include <utility>
+
+#include "third_party/blink/renderer/platform/geometry/float_quad.h"
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkPicture.h"
+
+namespace blink {
+
+namespace {
+
+class DrawsRectangleCanvas : public SkCanvas {
+ public:
+ DrawsRectangleCanvas()
+ : SkCanvas(800, 600),
+ save_count_(0),
+ alpha_(255),
+ alpha_save_layer_count_(-1) {}
+ const Vector<RectWithColor>& RectsWithColor() const { return rects_; }
+
+ void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
+ SkPoint quad[4];
+ getTotalMatrix().mapRectToQuad(quad, rect);
+
+ SkRect device_rect;
+ device_rect.set(quad, 4);
+ SkIRect device_clip_bounds;
+ FloatRect clipped_rect;
+ if (getDeviceClipBounds(&device_clip_bounds) &&
+ device_rect.intersect(SkRect::Make(device_clip_bounds)))
+ clipped_rect = device_rect;
+
+ unsigned paint_alpha = static_cast<unsigned>(paint.getAlpha());
+ SkPaint paint_with_alpha(paint);
+ paint_with_alpha.setAlpha(static_cast<U8CPU>(alpha_ * paint_alpha / 255));
+ Color color = Color(paint_with_alpha.getColor());
+
+ rects_.emplace_back(clipped_rect, color);
+ SkCanvas::onDrawRect(rect, paint);
+ }
+
+ SkCanvas::SaveLayerStrategy getSaveLayerStrategy(
+ const SaveLayerRec& rec) override {
+ save_count_++;
+ unsigned layer_alpha = static_cast<unsigned>(rec.fPaint->getAlpha());
+ if (layer_alpha < 255) {
+ DCHECK_EQ(alpha_save_layer_count_, -1);
+ alpha_save_layer_count_ = save_count_;
+ alpha_ = layer_alpha;
+ }
+ return SkCanvas::getSaveLayerStrategy(rec);
+ }
+
+ void willSave() override {
+ save_count_++;
+ SkCanvas::willSave();
+ }
+
+ void willRestore() override {
+ DCHECK_GT(save_count_, 0);
+ if (alpha_save_layer_count_ == save_count_) {
+ alpha_ = 255;
+ alpha_save_layer_count_ = -1;
+ }
+ save_count_--;
+ SkCanvas::willRestore();
+ }
+
+ private:
+ Vector<RectWithColor> rects_;
+ int save_count_;
+ unsigned alpha_;
+ int alpha_save_layer_count_;
+};
+
+class DrawsRectanglesMatcher
+ : public testing::MatcherInterface<const SkPicture&> {
+ public:
+ DrawsRectanglesMatcher(const Vector<RectWithColor>& rects_with_color)
+ : rects_with_color_(rects_with_color) {}
+
+ bool MatchAndExplain(const SkPicture& picture,
+ testing::MatchResultListener* listener) const override {
+ DrawsRectangleCanvas canvas;
+ picture.playback(&canvas);
+ const auto& actual_rects = canvas.RectsWithColor();
+ if (actual_rects.size() != rects_with_color_.size()) {
+ *listener << "which draws " << actual_rects.size() << " rects";
+ return false;
+ }
+
+ for (unsigned index = 0; index < actual_rects.size(); index++) {
+ const auto& actual_rect_with_color = actual_rects[index];
+ const auto& expect_rect_with_color = rects_with_color_[index];
+
+ if (EnclosingIntRect(actual_rect_with_color.rect) !=
+ EnclosingIntRect(expect_rect_with_color.rect) ||
+ actual_rect_with_color.color != expect_rect_with_color.color) {
+ if (listener->IsInterested()) {
+ *listener << "at index " << index << " which draws "
+ << actual_rect_with_color.rect << " with color "
+ << actual_rect_with_color.color.Serialized().Ascii().data()
+ << "\n";
+ }
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ void DescribeTo(::std::ostream* os) const override {
+ *os << "\n";
+ for (unsigned index = 0; index < rects_with_color_.size(); index++) {
+ const auto& rect_with_color = rects_with_color_[index];
+ *os << "at index " << index << " rect draws " << rect_with_color.rect
+ << " with color " << rect_with_color.color.Serialized().Ascii().data()
+ << "\n";
+ }
+ }
+
+ private:
+ const Vector<RectWithColor> rects_with_color_;
+};
+
+} // namespace
+
+testing::Matcher<const SkPicture&> DrawsRectangle(const FloatRect& rect,
+ Color color) {
+ Vector<RectWithColor> rects_with_color;
+ rects_with_color.push_back(RectWithColor(rect, color));
+ return testing::MakeMatcher(new DrawsRectanglesMatcher(rects_with_color));
+}
+
+testing::Matcher<const SkPicture&> DrawsRectangles(
+ const Vector<RectWithColor>& rects_with_color) {
+ return testing::MakeMatcher(new DrawsRectanglesMatcher(rects_with_color));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/testing/picture_matchers.h b/chromium/third_party/blink/renderer/platform/testing/picture_matchers.h
new file mode 100644
index 00000000000..f9758acb4f4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/picture_matchers.h
@@ -0,0 +1,40 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_PICTURE_MATCHERS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_PICTURE_MATCHERS_H_
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/graphics/color.h"
+
+class SkPicture;
+
+namespace blink {
+
+class FloatRect;
+
+// Matches if the picture draws exactly one rectangle, which (after accounting
+// for the total transformation matrix and applying any clips inside that
+// transform) matches the rect provided, and whose paint has the color
+// requested.
+// Note that clips which appear outside of a transform are not currently
+// supported.
+testing::Matcher<const SkPicture&> DrawsRectangle(const FloatRect&, Color);
+
+struct RectWithColor {
+ RectWithColor(const FloatRect& rect_arg, const Color& color_arg)
+ : rect(rect_arg), color(color_arg) {}
+ FloatRect rect;
+ Color color;
+};
+
+// Same as above, but matches a number of rectangles equal to the size of the
+// given vector.
+testing::Matcher<const SkPicture&> DrawsRectangles(
+ const Vector<RectWithColor>&);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_PICTURE_MATCHERS_H_
diff --git a/chromium/third_party/blink/renderer/platform/testing/run_all_perf_tests.cc b/chromium/third_party/blink/renderer/platform/testing/run_all_perf_tests.cc
new file mode 100644
index 00000000000..81ccef3732f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/run_all_perf_tests.cc
@@ -0,0 +1,9 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/testing/blink_perf_test_suite.h"
+
+int main(int argc, char** argv) {
+ return blink::BlinkPerfTestSuite(argc, argv).Run();
+}
diff --git a/chromium/third_party/blink/renderer/platform/testing/run_all_tests.cc b/chromium/third_party/blink/renderer/platform/testing/run_all_tests.cc
new file mode 100644
index 00000000000..ce8e4d87dd0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/run_all_tests.cc
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2013 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 <memory>
+#include "base/bind.h"
+#include "base/test/launcher/unit_test_launcher.h"
+#include "base/test/test_io_thread.h"
+#include "base/test/test_suite.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/edk/embedder/scoped_ipc_support.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
+
+namespace {
+
+int runTestSuite(base::TestSuite* testSuite) {
+ int result = testSuite->Run();
+ blink::ThreadState::Current()->CollectAllGarbage();
+ return result;
+}
+
+} // namespace
+
+int main(int argc, char** argv) {
+ blink::ScopedUnittestsEnvironmentSetup testEnvironmentSetup(argc, argv);
+ int result = 0;
+ {
+ base::TestSuite testSuite(argc, argv);
+
+ mojo::edk::Init();
+ base::TestIOThread testIoThread(base::TestIOThread::kAutoStart);
+ mojo::edk::ScopedIPCSupport ipcSupport(
+ testIoThread.task_runner(),
+ mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN);
+ result = base::LaunchUnitTests(
+ argc, argv, base::BindOnce(runTestSuite, base::Unretained(&testSuite)));
+ }
+ return result;
+}
diff --git a/chromium/third_party/blink/renderer/platform/testing/scoped_mocked_url.cc b/chromium/third_party/blink/renderer/platform/testing/scoped_mocked_url.cc
new file mode 100644
index 00000000000..95965ad1875
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/scoped_mocked_url.cc
@@ -0,0 +1,28 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/testing/scoped_mocked_url.h"
+
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
+#include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
+
+namespace blink {
+namespace test {
+
+ScopedMockedURL::ScopedMockedURL(const WebURL& url) : url_(url) {}
+
+ScopedMockedURL::~ScopedMockedURL() {
+ Platform::Current()->GetURLLoaderMockFactory()->UnregisterURL(url_);
+}
+
+ScopedMockedURLLoad::ScopedMockedURLLoad(const WebURL& full_url,
+ const WebString& file_path,
+ const WebString& mime_type)
+ : ScopedMockedURL(full_url) {
+ URLTestHelpers::RegisterMockedURLLoad(full_url, file_path, mime_type);
+}
+
+} // namespace test
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/testing/scoped_mocked_url.h b/chromium/third_party/blink/renderer/platform/testing/scoped_mocked_url.h
new file mode 100644
index 00000000000..05326b9642e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/scoped_mocked_url.h
@@ -0,0 +1,41 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_SCOPED_MOCKED_URL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_SCOPED_MOCKED_URL_H_
+
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/platform/web_url.h"
+#include "third_party/blink/public/platform/web_url_response.h"
+
+namespace blink {
+
+namespace test {
+
+// Convenience classes that register a mocked URL on construction, and
+// unregister it on destruction. This prevent mocked URL from leaking to other
+// tests.
+class ScopedMockedURL {
+ public:
+ explicit ScopedMockedURL(const WebURL&);
+ virtual ~ScopedMockedURL();
+
+ private:
+ WebURL url_;
+};
+
+class ScopedMockedURLLoad : ScopedMockedURL {
+ public:
+ ScopedMockedURLLoad(
+ const WebURL& full_url,
+ const WebString& file_path,
+ const WebString& mime_type = WebString::FromUTF8("text/html"));
+ ~ScopedMockedURLLoad() override = default;
+};
+
+} // namespace test
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/testing/shaping_line_breaker_perf_test.cc b/chromium/third_party/blink/renderer/platform/testing/shaping_line_breaker_perf_test.cc
new file mode 100644
index 00000000000..cb8149e50c9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/shaping_line_breaker_perf_test.cc
@@ -0,0 +1,146 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.h"
+
+#include <unicode/uscript.h>
+#include "base/time/time.h"
+#include "cc/base/lap_timer.h"
+#include "third_party/blink/renderer/platform/fonts/font.h"
+#include "third_party/blink/renderer/platform/fonts/font_cache.h"
+#include "third_party/blink/renderer/platform/fonts/font_test_utilities.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_inline_headers.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_test_info.h"
+#include "third_party/blink/renderer/platform/text/text_break_iterator.h"
+#include "third_party/blink/renderer/platform/text/text_run.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/perf/perf_test.h"
+
+namespace blink {
+namespace {
+
+static const int kTimeLimitMillis = 2000;
+static const int kWarmupRuns = 5;
+static const int kTimeCheckInterval = 10;
+
+LayoutUnit ShapeText(ShapingLineBreaker* breaker,
+ LayoutUnit available_space,
+ unsigned string_length) {
+ unsigned break_offset = 0;
+ LayoutUnit total_width;
+ ShapingLineBreaker::Result result;
+ scoped_refptr<ShapeResult> shape_result;
+ while (break_offset < string_length) {
+ shape_result = breaker->ShapeLine(break_offset, available_space, &result);
+ break_offset = result.break_offset;
+ total_width += shape_result->SnappedWidth();
+ }
+ return total_width;
+}
+
+} // anonymous namespace
+
+class ShapingLineBreakerPerfTest : public testing::Test {
+ public:
+ ShapingLineBreakerPerfTest()
+ : timer_(kWarmupRuns,
+ base::TimeDelta::FromMilliseconds(kTimeLimitMillis),
+ kTimeCheckInterval) {}
+
+ void SetUp() override {
+ font_description.SetComputedSize(12.0);
+ font = Font(font_description);
+ font.Update(nullptr);
+ }
+
+ void TearDown() override {}
+
+ FontCachePurgePreventer font_cache_purge_preventer;
+ FontDescription font_description;
+ Font font;
+ unsigned start_index = 0;
+ unsigned num_glyphs = 0;
+ hb_script_t script = HB_SCRIPT_INVALID;
+ cc::LapTimer timer_;
+};
+
+TEST_F(ShapingLineBreakerPerfTest, ShapeLatinText) {
+ // "My Brother's Keeper?"
+ // By William Arthur Dunkerley (John Oxenham)
+ // In the public domain.
+ String string(
+ u"\"Am I my brother's keeper?\""
+ "Yes, of a truth!"
+ "Thine asking is thine answer."
+ "That self-condemning cry of Cain"
+ "Has been the plea of every selfish soul since then,"
+ "Which hath its brother slain."
+ "God's word is plain,"
+ "And doth thy shrinking soul arraign."
+ ""
+ "Thy brother's keeper?"
+ "Yea, of a truth thou art!"
+ "For if not--who?"
+ "Are ye not both,--both thou and he"
+ "Of God's great family?"
+ "How rid thee of thy soul's responsibility?"
+ "For every ill in all the world"
+ "Each soul is sponsor and account must bear."
+ "And He, and he thy brother of despair,"
+ "Claim, of thy overmuch, their share."
+ ""
+ "Thou hast had good, and he the strangled days;"
+ "But now,--the old things pass."
+ "No longer of thy grace"
+ "Is he content to live in evil case"
+ "For the anointing of thy shining face."
+ "The old things pass.--Beware lest ye pass with them,"
+ "And your place"
+ "Become an emptiness!"
+ ""
+ "Beware! Lest, when the \"Have-nots\" claim,"
+ "From those who have, their rightful share,"
+ "Thy borders be swept bare"
+ "As by the final flame."
+ "Better to share before than after."
+ "\"After?\" ... For thee may be no after!"
+ "Only the howl of mocking laughter"
+ "At thy belated care. Make no mistake!--"
+ "\"After\" will be too late."
+ "When once the \"Have-nots\" claim ... they take."
+ "\"After!\" ... When that full claim is made,"
+ "You and your golden gods may all lie dead."
+ ""
+ "Set now your house in order,"
+ "Ere it be too late!"
+ "For, once the storm of hate"
+ "Be loosed, no man shall stay it till"
+ "Its thirst has slaked its fill,"
+ "And you, poor victims of this last \"too late,\""
+ "Shall in the shadows mourn your lost estate.");
+ unsigned len = string.length();
+ LazyLineBreakIterator break_iterator(string, "en-US", LineBreakType::kNormal);
+ TextDirection direction = TextDirection::kLtr;
+
+ HarfBuzzShaper shaper(string.Characters16(), len);
+ scoped_refptr<ShapeResult> result = shaper.Shape(&font, direction);
+ ShapingLineBreaker breaker(&shaper, &font, result.get(), &break_iterator);
+
+ scoped_refptr<ShapeResult> line;
+ LayoutUnit available_width_px(500);
+
+ LayoutUnit expected_width = ShapeText(&breaker, available_width_px, len);
+ timer_.Reset();
+ do {
+ LayoutUnit width = ShapeText(&breaker, available_width_px, len);
+ EXPECT_EQ(expected_width, width);
+ timer_.NextLap();
+ } while (!timer_.HasTimeLimitExpired());
+
+ perf_test::PrintResult("ShapingLineBreakerPerfTest", "shape latin text", "",
+ timer_.LapsPerSecond(), "runs/s", true);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/testing/test_paint_artifact.cc b/chromium/third_party/blink/renderer/platform/testing/test_paint_artifact.cc
new file mode 100644
index 00000000000..52b205c5fdd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/test_paint_artifact.cc
@@ -0,0 +1,155 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/testing/test_paint_artifact.h"
+
+#include <memory>
+#include "cc/layers/layer.h"
+#include "third_party/blink/renderer/platform/graphics/paint/display_item_client.h"
+#include "third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h"
+#include "third_party/blink/renderer/platform/graphics/paint/foreign_layer_display_item.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_artifact.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_record.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_recorder.h"
+#include "third_party/blink/renderer/platform/graphics/paint/scroll_hit_test_display_item.h"
+#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
+#include "third_party/blink/renderer/platform/testing/fake_display_item_client.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+namespace blink {
+
+class TestPaintArtifact::DummyRectClient : public FakeDisplayItemClient {
+ public:
+ LayoutRect VisualRect() const final { return EnclosingLayoutRect(rect_); }
+ void SetVisualRect(const FloatRect& rect) { rect_ = rect; }
+
+ sk_sp<PaintRecord> MakeRecord(const FloatRect& rect, Color color) {
+ rect_ = rect;
+ PaintRecorder recorder;
+ PaintCanvas* canvas = recorder.beginRecording(rect);
+ PaintFlags flags;
+ flags.setColor(color.Rgb());
+ canvas->drawRect(rect, flags);
+ return recorder.finishRecordingAsPicture();
+ }
+
+ private:
+ FloatRect rect_;
+};
+
+TestPaintArtifact::TestPaintArtifact() : display_item_list_(0), built_(false) {}
+
+TestPaintArtifact::~TestPaintArtifact() = default;
+
+TestPaintArtifact& TestPaintArtifact::Chunk(
+ scoped_refptr<const TransformPaintPropertyNode> transform,
+ scoped_refptr<const ClipPaintPropertyNode> clip,
+ scoped_refptr<const EffectPaintPropertyNode> effect) {
+ return Chunk(NewClient(), transform, clip, effect);
+}
+
+TestPaintArtifact& TestPaintArtifact::Chunk(
+ DisplayItemClient& client,
+ scoped_refptr<const TransformPaintPropertyNode> transform,
+ scoped_refptr<const ClipPaintPropertyNode> clip,
+ scoped_refptr<const EffectPaintPropertyNode> effect) {
+ PropertyTreeState property_tree_state(transform.get(), clip.get(),
+ effect.get());
+ PaintChunkProperties properties(property_tree_state);
+ return Chunk(client, properties);
+}
+
+TestPaintArtifact& TestPaintArtifact::Chunk(
+ const PaintChunkProperties& properties) {
+ return Chunk(NewClient(), properties);
+}
+
+TestPaintArtifact& TestPaintArtifact::Chunk(
+ DisplayItemClient& client,
+ const PaintChunkProperties& properties) {
+ if (!paint_chunks_.IsEmpty())
+ paint_chunks_.back().end_index = display_item_list_.size();
+ paint_chunks_.push_back(PaintChunk(
+ display_item_list_.size(), 0,
+ PaintChunk::Id(client, DisplayItem::kDrawingFirst), properties));
+ // Assume PaintController has processed this chunk.
+ paint_chunks_.back().client_is_just_created = false;
+ return *this;
+}
+
+TestPaintArtifact& TestPaintArtifact::RectDrawing(const FloatRect& bounds,
+ Color color) {
+ return RectDrawing(NewClient(), bounds, color);
+}
+
+TestPaintArtifact& TestPaintArtifact::RectDrawing(DisplayItemClient& client,
+ const FloatRect& bounds,
+ Color color) {
+ display_item_list_.AllocateAndConstruct<DrawingDisplayItem>(
+ client, DisplayItem::kDrawingFirst,
+ static_cast<DummyRectClient&>(client).MakeRecord(bounds, color));
+ return *this;
+}
+
+TestPaintArtifact& TestPaintArtifact::ForeignLayer(
+ const FloatPoint& location,
+ const IntSize& size,
+ scoped_refptr<cc::Layer> layer) {
+ return ForeignLayer(NewClient(), location, size, layer);
+}
+
+TestPaintArtifact& TestPaintArtifact::ForeignLayer(
+ DisplayItemClient& client,
+ const FloatPoint& location,
+ const IntSize& size,
+ scoped_refptr<cc::Layer> layer) {
+ static_cast<DummyRectClient&>(client).SetVisualRect(
+ FloatRect(location, FloatSize(size)));
+ display_item_list_.AllocateAndConstruct<ForeignLayerDisplayItem>(
+ client, DisplayItem::kForeignLayerFirst, std::move(layer), location,
+ size);
+ return *this;
+}
+
+TestPaintArtifact& TestPaintArtifact::ScrollHitTest(
+ scoped_refptr<const TransformPaintPropertyNode> scroll_offset) {
+ return ScrollHitTest(NewClient(), scroll_offset);
+}
+
+TestPaintArtifact& TestPaintArtifact::ScrollHitTest(
+ DisplayItemClient& client,
+ scoped_refptr<const TransformPaintPropertyNode> scroll_offset) {
+ display_item_list_.AllocateAndConstruct<ScrollHitTestDisplayItem>(
+ client, DisplayItem::kScrollHitTest, std::move(scroll_offset));
+ return *this;
+}
+
+TestPaintArtifact& TestPaintArtifact::KnownToBeOpaque() {
+ paint_chunks_.back().known_to_be_opaque = true;
+ return *this;
+}
+
+const PaintArtifact& TestPaintArtifact::Build() {
+ if (built_)
+ return paint_artifact_;
+
+ if (!paint_chunks_.IsEmpty())
+ paint_chunks_.back().end_index = display_item_list_.size();
+ paint_artifact_ =
+ PaintArtifact(std::move(display_item_list_), std::move(paint_chunks_));
+ built_ = true;
+ return paint_artifact_;
+}
+
+DisplayItemClient& TestPaintArtifact::NewClient() {
+ dummy_clients_.push_back(std::make_unique<DummyRectClient>());
+ return *dummy_clients_.back();
+}
+
+DisplayItemClient& TestPaintArtifact::Client(size_t i) const {
+ return *dummy_clients_[i];
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/testing/test_paint_artifact.h b/chromium/third_party/blink/renderer/platform/testing/test_paint_artifact.h
new file mode 100644
index 00000000000..89e0a096468
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/test_paint_artifact.h
@@ -0,0 +1,103 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_TEST_PAINT_ARTIFACT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_TEST_PAINT_ARTIFACT_H_
+
+#include <memory>
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/graphics/color.h"
+#include "third_party/blink/renderer/platform/graphics/paint/display_item_list.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_artifact.h"
+#include "third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace cc {
+class Layer;
+}
+
+namespace blink {
+
+class ClipPaintPropertyNode;
+class EffectPaintPropertyNode;
+class FloatRect;
+class PaintArtifact;
+class TransformPaintPropertyNode;
+
+// Useful for quickly making a paint artifact in unit tests.
+// Must remain in scope while the paint artifact is used, because it owns the
+// display item clients.
+//
+// Usage:
+// TestPaintArtifact artifact;
+// artifact.Chunk(paintProperties)
+// .RectDrawing(bounds, color)
+// .RectDrawing(bounds2, color2);
+// artifact.Chunk(otherPaintProperties)
+// .RectDrawing(bounds3, color3);
+// DoSomethingWithArtifact(artifact);
+class TestPaintArtifact {
+ STACK_ALLOCATED();
+
+ public:
+ TestPaintArtifact();
+ ~TestPaintArtifact();
+
+ // Add to the artifact.
+ TestPaintArtifact& Chunk(scoped_refptr<const TransformPaintPropertyNode>,
+ scoped_refptr<const ClipPaintPropertyNode>,
+ scoped_refptr<const EffectPaintPropertyNode>);
+ TestPaintArtifact& Chunk(const PaintChunkProperties&);
+ TestPaintArtifact& RectDrawing(const FloatRect& bounds, Color);
+ TestPaintArtifact& ForeignLayer(const FloatPoint&,
+ const IntSize&,
+ scoped_refptr<cc::Layer>);
+ TestPaintArtifact& ScrollHitTest(
+ scoped_refptr<const TransformPaintPropertyNode> scroll_offset);
+ TestPaintArtifact& KnownToBeOpaque();
+
+ // Add to the artifact, with specified display item client. These are used
+ // to test incremental paint artifact updates.
+ TestPaintArtifact& Chunk(DisplayItemClient&,
+ scoped_refptr<const TransformPaintPropertyNode>,
+ scoped_refptr<const ClipPaintPropertyNode>,
+ scoped_refptr<const EffectPaintPropertyNode>);
+ TestPaintArtifact& Chunk(DisplayItemClient&, const PaintChunkProperties&);
+ TestPaintArtifact& RectDrawing(DisplayItemClient&,
+ const FloatRect& bounds,
+ Color);
+ TestPaintArtifact& ForeignLayer(DisplayItemClient&,
+ const FloatPoint&,
+ const IntSize&,
+ scoped_refptr<cc::Layer>);
+ TestPaintArtifact& ScrollHitTest(
+ DisplayItemClient&,
+ scoped_refptr<const TransformPaintPropertyNode> scroll_offset);
+
+ // Can't add more things once this is called.
+ const PaintArtifact& Build();
+
+ // Create a new display item client which is owned by this TestPaintArtifact.
+ DisplayItemClient& NewClient();
+
+ DisplayItemClient& Client(size_t) const;
+
+ private:
+ class DummyRectClient;
+ Vector<std::unique_ptr<DummyRectClient>> dummy_clients_;
+
+ // Exists if m_built is false.
+ DisplayItemList display_item_list_;
+ Vector<PaintChunk> paint_chunks_;
+
+ // Exists if m_built is true.
+ PaintArtifact paint_artifact_;
+
+ bool built_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_TEST_PAINT_ARTIFACT_H_
diff --git a/chromium/third_party/blink/renderer/platform/testing/testing_platform_support.cc b/chromium/third_party/blink/renderer/platform/testing/testing_platform_support.cc
new file mode 100644
index 00000000000..50eab238453
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/testing_platform_support.cc
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2014 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 "third_party/blink/renderer/platform/testing/testing_platform_support.h"
+
+#include <memory>
+#include <string>
+
+#include "base/command_line.h"
+#include "base/memory/discardable_memory_allocator.h"
+#include "base/run_loop.h"
+#include "base/test/icu_test_util.h"
+#include "base/test/test_discardable_memory_allocator.h"
+#include "cc/blink/web_compositor_support_impl.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "third_party/blink/public/platform/interface_provider.h"
+#include "third_party/blink/public/platform/web_content_layer.h"
+#include "third_party/blink/public/platform/web_external_texture_layer.h"
+#include "third_party/blink/public/platform/web_image_layer.h"
+#include "third_party/blink/public/platform/web_runtime_features.h"
+#include "third_party/blink/public/platform/web_scrollbar_layer.h"
+#include "third_party/blink/renderer/platform/font_family_names.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/instrumentation/resource_coordinator/blink_resource_coordinator_base.h"
+#include "third_party/blink/renderer/platform/instrumentation/resource_coordinator/renderer_resource_coordinator.h"
+#include "third_party/blink/renderer/platform/language.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_initiator_type_names.h"
+#include "third_party/blink/renderer/platform/network/http_names.h"
+#include "third_party/blink/renderer/platform/network/mime/mock_mime_registry.h"
+#include "third_party/blink/renderer/platform/scheduler/base/real_time_domain.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_manager.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
+#include "third_party/blink/renderer/platform/wtf/cryptographically_random_number.h"
+#include "third_party/blink/renderer/platform/wtf/wtf.h"
+
+namespace blink {
+
+class TestingPlatformSupport::TestingInterfaceProvider
+ : public blink::InterfaceProvider {
+ public:
+ TestingInterfaceProvider() = default;
+ virtual ~TestingInterfaceProvider() = default;
+
+ void GetInterface(const char* name,
+ mojo::ScopedMessagePipeHandle handle) override {
+ if (std::string(name) == mojom::blink::MimeRegistry::Name_) {
+ mojo::MakeStrongBinding(
+ std::make_unique<MockMimeRegistry>(),
+ mojom::blink::MimeRegistryRequest(std::move(handle)));
+ return;
+ }
+ }
+};
+
+namespace {
+
+class DummyThread final : public blink::WebThread {
+ public:
+ bool IsCurrentThread() const override { return true; }
+ blink::WebScheduler* Scheduler() const override { return nullptr; }
+};
+
+} // namespace
+
+std::unique_ptr<WebLayer> TestingCompositorSupport::CreateLayer() {
+ return nullptr;
+}
+
+std::unique_ptr<WebLayer> TestingCompositorSupport::CreateLayerFromCCLayer(
+ cc::Layer*) {
+ return nullptr;
+}
+
+std::unique_ptr<WebContentLayer> TestingCompositorSupport::CreateContentLayer(
+ WebContentLayerClient*) {
+ return nullptr;
+}
+std::unique_ptr<WebExternalTextureLayer>
+TestingCompositorSupport::CreateExternalTextureLayer(cc::TextureLayerClient*) {
+ return nullptr;
+}
+
+std::unique_ptr<WebImageLayer> TestingCompositorSupport::CreateImageLayer() {
+ return nullptr;
+}
+
+std::unique_ptr<WebScrollbarLayer>
+TestingCompositorSupport::CreateScrollbarLayer(
+ std::unique_ptr<WebScrollbar>,
+ WebScrollbarThemePainter,
+ std::unique_ptr<WebScrollbarThemeGeometry>) {
+ return nullptr;
+}
+
+std::unique_ptr<WebScrollbarLayer>
+TestingCompositorSupport::CreateOverlayScrollbarLayer(
+ std::unique_ptr<WebScrollbar>,
+ WebScrollbarThemePainter,
+ std::unique_ptr<WebScrollbarThemeGeometry>) {
+ return nullptr;
+}
+
+std::unique_ptr<WebScrollbarLayer>
+TestingCompositorSupport::CreateSolidColorScrollbarLayer(
+ WebScrollbar::Orientation,
+ int thumb_thickness,
+ int track_start,
+ bool is_left_side_vertical_scrollbar) {
+ return nullptr;
+}
+
+TestingPlatformSupport::TestingPlatformSupport()
+ : TestingPlatformSupport(TestingPlatformSupport::Config()) {}
+
+TestingPlatformSupport::TestingPlatformSupport(const Config& config)
+ : config_(config),
+ old_platform_(Platform::Current()),
+ interface_provider_(new TestingInterfaceProvider) {
+ DCHECK(old_platform_);
+}
+
+TestingPlatformSupport::~TestingPlatformSupport() {
+ DCHECK_EQ(this, Platform::Current());
+}
+
+WebString TestingPlatformSupport::DefaultLocale() {
+ return WebString::FromUTF8("en-US");
+}
+
+WebCompositorSupport* TestingPlatformSupport::CompositorSupport() {
+ if (config_.compositor_support)
+ return config_.compositor_support;
+
+ return old_platform_ ? old_platform_->CompositorSupport() : nullptr;
+}
+
+WebThread* TestingPlatformSupport::CurrentThread() {
+ return old_platform_ ? old_platform_->CurrentThread() : nullptr;
+}
+
+WebBlobRegistry* TestingPlatformSupport::GetBlobRegistry() {
+ return old_platform_ ? old_platform_->GetBlobRegistry() : nullptr;
+}
+
+WebClipboard* TestingPlatformSupport::Clipboard() {
+ return old_platform_ ? old_platform_->Clipboard() : nullptr;
+}
+
+WebFileUtilities* TestingPlatformSupport::GetFileUtilities() {
+ return old_platform_ ? old_platform_->GetFileUtilities() : nullptr;
+}
+
+WebIDBFactory* TestingPlatformSupport::IdbFactory() {
+ return old_platform_ ? old_platform_->IdbFactory() : nullptr;
+}
+
+WebURLLoaderMockFactory* TestingPlatformSupport::GetURLLoaderMockFactory() {
+ return old_platform_ ? old_platform_->GetURLLoaderMockFactory() : nullptr;
+}
+
+std::unique_ptr<WebURLLoaderFactory>
+TestingPlatformSupport::CreateDefaultURLLoaderFactory() {
+ return old_platform_ ? old_platform_->CreateDefaultURLLoaderFactory()
+ : nullptr;
+}
+
+WebData TestingPlatformSupport::GetDataResource(const char* name) {
+ return old_platform_ ? old_platform_->GetDataResource(name) : WebData();
+}
+
+InterfaceProvider* TestingPlatformSupport::GetInterfaceProvider() {
+ return interface_provider_.get();
+}
+
+void TestingPlatformSupport::RunUntilIdle() {
+ base::RunLoop().RunUntilIdle();
+}
+
+class ScopedUnittestsEnvironmentSetup::DummyPlatform final
+ : public blink::Platform {
+ public:
+ DummyPlatform() = default;
+
+ blink::WebThread* CurrentThread() override {
+ static DummyThread dummy_thread;
+ return &dummy_thread;
+ };
+};
+
+class ScopedUnittestsEnvironmentSetup::DummyRendererResourceCoordinator final
+ : public blink::RendererResourceCoordinator {};
+
+ScopedUnittestsEnvironmentSetup::ScopedUnittestsEnvironmentSetup(int argc,
+ char** argv) {
+ base::CommandLine::Init(argc, argv);
+
+ base::test::InitializeICUForTesting();
+
+ discardable_memory_allocator_ =
+ std::make_unique<base::TestDiscardableMemoryAllocator>();
+ base::DiscardableMemoryAllocator::SetInstance(
+ discardable_memory_allocator_.get());
+
+ dummy_platform_ = std::make_unique<DummyPlatform>();
+ Platform::SetCurrentPlatformForTesting(dummy_platform_.get());
+
+ WTF::Partitions::Initialize(nullptr);
+ WTF::Initialize(nullptr);
+
+ compositor_support_ = std::make_unique<cc_blink::WebCompositorSupportImpl>();
+ testing_platform_config_.compositor_support = compositor_support_.get();
+ testing_platform_support_ =
+ std::make_unique<TestingPlatformSupport>(testing_platform_config_);
+ Platform::SetCurrentPlatformForTesting(testing_platform_support_.get());
+
+ if (BlinkResourceCoordinatorBase::IsEnabled()) {
+ dummy_renderer_resource_coordinator_ =
+ std::make_unique<DummyRendererResourceCoordinator>();
+ RendererResourceCoordinator::
+ SetCurrentRendererResourceCoordinatorForTesting(
+ dummy_renderer_resource_coordinator_.get());
+ }
+
+ ProcessHeap::Init();
+ ThreadState::AttachMainThread();
+ ThreadState::Current()->RegisterTraceDOMWrappers(nullptr, nullptr, nullptr,
+ nullptr);
+ HTTPNames::init();
+ FetchInitiatorTypeNames::init();
+
+ InitializePlatformLanguage();
+ FontFamilyNames::init();
+ WebRuntimeFeatures::EnableExperimentalFeatures(true);
+ WebRuntimeFeatures::EnableTestOnlyFeatures(true);
+}
+
+ScopedUnittestsEnvironmentSetup::~ScopedUnittestsEnvironmentSetup() = default;
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/testing/testing_platform_support.h b/chromium/third_party/blink/renderer/platform/testing/testing_platform_support.h
new file mode 100644
index 00000000000..33ad94665b3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/testing_platform_support.h
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2014 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_TESTING_PLATFORM_SUPPORT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_TESTING_PLATFORM_SUPPORT_H_
+
+#include <memory>
+#include <utility>
+
+#include "base/macros.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_compositor_support.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+namespace base {
+class TestDiscardableMemoryAllocator;
+}
+
+namespace cc_blink {
+class WebCompositorSupportImpl;
+} // namespace cc_blink
+
+namespace blink {
+class WebCompositorSupport;
+class WebThread;
+
+class TestingCompositorSupport : public WebCompositorSupport {
+ std::unique_ptr<WebLayer> CreateLayer() override;
+ std::unique_ptr<WebLayer> CreateLayerFromCCLayer(cc::Layer*) override;
+ std::unique_ptr<WebContentLayer> CreateContentLayer(
+ WebContentLayerClient*) override;
+ std::unique_ptr<WebExternalTextureLayer> CreateExternalTextureLayer(
+ cc::TextureLayerClient*) override;
+ std::unique_ptr<WebImageLayer> CreateImageLayer() override;
+ std::unique_ptr<WebScrollbarLayer> CreateScrollbarLayer(
+ std::unique_ptr<WebScrollbar>,
+ WebScrollbarThemePainter,
+ std::unique_ptr<WebScrollbarThemeGeometry>) override;
+ std::unique_ptr<WebScrollbarLayer> CreateOverlayScrollbarLayer(
+ std::unique_ptr<WebScrollbar>,
+ WebScrollbarThemePainter,
+ std::unique_ptr<WebScrollbarThemeGeometry>) override;
+ std::unique_ptr<WebScrollbarLayer> CreateSolidColorScrollbarLayer(
+ WebScrollbar::Orientation,
+ int thumb_thickness,
+ int track_start,
+ bool is_left_side_vertical_scrollbar) override;
+};
+
+// A base class to override Platform methods for testing. You can override the
+// behavior by subclassing TestingPlatformSupport or using
+// ScopedTestingPlatformSupport (see below).
+class TestingPlatformSupport : public Platform {
+ public:
+ struct Config {
+ WebCompositorSupport* compositor_support = nullptr;
+ };
+
+ TestingPlatformSupport();
+ explicit TestingPlatformSupport(const Config&);
+
+ ~TestingPlatformSupport() override;
+
+ // Platform:
+ WebString DefaultLocale() override;
+ WebCompositorSupport* CompositorSupport() override;
+ WebThread* CurrentThread() override;
+ WebBlobRegistry* GetBlobRegistry() override;
+ WebClipboard* Clipboard() override;
+ WebFileUtilities* GetFileUtilities() override;
+ WebIDBFactory* IdbFactory() override;
+ WebURLLoaderMockFactory* GetURLLoaderMockFactory() override;
+ std::unique_ptr<blink::WebURLLoaderFactory> CreateDefaultURLLoaderFactory()
+ override;
+ WebData GetDataResource(const char* name) override;
+ InterfaceProvider* GetInterfaceProvider() override;
+
+ virtual void RunUntilIdle();
+
+ protected:
+ class TestingInterfaceProvider;
+
+ const Config config_;
+ Platform* const old_platform_;
+ std::unique_ptr<TestingInterfaceProvider> interface_provider_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestingPlatformSupport);
+};
+
+// ScopedTestingPlatformSupport<MyTestingPlatformSupport> can be used to
+// override Platform::current() with MyTestingPlatformSupport, like this:
+//
+// #include
+// "third_party/blink/renderer/platform/testing/testing_platform_support.h"
+//
+// TEST_F(SampleTest, sampleTest) {
+// ScopedTestingPlatformSupport<MyTestingPlatformSupport> platform;
+// ...
+// // You can call methods of MyTestingPlatformSupport.
+// EXPECT_TRUE(platform->myMethodIsCalled());
+//
+// // Another instance can be nested.
+// {
+// // Constructor's arguments can be passed like this.
+// Arg arg;
+// ScopedTestingPlatformSupport<MyAnotherTestingPlatformSupport, const Arg&>
+// another_platform(args);
+// ...
+// }
+//
+// // Here the original MyTestingPlatformSupport should be restored.
+// }
+template <class T, typename... Args>
+class ScopedTestingPlatformSupport final {
+ DISALLOW_COPY_AND_ASSIGN(ScopedTestingPlatformSupport);
+
+ public:
+ explicit ScopedTestingPlatformSupport(Args&&... args) {
+ testing_platform_support_ =
+ std::make_unique<T>(std::forward<Args>(args)...);
+ original_platform_ = Platform::Current();
+ DCHECK(original_platform_);
+ Platform::SetCurrentPlatformForTesting(testing_platform_support_.get());
+ }
+ ~ScopedTestingPlatformSupport() {
+ DCHECK_EQ(testing_platform_support_.get(), Platform::Current());
+ testing_platform_support_.reset();
+ Platform::SetCurrentPlatformForTesting(original_platform_);
+ }
+
+ const T* operator->() const { return testing_platform_support_.get(); }
+ T* operator->() { return testing_platform_support_.get(); }
+
+ T* GetTestingPlatformSupport() { return testing_platform_support_.get(); }
+
+ private:
+ std::unique_ptr<T> testing_platform_support_;
+ Platform* original_platform_;
+};
+
+class ScopedUnittestsEnvironmentSetup final {
+ DISALLOW_COPY_AND_ASSIGN(ScopedUnittestsEnvironmentSetup);
+
+ public:
+ ScopedUnittestsEnvironmentSetup(int argc, char** argv);
+ ~ScopedUnittestsEnvironmentSetup();
+
+ private:
+ class DummyPlatform;
+ class DummyRendererResourceCoordinator;
+ std::unique_ptr<base::TestDiscardableMemoryAllocator>
+ discardable_memory_allocator_;
+ std::unique_ptr<DummyPlatform> dummy_platform_;
+ std::unique_ptr<DummyRendererResourceCoordinator>
+ dummy_renderer_resource_coordinator_;
+ std::unique_ptr<cc_blink::WebCompositorSupportImpl> compositor_support_;
+ TestingPlatformSupport::Config testing_platform_config_;
+ std::unique_ptr<TestingPlatformSupport> testing_platform_support_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_TESTING_PLATFORM_SUPPORT_H_
diff --git a/chromium/third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.cc b/chromium/third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.cc
new file mode 100644
index 00000000000..00012088c9c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.cc
@@ -0,0 +1,151 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
+
+#include "base/bind.h"
+#include "components/viz/test/ordered_simple_task_runner.h"
+#include "third_party/blink/public/platform/scheduler/child/webthread_base.h"
+#include "third_party/blink/renderer/platform/scheduler/base/real_time_domain.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/waitable_event.h"
+#include "third_party/blink/renderer/platform/wtf/thread_specific.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+namespace {
+
+struct ThreadLocalStorage {
+ WebThread* current_thread = nullptr;
+};
+
+ThreadLocalStorage* GetThreadLocalStorage() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadSpecific<ThreadLocalStorage>, tls, ());
+ return tls;
+}
+
+void PrepareCurrentThread(WaitableEvent* event, WebThread* thread) {
+ GetThreadLocalStorage()->current_thread = thread;
+ event->Signal();
+}
+
+} // namespace
+
+TestingPlatformSupportWithMockScheduler::
+ TestingPlatformSupportWithMockScheduler()
+ : TestingPlatformSupportWithMockScheduler(
+ TestingPlatformSupport::Config()) {}
+
+TestingPlatformSupportWithMockScheduler::
+ TestingPlatformSupportWithMockScheduler(const Config& config)
+ : TestingPlatformSupport(config),
+ mock_task_runner_(new cc::OrderedSimpleTaskRunner(&clock_, true)) {
+ DCHECK(IsMainThread());
+ std::unique_ptr<scheduler::TaskQueueManagerForTest> task_queue_manager =
+ scheduler::TaskQueueManagerForTest::Create(nullptr, mock_task_runner_,
+ &clock_);
+ task_queue_manager_ = task_queue_manager.get();
+ scheduler_ = std::make_unique<scheduler::MainThreadSchedulerImpl>(
+ std::move(task_queue_manager), base::nullopt);
+ thread_ = scheduler_->CreateMainThread();
+ // Set the work batch size to one so RunPendingTasks behaves as expected.
+ scheduler_->GetSchedulerHelperForTesting()->SetWorkBatchSizeForTesting(1);
+
+ WTF::SetTimeFunctionsForTesting(GetTestTime);
+}
+
+TestingPlatformSupportWithMockScheduler::
+ ~TestingPlatformSupportWithMockScheduler() {
+ WTF::SetTimeFunctionsForTesting(nullptr);
+ scheduler_->Shutdown();
+}
+
+std::unique_ptr<WebThread>
+TestingPlatformSupportWithMockScheduler::CreateThread(
+ const WebThreadCreationParams& params) {
+ std::unique_ptr<scheduler::WebThreadBase> thread =
+ scheduler::WebThreadBase::CreateWorkerThread(params);
+ thread->Init();
+ WaitableEvent event;
+ thread->GetTaskRunner()->PostTask(
+ FROM_HERE, base::BindOnce(PrepareCurrentThread, base::Unretained(&event),
+ base::Unretained(thread.get())));
+ event.Wait();
+ return std::move(thread);
+}
+
+WebThread* TestingPlatformSupportWithMockScheduler::CurrentThread() {
+ DCHECK_EQ(thread_->IsCurrentThread(), IsMainThread());
+
+ if (thread_->IsCurrentThread()) {
+ return thread_.get();
+ }
+ ThreadLocalStorage* storage = GetThreadLocalStorage();
+ DCHECK(storage->current_thread);
+ return storage->current_thread;
+}
+
+void TestingPlatformSupportWithMockScheduler::RunSingleTask() {
+ mock_task_runner_->SetRunTaskLimit(1);
+ mock_task_runner_->RunPendingTasks();
+ mock_task_runner_->ClearRunTaskLimit();
+}
+
+void TestingPlatformSupportWithMockScheduler::RunUntilIdle() {
+ mock_task_runner_->RunUntilIdle();
+}
+
+void TestingPlatformSupportWithMockScheduler::RunForPeriodSeconds(
+ double seconds) {
+ const base::TimeTicks deadline =
+ clock_.NowTicks() + base::TimeDelta::FromSecondsD(seconds);
+
+ for (;;) {
+ // If we've run out of immediate work then fast forward to the next delayed
+ // task, but don't pass |deadline|.
+ if (!task_queue_manager_->HasImmediateWork()) {
+ base::TimeTicks next_delayed_task;
+ if (!task_queue_manager_->GetRealTimeDomain()->NextScheduledRunTime(
+ &next_delayed_task) ||
+ next_delayed_task > deadline) {
+ break;
+ }
+
+ clock_.SetNowTicks(next_delayed_task);
+ }
+
+ if (clock_.NowTicks() > deadline)
+ break;
+
+ mock_task_runner_->RunPendingTasks();
+ }
+
+ clock_.SetNowTicks(deadline);
+}
+
+void TestingPlatformSupportWithMockScheduler::AdvanceClockSeconds(
+ double seconds) {
+ clock_.Advance(base::TimeDelta::FromSecondsD(seconds));
+}
+
+void TestingPlatformSupportWithMockScheduler::SetAutoAdvanceNowToPendingTasks(
+ bool auto_advance) {
+ mock_task_runner_->SetAutoAdvanceNowToPendingTasks(auto_advance);
+}
+
+scheduler::MainThreadSchedulerImpl*
+TestingPlatformSupportWithMockScheduler::GetMainThreadScheduler() const {
+ return scheduler_.get();
+}
+
+// static
+double TestingPlatformSupportWithMockScheduler::GetTestTime() {
+ TestingPlatformSupportWithMockScheduler* platform =
+ static_cast<TestingPlatformSupportWithMockScheduler*>(
+ Platform::Current());
+ return (platform->clock_.NowTicks() - base::TimeTicks()).InSecondsF();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h b/chromium/third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h
new file mode 100644
index 00000000000..8b483633dd6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h
@@ -0,0 +1,79 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_TESTING_PLATFORM_SUPPORT_WITH_MOCK_SCHEDULER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_TESTING_PLATFORM_SUPPORT_WITH_MOCK_SCHEDULER_H_
+
+#include <memory>
+#include "base/test/simple_test_tick_clock.h"
+#include "third_party/blink/public/platform/web_thread.h"
+#include "third_party/blink/renderer/platform/scheduler/test/task_queue_manager_for_test.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace cc {
+class OrderedSimpleTaskRunner;
+}
+
+namespace blink {
+
+namespace scheduler {
+class MainThreadSchedulerImpl;
+}
+
+// This class adds scheduler and threading support to TestingPlatformSupport.
+// See also ScopedTestingPlatformSupport to use this class correctly.
+class TestingPlatformSupportWithMockScheduler : public TestingPlatformSupport {
+ WTF_MAKE_NONCOPYABLE(TestingPlatformSupportWithMockScheduler);
+
+ public:
+ TestingPlatformSupportWithMockScheduler();
+ explicit TestingPlatformSupportWithMockScheduler(const Config&);
+ ~TestingPlatformSupportWithMockScheduler() override;
+
+ // Platform:
+ std::unique_ptr<WebThread> CreateThread(
+ const WebThreadCreationParams&) override;
+ WebThread* CurrentThread() override;
+
+ // Runs a single task.
+ void RunSingleTask();
+
+ // Runs all currently queued immediate tasks and delayed tasks whose delay has
+ // expired plus any immediate tasks that are posted as a result of running
+ // those tasks.
+ //
+ // This function ignores future delayed tasks when deciding if the system is
+ // idle. If you need to ensure delayed tasks run, try runForPeriodSeconds()
+ // instead.
+ void RunUntilIdle() override;
+
+ // Runs for |seconds| the testing clock is advanced by |seconds|. Note real
+ // time elapsed will typically much less than |seconds| because delays between
+ // timers are fast forwarded.
+ void RunForPeriodSeconds(double seconds);
+
+ // Advances |m_clock| by |seconds|.
+ void AdvanceClockSeconds(double seconds);
+
+ scheduler::MainThreadSchedulerImpl* GetMainThreadScheduler() const;
+
+ // Controls the behavior of |m_mockTaskRunner| if true, then |m_clock| will
+ // be advanced to the next timer when there's no more immediate work to do.
+ void SetAutoAdvanceNowToPendingTasks(bool);
+
+ protected:
+ static double GetTestTime();
+
+ base::SimpleTestTickClock clock_;
+ scoped_refptr<cc::OrderedSimpleTaskRunner> mock_task_runner_;
+ std::unique_ptr<scheduler::MainThreadSchedulerImpl> scheduler_;
+ scheduler::TaskQueueManagerForTest*
+ task_queue_manager_; // Owned by scheduler_.
+ std::unique_ptr<WebThread> thread_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_TESTING_PLATFORM_SUPPORT_WITH_MOCK_SCHEDULER_H_
diff --git a/chromium/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.cc b/chromium/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.cc
new file mode 100644
index 00000000000..a86dbb70161
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.cc
@@ -0,0 +1,127 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.h"
+
+#include <memory>
+#include "third_party/blink/public/platform/web_media_stream_track.h"
+#include "third_party/blink/public/platform/web_rtc_dtmf_sender_handler.h"
+#include "third_party/blink/public/platform/web_rtc_error.h"
+#include "third_party/blink/public/platform/web_rtc_rtp_receiver.h"
+#include "third_party/blink/public/platform/web_rtc_rtp_sender.h"
+#include "third_party/blink/public/platform/web_rtc_session_description.h"
+#include "third_party/blink/public/platform/web_vector.h"
+
+namespace blink {
+
+namespace {
+
+class DummyWebRTCRtpSender : public WebRTCRtpSender {
+ private:
+ static uintptr_t last_id_;
+
+ public:
+ DummyWebRTCRtpSender() : id_(++last_id_) {}
+ ~DummyWebRTCRtpSender() override {}
+
+ uintptr_t Id() const override { return id_; }
+ WebMediaStreamTrack Track() const override { return WebMediaStreamTrack(); }
+ void ReplaceTrack(WebMediaStreamTrack, WebRTCVoidRequest) override {}
+ std::unique_ptr<WebRTCDTMFSenderHandler> GetDtmfSender() const override {
+ return nullptr;
+ }
+ std::unique_ptr<WebRTCRtpParameters> GetParameters() const override {
+ return std::unique_ptr<WebRTCRtpParameters>();
+ }
+ void GetStats(std::unique_ptr<blink::WebRTCStatsReportCallback>) override {}
+
+ private:
+ const uintptr_t id_;
+};
+
+uintptr_t DummyWebRTCRtpSender::last_id_ = 0;
+
+} // namespace
+
+MockWebRTCPeerConnectionHandler::MockWebRTCPeerConnectionHandler() = default;
+
+MockWebRTCPeerConnectionHandler::~MockWebRTCPeerConnectionHandler() = default;
+
+bool MockWebRTCPeerConnectionHandler::Initialize(const WebRTCConfiguration&,
+ const WebMediaConstraints&) {
+ return true;
+}
+
+void MockWebRTCPeerConnectionHandler::CreateOffer(
+ const WebRTCSessionDescriptionRequest&,
+ const WebMediaConstraints&) {}
+
+void MockWebRTCPeerConnectionHandler::CreateOffer(
+ const WebRTCSessionDescriptionRequest&,
+ const WebRTCOfferOptions&) {}
+
+void MockWebRTCPeerConnectionHandler::CreateAnswer(
+ const WebRTCSessionDescriptionRequest&,
+ const WebMediaConstraints&) {}
+
+void MockWebRTCPeerConnectionHandler::CreateAnswer(
+ const WebRTCSessionDescriptionRequest&,
+ const WebRTCAnswerOptions&) {}
+
+void MockWebRTCPeerConnectionHandler::SetLocalDescription(
+ const WebRTCVoidRequest&,
+ const WebRTCSessionDescription&) {}
+
+void MockWebRTCPeerConnectionHandler::SetRemoteDescription(
+ const WebRTCVoidRequest&,
+ const WebRTCSessionDescription&) {}
+
+WebRTCSessionDescription MockWebRTCPeerConnectionHandler::LocalDescription() {
+ return WebRTCSessionDescription();
+}
+
+WebRTCSessionDescription MockWebRTCPeerConnectionHandler::RemoteDescription() {
+ return WebRTCSessionDescription();
+}
+
+WebRTCErrorType MockWebRTCPeerConnectionHandler::SetConfiguration(
+ const WebRTCConfiguration&) {
+ return WebRTCErrorType::kNone;
+}
+
+void MockWebRTCPeerConnectionHandler::GetStats(const WebRTCStatsRequest&) {}
+
+void MockWebRTCPeerConnectionHandler::GetStats(
+ std::unique_ptr<WebRTCStatsReportCallback>) {}
+
+std::unique_ptr<WebRTCRtpSender> MockWebRTCPeerConnectionHandler::AddTrack(
+ const WebMediaStreamTrack&,
+ const WebVector<WebMediaStream>&) {
+ return std::make_unique<DummyWebRTCRtpSender>();
+}
+
+bool MockWebRTCPeerConnectionHandler::RemoveTrack(WebRTCRtpSender*) {
+ return true;
+}
+
+WebRTCDataChannelHandler* MockWebRTCPeerConnectionHandler::CreateDataChannel(
+ const WebString& label,
+ const WebRTCDataChannelInit&) {
+ return nullptr;
+}
+
+void MockWebRTCPeerConnectionHandler::Stop() {}
+
+WebString MockWebRTCPeerConnectionHandler::Id() const {
+ return WebString();
+}
+
+std::unique_ptr<WebRTCPeerConnectionHandler>
+TestingPlatformSupportWithWebRTC::CreateRTCPeerConnectionHandler(
+ WebRTCPeerConnectionHandlerClient*,
+ scoped_refptr<base::SingleThreadTaskRunner>) {
+ return std::make_unique<MockWebRTCPeerConnectionHandler>();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.h b/chromium/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.h
new file mode 100644
index 00000000000..f5bed12aefd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.h
@@ -0,0 +1,58 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_TESTING_PLATFORM_SUPPORT_WITH_WEB_RTC_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_TESTING_PLATFORM_SUPPORT_WITH_WEB_RTC_H_
+
+#include "third_party/blink/public/platform/web_rtc_peer_connection_handler.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
+
+namespace blink {
+
+class MockWebRTCPeerConnectionHandler : public WebRTCPeerConnectionHandler {
+ public:
+ MockWebRTCPeerConnectionHandler();
+ ~MockWebRTCPeerConnectionHandler() override;
+
+ bool Initialize(const WebRTCConfiguration&,
+ const WebMediaConstraints&) override;
+
+ void CreateOffer(const WebRTCSessionDescriptionRequest&,
+ const WebMediaConstraints&) override;
+ void CreateOffer(const WebRTCSessionDescriptionRequest&,
+ const WebRTCOfferOptions&) override;
+ void CreateAnswer(const WebRTCSessionDescriptionRequest&,
+ const WebMediaConstraints&) override;
+ void CreateAnswer(const WebRTCSessionDescriptionRequest&,
+ const WebRTCAnswerOptions&) override;
+ void SetLocalDescription(const WebRTCVoidRequest&,
+ const WebRTCSessionDescription&) override;
+ void SetRemoteDescription(const WebRTCVoidRequest&,
+ const WebRTCSessionDescription&) override;
+ WebRTCSessionDescription LocalDescription() override;
+ WebRTCSessionDescription RemoteDescription() override;
+ WebRTCErrorType SetConfiguration(const WebRTCConfiguration&) override;
+ void GetStats(const WebRTCStatsRequest&) override;
+ void GetStats(std::unique_ptr<WebRTCStatsReportCallback>) override;
+ std::unique_ptr<WebRTCRtpSender> AddTrack(
+ const WebMediaStreamTrack&,
+ const WebVector<WebMediaStream>&) override;
+ bool RemoveTrack(WebRTCRtpSender*) override;
+ WebRTCDataChannelHandler* CreateDataChannel(
+ const WebString& label,
+ const WebRTCDataChannelInit&) override;
+ void Stop() override;
+ WebString Id() const override;
+};
+
+class TestingPlatformSupportWithWebRTC : public TestingPlatformSupport {
+ public:
+ std::unique_ptr<WebRTCPeerConnectionHandler> CreateRTCPeerConnectionHandler(
+ WebRTCPeerConnectionHandlerClient*,
+ scoped_refptr<base::SingleThreadTaskRunner>) override;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_TESTING_PLATFORM_SUPPORT_WITH_WEB_RTC_H_
diff --git a/chromium/third_party/blink/renderer/platform/testing/tree_test_helpers.cc b/chromium/third_party/blink/renderer/platform/testing/tree_test_helpers.cc
new file mode 100644
index 00000000000..b3c7089d0a4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/tree_test_helpers.cc
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/testing/tree_test_helpers.h"
+
+#include <cstdlib>
+
+namespace blink {
+namespace TreeTestHelpers {
+
+void InitRandom(const int32_t seed) {
+ srand(seed);
+}
+
+int32_t NextRandom(const int32_t maximum_value) {
+ // rand_r is not available on Windows
+ return rand() % maximum_value;
+}
+
+} // namespace TreeTestHelpers
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/testing/tree_test_helpers.h b/chromium/third_party/blink/renderer/platform/testing/tree_test_helpers.h
new file mode 100644
index 00000000000..0192b08e71b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/tree_test_helpers.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2010 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:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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.
+ */
+
+// Simple pseudorandom number generator helper functions, used by the
+// red-black and interval tree tests.
+//
+// These are **not** thread safe!
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_TREE_TEST_HELPERS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_TREE_TEST_HELPERS_H_
+
+#include <stdint.h>
+
+namespace blink {
+namespace TreeTestHelpers {
+
+// Initializes the pseudo-random number generator with a specific seed.
+void InitRandom(const int32_t seed);
+
+// Produces the next pseudo-random number in the sequence, in the
+// range from [0..maximumValue). Negative numbers are not allowed and will
+// produce undefined results.
+int32_t NextRandom(const int32_t maximum_value);
+
+} // namespace TreeTestHelpers
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_TREE_TEST_HELPERS_H_
diff --git a/chromium/third_party/blink/renderer/platform/testing/unit_test_helpers.cc b/chromium/third_party/blink/renderer/platform/testing/unit_test_helpers.cc
new file mode 100644
index 00000000000..60235e9d3df
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/unit_test_helpers.cc
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2013 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:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * (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 "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/location.h"
+#include "base/path_service.h"
+#include "base/run_loop.h"
+#include "third_party/blink/public/platform/file_path_conversion.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/platform/web_thread.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
+
+namespace blink {
+namespace test {
+
+namespace {
+
+base::FilePath BlinkRootFilePath() {
+ base::FilePath path;
+ base::PathService::Get(base::DIR_SOURCE_ROOT, &path);
+ return base::MakeAbsoluteFilePath(
+ path.Append(FILE_PATH_LITERAL("third_party/blink")));
+}
+
+base::FilePath LayoutTestsFilePath() {
+ base::FilePath path;
+ base::PathService::Get(base::DIR_SOURCE_ROOT, &path);
+ return base::MakeAbsoluteFilePath(
+ path.Append(FILE_PATH_LITERAL("third_party/WebKit/LayoutTests")));
+}
+
+} // namespace
+
+void RunPendingTasks() {
+ Platform::Current()->CurrentThread()->GetTaskRunner()->PostTask(
+ FROM_HERE, WTF::Bind(&ExitRunLoop));
+
+ // We forbid GC in the tasks. Otherwise the registered GCTaskObserver tries
+ // to run GC with NoHeapPointerOnStack.
+ ThreadState::Current()->EnterGCForbiddenScope();
+ EnterRunLoop();
+ ThreadState::Current()->LeaveGCForbiddenScope();
+}
+
+void RunDelayedTasks(TimeDelta delay) {
+ Platform::Current()->CurrentThread()->GetTaskRunner()->PostDelayedTask(
+ FROM_HERE, WTF::Bind(&ExitRunLoop), delay);
+ EnterRunLoop();
+}
+
+void EnterRunLoop() {
+ base::RunLoop().Run();
+}
+
+void ExitRunLoop() {
+ base::RunLoop::QuitCurrentWhenIdleDeprecated();
+}
+
+void YieldCurrentThread() {
+ base::PlatformThread::YieldCurrentThread();
+}
+
+String BlinkRootDir() {
+ return FilePathToWebString(BlinkRootFilePath());
+}
+
+String BlinkLayoutTestsDir() {
+ return FilePathToWebString(LayoutTestsFilePath());
+}
+
+String ExecutableDir() {
+ base::FilePath path;
+ base::PathService::Get(base::DIR_EXE, &path);
+ return FilePathToWebString(base::MakeAbsoluteFilePath(path));
+}
+
+String CoreTestDataPath(const String& relative_path) {
+ return FilePathToWebString(
+ BlinkRootFilePath()
+ .Append(FILE_PATH_LITERAL("renderer/core/testing/data"))
+ .Append(WebStringToFilePath(relative_path)));
+}
+
+String PlatformTestDataPath(const String& relative_path) {
+ return FilePathToWebString(
+ BlinkRootFilePath()
+ .Append(FILE_PATH_LITERAL("renderer/platform/testing/data"))
+ .Append(WebStringToFilePath(relative_path)));
+}
+
+scoped_refptr<SharedBuffer> ReadFromFile(const String& path) {
+ base::FilePath file_path = blink::WebStringToFilePath(path);
+ std::string buffer;
+ base::ReadFileToString(file_path, &buffer);
+ return SharedBuffer::Create(buffer.data(), buffer.size());
+}
+
+LineReader::LineReader(const std::string& text) : text_(text), index_(0) {}
+
+bool LineReader::GetNextLine(std::string* line) {
+ line->clear();
+ if (index_ >= text_.length())
+ return false;
+
+ size_t end_of_line_index = text_.find("\r\n", index_);
+ if (end_of_line_index == std::string::npos) {
+ *line = text_.substr(index_);
+ index_ = text_.length();
+ return true;
+ }
+
+ *line = text_.substr(index_, end_of_line_index - index_);
+ index_ = end_of_line_index + 2;
+ return true;
+}
+
+} // namespace test
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/testing/unit_test_helpers.h b/chromium/third_party/blink/renderer/platform/testing/unit_test_helpers.h
new file mode 100644
index 00000000000..e3fd7b9a369
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/unit_test_helpers.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2013 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:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_UNIT_TEST_HELPERS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_UNIT_TEST_HELPERS_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/timer.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class SharedBuffer;
+
+namespace test {
+
+// Note: You may want to use TestingPlatformSupportWithMockScheduler to
+// provides runUntilIdle() method that can work with WebURLLoaderMockFactory.
+void RunPendingTasks();
+
+// Waits for delayed task to complete or timers to fire for |delay|.
+void RunDelayedTasks(TimeDelta delay);
+
+void EnterRunLoop();
+void ExitRunLoop();
+
+void YieldCurrentThread();
+
+// Returns Blink top directory as an absolute path, e.g.
+// /src/third_party/blink.
+String BlinkRootDir();
+
+// Returns Blink LayoutTests directory as an absolute path, e.g.
+// /src/third_party/WebKit/LayoutTests.
+String BlinkLayoutTestsDir();
+
+// Returns directory containing the current executable as absolute path.
+String ExecutableDir();
+
+// Returns test data absolute path for webkit_unit_tests in core, i.e.
+// <blinkRootDir>/Source/core/testing/data/<relativePath>.
+// It returns the top web test directory if |relativePath| was not specified.
+String CoreTestDataPath(const String& relative_path = String());
+
+// Returns test data absolute path for blink_platform_unittests, i.e.
+// <blinkRootDir>/Source/platform/testing/data/<relativePath>.
+// It returns the top platform test directory if |relativePath| was not
+// specified.
+String PlatformTestDataPath(const String& relative_path = String());
+
+scoped_refptr<SharedBuffer> ReadFromFile(const String& path);
+
+class LineReader {
+ public:
+ LineReader(const std::string& text);
+ bool GetNextLine(std::string* line);
+
+ private:
+ std::string text_;
+ size_t index_;
+};
+
+} // namespace test
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/testing/url_test_helpers.cc b/chromium/third_party/blink/renderer/platform/testing/url_test_helpers.cc
new file mode 100644
index 00000000000..33ec9064890
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/url_test_helpers.cc
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2012 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 "third_party/blink/renderer/platform/testing/url_test_helpers.h"
+
+#include <string>
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "third_party/blink/public/platform/file_path_conversion.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_url_error.h"
+#include "third_party/blink/public/platform/web_url_load_timing.h"
+#include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loader.h"
+#include "third_party/blink/renderer/platform/network/http_names.h"
+#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
+
+namespace blink {
+namespace URLTestHelpers {
+
+WebURL RegisterMockedURLLoadFromBase(const WebString& base_url,
+ const WebString& base_path,
+ const WebString& file_name,
+ const WebString& mime_type) {
+ // fullURL = baseURL + fileName.
+ std::string full_url = std::string(base_url.Utf8().data()) +
+ std::string(file_name.Utf8().data());
+
+ // filePath = basePath + ("/" +) fileName.
+ base::FilePath file_path =
+ WebStringToFilePath(base_path).Append(WebStringToFilePath(file_name));
+
+ KURL url = ToKURL(full_url);
+ RegisterMockedURLLoad(url, FilePathToWebString(file_path), mime_type);
+ return WebURL(url);
+}
+
+void RegisterMockedURLLoad(const WebURL& full_url,
+ const WebString& file_path,
+ const WebString& mime_type) {
+ WebURLLoadTiming timing;
+ timing.Initialize();
+
+ WebURLResponse response(full_url);
+ response.SetMIMEType(mime_type);
+ response.SetHTTPHeaderField(HTTPNames::Content_Type, mime_type);
+ response.SetHTTPStatusCode(200);
+ response.SetLoadTiming(timing);
+
+ RegisterMockedURLLoadWithCustomResponse(full_url, file_path, response);
+}
+
+void RegisterMockedErrorURLLoad(const WebURL& full_url) {
+ WebURLLoadTiming timing;
+ timing.Initialize();
+
+ WebURLResponse response;
+ response.SetMIMEType("image/png");
+ response.SetHTTPHeaderField(HTTPNames::Content_Type, "image/png");
+ response.SetHTTPStatusCode(404);
+ response.SetLoadTiming(timing);
+
+ ResourceError error = ResourceError::Failure(full_url);
+ Platform::Current()->GetURLLoaderMockFactory()->RegisterErrorURL(
+ full_url, response, error);
+}
+
+void RegisterMockedURLLoadWithCustomResponse(const WebURL& full_url,
+ const WebString& file_path,
+ WebURLResponse response) {
+ Platform::Current()->GetURLLoaderMockFactory()->RegisterURL(
+ full_url, response, file_path);
+}
+
+void RegisterMockedURLUnregister(const WebURL& url) {
+ Platform::Current()->GetURLLoaderMockFactory()->UnregisterURL(url);
+}
+
+} // namespace URLTestHelpers
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/testing/url_test_helpers.h b/chromium/third_party/blink/renderer/platform/testing/url_test_helpers.h
new file mode 100644
index 00000000000..051339d7c07
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/url_test_helpers.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2012 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_URL_TEST_HELPERS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_URL_TEST_HELPERS_H_
+
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/platform/web_url.h"
+#include "third_party/blink/public/platform/web_url_response.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+
+namespace blink {
+namespace URLTestHelpers {
+
+inline blink::KURL ToKURL(const std::string& url) {
+ WTF::String wtf_string(url.c_str());
+ return blink::KURL(wtf_string);
+}
+
+// Avoid directly using these methods, instead please use ScopedMockedURL (or
+// define a variant of ScopedMockedURL classes if necessary) in new code.
+//
+// Helper functions for mock URLs. These functions set up the desired URL and
+// mimeType, with a 200 OK return status.
+// webTestDataPath() or platformTestDataPath() in UnitTestHelpers can be used to
+// get the appropriate |basePath| and |filePath| for test data directories.
+// - For the mock URL, fullURL == baseURL + fileName.
+// - For the file path, filePath == basePath + ("/" +) fileName.
+//
+// TODO(kinuko,toyoshim): These helpers should take URLLoaderMockFactory as a
+// parameter, or maybe have static {Set,Get}URLLoaderMockFactory() methods
+// so that we can deprecate the platform's GetURLLoaderMockFactory().
+// (crbug.com/751425)
+
+// Registers from a base URL and a base file path, and returns a calculated full
+// URL.
+WebURL RegisterMockedURLLoadFromBase(
+ const WebString& base_url,
+ const WebString& base_path,
+ const WebString& file_name,
+ const WebString& mime_type = WebString::FromUTF8("text/html"));
+
+// Registers from a full URL and a full file path.
+void RegisterMockedURLLoad(
+ const WebURL& full_url,
+ const WebString& file_path,
+ const WebString& mime_type = WebString::FromUTF8("text/html"));
+
+// Unregisters a URL that has been registered, so that the same URL can be
+// registered again from the another test.
+void RegisterMockedURLUnregister(const WebURL&);
+
+// Registers with a custom response.
+void RegisterMockedURLLoadWithCustomResponse(const WebURL& full_url,
+ const WebString& file_path,
+ WebURLResponse);
+
+// Registers a mock URL that returns a 404 error.
+void RegisterMockedErrorURLLoad(const WebURL& full_url);
+
+} // namespace URLTestHelpers
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/testing/use_mock_scrollbar_settings.h b/chromium/third_party/blink/renderer/platform/testing/use_mock_scrollbar_settings.h
new file mode 100644
index 00000000000..64cb943a027
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/use_mock_scrollbar_settings.h
@@ -0,0 +1,51 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_USE_MOCK_SCROLLBAR_SETTINGS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_USE_MOCK_SCROLLBAR_SETTINGS_H_
+
+#include "third_party/blink/renderer/platform/scroll/scrollbar_theme.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+
+namespace blink {
+
+// Forces to use mocked overlay scrollbars instead of the default native theme
+// scrollbars to avoid crash in Chromium code when it tries to load UI
+// resources that are not available when running blink unit tests, and to
+// ensure consistent layout regardless of differences between scrollbar themes.
+// WebViewHelper includes this, so this is only needed if a test doesn't use
+// WebViewHelper or the test needs a bigger scope of mock scrollbar settings
+// than the scope of WebViewHelper.
+class UseMockScrollbarSettings : private ScopedOverlayScrollbarsForTest {
+ public:
+ UseMockScrollbarSettings()
+ : ScopedOverlayScrollbarsForTest(true),
+ original_mock_scrollbar_enabled_(
+ ScrollbarTheme::MockScrollbarsEnabled()),
+ original_overlay_scrollbars_enabled_(
+ RuntimeEnabledFeatures::OverlayScrollbarsEnabled()) {
+ ScrollbarTheme::SetMockScrollbarsEnabled(true);
+ }
+
+ UseMockScrollbarSettings(bool use_mock, bool use_overlay)
+ : ScopedOverlayScrollbarsForTest(use_overlay),
+ original_mock_scrollbar_enabled_(
+ ScrollbarTheme::MockScrollbarsEnabled()),
+ original_overlay_scrollbars_enabled_(
+ RuntimeEnabledFeatures::OverlayScrollbarsEnabled()) {
+ ScrollbarTheme::SetMockScrollbarsEnabled(use_mock);
+ }
+
+ ~UseMockScrollbarSettings() {
+ ScrollbarTheme::SetMockScrollbarsEnabled(original_mock_scrollbar_enabled_);
+ }
+
+ private:
+ bool original_mock_scrollbar_enabled_;
+ bool original_overlay_scrollbars_enabled_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_USE_MOCK_SCROLLBAR_SETTINGS_H_
diff --git a/chromium/third_party/blink/renderer/platform/testing/web_layer_tree_view_impl_for_testing.cc b/chromium/third_party/blink/renderer/platform/testing/web_layer_tree_view_impl_for_testing.cc
new file mode 100644
index 00000000000..7247f59aa6b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/web_layer_tree_view_impl_for_testing.cc
@@ -0,0 +1,211 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/testing/web_layer_tree_view_impl_for_testing.h"
+
+#include "base/threading/thread_task_runner_handle.h"
+#include "cc/animation/animation_host.h"
+#include "cc/animation/animation_timeline.h"
+#include "cc/blink/web_layer_impl.h"
+#include "cc/layers/layer.h"
+#include "cc/trees/layer_tree_host.h"
+#include "cc/trees/layer_tree_settings.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_layer.h"
+#include "third_party/blink/public/platform/web_layer_tree_view.h"
+#include "third_party/blink/public/platform/web_size.h"
+
+namespace blink {
+
+WebLayerTreeViewImplForTesting::WebLayerTreeViewImplForTesting()
+ : WebLayerTreeViewImplForTesting(DefaultLayerTreeSettings()) {}
+
+WebLayerTreeViewImplForTesting::WebLayerTreeViewImplForTesting(
+ const cc::LayerTreeSettings& settings) {
+ animation_host_ = cc::AnimationHost::CreateMainInstance();
+ cc::LayerTreeHost::InitParams params;
+ params.client = this;
+ params.settings = &settings;
+ params.main_task_runner = base::ThreadTaskRunnerHandle::Get();
+ params.task_graph_runner = &task_graph_runner_;
+ params.mutator_host = animation_host_.get();
+ layer_tree_host_ = cc::LayerTreeHost::CreateSingleThreaded(this, &params);
+ DCHECK(layer_tree_host_);
+}
+
+WebLayerTreeViewImplForTesting::~WebLayerTreeViewImplForTesting() = default;
+
+// static
+cc::LayerTreeSettings
+WebLayerTreeViewImplForTesting::DefaultLayerTreeSettings() {
+ cc::LayerTreeSettings settings;
+
+ // For web contents, layer transforms should scale up the contents of layers
+ // to keep content always crisp when possible.
+ settings.layer_transforms_should_scale_layer_contents = true;
+
+ return settings;
+}
+
+bool WebLayerTreeViewImplForTesting::HasLayer(const WebLayer& layer) {
+ return layer.CcLayer()->GetLayerTreeHostForTesting() ==
+ layer_tree_host_.get();
+}
+
+void WebLayerTreeViewImplForTesting::SetViewportSize(
+ const WebSize& device_viewport_size) {
+ gfx::Size gfx_size(std::max(0, device_viewport_size.width),
+ std::max(0, device_viewport_size.height));
+ // TODO(ccameron): This likely causes surface invariant violations.
+ layer_tree_host_->SetViewportSizeAndScale(
+ gfx_size, layer_tree_host_->device_scale_factor(),
+ layer_tree_host_->local_surface_id());
+}
+
+void WebLayerTreeViewImplForTesting::SetRootLayer(const blink::WebLayer& root) {
+ layer_tree_host_->SetRootLayer(
+ static_cast<const cc_blink::WebLayerImpl*>(&root)->layer());
+}
+
+void WebLayerTreeViewImplForTesting::ClearRootLayer() {
+ layer_tree_host_->SetRootLayer(scoped_refptr<cc::Layer>());
+}
+
+cc::AnimationHost* WebLayerTreeViewImplForTesting::CompositorAnimationHost() {
+ return animation_host_.get();
+}
+
+WebSize WebLayerTreeViewImplForTesting::GetViewportSize() const {
+ return WebSize(layer_tree_host_->device_viewport_size().width(),
+ layer_tree_host_->device_viewport_size().height());
+}
+
+void WebLayerTreeViewImplForTesting::SetBackgroundColor(WebColor color) {
+ layer_tree_host_->set_background_color(color);
+}
+
+void WebLayerTreeViewImplForTesting::SetVisible(bool visible) {
+ layer_tree_host_->SetVisible(visible);
+}
+
+void WebLayerTreeViewImplForTesting::SetPageScaleFactorAndLimits(
+ float page_scale_factor,
+ float minimum,
+ float maximum) {
+ layer_tree_host_->SetPageScaleFactorAndLimits(page_scale_factor, minimum,
+ maximum);
+}
+
+void WebLayerTreeViewImplForTesting::StartPageScaleAnimation(
+ const blink::WebPoint& scroll,
+ bool use_anchor,
+ float new_page_scale,
+ double duration_sec) {}
+
+void WebLayerTreeViewImplForTesting::SetNeedsBeginFrame() {
+ layer_tree_host_->SetNeedsAnimate();
+}
+
+void WebLayerTreeViewImplForTesting::DidStopFlinging() {}
+
+void WebLayerTreeViewImplForTesting::SetDeferCommits(bool defer_commits) {
+ layer_tree_host_->SetDeferCommits(defer_commits);
+}
+
+void WebLayerTreeViewImplForTesting::UpdateLayerTreeHost(
+ VisualStateUpdate requested_update) {}
+
+void WebLayerTreeViewImplForTesting::ApplyViewportDeltas(
+ const gfx::Vector2dF& inner_delta,
+ const gfx::Vector2dF& outer_delta,
+ const gfx::Vector2dF& elastic_overscroll_delta,
+ float page_scale,
+ float browser_controls_delta) {}
+
+void WebLayerTreeViewImplForTesting::RecordWheelAndTouchScrollingCount(
+ bool has_scrolled_by_wheel,
+ bool has_scrolled_by_touch) {}
+
+void WebLayerTreeViewImplForTesting::RequestNewLayerTreeFrameSink() {
+ // Intentionally do not create and set a LayerTreeFrameSink.
+}
+
+void WebLayerTreeViewImplForTesting::DidFailToInitializeLayerTreeFrameSink() {
+ NOTREACHED();
+}
+
+void WebLayerTreeViewImplForTesting::RegisterViewportLayers(
+ const WebLayerTreeView::ViewportLayers& layers) {
+ cc::LayerTreeHost::ViewportLayers viewport_layers;
+ if (layers.overscroll_elasticity) {
+ viewport_layers.overscroll_elasticity =
+ static_cast<const cc_blink::WebLayerImpl*>(layers.overscroll_elasticity)
+ ->layer();
+ }
+ viewport_layers.page_scale =
+ static_cast<const cc_blink::WebLayerImpl*>(layers.page_scale)->layer();
+ if (layers.inner_viewport_container) {
+ viewport_layers.inner_viewport_container =
+ static_cast<const cc_blink::WebLayerImpl*>(
+ layers.inner_viewport_container)
+ ->layer();
+ }
+ if (layers.outer_viewport_container) {
+ viewport_layers.outer_viewport_container =
+ static_cast<const cc_blink::WebLayerImpl*>(
+ layers.outer_viewport_container)
+ ->layer();
+ }
+ viewport_layers.inner_viewport_scroll =
+ static_cast<const cc_blink::WebLayerImpl*>(layers.inner_viewport_scroll)
+ ->layer();
+ if (layers.outer_viewport_scroll) {
+ viewport_layers.outer_viewport_scroll =
+ static_cast<const cc_blink::WebLayerImpl*>(layers.outer_viewport_scroll)
+ ->layer();
+ }
+ layer_tree_host_->RegisterViewportLayers(viewport_layers);
+}
+
+void WebLayerTreeViewImplForTesting::ClearViewportLayers() {
+ layer_tree_host_->RegisterViewportLayers(cc::LayerTreeHost::ViewportLayers());
+}
+
+void WebLayerTreeViewImplForTesting::RegisterSelection(
+ const blink::WebSelection& selection) {}
+
+void WebLayerTreeViewImplForTesting::ClearSelection() {}
+
+void WebLayerTreeViewImplForTesting::SetEventListenerProperties(
+ blink::WebEventListenerClass event_class,
+ blink::WebEventListenerProperties properties) {
+ // Equality of static_cast is checked in render_widget_compositor.cc.
+ layer_tree_host_->SetEventListenerProperties(
+ static_cast<cc::EventListenerClass>(event_class),
+ static_cast<cc::EventListenerProperties>(properties));
+}
+
+blink::WebEventListenerProperties
+WebLayerTreeViewImplForTesting::EventListenerProperties(
+ blink::WebEventListenerClass event_class) const {
+ // Equality of static_cast is checked in render_widget_compositor.cc.
+ return static_cast<blink::WebEventListenerProperties>(
+ layer_tree_host_->event_listener_properties(
+ static_cast<cc::EventListenerClass>(event_class)));
+}
+
+void WebLayerTreeViewImplForTesting::SetHaveScrollEventHandlers(
+ bool have_eent_handlers) {
+ layer_tree_host_->SetHaveScrollEventHandlers(have_eent_handlers);
+}
+
+bool WebLayerTreeViewImplForTesting::HaveScrollEventHandlers() const {
+ return layer_tree_host_->have_scroll_event_handlers();
+}
+
+bool WebLayerTreeViewImplForTesting::IsForSubframe() {
+ return false;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/testing/web_layer_tree_view_impl_for_testing.h b/chromium/third_party/blink/renderer/platform/testing/web_layer_tree_view_impl_for_testing.h
new file mode 100644
index 00000000000..a715c170415
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/web_layer_tree_view_impl_for_testing.h
@@ -0,0 +1,107 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_WEB_LAYER_TREE_VIEW_IMPL_FOR_TESTING_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_WEB_LAYER_TREE_VIEW_IMPL_FOR_TESTING_H_
+
+#include <memory>
+#include "cc/test/test_task_graph_runner.h"
+#include "cc/trees/layer_tree_host_client.h"
+#include "cc/trees/layer_tree_host_single_thread_client.h"
+#include "third_party/blink/public/platform/web_layer_tree_view.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace cc {
+class AnimationHost;
+class LayerTreeHost;
+class LayerTreeSettings;
+}
+
+namespace blink {
+
+class WebLayer;
+
+// Dummy WeblayerTeeView that does not support any actual compositing.
+class WebLayerTreeViewImplForTesting
+ : public blink::WebLayerTreeView,
+ public cc::LayerTreeHostClient,
+ public cc::LayerTreeHostSingleThreadClient {
+ WTF_MAKE_NONCOPYABLE(WebLayerTreeViewImplForTesting);
+
+ public:
+ WebLayerTreeViewImplForTesting();
+ explicit WebLayerTreeViewImplForTesting(const cc::LayerTreeSettings&);
+ ~WebLayerTreeViewImplForTesting() override;
+
+ static cc::LayerTreeSettings DefaultLayerTreeSettings();
+ cc::LayerTreeHost* GetLayerTreeHost() { return layer_tree_host_.get(); }
+ bool HasLayer(const WebLayer&);
+ void SetViewportSize(const blink::WebSize&);
+
+ // blink::WebLayerTreeView implementation.
+ void SetRootLayer(const blink::WebLayer&) override;
+ void ClearRootLayer() override;
+ cc::AnimationHost* CompositorAnimationHost() override;
+ WebSize GetViewportSize() const override;
+ void SetBackgroundColor(blink::WebColor) override;
+ void SetVisible(bool) override;
+ void SetPageScaleFactorAndLimits(float page_scale_factor,
+ float minimum,
+ float maximum) override;
+ void StartPageScaleAnimation(const blink::WebPoint& destination,
+ bool use_anchor,
+ float new_page_scale,
+ double duration_sec) override;
+ void SetNeedsBeginFrame() override;
+ void DidStopFlinging() override;
+ void SetDeferCommits(bool) override;
+ void RegisterViewportLayers(const WebLayerTreeView::ViewportLayers&) override;
+ void ClearViewportLayers() override;
+ void RegisterSelection(const blink::WebSelection&) override;
+ void ClearSelection() override;
+ void SetEventListenerProperties(blink::WebEventListenerClass event_class,
+ blink::WebEventListenerProperties) override;
+ blink::WebEventListenerProperties EventListenerProperties(
+ blink::WebEventListenerClass event_class) const override;
+ void SetHaveScrollEventHandlers(bool) override;
+ bool HaveScrollEventHandlers() const override;
+
+ // cc::LayerTreeHostClient implementation.
+ void WillBeginMainFrame() override {}
+ void DidBeginMainFrame() override {}
+ void BeginMainFrame(const viz::BeginFrameArgs& args) override {}
+ void BeginMainFrameNotExpectedSoon() override {}
+ void BeginMainFrameNotExpectedUntil(base::TimeTicks) override {}
+ void UpdateLayerTreeHost(VisualStateUpdate requested_update) override;
+ void ApplyViewportDeltas(const gfx::Vector2dF& inner_delta,
+ const gfx::Vector2dF& outer_delta,
+ const gfx::Vector2dF& elastic_overscroll_delta,
+ float page_scale,
+ float browser_controls_delta) override;
+ void RecordWheelAndTouchScrollingCount(bool has_scrolled_by_wheel,
+ bool has_scrolled_by_touch) override;
+ void RequestNewLayerTreeFrameSink() override;
+ void DidInitializeLayerTreeFrameSink() override {}
+ void DidFailToInitializeLayerTreeFrameSink() override;
+ void WillCommit() override {}
+ void DidCommit() override {}
+ void DidCommitAndDrawFrame() override {}
+ void DidReceiveCompositorFrameAck() override {}
+ void DidCompletePageScaleAnimation() override {}
+
+ bool IsForSubframe() override;
+
+ // cc::LayerTreeHostSingleThreadClient implementation.
+ void DidSubmitCompositorFrame() override {}
+ void DidLoseLayerTreeFrameSink() override {}
+
+ private:
+ cc::TestTaskGraphRunner task_graph_runner_;
+ std::unique_ptr<cc::AnimationHost> animation_host_;
+ std::unique_ptr<cc::LayerTreeHost> layer_tree_host_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_WEB_LAYER_TREE_VIEW_IMPL_FOR_TESTING_H_
diff --git a/chromium/third_party/blink/renderer/platform/testing/weburl_loader_mock.cc b/chromium/third_party/blink/renderer/platform/testing/weburl_loader_mock.cc
new file mode 100644
index 00000000000..ae4385607d2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/weburl_loader_mock.cc
@@ -0,0 +1,171 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/testing/weburl_loader_mock.h"
+
+#include <utility>
+
+#include "third_party/blink/public/platform/url_conversion.h"
+#include "third_party/blink/public/platform/web_data.h"
+#include "third_party/blink/public/platform/web_url_error.h"
+#include "third_party/blink/public/platform/web_url_loader_client.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/testing/weburl_loader_mock_factory_impl.h"
+
+namespace blink {
+
+namespace {
+
+void AssertFallbackLoaderAvailability(const WebURL& url,
+ const WebURLLoader* default_loader) {
+ DCHECK(KURL(url).ProtocolIsData())
+ << "shouldn't be falling back: " << url.GetString().Utf8();
+ DCHECK(default_loader) << "default_loader wasn't set: "
+ << url.GetString().Utf8();
+}
+
+} // namespace
+
+WebURLLoaderMock::WebURLLoaderMock(WebURLLoaderMockFactoryImpl* factory,
+ std::unique_ptr<WebURLLoader> default_loader)
+ : factory_(factory),
+ default_loader_(std::move(default_loader)),
+ weak_factory_(this) {}
+
+WebURLLoaderMock::~WebURLLoaderMock() {
+ Cancel();
+}
+
+void WebURLLoaderMock::ServeAsynchronousRequest(
+ WebURLLoaderTestDelegate* delegate,
+ const WebURLResponse& response,
+ const WebData& data,
+ const Optional<WebURLError>& error) {
+ DCHECK(!using_default_loader_);
+ if (!client_)
+ return;
+
+ // If no delegate is provided then create an empty one. The default behavior
+ // will just proxy to the client.
+ std::unique_ptr<WebURLLoaderTestDelegate> default_delegate;
+ if (!delegate) {
+ default_delegate = std::make_unique<WebURLLoaderTestDelegate>();
+ delegate = default_delegate.get();
+ }
+
+ // didReceiveResponse() and didReceiveData() might end up getting ::cancel()
+ // to be called which will make the ResourceLoader to delete |this|.
+ base::WeakPtr<WebURLLoaderMock> self = weak_factory_.GetWeakPtr();
+
+ delegate->DidReceiveResponse(client_, response);
+ if (!self)
+ return;
+
+ if (error) {
+ delegate->DidFail(client_, *error, data.size(), 0, 0);
+ return;
+ }
+
+ data.ForEachSegment([this, &delegate, &self](const char* segment,
+ size_t segment_size,
+ size_t segment_offset) {
+ delegate->DidReceiveData(client_, segment, segment_size);
+ // DidReceiveData() may clear the |self| weak ptr. We stop iterating
+ // when that happens.
+ return self;
+ });
+
+ if (!self)
+ return;
+
+ delegate->DidFinishLoading(client_, 0, data.size(), data.size(), data.size());
+}
+
+WebURL WebURLLoaderMock::ServeRedirect(
+ const WebURLRequest& request,
+ const WebURLResponse& redirect_response) {
+ KURL redirect_url(redirect_response.HttpHeaderField("Location"));
+
+ base::WeakPtr<WebURLLoaderMock> self = weak_factory_.GetWeakPtr();
+
+ bool report_raw_headers = false;
+ bool follow = client_->WillFollowRedirect(
+ redirect_url, redirect_url, WebString(), kWebReferrerPolicyDefault,
+ request.HttpMethod(), redirect_response, report_raw_headers);
+ // |this| might be deleted in willFollowRedirect().
+ if (!self)
+ return redirect_url;
+
+ if (!follow)
+ Cancel();
+
+ return redirect_url;
+}
+
+void WebURLLoaderMock::LoadSynchronously(
+ const WebURLRequest& request,
+ WebURLResponse& response,
+ base::Optional<WebURLError>& error,
+ WebData& data,
+ int64_t& encoded_data_length,
+ int64_t& encoded_body_length,
+ base::Optional<int64_t>& downloaded_file_length,
+ blink::WebBlobInfo& downloaded_blob) {
+ if (factory_->IsMockedURL(request.Url())) {
+ factory_->LoadSynchronously(request, &response, &error, &data,
+ &encoded_data_length);
+ return;
+ }
+ AssertFallbackLoaderAvailability(request.Url(), default_loader_.get());
+ using_default_loader_ = true;
+ default_loader_->LoadSynchronously(request, response, error, data,
+ encoded_data_length, encoded_body_length,
+ downloaded_file_length, downloaded_blob);
+}
+
+void WebURLLoaderMock::LoadAsynchronously(const WebURLRequest& request,
+ WebURLLoaderClient* client) {
+ DCHECK(client);
+ if (factory_->IsMockedURL(request.Url())) {
+ client_ = client;
+ factory_->LoadAsynchronouly(request, this);
+ return;
+ }
+ AssertFallbackLoaderAvailability(request.Url(), default_loader_.get());
+ using_default_loader_ = true;
+ default_loader_->LoadAsynchronously(request, client);
+}
+
+void WebURLLoaderMock::Cancel() {
+ if (using_default_loader_) {
+ default_loader_->Cancel();
+ return;
+ }
+ client_ = nullptr;
+ factory_->CancelLoad(this);
+}
+
+void WebURLLoaderMock::SetDefersLoading(bool deferred) {
+ is_deferred_ = deferred;
+ if (using_default_loader_) {
+ default_loader_->SetDefersLoading(deferred);
+ return;
+ }
+
+ // Ignores setDefersLoading(false) safely.
+ if (!deferred)
+ return;
+
+ // setDefersLoading(true) is not implemented.
+ NOTIMPLEMENTED();
+}
+
+void WebURLLoaderMock::DidChangePriority(WebURLRequest::Priority new_priority,
+ int intra_priority_value) {}
+
+base::WeakPtr<WebURLLoaderMock> WebURLLoaderMock::GetWeakPtr() {
+ return weak_factory_.GetWeakPtr();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/testing/weburl_loader_mock.h b/chromium/third_party/blink/renderer/platform/testing/weburl_loader_mock.h
new file mode 100644
index 00000000000..ca63eec4a95
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/weburl_loader_mock.h
@@ -0,0 +1,82 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WebURLLoaderMock_h
+#define WebURLLoaderMock_h
+
+#include <memory>
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "third_party/blink/public/platform/web_url_error.h"
+#include "third_party/blink/public/platform/web_url_loader.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+
+namespace blink {
+
+class WebData;
+class WebURLLoaderClient;
+class WebURLLoaderMockFactoryImpl;
+class WebURLLoaderTestDelegate;
+class WebURLRequest;
+class WebURLResponse;
+
+const int kRedirectResponseOverheadBytes = 300;
+
+// A simple class for mocking WebURLLoader.
+// If the WebURLLoaderMockFactory it is associated with has been configured to
+// mock the request it gets, it serves the mocked resource. Otherwise it just
+// forwards it to the default loader.
+class WebURLLoaderMock : public WebURLLoader {
+ public:
+ // This object becomes the owner of |default_loader|.
+ WebURLLoaderMock(WebURLLoaderMockFactoryImpl* factory,
+ std::unique_ptr<WebURLLoader> default_loader);
+ ~WebURLLoaderMock() override;
+
+ // Simulates the asynchronous request being served.
+ void ServeAsynchronousRequest(WebURLLoaderTestDelegate* delegate,
+ const WebURLResponse& response,
+ const WebData& data,
+ const Optional<WebURLError>& error);
+
+ // Simulates the redirect being served.
+ WebURL ServeRedirect(const WebURLRequest& request,
+ const WebURLResponse& redirect_response);
+
+ // WebURLLoader methods:
+ void LoadSynchronously(const WebURLRequest&,
+ WebURLResponse&,
+ Optional<WebURLError>&,
+ WebData&,
+ int64_t& encoded_data_length,
+ int64_t& encoded_body_length,
+ base::Optional<int64_t>& downloaded_file_length,
+ blink::WebBlobInfo& downloaded_blob) override;
+ void LoadAsynchronously(const WebURLRequest& request,
+ WebURLLoaderClient* client) override;
+ void Cancel() override;
+ void SetDefersLoading(bool defer) override;
+ void DidChangePriority(WebURLRequest::Priority new_priority,
+ int intra_priority_value) override;
+
+ bool is_deferred() { return is_deferred_; }
+ bool is_cancelled() { return !client_; }
+
+ base::WeakPtr<WebURLLoaderMock> GetWeakPtr();
+
+ private:
+ WebURLLoaderMockFactoryImpl* factory_ = nullptr;
+ WebURLLoaderClient* client_ = nullptr;
+ std::unique_ptr<WebURLLoader> default_loader_;
+ bool using_default_loader_ = false;
+ bool is_deferred_ = false;
+
+ base::WeakPtrFactory<WebURLLoaderMock> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebURLLoaderMock);
+};
+
+} // namespace blink
+
+#endif // WebURLLoaderMock_h
diff --git a/chromium/third_party/blink/renderer/platform/testing/weburl_loader_mock_factory_impl.cc b/chromium/third_party/blink/renderer/platform/testing/weburl_loader_mock_factory_impl.cc
new file mode 100644
index 00000000000..711876d3a34
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/weburl_loader_mock_factory_impl.cc
@@ -0,0 +1,236 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/testing/weburl_loader_mock_factory_impl.h"
+
+#include <stdint.h>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/files/file_util.h"
+#include "base/memory/ptr_util.h"
+#include "base/run_loop.h"
+#include "third_party/blink/public/platform/file_path_conversion.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/platform/web_url_error.h"
+#include "third_party/blink/public/platform/web_url_request.h"
+#include "third_party/blink/public/platform/web_url_response.h"
+#include "third_party/blink/renderer/platform/loader/fetch/memory_cache.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
+#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/weburl_loader_mock.h"
+
+namespace blink {
+
+std::unique_ptr<WebURLLoaderMockFactory> WebURLLoaderMockFactory::Create() {
+ return base::WrapUnique(new WebURLLoaderMockFactoryImpl(nullptr));
+}
+
+WebURLLoaderMockFactoryImpl::WebURLLoaderMockFactoryImpl(
+ TestingPlatformSupport* platform)
+ : platform_(platform) {}
+
+WebURLLoaderMockFactoryImpl::~WebURLLoaderMockFactoryImpl() = default;
+
+std::unique_ptr<WebURLLoader> WebURLLoaderMockFactoryImpl::CreateURLLoader(
+ std::unique_ptr<WebURLLoader> default_loader) {
+ return std::make_unique<WebURLLoaderMock>(this, std::move(default_loader));
+}
+
+void WebURLLoaderMockFactoryImpl::RegisterURL(const WebURL& url,
+ const WebURLResponse& response,
+ const WebString& file_path) {
+ ResponseInfo response_info;
+ response_info.response = response;
+ if (!file_path.IsNull() && !file_path.IsEmpty()) {
+ response_info.file_path = blink::WebStringToFilePath(file_path);
+ DCHECK(base::PathExists(response_info.file_path))
+ << response_info.file_path.MaybeAsASCII() << " does not exist.";
+ }
+
+ DCHECK(url_to_response_info_.find(url) == url_to_response_info_.end());
+ url_to_response_info_.Set(url, response_info);
+}
+
+void WebURLLoaderMockFactoryImpl::RegisterErrorURL(
+ const WebURL& url,
+ const WebURLResponse& response,
+ const WebURLError& error) {
+ DCHECK(url_to_response_info_.find(url) == url_to_response_info_.end());
+ RegisterURL(url, response, WebString());
+ url_to_error_info_.Set(url, error);
+}
+
+void WebURLLoaderMockFactoryImpl::UnregisterURL(const blink::WebURL& url) {
+ URLToResponseMap::iterator iter = url_to_response_info_.find(url);
+ DCHECK(iter != url_to_response_info_.end());
+ url_to_response_info_.erase(iter);
+
+ URLToErrorMap::iterator error_iter = url_to_error_info_.find(url);
+ if (error_iter != url_to_error_info_.end())
+ url_to_error_info_.erase(error_iter);
+}
+
+void WebURLLoaderMockFactoryImpl::RegisterURLProtocol(
+ const WebString& protocol,
+ const WebURLResponse& response,
+ const WebString& file_path) {
+ DCHECK(protocol.ContainsOnlyASCII());
+
+ ResponseInfo response_info;
+ response_info.response = response;
+ if (!file_path.IsNull() && !file_path.IsEmpty()) {
+ response_info.file_path = blink::WebStringToFilePath(file_path);
+ DCHECK(base::PathExists(response_info.file_path))
+ << response_info.file_path.MaybeAsASCII() << " does not exist.";
+ }
+
+ DCHECK(protocol_to_response_info_.find(protocol) ==
+ protocol_to_response_info_.end());
+ protocol_to_response_info_.Set(protocol, response_info);
+}
+
+void WebURLLoaderMockFactoryImpl::UnregisterURLProtocol(
+ const WebString& protocol) {
+ ProtocolToResponseMap::iterator iter =
+ protocol_to_response_info_.find(protocol);
+ DCHECK(iter != protocol_to_response_info_.end());
+ protocol_to_response_info_.erase(iter);
+}
+
+void WebURLLoaderMockFactoryImpl::UnregisterAllURLsAndClearMemoryCache() {
+ url_to_response_info_.clear();
+ url_to_error_info_.clear();
+ protocol_to_response_info_.clear();
+ if (IsMainThread())
+ GetMemoryCache()->EvictResources();
+}
+
+void WebURLLoaderMockFactoryImpl::ServeAsynchronousRequests() {
+ // Serving a request might trigger more requests, so we cannot iterate on
+ // pending_loaders_ as it might get modified.
+ while (!pending_loaders_.IsEmpty()) {
+ LoaderToRequestMap::iterator iter = pending_loaders_.begin();
+ base::WeakPtr<WebURLLoaderMock> loader(iter->key->GetWeakPtr());
+ const WebURLRequest request = iter->value;
+ pending_loaders_.erase(loader.get());
+
+ WebURLResponse response;
+ Optional<WebURLError> error;
+ WebData data;
+ LoadRequest(request.Url(), &response, &error, &data);
+ // Follow any redirects while the loader is still active.
+ while (response.HttpStatusCode() >= 300 &&
+ response.HttpStatusCode() < 400) {
+ WebURL new_url = loader->ServeRedirect(request, response);
+ RunUntilIdle();
+ if (!loader || loader->is_cancelled() || loader->is_deferred())
+ break;
+ LoadRequest(new_url, &response, &error, &data);
+ }
+ // Serve the request if the loader is still active.
+ if (loader && !loader->is_cancelled() && !loader->is_deferred()) {
+ loader->ServeAsynchronousRequest(delegate_, response, data, error);
+ RunUntilIdle();
+ }
+ }
+ RunUntilIdle();
+}
+
+bool WebURLLoaderMockFactoryImpl::IsMockedURL(const blink::WebURL& url) {
+ Optional<WebURLError> error;
+ ResponseInfo response_info;
+ return LookupURL(url, &error, &response_info);
+}
+
+void WebURLLoaderMockFactoryImpl::CancelLoad(WebURLLoaderMock* loader) {
+ pending_loaders_.erase(loader);
+}
+
+void WebURLLoaderMockFactoryImpl::LoadSynchronously(
+ const WebURLRequest& request,
+ WebURLResponse* response,
+ Optional<WebURLError>* error,
+ WebData* data,
+ int64_t* encoded_data_length) {
+ LoadRequest(request.Url(), response, error, data);
+ *encoded_data_length = data->size();
+}
+
+void WebURLLoaderMockFactoryImpl::LoadAsynchronouly(
+ const WebURLRequest& request,
+ WebURLLoaderMock* loader) {
+ DCHECK(!pending_loaders_.Contains(loader));
+ pending_loaders_.Set(loader, request);
+}
+
+void WebURLLoaderMockFactoryImpl::RunUntilIdle() {
+ if (platform_)
+ platform_->RunUntilIdle();
+ else
+ base::RunLoop().RunUntilIdle();
+}
+
+void WebURLLoaderMockFactoryImpl::LoadRequest(const WebURL& url,
+ WebURLResponse* response,
+ Optional<WebURLError>* error,
+ WebData* data) {
+ ResponseInfo response_info;
+ if (!LookupURL(url, error, &response_info)) {
+ // Non mocked URLs should not have been passed to the default URLLoader.
+ NOTREACHED();
+ return;
+ }
+
+ if (!*error && !ReadFile(response_info.file_path, data)) {
+ NOTREACHED();
+ return;
+ }
+
+ *response = response_info.response;
+}
+
+bool WebURLLoaderMockFactoryImpl::LookupURL(const WebURL& url,
+ Optional<WebURLError>* error,
+ ResponseInfo* response_info) {
+ URLToErrorMap::const_iterator error_iter = url_to_error_info_.find(url);
+ if (error_iter != url_to_error_info_.end())
+ *error = error_iter->value;
+
+ URLToResponseMap::const_iterator iter = url_to_response_info_.find(url);
+ if (iter != url_to_response_info_.end()) {
+ *response_info = iter->value;
+ return true;
+ }
+
+ for (const auto& key_value_pair : protocol_to_response_info_) {
+ String protocol = key_value_pair.key;
+ if (url.ProtocolIs(protocol.Ascii().data())) {
+ *response_info = key_value_pair.value;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// static
+bool WebURLLoaderMockFactoryImpl::ReadFile(const base::FilePath& file_path,
+ WebData* data) {
+ // If the path is empty then we return an empty file so tests can simulate
+ // requests without needing to actually load files.
+ if (file_path.empty())
+ return true;
+
+ std::string buffer;
+ if (!base::ReadFileToString(file_path, &buffer))
+ return false;
+
+ data->Assign(buffer.data(), buffer.size());
+ return true;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/testing/weburl_loader_mock_factory_impl.h b/chromium/third_party/blink/renderer/platform/testing/weburl_loader_mock_factory_impl.h
new file mode 100644
index 00000000000..c3481621d3c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/weburl_loader_mock_factory_impl.h
@@ -0,0 +1,130 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WebURLLoaderMockFactoryImpl_h
+#define WebURLLoaderMockFactoryImpl_h
+
+#include <map>
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "third_party/blink/public/platform/web_url.h"
+#include "third_party/blink/public/platform/web_url_error.h"
+#include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
+#include "third_party/blink/public/platform/web_url_request.h"
+#include "third_party/blink/public/platform/web_url_response.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl_hash.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+
+namespace blink {
+
+class TestingPlatformSupport;
+class WebData;
+class WebURLLoader;
+class WebURLLoaderMock;
+class WebURLLoaderTestDelegate;
+
+// A factory that creates WebURLLoaderMock to simulate resource loading in
+// tests. Since there are restriction and rules to follow, please read comments
+// in WebURLLoaderMockFactory carefully to use this class correctly.
+class WebURLLoaderMockFactoryImpl : public WebURLLoaderMockFactory {
+ public:
+ WebURLLoaderMockFactoryImpl(TestingPlatformSupport*);
+ ~WebURLLoaderMockFactoryImpl() override;
+
+ // WebURLLoaderMockFactory:
+ std::unique_ptr<WebURLLoader> CreateURLLoader(
+ std::unique_ptr<WebURLLoader> default_loader) override;
+ void RegisterURL(const WebURL& url,
+ const WebURLResponse& response,
+ const WebString& file_path = WebString()) override;
+ void RegisterErrorURL(const WebURL& url,
+ const WebURLResponse& response,
+ const WebURLError& error) override;
+ void UnregisterURL(const WebURL& url) override;
+ void RegisterURLProtocol(const WebString& protocol,
+ const WebURLResponse& response,
+ const WebString& file_path) override;
+ void UnregisterURLProtocol(const WebString& protocol) override;
+ void UnregisterAllURLsAndClearMemoryCache() override;
+ void ServeAsynchronousRequests() override;
+ void SetLoaderDelegate(WebURLLoaderTestDelegate* delegate) override {
+ delegate_ = delegate;
+ }
+
+ // Returns true if |url| was registered for being mocked.
+ bool IsMockedURL(const WebURL& url);
+
+ // Called by the loader to load a resource.
+ void LoadSynchronously(const WebURLRequest& request,
+ WebURLResponse* response,
+ Optional<WebURLError>* error,
+ WebData* data,
+ int64_t* encoded_data_length);
+ void LoadAsynchronouly(const WebURLRequest& request,
+ WebURLLoaderMock* loader);
+
+ // Removes the loader from the list of pending loaders.
+ void CancelLoad(WebURLLoaderMock* loader);
+
+ private:
+ struct ResponseInfo {
+ WebURLResponse response;
+ base::FilePath file_path;
+ };
+
+ virtual void RunUntilIdle();
+
+ // Loads the specified request and populates the response, error and data
+ // accordingly.
+ void LoadRequest(const WebURL& url,
+ WebURLResponse* response,
+ Optional<WebURLError>* error,
+ WebData* data);
+
+ // Checks if the loader is pending. Otherwise, it may have been deleted.
+ bool IsPending(base::WeakPtr<WebURLLoaderMock> loader);
+
+ // Looks up an URL in the mock URL table.
+ //
+ // If the URL is found, returns true and sets |error| and |response_info|.
+ bool LookupURL(const WebURL& url,
+ Optional<WebURLError>* error,
+ ResponseInfo* response_info);
+
+ // Reads |m_filePath| and puts its content in |data|.
+ // Returns true if it successfully read the file.
+ static bool ReadFile(const base::FilePath& file_path, WebData* data);
+
+ WebURLLoaderTestDelegate* delegate_ = nullptr;
+
+ // The loaders that have not being served data yet.
+ using LoaderToRequestMap = HashMap<WebURLLoaderMock*, WebURLRequest>;
+ LoaderToRequestMap pending_loaders_;
+
+ // All values must be valid, but we use Optional because HashMap requires
+ // "empty value".
+ typedef HashMap<KURL, Optional<WebURLError>> URLToErrorMap;
+ URLToErrorMap url_to_error_info_;
+
+ // Table of the registered URLs and the responses that they should receive.
+ using URLToResponseMap = HashMap<KURL, ResponseInfo>;
+ URLToResponseMap url_to_response_info_;
+
+ // Table of the registered URL protocols and the responses that they should
+ // receive.
+ using ProtocolToResponseMap = HashMap<String, ResponseInfo>;
+ ProtocolToResponseMap protocol_to_response_info_;
+
+ TestingPlatformSupport* platform_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebURLLoaderMockFactoryImpl);
+};
+
+} // namespace blink
+
+#endif // WebURLLoaderMockFactoryImpl_h
diff --git a/chromium/third_party/blink/renderer/platform/testing/wtf/scoped_mock_clock.cc b/chromium/third_party/blink/renderer/platform/testing/wtf/scoped_mock_clock.cc
new file mode 100644
index 00000000000..8f8c9e01e55
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/wtf/scoped_mock_clock.cc
@@ -0,0 +1,32 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/testing/wtf/scoped_mock_clock.h"
+
+namespace WTF {
+
+ScopedMockClock* ScopedMockClock::top_ = nullptr;
+
+ScopedMockClock::ScopedMockClock()
+ : next_(top_), next_time_function_(GetTimeFunctionForTesting()) {
+ top_ = this;
+ SetTimeFunctionsForTesting(&Now);
+}
+
+ScopedMockClock::~ScopedMockClock() {
+ top_ = next_;
+ SetTimeFunctionsForTesting(next_time_function_);
+}
+
+double ScopedMockClock::Now() {
+ return top_->now_.since_origin().InSecondsF();
+}
+
+void ScopedMockClock::Advance(TimeDelta delta) {
+ DCHECK_GT(delta, base::TimeDelta())
+ << "Monotonically increasing time may not go backwards";
+ now_ += delta;
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/testing/wtf/scoped_mock_clock.h b/chromium/third_party/blink/renderer/platform/testing/wtf/scoped_mock_clock.h
new file mode 100644
index 00000000000..6e0580e113a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/wtf/scoped_mock_clock.h
@@ -0,0 +1,33 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_WTF_SCOPED_MOCK_CLOCK_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_WTF_SCOPED_MOCK_CLOCK_H_
+
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace WTF {
+
+// Scoped helper to override WTF time functions for testing. Creating one on the
+// stack resets mock time to the zero point for WTF::Time and WTF::TimeTicks.
+// Mock time may only flow forwards, not backwards.
+class ScopedMockClock {
+ public:
+ ScopedMockClock();
+ ~ScopedMockClock();
+
+ static double Now();
+
+ void Advance(TimeDelta);
+
+ private:
+ static ScopedMockClock* top_;
+ ScopedMockClock* next_;
+ TimeFunction next_time_function_;
+ base::TimeTicks now_;
+};
+
+} // namespace WTF
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/text/bidi_character_run.h b/chromium/third_party/blink/renderer/platform/text/bidi_character_run.h
new file mode 100644
index 00000000000..1cb25f0250e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/bidi_character_run.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * Copyright (C) 2003, 2004, 2006, 2007, 2008 Apple Inc. All right reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_BIDI_CHARACTER_RUN_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_BIDI_CHARACTER_RUN_H_
+
+#include "third_party/blink/renderer/platform/text/bidi_context.h"
+#include "third_party/blink/renderer/platform/text/text_direction.h"
+
+namespace blink {
+
+struct BidiCharacterRun {
+ USING_FAST_MALLOC(BidiCharacterRun);
+
+ public:
+ BidiCharacterRun(bool override,
+ unsigned char level,
+ int start,
+ int stop,
+ WTF::Unicode::CharDirection dir,
+ WTF::Unicode::CharDirection override_dir)
+ : override_(override),
+ level_(level),
+ next_(nullptr),
+ start_(start),
+ stop_(stop) {
+ DCHECK_LE(start_, stop_);
+ if (dir == WTF::Unicode::kOtherNeutral)
+ dir = override_dir;
+
+ level_ = level;
+
+ // add level of run (cases I1 & I2)
+ if (level_ % 2) {
+ if (dir == WTF::Unicode::kLeftToRight ||
+ dir == WTF::Unicode::kArabicNumber ||
+ dir == WTF::Unicode::kEuropeanNumber)
+ level_++;
+ } else {
+ if (dir == WTF::Unicode::kRightToLeft)
+ level_++;
+ else if (dir == WTF::Unicode::kArabicNumber ||
+ dir == WTF::Unicode::kEuropeanNumber)
+ level_ += 2;
+ }
+ }
+
+ BidiCharacterRun(int start, int stop, unsigned char level)
+ : override_(false),
+ level_(level),
+ next_(nullptr),
+ start_(start),
+ stop_(stop) {}
+
+ int Start() const { return start_; }
+ int Stop() const { return stop_; }
+ unsigned char Level() const { return level_; }
+ bool Reversed(bool visually_ordered) const {
+ return level_ % 2 && !visually_ordered;
+ }
+ bool DirOverride(bool visually_ordered) {
+ return override_ || visually_ordered;
+ }
+ TextDirection Direction() const {
+ return Reversed(false) ? TextDirection::kRtl : TextDirection::kLtr;
+ }
+
+ BidiCharacterRun* Next() const { return next_; }
+ void SetNext(BidiCharacterRun* next) { next_ = next; }
+
+ // Do not add anything apart from bitfields until after m_next. See
+ // https://bugs.webkit.org/show_bug.cgi?id=100173
+ bool override_ : 1;
+ // Used by BidiRun subclass which is a layering violation but enables us to
+ // save 8 bytes per object on 64-bit.
+ bool has_hyphen_ : 1;
+ unsigned char level_;
+ BidiCharacterRun* next_;
+ int start_;
+ int stop_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_BIDI_CHARACTER_RUN_H_
diff --git a/chromium/third_party/blink/renderer/platform/text/bidi_context.cc b/chromium/third_party/blink/renderer/platform/text/bidi_context.cc
new file mode 100644
index 00000000000..c8c89b1735f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/bidi_context.cc
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * Copyright (C) 2003, 2004, 2006, 2007, 2009, 2010 Apple Inc.
+ * All right reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "third_party/blink/renderer/platform/text/bidi_context.h"
+
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+struct SameSizeAsBidiContext
+ : public ThreadSafeRefCounted<SameSizeAsBidiContext> {
+ uint32_t bitfields : 16;
+ void* parent;
+};
+
+static_assert(sizeof(BidiContext) == sizeof(SameSizeAsBidiContext),
+ "BidiContext should stay small");
+
+inline scoped_refptr<BidiContext> BidiContext::CreateUncached(
+ unsigned char level,
+ WTF::Unicode::CharDirection direction,
+ bool override,
+ BidiEmbeddingSource source,
+ BidiContext* parent) {
+ return base::AdoptRef(
+ new BidiContext(level, direction, override, source, parent));
+}
+
+scoped_refptr<BidiContext> BidiContext::Create(
+ unsigned char level,
+ WTF::Unicode::CharDirection direction,
+ bool override,
+ BidiEmbeddingSource source,
+ BidiContext* parent) {
+ DCHECK_EQ(direction, (level % 2 ? WTF::Unicode::kRightToLeft
+ : WTF::Unicode::kLeftToRight));
+
+ if (parent || level >= 2)
+ return CreateUncached(level, direction, override, source, parent);
+
+ DCHECK_LE(level, 1);
+ if (!level) {
+ if (!override) {
+ DEFINE_STATIC_REF(BidiContext, ltr_context,
+ (CreateUncached(0, WTF::Unicode::kLeftToRight, false,
+ kFromStyleOrDOM, nullptr)));
+ return ltr_context;
+ }
+
+ DEFINE_STATIC_REF(BidiContext, ltr_override_context,
+ (CreateUncached(0, WTF::Unicode::kLeftToRight, true,
+ kFromStyleOrDOM, nullptr)));
+ return ltr_override_context;
+ }
+
+ if (!override) {
+ DEFINE_STATIC_REF(BidiContext, rtl_context,
+ (CreateUncached(1, WTF::Unicode::kRightToLeft, false,
+ kFromStyleOrDOM, nullptr)));
+ return rtl_context;
+ }
+
+ DEFINE_STATIC_REF(BidiContext, rtl_override_context,
+ (CreateUncached(1, WTF::Unicode::kRightToLeft, true,
+ kFromStyleOrDOM, nullptr)));
+ return rtl_override_context;
+}
+
+static inline scoped_refptr<BidiContext> CopyContextAndRebaselineLevel(
+ BidiContext* context,
+ BidiContext* parent) {
+ DCHECK(context);
+ unsigned char new_level = parent ? parent->Level() : 0;
+ if (context->Dir() == WTF::Unicode::kRightToLeft)
+ new_level = NextGreaterOddLevel(new_level);
+ else if (parent)
+ new_level = NextGreaterEvenLevel(new_level);
+
+ return BidiContext::Create(new_level, context->Dir(), context->Override(),
+ context->Source(), parent);
+}
+
+// The BidiContext stack must be immutable -- they're re-used for re-layout
+// after DOM modification/editing -- so we copy all the non-unicode contexts,
+// and recalculate their levels.
+scoped_refptr<BidiContext>
+BidiContext::CopyStackRemovingUnicodeEmbeddingContexts() {
+ Vector<BidiContext*, 64> contexts;
+ for (BidiContext* iter = this; iter; iter = iter->Parent()) {
+ if (iter->Source() != kFromUnicode)
+ contexts.push_back(iter);
+ }
+ DCHECK(contexts.size());
+
+ scoped_refptr<BidiContext> top_context =
+ CopyContextAndRebaselineLevel(contexts.back(), nullptr);
+ for (int i = contexts.size() - 1; i > 0; --i) {
+ top_context =
+ CopyContextAndRebaselineLevel(contexts[i - 1], top_context.get());
+ }
+
+ return top_context;
+}
+
+bool operator==(const BidiContext& c1, const BidiContext& c2) {
+ if (&c1 == &c2)
+ return true;
+ if (c1.Level() != c2.Level() || c1.Override() != c2.Override() ||
+ c1.Dir() != c2.Dir() || c1.Source() != c2.Source())
+ return false;
+ if (!c1.Parent())
+ return !c2.Parent();
+ return c2.Parent() && *c1.Parent() == *c2.Parent();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/bidi_context.h b/chromium/third_party/blink/renderer/platform/text/bidi_context.h
new file mode 100644
index 00000000000..e376d5f99fa
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/bidi_context.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * Copyright (C) 2003, 2004, 2006, 2007, 2009, 2010 Apple Inc.
+ * All right reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_BIDI_CONTEXT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_BIDI_CONTEXT_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
+#include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
+
+namespace blink {
+
+enum BidiEmbeddingSource { kFromStyleOrDOM, kFromUnicode };
+
+// Used to keep track of explicit embeddings.
+class PLATFORM_EXPORT BidiContext : public ThreadSafeRefCounted<BidiContext> {
+ public:
+ static scoped_refptr<BidiContext> Create(
+ unsigned char level,
+ WTF::Unicode::CharDirection,
+ bool override = false,
+ BidiEmbeddingSource = kFromStyleOrDOM,
+ BidiContext* parent = nullptr);
+
+ BidiContext* Parent() const { return parent_.get(); }
+ unsigned char Level() const { return level_; }
+ WTF::Unicode::CharDirection Dir() const {
+ return static_cast<WTF::Unicode::CharDirection>(direction_);
+ }
+ bool Override() const { return override_; }
+ BidiEmbeddingSource Source() const {
+ return static_cast<BidiEmbeddingSource>(source_);
+ }
+
+ scoped_refptr<BidiContext> CopyStackRemovingUnicodeEmbeddingContexts();
+
+ // http://www.unicode.org/reports/tr9/#Modifications
+ // 6.3 raised the limit from 61 to 125.
+ // http://unicode.org/reports/tr9/#BD2
+ static const unsigned char kMaxLevel = 125;
+
+ private:
+ BidiContext(unsigned char level,
+ WTF::Unicode::CharDirection direction,
+ bool override,
+ BidiEmbeddingSource source,
+ BidiContext* parent)
+ : level_(level),
+ direction_(direction),
+ override_(override),
+ source_(source),
+ parent_(parent) {
+ DCHECK(level <= kMaxLevel);
+ }
+
+ static scoped_refptr<BidiContext> CreateUncached(unsigned char level,
+ WTF::Unicode::CharDirection,
+ bool override,
+ BidiEmbeddingSource,
+ BidiContext* parent);
+
+ // The maximium bidi level is 125:
+ // http://unicode.org/reports/tr9/#Explicit_Levels_and_Directions
+ unsigned level_ : 7;
+ unsigned direction_ : 5; // Direction
+ unsigned override_ : 1;
+ unsigned source_ : 1; // BidiEmbeddingSource
+ scoped_refptr<BidiContext> parent_;
+};
+
+inline unsigned char NextGreaterOddLevel(unsigned char level) {
+ return (level + 1) | 1;
+}
+
+inline unsigned char NextGreaterEvenLevel(unsigned char level) {
+ return (level + 2) & ~1;
+}
+
+PLATFORM_EXPORT bool operator==(const BidiContext&, const BidiContext&);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_BIDI_CONTEXT_H_
diff --git a/chromium/third_party/blink/renderer/platform/text/bidi_resolver.h b/chromium/third_party/blink/renderer/platform/text/bidi_resolver.h
new file mode 100644
index 00000000000..6002456a003
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/bidi_resolver.h
@@ -0,0 +1,1252 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * Copyright (C) 2003, 2004, 2006, 2007, 2008 Apple Inc. All right reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_BIDI_RESOLVER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_BIDI_RESOLVER_H_
+
+#include "third_party/blink/renderer/platform/text/bidi_character_run.h"
+#include "third_party/blink/renderer/platform/text/bidi_context.h"
+#include "third_party/blink/renderer/platform/text/bidi_run_list.h"
+#include "third_party/blink/renderer/platform/text/text_direction.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+template <class Iterator>
+class MidpointState final {
+ DISALLOW_NEW();
+
+ public:
+ MidpointState() { Reset(); }
+
+ void Reset() {
+ num_midpoints_ = 0;
+ current_midpoint_ = 0;
+ between_midpoints_ = false;
+ }
+
+ void StartIgnoringSpaces(const Iterator& midpoint) {
+ DCHECK(!(num_midpoints_ % 2));
+ AddMidpoint(midpoint);
+ }
+
+ void StopIgnoringSpaces(const Iterator& midpoint) {
+ DCHECK(num_midpoints_ % 2);
+ AddMidpoint(midpoint);
+ }
+
+ // Adding a pair of midpoints before a character will split it out into a new
+ // line box.
+ void EnsureCharacterGetsLineBox(Iterator& text_paragraph_separator) {
+ StartIgnoringSpaces(Iterator(nullptr,
+ text_paragraph_separator.GetLineLayoutItem(),
+ text_paragraph_separator.Offset() - 1));
+ StopIgnoringSpaces(Iterator(nullptr,
+ text_paragraph_separator.GetLineLayoutItem(),
+ text_paragraph_separator.Offset()));
+ }
+
+ void CheckMidpoints(Iterator& l_break) {
+ // Check to see if our last midpoint is a start point beyond the line break.
+ // If so, shave it off the list, and shave off a trailing space if the
+ // previous end point doesn't preserve whitespace.
+ if (l_break.GetLineLayoutItem() && num_midpoints_ &&
+ !(num_midpoints_ % 2)) {
+ Iterator* midpoints_iterator = midpoints_.data();
+ Iterator& endpoint = midpoints_iterator[num_midpoints_ - 2];
+ const Iterator& startpoint = midpoints_iterator[num_midpoints_ - 1];
+ Iterator currpoint = endpoint;
+ while (!currpoint.AtEnd() && currpoint != startpoint &&
+ currpoint != l_break)
+ currpoint.Increment();
+ if (currpoint == l_break) {
+ // We hit the line break before the start point. Shave off the start
+ // point.
+ num_midpoints_--;
+ if (endpoint.GetLineLayoutItem().Style()->CollapseWhiteSpace() &&
+ endpoint.GetLineLayoutItem().IsText())
+ endpoint.SetOffset(endpoint.Offset() - 1);
+ }
+ }
+ }
+
+ Vector<Iterator>& Midpoints() { return midpoints_; }
+ const unsigned& NumMidpoints() const { return num_midpoints_; }
+ const unsigned& CurrentMidpoint() const { return current_midpoint_; }
+ void IncrementCurrentMidpoint() { current_midpoint_++; }
+ const bool& BetweenMidpoints() const { return between_midpoints_; }
+ void SetBetweenMidpoints(bool between_midpoint) {
+ between_midpoints_ = between_midpoint;
+ }
+
+ private:
+ // The goal is to reuse the line state across multiple
+ // lines so we just keep an array around for midpoints and never clear it
+ // across multiple lines. We track the number of items and position using the
+ // two other variables.
+ Vector<Iterator> midpoints_;
+ unsigned num_midpoints_;
+ unsigned current_midpoint_;
+ bool between_midpoints_;
+
+ void AddMidpoint(const Iterator& midpoint) {
+ if (midpoints_.size() <= num_midpoints_)
+ midpoints_.Grow(num_midpoints_ + 10);
+
+ Iterator* midpoints_iterator = midpoints_.data();
+ midpoints_iterator[num_midpoints_++] = midpoint;
+ }
+};
+
+// The BidiStatus at a given position (typically the end of a line) can
+// be cached and then used to restart bidi resolution at that position.
+struct BidiStatus final {
+ DISALLOW_NEW();
+ BidiStatus()
+ : eor(WTF::Unicode::kOtherNeutral),
+ last_strong(WTF::Unicode::kOtherNeutral),
+ last(WTF::Unicode::kOtherNeutral) {}
+
+ // Creates a BidiStatus representing a new paragraph root with a default
+ // direction. Uses TextDirection as it only has two possibilities instead of
+ // WTF::Unicode::Direction which has 19.
+ BidiStatus(TextDirection text_direction, bool is_override) {
+ WTF::Unicode::CharDirection direction =
+ text_direction == TextDirection::kLtr ? WTF::Unicode::kLeftToRight
+ : WTF::Unicode::kRightToLeft;
+ eor = last_strong = last = direction;
+ context = BidiContext::Create(text_direction == TextDirection::kLtr ? 0 : 1,
+ direction, is_override);
+ }
+
+ BidiStatus(WTF::Unicode::CharDirection eor_dir,
+ WTF::Unicode::CharDirection last_strong_dir,
+ WTF::Unicode::CharDirection last_dir,
+ scoped_refptr<BidiContext> bidi_context)
+ : eor(eor_dir),
+ last_strong(last_strong_dir),
+ last(last_dir),
+ context(std::move(bidi_context)) {}
+
+ // Creates a BidiStatus for Isolates (RLI/LRI).
+ // The rule X5a ans X5b of UAX#9: http://unicode.org/reports/tr9/#X5a
+ static BidiStatus CreateForIsolate(TextDirection text_direction,
+ bool is_override,
+ unsigned char level) {
+ WTF::Unicode::CharDirection direction;
+ if (text_direction == TextDirection::kRtl) {
+ level = NextGreaterOddLevel(level);
+ direction = WTF::Unicode::kRightToLeft;
+ } else {
+ level = NextGreaterEvenLevel(level);
+ direction = WTF::Unicode::kLeftToRight;
+ }
+ scoped_refptr<BidiContext> context =
+ BidiContext::Create(level, direction, is_override, kFromStyleOrDOM);
+
+ // This copies BidiStatus and may churn the ref on BidiContext.
+ // I doubt it matters.
+ return BidiStatus(direction, direction, direction, std::move(context));
+ }
+
+ WTF::Unicode::CharDirection eor;
+ WTF::Unicode::CharDirection last_strong;
+ WTF::Unicode::CharDirection last;
+ scoped_refptr<BidiContext> context;
+};
+
+class BidiEmbedding final {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ BidiEmbedding(WTF::Unicode::CharDirection direction,
+ BidiEmbeddingSource source)
+ : direction_(direction), source_(source) {}
+
+ WTF::Unicode::CharDirection Direction() const { return direction_; }
+ BidiEmbeddingSource Source() const { return source_; }
+
+ private:
+ WTF::Unicode::CharDirection direction_;
+ BidiEmbeddingSource source_;
+};
+
+inline bool operator==(const BidiStatus& status1, const BidiStatus& status2) {
+ return status1.eor == status2.eor && status1.last == status2.last &&
+ status1.last_strong == status2.last_strong &&
+ *(status1.context) == *(status2.context);
+}
+
+inline bool operator!=(const BidiStatus& status1, const BidiStatus& status2) {
+ return !(status1 == status2);
+}
+
+enum VisualDirectionOverride {
+ kNoVisualOverride,
+ kVisualLeftToRightOverride,
+ kVisualRightToLeftOverride
+};
+
+class NoIsolatedRun {};
+
+// BidiResolver is WebKit's implementation of the Unicode Bidi Algorithm
+// http://unicode.org/reports/tr9
+template <class Iterator, class Run, class IsolatedRun = NoIsolatedRun>
+class BidiResolver final {
+ DISALLOW_NEW();
+
+ public:
+ BidiResolver()
+ : direction_(WTF::Unicode::kOtherNeutral),
+ reached_end_of_line_(false),
+ empty_run_(true),
+ nested_isolate_count_(0),
+ trailing_space_run_(nullptr),
+ needs_trailing_space_(false) {}
+
+#if DCHECK_IS_ON()
+ ~BidiResolver();
+#endif
+
+ const Iterator& GetPosition() const { return current_; }
+ Iterator& GetPosition() { return current_; }
+ void SetPositionIgnoringNestedIsolates(const Iterator& position) {
+ current_ = position;
+ }
+ void SetPosition(const Iterator& position, unsigned nested_isolated_count) {
+ current_ = position;
+ nested_isolate_count_ = nested_isolated_count;
+ }
+
+ BidiContext* Context() const { return status_.context.get(); }
+ void SetContext(scoped_refptr<BidiContext> c) {
+ status_.context = std::move(c);
+ }
+
+ void SetLastDir(WTF::Unicode::CharDirection last_dir) {
+ status_.last = last_dir;
+ }
+ void SetLastStrongDir(WTF::Unicode::CharDirection last_strong_dir) {
+ status_.last_strong = last_strong_dir;
+ }
+ void SetEorDir(WTF::Unicode::CharDirection eor_dir) { status_.eor = eor_dir; }
+
+ WTF::Unicode::CharDirection Dir() const { return direction_; }
+ void SetDir(WTF::Unicode::CharDirection d) { direction_ = d; }
+
+ const BidiStatus& Status() const { return status_; }
+ void SetStatus(const BidiStatus s) {
+ DCHECK(s.context);
+ status_ = s;
+ paragraph_directionality_ = s.context->Dir() == WTF::Unicode::kLeftToRight
+ ? TextDirection::kLtr
+ : TextDirection::kRtl;
+ }
+
+ MidpointState<Iterator>& GetMidpointState() { return midpoint_state_; }
+
+ // The current algorithm handles nested isolates one layer of nesting at a
+ // time. But when we layout each isolated span, we will walk into (and
+ // ignore) all child isolated spans.
+ void EnterIsolate() { nested_isolate_count_++; }
+ void ExitIsolate() {
+ DCHECK_GE(nested_isolate_count_, 1u);
+ nested_isolate_count_--;
+ }
+ bool InIsolate() const { return nested_isolate_count_; }
+
+ void Embed(WTF::Unicode::CharDirection, BidiEmbeddingSource);
+ bool CommitExplicitEmbedding(BidiRunList<Run>&);
+
+ void CreateBidiRunsForLine(const Iterator& end,
+ VisualDirectionOverride = kNoVisualOverride,
+ bool hard_line_break = false,
+ bool reorder_runs = true);
+
+ BidiRunList<Run>& Runs() { return runs_; }
+
+ // FIXME: This used to be part of deleteRuns() but was a layering violation.
+ // It's unclear if this is still needed.
+ void MarkCurrentRunEmpty() { empty_run_ = true; }
+
+ Vector<IsolatedRun>& IsolatedRuns() { return isolated_runs_; }
+
+ bool IsEndOfLine(const Iterator& end) {
+ return current_ == end || current_.AtEnd();
+ }
+
+ TextDirection DetermineParagraphDirectionality(
+ bool* has_strong_directionality = nullptr) {
+ bool break_on_paragraph = true;
+ return DetermineDirectionalityInternal(break_on_paragraph,
+ has_strong_directionality);
+ }
+ TextDirection DetermineDirectionality(
+ bool* has_strong_directionality = nullptr) {
+ bool break_on_paragraph = false;
+ return DetermineDirectionalityInternal(break_on_paragraph,
+ has_strong_directionality);
+ }
+
+ void SetMidpointStateForIsolatedRun(Run&, const MidpointState<Iterator>&);
+ MidpointState<Iterator> MidpointStateForIsolatedRun(Run&);
+
+ Iterator EndOfLine() const { return end_of_line_; }
+
+ void SetNeedsTrailingSpace(bool value) { needs_trailing_space_ = value; }
+ Run* TrailingSpaceRun() const { return trailing_space_run_; }
+
+ protected:
+ void Increment() { current_.Increment(); }
+ // FIXME: Instead of InlineBidiResolvers subclassing this method, we should
+ // pass in some sort of Traits object which knows how to create runs for
+ // appending.
+ void AppendRun(BidiRunList<Run>&);
+
+ Run* AddTrailingRun(BidiRunList<Run>&,
+ int,
+ int,
+ Run*,
+ BidiContext*,
+ TextDirection) const {
+ return nullptr;
+ }
+ Iterator current_;
+ // sor and eor are "start of run" and "end of run" respectively and correpond
+ // to abreviations used in UBA spec: http://unicode.org/reports/tr9/#BD7
+ Iterator sor_; // Points to the first character in the current run.
+ Iterator eor_; // Points to the last character in the current run.
+ Iterator last_;
+ BidiStatus status_;
+ WTF::Unicode::CharDirection direction_;
+ // m_endOfRunAtEndOfLine is "the position last eor in the end of line"
+ Iterator end_of_run_at_end_of_line_;
+ Iterator end_of_line_;
+ bool reached_end_of_line_;
+ Iterator last_before_et_; // Before a EuropeanNumberTerminator
+ bool empty_run_;
+
+ // FIXME: This should not belong to the resolver, but rather be passed
+ // into createBidiRunsForLine by the caller.
+ BidiRunList<Run> runs_;
+
+ MidpointState<Iterator> midpoint_state_;
+
+ unsigned nested_isolate_count_;
+ Vector<IsolatedRun> isolated_runs_;
+ Run* trailing_space_run_;
+ bool needs_trailing_space_;
+ TextDirection paragraph_directionality_;
+
+ private:
+ void RaiseExplicitEmbeddingLevel(BidiRunList<Run>&,
+ WTF::Unicode::CharDirection from,
+ WTF::Unicode::CharDirection to);
+ void LowerExplicitEmbeddingLevel(BidiRunList<Run>&,
+ WTF::Unicode::CharDirection from);
+ void CheckDirectionInLowerRaiseEmbeddingLevel();
+
+ void UpdateStatusLastFromCurrentDirection(WTF::Unicode::CharDirection);
+ void ReorderRunsFromLevels(BidiRunList<Run>&) const;
+
+ bool NeedsTrailingSpace(BidiRunList<Run>&) { return needs_trailing_space_; }
+ int FindFirstTrailingSpaceAtRun(Run*) { return 0; }
+ // http://www.unicode.org/reports/tr9/#L1
+ void ComputeTrailingSpace(BidiRunList<Run>&);
+
+ TextDirection DetermineDirectionalityInternal(
+ bool break_on_paragraph,
+ bool* has_strong_directionality);
+
+ Vector<BidiEmbedding, 8> current_explicit_embedding_sequence_;
+ HashMap<Run*, MidpointState<Iterator>> midpoint_state_for_isolated_run_;
+
+ DISALLOW_COPY_AND_ASSIGN(BidiResolver);
+};
+
+#if DCHECK_IS_ON()
+template <class Iterator, class Run, class IsolatedRun>
+BidiResolver<Iterator, Run, IsolatedRun>::~BidiResolver() {
+ // The owner of this resolver should have handled the isolated runs.
+ DCHECK(isolated_runs_.IsEmpty());
+ DCHECK(!runs_.RunCount());
+}
+#endif
+
+template <class Iterator, class Run, class IsolatedRun>
+void BidiResolver<Iterator, Run, IsolatedRun>::AppendRun(
+ BidiRunList<Run>& runs) {
+ if (!empty_run_ && !eor_.AtEnd()) {
+ unsigned start_offset = sor_.Offset();
+ unsigned end_offset = eor_.Offset();
+
+ if (!end_of_run_at_end_of_line_.AtEnd() &&
+ end_offset >= end_of_run_at_end_of_line_.Offset()) {
+ reached_end_of_line_ = true;
+ end_offset = end_of_run_at_end_of_line_.Offset();
+ }
+
+ // m_eor and m_endOfRunAtEndOfLine are inclusive while BidiRun's stop is
+ // exclusive so offset needs to be increased by one.
+ end_offset += 1;
+
+ // Append BidiRun objects, at most 64K chars at a time, until all
+ // text between |startOffset| and |endOffset| is represented.
+ while (start_offset < end_offset) {
+ unsigned end = end_offset;
+ const int kLimit =
+ USHRT_MAX; // InlineTextBox stores text length as unsigned short.
+ if (end - start_offset > kLimit)
+ end = start_offset + kLimit;
+ runs.AddRun(new Run(Context()->Override(), Context()->Level(),
+ start_offset, end, direction_, Context()->Dir()));
+ start_offset = end;
+ }
+
+ eor_.Increment();
+ sor_ = eor_;
+ }
+
+ direction_ = WTF::Unicode::kOtherNeutral;
+ status_.eor = WTF::Unicode::kOtherNeutral;
+}
+
+template <class Iterator, class Run, class IsolatedRun>
+void BidiResolver<Iterator, Run, IsolatedRun>::Embed(
+ WTF::Unicode::CharDirection dir,
+ BidiEmbeddingSource source) {
+ // Isolated spans compute base directionality during their own UBA run.
+ // Do not insert fake embed characters once we enter an isolated span.
+ DCHECK(!InIsolate());
+
+ DCHECK(dir == WTF::Unicode::kPopDirectionalFormat ||
+ dir == WTF::Unicode::kLeftToRightEmbedding ||
+ dir == WTF::Unicode::kLeftToRightOverride ||
+ dir == WTF::Unicode::kRightToLeftEmbedding ||
+ dir == WTF::Unicode::kRightToLeftOverride);
+ current_explicit_embedding_sequence_.push_back(BidiEmbedding(dir, source));
+}
+
+template <class Iterator, class Run, class IsolatedRun>
+void BidiResolver<Iterator, Run, IsolatedRun>::
+ CheckDirectionInLowerRaiseEmbeddingLevel() {
+ DCHECK(status_.eor != WTF::Unicode::kOtherNeutral || eor_.AtEnd());
+ DCHECK_NE(status_.last, WTF::Unicode::kNonSpacingMark);
+ DCHECK_NE(status_.last, WTF::Unicode::kBoundaryNeutral);
+ DCHECK_NE(status_.last, WTF::Unicode::kRightToLeftEmbedding);
+ DCHECK_NE(status_.last, WTF::Unicode::kLeftToRightEmbedding);
+ DCHECK_NE(status_.last, WTF::Unicode::kRightToLeftOverride);
+ DCHECK_NE(status_.last, WTF::Unicode::kLeftToRightOverride);
+ DCHECK_NE(status_.last, WTF::Unicode::kPopDirectionalFormat);
+ if (direction_ == WTF::Unicode::kOtherNeutral) {
+ direction_ = status_.last_strong == WTF::Unicode::kLeftToRight
+ ? WTF::Unicode::kLeftToRight
+ : WTF::Unicode::kRightToLeft;
+ }
+}
+
+template <class Iterator, class Run, class IsolatedRun>
+void BidiResolver<Iterator, Run, IsolatedRun>::LowerExplicitEmbeddingLevel(
+ BidiRunList<Run>& runs,
+ WTF::Unicode::CharDirection from) {
+ if (!empty_run_ && eor_ != last_) {
+ CheckDirectionInLowerRaiseEmbeddingLevel();
+ // bidi.sor ... bidi.eor ... bidi.last eor; need to append the
+ // bidi.sor-bidi.eor run or extend it through bidi.last
+ if (from == WTF::Unicode::kLeftToRight) {
+ // bidi.sor ... bidi.eor ... bidi.last L
+ if (status_.eor == WTF::Unicode::kEuropeanNumber) {
+ if (status_.last_strong != WTF::Unicode::kLeftToRight) {
+ direction_ = WTF::Unicode::kEuropeanNumber;
+ AppendRun(runs);
+ }
+ } else if (status_.eor == WTF::Unicode::kArabicNumber) {
+ direction_ = WTF::Unicode::kArabicNumber;
+ AppendRun(runs);
+ } else if (status_.last_strong != WTF::Unicode::kLeftToRight) {
+ AppendRun(runs);
+ direction_ = WTF::Unicode::kLeftToRight;
+ }
+ } else if (status_.eor == WTF::Unicode::kEuropeanNumber ||
+ status_.eor == WTF::Unicode::kArabicNumber ||
+ status_.last_strong == WTF::Unicode::kLeftToRight) {
+ AppendRun(runs);
+ direction_ = WTF::Unicode::kRightToLeft;
+ }
+ eor_ = last_;
+ }
+
+ AppendRun(runs);
+ empty_run_ = true;
+
+ // sor for the new run is determined by the higher level (rule X10)
+ SetLastDir(from);
+ SetLastStrongDir(from);
+ eor_ = Iterator();
+}
+
+template <class Iterator, class Run, class IsolatedRun>
+void BidiResolver<Iterator, Run, IsolatedRun>::RaiseExplicitEmbeddingLevel(
+ BidiRunList<Run>& runs,
+ WTF::Unicode::CharDirection from,
+ WTF::Unicode::CharDirection to) {
+ if (!empty_run_ && eor_ != last_) {
+ CheckDirectionInLowerRaiseEmbeddingLevel();
+ // bidi.sor ... bidi.eor ... bidi.last eor; need to append the
+ // bidi.sor-bidi.eor run or extend it through bidi.last
+ if (to == WTF::Unicode::kLeftToRight) {
+ // bidi.sor ... bidi.eor ... bidi.last L
+ if (status_.eor == WTF::Unicode::kEuropeanNumber) {
+ if (status_.last_strong != WTF::Unicode::kLeftToRight) {
+ direction_ = WTF::Unicode::kEuropeanNumber;
+ AppendRun(runs);
+ }
+ } else if (status_.eor == WTF::Unicode::kArabicNumber) {
+ direction_ = WTF::Unicode::kArabicNumber;
+ AppendRun(runs);
+ } else if (status_.last_strong != WTF::Unicode::kLeftToRight &&
+ from == WTF::Unicode::kLeftToRight) {
+ AppendRun(runs);
+ direction_ = WTF::Unicode::kLeftToRight;
+ }
+ } else if (status_.eor == WTF::Unicode::kArabicNumber ||
+ (status_.eor == WTF::Unicode::kEuropeanNumber &&
+ (status_.last_strong != WTF::Unicode::kLeftToRight ||
+ from == WTF::Unicode::kRightToLeft)) ||
+ (status_.eor != WTF::Unicode::kEuropeanNumber &&
+ status_.last_strong == WTF::Unicode::kLeftToRight &&
+ from == WTF::Unicode::kRightToLeft)) {
+ AppendRun(runs);
+ direction_ = WTF::Unicode::kRightToLeft;
+ }
+ eor_ = last_;
+ }
+
+ AppendRun(runs);
+ empty_run_ = true;
+
+ SetLastDir(to);
+ SetLastStrongDir(to);
+ eor_ = Iterator();
+}
+
+template <class Iterator, class Run, class IsolatedRun>
+void BidiResolver<Iterator, Run, IsolatedRun>::ComputeTrailingSpace(
+ BidiRunList<Run>& runs) {
+ DCHECK(runs.RunCount());
+
+ Run* trailing_space_run = runs.LogicallyLastRun();
+
+ int first_space = FindFirstTrailingSpaceAtRun(trailing_space_run);
+ if (first_space == trailing_space_run->Stop())
+ return;
+
+ bool should_reorder =
+ trailing_space_run != (paragraph_directionality_ == TextDirection::kLtr
+ ? runs.LastRun()
+ : runs.FirstRun());
+ if (first_space != trailing_space_run->Start()) {
+ BidiContext* base_context = Context();
+ while (BidiContext* parent = base_context->Parent())
+ base_context = parent;
+
+ trailing_space_run_ = AddTrailingRun(
+ runs, first_space, trailing_space_run->stop_, trailing_space_run,
+ base_context, paragraph_directionality_);
+ DCHECK(trailing_space_run_);
+ trailing_space_run->stop_ = first_space;
+ return;
+ }
+ if (!should_reorder) {
+ trailing_space_run_ = trailing_space_run;
+ return;
+ }
+
+ // Apply L1 rule.
+ if (paragraph_directionality_ == TextDirection::kLtr) {
+ runs.MoveRunToEnd(trailing_space_run);
+ trailing_space_run->level_ = 0;
+ } else {
+ runs.MoveRunToBeginning(trailing_space_run);
+ trailing_space_run->level_ = 1;
+ }
+ trailing_space_run_ = trailing_space_run;
+}
+
+template <class Iterator, class Run, class IsolatedRun>
+bool BidiResolver<Iterator, Run, IsolatedRun>::CommitExplicitEmbedding(
+ BidiRunList<Run>& runs) {
+ // When we're "inIsolate()" we're resolving the parent context which
+ // ignores (skips over) the isolated content, including embedding levels.
+ // We should never accrue embedding levels while skipping over isolated
+ // content.
+ DCHECK(!InIsolate() || current_explicit_embedding_sequence_.IsEmpty());
+
+ unsigned char from_level = Context()->Level();
+ scoped_refptr<BidiContext> to_context = Context();
+
+ for (size_t i = 0; i < current_explicit_embedding_sequence_.size(); ++i) {
+ BidiEmbedding embedding = current_explicit_embedding_sequence_[i];
+ if (embedding.Direction() == WTF::Unicode::kPopDirectionalFormat) {
+ if (BidiContext* parent_context = to_context->Parent())
+ to_context = parent_context;
+ } else {
+ WTF::Unicode::CharDirection direction =
+ (embedding.Direction() == WTF::Unicode::kRightToLeftEmbedding ||
+ embedding.Direction() == WTF::Unicode::kRightToLeftOverride)
+ ? WTF::Unicode::kRightToLeft
+ : WTF::Unicode::kLeftToRight;
+ bool override =
+ embedding.Direction() == WTF::Unicode::kLeftToRightOverride ||
+ embedding.Direction() == WTF::Unicode::kRightToLeftOverride;
+ unsigned char level = to_context->Level();
+ if (direction == WTF::Unicode::kRightToLeft)
+ level = NextGreaterOddLevel(level);
+ else
+ level = NextGreaterEvenLevel(level);
+ if (level < BidiContext::kMaxLevel) {
+ to_context = BidiContext::Create(level, direction, override,
+ embedding.Source(), to_context.get());
+ }
+ }
+ }
+
+ unsigned char to_level = to_context->Level();
+
+ if (to_level > from_level) {
+ RaiseExplicitEmbeddingLevel(
+ runs,
+ from_level % 2 ? WTF::Unicode::kRightToLeft
+ : WTF::Unicode::kLeftToRight,
+ to_level % 2 ? WTF::Unicode::kRightToLeft : WTF::Unicode::kLeftToRight);
+ } else if (to_level < from_level) {
+ LowerExplicitEmbeddingLevel(runs, from_level % 2
+ ? WTF::Unicode::kRightToLeft
+ : WTF::Unicode::kLeftToRight);
+ }
+
+ SetContext(to_context);
+
+ current_explicit_embedding_sequence_.clear();
+
+ return from_level != to_level;
+}
+
+template <class Iterator, class Run, class IsolatedRun>
+inline void
+BidiResolver<Iterator, Run, IsolatedRun>::UpdateStatusLastFromCurrentDirection(
+ WTF::Unicode::CharDirection dir_current) {
+ switch (dir_current) {
+ case WTF::Unicode::kEuropeanNumberTerminator:
+ if (status_.last != WTF::Unicode::kEuropeanNumber)
+ status_.last = WTF::Unicode::kEuropeanNumberTerminator;
+ break;
+ case WTF::Unicode::kEuropeanNumberSeparator:
+ case WTF::Unicode::kCommonNumberSeparator:
+ case WTF::Unicode::kSegmentSeparator:
+ case WTF::Unicode::kWhiteSpaceNeutral:
+ case WTF::Unicode::kOtherNeutral:
+ switch (status_.last) {
+ case WTF::Unicode::kLeftToRight:
+ case WTF::Unicode::kRightToLeft:
+ case WTF::Unicode::kRightToLeftArabic:
+ case WTF::Unicode::kEuropeanNumber:
+ case WTF::Unicode::kArabicNumber:
+ status_.last = dir_current;
+ break;
+ default:
+ status_.last = WTF::Unicode::kOtherNeutral;
+ }
+ break;
+ case WTF::Unicode::kNonSpacingMark:
+ case WTF::Unicode::kBoundaryNeutral:
+ case WTF::Unicode::kRightToLeftEmbedding:
+ case WTF::Unicode::kLeftToRightEmbedding:
+ case WTF::Unicode::kRightToLeftOverride:
+ case WTF::Unicode::kLeftToRightOverride:
+ case WTF::Unicode::kPopDirectionalFormat:
+ // ignore these
+ break;
+ case WTF::Unicode::kEuropeanNumber:
+ // fall through
+ default:
+ status_.last = dir_current;
+ }
+}
+
+template <class Iterator, class Run, class IsolatedRun>
+inline void BidiResolver<Iterator, Run, IsolatedRun>::ReorderRunsFromLevels(
+ BidiRunList<Run>& runs) const {
+ unsigned char level_low = BidiContext::kMaxLevel;
+ unsigned char level_high = 0;
+ for (Run* run = runs.FirstRun(); run; run = run->Next()) {
+ level_high = std::max(run->Level(), level_high);
+ level_low = std::min(run->Level(), level_low);
+ }
+
+ // This implements reordering of the line (L2 according to Bidi spec):
+ // http://unicode.org/reports/tr9/#L2
+ // L2. From the highest level found in the text to the lowest odd level on
+ // each line, reverse any contiguous sequence of characters that are at that
+ // level or higher.
+
+ // Reversing is only done up to the lowest odd level.
+ if (!(level_low % 2))
+ level_low++;
+
+ unsigned count = runs.RunCount() - 1;
+
+ while (level_high >= level_low) {
+ unsigned i = 0;
+ Run* run = runs.FirstRun();
+ while (i < count) {
+ for (; i < count && run && run->Level() < level_high; i++)
+ run = run->Next();
+ unsigned start = i;
+ for (; i <= count && run && run->Level() >= level_high; i++)
+ run = run->Next();
+ unsigned end = i - 1;
+ runs.ReverseRuns(start, end);
+ }
+ level_high--;
+ }
+}
+
+template <class Iterator, class Run, class IsolatedRun>
+TextDirection
+BidiResolver<Iterator, Run, IsolatedRun>::DetermineDirectionalityInternal(
+ bool break_on_paragraph,
+ bool* has_strong_directionality) {
+ while (!current_.AtEnd()) {
+ if (InIsolate()) {
+ Increment();
+ continue;
+ }
+ if (break_on_paragraph && current_.AtParagraphSeparator())
+ break;
+ UChar32 current = current_.Current();
+ if (UNLIKELY(U16_IS_SURROGATE(current))) {
+ Increment();
+ // If this not the high part of the surrogate pair, then drop it and move
+ // to the next.
+ if (!U16_IS_SURROGATE_LEAD(current))
+ continue;
+ UChar high = static_cast<UChar>(current);
+ if (current_.AtEnd())
+ continue;
+ UChar low = current_.Current();
+ // Verify the low part. If invalid, then assume an invalid surrogate pair
+ // and retry.
+ if (!U16_IS_TRAIL(low))
+ continue;
+ current = U16_GET_SUPPLEMENTARY(high, low);
+ }
+ WTF::Unicode::CharDirection char_direction =
+ WTF::Unicode::Direction(current);
+ if (char_direction == WTF::Unicode::kLeftToRight) {
+ if (has_strong_directionality)
+ *has_strong_directionality = true;
+ return TextDirection::kLtr;
+ }
+ if (char_direction == WTF::Unicode::kRightToLeft ||
+ char_direction == WTF::Unicode::kRightToLeftArabic) {
+ if (has_strong_directionality)
+ *has_strong_directionality = true;
+ return TextDirection::kRtl;
+ }
+ Increment();
+ }
+ if (has_strong_directionality)
+ *has_strong_directionality = false;
+ return TextDirection::kLtr;
+}
+
+inline TextDirection DirectionForCharacter(UChar32 character) {
+ WTF::Unicode::CharDirection char_direction =
+ WTF::Unicode::Direction(character);
+ if (char_direction == WTF::Unicode::kRightToLeft ||
+ char_direction == WTF::Unicode::kRightToLeftArabic)
+ return TextDirection::kRtl;
+ return TextDirection::kLtr;
+}
+
+template <class Iterator, class Run, class IsolatedRun>
+void BidiResolver<Iterator, Run, IsolatedRun>::CreateBidiRunsForLine(
+ const Iterator& end,
+ VisualDirectionOverride override,
+ bool hard_line_break,
+ bool reorder_runs) {
+ DCHECK_EQ(direction_, WTF::Unicode::kOtherNeutral);
+ trailing_space_run_ = nullptr;
+
+ end_of_line_ = end;
+
+ if (override != kNoVisualOverride) {
+ empty_run_ = false;
+ sor_ = current_;
+ eor_ = Iterator();
+ while (current_ != end && !current_.AtEnd()) {
+ eor_ = current_;
+ Increment();
+ }
+ direction_ = override == kVisualLeftToRightOverride
+ ? WTF::Unicode::kLeftToRight
+ : WTF::Unicode::kRightToLeft;
+ AppendRun(runs_);
+ runs_.SetLogicallyLastRun(runs_.LastRun());
+ if (override == kVisualRightToLeftOverride && runs_.RunCount())
+ runs_.ReverseRuns(0, runs_.RunCount() - 1);
+ return;
+ }
+
+ empty_run_ = true;
+
+ eor_ = Iterator();
+
+ last_ = current_;
+ bool last_line_ended = false;
+ BidiResolver<Iterator, Run, IsolatedRun> state_at_end;
+
+ while (true) {
+ if (InIsolate() && empty_run_) {
+ sor_ = current_;
+ empty_run_ = false;
+ }
+
+ if (!last_line_ended && IsEndOfLine(end)) {
+ if (empty_run_)
+ break;
+
+ state_at_end.status_ = status_;
+ state_at_end.sor_ = sor_;
+ state_at_end.eor_ = eor_;
+ state_at_end.last_ = last_;
+ state_at_end.reached_end_of_line_ = reached_end_of_line_;
+ state_at_end.last_before_et_ = last_before_et_;
+ state_at_end.empty_run_ = empty_run_;
+ end_of_run_at_end_of_line_ = last_;
+ last_line_ended = true;
+ }
+ WTF::Unicode::CharDirection dir_current;
+ if (last_line_ended && (hard_line_break || current_.AtEnd())) {
+ BidiContext* c = Context();
+ if (hard_line_break) {
+ // A deviation from the Unicode Bidi Algorithm in order to match
+ // WinIE and user expectations: hard line breaks reset bidi state
+ // coming from unicode bidi control characters, but not those from
+ // DOM nodes with specified directionality
+ state_at_end.SetContext(c->CopyStackRemovingUnicodeEmbeddingContexts());
+
+ dir_current = state_at_end.Context()->Dir();
+ state_at_end.SetEorDir(dir_current);
+ state_at_end.SetLastDir(dir_current);
+ state_at_end.SetLastStrongDir(dir_current);
+ } else {
+ while (c->Parent())
+ c = c->Parent();
+ dir_current = c->Dir();
+ }
+ } else {
+ dir_current = current_.Direction();
+ if (Context()->Override() &&
+ dir_current != WTF::Unicode::kRightToLeftEmbedding &&
+ dir_current != WTF::Unicode::kLeftToRightEmbedding &&
+ dir_current != WTF::Unicode::kRightToLeftOverride &&
+ dir_current != WTF::Unicode::kLeftToRightOverride &&
+ dir_current != WTF::Unicode::kPopDirectionalFormat)
+ dir_current = Context()->Dir();
+ else if (dir_current == WTF::Unicode::kNonSpacingMark)
+ dir_current = status_.last;
+ }
+
+ // We ignore all character directionality while in unicode-bidi: isolate
+ // spans. We'll handle ordering the isolated characters in a second pass.
+ if (InIsolate())
+ dir_current = WTF::Unicode::kOtherNeutral;
+
+ DCHECK(status_.eor != WTF::Unicode::kOtherNeutral || eor_.AtEnd());
+ switch (dir_current) {
+ // embedding and overrides (X1-X9 in the Bidi specs)
+ case WTF::Unicode::kRightToLeftEmbedding:
+ case WTF::Unicode::kLeftToRightEmbedding:
+ case WTF::Unicode::kRightToLeftOverride:
+ case WTF::Unicode::kLeftToRightOverride:
+ case WTF::Unicode::kPopDirectionalFormat:
+ Embed(dir_current, kFromUnicode);
+ CommitExplicitEmbedding(runs_);
+ break;
+
+ // strong types
+ case WTF::Unicode::kLeftToRight:
+ switch (status_.last) {
+ case WTF::Unicode::kRightToLeft:
+ case WTF::Unicode::kRightToLeftArabic:
+ case WTF::Unicode::kEuropeanNumber:
+ case WTF::Unicode::kArabicNumber:
+ if (status_.last != WTF::Unicode::kEuropeanNumber ||
+ status_.last_strong != WTF::Unicode::kLeftToRight)
+ AppendRun(runs_);
+ break;
+ case WTF::Unicode::kLeftToRight:
+ break;
+ case WTF::Unicode::kEuropeanNumberSeparator:
+ case WTF::Unicode::kEuropeanNumberTerminator:
+ case WTF::Unicode::kCommonNumberSeparator:
+ case WTF::Unicode::kBoundaryNeutral:
+ case WTF::Unicode::kBlockSeparator:
+ case WTF::Unicode::kSegmentSeparator:
+ case WTF::Unicode::kWhiteSpaceNeutral:
+ case WTF::Unicode::kOtherNeutral:
+ if (status_.eor == WTF::Unicode::kEuropeanNumber) {
+ if (status_.last_strong != WTF::Unicode::kLeftToRight) {
+ // the numbers need to be on a higher embedding level, so let's
+ // close that run
+ direction_ = WTF::Unicode::kEuropeanNumber;
+ AppendRun(runs_);
+ if (Context()->Dir() != WTF::Unicode::kLeftToRight) {
+ // the neutrals take the embedding direction, which is R
+ eor_ = last_;
+ direction_ = WTF::Unicode::kRightToLeft;
+ AppendRun(runs_);
+ }
+ }
+ } else if (status_.eor == WTF::Unicode::kArabicNumber) {
+ // Arabic numbers are always on a higher embedding level, so let's
+ // close that run
+ direction_ = WTF::Unicode::kArabicNumber;
+ AppendRun(runs_);
+ if (Context()->Dir() != WTF::Unicode::kLeftToRight) {
+ // the neutrals take the embedding direction, which is R
+ eor_ = last_;
+ direction_ = WTF::Unicode::kRightToLeft;
+ AppendRun(runs_);
+ }
+ } else if (status_.last_strong != WTF::Unicode::kLeftToRight) {
+ // last stuff takes embedding dir
+ if (Context()->Dir() == WTF::Unicode::kRightToLeft) {
+ eor_ = last_;
+ direction_ = WTF::Unicode::kRightToLeft;
+ }
+ AppendRun(runs_);
+ }
+ break;
+ default:
+ break;
+ }
+ eor_ = current_;
+ status_.eor = WTF::Unicode::kLeftToRight;
+ status_.last_strong = WTF::Unicode::kLeftToRight;
+ direction_ = WTF::Unicode::kLeftToRight;
+ break;
+ case WTF::Unicode::kRightToLeftArabic:
+ case WTF::Unicode::kRightToLeft:
+ switch (status_.last) {
+ case WTF::Unicode::kLeftToRight:
+ case WTF::Unicode::kEuropeanNumber:
+ case WTF::Unicode::kArabicNumber:
+ AppendRun(runs_);
+ break;
+ case WTF::Unicode::kRightToLeft:
+ case WTF::Unicode::kRightToLeftArabic:
+ break;
+ case WTF::Unicode::kEuropeanNumberSeparator:
+ case WTF::Unicode::kEuropeanNumberTerminator:
+ case WTF::Unicode::kCommonNumberSeparator:
+ case WTF::Unicode::kBoundaryNeutral:
+ case WTF::Unicode::kBlockSeparator:
+ case WTF::Unicode::kSegmentSeparator:
+ case WTF::Unicode::kWhiteSpaceNeutral:
+ case WTF::Unicode::kOtherNeutral:
+ if (status_.eor == WTF::Unicode::kEuropeanNumber) {
+ if (status_.last_strong == WTF::Unicode::kLeftToRight &&
+ Context()->Dir() == WTF::Unicode::kLeftToRight)
+ eor_ = last_;
+ AppendRun(runs_);
+ } else if (status_.eor == WTF::Unicode::kArabicNumber) {
+ AppendRun(runs_);
+ } else if (status_.last_strong == WTF::Unicode::kLeftToRight) {
+ if (Context()->Dir() == WTF::Unicode::kLeftToRight)
+ eor_ = last_;
+ AppendRun(runs_);
+ }
+ break;
+ default:
+ break;
+ }
+ eor_ = current_;
+ status_.eor = WTF::Unicode::kRightToLeft;
+ status_.last_strong = dir_current;
+ direction_ = WTF::Unicode::kRightToLeft;
+ break;
+
+ // weak types:
+
+ case WTF::Unicode::kEuropeanNumber:
+ // If last_strong is kRightToLeftArabic, change kEuropeanNumber to
+ // kArabicNumber by falling through after this if.
+ if (status_.last_strong != WTF::Unicode::kRightToLeftArabic) {
+ switch (status_.last) {
+ case WTF::Unicode::kEuropeanNumber:
+ case WTF::Unicode::kLeftToRight:
+ break;
+ case WTF::Unicode::kRightToLeft:
+ case WTF::Unicode::kRightToLeftArabic:
+ case WTF::Unicode::kArabicNumber:
+ eor_ = last_;
+ AppendRun(runs_);
+ direction_ = WTF::Unicode::kEuropeanNumber;
+ break;
+ case WTF::Unicode::kEuropeanNumberSeparator:
+ case WTF::Unicode::kCommonNumberSeparator:
+ if (status_.eor == WTF::Unicode::kEuropeanNumber)
+ break;
+ FALLTHROUGH;
+ case WTF::Unicode::kEuropeanNumberTerminator:
+ case WTF::Unicode::kBoundaryNeutral:
+ case WTF::Unicode::kBlockSeparator:
+ case WTF::Unicode::kSegmentSeparator:
+ case WTF::Unicode::kWhiteSpaceNeutral:
+ case WTF::Unicode::kOtherNeutral:
+ if (status_.eor == WTF::Unicode::kEuropeanNumber) {
+ if (status_.last_strong == WTF::Unicode::kRightToLeft) {
+ // ENs on both sides behave like Rs, so the neutrals should be
+ // R. Terminate the EN run.
+ AppendRun(runs_);
+ // Make an R run.
+ eor_ = status_.last == WTF::Unicode::kEuropeanNumberTerminator
+ ? last_before_et_
+ : last_;
+ direction_ = WTF::Unicode::kRightToLeft;
+ AppendRun(runs_);
+ // Begin a new EN run.
+ direction_ = WTF::Unicode::kEuropeanNumber;
+ }
+ } else if (status_.eor == WTF::Unicode::kArabicNumber) {
+ // Terminate the AN run.
+ AppendRun(runs_);
+ if (status_.last_strong == WTF::Unicode::kRightToLeft ||
+ Context()->Dir() == WTF::Unicode::kRightToLeft) {
+ // Make an R run.
+ eor_ = status_.last == WTF::Unicode::kEuropeanNumberTerminator
+ ? last_before_et_
+ : last_;
+ direction_ = WTF::Unicode::kRightToLeft;
+ AppendRun(runs_);
+ // Begin a new EN run.
+ direction_ = WTF::Unicode::kEuropeanNumber;
+ }
+ } else if (status_.last_strong == WTF::Unicode::kRightToLeft) {
+ // Extend the R run to include the neutrals.
+ eor_ = status_.last == WTF::Unicode::kEuropeanNumberTerminator
+ ? last_before_et_
+ : last_;
+ direction_ = WTF::Unicode::kRightToLeft;
+ AppendRun(runs_);
+ // Begin a new EN run.
+ direction_ = WTF::Unicode::kEuropeanNumber;
+ }
+ break;
+ default:
+ break;
+ }
+ eor_ = current_;
+ status_.eor = WTF::Unicode::kEuropeanNumber;
+ if (direction_ == WTF::Unicode::kOtherNeutral)
+ direction_ = WTF::Unicode::kLeftToRight;
+ break;
+ }
+ FALLTHROUGH;
+ case WTF::Unicode::kArabicNumber:
+ dir_current = WTF::Unicode::kArabicNumber;
+ switch (status_.last) {
+ case WTF::Unicode::kLeftToRight:
+ if (Context()->Dir() == WTF::Unicode::kLeftToRight)
+ AppendRun(runs_);
+ break;
+ case WTF::Unicode::kArabicNumber:
+ break;
+ case WTF::Unicode::kRightToLeft:
+ case WTF::Unicode::kRightToLeftArabic:
+ case WTF::Unicode::kEuropeanNumber:
+ eor_ = last_;
+ AppendRun(runs_);
+ break;
+ case WTF::Unicode::kCommonNumberSeparator:
+ if (status_.eor == WTF::Unicode::kArabicNumber)
+ break;
+ FALLTHROUGH;
+ case WTF::Unicode::kEuropeanNumberSeparator:
+ case WTF::Unicode::kEuropeanNumberTerminator:
+ case WTF::Unicode::kBoundaryNeutral:
+ case WTF::Unicode::kBlockSeparator:
+ case WTF::Unicode::kSegmentSeparator:
+ case WTF::Unicode::kWhiteSpaceNeutral:
+ case WTF::Unicode::kOtherNeutral:
+ if (status_.eor == WTF::Unicode::kArabicNumber ||
+ (status_.eor == WTF::Unicode::kEuropeanNumber &&
+ (status_.last_strong == WTF::Unicode::kRightToLeft ||
+ Context()->Dir() == WTF::Unicode::kRightToLeft)) ||
+ (status_.eor != WTF::Unicode::kEuropeanNumber &&
+ status_.last_strong == WTF::Unicode::kLeftToRight &&
+ Context()->Dir() == WTF::Unicode::kRightToLeft)) {
+ // Terminate the run before the neutrals.
+ AppendRun(runs_);
+ // Begin an R run for the neutrals.
+ direction_ = WTF::Unicode::kRightToLeft;
+ } else if (direction_ == WTF::Unicode::kOtherNeutral) {
+ direction_ = status_.last_strong == WTF::Unicode::kLeftToRight
+ ? WTF::Unicode::kLeftToRight
+ : WTF::Unicode::kRightToLeft;
+ }
+ eor_ = last_;
+ AppendRun(runs_);
+ break;
+ default:
+ break;
+ }
+ eor_ = current_;
+ status_.eor = WTF::Unicode::kArabicNumber;
+ if (direction_ == WTF::Unicode::kOtherNeutral)
+ direction_ = WTF::Unicode::kArabicNumber;
+ break;
+ case WTF::Unicode::kEuropeanNumberSeparator:
+ case WTF::Unicode::kCommonNumberSeparator:
+ break;
+ case WTF::Unicode::kEuropeanNumberTerminator:
+ if (status_.last == WTF::Unicode::kEuropeanNumber) {
+ dir_current = WTF::Unicode::kEuropeanNumber;
+ eor_ = current_;
+ status_.eor = dir_current;
+ } else if (status_.last != WTF::Unicode::kEuropeanNumberTerminator) {
+ last_before_et_ = empty_run_ ? eor_ : last_;
+ }
+ break;
+
+ // boundary neutrals should be ignored
+ case WTF::Unicode::kBoundaryNeutral:
+ if (eor_ == last_)
+ eor_ = current_;
+ break;
+ // neutrals
+ case WTF::Unicode::kBlockSeparator:
+ // ### what do we do with newline and paragraph seperators that come to
+ // here?
+ break;
+ case WTF::Unicode::kSegmentSeparator:
+ // ### implement rule L1
+ break;
+ case WTF::Unicode::kWhiteSpaceNeutral:
+ break;
+ case WTF::Unicode::kOtherNeutral:
+ break;
+ default:
+ break;
+ }
+
+ if (last_line_ended && eor_ == current_) {
+ if (!reached_end_of_line_) {
+ eor_ = end_of_run_at_end_of_line_;
+ switch (status_.eor) {
+ case WTF::Unicode::kLeftToRight:
+ case WTF::Unicode::kRightToLeft:
+ case WTF::Unicode::kArabicNumber:
+ direction_ = status_.eor;
+ break;
+ case WTF::Unicode::kEuropeanNumber:
+ direction_ = status_.last_strong == WTF::Unicode::kLeftToRight
+ ? WTF::Unicode::kLeftToRight
+ : WTF::Unicode::kEuropeanNumber;
+ break;
+ default:
+ NOTREACHED();
+ }
+ AppendRun(runs_);
+ }
+ current_ = end;
+ status_ = state_at_end.status_;
+ sor_ = state_at_end.sor_;
+ eor_ = state_at_end.eor_;
+ last_ = state_at_end.last_;
+ reached_end_of_line_ = state_at_end.reached_end_of_line_;
+ last_before_et_ = state_at_end.last_before_et_;
+ empty_run_ = state_at_end.empty_run_;
+ direction_ = WTF::Unicode::kOtherNeutral;
+ break;
+ }
+
+ UpdateStatusLastFromCurrentDirection(dir_current);
+ last_ = current_;
+
+ if (empty_run_) {
+ sor_ = current_;
+ empty_run_ = false;
+ }
+
+ Increment();
+ if (!current_explicit_embedding_sequence_.IsEmpty()) {
+ bool committed = CommitExplicitEmbedding(runs_);
+ if (committed && last_line_ended) {
+ current_ = end;
+ status_ = state_at_end.status_;
+ sor_ = state_at_end.sor_;
+ eor_ = state_at_end.eor_;
+ last_ = state_at_end.last_;
+ reached_end_of_line_ = state_at_end.reached_end_of_line_;
+ last_before_et_ = state_at_end.last_before_et_;
+ empty_run_ = state_at_end.empty_run_;
+ direction_ = WTF::Unicode::kOtherNeutral;
+ break;
+ }
+ }
+ }
+
+ runs_.SetLogicallyLastRun(runs_.LastRun());
+ if (reorder_runs)
+ ReorderRunsFromLevels(runs_);
+ end_of_run_at_end_of_line_ = Iterator();
+ end_of_line_ = Iterator();
+
+ if (!hard_line_break && runs_.RunCount() && NeedsTrailingSpace(runs_)) {
+ ComputeTrailingSpace(runs_);
+ }
+}
+
+template <class Iterator, class Run, class IsolatedRun>
+void BidiResolver<Iterator, Run, IsolatedRun>::SetMidpointStateForIsolatedRun(
+ Run& run,
+ const MidpointState<Iterator>& midpoint) {
+ DCHECK(!midpoint_state_for_isolated_run_.Contains(&run));
+ midpoint_state_for_isolated_run_.insert(&run, midpoint);
+}
+
+template <class Iterator, class Run, class IsolatedRun>
+MidpointState<Iterator>
+BidiResolver<Iterator, Run, IsolatedRun>::MidpointStateForIsolatedRun(
+ Run& run) {
+ return midpoint_state_for_isolated_run_.Take(&run);
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_BIDI_RESOLVER_H_
diff --git a/chromium/third_party/blink/renderer/platform/text/bidi_resolver_test.cc b/chromium/third_party/blink/renderer/platform/text/bidi_resolver_test.cc
new file mode 100644
index 00000000000..4a66780d024
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/bidi_resolver_test.cc
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/renderer/platform/text/bidi_resolver.h"
+
+#include <fstream>
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/text/bidi_test_harness.h"
+#include "third_party/blink/renderer/platform/text/text_run_iterator.h"
+
+namespace blink {
+
+TEST(BidiResolver, Basic) {
+ bool has_strong_directionality;
+ String value("foo");
+ TextRun run(value);
+ BidiResolver<TextRunIterator, BidiCharacterRun> bidi_resolver;
+ bidi_resolver.SetStatus(
+ BidiStatus(run.Direction(), run.DirectionalOverride()));
+ bidi_resolver.SetPositionIgnoringNestedIsolates(TextRunIterator(&run, 0));
+ TextDirection direction = bidi_resolver.DetermineParagraphDirectionality(
+ &has_strong_directionality);
+ EXPECT_TRUE(has_strong_directionality);
+ EXPECT_EQ(TextDirection::kLtr, direction);
+}
+
+TextDirection DetermineParagraphDirectionality(
+ const TextRun& text_run,
+ bool* has_strong_directionality = nullptr) {
+ BidiResolver<TextRunIterator, BidiCharacterRun> resolver;
+ resolver.SetStatus(BidiStatus(TextDirection::kLtr, false));
+ resolver.SetPositionIgnoringNestedIsolates(TextRunIterator(&text_run, 0));
+ return resolver.DetermineParagraphDirectionality(has_strong_directionality);
+}
+
+struct TestData {
+ UChar text[3];
+ size_t length;
+ TextDirection expected_direction;
+ bool expected_strong;
+};
+
+void TestDirectionality(const TestData& entry) {
+ bool has_strong_directionality;
+ String data(entry.text, entry.length);
+ TextRun run(data);
+ TextDirection direction =
+ DetermineParagraphDirectionality(run, &has_strong_directionality);
+ EXPECT_EQ(entry.expected_strong, has_strong_directionality);
+ EXPECT_EQ(entry.expected_direction, direction);
+}
+
+TEST(BidiResolver, ParagraphDirectionSurrogates) {
+ const TestData kTestData[] = {
+ // Test strong RTL, non-BMP. (U+10858 Imperial
+ // Aramaic number one, strong RTL)
+ {{0xD802, 0xDC58}, 2, TextDirection::kRtl, true},
+
+ // Test strong LTR, non-BMP. (U+1D15F Musical
+ // symbol quarter note, strong LTR)
+ {{0xD834, 0xDD5F}, 2, TextDirection::kLtr, true},
+
+ // Test broken surrogate: valid leading, invalid
+ // trail. (Lead of U+10858, space)
+ {{0xD802, ' '}, 2, TextDirection::kLtr, false},
+
+ // Test broken surrogate: invalid leading. (Trail
+ // of U+10858, U+05D0 Hebrew Alef)
+ {{0xDC58, 0x05D0}, 2, TextDirection::kRtl, true},
+
+ // Test broken surrogate: valid leading, invalid
+ // trail/valid lead, valid trail.
+ {{0xD802, 0xD802, 0xDC58}, 3, TextDirection::kRtl, true},
+
+ // Test broken surrogate: valid leading, no trail
+ // (string too short). (Lead of U+10858)
+ {{0xD802, 0xDC58}, 1, TextDirection::kLtr, false},
+
+ // Test broken surrogate: trail appearing before
+ // lead. (U+10858 units reversed)
+ {{0xDC58, 0xD802}, 2, TextDirection::kLtr, false}};
+ for (size_t i = 0; i < WTF_ARRAY_LENGTH(kTestData); ++i)
+ TestDirectionality(kTestData[i]);
+}
+
+class BidiTestRunner {
+ public:
+ BidiTestRunner()
+ : tests_run_(0),
+ tests_skipped_(0),
+ ignored_char_failures_(0),
+ level_failures_(0),
+ order_failures_(0) {}
+
+ void SkipTestsWith(UChar codepoint) {
+ skipped_code_points_.insert(codepoint);
+ }
+
+ void RunTest(const std::basic_string<UChar>& input,
+ const std::vector<int>& reorder,
+ const std::vector<int>& levels,
+ bidi_test::ParagraphDirection,
+ const std::string& line,
+ size_t line_number);
+
+ size_t tests_run_;
+ size_t tests_skipped_;
+ std::set<UChar> skipped_code_points_;
+ size_t ignored_char_failures_;
+ size_t level_failures_;
+ size_t order_failures_;
+};
+
+// Blink's UBA does not filter out control characters, etc. Maybe it should?
+// Instead it depends on later layers of Blink to simply ignore them.
+// This function helps us emulate that to be compatible with BidiTest.txt
+// expectations.
+static bool IsNonRenderedCodePoint(UChar c) {
+ // The tests also expect us to ignore soft-hyphen.
+ if (c == 0xAD)
+ return true;
+ // Control characters are not rendered:
+ return c >= 0x202A && c <= 0x202E;
+ // But it seems to expect LRI, etc. to be rendered!?
+}
+
+std::string DiffString(const std::vector<int>& actual,
+ const std::vector<int>& expected) {
+ std::ostringstream diff;
+ diff << "actual: ";
+ // This is the magical way to print a vector to a stream, clear, right?
+ std::copy(actual.begin(), actual.end(),
+ std::ostream_iterator<int>(diff, " "));
+ diff << " expected: ";
+ std::copy(expected.begin(), expected.end(),
+ std::ostream_iterator<int>(diff, " "));
+ return diff.str();
+}
+
+void BidiTestRunner::RunTest(const std::basic_string<UChar>& input,
+ const std::vector<int>& expected_order,
+ const std::vector<int>& expected_levels,
+ bidi_test::ParagraphDirection paragraph_direction,
+ const std::string& line,
+ size_t line_number) {
+ if (!skipped_code_points_.empty()) {
+ for (size_t i = 0; i < input.size(); i++) {
+ if (skipped_code_points_.count(input[i])) {
+ tests_skipped_++;
+ return;
+ }
+ }
+ }
+
+ tests_run_++;
+
+ TextRun text_run(input.data(), input.size());
+ switch (paragraph_direction) {
+ case bidi_test::kDirectionAutoLTR:
+ text_run.SetDirection(DetermineParagraphDirectionality(text_run));
+ break;
+ case bidi_test::kDirectionLTR:
+ text_run.SetDirection(TextDirection::kLtr);
+ break;
+ case bidi_test::kDirectionRTL:
+ text_run.SetDirection(TextDirection::kRtl);
+ break;
+ default:
+ break;
+ }
+ BidiResolver<TextRunIterator, BidiCharacterRun> resolver;
+ resolver.SetStatus(
+ BidiStatus(text_run.Direction(), text_run.DirectionalOverride()));
+ resolver.SetPositionIgnoringNestedIsolates(TextRunIterator(&text_run, 0));
+
+ BidiRunList<BidiCharacterRun>& runs = resolver.Runs();
+ resolver.CreateBidiRunsForLine(TextRunIterator(&text_run, text_run.length()));
+
+ std::ostringstream error_context;
+ error_context << ", line " << line_number << " \"" << line << "\"";
+ error_context << " context: "
+ << bidi_test::NameFromParagraphDirection(paragraph_direction);
+
+ std::vector<int> actual_order;
+ std::vector<int> actual_levels;
+ actual_levels.assign(input.size(), -1);
+ BidiCharacterRun* run = runs.FirstRun();
+ while (run) {
+ // Blink's UBA just makes runs, the actual ordering of the display of
+ // characters is handled later in our pipeline, so we fake it here:
+ bool reversed = run->Reversed(false);
+ DCHECK_GE(run->Stop(), run->Start());
+ size_t length = run->Stop() - run->Start();
+ for (size_t i = 0; i < length; i++) {
+ int input_index = reversed ? run->Stop() - i - 1 : run->Start() + i;
+ if (!IsNonRenderedCodePoint(input[input_index]))
+ actual_order.push_back(input_index);
+ // BidiTest.txt gives expected level data in the order of the original
+ // input.
+ actual_levels[input_index] = run->Level();
+ }
+ run = run->Next();
+ }
+
+ if (expected_order.size() != actual_order.size()) {
+ ignored_char_failures_++;
+ EXPECT_EQ(expected_order.size(), actual_order.size())
+ << error_context.str();
+ } else if (expected_order != actual_order) {
+ order_failures_++;
+ printf("ORDER %s%s\n", DiffString(actual_order, expected_order).c_str(),
+ error_context.str().c_str());
+ }
+
+ if (expected_levels.size() != actual_levels.size()) {
+ ignored_char_failures_++;
+ EXPECT_EQ(expected_levels.size(), actual_levels.size())
+ << error_context.str();
+ } else {
+ for (size_t i = 0; i < expected_levels.size(); i++) {
+ // level == -1 means the level should be ignored.
+ if (expected_levels[i] == actual_levels[i] || expected_levels[i] == -1)
+ continue;
+
+ printf("LEVELS %s%s\n",
+ DiffString(actual_levels, expected_levels).c_str(),
+ error_context.str().c_str());
+ level_failures_++;
+ break;
+ }
+ }
+ runs.DeleteRuns();
+}
+
+TEST(BidiResolver, DISABLED_BidiTest_txt) {
+ BidiTestRunner runner;
+ // Blink's Unicode Bidi Algorithm (UBA) doesn't yet support the
+ // new isolate directives from Unicode 6.3:
+ // http://www.unicode.org/reports/tr9/#Explicit_Directional_Isolates
+ runner.SkipTestsWith(0x2066); // LRI
+ runner.SkipTestsWith(0x2067); // RLI
+ runner.SkipTestsWith(0x2068); // FSI
+ runner.SkipTestsWith(0x2069); // PDI
+
+ std::string bidi_test_path = "BidiTest.txt";
+ std::ifstream bidi_test_file(bidi_test_path.c_str());
+ EXPECT_TRUE(bidi_test_file.is_open());
+ bidi_test::Harness<BidiTestRunner> harness(runner);
+ harness.Parse(bidi_test_file);
+ bidi_test_file.close();
+
+ if (runner.tests_skipped_)
+ LOG(WARNING) << "WARNING: Skipped " << runner.tests_skipped_ << " tests.";
+ LOG(INFO) << "Ran " << runner.tests_run_
+ << " tests: " << runner.level_failures_ << " level failures "
+ << runner.order_failures_ << " order failures.";
+
+ // The unittest harness only pays attention to GTest output, so we verify
+ // that the tests behaved as expected:
+ EXPECT_EQ(352098u, runner.tests_run_);
+ EXPECT_EQ(418143u, runner.tests_skipped_);
+ EXPECT_EQ(0u, runner.ignored_char_failures_);
+ EXPECT_EQ(44882u, runner.level_failures_);
+ EXPECT_EQ(19151u, runner.order_failures_);
+}
+
+TEST(BidiResolver, DISABLED_BidiCharacterTest_txt) {
+ BidiTestRunner runner;
+ // Blink's Unicode Bidi Algorithm (UBA) doesn't yet support the
+ // new isolate directives from Unicode 6.3:
+ // http://www.unicode.org/reports/tr9/#Explicit_Directional_Isolates
+ runner.SkipTestsWith(0x2066); // LRI
+ runner.SkipTestsWith(0x2067); // RLI
+ runner.SkipTestsWith(0x2068); // FSI
+ runner.SkipTestsWith(0x2069); // PDI
+
+ std::string bidi_test_path = "BidiCharacterTest.txt";
+ std::ifstream bidi_test_file(bidi_test_path.c_str());
+ EXPECT_TRUE(bidi_test_file.is_open());
+ bidi_test::CharacterHarness<BidiTestRunner> harness(runner);
+ harness.Parse(bidi_test_file);
+ bidi_test_file.close();
+
+ if (runner.tests_skipped_)
+ LOG(WARNING) << "WARNING: Skipped " << runner.tests_skipped_ << " tests.";
+ LOG(INFO) << "Ran " << runner.tests_run_
+ << " tests: " << runner.level_failures_ << " level failures "
+ << runner.order_failures_ << " order failures.";
+
+ EXPECT_EQ(91660u, runner.tests_run_);
+ EXPECT_EQ(39u, runner.tests_skipped_);
+ EXPECT_EQ(0u, runner.ignored_char_failures_);
+ EXPECT_EQ(14533u, runner.level_failures_);
+ EXPECT_EQ(14533u, runner.order_failures_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/bidi_run_list.h b/chromium/third_party/blink/renderer/platform/text/bidi_run_list.h
new file mode 100644
index 00000000000..5935a10faf8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/bidi_run_list.h
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * Copyright (C) 2003, 2004, 2006, 2007, 2008 Apple Inc. All right reserved.
+ * Copyright (C) 2011 Google, Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_BIDI_RUN_LIST_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_BIDI_RUN_LIST_H_
+
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+template <class Run>
+class BidiRunList final {
+ DISALLOW_NEW();
+
+ public:
+ BidiRunList()
+ : first_run_(nullptr),
+ last_run_(nullptr),
+ logically_last_run_(nullptr),
+ run_count_(0) {}
+
+ // FIXME: Once BidiResolver no longer owns the BidiRunList,
+ // then ~BidiRunList should call deleteRuns() automatically.
+
+ Run* FirstRun() const { return first_run_; }
+ Run* LastRun() const { return last_run_; }
+ Run* LogicallyLastRun() const { return logically_last_run_; }
+ unsigned RunCount() const { return run_count_; }
+
+ void AddRun(Run*);
+ void PrependRun(Run*);
+
+ void MoveRunToEnd(Run*);
+ void MoveRunToBeginning(Run*);
+
+ void DeleteRuns();
+ void ReverseRuns(unsigned start, unsigned end);
+ void ReorderRunsFromLevels();
+
+ void SetLogicallyLastRun(Run* run) { logically_last_run_ = run; }
+
+ void ReplaceRunWithRuns(Run* to_replace, BidiRunList<Run>& new_runs);
+
+ private:
+ void ClearWithoutDestroyingRuns();
+
+ Run* first_run_;
+ Run* last_run_;
+ Run* logically_last_run_;
+ unsigned run_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(BidiRunList);
+};
+
+template <class Run>
+inline void BidiRunList<Run>::AddRun(Run* run) {
+ if (!first_run_)
+ first_run_ = run;
+ else
+ last_run_->next_ = run;
+ last_run_ = run;
+ run_count_++;
+}
+
+template <class Run>
+inline void BidiRunList<Run>::PrependRun(Run* run) {
+ DCHECK(!run->next_);
+
+ if (!last_run_)
+ last_run_ = run;
+ else
+ run->next_ = first_run_;
+ first_run_ = run;
+ run_count_++;
+}
+
+template <class Run>
+inline void BidiRunList<Run>::MoveRunToEnd(Run* run) {
+ DCHECK(first_run_);
+ DCHECK(last_run_);
+ DCHECK(run->next_);
+
+ Run* current = nullptr;
+ Run* next = first_run_;
+ while (next != run) {
+ current = next;
+ next = current->Next();
+ }
+
+ if (!current)
+ first_run_ = run->Next();
+ else
+ current->next_ = run->next_;
+
+ run->next_ = nullptr;
+ last_run_->next_ = run;
+ last_run_ = run;
+}
+
+template <class Run>
+inline void BidiRunList<Run>::MoveRunToBeginning(Run* run) {
+ DCHECK(first_run_);
+ DCHECK(last_run_);
+ DCHECK_NE(run, first_run_);
+
+ Run* current = first_run_;
+ Run* next = current->Next();
+ while (next != run) {
+ current = next;
+ next = current->Next();
+ }
+
+ current->next_ = run->next_;
+ if (run == last_run_)
+ last_run_ = current;
+
+ run->next_ = first_run_;
+ first_run_ = run;
+}
+
+template <class Run>
+void BidiRunList<Run>::ReplaceRunWithRuns(Run* to_replace,
+ BidiRunList<Run>& new_runs) {
+ DCHECK(new_runs.RunCount());
+ DCHECK(first_run_);
+ DCHECK(to_replace);
+
+ if (first_run_ == to_replace) {
+ first_run_ = new_runs.FirstRun();
+ } else {
+ // Find the run just before "toReplace" in the list of runs.
+ Run* previous_run = first_run_;
+ while (previous_run->Next() != to_replace)
+ previous_run = previous_run->Next();
+ DCHECK(previous_run);
+ previous_run->SetNext(new_runs.FirstRun());
+ }
+
+ new_runs.LastRun()->SetNext(to_replace->Next());
+
+ // Fix up any of other pointers which may now be stale.
+ if (last_run_ == to_replace)
+ last_run_ = new_runs.LastRun();
+ if (logically_last_run_ == to_replace)
+ logically_last_run_ = new_runs.LogicallyLastRun();
+ run_count_ +=
+ new_runs.RunCount() - 1; // We added the new runs and removed toReplace.
+
+ delete to_replace;
+ new_runs.ClearWithoutDestroyingRuns();
+}
+
+template <class Run>
+void BidiRunList<Run>::ClearWithoutDestroyingRuns() {
+ first_run_ = nullptr;
+ last_run_ = nullptr;
+ logically_last_run_ = nullptr;
+ run_count_ = 0;
+}
+
+template <class Run>
+void BidiRunList<Run>::DeleteRuns() {
+ if (!first_run_)
+ return;
+
+ Run* curr = first_run_;
+ while (curr) {
+ Run* s = curr->Next();
+ delete curr;
+ curr = s;
+ }
+
+ ClearWithoutDestroyingRuns();
+}
+
+template <class Run>
+void BidiRunList<Run>::ReverseRuns(unsigned start, unsigned end) {
+ DCHECK(run_count_);
+ if (start >= end)
+ return;
+
+ DCHECK_LT(end, run_count_);
+
+ // Get the item before the start of the runs to reverse and put it in
+ // |beforeStart|. |curr| should point to the first run to reverse.
+ Run* curr = first_run_;
+ Run* before_start = nullptr;
+ unsigned i = 0;
+ while (i < start) {
+ i++;
+ before_start = curr;
+ curr = curr->Next();
+ }
+
+ Run* start_run = curr;
+ while (i < end) {
+ i++;
+ curr = curr->Next();
+ }
+ Run* end_run = curr;
+ Run* after_end = curr->Next();
+
+ i = start;
+ curr = start_run;
+ Run* new_next = after_end;
+ while (i <= end) {
+ // Do the reversal.
+ Run* next = curr->Next();
+ curr->next_ = new_next;
+ new_next = curr;
+ curr = next;
+ i++;
+ }
+
+ // Now hook up beforeStart and afterEnd to the startRun and endRun.
+ if (before_start)
+ before_start->next_ = end_run;
+ else
+ first_run_ = end_run;
+
+ start_run->next_ = after_end;
+ if (!after_end)
+ last_run_ = start_run;
+}
+
+} // namespace blink
+
+#endif // BidiRunList
diff --git a/chromium/third_party/blink/renderer/platform/text/bidi_test_harness.h b/chromium/third_party/blink/renderer/platform/text/bidi_test_harness.h
new file mode 100644
index 00000000000..104d27d66fa
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/bidi_test_harness.h
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_BIDI_TEST_HARNESS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_BIDI_TEST_HARNESS_H_
+
+#include <istream>
+#include <map>
+#include <stdio.h>
+#include <string>
+#include <vector>
+
+// FIXME: We don't have any business owning this code. We should try to
+// upstream this to unicode.org if possible (for other implementations to use).
+// Unicode.org provides a reference implmentation, including parser:
+// http://www.unicode.org/Public/PROGRAMS/BidiReferenceC/6.3.0/source/brtest.c
+// But it, like the other implementations I've found, is rather tied to
+// the algorithms it is testing. This file seeks to only implement the parser
+// bits.
+
+// Other C/C++ implementations of this parser:
+// https://github.com/googlei18n/fribidi-vs-unicode/blob/master/test.c
+// http://source.icu-project.org/repos/icu/icu/trunk/source/test/intltest/bidiconf.cpp
+// Both of those are too tied to their respective projects to be use to Blink.
+
+// There are non-C implmentations to parse BidiTest.txt as well, including:
+// https://github.com/twitter/twitter-cldr-rb/blob/master/spec/bidi/bidi_spec.rb
+
+// NOTE: None of this file is currently written to be thread-safe.
+
+namespace bidi_test {
+
+enum ParagraphDirection {
+ kDirectionNone = 0,
+ kDirectionAutoLTR = 1,
+ kDirectionLTR = 2,
+ kDirectionRTL = 4,
+};
+const int kMaxParagraphDirection =
+ kDirectionAutoLTR | kDirectionLTR | kDirectionRTL;
+
+// For error printing:
+std::string NameFromParagraphDirection(ParagraphDirection paragraph_direction) {
+ switch (paragraph_direction) {
+ case bidi_test::kDirectionAutoLTR:
+ return "Auto-LTR";
+ case bidi_test::kDirectionLTR:
+ return "LTR";
+ case bidi_test::kDirectionRTL:
+ return "RTL";
+ default:
+ // This should never be reached.
+ return "";
+ }
+}
+
+template <class Runner>
+class Harness {
+ public:
+ Harness(Runner& runner) : runner_(runner) {}
+ void Parse(std::istream& bidi_test_file);
+
+ private:
+ Runner& runner_;
+};
+
+// We could use boost::trim, but no other part of Blink uses boost yet.
+inline void Ltrim(std::string& s) {
+ static const std::string kSeparators(" \t");
+ s.erase(0, s.find_first_not_of(kSeparators));
+}
+
+inline void Rtrim(std::string& s) {
+ static const std::string kSeparators(" \t");
+ size_t last_non_space = s.find_last_not_of(kSeparators);
+ if (last_non_space == std::string::npos) {
+ s.erase();
+ return;
+ }
+ size_t first_space_at_end_of_string = last_non_space + 1;
+ if (first_space_at_end_of_string >= s.size())
+ return; // lastNonSpace was the last char.
+ s.erase(first_space_at_end_of_string,
+ std::string::npos); // erase to the end of the string.
+}
+
+inline void Trim(std::string& s) {
+ Rtrim(s);
+ Ltrim(s);
+}
+
+static std::vector<std::string> ParseStringList(const std::string& str) {
+ std::vector<std::string> strings;
+ static const std::string kSeparators(" \t");
+ size_t last_pos = str.find_first_not_of(kSeparators); // skip leading spaces
+ size_t pos = str.find_first_of(kSeparators, last_pos); // find next space
+
+ while (std::string::npos != pos || std::string::npos != last_pos) {
+ strings.push_back(str.substr(last_pos, pos - last_pos));
+ last_pos = str.find_first_not_of(kSeparators, pos);
+ pos = str.find_first_of(kSeparators, last_pos);
+ }
+ return strings;
+}
+
+static int ParseInt(const std::string& str) {
+ return atoi(str.c_str());
+}
+
+static std::vector<int> ParseIntList(const std::string& str) {
+ std::vector<int> ints;
+ std::vector<std::string> strings = ParseStringList(str);
+ for (size_t x = 0; x < strings.size(); x++) {
+ int i = ParseInt(strings[x]);
+ ints.push_back(i);
+ }
+ return ints;
+}
+
+static std::vector<int> ParseLevels(const std::string& line) {
+ std::vector<int> levels;
+ std::vector<std::string> strings = ParseStringList(line);
+ for (size_t x = 0; x < strings.size(); x++) {
+ const std::string& level_string = strings[x];
+ int i;
+ if (level_string == "x")
+ i = -1;
+ else
+ i = ParseInt(level_string);
+ levels.push_back(i);
+ }
+ return levels;
+}
+
+// This is not thread-safe as written.
+static std::basic_string<UChar> ParseTestString(const std::string& line) {
+ std::basic_string<UChar> test_string;
+ static std::map<std::string, UChar> char_class_examples;
+ if (char_class_examples.empty()) {
+ // FIXME: Explicit make_pair is ugly, but required for C++98 compat.
+ char_class_examples.insert(std::make_pair("L", 0x6c)); // 'l' for L
+ char_class_examples.insert(std::make_pair("R", 0x05D0)); // HEBREW ALEF
+ char_class_examples.insert(std::make_pair("EN", 0x33)); // '3' for EN
+ char_class_examples.insert(std::make_pair("ES", 0x2d)); // '-' for ES
+ char_class_examples.insert(std::make_pair("ET", 0x25)); // '%' for ET
+ char_class_examples.insert(std::make_pair("AN", 0x0660)); // arabic 0
+ char_class_examples.insert(std::make_pair("CS", 0x2c)); // ',' for CS
+ char_class_examples.insert(std::make_pair("B", 0x0A)); // <control-000A>
+ char_class_examples.insert(std::make_pair("S", 0x09)); // <control-0009>
+ char_class_examples.insert(std::make_pair("WS", 0x20)); // ' ' for WS
+ char_class_examples.insert(std::make_pair("ON", 0x3d)); // '=' for ON
+ char_class_examples.insert(
+ std::make_pair("NSM", 0x05BF)); // HEBREW POINT RAFE
+ char_class_examples.insert(std::make_pair("AL", 0x0608)); // ARABIC RAY
+ char_class_examples.insert(std::make_pair("BN", 0x00AD)); // SOFT HYPHEN
+ char_class_examples.insert(std::make_pair("LRE", 0x202A));
+ char_class_examples.insert(std::make_pair("RLE", 0x202B));
+ char_class_examples.insert(std::make_pair("PDF", 0x202C));
+ char_class_examples.insert(std::make_pair("LRO", 0x202D));
+ char_class_examples.insert(std::make_pair("RLO", 0x202E));
+ char_class_examples.insert(std::make_pair("LRI", 0x2066));
+ char_class_examples.insert(std::make_pair("RLI", 0x2067));
+ char_class_examples.insert(std::make_pair("FSI", 0x2068));
+ char_class_examples.insert(std::make_pair("PDI", 0x2069));
+ }
+
+ std::vector<std::string> char_classes = ParseStringList(line);
+ for (size_t i = 0; i < char_classes.size(); i++) {
+ // FIXME: If the lookup failed we could return false for a parse error.
+ test_string.push_back(char_class_examples.find(char_classes[i])->second);
+ }
+ return test_string;
+}
+
+static bool ParseParagraphDirectionMask(const std::string& line,
+ int& mode_mask) {
+ mode_mask = ParseInt(line);
+ return mode_mask >= 1 && mode_mask <= kMaxParagraphDirection;
+}
+
+static void ParseError(const std::string& line, size_t line_number) {
+ // Use printf to avoid the expense of std::cout.
+ printf("Parse error, line %zu : %s\n", line_number, line.c_str());
+}
+
+template <class Runner>
+void Harness<Runner>::Parse(std::istream& bidi_test_file) {
+ static const std::string kLevelsPrefix("@Levels");
+ static const std::string kReorderPrefix("@Reorder");
+
+ // FIXME: UChar is an ICU type and cheating a bit to use here.
+ // uint16_t might be more portable.
+ std::basic_string<UChar> test_string;
+ std::vector<int> levels;
+ std::vector<int> reorder;
+ int paragraph_direction_mask;
+
+ std::string line;
+ size_t line_number = 0;
+ while (std::getline(bidi_test_file, line)) {
+ line_number++;
+ const std::string original_line = line;
+ size_t comment_start = line.find_first_of('#');
+ if (comment_start != std::string::npos)
+ line = line.substr(0, comment_start);
+ Trim(line);
+ if (line.empty())
+ continue;
+ if (line[0] == '@') {
+ if (!line.find(kLevelsPrefix)) {
+ levels = ParseLevels(line.substr(kLevelsPrefix.length() + 1));
+ continue;
+ }
+ if (!line.find(kReorderPrefix)) {
+ reorder = ParseIntList(line.substr(kReorderPrefix.length() + 1));
+ continue;
+ }
+ } else {
+ // Assume it's a data line.
+ size_t seperator_index = line.find_first_of(';');
+ if (seperator_index == std::string::npos) {
+ ParseError(original_line, line_number);
+ continue;
+ }
+ test_string = ParseTestString(line.substr(0, seperator_index));
+ if (!ParseParagraphDirectionMask(line.substr(seperator_index + 1),
+ paragraph_direction_mask)) {
+ ParseError(original_line, line_number);
+ continue;
+ }
+
+ if (paragraph_direction_mask & kDirectionAutoLTR) {
+ runner_.RunTest(test_string, reorder, levels, kDirectionAutoLTR,
+ original_line, line_number);
+ }
+ if (paragraph_direction_mask & kDirectionLTR) {
+ runner_.RunTest(test_string, reorder, levels, kDirectionLTR,
+ original_line, line_number);
+ }
+ if (paragraph_direction_mask & kDirectionRTL) {
+ runner_.RunTest(test_string, reorder, levels, kDirectionRTL,
+ original_line, line_number);
+ }
+ }
+ }
+}
+
+template <class Runner>
+class CharacterHarness {
+ public:
+ CharacterHarness(Runner& runner) : runner_(runner) {}
+ void Parse(std::istream& bidi_test_file);
+
+ private:
+ Runner& runner_;
+};
+
+static std::basic_string<UChar> ParseUCharHexadecimalList(
+ const std::string& str) {
+ std::basic_string<UChar> string;
+ std::vector<std::string> strings = ParseStringList(str);
+ for (size_t x = 0; x < strings.size(); x++) {
+ int i = strtol(strings[x].c_str(), nullptr, 16);
+ string.push_back((UChar)i);
+ }
+ return string;
+}
+
+static ParagraphDirection ParseParagraphDirection(const std::string& str) {
+ int i = ParseInt(str);
+ switch (i) {
+ case 0:
+ return kDirectionLTR;
+ case 1:
+ return kDirectionRTL;
+ case 2:
+ return kDirectionAutoLTR;
+ default:
+ return kDirectionNone;
+ }
+}
+
+static int ParseSuppresedChars(const std::string& str) {
+ std::vector<std::string> strings = ParseStringList(str);
+ int suppresed_chars = 0;
+ for (size_t x = 0; x < strings.size(); x++) {
+ if (strings[x] == "x")
+ suppresed_chars++;
+ }
+ return suppresed_chars;
+}
+
+template <class Runner>
+void CharacterHarness<Runner>::Parse(std::istream& bidi_test_file) {
+ std::string line;
+ size_t line_number = 0;
+ while (std::getline(bidi_test_file, line)) {
+ line_number++;
+
+ const std::string original_line = line;
+ size_t comment_start = line.find_first_of('#');
+ if (comment_start != std::string::npos)
+ line = line.substr(0, comment_start);
+ Trim(line);
+ if (line.empty())
+ continue;
+
+ // Field 0: list of uchars as 4 char strings
+ size_t separator_index = line.find_first_of(';');
+ if (separator_index == std::string::npos) {
+ ParseError(original_line, line_number);
+ continue;
+ }
+
+ std::basic_string<UChar> test_string =
+ ParseUCharHexadecimalList(line.substr(0, separator_index));
+ if (test_string.empty()) {
+ ParseError(original_line, line_number);
+ continue;
+ }
+ line = line.substr(separator_index + 1);
+
+ // Field 1: paragraph direction (0 LTR, 1 RTL, 2 AutoLTR)
+ separator_index = line.find_first_of(';');
+ if (separator_index == std::string::npos) {
+ ParseError(original_line, line_number);
+ continue;
+ }
+
+ ParagraphDirection paragraph_direction =
+ ParseParagraphDirection(line.substr(0, separator_index));
+ if (paragraph_direction == kDirectionNone) {
+ ParseError(original_line, line_number);
+ continue;
+ }
+ line = line.substr(separator_index + 1);
+
+ // Field 2: resolved paragraph embedding level
+ separator_index = line.find_first_of(';');
+ if (separator_index == std::string::npos) {
+ ParseError(original_line, line_number);
+ continue;
+ }
+
+ int paragraph_embedding_level = ParseInt(line.substr(0, separator_index));
+ if (paragraph_embedding_level < 0) {
+ ParseError(original_line, line_number);
+ continue;
+ }
+ line = line.substr(separator_index + 1);
+
+ // Field 3: List of resolved levels
+ separator_index = line.find_first_of(';');
+ if (separator_index == std::string::npos) {
+ ParseError(original_line, line_number);
+ continue;
+ }
+
+ int supressed_chars = ParseSuppresedChars(line.substr(0, separator_index));
+ std::vector<int> levels = ParseLevels(line.substr(0, separator_index));
+ if (test_string.size() != levels.size()) {
+ ParseError(original_line, line_number);
+ continue;
+ }
+ line = line.substr(separator_index + 1);
+
+ // Field 4: visual ordering of characters
+ separator_index = line.find_first_of(';');
+ if (separator_index != std::string::npos) {
+ ParseError(original_line, line_number);
+ continue;
+ }
+
+ std::vector<int> visual_ordering = ParseIntList(line);
+ if (test_string.size() - supressed_chars != visual_ordering.size()) {
+ ParseError(original_line, line_number);
+ continue;
+ }
+
+ runner_.RunTest(test_string, visual_ordering, levels, paragraph_direction,
+ original_line, line_number);
+ }
+}
+
+} // namespace bidi_test
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_BIDI_TEST_HARNESS_H_
diff --git a/chromium/third_party/blink/renderer/platform/text/bidi_text_run.cc b/chromium/third_party/blink/renderer/platform/text/bidi_text_run.cc
new file mode 100644
index 00000000000..d149e93b2a5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/bidi_text_run.cc
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/renderer/platform/text/bidi_text_run.h"
+
+#include "third_party/blink/renderer/platform/text/bidi_resolver.h"
+#include "third_party/blink/renderer/platform/text/text_run_iterator.h"
+
+namespace blink {
+
+TextDirection DirectionForRun(TextRun& run, bool* has_strong_directionality) {
+ if (!has_strong_directionality) {
+ // 8bit is Latin-1 and therefore is always LTR.
+ if (run.Is8Bit())
+ return TextDirection::kLtr;
+
+ // length == 1 for more than 90% of cases of width() for CJK text.
+ if (run.length() == 1 && U16_IS_SINGLE(run.Characters16()[0]))
+ return DirectionForCharacter(run.Characters16()[0]);
+ }
+
+ BidiResolver<TextRunIterator, BidiCharacterRun> bidi_resolver;
+ bidi_resolver.SetStatus(
+ BidiStatus(run.Direction(), run.DirectionalOverride()));
+ bidi_resolver.SetPositionIgnoringNestedIsolates(TextRunIterator(&run, 0));
+ return bidi_resolver.DetermineDirectionality(has_strong_directionality);
+}
+
+TextDirection DetermineDirectionality(const String& value,
+ bool* has_strong_directionality) {
+ TextRun run(value);
+ return DirectionForRun(run, has_strong_directionality);
+}
+
+TextRun TextRunWithDirectionality(const String& value,
+ bool* has_strong_directionality) {
+ TextRun run(value);
+ TextDirection direction = DirectionForRun(run, has_strong_directionality);
+ if (has_strong_directionality)
+ run.SetDirection(direction);
+ return run;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/bidi_text_run.h b/chromium/third_party/blink/renderer/platform/text/bidi_text_run.h
new file mode 100644
index 00000000000..8ef324e0475
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/bidi_text_run.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_BIDI_TEXT_RUN_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_BIDI_TEXT_RUN_H_
+
+#include "third_party/blink/renderer/platform/text/text_direction.h"
+#include "third_party/blink/renderer/platform/text/text_run.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+PLATFORM_EXPORT TextDirection
+DirectionForRun(TextRun&, bool* has_strong_directionality = nullptr);
+PLATFORM_EXPORT TextDirection
+DetermineDirectionality(const String& value,
+ bool* has_strong_directionality = nullptr);
+PLATFORM_EXPORT TextRun
+TextRunWithDirectionality(const String& value,
+ bool* has_strong_directionality = nullptr);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_BIDI_TEXT_RUN_H_
diff --git a/chromium/third_party/blink/renderer/platform/text/character.cc b/chromium/third_party/blink/renderer/platform/text/character.cc
new file mode 100644
index 00000000000..de4af537b67
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/character.cc
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2014 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 "third_party/blink/renderer/platform/text/character.h"
+
+#include <unicode/uobject.h>
+#include <unicode/uscript.h>
+#include <algorithm>
+#include "third_party/blink/renderer/platform/text/icu_error.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+
+#if defined(USING_SYSTEM_ICU)
+#include <unicode/uniset.h>
+#include "third_party/blink/renderer/platform/text/character_property_data_generator.h"
+#else
+#define MUTEX_H // Prevent compile failure of utrie2.h on Windows
+#include <utrie2.h>
+#endif
+
+namespace blink {
+
+#if defined(USING_SYSTEM_ICU)
+static icu::UnicodeSet* createUnicodeSet(const UChar32* characters,
+ size_t charactersCount,
+ const UChar32* ranges,
+ size_t rangesCount) {
+ icu::UnicodeSet* unicodeSet = new icu::UnicodeSet();
+ for (size_t i = 0; i < charactersCount; i++)
+ unicodeSet->add(characters[i]);
+ for (size_t i = 0; i < rangesCount; i += 2)
+ unicodeSet->add(ranges[i], ranges[i + 1]);
+ unicodeSet->freeze();
+ return unicodeSet;
+}
+
+#define CREATE_UNICODE_SET(name) \
+ createUnicodeSet(name##Array, WTF_ARRAY_LENGTH(name##Array), name##Ranges, \
+ WTF_ARRAY_LENGTH(name##Ranges))
+
+#define RETURN_HAS_PROPERTY(c, name) \
+ static icu::UnicodeSet* unicodeSet = nullptr; \
+ if (!unicodeSet) \
+ unicodeSet = CREATE_UNICODE_SET(name); \
+ return unicodeSet->contains(c);
+#else
+// Freezed trie tree, see CharacterDataGenerator.cpp.
+extern const int32_t kSerializedCharacterDataSize;
+extern const uint8_t kSerializedCharacterData[];
+
+static UTrie2* CreateTrie() {
+ // Create a Trie from the value array.
+ ICUError error;
+ UTrie2* trie = utrie2_openFromSerialized(
+ UTrie2ValueBits::UTRIE2_16_VALUE_BITS, kSerializedCharacterData,
+ kSerializedCharacterDataSize, nullptr, &error);
+ DCHECK_EQ(error, U_ZERO_ERROR);
+ return trie;
+}
+
+static bool HasProperty(UChar32 c, CharacterProperty property) {
+ static UTrie2* trie = nullptr;
+ if (!trie)
+ trie = CreateTrie();
+ return UTRIE2_GET16(trie, c) & static_cast<CharacterPropertyType>(property);
+}
+
+#define RETURN_HAS_PROPERTY(c, name) \
+ return HasProperty(c, CharacterProperty::name);
+#endif
+
+// Takes a flattened list of closed intervals
+template <class T, size_t size>
+bool ValueInIntervalList(const T (&interval_list)[size], const T& value) {
+ const T* bound =
+ std::upper_bound(&interval_list[0], &interval_list[size], value);
+ if ((bound - interval_list) % 2 == 1)
+ return true;
+ return bound > interval_list && *(bound - 1) == value;
+}
+
+bool Character::IsUprightInMixedVertical(UChar32 character) {
+ RETURN_HAS_PROPERTY(character, kIsUprightInMixedVertical)
+}
+
+bool Character::IsCJKIdeographOrSymbolSlow(UChar32 c) {
+ RETURN_HAS_PROPERTY(c, kIsCJKIdeographOrSymbol)
+}
+
+bool Character::IsPotentialCustomElementNameChar(UChar32 character) {
+ RETURN_HAS_PROPERTY(character, kIsPotentialCustomElementNameChar);
+}
+
+unsigned Character::ExpansionOpportunityCount(const LChar* characters,
+ size_t length,
+ TextDirection direction,
+ bool& is_after_expansion,
+ const TextJustify text_justify) {
+ unsigned count = 0;
+ if (text_justify == TextJustify::kDistribute) {
+ is_after_expansion = true;
+ return length;
+ }
+
+ if (direction == TextDirection::kLtr) {
+ for (size_t i = 0; i < length; ++i) {
+ if (TreatAsSpace(characters[i])) {
+ count++;
+ is_after_expansion = true;
+ } else {
+ is_after_expansion = false;
+ }
+ }
+ } else {
+ for (size_t i = length; i > 0; --i) {
+ if (TreatAsSpace(characters[i - 1])) {
+ count++;
+ is_after_expansion = true;
+ } else {
+ is_after_expansion = false;
+ }
+ }
+ }
+
+ return count;
+}
+
+unsigned Character::ExpansionOpportunityCount(const UChar* characters,
+ size_t length,
+ TextDirection direction,
+ bool& is_after_expansion,
+ const TextJustify text_justify) {
+ unsigned count = 0;
+ if (direction == TextDirection::kLtr) {
+ for (size_t i = 0; i < length; ++i) {
+ UChar32 character = characters[i];
+ if (TreatAsSpace(character)) {
+ count++;
+ is_after_expansion = true;
+ continue;
+ }
+ if (U16_IS_LEAD(character) && i + 1 < length &&
+ U16_IS_TRAIL(characters[i + 1])) {
+ character = U16_GET_SUPPLEMENTARY(character, characters[i + 1]);
+ i++;
+ }
+ if (text_justify == TextJustify::kAuto &&
+ IsCJKIdeographOrSymbol(character)) {
+ if (!is_after_expansion)
+ count++;
+ count++;
+ is_after_expansion = true;
+ continue;
+ }
+ is_after_expansion = false;
+ }
+ } else {
+ for (size_t i = length; i > 0; --i) {
+ UChar32 character = characters[i - 1];
+ if (TreatAsSpace(character)) {
+ count++;
+ is_after_expansion = true;
+ continue;
+ }
+ if (U16_IS_TRAIL(character) && i > 1 && U16_IS_LEAD(characters[i - 2])) {
+ character = U16_GET_SUPPLEMENTARY(characters[i - 2], character);
+ i--;
+ }
+ if (text_justify == TextJustify::kAuto &&
+ IsCJKIdeographOrSymbol(character)) {
+ if (!is_after_expansion)
+ count++;
+ count++;
+ is_after_expansion = true;
+ continue;
+ }
+ is_after_expansion = false;
+ }
+ }
+ return count;
+}
+
+bool Character::CanTextDecorationSkipInk(UChar32 codepoint) {
+ if (codepoint == kSolidusCharacter || codepoint == kReverseSolidusCharacter ||
+ codepoint == kLowLineCharacter)
+ return false;
+
+ if (Character::IsCJKIdeographOrSymbol(codepoint))
+ return false;
+
+ UBlockCode block = ublock_getCode(codepoint);
+ switch (block) {
+ // These blocks contain CJK characters we don't want to skip ink, but are
+ // not ideograph that IsCJKIdeographOrSymbol() does not cover.
+ case UBLOCK_HANGUL_JAMO:
+ case UBLOCK_HANGUL_COMPATIBILITY_JAMO:
+ case UBLOCK_HANGUL_SYLLABLES:
+ case UBLOCK_HANGUL_JAMO_EXTENDED_A:
+ case UBLOCK_HANGUL_JAMO_EXTENDED_B:
+ case UBLOCK_LINEAR_B_IDEOGRAMS:
+ return false;
+ default:
+ return true;
+ }
+}
+
+bool Character::CanReceiveTextEmphasis(UChar32 c) {
+ WTF::Unicode::CharCategory category = WTF::Unicode::Category(c);
+ if (category &
+ (WTF::Unicode::kSeparator_Space | WTF::Unicode::kSeparator_Line |
+ WTF::Unicode::kSeparator_Paragraph | WTF::Unicode::kOther_NotAssigned |
+ WTF::Unicode::kOther_Control | WTF::Unicode::kOther_Format))
+ return false;
+
+ // Additional word-separator characters listed in CSS Text Level 3 Editor's
+ // Draft 3 November 2010.
+ if (c == kEthiopicWordspaceCharacter ||
+ c == kAegeanWordSeparatorLineCharacter ||
+ c == kAegeanWordSeparatorDotCharacter ||
+ c == kUgariticWordDividerCharacter ||
+ c == kTibetanMarkIntersyllabicTshegCharacter ||
+ c == kTibetanMarkDelimiterTshegBstarCharacter)
+ return false;
+
+ return true;
+}
+
+bool Character::IsEmojiFlagSequenceTag(UChar32 c) {
+ // Only allow valid sequences from
+ // http://www.unicode.org/reports/tr51/proposed.html#valid-emoji-tag-sequences
+ return (c >= kTagDigitZero && c <= kTagDigitNine) ||
+ (c >= kTagLatinSmallLetterA && c <= kTagLatinSmallLetterZ) ||
+ c == kCancelTag;
+}
+
+template <typename CharacterType>
+static inline String NormalizeSpacesInternal(const CharacterType* characters,
+ unsigned length) {
+ StringBuilder normalized;
+ normalized.ReserveCapacity(length);
+
+ for (unsigned i = 0; i < length; ++i)
+ normalized.Append(Character::NormalizeSpaces(characters[i]));
+
+ return normalized.ToString();
+}
+
+String Character::NormalizeSpaces(const LChar* characters, unsigned length) {
+ return NormalizeSpacesInternal(characters, length);
+}
+
+String Character::NormalizeSpaces(const UChar* characters, unsigned length) {
+ return NormalizeSpacesInternal(characters, length);
+}
+
+bool Character::IsCommonOrInheritedScript(UChar32 character) {
+ ICUError status;
+ UScriptCode script = uscript_getScript(character, &status);
+ return U_SUCCESS(status) &&
+ (script == USCRIPT_COMMON || script == USCRIPT_INHERITED);
+}
+
+bool Character::IsUnassignedOrPrivateUse(UChar32 character) {
+ return WTF::Unicode::Category(character) &
+ (WTF::Unicode::kOther_NotAssigned | WTF::Unicode::kOther_PrivateUse);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/character.h b/chromium/third_party/blink/renderer/platform/text/character.h
new file mode 100644
index 00000000000..bbee1b062f1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/character.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2014 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_CHARACTER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_CHARACTER_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/text/character_property.h"
+#include "third_party/blink/renderer/platform/text/text_direction.h"
+#include "third_party/blink/renderer/platform/text/text_run.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/ascii_ctype.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT Character {
+ STATIC_ONLY(Character);
+
+ public:
+ static inline bool IsInRange(UChar32 character,
+ UChar32 lower_bound,
+ UChar32 upper_bound) {
+ return character >= lower_bound && character <= upper_bound;
+ }
+
+ static inline bool IsUnicodeVariationSelector(UChar32 character) {
+ // http://www.unicode.org/Public/UCD/latest/ucd/StandardizedVariants.html
+ return IsInRange(character, 0x180B,
+ 0x180D) // MONGOLIAN FREE VARIATION SELECTOR ONE to THREE
+ ||
+ IsInRange(character, 0xFE00, 0xFE0F) // VARIATION SELECTOR-1 to 16
+ || IsInRange(character, 0xE0100,
+ 0xE01EF); // VARIATION SELECTOR-17 to 256
+ }
+
+ static bool IsCJKIdeographOrSymbol(UChar32 c) {
+ // Below U+02C7 is likely a common case.
+ return c < 0x2C7 ? false : IsCJKIdeographOrSymbolSlow(c);
+ }
+ static bool IsCJKIdeographOrSymbolBase(UChar32 c) {
+ return IsCJKIdeographOrSymbol(c) &&
+ !(U_GET_GC_MASK(c) & (U_GC_M_MASK | U_GC_LM_MASK | U_GC_SK_MASK));
+ }
+
+ static unsigned ExpansionOpportunityCount(const LChar*,
+ size_t length,
+ TextDirection,
+ bool& is_after_expansion,
+ const TextJustify);
+ static unsigned ExpansionOpportunityCount(const UChar*,
+ size_t length,
+ TextDirection,
+ bool& is_after_expansion,
+ const TextJustify);
+ static unsigned ExpansionOpportunityCount(const TextRun& run,
+ bool& is_after_expansion) {
+ if (run.Is8Bit())
+ return ExpansionOpportunityCount(run.Characters8(), run.length(),
+ run.Direction(), is_after_expansion,
+ run.GetTextJustify());
+ return ExpansionOpportunityCount(run.Characters16(), run.length(),
+ run.Direction(), is_after_expansion,
+ run.GetTextJustify());
+ }
+
+ static bool IsUprightInMixedVertical(UChar32 character);
+
+ // https://html.spec.whatwg.org/multipage/scripting.html#prod-potentialcustomelementname
+ static bool IsPotentialCustomElementName8BitChar(LChar ch) {
+ return IsASCIILower(ch) || IsASCIIDigit(ch) || ch == '-' || ch == '.' ||
+ ch == '_' || ch == 0xb7 || (0xc0 <= ch && ch != 0xd7 && ch != 0xf7);
+ }
+ static bool IsPotentialCustomElementNameChar(UChar32 character);
+
+ static bool TreatAsSpace(UChar32 c) {
+ return c == kSpaceCharacter || c == kTabulationCharacter ||
+ c == kNewlineCharacter || c == kNoBreakSpaceCharacter;
+ }
+ static bool TreatAsZeroWidthSpace(UChar32 c) {
+ return TreatAsZeroWidthSpaceInComplexScript(c) ||
+ c == kZeroWidthNonJoinerCharacter || c == kZeroWidthJoinerCharacter;
+ }
+ static bool LegacyTreatAsZeroWidthSpaceInComplexScript(UChar32 c) {
+ return c < 0x20 // ASCII Control Characters
+ ||
+ (c >= 0x7F && c < 0xA0) // ASCII Delete .. No-break spaceCharacter
+ || TreatAsZeroWidthSpaceInComplexScript(c);
+ }
+ static bool TreatAsZeroWidthSpaceInComplexScript(UChar32 c) {
+ return c == kFormFeedCharacter || c == kCarriageReturnCharacter ||
+ c == kSoftHyphenCharacter || c == kZeroWidthSpaceCharacter ||
+ (c >= kLeftToRightMarkCharacter && c <= kRightToLeftMarkCharacter) ||
+ (c >= kLeftToRightEmbedCharacter &&
+ c <= kRightToLeftOverrideCharacter) ||
+ c == kZeroWidthNoBreakSpaceCharacter ||
+ c == kObjectReplacementCharacter;
+ }
+ static bool CanTextDecorationSkipInk(UChar32);
+ static bool CanReceiveTextEmphasis(UChar32);
+
+ static bool IsGraphemeExtended(UChar32 c) {
+ // http://unicode.org/reports/tr29/#Extend
+ return u_hasBinaryProperty(c, UCHAR_GRAPHEME_EXTEND);
+ }
+
+ // Returns true if the character has a Emoji property.
+ // See http://www.unicode.org/Public/emoji/3.0/emoji-data.txt
+ static bool IsEmoji(UChar32);
+ // Default presentation style according to:
+ // http://www.unicode.org/reports/tr51/#Presentation_Style
+ static bool IsEmojiTextDefault(UChar32);
+ static bool IsEmojiEmojiDefault(UChar32);
+ static bool IsEmojiModifierBase(UChar32);
+ static bool IsEmojiKeycapBase(UChar32);
+ static bool IsRegionalIndicator(UChar32);
+ static bool IsModifier(UChar32 c) { return c >= 0x1F3FB && c <= 0x1F3FF; }
+ // http://www.unicode.org/reports/tr51/proposed.html#flag-emoji-tag-sequences
+ static bool IsEmojiFlagSequenceTag(UChar32);
+
+ static inline UChar NormalizeSpaces(UChar character) {
+ if (TreatAsSpace(character))
+ return kSpaceCharacter;
+
+ if (TreatAsZeroWidthSpace(character))
+ return kZeroWidthSpaceCharacter;
+
+ return character;
+ }
+
+ static inline bool IsNormalizedCanvasSpaceCharacter(UChar32 c) {
+ // According to specification all space characters should be replaced with
+ // 0x0020 space character.
+ // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#text-preparation-algorithm
+ // The space characters according to specification are : U+0020, U+0009,
+ // U+000A, U+000C, and U+000D.
+ // http://www.whatwg.org/specs/web-apps/current-work/multipage/common-microsyntaxes.html#space-character
+ // This function returns true for 0x000B also, so that this is backward
+ // compatible. Otherwise, the test
+ // LayoutTests/canvas/philip/tests/2d.text.draw.space.collapse.space.html
+ // will fail
+ return c == 0x0009 || (c >= 0x000A && c <= 0x000D);
+ }
+
+ static String NormalizeSpaces(const LChar*, unsigned length);
+ static String NormalizeSpaces(const UChar*, unsigned length);
+
+ static bool IsCommonOrInheritedScript(UChar32);
+ static bool IsUnassignedOrPrivateUse(UChar32);
+
+ private:
+ static bool IsCJKIdeographOrSymbolSlow(UChar32);
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/text/character_emoji.cc b/chromium/third_party/blink/renderer/platform/text/character_emoji.cc
new file mode 100644
index 00000000000..4d281e2b94b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/character_emoji.cc
@@ -0,0 +1,243 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/text/character.h"
+
+#include <unicode/uvernum.h>
+#include "third_party/blink/renderer/platform/text/icu_error.h"
+
+#if defined(USING_SYSTEM_ICU) || (U_ICU_VERSION_MAJOR_NUM <= 57)
+#include <unicode/uniset.h>
+#else
+#include <unicode/uchar.h>
+#endif
+
+namespace blink {
+
+// ICU 57 or earlier does not have up to date Emoji properties, so we're
+// temporarily uing our own functions again. Compare crbug.com/628333 Other than
+// that: versions before 56 do not have an API for Emoji properties, but
+// Chrome's copy of ICU 56 does.
+#if defined(USING_SYSTEM_ICU) || (U_ICU_VERSION_MAJOR_NUM <= 57)
+// The following UnicodeSet patterns were compiled from
+// http://www.unicode.org/Public/emoji/3.0//emoji-data.txt
+
+// The following patterns can be generated from the respective sections of the
+// emoji_data.txt file by using the following Elisp function in Emacs.
+// Known issues:
+// 1) Does not insert the double [[ and ]] at the beginning and end of the
+// pattern.
+// 2) Does not insert \U0000 at the very last codepoint of a pattern.
+//
+// (defun convertemojidata ()
+// "Convert a section of the emoji_data.txt file to an ICU trie definition."
+// (interactive)
+// (goto-char 0)
+// (while (re-search-forward " *;.*$" nil t)
+// (replace-match "" nil nil))
+// (goto-char 0)
+// (while (re-search-forward "\\.\\." nil t)
+// (replace-match "-" nil nil))
+// (goto-char 0)
+// ; Pad 4 digit characters, step 1
+// (while (re-search-forward
+// "\\([^0-9A-F]*\\)\\([0-9A-F]\\{4\\}\\)\\([^0-9A-F]\\)"
+// nil t)
+// (replace-match "\\1\\\\U0000\\2\\3" nil nil))
+// (goto-char 0)
+// ; Fix up 5 digit characters padding, step 2
+// (while (re-search-forward "1\\\\U0000" nil t)
+// (replace-match "\\\\U0001" nil nil))
+// (goto-char 0)
+// (while (re-search-forward "^\\(.*\\)$" nil t)
+// (replace-match "[\\1]" nil nil))
+// (goto-char 0)
+// (replace-string "\n" " ")
+// (set-fill-column 72)
+// (goto-char 0)
+// (fill-paragraph)
+// (replace-string " " "")
+// (goto-char 0)
+// (while (re-search-forward "^\\(.*\\)$" nil t)
+// (replace-match " R\"(\\1)\"" nil nil))
+// (goto-char 8)
+// (insert "[")
+// (goto-char (- (point-max) 3))
+// (insert "]")
+// )
+
+static const char kEmojiTextPattern[] =
+ R"([[\U00000023][\U0000002A][\U00000030-\U00000039][\U000000A9])"
+ R"([\U000000AE][\U0000203C][\U00002049][\U00002122][\U00002139])"
+ R"([\U00002194-\U00002199][\U000021A9-\U000021AA][\U0000231A-\U0000231B])"
+ R"([\U00002328][\U000023CF][\U000023E9-\U000023F3])"
+ R"([\U000023F8-\U000023FA][\U000024C2][\U000025AA-\U000025AB])"
+ R"([\U000025B6][\U000025C0][\U000025FB-\U000025FE])"
+ R"([\U00002600-\U00002604][\U0000260E][\U00002611])"
+ R"([\U00002614-\U00002615][\U00002618][\U0000261D][\U00002620])"
+ R"([\U00002622-\U00002623][\U00002626][\U0000262A])"
+ R"([\U0000262E-\U0000262F][\U00002638-\U0000263A][\U00002640])"
+ R"([\U00002642][\U00002648-\U00002653][\U00002660][\U00002663])"
+ R"([\U00002665-\U00002666][\U00002668][\U0000267B][\U0000267F])"
+ R"([\U00002692-\U00002697][\U00002699][\U0000269B-\U0000269C])"
+ R"([\U000026A0-\U000026A1][\U000026AA-\U000026AB][\U000026B0-\U000026B1])"
+ R"([\U000026BD-\U000026BE][\U000026C4-\U000026C5][\U000026C8])"
+ R"([\U000026CE][\U000026CF][\U000026D1][\U000026D3-\U000026D4])"
+ R"([\U000026E9-\U000026EA][\U000026F0-\U000026F5][\U000026F7-\U000026FA])"
+ R"([\U000026FD][\U00002702][\U00002705][\U00002708-\U00002709])"
+ R"([\U0000270A-\U0000270B][\U0000270C-\U0000270D][\U0000270F])"
+ R"([\U00002712][\U00002714][\U00002716][\U0000271D][\U00002721])"
+ R"([\U00002728][\U00002733-\U00002734][\U00002744][\U00002747])"
+ R"([\U0000274C][\U0000274E][\U00002753-\U00002755][\U00002757])"
+ R"([\U00002763-\U00002764][\U00002795-\U00002797][\U000027A1])"
+ R"([\U000027B0][\U000027BF][\U00002934-\U00002935])"
+ R"([\U00002B05-\U00002B07][\U00002B1B-\U00002B1C][\U00002B50])"
+ R"([\U00002B55][\U00003030][\U0000303D][\U00003297][\U00003299])"
+ R"([\U0001F004][\U0001F0CF][\U0001F170-\U0001F171][\U0001F17E])"
+ R"([\U0001F17F][\U0001F18E][\U0001F191-\U0001F19A])"
+ R"([\U0001F1E6-\U0001F1FF][\U0001F201-\U0001F202][\U0001F21A])"
+ R"([\U0001F22F][\U0001F232-\U0001F23A][\U0001F250-\U0001F251])"
+ R"([\U0001F300-\U0001F320][\U0001F321][\U0001F324-\U0001F32C])"
+ R"([\U0001F32D-\U0001F32F][\U0001F330-\U0001F335][\U0001F336])"
+ R"([\U0001F337-\U0001F37C][\U0001F37D][\U0001F37E-\U0001F37F])"
+ R"([\U0001F380-\U0001F393][\U0001F396-\U0001F397][\U0001F399-\U0001F39B])"
+ R"([\U0001F39E-\U0001F39F][\U0001F3A0-\U0001F3C4][\U0001F3C5])"
+ R"([\U0001F3C6-\U0001F3CA][\U0001F3CB-\U0001F3CE][\U0001F3CF-\U0001F3D3])"
+ R"([\U0001F3D4-\U0001F3DF][\U0001F3E0-\U0001F3F0][\U0001F3F3-\U0001F3F5])"
+ R"([\U0001F3F7][\U0001F3F8-\U0001F3FF][\U0001F400-\U0001F43E])"
+ R"([\U0001F43F][\U0001F440][\U0001F441][\U0001F442-\U0001F4F7])"
+ R"([\U0001F4F8][\U0001F4F9-\U0001F4FC][\U0001F4FD][\U0001F4FF])"
+ R"([\U0001F500-\U0001F53D][\U0001F549-\U0001F54A][\U0001F54B-\U0001F54E])"
+ R"([\U0001F550-\U0001F567][\U0001F56F-\U0001F570][\U0001F573-\U0001F579])"
+ R"([\U0001F57A][\U0001F587][\U0001F58A-\U0001F58D][\U0001F590])"
+ R"([\U0001F595-\U0001F596][\U0001F5A4][\U0001F5A5][\U0001F5A8])"
+ R"([\U0001F5B1-\U0001F5B2][\U0001F5BC][\U0001F5C2-\U0001F5C4])"
+ R"([\U0001F5D1-\U0001F5D3][\U0001F5DC-\U0001F5DE][\U0001F5E1])"
+ R"([\U0001F5E3][\U0001F5E8][\U0001F5EF][\U0001F5F3][\U0001F5FA])"
+ R"([\U0001F5FB-\U0001F5FF][\U0001F600][\U0001F601-\U0001F610])"
+ R"([\U0001F611][\U0001F612-\U0001F614][\U0001F615][\U0001F616])"
+ R"([\U0001F617][\U0001F618][\U0001F619][\U0001F61A][\U0001F61B])"
+ R"([\U0001F61C-\U0001F61E][\U0001F61F][\U0001F620-\U0001F625])"
+ R"([\U0001F626-\U0001F627][\U0001F628-\U0001F62B][\U0001F62C])"
+ R"([\U0001F62D][\U0001F62E-\U0001F62F][\U0001F630-\U0001F633])"
+ R"([\U0001F634][\U0001F635-\U0001F640][\U0001F641-\U0001F642])"
+ R"([\U0001F643-\U0001F644][\U0001F645-\U0001F64F][\U0001F680-\U0001F6C5])"
+ R"([\U0001F6CB-\U0001F6CF][\U0001F6D0][\U0001F6D1-\U0001F6D2])"
+ R"([\U0001F6E0-\U0001F6E5][\U0001F6E9][\U0001F6EB-\U0001F6EC])"
+ R"([\U0001F6F0][\U0001F6F3][\U0001F6F4-\U0001F6F6])"
+ R"([\U0001F910-\U0001F918][\U0001F919-\U0001F91E][\U0001F920-\U0001F927])"
+ R"([\U0001F930][\U0001F933-\U0001F93A][\U0001F93C-\U0001F93E])"
+ R"([\U0001F940-\U0001F945][\U0001F947-\U0001F94B][\U0001F950-\U0001F95E])"
+ R"([\U0001F980-\U0001F984][\U0001F985-\U0001F991][\U0001F9C0]])";
+
+static const char kEmojiEmojiPattern[] =
+ R"([[\U0000231A-\U0000231B][\U000023E9-\U000023EC][\U000023F0])"
+ R"([\U000023F3][\U000025FD-\U000025FE][\U00002614-\U00002615])"
+ R"([\U00002648-\U00002653][\U0000267F][\U00002693][\U000026A1])"
+ R"([\U000026AA-\U000026AB][\U000026BD-\U000026BE][\U000026C4-\U000026C5])"
+ R"([\U000026CE][\U000026D4][\U000026EA][\U000026F2-\U000026F3])"
+ R"([\U000026F5][\U000026FA][\U000026FD][\U00002705])"
+ R"([\U0000270A-\U0000270B][\U00002728][\U0000274C][\U0000274E])"
+ R"([\U00002753-\U00002755][\U00002757][\U00002795-\U00002797])"
+ R"([\U000027B0][\U000027BF][\U00002B1B-\U00002B1C][\U00002B50])"
+ R"([\U00002B55][\U0001F004][\U0001F0CF][\U0001F18E])"
+ R"([\U0001F191-\U0001F19A][\U0001F1E6-\U0001F1FF][\U0001F201])"
+ R"([\U0001F21A][\U0001F22F][\U0001F232-\U0001F236])"
+ R"([\U0001F238-\U0001F23A][\U0001F250-\U0001F251][\U0001F300-\U0001F320])"
+ R"([\U0001F32D-\U0001F32F][\U0001F330-\U0001F335][\U0001F337-\U0001F37C])"
+ R"([\U0001F37E-\U0001F37F][\U0001F380-\U0001F393][\U0001F3A0-\U0001F3C4])"
+ R"([\U0001F3C5][\U0001F3C6-\U0001F3CA][\U0001F3CF-\U0001F3D3])"
+ R"([\U0001F3E0-\U0001F3F0][\U0001F3F4][\U0001F3F8-\U0001F3FF])"
+ R"([\U0001F400-\U0001F43E][\U0001F440][\U0001F442-\U0001F4F7])"
+ R"([\U0001F4F8][\U0001F4F9-\U0001F4FC][\U0001F4FF])"
+ R"([\U0001F500-\U0001F53D][\U0001F54B-\U0001F54E][\U0001F550-\U0001F567])"
+ R"([\U0001F57A][\U0001F595-\U0001F596][\U0001F5A4])"
+ R"([\U0001F5FB-\U0001F5FF][\U0001F600][\U0001F601-\U0001F610])"
+ R"([\U0001F611][\U0001F612-\U0001F614][\U0001F615][\U0001F616])"
+ R"([\U0001F617][\U0001F618][\U0001F619][\U0001F61A][\U0001F61B])"
+ R"([\U0001F61C-\U0001F61E][\U0001F61F][\U0001F620-\U0001F625])"
+ R"([\U0001F626-\U0001F627][\U0001F628-\U0001F62B][\U0001F62C])"
+ R"([\U0001F62D][\U0001F62E-\U0001F62F][\U0001F630-\U0001F633])"
+ R"([\U0001F634][\U0001F635-\U0001F640][\U0001F641-\U0001F642])"
+ R"([\U0001F643-\U0001F644][\U0001F645-\U0001F64F][\U0001F680-\U0001F6C5])"
+ R"([\U0001F6CC][\U0001F6D0][\U0001F6D1-\U0001F6D2])"
+ R"([\U0001F6EB-\U0001F6EC][\U0001F6F4-\U0001F6F6][\U0001F910-\U0001F918])"
+ R"([\U0001F919-\U0001F91E][\U0001F920-\U0001F927][\U0001F930])"
+ R"([\U0001F933-\U0001F93A][\U0001F93C-\U0001F93E][\U0001F940-\U0001F945])"
+ R"([\U0001F947-\U0001F94B][\U0001F950-\U0001F95E][\U0001F980-\U0001F984])"
+ R"([\U0001F985-\U0001F991][\U0001F9C0]])";
+
+static const char kEmojiModifierBasePattern[] =
+ R"([[\U0000261D][\U000026F9][\U0000270A-\U0000270B])"
+ R"([\U0000270C-\U0000270D][\U0001F385][\U0001F3C2-\U0001F3C4])"
+ R"([\U0001F3C7][\U0001F3CA][\U0001F3CB-\U0001F3CC])"
+ R"([\U0001F442-\U0001F443][\U0001F446-\U0001F450][\U0001F466-\U0001F478])"
+ R"([\U0001F47C][\U0001F481-\U0001F483][\U0001F485-\U0001F487])"
+ R"([\U0001F4AA][\U0001F574-\U0001F575][\U0001F57A][\U0001F590])"
+ R"([\U0001F595-\U0001F596][\U0001F645-\U0001F647][\U0001F64B-\U0001F64F])"
+ R"([\U0001F6A3][\U0001F6B4-\U0001F6B6][\U0001F6C0][\U0001F6CC])"
+ R"([\U0001F918][\U0001F919-\U0001F91E][\U0001F926][\U0001F930])"
+ R"([\U0001F933-\U0001F939][\U0001F93C-\U0001F93E]])";
+
+static void applyPatternAndFreeze(icu::UnicodeSet* unicodeSet,
+ const char* pattern) {
+ ICUError err;
+ // Use ICU's invariant-character initialization method.
+ unicodeSet->applyPattern(icu::UnicodeString(pattern, -1, US_INV), err);
+ unicodeSet->freeze();
+ DCHECK_EQ(err, U_ZERO_ERROR);
+}
+
+bool Character::IsEmoji(UChar32 ch) {
+ return Character::IsEmojiTextDefault(ch) ||
+ Character::IsEmojiEmojiDefault(ch);
+}
+
+bool Character::IsEmojiTextDefault(UChar32 ch) {
+ DEFINE_STATIC_LOCAL(icu::UnicodeSet, emojiTextSet, ());
+ if (emojiTextSet.isEmpty())
+ applyPatternAndFreeze(&emojiTextSet, kEmojiTextPattern);
+ return emojiTextSet.contains(ch) && !IsEmojiEmojiDefault(ch);
+}
+
+bool Character::IsEmojiEmojiDefault(UChar32 ch) {
+ DEFINE_STATIC_LOCAL(icu::UnicodeSet, emojiEmojiSet, ());
+ if (emojiEmojiSet.isEmpty())
+ applyPatternAndFreeze(&emojiEmojiSet, kEmojiEmojiPattern);
+ return emojiEmojiSet.contains(ch);
+}
+
+bool Character::IsEmojiModifierBase(UChar32 ch) {
+ DEFINE_STATIC_LOCAL(icu::UnicodeSet, emojieModifierBaseSet, ());
+ if (emojieModifierBaseSet.isEmpty())
+ applyPatternAndFreeze(&emojieModifierBaseSet, kEmojiModifierBasePattern);
+ return emojieModifierBaseSet.contains(ch);
+}
+#else
+bool Character::IsEmoji(UChar32 ch) {
+ return u_hasBinaryProperty(ch, UCHAR_EMOJI);
+}
+bool Character::IsEmojiTextDefault(UChar32 ch) {
+ return u_hasBinaryProperty(ch, UCHAR_EMOJI) &&
+ !u_hasBinaryProperty(ch, UCHAR_EMOJI_PRESENTATION);
+}
+
+bool Character::IsEmojiEmojiDefault(UChar32 ch) {
+ return u_hasBinaryProperty(ch, UCHAR_EMOJI_PRESENTATION);
+}
+
+bool Character::IsEmojiModifierBase(UChar32 ch) {
+ return u_hasBinaryProperty(ch, UCHAR_EMOJI_MODIFIER_BASE);
+}
+#endif // defined(USING_SYSTEM_ICU) && (U_ICU_VERSION_MAJOR_NUM <= 57)
+
+bool Character::IsEmojiKeycapBase(UChar32 ch) {
+ return (ch >= '0' && ch <= '9') || ch == '#' || ch == '*';
+}
+
+bool Character::IsRegionalIndicator(UChar32 ch) {
+ return (ch >= 0x1F1E6 && ch <= 0x1F1FF);
+}
+
+}; // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/character_property.h b/chromium/third_party/blink/renderer/platform/text/character_property.h
new file mode 100644
index 00000000000..e474c835507
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/character_property.h
@@ -0,0 +1,37 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_CHARACTER_PROPERTY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_CHARACTER_PROPERTY_H_
+
+#include <cstdint>
+
+namespace blink {
+
+using CharacterPropertyType = uint8_t;
+
+enum class CharacterProperty : CharacterPropertyType {
+ kIsCJKIdeographOrSymbol = 0x0001,
+ kIsUprightInMixedVertical = 0x0002,
+ kIsPotentialCustomElementNameChar = 0x0004,
+};
+
+inline CharacterProperty operator|(CharacterProperty a, CharacterProperty b) {
+ return static_cast<CharacterProperty>(static_cast<CharacterPropertyType>(a) |
+ static_cast<CharacterPropertyType>(b));
+}
+
+inline CharacterProperty operator&(CharacterProperty a, CharacterProperty b) {
+ return static_cast<CharacterProperty>(static_cast<CharacterPropertyType>(a) &
+ static_cast<CharacterPropertyType>(b));
+}
+
+inline CharacterProperty operator|=(CharacterProperty& a, CharacterProperty b) {
+ a = a | b;
+ return a;
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_CHARACTER_PROPERTY_H_
diff --git a/chromium/third_party/blink/renderer/platform/text/character_property_data_generator.cc b/chromium/third_party/blink/renderer/platform/text/character_property_data_generator.cc
new file mode 100644
index 00000000000..e4dd77bf68a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/character_property_data_generator.cc
@@ -0,0 +1,134 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/text/character_property_data_generator.h"
+
+#include <stdio.h>
+#include <cassert>
+#include <cstring>
+#include <memory>
+#include "third_party/blink/renderer/platform/text/character_property.h"
+#if !defined(USING_SYSTEM_ICU)
+#define MUTEX_H // Prevent compile failure of utrie2.h on Windows
+#include <utrie2.h>
+#endif
+
+namespace blink {
+
+#if defined(USING_SYSTEM_ICU)
+static void Generate(FILE*) {}
+#else
+
+const UChar32 kMaxCodepoint = 0x10FFFF;
+#define ARRAY_LENGTH(a) (sizeof(a) / sizeof((a)[0]))
+
+static void SetRanges(CharacterProperty* values,
+ const UChar32* ranges,
+ size_t length,
+ CharacterProperty value) {
+ assert(length % 2 == 0);
+ const UChar32* end = ranges + length;
+ for (; ranges != end; ranges += 2) {
+ assert(ranges[0] <= ranges[1] && ranges[1] <= kMaxCodepoint);
+ for (UChar32 c = ranges[0]; c <= ranges[1]; c++)
+ values[c] |= value;
+ }
+}
+
+static void SetValues(CharacterProperty* values,
+ const UChar32* begin,
+ size_t length,
+ CharacterProperty value) {
+ const UChar32* end = begin + length;
+ for (; begin != end; begin++) {
+ assert(*begin <= kMaxCodepoint);
+ values[*begin] |= value;
+ }
+}
+
+static void GenerateUTrieSerialized(FILE* fp, int32_t size, uint8_t* array) {
+ fprintf(fp,
+ "#include <cstdint>\n\n"
+ "namespace blink {\n\n"
+ "extern const int32_t kSerializedCharacterDataSize = %d;\n"
+ // The utrie2_openFromSerialized function requires character data to
+ // be aligned to 4 bytes.
+ "alignas(4) extern const uint8_t kSerializedCharacterData[] = {",
+ size);
+ for (int32_t i = 0; i < size;) {
+ fprintf(fp, "\n ");
+ for (int col = 0; col < 16 && i < size; col++, i++)
+ fprintf(fp, " 0x%02X,", array[i]);
+ }
+ fprintf(fp,
+ "\n};\n\n"
+ "} // namespace blink\n");
+}
+
+static void Generate(FILE* fp) {
+ // Create a value array of all possible code points.
+ const UChar32 kSize = kMaxCodepoint + 1;
+ std::unique_ptr<CharacterProperty[]> values(new CharacterProperty[kSize]);
+ memset(values.get(), 0, sizeof(CharacterProperty) * kSize);
+
+#define SET(name) \
+ SetRanges(values.get(), name##Ranges, ARRAY_LENGTH(name##Ranges), \
+ CharacterProperty::name); \
+ SetValues(values.get(), name##Array, ARRAY_LENGTH(name##Array), \
+ CharacterProperty::name);
+
+ SET(kIsCJKIdeographOrSymbol);
+ SET(kIsUprightInMixedVertical);
+ SET(kIsPotentialCustomElementNameChar);
+
+ // Create a trie from the value array.
+ UErrorCode error = U_ZERO_ERROR;
+ UTrie2* trie = utrie2_open(0, 0, &error);
+ assert(error == U_ZERO_ERROR);
+ UChar32 start = 0;
+ CharacterProperty value = values[0];
+ for (UChar32 c = 1;; c++) {
+ if (c < kSize && values[c] == value)
+ continue;
+ if (static_cast<uint32_t>(value)) {
+ utrie2_setRange32(trie, start, c - 1, static_cast<uint32_t>(value), TRUE,
+ &error);
+ assert(error == U_ZERO_ERROR);
+ }
+ if (c >= kSize)
+ break;
+ start = c;
+ value = values[start];
+ }
+
+ // Freeze and serialize the trie to a byte array.
+ utrie2_freeze(trie, UTrie2ValueBits::UTRIE2_16_VALUE_BITS, &error);
+ assert(error == U_ZERO_ERROR);
+ int32_t serialized_size = utrie2_serialize(trie, nullptr, 0, &error);
+ error = U_ZERO_ERROR;
+ std::unique_ptr<uint8_t[]> serialized(new uint8_t[serialized_size]);
+ serialized_size =
+ utrie2_serialize(trie, serialized.get(), serialized_size, &error);
+ assert(error == U_ZERO_ERROR);
+
+ GenerateUTrieSerialized(fp, serialized_size, serialized.get());
+
+ utrie2_close(trie);
+}
+#endif
+
+} // namespace blink
+
+int main(int argc, char** argv) {
+ // Write the serialized array to the source file.
+ if (argc <= 1) {
+ blink::Generate(stdout);
+ } else {
+ FILE* fp = fopen(argv[1], "wb");
+ blink::Generate(fp);
+ fclose(fp);
+ }
+
+ return 0;
+}
diff --git a/chromium/third_party/blink/renderer/platform/text/character_property_data_generator.h b/chromium/third_party/blink/renderer/platform/text/character_property_data_generator.h
new file mode 100644
index 00000000000..57966cbd789
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/character_property_data_generator.h
@@ -0,0 +1,228 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_CHARACTER_PROPERTY_DATA_GENERATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_CHARACTER_PROPERTY_DATA_GENERATOR_H_
+
+#include <unicode/uobject.h>
+
+namespace blink {
+
+static const UChar32 kIsCJKIdeographOrSymbolArray[] = {
+ // 0x2C7 Caron, Mandarin Chinese 3rd Tone
+ 0x2C7,
+ // 0x2CA Modifier Letter Acute Accent, Mandarin Chinese 2nd Tone
+ 0x2CA,
+ // 0x2CB Modifier Letter Grave Access, Mandarin Chinese 4th Tone
+ 0x2CB,
+ // 0x2D9 Dot Above, Mandarin Chinese 5th Tone
+ 0x2D9, 0x2020, 0x2021, 0x2030, 0x203B, 0x203C, 0x2042, 0x2047, 0x2048,
+ 0x2049, 0x2051, 0x20DD, 0x20DE, 0x2100, 0x2103, 0x2105, 0x2109, 0x210A,
+ 0x2113, 0x2116, 0x2121, 0x212B, 0x213B, 0x2150, 0x2151, 0x2152, 0x217F,
+ 0x2189, 0x2307, 0x23F0, 0x23F3, 0x2312, 0x23CE, 0x2423, 0x25A0, 0x25A1,
+ 0x25A2, 0x25AA, 0x25AB, 0x25B1, 0x25B2, 0x25B3, 0x25B6, 0x25B7, 0x25BC,
+ 0x25BD, 0x25C0, 0x25C1, 0x25C6, 0x25C7, 0x25C9, 0x25CB, 0x25CC, 0x25EF,
+ 0x2605, 0x2606, 0x260E, 0x2616, 0x2617, 0x261D, 0x2640, 0x2642, 0x267F,
+ 0x2693, 0x26A0, 0x26A1, 0x26BD, 0x26BE, 0x26CE, 0x26D4,
+ // AIRPLANE added for PILOT emoji sequences.
+ 0x26EA, 0x26F5, 0x26F9, 0x26FA, 0x26FD, 0x2705, 0x2708, 0x2713, 0x271A,
+ 0x2728, 0x273F, 0x2740, 0x274C, 0x274E, 0x27B0, 0x27BF, 0x2B1A, 0x2B1B,
+ 0x2B1C, 0x2B50, 0x2B55, 0xFE10, 0xFE11, 0xFE12, 0xFE19, 0xFF1D,
+ // Emoji.
+ 0x1F100, 0x1F004, 0x1F0CF, 0x1F18E};
+
+static const UChar32 kIsCJKIdeographOrSymbolRanges[] = {
+ // STAFF OF AESCULAPIUS..SCALES for emoji sequences for doctor and judge
+ // professions.
+ 0x2695, 0x2696,
+ // cjkIdeographRanges
+ // CJK Radicals Supplement and Kangxi Radicals.
+ 0x2E80, 0x2FDF,
+ // CJK Strokes.
+ 0x31C0, 0x31EF,
+ // CJK Unified Ideographs Extension A.
+ 0x3400, 0x4DBF,
+ // The basic CJK Unified Ideographs block.
+ 0x4E00, 0x9FFF,
+ // CJK Compatibility Ideographs.
+ 0xF900, 0xFAFF,
+ // Unicode Plane 2: Supplementary Ideographic Plane. This plane includes:
+ // CJK Unified Ideographs Extension B to F.
+ // CJK Compatibility Ideographs Supplement.
+ 0x20000, 0x2FFFF,
+
+ // cjkSymbolRanges
+ 0x2156, 0x215A, 0x2160, 0x216B, 0x2170, 0x217B, 0x231A, 0x231B, 0x23E9,
+ 0x23EC, 0x23BE, 0x23CC, 0x2460, 0x2492, 0x249C, 0x24FF, 0x25CE, 0x25D3,
+ 0x25E2, 0x25E6, 0x25FD, 0x25FE, 0x2600, 0x2603, 0x2660, 0x266F,
+ // Emoji HEAVY HEART EXCLAMATION MARK ORNAMENT..HEAVY BLACK HEART
+ // Needed in order not to break Emoji heart-kiss sequences in
+ // CachingWordShapeIterator.
+ // cmp. http://www.unicode.org/emoji/charts/emoji-zwj-sequences.html
+ 0x2614, 0x2615, 0x2648, 0x2653, 0x26AA, 0x26AB, 0x26C4, 0x26C5, 0x26F2,
+ 0x26F3, 0x2753, 0x2757, 0x2763, 0x2764, 0x2672, 0x267D, 0x2776, 0x277F,
+ 0x2795, 0x2797,
+ // Hand signs needed in order
+ // not to break Emoji modifier sequences.
+ 0x270A, 0x270D,
+ // Ideographic Description Characters, with CJK Symbols and Punctuation,
+ // excluding 0x3030.
+ // Exclude Hangul Tone Marks (0x302E .. 0x302F) because Hangul is not Han
+ // and no other Hangul are included.
+ // Then Hiragana 0x3040 .. 0x309F, Katakana 0x30A0 .. 0x30FF, Bopomofo
+ // 0x3100 .. 0x312F
+ 0x2FF0, 0x302D, 0x3031, 0x312F,
+ // More Bopomofo and Bopomofo Extended 0x31A0 .. 0x31BF
+ 0x3190, 0x31BF,
+ // Enclosed CJK Letters and Months (0x3200 .. 0x32FF).
+ // CJK Compatibility (0x3300 .. 0x33FF).
+ 0x3200, 0x33FF,
+ // Yijing Hexagram Symbols
+ 0x4DC0, 0x4DFF,
+ // http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/JAPANESE.TXT
+ 0xF860, 0xF862,
+ // CJK Compatibility Forms.
+ // Small Form Variants (for CNS 11643).
+ 0xFE30, 0xFE6F,
+ // Halfwidth and Fullwidth Forms
+ // Usually only used in CJK
+ 0xFF00, 0xFF0C, 0xFF0E, 0xFF1A, 0xFF1F, 0xFFEF,
+ // Ideographic Symbols and Punctuation
+ 0x16FE0, 0x16FFF,
+ // Tangut
+ 0x17000, 0x187FF,
+ // Tangut Components
+ 0x18800, 0x18AFF,
+ // Kana Supplement
+ 0x1B000, 0x1B0FF,
+ // Kana Extended-A
+ 0x1B100, 0x1B12F,
+ // Nushu
+ 0x1B170, 0x1B2FF,
+ // Emoji.
+ 0x1F110, 0x1F129, 0x1F130, 0x1F149, 0x1F150, 0x1F169, 0x1F170, 0x1F189,
+ 0x1F191, 0x1F19A, 0x1F1E6, 0x1F1FF, 0x1F200, 0x1F6FF,
+ // Modifiers
+ 0x1F3FB, 0x1F3FF,
+
+ 0x1F900, 0x1F90F,
+ // ZIPPER-MOUTH FACE...SIGN OF THE HORNS
+ 0x1F910, 0x1F918, 0x1F919, 0x1F97F, 0x1F980, 0x1F9BF, 0x1F9C0, 0x1F9FF};
+
+// Individual codepoints needed for Unicode vertical text layout according to
+// http://www.unicode.org/reports/tr50/
+// Taken from the corresponding data file:
+// http://www.unicode.org/Public/vertical/revision-16/VerticalOrientation-16.txt
+static const UChar32 kIsUprightInMixedVerticalArray[] = {
+ 0x000A7, 0x000A9, 0x000AE, 0x000B1, 0x000D7, 0x000F7};
+
+static const UChar32 kIsUprightInMixedVerticalRanges[] = {
+ 0x000BC, 0x000BE,
+ // Spacing Modifier Letters (Part of)
+ 0x002EA, 0x002EB,
+ // Hangul Jamo
+ 0x01100, 0x011FF,
+ // Unified Canadian Aboriginal Syllabics
+ 0x01401, 0x0167F,
+ // Unified Canadian Aboriginal Syllabics Extended
+ 0x018B0, 0x018FF,
+ // General Punctuation (Part of)
+ 0x02016, 0x02016, 0x02020, 0x02021, 0x02030, 0x02031, 0x0203B, 0x0203C,
+ 0x02042, 0x02042, 0x02047, 0x02049, 0x02051, 0x02051, 0x02065, 0x02069,
+ // Combining Diacritical Marks for Symbols (Part of)
+ 0x020DD, 0x020E0, 0x020E2, 0x020E4,
+ // Letterlike Symbols (Part of)/Number Forms
+ 0x02100, 0x02101, 0x02103, 0x02109, 0x0210F, 0x0210F, 0x02113, 0x02114,
+ 0x02116, 0x02117, 0x0211E, 0x02123, 0x02125, 0x02125, 0x02127, 0x02127,
+ 0x02129, 0x02129, 0x0212E, 0x0212E, 0x02135, 0x0213F, 0x02145, 0x0214A,
+ 0x0214C, 0x0214D, 0x0214F, 0x02189, 0x0218C, 0x0218F,
+ // Mathematical Operators (Part of)
+ 0x0221E, 0x0221E, 0x02234, 0x02235,
+ // Miscellaneous Technical (Part of)
+ 0x02300, 0x02307, 0x0230C, 0x0231F, 0x02324, 0x0232B, 0x0237D, 0x0239A,
+ 0x023BE, 0x023CD, 0x023CF, 0x023CF, 0x023D1, 0x023DB, 0x023E2, 0x02422,
+ // Control Pictures (Part of)/Optical Character Recognition/Enclosed
+ // Alphanumerics
+ 0x02424, 0x024FF,
+ // Geometric Shapes/Miscellaneous Symbols (Part of)
+ 0x025A0, 0x02619, 0x02620, 0x02767, 0x02776, 0x02793,
+ // Miscellaneous Symbols and Arrows (Part of)
+ 0x02B12, 0x02B2F, 0x02B50, 0x02B59, 0x02BB8, 0x02BEB, 0x02BF0, 0x02BFF,
+ // Common CJK
+ 0x02E80, 0x0A4CF,
+ // Hangul Jamo Extended-A
+ 0x0A960, 0x0A97F,
+ // Hangul Syllables/Hangul Jamo Extended-B
+ 0x0AC00, 0x0D7FF,
+ // Private Use Area/CJK Compatibility Ideographs
+ 0x0E000, 0x0FAFF,
+ // Vertical Forms
+ 0x0FE10, 0x0FE1F,
+ // CJK Compatibility Forms (Part of)
+ 0x0FE30, 0x0FE48,
+ // Small Form Variants (Part of)
+ 0x0FE50, 0x0FE57, 0x0FE59, 0x0FE62, 0x0FE67, 0x0FE6F,
+ // Halfwidth and Fullwidth Forms
+ 0x0FF01, 0x0FF0C, 0x0FF0E, 0x0FF1B, 0x0FF1F, 0x0FF60, 0x0FFE0, 0x0FFE7,
+ // Specials (Part of)
+ 0x0FFF0, 0x0FFF8, 0x0FFFC, 0x0FFFD,
+ // Meroitic Hieroglyphs
+ 0x10980, 0x1099F,
+ // Siddham
+ 0x11580, 0x115FF,
+ // Zanabazar Square
+ 0x11A00, 0x11AAF,
+ // Egyptian Hieroglyphs
+ 0x13000, 0x1342F,
+ // Anatolian Hieroglyphs
+ 0x14400, 0x1467F,
+ // Ideographic Symbols and Punctuation
+ 0x16FE0, 0x16FFF,
+ // Tangut
+ 0x17000, 0x187FF,
+ // Tangut Components
+ 0x18800, 0x18AFF,
+ // Kana Supplement
+ 0x1B000, 0x1B0FF,
+ // Kana Extended-A
+ 0x1B100, 0x1B12F,
+ // Nushu
+ 0x1B170, 0x1B2FF,
+ // Byzantine Musical Symbols/Musical Symbols
+ 0x1D000, 0x1D1FF,
+ // Tai Xuan Jing Symbols/Counting Rod Numerals
+ 0x1D300, 0x1D37F,
+ // Sutton SignWriting
+ 0x1D800, 0x1DAAF,
+ // Mahjong Tiles/Domino Tiles/Playing Cards/Enclosed Alphanumeric Supplement
+ // Enclosed Ideographic Supplement/Enclosed Ideographic Supplement
+ // Emoticons/Ornamental Dingbats/Transport and Map Symbols/Alchemical
+ // Symbols Alchemical Symbols
+ 0x1F000, 0x1F7FF,
+ // Supplemental Symbols and Pictographs
+ 0x1F900, 0x1F9FF,
+ // CJK Unified Ideographs Extension B/C/D
+ // CJK Compatibility Ideographs Supplement
+ 0x20000, 0x2FFFD, 0x30000, 0x3FFFD,
+ // Supplementary Private Use Area-A
+ 0xF0000, 0xFFFFD,
+ // Supplementary Private Use Area-B
+ 0x100000, 0x10FFFD,
+};
+
+// https://html.spec.whatwg.org/multipage/scripting.html#prod-potentialcustomelementname
+static const UChar32 kIsPotentialCustomElementNameCharArray[] = {
+ '-', '.', '_', 0xB7,
+};
+
+static const UChar32 kIsPotentialCustomElementNameCharRanges[] = {
+ '0', '9', 'a', 'z', 0xC0, 0xD6, 0xD8, 0xF6,
+ 0xF8, 0x2FF, 0x300, 0x37D, 0x37F, 0x1FFF, 0x200C, 0x200D,
+ 0x203F, 0x2040, 0x2070, 0x218F, 0x2C00, 0x2FEF, 0x3001, 0xD7FF,
+ 0xF900, 0xFDCF, 0xFDF0, 0xFFFD, 0x10000, 0xEFFFF,
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/text/character_test.cc b/chromium/third_party/blink/renderer/platform/text/character_test.cc
new file mode 100644
index 00000000000..007442af75c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/character_test.cc
@@ -0,0 +1,334 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/text/character.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
+
+namespace blink {
+
+testing::AssertionResult IsCJKIdeographOrSymbolWithMessage(UChar32 codepoint) {
+ const size_t kFormatBufferSize = 10;
+ char formatted_as_hex[kFormatBufferSize];
+ snprintf(formatted_as_hex, kFormatBufferSize, "0x%x", codepoint);
+
+ if (Character::IsCJKIdeographOrSymbol(codepoint)) {
+ return testing::AssertionSuccess()
+ << "Codepoint " << formatted_as_hex << " is a CJKIdeographOrSymbol.";
+ }
+
+ return testing::AssertionFailure() << "Codepoint " << formatted_as_hex
+ << " is not a CJKIdeographOrSymbol.";
+}
+
+TEST(CharacterTest, HammerEmojiVsCJKIdeographOrSymbol) {
+ for (UChar32 test_char = 0; test_char < kMaxCodepoint; test_char++) {
+ if (Character::IsEmojiEmojiDefault(test_char)) {
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(test_char));
+ }
+ }
+}
+
+static void TestSpecificUChar32RangeIdeograph(UChar32 range_start,
+ UChar32 range_end,
+ bool before = true,
+ bool after = true) {
+ if (before) {
+ EXPECT_FALSE(Character::IsCJKIdeographOrSymbol(range_start - 1))
+ << std::hex << (range_start - 1);
+ }
+ EXPECT_TRUE(Character::IsCJKIdeographOrSymbol(range_start))
+ << std::hex << range_start;
+ UChar32 mid = static_cast<UChar32>(
+ (static_cast<uint64_t>(range_start) + range_end) / 2);
+ EXPECT_TRUE(Character::IsCJKIdeographOrSymbol(mid)) << std::hex << mid;
+ EXPECT_TRUE(Character::IsCJKIdeographOrSymbol(range_end))
+ << std::hex << range_end;
+ if (after) {
+ EXPECT_FALSE(Character::IsCJKIdeographOrSymbol(range_end + 1))
+ << std::hex << (range_end + 1);
+ }
+}
+
+TEST(CharacterTest, TestIsCJKIdeograph) {
+ // The basic CJK Unified Ideographs block.
+ TestSpecificUChar32RangeIdeograph(0x4E00, 0x9FFF, false);
+ // CJK Unified Ideographs Extension A.
+ TestSpecificUChar32RangeIdeograph(0x3400, 0x4DBF, false, false);
+ // CJK Unified Ideographs Extension A and Kangxi Radicals.
+ TestSpecificUChar32RangeIdeograph(0x2E80, 0x2FDF);
+ // CJK Strokes.
+ TestSpecificUChar32RangeIdeograph(0x31C0, 0x31EF, false);
+ // CJK Compatibility Ideographs.
+ TestSpecificUChar32RangeIdeograph(0xF900, 0xFAFF);
+ // CJK Unified Ideographs Extension B.
+ TestSpecificUChar32RangeIdeograph(0x20000, 0x2A6DF, true, false);
+ // CJK Unified Ideographs Extension C.
+ // CJK Unified Ideographs Extension D.
+ TestSpecificUChar32RangeIdeograph(0x2A700, 0x2B81F, false, false);
+ // CJK Compatibility Ideographs Supplement.
+ TestSpecificUChar32RangeIdeograph(0x2F800, 0x2FA1F, false, false);
+}
+
+static void TestSpecificUChar32RangeIdeographSymbol(UChar32 range_start,
+ UChar32 range_end) {
+ EXPECT_FALSE(IsCJKIdeographOrSymbolWithMessage(range_start - 1));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(range_start));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(
+ (UChar32)((uint64_t)range_start + (uint64_t)range_end) / 2));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(range_end));
+ EXPECT_FALSE(IsCJKIdeographOrSymbolWithMessage(range_end + 1));
+}
+
+TEST(CharacterTest, TestIsCJKIdeographOrSymbol) {
+ // CJK Compatibility Ideographs Supplement.
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x2C7));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x2CA));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x2CB));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x2D9));
+
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x2020));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x2021));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x2030));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x203B));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x203C));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x2042));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x2047));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x2048));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x2049));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x2051));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x20DD));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x20DE));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x2100));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x2103));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x2105));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x2109));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x210A));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x2113));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x2116));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x2121));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x212B));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x213B));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x2150));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x2151));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x2152));
+
+ TestSpecificUChar32RangeIdeographSymbol(0x2156, 0x215A);
+ TestSpecificUChar32RangeIdeographSymbol(0x2160, 0x216B);
+ TestSpecificUChar32RangeIdeographSymbol(0x2170, 0x217B);
+
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x217F));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x2189));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x2307));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x2312));
+
+ EXPECT_FALSE(IsCJKIdeographOrSymbolWithMessage(0x23BD));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x23BE));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x23C4));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x23CC));
+ EXPECT_FALSE(IsCJKIdeographOrSymbolWithMessage(0x23CD));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x23CE));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x2423));
+
+ TestSpecificUChar32RangeIdeographSymbol(0x2460, 0x2492);
+ TestSpecificUChar32RangeIdeographSymbol(0x249C, 0x24FF);
+
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x25A0));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x25A1));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x25A2));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x25AA));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x25AB));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x25B1));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x25B2));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x25B3));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x25B6));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x25B7));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x25BC));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x25BD));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x25C0));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x25C1));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x25C6));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x25C7));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x25C9));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x25CB));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x25CC));
+
+ TestSpecificUChar32RangeIdeographSymbol(0x25CE, 0x25D3);
+ TestSpecificUChar32RangeIdeographSymbol(0x25E2, 0x25E6);
+
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x25EF));
+
+ TestSpecificUChar32RangeIdeographSymbol(0x2600, 0x2603);
+
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x2605));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x2606));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x260E));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x2616));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x2617));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x2640));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x2642));
+
+ TestSpecificUChar32RangeIdeographSymbol(0x2660, 0x266F);
+ TestSpecificUChar32RangeIdeographSymbol(0x2672, 0x267D);
+
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x26A0));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x26BD));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x26BE));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x2713));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x271A));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x273F));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x2740));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x2756));
+
+ TestSpecificUChar32RangeIdeographSymbol(0x2763, 0x2764);
+ TestSpecificUChar32RangeIdeographSymbol(0x2776, 0x277F);
+
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x2B1A));
+
+ TestSpecificUChar32RangeIdeographSymbol(0x2FF0, 0x302D);
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x3031));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x312F));
+ EXPECT_FALSE(IsCJKIdeographOrSymbolWithMessage(0x3130));
+
+ EXPECT_FALSE(IsCJKIdeographOrSymbolWithMessage(0x318F));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x3190));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x319F));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x31BF));
+
+ EXPECT_FALSE(IsCJKIdeographOrSymbolWithMessage(0x31FF));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x3200));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x3300));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x33FF));
+
+ TestSpecificUChar32RangeIdeographSymbol(0xF860, 0xF862);
+ TestSpecificUChar32RangeIdeographSymbol(0xFE30, 0xFE6F);
+
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0xFE10));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0xFE11));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0xFE12));
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0xFE19));
+
+ EXPECT_FALSE(IsCJKIdeographOrSymbolWithMessage(0xFF0D));
+ EXPECT_FALSE(IsCJKIdeographOrSymbolWithMessage(0xFF1B));
+ EXPECT_FALSE(IsCJKIdeographOrSymbolWithMessage(0xFF1C));
+ EXPECT_FALSE(IsCJKIdeographOrSymbolWithMessage(0xFF1E));
+
+ TestSpecificUChar32RangeIdeographSymbol(0xFF00, 0xFFEF);
+
+ EXPECT_TRUE(IsCJKIdeographOrSymbolWithMessage(0x1F100));
+
+ TestSpecificUChar32RangeIdeographSymbol(0x1F110, 0x1F129);
+ TestSpecificUChar32RangeIdeographSymbol(0x1F130, 0x1F149);
+ TestSpecificUChar32RangeIdeographSymbol(0x1F150, 0x1F169);
+ TestSpecificUChar32RangeIdeographSymbol(0x1F170, 0x1F189);
+ TestSpecificUChar32RangeIdeographSymbol(0x1F1E6, 0x1F6FF);
+}
+
+TEST(CharacterTest, CanTextDecorationSkipInk) {
+ // ASCII
+ EXPECT_TRUE(Character::CanTextDecorationSkipInk('a'));
+ // Hangul Jamo
+ EXPECT_FALSE(Character::CanTextDecorationSkipInk(0x1100));
+ // Hiragana
+ EXPECT_FALSE(Character::CanTextDecorationSkipInk(0x3041));
+ // Bopomofo
+ EXPECT_FALSE(Character::CanTextDecorationSkipInk(0x31A0));
+ // The basic CJK Unified Ideographs block
+ EXPECT_FALSE(Character::CanTextDecorationSkipInk(0x4E01));
+ // Hangul Syllables
+ EXPECT_FALSE(Character::CanTextDecorationSkipInk(0xAC00));
+ // Plane 2 / CJK Ideograph Extension B
+ EXPECT_FALSE(Character::CanTextDecorationSkipInk(0x20000));
+}
+
+TEST(CharacterTest, TestEmojiTextDefault) {
+ // Text-default emoji, i.e.
+ // Emoji=Yes and EmojiPresentation=No
+ EXPECT_TRUE(Character::IsEmojiTextDefault(0x0023));
+ EXPECT_TRUE(Character::IsEmojiTextDefault(0x2744));
+ EXPECT_TRUE(Character::IsEmojiTextDefault(0x1F6F3));
+
+ // Non-emoji
+ EXPECT_FALSE(Character::IsEmojiTextDefault('A'));
+ EXPECT_FALSE(Character::IsEmojiTextDefault(0x2713));
+
+ // Emoji=Yes and EmojiPresentation=Yes
+ EXPECT_FALSE(Character::IsEmojiTextDefault(0x1F9C0));
+ EXPECT_FALSE(Character::IsEmojiTextDefault(0x26BD));
+ EXPECT_FALSE(Character::IsEmojiTextDefault(0x26BE));
+}
+
+TEST(CharacterTest, TestEmojiEmojiDefault) {
+ // Emoji=Yes and EmojiPresentation=Yes
+ EXPECT_TRUE(Character::IsEmojiEmojiDefault(0x231A));
+ EXPECT_TRUE(Character::IsEmojiEmojiDefault(0x1F191));
+ EXPECT_TRUE(Character::IsEmojiEmojiDefault(0x1F19A));
+ EXPECT_TRUE(Character::IsEmojiEmojiDefault(0x1F9C0));
+ // Kiss
+ EXPECT_TRUE(Character::IsEmojiEmojiDefault(0x1F48F));
+ // Couple with heart
+ EXPECT_TRUE(Character::IsEmojiEmojiDefault(0x1F491));
+ EXPECT_TRUE(Character::IsEmojiEmojiDefault(0x1F46A));
+
+ // Non-emoji
+ EXPECT_FALSE(Character::IsEmojiEmojiDefault('A'));
+
+ // Emoji=Yes and EmojiPresentation=No
+ EXPECT_FALSE(Character::IsEmojiEmojiDefault(0x1F202));
+}
+
+TEST(CharacterTest, TestEmojiModifierBase) {
+ EXPECT_TRUE(Character::IsEmojiModifierBase(0x261D));
+ EXPECT_TRUE(Character::IsEmojiModifierBase(0x1F470));
+ EXPECT_TRUE(Character::IsEmojiModifierBase(0x1F478));
+ EXPECT_TRUE(Character::IsEmojiModifierBase(0x1F918));
+ EXPECT_FALSE(Character::IsEmojiModifierBase('A'));
+ EXPECT_FALSE(Character::IsEmojiModifierBase(0x1F47D));
+}
+
+TEST(CharacterTest, TestEmoji40Data) {
+ EXPECT_TRUE(Character::IsEmojiEmojiDefault(0x1F32F));
+ EXPECT_TRUE(Character::IsEmojiEmojiDefault(0x1F57A));
+ EXPECT_TRUE(Character::IsEmojiEmojiDefault(0x1F919));
+ EXPECT_TRUE(Character::IsEmojiEmojiDefault(0x1F926));
+ EXPECT_TRUE(Character::IsEmojiModifierBase(0x1F574));
+ EXPECT_TRUE(Character::IsEmojiModifierBase(0x1F6CC));
+ EXPECT_TRUE(Character::IsEmojiModifierBase(0x1F919));
+ EXPECT_TRUE(Character::IsEmojiModifierBase(0x1F926));
+ EXPECT_TRUE(Character::IsEmojiModifierBase(0x1F933));
+}
+
+TEST(CharacterTest, LineBreakAndQuoteNotEmoji) {
+ EXPECT_FALSE(Character::IsEmojiTextDefault('\n'));
+ EXPECT_FALSE(Character::IsEmojiTextDefault('"'));
+}
+
+TEST(CharacterTest, Truncation) {
+ const UChar32 kBase = 0x90000;
+ UChar32 test_char = 0;
+
+ test_char = kBase + kSpaceCharacter;
+ EXPECT_FALSE(Character::TreatAsSpace(test_char));
+ test_char = kBase + kNoBreakSpaceCharacter;
+ EXPECT_FALSE(Character::TreatAsSpace(test_char));
+
+ test_char = kBase + kZeroWidthNonJoinerCharacter;
+ EXPECT_FALSE(Character::TreatAsZeroWidthSpace(test_char));
+ test_char = kBase + kZeroWidthJoinerCharacter;
+ EXPECT_FALSE(Character::TreatAsZeroWidthSpace(test_char));
+
+ test_char = kBase + 0x12;
+ EXPECT_FALSE(Character::TreatAsZeroWidthSpaceInComplexScript(test_char));
+ EXPECT_FALSE(Character::TreatAsZeroWidthSpaceInComplexScript(test_char));
+ test_char = kBase + kObjectReplacementCharacter;
+ EXPECT_FALSE(Character::TreatAsZeroWidthSpaceInComplexScript(test_char));
+
+ test_char = kBase + 0xA;
+ EXPECT_FALSE(Character::IsNormalizedCanvasSpaceCharacter(test_char));
+ test_char = kBase + 0x9;
+ EXPECT_FALSE(Character::IsNormalizedCanvasSpaceCharacter(test_char));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/date_time_format.cc b/chromium/third_party/blink/renderer/platform/text/date_time_format.cc
new file mode 100644
index 00000000000..aecc0ee4d26
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/date_time_format.cc
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2012 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/text/date_time_format.h"
+
+#include "third_party/blink/renderer/platform/wtf/ascii_ctype.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+
+namespace blink {
+
+static const DateTimeFormat::FieldType kLowerCaseToFieldTypeMap[26] = {
+ DateTimeFormat::kFieldTypePeriod, // a
+ DateTimeFormat::kFieldTypeInvalid, // b
+ DateTimeFormat::kFieldTypeLocalDayOfWeekStandAlon, // c
+ DateTimeFormat::kFieldTypeDayOfMonth, // d
+ DateTimeFormat::kFieldTypeLocalDayOfWeek, // e
+ DateTimeFormat::kFieldTypeInvalid, // f
+ DateTimeFormat::kFieldTypeModifiedJulianDay, // g
+ DateTimeFormat::kFieldTypeHour12, // h
+ DateTimeFormat::kFieldTypeInvalid, // i
+ DateTimeFormat::kFieldTypeInvalid, // j
+ DateTimeFormat::kFieldTypeHour24, // k
+ DateTimeFormat::kFieldTypeInvalid, // l
+ DateTimeFormat::kFieldTypeMinute, // m
+ DateTimeFormat::kFieldTypeInvalid, // n
+ DateTimeFormat::kFieldTypeInvalid, // o
+ DateTimeFormat::kFieldTypeInvalid, // p
+ DateTimeFormat::kFieldTypeQuaterStandAlone, // q
+ DateTimeFormat::kFieldTypeInvalid, // r
+ DateTimeFormat::kFieldTypeSecond, // s
+ DateTimeFormat::kFieldTypeInvalid, // t
+ DateTimeFormat::kFieldTypeExtendedYear, // u
+ DateTimeFormat::kFieldTypeNonLocationZone, // v
+ DateTimeFormat::kFieldTypeWeekOfYear, // w
+ DateTimeFormat::kFieldTypeInvalid, // x
+ DateTimeFormat::kFieldTypeYear, // y
+ DateTimeFormat::kFieldTypeZone, // z
+};
+
+static const DateTimeFormat::FieldType kUpperCaseToFieldTypeMap[26] = {
+ DateTimeFormat::kFieldTypeMillisecondsInDay, // A
+ DateTimeFormat::kFieldTypeInvalid, // B
+ DateTimeFormat::kFieldTypeInvalid, // C
+ DateTimeFormat::kFieldTypeDayOfYear, // D
+ DateTimeFormat::kFieldTypeDayOfWeek, // E
+ DateTimeFormat::kFieldTypeDayOfWeekInMonth, // F
+ DateTimeFormat::kFieldTypeEra, // G
+ DateTimeFormat::kFieldTypeHour23, // H
+ DateTimeFormat::kFieldTypeInvalid, // I
+ DateTimeFormat::kFieldTypeInvalid, // J
+ DateTimeFormat::kFieldTypeHour11, // K
+ DateTimeFormat::kFieldTypeMonthStandAlone, // L
+ DateTimeFormat::kFieldTypeMonth, // M
+ DateTimeFormat::kFieldTypeInvalid, // N
+ DateTimeFormat::kFieldTypeInvalid, // O
+ DateTimeFormat::kFieldTypeInvalid, // P
+ DateTimeFormat::kFieldTypeQuater, // Q
+ DateTimeFormat::kFieldTypeInvalid, // R
+ DateTimeFormat::kFieldTypeFractionalSecond, // S
+ DateTimeFormat::kFieldTypeInvalid, // T
+ DateTimeFormat::kFieldTypeInvalid, // U
+ DateTimeFormat::kFieldTypeInvalid, // V
+ DateTimeFormat::kFieldTypeWeekOfMonth, // W
+ DateTimeFormat::kFieldTypeInvalid, // X
+ DateTimeFormat::kFieldTypeYearOfWeekOfYear, // Y
+ DateTimeFormat::kFieldTypeRFC822Zone, // Z
+};
+
+static DateTimeFormat::FieldType MapCharacterToFieldType(const UChar ch) {
+ if (IsASCIIUpper(ch))
+ return kUpperCaseToFieldTypeMap[ch - 'A'];
+
+ if (IsASCIILower(ch))
+ return kLowerCaseToFieldTypeMap[ch - 'a'];
+
+ return DateTimeFormat::kFieldTypeLiteral;
+}
+
+bool DateTimeFormat::Parse(const String& source, TokenHandler& token_handler) {
+ enum State {
+ kStateInQuote,
+ kStateInQuoteQuote,
+ kStateLiteral,
+ kStateQuote,
+ kStateSymbol,
+ } state = kStateLiteral;
+
+ FieldType field_type = kFieldTypeLiteral;
+ StringBuilder literal_buffer;
+ int field_counter = 0;
+
+ for (unsigned index = 0; index < source.length(); ++index) {
+ const UChar ch = source[index];
+ switch (state) {
+ case kStateInQuote:
+ if (ch == '\'') {
+ state = kStateInQuoteQuote;
+ break;
+ }
+
+ literal_buffer.Append(ch);
+ break;
+
+ case kStateInQuoteQuote:
+ if (ch == '\'') {
+ literal_buffer.Append('\'');
+ state = kStateInQuote;
+ break;
+ }
+
+ field_type = MapCharacterToFieldType(ch);
+ if (field_type == kFieldTypeInvalid)
+ return false;
+
+ if (field_type == kFieldTypeLiteral) {
+ literal_buffer.Append(ch);
+ state = kStateLiteral;
+ break;
+ }
+
+ if (literal_buffer.length()) {
+ token_handler.VisitLiteral(literal_buffer.ToString());
+ literal_buffer.Clear();
+ }
+
+ field_counter = 1;
+ state = kStateSymbol;
+ break;
+
+ case kStateLiteral:
+ if (ch == '\'') {
+ state = kStateQuote;
+ break;
+ }
+
+ field_type = MapCharacterToFieldType(ch);
+ if (field_type == kFieldTypeInvalid)
+ return false;
+
+ if (field_type == kFieldTypeLiteral) {
+ literal_buffer.Append(ch);
+ break;
+ }
+
+ if (literal_buffer.length()) {
+ token_handler.VisitLiteral(literal_buffer.ToString());
+ literal_buffer.Clear();
+ }
+
+ field_counter = 1;
+ state = kStateSymbol;
+ break;
+
+ case kStateQuote:
+ literal_buffer.Append(ch);
+ state = ch == '\'' ? kStateLiteral : kStateInQuote;
+ break;
+
+ case kStateSymbol: {
+ DCHECK_NE(field_type, kFieldTypeInvalid);
+ DCHECK_NE(field_type, kFieldTypeLiteral);
+ DCHECK(literal_buffer.IsEmpty());
+
+ FieldType field_type2 = MapCharacterToFieldType(ch);
+ if (field_type2 == kFieldTypeInvalid)
+ return false;
+
+ if (field_type == field_type2) {
+ ++field_counter;
+ break;
+ }
+
+ token_handler.VisitField(field_type, field_counter);
+
+ if (field_type2 == kFieldTypeLiteral) {
+ if (ch == '\'') {
+ state = kStateQuote;
+ } else {
+ literal_buffer.Append(ch);
+ state = kStateLiteral;
+ }
+ break;
+ }
+
+ field_counter = 1;
+ field_type = field_type2;
+ break;
+ }
+ }
+ }
+
+ DCHECK_NE(field_type, kFieldTypeInvalid);
+
+ switch (state) {
+ case kStateLiteral:
+ case kStateInQuoteQuote:
+ if (literal_buffer.length())
+ token_handler.VisitLiteral(literal_buffer.ToString());
+ return true;
+
+ case kStateQuote:
+ case kStateInQuote:
+ if (literal_buffer.length())
+ token_handler.VisitLiteral(literal_buffer.ToString());
+ return false;
+
+ case kStateSymbol:
+ DCHECK_NE(field_type, kFieldTypeLiteral);
+ DCHECK(!literal_buffer.length());
+ token_handler.VisitField(field_type, field_counter);
+ return true;
+ }
+
+ NOTREACHED();
+ return false;
+}
+
+static bool IsASCIIAlphabetOrQuote(UChar ch) {
+ return IsASCIIAlpha(ch) || ch == '\'';
+}
+
+void DateTimeFormat::QuoteAndappend(const String& literal,
+ StringBuilder& buffer) {
+ if (literal.length() <= 0)
+ return;
+
+ if (literal.Find(IsASCIIAlphabetOrQuote) == kNotFound) {
+ buffer.Append(literal);
+ return;
+ }
+
+ if (literal.find('\'') == kNotFound) {
+ buffer.Append('\'');
+ buffer.Append(literal);
+ buffer.Append('\'');
+ return;
+ }
+
+ for (unsigned i = 0; i < literal.length(); ++i) {
+ if (literal[i] == '\'') {
+ buffer.Append("''");
+ } else {
+ String escaped = literal.Substring(i);
+ escaped.Replace("'", "''");
+ buffer.Append('\'');
+ buffer.Append(escaped);
+ buffer.Append('\'');
+ return;
+ }
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/date_time_format.h b/chromium/third_party/blink/renderer/platform/text/date_time_format.h
new file mode 100644
index 00000000000..e15bc5a467f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/date_time_format.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2012 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_DATE_TIME_FORMAT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_DATE_TIME_FORMAT_H_
+
+#include "base/macros.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+// DateTimeFormat parses date time format defined in Unicode Technical
+// standard 35, Locale Data Markup Language (LDML)[1].
+// [1] LDML http://unicode.org/reports/tr35/tr35-6.html#Date_Format_Patterns
+class PLATFORM_EXPORT DateTimeFormat {
+ STATIC_ONLY(DateTimeFormat);
+
+ public:
+ enum FieldType {
+ kFieldTypeInvalid,
+ kFieldTypeLiteral,
+
+ // Era: AD
+ kFieldTypeEra = 'G',
+
+ // Year: 1996
+ kFieldTypeYear = 'y',
+ kFieldTypeYearOfWeekOfYear = 'Y',
+ kFieldTypeExtendedYear = 'u',
+
+ // Quater: Q2
+ kFieldTypeQuater = 'Q',
+ kFieldTypeQuaterStandAlone = 'q',
+
+ // Month: September
+ kFieldTypeMonth = 'M',
+ kFieldTypeMonthStandAlone = 'L',
+
+ // Week: 42
+ kFieldTypeWeekOfYear = 'w',
+ kFieldTypeWeekOfMonth = 'W',
+
+ // Day: 12
+ kFieldTypeDayOfMonth = 'd',
+ kFieldTypeDayOfYear = 'D',
+ kFieldTypeDayOfWeekInMonth = 'F',
+ kFieldTypeModifiedJulianDay = 'g',
+
+ // Week Day: Tuesday
+ kFieldTypeDayOfWeek = 'E',
+ kFieldTypeLocalDayOfWeek = 'e',
+ kFieldTypeLocalDayOfWeekStandAlon = 'c',
+
+ // Period: AM or PM
+ kFieldTypePeriod = 'a',
+
+ // Hour: 7
+ kFieldTypeHour12 = 'h',
+ kFieldTypeHour23 = 'H',
+ kFieldTypeHour11 = 'K',
+ kFieldTypeHour24 = 'k',
+
+ // Minute: 59
+ kFieldTypeMinute = 'm',
+
+ // Second: 12
+ kFieldTypeSecond = 's',
+ kFieldTypeFractionalSecond = 'S',
+ kFieldTypeMillisecondsInDay = 'A',
+
+ // Zone: PDT
+ kFieldTypeZone = 'z',
+ kFieldTypeRFC822Zone = 'Z',
+ kFieldTypeNonLocationZone = 'v',
+ };
+
+ class TokenHandler {
+ STACK_ALLOCATED();
+
+ public:
+ TokenHandler() = default;
+ virtual ~TokenHandler() = default;
+ virtual void VisitField(FieldType, int number_of_pattern_characters) = 0;
+ virtual void VisitLiteral(const String&) = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(TokenHandler);
+ };
+
+ // Returns true if succeeded, false if failed.
+ static bool Parse(const String&, TokenHandler&);
+ static void QuoteAndappend(const String&, StringBuilder&);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_DATE_TIME_FORMAT_H_
diff --git a/chromium/third_party/blink/renderer/platform/text/date_time_format_test.cc b/chromium/third_party/blink/renderer/platform/text/date_time_format_test.cc
new file mode 100644
index 00000000000..2cb0652943e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/date_time_format_test.cc
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2012 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/text/date_time_format.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+
+namespace blink {
+
+class DateTimeFormatTest : public testing::Test {
+ public:
+ using FieldType = DateTimeFormat::FieldType;
+
+ struct Token {
+ String string;
+ int count;
+ FieldType field_type;
+
+ Token(FieldType field_type, int count = 1)
+ : count(count), field_type(field_type) {
+ DCHECK_NE(field_type, DateTimeFormat::kFieldTypeLiteral);
+ }
+
+ Token(const String& string)
+ : string(string),
+ count(0),
+ field_type(DateTimeFormat::kFieldTypeLiteral) {}
+
+ bool operator==(const Token& other) const {
+ return field_type == other.field_type && count == other.count &&
+ string == other.string;
+ }
+
+ String ToString() const {
+ switch (field_type) {
+ case DateTimeFormat::kFieldTypeInvalid:
+ return "*invalid*";
+ case DateTimeFormat::kFieldTypeLiteral: {
+ StringBuilder builder;
+ builder.Append('"');
+ builder.Append(string);
+ builder.Append('"');
+ return builder.ToString();
+ }
+ default:
+ return String::Format("Token(%d, %d)", field_type, count);
+ }
+ }
+ };
+
+ class Tokens {
+ public:
+ Tokens() = default;
+
+ explicit Tokens(const Vector<Token> tokens) : tokens_(tokens) {}
+
+ explicit Tokens(const String& string) { tokens_.push_back(Token(string)); }
+
+ explicit Tokens(Token token1) { tokens_.push_back(token1); }
+
+ Tokens(Token token1, Token token2) {
+ tokens_.push_back(token1);
+ tokens_.push_back(token2);
+ }
+
+ Tokens(Token token1, Token token2, Token token3) {
+ tokens_.push_back(token1);
+ tokens_.push_back(token2);
+ tokens_.push_back(token3);
+ }
+
+ Tokens(Token token1, Token token2, Token token3, Token token4) {
+ tokens_.push_back(token1);
+ tokens_.push_back(token2);
+ tokens_.push_back(token3);
+ tokens_.push_back(token4);
+ }
+
+ Tokens(Token token1,
+ Token token2,
+ Token token3,
+ Token token4,
+ Token token5) {
+ tokens_.push_back(token1);
+ tokens_.push_back(token2);
+ tokens_.push_back(token3);
+ tokens_.push_back(token4);
+ tokens_.push_back(token5);
+ }
+
+ Tokens(Token token1,
+ Token token2,
+ Token token3,
+ Token token4,
+ Token token5,
+ Token token6) {
+ tokens_.push_back(token1);
+ tokens_.push_back(token2);
+ tokens_.push_back(token3);
+ tokens_.push_back(token4);
+ tokens_.push_back(token5);
+ tokens_.push_back(token6);
+ }
+
+ bool operator==(const Tokens& other) const {
+ return tokens_ == other.tokens_;
+ }
+
+ String ToString() const {
+ StringBuilder builder;
+ builder.Append("Tokens(");
+ for (unsigned index = 0; index < tokens_.size(); ++index) {
+ if (index)
+ builder.Append(',');
+ builder.Append(tokens_[index].ToString());
+ }
+ builder.Append(')');
+ return builder.ToString();
+ }
+
+ private:
+ Vector<Token> tokens_;
+ };
+
+ protected:
+ Tokens Parse(const String& format_string) {
+ TokenHandler handler;
+ if (!DateTimeFormat::Parse(format_string, handler))
+ return Tokens(Token("*failed*"));
+ return handler.GetTokens();
+ }
+
+ FieldType Single(const char ch) {
+ char format_string[2];
+ format_string[0] = ch;
+ format_string[1] = 0;
+ TokenHandler handler;
+ if (!DateTimeFormat::Parse(format_string, handler))
+ return DateTimeFormat::kFieldTypeInvalid;
+ return handler.GetFieldType(0);
+ }
+
+ private:
+ class TokenHandler : public DateTimeFormat::TokenHandler {
+ public:
+ ~TokenHandler() override = default;
+
+ FieldType GetFieldType(int index) const {
+ return index >= 0 && index < static_cast<int>(tokens_.size())
+ ? tokens_[index].field_type
+ : DateTimeFormat::kFieldTypeInvalid;
+ }
+
+ Tokens GetTokens() const { return Tokens(tokens_); }
+
+ private:
+ void VisitField(FieldType field_type, int count) override {
+ tokens_.push_back(Token(field_type, count));
+ }
+
+ void VisitLiteral(const String& string) override {
+ tokens_.push_back(Token(string));
+ }
+
+ Vector<Token> tokens_;
+ };
+};
+
+std::ostream& operator<<(std::ostream& os,
+ const DateTimeFormatTest::Tokens& tokens) {
+ return os << tokens.ToString().Ascii().data();
+}
+
+TEST_F(DateTimeFormatTest, CommonPattern) {
+ EXPECT_EQ(Tokens(), Parse(""));
+
+ EXPECT_EQ(Tokens(Token(DateTimeFormat::kFieldTypeYear, 4), Token("-"),
+ Token(DateTimeFormat::kFieldTypeMonth, 2), Token("-"),
+ Token(DateTimeFormat::kFieldTypeDayOfMonth, 2)),
+ Parse("yyyy-MM-dd"));
+
+ EXPECT_EQ(Tokens(Token(DateTimeFormat::kFieldTypeHour24, 2), Token(":"),
+ Token(DateTimeFormat::kFieldTypeMinute, 2), Token(":"),
+ Token(DateTimeFormat::kFieldTypeSecond, 2)),
+ Parse("kk:mm:ss"));
+
+ EXPECT_EQ(Tokens(Token(DateTimeFormat::kFieldTypeHour12), Token(":"),
+ Token(DateTimeFormat::kFieldTypeMinute), Token(" "),
+ Token(DateTimeFormat::kFieldTypePeriod)),
+ Parse("h:m a"));
+
+ EXPECT_EQ(Tokens(Token(DateTimeFormat::kFieldTypeYear), Token("Nen "),
+ Token(DateTimeFormat::kFieldTypeMonth), Token("Getsu "),
+ Token(DateTimeFormat::kFieldTypeDayOfMonth), Token("Nichi")),
+ Parse("y'Nen' M'Getsu' d'Nichi'"));
+}
+
+TEST_F(DateTimeFormatTest, MissingClosingQuote) {
+ EXPECT_EQ(Tokens("*failed*"), Parse("'foo"));
+ EXPECT_EQ(Tokens("*failed*"), Parse("fo'o"));
+ EXPECT_EQ(Tokens("*failed*"), Parse("foo'"));
+}
+
+TEST_F(DateTimeFormatTest, Quote) {
+ EXPECT_EQ(Tokens("FooBar"), Parse("'FooBar'"));
+ EXPECT_EQ(Tokens("'"), Parse("''"));
+ EXPECT_EQ(Tokens("'-'"), Parse("''-''"));
+ EXPECT_EQ(Tokens("Foo'Bar"), Parse("'Foo''Bar'"));
+ EXPECT_EQ(Tokens(Token(DateTimeFormat::kFieldTypeEra), Token("'s")),
+ Parse("G'''s'"));
+ EXPECT_EQ(Tokens(Token(DateTimeFormat::kFieldTypeEra), Token("'"),
+ Token(DateTimeFormat::kFieldTypeSecond)),
+ Parse("G''s"));
+}
+
+TEST_F(DateTimeFormatTest, SingleLowerCaseCharacter) {
+ EXPECT_EQ(DateTimeFormat::kFieldTypeInvalid, Single('b'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeLocalDayOfWeekStandAlon, Single('c'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeDayOfMonth, Single('d'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeLocalDayOfWeek, Single('e'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeModifiedJulianDay, Single('g'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeHour12, Single('h'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeHour24, Single('k'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeMinute, Single('m'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeQuaterStandAlone, Single('q'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeSecond, Single('s'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeExtendedYear, Single('u'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeNonLocationZone, Single('v'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeWeekOfMonth, Single('W'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeYear, Single('y'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeZone, Single('z'));
+}
+
+TEST_F(DateTimeFormatTest, SingleLowerCaseInvalid) {
+ EXPECT_EQ(DateTimeFormat::kFieldTypePeriod, Single('a'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeInvalid, Single('f'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeInvalid, Single('i'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeInvalid, Single('j'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeInvalid, Single('l'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeInvalid, Single('n'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeInvalid, Single('o'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeInvalid, Single('p'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeInvalid, Single('r'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeInvalid, Single('t'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeInvalid, Single('x'));
+}
+
+TEST_F(DateTimeFormatTest, SingleUpperCaseCharacter) {
+ EXPECT_EQ(DateTimeFormat::kFieldTypeMillisecondsInDay, Single('A'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeDayOfYear, Single('D'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeDayOfWeek, Single('E'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeDayOfWeekInMonth, Single('F'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeEra, Single('G'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeHour23, Single('H'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeHour11, Single('K'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeMonthStandAlone, Single('L'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeMonth, Single('M'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeQuater, Single('Q'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeFractionalSecond, Single('S'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeWeekOfYear, Single('w'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeYearOfWeekOfYear, Single('Y'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeRFC822Zone, Single('Z'));
+}
+
+TEST_F(DateTimeFormatTest, SingleUpperCaseInvalid) {
+ EXPECT_EQ(DateTimeFormat::kFieldTypeInvalid, Single('B'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeInvalid, Single('C'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeInvalid, Single('I'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeInvalid, Single('J'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeInvalid, Single('N'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeInvalid, Single('O'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeInvalid, Single('P'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeInvalid, Single('R'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeInvalid, Single('T'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeInvalid, Single('U'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeInvalid, Single('V'));
+ EXPECT_EQ(DateTimeFormat::kFieldTypeInvalid, Single('X'));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/decode_escape_sequences.h b/chromium/third_party/blink/renderer/platform/text/decode_escape_sequences.h
new file mode 100644
index 00000000000..d40324ed0ac
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/decode_escape_sequences.h
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2011 Daniel Bates (dbates@intudata.com). All Rights Reserved.
+ * Copyright (c) 2012 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:
+ * 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 APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_DECODE_ESCAPE_SEQUENCES_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_DECODE_ESCAPE_SEQUENCES_H_
+
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/ascii_ctype.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
+
+namespace blink {
+
+// See
+// <http://en.wikipedia.org/wiki/Percent-encoding#Non-standard_implementations>.
+struct Unicode16BitEscapeSequence {
+ STATIC_ONLY(Unicode16BitEscapeSequence);
+ enum { kSequenceSize = 6 }; // e.g. %u26C4
+ static size_t FindInString(const String& string, size_t start_position) {
+ return string.Find("%u", start_position);
+ }
+ static size_t FindEndOfRun(const String& string,
+ size_t start_position,
+ size_t end_position) {
+ size_t run_end = start_position;
+ while (end_position - run_end >= kSequenceSize && string[run_end] == '%' &&
+ string[run_end + 1] == 'u' && IsASCIIHexDigit(string[run_end + 2]) &&
+ IsASCIIHexDigit(string[run_end + 3]) &&
+ IsASCIIHexDigit(string[run_end + 4]) &&
+ IsASCIIHexDigit(string[run_end + 5])) {
+ run_end += kSequenceSize;
+ }
+ return run_end;
+ }
+
+ template <typename CharType>
+ static String DecodeRun(const CharType* run,
+ size_t run_length,
+ const WTF::TextEncoding&) {
+ // Each %u-escape sequence represents a UTF-16 code unit. See
+ // <http://www.w3.org/International/iri-edit/draft-duerst-iri.html#anchor29>.
+ // For 16-bit escape sequences, we know that findEndOfRun() has given us a
+ // contiguous run of sequences without any intervening characters, so decode
+ // the run without additional checks.
+ size_t number_of_sequences = run_length / kSequenceSize;
+ StringBuilder builder;
+ builder.ReserveCapacity(number_of_sequences);
+ while (number_of_sequences--) {
+ UChar code_unit =
+ (ToASCIIHexValue(run[2]) << 12) | (ToASCIIHexValue(run[3]) << 8) |
+ (ToASCIIHexValue(run[4]) << 4) | ToASCIIHexValue(run[5]);
+ builder.Append(code_unit);
+ run += kSequenceSize;
+ }
+ return builder.ToString();
+ }
+};
+
+struct URLEscapeSequence {
+ enum { kSequenceSize = 3 }; // e.g. %41
+ static size_t FindInString(const String& string, size_t start_position) {
+ return string.find('%', start_position);
+ }
+ static size_t FindEndOfRun(const String& string,
+ size_t start_position,
+ size_t end_position) {
+ // Make the simplifying assumption that supported encodings may have up to
+ // two unescaped characters in the range 0x40 - 0x7F as the trailing bytes
+ // of their sequences which need to be passed into the decoder as part of
+ // the run. In other words, we end the run at the first value outside of the
+ // 0x40 - 0x7F range, after two values in this range, or at a %-sign that
+ // does not introduce a valid escape sequence.
+ size_t run_end = start_position;
+ int number_of_trailing_characters = 0;
+ while (run_end < end_position) {
+ if (string[run_end] == '%') {
+ if (end_position - run_end >= kSequenceSize &&
+ IsASCIIHexDigit(string[run_end + 1]) &&
+ IsASCIIHexDigit(string[run_end + 2])) {
+ run_end += kSequenceSize;
+ number_of_trailing_characters = 0;
+ } else
+ break;
+ } else if (string[run_end] >= 0x40 && string[run_end] <= 0x7F &&
+ number_of_trailing_characters < 2) {
+ run_end += 1;
+ number_of_trailing_characters += 1;
+ } else
+ break;
+ }
+ return run_end;
+ }
+
+ template <typename CharType>
+ static String DecodeRun(const CharType* run,
+ size_t run_length,
+ const WTF::TextEncoding& encoding) {
+ // For URL escape sequences, we know that findEndOfRun() has given us a run
+ // where every %-sign introduces a valid escape sequence, but there may be
+ // characters between the sequences.
+ Vector<char, 512> buffer;
+ buffer.resize(
+ run_length); // Unescaping hex sequences only makes the length smaller.
+ char* p = buffer.data();
+ const CharType* run_end = run + run_length;
+ while (run < run_end) {
+ if (run[0] == '%') {
+ *p++ = (ToASCIIHexValue(run[1]) << 4) | ToASCIIHexValue(run[2]);
+ run += kSequenceSize;
+ } else {
+ *p++ = run[0];
+ run += 1;
+ }
+ }
+ DCHECK_GE(
+ buffer.size(),
+ static_cast<size_t>(p - buffer.data())); // Prove buffer not overrun.
+ return (encoding.IsValid() ? encoding : UTF8Encoding())
+ .Decode(buffer.data(), p - buffer.data());
+ }
+};
+
+template <typename EscapeSequence>
+String DecodeEscapeSequences(const String& string,
+ const WTF::TextEncoding& encoding) {
+ StringBuilder result;
+ size_t length = string.length();
+ size_t decoded_position = 0;
+ size_t search_position = 0;
+ size_t encoded_run_position;
+ while ((encoded_run_position = EscapeSequence::FindInString(
+ string, search_position)) != kNotFound) {
+ size_t encoded_run_end =
+ EscapeSequence::FindEndOfRun(string, encoded_run_position, length);
+ search_position = encoded_run_end;
+ if (encoded_run_end == encoded_run_position) {
+ ++search_position;
+ continue;
+ }
+
+ String decoded =
+ string.Is8Bit() ? EscapeSequence::DecodeRun(
+ string.Characters8() + encoded_run_position,
+ encoded_run_end - encoded_run_position, encoding)
+ : EscapeSequence::DecodeRun(
+ string.Characters16() + encoded_run_position,
+ encoded_run_end - encoded_run_position, encoding);
+
+ if (decoded.IsEmpty())
+ continue;
+
+ result.Append(string, decoded_position,
+ encoded_run_position - decoded_position);
+ result.Append(decoded);
+ decoded_position = encoded_run_end;
+ }
+ result.Append(string, decoded_position, length - decoded_position);
+ return result.ToString();
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_DECODE_ESCAPE_SEQUENCES_H_
diff --git a/chromium/third_party/blink/renderer/platform/text/fuchsia/hyphenation_fuchsia.cc b/chromium/third_party/blink/renderer/platform/text/fuchsia/hyphenation_fuchsia.cc
new file mode 100644
index 00000000000..54463f94c44
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/fuchsia/hyphenation_fuchsia.cc
@@ -0,0 +1,15 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/text/hyphenation.h"
+
+namespace blink {
+
+scoped_refptr<Hyphenation> Hyphenation::PlatformGetHyphenation(
+ const AtomicString&) {
+ // TODO(fuchsia): Implement this when UI support is ready. crbug.com/750946
+ return nullptr;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/hyphenation.cc b/chromium/third_party/blink/renderer/platform/text/hyphenation.cc
new file mode 100644
index 00000000000..fcc5649c497
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/hyphenation.cc
@@ -0,0 +1,48 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/text/hyphenation.h"
+
+#include "third_party/blink/renderer/platform/fonts/font.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_view.h"
+
+namespace blink {
+
+size_t Hyphenation::FirstHyphenLocation(const StringView& text,
+ size_t after_index) const {
+ Vector<size_t, 8> hyphen_locations = HyphenLocations(text);
+ for (auto it = hyphen_locations.rbegin(); it != hyphen_locations.rend();
+ ++it) {
+ if (*it > after_index)
+ return *it;
+ }
+ return 0;
+}
+
+Vector<size_t, 8> Hyphenation::HyphenLocations(const StringView& text) const {
+ Vector<size_t, 8> hyphen_locations;
+ size_t hyphen_location = text.length();
+ if (hyphen_location <= kMinimumSuffixLength)
+ return hyphen_locations;
+ hyphen_location -= kMinimumSuffixLength;
+
+ while ((hyphen_location = LastHyphenLocation(text, hyphen_location)) >=
+ kMinimumPrefixLength)
+ hyphen_locations.push_back(hyphen_location);
+
+ return hyphen_locations;
+}
+
+int Hyphenation::MinimumPrefixWidth(const Font& font) {
+ // If the maximum width available for the prefix before the hyphen is small,
+ // then it is very unlikely that an hyphenation opportunity exists, so do not
+ // bother to look for it. These are heuristic numbers for performance added
+ // in http://wkb.ug/45606
+ const int kMinimumPrefixWidthNumerator = 5;
+ const int kMinimumPrefixWidthDenominator = 4;
+ return font.GetFontDescription().ComputedPixelSize() *
+ kMinimumPrefixWidthNumerator / kMinimumPrefixWidthDenominator;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/hyphenation.h b/chromium/third_party/blink/renderer/platform/text/hyphenation.h
new file mode 100644
index 00000000000..c903fbf942a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/hyphenation.h
@@ -0,0 +1,48 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_HYPHENATION_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_HYPHENATION_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+
+namespace blink {
+
+class Font;
+
+class PLATFORM_EXPORT Hyphenation : public RefCounted<Hyphenation> {
+ public:
+ virtual ~Hyphenation() = default;
+
+ // Find the last hyphenation location before |before_index|.
+ // Returns 0 if no hyphenation locations were found.
+ virtual size_t LastHyphenLocation(const StringView&,
+ size_t before_index) const = 0;
+
+ // Find the first hyphenation location after |after_index|.
+ // Returns 0 if no hyphenation locations were found.
+ virtual size_t FirstHyphenLocation(const StringView&,
+ size_t after_index) const;
+
+ // Find all hyphenation locations in the reverse order.
+ virtual Vector<size_t, 8> HyphenLocations(const StringView&) const;
+
+ static const unsigned kMinimumPrefixLength = 2;
+ static const unsigned kMinimumSuffixLength = 2;
+ static int MinimumPrefixWidth(const Font&);
+
+ private:
+ friend class LayoutLocale;
+ static scoped_refptr<Hyphenation> PlatformGetHyphenation(
+ const AtomicString& locale);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_HYPHENATION_H_
diff --git a/chromium/third_party/blink/renderer/platform/text/hyphenation/hyphenation_minikin.cc b/chromium/third_party/blink/renderer/platform/text/hyphenation/hyphenation_minikin.cc
new file mode 100644
index 00000000000..68d8ee7de49
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/hyphenation/hyphenation_minikin.cc
@@ -0,0 +1,209 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/text/hyphenation/hyphenation_minikin.h"
+
+#include <algorithm>
+#include <utility>
+#include <vector>
+
+#include "base/files/file.h"
+#include "base/files/memory_mapped_file.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/timer/elapsed_timer.h"
+#include "third_party/blink/public/platform/interface_provider.h"
+#include "third_party/blink/public/platform/modules/hyphenation/hyphenation.mojom-blink.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/layout_locale.h"
+#include "third_party/blink/renderer/platform/text/character.h"
+#include "third_party/blink/renderer/platform/text/hyphenation/hyphenator_aosp.h"
+
+namespace blink {
+
+namespace {
+
+template <typename CharType>
+StringView SkipLeadingSpaces(const CharType* text,
+ unsigned length,
+ unsigned* num_leading_spaces_out) {
+ const CharType* begin = text;
+ const CharType* end = text + length;
+ while (text != end && Character::TreatAsSpace(*text))
+ text++;
+ *num_leading_spaces_out = text - begin;
+ return StringView(text, end - text);
+}
+
+StringView SkipLeadingSpaces(const StringView& text,
+ unsigned* num_leading_spaces_out) {
+ if (text.Is8Bit()) {
+ return SkipLeadingSpaces(text.Characters8(), text.length(),
+ num_leading_spaces_out);
+ }
+ return SkipLeadingSpaces(text.Characters16(), text.length(),
+ num_leading_spaces_out);
+}
+
+} // namespace
+
+using Hyphenator = android::Hyphenator;
+
+static mojom::blink::HyphenationPtr ConnectToRemoteService() {
+ mojom::blink::HyphenationPtr service;
+ Platform::Current()->GetInterfaceProvider()->GetInterface(
+ mojo::MakeRequest(&service));
+ return service;
+}
+
+static const mojom::blink::HyphenationPtr& GetService() {
+ DEFINE_STATIC_LOCAL(mojom::blink::HyphenationPtr, service,
+ (ConnectToRemoteService()));
+ return service;
+}
+
+bool HyphenationMinikin::OpenDictionary(const AtomicString& locale) {
+ const mojom::blink::HyphenationPtr& service = GetService();
+ base::File file;
+ base::ElapsedTimer timer;
+ service->OpenDictionary(locale, &file);
+ UMA_HISTOGRAM_TIMES("Hyphenation.Open", timer.Elapsed());
+
+ return OpenDictionary(std::move(file));
+}
+
+bool HyphenationMinikin::OpenDictionary(base::File file) {
+ if (!file.IsValid())
+ return false;
+ if (!file_.Initialize(std::move(file))) {
+ DLOG(ERROR) << "mmap failed";
+ return false;
+ }
+
+ hyphenator_ = base::WrapUnique(Hyphenator::loadBinary(file_.data()));
+
+ return true;
+}
+
+std::vector<uint8_t> HyphenationMinikin::Hyphenate(
+ const StringView& text) const {
+ std::vector<uint8_t> result;
+ if (text.Is8Bit()) {
+ String text16_bit = text.ToString();
+ text16_bit.Ensure16Bit();
+ hyphenator_->hyphenate(&result, text16_bit.Characters16(),
+ text16_bit.length());
+ } else {
+ hyphenator_->hyphenate(&result, text.Characters16(), text.length());
+ }
+ return result;
+}
+
+size_t HyphenationMinikin::LastHyphenLocation(const StringView& text,
+ size_t before_index) const {
+ unsigned num_leading_spaces;
+ StringView word = SkipLeadingSpaces(text, &num_leading_spaces);
+ if (before_index <= num_leading_spaces)
+ return 0;
+ before_index = std::min<size_t>(before_index - num_leading_spaces,
+ word.length() - kMinimumSuffixLength);
+
+ if (word.length() < kMinimumPrefixLength + kMinimumSuffixLength ||
+ before_index <= kMinimumPrefixLength)
+ return 0;
+
+ std::vector<uint8_t> result = Hyphenate(word);
+ CHECK_LE(before_index, result.size());
+ CHECK_GE(before_index, 1u);
+ static_assert(kMinimumPrefixLength >= 1, "|beforeIndex - 1| can underflow");
+ for (size_t i = before_index - 1; i >= kMinimumPrefixLength; i--) {
+ if (result[i])
+ return i + num_leading_spaces;
+ }
+ return 0;
+}
+
+Vector<size_t, 8> HyphenationMinikin::HyphenLocations(
+ const StringView& text) const {
+ unsigned num_leading_spaces;
+ StringView word = SkipLeadingSpaces(text, &num_leading_spaces);
+
+ Vector<size_t, 8> hyphen_locations;
+ if (word.length() < kMinimumPrefixLength + kMinimumSuffixLength)
+ return hyphen_locations;
+
+ std::vector<uint8_t> result = Hyphenate(word);
+ static_assert(kMinimumPrefixLength >= 1,
+ "Change the 'if' above if this fails");
+ for (size_t i = word.length() - kMinimumSuffixLength - 1;
+ i >= kMinimumPrefixLength; i--) {
+ if (result[i])
+ hyphen_locations.push_back(i + num_leading_spaces);
+ }
+ return hyphen_locations;
+}
+
+using LocaleMap = HashMap<AtomicString, AtomicString, CaseFoldingHash>;
+
+static LocaleMap CreateLocaleFallbackMap() {
+ // This data is from CLDR, compiled by AOSP.
+ // https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/text/Hyphenator.java
+ using LocaleFallback = const char * [2];
+ static LocaleFallback locale_fallback_data[] = {
+ {"en-AS", "en-us"}, // English (American Samoa)
+ {"en-GU", "en-us"}, // English (Guam)
+ {"en-MH", "en-us"}, // English (Marshall Islands)
+ {"en-MP", "en-us"}, // English (Northern Mariana Islands)
+ {"en-PR", "en-us"}, // English (Puerto Rico)
+ {"en-UM", "en-us"}, // English (United States Minor Outlying Islands)
+ {"en-VI", "en-us"}, // English (Virgin Islands)
+ // All English locales other than those falling back to en-US are mapped
+ // to en-GB.
+ {"en", "en-gb"},
+ // For German, we're assuming the 1996 (and later) orthography by default.
+ {"de", "de-1996"},
+ // Liechtenstein uses the Swiss hyphenation rules for the 1901
+ // orthography.
+ {"de-LI-1901", "de-ch-1901"},
+ // Norwegian is very probably Norwegian Bokmål.
+ {"no", "nb"},
+ {"mn", "mn-cyrl"}, // Mongolian
+ {"am", "und-ethi"}, // Amharic
+ {"byn", "und-ethi"}, // Blin
+ {"gez", "und-ethi"}, // Geʻez
+ {"ti", "und-ethi"}, // Tigrinya
+ {"wal", "und-ethi"}, // Wolaytta
+ };
+ LocaleMap map;
+ for (const auto& it : locale_fallback_data)
+ map.insert(it[0], it[1]);
+ return map;
+}
+
+scoped_refptr<Hyphenation> Hyphenation::PlatformGetHyphenation(
+ const AtomicString& locale) {
+ scoped_refptr<HyphenationMinikin> hyphenation(
+ base::AdoptRef(new HyphenationMinikin));
+ if (hyphenation->OpenDictionary(locale.LowerASCII()))
+ return hyphenation;
+ hyphenation = nullptr;
+
+ DEFINE_STATIC_LOCAL(LocaleMap, locale_fallback, (CreateLocaleFallbackMap()));
+ const auto& it = locale_fallback.find(locale);
+ if (it != locale_fallback.end())
+ return LayoutLocale::Get(it->value)->GetHyphenation();
+
+ return nullptr;
+}
+
+scoped_refptr<HyphenationMinikin> HyphenationMinikin::FromFileForTesting(
+ base::File file) {
+ scoped_refptr<HyphenationMinikin> hyphenation(
+ base::AdoptRef(new HyphenationMinikin));
+ if (hyphenation->OpenDictionary(std::move(file)))
+ return hyphenation;
+ return nullptr;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/hyphenation/hyphenation_minikin.h b/chromium/third_party/blink/renderer/platform/text/hyphenation/hyphenation_minikin.h
new file mode 100644
index 00000000000..e3c4f6fd913
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/hyphenation/hyphenation_minikin.h
@@ -0,0 +1,44 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_HYPHENATION_HYPHENATION_MINIKIN_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_HYPHENATION_HYPHENATION_MINIKIN_H_
+
+#include "third_party/blink/renderer/platform/text/hyphenation.h"
+
+#include "base/files/memory_mapped_file.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace base {
+class File;
+} // namespace base
+
+namespace android {
+class Hyphenator;
+} // namespace andorid
+
+namespace blink {
+
+class PLATFORM_EXPORT HyphenationMinikin final : public Hyphenation {
+ public:
+ bool OpenDictionary(const AtomicString& locale);
+
+ size_t LastHyphenLocation(const StringView& text,
+ size_t before_index) const override;
+ Vector<size_t, 8> HyphenLocations(const StringView&) const override;
+
+ static scoped_refptr<HyphenationMinikin> FromFileForTesting(base::File);
+
+ private:
+ bool OpenDictionary(base::File);
+
+ std::vector<uint8_t> Hyphenate(const StringView&) const;
+
+ base::MemoryMappedFile file_;
+ std::unique_ptr<android::Hyphenator> hyphenator_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_HYPHENATION_HYPHENATION_MINIKIN_H_
diff --git a/chromium/third_party/blink/renderer/platform/text/hyphenation/hyphenator_aosp.cc b/chromium/third_party/blink/renderer/platform/text/hyphenation/hyphenator_aosp.cc
new file mode 100644
index 00000000000..b830775b8a7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/hyphenation/hyphenator_aosp.cc
@@ -0,0 +1,248 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ *
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <vector>
+#include <memory>
+#include <algorithm>
+#include <unicode/uchar.h>
+
+// HACK: for reading pattern file
+#include <fcntl.h>
+
+#include "third_party/blink/renderer/platform/text/hyphenation/hyphenator_aosp.h"
+
+using std::vector;
+
+namespace android {
+
+static const uint16_t CHAR_SOFT_HYPHEN = 0x00AD;
+
+// The following are structs that correspond to tables inside the hyb file
+// format
+
+struct AlphabetTable0 {
+ uint32_t version;
+ uint32_t min_codepoint;
+ uint32_t max_codepoint;
+ uint8_t data[1]; // actually flexible array, size is known at runtime
+};
+
+struct AlphabetTable1 {
+ uint32_t version;
+ uint32_t n_entries;
+ uint32_t data[1]; // actually flexible array, size is known at runtime
+
+ static uint32_t codepoint(uint32_t entry) { return entry >> 11; }
+ static uint32_t value(uint32_t entry) { return entry & 0x7ff; }
+};
+
+struct Trie {
+ uint32_t version;
+ uint32_t char_mask;
+ uint32_t link_shift;
+ uint32_t link_mask;
+ uint32_t pattern_shift;
+ uint32_t n_entries;
+ uint32_t data[1]; // actually flexible array, size is known at runtime
+};
+
+struct Pattern {
+ uint32_t version;
+ uint32_t n_entries;
+ uint32_t pattern_offset;
+ uint32_t pattern_size;
+ uint32_t data[1]; // actually flexible array, size is known at runtime
+
+ // accessors
+ static uint32_t len(uint32_t entry) { return entry >> 26; }
+ static uint32_t shift(uint32_t entry) { return (entry >> 20) & 0x3f; }
+ const uint8_t* buf(uint32_t entry) const {
+ return reinterpret_cast<const uint8_t*>(this) + pattern_offset +
+ (entry & 0xfffff);
+ }
+};
+
+struct Header {
+ uint32_t magic;
+ uint32_t version;
+ uint32_t alphabet_offset;
+ uint32_t trie_offset;
+ uint32_t pattern_offset;
+ uint32_t file_size;
+
+ // accessors
+ const uint8_t* bytes() const {
+ return reinterpret_cast<const uint8_t*>(this);
+ }
+ uint32_t alphabetVersion() const {
+ return *reinterpret_cast<const uint32_t*>(bytes() + alphabet_offset);
+ }
+ const AlphabetTable0* alphabetTable0() const {
+ return reinterpret_cast<const AlphabetTable0*>(bytes() + alphabet_offset);
+ }
+ const AlphabetTable1* alphabetTable1() const {
+ return reinterpret_cast<const AlphabetTable1*>(bytes() + alphabet_offset);
+ }
+ const Trie* trieTable() const {
+ return reinterpret_cast<const Trie*>(bytes() + trie_offset);
+ }
+ const Pattern* patternTable() const {
+ return reinterpret_cast<const Pattern*>(bytes() + pattern_offset);
+ }
+};
+
+Hyphenator* Hyphenator::loadBinary(const uint8_t* patternData) {
+ Hyphenator* result = new Hyphenator;
+ result->patternData = patternData;
+ return result;
+}
+
+void Hyphenator::hyphenate(vector<uint8_t>* result,
+ const uint16_t* word,
+ size_t len) {
+ result->clear();
+ result->resize(len);
+ const size_t paddedLen = len + 2; // start and stop code each count for 1
+ if (patternData != nullptr && (int)len >= MIN_PREFIX + MIN_SUFFIX &&
+ paddedLen <= MAX_HYPHENATED_SIZE) {
+ uint16_t alpha_codes[MAX_HYPHENATED_SIZE];
+ if (alphabetLookup(alpha_codes, word, len)) {
+ hyphenateFromCodes(result->data(), alpha_codes, paddedLen);
+ return;
+ }
+ // TODO: try NFC normalization
+ // TODO: handle non-BMP Unicode (requires remapping of offsets)
+ }
+ hyphenateSoft(result->data(), word, len);
+}
+
+// If any soft hyphen is present in the word, use soft hyphens to decide
+// hyphenation, as recommended in UAX #14 (Use of Soft Hyphen)
+void Hyphenator::hyphenateSoft(uint8_t* result,
+ const uint16_t* word,
+ size_t len) {
+ result[0] = 0;
+ for (size_t i = 1; i < len; i++) {
+ result[i] = word[i - 1] == CHAR_SOFT_HYPHEN;
+ }
+}
+
+bool Hyphenator::alphabetLookup(uint16_t* alpha_codes,
+ const uint16_t* word,
+ size_t len) {
+ const Header* header = getHeader();
+ // TODO: check header magic
+ uint32_t alphabetVersion = header->alphabetVersion();
+ if (alphabetVersion == 0) {
+ const AlphabetTable0* alphabet = header->alphabetTable0();
+ uint32_t min_codepoint = alphabet->min_codepoint;
+ uint32_t max_codepoint = alphabet->max_codepoint;
+ alpha_codes[0] = 0; // word start
+ for (size_t i = 0; i < len; i++) {
+ uint16_t c = word[i];
+ if (c < min_codepoint || c >= max_codepoint) {
+ return false;
+ }
+ uint8_t code = alphabet->data[c - min_codepoint];
+ if (code == 0) {
+ return false;
+ }
+ alpha_codes[i + 1] = code;
+ }
+ alpha_codes[len + 1] = 0; // word termination
+ return true;
+ } else if (alphabetVersion == 1) {
+ const AlphabetTable1* alphabet = header->alphabetTable1();
+ size_t n_entries = alphabet->n_entries;
+ const uint32_t* begin = alphabet->data;
+ const uint32_t* end = begin + n_entries;
+ alpha_codes[0] = 0;
+ for (size_t i = 0; i < len; i++) {
+ uint16_t c = word[i];
+ auto p = std::lower_bound(begin, end, c << 11);
+ if (p == end) {
+ return false;
+ }
+ uint32_t entry = *p;
+ if (AlphabetTable1::codepoint(entry) != c) {
+ return false;
+ }
+ alpha_codes[i + 1] = AlphabetTable1::value(entry);
+ }
+ alpha_codes[len + 1] = 0;
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Internal implementation, after conversion to codes. All case folding and
+ * normalization has been done by now, and all characters have been found in the
+ * alphabet. Note: len here is the padded length including 0 codes at start and
+ * end.
+ **/
+void Hyphenator::hyphenateFromCodes(uint8_t* result,
+ const uint16_t* codes,
+ size_t len) {
+ const Header* header = getHeader();
+ const Trie* trie = header->trieTable();
+ const Pattern* pattern = header->patternTable();
+ uint32_t char_mask = trie->char_mask;
+ uint32_t link_shift = trie->link_shift;
+ uint32_t link_mask = trie->link_mask;
+ uint32_t pattern_shift = trie->pattern_shift;
+ size_t maxOffset = len - MIN_SUFFIX - 1;
+ for (size_t i = 0; i < len - 1; i++) {
+ uint32_t node = 0; // index into Trie table
+ for (size_t j = i; j < len; j++) {
+ uint16_t c = codes[j];
+ uint32_t entry = trie->data[node + c];
+ if ((entry & char_mask) == c) {
+ node = (entry & link_mask) >> link_shift;
+ } else {
+ break;
+ }
+ uint32_t pat_ix = trie->data[node] >> pattern_shift;
+ // pat_ix contains a 3-tuple of length, shift (number of trailing zeros),
+ // and an offset into the buf pool. This is the pattern for the substring
+ // (i..j) we just matched,
+ // which we combine (via point-wise max) into the result vector.
+ if (pat_ix != 0) {
+ uint32_t pat_entry = pattern->data[pat_ix];
+ int pat_len = Pattern::len(pat_entry);
+ int pat_shift = Pattern::shift(pat_entry);
+ const uint8_t* pat_buf = pattern->buf(pat_entry);
+ int offset = j + 1 - (pat_len + pat_shift);
+ // offset is the index within result that lines up with the start of
+ // pat_buf
+ int start = std::max(MIN_PREFIX - offset, 0);
+ int end = std::min(pat_len, (int)maxOffset - offset);
+ for (int k = start; k < end; k++) {
+ result[offset + k] = std::max(result[offset + k], pat_buf[k]);
+ }
+ }
+ }
+ }
+ // Since the above calculation does not modify values outside
+ // [MIN_PREFIX, len - MIN_SUFFIX], they are left as 0.
+ for (size_t i = MIN_PREFIX; i < maxOffset; i++) {
+ result[i] &= 1;
+ }
+}
+
+} // namespace android
diff --git a/chromium/third_party/blink/renderer/platform/text/hyphenation/hyphenator_aosp.h b/chromium/third_party/blink/renderer/platform/text/hyphenation/hyphenator_aosp.h
new file mode 100644
index 00000000000..7ae5e475904
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/hyphenation/hyphenator_aosp.h
@@ -0,0 +1,90 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ *
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_HYPHENATION_HYPHENATOR_AOSP_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_HYPHENATION_HYPHENATOR_AOSP_H_
+
+/**
+ * An implementation of Liang's hyphenation algorithm.
+ */
+
+#include <memory>
+#include <unordered_map>
+
+namespace android {
+
+// hyb file header; implementation details are in the .cpp file
+struct Header;
+
+class Hyphenator {
+ public:
+ // Note: this will also require a locale, for proper case folding behavior
+ static Hyphenator* load(const uint16_t* patternData, size_t size);
+
+ // Compute the hyphenation of a word, storing the hyphenation in result
+ // vector. Each entry in the vector is a "hyphen edit" to be applied at the
+ // corresponding code unit offset in the word. Currently 0 means no hyphen and
+ // 1 means insert hyphen and break, but this will be expanded to other edits
+ // for nonstandard hyphenation. Example: word is "hyphen", result is [0 0 1 0
+ // 0 0], corresponding to "hy-phen".
+ void hyphenate(std::vector<uint8_t>* result,
+ const uint16_t* word,
+ size_t len);
+
+ // pattern data is in binary format, as described in doc/hyb_file_format.md.
+ // Note: the caller is responsible for ensuring that the lifetime of the
+ // pattern data is at least as long as the Hyphenator object.
+
+ // Note: nullptr is valid input, in which case the hyphenator only processes
+ // soft hyphens
+ static Hyphenator* loadBinary(const uint8_t* patternData);
+
+ private:
+ // apply soft hyphens only, ignoring patterns
+ void hyphenateSoft(uint8_t* result, const uint16_t* word, size_t len);
+
+ // Try looking up word in alphabet table, return false if any code units fail
+ // to map. Note that this methor writes len+2 entries into alpha_codes
+ // (including start and stop).
+ bool alphabetLookup(uint16_t* alpha_codes, const uint16_t* word, size_t len);
+
+ // calculate hyphenation from patterns, assuming alphabet lookup has already
+ // been done
+ void hyphenateFromCodes(uint8_t* result, const uint16_t* codes, size_t len);
+
+ // TODO: these should become parameters, as they might vary by locale, screen
+ // size, and possibly explicit user control.
+ static const int MIN_PREFIX = 2;
+ static const int MIN_SUFFIX = 3;
+
+ // See also LONGEST_HYPHENATED_WORD in LineBreaker.cpp. Here the constant is
+ // used so that temporary buffers can be stack-allocated without waste, which
+ // is a slightly different use case. It measures UTF-16 code units.
+ static const size_t MAX_HYPHENATED_SIZE = 64;
+
+ const uint8_t* patternData;
+
+ // accessors for binary data
+ const Header* getHeader() const {
+ return reinterpret_cast<const Header*>(patternData);
+ }
+};
+
+} // namespace android
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_HYPHENATION_HYPHENATOR_AOSP_H_
diff --git a/chromium/third_party/blink/renderer/platform/text/hyphenation_test.cc b/chromium/third_party/blink/renderer/platform/text/hyphenation_test.cc
new file mode 100644
index 00000000000..7b827e84324
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/hyphenation_test.cc
@@ -0,0 +1,176 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/text/hyphenation.h"
+
+#include "build/build_config.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/fonts/font_global_context.h"
+#include "third_party/blink/renderer/platform/layout_locale.h"
+
+using testing::ElementsAre;
+using testing::ElementsAreArray;
+
+#if defined(OS_ANDROID)
+#define USE_MINIKIN_HYPHENATION
+#endif
+#if defined(USE_MINIKIN_HYPHENATION)
+#include "base/files/file_path.h"
+#include "third_party/blink/renderer/platform/text/hyphenation/hyphenation_minikin.h"
+#endif
+
+namespace blink {
+
+class NoHyphenation : public Hyphenation {
+ public:
+ size_t LastHyphenLocation(const StringView&,
+ size_t before_index) const override {
+ return 0;
+ }
+};
+
+class HyphenationTest : public testing::Test {
+ protected:
+ void TearDown() override { FontGlobalContext::ClearForTesting(); }
+
+#if defined(USE_MINIKIN_HYPHENATION) || defined(OS_MACOSX)
+ // Get a |Hyphenation| instnace for the specified locale for testing.
+ scoped_refptr<Hyphenation> GetHyphenation(const AtomicString& locale) {
+#if defined(USE_MINIKIN_HYPHENATION)
+ // Because the mojo service to open hyphenation dictionaries is not
+ // accessible from the unit test, open the dictionary file directly for
+ // testing.
+ std::string filename =
+ std::string("hyph-") + locale.Ascii().data() + ".hyb";
+#if defined(OS_ANDROID)
+ base::FilePath path("/system/usr/hyphen-data");
+#else
+#error "This configuration is not supported."
+#endif
+ path = path.AppendASCII(filename);
+ base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
+ if (file.IsValid())
+ return HyphenationMinikin::FromFileForTesting(std::move(file));
+#else
+ if (const LayoutLocale* layout_locale = LayoutLocale::Get(locale))
+ return layout_locale->GetHyphenation();
+#endif
+ return nullptr;
+ }
+#endif
+};
+
+TEST_F(HyphenationTest, Get) {
+ scoped_refptr<Hyphenation> hyphenation = base::AdoptRef(new NoHyphenation);
+ LayoutLocale::SetHyphenationForTesting("en-US", hyphenation);
+ EXPECT_EQ(hyphenation.get(), LayoutLocale::Get("en-US")->GetHyphenation());
+
+ LayoutLocale::SetHyphenationForTesting("en-UK", nullptr);
+ EXPECT_EQ(nullptr, LayoutLocale::Get("en-UK")->GetHyphenation());
+}
+
+#if defined(USE_MINIKIN_HYPHENATION) || defined(OS_MACOSX)
+TEST_F(HyphenationTest, HyphenLocations) {
+ scoped_refptr<Hyphenation> hyphenation = GetHyphenation("en-us");
+ ASSERT_TRUE(hyphenation) << "Cannot find the hyphenation engine";
+
+ // Get all hyphenation points by |HyphenLocations|.
+ const String word("hyphenation");
+ Vector<size_t, 8> locations = hyphenation->HyphenLocations(word);
+ EXPECT_GT(locations.size(), 0u);
+
+ for (unsigned i = 1; i < locations.size(); i++) {
+ ASSERT_GT(locations[i - 1], locations[i])
+ << "hyphenLocations must return locations in the descending order";
+ }
+
+ // Test |LastHyphenLocation| returns all hyphenation points.
+ Vector<size_t, 8> actual;
+ for (unsigned offset = word.length();;) {
+ offset = hyphenation->LastHyphenLocation(word, offset);
+ if (!offset)
+ break;
+ actual.push_back(offset);
+ }
+ EXPECT_THAT(actual, ElementsAreArray(locations));
+
+ // Test |FirstHyphenLocation| returns all hyphenation points.
+ actual.clear();
+ for (unsigned offset = 0;;) {
+ offset = hyphenation->FirstHyphenLocation(word, offset);
+ if (!offset)
+ break;
+ actual.push_back(offset);
+ }
+ locations.Reverse();
+ EXPECT_THAT(actual, ElementsAreArray(locations));
+}
+
+TEST_F(HyphenationTest, LeadingSpaces) {
+ scoped_refptr<Hyphenation> hyphenation = GetHyphenation("en-us");
+#if defined(OS_ANDROID)
+ // Hyphenation is available only for Android M MR1 or later.
+ if (!hyphenation)
+ return;
+#endif
+ ASSERT_TRUE(hyphenation) << "Cannot find the hyphenation for en-us";
+
+ String leading_space(" principle");
+ EXPECT_THAT(hyphenation->HyphenLocations(leading_space), ElementsAre(7, 5));
+ EXPECT_EQ(5u, hyphenation->LastHyphenLocation(leading_space, 6));
+
+ String multi_leading_spaces(" principle");
+ EXPECT_THAT(hyphenation->HyphenLocations(multi_leading_spaces),
+ ElementsAre(9, 7));
+ EXPECT_EQ(7u, hyphenation->LastHyphenLocation(multi_leading_spaces, 8));
+
+ // Line breaker is not supposed to pass only spaces, no locations.
+ String only_spaces(" ");
+ EXPECT_THAT(hyphenation->HyphenLocations(only_spaces), ElementsAre());
+ EXPECT_EQ(0u, hyphenation->LastHyphenLocation(only_spaces, 3));
+
+ // Line breaker is not supposed to pass with trailing spaces.
+ String trailing_space("principle ");
+ EXPECT_THAT(hyphenation->HyphenLocations(trailing_space),
+ testing::AnyOf(ElementsAre(), ElementsAre(6, 4)));
+ EXPECT_EQ(0u, hyphenation->LastHyphenLocation(trailing_space, 10));
+}
+
+TEST_F(HyphenationTest, English) {
+ scoped_refptr<Hyphenation> hyphenation = GetHyphenation("en-us");
+#if defined(OS_ANDROID)
+ // Hyphenation is available only for Android M MR1 or later.
+ if (!hyphenation)
+ return;
+#endif
+ ASSERT_TRUE(hyphenation) << "Cannot find the hyphenation for en-us";
+
+ Vector<size_t, 8> locations = hyphenation->HyphenLocations("hyphenation");
+ EXPECT_THAT(locations, testing::AnyOf(ElementsAreArray({6, 2}),
+ ElementsAreArray({7, 6, 2})));
+}
+
+TEST_F(HyphenationTest, German) {
+ scoped_refptr<Hyphenation> hyphenation = GetHyphenation("de-1996");
+#if defined(OS_ANDROID)
+ // Hyphenation is available only for Android M MR1 or later.
+ if (!hyphenation)
+ return;
+#endif
+ ASSERT_TRUE(hyphenation) << "Cannot find the hyphenation for de-1996";
+
+ Vector<size_t, 8> locations = hyphenation->HyphenLocations("konsonantien");
+ EXPECT_THAT(locations, ElementsAreArray({8, 5, 3}));
+
+ // Test words with non-ASCII (> U+0080) characters.
+ locations = hyphenation->HyphenLocations(
+ "B"
+ "\xE4" // LATIN SMALL LETTER A WITH DIAERESIS
+ "chlein");
+ EXPECT_THAT(locations, ElementsAreArray({4}));
+}
+#endif
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/icu_error.cc b/chromium/third_party/blink/renderer/platform/text/icu_error.cc
new file mode 100644
index 00000000000..d2e9cce4687
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/icu_error.cc
@@ -0,0 +1,30 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/text/icu_error.h"
+
+#include "base/allocator/partition_allocator/oom.h"
+
+namespace blink {
+
+// Distinguish memory allocation failures from other errors.
+// https://groups.google.com/a/chromium.org/d/msg/platform-architecture-dev/MP0k9WGnCjA/zIBiJtilBwAJ
+static NEVER_INLINE void ICUOutOfMemory() {
+ OOM_CRASH();
+}
+
+void ICUError::HandleFailure() {
+ switch (error_) {
+ case U_MEMORY_ALLOCATION_ERROR:
+ ICUOutOfMemory();
+ break;
+ case U_ILLEGAL_ARGUMENT_ERROR:
+ CHECK(false) << error_;
+ break;
+ default:
+ break;
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/icu_error.h b/chromium/third_party/blink/renderer/platform/text/icu_error.h
new file mode 100644
index 00000000000..a57b27ec33c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/icu_error.h
@@ -0,0 +1,44 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_ICU_ERROR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_ICU_ERROR_H_
+
+#include <unicode/utypes.h>
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+namespace blink {
+
+// ICUError provides the unified way to handle ICU errors in Blink.
+class PLATFORM_EXPORT ICUError {
+ STACK_ALLOCATED();
+
+ public:
+ ~ICUError() { CrashIfCritical(); }
+
+ UErrorCode* operator&() { return &error_; }
+ operator UErrorCode() const { return error_; }
+ operator UErrorCode&() { return error_; }
+
+ void operator=(UErrorCode error) { error_ = error; }
+
+ // Crash the renderer in the appropriate way if critical failure occurred.
+ void CrashIfCritical();
+
+ private:
+ UErrorCode error_ = U_ZERO_ERROR;
+
+ void HandleFailure();
+};
+
+inline void ICUError::CrashIfCritical() {
+ if (U_FAILURE(error_))
+ HandleFailure();
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_ICU_ERROR_H_
diff --git a/chromium/third_party/blink/renderer/platform/text/icu_error_test.cc b/chromium/third_party/blink/renderer/platform/text/icu_error_test.cc
new file mode 100644
index 00000000000..8fccb16c5ed
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/icu_error_test.cc
@@ -0,0 +1,24 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/text/icu_error.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+const UErrorCode kTestErrorCode = U_INVALID_FORMAT_ERROR;
+
+void CauseICUError(UErrorCode& err) {
+ err = kTestErrorCode;
+}
+
+TEST(ICUErrorTest, assignToAutomaticReference) {
+ ICUError icu_error;
+ EXPECT_EQ(icu_error, U_ZERO_ERROR);
+ CauseICUError(icu_error);
+ EXPECT_EQ(icu_error, kTestErrorCode);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/line_ending.cc b/chromium/third_party/blink/renderer/platform/text/line_ending.cc
new file mode 100644
index 00000000000..242c81d6f6b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/line_ending.cc
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2005, 2006, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2010 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 "third_party/blink/renderer/platform/text/line_ending.h"
+
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+namespace {
+
+class OutputBuffer {
+ STACK_ALLOCATED();
+ DISALLOW_COPY_AND_ASSIGN(OutputBuffer);
+
+ public:
+ OutputBuffer() = default;
+ virtual char* Allocate(size_t) = 0;
+ virtual void Copy(const CString&) = 0;
+ virtual ~OutputBuffer() = default;
+};
+
+class CStringBuffer final : public OutputBuffer {
+ public:
+ CStringBuffer(CString& buffer) : buffer_(buffer) {}
+ ~CStringBuffer() override = default;
+
+ char* Allocate(size_t size) override {
+ char* ptr;
+ buffer_ = CString::CreateUninitialized(size, ptr);
+ return ptr;
+ }
+
+ void Copy(const CString& source) override { buffer_ = source; }
+
+ const CString& Buffer() const { return buffer_; }
+
+ private:
+ CString buffer_;
+};
+
+class VectorCharAppendBuffer final : public OutputBuffer {
+ public:
+ VectorCharAppendBuffer(Vector<char>& buffer) : buffer_(buffer) {}
+ ~VectorCharAppendBuffer() override = default;
+
+ char* Allocate(size_t size) override {
+ size_t old_size = buffer_.size();
+ buffer_.Grow(old_size + size);
+ return buffer_.data() + old_size;
+ }
+
+ void Copy(const CString& source) override {
+ buffer_.Append(source.data(), source.length());
+ }
+
+ private:
+ Vector<char>& buffer_;
+};
+
+void InternalNormalizeLineEndingsToCRLF(const CString& from,
+ OutputBuffer& buffer) {
+ // Compute the new length.
+ size_t new_len = 0;
+ const char* p = from.data();
+ while (p < from.data() + from.length()) {
+ char c = *p++;
+ if (c == '\r') {
+ // Safe to look ahead because of trailing '\0'.
+ if (*p != '\n') {
+ // Turn CR into CRLF.
+ new_len += 2;
+ }
+ } else if (c == '\n') {
+ // Turn LF into CRLF.
+ new_len += 2;
+ } else {
+ // Leave other characters alone.
+ new_len += 1;
+ }
+ }
+ if (new_len < from.length())
+ return;
+
+ if (new_len == from.length()) {
+ buffer.Copy(from);
+ return;
+ }
+
+ p = from.data();
+ char* q = buffer.Allocate(new_len);
+
+ // Make a copy of the string.
+ while (p < from.data() + from.length()) {
+ char c = *p++;
+ if (c == '\r') {
+ // Safe to look ahead because of trailing '\0'.
+ if (*p != '\n') {
+ // Turn CR into CRLF.
+ *q++ = '\r';
+ *q++ = '\n';
+ }
+ } else if (c == '\n') {
+ // Turn LF into CRLF.
+ *q++ = '\r';
+ *q++ = '\n';
+ } else {
+ // Leave other characters alone.
+ *q++ = c;
+ }
+ }
+}
+
+} // namespace
+
+void NormalizeLineEndingsToLF(const CString& from, Vector<char>& result) {
+ // Compute the new length.
+ size_t new_len = 0;
+ bool need_fix = false;
+ const char* p = from.data();
+ char from_ending_char = '\r';
+ char to_ending_char = '\n';
+ while (p < from.data() + from.length()) {
+ char c = *p++;
+ if (c == '\r' && *p == '\n') {
+ // Turn CRLF into CR or LF.
+ p++;
+ need_fix = true;
+ } else if (c == from_ending_char) {
+ // Turn CR/LF into LF/CR.
+ need_fix = true;
+ }
+ new_len += 1;
+ }
+
+ // Grow the result buffer.
+ p = from.data();
+ size_t old_result_size = result.size();
+ result.Grow(old_result_size + new_len);
+ char* q = result.data() + old_result_size;
+
+ // If no need to fix the string, just copy the string over.
+ if (!need_fix) {
+ memcpy(q, p, from.length());
+ return;
+ }
+
+ // Make a copy of the string.
+ while (p < from.data() + from.length()) {
+ char c = *p++;
+ if (c == '\r' && *p == '\n') {
+ // Turn CRLF or CR into CR or LF.
+ p++;
+ *q++ = to_ending_char;
+ } else if (c == from_ending_char) {
+ // Turn CR/LF into LF/CR.
+ *q++ = to_ending_char;
+ } else {
+ // Leave other characters alone.
+ *q++ = c;
+ }
+ }
+}
+
+CString NormalizeLineEndingsToCRLF(const CString& from) {
+ if (!from.length())
+ return from;
+ CString result;
+ CStringBuffer buffer(result);
+ InternalNormalizeLineEndingsToCRLF(from, buffer);
+ return buffer.Buffer();
+}
+
+void NormalizeLineEndingsToNative(const CString& from, Vector<char>& result) {
+#if defined(OS_WIN)
+ VectorCharAppendBuffer buffer(result);
+ InternalNormalizeLineEndingsToCRLF(from, buffer);
+#else
+ NormalizeLineEndingsToLF(from, result);
+#endif
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/line_ending.h b/chromium/third_party/blink/renderer/platform/text/line_ending.h
new file mode 100644
index 00000000000..6dc43e228da
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/line_ending.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2005, 2006, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2010 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_LINE_ENDING_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_LINE_ENDING_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+// Normalize all line-endings in the given string to CRLF.
+PLATFORM_EXPORT CString NormalizeLineEndingsToCRLF(const CString& from);
+
+// Normalize all line-endings in the given string to LF and append the result to
+// the given buffer.
+PLATFORM_EXPORT void NormalizeLineEndingsToLF(const CString& from,
+ Vector<char>& result);
+
+// Normalize all line-endings in the given string to the native line-endings and
+// append the result to the given buffer.
+// (Normalize to CRLF on Windows and normalize to LF on all other platforms.)
+PLATFORM_EXPORT void NormalizeLineEndingsToNative(const CString& from,
+ Vector<char>& result);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_LINE_ENDING_H_
diff --git a/chromium/third_party/blink/renderer/platform/text/linux/hyphenation_linux.cc b/chromium/third_party/blink/renderer/platform/text/linux/hyphenation_linux.cc
new file mode 100644
index 00000000000..6e21b36d8b0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/linux/hyphenation_linux.cc
@@ -0,0 +1,14 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/text/hyphenation.h"
+
+namespace blink {
+
+scoped_refptr<Hyphenation> Hyphenation::PlatformGetHyphenation(
+ const AtomicString&) {
+ return nullptr;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/locale_icu.cc b/chromium/third_party/blink/renderer/platform/text/locale_icu.cc
new file mode 100644
index 00000000000..1337a365ec0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/locale_icu.cc
@@ -0,0 +1,464 @@
+/*
+ * Copyright (C) 2011,2012 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 "third_party/blink/renderer/platform/text/locale_icu.h"
+
+#include <unicode/udatpg.h>
+#include <unicode/udisplaycontext.h>
+#include <unicode/uloc.h>
+#include <limits>
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/platform/wtf/date_math.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_buffer.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+
+namespace blink {
+
+std::unique_ptr<Locale> Locale::Create(const String& locale) {
+ return LocaleICU::Create(locale.Utf8().data());
+}
+
+LocaleICU::LocaleICU(const char* locale)
+ : locale_(locale),
+ number_format_(nullptr),
+ short_date_format_(nullptr),
+ did_create_decimal_format_(false),
+ did_create_short_date_format_(false),
+ first_day_of_week_(0),
+ medium_time_format_(nullptr),
+ short_time_format_(nullptr),
+ did_create_time_format_(false) {}
+
+LocaleICU::~LocaleICU() {
+ unum_close(number_format_);
+ udat_close(short_date_format_);
+ udat_close(medium_time_format_);
+ udat_close(short_time_format_);
+}
+
+std::unique_ptr<LocaleICU> LocaleICU::Create(const char* locale_string) {
+ return base::WrapUnique(new LocaleICU(locale_string));
+}
+
+String LocaleICU::DecimalSymbol(UNumberFormatSymbol symbol) {
+ UErrorCode status = U_ZERO_ERROR;
+ int32_t buffer_length =
+ unum_getSymbol(number_format_, symbol, nullptr, 0, &status);
+ DCHECK(U_SUCCESS(status) || status == U_BUFFER_OVERFLOW_ERROR);
+ if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR)
+ return String();
+ StringBuffer<UChar> buffer(buffer_length);
+ status = U_ZERO_ERROR;
+ unum_getSymbol(number_format_, symbol, buffer.Characters(), buffer_length,
+ &status);
+ if (U_FAILURE(status))
+ return String();
+ return String::Adopt(buffer);
+}
+
+String LocaleICU::DecimalTextAttribute(UNumberFormatTextAttribute tag) {
+ UErrorCode status = U_ZERO_ERROR;
+ int32_t buffer_length =
+ unum_getTextAttribute(number_format_, tag, nullptr, 0, &status);
+ DCHECK(U_SUCCESS(status) || status == U_BUFFER_OVERFLOW_ERROR);
+ if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR)
+ return String();
+ StringBuffer<UChar> buffer(buffer_length);
+ status = U_ZERO_ERROR;
+ unum_getTextAttribute(number_format_, tag, buffer.Characters(), buffer_length,
+ &status);
+ DCHECK(U_SUCCESS(status));
+ if (U_FAILURE(status))
+ return String();
+ return String::Adopt(buffer);
+}
+
+void LocaleICU::InitializeLocaleData() {
+ if (did_create_decimal_format_)
+ return;
+ did_create_decimal_format_ = true;
+ UErrorCode status = U_ZERO_ERROR;
+ number_format_ =
+ unum_open(UNUM_DECIMAL, nullptr, 0, locale_.data(), nullptr, &status);
+ if (!U_SUCCESS(status))
+ return;
+
+ Vector<String, kDecimalSymbolsSize> symbols;
+ symbols.push_back(DecimalSymbol(UNUM_ZERO_DIGIT_SYMBOL));
+ symbols.push_back(DecimalSymbol(UNUM_ONE_DIGIT_SYMBOL));
+ symbols.push_back(DecimalSymbol(UNUM_TWO_DIGIT_SYMBOL));
+ symbols.push_back(DecimalSymbol(UNUM_THREE_DIGIT_SYMBOL));
+ symbols.push_back(DecimalSymbol(UNUM_FOUR_DIGIT_SYMBOL));
+ symbols.push_back(DecimalSymbol(UNUM_FIVE_DIGIT_SYMBOL));
+ symbols.push_back(DecimalSymbol(UNUM_SIX_DIGIT_SYMBOL));
+ symbols.push_back(DecimalSymbol(UNUM_SEVEN_DIGIT_SYMBOL));
+ symbols.push_back(DecimalSymbol(UNUM_EIGHT_DIGIT_SYMBOL));
+ symbols.push_back(DecimalSymbol(UNUM_NINE_DIGIT_SYMBOL));
+ symbols.push_back(DecimalSymbol(UNUM_DECIMAL_SEPARATOR_SYMBOL));
+ symbols.push_back(DecimalSymbol(UNUM_GROUPING_SEPARATOR_SYMBOL));
+ DCHECK_EQ(symbols.size(), kDecimalSymbolsSize);
+ SetLocaleData(symbols, DecimalTextAttribute(UNUM_POSITIVE_PREFIX),
+ DecimalTextAttribute(UNUM_POSITIVE_SUFFIX),
+ DecimalTextAttribute(UNUM_NEGATIVE_PREFIX),
+ DecimalTextAttribute(UNUM_NEGATIVE_SUFFIX));
+}
+
+bool LocaleICU::InitializeShortDateFormat() {
+ if (did_create_short_date_format_)
+ return short_date_format_;
+ short_date_format_ = OpenDateFormat(UDAT_NONE, UDAT_SHORT);
+ did_create_short_date_format_ = true;
+ return short_date_format_;
+}
+
+UDateFormat* LocaleICU::OpenDateFormat(UDateFormatStyle time_style,
+ UDateFormatStyle date_style) const {
+ const UChar kGmtTimezone[3] = {'G', 'M', 'T'};
+ UErrorCode status = U_ZERO_ERROR;
+ return udat_open(time_style, date_style, locale_.data(), kGmtTimezone,
+ WTF_ARRAY_LENGTH(kGmtTimezone), nullptr, -1, &status);
+}
+
+// We cannot use udat_*Symbols API to get standalone month names to use in
+// calendar headers for Russian and potentially other languages. Instead,
+// we have to format dates with patterns "LLLL" or "LLL" and set the
+// display context to 'standalone'. See
+// http://bugs.icu-project.org/trac/ticket/11552
+UDateFormat* LocaleICU::OpenDateFormatForStandAloneMonthLabels(
+ bool is_short) const {
+ const UChar kMonthPattern[4] = {'L', 'L', 'L', 'L'};
+ UErrorCode status = U_ZERO_ERROR;
+ UDateFormat* formatter =
+ udat_open(UDAT_PATTERN, UDAT_PATTERN, locale_.data(), nullptr, -1,
+ kMonthPattern, is_short ? 3 : 4, &status);
+ udat_setContext(formatter, UDISPCTX_CAPITALIZATION_FOR_STANDALONE, &status);
+ DCHECK(U_SUCCESS(status));
+ return formatter;
+}
+
+static String GetDateFormatPattern(const UDateFormat* date_format) {
+ if (!date_format)
+ return g_empty_string;
+
+ UErrorCode status = U_ZERO_ERROR;
+ int32_t length = udat_toPattern(date_format, TRUE, nullptr, 0, &status);
+ if (status != U_BUFFER_OVERFLOW_ERROR || !length)
+ return g_empty_string;
+ StringBuffer<UChar> buffer(length);
+ status = U_ZERO_ERROR;
+ udat_toPattern(date_format, TRUE, buffer.Characters(), length, &status);
+ if (U_FAILURE(status))
+ return g_empty_string;
+ return String::Adopt(buffer);
+}
+
+std::unique_ptr<Vector<String>> LocaleICU::CreateLabelVector(
+ const UDateFormat* date_format,
+ UDateFormatSymbolType type,
+ int32_t start_index,
+ int32_t size) {
+ if (!date_format)
+ return std::unique_ptr<Vector<String>>();
+ if (udat_countSymbols(date_format, type) != start_index + size)
+ return std::unique_ptr<Vector<String>>();
+
+ std::unique_ptr<Vector<String>> labels = std::make_unique<Vector<String>>();
+ labels->ReserveCapacity(size);
+ bool is_stand_alone_month = (type == UDAT_STANDALONE_MONTHS) ||
+ (type == UDAT_STANDALONE_SHORT_MONTHS);
+ for (int32_t i = 0; i < size; ++i) {
+ UErrorCode status = U_ZERO_ERROR;
+ int32_t length;
+ static const UDate kEpoch = U_MILLIS_PER_DAY * 15u; // 1970-01-15
+ static const UDate kMonth = U_MILLIS_PER_DAY * 30u; // 30 days in ms
+ if (is_stand_alone_month) {
+ length = udat_format(date_format, kEpoch + i * kMonth, nullptr, 0,
+ nullptr, &status);
+ } else {
+ length = udat_getSymbols(date_format, type, start_index + i, nullptr, 0,
+ &status);
+ }
+ if (status != U_BUFFER_OVERFLOW_ERROR)
+ return std::unique_ptr<Vector<String>>();
+ StringBuffer<UChar> buffer(length);
+ status = U_ZERO_ERROR;
+ if (is_stand_alone_month) {
+ udat_format(date_format, kEpoch + i * kMonth, buffer.Characters(), length,
+ nullptr, &status);
+ } else {
+ udat_getSymbols(date_format, type, start_index + i, buffer.Characters(),
+ length, &status);
+ }
+ if (U_FAILURE(status))
+ return std::unique_ptr<Vector<String>>();
+ labels->push_back(String::Adopt(buffer));
+ }
+ return labels;
+}
+
+static std::unique_ptr<Vector<String>> CreateFallbackWeekDayShortLabels() {
+ std::unique_ptr<Vector<String>> labels = std::make_unique<Vector<String>>();
+ labels->ReserveCapacity(7);
+ labels->push_back("Sun");
+ labels->push_back("Mon");
+ labels->push_back("Tue");
+ labels->push_back("Wed");
+ labels->push_back("Thu");
+ labels->push_back("Fri");
+ labels->push_back("Sat");
+ return labels;
+}
+
+void LocaleICU::InitializeCalendar() {
+ if (week_day_short_labels_)
+ return;
+
+ if (!InitializeShortDateFormat()) {
+ first_day_of_week_ = 0;
+ week_day_short_labels_ = CreateFallbackWeekDayShortLabels();
+ return;
+ }
+ first_day_of_week_ = ucal_getAttribute(udat_getCalendar(short_date_format_),
+ UCAL_FIRST_DAY_OF_WEEK) -
+ UCAL_SUNDAY;
+
+ week_day_short_labels_ = CreateLabelVector(
+ short_date_format_, UDAT_SHORT_WEEKDAYS, UCAL_SUNDAY, 7);
+ if (!week_day_short_labels_)
+ week_day_short_labels_ = CreateFallbackWeekDayShortLabels();
+}
+
+static std::unique_ptr<Vector<String>> CreateFallbackMonthLabels() {
+ std::unique_ptr<Vector<String>> labels = std::make_unique<Vector<String>>();
+ labels->ReserveCapacity(WTF_ARRAY_LENGTH(WTF::kMonthFullName));
+ for (unsigned i = 0; i < WTF_ARRAY_LENGTH(WTF::kMonthFullName); ++i)
+ labels->push_back(WTF::kMonthFullName[i]);
+ return labels;
+}
+
+const Vector<String>& LocaleICU::MonthLabels() {
+ if (month_labels_)
+ return *month_labels_;
+ if (InitializeShortDateFormat()) {
+ month_labels_ =
+ CreateLabelVector(short_date_format_, UDAT_MONTHS, UCAL_JANUARY, 12);
+ if (month_labels_)
+ return *month_labels_;
+ }
+ month_labels_ = CreateFallbackMonthLabels();
+ return *month_labels_;
+}
+
+const Vector<String>& LocaleICU::WeekDayShortLabels() {
+ InitializeCalendar();
+ return *week_day_short_labels_;
+}
+
+unsigned LocaleICU::FirstDayOfWeek() {
+ InitializeCalendar();
+ return first_day_of_week_;
+}
+
+bool LocaleICU::IsRTL() {
+ UErrorCode status = U_ZERO_ERROR;
+ return uloc_getCharacterOrientation(locale_.data(), &status) ==
+ ULOC_LAYOUT_RTL;
+}
+
+static std::unique_ptr<Vector<String>> CreateFallbackAMPMLabels() {
+ std::unique_ptr<Vector<String>> labels = std::make_unique<Vector<String>>();
+ labels->ReserveCapacity(2);
+ labels->push_back("AM");
+ labels->push_back("PM");
+ return labels;
+}
+
+void LocaleICU::InitializeDateTimeFormat() {
+ if (did_create_time_format_)
+ return;
+
+ // We assume ICU medium time pattern and short time pattern are compatible
+ // with LDML, because ICU specific pattern character "V" doesn't appear
+ // in both medium and short time pattern.
+ medium_time_format_ = OpenDateFormat(UDAT_MEDIUM, UDAT_NONE);
+ time_format_with_seconds_ = GetDateFormatPattern(medium_time_format_);
+
+ short_time_format_ = OpenDateFormat(UDAT_SHORT, UDAT_NONE);
+ time_format_without_seconds_ = GetDateFormatPattern(short_time_format_);
+
+ UDateFormat* date_time_format_with_seconds =
+ OpenDateFormat(UDAT_MEDIUM, UDAT_SHORT);
+ date_time_format_with_seconds_ =
+ GetDateFormatPattern(date_time_format_with_seconds);
+ udat_close(date_time_format_with_seconds);
+
+ UDateFormat* date_time_format_without_seconds =
+ OpenDateFormat(UDAT_SHORT, UDAT_SHORT);
+ date_time_format_without_seconds_ =
+ GetDateFormatPattern(date_time_format_without_seconds);
+ udat_close(date_time_format_without_seconds);
+
+ std::unique_ptr<Vector<String>> time_ampm_labels =
+ CreateLabelVector(medium_time_format_, UDAT_AM_PMS, UCAL_AM, 2);
+ if (!time_ampm_labels)
+ time_ampm_labels = CreateFallbackAMPMLabels();
+ time_ampm_labels_ = *time_ampm_labels;
+
+ did_create_time_format_ = true;
+}
+
+String LocaleICU::DateFormat() {
+ if (!date_format_.IsNull())
+ return date_format_;
+ if (!InitializeShortDateFormat())
+ return "yyyy-MM-dd";
+ date_format_ = GetDateFormatPattern(short_date_format_);
+ return date_format_;
+}
+
+static String GetFormatForSkeleton(const char* locale, const String& skeleton) {
+ String format = "yyyy-MM";
+ UErrorCode status = U_ZERO_ERROR;
+ UDateTimePatternGenerator* pattern_generator = udatpg_open(locale, &status);
+ if (!pattern_generator)
+ return format;
+ status = U_ZERO_ERROR;
+ Vector<UChar> skeleton_characters;
+ skeleton.AppendTo(skeleton_characters);
+ int32_t length =
+ udatpg_getBestPattern(pattern_generator, skeleton_characters.data(),
+ skeleton_characters.size(), nullptr, 0, &status);
+ if (status == U_BUFFER_OVERFLOW_ERROR && length) {
+ StringBuffer<UChar> buffer(length);
+ status = U_ZERO_ERROR;
+ udatpg_getBestPattern(pattern_generator, skeleton_characters.data(),
+ skeleton_characters.size(), buffer.Characters(),
+ length, &status);
+ if (U_SUCCESS(status))
+ format = String::Adopt(buffer);
+ }
+ udatpg_close(pattern_generator);
+ return format;
+}
+
+String LocaleICU::MonthFormat() {
+ if (!month_format_.IsNull())
+ return month_format_;
+ // Gets a format for "MMMM" because Windows API always provides formats for
+ // "MMMM" in some locales.
+ month_format_ = GetFormatForSkeleton(locale_.data(), "yyyyMMMM");
+ return month_format_;
+}
+
+String LocaleICU::ShortMonthFormat() {
+ if (!short_month_format_.IsNull())
+ return short_month_format_;
+ short_month_format_ = GetFormatForSkeleton(locale_.data(), "yyyyMMM");
+ return short_month_format_;
+}
+
+String LocaleICU::TimeFormat() {
+ InitializeDateTimeFormat();
+ return time_format_with_seconds_;
+}
+
+String LocaleICU::ShortTimeFormat() {
+ InitializeDateTimeFormat();
+ return time_format_without_seconds_;
+}
+
+String LocaleICU::DateTimeFormatWithSeconds() {
+ InitializeDateTimeFormat();
+ return date_time_format_with_seconds_;
+}
+
+String LocaleICU::DateTimeFormatWithoutSeconds() {
+ InitializeDateTimeFormat();
+ return date_time_format_without_seconds_;
+}
+
+const Vector<String>& LocaleICU::ShortMonthLabels() {
+ if (!short_month_labels_.IsEmpty())
+ return short_month_labels_;
+ if (InitializeShortDateFormat()) {
+ if (std::unique_ptr<Vector<String>> labels = CreateLabelVector(
+ short_date_format_, UDAT_SHORT_MONTHS, UCAL_JANUARY, 12)) {
+ short_month_labels_ = *labels;
+ return short_month_labels_;
+ }
+ }
+ short_month_labels_.ReserveCapacity(WTF_ARRAY_LENGTH(WTF::kMonthName));
+ for (unsigned i = 0; i < WTF_ARRAY_LENGTH(WTF::kMonthName); ++i)
+ short_month_labels_.push_back(WTF::kMonthName[i]);
+ return short_month_labels_;
+}
+
+const Vector<String>& LocaleICU::StandAloneMonthLabels() {
+ if (!stand_alone_month_labels_.IsEmpty())
+ return stand_alone_month_labels_;
+ UDateFormat* month_formatter = OpenDateFormatForStandAloneMonthLabels(false);
+ if (month_formatter) {
+ if (std::unique_ptr<Vector<String>> labels = CreateLabelVector(
+ month_formatter, UDAT_STANDALONE_MONTHS, UCAL_JANUARY, 12)) {
+ stand_alone_month_labels_ = *labels;
+ udat_close(month_formatter);
+ return stand_alone_month_labels_;
+ }
+ udat_close(month_formatter);
+ }
+ stand_alone_month_labels_ = MonthLabels();
+ return stand_alone_month_labels_;
+}
+
+const Vector<String>& LocaleICU::ShortStandAloneMonthLabels() {
+ if (!short_stand_alone_month_labels_.IsEmpty())
+ return short_stand_alone_month_labels_;
+ UDateFormat* month_formatter = OpenDateFormatForStandAloneMonthLabels(true);
+ if (month_formatter) {
+ if (std::unique_ptr<Vector<String>> labels = CreateLabelVector(
+ month_formatter, UDAT_STANDALONE_SHORT_MONTHS, UCAL_JANUARY, 12)) {
+ short_stand_alone_month_labels_ = *labels;
+ udat_close(month_formatter);
+ return short_stand_alone_month_labels_;
+ }
+ udat_close(month_formatter);
+ }
+ short_stand_alone_month_labels_ = ShortMonthLabels();
+ return short_stand_alone_month_labels_;
+}
+
+const Vector<String>& LocaleICU::TimeAMPMLabels() {
+ InitializeDateTimeFormat();
+ return time_ampm_labels_;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/locale_icu.h b/chromium/third_party/blink/renderer/platform/text/locale_icu.h
new file mode 100644
index 00000000000..95b4e758f82
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/locale_icu.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2012 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_LOCALE_ICU_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_LOCALE_ICU_H_
+
+#include <unicode/udat.h>
+#include <unicode/unum.h>
+#include <memory>
+#include "third_party/blink/renderer/platform/date_components.h"
+#include "third_party/blink/renderer/platform/text/platform_locale.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+// We should use this class only for LocalizedNumberICU.cpp,
+// LocalizedDateICU.cpp, and LocalizedNumberICUTest.cpp.
+class PLATFORM_EXPORT LocaleICU : public Locale {
+ public:
+ static std::unique_ptr<LocaleICU> Create(const char* locale_string);
+ ~LocaleICU() override;
+
+ const Vector<String>& WeekDayShortLabels() override;
+ unsigned FirstDayOfWeek() override;
+ bool IsRTL() override;
+ String DateFormat() override;
+ String MonthFormat() override;
+ String ShortMonthFormat() override;
+ String TimeFormat() override;
+ String ShortTimeFormat() override;
+ String DateTimeFormatWithSeconds() override;
+ String DateTimeFormatWithoutSeconds() override;
+ const Vector<String>& MonthLabels() override;
+ const Vector<String>& ShortMonthLabels() override;
+ const Vector<String>& StandAloneMonthLabels() override;
+ const Vector<String>& ShortStandAloneMonthLabels() override;
+ const Vector<String>& TimeAMPMLabels() override;
+
+ private:
+ explicit LocaleICU(const char*);
+ String DecimalSymbol(UNumberFormatSymbol);
+ String DecimalTextAttribute(UNumberFormatTextAttribute);
+ void InitializeLocaleData() override;
+
+ bool DetectSignAndGetDigitRange(const String& input,
+ bool& is_negative,
+ unsigned& start_index,
+ unsigned& end_index);
+ unsigned MatchedDecimalSymbolIndex(const String& input, unsigned& position);
+
+ bool InitializeShortDateFormat();
+ UDateFormat* OpenDateFormat(UDateFormatStyle time_style,
+ UDateFormatStyle date_style) const;
+ UDateFormat* OpenDateFormatForStandAloneMonthLabels(bool is_short) const;
+
+ void InitializeCalendar();
+
+ std::unique_ptr<Vector<String>> CreateLabelVector(const UDateFormat*,
+ UDateFormatSymbolType,
+ int32_t start_index,
+ int32_t size);
+ void InitializeDateTimeFormat();
+
+ CString locale_;
+ UNumberFormat* number_format_;
+ UDateFormat* short_date_format_;
+ bool did_create_decimal_format_;
+ bool did_create_short_date_format_;
+
+ std::unique_ptr<Vector<String>> week_day_short_labels_;
+ unsigned first_day_of_week_;
+ std::unique_ptr<Vector<String>> month_labels_;
+ String date_format_;
+ String month_format_;
+ String short_month_format_;
+ String time_format_with_seconds_;
+ String time_format_without_seconds_;
+ String date_time_format_with_seconds_;
+ String date_time_format_without_seconds_;
+ UDateFormat* medium_time_format_;
+ UDateFormat* short_time_format_;
+ Vector<String> short_month_labels_;
+ Vector<String> stand_alone_month_labels_;
+ Vector<String> short_stand_alone_month_labels_;
+ Vector<String> time_ampm_labels_;
+ bool did_create_time_format_;
+};
+
+} // namespace blink
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/text/locale_icu_test.cc b/chromium/third_party/blink/renderer/platform/text/locale_icu_test.cc
new file mode 100644
index 00000000000..4531a341386
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/locale_icu_test.cc
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2012 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 "third_party/blink/renderer/platform/text/locale_icu.h"
+
+#include <unicode/uvernum.h>
+#include <memory>
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+
+namespace blink {
+
+class LocaleICUTest : public testing::Test {
+ public:
+ // Labels class is used for printing results in EXPECT_EQ macro.
+ class Labels {
+ public:
+ Labels(const Vector<String> labels) : labels_(labels) {}
+
+ // FIXME: We should use Vector<T>::operator==() if it works.
+ bool operator==(const Labels& other) const {
+ if (labels_.size() != other.labels_.size())
+ return false;
+ for (unsigned index = 0; index < labels_.size(); ++index) {
+ if (labels_[index] != other.labels_[index])
+ return false;
+ }
+ return true;
+ }
+
+ String ToString() const {
+ StringBuilder builder;
+ builder.Append("labels(");
+ for (unsigned index = 0; index < labels_.size(); ++index) {
+ if (index)
+ builder.Append(", ");
+ builder.Append('"');
+ builder.Append(labels_[index]);
+ builder.Append('"');
+ }
+ builder.Append(')');
+ return builder.ToString();
+ }
+
+ private:
+ Vector<String> labels_;
+ };
+
+ protected:
+ Labels LabelsFromTwoElements(const String& element1, const String& element2) {
+ Vector<String> labels = Vector<String>();
+ labels.push_back(element1);
+ labels.push_back(element2);
+ return Labels(labels);
+ }
+
+ String MonthFormat(const char* locale_string) {
+ std::unique_ptr<LocaleICU> locale = LocaleICU::Create(locale_string);
+ return locale->MonthFormat();
+ }
+
+ String LocalizedDateFormatText(const char* locale_string) {
+ std::unique_ptr<LocaleICU> locale = LocaleICU::Create(locale_string);
+ return locale->TimeFormat();
+ }
+
+ String LocalizedShortDateFormatText(const char* locale_string) {
+ std::unique_ptr<LocaleICU> locale = LocaleICU::Create(locale_string);
+ return locale->ShortTimeFormat();
+ }
+
+ String ShortMonthLabel(const char* locale_string, unsigned index) {
+ std::unique_ptr<LocaleICU> locale = LocaleICU::Create(locale_string);
+ return locale->ShortMonthLabels()[index];
+ }
+
+ String ShortStandAloneMonthLabel(const char* locale_string, unsigned index) {
+ std::unique_ptr<LocaleICU> locale = LocaleICU::Create(locale_string);
+ return locale->ShortStandAloneMonthLabels()[index];
+ }
+
+ String StandAloneMonthLabel(const char* locale_string, unsigned index) {
+ std::unique_ptr<LocaleICU> locale = LocaleICU::Create(locale_string);
+ return locale->StandAloneMonthLabels()[index];
+ }
+
+ Labels TimeAMPMLabels(const char* locale_string) {
+ std::unique_ptr<LocaleICU> locale = LocaleICU::Create(locale_string);
+ return Labels(locale->TimeAMPMLabels());
+ }
+
+ bool IsRTL(const char* locale_string) {
+ std::unique_ptr<LocaleICU> locale = LocaleICU::Create(locale_string);
+ return locale->IsRTL();
+ }
+};
+
+std::ostream& operator<<(std::ostream& os,
+ const LocaleICUTest::Labels& labels) {
+ return os << labels.ToString().Utf8().data();
+}
+
+TEST_F(LocaleICUTest, isRTL) {
+ EXPECT_TRUE(IsRTL("ar-EG"));
+ EXPECT_FALSE(IsRTL("en-us"));
+ EXPECT_FALSE(IsRTL("ja-jp"));
+ EXPECT_FALSE(IsRTL("**invalid**"));
+}
+
+TEST_F(LocaleICUTest, monthFormat) {
+ EXPECT_STREQ("MMMM yyyy", MonthFormat("en_US").Utf8().data());
+ EXPECT_STREQ("MMMM yyyy", MonthFormat("fr").Utf8().data());
+ EXPECT_STREQ("yyyy\xE5\xB9\xB4M\xE6\x9C\x88",
+ MonthFormat("ja").Utf8().data());
+}
+
+TEST_F(LocaleICUTest, localizedDateFormatText) {
+ // Note: EXPECT_EQ(String, String) doesn't print result as string.
+ EXPECT_STREQ("h:mm:ss a", LocalizedDateFormatText("en_US").Utf8().data());
+ EXPECT_STREQ("HH:mm:ss", LocalizedDateFormatText("fr").Utf8().data());
+ EXPECT_STREQ("H:mm:ss", LocalizedDateFormatText("ja").Utf8().data());
+}
+
+TEST_F(LocaleICUTest, localizedShortDateFormatText) {
+ EXPECT_STREQ("h:mm a", LocalizedShortDateFormatText("en_US").Utf8().data());
+ EXPECT_STREQ("HH:mm", LocalizedShortDateFormatText("fr").Utf8().data());
+ EXPECT_STREQ("H:mm", LocalizedShortDateFormatText("ja").Utf8().data());
+}
+
+TEST_F(LocaleICUTest, standAloneMonthLabels) {
+ EXPECT_STREQ("January", StandAloneMonthLabel("en_US", 0).Utf8().data());
+ EXPECT_STREQ("June", StandAloneMonthLabel("en_US", 5).Utf8().data());
+ EXPECT_STREQ("December", StandAloneMonthLabel("en_US", 11).Utf8().data());
+
+#if U_ICU_VERSION_MAJOR_NUM >= 54
+ EXPECT_STREQ("Janvier", StandAloneMonthLabel("fr_FR", 0).Utf8().data());
+ EXPECT_STREQ("Juin", StandAloneMonthLabel("fr_FR", 5).Utf8().data());
+ EXPECT_STREQ(
+ "D\xC3\xA9"
+ "cembre",
+ StandAloneMonthLabel("fr_FR", 11).Utf8().data());
+#else
+ EXPECT_STREQ("janvier", standAloneMonthLabel("fr_FR", 0).utf8().data());
+ EXPECT_STREQ("juin", standAloneMonthLabel("fr_FR", 5).utf8().data());
+ EXPECT_STREQ(
+ "d\xC3\xA9"
+ "cembre",
+ standAloneMonthLabel("fr_FR", 11).utf8().data());
+#endif
+
+ EXPECT_STREQ("1\xE6\x9C\x88", StandAloneMonthLabel("ja_JP", 0).Utf8().data());
+ EXPECT_STREQ("6\xE6\x9C\x88", StandAloneMonthLabel("ja_JP", 5).Utf8().data());
+ EXPECT_STREQ("12\xE6\x9C\x88",
+ StandAloneMonthLabel("ja_JP", 11).Utf8().data());
+
+ EXPECT_STREQ("\xD0\x9C\xD0\xB0\xD1\x80\xD1\x82",
+ StandAloneMonthLabel("ru_RU", 2).Utf8().data());
+ EXPECT_STREQ("\xD0\x9C\xD0\xB0\xD0\xB9",
+ StandAloneMonthLabel("ru_RU", 4).Utf8().data());
+}
+
+TEST_F(LocaleICUTest, shortMonthLabels) {
+ EXPECT_STREQ("Jan", ShortMonthLabel("en_US", 0).Utf8().data());
+ EXPECT_STREQ("Jan", ShortStandAloneMonthLabel("en_US", 0).Utf8().data());
+ EXPECT_STREQ("Dec", ShortMonthLabel("en_US", 11).Utf8().data());
+ EXPECT_STREQ("Dec", ShortStandAloneMonthLabel("en_US", 11).Utf8().data());
+
+#if U_ICU_VERSION_MAJOR_NUM >= 54
+ EXPECT_STREQ("janv.", ShortMonthLabel("fr_FR", 0).Utf8().data());
+ EXPECT_STREQ("Janv.", ShortStandAloneMonthLabel("fr_FR", 0).Utf8().data());
+ EXPECT_STREQ(
+ "d\xC3\xA9"
+ "c.",
+ ShortMonthLabel("fr_FR", 11).Utf8().data());
+ EXPECT_STREQ(
+ "D\xC3\xA9"
+ "c.",
+ ShortStandAloneMonthLabel("fr_FR", 11).Utf8().data());
+#else
+ EXPECT_STREQ("janv.", shortMonthLabel("fr_FR", 0).utf8().data());
+ EXPECT_STREQ("janv.", shortStandAloneMonthLabel("fr_FR", 0).utf8().data());
+ EXPECT_STREQ(
+ "d\xC3\xA9"
+ "c.",
+ shortMonthLabel("fr_FR", 11).utf8().data());
+ EXPECT_STREQ(
+ "d\xC3\xA9"
+ "c.",
+ shortStandAloneMonthLabel("fr_FR", 11).utf8().data());
+#endif
+
+ EXPECT_STREQ("1\xE6\x9C\x88", ShortMonthLabel("ja_JP", 0).Utf8().data());
+ EXPECT_STREQ("1\xE6\x9C\x88",
+ ShortStandAloneMonthLabel("ja_JP", 0).Utf8().data());
+ EXPECT_STREQ("12\xE6\x9C\x88", ShortMonthLabel("ja_JP", 11).Utf8().data());
+ EXPECT_STREQ("12\xE6\x9C\x88",
+ ShortStandAloneMonthLabel("ja_JP", 11).Utf8().data());
+
+ EXPECT_STREQ("\xD0\xBC\xD0\xB0\xD1\x80.",
+ ShortMonthLabel("ru_RU", 2).Utf8().data());
+ EXPECT_STREQ("\xD0\x9C\xD0\xB0\xD1\x80\xD1\x82",
+ ShortStandAloneMonthLabel("ru_RU", 2).Utf8().data());
+ EXPECT_STREQ("\xD0\xBC\xD0\xB0\xD1\x8F",
+ ShortMonthLabel("ru_RU", 4).Utf8().data());
+ EXPECT_STREQ("\xD0\x9C\xD0\xB0\xD0\xB9",
+ ShortStandAloneMonthLabel("ru_RU", 4).Utf8().data());
+}
+
+TEST_F(LocaleICUTest, timeAMPMLabels) {
+ EXPECT_EQ(LabelsFromTwoElements("AM", "PM"), TimeAMPMLabels("en_US"));
+ EXPECT_EQ(LabelsFromTwoElements("AM", "PM"), TimeAMPMLabels("fr"));
+
+ UChar ja_am[3] = {0x5348, 0x524d, 0};
+ UChar ja_pm[3] = {0x5348, 0x5F8C, 0};
+ EXPECT_EQ(LabelsFromTwoElements(String(ja_am), String(ja_pm)),
+ TimeAMPMLabels("ja"));
+}
+
+static String TestDecimalSeparator(const AtomicString& locale_identifier) {
+ std::unique_ptr<Locale> locale = Locale::Create(locale_identifier);
+ return locale->LocalizedDecimalSeparator();
+}
+
+TEST_F(LocaleICUTest, localizedDecimalSeparator) {
+ EXPECT_EQ(String("."), TestDecimalSeparator("en_US"));
+ EXPECT_EQ(String(","), TestDecimalSeparator("fr"));
+}
+
+void TestNumberIsReversible(const AtomicString& locale_identifier,
+ const char* original,
+ const char* should_have = nullptr) {
+ std::unique_ptr<Locale> locale = Locale::Create(locale_identifier);
+ String localized = locale->ConvertToLocalizedNumber(original);
+ if (should_have)
+ EXPECT_TRUE(localized.Contains(should_have));
+ String converted = locale->ConvertFromLocalizedNumber(localized);
+ EXPECT_EQ(original, converted);
+}
+
+void TestNumbers(const char* locale_string) {
+ TestNumberIsReversible(locale_string, "123456789012345678901234567890");
+ TestNumberIsReversible(locale_string, "-123.456");
+ TestNumberIsReversible(locale_string, ".456");
+ TestNumberIsReversible(locale_string, "-0.456");
+}
+
+TEST_F(LocaleICUTest, reversible) {
+ TestNumberIsReversible("en_US", "123456789012345678901234567890");
+ TestNumberIsReversible("en_US", "-123.456", ".");
+ TestNumberIsReversible("en_US", ".456", ".");
+ TestNumberIsReversible("en_US", "-0.456", ".");
+
+ TestNumberIsReversible("fr", "123456789012345678901234567890");
+ TestNumberIsReversible("fr", "-123.456", ",");
+ TestNumberIsReversible("fr", ".456", ",");
+ TestNumberIsReversible("fr", "-0.456", ",");
+
+ // Persian locale has a negative prefix and a negative suffix.
+ TestNumbers("fa");
+
+ // Test some of major locales.
+ TestNumbers("ar");
+ TestNumbers("de_DE");
+ TestNumbers("es_ES");
+ TestNumbers("ja_JP");
+ TestNumbers("ko_KR");
+ TestNumbers("zh_CN");
+ TestNumbers("zh_HK");
+ TestNumbers("zh_TW");
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/locale_mac.h b/chromium/third_party/blink/renderer/platform/text/locale_mac.h
new file mode 100644
index 00000000000..912fdbf9316
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/locale_mac.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2012 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_LOCALE_MAC_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_LOCALE_MAC_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/text/platform_locale.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/retain_ptr.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+OBJC_CLASS NSCalendar;
+OBJC_CLASS NSDateFormatter;
+OBJC_CLASS NSLocale;
+
+namespace blink {
+
+class PLATFORM_EXPORT LocaleMac : public Locale {
+ public:
+ static std::unique_ptr<LocaleMac> Create(const String&);
+ static std::unique_ptr<LocaleMac> Create(NSLocale*);
+ ~LocaleMac();
+
+ const Vector<String>& WeekDayShortLabels() override;
+ unsigned FirstDayOfWeek() override;
+ bool IsRTL() override;
+
+ String DateFormat() override;
+ String MonthFormat() override;
+ String ShortMonthFormat() override;
+ String TimeFormat() override;
+ String ShortTimeFormat() override;
+ String DateTimeFormatWithSeconds() override;
+ String DateTimeFormatWithoutSeconds() override;
+ const Vector<String>& MonthLabels() override;
+ const Vector<String>& ShortMonthLabels() override;
+ const Vector<String>& StandAloneMonthLabels() override;
+ const Vector<String>& ShortStandAloneMonthLabels() override;
+ const Vector<String>& TimeAMPMLabels() override;
+
+ private:
+ explicit LocaleMac(NSLocale*);
+ RetainPtr<NSDateFormatter> ShortDateFormatter();
+ void InitializeLocaleData() override;
+
+ RetainPtr<NSLocale> locale_;
+ RetainPtr<NSCalendar> gregorian_calendar_;
+ Vector<String> week_day_short_labels_;
+ Vector<String> month_labels_;
+ RetainPtr<NSDateFormatter> TimeFormatter();
+ RetainPtr<NSDateFormatter> ShortTimeFormatter();
+ RetainPtr<NSDateFormatter> DateTimeFormatterWithSeconds();
+ RetainPtr<NSDateFormatter> DateTimeFormatterWithoutSeconds();
+
+ String date_format_;
+ String month_format_;
+ String short_month_format_;
+ String time_format_with_seconds_;
+ String time_format_without_seconds_;
+ String date_time_format_with_seconds_;
+ String date_time_format_without_seconds_;
+ Vector<String> short_month_labels_;
+ Vector<String> stand_alone_month_labels_;
+ Vector<String> short_stand_alone_month_labels_;
+ Vector<String> time_ampm_labels_;
+ bool did_initialize_number_data_;
+};
+
+} // namespace blink
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/text/locale_mac.mm b/chromium/third_party/blink/renderer/platform/text/locale_mac.mm
new file mode 100644
index 00000000000..5c9f13272ce
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/locale_mac.mm
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2012 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 "third_party/blink/renderer/platform/text/locale_mac.h"
+
+#import <Foundation/NSDateFormatter.h>
+#import <Foundation/NSLocale.h>
+#include <memory>
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/platform/language.h"
+#include "third_party/blink/renderer/platform/layout_test_support.h"
+#include "third_party/blink/renderer/platform/wtf/date_math.h"
+#include "third_party/blink/renderer/platform/wtf/retain_ptr.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+
+namespace blink {
+
+static inline String LanguageFromLocale(const String& locale) {
+ String normalized_locale = locale;
+ normalized_locale.Replace('-', '_');
+ size_t separator_position = normalized_locale.find('_');
+ if (separator_position == kNotFound)
+ return normalized_locale;
+ return normalized_locale.Left(separator_position);
+}
+
+static RetainPtr<NSLocale> DetermineLocale(const String& locale) {
+ if (!LayoutTestSupport::IsRunningLayoutTest()) {
+ RetainPtr<NSLocale> current_locale = [NSLocale currentLocale];
+ String current_locale_language =
+ LanguageFromLocale(String([current_locale.Get() localeIdentifier]));
+ String locale_language = LanguageFromLocale(locale);
+ if (DeprecatedEqualIgnoringCase(current_locale_language, locale_language))
+ return current_locale;
+ }
+ // It seems initWithLocaleIdentifier accepts dash-separated locale identifier.
+ return RetainPtr<NSLocale>(
+ kAdoptNS, [[NSLocale alloc] initWithLocaleIdentifier:locale]);
+}
+
+std::unique_ptr<Locale> Locale::Create(const String& locale) {
+ return LocaleMac::Create(DetermineLocale(locale).Get());
+}
+
+static RetainPtr<NSDateFormatter> CreateDateTimeFormatter(
+ NSLocale* locale,
+ NSCalendar* calendar,
+ NSDateFormatterStyle date_style,
+ NSDateFormatterStyle time_style) {
+ NSDateFormatter* formatter = [[NSDateFormatter alloc] init];
+ [formatter setLocale:locale];
+ [formatter setDateStyle:date_style];
+ [formatter setTimeStyle:time_style];
+ [formatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];
+ [formatter setCalendar:calendar];
+ return AdoptNS(formatter);
+}
+
+LocaleMac::LocaleMac(NSLocale* locale)
+ : locale_(locale),
+ gregorian_calendar_(
+ kAdoptNS,
+ [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]),
+ did_initialize_number_data_(false) {
+ NSArray* available_languages = [NSLocale ISOLanguageCodes];
+ // NSLocale returns a lower case NSLocaleLanguageCode so we don't have care
+ // about case.
+ NSString* language = [locale_.Get() objectForKey:NSLocaleLanguageCode];
+ if ([available_languages indexOfObject:language] == NSNotFound)
+ locale_.AdoptNS(
+ [[NSLocale alloc] initWithLocaleIdentifier:DefaultLanguage()]);
+ [gregorian_calendar_.Get() setLocale:locale_.Get()];
+}
+
+LocaleMac::~LocaleMac() {}
+
+std::unique_ptr<LocaleMac> LocaleMac::Create(const String& locale_identifier) {
+ RetainPtr<NSLocale> locale =
+ [[NSLocale alloc] initWithLocaleIdentifier:locale_identifier];
+ return base::WrapUnique(new LocaleMac(locale.Get()));
+}
+
+std::unique_ptr<LocaleMac> LocaleMac::Create(NSLocale* locale) {
+ return base::WrapUnique(new LocaleMac(locale));
+}
+
+RetainPtr<NSDateFormatter> LocaleMac::ShortDateFormatter() {
+ return CreateDateTimeFormatter(locale_.Get(), gregorian_calendar_.Get(),
+ NSDateFormatterShortStyle,
+ NSDateFormatterNoStyle);
+}
+
+const Vector<String>& LocaleMac::MonthLabels() {
+ if (!month_labels_.IsEmpty())
+ return month_labels_;
+ month_labels_.ReserveCapacity(12);
+ NSArray* array = [ShortDateFormatter().Get() monthSymbols];
+ if ([array count] == 12) {
+ for (unsigned i = 0; i < 12; ++i)
+ month_labels_.push_back(String([array objectAtIndex:i]));
+ return month_labels_;
+ }
+ for (unsigned i = 0; i < WTF_ARRAY_LENGTH(WTF::kMonthFullName); ++i)
+ month_labels_.push_back(WTF::kMonthFullName[i]);
+ return month_labels_;
+}
+
+const Vector<String>& LocaleMac::WeekDayShortLabels() {
+ if (!week_day_short_labels_.IsEmpty())
+ return week_day_short_labels_;
+ week_day_short_labels_.ReserveCapacity(7);
+ NSArray* array = [ShortDateFormatter().Get() shortWeekdaySymbols];
+ if ([array count] == 7) {
+ for (unsigned i = 0; i < 7; ++i)
+ week_day_short_labels_.push_back(String([array objectAtIndex:i]));
+ return week_day_short_labels_;
+ }
+ for (unsigned i = 0; i < WTF_ARRAY_LENGTH(WTF::kWeekdayName); ++i) {
+ // weekdayName starts with Monday.
+ week_day_short_labels_.push_back(WTF::kWeekdayName[(i + 6) % 7]);
+ }
+ return week_day_short_labels_;
+}
+
+unsigned LocaleMac::FirstDayOfWeek() {
+ // The document for NSCalendar - firstWeekday doesn't have an explanation of
+ // firstWeekday value. We can guess it by the document of NSDateComponents -
+ // weekDay, so it can be 1 through 7 and 1 is Sunday.
+ return [gregorian_calendar_.Get() firstWeekday] - 1;
+}
+
+bool LocaleMac::IsRTL() {
+ return NSLocaleLanguageDirectionRightToLeft ==
+ [NSLocale characterDirectionForLanguage:
+ [NSLocale canonicalLanguageIdentifierFromString:
+ [locale_.Get() localeIdentifier]]];
+}
+
+RetainPtr<NSDateFormatter> LocaleMac::TimeFormatter() {
+ return CreateDateTimeFormatter(locale_.Get(), gregorian_calendar_.Get(),
+ NSDateFormatterNoStyle,
+ NSDateFormatterMediumStyle);
+}
+
+RetainPtr<NSDateFormatter> LocaleMac::ShortTimeFormatter() {
+ return CreateDateTimeFormatter(locale_.Get(), gregorian_calendar_.Get(),
+ NSDateFormatterNoStyle,
+ NSDateFormatterShortStyle);
+}
+
+RetainPtr<NSDateFormatter> LocaleMac::DateTimeFormatterWithSeconds() {
+ return CreateDateTimeFormatter(locale_.Get(), gregorian_calendar_.Get(),
+ NSDateFormatterShortStyle,
+ NSDateFormatterMediumStyle);
+}
+
+RetainPtr<NSDateFormatter> LocaleMac::DateTimeFormatterWithoutSeconds() {
+ return CreateDateTimeFormatter(locale_.Get(), gregorian_calendar_.Get(),
+ NSDateFormatterShortStyle,
+ NSDateFormatterShortStyle);
+}
+
+String LocaleMac::DateFormat() {
+ if (!date_format_.IsNull())
+ return date_format_;
+ date_format_ = [ShortDateFormatter().Get() dateFormat];
+ return date_format_;
+}
+
+String LocaleMac::MonthFormat() {
+ if (!month_format_.IsNull())
+ return month_format_;
+ // Gets a format for "MMMM" because Windows API always provides formats for
+ // "MMMM" in some locales.
+ month_format_ = [NSDateFormatter dateFormatFromTemplate:@"yyyyMMMM"
+ options:0
+ locale:locale_.Get()];
+ return month_format_;
+}
+
+String LocaleMac::ShortMonthFormat() {
+ if (!short_month_format_.IsNull())
+ return short_month_format_;
+ short_month_format_ = [NSDateFormatter dateFormatFromTemplate:@"yyyyMMM"
+ options:0
+ locale:locale_.Get()];
+ return short_month_format_;
+}
+
+String LocaleMac::TimeFormat() {
+ if (!time_format_with_seconds_.IsNull())
+ return time_format_with_seconds_;
+ time_format_with_seconds_ = [TimeFormatter().Get() dateFormat];
+ return time_format_with_seconds_;
+}
+
+String LocaleMac::ShortTimeFormat() {
+ if (!time_format_without_seconds_.IsNull())
+ return time_format_without_seconds_;
+ time_format_without_seconds_ = [ShortTimeFormatter().Get() dateFormat];
+ return time_format_without_seconds_;
+}
+
+String LocaleMac::DateTimeFormatWithSeconds() {
+ if (!date_time_format_with_seconds_.IsNull())
+ return date_time_format_with_seconds_;
+ date_time_format_with_seconds_ =
+ [DateTimeFormatterWithSeconds().Get() dateFormat];
+ return date_time_format_with_seconds_;
+}
+
+String LocaleMac::DateTimeFormatWithoutSeconds() {
+ if (!date_time_format_without_seconds_.IsNull())
+ return date_time_format_without_seconds_;
+ date_time_format_without_seconds_ =
+ [DateTimeFormatterWithoutSeconds().Get() dateFormat];
+ return date_time_format_without_seconds_;
+}
+
+const Vector<String>& LocaleMac::ShortMonthLabels() {
+ if (!short_month_labels_.IsEmpty())
+ return short_month_labels_;
+ short_month_labels_.ReserveCapacity(12);
+ NSArray* array = [ShortDateFormatter().Get() shortMonthSymbols];
+ if ([array count] == 12) {
+ for (unsigned i = 0; i < 12; ++i)
+ short_month_labels_.push_back([array objectAtIndex:i]);
+ return short_month_labels_;
+ }
+ for (unsigned i = 0; i < WTF_ARRAY_LENGTH(WTF::kMonthName); ++i)
+ short_month_labels_.push_back(WTF::kMonthName[i]);
+ return short_month_labels_;
+}
+
+const Vector<String>& LocaleMac::StandAloneMonthLabels() {
+ if (!stand_alone_month_labels_.IsEmpty())
+ return stand_alone_month_labels_;
+ NSArray* array = [ShortDateFormatter().Get() standaloneMonthSymbols];
+ if ([array count] == 12) {
+ stand_alone_month_labels_.ReserveCapacity(12);
+ for (unsigned i = 0; i < 12; ++i)
+ stand_alone_month_labels_.push_back([array objectAtIndex:i]);
+ return stand_alone_month_labels_;
+ }
+ stand_alone_month_labels_ = ShortMonthLabels();
+ return stand_alone_month_labels_;
+}
+
+const Vector<String>& LocaleMac::ShortStandAloneMonthLabels() {
+ if (!short_stand_alone_month_labels_.IsEmpty())
+ return short_stand_alone_month_labels_;
+ NSArray* array = [ShortDateFormatter().Get() shortStandaloneMonthSymbols];
+ if ([array count] == 12) {
+ short_stand_alone_month_labels_.ReserveCapacity(12);
+ for (unsigned i = 0; i < 12; ++i)
+ short_stand_alone_month_labels_.push_back([array objectAtIndex:i]);
+ return short_stand_alone_month_labels_;
+ }
+ short_stand_alone_month_labels_ = ShortMonthLabels();
+ return short_stand_alone_month_labels_;
+}
+
+const Vector<String>& LocaleMac::TimeAMPMLabels() {
+ if (!time_ampm_labels_.IsEmpty())
+ return time_ampm_labels_;
+ time_ampm_labels_.ReserveCapacity(2);
+ RetainPtr<NSDateFormatter> formatter = ShortTimeFormatter();
+ time_ampm_labels_.push_back([formatter.Get() AMSymbol]);
+ time_ampm_labels_.push_back([formatter.Get() PMSymbol]);
+ return time_ampm_labels_;
+}
+
+void LocaleMac::InitializeLocaleData() {
+ if (did_initialize_number_data_)
+ return;
+ did_initialize_number_data_ = true;
+
+ RetainPtr<NSNumberFormatter> formatter(kAdoptNS,
+ [[NSNumberFormatter alloc] init]);
+ [formatter.Get() setLocale:locale_.Get()];
+ [formatter.Get() setNumberStyle:NSNumberFormatterDecimalStyle];
+ [formatter.Get() setUsesGroupingSeparator:NO];
+
+ RetainPtr<NSNumber> sample_number(
+ kAdoptNS, [[NSNumber alloc] initWithDouble:9876543210]);
+ String nine_to_zero([formatter.Get() stringFromNumber:sample_number.Get()]);
+ if (nine_to_zero.length() != 10)
+ return;
+ Vector<String, kDecimalSymbolsSize> symbols;
+ for (unsigned i = 0; i < 10; ++i)
+ symbols.push_back(nine_to_zero.Substring(9 - i, 1));
+ DCHECK(symbols.size() == kDecimalSeparatorIndex);
+ symbols.push_back([formatter.Get() decimalSeparator]);
+ DCHECK(symbols.size() == kGroupSeparatorIndex);
+ symbols.push_back([formatter.Get() groupingSeparator]);
+ DCHECK(symbols.size() == kDecimalSymbolsSize);
+
+ String positive_prefix([formatter.Get() positivePrefix]);
+ String positive_suffix([formatter.Get() positiveSuffix]);
+ String negative_prefix([formatter.Get() negativePrefix]);
+ String negative_suffix([formatter.Get() negativeSuffix]);
+ SetLocaleData(symbols, positive_prefix, positive_suffix, negative_prefix,
+ negative_suffix);
+}
+}
diff --git a/chromium/third_party/blink/renderer/platform/text/locale_mac_test.cc b/chromium/third_party/blink/renderer/platform/text/locale_mac_test.cc
new file mode 100644
index 00000000000..aad937bd696
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/locale_mac_test.cc
@@ -0,0 +1,452 @@
+/*
+ * Copyright (C) 2012 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/text/locale_mac.h"
+
+#include <memory>
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/date_components.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
+#include "third_party/blink/renderer/platform/wtf/date_math.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+
+namespace blink {
+
+class LocalePlatformSupport : public TestingPlatformSupport {
+ public:
+ WebString QueryLocalizedString(WebLocalizedString::Name /*name*/) override {
+ return WebString::FromUTF8("Week $2, $1");
+ }
+};
+
+class LocaleMacTest : public testing::Test {
+ protected:
+ enum {
+ kJanuary = 0,
+ kFebruary,
+ kMarch,
+ kApril,
+ kMay,
+ kJune,
+ kJuly,
+ kAugust,
+ kSeptember,
+ kOctober,
+ kNovember,
+ kDecember,
+ };
+
+ enum {
+ kSunday = 0,
+ kMonday,
+ kTuesday,
+ kWednesday,
+ kThursday,
+ kFriday,
+ kSaturday,
+ };
+
+ DateComponents GetDateComponents(int year, int month, int day) {
+ DateComponents date;
+ date.SetMillisecondsSinceEpochForDate(MsForDate(year, month, day));
+ return date;
+ }
+
+ DateComponents GetTimeComponents(int hour,
+ int minute,
+ int second,
+ int millisecond) {
+ DateComponents date;
+ date.SetMillisecondsSinceMidnight(hour * kMsPerHour +
+ minute * kMsPerMinute +
+ second * kMsPerSecond + millisecond);
+ return date;
+ }
+
+ double MsForDate(int year, int month, int day) {
+ return DateToDaysFrom1970(year, month, day) * kMsPerDay;
+ }
+
+ String FormatWeek(const String& locale_string, const String& iso_string) {
+ std::unique_ptr<LocaleMac> locale = LocaleMac::Create(locale_string);
+ DateComponents date;
+ unsigned end;
+ date.ParseWeek(iso_string, 0, end);
+ return locale->FormatDateTime(date);
+ }
+
+ String FormatMonth(const String& locale_string,
+ const String& iso_string,
+ bool use_short_format) {
+ std::unique_ptr<LocaleMac> locale = LocaleMac::Create(locale_string);
+ DateComponents date;
+ unsigned end;
+ date.ParseMonth(iso_string, 0, end);
+ return locale->FormatDateTime(
+ date, (use_short_format ? Locale::kFormatTypeShort
+ : Locale::kFormatTypeMedium));
+ }
+
+ String FormatDate(const String& locale_string, int year, int month, int day) {
+ std::unique_ptr<LocaleMac> locale = LocaleMac::Create(locale_string);
+ return locale->FormatDateTime(GetDateComponents(year, month, day));
+ }
+
+ String FormatTime(const String& locale_string,
+ int hour,
+ int minute,
+ int second,
+ int millisecond,
+ bool use_short_format) {
+ std::unique_ptr<LocaleMac> locale = LocaleMac::Create(locale_string);
+ return locale->FormatDateTime(
+ GetTimeComponents(hour, minute, second, millisecond),
+ (use_short_format ? Locale::kFormatTypeShort
+ : Locale::kFormatTypeMedium));
+ }
+
+ unsigned FirstDayOfWeek(const String& locale_string) {
+ std::unique_ptr<LocaleMac> locale = LocaleMac::Create(locale_string);
+ return locale->FirstDayOfWeek();
+ }
+
+ String MonthLabel(const String& locale_string, unsigned index) {
+ std::unique_ptr<LocaleMac> locale = LocaleMac::Create(locale_string);
+ return locale->MonthLabels()[index];
+ }
+
+ String WeekDayShortLabel(const String& locale_string, unsigned index) {
+ std::unique_ptr<LocaleMac> locale = LocaleMac::Create(locale_string);
+ return locale->WeekDayShortLabels()[index];
+ }
+
+ bool IsRTL(const String& locale_string) {
+ std::unique_ptr<LocaleMac> locale = LocaleMac::Create(locale_string);
+ return locale->IsRTL();
+ }
+
+ String MonthFormat(const String& locale_string) {
+ std::unique_ptr<LocaleMac> locale = LocaleMac::Create(locale_string);
+ return locale->MonthFormat();
+ }
+
+ String TimeFormat(const String& locale_string) {
+ std::unique_ptr<LocaleMac> locale = LocaleMac::Create(locale_string);
+ return locale->TimeFormat();
+ }
+
+ String ShortTimeFormat(const String& locale_string) {
+ std::unique_ptr<LocaleMac> locale = LocaleMac::Create(locale_string);
+ return locale->ShortTimeFormat();
+ }
+
+ String ShortMonthLabel(const String& locale_string, unsigned index) {
+ std::unique_ptr<LocaleMac> locale = LocaleMac::Create(locale_string);
+ return locale->ShortMonthLabels()[index];
+ }
+
+ String StandAloneMonthLabel(const String& locale_string, unsigned index) {
+ std::unique_ptr<LocaleMac> locale = LocaleMac::Create(locale_string);
+ return locale->StandAloneMonthLabels()[index];
+ }
+
+ String ShortStandAloneMonthLabel(const String& locale_string,
+ unsigned index) {
+ std::unique_ptr<LocaleMac> locale = LocaleMac::Create(locale_string);
+ return locale->ShortStandAloneMonthLabels()[index];
+ }
+
+ String TimeAMPMLabel(const String& locale_string, unsigned index) {
+ std::unique_ptr<LocaleMac> locale = LocaleMac::Create(locale_string);
+ return locale->TimeAMPMLabels()[index];
+ }
+
+ String DecimalSeparator(const String& locale_string) {
+ std::unique_ptr<LocaleMac> locale = LocaleMac::Create(locale_string);
+ return locale->LocalizedDecimalSeparator();
+ }
+};
+
+TEST_F(LocaleMacTest, formatWeek) {
+ ScopedTestingPlatformSupport<LocalePlatformSupport> support;
+ EXPECT_STREQ("Week 04, 2005", FormatWeek("en_US", "2005-W04").Utf8().data());
+ EXPECT_STREQ("Week 52, 2005", FormatWeek("en_US", "2005-W52").Utf8().data());
+}
+
+TEST_F(LocaleMacTest, formatMonth) {
+ EXPECT_STREQ("April 2005",
+ FormatMonth("en_US", "2005-04", false).Utf8().data());
+ EXPECT_STREQ("avril 2005",
+ FormatMonth("fr_FR", "2005-04", false).Utf8().data());
+ EXPECT_STREQ(
+ "2005\xE5\xB9\xB4"
+ "04\xE6\x9C\x88",
+ FormatMonth("ja_JP", "2005-04", false).Utf8().data());
+
+ EXPECT_STREQ("Apr 2005", FormatMonth("en_US", "2005-04", true).Utf8().data());
+ EXPECT_STREQ("avr. 2005",
+ FormatMonth("fr_FR", "2005-04", true).Utf8().data());
+ EXPECT_STREQ(
+ "2005\xE5\xB9\xB4"
+ "04\xE6\x9C\x88",
+ FormatMonth("ja_JP", "2005-04", true).Utf8().data());
+}
+
+TEST_F(LocaleMacTest, formatDate) {
+ EXPECT_STREQ("04/27/2005",
+ FormatDate("en_US", 2005, kApril, 27).Utf8().data());
+ EXPECT_STREQ("27/04/2005",
+ FormatDate("fr_FR", 2005, kApril, 27).Utf8().data());
+ // Do not test ja_JP locale. OS X 10.8 and 10.7 have different formats.
+}
+
+// http://crbug.com/811685 This test is flaky.
+TEST_F(LocaleMacTest, DISABLED_formatTime) {
+ EXPECT_STREQ("1:23 PM",
+ FormatTime("en_US", 13, 23, 00, 000, true).Utf8().data());
+ EXPECT_STREQ("13:23",
+ FormatTime("fr_FR", 13, 23, 00, 000, true).Utf8().data());
+ EXPECT_STREQ("13:23",
+ FormatTime("ja_JP", 13, 23, 00, 000, true).Utf8().data());
+ EXPECT_STREQ("\xD9\xA1:\xD9\xA2\xD9\xA3 \xD9\x85",
+ FormatTime("ar", 13, 23, 00, 000, true).Utf8().data());
+ EXPECT_STREQ("\xDB\xB1\xDB\xB3:\xDB\xB2\xDB\xB3",
+ FormatTime("fa", 13, 23, 00, 000, true).Utf8().data());
+
+ EXPECT_STREQ("12:00 AM",
+ FormatTime("en_US", 00, 00, 00, 000, true).Utf8().data());
+ EXPECT_STREQ("00:00",
+ FormatTime("fr_FR", 00, 00, 00, 000, true).Utf8().data());
+ EXPECT_STREQ("0:00",
+ FormatTime("ja_JP", 00, 00, 00, 000, true).Utf8().data());
+ EXPECT_STREQ("\xD9\xA1\xD9\xA2:\xD9\xA0\xD9\xA0 \xD8\xB5",
+ FormatTime("ar", 00, 00, 00, 000, true).Utf8().data());
+ EXPECT_STREQ("\xDB\xB0:\xDB\xB0\xDB\xB0",
+ FormatTime("fa", 00, 00, 00, 000, true).Utf8().data());
+
+ EXPECT_STREQ("7:07:07.007 AM",
+ FormatTime("en_US", 07, 07, 07, 007, false).Utf8().data());
+ EXPECT_STREQ("07:07:07,007",
+ FormatTime("fr_FR", 07, 07, 07, 007, false).Utf8().data());
+ EXPECT_STREQ("7:07:07.007",
+ FormatTime("ja_JP", 07, 07, 07, 007, false).Utf8().data());
+ EXPECT_STREQ(
+ "\xD9\xA7:\xD9\xA0\xD9\xA7:"
+ "\xD9\xA0\xD9\xA7\xD9\xAB\xD9\xA0\xD9\xA0\xD9\xA7 \xD8\xB5",
+ FormatTime("ar", 07, 07, 07, 007, false).Utf8().data());
+ EXPECT_STREQ(
+ "\xDB\xB7:\xDB\xB0\xDB\xB7:"
+ "\xDB\xB0\xDB\xB7\xD9\xAB\xDB\xB0\xDB\xB0\xDB\xB7",
+ FormatTime("fa", 07, 07, 07, 007, false).Utf8().data());
+}
+
+TEST_F(LocaleMacTest, firstDayOfWeek) {
+ EXPECT_EQ(kSunday, FirstDayOfWeek("en_US"));
+ EXPECT_EQ(kMonday, FirstDayOfWeek("fr_FR"));
+ EXPECT_EQ(kSunday, FirstDayOfWeek("ja_JP"));
+}
+
+TEST_F(LocaleMacTest, monthLabels) {
+ EXPECT_STREQ("January", MonthLabel("en_US", kJanuary).Utf8().data());
+ EXPECT_STREQ("June", MonthLabel("en_US", kJune).Utf8().data());
+ EXPECT_STREQ("December", MonthLabel("en_US", kDecember).Utf8().data());
+
+ EXPECT_STREQ("janvier", MonthLabel("fr_FR", kJanuary).Utf8().data());
+ EXPECT_STREQ("juin", MonthLabel("fr_FR", kJune).Utf8().data());
+ EXPECT_STREQ(
+ "d\xC3\xA9"
+ "cembre",
+ MonthLabel("fr_FR", kDecember).Utf8().data());
+
+ EXPECT_STREQ("1\xE6\x9C\x88", MonthLabel("ja_JP", kJanuary).Utf8().data());
+ EXPECT_STREQ("6\xE6\x9C\x88", MonthLabel("ja_JP", kJune).Utf8().data());
+ EXPECT_STREQ("12\xE6\x9C\x88", MonthLabel("ja_JP", kDecember).Utf8().data());
+}
+
+TEST_F(LocaleMacTest, weekDayShortLabels) {
+ EXPECT_STREQ("Sun", WeekDayShortLabel("en_US", kSunday).Utf8().data());
+ EXPECT_STREQ("Wed", WeekDayShortLabel("en_US", kWednesday).Utf8().data());
+ EXPECT_STREQ("Sat", WeekDayShortLabel("en_US", kSaturday).Utf8().data());
+
+ EXPECT_STREQ("dim.", WeekDayShortLabel("fr_FR", kSunday).Utf8().data());
+ EXPECT_STREQ("mer.", WeekDayShortLabel("fr_FR", kWednesday).Utf8().data());
+ EXPECT_STREQ("sam.", WeekDayShortLabel("fr_FR", kSaturday).Utf8().data());
+
+ EXPECT_STREQ("\xE6\x97\xA5",
+ WeekDayShortLabel("ja_JP", kSunday).Utf8().data());
+ EXPECT_STREQ("\xE6\xB0\xB4",
+ WeekDayShortLabel("ja_JP", kWednesday).Utf8().data());
+ EXPECT_STREQ("\xE5\x9C\x9F",
+ WeekDayShortLabel("ja_JP", kSaturday).Utf8().data());
+}
+
+TEST_F(LocaleMacTest, isRTL) {
+ EXPECT_TRUE(IsRTL("ar-eg"));
+ EXPECT_FALSE(IsRTL("en-us"));
+ EXPECT_FALSE(IsRTL("ja-jp"));
+ EXPECT_FALSE(IsRTL("**invalid**"));
+}
+
+TEST_F(LocaleMacTest, monthFormat) {
+ EXPECT_STREQ("MMMM yyyy", MonthFormat("en_US").Utf8().data());
+ EXPECT_STREQ("yyyy\xE5\xB9\xB4M\xE6\x9C\x88",
+ MonthFormat("ja_JP").Utf8().data());
+
+ // fr_FR and ru return different results on OS versions.
+ // "MMM yyyy" "LLL yyyy" on 10.6 and 10.7
+ // "MMM y" "LLL y" on 10.8
+}
+
+TEST_F(LocaleMacTest, timeFormat) {
+ EXPECT_STREQ("h:mm:ss a", TimeFormat("en_US").Utf8().data());
+ EXPECT_STREQ("HH:mm:ss", TimeFormat("fr_FR").Utf8().data());
+ EXPECT_STREQ("H:mm:ss", TimeFormat("ja_JP").Utf8().data());
+}
+
+TEST_F(LocaleMacTest, shortTimeFormat) {
+ EXPECT_STREQ("h:mm a", ShortTimeFormat("en_US").Utf8().data());
+ EXPECT_STREQ("HH:mm", ShortTimeFormat("fr_FR").Utf8().data());
+ EXPECT_STREQ("H:mm", ShortTimeFormat("ja_JP").Utf8().data());
+}
+
+TEST_F(LocaleMacTest, standAloneMonthLabels) {
+ EXPECT_STREQ("January",
+ StandAloneMonthLabel("en_US", kJanuary).Utf8().data());
+ EXPECT_STREQ("June", StandAloneMonthLabel("en_US", kJune).Utf8().data());
+ EXPECT_STREQ("December",
+ StandAloneMonthLabel("en_US", kDecember).Utf8().data());
+
+ EXPECT_STREQ("janvier",
+ StandAloneMonthLabel("fr_FR", kJanuary).Utf8().data());
+ EXPECT_STREQ("juin", StandAloneMonthLabel("fr_FR", kJune).Utf8().data());
+ EXPECT_STREQ(
+ "d\xC3\xA9"
+ "cembre",
+ StandAloneMonthLabel("fr_FR", kDecember).Utf8().data());
+
+ EXPECT_STREQ("1\xE6\x9C\x88",
+ StandAloneMonthLabel("ja_JP", kJanuary).Utf8().data());
+ EXPECT_STREQ("6\xE6\x9C\x88",
+ StandAloneMonthLabel("ja_JP", kJune).Utf8().data());
+ EXPECT_STREQ("12\xE6\x9C\x88",
+ StandAloneMonthLabel("ja_JP", kDecember).Utf8().data());
+}
+
+TEST_F(LocaleMacTest, shortMonthLabels) {
+ EXPECT_STREQ("Jan", ShortMonthLabel("en_US", 0).Utf8().data());
+ EXPECT_STREQ("Jan", ShortStandAloneMonthLabel("en_US", 0).Utf8().data());
+ EXPECT_STREQ("Dec", ShortMonthLabel("en_US", 11).Utf8().data());
+ EXPECT_STREQ("Dec", ShortStandAloneMonthLabel("en_US", 11).Utf8().data());
+
+ EXPECT_STREQ("janv.", ShortMonthLabel("fr_FR", 0).Utf8().data());
+ EXPECT_STREQ("janv.", ShortStandAloneMonthLabel("fr_FR", 0).Utf8().data());
+ EXPECT_STREQ(
+ "d\xC3\xA9"
+ "c.",
+ ShortMonthLabel("fr_FR", 11).Utf8().data());
+ EXPECT_STREQ(
+ "d\xC3\xA9"
+ "c.",
+ ShortStandAloneMonthLabel("fr_FR", 11).Utf8().data());
+
+ EXPECT_STREQ("1\xE6\x9C\x88", ShortMonthLabel("ja_JP", 0).Utf8().data());
+ EXPECT_STREQ("1\xE6\x9C\x88",
+ ShortStandAloneMonthLabel("ja_JP", 0).Utf8().data());
+ EXPECT_STREQ("12\xE6\x9C\x88", ShortMonthLabel("ja_JP", 11).Utf8().data());
+ EXPECT_STREQ("12\xE6\x9C\x88",
+ ShortStandAloneMonthLabel("ja_JP", 11).Utf8().data());
+
+ EXPECT_STREQ("\xD0\xBC\xD0\xB0\xD1\x80\xD1\x82\xD0\xB0",
+ ShortMonthLabel("ru_RU", 2).Utf8().data());
+ EXPECT_STREQ("\xD0\xBC\xD0\xB0\xD1\x8F",
+ ShortMonthLabel("ru_RU", 4).Utf8().data());
+ // The ru_RU locale returns different stand-alone month labels on OS versions.
+ // "\xD0\xBC\xD0\xB0\xD1\x80\xD1\x82" "\xD0\xBC\xD0\xB0\xD0\xB9" on 10.7
+ // "\xD0\x9C\xD0\xB0\xD1\x80\xD1\x82" "\xD0\x9C\xD0\xB0\xD0\xB9" on 10.8
+}
+
+TEST_F(LocaleMacTest, timeAMPMLabels) {
+ EXPECT_STREQ("AM", TimeAMPMLabel("en_US", 0).Utf8().data());
+ EXPECT_STREQ("PM", TimeAMPMLabel("en_US", 1).Utf8().data());
+
+ EXPECT_STREQ("AM", TimeAMPMLabel("fr_FR", 0).Utf8().data());
+ EXPECT_STREQ("PM", TimeAMPMLabel("fr_FR", 1).Utf8().data());
+
+ EXPECT_STREQ("\xE5\x8D\x88\xE5\x89\x8D",
+ TimeAMPMLabel("ja_JP", 0).Utf8().data());
+ EXPECT_STREQ("\xE5\x8D\x88\xE5\xBE\x8C",
+ TimeAMPMLabel("ja_JP", 1).Utf8().data());
+}
+
+TEST_F(LocaleMacTest, decimalSeparator) {
+ EXPECT_STREQ(".", DecimalSeparator("en_US").Utf8().data());
+ EXPECT_STREQ(",", DecimalSeparator("fr_FR").Utf8().data());
+}
+
+TEST_F(LocaleMacTest, invalidLocale) {
+ EXPECT_STREQ(MonthLabel("en_US", kJanuary).Utf8().data(),
+ MonthLabel("foo", kJanuary).Utf8().data());
+ EXPECT_STREQ(DecimalSeparator("en_US").Utf8().data(),
+ DecimalSeparator("foo").Utf8().data());
+}
+
+static void TestNumberIsReversible(const AtomicString& locale_string,
+ const char* original,
+ const char* should_have = 0) {
+ std::unique_ptr<LocaleMac> locale = LocaleMac::Create(locale_string);
+ String localized = locale->ConvertToLocalizedNumber(original);
+ if (should_have)
+ EXPECT_TRUE(localized.Contains(should_have));
+ String converted = locale->ConvertFromLocalizedNumber(localized);
+ EXPECT_STREQ(original, converted.Utf8().data());
+}
+
+void TestNumbers(const AtomicString& locale_string,
+ const char* decimal_separator_should_be = 0) {
+ TestNumberIsReversible(locale_string, "123456789012345678901234567890");
+ TestNumberIsReversible(locale_string, "-123.456",
+ decimal_separator_should_be);
+ TestNumberIsReversible(locale_string, ".456", decimal_separator_should_be);
+ TestNumberIsReversible(locale_string, "-0.456", decimal_separator_should_be);
+}
+
+TEST_F(LocaleMacTest, localizedNumberRoundTrip) {
+ // Test some of major locales.
+ TestNumbers("en_US", ".");
+ TestNumbers("fr_FR", ",");
+ TestNumbers("ar");
+ TestNumbers("de_DE");
+ TestNumbers("es_ES");
+ TestNumbers("fa");
+ TestNumbers("ja_JP");
+ TestNumbers("ko_KR");
+ TestNumbers("zh_CN");
+ TestNumbers("zh_HK");
+ TestNumbers("zh_TW");
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/locale_to_script_mapping.cc b/chromium/third_party/blink/renderer/platform/text/locale_to_script_mapping.cc
new file mode 100644
index 00000000000..2755ce53f88
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/locale_to_script_mapping.cc
@@ -0,0 +1,506 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/renderer/platform/text/locale_to_script_mapping.h"
+
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+
+namespace blink {
+
+struct SubtagScript {
+ const char* subtag;
+ UScriptCode script;
+};
+
+UScriptCode ScriptNameToCode(const String& script_name) {
+ // This generally maps an ISO 15924 script code to its UScriptCode, but
+ // certain families of script codes are treated as a single script for
+ // assigning a per-script font in Settings. For example, "hira" is mapped to
+ // USCRIPT_KATAKANA_OR_HIRAGANA instead of USCRIPT_HIRAGANA, since we want all
+ // Japanese scripts to be rendered using the same font setting.
+ static constexpr SubtagScript kScriptNameCodeList[] = {
+ {"zyyy", USCRIPT_COMMON},
+ {"qaai", USCRIPT_INHERITED},
+ {"arab", USCRIPT_ARABIC},
+ {"armn", USCRIPT_ARMENIAN},
+ {"beng", USCRIPT_BENGALI},
+ {"bopo", USCRIPT_BOPOMOFO},
+ {"cher", USCRIPT_CHEROKEE},
+ {"copt", USCRIPT_COPTIC},
+ {"cyrl", USCRIPT_CYRILLIC},
+ {"dsrt", USCRIPT_DESERET},
+ {"deva", USCRIPT_DEVANAGARI},
+ {"ethi", USCRIPT_ETHIOPIC},
+ {"geor", USCRIPT_GEORGIAN},
+ {"goth", USCRIPT_GOTHIC},
+ {"grek", USCRIPT_GREEK},
+ {"gujr", USCRIPT_GUJARATI},
+ {"guru", USCRIPT_GURMUKHI},
+ {"hani", USCRIPT_HAN},
+ {"hang", USCRIPT_HANGUL},
+ {"hebr", USCRIPT_HEBREW},
+ {"hira", USCRIPT_KATAKANA_OR_HIRAGANA},
+ {"knda", USCRIPT_KANNADA},
+ {"kana", USCRIPT_KATAKANA_OR_HIRAGANA},
+ {"khmr", USCRIPT_KHMER},
+ {"laoo", USCRIPT_LAO},
+ {"latn", USCRIPT_LATIN},
+ {"mlym", USCRIPT_MALAYALAM},
+ {"mong", USCRIPT_MONGOLIAN},
+ {"mymr", USCRIPT_MYANMAR},
+ {"ogam", USCRIPT_OGHAM},
+ {"ital", USCRIPT_OLD_ITALIC},
+ {"orya", USCRIPT_ORIYA},
+ {"runr", USCRIPT_RUNIC},
+ {"sinh", USCRIPT_SINHALA},
+ {"syrc", USCRIPT_SYRIAC},
+ {"taml", USCRIPT_TAMIL},
+ {"telu", USCRIPT_TELUGU},
+ {"thaa", USCRIPT_THAANA},
+ {"thai", USCRIPT_THAI},
+ {"tibt", USCRIPT_TIBETAN},
+ {"cans", USCRIPT_CANADIAN_ABORIGINAL},
+ {"yiii", USCRIPT_YI},
+ {"tglg", USCRIPT_TAGALOG},
+ {"hano", USCRIPT_HANUNOO},
+ {"buhd", USCRIPT_BUHID},
+ {"tagb", USCRIPT_TAGBANWA},
+ {"brai", USCRIPT_BRAILLE},
+ {"cprt", USCRIPT_CYPRIOT},
+ {"limb", USCRIPT_LIMBU},
+ {"linb", USCRIPT_LINEAR_B},
+ {"osma", USCRIPT_OSMANYA},
+ {"shaw", USCRIPT_SHAVIAN},
+ {"tale", USCRIPT_TAI_LE},
+ {"ugar", USCRIPT_UGARITIC},
+ {"hrkt", USCRIPT_KATAKANA_OR_HIRAGANA},
+ {"bugi", USCRIPT_BUGINESE},
+ {"glag", USCRIPT_GLAGOLITIC},
+ {"khar", USCRIPT_KHAROSHTHI},
+ {"sylo", USCRIPT_SYLOTI_NAGRI},
+ {"talu", USCRIPT_NEW_TAI_LUE},
+ {"tfng", USCRIPT_TIFINAGH},
+ {"xpeo", USCRIPT_OLD_PERSIAN},
+ {"bali", USCRIPT_BALINESE},
+ {"batk", USCRIPT_BATAK},
+ {"blis", USCRIPT_BLISSYMBOLS},
+ {"brah", USCRIPT_BRAHMI},
+ {"cham", USCRIPT_CHAM},
+ {"cirt", USCRIPT_CIRTH},
+ {"cyrs", USCRIPT_OLD_CHURCH_SLAVONIC_CYRILLIC},
+ {"egyd", USCRIPT_DEMOTIC_EGYPTIAN},
+ {"egyh", USCRIPT_HIERATIC_EGYPTIAN},
+ {"egyp", USCRIPT_EGYPTIAN_HIEROGLYPHS},
+ {"geok", USCRIPT_KHUTSURI},
+ {"hans", USCRIPT_SIMPLIFIED_HAN},
+ {"hant", USCRIPT_TRADITIONAL_HAN},
+ {"hmng", USCRIPT_PAHAWH_HMONG},
+ {"hung", USCRIPT_OLD_HUNGARIAN},
+ {"inds", USCRIPT_HARAPPAN_INDUS},
+ {"java", USCRIPT_JAVANESE},
+ {"kali", USCRIPT_KAYAH_LI},
+ {"latf", USCRIPT_LATIN_FRAKTUR},
+ {"latg", USCRIPT_LATIN_GAELIC},
+ {"lepc", USCRIPT_LEPCHA},
+ {"lina", USCRIPT_LINEAR_A},
+ {"mand", USCRIPT_MANDAEAN},
+ {"maya", USCRIPT_MAYAN_HIEROGLYPHS},
+ {"mero", USCRIPT_MEROITIC},
+ {"nkoo", USCRIPT_NKO},
+ {"orkh", USCRIPT_ORKHON},
+ {"perm", USCRIPT_OLD_PERMIC},
+ {"phag", USCRIPT_PHAGS_PA},
+ {"phnx", USCRIPT_PHOENICIAN},
+ {"plrd", USCRIPT_PHONETIC_POLLARD},
+ {"roro", USCRIPT_RONGORONGO},
+ {"sara", USCRIPT_SARATI},
+ {"syre", USCRIPT_ESTRANGELO_SYRIAC},
+ {"syrj", USCRIPT_WESTERN_SYRIAC},
+ {"syrn", USCRIPT_EASTERN_SYRIAC},
+ {"teng", USCRIPT_TENGWAR},
+ {"vaii", USCRIPT_VAI},
+ {"visp", USCRIPT_VISIBLE_SPEECH},
+ {"xsux", USCRIPT_CUNEIFORM},
+ {"jpan", USCRIPT_KATAKANA_OR_HIRAGANA},
+ {"kore", USCRIPT_HANGUL},
+ {"zxxx", USCRIPT_UNWRITTEN_LANGUAGES},
+ {"zzzz", USCRIPT_UNKNOWN}};
+ for (const auto& kv : kScriptNameCodeList) {
+ if (CodePointCompareIgnoringASCIICase(script_name, kv.subtag) == 0)
+ return kv.script;
+ }
+ return USCRIPT_INVALID_CODE;
+}
+
+UScriptCode LocaleToScriptCodeForFontSelection(const String& locale) {
+ static constexpr SubtagScript kLocaleScriptList[] = {
+ {"aa", USCRIPT_LATIN},
+ {"ab", USCRIPT_CYRILLIC},
+ {"ady", USCRIPT_CYRILLIC},
+ {"aeb", USCRIPT_ARABIC},
+ {"af", USCRIPT_LATIN},
+ {"ak", USCRIPT_LATIN},
+ {"am", USCRIPT_ETHIOPIC},
+ {"ar", USCRIPT_ARABIC},
+ {"arq", USCRIPT_ARABIC},
+ {"ary", USCRIPT_ARABIC},
+ {"arz", USCRIPT_ARABIC},
+ {"as", USCRIPT_BENGALI},
+ {"ast", USCRIPT_LATIN},
+ {"av", USCRIPT_CYRILLIC},
+ {"ay", USCRIPT_LATIN},
+ {"az", USCRIPT_LATIN},
+ {"azb", USCRIPT_ARABIC},
+ {"ba", USCRIPT_CYRILLIC},
+ {"bal", USCRIPT_ARABIC},
+ {"be", USCRIPT_CYRILLIC},
+ {"bej", USCRIPT_ARABIC},
+ {"bg", USCRIPT_CYRILLIC},
+ {"bi", USCRIPT_LATIN},
+ {"bn", USCRIPT_BENGALI},
+ {"bo", USCRIPT_TIBETAN},
+ {"bqi", USCRIPT_ARABIC},
+ {"brh", USCRIPT_ARABIC},
+ {"bs", USCRIPT_LATIN},
+ {"ca", USCRIPT_LATIN},
+ {"ce", USCRIPT_CYRILLIC},
+ {"ceb", USCRIPT_LATIN},
+ {"ch", USCRIPT_LATIN},
+ {"chk", USCRIPT_LATIN},
+ {"cja", USCRIPT_ARABIC},
+ {"cjm", USCRIPT_ARABIC},
+ {"ckb", USCRIPT_ARABIC},
+ {"cs", USCRIPT_LATIN},
+ {"cy", USCRIPT_LATIN},
+ {"da", USCRIPT_LATIN},
+ {"dcc", USCRIPT_ARABIC},
+ {"de", USCRIPT_LATIN},
+ {"doi", USCRIPT_ARABIC},
+ {"dv", USCRIPT_THAANA},
+ {"dyo", USCRIPT_ARABIC},
+ {"dz", USCRIPT_TIBETAN},
+ {"ee", USCRIPT_LATIN},
+ {"efi", USCRIPT_LATIN},
+ {"el", USCRIPT_GREEK},
+ {"en", USCRIPT_LATIN},
+ {"es", USCRIPT_LATIN},
+ {"et", USCRIPT_LATIN},
+ {"eu", USCRIPT_LATIN},
+ {"fa", USCRIPT_ARABIC},
+ {"fi", USCRIPT_LATIN},
+ {"fil", USCRIPT_LATIN},
+ {"fj", USCRIPT_LATIN},
+ {"fo", USCRIPT_LATIN},
+ {"fr", USCRIPT_LATIN},
+ {"fur", USCRIPT_LATIN},
+ {"fy", USCRIPT_LATIN},
+ {"ga", USCRIPT_LATIN},
+ {"gaa", USCRIPT_LATIN},
+ {"gba", USCRIPT_ARABIC},
+ {"gbz", USCRIPT_ARABIC},
+ {"gd", USCRIPT_LATIN},
+ {"gil", USCRIPT_LATIN},
+ {"gl", USCRIPT_LATIN},
+ {"gjk", USCRIPT_ARABIC},
+ {"gju", USCRIPT_ARABIC},
+ {"glk", USCRIPT_ARABIC},
+ {"gn", USCRIPT_LATIN},
+ {"gsw", USCRIPT_LATIN},
+ {"gu", USCRIPT_GUJARATI},
+ {"ha", USCRIPT_LATIN},
+ {"haw", USCRIPT_LATIN},
+ {"haz", USCRIPT_ARABIC},
+ {"he", USCRIPT_HEBREW},
+ {"hi", USCRIPT_DEVANAGARI},
+ {"hil", USCRIPT_LATIN},
+ {"hnd", USCRIPT_ARABIC},
+ {"hno", USCRIPT_ARABIC},
+ {"ho", USCRIPT_LATIN},
+ {"hr", USCRIPT_LATIN},
+ {"ht", USCRIPT_LATIN},
+ {"hu", USCRIPT_LATIN},
+ {"hy", USCRIPT_ARMENIAN},
+ {"id", USCRIPT_LATIN},
+ {"ig", USCRIPT_LATIN},
+ {"ii", USCRIPT_YI},
+ {"ilo", USCRIPT_LATIN},
+ {"inh", USCRIPT_CYRILLIC},
+ {"is", USCRIPT_LATIN},
+ {"it", USCRIPT_LATIN},
+ {"iu", USCRIPT_CANADIAN_ABORIGINAL},
+ {"ja", USCRIPT_KATAKANA_OR_HIRAGANA},
+ {"jv", USCRIPT_LATIN},
+ {"ka", USCRIPT_GEORGIAN},
+ {"kaj", USCRIPT_LATIN},
+ {"kam", USCRIPT_LATIN},
+ {"kbd", USCRIPT_CYRILLIC},
+ {"kha", USCRIPT_LATIN},
+ {"khw", USCRIPT_ARABIC},
+ {"kk", USCRIPT_CYRILLIC},
+ {"kl", USCRIPT_LATIN},
+ {"km", USCRIPT_KHMER},
+ {"kn", USCRIPT_KANNADA},
+ {"ko", USCRIPT_HANGUL},
+ {"kok", USCRIPT_DEVANAGARI},
+ {"kos", USCRIPT_LATIN},
+ {"kpe", USCRIPT_LATIN},
+ {"krc", USCRIPT_CYRILLIC},
+ {"ks", USCRIPT_ARABIC},
+ {"ku", USCRIPT_ARABIC},
+ {"kum", USCRIPT_CYRILLIC},
+ {"kvx", USCRIPT_ARABIC},
+ {"kxp", USCRIPT_ARABIC},
+ {"ky", USCRIPT_CYRILLIC},
+ {"la", USCRIPT_LATIN},
+ {"lah", USCRIPT_ARABIC},
+ {"lb", USCRIPT_LATIN},
+ {"lez", USCRIPT_CYRILLIC},
+ {"lki", USCRIPT_ARABIC},
+ {"ln", USCRIPT_LATIN},
+ {"lo", USCRIPT_LAO},
+ {"lrc", USCRIPT_ARABIC},
+ {"lt", USCRIPT_LATIN},
+ {"luz", USCRIPT_ARABIC},
+ {"lv", USCRIPT_LATIN},
+ {"mai", USCRIPT_DEVANAGARI},
+ {"mdf", USCRIPT_CYRILLIC},
+ {"mfa", USCRIPT_ARABIC},
+ {"mg", USCRIPT_LATIN},
+ {"mh", USCRIPT_LATIN},
+ {"mi", USCRIPT_LATIN},
+ {"mk", USCRIPT_CYRILLIC},
+ {"ml", USCRIPT_MALAYALAM},
+ {"mn", USCRIPT_CYRILLIC},
+ {"mr", USCRIPT_DEVANAGARI},
+ {"ms", USCRIPT_LATIN},
+ {"mt", USCRIPT_LATIN},
+ {"mvy", USCRIPT_ARABIC},
+ {"my", USCRIPT_MYANMAR},
+ {"myv", USCRIPT_CYRILLIC},
+ {"mzn", USCRIPT_ARABIC},
+ {"na", USCRIPT_LATIN},
+ {"nb", USCRIPT_LATIN},
+ {"ne", USCRIPT_DEVANAGARI},
+ {"niu", USCRIPT_LATIN},
+ {"nl", USCRIPT_LATIN},
+ {"nn", USCRIPT_LATIN},
+ {"nr", USCRIPT_LATIN},
+ {"nso", USCRIPT_LATIN},
+ {"ny", USCRIPT_LATIN},
+ {"oc", USCRIPT_LATIN},
+ {"om", USCRIPT_LATIN},
+ {"or", USCRIPT_ORIYA},
+ {"os", USCRIPT_CYRILLIC},
+ {"pa", USCRIPT_GURMUKHI},
+ {"pag", USCRIPT_LATIN},
+ {"pap", USCRIPT_LATIN},
+ {"pau", USCRIPT_LATIN},
+ {"pl", USCRIPT_LATIN},
+ {"pon", USCRIPT_LATIN},
+ {"prd", USCRIPT_ARABIC},
+ {"prs", USCRIPT_ARABIC},
+ {"ps", USCRIPT_ARABIC},
+ {"pt", USCRIPT_LATIN},
+ {"qu", USCRIPT_LATIN},
+ {"rm", USCRIPT_LATIN},
+ {"rmt", USCRIPT_ARABIC},
+ {"rn", USCRIPT_LATIN},
+ {"ro", USCRIPT_LATIN},
+ {"ru", USCRIPT_CYRILLIC},
+ {"rw", USCRIPT_LATIN},
+ {"sa", USCRIPT_DEVANAGARI},
+ {"sah", USCRIPT_CYRILLIC},
+ {"sat", USCRIPT_LATIN},
+ {"sd", USCRIPT_ARABIC},
+ {"sdh", USCRIPT_ARABIC},
+ {"se", USCRIPT_LATIN},
+ {"sg", USCRIPT_LATIN},
+ {"shi", USCRIPT_ARABIC},
+ {"si", USCRIPT_SINHALA},
+ {"sid", USCRIPT_LATIN},
+ {"sk", USCRIPT_LATIN},
+ {"skr", USCRIPT_ARABIC},
+ {"sl", USCRIPT_LATIN},
+ {"sm", USCRIPT_LATIN},
+ {"so", USCRIPT_LATIN},
+ {"sq", USCRIPT_LATIN},
+ {"sr", USCRIPT_CYRILLIC},
+ {"ss", USCRIPT_LATIN},
+ {"st", USCRIPT_LATIN},
+ {"su", USCRIPT_LATIN},
+ {"sus", USCRIPT_ARABIC},
+ {"sv", USCRIPT_LATIN},
+ {"sw", USCRIPT_LATIN},
+ {"swb", USCRIPT_ARABIC},
+ {"syr", USCRIPT_ARABIC},
+ {"ta", USCRIPT_TAMIL},
+ {"te", USCRIPT_TELUGU},
+ {"tet", USCRIPT_LATIN},
+ {"tg", USCRIPT_CYRILLIC},
+ {"th", USCRIPT_THAI},
+ {"ti", USCRIPT_ETHIOPIC},
+ {"tig", USCRIPT_ETHIOPIC},
+ {"tk", USCRIPT_LATIN},
+ {"tkl", USCRIPT_LATIN},
+ {"tl", USCRIPT_LATIN},
+ {"tn", USCRIPT_LATIN},
+ {"to", USCRIPT_LATIN},
+ {"tpi", USCRIPT_LATIN},
+ {"tr", USCRIPT_LATIN},
+ {"trv", USCRIPT_LATIN},
+ {"ts", USCRIPT_LATIN},
+ {"tt", USCRIPT_CYRILLIC},
+ {"ttt", USCRIPT_ARABIC},
+ {"tvl", USCRIPT_LATIN},
+ {"tw", USCRIPT_LATIN},
+ {"ty", USCRIPT_LATIN},
+ {"tyv", USCRIPT_CYRILLIC},
+ {"udm", USCRIPT_CYRILLIC},
+ {"ug", USCRIPT_ARABIC},
+ {"uk", USCRIPT_CYRILLIC},
+ {"und", USCRIPT_LATIN},
+ {"ur", USCRIPT_ARABIC},
+ {"uz", USCRIPT_CYRILLIC},
+ {"ve", USCRIPT_LATIN},
+ {"vi", USCRIPT_LATIN},
+ {"wal", USCRIPT_ETHIOPIC},
+ {"war", USCRIPT_LATIN},
+ {"wo", USCRIPT_LATIN},
+ {"xh", USCRIPT_LATIN},
+ {"yap", USCRIPT_LATIN},
+ {"yo", USCRIPT_LATIN},
+ {"za", USCRIPT_LATIN},
+ {"zdj", USCRIPT_ARABIC},
+ {"zh", USCRIPT_SIMPLIFIED_HAN},
+ {"zu", USCRIPT_LATIN},
+ // Encompassed languages within the Chinese macrolanguage.
+ // http://www-01.sil.org/iso639-3/documentation.asp?id=zho
+ // http://lists.w3.org/Archives/Public/public-i18n-cjk/2016JulSep/0022.html
+ {"cdo", USCRIPT_SIMPLIFIED_HAN},
+ {"cjy", USCRIPT_SIMPLIFIED_HAN},
+ {"cmn", USCRIPT_SIMPLIFIED_HAN},
+ {"cpx", USCRIPT_SIMPLIFIED_HAN},
+ {"czh", USCRIPT_SIMPLIFIED_HAN},
+ {"czo", USCRIPT_SIMPLIFIED_HAN},
+ {"gan", USCRIPT_SIMPLIFIED_HAN},
+ {"hsn", USCRIPT_SIMPLIFIED_HAN},
+ {"mnp", USCRIPT_SIMPLIFIED_HAN},
+ {"wuu", USCRIPT_SIMPLIFIED_HAN},
+ {"hak", USCRIPT_TRADITIONAL_HAN},
+ {"lzh", USCRIPT_TRADITIONAL_HAN},
+ {"nan", USCRIPT_TRADITIONAL_HAN},
+ {"yue", USCRIPT_TRADITIONAL_HAN},
+ {"zh-cdo", USCRIPT_SIMPLIFIED_HAN},
+ {"zh-cjy", USCRIPT_SIMPLIFIED_HAN},
+ {"zh-cmn", USCRIPT_SIMPLIFIED_HAN},
+ {"zh-cpx", USCRIPT_SIMPLIFIED_HAN},
+ {"zh-czh", USCRIPT_SIMPLIFIED_HAN},
+ {"zh-czo", USCRIPT_SIMPLIFIED_HAN},
+ {"zh-gan", USCRIPT_SIMPLIFIED_HAN},
+ {"zh-hsn", USCRIPT_SIMPLIFIED_HAN},
+ {"zh-mnp", USCRIPT_SIMPLIFIED_HAN},
+ {"zh-wuu", USCRIPT_SIMPLIFIED_HAN},
+ {"zh-hak", USCRIPT_TRADITIONAL_HAN},
+ {"zh-lzh", USCRIPT_TRADITIONAL_HAN},
+ {"zh-nan", USCRIPT_TRADITIONAL_HAN},
+ {"zh-yue", USCRIPT_TRADITIONAL_HAN},
+ // Chinese with regions. Logically, regions should be handled
+ // separately, but this works for the current purposes.
+ {"zh-hk", USCRIPT_TRADITIONAL_HAN},
+ {"zh-mo", USCRIPT_TRADITIONAL_HAN},
+ {"zh-tw", USCRIPT_TRADITIONAL_HAN},
+ };
+
+ // BCP 47 uses '-' as the delimiter but ICU uses '_'.
+ // https://tools.ietf.org/html/bcp47
+ String canonical_locale = locale;
+ canonical_locale.Replace('_', '-');
+
+ while (!canonical_locale.IsEmpty()) {
+ for (const auto& kv : kLocaleScriptList) {
+ if (CodePointCompareIgnoringASCIICase(canonical_locale, kv.subtag) == 0)
+ return kv.script;
+ }
+
+ size_t pos = canonical_locale.ReverseFind('-');
+ if (pos == kNotFound)
+ break;
+ // script = 4ALPHA
+ if (canonical_locale.length() - (pos + 1) == 4) {
+ UScriptCode code = ScriptNameToCode(canonical_locale.Substring(pos + 1));
+ if (code != USCRIPT_INVALID_CODE && code != USCRIPT_UNKNOWN)
+ return code;
+ }
+ canonical_locale = canonical_locale.Substring(0, pos);
+ }
+ return USCRIPT_COMMON;
+}
+
+static UScriptCode ScriptCodeForHanFromRegion(const String& region) {
+ static constexpr SubtagScript kRegionScriptList[] = {
+ {"hk", USCRIPT_TRADITIONAL_HAN}, {"jp", USCRIPT_KATAKANA_OR_HIRAGANA},
+ {"kr", USCRIPT_HANGUL}, {"mo", USCRIPT_TRADITIONAL_HAN},
+ {"tw", USCRIPT_TRADITIONAL_HAN},
+ };
+ for (const auto& kv : kRegionScriptList) {
+ if (CodePointCompareIgnoringASCIICase(region, kv.subtag) == 0)
+ return kv.script;
+ }
+ return USCRIPT_COMMON;
+}
+
+UScriptCode ScriptCodeForHanFromSubtags(const String& locale, char delimiter) {
+ // Some sites emit lang="en-JP" when English is set as the preferred
+ // language. Use script/region subtags of the content locale to pick the
+ // fallback font for unified Han ideographs.
+ for (size_t end = locale.find(delimiter); end != kNotFound;) {
+ size_t begin = end + 1;
+ end = locale.find(delimiter, begin);
+ size_t len = (end == kNotFound ? locale.length() : end) - begin;
+ UScriptCode script;
+ switch (len) {
+ case 2: // region = 2ALPHA / 3DIGIT
+ script = ScriptCodeForHanFromRegion(locale.Substring(begin, len));
+ if (script != USCRIPT_COMMON)
+ return script;
+ break;
+ case 4: // script = 4ALPHA
+ script = ScriptNameToCode(locale.Substring(begin, len));
+ if (IsUnambiguousHanScript(script))
+ return script;
+ }
+ }
+
+ return USCRIPT_COMMON;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/locale_to_script_mapping.h b/chromium/third_party/blink/renderer/platform/text/locale_to_script_mapping.h
new file mode 100644
index 00000000000..8ba77102119
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/locale_to_script_mapping.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_LOCALE_TO_SCRIPT_MAPPING_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_LOCALE_TO_SCRIPT_MAPPING_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
+
+#include <unicode/uscript.h>
+
+namespace blink {
+
+PLATFORM_EXPORT UScriptCode LocaleToScriptCodeForFontSelection(const String&);
+PLATFORM_EXPORT UScriptCode ScriptNameToCode(const String&);
+
+PLATFORM_EXPORT UScriptCode ScriptCodeForHanFromSubtags(const String&,
+ char delimiter = '-');
+
+inline bool IsUnambiguousHanScript(UScriptCode script) {
+ // localeToScriptCodeForFontSelection() does not return these values.
+ DCHECK(script != USCRIPT_HIRAGANA && script != USCRIPT_KATAKANA);
+ return script == USCRIPT_KATAKANA_OR_HIRAGANA ||
+ script == USCRIPT_SIMPLIFIED_HAN ||
+ script == USCRIPT_TRADITIONAL_HAN || script == USCRIPT_HANGUL;
+}
+}
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/text/locale_win.cc b/chromium/third_party/blink/renderer/platform/text/locale_win.cc
new file mode 100644
index 00000000000..a01610e2071
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/locale_win.cc
@@ -0,0 +1,501 @@
+/*
+ * Copyright (C) 2012 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 "third_party/blink/renderer/platform/text/locale_win.h"
+
+#include <limits>
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/platform/date_components.h"
+#include "third_party/blink/renderer/platform/language.h"
+#include "third_party/blink/renderer/platform/layout_test_support.h"
+#include "third_party/blink/renderer/platform/text/date_time_format.h"
+#include "third_party/blink/renderer/platform/wtf/date_math.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_buffer.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+static String ExtractLanguageCode(const String& locale) {
+ size_t dash_position = locale.find('-');
+ if (dash_position == kNotFound)
+ return locale;
+ return locale.Left(dash_position);
+}
+
+static LCID LCIDFromLocaleInternal(LCID user_default_lcid,
+ const String& user_default_language_code,
+ const String& locale) {
+ String locale_language_code = ExtractLanguageCode(locale);
+ if (DeprecatedEqualIgnoringCase(locale_language_code,
+ user_default_language_code))
+ return user_default_lcid;
+ if (locale.length() >= LOCALE_NAME_MAX_LENGTH)
+ return 0;
+ UChar buffer[LOCALE_NAME_MAX_LENGTH];
+ if (locale.Is8Bit())
+ StringImpl::CopyChars(buffer, locale.Characters8(), locale.length());
+ else
+ StringImpl::CopyChars(buffer, locale.Characters16(), locale.length());
+ buffer[locale.length()] = '\0';
+ return ::LocaleNameToLCID(buffer, 0);
+}
+
+static LCID LCIDFromLocale(const String& locale, bool defaults_for_locale) {
+ // According to MSDN, 9 is enough for LOCALE_SISO639LANGNAME.
+ const size_t kLanguageCodeBufferSize = 9;
+ WCHAR lowercase_language_code[kLanguageCodeBufferSize];
+ ::GetLocaleInfo(LOCALE_USER_DEFAULT,
+ LOCALE_SISO639LANGNAME |
+ (defaults_for_locale ? LOCALE_NOUSEROVERRIDE : 0),
+ lowercase_language_code, kLanguageCodeBufferSize);
+ String user_default_language_code = String(lowercase_language_code);
+
+ LCID lcid = LCIDFromLocaleInternal(LOCALE_USER_DEFAULT,
+ user_default_language_code, locale);
+ if (!lcid)
+ lcid = LCIDFromLocaleInternal(
+ LOCALE_USER_DEFAULT, user_default_language_code, DefaultLanguage());
+ return lcid;
+}
+
+std::unique_ptr<Locale> Locale::Create(const String& locale) {
+ // Whether the default settings for the locale should be used, ignoring user
+ // overrides.
+ bool defaults_for_locale = LayoutTestSupport::IsRunningLayoutTest();
+ return LocaleWin::Create(LCIDFromLocale(locale, defaults_for_locale),
+ defaults_for_locale);
+}
+
+inline LocaleWin::LocaleWin(LCID lcid, bool defaults_for_locale)
+ : lcid_(lcid),
+ did_initialize_number_data_(false),
+ defaults_for_locale_(defaults_for_locale) {
+ DWORD value = 0;
+ GetLocaleInfo(LOCALE_IFIRSTDAYOFWEEK |
+ (defaults_for_locale ? LOCALE_NOUSEROVERRIDE : 0),
+ value);
+ // 0:Monday, ..., 6:Sunday.
+ // We need 1 for Monday, 0 for Sunday.
+ first_day_of_week_ = (value + 1) % 7;
+}
+
+std::unique_ptr<LocaleWin> LocaleWin::Create(LCID lcid,
+ bool defaults_for_locale) {
+ return base::WrapUnique(new LocaleWin(lcid, defaults_for_locale));
+}
+
+LocaleWin::~LocaleWin() {}
+
+String LocaleWin::GetLocaleInfoString(LCTYPE type) {
+ int buffer_size_with_nul = ::GetLocaleInfo(
+ lcid_, type | (defaults_for_locale_ ? LOCALE_NOUSEROVERRIDE : 0), 0, 0);
+ if (buffer_size_with_nul <= 0)
+ return String();
+ StringBuffer<UChar> buffer(buffer_size_with_nul);
+ ::GetLocaleInfo(lcid_,
+ type | (defaults_for_locale_ ? LOCALE_NOUSEROVERRIDE : 0),
+ buffer.Characters(), buffer_size_with_nul);
+ buffer.Shrink(buffer_size_with_nul - 1);
+ return String::Adopt(buffer);
+}
+
+void LocaleWin::GetLocaleInfo(LCTYPE type, DWORD& result) {
+ ::GetLocaleInfo(lcid_, type | LOCALE_RETURN_NUMBER,
+ reinterpret_cast<LPWSTR>(&result),
+ sizeof(DWORD) / sizeof(TCHAR));
+}
+
+void LocaleWin::EnsureShortMonthLabels() {
+ if (!short_month_labels_.IsEmpty())
+ return;
+ const LCTYPE kTypes[12] = {
+ LOCALE_SABBREVMONTHNAME1, LOCALE_SABBREVMONTHNAME2,
+ LOCALE_SABBREVMONTHNAME3, LOCALE_SABBREVMONTHNAME4,
+ LOCALE_SABBREVMONTHNAME5, LOCALE_SABBREVMONTHNAME6,
+ LOCALE_SABBREVMONTHNAME7, LOCALE_SABBREVMONTHNAME8,
+ LOCALE_SABBREVMONTHNAME9, LOCALE_SABBREVMONTHNAME10,
+ LOCALE_SABBREVMONTHNAME11, LOCALE_SABBREVMONTHNAME12,
+ };
+ short_month_labels_.ReserveCapacity(WTF_ARRAY_LENGTH(kTypes));
+ for (unsigned i = 0; i < WTF_ARRAY_LENGTH(kTypes); ++i) {
+ short_month_labels_.push_back(GetLocaleInfoString(kTypes[i]));
+ if (short_month_labels_.back().IsEmpty()) {
+ short_month_labels_.Shrink(0);
+ short_month_labels_.ReserveCapacity(WTF_ARRAY_LENGTH(WTF::kMonthName));
+ for (unsigned m = 0; m < WTF_ARRAY_LENGTH(WTF::kMonthName); ++m)
+ short_month_labels_.push_back(WTF::kMonthName[m]);
+ return;
+ }
+ }
+}
+
+// -------------------------------- Tokenized date format
+
+static unsigned CountContinuousLetters(const String& format, unsigned index) {
+ unsigned count = 1;
+ UChar reference = format[index];
+ while (index + 1 < format.length()) {
+ if (format[++index] != reference)
+ break;
+ ++count;
+ }
+ return count;
+}
+
+static void CommitLiteralToken(StringBuilder& literal_buffer,
+ StringBuilder& converted) {
+ if (literal_buffer.length() <= 0)
+ return;
+ DateTimeFormat::QuoteAndappend(literal_buffer.ToString(), converted);
+ literal_buffer.Clear();
+}
+
+// This function converts Windows date/time pattern format [1][2] into LDML date
+// format pattern [3].
+//
+// i.e.
+// We set h, H, m, s, d, dd, M, or y as is. They have same meaning in both of
+// Windows and LDML.
+// We need to convert the following patterns:
+// t -> a
+// tt -> a
+// ddd -> EEE
+// dddd -> EEEE
+// g -> G
+// gg -> ignore
+//
+// [1] http://msdn.microsoft.com/en-us/library/dd317787(v=vs.85).aspx
+// [2] http://msdn.microsoft.com/en-us/library/dd318148(v=vs.85).aspx
+// [3] LDML http://unicode.org/reports/tr35/tr35-6.html#Date_Format_Patterns
+static String ConvertWindowsDateTimeFormat(const String& format) {
+ StringBuilder converted;
+ StringBuilder literal_buffer;
+ bool in_quote = false;
+ bool last_quote_can_be_literal = false;
+ for (unsigned i = 0; i < format.length(); ++i) {
+ UChar ch = format[i];
+ if (in_quote) {
+ if (ch == '\'') {
+ in_quote = false;
+ DCHECK(i);
+ if (last_quote_can_be_literal && format[i - 1] == '\'') {
+ literal_buffer.Append('\'');
+ last_quote_can_be_literal = false;
+ } else {
+ last_quote_can_be_literal = true;
+ }
+ } else {
+ literal_buffer.Append(ch);
+ }
+ continue;
+ }
+
+ if (ch == '\'') {
+ in_quote = true;
+ if (last_quote_can_be_literal && i > 0 && format[i - 1] == '\'') {
+ literal_buffer.Append(ch);
+ last_quote_can_be_literal = false;
+ } else {
+ last_quote_can_be_literal = true;
+ }
+ } else if (IsASCIIAlpha(ch)) {
+ CommitLiteralToken(literal_buffer, converted);
+ unsigned symbol_start = i;
+ unsigned count = CountContinuousLetters(format, i);
+ i += count - 1;
+ if (ch == 'h' || ch == 'H' || ch == 'm' || ch == 's' || ch == 'M' ||
+ ch == 'y') {
+ converted.Append(format, symbol_start, count);
+ } else if (ch == 'd') {
+ if (count <= 2)
+ converted.Append(format, symbol_start, count);
+ else if (count == 3)
+ converted.Append("EEE");
+ else
+ converted.Append("EEEE");
+ } else if (ch == 'g') {
+ if (count == 1) {
+ converted.Append('G');
+ } else {
+ // gg means imperial era in Windows.
+ // Just ignore it.
+ }
+ } else if (ch == 't') {
+ converted.Append('a');
+ } else {
+ literal_buffer.Append(format, symbol_start, count);
+ }
+ } else {
+ literal_buffer.Append(ch);
+ }
+ }
+ CommitLiteralToken(literal_buffer, converted);
+ return converted.ToString();
+}
+
+void LocaleWin::EnsureMonthLabels() {
+ if (!month_labels_.IsEmpty())
+ return;
+ const LCTYPE kTypes[12] = {
+ LOCALE_SMONTHNAME1, LOCALE_SMONTHNAME2, LOCALE_SMONTHNAME3,
+ LOCALE_SMONTHNAME4, LOCALE_SMONTHNAME5, LOCALE_SMONTHNAME6,
+ LOCALE_SMONTHNAME7, LOCALE_SMONTHNAME8, LOCALE_SMONTHNAME9,
+ LOCALE_SMONTHNAME10, LOCALE_SMONTHNAME11, LOCALE_SMONTHNAME12,
+ };
+ month_labels_.ReserveCapacity(WTF_ARRAY_LENGTH(kTypes));
+ for (unsigned i = 0; i < WTF_ARRAY_LENGTH(kTypes); ++i) {
+ month_labels_.push_back(GetLocaleInfoString(kTypes[i]));
+ if (month_labels_.back().IsEmpty()) {
+ month_labels_.Shrink(0);
+ month_labels_.ReserveCapacity(WTF_ARRAY_LENGTH(WTF::kMonthFullName));
+ for (unsigned m = 0; m < WTF_ARRAY_LENGTH(WTF::kMonthFullName); ++m)
+ month_labels_.push_back(WTF::kMonthFullName[m]);
+ return;
+ }
+ }
+}
+
+void LocaleWin::EnsureWeekDayShortLabels() {
+ if (!week_day_short_labels_.IsEmpty())
+ return;
+ const LCTYPE kTypes[7] = {LOCALE_SABBREVDAYNAME7, // Sunday
+ LOCALE_SABBREVDAYNAME1, // Monday
+ LOCALE_SABBREVDAYNAME2, LOCALE_SABBREVDAYNAME3,
+ LOCALE_SABBREVDAYNAME4, LOCALE_SABBREVDAYNAME5,
+ LOCALE_SABBREVDAYNAME6};
+ week_day_short_labels_.ReserveCapacity(WTF_ARRAY_LENGTH(kTypes));
+ for (unsigned i = 0; i < WTF_ARRAY_LENGTH(kTypes); ++i) {
+ week_day_short_labels_.push_back(GetLocaleInfoString(kTypes[i]));
+ if (week_day_short_labels_.back().IsEmpty()) {
+ week_day_short_labels_.Shrink(0);
+ week_day_short_labels_.ReserveCapacity(
+ WTF_ARRAY_LENGTH(WTF::kWeekdayName));
+ for (unsigned w = 0; w < WTF_ARRAY_LENGTH(WTF::kWeekdayName); ++w) {
+ // weekdayName starts with Monday.
+ week_day_short_labels_.push_back(WTF::kWeekdayName[(w + 6) % 7]);
+ }
+ return;
+ }
+ }
+}
+
+const Vector<String>& LocaleWin::MonthLabels() {
+ EnsureMonthLabels();
+ return month_labels_;
+}
+
+const Vector<String>& LocaleWin::WeekDayShortLabels() {
+ EnsureWeekDayShortLabels();
+ return week_day_short_labels_;
+}
+
+unsigned LocaleWin::FirstDayOfWeek() {
+ return first_day_of_week_;
+}
+
+bool LocaleWin::IsRTL() {
+ WTF::Unicode::CharDirection dir =
+ WTF::Unicode::Direction(MonthLabels()[0][0]);
+ return dir == WTF::Unicode::kRightToLeft ||
+ dir == WTF::Unicode::kRightToLeftArabic;
+}
+
+String LocaleWin::DateFormat() {
+ if (date_format_.IsNull())
+ date_format_ =
+ ConvertWindowsDateTimeFormat(GetLocaleInfoString(LOCALE_SSHORTDATE));
+ return date_format_;
+}
+
+String LocaleWin::DateFormat(const String& windows_format) {
+ return ConvertWindowsDateTimeFormat(windows_format);
+}
+
+String LocaleWin::MonthFormat() {
+ if (month_format_.IsNull())
+ month_format_ =
+ ConvertWindowsDateTimeFormat(GetLocaleInfoString(LOCALE_SYEARMONTH));
+ return month_format_;
+}
+
+String LocaleWin::ShortMonthFormat() {
+ if (short_month_format_.IsNull())
+ short_month_format_ =
+ ConvertWindowsDateTimeFormat(GetLocaleInfoString(LOCALE_SYEARMONTH))
+ .Replace("MMMM", "MMM");
+ return short_month_format_;
+}
+
+String LocaleWin::TimeFormat() {
+ if (time_format_with_seconds_.IsNull())
+ time_format_with_seconds_ =
+ ConvertWindowsDateTimeFormat(GetLocaleInfoString(LOCALE_STIMEFORMAT));
+ return time_format_with_seconds_;
+}
+
+String LocaleWin::ShortTimeFormat() {
+ if (!time_format_without_seconds_.IsNull())
+ return time_format_without_seconds_;
+ String format = GetLocaleInfoString(LOCALE_SSHORTTIME);
+ // Vista or older Windows doesn't support LOCALE_SSHORTTIME.
+ if (format.IsEmpty()) {
+ format = GetLocaleInfoString(LOCALE_STIMEFORMAT);
+ StringBuilder builder;
+ builder.Append(GetLocaleInfoString(LOCALE_STIME));
+ builder.Append("ss");
+ size_t pos = format.ReverseFind(builder.ToString());
+ if (pos != kNotFound)
+ format.Remove(pos, builder.length());
+ }
+ time_format_without_seconds_ = ConvertWindowsDateTimeFormat(format);
+ return time_format_without_seconds_;
+}
+
+String LocaleWin::DateTimeFormatWithSeconds() {
+ if (!date_time_format_with_seconds_.IsNull())
+ return date_time_format_with_seconds_;
+ StringBuilder builder;
+ builder.Append(DateFormat());
+ builder.Append(' ');
+ builder.Append(TimeFormat());
+ date_time_format_with_seconds_ = builder.ToString();
+ return date_time_format_with_seconds_;
+}
+
+String LocaleWin::DateTimeFormatWithoutSeconds() {
+ if (!date_time_format_without_seconds_.IsNull())
+ return date_time_format_without_seconds_;
+ StringBuilder builder;
+ builder.Append(DateFormat());
+ builder.Append(' ');
+ builder.Append(ShortTimeFormat());
+ date_time_format_without_seconds_ = builder.ToString();
+ return date_time_format_without_seconds_;
+}
+
+const Vector<String>& LocaleWin::ShortMonthLabels() {
+ EnsureShortMonthLabels();
+ return short_month_labels_;
+}
+
+const Vector<String>& LocaleWin::StandAloneMonthLabels() {
+ // Windows doesn't provide a way to get stand-alone month labels.
+ return MonthLabels();
+}
+
+const Vector<String>& LocaleWin::ShortStandAloneMonthLabels() {
+ // Windows doesn't provide a way to get stand-alone month labels.
+ return ShortMonthLabels();
+}
+
+const Vector<String>& LocaleWin::TimeAMPMLabels() {
+ if (time_ampm_labels_.IsEmpty()) {
+ time_ampm_labels_.push_back(GetLocaleInfoString(LOCALE_S1159));
+ time_ampm_labels_.push_back(GetLocaleInfoString(LOCALE_S2359));
+ }
+ return time_ampm_labels_;
+}
+
+void LocaleWin::InitializeLocaleData() {
+ if (did_initialize_number_data_)
+ return;
+
+ Vector<String, kDecimalSymbolsSize> symbols;
+ enum DigitSubstitution {
+ kDigitSubstitutionContext = 0,
+ kDigitSubstitution0to9 = 1,
+ kDigitSubstitutionNative = 2,
+ };
+ DWORD digit_substitution = kDigitSubstitution0to9;
+ GetLocaleInfo(LOCALE_IDIGITSUBSTITUTION, digit_substitution);
+ if (digit_substitution == kDigitSubstitution0to9) {
+ symbols.push_back("0");
+ symbols.push_back("1");
+ symbols.push_back("2");
+ symbols.push_back("3");
+ symbols.push_back("4");
+ symbols.push_back("5");
+ symbols.push_back("6");
+ symbols.push_back("7");
+ symbols.push_back("8");
+ symbols.push_back("9");
+ } else {
+ String digits = GetLocaleInfoString(LOCALE_SNATIVEDIGITS);
+ DCHECK_GE(digits.length(), 10u);
+ for (unsigned i = 0; i < 10; ++i)
+ symbols.push_back(digits.Substring(i, 1));
+ }
+ DCHECK(symbols.size() == kDecimalSeparatorIndex);
+ symbols.push_back(GetLocaleInfoString(LOCALE_SDECIMAL));
+ DCHECK(symbols.size() == kGroupSeparatorIndex);
+ symbols.push_back(GetLocaleInfoString(LOCALE_STHOUSAND));
+ DCHECK(symbols.size() == kDecimalSymbolsSize);
+
+ String negative_sign = GetLocaleInfoString(LOCALE_SNEGATIVESIGN);
+ enum NegativeFormat {
+ kNegativeFormatParenthesis = 0,
+ kNegativeFormatSignPrefix = 1,
+ kNegativeFormatSignSpacePrefix = 2,
+ kNegativeFormatSignSuffix = 3,
+ kNegativeFormatSpaceSignSuffix = 4,
+ };
+ DWORD negative_format = kNegativeFormatSignPrefix;
+ GetLocaleInfo(LOCALE_INEGNUMBER, negative_format);
+ String negative_prefix = g_empty_string;
+ String negative_suffix = g_empty_string;
+ switch (negative_format) {
+ case kNegativeFormatParenthesis:
+ negative_prefix = "(";
+ negative_suffix = ")";
+ break;
+ case kNegativeFormatSignSpacePrefix:
+ negative_prefix = negative_sign + " ";
+ break;
+ case kNegativeFormatSignSuffix:
+ negative_suffix = negative_sign;
+ break;
+ case kNegativeFormatSpaceSignSuffix:
+ negative_suffix = " " + negative_sign;
+ break;
+ case kNegativeFormatSignPrefix: // Fall through.
+ default:
+ negative_prefix = negative_sign;
+ break;
+ }
+ did_initialize_number_data_ = true;
+ SetLocaleData(symbols, g_empty_string, g_empty_string, negative_prefix,
+ negative_suffix);
+}
+}
diff --git a/chromium/third_party/blink/renderer/platform/text/locale_win.h b/chromium/third_party/blink/renderer/platform/text/locale_win.h
new file mode 100644
index 00000000000..c61238dc5bd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/locale_win.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2012 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_LOCALE_WIN_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_LOCALE_WIN_H_
+
+#include <windows.h>
+#include <memory>
+#include "third_party/blink/renderer/platform/text/platform_locale.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT LocaleWin : public Locale {
+ public:
+ static std::unique_ptr<LocaleWin> Create(LCID, bool defaults_for_locale);
+ ~LocaleWin();
+ const Vector<String>& WeekDayShortLabels() override;
+ unsigned FirstDayOfWeek() override;
+ bool IsRTL() override;
+ String DateFormat() override;
+ String MonthFormat() override;
+ String ShortMonthFormat() override;
+ String TimeFormat() override;
+ String ShortTimeFormat() override;
+ String DateTimeFormatWithSeconds() override;
+ String DateTimeFormatWithoutSeconds() override;
+ const Vector<String>& MonthLabels() override;
+ const Vector<String>& ShortMonthLabels() override;
+ const Vector<String>& StandAloneMonthLabels() override;
+ const Vector<String>& ShortStandAloneMonthLabels() override;
+ const Vector<String>& TimeAMPMLabels() override;
+
+ static String DateFormat(const String&);
+
+ private:
+ explicit LocaleWin(LCID, bool defaults_for_locale);
+ String GetLocaleInfoString(LCTYPE);
+ void GetLocaleInfo(LCTYPE, DWORD&);
+ void EnsureShortMonthLabels();
+ void EnsureMonthLabels();
+ void EnsureWeekDayShortLabels();
+ // Locale function:
+ void InitializeLocaleData() override;
+
+ LCID lcid_;
+ Vector<String> short_month_labels_;
+ Vector<String> month_labels_;
+ String date_format_;
+ String month_format_;
+ String short_month_format_;
+ String time_format_with_seconds_;
+ String time_format_without_seconds_;
+ String date_time_format_with_seconds_;
+ String date_time_format_without_seconds_;
+ Vector<String> time_ampm_labels_;
+ Vector<String> week_day_short_labels_;
+ unsigned first_day_of_week_;
+ bool did_initialize_number_data_;
+ bool defaults_for_locale_;
+};
+
+} // namespace blink
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/text/locale_win_test.cc b/chromium/third_party/blink/renderer/platform/text/locale_win_test.cc
new file mode 100644
index 00000000000..a3e638302c3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/locale_win_test.cc
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2012 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 "third_party/blink/renderer/platform/text/locale_win.h"
+
+#include <memory>
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/date_components.h"
+#include "third_party/blink/renderer/platform/wtf/date_math.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+
+namespace blink {
+
+class LocaleWinTest : public testing::Test {
+ protected:
+ enum {
+ kJanuary = 0,
+ kFebruary,
+ kMarch,
+ kApril,
+ kMay,
+ kJune,
+ kJuly,
+ kAugust,
+ kSeptember,
+ kOctober,
+ kNovember,
+ kDecember,
+ };
+
+ enum {
+ kSunday = 0,
+ kMonday,
+ kTuesday,
+ kWednesday,
+ kThursday,
+ kFriday,
+ kSaturday,
+ };
+
+ // See http://msdn.microsoft.com/en-us/goglobal/bb964664.aspx
+ // Note that some locales are country-neutral.
+ enum {
+ kArabicEG = 0x0C01, // ar-eg
+ kChineseCN = 0x0804, // zh-cn
+ kChineseHK = 0x0C04, // zh-hk
+ kChineseTW = 0x0404, // zh-tw
+ kGerman = 0x0407, // de
+ kEnglishUS = 0x409, // en-us
+ kFrenchFR = 0x40C, // fr
+ kJapaneseJP = 0x411, // ja
+ kKoreanKR = 0x0412, // ko
+ kPersian = 0x0429, // fa
+ kSpanish = 0x040A, // es
+ };
+
+ DateComponents GetDateComponents(int year, int month, int day) {
+ DateComponents date;
+ date.SetMillisecondsSinceEpochForDate(MsForDate(year, month, day));
+ return date;
+ }
+
+ double MsForDate(int year, int month, int day) {
+ return DateToDaysFrom1970(year, month, day) * kMsPerDay;
+ }
+
+ String FormatDate(LCID lcid, int year, int month, int day) {
+ std::unique_ptr<LocaleWin> locale =
+ LocaleWin::Create(lcid, true /* defaultsForLocale */);
+ return locale->FormatDateTime(GetDateComponents(year, month, day));
+ }
+
+ unsigned FirstDayOfWeek(LCID lcid) {
+ std::unique_ptr<LocaleWin> locale =
+ LocaleWin::Create(lcid, true /* defaultsForLocale */);
+ return locale->FirstDayOfWeek();
+ }
+
+ String MonthLabel(LCID lcid, unsigned index) {
+ std::unique_ptr<LocaleWin> locale =
+ LocaleWin::Create(lcid, true /* defaultsForLocale */);
+ return locale->MonthLabels()[index];
+ }
+
+ String WeekDayShortLabel(LCID lcid, unsigned index) {
+ std::unique_ptr<LocaleWin> locale =
+ LocaleWin::Create(lcid, true /* defaultsForLocale */);
+ return locale->WeekDayShortLabels()[index];
+ }
+
+ bool IsRTL(LCID lcid) {
+ std::unique_ptr<LocaleWin> locale =
+ LocaleWin::Create(lcid, true /* defaultsForLocale */);
+ return locale->IsRTL();
+ }
+
+ String MonthFormat(LCID lcid) {
+ std::unique_ptr<LocaleWin> locale =
+ LocaleWin::Create(lcid, true /* defaultsForLocale */);
+ return locale->MonthFormat();
+ }
+
+ String TimeFormat(LCID lcid) {
+ std::unique_ptr<LocaleWin> locale =
+ LocaleWin::Create(lcid, true /* defaultsForLocale */);
+ return locale->TimeFormat();
+ }
+
+ String ShortTimeFormat(LCID lcid) {
+ std::unique_ptr<LocaleWin> locale =
+ LocaleWin::Create(lcid, true /* defaultsForLocale */);
+ return locale->ShortTimeFormat();
+ }
+
+ String ShortMonthLabel(LCID lcid, unsigned index) {
+ std::unique_ptr<LocaleWin> locale =
+ LocaleWin::Create(lcid, true /* defaultsForLocale */);
+ return locale->ShortMonthLabels()[index];
+ }
+
+ String TimeAMPMLabel(LCID lcid, unsigned index) {
+ std::unique_ptr<LocaleWin> locale =
+ LocaleWin::Create(lcid, true /* defaultsForLocale */);
+ return locale->TimeAMPMLabels()[index];
+ }
+
+ String DecimalSeparator(LCID lcid) {
+ std::unique_ptr<LocaleWin> locale =
+ LocaleWin::Create(lcid, true /* defaultsForLocale */);
+ return locale->LocalizedDecimalSeparator();
+ }
+};
+
+TEST_F(LocaleWinTest, formatDate) {
+ EXPECT_STREQ("04/27/2005",
+ FormatDate(kEnglishUS, 2005, kApril, 27).Utf8().data());
+ EXPECT_STREQ("27/04/2005",
+ FormatDate(kFrenchFR, 2005, kApril, 27).Utf8().data());
+ EXPECT_STREQ("2005/04/27",
+ FormatDate(kJapaneseJP, 2005, kApril, 27).Utf8().data());
+}
+
+TEST_F(LocaleWinTest, firstDayOfWeek) {
+ EXPECT_EQ(static_cast<unsigned>(kSunday), FirstDayOfWeek(kEnglishUS));
+ EXPECT_EQ(static_cast<unsigned>(kMonday), FirstDayOfWeek(kFrenchFR));
+ EXPECT_EQ(static_cast<unsigned>(kSunday), FirstDayOfWeek(kJapaneseJP));
+}
+
+TEST_F(LocaleWinTest, monthLabels) {
+ EXPECT_STREQ("January", MonthLabel(kEnglishUS, kJanuary).Utf8().data());
+ EXPECT_STREQ("June", MonthLabel(kEnglishUS, kJune).Utf8().data());
+ EXPECT_STREQ("December", MonthLabel(kEnglishUS, kDecember).Utf8().data());
+
+ EXPECT_STREQ("janvier", MonthLabel(kFrenchFR, kJanuary).Utf8().data());
+ EXPECT_STREQ("juin", MonthLabel(kFrenchFR, kJune).Utf8().data());
+ EXPECT_STREQ(
+ "d\xC3\xA9"
+ "cembre",
+ MonthLabel(kFrenchFR, kDecember).Utf8().data());
+
+ EXPECT_STREQ("1\xE6\x9C\x88",
+ MonthLabel(kJapaneseJP, kJanuary).Utf8().data());
+ EXPECT_STREQ("6\xE6\x9C\x88", MonthLabel(kJapaneseJP, kJune).Utf8().data());
+ EXPECT_STREQ("12\xE6\x9C\x88",
+ MonthLabel(kJapaneseJP, kDecember).Utf8().data());
+}
+
+TEST_F(LocaleWinTest, weekDayShortLabels) {
+ EXPECT_STREQ("Sun", WeekDayShortLabel(kEnglishUS, kSunday).Utf8().data());
+ EXPECT_STREQ("Wed", WeekDayShortLabel(kEnglishUS, kWednesday).Utf8().data());
+ EXPECT_STREQ("Sat", WeekDayShortLabel(kEnglishUS, kSaturday).Utf8().data());
+
+ EXPECT_STREQ("dim.", WeekDayShortLabel(kFrenchFR, kSunday).Utf8().data());
+ EXPECT_STREQ("mer.", WeekDayShortLabel(kFrenchFR, kWednesday).Utf8().data());
+ EXPECT_STREQ("sam.", WeekDayShortLabel(kFrenchFR, kSaturday).Utf8().data());
+
+ EXPECT_STREQ("\xE6\x97\xA5",
+ WeekDayShortLabel(kJapaneseJP, kSunday).Utf8().data());
+ EXPECT_STREQ("\xE6\xB0\xB4",
+ WeekDayShortLabel(kJapaneseJP, kWednesday).Utf8().data());
+ EXPECT_STREQ("\xE5\x9C\x9F",
+ WeekDayShortLabel(kJapaneseJP, kSaturday).Utf8().data());
+}
+
+TEST_F(LocaleWinTest, isRTL) {
+ EXPECT_TRUE(IsRTL(kArabicEG));
+ EXPECT_FALSE(IsRTL(kEnglishUS));
+}
+
+TEST_F(LocaleWinTest, dateFormat) {
+ EXPECT_STREQ("y-M-d", LocaleWin::DateFormat("y-M-d").Utf8().data());
+ EXPECT_STREQ("''yy'-'''MM'''-'dd",
+ LocaleWin::DateFormat("''yy-''MM''-dd").Utf8().data());
+ EXPECT_STREQ("yyyy'-''''-'MMM'''''-'dd",
+ LocaleWin::DateFormat("yyyy-''''-MMM''''-dd").Utf8().data());
+ EXPECT_STREQ("yyyy'-'''''MMMM-dd",
+ LocaleWin::DateFormat("yyyy-''''MMMM-dd").Utf8().data());
+}
+
+TEST_F(LocaleWinTest, monthFormat) {
+ // Month format for EnglishUS:
+ // "MMMM, yyyy" on Windows 7 or older.
+ // "MMMM yyyy" on Window 8 or later.
+ EXPECT_STREQ("MMMM yyyy",
+ MonthFormat(kEnglishUS).Replace(',', "").Utf8().data());
+ EXPECT_STREQ("MMMM yyyy", MonthFormat(kFrenchFR).Utf8().data());
+ EXPECT_STREQ("yyyy\xE5\xB9\xB4M\xE6\x9C\x88",
+ MonthFormat(kJapaneseJP).Utf8().data());
+}
+
+TEST_F(LocaleWinTest, timeFormat) {
+ EXPECT_STREQ("h:mm:ss a", TimeFormat(kEnglishUS).Utf8().data());
+ EXPECT_STREQ("HH:mm:ss", TimeFormat(kFrenchFR).Utf8().data());
+ EXPECT_STREQ("H:mm:ss", TimeFormat(kJapaneseJP).Utf8().data());
+}
+
+TEST_F(LocaleWinTest, shortTimeFormat) {
+ EXPECT_STREQ("h:mm a", ShortTimeFormat(kEnglishUS).Utf8().data());
+ EXPECT_STREQ("HH:mm", ShortTimeFormat(kFrenchFR).Utf8().data());
+ EXPECT_STREQ("H:mm", ShortTimeFormat(kJapaneseJP).Utf8().data());
+}
+
+TEST_F(LocaleWinTest, shortMonthLabels) {
+ EXPECT_STREQ("Jan", ShortMonthLabel(kEnglishUS, 0).Utf8().data());
+ EXPECT_STREQ("Dec", ShortMonthLabel(kEnglishUS, 11).Utf8().data());
+ EXPECT_STREQ("janv.", ShortMonthLabel(kFrenchFR, 0).Utf8().data());
+ EXPECT_STREQ(
+ "d\xC3\xA9"
+ "c.",
+ ShortMonthLabel(kFrenchFR, 11).Utf8().data());
+ EXPECT_STREQ("1", ShortMonthLabel(kJapaneseJP, 0).Utf8().data());
+ EXPECT_STREQ("12", ShortMonthLabel(kJapaneseJP, 11).Utf8().data());
+}
+
+TEST_F(LocaleWinTest, timeAMPMLabels) {
+ EXPECT_STREQ("AM", TimeAMPMLabel(kEnglishUS, 0).Utf8().data());
+ EXPECT_STREQ("PM", TimeAMPMLabel(kEnglishUS, 1).Utf8().data());
+
+ EXPECT_STREQ("", TimeAMPMLabel(kFrenchFR, 0).Utf8().data());
+ EXPECT_STREQ("", TimeAMPMLabel(kFrenchFR, 1).Utf8().data());
+
+ EXPECT_STREQ("\xE5\x8D\x88\xE5\x89\x8D",
+ TimeAMPMLabel(kJapaneseJP, 0).Utf8().data());
+ EXPECT_STREQ("\xE5\x8D\x88\xE5\xBE\x8C",
+ TimeAMPMLabel(kJapaneseJP, 1).Utf8().data());
+}
+
+TEST_F(LocaleWinTest, decimalSeparator) {
+ EXPECT_STREQ(".", DecimalSeparator(kEnglishUS).Utf8().data());
+ EXPECT_STREQ(",", DecimalSeparator(kFrenchFR).Utf8().data());
+}
+
+static void TestNumberIsReversible(LCID lcid,
+ const char* original,
+ const char* should_have = 0) {
+ std::unique_ptr<LocaleWin> locale =
+ LocaleWin::Create(lcid, true /* defaultsForLocale */);
+ String localized = locale->ConvertToLocalizedNumber(original);
+ if (should_have)
+ EXPECT_TRUE(localized.Contains(should_have));
+ String converted = locale->ConvertFromLocalizedNumber(localized);
+ EXPECT_STREQ(original, converted.Utf8().data());
+}
+
+void TestNumbers(LCID lcid) {
+ TestNumberIsReversible(lcid, "123456789012345678901234567890");
+ TestNumberIsReversible(lcid, "-123.456");
+ TestNumberIsReversible(lcid, ".456");
+ TestNumberIsReversible(lcid, "-0.456");
+}
+
+TEST_F(LocaleWinTest, localizedNumberRoundTrip) {
+ TestNumberIsReversible(kEnglishUS, "123456789012345678901234567890");
+ TestNumberIsReversible(kEnglishUS, "-123.456", ".");
+ TestNumberIsReversible(kEnglishUS, ".456", ".");
+ TestNumberIsReversible(kEnglishUS, "-0.456", ".");
+
+ TestNumberIsReversible(kFrenchFR, "123456789012345678901234567890");
+ TestNumberIsReversible(kFrenchFR, "-123.456", ",");
+ TestNumberIsReversible(kFrenchFR, ".456", ",");
+ TestNumberIsReversible(kFrenchFR, "-0.456", ",");
+
+ // Test some of major locales.
+ TestNumbers(kArabicEG);
+ TestNumbers(kGerman);
+ TestNumbers(kSpanish);
+ TestNumbers(kPersian);
+ TestNumbers(kJapaneseJP);
+ TestNumbers(kKoreanKR);
+ TestNumbers(kChineseCN);
+ TestNumbers(kChineseHK);
+ TestNumbers(kChineseTW);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/mac/hyphenation_mac.cc b/chromium/third_party/blink/renderer/platform/text/mac/hyphenation_mac.cc
new file mode 100644
index 00000000000..009de61f986
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/mac/hyphenation_mac.cc
@@ -0,0 +1,62 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/text/hyphenation.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+#include "third_party/blink/renderer/platform/wtf/retain_ptr.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_view.h"
+
+namespace blink {
+
+class HyphenationCF final : public Hyphenation {
+ public:
+ HyphenationCF(RetainPtr<CFLocaleRef>& locale_cf) : locale_cf_(locale_cf) {
+ DCHECK(locale_cf_);
+ }
+
+ size_t LastHyphenLocation(const StringView& text,
+ size_t before_index) const override {
+ CFIndex result = CFStringGetHyphenationLocationBeforeIndex(
+ text.ToString().Impl()->CreateCFString().Get(), before_index,
+ CFRangeMake(0, text.length()), 0, locale_cf_.Get(), 0);
+ return result == kCFNotFound ? 0 : result;
+ }
+
+ // While Hyphenation::FirstHyphenLocation() works good, it computes all
+ // locations and discards ones after |after_index|.
+ // This version minimizes the computation for platforms that supports
+ // LastHyphenLocation() but does not support HyphenLocations().
+ size_t FirstHyphenLocation(const StringView& text, size_t after_index) const {
+ after_index =
+ std::max(after_index, static_cast<size_t>(kMinimumPrefixLength - 1));
+ size_t hyphen_location = text.length();
+ if (hyphen_location <= kMinimumSuffixLength)
+ return 0;
+ size_t max_hyphen_location = hyphen_location - kMinimumSuffixLength;
+ hyphen_location = max_hyphen_location;
+ for (;;) {
+ size_t previous = LastHyphenLocation(text, hyphen_location);
+ if (previous <= after_index)
+ break;
+ hyphen_location = previous;
+ }
+ return hyphen_location >= max_hyphen_location ? 0 : hyphen_location;
+ }
+
+ private:
+ RetainPtr<CFLocaleRef> locale_cf_;
+};
+
+scoped_refptr<Hyphenation> Hyphenation::PlatformGetHyphenation(
+ const AtomicString& locale) {
+ RetainPtr<CFStringRef> locale_cf_string = locale.Impl()->CreateCFString();
+ RetainPtr<CFLocaleRef> locale_cf =
+ AdoptCF(CFLocaleCreate(kCFAllocatorDefault, locale_cf_string.Get()));
+ if (!CFStringIsHyphenationAvailableForLocale(locale_cf.Get()))
+ return nullptr;
+ return base::AdoptRef(new HyphenationCF(locale_cf));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/platform_locale.cc b/chromium/third_party/blink/renderer/platform/text/platform_locale.cc
new file mode 100644
index 00000000000..a816c7bf0a0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/platform_locale.cc
@@ -0,0 +1,488 @@
+/*
+ * Copyright (C) 2011,2012 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 "third_party/blink/renderer/platform/text/platform_locale.h"
+
+#include <memory>
+
+#include "base/macros.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/text/date_time_format.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+
+namespace blink {
+
+namespace {
+Locale* g_default_locale;
+}
+
+class DateTimeStringBuilder : private DateTimeFormat::TokenHandler {
+ public:
+ // The argument objects must be alive until this object dies.
+ DateTimeStringBuilder(Locale&, const DateComponents&);
+
+ bool Build(const String&);
+ String ToString();
+
+ private:
+ // DateTimeFormat::TokenHandler functions.
+ void VisitField(DateTimeFormat::FieldType, int) final;
+ void VisitLiteral(const String&) final;
+
+ String ZeroPadString(const String&, size_t width);
+ void AppendNumber(int number, size_t width);
+
+ StringBuilder builder_;
+ Locale& localizer_;
+ const DateComponents& date_;
+
+ DISALLOW_COPY_AND_ASSIGN(DateTimeStringBuilder);
+};
+
+DateTimeStringBuilder::DateTimeStringBuilder(Locale& localizer,
+ const DateComponents& date)
+ : localizer_(localizer), date_(date) {}
+
+bool DateTimeStringBuilder::Build(const String& format_string) {
+ builder_.ReserveCapacity(format_string.length());
+ return DateTimeFormat::Parse(format_string, *this);
+}
+
+String DateTimeStringBuilder::ZeroPadString(const String& string,
+ size_t width) {
+ if (string.length() >= width)
+ return string;
+ StringBuilder zero_padded_string_builder;
+ zero_padded_string_builder.ReserveCapacity(width);
+ for (size_t i = string.length(); i < width; ++i)
+ zero_padded_string_builder.Append('0');
+ zero_padded_string_builder.Append(string);
+ return zero_padded_string_builder.ToString();
+}
+
+void DateTimeStringBuilder::AppendNumber(int number, size_t width) {
+ String zero_padded_number_string =
+ ZeroPadString(String::Number(number), width);
+ builder_.Append(
+ localizer_.ConvertToLocalizedNumber(zero_padded_number_string));
+}
+
+void DateTimeStringBuilder::VisitField(DateTimeFormat::FieldType field_type,
+ int number_of_pattern_characters) {
+ switch (field_type) {
+ case DateTimeFormat::kFieldTypeYear:
+ // Always use padding width of 4 so it matches DateTimeEditElement.
+ AppendNumber(date_.FullYear(), 4);
+ return;
+ case DateTimeFormat::kFieldTypeMonth:
+ if (number_of_pattern_characters == 3) {
+ builder_.Append(localizer_.ShortMonthLabels()[date_.Month()]);
+ } else if (number_of_pattern_characters == 4) {
+ builder_.Append(localizer_.MonthLabels()[date_.Month()]);
+ } else {
+ // Always use padding width of 2 so it matches DateTimeEditElement.
+ AppendNumber(date_.Month() + 1, 2);
+ }
+ return;
+ case DateTimeFormat::kFieldTypeMonthStandAlone:
+ if (number_of_pattern_characters == 3) {
+ builder_.Append(localizer_.ShortStandAloneMonthLabels()[date_.Month()]);
+ } else if (number_of_pattern_characters == 4) {
+ builder_.Append(localizer_.StandAloneMonthLabels()[date_.Month()]);
+ } else {
+ // Always use padding width of 2 so it matches DateTimeEditElement.
+ AppendNumber(date_.Month() + 1, 2);
+ }
+ return;
+ case DateTimeFormat::kFieldTypeDayOfMonth:
+ // Always use padding width of 2 so it matches DateTimeEditElement.
+ AppendNumber(date_.MonthDay(), 2);
+ return;
+ case DateTimeFormat::kFieldTypeWeekOfYear:
+ // Always use padding width of 2 so it matches DateTimeEditElement.
+ AppendNumber(date_.Week(), 2);
+ return;
+ case DateTimeFormat::kFieldTypePeriod:
+ builder_.Append(
+ localizer_.TimeAMPMLabels()[(date_.Hour() >= 12 ? 1 : 0)]);
+ return;
+ case DateTimeFormat::kFieldTypeHour12: {
+ int hour12 = date_.Hour() % 12;
+ if (!hour12)
+ hour12 = 12;
+ AppendNumber(hour12, number_of_pattern_characters);
+ return;
+ }
+ case DateTimeFormat::kFieldTypeHour23:
+ AppendNumber(date_.Hour(), number_of_pattern_characters);
+ return;
+ case DateTimeFormat::kFieldTypeHour11:
+ AppendNumber(date_.Hour() % 12, number_of_pattern_characters);
+ return;
+ case DateTimeFormat::kFieldTypeHour24: {
+ int hour24 = date_.Hour();
+ if (!hour24)
+ hour24 = 24;
+ AppendNumber(hour24, number_of_pattern_characters);
+ return;
+ }
+ case DateTimeFormat::kFieldTypeMinute:
+ AppendNumber(date_.Minute(), number_of_pattern_characters);
+ return;
+ case DateTimeFormat::kFieldTypeSecond:
+ if (!date_.Millisecond()) {
+ AppendNumber(date_.Second(), number_of_pattern_characters);
+ } else {
+ double second = date_.Second() + date_.Millisecond() / 1000.0;
+ String zero_padded_second_string = ZeroPadString(
+ String::Format("%.03f", second), number_of_pattern_characters + 4);
+ builder_.Append(
+ localizer_.ConvertToLocalizedNumber(zero_padded_second_string));
+ }
+ return;
+ default:
+ return;
+ }
+}
+
+void DateTimeStringBuilder::VisitLiteral(const String& text) {
+ DCHECK(text.length());
+ builder_.Append(text);
+}
+
+String DateTimeStringBuilder::ToString() {
+ return builder_.ToString();
+}
+
+Locale& Locale::DefaultLocale() {
+ DCHECK(IsMainThread());
+ if (!g_default_locale)
+ g_default_locale = Locale::Create(DefaultLanguage()).release();
+ return *g_default_locale;
+}
+
+void Locale::ResetDefaultLocale() {
+ // This is safe because no one owns a Locale object returned by
+ // DefaultLocale().
+ delete g_default_locale;
+ g_default_locale = nullptr;
+}
+
+Locale::~Locale() = default;
+
+String Locale::QueryString(WebLocalizedString::Name name) {
+ // FIXME: Returns a string locazlied for this locale.
+ return Platform::Current()->QueryLocalizedString(name);
+}
+
+String Locale::QueryString(WebLocalizedString::Name name,
+ const String& parameter) {
+ // FIXME: Returns a string locazlied for this locale.
+ return Platform::Current()->QueryLocalizedString(name, parameter);
+}
+
+String Locale::QueryString(WebLocalizedString::Name name,
+ const String& parameter1,
+ const String& parameter2) {
+ // FIXME: Returns a string locazlied for this locale.
+ return Platform::Current()->QueryLocalizedString(name, parameter1,
+ parameter2);
+}
+
+String Locale::ValidationMessageTooLongText(unsigned value_length,
+ int max_length) {
+ return QueryString(WebLocalizedString::kValidationTooLong,
+ ConvertToLocalizedNumber(String::Number(value_length)),
+ ConvertToLocalizedNumber(String::Number(max_length)));
+}
+
+String Locale::ValidationMessageTooShortText(unsigned value_length,
+ int min_length) {
+ if (value_length == 1) {
+ return QueryString(WebLocalizedString::kValidationTooShort,
+ ConvertToLocalizedNumber(String::Number(value_length)),
+ ConvertToLocalizedNumber(String::Number(min_length)));
+ }
+
+ return QueryString(WebLocalizedString::kValidationTooShortPlural,
+ ConvertToLocalizedNumber(String::Number(value_length)),
+ ConvertToLocalizedNumber(String::Number(min_length)));
+}
+
+String Locale::WeekFormatInLDML() {
+ String templ = QueryString(WebLocalizedString::kWeekFormatTemplate);
+ // Converts a string like "Week $2, $1" to an LDML date format pattern like
+ // "'Week 'ww', 'yyyy".
+ StringBuilder builder;
+ unsigned literal_start = 0;
+ unsigned length = templ.length();
+ for (unsigned i = 0; i + 1 < length; ++i) {
+ if (templ[i] == '$' && (templ[i + 1] == '1' || templ[i + 1] == '2')) {
+ if (literal_start < i)
+ DateTimeFormat::QuoteAndappend(
+ templ.Substring(literal_start, i - literal_start), builder);
+ builder.Append(templ[++i] == '1' ? "yyyy" : "ww");
+ literal_start = i + 1;
+ }
+ }
+ if (literal_start < length)
+ DateTimeFormat::QuoteAndappend(
+ templ.Substring(literal_start, length - literal_start), builder);
+ return builder.ToString();
+}
+
+void Locale::SetLocaleData(const Vector<String, kDecimalSymbolsSize>& symbols,
+ const String& positive_prefix,
+ const String& positive_suffix,
+ const String& negative_prefix,
+ const String& negative_suffix) {
+ for (size_t i = 0; i < symbols.size(); ++i) {
+ DCHECK(!symbols[i].IsEmpty());
+ decimal_symbols_[i] = symbols[i];
+ }
+ positive_prefix_ = positive_prefix;
+ positive_suffix_ = positive_suffix;
+ negative_prefix_ = negative_prefix;
+ negative_suffix_ = negative_suffix;
+ DCHECK(!positive_prefix_.IsEmpty() || !positive_suffix_.IsEmpty() ||
+ !negative_prefix_.IsEmpty() || !negative_suffix_.IsEmpty());
+ has_locale_data_ = true;
+
+ StringBuilder builder;
+ for (size_t i = 0; i < kDecimalSymbolsSize; ++i) {
+ // We don't accept group separatros.
+ if (i != kGroupSeparatorIndex)
+ builder.Append(decimal_symbols_[i]);
+ }
+ builder.Append(positive_prefix_);
+ builder.Append(positive_suffix_);
+ builder.Append(negative_prefix_);
+ builder.Append(negative_suffix_);
+ acceptable_number_characters_ = builder.ToString();
+}
+
+String Locale::ConvertToLocalizedNumber(const String& input) {
+ InitializeLocaleData();
+ if (!has_locale_data_ || input.IsEmpty())
+ return input;
+
+ unsigned i = 0;
+ bool is_negative = false;
+ StringBuilder builder;
+ builder.ReserveCapacity(input.length());
+
+ if (input[0] == '-') {
+ ++i;
+ is_negative = true;
+ builder.Append(negative_prefix_);
+ } else {
+ builder.Append(positive_prefix_);
+ }
+
+ for (; i < input.length(); ++i) {
+ switch (input[i]) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ builder.Append(decimal_symbols_[input[i] - '0']);
+ break;
+ case '.':
+ builder.Append(decimal_symbols_[kDecimalSeparatorIndex]);
+ break;
+ default:
+ NOTREACHED();
+ }
+ }
+
+ builder.Append(is_negative ? negative_suffix_ : positive_suffix_);
+
+ return builder.ToString();
+}
+
+static bool Matches(const String& text, unsigned position, const String& part) {
+ if (part.IsEmpty())
+ return true;
+ if (position + part.length() > text.length())
+ return false;
+ for (unsigned i = 0; i < part.length(); ++i) {
+ if (text[position + i] != part[i])
+ return false;
+ }
+ return true;
+}
+
+bool Locale::DetectSignAndGetDigitRange(const String& input,
+ bool& is_negative,
+ unsigned& start_index,
+ unsigned& end_index) {
+ start_index = 0;
+ end_index = input.length();
+ if (negative_prefix_.IsEmpty() && negative_suffix_.IsEmpty()) {
+ if (input.StartsWith(positive_prefix_) &&
+ input.EndsWith(positive_suffix_)) {
+ is_negative = false;
+ start_index = positive_prefix_.length();
+ end_index -= positive_suffix_.length();
+ } else {
+ is_negative = true;
+ }
+ } else {
+ if (input.StartsWith(negative_prefix_) &&
+ input.EndsWith(negative_suffix_)) {
+ is_negative = true;
+ start_index = negative_prefix_.length();
+ end_index -= negative_suffix_.length();
+ } else {
+ is_negative = false;
+ if (input.StartsWith(positive_prefix_) &&
+ input.EndsWith(positive_suffix_)) {
+ start_index = positive_prefix_.length();
+ end_index -= positive_suffix_.length();
+ } else {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+unsigned Locale::MatchedDecimalSymbolIndex(const String& input,
+ unsigned& position) {
+ for (unsigned symbol_index = 0; symbol_index < kDecimalSymbolsSize;
+ ++symbol_index) {
+ if (decimal_symbols_[symbol_index].length() &&
+ Matches(input, position, decimal_symbols_[symbol_index])) {
+ position += decimal_symbols_[symbol_index].length();
+ return symbol_index;
+ }
+ }
+ return kDecimalSymbolsSize;
+}
+
+String Locale::ConvertFromLocalizedNumber(const String& localized) {
+ InitializeLocaleData();
+ String input = localized.RemoveCharacters(IsASCIISpace);
+ if (!has_locale_data_ || input.IsEmpty())
+ return input;
+
+ bool is_negative;
+ unsigned start_index;
+ unsigned end_index;
+ if (!DetectSignAndGetDigitRange(input, is_negative, start_index, end_index))
+ return input;
+
+ // Ignore leading '+', but will reject '+'-only string later.
+ if (!is_negative && end_index - start_index >= 2 && input[start_index] == '+')
+ ++start_index;
+
+ StringBuilder builder;
+ builder.ReserveCapacity(input.length());
+ if (is_negative)
+ builder.Append('-');
+ for (unsigned i = start_index; i < end_index;) {
+ unsigned symbol_index = MatchedDecimalSymbolIndex(input, i);
+ if (symbol_index >= kDecimalSymbolsSize)
+ return input;
+ if (symbol_index == kDecimalSeparatorIndex)
+ builder.Append('.');
+ else if (symbol_index == kGroupSeparatorIndex)
+ return input;
+ else
+ builder.Append(static_cast<UChar>('0' + symbol_index));
+ }
+ String converted = builder.ToString();
+ // Ignore trailing '.', but will reject '.'-only string later.
+ if (converted.length() >= 2 && converted[converted.length() - 1] == '.')
+ converted = converted.Left(converted.length() - 1);
+ return converted;
+}
+
+String Locale::StripInvalidNumberCharacters(const String& input,
+ const String& standard_chars) {
+ InitializeLocaleData();
+ StringBuilder builder;
+ builder.ReserveCapacity(input.length());
+ for (unsigned i = 0; i < input.length(); ++i) {
+ UChar ch = input[i];
+ if (standard_chars.find(ch) != kNotFound)
+ builder.Append(ch);
+ else if (acceptable_number_characters_.find(ch) != kNotFound)
+ builder.Append(ch);
+ }
+ return builder.ToString();
+}
+
+String Locale::LocalizedDecimalSeparator() {
+ InitializeLocaleData();
+ return decimal_symbols_[kDecimalSeparatorIndex];
+}
+
+String Locale::FormatDateTime(const DateComponents& date,
+ FormatType format_type) {
+ if (date.GetType() == DateComponents::kInvalid)
+ return String();
+
+ DateTimeStringBuilder builder(*this, date);
+ switch (date.GetType()) {
+ case DateComponents::kTime:
+ builder.Build(format_type == kFormatTypeShort ? ShortTimeFormat()
+ : TimeFormat());
+ break;
+ case DateComponents::kDate:
+ builder.Build(DateFormat());
+ break;
+ case DateComponents::kMonth:
+ builder.Build(format_type == kFormatTypeShort ? ShortMonthFormat()
+ : MonthFormat());
+ break;
+ case DateComponents::kWeek:
+ builder.Build(WeekFormatInLDML());
+ break;
+ case DateComponents::kDateTime:
+ case DateComponents::kDateTimeLocal:
+ builder.Build(format_type == kFormatTypeShort
+ ? DateTimeFormatWithoutSeconds()
+ : DateTimeFormatWithSeconds());
+ break;
+ case DateComponents::kInvalid:
+ NOTREACHED();
+ break;
+ }
+ return builder.ToString();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/platform_locale.h b/chromium/third_party/blink/renderer/platform/text/platform_locale.h
new file mode 100644
index 00000000000..132d2cab3a5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/platform_locale.h
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2012 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_PLATFORM_LOCALE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_PLATFORM_LOCALE_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "third_party/blink/public/platform/web_localized_string.h"
+#include "third_party/blink/renderer/platform/date_components.h"
+#include "third_party/blink/renderer/platform/language.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT Locale {
+ USING_FAST_MALLOC(Locale);
+
+ public:
+ static std::unique_ptr<Locale> Create(const String& locale_identifier);
+ static Locale& DefaultLocale();
+ static void ResetDefaultLocale();
+
+ String QueryString(WebLocalizedString::Name);
+ String QueryString(WebLocalizedString::Name, const String& parameter);
+ String QueryString(WebLocalizedString::Name,
+ const String& parameter1,
+ const String& parameter2);
+ String ValidationMessageTooLongText(unsigned value_length, int max_length);
+ String ValidationMessageTooShortText(unsigned value_length, int min_length);
+
+ // Converts the specified number string to another number string localized
+ // for this Locale locale. The input string must conform to HTML
+ // floating-point numbers, and is not empty.
+ String ConvertToLocalizedNumber(const String&);
+
+ // Converts the specified localized number string to a number string in the
+ // HTML floating-point number format. The input string is provided by a end
+ // user, and might not be a number string. It's ok that the function returns
+ // a string which is not conforms to the HTML floating-point number format,
+ // callers of this function are responsible to check the format of the
+ // resultant string.
+ String ConvertFromLocalizedNumber(const String&);
+
+ // Remove characters from |input| if a character is not included in
+ // locale-specific number characters and |standardChars|.
+ String StripInvalidNumberCharacters(const String& input,
+ const String& standard_chars);
+
+ // Returns localized decimal separator, e.g. "." for English, "," for French.
+ String LocalizedDecimalSeparator();
+
+ // Returns date format in Unicode TR35 LDML[1] containing day of month,
+ // month, and year, e.g. "dd/mm/yyyy"
+ // [1] LDML http://unicode.org/reports/tr35/#Date_Format_Patterns
+ virtual String DateFormat() = 0;
+
+ // Returns a year-month format in Unicode TR35 LDML.
+ virtual String MonthFormat() = 0;
+
+ // Returns a year-month format using short month lanel in Unicode TR35 LDML.
+ virtual String ShortMonthFormat() = 0;
+
+ // Returns time format in Unicode TR35 LDML[1] containing hour, minute, and
+ // second with optional period(AM/PM), e.g. "h:mm:ss a"
+ // [1] LDML http://unicode.org/reports/tr35/#Date_Format_Patterns
+ virtual String TimeFormat() = 0;
+
+ // Returns time format in Unicode TR35 LDML containing hour, and minute
+ // with optional period(AM/PM), e.g. "h:mm a"
+ // Note: Some platforms return same value as timeFormat().
+ virtual String ShortTimeFormat() = 0;
+
+ // Returns a date-time format in Unicode TR35 LDML. It should have a seconds
+ // field.
+ virtual String DateTimeFormatWithSeconds() = 0;
+
+ // Returns a date-time format in Unicode TR35 LDML. It should have no seconds
+ // field.
+ virtual String DateTimeFormatWithoutSeconds() = 0;
+
+ // weekFormatInLDML() returns week and year format in LDML, Unicode
+ // technical standard 35, Locale Data Markup Language, e.g. "'Week' ww, yyyy"
+ String WeekFormatInLDML();
+
+ // Returns a vector of string of which size is 12. The first item is a
+ // localized string of Jan and the last item is a localized string of
+ // Dec. These strings should be short.
+ virtual const Vector<String>& ShortMonthLabels() = 0;
+
+ // Returns a vector of string of which size is 12. The first item is a
+ // stand-alone localized string of January and the last item is a
+ // stand-alone localized string of December. These strings should not be
+ // abbreviations.
+ virtual const Vector<String>& StandAloneMonthLabels() = 0;
+
+ // Stand-alone month version of shortMonthLabels.
+ virtual const Vector<String>& ShortStandAloneMonthLabels() = 0;
+
+ // Returns localized period field(AM/PM) strings.
+ virtual const Vector<String>& TimeAMPMLabels() = 0;
+
+ // Returns a vector of string of which size is 12. The first item is a
+ // localized string of January, and the last item is a localized string of
+ // December. These strings should not be abbreviations.
+ virtual const Vector<String>& MonthLabels() = 0;
+
+ // Returns a vector of string of which size is 7. The first item is a
+ // localized short string of Monday, and the last item is a localized
+ // short string of Saturday. These strings should be short.
+ virtual const Vector<String>& WeekDayShortLabels() = 0;
+
+ // The first day of a week. 0 is Sunday, and 6 is Saturday.
+ virtual unsigned FirstDayOfWeek() = 0;
+
+ // Returns true if people use right-to-left writing in the locale for this
+ // object.
+ virtual bool IsRTL() = 0;
+
+ enum FormatType {
+ kFormatTypeUnspecified,
+ kFormatTypeShort,
+ kFormatTypeMedium
+ };
+
+ // Serializes the specified date into a formatted date string to
+ // display to the user. If an implementation doesn't support
+ // localized dates the function should return an empty string.
+ // FormatType can be used to specify if you want the short format.
+ String FormatDateTime(const DateComponents&,
+ FormatType = kFormatTypeUnspecified);
+
+ virtual ~Locale();
+
+ protected:
+ enum {
+ // 0-9 for digits.
+ kDecimalSeparatorIndex = 10,
+ kGroupSeparatorIndex = 11,
+ kDecimalSymbolsSize
+ };
+
+ Locale() : has_locale_data_(false) {}
+ virtual void InitializeLocaleData() = 0;
+ void SetLocaleData(const Vector<String, kDecimalSymbolsSize>&,
+ const String& positive_prefix,
+ const String& positive_suffix,
+ const String& negative_prefix,
+ const String& negative_suffix);
+
+ private:
+ bool DetectSignAndGetDigitRange(const String& input,
+ bool& is_negative,
+ unsigned& start_index,
+ unsigned& end_index);
+ unsigned MatchedDecimalSymbolIndex(const String& input, unsigned& position);
+
+ String decimal_symbols_[kDecimalSymbolsSize];
+ String positive_prefix_;
+ String positive_suffix_;
+ String negative_prefix_;
+ String negative_suffix_;
+ String acceptable_number_characters_;
+ bool has_locale_data_;
+
+ DISALLOW_COPY_AND_ASSIGN(Locale);
+};
+
+} // namespace blink
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/text/platform_locale_test.cc b/chromium/third_party/blink/renderer/platform/text/platform_locale_test.cc
new file mode 100644
index 00000000000..3882b5c6f93
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/platform_locale_test.cc
@@ -0,0 +1,18 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/text/platform_locale.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+TEST(PlatformLocaleTest, StripInvalidNumberCharacters) {
+ std::unique_ptr<Locale> locale = Locale::Create("ar");
+ String result = locale->StripInvalidNumberCharacters(
+ String::FromUTF8("abc\xD9\xA0ghi"), "0123456789");
+ // ARABIC-INDIC DIGIT ZERO U+0660 = \xD9\xA0 in UTF-8
+ EXPECT_EQ(String::FromUTF8("\xD9\xA0"), result);
+}
+}
diff --git a/chromium/third_party/blink/renderer/platform/text/quoted_printable.cc b/chromium/third_party/blink/renderer/platform/text/quoted_printable.cc
new file mode 100644
index 00000000000..791352f18fd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/quoted_printable.cc
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/renderer/platform/text/quoted_printable.h"
+
+#include "third_party/blink/renderer/platform/wtf/ascii_ctype.h"
+
+namespace blink {
+
+static size_t LengthOfLineEndingAtIndex(const char* input,
+ size_t input_length,
+ size_t index) {
+ SECURITY_DCHECK(index < input_length);
+ if (input[index] == '\n')
+ return 1; // Single LF.
+
+ if (input[index] == '\r') {
+ if ((index + 1) == input_length || input[index + 1] != '\n')
+ return 1; // Single CR (Classic Mac OS).
+ return 2; // CR-LF.
+ }
+
+ return 0;
+}
+
+void QuotedPrintableEncode(const char* input,
+ size_t input_length,
+ QuotedPrintableEncodeDelegate* delegate,
+ Vector<char>& out) {
+ out.clear();
+ out.ReserveCapacity(input_length);
+ delegate->DidStartLine(out);
+ size_t current_line_length = 0;
+ for (size_t i = 0; i < input_length; ++i) {
+ bool is_last_character = (i == input_length - 1);
+ char current_character = input[i];
+ bool requires_encoding = false;
+ // All non-printable ASCII characters and = require encoding.
+ if ((current_character < ' ' || current_character > '~' ||
+ current_character == '=') &&
+ current_character != '\t')
+ requires_encoding = true;
+
+ // Decide if space and tab characters need to be encoded.
+ if (!requires_encoding &&
+ (current_character == '\t' || current_character == ' ')) {
+ bool end_of_line = is_last_character ||
+ LengthOfLineEndingAtIndex(input, input_length, i + 1);
+ requires_encoding =
+ delegate->ShouldEncodeWhiteSpaceCharacters(end_of_line);
+ }
+
+ // End of line should be converted to CR-LF sequences.
+ if (!is_last_character) {
+ size_t length_of_line_ending =
+ LengthOfLineEndingAtIndex(input, input_length, i);
+ if (length_of_line_ending) {
+ out.Append("\r\n", 2);
+ current_line_length = 0;
+ i += (length_of_line_ending -
+ 1); // -1 because we'll ++ in the for() above.
+ continue;
+ }
+ }
+
+ size_t length_of_encoded_character = 1;
+ if (requires_encoding)
+ length_of_encoded_character += 2;
+ if (!is_last_character)
+ length_of_encoded_character += 1; // + 1 for the = (soft line break).
+
+ // Insert a soft line break if necessary.
+ if (current_line_length + length_of_encoded_character >
+ delegate->GetMaxLineLengthForEncodedContent()) {
+ delegate->DidFinishLine(false /*last_line*/, out);
+ current_line_length = 0;
+ delegate->DidStartLine(out);
+ }
+
+ // Finally, insert the actual character(s).
+ if (requires_encoding) {
+ out.push_back('=');
+ out.push_back(UpperNibbleToASCIIHexDigit(current_character));
+ out.push_back(LowerNibbleToASCIIHexDigit(current_character));
+ current_line_length += 3;
+ } else {
+ out.push_back(current_character);
+ current_line_length++;
+ }
+ }
+ delegate->DidFinishLine(true /*last_line*/, out);
+}
+
+void QuotedPrintableDecode(const Vector<char>& in, Vector<char>& out) {
+ QuotedPrintableDecode(in.data(), in.size(), out);
+}
+
+void QuotedPrintableDecode(const char* data,
+ size_t data_length,
+ Vector<char>& out) {
+ out.clear();
+ if (!data_length)
+ return;
+
+ for (size_t i = 0; i < data_length; ++i) {
+ char current_character = data[i];
+ if (current_character != '=') {
+ out.push_back(current_character);
+ continue;
+ }
+ // We are dealing with a '=xx' sequence.
+ if (data_length - i < 3) {
+ // Unfinished = sequence, append as is.
+ out.push_back(current_character);
+ continue;
+ }
+ char upper_character = data[++i];
+ char lower_character = data[++i];
+ if (upper_character == '\r' && lower_character == '\n')
+ continue;
+
+ if (!IsASCIIHexDigit(upper_character) ||
+ !IsASCIIHexDigit(lower_character)) {
+ // Invalid sequence, = followed by non hex digits, just insert the
+ // characters as is.
+ out.push_back('=');
+ out.push_back(upper_character);
+ out.push_back(lower_character);
+ continue;
+ }
+ out.push_back(
+ static_cast<char>(ToASCIIHexValue(upper_character, lower_character)));
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/quoted_printable.h b/chromium/third_party/blink/renderer/platform/text/quoted_printable.h
new file mode 100644
index 00000000000..f6711ee0a89
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/quoted_printable.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_QUOTED_PRINTABLE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_QUOTED_PRINTABLE_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+// Delegate for controling the behavior of quoted-printable encoding. The
+// original characters may be encoded a bit differently depending on where
+// they live, header or body. For example, "=CRLF" should be used to break
+// long line in body while "CRLF+SPACE" should be used to break long line in
+// header.
+class PLATFORM_EXPORT QuotedPrintableEncodeDelegate {
+ public:
+ QuotedPrintableEncodeDelegate() = default;
+ virtual ~QuotedPrintableEncodeDelegate() = default;
+
+ // Returns maximum number of characters allowed for an encoded line, excluding
+ // prefix and soft line break.
+ virtual size_t GetMaxLineLengthForEncodedContent() const = 0;
+
+ // Returns true if space and tab characters need to be encoded.
+ virtual bool ShouldEncodeWhiteSpaceCharacters(bool end_of_line) const = 0;
+
+ // Called when an encoded line starts. The delegate can take this chance to
+ // add any prefix.
+ virtual void DidStartLine(Vector<char>& out) = 0;
+
+ // Called when an encoded line ends. The delegate can take this chance to add
+ // any suffix. If it is not last line, a soft line break should also
+ // be added after the suffix.
+ virtual void DidFinishLine(bool last_line, Vector<char>& out) = 0;
+};
+
+PLATFORM_EXPORT void QuotedPrintableEncode(const char*,
+ size_t,
+ QuotedPrintableEncodeDelegate*,
+ Vector<char>&);
+
+PLATFORM_EXPORT void QuotedPrintableDecode(const char*, size_t, Vector<char>&);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_QUOTED_PRINTABLE_H_
diff --git a/chromium/third_party/blink/renderer/platform/text/segmented_string.cc b/chromium/third_party/blink/renderer/platform/text/segmented_string.cc
new file mode 100644
index 00000000000..4568be87c85
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/segmented_string.cc
@@ -0,0 +1,180 @@
+/*
+ Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "third_party/blink/renderer/platform/text/segmented_string.h"
+
+namespace blink {
+
+unsigned SegmentedString::length() const {
+ unsigned length = current_string_.length();
+ if (IsComposite()) {
+ for (auto& substring : substrings_)
+ length += substring.length();
+ }
+ return length;
+}
+
+void SegmentedString::SetExcludeLineNumbers() {
+ current_string_.SetExcludeLineNumbers();
+ if (IsComposite()) {
+ for (auto& substring : substrings_)
+ substring.SetExcludeLineNumbers();
+ }
+}
+
+void SegmentedString::Clear() {
+ current_string_.Clear();
+ number_of_characters_consumed_prior_to_current_string_ = 0;
+ number_of_characters_consumed_prior_to_current_line_ = 0;
+ current_line_ = 0;
+ substrings_.clear();
+ closed_ = false;
+ empty_ = true;
+}
+
+void SegmentedString::Append(const SegmentedSubstring& s) {
+ DCHECK(!closed_);
+ if (!s.length())
+ return;
+
+ if (!current_string_.length()) {
+ number_of_characters_consumed_prior_to_current_string_ +=
+ current_string_.NumberOfCharactersConsumed();
+ current_string_ = s;
+ } else {
+ substrings_.push_back(s);
+ }
+ empty_ = false;
+}
+
+void SegmentedString::Push(UChar c) {
+ DCHECK(c);
+
+ // pushIfPossible attempts to rewind the pointer in the SegmentedSubstring,
+ // however it will fail if the SegmentedSubstring is empty, or
+ // when we prepended some text while consuming a SegmentedSubstring by
+ // document.write().
+ if (current_string_.PushIfPossible(c))
+ return;
+
+ Prepend(SegmentedString(String(&c, 1)), PrependType::kUnconsume);
+}
+
+void SegmentedString::Prepend(const SegmentedSubstring& s, PrependType type) {
+ DCHECK(!s.NumberOfCharactersConsumed());
+ if (!s.length())
+ return;
+
+ // FIXME: We're also ASSERTing that s is a fresh SegmentedSubstring.
+ // The assumption is sufficient for our current use, but we might
+ // need to handle the more elaborate cases in the future.
+ number_of_characters_consumed_prior_to_current_string_ +=
+ current_string_.NumberOfCharactersConsumed();
+ if (type == PrependType::kUnconsume)
+ number_of_characters_consumed_prior_to_current_string_ -= s.length();
+ if (!current_string_.length()) {
+ current_string_ = s;
+ } else {
+ // Shift our m_currentString into our list.
+ substrings_.push_front(current_string_);
+ current_string_ = s;
+ }
+ empty_ = false;
+}
+
+void SegmentedString::Close() {
+ // Closing a stream twice is likely a coding mistake.
+ DCHECK(!closed_);
+ closed_ = true;
+}
+
+void SegmentedString::Append(const SegmentedString& s) {
+ DCHECK(!closed_);
+
+ Append(s.current_string_);
+ if (s.IsComposite()) {
+ for (auto& substring : s.substrings_)
+ Append(substring);
+ }
+}
+
+void SegmentedString::Prepend(const SegmentedString& s, PrependType type) {
+ if (s.IsComposite()) {
+ auto it = s.substrings_.rbegin();
+ auto e = s.substrings_.rend();
+ for (; it != e; ++it)
+ Prepend(*it, type);
+ }
+ Prepend(s.current_string_, type);
+}
+
+void SegmentedString::AdvanceSubstring() {
+ if (IsComposite()) {
+ number_of_characters_consumed_prior_to_current_string_ +=
+ current_string_.NumberOfCharactersConsumed() + 1;
+ current_string_ = substrings_.TakeFirst();
+ // If we've previously consumed some characters of the non-current
+ // string, we now account for those characters as part of the current
+ // string, not as part of "prior to current string."
+ number_of_characters_consumed_prior_to_current_string_ -=
+ current_string_.NumberOfCharactersConsumed();
+ } else {
+ current_string_.Clear();
+ empty_ = true;
+ }
+}
+
+String SegmentedString::ToString() const {
+ StringBuilder result;
+ current_string_.AppendTo(result);
+ if (IsComposite()) {
+ for (auto& substring : substrings_)
+ substring.AppendTo(result);
+ }
+ return result.ToString();
+}
+
+void SegmentedString::Advance(unsigned count, UChar* consumed_characters) {
+ SECURITY_DCHECK(count <= length());
+ for (unsigned i = 0; i < count; ++i) {
+ consumed_characters[i] = CurrentChar();
+ Advance();
+ }
+}
+
+OrdinalNumber SegmentedString::CurrentLine() const {
+ return OrdinalNumber::FromZeroBasedInt(current_line_);
+}
+
+OrdinalNumber SegmentedString::CurrentColumn() const {
+ int zero_based_column = NumberOfCharactersConsumed() -
+ number_of_characters_consumed_prior_to_current_line_;
+ return OrdinalNumber::FromZeroBasedInt(zero_based_column);
+}
+
+void SegmentedString::SetCurrentPosition(OrdinalNumber line,
+ OrdinalNumber column_aftre_prolog,
+ int prolog_length) {
+ current_line_ = line.ZeroBasedInt();
+ number_of_characters_consumed_prior_to_current_line_ =
+ NumberOfCharactersConsumed() + prolog_length -
+ column_aftre_prolog.ZeroBasedInt();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/segmented_string.h b/chromium/third_party/blink/renderer/platform/text/segmented_string.h
new file mode 100644
index 00000000000..d0e893ec59f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/segmented_string.h
@@ -0,0 +1,326 @@
+/*
+ Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_SEGMENTED_STRING_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_SEGMENTED_STRING_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/deque.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_position.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class SegmentedString;
+
+class PLATFORM_EXPORT SegmentedSubstring {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ SegmentedSubstring() { data_.string8_ptr = nullptr; }
+
+ SegmentedSubstring(const String& str)
+ : length_(str.length()),
+ string_(str) {
+ if (length_) {
+ if (string_.Is8Bit()) {
+ is8_bit_ = true;
+ data_.string8_ptr = string_.Characters8();
+ current_char_ = *data_.string8_ptr;
+ } else {
+ is8_bit_ = false;
+ data_.string16_ptr = string_.Characters16();
+ current_char_ = *data_.string16_ptr;
+ }
+ } else {
+ is8_bit_ = true;
+ data_.string8_ptr = nullptr;
+ }
+ }
+
+ void Clear() {
+ length_ = 0;
+ is8_bit_ = true;
+ data_.string8_ptr = nullptr;
+ current_char_ = 0;
+ }
+
+ bool ExcludeLineNumbers() const { return !do_not_exclude_line_numbers_; }
+ bool DoNotExcludeLineNumbers() const { return do_not_exclude_line_numbers_; }
+
+ void SetExcludeLineNumbers() { do_not_exclude_line_numbers_ = false; }
+
+ int NumberOfCharactersConsumed() const { return string_.length() - length_; }
+
+ void AppendTo(StringBuilder& builder) const {
+ int offset = string_.length() - length_;
+
+ if (!offset) {
+ if (length_)
+ builder.Append(string_);
+ } else {
+ builder.Append(string_.Substring(offset, length_));
+ }
+ }
+
+ bool PushIfPossible(UChar c) {
+ if (!length_)
+ return false;
+
+ // This checks if either 8 or 16 bit strings are in the first character
+ // (where we can't rewind). Since length_ is greater than zero, we can check
+ // the Impl() directly and avoid a branch here.
+ if (data_.void_ptr == string_.Impl()->Bytes())
+ return false;
+
+ if (is8_bit_) {
+ if (*(data_.string8_ptr - 1) != c)
+ return false;
+
+ --data_.string8_ptr;
+ } else {
+ if (*(data_.string16_ptr - 1) != c)
+ return false;
+
+ --data_.string16_ptr;
+ }
+
+ current_char_ = c;
+ ++length_;
+ return true;
+ }
+
+ ALWAYS_INLINE UChar GetCurrentChar() const { return current_char_; }
+
+ ALWAYS_INLINE void IncrementAndDecrementLength() {
+ current_char_ = is8_bit_ ? *++data_.string8_ptr : *++data_.string16_ptr;
+ --length_;
+ }
+
+ String CurrentSubString(unsigned length) {
+ int offset = string_.length() - length_;
+ return string_.Substring(offset, length);
+ }
+
+ ALWAYS_INLINE int length() const { return length_; }
+
+ private:
+ union {
+ const LChar* string8_ptr;
+ const UChar* string16_ptr;
+ const void* void_ptr;
+ } data_;
+ int length_ = 0;
+ UChar current_char_ = 0;
+ bool do_not_exclude_line_numbers_ = true;
+ bool is8_bit_ = true;
+ String string_;
+};
+
+class PLATFORM_EXPORT SegmentedString {
+ DISALLOW_NEW();
+
+ public:
+ SegmentedString()
+ : number_of_characters_consumed_prior_to_current_string_(0),
+ number_of_characters_consumed_prior_to_current_line_(0),
+ current_line_(0),
+ closed_(false),
+ empty_(true) {}
+
+ SegmentedString(const String& str)
+ : current_string_(str),
+ number_of_characters_consumed_prior_to_current_string_(0),
+ number_of_characters_consumed_prior_to_current_line_(0),
+ current_line_(0),
+ closed_(false),
+ empty_(!str.length()) {}
+
+ void Clear();
+ void Close();
+
+ void Append(const SegmentedString&);
+ enum class PrependType {
+ kNewInput = 0,
+ kUnconsume = 1,
+ };
+ void Prepend(const SegmentedString&, PrependType);
+
+ bool ExcludeLineNumbers() const {
+ return current_string_.ExcludeLineNumbers();
+ }
+ void SetExcludeLineNumbers();
+
+ void Push(UChar);
+
+ bool IsEmpty() const { return empty_; }
+ unsigned length() const;
+
+ bool IsClosed() const { return closed_; }
+
+ enum LookAheadResult {
+ kDidNotMatch,
+ kDidMatch,
+ kNotEnoughCharacters,
+ };
+
+ LookAheadResult LookAhead(const String& string) {
+ return LookAheadInline(string, kTextCaseSensitive);
+ }
+ LookAheadResult LookAheadIgnoringCase(const String& string) {
+ return LookAheadInline(string, kTextCaseASCIIInsensitive);
+ }
+
+ ALWAYS_INLINE void Advance() {
+ if (LIKELY(current_string_.length() > 1)) {
+ current_string_.IncrementAndDecrementLength();
+ } else {
+ AdvanceSubstring();
+ }
+ }
+
+ ALWAYS_INLINE void UpdateLineNumber() {
+ if (LIKELY(current_string_.DoNotExcludeLineNumbers())) {
+ ++current_line_;
+ // Plus 1 because numberOfCharactersConsumed value hasn't incremented yet;
+ // it does with length() decrement below.
+ number_of_characters_consumed_prior_to_current_line_ =
+ NumberOfCharactersConsumed() + 1;
+ }
+ }
+
+ ALWAYS_INLINE void AdvanceAndUpdateLineNumber() {
+ DCHECK_GE(current_string_.length(), 1);
+
+ if (current_string_.GetCurrentChar() == '\n')
+ UpdateLineNumber();
+
+ if (LIKELY(current_string_.length() > 1)) {
+ current_string_.IncrementAndDecrementLength();
+ } else {
+ AdvanceSubstring();
+ }
+ }
+
+ ALWAYS_INLINE void AdvanceAndASSERT(UChar expected_character) {
+ DCHECK_EQ(expected_character, CurrentChar());
+ Advance();
+ }
+
+ ALWAYS_INLINE void AdvanceAndASSERTIgnoringCase(UChar expected_character) {
+ DCHECK_EQ(WTF::Unicode::FoldCase(CurrentChar()),
+ WTF::Unicode::FoldCase(expected_character));
+ Advance();
+ }
+
+ ALWAYS_INLINE void AdvancePastNonNewline() {
+ DCHECK_NE(CurrentChar(), '\n');
+ Advance();
+ }
+
+ ALWAYS_INLINE void AdvancePastNewlineAndUpdateLineNumber() {
+ DCHECK_EQ(CurrentChar(), '\n');
+ DCHECK_GE(current_string_.length(), 1);
+
+ UpdateLineNumber();
+
+ if (LIKELY(current_string_.length() > 1)) {
+ current_string_.IncrementAndDecrementLength();
+ } else {
+ AdvanceSubstring();
+ }
+ }
+
+ // Writes the consumed characters into consumedCharacters, which must
+ // have space for at least |count| characters.
+ void Advance(unsigned count, UChar* consumed_characters);
+
+ int NumberOfCharactersConsumed() const {
+ int number_of_pushed_characters = 0;
+ return number_of_characters_consumed_prior_to_current_string_ +
+ current_string_.NumberOfCharactersConsumed() -
+ number_of_pushed_characters;
+ }
+
+ String ToString() const;
+
+ ALWAYS_INLINE UChar CurrentChar() const {
+ return current_string_.GetCurrentChar();
+ }
+
+ // The method is moderately slow, comparing to currentLine method.
+ OrdinalNumber CurrentColumn() const;
+ OrdinalNumber CurrentLine() const;
+ // Sets value of line/column variables. Column is specified indirectly by a
+ // parameter columnAftreProlog which is a value of column that we should get
+ // after a prolog (first prologLength characters) has been consumed.
+ void SetCurrentPosition(OrdinalNumber line,
+ OrdinalNumber column_aftre_prolog,
+ int prolog_length);
+
+ private:
+ void Append(const SegmentedSubstring&);
+ void Prepend(const SegmentedSubstring&, PrependType);
+
+ void AdvanceSubstring();
+
+ inline LookAheadResult LookAheadInline(const String& string,
+ TextCaseSensitivity case_sensitivity) {
+ if (string.length() <= static_cast<unsigned>(current_string_.length())) {
+ String current_substring =
+ current_string_.CurrentSubString(string.length());
+ if (current_substring.StartsWith(string, case_sensitivity))
+ return kDidMatch;
+ return kDidNotMatch;
+ }
+ return LookAheadSlowCase(string, case_sensitivity);
+ }
+
+ LookAheadResult LookAheadSlowCase(const String& string,
+ TextCaseSensitivity case_sensitivity) {
+ unsigned count = string.length();
+ if (count > length())
+ return kNotEnoughCharacters;
+ UChar* consumed_characters;
+ String consumed_string =
+ String::CreateUninitialized(count, consumed_characters);
+ Advance(count, consumed_characters);
+ LookAheadResult result = kDidNotMatch;
+ if (consumed_string.StartsWith(string, case_sensitivity))
+ result = kDidMatch;
+ Prepend(SegmentedString(consumed_string), PrependType::kUnconsume);
+ return result;
+ }
+
+ bool IsComposite() const { return !substrings_.IsEmpty(); }
+
+ SegmentedSubstring current_string_;
+ int number_of_characters_consumed_prior_to_current_string_;
+ int number_of_characters_consumed_prior_to_current_line_;
+ int current_line_;
+ Deque<SegmentedSubstring> substrings_;
+ bool closed_;
+ bool empty_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/text/segmented_string_test.cc b/chromium/third_party/blink/renderer/platform/text/segmented_string_test.cc
new file mode 100644
index 00000000000..48b6d05f1f9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/segmented_string_test.cc
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2014 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 "third_party/blink/renderer/platform/text/segmented_string.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+TEST(SegmentedStringTest, CurrentChar) {
+ SegmentedString original(String("cde"));
+ {
+ SegmentedString copied(original);
+ SegmentedString assigned;
+ assigned = original;
+ EXPECT_EQ("cde", original.ToString());
+ EXPECT_EQ('c', original.CurrentChar());
+ EXPECT_EQ('c', copied.CurrentChar());
+ EXPECT_EQ('c', assigned.CurrentChar());
+ }
+ original.Push('b');
+ {
+ SegmentedString copied(original);
+ SegmentedString assigned;
+ assigned = original;
+ EXPECT_EQ("bcde", original.ToString());
+ EXPECT_EQ('b', original.CurrentChar());
+ EXPECT_EQ('b', copied.CurrentChar());
+ EXPECT_EQ('b', assigned.CurrentChar());
+ }
+ original.Push('a');
+ {
+ SegmentedString copied(original);
+ SegmentedString assigned;
+ assigned = original;
+ EXPECT_EQ("abcde", original.ToString());
+ EXPECT_EQ('a', original.CurrentChar());
+ EXPECT_EQ('a', copied.CurrentChar());
+ EXPECT_EQ('a', assigned.CurrentChar());
+ }
+ original.Advance();
+ {
+ SegmentedString copied(original);
+ SegmentedString assigned;
+ assigned = original;
+ EXPECT_EQ("bcde", original.ToString());
+ EXPECT_EQ('b', original.CurrentChar());
+ EXPECT_EQ('b', copied.CurrentChar());
+ EXPECT_EQ('b', assigned.CurrentChar());
+ }
+}
+
+TEST(SegmentedStringTest, Prepend) {
+ SegmentedString s1("1");
+ s1.Append(SegmentedString("2"));
+ s1.Append(SegmentedString("3"));
+ SegmentedString s2("4");
+ s2.Append(SegmentedString("5"));
+ s2.Append(SegmentedString("6"));
+
+ s2.Prepend(s1, SegmentedString::PrependType::kUnconsume);
+
+ EXPECT_EQ(s2.ToString(), String("123456"));
+}
+
+TEST(SegmentedStringTest, AdvanceSubstringConsumesCharacters) {
+ SegmentedString s1("1");
+ s1.Append(SegmentedString("2"));
+ s1.Append(SegmentedString("3"));
+
+ EXPECT_EQ(s1.NumberOfCharactersConsumed(), 0);
+ s1.Advance();
+ EXPECT_EQ(s1.NumberOfCharactersConsumed(), 1);
+ s1.Advance();
+ EXPECT_EQ(s1.NumberOfCharactersConsumed(), 2);
+ s1.Advance();
+ EXPECT_EQ(s1.NumberOfCharactersConsumed(), 3);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/string_truncator.cc b/chromium/third_party/blink/renderer/platform/text/string_truncator.cc
new file mode 100644
index 00000000000..ea7586d312f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/string_truncator.cc
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2005, 2006, 2007 Apple 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/text/string_truncator.h"
+
+#include "third_party/blink/renderer/platform/fonts/font.h"
+#include "third_party/blink/renderer/platform/text/text_break_iterator.h"
+#include "third_party/blink/renderer/platform/text/text_run.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
+
+namespace blink {
+
+#define STRING_BUFFER_SIZE 2048
+
+typedef unsigned TruncationFunction(const String&,
+ unsigned length,
+ unsigned keep_count,
+ UChar* buffer);
+
+static inline int TextBreakAtOrPreceding(
+ const NonSharedCharacterBreakIterator& it,
+ int offset) {
+ if (it.IsBreak(offset))
+ return offset;
+
+ int result = it.Preceding(offset);
+ return result == kTextBreakDone ? 0 : result;
+}
+
+static inline int BoundedTextBreakFollowing(
+ const NonSharedCharacterBreakIterator& it,
+ int offset,
+ int length) {
+ int result = it.Following(offset);
+ return result == kTextBreakDone ? length : result;
+}
+
+static unsigned CenterTruncateToBuffer(const String& string,
+ unsigned length,
+ unsigned keep_count,
+ UChar* buffer) {
+ DCHECK_LT(keep_count, length);
+ DCHECK(keep_count < STRING_BUFFER_SIZE);
+
+ unsigned omit_start = (keep_count + 1) / 2;
+ NonSharedCharacterBreakIterator it(string);
+ unsigned omit_end = BoundedTextBreakFollowing(
+ it, omit_start + (length - keep_count) - 1, length);
+ omit_start = TextBreakAtOrPreceding(it, omit_start);
+
+ unsigned truncated_length = omit_start + 1 + (length - omit_end);
+ DCHECK_LE(truncated_length, length);
+
+ string.CopyTo(buffer, 0, omit_start);
+ buffer[omit_start] = kHorizontalEllipsisCharacter;
+ string.CopyTo(&buffer[omit_start + 1], omit_end, length - omit_end);
+
+ return truncated_length;
+}
+
+static unsigned RightTruncateToBuffer(const String& string,
+ unsigned length,
+ unsigned keep_count,
+ UChar* buffer) {
+ DCHECK_LT(keep_count, length);
+ DCHECK(keep_count < STRING_BUFFER_SIZE);
+
+ NonSharedCharacterBreakIterator it(string);
+ unsigned keep_length = TextBreakAtOrPreceding(it, keep_count);
+ unsigned truncated_length = keep_length + 1;
+
+ string.CopyTo(buffer, 0, keep_length);
+ buffer[keep_length] = kHorizontalEllipsisCharacter;
+
+ return truncated_length;
+}
+
+static float StringWidth(const Font& renderer, const String& string) {
+ TextRun run(string);
+ return renderer.Width(run);
+}
+
+static float StringWidth(const Font& renderer,
+ const UChar* characters,
+ unsigned length) {
+ TextRun run(characters, length);
+ return renderer.Width(run);
+}
+
+static String TruncateString(const String& string,
+ float max_width,
+ const Font& font,
+ TruncationFunction truncate_to_buffer) {
+ if (string.IsEmpty())
+ return string;
+
+ DCHECK_GE(max_width, 0);
+
+ float current_ellipsis_width =
+ StringWidth(font, &kHorizontalEllipsisCharacter, 1);
+
+ UChar string_buffer[STRING_BUFFER_SIZE];
+ unsigned truncated_length;
+ unsigned keep_count;
+ unsigned length = string.length();
+
+ if (length > STRING_BUFFER_SIZE) {
+ keep_count = STRING_BUFFER_SIZE - 1; // need 1 character for the ellipsis
+ truncated_length =
+ CenterTruncateToBuffer(string, length, keep_count, string_buffer);
+ } else {
+ keep_count = length;
+ string.CopyTo(string_buffer, 0, length);
+ truncated_length = length;
+ }
+
+ float width = StringWidth(font, string_buffer, truncated_length);
+ if (width <= max_width)
+ return string;
+
+ unsigned keep_count_for_largest_known_to_fit = 0;
+ float width_for_largest_known_to_fit = current_ellipsis_width;
+
+ unsigned keep_count_for_smallest_known_to_not_fit = keep_count;
+ float width_for_smallest_known_to_not_fit = width;
+
+ if (current_ellipsis_width >= max_width) {
+ keep_count_for_largest_known_to_fit = 1;
+ keep_count_for_smallest_known_to_not_fit = 2;
+ }
+
+ while (keep_count_for_largest_known_to_fit + 1 <
+ keep_count_for_smallest_known_to_not_fit) {
+ DCHECK_LE(width_for_largest_known_to_fit, max_width);
+ DCHECK_GT(width_for_smallest_known_to_not_fit, max_width);
+
+ float ratio =
+ (keep_count_for_smallest_known_to_not_fit -
+ keep_count_for_largest_known_to_fit) /
+ (width_for_smallest_known_to_not_fit - width_for_largest_known_to_fit);
+ keep_count = static_cast<unsigned>(max_width * ratio);
+
+ if (keep_count <= keep_count_for_largest_known_to_fit) {
+ keep_count = keep_count_for_largest_known_to_fit + 1;
+ } else if (keep_count >= keep_count_for_smallest_known_to_not_fit) {
+ keep_count = keep_count_for_smallest_known_to_not_fit - 1;
+ }
+
+ DCHECK_LT(keep_count, length);
+ DCHECK_GT(keep_count, 0u);
+ DCHECK_LT(keep_count, keep_count_for_smallest_known_to_not_fit);
+ DCHECK_GT(keep_count, keep_count_for_largest_known_to_fit);
+
+ truncated_length =
+ truncate_to_buffer(string, length, keep_count, string_buffer);
+
+ width = StringWidth(font, string_buffer, truncated_length);
+ if (width <= max_width) {
+ keep_count_for_largest_known_to_fit = keep_count;
+ width_for_largest_known_to_fit = width;
+ } else {
+ keep_count_for_smallest_known_to_not_fit = keep_count;
+ width_for_smallest_known_to_not_fit = width;
+ }
+ }
+
+ if (!keep_count_for_largest_known_to_fit)
+ keep_count_for_largest_known_to_fit = 1;
+
+ if (keep_count != keep_count_for_largest_known_to_fit) {
+ keep_count = keep_count_for_largest_known_to_fit;
+ truncated_length =
+ truncate_to_buffer(string, length, keep_count, string_buffer);
+ }
+
+ return String(string_buffer, truncated_length);
+}
+
+String StringTruncator::CenterTruncate(const String& string,
+ float max_width,
+ const Font& font) {
+ return TruncateString(string, max_width, font, CenterTruncateToBuffer);
+}
+
+String StringTruncator::RightTruncate(const String& string,
+ float max_width,
+ const Font& font) {
+ return TruncateString(string, max_width, font, RightTruncateToBuffer);
+}
+
+float StringTruncator::Width(const String& string, const Font& font) {
+ return StringWidth(font, string);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/string_truncator.h b/chromium/third_party/blink/renderer/platform/text/string_truncator.h
new file mode 100644
index 00000000000..b93a48d82cc
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/string_truncator.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2005, 2006, 2007 Apple 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_STRING_TRUNCATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_STRING_TRUNCATOR_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+class Font;
+
+class PLATFORM_EXPORT StringTruncator {
+ STATIC_ONLY(StringTruncator);
+
+ public:
+ static String CenterTruncate(const String&, float max_width, const Font&);
+ static String RightTruncate(const String&, float max_width, const Font&);
+ static float Width(const String&, const Font&);
+};
+
+} // namespace blink
+
+#endif // !defined(StringTruncator_h)
diff --git a/chromium/third_party/blink/renderer/platform/text/suffix_tree.h b/chromium/third_party/blink/renderer/platform/text/suffix_tree.h
new file mode 100644
index 00000000000..4f20c2a2046
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/suffix_tree.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2010 Adam Barth. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_SUFFIX_TREE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_SUFFIX_TREE_H_
+
+#include <algorithm>
+#include <utility>
+
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class UnicodeCodebook {
+ STATIC_ONLY(UnicodeCodebook);
+
+ public:
+ static int CodeWord(UChar c) { return c; }
+ enum { kCodeSize = 1 << 8 * sizeof(UChar) };
+};
+
+class ASCIICodebook {
+ STATIC_ONLY(ASCIICodebook);
+
+ public:
+ static int CodeWord(UChar c) { return c & (kCodeSize - 1); }
+ enum { kCodeSize = 1 << (8 * sizeof(char) - 1) };
+};
+
+template <typename Codebook>
+class SuffixTree {
+ USING_FAST_MALLOC(SuffixTree);
+
+ public:
+ SuffixTree(const String& text, unsigned depth) : depth_(depth), leaf_(true) {
+ Build(text);
+ }
+
+ bool MightContain(const String& query) {
+ Node* current = &root_;
+ int limit = std::min(depth_, query.length());
+ for (int i = 0; i < limit; ++i) {
+ auto iter = current->Find(Codebook::CodeWord(query[i]));
+ if (iter == current->End())
+ return false;
+ current = iter->second;
+ }
+ return true;
+ }
+
+ private:
+ class Node {
+ USING_FAST_MALLOC(Node);
+
+ public:
+ Node(bool is_leaf = false) : is_leaf_(is_leaf) {}
+
+ ~Node() {
+ for (const auto& pair : children_) {
+ Node* child = pair.second;
+ if (child && !child->is_leaf_)
+ delete child;
+ }
+ }
+
+ Node*& At(int key) {
+ auto it = Find(key);
+ if (it != children_.end())
+ return it->second;
+ children_.emplace_back(key, nullptr);
+ return children_.back().second;
+ }
+
+ typename Vector<std::pair<int, Node*>>::iterator Find(int key) {
+ return std::find_if(children_.begin(), children_.end(),
+ [key](const std::pair<int, Node*>& entry) {
+ return entry.first == key;
+ });
+ }
+
+ typename Vector<std::pair<int, Node*>>::iterator End() {
+ return children_.end();
+ }
+
+ private:
+ // TODO(tsepez): convert to base::flat_map when allowed in blink.
+ Vector<std::pair<int, Node*>> children_;
+ const bool is_leaf_;
+
+ DISALLOW_COPY_AND_ASSIGN(Node);
+ };
+
+ void Build(const String& text) {
+ for (unsigned base = 0; base < text.length(); ++base) {
+ Node* current = &root_;
+ unsigned limit = std::min(base + depth_, text.length());
+ for (unsigned offset = 0; base + offset < limit; ++offset) {
+ DCHECK_NE(current, &leaf_);
+ Node*& child = current->At(Codebook::CodeWord(text[base + offset]));
+ if (!child)
+ child = base + offset + 1 == limit ? &leaf_ : new Node();
+ current = child;
+ }
+ }
+ }
+
+ Node root_;
+ unsigned depth_;
+
+ // Instead of allocating a fresh empty leaf node for ever leaf in the tree
+ // (there can be a lot of these), we alias all the leaves to this "static"
+ // leaf node.
+ Node leaf_;
+
+ DISALLOW_COPY_AND_ASSIGN(SuffixTree);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_SUFFIX_TREE_H_
diff --git a/chromium/third_party/blink/renderer/platform/text/suffix_tree_test.cc b/chromium/third_party/blink/renderer/platform/text/suffix_tree_test.cc
new file mode 100644
index 00000000000..9aebc33b6e0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/suffix_tree_test.cc
@@ -0,0 +1,32 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/text/suffix_tree.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+TEST(SuffixTreeTest, EmptyString) {
+ SuffixTree<ASCIICodebook> tree("", 16);
+ EXPECT_TRUE(tree.MightContain(""));
+ EXPECT_FALSE(tree.MightContain("potato"));
+}
+
+TEST(SuffixTreeTest, NormalString) {
+ SuffixTree<ASCIICodebook> tree("banana", 16);
+ EXPECT_TRUE(tree.MightContain(""));
+ EXPECT_TRUE(tree.MightContain("a"));
+ EXPECT_TRUE(tree.MightContain("na"));
+ EXPECT_TRUE(tree.MightContain("ana"));
+ EXPECT_TRUE(tree.MightContain("nana"));
+ EXPECT_TRUE(tree.MightContain("anana"));
+ EXPECT_TRUE(tree.MightContain("banana"));
+ EXPECT_FALSE(tree.MightContain("ab"));
+ EXPECT_FALSE(tree.MightContain("bananan"));
+ EXPECT_FALSE(tree.MightContain("abanana"));
+ EXPECT_FALSE(tree.MightContain("potato"));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/tab_size.h b/chromium/third_party/blink/renderer/platform/text/tab_size.h
new file mode 100644
index 00000000000..d7b5b5e999e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/tab_size.h
@@ -0,0 +1,38 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_TAB_SIZE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_TAB_SIZE_H_
+
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+struct TabSize {
+ DISALLOW_NEW();
+ TabSize(float pixels) : float_value_(pixels), is_spaces_(0) {}
+
+ TabSize(int spaces) : float_value_(spaces), is_spaces_(1) {}
+
+ bool IsSpaces() const { return is_spaces_; }
+
+ float GetPixelSize(float space_width) const {
+ return is_spaces_ ? float_value_ * space_width : float_value_;
+ }
+
+ float float_value_;
+ unsigned is_spaces_ : 1;
+};
+
+inline bool operator==(const TabSize& a, const TabSize& b) {
+ return (a.float_value_ == b.float_value_) && (a.is_spaces_ == b.is_spaces_);
+}
+
+inline bool operator!=(const TabSize& a, const TabSize& b) {
+ return !(a == b);
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_TAB_SIZE_H_
diff --git a/chromium/third_party/blink/renderer/platform/text/text_boundaries.cc b/chromium/third_party/blink/renderer/platform/text/text_boundaries.cc
new file mode 100644
index 00000000000..2161defad9d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/text_boundaries.cc
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2009 Dominik Roettsches <dominik.roettsches@access-company.com>
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/text/text_boundaries.h"
+
+#include "third_party/blink/renderer/platform/text/text_break_iterator.h"
+#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_impl.h"
+
+namespace blink {
+
+int EndOfFirstWordBoundaryContext(const UChar* characters, int length) {
+ for (int i = 0; i < length;) {
+ int first = i;
+ UChar32 ch;
+ U16_NEXT(characters, i, length, ch);
+ if (!RequiresContextForWordBoundary(ch))
+ return first;
+ }
+ return length;
+}
+
+int StartOfLastWordBoundaryContext(const UChar* characters, int length) {
+ for (int i = length; i > 0;) {
+ int last = i;
+ UChar32 ch;
+ U16_PREV(characters, 0, i, ch);
+ if (!RequiresContextForWordBoundary(ch))
+ return last;
+ }
+ return 0;
+}
+
+int FindNextWordForward(const UChar* chars, int len, int position) {
+ TextBreakIterator* it = WordBreakIterator(chars, len);
+
+ position = it->following(position);
+ while (position != kTextBreakDone) {
+ // We stop searching when the character preceeding the break
+ // is alphanumeric or underscore.
+ if (position < len &&
+ (WTF::Unicode::IsAlphanumeric(chars[position - 1]) ||
+ chars[position - 1] == kLowLineCharacter))
+ return position;
+
+ position = it->following(position);
+ }
+
+ return len;
+}
+
+int FindNextWordBackward(const UChar* chars, int len, int position) {
+ TextBreakIterator* it = WordBreakIterator(chars, len);
+
+ position = it->preceding(position);
+ while (position != kTextBreakDone) {
+ // We stop searching when the character following the break
+ // is alphanumeric or underscore.
+ if (position > 0 && (WTF::Unicode::IsAlphanumeric(chars[position]) ||
+ chars[position] == kLowLineCharacter))
+ return position;
+
+ position = it->preceding(position);
+ }
+
+ return 0;
+}
+
+std::pair<int, int> FindWordBackward(const UChar* chars,
+ int len,
+ int position) {
+ DCHECK_GE(len, 0);
+ DCHECK_LE(position, len);
+ if (len == 0)
+ return {0, 0};
+ TextBreakIterator* it = WordBreakIterator(chars, len);
+ const int start = it->preceding(position);
+ const int end = it->next();
+ if (start < 0) {
+ // There are no words at |position|.
+ return {0, 0};
+ }
+ return {start, end};
+}
+
+std::pair<int, int> FindWordForward(const UChar* chars, int len, int position) {
+ DCHECK_GE(len, 0);
+ DCHECK_LE(position, len);
+ if (len == 0)
+ return {0, 0};
+ TextBreakIterator* it = WordBreakIterator(chars, len);
+ const int end = it->following(position);
+ const int start = it->previous();
+ if (end < 0) {
+ // There are no words at |position|.
+ return {len, len};
+ }
+ return {start, end};
+}
+
+int FindWordStartBoundary(const UChar* chars, int len, int position) {
+ TextBreakIterator* it = WordBreakIterator(chars, len);
+ it->following(position);
+ return it->previous();
+}
+
+int FindWordEndBoundary(const UChar* chars, int len, int position) {
+ TextBreakIterator* it = WordBreakIterator(chars, len);
+ int end = it->following(position);
+ return end < 0 ? it->last() : end;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/text_boundaries.h b/chromium/third_party/blink/renderer/platform/text/text_boundaries.h
new file mode 100644
index 00000000000..44c5438c5ab
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/text_boundaries.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2004, 2006 Apple Computer, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_TEXT_BOUNDARIES_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_TEXT_BOUNDARIES_H_
+
+#include <utility>
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
+
+namespace blink {
+
+inline bool RequiresContextForWordBoundary(UChar32 ch) {
+ return WTF::Unicode::HasLineBreakingPropertyComplexContext(ch);
+}
+
+PLATFORM_EXPORT int EndOfFirstWordBoundaryContext(const UChar* characters,
+ int length);
+PLATFORM_EXPORT int StartOfLastWordBoundaryContext(const UChar* characters,
+ int length);
+
+// |UChar*| should be a string in logical order instead of visual order, since
+// |FindWordBoundary()| uses ICU, which works on logical order strings
+PLATFORM_EXPORT std::pair<int, int> FindWordBackward(const UChar*,
+ int len,
+ int position);
+PLATFORM_EXPORT std::pair<int, int> FindWordForward(const UChar*,
+ int len,
+ int position);
+PLATFORM_EXPORT int FindWordStartBoundary(const UChar*, int len, int position);
+PLATFORM_EXPORT int FindWordEndBoundary(const UChar*, int len, int position);
+PLATFORM_EXPORT int FindNextWordBackward(const UChar*, int len, int position);
+PLATFORM_EXPORT int FindNextWordForward(const UChar*, int len, int position);
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/text/text_boundaries_test.cc b/chromium/third_party/blink/renderer/platform/text/text_boundaries_test.cc
new file mode 100644
index 00000000000..ca60c49d010
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/text_boundaries_test.cc
@@ -0,0 +1,175 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include "third_party/blink/renderer/platform/text/text_boundaries.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class TextBoundariesTest : public testing::Test {};
+
+namespace {
+
+std::pair<String, int> ParsePositionMarker(const std::string input8) {
+ String input16 = String::FromUTF8(input8.data(), input8.size());
+ input16.Ensure16Bit();
+ const size_t position = input16.find('|');
+ DCHECK(position != kNotFound) << input8 << " should have position marker(|).";
+ String text16 = input16.Left(position);
+ text16.append(input16.Substring(position + 1));
+ text16.Ensure16Bit();
+ return {text16, position};
+}
+
+std::string MakeResultText(const String& text, int start, int end) {
+ StringBuilder builder;
+ if (start < 0 && end < 0) {
+ builder.Append(text);
+ } else if (start < 0) {
+ builder.Append(text.Left(end));
+ builder.Append('^');
+ builder.Append(text.Substring(end));
+ } else if (end < 0) {
+ builder.Append(text.Left(start));
+ builder.Append('|');
+ builder.Append(text.Substring(start));
+ } else {
+ builder.Append(text.Left(start));
+ builder.Append('^');
+ builder.Append(text.Substring(start, end - start));
+ builder.Append('|');
+ }
+ builder.Append(text.Substring(end));
+ const CString result8 = builder.ToString().Utf8();
+ return std::string(result8.data(), result8.length());
+}
+
+// Returns word boundray with start(^) and end(|) markes from text with
+// position(|) marker.
+std::string TryFindWordBackward(const std::string input8) {
+ std::pair<String, int> string_and_offset = ParsePositionMarker(input8);
+ const String text16 = string_and_offset.first;
+ const int position = string_and_offset.second;
+ std::pair<int, int> start_and_end =
+ FindWordBackward(text16.Characters16(), text16.length(), position);
+ return MakeResultText(text16, start_and_end.first, start_and_end.second);
+}
+
+// Returns word boundray with start(^) and end(|) markes from text with
+// position(|) marker.
+std::string TryFindWordForward(const std::string input8) {
+ std::pair<String, int> string_and_offset = ParsePositionMarker(input8);
+ const String text16 = string_and_offset.first;
+ const int position = string_and_offset.second;
+ std::pair<int, int> start_and_end =
+ FindWordForward(text16.Characters16(), text16.length(), position);
+ return MakeResultText(text16, start_and_end.first, start_and_end.second);
+}
+
+} // namespace
+
+TEST_F(TextBoundariesTest, BackwardBasic) {
+ EXPECT_EQ("^|abc def", TryFindWordBackward("|abc def"));
+ EXPECT_EQ("^abc| def", TryFindWordBackward("a|bc def"));
+ EXPECT_EQ("^abc| def", TryFindWordBackward("ab|c def"));
+ EXPECT_EQ("^abc| def", TryFindWordBackward("abc| def"));
+ EXPECT_EQ("abc^ |def", TryFindWordBackward("abc |def"));
+ EXPECT_EQ("abc ^def|", TryFindWordBackward("abc d|ef"));
+ EXPECT_EQ("abc ^def|", TryFindWordBackward("abc de|f"));
+ EXPECT_EQ("abc ^def|", TryFindWordBackward("abc def|"));
+}
+
+TEST_F(TextBoundariesTest, ForwardBasic) {
+ EXPECT_EQ("^abc| def", TryFindWordForward("|abc def"));
+ EXPECT_EQ("^abc| def", TryFindWordForward("a|bc def"));
+ EXPECT_EQ("^abc| def", TryFindWordForward("ab|c def"));
+ EXPECT_EQ("abc^ |def", TryFindWordForward("abc| def"));
+ EXPECT_EQ("abc ^def|", TryFindWordForward("abc |def"));
+ EXPECT_EQ("abc ^def|", TryFindWordForward("abc d|ef"));
+ EXPECT_EQ("abc ^def|", TryFindWordForward("abc de|f"));
+ EXPECT_EQ("abc def^|", TryFindWordForward("abc def|"));
+}
+
+TEST_F(TextBoundariesTest, ForwardBiDi) {
+ EXPECT_EQ(u8"^\u0620\u0621\u0622| \u0623\u0624\u0625",
+ TryFindWordForward(u8"|\u0620\u0621\u0622 \u0623\u0624\u0625"));
+ EXPECT_EQ(u8"^\u0620\u0621\u0622| \u0623\u0624\u0625",
+ TryFindWordForward(u8"\u0620|\u0621\u0622 \u0623\u0624\u0625"));
+ EXPECT_EQ(u8"^\u0620\u0621\u0622| \u0623\u0624\u0625",
+ TryFindWordForward(u8"\u0620\u0621|\u0622 \u0623\u0624\u0625"));
+ EXPECT_EQ(u8"\u0620\u0621\u0622^ |\u0623\u0624\u0625",
+ TryFindWordForward(u8"\u0620\u0621\u0622| \u0623\u0624\u0625"));
+ EXPECT_EQ(u8"\u0620\u0621\u0622 ^\u0623\u0624\u0625|",
+ TryFindWordForward(u8"\u0620\u0621\u0622 |\u0623\u0624\u0625"));
+ EXPECT_EQ(u8"\u0620\u0621\u0622 \u0623\u0624\u0625^|",
+ TryFindWordForward(u8"\u0620\u0621\u0622 \u0623\u0624\u0625|"));
+}
+
+TEST_F(TextBoundariesTest, ForwardBiDiMixed) {
+ EXPECT_EQ(u8"^abc\u0620\u0621\u0622|",
+ TryFindWordForward(u8"|abc\u0620\u0621\u0622"));
+ EXPECT_EQ(u8"^abc\u0620\u0621\u0622|",
+ TryFindWordForward(u8"ab|c\u0620\u0621\u0622"));
+ EXPECT_EQ(u8"^abc\u0620\u0621\u0622|",
+ TryFindWordForward(u8"abc|\u0620\u0621\u0622"))
+ << "At L1/L2 boundary";
+ EXPECT_EQ(u8"^abc\u0620\u0621\u0622|",
+ TryFindWordForward(u8"abc\u0620|\u0621\u0622"));
+
+ EXPECT_EQ(u8"^\u0620\u0621\u0622xyz|",
+ TryFindWordForward(u8"|\u0620\u0621\u0622xyz"));
+ EXPECT_EQ(u8"^\u0620\u0621\u0622xyz|",
+ TryFindWordForward(u8"\u0620|\u0621\u0622xyz"));
+ EXPECT_EQ(u8"^\u0620\u0621\u0622xyz|",
+ TryFindWordForward(u8"\u0620\u0621\u0622|xyz"))
+ << "At L2/L1 boundary";
+ EXPECT_EQ(u8"^\u0620\u0621\u0622xyz|",
+ TryFindWordForward(u8"\u0620\u0621\u0622xy|z"));
+}
+
+TEST_F(TextBoundariesTest, ForwardOne) {
+ EXPECT_EQ("^a|", TryFindWordForward("|a"));
+ EXPECT_EQ("a^|", TryFindWordForward("a|")) << "No word after |";
+}
+
+TEST_F(TextBoundariesTest, ForwardParenthesis) {
+ EXPECT_EQ("^(|abc)", TryFindWordForward("|(abc)"));
+ EXPECT_EQ("(^abc|)", TryFindWordForward("(|abc)"));
+ EXPECT_EQ("(^abc|)", TryFindWordForward("(a|bc)"));
+ EXPECT_EQ("(^abc|)", TryFindWordForward("(ab|c)"));
+ EXPECT_EQ("(abc)^|", TryFindWordForward("(abc)|")) << "No word after |";
+}
+
+TEST_F(TextBoundariesTest, ForwardPunctuations) {
+ EXPECT_EQ("^abc|,,", TryFindWordForward("|abc,,"));
+ EXPECT_EQ("abc^,|,", TryFindWordForward("abc|,,"));
+}
+
+TEST_F(TextBoundariesTest, ForwardWhitespaces) {
+ EXPECT_EQ("^ | abc def ", TryFindWordForward("| abc def "));
+ EXPECT_EQ(" ^ |abc def ", TryFindWordForward(" | abc def "));
+ EXPECT_EQ(" ^abc| def ", TryFindWordForward(" |abc def "));
+ EXPECT_EQ(" ^abc| def ", TryFindWordForward(" a|bc def "));
+ EXPECT_EQ(" ^abc| def ", TryFindWordForward(" ab|c def "));
+ EXPECT_EQ(" abc^ | def ", TryFindWordForward(" abc| def "));
+ EXPECT_EQ(" abc ^ |def ", TryFindWordForward(" abc | def "));
+ EXPECT_EQ(" abc ^def| ", TryFindWordForward(" abc |def "));
+ EXPECT_EQ(" abc ^def| ", TryFindWordForward(" abc d|ef "));
+ EXPECT_EQ(" abc ^def| ", TryFindWordForward(" abc de|f "));
+ EXPECT_EQ(" abc def^ | ", TryFindWordForward(" abc def| "));
+ EXPECT_EQ(" abc def ^ |", TryFindWordForward(" abc def | "));
+ EXPECT_EQ(" abc def ^|", TryFindWordForward(" abc def |"))
+ << "No word after |";
+}
+
+TEST_F(TextBoundariesTest, ForwardZero) {
+ EXPECT_EQ("^|", TryFindWordForward("|"));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/text_break_iterator.cc b/chromium/third_party/blink/renderer/platform/text/text_break_iterator.cc
new file mode 100644
index 00000000000..8b3625cfeec
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/text_break_iterator.cc
@@ -0,0 +1,452 @@
+/*
+ * (C) 1999 Lars Knoll (knoll@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2010 Apple Inc. All rights
+ * reserved.
+ * Copyright (C) 2007-2009 Torch Mobile, Inc.
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "third_party/blink/renderer/platform/text/text_break_iterator.h"
+
+#include "third_party/blink/renderer/platform/text/character.h"
+#include "third_party/blink/renderer/platform/wtf/ascii_ctype.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
+
+#include <unicode/uchar.h>
+#include <unicode/uvernum.h>
+
+namespace blink {
+
+unsigned NumGraphemeClusters(const String& string) {
+ unsigned string_length = string.length();
+
+ if (!string_length)
+ return 0;
+
+ // The only Latin-1 Extended Grapheme Cluster is CR LF
+ if (string.Is8Bit() && !string.Contains('\r'))
+ return string_length;
+
+ NonSharedCharacterBreakIterator it(string);
+ if (!it)
+ return string_length;
+
+ unsigned num = 0;
+ while (it.Next() != kTextBreakDone)
+ ++num;
+ return num;
+}
+
+unsigned LengthOfGraphemeCluster(const String& string, unsigned offset) {
+ unsigned string_length = string.length();
+
+ if (string_length - offset <= 1)
+ return string_length - offset;
+
+ // The only Latin-1 Extended Grapheme Cluster is CRLF.
+ if (string.Is8Bit()) {
+ auto* characters = string.Characters8();
+ return 1 + (characters[offset] == '\r' && characters[offset + 1] == '\n');
+ }
+
+ NonSharedCharacterBreakIterator it(string);
+ if (!it)
+ return string_length - offset;
+
+ if (it.Following(offset) == kTextBreakDone)
+ return string_length - offset;
+ return it.Current() - offset;
+}
+
+static const UChar kAsciiLineBreakTableFirstChar = '!';
+static const UChar kAsciiLineBreakTableLastChar = 127;
+
+// Pack 8 bits into one byte
+#define B(a, b, c, d, e, f, g, h) \
+ ((a) | ((b) << 1) | ((c) << 2) | ((d) << 3) | ((e) << 4) | ((f) << 5) | \
+ ((g) << 6) | ((h) << 7))
+
+// Line breaking table row for each digit (0-9)
+#define DI \
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+
+// Line breaking table row for ascii letters (a-z A-Z)
+#define AL \
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+
+#define F 0xFF
+
+// Line breaking table for printable ASCII characters. Line breaking
+// opportunities in this table are as below:
+// - before opening punctuations such as '(', '<', '[', '{' after certain
+// characters (compatible with Firefox 3.6);
+// - after '-' and '?' (backward-compatible, and compatible with Internet
+// Explorer).
+// Please refer to <https://bugs.webkit.org/show_bug.cgi?id=37698> for line
+// breaking matrixes of different browsers and the ICU standard.
+// clang-format off
+static const unsigned char kAsciiLineBreakTable[][(kAsciiLineBreakTableLastChar - kAsciiLineBreakTableFirstChar) / 8 + 1] = {
+ // ! " # $ % & ' ( ) * + , - . / 0 1-8 9 : ; < = > ? @ A-X Y Z [ \ ] ^ _ ` a-x y z { | } ~ DEL
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // !
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // "
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // #
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0) }, // $
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // %
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // &
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0) }, // '
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0) }, // (
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // )
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // *
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // +
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // ,
+ { B(0, 1, 1, 0, 1, 1, 1, 1), B(0, 1, 1, 0, 1, 0, 0, 0), 0, B(0, 0, 0, 1, 1, 1, 0, 1), F, F, F, B(1, 1, 1, 1, 0, 1, 1, 1), F, F, F, B(1, 1, 1, 1, 0, 1, 1, 1) }, // - Note: breaking before '0'-'9' is handled hard-coded in shouldBreakAfter().
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // .
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0) }, // /
+ DI, DI, DI, DI, DI, DI, DI, DI, DI, DI, // 0-9
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // :
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // ;
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0) }, // <
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // =
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // >
+ { B(0, 0, 1, 1, 1, 1, 0, 1), B(0, 1, 1, 0, 1, 0, 0, 1), F, B(1, 0, 0, 1, 1, 1, 0, 1), F, F, F, B(1, 1, 1, 1, 0, 1, 1, 1), F, F, F, B(1, 1, 1, 1, 0, 1, 1, 0) }, // ?
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0) }, // @
+ AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, // A-Z
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0) }, // [
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // '\'
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // ]
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0) }, // ^
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0) }, // _
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0) }, // `
+ AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, // a-z
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0) }, // {
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // |
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // }
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // ~
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0) }, // DEL
+};
+// clang-format on
+
+#if U_ICU_VERSION_MAJOR_NUM >= 58
+#define BA_LB_COUNT (U_LB_COUNT - 3)
+#else
+#define BA_LB_COUNT U_LB_COUNT
+#endif
+// Line breaking table for CSS word-break: break-all. This table differs from
+// asciiLineBreakTable in:
+// - Indices are Line Breaking Classes defined in UAX#14 Unicode Line Breaking
+// Algorithm: http://unicode.org/reports/tr14/#DescriptionOfProperties
+// - 1 indicates additional break opportunities. 0 indicates to fallback to
+// normal line break, not "prohibit break."
+// clang-format off
+static const unsigned char kBreakAllLineBreakClassTable[][BA_LB_COUNT / 8 + 1] = {
+ // XX AI AL B2 BA BB BK CB CL CM CR EX GL HY ID IN IS LF NS NU OP PO PR QU SA SG SP SY ZW NL WJ H2 H3 JL JT JV CP CJ HL RI
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0) }, // XX
+ { B(0, 1, 1, 0, 1, 0, 0, 0), B(0, 0, 0, 0, 0, 1, 0, 0), B(0, 0, 0, 1, 1, 0, 1, 0), B(1, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 1, 0) }, // AI
+ { B(0, 1, 1, 0, 1, 0, 0, 0), B(0, 0, 0, 0, 0, 1, 0, 0), B(0, 0, 0, 1, 1, 0, 1, 0), B(1, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 1, 0) }, // AL
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0) }, // B2
+ { B(0, 1, 1, 0, 1, 0, 0, 0), B(0, 0, 0, 0, 0, 1, 0, 0), B(0, 0, 0, 1, 1, 0, 1, 0), B(1, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 1, 0) }, // BA
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0) }, // BB
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0) }, // BK
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0) }, // CB
+ { B(0, 1, 1, 0, 1, 0, 0, 0), B(0, 0, 0, 0, 0, 1, 0, 0), B(0, 0, 0, 1, 0, 0, 1, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 1, 0) }, // CL
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0) }, // CM
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0) }, // CR
+ { B(0, 1, 1, 0, 1, 0, 0, 0), B(0, 0, 0, 0, 0, 1, 0, 0), B(0, 0, 0, 1, 0, 1, 1, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 1, 0) }, // EX
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0) }, // GL
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 1, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0) }, // HY
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0) }, // ID
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0) }, // IN
+ { B(0, 1, 1, 0, 1, 0, 0, 0), B(0, 0, 0, 0, 0, 1, 0, 0), B(0, 0, 0, 1, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 1, 0) }, // IS
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0) }, // LF
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0) }, // NS
+ { B(0, 1, 1, 0, 1, 0, 0, 0), B(0, 0, 0, 0, 0, 1, 0, 0), B(0, 0, 0, 1, 1, 0, 1, 0), B(1, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 1, 0) }, // NU
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0) }, // OP
+ { B(0, 1, 1, 0, 1, 0, 0, 0), B(0, 0, 0, 0, 0, 1, 0, 0), B(0, 0, 0, 1, 0, 1, 1, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 1, 0) }, // PO
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 1, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0) }, // PR
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0) }, // QU
+ { B(0, 1, 1, 0, 1, 0, 0, 0), B(0, 0, 0, 0, 0, 1, 0, 0), B(0, 0, 0, 1, 1, 0, 1, 0), B(1, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 1, 0) }, // SA
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0) }, // SG
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0) }, // SP
+ { B(0, 1, 1, 0, 1, 0, 0, 0), B(0, 0, 0, 0, 0, 1, 0, 0), B(0, 0, 0, 1, 1, 0, 1, 0), B(1, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 1, 0) }, // SY
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0) }, // ZW
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0) }, // NL
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0) }, // WJ
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0) }, // H2
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0) }, // H3
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0) }, // JL
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0) }, // JT
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0) }, // JV
+ { B(0, 1, 1, 0, 1, 0, 0, 0), B(0, 0, 0, 0, 0, 1, 0, 0), B(0, 0, 0, 1, 0, 0, 1, 0), B(1, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 1, 0) }, // CP
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0) }, // CJ
+ { B(0, 1, 1, 0, 1, 0, 0, 0), B(0, 0, 0, 0, 0, 1, 0, 0), B(0, 0, 0, 1, 1, 0, 1, 0), B(1, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 1, 0) }, // HL
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0) }, // RI
+};
+// clang-format on
+
+#undef B
+#undef F
+#undef DI
+#undef AL
+
+static_assert(WTF_ARRAY_LENGTH(kAsciiLineBreakTable) ==
+ kAsciiLineBreakTableLastChar - kAsciiLineBreakTableFirstChar +
+ 1,
+ "asciiLineBreakTable should be consistent");
+static_assert(WTF_ARRAY_LENGTH(kBreakAllLineBreakClassTable) == BA_LB_COUNT,
+ "breakAllLineBreakClassTable should be consistent");
+
+static inline bool ShouldBreakAfter(UChar last_ch, UChar ch, UChar next_ch) {
+ // Don't allow line breaking between '-' and a digit if the '-' may mean a
+ // minus sign in the context, while allow breaking in 'ABCD-1234' and
+ // '1234-5678' which may be in long URLs.
+ if (ch == '-' && IsASCIIDigit(next_ch))
+ return IsASCIIAlphanumeric(last_ch);
+
+ // If both ch and nextCh are ASCII characters, use a lookup table for enhanced
+ // speed and for compatibility with other browsers (see comments for
+ // asciiLineBreakTable for details).
+ if (ch >= kAsciiLineBreakTableFirstChar &&
+ ch <= kAsciiLineBreakTableLastChar &&
+ next_ch >= kAsciiLineBreakTableFirstChar &&
+ next_ch <= kAsciiLineBreakTableLastChar) {
+ const unsigned char* table_row =
+ kAsciiLineBreakTable[ch - kAsciiLineBreakTableFirstChar];
+ int next_ch_index = next_ch - kAsciiLineBreakTableFirstChar;
+ return table_row[next_ch_index / 8] & (1 << (next_ch_index % 8));
+ }
+ // Otherwise defer to the Unicode algorithm by returning false.
+ return false;
+}
+
+static inline ULineBreak LineBreakPropertyValue(UChar last_ch, UChar ch) {
+ if (ch == '+') // IE tailors '+' to AL-like class when break-all is enabled.
+ return U_LB_ALPHABETIC;
+ UChar32 ch32 = U16_IS_LEAD(last_ch) && U16_IS_TRAIL(ch)
+ ? U16_GET_SUPPLEMENTARY(last_ch, ch)
+ : ch;
+ return static_cast<ULineBreak>(u_getIntPropertyValue(ch32, UCHAR_LINE_BREAK));
+}
+
+static inline bool ShouldBreakAfterBreakAll(ULineBreak last_line_break,
+ ULineBreak line_break) {
+ if (line_break >= 0 && line_break < BA_LB_COUNT && last_line_break >= 0 &&
+ last_line_break < BA_LB_COUNT) {
+ const unsigned char* table_row =
+ kBreakAllLineBreakClassTable[last_line_break];
+ return table_row[line_break / 8] & (1 << (line_break % 8));
+ }
+ return false;
+}
+
+// Computes if 'word-break:keep-all' should prevent line break.
+// https://drafts.csswg.org/css-text-3/#valdef-word-break-keep-all
+// The spec is not very verbose on how this should work. This logic prevents L/M
+// general categories and complex line breaking since the spec says "except some
+// south east aisans".
+// https://github.com/w3c/csswg-drafts/issues/1619
+static inline bool ShouldKeepAfterKeepAll(UChar last_ch,
+ UChar ch,
+ UChar next_ch) {
+ UChar pre_ch = U_MASK(u_charType(ch)) & U_GC_M_MASK ? last_ch : ch;
+ return U_MASK(u_charType(pre_ch)) & (U_GC_L_MASK | U_GC_N_MASK) &&
+ !WTF::Unicode::HasLineBreakingPropertyComplexContext(pre_ch) &&
+ U_MASK(u_charType(next_ch)) & (U_GC_L_MASK | U_GC_N_MASK) &&
+ !WTF::Unicode::HasLineBreakingPropertyComplexContext(next_ch);
+}
+
+inline bool NeedsLineBreakIterator(UChar ch) {
+ return ch > kAsciiLineBreakTableLastChar && ch != kNoBreakSpaceCharacter;
+}
+
+template <typename CharacterType,
+ LineBreakType lineBreakType,
+ BreakSpaceType break_space>
+inline int LazyLineBreakIterator::NextBreakablePosition(
+ int pos,
+ const CharacterType* str) const {
+ int len = static_cast<int>(string_.length());
+ int next_break = -1;
+
+ UChar last_last_ch = pos > 1 ? str[pos - 2] : SecondToLastCharacter();
+ UChar last_ch = pos > 0 ? str[pos - 1] : LastCharacter();
+ bool is_last_space = IsBreakableSpace(last_ch);
+ ULineBreak last_line_break;
+ if (lineBreakType == LineBreakType::kBreakAll)
+ last_line_break = LineBreakPropertyValue(last_last_ch, last_ch);
+ unsigned prior_context_length = PriorContextLength();
+ CharacterType ch;
+ bool is_space;
+ for (int i = pos; i < len;
+ i++, last_last_ch = last_ch, last_ch = ch, is_last_space = is_space) {
+ ch = str[i];
+
+ is_space = IsBreakableSpace(ch);
+ switch (break_space) {
+ case BreakSpaceType::kBeforeEverySpace:
+ if (is_space)
+ return i;
+ break;
+ case BreakSpaceType::kBeforeSpaceRun:
+ // Theoritically, preserved newline characters are different from space
+ // and tab characters. The difference is not implemented because the
+ // LayoutNG line breaker handles preserved newline characters by itself.
+ if (is_space) {
+ if (!is_last_space)
+ return i;
+ continue;
+ }
+ break;
+ }
+
+ if (ShouldBreakAfter(last_last_ch, last_ch, ch))
+ return i;
+
+ if (lineBreakType == LineBreakType::kBreakAll && !U16_IS_LEAD(ch)) {
+ ULineBreak line_break = LineBreakPropertyValue(last_ch, ch);
+ if (ShouldBreakAfterBreakAll(last_line_break, line_break))
+ return i > pos && U16_IS_TRAIL(ch) ? i - 1 : i;
+ if (line_break != U_LB_COMBINING_MARK)
+ last_line_break = line_break;
+ }
+
+ if (lineBreakType == LineBreakType::kKeepAll &&
+ ShouldKeepAfterKeepAll(last_last_ch, last_ch, ch)) {
+ // word-break:keep-all prevents breaks between East Asian ideographic.
+ continue;
+ }
+
+ if (NeedsLineBreakIterator(ch) || NeedsLineBreakIterator(last_ch)) {
+ if (next_break < i) {
+ // Don't break if positioned at start of primary context and there is no
+ // prior context.
+ if (i || prior_context_length) {
+ TextBreakIterator* break_iterator = Get(prior_context_length);
+ if (break_iterator) {
+ next_break =
+ break_iterator->following(i - 1 + prior_context_length);
+ if (next_break >= 0) {
+ next_break -= prior_context_length;
+ }
+ }
+ }
+ }
+ if (i == next_break && !is_last_space)
+ return i;
+ }
+ }
+
+ return len;
+}
+
+template <typename CharacterType, LineBreakType lineBreakType>
+inline int LazyLineBreakIterator::NextBreakablePosition(
+ int pos,
+ const CharacterType* str) const {
+ switch (break_space_) {
+ case BreakSpaceType::kBeforeEverySpace:
+ return NextBreakablePosition<CharacterType, lineBreakType,
+ BreakSpaceType::kBeforeEverySpace>(pos, str);
+ case BreakSpaceType::kBeforeSpaceRun:
+ return NextBreakablePosition<CharacterType, lineBreakType,
+ BreakSpaceType::kBeforeSpaceRun>(pos, str);
+ }
+ NOTREACHED();
+ return NextBreakablePosition<CharacterType, lineBreakType,
+ BreakSpaceType::kBeforeEverySpace>(pos, str);
+}
+
+template <LineBreakType lineBreakType>
+inline int LazyLineBreakIterator::NextBreakablePosition(int pos) const {
+ if (UNLIKELY(string_.IsNull()))
+ return 0;
+ if (string_.Is8Bit()) {
+ return NextBreakablePosition<LChar, lineBreakType>(pos,
+ string_.Characters8());
+ }
+ return NextBreakablePosition<UChar, lineBreakType>(pos,
+ string_.Characters16());
+}
+
+int LazyLineBreakIterator::NextBreakablePositionBreakCharacter(int pos) const {
+ NonSharedCharacterBreakIterator iterator(string_);
+ int next = iterator.Following(std::max(pos - 1, 0));
+ return next != kTextBreakDone ? next : string_.length();
+}
+
+int LazyLineBreakIterator::NextBreakablePosition(
+ int pos,
+ LineBreakType line_break_type) const {
+ switch (line_break_type) {
+ case LineBreakType::kNormal:
+ return NextBreakablePosition<LineBreakType::kNormal>(pos);
+ case LineBreakType::kBreakAll:
+ return NextBreakablePosition<LineBreakType::kBreakAll>(pos);
+ case LineBreakType::kKeepAll:
+ return NextBreakablePosition<LineBreakType::kKeepAll>(pos);
+ case LineBreakType::kBreakCharacter:
+ return NextBreakablePositionBreakCharacter(pos);
+ }
+ NOTREACHED();
+ return NextBreakablePosition(pos, LineBreakType::kNormal);
+}
+
+unsigned LazyLineBreakIterator::NextBreakOpportunity(unsigned offset) const {
+ int next_break = -1;
+ IsBreakable(offset, next_break);
+ DCHECK_GE(next_break, 0);
+ return next_break;
+}
+
+unsigned LazyLineBreakIterator::PreviousBreakOpportunity(unsigned offset,
+ unsigned min) const {
+ unsigned pos = std::min(offset, string_.length());
+ for (; pos > min; pos--) {
+ if (IsBreakable(pos))
+ return pos;
+ }
+ return min;
+}
+
+std::ostream& operator<<(std::ostream& ostream, LineBreakType line_break_type) {
+ switch (line_break_type) {
+ case LineBreakType::kNormal:
+ return ostream << "Normal";
+ case LineBreakType::kBreakAll:
+ return ostream << "BreakAll";
+ case LineBreakType::kBreakCharacter:
+ return ostream << "BreakCharacter";
+ case LineBreakType::kKeepAll:
+ return ostream << "KeepAll";
+ }
+ NOTREACHED();
+ return ostream << "LineBreakType::" << static_cast<int>(line_break_type);
+}
+
+std::ostream& operator<<(std::ostream& ostream, BreakSpaceType break_space) {
+ switch (break_space) {
+ case BreakSpaceType::kBeforeEverySpace:
+ return ostream << "kBeforeEverySpace";
+ case BreakSpaceType::kBeforeSpaceRun:
+ return ostream << "kBeforeSpaceRun";
+ }
+ NOTREACHED();
+ return ostream << "BreakSpaceType::" << static_cast<int>(break_space);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/text_break_iterator.h b/chromium/third_party/blink/renderer/platform/text/text_break_iterator.h
new file mode 100644
index 00000000000..d5bff169749
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/text_break_iterator.h
@@ -0,0 +1,367 @@
+/*
+ * Copyright (C) 2006 Lars Knoll <lars@trolltech.com>
+ * Copyright (C) 2007, 2011, 2012 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_TEXT_BREAK_ITERATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_TEXT_BREAK_ITERATOR_H_
+
+#include "base/macros.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
+#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
+
+#include <unicode/brkiter.h>
+
+namespace blink {
+
+typedef icu::BreakIterator TextBreakIterator;
+
+// Note: The returned iterator is good only until you get another iterator, with
+// the exception of acquireLineBreakIterator.
+
+// This is similar to character break iterator in most cases, but is subject to
+// platform UI conventions. One notable example where this can be different
+// from character break iterator is Thai prepend characters, see bug 24342.
+// Use this for insertion point and selection manipulations.
+PLATFORM_EXPORT TextBreakIterator* CursorMovementIterator(const UChar*,
+ int length);
+
+PLATFORM_EXPORT TextBreakIterator* WordBreakIterator(const String&,
+ int start,
+ int length);
+PLATFORM_EXPORT TextBreakIterator* WordBreakIterator(const UChar*, int length);
+PLATFORM_EXPORT TextBreakIterator* AcquireLineBreakIterator(
+ const LChar*,
+ int length,
+ const AtomicString& locale,
+ const UChar* prior_context,
+ unsigned prior_context_length);
+PLATFORM_EXPORT TextBreakIterator* AcquireLineBreakIterator(
+ const UChar*,
+ int length,
+ const AtomicString& locale,
+ const UChar* prior_context,
+ unsigned prior_context_length);
+PLATFORM_EXPORT void ReleaseLineBreakIterator(TextBreakIterator*);
+PLATFORM_EXPORT TextBreakIterator* SentenceBreakIterator(const UChar*,
+ int length);
+
+// Before calling this, check if the iterator is not at the end. Otherwise,
+// it may not work as expected.
+// See https://ssl.icu-project.org/trac/ticket/13447 .
+PLATFORM_EXPORT bool IsWordTextBreak(TextBreakIterator*);
+
+const int kTextBreakDone = -1;
+
+enum class LineBreakType {
+ kNormal,
+
+ // word-break:break-all allows breaks between letters/numbers, but prohibits
+ // break before/after certain punctuation.
+ kBreakAll,
+
+ // Allows breaks at every grapheme cluster boundary.
+ // Terminal style line breaks described in UAX#14: Examples of Customization
+ // http://unicode.org/reports/tr14/#Examples
+ // CSS is discussing to add this feature crbug.com/720205
+ // Used internally for word-break:break-word.
+ kBreakCharacter,
+
+ // word-break:keep-all doesn't allow breaks between all kind of
+ // letters/numbers except some south east asians'.
+ kKeepAll,
+};
+
+// Determines break opportunities around collapsible space characters (space,
+// newline, and tabulation characters.)
+enum class BreakSpaceType {
+ // Break before every collapsible space character.
+ // This is a specialized optimization for CSS, where leading/trailing spaces
+ // in each line are removed, and thus breaking before spaces can save
+ // computing hanging spaces.
+ // Callers are expected to handle spaces by themselves. Because a run of
+ // spaces can include different types of spaces, break opportunity is given
+ // for every space character.
+ // Pre-LayoutNG line breaker uses this type.
+ kBeforeEverySpace,
+
+ // Break before a run of white space characters.
+ // This is for CSS line breaking as in |kBeforeEverySpace|, but when
+ // whitespace collapsing is already applied to the target string. In this
+ // case, a run of white spaces are preserved spaces. There should not be break
+ // opportunities between white spaces.
+ // LayoutNG line breaker uses this type.
+ kBeforeSpaceRun,
+};
+
+PLATFORM_EXPORT std::ostream& operator<<(std::ostream&, LineBreakType);
+PLATFORM_EXPORT std::ostream& operator<<(std::ostream&, BreakSpaceType);
+
+class PLATFORM_EXPORT LazyLineBreakIterator final {
+ STACK_ALLOCATED();
+
+ public:
+ LazyLineBreakIterator()
+ : iterator_(nullptr),
+ cached_prior_context_(nullptr),
+ cached_prior_context_length_(0),
+ break_type_(LineBreakType::kNormal) {
+ ResetPriorContext();
+ }
+
+ LazyLineBreakIterator(String string,
+ const AtomicString& locale = AtomicString(),
+ LineBreakType break_type = LineBreakType::kNormal)
+ : string_(string),
+ locale_(locale),
+ iterator_(nullptr),
+ cached_prior_context_(nullptr),
+ cached_prior_context_length_(0),
+ break_type_(break_type) {
+ ResetPriorContext();
+ }
+
+ ~LazyLineBreakIterator() {
+ if (iterator_)
+ ReleaseLineBreakIterator(iterator_);
+ }
+
+ const String& GetString() const { return string_; }
+
+ UChar LastCharacter() const {
+ static_assert(WTF_ARRAY_LENGTH(prior_context_) == 2,
+ "TextBreakIterator has unexpected prior context length");
+ return prior_context_[1];
+ }
+
+ UChar SecondToLastCharacter() const {
+ static_assert(WTF_ARRAY_LENGTH(prior_context_) == 2,
+ "TextBreakIterator has unexpected prior context length");
+ return prior_context_[0];
+ }
+
+ void SetPriorContext(UChar last, UChar second_to_last) {
+ static_assert(WTF_ARRAY_LENGTH(prior_context_) == 2,
+ "TextBreakIterator has unexpected prior context length");
+ prior_context_[0] = second_to_last;
+ prior_context_[1] = last;
+ }
+
+ void UpdatePriorContext(UChar last) {
+ static_assert(WTF_ARRAY_LENGTH(prior_context_) == 2,
+ "TextBreakIterator has unexpected prior context length");
+ prior_context_[0] = prior_context_[1];
+ prior_context_[1] = last;
+ }
+
+ void ResetPriorContext() {
+ static_assert(WTF_ARRAY_LENGTH(prior_context_) == 2,
+ "TextBreakIterator has unexpected prior context length");
+ prior_context_[0] = 0;
+ prior_context_[1] = 0;
+ }
+
+ unsigned PriorContextLength() const {
+ unsigned prior_context_length = 0;
+ static_assert(WTF_ARRAY_LENGTH(prior_context_) == 2,
+ "TextBreakIterator has unexpected prior context length");
+ if (prior_context_[1]) {
+ ++prior_context_length;
+ if (prior_context_[0])
+ ++prior_context_length;
+ }
+ return prior_context_length;
+ }
+
+ // Obtain text break iterator, possibly previously cached, where this iterator
+ // is (or has been) initialized to use the previously stored string as the
+ // primary breaking context and using previously stored prior context if
+ // non-empty.
+ TextBreakIterator* Get(unsigned prior_context_length) const {
+ DCHECK(prior_context_length <= kPriorContextCapacity);
+ const UChar* prior_context =
+ prior_context_length
+ ? &prior_context_[kPriorContextCapacity - prior_context_length]
+ : nullptr;
+ if (!iterator_) {
+ if (string_.Is8Bit())
+ iterator_ = AcquireLineBreakIterator(
+ string_.Characters8(), string_.length(), locale_, prior_context,
+ prior_context_length);
+ else
+ iterator_ = AcquireLineBreakIterator(
+ string_.Characters16(), string_.length(), locale_, prior_context,
+ prior_context_length);
+ cached_prior_context_ = prior_context;
+ cached_prior_context_length_ = prior_context_length;
+ } else if (prior_context != cached_prior_context_ ||
+ prior_context_length != cached_prior_context_length_) {
+ ReleaseIterator();
+ return Get(prior_context_length);
+ }
+ return iterator_;
+ }
+
+ void ResetStringAndReleaseIterator(String string,
+ const AtomicString& locale) {
+ string_ = string;
+ locale_ = locale;
+
+ ReleaseIterator();
+ }
+
+ void SetLocale(const AtomicString& locale) {
+ if (locale == locale_)
+ return;
+ locale_ = locale;
+ ReleaseIterator();
+ }
+
+ LineBreakType BreakType() const { return break_type_; }
+ void SetBreakType(LineBreakType break_type) { break_type_ = break_type; }
+ BreakSpaceType BreakSpace() const { return break_space_; }
+ void SetBreakSpace(BreakSpaceType break_space) { break_space_ = break_space; }
+
+ inline bool IsBreakable(int pos,
+ int& next_breakable,
+ LineBreakType line_break_type) const {
+ if (pos > next_breakable) {
+ next_breakable = NextBreakablePosition(pos, line_break_type);
+ }
+ return pos == next_breakable;
+ }
+
+ inline bool IsBreakable(int pos, int& next_breakable) const {
+ return IsBreakable(pos, next_breakable, break_type_);
+ }
+
+ inline bool IsBreakable(int pos) const {
+ int next_breakable = -1;
+ return IsBreakable(pos, next_breakable, break_type_);
+ }
+
+ // Returns the break opportunity at or after |offset|.
+ unsigned NextBreakOpportunity(unsigned offset) const;
+
+ // Returns the break opportunity at or before |offset|.
+ unsigned PreviousBreakOpportunity(unsigned offset, unsigned min = 0) const;
+
+ static bool IsBreakableSpace(UChar ch) {
+ return ch == kSpaceCharacter || ch == kTabulationCharacter ||
+ ch == kNewlineCharacter;
+ }
+
+ private:
+ void ReleaseIterator() const {
+ if (iterator_)
+ ReleaseLineBreakIterator(iterator_);
+ iterator_ = nullptr;
+ cached_prior_context_ = nullptr;
+ cached_prior_context_length_ = 0;
+ }
+
+ template <typename CharacterType, LineBreakType, BreakSpaceType>
+ int NextBreakablePosition(int pos, const CharacterType* str) const;
+ template <typename CharacterType, LineBreakType>
+ int NextBreakablePosition(int pos, const CharacterType* str) const;
+ template <LineBreakType>
+ int NextBreakablePosition(int pos) const;
+ int NextBreakablePositionBreakCharacter(int pos) const;
+ int NextBreakablePosition(int pos, LineBreakType) const;
+
+ static const unsigned kPriorContextCapacity = 2;
+ String string_;
+ AtomicString locale_;
+ mutable TextBreakIterator* iterator_;
+ UChar prior_context_[kPriorContextCapacity];
+ mutable const UChar* cached_prior_context_;
+ mutable unsigned cached_prior_context_length_;
+ LineBreakType break_type_;
+ BreakSpaceType break_space_ = BreakSpaceType::kBeforeEverySpace;
+};
+
+// Iterates over "extended grapheme clusters", as defined in UAX #29.
+// Note that platform implementations may be less sophisticated - e.g. ICU prior
+// to version 4.0 only supports "legacy grapheme clusters". Use this for
+// general text processing, e.g. string truncation.
+
+class PLATFORM_EXPORT NonSharedCharacterBreakIterator final {
+ STACK_ALLOCATED();
+
+ public:
+ explicit NonSharedCharacterBreakIterator(const String&);
+ NonSharedCharacterBreakIterator(const UChar*, unsigned length);
+ ~NonSharedCharacterBreakIterator();
+
+ int Next();
+ int Current();
+
+ bool IsBreak(int offset) const;
+ int Preceding(int offset) const;
+ int Following(int offset) const;
+
+ bool operator!() const { return !is8_bit_ && !iterator_; }
+
+ private:
+ void CreateIteratorForBuffer(const UChar*, unsigned length);
+
+ unsigned ClusterLengthStartingAt(unsigned offset) const {
+ DCHECK(is8_bit_);
+ // The only Latin-1 Extended Grapheme Cluster is CR LF
+ return IsCRBeforeLF(offset) ? 2 : 1;
+ }
+
+ bool IsCRBeforeLF(unsigned offset) const {
+ DCHECK(is8_bit_);
+ return charaters8_[offset] == '\r' && offset + 1 < length_ &&
+ charaters8_[offset + 1] == '\n';
+ }
+
+ bool IsLFAfterCR(unsigned offset) const {
+ DCHECK(is8_bit_);
+ return charaters8_[offset] == '\n' && offset >= 1 &&
+ charaters8_[offset - 1] == '\r';
+ }
+
+ bool is8_bit_;
+
+ // For 8 bit strings, we implement the iterator ourselves.
+ const LChar* charaters8_;
+ unsigned offset_;
+ unsigned length_;
+
+ // For 16 bit strings, we use a TextBreakIterator.
+ TextBreakIterator* iterator_;
+
+ DISALLOW_COPY_AND_ASSIGN(NonSharedCharacterBreakIterator);
+};
+
+// Counts the number of grapheme clusters. A surrogate pair or a sequence
+// of a non-combining character and following combining characters is
+// counted as 1 grapheme cluster.
+PLATFORM_EXPORT unsigned NumGraphemeClusters(const String&);
+
+// Returns the number of code units that the next grapheme cluster is made of.
+PLATFORM_EXPORT unsigned LengthOfGraphemeCluster(const String&, unsigned = 0);
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/text/text_break_iterator_icu.cc b/chromium/third_party/blink/renderer/platform/text/text_break_iterator_icu.cc
new file mode 100644
index 00000000000..824a51f0d46
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/text_break_iterator_icu.cc
@@ -0,0 +1,989 @@
+/*
+ * Copyright (C) 2006 Lars Knoll <lars@trolltech.com>
+ * Copyright (C) 2007, 2011, 2012 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "third_party/blink/renderer/platform/text/text_break_iterator.h"
+
+#include <unicode/rbbi.h>
+#include <unicode/ubrk.h>
+#include <algorithm>
+#include <limits>
+#include <memory>
+#include <utility>
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/platform/text/icu_error.h"
+#include "third_party/blink/renderer/platform/text/text_break_iterator_internal_icu.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/thread_specific.h"
+#include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
+
+namespace blink {
+
+class LineBreakIteratorPool final {
+ USING_FAST_MALLOC(LineBreakIteratorPool);
+
+ public:
+ static LineBreakIteratorPool& SharedPool() {
+ static WTF::ThreadSpecific<LineBreakIteratorPool>* pool =
+ new WTF::ThreadSpecific<LineBreakIteratorPool>;
+ return **pool;
+ }
+
+ static std::unique_ptr<LineBreakIteratorPool> Create() {
+ return base::WrapUnique(new LineBreakIteratorPool);
+ }
+
+ icu::BreakIterator* Take(const AtomicString& locale) {
+ icu::BreakIterator* iterator = nullptr;
+ for (size_t i = 0; i < pool_.size(); ++i) {
+ if (pool_[i].first == locale) {
+ iterator = pool_[i].second;
+ pool_.EraseAt(i);
+ break;
+ }
+ }
+
+ if (!iterator) {
+ UErrorCode open_status = U_ZERO_ERROR;
+ bool locale_is_empty = locale.IsEmpty();
+ iterator = icu::BreakIterator::createLineInstance(
+ locale_is_empty ? icu::Locale(CurrentTextBreakLocaleID())
+ : icu::Locale(locale.Utf8().data()),
+ open_status);
+ // locale comes from a web page and it can be invalid, leading ICU
+ // to fail, in which case we fall back to the default locale.
+ if (!locale_is_empty && U_FAILURE(open_status)) {
+ open_status = U_ZERO_ERROR;
+ iterator = icu::BreakIterator::createLineInstance(
+ icu::Locale(CurrentTextBreakLocaleID()), open_status);
+ }
+
+ if (U_FAILURE(open_status)) {
+ DLOG(ERROR) << "icu::BreakIterator construction failed with status "
+ << open_status;
+ return nullptr;
+ }
+ }
+
+ DCHECK(!vended_iterators_.Contains(iterator));
+ vended_iterators_.Set(iterator, locale);
+ return iterator;
+ }
+
+ void Put(icu::BreakIterator* iterator) {
+ DCHECK(vended_iterators_.Contains(iterator));
+
+ if (pool_.size() == kCapacity) {
+ delete (pool_[0].second);
+ pool_.EraseAt(0);
+ }
+
+ pool_.push_back(Entry(vended_iterators_.Take(iterator), iterator));
+ }
+
+ private:
+ LineBreakIteratorPool() = default;
+
+ static const size_t kCapacity = 4;
+
+ typedef std::pair<AtomicString, icu::BreakIterator*> Entry;
+ typedef Vector<Entry, kCapacity> Pool;
+ Pool pool_;
+ HashMap<icu::BreakIterator*, AtomicString> vended_iterators_;
+
+ friend WTF::ThreadSpecific<LineBreakIteratorPool>::
+ operator LineBreakIteratorPool*();
+
+ DISALLOW_COPY_AND_ASSIGN(LineBreakIteratorPool);
+};
+
+enum TextContext { kNoContext, kPriorContext, kPrimaryContext };
+
+const int kTextBufferCapacity = 16;
+
+typedef struct {
+ DISALLOW_NEW();
+ UText text;
+ UChar buffer[kTextBufferCapacity];
+} UTextWithBuffer;
+
+static inline int64_t TextPinIndex(int64_t& index, int64_t limit) {
+ if (index < 0)
+ index = 0;
+ else if (index > limit)
+ index = limit;
+ return index;
+}
+
+static inline int64_t TextNativeLength(UText* text) {
+ return text->a + text->b;
+}
+
+// Relocate pointer from source into destination as required.
+static void TextFixPointer(const UText* source,
+ UText* destination,
+ const void*& pointer) {
+ if (pointer >= source->pExtra &&
+ pointer < static_cast<char*>(source->pExtra) + source->extraSize) {
+ // Pointer references source extra buffer.
+ pointer = static_cast<char*>(destination->pExtra) +
+ (static_cast<const char*>(pointer) -
+ static_cast<const char*>(source->pExtra));
+ } else if (pointer >= source &&
+ pointer <
+ reinterpret_cast<const char*>(source) + source->sizeOfStruct) {
+ // Pointer references source text structure, but not source extra buffer.
+ pointer = reinterpret_cast<char*>(destination) +
+ (static_cast<const char*>(pointer) -
+ reinterpret_cast<const char*>(source));
+ }
+}
+
+static UText* TextClone(UText* destination,
+ const UText* source,
+ UBool deep,
+ UErrorCode* status) {
+ DCHECK(!deep);
+ if (U_FAILURE(*status))
+ return nullptr;
+ int32_t extra_size = source->extraSize;
+ destination = utext_setup(destination, extra_size, status);
+ if (U_FAILURE(*status))
+ return destination;
+ void* extra_new = destination->pExtra;
+ int32_t flags = destination->flags;
+ int size_to_copy = std::min(source->sizeOfStruct, destination->sizeOfStruct);
+ memcpy(destination, source, size_to_copy);
+ destination->pExtra = extra_new;
+ destination->flags = flags;
+ memcpy(destination->pExtra, source->pExtra, extra_size);
+ TextFixPointer(source, destination, destination->context);
+ TextFixPointer(source, destination, destination->p);
+ TextFixPointer(source, destination, destination->q);
+ DCHECK(!destination->r);
+ const void* chunk_contents =
+ static_cast<const void*>(destination->chunkContents);
+ TextFixPointer(source, destination, chunk_contents);
+ destination->chunkContents = static_cast<const UChar*>(chunk_contents);
+ return destination;
+}
+
+static int32_t TextExtract(UText*,
+ int64_t,
+ int64_t,
+ UChar*,
+ int32_t,
+ UErrorCode* error_code) {
+ // In the present context, this text provider is used only with ICU functions
+ // that do not perform an extract operation.
+ NOTREACHED();
+ *error_code = U_UNSUPPORTED_ERROR;
+ return 0;
+}
+
+static void TextClose(UText* text) {
+ text->context = nullptr;
+}
+
+static inline TextContext TextGetContext(const UText* text,
+ int64_t native_index,
+ UBool forward) {
+ if (!text->b || native_index > text->b)
+ return kPrimaryContext;
+ if (native_index == text->b)
+ return forward ? kPrimaryContext : kPriorContext;
+ return kPriorContext;
+}
+
+static inline TextContext TextLatin1GetCurrentContext(const UText* text) {
+ if (!text->chunkContents)
+ return kNoContext;
+ return text->chunkContents == text->pExtra ? kPrimaryContext : kPriorContext;
+}
+
+static void TextLatin1MoveInPrimaryContext(UText* text,
+ int64_t native_index,
+ int64_t native_length,
+ UBool forward) {
+ DCHECK_EQ(text->chunkContents, text->pExtra);
+ if (forward) {
+ DCHECK_GE(native_index, text->b);
+ DCHECK_LT(native_index, native_length);
+ text->chunkNativeStart = native_index;
+ text->chunkNativeLimit = native_index + text->extraSize / sizeof(UChar);
+ if (text->chunkNativeLimit > native_length)
+ text->chunkNativeLimit = native_length;
+ } else {
+ DCHECK_GT(native_index, text->b);
+ DCHECK_LE(native_index, native_length);
+ text->chunkNativeLimit = native_index;
+ text->chunkNativeStart = native_index - text->extraSize / sizeof(UChar);
+ if (text->chunkNativeStart < text->b)
+ text->chunkNativeStart = text->b;
+ }
+ int64_t length = text->chunkNativeLimit - text->chunkNativeStart;
+ // Ensure chunk length is well defined if computed length exceeds int32_t
+ // range.
+ DCHECK_LE(length, std::numeric_limits<int32_t>::max());
+ text->chunkLength = length <= std::numeric_limits<int32_t>::max()
+ ? static_cast<int32_t>(length)
+ : 0;
+ text->nativeIndexingLimit = text->chunkLength;
+ text->chunkOffset = forward ? 0 : text->chunkLength;
+ StringImpl::CopyChars(
+ const_cast<UChar*>(text->chunkContents),
+ static_cast<const LChar*>(text->p) + (text->chunkNativeStart - text->b),
+ static_cast<unsigned>(text->chunkLength));
+}
+
+static void TextLatin1SwitchToPrimaryContext(UText* text,
+ int64_t native_index,
+ int64_t native_length,
+ UBool forward) {
+ DCHECK(!text->chunkContents || text->chunkContents == text->q);
+ text->chunkContents = static_cast<const UChar*>(text->pExtra);
+ TextLatin1MoveInPrimaryContext(text, native_index, native_length, forward);
+}
+
+static void TextLatin1MoveInPriorContext(UText* text,
+ int64_t native_index,
+ int64_t native_length,
+ UBool forward) {
+ DCHECK_EQ(text->chunkContents, text->q);
+ DCHECK(forward ? native_index < text->b : native_index <= text->b);
+ DCHECK(forward ? native_index < native_length
+ : native_index <= native_length);
+ DCHECK(forward ? native_index < native_length
+ : native_index <= native_length);
+ text->chunkNativeStart = 0;
+ text->chunkNativeLimit = text->b;
+ text->chunkLength = text->b;
+ text->nativeIndexingLimit = text->chunkLength;
+ int64_t offset = native_index - text->chunkNativeStart;
+ // Ensure chunk offset is well defined if computed offset exceeds int32_t
+ // range or chunk length.
+ DCHECK_LE(offset, std::numeric_limits<int32_t>::max());
+ text->chunkOffset = std::min(offset <= std::numeric_limits<int32_t>::max()
+ ? static_cast<int32_t>(offset)
+ : 0,
+ text->chunkLength);
+}
+
+static void TextLatin1SwitchToPriorContext(UText* text,
+ int64_t native_index,
+ int64_t native_length,
+ UBool forward) {
+ DCHECK(!text->chunkContents || text->chunkContents == text->pExtra);
+ text->chunkContents = static_cast<const UChar*>(text->q);
+ TextLatin1MoveInPriorContext(text, native_index, native_length, forward);
+}
+
+static inline bool TextInChunkOrOutOfRange(UText* text,
+ int64_t native_index,
+ int64_t native_length,
+ UBool forward,
+ UBool& is_accessible) {
+ if (forward) {
+ if (native_index >= text->chunkNativeStart &&
+ native_index < text->chunkNativeLimit) {
+ int64_t offset = native_index - text->chunkNativeStart;
+ // Ensure chunk offset is well formed if computed offset exceeds int32_t
+ // range.
+ DCHECK_LE(offset, std::numeric_limits<int32_t>::max());
+ text->chunkOffset = offset <= std::numeric_limits<int32_t>::max()
+ ? static_cast<int32_t>(offset)
+ : 0;
+ is_accessible = TRUE;
+ return true;
+ }
+ if (native_index >= native_length &&
+ text->chunkNativeLimit == native_length) {
+ text->chunkOffset = text->chunkLength;
+ is_accessible = FALSE;
+ return true;
+ }
+ } else {
+ if (native_index > text->chunkNativeStart &&
+ native_index <= text->chunkNativeLimit) {
+ int64_t offset = native_index - text->chunkNativeStart;
+ // Ensure chunk offset is well formed if computed offset exceeds int32_t
+ // range.
+ DCHECK_LE(offset, std::numeric_limits<int32_t>::max());
+ text->chunkOffset = offset <= std::numeric_limits<int32_t>::max()
+ ? static_cast<int32_t>(offset)
+ : 0;
+ is_accessible = TRUE;
+ return true;
+ }
+ if (native_index <= 0 && !text->chunkNativeStart) {
+ text->chunkOffset = 0;
+ is_accessible = FALSE;
+ return true;
+ }
+ }
+ return false;
+}
+
+static UBool TextLatin1Access(UText* text,
+ int64_t native_index,
+ UBool forward) {
+ if (!text->context)
+ return FALSE;
+ int64_t native_length = TextNativeLength(text);
+ UBool is_accessible;
+ if (TextInChunkOrOutOfRange(text, native_index, native_length, forward,
+ is_accessible))
+ return is_accessible;
+ native_index = TextPinIndex(native_index, native_length - 1);
+ TextContext current_context = TextLatin1GetCurrentContext(text);
+ TextContext new_context = TextGetContext(text, native_index, forward);
+ DCHECK_NE(new_context, kNoContext);
+ if (new_context == current_context) {
+ if (current_context == kPrimaryContext) {
+ TextLatin1MoveInPrimaryContext(text, native_index, native_length,
+ forward);
+ } else {
+ TextLatin1MoveInPriorContext(text, native_index, native_length, forward);
+ }
+ } else if (new_context == kPrimaryContext) {
+ TextLatin1SwitchToPrimaryContext(text, native_index, native_length,
+ forward);
+ } else {
+ DCHECK_EQ(new_context, kPriorContext);
+ TextLatin1SwitchToPriorContext(text, native_index, native_length, forward);
+ }
+ return TRUE;
+}
+
+static const struct UTextFuncs kTextLatin1Funcs = {
+ sizeof(UTextFuncs),
+ 0,
+ 0,
+ 0,
+ TextClone,
+ TextNativeLength,
+ TextLatin1Access,
+ TextExtract,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ TextClose,
+ nullptr,
+ nullptr,
+ nullptr,
+};
+
+static void TextInit(UText* text,
+ const UTextFuncs* funcs,
+ const void* string,
+ unsigned length,
+ const UChar* prior_context,
+ int prior_context_length) {
+ text->pFuncs = funcs;
+ text->providerProperties = 1 << UTEXT_PROVIDER_STABLE_CHUNKS;
+ text->context = string;
+ text->p = string;
+ text->a = length;
+ text->q = prior_context;
+ text->b = prior_context_length;
+}
+
+static UText* TextOpenLatin1(UTextWithBuffer* ut_with_buffer,
+ const LChar* string,
+ unsigned length,
+ const UChar* prior_context,
+ int prior_context_length,
+ UErrorCode* status) {
+ if (U_FAILURE(*status))
+ return nullptr;
+
+ if (!string ||
+ length > static_cast<unsigned>(std::numeric_limits<int32_t>::max())) {
+ *status = U_ILLEGAL_ARGUMENT_ERROR;
+ return nullptr;
+ }
+ UText* text = utext_setup(&ut_with_buffer->text,
+ sizeof(ut_with_buffer->buffer), status);
+ if (U_FAILURE(*status)) {
+ DCHECK(!text);
+ return nullptr;
+ }
+ TextInit(text, &kTextLatin1Funcs, string, length, prior_context,
+ prior_context_length);
+ return text;
+}
+
+static inline TextContext TextUTF16GetCurrentContext(const UText* text) {
+ if (!text->chunkContents)
+ return kNoContext;
+ return text->chunkContents == text->p ? kPrimaryContext : kPriorContext;
+}
+
+static void TextUTF16MoveInPrimaryContext(UText* text,
+ int64_t native_index,
+ int64_t native_length,
+ UBool forward) {
+ DCHECK_EQ(text->chunkContents, text->p);
+ DCHECK(forward ? native_index >= text->b : native_index > text->b);
+ DCHECK(forward ? native_index < native_length
+ : native_index <= native_length);
+ text->chunkNativeStart = text->b;
+ text->chunkNativeLimit = native_length;
+ int64_t length = text->chunkNativeLimit - text->chunkNativeStart;
+ // Ensure chunk length is well defined if computed length exceeds int32_t
+ // range.
+ DCHECK_LE(length, std::numeric_limits<int32_t>::max());
+ text->chunkLength = length <= std::numeric_limits<int32_t>::max()
+ ? static_cast<int32_t>(length)
+ : 0;
+ text->nativeIndexingLimit = text->chunkLength;
+ int64_t offset = native_index - text->chunkNativeStart;
+ // Ensure chunk offset is well defined if computed offset exceeds int32_t
+ // range or chunk length.
+ DCHECK_LE(offset, std::numeric_limits<int32_t>::max());
+ text->chunkOffset = std::min(offset <= std::numeric_limits<int32_t>::max()
+ ? static_cast<int32_t>(offset)
+ : 0,
+ text->chunkLength);
+}
+
+static void TextUTF16SwitchToPrimaryContext(UText* text,
+ int64_t native_index,
+ int64_t native_length,
+ UBool forward) {
+ DCHECK(!text->chunkContents || text->chunkContents == text->q);
+ text->chunkContents = static_cast<const UChar*>(text->p);
+ TextUTF16MoveInPrimaryContext(text, native_index, native_length, forward);
+}
+
+static void TextUTF16MoveInPriorContext(UText* text,
+ int64_t native_index,
+ int64_t native_length,
+ UBool forward) {
+ DCHECK_EQ(text->chunkContents, text->q);
+ DCHECK(forward ? native_index < text->b : native_index <= text->b);
+ DCHECK(forward ? native_index < native_length
+ : native_index <= native_length);
+ DCHECK(forward ? native_index < native_length
+ : native_index <= native_length);
+ text->chunkNativeStart = 0;
+ text->chunkNativeLimit = text->b;
+ text->chunkLength = text->b;
+ text->nativeIndexingLimit = text->chunkLength;
+ int64_t offset = native_index - text->chunkNativeStart;
+ // Ensure chunk offset is well defined if computed offset exceeds int32_t
+ // range or chunk length.
+ DCHECK_LE(offset, std::numeric_limits<int32_t>::max());
+ text->chunkOffset = std::min(offset <= std::numeric_limits<int32_t>::max()
+ ? static_cast<int32_t>(offset)
+ : 0,
+ text->chunkLength);
+}
+
+static void TextUTF16SwitchToPriorContext(UText* text,
+ int64_t native_index,
+ int64_t native_length,
+ UBool forward) {
+ DCHECK(!text->chunkContents || text->chunkContents == text->p);
+ text->chunkContents = static_cast<const UChar*>(text->q);
+ TextUTF16MoveInPriorContext(text, native_index, native_length, forward);
+}
+
+static UBool TextUTF16Access(UText* text, int64_t native_index, UBool forward) {
+ if (!text->context)
+ return FALSE;
+ int64_t native_length = TextNativeLength(text);
+ UBool is_accessible;
+ if (TextInChunkOrOutOfRange(text, native_index, native_length, forward,
+ is_accessible))
+ return is_accessible;
+ native_index = TextPinIndex(native_index, native_length - 1);
+ TextContext current_context = TextUTF16GetCurrentContext(text);
+ TextContext new_context = TextGetContext(text, native_index, forward);
+ DCHECK_NE(new_context, kNoContext);
+ if (new_context == current_context) {
+ if (current_context == kPrimaryContext) {
+ TextUTF16MoveInPrimaryContext(text, native_index, native_length, forward);
+ } else {
+ TextUTF16MoveInPriorContext(text, native_index, native_length, forward);
+ }
+ } else if (new_context == kPrimaryContext) {
+ TextUTF16SwitchToPrimaryContext(text, native_index, native_length, forward);
+ } else {
+ DCHECK_EQ(new_context, kPriorContext);
+ TextUTF16SwitchToPriorContext(text, native_index, native_length, forward);
+ }
+ return TRUE;
+}
+
+static const struct UTextFuncs kTextUTF16Funcs = {
+ sizeof(UTextFuncs),
+ 0,
+ 0,
+ 0,
+ TextClone,
+ TextNativeLength,
+ TextUTF16Access,
+ TextExtract,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ TextClose,
+ nullptr,
+ nullptr,
+ nullptr,
+};
+
+static UText* TextOpenUTF16(UText* text,
+ const UChar* string,
+ unsigned length,
+ const UChar* prior_context,
+ int prior_context_length,
+ UErrorCode* status) {
+ if (U_FAILURE(*status))
+ return nullptr;
+
+ if (!string ||
+ length > static_cast<unsigned>(std::numeric_limits<int32_t>::max())) {
+ *status = U_ILLEGAL_ARGUMENT_ERROR;
+ return nullptr;
+ }
+
+ text = utext_setup(text, 0, status);
+ if (U_FAILURE(*status)) {
+ DCHECK(!text);
+ return nullptr;
+ }
+ TextInit(text, &kTextUTF16Funcs, string, length, prior_context,
+ prior_context_length);
+ return text;
+}
+
+static UText g_empty_text = UTEXT_INITIALIZER;
+
+static TextBreakIterator* WordBreakIterator(const LChar* string, int length) {
+ UErrorCode error_code = U_ZERO_ERROR;
+ static TextBreakIterator* break_iter = nullptr;
+ if (!break_iter) {
+ break_iter = icu::BreakIterator::createWordInstance(
+ icu::Locale(CurrentTextBreakLocaleID()), error_code);
+ DCHECK(U_SUCCESS(error_code))
+ << "ICU could not open a break iterator: " << u_errorName(error_code)
+ << " (" << error_code << ")";
+ if (!break_iter)
+ return nullptr;
+ }
+
+ UTextWithBuffer text_local;
+ text_local.text = g_empty_text;
+ text_local.text.extraSize = sizeof(text_local.buffer);
+ text_local.text.pExtra = text_local.buffer;
+
+ UErrorCode open_status = U_ZERO_ERROR;
+ UText* text =
+ TextOpenLatin1(&text_local, string, length, nullptr, 0, &open_status);
+ if (U_FAILURE(open_status)) {
+ DLOG(ERROR) << "textOpenLatin1 failed with status " << open_status;
+ return nullptr;
+ }
+
+ UErrorCode set_text_status = U_ZERO_ERROR;
+ break_iter->setText(text, set_text_status);
+ if (U_FAILURE(set_text_status))
+ DLOG(ERROR) << "BreakIterator::seText failed with status "
+ << set_text_status;
+
+ utext_close(text);
+
+ return break_iter;
+}
+
+static void SetText16(TextBreakIterator* iter,
+ const UChar* string,
+ int length) {
+ UErrorCode error_code = U_ZERO_ERROR;
+ UText u_text = UTEXT_INITIALIZER;
+ utext_openUChars(&u_text, string, length, &error_code);
+ if (U_FAILURE(error_code))
+ return;
+ iter->setText(&u_text, error_code);
+}
+
+TextBreakIterator* WordBreakIterator(const UChar* string, int length) {
+ UErrorCode error_code = U_ZERO_ERROR;
+ static TextBreakIterator* break_iter = nullptr;
+ if (!break_iter) {
+ break_iter = icu::BreakIterator::createWordInstance(
+ icu::Locale(CurrentTextBreakLocaleID()), error_code);
+ DCHECK(U_SUCCESS(error_code))
+ << "ICU could not open a break iterator: " << u_errorName(error_code)
+ << " (" << error_code << ")";
+ if (!break_iter)
+ return nullptr;
+ }
+ SetText16(break_iter, string, length);
+ return break_iter;
+}
+
+TextBreakIterator* WordBreakIterator(const String& string,
+ int start,
+ int length) {
+ if (string.IsEmpty())
+ return nullptr;
+ if (string.Is8Bit())
+ return WordBreakIterator(string.Characters8() + start, length);
+ return WordBreakIterator(string.Characters16() + start, length);
+}
+
+TextBreakIterator* AcquireLineBreakIterator(const LChar* string,
+ int length,
+ const AtomicString& locale,
+ const UChar* prior_context,
+ unsigned prior_context_length) {
+ TextBreakIterator* iterator =
+ LineBreakIteratorPool::SharedPool().Take(locale);
+ if (!iterator)
+ return nullptr;
+
+ UTextWithBuffer text_local;
+ text_local.text = g_empty_text;
+ text_local.text.extraSize = sizeof(text_local.buffer);
+ text_local.text.pExtra = text_local.buffer;
+
+ UErrorCode open_status = U_ZERO_ERROR;
+ UText* text = TextOpenLatin1(&text_local, string, length, prior_context,
+ prior_context_length, &open_status);
+ if (U_FAILURE(open_status)) {
+ DLOG(ERROR) << "textOpenLatin1 failed with status " << open_status;
+ return nullptr;
+ }
+
+ UErrorCode set_text_status = U_ZERO_ERROR;
+ iterator->setText(text, set_text_status);
+ if (U_FAILURE(set_text_status)) {
+ DLOG(ERROR) << "ubrk_setUText failed with status " << set_text_status;
+ return nullptr;
+ }
+
+ utext_close(text);
+
+ return iterator;
+}
+
+TextBreakIterator* AcquireLineBreakIterator(const UChar* string,
+ int length,
+ const AtomicString& locale,
+ const UChar* prior_context,
+ unsigned prior_context_length) {
+ TextBreakIterator* iterator =
+ LineBreakIteratorPool::SharedPool().Take(locale);
+ if (!iterator)
+ return nullptr;
+
+ UText text_local = UTEXT_INITIALIZER;
+
+ UErrorCode open_status = U_ZERO_ERROR;
+ UText* text = TextOpenUTF16(&text_local, string, length, prior_context,
+ prior_context_length, &open_status);
+ if (U_FAILURE(open_status)) {
+ DLOG(ERROR) << "textOpenUTF16 failed with status " << open_status;
+ return nullptr;
+ }
+
+ UErrorCode set_text_status = U_ZERO_ERROR;
+ iterator->setText(text, set_text_status);
+ if (U_FAILURE(set_text_status)) {
+ DLOG(ERROR) << "ubrk_setUText failed with status " << set_text_status;
+ return nullptr;
+ }
+
+ utext_close(text);
+
+ return iterator;
+}
+
+void ReleaseLineBreakIterator(TextBreakIterator* iterator) {
+ DCHECK(iterator);
+ LineBreakIteratorPool::SharedPool().Put(iterator);
+}
+
+static TextBreakIterator* g_non_shared_character_break_iterator;
+
+static inline bool CompareAndSwapNonSharedCharacterBreakIterator(
+ TextBreakIterator* expected,
+ TextBreakIterator* new_value) {
+ DEFINE_STATIC_LOCAL(Mutex, non_shared_character_break_iterator_mutex, ());
+ MutexLocker locker(non_shared_character_break_iterator_mutex);
+ if (g_non_shared_character_break_iterator != expected)
+ return false;
+ g_non_shared_character_break_iterator = new_value;
+ return true;
+}
+
+NonSharedCharacterBreakIterator::NonSharedCharacterBreakIterator(
+ const String& string)
+ : is8_bit_(true),
+ charaters8_(nullptr),
+ offset_(0),
+ length_(0),
+ iterator_(nullptr) {
+ if (string.IsEmpty())
+ return;
+
+ is8_bit_ = string.Is8Bit();
+
+ if (is8_bit_) {
+ charaters8_ = string.Characters8();
+ offset_ = 0;
+ length_ = string.length();
+ return;
+ }
+
+ CreateIteratorForBuffer(string.Characters16(), string.length());
+}
+
+NonSharedCharacterBreakIterator::NonSharedCharacterBreakIterator(
+ const UChar* buffer,
+ unsigned length)
+ : is8_bit_(false),
+ charaters8_(nullptr),
+ offset_(0),
+ length_(0),
+ iterator_(nullptr) {
+ CreateIteratorForBuffer(buffer, length);
+}
+
+void NonSharedCharacterBreakIterator::CreateIteratorForBuffer(
+ const UChar* buffer,
+ unsigned length) {
+ iterator_ = g_non_shared_character_break_iterator;
+ bool created_iterator =
+ iterator_ &&
+ CompareAndSwapNonSharedCharacterBreakIterator(iterator_, nullptr);
+ if (!created_iterator) {
+ ICUError error_code;
+ iterator_ = icu::BreakIterator::createCharacterInstance(
+ icu::Locale(CurrentTextBreakLocaleID()), error_code);
+ CHECK(U_SUCCESS(error_code) && iterator_)
+ << "ICU could not open a break iterator: " << u_errorName(error_code)
+ << " (" << error_code << ")";
+ } else {
+ CHECK(iterator_);
+ }
+
+ SetText16(iterator_, buffer, length);
+}
+
+NonSharedCharacterBreakIterator::~NonSharedCharacterBreakIterator() {
+ if (is8_bit_)
+ return;
+ if (!CompareAndSwapNonSharedCharacterBreakIterator(nullptr, iterator_))
+ delete iterator_;
+}
+
+int NonSharedCharacterBreakIterator::Next() {
+ if (!is8_bit_)
+ return iterator_->next();
+
+ if (offset_ >= length_)
+ return kTextBreakDone;
+
+ offset_ += ClusterLengthStartingAt(offset_);
+ return offset_;
+}
+
+int NonSharedCharacterBreakIterator::Current() {
+ if (!is8_bit_)
+ return iterator_->current();
+ return offset_;
+}
+
+bool NonSharedCharacterBreakIterator::IsBreak(int offset) const {
+ if (!is8_bit_)
+ return iterator_->isBoundary(offset);
+ return !IsLFAfterCR(offset);
+}
+
+int NonSharedCharacterBreakIterator::Preceding(int offset) const {
+ if (!is8_bit_)
+ return iterator_->preceding(offset);
+ if (offset <= 0)
+ return kTextBreakDone;
+ if (IsLFAfterCR(offset))
+ return offset - 2;
+ return offset - 1;
+}
+
+int NonSharedCharacterBreakIterator::Following(int offset) const {
+ if (!is8_bit_)
+ return iterator_->following(offset);
+ if (static_cast<unsigned>(offset) >= length_)
+ return kTextBreakDone;
+ return offset + ClusterLengthStartingAt(offset);
+}
+
+TextBreakIterator* SentenceBreakIterator(const UChar* string, int length) {
+ UErrorCode open_status = U_ZERO_ERROR;
+ static TextBreakIterator* iterator = nullptr;
+ if (!iterator) {
+ iterator = icu::BreakIterator::createSentenceInstance(
+ icu::Locale(CurrentTextBreakLocaleID()), open_status);
+ DCHECK(U_SUCCESS(open_status))
+ << "ICU could not open a break iterator: " << u_errorName(open_status)
+ << " (" << open_status << ")";
+ if (!iterator)
+ return nullptr;
+ }
+
+ SetText16(iterator, string, length);
+ return iterator;
+}
+
+bool IsWordTextBreak(TextBreakIterator* iterator) {
+ icu::RuleBasedBreakIterator* rule_based_break_iterator =
+ static_cast<icu::RuleBasedBreakIterator*>(iterator);
+ int rule_status = rule_based_break_iterator->getRuleStatus();
+ return rule_status != UBRK_WORD_NONE;
+}
+
+static TextBreakIterator* SetUpIteratorWithRules(const char* break_rules,
+ const UChar* string,
+ int length) {
+ if (!string)
+ return nullptr;
+
+ static TextBreakIterator* iterator = nullptr;
+ if (!iterator) {
+ UParseError parse_status;
+ UErrorCode open_status = U_ZERO_ERROR;
+ // break_rules is ASCII. Pick the most efficient UnicodeString ctor.
+ iterator = new icu::RuleBasedBreakIterator(
+ icu::UnicodeString(break_rules, -1, US_INV), parse_status, open_status);
+ DCHECK(U_SUCCESS(open_status))
+ << "ICU could not open a break iterator: " << u_errorName(open_status)
+ << " (" << open_status << ")";
+ if (!iterator)
+ return nullptr;
+ }
+
+ SetText16(iterator, string, length);
+ return iterator;
+}
+
+TextBreakIterator* CursorMovementIterator(const UChar* string, int length) {
+ // This rule set is based on character-break iterator rules of ICU 4.0
+ // <http://source.icu-project.org/repos/icu/icu/tags/release-4-0/source/data/brkitr/char.txt>.
+ // The major differences from the original ones are listed below:
+ // * Replaced '[\p{Grapheme_Cluster_Break = SpacingMark}]' with
+ // '[\p{General_Category = Spacing Mark} - $Extend]' for ICU 3.8 or earlier;
+ // * Removed rules that prevent a cursor from moving after prepend characters
+ // (Bug 24342);
+ // * Added rules that prevent a cursor from moving after virama signs of Indic
+ // languages except Tamil (Bug 15790), and;
+ // * Added rules that prevent a cursor from moving before Japanese half-width
+ // katakara voiced marks.
+ // * Added rules for regional indicator symbols.
+ static const char* const kRules =
+ "$CR = [\\p{Grapheme_Cluster_Break = CR}];"
+ "$LF = [\\p{Grapheme_Cluster_Break = LF}];"
+ "$Control = [\\p{Grapheme_Cluster_Break = Control}];"
+ "$VoiceMarks = [\\uFF9E\\uFF9F];" // Japanese half-width katakana voiced
+ // marks
+ "$Extend = [\\p{Grapheme_Cluster_Break = Extend} $VoiceMarks - [\\u0E30 "
+ "\\u0E32 \\u0E45 \\u0EB0 \\u0EB2]];"
+ "$SpacingMark = [[\\p{General_Category = Spacing Mark}] - $Extend];"
+ "$L = [\\p{Grapheme_Cluster_Break = L}];"
+ "$V = [\\p{Grapheme_Cluster_Break = V}];"
+ "$T = [\\p{Grapheme_Cluster_Break = T}];"
+ "$LV = [\\p{Grapheme_Cluster_Break = LV}];"
+ "$LVT = [\\p{Grapheme_Cluster_Break = LVT}];"
+ "$Hin0 = [\\u0905-\\u0939];" // Devanagari Letter A,...,Ha
+ "$HinV = \\u094D;" // Devanagari Sign Virama
+ "$Hin1 = [\\u0915-\\u0939];" // Devanagari Letter Ka,...,Ha
+ "$Ben0 = [\\u0985-\\u09B9];" // Bengali Letter A,...,Ha
+ "$BenV = \\u09CD;" // Bengali Sign Virama
+ "$Ben1 = [\\u0995-\\u09B9];" // Bengali Letter Ka,...,Ha
+ "$Pan0 = [\\u0A05-\\u0A39];" // Gurmukhi Letter A,...,Ha
+ "$PanV = \\u0A4D;" // Gurmukhi Sign Virama
+ "$Pan1 = [\\u0A15-\\u0A39];" // Gurmukhi Letter Ka,...,Ha
+ "$Guj0 = [\\u0A85-\\u0AB9];" // Gujarati Letter A,...,Ha
+ "$GujV = \\u0ACD;" // Gujarati Sign Virama
+ "$Guj1 = [\\u0A95-\\u0AB9];" // Gujarati Letter Ka,...,Ha
+ "$Ori0 = [\\u0B05-\\u0B39];" // Oriya Letter A,...,Ha
+ "$OriV = \\u0B4D;" // Oriya Sign Virama
+ "$Ori1 = [\\u0B15-\\u0B39];" // Oriya Letter Ka,...,Ha
+ "$Tel0 = [\\u0C05-\\u0C39];" // Telugu Letter A,...,Ha
+ "$TelV = \\u0C4D;" // Telugu Sign Virama
+ "$Tel1 = [\\u0C14-\\u0C39];" // Telugu Letter Ka,...,Ha
+ "$Kan0 = [\\u0C85-\\u0CB9];" // Kannada Letter A,...,Ha
+ "$KanV = \\u0CCD;" // Kannada Sign Virama
+ "$Kan1 = [\\u0C95-\\u0CB9];" // Kannada Letter A,...,Ha
+ "$Mal0 = [\\u0D05-\\u0D39];" // Malayalam Letter A,...,Ha
+ "$MalV = \\u0D4D;" // Malayalam Sign Virama
+ "$Mal1 = [\\u0D15-\\u0D39];" // Malayalam Letter A,...,Ha
+ "$RI = [\\U0001F1E6-\\U0001F1FF];" // Emoji regional indicators
+ "!!chain;"
+ "!!forward;"
+ "$CR $LF;"
+ "$L ($L | $V | $LV | $LVT);"
+ "($LV | $V) ($V | $T);"
+ "($LVT | $T) $T;"
+ "[^$Control $CR $LF] $Extend;"
+ "[^$Control $CR $LF] $SpacingMark;"
+ "$RI $RI / $RI;"
+ "$RI $RI;"
+ "$Hin0 $HinV $Hin1;" // Devanagari Virama (forward)
+ "$Ben0 $BenV $Ben1;" // Bengali Virama (forward)
+ "$Pan0 $PanV $Pan1;" // Gurmukhi Virama (forward)
+ "$Guj0 $GujV $Guj1;" // Gujarati Virama (forward)
+ "$Ori0 $OriV $Ori1;" // Oriya Virama (forward)
+ "$Tel0 $TelV $Tel1;" // Telugu Virama (forward)
+ "$Kan0 $KanV $Kan1;" // Kannada Virama (forward)
+ "$Mal0 $MalV $Mal1;" // Malayalam Virama (forward)
+ "!!reverse;"
+ "$LF $CR;"
+ "($L | $V | $LV | $LVT) $L;"
+ "($V | $T) ($LV | $V);"
+ "$T ($LVT | $T);"
+ "$Extend [^$Control $CR $LF];"
+ "$SpacingMark [^$Control $CR $LF];"
+ "$RI $RI / $RI $RI;"
+ "$RI $RI;"
+ "$Hin1 $HinV $Hin0;" // Devanagari Virama (backward)
+ "$Ben1 $BenV $Ben0;" // Bengali Virama (backward)
+ "$Pan1 $PanV $Pan0;" // Gurmukhi Virama (backward)
+ "$Guj1 $GujV $Guj0;" // Gujarati Virama (backward)
+ "$Ori1 $OriV $Ori0;" // Gujarati Virama (backward)
+ "$Tel1 $TelV $Tel0;" // Telugu Virama (backward)
+ "$Kan1 $KanV $Kan0;" // Kannada Virama (backward)
+ "$Mal1 $MalV $Mal0;" // Malayalam Virama (backward)
+ "!!safe_reverse;"
+ "!!safe_forward;";
+
+ return SetUpIteratorWithRules(kRules, string, length);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/text_break_iterator_internal_icu.cc b/chromium/third_party/blink/renderer/platform/text/text_break_iterator_internal_icu.cc
new file mode 100644
index 00000000000..75151ef1b4b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/text_break_iterator_internal_icu.cc
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2009 Google Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "third_party/blink/renderer/platform/text/text_break_iterator_internal_icu.h"
+
+#include "third_party/blink/renderer/platform/language.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+static const char* UILanguage() {
+ // Chrome's UI language can be different from the OS UI language on Windows.
+ // We want to return Chrome's UI language here.
+ DEFINE_STATIC_LOCAL(const CString, locale, (DefaultLanguage().Latin1()));
+ return locale.data();
+}
+
+const char* CurrentSearchLocaleID() {
+ return UILanguage();
+}
+
+const char* CurrentTextBreakLocaleID() {
+ return UILanguage();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/text_break_iterator_internal_icu.h b/chromium/third_party/blink/renderer/platform/text/text_break_iterator_internal_icu.h
new file mode 100644
index 00000000000..628f776dc01
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/text_break_iterator_internal_icu.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_TEXT_BREAK_ITERATOR_INTERNAL_ICU_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_TEXT_BREAK_ITERATOR_INTERNAL_ICU_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+// FIXME: Now that this handles locales for ICU, not just for text breaking,
+// this file and the various implementation files should be renamed.
+
+namespace blink {
+
+PLATFORM_EXPORT const char* CurrentSearchLocaleID();
+PLATFORM_EXPORT const char* CurrentTextBreakLocaleID();
+}
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/text/text_break_iterator_test.cc b/chromium/third_party/blink/renderer/platform/text/text_break_iterator_test.cc
new file mode 100644
index 00000000000..74f59591782
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/text_break_iterator_test.cc
@@ -0,0 +1,271 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/text/text_break_iterator.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class TextBreakIteratorTest : public testing::Test {
+ protected:
+ void SetTestString(const char* test_string) {
+ test_string_ = String::FromUTF8(test_string);
+ }
+
+ void SetTestString16(Vector<UChar> input) {
+ test_string_ = String(input.data(), static_cast<unsigned>(input.size()));
+ }
+
+ // The expected break positions must be specified UTF-16 character boundaries.
+ void MatchLineBreaks(
+ LineBreakType line_break_type,
+ const Vector<int> expected_break_positions,
+ BreakSpaceType break_space = BreakSpaceType::kBeforeEverySpace) {
+ if (test_string_.Is8Bit()) {
+ test_string_ = String::Make16BitFrom8BitSource(test_string_.Characters8(),
+ test_string_.length());
+ }
+ LazyLineBreakIterator lazy_break_iterator(test_string_);
+ lazy_break_iterator.SetBreakType(line_break_type);
+ lazy_break_iterator.SetBreakSpace(break_space);
+ TestIsBreakable(expected_break_positions, lazy_break_iterator);
+ TestNextBreakOpportunity(expected_break_positions, lazy_break_iterator);
+ }
+
+ // Test IsBreakable() by iterating all positions. BreakingContext uses this
+ // interface.
+ void TestIsBreakable(const Vector<int> expected_break_positions,
+ const LazyLineBreakIterator& break_iterator) {
+ Vector<int> break_positions;
+ int next_breakable = -1;
+ for (unsigned i = 0; i <= test_string_.length(); i++) {
+ if (break_iterator.IsBreakable(i, next_breakable))
+ break_positions.push_back(i);
+ }
+ EXPECT_THAT(break_positions,
+ testing::ElementsAreArray(expected_break_positions))
+ << test_string_ << " " << break_iterator.BreakType() << " "
+ << break_iterator.BreakSpace();
+ }
+
+ // Test NextBreakOpportunity() by iterating break opportunities.
+ // ShapingLineBreaker uses this interface.
+ void TestNextBreakOpportunity(const Vector<int> expected_break_positions,
+ const LazyLineBreakIterator& break_iterator) {
+ Vector<int> break_positions;
+ for (unsigned i = 0; i <= test_string_.length(); i++) {
+ i = break_iterator.NextBreakOpportunity(i);
+ break_positions.push_back(i);
+ }
+ EXPECT_THAT(break_positions,
+ testing::ElementsAreArray(expected_break_positions))
+ << test_string_ << " " << break_iterator.BreakType() << " "
+ << break_iterator.BreakSpace();
+ }
+
+ unsigned TestLengthOfGraphemeCluster() {
+ return LengthOfGraphemeCluster(test_string_);
+ }
+
+ private:
+ String test_string_;
+};
+
+static const LineBreakType all_break_types[] = {
+ LineBreakType::kNormal, LineBreakType::kBreakAll,
+ LineBreakType::kBreakCharacter, LineBreakType::kKeepAll};
+
+class BreakTypeTest : public TextBreakIteratorTest,
+ public testing::WithParamInterface<LineBreakType> {};
+
+INSTANTIATE_TEST_CASE_P(TextBreakIteratorTest,
+ BreakTypeTest,
+ testing::ValuesIn(all_break_types));
+
+TEST_P(BreakTypeTest, EmptyString) {
+ LazyLineBreakIterator iterator(g_empty_string);
+ iterator.SetBreakType(GetParam());
+ EXPECT_TRUE(iterator.IsBreakable(0));
+}
+
+TEST_P(BreakTypeTest, EmptyNullString) {
+ LazyLineBreakIterator iterator(String{});
+ iterator.SetBreakType(GetParam());
+ EXPECT_TRUE(iterator.IsBreakable(0));
+}
+
+TEST_P(BreakTypeTest, EmptyDefaultConstructor) {
+ LazyLineBreakIterator iterator;
+ iterator.SetBreakType(GetParam());
+ EXPECT_TRUE(iterator.IsBreakable(0));
+}
+
+TEST_F(TextBreakIteratorTest, Basic) {
+ SetTestString("a b c");
+ MatchLineBreaks(LineBreakType::kNormal, {1, 3, 4, 6});
+ MatchLineBreaks(LineBreakType::kNormal, {1, 3, 6},
+ BreakSpaceType::kBeforeSpaceRun);
+}
+
+TEST_F(TextBreakIteratorTest, Newline) {
+ SetTestString("a\nb\n\nc\n d");
+ MatchLineBreaks(LineBreakType::kNormal, {1, 3, 4, 6, 7, 9});
+ MatchLineBreaks(LineBreakType::kNormal, {1, 3, 6, 9},
+ BreakSpaceType::kBeforeSpaceRun);
+}
+
+TEST_F(TextBreakIteratorTest, Tab) {
+ SetTestString("a\tb\t\tc");
+ MatchLineBreaks(LineBreakType::kNormal, {1, 3, 4, 6});
+ MatchLineBreaks(LineBreakType::kNormal, {1, 3, 6},
+ BreakSpaceType::kBeforeSpaceRun);
+}
+
+TEST_F(TextBreakIteratorTest, LatinPunctuation) {
+ SetTestString("(ab) cd.");
+ MatchLineBreaks(LineBreakType::kNormal, {4, 8});
+ MatchLineBreaks(LineBreakType::kBreakAll, {2, 4, 6, 8});
+ MatchLineBreaks(LineBreakType::kBreakCharacter, {1, 2, 3, 4, 5, 6, 7, 8});
+ MatchLineBreaks(LineBreakType::kKeepAll, {4, 8});
+}
+
+TEST_F(TextBreakIteratorTest, Chinese) {
+ SetTestString("標準萬國碼");
+ MatchLineBreaks(LineBreakType::kNormal, {1, 2, 3, 4, 5});
+ MatchLineBreaks(LineBreakType::kBreakAll, {1, 2, 3, 4, 5});
+ MatchLineBreaks(LineBreakType::kBreakCharacter, {1, 2, 3, 4, 5});
+ MatchLineBreaks(LineBreakType::kKeepAll, {5});
+}
+
+TEST_F(TextBreakIteratorTest, ChineseMixed) {
+ SetTestString("標(準)萬ab國.碼");
+ MatchLineBreaks(LineBreakType::kNormal, {1, 4, 5, 7, 9, 10});
+ MatchLineBreaks(LineBreakType::kBreakAll, {1, 4, 5, 6, 7, 9, 10});
+ MatchLineBreaks(LineBreakType::kBreakCharacter,
+ {1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
+ MatchLineBreaks(LineBreakType::kKeepAll, {1, 4, 9, 10});
+}
+
+TEST_F(TextBreakIteratorTest, ChineseSpaces) {
+ SetTestString("標 萬 a 國");
+ MatchLineBreaks(LineBreakType::kNormal, {1, 2, 4, 5, 7, 8, 10});
+ MatchLineBreaks(LineBreakType::kBreakAll, {1, 2, 4, 5, 7, 8, 10});
+ MatchLineBreaks(LineBreakType::kBreakCharacter,
+ {1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
+ MatchLineBreaks(LineBreakType::kKeepAll, {1, 2, 4, 5, 7, 8, 10});
+ MatchLineBreaks(LineBreakType::kNormal, {1, 4, 7, 10},
+ BreakSpaceType::kBeforeSpaceRun);
+}
+
+TEST_F(TextBreakIteratorTest, KeepEmojiZWJFamilyIsolate) {
+ SetTestString(u8"\U0001F468\u200D\U0001F469\u200D\U0001F467\u200D\U0001F466");
+ MatchLineBreaks(LineBreakType::kNormal, {11});
+ MatchLineBreaks(LineBreakType::kBreakAll, {11});
+ MatchLineBreaks(LineBreakType::kBreakCharacter, {11});
+ MatchLineBreaks(LineBreakType::kKeepAll, {11});
+}
+
+TEST_F(TextBreakIteratorTest, KeepEmojiModifierSequenceIsolate) {
+ SetTestString(u8"\u261D\U0001F3FB");
+ MatchLineBreaks(LineBreakType::kNormal, {3});
+ MatchLineBreaks(LineBreakType::kBreakAll, {3});
+ MatchLineBreaks(LineBreakType::kBreakCharacter, {3});
+ MatchLineBreaks(LineBreakType::kKeepAll, {3});
+}
+
+TEST_F(TextBreakIteratorTest, KeepEmojiZWJSequence) {
+ SetTestString(
+ u8"abc \U0001F469\u200D\U0001F469\u200D\U0001F467\u200D\U0001F467 def");
+ MatchLineBreaks(LineBreakType::kNormal, {3, 15, 19});
+ MatchLineBreaks(LineBreakType::kBreakAll, {1, 2, 3, 15, 17, 18, 19});
+ MatchLineBreaks(LineBreakType::kBreakCharacter,
+ {1, 2, 3, 4, 15, 16, 17, 18, 19});
+ MatchLineBreaks(LineBreakType::kKeepAll, {3, 15, 19});
+}
+
+TEST_F(TextBreakIteratorTest, KeepEmojiModifierSequence) {
+ SetTestString(u8"abc \u261D\U0001F3FB def");
+ MatchLineBreaks(LineBreakType::kNormal, {3, 7, 11});
+ MatchLineBreaks(LineBreakType::kBreakAll, {1, 2, 3, 7, 9, 10, 11});
+ MatchLineBreaks(LineBreakType::kBreakCharacter,
+ {1, 2, 3, 4, 7, 8, 9, 10, 11});
+ MatchLineBreaks(LineBreakType::kKeepAll, {3, 7, 11});
+}
+
+TEST_F(TextBreakIteratorTest, NextBreakOpportunityAtEnd) {
+ LineBreakType break_types[] = {
+ LineBreakType::kNormal, LineBreakType::kBreakAll,
+ LineBreakType::kBreakCharacter, LineBreakType::kKeepAll};
+ for (const auto break_type : break_types) {
+ LazyLineBreakIterator break_iterator(String("1"));
+ break_iterator.SetBreakType(break_type);
+ EXPECT_EQ(1u, break_iterator.NextBreakOpportunity(1));
+ }
+}
+
+TEST_F(TextBreakIteratorTest, LengthOfGraphemeCluster) {
+ SetTestString("");
+ EXPECT_EQ(0u, TestLengthOfGraphemeCluster());
+
+ SetTestString16({});
+ EXPECT_EQ(0u, TestLengthOfGraphemeCluster());
+
+ SetTestString("a");
+ EXPECT_EQ(1u, TestLengthOfGraphemeCluster());
+ SetTestString("\n");
+ EXPECT_EQ(1u, TestLengthOfGraphemeCluster());
+ SetTestString("\r");
+ EXPECT_EQ(1u, TestLengthOfGraphemeCluster());
+ SetTestString16({'a'});
+ EXPECT_EQ(1u, TestLengthOfGraphemeCluster());
+ SetTestString16({'\n'});
+ EXPECT_EQ(1u, TestLengthOfGraphemeCluster());
+ SetTestString16({'\r'});
+ EXPECT_EQ(1u, TestLengthOfGraphemeCluster());
+
+ SetTestString("abc");
+ EXPECT_EQ(1u, TestLengthOfGraphemeCluster());
+
+ SetTestString16({'a', 'b', 'c'});
+ EXPECT_EQ(1u, TestLengthOfGraphemeCluster());
+
+ SetTestString("\r\n");
+ EXPECT_EQ(2u, TestLengthOfGraphemeCluster());
+
+ SetTestString16({'\r', '\n'});
+ EXPECT_EQ(2u, TestLengthOfGraphemeCluster());
+
+ SetTestString("\n\r");
+ EXPECT_EQ(1u, TestLengthOfGraphemeCluster());
+
+ SetTestString16({'\n', '\r'});
+ EXPECT_EQ(1u, TestLengthOfGraphemeCluster());
+
+ SetTestString("\r\n\r");
+ EXPECT_EQ(2u, TestLengthOfGraphemeCluster());
+
+ SetTestString16({'\r', '\n', '\r'});
+ EXPECT_EQ(2u, TestLengthOfGraphemeCluster());
+
+ SetTestString16({'g', 0x308});
+ EXPECT_EQ(2u, TestLengthOfGraphemeCluster());
+ SetTestString16({0x1100, 0x1161, 0x11A8});
+ EXPECT_EQ(3u, TestLengthOfGraphemeCluster());
+ SetTestString16({0x0BA8, 0x0BBF});
+ EXPECT_EQ(2u, TestLengthOfGraphemeCluster());
+
+ SetTestString16({0x308, 'g'});
+ EXPECT_EQ(1u, TestLengthOfGraphemeCluster());
+
+ SetTestString("\r\nbc");
+ EXPECT_EQ(2u, TestLengthOfGraphemeCluster());
+
+ SetTestString16({'g', 0x308, 'b', 'c'});
+ EXPECT_EQ(2u, TestLengthOfGraphemeCluster());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/text_direction.cc b/chromium/third_party/blink/renderer/platform/text/text_direction.cc
new file mode 100644
index 00000000000..2ea56e90187
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/text_direction.cc
@@ -0,0 +1,15 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/text/text_direction.h"
+
+#include <ostream>
+
+namespace blink {
+
+std::ostream& operator<<(std::ostream& ostream, TextDirection direction) {
+ return ostream << (IsLtr(direction) ? "LTR" : "RTL");
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/text_direction.h b/chromium/third_party/blink/renderer/platform/text/text_direction.h
new file mode 100644
index 00000000000..3385465093f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/text_direction.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2003, 2006 Apple Computer, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_TEXT_DIRECTION_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_TEXT_DIRECTION_H_
+
+#include <cstdint>
+#include <iosfwd>
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+
+// The direction of text in bidirectional scripts such as Arabic or Hebrew.
+//
+// Used for explicit directions such as in the HTML dir attribute or the CSS
+// 'direction' property.
+// https://html.spec.whatwg.org/multipage/dom.html#the-dir-attribute
+// https://drafts.csswg.org/css-writing-modes/#direction
+//
+// Also used for resolved directions by UAX#9 UNICODE BIDIRECTIONAL ALGORITHM.
+// http://unicode.org/reports/tr9/
+enum class TextDirection : uint8_t { kLtr = 0, kRtl = 1 };
+
+inline bool IsLtr(TextDirection direction) {
+ return direction == TextDirection::kLtr;
+}
+
+inline bool IsRtl(TextDirection direction) {
+ return direction != TextDirection::kLtr;
+}
+
+inline TextDirection DirectionFromLevel(unsigned level) {
+ return level & 1 ? TextDirection::kRtl : TextDirection::kLtr;
+}
+
+PLATFORM_EXPORT std::ostream& operator<<(std::ostream&, TextDirection);
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/text/text_encoding_detector.cc b/chromium/third_party/blink/renderer/platform/text/text_encoding_detector.cc
new file mode 100644
index 00000000000..16efbc2ee4e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/text_encoding_detector.cc
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2008, 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/renderer/platform/text/text_encoding_detector.h"
+
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
+#include "third_party/ced/src/compact_enc_det/compact_enc_det.h"
+
+// third_party/ced/src/util/encodings/encodings.h, which is included
+// by the include above, undefs UNICODE because that is a macro used
+// internally in ced. If we later in the same translation unit do
+// anything related to Windows or Windows headers those will then use
+// the ASCII versions which we do not want. To avoid that happening in
+// jumbo builds, we redefine UNICODE again here.
+#if defined(OS_WIN)
+#define UNICODE 1
+#endif // OS_WIN
+
+namespace blink {
+
+bool DetectTextEncoding(const char* data,
+ size_t length,
+ const char* hint_encoding_name,
+ const KURL& hint_url,
+ const char* hint_user_language,
+ WTF::TextEncoding* detected_encoding) {
+ *detected_encoding = WTF::TextEncoding();
+ // In general, do not use language hint. This helps get more
+ // deterministic encoding detection results across devices. Note that local
+ // file resources can still benefit from the hint.
+ Language language = UNKNOWN_LANGUAGE;
+ if (hint_url.Protocol() == "file")
+ LanguageFromCode(hint_user_language, &language);
+ int consumed_bytes;
+ bool is_reliable;
+ Encoding encoding = CompactEncDet::DetectEncoding(
+ data, length, hint_url.GetString().Ascii().data(), nullptr, nullptr,
+ EncodingNameAliasToEncoding(hint_encoding_name), language,
+ CompactEncDet::WEB_CORPUS,
+ false, // Include 7-bit encodings to detect ISO-2022-JP
+ &consumed_bytes, &is_reliable);
+
+ // Should return false if the detected encoding is UTF8. This helps prevent
+ // modern web sites from neglecting proper encoding labelling and simply
+ // relying on browser-side encoding detection. Encoding detection is supposed
+ // to work for web sites with legacy encoding only (so this doesn't have to
+ // be applied to local file resources).
+ // Detection failure leads |TextResourceDecoder| to use its default encoding
+ // determined from system locale or TLD.
+ if (encoding == UNKNOWN_ENCODING ||
+ (hint_url.Protocol() != "file" && encoding == UTF8))
+ return false;
+
+ *detected_encoding = WTF::TextEncoding(MimeEncodingName(encoding));
+ return true;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/text_encoding_detector.h b/chromium/third_party/blink/renderer/platform/text/text_encoding_detector.h
new file mode 100644
index 00000000000..2650a58b549
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/text_encoding_detector.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_TEXT_ENCODING_DETECTOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_TEXT_ENCODING_DETECTOR_H_
+
+#include <cstddef>
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace WTF {
+class TextEncoding;
+}
+
+namespace blink {
+class KURL;
+
+// Given a sequence of bytes in |data| of length |len| and an optional
+// hintEncodingName, detect the most likely character encoding.
+// The way hintEncodingName is used is up to an implementation.
+// Currently, the only caller sets it to the parent frame encoding.
+// |hintUrl| is optional. You can pass nullptr.
+// |hintUserLanguage| is an optional language code like "fr", and can be
+// |nullptr.
+PLATFORM_EXPORT bool DetectTextEncoding(const char* data,
+ size_t length,
+ const char* hint_encoding_name,
+ const KURL& hint_url,
+ const char* hint_user_language,
+ WTF::TextEncoding* detected_encoding);
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/text/text_encoding_detector_test.cc b/chromium/third_party/blink/renderer/platform/text/text_encoding_detector_test.cc
new file mode 100644
index 00000000000..94349fc2cb2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/text_encoding_detector_test.cc
@@ -0,0 +1,120 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/text/text_encoding_detector.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
+
+namespace blink {
+
+TEST(TextEncodingDetectorTest, RespectIso2022Jp) {
+ // ISO-2022-JP is the only 7-bit encoding defined in WHATWG standard.
+ std::string iso2022jp =
+ " \x1B"
+ "$BKL3$F;F|K\\%O%`%U%!%$%?!<%:$,%=%U%H%P%s%/$H$N%W%l!<%*%U$r@)$7!\"";
+ WTF::TextEncoding encoding;
+ bool result = DetectTextEncoding(iso2022jp.c_str(), iso2022jp.length(),
+ nullptr, NullURL(), nullptr, &encoding);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(WTF::TextEncoding("ISO-2022-JP"), encoding);
+}
+
+TEST(TextEncodingDetectorTest, Ignore7BitEncoding) {
+ // 7-bit encodings except ISO-2022-JP are not supported by WHATWG.
+ // They should be detected as plain text (US-ASCII).
+ std::string hz_gb2312 =
+ " ~{\x54\x42\x31\x7D\x37\x22\x55\x39\x35\x3D\x3D\x71~} abc";
+ WTF::TextEncoding encoding;
+ bool result = DetectTextEncoding(hz_gb2312.c_str(), hz_gb2312.length(),
+ nullptr, NullURL(), nullptr, &encoding);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(WTF::TextEncoding("US-ASCII"), encoding);
+}
+
+TEST(TextEncodingDetectorTest, NonWHATWGEncodingBecomesAscii) {
+ std::string pseudo_jpg =
+ "\xff\xd8\xff\xe0\x00\x10JFIF foo bar baz\xff\xe1\x00\xa5"
+ "\x01\xd7\xff\x01\x57\x33\x44\x55\x66\x77\xed\xcb\xa9\x87"
+ "\xff\xd7\xff\xe0\x00\x10JFIF foo bar baz\xff\xe1\x00\xa5"
+ "\x87\x01\xd7\xff\x01\x57\x33\x44\x55\x66\x77\xed\xcb\xa9";
+ WTF::TextEncoding encoding;
+ bool result = DetectTextEncoding(pseudo_jpg.c_str(), pseudo_jpg.length(),
+ nullptr, NullURL(), nullptr, &encoding);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(WTF::TextEncoding("US-ASCII"), encoding);
+}
+
+TEST(TextEncodingDetectorTest, UrlHintHelpsEUCJP) {
+ std::string eucjp_bytes =
+ "<TITLE>"
+ "\xA5\xD1\xA5\xEF\xA1\xBC\xA5\xC1\xA5\xE3\xA1\xBC\xA5\xC8\xA1\xC3\xC5\xEA"
+ "\xBB\xF1\xBE\xF0\xCA\xF3\xA4\xCE\xA5\xD5\xA5\xA3\xA5\xB9\xA5\xB3</"
+ "TITLE>";
+ WTF::TextEncoding encoding;
+ bool result = DetectTextEncoding(eucjp_bytes.c_str(), eucjp_bytes.length(),
+ nullptr, NullURL(), nullptr, &encoding);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(WTF::TextEncoding("GBK"), encoding)
+ << "Without language hint, it's detected as GBK";
+
+ KURL url_jp_domain("http://example.co.jp/");
+ result = DetectTextEncoding(eucjp_bytes.c_str(), eucjp_bytes.length(),
+ nullptr, url_jp_domain, nullptr, &encoding);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(WTF::TextEncoding("EUC-JP"), encoding)
+ << "With URL hint including '.jp', it's detected as EUC-JP";
+}
+
+TEST(TextEncodingDetectorTest, LanguageHintHelpsEUCJP) {
+ std::string eucjp_bytes =
+ "<TITLE>"
+ "\xA5\xD1\xA5\xEF\xA1\xBC\xA5\xC1\xA5\xE3\xA1\xBC\xA5\xC8\xA1\xC3\xC5\xEA"
+ "\xBB\xF1\xBE\xF0\xCA\xF3\xA4\xCE\xA5\xD5\xA5\xA3\xA5\xB9\xA5\xB3</"
+ "TITLE>";
+ WTF::TextEncoding encoding;
+ bool result = DetectTextEncoding(eucjp_bytes.c_str(), eucjp_bytes.length(),
+ nullptr, NullURL(), nullptr, &encoding);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(WTF::TextEncoding("GBK"), encoding)
+ << "Without language hint, it's detected as GBK";
+
+ KURL url("http://example.com/");
+ result = DetectTextEncoding(eucjp_bytes.c_str(), eucjp_bytes.length(),
+ nullptr, url, "ja", &encoding);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(WTF::TextEncoding("GBK"), encoding)
+ << "Language hint doesn't help for normal URL. Should be detected as GBK";
+
+ KURL file_url("file:///text.txt");
+ result = DetectTextEncoding(eucjp_bytes.c_str(), eucjp_bytes.length(),
+ nullptr, file_url, "ja", &encoding);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(WTF::TextEncoding("EUC-JP"), encoding)
+ << "Language hint works for file resource. Should be detected as EUC-JP";
+}
+
+TEST(TextEncodingDetectorTest, UTF8DetectionShouldFail) {
+ std::string utf8_bytes =
+ "tnegirjji gosa gii beare s\xC3\xA1htt\xC3\xA1 \xC4\x8D\xC3"
+ "\xA1llit artihkkaliid. Maid don s\xC3\xA1ht\xC3\xA1t dievasmah";
+ WTF::TextEncoding encoding;
+ bool result = DetectTextEncoding(utf8_bytes.c_str(), utf8_bytes.length(),
+ nullptr, NullURL(), nullptr, &encoding);
+ EXPECT_FALSE(result);
+}
+
+TEST(TextEncodingDetectorTest, RespectUTF8DetectionForFileResource) {
+ std::string utf8_bytes =
+ "tnegirjji gosa gii beare s\xC3\xA1htt\xC3\xA1 \xC4\x8D\xC3"
+ "\xA1llit artihkkaliid. Maid don s\xC3\xA1ht\xC3\xA1t dievasmah";
+ WTF::TextEncoding encoding;
+ KURL file_url("file:///text.txt");
+ bool result = DetectTextEncoding(utf8_bytes.c_str(), utf8_bytes.length(),
+ nullptr, file_url, nullptr, &encoding);
+ EXPECT_TRUE(result);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/text_justify.h b/chromium/third_party/blink/renderer/platform/text/text_justify.h
new file mode 100644
index 00000000000..2f909263617
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/text_justify.h
@@ -0,0 +1,19 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_TEXT_JUSTIFY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_TEXT_JUSTIFY_H_
+
+namespace blink {
+
+enum class TextJustify : unsigned {
+ kAuto = 0x0,
+ kNone = 0x1,
+ kInterWord = 0x2,
+ kDistribute = 0x3
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_TEXT_JUSTIFY_H_
diff --git a/chromium/third_party/blink/renderer/platform/text/text_run.cc b/chromium/third_party/blink/renderer/platform/text/text_run.cc
new file mode 100644
index 00000000000..3aa7a156c69
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/text_run.cc
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2011 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/text/text_run.h"
+
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/text/character.h"
+
+namespace blink {
+
+struct ExpectedTextRunSize {
+ DISALLOW_NEW();
+ const void* pointer;
+ int integers[2];
+ float floats[2];
+ uint32_t bitfields : 10;
+ TabSize tab_size;
+};
+
+static_assert(sizeof(TextRun) == sizeof(ExpectedTextRunSize),
+ "TextRun should have expected size");
+
+void TextRun::SetText(const String& string) {
+ len_ = string.length();
+ if (!len_) {
+ data_.characters8 = nullptr;
+ is8_bit_ = true;
+ return;
+ }
+ is8_bit_ = string.Is8Bit();
+ if (is8_bit_)
+ data_.characters8 = string.Characters8();
+ else
+ data_.characters16 = string.Characters16();
+}
+
+std::unique_ptr<UChar[]> TextRun::NormalizedUTF16(
+ unsigned* result_length) const {
+ const UChar* source;
+ String string_for8_bit_run;
+ if (Is8Bit()) {
+ string_for8_bit_run =
+ String::Make16BitFrom8BitSource(Characters8(), length());
+ source = string_for8_bit_run.Characters16();
+ } else {
+ source = Characters16();
+ }
+
+ auto buffer = std::make_unique<UChar[]>(len_ + 1);
+ *result_length = 0;
+
+ bool error = false;
+ unsigned position = 0;
+ while (position < len_) {
+ UChar32 character;
+ U16_NEXT(source, position, len_, character);
+ // Don't normalize tabs as they are not treated as spaces for word-end.
+ if (NormalizeSpace() &&
+ Character::IsNormalizedCanvasSpaceCharacter(character)) {
+ character = kSpaceCharacter;
+ } else if (Character::TreatAsSpace(character) &&
+ character != kNoBreakSpaceCharacter) {
+ character = kSpaceCharacter;
+ } else if (!RuntimeEnabledFeatures::
+ RenderUnicodeControlCharactersEnabled() &&
+ Character::LegacyTreatAsZeroWidthSpaceInComplexScript(
+ character)) {
+ character = kZeroWidthSpaceCharacter;
+ } else if (Character::TreatAsZeroWidthSpaceInComplexScript(character)) {
+ character = kZeroWidthSpaceCharacter;
+ }
+
+ U16_APPEND(buffer, *result_length, len_, character, error);
+ DCHECK(!error);
+ }
+
+ DCHECK(*result_length <= len_);
+ return buffer;
+}
+
+unsigned TextRun::IndexOfSubRun(const TextRun& sub_run) const {
+ if (Is8Bit() == sub_run.Is8Bit() && sub_run.Bytes() >= Bytes()) {
+ size_t start_index = Is8Bit() ? sub_run.Characters8() - Characters8()
+ : sub_run.Characters16() - Characters16();
+ if (start_index + sub_run.length() <= length())
+ return start_index;
+ }
+ return std::numeric_limits<unsigned>::max();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/text_run.h b/chromium/third_party/blink/renderer/platform/text/text_run.h
new file mode 100644
index 00000000000..2e5e3455e72
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/text_run.h
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2006, 2007, 2011 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_TEXT_RUN_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_TEXT_RUN_H_
+
+#include "third_party/blink/renderer/platform/fonts/glyph.h"
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/text/tab_size.h"
+#include "third_party/blink/renderer/platform/text/text_direction.h"
+#include "third_party/blink/renderer/platform/text/text_justify.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_view.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+#include <SkRefCnt.h>
+#include <unicode/utf16.h>
+
+namespace blink {
+
+class PLATFORM_EXPORT TextRun final {
+ DISALLOW_NEW();
+
+ public:
+ enum ExpansionBehaviorFlags {
+ kForbidTrailingExpansion = 0 << 0,
+ kAllowTrailingExpansion = 1 << 0,
+ kForbidLeadingExpansion = 0 << 1,
+ kAllowLeadingExpansion = 1 << 1,
+ };
+
+ enum TextCodePath { kAuto = 0, kForceSimple = 1, kForceComplex = 2 };
+
+ typedef unsigned ExpansionBehavior;
+
+ TextRun(const LChar* c,
+ unsigned len,
+ float xpos = 0,
+ float expansion = 0,
+ ExpansionBehavior expansion_behavior = kAllowTrailingExpansion |
+ kForbidLeadingExpansion,
+ TextDirection direction = TextDirection::kLtr,
+ bool directional_override = false)
+ : characters_length_(len),
+ len_(len),
+ xpos_(xpos),
+ expansion_(expansion),
+ expansion_behavior_(expansion_behavior),
+ is8_bit_(true),
+ allow_tabs_(false),
+ direction_(static_cast<unsigned>(direction)),
+ directional_override_(directional_override),
+ disable_spacing_(false),
+ text_justify_(static_cast<unsigned>(TextJustify::kAuto)),
+ normalize_space_(false),
+ tab_size_(0) {
+ data_.characters8 = c;
+ }
+
+ TextRun(const UChar* c,
+ unsigned len,
+ float xpos = 0,
+ float expansion = 0,
+ ExpansionBehavior expansion_behavior = kAllowTrailingExpansion |
+ kForbidLeadingExpansion,
+ TextDirection direction = TextDirection::kLtr,
+ bool directional_override = false)
+ : characters_length_(len),
+ len_(len),
+ xpos_(xpos),
+ expansion_(expansion),
+ expansion_behavior_(expansion_behavior),
+ is8_bit_(false),
+ allow_tabs_(false),
+ direction_(static_cast<unsigned>(direction)),
+ directional_override_(directional_override),
+ disable_spacing_(false),
+ text_justify_(static_cast<unsigned>(TextJustify::kAuto)),
+ normalize_space_(false),
+ tab_size_(0) {
+ data_.characters16 = c;
+ }
+
+ TextRun(const StringView& string,
+ float xpos = 0,
+ float expansion = 0,
+ ExpansionBehavior expansion_behavior = kAllowTrailingExpansion |
+ kForbidLeadingExpansion,
+ TextDirection direction = TextDirection::kLtr,
+ bool directional_override = false)
+ : characters_length_(string.length()),
+ len_(string.length()),
+ xpos_(xpos),
+ expansion_(expansion),
+ expansion_behavior_(expansion_behavior),
+ allow_tabs_(false),
+ direction_(static_cast<unsigned>(direction)),
+ directional_override_(directional_override),
+ disable_spacing_(false),
+ text_justify_(static_cast<unsigned>(TextJustify::kAuto)),
+ normalize_space_(false),
+ tab_size_(0) {
+ if (!characters_length_) {
+ is8_bit_ = true;
+ data_.characters8 = nullptr;
+ } else if (string.Is8Bit()) {
+ data_.characters8 = string.Characters8();
+ is8_bit_ = true;
+ } else {
+ data_.characters16 = string.Characters16();
+ is8_bit_ = false;
+ }
+ }
+
+ TextRun SubRun(unsigned start_offset, unsigned length) const {
+ DCHECK_LT(start_offset, len_);
+
+ TextRun result = *this;
+
+ if (Is8Bit()) {
+ result.SetText(Data8(start_offset), length);
+ return result;
+ }
+ result.SetText(Data16(start_offset), length);
+ return result;
+ }
+
+ // Returns the start index of a sub run if it was created by |SubRun|.
+ // std::numeric_limits<unsigned>::max() if not a sub run.
+ unsigned IndexOfSubRun(const TextRun&) const;
+
+ UChar operator[](unsigned i) const {
+ SECURITY_DCHECK(i < len_);
+ return Is8Bit() ? data_.characters8[i] : data_.characters16[i];
+ }
+ const LChar* Data8(unsigned i) const {
+ SECURITY_DCHECK(i < len_);
+ DCHECK(Is8Bit());
+ return &data_.characters8[i];
+ }
+ const UChar* Data16(unsigned i) const {
+ SECURITY_DCHECK(i < len_);
+ DCHECK(!Is8Bit());
+ return &data_.characters16[i];
+ }
+
+ const LChar* Characters8() const {
+ DCHECK(Is8Bit());
+ return data_.characters8;
+ }
+ const UChar* Characters16() const {
+ DCHECK(!Is8Bit());
+ return data_.characters16;
+ }
+
+ UChar32 CodepointAt(unsigned i) const {
+ SECURITY_DCHECK(i < len_);
+ if (Is8Bit())
+ return (*this)[i];
+ UChar32 codepoint;
+ U16_GET(Characters16(), 0, i, len_, codepoint);
+ return codepoint;
+ }
+
+ UChar32 CodepointAtAndNext(unsigned& i) const {
+ SECURITY_DCHECK(i < len_);
+ if (Is8Bit())
+ return (*this)[i++];
+ UChar32 codepoint;
+ U16_NEXT(Characters16(), i, len_, codepoint);
+ return codepoint;
+ }
+
+ const void* Bytes() const { return data_.bytes_; }
+
+ bool Is8Bit() const { return is8_bit_; }
+ unsigned length() const { return len_; }
+ unsigned CharactersLength() const { return characters_length_; }
+
+ bool NormalizeSpace() const { return normalize_space_; }
+ void SetNormalizeSpace(bool normalize_space) {
+ normalize_space_ = normalize_space;
+ }
+
+ void SetText(const LChar* c, unsigned len) {
+ data_.characters8 = c;
+ len_ = len;
+ is8_bit_ = true;
+ }
+ void SetText(const UChar* c, unsigned len) {
+ data_.characters16 = c;
+ len_ = len;
+ is8_bit_ = false;
+ }
+ void SetText(const String&);
+ void SetCharactersLength(unsigned characters_length) {
+ characters_length_ = characters_length;
+ }
+
+ void SetExpansionBehavior(ExpansionBehavior behavior) {
+ expansion_behavior_ = behavior;
+ }
+
+ bool AllowTabs() const { return allow_tabs_; }
+ TabSize GetTabSize() const { return tab_size_; }
+ void SetTabSize(bool, TabSize);
+
+ float XPos() const { return xpos_; }
+ void SetXPos(float x_pos) { xpos_ = x_pos; }
+ float Expansion() const { return expansion_; }
+ void SetExpansion(float expansion) { expansion_ = expansion; }
+ bool AllowsLeadingExpansion() const {
+ return expansion_behavior_ & kAllowLeadingExpansion;
+ }
+ bool AllowsTrailingExpansion() const {
+ return expansion_behavior_ & kAllowTrailingExpansion;
+ }
+ TextDirection Direction() const {
+ return static_cast<TextDirection>(direction_);
+ }
+ bool Rtl() const { return Direction() == TextDirection::kRtl; }
+ bool Ltr() const { return Direction() == TextDirection::kLtr; }
+ bool DirectionalOverride() const { return directional_override_; }
+ bool SpacingDisabled() const { return disable_spacing_; }
+
+ void DisableSpacing() { disable_spacing_ = true; }
+ void SetDirection(TextDirection direction) {
+ direction_ = static_cast<unsigned>(direction);
+ }
+ void SetDirectionalOverride(bool override) {
+ directional_override_ = override;
+ }
+
+ void SetTextJustify(TextJustify text_justify) {
+ text_justify_ = static_cast<unsigned>(text_justify);
+ }
+ TextJustify GetTextJustify() const {
+ return static_cast<TextJustify>(text_justify_);
+ }
+
+ // Up-converts to UTF-16 as needed and normalizes spaces and Unicode control
+ // characters as per the CSS Text Module Level 3 specification.
+ // https://drafts.csswg.org/css-text-3/#white-space-processing
+ std::unique_ptr<UChar[]> NormalizedUTF16(unsigned* result_length) const;
+
+ private:
+ union {
+ const LChar* characters8;
+ const UChar* characters16;
+ const void* bytes_;
+ } data_;
+ // Marks the end of the characters buffer. Default equals to m_len.
+ unsigned characters_length_;
+ unsigned len_;
+
+ // m_xpos is the x position relative to the left start of the text line, not
+ // relative to the left start of the containing block. In the case of right
+ // alignment or center alignment, left start of the text line is not the same
+ // as left start of the containing block.
+ float xpos_;
+
+ float expansion_;
+ ExpansionBehavior expansion_behavior_ : 2;
+ unsigned is8_bit_ : 1;
+ unsigned allow_tabs_ : 1;
+ unsigned direction_ : 1;
+ // Was this direction set by an override character.
+ unsigned directional_override_ : 1;
+ unsigned disable_spacing_ : 1;
+ unsigned text_justify_ : 2;
+ unsigned normalize_space_ : 1;
+ TabSize tab_size_;
+};
+
+inline void TextRun::SetTabSize(bool allow, TabSize size) {
+ allow_tabs_ = allow;
+ tab_size_ = size;
+}
+
+// Container for parameters needed to paint TextRun.
+struct TextRunPaintInfo {
+ STACK_ALLOCATED();
+
+ public:
+ explicit TextRunPaintInfo(const TextRun& r)
+ : run(r), from(0), to(r.length()) {}
+
+ const TextRun& run;
+ unsigned from;
+ unsigned to;
+ FloatRect bounds;
+};
+
+} // namespace blink
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/text/text_run_iterator.h b/chromium/third_party/blink/renderer/platform/text/text_run_iterator.h
new file mode 100644
index 00000000000..4e64b830406
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/text_run_iterator.h
@@ -0,0 +1,75 @@
+// Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_TEXT_RUN_ITERATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_TEXT_RUN_ITERATOR_H_
+
+#include "third_party/blink/renderer/platform/text/text_run.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+class TextRunIterator {
+ DISALLOW_NEW();
+
+ public:
+ TextRunIterator() : text_run_(nullptr), offset_(0), length_(0) {}
+
+ TextRunIterator(const TextRun* text_run, unsigned offset)
+ : text_run_(text_run), offset_(offset), length_(text_run_->length()) {}
+
+ TextRunIterator(const TextRunIterator& other)
+ : text_run_(other.text_run_),
+ offset_(other.offset_),
+ length_(text_run_->length()) {}
+
+ unsigned Offset() const { return offset_; }
+ void Increment() { offset_++; }
+ bool AtEnd() const { return offset_ >= length_; }
+ UChar Current() const { return (*text_run_)[offset_]; }
+ WTF::Unicode::CharDirection Direction() const {
+ return AtEnd() ? WTF::Unicode::kOtherNeutral
+ : WTF::Unicode::Direction(Current());
+ }
+ bool AtParagraphSeparator() const { return Current() == '\n'; }
+
+ bool operator==(const TextRunIterator& other) {
+ return offset_ == other.offset_ && text_run_ == other.text_run_;
+ }
+
+ bool operator!=(const TextRunIterator& other) { return !operator==(other); }
+
+ private:
+ const TextRun* text_run_;
+ unsigned offset_;
+ unsigned length_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_TEXT_RUN_ITERATOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/text/text_run_test.cc b/chromium/third_party/blink/renderer/platform/text/text_run_test.cc
new file mode 100644
index 00000000000..4dd4b1e0ede
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/text_run_test.cc
@@ -0,0 +1,36 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/text/text_run.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+#if defined(THREAD_SANITIZER)
+#define MAYBE_IndexOfSubRun DISABLED_IndexOfSubRun // https://crbug.com/830648
+#else
+#define MAYBE_IndexOfSubRun IndexOfSubRun
+#endif
+
+TEST(TextRunTest, MAYBE_IndexOfSubRun) {
+ TextRun run(String("1234567890"));
+ EXPECT_EQ(0u, run.IndexOfSubRun(run.SubRun(0, 4)));
+ EXPECT_EQ(4u, run.IndexOfSubRun(run.SubRun(4, 4)));
+ EXPECT_EQ(6u, run.IndexOfSubRun(run.SubRun(6, 4)));
+ const unsigned kNotSubRun = std::numeric_limits<unsigned>::max();
+ EXPECT_EQ(kNotSubRun, run.IndexOfSubRun(run.SubRun(7, 4)));
+ EXPECT_EQ(kNotSubRun, run.IndexOfSubRun(TextRun(String("1"))));
+ EXPECT_EQ(kNotSubRun, run.IndexOfSubRun(TextRun(String(u"1"))));
+
+ TextRun run16(String(u"1234567890"));
+ EXPECT_EQ(0u, run16.IndexOfSubRun(run16.SubRun(0, 4)));
+ EXPECT_EQ(4u, run16.IndexOfSubRun(run16.SubRun(4, 4)));
+ EXPECT_EQ(6u, run16.IndexOfSubRun(run16.SubRun(6, 4)));
+ EXPECT_EQ(kNotSubRun, run16.IndexOfSubRun(run16.SubRun(7, 4)));
+ EXPECT_EQ(kNotSubRun, run16.IndexOfSubRun(TextRun(String("1"))));
+ EXPECT_EQ(kNotSubRun, run16.IndexOfSubRun(TextRun(String(u"1"))));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/text_stream.cc b/chromium/third_party/blink/renderer/platform/text/text_stream.cc
new file mode 100644
index 00000000000..cc833aa1c44
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/text_stream.cc
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2004, 2008, 2010 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/text/text_stream.h"
+
+#include "third_party/blink/renderer/platform/geometry/float_point.h"
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/geometry/float_size.h"
+#include "third_party/blink/renderer/platform/geometry/int_point.h"
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/geometry/layout_point.h"
+#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
+#include "third_party/blink/renderer/platform/geometry/layout_size.h"
+#include "third_party/blink/renderer/platform/layout_unit.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+// large enough for any integer or floating point value in string format,
+// including trailing null character
+static const size_t kPrintBufferSize = 100;
+
+static inline bool HasFractions(double val) {
+ // We use 0.011 to more than match the number of significant digits we print
+ // out when dumping the render tree.
+ static const double kEpsilon = 0.011;
+ int ival = static_cast<int>(round(val));
+ double dval = static_cast<double>(ival);
+ return fabs(val - dval) > kEpsilon;
+}
+
+TextStream& TextStream::operator<<(bool b) {
+ return *this << (b ? "1" : "0");
+}
+
+TextStream& TextStream::operator<<(int i) {
+ text_.AppendNumber(i);
+ return *this;
+}
+
+TextStream& TextStream::operator<<(unsigned i) {
+ text_.AppendNumber(i);
+ return *this;
+}
+
+TextStream& TextStream::operator<<(long i) {
+ text_.AppendNumber(i);
+ return *this;
+}
+
+TextStream& TextStream::operator<<(unsigned long i) {
+ text_.AppendNumber(i);
+ return *this;
+}
+
+TextStream& TextStream::operator<<(long long i) {
+ text_.AppendNumber(i);
+ return *this;
+}
+
+TextStream& TextStream::operator<<(unsigned long long i) {
+ text_.AppendNumber(i);
+ return *this;
+}
+
+TextStream& TextStream::operator<<(float f) {
+ text_.Append(String::NumberToStringFixedWidth(f, 2));
+ return *this;
+}
+
+TextStream& TextStream::operator<<(double d) {
+ text_.Append(String::NumberToStringFixedWidth(d, 2));
+ return *this;
+}
+
+TextStream& TextStream::operator<<(const char* string) {
+ text_.Append(string);
+ return *this;
+}
+
+TextStream& TextStream::operator<<(const void* p) {
+ char buffer[kPrintBufferSize];
+ snprintf(buffer, sizeof(buffer) - 1, "%p", p);
+ return *this << buffer;
+}
+
+TextStream& TextStream::operator<<(const String& string) {
+ text_.Append(string);
+ return *this;
+}
+
+TextStream& TextStream::operator<<(
+ const FormatNumberRespectingIntegers& number_to_format) {
+ if (HasFractions(number_to_format.value))
+ return *this << number_to_format.value;
+
+ text_.AppendNumber(static_cast<int>(round(number_to_format.value)));
+ return *this;
+}
+
+String TextStream::Release() {
+ String result = text_.ToString();
+ text_.Clear();
+ return result;
+}
+
+TextStream& operator<<(TextStream& ts, const IntRect& r) {
+ return ts << "at (" << r.X() << "," << r.Y() << ") size " << r.Width() << "x"
+ << r.Height();
+}
+
+TextStream& operator<<(TextStream& ts, const IntPoint& p) {
+ return ts << "(" << p.X() << "," << p.Y() << ")";
+}
+
+TextStream& operator<<(TextStream& ts, const FloatPoint& p) {
+ ts << "(" << TextStream::FormatNumberRespectingIntegers(p.X());
+ ts << "," << TextStream::FormatNumberRespectingIntegers(p.Y());
+ ts << ")";
+ return ts;
+}
+
+TextStream& operator<<(TextStream& ts, const FloatSize& s) {
+ ts << "width=" << TextStream::FormatNumberRespectingIntegers(s.Width());
+ ts << " height=" << TextStream::FormatNumberRespectingIntegers(s.Height());
+ return ts;
+}
+
+TextStream& operator<<(TextStream& ts, const FloatRect& r) {
+ ts << "at (" << TextStream::FormatNumberRespectingIntegers(r.X());
+ ts << "," << TextStream::FormatNumberRespectingIntegers(r.Y());
+ ts << ") size " << TextStream::FormatNumberRespectingIntegers(r.Width());
+ ts << "x" << TextStream::FormatNumberRespectingIntegers(r.Height());
+ return ts;
+}
+
+TextStream& operator<<(TextStream& ts, const LayoutUnit& unit) {
+ return ts << TextStream::FormatNumberRespectingIntegers(unit.ToDouble());
+}
+
+TextStream& operator<<(TextStream& ts, const LayoutPoint& point) {
+ return ts << FloatPoint(point);
+}
+
+TextStream& operator<<(TextStream& ts, const LayoutRect& rect) {
+ return ts << FloatRect(rect);
+}
+
+TextStream& operator<<(TextStream& ts, const LayoutSize& size) {
+ return ts << FloatSize(size);
+}
+
+void WriteIndent(TextStream& ts, int indent) {
+ for (int i = 0; i != indent; ++i)
+ ts << " ";
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/text_stream.h b/chromium/third_party/blink/renderer/platform/text/text_stream.h
new file mode 100644
index 00000000000..5c7c0c0a474
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/text_stream.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2004, 2008 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_TEXT_STREAM_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_TEXT_STREAM_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class IntPoint;
+class IntRect;
+class FloatPoint;
+class FloatRect;
+class FloatSize;
+class LayoutUnit;
+class LayoutPoint;
+class LayoutRect;
+class LayoutSize;
+
+class PLATFORM_EXPORT TextStream final {
+ STACK_ALLOCATED();
+
+ public:
+ struct FormatNumberRespectingIntegers {
+ FormatNumberRespectingIntegers(double number) : value(number) {}
+ double value;
+ };
+
+ TextStream& operator<<(bool);
+ TextStream& operator<<(int);
+ TextStream& operator<<(unsigned);
+ TextStream& operator<<(long);
+ TextStream& operator<<(unsigned long);
+ TextStream& operator<<(long long);
+ TextStream& operator<<(unsigned long long);
+ TextStream& operator<<(float);
+ TextStream& operator<<(double);
+ TextStream& operator<<(const char*);
+ TextStream& operator<<(const void*);
+ TextStream& operator<<(const String&);
+ TextStream& operator<<(const FormatNumberRespectingIntegers&);
+
+ String Release();
+
+ private:
+ StringBuilder text_;
+};
+
+PLATFORM_EXPORT TextStream& operator<<(TextStream&, const IntPoint&);
+PLATFORM_EXPORT TextStream& operator<<(TextStream&, const IntRect&);
+PLATFORM_EXPORT TextStream& operator<<(TextStream&, const FloatPoint&);
+PLATFORM_EXPORT TextStream& operator<<(TextStream&, const FloatSize&);
+PLATFORM_EXPORT TextStream& operator<<(TextStream&, const FloatRect&);
+PLATFORM_EXPORT TextStream& operator<<(TextStream&, const LayoutUnit&);
+PLATFORM_EXPORT TextStream& operator<<(TextStream&, const LayoutPoint&);
+PLATFORM_EXPORT TextStream& operator<<(TextStream&, const LayoutRect&);
+PLATFORM_EXPORT TextStream& operator<<(TextStream&, const LayoutSize&);
+
+PLATFORM_EXPORT void WriteIndent(TextStream&, int indent);
+
+template <typename Item>
+TextStream& operator<<(TextStream& ts, const Vector<Item>& vector) {
+ ts << "[";
+
+ unsigned size = vector.size();
+ for (unsigned i = 0; i < size; ++i) {
+ ts << vector[i];
+ if (i < size - 1)
+ ts << ", ";
+ }
+
+ ts << "]";
+ return ts;
+}
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/text/truncation.h b/chromium/third_party/blink/renderer/platform/text/truncation.h
new file mode 100644
index 00000000000..b2216691758
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/truncation.h
@@ -0,0 +1,27 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_TRUNCATION_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_TRUNCATION_H_
+
+#include <climits>
+
+namespace blink {
+
+// The two truncation values below are used as tokens representing truncation
+// state for a text fragment (in LayoutNG) or text box (in legacy layout), are
+// intended to be relative to |m_start|. They are set directly into
+// |m_truncation|. In the case where there is some truncation of the text but it
+// is not full, |m_truncation| is set to the character offset from |m_start|
+// representing the characters that are not truncated.
+//
+// Thus the maximum possible length of the text displayed before an ellipsis in
+// a single NGTextFragment or InlineTextBox is |USHRT_MAX - 2| to allow for the
+// no-truncation and full-truncation states.
+const unsigned short kCNoTruncation = USHRT_MAX;
+const unsigned short kCFullTruncation = USHRT_MAX - 1;
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_TRUNCATION_H_
diff --git a/chromium/third_party/blink/renderer/platform/text/unicode_bidi.h b/chromium/third_party/blink/renderer/platform/text/unicode_bidi.h
new file mode 100644
index 00000000000..75c53f97bc6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/unicode_bidi.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2011 Google, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_UNICODE_BIDI_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_UNICODE_BIDI_H_
+
+namespace blink {
+
+enum class UnicodeBidi : unsigned {
+ kNormal,
+ kEmbed,
+ kBidiOverride,
+ kIsolate,
+ kPlaintext,
+ kIsolateOverride,
+};
+
+inline bool IsIsolated(const UnicodeBidi& unicode_bidi) {
+ return unicode_bidi == UnicodeBidi::kIsolate ||
+ unicode_bidi == UnicodeBidi::kIsolateOverride ||
+ unicode_bidi == UnicodeBidi::kPlaintext;
+}
+
+inline bool IsOverride(UnicodeBidi unicode_bidi) {
+ return unicode_bidi == UnicodeBidi::kBidiOverride ||
+ unicode_bidi == UnicodeBidi::kIsolateOverride;
+}
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/text/unicode_range.cc b/chromium/third_party/blink/renderer/platform/text/unicode_range.cc
new file mode 100644
index 00000000000..bc7eabf83a3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/unicode_range.cc
@@ -0,0 +1,436 @@
+/*
+ * Copyright (C) 2007 Apple Computer, Inc.
+ *
+ * Portions are Copyright (C) 1998 Netscape Communications Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Alternatively, the contents of this file may be used under the terms
+ * of either the Mozilla Public License Version 1.1, found at
+ * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
+ * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
+ * (the "GPL"), in which case the provisions of the MPL or the GPL are
+ * applicable instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of one of those two
+ * licenses (the MPL or the GPL) and not to allow others to use your
+ * version of this file under the LGPL, indicate your decision by
+ * deletingthe provisions above and replace them with the notice and
+ * other provisions required by the MPL or the GPL, as the case may be.
+ * If you do not delete the provisions above, a recipient may use your
+ * version of this file under any of the LGPL, the MPL or the GPL.
+ */
+
+#include "third_party/blink/renderer/platform/text/unicode_range.h"
+
+namespace blink {
+
+/**********************************************************************
+ * Unicode subranges as defined in unicode 3.0
+ * x-western, x-central-euro, tr, x-baltic -> latin
+ * 0000 - 036f
+ * 1e00 - 1eff
+ * 2000 - 206f (general punctuation)
+ * 20a0 - 20cf (currency symbols)
+ * 2100 - 214f (letterlike symbols)
+ * 2150 - 218f (Number Forms)
+ * el -> greek
+ * 0370 - 03ff
+ * 1f00 - 1fff
+ * x-cyrillic -> cyrillic
+ * 0400 - 04ff
+ * he -> hebrew
+ * 0590 - 05ff
+ * ar -> arabic
+ * 0600 - 06ff
+ * fb50 - fdff (arabic presentation forms)
+ * fe70 - feff (arabic presentation forms b)
+ * th - thai
+ * 0e00 - 0e7f
+ * ko -> korean
+ * ac00 - d7af (hangul Syllables)
+ * 1100 - 11ff (jamo)
+ * 3130 - 318f (hangul compatibility jamo)
+ * ja
+ * 3040 - 309f (hiragana)
+ * 30a0 - 30ff (katakana)
+ * zh-CN
+ * zh-TW
+ *
+ * CJK
+ * 3100 - 312f (bopomofo)
+ * 31a0 - 31bf (bopomofo extended)
+ * 3000 - 303f (CJK Symbols and Punctuation)
+ * 2e80 - 2eff (CJK radicals supplement)
+ * 2f00 - 2fdf (Kangxi Radicals)
+ * 2ff0 - 2fff (Ideographic Description Characters)
+ * 3190 - 319f (kanbun)
+ * 3200 - 32ff (Enclosed CJK letters and Months)
+ * 3300 - 33ff (CJK compatibility)
+ * 3400 - 4dbf (CJK Unified Ideographs Extension A)
+ * 4e00 - 9faf (CJK Unified Ideographs)
+ * f900 - fa5f (CJK Compatibility Ideographs)
+ * fe30 - fe4f (CJK compatibility Forms)
+ * ff00 - ffef (halfwidth and fullwidth forms)
+ *
+ * Armenian
+ * 0530 - 058f
+ * Sriac
+ * 0700 - 074f
+ * Thaana
+ * 0780 - 07bf
+ * Devanagari
+ * 0900 - 097f
+ * Bengali
+ * 0980 - 09ff
+ * Gurmukhi
+ * 0a00 - 0a7f
+ * Gujarati
+ * 0a80 - 0aff
+ * Oriya
+ * 0b00 - 0b7f
+ * Tamil
+ * 0b80 - 0bff
+ * Telugu
+ * 0c00 - 0c7f
+ * Kannada
+ * 0c80 - 0cff
+ * Malayalam
+ * 0d00 - 0d7f
+ * Sinhala
+ * 0d80 - 0def
+ * Lao
+ * 0e80 - 0eff
+ * Tibetan
+ * 0f00 - 0fbf
+ * Myanmar
+ * 1000 - 109f
+ * Georgian
+ * 10a0 - 10ff
+ * Ethiopic
+ * 1200 - 137f
+ * Cherokee
+ * 13a0 - 13ff
+ * Canadian Aboriginal Syllabics
+ * 1400 - 167f
+ * Ogham
+ * 1680 - 169f
+ * Runic
+ * 16a0 - 16ff
+ * Khmer
+ * 1780 - 17ff
+ * Mongolian
+ * 1800 - 18af
+ * Misc - superscripts and subscripts
+ * 2070 - 209f
+ * Misc - Combining Diacritical Marks for Symbols
+ * 20d0 - 20ff
+ * Misc - Arrows
+ * 2190 - 21ff
+ * Misc - Mathematical Operators
+ * 2200 - 22ff
+ * Misc - Miscellaneous Technical
+ * 2300 - 23ff
+ * Misc - Control picture
+ * 2400 - 243f
+ * Misc - Optical character recognition
+ * 2440 - 2450
+ * Misc - Enclose Alphanumerics
+ * 2460 - 24ff
+ * Misc - Box Drawing
+ * 2500 - 257f
+ * Misc - Block Elements
+ * 2580 - 259f
+ * Misc - Geometric Shapes
+ * 25a0 - 25ff
+ * Misc - Miscellaneous Symbols
+ * 2600 - 267f
+ * Misc - Dingbats
+ * 2700 - 27bf
+ * Misc - Braille Patterns
+ * 2800 - 28ff
+ * Yi Syllables
+ * a000 - a48f
+ * Yi radicals
+ * a490 - a4cf
+ * Alphabetic Presentation Forms
+ * fb00 - fb4f
+ * Misc - Combining half Marks
+ * fe20 - fe2f
+ * Misc - small form variants
+ * fe50 - fe6f
+ * Misc - Specials
+ * fff0 - ffff
+ *********************************************************************/
+
+static const unsigned kCNumSubTables = 9;
+static const unsigned kCSubTableSize = 16;
+
+static const unsigned char
+ kGUnicodeSubrangeTable[kCNumSubTables][kCSubTableSize] = {
+ {
+ // table for X---
+ kCRangeTableBase + 1, // u0xxx
+ kCRangeTableBase + 2, // u1xxx
+ kCRangeTableBase + 3, // u2xxx
+ kCRangeSetCJK, // u3xxx
+ kCRangeSetCJK, // u4xxx
+ kCRangeSetCJK, // u5xxx
+ kCRangeSetCJK, // u6xxx
+ kCRangeSetCJK, // u7xxx
+ kCRangeSetCJK, // u8xxx
+ kCRangeSetCJK, // u9xxx
+ kCRangeTableBase + 4, // uaxxx
+ kCRangeKorean, // ubxxx
+ kCRangeKorean, // ucxxx
+ kCRangeTableBase + 5, // udxxx
+ kCRangePrivate, // uexxx
+ kCRangeTableBase + 6 // ufxxx
+ },
+ {
+ // table for 0X--
+ kCRangeSetLatin, // u00xx
+ kCRangeSetLatin, // u01xx
+ kCRangeSetLatin, // u02xx
+ kCRangeGreek, // u03xx XXX 0300-036f is in fact
+ // cRangeCombiningDiacriticalMarks
+ kCRangeCyrillic, // u04xx
+ kCRangeTableBase +
+ 7, // u05xx, includes Cyrillic supplement, Hebrew, and Armenian
+ kCRangeArabic, // u06xx
+ kCRangeTertiaryTable, // u07xx
+ kCRangeUnassigned, // u08xx
+ kCRangeTertiaryTable, // u09xx
+ kCRangeTertiaryTable, // u0axx
+ kCRangeTertiaryTable, // u0bxx
+ kCRangeTertiaryTable, // u0cxx
+ kCRangeTertiaryTable, // u0dxx
+ kCRangeTertiaryTable, // u0exx
+ kCRangeTibetan, // u0fxx
+ },
+ {
+ // table for 1x--
+ kCRangeTertiaryTable, // u10xx
+ kCRangeKorean, // u11xx
+ kCRangeEthiopic, // u12xx
+ kCRangeTertiaryTable, // u13xx
+ kCRangeCanadian, // u14xx
+ kCRangeCanadian, // u15xx
+ kCRangeTertiaryTable, // u16xx
+ kCRangeKhmer, // u17xx
+ kCRangeMongolian, // u18xx
+ kCRangeUnassigned, // u19xx
+ kCRangeUnassigned, // u1axx
+ kCRangeUnassigned, // u1bxx
+ kCRangeUnassigned, // u1cxx
+ kCRangeUnassigned, // u1dxx
+ kCRangeSetLatin, // u1exx
+ kCRangeGreek, // u1fxx
+ },
+ {
+ // table for 2x--
+ kCRangeSetLatin, // u20xx
+ kCRangeSetLatin, // u21xx
+ kCRangeMathOperators, // u22xx
+ kCRangeMiscTechnical, // u23xx
+ kCRangeControlOpticalEnclose, // u24xx
+ kCRangeBoxBlockGeometrics, // u25xx
+ kCRangeMiscSymbols, // u26xx
+ kCRangeDingbats, // u27xx
+ kCRangeBraillePattern, // u28xx
+ kCRangeUnassigned, // u29xx
+ kCRangeUnassigned, // u2axx
+ kCRangeUnassigned, // u2bxx
+ kCRangeUnassigned, // u2cxx
+ kCRangeUnassigned, // u2dxx
+ kCRangeSetCJK, // u2exx
+ kCRangeSetCJK, // u2fxx
+ },
+ {
+ // table for ax--
+ kCRangeYi, // ua0xx
+ kCRangeYi, // ua1xx
+ kCRangeYi, // ua2xx
+ kCRangeYi, // ua3xx
+ kCRangeYi, // ua4xx
+ kCRangeUnassigned, // ua5xx
+ kCRangeUnassigned, // ua6xx
+ kCRangeUnassigned, // ua7xx
+ kCRangeUnassigned, // ua8xx
+ kCRangeUnassigned, // ua9xx
+ kCRangeUnassigned, // uaaxx
+ kCRangeUnassigned, // uabxx
+ kCRangeKorean, // uacxx
+ kCRangeKorean, // uadxx
+ kCRangeKorean, // uaexx
+ kCRangeKorean, // uafxx
+ },
+ {
+ // table for dx--
+ kCRangeKorean, // ud0xx
+ kCRangeKorean, // ud1xx
+ kCRangeKorean, // ud2xx
+ kCRangeKorean, // ud3xx
+ kCRangeKorean, // ud4xx
+ kCRangeKorean, // ud5xx
+ kCRangeKorean, // ud6xx
+ kCRangeKorean, // ud7xx
+ kCRangeSurrogate, // ud8xx
+ kCRangeSurrogate, // ud9xx
+ kCRangeSurrogate, // udaxx
+ kCRangeSurrogate, // udbxx
+ kCRangeSurrogate, // udcxx
+ kCRangeSurrogate, // uddxx
+ kCRangeSurrogate, // udexx
+ kCRangeSurrogate, // udfxx
+ },
+ {
+ // table for fx--
+ kCRangePrivate, // uf0xx
+ kCRangePrivate, // uf1xx
+ kCRangePrivate, // uf2xx
+ kCRangePrivate, // uf3xx
+ kCRangePrivate, // uf4xx
+ kCRangePrivate, // uf5xx
+ kCRangePrivate, // uf6xx
+ kCRangePrivate, // uf7xx
+ kCRangePrivate, // uf8xx
+ kCRangeSetCJK, // uf9xx
+ kCRangeSetCJK, // ufaxx
+ kCRangeArabic, // ufbxx, includes alphabic presentation form
+ kCRangeArabic, // ufcxx
+ kCRangeArabic, // ufdxx
+ kCRangeArabic, // ufexx, includes Combining half marks,
+ // CJK compatibility forms,
+ // CJK compatibility forms,
+ // small form variants
+ kCRangeTableBase +
+ 8, // uffxx, halfwidth and fullwidth forms, includes Specials
+ },
+ {
+ // table for 0x0500 - 0x05ff
+ kCRangeCyrillic, // u050x
+ kCRangeCyrillic, // u051x
+ kCRangeCyrillic, // u052x
+ kCRangeArmenian, // u053x
+ kCRangeArmenian, // u054x
+ kCRangeArmenian, // u055x
+ kCRangeArmenian, // u056x
+ kCRangeArmenian, // u057x
+ kCRangeArmenian, // u058x
+ kCRangeHebrew, // u059x
+ kCRangeHebrew, // u05ax
+ kCRangeHebrew, // u05bx
+ kCRangeHebrew, // u05cx
+ kCRangeHebrew, // u05dx
+ kCRangeHebrew, // u05ex
+ kCRangeHebrew, // u05fx
+ },
+ {
+ // table for 0xff00 - 0xffff
+ kCRangeSetCJK, // uff0x, fullwidth latin
+ kCRangeSetCJK, // uff1x, fullwidth latin
+ kCRangeSetCJK, // uff2x, fullwidth latin
+ kCRangeSetCJK, // uff3x, fullwidth latin
+ kCRangeSetCJK, // uff4x, fullwidth latin
+ kCRangeSetCJK, // uff5x, fullwidth latin
+ kCRangeSetCJK, // uff6x, halfwidth katakana
+ kCRangeSetCJK, // uff7x, halfwidth katakana
+ kCRangeSetCJK, // uff8x, halfwidth katakana
+ kCRangeSetCJK, // uff9x, halfwidth katakana
+ kCRangeSetCJK, // uffax, halfwidth hangul jamo
+ kCRangeSetCJK, // uffbx, halfwidth hangul jamo
+ kCRangeSetCJK, // uffcx, halfwidth hangul jamo
+ kCRangeSetCJK, // uffdx, halfwidth hangul jamo
+ kCRangeSetCJK, // uffex, fullwidth symbols
+ kCRangeSpecials, // ufffx, Specials
+ },
+};
+
+// Most scripts between U+0700 and U+16FF are assigned a chunk of 128 (0x80)
+// code points so that the number of entries in the tertiary range
+// table for that range is obtained by dividing (0x1700 - 0x0700) by 128.
+// Exceptions: Ethiopic, Tibetan, Hangul Jamo and Canadian aboriginal
+// syllabaries take multiple chunks and Ogham and Runic share a single chunk.
+static const unsigned kCTertiaryTableSize = ((0x1700 - 0x0700) / 0x80);
+
+static const unsigned char kGUnicodeTertiaryRangeTable[kCTertiaryTableSize] = {
+ // table for 0x0700 - 0x1600
+ kCRangeSyriac, // u070x
+ kCRangeThaana, // u078x
+ kCRangeUnassigned, // u080x place holder(resolved in the 2ndary tab.)
+ kCRangeUnassigned, // u088x place holder(resolved in the 2ndary tab.)
+ kCRangeDevanagari, // u090x
+ kCRangeBengali, // u098x
+ kCRangeGurmukhi, // u0a0x
+ kCRangeGujarati, // u0a8x
+ kCRangeOriya, // u0b0x
+ kCRangeTamil, // u0b8x
+ kCRangeTelugu, // u0c0x
+ kCRangeKannada, // u0c8x
+ kCRangeMalayalam, // u0d0x
+ kCRangeSinhala, // u0d8x
+ kCRangeThai, // u0e0x
+ kCRangeLao, // u0e8x
+ kCRangeTibetan, // u0f0x place holder(resolved in the 2ndary tab.)
+ kCRangeTibetan, // u0f8x place holder(resolved in the 2ndary tab.)
+ kCRangeMyanmar, // u100x
+ kCRangeGeorgian, // u108x
+ kCRangeKorean, // u110x place holder(resolved in the 2ndary tab.)
+ kCRangeKorean, // u118x place holder(resolved in the 2ndary tab.)
+ kCRangeEthiopic, // u120x place holder(resolved in the 2ndary tab.)
+ kCRangeEthiopic, // u128x place holder(resolved in the 2ndary tab.)
+ kCRangeEthiopic, // u130x
+ kCRangeCherokee, // u138x
+ kCRangeCanadian, // u140x place holder(resolved in the 2ndary tab.)
+ kCRangeCanadian, // u148x place holder(resolved in the 2ndary tab.)
+ kCRangeCanadian, // u150x place holder(resolved in the 2ndary tab.)
+ kCRangeCanadian, // u158x place holder(resolved in the 2ndary tab.)
+ kCRangeCanadian, // u160x
+ kCRangeOghamRunic, // u168x this contains two scripts, Ogham & Runic
+};
+
+// A two level index is almost enough for locating a range, with the
+// exception of u03xx and u05xx. Since we don't really care about range for
+// combining diacritical marks in our font application, they are
+// not discriminated further. Future adoption of this method for other use
+// should be aware of this limitation. The implementation can be extended if
+// there is such a need.
+// For Indic, Southeast Asian scripts and some other scripts between
+// U+0700 and U+16FF, it's extended to the third level.
+unsigned FindCharUnicodeRange(UChar32 ch) {
+ if (ch >= 0xFFFF)
+ return 0;
+
+ unsigned range;
+
+ // search the first table
+ range = kGUnicodeSubrangeTable[0][ch >> 12];
+
+ if (range < kCRangeTableBase)
+ // we try to get a specific range
+ return range;
+
+ // otherwise, we have one more table to look at
+ range = kGUnicodeSubrangeTable[range - kCRangeTableBase][(ch & 0x0f00) >> 8];
+ if (range < kCRangeTableBase)
+ return range;
+ if (range < kCRangeTertiaryTable)
+ return kGUnicodeSubrangeTable[range - kCRangeTableBase][(ch & 0x00f0) >> 4];
+
+ // Yet another table to look at : U+0700 - U+16FF : 128 code point blocks
+ return kGUnicodeTertiaryRangeTable[(ch - 0x0700) >> 7];
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/unicode_range.h b/chromium/third_party/blink/renderer/platform/text/unicode_range.h
new file mode 100644
index 00000000000..083606e2401
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/unicode_range.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2007 Apple Computer, Inc.
+ *
+ * Portions are Copyright (C) 1998 Netscape Communications Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Alternatively, the contents of this file may be used under the terms
+ * of either the Mozilla Public License Version 1.1, found at
+ * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
+ * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
+ * (the "GPL"), in which case the provisions of the MPL or the GPL are
+ * applicable instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of one of those two
+ * licenses (the MPL or the GPL) and not to allow others to use your
+ * version of this file under the LGPL, indicate your decision by
+ * deletingthe provisions above and replace them with the notice and
+ * other provisions required by the MPL or the GPL, as the case may be.
+ * If you do not delete the provisions above, a recipient may use your
+ * version of this file under any of the LGPL, the MPL or the GPL.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_UNICODE_RANGE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_UNICODE_RANGE_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
+
+namespace blink {
+
+// The following constants define unicode subranges
+// values below cRangeNum must be continuous so that we can map to
+// a lang group directly.
+// All ranges we care about should fit within 32 bits.
+
+// Frequently used range definitions
+const unsigned char kCRangeCyrillic = 0;
+const unsigned char kCRangeGreek = 1;
+const unsigned char kCRangeTurkish = 2;
+const unsigned char kCRangeHebrew = 3;
+const unsigned char kCRangeArabic = 4;
+const unsigned char kCRangeBaltic = 5;
+const unsigned char kCRangeThai = 6;
+const unsigned char kCRangeKorean = 7;
+const unsigned char kCRangeJapanese = 8;
+const unsigned char kCRangeSChinese = 9;
+const unsigned char kCRangeTChinese = 10;
+const unsigned char kCRangeDevanagari = 11;
+const unsigned char kCRangeTamil = 12;
+const unsigned char kCRangeArmenian = 13;
+const unsigned char kCRangeBengali = 14;
+const unsigned char kCRangeCanadian = 15;
+const unsigned char kCRangeEthiopic = 16;
+const unsigned char kCRangeGeorgian = 17;
+const unsigned char kCRangeGujarati = 18;
+const unsigned char kCRangeGurmukhi = 19;
+const unsigned char kCRangeKhmer = 20;
+const unsigned char kCRangeMalayalam = 21;
+
+const unsigned char kCRangeSpecificItemNum = 22;
+
+// range/rangeSet grow to this place 22-29
+
+const unsigned char kCRangeSetStart =
+ 30; // range set definition starts from here
+const unsigned char kCRangeSetLatin = 30;
+const unsigned char kCRangeSetCJK = 31;
+const unsigned char kCRangeSetEnd = 31; // range set definition ends here
+
+// less frequently used range definition
+const unsigned char kCRangeSurrogate = 32;
+const unsigned char kCRangePrivate = 33;
+const unsigned char kCRangeMisc = 34;
+const unsigned char kCRangeUnassigned = 35;
+const unsigned char kCRangeSyriac = 36;
+const unsigned char kCRangeThaana = 37;
+const unsigned char kCRangeOriya = 38;
+const unsigned char kCRangeTelugu = 39;
+const unsigned char kCRangeKannada = 40;
+const unsigned char kCRangeSinhala = 41;
+const unsigned char kCRangeLao = 42;
+const unsigned char kCRangeTibetan = 43;
+const unsigned char kCRangeMyanmar = 44;
+const unsigned char kCRangeCherokee = 45;
+const unsigned char kCRangeOghamRunic = 46;
+const unsigned char kCRangeMongolian = 47;
+const unsigned char kCRangeMathOperators = 48;
+const unsigned char kCRangeMiscTechnical = 49;
+const unsigned char kCRangeControlOpticalEnclose = 50;
+const unsigned char kCRangeBoxBlockGeometrics = 51;
+const unsigned char kCRangeMiscSymbols = 52;
+const unsigned char kCRangeDingbats = 53;
+const unsigned char kCRangeBraillePattern = 54;
+const unsigned char kCRangeYi = 55;
+const unsigned char kCRangeCombiningDiacriticalMarks = 56;
+const unsigned char kCRangeSpecials = 57;
+
+const unsigned char kCRangeTableBase =
+ 128; // values over 127 are reserved for internal use only
+const unsigned char kCRangeTertiaryTable = 145; // leave room for 16 subtable
+ // indices (cRangeTableBase + 1
+ // .. cRangeTableBase + 16)
+
+PLATFORM_EXPORT unsigned FindCharUnicodeRange(UChar32);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_UNICODE_RANGE_H_
diff --git a/chromium/third_party/blink/renderer/platform/text/unicode_utilities.cc b/chromium/third_party/blink/renderer/platform/text/unicode_utilities.cc
new file mode 100644
index 00000000000..0fbdb1784c7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/unicode_utilities.cc
@@ -0,0 +1,434 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012 Apple Inc. All
+ * rights reserved.
+ * Copyright (C) 2005 Alexey Proskuryakov.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/text/unicode_utilities.h"
+
+#include <unicode/normalizer2.h>
+#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_buffer.h"
+
+namespace blink {
+
+enum VoicedSoundMarkType {
+ kNoVoicedSoundMark,
+ kVoicedSoundMark,
+ kSemiVoicedSoundMark
+};
+
+template <typename CharType>
+static inline CharType FoldQuoteMarkOrSoftHyphen(CharType c) {
+ switch (static_cast<UChar>(c)) {
+ case kHebrewPunctuationGershayimCharacter:
+ case kLeftDoubleQuotationMarkCharacter:
+ case kRightDoubleQuotationMarkCharacter:
+ return '"';
+ case kHebrewPunctuationGereshCharacter:
+ case kLeftSingleQuotationMarkCharacter:
+ case kRightSingleQuotationMarkCharacter:
+ return '\'';
+ case kSoftHyphenCharacter:
+ // Replace soft hyphen with an ignorable character so that their presence
+ // or absence will
+ // not affect string comparison.
+ return 0;
+ default:
+ return c;
+ }
+}
+
+void FoldQuoteMarksAndSoftHyphens(UChar* data, size_t length) {
+ for (size_t i = 0; i < length; ++i)
+ data[i] = FoldQuoteMarkOrSoftHyphen(data[i]);
+}
+
+void FoldQuoteMarksAndSoftHyphens(String& s) {
+ s.Replace(kHebrewPunctuationGereshCharacter, '\'');
+ s.Replace(kHebrewPunctuationGershayimCharacter, '"');
+ s.Replace(kLeftDoubleQuotationMarkCharacter, '"');
+ s.Replace(kLeftSingleQuotationMarkCharacter, '\'');
+ s.Replace(kRightDoubleQuotationMarkCharacter, '"');
+ s.Replace(kRightSingleQuotationMarkCharacter, '\'');
+ // Replace soft hyphen with an ignorable character so that their presence or
+ // absence will
+ // not affect string comparison.
+ s.Replace(kSoftHyphenCharacter, static_cast<UChar>('\0'));
+}
+
+static bool IsNonLatin1Separator(UChar32 character) {
+ DCHECK_GE(character, 256);
+ return U_GET_GC_MASK(character) &
+ (U_GC_S_MASK | U_GC_P_MASK | U_GC_Z_MASK | U_GC_CF_MASK);
+}
+
+bool IsSeparator(UChar32 character) {
+ // clang-format off
+ static const bool kLatin1SeparatorTable[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ // space ! " # $ % & ' ( ) * + , - . /
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ // : ; < = > ?
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
+ // @
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ // [ \ ] ^ _
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
+ // `
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ // { | } ~
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ // clang-format on
+ if (character < 256)
+ return kLatin1SeparatorTable[character];
+
+ return IsNonLatin1Separator(character);
+}
+
+// ICU's search ignores the distinction between small kana letters and ones
+// that are not small, and also characters that differ only in the voicing
+// marks when considering only primary collation strength differences.
+// This is not helpful for end users, since these differences make words
+// distinct, so for our purposes we need these to be considered.
+// The Unicode folks do not think the collation algorithm should be
+// changed. To work around this, we would like to tailor the ICU searcher,
+// but we can't get that to work yet. So instead, we check for cases where
+// these differences occur, and skip those matches.
+
+// We refer to the above technique as the "kana workaround". The next few
+// functions are helper functinos for the kana workaround.
+
+bool IsKanaLetter(UChar character) {
+ // Hiragana letters.
+ if (character >= 0x3041 && character <= 0x3096)
+ return true;
+
+ // Katakana letters.
+ if (character >= 0x30A1 && character <= 0x30FA)
+ return true;
+ if (character >= 0x31F0 && character <= 0x31FF)
+ return true;
+
+ // Halfwidth katakana letters.
+ if (character >= 0xFF66 && character <= 0xFF9D && character != 0xFF70)
+ return true;
+
+ return false;
+}
+
+bool IsSmallKanaLetter(UChar character) {
+ DCHECK(IsKanaLetter(character));
+
+ switch (character) {
+ case 0x3041: // HIRAGANA LETTER SMALL A
+ case 0x3043: // HIRAGANA LETTER SMALL I
+ case 0x3045: // HIRAGANA LETTER SMALL U
+ case 0x3047: // HIRAGANA LETTER SMALL E
+ case 0x3049: // HIRAGANA LETTER SMALL O
+ case 0x3063: // HIRAGANA LETTER SMALL TU
+ case 0x3083: // HIRAGANA LETTER SMALL YA
+ case 0x3085: // HIRAGANA LETTER SMALL YU
+ case 0x3087: // HIRAGANA LETTER SMALL YO
+ case 0x308E: // HIRAGANA LETTER SMALL WA
+ case 0x3095: // HIRAGANA LETTER SMALL KA
+ case 0x3096: // HIRAGANA LETTER SMALL KE
+ case 0x30A1: // KATAKANA LETTER SMALL A
+ case 0x30A3: // KATAKANA LETTER SMALL I
+ case 0x30A5: // KATAKANA LETTER SMALL U
+ case 0x30A7: // KATAKANA LETTER SMALL E
+ case 0x30A9: // KATAKANA LETTER SMALL O
+ case 0x30C3: // KATAKANA LETTER SMALL TU
+ case 0x30E3: // KATAKANA LETTER SMALL YA
+ case 0x30E5: // KATAKANA LETTER SMALL YU
+ case 0x30E7: // KATAKANA LETTER SMALL YO
+ case 0x30EE: // KATAKANA LETTER SMALL WA
+ case 0x30F5: // KATAKANA LETTER SMALL KA
+ case 0x30F6: // KATAKANA LETTER SMALL KE
+ case 0x31F0: // KATAKANA LETTER SMALL KU
+ case 0x31F1: // KATAKANA LETTER SMALL SI
+ case 0x31F2: // KATAKANA LETTER SMALL SU
+ case 0x31F3: // KATAKANA LETTER SMALL TO
+ case 0x31F4: // KATAKANA LETTER SMALL NU
+ case 0x31F5: // KATAKANA LETTER SMALL HA
+ case 0x31F6: // KATAKANA LETTER SMALL HI
+ case 0x31F7: // KATAKANA LETTER SMALL HU
+ case 0x31F8: // KATAKANA LETTER SMALL HE
+ case 0x31F9: // KATAKANA LETTER SMALL HO
+ case 0x31FA: // KATAKANA LETTER SMALL MU
+ case 0x31FB: // KATAKANA LETTER SMALL RA
+ case 0x31FC: // KATAKANA LETTER SMALL RI
+ case 0x31FD: // KATAKANA LETTER SMALL RU
+ case 0x31FE: // KATAKANA LETTER SMALL RE
+ case 0x31FF: // KATAKANA LETTER SMALL RO
+ case 0xFF67: // HALFWIDTH KATAKANA LETTER SMALL A
+ case 0xFF68: // HALFWIDTH KATAKANA LETTER SMALL I
+ case 0xFF69: // HALFWIDTH KATAKANA LETTER SMALL U
+ case 0xFF6A: // HALFWIDTH KATAKANA LETTER SMALL E
+ case 0xFF6B: // HALFWIDTH KATAKANA LETTER SMALL O
+ case 0xFF6C: // HALFWIDTH KATAKANA LETTER SMALL YA
+ case 0xFF6D: // HALFWIDTH KATAKANA LETTER SMALL YU
+ case 0xFF6E: // HALFWIDTH KATAKANA LETTER SMALL YO
+ case 0xFF6F: // HALFWIDTH KATAKANA LETTER SMALL TU
+ return true;
+ }
+ return false;
+}
+
+static inline VoicedSoundMarkType ComposedVoicedSoundMark(UChar character) {
+ DCHECK(IsKanaLetter(character));
+
+ switch (character) {
+ case 0x304C: // HIRAGANA LETTER GA
+ case 0x304E: // HIRAGANA LETTER GI
+ case 0x3050: // HIRAGANA LETTER GU
+ case 0x3052: // HIRAGANA LETTER GE
+ case 0x3054: // HIRAGANA LETTER GO
+ case 0x3056: // HIRAGANA LETTER ZA
+ case 0x3058: // HIRAGANA LETTER ZI
+ case 0x305A: // HIRAGANA LETTER ZU
+ case 0x305C: // HIRAGANA LETTER ZE
+ case 0x305E: // HIRAGANA LETTER ZO
+ case 0x3060: // HIRAGANA LETTER DA
+ case 0x3062: // HIRAGANA LETTER DI
+ case 0x3065: // HIRAGANA LETTER DU
+ case 0x3067: // HIRAGANA LETTER DE
+ case 0x3069: // HIRAGANA LETTER DO
+ case 0x3070: // HIRAGANA LETTER BA
+ case 0x3073: // HIRAGANA LETTER BI
+ case 0x3076: // HIRAGANA LETTER BU
+ case 0x3079: // HIRAGANA LETTER BE
+ case 0x307C: // HIRAGANA LETTER BO
+ case 0x3094: // HIRAGANA LETTER VU
+ case 0x30AC: // KATAKANA LETTER GA
+ case 0x30AE: // KATAKANA LETTER GI
+ case 0x30B0: // KATAKANA LETTER GU
+ case 0x30B2: // KATAKANA LETTER GE
+ case 0x30B4: // KATAKANA LETTER GO
+ case 0x30B6: // KATAKANA LETTER ZA
+ case 0x30B8: // KATAKANA LETTER ZI
+ case 0x30BA: // KATAKANA LETTER ZU
+ case 0x30BC: // KATAKANA LETTER ZE
+ case 0x30BE: // KATAKANA LETTER ZO
+ case 0x30C0: // KATAKANA LETTER DA
+ case 0x30C2: // KATAKANA LETTER DI
+ case 0x30C5: // KATAKANA LETTER DU
+ case 0x30C7: // KATAKANA LETTER DE
+ case 0x30C9: // KATAKANA LETTER DO
+ case 0x30D0: // KATAKANA LETTER BA
+ case 0x30D3: // KATAKANA LETTER BI
+ case 0x30D6: // KATAKANA LETTER BU
+ case 0x30D9: // KATAKANA LETTER BE
+ case 0x30DC: // KATAKANA LETTER BO
+ case 0x30F4: // KATAKANA LETTER VU
+ case 0x30F7: // KATAKANA LETTER VA
+ case 0x30F8: // KATAKANA LETTER VI
+ case 0x30F9: // KATAKANA LETTER VE
+ case 0x30FA: // KATAKANA LETTER VO
+ return kVoicedSoundMark;
+ case 0x3071: // HIRAGANA LETTER PA
+ case 0x3074: // HIRAGANA LETTER PI
+ case 0x3077: // HIRAGANA LETTER PU
+ case 0x307A: // HIRAGANA LETTER PE
+ case 0x307D: // HIRAGANA LETTER PO
+ case 0x30D1: // KATAKANA LETTER PA
+ case 0x30D4: // KATAKANA LETTER PI
+ case 0x30D7: // KATAKANA LETTER PU
+ case 0x30DA: // KATAKANA LETTER PE
+ case 0x30DD: // KATAKANA LETTER PO
+ return kSemiVoicedSoundMark;
+ }
+ return kNoVoicedSoundMark;
+}
+
+static inline bool IsCombiningVoicedSoundMark(UChar character) {
+ switch (character) {
+ case 0x3099: // COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK
+ case 0x309A: // COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK
+ return true;
+ }
+ return false;
+}
+
+bool ContainsKanaLetters(const String& pattern) {
+ const unsigned length = pattern.length();
+ for (unsigned i = 0; i < length; ++i) {
+ if (IsKanaLetter(pattern[i]))
+ return true;
+ }
+ return false;
+}
+
+void NormalizeCharactersIntoNFCForm(const UChar* characters,
+ unsigned length,
+ Vector<UChar>& buffer) {
+ DCHECK(length);
+
+ UErrorCode status = U_ZERO_ERROR;
+ const icu::Normalizer2* normalizer = icu::Normalizer2::getNFCInstance(status);
+ DCHECK(U_SUCCESS(status));
+ int32_t input_length = static_cast<int32_t>(length);
+ // copy-on-write.
+ icu::UnicodeString normalized(FALSE, characters, input_length);
+ // In the vast majority of cases, input is already NFC. Run a quick check
+ // to avoid normalizing the entire input unnecessarily.
+ int32_t normalized_prefix_length =
+ normalizer->spanQuickCheckYes(normalized, status);
+ if (normalized_prefix_length < input_length) {
+ icu::UnicodeString un_normalized(normalized, normalized_prefix_length);
+ normalized.truncate(normalized_prefix_length);
+ normalizer->normalizeSecondAndAppend(normalized, un_normalized, status);
+ }
+ size_t buffer_size = normalized.length();
+ DCHECK(buffer_size);
+
+ buffer.resize(buffer_size);
+ normalized.extract(buffer.data(), buffer_size, status);
+ DCHECK(U_SUCCESS(status));
+}
+
+// This function returns kNotFound if |first| and |second| contain different
+// Kana letters. If |first| and |second| contain the same Kana letter then
+// function returns offset in characters from |first|.
+// Pointers to both strings increase simultaneously so so it is possible to use
+// one offset value.
+static inline size_t CompareKanaLetterAndComposedVoicedSoundMarks(
+ const UChar* first,
+ const UChar* first_end,
+ const UChar* second,
+ const UChar* second_end) {
+ const UChar* start = first;
+ // Check for differences in the kana letter character itself.
+ if (IsSmallKanaLetter(*first) != IsSmallKanaLetter(*second))
+ return kNotFound;
+ if (ComposedVoicedSoundMark(*first) != ComposedVoicedSoundMark(*second))
+ return kNotFound;
+ ++first;
+ ++second;
+
+ // Check for differences in combining voiced sound marks found after the
+ // letter.
+ while (true) {
+ const bool second_is_not_sound_mark =
+ second == second_end || !IsCombiningVoicedSoundMark(*second);
+ if (first == first_end || !IsCombiningVoicedSoundMark(*first)) {
+ return second_is_not_sound_mark ? first - start : kNotFound;
+ }
+ if (second_is_not_sound_mark)
+ return kNotFound;
+ if (*first != *second)
+ return kNotFound;
+ ++first;
+ ++second;
+ }
+}
+
+bool CheckOnlyKanaLettersInStrings(const UChar* first_data,
+ unsigned first_length,
+ const UChar* second_data,
+ unsigned second_length) {
+ const UChar* a = first_data;
+ const UChar* a_end = first_data + first_length;
+
+ const UChar* b = second_data;
+ const UChar* b_end = second_data + second_length;
+ while (true) {
+ // Skip runs of non-kana-letter characters. This is necessary so we can
+ // correctly handle strings where the |firstData| and |secondData| have
+ // different-length runs of characters that match, while still double
+ // checking the correctness of matches of kana letters with other kana
+ // letters.
+ while (a != a_end && !IsKanaLetter(*a))
+ ++a;
+ while (b != b_end && !IsKanaLetter(*b))
+ ++b;
+
+ // If we reached the end of either the target or the match, we should have
+ // reached the end of both; both should have the same number of kana
+ // letters.
+ if (a == a_end || b == b_end) {
+ return a == a_end && b == b_end;
+ }
+
+ // Check that single Kana letters in |a| and |b| are the same.
+ const size_t offset =
+ CompareKanaLetterAndComposedVoicedSoundMarks(a, a_end, b, b_end);
+ if (offset == kNotFound)
+ return false;
+
+ // Update values of |a| and |b| after comparing.
+ a += offset;
+ b += offset;
+ }
+}
+
+bool CheckKanaStringsEqual(const UChar* first_data,
+ unsigned first_length,
+ const UChar* second_data,
+ unsigned second_length) {
+ const UChar* a = first_data;
+ const UChar* a_end = first_data + first_length;
+
+ const UChar* b = second_data;
+ const UChar* b_end = second_data + second_length;
+ while (true) {
+ // Check for non-kana-letter characters.
+ while (a != a_end && !IsKanaLetter(*a) && b != b_end && !IsKanaLetter(*b)) {
+ if (*a++ != *b++)
+ return false;
+ }
+
+ // If we reached the end of either the target or the match, we should have
+ // reached the end of both; both should have the same number of kana
+ // letters.
+ if (a == a_end || b == b_end) {
+ return a == a_end && b == b_end;
+ }
+
+ if (IsKanaLetter(*a) != IsKanaLetter(*b))
+ return false;
+
+ // Check that single Kana letters in |a| and |b| are the same.
+ const size_t offset =
+ CompareKanaLetterAndComposedVoicedSoundMarks(a, a_end, b, b_end);
+ if (offset == kNotFound)
+ return false;
+
+ // Update values of |a| and |b| after comparing.
+ a += offset;
+ b += offset;
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/unicode_utilities.h b/chromium/third_party/blink/renderer/platform/text/unicode_utilities.h
new file mode 100644
index 00000000000..b0089d97679
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/unicode_utilities.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2004, 2006, 2009 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_UNICODE_UTILITIES_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_UNICODE_UTILITIES_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+PLATFORM_EXPORT bool IsSeparator(UChar32);
+PLATFORM_EXPORT bool IsKanaLetter(UChar character);
+PLATFORM_EXPORT bool ContainsKanaLetters(const String&);
+PLATFORM_EXPORT void NormalizeCharactersIntoNFCForm(const UChar* characters,
+ unsigned length,
+ Vector<UChar>& buffer);
+PLATFORM_EXPORT void FoldQuoteMarksAndSoftHyphens(UChar* data, size_t length);
+PLATFORM_EXPORT void FoldQuoteMarksAndSoftHyphens(String&);
+PLATFORM_EXPORT bool CheckOnlyKanaLettersInStrings(const UChar* first_data,
+ unsigned first_length,
+ const UChar* second_data,
+ unsigned second_length);
+PLATFORM_EXPORT bool CheckKanaStringsEqual(const UChar* first_data,
+ unsigned first_length,
+ const UChar* second_data,
+ unsigned second_length);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_UNICODE_UTILITIES_H_
diff --git a/chromium/third_party/blink/renderer/platform/text/unicode_utilities_test.cc b/chromium/third_party/blink/renderer/platform/text/unicode_utilities_test.cc
new file mode 100644
index 00000000000..712d7be2c80
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/unicode_utilities_test.cc
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 2013 Yandex LLC. 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 Yandex LLC 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 "third_party/blink/renderer/platform/text/unicode_utilities.h"
+
+#include <unicode/uchar.h>
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+static const UChar32 kMaxLatinCharCount = 256;
+
+static bool g_is_test_first_and_last_chars_in_category_failed = false;
+UBool U_CALLCONV TestFirstAndLastCharsInCategory(const void* context,
+ UChar32 start,
+ UChar32 limit,
+ UCharCategory type) {
+ if (start >= kMaxLatinCharCount &&
+ U_MASK(type) & (U_GC_S_MASK | U_GC_P_MASK | U_GC_Z_MASK | U_GC_CF_MASK) &&
+ (!IsSeparator(start) || !IsSeparator(limit - 1))) {
+ g_is_test_first_and_last_chars_in_category_failed = true;
+
+ // Break enumeration process
+ return 0;
+ }
+
+ return 1;
+}
+
+TEST(UnicodeUtilitiesTest, Separators) {
+ // clang-format off
+ static const bool kLatinSeparatorTable[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ // space ! " # $ % & ' ( ) * + , - . /
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ // : ; < = > ?
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
+ // @
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ // [ \ ] ^ _
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
+ // `
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ // { | } ~
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ // clang-format on
+
+ for (UChar32 character = 0; character < kMaxLatinCharCount; ++character) {
+ EXPECT_EQ(IsSeparator(character), kLatinSeparatorTable[character]);
+ }
+
+ g_is_test_first_and_last_chars_in_category_failed = false;
+ u_enumCharTypes(&TestFirstAndLastCharsInCategory, nullptr);
+ EXPECT_FALSE(g_is_test_first_and_last_chars_in_category_failed);
+}
+
+TEST(UnicodeUtilitiesTest, KanaLetters) {
+ // Non Kana symbols
+ for (UChar character = 0; character < 0x3041; ++character)
+ EXPECT_FALSE(IsKanaLetter(character));
+
+ // Hiragana letters.
+ for (UChar character = 0x3041; character <= 0x3096; ++character)
+ EXPECT_TRUE(IsKanaLetter(character));
+
+ // Katakana letters.
+ for (UChar character = 0x30A1; character <= 0x30FA; ++character)
+ EXPECT_TRUE(IsKanaLetter(character));
+}
+
+TEST(UnicodeUtilitiesTest, ContainsKanaLetters) {
+ // Non Kana symbols
+ String non_kana_string;
+ for (UChar character = 0; character < 0x3041; ++character)
+ non_kana_string.append(character);
+ EXPECT_FALSE(ContainsKanaLetters(non_kana_string));
+
+ // Hiragana letters.
+ for (UChar character = 0x3041; character <= 0x3096; ++character) {
+ String str(non_kana_string);
+ str.append(character);
+ EXPECT_TRUE(ContainsKanaLetters(str));
+ }
+
+ // Katakana letters.
+ for (UChar character = 0x30A1; character <= 0x30FA; ++character) {
+ String str(non_kana_string);
+ str.append(character);
+ EXPECT_TRUE(ContainsKanaLetters(str));
+ }
+}
+
+TEST(UnicodeUtilitiesTest, FoldQuoteMarkOrSoftHyphenTest) {
+ const UChar kCharactersToFold[] = {kHebrewPunctuationGershayimCharacter,
+ kLeftDoubleQuotationMarkCharacter,
+ kRightDoubleQuotationMarkCharacter,
+ kHebrewPunctuationGereshCharacter,
+ kLeftSingleQuotationMarkCharacter,
+ kRightSingleQuotationMarkCharacter,
+ kSoftHyphenCharacter};
+
+ String string_to_fold(kCharactersToFold, WTF_ARRAY_LENGTH(kCharactersToFold));
+ Vector<UChar> buffer;
+ string_to_fold.AppendTo(buffer);
+
+ FoldQuoteMarksAndSoftHyphens(string_to_fold);
+
+ const String folded_string("\"\"\"\'\'\'\0",
+ WTF_ARRAY_LENGTH(kCharactersToFold));
+ EXPECT_EQ(string_to_fold, folded_string);
+
+ FoldQuoteMarksAndSoftHyphens(buffer.data(), buffer.size());
+ EXPECT_EQ(String(buffer), folded_string);
+}
+
+TEST(UnicodeUtilitiesTest, OnlyKanaLettersEqualityTest) {
+ const UChar kNonKanaString1[] = {'a', 'b', 'c', 'd'};
+ const UChar kNonKanaString2[] = {'e', 'f', 'g'};
+
+ // Check that non-Kana letters will be skipped.
+ EXPECT_TRUE(CheckOnlyKanaLettersInStrings(
+ kNonKanaString1, WTF_ARRAY_LENGTH(kNonKanaString1), kNonKanaString2,
+ WTF_ARRAY_LENGTH(kNonKanaString2)));
+
+ const UChar kKanaString[] = {'e', 'f', 'g', 0x3041};
+ EXPECT_FALSE(CheckOnlyKanaLettersInStrings(
+ kKanaString, WTF_ARRAY_LENGTH(kKanaString), kNonKanaString2,
+ WTF_ARRAY_LENGTH(kNonKanaString2)));
+
+ // Compare with self.
+ EXPECT_TRUE(CheckOnlyKanaLettersInStrings(
+ kKanaString, WTF_ARRAY_LENGTH(kKanaString), kKanaString,
+ WTF_ARRAY_LENGTH(kKanaString)));
+
+ UChar voiced_kana_string1[] = {0x3042, 0x3099};
+ UChar voiced_kana_string2[] = {0x3042, 0x309A};
+
+ // Comparing strings with different sound marks should fail.
+ EXPECT_FALSE(CheckOnlyKanaLettersInStrings(
+ voiced_kana_string1, WTF_ARRAY_LENGTH(voiced_kana_string1),
+ voiced_kana_string2, WTF_ARRAY_LENGTH(voiced_kana_string2)));
+
+ // Now strings will be the same.
+ voiced_kana_string2[1] = 0x3099;
+ EXPECT_TRUE(CheckOnlyKanaLettersInStrings(
+ voiced_kana_string1, WTF_ARRAY_LENGTH(voiced_kana_string1),
+ voiced_kana_string2, WTF_ARRAY_LENGTH(voiced_kana_string2)));
+
+ voiced_kana_string2[0] = 0x3043;
+ EXPECT_FALSE(CheckOnlyKanaLettersInStrings(
+ voiced_kana_string1, WTF_ARRAY_LENGTH(voiced_kana_string1),
+ voiced_kana_string2, WTF_ARRAY_LENGTH(voiced_kana_string2)));
+}
+
+TEST(UnicodeUtilitiesTest, StringsWithKanaLettersTest) {
+ const UChar kNonKanaString1[] = {'a', 'b', 'c'};
+ const UChar kNonKanaString2[] = {'a', 'b', 'c'};
+
+ // Check that non-Kana letters will be compared.
+ EXPECT_TRUE(CheckKanaStringsEqual(
+ kNonKanaString1, WTF_ARRAY_LENGTH(kNonKanaString1), kNonKanaString2,
+ WTF_ARRAY_LENGTH(kNonKanaString2)));
+
+ const UChar kKanaString[] = {'a', 'b', 'c', 0x3041};
+ EXPECT_FALSE(CheckKanaStringsEqual(kKanaString, WTF_ARRAY_LENGTH(kKanaString),
+ kNonKanaString2,
+ WTF_ARRAY_LENGTH(kNonKanaString2)));
+
+ // Compare with self.
+ EXPECT_TRUE(CheckKanaStringsEqual(kKanaString, WTF_ARRAY_LENGTH(kKanaString),
+ kKanaString,
+ WTF_ARRAY_LENGTH(kKanaString)));
+
+ const UChar kKanaString2[] = {'x', 'y', 'z', 0x3041};
+ // Comparing strings with different non-Kana letters should fail.
+ EXPECT_FALSE(CheckKanaStringsEqual(kKanaString, WTF_ARRAY_LENGTH(kKanaString),
+ kKanaString2,
+ WTF_ARRAY_LENGTH(kKanaString2)));
+
+ const UChar kKanaString3[] = {'a', 'b', 'c', 0x3042, 0x3099, 'm', 'n', 'o'};
+ // Check that non-Kana letters after Kana letters will be compared.
+ EXPECT_TRUE(
+ CheckKanaStringsEqual(kKanaString3, WTF_ARRAY_LENGTH(kKanaString3),
+ kKanaString3, WTF_ARRAY_LENGTH(kKanaString3)));
+
+ const UChar kKanaString4[] = {'a', 'b', 'c', 0x3042, 0x3099,
+ 'm', 'n', 'o', 'p'};
+ // And now comparing should fail.
+ EXPECT_FALSE(
+ CheckKanaStringsEqual(kKanaString3, WTF_ARRAY_LENGTH(kKanaString3),
+ kKanaString4, WTF_ARRAY_LENGTH(kKanaString4)));
+
+ UChar voiced_kana_string1[] = {0x3042, 0x3099};
+ UChar voiced_kana_string2[] = {0x3042, 0x309A};
+
+ // Comparing strings with different sound marks should fail.
+ EXPECT_FALSE(CheckKanaStringsEqual(
+ voiced_kana_string1, WTF_ARRAY_LENGTH(voiced_kana_string1),
+ voiced_kana_string2, WTF_ARRAY_LENGTH(voiced_kana_string2)));
+
+ // Now strings will be the same.
+ voiced_kana_string2[1] = 0x3099;
+ EXPECT_TRUE(CheckKanaStringsEqual(
+ voiced_kana_string1, WTF_ARRAY_LENGTH(voiced_kana_string1),
+ voiced_kana_string2, WTF_ARRAY_LENGTH(voiced_kana_string2)));
+
+ voiced_kana_string2[0] = 0x3043;
+ EXPECT_FALSE(CheckKanaStringsEqual(
+ voiced_kana_string1, WTF_ARRAY_LENGTH(voiced_kana_string1),
+ voiced_kana_string2, WTF_ARRAY_LENGTH(voiced_kana_string2)));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/web_entities.cc b/chromium/third_party/blink/renderer/platform/text/web_entities.cc
new file mode 100644
index 00000000000..b438f5e358e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/web_entities.cc
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/renderer/platform/text/web_entities.h"
+
+#include <string.h>
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+
+namespace blink {
+
+WebEntities::WebEntities(bool xml_entities) {
+ DCHECK(entities_map_.IsEmpty());
+ entities_map_.Set(0x003c, "lt");
+ entities_map_.Set(0x003e, "gt");
+ entities_map_.Set(0x0026, "amp");
+ entities_map_.Set(0x0027, "apos");
+ entities_map_.Set(0x0022, "quot");
+ // We add #39 for test-compatibility reason.
+ if (!xml_entities)
+ entities_map_.Set(0x0027, String("#39"));
+}
+
+String WebEntities::EntityNameByCode(int code) const {
+ // FIXME: We should use find so we only do one hash lookup.
+ if (entities_map_.Contains(code))
+ return entities_map_.at(code);
+ return "";
+}
+
+String WebEntities::ConvertEntitiesInString(const String& value) const {
+ StringBuilder result;
+ bool did_convert_entity = false;
+ unsigned length = value.length();
+ for (unsigned i = 0; i < length; ++i) {
+ UChar c = value[i];
+ // FIXME: We should use find so we only do one hash lookup.
+ if (entities_map_.Contains(c)) {
+ did_convert_entity = true;
+ result.Append('&');
+ result.Append(entities_map_.at(c));
+ result.Append(';');
+ } else {
+ result.Append(c);
+ }
+ }
+
+ if (!did_convert_entity)
+ return value;
+
+ return result.ToString();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/web_entities.h b/chromium/third_party/blink/renderer/platform/text/web_entities.h
new file mode 100644
index 00000000000..c0288cae6af
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/web_entities.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_WEB_ENTITIES_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_WEB_ENTITIES_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+// FIXME: This class is wrong and needs to be removed!
+class PLATFORM_EXPORT WebEntities {
+ public:
+ // &apos;, &percnt;, &nsup;, &supl; are not defined by the HTML standards.
+ // - IE does not support &apos; as an HTML entity (but support it as an XML
+ // entity.)
+ // - Firefox supports &apos; as an HTML entity.
+ // - Both of IE and Firefox don't support &percnt;, &nsup; and &supl;.
+ //
+ // A web page saved by Chromium should be able to be read by other browsers
+ // such as IE and Firefox. Chromium should produce only the standard entity
+ // references which other browsers can recognize.
+ // So if standard_html_entities_ is true, we will use a numeric character
+ // reference for &apos;, and don't use entity references for &percnt;, &nsup;
+ // and &supl; for serialization.
+ //
+ // If xmlEntities is true, WebEntities will only contain standard XML
+ // entities.
+ explicit WebEntities(bool xml_entities);
+
+ // Check whether specified unicode has corresponding html or xml built-in
+ // entity name. If yes, return the entity notation. If not, returns an
+ // empty string. Parameter isHTML indicates check the code in html entity
+ // map or in xml entity map.
+ WTF::String EntityNameByCode(int code) const;
+
+ // Returns a new string with corresponding entity names replaced.
+ WTF::String ConvertEntitiesInString(const WTF::String&) const;
+
+ private:
+ typedef HashMap<int, WTF::String> EntitiesMapType;
+ // An internal object that maps the Unicode character to corresponding
+ // entity notation.
+ EntitiesMapType entities_map_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/text/win/hyphenation_win.cc b/chromium/third_party/blink/renderer/platform/text/win/hyphenation_win.cc
new file mode 100644
index 00000000000..6e21b36d8b0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/win/hyphenation_win.cc
@@ -0,0 +1,14 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/text/hyphenation.h"
+
+namespace blink {
+
+scoped_refptr<Hyphenation> Hyphenation::PlatformGetHyphenation(
+ const AtomicString&) {
+ return nullptr;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/writing_mode.h b/chromium/third_party/blink/renderer/platform/text/writing_mode.h
new file mode 100644
index 00000000000..e954659304d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/writing_mode.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2012, 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_WRITING_MODE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_WRITING_MODE_H_
+
+namespace blink {
+
+// These values are named to match the CSS keywords they correspond to: namely
+// horizontal-tb, vertical-rl and vertical-lr.
+// Since these names aren't very self-explanatory, where possible use the
+// inline utility functions below.
+enum class WritingMode : unsigned {
+ kHorizontalTb = 0,
+ kVerticalRl = 1,
+ kVerticalLr = 2,
+ // sideways-rl and sideways-lr are only supported by LayoutNG.
+ kSidewaysRl = 3,
+ kSidewaysLr = 4
+};
+
+// Lines have horizontal orientation; modes horizontal-tb.
+inline bool IsHorizontalWritingMode(WritingMode writing_mode) {
+ return writing_mode == WritingMode::kHorizontalTb;
+}
+
+// Bottom of the line occurs earlier in the block; modes vertical-lr.
+inline bool IsFlippedLinesWritingMode(WritingMode writing_mode) {
+ return writing_mode == WritingMode::kVerticalLr;
+}
+
+// Block progression increases in the opposite direction to normal; modes
+// vertical-rl.
+inline bool IsFlippedBlocksWritingMode(WritingMode writing_mode) {
+ return writing_mode == WritingMode::kVerticalRl;
+}
+
+// Whether the child and the containing block are parallel to each other.
+// Example: vertical-rl and vertical-lr
+inline bool IsParallelWritingMode(WritingMode a, WritingMode b) {
+ return (a == WritingMode::kHorizontalTb) == (b == WritingMode::kHorizontalTb);
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_WRITING_MODE_H_
diff --git a/chromium/third_party/blink/renderer/platform/text/writing_mode_utils.h b/chromium/third_party/blink/renderer/platform/text/writing_mode_utils.h
new file mode 100644
index 00000000000..5207f3a937c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/writing_mode_utils.h
@@ -0,0 +1,312 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license thaT can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_WRITING_MODE_UTILS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_WRITING_MODE_UTILS_H_
+
+#include "third_party/blink/renderer/platform/text/text_direction.h"
+#include "third_party/blink/renderer/platform/text/writing_mode.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+// Templates to map values between logical orientations and physical
+// orientations. See https://www.w3.org/TR/css-writing-modes-3/ and
+// https://www.w3.org/TR/css-logical-1/ for definitions of logical orientations.
+
+// This file provides two types of templates:
+//
+// - Simple value mappers (PhysicalToLogical and LogicalToPhysical): they take
+// 4 input values in physical or logical orientations, and provide accessors
+// to get values in logical or physical orientations. As the inputs may be
+// evaluated even if not used (in case that the compiler is unable to remove
+// unused evaluations, e.g. containing non-inlined function calls), for
+// performance-senstive code, the evaluation of the inputs should be simple
+// and/or be fully inlined.
+//
+// - Value mappers based on getter/setter methods (PhysicalToLogicalGetter,
+// LogicalToPhysicalGetter, PhysicalToLogicalSetter and
+// LogicalToPhysicalSetter): they take 4 method pointers as inputs pointing to
+// methods accessing values in physical or logical orientations, and provide
+// accessors to get or set values in logical or physical orientations. They
+// are suitable for mapping of setters, or getters implemented with non-
+// inlined functions. Evaluation of the input values are delayed when they are
+// actually needed.
+//
+// See WritingModeUtilsTest.cpp, LayoutBoxModelObject.h and ComputedStyle.h for
+// examples.
+
+template <typename Value>
+class PhysicalToLogical {
+ STACK_ALLOCATED();
+
+ public:
+ PhysicalToLogical(WritingMode writing_mode,
+ TextDirection direction,
+ Value top,
+ Value right,
+ Value bottom,
+ Value left)
+ : writing_mode_(writing_mode),
+ direction_(direction),
+ top_(top),
+ right_(right),
+ bottom_(bottom),
+ left_(left) {}
+
+ Value InlineStart() const {
+ if (IsHorizontalWritingMode(writing_mode_))
+ return IsLtr(direction_) ? left_ : right_;
+ return IsLtr(direction_) ? top_ : bottom_;
+ }
+
+ Value InlineEnd() const {
+ if (IsHorizontalWritingMode(writing_mode_))
+ return IsLtr(direction_) ? right_ : left_;
+ return IsLtr(direction_) ? bottom_ : top_;
+ }
+
+ Value BlockStart() const {
+ if (IsHorizontalWritingMode(writing_mode_))
+ return top_;
+ return IsFlippedBlocksWritingMode(writing_mode_) ? right_ : left_;
+ }
+
+ Value BlockEnd() const {
+ if (IsHorizontalWritingMode(writing_mode_))
+ return bottom_;
+ return IsFlippedBlocksWritingMode(writing_mode_) ? left_ : right_;
+ }
+
+ Value Over() const {
+ return IsHorizontalWritingMode(writing_mode_) ? top_ : right_;
+ }
+
+ Value Under() const {
+ return IsHorizontalWritingMode(writing_mode_) ? bottom_ : left_;
+ }
+
+ Value LineLeft() const {
+ return IsHorizontalWritingMode(writing_mode_) ? left_ : top_;
+ }
+
+ Value LineRight() const {
+ return IsHorizontalWritingMode(writing_mode_) ? right_ : bottom_;
+ }
+
+ // Legacy logical directions.
+ Value Start() const { return InlineStart(); }
+ Value End() const { return InlineEnd(); }
+ Value Before() const { return BlockStart(); }
+ Value After() const { return BlockEnd(); }
+
+ private:
+ WritingMode writing_mode_;
+ TextDirection direction_;
+ Value top_;
+ Value right_;
+ Value bottom_;
+ Value left_;
+};
+
+template <typename Value>
+class LogicalToPhysical {
+ STACK_ALLOCATED();
+
+ public:
+ LogicalToPhysical(WritingMode writing_mode,
+ TextDirection direction,
+ Value inline_start,
+ Value inline_end,
+ Value block_start,
+ Value block_end)
+ : writing_mode_(writing_mode),
+ direction_(direction),
+ inline_start_(inline_start),
+ inline_end_(inline_end),
+ block_start_(block_start),
+ block_end_(block_end) {}
+
+ Value Left() const {
+ if (IsHorizontalWritingMode(writing_mode_))
+ return IsLtr(direction_) ? inline_start_ : inline_end_;
+ return IsFlippedBlocksWritingMode(writing_mode_) ? block_end_
+ : block_start_;
+ }
+
+ Value Right() const {
+ if (IsHorizontalWritingMode(writing_mode_))
+ return IsLtr(direction_) ? inline_end_ : inline_start_;
+ return IsFlippedBlocksWritingMode(writing_mode_) ? block_start_
+ : block_end_;
+ }
+
+ Value Top() const {
+ if (IsHorizontalWritingMode(writing_mode_))
+ return block_start_;
+ return IsLtr(direction_) ? inline_start_ : inline_end_;
+ }
+
+ Value Bottom() const {
+ if (IsHorizontalWritingMode(writing_mode_))
+ return block_end_;
+ return IsLtr(direction_) ? inline_end_ : inline_start_;
+ }
+
+ private:
+ WritingMode writing_mode_;
+ TextDirection direction_;
+ Value inline_start_; // a.k.a. start
+ Value inline_end_; // a.k.a. end
+ Value block_start_; // a.k.a. before
+ Value block_end_; // a.k.a. after
+};
+
+template <typename Value, typename Object>
+class LogicalToPhysicalGetter {
+ STACK_ALLOCATED();
+
+ public:
+ using Getter = Value (Object::*)() const;
+ LogicalToPhysicalGetter(WritingMode writing_mode,
+ TextDirection direction,
+ const Object& object,
+ Getter inline_start_getter,
+ Getter inline_end_getter,
+ Getter block_start_getter,
+ Getter block_end_getter)
+ : object_(object),
+ converter_(writing_mode,
+ direction,
+ inline_start_getter,
+ inline_end_getter,
+ block_start_getter,
+ block_end_getter) {}
+
+ Value Left() const { return (object_.*converter_.Left())(); }
+ Value Right() const { return (object_.*converter_.Right())(); }
+ Value Top() const { return (object_.*converter_.Top())(); }
+ Value Bottom() const { return (object_.*converter_.Bottom())(); }
+
+ private:
+ const Object& object_;
+ LogicalToPhysical<Getter> converter_;
+};
+
+template <typename Value, typename Object>
+class PhysicalToLogicalGetter {
+ STACK_ALLOCATED();
+
+ public:
+ using Getter = Value (Object::*)() const;
+ PhysicalToLogicalGetter(WritingMode writing_mode,
+ TextDirection direction,
+ const Object& object,
+ Getter top_getter,
+ Getter right_getter,
+ Getter bottom_getter,
+ Getter left_getter)
+ : object_(object),
+ converter_(writing_mode,
+ direction,
+ top_getter,
+ right_getter,
+ bottom_getter,
+ left_getter) {}
+
+ Value InlineStart() const { return (object_.*converter_.InlineStart())(); }
+ Value InlineEnd() const { return (object_.*converter_.InlineEnd())(); }
+ Value BlockStart() const { return (object_.*converter_.BlockStart())(); }
+ Value BlockEnd() const { return (object_.*converter_.BlockEnd())(); }
+ Value Over() const { return (object_.*converter_.Over())(); }
+ Value Under() const { return (object_.*converter_.Under())(); }
+ Value LineLeft() const { return (object_.*converter_.LineLeft())(); }
+ Value LineRight() const { return (object_.*converter_.LineRight())(); }
+ Value Start() const { return (object_.*converter_.Start())(); }
+ Value End() const { return (object_.*converter_.End())(); }
+ Value Before() const { return (object_.*converter_.Before())(); }
+ Value After() const { return (object_.*converter_.After())(); }
+
+ private:
+ const Object& object_;
+ PhysicalToLogical<Getter> converter_;
+};
+
+template <typename Value, typename Object>
+class PhysicalToLogicalSetter {
+ STACK_ALLOCATED();
+
+ public:
+ using Setter = void (Object::*)(Value);
+ PhysicalToLogicalSetter(WritingMode writing_mode,
+ TextDirection direction,
+ Object& object,
+ Setter inline_start_setter,
+ Setter inline_end_setter,
+ Setter block_start_setter,
+ Setter block_end_setter)
+ : object_(object),
+ converter_(writing_mode,
+ direction,
+ inline_start_setter,
+ inline_end_setter,
+ block_start_setter,
+ block_end_setter) {}
+
+ void SetLeft(Value v) { (object_.*converter_.Left())(v); }
+ void SetRight(Value v) { (object_.*converter_.Right())(v); }
+ void SetTop(Value v) { (object_.*converter_.Top())(v); }
+ void SetBottom(Value v) { (object_.*converter_.Bottom())(v); }
+
+ private:
+ Object& object_;
+ // This converter converts logical setters to physical setters which accept
+ // physical values and call the logical setters to set logical values.
+ LogicalToPhysical<Setter> converter_;
+};
+
+template <typename Value, typename Object>
+class LogicalToPhysicalSetter {
+ STACK_ALLOCATED();
+
+ public:
+ using Setter = void (Object::*)(Value);
+ LogicalToPhysicalSetter(WritingMode writing_mode,
+ TextDirection direction,
+ Object& object,
+ Setter top_setter,
+ Setter right_setter,
+ Setter bottom_setter,
+ Setter left_setter)
+ : object_(object),
+ converter_(writing_mode,
+ direction,
+ top_setter,
+ right_setter,
+ bottom_setter,
+ left_setter) {}
+
+ void SetInlineStart(Value v) { (object_.*converter_.InlineStart())(v); }
+ void SetInlineEnd(Value v) { (object_.*converter_.InlineEnd())(v); }
+ void SetBlockStart(Value v) { (object_.*converter_.BlockStart())(v); }
+ void SetBlockEnd(Value v) { (object_.*converter_.BlockEnd())(v); }
+ void SetOver(Value v) { (object_.*converter_.Over())(v); }
+ void SetUnder(Value v) { (object_.*converter_.Under())(v); }
+ void SetLineLeft(Value v) { (object_.*converter_.LineLeft())(v); }
+ void SetLineRight(Value v) { (object_.*converter_.LineRight())(v); }
+ void SetStart(Value v) { (object_.*converter_.Start())(v); }
+ void SetEnd(Value v) { (object_.*converter_.End())(v); }
+ void SetBefore(Value v) { (object_.*converter_.Before())(v); }
+ void SetAfter(Value v) { (object_.*converter_.After())(v); }
+
+ private:
+ Object& object_;
+ // This converter converts physical setters to logical setters which accept
+ // logical values and call the physical setters to set physical values.
+ PhysicalToLogical<Setter> converter_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_WRITING_MODE_UTILS_H_
diff --git a/chromium/third_party/blink/renderer/platform/text/writing_mode_utils_test.cc b/chromium/third_party/blink/renderer/platform/text/writing_mode_utils_test.cc
new file mode 100644
index 00000000000..8fb01300b4f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/writing_mode_utils_test.cc
@@ -0,0 +1,298 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/text/writing_mode_utils.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+namespace {
+
+enum { kTop, kRight, kBottom, kLeft };
+
+template <typename PhysicalToLogicalConverter>
+void CheckLegacyLogicalDirections(const PhysicalToLogicalConverter& converter) {
+ EXPECT_EQ(converter.InlineStart(), converter.Start());
+ EXPECT_EQ(converter.InlineEnd(), converter.End());
+ EXPECT_EQ(converter.BlockStart(), converter.Before());
+ EXPECT_EQ(converter.BlockEnd(), converter.After());
+}
+
+TEST(WritingModeUtilsTest, PhysicalToLogicalHorizontalLtr) {
+ PhysicalToLogical<int> converter(WritingMode::kHorizontalTb,
+ TextDirection::kLtr, kTop, kRight, kBottom,
+ kLeft);
+ EXPECT_EQ(kLeft, converter.InlineStart());
+ EXPECT_EQ(kRight, converter.InlineEnd());
+ EXPECT_EQ(kTop, converter.BlockStart());
+ EXPECT_EQ(kBottom, converter.BlockEnd());
+ EXPECT_EQ(kLeft, converter.LineLeft());
+ EXPECT_EQ(kRight, converter.LineRight());
+ EXPECT_EQ(kTop, converter.Over());
+ EXPECT_EQ(kBottom, converter.Under());
+ CheckLegacyLogicalDirections(converter);
+}
+
+TEST(WritingModeUtilsTest, PhysicalToLogicalHorizontalRtl) {
+ PhysicalToLogical<int> converter(WritingMode::kHorizontalTb,
+ TextDirection::kRtl, kTop, kRight, kBottom,
+ kLeft);
+ EXPECT_EQ(kRight, converter.InlineStart());
+ EXPECT_EQ(kLeft, converter.InlineEnd());
+ EXPECT_EQ(kTop, converter.BlockStart());
+ EXPECT_EQ(kBottom, converter.BlockEnd());
+ EXPECT_EQ(kLeft, converter.LineLeft());
+ EXPECT_EQ(kRight, converter.LineRight());
+ EXPECT_EQ(kTop, converter.Over());
+ EXPECT_EQ(kBottom, converter.Under());
+ CheckLegacyLogicalDirections(converter);
+}
+
+TEST(WritingModeUtilsTest, PhysicalToLogicalVlrLtr) {
+ PhysicalToLogical<int> converter(WritingMode::kVerticalLr,
+ TextDirection::kLtr, kTop, kRight, kBottom,
+ kLeft);
+ EXPECT_EQ(kTop, converter.InlineStart());
+ EXPECT_EQ(kBottom, converter.InlineEnd());
+ EXPECT_EQ(kLeft, converter.BlockStart());
+ EXPECT_EQ(kRight, converter.BlockEnd());
+ EXPECT_EQ(kTop, converter.LineLeft());
+ EXPECT_EQ(kBottom, converter.LineRight());
+ EXPECT_EQ(kRight, converter.Over());
+ EXPECT_EQ(kLeft, converter.Under());
+ CheckLegacyLogicalDirections(converter);
+}
+
+TEST(WritingModeUtilsTest, PhysicalToLogicalVlrRtl) {
+ PhysicalToLogical<int> converter(WritingMode::kVerticalLr,
+ TextDirection::kRtl, kTop, kRight, kBottom,
+ kLeft);
+ EXPECT_EQ(kBottom, converter.InlineStart());
+ EXPECT_EQ(kTop, converter.InlineEnd());
+ EXPECT_EQ(kLeft, converter.BlockStart());
+ EXPECT_EQ(kRight, converter.BlockEnd());
+ EXPECT_EQ(kTop, converter.LineLeft());
+ EXPECT_EQ(kBottom, converter.LineRight());
+ EXPECT_EQ(kRight, converter.Over());
+ EXPECT_EQ(kLeft, converter.Under());
+ CheckLegacyLogicalDirections(converter);
+}
+
+TEST(WritingModeUtilsTest, PhysicalToLogicalVrlLtr) {
+ PhysicalToLogical<int> converter(WritingMode::kVerticalRl,
+ TextDirection::kLtr, kTop, kRight, kBottom,
+ kLeft);
+ EXPECT_EQ(kTop, converter.InlineStart());
+ EXPECT_EQ(kBottom, converter.InlineEnd());
+ EXPECT_EQ(kRight, converter.BlockStart());
+ EXPECT_EQ(kLeft, converter.BlockEnd());
+ EXPECT_EQ(kTop, converter.LineLeft());
+ EXPECT_EQ(kBottom, converter.LineRight());
+ EXPECT_EQ(kRight, converter.Over());
+ EXPECT_EQ(kLeft, converter.Under());
+ CheckLegacyLogicalDirections(converter);
+}
+
+TEST(WritingModeUtilsTest, PhysicalToLogicalVrlRtl) {
+ PhysicalToLogical<int> converter(WritingMode::kVerticalRl,
+ TextDirection::kRtl, kTop, kRight, kBottom,
+ kLeft);
+ EXPECT_EQ(kBottom, converter.InlineStart());
+ EXPECT_EQ(kTop, converter.InlineEnd());
+ EXPECT_EQ(kRight, converter.BlockStart());
+ EXPECT_EQ(kLeft, converter.BlockEnd());
+ EXPECT_EQ(kTop, converter.LineLeft());
+ EXPECT_EQ(kBottom, converter.LineRight());
+ EXPECT_EQ(kRight, converter.Over());
+ EXPECT_EQ(kLeft, converter.Under());
+ CheckLegacyLogicalDirections(converter);
+}
+
+enum { kInlineStart = 1000, kInlineEnd, kBlockStart, kBlockEnd };
+
+TEST(WritingModeUtilsTest, LogicalToPhysicalHorizontalLtr) {
+ LogicalToPhysical<int> converter(WritingMode::kHorizontalTb,
+ TextDirection::kLtr, kInlineStart,
+ kInlineEnd, kBlockStart, kBlockEnd);
+ EXPECT_EQ(kInlineStart, converter.Left());
+ EXPECT_EQ(kInlineEnd, converter.Right());
+ EXPECT_EQ(kBlockStart, converter.Top());
+ EXPECT_EQ(kBlockEnd, converter.Bottom());
+}
+
+TEST(WritingModeUtilsTest, LogicalToPhysicalHorizontalRtl) {
+ LogicalToPhysical<int> converter(WritingMode::kHorizontalTb,
+ TextDirection::kRtl, kInlineStart,
+ kInlineEnd, kBlockStart, kBlockEnd);
+ EXPECT_EQ(kInlineEnd, converter.Left());
+ EXPECT_EQ(kInlineStart, converter.Right());
+ EXPECT_EQ(kBlockStart, converter.Top());
+ EXPECT_EQ(kBlockEnd, converter.Bottom());
+}
+
+TEST(WritingModeUtilsTest, LogicalToPhysicalVlrLtr) {
+ LogicalToPhysical<int> converter(WritingMode::kVerticalLr,
+ TextDirection::kLtr, kInlineStart,
+ kInlineEnd, kBlockStart, kBlockEnd);
+ EXPECT_EQ(kBlockStart, converter.Left());
+ EXPECT_EQ(kBlockEnd, converter.Right());
+ EXPECT_EQ(kInlineStart, converter.Top());
+ EXPECT_EQ(kInlineEnd, converter.Bottom());
+}
+
+TEST(WritingModeUtilsTest, LogicalToPhysicalVlrRtl) {
+ LogicalToPhysical<int> converter(WritingMode::kVerticalLr,
+ TextDirection::kRtl, kInlineStart,
+ kInlineEnd, kBlockStart, kBlockEnd);
+ EXPECT_EQ(kBlockStart, converter.Left());
+ EXPECT_EQ(kBlockEnd, converter.Right());
+ EXPECT_EQ(kInlineEnd, converter.Top());
+ EXPECT_EQ(kInlineStart, converter.Bottom());
+}
+
+TEST(WritingModeUtilsTest, LogicalToPhysicalVrlLtr) {
+ LogicalToPhysical<int> converter(WritingMode::kVerticalRl,
+ TextDirection::kLtr, kInlineStart,
+ kInlineEnd, kBlockStart, kBlockEnd);
+ EXPECT_EQ(kBlockEnd, converter.Left());
+ EXPECT_EQ(kBlockStart, converter.Right());
+ EXPECT_EQ(kInlineStart, converter.Top());
+ EXPECT_EQ(kInlineEnd, converter.Bottom());
+}
+
+TEST(WritingModeUtilsTest, LogicalToPhysicalVrlRtl) {
+ LogicalToPhysical<int> converter(WritingMode::kVerticalRl,
+ TextDirection::kRtl, kInlineStart,
+ kInlineEnd, kBlockStart, kBlockEnd);
+ EXPECT_EQ(kBlockEnd, converter.Left());
+ EXPECT_EQ(kBlockStart, converter.Right());
+ EXPECT_EQ(kInlineEnd, converter.Top());
+ EXPECT_EQ(kInlineStart, converter.Bottom());
+}
+
+class PhysicalValues {
+ public:
+ int Top() const { return top_; }
+ int Right() const { return right_; }
+ int Bottom() const { return bottom_; }
+ int Left() const { return left_; }
+ void SetTop(int top) { top_ = top; }
+ void SetRight(int right) { right_ = right; }
+ void SetBottom(int bottom) { bottom_ = bottom; }
+ void SetLeft(int left) { left_ = left; }
+
+ private:
+ int top_ = kTop;
+ int right_ = kRight;
+ int bottom_ = kBottom;
+ int left_ = kLeft;
+};
+
+TEST(WritingModeUtilsTest, PhysicalToLogicalGetter) {
+ PhysicalValues physical_values;
+ PhysicalToLogicalGetter<int, PhysicalValues> getter(
+ WritingMode::kVerticalRl, TextDirection::kRtl, physical_values,
+ &PhysicalValues::Top, &PhysicalValues::Right, &PhysicalValues::Bottom,
+ &PhysicalValues::Left);
+
+ EXPECT_EQ(kBottom, getter.InlineStart());
+ EXPECT_EQ(kTop, getter.InlineEnd());
+ EXPECT_EQ(kRight, getter.BlockStart());
+ EXPECT_EQ(kLeft, getter.BlockEnd());
+ EXPECT_EQ(kTop, getter.LineLeft());
+ EXPECT_EQ(kBottom, getter.LineRight());
+ EXPECT_EQ(kRight, getter.Over());
+ EXPECT_EQ(kLeft, getter.Under());
+ CheckLegacyLogicalDirections(getter);
+}
+
+TEST(WritingModeUtilsTest, LogicalToPhysicalSetter) {
+ PhysicalValues physical_values;
+ LogicalToPhysicalSetter<int, PhysicalValues> setter(
+ WritingMode::kVerticalRl, TextDirection::kRtl, physical_values,
+ &PhysicalValues::SetTop, &PhysicalValues::SetRight,
+ &PhysicalValues::SetBottom, &PhysicalValues::SetLeft);
+ setter.SetInlineStart(kInlineStart);
+ setter.SetInlineEnd(kInlineEnd);
+ setter.SetBlockStart(kBlockStart);
+ setter.SetBlockEnd(kBlockEnd);
+
+ EXPECT_EQ(kBlockEnd, physical_values.Left());
+ EXPECT_EQ(kBlockStart, physical_values.Right());
+ EXPECT_EQ(kInlineEnd, physical_values.Top());
+ EXPECT_EQ(kInlineStart, physical_values.Bottom());
+
+ setter.SetStart(kInlineStart);
+ setter.SetEnd(kInlineEnd);
+ setter.SetBefore(kBlockStart);
+ setter.SetAfter(kBlockEnd);
+
+ EXPECT_EQ(kBlockEnd, physical_values.Left());
+ EXPECT_EQ(kBlockStart, physical_values.Right());
+ EXPECT_EQ(kInlineEnd, physical_values.Top());
+ EXPECT_EQ(kInlineStart, physical_values.Bottom());
+
+ setter.SetLineRight(kInlineStart);
+ setter.SetLineLeft(kInlineEnd);
+ setter.SetOver(kBlockStart);
+ setter.SetUnder(kBlockEnd);
+
+ EXPECT_EQ(kBlockEnd, physical_values.Left());
+ EXPECT_EQ(kBlockStart, physical_values.Right());
+ EXPECT_EQ(kInlineEnd, physical_values.Top());
+ EXPECT_EQ(kInlineStart, physical_values.Bottom());
+}
+
+class LogicalValues {
+ public:
+ int InlineStart() const { return inline_start_; }
+ int InlineEnd() const { return inline_end_; }
+ int BlockStart() const { return block_start_; }
+ int BlockEnd() const { return block_end_; }
+ void SetInlineStart(int inline_start) { inline_start_ = inline_start; }
+ void SetInlineEnd(int inline_end) { inline_end_ = inline_end; }
+ void SetBlockStart(int block_start) { block_start_ = block_start; }
+ void SetBlockEnd(int block_end) { block_end_ = block_end; }
+
+ private:
+ int inline_start_ = kInlineStart;
+ int inline_end_ = kInlineEnd;
+ int block_start_ = kBlockStart;
+ int block_end_ = kBlockEnd;
+};
+
+TEST(WritingModeUtilsTest, LogicalToPhysicalGetter) {
+ LogicalValues logical_values;
+ LogicalToPhysicalGetter<int, LogicalValues> getter(
+ WritingMode::kVerticalRl, TextDirection::kRtl, logical_values,
+ &LogicalValues::InlineStart, &LogicalValues::InlineEnd,
+ &LogicalValues::BlockStart, &LogicalValues::BlockEnd);
+
+ EXPECT_EQ(kBlockEnd, getter.Left());
+ EXPECT_EQ(kBlockStart, getter.Right());
+ EXPECT_EQ(kInlineEnd, getter.Top());
+ EXPECT_EQ(kInlineStart, getter.Bottom());
+}
+
+TEST(WritingModeUtilsTest, PhysicalToLogicalSetter) {
+ LogicalValues logical_values;
+ PhysicalToLogicalSetter<int, LogicalValues> setter(
+ WritingMode::kVerticalRl, TextDirection::kRtl, logical_values,
+ &LogicalValues::SetInlineStart, &LogicalValues::SetInlineEnd,
+ &LogicalValues::SetBlockStart, &LogicalValues::SetBlockEnd);
+ setter.SetTop(kTop);
+ setter.SetRight(kRight);
+ setter.SetBottom(kBottom);
+ setter.SetLeft(kLeft);
+
+ EXPECT_EQ(kBottom, logical_values.InlineStart());
+ EXPECT_EQ(kTop, logical_values.InlineEnd());
+ EXPECT_EQ(kRight, logical_values.BlockStart());
+ EXPECT_EQ(kLeft, logical_values.BlockEnd());
+}
+
+} // namespace
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer.cc b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer.cc
new file mode 100644
index 00000000000..e5c06f75000
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer.cc
@@ -0,0 +1,91 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/wtf/text/text_codec.h"
+
+#include "third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.h"
+#include "third_party/blink/renderer/platform/testing/fuzzed_data_provider.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_encoding_registry.h"
+
+using namespace blink;
+
+// TODO(jsbell): This fuzzes code in wtf/ but has dependencies on platform/,
+// so it must live in the latter directory. Once wtf/ moves into platform/wtf
+// this should move there as well.
+
+WTF::FlushBehavior kFlushBehavior[] = {WTF::kDoNotFlush, WTF::kFetchEOF,
+ WTF::kDataEOF};
+
+WTF::UnencodableHandling kUnencodableHandlingOptions[] = {
+ WTF::kEntitiesForUnencodables, WTF::kURLEncodedEntitiesForUnencodables,
+ WTF::kCSSEncodedEntitiesForUnencodables};
+
+class TextCodecFuzzHarness {};
+
+// Fuzzer for WTF::TextCodec.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ static BlinkFuzzerTestSupport test_support = BlinkFuzzerTestSupport();
+ // The fuzzer picks 3 bytes off the end of the data to initialize metadata, so
+ // abort if the input is smaller than that.
+ if (size < 3)
+ return 0;
+
+ // TODO(csharrison): When crbug.com/701825 is resolved, add the rest of the
+ // text codecs.
+
+ // Initializes the codec map.
+ static const WTF::TextEncoding encoding = WTF::TextEncoding(
+#if defined(UTF_8)
+ "UTF-8"
+#elif defined(WINDOWS_1252)
+ "windows-1252"
+#endif
+ "");
+
+ FuzzedDataProvider fuzzedData(data, size);
+
+ // Initialize metadata using the fuzzed data.
+ bool stopOnError = fuzzedData.ConsumeBool();
+ WTF::UnencodableHandling unencodableHandling =
+ fuzzedData.PickValueInArray(kUnencodableHandlingOptions);
+ WTF::FlushBehavior flushBehavior =
+ fuzzedData.PickValueInArray(kFlushBehavior);
+
+ // Now, use the rest of the fuzzy data to stress test decoding and encoding.
+ const CString byteString = fuzzedData.ConsumeRemainingBytes();
+ std::unique_ptr<TextCodec> codec = NewTextCodec(encoding);
+
+ // Treat as bytes-off-the-wire.
+ bool sawError;
+ const String decoded = codec->Decode(byteString.data(), byteString.length(),
+ flushBehavior, stopOnError, sawError);
+
+ // Treat as blink 8-bit string (latin1).
+ if (size % sizeof(LChar) == 0) {
+ std::unique_ptr<TextCodec> codec = NewTextCodec(encoding);
+ codec->Encode(reinterpret_cast<const LChar*>(byteString.data()),
+ byteString.length() / sizeof(LChar), unencodableHandling);
+ }
+
+ // Treat as blink 16-bit string (utf-16) if there are an even number of bytes.
+ if (size % sizeof(UChar) == 0) {
+ std::unique_ptr<TextCodec> codec = NewTextCodec(encoding);
+ codec->Encode(reinterpret_cast<const UChar*>(byteString.data()),
+ byteString.length() / sizeof(UChar), unencodableHandling);
+ }
+
+ if (decoded.IsNull())
+ return 0;
+
+ // Round trip the bytes (aka encode the decoded bytes).
+ if (decoded.Is8Bit()) {
+ codec->Encode(decoded.Characters8(), decoded.length(), unencodableHandling);
+ } else {
+ codec->Encode(decoded.Characters16(), decoded.length(),
+ unencodableHandling);
+ }
+ return 0;
+}
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0091b89571c844a754df4bdd133ad4d5b628e51d b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0091b89571c844a754df4bdd133ad4d5b628e51d
new file mode 100644
index 00000000000..ffdfb4e054f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0091b89571c844a754df4bdd133ad4d5b628e51d
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/017fd5359998719d5fa3b3df5fc7b72fdb163936 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/017fd5359998719d5fa3b3df5fc7b72fdb163936
new file mode 100644
index 00000000000..de4806d66d0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/017fd5359998719d5fa3b3df5fc7b72fdb163936
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/031ccb60903b3cd5780b6d3743051e216068b7cc b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/031ccb60903b3cd5780b6d3743051e216068b7cc
new file mode 100644
index 00000000000..f41caf2ce29
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/031ccb60903b3cd5780b6d3743051e216068b7cc
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/037017bbfe44de68091da219ac9f83d9100e4b6a b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/037017bbfe44de68091da219ac9f83d9100e4b6a
new file mode 100644
index 00000000000..4196922ba46
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/037017bbfe44de68091da219ac9f83d9100e4b6a
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/03cc7ffa27b3a0309adfa4eed49fae9c7a91c970 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/03cc7ffa27b3a0309adfa4eed49fae9c7a91c970
new file mode 100644
index 00000000000..33086dd26b8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/03cc7ffa27b3a0309adfa4eed49fae9c7a91c970
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0425ce6fe2ee20810034f7c8347fe3f3a323f0e4 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0425ce6fe2ee20810034f7c8347fe3f3a323f0e4
new file mode 100644
index 00000000000..9fd0f972b95
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0425ce6fe2ee20810034f7c8347fe3f3a323f0e4
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/04bff48dcd66a033a9892df1ea38ca038436b3bb b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/04bff48dcd66a033a9892df1ea38ca038436b3bb
new file mode 100644
index 00000000000..9b18f270b72
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/04bff48dcd66a033a9892df1ea38ca038436b3bb
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/04ecc32312c98ebb1c08f6d57d98c7520a3b0d0f b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/04ecc32312c98ebb1c08f6d57d98c7520a3b0d0f
new file mode 100644
index 00000000000..f9dbe2143c8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/04ecc32312c98ebb1c08f6d57d98c7520a3b0d0f
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/06ae60aae063a917d795388c4c36230286745111 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/06ae60aae063a917d795388c4c36230286745111
new file mode 100644
index 00000000000..84633482490
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/06ae60aae063a917d795388c4c36230286745111
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/075d2498e7c5370f5b4ad4d3a39b0aeb7e7b6242 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/075d2498e7c5370f5b4ad4d3a39b0aeb7e7b6242
new file mode 100644
index 00000000000..d9e9748472b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/075d2498e7c5370f5b4ad4d3a39b0aeb7e7b6242
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/07a75c4fd6ce06b8b82c40108c1333fab8cdf7a7 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/07a75c4fd6ce06b8b82c40108c1333fab8cdf7a7
new file mode 100644
index 00000000000..5f33107ccb5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/07a75c4fd6ce06b8b82c40108c1333fab8cdf7a7
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/07e318cd144fc7efe7b9e95f065bd8e35189287b b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/07e318cd144fc7efe7b9e95f065bd8e35189287b
new file mode 100644
index 00000000000..e0a16b1fdb3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/07e318cd144fc7efe7b9e95f065bd8e35189287b
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/07ecf359453ac3521a0254b50fd2f2c2e90b481d b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/07ecf359453ac3521a0254b50fd2f2c2e90b481d
new file mode 100644
index 00000000000..d7bc861343f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/07ecf359453ac3521a0254b50fd2f2c2e90b481d
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0860ddb77abbc133ca7cf871020979abf1f82c0d b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0860ddb77abbc133ca7cf871020979abf1f82c0d
new file mode 100644
index 00000000000..0ec83e185b6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0860ddb77abbc133ca7cf871020979abf1f82c0d
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/090a9ed30399e75da9d5ec8ee1b09668881c82be b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/090a9ed30399e75da9d5ec8ee1b09668881c82be
new file mode 100644
index 00000000000..623e7ac59e4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/090a9ed30399e75da9d5ec8ee1b09668881c82be
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0952e648c4e17520dd85935964fee52375ecfedb b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0952e648c4e17520dd85935964fee52375ecfedb
new file mode 100644
index 00000000000..77e0a7c2d76
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0952e648c4e17520dd85935964fee52375ecfedb
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/096684dc01d867661fe2f7455f9ab22e0857d7e4 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/096684dc01d867661fe2f7455f9ab22e0857d7e4
new file mode 100644
index 00000000000..eff233e2a3d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/096684dc01d867661fe2f7455f9ab22e0857d7e4
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/09ec95829dbcf10db5349e44d7b4d442cc3646b6 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/09ec95829dbcf10db5349e44d7b4d442cc3646b6
new file mode 100644
index 00000000000..7f36eb4fdd4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/09ec95829dbcf10db5349e44d7b4d442cc3646b6
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0a80baa1797615faddb0ccfaa6d46382a6b3e0e2 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0a80baa1797615faddb0ccfaa6d46382a6b3e0e2
new file mode 100644
index 00000000000..37905719309
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0a80baa1797615faddb0ccfaa6d46382a6b3e0e2
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0b49f431e39df252e4b2404c7ead05fd97f3e23f b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0b49f431e39df252e4b2404c7ead05fd97f3e23f
new file mode 100644
index 00000000000..6fc4ec41ce5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0b49f431e39df252e4b2404c7ead05fd97f3e23f
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0c23817d368a1d420721709b639d99933b51fa45 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0c23817d368a1d420721709b639d99933b51fa45
new file mode 100644
index 00000000000..0a385b10cfa
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0c23817d368a1d420721709b639d99933b51fa45
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0cdb4b25ce305c78897a92f359a2c8c1477ba571 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0cdb4b25ce305c78897a92f359a2c8c1477ba571
new file mode 100644
index 00000000000..f21643a1b34
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0cdb4b25ce305c78897a92f359a2c8c1477ba571
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0d015b964100880a09aaa492447c5ea1ca60aaa4 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0d015b964100880a09aaa492447c5ea1ca60aaa4
new file mode 100644
index 00000000000..55e0e3a4916
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0d015b964100880a09aaa492447c5ea1ca60aaa4
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0d3ce4f01204e526d5bc5afb7a754235423044c1 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0d3ce4f01204e526d5bc5afb7a754235423044c1
new file mode 100644
index 00000000000..0fb05d355d8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0d3ce4f01204e526d5bc5afb7a754235423044c1
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0db82720176ea60c0948406dc77d8fe6d225f377 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0db82720176ea60c0948406dc77d8fe6d225f377
new file mode 100644
index 00000000000..6bd0d533400
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0db82720176ea60c0948406dc77d8fe6d225f377
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0df9cfd31ac0bd6d932145f32e68bc65cc883da8 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0df9cfd31ac0bd6d932145f32e68bc65cc883da8
new file mode 100644
index 00000000000..417b4d0b231
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0df9cfd31ac0bd6d932145f32e68bc65cc883da8
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0eb1c6c9f8227944aef1fd3dcb413b7e592d7bfe b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0eb1c6c9f8227944aef1fd3dcb413b7e592d7bfe
new file mode 100644
index 00000000000..28788bbd585
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0eb1c6c9f8227944aef1fd3dcb413b7e592d7bfe
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0ed3b05749ab8c5346fe983701ed388589f31012 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0ed3b05749ab8c5346fe983701ed388589f31012
new file mode 100644
index 00000000000..18a4a347598
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0ed3b05749ab8c5346fe983701ed388589f31012
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0f639c2b9e1a8ce651e9f78dbba9bf5d1cdec07d b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0f639c2b9e1a8ce651e9f78dbba9bf5d1cdec07d
new file mode 100644
index 00000000000..a4dc1f6dc1d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0f639c2b9e1a8ce651e9f78dbba9bf5d1cdec07d
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0f63d1414ef452e3a0cd6f02426027be24cf05ea b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0f63d1414ef452e3a0cd6f02426027be24cf05ea
new file mode 100644
index 00000000000..258370fe1ce
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/0f63d1414ef452e3a0cd6f02426027be24cf05ea
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/106e06e64de76e45fc8b350b9ac8c86b5c6bc511 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/106e06e64de76e45fc8b350b9ac8c86b5c6bc511
new file mode 100644
index 00000000000..eedc6297c94
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/106e06e64de76e45fc8b350b9ac8c86b5c6bc511
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/12108e195d16ed36f3a2342cbd6fa681e14d27be b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/12108e195d16ed36f3a2342cbd6fa681e14d27be
new file mode 100644
index 00000000000..b659b872558
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/12108e195d16ed36f3a2342cbd6fa681e14d27be
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/122ca249dac2943200b259fd23e18a4cb31035d9 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/122ca249dac2943200b259fd23e18a4cb31035d9
new file mode 100644
index 00000000000..9d888ff00ad
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/122ca249dac2943200b259fd23e18a4cb31035d9
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/13a965f9e03c788dda767783dedbdee04fc921d9 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/13a965f9e03c788dda767783dedbdee04fc921d9
new file mode 100644
index 00000000000..e7404db39de
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/13a965f9e03c788dda767783dedbdee04fc921d9
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/14be6cc2655b8c0eca2e16a205a48b619d4ab329 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/14be6cc2655b8c0eca2e16a205a48b619d4ab329
new file mode 100644
index 00000000000..7c7fda8360b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/14be6cc2655b8c0eca2e16a205a48b619d4ab329
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/155fa724bd473d8d66ea8e5866261ffad848589d b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/155fa724bd473d8d66ea8e5866261ffad848589d
new file mode 100644
index 00000000000..574338364b2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/155fa724bd473d8d66ea8e5866261ffad848589d
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1599e9fa41ec68c80230491902786bee889f5bcb b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1599e9fa41ec68c80230491902786bee889f5bcb
new file mode 100644
index 00000000000..0ce8ab049d5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1599e9fa41ec68c80230491902786bee889f5bcb
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/160fd1afc477f8f5f46b25291b4b33098f4f00f6 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/160fd1afc477f8f5f46b25291b4b33098f4f00f6
new file mode 100644
index 00000000000..c687e5956dc
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/160fd1afc477f8f5f46b25291b4b33098f4f00f6
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/165435e80cf32dfbb9fad8d24e0d2c92d1814b1f b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/165435e80cf32dfbb9fad8d24e0d2c92d1814b1f
new file mode 100644
index 00000000000..3d038907a9b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/165435e80cf32dfbb9fad8d24e0d2c92d1814b1f
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/167609a843b8eaa0d6db4375f66fda15461950be b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/167609a843b8eaa0d6db4375f66fda15461950be
new file mode 100644
index 00000000000..8603fdcf5e9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/167609a843b8eaa0d6db4375f66fda15461950be
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/167749d0e421d223150a6c4248f8b355e8ce66b0 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/167749d0e421d223150a6c4248f8b355e8ce66b0
new file mode 100644
index 00000000000..8df5bd32d63
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/167749d0e421d223150a6c4248f8b355e8ce66b0
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1714db8daebbab059a62b71f571565125c0a297b b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1714db8daebbab059a62b71f571565125c0a297b
new file mode 100644
index 00000000000..5bf0c3d6ef3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1714db8daebbab059a62b71f571565125c0a297b
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/18310219d1b7f501d07dff568926bcf8d997e6d3 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/18310219d1b7f501d07dff568926bcf8d997e6d3
new file mode 100644
index 00000000000..a76d7a47eae
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/18310219d1b7f501d07dff568926bcf8d997e6d3
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/19c42d45aa7d98a0ef2f2de2997b6e22aa1a8f1c b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/19c42d45aa7d98a0ef2f2de2997b6e22aa1a8f1c
new file mode 100644
index 00000000000..54639d7a5c9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/19c42d45aa7d98a0ef2f2de2997b6e22aa1a8f1c
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1a0cb01355c7db499eccf0dc45dc27ec89856f39 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1a0cb01355c7db499eccf0dc45dc27ec89856f39
new file mode 100644
index 00000000000..a4502e10e5c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1a0cb01355c7db499eccf0dc45dc27ec89856f39
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1b181fded6cd8ca58cfde81415b702b0e3da82a1 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1b181fded6cd8ca58cfde81415b702b0e3da82a1
new file mode 100644
index 00000000000..23e9dd6f714
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1b181fded6cd8ca58cfde81415b702b0e3da82a1
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1d8207d5b9b1abea52fb7d5d8c6ef2a0d5068ffe b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1d8207d5b9b1abea52fb7d5d8c6ef2a0d5068ffe
new file mode 100644
index 00000000000..df62c261a2c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1d8207d5b9b1abea52fb7d5d8c6ef2a0d5068ffe
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1e1946a1b74ecf5e21460531d89043b59b15270a b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1e1946a1b74ecf5e21460531d89043b59b15270a
new file mode 100644
index 00000000000..a89b2742815
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1e1946a1b74ecf5e21460531d89043b59b15270a
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1e1d033b68bcb40e166166d8d86be1350231d232 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1e1d033b68bcb40e166166d8d86be1350231d232
new file mode 100644
index 00000000000..1b4957b510e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1e1d033b68bcb40e166166d8d86be1350231d232
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1eb24b4138ebcc36e94021f06f7f89c64cd4b583 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1eb24b4138ebcc36e94021f06f7f89c64cd4b583
new file mode 100644
index 00000000000..5d31b4717c6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1eb24b4138ebcc36e94021f06f7f89c64cd4b583
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1ee6992c347409ea83901afbcad6666cf169f983 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1ee6992c347409ea83901afbcad6666cf169f983
new file mode 100644
index 00000000000..6d00b847735
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1ee6992c347409ea83901afbcad6666cf169f983
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1f412eca783baa2cebfdfa7d20b6d64e4ae712cd b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1f412eca783baa2cebfdfa7d20b6d64e4ae712cd
new file mode 100644
index 00000000000..a4330b3f5fa
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/1f412eca783baa2cebfdfa7d20b6d64e4ae712cd
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/201c21a10bd4fb38e8f399951ba4d303821f2dea b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/201c21a10bd4fb38e8f399951ba4d303821f2dea
new file mode 100644
index 00000000000..45b96e29312
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/201c21a10bd4fb38e8f399951ba4d303821f2dea
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/20f68a183711473f34885a340a83207791d73004 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/20f68a183711473f34885a340a83207791d73004
new file mode 100644
index 00000000000..7fbd27560e6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/20f68a183711473f34885a340a83207791d73004
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/22064b5d3d2b8920c85c3cd43ff9d0442f21c021 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/22064b5d3d2b8920c85c3cd43ff9d0442f21c021
new file mode 100644
index 00000000000..a1ef306ff35
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/22064b5d3d2b8920c85c3cd43ff9d0442f21c021
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/226493237f5a5d1ef606df325650862279f45c80 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/226493237f5a5d1ef606df325650862279f45c80
new file mode 100644
index 00000000000..ced687852d1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/226493237f5a5d1ef606df325650862279f45c80
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/22a3b3ca448b9873d362154bb4a0889e4ae72e7c b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/22a3b3ca448b9873d362154bb4a0889e4ae72e7c
new file mode 100644
index 00000000000..d88b86e9d70
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/22a3b3ca448b9873d362154bb4a0889e4ae72e7c
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2311b8b93a21c9565e1f39ff3749a982a8b21559 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2311b8b93a21c9565e1f39ff3749a982a8b21559
new file mode 100644
index 00000000000..58997c9a368
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2311b8b93a21c9565e1f39ff3749a982a8b21559
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/238b22352e0766896b15e26ee0b184690b39b7c9 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/238b22352e0766896b15e26ee0b184690b39b7c9
new file mode 100644
index 00000000000..4beb931f00d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/238b22352e0766896b15e26ee0b184690b39b7c9
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2393e7d3d00847697336ab38f7b812c2b09ecd60 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2393e7d3d00847697336ab38f7b812c2b09ecd60
new file mode 100644
index 00000000000..0dc43f935d5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2393e7d3d00847697336ab38f7b812c2b09ecd60
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/24c08e3d9ca730a81eac96c1345ae775b10acf5b b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/24c08e3d9ca730a81eac96c1345ae775b10acf5b
new file mode 100644
index 00000000000..ce7f0cda455
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/24c08e3d9ca730a81eac96c1345ae775b10acf5b
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/259b3a4f074e9cb311971fee987a232eaca8a589 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/259b3a4f074e9cb311971fee987a232eaca8a589
new file mode 100644
index 00000000000..1db68e2dedf
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/259b3a4f074e9cb311971fee987a232eaca8a589
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/25a03ae6dc0c77f9f5546031d22050a9374067bb b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/25a03ae6dc0c77f9f5546031d22050a9374067bb
new file mode 100644
index 00000000000..c61d5c19647
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/25a03ae6dc0c77f9f5546031d22050a9374067bb
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/264ff1528d215678b7d0cab6f30d75067a8b69ce b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/264ff1528d215678b7d0cab6f30d75067a8b69ce
new file mode 100644
index 00000000000..fad89291774
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/264ff1528d215678b7d0cab6f30d75067a8b69ce
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/265e53fceb8d465cbc82135e9dfdc45d78c50af9 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/265e53fceb8d465cbc82135e9dfdc45d78c50af9
new file mode 100644
index 00000000000..8bb8f1c1192
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/265e53fceb8d465cbc82135e9dfdc45d78c50af9
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/266ddd93d187e4bd0ac6495cedd8ebcbf55aa171 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/266ddd93d187e4bd0ac6495cedd8ebcbf55aa171
new file mode 100644
index 00000000000..a79499591ca
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/266ddd93d187e4bd0ac6495cedd8ebcbf55aa171
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/267e1b92f50fb25b7d6995d72ba2b607c491094e b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/267e1b92f50fb25b7d6995d72ba2b607c491094e
new file mode 100644
index 00000000000..392c3623e9f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/267e1b92f50fb25b7d6995d72ba2b607c491094e
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/268554ad42965d1d12fc2e14f6cd25dde82cbf22 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/268554ad42965d1d12fc2e14f6cd25dde82cbf22
new file mode 100644
index 00000000000..6eab3d6f5d3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/268554ad42965d1d12fc2e14f6cd25dde82cbf22
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/26b5604da5596b2733b603a176babce7db65084f b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/26b5604da5596b2733b603a176babce7db65084f
new file mode 100644
index 00000000000..d3f13513127
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/26b5604da5596b2733b603a176babce7db65084f
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/26e268692cf273e5ca7686759ffba67ab0c2a962 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/26e268692cf273e5ca7686759ffba67ab0c2a962
new file mode 100644
index 00000000000..2482d1fd65d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/26e268692cf273e5ca7686759ffba67ab0c2a962
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2751c451ecf0e2e1e61b14d5bc42fc96f8ee9a90 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2751c451ecf0e2e1e61b14d5bc42fc96f8ee9a90
new file mode 100644
index 00000000000..75d303c083f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2751c451ecf0e2e1e61b14d5bc42fc96f8ee9a90
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/29ed2cf7b7d7844677c23fcebf5d6d7c7c745433 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/29ed2cf7b7d7844677c23fcebf5d6d7c7c745433
new file mode 100644
index 00000000000..ba745dc0809
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/29ed2cf7b7d7844677c23fcebf5d6d7c7c745433
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/29ed3e6d98b4e03cb088941a472c74e258991dca b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/29ed3e6d98b4e03cb088941a472c74e258991dca
new file mode 100644
index 00000000000..a644cf086fc
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/29ed3e6d98b4e03cb088941a472c74e258991dca
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/29f498a7b643fad9f03c5563bec1d57177b55655 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/29f498a7b643fad9f03c5563bec1d57177b55655
new file mode 100644
index 00000000000..a0b34818f91
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/29f498a7b643fad9f03c5563bec1d57177b55655
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2a2568f696a5396ebf6a29f30d3e7da7517d583f b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2a2568f696a5396ebf6a29f30d3e7da7517d583f
new file mode 100644
index 00000000000..eaa166fffa1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2a2568f696a5396ebf6a29f30d3e7da7517d583f
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2b3dece417ae8dd46a921e70290367680dbc2ad4 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2b3dece417ae8dd46a921e70290367680dbc2ad4
new file mode 100644
index 00000000000..9268e161815
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2b3dece417ae8dd46a921e70290367680dbc2ad4
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2b465dd11f7a27dca616dfe36281f590128f4eb6 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2b465dd11f7a27dca616dfe36281f590128f4eb6
new file mode 100644
index 00000000000..653a7bd2098
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2b465dd11f7a27dca616dfe36281f590128f4eb6
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2bf3a0edb8d97471d635d43bbb2126018649437f b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2bf3a0edb8d97471d635d43bbb2126018649437f
new file mode 100644
index 00000000000..9764f4326d3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2bf3a0edb8d97471d635d43bbb2126018649437f
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2c3338b0400e039743ad2fa537cbbd89d4aa8824 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2c3338b0400e039743ad2fa537cbbd89d4aa8824
new file mode 100644
index 00000000000..9e958c10496
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2c3338b0400e039743ad2fa537cbbd89d4aa8824
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2c3a0197ebe801d0982f5230568081c936f95e28 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2c3a0197ebe801d0982f5230568081c936f95e28
new file mode 100644
index 00000000000..839a7cf02d1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2c3a0197ebe801d0982f5230568081c936f95e28
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2cbf6e11f35337aba018755545eacb3a08443337 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2cbf6e11f35337aba018755545eacb3a08443337
new file mode 100644
index 00000000000..4c5a9329768
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2cbf6e11f35337aba018755545eacb3a08443337
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2dd374ed8f7a33fdaeab8e161a010400340210c4 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2dd374ed8f7a33fdaeab8e161a010400340210c4
new file mode 100644
index 00000000000..c153df97948
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2dd374ed8f7a33fdaeab8e161a010400340210c4
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2f05530ca067c990cdc6283661f950ddca781d4a b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2f05530ca067c990cdc6283661f950ddca781d4a
new file mode 100644
index 00000000000..cccb2dd440a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2f05530ca067c990cdc6283661f950ddca781d4a
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2f235b7ba2d613888ad53a1430e1043752a3fb9a b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2f235b7ba2d613888ad53a1430e1043752a3fb9a
new file mode 100644
index 00000000000..f5d5e489f8f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2f235b7ba2d613888ad53a1430e1043752a3fb9a
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2f336d0e17a48e2edc13a2e8a437aa9773099608 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2f336d0e17a48e2edc13a2e8a437aa9773099608
new file mode 100644
index 00000000000..a9e0d9cd5ac
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2f336d0e17a48e2edc13a2e8a437aa9773099608
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2f3693dfdbb384b066ab8b2f69473456b03e5c2b b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2f3693dfdbb384b066ab8b2f69473456b03e5c2b
new file mode 100644
index 00000000000..6427966dc9f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2f3693dfdbb384b066ab8b2f69473456b03e5c2b
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2fb8064bc537fea2daec0ab6ae773577314b6876 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2fb8064bc537fea2daec0ab6ae773577314b6876
new file mode 100644
index 00000000000..6e23ec8b773
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2fb8064bc537fea2daec0ab6ae773577314b6876
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2fe44b618a89d1cdf9627297460f36d6e927ebd6 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2fe44b618a89d1cdf9627297460f36d6e927ebd6
new file mode 100644
index 00000000000..28d73e290b0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/2fe44b618a89d1cdf9627297460f36d6e927ebd6
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/30140397fe38ee61f01eff44b5cfa48285e47889 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/30140397fe38ee61f01eff44b5cfa48285e47889
new file mode 100644
index 00000000000..430fee49a73
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/30140397fe38ee61f01eff44b5cfa48285e47889
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/302de7fd35416714976c8b80d1aa3e64b604c671 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/302de7fd35416714976c8b80d1aa3e64b604c671
new file mode 100644
index 00000000000..b6f366e72e3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/302de7fd35416714976c8b80d1aa3e64b604c671
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/30af3da9ce882193e3588303797edb80ebaabd6b b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/30af3da9ce882193e3588303797edb80ebaabd6b
new file mode 100644
index 00000000000..9468712384c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/30af3da9ce882193e3588303797edb80ebaabd6b
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/30fd500a65dd24adaa7b516381301a183f08508e b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/30fd500a65dd24adaa7b516381301a183f08508e
new file mode 100644
index 00000000000..b73954107d4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/30fd500a65dd24adaa7b516381301a183f08508e
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/315c8c4c6165a05a6003907cf44bc7ac1d367d82 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/315c8c4c6165a05a6003907cf44bc7ac1d367d82
new file mode 100644
index 00000000000..e5f272f20d1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/315c8c4c6165a05a6003907cf44bc7ac1d367d82
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/31a9912eb9e093fcdce27d83ff1e9bbee5fe34e3 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/31a9912eb9e093fcdce27d83ff1e9bbee5fe34e3
new file mode 100644
index 00000000000..8452caf3b7c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/31a9912eb9e093fcdce27d83ff1e9bbee5fe34e3
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/31fd332b18f4960afad7af5b4a8ed9027e74da0f b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/31fd332b18f4960afad7af5b4a8ed9027e74da0f
new file mode 100644
index 00000000000..09e334e8b92
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/31fd332b18f4960afad7af5b4a8ed9027e74da0f
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/32ae073bdd9501d7617c7b2afeb53ca0c15f2ea4 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/32ae073bdd9501d7617c7b2afeb53ca0c15f2ea4
new file mode 100644
index 00000000000..f300f990232
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/32ae073bdd9501d7617c7b2afeb53ca0c15f2ea4
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/331c576d7334011219b2455e07e52c3633769585 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/331c576d7334011219b2455e07e52c3633769585
new file mode 100644
index 00000000000..4d9d9fc1371
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/331c576d7334011219b2455e07e52c3633769585
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3382c113667455cd7b38c8b41662fdddd3e60bf3 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3382c113667455cd7b38c8b41662fdddd3e60bf3
new file mode 100644
index 00000000000..9dc13216b1d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3382c113667455cd7b38c8b41662fdddd3e60bf3
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/345cdc7aadb5e1f24dd5708eaafe88ad5e1852af b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/345cdc7aadb5e1f24dd5708eaafe88ad5e1852af
new file mode 100644
index 00000000000..58bff1f6017
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/345cdc7aadb5e1f24dd5708eaafe88ad5e1852af
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/345ebd88fd96892142cef88bdefc7c16d3216a0d b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/345ebd88fd96892142cef88bdefc7c16d3216a0d
new file mode 100644
index 00000000000..fc224c983b2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/345ebd88fd96892142cef88bdefc7c16d3216a0d
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3479cba50395ea6785a51cfe3d247634f5e601fe b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3479cba50395ea6785a51cfe3d247634f5e601fe
new file mode 100644
index 00000000000..e5e5c0c2195
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3479cba50395ea6785a51cfe3d247634f5e601fe
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/34bc251961f054bf752bf103738399ba4eaa09c0 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/34bc251961f054bf752bf103738399ba4eaa09c0
new file mode 100644
index 00000000000..7a8170dab07
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/34bc251961f054bf752bf103738399ba4eaa09c0
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/34c2d14e77be385419644a3f15dbb3fea62724c2 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/34c2d14e77be385419644a3f15dbb3fea62724c2
new file mode 100644
index 00000000000..ecf2f86aa5a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/34c2d14e77be385419644a3f15dbb3fea62724c2
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/365791bfe51a4809b7b1be97a6c6064fff34245e b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/365791bfe51a4809b7b1be97a6c6064fff34245e
new file mode 100644
index 00000000000..e0cd8e956c7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/365791bfe51a4809b7b1be97a6c6064fff34245e
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/36a915cb8f078a9ed3b744c4f876ecc946a2ec5f b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/36a915cb8f078a9ed3b744c4f876ecc946a2ec5f
new file mode 100644
index 00000000000..03f29fa4716
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/36a915cb8f078a9ed3b744c4f876ecc946a2ec5f
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/370b4d8ccb0d0671e7c66651585703815fe16c09 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/370b4d8ccb0d0671e7c66651585703815fe16c09
new file mode 100644
index 00000000000..6116e535739
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/370b4d8ccb0d0671e7c66651585703815fe16c09
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/37b1e6b8ddaac24c3f3fa3b83646c4cc5ea4fcef b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/37b1e6b8ddaac24c3f3fa3b83646c4cc5ea4fcef
new file mode 100644
index 00000000000..532feb7c3bf
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/37b1e6b8ddaac24c3f3fa3b83646c4cc5ea4fcef
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/37daaae08ca0337f2b50a5db48171e8680583241 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/37daaae08ca0337f2b50a5db48171e8680583241
new file mode 100644
index 00000000000..407ea585b51
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/37daaae08ca0337f2b50a5db48171e8680583241
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3807d30510e275eeba48f261f1e1d72eca14b095 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3807d30510e275eeba48f261f1e1d72eca14b095
new file mode 100644
index 00000000000..59d2021803b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3807d30510e275eeba48f261f1e1d72eca14b095
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/383c7323b00bd06cc911bf28f025752cdb9d088e b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/383c7323b00bd06cc911bf28f025752cdb9d088e
new file mode 100644
index 00000000000..f0caaf12d7f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/383c7323b00bd06cc911bf28f025752cdb9d088e
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/386563dc4dbfd50a632e9ecf03b99e6955e56ee1 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/386563dc4dbfd50a632e9ecf03b99e6955e56ee1
new file mode 100644
index 00000000000..2e9a88d7da5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/386563dc4dbfd50a632e9ecf03b99e6955e56ee1
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/38e857fcb5086ca80620c1c77dacd93dd70c7247 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/38e857fcb5086ca80620c1c77dacd93dd70c7247
new file mode 100644
index 00000000000..5f4201a7652
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/38e857fcb5086ca80620c1c77dacd93dd70c7247
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/38f1413cd7389276c46425fbc06623ffca06ab8b b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/38f1413cd7389276c46425fbc06623ffca06ab8b
new file mode 100644
index 00000000000..f0bb5485cbd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/38f1413cd7389276c46425fbc06623ffca06ab8b
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3a387a4da1d05575e4154e689d3b7aea816c9955 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3a387a4da1d05575e4154e689d3b7aea816c9955
new file mode 100644
index 00000000000..b8e90d1d070
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3a387a4da1d05575e4154e689d3b7aea816c9955
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3a3e4ccd65af3efcb6717be0828690ad6196b532 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3a3e4ccd65af3efcb6717be0828690ad6196b532
new file mode 100644
index 00000000000..54c366155c8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3a3e4ccd65af3efcb6717be0828690ad6196b532
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3a5a90e4668e4112a67184bda480990ed4e25a18 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3a5a90e4668e4112a67184bda480990ed4e25a18
new file mode 100644
index 00000000000..67a007d09eb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3a5a90e4668e4112a67184bda480990ed4e25a18
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3a8bcfee35966bf857a833a91d6b3d17c333b961 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3a8bcfee35966bf857a833a91d6b3d17c333b961
new file mode 100644
index 00000000000..065c3cbf533
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3a8bcfee35966bf857a833a91d6b3d17c333b961
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3c4b8716d0701aec5792c6ef51a033487d9f2052 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3c4b8716d0701aec5792c6ef51a033487d9f2052
new file mode 100644
index 00000000000..e9ad184c8da
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3c4b8716d0701aec5792c6ef51a033487d9f2052
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3d15fbef3d7252f176dffffb42d9064ddd10f08e b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3d15fbef3d7252f176dffffb42d9064ddd10f08e
new file mode 100644
index 00000000000..d713abde87e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3d15fbef3d7252f176dffffb42d9064ddd10f08e
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3d8fbe87ab0c655db7c32a846112ad2c9d3e7f4a b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3d8fbe87ab0c655db7c32a846112ad2c9d3e7f4a
new file mode 100644
index 00000000000..1f9b8367f06
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3d8fbe87ab0c655db7c32a846112ad2c9d3e7f4a
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3d9fbb9019796c63c14425c56eb154f8e5d82717 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3d9fbb9019796c63c14425c56eb154f8e5d82717
new file mode 100644
index 00000000000..46d6fabd0e4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/3d9fbb9019796c63c14425c56eb154f8e5d82717
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/405a2d2c0bd3f6762605b612a668efe1763ce75b b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/405a2d2c0bd3f6762605b612a668efe1763ce75b
new file mode 100644
index 00000000000..a270aa3b27d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/405a2d2c0bd3f6762605b612a668efe1763ce75b
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/425098d86e6ab28bd6f17ad53ae09db75b5772e1 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/425098d86e6ab28bd6f17ad53ae09db75b5772e1
new file mode 100644
index 00000000000..9c8808ac57e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/425098d86e6ab28bd6f17ad53ae09db75b5772e1
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/428bc20500605fc0cf424f870d1e8f83c5059df5 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/428bc20500605fc0cf424f870d1e8f83c5059df5
new file mode 100644
index 00000000000..5ad8dfa9231
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/428bc20500605fc0cf424f870d1e8f83c5059df5
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/42bd94f85db3f5ac4c01ff7ba172bc8ddef60432 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/42bd94f85db3f5ac4c01ff7ba172bc8ddef60432
new file mode 100644
index 00000000000..7c6fe0eae70
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/42bd94f85db3f5ac4c01ff7ba172bc8ddef60432
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/43889ce379baa9132173fc398fafe55f3d356505 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/43889ce379baa9132173fc398fafe55f3d356505
new file mode 100644
index 00000000000..9b2fe08797e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/43889ce379baa9132173fc398fafe55f3d356505
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/438f603cb985b367fc217425d8f98200203a4dca b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/438f603cb985b367fc217425d8f98200203a4dca
new file mode 100644
index 00000000000..77a9011e4ca
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/438f603cb985b367fc217425d8f98200203a4dca
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/43c775987fb5eddb58c8c5df5efbc83b06b87546 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/43c775987fb5eddb58c8c5df5efbc83b06b87546
new file mode 100644
index 00000000000..1c339504c29
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/43c775987fb5eddb58c8c5df5efbc83b06b87546
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/45620f98144efb07521ce60df2d85adabb0df2ff b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/45620f98144efb07521ce60df2d85adabb0df2ff
new file mode 100644
index 00000000000..e9fc68c3ef5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/45620f98144efb07521ce60df2d85adabb0df2ff
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4622094cc10659c5b7185c529a4216821d481eee b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4622094cc10659c5b7185c529a4216821d481eee
new file mode 100644
index 00000000000..2c27498d1b9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4622094cc10659c5b7185c529a4216821d481eee
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/465fe4b5501c930d1d14e6ce04057889967c1c27 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/465fe4b5501c930d1d14e6ce04057889967c1c27
new file mode 100644
index 00000000000..0213bf46594
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/465fe4b5501c930d1d14e6ce04057889967c1c27
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/46bf374d6cbe5dcbc6d6642efdc0e21a742bf648 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/46bf374d6cbe5dcbc6d6642efdc0e21a742bf648
new file mode 100644
index 00000000000..ecef766d466
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/46bf374d6cbe5dcbc6d6642efdc0e21a742bf648
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/47bd0fe45358302b5d0ea59d5c81e8a0fe2aed7d b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/47bd0fe45358302b5d0ea59d5c81e8a0fe2aed7d
new file mode 100644
index 00000000000..a77d3f961d8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/47bd0fe45358302b5d0ea59d5c81e8a0fe2aed7d
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4974f2975b9edf3b5f045838364dbb141911ad17 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4974f2975b9edf3b5f045838364dbb141911ad17
new file mode 100644
index 00000000000..ae56910ec47
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4974f2975b9edf3b5f045838364dbb141911ad17
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4988df6115609e79c6a37a06046f47c571ddddeb b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4988df6115609e79c6a37a06046f47c571ddddeb
new file mode 100644
index 00000000000..fafe47c3a4b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4988df6115609e79c6a37a06046f47c571ddddeb
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/498b00ddd7f75a93d2b0731372c290e10020b17b b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/498b00ddd7f75a93d2b0731372c290e10020b17b
new file mode 100644
index 00000000000..521b5e8cf6f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/498b00ddd7f75a93d2b0731372c290e10020b17b
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4a6483b5d52b34b4a0aa9a3a1bc38340161c226e b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4a6483b5d52b34b4a0aa9a3a1bc38340161c226e
new file mode 100644
index 00000000000..ea485d16537
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4a6483b5d52b34b4a0aa9a3a1bc38340161c226e
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4ae70fa44b52ce5c6828ddc648ed0e0748629640 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4ae70fa44b52ce5c6828ddc648ed0e0748629640
new file mode 100644
index 00000000000..29d6eb8d0e7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4ae70fa44b52ce5c6828ddc648ed0e0748629640
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4af42e15d5fa2e5f6f83942c024a11dc7e70d7e7 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4af42e15d5fa2e5f6f83942c024a11dc7e70d7e7
new file mode 100644
index 00000000000..de440fa2fb5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4af42e15d5fa2e5f6f83942c024a11dc7e70d7e7
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4b61b7402eefc4f49ff187c549ce09bc5ea6f549 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4b61b7402eefc4f49ff187c549ce09bc5ea6f549
new file mode 100644
index 00000000000..8b228218dec
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4b61b7402eefc4f49ff187c549ce09bc5ea6f549
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4b8cbf97d57464de8802b869c7a16d91647f305b b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4b8cbf97d57464de8802b869c7a16d91647f305b
new file mode 100644
index 00000000000..dabd50364f2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4b8cbf97d57464de8802b869c7a16d91647f305b
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4be79e98814c59631c5737400ad09d67907b2416 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4be79e98814c59631c5737400ad09d67907b2416
new file mode 100644
index 00000000000..ab5aee47972
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4be79e98814c59631c5737400ad09d67907b2416
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4dd140b8aa76604ca93c928056fee0b7061712d2 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4dd140b8aa76604ca93c928056fee0b7061712d2
new file mode 100644
index 00000000000..4945c79edef
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4dd140b8aa76604ca93c928056fee0b7061712d2
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4e511d2c95640f8646e9aed3527aea1b9bac35be b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4e511d2c95640f8646e9aed3527aea1b9bac35be
new file mode 100644
index 00000000000..79ab329bac6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4e511d2c95640f8646e9aed3527aea1b9bac35be
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4e75b118acd9bbe263e8c1a00ba1cb8f71054c0d b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4e75b118acd9bbe263e8c1a00ba1cb8f71054c0d
new file mode 100644
index 00000000000..2c1cd736d8f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4e75b118acd9bbe263e8c1a00ba1cb8f71054c0d
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4efdcd497072f85306dca983b56b030a61bfdb5d b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4efdcd497072f85306dca983b56b030a61bfdb5d
new file mode 100644
index 00000000000..a000a16073f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4efdcd497072f85306dca983b56b030a61bfdb5d
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4f3d0b3312bb9e4897859408ff1455a70d83c56b b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4f3d0b3312bb9e4897859408ff1455a70d83c56b
new file mode 100644
index 00000000000..bb505ce4309
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4f3d0b3312bb9e4897859408ff1455a70d83c56b
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4f6d30eb731ef1bb8a2faf1d61363c21366455b4 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4f6d30eb731ef1bb8a2faf1d61363c21366455b4
new file mode 100644
index 00000000000..cdcfa0b9b92
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4f6d30eb731ef1bb8a2faf1d61363c21366455b4
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4f97056b65a42fb1f93b2fe25d70bb58a5e36180 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4f97056b65a42fb1f93b2fe25d70bb58a5e36180
new file mode 100644
index 00000000000..c7d004f6e9f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4f97056b65a42fb1f93b2fe25d70bb58a5e36180
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4fb0166607616ed8c7ffb9061cc214949974575b b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4fb0166607616ed8c7ffb9061cc214949974575b
new file mode 100644
index 00000000000..3d7c074c14e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4fb0166607616ed8c7ffb9061cc214949974575b
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4fc2b1701d23d3ef1bd18dad25da22b9d6b2a24c b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4fc2b1701d23d3ef1bd18dad25da22b9d6b2a24c
new file mode 100644
index 00000000000..8d31a1058e2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/4fc2b1701d23d3ef1bd18dad25da22b9d6b2a24c
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/500645591c80e1bf4641774d972da02865cdc697 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/500645591c80e1bf4641774d972da02865cdc697
new file mode 100644
index 00000000000..0c758aff4ca
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/500645591c80e1bf4641774d972da02865cdc697
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/50781232df4526cfdae1952e84e912352d0b92a0 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/50781232df4526cfdae1952e84e912352d0b92a0
new file mode 100644
index 00000000000..2dc57370b48
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/50781232df4526cfdae1952e84e912352d0b92a0
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/50a32d6d96c3273717e1c8b8e4daf4e7f5ff72de b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/50a32d6d96c3273717e1c8b8e4daf4e7f5ff72de
new file mode 100644
index 00000000000..8068baeca22
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/50a32d6d96c3273717e1c8b8e4daf4e7f5ff72de
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/50b87134e7464b32a37740403190c8ef03116565 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/50b87134e7464b32a37740403190c8ef03116565
new file mode 100644
index 00000000000..5835f875c2b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/50b87134e7464b32a37740403190c8ef03116565
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5308c7cd09d4b244fbbc544bcffdf7501f23b87b b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5308c7cd09d4b244fbbc544bcffdf7501f23b87b
new file mode 100644
index 00000000000..747583f3a31
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5308c7cd09d4b244fbbc544bcffdf7501f23b87b
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/53438950d2b47be5ab6849cac445825732abffdb b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/53438950d2b47be5ab6849cac445825732abffdb
new file mode 100644
index 00000000000..41961d14f7c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/53438950d2b47be5ab6849cac445825732abffdb
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/546fcaca292b59045074e9d78560511341987acc b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/546fcaca292b59045074e9d78560511341987acc
new file mode 100644
index 00000000000..dfb48ae3acc
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/546fcaca292b59045074e9d78560511341987acc
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/549c9f3fffff6112030056a212563dab0ccfc3ea b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/549c9f3fffff6112030056a212563dab0ccfc3ea
new file mode 100644
index 00000000000..e19f2057b97
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/549c9f3fffff6112030056a212563dab0ccfc3ea
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/55228c704fd6a9b6468d470ed6877332f7d912d0 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/55228c704fd6a9b6468d470ed6877332f7d912d0
new file mode 100644
index 00000000000..e5832625037
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/55228c704fd6a9b6468d470ed6877332f7d912d0
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/569eae3c857f63840f56560014ff4b2adb5e0b07 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/569eae3c857f63840f56560014ff4b2adb5e0b07
new file mode 100644
index 00000000000..1168ae6f2e2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/569eae3c857f63840f56560014ff4b2adb5e0b07
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5723227540d0bc577b6d06b42942e939b9fd1f85 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5723227540d0bc577b6d06b42942e939b9fd1f85
new file mode 100644
index 00000000000..b57175a2f97
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5723227540d0bc577b6d06b42942e939b9fd1f85
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5742af4a414373bf4d465c268766765a388272c1 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5742af4a414373bf4d465c268766765a388272c1
new file mode 100644
index 00000000000..54a364efc0e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5742af4a414373bf4d465c268766765a388272c1
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/57dc2437d9c8773f1d728052253e849ff093c64b b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/57dc2437d9c8773f1d728052253e849ff093c64b
new file mode 100644
index 00000000000..83fa2481284
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/57dc2437d9c8773f1d728052253e849ff093c64b
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/58036d855a77e281076e0ab54e4e63500317a00a b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/58036d855a77e281076e0ab54e4e63500317a00a
new file mode 100644
index 00000000000..4deadd4a58a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/58036d855a77e281076e0ab54e4e63500317a00a
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/59dc4f0b83a23844023b09cc4fe0f277121c7be4 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/59dc4f0b83a23844023b09cc4fe0f277121c7be4
new file mode 100644
index 00000000000..10713f95d8c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/59dc4f0b83a23844023b09cc4fe0f277121c7be4
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5a8c02ca27412b03a77d81d55e8f612b3139f3d6 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5a8c02ca27412b03a77d81d55e8f612b3139f3d6
new file mode 100644
index 00000000000..2a863c900d1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5a8c02ca27412b03a77d81d55e8f612b3139f3d6
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5a9b1c687e2ffdd959dce79456b0c3808c49a021 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5a9b1c687e2ffdd959dce79456b0c3808c49a021
new file mode 100644
index 00000000000..97f5147c9ae
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5a9b1c687e2ffdd959dce79456b0c3808c49a021
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5ac4c39d8de57e953d752984c2a454eaa6ac3921 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5ac4c39d8de57e953d752984c2a454eaa6ac3921
new file mode 100644
index 00000000000..ab1fe3586f7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5ac4c39d8de57e953d752984c2a454eaa6ac3921
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5af236be1e7908f68252598cfaba2b668151d65b b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5af236be1e7908f68252598cfaba2b668151d65b
new file mode 100644
index 00000000000..8c2de42dd69
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5af236be1e7908f68252598cfaba2b668151d65b
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5b3a38217ea491444c80a17d739936b0eb7d6276 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5b3a38217ea491444c80a17d739936b0eb7d6276
new file mode 100644
index 00000000000..c15df18f36b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5b3a38217ea491444c80a17d739936b0eb7d6276
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5bbf36ae3e0d4375b257eaddac36a20aea30e289 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5bbf36ae3e0d4375b257eaddac36a20aea30e289
new file mode 100644
index 00000000000..f8cf92985a8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5bbf36ae3e0d4375b257eaddac36a20aea30e289
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5d12e34c71e5d001fa6ced3b5946164f0a4e8157 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5d12e34c71e5d001fa6ced3b5946164f0a4e8157
new file mode 100644
index 00000000000..836d9a6cd5a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5d12e34c71e5d001fa6ced3b5946164f0a4e8157
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5d2e30fd6f0b5e45ca6a53957d45666ded1f7e62 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5d2e30fd6f0b5e45ca6a53957d45666ded1f7e62
new file mode 100644
index 00000000000..38b3cc9a130
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5d2e30fd6f0b5e45ca6a53957d45666ded1f7e62
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5d32f59baad9f63b365f61d2fe7604a83e668782 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5d32f59baad9f63b365f61d2fe7604a83e668782
new file mode 100644
index 00000000000..3832375de6c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5d32f59baad9f63b365f61d2fe7604a83e668782
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5d5d543446198ca0c1ee0a90d9bd1dc1c906f1e7 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5d5d543446198ca0c1ee0a90d9bd1dc1c906f1e7
new file mode 100644
index 00000000000..be311d4e94f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5d5d543446198ca0c1ee0a90d9bd1dc1c906f1e7
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5db3b1cd26803a8f8f3570720b1c8bda531d7973 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5db3b1cd26803a8f8f3570720b1c8bda531d7973
new file mode 100644
index 00000000000..dbd940f29d4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5db3b1cd26803a8f8f3570720b1c8bda531d7973
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5dda51b9d05f7d99590bd108e0fcbd51cfea8b92 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5dda51b9d05f7d99590bd108e0fcbd51cfea8b92
new file mode 100644
index 00000000000..27634a2961d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5dda51b9d05f7d99590bd108e0fcbd51cfea8b92
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5e6e9f58761723d241d8a5d131b289f0b4931142 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5e6e9f58761723d241d8a5d131b289f0b4931142
new file mode 100644
index 00000000000..fe80b0bed2f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5e6e9f58761723d241d8a5d131b289f0b4931142
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5e8f156ea2e2f8b82a31c57d387c9d73e3048b53 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5e8f156ea2e2f8b82a31c57d387c9d73e3048b53
new file mode 100644
index 00000000000..e492bd57505
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5e8f156ea2e2f8b82a31c57d387c9d73e3048b53
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5ec6996fffe4c392ad8a7ee446e17466c31eba6e b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5ec6996fffe4c392ad8a7ee446e17466c31eba6e
new file mode 100644
index 00000000000..632fa22ceb3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5ec6996fffe4c392ad8a7ee446e17466c31eba6e
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5f10e932ff3d91c82027093fcd7ebf7963622c82 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5f10e932ff3d91c82027093fcd7ebf7963622c82
new file mode 100644
index 00000000000..868a62e7c73
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5f10e932ff3d91c82027093fcd7ebf7963622c82
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5f32e883581eda6501dc45377d2cb092246c807b b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5f32e883581eda6501dc45377d2cb092246c807b
new file mode 100644
index 00000000000..be00c5ddc6e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/5f32e883581eda6501dc45377d2cb092246c807b
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6021fff400694a589fbe487512fe0a75acbefa81 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6021fff400694a589fbe487512fe0a75acbefa81
new file mode 100644
index 00000000000..2ff73e6ab71
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6021fff400694a589fbe487512fe0a75acbefa81
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6116459314bee4a46c07a849105edd8dd31086de b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6116459314bee4a46c07a849105edd8dd31086de
new file mode 100644
index 00000000000..a9e52e76394
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6116459314bee4a46c07a849105edd8dd31086de
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/62275b63bcdf4ddcff849c7c0d5b74685f951781 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/62275b63bcdf4ddcff849c7c0d5b74685f951781
new file mode 100644
index 00000000000..f500b3cc893
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/62275b63bcdf4ddcff849c7c0d5b74685f951781
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/628e5bbbae6fed6ea8e56c0020648fe805a4b81d b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/628e5bbbae6fed6ea8e56c0020648fe805a4b81d
new file mode 100644
index 00000000000..c6c9fa8cc70
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/628e5bbbae6fed6ea8e56c0020648fe805a4b81d
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/646a8e6c1455092fbdfeeff8511739ff88c4c31c b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/646a8e6c1455092fbdfeeff8511739ff88c4c31c
new file mode 100644
index 00000000000..61b89c928d9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/646a8e6c1455092fbdfeeff8511739ff88c4c31c
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/64b68bf5b882b9bd0b37267287980ecfa0e44a85 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/64b68bf5b882b9bd0b37267287980ecfa0e44a85
new file mode 100644
index 00000000000..d5c5e9b0b7b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/64b68bf5b882b9bd0b37267287980ecfa0e44a85
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6585f4e2b5be491aea76a1174036796535c01570 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6585f4e2b5be491aea76a1174036796535c01570
new file mode 100644
index 00000000000..82e467cc943
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6585f4e2b5be491aea76a1174036796535c01570
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6638644549adf34f2549da316a538874827f2dc4 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6638644549adf34f2549da316a538874827f2dc4
new file mode 100644
index 00000000000..2fc3cb0446c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6638644549adf34f2549da316a538874827f2dc4
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/66e303967b5f0df9441edfebc843f974bca85616 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/66e303967b5f0df9441edfebc843f974bca85616
new file mode 100644
index 00000000000..b8492b7db78
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/66e303967b5f0df9441edfebc843f974bca85616
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/675fb106acc92ecfe6d5e13081ac49bcd0366992 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/675fb106acc92ecfe6d5e13081ac49bcd0366992
new file mode 100644
index 00000000000..efd34af1b41
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/675fb106acc92ecfe6d5e13081ac49bcd0366992
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/676c9720a8e137a235adbd9857109ec4a80a3975 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/676c9720a8e137a235adbd9857109ec4a80a3975
new file mode 100644
index 00000000000..ff926f243d6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/676c9720a8e137a235adbd9857109ec4a80a3975
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/67a24c8ca1c9501b488ed1ebd0a6dca5b2beb18f b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/67a24c8ca1c9501b488ed1ebd0a6dca5b2beb18f
new file mode 100644
index 00000000000..b91530c762e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/67a24c8ca1c9501b488ed1ebd0a6dca5b2beb18f
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/683dc48c0057cd1f0fb8dd57299d7919d238e087 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/683dc48c0057cd1f0fb8dd57299d7919d238e087
new file mode 100644
index 00000000000..aea1c54b6cf
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/683dc48c0057cd1f0fb8dd57299d7919d238e087
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/68c906d67e12fb6eedef7accf599793b63424e07 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/68c906d67e12fb6eedef7accf599793b63424e07
new file mode 100644
index 00000000000..4d28b452650
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/68c906d67e12fb6eedef7accf599793b63424e07
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6907c48d082962b116a35a157470c25b3d615042 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6907c48d082962b116a35a157470c25b3d615042
new file mode 100644
index 00000000000..4d99b3173d6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6907c48d082962b116a35a157470c25b3d615042
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6982d23746dd5380bdbdc870b04d913975ebda35 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6982d23746dd5380bdbdc870b04d913975ebda35
new file mode 100644
index 00000000000..74b7c3200c0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6982d23746dd5380bdbdc870b04d913975ebda35
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6a2f9f7369d31e0961e6c066265fa93e677c1e98 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6a2f9f7369d31e0961e6c066265fa93e677c1e98
new file mode 100644
index 00000000000..4fe499e7d95
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6a2f9f7369d31e0961e6c066265fa93e677c1e98
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6a6d36fc57672f18dc8687c67f92fc7916dfa8ab b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6a6d36fc57672f18dc8687c67f92fc7916dfa8ab
new file mode 100644
index 00000000000..bd10768e252
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6a6d36fc57672f18dc8687c67f92fc7916dfa8ab
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6a6fe8f178f97ec588885365b8921044b567bb65 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6a6fe8f178f97ec588885365b8921044b567bb65
new file mode 100644
index 00000000000..230211a772c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6a6fe8f178f97ec588885365b8921044b567bb65
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6b4d1620e335083dd08e08e55407509336d45ad8 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6b4d1620e335083dd08e08e55407509336d45ad8
new file mode 100644
index 00000000000..3be31e69012
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6b4d1620e335083dd08e08e55407509336d45ad8
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6bc10c9d19b66c285f2f34787ded5ebacb3fd5f1 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6bc10c9d19b66c285f2f34787ded5ebacb3fd5f1
new file mode 100644
index 00000000000..5e83dfaaa04
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6bc10c9d19b66c285f2f34787ded5ebacb3fd5f1
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6cac1f78df3a53057534598c63d258e441da9260 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6cac1f78df3a53057534598c63d258e441da9260
new file mode 100644
index 00000000000..c06a458eb9d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6cac1f78df3a53057534598c63d258e441da9260
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6cb3b692a507eab869acfec4f59553dac54ee13d b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6cb3b692a507eab869acfec4f59553dac54ee13d
new file mode 100644
index 00000000000..25dabd26749
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6cb3b692a507eab869acfec4f59553dac54ee13d
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6d485d07919ee9e2ce40dbfe85af6d91180b37de b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6d485d07919ee9e2ce40dbfe85af6d91180b37de
new file mode 100644
index 00000000000..839cf23ebc8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6d485d07919ee9e2ce40dbfe85af6d91180b37de
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6e9248b93031bf55ca151bf7c4997efe03391194 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6e9248b93031bf55ca151bf7c4997efe03391194
new file mode 100644
index 00000000000..85bebdd9e46
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6e9248b93031bf55ca151bf7c4997efe03391194
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6e9f26c319655235857db9f515b4d7ce3540aa72 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6e9f26c319655235857db9f515b4d7ce3540aa72
new file mode 100644
index 00000000000..2e5f12dc330
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6e9f26c319655235857db9f515b4d7ce3540aa72
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6f57614f7968d1ec1272bf99eee1755ec0c4ec28 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6f57614f7968d1ec1272bf99eee1755ec0c4ec28
new file mode 100644
index 00000000000..cedfa3442bc
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6f57614f7968d1ec1272bf99eee1755ec0c4ec28
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6fd2b983ababa634e057f1c36a83e246882939ff b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6fd2b983ababa634e057f1c36a83e246882939ff
new file mode 100644
index 00000000000..6adac049a04
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6fd2b983ababa634e057f1c36a83e246882939ff
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6ff63cce00d07decba06d52e8968057477421b7d b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6ff63cce00d07decba06d52e8968057477421b7d
new file mode 100644
index 00000000000..db196c5e47e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/6ff63cce00d07decba06d52e8968057477421b7d
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/70c0f0a855145d47e1f43c4e2dfbc43c7ae83cb7 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/70c0f0a855145d47e1f43c4e2dfbc43c7ae83cb7
new file mode 100644
index 00000000000..ab44e93b66e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/70c0f0a855145d47e1f43c4e2dfbc43c7ae83cb7
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/70d13ee99f89b4766262b612910eed696f97c63b b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/70d13ee99f89b4766262b612910eed696f97c63b
new file mode 100644
index 00000000000..b0504d5eb76
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/70d13ee99f89b4766262b612910eed696f97c63b
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/70fb57e41949e4f274f796571f918a1a4ed90bf9 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/70fb57e41949e4f274f796571f918a1a4ed90bf9
new file mode 100644
index 00000000000..acc0f58bfd7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/70fb57e41949e4f274f796571f918a1a4ed90bf9
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7115b5fac7a322124258809ee267e218dcede33b b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7115b5fac7a322124258809ee267e218dcede33b
new file mode 100644
index 00000000000..96113b75528
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7115b5fac7a322124258809ee267e218dcede33b
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/71db046590e7096f4adb4df3870bc7a83be32a06 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/71db046590e7096f4adb4df3870bc7a83be32a06
new file mode 100644
index 00000000000..819901372ee
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/71db046590e7096f4adb4df3870bc7a83be32a06
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/720b5f41cee126560e096c86e63fbad79f27539c b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/720b5f41cee126560e096c86e63fbad79f27539c
new file mode 100644
index 00000000000..01218a2dd24
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/720b5f41cee126560e096c86e63fbad79f27539c
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/72152e023c4acac71fcb17eb190a5006d9cc326d b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/72152e023c4acac71fcb17eb190a5006d9cc326d
new file mode 100644
index 00000000000..be2752c8b2e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/72152e023c4acac71fcb17eb190a5006d9cc326d
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/72428a53149aa6814271b34e8739ca94a5186467 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/72428a53149aa6814271b34e8739ca94a5186467
new file mode 100644
index 00000000000..1604b3d0af1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/72428a53149aa6814271b34e8739ca94a5186467
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/749d4d53263431538db0aad0955a00d27a6290d3 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/749d4d53263431538db0aad0955a00d27a6290d3
new file mode 100644
index 00000000000..3e6f0ddc1b9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/749d4d53263431538db0aad0955a00d27a6290d3
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7512e4b6487628aab55a3bfcac9d98a044b35d4d b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7512e4b6487628aab55a3bfcac9d98a044b35d4d
new file mode 100644
index 00000000000..dd9b877a7a0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7512e4b6487628aab55a3bfcac9d98a044b35d4d
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7521a524b21f3bca4914862fcaec7c93b17ee456 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7521a524b21f3bca4914862fcaec7c93b17ee456
new file mode 100644
index 00000000000..9a928a4d002
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7521a524b21f3bca4914862fcaec7c93b17ee456
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/754326af79272baf12eed6135e279f05760b090d b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/754326af79272baf12eed6135e279f05760b090d
new file mode 100644
index 00000000000..2674be369ae
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/754326af79272baf12eed6135e279f05760b090d
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7544ab036f63e3d8d6fac775a1726b2b2b98880d b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7544ab036f63e3d8d6fac775a1726b2b2b98880d
new file mode 100644
index 00000000000..49ea1ba6a1c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7544ab036f63e3d8d6fac775a1726b2b2b98880d
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7567cd6037ee58f9c0e185b3ab9431b7c105621d b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7567cd6037ee58f9c0e185b3ab9431b7c105621d
new file mode 100644
index 00000000000..2b1e73ab561
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7567cd6037ee58f9c0e185b3ab9431b7c105621d
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/756a757748d1dd648d369ce8cf9a2a8236305280 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/756a757748d1dd648d369ce8cf9a2a8236305280
new file mode 100644
index 00000000000..1ea9936a745
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/756a757748d1dd648d369ce8cf9a2a8236305280
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7649cb328ba87f23e0f5a920223376b2342f9d91 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7649cb328ba87f23e0f5a920223376b2342f9d91
new file mode 100644
index 00000000000..53cf8ce817a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7649cb328ba87f23e0f5a920223376b2342f9d91
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/76aef317f1628a04c82e21bf6fe33d2d562e753a b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/76aef317f1628a04c82e21bf6fe33d2d562e753a
new file mode 100644
index 00000000000..929b8271cc5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/76aef317f1628a04c82e21bf6fe33d2d562e753a
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7777e7e27a1dd2804cf12bab78987a69286ab44e b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7777e7e27a1dd2804cf12bab78987a69286ab44e
new file mode 100644
index 00000000000..4de9401424f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7777e7e27a1dd2804cf12bab78987a69286ab44e
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/782b07efd1d43b640dc84626ec375c6593cb0e4b b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/782b07efd1d43b640dc84626ec375c6593cb0e4b
new file mode 100644
index 00000000000..9d2545628ae
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/782b07efd1d43b640dc84626ec375c6593cb0e4b
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/78a974488d0ef6bf8f585e03d1306edeba5ad2b4 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/78a974488d0ef6bf8f585e03d1306edeba5ad2b4
new file mode 100644
index 00000000000..87fc7fa0101
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/78a974488d0ef6bf8f585e03d1306edeba5ad2b4
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/78f4b1ff07a84ba09491b0d159eb65b8f17e97c9 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/78f4b1ff07a84ba09491b0d159eb65b8f17e97c9
new file mode 100644
index 00000000000..907bdf5440c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/78f4b1ff07a84ba09491b0d159eb65b8f17e97c9
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/797006985d47c774d618d5b5f9552348977fd70f b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/797006985d47c774d618d5b5f9552348977fd70f
new file mode 100644
index 00000000000..a691b386aba
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/797006985d47c774d618d5b5f9552348977fd70f
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/79e206d6a750918f6e73475d0961ee52d2899928 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/79e206d6a750918f6e73475d0961ee52d2899928
new file mode 100644
index 00000000000..fe1b8f830de
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/79e206d6a750918f6e73475d0961ee52d2899928
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7a6d3a6255de7ef65924ef6cf2beca37e07f2ab5 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7a6d3a6255de7ef65924ef6cf2beca37e07f2ab5
new file mode 100644
index 00000000000..e2172220db4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7a6d3a6255de7ef65924ef6cf2beca37e07f2ab5
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7ab9d193d63c4b48353e8eb18fb515d35acfeaa9 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7ab9d193d63c4b48353e8eb18fb515d35acfeaa9
new file mode 100644
index 00000000000..f3505aa5be1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7ab9d193d63c4b48353e8eb18fb515d35acfeaa9
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7af0ea65622381e22bfa8d5992bf39396f142033 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7af0ea65622381e22bfa8d5992bf39396f142033
new file mode 100644
index 00000000000..51533723d82
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7af0ea65622381e22bfa8d5992bf39396f142033
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7b8f7e6cca594c099cd64168f7d231a52d4a78ff b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7b8f7e6cca594c099cd64168f7d231a52d4a78ff
new file mode 100644
index 00000000000..d13c06b4605
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7b8f7e6cca594c099cd64168f7d231a52d4a78ff
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7bc82f8d0c482f7e90b3c29e7bee82dfdc177d89 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7bc82f8d0c482f7e90b3c29e7bee82dfdc177d89
new file mode 100644
index 00000000000..c9098c6fe83
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7bc82f8d0c482f7e90b3c29e7bee82dfdc177d89
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7cb507bbc72d774252939608d5dded947bd15e5d b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7cb507bbc72d774252939608d5dded947bd15e5d
new file mode 100644
index 00000000000..96b203e1a02
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7cb507bbc72d774252939608d5dded947bd15e5d
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7d39cf0e7187394e31a37fbc45edc3825ad3df67 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7d39cf0e7187394e31a37fbc45edc3825ad3df67
new file mode 100644
index 00000000000..bdddd7c03ee
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7d39cf0e7187394e31a37fbc45edc3825ad3df67
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7e80c5307b5b65c62ce8f97509a3588eaf0ad162 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7e80c5307b5b65c62ce8f97509a3588eaf0ad162
new file mode 100644
index 00000000000..7bf96757bca
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7e80c5307b5b65c62ce8f97509a3588eaf0ad162
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7efd9ce5b0523f09f7b4f37a838f07739038aba9 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7efd9ce5b0523f09f7b4f37a838f07739038aba9
new file mode 100644
index 00000000000..fa10e59da06
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7efd9ce5b0523f09f7b4f37a838f07739038aba9
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7f2f798b8893e00cfba14ebac6d233360ebfc101 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7f2f798b8893e00cfba14ebac6d233360ebfc101
new file mode 100644
index 00000000000..1e41662f626
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7f2f798b8893e00cfba14ebac6d233360ebfc101
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7f92472679d1050f3e766d2d34dc482d7e22e654 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7f92472679d1050f3e766d2d34dc482d7e22e654
new file mode 100644
index 00000000000..97c1cc85551
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/7f92472679d1050f3e766d2d34dc482d7e22e654
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/80ec0565eb3e7900903c134584d494b618d3abf5 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/80ec0565eb3e7900903c134584d494b618d3abf5
new file mode 100644
index 00000000000..44a576ce98b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/80ec0565eb3e7900903c134584d494b618d3abf5
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/844121ae58d6bb36f81e92c42723528a7123b5c0 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/844121ae58d6bb36f81e92c42723528a7123b5c0
new file mode 100644
index 00000000000..90d343c6a31
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/844121ae58d6bb36f81e92c42723528a7123b5c0
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/847c3d55a452bf83a6eb209b0411da3477491300 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/847c3d55a452bf83a6eb209b0411da3477491300
new file mode 100644
index 00000000000..ad270771593
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/847c3d55a452bf83a6eb209b0411da3477491300
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/84ff3957ecc05bc806b183ee8d2310b09f8fc5da b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/84ff3957ecc05bc806b183ee8d2310b09f8fc5da
new file mode 100644
index 00000000000..fba7f18ddbc
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/84ff3957ecc05bc806b183ee8d2310b09f8fc5da
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/856f3b79f3a56514f0fbbb7c63f361bb6cc07be8 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/856f3b79f3a56514f0fbbb7c63f361bb6cc07be8
new file mode 100644
index 00000000000..ec07cfdf144
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/856f3b79f3a56514f0fbbb7c63f361bb6cc07be8
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/859f2ac933251900bc8ac45ae8b6ae64c25f54e7 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/859f2ac933251900bc8ac45ae8b6ae64c25f54e7
new file mode 100644
index 00000000000..44fd96014b6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/859f2ac933251900bc8ac45ae8b6ae64c25f54e7
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/85e53271e14006f0265921d02d4d736cdc580b0b b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/85e53271e14006f0265921d02d4d736cdc580b0b
new file mode 100644
index 00000000000..fc3ff073cda
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/85e53271e14006f0265921d02d4d736cdc580b0b
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/86592b6d5006db6b8515fcf98800e0154e0e1894 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/86592b6d5006db6b8515fcf98800e0154e0e1894
new file mode 100644
index 00000000000..88242989735
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/86592b6d5006db6b8515fcf98800e0154e0e1894
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/868ff4054649d579a1d0efa5356a4363848b9fdb b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/868ff4054649d579a1d0efa5356a4363848b9fdb
new file mode 100644
index 00000000000..fe997581b58
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/868ff4054649d579a1d0efa5356a4363848b9fdb
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/87040a63d8cb013d0acf4dae68805d2e6b5cf251 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/87040a63d8cb013d0acf4dae68805d2e6b5cf251
new file mode 100644
index 00000000000..52457c798b5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/87040a63d8cb013d0acf4dae68805d2e6b5cf251
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8727e0346f1575344192b5457954528744289af4 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8727e0346f1575344192b5457954528744289af4
new file mode 100644
index 00000000000..1bc27fad72a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8727e0346f1575344192b5457954528744289af4
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/885f266cf4582b510d66e9de969beccd6b4d450f b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/885f266cf4582b510d66e9de969beccd6b4d450f
new file mode 100644
index 00000000000..6c3d4cfa23d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/885f266cf4582b510d66e9de969beccd6b4d450f
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8887b2f899212d3292a2e67015a9622938d79a8b b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8887b2f899212d3292a2e67015a9622938d79a8b
new file mode 100644
index 00000000000..e0f7ffbb1e8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8887b2f899212d3292a2e67015a9622938d79a8b
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/892c6b54361baf9960565fcf047da435a7c6d27e b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/892c6b54361baf9960565fcf047da435a7c6d27e
new file mode 100644
index 00000000000..02374bdc649
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/892c6b54361baf9960565fcf047da435a7c6d27e
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/894d0bc2371a815befbafda9971c31bfc65e27b6 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/894d0bc2371a815befbafda9971c31bfc65e27b6
new file mode 100644
index 00000000000..ac67c64e210
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/894d0bc2371a815befbafda9971c31bfc65e27b6
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8963c57885ebbd41fe8a12b7459efe6d5295fab9 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8963c57885ebbd41fe8a12b7459efe6d5295fab9
new file mode 100644
index 00000000000..66ff7f6ad7a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8963c57885ebbd41fe8a12b7459efe6d5295fab9
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/897c5f0c74ca1f0f63befc2b09e67acccf302acf b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/897c5f0c74ca1f0f63befc2b09e67acccf302acf
new file mode 100644
index 00000000000..502002135a9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/897c5f0c74ca1f0f63befc2b09e67acccf302acf
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8a39167b995cd88282a5c9f9ac54441325e80403 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8a39167b995cd88282a5c9f9ac54441325e80403
new file mode 100644
index 00000000000..08536e84e64
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8a39167b995cd88282a5c9f9ac54441325e80403
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8a78efcd5256d9523588dc0b6baf5deede51f309 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8a78efcd5256d9523588dc0b6baf5deede51f309
new file mode 100644
index 00000000000..d269906f9ce
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8a78efcd5256d9523588dc0b6baf5deede51f309
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8accdac41a14523199975b8919241f3c2bbc7e72 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8accdac41a14523199975b8919241f3c2bbc7e72
new file mode 100644
index 00000000000..2a635fae440
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8accdac41a14523199975b8919241f3c2bbc7e72
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8b0d9bf66f3fb977a9c7c57c3cb7ca4667391d0a b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8b0d9bf66f3fb977a9c7c57c3cb7ca4667391d0a
new file mode 100644
index 00000000000..118649b1ff8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8b0d9bf66f3fb977a9c7c57c3cb7ca4667391d0a
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8b455cb428e79e300de4ffdf63d523a1cc8c5f6e b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8b455cb428e79e300de4ffdf63d523a1cc8c5f6e
new file mode 100644
index 00000000000..3100c1c41c7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8b455cb428e79e300de4ffdf63d523a1cc8c5f6e
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8b641c5fd96962ec14679365774e141e5f417af3 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8b641c5fd96962ec14679365774e141e5f417af3
new file mode 100644
index 00000000000..bd3bdd1c6c2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8b641c5fd96962ec14679365774e141e5f417af3
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8cba0484abbb6537f3b95f2a9553b454fc67b175 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8cba0484abbb6537f3b95f2a9553b454fc67b175
new file mode 100644
index 00000000000..b4c85a78e0b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8cba0484abbb6537f3b95f2a9553b454fc67b175
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8cbd2c17b14d60e47de147325d2c2feae299c19e b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8cbd2c17b14d60e47de147325d2c2feae299c19e
new file mode 100644
index 00000000000..ff7ecbe5737
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8cbd2c17b14d60e47de147325d2c2feae299c19e
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8ce2e0a7e0b40b3aefbc5d78ae51d7a137bc7351 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8ce2e0a7e0b40b3aefbc5d78ae51d7a137bc7351
new file mode 100644
index 00000000000..02ca80dd37a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8ce2e0a7e0b40b3aefbc5d78ae51d7a137bc7351
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8d352b0bfe03414b30083dc56aace56eef975bba b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8d352b0bfe03414b30083dc56aace56eef975bba
new file mode 100644
index 00000000000..fb4c16cea00
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8d352b0bfe03414b30083dc56aace56eef975bba
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8ec10c7b8b7bad61459153975bece3912b7ba55b b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8ec10c7b8b7bad61459153975bece3912b7ba55b
new file mode 100644
index 00000000000..caa705460dd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8ec10c7b8b7bad61459153975bece3912b7ba55b
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8ef73a357235fae6f3ce4738b2af4e9918f4861f b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8ef73a357235fae6f3ce4738b2af4e9918f4861f
new file mode 100644
index 00000000000..c689d123efc
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8ef73a357235fae6f3ce4738b2af4e9918f4861f
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8f3b0d41963bd53158153fea42d73a6521289d7c b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8f3b0d41963bd53158153fea42d73a6521289d7c
new file mode 100644
index 00000000000..15d9bfcbb3d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/8f3b0d41963bd53158153fea42d73a6521289d7c
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/901a58adfcedf69d1eb9ede77bf365944564b653 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/901a58adfcedf69d1eb9ede77bf365944564b653
new file mode 100644
index 00000000000..23718f70fe2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/901a58adfcedf69d1eb9ede77bf365944564b653
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/92258e1bb3be4b1c16cc3ea9f83c0c1ad69a81ff b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/92258e1bb3be4b1c16cc3ea9f83c0c1ad69a81ff
new file mode 100644
index 00000000000..83f5ae20a61
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/92258e1bb3be4b1c16cc3ea9f83c0c1ad69a81ff
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/9364fa0234f0aa53d7e20fc960c9b1ed7b0a6c57 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/9364fa0234f0aa53d7e20fc960c9b1ed7b0a6c57
new file mode 100644
index 00000000000..e8758616c0d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/9364fa0234f0aa53d7e20fc960c9b1ed7b0a6c57
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/93683cc766ed6e4582c992e97f9a883be22772a9 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/93683cc766ed6e4582c992e97f9a883be22772a9
new file mode 100644
index 00000000000..48654a9fb43
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/93683cc766ed6e4582c992e97f9a883be22772a9
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/93865ee370b7c5b46eb9690fa88f70d288817a63 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/93865ee370b7c5b46eb9690fa88f70d288817a63
new file mode 100644
index 00000000000..2318e2cabd4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/93865ee370b7c5b46eb9690fa88f70d288817a63
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/93e914167275387141c1cafcff5c799b0310a81f b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/93e914167275387141c1cafcff5c799b0310a81f
new file mode 100644
index 00000000000..c7dbfabff70
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/93e914167275387141c1cafcff5c799b0310a81f
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/941cb1a3f06cae3ded8242d250eb7a4cdd0c00f0 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/941cb1a3f06cae3ded8242d250eb7a4cdd0c00f0
new file mode 100644
index 00000000000..c02d2a6582f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/941cb1a3f06cae3ded8242d250eb7a4cdd0c00f0
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/943d3677ac55bcc70ef846300b077cd3521f6c63 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/943d3677ac55bcc70ef846300b077cd3521f6c63
new file mode 100644
index 00000000000..9ac259a53e2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/943d3677ac55bcc70ef846300b077cd3521f6c63
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/943e337039d71a32c0d2800434b11a8f3ab0a3ff b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/943e337039d71a32c0d2800434b11a8f3ab0a3ff
new file mode 100644
index 00000000000..dbb6ebaebef
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/943e337039d71a32c0d2800434b11a8f3ab0a3ff
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/947507c0cf74bd7525de63a30de65067d5c753c0 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/947507c0cf74bd7525de63a30de65067d5c753c0
new file mode 100644
index 00000000000..ede1bc51234
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/947507c0cf74bd7525de63a30de65067d5c753c0
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/94abddc43e4382ddd011e570a40d4d6a8c6a83ff b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/94abddc43e4382ddd011e570a40d4d6a8c6a83ff
new file mode 100644
index 00000000000..35f9d08304c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/94abddc43e4382ddd011e570a40d4d6a8c6a83ff
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/94bf062093c396d7a7915dc0e826e13233522815 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/94bf062093c396d7a7915dc0e826e13233522815
new file mode 100644
index 00000000000..96eb226e496
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/94bf062093c396d7a7915dc0e826e13233522815
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/95358d5226cce134ef7383a07b3088fefa510fa7 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/95358d5226cce134ef7383a07b3088fefa510fa7
new file mode 100644
index 00000000000..e7021ab976a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/95358d5226cce134ef7383a07b3088fefa510fa7
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/95750c0f6ae3297204e59b120601c5a349dde5d1 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/95750c0f6ae3297204e59b120601c5a349dde5d1
new file mode 100644
index 00000000000..096196ac6f0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/95750c0f6ae3297204e59b120601c5a349dde5d1
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/959432e49a04a4d29f98efbaee5994c8f19bae2a b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/959432e49a04a4d29f98efbaee5994c8f19bae2a
new file mode 100644
index 00000000000..8e1921f0b4c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/959432e49a04a4d29f98efbaee5994c8f19bae2a
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/960a49d0fc52b2cae053dce5d78d47d0a8273b84 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/960a49d0fc52b2cae053dce5d78d47d0a8273b84
new file mode 100644
index 00000000000..f1a4cf05549
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/960a49d0fc52b2cae053dce5d78d47d0a8273b84
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/96706073f398f6811f43000954ec6cf2a6ac6e2c b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/96706073f398f6811f43000954ec6cf2a6ac6e2c
new file mode 100644
index 00000000000..ca9365bbc32
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/96706073f398f6811f43000954ec6cf2a6ac6e2c
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/96847b259da89a453d233ade419eb7db38484193 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/96847b259da89a453d233ade419eb7db38484193
new file mode 100644
index 00000000000..8f5517ab263
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/96847b259da89a453d233ade419eb7db38484193
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/991e91d666edbf577e009fdf1705df66b34c0e2b b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/991e91d666edbf577e009fdf1705df66b34c0e2b
new file mode 100644
index 00000000000..deee93316dd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/991e91d666edbf577e009fdf1705df66b34c0e2b
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/993836301f85ee87b8049c3d277be35699da6b53 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/993836301f85ee87b8049c3d277be35699da6b53
new file mode 100644
index 00000000000..6a7bf944f2d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/993836301f85ee87b8049c3d277be35699da6b53
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/99a15414b858c8899c547b872c14d885ccfd7732 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/99a15414b858c8899c547b872c14d885ccfd7732
new file mode 100644
index 00000000000..26a41ba76dc
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/99a15414b858c8899c547b872c14d885ccfd7732
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/99f8d3a4b9e03c55d49051c37facc09060a27a17 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/99f8d3a4b9e03c55d49051c37facc09060a27a17
new file mode 100644
index 00000000000..0597dedf257
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/99f8d3a4b9e03c55d49051c37facc09060a27a17
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/9afde5ff243e9e0b83bd41fe9e14a2b51700f3b0 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/9afde5ff243e9e0b83bd41fe9e14a2b51700f3b0
new file mode 100644
index 00000000000..f0dc6896393
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/9afde5ff243e9e0b83bd41fe9e14a2b51700f3b0
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/9b0f3060c0c827affd4d9d09ae7c5ef8d9a02bdb b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/9b0f3060c0c827affd4d9d09ae7c5ef8d9a02bdb
new file mode 100644
index 00000000000..f0dffb6ec3a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/9b0f3060c0c827affd4d9d09ae7c5ef8d9a02bdb
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/9b3cc9238c57e1fbf96b881d3fc7a50b5fe1adc8 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/9b3cc9238c57e1fbf96b881d3fc7a50b5fe1adc8
new file mode 100644
index 00000000000..d131c6d373d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/9b3cc9238c57e1fbf96b881d3fc7a50b5fe1adc8
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/9be325272f875f0ac5da945ae13eddf54f478d2b b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/9be325272f875f0ac5da945ae13eddf54f478d2b
new file mode 100644
index 00000000000..5ee8d52d7fa
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/9be325272f875f0ac5da945ae13eddf54f478d2b
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/9bec724abc6ad8aa4736674b0400f24e0964acb8 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/9bec724abc6ad8aa4736674b0400f24e0964acb8
new file mode 100644
index 00000000000..3fafc752999
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/9bec724abc6ad8aa4736674b0400f24e0964acb8
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/9eeaded113dc382607cd40008ff95ffc3931698e b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/9eeaded113dc382607cd40008ff95ffc3931698e
new file mode 100644
index 00000000000..ea59bbaff24
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/9eeaded113dc382607cd40008ff95ffc3931698e
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/9f044ac98aafab31444e6b40701aa1aa24c4f16b b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/9f044ac98aafab31444e6b40701aa1aa24c4f16b
new file mode 100644
index 00000000000..e9214750974
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/9f044ac98aafab31444e6b40701aa1aa24c4f16b
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a01dadac65200d320c4237dcfd2b5a918f0e75b3 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a01dadac65200d320c4237dcfd2b5a918f0e75b3
new file mode 100644
index 00000000000..c825b6e8010
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a01dadac65200d320c4237dcfd2b5a918f0e75b3
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a1a7715c7596c77b892dc6d4debb7c108ca4ef97 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a1a7715c7596c77b892dc6d4debb7c108ca4ef97
new file mode 100644
index 00000000000..99f3402938b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a1a7715c7596c77b892dc6d4debb7c108ca4ef97
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a1fb0023740e215a79615b048ae2cd44c5c5a21c b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a1fb0023740e215a79615b048ae2cd44c5c5a21c
new file mode 100644
index 00000000000..6f9c0b11648
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a1fb0023740e215a79615b048ae2cd44c5c5a21c
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a2ac47e1458897c2106586cd82ba4cf955250db9 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a2ac47e1458897c2106586cd82ba4cf955250db9
new file mode 100644
index 00000000000..ed70da8a588
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a2ac47e1458897c2106586cd82ba4cf955250db9
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a331fcf28f87a61b052f8a1625166e4aed890951 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a331fcf28f87a61b052f8a1625166e4aed890951
new file mode 100644
index 00000000000..18a8b4911ee
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a331fcf28f87a61b052f8a1625166e4aed890951
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a33286264113976a15d42b41d7365c9f9c7fdb7f b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a33286264113976a15d42b41d7365c9f9c7fdb7f
new file mode 100644
index 00000000000..f373e6aac83
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a33286264113976a15d42b41d7365c9f9c7fdb7f
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a3dfc0c77acade0ee48dcc73e795a597d0270a73 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a3dfc0c77acade0ee48dcc73e795a597d0270a73
new file mode 100644
index 00000000000..aa3131d3983
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a3dfc0c77acade0ee48dcc73e795a597d0270a73
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a425a34727d6676a695037d5466f691b551778f6 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a425a34727d6676a695037d5466f691b551778f6
new file mode 100644
index 00000000000..5fa99a688ad
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a425a34727d6676a695037d5466f691b551778f6
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a5a1aad89237ae6074cecd5359b7632c57153a7d b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a5a1aad89237ae6074cecd5359b7632c57153a7d
new file mode 100644
index 00000000000..077e020c557
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a5a1aad89237ae6074cecd5359b7632c57153a7d
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a74d9214eba82e2d2e619f60087628a690970811 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a74d9214eba82e2d2e619f60087628a690970811
new file mode 100644
index 00000000000..d203e0e6964
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a74d9214eba82e2d2e619f60087628a690970811
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a79e5e7e7e73e60bfe334f7700723467399f8292 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a79e5e7e7e73e60bfe334f7700723467399f8292
new file mode 100644
index 00000000000..7a087ad021a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a79e5e7e7e73e60bfe334f7700723467399f8292
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a869cc1ba8c28159882e53171a480f3ac97138ea b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a869cc1ba8c28159882e53171a480f3ac97138ea
new file mode 100644
index 00000000000..fa2fc827dee
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a869cc1ba8c28159882e53171a480f3ac97138ea
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a8954512ba38ba4fddc38c8918ab99bce9f35c02 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a8954512ba38ba4fddc38c8918ab99bce9f35c02
new file mode 100644
index 00000000000..9369fa43a1c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a8954512ba38ba4fddc38c8918ab99bce9f35c02
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a922e9e2cc2edb309b2a8ddc0cf7b062657b72e5 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a922e9e2cc2edb309b2a8ddc0cf7b062657b72e5
new file mode 100644
index 00000000000..a78fa06638f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a922e9e2cc2edb309b2a8ddc0cf7b062657b72e5
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a9b6b42e3c65774dcfb7af278e1d10f8a6f24b71 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a9b6b42e3c65774dcfb7af278e1d10f8a6f24b71
new file mode 100644
index 00000000000..75d8d03f30f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/a9b6b42e3c65774dcfb7af278e1d10f8a6f24b71
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ab131dde88bf9765ecdb4f21c6e0ba6c7254d809 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ab131dde88bf9765ecdb4f21c6e0ba6c7254d809
new file mode 100644
index 00000000000..6230f0170b3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ab131dde88bf9765ecdb4f21c6e0ba6c7254d809
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ab2902184de2ffc88cabfdbc9fa6171d6b7a6f2f b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ab2902184de2ffc88cabfdbc9fa6171d6b7a6f2f
new file mode 100644
index 00000000000..32c9cb2ab39
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ab2902184de2ffc88cabfdbc9fa6171d6b7a6f2f
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ab76a7aaa7285c49488be4d8e9f4a6a86e4ab51b b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ab76a7aaa7285c49488be4d8e9f4a6a86e4ab51b
new file mode 100644
index 00000000000..79f14aed0b1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ab76a7aaa7285c49488be4d8e9f4a6a86e4ab51b
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/abc389bb60ba2d57b1366067aac6fe90e0ce246f b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/abc389bb60ba2d57b1366067aac6fe90e0ce246f
new file mode 100644
index 00000000000..daa0dc46b0a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/abc389bb60ba2d57b1366067aac6fe90e0ce246f
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ac6e5a17a260ee1601f58e997e9943888a07bf07 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ac6e5a17a260ee1601f58e997e9943888a07bf07
new file mode 100644
index 00000000000..f58cbfe683f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ac6e5a17a260ee1601f58e997e9943888a07bf07
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/acbaf77d495a9cdc3a6602ccdfb98ac7cce3d1e7 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/acbaf77d495a9cdc3a6602ccdfb98ac7cce3d1e7
new file mode 100644
index 00000000000..3becaa61a02
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/acbaf77d495a9cdc3a6602ccdfb98ac7cce3d1e7
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ad3048344db255a3ce132b52c0bed0519d746779 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ad3048344db255a3ce132b52c0bed0519d746779
new file mode 100644
index 00000000000..677d9527f02
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ad3048344db255a3ce132b52c0bed0519d746779
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ad323408848448bccf938cc89d39b019766052b8 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ad323408848448bccf938cc89d39b019766052b8
new file mode 100644
index 00000000000..69574038708
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ad323408848448bccf938cc89d39b019766052b8
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ae0e0a09eefd341e3c578df802dc004f83c61e9d b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ae0e0a09eefd341e3c578df802dc004f83c61e9d
new file mode 100644
index 00000000000..0c9c94a44de
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ae0e0a09eefd341e3c578df802dc004f83c61e9d
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/af26968b71caf11c882b07cc25306d748e28c4f6 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/af26968b71caf11c882b07cc25306d748e28c4f6
new file mode 100644
index 00000000000..87b77c4b208
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/af26968b71caf11c882b07cc25306d748e28c4f6
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b1b3ab17cfd331677d32b65e52fb3ebc3fdae431 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b1b3ab17cfd331677d32b65e52fb3ebc3fdae431
new file mode 100644
index 00000000000..bae8dc81805
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b1b3ab17cfd331677d32b65e52fb3ebc3fdae431
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b1d3810c7343c79212acdaada973ee18b7db156f b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b1d3810c7343c79212acdaada973ee18b7db156f
new file mode 100644
index 00000000000..8a4461aacc8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b1d3810c7343c79212acdaada973ee18b7db156f
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b20bcec80dcb1d4ac6d26070a6032e7e21c1b8ed b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b20bcec80dcb1d4ac6d26070a6032e7e21c1b8ed
new file mode 100644
index 00000000000..aa439976a5c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b20bcec80dcb1d4ac6d26070a6032e7e21c1b8ed
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b2c4d853da500f3766902c2d0f4bc5b11605a7c1 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b2c4d853da500f3766902c2d0f4bc5b11605a7c1
new file mode 100644
index 00000000000..ed256e23ea9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b2c4d853da500f3766902c2d0f4bc5b11605a7c1
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b2fe70050194511642fb0ce458f7eabf99c827fe b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b2fe70050194511642fb0ce458f7eabf99c827fe
new file mode 100644
index 00000000000..d3d07c29249
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b2fe70050194511642fb0ce458f7eabf99c827fe
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b37e56fe2d9ef05a0383a9ea4eb4858711528321 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b37e56fe2d9ef05a0383a9ea4eb4858711528321
new file mode 100644
index 00000000000..b74ae2375b2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b37e56fe2d9ef05a0383a9ea4eb4858711528321
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b5245e2f6770333319794ee0148866173b2f4941 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b5245e2f6770333319794ee0148866173b2f4941
new file mode 100644
index 00000000000..7ecab8f5848
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b5245e2f6770333319794ee0148866173b2f4941
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b5af0f0f8e809e2de1a516ce42d5930f5b913326 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b5af0f0f8e809e2de1a516ce42d5930f5b913326
new file mode 100644
index 00000000000..ecee1b8bd88
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b5af0f0f8e809e2de1a516ce42d5930f5b913326
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b69aae002ffa00a7db0fdfe2d2ab8bd8ddb192b1 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b69aae002ffa00a7db0fdfe2d2ab8bd8ddb192b1
new file mode 100644
index 00000000000..0ee0659f666
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b69aae002ffa00a7db0fdfe2d2ab8bd8ddb192b1
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b8bc0fbaf88034370ef49d7cf791f32783221d00 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b8bc0fbaf88034370ef49d7cf791f32783221d00
new file mode 100644
index 00000000000..c7392a0974b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b8bc0fbaf88034370ef49d7cf791f32783221d00
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b8d8a865f23bcaa4c6428712bec185e0bdc421d2 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b8d8a865f23bcaa4c6428712bec185e0bdc421d2
new file mode 100644
index 00000000000..acab37e13fe
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b8d8a865f23bcaa4c6428712bec185e0bdc421d2
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b98689a4ba024cfb07535194d9b9f23d95ef70b3 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b98689a4ba024cfb07535194d9b9f23d95ef70b3
new file mode 100644
index 00000000000..e2809835f77
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/b98689a4ba024cfb07535194d9b9f23d95ef70b3
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ba714f908ae61e60c111d21a75a6126b02afe85a b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ba714f908ae61e60c111d21a75a6126b02afe85a
new file mode 100644
index 00000000000..7bf22e5c45e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ba714f908ae61e60c111d21a75a6126b02afe85a
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/bb6d10fd85abd60bb59bde89d770f99d3ed25454 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/bb6d10fd85abd60bb59bde89d770f99d3ed25454
new file mode 100644
index 00000000000..ae0c13d4570
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/bb6d10fd85abd60bb59bde89d770f99d3ed25454
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/bbfbcb079db8785b0ff8a27814a4298d97a24a0c b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/bbfbcb079db8785b0ff8a27814a4298d97a24a0c
new file mode 100644
index 00000000000..88e3b4283a7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/bbfbcb079db8785b0ff8a27814a4298d97a24a0c
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/bd38ac80bdfada6fdbd70689d28052206b5da757 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/bd38ac80bdfada6fdbd70689d28052206b5da757
new file mode 100644
index 00000000000..30f4be854e8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/bd38ac80bdfada6fdbd70689d28052206b5da757
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/be00ab687a5c33e75468f68662b87b13e2393fe5 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/be00ab687a5c33e75468f68662b87b13e2393fe5
new file mode 100644
index 00000000000..2b8e8a4659f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/be00ab687a5c33e75468f68662b87b13e2393fe5
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/be3950f02753b8e0cdf26e70367c1eb0fb41e20a b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/be3950f02753b8e0cdf26e70367c1eb0fb41e20a
new file mode 100644
index 00000000000..f57c844c06c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/be3950f02753b8e0cdf26e70367c1eb0fb41e20a
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/be4ae7a571dd64ddbfdedec01560f4795e8d83cc b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/be4ae7a571dd64ddbfdedec01560f4795e8d83cc
new file mode 100644
index 00000000000..a5ce75be3bf
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/be4ae7a571dd64ddbfdedec01560f4795e8d83cc
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/be71e72303e85788184ead184fac1dd90c84030e b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/be71e72303e85788184ead184fac1dd90c84030e
new file mode 100644
index 00000000000..d1831470747
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/be71e72303e85788184ead184fac1dd90c84030e
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/bf44d29be7a2eadad300f06b57f65a1af8368426 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/bf44d29be7a2eadad300f06b57f65a1af8368426
new file mode 100644
index 00000000000..72c4076f712
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/bf44d29be7a2eadad300f06b57f65a1af8368426
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c0887dd7e04bd38e49e30842cbea040b6d725302 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c0887dd7e04bd38e49e30842cbea040b6d725302
new file mode 100644
index 00000000000..053f66ec2e0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c0887dd7e04bd38e49e30842cbea040b6d725302
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c15d4f25799729e0df173d00b9344a41866872c6 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c15d4f25799729e0df173d00b9344a41866872c6
new file mode 100644
index 00000000000..bb7c2ef7a7f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c15d4f25799729e0df173d00b9344a41866872c6
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c20e3f03556e0c3bdcc145929b39b724d554d24c b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c20e3f03556e0c3bdcc145929b39b724d554d24c
new file mode 100644
index 00000000000..15213641673
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c20e3f03556e0c3bdcc145929b39b724d554d24c
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c3ee8765cdf7204dd0e84c749b488a2d57ed6fae b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c3ee8765cdf7204dd0e84c749b488a2d57ed6fae
new file mode 100644
index 00000000000..1bf5a1268ad
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c3ee8765cdf7204dd0e84c749b488a2d57ed6fae
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c50f46e18d37df40e28d1757cec8f461e4a2643b b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c50f46e18d37df40e28d1757cec8f461e4a2643b
new file mode 100644
index 00000000000..03fc74ba495
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c50f46e18d37df40e28d1757cec8f461e4a2643b
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c5248449556ff7961b05cdcb46e26915bf71f232 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c5248449556ff7961b05cdcb46e26915bf71f232
new file mode 100644
index 00000000000..fb37c8ae6b2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c5248449556ff7961b05cdcb46e26915bf71f232
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c5abe4b58a57cd820ba85d4b4f8c978c4b6fc325 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c5abe4b58a57cd820ba85d4b4f8c978c4b6fc325
new file mode 100644
index 00000000000..ee8dd228b29
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c5abe4b58a57cd820ba85d4b4f8c978c4b6fc325
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c668a201cf1472bfd7b1133cf3037e43d8ddac44 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c668a201cf1472bfd7b1133cf3037e43d8ddac44
new file mode 100644
index 00000000000..dc2bd7c8774
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c668a201cf1472bfd7b1133cf3037e43d8ddac44
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c799bee0f970f0026ac15c5fd69f9ab129672c65 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c799bee0f970f0026ac15c5fd69f9ab129672c65
new file mode 100644
index 00000000000..dbb73079066
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c799bee0f970f0026ac15c5fd69f9ab129672c65
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c80a0213adf212446effe69a790c2ae590e5d1db b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c80a0213adf212446effe69a790c2ae590e5d1db
new file mode 100644
index 00000000000..387a68a257b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c80a0213adf212446effe69a790c2ae590e5d1db
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c9a7f5a196e67f9e9d8269fdf913c8c6cc7ee320 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c9a7f5a196e67f9e9d8269fdf913c8c6cc7ee320
new file mode 100644
index 00000000000..96511567617
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/c9a7f5a196e67f9e9d8269fdf913c8c6cc7ee320
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ca736d07e01117faa9efdb2eb46fd76fe1a4b361 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ca736d07e01117faa9efdb2eb46fd76fe1a4b361
new file mode 100644
index 00000000000..bec293f93bd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ca736d07e01117faa9efdb2eb46fd76fe1a4b361
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cab3cd4da6ace0baf6e2098437a20e67df16c854 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cab3cd4da6ace0baf6e2098437a20e67df16c854
new file mode 100644
index 00000000000..462d9a2ec17
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cab3cd4da6ace0baf6e2098437a20e67df16c854
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cb00a2de12f9d97ced5e76216cf9c3421a5572a7 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cb00a2de12f9d97ced5e76216cf9c3421a5572a7
new file mode 100644
index 00000000000..02f413f54e2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cb00a2de12f9d97ced5e76216cf9c3421a5572a7
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cb29f52b15a3d9ce259a78569e3889e874d0a1f2 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cb29f52b15a3d9ce259a78569e3889e874d0a1f2
new file mode 100644
index 00000000000..ba2b25712c5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cb29f52b15a3d9ce259a78569e3889e874d0a1f2
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cc7c3847fd9c3dae275a047ad9768e7b0cf0d34e b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cc7c3847fd9c3dae275a047ad9768e7b0cf0d34e
new file mode 100644
index 00000000000..5641b4912ff
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cc7c3847fd9c3dae275a047ad9768e7b0cf0d34e
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cc8cfcc9023fb2641c1edd4deb1082d4fa7e3b48 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cc8cfcc9023fb2641c1edd4deb1082d4fa7e3b48
new file mode 100644
index 00000000000..0d54485ee73
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cc8cfcc9023fb2641c1edd4deb1082d4fa7e3b48
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ccd8917b4913548142a88960662a064e51e675ad b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ccd8917b4913548142a88960662a064e51e675ad
new file mode 100644
index 00000000000..6f537791f68
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ccd8917b4913548142a88960662a064e51e675ad
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cd428d63c2a5b48f6034bc6e2ab9c6dcba3cc010 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cd428d63c2a5b48f6034bc6e2ab9c6dcba3cc010
new file mode 100644
index 00000000000..87bd27d2df4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cd428d63c2a5b48f6034bc6e2ab9c6dcba3cc010
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cd7dacec5470c7822000513f2b1c2c98b4cd0845 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cd7dacec5470c7822000513f2b1c2c98b4cd0845
new file mode 100644
index 00000000000..3a4d596eefb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cd7dacec5470c7822000513f2b1c2c98b4cd0845
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cd9aeb9f43d9aadb4e36572c8a6b81aebdd52c15 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cd9aeb9f43d9aadb4e36572c8a6b81aebdd52c15
new file mode 100644
index 00000000000..491b3ee710d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cd9aeb9f43d9aadb4e36572c8a6b81aebdd52c15
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cef34c646eaffb941f130a68d2059f48d5c7ab46 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cef34c646eaffb941f130a68d2059f48d5c7ab46
new file mode 100644
index 00000000000..8d079443f4e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cef34c646eaffb941f130a68d2059f48d5c7ab46
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cf7167d0b37666c882b00c3f5e1237dbf4e83204 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cf7167d0b37666c882b00c3f5e1237dbf4e83204
new file mode 100644
index 00000000000..b72ad34ea90
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cf7167d0b37666c882b00c3f5e1237dbf4e83204
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cf8a91ce4797721dd124ecdd875b9b9934154f62 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cf8a91ce4797721dd124ecdd875b9b9934154f62
new file mode 100644
index 00000000000..db455ad09f0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cf8a91ce4797721dd124ecdd875b9b9934154f62
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cfde91bf46ff3ae67a9ecfbdb38f37173e71c8f4 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cfde91bf46ff3ae67a9ecfbdb38f37173e71c8f4
new file mode 100644
index 00000000000..c2d6df8f7f3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/cfde91bf46ff3ae67a9ecfbdb38f37173e71c8f4
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d0559199e54c28f9e946d0dce5ab2dbb377a9375 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d0559199e54c28f9e946d0dce5ab2dbb377a9375
new file mode 100644
index 00000000000..3c6b366dd31
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d0559199e54c28f9e946d0dce5ab2dbb377a9375
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d07e4bc786c88b8d2304f84c7db2098666f822c0 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d07e4bc786c88b8d2304f84c7db2098666f822c0
new file mode 100644
index 00000000000..a3ac2092af7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d07e4bc786c88b8d2304f84c7db2098666f822c0
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d146392e0062eeb2ce5a7c41ed2bdde4c7496309 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d146392e0062eeb2ce5a7c41ed2bdde4c7496309
new file mode 100644
index 00000000000..a3797ef802a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d146392e0062eeb2ce5a7c41ed2bdde4c7496309
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d1aa3d34981242af940bd5aed0da9c4a3ab8df66 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d1aa3d34981242af940bd5aed0da9c4a3ab8df66
new file mode 100644
index 00000000000..9f908301d80
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d1aa3d34981242af940bd5aed0da9c4a3ab8df66
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d257fd5032bef81e055650924b99bf3f9edc9241 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d257fd5032bef81e055650924b99bf3f9edc9241
new file mode 100644
index 00000000000..a92ad3d6655
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d257fd5032bef81e055650924b99bf3f9edc9241
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d26ec55cbd5c5b8e4caa7d3db1e3a28050279061 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d26ec55cbd5c5b8e4caa7d3db1e3a28050279061
new file mode 100644
index 00000000000..0911d8cffe4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d26ec55cbd5c5b8e4caa7d3db1e3a28050279061
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d35f44f256dd19b4c3a57552b3c31c7ac2f10c71 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d35f44f256dd19b4c3a57552b3c31c7ac2f10c71
new file mode 100644
index 00000000000..0c3c2136b42
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d35f44f256dd19b4c3a57552b3c31c7ac2f10c71
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d3b49ca37e4587315ae85884c71eadb46fd216b2 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d3b49ca37e4587315ae85884c71eadb46fd216b2
new file mode 100644
index 00000000000..b706129efb6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d3b49ca37e4587315ae85884c71eadb46fd216b2
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d4167367d1f2f8f8a215d26a9e8d769ad76088d7 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d4167367d1f2f8f8a215d26a9e8d769ad76088d7
new file mode 100644
index 00000000000..15eea6866ff
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d4167367d1f2f8f8a215d26a9e8d769ad76088d7
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d515ed898716e2cb64a423f9616226222e2cc094 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d515ed898716e2cb64a423f9616226222e2cc094
new file mode 100644
index 00000000000..cd401b56b69
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d515ed898716e2cb64a423f9616226222e2cc094
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d59af7f859402a6e5f5ecd70f89da19fbdc91df8 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d59af7f859402a6e5f5ecd70f89da19fbdc91df8
new file mode 100644
index 00000000000..73855c81043
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d59af7f859402a6e5f5ecd70f89da19fbdc91df8
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d665dabc1a3fd8b71784e4bd1f7ee30d8ae5810d b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d665dabc1a3fd8b71784e4bd1f7ee30d8ae5810d
new file mode 100644
index 00000000000..c608cafd539
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d665dabc1a3fd8b71784e4bd1f7ee30d8ae5810d
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d69e544084fd583fcc259a395fe27ae401493fba b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d69e544084fd583fcc259a395fe27ae401493fba
new file mode 100644
index 00000000000..668adf49a57
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d69e544084fd583fcc259a395fe27ae401493fba
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d748324df83ffc92899a87a249da3b22a766ace0 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d748324df83ffc92899a87a249da3b22a766ace0
new file mode 100644
index 00000000000..c3b8270f3c4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d748324df83ffc92899a87a249da3b22a766ace0
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d7a63b95c19318604e338a59367e4c1e482cf23f b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d7a63b95c19318604e338a59367e4c1e482cf23f
new file mode 100644
index 00000000000..8ed8b654da3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d7a63b95c19318604e338a59367e4c1e482cf23f
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d901627df99ef6e0dd39f069fe57f8a6b4d07eaf b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d901627df99ef6e0dd39f069fe57f8a6b4d07eaf
new file mode 100644
index 00000000000..088623437db
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d901627df99ef6e0dd39f069fe57f8a6b4d07eaf
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d9bdb949b4b12ecc07bb2a6f069945184154b5b2 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d9bdb949b4b12ecc07bb2a6f069945184154b5b2
new file mode 100644
index 00000000000..5d04c0df1a6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/d9bdb949b4b12ecc07bb2a6f069945184154b5b2
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/da0759b181fdb1786c825b9269dfcd6576294540 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/da0759b181fdb1786c825b9269dfcd6576294540
new file mode 100644
index 00000000000..6d037d4da0a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/da0759b181fdb1786c825b9269dfcd6576294540
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/db081156226de488cba28da316a06b0584e47dac b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/db081156226de488cba28da316a06b0584e47dac
new file mode 100644
index 00000000000..2e28576ee89
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/db081156226de488cba28da316a06b0584e47dac
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/db2a2a890cece89427cdac0f3da25870f79de5b5 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/db2a2a890cece89427cdac0f3da25870f79de5b5
new file mode 100644
index 00000000000..20876bcad5f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/db2a2a890cece89427cdac0f3da25870f79de5b5
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/db41c3fd66b879bc44734149c16673506bdb5cb1 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/db41c3fd66b879bc44734149c16673506bdb5cb1
new file mode 100644
index 00000000000..837b3636501
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/db41c3fd66b879bc44734149c16673506bdb5cb1
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/dc2550cc5d4d010b825bc19b7a199776826e78f8 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/dc2550cc5d4d010b825bc19b7a199776826e78f8
new file mode 100644
index 00000000000..93c430c8da5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/dc2550cc5d4d010b825bc19b7a199776826e78f8
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/dc2d035effe5ef99e8053dd0e491bb46a0823eed b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/dc2d035effe5ef99e8053dd0e491bb46a0823eed
new file mode 100644
index 00000000000..b7c3cd09c09
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/dc2d035effe5ef99e8053dd0e491bb46a0823eed
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/dceca140e820d93bae46c226db040532fcbf44a2 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/dceca140e820d93bae46c226db040532fcbf44a2
new file mode 100644
index 00000000000..41e91cdaa75
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/dceca140e820d93bae46c226db040532fcbf44a2
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/dcf3999552449b6700c3216bbbc6ef1bdc77505b b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/dcf3999552449b6700c3216bbbc6ef1bdc77505b
new file mode 100644
index 00000000000..07b92cedb5d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/dcf3999552449b6700c3216bbbc6ef1bdc77505b
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/dd42e9c767d472a425beeb8acc32d564860b7d2e b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/dd42e9c767d472a425beeb8acc32d564860b7d2e
new file mode 100644
index 00000000000..99fb32e1230
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/dd42e9c767d472a425beeb8acc32d564860b7d2e
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/dd97e6544dd275f099f8061d623706f1c13f2138 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/dd97e6544dd275f099f8061d623706f1c13f2138
new file mode 100644
index 00000000000..3118a56b66f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/dd97e6544dd275f099f8061d623706f1c13f2138
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/de5319c8c809e45e7b0e488d3cc9acf6bf752687 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/de5319c8c809e45e7b0e488d3cc9acf6bf752687
new file mode 100644
index 00000000000..e3a796af7bc
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/de5319c8c809e45e7b0e488d3cc9acf6bf752687
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/decb0cfd19b3258049933b6fbcb46fec73a4d548 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/decb0cfd19b3258049933b6fbcb46fec73a4d548
new file mode 100644
index 00000000000..d50e9603423
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/decb0cfd19b3258049933b6fbcb46fec73a4d548
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/df10bfdd5da26e30797873ef8674a13fb1c3f053 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/df10bfdd5da26e30797873ef8674a13fb1c3f053
new file mode 100644
index 00000000000..67d0e7b2c16
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/df10bfdd5da26e30797873ef8674a13fb1c3f053
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/dffb62d6a8e866560a740ed82a9c393a38b637a9 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/dffb62d6a8e866560a740ed82a9c393a38b637a9
new file mode 100644
index 00000000000..c9555a660af
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/dffb62d6a8e866560a740ed82a9c393a38b637a9
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e03108134e00e531868244ee6bf423e99ebbfc46 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e03108134e00e531868244ee6bf423e99ebbfc46
new file mode 100644
index 00000000000..68bddee201a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e03108134e00e531868244ee6bf423e99ebbfc46
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e03f6f90b2caae447cc9e98e84d6a75e35e58751 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e03f6f90b2caae447cc9e98e84d6a75e35e58751
new file mode 100644
index 00000000000..090dcf7ac9d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e03f6f90b2caae447cc9e98e84d6a75e35e58751
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e0512998437db37bd3cb8a7aad0b3ab0242ce797 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e0512998437db37bd3cb8a7aad0b3ab0242ce797
new file mode 100644
index 00000000000..cc0a032f16e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e0512998437db37bd3cb8a7aad0b3ab0242ce797
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e2e4bc47f012d97d70d1956e49bddae2e1244d0f b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e2e4bc47f012d97d70d1956e49bddae2e1244d0f
new file mode 100644
index 00000000000..b7f0716c2de
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e2e4bc47f012d97d70d1956e49bddae2e1244d0f
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e3db9eacfc32f38da67361cba3b3eb3752634164 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e3db9eacfc32f38da67361cba3b3eb3752634164
new file mode 100644
index 00000000000..b87b40ca84d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e3db9eacfc32f38da67361cba3b3eb3752634164
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e4969d40e8341427dbf49bf74b7d97afe1169bf1 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e4969d40e8341427dbf49bf74b7d97afe1169bf1
new file mode 100644
index 00000000000..7c90707c912
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e4969d40e8341427dbf49bf74b7d97afe1169bf1
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e50634e178601a5938c5a0448cb4157b02e6a1b4 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e50634e178601a5938c5a0448cb4157b02e6a1b4
new file mode 100644
index 00000000000..b29cf938e5e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e50634e178601a5938c5a0448cb4157b02e6a1b4
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e5179878e3fb95877a41c20778ee5016dea8d8d3 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e5179878e3fb95877a41c20778ee5016dea8d8d3
new file mode 100644
index 00000000000..f5c2c00f6a0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e5179878e3fb95877a41c20778ee5016dea8d8d3
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e5ee3fabcb52e7f8858042a856fff30efe6c7acc b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e5ee3fabcb52e7f8858042a856fff30efe6c7acc
new file mode 100644
index 00000000000..ae3ce77c17a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e5ee3fabcb52e7f8858042a856fff30efe6c7acc
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e62b3eb166d52861354ca337ded5d43daba4bb66 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e62b3eb166d52861354ca337ded5d43daba4bb66
new file mode 100644
index 00000000000..79ac45cd282
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e62b3eb166d52861354ca337ded5d43daba4bb66
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e6896001a225f621044aec7ddb845e5cb38eff00 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e6896001a225f621044aec7ddb845e5cb38eff00
new file mode 100644
index 00000000000..dd690a3c1aa
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e6896001a225f621044aec7ddb845e5cb38eff00
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e730ae86386fa1fe0c4450398b1e54ae1c39634b b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e730ae86386fa1fe0c4450398b1e54ae1c39634b
new file mode 100644
index 00000000000..dcfb0a37fb6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e730ae86386fa1fe0c4450398b1e54ae1c39634b
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e7b71ef22ca8436fde215810dae55aaa887ccb32 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e7b71ef22ca8436fde215810dae55aaa887ccb32
new file mode 100644
index 00000000000..589db6d47f6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e7b71ef22ca8436fde215810dae55aaa887ccb32
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e7c8785213e42e2f7697a217806e5de4a0997b26 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e7c8785213e42e2f7697a217806e5de4a0997b26
new file mode 100644
index 00000000000..cfe5f16be48
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e7c8785213e42e2f7697a217806e5de4a0997b26
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e83aed593d8d3ff801af1b34880f50125dba6c33 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e83aed593d8d3ff801af1b34880f50125dba6c33
new file mode 100644
index 00000000000..5f47c4440ff
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/e83aed593d8d3ff801af1b34880f50125dba6c33
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ea28a8c84a47b956dc3e847c6145bb91d3cd82d0 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ea28a8c84a47b956dc3e847c6145bb91d3cd82d0
new file mode 100644
index 00000000000..5f2b1d2d3b5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ea28a8c84a47b956dc3e847c6145bb91d3cd82d0
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ea768448f4902b2b15ebdb25938aa739b2d6e881 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ea768448f4902b2b15ebdb25938aa739b2d6e881
new file mode 100644
index 00000000000..6f3a57e2bca
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ea768448f4902b2b15ebdb25938aa739b2d6e881
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/eadf0ead393cb49cfbb02f765da794d368393401 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/eadf0ead393cb49cfbb02f765da794d368393401
new file mode 100644
index 00000000000..5ed22d3816e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/eadf0ead393cb49cfbb02f765da794d368393401
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ebb6456eb1eb805f71c55cbc46a42a14a622d8dc b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ebb6456eb1eb805f71c55cbc46a42a14a622d8dc
new file mode 100644
index 00000000000..520f78e3a8f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ebb6456eb1eb805f71c55cbc46a42a14a622d8dc
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ecbbcb5ad293d71d49e3cc3885b7f96e5b1dcf5f b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ecbbcb5ad293d71d49e3cc3885b7f96e5b1dcf5f
new file mode 100644
index 00000000000..10efac73ca8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ecbbcb5ad293d71d49e3cc3885b7f96e5b1dcf5f
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ece5af415cc041bb735aa86afa06b075b4c54834 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ece5af415cc041bb735aa86afa06b075b4c54834
new file mode 100644
index 00000000000..3294d84768b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ece5af415cc041bb735aa86afa06b075b4c54834
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ed8e0e4cb585c3ff11e7f8d6efafec59e471e2cc b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ed8e0e4cb585c3ff11e7f8d6efafec59e471e2cc
new file mode 100644
index 00000000000..c12e4d8378f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ed8e0e4cb585c3ff11e7f8d6efafec59e471e2cc
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ee336547d0acfd8958ec01b018e8530947520977 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ee336547d0acfd8958ec01b018e8530947520977
new file mode 100644
index 00000000000..b88e6a6d00e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ee336547d0acfd8958ec01b018e8530947520977
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/eefa939889601e0d205e95378595edc06832711e b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/eefa939889601e0d205e95378595edc06832711e
new file mode 100644
index 00000000000..624f8b7908c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/eefa939889601e0d205e95378595edc06832711e
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/efc3c7f8c4fd74deed16b5bb2aee0615e90c47a9 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/efc3c7f8c4fd74deed16b5bb2aee0615e90c47a9
new file mode 100644
index 00000000000..88ebcb47fcb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/efc3c7f8c4fd74deed16b5bb2aee0615e90c47a9
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f0af4ce54b32c3d9dd2b0711541bbe9f7c2a38ad b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f0af4ce54b32c3d9dd2b0711541bbe9f7c2a38ad
new file mode 100644
index 00000000000..5fdc62c87ae
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f0af4ce54b32c3d9dd2b0711541bbe9f7c2a38ad
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f0d760923f2a897a427b03ce6c9a47cd48181150 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f0d760923f2a897a427b03ce6c9a47cd48181150
new file mode 100644
index 00000000000..45865a1801a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f0d760923f2a897a427b03ce6c9a47cd48181150
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f1a2437e8045ddca09679937f1f992eccdf78cf5 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f1a2437e8045ddca09679937f1f992eccdf78cf5
new file mode 100644
index 00000000000..23f43218e9d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f1a2437e8045ddca09679937f1f992eccdf78cf5
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f1c41d56e520130a00a1eb903ff65ff56959b383 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f1c41d56e520130a00a1eb903ff65ff56959b383
new file mode 100644
index 00000000000..4c7b3369f20
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f1c41d56e520130a00a1eb903ff65ff56959b383
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f375c6bb597d19eba397de0c632e3827b2ab0e60 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f375c6bb597d19eba397de0c632e3827b2ab0e60
new file mode 100644
index 00000000000..e42ce19f841
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f375c6bb597d19eba397de0c632e3827b2ab0e60
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f3c93dcf82cbd38a84d53163889ed83583c1757a b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f3c93dcf82cbd38a84d53163889ed83583c1757a
new file mode 100644
index 00000000000..b74d23675b4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f3c93dcf82cbd38a84d53163889ed83583c1757a
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f3d896c80afd90ba7aaad52e5a58a6847d3b06b2 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f3d896c80afd90ba7aaad52e5a58a6847d3b06b2
new file mode 100644
index 00000000000..87b4b095699
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f3d896c80afd90ba7aaad52e5a58a6847d3b06b2
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f3f1d178edbab5fbc6ad3633279b6e2c6122ba92 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f3f1d178edbab5fbc6ad3633279b6e2c6122ba92
new file mode 100644
index 00000000000..82a222e7f39
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f3f1d178edbab5fbc6ad3633279b6e2c6122ba92
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f406f3f60cf7f821824b7f6ed96e8464059da397 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f406f3f60cf7f821824b7f6ed96e8464059da397
new file mode 100644
index 00000000000..dc70146b5d9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f406f3f60cf7f821824b7f6ed96e8464059da397
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f433573fbc60e06dbef10cab93d4da29824aaa66 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f433573fbc60e06dbef10cab93d4da29824aaa66
new file mode 100644
index 00000000000..d7a88e8c592
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f433573fbc60e06dbef10cab93d4da29824aaa66
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f4374cd2ce923bfa39f1c610ebd98c20b9479bb3 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f4374cd2ce923bfa39f1c610ebd98c20b9479bb3
new file mode 100644
index 00000000000..30746234970
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f4374cd2ce923bfa39f1c610ebd98c20b9479bb3
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f4be1d5768af0d4d87947a5d9d7047304df57e10 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f4be1d5768af0d4d87947a5d9d7047304df57e10
new file mode 100644
index 00000000000..2f5bcbd6a1e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f4be1d5768af0d4d87947a5d9d7047304df57e10
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f503dd5d6d53129eb2a3788297afe481249cbed7 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f503dd5d6d53129eb2a3788297afe481249cbed7
new file mode 100644
index 00000000000..c9e9f07719d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f503dd5d6d53129eb2a3788297afe481249cbed7
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f69cee58c4edfe1fa3d9f5bb55101e23bfa2904d b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f69cee58c4edfe1fa3d9f5bb55101e23bfa2904d
new file mode 100644
index 00000000000..687946d9842
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f69cee58c4edfe1fa3d9f5bb55101e23bfa2904d
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f6ac1e6f1bab62eb2249b23ebff5c37c7433dfde b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f6ac1e6f1bab62eb2249b23ebff5c37c7433dfde
new file mode 100644
index 00000000000..2db77df9e15
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f6ac1e6f1bab62eb2249b23ebff5c37c7433dfde
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f6c4066e45d76615b04825c9b15a577877e55bdf b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f6c4066e45d76615b04825c9b15a577877e55bdf
new file mode 100644
index 00000000000..fae4748c3b2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f6c4066e45d76615b04825c9b15a577877e55bdf
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f7272e731ab47fb9d5a040a54ef562cd8dfba839 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f7272e731ab47fb9d5a040a54ef562cd8dfba839
new file mode 100644
index 00000000000..1abc9d96d1f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f7272e731ab47fb9d5a040a54ef562cd8dfba839
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f8753a03428cb55091e1e616cbfae5ef4d34dfd1 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f8753a03428cb55091e1e616cbfae5ef4d34dfd1
new file mode 100644
index 00000000000..bb787ba4f78
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/f8753a03428cb55091e1e616cbfae5ef4d34dfd1
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/fa547ad152f92ad07d5b92b4fb237bf8e0dbae2c b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/fa547ad152f92ad07d5b92b4fb237bf8e0dbae2c
new file mode 100644
index 00000000000..bfdd7484ffd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/fa547ad152f92ad07d5b92b4fb237bf8e0dbae2c
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/fae3a5206c778c8bac02f916f55fd3f18d100173 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/fae3a5206c778c8bac02f916f55fd3f18d100173
new file mode 100644
index 00000000000..c36485d4c26
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/fae3a5206c778c8bac02f916f55fd3f18d100173
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/fb77df6aaea88f191f0e615f8637544f1d9908f7 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/fb77df6aaea88f191f0e615f8637544f1d9908f7
new file mode 100644
index 00000000000..35cd4de765c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/fb77df6aaea88f191f0e615f8637544f1d9908f7
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/fb8e8dcceaed38fe038d6a8b7aaa45841f0897ea b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/fb8e8dcceaed38fe038d6a8b7aaa45841f0897ea
new file mode 100644
index 00000000000..915bf322efa
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/fb8e8dcceaed38fe038d6a8b7aaa45841f0897ea
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/fb95bb125f3d370e97278aaff87dc7650b6c89a5 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/fb95bb125f3d370e97278aaff87dc7650b6c89a5
new file mode 100644
index 00000000000..dde151cb029
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/fb95bb125f3d370e97278aaff87dc7650b6c89a5
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/fdd02b03ce9fdcb1ad0677308ede92f1f6b80951 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/fdd02b03ce9fdcb1ad0677308ede92f1f6b80951
new file mode 100644
index 00000000000..4d5e86239ae
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/fdd02b03ce9fdcb1ad0677308ede92f1f6b80951
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/fe58d5023d23b8960c8d7018a96dcb7659fe809d b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/fe58d5023d23b8960c8d7018a96dcb7659fe809d
new file mode 100644
index 00000000000..d1cd18f79f1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/fe58d5023d23b8960c8d7018a96dcb7659fe809d
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/fe7afd1d477b946c02e7609d4c5fddf2df911c49 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/fe7afd1d477b946c02e7609d4c5fddf2df911c49
new file mode 100644
index 00000000000..2ffd8895fae
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/fe7afd1d477b946c02e7609d4c5fddf2df911c49
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/fe8488887062749e9c47274436d3b4a3586c8585 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/fe8488887062749e9c47274436d3b4a3586c8585
new file mode 100644
index 00000000000..3221629ef49
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/fe8488887062749e9c47274436d3b4a3586c8585
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ff4efc7e62cbf78e3bb877eee51602eb0d335a61 b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ff4efc7e62cbf78e3bb877eee51602eb0d335a61
new file mode 100644
index 00000000000..4b0d9e07de1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text_codec_fuzzer_seed_corpus/ff4efc7e62cbf78e3bb877eee51602eb0d335a61
Binary files differ
diff --git a/chromium/third_party/blink/renderer/platform/theme.cc b/chromium/third_party/blink/renderer/platform/theme.cc
new file mode 100644
index 00000000000..af8d1a3b41f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/theme.cc
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2008 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 "third_party/blink/renderer/platform/theme.h"
+
+namespace blink {
+
+LengthBox Theme::ControlBorder(ControlPart part,
+ const FontDescription&,
+ const LengthBox& zoomed_box,
+ float) const {
+ switch (part) {
+ case kPushButtonPart:
+ case kMenulistPart:
+ case kSearchFieldPart:
+ case kCheckboxPart:
+ case kRadioPart:
+ return LengthBox(0);
+ default:
+ return zoomed_box;
+ }
+}
+
+LengthBox Theme::ControlPadding(ControlPart part,
+ const FontDescription&,
+ const Length& zoomed_box_top,
+ const Length& zoomed_box_right,
+ const Length& zoomed_box_bottom,
+ const Length& zoomed_box_left,
+ float) const {
+ switch (part) {
+ case kMenulistPart:
+ case kMenulistButtonPart:
+ case kCheckboxPart:
+ case kRadioPart:
+ return LengthBox(0);
+ default:
+ return LengthBox(zoomed_box_top, zoomed_box_right, zoomed_box_bottom,
+ zoomed_box_left);
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/theme.h b/chromium/third_party/blink/renderer/platform/theme.h
new file mode 100644
index 00000000000..fb4f97d527c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/theme.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2008 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_THEME_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_THEME_H_
+
+#include "third_party/blink/renderer/platform/fonts/font_description.h"
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/graphics/color.h"
+#include "third_party/blink/renderer/platform/length_box.h"
+#include "third_party/blink/renderer/platform/length_size.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/theme_types.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+class GraphicsContext;
+class ScrollableArea;
+
+// Unlike other platform classes, Theme does extensively use virtual functions.
+// This design allows a platform to switch between multiple themes at runtime.
+class PLATFORM_EXPORT Theme {
+ USING_FAST_MALLOC(Theme);
+ WTF_MAKE_NONCOPYABLE(Theme);
+
+ public:
+ Theme() = default;
+ virtual ~Theme() = default;
+
+ // A method to obtain the baseline position adjustment for a "leaf" control.
+ // This will only be used if a baseline position cannot be determined by
+ // examining child content. Checkboxes and radio buttons are examples of
+ // controls that need to do this. The adjustment is an offset that adds to
+ // the baseline, e.g., marginTop() + height() + |offset|.
+ // The offset is not zoomed.
+ virtual int BaselinePositionAdjustment(ControlPart) const { return 0; }
+
+ // A method asking if the control changes its appearance when the window is
+ // inactive.
+ virtual bool ControlHasInactiveAppearance(ControlPart) const { return false; }
+
+ // General methods for whether or not any of the controls in the theme change
+ // appearance when the window is inactive or when hovered over.
+ virtual bool ControlsCanHaveInactiveAppearance() const { return false; }
+ virtual bool ControlsCanHaveHoveredAppearance() const { return false; }
+
+ // Used by LayoutTheme::isControlStyled to figure out if the native look and
+ // feel should be turned off.
+ virtual bool ControlDrawsBorder(ControlPart) const { return true; }
+ virtual bool ControlDrawsBackground(ControlPart) const { return true; }
+ virtual bool ControlDrawsFocusOutline(ControlPart) const { return true; }
+
+ // Methods for obtaining platform-specific colors.
+ virtual Color SelectionColor(ControlPart, ControlState, SelectionPart) const {
+ return Color();
+ }
+ virtual Color TextSearchHighlightColor() const { return Color(); }
+
+ // CSS system colors and fonts
+ virtual Color SystemColor(ThemeColor) const { return Color(); }
+
+ // How fast the caret blinks in text fields.
+ virtual TimeDelta CaretBlinkInterval() const {
+ return TimeDelta::FromMilliseconds(500);
+ }
+
+ // Methods used to adjust the ComputedStyles of controls.
+
+ // The font description result should have a zoomed font size.
+ virtual FontDescription ControlFont(ControlPart,
+ const FontDescription& font_description,
+ float /*zoomFactor*/) const {
+ return font_description;
+ }
+
+ // The size here is in zoomed coordinates already. If a new size is returned,
+ // it also needs to be in zoomed coordinates.
+ virtual LengthSize GetControlSize(ControlPart,
+ const FontDescription&,
+ const LengthSize& zoomed_size,
+ float /*zoomFactor*/) const {
+ return zoomed_size;
+ }
+
+ // Returns the minimum size for a control in zoomed coordinates.
+ virtual LengthSize MinimumControlSize(ControlPart,
+ const FontDescription&,
+ float /*zoomFactor*/) const {
+ return LengthSize(Length(0, kFixed), Length(0, kFixed));
+ }
+
+ // Allows the theme to modify the existing padding/border.
+ virtual LengthBox ControlPadding(ControlPart,
+ const FontDescription&,
+ const Length& zoomed_box_top,
+ const Length& zoomed_box_right,
+ const Length& zoomed_box_bottom,
+ const Length& zoomed_box_left,
+ float zoom_factor) const;
+ virtual LengthBox ControlBorder(ControlPart,
+ const FontDescription&,
+ const LengthBox& zoomed_box,
+ float zoom_factor) const;
+
+ // Whether or not whitespace: pre should be forced on always.
+ virtual bool ControlRequiresPreWhiteSpace(ControlPart) const { return false; }
+
+ // Method for painting a control. The rect is in zoomed coordinates.
+ virtual void Paint(ControlPart,
+ ControlStates,
+ GraphicsContext&,
+ const IntRect& /*zoomedRect*/,
+ float /*zoomFactor*/,
+ ScrollableArea*) const {}
+
+ // Add visual overflow (e.g., the check on an OS X checkbox). The rect passed
+ // in is in zoomed coordinates so the inflation should take that into account
+ // and make sure the inflation amount is also scaled by the zoomFactor.
+ virtual void AddVisualOverflow(ControlPart,
+ ControlStates,
+ float zoom_factor,
+ IntRect& border_box) const {}
+
+ private:
+ mutable Color active_selection_color_;
+ mutable Color inactive_selection_color_;
+};
+
+PLATFORM_EXPORT Theme* PlatformTheme();
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_THEME_H_
diff --git a/chromium/third_party/blink/renderer/platform/theme_types.h b/chromium/third_party/blink/renderer/platform/theme_types.h
new file mode 100644
index 00000000000..b43231ea978
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/theme_types.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2008, 2009, 2010 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_THEME_TYPES_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_THEME_TYPES_H_
+
+namespace blink {
+
+enum ControlState {
+ kHoverControlState = 1,
+ kPressedControlState = 1 << 1,
+ kFocusControlState = 1 << 2,
+ kEnabledControlState = 1 << 3,
+ kCheckedControlState = 1 << 4,
+ kReadOnlyControlState = 1 << 5,
+ kWindowInactiveControlState = 1 << 7,
+ kIndeterminateControlState = 1 << 8,
+ kSpinUpControlState =
+ 1 << 9, // Sub-state for HoverControlState and PressedControlState.
+ kAllControlStates = 0xffffffff
+};
+
+typedef unsigned ControlStates;
+
+// Must follow CSSValueKeywords.in order
+enum ControlPart {
+ kNoControlPart,
+ kCheckboxPart,
+ kRadioPart,
+ kPushButtonPart,
+ kSquareButtonPart,
+ kButtonPart,
+ kButtonBevelPart,
+ kInnerSpinButtonPart,
+ kListboxPart,
+ kListItemPart,
+ kMediaEnterFullscreenButtonPart,
+ kMediaExitFullscreenButtonPart,
+ kMediaMuteButtonPart,
+ kMediaPlayButtonPart,
+ kMediaOverlayPlayButtonPart,
+ kMediaToggleClosedCaptionsButtonPart,
+ kMediaSliderPart,
+ kMediaSliderThumbPart,
+ kMediaVolumeSliderContainerPart,
+ kMediaVolumeSliderPart,
+ kMediaVolumeSliderThumbPart,
+ kMediaControlsBackgroundPart,
+ kMediaControlsFullscreenBackgroundPart,
+ kMediaCurrentTimePart,
+ kMediaTimeRemainingPart,
+ kMediaCastOffButtonPart,
+ kMediaOverlayCastOffButtonPart,
+ kMediaTrackSelectionCheckmarkPart,
+ kMediaClosedCaptionsIconPart,
+ kMediaSubtitlesIconPart,
+ kMediaOverflowMenuButtonPart,
+ kMediaDownloadIconPart,
+ kMediaRemotingCastIconPart,
+ kMediaControlPart,
+ kMenulistPart,
+ kMenulistButtonPart,
+ kMenulistTextPart,
+ kMenulistTextFieldPart,
+ kMeterPart,
+ kProgressBarPart,
+ kProgressBarValuePart,
+ kSliderHorizontalPart,
+ kSliderVerticalPart,
+ kSliderThumbHorizontalPart,
+ kSliderThumbVerticalPart,
+ kCaretPart,
+ kSearchFieldPart,
+ kSearchFieldCancelButtonPart,
+ kTextFieldPart,
+ kTextAreaPart,
+ kCapsLockIndicatorPart
+};
+
+enum SelectionPart { kSelectionBackground, kSelectionForeground };
+
+enum ThemeFont {
+ kCaptionFont,
+ kIconFont,
+ kMenuFont,
+ kMessageBoxFont,
+ kSmallCaptionFont,
+ kStatusBarFont,
+ kMiniControlFont,
+ kSmallControlFont,
+ kControlFont
+};
+
+enum ThemeColor {
+ kActiveBorderColor,
+ kActiveCaptionColor,
+ kAppWorkspaceColor,
+ kBackgroundColor,
+ kButtonFaceColor,
+ kButtonHighlightColor,
+ kButtonShadowColor,
+ kButtonTextColor,
+ kCaptionTextColor,
+ kGrayTextColor,
+ kHighlightColor,
+ kHighlightTextColor,
+ kInactiveBorderColor,
+ kInactiveCaptionColor,
+ kInactiveCaptionTextColor,
+ kInfoBackgroundColor,
+ kInfoTextColor,
+ kMatchColor,
+ kMenuTextColor,
+ kScrollbarColor,
+ kThreeDDarkDhasowColor,
+ kThreeDFaceColor,
+ kThreeDHighlightColor,
+ kThreeDLightShadowColor,
+ kThreeDShadowCLor,
+ kWindowColor,
+ kWindowFrameColor,
+ kWindowTextColor,
+ kFocusRingColor,
+ kActiveListBoxSelection,
+ kActiveListBoxSelectionText,
+ kInactiveListBoxSelection,
+ kInactiveListBoxSelectionText
+};
+
+} // namespace blink
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/threading/DEPS b/chromium/third_party/blink/renderer/platform/threading/DEPS
new file mode 100644
index 00000000000..831fae70c03
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/threading/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+ # To whitelist base/ stuff Blink is allowed to include, we list up all
+ # directories and files instead of writing 'base/'.
+ "+base/task_scheduler",
+ "+base/test",
+]
diff --git a/chromium/third_party/blink/renderer/platform/threading/background_task_runner.cc b/chromium/third_party/blink/renderer/platform/threading/background_task_runner.cc
new file mode 100644
index 00000000000..b5d0e0c80bd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/threading/background_task_runner.cc
@@ -0,0 +1,20 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/threading/background_task_runner.h"
+
+#include "base/location.h"
+#include "base/task_scheduler/post_task.h"
+
+namespace blink {
+
+void BackgroundTaskRunner::PostOnBackgroundThread(
+ const base::Location& location,
+ CrossThreadClosure closure) {
+ base::PostTaskWithTraits(location,
+ {base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
+ ConvertToBaseCallback(std::move(closure)));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/threading/background_task_runner.h b/chromium/third_party/blink/renderer/platform/threading/background_task_runner.h
new file mode 100644
index 00000000000..52df8375607
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/threading/background_task_runner.h
@@ -0,0 +1,23 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_THREADING_BACKGROUND_TASK_RUNNER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_THREADING_BACKGROUND_TASK_RUNNER_H_
+
+#include "base/location.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+
+namespace blink {
+
+namespace BackgroundTaskRunner {
+
+PLATFORM_EXPORT void PostOnBackgroundThread(const base::Location&,
+ CrossThreadClosure);
+
+} // BackgroundTaskRunner
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/threading/background_task_runner_test.cc b/chromium/third_party/blink/renderer/platform/threading/background_task_runner_test.cc
new file mode 100644
index 00000000000..5c9cc81b154
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/threading/background_task_runner_test.cc
@@ -0,0 +1,34 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/threading/background_task_runner.h"
+
+#include <memory>
+#include "base/location.h"
+#include "base/test/scoped_task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/waitable_event.h"
+
+namespace blink {
+
+namespace {
+
+void PingPongTask(WaitableEvent* done_event) {
+ done_event->Signal();
+}
+
+} // namespace
+
+TEST(BackgroundTaskRunnerTest, RunOnBackgroundThread) {
+ base::test::ScopedTaskEnvironment scoped_task_environment;
+ std::unique_ptr<WaitableEvent> done_event = std::make_unique<WaitableEvent>();
+ BackgroundTaskRunner::PostOnBackgroundThread(
+ FROM_HERE,
+ CrossThreadBind(&PingPongTask, CrossThreadUnretained(done_event.get())));
+ // Test passes by not hanging on the following wait().
+ done_event->Wait();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/time_clamper.cc b/chromium/third_party/blink/renderer/platform/time_clamper.cc
new file mode 100644
index 00000000000..9e120ff6cc4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/time_clamper.cc
@@ -0,0 +1,54 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/time_clamper.h"
+
+#include "base/bit_cast.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/cryptographically_random_number.h"
+
+#include <cmath>
+
+namespace blink {
+
+TimeClamper::TimeClamper() {
+ CryptographicallyRandomValues(&secret_, sizeof(secret_));
+}
+
+double TimeClamper::ClampTimeResolution(double time_seconds) const {
+ DCHECK_GE(time_seconds, 0);
+ double interval = floor(time_seconds / kResolutionSeconds);
+ double clamped_time = interval * kResolutionSeconds;
+ double tick_threshold = ThresholdFor(clamped_time);
+
+ if (time_seconds >= tick_threshold)
+ return (interval + 1) * kResolutionSeconds;
+ return clamped_time;
+}
+
+inline double TimeClamper::ThresholdFor(double clamped_time) const {
+ uint64_t time_hash = MurmurHash3(bit_cast<int64_t>(clamped_time) ^ secret_);
+ return clamped_time + kResolutionSeconds * ToDouble(time_hash);
+}
+
+// static
+inline double TimeClamper::ToDouble(uint64_t value) {
+ // Exponent for double values for [1.0 .. 2.0]
+ static const uint64_t kExponentBits = uint64_t{0x3FF0000000000000};
+ static const uint64_t kMantissaMask = uint64_t{0x000FFFFFFFFFFFFF};
+ uint64_t random = (value & kMantissaMask) | kExponentBits;
+ return bit_cast<double>(random) - 1;
+}
+
+// static
+inline uint64_t TimeClamper::MurmurHash3(uint64_t value) {
+ value ^= value >> 33;
+ value *= uint64_t{0xFF51AFD7ED558CCD};
+ value ^= value >> 33;
+ value *= uint64_t{0xC4CEB9FE1A85EC53};
+ value ^= value >> 33;
+ return value;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/time_clamper.h b/chromium/third_party/blink/renderer/platform/time_clamper.h
new file mode 100644
index 00000000000..76e2888e8e7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/time_clamper.h
@@ -0,0 +1,42 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TIME_CLAMPER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TIME_CLAMPER_H_
+
+#include "base/macros.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+#include <stdint.h>
+
+namespace blink {
+
+class PLATFORM_EXPORT TimeClamper {
+ public:
+ static constexpr double kResolutionSeconds = 0.0001;
+
+ TimeClamper();
+
+ // Deterministically clamp the time value |time_seconds| to a 100us interval
+ // to prevent timing attacks. See
+ // http://www.w3.org/TR/hr-time-2/#privacy-security.
+ //
+ // For each clamped time interval, we compute a pseudorandom transition
+ // threshold. The returned time will either be the start of that interval or
+ // the next one depending on which side of the threshold |time_seconds| is.
+ double ClampTimeResolution(double time_seconds) const;
+
+ private:
+ inline double ThresholdFor(double clamped_time) const;
+ static inline double ToDouble(uint64_t value);
+ static inline uint64_t MurmurHash3(uint64_t value);
+
+ uint64_t secret_;
+
+ DISALLOW_COPY_AND_ASSIGN(TimeClamper);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TIME_CLAMPER_H_
diff --git a/chromium/third_party/blink/renderer/platform/time_clamper_test.cc b/chromium/third_party/blink/renderer/platform/time_clamper_test.cc
new file mode 100644
index 00000000000..f519dcfb692
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/time_clamper_test.cc
@@ -0,0 +1,99 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/time_clamper.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include <cmath>
+
+namespace blink {
+namespace {
+const double kInterval = TimeClamper::kResolutionSeconds;
+}
+
+TEST(TimeClamperTest, TimeStampsAreNonNegative) {
+ TimeClamper clamper;
+ EXPECT_GE(clamper.ClampTimeResolution(0), 0.f);
+ EXPECT_GE(clamper.ClampTimeResolution(TimeClamper::kResolutionSeconds), 0.f);
+}
+
+TEST(TimeClamperTest, TimeStampsIncreaseByFixedAmount) {
+ const double kEpsilon = 1e-10;
+ TimeClamper clamper;
+ double prev = clamper.ClampTimeResolution(0);
+ for (double time_seconds = 0; time_seconds < kInterval * 100;
+ time_seconds += kInterval * 0.1) {
+ double clamped_time = clamper.ClampTimeResolution(time_seconds);
+ double delta = clamped_time - prev;
+ ASSERT_GE(delta, 0);
+ if (delta > kEpsilon) {
+ ASSERT_TRUE(std::fabs(delta - kInterval) < kEpsilon);
+ prev = clamped_time;
+ }
+ }
+}
+
+TEST(TimeClamperTest, ClampingIsConsistent) {
+ TimeClamper clamper;
+ for (double time_seconds = 0; time_seconds < kInterval * 100;
+ time_seconds += kInterval * 0.1) {
+ double t1 = clamper.ClampTimeResolution(time_seconds);
+ double t2 = clamper.ClampTimeResolution(time_seconds);
+ EXPECT_EQ(t1, t2);
+ }
+}
+
+TEST(TimeClamperTest, ClampingIsPerInstance) {
+ const double kEpsilon = 1e-10;
+ TimeClamper clamper1;
+ TimeClamper clamper2;
+ double time_seconds = 0;
+ while (true) {
+ if (std::fabs(clamper1.ClampTimeResolution(time_seconds) -
+ clamper2.ClampTimeResolution(time_seconds)) > kEpsilon) {
+ break;
+ }
+ time_seconds += kInterval;
+ }
+}
+
+TEST(TimeClamperTest, ClampingIsUniform) {
+ const int kBuckets = 8;
+ const int kSampleCount = 10000;
+ const double kEpsilon = 1e-10;
+ const double kTimeStep = kInterval / kBuckets;
+ double time_seconds = 299792.458;
+ int histogram[kBuckets] = {0};
+ TimeClamper clamper;
+
+ // This test ensures the jitter thresholds are approximately uniformly
+ // distributed inside the clamping intervals. It samples individual intervals
+ // to detect where the threshold is and counts the number of steps taken.
+ for (int i = 0; i < kSampleCount; i++) {
+ double start = clamper.ClampTimeResolution(time_seconds);
+ for (int step = 0; step < kBuckets; step++) {
+ time_seconds += kTimeStep;
+ if (std::abs(clamper.ClampTimeResolution(time_seconds) - start) >
+ kEpsilon) {
+ histogram[step]++;
+ // Skip to the next interval to make sure each measurement is
+ // independent.
+ time_seconds = floor(time_seconds / kInterval) * kInterval + kInterval;
+ break;
+ }
+ }
+ }
+
+ double expected_count = kSampleCount / kBuckets;
+ double chi_squared = 0;
+ for (int i = 0; i < kBuckets; ++i) {
+ double difference = histogram[i] - expected_count;
+ chi_squared += difference * difference / expected_count;
+ }
+ // P-value for a 0.001 significance level with 7 degrees of freedom.
+ EXPECT_LT(chi_squared, 24.322);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/timer.cc b/chromium/third_party/blink/renderer/platform/timer.cc
new file mode 100644
index 00000000000..15a4992b6e1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/timer.cc
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/timer.h"
+
+#include <limits.h>
+#include <math.h>
+#include <algorithm>
+#include <limits>
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/scheduler/child/web_scheduler.h"
+#include "third_party/blink/renderer/platform/wtf/address_sanitizer.h"
+#include "third_party/blink/renderer/platform/wtf/atomics.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+TimerBase::TimerBase(
+ scoped_refptr<base::SingleThreadTaskRunner> web_task_runner)
+ : web_task_runner_(std::move(web_task_runner)),
+#if DCHECK_IS_ON()
+ thread_(CurrentThread()),
+#endif
+ weak_ptr_factory_(this) {
+}
+
+TimerBase::~TimerBase() {
+ Stop();
+}
+
+void TimerBase::Start(TimeDelta next_fire_interval,
+ TimeDelta repeat_interval,
+ const base::Location& caller) {
+#if DCHECK_IS_ON()
+ DCHECK_EQ(thread_, CurrentThread());
+#endif
+
+ location_ = caller;
+ repeat_interval_ = repeat_interval;
+ SetNextFireTime(TimerCurrentTimeTicks(), next_fire_interval);
+}
+
+void TimerBase::Stop() {
+#if DCHECK_IS_ON()
+ DCHECK_EQ(thread_, CurrentThread());
+#endif
+
+ repeat_interval_ = TimeDelta();
+ next_fire_time_ = TimeTicks();
+ weak_ptr_factory_.InvalidateWeakPtrs();
+}
+
+TimeDelta TimerBase::NextFireIntervalDelta() const {
+ DCHECK(IsActive());
+ TimeTicks current = TimerCurrentTimeTicks();
+ if (next_fire_time_ < current)
+ return TimeDelta();
+ return next_fire_time_ - current;
+}
+
+void TimerBase::MoveToNewTaskRunner(
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+#if DCHECK_IS_ON()
+ DCHECK_EQ(thread_, CurrentThread());
+ DCHECK(task_runner->RunsTasksInCurrentSequence());
+#endif
+ // If the underlying task runner stays the same, ignore it.
+ if (web_task_runner_ == task_runner) {
+ return;
+ }
+
+ bool active = IsActive();
+ weak_ptr_factory_.InvalidateWeakPtrs();
+ web_task_runner_ = std::move(task_runner);
+
+ if (!active)
+ return;
+
+ TimeTicks now = TimerCurrentTimeTicks();
+ TimeTicks next_fire_time = std::max(next_fire_time_, now);
+ next_fire_time_ = TimeTicks();
+
+ SetNextFireTime(now, next_fire_time - now);
+}
+
+scoped_refptr<base::SingleThreadTaskRunner> TimerBase::TimerTaskRunner() const {
+ return web_task_runner_;
+}
+
+void TimerBase::SetNextFireTime(TimeTicks now, TimeDelta delay) {
+#if DCHECK_IS_ON()
+ DCHECK_EQ(thread_, CurrentThread());
+#endif
+
+ TimeTicks new_time = now + delay;
+
+ if (next_fire_time_ != new_time) {
+ next_fire_time_ = new_time;
+
+ // Cancel any previously posted task.
+ weak_ptr_factory_.InvalidateWeakPtrs();
+
+ TimerTaskRunner()->PostDelayedTask(
+ location_,
+ WTF::Bind(&TimerBase::RunInternal, weak_ptr_factory_.GetWeakPtr()),
+ delay);
+ }
+}
+
+NO_SANITIZE_ADDRESS
+void TimerBase::RunInternal() {
+ if (!CanFire())
+ return;
+
+ weak_ptr_factory_.InvalidateWeakPtrs();
+
+ TRACE_EVENT0("blink", "TimerBase::run");
+#if DCHECK_IS_ON()
+ DCHECK_EQ(thread_, CurrentThread())
+ << "Timer posted by " << location_.function_name() << " "
+ << location_.file_name() << " was run on a different thread";
+#endif
+
+ if (!repeat_interval_.is_zero()) {
+ TimeTicks now = TimerCurrentTimeTicks();
+ // This computation should be drift free, and it will cope if we miss a
+ // beat, which can easily happen if the thread is busy. It will also cope
+ // if we get called slightly before m_unalignedNextFireTime, which can
+ // happen due to lack of timer precision.
+ TimeDelta interval_to_next_fire_time =
+ repeat_interval_ - (now - next_fire_time_) % repeat_interval_;
+ SetNextFireTime(now, interval_to_next_fire_time);
+ } else {
+ next_fire_time_ = TimeTicks();
+ }
+ Fired();
+}
+
+bool TimerBase::Comparator::operator()(const TimerBase* a,
+ const TimerBase* b) const {
+ return a->next_fire_time_ < b->next_fire_time_;
+}
+
+// static
+TimeTicks TimerBase::TimerCurrentTimeTicks() const {
+ return WTF::TimeTicks(Platform::Current()
+ ->CurrentThread()
+ ->Scheduler()
+ ->MonotonicallyIncreasingVirtualTime());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/timer.h b/chromium/third_party/blink/renderer/platform/timer.h
new file mode 100644
index 00000000000..ede8fc7b73d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/timer.h
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2006 Apple Computer, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_TIMER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TIMER_H_
+
+#include "base/location.h"
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
+#include "base/time/time.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/address_sanitizer.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/threading.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+// Time intervals are all in seconds.
+
+class PLATFORM_EXPORT TimerBase {
+ WTF_MAKE_NONCOPYABLE(TimerBase);
+
+ public:
+ explicit TimerBase(scoped_refptr<base::SingleThreadTaskRunner>);
+ virtual ~TimerBase();
+
+ void Start(TimeDelta next_fire_interval,
+ TimeDelta repeat_interval,
+ const base::Location&);
+ void Start(double next_fire_interval,
+ double repeat_interval,
+ const base::Location& from_here) {
+ Start(TimeDelta::FromSecondsD(next_fire_interval),
+ TimeDelta::FromSecondsD(repeat_interval), from_here);
+ }
+
+ void StartRepeating(TimeDelta repeat_interval, const base::Location& caller) {
+ Start(repeat_interval, repeat_interval, caller);
+ }
+
+ void StartOneShot(TimeDelta interval, const base::Location& caller) {
+ Start(interval, TimeDelta(), caller);
+ }
+ void StartOneShot(double interval, const base::Location& caller) {
+ StartOneShot(TimeDelta::FromSecondsD(interval), caller);
+ }
+
+ // Timer cancellation is fast enough that you shouldn't have to worry
+ // about it unless you're canceling tens of thousands of tasks.
+ virtual void Stop();
+ bool IsActive() const;
+ const base::Location& GetLocation() const { return location_; }
+
+ TimeDelta NextFireIntervalDelta() const;
+ double NextFireInterval() const {
+ return NextFireIntervalDelta().InSecondsF();
+ }
+
+ TimeDelta RepeatIntervalDelta() const { return repeat_interval_; }
+ double RepeatInterval() const { return RepeatIntervalDelta().InSecondsF(); }
+
+ void AugmentRepeatInterval(TimeDelta delta) {
+ TimeTicks now = TimerCurrentTimeTicks();
+ SetNextFireTime(now, std::max(next_fire_time_ - now + delta, TimeDelta()));
+ repeat_interval_ += delta;
+ }
+ void AugmentRepeatInterval(double delta) {
+ AugmentRepeatInterval(TimeDelta::FromSecondsD(delta));
+ }
+
+ void MoveToNewTaskRunner(scoped_refptr<base::SingleThreadTaskRunner>);
+
+ struct PLATFORM_EXPORT Comparator {
+ bool operator()(const TimerBase* a, const TimerBase* b) const;
+ };
+
+ private:
+ virtual void Fired() = 0;
+
+ virtual scoped_refptr<base::SingleThreadTaskRunner> TimerTaskRunner() const;
+
+ NO_SANITIZE_ADDRESS
+ virtual bool CanFire() const { return true; }
+
+ TimeTicks TimerCurrentTimeTicks() const;
+
+ void SetNextFireTime(TimeTicks now, TimeDelta delay);
+
+ void RunInternal();
+
+ TimeTicks next_fire_time_; // 0 if inactive
+ TimeDelta repeat_interval_; // 0 if not repeating
+ base::Location location_;
+ scoped_refptr<base::SingleThreadTaskRunner> web_task_runner_;
+
+#if DCHECK_IS_ON()
+ ThreadIdentifier thread_;
+#endif
+ base::WeakPtrFactory<TimerBase> weak_ptr_factory_;
+
+ friend class ThreadTimers;
+ friend class TimerHeapLessThanFunction;
+ friend class TimerHeapReference;
+};
+
+template <typename T, bool = IsGarbageCollectedType<T>::value>
+class TimerIsObjectAliveTrait {
+ public:
+ static bool IsHeapObjectAlive(T*) { return true; }
+};
+
+template <typename T>
+class TimerIsObjectAliveTrait<T, true> {
+ public:
+ static bool IsHeapObjectAlive(T* object_pointer) {
+ return !ThreadHeap::WillObjectBeLazilySwept(object_pointer);
+ }
+};
+
+template <typename TimerFiredClass>
+class TaskRunnerTimer : public TimerBase {
+ public:
+ using TimerFiredFunction = void (TimerFiredClass::*)(TimerBase*);
+
+ TaskRunnerTimer(scoped_refptr<base::SingleThreadTaskRunner> web_task_runner,
+ TimerFiredClass* o,
+ TimerFiredFunction f)
+ : TimerBase(std::move(web_task_runner)), object_(o), function_(f) {}
+
+ ~TaskRunnerTimer() override = default;
+
+ protected:
+ void Fired() override { (object_->*function_)(this); }
+
+ NO_SANITIZE_ADDRESS
+ bool CanFire() const override {
+ // Oilpan: if a timer fires while Oilpan heaps are being lazily
+ // swept, it is not safe to proceed if the object is about to
+ // be swept (and this timer will be stopped while doing so.)
+ return TimerIsObjectAliveTrait<TimerFiredClass>::IsHeapObjectAlive(object_);
+ }
+
+ private:
+ // FIXME: Oilpan: TimerBase should be moved to the heap and m_object should be
+ // traced. This raw pointer is safe as long as Timer<X> is held by the X
+ // itself (That's the case
+ // in the current code base).
+ GC_PLUGIN_IGNORE("363031")
+ TimerFiredClass* object_;
+ TimerFiredFunction function_;
+};
+
+NO_SANITIZE_ADDRESS
+inline bool TimerBase::IsActive() const {
+#if DCHECK_IS_ON()
+ DCHECK_EQ(thread_, CurrentThread());
+#endif
+ return weak_ptr_factory_.HasWeakPtrs();
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TIMER_H_
diff --git a/chromium/third_party/blink/renderer/platform/timer_perf_test.cc b/chromium/third_party/blink/renderer/platform/timer_perf_test.cc
new file mode 100644
index 00000000000..e5feb2813b1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/timer_perf_test.cc
@@ -0,0 +1,117 @@
+
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/timer.h"
+
+#include "base/run_loop.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
+#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class TimerPerfTest : public testing::Test {
+ public:
+ void NopTask(TimerBase*) {}
+
+ void RecordStartRunTime(TimerBase*) { run_start_ = base::ThreadTicks::Now(); }
+
+ void RecordEndRunTime(TimerBase*) {
+ run_end_ = base::ThreadTicks::Now();
+ base::RunLoop::QuitCurrentDeprecated();
+ }
+
+ base::ThreadTicks run_start_;
+ base::ThreadTicks run_end_;
+};
+
+TEST_F(TimerPerfTest, PostAndRunTimers) {
+ const int kNumIterations = 10000;
+ Vector<std::unique_ptr<TaskRunnerTimer<TimerPerfTest>>> timers(
+ kNumIterations);
+ for (int i = 0; i < kNumIterations; i++) {
+ timers[i].reset(new TaskRunnerTimer<TimerPerfTest>(
+ scheduler::GetSingleThreadTaskRunnerForTesting(), this,
+ &TimerPerfTest::NopTask));
+ }
+
+ TaskRunnerTimer<TimerPerfTest> measure_run_start(
+ scheduler::GetSingleThreadTaskRunnerForTesting(), this,
+ &TimerPerfTest::RecordStartRunTime);
+ TaskRunnerTimer<TimerPerfTest> measure_run_end(
+ scheduler::GetSingleThreadTaskRunnerForTesting(), this,
+ &TimerPerfTest::RecordEndRunTime);
+
+ measure_run_start.StartOneShot(TimeDelta(), FROM_HERE);
+ base::ThreadTicks post_start = base::ThreadTicks::Now();
+ for (int i = 0; i < kNumIterations; i++) {
+ timers[i]->StartOneShot(TimeDelta(), FROM_HERE);
+ }
+ base::ThreadTicks post_end = base::ThreadTicks::Now();
+ measure_run_end.StartOneShot(TimeDelta(), FROM_HERE);
+
+ test::EnterRunLoop();
+
+ double posting_time = (post_end - post_start).InMicroseconds();
+ double posting_time_us_per_call =
+ posting_time / static_cast<double>(kNumIterations);
+ LOG(INFO) << "TimerBase::startOneShot cost (us/call) "
+ << posting_time_us_per_call << " (total " << posting_time << " us)";
+ LOG(INFO) << "Time to run " << kNumIterations << " trivial tasks (us) "
+ << (run_end_ - run_start_).InMicroseconds();
+}
+
+TEST_F(TimerPerfTest, PostThenCancelTenThousandTimers) {
+ const int kNumIterations = 10000;
+ Vector<std::unique_ptr<TaskRunnerTimer<TimerPerfTest>>> timers(
+ kNumIterations);
+ for (int i = 0; i < kNumIterations; i++) {
+ timers[i].reset(new TaskRunnerTimer<TimerPerfTest>(
+ scheduler::GetSingleThreadTaskRunnerForTesting(), this,
+ &TimerPerfTest::NopTask));
+ }
+
+ TaskRunnerTimer<TimerPerfTest> measure_run_start(
+ scheduler::GetSingleThreadTaskRunnerForTesting(), this,
+ &TimerPerfTest::RecordStartRunTime);
+ TaskRunnerTimer<TimerPerfTest> measure_run_end(
+ scheduler::GetSingleThreadTaskRunnerForTesting(), this,
+ &TimerPerfTest::RecordEndRunTime);
+
+ measure_run_start.StartOneShot(TimeDelta(), FROM_HERE);
+ base::ThreadTicks post_start = base::ThreadTicks::Now();
+ for (int i = 0; i < kNumIterations; i++) {
+ timers[i]->StartOneShot(TimeDelta(), FROM_HERE);
+ }
+ base::ThreadTicks post_end = base::ThreadTicks::Now();
+ measure_run_end.StartOneShot(TimeDelta(), FROM_HERE);
+
+ base::ThreadTicks cancel_start = base::ThreadTicks::Now();
+ for (int i = 0; i < kNumIterations; i++) {
+ timers[i]->Stop();
+ }
+ base::ThreadTicks cancel_end = base::ThreadTicks::Now();
+
+ test::EnterRunLoop();
+
+ double posting_time = (post_end - post_start).InMicroseconds();
+ double posting_time_us_per_call =
+ posting_time / static_cast<double>(kNumIterations);
+ LOG(INFO) << "TimerBase::startOneShot cost (us/call) "
+ << posting_time_us_per_call << " (total " << posting_time << " us)";
+
+ double cancel_time = (cancel_end - cancel_start).InMicroseconds();
+ double cancel_time_us_per_call =
+ cancel_time / static_cast<double>(kNumIterations);
+ LOG(INFO) << "TimerBase::stop cost (us/call) " << cancel_time_us_per_call
+ << " (total " << cancel_time << " us)";
+ LOG(INFO) << "Time to run " << kNumIterations << " canceled tasks (us) "
+ << (run_end_ - run_start_).InMicroseconds();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/timer_test.cc b/chromium/third_party/blink/renderer/platform/timer_test.cc
new file mode 100644
index 00000000000..4e5460c7be6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/timer_test.cc
@@ -0,0 +1,806 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/timer.h"
+
+#include <memory>
+#include <queue>
+#include "base/message_loop/message_loop.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_thread.h"
+#include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/child/task_runner_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/child/web_scheduler.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/renderer/main_thread_task_queue.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+using testing::ElementsAre;
+
+namespace blink {
+namespace {
+
+class TimerTest : public testing::Test {
+ public:
+ void SetUp() override {
+ run_times_.clear();
+ platform_->AdvanceClockSeconds(10.0);
+ start_time_ = CurrentTimeTicksInSeconds();
+ }
+
+ void CountingTask(TimerBase*) {
+ run_times_.push_back(CurrentTimeTicksInSeconds());
+ }
+
+ void RecordNextFireTimeTask(TimerBase* timer) {
+ next_fire_times_.push_back(CurrentTimeTicksInSeconds() +
+ timer->NextFireInterval());
+ }
+
+ void RunUntilDeadline(double deadline) {
+ double period = deadline - CurrentTimeTicksInSeconds();
+ EXPECT_GE(period, 0.0);
+ platform_->RunForPeriodSeconds(period);
+ }
+
+ // Returns false if there are no pending delayed tasks, otherwise sets |time|
+ // to the delay in seconds till the next pending delayed task is scheduled to
+ // fire.
+ bool TimeTillNextDelayedTask(double* time) const {
+ base::TimeTicks next_run_time;
+ if (!platform_->GetMainThreadScheduler()
+ ->DefaultTaskQueue()
+ ->GetTimeDomain()
+ ->NextScheduledRunTime(&next_run_time))
+ return false;
+ *time = (next_run_time - platform_->GetMainThreadScheduler()
+ ->DefaultTaskQueue()
+ ->GetTimeDomain()
+ ->Now())
+ .InSecondsF();
+ return true;
+ }
+
+ protected:
+ double start_time_;
+ WTF::Vector<double> run_times_;
+ WTF::Vector<double> next_fire_times_;
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform_;
+
+ base::MessageLoop message_loop_;
+};
+
+class OnHeapTimerOwner final
+ : public GarbageCollectedFinalized<OnHeapTimerOwner> {
+ public:
+ class Record final : public RefCounted<Record> {
+ public:
+ static scoped_refptr<Record> Create() { return base::AdoptRef(new Record); }
+
+ bool TimerHasFired() const { return timer_has_fired_; }
+ bool IsDisposed() const { return is_disposed_; }
+ bool OwnerIsDestructed() const { return owner_is_destructed_; }
+ void SetTimerHasFired() { timer_has_fired_ = true; }
+ void Dispose() { is_disposed_ = true; }
+ void SetOwnerIsDestructed() { owner_is_destructed_ = true; }
+
+ private:
+ Record() = default;
+
+ bool timer_has_fired_ = false;
+ bool is_disposed_ = false;
+ bool owner_is_destructed_ = false;
+ };
+
+ explicit OnHeapTimerOwner(
+ scoped_refptr<Record> record,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+ : timer_(std::move(task_runner), this, &OnHeapTimerOwner::Fired),
+ record_(std::move(record)) {}
+ ~OnHeapTimerOwner() { record_->SetOwnerIsDestructed(); }
+
+ void StartOneShot(TimeDelta interval, const base::Location& caller) {
+ timer_.StartOneShot(interval, caller);
+ }
+
+ void Trace(blink::Visitor* visitor) {}
+
+ private:
+ void Fired(TimerBase*) {
+ EXPECT_FALSE(record_->IsDisposed());
+ record_->SetTimerHasFired();
+ }
+
+ TaskRunnerTimer<OnHeapTimerOwner> timer_;
+ scoped_refptr<Record> record_;
+};
+
+class GCForbiddenScope final {
+ public:
+ STACK_ALLOCATED();
+ GCForbiddenScope() { ThreadState::Current()->EnterGCForbiddenScope(); }
+ ~GCForbiddenScope() { ThreadState::Current()->LeaveGCForbiddenScope(); }
+};
+
+TEST_F(TimerTest, StartOneShot_Zero) {
+ TaskRunnerTimer<TimerTest> timer(
+ platform_->GetMainThreadScheduler()->DefaultTaskQueue(), this,
+ &TimerTest::CountingTask);
+ timer.StartOneShot(TimeDelta(), FROM_HERE);
+
+ double run_time;
+ EXPECT_FALSE(TimeTillNextDelayedTask(&run_time));
+
+ platform_->RunUntilIdle();
+ EXPECT_THAT(run_times_, ElementsAre(start_time_));
+}
+
+TEST_F(TimerTest, StartOneShot_ZeroAndCancel) {
+ TaskRunnerTimer<TimerTest> timer(
+ platform_->GetMainThreadScheduler()->DefaultTaskQueue(), this,
+ &TimerTest::CountingTask);
+ timer.StartOneShot(TimeDelta(), FROM_HERE);
+
+ double run_time;
+ EXPECT_FALSE(TimeTillNextDelayedTask(&run_time));
+
+ timer.Stop();
+
+ platform_->RunUntilIdle();
+ EXPECT_FALSE(run_times_.size());
+}
+
+TEST_F(TimerTest, StartOneShot_ZeroAndCancelThenRepost) {
+ TaskRunnerTimer<TimerTest> timer(
+ platform_->GetMainThreadScheduler()->DefaultTaskQueue(), this,
+ &TimerTest::CountingTask);
+ timer.StartOneShot(TimeDelta(), FROM_HERE);
+
+ double run_time;
+ EXPECT_FALSE(TimeTillNextDelayedTask(&run_time));
+
+ timer.Stop();
+
+ platform_->RunUntilIdle();
+ EXPECT_FALSE(run_times_.size());
+
+ timer.StartOneShot(TimeDelta(), FROM_HERE);
+
+ EXPECT_FALSE(TimeTillNextDelayedTask(&run_time));
+
+ platform_->RunUntilIdle();
+ EXPECT_THAT(run_times_, ElementsAre(start_time_));
+}
+
+TEST_F(TimerTest, StartOneShot_Zero_RepostingAfterRunning) {
+ TaskRunnerTimer<TimerTest> timer(
+ platform_->GetMainThreadScheduler()->DefaultTaskQueue(), this,
+ &TimerTest::CountingTask);
+ timer.StartOneShot(TimeDelta(), FROM_HERE);
+
+ double run_time;
+ EXPECT_FALSE(TimeTillNextDelayedTask(&run_time));
+
+ platform_->RunUntilIdle();
+ EXPECT_THAT(run_times_, ElementsAre(start_time_));
+
+ timer.StartOneShot(TimeDelta(), FROM_HERE);
+
+ EXPECT_FALSE(TimeTillNextDelayedTask(&run_time));
+
+ platform_->RunUntilIdle();
+ EXPECT_THAT(run_times_, ElementsAre(start_time_, start_time_));
+}
+
+TEST_F(TimerTest, StartOneShot_NonZero) {
+ TaskRunnerTimer<TimerTest> timer(
+ platform_->GetMainThreadScheduler()->DefaultTaskQueue(), this,
+ &TimerTest::CountingTask);
+ timer.StartOneShot(TimeDelta::FromSeconds(10), FROM_HERE);
+
+ double run_time;
+ EXPECT_TRUE(TimeTillNextDelayedTask(&run_time));
+ EXPECT_FLOAT_EQ(10.0, run_time);
+
+ platform_->RunUntilIdle();
+ EXPECT_THAT(run_times_, ElementsAre(start_time_ + 10.0));
+}
+
+TEST_F(TimerTest, StartOneShot_NonZeroAndCancel) {
+ TaskRunnerTimer<TimerTest> timer(
+ platform_->GetMainThreadScheduler()->DefaultTaskQueue(), this,
+ &TimerTest::CountingTask);
+ timer.StartOneShot(TimeDelta::FromSeconds(10), FROM_HERE);
+
+ double run_time;
+ EXPECT_TRUE(TimeTillNextDelayedTask(&run_time));
+ EXPECT_FLOAT_EQ(10.0, run_time);
+
+ timer.Stop();
+ EXPECT_TRUE(TimeTillNextDelayedTask(&run_time));
+
+ platform_->RunUntilIdle();
+ EXPECT_FALSE(run_times_.size());
+}
+
+TEST_F(TimerTest, StartOneShot_NonZeroAndCancelThenRepost) {
+ TaskRunnerTimer<TimerTest> timer(
+ platform_->GetMainThreadScheduler()->DefaultTaskQueue(), this,
+ &TimerTest::CountingTask);
+ timer.StartOneShot(TimeDelta::FromSeconds(10), FROM_HERE);
+
+ double run_time;
+ EXPECT_TRUE(TimeTillNextDelayedTask(&run_time));
+ EXPECT_FLOAT_EQ(10.0, run_time);
+
+ timer.Stop();
+ EXPECT_TRUE(TimeTillNextDelayedTask(&run_time));
+
+ platform_->RunUntilIdle();
+ EXPECT_FALSE(run_times_.size());
+
+ double second_post_time = CurrentTimeTicksInSeconds();
+ timer.StartOneShot(TimeDelta::FromSeconds(10), FROM_HERE);
+
+ EXPECT_TRUE(TimeTillNextDelayedTask(&run_time));
+ EXPECT_FLOAT_EQ(10.0, run_time);
+
+ platform_->RunUntilIdle();
+ EXPECT_THAT(run_times_, ElementsAre(second_post_time + 10.0));
+}
+
+TEST_F(TimerTest, StartOneShot_NonZero_RepostingAfterRunning) {
+ TaskRunnerTimer<TimerTest> timer(
+ platform_->GetMainThreadScheduler()->DefaultTaskQueue(), this,
+ &TimerTest::CountingTask);
+ timer.StartOneShot(TimeDelta::FromSeconds(10), FROM_HERE);
+
+ double run_time;
+ EXPECT_TRUE(TimeTillNextDelayedTask(&run_time));
+ EXPECT_FLOAT_EQ(10.0, run_time);
+
+ platform_->RunUntilIdle();
+ EXPECT_THAT(run_times_, ElementsAre(start_time_ + 10.0));
+
+ timer.StartOneShot(TimeDelta::FromSeconds(20), FROM_HERE);
+
+ EXPECT_TRUE(TimeTillNextDelayedTask(&run_time));
+ EXPECT_FLOAT_EQ(20.0, run_time);
+
+ platform_->RunUntilIdle();
+ EXPECT_THAT(run_times_, ElementsAre(start_time_ + 10.0, start_time_ + 30.0));
+}
+
+TEST_F(TimerTest, PostingTimerTwiceWithSameRunTimeDoesNothing) {
+ TaskRunnerTimer<TimerTest> timer(
+ platform_->GetMainThreadScheduler()->DefaultTaskQueue(), this,
+ &TimerTest::CountingTask);
+ timer.StartOneShot(TimeDelta::FromSeconds(10), FROM_HERE);
+ timer.StartOneShot(TimeDelta::FromSeconds(10), FROM_HERE);
+
+ double run_time;
+ EXPECT_TRUE(TimeTillNextDelayedTask(&run_time));
+ EXPECT_FLOAT_EQ(10.0, run_time);
+
+ platform_->RunUntilIdle();
+ EXPECT_THAT(run_times_, ElementsAre(start_time_ + 10.0));
+}
+
+TEST_F(TimerTest, PostingTimerTwiceWithNewerRunTimeCancelsOriginalTask) {
+ TaskRunnerTimer<TimerTest> timer(
+ platform_->GetMainThreadScheduler()->DefaultTaskQueue(), this,
+ &TimerTest::CountingTask);
+ timer.StartOneShot(TimeDelta::FromSeconds(10), FROM_HERE);
+ timer.StartOneShot(TimeDelta(), FROM_HERE);
+
+ platform_->RunUntilIdle();
+ EXPECT_THAT(run_times_, ElementsAre(start_time_ + 0.0));
+}
+
+TEST_F(TimerTest, PostingTimerTwiceWithLaterRunTimeCancelsOriginalTask) {
+ TaskRunnerTimer<TimerTest> timer(
+ platform_->GetMainThreadScheduler()->DefaultTaskQueue(), this,
+ &TimerTest::CountingTask);
+ timer.StartOneShot(TimeDelta(), FROM_HERE);
+ timer.StartOneShot(TimeDelta::FromSeconds(10), FROM_HERE);
+
+ platform_->RunUntilIdle();
+ EXPECT_THAT(run_times_, ElementsAre(start_time_ + 10.0));
+}
+
+TEST_F(TimerTest, StartRepeatingTask) {
+ TaskRunnerTimer<TimerTest> timer(
+ platform_->GetMainThreadScheduler()->DefaultTaskQueue(), this,
+ &TimerTest::CountingTask);
+ timer.StartRepeating(TimeDelta::FromSeconds(1), FROM_HERE);
+
+ double run_time;
+ EXPECT_TRUE(TimeTillNextDelayedTask(&run_time));
+ EXPECT_FLOAT_EQ(1.0, run_time);
+
+ RunUntilDeadline(start_time_ + 5.5);
+ EXPECT_THAT(run_times_, ElementsAre(start_time_ + 1.0, start_time_ + 2.0,
+ start_time_ + 3.0, start_time_ + 4.0,
+ start_time_ + 5.0));
+}
+
+TEST_F(TimerTest, StartRepeatingTask_ThenCancel) {
+ TaskRunnerTimer<TimerTest> timer(
+ platform_->GetMainThreadScheduler()->DefaultTaskQueue(), this,
+ &TimerTest::CountingTask);
+ timer.StartRepeating(TimeDelta::FromSeconds(1), FROM_HERE);
+
+ double run_time;
+ EXPECT_TRUE(TimeTillNextDelayedTask(&run_time));
+ EXPECT_FLOAT_EQ(1.0, run_time);
+
+ RunUntilDeadline(start_time_ + 2.5);
+ EXPECT_THAT(run_times_, ElementsAre(start_time_ + 1.0, start_time_ + 2.0));
+
+ timer.Stop();
+ platform_->RunUntilIdle();
+
+ EXPECT_THAT(run_times_, ElementsAre(start_time_ + 1.0, start_time_ + 2.0));
+}
+
+TEST_F(TimerTest, StartRepeatingTask_ThenPostOneShot) {
+ TaskRunnerTimer<TimerTest> timer(
+ platform_->GetMainThreadScheduler()->DefaultTaskQueue(), this,
+ &TimerTest::CountingTask);
+ timer.StartRepeating(TimeDelta::FromSeconds(1), FROM_HERE);
+
+ double run_time;
+ EXPECT_TRUE(TimeTillNextDelayedTask(&run_time));
+ EXPECT_FLOAT_EQ(1.0, run_time);
+
+ RunUntilDeadline(start_time_ + 2.5);
+ EXPECT_THAT(run_times_, ElementsAre(start_time_ + 1.0, start_time_ + 2.0));
+
+ timer.StartOneShot(TimeDelta(), FROM_HERE);
+ platform_->RunUntilIdle();
+
+ EXPECT_THAT(run_times_, ElementsAre(start_time_ + 1.0, start_time_ + 2.0,
+ start_time_ + 2.5));
+}
+
+TEST_F(TimerTest, IsActive_NeverPosted) {
+ TaskRunnerTimer<TimerTest> timer(
+ platform_->GetMainThreadScheduler()->DefaultTaskQueue(), this,
+ &TimerTest::CountingTask);
+
+ EXPECT_FALSE(timer.IsActive());
+}
+
+TEST_F(TimerTest, IsActive_AfterPosting_OneShotZero) {
+ TaskRunnerTimer<TimerTest> timer(
+ platform_->GetMainThreadScheduler()->DefaultTaskQueue(), this,
+ &TimerTest::CountingTask);
+ timer.StartOneShot(TimeDelta(), FROM_HERE);
+
+ EXPECT_TRUE(timer.IsActive());
+}
+
+TEST_F(TimerTest, IsActive_AfterPosting_OneShotNonZero) {
+ TaskRunnerTimer<TimerTest> timer(
+ platform_->GetMainThreadScheduler()->DefaultTaskQueue(), this,
+ &TimerTest::CountingTask);
+ timer.StartOneShot(TimeDelta::FromSeconds(10), FROM_HERE);
+
+ EXPECT_TRUE(timer.IsActive());
+}
+
+TEST_F(TimerTest, IsActive_AfterPosting_Repeating) {
+ TaskRunnerTimer<TimerTest> timer(
+ platform_->GetMainThreadScheduler()->DefaultTaskQueue(), this,
+ &TimerTest::CountingTask);
+ timer.StartRepeating(TimeDelta::FromSeconds(1), FROM_HERE);
+
+ EXPECT_TRUE(timer.IsActive());
+}
+
+TEST_F(TimerTest, IsActive_AfterRunning_OneShotZero) {
+ TaskRunnerTimer<TimerTest> timer(
+ platform_->GetMainThreadScheduler()->DefaultTaskQueue(), this,
+ &TimerTest::CountingTask);
+ timer.StartOneShot(TimeDelta(), FROM_HERE);
+
+ platform_->RunUntilIdle();
+ EXPECT_FALSE(timer.IsActive());
+}
+
+TEST_F(TimerTest, IsActive_AfterRunning_OneShotNonZero) {
+ TaskRunnerTimer<TimerTest> timer(
+ platform_->GetMainThreadScheduler()->DefaultTaskQueue(), this,
+ &TimerTest::CountingTask);
+ timer.StartOneShot(TimeDelta::FromSeconds(10), FROM_HERE);
+
+ platform_->RunUntilIdle();
+ EXPECT_FALSE(timer.IsActive());
+}
+
+TEST_F(TimerTest, IsActive_AfterRunning_Repeating) {
+ TaskRunnerTimer<TimerTest> timer(
+ platform_->GetMainThreadScheduler()->DefaultTaskQueue(), this,
+ &TimerTest::CountingTask);
+ timer.StartRepeating(TimeDelta::FromSeconds(1), FROM_HERE);
+
+ RunUntilDeadline(start_time_ + 10);
+ EXPECT_TRUE(timer.IsActive()); // It should run until cancelled.
+}
+
+TEST_F(TimerTest, NextFireInterval_OneShotZero) {
+ TaskRunnerTimer<TimerTest> timer(
+ platform_->GetMainThreadScheduler()->DefaultTaskQueue(), this,
+ &TimerTest::CountingTask);
+ timer.StartOneShot(TimeDelta(), FROM_HERE);
+
+ EXPECT_FLOAT_EQ(0.0, timer.NextFireInterval());
+}
+
+TEST_F(TimerTest, NextFireInterval_OneShotNonZero) {
+ TaskRunnerTimer<TimerTest> timer(
+ platform_->GetMainThreadScheduler()->DefaultTaskQueue(), this,
+ &TimerTest::CountingTask);
+ timer.StartOneShot(TimeDelta::FromSeconds(10), FROM_HERE);
+
+ EXPECT_FLOAT_EQ(10.0, timer.NextFireInterval());
+}
+
+TEST_F(TimerTest, NextFireInterval_OneShotNonZero_AfterAFewSeconds) {
+ platform_->SetAutoAdvanceNowToPendingTasks(false);
+
+ TaskRunnerTimer<TimerTest> timer(
+ platform_->GetMainThreadScheduler()->DefaultTaskQueue(), this,
+ &TimerTest::CountingTask);
+ timer.StartOneShot(TimeDelta::FromSeconds(10), FROM_HERE);
+
+ platform_->AdvanceClockSeconds(2.0);
+ EXPECT_FLOAT_EQ(8.0, timer.NextFireInterval());
+}
+
+TEST_F(TimerTest, NextFireInterval_Repeating) {
+ TaskRunnerTimer<TimerTest> timer(
+ platform_->GetMainThreadScheduler()->DefaultTaskQueue(), this,
+ &TimerTest::CountingTask);
+ timer.StartRepeating(TimeDelta::FromSeconds(20), FROM_HERE);
+
+ EXPECT_FLOAT_EQ(20.0, timer.NextFireInterval());
+}
+
+TEST_F(TimerTest, RepeatInterval_NeverStarted) {
+ TaskRunnerTimer<TimerTest> timer(
+ platform_->GetMainThreadScheduler()->DefaultTaskQueue(), this,
+ &TimerTest::CountingTask);
+
+ EXPECT_FLOAT_EQ(0.0, timer.RepeatInterval());
+}
+
+TEST_F(TimerTest, RepeatInterval_OneShotZero) {
+ TaskRunnerTimer<TimerTest> timer(
+ platform_->GetMainThreadScheduler()->DefaultTaskQueue(), this,
+ &TimerTest::CountingTask);
+ timer.StartOneShot(TimeDelta(), FROM_HERE);
+
+ EXPECT_FLOAT_EQ(0.0, timer.RepeatInterval());
+}
+
+TEST_F(TimerTest, RepeatInterval_OneShotNonZero) {
+ TaskRunnerTimer<TimerTest> timer(
+ platform_->GetMainThreadScheduler()->DefaultTaskQueue(), this,
+ &TimerTest::CountingTask);
+ timer.StartOneShot(TimeDelta::FromSeconds(10), FROM_HERE);
+
+ EXPECT_FLOAT_EQ(0.0, timer.RepeatInterval());
+}
+
+TEST_F(TimerTest, RepeatInterval_Repeating) {
+ TaskRunnerTimer<TimerTest> timer(
+ platform_->GetMainThreadScheduler()->DefaultTaskQueue(), this,
+ &TimerTest::CountingTask);
+ timer.StartRepeating(TimeDelta::FromSeconds(20), FROM_HERE);
+
+ EXPECT_FLOAT_EQ(20.0, timer.RepeatInterval());
+}
+
+TEST_F(TimerTest, AugmentRepeatInterval) {
+ TaskRunnerTimer<TimerTest> timer(
+ platform_->GetMainThreadScheduler()->DefaultTaskQueue(), this,
+ &TimerTest::CountingTask);
+ timer.StartRepeating(TimeDelta::FromSeconds(10), FROM_HERE);
+ EXPECT_FLOAT_EQ(10.0, timer.RepeatInterval());
+ EXPECT_FLOAT_EQ(10.0, timer.NextFireInterval());
+
+ platform_->AdvanceClockSeconds(2.0);
+ timer.AugmentRepeatInterval(10);
+
+ EXPECT_FLOAT_EQ(20.0, timer.RepeatInterval());
+ EXPECT_FLOAT_EQ(18.0, timer.NextFireInterval());
+
+ // NOTE setAutoAdvanceNowToPendingTasks(true) (which uses
+ // cc::OrderedSimpleTaskRunner) results in somewhat strange behavior of the
+ // test clock which breaks this test. Specifically the test clock advancing
+ // logic ignores newly posted delayed tasks and advances too far.
+ RunUntilDeadline(start_time_ + 50.0);
+ EXPECT_THAT(run_times_, ElementsAre(start_time_ + 20.0, start_time_ + 40.0));
+}
+
+TEST_F(TimerTest, AugmentRepeatInterval_TimerFireDelayed) {
+ platform_->SetAutoAdvanceNowToPendingTasks(false);
+
+ TaskRunnerTimer<TimerTest> timer(
+ platform_->GetMainThreadScheduler()->DefaultTaskQueue(), this,
+ &TimerTest::CountingTask);
+ timer.StartRepeating(TimeDelta::FromSeconds(10), FROM_HERE);
+ EXPECT_FLOAT_EQ(10.0, timer.RepeatInterval());
+ EXPECT_FLOAT_EQ(10.0, timer.NextFireInterval());
+
+ platform_->AdvanceClockSeconds(123.0); // Make the timer long overdue.
+ timer.AugmentRepeatInterval(10);
+
+ EXPECT_FLOAT_EQ(20.0, timer.RepeatInterval());
+ // The timer is overdue so it should be scheduled to fire immediatly.
+ EXPECT_FLOAT_EQ(0.0, timer.NextFireInterval());
+}
+
+TEST_F(TimerTest, RepeatingTimerDoesNotDrift) {
+ platform_->SetAutoAdvanceNowToPendingTasks(false);
+
+ TaskRunnerTimer<TimerTest> timer(
+ platform_->GetMainThreadScheduler()->DefaultTaskQueue(), this,
+ &TimerTest::RecordNextFireTimeTask);
+ timer.StartRepeating(TimeDelta::FromSeconds(2), FROM_HERE);
+
+ RecordNextFireTimeTask(
+ &timer); // Next scheduled task to run at m_startTime + 2.0
+
+ // Simulate timer firing early. Next scheduled task to run at
+ // m_startTime + 4.0
+ platform_->AdvanceClockSeconds(1.9);
+ RunUntilDeadline(CurrentTimeTicksInSeconds() + 0.2);
+
+ // Next scheduled task to run at m_startTime + 6.0
+ platform_->RunForPeriodSeconds(2.0);
+ // Next scheduled task to run at m_startTime + 8.0
+ platform_->RunForPeriodSeconds(2.1);
+ // Next scheduled task to run at m_startTime + 10.0
+ platform_->RunForPeriodSeconds(2.9);
+ // Next scheduled task to run at m_startTime + 14.0 (skips a beat)
+ platform_->AdvanceClockSeconds(3.1);
+ platform_->RunUntilIdle();
+ // Next scheduled task to run at m_startTime + 18.0 (skips a beat)
+ platform_->AdvanceClockSeconds(4.0);
+ platform_->RunUntilIdle();
+ // Next scheduled task to run at m_startTime + 28.0 (skips 5 beats)
+ platform_->AdvanceClockSeconds(10.0);
+ platform_->RunUntilIdle();
+
+ EXPECT_THAT(
+ next_fire_times_,
+ ElementsAre(start_time_ + 2.0, start_time_ + 4.0, start_time_ + 6.0,
+ start_time_ + 8.0, start_time_ + 10.0, start_time_ + 14.0,
+ start_time_ + 18.0, start_time_ + 28.0));
+}
+
+template <typename TimerFiredClass>
+class TimerForTest : public TaskRunnerTimer<TimerFiredClass> {
+ public:
+ using TimerFiredFunction =
+ typename TaskRunnerTimer<TimerFiredClass>::TimerFiredFunction;
+
+ ~TimerForTest() override = default;
+
+ TimerForTest(scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+ TimerFiredClass* timer_fired_class,
+ TimerFiredFunction timer_fired_function)
+ : TaskRunnerTimer<TimerFiredClass>(std::move(task_runner),
+ timer_fired_class,
+ timer_fired_function) {}
+};
+
+TEST_F(TimerTest, UserSuppliedTaskRunner) {
+ scoped_refptr<scheduler::TaskQueue> task_runner(
+ platform_->GetMainThreadScheduler()->NewTimerTaskQueue(
+ scheduler::MainThreadTaskQueue::QueueType::kFrameThrottleable));
+ scoped_refptr<scheduler::TaskRunnerImpl> task_runner_impl =
+ scheduler::TaskRunnerImpl::Create(task_runner, TaskType::kInternalTest);
+ TimerForTest<TimerTest> timer(task_runner_impl, this,
+ &TimerTest::CountingTask);
+ timer.StartOneShot(TimeDelta(), FROM_HERE);
+
+ // Make sure the task was posted on taskRunner.
+ EXPECT_FALSE(task_runner->IsEmpty());
+}
+
+TEST_F(TimerTest, RunOnHeapTimer) {
+ scoped_refptr<OnHeapTimerOwner::Record> record =
+ OnHeapTimerOwner::Record::Create();
+ Persistent<OnHeapTimerOwner> owner = new OnHeapTimerOwner(
+ record, platform_->GetMainThreadScheduler()->DefaultTaskQueue());
+
+ owner->StartOneShot(TimeDelta(), FROM_HERE);
+
+ EXPECT_FALSE(record->TimerHasFired());
+ platform_->RunUntilIdle();
+ EXPECT_TRUE(record->TimerHasFired());
+}
+
+TEST_F(TimerTest, DestructOnHeapTimer) {
+ scoped_refptr<OnHeapTimerOwner::Record> record =
+ OnHeapTimerOwner::Record::Create();
+ Persistent<OnHeapTimerOwner> owner = new OnHeapTimerOwner(
+ record, platform_->GetMainThreadScheduler()->DefaultTaskQueue());
+
+ record->Dispose();
+ owner->StartOneShot(TimeDelta(), FROM_HERE);
+
+ owner = nullptr;
+ ThreadState::Current()->CollectGarbage(
+ BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
+ BlinkGC::kEagerSweeping, BlinkGC::kForcedGC);
+ EXPECT_TRUE(record->OwnerIsDestructed());
+
+ EXPECT_FALSE(record->TimerHasFired());
+ platform_->RunUntilIdle();
+ EXPECT_FALSE(record->TimerHasFired());
+}
+
+TEST_F(TimerTest, MarkOnHeapTimerAsUnreachable) {
+ scoped_refptr<OnHeapTimerOwner::Record> record =
+ OnHeapTimerOwner::Record::Create();
+ Persistent<OnHeapTimerOwner> owner = new OnHeapTimerOwner(
+ record, platform_->GetMainThreadScheduler()->DefaultTaskQueue());
+
+ record->Dispose();
+ owner->StartOneShot(TimeDelta(), FROM_HERE);
+
+ owner = nullptr;
+ ThreadState::Current()->CollectGarbage(
+ BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
+ BlinkGC::kLazySweeping, BlinkGC::kForcedGC);
+ EXPECT_FALSE(record->OwnerIsDestructed());
+
+ {
+ GCForbiddenScope scope;
+ EXPECT_FALSE(record->TimerHasFired());
+ platform_->RunUntilIdle();
+ EXPECT_FALSE(record->TimerHasFired());
+ EXPECT_FALSE(record->OwnerIsDestructed());
+ }
+}
+
+namespace {
+
+class TaskObserver : public base::MessageLoop::TaskObserver {
+ public:
+ TaskObserver(
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+ std::vector<scoped_refptr<base::SingleThreadTaskRunner>>* run_order)
+ : task_runner_(std::move(task_runner)), run_order_(run_order) {}
+
+ void WillProcessTask(const base::PendingTask&) {}
+
+ void DidProcessTask(const base::PendingTask&) {
+ run_order_->push_back(task_runner_);
+ }
+
+ private:
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ std::vector<scoped_refptr<base::SingleThreadTaskRunner>>* run_order_;
+};
+
+} // namespace
+
+TEST_F(TimerTest, MoveToNewTaskRunnerOneShot) {
+ std::vector<scoped_refptr<base::SingleThreadTaskRunner>> run_order;
+
+ scoped_refptr<scheduler::TaskQueue> task_runner1(
+ platform_->GetMainThreadScheduler()->NewTimerTaskQueue(
+ scheduler::MainThreadTaskQueue::QueueType::kFrameThrottleable));
+ scoped_refptr<scheduler::TaskRunnerImpl> task_runner_impl1 =
+ scheduler::TaskRunnerImpl::Create(task_runner1, TaskType::kInternalTest);
+ TaskObserver task_observer1(task_runner_impl1, &run_order);
+ task_runner1->AddTaskObserver(&task_observer1);
+
+ scoped_refptr<scheduler::TaskQueue> task_runner2(
+ platform_->GetMainThreadScheduler()->NewTimerTaskQueue(
+ scheduler::MainThreadTaskQueue::QueueType::kFrameThrottleable));
+ scoped_refptr<scheduler::TaskRunnerImpl> task_runner_impl2 =
+ scheduler::TaskRunnerImpl::Create(task_runner2, TaskType::kInternalTest);
+ TaskObserver task_observer2(task_runner_impl2, &run_order);
+ task_runner2->AddTaskObserver(&task_observer2);
+
+ TimerForTest<TimerTest> timer(task_runner_impl1, this,
+ &TimerTest::CountingTask);
+
+ double start_time = CurrentTimeTicksInSeconds();
+
+ timer.StartOneShot(TimeDelta::FromSeconds(1), FROM_HERE);
+
+ platform_->RunForPeriodSeconds(0.5);
+
+ timer.MoveToNewTaskRunner(task_runner_impl2);
+
+ platform_->RunUntilIdle();
+
+ EXPECT_THAT(run_times_, ElementsAre(start_time + 1.0));
+
+ EXPECT_THAT(run_order, ElementsAre(task_runner_impl2));
+
+ EXPECT_TRUE(task_runner1->IsEmpty());
+ EXPECT_TRUE(task_runner2->IsEmpty());
+}
+
+TEST_F(TimerTest, MoveToNewTaskRunnerRepeating) {
+ std::vector<scoped_refptr<base::SingleThreadTaskRunner>> run_order;
+
+ scoped_refptr<scheduler::TaskQueue> task_runner1(
+ platform_->GetMainThreadScheduler()->NewTimerTaskQueue(
+ scheduler::MainThreadTaskQueue::QueueType::kFrameThrottleable));
+ scoped_refptr<scheduler::TaskRunnerImpl> task_runner_impl1 =
+ scheduler::TaskRunnerImpl::Create(task_runner1, TaskType::kInternalTest);
+ TaskObserver task_observer1(task_runner_impl1, &run_order);
+ task_runner1->AddTaskObserver(&task_observer1);
+
+ scoped_refptr<scheduler::TaskQueue> task_runner2(
+ platform_->GetMainThreadScheduler()->NewTimerTaskQueue(
+ scheduler::MainThreadTaskQueue::QueueType::kFrameThrottleable));
+ scoped_refptr<scheduler::TaskRunnerImpl> task_runner_impl2 =
+ scheduler::TaskRunnerImpl::Create(task_runner2, TaskType::kInternalTest);
+ TaskObserver task_observer2(task_runner_impl2, &run_order);
+ task_runner2->AddTaskObserver(&task_observer2);
+
+ TimerForTest<TimerTest> timer(task_runner_impl1, this,
+ &TimerTest::CountingTask);
+
+ double start_time = CurrentTimeTicksInSeconds();
+
+ timer.StartRepeating(TimeDelta::FromSeconds(1), FROM_HERE);
+
+ platform_->RunForPeriodSeconds(2.5);
+
+ timer.MoveToNewTaskRunner(task_runner_impl2);
+
+ platform_->RunForPeriodSeconds(2);
+
+ EXPECT_THAT(run_times_, ElementsAre(start_time + 1.0, start_time + 2.0,
+ start_time + 3.0, start_time + 4.0));
+
+ EXPECT_THAT(run_order, ElementsAre(task_runner_impl1, task_runner_impl1,
+ task_runner_impl2, task_runner_impl2));
+
+ EXPECT_TRUE(task_runner1->IsEmpty());
+ EXPECT_FALSE(task_runner2->IsEmpty());
+}
+
+// This test checks that when inactive timer is moved to a different task
+// runner it isn't activated.
+TEST_F(TimerTest, MoveToNewTaskRunnerWithoutTasks) {
+ scoped_refptr<scheduler::TaskQueue> task_runner1(
+ platform_->GetMainThreadScheduler()->NewTimerTaskQueue(
+ scheduler::MainThreadTaskQueue::QueueType::kFrameThrottleable));
+ scoped_refptr<scheduler::TaskRunnerImpl> task_runner_impl1 =
+ scheduler::TaskRunnerImpl::Create(task_runner1, TaskType::kInternalTest);
+
+ scoped_refptr<scheduler::TaskQueue> task_runner2(
+ platform_->GetMainThreadScheduler()->NewTimerTaskQueue(
+ scheduler::MainThreadTaskQueue::QueueType::kFrameThrottleable));
+ scoped_refptr<scheduler::TaskRunnerImpl> task_runner_impl2 =
+ scheduler::TaskRunnerImpl::Create(task_runner2, TaskType::kInternalTest);
+
+ TimerForTest<TimerTest> timer(task_runner_impl1, this,
+ &TimerTest::CountingTask);
+
+ platform_->RunUntilIdle();
+ EXPECT_TRUE(!run_times_.size());
+ EXPECT_TRUE(task_runner1->IsEmpty());
+ EXPECT_TRUE(task_runner2->IsEmpty());
+}
+
+} // namespace
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/transforms/affine_transform.cc b/chromium/third_party/blink/renderer/platform/transforms/affine_transform.cc
new file mode 100644
index 00000000000..17ef1648db9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/transforms/affine_transform.cc
@@ -0,0 +1,411 @@
+/*
+ * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved.
+ * 2010 Dirk Schulze <krit@webkit.org>
+ * Copyright (C) 2013 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/transforms/affine_transform.h"
+
+#include "third_party/blink/renderer/platform/geometry/float_quad.h"
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+AffineTransform::AffineTransform() {
+ const Transform kIdentity = IDENTITY_TRANSFORM;
+ SetMatrix(kIdentity);
+}
+
+AffineTransform::AffineTransform(double a,
+ double b,
+ double c,
+ double d,
+ double e,
+ double f) {
+ SetMatrix(a, b, c, d, e, f);
+}
+
+void AffineTransform::MakeIdentity() {
+ SetMatrix(1, 0, 0, 1, 0, 0);
+}
+
+void AffineTransform::SetMatrix(double a,
+ double b,
+ double c,
+ double d,
+ double e,
+ double f) {
+ transform_[0] = a;
+ transform_[1] = b;
+ transform_[2] = c;
+ transform_[3] = d;
+ transform_[4] = e;
+ transform_[5] = f;
+}
+
+bool AffineTransform::IsIdentity() const {
+ return (transform_[0] == 1 && transform_[1] == 0 && transform_[2] == 0 &&
+ transform_[3] == 1 && transform_[4] == 0 && transform_[5] == 0);
+}
+
+double AffineTransform::XScaleSquared() const {
+ return transform_[0] * transform_[0] + transform_[1] * transform_[1];
+}
+
+double AffineTransform::XScale() const {
+ return sqrt(XScaleSquared());
+}
+
+double AffineTransform::YScaleSquared() const {
+ return transform_[2] * transform_[2] + transform_[3] * transform_[3];
+}
+
+double AffineTransform::YScale() const {
+ return sqrt(YScaleSquared());
+}
+
+double AffineTransform::Det() const {
+ return transform_[0] * transform_[3] - transform_[1] * transform_[2];
+}
+
+bool AffineTransform::IsInvertible() const {
+ return Det() != 0.0;
+}
+
+AffineTransform AffineTransform::Inverse() const {
+ double determinant = Det();
+ if (determinant == 0.0)
+ return AffineTransform();
+
+ AffineTransform result;
+ if (IsIdentityOrTranslation()) {
+ result.transform_[4] = -transform_[4];
+ result.transform_[5] = -transform_[5];
+ return result;
+ }
+
+ result.transform_[0] = transform_[3] / determinant;
+ result.transform_[1] = -transform_[1] / determinant;
+ result.transform_[2] = -transform_[2] / determinant;
+ result.transform_[3] = transform_[0] / determinant;
+ result.transform_[4] =
+ (transform_[2] * transform_[5] - transform_[3] * transform_[4]) /
+ determinant;
+ result.transform_[5] =
+ (transform_[1] * transform_[4] - transform_[0] * transform_[5]) /
+ determinant;
+
+ return result;
+}
+
+namespace {
+
+// res = t1 * t2
+inline void DoMultiply(const AffineTransform& t1,
+ const AffineTransform& t2,
+ AffineTransform* res) {
+ res->SetA(t1.A() * t2.A() + t1.C() * t2.B());
+ res->SetB(t1.B() * t2.A() + t1.D() * t2.B());
+ res->SetC(t1.A() * t2.C() + t1.C() * t2.D());
+ res->SetD(t1.B() * t2.C() + t1.D() * t2.D());
+ res->SetE(t1.A() * t2.E() + t1.C() * t2.F() + t1.E());
+ res->SetF(t1.B() * t2.E() + t1.D() * t2.F() + t1.F());
+}
+
+} // anonymous namespace
+
+AffineTransform& AffineTransform::Multiply(const AffineTransform& other) {
+ if (other.IsIdentityOrTranslation()) {
+ if (other.transform_[4] || other.transform_[5])
+ Translate(other.transform_[4], other.transform_[5]);
+ return *this;
+ }
+
+ AffineTransform trans;
+ DoMultiply(*this, other, &trans);
+ SetMatrix(trans.transform_);
+
+ return *this;
+}
+
+AffineTransform& AffineTransform::PreMultiply(const AffineTransform& other) {
+ if (other.IsIdentityOrTranslation()) {
+ if (other.transform_[4] || other.transform_[5]) {
+ transform_[4] += other.transform_[4];
+ transform_[5] += other.transform_[5];
+ }
+ return *this;
+ }
+
+ AffineTransform trans;
+ DoMultiply(other, *this, &trans);
+ SetMatrix(trans.transform_);
+
+ return *this;
+}
+
+AffineTransform& AffineTransform::Rotate(double a) {
+ // angle is in degree. Switch to radian
+ return RotateRadians(deg2rad(a));
+}
+
+AffineTransform& AffineTransform::RotateRadians(double a) {
+ double cos_angle = cos(a);
+ double sin_angle = sin(a);
+ AffineTransform rot(cos_angle, sin_angle, -sin_angle, cos_angle, 0, 0);
+
+ Multiply(rot);
+ return *this;
+}
+
+AffineTransform& AffineTransform::Scale(double s) {
+ return Scale(s, s);
+}
+
+AffineTransform& AffineTransform::Scale(double sx, double sy) {
+ transform_[0] *= sx;
+ transform_[1] *= sx;
+ transform_[2] *= sy;
+ transform_[3] *= sy;
+ return *this;
+}
+
+// *this = *this * translation
+AffineTransform& AffineTransform::Translate(double tx, double ty) {
+ if (IsIdentityOrTranslation()) {
+ transform_[4] += tx;
+ transform_[5] += ty;
+ return *this;
+ }
+
+ transform_[4] += tx * transform_[0] + ty * transform_[2];
+ transform_[5] += tx * transform_[1] + ty * transform_[3];
+ return *this;
+}
+
+AffineTransform& AffineTransform::ScaleNonUniform(double sx, double sy) {
+ return Scale(sx, sy);
+}
+
+AffineTransform& AffineTransform::RotateFromVector(double x, double y) {
+ return RotateRadians(atan2(y, x));
+}
+
+AffineTransform& AffineTransform::FlipX() {
+ return Scale(-1, 1);
+}
+
+AffineTransform& AffineTransform::FlipY() {
+ return Scale(1, -1);
+}
+
+AffineTransform& AffineTransform::Shear(double sx, double sy) {
+ double a = transform_[0];
+ double b = transform_[1];
+
+ transform_[0] += sy * transform_[2];
+ transform_[1] += sy * transform_[3];
+ transform_[2] += sx * a;
+ transform_[3] += sx * b;
+
+ return *this;
+}
+
+AffineTransform& AffineTransform::Skew(double angle_x, double angle_y) {
+ return Shear(tan(deg2rad(angle_x)), tan(deg2rad(angle_y)));
+}
+
+AffineTransform& AffineTransform::SkewX(double angle) {
+ return Shear(tan(deg2rad(angle)), 0);
+}
+
+AffineTransform& AffineTransform::SkewY(double angle) {
+ return Shear(0, tan(deg2rad(angle)));
+}
+
+void AffineTransform::Map(double x, double y, double& x2, double& y2) const {
+ x2 = (transform_[0] * x + transform_[2] * y + transform_[4]);
+ y2 = (transform_[1] * x + transform_[3] * y + transform_[5]);
+}
+
+IntPoint AffineTransform::MapPoint(const IntPoint& point) const {
+ double x2, y2;
+ Map(point.X(), point.Y(), x2, y2);
+
+ // Round the point.
+ return IntPoint(lround(x2), lround(y2));
+}
+
+FloatPoint AffineTransform::MapPoint(const FloatPoint& point) const {
+ double x2, y2;
+ Map(point.X(), point.Y(), x2, y2);
+
+ return FloatPoint(clampTo<float>(x2), clampTo<float>(y2));
+}
+
+IntSize AffineTransform::MapSize(const IntSize& size) const {
+ double width2 = size.Width() * XScale();
+ double height2 = size.Height() * YScale();
+
+ return IntSize(lround(width2), lround(height2));
+}
+
+FloatSize AffineTransform::MapSize(const FloatSize& size) const {
+ double width2 = size.Width() * XScale();
+ double height2 = size.Height() * YScale();
+
+ return FloatSize(clampTo<float>(width2), clampTo<float>(height2));
+}
+
+IntRect AffineTransform::MapRect(const IntRect& rect) const {
+ return EnclosingIntRect(MapRect(FloatRect(rect)));
+}
+
+FloatRect AffineTransform::MapRect(const FloatRect& rect) const {
+ if (IsIdentityOrTranslation()) {
+ if (!transform_[4] && !transform_[5])
+ return rect;
+
+ FloatRect mapped_rect(rect);
+ mapped_rect.Move(clampTo<float>(transform_[4]),
+ clampTo<float>(transform_[5]));
+ return mapped_rect;
+ }
+
+ FloatQuad result;
+ result.SetP1(MapPoint(rect.Location()));
+ result.SetP2(MapPoint(FloatPoint(rect.MaxX(), rect.Y())));
+ result.SetP3(MapPoint(FloatPoint(rect.MaxX(), rect.MaxY())));
+ result.SetP4(MapPoint(FloatPoint(rect.X(), rect.MaxY())));
+ return result.BoundingBox();
+}
+
+FloatQuad AffineTransform::MapQuad(const FloatQuad& q) const {
+ if (IsIdentityOrTranslation()) {
+ FloatQuad mapped_quad(q);
+ mapped_quad.Move(clampTo<float>(transform_[4]),
+ clampTo<float>(transform_[5]));
+ return mapped_quad;
+ }
+
+ FloatQuad result;
+ result.SetP1(MapPoint(q.P1()));
+ result.SetP2(MapPoint(q.P2()));
+ result.SetP3(MapPoint(q.P3()));
+ result.SetP4(MapPoint(q.P4()));
+ return result;
+}
+
+TransformationMatrix AffineTransform::ToTransformationMatrix() const {
+ return TransformationMatrix(transform_[0], transform_[1], transform_[2],
+ transform_[3], transform_[4], transform_[5]);
+}
+
+bool AffineTransform::Decompose(DecomposedType& decomp) const {
+ AffineTransform m(*this);
+
+ // Compute scaling factors
+ double sx = XScale();
+ double sy = YScale();
+
+ // Compute cross product of transformed unit vectors. If negative,
+ // one axis was flipped.
+ if (m.A() * m.D() - m.C() * m.B() < 0) {
+ // Flip axis with minimum unit vector dot product
+ if (m.A() < m.D())
+ sx = -sx;
+ else
+ sy = -sy;
+ }
+
+ // Remove scale from matrix
+ m.Scale(1 / sx, 1 / sy);
+
+ // Compute rotation
+ double angle = atan2(m.B(), m.A());
+
+ // Remove rotation from matrix
+ m.RotateRadians(-angle);
+
+ // Return results
+ decomp.scale_x = sx;
+ decomp.scale_y = sy;
+ decomp.angle = angle;
+ decomp.remainder_a = m.A();
+ decomp.remainder_b = m.B();
+ decomp.remainder_c = m.C();
+ decomp.remainder_d = m.D();
+ decomp.translate_x = m.E();
+ decomp.translate_y = m.F();
+
+ return true;
+}
+
+void AffineTransform::Recompose(const DecomposedType& decomp) {
+ this->SetA(decomp.remainder_a);
+ this->SetB(decomp.remainder_b);
+ this->SetC(decomp.remainder_c);
+ this->SetD(decomp.remainder_d);
+ this->SetE(decomp.translate_x);
+ this->SetF(decomp.translate_y);
+ this->RotateRadians(decomp.angle);
+ this->Scale(decomp.scale_x, decomp.scale_y);
+}
+
+String AffineTransform::ToString(bool as_matrix) const {
+ if (as_matrix) {
+ // Return as a matrix in row-major order.
+ return String::Format("[%lg,%lg,%lg,\n%lg,%lg,%lg]", A(), C(), E(), B(),
+ D(), F());
+ }
+
+ if (IsIdentity())
+ return "identity";
+
+ AffineTransform::DecomposedType decomposition;
+ Decompose(decomposition);
+
+ if (IsIdentityOrTranslation())
+ return String::Format("translation(%lg,%lg)", decomposition.translate_x,
+ decomposition.translate_y);
+
+ return String::Format(
+ "translation(%lg,%lg), scale(%lg,%lg), angle(%lgdeg), "
+ "remainder(%lg,%lg,%lg,%lg)",
+ decomposition.translate_x, decomposition.translate_y,
+ decomposition.scale_x, decomposition.scale_y,
+ rad2deg(decomposition.angle), decomposition.remainder_a,
+ decomposition.remainder_b, decomposition.remainder_c,
+ decomposition.remainder_d);
+}
+
+std::ostream& operator<<(std::ostream& ostream,
+ const AffineTransform& transform) {
+ return ostream << transform.ToString();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/transforms/affine_transform.h b/chromium/third_party/blink/renderer/platform/transforms/affine_transform.h
new file mode 100644
index 00000000000..5e986badc23
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/transforms/affine_transform.h
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved.
+ * 2010 Dirk Schulze <krit@webkit.org>
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_AFFINE_TRANSFORM_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_AFFINE_TRANSFORM_H_
+
+#include "third_party/blink/renderer/platform/transforms/transformation_matrix.h"
+
+#include <string.h> // for memcpy
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+class FloatPoint;
+class FloatQuad;
+class FloatRect;
+class IntPoint;
+class IntRect;
+class TransformationMatrix;
+
+#define IDENTITY_TRANSFORM \
+ { 1, 0, 0, 1, 0, 0 }
+
+class PLATFORM_EXPORT AffineTransform {
+ DISALLOW_NEW();
+
+ public:
+ typedef double Transform[6];
+
+ AffineTransform();
+ AffineTransform(double a, double b, double c, double d, double e, double f);
+ AffineTransform(const Transform transform) { SetMatrix(transform); }
+
+ void SetMatrix(double a, double b, double c, double d, double e, double f);
+
+ void SetTransform(const AffineTransform& other) {
+ SetMatrix(other.transform_);
+ }
+
+ void Map(double x, double y, double& x2, double& y2) const;
+
+ // Rounds the mapped point to the nearest integer value.
+ IntPoint MapPoint(const IntPoint&) const;
+
+ FloatPoint MapPoint(const FloatPoint&) const;
+
+ IntSize MapSize(const IntSize&) const;
+
+ FloatSize MapSize(const FloatSize&) const;
+
+ // Rounds the resulting mapped rectangle out. This is helpful for bounding
+ // box computations but may not be what is wanted in other contexts.
+ IntRect MapRect(const IntRect&) const;
+
+ FloatRect MapRect(const FloatRect&) const;
+ FloatQuad MapQuad(const FloatQuad&) const;
+
+ bool IsIdentity() const;
+
+ double A() const { return transform_[0]; }
+ void SetA(double a) { transform_[0] = a; }
+ double B() const { return transform_[1]; }
+ void SetB(double b) { transform_[1] = b; }
+ double C() const { return transform_[2]; }
+ void SetC(double c) { transform_[2] = c; }
+ double D() const { return transform_[3]; }
+ void SetD(double d) { transform_[3] = d; }
+ double E() const { return transform_[4]; }
+ void SetE(double e) { transform_[4] = e; }
+ double F() const { return transform_[5]; }
+ void SetF(double f) { transform_[5] = f; }
+
+ void MakeIdentity();
+
+ // this' = this * other
+ AffineTransform& Multiply(const AffineTransform& other);
+ // this' = other * this
+ AffineTransform& PreMultiply(const AffineTransform& other);
+
+ AffineTransform& Scale(double);
+ AffineTransform& Scale(double sx, double sy);
+ AffineTransform& ScaleNonUniform(double sx, double sy);
+ AffineTransform& Rotate(double a);
+ AffineTransform& RotateRadians(double a);
+ AffineTransform& RotateFromVector(double x, double y);
+ AffineTransform& Translate(double tx, double ty);
+ AffineTransform& Shear(double sx, double sy);
+ AffineTransform& FlipX();
+ AffineTransform& FlipY();
+ AffineTransform& Skew(double angle_x, double angle_y);
+ AffineTransform& SkewX(double angle);
+ AffineTransform& SkewY(double angle);
+
+ double XScaleSquared() const;
+ double XScale() const;
+ double YScaleSquared() const;
+ double YScale() const;
+
+ double Det() const;
+ bool IsInvertible() const;
+ AffineTransform Inverse() const;
+
+ TransformationMatrix ToTransformationMatrix() const;
+
+ bool IsIdentityOrTranslation() const {
+ return transform_[0] == 1 && transform_[1] == 0 && transform_[2] == 0 &&
+ transform_[3] == 1;
+ }
+
+ bool IsIdentityOrTranslationOrFlipped() const {
+ return transform_[0] == 1 && transform_[1] == 0 && transform_[2] == 0 &&
+ (transform_[3] == 1 || transform_[3] == -1);
+ }
+
+ bool PreservesAxisAlignment() const {
+ return (transform_[1] == 0 && transform_[2] == 0) ||
+ (transform_[0] == 0 && transform_[3] == 0);
+ }
+
+ bool operator==(const AffineTransform& m2) const {
+ return (transform_[0] == m2.transform_[0] &&
+ transform_[1] == m2.transform_[1] &&
+ transform_[2] == m2.transform_[2] &&
+ transform_[3] == m2.transform_[3] &&
+ transform_[4] == m2.transform_[4] &&
+ transform_[5] == m2.transform_[5]);
+ }
+
+ bool operator!=(const AffineTransform& other) const {
+ return !(*this == other);
+ }
+
+ // *this = *this * t (i.e., a multRight)
+ AffineTransform& operator*=(const AffineTransform& t) { return Multiply(t); }
+
+ // result = *this * t (i.e., a multRight)
+ AffineTransform operator*(const AffineTransform& t) const {
+ AffineTransform result = *this;
+ result *= t;
+ return result;
+ }
+
+ static AffineTransform Translation(double x, double y) {
+ return AffineTransform(1, 0, 0, 1, x, y);
+ }
+
+ // decompose the matrix into its component parts
+ typedef struct {
+ double scale_x, scale_y;
+ double angle;
+ double remainder_a, remainder_b, remainder_c, remainder_d;
+ double translate_x, translate_y;
+ } DecomposedType;
+
+ bool Decompose(DecomposedType&) const;
+ void Recompose(const DecomposedType&);
+
+ void CopyTransformTo(Transform m) {
+ memcpy(m, transform_, sizeof(Transform));
+ }
+
+ // If |asMatrix| is true, the transform is returned as a matrix in row-major
+ // order. Otherwise, the transform's decomposition is returned which shows
+ // the translation, scale, etc.
+ String ToString(bool as_matrix = false) const;
+
+ private:
+ void SetMatrix(const Transform m) {
+ if (m && m != transform_)
+ memcpy(transform_, m, sizeof(Transform));
+ }
+
+ Transform transform_;
+};
+
+PLATFORM_EXPORT std::ostream& operator<<(std::ostream&, const AffineTransform&);
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/transforms/affine_transform_test.cc b/chromium/third_party/blink/renderer/platform/transforms/affine_transform_test.cc
new file mode 100644
index 00000000000..7ebef5bb2e8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/transforms/affine_transform_test.cc
@@ -0,0 +1,31 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/transforms/affine_transform.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+TEST(AffineTransformTest, ToString) {
+ AffineTransform identity;
+ EXPECT_EQ("identity", identity.ToString());
+ EXPECT_EQ("[1,0,0,\n0,1,0]", identity.ToString(true));
+
+ AffineTransform translation = AffineTransform::Translation(7, 9);
+ EXPECT_EQ("translation(7,9)", translation.ToString());
+ EXPECT_EQ("[1,0,7,\n0,1,9]", translation.ToString(true));
+
+ AffineTransform rotation;
+ rotation.Rotate(180);
+ EXPECT_EQ("translation(0,0), scale(1,1), angle(180deg), remainder(1,0,0,1)",
+ rotation.ToString());
+ EXPECT_EQ("[-1,-1.22465e-16,0,\n1.22465e-16,-1,0]", rotation.ToString(true));
+
+ AffineTransform column_major_constructor(1, 4, 2, 5, 3, 6);
+ EXPECT_EQ("[1,2,3,\n4,5,6]", column_major_constructor.ToString(true));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/transforms/identity_transform_operation.h b/chromium/third_party/blink/renderer/platform/transforms/identity_transform_operation.h
new file mode 100644
index 00000000000..2638c9974c2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/transforms/identity_transform_operation.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_IDENTITY_TRANSFORM_OPERATION_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_IDENTITY_TRANSFORM_OPERATION_H_
+
+#include "third_party/blink/renderer/platform/transforms/transform_operation.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT IdentityTransformOperation final
+ : public TransformOperation {
+ public:
+ static scoped_refptr<IdentityTransformOperation> Create() {
+ return base::AdoptRef(new IdentityTransformOperation());
+ }
+
+ virtual bool CanBlendWith(const TransformOperation& other) const {
+ return IsSameType(other);
+ }
+
+ private:
+ OperationType GetType() const override { return kIdentity; }
+
+ bool operator==(const TransformOperation& o) const override {
+ return IsSameType(o);
+ }
+
+ void Apply(TransformationMatrix&, const FloatSize&) const override {}
+
+ scoped_refptr<TransformOperation> Blend(
+ const TransformOperation*,
+ double progress,
+ bool blend_to_identity = false) override {
+ return this;
+ }
+
+ scoped_refptr<TransformOperation> Zoom(double factor) final { return this; }
+
+ IdentityTransformOperation() = default;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_IDENTITY_TRANSFORM_OPERATION_H_
diff --git a/chromium/third_party/blink/renderer/platform/transforms/interpolated_transform_operation.cc b/chromium/third_party/blink/renderer/platform/transforms/interpolated_transform_operation.cc
new file mode 100644
index 00000000000..1430d4034b5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/transforms/interpolated_transform_operation.cc
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/renderer/platform/transforms/interpolated_transform_operation.h"
+
+#include "third_party/blink/renderer/platform/transforms/identity_transform_operation.h"
+
+namespace blink {
+
+bool InterpolatedTransformOperation::operator==(
+ const TransformOperation& o) const {
+ if (!IsSameType(o))
+ return false;
+ const InterpolatedTransformOperation* t =
+ static_cast<const InterpolatedTransformOperation*>(&o);
+ return progress == t->progress && from == t->from && to == t->to;
+}
+
+void InterpolatedTransformOperation::Apply(
+ TransformationMatrix& transform,
+ const FloatSize& border_box_size) const {
+ TransformationMatrix from_transform;
+ TransformationMatrix to_transform;
+ from.Apply(border_box_size, from_transform);
+ to.Apply(border_box_size, to_transform);
+
+ to_transform.Blend(from_transform, progress);
+ transform.Multiply(to_transform);
+}
+
+scoped_refptr<TransformOperation> InterpolatedTransformOperation::Blend(
+ const TransformOperation* from,
+ double progress,
+ bool blend_to_identity) {
+ if (from && !from->IsSameType(*this))
+ return this;
+
+ TransformOperations this_operations;
+ this_operations.Operations().push_back(this);
+ TransformOperations from_operations;
+ if (blend_to_identity)
+ from_operations.Operations().push_back(
+ IdentityTransformOperation::Create());
+ else
+ from_operations.Operations().push_back(
+ const_cast<TransformOperation*>(from));
+ return InterpolatedTransformOperation::Create(this_operations,
+ from_operations, progress);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/transforms/interpolated_transform_operation.h b/chromium/third_party/blink/renderer/platform/transforms/interpolated_transform_operation.h
new file mode 100644
index 00000000000..83e9188e6c7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/transforms/interpolated_transform_operation.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_INTERPOLATED_TRANSFORM_OPERATION_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_INTERPOLATED_TRANSFORM_OPERATION_H_
+
+#include "third_party/blink/renderer/platform/transforms/transform_operation.h"
+#include "third_party/blink/renderer/platform/transforms/transform_operations.h"
+
+namespace blink {
+
+// This class is an implementation detail for deferred interpolations.
+class PLATFORM_EXPORT InterpolatedTransformOperation final
+ : public TransformOperation {
+ public:
+ static scoped_refptr<InterpolatedTransformOperation> Create(
+ const TransformOperations& from,
+ const TransformOperations& to,
+ double progress) {
+ return base::AdoptRef(
+ new InterpolatedTransformOperation(from, to, progress));
+ }
+
+ virtual bool CanBlendWith(const TransformOperation& other) const {
+ return IsSameType(other);
+ }
+
+ private:
+ OperationType GetType() const override { return kInterpolated; }
+
+ bool operator==(const TransformOperation&) const override;
+ void Apply(TransformationMatrix&,
+ const FloatSize& border_box_size) const override;
+
+ scoped_refptr<TransformOperation> Blend(
+ const TransformOperation* from,
+ double progress,
+ bool blend_to_identity = false) override;
+ scoped_refptr<TransformOperation> Zoom(double factor) final {
+ return Create(from.Zoom(factor), to.Zoom(factor), progress);
+ }
+
+ bool DependsOnBoxSize() const override {
+ return from.DependsOnBoxSize() || to.DependsOnBoxSize();
+ }
+
+ InterpolatedTransformOperation(const TransformOperations& from,
+ const TransformOperations& to,
+ double progress)
+ : from(from), to(to), progress(progress) {}
+
+ const TransformOperations from;
+ const TransformOperations to;
+ double progress;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_INTERPOLATED_TRANSFORM_OPERATION_H_
diff --git a/chromium/third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.cc b/chromium/third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.cc
new file mode 100644
index 00000000000..2238948ca78
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.cc
@@ -0,0 +1,64 @@
+
+
+/*
+ * Copyright (C) 2009 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.h"
+
+#include <algorithm>
+
+namespace blink {
+
+scoped_refptr<TransformOperation> Matrix3DTransformOperation::Blend(
+ const TransformOperation* from,
+ double progress,
+ bool blend_to_identity) {
+ if (from && !from->IsSameType(*this))
+ return this;
+
+ // Convert the TransformOperations into matrices
+ FloatSize size;
+ TransformationMatrix from_t;
+ TransformationMatrix to_t;
+ if (from)
+ from->Apply(from_t, size);
+
+ Apply(to_t, size);
+
+ if (blend_to_identity)
+ std::swap(from_t, to_t);
+
+ to_t.Blend(from_t, progress);
+ return Matrix3DTransformOperation::Create(to_t);
+}
+
+scoped_refptr<TransformOperation> Matrix3DTransformOperation::Zoom(
+ double factor) {
+ TransformationMatrix result = matrix_;
+ result.Zoom(factor);
+ return Create(result);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.h b/chromium/third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.h
new file mode 100644
index 00000000000..d01da19fe28
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2009 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_MATRIX_3D_TRANSFORM_OPERATION_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_MATRIX_3D_TRANSFORM_OPERATION_H_
+
+#include "third_party/blink/renderer/platform/transforms/transform_operation.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT Matrix3DTransformOperation final
+ : public TransformOperation {
+ public:
+ static scoped_refptr<Matrix3DTransformOperation> Create(
+ const TransformationMatrix& matrix) {
+ return base::AdoptRef(new Matrix3DTransformOperation(matrix));
+ }
+
+ TransformationMatrix Matrix() const { return matrix_; }
+
+ virtual bool CanBlendWith(const TransformOperation& other) const {
+ return false;
+ }
+
+ static bool IsMatchingOperationType(OperationType type) {
+ return type == kMatrix3D;
+ }
+
+ private:
+ OperationType GetType() const override { return kMatrix3D; }
+
+ bool operator==(const TransformOperation& o) const override {
+ if (!IsSameType(o))
+ return false;
+ const Matrix3DTransformOperation* m =
+ static_cast<const Matrix3DTransformOperation*>(&o);
+ return matrix_ == m->matrix_;
+ }
+
+ void Apply(TransformationMatrix& transform, const FloatSize&) const override {
+ transform.Multiply(TransformationMatrix(matrix_));
+ }
+
+ scoped_refptr<TransformOperation> Blend(
+ const TransformOperation* from,
+ double progress,
+ bool blend_to_identity = false) override;
+ scoped_refptr<TransformOperation> Zoom(double factor) final;
+
+ Matrix3DTransformOperation(const TransformationMatrix& mat) { matrix_ = mat; }
+
+ TransformationMatrix matrix_;
+};
+
+DEFINE_TRANSFORM_TYPE_CASTS(Matrix3DTransformOperation);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_MATRIX_3D_TRANSFORM_OPERATION_H_
diff --git a/chromium/third_party/blink/renderer/platform/transforms/matrix_transform_operation.cc b/chromium/third_party/blink/renderer/platform/transforms/matrix_transform_operation.cc
new file mode 100644
index 00000000000..531d274d7eb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/transforms/matrix_transform_operation.cc
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "third_party/blink/renderer/platform/transforms/matrix_transform_operation.h"
+
+#include <algorithm>
+
+namespace blink {
+
+scoped_refptr<TransformOperation> MatrixTransformOperation::Blend(
+ const TransformOperation* from,
+ double progress,
+ bool blend_to_identity) {
+ if (from && !from->IsSameType(*this))
+ return this;
+
+ // convert the TransformOperations into matrices
+ FloatSize size;
+ TransformationMatrix from_t;
+ TransformationMatrix to_t(a_, b_, c_, d_, e_, f_);
+ if (from) {
+ const MatrixTransformOperation* m =
+ static_cast<const MatrixTransformOperation*>(from);
+ from_t.SetMatrix(m->a_, m->b_, m->c_, m->d_, m->e_, m->f_);
+ }
+
+ if (blend_to_identity)
+ std::swap(from_t, to_t);
+
+ to_t.Blend(from_t, progress);
+ return MatrixTransformOperation::Create(to_t.A(), to_t.B(), to_t.C(),
+ to_t.D(), to_t.E(), to_t.F());
+}
+
+scoped_refptr<TransformOperation> MatrixTransformOperation::Zoom(
+ double factor) {
+ return Create(a_, b_, c_, d_, e_ * factor, f_ * factor);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/transforms/matrix_transform_operation.h b/chromium/third_party/blink/renderer/platform/transforms/matrix_transform_operation.h
new file mode 100644
index 00000000000..2d0cdaedda4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/transforms/matrix_transform_operation.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_MATRIX_TRANSFORM_OPERATION_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_MATRIX_TRANSFORM_OPERATION_H_
+
+#include "third_party/blink/renderer/platform/transforms/transform_operation.h"
+#include "third_party/blink/renderer/platform/transforms/transformation_matrix.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT MatrixTransformOperation final
+ : public TransformOperation {
+ public:
+ static scoped_refptr<MatrixTransformOperation> Create(double a,
+ double b,
+ double c,
+ double d,
+ double e,
+ double f) {
+ return base::AdoptRef(new MatrixTransformOperation(a, b, c, d, e, f));
+ }
+
+ static scoped_refptr<MatrixTransformOperation> Create(
+ const TransformationMatrix& t) {
+ return base::AdoptRef(new MatrixTransformOperation(t));
+ }
+
+ TransformationMatrix Matrix() const {
+ return TransformationMatrix(a_, b_, c_, d_, e_, f_);
+ }
+
+ virtual bool CanBlendWith(const TransformOperation& other) const {
+ return false;
+ }
+
+ static bool IsMatchingOperationType(OperationType type) {
+ return type == kMatrix;
+ }
+
+ private:
+ OperationType GetType() const override { return kMatrix; }
+
+ bool operator==(const TransformOperation& o) const override {
+ if (!IsSameType(o))
+ return false;
+
+ const MatrixTransformOperation* m =
+ static_cast<const MatrixTransformOperation*>(&o);
+ return a_ == m->a_ && b_ == m->b_ && c_ == m->c_ && d_ == m->d_ &&
+ e_ == m->e_ && f_ == m->f_;
+ }
+
+ void Apply(TransformationMatrix& transform, const FloatSize&) const override {
+ TransformationMatrix matrix(a_, b_, c_, d_, e_, f_);
+ transform.Multiply(matrix);
+ }
+
+ scoped_refptr<TransformOperation> Blend(
+ const TransformOperation* from,
+ double progress,
+ bool blend_to_identity = false) override;
+ scoped_refptr<TransformOperation> Zoom(double factor) final;
+
+ MatrixTransformOperation(double a,
+ double b,
+ double c,
+ double d,
+ double e,
+ double f)
+ : a_(a), b_(b), c_(c), d_(d), e_(e), f_(f) {}
+
+ MatrixTransformOperation(const TransformationMatrix& t)
+ : a_(t.A()), b_(t.B()), c_(t.C()), d_(t.D()), e_(t.E()), f_(t.F()) {}
+
+ double a_;
+ double b_;
+ double c_;
+ double d_;
+ double e_;
+ double f_;
+};
+
+DEFINE_TRANSFORM_TYPE_CASTS(MatrixTransformOperation);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_MATRIX_TRANSFORM_OPERATION_H_
diff --git a/chromium/third_party/blink/renderer/platform/transforms/perspective_transform_operation.cc b/chromium/third_party/blink/renderer/platform/transforms/perspective_transform_operation.cc
new file mode 100644
index 00000000000..48d89c48e2e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/transforms/perspective_transform_operation.cc
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2009 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/transforms/perspective_transform_operation.h"
+
+#include "third_party/blink/renderer/platform/animation/animation_utilities.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+namespace blink {
+
+scoped_refptr<TransformOperation> PerspectiveTransformOperation::Blend(
+ const TransformOperation* from,
+ double progress,
+ bool blend_to_identity) {
+ if (from && !from->IsSameType(*this))
+ return this;
+
+ if (blend_to_identity) {
+ // FIXME: this seems wrong. https://bugs.webkit.org/show_bug.cgi?id=52700
+ double p = blink::Blend(p_, 1., progress);
+ return PerspectiveTransformOperation::Create(clampTo<int>(p, 0));
+ }
+
+ const PerspectiveTransformOperation* from_op =
+ static_cast<const PerspectiveTransformOperation*>(from);
+
+ TransformationMatrix from_t;
+ TransformationMatrix to_t;
+ from_t.ApplyPerspective(from_op ? from_op->p_ : 0);
+ to_t.ApplyPerspective(p_);
+ to_t.Blend(from_t, progress);
+
+ TransformationMatrix::DecomposedType decomp;
+ if (!to_t.Decompose(decomp)) {
+ // If we can't decompose, bail out of interpolation.
+ const PerspectiveTransformOperation* used_operation =
+ progress > 0.5 ? this : from_op;
+ return PerspectiveTransformOperation::Create(used_operation->Perspective());
+ }
+
+ if (decomp.perspective_z) {
+ double val = -1.0 / decomp.perspective_z;
+ return PerspectiveTransformOperation::Create(clampTo<int>(val, 0));
+ }
+ return PerspectiveTransformOperation::Create(0);
+}
+
+scoped_refptr<TransformOperation> PerspectiveTransformOperation::Zoom(
+ double factor) {
+ return Create(p_ * factor);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/transforms/perspective_transform_operation.h b/chromium/third_party/blink/renderer/platform/transforms/perspective_transform_operation.h
new file mode 100644
index 00000000000..28728bdac53
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/transforms/perspective_transform_operation.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2009 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_PERSPECTIVE_TRANSFORM_OPERATION_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_PERSPECTIVE_TRANSFORM_OPERATION_H_
+
+#include "third_party/blink/renderer/platform/transforms/transform_operation.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT PerspectiveTransformOperation final
+ : public TransformOperation {
+ public:
+ static scoped_refptr<PerspectiveTransformOperation> Create(double p) {
+ return base::AdoptRef(new PerspectiveTransformOperation(p));
+ }
+
+ double Perspective() const { return p_; }
+
+ virtual bool CanBlendWith(const TransformOperation& other) const {
+ return IsSameType(other);
+ }
+
+ static bool IsMatchingOperationType(OperationType type) {
+ return type == kPerspective;
+ }
+
+ private:
+ OperationType GetType() const override { return kPerspective; }
+
+ bool operator==(const TransformOperation& o) const override {
+ if (!IsSameType(o))
+ return false;
+ const PerspectiveTransformOperation* p =
+ static_cast<const PerspectiveTransformOperation*>(&o);
+ return p_ == p->p_;
+ }
+
+ void Apply(TransformationMatrix& transform, const FloatSize&) const override {
+ transform.ApplyPerspective(p_);
+ }
+
+ scoped_refptr<TransformOperation> Blend(
+ const TransformOperation* from,
+ double progress,
+ bool blend_to_identity = false) override;
+ scoped_refptr<TransformOperation> Zoom(double factor) final;
+
+ PerspectiveTransformOperation(double p) : p_(p) {}
+
+ double p_;
+};
+
+DEFINE_TRANSFORM_TYPE_CASTS(PerspectiveTransformOperation);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_PERSPECTIVE_TRANSFORM_OPERATION_H_
diff --git a/chromium/third_party/blink/renderer/platform/transforms/rotate_transform_operation.cc b/chromium/third_party/blink/renderer/platform/transforms/rotate_transform_operation.cc
new file mode 100644
index 00000000000..def305a3293
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/transforms/rotate_transform_operation.cc
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "third_party/blink/renderer/platform/transforms/rotate_transform_operation.h"
+
+#include "third_party/blink/renderer/platform/animation/animation_utilities.h"
+
+namespace blink {
+
+bool RotateTransformOperation::operator==(
+ const TransformOperation& other) const {
+ if (!IsSameType(other))
+ return false;
+ const Rotation& other_rotation = ToRotateTransformOperation(other).rotation_;
+ return rotation_.axis == other_rotation.axis &&
+ rotation_.angle == other_rotation.angle;
+}
+
+bool RotateTransformOperation::GetCommonAxis(const RotateTransformOperation* a,
+ const RotateTransformOperation* b,
+ FloatPoint3D& result_axis,
+ double& result_angle_a,
+ double& result_angle_b) {
+ return Rotation::GetCommonAxis(a ? a->rotation_ : Rotation(),
+ b ? b->rotation_ : Rotation(), result_axis,
+ result_angle_a, result_angle_b);
+}
+
+scoped_refptr<TransformOperation> RotateTransformOperation::Blend(
+ const TransformOperation* from,
+ double progress,
+ bool blend_to_identity) {
+ if (from && !from->IsSameType(*this))
+ return this;
+
+ if (blend_to_identity)
+ return RotateTransformOperation::Create(
+ Rotation(Axis(), Angle() * (1 - progress)), type_);
+
+ // Optimize for single axis rotation
+ if (!from)
+ return RotateTransformOperation::Create(
+ Rotation(Axis(), Angle() * progress), type_);
+
+ const RotateTransformOperation& from_rotate =
+ ToRotateTransformOperation(*from);
+ if (GetType() == kRotate3D) {
+ return RotateTransformOperation::Create(
+ Rotation::Slerp(from_rotate.rotation_, rotation_, progress), kRotate3D);
+ }
+
+ DCHECK(Axis() == from_rotate.Axis());
+ return RotateTransformOperation::Create(
+ Rotation(Axis(), blink::Blend(from_rotate.Angle(), Angle(), progress)),
+ type_);
+}
+
+bool RotateTransformOperation::CanBlendWith(
+ const TransformOperation& other) const {
+ return other.IsSameType(*this);
+}
+
+RotateAroundOriginTransformOperation::RotateAroundOriginTransformOperation(
+ double angle,
+ double origin_x,
+ double origin_y)
+ : RotateTransformOperation(Rotation(FloatPoint3D(0, 0, 1), angle),
+ kRotateAroundOrigin),
+ origin_x_(origin_x),
+ origin_y_(origin_y) {}
+
+void RotateAroundOriginTransformOperation::Apply(
+ TransformationMatrix& transform,
+ const FloatSize& box_size) const {
+ transform.Translate(origin_x_, origin_y_);
+ RotateTransformOperation::Apply(transform, box_size);
+ transform.Translate(-origin_x_, -origin_y_);
+}
+
+bool RotateAroundOriginTransformOperation::operator==(
+ const TransformOperation& other) const {
+ if (!IsSameType(other))
+ return false;
+ const RotateAroundOriginTransformOperation& other_rotate =
+ ToRotateAroundOriginTransformOperation(other);
+ const Rotation& other_rotation = other_rotate.rotation_;
+ return rotation_.axis == other_rotation.axis &&
+ rotation_.angle == other_rotation.angle &&
+ origin_x_ == other_rotate.origin_x_ &&
+ origin_y_ == other_rotate.origin_y_;
+}
+
+scoped_refptr<TransformOperation> RotateAroundOriginTransformOperation::Blend(
+ const TransformOperation* from,
+ double progress,
+ bool blend_to_identity) {
+ if (from && !from->IsSameType(*this))
+ return this;
+ if (blend_to_identity) {
+ return RotateAroundOriginTransformOperation::Create(
+ Angle() * (1 - progress), origin_x_, origin_y_);
+ }
+ if (!from) {
+ return RotateAroundOriginTransformOperation::Create(Angle() * progress,
+ origin_x_, origin_y_);
+ }
+ const RotateAroundOriginTransformOperation& from_rotate =
+ ToRotateAroundOriginTransformOperation(*from);
+ return RotateAroundOriginTransformOperation::Create(
+ blink::Blend(from_rotate.Angle(), Angle(), progress),
+ blink::Blend(from_rotate.origin_x_, origin_x_, progress),
+ blink::Blend(from_rotate.origin_y_, origin_y_, progress));
+}
+
+scoped_refptr<TransformOperation> RotateAroundOriginTransformOperation::Zoom(
+ double factor) {
+ return Create(Angle(), origin_x_ * factor, origin_y_ * factor);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/transforms/rotate_transform_operation.h b/chromium/third_party/blink/renderer/platform/transforms/rotate_transform_operation.h
new file mode 100644
index 00000000000..4fbdaa271eb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/transforms/rotate_transform_operation.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_ROTATE_TRANSFORM_OPERATION_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_ROTATE_TRANSFORM_OPERATION_H_
+
+#include "third_party/blink/renderer/platform/geometry/float_point_3d.h"
+#include "third_party/blink/renderer/platform/transforms/rotation.h"
+#include "third_party/blink/renderer/platform/transforms/transform_operation.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT RotateTransformOperation : public TransformOperation {
+ public:
+ static scoped_refptr<RotateTransformOperation> Create(double angle,
+ OperationType type) {
+ return Create(Rotation(FloatPoint3D(0, 0, 1), angle), type);
+ }
+
+ static scoped_refptr<RotateTransformOperation> Create(double x,
+ double y,
+ double z,
+ double angle,
+ OperationType type) {
+ return Create(Rotation(FloatPoint3D(x, y, z), angle), type);
+ }
+
+ static scoped_refptr<RotateTransformOperation> Create(
+ const Rotation& rotation,
+ OperationType type) {
+ DCHECK(IsMatchingOperationType(type));
+ return base::AdoptRef(new RotateTransformOperation(rotation, type));
+ }
+
+ bool operator==(const RotateTransformOperation& other) const {
+ return *this == static_cast<const TransformOperation&>(other);
+ }
+
+ double X() const { return rotation_.axis.X(); }
+ double Y() const { return rotation_.axis.Y(); }
+ double Z() const { return rotation_.axis.Z(); }
+ double Angle() const { return rotation_.angle; }
+ const FloatPoint3D& Axis() const { return rotation_.axis; }
+
+ static bool GetCommonAxis(const RotateTransformOperation*,
+ const RotateTransformOperation*,
+ FloatPoint3D& result_axis,
+ double& result_angle_a,
+ double& result_angle_b);
+
+ virtual bool CanBlendWith(const TransformOperation& other) const;
+ OperationType GetType() const override { return type_; }
+ OperationType PrimitiveType() const final { return kRotate3D; }
+
+ void Apply(TransformationMatrix& transform,
+ const FloatSize& /*borderBoxSize*/) const override {
+ transform.Rotate3d(rotation_);
+ }
+
+ static bool IsMatchingOperationType(OperationType type) {
+ return type == kRotate || type == kRotateX || type == kRotateY ||
+ type == kRotateZ || type == kRotate3D;
+ }
+
+ protected:
+ bool operator==(const TransformOperation&) const override;
+
+ scoped_refptr<TransformOperation> Blend(
+ const TransformOperation* from,
+ double progress,
+ bool blend_to_identity = false) override;
+ scoped_refptr<TransformOperation> Zoom(double factor) override {
+ return this;
+ }
+
+ RotateTransformOperation(const Rotation& rotation, OperationType type)
+ : rotation_(rotation), type_(type) {}
+
+ const Rotation rotation_;
+ const OperationType type_;
+};
+
+DEFINE_TRANSFORM_TYPE_CASTS(RotateTransformOperation);
+
+class PLATFORM_EXPORT RotateAroundOriginTransformOperation final
+ : public RotateTransformOperation {
+ public:
+ static scoped_refptr<RotateAroundOriginTransformOperation>
+ Create(double angle, double origin_x, double origin_y) {
+ return base::AdoptRef(
+ new RotateAroundOriginTransformOperation(angle, origin_x, origin_y));
+ }
+
+ void Apply(TransformationMatrix&, const FloatSize&) const override;
+
+ static bool IsMatchingOperationType(OperationType type) {
+ return type == kRotateAroundOrigin;
+ }
+
+ private:
+ RotateAroundOriginTransformOperation(double angle,
+ double origin_x,
+ double origin_y);
+
+ bool operator==(const TransformOperation&) const override;
+
+ scoped_refptr<TransformOperation> Blend(
+ const TransformOperation* from,
+ double progress,
+ bool blend_to_identity = false) override;
+ scoped_refptr<TransformOperation> Zoom(double factor) override;
+
+ double origin_x_;
+ double origin_y_;
+};
+
+DEFINE_TRANSFORM_TYPE_CASTS(RotateAroundOriginTransformOperation);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_ROTATE_TRANSFORM_OPERATION_H_
diff --git a/chromium/third_party/blink/renderer/platform/transforms/rotation.cc b/chromium/third_party/blink/renderer/platform/transforms/rotation.cc
new file mode 100644
index 00000000000..eb4fb2d8deb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/transforms/rotation.cc
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "third_party/blink/renderer/platform/transforms/rotation.h"
+
+#include "third_party/blink/renderer/platform/animation/animation_utilities.h"
+#include "third_party/blink/renderer/platform/transforms/transformation_matrix.h"
+
+namespace blink {
+
+namespace {
+
+const double kAngleEpsilon = 1e-4;
+
+Rotation ExtractFromMatrix(const TransformationMatrix& matrix,
+ const Rotation& fallback_value) {
+ TransformationMatrix::DecomposedType decomp;
+ if (!matrix.Decompose(decomp))
+ return fallback_value;
+ double x = -decomp.quaternion_x;
+ double y = -decomp.quaternion_y;
+ double z = -decomp.quaternion_z;
+ double length = std::sqrt(x * x + y * y + z * z);
+ double angle = 0;
+ if (length > 0.00001) {
+ x /= length;
+ y /= length;
+ z /= length;
+ angle = rad2deg(std::acos(decomp.quaternion_w) * 2);
+ } else {
+ x = 0;
+ y = 0;
+ z = 1;
+ }
+ return Rotation(FloatPoint3D(x, y, z), angle);
+}
+
+} // namespace
+
+bool Rotation::GetCommonAxis(const Rotation& a,
+ const Rotation& b,
+ FloatPoint3D& result_axis,
+ double& result_angle_a,
+ double& result_angle_b) {
+ result_axis = FloatPoint3D(0, 0, 1);
+ result_angle_a = 0;
+ result_angle_b = 0;
+
+ bool is_zero_a = a.axis.IsZero() || fabs(a.angle) < kAngleEpsilon;
+ bool is_zero_b = b.axis.IsZero() || fabs(b.angle) < kAngleEpsilon;
+
+ if (is_zero_a && is_zero_b)
+ return true;
+
+ if (is_zero_a) {
+ result_axis = b.axis;
+ result_angle_b = b.angle;
+ return true;
+ }
+
+ if (is_zero_b) {
+ result_axis = a.axis;
+ result_angle_a = a.angle;
+ return true;
+ }
+
+ double dot = a.axis.Dot(b.axis);
+ if (dot < 0)
+ return false;
+
+ double a_squared = a.axis.LengthSquared();
+ double b_squared = b.axis.LengthSquared();
+ double error = std::abs(1 - (dot * dot) / (a_squared * b_squared));
+ if (error > kAngleEpsilon)
+ return false;
+
+ result_axis = a.axis;
+ result_angle_a = a.angle;
+ result_angle_b = b.angle;
+ return true;
+}
+
+Rotation Rotation::Slerp(const Rotation& from,
+ const Rotation& to,
+ double progress) {
+ double from_angle;
+ double to_angle;
+ FloatPoint3D axis;
+ if (GetCommonAxis(from, to, axis, from_angle, to_angle))
+ return Rotation(axis, blink::Blend(from_angle, to_angle, progress));
+
+ TransformationMatrix from_matrix;
+ TransformationMatrix to_matrix;
+ from_matrix.Rotate3d(from);
+ to_matrix.Rotate3d(to);
+ to_matrix.Blend(from_matrix, progress);
+ return ExtractFromMatrix(to_matrix, progress < 0.5 ? from : to);
+}
+
+Rotation Rotation::Add(const Rotation& a, const Rotation& b) {
+ double angle_a;
+ double angle_b;
+ FloatPoint3D axis;
+ if (GetCommonAxis(a, b, axis, angle_a, angle_b))
+ return Rotation(axis, angle_a + angle_b);
+
+ TransformationMatrix matrix;
+ matrix.Rotate3d(a);
+ matrix.Rotate3d(b);
+ return ExtractFromMatrix(matrix, b);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/transforms/rotation.h b/chromium/third_party/blink/renderer/platform/transforms/rotation.h
new file mode 100644
index 00000000000..5cc9c82e6e1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/transforms/rotation.h
@@ -0,0 +1,45 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_ROTATION_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_ROTATION_H_
+
+#include "third_party/blink/renderer/platform/geometry/float_point_3d.h"
+
+namespace blink {
+
+struct PLATFORM_EXPORT Rotation {
+ Rotation() : axis(0, 0, 0), angle(0) {}
+
+ Rotation(const FloatPoint3D& axis, double angle) : axis(axis), angle(angle) {}
+
+ // If either rotation is effectively "zero" or both rotations share the same
+ // normalized axes this function returns true and the "non-zero" axis is
+ // returned as resultAxis and the effective angles are returned as
+ // resultAngleA and resultAngleB. Otherwise false is returned.
+ static bool GetCommonAxis(const Rotation& /*a*/,
+ const Rotation& /*b*/,
+ FloatPoint3D& result_axis,
+ double& result_angle_a,
+ double& result_angle_b);
+
+ // A progress of 0 corresponds to "from" and a progress of 1 corresponds to
+ // "to".
+ static Rotation Slerp(const Rotation& from,
+ const Rotation& to,
+ double progress);
+
+ // Returns a rotation whose effect is equivalent to applying a followed by b.
+ static Rotation Add(const Rotation& /*a*/, const Rotation& /*b*/);
+
+ // No restrictions on the axis vector.
+ FloatPoint3D axis;
+
+ // Measured in degrees.
+ double angle;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_ROTATION_H_
diff --git a/chromium/third_party/blink/renderer/platform/transforms/rotation_test.cc b/chromium/third_party/blink/renderer/platform/transforms/rotation_test.cc
new file mode 100644
index 00000000000..87ebd020144
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/transforms/rotation_test.cc
@@ -0,0 +1,67 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/transforms/rotation.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+TEST(RotationTest, GetCommonAxisTest) {
+ FloatPoint3D axis;
+ double angle_a;
+ double angle_b;
+
+ EXPECT_TRUE(Rotation::GetCommonAxis(Rotation(FloatPoint3D(0, 0, 0), 0),
+ Rotation(FloatPoint3D(1, 2, 3), 100),
+ axis, angle_a, angle_b));
+ EXPECT_EQ(FloatPoint3D(1, 2, 3), axis);
+ EXPECT_EQ(0, angle_a);
+ EXPECT_EQ(100, angle_b);
+
+ EXPECT_TRUE(Rotation::GetCommonAxis(Rotation(FloatPoint3D(1, 2, 3), 100),
+ Rotation(FloatPoint3D(0, 0, 0), 0), axis,
+ angle_a, angle_b));
+ EXPECT_EQ(FloatPoint3D(1, 2, 3), axis);
+ EXPECT_EQ(100, angle_a);
+ EXPECT_EQ(0, angle_b);
+
+ EXPECT_TRUE(Rotation::GetCommonAxis(Rotation(FloatPoint3D(0, 0, 0), 100),
+ Rotation(FloatPoint3D(1, 2, 3), 100),
+ axis, angle_a, angle_b));
+ EXPECT_EQ(FloatPoint3D(1, 2, 3), axis);
+ EXPECT_EQ(0, angle_a);
+ EXPECT_EQ(100, angle_b);
+
+ EXPECT_TRUE(Rotation::GetCommonAxis(Rotation(FloatPoint3D(3, 2, 1), 0),
+ Rotation(FloatPoint3D(1, 2, 3), 100),
+ axis, angle_a, angle_b));
+ EXPECT_EQ(FloatPoint3D(1, 2, 3), axis);
+ EXPECT_EQ(0, angle_a);
+ EXPECT_EQ(100, angle_b);
+
+ EXPECT_TRUE(Rotation::GetCommonAxis(Rotation(FloatPoint3D(1, 2, 3), 50),
+ Rotation(FloatPoint3D(1, 2, 3), 100),
+ axis, angle_a, angle_b));
+ EXPECT_EQ(FloatPoint3D(1, 2, 3), axis);
+ EXPECT_EQ(50, angle_a);
+ EXPECT_EQ(100, angle_b);
+
+ EXPECT_TRUE(Rotation::GetCommonAxis(Rotation(FloatPoint3D(1, 2, 3), 50),
+ Rotation(FloatPoint3D(2, 4, 6), 100),
+ axis, angle_a, angle_b));
+ EXPECT_EQ(FloatPoint3D(1, 2, 3), axis);
+ EXPECT_EQ(50, angle_a);
+ EXPECT_EQ(100, angle_b);
+
+ EXPECT_FALSE(Rotation::GetCommonAxis(Rotation(FloatPoint3D(1, 2, 3), 50),
+ Rotation(FloatPoint3D(3, 2, 1), 100),
+ axis, angle_a, angle_b));
+
+ EXPECT_FALSE(Rotation::GetCommonAxis(Rotation(FloatPoint3D(1, 2, 3), 50),
+ Rotation(FloatPoint3D(-1, -2, -3), 100),
+ axis, angle_a, angle_b));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/transforms/scale_transform_operation.cc b/chromium/third_party/blink/renderer/platform/transforms/scale_transform_operation.cc
new file mode 100644
index 00000000000..7b6d5ce438c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/transforms/scale_transform_operation.cc
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "third_party/blink/renderer/platform/transforms/scale_transform_operation.h"
+
+#include "third_party/blink/renderer/platform/animation/animation_utilities.h"
+
+namespace blink {
+
+scoped_refptr<TransformOperation> ScaleTransformOperation::Blend(
+ const TransformOperation* from,
+ double progress,
+ bool blend_to_identity) {
+ if (from && !from->CanBlendWith(*this))
+ return this;
+
+ if (blend_to_identity)
+ return ScaleTransformOperation::Create(
+ blink::Blend(x_, 1.0, progress), blink::Blend(y_, 1.0, progress),
+ blink::Blend(z_, 1.0, progress), type_);
+
+ const ScaleTransformOperation* from_op =
+ static_cast<const ScaleTransformOperation*>(from);
+ double from_x = from_op ? from_op->x_ : 1.0;
+ double from_y = from_op ? from_op->y_ : 1.0;
+ double from_z = from_op ? from_op->z_ : 1.0;
+ return ScaleTransformOperation::Create(
+ blink::Blend(from_x, x_, progress), blink::Blend(from_y, y_, progress),
+ blink::Blend(from_z, z_, progress), type_);
+}
+
+bool ScaleTransformOperation::CanBlendWith(
+ const TransformOperation& other) const {
+ return other.GetType() == kScaleX || other.GetType() == kScaleY ||
+ other.GetType() == kScaleZ || other.GetType() == kScale3D ||
+ other.GetType() == kScale;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/transforms/scale_transform_operation.h b/chromium/third_party/blink/renderer/platform/transforms/scale_transform_operation.h
new file mode 100644
index 00000000000..91d37321729
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/transforms/scale_transform_operation.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_SCALE_TRANSFORM_OPERATION_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_SCALE_TRANSFORM_OPERATION_H_
+
+#include "third_party/blink/renderer/platform/transforms/transform_operation.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT ScaleTransformOperation final
+ : public TransformOperation {
+ public:
+ static scoped_refptr<ScaleTransformOperation> Create(double sx,
+ double sy,
+ OperationType type) {
+ return base::AdoptRef(new ScaleTransformOperation(sx, sy, 1, type));
+ }
+
+ static scoped_refptr<ScaleTransformOperation> Create(double sx,
+ double sy,
+ double sz,
+ OperationType type) {
+ return base::AdoptRef(new ScaleTransformOperation(sx, sy, sz, type));
+ }
+
+ bool operator==(const ScaleTransformOperation& other) const {
+ return *this == static_cast<const TransformOperation&>(other);
+ }
+
+ double X() const { return x_; }
+ double Y() const { return y_; }
+ double Z() const { return z_; }
+
+ virtual bool CanBlendWith(const TransformOperation& other) const;
+
+ void Apply(TransformationMatrix& transform, const FloatSize&) const override {
+ transform.Scale3d(x_, y_, z_);
+ }
+ scoped_refptr<TransformOperation> Blend(
+ const TransformOperation* from,
+ double progress,
+ bool blend_to_identity = false) override;
+
+ static bool IsMatchingOperationType(OperationType type) {
+ return type == kScale || type == kScaleX || type == kScaleY ||
+ type == kScaleZ || type == kScale3D;
+ }
+
+ OperationType GetType() const override { return type_; }
+ OperationType PrimitiveType() const final { return kScale3D; }
+
+ private:
+ bool operator==(const TransformOperation& o) const override {
+ if (!IsSameType(o))
+ return false;
+ const ScaleTransformOperation* s =
+ static_cast<const ScaleTransformOperation*>(&o);
+ return x_ == s->x_ && y_ == s->y_ && z_ == s->z_;
+ }
+
+ virtual bool HasNonTrivial3DComponent() const { return z_ != 1.0; }
+
+ scoped_refptr<TransformOperation> Zoom(double factor) final { return this; }
+
+ ScaleTransformOperation(double sx, double sy, double sz, OperationType type)
+ : x_(sx), y_(sy), z_(sz), type_(type) {
+ DCHECK(IsMatchingOperationType(type));
+ }
+
+ double x_;
+ double y_;
+ double z_;
+ OperationType type_;
+};
+
+DEFINE_TRANSFORM_TYPE_CASTS(ScaleTransformOperation);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_SCALE_TRANSFORM_OPERATION_H_
diff --git a/chromium/third_party/blink/renderer/platform/transforms/skew_transform_operation.cc b/chromium/third_party/blink/renderer/platform/transforms/skew_transform_operation.cc
new file mode 100644
index 00000000000..608716022d2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/transforms/skew_transform_operation.cc
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "third_party/blink/renderer/platform/transforms/skew_transform_operation.h"
+
+#include "third_party/blink/renderer/platform/animation/animation_utilities.h"
+
+namespace blink {
+
+scoped_refptr<TransformOperation> SkewTransformOperation::Blend(
+ const TransformOperation* from,
+ double progress,
+ bool blend_to_identity) {
+ if (from && !from->CanBlendWith(*this))
+ return this;
+
+ if (blend_to_identity)
+ return SkewTransformOperation::Create(blink::Blend(angle_x_, 0.0, progress),
+ blink::Blend(angle_y_, 0.0, progress),
+ type_);
+
+ const SkewTransformOperation* from_op =
+ static_cast<const SkewTransformOperation*>(from);
+ double from_angle_x = from_op ? from_op->angle_x_ : 0;
+ double from_angle_y = from_op ? from_op->angle_y_ : 0;
+ return SkewTransformOperation::Create(
+ blink::Blend(from_angle_x, angle_x_, progress),
+ blink::Blend(from_angle_y, angle_y_, progress), type_);
+}
+
+bool SkewTransformOperation::CanBlendWith(
+ const TransformOperation& other) const {
+ return other.GetType() == kSkew || other.GetType() == kSkewX ||
+ other.GetType() == kSkewY;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/transforms/skew_transform_operation.h b/chromium/third_party/blink/renderer/platform/transforms/skew_transform_operation.h
new file mode 100644
index 00000000000..11047965058
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/transforms/skew_transform_operation.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_SKEW_TRANSFORM_OPERATION_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_SKEW_TRANSFORM_OPERATION_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/transforms/transform_operation.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT SkewTransformOperation final : public TransformOperation {
+ public:
+ static scoped_refptr<SkewTransformOperation> Create(double angle_x,
+ double angle_y,
+ OperationType type) {
+ return base::AdoptRef(new SkewTransformOperation(angle_x, angle_y, type));
+ }
+
+ double AngleX() const { return angle_x_; }
+ double AngleY() const { return angle_y_; }
+
+ virtual bool CanBlendWith(const TransformOperation& other) const;
+
+ static bool IsMatchingOperationType(OperationType type) {
+ return type == kSkewX || type == kSkewY || type == kSkew;
+ }
+
+ private:
+ OperationType GetType() const override { return type_; }
+
+ bool operator==(const TransformOperation& o) const override {
+ if (!IsSameType(o))
+ return false;
+ const SkewTransformOperation* s =
+ static_cast<const SkewTransformOperation*>(&o);
+ return angle_x_ == s->angle_x_ && angle_y_ == s->angle_y_;
+ }
+
+ void Apply(TransformationMatrix& transform, const FloatSize&) const override {
+ transform.Skew(angle_x_, angle_y_);
+ }
+
+ scoped_refptr<TransformOperation> Blend(
+ const TransformOperation* from,
+ double progress,
+ bool blend_to_identity = false) override;
+ scoped_refptr<TransformOperation> Zoom(double factor) final { return this; }
+
+ SkewTransformOperation(double angle_x, double angle_y, OperationType type)
+ : angle_x_(angle_x), angle_y_(angle_y), type_(type) {}
+
+ double angle_x_;
+ double angle_y_;
+ OperationType type_;
+};
+
+DEFINE_TRANSFORM_TYPE_CASTS(SkewTransformOperation);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_SKEW_TRANSFORM_OPERATION_H_
diff --git a/chromium/third_party/blink/renderer/platform/transforms/transform_operation.h b/chromium/third_party/blink/renderer/platform/transforms/transform_operation.h
new file mode 100644
index 00000000000..2cef1af9a3f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/transforms/transform_operation.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_TRANSFORM_OPERATION_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_TRANSFORM_OPERATION_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/geometry/float_size.h"
+#include "third_party/blink/renderer/platform/transforms/transformation_matrix.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+
+namespace blink {
+
+// CSS Transforms (may become part of CSS3)
+
+class PLATFORM_EXPORT TransformOperation
+ : public RefCounted<TransformOperation> {
+ WTF_MAKE_NONCOPYABLE(TransformOperation);
+
+ public:
+ enum OperationType {
+ kScaleX,
+ kScaleY,
+ kScale,
+ kTranslateX,
+ kTranslateY,
+ kTranslate,
+ kRotate,
+ kRotateZ = kRotate,
+ kSkewX,
+ kSkewY,
+ kSkew,
+ kMatrix,
+ kScaleZ,
+ kScale3D,
+ kTranslateZ,
+ kTranslate3D,
+ kRotateX,
+ kRotateY,
+ kRotate3D,
+ kMatrix3D,
+ kPerspective,
+ kInterpolated,
+ kIdentity,
+ kRotateAroundOrigin,
+ };
+
+ TransformOperation() = default;
+ virtual ~TransformOperation() = default;
+
+ virtual bool operator==(const TransformOperation&) const = 0;
+ bool operator!=(const TransformOperation& o) const { return !(*this == o); }
+
+ virtual void Apply(TransformationMatrix&,
+ const FloatSize& border_box_size) const = 0;
+
+ virtual scoped_refptr<TransformOperation> Blend(
+ const TransformOperation* from,
+ double progress,
+ bool blend_to_identity = false) = 0;
+ virtual scoped_refptr<TransformOperation> Zoom(double factor) = 0;
+
+ virtual OperationType GetType() const = 0;
+
+ // https://drafts.csswg.org/css-transforms/#transform-primitives
+ virtual OperationType PrimitiveType() const { return GetType(); }
+
+ bool IsSameType(const TransformOperation& other) const {
+ return other.GetType() == GetType();
+ }
+ virtual bool CanBlendWith(const TransformOperation& other) const = 0;
+
+ bool Is3DOperation() const {
+ OperationType op_type = GetType();
+ return op_type == kScaleZ || op_type == kScale3D ||
+ op_type == kTranslateZ || op_type == kTranslate3D ||
+ op_type == kRotateX || op_type == kRotateY || op_type == kRotate3D ||
+ op_type == kMatrix3D || op_type == kPerspective ||
+ op_type == kInterpolated;
+ }
+
+ virtual bool HasNonTrivial3DComponent() const { return Is3DOperation(); }
+
+ virtual bool DependsOnBoxSize() const { return false; }
+};
+
+#define DEFINE_TRANSFORM_TYPE_CASTS(thisType) \
+ DEFINE_TYPE_CASTS(thisType, TransformOperation, transform, \
+ thisType::IsMatchingOperationType(transform->GetType()), \
+ thisType::IsMatchingOperationType(transform.GetType()))
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_TRANSFORM_OPERATION_H_
diff --git a/chromium/third_party/blink/renderer/platform/transforms/transform_operations.cc b/chromium/third_party/blink/renderer/platform/transforms/transform_operations.cc
new file mode 100644
index 00000000000..77b56ea934b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/transforms/transform_operations.cc
@@ -0,0 +1,435 @@
+/*
+ * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "third_party/blink/renderer/platform/transforms/transform_operations.h"
+
+#include <algorithm>
+#include "third_party/blink/renderer/platform/animation/animation_utilities.h"
+#include "third_party/blink/renderer/platform/geometry/float_box.h"
+#include "third_party/blink/renderer/platform/transforms/identity_transform_operation.h"
+#include "third_party/blink/renderer/platform/transforms/interpolated_transform_operation.h"
+#include "third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.h"
+#include "third_party/blink/renderer/platform/transforms/rotate_transform_operation.h"
+
+namespace blink {
+
+TransformOperations::TransformOperations(bool make_identity) {
+ if (make_identity)
+ operations_.push_back(IdentityTransformOperation::Create());
+}
+
+bool TransformOperations::operator==(const TransformOperations& o) const {
+ if (operations_.size() != o.operations_.size())
+ return false;
+
+ unsigned s = operations_.size();
+ for (unsigned i = 0; i < s; i++) {
+ if (*operations_[i] != *o.operations_[i])
+ return false;
+ }
+
+ return true;
+}
+
+bool TransformOperations::OperationsMatch(
+ const TransformOperations& other) const {
+ size_t num_operations = Operations().size();
+ if (num_operations != other.Operations().size())
+ return false;
+
+ for (size_t i = 0; i < num_operations; ++i) {
+ if (Operations()[i]->PrimitiveType() !=
+ other.Operations()[i]->PrimitiveType()) {
+ return false;
+ }
+ }
+ return true;
+}
+
+TransformOperations TransformOperations::BlendByMatchingOperations(
+ const TransformOperations& from,
+ const double& progress) const {
+ TransformOperations result;
+
+ unsigned from_size = from.Operations().size();
+ unsigned to_size = Operations().size();
+ unsigned size = std::max(from_size, to_size);
+ for (unsigned i = 0; i < size; i++) {
+ scoped_refptr<TransformOperation> from_operation =
+ (i < from_size) ? from.Operations()[i].get() : nullptr;
+ scoped_refptr<TransformOperation> to_operation =
+ (i < to_size) ? Operations()[i].get() : nullptr;
+ scoped_refptr<TransformOperation> blended_operation =
+ to_operation
+ ? to_operation->Blend(from_operation.get(), progress)
+ : (from_operation ? from_operation->Blend(nullptr, progress, true)
+ : nullptr);
+ if (blended_operation)
+ result.Operations().push_back(blended_operation);
+ else {
+ scoped_refptr<TransformOperation> identity_operation =
+ IdentityTransformOperation::Create();
+ if (progress > 0.5)
+ result.Operations().push_back(to_operation ? to_operation
+ : identity_operation);
+ else
+ result.Operations().push_back(from_operation ? from_operation
+ : identity_operation);
+ }
+ }
+
+ return result;
+}
+
+scoped_refptr<TransformOperation>
+TransformOperations::BlendByUsingMatrixInterpolation(
+ const TransformOperations& from,
+ double progress) const {
+ if (DependsOnBoxSize() || from.DependsOnBoxSize())
+ return InterpolatedTransformOperation::Create(from, *this, progress);
+
+ // Evaluate blended matrix here to avoid creating a nested data structure of
+ // unbounded depth.
+ TransformationMatrix from_transform;
+ TransformationMatrix to_transform;
+ from.Apply(FloatSize(), from_transform);
+ Apply(FloatSize(), to_transform);
+ to_transform.Blend(from_transform, progress);
+ return Matrix3DTransformOperation::Create(to_transform);
+}
+
+// https://drafts.csswg.org/css-transforms-1/#interpolation-of-transforms
+TransformOperations TransformOperations::Blend(const TransformOperations& from,
+ double progress) const {
+ if (from == *this || (!from.size() && !size()))
+ return *this;
+
+ // If either list is empty, use blendByMatchingOperations which has special
+ // logic for this case.
+ if (!from.size() || !size() || from.OperationsMatch(*this))
+ return BlendByMatchingOperations(from, progress);
+
+ TransformOperations result;
+ result.Operations().push_back(
+ BlendByUsingMatrixInterpolation(from, progress));
+ return result;
+}
+
+static void FindCandidatesInPlane(double px,
+ double py,
+ double nz,
+ double* candidates,
+ int* num_candidates) {
+ // The angle that this point is rotated with respect to the plane nz
+ double phi = atan2(px, py);
+
+ *num_candidates = 4;
+ candidates[0] = phi; // The element at 0deg (maximum x)
+
+ for (int i = 1; i < *num_candidates; ++i)
+ candidates[i] = candidates[i - 1] + M_PI_2; // every 90 deg
+ if (nz < 0.f) {
+ for (int i = 0; i < *num_candidates; ++i)
+ candidates[i] *= -1;
+ }
+}
+
+// This method returns the bounding box that contains the starting point,
+// the ending point, and any of the extrema (in each dimension) found across
+// the circle described by the arc. These are then filtered to points that
+// actually reside on the arc.
+static void BoundingBoxForArc(const FloatPoint3D& point,
+ const RotateTransformOperation& from_transform,
+ const RotateTransformOperation& to_transform,
+ double min_progress,
+ double max_progress,
+ FloatBox& box) {
+ double candidates[6];
+ int num_candidates = 0;
+
+ FloatPoint3D axis(from_transform.Axis());
+ double from_degrees = from_transform.Angle();
+ double to_degrees = to_transform.Angle();
+
+ if (axis.Dot(to_transform.Axis()) < 0)
+ to_degrees *= -1;
+
+ from_degrees = Blend(from_degrees, to_transform.Angle(), min_progress);
+ to_degrees = Blend(to_degrees, from_transform.Angle(), 1.0 - max_progress);
+ if (from_degrees > to_degrees)
+ std::swap(from_degrees, to_degrees);
+
+ TransformationMatrix from_matrix;
+ TransformationMatrix to_matrix;
+ from_matrix.Rotate3d(from_transform.X(), from_transform.Y(),
+ from_transform.Z(), from_degrees);
+ to_matrix.Rotate3d(from_transform.X(), from_transform.Y(), from_transform.Z(),
+ to_degrees);
+
+ FloatPoint3D from_point = from_matrix.MapPoint(point);
+ FloatPoint3D to_point = to_matrix.MapPoint(point);
+
+ if (box.IsEmpty())
+ box.SetOrigin(from_point);
+ else
+ box.ExpandTo(from_point);
+
+ box.ExpandTo(to_point);
+
+ switch (from_transform.GetType()) {
+ case TransformOperation::kRotateX:
+ FindCandidatesInPlane(point.Y(), point.Z(), from_transform.X(),
+ candidates, &num_candidates);
+ break;
+ case TransformOperation::kRotateY:
+ FindCandidatesInPlane(point.Z(), point.X(), from_transform.Y(),
+ candidates, &num_candidates);
+ break;
+ case TransformOperation::kRotateZ:
+ FindCandidatesInPlane(point.X(), point.Y(), from_transform.Z(),
+ candidates, &num_candidates);
+ break;
+ default: {
+ FloatPoint3D normal = axis;
+ if (normal.IsZero())
+ return;
+ normal.Normalize();
+ FloatPoint3D origin;
+ FloatPoint3D to_point = point - origin;
+ FloatPoint3D center = origin + normal * to_point.Dot(normal);
+ FloatPoint3D v1 = point - center;
+ if (v1.IsZero())
+ return;
+
+ v1.Normalize();
+ FloatPoint3D v2 = normal.Cross(v1);
+ // v1 is the basis vector in the direction of the point.
+ // i.e. with a rotation of 0, v1 is our +x vector.
+ // v2 is a perpenticular basis vector of our plane (+y).
+
+ // Take the parametric equation of a circle.
+ // (x = r*cos(t); y = r*sin(t);
+ // We can treat that as a circle on the plane v1xv2
+ // From that we get the parametric equations for a circle on the
+ // plane in 3d space of
+ // x(t) = r*cos(t)*v1.x + r*sin(t)*v2.x + cx
+ // y(t) = r*cos(t)*v1.y + r*sin(t)*v2.y + cy
+ // z(t) = r*cos(t)*v1.z + r*sin(t)*v2.z + cz
+ // taking the derivative of (x, y, z) and solving for 0 gives us our
+ // maximum/minimum x, y, z values
+ // x'(t) = r*cos(t)*v2.x - r*sin(t)*v1.x = 0
+ // tan(t) = v2.x/v1.x
+ // t = atan2(v2.x, v1.x) + n*M_PI;
+
+ candidates[0] = atan2(v2.X(), v1.X());
+ candidates[1] = candidates[0] + M_PI;
+ candidates[2] = atan2(v2.Y(), v1.Y());
+ candidates[3] = candidates[2] + M_PI;
+ candidates[4] = atan2(v2.Z(), v1.Z());
+ candidates[5] = candidates[4] + M_PI;
+ num_candidates = 6;
+ } break;
+ }
+
+ double min_radians = deg2rad(from_degrees);
+ double max_radians = deg2rad(to_degrees);
+ // Once we have the candidates, we now filter them down to ones that
+ // actually live on the arc, rather than the entire circle.
+ for (int i = 0; i < num_candidates; ++i) {
+ double radians = candidates[i];
+
+ while (radians < min_radians)
+ radians += 2.0 * M_PI;
+ while (radians > max_radians)
+ radians -= 2.0 * M_PI;
+ if (radians < min_radians)
+ continue;
+
+ TransformationMatrix rotation;
+ rotation.Rotate3d(axis.X(), axis.Y(), axis.Z(), rad2deg(radians));
+ box.ExpandTo(rotation.MapPoint(point));
+ }
+}
+
+bool TransformOperations::BlendedBoundsForBox(const FloatBox& box,
+ const TransformOperations& from,
+ const double& min_progress,
+ const double& max_progress,
+ FloatBox* bounds) const {
+ int from_size = from.Operations().size();
+ int to_size = Operations().size();
+ int size = std::max(from_size, to_size);
+
+ *bounds = box;
+ for (int i = size - 1; i >= 0; i--) {
+ scoped_refptr<TransformOperation> from_operation =
+ (i < from_size) ? from.Operations()[i] : nullptr;
+ scoped_refptr<TransformOperation> to_operation =
+ (i < to_size) ? Operations()[i] : nullptr;
+
+ DCHECK(from_operation || to_operation);
+ TransformOperation::OperationType interpolation_type =
+ to_operation ? to_operation->GetType() : from_operation->GetType();
+ if (from_operation && to_operation &&
+ !from_operation->CanBlendWith(*to_operation.get()))
+ return false;
+
+ switch (interpolation_type) {
+ case TransformOperation::kIdentity:
+ bounds->ExpandTo(box);
+ continue;
+ case TransformOperation::kTranslate:
+ case TransformOperation::kTranslateX:
+ case TransformOperation::kTranslateY:
+ case TransformOperation::kTranslateZ:
+ case TransformOperation::kTranslate3D:
+ case TransformOperation::kScale:
+ case TransformOperation::kScaleX:
+ case TransformOperation::kScaleY:
+ case TransformOperation::kScaleZ:
+ case TransformOperation::kScale3D:
+ case TransformOperation::kSkew:
+ case TransformOperation::kSkewX:
+ case TransformOperation::kSkewY:
+ case TransformOperation::kPerspective: {
+ scoped_refptr<TransformOperation> from_transform;
+ scoped_refptr<TransformOperation> to_transform;
+ if (!to_operation) {
+ from_transform = from_operation->Blend(to_operation.get(),
+ 1 - min_progress, false);
+ to_transform = from_operation->Blend(to_operation.get(),
+ 1 - max_progress, false);
+ } else {
+ from_transform =
+ to_operation->Blend(from_operation.get(), min_progress, false);
+ to_transform =
+ to_operation->Blend(from_operation.get(), max_progress, false);
+ }
+ if (!from_transform || !to_transform)
+ continue;
+ TransformationMatrix from_matrix;
+ TransformationMatrix to_matrix;
+ from_transform->Apply(from_matrix, FloatSize());
+ to_transform->Apply(to_matrix, FloatSize());
+ FloatBox from_box = *bounds;
+ FloatBox to_box = *bounds;
+ from_matrix.TransformBox(from_box);
+ to_matrix.TransformBox(to_box);
+ *bounds = from_box;
+ bounds->ExpandTo(to_box);
+ continue;
+ }
+ case TransformOperation::kRotate: // This is also RotateZ
+ case TransformOperation::kRotate3D:
+ case TransformOperation::kRotateX:
+ case TransformOperation::kRotateY: {
+ scoped_refptr<RotateTransformOperation> identity_rotation;
+ const RotateTransformOperation* from_rotation = nullptr;
+ const RotateTransformOperation* to_rotation = nullptr;
+ if (from_operation) {
+ from_rotation = static_cast<const RotateTransformOperation*>(
+ from_operation.get());
+ if (from_rotation->Axis().IsZero())
+ from_rotation = nullptr;
+ }
+
+ if (to_operation) {
+ to_rotation =
+ static_cast<const RotateTransformOperation*>(to_operation.get());
+ if (to_rotation->Axis().IsZero())
+ to_rotation = nullptr;
+ }
+
+ double from_angle;
+ double to_angle;
+ FloatPoint3D axis;
+ if (!RotateTransformOperation::GetCommonAxis(
+ from_rotation, to_rotation, axis, from_angle, to_angle)) {
+ return false;
+ }
+
+ if (!from_rotation) {
+ identity_rotation = RotateTransformOperation::Create(
+ axis.X(), axis.Y(), axis.Z(), 0,
+ from_operation ? from_operation->GetType()
+ : to_operation->GetType());
+ from_rotation = identity_rotation.get();
+ }
+
+ if (!to_rotation) {
+ if (!identity_rotation)
+ identity_rotation = RotateTransformOperation::Create(
+ axis.X(), axis.Y(), axis.Z(), 0,
+ from_operation ? from_operation->GetType()
+ : to_operation->GetType());
+ to_rotation = identity_rotation.get();
+ }
+
+ FloatBox from_box = *bounds;
+ bool first = true;
+ for (size_t i = 0; i < 2; ++i) {
+ for (size_t j = 0; j < 2; ++j) {
+ for (size_t k = 0; k < 2; ++k) {
+ FloatBox bounds_for_arc;
+ FloatPoint3D corner(from_box.X(), from_box.Y(), from_box.Z());
+ corner +=
+ FloatPoint3D(i * from_box.Width(), j * from_box.Height(),
+ k * from_box.Depth());
+ BoundingBoxForArc(corner, *from_rotation, *to_rotation,
+ min_progress, max_progress, bounds_for_arc);
+ if (first) {
+ *bounds = bounds_for_arc;
+ first = false;
+ } else {
+ bounds->ExpandTo(bounds_for_arc);
+ }
+ }
+ }
+ }
+ }
+ continue;
+ case TransformOperation::kMatrix:
+ case TransformOperation::kMatrix3D:
+ case TransformOperation::kInterpolated:
+ case TransformOperation::kRotateAroundOrigin:
+ return false;
+ }
+ }
+
+ return true;
+}
+
+TransformOperations TransformOperations::Add(
+ const TransformOperations& addend) const {
+ TransformOperations result;
+ result.operations_ = Operations();
+ result.operations_.AppendVector(addend.Operations());
+ return result;
+}
+
+TransformOperations TransformOperations::Zoom(double factor) const {
+ TransformOperations result;
+ for (auto& transform_operation : operations_)
+ result.operations_.push_back(transform_operation->Zoom(factor));
+ return result;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/transforms/transform_operations.h b/chromium/third_party/blink/renderer/platform/transforms/transform_operations.h
new file mode 100644
index 00000000000..d932a60dbcd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/transforms/transform_operations.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_TRANSFORM_OPERATIONS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_TRANSFORM_OPERATIONS_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/geometry/layout_size.h"
+#include "third_party/blink/renderer/platform/transforms/transform_operation.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+class FloatBox;
+
+class PLATFORM_EXPORT EmptyTransformOperations final {
+ DISALLOW_NEW();
+};
+
+class PLATFORM_EXPORT TransformOperations {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ explicit TransformOperations(bool make_identity = false);
+ TransformOperations(const EmptyTransformOperations&) {}
+
+ bool operator==(const TransformOperations& o) const;
+ bool operator!=(const TransformOperations& o) const { return !(*this == o); }
+
+ void Apply(const FloatSize& sz, TransformationMatrix& t) const {
+ for (auto& operation : operations_)
+ operation->Apply(t, sz);
+ }
+
+ // Return true if any of the operation types are 3D operation types (even if
+ // the values describe affine transforms)
+ bool Has3DOperation() const {
+ for (auto& operation : operations_)
+ if (operation->Is3DOperation())
+ return true;
+ return false;
+ }
+
+ // Returns true if any operation has a non-trivial component in the Z
+ // axis.
+ bool HasNonTrivial3DComponent() const {
+ for (auto& operation : operations_) {
+ if (operation->HasNonTrivial3DComponent())
+ return true;
+ }
+ return false;
+ }
+
+ bool DependsOnBoxSize() const {
+ for (auto& operation : operations_) {
+ if (operation->DependsOnBoxSize())
+ return true;
+ }
+ return false;
+ }
+
+ bool OperationsMatch(const TransformOperations&) const;
+
+ void clear() { operations_.clear(); }
+
+ Vector<scoped_refptr<TransformOperation>>& Operations() {
+ return operations_;
+ }
+ const Vector<scoped_refptr<TransformOperation>>& Operations() const {
+ return operations_;
+ }
+
+ size_t size() const { return operations_.size(); }
+ const TransformOperation* at(size_t index) const {
+ return index < operations_.size() ? operations_.at(index).get() : nullptr;
+ }
+
+ bool BlendedBoundsForBox(const FloatBox&,
+ const TransformOperations& from,
+ const double& min_progress,
+ const double& max_progress,
+ FloatBox* bounds) const;
+ TransformOperations BlendByMatchingOperations(const TransformOperations& from,
+ const double& progress) const;
+ scoped_refptr<TransformOperation> BlendByUsingMatrixInterpolation(
+ const TransformOperations& from,
+ double progress) const;
+ TransformOperations Blend(const TransformOperations& from,
+ double progress) const;
+ TransformOperations Add(const TransformOperations& addend) const;
+ TransformOperations Zoom(double factor) const;
+
+ private:
+ Vector<scoped_refptr<TransformOperation>> operations_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_TRANSFORM_OPERATIONS_H_
diff --git a/chromium/third_party/blink/renderer/platform/transforms/transform_operations_test.cc b/chromium/third_party/blink/renderer/platform/transforms/transform_operations_test.cc
new file mode 100644
index 00000000000..ca2d669c5f2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/transforms/transform_operations_test.cc
@@ -0,0 +1,548 @@
+/*
+ * Copyright (C) 2014 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/transforms/transform_operations.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/geometry/float_box.h"
+#include "third_party/blink/renderer/platform/geometry/float_box_test_helpers.h"
+#include "third_party/blink/renderer/platform/transforms/identity_transform_operation.h"
+#include "third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.h"
+#include "third_party/blink/renderer/platform/transforms/matrix_transform_operation.h"
+#include "third_party/blink/renderer/platform/transforms/perspective_transform_operation.h"
+#include "third_party/blink/renderer/platform/transforms/rotate_transform_operation.h"
+#include "third_party/blink/renderer/platform/transforms/scale_transform_operation.h"
+#include "third_party/blink/renderer/platform/transforms/skew_transform_operation.h"
+#include "third_party/blink/renderer/platform/transforms/translate_transform_operation.h"
+
+namespace blink {
+
+static const TransformOperations kIdentityOperations;
+
+static void EmpiricallyTestBounds(const TransformOperations& from,
+ const TransformOperations& to,
+ const double& min_progress,
+ const double& max_progress) {
+ FloatBox box(200, 500, 100, 100, 300, 200);
+ FloatBox bounds;
+
+ EXPECT_TRUE(
+ to.BlendedBoundsForBox(box, from, min_progress, max_progress, &bounds));
+ bool first_time = true;
+
+ FloatBox empirical_bounds;
+ static const size_t kNumSteps = 10;
+ for (size_t step = 0; step < kNumSteps; ++step) {
+ float t = step / (kNumSteps - 1);
+ t = min_progress + (max_progress - min_progress) * t;
+ TransformOperations operations = from.Blend(to, t);
+ TransformationMatrix matrix;
+ operations.Apply(FloatSize(0, 0), matrix);
+ FloatBox transformed = box;
+ matrix.TransformBox(transformed);
+
+ if (first_time)
+ empirical_bounds = transformed;
+ else
+ empirical_bounds.UnionBounds(transformed);
+ first_time = false;
+ }
+
+ ASSERT_PRED_FORMAT2(FloatBoxTest::AssertContains, bounds, empirical_bounds);
+}
+
+TEST(TransformOperationsTest, AbsoluteAnimatedTranslatedBoundsTest) {
+ TransformOperations from_ops;
+ TransformOperations to_ops;
+ from_ops.Operations().push_back(TranslateTransformOperation::Create(
+ Length(-30, blink::kFixed), Length(20, blink::kFixed), 15,
+ TransformOperation::kTranslate3D));
+ to_ops.Operations().push_back(TranslateTransformOperation::Create(
+ Length(10, blink::kFixed), Length(10, blink::kFixed), 200,
+ TransformOperation::kTranslate3D));
+ FloatBox box(0, 0, 0, 10, 10, 10);
+ FloatBox bounds;
+
+ EXPECT_TRUE(
+ to_ops.BlendedBoundsForBox(box, kIdentityOperations, 0, 1, &bounds));
+ EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual,
+ FloatBox(0, 0, 0, 20, 20, 210), bounds);
+
+ EXPECT_TRUE(
+ kIdentityOperations.BlendedBoundsForBox(box, to_ops, 0, 1, &bounds));
+ EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual,
+ FloatBox(0, 0, 0, 20, 20, 210), bounds);
+
+ EXPECT_TRUE(
+ kIdentityOperations.BlendedBoundsForBox(box, from_ops, 0, 1, &bounds));
+ EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual,
+ FloatBox(-30, 0, 0, 40, 30, 25), bounds);
+
+ EXPECT_TRUE(to_ops.BlendedBoundsForBox(box, from_ops, 0, 1, &bounds));
+ EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual,
+ FloatBox(-30, 10, 15, 50, 20, 195), bounds);
+
+ EXPECT_TRUE(to_ops.BlendedBoundsForBox(box, from_ops, -0.5, 1.25, &bounds));
+ EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual,
+ FloatBox(-50, 7.5, -77.5, 80, 27.5, 333.75), bounds);
+}
+
+TEST(TransformOperationsTest, EmpiricalAnimatedTranslatedBoundsTest) {
+ float test_transforms[][2][3] = {{{0, 0, 0}, {10, 10, 0}},
+ {{-100, 202.5, -32.6}, {43.2, 56.1, 89.75}},
+ {{43.2, 56.1, 89.75}, {-100, 202.5, -32.6}}};
+
+ // All progressions for animations start and end at 0, 1 respectively,
+ // we can go outside of these bounds, but will always at least contain
+ // [0,1].
+ float progress[][2] = {{0, 1}, {-.25, 1.25}};
+
+ for (size_t i = 0; i < WTF_ARRAY_LENGTH(test_transforms); ++i) {
+ for (size_t j = 0; j < WTF_ARRAY_LENGTH(progress); ++j) {
+ TransformOperations from_ops;
+ TransformOperations to_ops;
+ from_ops.Operations().push_back(TranslateTransformOperation::Create(
+ Length(test_transforms[i][0][0], blink::kFixed),
+ Length(test_transforms[i][0][1], blink::kFixed),
+ test_transforms[i][0][2], TransformOperation::kTranslate3D));
+ to_ops.Operations().push_back(TranslateTransformOperation::Create(
+ Length(test_transforms[i][1][0], blink::kFixed),
+ Length(test_transforms[i][1][1], blink::kFixed),
+ test_transforms[i][1][2], TransformOperation::kTranslate3D));
+ EmpiricallyTestBounds(from_ops, to_ops, progress[j][0], progress[j][1]);
+ }
+ }
+}
+
+TEST(TransformOperationsTest, AbsoluteAnimatedScaleBoundsTest) {
+ TransformOperations from_ops;
+ TransformOperations to_ops;
+ from_ops.Operations().push_back(
+ ScaleTransformOperation::Create(4, -3, TransformOperation::kScale));
+ to_ops.Operations().push_back(
+ ScaleTransformOperation::Create(5, 2, TransformOperation::kScale));
+
+ FloatBox box(0, 0, 0, 10, 10, 10);
+ FloatBox bounds;
+
+ EXPECT_TRUE(
+ to_ops.BlendedBoundsForBox(box, kIdentityOperations, 0, 1, &bounds));
+ EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual,
+ FloatBox(0, 0, 0, 50, 20, 10), bounds);
+
+ EXPECT_TRUE(
+ kIdentityOperations.BlendedBoundsForBox(box, to_ops, 0, 1, &bounds));
+ EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual,
+ FloatBox(0, 0, 0, 50, 20, 10), bounds);
+
+ EXPECT_TRUE(
+ kIdentityOperations.BlendedBoundsForBox(box, from_ops, 0, 1, &bounds));
+ EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual,
+ FloatBox(0, -30, 0, 40, 40, 10), bounds);
+
+ EXPECT_TRUE(to_ops.BlendedBoundsForBox(box, from_ops, 0, 1, &bounds));
+ EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual,
+ FloatBox(0, -30, 0, 50, 50, 10), bounds);
+
+ EXPECT_TRUE(to_ops.BlendedBoundsForBox(box, from_ops, -0.5, 1.25, &bounds));
+ EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual,
+ FloatBox(0, -55, 0, 52.5, 87.5, 10), bounds);
+}
+
+TEST(TransformOperationsTest, EmpiricalAnimatedScaleBoundsTest) {
+ float test_transforms[][2][3] = {{{1, 1, 1}, {10, 10, -32}},
+ {{1, 2, 5}, {-1, -2, -4}},
+ {{0, 0, 0}, {1, 2, 3}},
+ {{0, 0, 0}, {0, 0, 0}}};
+
+ // All progressions for animations start and end at 0, 1 respectively,
+ // we can go outside of these bounds, but will always at least contain
+ // [0,1].
+ float progress[][2] = {{0, 1}, {-.25f, 1.25f}};
+
+ for (size_t i = 0; i < WTF_ARRAY_LENGTH(test_transforms); ++i) {
+ for (size_t j = 0; j < WTF_ARRAY_LENGTH(progress); ++j) {
+ TransformOperations from_ops;
+ TransformOperations to_ops;
+ from_ops.Operations().push_back(TranslateTransformOperation::Create(
+ Length(test_transforms[i][0][0], blink::kFixed),
+ Length(test_transforms[i][0][1], blink::kFixed),
+ test_transforms[i][0][2], TransformOperation::kTranslate3D));
+ to_ops.Operations().push_back(TranslateTransformOperation::Create(
+ Length(test_transforms[i][1][0], blink::kFixed),
+ Length(test_transforms[i][1][1], blink::kFixed),
+ test_transforms[i][1][2], TransformOperation::kTranslate3D));
+ EmpiricallyTestBounds(from_ops, to_ops, progress[j][0], progress[j][1]);
+ }
+ }
+}
+
+TEST(TransformOperationsTest, AbsoluteAnimatedRotationBounds) {
+ TransformOperations from_ops;
+ TransformOperations to_ops;
+ from_ops.Operations().push_back(
+ RotateTransformOperation::Create(0, TransformOperation::kRotate));
+ to_ops.Operations().push_back(
+ RotateTransformOperation::Create(360, TransformOperation::kRotate));
+ float sqrt2 = sqrt(2.0f);
+ FloatBox box(-sqrt2, -sqrt2, 0, sqrt2, sqrt2, 0);
+ FloatBox bounds;
+
+ // Since we're rotating 360 degrees, any box with dimensions between 0 and
+ // 2 * sqrt(2) should give the same result.
+ float sizes[] = {0, 0.1f, sqrt2, 2 * sqrt2};
+ to_ops.BlendedBoundsForBox(box, from_ops, 0, 1, &bounds);
+ for (size_t i = 0; i < WTF_ARRAY_LENGTH(sizes); ++i) {
+ box.SetSize(FloatPoint3D(sizes[i], sizes[i], 0));
+
+ EXPECT_TRUE(to_ops.BlendedBoundsForBox(box, from_ops, 0, 1, &bounds));
+ EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual,
+ FloatBox(-2, -2, 0, 4, 4, 0), bounds);
+ }
+}
+
+TEST(TransformOperationsTest, AbsoluteAnimatedExtremeRotationBounds) {
+ // If the normal is off-plane, we can have up to 6 exrema (min/max in each
+ // dimension between) the endpoints of the arg. This makes sure we are
+ // catching all 6.
+ TransformOperations from_ops;
+ TransformOperations to_ops;
+ from_ops.Operations().push_back(RotateTransformOperation::Create(
+ 1, 1, 1, 30, TransformOperation::kRotate3D));
+ to_ops.Operations().push_back(RotateTransformOperation::Create(
+ 1, 1, 1, 390, TransformOperation::kRotate3D));
+
+ FloatBox box(1, 0, 0, 0, 0, 0);
+ FloatBox bounds;
+ float min = -1 / 3.0f;
+ float max = 1;
+ float size = max - min;
+ EXPECT_TRUE(to_ops.BlendedBoundsForBox(box, from_ops, 0, 1, &bounds));
+ EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual,
+ FloatBox(min, min, min, size, size, size), bounds);
+}
+
+TEST(TransformOperationsTest, AbsoluteAnimatedAxisRotationBounds) {
+ // We can handle rotations about a single axis. If the axes are different,
+ // we revert to matrix interpolation for which inflated bounds cannot be
+ // computed.
+ TransformOperations from_ops;
+ TransformOperations to_same;
+ TransformOperations to_opposite;
+ TransformOperations to_different;
+ from_ops.Operations().push_back(RotateTransformOperation::Create(
+ 1, 1, 1, 30, TransformOperation::kRotate3D));
+ to_same.Operations().push_back(RotateTransformOperation::Create(
+ 1, 1, 1, 390, TransformOperation::kRotate3D));
+ to_opposite.Operations().push_back(RotateTransformOperation::Create(
+ -1, -1, -1, 390, TransformOperation::kRotate3D));
+ to_different.Operations().push_back(RotateTransformOperation::Create(
+ 1, 3, 1, 390, TransformOperation::kRotate3D));
+
+ FloatBox box(1, 0, 0, 0, 0, 0);
+ FloatBox bounds;
+ EXPECT_TRUE(to_same.BlendedBoundsForBox(box, from_ops, 0, 1, &bounds));
+ EXPECT_FALSE(to_opposite.BlendedBoundsForBox(box, from_ops, 0, 1, &bounds));
+ EXPECT_FALSE(to_different.BlendedBoundsForBox(box, from_ops, 0, 1, &bounds));
+}
+
+TEST(TransformOperationsTest, AbsoluteAnimatedOnAxisRotationBounds) {
+ // If we rotate a point that is on the axis of rotation, the box should not
+ // change at all.
+ TransformOperations from_ops;
+ TransformOperations to_ops;
+ from_ops.Operations().push_back(RotateTransformOperation::Create(
+ 1, 1, 1, 30, TransformOperation::kRotate3D));
+ to_ops.Operations().push_back(RotateTransformOperation::Create(
+ 1, 1, 1, 390, TransformOperation::kRotate3D));
+
+ FloatBox box(1, 1, 1, 0, 0, 0);
+ FloatBox bounds;
+
+ EXPECT_TRUE(to_ops.BlendedBoundsForBox(box, from_ops, 0, 1, &bounds));
+ EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, box, bounds);
+}
+
+// This would have been best as anonymous structs, but |WTF_ARRAY_LENGTH|
+// does not get along with anonymous structs once we support C++11
+// WTF_ARRAY_LENGTH will automatically support anonymous structs.
+
+struct ProblematicAxisTest {
+ double x;
+ double y;
+ double z;
+ FloatBox expected;
+};
+
+TEST(TransformOperationsTest, AbsoluteAnimatedProblematicAxisRotationBounds) {
+ // Zeros in the components of the axis osf rotation turned out to be tricky to
+ // deal with in practice. This function tests some potentially problematic
+ // axes to ensure sane behavior.
+
+ // Some common values used in the expected boxes.
+ float dim1 = 0.292893f;
+ float dim2 = sqrt(2.0f);
+ float dim3 = 2 * dim2;
+
+ ProblematicAxisTest tests[] = {
+ {0, 0, 0, FloatBox(1, 1, 1, 0, 0, 0)},
+ {1, 0, 0, FloatBox(1, -dim2, -dim2, 0, dim3, dim3)},
+ {0, 1, 0, FloatBox(-dim2, 1, -dim2, dim3, 0, dim3)},
+ {0, 0, 1, FloatBox(-dim2, -dim2, 1, dim3, dim3, 0)},
+ {1, 1, 0, FloatBox(dim1, dim1, -1, dim2, dim2, 2)},
+ {0, 1, 1, FloatBox(-1, dim1, dim1, 2, dim2, dim2)},
+ {1, 0, 1, FloatBox(dim1, -1, dim1, dim2, 2, dim2)}};
+
+ for (size_t i = 0; i < WTF_ARRAY_LENGTH(tests); ++i) {
+ float x = tests[i].x;
+ float y = tests[i].y;
+ float z = tests[i].z;
+ TransformOperations from_ops;
+ from_ops.Operations().push_back(RotateTransformOperation::Create(
+ x, y, z, 0, TransformOperation::kRotate3D));
+ TransformOperations to_ops;
+ to_ops.Operations().push_back(RotateTransformOperation::Create(
+ x, y, z, 360, TransformOperation::kRotate3D));
+ FloatBox box(1, 1, 1, 0, 0, 0);
+ FloatBox bounds;
+
+ EXPECT_TRUE(to_ops.BlendedBoundsForBox(box, from_ops, 0, 1, &bounds));
+ EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, tests[i].expected,
+ bounds);
+ }
+}
+
+TEST(TransformOperationsTest, BlendedBoundsForRotationEmpiricalTests) {
+ float axes[][3] = {{1, 1, 1}, {-1, -1, -1}, {-1, 2, 3}, {1, -2, 3},
+ {0, 0, 0}, {1, 0, 0}, {0, 1, 0}, {0, 0, 1},
+ {1, 1, 0}, {0, 1, 1}, {1, 0, 1}, {-1, 0, 0},
+ {0, -1, 0}, {0, 0, -1}, {-1, -1, 0}, {0, -1, -1},
+ {-1, 0, -1}};
+
+ float angles[][2] = {{5, 100}, {10, 5}, {0, 360}, {20, 180},
+ {-20, -180}, {180, -220}, {220, 320}, {1020, 1120},
+ {-3200, 120}, {-9000, -9050}};
+
+ float progress[][2] = {{0, 1}, {-0.25f, 1.25f}};
+
+ for (size_t i = 0; i < WTF_ARRAY_LENGTH(axes); ++i) {
+ for (size_t j = 0; j < WTF_ARRAY_LENGTH(angles); ++j) {
+ for (size_t k = 0; k < WTF_ARRAY_LENGTH(progress); ++k) {
+ float x = axes[i][0];
+ float y = axes[i][1];
+ float z = axes[i][2];
+
+ TransformOperations from_ops;
+ TransformOperations to_ops;
+
+ from_ops.Operations().push_back(RotateTransformOperation::Create(
+ x, y, z, angles[j][0], TransformOperation::kRotate3D));
+ to_ops.Operations().push_back(RotateTransformOperation::Create(
+ x, y, z, angles[j][1], TransformOperation::kRotate3D));
+ EmpiricallyTestBounds(from_ops, to_ops, progress[k][0], progress[k][1]);
+ }
+ }
+ }
+}
+
+TEST(TransformOperationsTest, AbsoluteAnimatedPerspectiveBoundsTest) {
+ TransformOperations from_ops;
+ TransformOperations to_ops;
+ from_ops.Operations().push_back(PerspectiveTransformOperation::Create(20));
+ to_ops.Operations().push_back(PerspectiveTransformOperation::Create(40));
+ FloatBox box(0, 0, 0, 10, 10, 10);
+ FloatBox bounds;
+ to_ops.BlendedBoundsForBox(box, from_ops, 0, 1, &bounds);
+ EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual,
+ FloatBox(0, 0, 0, 20, 20, 20), bounds);
+
+ from_ops.BlendedBoundsForBox(box, to_ops, -0.25, 1.25, &bounds);
+ // The perspective range was [20, 40] and blending will extrapolate that to
+ // [17, 53]. The cube has w/h/d of 10 and the observer is at 17, so the face
+ // closest the observer is 17-10=7.
+ double projected_size = 10.0 / 7.0 * 17.0;
+ EXPECT_PRED_FORMAT2(
+ FloatBoxTest::AssertAlmostEqual,
+ FloatBox(0, 0, 0, projected_size, projected_size, projected_size),
+ bounds);
+}
+
+TEST(TransformOperationsTest, EmpiricalAnimatedPerspectiveBoundsTest) {
+ float depths[][2] = {
+ {600, 400}, {800, 1000}, {800, std::numeric_limits<float>::infinity()}};
+
+ float progress[][2] = {{0, 1}, {-0.1f, 1.1f}};
+
+ for (size_t i = 0; i < WTF_ARRAY_LENGTH(depths); ++i) {
+ for (size_t j = 0; j < WTF_ARRAY_LENGTH(progress); ++j) {
+ TransformOperations from_ops;
+ TransformOperations to_ops;
+
+ from_ops.Operations().push_back(
+ PerspectiveTransformOperation::Create(depths[i][0]));
+ to_ops.Operations().push_back(
+ PerspectiveTransformOperation::Create(depths[i][1]));
+
+ EmpiricallyTestBounds(from_ops, to_ops, progress[j][0], progress[j][1]);
+ }
+ }
+}
+
+TEST(TransformOperationsTest, AnimatedSkewBoundsTest) {
+ TransformOperations from_ops;
+ TransformOperations to_ops;
+ from_ops.Operations().push_back(
+ SkewTransformOperation::Create(-45, 0, TransformOperation::kSkew));
+ to_ops.Operations().push_back(
+ SkewTransformOperation::Create(0, 45, TransformOperation::kSkew));
+ FloatBox box(0, 0, 0, 10, 10, 10);
+ FloatBox bounds;
+
+ to_ops.BlendedBoundsForBox(box, kIdentityOperations, 0, 1, &bounds);
+ ASSERT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual,
+ FloatBox(0, 0, 0, 10, 20, 10), bounds);
+
+ kIdentityOperations.BlendedBoundsForBox(box, from_ops, 0, 1, &bounds);
+ ASSERT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual,
+ FloatBox(-10, 0, 0, 20, 10, 10), bounds);
+
+ to_ops.BlendedBoundsForBox(box, from_ops, 0, 1, &bounds);
+ ASSERT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual,
+ FloatBox(-10, 0, 0, 20, 20, 10), bounds);
+
+ from_ops.BlendedBoundsForBox(box, to_ops, 0, 1, &bounds);
+ ASSERT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual,
+ FloatBox(-10, 0, 0, 20, 20, 10), bounds);
+}
+
+TEST(TransformOperationsTest, NonCommutativeRotations) {
+ TransformOperations from_ops;
+ from_ops.Operations().push_back(RotateTransformOperation::Create(
+ 1, 0, 0, 0, TransformOperation::kRotate3D));
+ from_ops.Operations().push_back(RotateTransformOperation::Create(
+ 0, 1, 0, 0, TransformOperation::kRotate3D));
+ TransformOperations to_ops;
+ to_ops.Operations().push_back(RotateTransformOperation::Create(
+ 1, 0, 0, 45, TransformOperation::kRotate3D));
+ to_ops.Operations().push_back(RotateTransformOperation::Create(
+ 0, 1, 0, 135, TransformOperation::kRotate3D));
+
+ FloatBox box(0, 0, 0, 1, 1, 1);
+ FloatBox bounds;
+
+ double min_progress = 0;
+ double max_progress = 1;
+ EXPECT_TRUE(to_ops.BlendedBoundsForBox(box, from_ops, min_progress,
+ max_progress, &bounds));
+
+ TransformOperations operations = to_ops.Blend(from_ops, max_progress);
+ TransformationMatrix blended_transform;
+ operations.Apply(FloatSize(0, 0), blended_transform);
+
+ FloatPoint3D blended_point(0.9f, 0.9f, 0);
+ blended_point = blended_transform.MapPoint(blended_point);
+ FloatBox expanded_bounds = bounds;
+ expanded_bounds.ExpandTo(blended_point);
+
+ ASSERT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual, bounds, expanded_bounds);
+}
+
+TEST(TransformOperationsTest, AbsoluteSequenceBoundsTest) {
+ TransformOperations from_ops;
+ TransformOperations to_ops;
+
+ from_ops.Operations().push_back(
+ TranslateTransformOperation::Create(Length(1, kFixed), Length(-5, kFixed),
+ 1, TransformOperation::kTranslate3D));
+ from_ops.Operations().push_back(
+ ScaleTransformOperation::Create(-1, 2, 3, TransformOperation::kScale3D));
+ from_ops.Operations().push_back(TranslateTransformOperation::Create(
+ Length(2, kFixed), Length(4, kFixed), -1,
+ TransformOperation::kTranslate3D));
+
+ to_ops.Operations().push_back(TranslateTransformOperation::Create(
+ Length(13, kFixed), Length(-1, kFixed), 5,
+ TransformOperation::kTranslate3D));
+ to_ops.Operations().push_back(
+ ScaleTransformOperation::Create(-3, -2, 5, TransformOperation::kScale3D));
+ to_ops.Operations().push_back(
+ TranslateTransformOperation::Create(Length(6, kFixed), Length(-2, kFixed),
+ 3, TransformOperation::kTranslate3D));
+
+ FloatBox box(1, 2, 3, 4, 4, 4);
+ FloatBox bounds;
+
+ EXPECT_TRUE(to_ops.BlendedBoundsForBox(box, from_ops, -0.5, 1.5, &bounds));
+ EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual,
+ FloatBox(-57, -59, -1, 76, 112, 80), bounds);
+
+ EXPECT_TRUE(to_ops.BlendedBoundsForBox(box, from_ops, 0, 1, &bounds));
+ EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual,
+ FloatBox(-32, -25, 7, 42, 44, 48), bounds);
+
+ EXPECT_TRUE(
+ to_ops.BlendedBoundsForBox(box, kIdentityOperations, 0, 1, &bounds));
+ EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual,
+ FloatBox(-33, -13, 3, 57, 19, 52), bounds);
+
+ EXPECT_TRUE(
+ kIdentityOperations.BlendedBoundsForBox(box, from_ops, 0, 1, &bounds));
+ EXPECT_PRED_FORMAT2(FloatBoxTest::AssertAlmostEqual,
+ FloatBox(-7, -3, 2, 15, 23, 20), bounds);
+}
+
+TEST(TransformOperationsTest, ZoomTest) {
+ double zoom_factor = 1.25;
+
+ FloatPoint3D original_point(2, 3, 4);
+
+ TransformOperations ops;
+ ops.Operations().push_back(
+ TranslateTransformOperation::Create(Length(1, kFixed), Length(2, kFixed),
+ 3, TransformOperation::kTranslate3D));
+ ops.Operations().push_back(PerspectiveTransformOperation::Create(1234));
+ ops.Operations().push_back(
+ Matrix3DTransformOperation::Create(TransformationMatrix(
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)));
+
+ // Apply unzoomed ops to unzoomed units, then zoom in
+ FloatPoint3D unzoomed_point = original_point;
+ TransformOperations unzoomed_ops = ops;
+ TransformationMatrix unzoomed_matrix;
+ ops.Apply(FloatSize(0, 0), unzoomed_matrix);
+ FloatPoint3D result1 = unzoomed_matrix.MapPoint(unzoomed_point);
+ result1.Scale(zoom_factor, zoom_factor, zoom_factor);
+
+ // Apply zoomed ops to zoomed units
+ FloatPoint3D zoomed_point = original_point;
+ zoomed_point.Scale(zoom_factor, zoom_factor, zoom_factor);
+ TransformOperations zoomed_ops = ops.Zoom(zoom_factor);
+ TransformationMatrix zoomed_matrix;
+ zoomed_ops.Apply(FloatSize(0, 0), zoomed_matrix);
+ FloatPoint3D result2 = zoomed_matrix.MapPoint(zoomed_point);
+
+ EXPECT_EQ(result1, result2);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/transforms/transformation_matrix.cc b/chromium/third_party/blink/renderer/platform/transforms/transformation_matrix.cc
new file mode 100644
index 00000000000..2a71d73dad6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/transforms/transformation_matrix.cc
@@ -0,0 +1,1924 @@
+/*
+ * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2009 Torch Mobile, Inc.
+ * Copyright (C) 2013 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/transforms/transformation_matrix.h"
+
+#include <cmath>
+#include <cstdlib>
+
+#include "third_party/blink/renderer/platform/geometry/float_box.h"
+#include "third_party/blink/renderer/platform/geometry/float_quad.h"
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
+#include "third_party/blink/renderer/platform/transforms/affine_transform.h"
+#include "third_party/blink/renderer/platform/transforms/rotation.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/cpu.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+#if defined(ARCH_CPU_X86_64)
+#include <emmintrin.h>
+#endif
+
+#if HAVE_MIPS_MSA_INTRINSICS
+#include "third_party/blink/renderer/platform/cpu/mips/common_macros_msa.h"
+#endif
+
+namespace blink {
+
+//
+// Supporting Math Functions
+//
+// This is a set of function from various places (attributed inline) to do
+// things like inversion and decomposition of a 4x4 matrix. They are used
+// throughout the code
+//
+
+//
+// Adapted from Matrix Inversion by Richard Carling, Graphics Gems
+// <http://tog.acm.org/GraphicsGems/index.html>.
+
+// EULA: The Graphics Gems code is copyright-protected. In other words, you
+// cannot claim the text of the code as your own and resell it. Using the code
+// is permitted in any program, product, or library, non-commercial or
+// commercial. Giving credit is not required, though is a nice gesture. The code
+// comes as-is, and if there are any flaws or problems with any Gems code,
+// nobody involved with Gems - authors, editors, publishers, or webmasters - are
+// to be held responsible. Basically, don't be a jerk, and remember that
+// anything free comes with no guarantee.
+
+typedef double Vector4[4];
+typedef double Vector3[3];
+
+const double kSmallNumber = 1.e-8;
+
+// inverse(original_matrix, inverse_matrix)
+//
+// calculate the inverse of a 4x4 matrix
+//
+// -1
+// A = ___1__ adjoint A
+// det A
+
+// double = determinant2x2(double a, double b, double c, double d)
+//
+// calculate the determinant of a 2x2 matrix.
+
+static double Determinant2x2(double a, double b, double c, double d) {
+ return a * d - b * c;
+}
+
+// double = determinant3x3(a1, a2, a3, b1, b2, b3, c1, c2, c3)
+//
+// Calculate the determinant of a 3x3 matrix
+// in the form
+//
+// | a1, b1, c1 |
+// | a2, b2, c2 |
+// | a3, b3, c3 |
+
+static double Determinant3x3(double a1,
+ double a2,
+ double a3,
+ double b1,
+ double b2,
+ double b3,
+ double c1,
+ double c2,
+ double c3) {
+ return a1 * Determinant2x2(b2, b3, c2, c3) -
+ b1 * Determinant2x2(a2, a3, c2, c3) +
+ c1 * Determinant2x2(a2, a3, b2, b3);
+}
+
+// double = determinant4x4(matrix)
+//
+// calculate the determinant of a 4x4 matrix.
+
+static double Determinant4x4(const TransformationMatrix::Matrix4& m) {
+ // Assign to individual variable names to aid selecting
+ // correct elements
+
+ double a1 = m[0][0];
+ double b1 = m[0][1];
+ double c1 = m[0][2];
+ double d1 = m[0][3];
+
+ double a2 = m[1][0];
+ double b2 = m[1][1];
+ double c2 = m[1][2];
+ double d2 = m[1][3];
+
+ double a3 = m[2][0];
+ double b3 = m[2][1];
+ double c3 = m[2][2];
+ double d3 = m[2][3];
+
+ double a4 = m[3][0];
+ double b4 = m[3][1];
+ double c4 = m[3][2];
+ double d4 = m[3][3];
+
+ return a1 * Determinant3x3(b2, b3, b4, c2, c3, c4, d2, d3, d4) -
+ b1 * Determinant3x3(a2, a3, a4, c2, c3, c4, d2, d3, d4) +
+ c1 * Determinant3x3(a2, a3, a4, b2, b3, b4, d2, d3, d4) -
+ d1 * Determinant3x3(a2, a3, a4, b2, b3, b4, c2, c3, c4);
+}
+
+#if !defined(ARCH_CPU_ARM64) && !HAVE_MIPS_MSA_INTRINSICS
+// adjoint( original_matrix, inverse_matrix )
+//
+// calculate the adjoint of a 4x4 matrix
+//
+// Let a denote the minor determinant of matrix A obtained by
+// ij
+//
+// deleting the ith row and jth column from A.
+//
+// i+j
+// Let b = (-1) a
+// ij ji
+//
+// The matrix B = (b ) is the adjoint of A
+// ij
+
+static inline void Adjoint(const TransformationMatrix::Matrix4& matrix,
+ TransformationMatrix::Matrix4& result) {
+ // Assign to individual variable names to aid
+ // selecting correct values
+ double a1 = matrix[0][0];
+ double b1 = matrix[0][1];
+ double c1 = matrix[0][2];
+ double d1 = matrix[0][3];
+
+ double a2 = matrix[1][0];
+ double b2 = matrix[1][1];
+ double c2 = matrix[1][2];
+ double d2 = matrix[1][3];
+
+ double a3 = matrix[2][0];
+ double b3 = matrix[2][1];
+ double c3 = matrix[2][2];
+ double d3 = matrix[2][3];
+
+ double a4 = matrix[3][0];
+ double b4 = matrix[3][1];
+ double c4 = matrix[3][2];
+ double d4 = matrix[3][3];
+
+ // Row column labeling reversed since we transpose rows & columns
+ result[0][0] = Determinant3x3(b2, b3, b4, c2, c3, c4, d2, d3, d4);
+ result[1][0] = -Determinant3x3(a2, a3, a4, c2, c3, c4, d2, d3, d4);
+ result[2][0] = Determinant3x3(a2, a3, a4, b2, b3, b4, d2, d3, d4);
+ result[3][0] = -Determinant3x3(a2, a3, a4, b2, b3, b4, c2, c3, c4);
+
+ result[0][1] = -Determinant3x3(b1, b3, b4, c1, c3, c4, d1, d3, d4);
+ result[1][1] = Determinant3x3(a1, a3, a4, c1, c3, c4, d1, d3, d4);
+ result[2][1] = -Determinant3x3(a1, a3, a4, b1, b3, b4, d1, d3, d4);
+ result[3][1] = Determinant3x3(a1, a3, a4, b1, b3, b4, c1, c3, c4);
+
+ result[0][2] = Determinant3x3(b1, b2, b4, c1, c2, c4, d1, d2, d4);
+ result[1][2] = -Determinant3x3(a1, a2, a4, c1, c2, c4, d1, d2, d4);
+ result[2][2] = Determinant3x3(a1, a2, a4, b1, b2, b4, d1, d2, d4);
+ result[3][2] = -Determinant3x3(a1, a2, a4, b1, b2, b4, c1, c2, c4);
+
+ result[0][3] = -Determinant3x3(b1, b2, b3, c1, c2, c3, d1, d2, d3);
+ result[1][3] = Determinant3x3(a1, a2, a3, c1, c2, c3, d1, d2, d3);
+ result[2][3] = -Determinant3x3(a1, a2, a3, b1, b2, b3, d1, d2, d3);
+ result[3][3] = Determinant3x3(a1, a2, a3, b1, b2, b3, c1, c2, c3);
+}
+#endif
+
+// Returns false if the matrix is not invertible
+static bool Inverse(const TransformationMatrix::Matrix4& matrix,
+ TransformationMatrix::Matrix4& result) {
+ // Calculate the 4x4 determinant
+ // If the determinant is zero,
+ // then the inverse matrix is not unique.
+ double det = Determinant4x4(matrix);
+
+ if (fabs(det) < kSmallNumber)
+ return false;
+
+#if defined(ARCH_CPU_ARM64)
+ double rdet = 1 / det;
+ const double* mat = &(matrix[0][0]);
+ double* pr = &(result[0][0]);
+ asm volatile(
+ // mat: v16 - v23
+ // m11, m12, m13, m14
+ // m21, m22, m23, m24
+ // m31, m32, m33, m34
+ // m41, m42, m43, m44
+ "ld1 {v16.2d - v19.2d}, [%[mat]], 64 \n\t"
+ "ld1 {v20.2d - v23.2d}, [%[mat]] \n\t"
+ "ins v30.d[0], %[rdet] \n\t"
+ // Determinant: right mat2x2
+ "trn1 v0.2d, v17.2d, v21.2d \n\t"
+ "trn2 v1.2d, v19.2d, v23.2d \n\t"
+ "trn2 v2.2d, v17.2d, v21.2d \n\t"
+ "trn1 v3.2d, v19.2d, v23.2d \n\t"
+ "trn2 v5.2d, v21.2d, v23.2d \n\t"
+ "trn1 v4.2d, v17.2d, v19.2d \n\t"
+ "trn2 v6.2d, v17.2d, v19.2d \n\t"
+ "trn1 v7.2d, v21.2d, v23.2d \n\t"
+ "trn2 v25.2d, v23.2d, v21.2d \n\t"
+ "trn1 v27.2d, v23.2d, v21.2d \n\t"
+ "fmul v0.2d, v0.2d, v1.2d \n\t"
+ "fmul v1.2d, v4.2d, v5.2d \n\t"
+ "fmls v0.2d, v2.2d, v3.2d \n\t"
+ "fmul v2.2d, v4.2d, v25.2d \n\t"
+ "fmls v1.2d, v6.2d, v7.2d \n\t"
+ "fmls v2.2d, v6.2d, v27.2d \n\t"
+ // Adjoint:
+ // v24: A11A12, v25: A13A14
+ // v26: A21A22, v27: A23A24
+ "fmul v3.2d, v18.2d, v0.d[1] \n\t"
+ "fmul v4.2d, v16.2d, v0.d[1] \n\t"
+ "fmul v5.2d, v16.2d, v1.d[1] \n\t"
+ "fmul v6.2d, v16.2d, v2.d[1] \n\t"
+ "fmls v3.2d, v20.2d, v1.d[1] \n\t"
+ "fmls v4.2d, v20.2d, v2.d[0] \n\t"
+ "fmls v5.2d, v18.2d, v2.d[0] \n\t"
+ "fmls v6.2d, v18.2d, v1.d[0] \n\t"
+ "fmla v3.2d, v22.2d, v2.d[1] \n\t"
+ "fmla v4.2d, v22.2d, v1.d[0] \n\t"
+ "fmla v5.2d, v22.2d, v0.d[0] \n\t"
+ "fmla v6.2d, v20.2d, v0.d[0] \n\t"
+ "fneg v3.2d, v3.2d \n\t"
+ "fneg v5.2d, v5.2d \n\t"
+ "trn1 v26.2d, v3.2d, v4.2d \n\t"
+ "trn1 v27.2d, v5.2d, v6.2d \n\t"
+ "trn2 v24.2d, v3.2d, v4.2d \n\t"
+ "trn2 v25.2d, v5.2d, v6.2d \n\t"
+ "fneg v24.2d, v24.2d \n\t"
+ "fneg v25.2d, v25.2d \n\t"
+ // Inverse
+ // v24: I11I12, v25: I13I14
+ // v26: I21I22, v27: I23I24
+ "fmul v24.2d, v24.2d, v30.d[0] \n\t"
+ "fmul v25.2d, v25.2d, v30.d[0] \n\t"
+ "fmul v26.2d, v26.2d, v30.d[0] \n\t"
+ "fmul v27.2d, v27.2d, v30.d[0] \n\t"
+ "st1 {v24.2d - v27.2d}, [%[pr]], 64 \n\t"
+ // Determinant: left mat2x2
+ "trn1 v0.2d, v16.2d, v20.2d \n\t"
+ "trn2 v1.2d, v18.2d, v22.2d \n\t"
+ "trn2 v2.2d, v16.2d, v20.2d \n\t"
+ "trn1 v3.2d, v18.2d, v22.2d \n\t"
+ "trn2 v5.2d, v20.2d, v22.2d \n\t"
+ "trn1 v4.2d, v16.2d, v18.2d \n\t"
+ "trn2 v6.2d, v16.2d, v18.2d \n\t"
+ "trn1 v7.2d, v20.2d, v22.2d \n\t"
+ "trn2 v25.2d, v22.2d, v20.2d \n\t"
+ "trn1 v27.2d, v22.2d, v20.2d \n\t"
+ "fmul v0.2d, v0.2d, v1.2d \n\t"
+ "fmul v1.2d, v4.2d, v5.2d \n\t"
+ "fmls v0.2d, v2.2d, v3.2d \n\t"
+ "fmul v2.2d, v4.2d, v25.2d \n\t"
+ "fmls v1.2d, v6.2d, v7.2d \n\t"
+ "fmls v2.2d, v6.2d, v27.2d \n\t"
+ // Adjoint:
+ // v24: A31A32, v25: A33A34
+ // v26: A41A42, v27: A43A44
+ "fmul v3.2d, v19.2d, v0.d[1] \n\t"
+ "fmul v4.2d, v17.2d, v0.d[1] \n\t"
+ "fmul v5.2d, v17.2d, v1.d[1] \n\t"
+ "fmul v6.2d, v17.2d, v2.d[1] \n\t"
+ "fmls v3.2d, v21.2d, v1.d[1] \n\t"
+ "fmls v4.2d, v21.2d, v2.d[0] \n\t"
+ "fmls v5.2d, v19.2d, v2.d[0] \n\t"
+ "fmls v6.2d, v19.2d, v1.d[0] \n\t"
+ "fmla v3.2d, v23.2d, v2.d[1] \n\t"
+ "fmla v4.2d, v23.2d, v1.d[0] \n\t"
+ "fmla v5.2d, v23.2d, v0.d[0] \n\t"
+ "fmla v6.2d, v21.2d, v0.d[0] \n\t"
+ "fneg v3.2d, v3.2d \n\t"
+ "fneg v5.2d, v5.2d \n\t"
+ "trn1 v26.2d, v3.2d, v4.2d \n\t"
+ "trn1 v27.2d, v5.2d, v6.2d \n\t"
+ "trn2 v24.2d, v3.2d, v4.2d \n\t"
+ "trn2 v25.2d, v5.2d, v6.2d \n\t"
+ "fneg v24.2d, v24.2d \n\t"
+ "fneg v25.2d, v25.2d \n\t"
+ // Inverse
+ // v24: I31I32, v25: I33I34
+ // v26: I41I42, v27: I43I44
+ "fmul v24.2d, v24.2d, v30.d[0] \n\t"
+ "fmul v25.2d, v25.2d, v30.d[0] \n\t"
+ "fmul v26.2d, v26.2d, v30.d[0] \n\t"
+ "fmul v27.2d, v27.2d, v30.d[0] \n\t"
+ "st1 {v24.2d - v27.2d}, [%[pr]] \n\t"
+ : [mat] "+r"(mat), [pr] "+r"(pr)
+ : [rdet] "r"(rdet)
+ : "memory", "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v16", "v17",
+ "v18", "v19", "v20", "v21", "v22", "v23", "24", "25", "v26", "v27",
+ "v28", "v29", "v30");
+#elif HAVE_MIPS_MSA_INTRINSICS
+ const double rDet = 1 / det;
+ const double* mat = &(matrix[0][0]);
+ v2f64 mat0, mat1, mat2, mat3, mat4, mat5, mat6, mat7;
+ v2f64 rev2, rev3, rev4, rev5, rev6, rev7;
+ v2f64 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7;
+ v2f64 det0, det1, det2, tmp8, tmp9, tmp10, tmp11;
+ const v2f64 rdet = COPY_DOUBLE_TO_VECTOR(rDet);
+ // mat0 mat1 --> m00 m01 m02 m03
+ // mat2 mat3 --> m10 m11 m12 m13
+ // mat4 mat5 --> m20 m21 m22 m23
+ // mat6 mat7 --> m30 m31 m32 m33
+ LD_DP8(mat, 2, mat0, mat1, mat2, mat3, mat4, mat5, mat6, mat7);
+
+ // Right half
+ rev3 = SLDI_D(mat3, mat3, 8); // m13 m12
+ rev5 = SLDI_D(mat5, mat5, 8); // m23 m22
+ rev7 = SLDI_D(mat7, mat7, 8); // m33 m32
+
+ // 2*2 Determinants
+ // for A00 & A01
+ tmp0 = mat5 * rev7;
+ tmp1 = mat3 * rev7;
+ tmp2 = mat3 * rev5;
+ // for A10 & A11
+ tmp3 = mat1 * rev7;
+ tmp4 = mat1 * rev5;
+ // for A20 & A21
+ tmp5 = mat1 * rev3;
+ // for A30 & A31
+ tmp6 = (v2f64)__msa_ilvr_d((v2i64)tmp1, (v2i64)tmp0);
+ tmp7 = (v2f64)__msa_ilvl_d((v2i64)tmp1, (v2i64)tmp0);
+ det0 = tmp6 - tmp7;
+ tmp6 = (v2f64)__msa_ilvr_d((v2i64)tmp3, (v2i64)tmp2);
+ tmp7 = (v2f64)__msa_ilvl_d((v2i64)tmp3, (v2i64)tmp2);
+ det1 = tmp6 - tmp7;
+ tmp6 = (v2f64)__msa_ilvr_d((v2i64)tmp5, (v2i64)tmp4);
+ tmp7 = (v2f64)__msa_ilvl_d((v2i64)tmp5, (v2i64)tmp4);
+ det2 = tmp6 - tmp7;
+
+ // Co-factors
+ tmp0 = mat0 * (v2f64)__msa_splati_d((v2i64)det0, 0);
+ tmp1 = mat0 * (v2f64)__msa_splati_d((v2i64)det0, 1);
+ tmp2 = mat0 * (v2f64)__msa_splati_d((v2i64)det1, 0);
+ tmp3 = mat2 * (v2f64)__msa_splati_d((v2i64)det0, 0);
+ tmp4 = mat2 * (v2f64)__msa_splati_d((v2i64)det1, 1);
+ tmp5 = mat2 * (v2f64)__msa_splati_d((v2i64)det2, 0);
+ tmp6 = mat4 * (v2f64)__msa_splati_d((v2i64)det0, 1);
+ tmp7 = mat4 * (v2f64)__msa_splati_d((v2i64)det1, 1);
+ tmp8 = mat4 * (v2f64)__msa_splati_d((v2i64)det2, 1);
+ tmp9 = mat6 * (v2f64)__msa_splati_d((v2i64)det1, 0);
+ tmp10 = mat6 * (v2f64)__msa_splati_d((v2i64)det2, 0);
+ tmp11 = mat6 * (v2f64)__msa_splati_d((v2i64)det2, 1);
+
+ tmp0 -= tmp7;
+ tmp1 -= tmp4;
+ tmp2 -= tmp5;
+ tmp3 -= tmp6;
+ tmp0 += tmp10;
+ tmp1 += tmp11;
+ tmp2 += tmp8;
+ tmp3 += tmp9;
+
+ // Multiply with 1/det
+ tmp0 *= rdet;
+ tmp1 *= rdet;
+ tmp2 *= rdet;
+ tmp3 *= rdet;
+
+ // Inverse: Upper half
+ result[0][0] = tmp3[1];
+ result[0][1] = -tmp0[1];
+ result[0][2] = tmp1[1];
+ result[0][3] = -tmp2[1];
+ result[1][0] = -tmp3[0];
+ result[1][1] = tmp0[0];
+ result[1][2] = -tmp1[0];
+ result[1][3] = tmp2[0];
+ // Left half
+ rev2 = SLDI_D(mat2, mat2, 8); // m13 m12
+ rev4 = SLDI_D(mat4, mat4, 8); // m23 m22
+ rev6 = SLDI_D(mat6, mat6, 8); // m33 m32
+
+ // 2*2 Determinants
+ // for A00 & A01
+ tmp0 = mat4 * rev6;
+ tmp1 = mat2 * rev6;
+ tmp2 = mat2 * rev4;
+ // for A10 & A11
+ tmp3 = mat0 * rev6;
+ tmp4 = mat0 * rev4;
+ // for A20 & A21
+ tmp5 = mat0 * rev2;
+ // for A30 & A31
+ tmp6 = (v2f64)__msa_ilvr_d((v2i64)tmp1, (v2i64)tmp0);
+ tmp7 = (v2f64)__msa_ilvl_d((v2i64)tmp1, (v2i64)tmp0);
+ det0 = tmp6 - tmp7;
+ tmp6 = (v2f64)__msa_ilvr_d((v2i64)tmp3, (v2i64)tmp2);
+ tmp7 = (v2f64)__msa_ilvl_d((v2i64)tmp3, (v2i64)tmp2);
+ det1 = tmp6 - tmp7;
+ tmp6 = (v2f64)__msa_ilvr_d((v2i64)tmp5, (v2i64)tmp4);
+ tmp7 = (v2f64)__msa_ilvl_d((v2i64)tmp5, (v2i64)tmp4);
+ det2 = tmp6 - tmp7;
+
+ // Co-factors
+ tmp0 = mat3 * (v2f64)__msa_splati_d((v2i64)det0, 0);
+ tmp1 = mat1 * (v2f64)__msa_splati_d((v2i64)det0, 1);
+ tmp2 = mat1 * (v2f64)__msa_splati_d((v2i64)det0, 0);
+ tmp3 = mat1 * (v2f64)__msa_splati_d((v2i64)det1, 0);
+ tmp4 = mat3 * (v2f64)__msa_splati_d((v2i64)det1, 1);
+ tmp5 = mat3 * (v2f64)__msa_splati_d((v2i64)det2, 0);
+ tmp6 = mat5 * (v2f64)__msa_splati_d((v2i64)det0, 1);
+ tmp7 = mat5 * (v2f64)__msa_splati_d((v2i64)det1, 1);
+ tmp8 = mat5 * (v2f64)__msa_splati_d((v2i64)det2, 1);
+ tmp9 = mat7 * (v2f64)__msa_splati_d((v2i64)det1, 0);
+ tmp10 = mat7 * (v2f64)__msa_splati_d((v2i64)det2, 0);
+ tmp11 = mat7 * (v2f64)__msa_splati_d((v2i64)det2, 1);
+ tmp0 -= tmp6;
+ tmp1 -= tmp4;
+ tmp2 -= tmp7;
+ tmp3 -= tmp5;
+ tmp0 += tmp9;
+ tmp1 += tmp11;
+ tmp2 += tmp10;
+ tmp3 += tmp8;
+
+ // Multiply with 1/det
+ tmp0 *= rdet;
+ tmp1 *= rdet;
+ tmp2 *= rdet;
+ tmp3 *= rdet;
+
+ // Inverse: Lower half
+ result[2][0] = tmp0[1];
+ result[2][1] = -tmp2[1];
+ result[2][2] = tmp1[1];
+ result[2][3] = -tmp3[1];
+ result[3][0] = -tmp0[0];
+ result[3][1] = tmp2[0];
+ result[3][2] = -tmp1[0];
+ result[3][3] = tmp3[0];
+#else
+ // Calculate the adjoint matrix
+ Adjoint(matrix, result);
+
+ double rdet = 1 / det;
+
+ // Scale the adjoint matrix to get the inverse
+ for (int i = 0; i < 4; i++)
+ for (int j = 0; j < 4; j++)
+ result[i][j] = result[i][j] * rdet;
+#endif
+ return true;
+}
+
+// End of code adapted from Matrix Inversion by Richard Carling
+
+// Perform a decomposition on the passed matrix, return false if unsuccessful
+// From Graphics Gems: unmatrix.c
+
+// Transpose rotation portion of matrix a, return b
+static void TransposeMatrix4(const TransformationMatrix::Matrix4& a,
+ TransformationMatrix::Matrix4& b) {
+ for (int i = 0; i < 4; i++)
+ for (int j = 0; j < 4; j++)
+ b[i][j] = a[j][i];
+}
+
+// Multiply a homogeneous point by a matrix and return the transformed point
+static void V4MulPointByMatrix(const Vector4 p,
+ const TransformationMatrix::Matrix4& m,
+ Vector4 result) {
+ result[0] =
+ (p[0] * m[0][0]) + (p[1] * m[1][0]) + (p[2] * m[2][0]) + (p[3] * m[3][0]);
+ result[1] =
+ (p[0] * m[0][1]) + (p[1] * m[1][1]) + (p[2] * m[2][1]) + (p[3] * m[3][1]);
+ result[2] =
+ (p[0] * m[0][2]) + (p[1] * m[1][2]) + (p[2] * m[2][2]) + (p[3] * m[3][2]);
+ result[3] =
+ (p[0] * m[0][3]) + (p[1] * m[1][3]) + (p[2] * m[2][3]) + (p[3] * m[3][3]);
+}
+
+static double V3Length(Vector3 a) {
+ return std::sqrt((a[0] * a[0]) + (a[1] * a[1]) + (a[2] * a[2]));
+}
+
+static void V3Scale(Vector3 v, double desired_length) {
+ double len = V3Length(v);
+ if (len != 0) {
+ double l = desired_length / len;
+ v[0] *= l;
+ v[1] *= l;
+ v[2] *= l;
+ }
+}
+
+static double V3Dot(const Vector3 a, const Vector3 b) {
+ return (a[0] * b[0]) + (a[1] * b[1]) + (a[2] * b[2]);
+}
+
+// Make a linear combination of two vectors and return the result.
+// result = (a * ascl) + (b * bscl)
+static void V3Combine(const Vector3 a,
+ const Vector3 b,
+ Vector3 result,
+ double ascl,
+ double bscl) {
+ result[0] = (ascl * a[0]) + (bscl * b[0]);
+ result[1] = (ascl * a[1]) + (bscl * b[1]);
+ result[2] = (ascl * a[2]) + (bscl * b[2]);
+}
+
+// Return the cross product result = a cross b */
+static void V3Cross(const Vector3 a, const Vector3 b, Vector3 result) {
+ result[0] = (a[1] * b[2]) - (a[2] * b[1]);
+ result[1] = (a[2] * b[0]) - (a[0] * b[2]);
+ result[2] = (a[0] * b[1]) - (a[1] * b[0]);
+}
+
+static bool Decompose(const TransformationMatrix::Matrix4& mat,
+ TransformationMatrix::DecomposedType& result) {
+ TransformationMatrix::Matrix4 local_matrix;
+ memcpy(local_matrix, mat, sizeof(TransformationMatrix::Matrix4));
+
+ // Normalize the matrix.
+ if (local_matrix[3][3] == 0)
+ return false;
+
+ int i, j;
+ for (i = 0; i < 4; i++)
+ for (j = 0; j < 4; j++)
+ local_matrix[i][j] /= local_matrix[3][3];
+
+ // perspectiveMatrix is used to solve for perspective, but it also provides
+ // an easy way to test for singularity of the upper 3x3 component.
+ TransformationMatrix::Matrix4 perspective_matrix;
+ memcpy(perspective_matrix, local_matrix,
+ sizeof(TransformationMatrix::Matrix4));
+ for (i = 0; i < 3; i++)
+ perspective_matrix[i][3] = 0;
+ perspective_matrix[3][3] = 1;
+
+ if (Determinant4x4(perspective_matrix) == 0)
+ return false;
+
+ // First, isolate perspective. This is the messiest.
+ if (local_matrix[0][3] != 0 || local_matrix[1][3] != 0 ||
+ local_matrix[2][3] != 0) {
+ // rightHandSide is the right hand side of the equation.
+ Vector4 right_hand_side;
+ right_hand_side[0] = local_matrix[0][3];
+ right_hand_side[1] = local_matrix[1][3];
+ right_hand_side[2] = local_matrix[2][3];
+ right_hand_side[3] = local_matrix[3][3];
+
+ // Solve the equation by inverting perspectiveMatrix and multiplying
+ // rightHandSide by the inverse. (This is the easiest way, not
+ // necessarily the best.)
+ TransformationMatrix::Matrix4 inverse_perspective_matrix,
+ transposed_inverse_perspective_matrix;
+ if (!Inverse(perspective_matrix, inverse_perspective_matrix))
+ return false;
+ TransposeMatrix4(inverse_perspective_matrix,
+ transposed_inverse_perspective_matrix);
+
+ Vector4 perspective_point;
+ V4MulPointByMatrix(right_hand_side, transposed_inverse_perspective_matrix,
+ perspective_point);
+
+ result.perspective_x = perspective_point[0];
+ result.perspective_y = perspective_point[1];
+ result.perspective_z = perspective_point[2];
+ result.perspective_w = perspective_point[3];
+
+ // Clear the perspective partition
+ local_matrix[0][3] = local_matrix[1][3] = local_matrix[2][3] = 0;
+ local_matrix[3][3] = 1;
+ } else {
+ // No perspective.
+ result.perspective_x = result.perspective_y = result.perspective_z = 0;
+ result.perspective_w = 1;
+ }
+
+ // Next take care of translation (easy).
+ result.translate_x = local_matrix[3][0];
+ local_matrix[3][0] = 0;
+ result.translate_y = local_matrix[3][1];
+ local_matrix[3][1] = 0;
+ result.translate_z = local_matrix[3][2];
+ local_matrix[3][2] = 0;
+
+ // Vector4 type and functions need to be added to the common set.
+ Vector3 row[3], pdum3;
+
+ // Now get scale and shear.
+ for (i = 0; i < 3; i++) {
+ row[i][0] = local_matrix[i][0];
+ row[i][1] = local_matrix[i][1];
+ row[i][2] = local_matrix[i][2];
+ }
+
+ // Compute X scale factor and normalize first row.
+ result.scale_x = V3Length(row[0]);
+ V3Scale(row[0], 1.0);
+
+ // Compute XY shear factor and make 2nd row orthogonal to 1st.
+ result.skew_xy = V3Dot(row[0], row[1]);
+ V3Combine(row[1], row[0], row[1], 1.0, -result.skew_xy);
+
+ // Now, compute Y scale and normalize 2nd row.
+ result.scale_y = V3Length(row[1]);
+ V3Scale(row[1], 1.0);
+ result.skew_xy /= result.scale_y;
+
+ // Compute XZ and YZ shears, orthogonalize 3rd row.
+ result.skew_xz = V3Dot(row[0], row[2]);
+ V3Combine(row[2], row[0], row[2], 1.0, -result.skew_xz);
+ result.skew_yz = V3Dot(row[1], row[2]);
+ V3Combine(row[2], row[1], row[2], 1.0, -result.skew_yz);
+
+ // Next, get Z scale and normalize 3rd row.
+ result.scale_z = V3Length(row[2]);
+ V3Scale(row[2], 1.0);
+ result.skew_xz /= result.scale_z;
+ result.skew_yz /= result.scale_z;
+
+ // At this point, the matrix (in rows[]) is orthonormal.
+ // Check for a coordinate system flip. If the determinant
+ // is -1, then negate the matrix and the scaling factors.
+ V3Cross(row[1], row[2], pdum3);
+ if (V3Dot(row[0], pdum3) < 0) {
+ result.scale_x *= -1;
+ result.scale_y *= -1;
+ result.scale_z *= -1;
+
+ for (i = 0; i < 3; i++) {
+ row[i][0] *= -1;
+ row[i][1] *= -1;
+ row[i][2] *= -1;
+ }
+ }
+
+ // Now, get the rotations out, as described in the gem.
+
+ // FIXME - Add the ability to return either quaternions (which are
+ // easier to recompose with) or Euler angles (rx, ry, rz), which
+ // are easier for authors to deal with. The latter will only be useful
+ // when we fix https://bugs.webkit.org/show_bug.cgi?id=23799, so I
+ // will leave the Euler angle code here for now.
+
+ // ret.rotateY = asin(-row[0][2]);
+ // if (cos(ret.rotateY) != 0) {
+ // ret.rotateX = atan2(row[1][2], row[2][2]);
+ // ret.rotateZ = atan2(row[0][1], row[0][0]);
+ // } else {
+ // ret.rotateX = atan2(-row[2][0], row[1][1]);
+ // ret.rotateZ = 0;
+ // }
+
+ double s, t, x, y, z, w;
+
+ t = row[0][0] + row[1][1] + row[2][2] + 1.0;
+
+ if (t > 1e-4) {
+ s = 0.5 / std::sqrt(t);
+ w = 0.25 / s;
+ x = (row[2][1] - row[1][2]) * s;
+ y = (row[0][2] - row[2][0]) * s;
+ z = (row[1][0] - row[0][1]) * s;
+ } else if (row[0][0] > row[1][1] && row[0][0] > row[2][2]) {
+ s = std::sqrt(1.0 + row[0][0] - row[1][1] - row[2][2]) * 2.0; // S=4*qx
+ x = 0.25 * s;
+ y = (row[0][1] + row[1][0]) / s;
+ z = (row[0][2] + row[2][0]) / s;
+ w = (row[2][1] - row[1][2]) / s;
+ } else if (row[1][1] > row[2][2]) {
+ s = std::sqrt(1.0 + row[1][1] - row[0][0] - row[2][2]) * 2.0; // S=4*qy
+ x = (row[0][1] + row[1][0]) / s;
+ y = 0.25 * s;
+ z = (row[1][2] + row[2][1]) / s;
+ w = (row[0][2] - row[2][0]) / s;
+ } else {
+ s = std::sqrt(1.0 + row[2][2] - row[0][0] - row[1][1]) * 2.0; // S=4*qz
+ x = (row[0][2] + row[2][0]) / s;
+ y = (row[1][2] + row[2][1]) / s;
+ z = 0.25 * s;
+ w = (row[1][0] - row[0][1]) / s;
+ }
+
+ result.quaternion_x = x;
+ result.quaternion_y = y;
+ result.quaternion_z = z;
+ result.quaternion_w = w;
+
+ return true;
+}
+
+// Perform a spherical linear interpolation between the two
+// passed quaternions with 0 <= t <= 1
+static void Slerp(double qa[4], const double qb[4], double t) {
+ double ax, ay, az, aw;
+ double bx, by, bz, bw;
+ double cx, cy, cz, cw;
+ double product;
+
+ ax = qa[0];
+ ay = qa[1];
+ az = qa[2];
+ aw = qa[3];
+ bx = qb[0];
+ by = qb[1];
+ bz = qb[2];
+ bw = qb[3];
+
+ product = ax * bx + ay * by + az * bz + aw * bw;
+
+ product = clampTo(product, -1.0, 1.0);
+
+ const double kEpsilon = 1e-5;
+ if (std::abs(product - 1.0) < kEpsilon) {
+ // Result is qa, so just return
+ return;
+ }
+
+ double denom = std::sqrt(1.0 - product * product);
+ double theta = std::acos(product);
+ double w = std::sin(t * theta) * (1.0 / denom);
+
+ double scale1 = std::cos(t * theta) - product * w;
+ double scale2 = w;
+
+ cx = ax * scale1 + bx * scale2;
+ cy = ay * scale1 + by * scale2;
+ cz = az * scale1 + bz * scale2;
+ cw = aw * scale1 + bw * scale2;
+
+ qa[0] = cx;
+ qa[1] = cy;
+ qa[2] = cz;
+ qa[3] = cw;
+}
+
+// End of Supporting Math Functions
+
+TransformationMatrix::TransformationMatrix(const AffineTransform& t) {
+ CheckAlignment();
+ SetMatrix(t.A(), t.B(), t.C(), t.D(), t.E(), t.F());
+}
+
+TransformationMatrix& TransformationMatrix::Scale(double s) {
+ return ScaleNonUniform(s, s);
+}
+
+FloatPoint TransformationMatrix::ProjectPoint(const FloatPoint& p,
+ bool* clamped) const {
+ // This is basically raytracing. We have a point in the destination
+ // plane with z=0, and we cast a ray parallel to the z-axis from that
+ // point to find the z-position at which it intersects the z=0 plane
+ // with the transform applied. Once we have that point we apply the
+ // inverse transform to find the corresponding point in the source
+ // space.
+ //
+ // Given a plane with normal Pn, and a ray starting at point R0 and
+ // with direction defined by the vector Rd, we can find the
+ // intersection point as a distance d from R0 in units of Rd by:
+ //
+ // d = -dot (Pn', R0) / dot (Pn', Rd)
+ if (clamped)
+ *clamped = false;
+
+ if (M33() == 0) {
+ // In this case, the projection plane is parallel to the ray we are trying
+ // to trace, and there is no well-defined value for the projection.
+ return FloatPoint();
+ }
+
+ double x = p.X();
+ double y = p.Y();
+ double z = -(M13() * x + M23() * y + M43()) / M33();
+
+ // FIXME: use multVecMatrix()
+ double out_x = x * M11() + y * M21() + z * M31() + M41();
+ double out_y = x * M12() + y * M22() + z * M32() + M42();
+
+ double w = x * M14() + y * M24() + z * M34() + M44();
+ if (w <= 0) {
+ // Using int max causes overflow when other code uses the projected point.
+ // To represent infinity yet reduce the risk of overflow, we use a large but
+ // not-too-large number here when clamping.
+ const int kLargeNumber = 100000000 / kFixedPointDenominator;
+ out_x = copysign(kLargeNumber, out_x);
+ out_y = copysign(kLargeNumber, out_y);
+ if (clamped)
+ *clamped = true;
+ } else if (w != 1) {
+ out_x /= w;
+ out_y /= w;
+ }
+
+ return FloatPoint(static_cast<float>(out_x), static_cast<float>(out_y));
+}
+
+FloatQuad TransformationMatrix::ProjectQuad(const FloatQuad& q,
+ bool* clamped) const {
+ FloatQuad projected_quad;
+
+ bool clamped1 = false;
+ bool clamped2 = false;
+ bool clamped3 = false;
+ bool clamped4 = false;
+
+ projected_quad.SetP1(ProjectPoint(q.P1(), &clamped1));
+ projected_quad.SetP2(ProjectPoint(q.P2(), &clamped2));
+ projected_quad.SetP3(ProjectPoint(q.P3(), &clamped3));
+ projected_quad.SetP4(ProjectPoint(q.P4(), &clamped4));
+
+ if (clamped)
+ *clamped = clamped1 || clamped2 || clamped3 || clamped4;
+
+ // If all points on the quad had w < 0, then the entire quad would not be
+ // visible to the projected surface.
+ bool everything_was_clipped = clamped1 && clamped2 && clamped3 && clamped4;
+ if (everything_was_clipped)
+ return FloatQuad();
+
+ return projected_quad;
+}
+
+static float ClampEdgeValue(float f) {
+ DCHECK(!std::isnan(f));
+ return clampTo(f, (-LayoutUnit::Max() / 2).ToFloat(),
+ (LayoutUnit::Max() / 2).ToFloat());
+}
+
+LayoutRect TransformationMatrix::ClampedBoundsOfProjectedQuad(
+ const FloatQuad& q) const {
+ FloatRect mapped_quad_bounds = ProjectQuad(q).BoundingBox();
+
+ float left = ClampEdgeValue(floorf(mapped_quad_bounds.X()));
+ float top = ClampEdgeValue(floorf(mapped_quad_bounds.Y()));
+
+ float right;
+ if (std::isinf(mapped_quad_bounds.X()) &&
+ std::isinf(mapped_quad_bounds.Width()))
+ right = (LayoutUnit::Max() / 2).ToFloat();
+ else
+ right = ClampEdgeValue(ceilf(mapped_quad_bounds.MaxX()));
+
+ float bottom;
+ if (std::isinf(mapped_quad_bounds.Y()) &&
+ std::isinf(mapped_quad_bounds.Height()))
+ bottom = (LayoutUnit::Max() / 2).ToFloat();
+ else
+ bottom = ClampEdgeValue(ceilf(mapped_quad_bounds.MaxY()));
+
+ return LayoutRect(LayoutUnit::Clamp(left), LayoutUnit::Clamp(top),
+ LayoutUnit::Clamp(right - left),
+ LayoutUnit::Clamp(bottom - top));
+}
+
+void TransformationMatrix::TransformBox(FloatBox& box) const {
+ FloatBox bounds;
+ bool first_point = true;
+ for (size_t i = 0; i < 2; ++i) {
+ for (size_t j = 0; j < 2; ++j) {
+ for (size_t k = 0; k < 2; ++k) {
+ FloatPoint3D point(box.X(), box.Y(), box.Z());
+ point +=
+ FloatPoint3D(i * box.Width(), j * box.Height(), k * box.Depth());
+ point = MapPoint(point);
+ if (first_point) {
+ bounds.SetOrigin(point);
+ first_point = false;
+ } else {
+ bounds.ExpandTo(point);
+ }
+ }
+ }
+ }
+ box = bounds;
+}
+
+FloatPoint TransformationMatrix::MapPoint(const FloatPoint& p) const {
+ if (IsIdentityOrTranslation())
+ return FloatPoint(p.X() + static_cast<float>(matrix_[3][0]),
+ p.Y() + static_cast<float>(matrix_[3][1]));
+
+ return InternalMapPoint(p);
+}
+
+FloatPoint3D TransformationMatrix::MapPoint(const FloatPoint3D& p) const {
+ if (IsIdentityOrTranslation())
+ return FloatPoint3D(p.X() + static_cast<float>(matrix_[3][0]),
+ p.Y() + static_cast<float>(matrix_[3][1]),
+ p.Z() + static_cast<float>(matrix_[3][2]));
+
+ return InternalMapPoint(p);
+}
+
+IntRect TransformationMatrix::MapRect(const IntRect& rect) const {
+ return EnclosingIntRect(MapRect(FloatRect(rect)));
+}
+
+LayoutRect TransformationMatrix::MapRect(const LayoutRect& r) const {
+ return EnclosingLayoutRect(MapRect(FloatRect(r)));
+}
+
+FloatRect TransformationMatrix::MapRect(const FloatRect& r) const {
+ if (IsIdentityOrTranslation()) {
+ FloatRect mapped_rect(r);
+ mapped_rect.Move(static_cast<float>(matrix_[3][0]),
+ static_cast<float>(matrix_[3][1]));
+ return mapped_rect;
+ }
+
+ FloatQuad result;
+
+ float max_x = r.MaxX();
+ float max_y = r.MaxY();
+ result.SetP1(InternalMapPoint(FloatPoint(r.X(), r.Y())));
+ result.SetP2(InternalMapPoint(FloatPoint(max_x, r.Y())));
+ result.SetP3(InternalMapPoint(FloatPoint(max_x, max_y)));
+ result.SetP4(InternalMapPoint(FloatPoint(r.X(), max_y)));
+
+ return result.BoundingBox();
+}
+
+FloatQuad TransformationMatrix::MapQuad(const FloatQuad& q) const {
+ if (IsIdentityOrTranslation()) {
+ FloatQuad mapped_quad(q);
+ mapped_quad.Move(static_cast<float>(matrix_[3][0]),
+ static_cast<float>(matrix_[3][1]));
+ return mapped_quad;
+ }
+
+ FloatQuad result;
+ result.SetP1(InternalMapPoint(q.P1()));
+ result.SetP2(InternalMapPoint(q.P2()));
+ result.SetP3(InternalMapPoint(q.P3()));
+ result.SetP4(InternalMapPoint(q.P4()));
+ return result;
+}
+
+TransformationMatrix& TransformationMatrix::ScaleNonUniform(double sx,
+ double sy) {
+ matrix_[0][0] *= sx;
+ matrix_[0][1] *= sx;
+ matrix_[0][2] *= sx;
+ matrix_[0][3] *= sx;
+
+ matrix_[1][0] *= sy;
+ matrix_[1][1] *= sy;
+ matrix_[1][2] *= sy;
+ matrix_[1][3] *= sy;
+ return *this;
+}
+
+TransformationMatrix& TransformationMatrix::Scale3d(double sx,
+ double sy,
+ double sz) {
+ ScaleNonUniform(sx, sy);
+
+ matrix_[2][0] *= sz;
+ matrix_[2][1] *= sz;
+ matrix_[2][2] *= sz;
+ matrix_[2][3] *= sz;
+ return *this;
+}
+
+TransformationMatrix& TransformationMatrix::Rotate3d(const Rotation& rotation) {
+ return Rotate3d(rotation.axis.X(), rotation.axis.Y(), rotation.axis.Z(),
+ rotation.angle);
+}
+
+TransformationMatrix& TransformationMatrix::Rotate3d(double x,
+ double y,
+ double z,
+ double angle) {
+ // Normalize the axis of rotation
+ double length = std::sqrt(x * x + y * y + z * z);
+ if (length == 0) {
+ // A direction vector that cannot be normalized, such as [0, 0, 0], will
+ // cause the rotation to not be applied.
+ return *this;
+ } else if (length != 1) {
+ x /= length;
+ y /= length;
+ z /= length;
+ }
+
+ // Angles are in degrees. Switch to radians.
+ angle = deg2rad(angle);
+
+ double sin_theta = std::sin(angle);
+ double cos_theta = std::cos(angle);
+
+ TransformationMatrix mat;
+
+ // Optimize cases where the axis is along a major axis
+ if (x == 1.0 && y == 0.0 && z == 0.0) {
+ mat.matrix_[0][0] = 1.0;
+ mat.matrix_[0][1] = 0.0;
+ mat.matrix_[0][2] = 0.0;
+ mat.matrix_[1][0] = 0.0;
+ mat.matrix_[1][1] = cos_theta;
+ mat.matrix_[1][2] = sin_theta;
+ mat.matrix_[2][0] = 0.0;
+ mat.matrix_[2][1] = -sin_theta;
+ mat.matrix_[2][2] = cos_theta;
+ mat.matrix_[0][3] = mat.matrix_[1][3] = mat.matrix_[2][3] = 0.0;
+ mat.matrix_[3][0] = mat.matrix_[3][1] = mat.matrix_[3][2] = 0.0;
+ mat.matrix_[3][3] = 1.0;
+ } else if (x == 0.0 && y == 1.0 && z == 0.0) {
+ mat.matrix_[0][0] = cos_theta;
+ mat.matrix_[0][1] = 0.0;
+ mat.matrix_[0][2] = -sin_theta;
+ mat.matrix_[1][0] = 0.0;
+ mat.matrix_[1][1] = 1.0;
+ mat.matrix_[1][2] = 0.0;
+ mat.matrix_[2][0] = sin_theta;
+ mat.matrix_[2][1] = 0.0;
+ mat.matrix_[2][2] = cos_theta;
+ mat.matrix_[0][3] = mat.matrix_[1][3] = mat.matrix_[2][3] = 0.0;
+ mat.matrix_[3][0] = mat.matrix_[3][1] = mat.matrix_[3][2] = 0.0;
+ mat.matrix_[3][3] = 1.0;
+ } else if (x == 0.0 && y == 0.0 && z == 1.0) {
+ mat.matrix_[0][0] = cos_theta;
+ mat.matrix_[0][1] = sin_theta;
+ mat.matrix_[0][2] = 0.0;
+ mat.matrix_[1][0] = -sin_theta;
+ mat.matrix_[1][1] = cos_theta;
+ mat.matrix_[1][2] = 0.0;
+ mat.matrix_[2][0] = 0.0;
+ mat.matrix_[2][1] = 0.0;
+ mat.matrix_[2][2] = 1.0;
+ mat.matrix_[0][3] = mat.matrix_[1][3] = mat.matrix_[2][3] = 0.0;
+ mat.matrix_[3][0] = mat.matrix_[3][1] = mat.matrix_[3][2] = 0.0;
+ mat.matrix_[3][3] = 1.0;
+ } else {
+ // This case is the rotation about an arbitrary unit vector.
+ //
+ // Formula is adapted from Wikipedia article on Rotation matrix,
+ // http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle
+ //
+ // An alternate resource with the same matrix:
+ // http://www.fastgraph.com/makegames/3drotation/
+ //
+ double one_minus_cos_theta = 1 - cos_theta;
+ mat.matrix_[0][0] = cos_theta + x * x * one_minus_cos_theta;
+ mat.matrix_[0][1] = y * x * one_minus_cos_theta + z * sin_theta;
+ mat.matrix_[0][2] = z * x * one_minus_cos_theta - y * sin_theta;
+ mat.matrix_[1][0] = x * y * one_minus_cos_theta - z * sin_theta;
+ mat.matrix_[1][1] = cos_theta + y * y * one_minus_cos_theta;
+ mat.matrix_[1][2] = z * y * one_minus_cos_theta + x * sin_theta;
+ mat.matrix_[2][0] = x * z * one_minus_cos_theta + y * sin_theta;
+ mat.matrix_[2][1] = y * z * one_minus_cos_theta - x * sin_theta;
+ mat.matrix_[2][2] = cos_theta + z * z * one_minus_cos_theta;
+ mat.matrix_[0][3] = mat.matrix_[1][3] = mat.matrix_[2][3] = 0.0;
+ mat.matrix_[3][0] = mat.matrix_[3][1] = mat.matrix_[3][2] = 0.0;
+ mat.matrix_[3][3] = 1.0;
+ }
+ Multiply(mat);
+ return *this;
+}
+
+TransformationMatrix& TransformationMatrix::Rotate3d(double rx,
+ double ry,
+ double rz) {
+ // Angles are in degrees. Switch to radians.
+ rx = deg2rad(rx);
+ ry = deg2rad(ry);
+ rz = deg2rad(rz);
+
+ TransformationMatrix mat;
+
+ double sin_theta = std::sin(rz);
+ double cos_theta = std::cos(rz);
+
+ mat.matrix_[0][0] = cos_theta;
+ mat.matrix_[0][1] = sin_theta;
+ mat.matrix_[0][2] = 0.0;
+ mat.matrix_[1][0] = -sin_theta;
+ mat.matrix_[1][1] = cos_theta;
+ mat.matrix_[1][2] = 0.0;
+ mat.matrix_[2][0] = 0.0;
+ mat.matrix_[2][1] = 0.0;
+ mat.matrix_[2][2] = 1.0;
+ mat.matrix_[0][3] = mat.matrix_[1][3] = mat.matrix_[2][3] = 0.0;
+ mat.matrix_[3][0] = mat.matrix_[3][1] = mat.matrix_[3][2] = 0.0;
+ mat.matrix_[3][3] = 1.0;
+
+ TransformationMatrix rmat(mat);
+
+ sin_theta = std::sin(ry);
+ cos_theta = std::cos(ry);
+
+ mat.matrix_[0][0] = cos_theta;
+ mat.matrix_[0][1] = 0.0;
+ mat.matrix_[0][2] = -sin_theta;
+ mat.matrix_[1][0] = 0.0;
+ mat.matrix_[1][1] = 1.0;
+ mat.matrix_[1][2] = 0.0;
+ mat.matrix_[2][0] = sin_theta;
+ mat.matrix_[2][1] = 0.0;
+ mat.matrix_[2][2] = cos_theta;
+ mat.matrix_[0][3] = mat.matrix_[1][3] = mat.matrix_[2][3] = 0.0;
+ mat.matrix_[3][0] = mat.matrix_[3][1] = mat.matrix_[3][2] = 0.0;
+ mat.matrix_[3][3] = 1.0;
+
+ rmat.Multiply(mat);
+
+ sin_theta = std::sin(rx);
+ cos_theta = std::cos(rx);
+
+ mat.matrix_[0][0] = 1.0;
+ mat.matrix_[0][1] = 0.0;
+ mat.matrix_[0][2] = 0.0;
+ mat.matrix_[1][0] = 0.0;
+ mat.matrix_[1][1] = cos_theta;
+ mat.matrix_[1][2] = sin_theta;
+ mat.matrix_[2][0] = 0.0;
+ mat.matrix_[2][1] = -sin_theta;
+ mat.matrix_[2][2] = cos_theta;
+ mat.matrix_[0][3] = mat.matrix_[1][3] = mat.matrix_[2][3] = 0.0;
+ mat.matrix_[3][0] = mat.matrix_[3][1] = mat.matrix_[3][2] = 0.0;
+ mat.matrix_[3][3] = 1.0;
+
+ rmat.Multiply(mat);
+
+ Multiply(rmat);
+ return *this;
+}
+
+TransformationMatrix& TransformationMatrix::Translate(double tx, double ty) {
+ matrix_[3][0] += tx * matrix_[0][0] + ty * matrix_[1][0];
+ matrix_[3][1] += tx * matrix_[0][1] + ty * matrix_[1][1];
+ matrix_[3][2] += tx * matrix_[0][2] + ty * matrix_[1][2];
+ matrix_[3][3] += tx * matrix_[0][3] + ty * matrix_[1][3];
+ return *this;
+}
+
+TransformationMatrix& TransformationMatrix::Translate3d(double tx,
+ double ty,
+ double tz) {
+ matrix_[3][0] += tx * matrix_[0][0] + ty * matrix_[1][0] + tz * matrix_[2][0];
+ matrix_[3][1] += tx * matrix_[0][1] + ty * matrix_[1][1] + tz * matrix_[2][1];
+ matrix_[3][2] += tx * matrix_[0][2] + ty * matrix_[1][2] + tz * matrix_[2][2];
+ matrix_[3][3] += tx * matrix_[0][3] + ty * matrix_[1][3] + tz * matrix_[2][3];
+ return *this;
+}
+
+TransformationMatrix& TransformationMatrix::PostTranslate(double tx,
+ double ty) {
+ if (tx != 0) {
+ matrix_[0][0] += matrix_[0][3] * tx;
+ matrix_[1][0] += matrix_[1][3] * tx;
+ matrix_[2][0] += matrix_[2][3] * tx;
+ matrix_[3][0] += matrix_[3][3] * tx;
+ }
+
+ if (ty != 0) {
+ matrix_[0][1] += matrix_[0][3] * ty;
+ matrix_[1][1] += matrix_[1][3] * ty;
+ matrix_[2][1] += matrix_[2][3] * ty;
+ matrix_[3][1] += matrix_[3][3] * ty;
+ }
+
+ return *this;
+}
+
+TransformationMatrix& TransformationMatrix::PostTranslate3d(double tx,
+ double ty,
+ double tz) {
+ PostTranslate(tx, ty);
+ if (tz != 0) {
+ matrix_[0][2] += matrix_[0][3] * tz;
+ matrix_[1][2] += matrix_[1][3] * tz;
+ matrix_[2][2] += matrix_[2][3] * tz;
+ matrix_[3][2] += matrix_[3][3] * tz;
+ }
+
+ return *this;
+}
+
+TransformationMatrix& TransformationMatrix::Skew(double sx, double sy) {
+ // angles are in degrees. Switch to radians
+ sx = deg2rad(sx);
+ sy = deg2rad(sy);
+
+ TransformationMatrix mat;
+ mat.matrix_[0][1] =
+ std::tan(sy); // note that the y shear goes in the first row
+ mat.matrix_[1][0] = std::tan(sx); // and the x shear in the second row
+
+ Multiply(mat);
+ return *this;
+}
+
+TransformationMatrix& TransformationMatrix::ApplyPerspective(double p) {
+ TransformationMatrix mat;
+ if (p != 0)
+ mat.matrix_[2][3] = -1 / p;
+
+ Multiply(mat);
+ return *this;
+}
+
+TransformationMatrix& TransformationMatrix::ApplyTransformOrigin(double x,
+ double y,
+ double z) {
+ PostTranslate3d(x, y, z);
+ Translate3d(-x, -y, -z);
+ return *this;
+}
+
+TransformationMatrix& TransformationMatrix::Zoom(double zoom_factor) {
+ matrix_[0][3] /= zoom_factor;
+ matrix_[1][3] /= zoom_factor;
+ matrix_[2][3] /= zoom_factor;
+ matrix_[3][0] *= zoom_factor;
+ matrix_[3][1] *= zoom_factor;
+ matrix_[3][2] *= zoom_factor;
+ return *this;
+}
+
+// Calculates *this = *this * mat.
+// Note: As we are using the column vector convention, i.e. T * P,
+// (lhs * rhs) * P = lhs * (rhs * P)
+// That means from the perspective of the transformed object, the combined
+// transform is equal to applying the rhs(mat) first, then lhs(*this) second.
+// For example:
+// TransformationMatrix lhs; lhs.Rotate(90.f);
+// TransformationMatrix rhs; rhs.Translate(12.f, 34.f);
+// TransformationMatrix prod = lhs;
+// prod.Multiply(rhs);
+// lhs.MapPoint(rhs.MapPoint(p)) == prod.MapPoint(p)
+// Also 'prod' corresponds to CSS transform:rotateZ(90deg)translate(12px,34px).
+// TODO(crbug.com/584508): As of 2017-04-11, the ARM64 CQ bots skip
+// blink_platform_unittests, therefore the ARM64 branch is not tested by CQ.
+TransformationMatrix& TransformationMatrix::Multiply(
+ const TransformationMatrix& mat) {
+#if defined(ARCH_CPU_ARM64)
+ double* left_matrix = &(matrix_[0][0]);
+ const double* right_matrix = &(mat.matrix_[0][0]);
+ asm volatile(
+ // Load this->matrix_ to v24 - v31.
+ // Load mat.matrix_ to v16 - v23.
+ // Result: *this = *this * mat
+ // | v0 v2 v4 v6 | | v24 v26 v28 v30 | | v16 v18 v20 v22 |
+ // | | = | | * | |
+ // | v1 v3 v5 v7 | | v25 v27 v29 v31 | | v17 v19 v21 v23 |
+ // | | | | | |
+ "mov x9, %[left_matrix] \t\n"
+ "ld1 {v16.2d - v19.2d}, [%[right_matrix]], 64 \t\n"
+ "ld1 {v20.2d - v23.2d}, [%[right_matrix]] \t\n"
+ "ld1 {v24.2d - v27.2d}, [%[left_matrix]], 64 \t\n"
+ "ld1 {v28.2d - v31.2d}, [%[left_matrix]] \t\n"
+
+ "fmul v0.2d, v24.2d, v16.d[0] \t\n"
+ "fmul v1.2d, v25.2d, v16.d[0] \t\n"
+ "fmul v2.2d, v24.2d, v18.d[0] \t\n"
+ "fmul v3.2d, v25.2d, v18.d[0] \t\n"
+ "fmul v4.2d, v24.2d, v20.d[0] \t\n"
+ "fmul v5.2d, v25.2d, v20.d[0] \t\n"
+ "fmul v6.2d, v24.2d, v22.d[0] \t\n"
+ "fmul v7.2d, v25.2d, v22.d[0] \t\n"
+
+ "fmla v0.2d, v26.2d, v16.d[1] \t\n"
+ "fmla v1.2d, v27.2d, v16.d[1] \t\n"
+ "fmla v2.2d, v26.2d, v18.d[1] \t\n"
+ "fmla v3.2d, v27.2d, v18.d[1] \t\n"
+ "fmla v4.2d, v26.2d, v20.d[1] \t\n"
+ "fmla v5.2d, v27.2d, v20.d[1] \t\n"
+ "fmla v6.2d, v26.2d, v22.d[1] \t\n"
+ "fmla v7.2d, v27.2d, v22.d[1] \t\n"
+
+ "fmla v0.2d, v28.2d, v17.d[0] \t\n"
+ "fmla v1.2d, v29.2d, v17.d[0] \t\n"
+ "fmla v2.2d, v28.2d, v19.d[0] \t\n"
+ "fmla v3.2d, v29.2d, v19.d[0] \t\n"
+ "fmla v4.2d, v28.2d, v21.d[0] \t\n"
+ "fmla v5.2d, v29.2d, v21.d[0] \t\n"
+ "fmla v6.2d, v28.2d, v23.d[0] \t\n"
+ "fmla v7.2d, v29.2d, v23.d[0] \t\n"
+
+ "fmla v0.2d, v30.2d, v17.d[1] \t\n"
+ "fmla v1.2d, v31.2d, v17.d[1] \t\n"
+ "fmla v2.2d, v30.2d, v19.d[1] \t\n"
+ "fmla v3.2d, v31.2d, v19.d[1] \t\n"
+ "fmla v4.2d, v30.2d, v21.d[1] \t\n"
+ "fmla v5.2d, v31.2d, v21.d[1] \t\n"
+ "fmla v6.2d, v30.2d, v23.d[1] \t\n"
+ "fmla v7.2d, v31.2d, v23.d[1] \t\n"
+
+ "st1 {v0.2d - v3.2d}, [x9], 64 \t\n"
+ "st1 {v4.2d - v7.2d}, [x9] \t\n"
+ : [right_matrix] "+r"(right_matrix), [left_matrix] "+r"(left_matrix)
+ :
+ : "memory", "x9", "v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23",
+ "v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31", "v0", "v1",
+ "v2", "v3", "v4", "v5", "v6", "v7");
+#elif HAVE_MIPS_MSA_INTRINSICS
+ v2f64 v_right_m0, v_right_m1, v_right_m2, v_right_m3, v_right_m4, v_right_m5,
+ v_right_m6, v_right_m7;
+ v2f64 v_left_m0, v_left_m1, v_left_m2, v_left_m3, v_left_m4, v_left_m5,
+ v_left_m6, v_left_m7;
+ v2f64 v_tmp_m0, v_tmp_m1, v_tmp_m2, v_tmp_m3;
+
+ v_left_m0 = LD_DP(&(matrix_[0][0]));
+ v_left_m1 = LD_DP(&(matrix_[0][2]));
+ v_left_m2 = LD_DP(&(matrix_[1][0]));
+ v_left_m3 = LD_DP(&(matrix_[1][2]));
+ v_left_m4 = LD_DP(&(matrix_[2][0]));
+ v_left_m5 = LD_DP(&(matrix_[2][2]));
+ v_left_m6 = LD_DP(&(matrix_[3][0]));
+ v_left_m7 = LD_DP(&(matrix_[3][2]));
+
+ v_right_m0 = LD_DP(&(mat.matrix_[0][0]));
+ v_right_m2 = LD_DP(&(mat.matrix_[0][2]));
+ v_right_m4 = LD_DP(&(mat.matrix_[1][0]));
+ v_right_m6 = LD_DP(&(mat.matrix_[1][2]));
+
+ v_right_m1 = (v2f64)__msa_splati_d((v2i64)v_right_m0, 1);
+ v_right_m0 = (v2f64)__msa_splati_d((v2i64)v_right_m0, 0);
+ v_right_m3 = (v2f64)__msa_splati_d((v2i64)v_right_m2, 1);
+ v_right_m2 = (v2f64)__msa_splati_d((v2i64)v_right_m2, 0);
+ v_right_m5 = (v2f64)__msa_splati_d((v2i64)v_right_m4, 1);
+ v_right_m4 = (v2f64)__msa_splati_d((v2i64)v_right_m4, 0);
+ v_right_m7 = (v2f64)__msa_splati_d((v2i64)v_right_m6, 1);
+ v_right_m6 = (v2f64)__msa_splati_d((v2i64)v_right_m6, 0);
+
+ v_tmp_m0 = v_right_m0 * v_left_m0;
+ v_tmp_m1 = v_right_m0 * v_left_m1;
+ v_tmp_m0 += v_right_m1 * v_left_m2;
+ v_tmp_m1 += v_right_m1 * v_left_m3;
+ v_tmp_m0 += v_right_m2 * v_left_m4;
+ v_tmp_m1 += v_right_m2 * v_left_m5;
+ v_tmp_m0 += v_right_m3 * v_left_m6;
+ v_tmp_m1 += v_right_m3 * v_left_m7;
+
+ v_tmp_m2 = v_right_m4 * v_left_m0;
+ v_tmp_m3 = v_right_m4 * v_left_m1;
+ v_tmp_m2 += v_right_m5 * v_left_m2;
+ v_tmp_m3 += v_right_m5 * v_left_m3;
+ v_tmp_m2 += v_right_m6 * v_left_m4;
+ v_tmp_m3 += v_right_m6 * v_left_m5;
+ v_tmp_m2 += v_right_m7 * v_left_m6;
+ v_tmp_m3 += v_right_m7 * v_left_m7;
+
+ v_right_m0 = LD_DP(&(mat.matrix_[2][0]));
+ v_right_m2 = LD_DP(&(mat.matrix_[2][2]));
+ v_right_m4 = LD_DP(&(mat.matrix_[3][0]));
+ v_right_m6 = LD_DP(&(mat.matrix_[3][2]));
+
+ ST_DP(v_tmp_m0, &(matrix_[0][0]));
+ ST_DP(v_tmp_m1, &(matrix_[0][2]));
+ ST_DP(v_tmp_m2, &(matrix_[1][0]));
+ ST_DP(v_tmp_m3, &(matrix_[1][2]));
+
+ v_right_m1 = (v2f64)__msa_splati_d((v2i64)v_right_m0, 1);
+ v_right_m0 = (v2f64)__msa_splati_d((v2i64)v_right_m0, 0);
+ v_right_m3 = (v2f64)__msa_splati_d((v2i64)v_right_m2, 1);
+ v_right_m2 = (v2f64)__msa_splati_d((v2i64)v_right_m2, 0);
+ v_right_m5 = (v2f64)__msa_splati_d((v2i64)v_right_m4, 1);
+ v_right_m4 = (v2f64)__msa_splati_d((v2i64)v_right_m4, 0);
+ v_right_m7 = (v2f64)__msa_splati_d((v2i64)v_right_m6, 1);
+ v_right_m6 = (v2f64)__msa_splati_d((v2i64)v_right_m6, 0);
+
+ v_tmp_m0 = v_right_m0 * v_left_m0;
+ v_tmp_m1 = v_right_m0 * v_left_m1;
+ v_tmp_m0 += v_right_m1 * v_left_m2;
+ v_tmp_m1 += v_right_m1 * v_left_m3;
+ v_tmp_m0 += v_right_m2 * v_left_m4;
+ v_tmp_m1 += v_right_m2 * v_left_m5;
+ v_tmp_m0 += v_right_m3 * v_left_m6;
+ v_tmp_m1 += v_right_m3 * v_left_m7;
+
+ v_tmp_m2 = v_right_m4 * v_left_m0;
+ v_tmp_m3 = v_right_m4 * v_left_m1;
+ v_tmp_m2 += v_right_m5 * v_left_m2;
+ v_tmp_m3 += v_right_m5 * v_left_m3;
+ v_tmp_m2 += v_right_m6 * v_left_m4;
+ v_tmp_m3 += v_right_m6 * v_left_m5;
+ v_tmp_m2 += v_right_m7 * v_left_m6;
+ v_tmp_m3 += v_right_m7 * v_left_m7;
+
+ ST_DP(v_tmp_m0, &(matrix_[2][0]));
+ ST_DP(v_tmp_m1, &(matrix_[2][2]));
+ ST_DP(v_tmp_m2, &(matrix_[3][0]));
+ ST_DP(v_tmp_m3, &(matrix_[3][2]));
+#elif defined(TRANSFORMATION_MATRIX_USE_X86_64_SSE2)
+ // x86_64 has 16 XMM registers which is enough to do the multiplication fully
+ // in registers.
+ __m128d matrix_block_a = _mm_load_pd(&(matrix_[0][0]));
+ __m128d matrix_block_c = _mm_load_pd(&(matrix_[1][0]));
+ __m128d matrix_block_e = _mm_load_pd(&(matrix_[2][0]));
+ __m128d matrix_block_g = _mm_load_pd(&(matrix_[3][0]));
+
+ // First column.
+ __m128d other_matrix_first_param = _mm_set1_pd(mat.matrix_[0][0]);
+ __m128d other_matrix_second_param = _mm_set1_pd(mat.matrix_[0][1]);
+ __m128d other_matrix_third_param = _mm_set1_pd(mat.matrix_[0][2]);
+ __m128d other_matrix_fourth_param = _mm_set1_pd(mat.matrix_[0][3]);
+
+ // output00 and output01.
+ __m128d accumulator = _mm_mul_pd(matrix_block_a, other_matrix_first_param);
+ __m128d temp1 = _mm_mul_pd(matrix_block_c, other_matrix_second_param);
+ __m128d temp2 = _mm_mul_pd(matrix_block_e, other_matrix_third_param);
+ __m128d temp3 = _mm_mul_pd(matrix_block_g, other_matrix_fourth_param);
+
+ __m128d matrix_block_b = _mm_load_pd(&(matrix_[0][2]));
+ __m128d matrix_block_d = _mm_load_pd(&(matrix_[1][2]));
+ __m128d matrix_block_f = _mm_load_pd(&(matrix_[2][2]));
+ __m128d matrix_block_h = _mm_load_pd(&(matrix_[3][2]));
+
+ accumulator = _mm_add_pd(accumulator, temp1);
+ accumulator = _mm_add_pd(accumulator, temp2);
+ accumulator = _mm_add_pd(accumulator, temp3);
+ _mm_store_pd(&matrix_[0][0], accumulator);
+
+ // output02 and output03.
+ accumulator = _mm_mul_pd(matrix_block_b, other_matrix_first_param);
+ temp1 = _mm_mul_pd(matrix_block_d, other_matrix_second_param);
+ temp2 = _mm_mul_pd(matrix_block_f, other_matrix_third_param);
+ temp3 = _mm_mul_pd(matrix_block_h, other_matrix_fourth_param);
+
+ accumulator = _mm_add_pd(accumulator, temp1);
+ accumulator = _mm_add_pd(accumulator, temp2);
+ accumulator = _mm_add_pd(accumulator, temp3);
+ _mm_store_pd(&matrix_[0][2], accumulator);
+
+ // Second column.
+ other_matrix_first_param = _mm_set1_pd(mat.matrix_[1][0]);
+ other_matrix_second_param = _mm_set1_pd(mat.matrix_[1][1]);
+ other_matrix_third_param = _mm_set1_pd(mat.matrix_[1][2]);
+ other_matrix_fourth_param = _mm_set1_pd(mat.matrix_[1][3]);
+
+ // output10 and output11.
+ accumulator = _mm_mul_pd(matrix_block_a, other_matrix_first_param);
+ temp1 = _mm_mul_pd(matrix_block_c, other_matrix_second_param);
+ temp2 = _mm_mul_pd(matrix_block_e, other_matrix_third_param);
+ temp3 = _mm_mul_pd(matrix_block_g, other_matrix_fourth_param);
+
+ accumulator = _mm_add_pd(accumulator, temp1);
+ accumulator = _mm_add_pd(accumulator, temp2);
+ accumulator = _mm_add_pd(accumulator, temp3);
+ _mm_store_pd(&matrix_[1][0], accumulator);
+
+ // output12 and output13.
+ accumulator = _mm_mul_pd(matrix_block_b, other_matrix_first_param);
+ temp1 = _mm_mul_pd(matrix_block_d, other_matrix_second_param);
+ temp2 = _mm_mul_pd(matrix_block_f, other_matrix_third_param);
+ temp3 = _mm_mul_pd(matrix_block_h, other_matrix_fourth_param);
+
+ accumulator = _mm_add_pd(accumulator, temp1);
+ accumulator = _mm_add_pd(accumulator, temp2);
+ accumulator = _mm_add_pd(accumulator, temp3);
+ _mm_store_pd(&matrix_[1][2], accumulator);
+
+ // Third column.
+ other_matrix_first_param = _mm_set1_pd(mat.matrix_[2][0]);
+ other_matrix_second_param = _mm_set1_pd(mat.matrix_[2][1]);
+ other_matrix_third_param = _mm_set1_pd(mat.matrix_[2][2]);
+ other_matrix_fourth_param = _mm_set1_pd(mat.matrix_[2][3]);
+
+ // output20 and output21.
+ accumulator = _mm_mul_pd(matrix_block_a, other_matrix_first_param);
+ temp1 = _mm_mul_pd(matrix_block_c, other_matrix_second_param);
+ temp2 = _mm_mul_pd(matrix_block_e, other_matrix_third_param);
+ temp3 = _mm_mul_pd(matrix_block_g, other_matrix_fourth_param);
+
+ accumulator = _mm_add_pd(accumulator, temp1);
+ accumulator = _mm_add_pd(accumulator, temp2);
+ accumulator = _mm_add_pd(accumulator, temp3);
+ _mm_store_pd(&matrix_[2][0], accumulator);
+
+ // output22 and output23.
+ accumulator = _mm_mul_pd(matrix_block_b, other_matrix_first_param);
+ temp1 = _mm_mul_pd(matrix_block_d, other_matrix_second_param);
+ temp2 = _mm_mul_pd(matrix_block_f, other_matrix_third_param);
+ temp3 = _mm_mul_pd(matrix_block_h, other_matrix_fourth_param);
+
+ accumulator = _mm_add_pd(accumulator, temp1);
+ accumulator = _mm_add_pd(accumulator, temp2);
+ accumulator = _mm_add_pd(accumulator, temp3);
+ _mm_store_pd(&matrix_[2][2], accumulator);
+
+ // Fourth column.
+ other_matrix_first_param = _mm_set1_pd(mat.matrix_[3][0]);
+ other_matrix_second_param = _mm_set1_pd(mat.matrix_[3][1]);
+ other_matrix_third_param = _mm_set1_pd(mat.matrix_[3][2]);
+ other_matrix_fourth_param = _mm_set1_pd(mat.matrix_[3][3]);
+
+ // output30 and output31.
+ accumulator = _mm_mul_pd(matrix_block_a, other_matrix_first_param);
+ temp1 = _mm_mul_pd(matrix_block_c, other_matrix_second_param);
+ temp2 = _mm_mul_pd(matrix_block_e, other_matrix_third_param);
+ temp3 = _mm_mul_pd(matrix_block_g, other_matrix_fourth_param);
+
+ accumulator = _mm_add_pd(accumulator, temp1);
+ accumulator = _mm_add_pd(accumulator, temp2);
+ accumulator = _mm_add_pd(accumulator, temp3);
+ _mm_store_pd(&matrix_[3][0], accumulator);
+
+ // output32 and output33.
+ accumulator = _mm_mul_pd(matrix_block_b, other_matrix_first_param);
+ temp1 = _mm_mul_pd(matrix_block_d, other_matrix_second_param);
+ temp2 = _mm_mul_pd(matrix_block_f, other_matrix_third_param);
+ temp3 = _mm_mul_pd(matrix_block_h, other_matrix_fourth_param);
+
+ accumulator = _mm_add_pd(accumulator, temp1);
+ accumulator = _mm_add_pd(accumulator, temp2);
+ accumulator = _mm_add_pd(accumulator, temp3);
+ _mm_store_pd(&matrix_[3][2], accumulator);
+#else
+ Matrix4 tmp;
+
+ tmp[0][0] =
+ (mat.matrix_[0][0] * matrix_[0][0] + mat.matrix_[0][1] * matrix_[1][0] +
+ mat.matrix_[0][2] * matrix_[2][0] + mat.matrix_[0][3] * matrix_[3][0]);
+ tmp[0][1] =
+ (mat.matrix_[0][0] * matrix_[0][1] + mat.matrix_[0][1] * matrix_[1][1] +
+ mat.matrix_[0][2] * matrix_[2][1] + mat.matrix_[0][3] * matrix_[3][1]);
+ tmp[0][2] =
+ (mat.matrix_[0][0] * matrix_[0][2] + mat.matrix_[0][1] * matrix_[1][2] +
+ mat.matrix_[0][2] * matrix_[2][2] + mat.matrix_[0][3] * matrix_[3][2]);
+ tmp[0][3] =
+ (mat.matrix_[0][0] * matrix_[0][3] + mat.matrix_[0][1] * matrix_[1][3] +
+ mat.matrix_[0][2] * matrix_[2][3] + mat.matrix_[0][3] * matrix_[3][3]);
+
+ tmp[1][0] =
+ (mat.matrix_[1][0] * matrix_[0][0] + mat.matrix_[1][1] * matrix_[1][0] +
+ mat.matrix_[1][2] * matrix_[2][0] + mat.matrix_[1][3] * matrix_[3][0]);
+ tmp[1][1] =
+ (mat.matrix_[1][0] * matrix_[0][1] + mat.matrix_[1][1] * matrix_[1][1] +
+ mat.matrix_[1][2] * matrix_[2][1] + mat.matrix_[1][3] * matrix_[3][1]);
+ tmp[1][2] =
+ (mat.matrix_[1][0] * matrix_[0][2] + mat.matrix_[1][1] * matrix_[1][2] +
+ mat.matrix_[1][2] * matrix_[2][2] + mat.matrix_[1][3] * matrix_[3][2]);
+ tmp[1][3] =
+ (mat.matrix_[1][0] * matrix_[0][3] + mat.matrix_[1][1] * matrix_[1][3] +
+ mat.matrix_[1][2] * matrix_[2][3] + mat.matrix_[1][3] * matrix_[3][3]);
+
+ tmp[2][0] =
+ (mat.matrix_[2][0] * matrix_[0][0] + mat.matrix_[2][1] * matrix_[1][0] +
+ mat.matrix_[2][2] * matrix_[2][0] + mat.matrix_[2][3] * matrix_[3][0]);
+ tmp[2][1] =
+ (mat.matrix_[2][0] * matrix_[0][1] + mat.matrix_[2][1] * matrix_[1][1] +
+ mat.matrix_[2][2] * matrix_[2][1] + mat.matrix_[2][3] * matrix_[3][1]);
+ tmp[2][2] =
+ (mat.matrix_[2][0] * matrix_[0][2] + mat.matrix_[2][1] * matrix_[1][2] +
+ mat.matrix_[2][2] * matrix_[2][2] + mat.matrix_[2][3] * matrix_[3][2]);
+ tmp[2][3] =
+ (mat.matrix_[2][0] * matrix_[0][3] + mat.matrix_[2][1] * matrix_[1][3] +
+ mat.matrix_[2][2] * matrix_[2][3] + mat.matrix_[2][3] * matrix_[3][3]);
+
+ tmp[3][0] =
+ (mat.matrix_[3][0] * matrix_[0][0] + mat.matrix_[3][1] * matrix_[1][0] +
+ mat.matrix_[3][2] * matrix_[2][0] + mat.matrix_[3][3] * matrix_[3][0]);
+ tmp[3][1] =
+ (mat.matrix_[3][0] * matrix_[0][1] + mat.matrix_[3][1] * matrix_[1][1] +
+ mat.matrix_[3][2] * matrix_[2][1] + mat.matrix_[3][3] * matrix_[3][1]);
+ tmp[3][2] =
+ (mat.matrix_[3][0] * matrix_[0][2] + mat.matrix_[3][1] * matrix_[1][2] +
+ mat.matrix_[3][2] * matrix_[2][2] + mat.matrix_[3][3] * matrix_[3][2]);
+ tmp[3][3] =
+ (mat.matrix_[3][0] * matrix_[0][3] + mat.matrix_[3][1] * matrix_[1][3] +
+ mat.matrix_[3][2] * matrix_[2][3] + mat.matrix_[3][3] * matrix_[3][3]);
+
+ SetMatrix(tmp);
+#endif
+ return *this;
+}
+
+void TransformationMatrix::MultVecMatrix(double x,
+ double y,
+ double& result_x,
+ double& result_y) const {
+ result_x = matrix_[3][0] + x * matrix_[0][0] + y * matrix_[1][0];
+ result_y = matrix_[3][1] + x * matrix_[0][1] + y * matrix_[1][1];
+ double w = matrix_[3][3] + x * matrix_[0][3] + y * matrix_[1][3];
+ if (w != 1 && w != 0) {
+ result_x /= w;
+ result_y /= w;
+ }
+}
+
+void TransformationMatrix::MultVecMatrix(double x,
+ double y,
+ double z,
+ double& result_x,
+ double& result_y,
+ double& result_z) const {
+ result_x =
+ matrix_[3][0] + x * matrix_[0][0] + y * matrix_[1][0] + z * matrix_[2][0];
+ result_y =
+ matrix_[3][1] + x * matrix_[0][1] + y * matrix_[1][1] + z * matrix_[2][1];
+ result_z =
+ matrix_[3][2] + x * matrix_[0][2] + y * matrix_[1][2] + z * matrix_[2][2];
+ double w =
+ matrix_[3][3] + x * matrix_[0][3] + y * matrix_[1][3] + z * matrix_[2][3];
+ if (w != 1 && w != 0) {
+ result_x /= w;
+ result_y /= w;
+ result_z /= w;
+ }
+}
+
+bool TransformationMatrix::IsInvertible() const {
+ if (IsIdentityOrTranslation())
+ return true;
+
+ double det = blink::Determinant4x4(matrix_);
+
+ if (fabs(det) < kSmallNumber)
+ return false;
+
+ return true;
+}
+
+TransformationMatrix TransformationMatrix::Inverse() const {
+ if (IsIdentityOrTranslation()) {
+ // identity matrix
+ if (matrix_[3][0] == 0 && matrix_[3][1] == 0 && matrix_[3][2] == 0)
+ return TransformationMatrix();
+
+ // translation
+ return TransformationMatrix(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0,
+ -matrix_[3][0], -matrix_[3][1], -matrix_[3][2],
+ 1);
+ }
+
+ TransformationMatrix inv_mat;
+ bool inverted = blink::Inverse(matrix_, inv_mat.matrix_);
+ if (!inverted)
+ return TransformationMatrix();
+
+ return inv_mat;
+}
+
+void TransformationMatrix::MakeAffine() {
+ matrix_[0][2] = 0;
+ matrix_[0][3] = 0;
+
+ matrix_[1][2] = 0;
+ matrix_[1][3] = 0;
+
+ matrix_[2][0] = 0;
+ matrix_[2][1] = 0;
+ matrix_[2][2] = 1;
+ matrix_[2][3] = 0;
+
+ matrix_[3][2] = 0;
+ matrix_[3][3] = 1;
+}
+
+AffineTransform TransformationMatrix::ToAffineTransform() const {
+ return AffineTransform(matrix_[0][0], matrix_[0][1], matrix_[1][0],
+ matrix_[1][1], matrix_[3][0], matrix_[3][1]);
+}
+
+void TransformationMatrix::FlattenTo2d() {
+ matrix_[2][0] = 0;
+ matrix_[2][1] = 0;
+ matrix_[0][2] = 0;
+ matrix_[1][2] = 0;
+ matrix_[2][2] = 1;
+ matrix_[3][2] = 0;
+ matrix_[2][3] = 0;
+}
+
+static inline void BlendFloat(double& from, double to, double progress) {
+ if (from != to)
+ from = from + (to - from) * progress;
+}
+
+void TransformationMatrix::Blend(const TransformationMatrix& from,
+ double progress) {
+ if (from.IsIdentity() && IsIdentity())
+ return;
+
+ // decompose
+ DecomposedType from_decomp;
+ DecomposedType to_decomp;
+ if (!from.Decompose(from_decomp) || !Decompose(to_decomp)) {
+ if (progress < 0.5)
+ *this = from;
+ return;
+ }
+
+ // interpolate
+ BlendFloat(from_decomp.scale_x, to_decomp.scale_x, progress);
+ BlendFloat(from_decomp.scale_y, to_decomp.scale_y, progress);
+ BlendFloat(from_decomp.scale_z, to_decomp.scale_z, progress);
+ BlendFloat(from_decomp.skew_xy, to_decomp.skew_xy, progress);
+ BlendFloat(from_decomp.skew_xz, to_decomp.skew_xz, progress);
+ BlendFloat(from_decomp.skew_yz, to_decomp.skew_yz, progress);
+ BlendFloat(from_decomp.translate_x, to_decomp.translate_x, progress);
+ BlendFloat(from_decomp.translate_y, to_decomp.translate_y, progress);
+ BlendFloat(from_decomp.translate_z, to_decomp.translate_z, progress);
+ BlendFloat(from_decomp.perspective_x, to_decomp.perspective_x, progress);
+ BlendFloat(from_decomp.perspective_y, to_decomp.perspective_y, progress);
+ BlendFloat(from_decomp.perspective_z, to_decomp.perspective_z, progress);
+ BlendFloat(from_decomp.perspective_w, to_decomp.perspective_w, progress);
+
+ Slerp(&from_decomp.quaternion_x, &to_decomp.quaternion_x, progress);
+
+ // recompose
+ Recompose(from_decomp);
+}
+
+bool TransformationMatrix::Decompose(DecomposedType& decomp) const {
+ if (IsIdentity()) {
+ memset(&decomp, 0, sizeof(decomp));
+ decomp.perspective_w = 1;
+ decomp.scale_x = 1;
+ decomp.scale_y = 1;
+ decomp.scale_z = 1;
+ }
+
+ if (!blink::Decompose(matrix_, decomp))
+ return false;
+ return true;
+}
+
+void TransformationMatrix::Recompose(const DecomposedType& decomp) {
+ MakeIdentity();
+
+ // first apply perspective
+ matrix_[0][3] = decomp.perspective_x;
+ matrix_[1][3] = decomp.perspective_y;
+ matrix_[2][3] = decomp.perspective_z;
+ matrix_[3][3] = decomp.perspective_w;
+
+ // now translate
+ Translate3d(decomp.translate_x, decomp.translate_y, decomp.translate_z);
+
+ // apply rotation
+ double xx = decomp.quaternion_x * decomp.quaternion_x;
+ double xy = decomp.quaternion_x * decomp.quaternion_y;
+ double xz = decomp.quaternion_x * decomp.quaternion_z;
+ double xw = decomp.quaternion_x * decomp.quaternion_w;
+ double yy = decomp.quaternion_y * decomp.quaternion_y;
+ double yz = decomp.quaternion_y * decomp.quaternion_z;
+ double yw = decomp.quaternion_y * decomp.quaternion_w;
+ double zz = decomp.quaternion_z * decomp.quaternion_z;
+ double zw = decomp.quaternion_z * decomp.quaternion_w;
+
+ // Construct a composite rotation matrix from the quaternion values
+ TransformationMatrix rotation_matrix(
+ 1 - 2 * (yy + zz), 2 * (xy - zw), 2 * (xz + yw), 0, 2 * (xy + zw),
+ 1 - 2 * (xx + zz), 2 * (yz - xw), 0, 2 * (xz - yw), 2 * (yz + xw),
+ 1 - 2 * (xx + yy), 0, 0, 0, 0, 1);
+
+ Multiply(rotation_matrix);
+
+ // now apply skew
+ if (decomp.skew_yz) {
+ TransformationMatrix tmp;
+ tmp.SetM32(decomp.skew_yz);
+ Multiply(tmp);
+ }
+
+ if (decomp.skew_xz) {
+ TransformationMatrix tmp;
+ tmp.SetM31(decomp.skew_xz);
+ Multiply(tmp);
+ }
+
+ if (decomp.skew_xy) {
+ TransformationMatrix tmp;
+ tmp.SetM21(decomp.skew_xy);
+ Multiply(tmp);
+ }
+
+ // finally, apply scale
+ Scale3d(decomp.scale_x, decomp.scale_y, decomp.scale_z);
+}
+
+bool TransformationMatrix::IsIntegerTranslation() const {
+ if (!IsIdentityOrTranslation())
+ return false;
+
+ // Check for translate Z.
+ if (matrix_[3][2])
+ return false;
+
+ // Check for non-integer translate X/Y.
+ if (static_cast<int>(matrix_[3][0]) != matrix_[3][0] ||
+ static_cast<int>(matrix_[3][1]) != matrix_[3][1])
+ return false;
+
+ return true;
+}
+
+FloatSize TransformationMatrix::To2DTranslation() const {
+ DCHECK(IsIdentityOr2DTranslation());
+ return FloatSize(matrix_[3][0], matrix_[3][1]);
+}
+
+void TransformationMatrix::ToColumnMajorFloatArray(FloatMatrix4& result) const {
+ result[0] = M11();
+ result[1] = M12();
+ result[2] = M13();
+ result[3] = M14();
+ result[4] = M21();
+ result[5] = M22();
+ result[6] = M23();
+ result[7] = M24();
+ result[8] = M31();
+ result[9] = M32();
+ result[10] = M33();
+ result[11] = M34();
+ result[12] = M41();
+ result[13] = M42();
+ result[14] = M43();
+ result[15] = M44();
+}
+
+SkMatrix44 TransformationMatrix::ToSkMatrix44(
+ const TransformationMatrix& matrix) {
+ SkMatrix44 ret(SkMatrix44::kUninitialized_Constructor);
+ ret.setDouble(0, 0, matrix.M11());
+ ret.setDouble(0, 1, matrix.M21());
+ ret.setDouble(0, 2, matrix.M31());
+ ret.setDouble(0, 3, matrix.M41());
+ ret.setDouble(1, 0, matrix.M12());
+ ret.setDouble(1, 1, matrix.M22());
+ ret.setDouble(1, 2, matrix.M32());
+ ret.setDouble(1, 3, matrix.M42());
+ ret.setDouble(2, 0, matrix.M13());
+ ret.setDouble(2, 1, matrix.M23());
+ ret.setDouble(2, 2, matrix.M33());
+ ret.setDouble(2, 3, matrix.M43());
+ ret.setDouble(3, 0, matrix.M14());
+ ret.setDouble(3, 1, matrix.M24());
+ ret.setDouble(3, 2, matrix.M34());
+ ret.setDouble(3, 3, matrix.M44());
+ return ret;
+}
+
+String TransformationMatrix::ToString(bool as_matrix) const {
+ if (as_matrix) {
+ // Return as a matrix in row-major order.
+ return String::Format(
+ "[%lg,%lg,%lg,%lg,\n%lg,%lg,%lg,%lg,\n%lg,%lg,%lg,%lg,\n%lg,%lg,%lg,%"
+ "lg]",
+ M11(), M21(), M31(), M41(), M12(), M22(), M32(), M42(), M13(), M23(),
+ M33(), M43(), M14(), M24(), M34(), M44());
+ }
+
+ TransformationMatrix::DecomposedType decomposition;
+ if (!Decompose(decomposition))
+ return ToString(true) + " (degenerate)";
+
+ if (IsIdentityOrTranslation()) {
+ if (decomposition.translate_x == 0 && decomposition.translate_y == 0 &&
+ decomposition.translate_z == 0)
+ return "identity";
+ return String::Format("translation(%lg,%lg,%lg)", decomposition.translate_x,
+ decomposition.translate_y, decomposition.translate_z);
+ }
+
+ return String::Format(
+ "translation(%lg,%lg,%lg), scale(%lg,%lg,%lg), skew(%lg,%lg,%lg), "
+ "quaternion(%lg,%lg,%lg,%lg), perspective(%lg,%lg,%lg,%lg)",
+ decomposition.translate_x, decomposition.translate_y,
+ decomposition.translate_z, decomposition.scale_x, decomposition.scale_y,
+ decomposition.scale_z, decomposition.skew_xy, decomposition.skew_xz,
+ decomposition.skew_yz, decomposition.quaternion_x,
+ decomposition.quaternion_y, decomposition.quaternion_z,
+ decomposition.quaternion_w, decomposition.perspective_x,
+ decomposition.perspective_y, decomposition.perspective_z,
+ decomposition.perspective_w);
+}
+
+std::ostream& operator<<(std::ostream& ostream,
+ const TransformationMatrix& transform) {
+ return ostream << transform.ToString();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/transforms/transformation_matrix.h b/chromium/third_party/blink/renderer/platform/transforms/transformation_matrix.h
new file mode 100644
index 00000000000..768903d8fa7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/transforms/transformation_matrix.h
@@ -0,0 +1,526 @@
+/*
+ * Copyright (C) 2005, 2006 Apple Computer, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_TRANSFORMATION_MATRIX_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_TRANSFORMATION_MATRIX_H_
+
+#include <string.h> // for memcpy
+#include <memory>
+#include "SkMatrix44.h"
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/geometry/float_point.h"
+#include "third_party/blink/renderer/platform/geometry/float_point_3d.h"
+#include "third_party/blink/renderer/platform/wtf/alignment.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+class AffineTransform;
+class IntRect;
+class LayoutRect;
+class FloatRect;
+class FloatQuad;
+class FloatBox;
+struct Rotation;
+#if defined(ARCH_CPU_X86_64)
+#define TRANSFORMATION_MATRIX_USE_X86_64_SSE2
+#endif
+
+class PLATFORM_EXPORT TransformationMatrix {
+ // TransformationMatrix must not be allocated on Oilpan's heap since
+ // Oilpan doesn't (yet) have an ability to allocate the TransformationMatrix
+ // with 16-byte alignment. PartitionAlloc has the ability.
+ USING_FAST_MALLOC(TransformationMatrix);
+
+ public:
+// Throughout this class, we will be speaking in column vector convention.
+// i.e. Applying a transform T to point P is T * P.
+// The elements of the matrix and the vector looks like:
+// | scale_x skew_y_x skew_z_x translate_x | | x |
+// | skew_x_y scale_y skew_z_y translate_y | * | y |
+// | skew_x_z skew_y_z scale_z translate_z | | z |
+// | persp_x persp_y persp_z persp_w | | w |
+// Internally the matrix is stored as a 2-dimensional array in col-major order.
+// In other words, this is the layout of the matrix:
+// | matrix_[0][0] matrix_[1][0] matrix_[2][0] matrix_[3][0] |
+// | matrix_[0][1] matrix_[1][1] matrix_[2][1] matrix_[3][1] |
+// | matrix_[0][2] matrix_[1][2] matrix_[2][2] matrix_[3][2] |
+// | matrix_[0][3] matrix_[1][3] matrix_[2][3] matrix_[3][3] |
+#if defined(TRANSFORMATION_MATRIX_USE_X86_64_SSE2)
+ typedef WTF_ALIGNED(double, Matrix4[4][4], 16);
+#else
+ typedef double Matrix4[4][4];
+#endif
+
+ static std::unique_ptr<TransformationMatrix> Create() {
+ return std::make_unique<TransformationMatrix>();
+ }
+ static std::unique_ptr<TransformationMatrix> Create(
+ const TransformationMatrix& t) {
+ return std::make_unique<TransformationMatrix>(t);
+ }
+ static std::unique_ptr<TransformationMatrix> Create(double a,
+ double b,
+ double c,
+ double d,
+ double e,
+ double f) {
+ return std::make_unique<TransformationMatrix>(a, b, c, d, e, f);
+ }
+ static std::unique_ptr<TransformationMatrix> Create(double m11,
+ double m12,
+ double m13,
+ double m14,
+ double m21,
+ double m22,
+ double m23,
+ double m24,
+ double m31,
+ double m32,
+ double m33,
+ double m34,
+ double m41,
+ double m42,
+ double m43,
+ double m44) {
+ return std::make_unique<TransformationMatrix>(m11, m12, m13, m14, m21, m22,
+ m23, m24, m31, m32, m33, m34,
+ m41, m42, m43, m44);
+ }
+
+ TransformationMatrix() {
+ CheckAlignment();
+ MakeIdentity();
+ }
+ TransformationMatrix(const AffineTransform&);
+ TransformationMatrix(const TransformationMatrix& t) {
+ CheckAlignment();
+ *this = t;
+ }
+ TransformationMatrix(double a,
+ double b,
+ double c,
+ double d,
+ double e,
+ double f) {
+ CheckAlignment();
+ SetMatrix(a, b, c, d, e, f);
+ }
+ TransformationMatrix(double m11,
+ double m12,
+ double m13,
+ double m14,
+ double m21,
+ double m22,
+ double m23,
+ double m24,
+ double m31,
+ double m32,
+ double m33,
+ double m34,
+ double m41,
+ double m42,
+ double m43,
+ double m44) {
+ CheckAlignment();
+ SetMatrix(m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41,
+ m42, m43, m44);
+ }
+ TransformationMatrix(const SkMatrix44& matrix) {
+ CheckAlignment();
+ SetMatrix(
+ matrix.get(0, 0), matrix.get(1, 0), matrix.get(2, 0), matrix.get(3, 0),
+ matrix.get(0, 1), matrix.get(1, 1), matrix.get(2, 1), matrix.get(3, 1),
+ matrix.get(0, 2), matrix.get(1, 2), matrix.get(2, 2), matrix.get(3, 2),
+ matrix.get(0, 3), matrix.get(1, 3), matrix.get(2, 3), matrix.get(3, 3));
+ }
+
+ void SetMatrix(double a, double b, double c, double d, double e, double f) {
+ matrix_[0][0] = a;
+ matrix_[0][1] = b;
+ matrix_[0][2] = 0;
+ matrix_[0][3] = 0;
+ matrix_[1][0] = c;
+ matrix_[1][1] = d;
+ matrix_[1][2] = 0;
+ matrix_[1][3] = 0;
+ matrix_[2][0] = 0;
+ matrix_[2][1] = 0;
+ matrix_[2][2] = 1;
+ matrix_[2][3] = 0;
+ matrix_[3][0] = e;
+ matrix_[3][1] = f;
+ matrix_[3][2] = 0;
+ matrix_[3][3] = 1;
+ }
+
+ void SetMatrix(double m11,
+ double m12,
+ double m13,
+ double m14,
+ double m21,
+ double m22,
+ double m23,
+ double m24,
+ double m31,
+ double m32,
+ double m33,
+ double m34,
+ double m41,
+ double m42,
+ double m43,
+ double m44) {
+ matrix_[0][0] = m11;
+ matrix_[0][1] = m12;
+ matrix_[0][2] = m13;
+ matrix_[0][3] = m14;
+ matrix_[1][0] = m21;
+ matrix_[1][1] = m22;
+ matrix_[1][2] = m23;
+ matrix_[1][3] = m24;
+ matrix_[2][0] = m31;
+ matrix_[2][1] = m32;
+ matrix_[2][2] = m33;
+ matrix_[2][3] = m34;
+ matrix_[3][0] = m41;
+ matrix_[3][1] = m42;
+ matrix_[3][2] = m43;
+ matrix_[3][3] = m44;
+ }
+
+ TransformationMatrix& operator=(const TransformationMatrix& t) {
+ SetMatrix(t.matrix_);
+ return *this;
+ }
+
+ TransformationMatrix& MakeIdentity() {
+ SetMatrix(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
+ return *this;
+ }
+
+ bool IsIdentity() const {
+ return matrix_[0][0] == 1 && matrix_[0][1] == 0 && matrix_[0][2] == 0 &&
+ matrix_[0][3] == 0 && matrix_[1][0] == 0 && matrix_[1][1] == 1 &&
+ matrix_[1][2] == 0 && matrix_[1][3] == 0 && matrix_[2][0] == 0 &&
+ matrix_[2][1] == 0 && matrix_[2][2] == 1 && matrix_[2][3] == 0 &&
+ matrix_[3][0] == 0 && matrix_[3][1] == 0 && matrix_[3][2] == 0 &&
+ matrix_[3][3] == 1;
+ }
+
+ // Map a 3D point through the transform, returning a 3D point.
+ FloatPoint3D MapPoint(const FloatPoint3D&) const;
+
+ // Map a 2D point through the transform, returning a 2D point.
+ // Note that this ignores the z component, effectively projecting the point
+ // into the z=0 plane.
+ FloatPoint MapPoint(const FloatPoint&) const;
+
+ // If the matrix has 3D components, the z component of the result is
+ // dropped, effectively projecting the rect into the z=0 plane
+ FloatRect MapRect(const FloatRect&) const;
+
+ // Rounds the resulting mapped rectangle out. This is helpful for bounding
+ // box computations but may not be what is wanted in other contexts.
+ IntRect MapRect(const IntRect&) const;
+ LayoutRect MapRect(const LayoutRect&) const;
+
+ // If the matrix has 3D components, the z component of the result is
+ // dropped, effectively projecting the quad into the z=0 plane
+ FloatQuad MapQuad(const FloatQuad&) const;
+
+ // Map a point on the z=0 plane into a point on
+ // the plane with with the transform applied, by extending
+ // a ray perpendicular to the source plane and computing
+ // the local x,y position of the point where that ray intersects
+ // with the destination plane.
+ FloatPoint ProjectPoint(const FloatPoint&, bool* clamped = nullptr) const;
+ // Projects the four corners of the quad
+ FloatQuad ProjectQuad(const FloatQuad&, bool* clamped = nullptr) const;
+ // Projects the four corners of the quad and takes a bounding box,
+ // while sanitizing values created when the w component is negative.
+ LayoutRect ClampedBoundsOfProjectedQuad(const FloatQuad&) const;
+
+ void TransformBox(FloatBox&) const;
+
+ // Important: These indices are spoken in col-major order. i.e.:
+ // | M11() M21() M31() M41() |
+ // | M12() M22() M32() M42() |
+ // | M13() M23() M33() M43() |
+ // | M14() M24() M34() M44() |
+ double M11() const { return matrix_[0][0]; }
+ void SetM11(double f) { matrix_[0][0] = f; }
+ double M12() const { return matrix_[0][1]; }
+ void SetM12(double f) { matrix_[0][1] = f; }
+ double M13() const { return matrix_[0][2]; }
+ void SetM13(double f) { matrix_[0][2] = f; }
+ double M14() const { return matrix_[0][3]; }
+ void SetM14(double f) { matrix_[0][3] = f; }
+ double M21() const { return matrix_[1][0]; }
+ void SetM21(double f) { matrix_[1][0] = f; }
+ double M22() const { return matrix_[1][1]; }
+ void SetM22(double f) { matrix_[1][1] = f; }
+ double M23() const { return matrix_[1][2]; }
+ void SetM23(double f) { matrix_[1][2] = f; }
+ double M24() const { return matrix_[1][3]; }
+ void SetM24(double f) { matrix_[1][3] = f; }
+ double M31() const { return matrix_[2][0]; }
+ void SetM31(double f) { matrix_[2][0] = f; }
+ double M32() const { return matrix_[2][1]; }
+ void SetM32(double f) { matrix_[2][1] = f; }
+ double M33() const { return matrix_[2][2]; }
+ void SetM33(double f) { matrix_[2][2] = f; }
+ double M34() const { return matrix_[2][3]; }
+ void SetM34(double f) { matrix_[2][3] = f; }
+ double M41() const { return matrix_[3][0]; }
+ void SetM41(double f) { matrix_[3][0] = f; }
+ double M42() const { return matrix_[3][1]; }
+ void SetM42(double f) { matrix_[3][1] = f; }
+ double M43() const { return matrix_[3][2]; }
+ void SetM43(double f) { matrix_[3][2] = f; }
+ double M44() const { return matrix_[3][3]; }
+ void SetM44(double f) { matrix_[3][3] = f; }
+
+ double A() const { return matrix_[0][0]; }
+ void SetA(double a) { matrix_[0][0] = a; }
+
+ double B() const { return matrix_[0][1]; }
+ void SetB(double b) { matrix_[0][1] = b; }
+
+ double C() const { return matrix_[1][0]; }
+ void SetC(double c) { matrix_[1][0] = c; }
+
+ double D() const { return matrix_[1][1]; }
+ void SetD(double d) { matrix_[1][1] = d; }
+
+ double E() const { return matrix_[3][0]; }
+ void SetE(double e) { matrix_[3][0] = e; }
+
+ double F() const { return matrix_[3][1]; }
+ void SetF(double f) { matrix_[3][1] = f; }
+
+ // *this = *this * mat.
+ TransformationMatrix& Multiply(const TransformationMatrix&);
+
+ TransformationMatrix& Scale(double);
+ TransformationMatrix& ScaleNonUniform(double sx, double sy);
+ TransformationMatrix& Scale3d(double sx, double sy, double sz);
+
+ TransformationMatrix& Rotate(double d) { return Rotate3d(0, 0, d); }
+ // Angles are in degrees.
+ TransformationMatrix& Rotate3d(double rx, double ry, double rz);
+ TransformationMatrix& Rotate3d(const Rotation&);
+
+ // The vector (x,y,z) is normalized if it's not already. A vector of
+ // (0,0,0) uses a vector of (0,0,1).
+ TransformationMatrix& Rotate3d(double x, double y, double z, double angle);
+
+ TransformationMatrix& Translate(double tx, double ty);
+ TransformationMatrix& Translate3d(double tx, double ty, double tz);
+
+ // Append translation after existing operations. i.e.
+ // TransformationMatrix t2 = t1;
+ // t2.PostTranslate(x, y);
+ // t2.MapPoint(p) == t1.MapPoint(p) + FloatPoint(x, y)
+ TransformationMatrix& PostTranslate(double tx, double ty);
+ TransformationMatrix& PostTranslate3d(double tx, double ty, double tz);
+
+ TransformationMatrix& Skew(double angle_x, double angle_y);
+ TransformationMatrix& SkewX(double angle) { return Skew(angle, 0); }
+ TransformationMatrix& SkewY(double angle) { return Skew(0, angle); }
+
+ TransformationMatrix& ApplyPerspective(double p);
+
+ // Changes the transform to apply as if the origin were at (x, y, z).
+ TransformationMatrix& ApplyTransformOrigin(double x, double y, double z);
+ TransformationMatrix& ApplyTransformOrigin(const FloatPoint3D& origin) {
+ return ApplyTransformOrigin(origin.X(), origin.Y(), origin.Z());
+ }
+
+ // Changes the transform to:
+ //
+ // scale3d(z, z, z) * mat * scale3d(1/z, 1/z, 1/z)
+ //
+ // Useful for mapping zoomed points to their zoomed transformed result:
+ //
+ // new_mat * (scale3d(z, z, z) * x) == scale3d(z, z, z) * (mat * x)
+ //
+ TransformationMatrix& Zoom(double zoom_factor);
+
+ bool IsInvertible() const;
+
+ // This method returns the identity matrix if it is not invertible.
+ // Use isInvertible() before calling this if you need to know.
+ TransformationMatrix Inverse() const;
+
+ // decompose the matrix into its component parts
+ typedef struct {
+ double scale_x, scale_y, scale_z;
+ double skew_xy, skew_xz, skew_yz;
+ double quaternion_x, quaternion_y, quaternion_z, quaternion_w;
+ double translate_x, translate_y, translate_z;
+ double perspective_x, perspective_y, perspective_z, perspective_w;
+ } DecomposedType;
+
+ WARN_UNUSED_RESULT bool Decompose(DecomposedType&) const;
+ void Recompose(const DecomposedType&);
+
+ void Blend(const TransformationMatrix& from, double progress);
+
+ bool IsAffine() const {
+ return M13() == 0 && M14() == 0 && M23() == 0 && M24() == 0 && M31() == 0 &&
+ M32() == 0 && M33() == 1 && M34() == 0 && M43() == 0 && M44() == 1;
+ }
+
+ // Throw away the non-affine parts of the matrix (lossy!)
+ void MakeAffine();
+
+ AffineTransform ToAffineTransform() const;
+
+ // Flatten into a 2-D transformation (non-invertable).
+ // Same as gfx::Transform::FlattenTo2d(); see the docs for that function for
+ // details and discussion.
+ void FlattenTo2d();
+
+ bool operator==(const TransformationMatrix& m2) const {
+ return matrix_[0][0] == m2.matrix_[0][0] &&
+ matrix_[0][1] == m2.matrix_[0][1] &&
+ matrix_[0][2] == m2.matrix_[0][2] &&
+ matrix_[0][3] == m2.matrix_[0][3] &&
+ matrix_[1][0] == m2.matrix_[1][0] &&
+ matrix_[1][1] == m2.matrix_[1][1] &&
+ matrix_[1][2] == m2.matrix_[1][2] &&
+ matrix_[1][3] == m2.matrix_[1][3] &&
+ matrix_[2][0] == m2.matrix_[2][0] &&
+ matrix_[2][1] == m2.matrix_[2][1] &&
+ matrix_[2][2] == m2.matrix_[2][2] &&
+ matrix_[2][3] == m2.matrix_[2][3] &&
+ matrix_[3][0] == m2.matrix_[3][0] &&
+ matrix_[3][1] == m2.matrix_[3][1] &&
+ matrix_[3][2] == m2.matrix_[3][2] &&
+ matrix_[3][3] == m2.matrix_[3][3];
+ }
+
+ bool operator!=(const TransformationMatrix& other) const {
+ return !(*this == other);
+ }
+
+ // *this = *this * t
+ TransformationMatrix& operator*=(const TransformationMatrix& t) {
+ return Multiply(t);
+ }
+
+ // result = *this * t
+ TransformationMatrix operator*(const TransformationMatrix& t) const {
+ TransformationMatrix result = *this;
+ result.Multiply(t);
+ return result;
+ }
+
+ bool IsFlat() const {
+ return matrix_[0][2] == 0.f && matrix_[1][2] == 0.f &&
+ matrix_[2][0] == 0.f && matrix_[2][1] == 0.f &&
+ matrix_[2][2] == 1.f && matrix_[2][3] == 0.f && matrix_[3][2] == 0.f;
+ }
+
+ bool IsIdentityOrTranslation() const {
+ return matrix_[0][0] == 1 && matrix_[0][1] == 0 && matrix_[0][2] == 0 &&
+ matrix_[0][3] == 0 && matrix_[1][0] == 0 && matrix_[1][1] == 1 &&
+ matrix_[1][2] == 0 && matrix_[1][3] == 0 && matrix_[2][0] == 0 &&
+ matrix_[2][1] == 0 && matrix_[2][2] == 1 && matrix_[2][3] == 0 &&
+ matrix_[3][3] == 1;
+ }
+
+ bool IsIdentityOr2DTranslation() const {
+ return IsIdentityOrTranslation() && matrix_[3][2] == 0;
+ }
+
+ bool IsIntegerTranslation() const;
+
+ // If this transformation is identity or 2D translation, returns the
+ // translation.
+ FloatSize To2DTranslation() const;
+
+ typedef float FloatMatrix4[16];
+ void ToColumnMajorFloatArray(FloatMatrix4& result) const;
+
+ static SkMatrix44 ToSkMatrix44(const TransformationMatrix&);
+
+ // If |asMatrix|, return the matrix in row-major order. Otherwise, return
+ // the transform's decomposition which shows the translation, scale, etc.
+ String ToString(bool as_matrix = false) const;
+
+ private:
+ // multiply passed 2D point by matrix (assume z=0)
+ void MultVecMatrix(double x, double y, double& dst_x, double& dst_y) const;
+ FloatPoint InternalMapPoint(const FloatPoint& source_point) const {
+ double result_x;
+ double result_y;
+ MultVecMatrix(source_point.X(), source_point.Y(), result_x, result_y);
+ return FloatPoint(static_cast<float>(result_x),
+ static_cast<float>(result_y));
+ }
+
+ // multiply passed 3D point by matrix
+ void MultVecMatrix(double x,
+ double y,
+ double z,
+ double& dst_x,
+ double& dst_y,
+ double& dst_z) const;
+ FloatPoint3D InternalMapPoint(const FloatPoint3D& source_point) const {
+ double result_x;
+ double result_y;
+ double result_z;
+ MultVecMatrix(source_point.X(), source_point.Y(), source_point.Z(),
+ result_x, result_y, result_z);
+ return FloatPoint3D(static_cast<float>(result_x),
+ static_cast<float>(result_y),
+ static_cast<float>(result_z));
+ }
+
+ void SetMatrix(const Matrix4 m) {
+ if (m && m != matrix_)
+ memcpy(matrix_, m, sizeof(Matrix4));
+ }
+
+ void CheckAlignment() {
+#if defined(TRANSFORMATION_MATRIX_USE_X86_64_SSE2)
+ // m_matrix can cause this class to require higher than usual alignment.
+ // Make sure the allocator handles this.
+ DCHECK_EQ((reinterpret_cast<uintptr_t>(this) &
+ (WTF_ALIGN_OF(TransformationMatrix) - 1)),
+ 0UL);
+#endif
+ }
+
+ Matrix4 matrix_;
+};
+
+PLATFORM_EXPORT std::ostream& operator<<(std::ostream&,
+ const TransformationMatrix&);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_TRANSFORMATION_MATRIX_H_
diff --git a/chromium/third_party/blink/renderer/platform/transforms/transformation_matrix_test.cc b/chromium/third_party/blink/renderer/platform/transforms/transformation_matrix_test.cc
new file mode 100644
index 00000000000..698aeb9ce50
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/transforms/transformation_matrix_test.cc
@@ -0,0 +1,288 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/transforms/transformation_matrix.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+TEST(TransformationMatrixTest, NonInvertableBlendTest) {
+ TransformationMatrix from;
+ TransformationMatrix to(2.7133590938, 0.0, 0.0, 0.0, 0.0, 2.4645137761, 0.0,
+ 0.0, 0.0, 0.0, 0.00, 0.01, 0.02, 0.03, 0.04, 0.05);
+ TransformationMatrix result;
+
+ result = to;
+ result.Blend(from, 0.25);
+ EXPECT_EQ(result, from);
+
+ result = to;
+ result.Blend(from, 0.75);
+ EXPECT_EQ(result, to);
+}
+
+TEST(TransformationMatrixTest, IsIdentityOr2DTranslation) {
+ TransformationMatrix matrix;
+ EXPECT_TRUE(matrix.IsIdentityOr2DTranslation());
+
+ matrix.MakeIdentity();
+ matrix.Translate(10, 0);
+ EXPECT_TRUE(matrix.IsIdentityOr2DTranslation());
+
+ matrix.MakeIdentity();
+ matrix.Translate(0, -20);
+ EXPECT_TRUE(matrix.IsIdentityOr2DTranslation());
+
+ matrix.MakeIdentity();
+ matrix.Translate3d(0, 0, 1);
+ EXPECT_FALSE(matrix.IsIdentityOr2DTranslation());
+
+ matrix.MakeIdentity();
+ matrix.Rotate(40 /* degrees */);
+ EXPECT_FALSE(matrix.IsIdentityOr2DTranslation());
+
+ matrix.MakeIdentity();
+ matrix.SkewX(30 /* degrees */);
+ EXPECT_FALSE(matrix.IsIdentityOr2DTranslation());
+}
+
+TEST(TransformationMatrixTest, To2DTranslation) {
+ TransformationMatrix matrix;
+ EXPECT_EQ(FloatSize(), matrix.To2DTranslation());
+ matrix.Translate(30, -40);
+ EXPECT_EQ(FloatSize(30, -40), matrix.To2DTranslation());
+}
+
+TEST(TransformationMatrixTest, ApplyTransformOrigin) {
+ TransformationMatrix matrix;
+
+ // (0,0,0) is a fixed point of this scale.
+ // (1,1,1) should be scaled appropriately.
+ matrix.Scale3d(2, 3, 4);
+ EXPECT_EQ(FloatPoint3D(0, 0, 0), matrix.MapPoint(FloatPoint3D(0, 0, 0)));
+ EXPECT_EQ(FloatPoint3D(2, 3, -4), matrix.MapPoint(FloatPoint3D(1, 1, -1)));
+
+ // With the transform origin applied, (1,2,3) is the fixed point.
+ // (0,0,0) should be scaled according to its distance from (1,2,3).
+ matrix.ApplyTransformOrigin(1, 2, 3);
+ EXPECT_EQ(FloatPoint3D(1, 2, 3), matrix.MapPoint(FloatPoint3D(1, 2, 3)));
+ EXPECT_EQ(FloatPoint3D(-1, -4, -9), matrix.MapPoint(FloatPoint3D(0, 0, 0)));
+}
+
+TEST(TransformationMatrixTest, Multiplication) {
+ TransformationMatrix a(1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4);
+ // [ 1 2 3 4 ]
+ // [ 1 2 3 4 ]
+ // [ 1 2 3 4 ]
+ // [ 1 2 3 4 ]
+
+ TransformationMatrix b(1, 2, 3, 5, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4);
+ // [ 1 1 1 1 ]
+ // [ 2 2 2 2 ]
+ // [ 3 3 3 3 ]
+ // [ 5 4 4 4 ]
+
+ TransformationMatrix expected_atimes_b(34, 34, 34, 34, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30);
+
+ EXPECT_EQ(expected_atimes_b, a * b);
+
+ a.Multiply(b);
+ EXPECT_EQ(expected_atimes_b, a);
+}
+
+TEST(TransformationMatrixTest, BasicOperations) {
+ // Just some arbitrary matrix that introduces no rounding, and is unlikely
+ // to commute with other operations.
+ TransformationMatrix m(2.f, 3.f, 5.f, 0.f, 7.f, 11.f, 13.f, 0.f, 17.f, 19.f,
+ 23.f, 0.f, 29.f, 31.f, 37.f, 1.f);
+
+ FloatPoint3D p(41.f, 43.f, 47.f);
+
+ EXPECT_EQ(FloatPoint3D(1211.f, 1520.f, 1882.f), m.MapPoint(p));
+
+ {
+ TransformationMatrix n;
+ n.Scale(2.f);
+ EXPECT_EQ(FloatPoint3D(82.f, 86.f, 47.f), n.MapPoint(p));
+
+ TransformationMatrix mn = m;
+ mn.Scale(2.f);
+ EXPECT_EQ(mn.MapPoint(p), m.MapPoint(n.MapPoint(p)));
+ }
+
+ {
+ TransformationMatrix n;
+ n.ScaleNonUniform(2.f, 3.f);
+ EXPECT_EQ(FloatPoint3D(82.f, 129.f, 47.f), n.MapPoint(p));
+
+ TransformationMatrix mn = m;
+ mn.ScaleNonUniform(2.f, 3.f);
+ EXPECT_EQ(mn.MapPoint(p), m.MapPoint(n.MapPoint(p)));
+ }
+
+ {
+ TransformationMatrix n;
+ n.Scale3d(2.f, 3.f, 4.f);
+ EXPECT_EQ(FloatPoint3D(82.f, 129.f, 188.f), n.MapPoint(p));
+
+ TransformationMatrix mn = m;
+ mn.Scale3d(2.f, 3.f, 4.f);
+ EXPECT_EQ(mn.MapPoint(p), m.MapPoint(n.MapPoint(p)));
+ }
+
+ {
+ TransformationMatrix n;
+ n.Rotate(90.f);
+ EXPECT_FLOAT_EQ(0.f,
+ (FloatPoint3D(-43.f, 41.f, 47.f) - n.MapPoint(p)).length());
+
+ TransformationMatrix mn = m;
+ mn.Rotate(90.f);
+ EXPECT_FLOAT_EQ(0.f, (mn.MapPoint(p) - m.MapPoint(n.MapPoint(p))).length());
+ }
+
+ {
+ TransformationMatrix n;
+ n.Rotate3d(10.f, 10.f, 10.f, 120.f);
+ EXPECT_FLOAT_EQ(0.f,
+ (FloatPoint3D(47.f, 41.f, 43.f) - n.MapPoint(p)).length());
+
+ TransformationMatrix mn = m;
+ mn.Rotate3d(10.f, 10.f, 10.f, 120.f);
+ EXPECT_FLOAT_EQ(0.f, (mn.MapPoint(p) - m.MapPoint(n.MapPoint(p))).length());
+ }
+
+ {
+ TransformationMatrix n;
+ n.Translate(5.f, 6.f);
+ EXPECT_EQ(FloatPoint3D(46.f, 49.f, 47.f), n.MapPoint(p));
+
+ TransformationMatrix mn = m;
+ mn.Translate(5.f, 6.f);
+ EXPECT_EQ(mn.MapPoint(p), m.MapPoint(n.MapPoint(p)));
+ }
+
+ {
+ TransformationMatrix n;
+ n.Translate3d(5.f, 6.f, 7.f);
+ EXPECT_EQ(FloatPoint3D(46.f, 49.f, 54.f), n.MapPoint(p));
+
+ TransformationMatrix mn = m;
+ mn.Translate3d(5.f, 6.f, 7.f);
+ EXPECT_EQ(mn.MapPoint(p), m.MapPoint(n.MapPoint(p)));
+ }
+
+ {
+ TransformationMatrix nm = m;
+ nm.PostTranslate(5.f, 6.f);
+ EXPECT_EQ(nm.MapPoint(p), m.MapPoint(p) + FloatPoint3D(5.f, 6.f, 0.f));
+ }
+
+ {
+ TransformationMatrix nm = m;
+ nm.PostTranslate3d(5.f, 6.f, 7.f);
+ EXPECT_EQ(nm.MapPoint(p), m.MapPoint(p) + FloatPoint3D(5.f, 6.f, 7.f));
+ }
+
+ {
+ TransformationMatrix n;
+ n.Skew(45.f, -45.f);
+ EXPECT_FLOAT_EQ(0.f,
+ (FloatPoint3D(84.f, 2.f, 47.f) - n.MapPoint(p)).length());
+
+ TransformationMatrix mn = m;
+ mn.Skew(45.f, -45.f);
+ EXPECT_FLOAT_EQ(0.f, (mn.MapPoint(p) - m.MapPoint(n.MapPoint(p))).length());
+ }
+
+ {
+ TransformationMatrix n;
+ n.SkewX(45.f);
+ EXPECT_FLOAT_EQ(0.f,
+ (FloatPoint3D(84.f, 43.f, 47.f) - n.MapPoint(p)).length());
+
+ TransformationMatrix mn = m;
+ mn.SkewX(45.f);
+ EXPECT_FLOAT_EQ(0.f, (mn.MapPoint(p) - m.MapPoint(n.MapPoint(p))).length());
+ }
+
+ {
+ TransformationMatrix n;
+ n.SkewY(45.f);
+ EXPECT_FLOAT_EQ(0.f,
+ (FloatPoint3D(41.f, 84.f, 47.f) - n.MapPoint(p)).length());
+
+ TransformationMatrix mn = m;
+ mn.SkewY(45.f);
+ EXPECT_FLOAT_EQ(0.f, (mn.MapPoint(p) - m.MapPoint(n.MapPoint(p))).length());
+ }
+
+ {
+ TransformationMatrix n;
+ n.ApplyPerspective(94.f);
+ EXPECT_FLOAT_EQ(0.f,
+ (FloatPoint3D(82.f, 86.f, 94.f) - n.MapPoint(p)).length());
+
+ TransformationMatrix mn = m;
+ mn.ApplyPerspective(94.f);
+ EXPECT_FLOAT_EQ(0.f, (mn.MapPoint(p) - m.MapPoint(n.MapPoint(p))).length());
+ }
+
+ {
+ FloatPoint3D origin(5.f, 6.f, 7.f);
+ TransformationMatrix n = m;
+ n.ApplyTransformOrigin(origin);
+ EXPECT_EQ(m.MapPoint(p - origin) + origin, n.MapPoint(p));
+ }
+
+ {
+ TransformationMatrix n = m;
+ n.Zoom(2.f);
+ FloatPoint3D expectation = p;
+ expectation.Scale(0.5f, 0.5f, 0.5f);
+ expectation = m.MapPoint(expectation);
+ expectation.Scale(2.f, 2.f, 2.f);
+ EXPECT_EQ(expectation, n.MapPoint(p));
+ }
+}
+
+TEST(TransformationMatrixTest, ToString) {
+ TransformationMatrix zeros(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+ EXPECT_EQ("[0,0,0,0,\n0,0,0,0,\n0,0,0,0,\n0,0,0,0] (degenerate)",
+ zeros.ToString());
+ EXPECT_EQ("[0,0,0,0,\n0,0,0,0,\n0,0,0,0,\n0,0,0,0]", zeros.ToString(true));
+
+ TransformationMatrix identity;
+ EXPECT_EQ("identity", identity.ToString());
+ EXPECT_EQ("[1,0,0,0,\n0,1,0,0,\n0,0,1,0,\n0,0,0,1]", identity.ToString(true));
+
+ TransformationMatrix translation;
+ translation.Translate3d(3, 5, 7);
+ EXPECT_EQ("translation(3,5,7)", translation.ToString());
+ EXPECT_EQ("[1,0,0,3,\n0,1,0,5,\n0,0,1,7,\n0,0,0,1]",
+ translation.ToString(true));
+
+ TransformationMatrix rotation;
+ rotation.Rotate(180);
+ EXPECT_EQ(
+ "translation(0,0,0), scale(1,1,1), skew(0,0,0), "
+ "quaternion(0,0,1,-6.12323e-17), perspective(0,0,0,1)",
+ rotation.ToString());
+ EXPECT_EQ("[-1,-1.22465e-16,0,0,\n1.22465e-16,-1,0,0,\n0,0,1,0,\n0,0,0,1]",
+ rotation.ToString(true));
+
+ TransformationMatrix column_major_constructor(1, 1, 1, 6, 2, 2, 0, 7, 3, 3, 3,
+ 8, 4, 4, 4, 9);
+ // [ 1 2 3 4 ]
+ // [ 1 2 3 4 ]
+ // [ 1 0 3 4 ]
+ // [ 6 7 8 9 ]
+ EXPECT_EQ("[1,2,3,4,\n1,2,3,4,\n1,0,3,4,\n6,7,8,9]",
+ column_major_constructor.ToString(true));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/transforms/translate_transform_operation.cc b/chromium/third_party/blink/renderer/platform/transforms/translate_transform_operation.cc
new file mode 100644
index 00000000000..88488a05c70
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/transforms/translate_transform_operation.cc
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "third_party/blink/renderer/platform/transforms/translate_transform_operation.h"
+
+#include "third_party/blink/renderer/platform/animation/animation_utilities.h"
+
+namespace blink {
+
+scoped_refptr<TransformOperation> TranslateTransformOperation::Blend(
+ const TransformOperation* from,
+ double progress,
+ bool blend_to_identity) {
+ if (from && !from->CanBlendWith(*this))
+ return this;
+
+ const Length zero_length(0, kFixed);
+ if (blend_to_identity) {
+ return TranslateTransformOperation::Create(
+ zero_length.Blend(x_, progress, kValueRangeAll),
+ zero_length.Blend(y_, progress, kValueRangeAll),
+ blink::Blend(z_, 0., progress), type_);
+ }
+
+ const auto* from_op = ToTranslateTransformOperation(from);
+ const Length& from_x = from_op ? from_op->x_ : zero_length;
+ const Length& from_y = from_op ? from_op->y_ : zero_length;
+ double from_z = from_op ? from_op->z_ : 0;
+ return TranslateTransformOperation::Create(
+ x_.Blend(from_x, progress, kValueRangeAll),
+ y_.Blend(from_y, progress, kValueRangeAll),
+ blink::Blend(from_z, z_, progress), type_);
+}
+
+bool TranslateTransformOperation::CanBlendWith(
+ const TransformOperation& other) const {
+ return other.GetType() == kTranslate || other.GetType() == kTranslateX ||
+ other.GetType() == kTranslateY || other.GetType() == kTranslateZ ||
+ other.GetType() == kTranslate3D;
+}
+
+scoped_refptr<TranslateTransformOperation>
+TranslateTransformOperation::ZoomTranslate(double factor) {
+ return Create(x_.Zoom(factor), y_.Zoom(factor), z_ * factor, type_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/transforms/translate_transform_operation.h b/chromium/third_party/blink/renderer/platform/transforms/translate_transform_operation.h
new file mode 100644
index 00000000000..3657ccb2102
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/transforms/translate_transform_operation.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_TRANSLATE_TRANSFORM_OPERATION_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_TRANSLATE_TRANSFORM_OPERATION_H_
+
+#include "third_party/blink/renderer/platform/length.h"
+#include "third_party/blink/renderer/platform/length_functions.h"
+#include "third_party/blink/renderer/platform/transforms/transform_operation.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT TranslateTransformOperation final
+ : public TransformOperation {
+ public:
+ static scoped_refptr<TranslateTransformOperation> Create(const Length& tx,
+ const Length& ty,
+ OperationType type) {
+ return base::AdoptRef(new TranslateTransformOperation(tx, ty, 0, type));
+ }
+
+ static scoped_refptr<TranslateTransformOperation> Create(const Length& tx,
+ const Length& ty,
+ double tz,
+ OperationType type) {
+ return base::AdoptRef(new TranslateTransformOperation(tx, ty, tz, type));
+ }
+
+ bool operator==(const TranslateTransformOperation& other) const {
+ return *this == static_cast<const TransformOperation&>(other);
+ }
+
+ bool CanBlendWith(const TransformOperation& other) const override;
+ bool DependsOnBoxSize() const override {
+ return x_.IsPercentOrCalc() || y_.IsPercentOrCalc();
+ }
+
+ double X(const FloatSize& border_box_size) const {
+ return FloatValueForLength(x_, border_box_size.Width());
+ }
+ double Y(const FloatSize& border_box_size) const {
+ return FloatValueForLength(y_, border_box_size.Height());
+ }
+
+ const Length& X() const { return x_; }
+ const Length& Y() const { return y_; }
+ double Z() const { return z_; }
+
+ void Apply(TransformationMatrix& transform,
+ const FloatSize& border_box_size) const override {
+ transform.Translate3d(X(border_box_size), Y(border_box_size), Z());
+ }
+
+ static bool IsMatchingOperationType(OperationType type) {
+ return type == kTranslate || type == kTranslateX || type == kTranslateY ||
+ type == kTranslateZ || type == kTranslate3D;
+ }
+
+ scoped_refptr<TranslateTransformOperation> ZoomTranslate(double factor);
+
+ OperationType GetType() const override { return type_; }
+ OperationType PrimitiveType() const final { return kTranslate3D; }
+
+ private:
+ bool operator==(const TransformOperation& o) const override {
+ if (!IsSameType(o))
+ return false;
+ const TranslateTransformOperation* t =
+ static_cast<const TranslateTransformOperation*>(&o);
+ return x_ == t->x_ && y_ == t->y_ && z_ == t->z_;
+ }
+
+ scoped_refptr<TransformOperation> Blend(
+ const TransformOperation* from,
+ double progress,
+ bool blend_to_identity = false) override;
+ scoped_refptr<TransformOperation> Zoom(double factor) final {
+ return ZoomTranslate(factor);
+ }
+
+ TranslateTransformOperation(const Length& tx,
+ const Length& ty,
+ double tz,
+ OperationType type)
+ : x_(tx), y_(ty), z_(tz), type_(type) {
+ DCHECK(IsMatchingOperationType(type));
+ }
+
+ bool HasNonTrivial3DComponent() const override { return z_ != 0.0; }
+
+ Length x_;
+ Length y_;
+ double z_;
+ OperationType type_;
+};
+
+DEFINE_TRANSFORM_TYPE_CASTS(TranslateTransformOperation);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_TRANSLATE_TRANSFORM_OPERATION_H_
diff --git a/chromium/third_party/blink/renderer/platform/ukm_time_aggregator.cc b/chromium/third_party/blink/renderer/platform/ukm_time_aggregator.cc
new file mode 100644
index 00000000000..0f0e8e8bec6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/ukm_time_aggregator.cc
@@ -0,0 +1,116 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/ukm_time_aggregator.h"
+
+#include "services/metrics/public/cpp/ukm_entry_builder.h"
+#include "services/metrics/public/cpp/ukm_recorder.h"
+#include "third_party/blink/renderer/platform/histogram.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+UkmTimeAggregator::ScopedUkmTimer::ScopedUkmTimer(
+ UkmTimeAggregator* aggregator,
+ size_t metric_index,
+ CustomCountHistogram* histogram_counter)
+ : aggregator_(aggregator),
+ metric_index_(metric_index),
+ histogram_counter_(histogram_counter),
+ start_time_(CurrentTimeTicks()) {}
+
+UkmTimeAggregator::ScopedUkmTimer::ScopedUkmTimer(ScopedUkmTimer&& other)
+ : aggregator_(other.aggregator_),
+ metric_index_(other.metric_index_),
+ histogram_counter_(other.histogram_counter_),
+ start_time_(other.start_time_) {
+ other.aggregator_ = nullptr;
+}
+
+UkmTimeAggregator::ScopedUkmTimer::~ScopedUkmTimer() {
+ if (aggregator_) {
+ aggregator_->RecordSample(metric_index_, start_time_, CurrentTimeTicks(),
+ histogram_counter_);
+ }
+}
+
+UkmTimeAggregator::UkmTimeAggregator(String event_name,
+ int64_t source_id,
+ ukm::UkmRecorder* recorder,
+ const Vector<String>& metric_names,
+ TimeDelta event_frequency)
+ : event_name_(std::move(event_name)),
+ source_id_(source_id),
+ recorder_(recorder),
+ event_frequency_(event_frequency),
+ last_flushed_time_(CurrentTimeTicks()) {
+ metric_records_.ReserveCapacity(metric_names.size());
+ for (const auto& metric_name : metric_names) {
+ auto& record = metric_records_.emplace_back();
+ record.worst_case_metric_name = metric_name;
+ record.worst_case_metric_name.append(".WorstCase");
+ record.average_metric_name = metric_name;
+ record.average_metric_name.append(".Average");
+ }
+}
+
+UkmTimeAggregator::~UkmTimeAggregator() {
+ Flush(TimeTicks());
+}
+
+UkmTimeAggregator::ScopedUkmTimer UkmTimeAggregator::GetScopedTimer(
+ size_t metric_index,
+ CustomCountHistogram* histogram_counter) {
+ return ScopedUkmTimer(this, metric_index, histogram_counter);
+}
+
+void UkmTimeAggregator::RecordSample(size_t metric_index,
+ TimeTicks start,
+ TimeTicks end,
+ CustomCountHistogram* histogram_counter) {
+ FlushIfNeeded(end);
+
+ // Record the UMA if we have a counter.
+ TimeDelta duration = end - start;
+ if (histogram_counter)
+ histogram_counter->Count(duration.InMicroseconds());
+
+ // Append the duration to the appropriate metrics record.
+ DCHECK_LT(metric_index, metric_records_.size());
+ auto& record = metric_records_[metric_index];
+ if (duration > record.worst_case_duration)
+ record.worst_case_duration = duration;
+ record.total_duration += duration;
+ ++record.sample_count;
+ has_data_ = true;
+}
+
+void UkmTimeAggregator::FlushIfNeeded(TimeTicks current_time) {
+ if (current_time >= last_flushed_time_ + event_frequency_)
+ Flush(current_time);
+}
+
+void UkmTimeAggregator::Flush(TimeTicks current_time) {
+ last_flushed_time_ = current_time;
+ if (!has_data_)
+ return;
+
+ auto builder =
+ recorder_->GetEntryBuilder(source_id_, event_name_.Utf8().data());
+ for (auto& record : metric_records_) {
+ if (record.sample_count == 0)
+ continue;
+ builder->AddMetric(record.worst_case_metric_name.Utf8().data(),
+ record.worst_case_duration.InMicroseconds());
+ builder->AddMetric(record.average_metric_name.Utf8().data(),
+ record.total_duration.InMicroseconds() /
+ static_cast<int64_t>(record.sample_count));
+ record.reset();
+ }
+ has_data_ = false;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/ukm_time_aggregator.h b/chromium/third_party/blink/renderer/platform/ukm_time_aggregator.h
new file mode 100644
index 00000000000..14299573bbc
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/ukm_time_aggregator.h
@@ -0,0 +1,166 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_UKM_TIME_AGGREGATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_UKM_TIME_AGGREGATOR_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace ukm {
+class UkmRecorder;
+}
+
+namespace blink {
+class CustomCountHistogram;
+
+// This class is a helper class for aggregating and recording time based UKM
+// metrics. The simplest way to use it is via the SCOPED_UMA_AND_UKM_TIMER macro
+// in LocalFrameView.
+//
+// It takes the following constructor parameters:
+// - event_name: The name of the event(s) that will be generated by this
+// aggregator.
+// - source_id: UKM Source ID associated with the events.
+// - recorder: UkmRecorder which will handle the events
+// - metric_names: A Vector of strings that represent the base names for the
+// metrics associated with the event. Note that the order of
+// this array is important, since the scoped timer is created
+// with an index into this array.
+// - event_frequency: The interval over which metrics are aggregated. Within
+// one interval there will be one event generated.
+//
+// After the aggregator is created, one can create ScopedUkmTimer object which
+// will measure the time, in microseconds, from creation until the object
+// destruction. When destroyed, it will record a sample into the aggregator.
+// This does not necessarily generate a UKM event. Instead, the sample is
+// aggregated with other samples in the interval specified by event frequency.
+// An event is actually generated in one of two situations:
+//
+// - If a sample is added that lies in the next event frequency interval (this
+// will generate an event for the previous interval)
+// - If the aggregator is destroyed (this will generate an event for any
+// remaining samples in the aggregator)
+//
+// Note that no event is generated if there were no samples in an interval.
+//
+// Note that one can also give an optional CustomCountHistogram pointer when
+// creating the scoped timer. This is provided as a convenience. Upon
+// destruction the timer will record a sample into the histogram counter.
+//
+// Sample usage (see also SCOPED_UMA_AND_UKM_TIMER):
+// // Note that values of this enum correspond to the indices of the metric
+// // names array given to UkmTimeAggregator at construction.
+// enum class MetricNames { kMetric1, kMetric2, kMetric3 };
+// std::unique_ptr<UkmTimeAggregator> aggregator(
+// new UkmTimeAggregator("my_event",
+// GetSourceId(),
+// GetUkmRecorder(),
+// {"metric1", "metric2", "metric3"},
+// TimeDelta::FromSeconds(1)));
+//
+// ...
+// {
+// auto timer =
+// aggregator->GetScopedTimer(static_cast<size_t>(MetricNames::kMetric2));
+// ...
+// }
+// // At this point an sample for "metric2" is recorded, which may generate
+// // an event for "my_event" if there were previous samples.
+// ...
+// // Destroying an aggregator will generate an event as well if there were
+// // samples.
+// aggregator.reset();
+//
+// In the example above, the event name is "my_event". It will measure six
+// metrics:
+// "metric1.Average", "metric1.WorstCase",
+// "metric2.Average", "metric2.WorstCase",
+// "metric3.Average", "metric3.WorstCase"
+//
+// Note that these have to be specified in the appropriate ukm.xml file to be
+// captured.
+//
+// If the source_id/event_name/recorder change then a new UkmTimeAggregator has
+// to be created.
+class PLATFORM_EXPORT UkmTimeAggregator {
+ public:
+ // This class will start a timer upon creation, which will end when the
+ // object is destroyed. Upon destruction it will record a sample into the
+ // aggregator that created the scoped timer. It will also record an event
+ // into an optional histogram counter.
+ class PLATFORM_EXPORT ScopedUkmTimer {
+ public:
+ ScopedUkmTimer(ScopedUkmTimer&&);
+ ~ScopedUkmTimer();
+
+ private:
+ friend class UkmTimeAggregator;
+
+ ScopedUkmTimer(UkmTimeAggregator*,
+ size_t metric_index,
+ CustomCountHistogram* histogram_counter);
+
+ UkmTimeAggregator* aggregator_;
+ const size_t metric_index_;
+ CustomCountHistogram* const histogram_counter_;
+ const TimeTicks start_time_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedUkmTimer);
+ };
+
+ UkmTimeAggregator(String event_name,
+ int64_t source_id,
+ ukm::UkmRecorder*,
+ const Vector<String>& metric_names,
+ TimeDelta event_frequency);
+ ~UkmTimeAggregator();
+
+ // Create a scoped timer with the index of the metric and an optional
+ // histogram counter. Note the index has to correspond to the right index in
+ // metric_names. For example, if metric_names is {"a", "b", "c"} then index
+ // 1 refers to metric "a", which will end up generating "a.Average" and
+ // "a.WorstCase" metrics.
+ ScopedUkmTimer GetScopedTimer(
+ size_t metric_index,
+ CustomCountHistogram* histogram_counter = nullptr);
+
+ private:
+ struct MetricRecord {
+ String worst_case_metric_name;
+ String average_metric_name;
+ TimeDelta total_duration;
+ TimeDelta worst_case_duration;
+ size_t sample_count = 0u;
+
+ void reset() {
+ total_duration = TimeDelta();
+ worst_case_duration = TimeDelta();
+ sample_count = 0u;
+ }
+ };
+
+ void RecordSample(size_t metric_index,
+ TimeTicks start,
+ TimeTicks end,
+ CustomCountHistogram* histogram_counter);
+ void FlushIfNeeded(TimeTicks current_time);
+ void Flush(TimeTicks current_time);
+
+ const String event_name_;
+ const int64_t source_id_;
+ ukm::UkmRecorder* const recorder_;
+ const TimeDelta event_frequency_;
+ TimeTicks last_flushed_time_;
+ Vector<MetricRecord> metric_records_;
+ bool has_data_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(UkmTimeAggregator);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_UKM_TIME_AGGREGATOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/ukm_time_aggregator_test.cc b/chromium/third_party/blink/renderer/platform/ukm_time_aggregator_test.cc
new file mode 100644
index 00000000000..51814e94989
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/ukm_time_aggregator_test.cc
@@ -0,0 +1,149 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/ukm_time_aggregator.h"
+
+#include "components/ukm/test_ukm_recorder.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+namespace {
+
+struct Timer {
+ static double GetTime() { return fake_time; }
+ static double fake_time;
+};
+
+double Timer::fake_time = 0;
+
+TEST(UkmTimeAggregatorTest, EmptyEventsNotRecorded) {
+ Timer::fake_time = 0;
+ auto original_time_function = SetTimeFunctionsForTesting(&Timer::GetTime);
+
+ ukm::TestUkmRecorder recorder;
+ int64_t source_id = ukm::UkmRecorder::GetNewSourceID();
+ std::unique_ptr<UkmTimeAggregator> aggregator(
+ new UkmTimeAggregator("event", source_id, &recorder,
+ {"metric1", "metric2"}, TimeDelta::FromSeconds(1)));
+ Timer::fake_time += 10.;
+ aggregator.reset();
+
+ EXPECT_EQ(recorder.sources_count(), 0u);
+ EXPECT_EQ(recorder.entries_count(), 0u);
+ SetTimeFunctionsForTesting(original_time_function);
+}
+
+TEST(UkmTimeAggregatorTest, EventsRecordedPerSecond) {
+ Timer::fake_time = 0;
+ auto original_time_function = SetTimeFunctionsForTesting(&Timer::GetTime);
+
+ ukm::TestUkmRecorder recorder;
+ int64_t source_id = ukm::UkmRecorder::GetNewSourceID();
+ std::unique_ptr<UkmTimeAggregator> aggregator(
+ new UkmTimeAggregator("event", source_id, &recorder,
+ {"metric1", "metric2"}, TimeDelta::FromSeconds(1)));
+ // Have 100 events 99ms each; if the records are recorded once per second, we
+ // should expect 9 records to be recorded while the timer ticks. 0-1, 1-2,
+ // ..., 8-9 seconds.
+ for (int i = 0; i < 100; ++i) {
+ auto timer = aggregator->GetScopedTimer(i % 2);
+ Timer::fake_time += 0.099;
+ }
+
+ EXPECT_EQ(recorder.entries_count(), 9u);
+
+ // Once we reset, we record any remaining samples into one more entry, for a
+ // total of 10.
+ aggregator.reset();
+
+ EXPECT_EQ(recorder.entries_count(), 10u);
+ auto entries = recorder.GetEntriesByName("event");
+ EXPECT_EQ(entries.size(), 10u);
+
+ for (auto* entry : entries) {
+ EXPECT_TRUE(ukm::TestUkmRecorder::EntryHasMetric(entry, "metric1.Average"));
+ const int64_t* metric1_average =
+ ukm::TestUkmRecorder::GetEntryMetric(entry, "metric1.Average");
+ EXPECT_NEAR(*metric1_average / 1e6, 0.099, 0.0001);
+
+ EXPECT_TRUE(
+ ukm::TestUkmRecorder::EntryHasMetric(entry, "metric1.WorstCase"));
+ const int64_t* metric1_worst =
+ ukm::TestUkmRecorder::GetEntryMetric(entry, "metric1.WorstCase");
+ EXPECT_NEAR(*metric1_worst / 1e6, 0.099, 0.0001);
+
+ EXPECT_TRUE(ukm::TestUkmRecorder::EntryHasMetric(entry, "metric2.Average"));
+ const int64_t* metric2_average =
+ ukm::TestUkmRecorder::GetEntryMetric(entry, "metric2.Average");
+ EXPECT_NEAR(*metric2_average / 1e6, 0.099, 0.0001);
+
+ EXPECT_TRUE(
+ ukm::TestUkmRecorder::EntryHasMetric(entry, "metric2.WorstCase"));
+ const int64_t* metric2_worst =
+ ukm::TestUkmRecorder::GetEntryMetric(entry, "metric2.WorstCase");
+ EXPECT_NEAR(*metric2_worst / 1e6, 0.099, 0.0001);
+ }
+
+ SetTimeFunctionsForTesting(original_time_function);
+}
+
+TEST(UkmTimeAggregatorTest, EventsAveragedCorrectly) {
+ Timer::fake_time = 0;
+ auto original_time_function = SetTimeFunctionsForTesting(&Timer::GetTime);
+
+ ukm::TestUkmRecorder recorder;
+ int64_t source_id = ukm::UkmRecorder::GetNewSourceID();
+ std::unique_ptr<UkmTimeAggregator> aggregator(new UkmTimeAggregator(
+ "event", source_id, &recorder, {"metric1", "metric2"},
+ TimeDelta::FromSeconds(10000)));
+ // 1, 2, and 3 seconds.
+ for (int i = 1; i <= 3; ++i) {
+ auto timer = aggregator->GetScopedTimer(0);
+ Timer::fake_time += i;
+ }
+
+ // 3, 3, 3, and then 1 outside of the loop.
+ for (int i = 0; i < 3; ++i) {
+ auto timer = aggregator->GetScopedTimer(1);
+ Timer::fake_time += 3.;
+ }
+ {
+ auto timer = aggregator->GetScopedTimer(1);
+ Timer::fake_time += 1.;
+ }
+
+ aggregator.reset();
+ auto entries = recorder.GetEntriesByName("event");
+ EXPECT_EQ(entries.size(), 1u);
+ auto* entry = entries[0];
+
+ EXPECT_TRUE(ukm::TestUkmRecorder::EntryHasMetric(entry, "metric1.Average"));
+ const int64_t* metric1_average =
+ ukm::TestUkmRecorder::GetEntryMetric(entry, "metric1.Average");
+ // metric1 (1, 2, 3) average is 2
+ EXPECT_NEAR(*metric1_average / 1e6, 2.0, 0.0001);
+
+ EXPECT_TRUE(ukm::TestUkmRecorder::EntryHasMetric(entry, "metric1.WorstCase"));
+ const int64_t* metric1_worst =
+ ukm::TestUkmRecorder::GetEntryMetric(entry, "metric1.WorstCase");
+ // metric1 (1, 2, 3) worst case is 3
+ EXPECT_NEAR(*metric1_worst / 1e6, 3.0, 0.0001);
+
+ EXPECT_TRUE(ukm::TestUkmRecorder::EntryHasMetric(entry, "metric2.Average"));
+ const int64_t* metric2_average =
+ ukm::TestUkmRecorder::GetEntryMetric(entry, "metric2.Average");
+ // metric1 (3, 3, 3, 1) average is 2.5
+ EXPECT_NEAR(*metric2_average / 1e6, 2.5, 0.0001);
+
+ EXPECT_TRUE(ukm::TestUkmRecorder::EntryHasMetric(entry, "metric2.WorstCase"));
+ const int64_t* metric2_worst =
+ ukm::TestUkmRecorder::GetEntryMetric(entry, "metric2.WorstCase");
+ // metric1 (3, 3, 3, 1) worst case is 3
+ EXPECT_NEAR(*metric2_worst / 1e6, 3.0, 0.0001);
+
+ SetTimeFunctionsForTesting(original_time_function);
+}
+
+} // namespace
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/uuid.cc b/chromium/third_party/blink/renderer/platform/uuid.cc
new file mode 100644
index 00000000000..f4b035e673f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/uuid.cc
@@ -0,0 +1,24 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/uuid.h"
+
+#include "base/guid.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
+
+namespace blink {
+
+String CreateCanonicalUUIDString() {
+ std::string uuid = base::GenerateGUID();
+ return String::FromUTF8(uuid.data(), uuid.length());
+}
+
+bool IsValidUUID(const String& uuid) {
+ // In most (if not all) cases the given uuid should be utf-8, so this
+ // conversion should be almost no-op.
+ StringUTF8Adaptor utf8(uuid);
+ return base::IsValidGUIDOutputString(utf8.AsStringPiece());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/uuid.h b/chromium/third_party/blink/renderer/platform/uuid.h
new file mode 100644
index 00000000000..e7908e78979
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/uuid.h
@@ -0,0 +1,59 @@
+/*
+* Copyright (C) 2010 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_UUID_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_UUID_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+// Creates a UUID that consists of 32 hexadecimal digits and returns its
+// canonical form. The canonical form is displayed in 5 groups separated by
+// hyphens, in the form 8-4-4-4-12 for a total of 36 characters.
+// The hexadecimal values "a" through "f" are output as lower case characters.
+//
+// Note: for security reason, we should always generate version 4 UUID that use
+// a scheme relying only on random numbers. This algorithm sets the version
+// number as well as two reserved bits. All other bits are set using a random or
+// pseudorandom data source. Version 4 UUIDs have the form
+// xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx with hexadecimal digits for x and one of
+// 8, 9, A, or B for y.
+PLATFORM_EXPORT String CreateCanonicalUUIDString();
+
+// Check that the UUID is a valid UUID. A valid UUID is a string made out of 5
+// groups of lower case hexadecimal characters separated by hyphens, in the form
+// 8-4-4-4-12.
+PLATFORM_EXPORT bool IsValidUUID(const String& uuid);
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/uuid_test.cc b/chromium/third_party/blink/renderer/platform/uuid_test.cc
new file mode 100644
index 00000000000..36f3367d3b6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/uuid_test.cc
@@ -0,0 +1,76 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/uuid.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+TEST(UUIDTest, BaseUUID) {
+ EXPECT_TRUE(IsValidUUID("00000000-0000-0000-0000-000000000000"));
+}
+
+TEST(UUIDTest, ComplexUUID) {
+ EXPECT_TRUE(IsValidUUID("01234567-89ab-cdef-0123-456789abcdef"));
+ EXPECT_TRUE(IsValidUUID("7ad025e0-1e86-11e5-b5f7-727283247c7f"));
+}
+
+TEST(UUIDTest, WrongCharacter) {
+ EXPECT_FALSE(IsValidUUID("g0000000-0000-0000-0000-000000000000"));
+ EXPECT_FALSE(IsValidUUID("0000000g-0000-0000-0000-000000000000"));
+ EXPECT_FALSE(IsValidUUID("00000000-g000-0000-0000-000000000000"));
+ EXPECT_FALSE(IsValidUUID("00000000-000g-0000-0000-000000000000"));
+ EXPECT_FALSE(IsValidUUID("00000000-0000-g000-0000-000000000000"));
+ EXPECT_FALSE(IsValidUUID("00000000-0000-000g-0000-000000000000"));
+ EXPECT_FALSE(IsValidUUID("00000000-0000-0000-g000-000000000000"));
+ EXPECT_FALSE(IsValidUUID("00000000-0000-0000-000g-000000000000"));
+ EXPECT_FALSE(IsValidUUID("00000000-0000-0000-0000-g00000000000"));
+ EXPECT_FALSE(IsValidUUID("00000000-0000-0000-0000-00000000000g"));
+}
+
+TEST(UUIDTest, UpperCaseCharacter) {
+ EXPECT_FALSE(IsValidUUID("A0000000-0000-0000-0000-000000000000"));
+ EXPECT_FALSE(IsValidUUID("0000000A-0000-0000-0000-000000000000"));
+ EXPECT_FALSE(IsValidUUID("00000000-A000-0000-0000-000000000000"));
+ EXPECT_FALSE(IsValidUUID("00000000-000A-0000-0000-000000000000"));
+ EXPECT_FALSE(IsValidUUID("00000000-0000-A000-0000-000000000000"));
+ EXPECT_FALSE(IsValidUUID("00000000-0000-000A-0000-000000000000"));
+ EXPECT_FALSE(IsValidUUID("00000000-0000-0000-A000-000000000000"));
+ EXPECT_FALSE(IsValidUUID("00000000-0000-0000-000A-000000000000"));
+ EXPECT_FALSE(IsValidUUID("00000000-0000-0000-0000-A00000000000"));
+ EXPECT_FALSE(IsValidUUID("00000000-0000-0000-0000-00000000000A"));
+}
+
+TEST(UUIDTest, LongUUID) {
+ EXPECT_FALSE(IsValidUUID("a00000000-0000-0000-0000-000000000000"));
+ EXPECT_FALSE(IsValidUUID("00000000a-0000-0000-0000-000000000000"));
+ EXPECT_FALSE(IsValidUUID("00000000-a0000-0000-0000-000000000000"));
+ EXPECT_FALSE(IsValidUUID("00000000-0000a-0000-0000-000000000000"));
+ EXPECT_FALSE(IsValidUUID("00000000-0000-a0000-0000-000000000000"));
+ EXPECT_FALSE(IsValidUUID("00000000-0000-0000a-0000-000000000000"));
+ EXPECT_FALSE(IsValidUUID("00000000-0000-0000-a0000-000000000000"));
+ EXPECT_FALSE(IsValidUUID("00000000-0000-0000-0000a-000000000000"));
+ EXPECT_FALSE(IsValidUUID("00000000-0000-0000-0000-a000000000000"));
+ EXPECT_FALSE(IsValidUUID("00000000-0000-0000-0000-000000000000a"));
+}
+
+TEST(UUIDTest, ShortUUID) {
+ EXPECT_FALSE(IsValidUUID("0000000-0000-0000-0000-000000000000"));
+ EXPECT_FALSE(IsValidUUID("0000000-0000-0000-0000-000000000000"));
+ EXPECT_FALSE(IsValidUUID("00000000-000-0000-0000-000000000000"));
+ EXPECT_FALSE(IsValidUUID("00000000-000-0000-0000-000000000000"));
+ EXPECT_FALSE(IsValidUUID("00000000-0000-000-0000-000000000000"));
+ EXPECT_FALSE(IsValidUUID("00000000-0000-000-0000-000000000000"));
+ EXPECT_FALSE(IsValidUUID("00000000-0000-0000-000-000000000000"));
+ EXPECT_FALSE(IsValidUUID("00000000-0000-0000-000-000000000000"));
+ EXPECT_FALSE(IsValidUUID("00000000-0000-0000-0000-00000000000"));
+ EXPECT_FALSE(IsValidUUID("00000000-0000-0000-0000-00000000000"));
+}
+
+TEST(UUIDTest, NoHyphen) {
+ EXPECT_FALSE(IsValidUUID("00000000 0000 0000 0000 000000000000"));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/waitable_event.cc b/chromium/third_party/blink/renderer/platform/waitable_event.cc
new file mode 100644
index 00000000000..3b92b1e9f13
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/waitable_event.cc
@@ -0,0 +1,49 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/waitable_event.h"
+
+#include <vector>
+#include "base/synchronization/waitable_event.h"
+#include "third_party/blink/renderer/platform/heap/safe_point.h"
+#include "third_party/blink/renderer/platform/heap/thread_state.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+
+namespace blink {
+
+WaitableEvent::WaitableEvent(ResetPolicy policy, InitialState state) {
+ impl_ = std::make_unique<base::WaitableEvent>(
+ policy == ResetPolicy::kManual
+ ? base::WaitableEvent::ResetPolicy::MANUAL
+ : base::WaitableEvent::ResetPolicy::AUTOMATIC,
+ state == InitialState::kSignaled
+ ? base::WaitableEvent::InitialState::SIGNALED
+ : base::WaitableEvent::InitialState::NOT_SIGNALED);
+}
+
+WaitableEvent::~WaitableEvent() = default;
+
+void WaitableEvent::Reset() {
+ impl_->Reset();
+}
+
+void WaitableEvent::Wait() {
+ impl_->Wait();
+}
+
+void WaitableEvent::Signal() {
+ impl_->Signal();
+}
+
+size_t WaitableEvent::WaitMultiple(const WTF::Vector<WaitableEvent*>& events) {
+ std::vector<base::WaitableEvent*> base_events;
+ for (size_t i = 0; i < events.size(); ++i)
+ base_events.push_back(events[i]->impl_.get());
+ size_t idx =
+ base::WaitableEvent::WaitMany(base_events.data(), base_events.size());
+ DCHECK_LT(idx, events.size());
+ return idx;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/waitable_event.h b/chromium/third_party/blink/renderer/platform/waitable_event.h
new file mode 100644
index 00000000000..214a07ddca8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/waitable_event.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2014 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WAITABLE_EVENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WAITABLE_EVENT_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace base {
+class WaitableEvent;
+}; // namespace base
+
+namespace blink {
+
+// TODO(crbug.com/796799): Deprecate blink::WaitableEvent and use
+// base::WaitableEvent instead.
+//
+// Provides a thread synchronization that can be used to allow one thread to
+// wait until another thread to finish some work.
+class PLATFORM_EXPORT WaitableEvent {
+ public:
+ // If ResetPolicy::Manual is specified on creation, to set the event state
+ // to non-signaled, a consumer must call reset(). Otherwise, the system
+ // automatically resets the event state to non-signaled after a single
+ // waiting thread has been released.
+ enum class ResetPolicy { kAuto, kManual };
+
+ // Specify the initial state on creation.
+ enum class InitialState { kNonSignaled, kSignaled };
+
+ explicit WaitableEvent(ResetPolicy = ResetPolicy::kAuto,
+ InitialState = InitialState::kNonSignaled);
+
+ ~WaitableEvent();
+
+ // Puts the event in the un-signaled state.
+ void Reset();
+
+ // Waits indefinitely for the event to be signaled.
+ void Wait();
+
+ // Puts the event in the signaled state. Causing any thread blocked on Wait
+ // to be woken up. The event state is reset to non-signaled after
+ // a waiting thread has been released.
+ void Signal();
+
+ // Waits on multiple events and returns the index of the object that
+ // has been signaled. Any event objects given to this method must
+ // not deleted while this wait is happening.
+ static size_t WaitMultiple(const WTF::Vector<WaitableEvent*>& events);
+
+ private:
+ WaitableEvent(const WaitableEvent&) = delete;
+ void operator=(const WaitableEvent&) = delete;
+
+ std::unique_ptr<base::WaitableEvent> impl_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WAITABLE_EVENT_H_
diff --git a/chromium/third_party/blink/renderer/platform/web_gesture_event.cc b/chromium/third_party/blink/renderer/platform/web_gesture_event.cc
new file mode 100644
index 00000000000..e8900f6e405
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/web_gesture_event.cc
@@ -0,0 +1,158 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_gesture_event.h"
+
+namespace blink {
+
+float WebGestureEvent::DeltaXInRootFrame() const {
+ if (type_ == WebInputEvent::kGestureScrollBegin)
+ return data.scroll_begin.delta_x_hint / frame_scale_;
+ DCHECK(type_ == WebInputEvent::kGestureScrollUpdate);
+ return data.scroll_update.delta_x / frame_scale_;
+}
+
+float WebGestureEvent::DeltaYInRootFrame() const {
+ if (type_ == WebInputEvent::kGestureScrollBegin)
+ return data.scroll_begin.delta_y_hint / frame_scale_;
+ DCHECK(type_ == WebInputEvent::kGestureScrollUpdate);
+ return data.scroll_update.delta_y / frame_scale_;
+}
+
+WebGestureEvent::ScrollUnits WebGestureEvent::DeltaUnits() const {
+ if (type_ == WebInputEvent::kGestureScrollBegin)
+ return data.scroll_begin.delta_hint_units;
+ if (type_ == WebInputEvent::kGestureScrollUpdate)
+ return data.scroll_update.delta_units;
+ DCHECK(type_ == WebInputEvent::kGestureScrollEnd);
+ return data.scroll_end.delta_units;
+}
+
+float WebGestureEvent::PinchScale() const {
+ DCHECK(type_ == WebInputEvent::kGesturePinchUpdate);
+ return data.pinch_update.scale;
+}
+
+WebGestureEvent::InertialPhaseState WebGestureEvent::InertialPhase() const {
+ if (type_ == WebInputEvent::kGestureScrollBegin)
+ return data.scroll_begin.inertial_phase;
+ if (type_ == WebInputEvent::kGestureScrollUpdate)
+ return data.scroll_update.inertial_phase;
+ DCHECK(type_ == WebInputEvent::kGestureScrollEnd);
+ return data.scroll_end.inertial_phase;
+}
+
+bool WebGestureEvent::Synthetic() const {
+ if (type_ == WebInputEvent::kGestureScrollBegin)
+ return data.scroll_begin.synthetic;
+ DCHECK(type_ == WebInputEvent::kGestureScrollEnd);
+ return data.scroll_end.synthetic;
+}
+
+float WebGestureEvent::VelocityX() const {
+ if (type_ == WebInputEvent::kGestureScrollUpdate)
+ return data.scroll_update.velocity_x;
+ DCHECK(type_ == WebInputEvent::kGestureFlingStart);
+ return data.fling_start.velocity_x;
+}
+
+float WebGestureEvent::VelocityY() const {
+ if (type_ == WebInputEvent::kGestureScrollUpdate)
+ return data.scroll_update.velocity_y;
+ DCHECK(type_ == WebInputEvent::kGestureFlingStart);
+ return data.fling_start.velocity_y;
+}
+
+WebFloatSize WebGestureEvent::TapAreaInRootFrame() const {
+ if (type_ == WebInputEvent::kGestureTwoFingerTap) {
+ return WebFloatSize(data.two_finger_tap.first_finger_width / frame_scale_,
+ data.two_finger_tap.first_finger_height / frame_scale_);
+ } else if (type_ == WebInputEvent::kGestureLongPress ||
+ type_ == WebInputEvent::kGestureLongTap) {
+ return WebFloatSize(data.long_press.width / frame_scale_,
+ data.long_press.height / frame_scale_);
+ } else if (type_ == WebInputEvent::kGestureTap ||
+ type_ == WebInputEvent::kGestureTapUnconfirmed) {
+ return WebFloatSize(data.tap.width / frame_scale_,
+ data.tap.height / frame_scale_);
+ } else if (type_ == WebInputEvent::kGestureTapDown) {
+ return WebFloatSize(data.tap_down.width / frame_scale_,
+ data.tap_down.height / frame_scale_);
+ } else if (type_ == WebInputEvent::kGestureShowPress) {
+ return WebFloatSize(data.show_press.width / frame_scale_,
+ data.show_press.height / frame_scale_);
+ }
+ // This function is called for all gestures and determined if the tap
+ // area is empty or not; so return an empty rect here.
+ return WebFloatSize();
+}
+
+WebFloatPoint WebGestureEvent::PositionInRootFrame() const {
+ return WebFloatPoint(
+ (position_in_widget_.x / frame_scale_) + frame_translate_.x,
+ (position_in_widget_.y / frame_scale_) + frame_translate_.y);
+}
+
+int WebGestureEvent::TapCount() const {
+ DCHECK(type_ == WebInputEvent::kGestureTap);
+ return data.tap.tap_count;
+}
+
+void WebGestureEvent::ApplyTouchAdjustment(WebFloatPoint root_frame_coords) {
+ // Update the window-relative position of the event so that the node that
+ // was ultimately hit is under this point (i.e. elementFromPoint for the
+ // client co-ordinates in a 'click' event should yield the target). The
+ // global position is intentionally left unmodified because it's intended to
+ // reflect raw co-ordinates unrelated to any content.
+ frame_translate_.x =
+ root_frame_coords.x - (position_in_widget_.x / frame_scale_);
+ frame_translate_.y =
+ root_frame_coords.y - (position_in_widget_.y / frame_scale_);
+}
+
+void WebGestureEvent::FlattenTransform() {
+ if (frame_scale_ != 1) {
+ switch (type_) {
+ case WebInputEvent::kGestureScrollBegin:
+ data.scroll_begin.delta_x_hint /= frame_scale_;
+ data.scroll_begin.delta_y_hint /= frame_scale_;
+ break;
+ case WebInputEvent::kGestureScrollUpdate:
+ data.scroll_update.delta_x /= frame_scale_;
+ data.scroll_update.delta_y /= frame_scale_;
+ break;
+ case WebInputEvent::kGestureTwoFingerTap:
+ data.two_finger_tap.first_finger_width /= frame_scale_;
+ data.two_finger_tap.first_finger_height /= frame_scale_;
+ break;
+ case WebInputEvent::kGestureLongPress:
+ case WebInputEvent::kGestureLongTap:
+ data.long_press.width /= frame_scale_;
+ data.long_press.height /= frame_scale_;
+ break;
+ case WebInputEvent::kGestureTap:
+ case WebInputEvent::kGestureTapUnconfirmed:
+ data.tap.width /= frame_scale_;
+ data.tap.height /= frame_scale_;
+ break;
+ case WebInputEvent::kGestureTapDown:
+ data.tap_down.width /= frame_scale_;
+ data.tap_down.height /= frame_scale_;
+ break;
+ case WebInputEvent::kGestureShowPress:
+ data.show_press.width /= frame_scale_;
+ data.show_press.height /= frame_scale_;
+ break;
+ default:
+ break;
+ }
+ }
+
+ SetPositionInWidget(PositionInRootFrame());
+ frame_translate_.x = 0;
+ frame_translate_.y = 0;
+ frame_scale_ = 1;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/web_icon_sizes_fuzzer.cc b/chromium/third_party/blink/renderer/platform/web_icon_sizes_fuzzer.cc
new file mode 100644
index 00000000000..5c53f274010
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/web_icon_sizes_fuzzer.cc
@@ -0,0 +1,25 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_icon_sizes_parser.h"
+#include "third_party/blink/public/platform/web_size.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.h"
+
+namespace blink {
+
+// Fuzzer for blink::MHTMLParser.
+int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ static BlinkFuzzerTestSupport test_support = BlinkFuzzerTestSupport();
+ WebString string =
+ WebString::FromUTF8(reinterpret_cast<const char*>(data), size);
+ WebIconSizesParser::ParseIconSizes(string);
+ return 0;
+}
+
+} // namespace blink
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ return blink::LLVMFuzzerTestOneInput(data, size);
+}
diff --git a/chromium/third_party/blink/renderer/platform/web_icon_sizes_parser.cc b/chromium/third_party/blink/renderer/platform/web_icon_sizes_parser.cc
new file mode 100644
index 00000000000..9784162d598
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/web_icon_sizes_parser.cc
@@ -0,0 +1,111 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_icon_sizes_parser.h"
+
+#include <algorithm>
+#include "third_party/blink/public/platform/web_size.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_to_number.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+namespace {
+
+static inline bool IsIntegerStart(UChar c) {
+ return c > '0' && c <= '9';
+}
+
+static bool IsWhitespace(UChar c) {
+ // Sizes space characters are U+0020 SPACE, U+0009 CHARACTER TABULATION (tab),
+ // U+000A LINE FEED (LF), U+000C FORM FEED (FF),
+ // and U+000D CARRIAGE RETURN (CR).
+ return c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\r';
+}
+
+static bool IsNotWhitespace(UChar c) {
+ return !IsWhitespace(c);
+}
+
+static bool IsNonDigit(UChar c) {
+ return !IsASCIIDigit(c);
+}
+
+static inline size_t FindEndOfWord(const String& string, size_t start) {
+ return std::min(string.Find(IsWhitespace, start),
+ static_cast<size_t>(string.length()));
+}
+
+static inline int PartialStringToInt(const String& string,
+ size_t start,
+ size_t end) {
+ if (string.Is8Bit()) {
+ return CharactersToInt(string.Characters8() + start, end - start,
+ WTF::NumberParsingOptions::kNone, nullptr);
+ }
+ return CharactersToInt(string.Characters16() + start, end - start,
+ WTF::NumberParsingOptions::kNone, nullptr);
+}
+
+} // namespace
+
+WebVector<WebSize> WebIconSizesParser::ParseIconSizes(
+ const WebString& web_sizes_string) {
+ String sizes_string = web_sizes_string;
+ Vector<WebSize> icon_sizes;
+ if (sizes_string.IsEmpty())
+ return icon_sizes;
+
+ unsigned length = sizes_string.length();
+ for (unsigned i = 0; i < length; ++i) {
+ // Skip whitespaces.
+ i = std::min(sizes_string.Find(IsNotWhitespace, i),
+ static_cast<size_t>(length));
+ if (i >= length)
+ break;
+
+ // See if the current size is "any".
+ if (sizes_string.FindIgnoringCase("any", i) == i &&
+ (i + 3 == length || IsWhitespace(sizes_string[i + 3]))) {
+ icon_sizes.push_back(WebSize(0, 0));
+ i = i + 3;
+ continue;
+ }
+
+ // Parse the width.
+ if (!IsIntegerStart(sizes_string[i])) {
+ i = FindEndOfWord(sizes_string, i);
+ continue;
+ }
+ unsigned width_start = i;
+ i = std::min(sizes_string.Find(IsNonDigit, i), static_cast<size_t>(length));
+ if (i >= length || (sizes_string[i] != 'x' && sizes_string[i] != 'X')) {
+ i = FindEndOfWord(sizes_string, i);
+ continue;
+ }
+ unsigned width_end = i++;
+
+ // Parse the height.
+ if (i >= length || !IsIntegerStart(sizes_string[i])) {
+ i = FindEndOfWord(sizes_string, i);
+ continue;
+ }
+ unsigned height_start = i;
+ i = std::min(sizes_string.Find(IsNonDigit, i), static_cast<size_t>(length));
+ if (i < length && !IsWhitespace(sizes_string[i])) {
+ i = FindEndOfWord(sizes_string, i);
+ continue;
+ }
+ unsigned height_end = i;
+
+ // Append the parsed size to iconSizes.
+ icon_sizes.push_back(
+ WebSize(PartialStringToInt(sizes_string, width_start, width_end),
+ PartialStringToInt(sizes_string, height_start, height_end)));
+ }
+ return icon_sizes;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/web_icon_sizes_parser_test.cc b/chromium/third_party/blink/renderer/platform/web_icon_sizes_parser_test.cc
new file mode 100644
index 00000000000..a9807a54828
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/web_icon_sizes_parser_test.cc
@@ -0,0 +1,100 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_icon_sizes_parser.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/web_size.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+
+namespace blink {
+
+class WebIconSizesParserTest : public testing::Test {};
+
+TEST(WebIconSizesParserTest, parseSizes) {
+ WebString sizes_attribute = "32x33";
+ WebVector<WebSize> sizes;
+ sizes = WebIconSizesParser::ParseIconSizes(sizes_attribute);
+ ASSERT_EQ(1U, sizes.size());
+ EXPECT_EQ(32, sizes[0].width);
+ EXPECT_EQ(33, sizes[0].height);
+
+ sizes_attribute = "0x33";
+ sizes = WebIconSizesParser::ParseIconSizes(sizes_attribute);
+ ASSERT_EQ(0U, sizes.size());
+
+ UChar attribute[] = {'3', '2', 'x', '3', '3', 0};
+ sizes_attribute = AtomicString(attribute);
+ sizes = WebIconSizesParser::ParseIconSizes(sizes_attribute);
+ ASSERT_EQ(1U, sizes.size());
+ EXPECT_EQ(32, sizes[0].width);
+ EXPECT_EQ(33, sizes[0].height);
+
+ sizes_attribute = " 32x33 16X17 128x129 ";
+ sizes = WebIconSizesParser::ParseIconSizes(sizes_attribute);
+ ASSERT_EQ(3U, sizes.size());
+ EXPECT_EQ(32, sizes[0].width);
+ EXPECT_EQ(33, sizes[0].height);
+ EXPECT_EQ(16, sizes[1].width);
+ EXPECT_EQ(17, sizes[1].height);
+ EXPECT_EQ(128, sizes[2].width);
+ EXPECT_EQ(129, sizes[2].height);
+
+ sizes_attribute = " \n 32x33 \r 16X17 \t 128x129 \f ";
+ sizes = WebIconSizesParser::ParseIconSizes(sizes_attribute);
+ ASSERT_EQ(3U, sizes.size());
+
+ sizes_attribute = "any";
+ sizes = WebIconSizesParser::ParseIconSizes(sizes_attribute);
+ ASSERT_EQ(1U, sizes.size());
+ EXPECT_EQ(0, sizes[0].width);
+ EXPECT_EQ(0, sizes[0].height);
+
+ sizes_attribute = "ANY";
+ sizes = WebIconSizesParser::ParseIconSizes(sizes_attribute);
+ ASSERT_EQ(1U, sizes.size());
+
+ sizes_attribute = "AnY";
+ sizes = WebIconSizesParser::ParseIconSizes(sizes_attribute);
+ ASSERT_EQ(1U, sizes.size());
+
+ sizes_attribute = "an";
+ sizes = WebIconSizesParser::ParseIconSizes(sizes_attribute);
+ ASSERT_EQ(0U, sizes.size());
+
+ sizes_attribute = "32x33 32";
+ sizes = WebIconSizesParser::ParseIconSizes(sizes_attribute);
+ ASSERT_EQ(1U, sizes.size());
+ EXPECT_EQ(32, sizes[0].width);
+ EXPECT_EQ(33, sizes[0].height);
+
+ sizes_attribute = "32x33 32x";
+ sizes = WebIconSizesParser::ParseIconSizes(sizes_attribute);
+ ASSERT_EQ(1U, sizes.size());
+ EXPECT_EQ(32, sizes[0].width);
+ EXPECT_EQ(33, sizes[0].height);
+
+ sizes_attribute = "32x33 x32";
+ sizes = WebIconSizesParser::ParseIconSizes(sizes_attribute);
+ ASSERT_EQ(1U, sizes.size());
+ EXPECT_EQ(32, sizes[0].width);
+ EXPECT_EQ(33, sizes[0].height);
+
+ sizes_attribute = "32x33 any";
+ sizes = WebIconSizesParser::ParseIconSizes(sizes_attribute);
+ ASSERT_EQ(2U, sizes.size());
+ EXPECT_EQ(32, sizes[0].width);
+ EXPECT_EQ(33, sizes[0].height);
+ EXPECT_EQ(0, sizes[1].width);
+ EXPECT_EQ(0, sizes[1].height);
+
+ sizes_attribute = "32x33, 64x64";
+ sizes = WebIconSizesParser::ParseIconSizes(sizes_attribute);
+ ASSERT_EQ(1U, sizes.size());
+ EXPECT_EQ(64, sizes[0].width);
+ EXPECT_EQ(64, sizes[0].height);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/web_mouse_event.cc b/chromium/third_party/blink/renderer/platform/web_mouse_event.cc
new file mode 100644
index 00000000000..116f214fda1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/web_mouse_event.cc
@@ -0,0 +1,74 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_mouse_event.h"
+
+#include "third_party/blink/public/platform/web_gesture_event.h"
+
+namespace blink {
+
+WebMouseEvent::WebMouseEvent(WebInputEvent::Type type,
+ const WebGestureEvent& gesture_event,
+ Button button_param,
+ int click_count_param,
+ int modifiers,
+ double time_stamp_seconds,
+ PointerId id_param)
+ : WebInputEvent(sizeof(WebMouseEvent), type, modifiers, time_stamp_seconds),
+ WebPointerProperties(id_param,
+ WebPointerProperties::PointerType::kMouse,
+ button_param),
+ click_count(click_count_param) {
+ DCHECK_GE(type, kMouseTypeFirst);
+ DCHECK_LE(type, kMouseTypeLast);
+ SetPositionInWidget(gesture_event.PositionInWidget());
+ SetPositionInScreen(gesture_event.PositionInScreen());
+ SetFrameScale(gesture_event.FrameScale());
+ SetFrameTranslate(gesture_event.FrameTranslate());
+ SetMenuSourceType(gesture_event.GetType());
+}
+
+WebFloatPoint WebMouseEvent::MovementInRootFrame() const {
+ return WebFloatPoint((movement_x / frame_scale_),
+ (movement_y / frame_scale_));
+}
+
+WebFloatPoint WebMouseEvent::PositionInRootFrame() const {
+ return WebFloatPoint(
+ (position_in_widget_.x / frame_scale_) + frame_translate_.x,
+ (position_in_widget_.y / frame_scale_) + frame_translate_.y);
+}
+
+WebMouseEvent WebMouseEvent::FlattenTransform() const {
+ WebMouseEvent result = *this;
+ result.FlattenTransformSelf();
+ return result;
+}
+
+void WebMouseEvent::FlattenTransformSelf() {
+ position_in_widget_ = PositionInRootFrame();
+ frame_translate_.x = 0;
+ frame_translate_.y = 0;
+ frame_scale_ = 1;
+}
+
+void WebMouseEvent::SetMenuSourceType(WebInputEvent::Type type) {
+ switch (type) {
+ case kGestureTapDown:
+ case kGestureTap:
+ case kGestureDoubleTap:
+ menu_source_type = kMenuSourceTouch;
+ break;
+ case kGestureLongPress:
+ menu_source_type = kMenuSourceLongPress;
+ break;
+ case kGestureLongTap:
+ menu_source_type = kMenuSourceLongTap;
+ break;
+ default:
+ menu_source_type = kMenuSourceNone;
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/web_mouse_wheel_event.cc b/chromium/third_party/blink/renderer/platform/web_mouse_wheel_event.cc
new file mode 100644
index 00000000000..cc02b1e4947
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/web_mouse_wheel_event.cc
@@ -0,0 +1,25 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_mouse_wheel_event.h"
+
+namespace blink {
+
+float WebMouseWheelEvent::DeltaXInRootFrame() const {
+ return delta_x / frame_scale_;
+}
+
+float WebMouseWheelEvent::DeltaYInRootFrame() const {
+ return delta_y / frame_scale_;
+}
+
+WebMouseWheelEvent WebMouseWheelEvent::FlattenTransform() const {
+ WebMouseWheelEvent result = *this;
+ result.delta_x /= result.frame_scale_;
+ result.delta_y /= result.frame_scale_;
+ result.FlattenTransformSelf();
+ return result;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/web_pointer_event.cc b/chromium/third_party/blink/renderer/platform/web_pointer_event.cc
new file mode 100644
index 00000000000..db9d1237b55
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/web_pointer_event.cc
@@ -0,0 +1,102 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_pointer_event.h"
+
+#include "third_party/blink/public/platform/web_float_point.h"
+
+namespace blink {
+
+namespace {
+
+WebInputEvent::Type PointerEventTypeForTouchPointState(
+ WebTouchPoint::State state) {
+ switch (state) {
+ case WebTouchPoint::kStateReleased:
+ return WebInputEvent::Type::kPointerUp;
+ case WebTouchPoint::kStateCancelled:
+ return WebInputEvent::Type::kPointerCancel;
+ case WebTouchPoint::kStatePressed:
+ return WebInputEvent::Type::kPointerDown;
+ case WebTouchPoint::kStateMoved:
+ return WebInputEvent::Type::kPointerMove;
+ case WebTouchPoint::kStateStationary:
+ default:
+ NOTREACHED();
+ return WebInputEvent::Type::kUndefined;
+ }
+}
+
+} // namespace
+
+WebPointerEvent::WebPointerEvent(const WebTouchEvent& touch_event,
+ const WebTouchPoint& touch_point)
+ : WebInputEvent(sizeof(WebPointerEvent)),
+ WebPointerProperties(touch_point),
+ hovering(touch_event.hovering),
+ width(touch_point.radius_x * 2.f),
+ height(touch_point.radius_y * 2.f) {
+ // WebInutEvent attributes
+ SetFrameScale(touch_event.FrameScale());
+ SetFrameTranslate(touch_event.FrameTranslate());
+ SetTimeStampSeconds(touch_event.TimeStampSeconds());
+ SetType(PointerEventTypeForTouchPointState(touch_point.state));
+ SetModifiers(touch_event.GetModifiers());
+ // WebTouchEvent attributes
+ dispatch_type = touch_event.dispatch_type;
+ moved_beyond_slop_region = touch_event.moved_beyond_slop_region;
+ touch_start_or_first_touch_move = touch_event.touch_start_or_first_touch_move;
+ unique_touch_event_id = touch_event.unique_touch_event_id;
+ // WebTouchPoint attributes
+ rotation_angle = touch_point.rotation_angle;
+ // TODO(crbug.com/816504): Touch point button is not set at this point yet.
+ button = (GetType() == WebInputEvent::kPointerDown ||
+ GetType() == WebInputEvent::kPointerUp)
+ ? WebPointerProperties::Button::kLeft
+ : WebPointerProperties::Button::kNoButton;
+}
+
+WebPointerEvent::WebPointerEvent(WebInputEvent::Type type,
+ const WebMouseEvent& mouse_event)
+ : WebInputEvent(sizeof(WebPointerEvent)),
+ WebPointerProperties(mouse_event),
+ hovering(true),
+ width(std::numeric_limits<float>::quiet_NaN()),
+ height(std::numeric_limits<float>::quiet_NaN()) {
+ DCHECK_GE(type, WebInputEvent::kPointerTypeFirst);
+ DCHECK_LE(type, WebInputEvent::kPointerTypeLast);
+ SetFrameScale(mouse_event.FrameScale());
+ SetFrameTranslate(mouse_event.FrameTranslate());
+ SetTimeStampSeconds(mouse_event.TimeStampSeconds());
+ SetType(type);
+ SetModifiers(mouse_event.GetModifiers());
+}
+
+WebPointerEvent WebPointerEvent::CreatePointerCausesUaActionEvent(
+ WebPointerProperties::PointerType type,
+ double time_stamp_seconds) {
+ WebPointerEvent event;
+ event.pointer_type = type;
+ event.SetTimeStampSeconds(time_stamp_seconds);
+ event.SetType(WebInputEvent::Type::kPointerCausedUaAction);
+ return event;
+}
+
+WebPointerEvent WebPointerEvent::WebPointerEventInRootFrame() const {
+ WebPointerEvent transformed_event = *this;
+ if (HasWidth())
+ transformed_event.width /= frame_scale_;
+ if (HasHeight())
+ transformed_event.height /= frame_scale_;
+ transformed_event.movement_x /= frame_scale_;
+ transformed_event.movement_y /= frame_scale_;
+ transformed_event.position_in_widget_ =
+ WebFloatPoint((transformed_event.PositionInWidget().x / frame_scale_) +
+ frame_translate_.x,
+ (transformed_event.PositionInWidget().y / frame_scale_) +
+ frame_translate_.y);
+ return transformed_event;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/web_screen_info_test.cc b/chromium/third_party/blink/renderer/platform/web_screen_info_test.cc
new file mode 100644
index 00000000000..1fa38b01d10
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/web_screen_info_test.cc
@@ -0,0 +1,57 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_screen_info.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+TEST(WebScreenInfoTest, Equality) {
+ WebScreenInfo screen_info1;
+ WebScreenInfo screen_info2;
+
+ EXPECT_EQ(screen_info1, screen_info2);
+
+ // Change same values in screenInfo1.
+ screen_info1.device_scale_factor = 10.f;
+ screen_info1.depth = 3;
+ screen_info1.depth_per_component = 2;
+ screen_info1.is_monochrome = false;
+
+ EXPECT_NE(screen_info1, screen_info2);
+
+ // Set the same values to screenInfo2, they should be equal now.
+ screen_info2.device_scale_factor = 10.f;
+ screen_info2.depth = 3;
+ screen_info2.depth_per_component = 2;
+ screen_info2.is_monochrome = false;
+
+ EXPECT_EQ(screen_info1, screen_info2);
+
+ // Set all the known members.
+ screen_info1.device_scale_factor = 2.f;
+ screen_info1.depth = 1;
+ screen_info1.depth_per_component = 1;
+ screen_info1.is_monochrome = false;
+ screen_info1.rect = WebRect(0, 0, 1024, 1024);
+ screen_info1.available_rect = WebRect(0, 0, 1024, 1024);
+ screen_info1.orientation_type = blink::kWebScreenOrientationLandscapePrimary;
+ screen_info1.orientation_angle = 90;
+
+ EXPECT_NE(screen_info1, screen_info2);
+
+ screen_info2.device_scale_factor = 2.f;
+ screen_info2.depth = 1;
+ screen_info2.depth_per_component = 1;
+ screen_info2.is_monochrome = false;
+ screen_info2.rect = WebRect(0, 0, 1024, 1024);
+ screen_info2.available_rect = WebRect(0, 0, 1024, 1024);
+ screen_info2.orientation_type = blink::kWebScreenOrientationLandscapePrimary;
+ screen_info2.orientation_angle = 90;
+
+ EXPECT_EQ(screen_info1, screen_info2);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/web_task_runner.cc b/chromium/third_party/blink/renderer/platform/web_task_runner.cc
new file mode 100644
index 00000000000..3ae21f300a3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/web_task_runner.cc
@@ -0,0 +1,162 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/web_task_runner.h"
+
+#include "base/bind_helpers.h"
+#include "base/single_thread_task_runner.h"
+
+namespace base {
+
+using RunnerMethodType =
+ void (blink::TaskHandle::Runner::*)(const blink::TaskHandle&);
+
+template <>
+struct CallbackCancellationTraits<
+ RunnerMethodType,
+ std::tuple<base::WeakPtr<blink::TaskHandle::Runner>, blink::TaskHandle>> {
+ static constexpr bool is_cancellable = true;
+
+ static bool IsCancelled(RunnerMethodType,
+ const base::WeakPtr<blink::TaskHandle::Runner>&,
+ const blink::TaskHandle& handle) {
+ return !handle.IsActive();
+ }
+};
+
+} // namespace base
+
+namespace blink {
+
+namespace {
+
+void RunCrossThreadClosure(CrossThreadClosure task) {
+ std::move(task).Run();
+}
+
+} // namespace
+
+class TaskHandle::Runner : public WTF::ThreadSafeRefCounted<Runner> {
+ public:
+ explicit Runner(base::OnceClosure task)
+ : task_(std::move(task)), weak_ptr_factory_(this) {}
+
+ base::WeakPtr<Runner> AsWeakPtr() { return weak_ptr_factory_.GetWeakPtr(); }
+
+ bool IsActive() const { return task_ && !task_.IsCancelled(); }
+
+ void Cancel() {
+ base::OnceClosure task = std::move(task_);
+ weak_ptr_factory_.InvalidateWeakPtrs();
+ }
+
+ ~Runner() { Cancel(); }
+
+ // The TaskHandle parameter on run() holds a reference to the Runner to keep
+ // it alive while a task is pending in a task queue, and clears the reference
+ // on the task disposal, so that it doesn't leave a circular reference like
+ // below:
+ // struct Foo : GarbageCollected<Foo> {
+ // void bar() {}
+ // TaskHandle m_handle;
+ // };
+ //
+ // foo.m_handle = taskRunner->postCancellableTask(
+ // FROM_HERE, WTF::bind(&Foo::bar, wrapPersistent(foo)));
+ //
+ // There is a circular reference in the example above as:
+ // foo -> m_handle -> m_runner -> m_task -> Persistent<Foo> in WTF::bind.
+ // The TaskHandle parameter on run() is needed to break the circle by clearing
+ // |m_task| when the wrapped base::OnceClosure is deleted.
+ void Run(const TaskHandle&) {
+ base::OnceClosure task = std::move(task_);
+ weak_ptr_factory_.InvalidateWeakPtrs();
+ std::move(task).Run();
+ }
+
+ private:
+ base::OnceClosure task_;
+ base::WeakPtrFactory<Runner> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(Runner);
+};
+
+bool TaskHandle::IsActive() const {
+ return runner_ && runner_->IsActive();
+}
+
+void TaskHandle::Cancel() {
+ if (runner_) {
+ runner_->Cancel();
+ runner_ = nullptr;
+ }
+}
+
+TaskHandle::TaskHandle() = default;
+
+TaskHandle::~TaskHandle() {
+ Cancel();
+}
+
+TaskHandle::TaskHandle(TaskHandle&&) = default;
+
+TaskHandle& TaskHandle::operator=(TaskHandle&& other) {
+ TaskHandle tmp(std::move(other));
+ runner_.swap(tmp.runner_);
+ return *this;
+}
+
+TaskHandle::TaskHandle(scoped_refptr<Runner> runner)
+ : runner_(std::move(runner)) {
+ DCHECK(runner_);
+}
+
+// Use a custom function for base::Bind instead of WTF::Bind to
+// avoid copying the closure later in the call chain. Copying the bound state
+// can lead to data races with ref counted objects like StringImpl. See
+// crbug.com/679915 for more details.
+void PostCrossThreadTask(base::SequencedTaskRunner& task_runner,
+ const base::Location& location,
+ CrossThreadClosure task) {
+ task_runner.PostDelayedTask(
+ location, base::BindOnce(&RunCrossThreadClosure, std::move(task)),
+ base::TimeDelta());
+}
+
+void PostDelayedCrossThreadTask(base::SequencedTaskRunner& task_runner,
+ const base::Location& location,
+ CrossThreadClosure task,
+ TimeDelta delay) {
+ task_runner.PostDelayedTask(
+ location, base::BindOnce(&RunCrossThreadClosure, std::move(task)), delay);
+}
+
+TaskHandle PostCancellableTask(base::SequencedTaskRunner& task_runner,
+ const base::Location& location,
+ base::OnceClosure task) {
+ DCHECK(task_runner.RunsTasksInCurrentSequence());
+ scoped_refptr<TaskHandle::Runner> runner =
+ base::AdoptRef(new TaskHandle::Runner(std::move(task)));
+ task_runner.PostTask(location,
+ WTF::Bind(&TaskHandle::Runner::Run, runner->AsWeakPtr(),
+ TaskHandle(runner)));
+ return TaskHandle(runner);
+}
+
+TaskHandle PostDelayedCancellableTask(base::SequencedTaskRunner& task_runner,
+ const base::Location& location,
+ base::OnceClosure task,
+ TimeDelta delay) {
+ DCHECK(task_runner.RunsTasksInCurrentSequence());
+ scoped_refptr<TaskHandle::Runner> runner =
+ base::AdoptRef(new TaskHandle::Runner(std::move(task)));
+ task_runner.PostDelayedTask(
+ location,
+ WTF::Bind(&TaskHandle::Runner::Run, runner->AsWeakPtr(),
+ TaskHandle(runner)),
+ delay);
+ return TaskHandle(runner);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/web_task_runner.h b/chromium/third_party/blink/renderer/platform/web_task_runner.h
new file mode 100644
index 00000000000..4b12e2e093a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/web_task_runner.h
@@ -0,0 +1,84 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEB_TASK_RUNNER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEB_TASK_RUNNER_H_
+
+#include <memory>
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/sequenced_task_runner.h"
+#include "third_party/blink/public/platform/web_common.h"
+#include "third_party/blink/renderer/platform/wtf/compiler.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+// TaskHandle is associated to a task posted by PostCancellableTask() or
+// PostCancellableDelayedTask() and cancels the associated task on
+// TaskHandle::cancel() call or on TaskHandle destruction.
+class BLINK_PLATFORM_EXPORT TaskHandle {
+ public:
+ // Returns true if the task will run later. Returns false if the task is
+ // cancelled or the task is run already.
+ // This function is not thread safe. Call this on the thread that has posted
+ // the task.
+ bool IsActive() const;
+
+ // Cancels the task invocation. Do nothing if the task is cancelled or run
+ // already.
+ // This function is not thread safe. Call this on the thread that has posted
+ // the task.
+ void Cancel();
+
+ TaskHandle();
+ ~TaskHandle();
+
+ TaskHandle(TaskHandle&&);
+ TaskHandle& operator=(TaskHandle&&);
+
+ class Runner;
+
+ private:
+ friend BLINK_PLATFORM_EXPORT WARN_UNUSED_RESULT TaskHandle
+ PostCancellableTask(base::SequencedTaskRunner&,
+ const base::Location&,
+ base::OnceClosure);
+ friend BLINK_PLATFORM_EXPORT WARN_UNUSED_RESULT TaskHandle
+ PostDelayedCancellableTask(base::SequencedTaskRunner&,
+ const base::Location&,
+ base::OnceClosure,
+ TimeDelta delay);
+
+ explicit TaskHandle(scoped_refptr<Runner>);
+ scoped_refptr<Runner> runner_;
+};
+
+// For cross-thread posting. Can be called from any thread.
+BLINK_PLATFORM_EXPORT void PostCrossThreadTask(base::SequencedTaskRunner&,
+ const base::Location&,
+ CrossThreadClosure);
+BLINK_PLATFORM_EXPORT void PostDelayedCrossThreadTask(
+ base::SequencedTaskRunner&,
+ const base::Location&,
+ CrossThreadClosure,
+ TimeDelta delay);
+
+// For same-thread cancellable task posting. Returns a TaskHandle object for
+// cancellation.
+BLINK_PLATFORM_EXPORT WARN_UNUSED_RESULT TaskHandle
+PostCancellableTask(base::SequencedTaskRunner&,
+ const base::Location&,
+ base::OnceClosure);
+BLINK_PLATFORM_EXPORT WARN_UNUSED_RESULT TaskHandle
+PostDelayedCancellableTask(base::SequencedTaskRunner&,
+ const base::Location&,
+ base::OnceClosure,
+ TimeDelta delay);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEB_TASK_RUNNER_H_
diff --git a/chromium/third_party/blink/renderer/platform/web_task_runner_test.cc b/chromium/third_party/blink/renderer/platform/web_task_runner_test.cc
new file mode 100644
index 00000000000..c20c21dc9cf
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/web_task_runner_test.cc
@@ -0,0 +1,159 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/web_task_runner.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/scheduler/test/fake_task_runner.h"
+
+namespace blink {
+namespace {
+
+void Increment(int* x) {
+ ++*x;
+}
+
+void GetIsActive(bool* is_active, TaskHandle* handle) {
+ *is_active = handle->IsActive();
+}
+
+class CancellationTestHelper {
+ public:
+ CancellationTestHelper() : weak_ptr_factory_(this) {}
+
+ base::WeakPtr<CancellationTestHelper> GetWeakPtr() {
+ return weak_ptr_factory_.GetWeakPtr();
+ }
+
+ void RevokeWeakPtrs() { weak_ptr_factory_.InvalidateWeakPtrs(); }
+ void IncrementCounter() { ++counter_; }
+ int Counter() const { return counter_; }
+
+ private:
+ int counter_ = 0;
+ base::WeakPtrFactory<CancellationTestHelper> weak_ptr_factory_;
+};
+
+} // namespace
+
+TEST(WebTaskRunnerTest, PostCancellableTaskTest) {
+ scoped_refptr<scheduler::FakeTaskRunner> task_runner =
+ base::MakeRefCounted<scheduler::FakeTaskRunner>();
+
+ // Run without cancellation.
+ int count = 0;
+ TaskHandle handle = PostCancellableTask(
+ *task_runner, FROM_HERE, WTF::Bind(&Increment, WTF::Unretained(&count)));
+ EXPECT_EQ(0, count);
+ EXPECT_TRUE(handle.IsActive());
+ task_runner->RunUntilIdle();
+ EXPECT_EQ(1, count);
+ EXPECT_FALSE(handle.IsActive());
+
+ count = 0;
+ handle = PostDelayedCancellableTask(
+ *task_runner, FROM_HERE, WTF::Bind(&Increment, WTF::Unretained(&count)),
+ TimeDelta::FromMilliseconds(1));
+ EXPECT_EQ(0, count);
+ EXPECT_TRUE(handle.IsActive());
+ task_runner->RunUntilIdle();
+ EXPECT_EQ(1, count);
+ EXPECT_FALSE(handle.IsActive());
+
+ // Cancel a task.
+ count = 0;
+ handle = PostCancellableTask(*task_runner, FROM_HERE,
+ WTF::Bind(&Increment, WTF::Unretained(&count)));
+ handle.Cancel();
+ EXPECT_EQ(0, count);
+ EXPECT_FALSE(handle.IsActive());
+ task_runner->RunUntilIdle();
+ EXPECT_EQ(0, count);
+
+ // The task should be cancelled when the handle is dropped.
+ {
+ count = 0;
+ TaskHandle handle2 =
+ PostCancellableTask(*task_runner, FROM_HERE,
+ WTF::Bind(&Increment, WTF::Unretained(&count)));
+ EXPECT_TRUE(handle2.IsActive());
+ }
+ EXPECT_EQ(0, count);
+ task_runner->RunUntilIdle();
+ EXPECT_EQ(0, count);
+
+ // The task should be cancelled when another TaskHandle is assigned on it.
+ count = 0;
+ handle = PostCancellableTask(*task_runner, FROM_HERE,
+ WTF::Bind(&Increment, WTF::Unretained(&count)));
+ handle = PostCancellableTask(*task_runner, FROM_HERE, WTF::Bind([] {}));
+ EXPECT_EQ(0, count);
+ task_runner->RunUntilIdle();
+ EXPECT_EQ(0, count);
+
+ // Self assign should be nop.
+ count = 0;
+ handle = PostCancellableTask(*task_runner, FROM_HERE,
+ WTF::Bind(&Increment, WTF::Unretained(&count)));
+#if defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wself-move"
+ handle = std::move(handle);
+#pragma GCC diagnostic pop
+#else
+ handle = std::move(handle);
+#endif // defined(__clang__)
+ EXPECT_EQ(0, count);
+ task_runner->RunUntilIdle();
+ EXPECT_EQ(1, count);
+
+ // handle->isActive() should switch to false before the task starts running.
+ bool is_active = false;
+ handle =
+ PostCancellableTask(*task_runner, FROM_HERE,
+ WTF::Bind(&GetIsActive, WTF::Unretained(&is_active),
+ WTF::Unretained(&handle)));
+ EXPECT_TRUE(handle.IsActive());
+ task_runner->RunUntilIdle();
+ EXPECT_FALSE(is_active);
+ EXPECT_FALSE(handle.IsActive());
+}
+
+TEST(WebTaskRunnerTest, CancellationCheckerTest) {
+ scoped_refptr<scheduler::FakeTaskRunner> task_runner =
+ base::MakeRefCounted<scheduler::FakeTaskRunner>();
+
+ int count = 0;
+ TaskHandle handle = PostCancellableTask(
+ *task_runner, FROM_HERE, WTF::Bind(&Increment, WTF::Unretained(&count)));
+ EXPECT_EQ(0, count);
+
+ // TaskHandle::isActive should detect the deletion of posted task.
+ auto queue = task_runner->TakePendingTasksForTesting();
+ ASSERT_EQ(1u, queue.size());
+ EXPECT_FALSE(queue[0].first.IsCancelled());
+ EXPECT_TRUE(handle.IsActive());
+ queue.clear();
+ EXPECT_FALSE(handle.IsActive());
+ EXPECT_EQ(0, count);
+
+ count = 0;
+ CancellationTestHelper helper;
+ handle =
+ PostCancellableTask(*task_runner, FROM_HERE,
+ WTF::Bind(&CancellationTestHelper::IncrementCounter,
+ helper.GetWeakPtr()));
+ EXPECT_EQ(0, helper.Counter());
+
+ // The cancellation of the posted task should be propagated to TaskHandle.
+ queue = task_runner->TakePendingTasksForTesting();
+ ASSERT_EQ(1u, queue.size());
+ EXPECT_FALSE(queue[0].first.IsCancelled());
+ EXPECT_TRUE(handle.IsActive());
+ helper.RevokeWeakPtrs();
+ EXPECT_TRUE(queue[0].first.IsCancelled());
+ EXPECT_FALSE(handle.IsActive());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/web_text_input_info.cc b/chromium/third_party/blink/renderer/platform/web_text_input_info.cc
new file mode 100644
index 00000000000..b1d11491b9a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/web_text_input_info.cc
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2012 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 "third_party/blink/public/platform/web_text_input_info.h"
+
+namespace blink {
+
+bool WebTextInputInfo::Equals(const WebTextInputInfo& other) const {
+ return type == other.type && value == other.value && flags == other.flags &&
+ selection_start == other.selection_start &&
+ selection_end == other.selection_end &&
+ composition_start == other.composition_start &&
+ composition_end == other.composition_end &&
+ input_mode == other.input_mode;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/web_thread.cc b/chromium/third_party/blink/renderer/platform/web_thread.cc
new file mode 100644
index 00000000000..15d74082f25
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/web_thread.cc
@@ -0,0 +1,46 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_thread.h"
+
+#include "base/single_thread_task_runner.h"
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#elif defined(OS_POSIX)
+#include <unistd.h>
+#endif
+
+namespace blink {
+
+WebThreadCreationParams::WebThreadCreationParams(WebThreadType thread_type)
+ : thread_type(thread_type),
+ name(GetNameForThreadType(thread_type)),
+ frame_scheduler(nullptr) {}
+
+WebThreadCreationParams& WebThreadCreationParams::SetThreadNameForTest(
+ const char* thread_name) {
+ name = thread_name;
+ return *this;
+}
+
+WebThreadCreationParams& WebThreadCreationParams::SetFrameScheduler(
+ FrameScheduler* scheduler) {
+ frame_scheduler = scheduler;
+ return *this;
+}
+
+#if defined(OS_WIN)
+static_assert(sizeof(blink::PlatformThreadId) >= sizeof(DWORD),
+ "size of platform thread id is too small");
+#elif defined(OS_POSIX)
+static_assert(sizeof(blink::PlatformThreadId) >= sizeof(pid_t),
+ "size of platform thread id is too small");
+#else
+#error Unexpected platform
+#endif
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/web_thread_supporting_gc.cc b/chromium/third_party/blink/renderer/platform/web_thread_supporting_gc.cc
new file mode 100644
index 00000000000..b7c9540a3cc
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/web_thread_supporting_gc.cc
@@ -0,0 +1,78 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/web_thread_supporting_gc.h"
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/platform/heap/safe_point.h"
+#include "third_party/blink/renderer/platform/memory_coordinator.h"
+#include "third_party/blink/renderer/platform/scheduler/child/web_scheduler.h"
+#include "third_party/blink/renderer/platform/wtf/threading.h"
+
+namespace blink {
+
+std::unique_ptr<WebThreadSupportingGC> WebThreadSupportingGC::Create(
+ const WebThreadCreationParams& params) {
+ return base::WrapUnique(new WebThreadSupportingGC(&params, nullptr));
+}
+
+std::unique_ptr<WebThreadSupportingGC> WebThreadSupportingGC::CreateForThread(
+ WebThread* thread) {
+ return base::WrapUnique(new WebThreadSupportingGC(nullptr, thread));
+}
+
+WebThreadSupportingGC::WebThreadSupportingGC(
+ const WebThreadCreationParams* params,
+ WebThread* thread)
+ : thread_(thread) {
+ DCHECK(IsMainThread());
+ DCHECK(!params || !thread);
+#if DCHECK_IS_ON()
+ // We call this regardless of whether an existing thread is given or not,
+ // as it means that blink is going to run with more than one thread.
+ WTF::WillCreateThread();
+#endif
+ if (!thread_) {
+ // If |thread| is not given, create a new one and own it.
+ // TODO(scheduler-dev): AnimationWorklet can pass nullptr as WebThread*
+ // reference when a test doesn't have a compositor thread.
+ owning_thread_ = Platform::Current()->CreateThread(
+ params ? *params : WebThreadCreationParams(WebThreadType::kTestThread));
+ thread_ = owning_thread_.get();
+ }
+ MemoryCoordinator::RegisterThread(thread_);
+}
+
+WebThreadSupportingGC::~WebThreadSupportingGC() {
+ DCHECK(IsMainThread());
+ // WebThread's destructor blocks until all the tasks are processed.
+ owning_thread_.reset();
+ MemoryCoordinator::UnregisterThread(thread_);
+}
+
+void WebThreadSupportingGC::InitializeOnThread() {
+ DCHECK(thread_->IsCurrentThread());
+ ThreadState::AttachCurrentThread();
+ gc_task_runner_ = std::make_unique<GCTaskRunner>(thread_);
+}
+
+void WebThreadSupportingGC::ShutdownOnThread() {
+ DCHECK(thread_->IsCurrentThread());
+#if defined(LEAK_SANITIZER)
+ ThreadState::Current()->ReleaseStaticPersistentNodes();
+#endif
+ // Ensure no posted tasks will run from this point on.
+ gc_task_runner_.reset();
+
+ // Shutdown the thread (via its scheduler) only when the thread is created
+ // and is owned by this instance.
+ if (owning_thread_)
+ owning_thread_->Scheduler()->Shutdown();
+
+ ThreadState::DetachCurrentThread();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/web_thread_supporting_gc.h b/chromium/third_party/blink/renderer/platform/web_thread_supporting_gc.h
new file mode 100644
index 00000000000..c533dde5da3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/web_thread_supporting_gc.h
@@ -0,0 +1,93 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEB_THREAD_SUPPORTING_GC_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEB_THREAD_SUPPORTING_GC_H_
+
+#include <memory>
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_thread.h"
+#include "third_party/blink/renderer/platform/heap/gc_task_runner.h"
+#include "third_party/blink/renderer/platform/web_task_runner.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+// WebThreadSupportingGC wraps a WebThread and adds support for attaching
+// to and detaching from the Blink GC infrastructure.
+//
+// The initialize method must be called during initialization on the WebThread
+// and before the thread allocates any objects managed by the Blink GC. The
+// shutdown method must be called on the WebThread during shutdown when the
+// thread no longer needs to access objects managed by the Blink GC.
+//
+// WebThreadSupportingGC usually internally creates and owns WebThread unless
+// an existing WebThread is given via createForThread.
+class PLATFORM_EXPORT WebThreadSupportingGC final {
+ USING_FAST_MALLOC(WebThreadSupportingGC);
+ WTF_MAKE_NONCOPYABLE(WebThreadSupportingGC);
+
+ public:
+ static std::unique_ptr<WebThreadSupportingGC> Create(
+ const WebThreadCreationParams&);
+ static std::unique_ptr<WebThreadSupportingGC> CreateForThread(WebThread*);
+ ~WebThreadSupportingGC();
+
+ void PostTask(const base::Location& location, base::OnceClosure task) {
+ thread_->GetTaskRunner()->PostTask(location, std::move(task));
+ }
+
+ void PostDelayedTask(const base::Location& location,
+ base::OnceClosure task,
+ TimeDelta delay) {
+ thread_->GetTaskRunner()->PostDelayedTask(location, std::move(task), delay);
+ }
+
+ void PostTask(const base::Location& location, CrossThreadClosure task) {
+ PostCrossThreadTask(*thread_->GetTaskRunner(), location, std::move(task));
+ }
+
+ void PostDelayedTask(const base::Location& location,
+ CrossThreadClosure task,
+ TimeDelta delay) {
+ PostDelayedCrossThreadTask(*thread_->GetTaskRunner(), location,
+ std::move(task), delay);
+ }
+
+ bool IsCurrentThread() const { return thread_->IsCurrentThread(); }
+
+ void AddTaskObserver(WebThread::TaskObserver* observer) {
+ thread_->AddTaskObserver(observer);
+ }
+
+ void RemoveTaskObserver(WebThread::TaskObserver* observer) {
+ thread_->RemoveTaskObserver(observer);
+ }
+
+ // Must be called on the WebThread.
+ void InitializeOnThread();
+ void ShutdownOnThread();
+
+ WebThread& PlatformThread() const {
+ DCHECK(thread_);
+ return *thread_;
+ }
+
+ private:
+ WebThreadSupportingGC(const WebThreadCreationParams*, WebThread*);
+
+ std::unique_ptr<GCTaskRunner> gc_task_runner_;
+
+ // m_thread is guaranteed to be non-null after this instance is constructed.
+ // m_owningThread is non-null unless this instance is constructed for an
+ // existing thread via createForThread().
+ WebThread* thread_ = nullptr;
+ std::unique_ptr<WebThread> owning_thread_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/web_thread_type.cc b/chromium/third_party/blink/renderer/platform/web_thread_type.cc
new file mode 100644
index 00000000000..f767f455df9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/web_thread_type.cc
@@ -0,0 +1,53 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_thread_type.h"
+
+#include "base/logging.h"
+
+namespace blink {
+
+const char* GetNameForThreadType(WebThreadType thread_type) {
+ switch (thread_type) {
+ case WebThreadType::kMainThread:
+ return "Main thread";
+ case WebThreadType::kUnspecifiedWorkerThread:
+ return "unspecified worker thread";
+ case WebThreadType::kCompositorThread:
+ // Some benchmarks depend on this value.
+ return "Compositor";
+ case WebThreadType::kDedicatedWorkerThread:
+ return "DedicatedWorker thread";
+ case WebThreadType::kSharedWorkerThread:
+ return "SharedWorker thread";
+ case WebThreadType::kAnimationWorkletThread:
+ return "AnimationWorklet thread";
+ case WebThreadType::kServiceWorkerThread:
+ return "ServiceWorker thread";
+ case WebThreadType::kAudioWorkletThread:
+ return "AudioWorklet thread";
+ case WebThreadType::kFileThread:
+ return "File thread";
+ case WebThreadType::kDatabaseThread:
+ return "Database thread";
+ case WebThreadType::kWebAudioThread:
+ return "WebAudio thread";
+ case WebThreadType::kScriptStreamerThread:
+ return "ScriptStreamer thread";
+ case WebThreadType::kOfflineAudioRenderThread:
+ return "OfflineAudioRender thread";
+ case WebThreadType::kReverbConvolutionBackgroundThread:
+ return "Reverb convolution background thread";
+ case WebThreadType::kHRTFDatabaseLoaderThread:
+ return "HRTF database loader thread";
+ case WebThreadType::kTestThread:
+ return "test thread";
+ case WebThreadType::kCount:
+ NOTREACHED();
+ return nullptr;
+ }
+ return nullptr;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/web_touch_event.cc b/chromium/third_party/blink/renderer/platform/web_touch_event.cc
new file mode 100644
index 00000000000..4f6bf26e1c7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/web_touch_event.cc
@@ -0,0 +1,39 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_touch_event.h"
+
+namespace blink {
+
+WebTouchEvent WebTouchEvent::FlattenTransform() const {
+ WebTouchEvent transformed_event = *this;
+ for (unsigned i = 0; i < touches_length; ++i) {
+ transformed_event.touches[i] = TouchPointInRootFrame(i);
+ }
+ transformed_event.frame_translate_.x = 0;
+ transformed_event.frame_translate_.y = 0;
+ transformed_event.frame_scale_ = 1;
+
+ return transformed_event;
+}
+
+WebTouchPoint WebTouchEvent::TouchPointInRootFrame(unsigned point) const {
+ DCHECK_LT(point, touches_length);
+ if (point >= touches_length)
+ return WebTouchPoint();
+
+ WebTouchPoint transformed_point = touches[point];
+ transformed_point.radius_x /= frame_scale_;
+ transformed_point.radius_y /= frame_scale_;
+ transformed_point.movement_x /= frame_scale_;
+ transformed_point.movement_y /= frame_scale_;
+ transformed_point.SetPositionInWidget(
+ (transformed_point.PositionInWidget().x / frame_scale_) +
+ frame_translate_.x,
+ (transformed_point.PositionInWidget().y / frame_scale_) +
+ frame_translate_.y);
+ return transformed_point;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/web_vector_test.cc b/chromium/third_party/blink/renderer/platform/web_vector_test.cc
new file mode 100644
index 00000000000..c90b0fdaf11
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/web_vector_test.cc
@@ -0,0 +1,195 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_vector.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+TEST(WebVectorTest, Iterators) {
+ Vector<int> input;
+ for (int i = 0; i < 5; ++i)
+ input.push_back(i);
+
+ WebVector<int> web_vector(input);
+ const WebVector<int>& const_web_vector = web_vector;
+ Vector<int> output;
+
+ ASSERT_EQ(input.size(), web_vector.size());
+
+ // Use begin()/end() iterators directly.
+ for (WebVector<int>::iterator it = web_vector.begin(); it != web_vector.end();
+ ++it)
+ output.push_back(*it);
+ ASSERT_EQ(input.size(), output.size());
+ for (size_t i = 0; i < input.size(); ++i)
+ EXPECT_EQ(input[i], output[i]);
+
+ // Use begin()/end() const_iterators directly.
+ output.clear();
+ for (WebVector<int>::const_iterator it = const_web_vector.begin();
+ it != const_web_vector.end(); ++it)
+ output.push_back(*it);
+ ASSERT_EQ(input.size(), output.size());
+ for (size_t i = 0; i < input.size(); ++i)
+ EXPECT_EQ(input[i], output[i]);
+
+ // Use range-based for loop.
+ output.clear();
+ for (int x : web_vector)
+ output.push_back(x);
+ ASSERT_EQ(input.size(), output.size());
+ for (size_t i = 0; i < input.size(); ++i)
+ EXPECT_EQ(input[i], output[i]);
+}
+
+TEST(WebVectorTest, IsEmpty) {
+ WebVector<int> vector;
+ ASSERT_TRUE(vector.IsEmpty());
+ int value = 1;
+ vector.Assign(&value, 1);
+ ASSERT_EQ(1u, vector.size());
+ ASSERT_FALSE(vector.IsEmpty());
+}
+
+TEST(WebVectorTest, Swap) {
+ const int kFirstData[] = {1, 2, 3, 4, 5};
+ const int kSecondData[] = {6, 5, 8};
+ const size_t kFirstDataLength = WTF_ARRAY_LENGTH(kFirstData);
+ const size_t kSecondDataLength = WTF_ARRAY_LENGTH(kSecondData);
+
+ WebVector<int> first(kFirstData, kFirstDataLength);
+ WebVector<int> second(kSecondData, kSecondDataLength);
+ ASSERT_EQ(kFirstDataLength, first.size());
+ ASSERT_EQ(kSecondDataLength, second.size());
+ first.Swap(second);
+ ASSERT_EQ(kSecondDataLength, first.size());
+ ASSERT_EQ(kFirstDataLength, second.size());
+ for (size_t i = 0; i < first.size(); ++i)
+ EXPECT_EQ(kSecondData[i], first[i]);
+ for (size_t i = 0; i < second.size(); ++i)
+ EXPECT_EQ(kFirstData[i], second[i]);
+}
+
+TEST(WebVectorTest, CreateFromPointer) {
+ const int kValues[] = {1, 2, 3, 4, 5};
+
+ WebVector<int> vector(kValues, 3);
+ ASSERT_EQ(3u, vector.size());
+ ASSERT_EQ(1, vector[0]);
+ ASSERT_EQ(2, vector[1]);
+ ASSERT_EQ(3, vector[2]);
+}
+
+TEST(WebVectorTest, CreateFromWtfVector) {
+ Vector<int> input;
+ for (int i = 0; i < 5; ++i)
+ input.push_back(i);
+
+ WebVector<int> vector(input);
+ ASSERT_EQ(input.size(), vector.size());
+ for (size_t i = 0; i < vector.size(); ++i)
+ EXPECT_EQ(input[i], vector[i]);
+
+ WebVector<int> copy(input);
+ ASSERT_EQ(input.size(), copy.size());
+ for (size_t i = 0; i < copy.size(); ++i)
+ EXPECT_EQ(input[i], copy[i]);
+
+ WebVector<int> assigned;
+ assigned = copy;
+ ASSERT_EQ(input.size(), assigned.size());
+ for (size_t i = 0; i < assigned.size(); ++i)
+ EXPECT_EQ(input[i], assigned[i]);
+}
+
+TEST(WebVectorTest, CreateFromStdVector) {
+ std::vector<int> input;
+ for (int i = 0; i < 5; ++i)
+ input.push_back(i);
+
+ WebVector<int> vector(input);
+ ASSERT_EQ(input.size(), vector.size());
+ for (size_t i = 0; i < vector.size(); ++i)
+ EXPECT_EQ(input[i], vector[i]);
+
+ WebVector<int> assigned;
+ assigned = input;
+ ASSERT_EQ(input.size(), assigned.size());
+ for (size_t i = 0; i < assigned.size(); ++i)
+ EXPECT_EQ(input[i], assigned[i]);
+}
+
+TEST(WebVectorTest, Reserve) {
+ WebVector<int> vector;
+ vector.reserve(10);
+
+ EXPECT_EQ(10U, vector.capacity());
+}
+
+TEST(WebVectorTest, EmplaceBackArgumentForwarding) {
+ WebVector<WebString> vector;
+ vector.reserve(1);
+ WebUChar buffer[] = {'H', 'e', 'l', 'l', 'o', ' ', 'b', 'l', 'i', 'n', 'k'};
+ vector.emplace_back(buffer, WTF_ARRAY_LENGTH(buffer));
+ ASSERT_EQ(1U, vector.size());
+ EXPECT_EQ(WebString(buffer, WTF_ARRAY_LENGTH(buffer)), vector[0]);
+}
+
+TEST(WebVectorTest, EmplaceBackElementPlacement) {
+ WebVector<int> vector;
+ vector.reserve(10);
+ for (int i = 0; i < 10; ++i)
+ vector.emplace_back(i);
+ ASSERT_EQ(10U, vector.size());
+ for (int i = 0; i < 10; ++i)
+ EXPECT_EQ(i, vector[i]);
+}
+
+TEST(WebVectorTest, ResizeToSameSize) {
+ WebVector<int> vector;
+ vector.reserve(10);
+ for (int i = 0; i < 10; ++i)
+ vector.emplace_back(i);
+ vector.resize(10);
+ ASSERT_EQ(10U, vector.size());
+ for (int i = 0; i < 10; ++i)
+ EXPECT_EQ(i, vector[i]);
+}
+
+TEST(WebVectorTest, ResizeShrink) {
+ WebVector<int> vector;
+ vector.reserve(10);
+ for (int i = 0; i < 10; ++i)
+ vector.emplace_back(i);
+ vector.resize(5);
+ ASSERT_EQ(5U, vector.size());
+ for (int i = 0; i < 5; ++i)
+ EXPECT_EQ(i, vector[i]);
+}
+
+namespace {
+
+// Used to ensure that WebVector supports types without a default constructor.
+struct NoDefaultConstructor {
+ NoDefaultConstructor(int data) : data(data) {}
+
+ int data;
+};
+
+} // anonymous namespace
+
+TEST(WebVectorTest, NoDefaultConstructor) {
+ WebVector<NoDefaultConstructor> vector;
+ vector.reserve(1);
+ vector.emplace_back(42);
+ ASSERT_EQ(1U, vector.size());
+ EXPECT_EQ(42, vector[0].data);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/weborigin/DEPS b/chromium/third_party/blink/renderer/platform/weborigin/DEPS
new file mode 100644
index 00000000000..41871a218f9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/weborigin/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+ # net/ includes should be allowed only in a limited set of directories,
+ # so we have separate DEPS from platform's one.
+ "+net/base",
+]
diff --git a/chromium/third_party/blink/renderer/platform/weborigin/OWNERS b/chromium/third_party/blink/renderer/platform/weborigin/OWNERS
new file mode 100644
index 00000000000..306592df252
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/weborigin/OWNERS
@@ -0,0 +1,5 @@
+darin@chromium.org
+tsepez@chromium.org
+
+# TEAM: platform-architecture-dev@chromium.org
+# COMPONENT: Blink>Internals
diff --git a/chromium/third_party/blink/renderer/platform/weborigin/known_ports.cc b/chromium/third_party/blink/renderer/platform/weborigin/known_ports.cc
new file mode 100644
index 00000000000..8a459fffe26
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/weborigin/known_ports.cc
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2004, 2007, 2008, 2011, 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2012 Research In Motion Limited. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/weborigin/known_ports.h"
+
+#include "net/base/port_util.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+bool IsDefaultPortForProtocol(unsigned short port,
+ const WTF::String& protocol) {
+ if (protocol.IsEmpty())
+ return false;
+
+ switch (port) {
+ case 80:
+ return protocol == "http" || protocol == "ws";
+ case 443:
+ return protocol == "https" || protocol == "wss";
+ case 21:
+ return protocol == "ftp";
+ case 990:
+ return protocol == "ftps";
+ }
+ return false;
+}
+
+unsigned short DefaultPortForProtocol(const WTF::String& protocol) {
+ if (protocol == "http" || protocol == "ws")
+ return 80;
+ if (protocol == "https" || protocol == "wss")
+ return 443;
+ if (protocol == "ftp")
+ return 21;
+ if (protocol == "ftps")
+ return 990;
+
+ return 0;
+}
+
+bool IsPortAllowedForScheme(const KURL& url) {
+ // Returns true for URLs without a port specified. This is needed to let
+ // through non-network schemes that don't go over the network.
+ if (!url.HasPort())
+ return true;
+ String protocol = url.Protocol();
+ if (protocol.IsNull())
+ protocol = g_empty_string;
+ unsigned short effective_port = url.Port();
+ if (!effective_port)
+ effective_port = DefaultPortForProtocol(protocol);
+ StringUTF8Adaptor utf8(protocol);
+ return net::IsPortAllowedForScheme(effective_port,
+ std::string(utf8.Data(), utf8.length()));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/weborigin/known_ports.h b/chromium/third_party/blink/renderer/platform/weborigin/known_ports.h
new file mode 100644
index 00000000000..257ff727e2c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/weborigin/known_ports.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2011, 2012 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_KNOWN_PORTS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_KNOWN_PORTS_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class KURL;
+
+// Returns true if |port| is known to be the default for |protocol|. |protocol|
+// must be lower case.
+PLATFORM_EXPORT bool IsDefaultPortForProtocol(unsigned short port,
+ const WTF::String& protocol);
+
+// Returns 0 for unknown protocols. |protocol| must be lower case.
+PLATFORM_EXPORT unsigned short DefaultPortForProtocol(
+ const WTF::String& protocol);
+
+// Returns true if the port of the |url| is allowed for the scheme of the |url|.
+PLATFORM_EXPORT bool IsPortAllowedForScheme(const KURL&);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_KNOWN_PORTS_H_
diff --git a/chromium/third_party/blink/renderer/platform/weborigin/known_ports_test.cc b/chromium/third_party/blink/renderer/platform/weborigin/known_ports_test.cc
new file mode 100644
index 00000000000..aaa9be0fd0a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/weborigin/known_ports_test.cc
@@ -0,0 +1,90 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/weborigin/known_ports.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+TEST(KnownPortsTest, IsDefaultPortForProtocol) {
+ struct TestCase {
+ const unsigned short port;
+ const char* protocol;
+ const bool is_known;
+ } inputs[] = {
+ // Known ones.
+ {80, "http", true},
+ {443, "https", true},
+ {80, "ws", true},
+ {443, "wss", true},
+ {21, "ftp", true},
+ {990, "ftps", true},
+
+ // Unknown ones.
+ {5, "foo", false},
+ {80, "http:", false},
+ {443, "http", false},
+ {21, "ftps", false},
+ {990, "ftp", false},
+
+ // With upper cases.
+ {80, "HTTP", false},
+ {443, "Https", false},
+ };
+
+ for (const TestCase& test : inputs) {
+ bool result = IsDefaultPortForProtocol(test.port, test.protocol);
+ EXPECT_EQ(test.is_known, result);
+ }
+}
+
+TEST(KnownPortsTest, DefaultPortForProtocol) {
+ struct TestCase {
+ const unsigned short port;
+ const char* protocol;
+ } inputs[] = {
+ // Known ones.
+ {80, "http"},
+ {443, "https"},
+ {80, "ws"},
+ {443, "wss"},
+ {21, "ftp"},
+ {990, "ftps"},
+
+ // Unknown ones.
+ {0, "foo"},
+ {0, "http:"},
+ {0, "HTTP"},
+ {0, "Https"},
+ };
+
+ for (const TestCase& test : inputs)
+ EXPECT_EQ(test.port, DefaultPortForProtocol(test.protocol));
+}
+
+TEST(KnownPortsTest, IsPortAllowedForScheme) {
+ struct TestCase {
+ const char* url;
+ const bool is_allowed;
+ } inputs[] = {
+ // Allowed ones.
+ {"http://example.com", true},
+ {"file://example.com", true},
+ {"file://example.com:87", true},
+ {"ftp://example.com:21", true},
+ {"http://example.com:80", true},
+ {"http://example.com:8889", true},
+
+ // Disallowed ones.
+ {"ftp://example.com:87", false},
+ {"ws://example.com:21", false},
+ };
+
+ for (const TestCase& test : inputs)
+ EXPECT_EQ(test.is_allowed, IsPortAllowedForScheme(KURL(test.url)));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/weborigin/kurl.cc b/chromium/third_party/blink/renderer/platform/weborigin/kurl.cc
new file mode 100644
index 00000000000..09d31ec610b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/weborigin/kurl.cc
@@ -0,0 +1,886 @@
+/*
+ * Copyright (C) 2004, 2007, 2008, 2011, 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2012 Research In Motion Limited. All rights reserved.
+ * Copyright (C) 2008, 2009, 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/weborigin/kurl.h"
+
+#include <algorithm>
+
+#include "third_party/blink/renderer/platform/weborigin/known_ports.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_statics.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
+#include "url/gurl.h"
+#include "url/url_util.h"
+#ifndef NDEBUG
+#include <stdio.h>
+#endif
+
+namespace blink {
+
+static const int kMaximumValidPortNumber = 0xFFFE;
+static const int kInvalidPortNumber = 0xFFFF;
+
+#if DCHECK_IS_ON()
+static void AssertProtocolIsGood(const StringView protocol) {
+ DCHECK(protocol != "");
+ for (size_t i = 0; i < protocol.length(); ++i) {
+ LChar c = protocol.Characters8()[i];
+ DCHECK(c > ' ' && c < 0x7F && !(c >= 'A' && c <= 'Z'));
+ }
+}
+#endif
+
+// Note: You must ensure that |spec| is a valid canonicalized URL before calling
+// this function.
+static const char* AsURLChar8Subtle(const String& spec) {
+ DCHECK(spec.Is8Bit());
+ // characters8 really return characters in Latin-1, but because we
+ // canonicalize URL strings, we know that everything before the fragment
+ // identifier will actually be ASCII, which means this cast is safe as long as
+ // you don't look at the fragment component.
+ return reinterpret_cast<const char*>(spec.Characters8());
+}
+
+// Returns the characters for the given string, or a pointer to a static empty
+// string if the input string is null. This will always ensure we have a non-
+// null character pointer since ReplaceComponents has special meaning for null.
+static const char* CharactersOrEmpty(const StringUTF8Adaptor& string) {
+ static const char kZero = 0;
+ return string.Data() ? string.Data() : &kZero;
+}
+
+static bool IsSchemeFirstChar(char c) {
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
+}
+
+static bool IsSchemeChar(char c) {
+ return IsSchemeFirstChar(c) || (c >= '0' && c <= '9') || c == '.' ||
+ c == '-' || c == '+';
+}
+
+static bool IsUnicodeEncoding(const WTF::TextEncoding* encoding) {
+ return encoding->EncodingForFormSubmission() == UTF8Encoding();
+}
+
+namespace {
+
+class KURLCharsetConverter final : public url::CharsetConverter {
+ DISALLOW_NEW();
+
+ public:
+ // The encoding parameter may be 0, but in this case the object must not be
+ // called.
+ explicit KURLCharsetConverter(const WTF::TextEncoding* encoding)
+ : encoding_(encoding) {}
+
+ void ConvertFromUTF16(const base::char16* input,
+ int input_length,
+ url::CanonOutput* output) override {
+ CString encoded = encoding_->Encode(
+ String(input, input_length), WTF::kURLEncodedEntitiesForUnencodables);
+ output->Append(encoded.data(), static_cast<int>(encoded.length()));
+ }
+
+ private:
+ const WTF::TextEncoding* encoding_;
+};
+
+} // namespace
+
+bool IsValidProtocol(const String& protocol) {
+ // RFC3986: ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
+ if (protocol.IsEmpty())
+ return false;
+ if (!IsSchemeFirstChar(protocol[0]))
+ return false;
+ unsigned protocol_length = protocol.length();
+ for (unsigned i = 1; i < protocol_length; i++) {
+ if (!IsSchemeChar(protocol[i]))
+ return false;
+ }
+ return true;
+}
+
+void KURL::Initialize() {
+ // This must be called before we create other threads to
+ // avoid racy static local initialization.
+ BlankURL();
+}
+
+String KURL::StrippedForUseAsReferrer() const {
+ if (!ProtocolIsInHTTPFamily())
+ return String();
+
+ if (parsed_.username.is_nonempty() || parsed_.password.is_nonempty() ||
+ parsed_.ref.is_valid()) {
+ KURL referrer(*this);
+ referrer.SetUser(String());
+ referrer.SetPass(String());
+ referrer.RemoveFragmentIdentifier();
+ return referrer.GetString();
+ }
+ return GetString();
+}
+
+String KURL::StrippedForUseAsHref() const {
+ if (parsed_.username.is_nonempty() || parsed_.password.is_nonempty()) {
+ KURL href(*this);
+ href.SetUser(String());
+ href.SetPass(String());
+ return href.GetString();
+ }
+ return GetString();
+}
+
+bool KURL::IsLocalFile() const {
+ // Including feed here might be a bad idea since drag and drop uses this check
+ // and including feed would allow feeds to potentially let someone's blog
+ // read the contents of the clipboard on a drag, even without a drop.
+ // Likewise with using the FrameLoader::shouldTreatURLAsLocal() function.
+ return ProtocolIs("file");
+}
+
+bool ProtocolIsJavaScript(const String& url) {
+ return ProtocolIs(url, "javascript");
+}
+
+const KURL& BlankURL() {
+ DEFINE_STATIC_LOCAL(KURL, static_blank_url, ("about:blank"));
+ return static_blank_url;
+}
+
+bool KURL::IsAboutBlankURL() const {
+ return *this == BlankURL();
+}
+
+const KURL& SrcdocURL() {
+ DEFINE_STATIC_LOCAL(KURL, static_srcdoc_url, ("about:srcdoc"));
+ return static_srcdoc_url;
+}
+
+bool KURL::IsAboutSrcdocURL() const {
+ return *this == SrcdocURL();
+}
+
+const KURL& NullURL() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(KURL, static_null_url, ());
+ return static_null_url;
+}
+
+String KURL::ElidedString() const {
+ if (GetString().length() <= 1024)
+ return GetString();
+
+ return GetString().Left(511) + "..." + GetString().Right(510);
+}
+
+KURL::KURL() : is_valid_(false), protocol_is_in_http_family_(false) {}
+
+// Initializes with a string representing an absolute URL. No encoding
+// information is specified. This generally happens when a KURL is converted
+// to a string and then converted back. In this case, the URL is already
+// canonical and in proper escaped form so needs no encoding. We treat it as
+// UTF-8 just in case.
+KURL::KURL(const String& url) {
+ if (!url.IsNull())
+ Init(NullURL(), url, nullptr);
+ else {
+ // WebCore expects us to preserve the nullness of strings when this
+ // constructor is used. In all other cases, it expects a non-null
+ // empty string, which is what init() will create.
+ is_valid_ = false;
+ protocol_is_in_http_family_ = false;
+ }
+}
+
+KURL KURL::CreateIsolated(const String& url) {
+ // FIXME: We should be able to skip this extra copy and created an
+ // isolated KURL more efficiently.
+ return KURL(url).Copy();
+}
+
+// Constructs a new URL given a base URL and a possibly relative input URL.
+// This assumes UTF-8 encoding.
+KURL::KURL(const KURL& base, const String& relative) {
+ Init(base, relative, nullptr);
+}
+
+// Constructs a new URL given a base URL and a possibly relative input URL.
+// Any query portion of the relative URL will be encoded in the given encoding.
+KURL::KURL(const KURL& base,
+ const String& relative,
+ const WTF::TextEncoding& encoding) {
+ Init(base, relative, &encoding.EncodingForFormSubmission());
+}
+
+KURL::KURL(const AtomicString& canonical_string,
+ const url::Parsed& parsed,
+ bool is_valid)
+ : is_valid_(is_valid),
+ protocol_is_in_http_family_(false),
+ parsed_(parsed),
+ string_(canonical_string) {
+ InitProtocolMetadata();
+ InitInnerURL();
+}
+
+KURL::KURL(const KURL& other)
+ : is_valid_(other.is_valid_),
+ protocol_is_in_http_family_(other.protocol_is_in_http_family_),
+ protocol_(other.protocol_),
+ parsed_(other.parsed_),
+ string_(other.string_) {
+ if (other.inner_url_.get())
+ inner_url_ = std::make_unique<KURL>(other.inner_url_->Copy());
+}
+
+KURL::~KURL() = default;
+
+KURL& KURL::operator=(const KURL& other) {
+ is_valid_ = other.is_valid_;
+ protocol_is_in_http_family_ = other.protocol_is_in_http_family_;
+ protocol_ = other.protocol_;
+ parsed_ = other.parsed_;
+ string_ = other.string_;
+ if (other.inner_url_)
+ inner_url_ = std::make_unique<KURL>(other.inner_url_->Copy());
+ else
+ inner_url_.reset();
+ return *this;
+}
+
+KURL KURL::Copy() const {
+ KURL result;
+ result.is_valid_ = is_valid_;
+ result.protocol_is_in_http_family_ = protocol_is_in_http_family_;
+ result.protocol_ = protocol_.IsolatedCopy();
+ result.parsed_ = parsed_;
+ result.string_ = string_.IsolatedCopy();
+ if (inner_url_)
+ result.inner_url_ = std::make_unique<KURL>(inner_url_->Copy());
+ return result;
+}
+
+bool KURL::IsNull() const {
+ return string_.IsNull();
+}
+
+bool KURL::IsEmpty() const {
+ return string_.IsEmpty();
+}
+
+bool KURL::IsValid() const {
+ return is_valid_;
+}
+
+bool KURL::HasPort() const {
+ return HostEnd() < PathStart();
+}
+
+bool KURL::ProtocolIsJavaScript() const {
+ return ComponentStringView(parsed_.scheme) == "javascript";
+}
+
+bool KURL::ProtocolIsInHTTPFamily() const {
+ return protocol_is_in_http_family_;
+}
+
+bool KURL::HasPath() const {
+ // Note that http://www.google.com/" has a path, the path is "/". This can
+ // return false only for invalid or nonstandard URLs.
+ return parsed_.path.len >= 0;
+}
+
+String KURL::LastPathComponent() const {
+ if (!is_valid_)
+ return StringViewForInvalidComponent().ToString();
+ DCHECK(!string_.IsNull());
+
+ // When the output ends in a slash, WebCore has different expectations than
+ // the GoogleURL library. For "/foo/bar/" the library will return the empty
+ // string, but WebCore wants "bar".
+ url::Component path = parsed_.path;
+ if (path.len > 0 && string_[path.end() - 1] == '/')
+ path.len--;
+
+ url::Component file;
+ if (string_.Is8Bit())
+ url::ExtractFileName(AsURLChar8Subtle(string_), path, &file);
+ else
+ url::ExtractFileName(string_.Characters16(), path, &file);
+
+ // Bug: https://bugs.webkit.org/show_bug.cgi?id=21015 this function returns
+ // a null string when the path is empty, which we duplicate here.
+ if (!file.is_nonempty())
+ return String();
+ return ComponentString(file);
+}
+
+String KURL::Protocol() const {
+ DCHECK_EQ(ComponentString(parsed_.scheme), protocol_);
+ return protocol_;
+}
+
+String KURL::Host() const {
+ return ComponentString(parsed_.host);
+}
+
+// Returns 0 when there is no port.
+//
+// We treat URL's with out-of-range port numbers as invalid URLs, and they will
+// be rejected by the canonicalizer. KURL.cpp will allow them in parsing, but
+// return invalidPortNumber from this port() function, so we mirror that
+// behavior here.
+unsigned short KURL::Port() const {
+ if (!is_valid_ || parsed_.port.len <= 0)
+ return 0;
+ DCHECK(!string_.IsNull());
+ int port = string_.Is8Bit()
+ ? url::ParsePort(AsURLChar8Subtle(string_), parsed_.port)
+ : url::ParsePort(string_.Characters16(), parsed_.port);
+ DCHECK_NE(port, url::PORT_UNSPECIFIED); // Checked port.len <= 0 before.
+
+ if (port == url::PORT_INVALID ||
+ port > kMaximumValidPortNumber) // Mimic KURL::port()
+ port = kInvalidPortNumber;
+
+ return static_cast<unsigned short>(port);
+}
+
+// TODO(csharrison): Migrate pass() and user() to return a StringView. Most
+// consumers just need to know if the string is empty.
+
+String KURL::Pass() const {
+ // Bug: https://bugs.webkit.org/show_bug.cgi?id=21015 this function returns
+ // a null string when the password is empty, which we duplicate here.
+ if (!parsed_.password.is_nonempty())
+ return String();
+ return ComponentString(parsed_.password);
+}
+
+String KURL::User() const {
+ return ComponentString(parsed_.username);
+}
+
+String KURL::FragmentIdentifier() const {
+ // Empty but present refs ("foo.com/bar#") should result in the empty
+ // string, which componentString will produce. Nonexistent refs
+ // should be the null string.
+ if (!parsed_.ref.is_valid())
+ return String();
+ return ComponentString(parsed_.ref);
+}
+
+bool KURL::HasFragmentIdentifier() const {
+ return parsed_.ref.len >= 0;
+}
+
+String KURL::BaseAsString() const {
+ // FIXME: There is probably a more efficient way to do this?
+ return string_.Left(PathAfterLastSlash());
+}
+
+String KURL::Query() const {
+ if (parsed_.query.len >= 0)
+ return ComponentString(parsed_.query);
+
+ // Bug: https://bugs.webkit.org/show_bug.cgi?id=21015 this function returns
+ // an empty string when the query is empty rather than a null (not sure
+ // which is right).
+ // Returns a null if the query is not specified, instead of empty.
+ if (parsed_.query.is_valid())
+ return g_empty_string;
+ return String();
+}
+
+String KURL::GetPath() const {
+ return ComponentString(parsed_.path);
+}
+
+bool KURL::SetProtocol(const String& protocol) {
+ // Firefox and IE remove everything after the first ':'.
+ int separator_position = protocol.find(':');
+ String new_protocol = protocol.Substring(0, separator_position);
+ StringUTF8Adaptor new_protocol_utf8(new_protocol);
+
+ // If KURL is given an invalid scheme, it returns failure without modifying
+ // the URL at all. This is in contrast to most other setters which modify
+ // the URL and set "m_isValid."
+ url::RawCanonOutputT<char> canon_protocol;
+ url::Component protocol_component;
+ if (!url::CanonicalizeScheme(new_protocol_utf8.Data(),
+ url::Component(0, new_protocol_utf8.length()),
+ &canon_protocol, &protocol_component) ||
+ !protocol_component.is_nonempty())
+ return false;
+
+ url::Replacements<char> replacements;
+ replacements.SetScheme(CharactersOrEmpty(new_protocol_utf8),
+ url::Component(0, new_protocol_utf8.length()));
+ ReplaceComponents(replacements);
+
+ // isValid could be false but we still return true here. This is because
+ // WebCore or JS scripts can build up a URL by setting individual
+ // components, and a JS exception is based on the return value of this
+ // function. We want to throw the exception and stop the script only when
+ // its trying to set a bad protocol, and not when it maybe just hasn't
+ // finished building up its final scheme.
+ return true;
+}
+
+void KURL::SetHost(const String& host) {
+ StringUTF8Adaptor host_utf8(host);
+ url::Replacements<char> replacements;
+ replacements.SetHost(CharactersOrEmpty(host_utf8),
+ url::Component(0, host_utf8.length()));
+ ReplaceComponents(replacements);
+}
+
+static String ParsePortFromStringPosition(const String& value,
+ unsigned port_start) {
+ // "008080junk" needs to be treated as port "8080" and "000" as "0".
+ size_t length = value.length();
+ unsigned port_end = port_start;
+ while (IsASCIIDigit(value[port_end]) && port_end < length)
+ ++port_end;
+ while (value[port_start] == '0' && port_start < port_end - 1)
+ ++port_start;
+
+ // Required for backwards compat.
+ // https://www.w3.org/Bugs/Public/show_bug.cgi?id=23463
+ if (port_start == port_end)
+ return "0";
+
+ return value.Substring(port_start, port_end - port_start);
+}
+
+void KURL::SetHostAndPort(const String& host_and_port) {
+ size_t separator = host_and_port.find(':');
+ if (!separator)
+ return;
+
+ if (separator == kNotFound) {
+ url::Replacements<char> replacements;
+ StringUTF8Adaptor host_utf8(host_and_port);
+ replacements.SetHost(CharactersOrEmpty(host_utf8),
+ url::Component(0, host_utf8.length()));
+ ReplaceComponents(replacements);
+ return;
+ }
+
+ String host = host_and_port.Substring(0, separator);
+ String port = ParsePortFromStringPosition(host_and_port, separator + 1);
+
+ StringUTF8Adaptor host_utf8(host);
+ StringUTF8Adaptor port_utf8(port);
+
+ url::Replacements<char> replacements;
+ replacements.SetHost(CharactersOrEmpty(host_utf8),
+ url::Component(0, host_utf8.length()));
+ replacements.SetPort(CharactersOrEmpty(port_utf8),
+ url::Component(0, port_utf8.length()));
+ ReplaceComponents(replacements);
+}
+
+void KURL::RemovePort() {
+ if (!HasPort())
+ return;
+ url::Replacements<char> replacements;
+ replacements.ClearPort();
+ ReplaceComponents(replacements);
+}
+
+void KURL::SetPort(const String& port) {
+ String parsed_port = ParsePortFromStringPosition(port, 0);
+ SetPort(parsed_port.ToUInt());
+}
+
+void KURL::SetPort(unsigned short port) {
+ if (IsDefaultPortForProtocol(port, Protocol())) {
+ RemovePort();
+ return;
+ }
+
+ String port_string = String::Number(port);
+ DCHECK(port_string.Is8Bit());
+
+ url::Replacements<char> replacements;
+ replacements.SetPort(reinterpret_cast<const char*>(port_string.Characters8()),
+ url::Component(0, port_string.length()));
+ ReplaceComponents(replacements);
+}
+
+void KURL::SetUser(const String& user) {
+ // This function is commonly called to clear the username, which we
+ // normally don't have, so we optimize this case.
+ if (user.IsEmpty() && !parsed_.username.is_valid())
+ return;
+
+ // The canonicalizer will clear any usernames that are empty, so we
+ // don't have to explicitly call ClearUsername() here.
+ StringUTF8Adaptor user_utf8(user);
+ url::Replacements<char> replacements;
+ replacements.SetUsername(CharactersOrEmpty(user_utf8),
+ url::Component(0, user_utf8.length()));
+ ReplaceComponents(replacements);
+}
+
+void KURL::SetPass(const String& pass) {
+ // This function is commonly called to clear the password, which we
+ // normally don't have, so we optimize this case.
+ if (pass.IsEmpty() && !parsed_.password.is_valid())
+ return;
+
+ // The canonicalizer will clear any passwords that are empty, so we
+ // don't have to explicitly call ClearUsername() here.
+ StringUTF8Adaptor pass_utf8(pass);
+ url::Replacements<char> replacements;
+ replacements.SetPassword(CharactersOrEmpty(pass_utf8),
+ url::Component(0, pass_utf8.length()));
+ ReplaceComponents(replacements);
+}
+
+void KURL::SetFragmentIdentifier(const String& fragment) {
+ // This function is commonly called to clear the ref, which we
+ // normally don't have, so we optimize this case.
+ if (fragment.IsNull() && !parsed_.ref.is_valid())
+ return;
+
+ StringUTF8Adaptor fragment_utf8(fragment);
+
+ url::Replacements<char> replacements;
+ if (fragment.IsNull())
+ replacements.ClearRef();
+ else
+ replacements.SetRef(CharactersOrEmpty(fragment_utf8),
+ url::Component(0, fragment_utf8.length()));
+ ReplaceComponents(replacements);
+}
+
+void KURL::RemoveFragmentIdentifier() {
+ url::Replacements<char> replacements;
+ replacements.ClearRef();
+ ReplaceComponents(replacements);
+}
+
+void KURL::SetQuery(const String& query) {
+ StringUTF8Adaptor query_utf8(query);
+ url::Replacements<char> replacements;
+ if (query.IsNull()) {
+ // KURL.cpp sets to null to clear any query.
+ replacements.ClearQuery();
+ } else if (query.length() > 0 && query[0] == '?') {
+ // WebCore expects the query string to begin with a question mark, but
+ // GoogleURL doesn't. So we trim off the question mark when setting.
+ replacements.SetQuery(CharactersOrEmpty(query_utf8),
+ url::Component(1, query_utf8.length() - 1));
+ } else {
+ // When set with the empty string or something that doesn't begin with
+ // a question mark, KURL.cpp will add a question mark for you. The only
+ // way this isn't compatible is if you call this function with an empty
+ // string. KURL.cpp will leave a '?' with nothing following it in the
+ // URL, whereas we'll clear it.
+ // FIXME We should eliminate this difference.
+ replacements.SetQuery(CharactersOrEmpty(query_utf8),
+ url::Component(0, query_utf8.length()));
+ }
+ ReplaceComponents(replacements);
+}
+
+void KURL::SetPath(const String& path) {
+ // Empty paths will be canonicalized to "/", so we don't have to worry
+ // about calling ClearPath().
+ StringUTF8Adaptor path_utf8(path);
+ url::Replacements<char> replacements;
+ replacements.SetPath(CharactersOrEmpty(path_utf8),
+ url::Component(0, path_utf8.length()));
+ ReplaceComponents(replacements);
+}
+
+String DecodeURLEscapeSequences(const String& string,
+ DecodeURLResult* optional_result) {
+ StringUTF8Adaptor string_utf8(string);
+ url::RawCanonOutputT<base::char16> unescaped;
+ DecodeURLResult result = url::DecodeURLEscapeSequences(
+ string_utf8.Data(), string_utf8.length(), &unescaped);
+ if (optional_result)
+ *optional_result = result;
+ return StringImpl::Create8BitIfPossible(
+ reinterpret_cast<UChar*>(unescaped.data()), unescaped.length());
+}
+
+String EncodeWithURLEscapeSequences(const String& not_encoded_string) {
+ CString utf8 = UTF8Encoding().Encode(not_encoded_string,
+ WTF::kURLEncodedEntitiesForUnencodables);
+
+ url::RawCanonOutputT<char> buffer;
+ int input_length = utf8.length();
+ if (buffer.capacity() < input_length * 3)
+ buffer.Resize(input_length * 3);
+
+ url::EncodeURIComponent(utf8.data(), input_length, &buffer);
+ String escaped(buffer.data(), buffer.length());
+ // Unescape '/'; it's safe and much prettier.
+ escaped.Replace("%2F", "/");
+ return escaped;
+}
+
+bool KURL::IsHierarchical() const {
+ if (string_.IsNull() || !parsed_.scheme.is_nonempty())
+ return false;
+ return string_.Is8Bit()
+ ? url::IsStandard(AsURLChar8Subtle(string_), parsed_.scheme)
+ : url::IsStandard(string_.Characters16(), parsed_.scheme);
+}
+
+bool EqualIgnoringFragmentIdentifier(const KURL& a, const KURL& b) {
+ // Compute the length of each URL without its ref. Note that the reference
+ // begin (if it exists) points to the character *after* the '#', so we need
+ // to subtract one.
+ int a_length = a.string_.length();
+ if (a.parsed_.ref.len >= 0)
+ a_length = a.parsed_.ref.begin - 1;
+
+ int b_length = b.string_.length();
+ if (b.parsed_.ref.len >= 0)
+ b_length = b.parsed_.ref.begin - 1;
+
+ if (a_length != b_length)
+ return false;
+
+ const String& a_string = a.string_;
+ const String& b_string = b.string_;
+ // FIXME: Abstraction this into a function in WTFString.h.
+ for (int i = 0; i < a_length; ++i) {
+ if (a_string[i] != b_string[i])
+ return false;
+ }
+ return true;
+}
+
+unsigned KURL::HostStart() const {
+ return parsed_.CountCharactersBefore(url::Parsed::HOST, false);
+}
+
+unsigned KURL::HostEnd() const {
+ return parsed_.CountCharactersBefore(url::Parsed::PORT, true);
+}
+
+unsigned KURL::PathStart() const {
+ return parsed_.CountCharactersBefore(url::Parsed::PATH, false);
+}
+
+unsigned KURL::PathEnd() const {
+ return parsed_.CountCharactersBefore(url::Parsed::QUERY, true);
+}
+
+unsigned KURL::PathAfterLastSlash() const {
+ if (string_.IsNull())
+ return 0;
+ if (!is_valid_ || !parsed_.path.is_valid())
+ return parsed_.CountCharactersBefore(url::Parsed::PATH, false);
+ url::Component filename;
+ if (string_.Is8Bit())
+ url::ExtractFileName(AsURLChar8Subtle(string_), parsed_.path, &filename);
+ else
+ url::ExtractFileName(string_.Characters16(), parsed_.path, &filename);
+ return filename.begin;
+}
+
+bool ProtocolIs(const String& url, const char* protocol) {
+#if DCHECK_IS_ON()
+ AssertProtocolIsGood(protocol);
+#endif
+ if (url.IsNull())
+ return false;
+ if (url.Is8Bit()) {
+ return url::FindAndCompareScheme(AsURLChar8Subtle(url), url.length(),
+ protocol, nullptr);
+ }
+ return url::FindAndCompareScheme(url.Characters16(), url.length(), protocol,
+ nullptr);
+}
+
+void KURL::Init(const KURL& base,
+ const String& relative,
+ const WTF::TextEncoding* query_encoding) {
+ // As a performance optimization, we do not use the charset converter
+ // if encoding is UTF-8 or other Unicode encodings. Note that this is
+ // per HTML5 2.5.3 (resolving URL). The URL canonicalizer will be more
+ // efficient with no charset converter object because it can do UTF-8
+ // internally with no extra copies.
+
+ StringUTF8Adaptor base_utf8(base.GetString());
+
+ // We feel free to make the charset converter object every time since it's
+ // just a wrapper around a reference.
+ KURLCharsetConverter charset_converter_object(query_encoding);
+ KURLCharsetConverter* charset_converter =
+ (!query_encoding || IsUnicodeEncoding(query_encoding))
+ ? nullptr
+ : &charset_converter_object;
+
+ // Clamp to int max to avoid overflow.
+ url::RawCanonOutputT<char> output;
+ if (!relative.IsNull() && relative.Is8Bit()) {
+ StringUTF8Adaptor relative_utf8(relative);
+ is_valid_ = url::ResolveRelative(base_utf8.Data(), base_utf8.length(),
+ base.parsed_, relative_utf8.Data(),
+ clampTo<int>(relative_utf8.length()),
+ charset_converter, &output, &parsed_);
+ } else {
+ is_valid_ = url::ResolveRelative(base_utf8.Data(), base_utf8.length(),
+ base.parsed_, relative.Characters16(),
+ clampTo<int>(relative.length()),
+ charset_converter, &output, &parsed_);
+ }
+
+ // AtomicString::fromUTF8 will re-hash the raw output and check the
+ // AtomicStringTable (addWithTranslator) for the string. This can be very
+ // expensive for large URLs. However, since many URLs are generated from
+ // existing AtomicStrings (which already have their hashes computed), this
+ // fast path is used if the input string is already canonicalized.
+ //
+ // Because this optimization does not apply to non-AtomicStrings, explicitly
+ // check that the input is Atomic before moving forward with it. If we mark
+ // non-Atomic input as Atomic here, we will render the (const) input string
+ // thread unsafe.
+ if (!relative.IsNull() && relative.Impl()->IsAtomic() &&
+ StringView(output.data(), static_cast<unsigned>(output.length())) ==
+ relative) {
+ string_ = relative;
+ } else {
+ string_ = AtomicString::FromUTF8(output.data(), output.length());
+ }
+
+ InitProtocolMetadata();
+ InitInnerURL();
+ DCHECK(!::blink::ProtocolIsJavaScript(string_) || ProtocolIsJavaScript());
+}
+
+void KURL::InitInnerURL() {
+ if (!is_valid_) {
+ inner_url_.reset();
+ return;
+ }
+ if (url::Parsed* inner_parsed = parsed_.inner_parsed()) {
+ inner_url_ = std::make_unique<KURL>(
+ string_.Substring(inner_parsed->scheme.begin,
+ inner_parsed->Length() - inner_parsed->scheme.begin));
+ } else {
+ inner_url_.reset();
+ }
+}
+
+void KURL::InitProtocolMetadata() {
+ if (!is_valid_) {
+ protocol_is_in_http_family_ = false;
+ protocol_ = ComponentString(parsed_.scheme);
+ return;
+ }
+
+ DCHECK(!string_.IsNull());
+ StringView protocol = ComponentStringView(parsed_.scheme);
+ protocol_is_in_http_family_ = true;
+ if (protocol == WTF::g_https_atom) {
+ protocol_ = WTF::g_https_atom;
+ } else if (protocol == WTF::g_http_atom) {
+ protocol_ = WTF::g_http_atom;
+ } else {
+ protocol_ = protocol.ToAtomicString();
+ protocol_is_in_http_family_ = false;
+ }
+ DCHECK_EQ(protocol_, protocol_.DeprecatedLower());
+}
+
+bool KURL::ProtocolIs(const StringView protocol) const {
+#if DCHECK_IS_ON()
+ AssertProtocolIsGood(protocol);
+#endif
+
+ // JavaScript URLs are "valid" and should be executed even if KURL decides
+ // they are invalid. The free function protocolIsJavaScript() should be used
+ // instead.
+ // FIXME: Chromium code needs to be fixed for this assert to be enabled.
+ // DCHECK(strcmp(protocol, "javascript"));
+ return protocol_ == protocol;
+}
+
+StringView KURL::StringViewForInvalidComponent() const {
+ return string_.IsNull() ? StringView() : StringView(StringImpl::empty_);
+}
+
+StringView KURL::ComponentStringView(const url::Component& component) const {
+ if (!is_valid_ || component.len <= 0)
+ return StringViewForInvalidComponent();
+ // begin and len are in terms of bytes which do not match
+ // if string() is UTF-16 and input contains non-ASCII characters.
+ // However, the only part in urlString that can contain non-ASCII
+ // characters is 'ref' at the end of the string. In that case,
+ // begin will always match the actual value and len (in terms of
+ // byte) will be longer than what's needed by 'mid'. However, mid
+ // truncates len to avoid go past the end of a string so that we can
+ // get away without doing anything here.
+
+ int max_length = GetString().length() - component.begin;
+ return StringView(GetString(), component.begin,
+ component.len > max_length ? max_length : component.len);
+}
+
+String KURL::ComponentString(const url::Component& component) const {
+ return ComponentStringView(component).ToString();
+}
+
+template <typename CHAR>
+void KURL::ReplaceComponents(const url::Replacements<CHAR>& replacements) {
+ url::RawCanonOutputT<char> output;
+ url::Parsed new_parsed;
+
+ StringUTF8Adaptor utf8(string_);
+ is_valid_ =
+ url::ReplaceComponents(utf8.Data(), utf8.length(), parsed_, replacements,
+ nullptr, &output, &new_parsed);
+
+ parsed_ = new_parsed;
+ string_ = AtomicString::FromUTF8(output.data(), output.length());
+ InitProtocolMetadata();
+}
+
+bool KURL::IsSafeToSendToAnotherThread() const {
+ return string_.IsSafeToSendToAnotherThread() &&
+ (!inner_url_ || inner_url_->IsSafeToSendToAnotherThread());
+}
+
+KURL::operator GURL() const {
+ return GURL(string_.Utf8().data(), parsed_, is_valid_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/weborigin/kurl.h b/chromium/third_party/blink/renderer/platform/weborigin/kurl.h
new file mode 100644
index 00000000000..67a5e9df177
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/weborigin/kurl.h
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2011, 2012 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_KURL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_KURL_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "url/third_party/mozilla/url_parse.h"
+#include "url/url_canon.h"
+#include "url/url_util.h"
+
+// KURL is Blink's main URL class, and is the analog to GURL in other Chromium
+// code. It is not thread safe but is generally cheap to copy and compare KURLs
+// to each other.
+//
+// KURL and GURL both share the same underlying URL parser, whose code is
+// located in //url, but KURL is backed by Blink specific WTF::Strings. This
+// means that KURLs are usually cheap to copy due to WTF::Strings being
+// internally ref-counted. However, please don't copy KURLs if you can use a
+// const ref, since the size of the parsed structure and related metadata is
+// non-trivial.
+//
+// In fact, for the majority of KURLs (i.e. those not copied across threads),
+// the backing string is an AtomicString, meaning that it is stored in the
+// thread-local AtomicString table, allowing optimizations like fast comparison.
+// See platform/wtf/text/AtomicString.h for information on the performance
+// characteristics of AtomicStrings.
+//
+// KURL also has a few other optimizations, including:
+// - Cached bit for whether the KURL is http/https
+// - Internal reference to the URL protocol (scheme) to avoid String allocation
+// for the callers that require it. Common protocols like http and https are
+// stored as static strings which can be shared across threads.
+namespace WTF {
+class TextEncoding;
+}
+
+class GURL;
+
+namespace blink {
+
+struct KURLHash;
+
+class PLATFORM_EXPORT KURL {
+ USING_FAST_MALLOC(KURL);
+
+ public:
+ // This must be called during initialization (before we create
+ // other threads).
+ static void Initialize();
+
+ KURL();
+ KURL(const KURL&);
+ KURL& operator=(const KURL&);
+
+ // The argument is an absolute URL string. The string is assumed to be
+ // output of KURL::GetString() called on a valid KURL object, or
+ // indiscernible from such.
+ //
+ // It is usually best to avoid repeatedly parsing a String, unless memory
+ // saving outweigh the possible slow-downs.
+ explicit KURL(const String&);
+
+ // Creates an isolated URL object suitable for sending to another thread.
+ static KURL CreateIsolated(const String&);
+
+ // Resolves the relative URL with the given base URL. If provided, the
+ // TextEncoding is used to encode non-ASCII characters. The base URL can be
+ // null or empty, in which case the relative URL will be interpreted as
+ // absolute.
+ // FIXME: If the base URL is invalid, this always creates an invalid
+ // URL. Instead I think it would be better to treat all invalid base URLs
+ // the same way we treate null and empty base URLs.
+ KURL(const KURL& base, const String& relative);
+ KURL(const KURL& base, const String& relative, const WTF::TextEncoding&);
+
+ // For conversions from other structures that have already parsed and
+ // canonicalized the URL. The input must be exactly what KURL would have
+ // done with the same input.
+ KURL(const AtomicString& canonical_string, const url::Parsed&, bool is_valid);
+
+ ~KURL();
+
+ String StrippedForUseAsReferrer() const;
+ String StrippedForUseAsHref() const;
+
+ // FIXME: The above functions should be harmonized so that passing a
+ // base of null or the empty string gives the same result as the
+ // standard String constructor.
+
+ // Makes a deep copy. Helpful only if you need to use a KURL on another
+ // thread. Since the underlying StringImpl objects are immutable, there's
+ // no other reason to ever prefer copy() over plain old assignment.
+ KURL Copy() const;
+
+ bool IsNull() const;
+ bool IsEmpty() const;
+ bool IsValid() const;
+
+ // Returns true if this URL has a path. Note that "http://foo.com/" has a
+ // path of "/", so this function will return true. Only invalid or
+ // non-hierarchical (like "javascript:") URLs will have no path.
+ bool HasPath() const;
+
+ // Returns true if you can set the host and port for the URL.
+ // Non-hierarchical URLs don't have a host and port.
+ bool CanSetHostOrPort() const { return IsHierarchical(); }
+
+ bool CanSetPathname() const { return IsHierarchical(); }
+ bool IsHierarchical() const;
+
+ const String& GetString() const { return string_; }
+
+ String ElidedString() const;
+
+ String Protocol() const;
+ String Host() const;
+ unsigned short Port() const;
+ bool HasPort() const;
+ String User() const;
+ String Pass() const;
+ String GetPath() const;
+ // This method handles "parameters" separated by a semicolon.
+ String LastPathComponent() const;
+ String Query() const;
+ String FragmentIdentifier() const;
+ bool HasFragmentIdentifier() const;
+
+ String BaseAsString() const;
+
+ // Returns true if the current URL's protocol is the same as the StringView
+ // argument. The argument must be lower-case.
+ bool ProtocolIs(const StringView protocol) const;
+ bool ProtocolIsData() const { return ProtocolIs("data"); }
+ // This includes at least about:blank and about:srcdoc.
+ bool ProtocolIsAbout() const { return ProtocolIs("about"); }
+ bool ProtocolIsJavaScript() const;
+ bool ProtocolIsInHTTPFamily() const;
+ bool IsLocalFile() const;
+ bool IsAboutBlankURL() const; // Is exactly about:blank.
+ bool IsAboutSrcdocURL() const; // Is exactly about:srcdoc.
+
+ bool SetProtocol(const String&);
+ void SetHost(const String&);
+
+ void RemovePort();
+ void SetPort(unsigned short);
+ void SetPort(const String&);
+
+ // Input is like "foo.com" or "foo.com:8000".
+ void SetHostAndPort(const String&);
+
+ void SetUser(const String&);
+ void SetPass(const String&);
+
+ // If you pass an empty path for HTTP or HTTPS URLs, the resulting path
+ // will be "/".
+ void SetPath(const String&);
+
+ // The query may begin with a question mark, or, if not, one will be added
+ // for you. Setting the query to the empty string will leave a "?" in the
+ // URL (with nothing after it). To clear the query, pass a null string.
+ void SetQuery(const String&);
+
+ void SetFragmentIdentifier(const String&);
+ void RemoveFragmentIdentifier();
+
+ PLATFORM_EXPORT friend bool EqualIgnoringFragmentIdentifier(const KURL&,
+ const KURL&);
+
+ unsigned HostStart() const;
+ unsigned HostEnd() const;
+
+ unsigned PathStart() const;
+ unsigned PathEnd() const;
+ unsigned PathAfterLastSlash() const;
+
+ operator const String&() const { return GetString(); }
+ operator StringView() const { return StringView(GetString()); }
+
+ const url::Parsed& GetParsed() const { return parsed_; }
+
+ const KURL* InnerURL() const { return inner_url_.get(); }
+
+ bool IsSafeToSendToAnotherThread() const;
+
+ bool PotentiallyDanglingMarkup() const {
+ return parsed_.potentially_dangling_markup;
+ }
+
+ // Returns a GURL with the same properties. This can be used in platform/ and
+ // web/. However, in core/ and modules/, this should only be used to pass
+ // a GURL to a layer that is expecting one instead of a KURL or a WebURL.
+ operator GURL() const;
+
+ private:
+ friend struct WTF::HashTraits<blink::KURL>;
+
+ void Init(const KURL& base,
+ const String& relative,
+ const WTF::TextEncoding* query_encoding);
+
+ StringView ComponentStringView(const url::Component&) const;
+ String ComponentString(const url::Component&) const;
+ StringView StringViewForInvalidComponent() const;
+
+ template <typename CHAR>
+ void ReplaceComponents(const url::Replacements<CHAR>&);
+
+ void InitInnerURL();
+ void InitProtocolMetadata();
+
+ bool is_valid_;
+ bool protocol_is_in_http_family_;
+
+ // Keep a separate string for the protocol to avoid copious copies for
+ // protocol(). Normally this will be Atomic, except when constructed via
+ // KURL::copy(), which is deep.
+ String protocol_;
+
+ url::Parsed parsed_;
+ String string_;
+ std::unique_ptr<KURL> inner_url_;
+};
+
+PLATFORM_EXPORT bool operator==(const KURL&, const KURL&);
+PLATFORM_EXPORT bool operator==(const KURL&, const String&);
+PLATFORM_EXPORT bool operator==(const String&, const KURL&);
+PLATFORM_EXPORT bool operator!=(const KURL&, const KURL&);
+PLATFORM_EXPORT bool operator!=(const KURL&, const String&);
+PLATFORM_EXPORT bool operator!=(const String&, const KURL&);
+
+PLATFORM_EXPORT bool EqualIgnoringFragmentIdentifier(const KURL&, const KURL&);
+
+PLATFORM_EXPORT const KURL& BlankURL();
+PLATFORM_EXPORT const KURL& SrcdocURL();
+PLATFORM_EXPORT const KURL& NullURL();
+
+// Functions to do URL operations on strings.
+// These are operations that aren't faster on a parsed URL.
+// These are also different from the KURL functions in that they don't require
+// the string to be a valid and parsable URL. This is especially important
+// because valid javascript URLs are not necessarily considered valid by KURL.
+
+PLATFORM_EXPORT bool ProtocolIs(const String& url, const char* protocol);
+PLATFORM_EXPORT bool ProtocolIsJavaScript(const String& url);
+
+PLATFORM_EXPORT bool IsValidProtocol(const String&);
+
+using DecodeURLResult = url::DecodeURLResult;
+
+// Unescapes the given string using URL escaping rules.
+//
+// DANGER: If the URL has "%00" in it, the resulting string will have embedded
+// null characters!
+//
+// This function is also used to decode javascript: URLs and as a general
+// purpose unescaping function.
+// TODO(tkent): Remove the second argument after collecting data.
+PLATFORM_EXPORT String DecodeURLEscapeSequences(const String&,
+ DecodeURLResult* = nullptr);
+
+PLATFORM_EXPORT String EncodeWithURLEscapeSequences(const String&);
+
+// Inlines.
+
+inline bool operator==(const KURL& a, const KURL& b) {
+ return a.GetString() == b.GetString();
+}
+
+inline bool operator==(const KURL& a, const String& b) {
+ return a.GetString() == b;
+}
+
+inline bool operator==(const String& a, const KURL& b) {
+ return a == b.GetString();
+}
+
+inline bool operator!=(const KURL& a, const KURL& b) {
+ return a.GetString() != b.GetString();
+}
+
+inline bool operator!=(const KURL& a, const String& b) {
+ return a.GetString() != b;
+}
+
+inline bool operator!=(const String& a, const KURL& b) {
+ return a != b.GetString();
+}
+
+} // namespace blink
+
+namespace WTF {
+
+// KURLHash is the default hash for String
+template <>
+struct DefaultHash<blink::KURL> {
+ typedef blink::KURLHash Hash;
+};
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_KURL_H_
diff --git a/chromium/third_party/blink/renderer/platform/weborigin/kurl_hash.h b/chromium/third_party/blink/renderer/platform/weborigin/kurl_hash.h
new file mode 100644
index 00000000000..3c020af181e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/weborigin/kurl_hash.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2008 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_KURL_HASH_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_KURL_HASH_H_
+
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+// KURLHash doesn't support null KURLs. get(), contains(), and add() on
+// HashMap<KURL,..., KURLHash> cause a null-pointer dereference when passed null
+// KURLs.
+
+struct KURLHash {
+ STATIC_ONLY(KURLHash);
+ static unsigned GetHash(const KURL& key) {
+ return key.GetString().Impl()->GetHash();
+ }
+
+ static bool Equal(const KURL& a, const KURL& b) {
+ return StringHash::Equal(a.GetString(), b.GetString());
+ }
+
+ static const bool safe_to_compare_to_empty_or_deleted = false;
+};
+
+} // namespace blink
+
+namespace WTF {
+
+template <>
+struct HashTraits<blink::KURL> : SimpleClassHashTraits<blink::KURL> {
+ static bool IsDeletedValue(const blink::KURL& value) {
+ return HashTraits<String>::IsDeletedValue(value.GetString());
+ }
+
+ static void ConstructDeletedValue(blink::KURL& slot, bool zero_value) {
+ HashTraits<String>::ConstructDeletedValue(slot.string_, zero_value);
+ }
+};
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_KURL_HASH_H_
diff --git a/chromium/third_party/blink/renderer/platform/weborigin/kurl_test.cc b/chromium/third_party/blink/renderer/platform/weborigin/kurl_test.cc
new file mode 100644
index 00000000000..93cfe0e38aa
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/weborigin/kurl_test.cc
@@ -0,0 +1,841 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+// Basic tests that verify our KURL's interface behaves the same as the
+// original KURL's.
+
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "url/url_util.h"
+
+namespace blink {
+
+TEST(KURLTest, Getters) {
+ struct GetterCase {
+ const char* url;
+ const char* protocol;
+ const char* host;
+ int port;
+ const char* user;
+ const char* pass;
+ const char* path;
+ const char* last_path_component;
+ const char* query;
+ const char* fragment_identifier;
+ bool has_fragment_identifier;
+ } cases[] = {
+ {"http://www.google.com/foo/blah?bar=baz#ref", "http", "www.google.com",
+ 0, "", nullptr, "/foo/blah", "blah", "bar=baz", "ref", true},
+ {// Non-ASCII code points in the fragment part. fragmentIdentifier()
+ // should return it in percent-encoded form.
+ "http://www.google.com/foo/blah?bar=baz#\xce\xb1\xce\xb2", "http",
+ "www.google.com", 0, "", nullptr, "/foo/blah", "blah", "bar=baz",
+ "%CE%B1%CE%B2", true},
+ {"http://foo.com:1234/foo/bar/", "http", "foo.com", 1234, "", nullptr,
+ "/foo/bar/", "bar", nullptr, nullptr, false},
+ {"http://www.google.com?#", "http", "www.google.com", 0, "", nullptr, "/",
+ nullptr, "", "", true},
+ {"https://me:pass@google.com:23#foo", "https", "google.com", 23, "me",
+ "pass", "/", nullptr, nullptr, "foo", true},
+ {"javascript:hello!//world", "javascript", "", 0, "", nullptr,
+ "hello!//world", "world", nullptr, nullptr, false},
+ {// Recognize a query and a fragment in the path portion of a path
+ // URL.
+ "javascript:hello!?#/\\world", "javascript", "", 0, "", nullptr,
+ "hello!", "hello!", "", "/\\world", true},
+ {// lastPathComponent() method handles "parameters" in a path. path()
+ // method doesn't.
+ "http://a.com/hello;world", "http", "a.com", 0, "", nullptr,
+ "/hello;world", "hello", nullptr, nullptr, false},
+ {// IDNA
+ "http://\xe4\xbd\xa0\xe5\xa5\xbd\xe4\xbd\xa0\xe5\xa5\xbd/", "http",
+ "xn--6qqa088eba", 0, "", nullptr, "/", nullptr, nullptr, nullptr, false},
+ };
+
+ for (size_t i = 0; i < WTF_ARRAY_LENGTH(cases); i++) {
+ const GetterCase& c = cases[i];
+
+ const String& url = String::FromUTF8(c.url);
+
+ const KURL kurl(url);
+
+ // Casted to the String (or coverted to using fromUTF8() for
+ // expectations which may include non-ASCII code points) so that the
+ // contents are printed on failure.
+ EXPECT_EQ(String(c.protocol), kurl.Protocol()) << url;
+ EXPECT_EQ(String(c.host), kurl.Host()) << url;
+ EXPECT_EQ(c.port, kurl.Port()) << url;
+ EXPECT_EQ(String(c.user), kurl.User()) << url;
+ EXPECT_EQ(String(c.pass), kurl.Pass()) << url;
+ EXPECT_EQ(String(c.path), kurl.GetPath()) << url;
+ EXPECT_EQ(String(c.last_path_component), kurl.LastPathComponent()) << url;
+ EXPECT_EQ(String(c.query), kurl.Query()) << url;
+ if (c.has_fragment_identifier)
+ EXPECT_EQ(String::FromUTF8(c.fragment_identifier),
+ kurl.FragmentIdentifier())
+ << url;
+ else
+ EXPECT_TRUE(kurl.FragmentIdentifier().IsNull()) << url;
+ }
+}
+
+TEST(KURLTest, Setters) {
+ // Replace the starting URL with the given components one at a time and
+ // verify that we're always the same as the old KURL.
+ //
+ // Note that old KURL won't canonicalize the default port away, so we
+ // can't set setting the http port to "80" (or even "0").
+ //
+ // We also can't test clearing the query.
+ struct ExpectedComponentCase {
+ const char* url;
+
+ const char* protocol;
+ const char* expected_protocol;
+
+ const char* host;
+ const char* expected_host;
+
+ const int port;
+ const char* expected_port;
+
+ const char* user;
+ const char* expected_user;
+
+ const char* pass;
+ const char* expected_pass;
+
+ const char* path;
+ const char* expected_path;
+
+ const char* query;
+ const char* expected_query;
+ } cases[] = {
+ {"http://www.google.com/",
+ // protocol
+ "https", "https://www.google.com/",
+ // host
+ "news.google.com", "https://news.google.com/",
+ // port
+ 8888, "https://news.google.com:8888/",
+ // user
+ "me", "https://me@news.google.com:8888/",
+ // pass
+ "pass", "https://me:pass@news.google.com:8888/",
+ // path
+ "/foo", "https://me:pass@news.google.com:8888/foo",
+ // query
+ "?q=asdf", "https://me:pass@news.google.com:8888/foo?q=asdf"},
+ {"https://me:pass@google.com:88/a?f#b",
+ // protocol
+ "http", "http://me:pass@google.com:88/a?f#b",
+ // host
+ "goo.com", "http://me:pass@goo.com:88/a?f#b",
+ // port
+ 92, "http://me:pass@goo.com:92/a?f#b",
+ // user
+ "", "http://:pass@goo.com:92/a?f#b",
+ // pass
+ "", "http://goo.com:92/a?f#b",
+ // path
+ "/", "http://goo.com:92/?f#b",
+ // query
+ nullptr, "http://goo.com:92/#b"},
+ };
+
+ for (size_t i = 0; i < WTF_ARRAY_LENGTH(cases); i++) {
+ KURL kurl(cases[i].url);
+
+ kurl.SetProtocol(cases[i].protocol);
+ EXPECT_STREQ(cases[i].expected_protocol, kurl.GetString().Utf8().data());
+
+ kurl.SetHost(cases[i].host);
+ EXPECT_STREQ(cases[i].expected_host, kurl.GetString().Utf8().data());
+
+ kurl.SetPort(cases[i].port);
+ EXPECT_STREQ(cases[i].expected_port, kurl.GetString().Utf8().data());
+
+ kurl.SetUser(cases[i].user);
+ EXPECT_STREQ(cases[i].expected_user, kurl.GetString().Utf8().data());
+
+ kurl.SetPass(cases[i].pass);
+ EXPECT_STREQ(cases[i].expected_pass, kurl.GetString().Utf8().data());
+
+ kurl.SetPath(cases[i].path);
+ EXPECT_STREQ(cases[i].expected_path, kurl.GetString().Utf8().data());
+
+ kurl.SetQuery(cases[i].query);
+ EXPECT_STREQ(cases[i].expected_query, kurl.GetString().Utf8().data());
+
+ // Refs are tested below. On the Safari 3.1 branch, we don't match their
+ // KURL since we integrated a fix from their trunk.
+ }
+}
+
+// Tests that KURL::decodeURLEscapeSequences works as expected
+TEST(KURLTest, DecodeURLEscapeSequences) {
+ struct DecodeCase {
+ const char* input;
+ const char* output;
+ } decode_cases[] = {
+ {"hello, world", "hello, world"},
+ {"%01%02%03%04%05%06%07%08%09%0a%0B%0C%0D%0e%0f/",
+ "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0B\x0C\x0D\x0e\x0f/"},
+ {"%10%11%12%13%14%15%16%17%18%19%1a%1B%1C%1D%1e%1f/",
+ "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1B\x1C\x1D\x1e\x1f/"},
+ {"%20%21%22%23%24%25%26%27%28%29%2a%2B%2C%2D%2e%2f/",
+ " !\"#$%&'()*+,-.//"},
+ {"%30%31%32%33%34%35%36%37%38%39%3a%3B%3C%3D%3e%3f/",
+ "0123456789:;<=>?/"},
+ {"%40%41%42%43%44%45%46%47%48%49%4a%4B%4C%4D%4e%4f/",
+ "@ABCDEFGHIJKLMNO/"},
+ {"%50%51%52%53%54%55%56%57%58%59%5a%5B%5C%5D%5e%5f/",
+ "PQRSTUVWXYZ[\\]^_/"},
+ {"%60%61%62%63%64%65%66%67%68%69%6a%6B%6C%6D%6e%6f/",
+ "`abcdefghijklmno/"},
+ {"%70%71%72%73%74%75%76%77%78%79%7a%7B%7C%7D%7e%7f/",
+ "pqrstuvwxyz{|}~\x7f/"},
+ // Test un-UTF-8-ization.
+ {"%e4%bd%a0%e5%a5%bd", "\xe4\xbd\xa0\xe5\xa5\xbd"},
+ };
+
+ for (size_t i = 0; i < WTF_ARRAY_LENGTH(decode_cases); i++) {
+ String input(decode_cases[i].input);
+ String str = DecodeURLEscapeSequences(input);
+ EXPECT_STREQ(decode_cases[i].output, str.Utf8().data());
+ }
+
+ // Our decode should decode %00
+ String zero = DecodeURLEscapeSequences("%00");
+ EXPECT_STRNE("%00", zero.Utf8().data());
+
+ // Decode UTF-8.
+ String decoded = DecodeURLEscapeSequences("%e6%bc%a2%e5%ad%97");
+ const UChar kDecodedExpected[] = {0x6F22, 0x5b57};
+ EXPECT_EQ(String(kDecodedExpected, WTF_ARRAY_LENGTH(kDecodedExpected)),
+ decoded);
+
+ // Test the error behavior for invalid UTF-8 (we differ from WebKit here).
+ String invalid = DecodeURLEscapeSequences("%e4%a0%e5%a5%bd");
+ UChar invalid_expected_helper[4] = {0x00e4, 0x00a0, 0x597d, 0};
+ String invalid_expected(
+ reinterpret_cast<const ::UChar*>(invalid_expected_helper), 3);
+ EXPECT_EQ(invalid_expected, invalid);
+}
+
+TEST(KURLTest, EncodeWithURLEscapeSequences) {
+ struct EncodeCase {
+ const char* input;
+ const char* output;
+ } encode_cases[] = {
+ {"hello, world", "hello%2C%20world"},
+ {"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F",
+ "%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F"},
+ {"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F",
+ "%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F"},
+ {" !\"#$%&'()*+,-./", "%20!%22%23%24%25%26%27()*%2B%2C-./"},
+ {"0123456789:;<=>?", "0123456789%3A%3B%3C%3D%3E%3F"},
+ {"@ABCDEFGHIJKLMNO", "%40ABCDEFGHIJKLMNO"},
+ {"PQRSTUVWXYZ[\\]^_", "PQRSTUVWXYZ%5B%5C%5D%5E_"},
+ {"`abcdefghijklmno", "%60abcdefghijklmno"},
+ {"pqrstuvwxyz{|}~\x7f", "pqrstuvwxyz%7B%7C%7D~%7F"},
+ };
+
+ for (size_t i = 0; i < WTF_ARRAY_LENGTH(encode_cases); i++) {
+ String input(encode_cases[i].input);
+ String expected_output(encode_cases[i].output);
+ String output = EncodeWithURLEscapeSequences(input);
+ EXPECT_EQ(expected_output, output);
+ }
+
+ // Our encode escapes NULLs for safety, so we need to check that too.
+ String input("\x00\x01", 2);
+ String reference("%00%01");
+
+ String output = EncodeWithURLEscapeSequences(input);
+ EXPECT_EQ(reference, output);
+
+ // Also test that it gets converted to UTF-8 properly.
+ UChar wide_input_helper[3] = {0x4f60, 0x597d, 0};
+ String wide_input(reinterpret_cast<const ::UChar*>(wide_input_helper), 2);
+ String wide_reference("%E4%BD%A0%E5%A5%BD");
+ String wide_output = EncodeWithURLEscapeSequences(wide_input);
+ EXPECT_EQ(wide_reference, wide_output);
+
+ // Encoding should not NFC-normalize the string.
+ // Contain a combining character ('e' + COMBINING OGONEK).
+ String combining(String::FromUTF8("\x65\xCC\xA8"));
+ EXPECT_EQ(EncodeWithURLEscapeSequences(combining), "e%CC%A8");
+ // Contain a precomposed character corresponding to |combining|.
+ String precomposed(String::FromUTF8("\xC4\x99"));
+ EXPECT_EQ(EncodeWithURLEscapeSequences(precomposed), "%C4%99");
+}
+
+TEST(KURLTest, AbsoluteRemoveWhitespace) {
+ struct {
+ const char* input;
+ const char* expected;
+ } cases[] = {
+ {"ht\ntps://example.com/yay?boo#foo", "https://example.com/yay?boo#foo"},
+ {"ht\ttps://example.com/yay?boo#foo", "https://example.com/yay?boo#foo"},
+ {"ht\rtps://example.com/yay?boo#foo", "https://example.com/yay?boo#foo"},
+ {"https://exa\nmple.com/yay?boo#foo", "https://example.com/yay?boo#foo"},
+ {"https://exa\tmple.com/yay?boo#foo", "https://example.com/yay?boo#foo"},
+ {"https://exa\rmple.com/yay?boo#foo", "https://example.com/yay?boo#foo"},
+ {"https://example.com/y\nay?boo#foo", "https://example.com/yay?boo#foo"},
+ {"https://example.com/y\tay?boo#foo", "https://example.com/yay?boo#foo"},
+ {"https://example.com/y\ray?boo#foo", "https://example.com/yay?boo#foo"},
+ {"https://example.com/yay?b\noo#foo", "https://example.com/yay?boo#foo"},
+ {"https://example.com/yay?b\too#foo", "https://example.com/yay?boo#foo"},
+ {"https://example.com/yay?b\roo#foo", "https://example.com/yay?boo#foo"},
+ {"https://example.com/yay?boo#f\noo", "https://example.com/yay?boo#foo"},
+ {"https://example.com/yay?boo#f\too", "https://example.com/yay?boo#foo"},
+ {"https://example.com/yay?boo#f\roo", "https://example.com/yay?boo#foo"},
+ };
+
+ for (const auto& test : cases) {
+ const KURL input(test.input);
+ const KURL expected(test.expected);
+ EXPECT_EQ(input, expected);
+ }
+}
+
+TEST(KURLTest, RelativeRemoveWhitespace) {
+ struct {
+ const char* base;
+ const char* relative;
+ } cases[] = {
+ {"http://example.com/", "/path"}, {"http://example.com/", "\n/path"},
+ {"http://example.com/", "\r/path"}, {"http://example.com/", "\t/path"},
+ {"http://example.com/", "/pa\nth"}, {"http://example.com/", "/pa\rth"},
+ {"http://example.com/", "/pa\tth"}, {"http://example.com/", "/path\n"},
+ {"http://example.com/", "/path\r"}, {"http://example.com/", "/path\t"},
+ };
+
+ for (const auto& test : cases) {
+ SCOPED_TRACE(testing::Message() << test.base << ", " << test.relative);
+ const KURL base(test.base);
+ const KURL expected("http://example.com/path");
+ const KURL actual(base, test.relative);
+ EXPECT_EQ(actual, expected);
+ }
+}
+
+TEST(KURLTest, AbsolutePotentiallyDanglingMarkup) {
+ struct {
+ const char* input;
+ const char* expected;
+ const bool potentially_dangling_markup;
+ } cases[] = {
+ // Just removable whitespace isn't enough:
+ {"ht\ntps://example.com/yay?boo#foo", "https://example.com/yay?boo#foo",
+ false},
+ {"ht\ttps://example.com/yay?boo#foo", "https://example.com/yay?boo#foo",
+ false},
+ {"ht\rtps://example.com/yay?boo#foo", "https://example.com/yay?boo#foo",
+ false},
+ {"https://exa\nmple.com/yay?boo#foo", "https://example.com/yay?boo#foo",
+ false},
+ {"https://exa\tmple.com/yay?boo#foo", "https://example.com/yay?boo#foo",
+ false},
+ {"https://exa\rmple.com/yay?boo#foo", "https://example.com/yay?boo#foo",
+ false},
+ {"https://example.com/y\nay?boo#foo", "https://example.com/yay?boo#foo",
+ false},
+ {"https://example.com/y\tay?boo#foo", "https://example.com/yay?boo#foo",
+ false},
+ {"https://example.com/y\ray?boo#foo", "https://example.com/yay?boo#foo",
+ false},
+ {"https://example.com/yay?b\noo#foo", "https://example.com/yay?boo#foo",
+ false},
+ {"https://example.com/yay?b\too#foo", "https://example.com/yay?boo#foo",
+ false},
+ {"https://example.com/yay?b\roo#foo", "https://example.com/yay?boo#foo",
+ false},
+ {"https://example.com/yay?boo#f\noo", "https://example.com/yay?boo#foo",
+ false},
+ {"https://example.com/yay?boo#f\too", "https://example.com/yay?boo#foo",
+ false},
+ {"https://example.com/yay?boo#f\roo", "https://example.com/yay?boo#foo",
+ false},
+
+ // Likewise, just a brace won't cut it:
+ {"https://example.com/y<ay?boo#foo", "https://example.com/y%3Cay?boo#foo",
+ false},
+ {"https://example.com/yay?b<oo#foo", "https://example.com/yay?b%3Coo#foo",
+ false},
+ {"https://example.com/yay?boo#f<oo", "https://example.com/yay?boo#f<oo",
+ false},
+
+ // Both, however:
+ {"ht\ntps://example.com/y<ay?boo#foo",
+ "https://example.com/y%3Cay?boo#foo", true},
+ {"https://e\nxample.com/y<ay?boo#foo",
+ "https://example.com/y%3Cay?boo#foo", true},
+ {"https://example.com/y<\nay?boo#foo",
+ "https://example.com/y%3Cay?boo#foo", true},
+ {"https://example.com/y<ay?b\noo#foo",
+ "https://example.com/y%3Cay?boo#foo", true},
+ {"https://example.com/y<ay?boo#f\noo",
+ "https://example.com/y%3Cay?boo#foo", true},
+ {"ht\ntps://example.com/yay?b<oo#foo",
+ "https://example.com/yay?b%3Coo#foo", true},
+ {"https://e\nxample.com/yay?b<oo#foo",
+ "https://example.com/yay?b%3Coo#foo", true},
+ {"https://example.com/y\nay?b<oo#foo",
+ "https://example.com/yay?b%3Coo#foo", true},
+ {"https://example.com/yay?b<\noo#foo",
+ "https://example.com/yay?b%3Coo#foo", true},
+ {"https://example.com/yay?b<oo#f\noo",
+ "https://example.com/yay?b%3Coo#foo", true},
+ {"ht\ntps://example.com/yay?boo#f<oo", "https://example.com/yay?boo#f<oo",
+ true},
+ {"https://e\nxample.com/yay?boo#f<oo", "https://example.com/yay?boo#f<oo",
+ true},
+ {"https://example.com/y\nay?boo#f<oo", "https://example.com/yay?boo#f<oo",
+ true},
+ {"https://example.com/yay?b\noo#f<oo", "https://example.com/yay?boo#f<oo",
+ true},
+ {"https://example.com/yay?boo#f<\noo", "https://example.com/yay?boo#f<oo",
+ true},
+ };
+
+ for (const auto& test : cases) {
+ SCOPED_TRACE(testing::Message() << test.input << ", " << test.expected);
+ const KURL input(test.input);
+ const KURL expected(test.expected);
+ EXPECT_EQ(input, expected) << input.GetString() << expected.GetString();
+ EXPECT_EQ(test.potentially_dangling_markup,
+ input.PotentiallyDanglingMarkup());
+ EXPECT_FALSE(expected.PotentiallyDanglingMarkup());
+ }
+}
+
+TEST(KURLTest, ResolveEmpty) {
+ const KURL empty_base;
+
+ // WebKit likes to be able to resolve absolute input agains empty base URLs,
+ // which would normally be invalid since the base URL is invalid.
+ const char kAbs[] = "http://www.google.com/";
+ KURL resolve_abs(empty_base, kAbs);
+ EXPECT_TRUE(resolve_abs.IsValid());
+ EXPECT_STREQ(kAbs, resolve_abs.GetString().Utf8().data());
+
+ // Resolving a non-relative URL agains the empty one should still error.
+ const char kRel[] = "foo.html";
+ KURL resolve_err(empty_base, kRel);
+ EXPECT_FALSE(resolve_err.IsValid());
+}
+
+// WebKit will make empty URLs and set components on them. kurl doesn't allow
+// replacements on invalid URLs, but here we do.
+TEST(KURLTest, ReplaceInvalid) {
+ KURL kurl;
+
+ EXPECT_FALSE(kurl.IsValid());
+ EXPECT_TRUE(kurl.IsEmpty());
+ EXPECT_STREQ("", kurl.GetString().Utf8().data());
+
+ kurl.SetProtocol("http");
+ // GKURL will say that a URL with just a scheme is invalid, KURL will not.
+ EXPECT_FALSE(kurl.IsValid());
+ EXPECT_FALSE(kurl.IsEmpty());
+ // At this point, we do things slightly differently if there is only a scheme.
+ // We check the results here to make it more obvious what is going on, but it
+ // shouldn't be a big deal if these change.
+ EXPECT_STREQ("http:", kurl.GetString().Utf8().data());
+
+ kurl.SetHost("www.google.com");
+ EXPECT_TRUE(kurl.IsValid());
+ EXPECT_FALSE(kurl.IsEmpty());
+ EXPECT_STREQ("http://www.google.com/", kurl.GetString().Utf8().data());
+
+ kurl.SetPort(8000);
+ EXPECT_TRUE(kurl.IsValid());
+ EXPECT_FALSE(kurl.IsEmpty());
+ EXPECT_STREQ("http://www.google.com:8000/", kurl.GetString().Utf8().data());
+
+ kurl.SetPath("/favicon.ico");
+ EXPECT_TRUE(kurl.IsValid());
+ EXPECT_FALSE(kurl.IsEmpty());
+ EXPECT_STREQ("http://www.google.com:8000/favicon.ico",
+ kurl.GetString().Utf8().data());
+
+ // Now let's test that giving an invalid replacement fails. Invalid
+ // protocols fail without modifying the URL, which should remain valid.
+ EXPECT_FALSE(kurl.SetProtocol("f/sj#@"));
+ EXPECT_TRUE(kurl.IsValid());
+}
+
+TEST(KURLTest, Valid_HTTP_FTP_URLsHaveHosts) {
+ KURL kurl("foo://www.google.com/");
+ EXPECT_TRUE(kurl.SetProtocol("http"));
+ EXPECT_TRUE(kurl.ProtocolIs("http"));
+ EXPECT_TRUE(kurl.ProtocolIsInHTTPFamily());
+ EXPECT_TRUE(kurl.IsValid());
+
+ EXPECT_TRUE(kurl.SetProtocol("https"));
+ EXPECT_TRUE(kurl.ProtocolIs("https"));
+ EXPECT_TRUE(kurl.IsValid());
+
+ EXPECT_TRUE(kurl.SetProtocol("ftp"));
+ EXPECT_TRUE(kurl.ProtocolIs("ftp"));
+ EXPECT_TRUE(kurl.IsValid());
+
+ kurl = KURL("http://");
+ EXPECT_FALSE(kurl.ProtocolIs("http"));
+
+ kurl = KURL("http://wide#鸡");
+ EXPECT_TRUE(kurl.ProtocolIs("http"));
+ EXPECT_EQ(kurl.Protocol(), "http");
+
+ kurl = KURL("https://foo");
+ EXPECT_TRUE(kurl.ProtocolIs("https"));
+
+ kurl = KURL("ftp://foo");
+ EXPECT_TRUE(kurl.ProtocolIs("ftp"));
+
+ kurl = KURL("http://host/");
+ EXPECT_TRUE(kurl.IsValid());
+ kurl.SetHost("");
+ EXPECT_FALSE(kurl.IsValid());
+
+ kurl = KURL("https://host/");
+ EXPECT_TRUE(kurl.IsValid());
+ kurl.SetHost("");
+ EXPECT_FALSE(kurl.IsValid());
+
+ kurl = KURL("ftp://host/");
+ EXPECT_TRUE(kurl.IsValid());
+ kurl.SetHost("");
+ EXPECT_FALSE(kurl.IsValid());
+
+ kurl = KURL("http:///noodles/pho.php");
+ EXPECT_STREQ("http://noodles/pho.php", kurl.GetString().Utf8().data());
+ EXPECT_STREQ("noodles", kurl.Host().Utf8().data());
+ EXPECT_TRUE(kurl.IsValid());
+
+ kurl = KURL("https://username:password@/");
+ EXPECT_FALSE(kurl.IsValid());
+
+ kurl = KURL("https://username:password@host/");
+ EXPECT_TRUE(kurl.IsValid());
+}
+
+TEST(KURLTest, Path) {
+ const char kInitial[] = "http://www.google.com/path/foo";
+ KURL kurl(kInitial);
+
+ // Clear by setting a null string.
+ String null_string;
+ EXPECT_TRUE(null_string.IsNull());
+ kurl.SetPath(null_string);
+ EXPECT_STREQ("http://www.google.com/", kurl.GetString().Utf8().data());
+}
+
+// Test that setting the query to different things works. Thq query is handled
+// a littler differently than some of the other components.
+TEST(KURLTest, Query) {
+ const char kInitial[] = "http://www.google.com/search?q=awesome";
+ KURL kurl(kInitial);
+
+ // Clear by setting a null string.
+ String null_string;
+ EXPECT_TRUE(null_string.IsNull());
+ kurl.SetQuery(null_string);
+ EXPECT_STREQ("http://www.google.com/search", kurl.GetString().Utf8().data());
+
+ // Clear by setting an empty string.
+ kurl = KURL(kInitial);
+ String empty_string("");
+ EXPECT_FALSE(empty_string.IsNull());
+ kurl.SetQuery(empty_string);
+ EXPECT_STREQ("http://www.google.com/search?", kurl.GetString().Utf8().data());
+
+ // Set with something that begins in a question mark.
+ const char kQuestion[] = "?foo=bar";
+ kurl.SetQuery(kQuestion);
+ EXPECT_STREQ("http://www.google.com/search?foo=bar",
+ kurl.GetString().Utf8().data());
+
+ // Set with something that doesn't begin in a question mark.
+ const char kQuery[] = "foo=bar";
+ kurl.SetQuery(kQuery);
+ EXPECT_STREQ("http://www.google.com/search?foo=bar",
+ kurl.GetString().Utf8().data());
+}
+
+TEST(KURLTest, Ref) {
+ const KURL kurl("http://foo/bar#baz");
+
+ // Basic ref setting.
+ KURL cur("http://foo/bar");
+ cur.SetFragmentIdentifier("asdf");
+ EXPECT_STREQ("http://foo/bar#asdf", cur.GetString().Utf8().data());
+ cur = kurl;
+ cur.SetFragmentIdentifier("asdf");
+ EXPECT_STREQ("http://foo/bar#asdf", cur.GetString().Utf8().data());
+
+ // Setting a ref to the empty string will set it to "#".
+ cur = KURL("http://foo/bar");
+ cur.SetFragmentIdentifier("");
+ EXPECT_STREQ("http://foo/bar#", cur.GetString().Utf8().data());
+ cur = kurl;
+ cur.SetFragmentIdentifier("");
+ EXPECT_STREQ("http://foo/bar#", cur.GetString().Utf8().data());
+
+ // Setting the ref to the null string will clear it altogether.
+ cur = KURL("http://foo/bar");
+ cur.SetFragmentIdentifier(String());
+ EXPECT_STREQ("http://foo/bar", cur.GetString().Utf8().data());
+ cur = kurl;
+ cur.SetFragmentIdentifier(String());
+ EXPECT_STREQ("http://foo/bar", cur.GetString().Utf8().data());
+}
+
+TEST(KURLTest, Empty) {
+ const KURL kurl;
+
+ // First test that regular empty URLs are the same.
+ EXPECT_TRUE(kurl.IsEmpty());
+ EXPECT_FALSE(kurl.IsValid());
+ EXPECT_TRUE(kurl.IsNull());
+ EXPECT_TRUE(kurl.GetString().IsNull());
+ EXPECT_TRUE(kurl.GetString().IsEmpty());
+
+ // Test resolving a null URL on an empty string.
+ const KURL kurl2(kurl, "");
+ EXPECT_FALSE(kurl2.IsNull());
+ EXPECT_TRUE(kurl2.IsEmpty());
+ EXPECT_FALSE(kurl2.IsValid());
+ EXPECT_FALSE(kurl2.GetString().IsNull());
+ EXPECT_TRUE(kurl2.GetString().IsEmpty());
+ EXPECT_FALSE(kurl2.GetString().IsNull());
+ EXPECT_TRUE(kurl2.GetString().IsEmpty());
+
+ // Resolve the null URL on a null string.
+ const KURL kurl22(kurl, String());
+ EXPECT_FALSE(kurl22.IsNull());
+ EXPECT_TRUE(kurl22.IsEmpty());
+ EXPECT_FALSE(kurl22.IsValid());
+ EXPECT_FALSE(kurl22.GetString().IsNull());
+ EXPECT_TRUE(kurl22.GetString().IsEmpty());
+ EXPECT_FALSE(kurl22.GetString().IsNull());
+ EXPECT_TRUE(kurl22.GetString().IsEmpty());
+
+ // Test non-hierarchical schemes resolving. The actual URLs will be different.
+ // WebKit's one will set the string to "something.gif" and we'll set it to an
+ // empty string. I think either is OK, so we just check our behavior.
+ const KURL kurl3(KURL("data:foo"), "something.gif");
+ EXPECT_TRUE(kurl3.IsEmpty());
+ EXPECT_FALSE(kurl3.IsValid());
+
+ // Test for weird isNull string input,
+ // see: http://bugs.webkit.org/show_bug.cgi?id=16487
+ const KURL kurl4(kurl.GetString());
+ EXPECT_TRUE(kurl4.IsEmpty());
+ EXPECT_FALSE(kurl4.IsValid());
+ EXPECT_TRUE(kurl4.GetString().IsNull());
+ EXPECT_TRUE(kurl4.GetString().IsEmpty());
+
+ // Resolving an empty URL on an invalid string.
+ const KURL kurl5("foo.js");
+ // We'll be empty in this case, but KURL won't be. Should be OK.
+ // EXPECT_EQ(kurl5.isEmpty(), kurl5.isEmpty());
+ // EXPECT_EQ(kurl5.getString().isEmpty(), kurl5.getString().isEmpty());
+ EXPECT_FALSE(kurl5.IsValid());
+ EXPECT_FALSE(kurl5.GetString().IsNull());
+
+ // Empty string as input
+ const KURL kurl6("");
+ EXPECT_TRUE(kurl6.IsEmpty());
+ EXPECT_FALSE(kurl6.IsValid());
+ EXPECT_FALSE(kurl6.GetString().IsNull());
+ EXPECT_TRUE(kurl6.GetString().IsEmpty());
+
+ // Non-empty but invalid C string as input.
+ const KURL kurl7("foo.js");
+ // WebKit will actually say this URL has the string "foo.js" but is invalid.
+ // We don't do that.
+ // EXPECT_EQ(kurl7.isEmpty(), kurl7.isEmpty());
+ EXPECT_FALSE(kurl7.IsValid());
+ EXPECT_FALSE(kurl7.GetString().IsNull());
+}
+
+TEST(KURLTest, UserPass) {
+ const char* src = "http://user:pass@google.com/";
+ KURL kurl(src);
+
+ // Clear just the username.
+ kurl.SetUser("");
+ EXPECT_EQ("http://:pass@google.com/", kurl.GetString());
+
+ // Clear just the password.
+ kurl = KURL(src);
+ kurl.SetPass("");
+ EXPECT_EQ("http://user@google.com/", kurl.GetString());
+
+ // Now clear both.
+ kurl.SetUser("");
+ EXPECT_EQ("http://google.com/", kurl.GetString());
+}
+
+TEST(KURLTest, Offsets) {
+ const char* src1 = "http://user:pass@google.com/foo/bar.html?baz=query#ref";
+ const KURL kurl1(src1);
+
+ EXPECT_EQ(17u, kurl1.HostStart());
+ EXPECT_EQ(27u, kurl1.HostEnd());
+ EXPECT_EQ(27u, kurl1.PathStart());
+ EXPECT_EQ(40u, kurl1.PathEnd());
+ EXPECT_EQ(32u, kurl1.PathAfterLastSlash());
+
+ const char* src2 = "http://google.com/foo/";
+ const KURL kurl2(src2);
+
+ EXPECT_EQ(7u, kurl2.HostStart());
+ EXPECT_EQ(17u, kurl2.HostEnd());
+ EXPECT_EQ(17u, kurl2.PathStart());
+ EXPECT_EQ(22u, kurl2.PathEnd());
+ EXPECT_EQ(22u, kurl2.PathAfterLastSlash());
+
+ const char* src3 = "javascript:foobar";
+ const KURL kurl3(src3);
+
+ EXPECT_EQ(11u, kurl3.HostStart());
+ EXPECT_EQ(11u, kurl3.HostEnd());
+ EXPECT_EQ(11u, kurl3.PathStart());
+ EXPECT_EQ(17u, kurl3.PathEnd());
+ EXPECT_EQ(11u, kurl3.PathAfterLastSlash());
+}
+
+TEST(KURLTest, DeepCopy) {
+ const char kUrl[] = "http://www.google.com/";
+ const KURL src(kUrl);
+ EXPECT_TRUE(src.GetString() ==
+ kUrl); // This really just initializes the cache.
+ const KURL dest = src.Copy();
+ EXPECT_TRUE(dest.GetString() ==
+ kUrl); // This really just initializes the cache.
+
+ // The pointers should be different for both UTF-8 and UTF-16.
+ EXPECT_NE(dest.GetString().Impl(), src.GetString().Impl());
+}
+
+TEST(KURLTest, DeepCopyInnerURL) {
+ const char kUrl[] = "filesystem:http://www.google.com/temporary/test.txt";
+ const char kInnerURL[] = "http://www.google.com/temporary";
+ const KURL src(kUrl);
+ EXPECT_TRUE(src.GetString() == kUrl);
+ EXPECT_TRUE(src.InnerURL()->GetString() == kInnerURL);
+ const KURL dest = src.Copy();
+ EXPECT_TRUE(dest.GetString() == kUrl);
+ EXPECT_TRUE(dest.InnerURL()->GetString() == kInnerURL);
+}
+
+TEST(KURLTest, LastPathComponent) {
+ const KURL url1("http://host/path/to/file.txt");
+ EXPECT_EQ("file.txt", url1.LastPathComponent());
+
+ const KURL invalid_utf8("http://a@9%aa%:/path/to/file.txt");
+ EXPECT_EQ(String(), invalid_utf8.LastPathComponent());
+}
+
+TEST(KURLTest, IsHierarchical) {
+ const KURL url1("http://host/path/to/file.txt");
+ EXPECT_TRUE(url1.IsHierarchical());
+
+ const KURL invalid_utf8("http://a@9%aa%:/path/to/file.txt");
+ EXPECT_FALSE(invalid_utf8.IsHierarchical());
+}
+
+TEST(KURLTest, PathAfterLastSlash) {
+ KURL url1("http://host/path/to/file.txt");
+ EXPECT_EQ(20u, url1.PathAfterLastSlash());
+
+ KURL invalid_utf8("http://a@9%aa%:/path/to/file.txt");
+ EXPECT_EQ(0u, invalid_utf8.PathAfterLastSlash());
+}
+
+TEST(KURLTest, ProtocolIsInHTTPFamily) {
+ const KURL url1("http://host/path/to/file.txt");
+ EXPECT_TRUE(url1.ProtocolIsInHTTPFamily());
+
+ const KURL invalid_utf8("http://a@9%aa%:/path/to/file.txt");
+ EXPECT_FALSE(invalid_utf8.ProtocolIsInHTTPFamily());
+}
+
+TEST(KURLTest, ProtocolIs) {
+ const KURL url1("foo://bar");
+ EXPECT_TRUE(url1.ProtocolIs("foo"));
+ EXPECT_FALSE(url1.ProtocolIs("foo-bar"));
+
+ const KURL url2("foo-bar:");
+ EXPECT_TRUE(url2.ProtocolIs("foo-bar"));
+ EXPECT_FALSE(url2.ProtocolIs("foo"));
+
+ const KURL invalid_utf8("http://a@9%aa%:");
+ EXPECT_FALSE(invalid_utf8.ProtocolIs("http"));
+
+ const KURL capital("HTTP://www.example.text");
+ EXPECT_TRUE(capital.ProtocolIs("http"));
+ EXPECT_EQ(capital.Protocol(), "http");
+}
+
+TEST(KURLTest, strippedForUseAsReferrer) {
+ struct ReferrerCase {
+ const char* input;
+ const char* output;
+ } referrer_cases[] = {
+ {"data:text/html;charset=utf-8,<html></html>", ""},
+ {"javascript:void(0);", ""},
+ {"about:config", ""},
+ {"https://www.google.com/", "https://www.google.com/"},
+ {"http://me@news.google.com:8888/", "http://news.google.com:8888/"},
+ {"http://:pass@news.google.com:8888/foo",
+ "http://news.google.com:8888/foo"},
+ {"http://me:pass@news.google.com:8888/", "http://news.google.com:8888/"},
+ {"https://www.google.com/a?f#b", "https://www.google.com/a?f"},
+ {"file:///tmp/test.html", ""},
+ {"https://www.google.com/#", "https://www.google.com/"},
+ };
+
+ for (size_t i = 0; i < WTF_ARRAY_LENGTH(referrer_cases); i++) {
+ const KURL kurl(referrer_cases[i].input);
+ String referrer = kurl.StrippedForUseAsReferrer();
+ EXPECT_STREQ(referrer_cases[i].output, referrer.Utf8().data());
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/weborigin/origin_access_entry.cc b/chromium/third_party/blink/renderer/platform/weborigin/origin_access_entry.cc
new file mode 100644
index 00000000000..d6c49e216d1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/weborigin/origin_access_entry.cc
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/renderer/platform/weborigin/origin_access_entry.h"
+
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_public_suffix_list.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+#include "url/third_party/mozilla/url_parse.h"
+#include "url/url_canon.h"
+
+namespace blink {
+
+namespace {
+
+// TODO(mkwst): This basically replicates GURL::HostIsIPAddress. If/when
+// we re-evaluate everything after merging the Blink and Chromium
+// repositories, perhaps we can just use that directly.
+bool HostIsIPAddress(const String& host) {
+ if (host.IsEmpty())
+ return false;
+
+ String protocol("https://");
+ KURL url(NullURL(), protocol + host + "/");
+ if (!url.IsValid())
+ return false;
+
+ url::RawCanonOutputT<char, 128> ignored_output;
+ url::CanonHostInfo host_info;
+ url::Component host_component(0,
+ static_cast<int>(url.Host().Utf8().length()));
+ url::CanonicalizeIPAddress(url.Host().Utf8().data(), host_component,
+ &ignored_output, &host_info);
+ return host_info.IsIPAddress();
+}
+
+bool IsSubdomainOfHost(const String& subdomain, const String& host) {
+ if (subdomain.length() <= host.length())
+ return false;
+
+ if (subdomain[subdomain.length() - host.length() - 1] != '.')
+ return false;
+
+ if (!subdomain.EndsWith(host))
+ return false;
+
+ return true;
+}
+} // namespace
+
+OriginAccessEntry::OriginAccessEntry(const String& protocol,
+ const String& host,
+ SubdomainSetting subdomain_setting)
+ : protocol_(protocol),
+ host_(host),
+ subdomain_settings_(subdomain_setting),
+ host_is_public_suffix_(false) {
+ DCHECK(subdomain_setting >= kAllowSubdomains ||
+ subdomain_setting <= kDisallowSubdomains);
+
+ host_is_ip_address_ = blink::HostIsIPAddress(host);
+
+ // Look for top-level domains, either with or without an additional dot.
+ if (!host_is_ip_address_) {
+ WebPublicSuffixList* suffix_list = Platform::Current()->PublicSuffixList();
+ if (!suffix_list)
+ return;
+
+ size_t public_suffix_length = suffix_list->GetPublicSuffixLength(host_);
+ if (host_.length() <= public_suffix_length + 1) {
+ host_is_public_suffix_ = true;
+ } else if (subdomain_setting == kAllowRegisterableDomains &&
+ public_suffix_length) {
+ // The "2" in the next line is 1 for the '.', plus a 1-char minimum label
+ // length.
+ const size_t dot =
+ host_.ReverseFind('.', host_.length() - public_suffix_length - 2);
+ if (dot == kNotFound)
+ registerable_domain_ = host;
+ else
+ registerable_domain_ = host.Substring(dot + 1);
+ }
+ }
+}
+
+OriginAccessEntry::MatchResult OriginAccessEntry::MatchesOrigin(
+ const SecurityOrigin& origin) const {
+ if (protocol_ != origin.Protocol())
+ return kDoesNotMatchOrigin;
+
+ return MatchesDomain(origin);
+}
+
+OriginAccessEntry::MatchResult OriginAccessEntry::MatchesDomain(
+ const SecurityOrigin& origin) const {
+ // Special case: Include subdomains and empty host means "all hosts, including
+ // ip addresses".
+ if (subdomain_settings_ != kDisallowSubdomains && host_.IsEmpty())
+ return kMatchesOrigin;
+
+ // Exact match.
+ if (host_ == origin.Host())
+ return kMatchesOrigin;
+
+ // Don't try to do subdomain matching on IP addresses.
+ if (host_is_ip_address_)
+ return kDoesNotMatchOrigin;
+
+ // Match subdomains.
+ switch (subdomain_settings_) {
+ case kDisallowSubdomains:
+ return kDoesNotMatchOrigin;
+
+ case kAllowSubdomains:
+ if (!IsSubdomainOfHost(origin.Host(), host_))
+ return kDoesNotMatchOrigin;
+ break;
+
+ case kAllowRegisterableDomains:
+ // Fall back to a simple subdomain check if no registerable domain could
+ // be found:
+ if (registerable_domain_.IsEmpty()) {
+ if (!IsSubdomainOfHost(origin.Host(), host_))
+ return kDoesNotMatchOrigin;
+ } else if (registerable_domain_ != origin.Host() &&
+ !IsSubdomainOfHost(origin.Host(), registerable_domain_)) {
+ return kDoesNotMatchOrigin;
+ }
+ break;
+ };
+
+ if (host_is_public_suffix_)
+ return kMatchesOriginButIsPublicSuffix;
+
+ return kMatchesOrigin;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/weborigin/origin_access_entry.h b/chromium/third_party/blink/renderer/platform/weborigin/origin_access_entry.h
new file mode 100644
index 00000000000..73212c78e66
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/weborigin/origin_access_entry.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_ORIGIN_ACCESS_ENTRY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_ORIGIN_ACCESS_ENTRY_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class SecurityOrigin;
+
+class PLATFORM_EXPORT OriginAccessEntry {
+ USING_FAST_MALLOC(OriginAccessEntry);
+
+ public:
+ enum SubdomainSetting {
+ // 'www.example.com' matches an OriginAccessEntry for 'example.com'
+ kAllowSubdomains,
+
+ // 'www.example.com' matches an OriginAccessEntry for 'not-www.example.com'
+ kAllowRegisterableDomains,
+
+ // 'www.example.com' does not match an OriginAccessEntry for 'example.com'
+ kDisallowSubdomains
+ };
+
+ enum MatchResult {
+ kMatchesOrigin,
+ kMatchesOriginButIsPublicSuffix,
+ kDoesNotMatchOrigin
+ };
+
+ // If host is empty string and SubdomainSetting is not DisallowSubdomains, the
+ // entry will match all domains in the specified protocol.
+ // IPv6 addresses must include brackets (e.g.
+ // '[2001:db8:85a3::8a2e:370:7334]', not '2001:db8:85a3::8a2e:370:7334').
+ OriginAccessEntry(const String& protocol,
+ const String& host,
+ SubdomainSetting);
+
+ // 'matchesOrigin' requires a protocol match (e.g. 'http' != 'https').
+ // 'matchesDomain' relaxes this constraint.
+ MatchResult MatchesOrigin(const SecurityOrigin&) const;
+ MatchResult MatchesDomain(const SecurityOrigin&) const;
+
+ const String& Protocol() const { return protocol_; }
+ const String& Host() const { return host_; }
+ SubdomainSetting SubdomainSettings() const { return subdomain_settings_; }
+ bool HostIsIPAddress() const { return host_is_ip_address_; }
+ const String& Registerable() const { return registerable_domain_; }
+
+ private:
+ String protocol_;
+ String host_;
+ String registerable_domain_;
+ SubdomainSetting subdomain_settings_;
+ bool host_is_ip_address_;
+ bool host_is_public_suffix_;
+};
+
+PLATFORM_EXPORT inline bool operator==(const OriginAccessEntry& a,
+ const OriginAccessEntry& b) {
+ return EqualIgnoringASCIICase(a.Protocol(), b.Protocol()) &&
+ EqualIgnoringASCIICase(a.Host(), b.Host()) &&
+ a.SubdomainSettings() == b.SubdomainSettings();
+}
+
+PLATFORM_EXPORT inline bool operator!=(const OriginAccessEntry& a,
+ const OriginAccessEntry& b) {
+ return !(a == b);
+}
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_ORIGIN_ACCESS_ENTRY_H_
diff --git a/chromium/third_party/blink/renderer/platform/weborigin/origin_access_entry_test.cc b/chromium/third_party/blink/renderer/platform/weborigin/origin_access_entry_test.cc
new file mode 100644
index 00000000000..e8ee05e2006
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/weborigin/origin_access_entry_test.cc
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/renderer/platform/weborigin/origin_access_entry.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_public_suffix_list.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+
+namespace blink {
+
+class OriginAccessEntryTestSuffixList : public blink::WebPublicSuffixList {
+ public:
+ size_t GetPublicSuffixLength(const blink::WebString&) override {
+ return length_;
+ }
+
+ void SetPublicSuffix(const blink::WebString& suffix) {
+ length_ = suffix.length();
+ }
+
+ private:
+ size_t length_;
+};
+
+class OriginAccessEntryTestPlatform : public TestingPlatformSupport {
+ public:
+ blink::WebPublicSuffixList* PublicSuffixList() override {
+ return &suffix_list_;
+ }
+
+ void SetPublicSuffix(const blink::WebString& suffix) {
+ suffix_list_.SetPublicSuffix(suffix);
+ }
+
+ private:
+ OriginAccessEntryTestSuffixList suffix_list_;
+};
+
+TEST(OriginAccessEntryTest, PublicSuffixListTest) {
+ ScopedTestingPlatformSupport<OriginAccessEntryTestPlatform> platform;
+ platform->SetPublicSuffix("com");
+
+ scoped_refptr<const SecurityOrigin> origin =
+ SecurityOrigin::CreateFromString("http://www.google.com");
+ OriginAccessEntry entry1("http", "google.com",
+ OriginAccessEntry::kAllowSubdomains);
+ OriginAccessEntry entry2("http", "hamster.com",
+ OriginAccessEntry::kAllowSubdomains);
+ OriginAccessEntry entry3("http", "com", OriginAccessEntry::kAllowSubdomains);
+ EXPECT_EQ(OriginAccessEntry::kMatchesOrigin, entry1.MatchesOrigin(*origin));
+ EXPECT_EQ(OriginAccessEntry::kDoesNotMatchOrigin,
+ entry2.MatchesOrigin(*origin));
+ EXPECT_EQ(OriginAccessEntry::kMatchesOriginButIsPublicSuffix,
+ entry3.MatchesOrigin(*origin));
+}
+
+TEST(OriginAccessEntryTest, AllowSubdomainsTest) {
+ struct TestCase {
+ const char* protocol;
+ const char* host;
+ const char* origin;
+ OriginAccessEntry::MatchResult expected_origin;
+ OriginAccessEntry::MatchResult expected_domain;
+ } inputs[] = {
+ {"http", "example.com", "http://example.com/",
+ OriginAccessEntry::kMatchesOrigin, OriginAccessEntry::kMatchesOrigin},
+ {"http", "example.com", "http://www.example.com/",
+ OriginAccessEntry::kMatchesOrigin, OriginAccessEntry::kMatchesOrigin},
+ {"http", "example.com", "http://www.www.example.com/",
+ OriginAccessEntry::kMatchesOrigin, OriginAccessEntry::kMatchesOrigin},
+ {"http", "www.example.com", "http://example.com/",
+ OriginAccessEntry::kDoesNotMatchOrigin,
+ OriginAccessEntry::kDoesNotMatchOrigin},
+ {"http", "www.example.com", "http://www.example.com/",
+ OriginAccessEntry::kMatchesOrigin, OriginAccessEntry::kMatchesOrigin},
+ {"http", "www.example.com", "http://www.www.example.com/",
+ OriginAccessEntry::kMatchesOrigin, OriginAccessEntry::kMatchesOrigin},
+ {"http", "com", "http://example.com/",
+ OriginAccessEntry::kMatchesOriginButIsPublicSuffix,
+ OriginAccessEntry::kMatchesOriginButIsPublicSuffix},
+ {"http", "com", "http://www.example.com/",
+ OriginAccessEntry::kMatchesOriginButIsPublicSuffix,
+ OriginAccessEntry::kMatchesOriginButIsPublicSuffix},
+ {"http", "com", "http://www.www.example.com/",
+ OriginAccessEntry::kMatchesOriginButIsPublicSuffix,
+ OriginAccessEntry::kMatchesOriginButIsPublicSuffix},
+ {"https", "example.com", "http://example.com/",
+ OriginAccessEntry::kDoesNotMatchOrigin,
+ OriginAccessEntry::kMatchesOrigin},
+ {"https", "example.com", "http://www.example.com/",
+ OriginAccessEntry::kDoesNotMatchOrigin,
+ OriginAccessEntry::kMatchesOrigin},
+ {"https", "example.com", "http://www.www.example.com/",
+ OriginAccessEntry::kDoesNotMatchOrigin,
+ OriginAccessEntry::kMatchesOrigin},
+ {"http", "example.com", "http://beispiel.de/",
+ OriginAccessEntry::kDoesNotMatchOrigin,
+ OriginAccessEntry::kDoesNotMatchOrigin},
+ {"http", "example.com", "https://beispiel.de/",
+ OriginAccessEntry::kDoesNotMatchOrigin,
+ OriginAccessEntry::kDoesNotMatchOrigin},
+ {"http", "", "http://example.com/", OriginAccessEntry::kMatchesOrigin,
+ OriginAccessEntry::kMatchesOrigin},
+ {"http", "", "http://beispiel.de/", OriginAccessEntry::kMatchesOrigin,
+ OriginAccessEntry::kMatchesOrigin},
+ {"https", "", "http://beispiel.de/",
+ OriginAccessEntry::kDoesNotMatchOrigin,
+ OriginAccessEntry::kMatchesOrigin},
+ };
+
+ ScopedTestingPlatformSupport<OriginAccessEntryTestPlatform> platform;
+ platform->SetPublicSuffix("com");
+
+ for (const auto& test : inputs) {
+ SCOPED_TRACE(testing::Message()
+ << "Host: " << test.host << ", Origin: " << test.origin);
+ scoped_refptr<const SecurityOrigin> origin_to_test =
+ SecurityOrigin::CreateFromString(test.origin);
+ OriginAccessEntry entry1(test.protocol, test.host,
+ OriginAccessEntry::kAllowSubdomains);
+ EXPECT_EQ(test.expected_origin, entry1.MatchesOrigin(*origin_to_test));
+ EXPECT_EQ(test.expected_domain, entry1.MatchesDomain(*origin_to_test));
+ }
+}
+
+TEST(OriginAccessEntryTest, AllowRegisterableDomainsTest) {
+ struct TestCase {
+ const char* protocol;
+ const char* host;
+ const char* origin;
+ OriginAccessEntry::MatchResult expected;
+ } inputs[] = {
+ {"http", "example.com", "http://example.com/",
+ OriginAccessEntry::kMatchesOrigin},
+ {"http", "example.com", "http://www.example.com/",
+ OriginAccessEntry::kMatchesOrigin},
+ {"http", "example.com", "http://www.www.example.com/",
+ OriginAccessEntry::kMatchesOrigin},
+ {"http", "www.example.com", "http://example.com/",
+ OriginAccessEntry::kMatchesOrigin},
+ {"http", "www.example.com", "http://www.example.com/",
+ OriginAccessEntry::kMatchesOrigin},
+ {"http", "www.example.com", "http://www.www.example.com/",
+ OriginAccessEntry::kMatchesOrigin},
+ {"http", "com", "http://example.com/",
+ OriginAccessEntry::kMatchesOriginButIsPublicSuffix},
+ {"http", "com", "http://www.example.com/",
+ OriginAccessEntry::kMatchesOriginButIsPublicSuffix},
+ {"http", "com", "http://www.www.example.com/",
+ OriginAccessEntry::kMatchesOriginButIsPublicSuffix},
+ {"https", "example.com", "http://example.com/",
+ OriginAccessEntry::kDoesNotMatchOrigin},
+ {"https", "example.com", "http://www.example.com/",
+ OriginAccessEntry::kDoesNotMatchOrigin},
+ {"https", "example.com", "http://www.www.example.com/",
+ OriginAccessEntry::kDoesNotMatchOrigin},
+ {"http", "example.com", "http://beispiel.de/",
+ OriginAccessEntry::kDoesNotMatchOrigin},
+ {"http", "", "http://example.com/", OriginAccessEntry::kMatchesOrigin},
+ {"http", "", "http://beispiel.de/", OriginAccessEntry::kMatchesOrigin},
+ {"https", "", "http://beispiel.de/",
+ OriginAccessEntry::kDoesNotMatchOrigin},
+ };
+
+ ScopedTestingPlatformSupport<OriginAccessEntryTestPlatform> platform;
+ platform->SetPublicSuffix("com");
+
+ for (const auto& test : inputs) {
+ scoped_refptr<const SecurityOrigin> origin_to_test =
+ SecurityOrigin::CreateFromString(test.origin);
+ OriginAccessEntry entry1(test.protocol, test.host,
+ OriginAccessEntry::kAllowRegisterableDomains);
+
+ SCOPED_TRACE(testing::Message()
+ << "Host: " << test.host << ", Origin: " << test.origin
+ << ", Domain: " << entry1.Registerable().Utf8().data());
+ EXPECT_EQ(test.expected, entry1.MatchesOrigin(*origin_to_test));
+ }
+}
+
+TEST(OriginAccessEntryTest, AllowRegisterableDomainsTestWithDottedSuffix) {
+ struct TestCase {
+ const char* protocol;
+ const char* host;
+ const char* origin;
+ OriginAccessEntry::MatchResult expected;
+ } inputs[] = {
+ {"http", "example.appspot.com", "http://example.appspot.com/",
+ OriginAccessEntry::kMatchesOrigin},
+ {"http", "example.appspot.com", "http://www.example.appspot.com/",
+ OriginAccessEntry::kMatchesOrigin},
+ {"http", "example.appspot.com", "http://www.www.example.appspot.com/",
+ OriginAccessEntry::kMatchesOrigin},
+ {"http", "www.example.appspot.com", "http://example.appspot.com/",
+ OriginAccessEntry::kMatchesOrigin},
+ {"http", "www.example.appspot.com", "http://www.example.appspot.com/",
+ OriginAccessEntry::kMatchesOrigin},
+ {"http", "www.example.appspot.com", "http://www.www.example.appspot.com/",
+ OriginAccessEntry::kMatchesOrigin},
+ {"http", "appspot.com", "http://example.appspot.com/",
+ OriginAccessEntry::kMatchesOriginButIsPublicSuffix},
+ {"http", "appspot.com", "http://www.example.appspot.com/",
+ OriginAccessEntry::kMatchesOriginButIsPublicSuffix},
+ {"http", "appspot.com", "http://www.www.example.appspot.com/",
+ OriginAccessEntry::kMatchesOriginButIsPublicSuffix},
+ {"https", "example.appspot.com", "http://example.appspot.com/",
+ OriginAccessEntry::kDoesNotMatchOrigin},
+ {"https", "example.appspot.com", "http://www.example.appspot.com/",
+ OriginAccessEntry::kDoesNotMatchOrigin},
+ {"https", "example.appspot.com", "http://www.www.example.appspot.com/",
+ OriginAccessEntry::kDoesNotMatchOrigin},
+ {"http", "example.appspot.com", "http://beispiel.de/",
+ OriginAccessEntry::kDoesNotMatchOrigin},
+ {"http", "", "http://example.appspot.com/",
+ OriginAccessEntry::kMatchesOrigin},
+ {"http", "", "http://beispiel.de/", OriginAccessEntry::kMatchesOrigin},
+ {"https", "", "http://beispiel.de/",
+ OriginAccessEntry::kDoesNotMatchOrigin},
+ };
+
+ ScopedTestingPlatformSupport<OriginAccessEntryTestPlatform> platform;
+ platform->SetPublicSuffix("appspot.com");
+
+ for (const auto& test : inputs) {
+ scoped_refptr<const SecurityOrigin> origin_to_test =
+ SecurityOrigin::CreateFromString(test.origin);
+ OriginAccessEntry entry1(test.protocol, test.host,
+ OriginAccessEntry::kAllowRegisterableDomains);
+
+ SCOPED_TRACE(testing::Message()
+ << "Host: " << test.host << ", Origin: " << test.origin
+ << ", Domain: " << entry1.Registerable().Utf8().data());
+ EXPECT_EQ(test.expected, entry1.MatchesOrigin(*origin_to_test));
+ }
+}
+
+TEST(OriginAccessEntryTest, DisallowSubdomainsTest) {
+ struct TestCase {
+ const char* protocol;
+ const char* host;
+ const char* origin;
+ OriginAccessEntry::MatchResult expected;
+ } inputs[] = {
+ {"http", "example.com", "http://example.com/",
+ OriginAccessEntry::kMatchesOrigin},
+ {"http", "example.com", "http://www.example.com/",
+ OriginAccessEntry::kDoesNotMatchOrigin},
+ {"http", "example.com", "http://www.www.example.com/",
+ OriginAccessEntry::kDoesNotMatchOrigin},
+ {"http", "com", "http://example.com/",
+ OriginAccessEntry::kDoesNotMatchOrigin},
+ {"http", "com", "http://www.example.com/",
+ OriginAccessEntry::kDoesNotMatchOrigin},
+ {"http", "com", "http://www.www.example.com/",
+ OriginAccessEntry::kDoesNotMatchOrigin},
+ {"https", "example.com", "http://example.com/",
+ OriginAccessEntry::kDoesNotMatchOrigin},
+ {"https", "example.com", "http://www.example.com/",
+ OriginAccessEntry::kDoesNotMatchOrigin},
+ {"https", "example.com", "http://www.www.example.com/",
+ OriginAccessEntry::kDoesNotMatchOrigin},
+ {"http", "example.com", "http://beispiel.de/",
+ OriginAccessEntry::kDoesNotMatchOrigin},
+ {"http", "", "http://example.com/",
+ OriginAccessEntry::kDoesNotMatchOrigin},
+ {"http", "", "http://beispiel.de/",
+ OriginAccessEntry::kDoesNotMatchOrigin},
+ {"https", "", "http://beispiel.de/",
+ OriginAccessEntry::kDoesNotMatchOrigin},
+ };
+
+ ScopedTestingPlatformSupport<OriginAccessEntryTestPlatform> platform;
+ platform->SetPublicSuffix("com");
+
+ for (const auto& test : inputs) {
+ SCOPED_TRACE(testing::Message()
+ << "Host: " << test.host << ", Origin: " << test.origin);
+ scoped_refptr<const SecurityOrigin> origin_to_test =
+ SecurityOrigin::CreateFromString(test.origin);
+ OriginAccessEntry entry1(test.protocol, test.host,
+ OriginAccessEntry::kDisallowSubdomains);
+ EXPECT_EQ(test.expected, entry1.MatchesOrigin(*origin_to_test));
+ }
+}
+
+TEST(OriginAccessEntryTest, IPAddressTest) {
+ struct TestCase {
+ const char* protocol;
+ const char* host;
+ bool is_ip_address;
+ } inputs[] = {
+ {"http", "1.1.1.1", true},
+ {"http", "1.1.1.1.1", false},
+ {"http", "example.com", false},
+ {"http", "hostname.that.ends.with.a.number1", false},
+ {"http", "2001:db8::1", false},
+ {"http", "[2001:db8::1]", true},
+ {"http", "2001:db8::a", false},
+ {"http", "[2001:db8::a]", true},
+ {"http", "", false},
+ };
+
+ ScopedTestingPlatformSupport<OriginAccessEntryTestPlatform> platform;
+ platform->SetPublicSuffix("com");
+
+ for (const auto& test : inputs) {
+ SCOPED_TRACE(testing::Message() << "Host: " << test.host);
+ OriginAccessEntry entry(test.protocol, test.host,
+ OriginAccessEntry::kDisallowSubdomains);
+ EXPECT_EQ(test.is_ip_address, entry.HostIsIPAddress()) << test.host;
+ }
+}
+
+TEST(OriginAccessEntryTest, IPAddressMatchingTest) {
+ struct TestCase {
+ const char* protocol;
+ const char* host;
+ const char* origin;
+ OriginAccessEntry::MatchResult expected;
+ } inputs[] = {
+ {"http", "192.0.0.123", "http://192.0.0.123/",
+ OriginAccessEntry::kMatchesOrigin},
+ {"http", "0.0.123", "http://192.0.0.123/",
+ OriginAccessEntry::kDoesNotMatchOrigin},
+ {"http", "0.123", "http://192.0.0.123/",
+ OriginAccessEntry::kDoesNotMatchOrigin},
+ {"http", "1.123", "http://192.0.0.123/",
+ OriginAccessEntry::kDoesNotMatchOrigin},
+ };
+
+ ScopedTestingPlatformSupport<OriginAccessEntryTestPlatform> platform;
+ platform->SetPublicSuffix("com");
+
+ for (const auto& test : inputs) {
+ SCOPED_TRACE(testing::Message()
+ << "Host: " << test.host << ", Origin: " << test.origin);
+ scoped_refptr<const SecurityOrigin> origin_to_test =
+ SecurityOrigin::CreateFromString(test.origin);
+ OriginAccessEntry entry1(test.protocol, test.host,
+ OriginAccessEntry::kAllowSubdomains);
+ EXPECT_EQ(test.expected, entry1.MatchesOrigin(*origin_to_test));
+
+ OriginAccessEntry entry2(test.protocol, test.host,
+ OriginAccessEntry::kDisallowSubdomains);
+ EXPECT_EQ(test.expected, entry2.MatchesOrigin(*origin_to_test));
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/weborigin/referrer.h b/chromium/third_party/blink/renderer/platform/weborigin/referrer.h
new file mode 100644
index 00000000000..e94952ed5eb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/weborigin/referrer.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_REFERRER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_REFERRER_H_
+
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/weborigin/referrer_policy.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+struct Referrer {
+ DISALLOW_NEW();
+ Referrer(const String& referrer, ReferrerPolicy referrer_policy)
+ : referrer(referrer), referrer_policy(referrer_policy) {
+ DCHECK(referrer == NoReferrer() || KURL(NullURL(), referrer).IsValid());
+ }
+ Referrer() : referrer_policy(kReferrerPolicyDefault) {}
+ static String NoReferrer() { return String(); }
+
+ AtomicString referrer;
+ ReferrerPolicy referrer_policy;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_REFERRER_H_
diff --git a/chromium/third_party/blink/renderer/platform/weborigin/referrer_policy.h b/chromium/third_party/blink/renderer/platform/weborigin/referrer_policy.h
new file mode 100644
index 00000000000..e8f6662721d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/weborigin/referrer_policy.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2012 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_REFERRER_POLICY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_REFERRER_POLICY_H_
+
+namespace blink {
+
+enum ReferrerPolicy : uint8_t {
+ // https://w3c.github.io/webappsec/specs/referrer-policy/#referrer-policy-state-unsafe-url
+ kReferrerPolicyAlways,
+ // The default policy, if no policy is explicitly set by the page.
+ kReferrerPolicyDefault,
+ // https://w3c.github.io/webappsec/specs/referrer-policy/#referrer-policy-state-no-referrer-when-downgrade
+ kReferrerPolicyNoReferrerWhenDowngrade,
+ // https://w3c.github.io/webappsec/specs/referrer-policy/#referrer-policy-state-no-referrer
+ kReferrerPolicyNever,
+ // https://w3c.github.io/webappsec/specs/referrer-policy/#referrer-policy-state-origin
+ kReferrerPolicyOrigin,
+ // https://w3c.github.io/webappsec/specs/referrer-policy/#referrer-policy-state-origin-when-cross-origin
+ kReferrerPolicyOriginWhenCrossOrigin,
+ // https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-strict-origin-when-cross-origin
+ // Also used as the default policy when reduced-referrer-granularity is
+ // enabled (not spec conformant).
+ kReferrerPolicyStrictOriginWhenCrossOrigin,
+ // https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-same-origin
+ kReferrerPolicySameOrigin,
+ // https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-strict-origin
+ kReferrerPolicyStrictOrigin,
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_REFERRER_POLICY_H_
diff --git a/chromium/third_party/blink/renderer/platform/weborigin/reporting_service_proxy_ptr_holder.h b/chromium/third_party/blink/renderer/platform/weborigin/reporting_service_proxy_ptr_holder.h
new file mode 100644
index 00000000000..c8776e5671a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/weborigin/reporting_service_proxy_ptr_holder.h
@@ -0,0 +1,81 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_REPORTING_SERVICE_PROXY_PTR_HOLDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_REPORTING_SERVICE_PROXY_PTR_HOLDER_H_
+
+#include "third_party/blink/public/platform/interface_provider.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/reporting.mojom-blink.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+
+namespace blink {
+
+class ReportingServiceProxyPtrHolder {
+ public:
+ ReportingServiceProxyPtrHolder() {
+ Platform::Current()->GetInterfaceProvider()->GetInterface(
+ mojo::MakeRequest(&reporting_service_proxy));
+ }
+ ~ReportingServiceProxyPtrHolder() = default;
+
+ void QueueInterventionReport(const KURL& url,
+ const String& message,
+ const String& source_file,
+ int line_number,
+ int column_number) {
+ if (reporting_service_proxy) {
+ reporting_service_proxy->QueueInterventionReport(
+ url, message ? message : "", source_file ? source_file : "",
+ line_number, column_number);
+ }
+ }
+
+ void QueueDeprecationReport(const KURL& url,
+ const String& id,
+ WTF::Time anticipatedRemoval,
+ const String& message,
+ const String& source_file,
+ int line_number,
+ int column_number) {
+ if (reporting_service_proxy) {
+ reporting_service_proxy->QueueDeprecationReport(
+ url, id, anticipatedRemoval, message ? message : "",
+ source_file ? source_file : "", line_number, column_number);
+ }
+ }
+
+ void QueueCspViolationReport(
+ const KURL& url,
+ const String& group,
+ const SecurityPolicyViolationEventInit& violation_data) {
+ if (reporting_service_proxy) {
+ reporting_service_proxy->QueueCspViolationReport(
+ url, group ? group : "default",
+ violation_data.documentURI() ? violation_data.documentURI() : "",
+ violation_data.referrer() ? violation_data.referrer() : "",
+ violation_data.violatedDirective()
+ ? violation_data.violatedDirective()
+ : "",
+ violation_data.effectiveDirective()
+ ? violation_data.effectiveDirective()
+ : "",
+ violation_data.originalPolicy() ? violation_data.originalPolicy()
+ : "",
+ violation_data.disposition() ? violation_data.disposition() : "",
+ violation_data.blockedURI() ? violation_data.blockedURI() : "",
+ violation_data.lineNumber(), violation_data.columnNumber(),
+ violation_data.sourceFile() ? violation_data.sourceFile() : "",
+ violation_data.statusCode(),
+ violation_data.sample() ? violation_data.sample() : "");
+ }
+ }
+
+ private:
+ mojom::blink::ReportingServiceProxyPtr reporting_service_proxy;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_REPORTING_SERVICE_PROXY_PTR_HOLDER_H_
diff --git a/chromium/third_party/blink/renderer/platform/weborigin/scheme_registry.cc b/chromium/third_party/blink/renderer/platform/weborigin/scheme_registry.cc
new file mode 100644
index 00000000000..8b511f95242
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/weborigin/scheme_registry.cc
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2010 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/weborigin/scheme_registry.h"
+
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+#include "third_party/blink/renderer/platform/wtf/thread_specific.h"
+#include "third_party/blink/renderer/platform/wtf/threading.h"
+#include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
+#include "url/url_util.h"
+
+namespace blink {
+
+namespace {
+
+struct PolicyAreasHashTraits : HashTraits<SchemeRegistry::PolicyAreas> {
+ static const bool kEmptyValueIsZero = true;
+ static SchemeRegistry::PolicyAreas EmptyValue() {
+ return SchemeRegistry::kPolicyAreaNone;
+ }
+};
+
+class URLSchemesRegistry final {
+ public:
+ URLSchemesRegistry()
+ : // For ServiceWorker schemes: HTTP is required because http://localhost
+ // is considered secure. Additional checks are performed to ensure that
+ // other http pages are filtered out.
+ service_worker_schemes({"http", "https"}),
+ fetch_api_schemes({"http", "https"}),
+ allowed_in_referrer_schemes({"http", "https"}) {
+ for (auto& scheme : url::GetLocalSchemes())
+ local_schemes.insert(scheme.c_str());
+ for (auto& scheme : url::GetSecureSchemes())
+ secure_schemes.insert(scheme.c_str());
+ for (auto& scheme : url::GetNoAccessSchemes())
+ schemes_with_unique_origins.insert(scheme.c_str());
+ for (auto& scheme : url::GetCORSEnabledSchemes())
+ cors_enabled_schemes.insert(scheme.c_str());
+ for (auto& scheme : url::GetCSPBypassingSchemes()) {
+ content_security_policy_bypassing_schemes.insert(
+ scheme.c_str(), SchemeRegistry::kPolicyAreaAll);
+ }
+ for (auto& scheme : url::GetEmptyDocumentSchemes())
+ empty_document_schemes.insert(scheme.c_str());
+ }
+ ~URLSchemesRegistry() = default;
+
+ URLSchemesSet local_schemes;
+ URLSchemesSet display_isolated_url_schemes;
+ URLSchemesSet secure_schemes;
+ URLSchemesSet schemes_with_unique_origins;
+ URLSchemesSet empty_document_schemes;
+ URLSchemesSet schemes_forbidden_from_domain_relaxation;
+ URLSchemesSet not_allowing_javascript_urls_schemes;
+ URLSchemesSet cors_enabled_schemes;
+ URLSchemesSet service_worker_schemes;
+ URLSchemesSet fetch_api_schemes;
+ URLSchemesSet first_party_when_top_level_schemes;
+ URLSchemesMap<SchemeRegistry::PolicyAreas, PolicyAreasHashTraits>
+ content_security_policy_bypassing_schemes;
+ URLSchemesSet secure_context_bypassing_schemes;
+ URLSchemesSet allowed_in_referrer_schemes;
+ URLSchemesSet wasm_eval_csp_schemes;
+
+ private:
+ friend const URLSchemesRegistry& GetURLSchemesRegistry();
+ friend URLSchemesRegistry& GetMutableURLSchemesRegistry();
+
+ static URLSchemesRegistry& GetInstance() {
+ DEFINE_STATIC_LOCAL(URLSchemesRegistry, schemes, ());
+ return schemes;
+ }
+};
+
+const URLSchemesRegistry& GetURLSchemesRegistry() {
+ return URLSchemesRegistry::GetInstance();
+}
+
+URLSchemesRegistry& GetMutableURLSchemesRegistry() {
+#if DCHECK_IS_ON()
+ DCHECK(WTF::IsBeforeThreadCreated());
+#endif
+ return URLSchemesRegistry::GetInstance();
+}
+
+} // namespace
+
+// Must be called before we create other threads to avoid racy static local
+// initialization.
+void SchemeRegistry::Initialize() {
+ GetURLSchemesRegistry();
+}
+
+void SchemeRegistry::RegisterURLSchemeAsLocal(const String& scheme) {
+ DCHECK_EQ(scheme, scheme.LowerASCII());
+ GetMutableURLSchemesRegistry().local_schemes.insert(scheme);
+}
+
+bool SchemeRegistry::ShouldTreatURLSchemeAsLocal(const String& scheme) {
+ DCHECK_EQ(scheme, scheme.LowerASCII());
+ if (scheme.IsEmpty())
+ return false;
+ return GetURLSchemesRegistry().local_schemes.Contains(scheme);
+}
+
+bool SchemeRegistry::ShouldTreatURLSchemeAsNoAccess(const String& scheme) {
+ DCHECK_EQ(scheme, scheme.LowerASCII());
+ if (scheme.IsEmpty())
+ return false;
+ return GetURLSchemesRegistry().schemes_with_unique_origins.Contains(scheme);
+}
+
+void SchemeRegistry::RegisterURLSchemeAsDisplayIsolated(const String& scheme) {
+ DCHECK_EQ(scheme, scheme.LowerASCII());
+ GetMutableURLSchemesRegistry().display_isolated_url_schemes.insert(scheme);
+}
+
+bool SchemeRegistry::ShouldTreatURLSchemeAsDisplayIsolated(
+ const String& scheme) {
+ DCHECK_EQ(scheme, scheme.LowerASCII());
+ if (scheme.IsEmpty())
+ return false;
+ return GetURLSchemesRegistry().display_isolated_url_schemes.Contains(scheme);
+}
+
+bool SchemeRegistry::ShouldTreatURLSchemeAsRestrictingMixedContent(
+ const String& scheme) {
+ DCHECK_EQ(scheme, scheme.LowerASCII());
+ return scheme == "https";
+}
+
+void SchemeRegistry::RegisterURLSchemeAsSecure(const String& scheme) {
+ DCHECK_EQ(scheme, scheme.LowerASCII());
+ GetMutableURLSchemesRegistry().secure_schemes.insert(scheme);
+}
+
+bool SchemeRegistry::ShouldTreatURLSchemeAsSecure(const String& scheme) {
+ DCHECK_EQ(scheme, scheme.LowerASCII());
+ if (scheme.IsEmpty())
+ return false;
+ return GetURLSchemesRegistry().secure_schemes.Contains(scheme);
+}
+
+bool SchemeRegistry::ShouldLoadURLSchemeAsEmptyDocument(const String& scheme) {
+ DCHECK_EQ(scheme, scheme.LowerASCII());
+ if (scheme.IsEmpty())
+ return false;
+ return GetURLSchemesRegistry().empty_document_schemes.Contains(scheme);
+}
+
+void SchemeRegistry::SetDomainRelaxationForbiddenForURLScheme(
+ bool forbidden,
+ const String& scheme) {
+ DCHECK_EQ(scheme, scheme.LowerASCII());
+ if (scheme.IsEmpty())
+ return;
+
+ if (forbidden) {
+ GetMutableURLSchemesRegistry()
+ .schemes_forbidden_from_domain_relaxation.insert(scheme);
+ } else {
+ GetMutableURLSchemesRegistry()
+ .schemes_forbidden_from_domain_relaxation.erase(scheme);
+ }
+}
+
+bool SchemeRegistry::IsDomainRelaxationForbiddenForURLScheme(
+ const String& scheme) {
+ DCHECK_EQ(scheme, scheme.LowerASCII());
+ if (scheme.IsEmpty())
+ return false;
+ return GetURLSchemesRegistry()
+ .schemes_forbidden_from_domain_relaxation.Contains(scheme);
+}
+
+bool SchemeRegistry::CanDisplayOnlyIfCanRequest(const String& scheme) {
+ DCHECK_EQ(scheme, scheme.LowerASCII());
+ return scheme == "blob" || scheme == "filesystem";
+}
+
+void SchemeRegistry::RegisterURLSchemeAsNotAllowingJavascriptURLs(
+ const String& scheme) {
+ DCHECK_EQ(scheme, scheme.LowerASCII());
+ GetMutableURLSchemesRegistry().not_allowing_javascript_urls_schemes.insert(
+ scheme);
+}
+
+void SchemeRegistry::RemoveURLSchemeAsNotAllowingJavascriptURLs(
+ const String& scheme) {
+ GetMutableURLSchemesRegistry().not_allowing_javascript_urls_schemes.erase(
+ scheme);
+}
+
+bool SchemeRegistry::ShouldTreatURLSchemeAsNotAllowingJavascriptURLs(
+ const String& scheme) {
+ DCHECK_EQ(scheme, scheme.LowerASCII());
+ if (scheme.IsEmpty())
+ return false;
+ return GetURLSchemesRegistry().not_allowing_javascript_urls_schemes.Contains(
+ scheme);
+}
+
+void SchemeRegistry::RegisterURLSchemeAsCORSEnabled(const String& scheme) {
+ DCHECK_EQ(scheme, scheme.LowerASCII());
+ GetMutableURLSchemesRegistry().cors_enabled_schemes.insert(scheme);
+}
+
+bool SchemeRegistry::ShouldTreatURLSchemeAsCORSEnabled(const String& scheme) {
+ DCHECK_EQ(scheme, scheme.LowerASCII());
+ if (scheme.IsEmpty())
+ return false;
+ return GetURLSchemesRegistry().cors_enabled_schemes.Contains(scheme);
+}
+
+String SchemeRegistry::ListOfCORSEnabledURLSchemes() {
+ StringBuilder builder;
+ bool add_separator = false;
+ for (const auto& scheme : GetURLSchemesRegistry().cors_enabled_schemes) {
+ if (add_separator)
+ builder.Append(", ");
+ else
+ add_separator = true;
+
+ builder.Append(scheme);
+ }
+ return builder.ToString();
+}
+
+bool SchemeRegistry::ShouldTreatURLSchemeAsLegacy(const String& scheme) {
+ return scheme == "ftp" || scheme == "gopher";
+}
+
+bool SchemeRegistry::ShouldTrackUsageMetricsForScheme(const String& scheme) {
+ // This SchemeRegistry is primarily used by Blink UseCounter, which aims to
+ // match the tracking policy of page_load_metrics (see
+ // pageTrackDecider::ShouldTrack() for more details).
+ // The scheme represents content which likely cannot be easily updated.
+ // Specifically this includes internal pages such as about, chrome-devtools,
+ // etc.
+ // "chrome-extension" is not included because they have a single deployment
+ // point (the webstore) and are designed specifically for Chrome.
+ // "data" is not included because real sites shouldn't be using it for
+ // top-level pages and Chrome does use it internally (eg. PluginPlaceholder).
+ // "file" is not included because file:// navigations have different loading
+ // behaviors.
+ return scheme == "http" || scheme == "https";
+}
+
+void SchemeRegistry::RegisterURLSchemeAsAllowingServiceWorkers(
+ const String& scheme) {
+ DCHECK_EQ(scheme, scheme.LowerASCII());
+ GetMutableURLSchemesRegistry().service_worker_schemes.insert(scheme);
+}
+
+bool SchemeRegistry::ShouldTreatURLSchemeAsAllowingServiceWorkers(
+ const String& scheme) {
+ DCHECK_EQ(scheme, scheme.LowerASCII());
+ if (scheme.IsEmpty())
+ return false;
+ return GetURLSchemesRegistry().service_worker_schemes.Contains(scheme);
+}
+
+void SchemeRegistry::RegisterURLSchemeAsSupportingFetchAPI(
+ const String& scheme) {
+ DCHECK_EQ(scheme, scheme.LowerASCII());
+ GetMutableURLSchemesRegistry().fetch_api_schemes.insert(scheme);
+}
+
+bool SchemeRegistry::ShouldTreatURLSchemeAsSupportingFetchAPI(
+ const String& scheme) {
+ DCHECK_EQ(scheme, scheme.LowerASCII());
+ if (scheme.IsEmpty())
+ return false;
+ return GetURLSchemesRegistry().fetch_api_schemes.Contains(scheme);
+}
+
+void SchemeRegistry::RegisterURLSchemeAsFirstPartyWhenTopLevel(
+ const String& scheme) {
+ DCHECK_EQ(scheme, scheme.LowerASCII());
+ GetMutableURLSchemesRegistry().first_party_when_top_level_schemes.insert(
+ scheme);
+}
+
+void SchemeRegistry::RemoveURLSchemeAsFirstPartyWhenTopLevel(
+ const String& scheme) {
+ DCHECK_EQ(scheme, scheme.LowerASCII());
+ GetMutableURLSchemesRegistry().first_party_when_top_level_schemes.erase(
+ scheme);
+}
+
+bool SchemeRegistry::ShouldTreatURLSchemeAsFirstPartyWhenTopLevel(
+ const String& scheme) {
+ DCHECK_EQ(scheme, scheme.LowerASCII());
+ if (scheme.IsEmpty())
+ return false;
+ return GetURLSchemesRegistry().first_party_when_top_level_schemes.Contains(
+ scheme);
+}
+
+void SchemeRegistry::RegisterURLSchemeAsAllowedForReferrer(
+ const String& scheme) {
+ DCHECK_EQ(scheme, scheme.LowerASCII());
+ GetMutableURLSchemesRegistry().allowed_in_referrer_schemes.insert(scheme);
+}
+
+void SchemeRegistry::RemoveURLSchemeAsAllowedForReferrer(const String& scheme) {
+ GetMutableURLSchemesRegistry().allowed_in_referrer_schemes.erase(scheme);
+}
+
+bool SchemeRegistry::ShouldTreatURLSchemeAsAllowedForReferrer(
+ const String& scheme) {
+ DCHECK_EQ(scheme, scheme.LowerASCII());
+ if (scheme.IsEmpty())
+ return false;
+ return GetURLSchemesRegistry().allowed_in_referrer_schemes.Contains(scheme);
+}
+
+void SchemeRegistry::RegisterURLSchemeAsBypassingContentSecurityPolicy(
+ const String& scheme,
+ PolicyAreas policy_areas) {
+ DCHECK_EQ(scheme, scheme.LowerASCII());
+ GetMutableURLSchemesRegistry()
+ .content_security_policy_bypassing_schemes.insert(scheme, policy_areas);
+}
+
+void SchemeRegistry::RemoveURLSchemeRegisteredAsBypassingContentSecurityPolicy(
+ const String& scheme) {
+ DCHECK_EQ(scheme, scheme.LowerASCII());
+ GetMutableURLSchemesRegistry()
+ .content_security_policy_bypassing_schemes.erase(scheme);
+}
+
+bool SchemeRegistry::SchemeShouldBypassContentSecurityPolicy(
+ const String& scheme,
+ PolicyAreas policy_areas) {
+ DCHECK_NE(policy_areas, kPolicyAreaNone);
+ if (scheme.IsEmpty() || policy_areas == kPolicyAreaNone)
+ return false;
+
+ // get() returns 0 (PolicyAreaNone) if there is no entry in the map.
+ // Thus by default, schemes do not bypass CSP.
+ return (GetURLSchemesRegistry().content_security_policy_bypassing_schemes.at(
+ scheme) &
+ policy_areas) == policy_areas;
+}
+
+void SchemeRegistry::RegisterURLSchemeBypassingSecureContextCheck(
+ const String& scheme) {
+ DCHECK_EQ(scheme, scheme.LowerASCII());
+ GetMutableURLSchemesRegistry().secure_context_bypassing_schemes.insert(
+ scheme);
+}
+
+bool SchemeRegistry::SchemeShouldBypassSecureContextCheck(
+ const String& scheme) {
+ if (scheme.IsEmpty())
+ return false;
+ DCHECK_EQ(scheme, scheme.LowerASCII());
+ return GetURLSchemesRegistry().secure_context_bypassing_schemes.Contains(
+ scheme);
+}
+
+void SchemeRegistry::RegisterURLSchemeAsAllowingWasmEvalCSP(
+ const String& scheme) {
+ DCHECK_EQ(scheme, scheme.LowerASCII());
+ GetMutableURLSchemesRegistry().wasm_eval_csp_schemes.insert(scheme);
+}
+
+bool SchemeRegistry::SchemeSupportsWasmEvalCSP(const String& scheme) {
+ if (scheme.IsEmpty())
+ return false;
+ DCHECK_EQ(scheme, scheme.LowerASCII());
+ return GetURLSchemesRegistry().wasm_eval_csp_schemes.Contains(scheme);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/weborigin/scheme_registry.h b/chromium/third_party/blink/renderer/platform/weborigin/scheme_registry.h
new file mode 100644
index 00000000000..09189d0ffc7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/weborigin/scheme_registry.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2010 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_SCHEME_REGISTRY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_SCHEME_REGISTRY_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+using URLSchemesSet = HashSet<String>;
+
+template <typename Mapped, typename MappedTraits>
+using URLSchemesMap = HashMap<String,
+ Mapped,
+ DefaultHash<String>::Hash,
+ HashTraits<String>,
+ MappedTraits>;
+
+class PLATFORM_EXPORT SchemeRegistry {
+ STATIC_ONLY(SchemeRegistry);
+
+ public:
+ static void Initialize();
+
+ static void RegisterURLSchemeAsLocal(const String&);
+ static bool ShouldTreatURLSchemeAsLocal(const String&);
+
+ static bool ShouldTreatURLSchemeAsRestrictingMixedContent(const String&);
+
+ // Subresources transported by secure schemes do not trigger mixed content
+ // warnings. For example, https and data are secure schemes because they
+ // cannot be corrupted by active network attackers.
+ static void RegisterURLSchemeAsSecure(const String&);
+ static bool ShouldTreatURLSchemeAsSecure(const String&);
+
+ static bool ShouldTreatURLSchemeAsNoAccess(const String&);
+
+ // Display-isolated schemes can only be displayed (in the sense of
+ // SecurityOrigin::canDisplay) by documents from the same scheme.
+ static void RegisterURLSchemeAsDisplayIsolated(const String&);
+ static bool ShouldTreatURLSchemeAsDisplayIsolated(const String&);
+
+ static bool ShouldLoadURLSchemeAsEmptyDocument(const String&);
+
+ static void SetDomainRelaxationForbiddenForURLScheme(bool forbidden,
+ const String&);
+ static bool IsDomainRelaxationForbiddenForURLScheme(const String&);
+
+ // Such schemes should delegate to SecurityOrigin::canRequest for any URL
+ // passed to SecurityOrigin::canDisplay.
+ static bool CanDisplayOnlyIfCanRequest(const String& scheme);
+
+ // Schemes against which javascript: URLs should not be allowed to run (stop
+ // bookmarklets from running on sensitive pages).
+ static void RegisterURLSchemeAsNotAllowingJavascriptURLs(
+ const String& scheme);
+ static void RemoveURLSchemeAsNotAllowingJavascriptURLs(const String& scheme);
+ static bool ShouldTreatURLSchemeAsNotAllowingJavascriptURLs(
+ const String& scheme);
+
+ // Allow non-HTTP schemes to be registered to allow CORS requests.
+ // This is not used in Chromium anymore but left here intentionally
+ // to allow other embedders of Blink to add more schemes
+ // to the CORS-enabled schemes list.
+ // As for now (Nov 2017) it is used by Electron.
+ static void RegisterURLSchemeAsCORSEnabled(const String& scheme);
+ static bool ShouldTreatURLSchemeAsCORSEnabled(const String& scheme);
+
+ // Serialize the registered schemes in a comma-separated list.
+ static String ListOfCORSEnabledURLSchemes();
+
+ // "Legacy" schemes (e.g. 'ftp:', 'gopher:') which we might want to treat
+ // differently from "webby" schemes.
+ static bool ShouldTreatURLSchemeAsLegacy(const String& scheme);
+
+ // Does the scheme represent a location relevant to web compatibility metrics?
+ static bool ShouldTrackUsageMetricsForScheme(const String& scheme);
+
+ // Schemes that can register a service worker.
+ static void RegisterURLSchemeAsAllowingServiceWorkers(const String& scheme);
+ static bool ShouldTreatURLSchemeAsAllowingServiceWorkers(
+ const String& scheme);
+
+ // HTTP-like schemes that are treated as supporting the Fetch API.
+ static void RegisterURLSchemeAsSupportingFetchAPI(const String& scheme);
+ static bool ShouldTreatURLSchemeAsSupportingFetchAPI(const String& scheme);
+
+ // Schemes which override the first-/third-party checks on a Document.
+ static void RegisterURLSchemeAsFirstPartyWhenTopLevel(const String& scheme);
+ static void RemoveURLSchemeAsFirstPartyWhenTopLevel(const String& scheme);
+ static bool ShouldTreatURLSchemeAsFirstPartyWhenTopLevel(
+ const String& scheme);
+
+ // Schemes that can be used in a referrer.
+ static void RegisterURLSchemeAsAllowedForReferrer(const String& scheme);
+ static void RemoveURLSchemeAsAllowedForReferrer(const String& scheme);
+ static bool ShouldTreatURLSchemeAsAllowedForReferrer(const String& scheme);
+
+ // Allow resources from some schemes to load on a page, regardless of its
+ // Content Security Policy.
+ enum PolicyAreas : uint32_t {
+ kPolicyAreaNone = 0,
+ kPolicyAreaImage = 1 << 0,
+ kPolicyAreaStyle = 1 << 1,
+ // Add more policy areas as needed by clients.
+ kPolicyAreaAll = ~static_cast<uint32_t>(0),
+ };
+ static void RegisterURLSchemeAsBypassingContentSecurityPolicy(
+ const String& scheme,
+ PolicyAreas = kPolicyAreaAll);
+ static void RemoveURLSchemeRegisteredAsBypassingContentSecurityPolicy(
+ const String& scheme);
+ static bool SchemeShouldBypassContentSecurityPolicy(
+ const String& scheme,
+ PolicyAreas = kPolicyAreaAll);
+
+ // Schemes which bypass Secure Context checks defined in
+ // https://w3c.github.io/webappsec-secure-contexts/#is-origin-trustworthy
+ static void RegisterURLSchemeBypassingSecureContextCheck(
+ const String& scheme);
+ static bool SchemeShouldBypassSecureContextCheck(const String& scheme);
+
+ // Schemes that can use 'wasm-eval'.
+ static void RegisterURLSchemeAsAllowingWasmEvalCSP(const String& scheme);
+ static bool SchemeSupportsWasmEvalCSP(const String& scheme);
+
+ private:
+ static const URLSchemesSet& LocalSchemes();
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_SCHEME_REGISTRY_H_
diff --git a/chromium/third_party/blink/renderer/platform/weborigin/scheme_registry_test.cc b/chromium/third_party/blink/renderer/platform/weborigin/scheme_registry_test.cc
new file mode 100644
index 00000000000..0ff1ebf987f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/weborigin/scheme_registry_test.cc
@@ -0,0 +1,72 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/weborigin/scheme_registry.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+namespace {
+
+const char kTestScheme[] = "test-scheme";
+const char kTestScheme2[] = "test-scheme-2";
+
+class SchemeRegistryTest : public testing::Test {
+ void TearDown() override {
+ SchemeRegistry::RemoveURLSchemeRegisteredAsBypassingContentSecurityPolicy(
+ kTestScheme);
+ }
+};
+
+TEST_F(SchemeRegistryTest, NoCSPBypass) {
+ EXPECT_FALSE(
+ SchemeRegistry::SchemeShouldBypassContentSecurityPolicy(kTestScheme));
+ EXPECT_FALSE(SchemeRegistry::SchemeShouldBypassContentSecurityPolicy(
+ kTestScheme, SchemeRegistry::kPolicyAreaImage));
+}
+
+TEST_F(SchemeRegistryTest, FullCSPBypass) {
+ SchemeRegistry::RegisterURLSchemeAsBypassingContentSecurityPolicy(
+ kTestScheme);
+ EXPECT_TRUE(
+ SchemeRegistry::SchemeShouldBypassContentSecurityPolicy(kTestScheme));
+ EXPECT_TRUE(SchemeRegistry::SchemeShouldBypassContentSecurityPolicy(
+ kTestScheme, SchemeRegistry::kPolicyAreaImage));
+ EXPECT_FALSE(
+ SchemeRegistry::SchemeShouldBypassContentSecurityPolicy(kTestScheme2));
+ EXPECT_FALSE(SchemeRegistry::SchemeShouldBypassContentSecurityPolicy(
+ kTestScheme2, SchemeRegistry::kPolicyAreaImage));
+}
+
+TEST_F(SchemeRegistryTest, PartialCSPBypass) {
+ SchemeRegistry::RegisterURLSchemeAsBypassingContentSecurityPolicy(
+ kTestScheme, SchemeRegistry::kPolicyAreaImage);
+ EXPECT_FALSE(
+ SchemeRegistry::SchemeShouldBypassContentSecurityPolicy(kTestScheme));
+ EXPECT_TRUE(SchemeRegistry::SchemeShouldBypassContentSecurityPolicy(
+ kTestScheme, SchemeRegistry::kPolicyAreaImage));
+ EXPECT_FALSE(SchemeRegistry::SchemeShouldBypassContentSecurityPolicy(
+ kTestScheme, SchemeRegistry::kPolicyAreaStyle));
+ EXPECT_FALSE(SchemeRegistry::SchemeShouldBypassContentSecurityPolicy(
+ kTestScheme2, SchemeRegistry::kPolicyAreaImage));
+}
+
+TEST_F(SchemeRegistryTest, BypassSecureContextCheck) {
+ const char* scheme1 = "http";
+ const char* scheme2 = "https";
+ const char* scheme3 = "random-scheme";
+
+ EXPECT_FALSE(SchemeRegistry::SchemeShouldBypassSecureContextCheck(scheme1));
+ EXPECT_FALSE(SchemeRegistry::SchemeShouldBypassSecureContextCheck(scheme2));
+ EXPECT_FALSE(SchemeRegistry::SchemeShouldBypassSecureContextCheck(scheme3));
+
+ SchemeRegistry::RegisterURLSchemeBypassingSecureContextCheck("random-scheme");
+
+ EXPECT_FALSE(SchemeRegistry::SchemeShouldBypassSecureContextCheck(scheme1));
+ EXPECT_FALSE(SchemeRegistry::SchemeShouldBypassSecureContextCheck(scheme2));
+ EXPECT_TRUE(SchemeRegistry::SchemeShouldBypassSecureContextCheck(scheme3));
+}
+
+} // namespace
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/weborigin/security_origin.cc b/chromium/third_party/blink/renderer/platform/weborigin/security_origin.cc
new file mode 100644
index 00000000000..c756e55f4bf
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/weborigin/security_origin.cc
@@ -0,0 +1,528 @@
+/*
+ * Copyright (C) 2007 Apple 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/weborigin/security_origin.h"
+
+#include <stdint.h>
+#include <memory>
+
+#include "net/base/url_util.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/weborigin/known_ports.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/weborigin/scheme_registry.h"
+#include "third_party/blink/renderer/platform/weborigin/security_policy.h"
+#include "third_party/blink/renderer/platform/weborigin/url_security_origin_map.h"
+#include "third_party/blink/renderer/platform/wtf/hex_number.h"
+#include "third_party/blink/renderer/platform/wtf/not_found.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "url/url_canon.h"
+#include "url/url_canon_ip.h"
+
+namespace blink {
+
+const uint16_t kInvalidPort = 0;
+
+static URLSecurityOriginMap* g_url_origin_map = nullptr;
+
+static SecurityOrigin* GetOriginFromMap(const KURL& url) {
+ if (g_url_origin_map)
+ return g_url_origin_map->GetOrigin(url);
+ return nullptr;
+}
+
+bool SecurityOrigin::ShouldUseInnerURL(const KURL& url) {
+ // FIXME: Blob URLs don't have inner URLs. Their form is
+ // "blob:<inner-origin>/<UUID>", so treating the part after "blob:" as a URL
+ // is incorrect.
+ if (url.ProtocolIs("blob"))
+ return true;
+ if (url.ProtocolIs("filesystem"))
+ return true;
+ return false;
+}
+
+// In general, extracting the inner URL varies by scheme. It just so happens
+// that all the URL schemes we currently support that use inner URLs for their
+// security origin can be parsed using this algorithm.
+KURL SecurityOrigin::ExtractInnerURL(const KURL& url) {
+ if (url.InnerURL())
+ return *url.InnerURL();
+ // FIXME: Update this callsite to use the innerURL member function when
+ // we finish implementing it.
+ return KURL(url.GetPath());
+}
+
+void SecurityOrigin::SetMap(URLSecurityOriginMap* map) {
+ g_url_origin_map = map;
+}
+
+static bool ShouldTreatAsUniqueOrigin(const KURL& url) {
+ if (!url.IsValid())
+ return true;
+
+ // FIXME: Do we need to unwrap the URL further?
+ KURL relevant_url;
+ if (SecurityOrigin::ShouldUseInnerURL(url)) {
+ relevant_url = SecurityOrigin::ExtractInnerURL(url);
+ if (!relevant_url.IsValid())
+ return true;
+ } else {
+ relevant_url = url;
+ }
+
+ // URLs with schemes that require an authority, but which don't have one,
+ // will have failed the isValid() test; e.g. valid HTTP URLs must have a
+ // host.
+ DCHECK(!((relevant_url.ProtocolIsInHTTPFamily() ||
+ relevant_url.ProtocolIs("ftp")) &&
+ relevant_url.Host().IsEmpty()));
+
+ if (SchemeRegistry::ShouldTreatURLSchemeAsNoAccess(relevant_url.Protocol()))
+ return true;
+
+ // This is the common case.
+ return false;
+}
+
+SecurityOrigin::SecurityOrigin(const KURL& url)
+ : protocol_(url.Protocol()),
+ host_(url.Host()),
+ port_(url.Port()),
+ effective_port_(url.Port()),
+ is_unique_(false),
+ universal_access_(false),
+ domain_was_set_in_dom_(false),
+ block_local_access_from_local_origin_(false),
+ is_unique_origin_potentially_trustworthy_(false) {
+ if (protocol_.IsNull())
+ protocol_ = g_empty_string;
+ if (host_.IsNull())
+ host_ = g_empty_string;
+
+ if (!effective_port_)
+ effective_port_ = DefaultPortForProtocol(protocol_);
+
+ // document.domain starts as m_host, but can be set by the DOM.
+ domain_ = host_;
+
+ if (IsDefaultPortForProtocol(port_, protocol_))
+ port_ = kInvalidPort;
+
+ // By default, only local SecurityOrigins can load local resources.
+ can_load_local_resources_ = IsLocal();
+}
+
+SecurityOrigin::SecurityOrigin()
+ : protocol_(g_empty_string),
+ host_(g_empty_string),
+ domain_(g_empty_string),
+ port_(kInvalidPort),
+ effective_port_(kInvalidPort),
+ is_unique_(true),
+ universal_access_(false),
+ domain_was_set_in_dom_(false),
+ can_load_local_resources_(false),
+ block_local_access_from_local_origin_(false),
+ is_unique_origin_potentially_trustworthy_(false) {}
+
+SecurityOrigin::SecurityOrigin(const SecurityOrigin* other)
+ : protocol_(other->protocol_.IsolatedCopy()),
+ host_(other->host_.IsolatedCopy()),
+ domain_(other->domain_.IsolatedCopy()),
+ port_(other->port_),
+ effective_port_(other->effective_port_),
+ is_unique_(other->is_unique_),
+ universal_access_(other->universal_access_),
+ domain_was_set_in_dom_(other->domain_was_set_in_dom_),
+ can_load_local_resources_(other->can_load_local_resources_),
+ block_local_access_from_local_origin_(
+ other->block_local_access_from_local_origin_),
+ is_unique_origin_potentially_trustworthy_(
+ other->is_unique_origin_potentially_trustworthy_) {}
+
+scoped_refptr<SecurityOrigin> SecurityOrigin::Create(const KURL& url) {
+ if (scoped_refptr<SecurityOrigin> origin = GetOriginFromMap(url))
+ return origin;
+
+ if (ShouldTreatAsUniqueOrigin(url))
+ return base::AdoptRef(new SecurityOrigin());
+
+ if (ShouldUseInnerURL(url))
+ return base::AdoptRef(new SecurityOrigin(ExtractInnerURL(url)));
+
+ return base::AdoptRef(new SecurityOrigin(url));
+}
+
+scoped_refptr<SecurityOrigin> SecurityOrigin::CreateUnique() {
+ scoped_refptr<SecurityOrigin> origin = base::AdoptRef(new SecurityOrigin());
+ DCHECK(origin->IsUnique());
+ return origin;
+}
+
+scoped_refptr<SecurityOrigin> SecurityOrigin::CreateFromUrlOrigin(
+ const url::Origin& origin) {
+ if (origin.unique())
+ return CreateUnique();
+
+ DCHECK(String::FromUTF8(origin.scheme().c_str()).ContainsOnlyASCII());
+ DCHECK(String::FromUTF8(origin.host().c_str()).ContainsOnlyASCII());
+
+ return Create(String::FromUTF8(origin.scheme().c_str()),
+ String::FromUTF8(origin.host().c_str()), origin.port());
+}
+
+url::Origin SecurityOrigin::ToUrlOrigin() const {
+ return IsUnique()
+ ? url::Origin()
+ : url::Origin::CreateFromNormalizedTuple(
+ StringUTF8Adaptor(protocol_).AsStringPiece().as_string(),
+ StringUTF8Adaptor(host_).AsStringPiece().as_string(),
+ effective_port_);
+}
+
+scoped_refptr<SecurityOrigin> SecurityOrigin::IsolatedCopy() const {
+ return base::AdoptRef(new SecurityOrigin(this));
+}
+
+void SecurityOrigin::SetDomainFromDOM(const String& new_domain) {
+ domain_was_set_in_dom_ = true;
+ domain_ = new_domain;
+}
+
+bool SecurityOrigin::IsSecure(const KURL& url) {
+ if (SchemeRegistry::ShouldTreatURLSchemeAsSecure(url.Protocol()))
+ return true;
+
+ // URLs that wrap inner URLs are secure if those inner URLs are secure.
+ if (ShouldUseInnerURL(url) && SchemeRegistry::ShouldTreatURLSchemeAsSecure(
+ ExtractInnerURL(url).Protocol()))
+ return true;
+
+ if (SecurityPolicy::IsUrlWhiteListedTrustworthy(url))
+ return true;
+
+ return false;
+}
+
+bool SecurityOrigin::SerializesAsNull() const {
+ if (IsUnique())
+ return true;
+
+ if (IsLocal() && block_local_access_from_local_origin_)
+ return true;
+
+ return false;
+}
+
+bool SecurityOrigin::CanAccess(const SecurityOrigin* other) const {
+ if (universal_access_)
+ return true;
+
+ if (this == other)
+ return true;
+
+ if (IsUnique() || other->IsUnique())
+ return false;
+
+ // document.domain handling, as per
+ // https://html.spec.whatwg.org/multipage/browsers.html#dom-document-domain:
+ //
+ // 1) Neither document has set document.domain. In this case, we insist
+ // that the scheme, host, and port of the URLs match.
+ //
+ // 2) Both documents have set document.domain. In this case, we insist
+ // that the documents have set document.domain to the same value and
+ // that the scheme of the URLs match. Ports do not need to match.
+ bool can_access = false;
+ if (protocol_ == other->protocol_) {
+ if (!domain_was_set_in_dom_ && !other->domain_was_set_in_dom_) {
+ if (host_ == other->host_ && port_ == other->port_)
+ can_access = true;
+ } else if (domain_was_set_in_dom_ && other->domain_was_set_in_dom_) {
+ // TODO(mkwst): If/when we ship this behavior, change this to check
+ // IsNull() rather than relying on string comparison.
+ // https://crbug.com/733150
+ if (domain_ == other->domain_ && domain_ != "null") {
+ can_access = true;
+ }
+ }
+ }
+
+ if (can_access && IsLocal())
+ can_access = PassesFileCheck(other);
+
+ return can_access;
+}
+
+bool SecurityOrigin::PassesFileCheck(const SecurityOrigin* other) const {
+ DCHECK(IsLocal());
+ DCHECK(other->IsLocal());
+
+ return !block_local_access_from_local_origin_ &&
+ !other->block_local_access_from_local_origin_;
+}
+
+bool SecurityOrigin::CanRequest(const KURL& url) const {
+ if (universal_access_)
+ return true;
+
+ if (GetOriginFromMap(url) == this)
+ return true;
+
+ if (IsUnique())
+ return false;
+
+ scoped_refptr<const SecurityOrigin> target_origin =
+ SecurityOrigin::Create(url);
+
+ if (target_origin->IsUnique())
+ return false;
+
+ // We call isSameSchemeHostPort here instead of canAccess because we want
+ // to ignore document.domain effects.
+ if (IsSameSchemeHostPort(target_origin.get()))
+ return true;
+
+ if (SecurityPolicy::IsAccessWhiteListed(this, target_origin.get()))
+ return true;
+
+ return false;
+}
+
+bool SecurityOrigin::CanReadContent(const KURL& url) const {
+ if (CanRequest(url))
+ return true;
+
+ // This function exists because we treat data URLs as having a unique opaque
+ // origin, see https://fetch.spec.whatwg.org/#main-fetch.
+ if (url.ProtocolIsData())
+ return true;
+
+ return false;
+}
+
+bool SecurityOrigin::CanDisplay(const KURL& url) const {
+ if (universal_access_)
+ return true;
+
+ String protocol = url.Protocol();
+ if (SchemeRegistry::CanDisplayOnlyIfCanRequest(protocol))
+ return CanRequest(url);
+
+ if (SchemeRegistry::ShouldTreatURLSchemeAsDisplayIsolated(protocol))
+ return protocol_ == protocol ||
+ SecurityPolicy::IsAccessToURLWhiteListed(this, url);
+
+ if (SchemeRegistry::ShouldTreatURLSchemeAsLocal(protocol))
+ return CanLoadLocalResources() ||
+ SecurityPolicy::IsAccessToURLWhiteListed(this, url);
+
+ return true;
+}
+
+bool SecurityOrigin::IsPotentiallyTrustworthy() const {
+ DCHECK_NE(protocol_, "data");
+ if (IsUnique())
+ return is_unique_origin_potentially_trustworthy_;
+
+ if (SchemeRegistry::ShouldTreatURLSchemeAsSecure(protocol_) || IsLocal() ||
+ IsLocalhost())
+ return true;
+
+ if (SecurityPolicy::IsOriginWhiteListedTrustworthy(*this))
+ return true;
+
+ return false;
+}
+
+// static
+String SecurityOrigin::IsPotentiallyTrustworthyErrorMessage() {
+ return "Only secure origins are allowed (see: https://goo.gl/Y0ZkNV).";
+}
+
+void SecurityOrigin::GrantLoadLocalResources() {
+ // Granting privileges to some, but not all, documents in a SecurityOrigin
+ // is a security hazard because the documents without the privilege can
+ // obtain the privilege by injecting script into the documents that have
+ // been granted the privilege.
+ can_load_local_resources_ = true;
+}
+
+void SecurityOrigin::GrantUniversalAccess() {
+ universal_access_ = true;
+}
+
+void SecurityOrigin::BlockLocalAccessFromLocalOrigin() {
+ DCHECK(IsLocal());
+ block_local_access_from_local_origin_ = true;
+}
+
+bool SecurityOrigin::IsLocal() const {
+ return SchemeRegistry::ShouldTreatURLSchemeAsLocal(protocol_);
+}
+
+bool SecurityOrigin::IsLocalhost() const {
+ // We special-case "[::1]" here because `net::HostStringIsLocalhost` expects a
+ // canonicalization that excludes the braces; a simple string comparison is
+ // simpler than trying to adjust Blink's canonicalization.
+ return host_ == "[::1]" || net::HostStringIsLocalhost(host_.Ascii().data());
+}
+
+String SecurityOrigin::ToString() const {
+ if (SerializesAsNull())
+ return "null";
+ return ToRawString();
+}
+
+AtomicString SecurityOrigin::ToAtomicString() const {
+ if (SerializesAsNull())
+ return AtomicString("null");
+
+ if (protocol_ == "file")
+ return AtomicString("file://");
+
+ StringBuilder result;
+ BuildRawString(result);
+ return result.ToAtomicString();
+}
+
+String SecurityOrigin::ToRawString() const {
+ if (protocol_ == "file")
+ return "file://";
+
+ StringBuilder result;
+ BuildRawString(result);
+ return result.ToString();
+}
+
+void SecurityOrigin::BuildRawString(StringBuilder& builder) const {
+ builder.Append(protocol_);
+ builder.Append("://");
+ builder.Append(host_);
+
+ if (port_) {
+ builder.Append(':');
+ builder.AppendNumber(port_);
+ }
+}
+
+scoped_refptr<SecurityOrigin> SecurityOrigin::CreateFromString(
+ const String& origin_string) {
+ return SecurityOrigin::Create(KURL(NullURL(), origin_string));
+}
+
+scoped_refptr<SecurityOrigin> SecurityOrigin::Create(const String& protocol,
+ const String& host,
+ uint16_t port) {
+ DCHECK_EQ(host, DecodeURLEscapeSequences(host));
+
+ String port_part = port ? ":" + String::Number(port) : String();
+ return Create(KURL(NullURL(), protocol + "://" + host + port_part + "/"));
+}
+
+bool SecurityOrigin::IsSameSchemeHostPort(const SecurityOrigin* other) const {
+ if (this == other)
+ return true;
+
+ if (IsUnique() || other->IsUnique())
+ return false;
+
+ if (host_ != other->host_)
+ return false;
+
+ if (protocol_ != other->protocol_)
+ return false;
+
+ if (port_ != other->port_)
+ return false;
+
+ if (IsLocal() && !PassesFileCheck(other))
+ return false;
+
+ return true;
+}
+
+bool SecurityOrigin::AreSameSchemeHostPort(const KURL& a, const KURL& b) {
+ scoped_refptr<const SecurityOrigin> origin_a = SecurityOrigin::Create(a);
+ scoped_refptr<const SecurityOrigin> origin_b = SecurityOrigin::Create(b);
+ return origin_b->IsSameSchemeHostPort(origin_a.get());
+}
+
+const KURL& SecurityOrigin::UrlWithUniqueSecurityOrigin() {
+ DCHECK(IsMainThread());
+ DEFINE_STATIC_LOCAL(const KURL, unique_security_origin_url, ("data:,"));
+ return unique_security_origin_url;
+}
+
+std::unique_ptr<SecurityOrigin::PrivilegeData>
+SecurityOrigin::CreatePrivilegeData() const {
+ std::unique_ptr<PrivilegeData> privilege_data =
+ std::make_unique<PrivilegeData>();
+ privilege_data->universal_access_ = universal_access_;
+ privilege_data->can_load_local_resources_ = can_load_local_resources_;
+ privilege_data->block_local_access_from_local_origin_ =
+ block_local_access_from_local_origin_;
+ return privilege_data;
+}
+
+void SecurityOrigin::TransferPrivilegesFrom(
+ std::unique_ptr<PrivilegeData> privilege_data) {
+ universal_access_ = privilege_data->universal_access_;
+ can_load_local_resources_ = privilege_data->can_load_local_resources_;
+ block_local_access_from_local_origin_ =
+ privilege_data->block_local_access_from_local_origin_;
+}
+
+void SecurityOrigin::SetUniqueOriginIsPotentiallyTrustworthy(
+ bool is_unique_origin_potentially_trustworthy) {
+ DCHECK(!is_unique_origin_potentially_trustworthy || IsUnique());
+ is_unique_origin_potentially_trustworthy_ =
+ is_unique_origin_potentially_trustworthy;
+}
+
+String SecurityOrigin::CanonicalizeHost(const String& host, bool* success) {
+ url::Component out_host;
+ url::RawCanonOutputT<char> canon_output;
+ if (host.Is8Bit()) {
+ StringUTF8Adaptor utf8(host);
+ *success =
+ url::CanonicalizeHost(utf8.Data(), url::Component(0, utf8.length()),
+ &canon_output, &out_host);
+ } else {
+ *success = url::CanonicalizeHost(host.Characters16(),
+ url::Component(0, host.length()),
+ &canon_output, &out_host);
+ }
+ return String::FromUTF8(canon_output.data(), canon_output.length());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/weborigin/security_origin.h b/chromium/third_party/blink/renderer/platform/weborigin/security_origin.h
new file mode 100644
index 00000000000..a1ed1f0c9cb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/weborigin/security_origin.h
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2007, 2008 Apple 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_SECURITY_ORIGIN_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_SECURITY_ORIGIN_H_
+
+#include <stdint.h>
+#include <memory>
+
+#include "base/gtest_prod_util.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
+#include "url/origin.h"
+
+namespace blink {
+
+class KURL;
+class URLSecurityOriginMap;
+
+class PLATFORM_EXPORT SecurityOrigin : public RefCounted<SecurityOrigin> {
+ WTF_MAKE_NONCOPYABLE(SecurityOrigin);
+
+ public:
+ static scoped_refptr<SecurityOrigin> Create(const KURL&);
+ static scoped_refptr<SecurityOrigin> CreateUnique();
+
+ static scoped_refptr<SecurityOrigin> CreateFromString(const String&);
+ static scoped_refptr<SecurityOrigin> Create(const String& protocol,
+ const String& host,
+ uint16_t port);
+ static scoped_refptr<SecurityOrigin> CreateFromUrlOrigin(const url::Origin&);
+ url::Origin ToUrlOrigin() const;
+
+ static void SetMap(URLSecurityOriginMap*);
+
+ // Some URL schemes use nested URLs for their security context. For example,
+ // filesystem URLs look like the following:
+ //
+ // filesystem:http://example.com/temporary/path/to/file.png
+ //
+ // We're supposed to use "http://example.com" as the origin.
+ //
+ // Generally, we add URL schemes to this list when WebKit support them. For
+ // example, we don't include the "jar" scheme, even though Firefox
+ // understands that "jar" uses an inner URL for it's security origin.
+ static bool ShouldUseInnerURL(const KURL&);
+ static KURL ExtractInnerURL(const KURL&);
+
+ // Create a deep copy of this SecurityOrigin. This method is useful
+ // when marshalling a SecurityOrigin to another thread.
+ scoped_refptr<SecurityOrigin> IsolatedCopy() const;
+
+ // Set the domain property of this security origin to newDomain. This
+ // function does not check whether newDomain is a suffix of the current
+ // domain. The caller is responsible for validating newDomain.
+ void SetDomainFromDOM(const String& new_domain);
+ bool DomainWasSetInDOM() const { return domain_was_set_in_dom_; }
+
+ String Protocol() const { return protocol_; }
+ String Host() const { return host_; }
+ String Domain() const { return domain_; }
+
+ // Returns 0 if the effective port of this origin is the default for its
+ // scheme.
+ uint16_t Port() const { return port_; }
+ // Returns the effective port, even if it is the default port for the
+ // scheme (e.g. "http" => 80).
+ uint16_t EffectivePort() const { return effective_port_; }
+
+ // Returns true if a given URL is secure, based either directly on its
+ // own protocol, or, when relevant, on the protocol of its "inner URL"
+ // Protocols like blob: and filesystem: fall into this latter category.
+ static bool IsSecure(const KURL&);
+
+ // Returns true if this SecurityOrigin can script objects in the given
+ // SecurityOrigin. For example, call this function before allowing
+ // script from one security origin to read or write objects from
+ // another SecurityOrigin.
+ bool CanAccess(const SecurityOrigin*) const;
+
+ // Returns true if this SecurityOrigin can read content retrieved from
+ // the given URL. For example, call this function before issuing
+ // XMLHttpRequests.
+ bool CanRequest(const KURL&) const;
+
+ // Returns true if content from this URL can be read without CORS from this
+ // security origin. For example, call this function before drawing an image
+ // onto an HTML canvas element with the drawImage API.
+ bool CanReadContent(const KURL&) const;
+
+ // Returns true if |document| can display content from the given URL (e.g.,
+ // in an iframe or as an image). For example, web sites generally cannot
+ // display content from the user's files system.
+ bool CanDisplay(const KURL&) const;
+
+ // Returns true if the origin loads resources either from the local
+ // machine or over the network from a
+ // cryptographically-authenticated origin, as described in
+ // https://w3c.github.io/webappsec-secure-contexts/#is-origin-trustworthy
+ bool IsPotentiallyTrustworthy() const;
+
+ // Returns a human-readable error message describing that a non-secure
+ // origin's access to a feature is denied.
+ static String IsPotentiallyTrustworthyErrorMessage();
+
+ // Returns true if this SecurityOrigin can load local resources, such
+ // as images, iframes, and style sheets, and can link to local URLs.
+ // For example, call this function before creating an iframe to a
+ // file:// URL.
+ //
+ // Note: A SecurityOrigin might be allowed to load local resources
+ // without being able to issue an XMLHttpRequest for a local URL.
+ // To determine whether the SecurityOrigin can issue an
+ // XMLHttpRequest for a URL, call canRequest(url).
+ bool CanLoadLocalResources() const { return can_load_local_resources_; }
+
+ // Explicitly grant the ability to load local resources to this
+ // SecurityOrigin.
+ //
+ // Note: This method exists only to support backwards compatibility
+ // with older versions of WebKit.
+ void GrantLoadLocalResources();
+
+ // Explicitly grant the ability to access every other SecurityOrigin.
+ //
+ // WARNING: This is an extremely powerful ability. Use with caution!
+ void GrantUniversalAccess();
+ bool IsGrantedUniversalAccess() const { return universal_access_; }
+
+ bool CanAccessDatabase() const { return !IsUnique(); }
+ bool CanAccessLocalStorage() const { return !IsUnique(); }
+ bool CanAccessSharedWorkers() const { return !IsUnique(); }
+ bool CanAccessServiceWorkers() const { return !IsUnique(); }
+ bool CanAccessCookies() const { return !IsUnique(); }
+ bool CanAccessPasswordManager() const { return !IsUnique(); }
+ bool CanAccessFileSystem() const { return !IsUnique(); }
+ bool CanAccessCacheStorage() const { return !IsUnique(); }
+ bool CanAccessLocks() const { return !IsUnique(); }
+
+ // Technically, we should always allow access to sessionStorage, but we
+ // currently don't handle creating a sessionStorage area for unique
+ // origins.
+ bool CanAccessSessionStorage() const { return !IsUnique(); }
+
+ // The local SecurityOrigin is the most privileged SecurityOrigin.
+ // The local SecurityOrigin can script any document, navigate to local
+ // resources, and can set arbitrary headers on XMLHttpRequests.
+ bool IsLocal() const;
+
+ // Returns true if the host is one of 127.0.0.1/8, ::1/128, or "localhost".
+ bool IsLocalhost() const;
+
+ // The origin is a globally unique identifier assigned when the Document is
+ // created. http://www.whatwg.org/specs/web-apps/current-work/#sandboxOrigin
+ //
+ // There's a subtle difference between a unique origin and an origin that
+ // has the SandboxOrigin flag set. The latter implies the former, and, in
+ // addition, the SandboxOrigin flag is inherited by iframes.
+ bool IsUnique() const { return is_unique_; }
+
+ // By default 'file:' URLs may access other 'file:' URLs. This method
+ // denies access. If either SecurityOrigin sets this flag, the access
+ // check will fail.
+ void BlockLocalAccessFromLocalOrigin();
+
+ // Convert this SecurityOrigin into a string. The string
+ // representation of a SecurityOrigin is similar to a URL, except it
+ // lacks a path component. The string representation does not encode
+ // the value of the SecurityOrigin's domain property.
+ //
+ // When using the string value, it's important to remember that it might be
+ // "null". This happens when this SecurityOrigin is unique. For example,
+ // this SecurityOrigin might have come from a sandboxed iframe, the
+ // SecurityOrigin might be empty, or we might have explicitly decided that
+ // we shouldTreatURLSchemeAsNoAccess.
+ String ToString() const;
+ AtomicString ToAtomicString() const;
+
+ // Similar to ToString(), but does not take into account any factors that
+ // could make the string return "null".
+ String ToRawString() const;
+
+ // This method checks for equality, ignoring the value of document.domain
+ // (and whether it was set) but considering the host. It is used for
+ // postMessage.
+ bool IsSameSchemeHostPort(const SecurityOrigin*) const;
+
+ static bool AreSameSchemeHostPort(const KURL& a, const KURL& b);
+
+ static const KURL& UrlWithUniqueSecurityOrigin();
+
+ // Transfer origin privileges from another security origin.
+ // The following privileges are currently copied over:
+ //
+ // - Grant universal access.
+ // - Grant loading of local resources.
+ // - Use path-based file:// origins.
+ struct PrivilegeData {
+ bool universal_access_;
+ bool can_load_local_resources_;
+ bool block_local_access_from_local_origin_;
+ };
+ std::unique_ptr<PrivilegeData> CreatePrivilegeData() const;
+ void TransferPrivilegesFrom(std::unique_ptr<PrivilegeData>);
+
+ void SetUniqueOriginIsPotentiallyTrustworthy(
+ bool is_unique_origin_potentially_trustworthy);
+
+ // Only used for document.domain setting. The method should probably be moved
+ // if we need it for something more general.
+ static String CanonicalizeHost(const String& host, bool* success);
+
+ private:
+ friend class SecurityOriginTest;
+
+ SecurityOrigin();
+ explicit SecurityOrigin(const KURL&);
+ explicit SecurityOrigin(const SecurityOrigin*);
+
+ // FIXME: Rename this function to something more semantic.
+ bool PassesFileCheck(const SecurityOrigin*) const;
+ void BuildRawString(StringBuilder&) const;
+
+ bool SerializesAsNull() const;
+
+ String protocol_;
+ String host_;
+ String domain_;
+ uint16_t port_;
+ uint16_t effective_port_;
+ const bool is_unique_;
+ bool universal_access_;
+ bool domain_was_set_in_dom_;
+ bool can_load_local_resources_;
+ bool block_local_access_from_local_origin_;
+ bool is_unique_origin_potentially_trustworthy_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_SECURITY_ORIGIN_H_
diff --git a/chromium/third_party/blink/renderer/platform/weborigin/security_origin_hash.h b/chromium/third_party/blink/renderer/platform/weborigin/security_origin_hash.h
new file mode 100644
index 00000000000..ba725eb87c9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/weborigin/security_origin_hash.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2008 Apple 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_SECURITY_ORIGIN_HASH_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_SECURITY_ORIGIN_HASH_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+struct SecurityOriginHash {
+ STATIC_ONLY(SecurityOriginHash);
+ static unsigned GetHash(const SecurityOrigin* origin) {
+ unsigned hash_codes[3] = {
+ origin->Protocol().Impl() ? origin->Protocol().Impl()->GetHash() : 0,
+ origin->Host().Impl() ? origin->Host().Impl()->GetHash() : 0,
+ origin->Port()};
+ return StringHasher::HashMemory<sizeof(hash_codes)>(hash_codes);
+ }
+ static unsigned GetHash(const scoped_refptr<const SecurityOrigin>& origin) {
+ return GetHash(origin.get());
+ }
+
+ static bool Equal(const SecurityOrigin* a, const SecurityOrigin* b) {
+ if (!a || !b)
+ return a == b;
+
+ if (a == b)
+ return true;
+
+ if (!a->IsSameSchemeHostPort(b))
+ return false;
+
+ if (a->DomainWasSetInDOM() != b->DomainWasSetInDOM())
+ return false;
+
+ if (a->DomainWasSetInDOM() && a->Domain() != b->Domain())
+ return false;
+
+ return true;
+ }
+ static bool Equal(const SecurityOrigin* a,
+ const scoped_refptr<const SecurityOrigin>& b) {
+ return Equal(a, b.get());
+ }
+ static bool Equal(const scoped_refptr<const SecurityOrigin>& a,
+ const SecurityOrigin* b) {
+ return Equal(a.get(), b);
+ }
+ static bool Equal(const scoped_refptr<const SecurityOrigin>& a,
+ const scoped_refptr<const SecurityOrigin>& b) {
+ return Equal(a.get(), b.get());
+ }
+
+ static const bool safe_to_compare_to_empty_or_deleted = false;
+};
+
+} // namespace blink
+
+namespace WTF {
+
+template <>
+struct DefaultHash<scoped_refptr<const blink::SecurityOrigin>> {
+ typedef blink::SecurityOriginHash Hash;
+};
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_SECURITY_ORIGIN_HASH_H_
diff --git a/chromium/third_party/blink/renderer/platform/weborigin/security_origin_test.cc b/chromium/third_party/blink/renderer/platform/weborigin/security_origin_test.cc
new file mode 100644
index 00000000000..a94433044c1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/weborigin/security_origin_test.cc
@@ -0,0 +1,490 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/renderer/platform/weborigin/security_origin.h"
+
+#include <stdint.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/blob/blob_url.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/weborigin/security_policy.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "url/gurl.h"
+#include "url/url_util.h"
+
+namespace blink {
+
+const uint16_t kMaxAllowedPort = UINT16_MAX;
+
+class SecurityOriginTest : public testing::Test {};
+
+TEST_F(SecurityOriginTest, ValidPortsCreateNonUniqueOrigins) {
+ uint16_t ports[] = {0, 80, 443, 5000, kMaxAllowedPort};
+
+ for (size_t i = 0; i < WTF_ARRAY_LENGTH(ports); ++i) {
+ scoped_refptr<const SecurityOrigin> origin =
+ SecurityOrigin::Create("http", "example.com", ports[i]);
+ EXPECT_FALSE(origin->IsUnique())
+ << "Port " << ports[i] << " should not have generated a unique origin.";
+ }
+}
+
+TEST_F(SecurityOriginTest, LocalAccess) {
+ scoped_refptr<SecurityOrigin> file1 =
+ SecurityOrigin::CreateFromString("file:///etc/passwd");
+ scoped_refptr<const SecurityOrigin> file2 =
+ SecurityOrigin::CreateFromString("file:///etc/shadow");
+
+ EXPECT_TRUE(file1->IsSameSchemeHostPort(file1.get()));
+ EXPECT_TRUE(file1->IsSameSchemeHostPort(file2.get()));
+ EXPECT_TRUE(file2->IsSameSchemeHostPort(file1.get()));
+
+ EXPECT_TRUE(file1->CanAccess(file1.get()));
+ EXPECT_TRUE(file1->CanAccess(file2.get()));
+ EXPECT_TRUE(file2->CanAccess(file1.get()));
+
+ // Block |file1|'s access to local origins. It should now be same-origin
+ // with itself, but shouldn't have access to |file2|.
+ file1->BlockLocalAccessFromLocalOrigin();
+ EXPECT_TRUE(file1->IsSameSchemeHostPort(file1.get()));
+ EXPECT_FALSE(file1->IsSameSchemeHostPort(file2.get()));
+ EXPECT_FALSE(file2->IsSameSchemeHostPort(file1.get()));
+
+ EXPECT_TRUE(file1->CanAccess(file1.get()));
+ EXPECT_FALSE(file1->CanAccess(file2.get()));
+ EXPECT_FALSE(file2->CanAccess(file1.get()));
+}
+
+TEST_F(SecurityOriginTest, IsPotentiallyTrustworthy) {
+ struct TestCase {
+ bool access_granted;
+ const char* url;
+ };
+
+ TestCase inputs[] = {
+ // Access is granted to webservers running on localhost.
+ {true, "http://localhost"},
+ {true, "http://LOCALHOST"},
+ {true, "http://localhost:100"},
+ {true, "http://a.localhost"},
+ {true, "http://127.0.0.1"},
+ {true, "http://127.0.0.2"},
+ {true, "http://127.1.0.2"},
+ {true, "http://0177.00.00.01"},
+ {true, "http://[::1]"},
+ {true, "http://[0:0::1]"},
+ {true, "http://[0:0:0:0:0:0:0:1]"},
+ {true, "http://[::1]:21"},
+ {true, "http://127.0.0.1:8080"},
+ {true, "ftp://127.0.0.1"},
+ {true, "ftp://127.0.0.1:443"},
+ {true, "ws://127.0.0.1"},
+
+ // Access is denied to non-localhost over HTTP
+ {false, "http://[1::]"},
+ {false, "http://[::2]"},
+ {false, "http://[1::1]"},
+ {false, "http://[1:2::3]"},
+ {false, "http://[::127.0.0.1]"},
+ {false, "http://a.127.0.0.1"},
+ {false, "http://127.0.0.1.b"},
+ {false, "http://localhost.a"},
+
+ // Access is granted to all secure transports.
+ {true, "https://foobar.com"},
+ {true, "wss://foobar.com"},
+
+ // Access is denied to insecure transports.
+ {false, "ftp://foobar.com"},
+ {false, "http://foobar.com"},
+ {false, "http://foobar.com:443"},
+ {false, "ws://foobar.com"},
+
+ // Access is granted to local files
+ {true, "file:///home/foobar/index.html"},
+
+ // blob: URLs must look to the inner URL's origin, and apply the same
+ // rules as above. Spot check some of them
+ {true, "blob:http://localhost:1000/578223a1-8c13-17b3-84d5-eca045ae384a"},
+ {true, "blob:https://foopy:99/578223a1-8c13-17b3-84d5-eca045ae384a"},
+ {false, "blob:http://baz:99/578223a1-8c13-17b3-84d5-eca045ae384a"},
+ {false, "blob:ftp://evil:99/578223a1-8c13-17b3-84d5-eca045ae384a"},
+
+ // filesystem: URLs work the same as blob: URLs, and look to the inner
+ // URL for security origin.
+ {true, "filesystem:http://localhost:1000/foo"},
+ {true, "filesystem:https://foopy:99/foo"},
+ {false, "filesystem:http://baz:99/foo"},
+ {false, "filesystem:ftp://evil:99/foo"},
+ };
+
+ for (size_t i = 0; i < WTF_ARRAY_LENGTH(inputs); ++i) {
+ SCOPED_TRACE(inputs[i].url);
+ scoped_refptr<const SecurityOrigin> origin =
+ SecurityOrigin::CreateFromString(inputs[i].url);
+ String error_message;
+ EXPECT_EQ(inputs[i].access_granted, origin->IsPotentiallyTrustworthy());
+ }
+
+ // Unique origins are not considered secure.
+ scoped_refptr<SecurityOrigin> unique_origin = SecurityOrigin::CreateUnique();
+ EXPECT_FALSE(unique_origin->IsPotentiallyTrustworthy());
+ // ... unless they are specially marked as such.
+ unique_origin->SetUniqueOriginIsPotentiallyTrustworthy(true);
+ EXPECT_TRUE(unique_origin->IsPotentiallyTrustworthy());
+ unique_origin->SetUniqueOriginIsPotentiallyTrustworthy(false);
+ EXPECT_FALSE(unique_origin->IsPotentiallyTrustworthy());
+}
+
+TEST_F(SecurityOriginTest, IsSecure) {
+ struct TestCase {
+ bool is_secure;
+ const char* url;
+ } inputs[] = {
+ {false, "blob:ftp://evil:99/578223a1-8c13-17b3-84d5-eca045ae384a"},
+ {false, "blob:http://example.com/578223a1-8c13-17b3-84d5-eca045ae384a"},
+ {false, "file:///etc/passwd"},
+ {false, "ftp://example.com/"},
+ {false, "http://example.com/"},
+ {false, "ws://example.com/"},
+ {true, "blob:https://example.com/578223a1-8c13-17b3-84d5-eca045ae384a"},
+ {true, "https://example.com/"},
+ {true, "wss://example.com/"},
+
+ {true, "about:blank"},
+ {false, ""},
+ {false, "\0"},
+ };
+
+ for (auto test : inputs)
+ EXPECT_EQ(test.is_secure, SecurityOrigin::IsSecure(KURL(test.url)))
+ << "URL: '" << test.url << "'";
+
+ EXPECT_FALSE(SecurityOrigin::IsSecure(NullURL()));
+}
+
+TEST_F(SecurityOriginTest, IsSecureViaTrustworthy) {
+ const char* urls[] = {"http://localhost/", "http://localhost:8080/",
+ "http://127.0.0.1/", "http://127.0.0.1:8080/",
+ "http://[::1]/"};
+
+ for (const char* test : urls) {
+ KURL url(test);
+ EXPECT_FALSE(SecurityOrigin::IsSecure(url));
+ SecurityPolicy::AddOriginTrustworthyWhiteList(*SecurityOrigin::Create(url));
+ EXPECT_TRUE(SecurityOrigin::IsSecure(url));
+ }
+}
+
+TEST_F(SecurityOriginTest, CanAccess) {
+ struct TestCase {
+ bool can_access;
+ const char* origin1;
+ const char* origin2;
+ };
+
+ TestCase tests[] = {
+ {true, "https://foobar.com", "https://foobar.com"},
+ {false, "https://foobar.com", "https://bazbar.com"},
+ };
+
+ for (size_t i = 0; i < WTF_ARRAY_LENGTH(tests); ++i) {
+ scoped_refptr<const SecurityOrigin> origin1 =
+ SecurityOrigin::CreateFromString(tests[i].origin1);
+ scoped_refptr<const SecurityOrigin> origin2 =
+ SecurityOrigin::CreateFromString(tests[i].origin2);
+ EXPECT_EQ(tests[i].can_access, origin1->CanAccess(origin2.get()));
+ }
+}
+
+TEST_F(SecurityOriginTest, CanRequest) {
+ struct TestCase {
+ bool can_request;
+ const char* origin;
+ const char* url;
+ };
+
+ TestCase tests[] = {
+ {true, "https://foobar.com", "https://foobar.com"},
+ {false, "https://foobar.com", "https://bazbar.com"},
+ };
+
+ for (size_t i = 0; i < WTF_ARRAY_LENGTH(tests); ++i) {
+ scoped_refptr<const SecurityOrigin> origin =
+ SecurityOrigin::CreateFromString(tests[i].origin);
+ blink::KURL url(tests[i].url);
+ EXPECT_EQ(tests[i].can_request, origin->CanRequest(url));
+ }
+}
+
+TEST_F(SecurityOriginTest, CanRequestWithWhitelistedAccess) {
+ scoped_refptr<const SecurityOrigin> origin =
+ SecurityOrigin::CreateFromString("https://chromium.org");
+ const blink::KURL url("https://example.com");
+
+ EXPECT_FALSE(origin->CanRequest(url));
+ // Adding the url to the access whitelist should allow the request.
+ SecurityPolicy::AddOriginAccessWhitelistEntry(*origin, "https", "example.com",
+ false);
+ EXPECT_TRUE(origin->CanRequest(url));
+}
+
+TEST_F(SecurityOriginTest, PortAndEffectivePortMethod) {
+ struct TestCase {
+ unsigned short port;
+ unsigned short effective_port;
+ const char* origin;
+ } cases[] = {
+ {0, 80, "http://example.com"},
+ {0, 80, "http://example.com:80"},
+ {81, 81, "http://example.com:81"},
+ {0, 443, "https://example.com"},
+ {0, 443, "https://example.com:443"},
+ {444, 444, "https://example.com:444"},
+ };
+
+ for (const auto& test : cases) {
+ scoped_refptr<const SecurityOrigin> origin =
+ SecurityOrigin::CreateFromString(test.origin);
+ EXPECT_EQ(test.port, origin->Port());
+ EXPECT_EQ(test.effective_port, origin->EffectivePort());
+ }
+}
+
+TEST_F(SecurityOriginTest, CreateFromTuple) {
+ struct TestCase {
+ const char* scheme;
+ const char* host;
+ uint16_t port;
+ const char* origin;
+ } cases[] = {
+ {"http", "example.com", 80, "http://example.com"},
+ {"http", "example.com", 81, "http://example.com:81"},
+ {"https", "example.com", 443, "https://example.com"},
+ {"https", "example.com", 444, "https://example.com:444"},
+ {"file", "", 0, "file://"},
+ {"file", "example.com", 0, "file://"},
+ };
+
+ for (const auto& test : cases) {
+ scoped_refptr<const SecurityOrigin> origin =
+ SecurityOrigin::Create(test.scheme, test.host, test.port);
+ EXPECT_EQ(test.origin, origin->ToString()) << test.origin;
+ }
+}
+
+TEST_F(SecurityOriginTest, UniquenessPropagatesToBlobUrls) {
+ struct TestCase {
+ const char* url;
+ bool expected_uniqueness;
+ const char* expected_origin_string;
+ } cases[]{
+ {"", true, "null"},
+ {"null", true, "null"},
+ {"data:text/plain,hello_world", true, "null"},
+ {"file:///path", false, "file://"},
+ {"filesystem:http://host/filesystem-path", false, "http://host"},
+ {"filesystem:file:///filesystem-path", false, "file://"},
+ {"filesystem:null/filesystem-path", true, "null"},
+ {"blob:http://host/blob-id", false, "http://host"},
+ {"blob:file:///blob-id", false, "file://"},
+ {"blob:null/blob-id", true, "null"},
+ };
+
+ for (const TestCase& test : cases) {
+ scoped_refptr<const SecurityOrigin> origin =
+ SecurityOrigin::CreateFromString(test.url);
+ EXPECT_EQ(test.expected_uniqueness, origin->IsUnique());
+ EXPECT_EQ(test.expected_origin_string, origin->ToString());
+
+ KURL blob_url = BlobURL::CreatePublicURL(origin.get());
+ scoped_refptr<const SecurityOrigin> blob_url_origin =
+ SecurityOrigin::Create(blob_url);
+ EXPECT_EQ(blob_url_origin->IsUnique(), origin->IsUnique());
+ EXPECT_EQ(blob_url_origin->ToString(), origin->ToString());
+ EXPECT_EQ(blob_url_origin->ToRawString(), origin->ToRawString());
+ }
+}
+
+TEST_F(SecurityOriginTest, UniqueOriginIsSameSchemeHostPort) {
+ scoped_refptr<const SecurityOrigin> unique_origin =
+ SecurityOrigin::CreateUnique();
+ scoped_refptr<const SecurityOrigin> tuple_origin =
+ SecurityOrigin::CreateFromString("http://example.com");
+
+ EXPECT_TRUE(unique_origin->IsSameSchemeHostPort(unique_origin.get()));
+ EXPECT_FALSE(SecurityOrigin::CreateUnique()->IsSameSchemeHostPort(
+ unique_origin.get()));
+ EXPECT_FALSE(tuple_origin->IsSameSchemeHostPort(unique_origin.get()));
+ EXPECT_FALSE(unique_origin->IsSameSchemeHostPort(tuple_origin.get()));
+}
+
+TEST_F(SecurityOriginTest, CanonicalizeHost) {
+ struct TestCase {
+ const char* host;
+ const char* canonical_output;
+ bool expected_success;
+ } cases[] = {
+ {"", "", true},
+ {"example.test", "example.test", true},
+ {"EXAMPLE.TEST", "example.test", true},
+ {"eXaMpLe.TeSt/path", "example.test%2Fpath", false},
+ {",", "%2C", true},
+ {"💩", "xn--ls8h", true},
+ {"[]", "[]", false},
+ {"%yo", "%25yo", false},
+ };
+
+ for (const TestCase& test : cases) {
+ SCOPED_TRACE(testing::Message() << "raw host: '" << test.host << "'");
+ String host = String::FromUTF8(test.host);
+ bool success = false;
+ String canonical_host = SecurityOrigin::CanonicalizeHost(host, &success);
+ EXPECT_EQ(test.canonical_output, canonical_host);
+ EXPECT_EQ(test.expected_success, success);
+ }
+}
+
+TEST_F(SecurityOriginTest, UrlOriginConversions) {
+ struct TestCases {
+ const char* const url;
+ const char* const scheme;
+ const char* const host;
+ uint16_t port;
+ } cases[] = {
+ // IP Addresses
+ {"http://192.168.9.1/", "http", "192.168.9.1", 80},
+ {"http://[2001:db8::1]/", "http", "[2001:db8::1]", 80},
+
+ // Punycode
+ {"http://☃.net/", "http", "xn--n3h.net", 80},
+ {"blob:http://☃.net/", "http", "xn--n3h.net", 80},
+
+ // Generic URLs
+ {"http://example.com/", "http", "example.com", 80},
+ {"http://example.com:123/", "http", "example.com", 123},
+ {"https://example.com/", "https", "example.com", 443},
+ {"https://example.com:123/", "https", "example.com", 123},
+ {"http://user:pass@example.com/", "http", "example.com", 80},
+ {"http://example.com:123/?query", "http", "example.com", 123},
+ {"https://example.com/#1234", "https", "example.com", 443},
+ {"https://u:p@example.com:123/?query#1234", "https", "example.com", 123},
+
+ // Registered URLs
+ {"ftp://example.com/", "ftp", "example.com", 21},
+ // crbug.com/781342
+ // Conversion doesn't work for gopher.
+ // {"gopher://example.com/", "gopher", "example.com", 70},
+ {"ws://example.com/", "ws", "example.com", 80},
+ {"wss://example.com/", "wss", "example.com", 443},
+
+ // file: URLs
+ {"file:///etc/passwd", "file", "", 0},
+ {"file://example.com/etc/passwd", "file", "example.com", 0},
+
+ // Filesystem:
+ {"filesystem:http://example.com/type/", "http", "example.com", 80},
+ {"filesystem:http://example.com:123/type/", "http", "example.com", 123},
+ {"filesystem:https://example.com/type/", "https", "example.com", 443},
+ {"filesystem:https://example.com:123/type/", "https", "example.com", 123},
+
+ // Blob:
+ {"blob:http://example.com/guid-goes-here", "http", "example.com", 80},
+ {"blob:http://example.com:123/guid-goes-here", "http", "example.com",
+ 123},
+ {"blob:https://example.com/guid-goes-here", "https", "example.com", 443},
+ {"blob:http://u:p@example.com/guid-goes-here", "http", "example.com", 80},
+ };
+
+ for (const auto& test_case : cases) {
+ url::Origin url_origin1 = url::Origin::Create(GURL(test_case.url));
+
+ // Test CreateFromUrlOrigin
+ scoped_refptr<const SecurityOrigin> security_origin =
+ SecurityOrigin::CreateFromUrlOrigin(url_origin1);
+ EXPECT_TRUE(security_origin->IsSameSchemeHostPort(
+ SecurityOrigin::Create(test_case.scheme, test_case.host, test_case.port)
+ .get()));
+
+ // Test ToUrlOrigin
+ url::Origin url_origin2 = security_origin->ToUrlOrigin();
+ EXPECT_TRUE(url_origin1.IsSameOriginWith(url_origin2))
+ << test_case.url << " : " << url_origin2.Serialize();
+ }
+}
+
+TEST_F(SecurityOriginTest, EffectiveDomain) {
+ constexpr struct {
+ const char* expected_effective_domain;
+ const char* origin;
+ } kTestCases[] = {
+ {NULL, ""},
+ {NULL, "null"},
+ {"", "file://"},
+ {"127.0.0.1", "https://127.0.0.1"},
+ {"[::1]", "https://[::1]"},
+ {"example.com", "file://example.com/foo"},
+ {"example.com", "http://example.com"},
+ {"example.com", "http://example.com:80"},
+ {"example.com", "https://example.com"},
+ {"suborigin.example.com", "https://suborigin.example.com"},
+ };
+
+ for (const auto& test : kTestCases) {
+ scoped_refptr<const SecurityOrigin> origin =
+ SecurityOrigin::CreateFromString(test.origin);
+ if (test.expected_effective_domain) {
+ EXPECT_EQ(test.expected_effective_domain, origin->Domain());
+ } else {
+ EXPECT_TRUE(origin->Domain().IsEmpty());
+ }
+ }
+}
+
+TEST_F(SecurityOriginTest, EffectiveDomainSetFromDom) {
+ constexpr struct {
+ const char* domain_set_from_dom;
+ const char* expected_effective_domain;
+ const char* origin;
+ } kDomainTestCases[] = {
+ {"example.com", "example.com", "http://www.suborigin.example.com"}};
+
+ for (const auto& test : kDomainTestCases) {
+ scoped_refptr<SecurityOrigin> origin =
+ SecurityOrigin::CreateFromString(test.origin);
+ origin->SetDomainFromDOM(test.domain_set_from_dom);
+ EXPECT_EQ(test.expected_effective_domain, origin->Domain());
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/weborigin/security_policy.cc b/chromium/third_party/blink/renderer/platform/weborigin/security_policy.cc
new file mode 100644
index 00000000000..881f509c3ce
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/weborigin/security_policy.cc
@@ -0,0 +1,443 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 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. ("Google") 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 GOOGLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/weborigin/security_policy.h"
+
+#include <memory>
+#include "third_party/blink/public/platform/web_referrer_policy.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/weborigin/origin_access_entry.h"
+#include "third_party/blink/renderer/platform/weborigin/scheme_registry.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/text/parsing_utilities.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/threading.h"
+
+namespace blink {
+
+using OriginAccessList = Vector<OriginAccessEntry>;
+using OriginAccessMap = HashMap<String, std::unique_ptr<OriginAccessList>>;
+using OriginSet = HashSet<String>;
+
+static OriginAccessMap& GetOriginAccessWhitelistMap() {
+ DEFINE_STATIC_LOCAL(OriginAccessMap, origin_access_whitelist_map, ());
+ return origin_access_whitelist_map;
+}
+
+static OriginAccessMap& GetOriginAccessBlacklistMap() {
+ DEFINE_STATIC_LOCAL(OriginAccessMap, origin_access_blacklist_map, ());
+ return origin_access_blacklist_map;
+}
+
+static OriginSet& TrustworthyOriginSet() {
+ DEFINE_STATIC_LOCAL(OriginSet, trustworthy_origin_set, ());
+ return trustworthy_origin_set;
+}
+
+static void AddOriginAccessEntry(const SecurityOrigin& source_origin,
+ const String& destination_protocol,
+ const String& destination_domain,
+ bool allow_destination_subdomains,
+ OriginAccessMap& access_map) {
+ DCHECK(IsMainThread());
+ DCHECK(!source_origin.IsUnique());
+ if (source_origin.IsUnique())
+ return;
+
+ String source_string = source_origin.ToString();
+ OriginAccessMap::AddResult result = access_map.insert(source_string, nullptr);
+ if (result.is_new_entry)
+ result.stored_value->value = std::make_unique<OriginAccessList>();
+
+ OriginAccessList* list = result.stored_value->value.get();
+ list->push_back(OriginAccessEntry(
+ destination_protocol, destination_domain,
+ allow_destination_subdomains ? OriginAccessEntry::kAllowSubdomains
+ : OriginAccessEntry::kDisallowSubdomains));
+}
+
+static void RemoveOriginAccessEntry(const SecurityOrigin& source_origin,
+ const String& destination_protocol,
+ const String& destination_domain,
+ bool allow_destination_subdomains,
+ OriginAccessMap& access_map) {
+ DCHECK(IsMainThread());
+ DCHECK(!source_origin.IsUnique());
+ if (source_origin.IsUnique())
+ return;
+
+ String source_string = source_origin.ToString();
+ OriginAccessMap::iterator it = access_map.find(source_string);
+ if (it == access_map.end())
+ return;
+
+ OriginAccessList* list = it->value.get();
+ size_t index = list->Find(OriginAccessEntry(
+ destination_protocol, destination_domain,
+ allow_destination_subdomains ? OriginAccessEntry::kAllowSubdomains
+ : OriginAccessEntry::kDisallowSubdomains));
+
+ if (index == kNotFound)
+ return;
+
+ list->EraseAt(index);
+
+ if (list->IsEmpty())
+ access_map.erase(it);
+}
+
+static bool IsOriginPairInAccessMap(const SecurityOrigin* active_origin,
+ const SecurityOrigin* target_origin,
+ const OriginAccessMap& access_map) {
+ if (access_map.IsEmpty())
+ return false;
+
+ if (OriginAccessList* list = access_map.at(active_origin->ToString())) {
+ for (size_t i = 0; i < list->size(); ++i) {
+ if (list->at(i).MatchesOrigin(*target_origin) !=
+ OriginAccessEntry::kDoesNotMatchOrigin)
+ return true;
+ }
+ }
+ return false;
+}
+
+void SecurityPolicy::Init() {
+ GetOriginAccessWhitelistMap();
+ GetOriginAccessBlacklistMap();
+ TrustworthyOriginSet();
+}
+
+bool SecurityPolicy::ShouldHideReferrer(const KURL& url, const KURL& referrer) {
+ bool referrer_is_secure_url = referrer.ProtocolIs("https");
+ bool scheme_is_allowed =
+ SchemeRegistry::ShouldTreatURLSchemeAsAllowedForReferrer(
+ referrer.Protocol());
+
+ if (!scheme_is_allowed)
+ return true;
+
+ if (!referrer_is_secure_url)
+ return false;
+
+ bool url_is_secure_url = url.ProtocolIs("https");
+
+ return !url_is_secure_url;
+}
+
+Referrer SecurityPolicy::GenerateReferrer(ReferrerPolicy referrer_policy,
+ const KURL& url,
+ const String& referrer) {
+ ReferrerPolicy referrer_policy_no_default = referrer_policy;
+ if (referrer_policy_no_default == kReferrerPolicyDefault) {
+ if (RuntimeEnabledFeatures::ReducedReferrerGranularityEnabled()) {
+ referrer_policy_no_default = kReferrerPolicyStrictOriginWhenCrossOrigin;
+ } else {
+ referrer_policy_no_default = kReferrerPolicyNoReferrerWhenDowngrade;
+ }
+ }
+ if (referrer == Referrer::NoReferrer())
+ return Referrer(Referrer::NoReferrer(), referrer_policy_no_default);
+ DCHECK(!referrer.IsEmpty());
+
+ KURL referrer_url = KURL(NullURL(), referrer);
+ String scheme = referrer_url.Protocol();
+ if (!SchemeRegistry::ShouldTreatURLSchemeAsAllowedForReferrer(scheme))
+ return Referrer(Referrer::NoReferrer(), referrer_policy_no_default);
+
+ if (SecurityOrigin::ShouldUseInnerURL(url))
+ return Referrer(Referrer::NoReferrer(), referrer_policy_no_default);
+
+ switch (referrer_policy_no_default) {
+ case kReferrerPolicyNever:
+ return Referrer(Referrer::NoReferrer(), referrer_policy_no_default);
+ case kReferrerPolicyAlways:
+ return Referrer(referrer, referrer_policy_no_default);
+ case kReferrerPolicyOrigin: {
+ String origin = SecurityOrigin::Create(referrer_url)->ToString();
+ // A security origin is not a canonical URL as it lacks a path. Add /
+ // to turn it into a canonical URL we can use as referrer.
+ return Referrer(origin + "/", referrer_policy_no_default);
+ }
+ case kReferrerPolicyOriginWhenCrossOrigin: {
+ scoped_refptr<const SecurityOrigin> referrer_origin =
+ SecurityOrigin::Create(referrer_url);
+ scoped_refptr<const SecurityOrigin> url_origin =
+ SecurityOrigin::Create(url);
+ if (!url_origin->IsSameSchemeHostPort(referrer_origin.get())) {
+ String origin = referrer_origin->ToString();
+ return Referrer(origin + "/", referrer_policy_no_default);
+ }
+ break;
+ }
+ case kReferrerPolicySameOrigin: {
+ scoped_refptr<const SecurityOrigin> referrer_origin =
+ SecurityOrigin::Create(referrer_url);
+ scoped_refptr<const SecurityOrigin> url_origin =
+ SecurityOrigin::Create(url);
+ if (!url_origin->IsSameSchemeHostPort(referrer_origin.get())) {
+ return Referrer(Referrer::NoReferrer(), referrer_policy_no_default);
+ }
+ return Referrer(referrer, referrer_policy_no_default);
+ }
+ case kReferrerPolicyStrictOrigin: {
+ String origin = SecurityOrigin::Create(referrer_url)->ToString();
+ return Referrer(ShouldHideReferrer(url, referrer_url)
+ ? Referrer::NoReferrer()
+ : origin + "/",
+ referrer_policy_no_default);
+ }
+ case kReferrerPolicyStrictOriginWhenCrossOrigin: {
+ scoped_refptr<const SecurityOrigin> referrer_origin =
+ SecurityOrigin::Create(referrer_url);
+ scoped_refptr<const SecurityOrigin> url_origin =
+ SecurityOrigin::Create(url);
+ if (!url_origin->IsSameSchemeHostPort(referrer_origin.get())) {
+ String origin = referrer_origin->ToString();
+ return Referrer(ShouldHideReferrer(url, referrer_url)
+ ? Referrer::NoReferrer()
+ : origin + "/",
+ referrer_policy_no_default);
+ }
+ break;
+ }
+ case kReferrerPolicyNoReferrerWhenDowngrade:
+ break;
+ case kReferrerPolicyDefault:
+ NOTREACHED();
+ break;
+ }
+
+ return Referrer(
+ ShouldHideReferrer(url, referrer_url) ? Referrer::NoReferrer() : referrer,
+ referrer_policy_no_default);
+}
+
+void SecurityPolicy::AddOriginTrustworthyWhiteList(
+ const SecurityOrigin& origin) {
+#if DCHECK_IS_ON()
+ // Must be called before we start other threads.
+ DCHECK(WTF::IsBeforeThreadCreated());
+#endif
+ if (origin.IsUnique())
+ return;
+ TrustworthyOriginSet().insert(origin.ToRawString());
+}
+
+bool SecurityPolicy::IsOriginWhiteListedTrustworthy(
+ const SecurityOrigin& origin) {
+ // Early return if there are no whitelisted origins to avoid unnecessary
+ // allocations, copies, and frees.
+ if (origin.IsUnique() || TrustworthyOriginSet().IsEmpty())
+ return false;
+ return TrustworthyOriginSet().Contains(origin.ToRawString());
+}
+
+bool SecurityPolicy::IsUrlWhiteListedTrustworthy(const KURL& url) {
+ // Early return to avoid initializing the SecurityOrigin.
+ if (TrustworthyOriginSet().IsEmpty())
+ return false;
+ return IsOriginWhiteListedTrustworthy(*SecurityOrigin::Create(url).get());
+}
+
+bool SecurityPolicy::IsAccessWhiteListed(const SecurityOrigin* active_origin,
+ const SecurityOrigin* target_origin) {
+ return IsOriginPairInAccessMap(active_origin, target_origin,
+ GetOriginAccessWhitelistMap()) &&
+ !IsOriginPairInAccessMap(active_origin, target_origin,
+ GetOriginAccessBlacklistMap());
+}
+
+bool SecurityPolicy::IsAccessToURLWhiteListed(
+ const SecurityOrigin* active_origin,
+ const KURL& url) {
+ scoped_refptr<const SecurityOrigin> target_origin =
+ SecurityOrigin::Create(url);
+ return IsAccessWhiteListed(active_origin, target_origin.get());
+}
+
+void SecurityPolicy::AddOriginAccessWhitelistEntry(
+ const SecurityOrigin& source_origin,
+ const String& destination_protocol,
+ const String& destination_domain,
+ bool allow_destination_subdomains) {
+ AddOriginAccessEntry(source_origin, destination_protocol, destination_domain,
+ allow_destination_subdomains,
+ GetOriginAccessWhitelistMap());
+}
+
+void SecurityPolicy::RemoveOriginAccessWhitelistEntry(
+ const SecurityOrigin& source_origin,
+ const String& destination_protocol,
+ const String& destination_domain,
+ bool allow_destination_subdomains) {
+ RemoveOriginAccessEntry(source_origin, destination_protocol,
+ destination_domain, allow_destination_subdomains,
+ GetOriginAccessWhitelistMap());
+}
+
+void SecurityPolicy::ResetOriginAccessWhitelists() {
+ DCHECK(IsMainThread());
+ GetOriginAccessWhitelistMap().clear();
+}
+
+void SecurityPolicy::AddOriginAccessBlacklistEntry(
+ const SecurityOrigin& source_origin,
+ const String& destination_protocol,
+ const String& destination_domain,
+ bool allow_destination_subdomains) {
+ AddOriginAccessEntry(source_origin, destination_protocol, destination_domain,
+ allow_destination_subdomains,
+ GetOriginAccessBlacklistMap());
+}
+
+void SecurityPolicy::RemoveOriginAccessBlacklistEntry(
+ const SecurityOrigin& source_origin,
+ const String& destination_protocol,
+ const String& destination_domain,
+ bool allow_destination_subdomains) {
+ RemoveOriginAccessEntry(source_origin, destination_protocol,
+ destination_domain, allow_destination_subdomains,
+ GetOriginAccessBlacklistMap());
+}
+
+void SecurityPolicy::ResetOriginAccessBlacklists() {
+ DCHECK(IsMainThread());
+ GetOriginAccessBlacklistMap().clear();
+}
+
+bool SecurityPolicy::ReferrerPolicyFromString(
+ const String& policy,
+ ReferrerPolicyLegacyKeywordsSupport legacy_keywords_support,
+ ReferrerPolicy* result) {
+ DCHECK(!policy.IsNull());
+ bool support_legacy_keywords =
+ (legacy_keywords_support == kSupportReferrerPolicyLegacyKeywords);
+
+ if (EqualIgnoringASCIICase(policy, "no-referrer") ||
+ (support_legacy_keywords && (EqualIgnoringASCIICase(policy, "never") ||
+ EqualIgnoringASCIICase(policy, "none")))) {
+ *result = kReferrerPolicyNever;
+ return true;
+ }
+ if (EqualIgnoringASCIICase(policy, "unsafe-url") ||
+ (support_legacy_keywords && EqualIgnoringASCIICase(policy, "always"))) {
+ *result = kReferrerPolicyAlways;
+ return true;
+ }
+ if (EqualIgnoringASCIICase(policy, "origin")) {
+ *result = kReferrerPolicyOrigin;
+ return true;
+ }
+ if (EqualIgnoringASCIICase(policy, "origin-when-cross-origin") ||
+ (support_legacy_keywords &&
+ EqualIgnoringASCIICase(policy, "origin-when-crossorigin"))) {
+ *result = kReferrerPolicyOriginWhenCrossOrigin;
+ return true;
+ }
+ if (EqualIgnoringASCIICase(policy, "same-origin")) {
+ *result = kReferrerPolicySameOrigin;
+ return true;
+ }
+ if (EqualIgnoringASCIICase(policy, "strict-origin")) {
+ *result = kReferrerPolicyStrictOrigin;
+ return true;
+ }
+ if (EqualIgnoringASCIICase(policy, "strict-origin-when-cross-origin")) {
+ *result = kReferrerPolicyStrictOriginWhenCrossOrigin;
+ return true;
+ }
+ if (EqualIgnoringASCIICase(policy, "no-referrer-when-downgrade") ||
+ (support_legacy_keywords && EqualIgnoringASCIICase(policy, "default"))) {
+ *result = kReferrerPolicyNoReferrerWhenDowngrade;
+ return true;
+ }
+ return false;
+}
+
+namespace {
+
+template <typename CharType>
+inline bool IsASCIIAlphaOrHyphen(CharType c) {
+ return IsASCIIAlpha(c) || c == '-';
+}
+
+} // namespace
+
+bool SecurityPolicy::ReferrerPolicyFromHeaderValue(
+ const String& header_value,
+ ReferrerPolicyLegacyKeywordsSupport legacy_keywords_support,
+ ReferrerPolicy* result) {
+ ReferrerPolicy referrer_policy = kReferrerPolicyDefault;
+
+ Vector<String> tokens;
+ header_value.Split(',', true, tokens);
+ for (const auto& token : tokens) {
+ ReferrerPolicy current_result;
+ auto stripped_token = token.StripWhiteSpace();
+ if (SecurityPolicy::ReferrerPolicyFromString(token.StripWhiteSpace(),
+ legacy_keywords_support,
+ &current_result)) {
+ referrer_policy = current_result;
+ } else {
+ Vector<UChar> characters;
+ stripped_token.AppendTo(characters);
+ const UChar* position = characters.data();
+ UChar* end = characters.data() + characters.size();
+ SkipWhile<UChar, IsASCIIAlphaOrHyphen>(position, end);
+ if (position != end)
+ return false;
+ }
+ }
+
+ if (referrer_policy == kReferrerPolicyDefault)
+ return false;
+
+ *result = referrer_policy;
+ return true;
+}
+
+STATIC_ASSERT_ENUM(kWebReferrerPolicyAlways, kReferrerPolicyAlways);
+STATIC_ASSERT_ENUM(kWebReferrerPolicyDefault, kReferrerPolicyDefault);
+STATIC_ASSERT_ENUM(kWebReferrerPolicyNoReferrerWhenDowngrade,
+ kReferrerPolicyNoReferrerWhenDowngrade);
+STATIC_ASSERT_ENUM(kWebReferrerPolicyNever, kReferrerPolicyNever);
+STATIC_ASSERT_ENUM(kWebReferrerPolicyOrigin, kReferrerPolicyOrigin);
+STATIC_ASSERT_ENUM(kWebReferrerPolicyOriginWhenCrossOrigin,
+ kReferrerPolicyOriginWhenCrossOrigin);
+STATIC_ASSERT_ENUM(kWebReferrerPolicySameOrigin, kReferrerPolicySameOrigin);
+STATIC_ASSERT_ENUM(kWebReferrerPolicyStrictOrigin, kReferrerPolicyStrictOrigin);
+STATIC_ASSERT_ENUM(
+ kWebReferrerPolicyNoReferrerWhenDowngradeOriginWhenCrossOrigin,
+ kReferrerPolicyStrictOriginWhenCrossOrigin);
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/weborigin/security_policy.h b/chromium/third_party/blink/renderer/platform/weborigin/security_policy.h
new file mode 100644
index 00000000000..285f481a398
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/weborigin/security_policy.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 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. ("Google") 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 GOOGLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_SECURITY_POLICY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_SECURITY_POLICY_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/weborigin/referrer.h"
+#include "third_party/blink/renderer/platform/weborigin/referrer_policy.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class KURL;
+class SecurityOrigin;
+
+enum ReferrerPolicyLegacyKeywordsSupport {
+ kSupportReferrerPolicyLegacyKeywords,
+ kDoNotSupportReferrerPolicyLegacyKeywords,
+};
+
+class PLATFORM_EXPORT SecurityPolicy {
+ STATIC_ONLY(SecurityPolicy);
+
+ public:
+ // This must be called during initialization (before we create
+ // other threads).
+ static void Init();
+
+ // True if the referrer should be omitted according to the
+ // ReferrerPolicyNoReferrerWhenDowngrade. If you intend to send a
+ // referrer header, you should use generateReferrer instead.
+ static bool ShouldHideReferrer(const KURL&, const KURL& referrer);
+
+ // Returns the referrer modified according to the referrer policy for a
+ // navigation to a given URL. If the referrer returned is empty, the
+ // referrer header should be omitted.
+ static Referrer GenerateReferrer(ReferrerPolicy,
+ const KURL&,
+ const String& referrer);
+
+ static void AddOriginAccessWhitelistEntry(const SecurityOrigin& source_origin,
+ const String& destination_protocol,
+ const String& destination_domain,
+ bool allow_destination_subdomains);
+ static void RemoveOriginAccessWhitelistEntry(
+ const SecurityOrigin& source_origin,
+ const String& destination_protocol,
+ const String& destination_domain,
+ bool allow_destination_subdomains);
+ static void ResetOriginAccessWhitelists();
+
+ static void AddOriginAccessBlacklistEntry(const SecurityOrigin& source_origin,
+ const String& destination_protocol,
+ const String& destination_domain,
+ bool allow_destination_subdomains);
+ static void RemoveOriginAccessBlacklistEntry(
+ const SecurityOrigin& source_origin,
+ const String& destination_protocol,
+ const String& destination_domain,
+ bool allow_destination_subdomains);
+ static void ResetOriginAccessBlacklists();
+
+ static bool IsAccessWhiteListed(const SecurityOrigin* active_origin,
+ const SecurityOrigin* target_origin);
+ static bool IsAccessToURLWhiteListed(const SecurityOrigin* active_origin,
+ const KURL&);
+
+ static void AddOriginTrustworthyWhiteList(const SecurityOrigin&);
+ static bool IsOriginWhiteListedTrustworthy(const SecurityOrigin&);
+ static bool IsUrlWhiteListedTrustworthy(const KURL&);
+
+ static bool ReferrerPolicyFromString(const String& policy,
+ ReferrerPolicyLegacyKeywordsSupport,
+ ReferrerPolicy* result);
+
+ static bool ReferrerPolicyFromHeaderValue(const String& header_value,
+ ReferrerPolicyLegacyKeywordsSupport,
+ ReferrerPolicy* result);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_SECURITY_POLICY_H_
diff --git a/chromium/third_party/blink/renderer/platform/weborigin/security_policy_test.cc b/chromium/third_party/blink/renderer/platform/weborigin/security_policy_test.cc
new file mode 100644
index 00000000000..c03f202572e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/weborigin/security_policy_test.cc
@@ -0,0 +1,428 @@
+/*
+ * Copyright (C) 2014 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 "third_party/blink/renderer/platform/weborigin/security_policy.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/weborigin/scheme_registry.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+
+namespace blink {
+
+TEST(SecurityPolicyTest, EmptyReferrerForUnauthorizedScheme) {
+ const KURL example_http_url = KURL("http://example.com/");
+ EXPECT_TRUE(String() == SecurityPolicy::GenerateReferrer(
+ kReferrerPolicyAlways, example_http_url,
+ String::FromUTF8("chrome://somepage/"))
+ .referrer);
+}
+
+TEST(SecurityPolicyTest, GenerateReferrerRespectsReferrerSchemesRegistry) {
+ const KURL example_http_url = KURL("http://example.com/");
+ const String foobar_url = String::FromUTF8("foobar://somepage/");
+ const String foobar_scheme = String::FromUTF8("foobar");
+
+ EXPECT_EQ(String(), SecurityPolicy::GenerateReferrer(
+ kReferrerPolicyAlways, example_http_url, foobar_url)
+ .referrer);
+ SchemeRegistry::RegisterURLSchemeAsAllowedForReferrer(foobar_scheme);
+ EXPECT_EQ(foobar_url, SecurityPolicy::GenerateReferrer(
+ kReferrerPolicyAlways, example_http_url, foobar_url)
+ .referrer);
+ SchemeRegistry::RemoveURLSchemeAsAllowedForReferrer(foobar_scheme);
+}
+
+TEST(SecurityPolicyTest, ShouldHideReferrerRespectsReferrerSchemesRegistry) {
+ const KURL example_http_url = KURL("http://example.com/");
+ const KURL foobar_url = KURL("foobar://somepage/");
+ const String foobar_scheme = String::FromUTF8("foobar");
+
+ EXPECT_TRUE(SecurityPolicy::ShouldHideReferrer(example_http_url, foobar_url));
+ SchemeRegistry::RegisterURLSchemeAsAllowedForReferrer(foobar_scheme);
+ EXPECT_FALSE(
+ SecurityPolicy::ShouldHideReferrer(example_http_url, foobar_url));
+ SchemeRegistry::RemoveURLSchemeAsAllowedForReferrer(foobar_scheme);
+}
+
+TEST(SecurityPolicyTest, GenerateReferrer) {
+ struct TestCase {
+ ReferrerPolicy policy;
+ const char* referrer;
+ const char* destination;
+ const char* expected;
+ };
+
+ const char kInsecureURLA[] = "http://a.test/path/to/file.html";
+ const char kInsecureURLB[] = "http://b.test/path/to/file.html";
+ const char kInsecureOriginA[] = "http://a.test/";
+
+ const char kSecureURLA[] = "https://a.test/path/to/file.html";
+ const char kSecureURLB[] = "https://b.test/path/to/file.html";
+ const char kSecureOriginA[] = "https://a.test/";
+
+ const char kBlobURL[] =
+ "blob:http://a.test/b3aae9c8-7f90-440d-8d7c-43aa20d72fde";
+ const char kFilesystemURL[] = "filesystem:http://a.test/path/t/file.html";
+
+ TestCase inputs[] = {
+ // HTTP -> HTTP: Same Origin
+ {kReferrerPolicyAlways, kInsecureURLA, kInsecureURLA, kInsecureURLA},
+ {kReferrerPolicyDefault, kInsecureURLA, kInsecureURLA, kInsecureURLA},
+ {kReferrerPolicyNoReferrerWhenDowngrade, kInsecureURLA, kInsecureURLA,
+ kInsecureURLA},
+ {kReferrerPolicyNever, kInsecureURLA, kInsecureURLA, nullptr},
+ {kReferrerPolicyOrigin, kInsecureURLA, kInsecureURLA, kInsecureOriginA},
+ {kReferrerPolicyOriginWhenCrossOrigin, kInsecureURLA, kInsecureURLA,
+ kInsecureURLA},
+ {kReferrerPolicySameOrigin, kInsecureURLA, kInsecureURLA, kInsecureURLA},
+ {kReferrerPolicyStrictOrigin, kInsecureURLA, kInsecureURLA,
+ kInsecureOriginA},
+ {kReferrerPolicyStrictOriginWhenCrossOrigin, kInsecureURLA, kInsecureURLA,
+ kInsecureURLA},
+
+ // HTTP -> HTTP: Cross Origin
+ {kReferrerPolicyAlways, kInsecureURLA, kInsecureURLB, kInsecureURLA},
+ {kReferrerPolicyDefault, kInsecureURLA, kInsecureURLB, kInsecureURLA},
+ {kReferrerPolicyNoReferrerWhenDowngrade, kInsecureURLA, kInsecureURLB,
+ kInsecureURLA},
+ {kReferrerPolicyNever, kInsecureURLA, kInsecureURLB, nullptr},
+ {kReferrerPolicyOrigin, kInsecureURLA, kInsecureURLB, kInsecureOriginA},
+ {kReferrerPolicyOriginWhenCrossOrigin, kInsecureURLA, kInsecureURLB,
+ kInsecureOriginA},
+ {kReferrerPolicySameOrigin, kInsecureURLA, kInsecureURLB, nullptr},
+ {kReferrerPolicyStrictOrigin, kInsecureURLA, kInsecureURLB,
+ kInsecureOriginA},
+ {kReferrerPolicyStrictOriginWhenCrossOrigin, kInsecureURLA, kInsecureURLB,
+ kInsecureOriginA},
+
+ // HTTPS -> HTTPS: Same Origin
+ {kReferrerPolicyAlways, kSecureURLA, kSecureURLA, kSecureURLA},
+ {kReferrerPolicyDefault, kSecureURLA, kSecureURLA, kSecureURLA},
+ {kReferrerPolicyNoReferrerWhenDowngrade, kSecureURLA, kSecureURLA,
+ kSecureURLA},
+ {kReferrerPolicyNever, kSecureURLA, kSecureURLA, nullptr},
+ {kReferrerPolicyOrigin, kSecureURLA, kSecureURLA, kSecureOriginA},
+ {kReferrerPolicyOriginWhenCrossOrigin, kSecureURLA, kSecureURLA,
+ kSecureURLA},
+ {kReferrerPolicySameOrigin, kSecureURLA, kSecureURLA, kSecureURLA},
+ {kReferrerPolicyStrictOrigin, kSecureURLA, kSecureURLA, kSecureOriginA},
+ {kReferrerPolicyStrictOriginWhenCrossOrigin, kSecureURLA, kSecureURLA,
+ kSecureURLA},
+
+ // HTTPS -> HTTPS: Cross Origin
+ {kReferrerPolicyAlways, kSecureURLA, kSecureURLB, kSecureURLA},
+ {kReferrerPolicyDefault, kSecureURLA, kSecureURLB, kSecureURLA},
+ {kReferrerPolicyNoReferrerWhenDowngrade, kSecureURLA, kSecureURLB,
+ kSecureURLA},
+ {kReferrerPolicyNever, kSecureURLA, kSecureURLB, nullptr},
+ {kReferrerPolicyOrigin, kSecureURLA, kSecureURLB, kSecureOriginA},
+ {kReferrerPolicyOriginWhenCrossOrigin, kSecureURLA, kSecureURLB,
+ kSecureOriginA},
+ {kReferrerPolicySameOrigin, kSecureURLA, kSecureURLB, nullptr},
+ {kReferrerPolicyStrictOrigin, kSecureURLA, kSecureURLB, kSecureOriginA},
+ {kReferrerPolicyStrictOriginWhenCrossOrigin, kSecureURLA, kSecureURLB,
+ kSecureOriginA},
+
+ // HTTP -> HTTPS
+ {kReferrerPolicyAlways, kInsecureURLA, kSecureURLB, kInsecureURLA},
+ {kReferrerPolicyDefault, kInsecureURLA, kSecureURLB, kInsecureURLA},
+ {kReferrerPolicyNoReferrerWhenDowngrade, kInsecureURLA, kSecureURLB,
+ kInsecureURLA},
+ {kReferrerPolicyNever, kInsecureURLA, kSecureURLB, nullptr},
+ {kReferrerPolicyOrigin, kInsecureURLA, kSecureURLB, kInsecureOriginA},
+ {kReferrerPolicyOriginWhenCrossOrigin, kInsecureURLA, kSecureURLB,
+ kInsecureOriginA},
+ {kReferrerPolicySameOrigin, kInsecureURLA, kSecureURLB, nullptr},
+ {kReferrerPolicyStrictOrigin, kInsecureURLA, kSecureURLB,
+ kInsecureOriginA},
+ {kReferrerPolicyStrictOriginWhenCrossOrigin, kInsecureURLA, kSecureURLB,
+ kInsecureOriginA},
+
+ // HTTPS -> HTTP
+ {kReferrerPolicyAlways, kSecureURLA, kInsecureURLB, kSecureURLA},
+ {kReferrerPolicyDefault, kSecureURLA, kInsecureURLB, nullptr},
+ {kReferrerPolicyNoReferrerWhenDowngrade, kSecureURLA, kInsecureURLB,
+ nullptr},
+ {kReferrerPolicyNever, kSecureURLA, kInsecureURLB, nullptr},
+ {kReferrerPolicyOrigin, kSecureURLA, kInsecureURLB, kSecureOriginA},
+ {kReferrerPolicyOriginWhenCrossOrigin, kSecureURLA, kSecureURLB,
+ kSecureOriginA},
+ {kReferrerPolicySameOrigin, kSecureURLA, kInsecureURLB, nullptr},
+ {kReferrerPolicyStrictOrigin, kSecureURLA, kInsecureURLB, nullptr},
+ {kReferrerPolicyStrictOriginWhenCrossOrigin, kSecureURLA, kInsecureURLB,
+ nullptr},
+
+ // blob and filesystem URL handling
+ {kReferrerPolicyAlways, kInsecureURLA, kBlobURL, nullptr},
+ {kReferrerPolicyAlways, kBlobURL, kInsecureURLA, nullptr},
+ {kReferrerPolicyAlways, kInsecureURLA, kFilesystemURL, nullptr},
+ {kReferrerPolicyAlways, kFilesystemURL, kInsecureURLA, nullptr},
+ };
+
+ for (TestCase test : inputs) {
+ KURL destination(test.destination);
+ Referrer result = SecurityPolicy::GenerateReferrer(
+ test.policy, destination, String::FromUTF8(test.referrer));
+ if (test.expected) {
+ EXPECT_EQ(String::FromUTF8(test.expected), result.referrer)
+ << "'" << test.referrer << "' to '" << test.destination
+ << "' should have been '" << test.expected << "': was '"
+ << result.referrer.Utf8().data() << "'.";
+ } else {
+ EXPECT_TRUE(result.referrer.IsEmpty())
+ << "'" << test.referrer << "' to '" << test.destination
+ << "' should have been empty: was '" << result.referrer.Utf8().data()
+ << "'.";
+ }
+ EXPECT_EQ(test.policy == kReferrerPolicyDefault
+ ? kReferrerPolicyNoReferrerWhenDowngrade
+ : test.policy,
+ result.referrer_policy);
+ }
+}
+
+TEST(SecurityPolicyTest, ReferrerPolicyFromHeaderValue) {
+ struct TestCase {
+ const char* header;
+ bool is_valid;
+ ReferrerPolicyLegacyKeywordsSupport keywords;
+ ReferrerPolicy expected_policy;
+ };
+
+ TestCase inputs[] = {
+ {"origin", true, kDoNotSupportReferrerPolicyLegacyKeywords,
+ kReferrerPolicyOrigin},
+ {"none", true, kSupportReferrerPolicyLegacyKeywords,
+ kReferrerPolicyNever},
+ {"none", false, kDoNotSupportReferrerPolicyLegacyKeywords,
+ kReferrerPolicyDefault},
+ {"foo", false, kDoNotSupportReferrerPolicyLegacyKeywords,
+ kReferrerPolicyDefault},
+ {"origin, foo", true, kDoNotSupportReferrerPolicyLegacyKeywords,
+ kReferrerPolicyOrigin},
+ {"origin, foo-bar", true, kDoNotSupportReferrerPolicyLegacyKeywords,
+ kReferrerPolicyOrigin},
+ {"origin, foo bar", false, kDoNotSupportReferrerPolicyLegacyKeywords,
+ kReferrerPolicyDefault},
+ };
+
+ for (TestCase test : inputs) {
+ ReferrerPolicy actual_policy = kReferrerPolicyDefault;
+ EXPECT_EQ(test.is_valid, SecurityPolicy::ReferrerPolicyFromHeaderValue(
+ test.header, test.keywords, &actual_policy));
+ if (test.is_valid)
+ EXPECT_EQ(test.expected_policy, actual_policy);
+ }
+}
+
+TEST(SecurityPolicyTest, TrustworthyWhiteList) {
+ const char* insecure_urls[] = {
+ "http://a.test/path/to/file.html", "http://b.test/path/to/file.html",
+ "blob:http://c.test/b3aae9c8-7f90-440d-8d7c-43aa20d72fde",
+ "filesystem:http://d.test/path/t/file.html",
+ };
+
+ for (const char* url : insecure_urls) {
+ scoped_refptr<const SecurityOrigin> origin =
+ SecurityOrigin::CreateFromString(url);
+ EXPECT_FALSE(origin->IsPotentiallyTrustworthy());
+ SecurityPolicy::AddOriginTrustworthyWhiteList(*origin);
+ EXPECT_TRUE(origin->IsPotentiallyTrustworthy());
+ }
+
+ // Tests that adding URLs that have inner-urls to the whitelist
+ // takes effect on the origins of the inner-urls (and vice versa).
+ struct TestCase {
+ const char* url;
+ const char* another_url_in_origin;
+ };
+ TestCase insecure_urls_with_inner_origin[] = {
+ {"blob:http://e.test/b3aae9c8-7f90-440d-8d7c-43aa20d72fde",
+ "http://e.test/foo.html"},
+ {"filesystem:http://f.test/path/t/file.html", "http://f.test/bar.html"},
+ {"http://g.test/foo.html",
+ "blob:http://g.test/b3aae9c8-7f90-440d-8d7c-43aa20d72fde"},
+ {"http://h.test/bar.html", "filesystem:http://h.test/path/t/file.html"},
+ };
+ for (const TestCase& test : insecure_urls_with_inner_origin) {
+ // Actually origins of both URLs should be same.
+ scoped_refptr<const SecurityOrigin> origin1 =
+ SecurityOrigin::CreateFromString(test.url);
+ scoped_refptr<const SecurityOrigin> origin2 =
+ SecurityOrigin::CreateFromString(test.another_url_in_origin);
+
+ EXPECT_FALSE(origin1->IsPotentiallyTrustworthy());
+ EXPECT_FALSE(origin2->IsPotentiallyTrustworthy());
+ SecurityPolicy::AddOriginTrustworthyWhiteList(*origin1);
+ EXPECT_TRUE(origin1->IsPotentiallyTrustworthy());
+ EXPECT_TRUE(origin2->IsPotentiallyTrustworthy());
+ }
+}
+
+class SecurityPolicyAccessTest : public testing::Test {
+ public:
+ SecurityPolicyAccessTest() = default;
+ ~SecurityPolicyAccessTest() override = default;
+
+ void SetUp() override {
+ https_example_origin_ =
+ SecurityOrigin::CreateFromString("https://example.com");
+ https_sub_example_origin_ =
+ SecurityOrigin::CreateFromString("https://sub.example.com");
+ http_example_origin_ =
+ SecurityOrigin::CreateFromString("http://example.com");
+ https_chromium_origin_ =
+ SecurityOrigin::CreateFromString("https://chromium.org");
+ https_google_origin_ =
+ SecurityOrigin::CreateFromString("https://google.com");
+ }
+
+ void TearDown() override {
+ SecurityPolicy::ResetOriginAccessWhitelists();
+ SecurityPolicy::ResetOriginAccessBlacklists();
+ }
+
+ const SecurityOrigin* https_example_origin() const {
+ return https_example_origin_.get();
+ }
+ const SecurityOrigin* https_sub_example_origin() const {
+ return https_sub_example_origin_.get();
+ }
+ const SecurityOrigin* http_example_origin() const {
+ return http_example_origin_.get();
+ }
+ const SecurityOrigin* https_chromium_origin() const {
+ return https_chromium_origin_.get();
+ }
+ const SecurityOrigin* https_google_origin() const {
+ return https_google_origin_.get();
+ }
+
+ private:
+ scoped_refptr<const SecurityOrigin> https_example_origin_;
+ scoped_refptr<const SecurityOrigin> https_sub_example_origin_;
+ scoped_refptr<const SecurityOrigin> http_example_origin_;
+ scoped_refptr<const SecurityOrigin> https_chromium_origin_;
+ scoped_refptr<const SecurityOrigin> https_google_origin_;
+
+ DISALLOW_COPY_AND_ASSIGN(SecurityPolicyAccessTest);
+};
+
+TEST_F(SecurityPolicyAccessTest, IsAccessWhiteListed) {
+ // By default, no access should be whitelisted.
+ EXPECT_FALSE(SecurityPolicy::IsAccessWhiteListed(https_chromium_origin(),
+ https_example_origin()));
+ EXPECT_FALSE(SecurityPolicy::IsAccessWhiteListed(https_chromium_origin(),
+ https_sub_example_origin()));
+ EXPECT_FALSE(SecurityPolicy::IsAccessWhiteListed(https_chromium_origin(),
+ http_example_origin()));
+
+ // Adding access for https://example.com should work, but should not grant
+ // access to subdomains or other schemes.
+ SecurityPolicy::AddOriginAccessWhitelistEntry(*https_chromium_origin(),
+ "https", "example.com", false);
+ EXPECT_TRUE(SecurityPolicy::IsAccessWhiteListed(https_chromium_origin(),
+ https_example_origin()));
+ EXPECT_FALSE(SecurityPolicy::IsAccessWhiteListed(https_chromium_origin(),
+ https_sub_example_origin()));
+ EXPECT_FALSE(SecurityPolicy::IsAccessWhiteListed(https_chromium_origin(),
+ http_example_origin()));
+
+ // Removing the entry should revoke access.
+ SecurityPolicy::RemoveOriginAccessWhitelistEntry(
+ *https_chromium_origin(), "https", "example.com", false);
+ EXPECT_FALSE(SecurityPolicy::IsAccessWhiteListed(https_chromium_origin(),
+ https_example_origin()));
+ EXPECT_FALSE(SecurityPolicy::IsAccessWhiteListed(https_chromium_origin(),
+ https_sub_example_origin()));
+ EXPECT_FALSE(SecurityPolicy::IsAccessWhiteListed(https_chromium_origin(),
+ http_example_origin()));
+
+ // Adding an entry that matches subdomains should grant access to any
+ // subdomains.
+ SecurityPolicy::AddOriginAccessWhitelistEntry(*https_chromium_origin(),
+ "https", "example.com", true);
+ EXPECT_TRUE(SecurityPolicy::IsAccessWhiteListed(https_chromium_origin(),
+ https_example_origin()));
+ EXPECT_TRUE(SecurityPolicy::IsAccessWhiteListed(https_chromium_origin(),
+ https_sub_example_origin()));
+ EXPECT_FALSE(SecurityPolicy::IsAccessWhiteListed(https_chromium_origin(),
+ http_example_origin()));
+
+ // Clearing the map should revoke all special access.
+ SecurityPolicy::ResetOriginAccessWhitelists();
+ EXPECT_FALSE(SecurityPolicy::IsAccessWhiteListed(https_chromium_origin(),
+ https_example_origin()));
+ EXPECT_FALSE(SecurityPolicy::IsAccessWhiteListed(https_chromium_origin(),
+ https_sub_example_origin()));
+ EXPECT_FALSE(SecurityPolicy::IsAccessWhiteListed(https_chromium_origin(),
+ http_example_origin()));
+}
+
+TEST_F(SecurityPolicyAccessTest, IsAccessWhiteListedWildCard) {
+ // An empty domain that matches subdomains results in matching every domain.
+ SecurityPolicy::AddOriginAccessWhitelistEntry(*https_chromium_origin(),
+ "https", "", true);
+ EXPECT_TRUE(SecurityPolicy::IsAccessWhiteListed(https_chromium_origin(),
+ https_example_origin()));
+ EXPECT_TRUE(SecurityPolicy::IsAccessWhiteListed(https_chromium_origin(),
+ https_google_origin()));
+ EXPECT_FALSE(SecurityPolicy::IsAccessWhiteListed(https_chromium_origin(),
+ http_example_origin()));
+}
+
+TEST_F(SecurityPolicyAccessTest, IsAccessWhiteListedWithBlacklistedEntries) {
+ // The blacklists take priority over the whitelist.
+ SecurityPolicy::AddOriginAccessWhitelistEntry(*https_chromium_origin(),
+ "https", "example.com", true);
+ SecurityPolicy::AddOriginAccessBlacklistEntry(*https_chromium_origin(),
+ "https", "example.com", false);
+
+ EXPECT_FALSE(SecurityPolicy::IsAccessWhiteListed(https_chromium_origin(),
+ https_example_origin()));
+ EXPECT_TRUE(SecurityPolicy::IsAccessWhiteListed(https_chromium_origin(),
+ https_sub_example_origin()));
+}
+
+TEST_F(SecurityPolicyAccessTest,
+ IsAccessWhiteListedWildcardWithBlacklistedEntries) {
+ SecurityPolicy::AddOriginAccessWhitelistEntry(*https_chromium_origin(),
+ "https", "", true);
+ SecurityPolicy::AddOriginAccessBlacklistEntry(*https_chromium_origin(),
+ "https", "google.com", false);
+
+ EXPECT_TRUE(SecurityPolicy::IsAccessWhiteListed(https_chromium_origin(),
+ https_example_origin()));
+ EXPECT_FALSE(SecurityPolicy::IsAccessWhiteListed(https_chromium_origin(),
+ https_google_origin()));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/weborigin/security_violation_reporting_policy.h b/chromium/third_party/blink/renderer/platform/weborigin/security_violation_reporting_policy.h
new file mode 100644
index 00000000000..363658cbca8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/weborigin/security_violation_reporting_policy.h
@@ -0,0 +1,17 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_SECURITY_VIOLATION_REPORTING_POLICY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_SECURITY_VIOLATION_REPORTING_POLICY_H_
+
+namespace blink {
+
+enum class SecurityViolationReportingPolicy {
+ kSuppressReporting,
+ kReport,
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_SECURITY_VIOLATION_REPORTING_POLICY_H_
diff --git a/chromium/third_party/blink/renderer/platform/weborigin/url_security_origin_map.h b/chromium/third_party/blink/renderer/platform/weborigin/url_security_origin_map.h
new file mode 100644
index 00000000000..660096c1b1b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/weborigin/url_security_origin_map.h
@@ -0,0 +1,54 @@
+/*
+* Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_URL_SECURITY_ORIGIN_MAP_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_URL_SECURITY_ORIGIN_MAP_H_
+
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class KURL;
+class SecurityOrigin;
+
+class URLSecurityOriginMap {
+ USING_FAST_MALLOC(URLSecurityOriginMap);
+ WTF_MAKE_NONCOPYABLE(URLSecurityOriginMap);
+
+ public:
+ URLSecurityOriginMap() = default;
+ virtual ~URLSecurityOriginMap() = default;
+ virtual SecurityOrigin* GetOrigin(const KURL&) = 0;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_URL_SECURITY_ORIGIN_MAP_H_
diff --git a/chromium/third_party/blink/renderer/platform/win/precompile_platform.cc b/chromium/third_party/blink/renderer/platform/win/precompile_platform.cc
new file mode 100644
index 00000000000..541965119e1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/win/precompile_platform.cc
@@ -0,0 +1,7 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Precompiled header generator for Windows builds. No include is needed
+// in this file as the PCH include is forced via the "Forced Include File"
+// flag.
diff --git a/chromium/third_party/blink/renderer/platform/windows_keyboard_codes.h b/chromium/third_party/blink/renderer/platform/windows_keyboard_codes.h
new file mode 100644
index 00000000000..36176e08b1f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/windows_keyboard_codes.h
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2010 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 VK_UNKNOWN
+
+#define VK_UNKNOWN 0
+
+// Left mouse button
+// Right mouse button
+// Control-break processing
+// Middle mouse button (three-button mouse)
+// VK_XBUTTON1 (05)
+// VK_XBUTTON2 (06)
+
+#ifndef VK_BACK
+#define VK_BACK 0x08
+#endif
+#ifndef VK_TAB
+#define VK_TAB 0x09
+#endif
+#ifndef VK_CLEAR
+#define VK_CLEAR 0x0C
+#endif
+#ifndef VK_RETURN
+#define VK_RETURN 0x0D
+#endif
+#ifndef VK_SHIFT
+#define VK_SHIFT 0x10
+#endif
+#ifndef VK_CONTROL
+#define VK_CONTROL 0x11 // CTRL key
+#endif
+#ifndef VK_MENU
+#define VK_MENU 0x12 // ALT key
+#endif
+#ifndef VK_PAUSE
+#define VK_PAUSE 0x13 // PAUSE key
+#endif
+#ifndef VK_CAPITAL
+#define VK_CAPITAL 0x14 // CAPS LOCK key
+#endif
+#ifndef VK_KANA
+#define VK_KANA 0x15 // Input Method Editor (IME) Kana mode
+#endif
+#ifndef VK_HANGUL
+#define VK_HANGUL 0x15 // IME Hangul mode
+#endif
+#ifndef VK_JUNJA
+#define VK_JUNJA 0x17 // IME Junja mode
+#endif
+#ifndef VK_FINAL
+#define VK_FINAL 0x18 // IME final mode
+#endif
+#ifndef VK_HANJA
+#define VK_HANJA 0x19 // IME Hanja mode
+#endif
+#ifndef VK_KANJI
+#define VK_KANJI 0x19 // IME Kanji mode
+#endif
+#ifndef VK_ESCAPE
+#define VK_ESCAPE 0x1B // ESC key
+#endif
+#ifndef VK_CONVERT
+#define VK_CONVERT 0x1C // IME convert
+#endif
+#ifndef VK_NONCONVERT
+#define VK_NONCONVERT 0x1D // IME nonconvert
+#endif
+#ifndef VK_ACCEPT
+#define VK_ACCEPT 0x1E // IME accept
+#endif
+#ifndef VK_MODECHANGE
+#define VK_MODECHANGE 0x1F // IME mode change request
+#endif
+#ifndef VK_SPACE
+#define VK_SPACE 0x20 // SPACE key
+#endif
+#ifndef VK_PRIOR
+#define VK_PRIOR 0x21 // PAGE UP key
+#endif
+#ifndef VK_NEXT
+#define VK_NEXT 0x22 // PAGE DOWN key
+#endif
+#ifndef VK_END
+#define VK_END 0x23 // END key
+#endif
+#ifndef VK_HOME
+#define VK_HOME 0x24 // HOME key
+#endif
+#ifndef VK_LEFT
+#define VK_LEFT 0x25 // LEFT ARROW key
+#endif
+#ifndef VK_UP
+#define VK_UP 0x26 // UP ARROW key
+#endif
+#ifndef VK_RIGHT
+#define VK_RIGHT 0x27 // RIGHT ARROW key
+#endif
+#ifndef VK_DOWN
+#define VK_DOWN 0x28 // DOWN ARROW key
+#endif
+#ifndef VK_SELECT
+#define VK_SELECT 0x29 // SELECT key
+#endif
+#ifndef VK_PRINT
+#define VK_PRINT 0x2A // PRINT key
+#endif
+#ifndef VK_EXECUTE
+#define VK_EXECUTE 0x2B // EXECUTE key
+#endif
+#ifndef VK_SNAPSHOT
+#define VK_SNAPSHOT 0x2C // PRINT SCREEN key
+#endif
+#ifndef VK_INSERT
+#define VK_INSERT 0x2D // INS key
+#endif
+#ifndef VK_DELETE
+#define VK_DELETE 0x2E // DEL key
+#endif
+#ifndef VK_HELP
+#define VK_HELP 0x2F // HELP key
+#endif
+
+#define VK_0 0x30
+#define VK_1 0x31
+#define VK_2 0x32
+#define VK_3 0x33
+#define VK_4 0x34
+#define VK_5 0x35
+#define VK_6 0x36
+#define VK_7 0x37
+#define VK_8 0x38
+#define VK_9 0x39
+#define VK_A 0x41
+#define VK_B 0x42
+#define VK_C 0x43
+#define VK_D 0x44
+#define VK_E 0x45
+#define VK_F 0x46
+#define VK_G 0x47
+#define VK_H 0x48
+#define VK_I 0x49
+#define VK_J 0x4A
+#define VK_K 0x4B
+#define VK_L 0x4C
+#define VK_M 0x4D
+#define VK_N 0x4E
+#define VK_O 0x4F
+#define VK_P 0x50
+#define VK_Q 0x51
+#define VK_R 0x52
+#define VK_S 0x53
+#define VK_T 0x54
+#define VK_U 0x55
+#define VK_V 0x56
+#define VK_W 0x57
+#define VK_X 0x58
+#define VK_Y 0x59
+#define VK_Z 0x5A
+
+#define VK_LWIN 0x5B // Left Windows key (Microsoft Natural keyboard)
+
+#define VK_RWIN 0x5C // Right Windows key (Natural keyboard)
+
+#define VK_APPS 0x5D // Applications key (Natural keyboard)
+
+#define VK_SLEEP 0x5F // Computer Sleep key
+
+// Num pad keys
+#define VK_NUMPAD0 0x60
+#define VK_NUMPAD1 0x61
+#define VK_NUMPAD2 0x62
+#define VK_NUMPAD3 0x63
+#define VK_NUMPAD4 0x64
+#define VK_NUMPAD5 0x65
+#define VK_NUMPAD6 0x66
+#define VK_NUMPAD7 0x67
+#define VK_NUMPAD8 0x68
+#define VK_NUMPAD9 0x69
+#define VK_MULTIPLY 0x6A
+#define VK_ADD 0x6B
+#define VK_SEPARATOR 0x6C
+#define VK_SUBTRACT 0x6D
+#define VK_DECIMAL 0x6E
+#define VK_DIVIDE 0x6F
+
+#define VK_F1 0x70
+#define VK_F2 0x71
+#define VK_F3 0x72
+#define VK_F4 0x73
+#define VK_F5 0x74
+#define VK_F6 0x75
+#define VK_F7 0x76
+#define VK_F8 0x77
+#define VK_F9 0x78
+#define VK_F10 0x79
+#define VK_F11 0x7A
+#define VK_F12 0x7B
+#define VK_F13 0x7C
+#define VK_F14 0x7D
+#define VK_F15 0x7E
+#define VK_F16 0x7F
+#define VK_F17 0x80
+#define VK_F18 0x81
+#define VK_F19 0x82
+#define VK_F20 0x83
+#define VK_F21 0x84
+#define VK_F22 0x85
+#define VK_F23 0x86
+#define VK_F24 0x87
+
+#define VK_NUMLOCK 0x90
+#define VK_SCROLL 0x91
+#define VK_LSHIFT 0xA0
+#define VK_RSHIFT 0xA1
+#define VK_LCONTROL 0xA2
+#define VK_RCONTROL 0xA3
+#define VK_LMENU 0xA4
+#define VK_RMENU 0xA5
+
+#define VK_BROWSER_BACK 0xA6 // Windows 2000/XP: Browser Back key
+#define VK_BROWSER_FORWARD 0xA7 // Windows 2000/XP: Browser Forward key
+#define VK_BROWSER_REFRESH 0xA8 // Windows 2000/XP: Browser Refresh key
+#define VK_BROWSER_STOP 0xA9 // Windows 2000/XP: Browser Stop key
+#define VK_BROWSER_SEARCH 0xAA // Windows 2000/XP: Browser Search key
+#define VK_BROWSER_FAVORITES 0xAB // Windows 2000/XP: Browser Favorites key
+#define VK_BROWSER_HOME 0xAC // Windows 2000/XP: Browser Start and Home key
+#define VK_VOLUME_MUTE 0xAD // Windows 2000/XP: Volume Mute key
+#define VK_VOLUME_DOWN 0xAE // Windows 2000/XP: Volume Down key
+#define VK_VOLUME_UP 0xAF // Windows 2000/XP: Volume Up key
+#define VK_MEDIA_NEXT_TRACK 0xB0 // Windows 2000/XP: Next Track key
+#define VK_MEDIA_PREV_TRACK 0xB1 // Windows 2000/XP: Previous Track key
+#define VK_MEDIA_STOP 0xB2 // Windows 2000/XP: Stop Media key
+#define VK_MEDIA_PLAY_PAUSE 0xB3 // Windows 2000/XP: Play/Pause Media key
+#define VK_MEDIA_LAUNCH_MAIL 0xB4 // Windows 2000/XP: Start Mail key
+#define VK_MEDIA_LAUNCH_MEDIA_SELECT 0xB5 // Windows 2000/XP: Select Media key
+#define VK_MEDIA_LAUNCH_APP1 \
+ 0xB6 // VK_LAUNCH_APP1 (B6) Windows 2000/XP: Start Application 1 key
+#define VK_MEDIA_LAUNCH_APP2 \
+ 0xB7 // VK_LAUNCH_APP2 (B7) Windows 2000/XP: Start Application 2 key
+
+// VK_OEM_1 (BA) Used for miscellaneous characters; it can vary by keyboard.
+// Windows 2000/XP: For the US standard keyboard, the ';:' key
+#define VK_OEM_1 0xBA
+
+// Windows 2000/XP: For any country/region, the '+' key
+#define VK_OEM_PLUS 0xBB
+
+// Windows 2000/XP: For any country/region, the ',' key
+#define VK_OEM_COMMA 0xBC
+
+// Windows 2000/XP: For any country/region, the '-' key
+#define VK_OEM_MINUS 0xBD
+
+// Windows 2000/XP: For any country/region, the '.' key
+#define VK_OEM_PERIOD 0xBE
+
+// VK_OEM_2 (BF) Used for miscellaneous characters; it can vary by keyboard.
+// Windows 2000/XP: For the US standard keyboard, the '/?' key
+#define VK_OEM_2 0xBF
+
+// VK_OEM_3 (C0) Used for miscellaneous characters; it can vary by keyboard.
+// Windows 2000/XP: For the US standard keyboard, the '`~' key
+#define VK_OEM_3 0xC0
+
+// VK_OEM_4 (DB) Used for miscellaneous characters; it can vary by keyboard.
+// Windows 2000/XP: For the US standard keyboard, the '[{' key
+#define VK_OEM_4 0xDB
+
+// VK_OEM_5 (DC) Used for miscellaneous characters; it can vary by keyboard.
+// Windows 2000/XP: For the US standard keyboard, the '\|' key
+#define VK_OEM_5 0xDC
+
+// VK_OEM_6 (DD) Used for miscellaneous characters; it can vary by keyboard.
+// Windows 2000/XP: For the US standard keyboard, the ']}' key
+#define VK_OEM_6 0xDD
+
+// VK_OEM_7 (DE) Used for miscellaneous characters; it can vary by keyboard.
+// Windows 2000/XP: For the US standard keyboard, the
+// 'single-quote/double-quote' key
+#define VK_OEM_7 0xDE
+
+// VK_OEM_8 (DF) Used for miscellaneous characters; it can vary by keyboard.
+#define VK_OEM_8 0xDF
+
+// VK_OEM_102 (E2) Windows 2000/XP: Either the angle bracket key or the
+// backslash key on the RT 102-key keyboard
+#define VK_OEM_102 0xE2
+
+// Windows 95/98/Me, Windows NT 4.0, Windows 2000/XP: IME PROCESS key
+#define VK_PROCESSKEY 0xE5
+
+// Windows 2000/XP: Used to pass Unicode characters as if they were keystrokes.
+// The VK_PACKET key is the low word of a 32-bit Virtual Key value used for
+// non-keyboard input methods. For more information, see Remark in
+// KEYBDINPUT,SendInput, WM_KEYDOWN, and WM_KEYUP
+#define VK_PACKET 0xE7
+
+#define VK_ATTN 0xF6 // Attn key
+#define VK_CRSEL 0xF7 // CrSel key
+#define VK_EXSEL 0xF8 // ExSel key
+#define VK_EREOF 0xF9 // Erase EOF key
+#define VK_PLAY 0xFA // Play key
+#define VK_ZOOM 0xFB // Zoom key
+
+#define VK_NONAME 0xFC // Reserved for future use
+
+#define VK_PA1 0xFD // VK_PA1 (FD) PA1 key
+
+#define VK_OEM_CLEAR 0xFE // Clear key
+
+#endif // VK_UNKNOWN
diff --git a/chromium/third_party/blink/renderer/platform/wtf/Allocator.md b/chromium/third_party/blink/renderer/platform/wtf/Allocator.md
new file mode 100644
index 00000000000..cf2d939fc87
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/Allocator.md
@@ -0,0 +1,186 @@
+# Memory management in Blink
+
+This document gives a high-level overview of the memory management in Blink.
+
+[TOC]
+
+## Memory allocators
+
+Blink objects are allocated by one of the following four memory allocators.
+
+### Oilpan
+
+Oilpan is a garbage collection system in Blink.
+The lifetime of objects allocated by Oilpan is automatically managed.
+The following objects are allocated by Oilpan:
+
+* Objects that inherit from GarbageCollected<T> or GarbageCollectedFinalized<T>.
+
+* HeapVector<T>, HeapHashSet<T>, HeapHashMap<T, U> etc
+
+The implementation is in platform/heap/.
+See [BlinkGCDesign.md](../platform/heap/BlinkGCDesign.md) to learn the design.
+
+### PartitionAlloc
+
+PartitionAlloc is Blink's default memory allocator.
+PartitionAlloc is highly optimized for performance and security requirements
+in Blink. All Blink objects that don't need a GC or discardable memory should be
+allocated by PartitionAlloc (instead of malloc).
+The following objects are allocated by PartitionAlloc:
+
+* Objects that have a USING_FAST_MALLOC macro.
+
+* Nodes (which will be moved to Oilpan in the near future)
+
+* LayoutObjects
+
+* Strings, Vectors, HashTables, ArrayBuffers and other primitive containers.
+
+The implementation is in /base/allocator/partition_allocator.
+See [PartitionAlloc.md](/base/allocator/partition_allocator/PartitionAlloc.md)
+to learn the design.
+
+### Discardable memory
+
+Discardable memory is a memory allocator that automatically discards
+(not-locked) objects under memory pressure. Currently SharedBuffers
+(which are mainly used as backing storage of Resource objects) are the only
+user of the discardable memory.
+
+The implementation is in src/base/memory/discardable_memory.*.
+See [this document](https://docs.google.com/document/d/1aNdOF_72_eG2KUM_z9kHdbT_fEupWhaDALaZs5D8IAg/edit)
+to learn the design.
+
+### malloc
+
+When you simply call 'new' or 'malloc', the object is allocated by malloc.
+As mentioned above, malloc is discouraged and should be replaced with
+PartitionAlloc. PartitionAlloc is faster, securer and more instrumentable
+than malloc.
+
+The actual implementation of malloc is platform-dependent.
+As of 2015 Dec, Linux uses tcmalloc. Mac and Windows use their system allocator.
+Android uses device-dependent allocators such as dlmalloc.
+
+## Basic allocation rules
+
+In summary, Blink objects (except several special objects) should be allocated
+using Oilpan or PartitionAlloc. malloc is discouraged.
+
+The following is a basic rule to determine which of Oilpan or PartitionAlloc
+you should use when allocating a new object:
+
+* Use Oilpan if you want a GC to manage the lifetime of the object.
+You need to make the object inherit from GarbageCollected<T> or
+GarbageCollectedFinalized<T>. See
+[BlinkGCAPIReference.md](../platform/heap/BlinkGCAPIReference.md) to learn
+programming with Oilpan.
+
+```c++
+class X : public GarbageCollected<X> {
+ ...;
+};
+
+void func() {
+ X* x = new X; // This is allocated by Oilpan.
+}
+```
+
+* Use PartitionAlloc if you don't need a GC to manage the lifetime of
+the object (i.e., if RefPtr or OwnPtr is enough to manage the lifetime
+of the object). You need to add a USING_FAST_MALLOC macro to the object.
+
+```c++
+class X {
+ USING_FAST_MALLOC(X);
+ ...;
+};
+
+void func() {
+ scoped_refptr<X> x = adoptRefPtr(new X); // This is allocated by PartitionAlloc.
+}
+```
+
+It is not a good idea to unnecessarily increase the number of objects
+managed by Oilpan. Although Oilpan implements an efficient GC, the more objects
+you allocate on Oilpan, the more pressure you put on Oilpan, leading to
+a longer pause time.
+
+Here is a guideline for when you ought to allocate an object using Oilpan,
+and when you probably shouldn't:
+
+* Use Oilpan for all script exposed objects (i.e., derives from
+ScriptWrappable).
+
+* Use Oilpan if managing its lifetime is usually simpler with Oilpan.
+But see the next bullet.
+
+* If the allocation rate of the object is very high, that may put unnecessary
+strain on the Oilpan's GC infrastructure as a whole. If so and the object can be
+allocated not using Oilpan without creating cyclic references or complex
+lifetime handling, then use PartitionAlloc. For example, we allocate Strings
+and LayoutObjects on PartitionAlloc.
+
+For objects that don't need an operator new, you need to use either of the
+following macros:
+
+* If the object is only stack allocated, use STACK_ALLOCATED().
+
+```c++
+class X {
+ STACK_ALLOCATED();
+ ...;
+};
+
+void func() {
+ X x; // This is allowed.
+ X* x2 = new X; // This is forbidden.
+}
+```
+
+* If the object can be allocated only as a part of object, use DISALLOW_NEW().
+
+```c++
+class X {
+ DISALLOW_NEW();
+ ...;
+};
+
+class Y {
+ USING_FAST_MALLOC(Y);
+ X m_x; // This is allowed.
+};
+
+void func() {
+ X x; // This is allowed.
+ X* x = new X; // This is forbidden.
+}
+```
+
+* If the object can be allocated only as a part of object or by a placement new
+(e.g., the object is used as a value object in a container),
+use DISALLOW_NEW_EXCEPT_PLACEMENT_NEW().
+
+```c++
+class X {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+ ...;
+};
+
+class Y {
+ USING_FAST_MALLOC(Y);
+ X m_x; // This is allowed.
+ Vector<X> m_vector; // This is allowed.
+};
+
+void func() {
+ X x; // This is allowed.
+ X* x = new X; // This is forbidden.
+}
+```
+
+Note that these macros are inherited. See a comment in wtf/Allocator.h
+for more details about the relationship between the macros and Oilpan.
+
+If you have any question, ask oilpan-reviews@chromium.org.
diff --git a/chromium/third_party/blink/renderer/platform/wtf/BUILD.gn b/chromium/third_party/blink/renderer/platform/wtf/BUILD.gn
new file mode 100644
index 00000000000..e550bd2c7d8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/BUILD.gn
@@ -0,0 +1,369 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+assert(!is_ios)
+
+import("//build/config/jumbo.gni")
+import("//testing/test.gni")
+import("//third_party/blink/renderer/config.gni")
+
+visibility = [
+ ":*",
+ "//mojo/public/cpp/bindings/*",
+ "//third_party/blink/*",
+]
+
+config("wtf_config") {
+ if (is_win) {
+ defines = [
+ "__STD_C",
+ "_CRT_SECURE_NO_DEPRECATE",
+ "_SCL_SECURE_NO_DEPRECATE",
+ ]
+ include_dirs = [ "os-win32" ]
+
+ cflags = [
+ # Don't complain about calling specific versions of templatized
+ # functions (e.g. in RefPtrHashMap.h).
+ "/wd4344",
+
+ # dtoa, icu, etc. like doing assignment within conditional.
+ "/wd4706",
+ ]
+
+ if (is_component_build) {
+ # Chromium windows multi-dll build enables C++ exceptions and this causes
+ # wtf to generate 4291 warning due to operator new/delete
+ # implementations. Disable the warning for chromium windows multi-dll
+ # build.
+ cflags += [ "/wd4291" ]
+ }
+ }
+}
+
+jumbo_component("wtf") {
+ sources = [
+ "address_sanitizer.h",
+ "alignment.h",
+ "allocator.h",
+ "allocator/partition_allocator.cc",
+ "allocator/partition_allocator.h",
+ "allocator/partitions.cc",
+ "allocator/partitions.h",
+ "ascii_ctype.cc",
+ "ascii_ctype.h",
+ "assertions.cc",
+ "assertions.h",
+ "atomics.h",
+ "auto_reset.h",
+ "bit_vector.cc",
+ "bit_vector.h",
+ "bloom_filter.h",
+ "byte_order.h",
+ "byte_swap.h",
+ "checked_numeric.h",
+ "compiler.h",
+ "conditional_destructor.h",
+ "container_annotations.h",
+ "cpu.h",
+ "cryptographically_random_number.cc",
+ "cryptographically_random_number.h",
+ "date_math.cc",
+ "date_math.h",
+ "deque.h",
+ "doubly_linked_list.h",
+ "dtoa.cc",
+ "dtoa.h",
+ "dtoa/bignum-dtoa.cc",
+ "dtoa/bignum-dtoa.h",
+ "dtoa/bignum.cc",
+ "dtoa/bignum.h",
+ "dtoa/cached-powers.cc",
+ "dtoa/cached-powers.h",
+ "dtoa/diy-fp.cc",
+ "dtoa/diy-fp.h",
+ "dtoa/double-conversion.cc",
+ "dtoa/double-conversion.h",
+ "dtoa/double.h",
+ "dtoa/fast-dtoa.cc",
+ "dtoa/fast-dtoa.h",
+ "dtoa/fixed-dtoa.cc",
+ "dtoa/fixed-dtoa.h",
+ "dtoa/strtod.cc",
+ "dtoa/strtod.h",
+ "dtoa/utils.h",
+ "dynamic_annotations.cc",
+ "dynamic_annotations.h",
+ "experimental/container_type_operations.h",
+ "forward.h",
+ "functional.h",
+ "get_ptr.h",
+ "hash_counted_set.h",
+ "hash_functions.h",
+ "hash_iterators.h",
+ "hash_map.h",
+ "hash_set.h",
+ "hash_table.cc",
+ "hash_table.h",
+ "hash_table_deleted_value_type.h",
+ "hash_traits.h",
+ "hex_number.h",
+ "leak_annotations.h",
+ "linked_hash_set.h",
+ "list_hash_set.h",
+ "locker.h",
+ "math_extras.h",
+ "noncopyable.h",
+ "not_found.h",
+ "optional.h",
+ "process_metrics.h",
+ "ref_counted.h",
+ "ref_vector.h",
+ "retain_ptr.h",
+ "saturated_arithmetic.h",
+ "saturated_arithmetic_arm.h",
+ "scoped_logger.cc",
+ "scoped_logger.h",
+ "size_assertions.h",
+ "size_limits.cc",
+ "stack_util.cc",
+ "stack_util.h",
+ "static_constructors.h",
+ "std_lib_extras.h",
+ "string_extras.h",
+ "string_hasher.h",
+ "terminated_array.h",
+ "terminated_array_builder.h",
+ "text/ascii_fast_path.h",
+ "text/atomic_string.cc",
+ "text/atomic_string.h",
+ "text/atomic_string_cf.cc",
+ "text/atomic_string_hash.h",
+ "text/atomic_string_table.cc",
+ "text/atomic_string_table.h",
+ "text/base64.cc",
+ "text/base64.h",
+ "text/character_names.h",
+ "text/collator.h",
+ "text/cstring.cc",
+ "text/cstring.h",
+ "text/icu/collator_icu.cc",
+ "text/icu/unicode_icu.h",
+ "text/integer_to_string_conversion.h",
+ "text/number_parsing_options.h",
+ "text/parsing_utilities.h",
+ "text/string_buffer.h",
+ "text/string_builder.cc",
+ "text/string_builder.h",
+ "text/string_concatenate.cc",
+ "text/string_concatenate.h",
+ "text/string_hash.h",
+ "text/string_impl.cc",
+ "text/string_impl.h",
+ "text/string_impl_cf.cc",
+ "text/string_impl_mac.mm",
+ "text/string_mac.mm",
+ "text/string_operators.h",
+ "text/string_statics.cc",
+ "text/string_statics.h",
+ "text/string_statics.h",
+ "text/string_to_number.cc",
+ "text/string_to_number.h",
+ "text/string_utf8_adaptor.h",
+ "text/string_view.cc",
+ "text/string_view.h",
+ "text/text_codec.cc",
+ "text/text_codec.h",
+ "text/text_codec_ascii_fast_path.h",
+ "text/text_codec_icu.cc",
+ "text/text_codec_icu.h",
+ "text/text_codec_latin1.cc",
+ "text/text_codec_latin1.h",
+ "text/text_codec_replacement.cc",
+ "text/text_codec_replacement.h",
+ "text/text_codec_user_defined.cc",
+ "text/text_codec_user_defined.h",
+ "text/text_codec_utf16.cc",
+ "text/text_codec_utf16.h",
+ "text/text_codec_utf8.cc",
+ "text/text_codec_utf8.h",
+ "text/text_encoding.cc",
+ "text/text_encoding.h",
+ "text/text_encoding_registry.cc",
+ "text/text_encoding_registry.h",
+ "text/text_position.cc",
+ "text/text_position.h",
+ "text/unicode.h",
+ "text/utf8.cc",
+ "text/utf8.h",
+ "text/wtf_string.cc",
+ "text/wtf_string.h",
+ "thread_restriction_verifier.h",
+ "thread_safe_ref_counted.h",
+ "thread_specific.h",
+ "threading.h",
+ "threading_primitives.h",
+ "threading_pthreads.cc",
+ "threading_win.cc",
+ "time.cc",
+ "time.h",
+ "tree_node.h",
+ "type_traits.h",
+ "typed_arrays/array_buffer.cc",
+ "typed_arrays/array_buffer.h",
+ "typed_arrays/array_buffer_builder.cc",
+ "typed_arrays/array_buffer_builder.h",
+ "typed_arrays/array_buffer_contents.cc",
+ "typed_arrays/array_buffer_contents.h",
+ "typed_arrays/array_buffer_view.cc",
+ "typed_arrays/array_buffer_view.h",
+ "typed_arrays/array_piece.cc",
+ "typed_arrays/array_piece.h",
+ "typed_arrays/bigint64_array.h",
+ "typed_arrays/biguint64_array.h",
+ "typed_arrays/float32_array.h",
+ "typed_arrays/float64_array.h",
+ "typed_arrays/int16_array.h",
+ "typed_arrays/int32_array.h",
+ "typed_arrays/int8_array.h",
+ "typed_arrays/integral_typed_array_base.h",
+ "typed_arrays/typed_array_base.h",
+ "typed_arrays/uint16_array.h",
+ "typed_arrays/uint32_array.h",
+ "typed_arrays/uint8_array.h",
+ "typed_arrays/uint8_clamped_array.h",
+ "vector.h",
+ "vector_traits.h",
+ "wtf.cc",
+ "wtf.h",
+ "wtf_export.h",
+ "wtf_thread_data.cc",
+ "wtf_thread_data.h",
+ ]
+
+ configs += [
+ "//third_party/blink/renderer:config",
+ "//third_party/blink/renderer:non_test_config",
+ "//third_party/blink/renderer:blink_pch",
+ ]
+
+ defines = [ "WTF_IMPLEMENTATION=1" ]
+
+ public_configs = [
+ ":wtf_config",
+
+ # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+ "//build/config/compiler:no_size_t_to_int_warning",
+ "//third_party/blink/renderer:features",
+ ]
+
+ public_deps = [
+ "//base",
+ "//third_party/icu",
+ ]
+
+ # Rules changing the |sources| list are temporarily commented out, until
+ # those files are actually moved to here.
+
+ if (is_win) {
+ sources -= [ "threading_pthreads.cc" ]
+
+ cflags = [ "/wd4068" ] # Unknown pragma.
+
+ # Inject Visual Studio debug visualizers for our types into all targets
+ # that reference this.
+ all_dependent_configs = [ "//tools/win/DebugVisualizers:webkit" ]
+ }
+
+ if (is_android) {
+ libs = [ "log" ]
+ }
+ if (is_linux) {
+ libs = [ "dl" ]
+ }
+
+ if (is_mac) {
+ libs = [
+ "CoreFoundation.framework",
+ "Foundation.framework",
+ ]
+ } else {
+ sources -= [
+ "text/atomic_string_cf.cc",
+ "text/string_impl_cf.cc",
+ ]
+ }
+
+ configs -= [ "//build/config/compiler:default_symbols" ]
+ configs += blink_symbols_config
+}
+
+test("wtf_unittests") {
+ deps = [
+ ":wtf_unittests_sources",
+ ]
+}
+
+jumbo_source_set("wtf_unittests_sources") {
+ visibility = [] # Allow re-assignment of list.
+ visibility = [ "*" ]
+ testonly = true
+
+ sources = [
+ "ascii_ctype_test.cc",
+ "assertions_test.cc",
+ "deque_test.cc",
+ "dtoa_test.cc",
+ "experimental/container_type_operations_test.cc",
+ "functional_test.cc",
+ "hash_map_test.cc",
+ "hash_set_test.cc",
+ "list_hash_set_test.cc",
+ "math_extras_test.cc",
+ "optional_test.cc",
+ "ref_ptr_test.cc",
+ "saturated_arithmetic_test.cc",
+ "scoped_logger_test.cc",
+ "string_hasher_test.cc",
+ "testing/run_all_tests.cc",
+ "text/atomic_string_test.cc",
+ "text/cstring_test.cc",
+ "text/integer_to_string_conversion_test.cc",
+ "text/string_buffer_test.cc",
+ "text/string_builder_test.cc",
+ "text/string_impl_test.cc",
+ "text/string_to_number_test.cc",
+ "text/string_view_test.cc",
+ "text/text_codec_icu_test.cc",
+ "text/text_codec_replacement_test.cc",
+ "text/text_codec_test.cc",
+ "text/text_codec_utf8_test.cc",
+ "text/text_encoding_test.cc",
+ "text/wtf_string_test.cc",
+ "tree_node_test.cc",
+ "type_traits_test.cc",
+ "typed_arrays/array_buffer_builder_test.cc",
+ "vector_test.cc",
+ ]
+
+ # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+ configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
+ if (is_win) {
+ cflags = [ "/wd4068" ] # Unknown pragma.
+ }
+
+ configs += [
+ "//third_party/blink/renderer:config",
+ "//third_party/blink/renderer:blink_pch",
+ ]
+
+ deps = [
+ ":wtf",
+ "//base",
+ "//base/test:test_support",
+ "//testing/gmock",
+ "//testing/gtest",
+ ]
+}
diff --git a/chromium/third_party/blink/renderer/platform/wtf/CONTRIBUTORS.pthreads-win32 b/chromium/third_party/blink/renderer/platform/wtf/CONTRIBUTORS.pthreads-win32
new file mode 100644
index 00000000000..7de0f260659
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/CONTRIBUTORS.pthreads-win32
@@ -0,0 +1,137 @@
+This is a copy of CONTRIBUTORS file for the Pthreads-win32 library, downloaded
+from http://sourceware.org/cgi-bin/cvsweb.cgi/~checkout~/pthreads/CONTRIBUTORS?rev=1.32&cvsroot=pthreads-win32
+
+Included here to compliment the Pthreads-win32 license header in wtf/ThreadingWin.cpp file.
+WebKit is using derived sources of ThreadCondition code from Pthreads-win32.
+
+-------------------------------------------------------------------------------
+
+Contributors (in approximate order of appearance)
+
+[See also the ChangeLog file where individuals are
+attributed in log entries. Likewise in the FAQ file.]
+
+Ben Elliston bje at cygnus dot com
+ Initiated the project;
+ setup the project infrastructure (CVS, web page, etc.);
+ early prototype routines.
+Ross Johnson rpj at callisto dot canberra dot edu dot au
+ early prototype routines;
+ ongoing project coordination/maintenance;
+ implementation of spin locks and barriers;
+ various enhancements;
+ bug fixes;
+ documentation;
+ testsuite.
+Robert Colquhoun rjc at trump dot net dot au
+ Early bug fixes.
+John E. Bossom John dot Bossom at cognos dot com
+ Contributed substantial original working implementation;
+ bug fixes;
+ ongoing guidance and standards interpretation.
+Anders Norlander anorland at hem2 dot passagen dot se
+ Early enhancements and runtime checking for supported
+ Win32 routines.
+Tor Lillqvist tml at iki dot fi
+ General enhancements;
+ early bug fixes to condition variables.
+Scott Lightner scott at curriculum dot com
+ Bug fix.
+Kevin Ruland Kevin dot Ruland at anheuser-busch dot com
+ Various bug fixes.
+Mike Russo miker at eai dot com
+ Bug fix.
+Mark E. Armstrong avail at pacbell dot net
+ Bug fixes.
+Lorin Hochstein lmh at xiphos dot ca
+ general bug fixes; bug fixes to condition variables.
+Peter Slacik Peter dot Slacik at tatramed dot sk
+ Bug fixes.
+Mumit Khan khan at xraylith dot wisc dot edu
+ Fixes to work with Mingw32.
+Milan Gardian mg at tatramed dot sk
+ Bug fixes and reports/analyses of obscure problems.
+Aurelio Medina aureliom at crt dot com
+ First implementation of read-write locks.
+Graham Dumpleton Graham dot Dumpleton at ra dot pad dot otc dot telstra dot com dot au
+ Bug fix in condition variables.
+Tristan Savatier tristan at mpegtv dot com
+ WinCE port.
+Erik Hensema erik at hensema dot xs4all dot nl
+ Bug fixes.
+Rich Peters rpeters at micro-magic dot com
+Todd Owen towen at lucidcalm dot dropbear dot id dot au
+ Bug fixes to dll loading.
+Jason Nye jnye at nbnet dot nb dot ca
+ Implementation of async cancelation.
+Fred Forester fforest at eticomm dot net
+Kevin D. Clark kclark at cabletron dot com
+David Baggett dmb at itasoftware dot com
+ Bug fixes.
+Paul Redondo paul at matchvision dot com
+Scott McCaskill scott at 3dfx dot com
+ Bug fixes.
+Jef Gearhart jgearhart at tpssys dot com
+ Bug fix.
+Arthur Kantor akantor at bexusa dot com
+ Mutex enhancements.
+Steven Reddie smr at essemer dot com dot au
+ Bug fix.
+Alexander Terekhov TEREKHOV at de dot ibm dot com
+ Re-implemented and improved read-write locks;
+ (with Louis Thomas) re-implemented and improved
+ condition variables;
+ enhancements to semaphores;
+ enhancements to mutexes;
+ new mutex implementation in 'futex' style;
+ suggested a robust implementation of pthread_once
+ similar to that implemented by V.Kliathcko;
+ system clock change handling re CV timeouts;
+ bug fixes.
+Thomas Pfaff tpfaff at gmx dot net
+ Changes to make C version usable with C++ applications;
+ re-implemented mutex routines to avoid Win32 mutexes
+ and TryEnterCriticalSection;
+ procedure to fix Mingw32 thread-safety issues.
+Franco Bez franco dot bez at gmx dot de
+ procedure to fix Mingw32 thread-safety issues.
+Louis Thomas lthomas at arbitrade dot com
+ (with Alexander Terekhov) re-implemented and improved
+ condition variables.
+David Korn dgk at research dot att dot com
+ Ported to UWIN.
+Phil Frisbie, Jr. phil at hawksoft dot com
+ Bug fix.
+Ralf Brese Ralf dot Brese at pdb4 dot siemens dot de
+ Bug fix.
+prionx at juno dot com prionx at juno dot com
+ Bug fixes.
+Max Woodbury mtew at cds dot duke dot edu
+ POSIX versioning conditionals;
+ reduced namespace pollution;
+ idea to separate routines to reduce statically
+ linked image sizes.
+Rob Fanner rfanner at stonethree dot com
+ Bug fix.
+Michael Johnson michaelj at maine dot rr dot com
+ Bug fix.
+Nicolas Barry boozai at yahoo dot com
+ Bug fixes.
+Piet van Bruggen pietvb at newbridges dot nl
+ Bug fix.
+Makoto Kato raven at oldskool dot jp
+ AMD64 port.
+Panagiotis E. Hadjidoukas peh at hpclab dot ceid dot upatras dot gr
+ Contributed the QueueUserAPCEx package which
+ makes preemptive async cancelation possible.
+Will Bryant will dot bryant at ecosm dot com
+ Borland compiler patch and makefile.
+Anuj Goyal anuj dot goyal at gmail dot com
+ Port to Digital Mars compiler.
+Gottlob Frege gottlobfrege at gmail dot com
+ re-implemented pthread_once (version 2)
+ (pthread_once cancellation added by rpj).
+Vladimir Kliatchko vladimir at kliatchko dot com
+ reimplemented pthread_once with the same form
+ as described by A.Terekhov (later version 2);
+ implementation of MCS (Mellor-Crummey/Scott) locks. \ No newline at end of file
diff --git a/chromium/third_party/blink/renderer/platform/wtf/DEPS b/chromium/third_party/blink/renderer/platform/wtf/DEPS
new file mode 100644
index 00000000000..886c40c8564
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/DEPS
@@ -0,0 +1,36 @@
+include_rules = [
+ # To whitelist base/ stuff Blink is allowed to include, we list up all
+ # directories and files instead of writing 'base/'.
+ "+base/allocator/partition_allocator",
+ "+base/atomic_ref_count.h",
+ "+base/auto_reset.h",
+ "+base/bind.h",
+ "+base/bits.h",
+ "+base/compiler_specific.h",
+ "+base/logging.h",
+ "+base/memory/ptr_util.h",
+ "+base/memory/ref_counted.h",
+ "+base/memory/weak_ptr.h",
+ "+base/numerics",
+ "+base/optional.h",
+ "+base/process/process_metrics.h",
+ "+base/rand_util.h",
+ "+base/stl_util.h",
+ "+base/strings",
+ "+base/template_util.h",
+ "+base/threading/thread_checker.h",
+ "+base/threading/thread_local_storage.h",
+ "+base/time/time.h",
+ "+base/tuple.h",
+ # To avoid recursive dependency, we impose a blanket ban on using other
+ # platform files. Think carefully if you want to relax this restriction.
+ "-third_party/blink/renderer/platform",
+ "+third_party/blink/renderer/platform/wtf",
+ "-v8",
+]
+
+specific_include_rules = {
+ ".*_test\.cc": [
+ "+base/threading/thread.h"
+ ]
+}
diff --git a/chromium/third_party/blink/renderer/platform/wtf/OWNERS b/chromium/third_party/blink/renderer/platform/wtf/OWNERS
new file mode 100644
index 00000000000..0be7ad9c02c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/OWNERS
@@ -0,0 +1,10 @@
+erik.corry@gmail.com
+haraken@chromium.org
+jochen@chromium.org
+mikhail.pozdnyakov@intel.com
+thakis@chromium.org
+tkent@chromium.org
+yutak@chromium.org
+
+# TEAM: platform-architecture-dev@chromium.org
+# COMPONENT: Blink>Internals>WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/README.md b/chromium/third_party/blink/renderer/platform/wtf/README.md
new file mode 100644
index 00000000000..be3012efb34
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/README.md
@@ -0,0 +1,122 @@
+# WTF (Web Template Framework)
+
+WTF is a base library for Blink providing a variety of basic functionalities,
+like containers, string libraries, reference counting mechanisms, functors,
+threading primitives etc.
+
+WTF's mission is to power and support all other Blink code base by providing
+fast, reliable, user-friendly and secure generic primitives.
+
+Dependency-wise, WTF cannot depend on any other Blink headers, including
+files under other platform/ subdirectories, since WTF is a library that can be
+referred from anywhere in Blink. WTF basically can only depend on [base].
+
+Main code base of Blink (core and modules) cannot directly depend on [base].
+The main objective of this is to limit what Blink core can use, so that Blink
+core code wouldn't use libraries prohibited in Blink accidentally (such as
+`std::string` or `std::vector`).
+
+Our approach here is to make WTF and other platform/ components a gatekeeper of
+the use of [base] libraries. platform/ (including WTF) can directly depend on
+[base], and if some of the [base] functionalities are worth having in Blink,
+WTF can expose them in a way Blink core code can easily use them. Usually,
+such a library would be a thin wrapper of the corresponding [base]
+functionality.
+
+Also, we are trying to eliminate duplicated functionalities between WTF and
+[base]. Historically, WTF was developed as a stand-alone library, so there
+are still many overlaps. We want to eventually delegate sharable implementation
+to [base] as much as possible.
+
+If you find a missing functionality in WTF, regardless of whether it is
+available in [base] or not, feel free to file a bug under the component
+Blink>Internals>WTF.
+
+## Library catalog
+
+The below is a list of major libraries. For a complete list, look at
+[the directory listing].
+
+* **Containers**
+
+ [Vector], [HashSet], [HashMap], [Deque]
+
+* **Strings**
+
+ [String], [AtomicString], [StringBuilder], [CString]
+
+* **Reference counting**
+
+ [RefCounted], [RefPtr]
+
+* **Memory**
+
+ [Allocator.h] (memory placement macros)
+
+* **Functors, binding**
+
+ [Functional.h]
+
+* **Threading**
+
+ [Threading.h], [ThreadingPrimitives.h]
+
+* **Compile-time switch macros**
+
+ [Compiler.h] (e.g. `COMPILER(GCC)`),
+ [CPU.h] (e.g. `WTF_CPU_ARM_NEON`),
+
+* **Miscellaneous**
+
+ [StdLibExtras.h] (`DEFINE_STATIC_LOCAL` etc.),
+ [Time.h],
+ [CryptographicallyRandomNumber.h],
+ [AutoReset.h],
+ [Optional.h]
+
+## History
+
+The name WTF first [appeared in 2006][1], as a replacement of its older name
+KXMLCore. At that point, there were already plenty of libraries we see today.
+For example, you can see [the initial implementation of `Vector`][2] was landed
+in 2006, replacing several manual array allocations and deallocations(!).
+
+If you dig the repository a bit more, you can find the original version of
+Assertions.h was [committed back in 2002][3]. This is probably the oldest
+library that we can find from the repository history.
+
+As you see, pretty much everything that we have today in WTF was created in
+the WebKit era. WTF was initially under the directory Source/JavaScriptCore,
+but it moved to Source/WTF/wtf in 2011-2012, then to Source/wtf in 2013.
+
+Blink forked WebKit in 2013. In 2017, the directory finally [moved to the
+current location][4] Source/platform/wtf.
+
+[the directory listing]: https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/wtf/
+[base]: https://cs.chromium.org/chromium/src/base/
+[Vector]: https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/wtf/vector.h
+[HashSet]: https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/wtf/hash_set.h
+[HashMap]: https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/wtf/hash_map.h
+[Deque]: https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/wtf/deque.h
+[String]: https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/wtf/text/wtf_string.h
+[AtomicString]: https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/wtf/text/atomic_string.h
+[StringBuilder]: https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/wtf/text/string_builder.h
+[CString]: https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/wtf/text/cstring.h
+[RefCounted]: https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/wtf/ref_counted.h
+[Allocator.h]: https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/wtf/allocator.h
+[Functional.h]: https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/wtf/functional.h
+[Threading.h]: https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/wtf/threading.h
+[ThreadingPrimitives.h]: https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/wtf/threading_primitives.h
+[Compiler.h]: https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/wtf/compiler.h
+[CPU.h]: https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/wtf/cpu.h
+[build_config.h]: https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/wtf/build_config.h
+[Noncopyable.h]: https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/wtf/noncopyable.h
+[StdLibExtras.h]: https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/wtf/std_lib_extras.h
+[Time.h]: https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/wtf/time.h
+[CryptographicallyRandomNumber.h]: https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/wtf/cryptographically_random_number.h
+[AutoReset.h]: https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/wtf/auto_reset.h
+[Optional.h]: https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/wtf/optional.h
+[1]: https://chromium.googlesource.com/chromium/src/+/e372c152fc6e57743ebc508fe17f6eb131b4ff8d
+[2]: https://chromium.googlesource.com/chromium/src/+/547a6ca360a56fbee3d5ea4a71ba18f91622455c
+[3]: https://chromium.googlesource.com/chromium/src/+/478890427ee03fd88e6f0f58ee8220512044bed9/third_party/WebKit/WebCore/kwq/KWQAssertions.h
+[4]:https://docs.google.com/document/d/1JK26H-1-cD9-s9QLvEfY55H2kgSxRFNPLfjs049Us5w/edit?usp=sharing
diff --git a/chromium/third_party/blink/renderer/platform/wtf/ScopedLogger.md b/chromium/third_party/blink/renderer/platform/wtf/ScopedLogger.md
new file mode 100644
index 00000000000..3593e16b5f4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/ScopedLogger.md
@@ -0,0 +1,102 @@
+# Debugging with ScopedLogger
+
+## Overview
+
+ScopedLogger is a logger that shows nested calls by indenting.
+
+For example, if you were debugging a layout issue you could add a ScopedLogger
+to the top of the `LayoutBlock::layout` function:
+
+```c++
+void LayoutBlock::layout()
+{
+ WTF_CREATE_SCOPED_LOGGER(logger, "layout %s", debugName().utf8().data());
+ ...
+```
+
+The arguments of the `WTF_CREATE_SCOPED_LOGGER` macro are the name of the
+object, followed by the first log message, followed by printf-style varargs.
+In the above example, the log message includes the debug name of the block that
+is currently being laid out.
+
+ScopedLogger wraps log messages in parentheses, with indentation proportional to
+the number of instances. This makes it easy to see the flow of control in the
+output, particularly when instrumenting recursive functions. Here is some of
+the output of the above example when laying out www.google.com:
+
+```
+( layout LayoutView #document
+ ( layout LayoutBlockFlow HTML
+ ( layout LayoutBlockFlow BODY id='gsr' class='hp vasq'
+ ( layout LayoutBlockFlow (relative positioned) DIV id='viewport' class='ctr-p'
+ ( layout LayoutBlockFlow DIV id='doc-info' )
+ ( layout LayoutBlockFlow DIV id='cst' )
+ ( layout LayoutBlockFlow (positioned) A )
+ ( layout LayoutBlockFlow (positioned) DIV id='searchform' class='jhp' )
+ )
+ )
+ )
+)
+```
+
+## Appending to a ScopedLogger
+
+Every ScopedLogger has an initial log message, which is often sufficient. But
+you can also write additional messages to an existing ScopedLogger with
+`WTF_APPEND_SCOPED_LOGGER`. For example:
+
+```c++
+ // further down in LayoutBlock::layout...
+
+ if (needsScrollAnchoring) {
+ WTF_APPEND_SCOPED_LOGGER(logger, "restoring scroll anchor");
+ getScrollableArea()->scrollAnchor().restore();
+ }
+```
+
+## Conditional ScopedLoggers
+
+It's often useful to create a ScopedLogger only if some condition is met.
+Unfortunately, the following doesn't work correctly:
+
+```c++
+void foo() {
+ if (condition) {
+ WTF_CREATE_SCOPED_LOGGER(logger, "foo, with condition");
+ // Oops: logger exits scope prematurely!
+ }
+ bar(); // any ScopedLogger in bar won't nest
+}
+```
+
+To guard a ScopedLogger construction with a condition without restricting its
+scope, use `WTF_CREATE_SCOPED_LOGGER_IF`:
+
+```c++
+void foo() {
+ WTF_CREATE_SCOPED_LOGGER_IF(logger, condition, "message");
+ bar();
+}
+```
+
+## Requirements
+
+The ScopedLogger class and associated macros are defined in
+[Assertions.h](Assertions.h), which most Blink source files already include
+indirectly. ScopedLogger can't be used outside of Blink code yet.
+
+The ScopedLogger macros work in debug builds by default. They are compiled out
+of release builds, unless your `GYP_DEFINES` or GN args file includes one of the
+following:
+
+* `dcheck_always_on`: enables assertions and ScopedLogger
+
+The macro names are cumbersome to type, but most editors can be configured to
+make this easier. For example, you can add the following to a Sublime Text key
+binding file to make Ctrl+Alt+L insert a ScopedLogger:
+
+```
+ { "keys": ["ctrl+alt+l"], "command": "insert",
+ "args": {"characters": "WTF_CREATE_SCOPED_LOGGER(logger, \"msg\");"}
+ }
+```
diff --git a/chromium/third_party/blink/renderer/platform/wtf/address_sanitizer.h b/chromium/third_party/blink/renderer/platform/wtf/address_sanitizer.h
new file mode 100644
index 00000000000..b8a7b02e0ed
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/address_sanitizer.h
@@ -0,0 +1,40 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_ADDRESS_SANITIZER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_ADDRESS_SANITIZER_H_
+// TODO(kojii): This file will need to be renamed, because it's no more
+// specific to AddressSanitizer.
+
+// TODO(sof): Add SyZyASan support?
+#if defined(ADDRESS_SANITIZER)
+#include <sanitizer/asan_interface.h>
+#define NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
+#else
+#define ASAN_POISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size))
+#define ASAN_UNPOISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size))
+#define NO_SANITIZE_ADDRESS
+#endif
+
+#if defined(LEAK_SANITIZER)
+#include <sanitizer/lsan_interface.h>
+#else
+#define __lsan_register_root_region(addr, size) ((void)(addr), (void)(size))
+#define __lsan_unregister_root_region(addr, size) ((void)(addr), (void)(size))
+#endif
+
+#if defined(MEMORY_SANITIZER)
+#include <sanitizer/msan_interface.h>
+#define NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory))
+#else
+#define NO_SANITIZE_MEMORY
+#endif
+
+#if defined(THREAD_SANITIZER)
+#define NO_SANITIZE_THREAD __attribute__((no_sanitize_thread))
+#else
+#define NO_SANITIZE_THREAD
+#endif
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_ADDRESS_SANITIZER_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/alignment.h b/chromium/third_party/blink/renderer/platform/wtf/alignment.h
new file mode 100644
index 00000000000..f4d782f4f77
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/alignment.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_ALIGNMENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_ALIGNMENT_H_
+
+#include <stdint.h>
+#include "build/build_config.h"
+
+namespace WTF {
+
+#if defined(COMPILER_GCC)
+#define WTF_ALIGN_OF(type) __alignof__(type)
+#define WTF_ALIGNED(variable_type, variable, n) \
+ variable_type variable __attribute__((__aligned__(n)))
+#elif defined(COMPILER_MSVC)
+#define WTF_ALIGN_OF(type) __alignof(type)
+#define WTF_ALIGNED(variable_type, variable, n) \
+ __declspec(align(n)) variable_type variable
+#else
+#error WTF_ALIGN macros need alignment control.
+#endif
+
+template <uintptr_t mask>
+inline bool IsAlignedTo(const void* pointer) {
+ return !(reinterpret_cast<uintptr_t>(pointer) & mask);
+}
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_ALIGNMENT_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/allocator.h b/chromium/third_party/blink/renderer/platform/wtf/allocator.h
new file mode 100644
index 00000000000..ff81a049cda
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/allocator.h
@@ -0,0 +1,155 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_ALLOCATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_ALLOCATOR_H_
+
+#include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/type_traits.h"
+
+namespace WTF {
+
+namespace internal {
+// A dummy class used in following macros.
+class __thisIsHereToForceASemicolonAfterThisMacro;
+}
+
+// Classes that contain references to garbage-collected objects but aren't
+// themselves garbaged allocated, have some extra macros available which
+// allows their use to be restricted to cases where the garbage collector
+// is able to discover their references. These macros will be useful for
+// non-garbage-collected objects to avoid unintended allocations.
+//
+// STACK_ALLOCATED(): Use if the object is only stack allocated.
+// Garbage-collected objects should be in Members but you do not need the
+// trace method as they are on the stack. (Down the line these might turn
+// in to raw pointers, but for now Members indicate that we have thought
+// about them and explicitly taken care of them.)
+//
+// DISALLOW_NEW(): Cannot be allocated with new operators but can be a
+// part of object. If it has Members you need a trace method and the containing
+// object needs to call that trace method.
+//
+// DISALLOW_NEW_EXCEPT_PLACEMENT_NEW(): Allows only placement new operator. This
+// disallows general allocation of this object but allows to put the object as a
+// value object in collections. If these have Members you need to have a trace
+// method. That trace method will be called automatically by the on-heap
+// collections.
+//
+#define DISALLOW_NEW() \
+ void* operator new(size_t) = delete; \
+ void* operator new(size_t, NotNullTag, void*) = delete; \
+ void* operator new(size_t, void*) = delete
+
+#define DISALLOW_NEW_EXCEPT_PLACEMENT_NEW() \
+ public: \
+ using IsAllowOnlyPlacementNew = int; \
+ void* operator new(size_t, NotNullTag, void* location) { return location; } \
+ void* operator new(size_t, void* location) { return location; } \
+ \
+ private: \
+ void* operator new(size_t) = delete; \
+ \
+ public: \
+ friend class ::WTF::internal::__thisIsHereToForceASemicolonAfterThisMacro
+
+#define STATIC_ONLY(Type) \
+ Type() = delete; \
+ Type(const Type&) = delete; \
+ Type& operator=(const Type&) = delete; \
+ void* operator new(size_t) = delete; \
+ void* operator new(size_t, NotNullTag, void*) = delete; \
+ void* operator new(size_t, void*) = delete
+
+#define IS_GARBAGE_COLLECTED_TYPE() \
+ public: \
+ using IsGarbageCollectedTypeMarker = int; \
+ \
+ private: \
+ friend class ::WTF::internal::__thisIsHereToForceASemicolonAfterThisMacro
+
+#if defined(__clang__)
+#define STACK_ALLOCATED() \
+ __attribute__((annotate("blink_stack_allocated"))) void* operator new( \
+ size_t) = delete; \
+ void* operator new(size_t, NotNullTag, void*) = delete; \
+ void* operator new(size_t, void*) = delete
+
+#else
+#define STACK_ALLOCATED() DISALLOW_NEW()
+#endif
+
+// Provides customizable overrides of fastMalloc/fastFree and operator
+// new/delete
+//
+// Provided functionality:
+// Macro: USING_FAST_MALLOC
+//
+// Example usage:
+// class Widget {
+// USING_FAST_MALLOC(Widget)
+// ...
+// };
+//
+// struct Data {
+// USING_FAST_MALLOC(Data)
+// public:
+// ...
+// };
+//
+
+#define USING_FAST_MALLOC_INTERNAL(type, typeName) \
+ public: \
+ void* operator new(size_t, void* p) { return p; } \
+ void* operator new[](size_t, void* p) { return p; } \
+ \
+ void* operator new(size_t size) { \
+ return ::WTF::Partitions::FastMalloc(size, typeName); \
+ } \
+ \
+ void operator delete(void* p) { ::WTF::Partitions::FastFree(p); } \
+ \
+ void* operator new[](size_t size) { \
+ return ::WTF::Partitions::FastMalloc(size, typeName); \
+ } \
+ \
+ void operator delete[](void* p) { ::WTF::Partitions::FastFree(p); } \
+ void* operator new(size_t, NotNullTag, void* location) { \
+ DCHECK(location); \
+ return location; \
+ } \
+ \
+ private: \
+ friend class ::WTF::internal::__thisIsHereToForceASemicolonAfterThisMacro
+
+// In official builds, do not include type info string literals to avoid
+// bloating the binary.
+#if defined(OFFICIAL_BUILD)
+#define WTF_HEAP_PROFILER_TYPE_NAME(T) nullptr
+#else
+#define WTF_HEAP_PROFILER_TYPE_NAME(T) ::WTF::GetStringWithTypeName<T>()
+#endif
+
+// Both of these macros enable fast malloc and provide type info to the heap
+// profiler. The regular macro does not provide type info in official builds,
+// to avoid bloating the binary with type name strings. The |WITH_TYPE_NAME|
+// variant provides type info unconditionally, so it should be used sparingly.
+// Furthermore, the |WITH_TYPE_NAME| variant does not work if |type| is a
+// template argument; |USING_FAST_MALLOC| does.
+#define USING_FAST_MALLOC(type) \
+ USING_FAST_MALLOC_INTERNAL(type, WTF_HEAP_PROFILER_TYPE_NAME(type))
+#define USING_FAST_MALLOC_WITH_TYPE_NAME(type) \
+ USING_FAST_MALLOC_INTERNAL(type, #type)
+
+} // namespace WTF
+
+// This version of placement new omits a 0 check.
+enum NotNullTag { NotNull };
+inline void* operator new(size_t, NotNullTag, void* location) {
+ DCHECK(location);
+ return location;
+}
+
+#endif /* WTF_Allocator_h */
diff --git a/chromium/third_party/blink/renderer/platform/wtf/allocator/Allocator.md b/chromium/third_party/blink/renderer/platform/wtf/allocator/Allocator.md
new file mode 100644
index 00000000000..0a978a43ccb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/allocator/Allocator.md
@@ -0,0 +1,43 @@
+# Design Of PartitionAlloc In Blink
+
+All objects in Blink are expected to be allocated with PartitionAlloc or Oilpan.
+
+Blink uses different PartitionAlloc partitions, for different kinds of objects:
+
+* LayoutObject partition: A partition to allocate `LayoutObject`s.
+The LayoutObject partition is a `SizeSpecificPartitionAllocator`. This means
+that no extra padding is needed to allocate a `LayoutObject` object. Different
+sizes of `LayoutObject`s are allocated in different buckets. Having a dedicated
+partition for `LayoutObject`s improves cache locality and thus performance.
+
+* Buffer partition: A partition to allocate objects that have a strong risk
+that the length and/or the contents are exploited by user scripts. Specifically,
+we allocate `Vector`s, `HashTable`s, and `String`s in the Buffer partition.
+
+* ArrayBuffer partition: A partition to allocate `ArrayBufferContents`s.
+
+* FastMalloc partition: A partition to allocate all other objects. Objects
+marked with `USING_FAST_MALLOC` are allocated on the FastMalloc partition.
+
+The Buffer partition and the FastMalloc partition have many buckets. They
+support any arbitrary size of allocations but padding may be added to align the
+allocation with the closest bucket size. The bucket sizes are chosen to keep the
+worst-case memory overhead less than 10%.
+
+## Security
+
+We put `LayoutObject`s into a dedicated partition because `LayoutObject`s are
+likely to be a source of use-after-free (UAF) vulnerabilities. Similarly, we put
+`String`s, `Vector`s, et c. into the Buffer partition, and
+`ArrayBufferContents`s into the ArrayBuffer partition, because malicious web
+contents are likely to exploit the length field and/or contents of these
+objects.
+
+## Performance
+
+PartitionAlloc doesn't acquire a lock when allocating on the LayoutObject
+partition, because it's guaranteed that `LayoutObject`s are allocated only by
+the main thread.
+
+PartitionAlloc acquires a lock when allocating on the Buffer, ArrayBuffer, and
+FastMalloc partitions.
diff --git a/chromium/third_party/blink/renderer/platform/wtf/allocator/partition_allocator.cc b/chromium/third_party/blink/renderer/platform/wtf/allocator/partition_allocator.cc
new file mode 100644
index 00000000000..bb4fd19f1c8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/allocator/partition_allocator.cc
@@ -0,0 +1,37 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/wtf/allocator/partition_allocator.h"
+
+#include "base/allocator/partition_allocator/partition_alloc.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
+
+namespace WTF {
+
+void* PartitionAllocator::AllocateBacking(size_t size, const char* type_name) {
+ return Partitions::BufferMalloc(size, type_name);
+}
+
+void PartitionAllocator::FreeVectorBacking(void* address) {
+ Partitions::BufferFree(address);
+}
+
+void PartitionAllocator::FreeHashTableBacking(void* address,
+ bool is_weak_table) {
+ Partitions::BufferFree(address);
+}
+
+template <>
+char* PartitionAllocator::AllocateVectorBacking<char>(size_t size) {
+ return reinterpret_cast<char*>(
+ AllocateBacking(size, "PartitionAllocator::allocateVectorBacking<char>"));
+}
+
+template <>
+char* PartitionAllocator::AllocateExpandedVectorBacking<char>(size_t size) {
+ return reinterpret_cast<char*>(AllocateBacking(
+ size, "PartitionAllocator::allocateExpandedVectorBacking<char>"));
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/allocator/partition_allocator.h b/chromium/third_party/blink/renderer/platform/wtf/allocator/partition_allocator.h
new file mode 100644
index 00000000000..d87e5c928e9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/allocator/partition_allocator.h
@@ -0,0 +1,152 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_ALLOCATOR_PARTITION_ALLOCATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_ALLOCATOR_PARTITION_ALLOCATOR_H_
+
+// This is the allocator that is used for allocations that are not on the
+// traced, garbage collected heap. It uses FastMalloc for collections,
+// but uses the partition allocator for the backing store of the collections.
+
+#include <string.h>
+#include "base/allocator/partition_allocator/partition_alloc.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/type_traits.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_export.h"
+
+namespace WTF {
+
+class PartitionAllocatorDummyVisitor {
+ DISALLOW_NEW();
+};
+
+class WTF_EXPORT PartitionAllocator {
+ public:
+ typedef PartitionAllocatorDummyVisitor Visitor;
+ static constexpr bool kIsGarbageCollected = false;
+
+ template <typename T>
+ static size_t MaxElementCountInBackingStore() {
+ return base::kGenericMaxDirectMapped / sizeof(T);
+ }
+
+ template <typename T>
+ static size_t QuantizedSize(size_t count) {
+ CHECK_LE(count, MaxElementCountInBackingStore<T>());
+ return WTF::Partitions::BufferPartition()->ActualSize(count * sizeof(T));
+ }
+ template <typename T>
+ static T* AllocateVectorBacking(size_t size) {
+ return reinterpret_cast<T*>(
+ AllocateBacking(size, WTF_HEAP_PROFILER_TYPE_NAME(T)));
+ }
+ template <typename T>
+ static T* AllocateExpandedVectorBacking(size_t size) {
+ return reinterpret_cast<T*>(
+ AllocateBacking(size, WTF_HEAP_PROFILER_TYPE_NAME(T)));
+ }
+ static void FreeVectorBacking(void* address);
+ static inline bool ExpandVectorBacking(void*, size_t) { return false; }
+ static inline bool ShrinkVectorBacking(void* address,
+ size_t quantized_current_size,
+ size_t quantized_shrunk_size) {
+ // Optimization: if we're downsizing inside the same allocator bucket,
+ // we can skip reallocation.
+ return quantized_current_size == quantized_shrunk_size;
+ }
+ template <typename T>
+ static T* AllocateInlineVectorBacking(size_t size) {
+ return AllocateVectorBacking<T>(size);
+ }
+ static inline void FreeInlineVectorBacking(void* address) {
+ FreeVectorBacking(address);
+ }
+ static inline bool ExpandInlineVectorBacking(void*, size_t) { return false; }
+ static inline bool ShrinkInlineVectorBacking(void* address,
+ size_t quantized_current_size,
+ size_t quantized_shrunk_size) {
+ return ShrinkVectorBacking(address, quantized_current_size,
+ quantized_shrunk_size);
+ }
+
+ template <typename T, typename HashTable>
+ static T* AllocateHashTableBacking(size_t size) {
+ return reinterpret_cast<T*>(
+ AllocateBacking(size, WTF_HEAP_PROFILER_TYPE_NAME(T)));
+ }
+ template <typename T, typename HashTable>
+ static T* AllocateZeroedHashTableBacking(size_t size) {
+ void* result = AllocateBacking(size, WTF_HEAP_PROFILER_TYPE_NAME(T));
+ memset(result, 0, size);
+ return reinterpret_cast<T*>(result);
+ }
+ static void FreeHashTableBacking(void* address, bool is_weak_table);
+
+ template <typename Return, typename Metadata>
+ static Return Malloc(size_t size, const char* type_name) {
+ return reinterpret_cast<Return>(
+ WTF::Partitions::FastMalloc(size, type_name));
+ }
+
+ static inline bool ExpandHashTableBacking(void*, size_t) { return false; }
+ static void Free(void* address) { WTF::Partitions::FastFree(address); }
+ template <typename T>
+ static void* NewArray(size_t bytes) {
+ return Malloc<void*, void>(bytes, WTF_HEAP_PROFILER_TYPE_NAME(T));
+ }
+ static void DeleteArray(void* ptr) {
+ Free(ptr); // Not the system free, the one from this class.
+ }
+
+ static void BackingWriteBarrier(void*) {}
+
+ static bool IsAllocationAllowed() { return true; }
+ static bool IsObjectResurrectionForbidden() { return false; }
+
+ static void EnterGCForbiddenScope() {}
+ static void LeaveGCForbiddenScope() {}
+
+ template <typename T, typename Traits>
+ static void NotifyNewObject(T* object) {}
+
+ template <typename T, typename Traits>
+ static void NotifyNewObjects(T* array, size_t len) {}
+
+ private:
+ static void* AllocateBacking(size_t, const char* type_name);
+};
+
+// Specializations for heap profiling, so type profiling of |char| is possible
+// even in official builds (because |char| makes up a large portion of the
+// heap.)
+template <>
+WTF_EXPORT char* PartitionAllocator::AllocateVectorBacking<char>(size_t);
+template <>
+WTF_EXPORT char* PartitionAllocator::AllocateExpandedVectorBacking<char>(
+ size_t);
+
+} // namespace WTF
+
+#define USE_ALLOCATOR(ClassName, Allocator) \
+ public: \
+ void* operator new(size_t size) { \
+ return Allocator::template Malloc<void*, ClassName>( \
+ size, WTF_HEAP_PROFILER_TYPE_NAME(ClassName)); \
+ } \
+ void operator delete(void* p) { Allocator::Free(p); } \
+ void* operator new[](size_t size) { \
+ return Allocator::template NewArray<ClassName>(size); \
+ } \
+ void operator delete[](void* p) { Allocator::DeleteArray(p); } \
+ void* operator new(size_t, NotNullTag, void* location) { \
+ DCHECK(location); \
+ return location; \
+ } \
+ void* operator new(size_t, void* location) { return location; } \
+ \
+ private: \
+ typedef int __thisIsHereToForceASemicolonAfterThisMacro
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_ALLOCATOR_PARTITION_ALLOCATOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/allocator/partitions.cc b/chromium/third_party/blink/renderer/platform/wtf/allocator/partitions.cc
new file mode 100644
index 00000000000..1d7bbd3a4c8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/allocator/partitions.cc
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
+
+#include "base/allocator/partition_allocator/oom.h"
+#include "base/allocator/partition_allocator/page_allocator.h"
+#include "base/debug/alias.h"
+#include "base/lazy_instance.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/partition_allocator.h"
+
+namespace WTF {
+
+const char* const Partitions::kAllocatedObjectPoolName =
+ "partition_alloc/allocated_objects";
+
+static base::LazyInstance<base::subtle::SpinLock>::Leaky initialization_lock_ =
+ LAZY_INSTANCE_INITIALIZER;
+bool Partitions::initialized_ = false;
+
+// These statics are inlined, so cannot be LazyInstances. We create
+// LazyInstances below, and then set the pointers correctly in Initialize().
+base::PartitionAllocatorGeneric* Partitions::fast_malloc_allocator_ = nullptr;
+base::PartitionAllocatorGeneric* Partitions::array_buffer_allocator_ = nullptr;
+base::PartitionAllocatorGeneric* Partitions::buffer_allocator_ = nullptr;
+base::SizeSpecificPartitionAllocator<1024>* Partitions::layout_allocator_ =
+ nullptr;
+
+static base::LazyInstance<base::PartitionAllocatorGeneric>::Leaky
+ lazy_fast_malloc = LAZY_INSTANCE_INITIALIZER;
+static base::LazyInstance<base::PartitionAllocatorGeneric>::Leaky
+ lazy_array_buffer = LAZY_INSTANCE_INITIALIZER;
+static base::LazyInstance<base::PartitionAllocatorGeneric>::Leaky lazy_buffer =
+ LAZY_INSTANCE_INITIALIZER;
+static base::LazyInstance<base::SizeSpecificPartitionAllocator<1024>>::Leaky
+ lazy_layout = LAZY_INSTANCE_INITIALIZER;
+
+Partitions::ReportPartitionAllocSizeFunction Partitions::report_size_function_ =
+ nullptr;
+
+void Partitions::Initialize(
+ ReportPartitionAllocSizeFunction report_size_function) {
+ base::subtle::SpinLock::Guard guard(initialization_lock_.Get());
+
+ if (!initialized_) {
+ fast_malloc_allocator_ = lazy_fast_malloc.Pointer();
+ array_buffer_allocator_ = lazy_array_buffer.Pointer();
+ buffer_allocator_ = lazy_buffer.Pointer();
+ layout_allocator_ = lazy_layout.Pointer();
+
+ base::PartitionAllocGlobalInit(&Partitions::HandleOutOfMemory);
+ fast_malloc_allocator_->init();
+ array_buffer_allocator_->init();
+ buffer_allocator_->init();
+ layout_allocator_->init();
+ report_size_function_ = report_size_function;
+ initialized_ = true;
+ }
+}
+
+void Partitions::DecommitFreeableMemory() {
+ CHECK(IsMainThread());
+ if (!initialized_)
+ return;
+
+ ArrayBufferPartition()->PurgeMemory(
+ base::PartitionPurgeDecommitEmptyPages |
+ base::PartitionPurgeDiscardUnusedSystemPages);
+ BufferPartition()->PurgeMemory(base::PartitionPurgeDecommitEmptyPages |
+ base::PartitionPurgeDiscardUnusedSystemPages);
+ FastMallocPartition()->PurgeMemory(
+ base::PartitionPurgeDecommitEmptyPages |
+ base::PartitionPurgeDiscardUnusedSystemPages);
+ LayoutPartition()->PurgeMemory(base::PartitionPurgeDecommitEmptyPages |
+ base::PartitionPurgeDiscardUnusedSystemPages);
+}
+
+void Partitions::ReportMemoryUsageHistogram() {
+ static size_t observed_max_size_in_mb = 0;
+
+ if (!report_size_function_)
+ return;
+ // We only report the memory in the main thread.
+ if (!IsMainThread())
+ return;
+ // +1 is for rounding up the sizeInMB.
+ size_t size_in_mb = Partitions::TotalSizeOfCommittedPages() / 1024 / 1024 + 1;
+ if (size_in_mb > observed_max_size_in_mb) {
+ report_size_function_(size_in_mb);
+ observed_max_size_in_mb = size_in_mb;
+ }
+}
+
+void Partitions::DumpMemoryStats(
+ bool is_light_dump,
+ base::PartitionStatsDumper* partition_stats_dumper) {
+ // Object model and rendering partitions are not thread safe and can be
+ // accessed only on the main thread.
+ DCHECK(IsMainThread());
+
+ DecommitFreeableMemory();
+ FastMallocPartition()->DumpStats("fast_malloc", is_light_dump,
+ partition_stats_dumper);
+ ArrayBufferPartition()->DumpStats("array_buffer", is_light_dump,
+ partition_stats_dumper);
+ BufferPartition()->DumpStats("buffer", is_light_dump, partition_stats_dumper);
+ LayoutPartition()->DumpStats("layout", is_light_dump, partition_stats_dumper);
+}
+
+namespace {
+
+class LightPartitionStatsDumperImpl : public WTF::PartitionStatsDumper {
+ public:
+ LightPartitionStatsDumperImpl() : total_active_bytes_(0) {}
+
+ void PartitionDumpTotals(
+ const char* partition_name,
+ const WTF::PartitionMemoryStats* memory_stats) override {
+ total_active_bytes_ += memory_stats->total_active_bytes;
+ }
+
+ void PartitionsDumpBucketStats(
+ const char* partition_name,
+ const WTF::PartitionBucketMemoryStats*) override {}
+
+ size_t TotalActiveBytes() const { return total_active_bytes_; }
+
+ private:
+ size_t total_active_bytes_;
+};
+
+} // namespace
+
+size_t Partitions::TotalActiveBytes() {
+ LightPartitionStatsDumperImpl dumper;
+ WTF::Partitions::DumpMemoryStats(true, &dumper);
+ return dumper.TotalActiveBytes();
+}
+
+static NEVER_INLINE void PartitionsOutOfMemoryUsing2G() {
+ size_t signature = 2UL * 1024 * 1024 * 1024;
+ base::debug::Alias(&signature);
+ OOM_CRASH();
+}
+
+static NEVER_INLINE void PartitionsOutOfMemoryUsing1G() {
+ size_t signature = 1UL * 1024 * 1024 * 1024;
+ base::debug::Alias(&signature);
+ OOM_CRASH();
+}
+
+static NEVER_INLINE void PartitionsOutOfMemoryUsing512M() {
+ size_t signature = 512 * 1024 * 1024;
+ base::debug::Alias(&signature);
+ OOM_CRASH();
+}
+
+static NEVER_INLINE void PartitionsOutOfMemoryUsing256M() {
+ size_t signature = 256 * 1024 * 1024;
+ base::debug::Alias(&signature);
+ OOM_CRASH();
+}
+
+static NEVER_INLINE void PartitionsOutOfMemoryUsing128M() {
+ size_t signature = 128 * 1024 * 1024;
+ base::debug::Alias(&signature);
+ OOM_CRASH();
+}
+
+static NEVER_INLINE void PartitionsOutOfMemoryUsing64M() {
+ size_t signature = 64 * 1024 * 1024;
+ base::debug::Alias(&signature);
+ OOM_CRASH();
+}
+
+static NEVER_INLINE void PartitionsOutOfMemoryUsing32M() {
+ size_t signature = 32 * 1024 * 1024;
+ base::debug::Alias(&signature);
+ OOM_CRASH();
+}
+
+static NEVER_INLINE void PartitionsOutOfMemoryUsing16M() {
+ size_t signature = 16 * 1024 * 1024;
+ base::debug::Alias(&signature);
+ OOM_CRASH();
+}
+
+static NEVER_INLINE void PartitionsOutOfMemoryUsingLessThan16M() {
+ size_t signature = 16 * 1024 * 1024 - 1;
+ base::debug::Alias(&signature);
+ DLOG(FATAL) << "ParitionAlloc: out of memory with < 16M usage (error:"
+ << GetAllocPageErrorCode() << ")";
+}
+
+void Partitions::HandleOutOfMemory() {
+ volatile size_t total_usage = TotalSizeOfCommittedPages();
+ uint32_t alloc_page_error_code = GetAllocPageErrorCode();
+ base::debug::Alias(&alloc_page_error_code);
+
+ if (total_usage >= 2UL * 1024 * 1024 * 1024)
+ PartitionsOutOfMemoryUsing2G();
+ if (total_usage >= 1UL * 1024 * 1024 * 1024)
+ PartitionsOutOfMemoryUsing1G();
+ if (total_usage >= 512 * 1024 * 1024)
+ PartitionsOutOfMemoryUsing512M();
+ if (total_usage >= 256 * 1024 * 1024)
+ PartitionsOutOfMemoryUsing256M();
+ if (total_usage >= 128 * 1024 * 1024)
+ PartitionsOutOfMemoryUsing128M();
+ if (total_usage >= 64 * 1024 * 1024)
+ PartitionsOutOfMemoryUsing64M();
+ if (total_usage >= 32 * 1024 * 1024)
+ PartitionsOutOfMemoryUsing32M();
+ if (total_usage >= 16 * 1024 * 1024)
+ PartitionsOutOfMemoryUsing16M();
+ PartitionsOutOfMemoryUsingLessThan16M();
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/allocator/partitions.h b/chromium/third_party/blink/renderer/platform/wtf/allocator/partitions.h
new file mode 100644
index 00000000000..5201b027d65
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/allocator/partitions.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_ALLOCATOR_PARTITIONS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_ALLOCATOR_PARTITIONS_H_
+
+#include <string.h>
+#include "base/allocator/partition_allocator/partition_alloc.h"
+#include "base/allocator/partition_allocator/spin_lock.h"
+#include "base/numerics/checked_math.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/wtf.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_export.h"
+
+namespace WTF {
+
+class WTF_EXPORT Partitions {
+ public:
+ typedef void (*ReportPartitionAllocSizeFunction)(size_t);
+
+ // Name of allocator used by tracing for marking sub-allocations while take
+ // memory snapshots.
+ static const char* const kAllocatedObjectPoolName;
+
+ static void Initialize(ReportPartitionAllocSizeFunction);
+ ALWAYS_INLINE static base::PartitionRootGeneric* ArrayBufferPartition() {
+ DCHECK(initialized_);
+ return array_buffer_allocator_->root();
+ }
+
+ ALWAYS_INLINE static base::PartitionRootGeneric* BufferPartition() {
+ DCHECK(initialized_);
+ return buffer_allocator_->root();
+ }
+
+ ALWAYS_INLINE static base::PartitionRootGeneric* FastMallocPartition() {
+ DCHECK(initialized_);
+ return fast_malloc_allocator_->root();
+ }
+
+ ALWAYS_INLINE static base::PartitionRoot* NodePartition() {
+ NOTREACHED();
+ return nullptr;
+ }
+
+ ALWAYS_INLINE static base::PartitionRoot* LayoutPartition() {
+ DCHECK(initialized_);
+ return layout_allocator_->root();
+ }
+
+ ALWAYS_INLINE static size_t ComputeAllocationSize(size_t count, size_t size) {
+ base::CheckedNumeric<size_t> total = count;
+ total *= size;
+ return total.ValueOrDie();
+ }
+
+ static size_t CurrentDOMMemoryUsage() {
+ NOTREACHED();
+ return 0;
+ }
+
+ static size_t TotalSizeOfCommittedPages() {
+ DCHECK(initialized_);
+ size_t total_size = 0;
+ total_size += fast_malloc_allocator_->root()->total_size_of_committed_pages;
+ total_size +=
+ array_buffer_allocator_->root()->total_size_of_committed_pages;
+ total_size += buffer_allocator_->root()->total_size_of_committed_pages;
+ total_size += layout_allocator_->root()->total_size_of_committed_pages;
+ return total_size;
+ }
+
+ static size_t TotalActiveBytes();
+
+ static void DecommitFreeableMemory();
+
+ static void ReportMemoryUsageHistogram();
+
+ static void DumpMemoryStats(bool is_light_dump, base::PartitionStatsDumper*);
+
+ ALWAYS_INLINE static void* BufferMalloc(size_t n, const char* type_name) {
+ return BufferPartition()->Alloc(n, type_name);
+ }
+ ALWAYS_INLINE static void* BufferRealloc(void* p,
+ size_t n,
+ const char* type_name) {
+ return BufferPartition()->Realloc(p, n, type_name);
+ }
+ ALWAYS_INLINE static void BufferFree(void* p) { BufferPartition()->Free(p); }
+ ALWAYS_INLINE static size_t BufferActualSize(size_t n) {
+ return BufferPartition()->ActualSize(n);
+ }
+ static void* FastMalloc(size_t n, const char* type_name) {
+ return Partitions::FastMallocPartition()->Alloc(n, type_name);
+ }
+ static void* FastZeroedMalloc(size_t n, const char* type_name) {
+ void* result = FastMalloc(n, type_name);
+ memset(result, 0, n);
+ return result;
+ }
+ static void* FastRealloc(void* p, size_t n, const char* type_name) {
+ return Partitions::FastMallocPartition()->Realloc(p, n, type_name);
+ }
+ static void FastFree(void* p) { Partitions::FastMallocPartition()->Free(p); }
+
+ static void HandleOutOfMemory();
+
+ private:
+ static bool initialized_;
+
+ // See Allocator.md for a description of these partitions.
+ static base::PartitionAllocatorGeneric* fast_malloc_allocator_;
+ static base::PartitionAllocatorGeneric* array_buffer_allocator_;
+ static base::PartitionAllocatorGeneric* buffer_allocator_;
+ static base::SizeSpecificPartitionAllocator<1024>* layout_allocator_;
+ static ReportPartitionAllocSizeFunction report_size_function_;
+};
+
+using base::kGenericMaxDirectMapped;
+using base::kPageAllocationGranularity;
+using base::kPageAllocationGranularityBaseMask;
+using base::kPageAllocationGranularityOffsetMask;
+using base::kSystemPageSize;
+
+using base::AllocPages;
+using base::DecommitSystemPages;
+using base::DiscardSystemPages;
+using base::PartitionFree;
+using base::FreePages;
+using base::GetAllocPageErrorCode;
+using base::RecommitSystemPages;
+using base::RoundDownToSystemPage;
+using base::RoundUpToSystemPage;
+using base::SetSystemPagesAccess;
+
+using base::PageInaccessible;
+using base::PageReadWrite;
+using base::PartitionStatsDumper;
+using base::PartitionMemoryStats;
+using base::PartitionBucketMemoryStats;
+using base::PartitionAllocHooks;
+
+using CheckedSizeT = base::CheckedNumeric<size_t>;
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_ALLOCATOR_PARTITIONS_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/ascii_ctype.cc b/chromium/third_party/blink/renderer/platform/wtf/ascii_ctype.cc
new file mode 100644
index 00000000000..d41dd964062
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/ascii_ctype.cc
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2007, 2008, 2009, 2011 Apple 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/wtf/ascii_ctype.h"
+
+namespace WTF {
+
+extern const LChar kASCIICaseFoldTable[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
+ 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
+ 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
+ 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73,
+ 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b,
+ 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
+ 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b,
+ 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3,
+ 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb,
+ 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3,
+ 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb,
+ 0xfc, 0xfd, 0xfe, 0xff};
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/ascii_ctype.h b/chromium/third_party/blink/renderer/platform/wtf/ascii_ctype.h
new file mode 100644
index 00000000000..0d748576522
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/ascii_ctype.h
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2007, 2008, 2009, 2011 Apple 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_ASCII_CTYPE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_ASCII_CTYPE_H_
+
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
+
+// The behavior of many of the functions in the <ctype.h> header is dependent
+// on the current locale. But in the WebKit project, all uses of those functions
+// are in code processing something that's not locale-specific. These
+// equivalents for some of the <ctype.h> functions are named more explicitly,
+// not dependent on the C library locale, and we should also optimize them as
+// needed.
+
+// All functions return false or leave the character unchanged if passed a
+// character that is outside the range 0-7F. So they can be used on Unicode
+// strings or characters if the intent is to do processing only if the
+// character is ASCII.
+
+namespace WTF {
+
+template <typename CharType>
+inline bool IsASCII(CharType c) {
+ return !(c & ~0x7F);
+}
+
+template <typename CharType>
+inline bool IsASCIIAlpha(CharType c) {
+ return (c | 0x20) >= 'a' && (c | 0x20) <= 'z';
+}
+
+template <typename CharType>
+inline bool IsASCIIDigit(CharType c) {
+ return c >= '0' && c <= '9';
+}
+
+template <typename CharType>
+inline bool IsASCIIAlphanumeric(CharType c) {
+ return IsASCIIDigit(c) || IsASCIIAlpha(c);
+}
+
+template <typename CharType>
+inline bool IsASCIIHexDigit(CharType c) {
+ return IsASCIIDigit(c) || ((c | 0x20) >= 'a' && (c | 0x20) <= 'f');
+}
+
+template <typename CharType>
+inline bool IsASCIILower(CharType c) {
+ return c >= 'a' && c <= 'z';
+}
+
+template <typename CharType>
+inline bool IsASCIIOctalDigit(CharType c) {
+ return (c >= '0') & (c <= '7');
+}
+
+template <typename CharType>
+inline bool IsASCIIPrintable(CharType c) {
+ return c >= ' ' && c <= '~';
+}
+
+/*
+ Statistics from a run of Apple's page load test for callers of IsASCIISpace:
+
+ character count
+ --------- -----
+ non-spaces 689383
+ 20 space 294720
+ 0A \n 89059
+ 09 \t 28320
+ 0D \r 0
+ 0C \f 0
+ 0B \v 0
+ */
+template <typename CharType>
+inline bool IsASCIISpace(CharType c) {
+ return c <= ' ' && (c == ' ' || (c <= 0xD && c >= 0x9));
+}
+
+template <typename CharType>
+inline bool IsASCIIUpper(CharType c) {
+ return c >= 'A' && c <= 'Z';
+}
+
+WTF_EXPORT extern const LChar kASCIICaseFoldTable[256];
+
+template <typename CharType>
+inline CharType ToASCIILower(CharType c) {
+ return c | ((c >= 'A' && c <= 'Z') << 5);
+}
+
+inline LChar ToASCIILower(LChar c) {
+ return kASCIICaseFoldTable[c];
+}
+
+inline char ToASCIILower(char c) {
+ return static_cast<char>(kASCIICaseFoldTable[static_cast<LChar>(c)]);
+}
+
+template <typename CharType>
+inline CharType ToASCIIUpper(CharType c) {
+ return c & ~((c >= 'a' && c <= 'z') << 5);
+}
+
+template <typename CharType>
+inline int ToASCIIHexValue(CharType c) {
+ DCHECK(IsASCIIHexDigit(c));
+ return c < 'A' ? c - '0' : (c - 'A' + 10) & 0xF;
+}
+
+template <typename CharType>
+inline int ToASCIIHexValue(CharType upper_value, CharType lower_value) {
+ DCHECK(IsASCIIHexDigit(upper_value));
+ DCHECK(IsASCIIHexDigit(lower_value));
+ return ((ToASCIIHexValue(upper_value) << 4) & 0xF0) |
+ ToASCIIHexValue(lower_value);
+}
+
+inline char LowerNibbleToASCIIHexDigit(char c) {
+ char nibble = c & 0xF;
+ return nibble < 10 ? '0' + nibble : 'A' + nibble - 10;
+}
+
+inline char UpperNibbleToASCIIHexDigit(char c) {
+ char nibble = (c >> 4) & 0xF;
+ return nibble < 10 ? '0' + nibble : 'A' + nibble - 10;
+}
+
+template <typename CharType>
+inline bool IsASCIIAlphaCaselessEqual(CharType css_character, char character) {
+ // This function compares a (preferrably) constant ASCII
+ // lowercase letter to any input character.
+ DCHECK_GE(character, 'a');
+ DCHECK_LE(character, 'z');
+ return LIKELY((css_character | 0x20) == character);
+}
+
+} // namespace WTF
+
+using WTF::IsASCII;
+using WTF::IsASCIIAlpha;
+using WTF::IsASCIIAlphanumeric;
+using WTF::IsASCIIDigit;
+using WTF::IsASCIIHexDigit;
+using WTF::IsASCIILower;
+using WTF::IsASCIIOctalDigit;
+using WTF::IsASCIIPrintable;
+using WTF::IsASCIISpace;
+using WTF::IsASCIIUpper;
+using WTF::ToASCIIHexValue;
+using WTF::ToASCIILower;
+using WTF::ToASCIIUpper;
+using WTF::LowerNibbleToASCIIHexDigit;
+using WTF::UpperNibbleToASCIIHexDigit;
+using WTF::IsASCIIAlphaCaselessEqual;
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/wtf/ascii_ctype_test.cc b/chromium/third_party/blink/renderer/platform/wtf/ascii_ctype_test.cc
new file mode 100644
index 00000000000..32146f5f677
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/ascii_ctype_test.cc
@@ -0,0 +1,18 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/wtf/ascii_ctype.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace WTF {
+
+TEST(ASCIICTypeTest, ASCIICaseFoldTable) {
+ LChar symbol = 0xff;
+ while (symbol--) {
+ EXPECT_EQ(ToASCIILower<LChar>(symbol), kASCIICaseFoldTable[symbol]);
+ }
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/assertions.cc b/chromium/third_party/blink/renderer/platform/wtf/assertions.cc
new file mode 100644
index 00000000000..c7ecc7ffb97
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/assertions.cc
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2003, 2006, 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2007-2009 Torch Mobile, Inc.
+ * Copyright (C) 2011 University of Szeged. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 vprintf_stderr_common function triggers this error in the Mac build.
+// Feel free to remove this pragma if this file builds on Mac.
+// According to
+// http://gcc.gnu.org/onlinedocs/gcc-4.2.1/gcc/Diagnostic-Pragmas.html#Diagnostic-Pragmas
+// we need to place this directive before any data or functions are defined.
+#pragma GCC diagnostic ignored "-Wmissing-format-attribute"
+
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+#include "build/build_config.h"
+
+#if defined(OS_MACOSX)
+#include <asl.h>
+#endif // defined(OS_MACOSX)
+
+#if defined(COMPILER_MSVC)
+#include <crtdbg.h>
+#endif
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+#if defined(OS_MACOSX) || (defined(OS_LINUX) && !defined(__UCLIBC__))
+#include <cxxabi.h>
+#include <dlfcn.h>
+#include <execinfo.h>
+#endif
+
+#if defined(OS_ANDROID)
+#include <android/log.h>
+#endif
+
+PRINTF_FORMAT(1, 0)
+void vprintf_stderr_common(const char* format, va_list args) {
+#if defined(OS_MACOSX)
+ va_list copyOfArgs;
+ va_copy(copyOfArgs, args);
+ asl_vlog(0, 0, ASL_LEVEL_NOTICE, format, copyOfArgs);
+ va_end(copyOfArgs);
+#elif defined(OS_ANDROID)
+ __android_log_vprint(ANDROID_LOG_WARN, "WebKit", format, args);
+#elif defined(OS_WIN)
+ if (IsDebuggerPresent()) {
+ size_t size = 1024;
+
+ do {
+ char* buffer = (char*)malloc(size);
+ if (!buffer)
+ break;
+
+ if (_vsnprintf(buffer, size, format, args) != -1) {
+ OutputDebugStringA(buffer);
+ free(buffer);
+ break;
+ }
+
+ free(buffer);
+ size *= 2;
+ } while (size > 1024);
+ }
+#endif
+ vfprintf(stderr, format, args);
+}
+
+#if defined(COMPILER_GCC)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+#endif
+
+#if defined(COMPILER_GCC)
+#pragma GCC diagnostic pop
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/wtf/assertions.h b/chromium/third_party/blink/renderer/platform/wtf/assertions.h
new file mode 100644
index 00000000000..41ace57cd6c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/assertions.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2003, 2006, 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2013 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_ASSERTIONS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_ASSERTIONS_H_
+
+#include <stdarg.h>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_export.h"
+
+// New code shouldn't use this function. This function will be deprecated.
+void vprintf_stderr_common(const char* format, va_list args);
+
+#define DCHECK_AT(assertion, file, line) \
+ LAZY_STREAM(logging::LogMessage(file, line, #assertion).stream(), \
+ DCHECK_IS_ON() ? !(assertion) : false)
+
+// Users must test "#if ENABLE_SECURITY_ASSERT", which helps ensure that code
+// testing this macro has included this header.
+#if defined(ADDRESS_SANITIZER) || DCHECK_IS_ON()
+#define ENABLE_SECURITY_ASSERT 1
+#else
+#define ENABLE_SECURITY_ASSERT 0
+#endif
+
+// SECURITY_DCHECK and SECURITY_CHECK
+// Use in places where failure of the assertion indicates a possible security
+// vulnerability. Classes of these vulnerabilities include bad casts, out of
+// bounds accesses, use-after-frees, etc. Please be sure to file bugs for these
+// failures using the security template:
+// https://bugs.chromium.org/p/chromium/issues/entry?template=Security%20Bug
+#if ENABLE_SECURITY_ASSERT
+#define SECURITY_DCHECK(condition) \
+ LOG_IF(DCHECK, !(condition)) << "Security DCHECK failed: " #condition ". "
+// A SECURITY_CHECK failure is actually not vulnerable.
+#define SECURITY_CHECK(condition) \
+ LOG_IF(FATAL, !(condition)) << "Security CHECK failed: " #condition ". "
+#else
+#define SECURITY_DCHECK(condition) ((void)0)
+#define SECURITY_CHECK(condition) CHECK(condition)
+#endif
+
+// DEFINE_COMPARISON_OPERATORS_WITH_REFERENCES
+// Allow equality comparisons of Objects by reference or pointer,
+// interchangeably. This can be only used on types whose equality makes no
+// other sense than pointer equality.
+#define DEFINE_COMPARISON_OPERATORS_WITH_REFERENCES(Type) \
+ inline bool operator==(const Type& a, const Type& b) { return &a == &b; } \
+ inline bool operator==(const Type& a, const Type* b) { return &a == b; } \
+ inline bool operator==(const Type* a, const Type& b) { return a == &b; } \
+ inline bool operator!=(const Type& a, const Type& b) { return !(a == b); } \
+ inline bool operator!=(const Type& a, const Type* b) { return !(a == b); } \
+ inline bool operator!=(const Type* a, const Type& b) { return !(a == b); }
+
+// DEFINE_TYPE_CASTS
+//
+// ToType() functions are static_cast<> wrappers with SECURITY_DCHECK. It's
+// helpful to find bad casts.
+//
+// ToTypeOrNull() functions are similar to dynamic_cast<>. They return
+// type-casted values if the specified predicate is true, and return
+// nullptr otherwise.
+//
+// ToTypeOrDie() has a runtime type check, and it crashes if the specified
+// object is not an instance of the destination type. It is used if
+// * it's hard to prevent from passing unexpected objects,
+// * proceeding with the following code doesn't make sense, and
+// * cost of runtime type check is acceptable.
+#define DEFINE_TYPE_CASTS(Type, ArgType, argument, pointerPredicate, \
+ referencePredicate) \
+ inline Type* To##Type(ArgType* argument) { \
+ SECURITY_DCHECK(!argument || (pointerPredicate)); \
+ return static_cast<Type*>(argument); \
+ } \
+ inline const Type* To##Type(const ArgType* argument) { \
+ SECURITY_DCHECK(!argument || (pointerPredicate)); \
+ return static_cast<const Type*>(argument); \
+ } \
+ inline Type& To##Type(ArgType& argument) { \
+ SECURITY_DCHECK(referencePredicate); \
+ return static_cast<Type&>(argument); \
+ } \
+ inline const Type& To##Type(const ArgType& argument) { \
+ SECURITY_DCHECK(referencePredicate); \
+ return static_cast<const Type&>(argument); \
+ } \
+ void To##Type(const Type*); \
+ void To##Type(const Type&); \
+ \
+ inline Type* To##Type##OrNull(ArgType* argument) { \
+ if (!(argument) || !(pointerPredicate)) \
+ return nullptr; \
+ return static_cast<Type*>(argument); \
+ } \
+ inline const Type* To##Type##OrNull(const ArgType* argument) { \
+ if (!(argument) || !(pointerPredicate)) \
+ return nullptr; \
+ return static_cast<const Type*>(argument); \
+ } \
+ inline Type* To##Type##OrNull(ArgType& argument) { \
+ if (!(referencePredicate)) \
+ return nullptr; \
+ return static_cast<Type*>(&argument); \
+ } \
+ inline const Type* To##Type##OrNull(const ArgType& argument) { \
+ if (!(referencePredicate)) \
+ return nullptr; \
+ return static_cast<const Type*>(&argument); \
+ } \
+ void To##Type##OrNull(const Type*); \
+ void To##Type##OrNull(const Type&); \
+ \
+ inline Type* To##Type##OrDie(ArgType* argument) { \
+ CHECK(!argument || (pointerPredicate)); \
+ return static_cast<Type*>(argument); \
+ } \
+ inline const Type* To##Type##OrDie(const ArgType* argument) { \
+ CHECK(!argument || (pointerPredicate)); \
+ return static_cast<const Type*>(argument); \
+ } \
+ inline Type& To##Type##OrDie(ArgType& argument) { \
+ CHECK(referencePredicate); \
+ return static_cast<Type&>(argument); \
+ } \
+ inline const Type& To##Type##OrDie(const ArgType& argument) { \
+ CHECK(referencePredicate); \
+ return static_cast<const Type&>(argument); \
+ } \
+ void To##Type##OrDie(const Type*); \
+ void To##Type##OrDie(const Type&)
+
+// Check at compile time that related enums stay in sync.
+#define STATIC_ASSERT_ENUM(a, b) \
+ static_assert(static_cast<int>(a) == static_cast<int>(b), \
+ "mismatching enum: " #a)
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_ASSERTIONS_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/assertions_test.cc b/chromium/third_party/blink/renderer/platform/wtf/assertions_test.cc
new file mode 100644
index 00000000000..c242681558f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/assertions_test.cc
@@ -0,0 +1,38 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+
+namespace WTF {
+
+TEST(AssertionsTest, Assertions) {
+ DCHECK(true);
+#if DCHECK_IS_ON()
+ EXPECT_DEATH_IF_SUPPORTED(DCHECK(false), "");
+ EXPECT_DEATH_IF_SUPPORTED(NOTREACHED(), "");
+ EXPECT_DEATH_IF_SUPPORTED(DCHECK_AT(false, __FILE__, __LINE__), "");
+#else
+ DCHECK(false);
+ NOTREACHED();
+ DCHECK_AT(false, __FILE__, __LINE__);
+#endif
+
+ CHECK(true);
+ EXPECT_DEATH_IF_SUPPORTED(CHECK(false), "");
+
+ SECURITY_DCHECK(true);
+#if ENABLE_SECURITY_ASSERT
+ EXPECT_DEATH_IF_SUPPORTED(SECURITY_DCHECK(false), "");
+#else
+ SECURITY_DCHECK(false);
+#endif
+
+ SECURITY_CHECK(true);
+ EXPECT_DEATH_IF_SUPPORTED(SECURITY_CHECK(false), "");
+};
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/atomics.h b/chromium/third_party/blink/renderer/platform/wtf/atomics.h
new file mode 100644
index 00000000000..bc0f0794035
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/atomics.h
@@ -0,0 +1,402 @@
+/*
+ * Copyright (C) 2007, 2008, 2010, 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com)
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_ATOMICS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_ATOMICS_H_
+
+#include <stdint.h>
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/wtf/address_sanitizer.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/cpu.h"
+
+#if defined(COMPILER_MSVC)
+#include <windows.h>
+#endif
+
+#if defined(THREAD_SANITIZER)
+#include <sanitizer/tsan_interface_atomic.h>
+#endif
+
+#if defined(ADDRESS_SANITIZER)
+#include <sanitizer/asan_interface.h>
+#endif
+
+namespace WTF {
+
+#if defined(COMPILER_MSVC)
+
+// atomicAdd returns the result of the addition.
+ALWAYS_INLINE int AtomicAdd(int volatile* addend, int increment) {
+ return InterlockedExchangeAdd(reinterpret_cast<long volatile*>(addend),
+ static_cast<long>(increment)) +
+ increment;
+}
+ALWAYS_INLINE unsigned AtomicAdd(unsigned volatile* addend,
+ unsigned increment) {
+ return InterlockedExchangeAdd(reinterpret_cast<long volatile*>(addend),
+ static_cast<long>(increment)) +
+ increment;
+}
+#if defined(_WIN64)
+ALWAYS_INLINE unsigned long long AtomicAdd(unsigned long long volatile* addend,
+ unsigned long long increment) {
+ return InterlockedExchangeAdd64(reinterpret_cast<long long volatile*>(addend),
+ static_cast<long long>(increment)) +
+ increment;
+}
+#endif
+
+// atomicSubtract returns the result of the subtraction.
+ALWAYS_INLINE int AtomicSubtract(int volatile* addend, int decrement) {
+ return InterlockedExchangeAdd(reinterpret_cast<long volatile*>(addend),
+ static_cast<long>(-decrement)) -
+ decrement;
+}
+ALWAYS_INLINE unsigned AtomicSubtract(unsigned volatile* addend,
+ unsigned decrement) {
+ return InterlockedExchangeAdd(reinterpret_cast<long volatile*>(addend),
+ -static_cast<long>(decrement)) -
+ decrement;
+}
+#if defined(_WIN64)
+ALWAYS_INLINE unsigned long long AtomicSubtract(
+ unsigned long long volatile* addend,
+ unsigned long long decrement) {
+ return InterlockedExchangeAdd64(reinterpret_cast<long long volatile*>(addend),
+ -static_cast<long long>(decrement)) -
+ decrement;
+}
+#endif
+
+ALWAYS_INLINE int AtomicIncrement(int volatile* addend) {
+ return InterlockedIncrement(reinterpret_cast<long volatile*>(addend));
+}
+ALWAYS_INLINE int AtomicDecrement(int volatile* addend) {
+ return InterlockedDecrement(reinterpret_cast<long volatile*>(addend));
+}
+
+ALWAYS_INLINE int64_t AtomicIncrement(int64_t volatile* addend) {
+ return InterlockedIncrement64(reinterpret_cast<long long volatile*>(addend));
+}
+ALWAYS_INLINE int64_t AtomicDecrement(int64_t volatile* addend) {
+ return InterlockedDecrement64(reinterpret_cast<long long volatile*>(addend));
+}
+
+ALWAYS_INLINE int AtomicTestAndSetToOne(int volatile* ptr) {
+ int ret = InterlockedExchange(reinterpret_cast<long volatile*>(ptr), 1);
+ DCHECK(!ret || ret == 1);
+ return ret;
+}
+
+ALWAYS_INLINE void AtomicSetOneToZero(int volatile* ptr) {
+ DCHECK_EQ(*ptr, 1);
+ InterlockedExchange(reinterpret_cast<long volatile*>(ptr), 0);
+}
+
+#else
+
+// atomicAdd returns the result of the addition.
+ALWAYS_INLINE int AtomicAdd(int volatile* addend, int increment) {
+ return __sync_add_and_fetch(addend, increment);
+}
+ALWAYS_INLINE unsigned AtomicAdd(unsigned volatile* addend,
+ unsigned increment) {
+ return __sync_add_and_fetch(addend, increment);
+}
+ALWAYS_INLINE unsigned long AtomicAdd(unsigned long volatile* addend,
+ unsigned long increment) {
+ return __sync_add_and_fetch(addend, increment);
+}
+// atomicSubtract returns the result of the subtraction.
+ALWAYS_INLINE int AtomicSubtract(int volatile* addend, int decrement) {
+ return __sync_sub_and_fetch(addend, decrement);
+}
+ALWAYS_INLINE unsigned AtomicSubtract(unsigned volatile* addend,
+ unsigned decrement) {
+ return __sync_sub_and_fetch(addend, decrement);
+}
+ALWAYS_INLINE unsigned long AtomicSubtract(unsigned long volatile* addend,
+ unsigned long decrement) {
+ return __sync_sub_and_fetch(addend, decrement);
+}
+
+ALWAYS_INLINE int AtomicIncrement(int volatile* addend) {
+ return AtomicAdd(addend, 1);
+}
+ALWAYS_INLINE int AtomicDecrement(int volatile* addend) {
+ return AtomicSubtract(addend, 1);
+}
+
+ALWAYS_INLINE int64_t AtomicIncrement(int64_t volatile* addend) {
+ return __sync_add_and_fetch(addend, 1);
+}
+ALWAYS_INLINE int64_t AtomicDecrement(int64_t volatile* addend) {
+ return __sync_sub_and_fetch(addend, 1);
+}
+
+ALWAYS_INLINE int AtomicTestAndSetToOne(int volatile* ptr) {
+ int ret = __sync_lock_test_and_set(ptr, 1);
+ DCHECK(!ret || ret == 1);
+ return ret;
+}
+
+ALWAYS_INLINE void AtomicSetOneToZero(int volatile* ptr) {
+ DCHECK_EQ(*ptr, 1);
+ __sync_lock_release(ptr);
+}
+#endif
+
+#if defined(THREAD_SANITIZER)
+// The definitions below assume an LP64 data model. This is fine because
+// TSan is only supported on x86_64 Linux.
+#if defined(ARCH_CPU_64_BITS) && defined(OS_LINUX)
+ALWAYS_INLINE void ReleaseStore(volatile int* ptr, int value) {
+ __tsan_atomic32_store(ptr, value, __tsan_memory_order_release);
+}
+ALWAYS_INLINE void ReleaseStore(volatile unsigned* ptr, unsigned value) {
+ __tsan_atomic32_store(reinterpret_cast<volatile int*>(ptr),
+ static_cast<int>(value), __tsan_memory_order_release);
+}
+ALWAYS_INLINE void ReleaseStore(volatile long* ptr, long value) {
+ __tsan_atomic64_store(reinterpret_cast<volatile __tsan_atomic64*>(ptr),
+ static_cast<__tsan_atomic64>(value),
+ __tsan_memory_order_release);
+}
+ALWAYS_INLINE void ReleaseStore(volatile unsigned long* ptr,
+ unsigned long value) {
+ __tsan_atomic64_store(reinterpret_cast<volatile __tsan_atomic64*>(ptr),
+ static_cast<__tsan_atomic64>(value),
+ __tsan_memory_order_release);
+}
+ALWAYS_INLINE void ReleaseStore(volatile unsigned long long* ptr,
+ unsigned long long value) {
+ __tsan_atomic64_store(reinterpret_cast<volatile __tsan_atomic64*>(ptr),
+ static_cast<__tsan_atomic64>(value),
+ __tsan_memory_order_release);
+}
+ALWAYS_INLINE void ReleaseStore(void* volatile* ptr, void* value) {
+ __tsan_atomic64_store(reinterpret_cast<volatile __tsan_atomic64*>(ptr),
+ reinterpret_cast<__tsan_atomic64>(value),
+ __tsan_memory_order_release);
+}
+ALWAYS_INLINE int AcquireLoad(volatile const int* ptr) {
+ return __tsan_atomic32_load(ptr, __tsan_memory_order_acquire);
+}
+ALWAYS_INLINE unsigned AcquireLoad(volatile const unsigned* ptr) {
+ return static_cast<unsigned>(__tsan_atomic32_load(
+ reinterpret_cast<volatile const int*>(ptr), __tsan_memory_order_acquire));
+}
+ALWAYS_INLINE long AcquireLoad(volatile const long* ptr) {
+ return static_cast<long>(__tsan_atomic64_load(
+ reinterpret_cast<volatile const __tsan_atomic64*>(ptr),
+ __tsan_memory_order_acquire));
+}
+ALWAYS_INLINE unsigned long AcquireLoad(volatile const unsigned long* ptr) {
+ return static_cast<unsigned long>(__tsan_atomic64_load(
+ reinterpret_cast<volatile const __tsan_atomic64*>(ptr),
+ __tsan_memory_order_acquire));
+}
+ALWAYS_INLINE void* AcquireLoad(void* volatile const* ptr) {
+ return reinterpret_cast<void*>(__tsan_atomic64_load(
+ reinterpret_cast<volatile const __tsan_atomic64*>(ptr),
+ __tsan_memory_order_acquire));
+}
+
+// Do not use NoBarrierStore/NoBarrierLoad for synchronization.
+ALWAYS_INLINE void NoBarrierStore(volatile float* ptr, float value) {
+ static_assert(sizeof(int) == sizeof(float),
+ "int and float are different sizes");
+ union {
+ int ivalue;
+ float fvalue;
+ } u;
+ u.fvalue = value;
+ __tsan_atomic32_store(reinterpret_cast<volatile __tsan_atomic32*>(ptr),
+ u.ivalue, __tsan_memory_order_relaxed);
+}
+
+ALWAYS_INLINE float NoBarrierLoad(volatile const float* ptr) {
+ static_assert(sizeof(int) == sizeof(float),
+ "int and float are different sizes");
+ union {
+ int ivalue;
+ float fvalue;
+ } u;
+ u.ivalue = __tsan_atomic32_load(reinterpret_cast<volatile const int*>(ptr),
+ __tsan_memory_order_relaxed);
+ return u.fvalue;
+}
+#endif
+
+#else // defined(THREAD_SANITIZER)
+
+#if defined(ARCH_CPU_X86_FAMILY)
+// Only compiler barrier is needed.
+#if defined(COMPILER_MSVC)
+// Starting from Visual Studio 2005 compiler guarantees acquire and release
+// semantics for operations on volatile variables. See MSDN entry for
+// MemoryBarrier macro.
+#define MEMORY_BARRIER()
+#else
+#define MEMORY_BARRIER() __asm__ __volatile__("" : : : "memory")
+#endif
+#else
+// Fallback to the compiler intrinsic on all other platforms.
+#define MEMORY_BARRIER() __sync_synchronize()
+#endif
+
+ALWAYS_INLINE void ReleaseStore(volatile int* ptr, int value) {
+ MEMORY_BARRIER();
+ *ptr = value;
+}
+ALWAYS_INLINE void ReleaseStore(volatile unsigned* ptr, unsigned value) {
+ MEMORY_BARRIER();
+ *ptr = value;
+}
+ALWAYS_INLINE void ReleaseStore(volatile long* ptr, long value) {
+ MEMORY_BARRIER();
+ *ptr = value;
+}
+ALWAYS_INLINE void ReleaseStore(volatile unsigned long* ptr,
+ unsigned long value) {
+ MEMORY_BARRIER();
+ *ptr = value;
+}
+#if defined(ARCH_CPU_64_BITS)
+ALWAYS_INLINE void ReleaseStore(volatile unsigned long long* ptr,
+ unsigned long long value) {
+ MEMORY_BARRIER();
+ *ptr = value;
+}
+#endif
+ALWAYS_INLINE void ReleaseStore(void* volatile* ptr, void* value) {
+ MEMORY_BARRIER();
+ *ptr = value;
+}
+
+ALWAYS_INLINE int AcquireLoad(volatile const int* ptr) {
+ int value = *ptr;
+ MEMORY_BARRIER();
+ return value;
+}
+ALWAYS_INLINE unsigned AcquireLoad(volatile const unsigned* ptr) {
+ unsigned value = *ptr;
+ MEMORY_BARRIER();
+ return value;
+}
+ALWAYS_INLINE long AcquireLoad(volatile const long* ptr) {
+ long value = *ptr;
+ MEMORY_BARRIER();
+ return value;
+}
+ALWAYS_INLINE unsigned long AcquireLoad(volatile const unsigned long* ptr) {
+ unsigned long value = *ptr;
+ MEMORY_BARRIER();
+ return value;
+}
+#if defined(ARCH_CPU_64_BITS)
+ALWAYS_INLINE unsigned long long AcquireLoad(
+ volatile const unsigned long long* ptr) {
+ unsigned long long value = *ptr;
+ MEMORY_BARRIER();
+ return value;
+}
+#endif
+ALWAYS_INLINE void* AcquireLoad(void* volatile const* ptr) {
+ void* value = *ptr;
+ MEMORY_BARRIER();
+ return value;
+}
+
+// Do not use noBarrierStore/noBarrierLoad for synchronization.
+ALWAYS_INLINE void NoBarrierStore(volatile float* ptr, float value) {
+ *ptr = value;
+}
+
+ALWAYS_INLINE float NoBarrierLoad(volatile const float* ptr) {
+ float value = *ptr;
+ return value;
+}
+
+#if defined(ADDRESS_SANITIZER)
+
+NO_SANITIZE_ADDRESS ALWAYS_INLINE void AsanUnsafeReleaseStore(
+ volatile unsigned* ptr,
+ unsigned value) {
+ MEMORY_BARRIER();
+ *ptr = value;
+}
+
+NO_SANITIZE_ADDRESS ALWAYS_INLINE unsigned AsanUnsafeAcquireLoad(
+ volatile const unsigned* ptr) {
+ unsigned value = *ptr;
+ MEMORY_BARRIER();
+ return value;
+}
+
+#endif // defined(ADDRESS_SANITIZER)
+
+#undef MEMORY_BARRIER
+
+#endif
+
+#if !defined(ADDRESS_SANITIZER)
+
+ALWAYS_INLINE void AsanUnsafeReleaseStore(volatile unsigned* ptr,
+ unsigned value) {
+ ReleaseStore(ptr, value);
+}
+
+ALWAYS_INLINE unsigned AsanUnsafeAcquireLoad(volatile const unsigned* ptr) {
+ return AcquireLoad(ptr);
+}
+
+#endif
+
+} // namespace WTF
+
+using WTF::AtomicAdd;
+using WTF::AtomicSubtract;
+using WTF::AtomicDecrement;
+using WTF::AtomicIncrement;
+using WTF::AtomicTestAndSetToOne;
+using WTF::AtomicSetOneToZero;
+using WTF::AcquireLoad;
+using WTF::ReleaseStore;
+using WTF::NoBarrierLoad;
+using WTF::NoBarrierStore;
+
+// These methods allow loading from and storing to poisoned memory. Only
+// use these methods if you know what you are doing since they will
+// silence use-after-poison errors from ASan.
+using WTF::AsanUnsafeAcquireLoad;
+using WTF::AsanUnsafeReleaseStore;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_ATOMICS_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/auto_reset.h b/chromium/third_party/blink/renderer/platform/wtf/auto_reset.h
new file mode 100644
index 00000000000..5689c7e1de3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/auto_reset.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_AUTO_RESET_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_AUTO_RESET_H_
+
+#include "base/auto_reset.h"
+
+namespace WTF {
+
+// WTF::AutoReset is base::AutoReset. See base/auto_reset.h for documentation.
+
+template <typename T>
+using AutoReset = base::AutoReset<T>;
+
+} // namespace WTF
+
+using WTF::AutoReset;
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/wtf/bit_vector.cc b/chromium/third_party/blink/renderer/platform/wtf/bit_vector.cc
new file mode 100644
index 00000000000..2b2cd2c4727
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/bit_vector.cc
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2011 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 "third_party/blink/renderer/platform/wtf/bit_vector.h"
+
+#include <string.h>
+#include <algorithm>
+#include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
+#include "third_party/blink/renderer/platform/wtf/leak_annotations.h"
+
+namespace WTF {
+
+void BitVector::SetSlow(const BitVector& other) {
+ uintptr_t new_bits_or_pointer;
+ if (other.IsInline()) {
+ new_bits_or_pointer = other.bits_or_pointer_;
+ } else {
+ OutOfLineBits* new_out_of_line_bits = OutOfLineBits::Create(other.size());
+ memcpy(new_out_of_line_bits->Bits(), other.Bits(), ByteCount(other.size()));
+ new_bits_or_pointer = bit_cast<uintptr_t>(new_out_of_line_bits) >> 1;
+ }
+ if (!IsInline())
+ OutOfLineBits::Destroy(GetOutOfLineBits());
+ bits_or_pointer_ = new_bits_or_pointer;
+}
+
+void BitVector::Resize(size_t num_bits) {
+ if (num_bits <= MaxInlineBits()) {
+ if (IsInline())
+ return;
+
+ OutOfLineBits* my_out_of_line_bits = GetOutOfLineBits();
+ bits_or_pointer_ = MakeInlineBits(*my_out_of_line_bits->Bits());
+ OutOfLineBits::Destroy(my_out_of_line_bits);
+ return;
+ }
+
+ ResizeOutOfLine(num_bits);
+}
+
+void BitVector::ClearAll() {
+ if (IsInline())
+ bits_or_pointer_ = MakeInlineBits(0);
+ else
+ memset(GetOutOfLineBits()->Bits(), 0, ByteCount(size()));
+}
+
+BitVector::OutOfLineBits* BitVector::OutOfLineBits::Create(size_t num_bits) {
+ // Because of the way BitVector stores the pointer, memory tools
+ // will erroneously report a leak here.
+ WTF_INTERNAL_LEAK_SANITIZER_DISABLED_SCOPE;
+ num_bits = (num_bits + BitsInPointer() - 1) &
+ ~(BitsInPointer() - static_cast<size_t>(1));
+ size_t size =
+ sizeof(OutOfLineBits) + sizeof(uintptr_t) * (num_bits / BitsInPointer());
+ void* allocation = Partitions::BufferMalloc(
+ size, WTF_HEAP_PROFILER_TYPE_NAME(OutOfLineBits));
+ OutOfLineBits* result = new (NotNull, allocation) OutOfLineBits(num_bits);
+ return result;
+}
+
+void BitVector::OutOfLineBits::Destroy(OutOfLineBits* out_of_line_bits) {
+ Partitions::BufferFree(out_of_line_bits);
+}
+
+void BitVector::ResizeOutOfLine(size_t num_bits) {
+ DCHECK_GT(num_bits, MaxInlineBits());
+ OutOfLineBits* new_out_of_line_bits = OutOfLineBits::Create(num_bits);
+ size_t new_num_words = new_out_of_line_bits->NumWords();
+ if (IsInline()) {
+ // Make sure that all of the bits are zero in case we do a no-op resize.
+ *new_out_of_line_bits->Bits() =
+ bits_or_pointer_ & ~(static_cast<uintptr_t>(1) << MaxInlineBits());
+ memset(new_out_of_line_bits->Bits() + 1, 0,
+ (new_num_words - 1) * sizeof(void*));
+ } else {
+ if (num_bits > size()) {
+ size_t old_num_words = GetOutOfLineBits()->NumWords();
+ memcpy(new_out_of_line_bits->Bits(), GetOutOfLineBits()->Bits(),
+ old_num_words * sizeof(void*));
+ memset(new_out_of_line_bits->Bits() + old_num_words, 0,
+ (new_num_words - old_num_words) * sizeof(void*));
+ } else {
+ memcpy(new_out_of_line_bits->Bits(), GetOutOfLineBits()->Bits(),
+ new_out_of_line_bits->NumWords() * sizeof(void*));
+ }
+ OutOfLineBits::Destroy(GetOutOfLineBits());
+ }
+ bits_or_pointer_ = bit_cast<uintptr_t>(new_out_of_line_bits) >> 1;
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/bit_vector.h b/chromium/third_party/blink/renderer/platform/wtf/bit_vector.h
new file mode 100644
index 00000000000..d434424deb0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/bit_vector.h
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2011 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_BIT_VECTOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_BIT_VECTOR_H_
+
+#include "base/bit_cast.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_export.h"
+
+namespace WTF {
+
+// This is a space-efficient, resizeable bitvector class. In the common case it
+// occupies one word, but if necessary, it will inflate this one word to point
+// to a single chunk of out-of-line allocated storage to store an arbitrary
+// number of bits.
+//
+// - The bitvector remembers the bound of how many bits can be stored, but this
+// may be slightly greater (by as much as some platform-specific constant)
+// than the last argument passed to ensureSize().
+//
+// - The bitvector can resize itself automatically (set, clear, get) or can be
+// used in a manual mode, which is faster (quickSet, quickClear, quickGet,
+// ensureSize).
+//
+// - Accesses assert that you are within bounds.
+//
+// - Bits are automatically initialized to zero.
+//
+// On the other hand, this BitVector class may not be the fastest around, since
+// it does conditionals on every get/set/clear. But it is great if you need to
+// juggle a lot of variable-length BitVectors and you're worried about wasting
+// space.
+
+class WTF_EXPORT BitVector {
+ DISALLOW_NEW();
+
+ public:
+ BitVector() : bits_or_pointer_(MakeInlineBits(0)) {}
+
+ explicit BitVector(size_t num_bits) : bits_or_pointer_(MakeInlineBits(0)) {
+ EnsureSize(num_bits);
+ }
+
+ BitVector(const BitVector& other) : bits_or_pointer_(MakeInlineBits(0)) {
+ (*this) = other;
+ }
+
+ ~BitVector() {
+ if (IsInline())
+ return;
+ OutOfLineBits::Destroy(GetOutOfLineBits());
+ }
+
+ BitVector& operator=(const BitVector& other) {
+ if (IsInline() && other.IsInline())
+ bits_or_pointer_ = other.bits_or_pointer_;
+ else
+ SetSlow(other);
+ return *this;
+ }
+
+ size_t size() const {
+ if (IsInline())
+ return MaxInlineBits();
+ return GetOutOfLineBits()->NumBits();
+ }
+
+ void EnsureSize(size_t num_bits) {
+ if (num_bits <= size())
+ return;
+ ResizeOutOfLine(num_bits);
+ }
+
+ // Like ensureSize(), but supports reducing the size of the bitvector.
+ void Resize(size_t num_bits);
+
+ void ClearAll();
+
+ bool QuickGet(size_t bit) const {
+ SECURITY_CHECK(bit < size());
+ return !!(Bits()[bit / BitsInPointer()] &
+ (static_cast<uintptr_t>(1) << (bit & (BitsInPointer() - 1))));
+ }
+
+ void QuickSet(size_t bit) {
+ SECURITY_CHECK(bit < size());
+ Bits()[bit / BitsInPointer()] |=
+ (static_cast<uintptr_t>(1) << (bit & (BitsInPointer() - 1)));
+ }
+
+ void QuickClear(size_t bit) {
+ SECURITY_CHECK(bit < size());
+ Bits()[bit / BitsInPointer()] &=
+ ~(static_cast<uintptr_t>(1) << (bit & (BitsInPointer() - 1)));
+ }
+
+ void QuickSet(size_t bit, bool value) {
+ if (value)
+ QuickSet(bit);
+ else
+ QuickClear(bit);
+ }
+
+ bool Get(size_t bit) const {
+ if (bit >= size())
+ return false;
+ return QuickGet(bit);
+ }
+
+ void Set(size_t bit) {
+ EnsureSize(bit + 1);
+ QuickSet(bit);
+ }
+
+ void EnsureSizeAndSet(size_t bit, size_t size) {
+ EnsureSize(size);
+ QuickSet(bit);
+ }
+
+ void Clear(size_t bit) {
+ if (bit >= size())
+ return;
+ QuickClear(bit);
+ }
+
+ void Set(size_t bit, bool value) {
+ if (value)
+ Set(bit);
+ else
+ Clear(bit);
+ }
+
+ private:
+ static unsigned BitsInPointer() { return sizeof(void*) << 3; }
+
+ static unsigned MaxInlineBits() { return BitsInPointer() - 1; }
+
+ static size_t ByteCount(size_t bit_count) { return (bit_count + 7) >> 3; }
+
+ static uintptr_t MakeInlineBits(uintptr_t bits) {
+ DCHECK(!(bits & (static_cast<uintptr_t>(1) << MaxInlineBits())));
+ return bits | (static_cast<uintptr_t>(1) << MaxInlineBits());
+ }
+
+ class WTF_EXPORT OutOfLineBits {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ size_t NumBits() const { return num_bits_; }
+ size_t NumWords() const {
+ return (num_bits_ + BitsInPointer() - 1) / BitsInPointer();
+ }
+ uintptr_t* Bits() { return bit_cast<uintptr_t*>(this + 1); }
+ const uintptr_t* Bits() const {
+ return bit_cast<const uintptr_t*>(this + 1);
+ }
+
+ static OutOfLineBits* Create(size_t num_bits);
+
+ static void Destroy(OutOfLineBits*);
+
+ private:
+ OutOfLineBits(size_t num_bits) : num_bits_(num_bits) {}
+
+ size_t num_bits_;
+ };
+
+ bool IsInline() const { return bits_or_pointer_ >> MaxInlineBits(); }
+
+ const OutOfLineBits* GetOutOfLineBits() const {
+ return bit_cast<const OutOfLineBits*>(bits_or_pointer_ << 1);
+ }
+ OutOfLineBits* GetOutOfLineBits() {
+ return bit_cast<OutOfLineBits*>(bits_or_pointer_ << 1);
+ }
+
+ void ResizeOutOfLine(size_t num_bits);
+ void SetSlow(const BitVector& other);
+
+ uintptr_t* Bits() {
+ if (IsInline())
+ return &bits_or_pointer_;
+ return GetOutOfLineBits()->Bits();
+ }
+
+ const uintptr_t* Bits() const {
+ if (IsInline())
+ return &bits_or_pointer_;
+ return GetOutOfLineBits()->Bits();
+ }
+
+ uintptr_t bits_or_pointer_;
+};
+
+} // namespace WTF
+
+using WTF::BitVector;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_BIT_VECTOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/bloom_filter.h b/chromium/third_party/blink/renderer/platform/wtf/bloom_filter.h
new file mode 100644
index 00000000000..7db6c577d83
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/bloom_filter.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2011 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_BLOOM_FILTER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_BLOOM_FILTER_H_
+
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/compiler.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+
+namespace WTF {
+
+// Counting bloom filter with k=2 and 8 bit counters. Uses 2^keyBits bytes of
+// memory. False positive rate is approximately (1-e^(-2n/m))^2, where n is
+// the number of unique keys and m is the table size (==2^keyBits).
+template <unsigned keyBits>
+class BloomFilter {
+ USING_FAST_MALLOC(BloomFilter);
+
+ public:
+ static_assert(keyBits <= 16, "bloom filter key size check");
+
+ static const size_t kTableSize = 1 << keyBits;
+ static const unsigned kKeyMask = (1 << keyBits) - 1;
+ static uint8_t MaximumCount() { return std::numeric_limits<uint8_t>::max(); }
+
+ BloomFilter() { Clear(); }
+
+ void Add(unsigned hash);
+ void Remove(unsigned hash);
+
+ // The filter may give false positives (claim it may contain a key it doesn't)
+ // but never false negatives (claim it doesn't contain a key it does).
+ bool MayContain(unsigned hash) const {
+ return FirstSlot(hash) && SecondSlot(hash);
+ }
+
+ // The filter must be cleared before reuse even if all keys are removed.
+ // Otherwise overflowed keys will stick around.
+ void Clear();
+
+ void Add(const AtomicString& string) { Add(string.Impl()->ExistingHash()); }
+ void Add(const String& string) { Add(string.Impl()->GetHash()); }
+ void Remove(const AtomicString& string) {
+ Remove(string.Impl()->ExistingHash());
+ }
+ void Remove(const String& string) { Remove(string.Impl()->GetHash()); }
+
+ bool MayContain(const AtomicString& string) const {
+ return MayContain(string.Impl()->ExistingHash());
+ }
+ bool MayContain(const String& string) const {
+ return MayContain(string.Impl()->GetHash());
+ }
+
+#if DCHECK_IS_ON()
+ // Slow.
+ bool LikelyEmpty() const;
+ bool IsClear() const;
+#endif
+
+ private:
+ uint8_t& FirstSlot(unsigned hash) { return table_[hash & kKeyMask]; }
+ uint8_t& SecondSlot(unsigned hash) { return table_[(hash >> 16) & kKeyMask]; }
+ const uint8_t& FirstSlot(unsigned hash) const {
+ return table_[hash & kKeyMask];
+ }
+ const uint8_t& SecondSlot(unsigned hash) const {
+ return table_[(hash >> 16) & kKeyMask];
+ }
+
+ uint8_t table_[kTableSize];
+};
+
+template <unsigned keyBits>
+inline void BloomFilter<keyBits>::Add(unsigned hash) {
+ uint8_t& first = FirstSlot(hash);
+ uint8_t& second = SecondSlot(hash);
+ if (LIKELY(first < MaximumCount()))
+ ++first;
+ if (LIKELY(second < MaximumCount()))
+ ++second;
+}
+
+template <unsigned keyBits>
+inline void BloomFilter<keyBits>::Remove(unsigned hash) {
+ uint8_t& first = FirstSlot(hash);
+ uint8_t& second = SecondSlot(hash);
+ DCHECK(first);
+ DCHECK(second);
+ // In case of an overflow, the slot sticks in the table until clear().
+ if (LIKELY(first < MaximumCount()))
+ --first;
+ if (LIKELY(second < MaximumCount()))
+ --second;
+}
+
+template <unsigned keyBits>
+inline void BloomFilter<keyBits>::Clear() {
+ memset(table_, 0, kTableSize);
+}
+
+#if DCHECK_IS_ON()
+template <unsigned keyBits>
+bool BloomFilter<keyBits>::LikelyEmpty() const {
+ for (size_t n = 0; n < kTableSize; ++n) {
+ if (table_[n] && table_[n] != MaximumCount())
+ return false;
+ }
+ return true;
+}
+
+template <unsigned keyBits>
+bool BloomFilter<keyBits>::IsClear() const {
+ for (size_t n = 0; n < kTableSize; ++n) {
+ if (table_[n])
+ return false;
+ }
+ return true;
+}
+#endif
+
+} // namespace WTF
+
+using WTF::BloomFilter;
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/wtf/byte_order.h b/chromium/third_party/blink/renderer/platform/wtf/byte_order.h
new file mode 100644
index 00000000000..94b6464021a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/byte_order.h
@@ -0,0 +1,74 @@
+/*
+* Copyright (C) 2012 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_BYTE_ORDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_BYTE_ORDER_H_
+
+#include "build/build_config.h"
+
+#if defined(OS_POSIX)
+#include <arpa/inet.h>
+#endif
+
+#if defined(OS_WIN)
+
+#include "third_party/blink/renderer/platform/wtf/byte_swap.h"
+
+#if defined(ARCH_CPU_BIG_ENDIAN)
+inline uint16_t ntohs(uint16_t x) {
+ return x;
+}
+inline uint16_t htons(uint16_t x) {
+ return x;
+}
+inline uint32_t ntohl(uint32_t x) {
+ return x;
+}
+inline uint32_t htonl(uint32_t x) {
+ return x;
+}
+#else
+inline uint16_t ntohs(uint16_t x) {
+ return WTF::Bswap16(x);
+}
+inline uint16_t htons(uint16_t x) {
+ return WTF::Bswap16(x);
+}
+inline uint32_t ntohl(uint32_t x) {
+ return WTF::Bswap32(x);
+}
+inline uint32_t htonl(uint32_t x) {
+ return WTF::Bswap32(x);
+}
+#endif
+
+#endif // defined(OS_WIN)
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_BYTE_ORDER_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/byte_swap.h b/chromium/third_party/blink/renderer/platform/wtf/byte_swap.h
new file mode 100644
index 00000000000..ebd6896f00e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/byte_swap.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_BYTE_SWAP_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_BYTE_SWAP_H_
+
+#include <stdint.h>
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/wtf/compiler.h"
+#include "third_party/blink/renderer/platform/wtf/cpu.h"
+
+#if defined(COMPILER_MSVC)
+#include <stdlib.h>
+#endif
+
+namespace WTF {
+
+inline uint32_t Wswap32(uint32_t x) {
+ return ((x & 0xffff0000) >> 16) | ((x & 0x0000ffff) << 16);
+}
+
+#if defined(COMPILER_MSVC)
+
+ALWAYS_INLINE uint64_t Bswap64(uint64_t x) {
+ return _byteswap_uint64(x);
+}
+ALWAYS_INLINE uint32_t Bswap32(uint32_t x) {
+ return _byteswap_ulong(x);
+}
+ALWAYS_INLINE uint16_t Bswap16(uint16_t x) {
+ return _byteswap_ushort(x);
+}
+
+#else
+
+ALWAYS_INLINE uint64_t Bswap64(uint64_t x) {
+ return __builtin_bswap64(x);
+}
+ALWAYS_INLINE uint32_t Bswap32(uint32_t x) {
+ return __builtin_bswap32(x);
+}
+ALWAYS_INLINE uint16_t Bswap16(uint16_t x) {
+ return __builtin_bswap16(x);
+}
+
+#endif
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_BYTE_SWAP_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/checked_numeric.h b/chromium/third_party/blink/renderer/platform/wtf/checked_numeric.h
new file mode 100644
index 00000000000..5a705f9cb9c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/checked_numeric.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2011 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_CHECKED_NUMERIC_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_CHECKED_NUMERIC_H_
+
+/* See base/numerics/safe_math.h for usage.
+ */
+#include "base/numerics/safe_math.h"
+
+namespace WTF {
+using base::CheckedNumeric;
+using base::IsValidForType;
+using base::ValueOrDieForType;
+using base::ValueOrDefaultForType;
+using base::MakeCheckedNum;
+using base::CheckMax;
+using base::CheckMin;
+using base::CheckAdd;
+using base::CheckSub;
+using base::CheckMul;
+using base::CheckDiv;
+using base::CheckMod;
+using base::CheckLsh;
+using base::CheckRsh;
+using base::CheckAnd;
+using base::CheckOr;
+using base::CheckXor;
+} // namespace WTF
+
+using WTF::CheckedNumeric;
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/wtf/compiler.h b/chromium/third_party/blink/renderer/platform/wtf/compiler.h
new file mode 100644
index 00000000000..4f0d73c30df
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/compiler.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2011, 2012 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_COMPILER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_COMPILER_H_
+
+#include "base/compiler_specific.h"
+#include "build/build_config.h"
+
+#if defined(__GNUC__)
+#define GCC_VERSION \
+ (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
+#define GCC_VERSION_AT_LEAST(major, minor, patch) \
+ (GCC_VERSION >= (major * 10000 + minor * 100 + patch))
+#else
+/* Define this for !GCC compilers, just so we can write things like
+ * GCC_VERSION_AT_LEAST(4, 1, 0). */
+#define GCC_VERSION_AT_LEAST(major, minor, patch) 0
+#endif
+
+/* ==== Compiler features ==== */
+
+/* NEVER_INLINE */
+
+// TODO(palmer): Remove this and update callers to use NOINLINE from Chromium
+// base. https://bugs.chromium.org/p/chromium/issues/detail?id=632441
+#define NEVER_INLINE NOINLINE
+
+/* OBJC_CLASS */
+
+#ifndef OBJC_CLASS
+#ifdef __OBJC__
+#define OBJC_CLASS @class
+#else
+#define OBJC_CLASS class
+#endif
+#endif
+
+/* WTF_PRETTY_FUNCTION */
+
+#if defined(COMPILER_GCC)
+#define WTF_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#elif defined(COMPILER_MSVC)
+#define WTF_PRETTY_FUNCTION __FUNCSIG__
+#else
+#define WTF_PRETTY_FUNCTION __func__
+#endif
+
+/* NO_SANITIZE_UNRELATED_CAST - Disable runtime checks related to casts between
+ * unrelated objects (-fsanitize=cfi-unrelated-cast or -fsanitize=vptr). */
+
+#if defined(__clang__)
+#define NO_SANITIZE_UNRELATED_CAST \
+ __attribute__((no_sanitize("cfi-unrelated-cast", "vptr")))
+#else
+#define NO_SANITIZE_UNRELATED_CAST
+#endif
+
+#endif /* WTF_Compiler_h */
diff --git a/chromium/third_party/blink/renderer/platform/wtf/conditional_destructor.h b/chromium/third_party/blink/renderer/platform/wtf/conditional_destructor.h
new file mode 100644
index 00000000000..1b7e0020cd0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/conditional_destructor.h
@@ -0,0 +1,28 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_CONDITIONAL_DESTRUCTOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_CONDITIONAL_DESTRUCTOR_H_
+
+namespace WTF {
+
+// ConditionalDestructor defines the destructor of the derived object.
+// This base is used in order to completely avoid creating a destructor
+// for an object that does not need to be destructed. By doing so,
+// the clang compiler will have correct information about whether or not
+// the object has a trivial destructor.
+// Note: the derived object MUST release all its recources at the finalize()
+// method.
+template <typename Derived, bool noDestructor>
+class ConditionalDestructor {
+ public:
+ ~ConditionalDestructor() { static_cast<Derived*>(this)->Finalize(); }
+};
+
+template <typename Derived>
+class ConditionalDestructor<Derived, true> {};
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_CONDITIONAL_DESTRUCTOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/construct_traits.h b/chromium/third_party/blink/renderer/platform/wtf/construct_traits.h
new file mode 100644
index 00000000000..c6a4fdc6a46
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/construct_traits.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_CONSTRUCT_TRAITS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_CONSTRUCT_TRAITS_H_
+
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/type_traits.h"
+#include "third_party/blink/renderer/platform/wtf/vector_traits.h"
+
+namespace WTF {
+
+// ConstructTraits is used to construct elements in WTF collections. All
+// in-place constructions that may assign Oilpan objects must be dispatched
+// through ConstructAndNotifyElement.
+template <typename T, typename Traits, typename Allocator>
+class ConstructTraits {
+ STATIC_ONLY(ConstructTraits);
+
+ public:
+ // Construct a single element that would otherwise be constructed using
+ // placement new.
+ template <typename... Args>
+ static T* ConstructAndNotifyElement(void* location, Args&&... args) {
+ T* object = new (NotNull, location) T(std::forward<Args>(args)...);
+ Allocator::template NotifyNewObject<T, Traits>(object);
+ return object;
+ }
+
+ // After constructing elements using memcopy or memmove (or similar)
+ // |NotifyNewElements| needs to be called to propagate that information.
+ static void NotifyNewElements(T* array, size_t len) {
+ Allocator::template NotifyNewObjects<T, Traits>(array, len);
+ }
+};
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_CONSTRUCT_TRAITS_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/container_annotations.h b/chromium/third_party/blink/renderer/platform/wtf/container_annotations.h
new file mode 100644
index 00000000000..199bf386953
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/container_annotations.h
@@ -0,0 +1,47 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_CONTAINER_ANNOTATIONS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_CONTAINER_ANNOTATIONS_H_
+
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/wtf/address_sanitizer.h"
+
+// TODO(ochang): Remove the ARCH_CPU_X86_64 condition to enable this for X86
+// once the crashes there have been fixed: http://crbug.com/461406
+#if defined(ADDRESS_SANITIZER) && defined(OS_LINUX) && defined(ARCH_CPU_X86_64)
+#define ANNOTATE_CONTIGUOUS_CONTAINER
+#define ANNOTATE_NEW_BUFFER(buffer, capacity, newSize) \
+ if (buffer) { \
+ __sanitizer_annotate_contiguous_container(buffer, (buffer) + (capacity), \
+ (buffer) + (capacity), \
+ (buffer) + (newSize)); \
+ }
+#define ANNOTATE_DELETE_BUFFER(buffer, capacity, oldSize) \
+ if (buffer) { \
+ __sanitizer_annotate_contiguous_container(buffer, (buffer) + (capacity), \
+ (buffer) + (oldSize), \
+ (buffer) + (capacity)); \
+ }
+#define ANNOTATE_CHANGE_SIZE(buffer, capacity, oldSize, newSize) \
+ if (buffer) { \
+ __sanitizer_annotate_contiguous_container(buffer, (buffer) + (capacity), \
+ (buffer) + (oldSize), \
+ (buffer) + (newSize)); \
+ }
+#define ANNOTATE_CHANGE_CAPACITY(buffer, oldCapacity, bufferSize, newCapacity) \
+ ANNOTATE_DELETE_BUFFER(buffer, oldCapacity, bufferSize); \
+ ANNOTATE_NEW_BUFFER(buffer, newCapacity, bufferSize);
+// Annotations require buffers to begin on an 8-byte boundary.
+
+#else // ADDRESS_SANITIZER && OS_LINUX && ARCH_CPU_X86_64
+
+#define ANNOTATE_NEW_BUFFER(buffer, capacity, newSize)
+#define ANNOTATE_DELETE_BUFFER(buffer, capacity, oldSize)
+#define ANNOTATE_CHANGE_SIZE(buffer, capacity, oldSize, newSize)
+#define ANNOTATE_CHANGE_CAPACITY(buffer, oldCapacity, bufferSize, newCapacity)
+
+#endif // ADDRESS_SANITIZER && OS_LINUX && ARCH_CPU_X86_64
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_CONTAINER_ANNOTATIONS_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/cpu.h b/chromium/third_party/blink/renderer/platform/wtf/cpu.h
new file mode 100644
index 00000000000..033f6eeaf3a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/cpu.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2006, 2007, 2008, 2009, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2007-2009 Torch Mobile, Inc.
+ * Copyright (C) 2010, 2011 Research In Motion Limited. All rights reserved.
+ * Copyright (C) 2013 Samsung Electronics. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_CPU_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_CPU_H_
+
+#if defined(arm) || defined(__arm__) || defined(ARM) || defined(_ARM_)
+
+#if !defined(__ARMEB__) && !defined(__ARM_EABI__) && !defined(__EABI__) && \
+ !defined(__VFP_FP__) && !defined(_WIN32_WCE) && !defined(ANDROID)
+#error Chromium does not support middle endian architecture
+#endif
+
+// WTF_CPU_ARM_NEON is 0 or 1, and should not use defined(WTF_CPU_ARM_NEON).
+#if defined(__ARM_NEON__) && !defined(WTF_CPU_ARM_NEON)
+#define WTF_CPU_ARM_NEON 1
+#endif
+
+#endif /* ARM */
+
+#if !defined(WTF_CPU_ARM_NEON)
+#define WTF_CPU_ARM_NEON 0
+#endif
+
+// HAVE_MIPS_MSA_INTRINSICS is 0 or 1, and we should not use
+// defined(HAVE_MIPS_MSA_INTRINSICS).
+#if defined(__mips_msa) && defined(__mips_isa_rev) && (__mips_isa_rev >= 5)
+#define HAVE_MIPS_MSA_INTRINSICS 1
+#else
+#define HAVE_MIPS_MSA_INTRINSICS 0
+#endif
+
+#endif /* WTF_CPU_h */
diff --git a/chromium/third_party/blink/renderer/platform/wtf/cryptographically_random_number.cc b/chromium/third_party/blink/renderer/platform/wtf/cryptographically_random_number.cc
new file mode 100644
index 00000000000..4c68e424619
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/cryptographically_random_number.cc
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 1996, David Mazieres <dm@uun.org>
+ * Copyright (c) 2008, Damien Miller <djm@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "third_party/blink/renderer/platform/wtf/cryptographically_random_number.h"
+
+#include "base/rand_util.h"
+
+namespace WTF {
+
+uint32_t CryptographicallyRandomNumber() {
+ uint32_t result;
+ CryptographicallyRandomValues(&result, sizeof(result));
+ return result;
+}
+
+void CryptographicallyRandomValues(void* buffer, size_t length) {
+ // This should really be crypto::RandBytes(), but WTF can't depend on crypto.
+ // The implementation of crypto::RandBytes() is just calling
+ // base::RandBytes(), so both are actually same.
+ base::RandBytes(buffer, length);
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/cryptographically_random_number.h b/chromium/third_party/blink/renderer/platform/wtf/cryptographically_random_number.h
new file mode 100644
index 00000000000..411a3a5bd4f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/cryptographically_random_number.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2008 Torch Mobile Inc. All rights reserved.
+ * (http://www.torchmobile.com/)
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_CRYPTOGRAPHICALLY_RANDOM_NUMBER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_CRYPTOGRAPHICALLY_RANDOM_NUMBER_H_
+
+#include <stdint.h>
+#include <cstddef>
+#include "third_party/blink/renderer/platform/wtf/wtf_export.h"
+
+namespace WTF {
+
+// These functions are threadsafe.
+WTF_EXPORT uint32_t CryptographicallyRandomNumber();
+WTF_EXPORT void CryptographicallyRandomValues(void* buffer, size_t length);
+}
+
+using WTF::CryptographicallyRandomNumber;
+using WTF::CryptographicallyRandomValues;
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/wtf/date_math.cc b/chromium/third_party/blink/renderer/platform/wtf/date_math.cc
new file mode 100644
index 00000000000..183148d26a5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/date_math.cc
@@ -0,0 +1,846 @@
+/*
+ * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
+ * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ * Copyright (C) 2007-2009 Torch Mobile, Inc.
+ * Copyright (C) 2010 &yet, LLC. (nate@andyet.net)
+ *
+ * The Original Code is Mozilla Communicator client code, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Alternatively, the contents of this file may be used under the terms
+ * of either the Mozilla Public License Version 1.1, found at
+ * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
+ * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
+ * (the "GPL"), in which case the provisions of the MPL or the GPL are
+ * applicable instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of one of those two
+ * licenses (the MPL or the GPL) and not to allow others to use your
+ * version of this file under the LGPL, indicate your decision by
+ * deletingthe provisions above and replace them with the notice and
+ * other provisions required by the MPL or the GPL, as the case may be.
+ * If you do not delete the provisions above, a recipient may use your
+ * version of this file under any of the LGPL, the MPL or the GPL.
+
+ * Copyright 2006-2008 the V8 project authors. All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "third_party/blink/renderer/platform/wtf/date_math.h"
+
+#include <limits.h>
+#include <math.h>
+#include <stdlib.h>
+#include <time.h>
+#include <algorithm>
+#include <limits>
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/wtf/ascii_ctype.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/string_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#else
+#include <sys/time.h>
+#endif
+
+namespace WTF {
+
+/* Constants */
+
+static const double kHoursPerDay = 24.0;
+static const double kSecondsPerDay = 24.0 * 60.0 * 60.0;
+
+static const double kMaxUnixTime = 2145859200.0; // 12/31/2037
+static const double kMinimumECMADateInMs = -8640000000000000.0;
+static const double kMaximumECMADateInMs = 8640000000000000.0;
+
+// Day of year for the first day of each month, where index 0 is January, and
+// day 0 is January 1. First for non-leap years, then for leap years.
+static const int kFirstDayOfMonth[2][12] = {
+ {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
+ {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}};
+
+static inline void GetLocalTime(const time_t* local_time, struct tm* local_tm) {
+#if defined(COMPILER_MSVC)
+ localtime_s(local_tm, local_time);
+#else
+ localtime_r(local_time, local_tm);
+#endif
+}
+
+bool IsLeapYear(int year) {
+ if (year % 4 != 0)
+ return false;
+ if (year % 400 == 0)
+ return true;
+ if (year % 100 == 0)
+ return false;
+ return true;
+}
+
+static inline int DaysInYear(int year) {
+ return 365 + IsLeapYear(year);
+}
+
+static inline double DaysFrom1970ToYear(int year) {
+ // The Gregorian Calendar rules for leap years:
+ // Every fourth year is a leap year. 2004, 2008, and 2012 are leap years.
+ // However, every hundredth year is not a leap year. 1900 and 2100 are not
+ // leap years.
+ // Every four hundred years, there's a leap year after all. 2000 and 2400 are
+ // leap years.
+
+ static const int kLeapDaysBefore1971By4Rule = 1970 / 4;
+ static const int kExcludedLeapDaysBefore1971By100Rule = 1970 / 100;
+ static const int kLeapDaysBefore1971By400Rule = 1970 / 400;
+
+ const double year_minus_one = year - 1;
+ const double years_to_add_by4_rule =
+ floor(year_minus_one / 4.0) - kLeapDaysBefore1971By4Rule;
+ const double years_to_exclude_by100_rule =
+ floor(year_minus_one / 100.0) - kExcludedLeapDaysBefore1971By100Rule;
+ const double years_to_add_by400_rule =
+ floor(year_minus_one / 400.0) - kLeapDaysBefore1971By400Rule;
+
+ return 365.0 * (year - 1970) + years_to_add_by4_rule -
+ years_to_exclude_by100_rule + years_to_add_by400_rule;
+}
+
+static double MsToDays(double ms) {
+ return floor(ms / kMsPerDay);
+}
+
+static void AppendTwoDigitNumber(StringBuilder& builder, int number) {
+ DCHECK_GE(number, 0);
+ DCHECK_LT(number, 100);
+ if (number <= 9)
+ builder.Append('0');
+ builder.AppendNumber(number);
+}
+
+int MsToYear(double ms) {
+ DCHECK(std::isfinite(ms));
+ DCHECK_GE(ms, kMinimumECMADateInMs);
+ DCHECK_LE(ms, kMaximumECMADateInMs);
+ int approx_year = static_cast<int>(floor(ms / (kMsPerDay * 365.2425)) + 1970);
+ double ms_from_approx_year_to1970 =
+ kMsPerDay * DaysFrom1970ToYear(approx_year);
+ if (ms_from_approx_year_to1970 > ms)
+ return approx_year - 1;
+ if (ms_from_approx_year_to1970 + kMsPerDay * DaysInYear(approx_year) <= ms)
+ return approx_year + 1;
+ return approx_year;
+}
+
+int DayInYear(double ms, int year) {
+ return static_cast<int>(MsToDays(ms) - DaysFrom1970ToYear(year));
+}
+
+static inline double MsToMilliseconds(double ms) {
+ double result = fmod(ms, kMsPerDay);
+ if (result < 0)
+ result += kMsPerDay;
+ return result;
+}
+
+int MonthFromDayInYear(int day_in_year, bool leap_year) {
+ const int d = day_in_year;
+ int step;
+
+ if (d < (step = 31))
+ return 0;
+ step += (leap_year ? 29 : 28);
+ if (d < step)
+ return 1;
+ if (d < (step += 31))
+ return 2;
+ if (d < (step += 30))
+ return 3;
+ if (d < (step += 31))
+ return 4;
+ if (d < (step += 30))
+ return 5;
+ if (d < (step += 31))
+ return 6;
+ if (d < (step += 31))
+ return 7;
+ if (d < (step += 30))
+ return 8;
+ if (d < (step += 31))
+ return 9;
+ if (d < (step += 30))
+ return 10;
+ return 11;
+}
+
+static inline bool CheckMonth(int day_in_year,
+ int& start_day_of_this_month,
+ int& start_day_of_next_month,
+ int days_in_this_month) {
+ start_day_of_this_month = start_day_of_next_month;
+ start_day_of_next_month += days_in_this_month;
+ return (day_in_year <= start_day_of_next_month);
+}
+
+int DayInMonthFromDayInYear(int day_in_year, bool leap_year) {
+ const int d = day_in_year;
+ int step;
+ int next = 30;
+
+ if (d <= next)
+ return d + 1;
+ const int days_in_feb = (leap_year ? 29 : 28);
+ if (CheckMonth(d, step, next, days_in_feb))
+ return d - step;
+ if (CheckMonth(d, step, next, 31))
+ return d - step;
+ if (CheckMonth(d, step, next, 30))
+ return d - step;
+ if (CheckMonth(d, step, next, 31))
+ return d - step;
+ if (CheckMonth(d, step, next, 30))
+ return d - step;
+ if (CheckMonth(d, step, next, 31))
+ return d - step;
+ if (CheckMonth(d, step, next, 31))
+ return d - step;
+ if (CheckMonth(d, step, next, 30))
+ return d - step;
+ if (CheckMonth(d, step, next, 31))
+ return d - step;
+ if (CheckMonth(d, step, next, 30))
+ return d - step;
+ step = next;
+ return d - step;
+}
+
+int DayInYear(int year, int month, int day) {
+ return kFirstDayOfMonth[IsLeapYear(year)][month] + day - 1;
+}
+
+double DateToDaysFrom1970(int year, int month, int day) {
+ year += month / 12;
+
+ month %= 12;
+ if (month < 0) {
+ month += 12;
+ --year;
+ }
+
+ double yearday = floor(DaysFrom1970ToYear(year));
+ DCHECK((year >= 1970 && yearday >= 0) || (year < 1970 && yearday < 0));
+ return yearday + DayInYear(year, month, day);
+}
+
+// There is a hard limit at 2038 that we currently do not have a workaround
+// for (rdar://problem/5052975).
+static inline int MaximumYearForDST() {
+ return 2037;
+}
+
+static inline double JsCurrentTime() {
+ // JavaScript doesn't recognize fractions of a millisecond.
+ return floor(WTF::CurrentTimeMS());
+}
+
+static inline int MinimumYearForDST() {
+ // Because of the 2038 issue (see maximumYearForDST) if the current year is
+ // greater than the max year minus 27 (2010), we want to use the max year
+ // minus 27 instead, to ensure there is a range of 28 years that all years
+ // can map to.
+ return std::min(MsToYear(JsCurrentTime()), MaximumYearForDST() - 27);
+}
+
+// Find an equivalent year for the one given, where equivalence is deterined by
+// the two years having the same leapness and the first day of the year, falling
+// on the same day of the week.
+//
+// This function returns a year between this current year and 2037, however this
+// function will potentially return incorrect results if the current year is
+// after 2010, (rdar://problem/5052975), if the year passed in is before 1900
+// or after 2100, (rdar://problem/5055038).
+static int EquivalentYearForDST(int year) {
+ // It is ok if the cached year is not the current year as long as the rules
+ // for DST did not change between the two years; if they did the app would
+ // need to be restarted.
+ static int min_year = MinimumYearForDST();
+ int max_year = MaximumYearForDST();
+
+ int difference;
+ if (year > max_year)
+ difference = min_year - year;
+ else if (year < min_year)
+ difference = max_year - year;
+ else
+ return year;
+
+ int quotient = difference / 28;
+ int product = (quotient)*28;
+
+ year += product;
+ DCHECK((year >= min_year && year <= max_year) ||
+ (product - year ==
+ static_cast<int>(std::numeric_limits<double>::quiet_NaN())));
+ return year;
+}
+
+static double CalculateUTCOffset() {
+#if defined(OS_WIN)
+ TIME_ZONE_INFORMATION time_zone_information;
+ GetTimeZoneInformation(&time_zone_information);
+ int32_t bias =
+ time_zone_information.Bias + time_zone_information.StandardBias;
+ return -bias * 60 * 1000;
+#else
+ time_t local_time = time(nullptr);
+ tm localt;
+ GetLocalTime(&local_time, &localt);
+
+ // tm_gmtoff includes any daylight savings offset, so subtract it.
+ return static_cast<double>(localt.tm_gmtoff * kMsPerSecond -
+ (localt.tm_isdst > 0 ? kMsPerHour : 0));
+#endif
+}
+
+/*
+ * Get the DST offset for the time passed in.
+ */
+static double CalculateDSTOffsetSimple(double local_time_seconds,
+ double utc_offset) {
+ if (local_time_seconds > kMaxUnixTime)
+ local_time_seconds = kMaxUnixTime;
+ else if (local_time_seconds <
+ 0) // Go ahead a day to make localtime work (does not work with 0)
+ local_time_seconds += kSecondsPerDay;
+
+ // FIXME: time_t has a potential problem in 2038
+ time_t local_time = static_cast<time_t>(local_time_seconds);
+
+ tm local_tm;
+ GetLocalTime(&local_time, &local_tm);
+
+ return local_tm.tm_isdst > 0 ? kMsPerHour : 0;
+}
+
+// Get the DST offset, given a time in UTC
+static double CalculateDSTOffset(double ms, double utc_offset) {
+ // On macOS, the call to localtime (see calculateDSTOffsetSimple) will return
+ // historically accurate DST information (e.g. New Zealand did not have DST
+ // from 1946 to 1974) however the JavaScript standard explicitly dictates
+ // that historical information should not be considered when determining DST.
+ // For this reason we shift away from years that localtime can handle but
+ // would return historically accurate information.
+ int year = MsToYear(ms);
+ int equivalent_year = EquivalentYearForDST(year);
+ if (year != equivalent_year) {
+ bool leap_year = IsLeapYear(year);
+ int day_in_year_local = DayInYear(ms, year);
+ int day_in_month = DayInMonthFromDayInYear(day_in_year_local, leap_year);
+ int month = MonthFromDayInYear(day_in_year_local, leap_year);
+ double day = DateToDaysFrom1970(equivalent_year, month, day_in_month);
+ ms = (day * kMsPerDay) + MsToMilliseconds(ms);
+ }
+
+ return CalculateDSTOffsetSimple(ms / kMsPerSecond, utc_offset);
+}
+
+void InitializeDates() {
+#if DCHECK_IS_ON()
+ static bool already_initialized;
+ DCHECK(!already_initialized);
+ already_initialized = true;
+#endif
+
+ EquivalentYearForDST(
+ 2000); // Need to call once to initialize a static used in this function.
+}
+
+static inline double YmdhmsToSeconds(int year,
+ long mon,
+ long day,
+ long hour,
+ long minute,
+ double second) {
+ double days =
+ (day - 32075) + floor(1461 * (year + 4800.0 + (mon - 14) / 12) / 4) +
+ 367 * (mon - 2 - (mon - 14) / 12 * 12) / 12 -
+ floor(3 * ((year + 4900.0 + (mon - 14) / 12) / 100) / 4) - 2440588;
+ return ((days * kHoursPerDay + hour) * kMinutesPerHour + minute) *
+ kSecondsPerMinute +
+ second;
+}
+
+// We follow the recommendation of RFC 2822 to consider all
+// obsolete time zones not listed here equivalent to "-0000".
+static const struct KnownZone {
+#if !defined(OS_WIN)
+ const
+#endif
+ char tz_name[4];
+ int tz_offset;
+} known_zones[] = {{"UT", 0}, {"GMT", 0}, {"EST", -300}, {"EDT", -240},
+ {"CST", -360}, {"CDT", -300}, {"MST", -420}, {"MDT", -360},
+ {"PST", -480}, {"PDT", -420}};
+
+inline static void SkipSpacesAndComments(const char*& s) {
+ int nesting = 0;
+ char ch;
+ while ((ch = *s)) {
+ if (!IsASCIISpace(ch)) {
+ if (ch == '(')
+ nesting++;
+ else if (ch == ')' && nesting > 0)
+ nesting--;
+ else if (nesting == 0)
+ break;
+ }
+ s++;
+ }
+}
+
+// returns 0-11 (Jan-Dec); -1 on failure
+static int FindMonth(const char* month_str) {
+ DCHECK(month_str);
+ char needle[4];
+ for (int i = 0; i < 3; ++i) {
+ if (!*month_str)
+ return -1;
+ needle[i] = static_cast<char>(ToASCIILower(*month_str++));
+ }
+ needle[3] = '\0';
+ const char* haystack = "janfebmaraprmayjunjulaugsepoctnovdec";
+ const char* str = strstr(haystack, needle);
+ if (str) {
+ int position = static_cast<int>(str - haystack);
+ if (position % 3 == 0)
+ return position / 3;
+ }
+ return -1;
+}
+
+static bool ParseInt(const char* string,
+ char** stop_position,
+ int base,
+ int* result) {
+ long long_result = strtol(string, stop_position, base);
+ // Avoid the use of errno as it is not available on Windows CE
+ if (string == *stop_position ||
+ long_result <= std::numeric_limits<int>::min() ||
+ long_result >= std::numeric_limits<int>::max())
+ return false;
+ *result = static_cast<int>(long_result);
+ return true;
+}
+
+static bool ParseLong(const char* string,
+ char** stop_position,
+ int base,
+ long* result) {
+ *result = strtol(string, stop_position, base);
+ // Avoid the use of errno as it is not available on Windows CE
+ if (string == *stop_position || *result == std::numeric_limits<long>::min() ||
+ *result == std::numeric_limits<long>::max())
+ return false;
+ return true;
+}
+
+// Odd case where 'exec' is allowed to be 0, to accomodate a caller in WebCore.
+static double ParseDateFromNullTerminatedCharacters(const char* date_string,
+ bool& have_tz,
+ int& offset) {
+ have_tz = false;
+ offset = 0;
+
+ // This parses a date in the form:
+ // Tuesday, 09-Nov-99 23:12:40 GMT
+ // or
+ // Sat, 01-Jan-2000 08:00:00 GMT
+ // or
+ // Sat, 01 Jan 2000 08:00:00 GMT
+ // or
+ // 01 Jan 99 22:00 +0100 (exceptions in rfc822/rfc2822)
+ // ### non RFC formats, added for Javascript:
+ // [Wednesday] January 09 1999 23:12:40 GMT
+ // [Wednesday] January 09 23:12:40 GMT 1999
+ //
+ // We ignore the weekday.
+
+ // Skip leading space
+ SkipSpacesAndComments(date_string);
+
+ long month = -1;
+ const char* word_start = date_string;
+ // Check contents of first words if not number
+ while (*date_string && !IsASCIIDigit(*date_string)) {
+ if (IsASCIISpace(*date_string) || *date_string == '(') {
+ if (date_string - word_start >= 3)
+ month = FindMonth(word_start);
+ SkipSpacesAndComments(date_string);
+ word_start = date_string;
+ } else {
+ date_string++;
+ }
+ }
+
+ // Missing delimiter between month and day (like "January29")?
+ if (month == -1 && word_start != date_string)
+ month = FindMonth(word_start);
+
+ SkipSpacesAndComments(date_string);
+
+ if (!*date_string)
+ return std::numeric_limits<double>::quiet_NaN();
+
+ // ' 09-Nov-99 23:12:40 GMT'
+ char* new_pos_str;
+ long day;
+ if (!ParseLong(date_string, &new_pos_str, 10, &day))
+ return std::numeric_limits<double>::quiet_NaN();
+ date_string = new_pos_str;
+
+ if (!*date_string)
+ return std::numeric_limits<double>::quiet_NaN();
+
+ if (day < 0)
+ return std::numeric_limits<double>::quiet_NaN();
+
+ int year = 0;
+ if (day > 31) {
+ // ### where is the boundary and what happens below?
+ if (*date_string != '/')
+ return std::numeric_limits<double>::quiet_NaN();
+ // looks like a YYYY/MM/DD date
+ if (!*++date_string)
+ return std::numeric_limits<double>::quiet_NaN();
+ if (day <= std::numeric_limits<int>::min() ||
+ day >= std::numeric_limits<int>::max())
+ return std::numeric_limits<double>::quiet_NaN();
+ year = static_cast<int>(day);
+ if (!ParseLong(date_string, &new_pos_str, 10, &month))
+ return std::numeric_limits<double>::quiet_NaN();
+ month -= 1;
+ date_string = new_pos_str;
+ if (*date_string++ != '/' || !*date_string)
+ return std::numeric_limits<double>::quiet_NaN();
+ if (!ParseLong(date_string, &new_pos_str, 10, &day))
+ return std::numeric_limits<double>::quiet_NaN();
+ date_string = new_pos_str;
+ } else if (*date_string == '/' && month == -1) {
+ date_string++;
+ // This looks like a MM/DD/YYYY date, not an RFC date.
+ month = day - 1; // 0-based
+ if (!ParseLong(date_string, &new_pos_str, 10, &day))
+ return std::numeric_limits<double>::quiet_NaN();
+ if (day < 1 || day > 31)
+ return std::numeric_limits<double>::quiet_NaN();
+ date_string = new_pos_str;
+ if (*date_string == '/')
+ date_string++;
+ if (!*date_string)
+ return std::numeric_limits<double>::quiet_NaN();
+ } else {
+ if (*date_string == '-')
+ date_string++;
+
+ SkipSpacesAndComments(date_string);
+
+ if (*date_string == ',')
+ date_string++;
+
+ if (month == -1) { // not found yet
+ month = FindMonth(date_string);
+ if (month == -1)
+ return std::numeric_limits<double>::quiet_NaN();
+
+ while (*date_string && *date_string != '-' && *date_string != ',' &&
+ !IsASCIISpace(*date_string))
+ date_string++;
+
+ if (!*date_string)
+ return std::numeric_limits<double>::quiet_NaN();
+
+ // '-99 23:12:40 GMT'
+ if (*date_string != '-' && *date_string != '/' && *date_string != ',' &&
+ !IsASCIISpace(*date_string))
+ return std::numeric_limits<double>::quiet_NaN();
+ date_string++;
+ }
+ }
+
+ if (month < 0 || month > 11)
+ return std::numeric_limits<double>::quiet_NaN();
+
+ // '99 23:12:40 GMT'
+ if (year <= 0 && *date_string) {
+ if (!ParseInt(date_string, &new_pos_str, 10, &year))
+ return std::numeric_limits<double>::quiet_NaN();
+ }
+
+ // Don't fail if the time is missing.
+ long hour = 0;
+ long minute = 0;
+ long second = 0;
+ if (!*new_pos_str) {
+ date_string = new_pos_str;
+ } else {
+ // ' 23:12:40 GMT'
+ if (!(IsASCIISpace(*new_pos_str) || *new_pos_str == ',')) {
+ if (*new_pos_str != ':')
+ return std::numeric_limits<double>::quiet_NaN();
+ // There was no year; the number was the hour.
+ year = -1;
+ } else {
+ // in the normal case (we parsed the year), advance to the next number
+ date_string = ++new_pos_str;
+ SkipSpacesAndComments(date_string);
+ }
+
+ ParseLong(date_string, &new_pos_str, 10, &hour);
+ // Do not check for errno here since we want to continue
+ // even if errno was set becasue we are still looking
+ // for the timezone!
+
+ // Read a number? If not, this might be a timezone name.
+ if (new_pos_str != date_string) {
+ date_string = new_pos_str;
+
+ if (hour < 0 || hour > 23)
+ return std::numeric_limits<double>::quiet_NaN();
+
+ if (!*date_string)
+ return std::numeric_limits<double>::quiet_NaN();
+
+ // ':12:40 GMT'
+ if (*date_string++ != ':')
+ return std::numeric_limits<double>::quiet_NaN();
+
+ if (!ParseLong(date_string, &new_pos_str, 10, &minute))
+ return std::numeric_limits<double>::quiet_NaN();
+ date_string = new_pos_str;
+
+ if (minute < 0 || minute > 59)
+ return std::numeric_limits<double>::quiet_NaN();
+
+ // ':40 GMT'
+ if (*date_string && *date_string != ':' && !IsASCIISpace(*date_string))
+ return std::numeric_limits<double>::quiet_NaN();
+
+ // seconds are optional in rfc822 + rfc2822
+ if (*date_string == ':') {
+ date_string++;
+
+ if (!ParseLong(date_string, &new_pos_str, 10, &second))
+ return std::numeric_limits<double>::quiet_NaN();
+ date_string = new_pos_str;
+
+ if (second < 0 || second > 59)
+ return std::numeric_limits<double>::quiet_NaN();
+ }
+
+ SkipSpacesAndComments(date_string);
+
+ if (strncasecmp(date_string, "AM", 2) == 0) {
+ if (hour > 12)
+ return std::numeric_limits<double>::quiet_NaN();
+ if (hour == 12)
+ hour = 0;
+ date_string += 2;
+ SkipSpacesAndComments(date_string);
+ } else if (strncasecmp(date_string, "PM", 2) == 0) {
+ if (hour > 12)
+ return std::numeric_limits<double>::quiet_NaN();
+ if (hour != 12)
+ hour += 12;
+ date_string += 2;
+ SkipSpacesAndComments(date_string);
+ }
+ }
+ }
+
+ // The year may be after the time but before the time zone.
+ if (IsASCIIDigit(*date_string) && year == -1) {
+ if (!ParseInt(date_string, &new_pos_str, 10, &year))
+ return std::numeric_limits<double>::quiet_NaN();
+ date_string = new_pos_str;
+ SkipSpacesAndComments(date_string);
+ }
+
+ // Don't fail if the time zone is missing.
+ // Some websites omit the time zone (4275206).
+ if (*date_string) {
+ if (strncasecmp(date_string, "GMT", 3) == 0 ||
+ strncasecmp(date_string, "UTC", 3) == 0) {
+ date_string += 3;
+ have_tz = true;
+ }
+
+ if (*date_string == '+' || *date_string == '-') {
+ int o;
+ if (!ParseInt(date_string, &new_pos_str, 10, &o))
+ return std::numeric_limits<double>::quiet_NaN();
+ date_string = new_pos_str;
+
+ if (o < -9959 || o > 9959)
+ return std::numeric_limits<double>::quiet_NaN();
+
+ int sgn = (o < 0) ? -1 : 1;
+ o = abs(o);
+ if (*date_string != ':') {
+ if (o >= 24)
+ offset = ((o / 100) * 60 + (o % 100)) * sgn;
+ else
+ offset = o * 60 * sgn;
+ } else { // GMT+05:00
+ ++date_string; // skip the ':'
+ int o2;
+ if (!ParseInt(date_string, &new_pos_str, 10, &o2))
+ return std::numeric_limits<double>::quiet_NaN();
+ date_string = new_pos_str;
+ offset = (o * 60 + o2) * sgn;
+ }
+ have_tz = true;
+ } else {
+ for (size_t i = 0; i < WTF_ARRAY_LENGTH(known_zones); ++i) {
+ if (0 == strncasecmp(date_string, known_zones[i].tz_name,
+ strlen(known_zones[i].tz_name))) {
+ offset = known_zones[i].tz_offset;
+ date_string += strlen(known_zones[i].tz_name);
+ have_tz = true;
+ break;
+ }
+ }
+ }
+ }
+
+ SkipSpacesAndComments(date_string);
+
+ if (*date_string && year == -1) {
+ if (!ParseInt(date_string, &new_pos_str, 10, &year))
+ return std::numeric_limits<double>::quiet_NaN();
+ date_string = new_pos_str;
+ SkipSpacesAndComments(date_string);
+ }
+
+ // Trailing garbage
+ if (*date_string)
+ return std::numeric_limits<double>::quiet_NaN();
+
+ // Y2K: Handle 2 digit years.
+ if (year >= 0 && year < 100) {
+ if (year < 50)
+ year += 2000;
+ else
+ year += 1900;
+ }
+
+ return YmdhmsToSeconds(year, month + 1, day, hour, minute, second) *
+ kMsPerSecond;
+}
+
+double ParseDateFromNullTerminatedCharacters(const char* date_string) {
+ bool have_tz;
+ int offset;
+ double ms =
+ ParseDateFromNullTerminatedCharacters(date_string, have_tz, offset);
+ if (std::isnan(ms))
+ return std::numeric_limits<double>::quiet_NaN();
+
+ // fall back to local timezone
+ if (!have_tz) {
+ double utc_offset = CalculateUTCOffset();
+ double dst_offset = CalculateDSTOffset(ms, utc_offset);
+ offset = static_cast<int>((utc_offset + dst_offset) / kMsPerMinute);
+ }
+ return ms - (offset * kMsPerMinute);
+}
+
+// See http://tools.ietf.org/html/rfc2822#section-3.3 for more information.
+String MakeRFC2822DateString(unsigned day_of_week,
+ unsigned day,
+ unsigned month,
+ unsigned year,
+ unsigned hours,
+ unsigned minutes,
+ unsigned seconds,
+ int utc_offset) {
+ StringBuilder string_builder;
+ string_builder.Append(kWeekdayName[day_of_week]);
+ string_builder.Append(", ");
+ string_builder.AppendNumber(day);
+ string_builder.Append(' ');
+ string_builder.Append(kMonthName[month]);
+ string_builder.Append(' ');
+ string_builder.AppendNumber(year);
+ string_builder.Append(' ');
+
+ AppendTwoDigitNumber(string_builder, hours);
+ string_builder.Append(':');
+ AppendTwoDigitNumber(string_builder, minutes);
+ string_builder.Append(':');
+ AppendTwoDigitNumber(string_builder, seconds);
+ string_builder.Append(' ');
+
+ string_builder.Append(utc_offset > 0 ? '+' : '-');
+ int absolute_utc_offset = abs(utc_offset);
+ AppendTwoDigitNumber(string_builder, absolute_utc_offset / 60);
+ AppendTwoDigitNumber(string_builder, absolute_utc_offset % 60);
+
+ return string_builder.ToString();
+}
+
+double ConvertToLocalTime(double ms) {
+ double utc_offset = CalculateUTCOffset();
+ double dst_offset = CalculateDSTOffset(ms, utc_offset);
+ return (ms + utc_offset + dst_offset);
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/date_math.h b/chromium/third_party/blink/renderer/platform/wtf/date_math.h
new file mode 100644
index 00000000000..35e881eddf3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/date_math.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
+ * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ * Copyright (C) 2010 Research In Motion Limited. All rights reserved.
+ *
+ * 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 Mozilla Communicator client code, released
+ * March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DATE_MATH_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DATE_MATH_H_
+
+#include <stdint.h>
+#include <string.h>
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_export.h"
+
+namespace WTF {
+
+WTF_EXPORT void InitializeDates();
+
+// Not really math related, but this is currently the only shared place to put
+// these.
+WTF_EXPORT double ParseDateFromNullTerminatedCharacters(
+ const char* date_string);
+// dayOfWeek: [0, 6] 0 being Monday
+// day: [1, 31]
+// month: [0, 11]
+// year: ex: 2011
+// hours: [0, 23]
+// minutes: [0, 59]
+// seconds: [0, 59]
+// utcOffset: [-720,720].
+WTF_EXPORT String MakeRFC2822DateString(unsigned day_of_week,
+ unsigned day,
+ unsigned month,
+ unsigned year,
+ unsigned hours,
+ unsigned minutes,
+ unsigned seconds,
+ int utc_offset);
+
+const char kWeekdayName[7][4] = {"Mon", "Tue", "Wed", "Thu",
+ "Fri", "Sat", "Sun"};
+const char kMonthName[12][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+const char* const kMonthFullName[12] = {
+ "January", "February", "March", "April", "May", "June",
+ "July", "August", "September", "October", "November", "December"};
+
+const double kMinutesPerHour = 60.0;
+const double kSecondsPerMinute = 60.0;
+const double kMsPerSecond = 1000.0;
+const double kMsPerMinute = 60.0 * 1000.0;
+const double kMsPerHour = 60.0 * 60.0 * 1000.0;
+const double kMsPerDay = 24.0 * 60.0 * 60.0 * 1000.0;
+
+WTF_EXPORT bool IsLeapYear(int year);
+
+// Returns the number of days from 1970-01-01 to the specified date.
+WTF_EXPORT double DateToDaysFrom1970(int year, int month, int day);
+WTF_EXPORT int MsToYear(double ms);
+WTF_EXPORT int DayInYear(int year, int month, int day);
+WTF_EXPORT int DayInYear(double ms, int year);
+WTF_EXPORT int MonthFromDayInYear(int day_in_year, bool leap_year);
+WTF_EXPORT int DayInMonthFromDayInYear(int day_in_year, bool leap_year);
+
+// Returns milliseconds with UTC and DST.
+WTF_EXPORT double ConvertToLocalTime(double ms);
+
+} // namespace WTF
+
+using WTF::IsLeapYear;
+using WTF::DateToDaysFrom1970;
+using WTF::DayInMonthFromDayInYear;
+using WTF::DayInYear;
+using WTF::kMinutesPerHour;
+using WTF::MonthFromDayInYear;
+using WTF::kMsPerDay;
+using WTF::kMsPerHour;
+using WTF::kMsPerMinute;
+using WTF::kMsPerSecond;
+using WTF::MsToYear;
+using WTF::kSecondsPerMinute;
+using WTF::ParseDateFromNullTerminatedCharacters;
+using WTF::MakeRFC2822DateString;
+using WTF::ConvertToLocalTime;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DATE_MATH_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/deque.h b/chromium/third_party/blink/renderer/platform/wtf/deque.h
new file mode 100644
index 00000000000..b46b088e415
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/deque.h
@@ -0,0 +1,719 @@
+/*
+ * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DEQUE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DEQUE_H_
+
+// FIXME: Could move what Vector and Deque share into a separate file.
+// Deque doesn't actually use Vector.
+
+#include <iterator>
+
+#include "base/macros.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/construct_traits.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace WTF {
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+class DequeIteratorBase;
+template <typename T, size_t inlineCapacity, typename Allocator>
+class DequeIterator;
+template <typename T, size_t inlineCapacity, typename Allocator>
+class DequeConstIterator;
+
+template <typename T,
+ size_t inlineCapacity = 0,
+ typename Allocator = PartitionAllocator>
+class Deque : public ConditionalDestructor<Deque<T, INLINE_CAPACITY, Allocator>,
+ (INLINE_CAPACITY == 0) &&
+ Allocator::kIsGarbageCollected> {
+ USE_ALLOCATOR(Deque, Allocator);
+
+ public:
+ typedef DequeIterator<T, inlineCapacity, Allocator> iterator;
+ typedef DequeConstIterator<T, inlineCapacity, Allocator> const_iterator;
+ typedef std::reverse_iterator<iterator> reverse_iterator;
+ typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+
+ Deque();
+ Deque(const Deque&);
+ Deque& operator=(const Deque&);
+ Deque(Deque&&);
+ Deque& operator=(Deque&&);
+
+ void Finalize();
+ void FinalizeGarbageCollectedObject() { Finalize(); }
+
+ void Swap(Deque&);
+
+ size_t size() const {
+ return start_ <= end_ ? end_ - start_ : end_ + buffer_.capacity() - start_;
+ }
+ bool IsEmpty() const { return start_ == end_; }
+
+ iterator begin() { return iterator(this, start_); }
+ iterator end() { return iterator(this, end_); }
+ const_iterator begin() const { return const_iterator(this, start_); }
+ const_iterator end() const { return const_iterator(this, end_); }
+ reverse_iterator rbegin() { return reverse_iterator(end()); }
+ reverse_iterator rend() { return reverse_iterator(begin()); }
+ const_reverse_iterator rbegin() const {
+ return const_reverse_iterator(end());
+ }
+ const_reverse_iterator rend() const {
+ return const_reverse_iterator(begin());
+ }
+
+ T& front() {
+ DCHECK_NE(start_, end_);
+ return buffer_.Buffer()[start_];
+ }
+ const T& front() const {
+ DCHECK_NE(start_, end_);
+ return buffer_.Buffer()[start_];
+ }
+ T TakeFirst();
+
+ T& back() {
+ DCHECK_NE(start_, end_);
+ return *(--end());
+ }
+ const T& back() const {
+ DCHECK_NE(start_, end_);
+ return *(--end());
+ }
+ T TakeLast();
+
+ T& at(size_t i) {
+ CHECK_LT(i, size());
+ size_t right = buffer_.capacity() - start_;
+ return i < right ? buffer_.Buffer()[start_ + i]
+ : buffer_.Buffer()[i - right];
+ }
+ const T& at(size_t i) const {
+ CHECK_LT(i, size());
+ size_t right = buffer_.capacity() - start_;
+ return i < right ? buffer_.Buffer()[start_ + i]
+ : buffer_.Buffer()[i - right];
+ }
+
+ T& operator[](size_t i) { return at(i); }
+ const T& operator[](size_t i) const { return at(i); }
+
+ template <typename U>
+ void push_front(U&&);
+ void erase(iterator&);
+ void erase(const_iterator&);
+
+ // STL compatibility.
+ template <typename U>
+ void push_back(U&&);
+ void pop_back();
+ void pop_front();
+ bool empty() const { return IsEmpty(); }
+ template <typename... Args>
+ void emplace_back(Args&&...);
+ template <typename... Args>
+ void emplace_front(Args&&...);
+
+ void clear();
+
+ template <typename VisitorDispatcher, typename A = Allocator>
+ std::enable_if_t<A::kIsGarbageCollected> Trace(VisitorDispatcher);
+
+ static_assert(!std::is_polymorphic<T>::value ||
+ !VectorTraits<T>::kCanInitializeWithMemset,
+ "Cannot initialize with memset if there is a vtable");
+ static_assert(Allocator::kIsGarbageCollected ||
+ !AllowsOnlyPlacementNew<T>::value ||
+ !IsTraceable<T>::value,
+ "Cannot put DISALLOW_NEW_EXCEPT_PLACEMENT_NEW objects that "
+ "have trace methods into an off-heap Deque");
+ static_assert(Allocator::kIsGarbageCollected ||
+ !IsPointerToGarbageCollectedType<T>::value,
+ "Cannot put raw pointers to garbage-collected classes into a "
+ "Deque. Use HeapDeque<Member<T>> instead.");
+
+ private:
+ friend class DequeIteratorBase<T, inlineCapacity, Allocator>;
+
+ class BackingBuffer : public VectorBuffer<T, INLINE_CAPACITY, Allocator> {
+ private:
+ using Base = VectorBuffer<T, INLINE_CAPACITY, Allocator>;
+ using Base::size_;
+
+ public:
+ BackingBuffer() : Base() {}
+ explicit BackingBuffer(size_t capacity) : Base(capacity) {}
+
+ void SetSize(size_t size) { size_ = size; }
+
+ DISALLOW_COPY_AND_ASSIGN(BackingBuffer);
+ };
+
+ typedef VectorTypeOperations<T, Allocator> TypeOperations;
+ typedef DequeIteratorBase<T, inlineCapacity, Allocator> IteratorBase;
+
+ void erase(size_t position);
+ void DestroyAll();
+ void ExpandCapacityIfNeeded();
+ void ExpandCapacity();
+
+ BackingBuffer buffer_;
+ unsigned start_;
+ unsigned end_;
+};
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+class DequeIteratorBase {
+ DISALLOW_NEW();
+
+ protected:
+ DequeIteratorBase();
+ DequeIteratorBase(const Deque<T, inlineCapacity, Allocator>*, size_t);
+ DequeIteratorBase(const DequeIteratorBase&);
+ DequeIteratorBase& operator=(const DequeIteratorBase<T, 0, Allocator>&);
+ ~DequeIteratorBase();
+
+ void Assign(const DequeIteratorBase& other) { *this = other; }
+
+ void Increment();
+ void Decrement();
+
+ T* Before() const;
+ T* After() const;
+
+ bool IsEqual(const DequeIteratorBase&) const;
+
+ private:
+ Deque<T, inlineCapacity, Allocator>* deque_;
+ unsigned index_;
+
+ friend class Deque<T, inlineCapacity, Allocator>;
+};
+
+template <typename T,
+ size_t inlineCapacity = 0,
+ typename Allocator = PartitionAllocator>
+class DequeIterator : public DequeIteratorBase<T, inlineCapacity, Allocator> {
+ private:
+ typedef DequeIteratorBase<T, inlineCapacity, Allocator> Base;
+ typedef DequeIterator<T, inlineCapacity, Allocator> Iterator;
+
+ public:
+ typedef ptrdiff_t difference_type;
+ typedef T value_type;
+ typedef T* pointer;
+ typedef T& reference;
+ typedef std::bidirectional_iterator_tag iterator_category;
+
+ DequeIterator(Deque<T, inlineCapacity, Allocator>* deque, size_t index)
+ : Base(deque, index) {}
+
+ DequeIterator(const Iterator& other) : Base(other) {}
+ DequeIterator& operator=(const Iterator& other) {
+ Base::Assign(other);
+ return *this;
+ }
+
+ T& operator*() const { return *Base::After(); }
+ T* operator->() const { return Base::After(); }
+
+ bool operator==(const Iterator& other) const { return Base::IsEqual(other); }
+ bool operator!=(const Iterator& other) const { return !Base::IsEqual(other); }
+
+ Iterator& operator++() {
+ Base::Increment();
+ return *this;
+ }
+ // postfix ++ intentionally omitted
+ Iterator& operator--() {
+ Base::Decrement();
+ return *this;
+ }
+ // postfix -- intentionally omitted
+};
+
+template <typename T,
+ size_t inlineCapacity = 0,
+ typename Allocator = PartitionAllocator>
+class DequeConstIterator
+ : public DequeIteratorBase<T, inlineCapacity, Allocator> {
+ private:
+ typedef DequeIteratorBase<T, inlineCapacity, Allocator> Base;
+ typedef DequeConstIterator<T, inlineCapacity, Allocator> Iterator;
+ typedef DequeIterator<T, inlineCapacity, Allocator> NonConstIterator;
+
+ public:
+ typedef ptrdiff_t difference_type;
+ typedef T value_type;
+ typedef const T* pointer;
+ typedef const T& reference;
+ typedef std::bidirectional_iterator_tag iterator_category;
+
+ DequeConstIterator(const Deque<T, inlineCapacity, Allocator>* deque,
+ size_t index)
+ : Base(deque, index) {}
+
+ DequeConstIterator(const Iterator& other) : Base(other) {}
+ DequeConstIterator(const NonConstIterator& other) : Base(other) {}
+ DequeConstIterator& operator=(const Iterator& other) {
+ Base::Assign(other);
+ return *this;
+ }
+ DequeConstIterator& operator=(const NonConstIterator& other) {
+ Base::Assign(other);
+ return *this;
+ }
+
+ const T& operator*() const { return *Base::After(); }
+ const T* operator->() const { return Base::After(); }
+
+ bool operator==(const Iterator& other) const { return Base::IsEqual(other); }
+ bool operator!=(const Iterator& other) const { return !Base::IsEqual(other); }
+
+ Iterator& operator++() {
+ Base::Increment();
+ return *this;
+ }
+ // postfix ++ intentionally omitted
+ Iterator& operator--() {
+ Base::Decrement();
+ return *this;
+ }
+ // postfix -- intentionally omitted
+};
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline Deque<T, inlineCapacity, Allocator>::Deque() : start_(0), end_(0) {}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline Deque<T, inlineCapacity, Allocator>::Deque(const Deque& other)
+ : buffer_(other.buffer_.capacity()),
+ start_(other.start_),
+ end_(other.end_) {
+ const T* other_buffer = other.buffer_.Buffer();
+ if (start_ <= end_) {
+ TypeOperations::UninitializedCopy(
+ other_buffer + start_, other_buffer + end_, buffer_.Buffer() + start_);
+ } else {
+ TypeOperations::UninitializedCopy(other_buffer, other_buffer + end_,
+ buffer_.Buffer());
+ TypeOperations::UninitializedCopy(other_buffer + start_,
+ other_buffer + buffer_.capacity(),
+ buffer_.Buffer() + start_);
+ }
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline Deque<T, inlineCapacity, Allocator>&
+Deque<T, inlineCapacity, Allocator>::operator=(const Deque& other) {
+ Deque<T> copy(other);
+ Swap(copy);
+ return *this;
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline Deque<T, inlineCapacity, Allocator>::Deque(Deque&& other)
+ : start_(0), end_(0) {
+ Swap(other);
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline Deque<T, inlineCapacity, Allocator>&
+Deque<T, inlineCapacity, Allocator>::operator=(Deque&& other) {
+ Swap(other);
+ return *this;
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline void Deque<T, inlineCapacity, Allocator>::DestroyAll() {
+ if (start_ <= end_) {
+ TypeOperations::Destruct(buffer_.Buffer() + start_,
+ buffer_.Buffer() + end_);
+ buffer_.ClearUnusedSlots(buffer_.Buffer() + start_,
+ buffer_.Buffer() + end_);
+ } else {
+ TypeOperations::Destruct(buffer_.Buffer(), buffer_.Buffer() + end_);
+ buffer_.ClearUnusedSlots(buffer_.Buffer(), buffer_.Buffer() + end_);
+ TypeOperations::Destruct(buffer_.Buffer() + start_,
+ buffer_.Buffer() + buffer_.capacity());
+ buffer_.ClearUnusedSlots(buffer_.Buffer() + start_,
+ buffer_.Buffer() + buffer_.capacity());
+ }
+}
+
+// Off-GC-heap deques: Destructor should be called.
+// On-GC-heap deques: Destructor should be called for inline buffers (if any)
+// but destructor shouldn't be called for vector backing since it is managed by
+// the traced GC heap.
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline void Deque<T, inlineCapacity, Allocator>::Finalize() {
+ if (!INLINE_CAPACITY && !buffer_.Buffer())
+ return;
+ if (!IsEmpty() &&
+ !(Allocator::kIsGarbageCollected && buffer_.HasOutOfLineBuffer()))
+ DestroyAll();
+
+ buffer_.Destruct();
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline void Deque<T, inlineCapacity, Allocator>::Swap(Deque& other) {
+ typename BackingBuffer::OffsetRange this_hole;
+ if (start_ <= end_) {
+ buffer_.SetSize(end_);
+ this_hole.begin = 0;
+ this_hole.end = start_;
+ } else {
+ buffer_.SetSize(buffer_.capacity());
+ this_hole.begin = end_;
+ this_hole.end = start_;
+ }
+ typename BackingBuffer::OffsetRange other_hole;
+ if (other.start_ <= other.end_) {
+ other.buffer_.SetSize(other.end_);
+ other_hole.begin = 0;
+ other_hole.end = other.start_;
+ } else {
+ other.buffer_.SetSize(other.buffer_.capacity());
+ other_hole.begin = other.end_;
+ other_hole.end = other.start_;
+ }
+
+ buffer_.SwapVectorBuffer(other.buffer_, this_hole, other_hole);
+
+ std::swap(start_, other.start_);
+ std::swap(end_, other.end_);
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline void Deque<T, inlineCapacity, Allocator>::clear() {
+ DestroyAll();
+ start_ = 0;
+ end_ = 0;
+ buffer_.DeallocateBuffer(buffer_.Buffer());
+ buffer_.ResetBufferPointer();
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline void Deque<T, inlineCapacity, Allocator>::ExpandCapacityIfNeeded() {
+ if (start_) {
+ if (end_ + 1 != start_)
+ return;
+ } else if (end_) {
+ if (end_ != buffer_.capacity() - 1)
+ return;
+ } else if (buffer_.capacity()) {
+ return;
+ }
+
+ ExpandCapacity();
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+void Deque<T, inlineCapacity, Allocator>::ExpandCapacity() {
+ size_t old_capacity = buffer_.capacity();
+ T* old_buffer = buffer_.Buffer();
+ size_t new_capacity =
+ std::max(static_cast<size_t>(16), old_capacity + old_capacity / 4 + 1);
+ if (buffer_.ExpandBuffer(new_capacity)) {
+ if (start_ <= end_) {
+ // No adjustments to be done.
+ } else {
+ size_t new_start = buffer_.capacity() - (old_capacity - start_);
+ TypeOperations::MoveOverlapping(old_buffer + start_,
+ old_buffer + old_capacity,
+ buffer_.Buffer() + new_start);
+ buffer_.ClearUnusedSlots(old_buffer + start_,
+ old_buffer + std::min(old_capacity, new_start));
+ start_ = new_start;
+ }
+ return;
+ }
+ buffer_.AllocateBuffer(new_capacity);
+ if (start_ <= end_) {
+ TypeOperations::Move(old_buffer + start_, old_buffer + end_,
+ buffer_.Buffer() + start_);
+ buffer_.ClearUnusedSlots(old_buffer + start_, old_buffer + end_);
+ } else {
+ TypeOperations::Move(old_buffer, old_buffer + end_, buffer_.Buffer());
+ buffer_.ClearUnusedSlots(old_buffer, old_buffer + end_);
+ size_t new_start = buffer_.capacity() - (old_capacity - start_);
+ TypeOperations::Move(old_buffer + start_, old_buffer + old_capacity,
+ buffer_.Buffer() + new_start);
+ buffer_.ClearUnusedSlots(old_buffer + start_, old_buffer + old_capacity);
+ start_ = new_start;
+ }
+ buffer_.DeallocateBuffer(old_buffer);
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline T Deque<T, inlineCapacity, Allocator>::TakeFirst() {
+ T old_first = std::move(front());
+ pop_front();
+ return old_first;
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline T Deque<T, inlineCapacity, Allocator>::TakeLast() {
+ T old_last = std::move(back());
+ pop_back();
+ return old_last;
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+template <typename U>
+inline void Deque<T, inlineCapacity, Allocator>::push_back(U&& value) {
+ ExpandCapacityIfNeeded();
+ T* new_element = &buffer_.Buffer()[end_];
+ if (end_ == buffer_.capacity() - 1)
+ end_ = 0;
+ else
+ ++end_;
+ ConstructTraits<T, VectorTraits<T>, Allocator>::ConstructAndNotifyElement(
+ new_element, std::forward<U>(value));
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+template <typename U>
+inline void Deque<T, inlineCapacity, Allocator>::push_front(U&& value) {
+ ExpandCapacityIfNeeded();
+ if (!start_)
+ start_ = buffer_.capacity() - 1;
+ else
+ --start_;
+ ConstructTraits<T, VectorTraits<T>, Allocator>::ConstructAndNotifyElement(
+ &buffer_.Buffer()[start_], std::forward<U>(value));
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+template <typename... Args>
+inline void Deque<T, inlineCapacity, Allocator>::emplace_back(Args&&... args) {
+ ExpandCapacityIfNeeded();
+ T* new_element = &buffer_.Buffer()[end_];
+ if (end_ == buffer_.capacity() - 1)
+ end_ = 0;
+ else
+ ++end_;
+ ConstructTraits<T, VectorTraits<T>, Allocator>::ConstructAndNotifyElement(
+ new_element, std::forward<Args>(args)...);
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+template <typename... Args>
+inline void Deque<T, inlineCapacity, Allocator>::emplace_front(Args&&... args) {
+ ExpandCapacityIfNeeded();
+ if (!start_)
+ start_ = buffer_.capacity() - 1;
+ else
+ --start_;
+ ConstructTraits<T, VectorTraits<T>, Allocator>::ConstructAndNotifyElement(
+ &buffer_.Buffer()[start_], std::forward<Args>(args)...);
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline void Deque<T, inlineCapacity, Allocator>::pop_front() {
+ DCHECK(!IsEmpty());
+ TypeOperations::Destruct(&buffer_.Buffer()[start_],
+ &buffer_.Buffer()[start_ + 1]);
+ buffer_.ClearUnusedSlots(&buffer_.Buffer()[start_],
+ &buffer_.Buffer()[start_ + 1]);
+ if (start_ == buffer_.capacity() - 1)
+ start_ = 0;
+ else
+ ++start_;
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline void Deque<T, inlineCapacity, Allocator>::pop_back() {
+ DCHECK(!IsEmpty());
+ if (!end_)
+ end_ = buffer_.capacity() - 1;
+ else
+ --end_;
+ TypeOperations::Destruct(&buffer_.Buffer()[end_],
+ &buffer_.Buffer()[end_ + 1]);
+ buffer_.ClearUnusedSlots(&buffer_.Buffer()[end_],
+ &buffer_.Buffer()[end_ + 1]);
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline void Deque<T, inlineCapacity, Allocator>::erase(iterator& it) {
+ erase(it.index_);
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline void Deque<T, inlineCapacity, Allocator>::erase(const_iterator& it) {
+ erase(it.index_);
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline void Deque<T, inlineCapacity, Allocator>::erase(size_t position) {
+ if (position == end_)
+ return;
+
+ T* buffer = buffer_.Buffer();
+ TypeOperations::Destruct(&buffer[position], &buffer[position + 1]);
+
+ // Find which segment of the circular buffer contained the remove element,
+ // and only move elements in that part.
+ if (position >= start_) {
+ TypeOperations::MoveOverlapping(buffer + start_, buffer + position,
+ buffer + start_ + 1);
+ buffer_.ClearUnusedSlots(buffer + start_, buffer + start_ + 1);
+ start_ = (start_ + 1) % buffer_.capacity();
+ } else {
+ TypeOperations::MoveOverlapping(buffer + position + 1, buffer + end_,
+ buffer + position);
+ buffer_.ClearUnusedSlots(buffer + end_ - 1, buffer + end_);
+ end_ = (end_ - 1 + buffer_.capacity()) % buffer_.capacity();
+ }
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline DequeIteratorBase<T, inlineCapacity, Allocator>::DequeIteratorBase()
+ : deque_(0) {}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline DequeIteratorBase<T, inlineCapacity, Allocator>::DequeIteratorBase(
+ const Deque<T, inlineCapacity, Allocator>* deque,
+ size_t index)
+ : deque_(const_cast<Deque<T, inlineCapacity, Allocator>*>(deque)),
+ index_(index) {}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline DequeIteratorBase<T, inlineCapacity, Allocator>::DequeIteratorBase(
+ const DequeIteratorBase& other)
+ : deque_(other.deque_), index_(other.index_) {}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline DequeIteratorBase<T, inlineCapacity, Allocator>&
+DequeIteratorBase<T, inlineCapacity, Allocator>::operator=(
+ const DequeIteratorBase<T, 0, Allocator>& other) {
+ deque_ = other.deque_;
+ index_ = other.index_;
+ return *this;
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline DequeIteratorBase<T, inlineCapacity, Allocator>::~DequeIteratorBase() =
+ default;
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline bool DequeIteratorBase<T, inlineCapacity, Allocator>::IsEqual(
+ const DequeIteratorBase& other) const {
+ return index_ == other.index_;
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline void DequeIteratorBase<T, inlineCapacity, Allocator>::Increment() {
+ DCHECK_NE(index_, deque_->end_);
+ DCHECK(deque_->buffer_.capacity());
+ if (index_ == deque_->buffer_.capacity() - 1)
+ index_ = 0;
+ else
+ ++index_;
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline void DequeIteratorBase<T, inlineCapacity, Allocator>::Decrement() {
+ DCHECK_NE(index_, deque_->start_);
+ DCHECK(deque_->buffer_.capacity());
+ if (!index_)
+ index_ = deque_->buffer_.capacity() - 1;
+ else
+ --index_;
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline T* DequeIteratorBase<T, inlineCapacity, Allocator>::After() const {
+ CHECK_NE(index_, deque_->end_);
+ return &deque_->buffer_.Buffer()[index_];
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline T* DequeIteratorBase<T, inlineCapacity, Allocator>::Before() const {
+ CHECK_NE(index_, deque_->start_);
+ if (!index_)
+ return &deque_->buffer_.buffer()[deque_->buffer_.capacity() - 1];
+ return &deque_->buffer_.buffer()[index_ - 1];
+}
+
+// This is only defined if the allocator is a HeapAllocator. It is used when
+// visiting during a tracing GC.
+template <typename T, size_t inlineCapacity, typename Allocator>
+template <typename VisitorDispatcher, typename A>
+std::enable_if_t<A::kIsGarbageCollected>
+Deque<T, inlineCapacity, Allocator>::Trace(VisitorDispatcher visitor) {
+ static_assert(Allocator::kIsGarbageCollected,
+ "Garbage collector must be enabled.");
+ if (buffer_.HasOutOfLineBuffer()) {
+ Allocator::TraceVectorBacking(visitor, buffer_.Buffer(),
+ buffer_.BufferSlot());
+ } else {
+ const T* buffer_begin = buffer_.Buffer();
+ const T* end = buffer_begin + end_;
+ if (IsTraceableInCollectionTrait<VectorTraits<T>>::value) {
+ if (start_ <= end_) {
+ for (const T* buffer_entry = buffer_begin + start_; buffer_entry != end;
+ buffer_entry++) {
+ Allocator::template Trace<VisitorDispatcher, T, VectorTraits<T>>(
+ visitor, *const_cast<T*>(buffer_entry));
+ }
+ } else {
+ for (const T* buffer_entry = buffer_begin; buffer_entry != end;
+ buffer_entry++) {
+ Allocator::template Trace<VisitorDispatcher, T, VectorTraits<T>>(
+ visitor, *const_cast<T*>(buffer_entry));
+ }
+ const T* buffer_end = buffer_.Buffer() + buffer_.capacity();
+ for (const T* buffer_entry = buffer_begin + start_;
+ buffer_entry != buffer_end; buffer_entry++) {
+ Allocator::template Trace<VisitorDispatcher, T, VectorTraits<T>>(
+ visitor, *const_cast<T*>(buffer_entry));
+ }
+ }
+ }
+ }
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline void swap(Deque<T, inlineCapacity, Allocator>& a,
+ Deque<T, inlineCapacity, Allocator>& b) {
+ a.Swap(b);
+}
+
+} // namespace WTF
+
+using WTF::Deque;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DEQUE_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/deque_test.cc b/chromium/third_party/blink/renderer/platform/wtf/deque_test.cc
new file mode 100644
index 00000000000..4b7cc4627da
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/deque_test.cc
@@ -0,0 +1,548 @@
+/*
+ * Copyright (C) 2011 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/wtf/deque.h"
+
+#include <memory>
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_test_helper.h"
+
+namespace WTF {
+
+namespace {
+
+TEST(DequeTest, Basic) {
+ Deque<int> int_deque;
+ EXPECT_TRUE(int_deque.IsEmpty());
+ EXPECT_EQ(0ul, int_deque.size());
+}
+
+template <size_t inlineCapacity>
+void CheckNumberSequence(Deque<int, inlineCapacity>& deque,
+ int from,
+ int to,
+ bool increment) {
+ auto it = increment ? deque.begin() : deque.end();
+ size_t index = increment ? 0 : deque.size();
+ int step = from < to ? 1 : -1;
+ for (int i = from; i != to + step; i += step) {
+ if (!increment) {
+ --it;
+ --index;
+ }
+
+ EXPECT_EQ(i, *it);
+ EXPECT_EQ(i, deque[index]);
+
+ if (increment) {
+ ++it;
+ ++index;
+ }
+ }
+ EXPECT_EQ(increment ? deque.end() : deque.begin(), it);
+ EXPECT_EQ(increment ? deque.size() : 0, index);
+}
+
+template <size_t inlineCapacity>
+void CheckNumberSequenceReverse(Deque<int, inlineCapacity>& deque,
+ int from,
+ int to,
+ bool increment) {
+ auto it = increment ? deque.rbegin() : deque.rend();
+ size_t index = increment ? 0 : deque.size();
+ int step = from < to ? 1 : -1;
+ for (int i = from; i != to + step; i += step) {
+ if (!increment) {
+ --it;
+ --index;
+ }
+
+ EXPECT_EQ(i, *it);
+ EXPECT_EQ(i, deque.at(deque.size() - 1 - index));
+
+ if (increment) {
+ ++it;
+ ++index;
+ }
+ }
+ EXPECT_EQ(increment ? deque.rend() : deque.rbegin(), it);
+ EXPECT_EQ(increment ? deque.size() : 0, index);
+}
+
+template <size_t inlineCapacity>
+void ReverseTest() {
+ Deque<int, inlineCapacity> int_deque;
+ int_deque.push_back(10);
+ int_deque.push_back(11);
+ int_deque.push_back(12);
+ int_deque.push_back(13);
+
+ CheckNumberSequence(int_deque, 10, 13, true);
+ CheckNumberSequence(int_deque, 13, 10, false);
+ CheckNumberSequenceReverse(int_deque, 13, 10, true);
+ CheckNumberSequenceReverse(int_deque, 10, 13, false);
+
+ int_deque.push_back(14);
+ int_deque.push_back(15);
+ EXPECT_EQ(10, int_deque.TakeFirst());
+ EXPECT_EQ(15, int_deque.TakeLast());
+ CheckNumberSequence(int_deque, 11, 14, true);
+ CheckNumberSequence(int_deque, 14, 11, false);
+ CheckNumberSequenceReverse(int_deque, 14, 11, true);
+ CheckNumberSequenceReverse(int_deque, 11, 14, false);
+
+ for (int i = 15; i < 200; ++i)
+ int_deque.push_back(i);
+ CheckNumberSequence(int_deque, 11, 199, true);
+ CheckNumberSequence(int_deque, 199, 11, false);
+ CheckNumberSequenceReverse(int_deque, 199, 11, true);
+ CheckNumberSequenceReverse(int_deque, 11, 199, false);
+
+ for (int i = 0; i < 180; ++i) {
+ EXPECT_EQ(i + 11, int_deque[0]);
+ EXPECT_EQ(i + 11, int_deque.TakeFirst());
+ }
+ CheckNumberSequence(int_deque, 191, 199, true);
+ CheckNumberSequence(int_deque, 199, 191, false);
+ CheckNumberSequenceReverse(int_deque, 199, 191, true);
+ CheckNumberSequenceReverse(int_deque, 191, 199, false);
+
+ Deque<int, inlineCapacity> int_deque2;
+ swap(int_deque, int_deque2);
+
+ CheckNumberSequence(int_deque2, 191, 199, true);
+ CheckNumberSequence(int_deque2, 199, 191, false);
+ CheckNumberSequenceReverse(int_deque2, 199, 191, true);
+ CheckNumberSequenceReverse(int_deque2, 191, 199, false);
+
+ int_deque.Swap(int_deque2);
+
+ CheckNumberSequence(int_deque, 191, 199, true);
+ CheckNumberSequence(int_deque, 199, 191, false);
+ CheckNumberSequenceReverse(int_deque, 199, 191, true);
+ CheckNumberSequenceReverse(int_deque, 191, 199, false);
+
+ int_deque.Swap(int_deque2);
+
+ CheckNumberSequence(int_deque2, 191, 199, true);
+ CheckNumberSequence(int_deque2, 199, 191, false);
+ CheckNumberSequenceReverse(int_deque2, 199, 191, true);
+ CheckNumberSequenceReverse(int_deque2, 191, 199, false);
+}
+
+TEST(DequeTest, Reverse) {
+ ReverseTest<0>();
+ ReverseTest<2>();
+}
+
+template <typename OwnPtrDeque>
+void OwnPtrTest() {
+ int destruct_number = 0;
+ OwnPtrDeque deque;
+ deque.push_back(std::make_unique<DestructCounter>(0, &destruct_number));
+ deque.push_back(std::make_unique<DestructCounter>(1, &destruct_number));
+ EXPECT_EQ(2u, deque.size());
+
+ std::unique_ptr<DestructCounter>& counter0 = deque.front();
+ EXPECT_EQ(0, counter0->Get());
+ int counter1 = deque.back()->Get();
+ EXPECT_EQ(1, counter1);
+ EXPECT_EQ(0, destruct_number);
+
+ size_t index = 0;
+ for (auto iter = deque.begin(); iter != deque.end(); ++iter) {
+ std::unique_ptr<DestructCounter>& ref_counter = *iter;
+ EXPECT_EQ(index, static_cast<size_t>(ref_counter->Get()));
+ EXPECT_EQ(index, static_cast<size_t>((*ref_counter).Get()));
+ index++;
+ }
+ EXPECT_EQ(0, destruct_number);
+
+ auto it = deque.begin();
+ for (index = 0; index < deque.size(); ++index) {
+ std::unique_ptr<DestructCounter>& ref_counter = *it;
+ EXPECT_EQ(index, static_cast<size_t>(ref_counter->Get()));
+ index++;
+ ++it;
+ }
+ EXPECT_EQ(0, destruct_number);
+
+ EXPECT_EQ(0, deque.front()->Get());
+ deque.pop_front();
+ EXPECT_EQ(1, deque.front()->Get());
+ EXPECT_EQ(1u, deque.size());
+ EXPECT_EQ(1, destruct_number);
+
+ std::unique_ptr<DestructCounter> own_counter1 = std::move(deque.front());
+ deque.pop_front();
+ EXPECT_EQ(counter1, own_counter1->Get());
+ EXPECT_EQ(0u, deque.size());
+ EXPECT_EQ(1, destruct_number);
+
+ own_counter1.reset();
+ EXPECT_EQ(2, destruct_number);
+
+ size_t count = 1025;
+ destruct_number = 0;
+ for (size_t i = 0; i < count; ++i)
+ deque.push_front(std::make_unique<DestructCounter>(i, &destruct_number));
+
+ // Deque relocation must not destruct std::unique_ptr element.
+ EXPECT_EQ(0, destruct_number);
+ EXPECT_EQ(count, deque.size());
+
+ OwnPtrDeque copy_deque;
+ deque.Swap(copy_deque);
+ EXPECT_EQ(0, destruct_number);
+ EXPECT_EQ(count, copy_deque.size());
+ EXPECT_EQ(0u, deque.size());
+
+ copy_deque.clear();
+ EXPECT_EQ(count, static_cast<size_t>(destruct_number));
+}
+
+TEST(DequeTest, OwnPtr) {
+ OwnPtrTest<Deque<std::unique_ptr<DestructCounter>>>();
+ OwnPtrTest<Deque<std::unique_ptr<DestructCounter>, 2>>();
+}
+
+TEST(DequeTest, MoveOnlyType) {
+ Deque<MoveOnly> deque;
+ deque.push_back(MoveOnly(1));
+ deque.push_back(MoveOnly(2));
+ EXPECT_EQ(2u, deque.size());
+
+ ASSERT_EQ(1, deque.front().Value());
+ ASSERT_EQ(2, deque.back().Value());
+
+ MoveOnly old_first = deque.TakeFirst();
+ ASSERT_EQ(1, old_first.Value());
+ EXPECT_EQ(1u, deque.size());
+
+ Deque<MoveOnly> other_deque;
+ deque.Swap(other_deque);
+ EXPECT_EQ(1u, other_deque.size());
+ EXPECT_EQ(0u, deque.size());
+}
+
+HashSet<void*> g_constructed_wrapped_ints;
+
+template <size_t inlineCapacity>
+void SwapWithOrWithoutInlineCapacity() {
+ Deque<WrappedInt, inlineCapacity> deque_a;
+ deque_a.push_back(WrappedInt(1));
+ Deque<WrappedInt, inlineCapacity> deque_b;
+ deque_b.push_back(WrappedInt(2));
+
+ ASSERT_EQ(deque_a.size(), deque_b.size());
+ deque_a.Swap(deque_b);
+
+ ASSERT_EQ(1u, deque_a.size());
+ EXPECT_EQ(2, deque_a.front().Get());
+ ASSERT_EQ(1u, deque_b.size());
+ EXPECT_EQ(1, deque_b.front().Get());
+
+ deque_a.push_back(WrappedInt(3));
+
+ ASSERT_GT(deque_a.size(), deque_b.size());
+ deque_a.Swap(deque_b);
+
+ ASSERT_EQ(1u, deque_a.size());
+ EXPECT_EQ(1, deque_a.front().Get());
+ ASSERT_EQ(2u, deque_b.size());
+ EXPECT_EQ(2, deque_b.front().Get());
+
+ ASSERT_LT(deque_a.size(), deque_b.size());
+ deque_a.Swap(deque_b);
+
+ ASSERT_EQ(2u, deque_a.size());
+ EXPECT_EQ(2, deque_a.front().Get());
+ ASSERT_EQ(1u, deque_b.size());
+ EXPECT_EQ(1, deque_b.front().Get());
+
+ deque_a.push_back(WrappedInt(4));
+ deque_a.Swap(deque_b);
+
+ ASSERT_EQ(1u, deque_a.size());
+ EXPECT_EQ(1, deque_a.front().Get());
+ ASSERT_EQ(3u, deque_b.size());
+ EXPECT_EQ(2, deque_b.front().Get());
+
+ deque_b.Swap(deque_a);
+}
+
+TEST(DequeTest, SwapWithOrWithoutInlineCapacity) {
+ SwapWithOrWithoutInlineCapacity<0>();
+ SwapWithOrWithoutInlineCapacity<2>();
+}
+
+// Filter a few numbers out to improve the running speed of the tests. The
+// test has nested loops, and removing even numbers from 4 and up from the
+// loops makes it run 10 times faster.
+bool InterestingNumber(int i) {
+ return i < 4 || (i & 1);
+}
+
+template <size_t inlineCapacity>
+void TestDequeDestructorAndConstructorCallsWhenSwappingWithInlineCapacity() {
+ LivenessCounter::live_ = 0;
+ LivenessCounter counter;
+ EXPECT_EQ(0u, LivenessCounter::live_);
+
+ Deque<scoped_refptr<LivenessCounter>, inlineCapacity> deque;
+ Deque<scoped_refptr<LivenessCounter>, inlineCapacity> deque2;
+ deque.push_back(&counter);
+ deque2.push_back(&counter);
+ EXPECT_EQ(2u, LivenessCounter::live_);
+
+ // Add various numbers of elements to deques, then remove various numbers
+ // of elements from the head. This creates in-use ranges in the backing
+ // that sometimes wrap around the end of the buffer, testing various ways
+ // in which the in-use ranges of the inline buffers can overlap when we
+ // call swap().
+ for (unsigned i = 0; i < 12; i++) {
+ if (!InterestingNumber(i))
+ continue;
+ for (unsigned j = i; j < 12; j++) {
+ if (!InterestingNumber(j))
+ continue;
+ deque.clear();
+ deque2.clear();
+ EXPECT_EQ(0u, LivenessCounter::live_);
+ for (unsigned k = 0; k < j; k++)
+ deque.push_back(&counter);
+ EXPECT_EQ(j, LivenessCounter::live_);
+ EXPECT_EQ(j, deque.size());
+ for (unsigned k = 0; k < i; k++)
+ deque.pop_front();
+
+ EXPECT_EQ(j - i, LivenessCounter::live_);
+ EXPECT_EQ(j - i, deque.size());
+ deque.Swap(deque2);
+ EXPECT_EQ(j - i, LivenessCounter::live_);
+ EXPECT_EQ(0u, deque.size());
+ EXPECT_EQ(j - i, deque2.size());
+ deque.Swap(deque2);
+ EXPECT_EQ(j - i, LivenessCounter::live_);
+
+ deque2.push_back(&counter);
+ deque2.push_back(&counter);
+ deque2.push_back(&counter);
+
+ for (unsigned k = 0; k < 12; k++) {
+ EXPECT_EQ(3 + j - i, LivenessCounter::live_);
+ EXPECT_EQ(j - i, deque.size());
+ EXPECT_EQ(3u, deque2.size());
+ deque.Swap(deque2);
+ EXPECT_EQ(3 + j - i, LivenessCounter::live_);
+ EXPECT_EQ(j - i, deque2.size());
+ EXPECT_EQ(3u, deque.size());
+ deque.Swap(deque2);
+ EXPECT_EQ(3 + j - i, LivenessCounter::live_);
+ EXPECT_EQ(j - i, deque.size());
+ EXPECT_EQ(3u, deque2.size());
+
+ deque2.pop_front();
+ deque2.push_back(&counter);
+ }
+ }
+ }
+}
+
+TEST(DequeTest, SwapWithConstructorsAndDestructors) {
+ TestDequeDestructorAndConstructorCallsWhenSwappingWithInlineCapacity<0>();
+ TestDequeDestructorAndConstructorCallsWhenSwappingWithInlineCapacity<4>();
+ TestDequeDestructorAndConstructorCallsWhenSwappingWithInlineCapacity<9>();
+}
+
+template <size_t inlineCapacity>
+void TestDequeValuesMovedAndSwappedWithInlineCapacity() {
+ Deque<unsigned, inlineCapacity> deque;
+ Deque<unsigned, inlineCapacity> deque2;
+
+ // Add various numbers of elements to deques, then remove various numbers
+ // of elements from the head. This creates in-use ranges in the backing
+ // that sometimes wrap around the end of the buffer, testing various ways
+ // in which the in-use ranges of the inline buffers can overlap when we
+ // call swap().
+ for (unsigned pad = 0; pad < 12; pad++) {
+ if (!InterestingNumber(pad))
+ continue;
+ for (unsigned pad2 = 0; pad2 < 12; pad2++) {
+ if (!InterestingNumber(pad2))
+ continue;
+ for (unsigned size = 0; size < 12; size++) {
+ if (!InterestingNumber(size))
+ continue;
+ for (unsigned size2 = 0; size2 < 12; size2++) {
+ if (!InterestingNumber(size2))
+ continue;
+ deque.clear();
+ deque2.clear();
+ for (unsigned i = 0; i < pad; i++)
+ deque.push_back(103);
+ for (unsigned i = 0; i < pad2; i++)
+ deque2.push_back(888);
+ for (unsigned i = 0; i < size; i++)
+ deque.push_back(i);
+ for (unsigned i = 0; i < size2; i++)
+ deque2.push_back(i + 42);
+ for (unsigned i = 0; i < pad; i++)
+ EXPECT_EQ(103u, deque.TakeFirst());
+ for (unsigned i = 0; i < pad2; i++)
+ EXPECT_EQ(888u, deque2.TakeFirst());
+ EXPECT_EQ(size, deque.size());
+ EXPECT_EQ(size2, deque2.size());
+ deque.Swap(deque2);
+ for (unsigned i = 0; i < size; i++)
+ EXPECT_EQ(i, deque2.TakeFirst());
+ for (unsigned i = 0; i < size2; i++)
+ EXPECT_EQ(i + 42, deque.TakeFirst());
+ }
+ }
+ }
+ }
+}
+
+TEST(DequeTest, ValuesMovedAndSwappedWithInlineCapacity) {
+ TestDequeValuesMovedAndSwappedWithInlineCapacity<0>();
+ TestDequeValuesMovedAndSwappedWithInlineCapacity<4>();
+ TestDequeValuesMovedAndSwappedWithInlineCapacity<9>();
+}
+
+TEST(DequeTest, UniquePtr) {
+ using Pointer = std::unique_ptr<int>;
+ Deque<Pointer> deque;
+ deque.push_back(Pointer(new int(1)));
+ deque.push_back(Pointer(new int(2)));
+ deque.push_front(Pointer(new int(-1)));
+ deque.push_front(Pointer(new int(-2)));
+ ASSERT_EQ(4u, deque.size());
+ EXPECT_EQ(-2, *deque[0]);
+ EXPECT_EQ(-1, *deque[1]);
+ EXPECT_EQ(1, *deque[2]);
+ EXPECT_EQ(2, *deque[3]);
+
+ Pointer first(deque.TakeFirst());
+ EXPECT_EQ(-2, *first);
+ Pointer last(deque.TakeLast());
+ EXPECT_EQ(2, *last);
+
+ EXPECT_EQ(2u, deque.size());
+ deque.pop_front();
+ deque.pop_back();
+ EXPECT_EQ(0u, deque.size());
+
+ deque.push_back(Pointer(new int(42)));
+ deque[0] = Pointer(new int(24));
+ ASSERT_EQ(1u, deque.size());
+ EXPECT_EQ(24, *deque[0]);
+
+ deque.clear();
+}
+
+TEST(DequeTest, MoveShouldNotMakeCopy) {
+ // Because data in inline buffer may be swapped or moved individually, we
+ // force the creation of out-of-line buffer so we can make sure there's no
+ // element-wise copy/move.
+ Deque<CountCopy, 1> deque;
+ int counter = 0;
+ deque.push_back(CountCopy(&counter));
+ deque.push_back(CountCopy(&counter));
+
+ Deque<CountCopy, 1> other(deque);
+ counter = 0;
+ deque = std::move(other); // Move assignment.
+ EXPECT_EQ(0, counter);
+
+ counter = 0;
+ Deque<CountCopy, 1> yet_another(std::move(deque)); // Move construction.
+ EXPECT_EQ(0, counter);
+}
+
+TEST(DequeTest, RemoveWhileIterating) {
+ Deque<int> deque;
+ for (int i = 0; i < 10; ++i)
+ deque.push_back(i);
+
+ // All numbers present.
+ {
+ int i = 0;
+ for (int v : deque)
+ EXPECT_EQ(i++, v);
+ }
+
+ // Remove the even numbers while iterating.
+ for (auto it = deque.begin(); it != deque.end(); ++it) {
+ if (*it % 2 == 0) {
+ deque.erase(it);
+ --it;
+ }
+ }
+
+ // Only odd numbers left.
+ {
+ int i = 1;
+ for (int v : deque)
+ EXPECT_EQ(i + 2, v);
+ }
+}
+
+struct Item {
+ Item(int value1, int value2) : value1(value1), value2(value2) {}
+ int value1;
+ int value2;
+};
+
+TEST(DequeTest, emplace_back) {
+ Deque<Item> deque;
+ deque.emplace_back(1, 2);
+ deque.emplace_back(3, 4);
+
+ EXPECT_EQ(2u, deque.size());
+ EXPECT_EQ(1, deque[0].value1);
+ EXPECT_EQ(2, deque[0].value2);
+ EXPECT_EQ(3, deque[1].value1);
+ EXPECT_EQ(4, deque[1].value2);
+}
+
+TEST(DequeTest, emplace_front) {
+ Deque<Item> deque;
+ deque.emplace_front(1, 2);
+ deque.emplace_front(3, 4);
+
+ EXPECT_EQ(2u, deque.size());
+ EXPECT_EQ(3, deque[0].value1);
+ EXPECT_EQ(4, deque[0].value2);
+ EXPECT_EQ(1, deque[1].value1);
+ EXPECT_EQ(2, deque[1].value2);
+}
+
+static_assert(!IsTraceable<Deque<int>>::value,
+ "Deque<int> must not be traceable.");
+
+} // anonymous namespace
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/doubly_linked_list.h b/chromium/third_party/blink/renderer/platform/wtf/doubly_linked_list.h
new file mode 100644
index 00000000000..94f95ffd735
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/doubly_linked_list.h
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2011 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DOUBLY_LINKED_LIST_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DOUBLY_LINKED_LIST_H_
+
+#include "base/macros.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace WTF {
+
+// This class allows nodes to share code without dictating data member layout.
+template <typename T>
+class DoublyLinkedListNode {
+ public:
+ DoublyLinkedListNode();
+
+ void SetPrev(T*);
+ void SetNext(T*);
+
+ T* Prev() const;
+ T* Next() const;
+};
+
+template <typename T>
+inline DoublyLinkedListNode<T>::DoublyLinkedListNode() {
+ SetPrev(nullptr);
+ SetNext(nullptr);
+}
+
+template <typename T>
+inline void DoublyLinkedListNode<T>::SetPrev(T* prev) {
+ static_cast<T*>(this)->prev_ = prev;
+}
+
+template <typename T>
+inline void DoublyLinkedListNode<T>::SetNext(T* next) {
+ static_cast<T*>(this)->next_ = next;
+}
+
+template <typename T>
+inline T* DoublyLinkedListNode<T>::Prev() const {
+ return static_cast<const T*>(this)->prev_;
+}
+
+template <typename T>
+inline T* DoublyLinkedListNode<T>::Next() const {
+ return static_cast<const T*>(this)->next_;
+}
+
+template <typename T, typename PointerType = T*>
+class DoublyLinkedList {
+ USING_FAST_MALLOC(DoublyLinkedList);
+
+ public:
+ DoublyLinkedList();
+
+ bool IsEmpty() const;
+ size_t size() const; // This is O(n).
+ void Clear();
+
+ T* Head() const;
+ T* RemoveHead();
+
+ T* Tail() const;
+
+ void Push(T*);
+ void Append(T*);
+ void Remove(T*);
+
+ protected:
+ PointerType head_;
+ PointerType tail_;
+
+ DISALLOW_COPY_AND_ASSIGN(DoublyLinkedList);
+};
+
+template <typename T, typename PointerType>
+inline DoublyLinkedList<T, PointerType>::DoublyLinkedList()
+ : head_(nullptr), tail_(nullptr) {
+ static_assert(
+ !IsGarbageCollectedType<T>::value ||
+ !std::is_same<PointerType, T*>::value,
+ "Cannot use DoublyLinkedList<> with garbage collected types, use "
+ "HeapDoublyLinkedList<> instead.");
+}
+
+template <typename T, typename PointerType>
+inline bool DoublyLinkedList<T, PointerType>::IsEmpty() const {
+ return !head_;
+}
+
+template <typename T, typename PointerType>
+inline size_t DoublyLinkedList<T, PointerType>::size() const {
+ size_t size = 0;
+ for (T* node = head_; node; node = node->Next())
+ ++size;
+ return size;
+}
+
+template <typename T, typename PointerType>
+inline void DoublyLinkedList<T, PointerType>::Clear() {
+ head_ = nullptr;
+ tail_ = nullptr;
+}
+
+template <typename T, typename PointerType>
+inline T* DoublyLinkedList<T, PointerType>::Head() const {
+ return head_;
+}
+
+template <typename T, typename PointerType>
+inline T* DoublyLinkedList<T, PointerType>::Tail() const {
+ return tail_;
+}
+
+template <typename T, typename PointerType>
+inline void DoublyLinkedList<T, PointerType>::Push(T* node) {
+ if (!head_) {
+ DCHECK(!tail_);
+ head_ = node;
+ tail_ = node;
+ node->SetPrev(nullptr);
+ node->SetNext(nullptr);
+ return;
+ }
+
+ DCHECK(tail_);
+ head_->SetPrev(node);
+ node->SetNext(head_);
+ node->SetPrev(nullptr);
+ head_ = node;
+}
+
+template <typename T, typename PointerType>
+inline void DoublyLinkedList<T, PointerType>::Append(T* node) {
+ if (!tail_) {
+ DCHECK(!head_);
+ head_ = node;
+ tail_ = node;
+ node->SetPrev(nullptr);
+ node->SetNext(nullptr);
+ return;
+ }
+
+ DCHECK(head_);
+ tail_->SetNext(node);
+ node->SetPrev(tail_);
+ node->SetNext(nullptr);
+ tail_ = node;
+}
+
+template <typename T, typename PointerType>
+inline void DoublyLinkedList<T, PointerType>::Remove(T* node) {
+ if (node->Prev()) {
+ DCHECK_NE(node, head_);
+ node->Prev()->SetNext(node->Next());
+ } else {
+ DCHECK_EQ(node, head_);
+ head_ = node->Next();
+ }
+
+ if (node->Next()) {
+ DCHECK_NE(node, tail_);
+ node->Next()->SetPrev(node->Prev());
+ } else {
+ DCHECK_EQ(node, tail_);
+ tail_ = node->Prev();
+ }
+}
+
+template <typename T, typename PointerType>
+inline T* DoublyLinkedList<T, PointerType>::RemoveHead() {
+ T* node = Head();
+ if (node)
+ Remove(node);
+ return node;
+}
+
+} // namespace WTF
+
+using WTF::DoublyLinkedListNode;
+using WTF::DoublyLinkedList;
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/wtf/dtoa.cc b/chromium/third_party/blink/renderer/platform/wtf/dtoa.cc
new file mode 100644
index 00000000000..ba12295637a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/dtoa.cc
@@ -0,0 +1,147 @@
+/****************************************************************
+ *
+ * The author of this software is David M. Gay.
+ *
+ * Copyright (c) 1991, 2000, 2001 by Lucent Technologies.
+ * Copyright (C) 2002, 2005, 2006, 2007, 2008, 2010, 2012 Apple Inc.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ *
+ ***************************************************************/
+
+/* Please send bug reports to David M. Gay (dmg at acm dot org,
+ * with " at " changed at "@" and " dot " changed to "."). */
+
+/* On a machine with IEEE extended-precision registers, it is
+ * necessary to specify double-precision (53-bit) rounding precision
+ * before invoking strtod or dtoa. If the machine uses (the equivalent
+ * of) Intel 80x87 arithmetic, the call
+ * _control87(PC_53, MCW_PC);
+ * does this with many compilers. Whether this or another call is
+ * appropriate depends on the compiler; for this to work, it may be
+ * necessary to #include "float.h" or another system-dependent header
+ * file.
+ */
+
+#include "third_party/blink/renderer/platform/wtf/dtoa.h"
+
+#include <string.h>
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace WTF {
+
+const char* NumberToString(double d, NumberToStringBuffer buffer) {
+ double_conversion::StringBuilder builder(buffer, kNumberToStringBufferLength);
+ const double_conversion::DoubleToStringConverter& converter =
+ double_conversion::DoubleToStringConverter::EcmaScriptConverter();
+ converter.ToShortest(d, &builder);
+ return builder.Finalize();
+}
+
+static inline const char* FormatStringTruncatingTrailingZerosIfNeeded(
+ NumberToStringBuffer buffer,
+ double_conversion::StringBuilder& builder) {
+ size_t length = builder.GetPosition();
+
+ // If there is an exponent, stripping trailing zeros would be incorrect.
+ // FIXME: Zeros should be stripped before the 'e'.
+ if (memchr(buffer, 'e', length))
+ return builder.Finalize();
+
+ size_t decimal_point_position = 0;
+ for (; decimal_point_position < length; ++decimal_point_position) {
+ if (buffer[decimal_point_position] == '.')
+ break;
+ }
+
+ // No decimal seperator found, early exit.
+ if (decimal_point_position == length)
+ return builder.Finalize();
+
+ size_t truncated_length = length - 1;
+ for (; truncated_length > decimal_point_position; --truncated_length) {
+ if (buffer[truncated_length] != '0')
+ break;
+ }
+
+ // No trailing zeros found to strip.
+ if (truncated_length == length - 1)
+ return builder.Finalize();
+
+ // If we removed all trailing zeros, remove the decimal point as well.
+ if (truncated_length == decimal_point_position) {
+ DCHECK_GT(truncated_length, 0u);
+ --truncated_length;
+ }
+
+ // Truncate the StringBuilder, and return the final result.
+ builder.SetPosition(truncated_length + 1);
+ return builder.Finalize();
+}
+
+const char* NumberToFixedPrecisionString(double d,
+ unsigned significant_figures,
+ NumberToStringBuffer buffer) {
+ // Mimic String::format("%.[precision]g", ...), but use dtoas rounding
+ // facilities.
+ // "g": Signed value printed in f or e format, whichever is more compact for
+ // the given value and precision.
+ // The e format is used only when the exponent of the value is less than -4 or
+ // greater than or equal to the precision argument. Trailing zeros are
+ // truncated, and the decimal point appears only if one or more digits follow
+ // it.
+ // "precision": The precision specifies the maximum number of significant
+ // digits printed.
+ double_conversion::StringBuilder builder(buffer, kNumberToStringBufferLength);
+ const double_conversion::DoubleToStringConverter& converter =
+ double_conversion::DoubleToStringConverter::EcmaScriptConverter();
+ converter.ToPrecision(d, significant_figures, &builder);
+ // FIXME: Trailing zeros should never be added in the first place. The
+ // current implementation does not strip when there is an exponent, eg.
+ // 1.50000e+10.
+ return FormatStringTruncatingTrailingZerosIfNeeded(buffer, builder);
+}
+
+const char* NumberToFixedWidthString(double d,
+ unsigned decimal_places,
+ NumberToStringBuffer buffer) {
+ // Mimic String::format("%.[precision]f", ...), but use dtoas rounding
+ // facilities.
+ // "f": Signed value having the form [ - ]dddd.dddd, where dddd is one or more
+ // decimal digits. The number of digits before the decimal point depends on
+ // the magnitude of the number, and the number of digits after the decimal
+ // point depends on the requested precision.
+ // "precision": The precision value specifies the number of digits after the
+ // decimal point. If a decimal point appears, at least one digit appears
+ // before it. The value is rounded to the appropriate number of digits.
+ double_conversion::StringBuilder builder(buffer, kNumberToStringBufferLength);
+ const double_conversion::DoubleToStringConverter& converter =
+ double_conversion::DoubleToStringConverter::EcmaScriptConverter();
+ converter.ToFixed(d, decimal_places, &builder);
+ return builder.Finalize();
+}
+
+namespace Internal {
+
+double ParseDoubleFromLongString(const UChar* string,
+ size_t length,
+ size_t& parsed_length) {
+ Vector<LChar> conversion_buffer(length);
+ for (size_t i = 0; i < length; ++i)
+ conversion_buffer[i] = IsASCII(string[i]) ? string[i] : 0;
+ return ParseDouble(conversion_buffer.data(), length, parsed_length);
+}
+
+} // namespace Internal
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/dtoa.h b/chromium/third_party/blink/renderer/platform/wtf/dtoa.h
new file mode 100644
index 00000000000..e618ed3e68e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/dtoa.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2003, 2008, 2012 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_H_
+
+#include "third_party/blink/renderer/platform/wtf/ascii_ctype.h"
+#include "third_party/blink/renderer/platform/wtf/dtoa/double-conversion.h"
+#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_export.h"
+
+namespace WTF {
+
+// Size = 80 for sizeof(DtoaBuffer) + some sign bits, decimal point, 'e',
+// exponent digits.
+const unsigned kNumberToStringBufferLength = 96;
+typedef char NumberToStringBuffer[kNumberToStringBufferLength];
+
+WTF_EXPORT const char* NumberToString(double, NumberToStringBuffer);
+WTF_EXPORT const char* NumberToFixedPrecisionString(
+ double,
+ unsigned significant_figures,
+ NumberToStringBuffer);
+WTF_EXPORT const char* NumberToFixedWidthString(double,
+ unsigned decimal_places,
+ NumberToStringBuffer);
+
+WTF_EXPORT double ParseDouble(const LChar* string,
+ size_t length,
+ size_t& parsed_length);
+WTF_EXPORT double ParseDouble(const UChar* string,
+ size_t length,
+ size_t& parsed_length);
+
+namespace Internal {
+double ParseDoubleFromLongString(const UChar* string,
+ size_t length,
+ size_t& parsed_length);
+}
+
+inline double ParseDouble(const LChar* string,
+ size_t length,
+ size_t& parsed_length) {
+ return double_conversion::StringToDoubleConverter::StringToDouble(
+ reinterpret_cast<const char*>(string), length, &parsed_length);
+}
+
+inline double ParseDouble(const UChar* string,
+ size_t length,
+ size_t& parsed_length) {
+ const size_t kConversionBufferSize = 64;
+ if (length > kConversionBufferSize)
+ return Internal::ParseDoubleFromLongString(string, length, parsed_length);
+ LChar conversion_buffer[kConversionBufferSize];
+ for (size_t i = 0; i < length; ++i)
+ conversion_buffer[i] =
+ IsASCII(string[i]) ? static_cast<LChar>(string[i]) : 0;
+ return ParseDouble(conversion_buffer, length, parsed_length);
+}
+
+} // namespace WTF
+
+using WTF::NumberToStringBuffer;
+using WTF::NumberToString;
+using WTF::NumberToFixedPrecisionString;
+using WTF::NumberToFixedWidthString;
+using WTF::ParseDouble;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/dtoa/COPYING b/chromium/third_party/blink/renderer/platform/wtf/dtoa/COPYING
new file mode 100644
index 00000000000..933718a9ef9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/dtoa/COPYING
@@ -0,0 +1,26 @@
+Copyright 2006-2011, the V8 project authors. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of Google Inc. nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/chromium/third_party/blink/renderer/platform/wtf/dtoa/LICENSE b/chromium/third_party/blink/renderer/platform/wtf/dtoa/LICENSE
new file mode 100644
index 00000000000..933718a9ef9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/dtoa/LICENSE
@@ -0,0 +1,26 @@
+Copyright 2006-2011, the V8 project authors. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of Google Inc. nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/chromium/third_party/blink/renderer/platform/wtf/dtoa/README b/chromium/third_party/blink/renderer/platform/wtf/dtoa/README
new file mode 100644
index 00000000000..f186b420f42
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/dtoa/README
@@ -0,0 +1,11 @@
+http://code.google.com/p/double-conversion
+
+This project (double-conversion) provides binary-decimal and decimal-binary
+routines for IEEE doubles.
+
+The library consists of efficient conversion routines that have been extracted
+from the V8 JavaScript engine. The code has been refactored and improved so that
+it can be used more easily in other projects.
+
+There is extensive documentation in src/double-conversion.h. Other examples can
+be found in test/cctest/test-conversions.cc.
diff --git a/chromium/third_party/blink/renderer/platform/wtf/dtoa/bignum-dtoa.cc b/chromium/third_party/blink/renderer/platform/wtf/dtoa/bignum-dtoa.cc
new file mode 100644
index 00000000000..f86315822b6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/dtoa/bignum-dtoa.cc
@@ -0,0 +1,680 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "third_party/blink/renderer/platform/wtf/dtoa/bignum-dtoa.h"
+
+#include <math.h>
+
+#include "third_party/blink/renderer/platform/wtf/dtoa/bignum.h"
+#include "third_party/blink/renderer/platform/wtf/dtoa/double.h"
+
+namespace WTF {
+
+namespace double_conversion {
+
+static int NormalizedExponent(uint64_t significand, int exponent) {
+ DCHECK_NE(significand, 0u);
+ while ((significand & Double::kHiddenBit) == 0) {
+ significand = significand << 1;
+ exponent = exponent - 1;
+ }
+ return exponent;
+}
+
+// Forward declarations:
+// Returns an estimation of k such that 10^(k-1) <= v < 10^k.
+static int EstimatePower(int exponent);
+// Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator
+// and denominator.
+static void InitialScaledStartValues(double v,
+ int estimated_power,
+ bool need_boundary_deltas,
+ Bignum* numerator,
+ Bignum* denominator,
+ Bignum* delta_minus,
+ Bignum* delta_plus);
+// Multiplies numerator/denominator so that its values lies in the range 1-10.
+// Returns decimal_point s.t.
+// v = numerator'/denominator' * 10^(decimal_point-1)
+// where numerator' and denominator' are the values of numerator and
+// denominator after the call to this function.
+static void FixupMultiply10(int estimated_power,
+ bool is_even,
+ int* decimal_point,
+ Bignum* numerator,
+ Bignum* denominator,
+ Bignum* delta_minus,
+ Bignum* delta_plus);
+// Generates digits from the left to the right and stops when the generated
+// digits yield the shortest decimal representation of v.
+static void GenerateShortestDigits(Bignum* numerator,
+ Bignum* denominator,
+ Bignum* delta_minus,
+ Bignum* delta_plus,
+ bool is_even,
+ Vector<char> buffer,
+ int* length);
+// Generates 'requested_digits' after the decimal point.
+static void BignumToFixed(int requested_digits,
+ int* decimal_point,
+ Bignum* numerator,
+ Bignum* denominator,
+ Vector<char>(buffer),
+ int* length);
+// Generates 'count' digits of numerator/denominator.
+// Once 'count' digits have been produced rounds the result depending on the
+// remainder (remainders of exactly .5 round upwards). Might update the
+// decimal_point when rounding up (for example for 0.9999).
+static void GenerateCountedDigits(int count,
+ int* decimal_point,
+ Bignum* numerator,
+ Bignum* denominator,
+ Vector<char>(buffer),
+ int* length);
+
+void BignumDtoa(double v,
+ BignumDtoaMode mode,
+ int requested_digits,
+ Vector<char> buffer,
+ int* length,
+ int* decimal_point) {
+ DCHECK_GT(v, 0);
+ DCHECK(!Double(v).IsSpecial());
+ uint64_t significand = Double(v).Significand();
+ bool is_even = (significand & 1) == 0;
+ int exponent = Double(v).Exponent();
+ int normalized_exponent = NormalizedExponent(significand, exponent);
+ // estimated_power might be too low by 1.
+ int estimated_power = EstimatePower(normalized_exponent);
+
+ // Shortcut for Fixed.
+ // The requested digits correspond to the digits after the point. If the
+ // number is much too small, then there is no need in trying to get any
+ // digits.
+ if (mode == BIGNUM_DTOA_FIXED && -estimated_power - 1 > requested_digits) {
+ buffer[0] = '\0';
+ *length = 0;
+ // Set decimal-point to -requested_digits. This is what Gay does.
+ // Note that it should not have any effect anyways since the string is
+ // empty.
+ *decimal_point = -requested_digits;
+ return;
+ }
+
+ Bignum numerator;
+ Bignum denominator;
+ Bignum delta_minus;
+ Bignum delta_plus;
+ // Make sure the bignum can grow large enough. The smallest double equals
+ // 4e-324. In this case the denominator needs fewer than 324*4 binary digits.
+ // The maximum double is 1.7976931348623157e308 which needs fewer than
+ // 308*4 binary digits.
+ DCHECK_GE(Bignum::kMaxSignificantBits, 324 * 4);
+ bool need_boundary_deltas = (mode == BIGNUM_DTOA_SHORTEST);
+ InitialScaledStartValues(v, estimated_power, need_boundary_deltas, &numerator,
+ &denominator, &delta_minus, &delta_plus);
+ // We now have v = (numerator / denominator) * 10^estimated_power.
+ FixupMultiply10(estimated_power, is_even, decimal_point, &numerator,
+ &denominator, &delta_minus, &delta_plus);
+ // We now have v = (numerator / denominator) * 10^(decimal_point-1), and
+ // 1 <= (numerator + delta_plus) / denominator < 10
+ switch (mode) {
+ case BIGNUM_DTOA_SHORTEST:
+ GenerateShortestDigits(&numerator, &denominator, &delta_minus,
+ &delta_plus, is_even, buffer, length);
+ break;
+ case BIGNUM_DTOA_FIXED:
+ BignumToFixed(requested_digits, decimal_point, &numerator, &denominator,
+ buffer, length);
+ break;
+ case BIGNUM_DTOA_PRECISION:
+ GenerateCountedDigits(requested_digits, decimal_point, &numerator,
+ &denominator, buffer, length);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ buffer[*length] = '\0';
+}
+
+// The procedure starts generating digits from the left to the right and stops
+// when the generated digits yield the shortest decimal representation of v. A
+// decimal representation of v is a number lying closer to v than to any other
+// double, so it converts to v when read.
+//
+// This is true if d, the decimal representation, is between m- and m+, the
+// upper and lower boundaries. d must be strictly between them if !is_even.
+// m- := (numerator - delta_minus) / denominator
+// m+ := (numerator + delta_plus) / denominator
+//
+// Precondition: 0 <= (numerator+delta_plus) / denominator < 10.
+// If 1 <= (numerator+delta_plus) / denominator < 10 then no leading 0 digit
+// will be produced. This should be the standard precondition.
+static void GenerateShortestDigits(Bignum* numerator,
+ Bignum* denominator,
+ Bignum* delta_minus,
+ Bignum* delta_plus,
+ bool is_even,
+ Vector<char> buffer,
+ int* length) {
+ // Small optimization: if delta_minus and delta_plus are the same just reuse
+ // one of the two bignums.
+ if (Bignum::Equal(*delta_minus, *delta_plus)) {
+ delta_plus = delta_minus;
+ }
+ *length = 0;
+ while (true) {
+ uint16_t digit;
+ digit = numerator->DivideModuloIntBignum(*denominator);
+ DCHECK_LE(digit, 9u); // digit is a uint16_t and therefore always positive.
+ // digit = numerator / denominator (integer division).
+ // numerator = numerator % denominator.
+ buffer[(*length)++] = static_cast<char>(digit + '0');
+
+ // Can we stop already?
+ // If the remainder of the division is less than the distance to the lower
+ // boundary we can stop. In this case we simply round down (discarding the
+ // remainder).
+ // Similarly we test if we can round up (using the upper boundary).
+ bool in_delta_room_minus;
+ bool in_delta_room_plus;
+ if (is_even) {
+ in_delta_room_minus = Bignum::LessEqual(*numerator, *delta_minus);
+ } else {
+ in_delta_room_minus = Bignum::Less(*numerator, *delta_minus);
+ }
+ if (is_even) {
+ in_delta_room_plus =
+ Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0;
+ } else {
+ in_delta_room_plus =
+ Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0;
+ }
+ if (!in_delta_room_minus && !in_delta_room_plus) {
+ // Prepare for next iteration.
+ numerator->Times10();
+ delta_minus->Times10();
+ // We optimized delta_plus to be equal to delta_minus (if they share the
+ // same value). So don't multiply delta_plus if they point to the same
+ // object.
+ if (delta_minus != delta_plus) {
+ delta_plus->Times10();
+ }
+ } else if (in_delta_room_minus && in_delta_room_plus) {
+ // Let's see if 2*numerator < denominator.
+ // If yes, then the next digit would be < 5 and we can round down.
+ int compare = Bignum::PlusCompare(*numerator, *numerator, *denominator);
+ if (compare < 0) {
+ // Remaining digits are less than .5. -> Round down (== do nothing).
+ } else if (compare > 0) {
+ // Remaining digits are more than .5 of denominator. -> Round up.
+ // Note that the last digit could not be a '9' as otherwise the whole
+ // loop would have stopped earlier.
+ // We still have an assert here in case the preconditions were not
+ // satisfied.
+ DCHECK_NE(buffer[(*length) - 1], '9');
+ buffer[(*length) - 1]++;
+ } else {
+ // Halfway case.
+ // TODO(floitsch): need a way to solve half-way cases.
+ // For now let's round towards even (since this is what Gay seems to
+ // do).
+
+ if ((buffer[(*length) - 1] - '0') % 2 == 0) {
+ // Round down => Do nothing.
+ } else {
+ DCHECK_NE(buffer[(*length) - 1], '9');
+ buffer[(*length) - 1]++;
+ }
+ }
+ return;
+ } else if (in_delta_room_minus) {
+ // Round down (== do nothing).
+ return;
+ } else { // in_delta_room_plus
+ // Round up.
+ // Note again that the last digit could not be '9' since this would have
+ // stopped the loop earlier.
+ // We still have an ASSERT here, in case the preconditions were not
+ // satisfied.
+ DCHECK_NE(buffer[(*length) - 1], '9');
+ buffer[(*length) - 1]++;
+ return;
+ }
+ }
+}
+
+// Let v = numerator / denominator < 10.
+// Then we generate 'count' digits of d = x.xxxxx... (without the decimal point)
+// from left to right. Once 'count' digits have been produced we decide wether
+// to round up or down. Remainders of exactly .5 round upwards. Numbers such
+// as 9.999999 propagate a carry all the way, and change the
+// exponent (decimal_point), when rounding upwards.
+static void GenerateCountedDigits(int count,
+ int* decimal_point,
+ Bignum* numerator,
+ Bignum* denominator,
+ Vector<char>(buffer),
+ int* length) {
+ DCHECK_GE(count, 0);
+ for (int i = 0; i < count - 1; ++i) {
+ uint16_t digit;
+ digit = numerator->DivideModuloIntBignum(*denominator);
+ DCHECK_LE(digit, 9u); // digit is a uint16_t and therefore always positive.
+ // digit = numerator / denominator (integer division).
+ // numerator = numerator % denominator.
+ buffer[i] = static_cast<char>(digit + '0');
+ // Prepare for next iteration.
+ numerator->Times10();
+ }
+ // Generate the last digit.
+ uint16_t digit;
+ digit = numerator->DivideModuloIntBignum(*denominator);
+ if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) {
+ digit++;
+ }
+ buffer[count - 1] = static_cast<char>(digit + '0');
+ // Correct bad digits (in case we had a sequence of '9's). Propagate the
+ // carry until we hat a non-'9' or til we reach the first digit.
+ for (int i = count - 1; i > 0; --i) {
+ if (buffer[i] != '0' + 10)
+ break;
+ buffer[i] = '0';
+ buffer[i - 1]++;
+ }
+ if (buffer[0] == '0' + 10) {
+ // Propagate a carry past the top place.
+ buffer[0] = '1';
+ (*decimal_point)++;
+ }
+ *length = count;
+}
+
+// Generates 'requested_digits' after the decimal point. It might omit
+// trailing '0's. If the input number is too small then no digits at all are
+// generated (ex.: 2 fixed digits for 0.00001).
+//
+// Input verifies: 1 <= (numerator + delta) / denominator < 10.
+static void BignumToFixed(int requested_digits,
+ int* decimal_point,
+ Bignum* numerator,
+ Bignum* denominator,
+ Vector<char>(buffer),
+ int* length) {
+ // Note that we have to look at more than just the requested_digits, since
+ // a number could be rounded up. Example: v=0.5 with requested_digits=0.
+ // Even though the power of v equals 0 we can't just stop here.
+ if (-(*decimal_point) > requested_digits) {
+ // The number is definitively too small.
+ // Ex: 0.001 with requested_digits == 1.
+ // Set decimal-point to -requested_digits. This is what Gay does.
+ // Note that it should not have any effect anyways since the string is
+ // empty.
+ *decimal_point = -requested_digits;
+ *length = 0;
+ return;
+ } else if (-(*decimal_point) == requested_digits) {
+ // We only need to verify if the number rounds down or up.
+ // Ex: 0.04 and 0.06 with requested_digits == 1.
+ DCHECK_EQ(*decimal_point, -requested_digits);
+ // Initially the fraction lies in range (1, 10]. Multiply the denominator
+ // by 10 so that we can compare more easily.
+ denominator->Times10();
+ if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) {
+ // If the fraction is >= 0.5 then we have to include the rounded
+ // digit.
+ buffer[0] = '1';
+ *length = 1;
+ (*decimal_point)++;
+ } else {
+ // Note that we caught most of similar cases earlier.
+ *length = 0;
+ }
+ return;
+ } else {
+ // The requested digits correspond to the digits after the point.
+ // The variable 'needed_digits' includes the digits before the point.
+ int needed_digits = (*decimal_point) + requested_digits;
+ GenerateCountedDigits(needed_digits, decimal_point, numerator, denominator,
+ buffer, length);
+ }
+}
+
+// Returns an estimation of k such that 10^(k-1) <= v < 10^k where
+// v = f * 2^exponent and 2^52 <= f < 2^53.
+// v is hence a normalized double with the given exponent. The output is an
+// approximation for the exponent of the decimal approimation .digits * 10^k.
+//
+// The result might undershoot by 1 in which case 10^k <= v < 10^k+1.
+// Note: this property holds for v's upper boundary m+ too.
+// 10^k <= m+ < 10^k+1.
+// (see explanation below).
+//
+// Examples:
+// EstimatePower(0) => 16
+// EstimatePower(-52) => 0
+//
+// Note: e >= 0 => EstimatedPower(e) > 0. No similar claim can be made for e<0.
+static int EstimatePower(int exponent) {
+ // This function estimates log10 of v where v = f*2^e (with e == exponent).
+ // Note that 10^floor(log10(v)) <= v, but v <= 10^ceil(log10(v)).
+ // Note that f is bounded by its container size. Let p = 53 (the double's
+ // significand size). Then 2^(p-1) <= f < 2^p.
+ //
+ // Given that log10(v) == log2(v)/log2(10) and e+(len(f)-1) is quite close
+ // to log2(v) the function is simplified to (e+(len(f)-1)/log2(10)).
+ // The computed number undershoots by less than 0.631 (when we compute log3
+ // and not log10).
+ //
+ // Optimization: since we only need an approximated result this computation
+ // can be performed on 64 bit integers. On x86/x64 architecture the speedup is
+ // not really measurable, though.
+ //
+ // Since we want to avoid overshooting we decrement by 1e10 so that
+ // floating-point imprecisions don't affect us.
+ //
+ // Explanation for v's boundary m+: the computation takes advantage of
+ // the fact that 2^(p-1) <= f < 2^p. Boundaries still satisfy this requirement
+ // (even for denormals where the delta can be much more important).
+
+ const double kK1Log10 = 0.30102999566398114; // 1/lg(10)
+
+ // For doubles len(f) == 53 (don't forget the hidden bit).
+ const int kSignificandSize = 53;
+ double estimate = ceil((exponent + kSignificandSize - 1) * kK1Log10 - 1e-10);
+ return static_cast<int>(estimate);
+}
+
+// See comments for InitialScaledStartValues.
+static void InitialScaledStartValuesPositiveExponent(double v,
+ int estimated_power,
+ bool need_boundary_deltas,
+ Bignum* numerator,
+ Bignum* denominator,
+ Bignum* delta_minus,
+ Bignum* delta_plus) {
+ // A positive exponent implies a positive power.
+ DCHECK_GE(estimated_power, 0);
+ // Since the estimated_power is positive we simply multiply the denominator
+ // by 10^estimated_power.
+
+ // numerator = v.
+ numerator->AssignUInt64(Double(v).Significand());
+ numerator->ShiftLeft(Double(v).Exponent());
+ // denominator = 10^estimated_power.
+ denominator->AssignPowerUInt16(10, estimated_power);
+
+ if (need_boundary_deltas) {
+ // Introduce a common denominator so that the deltas to the boundaries are
+ // integers.
+ denominator->ShiftLeft(1);
+ numerator->ShiftLeft(1);
+ // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common
+ // denominator (of 2) delta_plus equals 2^e.
+ delta_plus->AssignUInt16(1);
+ delta_plus->ShiftLeft(Double(v).Exponent());
+ // Same for delta_minus (with adjustments below if f == 2^p-1).
+ delta_minus->AssignUInt16(1);
+ delta_minus->ShiftLeft(Double(v).Exponent());
+
+ // If the significand (without the hidden bit) is 0, then the lower
+ // boundary is closer than just half a ulp (unit in the last place).
+ // There is only one exception: if the next lower number is a denormal then
+ // the distance is 1 ulp. This cannot be the case for exponent >= 0 (but we
+ // have to test it in the other function where exponent < 0).
+ uint64_t v_bits = Double(v).AsUint64();
+ if ((v_bits & Double::kSignificandMask) == 0) {
+ // The lower boundary is closer at half the distance of "normal" numbers.
+ // Increase the common denominator and adapt all but the delta_minus.
+ denominator->ShiftLeft(1); // *2
+ numerator->ShiftLeft(1); // *2
+ delta_plus->ShiftLeft(1); // *2
+ }
+ }
+}
+
+// See comments for InitialScaledStartValues
+static void InitialScaledStartValuesNegativeExponentPositivePower(
+ double v,
+ int estimated_power,
+ bool need_boundary_deltas,
+ Bignum* numerator,
+ Bignum* denominator,
+ Bignum* delta_minus,
+ Bignum* delta_plus) {
+ uint64_t significand = Double(v).Significand();
+ int exponent = Double(v).Exponent();
+ // v = f * 2^e with e < 0, and with estimated_power >= 0.
+ // This means that e is close to 0 (have a look at how estimated_power is
+ // computed).
+
+ // numerator = significand
+ // since v = significand * 2^exponent this is equivalent to
+ // numerator = v * / 2^-exponent
+ numerator->AssignUInt64(significand);
+ // denominator = 10^estimated_power * 2^-exponent (with exponent < 0)
+ denominator->AssignPowerUInt16(10, estimated_power);
+ denominator->ShiftLeft(-exponent);
+
+ if (need_boundary_deltas) {
+ // Introduce a common denominator so that the deltas to the boundaries are
+ // integers.
+ denominator->ShiftLeft(1);
+ numerator->ShiftLeft(1);
+ // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common
+ // denominator (of 2) delta_plus equals 2^e.
+ // Given that the denominator already includes v's exponent the distance
+ // to the boundaries is simply 1.
+ delta_plus->AssignUInt16(1);
+ // Same for delta_minus (with adjustments below if f == 2^p-1).
+ delta_minus->AssignUInt16(1);
+
+ // If the significand (without the hidden bit) is 0, then the lower
+ // boundary is closer than just one ulp (unit in the last place).
+ // There is only one exception: if the next lower number is a denormal
+ // then the distance is 1 ulp. Since the exponent is close to zero
+ // (otherwise estimated_power would have been negative) this cannot happen
+ // here either.
+ uint64_t v_bits = Double(v).AsUint64();
+ if ((v_bits & Double::kSignificandMask) == 0) {
+ // The lower boundary is closer at half the distance of "normal" numbers.
+ // Increase the denominator and adapt all but the delta_minus.
+ denominator->ShiftLeft(1); // *2
+ numerator->ShiftLeft(1); // *2
+ delta_plus->ShiftLeft(1); // *2
+ }
+ }
+}
+
+// See comments for InitialScaledStartValues
+static void InitialScaledStartValuesNegativeExponentNegativePower(
+ double v,
+ int estimated_power,
+ bool need_boundary_deltas,
+ Bignum* numerator,
+ Bignum* denominator,
+ Bignum* delta_minus,
+ Bignum* delta_plus) {
+ const uint64_t kMinimalNormalizedExponent =
+ UINT64_2PART_C(0x00100000, 00000000);
+ uint64_t significand = Double(v).Significand();
+ int exponent = Double(v).Exponent();
+ // Instead of multiplying the denominator with 10^estimated_power we
+ // multiply all values (numerator and deltas) by 10^-estimated_power.
+
+ // Use numerator as temporary container for power_ten.
+ Bignum* power_ten = numerator;
+ power_ten->AssignPowerUInt16(10, -estimated_power);
+
+ if (need_boundary_deltas) {
+ // Since power_ten == numerator we must make a copy of 10^estimated_power
+ // before we complete the computation of the numerator.
+ // delta_plus = delta_minus = 10^estimated_power
+ delta_plus->AssignBignum(*power_ten);
+ delta_minus->AssignBignum(*power_ten);
+ }
+
+ // numerator = significand * 2 * 10^-estimated_power
+ // since v = significand * 2^exponent this is equivalent to
+ // numerator = v * 10^-estimated_power * 2 * 2^-exponent.
+ // Remember: numerator has been abused as power_ten. So no need to assign it
+ // to itself.
+ DCHECK_EQ(numerator, power_ten);
+ numerator->MultiplyByUInt64(significand);
+
+ // denominator = 2 * 2^-exponent with exponent < 0.
+ denominator->AssignUInt16(1);
+ denominator->ShiftLeft(-exponent);
+
+ if (need_boundary_deltas) {
+ // Introduce a common denominator so that the deltas to the boundaries are
+ // integers.
+ numerator->ShiftLeft(1);
+ denominator->ShiftLeft(1);
+ // With this shift the boundaries have their correct value, since
+ // delta_plus = 10^-estimated_power, and
+ // delta_minus = 10^-estimated_power.
+ // These assignments have been done earlier.
+
+ // The special case where the lower boundary is twice as close.
+ // This time we have to look out for the exception too.
+ uint64_t v_bits = Double(v).AsUint64();
+ if ((v_bits & Double::kSignificandMask) == 0 &&
+ // The only exception where a significand == 0 has its boundaries at
+ // "normal" distances:
+ (v_bits & Double::kExponentMask) != kMinimalNormalizedExponent) {
+ numerator->ShiftLeft(1); // *2
+ denominator->ShiftLeft(1); // *2
+ delta_plus->ShiftLeft(1); // *2
+ }
+ }
+}
+
+// Let v = significand * 2^exponent.
+// Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator
+// and denominator. The functions GenerateShortestDigits and
+// GenerateCountedDigits will then convert this ratio to its decimal
+// representation d, with the required accuracy.
+// Then d * 10^estimated_power is the representation of v.
+// (Note: the fraction and the estimated_power might get adjusted before
+// generating the decimal representation.)
+//
+// The initial start values consist of:
+// - a scaled numerator: s.t. numerator/denominator == v / 10^estimated_power.
+// - a scaled (common) denominator.
+// optionally (used by GenerateShortestDigits to decide if it has the shortest
+// decimal converting back to v):
+// - v - m-: the distance to the lower boundary.
+// - m+ - v: the distance to the upper boundary.
+//
+// v, m+, m-, and therefore v - m- and m+ - v all share the same denominator.
+//
+// Let ep == estimated_power, then the returned values will satisfy:
+// v / 10^ep = numerator / denominator.
+// v's boundarys m- and m+:
+// m- / 10^ep == v / 10^ep - delta_minus / denominator
+// m+ / 10^ep == v / 10^ep + delta_plus / denominator
+// Or in other words:
+// m- == v - delta_minus * 10^ep / denominator;
+// m+ == v + delta_plus * 10^ep / denominator;
+//
+// Since 10^(k-1) <= v < 10^k (with k == estimated_power)
+// or 10^k <= v < 10^(k+1)
+// we then have 0.1 <= numerator/denominator < 1
+// or 1 <= numerator/denominator < 10
+//
+// It is then easy to kickstart the digit-generation routine.
+//
+// The boundary-deltas are only filled if need_boundary_deltas is set.
+static void InitialScaledStartValues(double v,
+ int estimated_power,
+ bool need_boundary_deltas,
+ Bignum* numerator,
+ Bignum* denominator,
+ Bignum* delta_minus,
+ Bignum* delta_plus) {
+ if (Double(v).Exponent() >= 0) {
+ InitialScaledStartValuesPositiveExponent(
+ v, estimated_power, need_boundary_deltas, numerator, denominator,
+ delta_minus, delta_plus);
+ } else if (estimated_power >= 0) {
+ InitialScaledStartValuesNegativeExponentPositivePower(
+ v, estimated_power, need_boundary_deltas, numerator, denominator,
+ delta_minus, delta_plus);
+ } else {
+ InitialScaledStartValuesNegativeExponentNegativePower(
+ v, estimated_power, need_boundary_deltas, numerator, denominator,
+ delta_minus, delta_plus);
+ }
+}
+
+// This routine multiplies numerator/denominator so that its values lies in the
+// range 1-10. That is after a call to this function we have:
+// 1 <= (numerator + delta_plus) /denominator < 10.
+// Let numerator the input before modification and numerator' the argument
+// after modification, then the output-parameter decimal_point is such that
+// numerator / denominator * 10^estimated_power ==
+// numerator' / denominator' * 10^(decimal_point - 1)
+// In some cases estimated_power was too low, and this is already the case. We
+// then simply adjust the power so that 10^(k-1) <= v < 10^k (with k ==
+// estimated_power) but do not touch the numerator or denominator.
+// Otherwise the routine multiplies the numerator and the deltas by 10.
+static void FixupMultiply10(int estimated_power,
+ bool is_even,
+ int* decimal_point,
+ Bignum* numerator,
+ Bignum* denominator,
+ Bignum* delta_minus,
+ Bignum* delta_plus) {
+ bool in_range;
+ if (is_even) {
+ // For IEEE doubles half-way cases (in decimal system numbers ending with 5)
+ // are rounded to the closest floating-point number with even significand.
+ in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0;
+ } else {
+ in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0;
+ }
+ if (in_range) {
+ // Since numerator + delta_plus >= denominator we already have
+ // 1 <= numerator/denominator < 10. Simply update the estimated_power.
+ *decimal_point = estimated_power + 1;
+ } else {
+ *decimal_point = estimated_power;
+ numerator->Times10();
+ if (Bignum::Equal(*delta_minus, *delta_plus)) {
+ delta_minus->Times10();
+ delta_plus->AssignBignum(*delta_minus);
+ } else {
+ delta_minus->Times10();
+ delta_plus->Times10();
+ }
+ }
+}
+
+} // namespace double_conversion
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/dtoa/bignum-dtoa.h b/chromium/third_party/blink/renderer/platform/wtf/dtoa/bignum-dtoa.h
new file mode 100644
index 00000000000..3878c7ce379
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/dtoa/bignum-dtoa.h
@@ -0,0 +1,90 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_BIGNUM_DTOA_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_BIGNUM_DTOA_H_
+
+#include "third_party/blink/renderer/platform/wtf/dtoa/utils.h"
+
+namespace WTF {
+
+namespace double_conversion {
+
+enum BignumDtoaMode {
+ // Return the shortest correct representation.
+ // For example the output of 0.299999999999999988897 is (the less accurate but
+ // correct) 0.3.
+ BIGNUM_DTOA_SHORTEST,
+ // Return a fixed number of digits after the decimal point.
+ // For instance fixed(0.1, 4) becomes 0.1000
+ // If the input number is big, the output will be big.
+ BIGNUM_DTOA_FIXED,
+ // Return a fixed number of digits, no matter what the exponent is.
+ BIGNUM_DTOA_PRECISION
+};
+
+// Converts the given double 'v' to ascii.
+// The result should be interpreted as buffer * 10^(point-length).
+// The buffer will be null-terminated.
+//
+// The input v must be > 0 and different from NaN, and Infinity.
+//
+// The output depends on the given mode:
+// - SHORTEST: produce the least amount of digits for which the internal
+// identity requirement is still satisfied. If the digits are printed
+// (together with the correct exponent) then reading this number will give
+// 'v' again. The buffer will choose the representation that is closest to
+// 'v'. If there are two at the same distance, than the number is round up.
+// In this mode the 'requested_digits' parameter is ignored.
+// - FIXED: produces digits necessary to print a given number with
+// 'requested_digits' digits after the decimal point. The produced digits
+// might be too short in which case the caller has to fill the gaps with '0's.
+// Example: toFixed(0.001, 5) is allowed to return buffer="1", point=-2.
+// Halfway cases are rounded up. The call toFixed(0.15, 2) thus returns
+// buffer="2", point=0.
+// Note: the length of the returned buffer has no meaning wrt the significance
+// of its digits. That is, just because it contains '0's does not mean that
+// any other digit would not satisfy the internal identity requirement.
+// - PRECISION: produces 'requested_digits' where the first digit is not '0'.
+// Even though the length of produced digits usually equals
+// 'requested_digits', the function is allowed to return fewer digits, in
+// which case the caller has to fill the missing digits with '0's.
+// Halfway cases are again rounded up.
+// 'BignumDtoa' expects the given buffer to be big enough to hold all digits
+// and a terminating null-character.
+void BignumDtoa(double v,
+ BignumDtoaMode mode,
+ int requested_digits,
+ Vector<char> buffer,
+ int* length,
+ int* point);
+
+} // namespace double_conversion
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_BIGNUM_DTOA_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/dtoa/bignum.cc b/chromium/third_party/blink/renderer/platform/wtf/dtoa/bignum.cc
new file mode 100644
index 00000000000..07a743c4b2d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/dtoa/bignum.cc
@@ -0,0 +1,770 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "third_party/blink/renderer/platform/wtf/dtoa/bignum.h"
+
+#include "third_party/blink/renderer/platform/wtf/dtoa/utils.h"
+
+namespace WTF {
+
+namespace double_conversion {
+
+Bignum::Bignum()
+ : bigits_(bigits_buffer_, kBigitCapacity), used_digits_(0), exponent_(0) {
+ for (int i = 0; i < kBigitCapacity; ++i) {
+ bigits_[i] = 0;
+ }
+}
+
+template <typename S>
+static int BitSize(S value) {
+ return 8 * sizeof(value);
+}
+
+// Guaranteed to lie in one Bigit.
+void Bignum::AssignUInt16(uint16_t value) {
+ DCHECK_GE(kBigitSize, BitSize(value));
+ Zero();
+ if (value == 0)
+ return;
+
+ EnsureCapacity(1);
+ bigits_[0] = value;
+ used_digits_ = 1;
+}
+
+void Bignum::AssignUInt64(uint64_t value) {
+ const int kUInt64Size = 64;
+
+ Zero();
+ if (value == 0)
+ return;
+
+ int needed_bigits = kUInt64Size / kBigitSize + 1;
+ EnsureCapacity(needed_bigits);
+ for (int i = 0; i < needed_bigits; ++i) {
+ bigits_[i] = (uint32_t)value & kBigitMask;
+ value = value >> kBigitSize;
+ }
+ used_digits_ = needed_bigits;
+ Clamp();
+}
+
+void Bignum::AssignBignum(const Bignum& other) {
+ exponent_ = other.exponent_;
+ for (int i = 0; i < other.used_digits_; ++i) {
+ bigits_[i] = other.bigits_[i];
+ }
+ // Clear the excess digits (if there were any).
+ for (int i = other.used_digits_; i < used_digits_; ++i) {
+ bigits_[i] = 0;
+ }
+ used_digits_ = other.used_digits_;
+}
+
+static uint64_t ReadUInt64(Vector<const char> buffer,
+ int from,
+ int digits_to_read) {
+ uint64_t result = 0;
+ for (int i = from; i < from + digits_to_read; ++i) {
+ int digit = buffer[i] - '0';
+ DCHECK_LE(0, digit);
+ DCHECK_LE(digit, 9);
+ result = result * 10 + digit;
+ }
+ return result;
+}
+
+void Bignum::AssignDecimalString(Vector<const char> value) {
+ // 2^64 = 18446744073709551616 > 10^19
+ const int kMaxUint64DecimalDigits = 19;
+ Zero();
+ int length = value.length();
+ int pos = 0;
+ // Let's just say that each digit needs 4 bits.
+ while (length >= kMaxUint64DecimalDigits) {
+ uint64_t digits = ReadUInt64(value, pos, kMaxUint64DecimalDigits);
+ pos += kMaxUint64DecimalDigits;
+ length -= kMaxUint64DecimalDigits;
+ MultiplyByPowerOfTen(kMaxUint64DecimalDigits);
+ AddUInt64(digits);
+ }
+ uint64_t digits = ReadUInt64(value, pos, length);
+ MultiplyByPowerOfTen(length);
+ AddUInt64(digits);
+ Clamp();
+}
+
+static int HexCharValue(char c) {
+ if ('0' <= c && c <= '9')
+ return c - '0';
+ if ('a' <= c && c <= 'f')
+ return 10 + c - 'a';
+ if ('A' <= c && c <= 'F')
+ return 10 + c - 'A';
+ UNREACHABLE();
+ return 0; // To make compiler happy.
+}
+
+void Bignum::AssignHexString(Vector<const char> value) {
+ Zero();
+ int length = value.length();
+
+ int needed_bigits = length * 4 / kBigitSize + 1;
+ EnsureCapacity(needed_bigits);
+ int string_index = length - 1;
+ for (int i = 0; i < needed_bigits - 1; ++i) {
+ // These bigits are guaranteed to be "full".
+ Chunk current_bigit = 0;
+ for (int j = 0; j < kBigitSize / 4; j++) {
+ current_bigit += HexCharValue(value[string_index--]) << (j * 4);
+ }
+ bigits_[i] = current_bigit;
+ }
+ used_digits_ = needed_bigits - 1;
+
+ Chunk most_significant_bigit = 0; // Could be = 0;
+ for (int j = 0; j <= string_index; ++j) {
+ most_significant_bigit <<= 4;
+ most_significant_bigit += HexCharValue(value[j]);
+ }
+ if (most_significant_bigit != 0) {
+ bigits_[used_digits_] = most_significant_bigit;
+ used_digits_++;
+ }
+ Clamp();
+}
+
+void Bignum::AddUInt64(uint64_t operand) {
+ if (operand == 0)
+ return;
+ Bignum other;
+ other.AssignUInt64(operand);
+ AddBignum(other);
+}
+
+void Bignum::AddBignum(const Bignum& other) {
+ DCHECK(IsClamped());
+ DCHECK(other.IsClamped());
+
+ // If this has a greater exponent than other append zero-bigits to this.
+ // After this call exponent_ <= other.exponent_.
+ Align(other);
+
+ // There are two possibilities:
+ // aaaaaaaaaaa 0000 (where the 0s represent a's exponent)
+ // bbbbb 00000000
+ // ----------------
+ // ccccccccccc 0000
+ // or
+ // aaaaaaaaaa 0000
+ // bbbbbbbbb 0000000
+ // -----------------
+ // cccccccccccc 0000
+ // In both cases we might need a carry bigit.
+
+ EnsureCapacity(1 + Max(BigitLength(), other.BigitLength()) - exponent_);
+ Chunk carry = 0;
+ int bigit_pos = other.exponent_ - exponent_;
+ DCHECK_GE(bigit_pos, 0);
+ for (int i = 0; i < other.used_digits_; ++i) {
+ Chunk sum = bigits_[bigit_pos] + other.bigits_[i] + carry;
+ bigits_[bigit_pos] = sum & kBigitMask;
+ carry = sum >> kBigitSize;
+ bigit_pos++;
+ }
+
+ while (carry != 0) {
+ Chunk sum = bigits_[bigit_pos] + carry;
+ bigits_[bigit_pos] = sum & kBigitMask;
+ carry = sum >> kBigitSize;
+ bigit_pos++;
+ }
+ used_digits_ = Max(bigit_pos, used_digits_);
+ DCHECK(IsClamped());
+}
+
+void Bignum::SubtractBignum(const Bignum& other) {
+ DCHECK(IsClamped());
+ DCHECK(other.IsClamped());
+ // We require this to be bigger than other.
+ DCHECK(LessEqual(other, *this));
+
+ Align(other);
+
+ int offset = other.exponent_ - exponent_;
+ Chunk borrow = 0;
+ int i;
+ for (i = 0; i < other.used_digits_; ++i) {
+ DCHECK((borrow == 0) || (borrow == 1));
+ Chunk difference = bigits_[i + offset] - other.bigits_[i] - borrow;
+ bigits_[i + offset] = difference & kBigitMask;
+ borrow = difference >> (kChunkSize - 1);
+ }
+ while (borrow != 0) {
+ Chunk difference = bigits_[i + offset] - borrow;
+ bigits_[i + offset] = difference & kBigitMask;
+ borrow = difference >> (kChunkSize - 1);
+ ++i;
+ }
+ Clamp();
+}
+
+void Bignum::ShiftLeft(int shift_amount) {
+ if (used_digits_ == 0)
+ return;
+ exponent_ += shift_amount / kBigitSize;
+ int local_shift = shift_amount % kBigitSize;
+ EnsureCapacity(used_digits_ + 1);
+ BigitsShiftLeft(local_shift);
+}
+
+void Bignum::MultiplyByUInt32(uint32_t factor) {
+ if (factor == 1)
+ return;
+ if (factor == 0) {
+ Zero();
+ return;
+ }
+ if (used_digits_ == 0)
+ return;
+
+ // The product of a bigit with the factor is of size kBigitSize + 32.
+ // Assert that this number + 1 (for the carry) fits into double chunk.
+ DCHECK_GE(kDoubleChunkSize, kBigitSize + 32 + 1);
+ DoubleChunk carry = 0;
+ for (int i = 0; i < used_digits_; ++i) {
+ DoubleChunk product = static_cast<DoubleChunk>(factor) * bigits_[i] + carry;
+ bigits_[i] = static_cast<Chunk>(product & kBigitMask);
+ carry = (product >> kBigitSize);
+ }
+ while (carry != 0) {
+ EnsureCapacity(used_digits_ + 1);
+ bigits_[used_digits_] = (uint32_t)carry & kBigitMask;
+ used_digits_++;
+ carry >>= kBigitSize;
+ }
+}
+
+void Bignum::MultiplyByUInt64(uint64_t factor) {
+ if (factor == 1)
+ return;
+ if (factor == 0) {
+ Zero();
+ return;
+ }
+ DCHECK_LT(kBigitSize, 32);
+ uint64_t carry = 0;
+ uint64_t low = factor & 0xFFFFFFFF;
+ uint64_t high = factor >> 32;
+ for (int i = 0; i < used_digits_; ++i) {
+ uint64_t product_low = low * bigits_[i];
+ uint64_t product_high = high * bigits_[i];
+ uint64_t tmp = (carry & kBigitMask) + product_low;
+ bigits_[i] = (uint32_t)tmp & kBigitMask;
+ carry = (carry >> kBigitSize) + (tmp >> kBigitSize) +
+ (product_high << (32 - kBigitSize));
+ }
+ while (carry != 0) {
+ EnsureCapacity(used_digits_ + 1);
+ bigits_[used_digits_] = (uint32_t)carry & kBigitMask;
+ used_digits_++;
+ carry >>= kBigitSize;
+ }
+}
+
+void Bignum::MultiplyByPowerOfTen(int exponent) {
+ const uint64_t kFive27 = UINT64_2PART_C(0x6765c793, fa10079d);
+ const uint16_t kFive1 = 5;
+ const uint16_t kFive2 = kFive1 * 5;
+ const uint16_t kFive3 = kFive2 * 5;
+ const uint16_t kFive4 = kFive3 * 5;
+ const uint16_t kFive5 = kFive4 * 5;
+ const uint16_t kFive6 = kFive5 * 5;
+ const uint32_t kFive7 = kFive6 * 5;
+ const uint32_t kFive8 = kFive7 * 5;
+ const uint32_t kFive9 = kFive8 * 5;
+ const uint32_t kFive10 = kFive9 * 5;
+ const uint32_t kFive11 = kFive10 * 5;
+ const uint32_t kFive12 = kFive11 * 5;
+ const uint32_t kFive13 = kFive12 * 5;
+ const uint32_t kFive1_to_12[] = {kFive1, kFive2, kFive3, kFive4,
+ kFive5, kFive6, kFive7, kFive8,
+ kFive9, kFive10, kFive11, kFive12};
+
+ DCHECK_GE(exponent, 0);
+ if (exponent == 0)
+ return;
+ if (used_digits_ == 0)
+ return;
+
+ // We shift by exponent at the end just before returning.
+ int remaining_exponent = exponent;
+ while (remaining_exponent >= 27) {
+ MultiplyByUInt64(kFive27);
+ remaining_exponent -= 27;
+ }
+ while (remaining_exponent >= 13) {
+ MultiplyByUInt32(kFive13);
+ remaining_exponent -= 13;
+ }
+ if (remaining_exponent > 0) {
+ MultiplyByUInt32(kFive1_to_12[remaining_exponent - 1]);
+ }
+ ShiftLeft(exponent);
+}
+
+void Bignum::Square() {
+ DCHECK(IsClamped());
+ int product_length = 2 * used_digits_;
+ EnsureCapacity(product_length);
+
+ // Comba multiplication: compute each column separately.
+ // Example: r = a2a1a0 * b2b1b0.
+ // r = 1 * a0b0 +
+ // 10 * (a1b0 + a0b1) +
+ // 100 * (a2b0 + a1b1 + a0b2) +
+ // 1000 * (a2b1 + a1b2) +
+ // 10000 * a2b2
+ //
+ // In the worst case we have to accumulate nb-digits products of
+ // digit*digit.
+ //
+ // Assert that the additional number of bits in a DoubleChunk are enough
+ // to sum up used_digits of Bigit*Bigit.
+ if ((1 << (2 * (kChunkSize - kBigitSize))) <= used_digits_) {
+ UNIMPLEMENTED();
+ }
+ DoubleChunk accumulator = 0;
+ // First shift the digits so we don't overwrite them.
+ int copy_offset = used_digits_;
+ for (int i = 0; i < used_digits_; ++i) {
+ bigits_[copy_offset + i] = bigits_[i];
+ }
+ // We have two loops to avoid some 'if's in the loop.
+ for (int i = 0; i < used_digits_; ++i) {
+ // Process temporary digit i with power i.
+ // The sum of the two indices must be equal to i.
+ int bigit_index1 = i;
+ int bigit_index2 = 0;
+ // Sum all of the sub-products.
+ while (bigit_index1 >= 0) {
+ Chunk chunk1 = bigits_[copy_offset + bigit_index1];
+ Chunk chunk2 = bigits_[copy_offset + bigit_index2];
+ accumulator += static_cast<DoubleChunk>(chunk1) * chunk2;
+ bigit_index1--;
+ bigit_index2++;
+ }
+ bigits_[i] = static_cast<Chunk>(accumulator) & kBigitMask;
+ accumulator >>= kBigitSize;
+ }
+ for (int i = used_digits_; i < product_length; ++i) {
+ int bigit_index1 = used_digits_ - 1;
+ int bigit_index2 = i - bigit_index1;
+ // Invariant: sum of both indices is again equal to i.
+ // Inner loop runs 0 times on last iteration, emptying accumulator.
+ while (bigit_index2 < used_digits_) {
+ Chunk chunk1 = bigits_[copy_offset + bigit_index1];
+ Chunk chunk2 = bigits_[copy_offset + bigit_index2];
+ accumulator += static_cast<DoubleChunk>(chunk1) * chunk2;
+ bigit_index1--;
+ bigit_index2++;
+ }
+ // The overwritten bigits_[i] will never be read in further loop iterations,
+ // because bigit_index1 and bigit_index2 are always greater
+ // than i - used_digits_.
+ bigits_[i] = static_cast<Chunk>(accumulator) & kBigitMask;
+ accumulator >>= kBigitSize;
+ }
+ // Since the result was guaranteed to lie inside the number the
+ // accumulator must be 0 now.
+ DCHECK_EQ(accumulator, 0u);
+
+ // Don't forget to update the used_digits and the exponent.
+ used_digits_ = product_length;
+ exponent_ *= 2;
+ Clamp();
+}
+
+void Bignum::AssignPowerUInt16(uint16_t base, int power_exponent) {
+ DCHECK_NE(base, 0);
+ DCHECK_GE(power_exponent, 0);
+ if (power_exponent == 0) {
+ AssignUInt16(1);
+ return;
+ }
+ Zero();
+ int shifts = 0;
+ // We expect base to be in range 2-32, and most often to be 10.
+ // It does not make much sense to implement different algorithms for counting
+ // the bits.
+ while ((base & 1) == 0) {
+ base >>= 1;
+ shifts++;
+ }
+ int bit_size = 0;
+ int tmp_base = base;
+ while (tmp_base != 0) {
+ tmp_base >>= 1;
+ bit_size++;
+ }
+ int final_size = bit_size * power_exponent;
+ // 1 extra bigit for the shifting, and one for rounded final_size.
+ EnsureCapacity(final_size / kBigitSize + 2);
+
+ // Left to Right exponentiation.
+ int mask = 1;
+ while (power_exponent >= mask)
+ mask <<= 1;
+
+ // The mask is now pointing to the bit above the most significant 1-bit of
+ // power_exponent.
+ // Get rid of first 1-bit;
+ mask >>= 2;
+ uint64_t this_value = base;
+
+ bool delayed_multipliciation = false;
+ const uint64_t max_32bits = 0xFFFFFFFF;
+ while (mask != 0 && this_value <= max_32bits) {
+ this_value = this_value * this_value;
+ // Verify that there is enough space in this_value to perform the
+ // multiplication. The first bit_size bits must be 0.
+ if ((power_exponent & mask) != 0) {
+ uint64_t base_bits_mask =
+ ~((static_cast<uint64_t>(1) << (64 - bit_size)) - 1);
+ bool high_bits_zero = (this_value & base_bits_mask) == 0;
+ if (high_bits_zero) {
+ this_value *= base;
+ } else {
+ delayed_multipliciation = true;
+ }
+ }
+ mask >>= 1;
+ }
+ AssignUInt64(this_value);
+ if (delayed_multipliciation) {
+ MultiplyByUInt32(base);
+ }
+
+ // Now do the same thing as a bignum.
+ while (mask != 0) {
+ Square();
+ if ((power_exponent & mask) != 0) {
+ MultiplyByUInt32(base);
+ }
+ mask >>= 1;
+ }
+
+ // And finally add the saved shifts.
+ ShiftLeft(shifts * power_exponent);
+}
+
+// Precondition: this/other < 16bit.
+uint16_t Bignum::DivideModuloIntBignum(const Bignum& other) {
+ DCHECK(IsClamped());
+ DCHECK(other.IsClamped());
+ DCHECK_GT(other.used_digits_, 0);
+
+ // Easy case: if we have less digits than the divisor than the result is
+ // 0. Note: this handles the case where this == 0, too.
+ if (BigitLength() < other.BigitLength()) {
+ return 0;
+ }
+
+ Align(other);
+
+ uint16_t result = 0;
+
+ // Start by removing multiples of 'other' until both numbers have the same
+ // number of digits.
+ while (BigitLength() > other.BigitLength()) {
+ // This naive approach is extremely inefficient if the this divided other
+ // might be big. This function is implemented for doubleToString where
+ // the result should be small (less than 10).
+ DCHECK_GE(other.bigits_[other.used_digits_ - 1], ((1u << kBigitSize) / 16));
+ // Remove the multiples of the first digit.
+ // Example this = 23 and other equals 9. -> Remove 2 multiples.
+ result += static_cast<uint16_t>(bigits_[used_digits_ - 1]);
+ SubtractTimes(other, bigits_[used_digits_ - 1]);
+ }
+
+ DCHECK_EQ(BigitLength(), other.BigitLength());
+
+ // Both bignums are at the same length now.
+ // Since other has more than 0 digits we know that the access to
+ // bigits_[used_digits_ - 1] is safe.
+ Chunk this_bigit = bigits_[used_digits_ - 1];
+ Chunk other_bigit = other.bigits_[other.used_digits_ - 1];
+
+ if (other.used_digits_ == 1) {
+ // Shortcut for easy (and common) case.
+ uint16_t quotient = static_cast<uint16_t>(this_bigit / other_bigit);
+ bigits_[used_digits_ - 1] = this_bigit - other_bigit * quotient;
+ result += quotient;
+ Clamp();
+ return result;
+ }
+
+ uint16_t division_estimate =
+ static_cast<uint16_t>(this_bigit / (other_bigit + 1));
+ result += division_estimate;
+ SubtractTimes(other, division_estimate);
+
+ if (other_bigit * (division_estimate + 1) > this_bigit) {
+ // No need to even try to subtract. Even if other's remaining digits were 0
+ // another subtraction would be too much.
+ return result;
+ }
+
+ while (LessEqual(other, *this)) {
+ SubtractBignum(other);
+ result++;
+ }
+ return result;
+}
+
+template <typename S>
+static int SizeInHexChars(S number) {
+ DCHECK_GT(number, 0u);
+ int result = 0;
+ while (number != 0) {
+ number >>= 4;
+ result++;
+ }
+ return result;
+}
+
+static char HexCharOfValue(uint8_t value) {
+ DCHECK_LE(0, value);
+ DCHECK_LE(value, 16);
+ if (value < 10)
+ return value + '0';
+ return value - 10 + 'A';
+}
+
+bool Bignum::ToHexString(char* buffer, int buffer_size) const {
+ DCHECK(IsClamped());
+ // Each bigit must be printable as separate hex-character.
+ DCHECK_EQ(kBigitSize % 4, 0);
+ const int kHexCharsPerBigit = kBigitSize / 4;
+
+ if (used_digits_ == 0) {
+ if (buffer_size < 2)
+ return false;
+ buffer[0] = '0';
+ buffer[1] = '\0';
+ return true;
+ }
+ // We add 1 for the terminating '\0' character.
+ int needed_chars = (BigitLength() - 1) * kHexCharsPerBigit +
+ SizeInHexChars(bigits_[used_digits_ - 1]) + 1;
+ if (needed_chars > buffer_size)
+ return false;
+ int string_index = needed_chars - 1;
+ buffer[string_index--] = '\0';
+ for (int i = 0; i < exponent_; ++i) {
+ for (int j = 0; j < kHexCharsPerBigit; ++j) {
+ buffer[string_index--] = '0';
+ }
+ }
+ for (int i = 0; i < used_digits_ - 1; ++i) {
+ Chunk current_bigit = bigits_[i];
+ for (int j = 0; j < kHexCharsPerBigit; ++j) {
+ buffer[string_index--] = HexCharOfValue(current_bigit & 0xF);
+ current_bigit >>= 4;
+ }
+ }
+ // And finally the last bigit.
+ Chunk most_significant_bigit = bigits_[used_digits_ - 1];
+ while (most_significant_bigit != 0) {
+ buffer[string_index--] = HexCharOfValue(most_significant_bigit & 0xF);
+ most_significant_bigit >>= 4;
+ }
+ return true;
+}
+
+Bignum::Chunk Bignum::BigitAt(int index) const {
+ if (index >= BigitLength())
+ return 0;
+ if (index < exponent_)
+ return 0;
+ return bigits_[index - exponent_];
+}
+
+int Bignum::Compare(const Bignum& a, const Bignum& b) {
+ DCHECK(a.IsClamped());
+ DCHECK(b.IsClamped());
+ int bigit_length_a = a.BigitLength();
+ int bigit_length_b = b.BigitLength();
+ if (bigit_length_a < bigit_length_b)
+ return -1;
+ if (bigit_length_a > bigit_length_b)
+ return +1;
+ for (int i = bigit_length_a - 1; i >= Min(a.exponent_, b.exponent_); --i) {
+ Chunk bigit_a = a.BigitAt(i);
+ Chunk bigit_b = b.BigitAt(i);
+ if (bigit_a < bigit_b)
+ return -1;
+ if (bigit_a > bigit_b)
+ return +1;
+ // Otherwise they are equal up to this digit. Try the next digit.
+ }
+ return 0;
+}
+
+int Bignum::PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c) {
+ DCHECK(a.IsClamped());
+ DCHECK(b.IsClamped());
+ DCHECK(c.IsClamped());
+ if (a.BigitLength() < b.BigitLength()) {
+ return PlusCompare(b, a, c);
+ }
+ if (a.BigitLength() + 1 < c.BigitLength())
+ return -1;
+ if (a.BigitLength() > c.BigitLength())
+ return +1;
+ // The exponent encodes 0-bigits. So if there are more 0-digits in 'a' than
+ // 'b' has digits, then the bigit-length of 'a'+'b' must be equal to the one
+ // of 'a'.
+ if (a.exponent_ >= b.BigitLength() && a.BigitLength() < c.BigitLength()) {
+ return -1;
+ }
+
+ Chunk borrow = 0;
+ // Starting at min_exponent all digits are == 0. So no need to compare them.
+ int min_exponent = Min(Min(a.exponent_, b.exponent_), c.exponent_);
+ for (int i = c.BigitLength() - 1; i >= min_exponent; --i) {
+ Chunk chunk_a = a.BigitAt(i);
+ Chunk chunk_b = b.BigitAt(i);
+ Chunk chunk_c = c.BigitAt(i);
+ Chunk sum = chunk_a + chunk_b;
+ if (sum > chunk_c + borrow) {
+ return +1;
+ } else {
+ borrow = chunk_c + borrow - sum;
+ if (borrow > 1)
+ return -1;
+ borrow <<= kBigitSize;
+ }
+ }
+ if (borrow == 0)
+ return 0;
+ return -1;
+}
+
+void Bignum::Clamp() {
+ while (used_digits_ > 0 && bigits_[used_digits_ - 1] == 0) {
+ used_digits_--;
+ }
+ if (used_digits_ == 0) {
+ // Zero.
+ exponent_ = 0;
+ }
+}
+
+bool Bignum::IsClamped() const {
+ return used_digits_ == 0 || bigits_[used_digits_ - 1] != 0;
+}
+
+void Bignum::Zero() {
+ for (int i = 0; i < used_digits_; ++i) {
+ bigits_[i] = 0;
+ }
+ used_digits_ = 0;
+ exponent_ = 0;
+}
+
+void Bignum::Align(const Bignum& other) {
+ if (exponent_ > other.exponent_) {
+ // If "X" represents a "hidden" digit (by the exponent) then we are in the
+ // following case (a == this, b == other):
+ // a: aaaaaaXXXX or a: aaaaaXXX
+ // b: bbbbbbX b: bbbbbbbbXX
+ // We replace some of the hidden digits (X) of a with 0 digits.
+ // a: aaaaaa000X or a: aaaaa0XX
+ int zero_digits = exponent_ - other.exponent_;
+ EnsureCapacity(used_digits_ + zero_digits);
+ for (int i = used_digits_ - 1; i >= 0; --i) {
+ bigits_[i + zero_digits] = bigits_[i];
+ }
+ for (int i = 0; i < zero_digits; ++i) {
+ bigits_[i] = 0;
+ }
+ used_digits_ += zero_digits;
+ exponent_ -= zero_digits;
+ DCHECK_GE(used_digits_, 0);
+ DCHECK_GE(exponent_, 0);
+ }
+}
+
+void Bignum::BigitsShiftLeft(int shift_amount) {
+ DCHECK_LT(shift_amount, kBigitSize);
+ DCHECK_GE(shift_amount, 0);
+ Chunk carry = 0;
+ for (int i = 0; i < used_digits_; ++i) {
+ Chunk new_carry = bigits_[i] >> (kBigitSize - shift_amount);
+ bigits_[i] = ((bigits_[i] << shift_amount) + carry) & kBigitMask;
+ carry = new_carry;
+ }
+ if (carry != 0) {
+ bigits_[used_digits_] = carry;
+ used_digits_++;
+ }
+}
+
+void Bignum::SubtractTimes(const Bignum& other, int factor) {
+ DCHECK_LE(exponent_, other.exponent_);
+ if (factor < 3) {
+ for (int i = 0; i < factor; ++i) {
+ SubtractBignum(other);
+ }
+ return;
+ }
+ Chunk borrow = 0;
+ int exponent_diff = other.exponent_ - exponent_;
+ for (int i = 0; i < other.used_digits_; ++i) {
+ DoubleChunk product = static_cast<DoubleChunk>(factor) * other.bigits_[i];
+ DoubleChunk remove = borrow + product;
+ Chunk difference =
+ bigits_[i + exponent_diff] - ((uint32_t)remove & kBigitMask);
+ bigits_[i + exponent_diff] = difference & kBigitMask;
+ borrow = static_cast<Chunk>((difference >> (kChunkSize - 1)) +
+ (remove >> kBigitSize));
+ }
+ for (int i = other.used_digits_ + exponent_diff; i < used_digits_; ++i) {
+ if (borrow == 0)
+ return;
+ Chunk difference = bigits_[i] - borrow;
+ bigits_[i] = difference & kBigitMask;
+ borrow = difference >> (kChunkSize - 1);
+ }
+ Clamp();
+}
+
+} // namespace double_conversion
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/dtoa/bignum.h b/chromium/third_party/blink/renderer/platform/wtf/dtoa/bignum.h
new file mode 100644
index 00000000000..1bc51a2e513
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/dtoa/bignum.h
@@ -0,0 +1,146 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_BIGNUM_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_BIGNUM_H_
+
+#include "third_party/blink/renderer/platform/wtf/dtoa/utils.h"
+
+namespace WTF {
+
+namespace double_conversion {
+
+class Bignum {
+ public:
+ // 3584 = 128 * 28. We can represent 2^3584 > 10^1000 accurately.
+ // This bignum can encode much bigger numbers, since it contains an
+ // exponent.
+ static const int kMaxSignificantBits = 3584;
+
+ Bignum();
+ void AssignUInt16(uint16_t value);
+ void AssignUInt64(uint64_t value);
+ void AssignBignum(const Bignum& other);
+
+ void AssignDecimalString(Vector<const char> value);
+ void AssignHexString(Vector<const char> value);
+
+ void AssignPowerUInt16(uint16_t base, int exponent);
+
+ void AddUInt16(uint16_t operand);
+ void AddUInt64(uint64_t operand);
+ void AddBignum(const Bignum& other);
+ // Precondition: this >= other.
+ void SubtractBignum(const Bignum& other);
+
+ void Square();
+ void ShiftLeft(int shift_amount);
+ void MultiplyByUInt32(uint32_t factor);
+ void MultiplyByUInt64(uint64_t factor);
+ void MultiplyByPowerOfTen(int exponent);
+ void Times10() { return MultiplyByUInt32(10); }
+ // Pseudocode:
+ // int result = this / other;
+ // this = this % other;
+ // In the worst case this function is in O(this/other).
+ uint16_t DivideModuloIntBignum(const Bignum& other);
+
+ bool ToHexString(char* buffer, int buffer_size) const;
+
+ static int Compare(const Bignum& a, const Bignum& b);
+ static bool Equal(const Bignum& a, const Bignum& b) {
+ return Compare(a, b) == 0;
+ }
+ static bool LessEqual(const Bignum& a, const Bignum& b) {
+ return Compare(a, b) <= 0;
+ }
+ static bool Less(const Bignum& a, const Bignum& b) {
+ return Compare(a, b) < 0;
+ }
+ // Returns Compare(a + b, c);
+ static int PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c);
+ // Returns a + b == c
+ static bool PlusEqual(const Bignum& a, const Bignum& b, const Bignum& c) {
+ return PlusCompare(a, b, c) == 0;
+ }
+ // Returns a + b <= c
+ static bool PlusLessEqual(const Bignum& a, const Bignum& b, const Bignum& c) {
+ return PlusCompare(a, b, c) <= 0;
+ }
+ // Returns a + b < c
+ static bool PlusLess(const Bignum& a, const Bignum& b, const Bignum& c) {
+ return PlusCompare(a, b, c) < 0;
+ }
+
+ private:
+ typedef uint32_t Chunk;
+ typedef uint64_t DoubleChunk;
+
+ static const int kChunkSize = sizeof(Chunk) * 8;
+ static const int kDoubleChunkSize = sizeof(DoubleChunk) * 8;
+ // With bigit size of 28 we loose some bits, but a double still fits easily
+ // into two chunks, and more importantly we can use the Comba multiplication.
+ static const int kBigitSize = 28;
+ static const Chunk kBigitMask = (1 << kBigitSize) - 1;
+ // Every instance allocates kBigitLength chunks on the stack. Bignums cannot
+ // grow. There are no checks if the stack-allocated space is sufficient.
+ static const int kBigitCapacity = kMaxSignificantBits / kBigitSize;
+
+ void EnsureCapacity(int size) {
+ if (size > kBigitCapacity) {
+ UNREACHABLE();
+ }
+ }
+ void Align(const Bignum& other);
+ void Clamp();
+ bool IsClamped() const;
+ void Zero();
+ // Requires this to have enough capacity (no tests done).
+ // Updates used_digits_ if necessary.
+ // shift_amount must be < kBigitSize.
+ void BigitsShiftLeft(int shift_amount);
+ // BigitLength includes the "hidden" digits encoded in the exponent.
+ int BigitLength() const { return used_digits_ + exponent_; }
+ Chunk BigitAt(int index) const;
+ void SubtractTimes(const Bignum& other, int factor);
+
+ Chunk bigits_buffer_[kBigitCapacity];
+ // A vector backed by bigits_buffer_. This way accesses to the array are
+ // checked for out-of-bounds errors.
+ Vector<Chunk> bigits_;
+ int used_digits_;
+ // The Bignum's value equals value(bigits_) * 2^(exponent_ * kBigitSize).
+ int exponent_;
+
+ DISALLOW_COPY_AND_ASSIGN(Bignum);
+};
+
+} // namespace double_conversion
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_BIGNUM_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/dtoa/cached-powers.cc b/chromium/third_party/blink/renderer/platform/wtf/dtoa/cached-powers.cc
new file mode 100644
index 00000000000..653b996f340
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/dtoa/cached-powers.cc
@@ -0,0 +1,204 @@
+// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "third_party/blink/renderer/platform/wtf/dtoa/cached-powers.h"
+
+#include <limits.h>
+#include <math.h>
+#include <stdarg.h>
+
+#include "third_party/blink/renderer/platform/wtf/dtoa/utils.h"
+
+namespace WTF {
+
+namespace double_conversion {
+
+struct CachedPower {
+ uint64_t significand;
+ int16_t binary_exponent;
+ int16_t decimal_exponent;
+};
+
+static const double kD_1_LOG2_10 = 0.30102999566398114; // 1 / lg(10)
+static const CachedPower kCachedPowers[] = {
+ {UINT64_2PART_C(0xfa8fd5a0, 081c0288), -1220, -348},
+ {UINT64_2PART_C(0xbaaee17f, a23ebf76), -1193, -340},
+ {UINT64_2PART_C(0x8b16fb20, 3055ac76), -1166, -332},
+ {UINT64_2PART_C(0xcf42894a, 5dce35ea), -1140, -324},
+ {UINT64_2PART_C(0x9a6bb0aa, 55653b2d), -1113, -316},
+ {UINT64_2PART_C(0xe61acf03, 3d1a45df), -1087, -308},
+ {UINT64_2PART_C(0xab70fe17, c79ac6ca), -1060, -300},
+ {UINT64_2PART_C(0xff77b1fc, bebcdc4f), -1034, -292},
+ {UINT64_2PART_C(0xbe5691ef, 416bd60c), -1007, -284},
+ {UINT64_2PART_C(0x8dd01fad, 907ffc3c), -980, -276},
+ {UINT64_2PART_C(0xd3515c28, 31559a83), -954, -268},
+ {UINT64_2PART_C(0x9d71ac8f, ada6c9b5), -927, -260},
+ {UINT64_2PART_C(0xea9c2277, 23ee8bcb), -901, -252},
+ {UINT64_2PART_C(0xaecc4991, 4078536d), -874, -244},
+ {UINT64_2PART_C(0x823c1279, 5db6ce57), -847, -236},
+ {UINT64_2PART_C(0xc2109436, 4dfb5637), -821, -228},
+ {UINT64_2PART_C(0x9096ea6f, 3848984f), -794, -220},
+ {UINT64_2PART_C(0xd77485cb, 25823ac7), -768, -212},
+ {UINT64_2PART_C(0xa086cfcd, 97bf97f4), -741, -204},
+ {UINT64_2PART_C(0xef340a98, 172aace5), -715, -196},
+ {UINT64_2PART_C(0xb23867fb, 2a35b28e), -688, -188},
+ {UINT64_2PART_C(0x84c8d4df, d2c63f3b), -661, -180},
+ {UINT64_2PART_C(0xc5dd4427, 1ad3cdba), -635, -172},
+ {UINT64_2PART_C(0x936b9fce, bb25c996), -608, -164},
+ {UINT64_2PART_C(0xdbac6c24, 7d62a584), -582, -156},
+ {UINT64_2PART_C(0xa3ab6658, 0d5fdaf6), -555, -148},
+ {UINT64_2PART_C(0xf3e2f893, dec3f126), -529, -140},
+ {UINT64_2PART_C(0xb5b5ada8, aaff80b8), -502, -132},
+ {UINT64_2PART_C(0x87625f05, 6c7c4a8b), -475, -124},
+ {UINT64_2PART_C(0xc9bcff60, 34c13053), -449, -116},
+ {UINT64_2PART_C(0x964e858c, 91ba2655), -422, -108},
+ {UINT64_2PART_C(0xdff97724, 70297ebd), -396, -100},
+ {UINT64_2PART_C(0xa6dfbd9f, b8e5b88f), -369, -92},
+ {UINT64_2PART_C(0xf8a95fcf, 88747d94), -343, -84},
+ {UINT64_2PART_C(0xb9447093, 8fa89bcf), -316, -76},
+ {UINT64_2PART_C(0x8a08f0f8, bf0f156b), -289, -68},
+ {UINT64_2PART_C(0xcdb02555, 653131b6), -263, -60},
+ {UINT64_2PART_C(0x993fe2c6, d07b7fac), -236, -52},
+ {UINT64_2PART_C(0xe45c10c4, 2a2b3b06), -210, -44},
+ {UINT64_2PART_C(0xaa242499, 697392d3), -183, -36},
+ {UINT64_2PART_C(0xfd87b5f2, 8300ca0e), -157, -28},
+ {UINT64_2PART_C(0xbce50864, 92111aeb), -130, -20},
+ {UINT64_2PART_C(0x8cbccc09, 6f5088cc), -103, -12},
+ {UINT64_2PART_C(0xd1b71758, e219652c), -77, -4},
+ {UINT64_2PART_C(0x9c400000, 00000000), -50, 4},
+ {UINT64_2PART_C(0xe8d4a510, 00000000), -24, 12},
+ {UINT64_2PART_C(0xad78ebc5, ac620000), 3, 20},
+ {UINT64_2PART_C(0x813f3978, f8940984), 30, 28},
+ {UINT64_2PART_C(0xc097ce7b, c90715b3), 56, 36},
+ {UINT64_2PART_C(0x8f7e32ce, 7bea5c70), 83, 44},
+ {UINT64_2PART_C(0xd5d238a4, abe98068), 109, 52},
+ {UINT64_2PART_C(0x9f4f2726, 179a2245), 136, 60},
+ {UINT64_2PART_C(0xed63a231, d4c4fb27), 162, 68},
+ {UINT64_2PART_C(0xb0de6538, 8cc8ada8), 189, 76},
+ {UINT64_2PART_C(0x83c7088e, 1aab65db), 216, 84},
+ {UINT64_2PART_C(0xc45d1df9, 42711d9a), 242, 92},
+ {UINT64_2PART_C(0x924d692c, a61be758), 269, 100},
+ {UINT64_2PART_C(0xda01ee64, 1a708dea), 295, 108},
+ {UINT64_2PART_C(0xa26da399, 9aef774a), 322, 116},
+ {UINT64_2PART_C(0xf209787b, b47d6b85), 348, 124},
+ {UINT64_2PART_C(0xb454e4a1, 79dd1877), 375, 132},
+ {UINT64_2PART_C(0x865b8692, 5b9bc5c2), 402, 140},
+ {UINT64_2PART_C(0xc83553c5, c8965d3d), 428, 148},
+ {UINT64_2PART_C(0x952ab45c, fa97a0b3), 455, 156},
+ {UINT64_2PART_C(0xde469fbd, 99a05fe3), 481, 164},
+ {UINT64_2PART_C(0xa59bc234, db398c25), 508, 172},
+ {UINT64_2PART_C(0xf6c69a72, a3989f5c), 534, 180},
+ {UINT64_2PART_C(0xb7dcbf53, 54e9bece), 561, 188},
+ {UINT64_2PART_C(0x88fcf317, f22241e2), 588, 196},
+ {UINT64_2PART_C(0xcc20ce9b, d35c78a5), 614, 204},
+ {UINT64_2PART_C(0x98165af3, 7b2153df), 641, 212},
+ {UINT64_2PART_C(0xe2a0b5dc, 971f303a), 667, 220},
+ {UINT64_2PART_C(0xa8d9d153, 5ce3b396), 694, 228},
+ {UINT64_2PART_C(0xfb9b7cd9, a4a7443c), 720, 236},
+ {UINT64_2PART_C(0xbb764c4c, a7a44410), 747, 244},
+ {UINT64_2PART_C(0x8bab8eef, b6409c1a), 774, 252},
+ {UINT64_2PART_C(0xd01fef10, a657842c), 800, 260},
+ {UINT64_2PART_C(0x9b10a4e5, e9913129), 827, 268},
+ {UINT64_2PART_C(0xe7109bfb, a19c0c9d), 853, 276},
+ {UINT64_2PART_C(0xac2820d9, 623bf429), 880, 284},
+ {UINT64_2PART_C(0x80444b5e, 7aa7cf85), 907, 292},
+ {UINT64_2PART_C(0xbf21e440, 03acdd2d), 933, 300},
+ {UINT64_2PART_C(0x8e679c2f, 5e44ff8f), 960, 308},
+ {UINT64_2PART_C(0xd433179d, 9c8cb841), 986, 316},
+ {UINT64_2PART_C(0x9e19db92, b4e31ba9), 1013, 324},
+ {UINT64_2PART_C(0xeb96bf6e, badf77d9), 1039, 332},
+ {UINT64_2PART_C(0xaf87023b, 9bf0ee6b), 1066, 340},
+};
+static const int kCachedPowersOffset =
+ 348; // -kCachedPowers[0].decimal_exponent
+
+const int PowersOfTenCache::kDecimalExponentDistance =
+ 8; // kCachedPowers[1].decimal_exponent - kCachedPowers[0].decimal_exponent
+const int PowersOfTenCache::kMinDecimalExponent =
+ -348; // kCachedPowers[0].decimal_exponent
+const int PowersOfTenCache::kMaxDecimalExponent =
+ 340; // kCachedPowers[kCachedPowersLength - 1].decimal_exponent
+
+#if DCHECK_IS_ON()
+static const int kCachedPowersLength = ARRAY_SIZE(kCachedPowers);
+
+// Check that the static constants match the values in kCachedPowers.
+static void ValidateStaticConstants() {
+ DCHECK_EQ(kCachedPowersOffset, -kCachedPowers[0].decimal_exponent);
+ DCHECK_EQ(
+ PowersOfTenCache::kDecimalExponentDistance,
+ (kCachedPowers[1].decimal_exponent - kCachedPowers[0].decimal_exponent));
+ DCHECK_EQ(PowersOfTenCache::kMinDecimalExponent,
+ kCachedPowers[0].decimal_exponent);
+ DCHECK_EQ(PowersOfTenCache::kMaxDecimalExponent,
+ kCachedPowers[kCachedPowersLength - 1].decimal_exponent);
+}
+#endif
+
+void PowersOfTenCache::GetCachedPowerForBinaryExponentRange(
+ int min_exponent,
+ int max_exponent,
+ DiyFp* power,
+ int* decimal_exponent) {
+#if DCHECK_IS_ON()
+ ValidateStaticConstants();
+#endif
+ const int kQ = DiyFp::kSignificandSize;
+ double k = ceil((min_exponent + kQ - 1) * kD_1_LOG2_10);
+ int foo = kCachedPowersOffset;
+ int index = (foo + static_cast<int>(k) - 1) / kDecimalExponentDistance + 1;
+ DCHECK_LE(0, index);
+#if DCHECK_IS_ON()
+ DCHECK_LT(index, kCachedPowersLength);
+#endif
+ CachedPower cached_power = kCachedPowers[index];
+ DCHECK_LE(min_exponent, cached_power.binary_exponent);
+ DCHECK_LE(cached_power.binary_exponent, max_exponent);
+ *decimal_exponent = cached_power.decimal_exponent;
+ *power = DiyFp(cached_power.significand, cached_power.binary_exponent);
+}
+
+void PowersOfTenCache::GetCachedPowerForDecimalExponent(int requested_exponent,
+ DiyFp* power,
+ int* found_exponent) {
+ DCHECK_LE(kMinDecimalExponent, requested_exponent);
+ DCHECK_LT(requested_exponent, kMaxDecimalExponent + kDecimalExponentDistance);
+#if DCHECK_IS_ON()
+ ValidateStaticConstants();
+#endif
+ int index =
+ (requested_exponent + kCachedPowersOffset) / kDecimalExponentDistance;
+ CachedPower cached_power = kCachedPowers[index];
+ *power = DiyFp(cached_power.significand, cached_power.binary_exponent);
+ *found_exponent = cached_power.decimal_exponent;
+ DCHECK_LE(*found_exponent, requested_exponent);
+ DCHECK_LT(requested_exponent, *found_exponent + kDecimalExponentDistance);
+}
+
+} // namespace double_conversion
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/dtoa/cached-powers.h b/chromium/third_party/blink/renderer/platform/wtf/dtoa/cached-powers.h
new file mode 100644
index 00000000000..fec5744a4fa
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/dtoa/cached-powers.h
@@ -0,0 +1,66 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_CACHED_POWERS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_CACHED_POWERS_H_
+
+#include "third_party/blink/renderer/platform/wtf/dtoa/diy-fp.h"
+
+namespace WTF {
+
+namespace double_conversion {
+
+class PowersOfTenCache {
+ public:
+ // Not all powers of ten are cached. The decimal exponent of two neighboring
+ // cached numbers will differ by kDecimalExponentDistance.
+ static const int kDecimalExponentDistance;
+
+ static const int kMinDecimalExponent;
+ static const int kMaxDecimalExponent;
+
+ // Returns a cached power-of-ten with a binary exponent in the range
+ // [min_exponent; max_exponent] (boundaries included).
+ static void GetCachedPowerForBinaryExponentRange(int min_exponent,
+ int max_exponent,
+ DiyFp* power,
+ int* decimal_exponent);
+
+ // Returns a cached power of ten x ~= 10^k such that
+ // k <= decimal_exponent < k + kCachedPowersDecimalDistance.
+ // The given decimal_exponent must satisfy
+ // kMinDecimalExponent <= requested_exponent, and
+ // requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance.
+ static void GetCachedPowerForDecimalExponent(int requested_exponent,
+ DiyFp* power,
+ int* found_exponent);
+};
+} // namespace double_conversion
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_CACHED_POWERS_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/dtoa/diy-fp.cc b/chromium/third_party/blink/renderer/platform/wtf/dtoa/diy-fp.cc
new file mode 100644
index 00000000000..0b28f3105eb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/dtoa/diy-fp.cc
@@ -0,0 +1,61 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "third_party/blink/renderer/platform/wtf/dtoa/diy-fp.h"
+
+#include "third_party/blink/renderer/platform/wtf/dtoa/utils.h"
+
+namespace WTF {
+
+namespace double_conversion {
+
+void DiyFp::Multiply(const DiyFp& other) {
+ // Simply "emulates" a 128 bit multiplication.
+ // However: the resulting number only contains 64 bits. The least
+ // significant 64 bits are only used for rounding the most significant 64
+ // bits.
+ const uint64_t kM32 = 0xFFFFFFFFU;
+ uint64_t a = f_ >> 32;
+ uint64_t b = f_ & kM32;
+ uint64_t c = other.f_ >> 32;
+ uint64_t d = other.f_ & kM32;
+ uint64_t ac = a * c;
+ uint64_t bc = b * c;
+ uint64_t ad = a * d;
+ uint64_t bd = b * d;
+ uint64_t tmp = (bd >> 32) + (ad & kM32) + (bc & kM32);
+ // By adding 1U << 31 to tmp we round the final result.
+ // Halfway cases will be round up.
+ tmp += 1U << 31;
+ uint64_t result_f = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32);
+ e_ += other.e_ + 64;
+ f_ = result_f;
+}
+
+} // namespace double_conversion
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/dtoa/diy-fp.h b/chromium/third_party/blink/renderer/platform/wtf/dtoa/diy-fp.h
new file mode 100644
index 00000000000..f5199c9fe81
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/dtoa/diy-fp.h
@@ -0,0 +1,121 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_DIY_FP_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_DIY_FP_H_
+
+#include "third_party/blink/renderer/platform/wtf/dtoa/utils.h"
+
+namespace WTF {
+
+namespace double_conversion {
+
+// This "Do It Yourself Floating Point" class implements a floating-point number
+// with a uint64 significand and an int exponent. Normalized DiyFp numbers will
+// have the most significant bit of the significand set.
+// Multiplication and Subtraction do not normalize their results.
+// DiyFp are not designed to contain special doubles (NaN and Infinity).
+class DiyFp {
+ public:
+ static const int kSignificandSize = 64;
+
+ DiyFp() : f_(0), e_(0) {}
+ DiyFp(uint64_t f, int e) : f_(f), e_(e) {}
+
+ // this = this - other.
+ // The exponents of both numbers must be the same and the significand of this
+ // must be bigger than the significand of other.
+ // The result will not be normalized.
+ void Subtract(const DiyFp& other) {
+ DCHECK_EQ(e_, other.e_);
+ DCHECK_GE(f_, other.f_);
+ f_ -= other.f_;
+ }
+
+ // Returns a - b.
+ // The exponents of both numbers must be the same and this must be bigger
+ // than other. The result will not be normalized.
+ static DiyFp Minus(const DiyFp& a, const DiyFp& b) {
+ DiyFp result = a;
+ result.Subtract(b);
+ return result;
+ }
+
+ // this = this * other.
+ void Multiply(const DiyFp& other);
+
+ // returns a * b;
+ static DiyFp Times(const DiyFp& a, const DiyFp& b) {
+ DiyFp result = a;
+ result.Multiply(b);
+ return result;
+ }
+
+ void Normalize() {
+ DCHECK_NE(f_, 0u);
+ uint64_t f = f_;
+ int e = e_;
+
+ // This method is mainly called for normalizing boundaries. In general
+ // boundaries need to be shifted by 10 bits. We thus optimize for this case.
+ const uint64_t kK10MSBits = UINT64_2PART_C(0xFFC00000, 00000000);
+ while ((f & kK10MSBits) == 0) {
+ f <<= 10;
+ e -= 10;
+ }
+ while ((f & kUint64MSB) == 0) {
+ f <<= 1;
+ e--;
+ }
+ f_ = f;
+ e_ = e;
+ }
+
+ static DiyFp Normalize(const DiyFp& a) {
+ DiyFp result = a;
+ result.Normalize();
+ return result;
+ }
+
+ uint64_t F() const { return f_; }
+ int E() const { return e_; }
+
+ void set_f(uint64_t new_value) { f_ = new_value; }
+ void set_e(int new_value) { e_ = new_value; }
+
+ private:
+ static const uint64_t kUint64MSB = UINT64_2PART_C(0x80000000, 00000000);
+
+ uint64_t f_;
+ int e_;
+};
+
+} // namespace double_conversion
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_DIY_FP_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/dtoa/double-conversion.cc b/chromium/third_party/blink/renderer/platform/wtf/dtoa/double-conversion.cc
new file mode 100644
index 00000000000..48821b40f43
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/dtoa/double-conversion.cc
@@ -0,0 +1,596 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "third_party/blink/renderer/platform/wtf/dtoa/double-conversion.h"
+
+#include <limits.h>
+#include <math.h>
+
+#include "third_party/blink/renderer/platform/wtf/dtoa/bignum-dtoa.h"
+#include "third_party/blink/renderer/platform/wtf/dtoa/double.h"
+#include "third_party/blink/renderer/platform/wtf/dtoa/fast-dtoa.h"
+#include "third_party/blink/renderer/platform/wtf/dtoa/fixed-dtoa.h"
+#include "third_party/blink/renderer/platform/wtf/dtoa/strtod.h"
+#include "third_party/blink/renderer/platform/wtf/dtoa/utils.h"
+
+namespace WTF {
+
+namespace double_conversion {
+
+const DoubleToStringConverter& DoubleToStringConverter::EcmaScriptConverter() {
+ int flags = UNIQUE_ZERO | EMIT_POSITIVE_EXPONENT_SIGN;
+ static DoubleToStringConverter converter(flags, "Infinity", "NaN", 'e', -6,
+ 21, 6, 0);
+ return converter;
+}
+
+bool DoubleToStringConverter::HandleSpecialValues(
+ double value,
+ StringBuilder* result_builder) const {
+ Double double_inspect(value);
+ if (double_inspect.IsInfinite()) {
+ if (infinity_symbol_ == nullptr)
+ return false;
+ if (value < 0) {
+ result_builder->AddCharacter('-');
+ }
+ result_builder->AddString(infinity_symbol_);
+ return true;
+ }
+ if (double_inspect.IsNan()) {
+ if (nan_symbol_ == nullptr)
+ return false;
+ result_builder->AddString(nan_symbol_);
+ return true;
+ }
+ return false;
+}
+
+void DoubleToStringConverter::CreateExponentialRepresentation(
+ const char* decimal_digits,
+ int length,
+ int exponent,
+ StringBuilder* result_builder) const {
+ DCHECK_NE(length, 0);
+ result_builder->AddCharacter(decimal_digits[0]);
+ if (length != 1) {
+ result_builder->AddCharacter('.');
+ result_builder->AddSubstring(&decimal_digits[1], length - 1);
+ }
+ result_builder->AddCharacter(exponent_character_);
+ if (exponent < 0) {
+ result_builder->AddCharacter('-');
+ exponent = -exponent;
+ } else {
+ if ((flags_ & EMIT_POSITIVE_EXPONENT_SIGN) != 0) {
+ result_builder->AddCharacter('+');
+ }
+ }
+ if (exponent == 0) {
+ result_builder->AddCharacter('0');
+ return;
+ }
+ DCHECK_LT(exponent, 1e4);
+ const int kMaxExponentLength = 5;
+ char buffer[kMaxExponentLength + 1];
+ int first_char_pos = kMaxExponentLength;
+ buffer[first_char_pos] = '\0';
+ while (exponent > 0) {
+ buffer[--first_char_pos] = '0' + (exponent % 10);
+ exponent /= 10;
+ }
+ result_builder->AddSubstring(&buffer[first_char_pos],
+ kMaxExponentLength - first_char_pos);
+}
+
+void DoubleToStringConverter::CreateDecimalRepresentation(
+ const char* decimal_digits,
+ int length,
+ int decimal_point,
+ int digits_after_point,
+ StringBuilder* result_builder) const {
+ // Create a representation that is padded with zeros if needed.
+ if (decimal_point <= 0) {
+ // "0.00000decimal_rep".
+ result_builder->AddCharacter('0');
+ if (digits_after_point > 0) {
+ result_builder->AddCharacter('.');
+ result_builder->AddPadding('0', -decimal_point);
+ DCHECK_LE(length, digits_after_point - (-decimal_point));
+ result_builder->AddSubstring(decimal_digits, length);
+ int remaining_digits = digits_after_point - (-decimal_point) - length;
+ result_builder->AddPadding('0', remaining_digits);
+ }
+ } else if (decimal_point >= length) {
+ // "decimal_rep0000.00000" or "decimal_rep.0000"
+ result_builder->AddSubstring(decimal_digits, length);
+ result_builder->AddPadding('0', decimal_point - length);
+ if (digits_after_point > 0) {
+ result_builder->AddCharacter('.');
+ result_builder->AddPadding('0', digits_after_point);
+ }
+ } else {
+ // "decima.l_rep000"
+ DCHECK_GT(digits_after_point, 0);
+ result_builder->AddSubstring(decimal_digits, decimal_point);
+ result_builder->AddCharacter('.');
+ DCHECK_LE(length - decimal_point, digits_after_point);
+ result_builder->AddSubstring(&decimal_digits[decimal_point],
+ length - decimal_point);
+ int remaining_digits = digits_after_point - (length - decimal_point);
+ result_builder->AddPadding('0', remaining_digits);
+ }
+ if (digits_after_point == 0) {
+ if ((flags_ & EMIT_TRAILING_DECIMAL_POINT) != 0) {
+ result_builder->AddCharacter('.');
+ }
+ if ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) {
+ result_builder->AddCharacter('0');
+ }
+ }
+}
+
+bool DoubleToStringConverter::ToShortest(double value,
+ StringBuilder* result_builder) const {
+ if (Double(value).IsSpecial()) {
+ return HandleSpecialValues(value, result_builder);
+ }
+
+ int decimal_point;
+ bool sign;
+ const int kDecimalRepCapacity = kBase10MaximalLength + 1;
+ char decimal_rep[kDecimalRepCapacity];
+ int decimal_rep_length;
+
+ DoubleToAscii(value, SHORTEST, 0, decimal_rep, kDecimalRepCapacity, &sign,
+ &decimal_rep_length, &decimal_point);
+
+ bool unique_zero = (flags_ & UNIQUE_ZERO) != 0;
+ if (sign && (value != 0.0 || !unique_zero)) {
+ result_builder->AddCharacter('-');
+ }
+
+ int exponent = decimal_point - 1;
+ if ((decimal_in_shortest_low_ <= exponent) &&
+ (exponent < decimal_in_shortest_high_)) {
+ CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point,
+ Max(0, decimal_rep_length - decimal_point),
+ result_builder);
+ } else {
+ CreateExponentialRepresentation(decimal_rep, decimal_rep_length, exponent,
+ result_builder);
+ }
+ return true;
+}
+
+bool DoubleToStringConverter::ToFixed(double value,
+ int requested_digits,
+ StringBuilder* result_builder) const {
+ DCHECK_EQ(kMaxFixedDigitsBeforePoint, 60);
+ const double kFirstNonFixed = 1e60;
+
+ if (Double(value).IsSpecial()) {
+ return HandleSpecialValues(value, result_builder);
+ }
+
+ if (requested_digits > kMaxFixedDigitsAfterPoint)
+ return false;
+ if (value >= kFirstNonFixed || value <= -kFirstNonFixed)
+ return false;
+
+ // Find a sufficiently precise decimal representation of n.
+ int decimal_point;
+ bool sign;
+ // Add space for the '\0' byte.
+ const int kDecimalRepCapacity =
+ kMaxFixedDigitsBeforePoint + kMaxFixedDigitsAfterPoint + 1;
+ char decimal_rep[kDecimalRepCapacity];
+ int decimal_rep_length;
+ DoubleToAscii(value, FIXED, requested_digits, decimal_rep,
+ kDecimalRepCapacity, &sign, &decimal_rep_length,
+ &decimal_point);
+
+ bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0);
+ if (sign && (value != 0.0 || !unique_zero)) {
+ result_builder->AddCharacter('-');
+ }
+
+ CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point,
+ requested_digits, result_builder);
+ return true;
+}
+
+bool DoubleToStringConverter::ToExponential(
+ double value,
+ int requested_digits,
+ StringBuilder* result_builder) const {
+ if (Double(value).IsSpecial()) {
+ return HandleSpecialValues(value, result_builder);
+ }
+
+ if (requested_digits < -1)
+ return false;
+ if (requested_digits > kMaxExponentialDigits)
+ return false;
+
+ int decimal_point;
+ bool sign;
+ // Add space for digit before the decimal point and the '\0' character.
+ const int kDecimalRepCapacity = kMaxExponentialDigits + 2;
+ DCHECK_GT(kDecimalRepCapacity, kBase10MaximalLength);
+ char decimal_rep[kDecimalRepCapacity];
+ int decimal_rep_length;
+
+ if (requested_digits == -1) {
+ DoubleToAscii(value, SHORTEST, 0, decimal_rep, kDecimalRepCapacity, &sign,
+ &decimal_rep_length, &decimal_point);
+ } else {
+ DoubleToAscii(value, PRECISION, requested_digits + 1, decimal_rep,
+ kDecimalRepCapacity, &sign, &decimal_rep_length,
+ &decimal_point);
+ DCHECK_LE(decimal_rep_length, requested_digits + 1);
+
+ for (int i = decimal_rep_length; i < requested_digits + 1; ++i) {
+ decimal_rep[i] = '0';
+ }
+ decimal_rep_length = requested_digits + 1;
+ }
+
+ bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0);
+ if (sign && (value != 0.0 || !unique_zero)) {
+ result_builder->AddCharacter('-');
+ }
+
+ int exponent = decimal_point - 1;
+ CreateExponentialRepresentation(decimal_rep, decimal_rep_length, exponent,
+ result_builder);
+ return true;
+}
+
+bool DoubleToStringConverter::ToPrecision(double value,
+ int precision,
+ StringBuilder* result_builder) const {
+ if (Double(value).IsSpecial()) {
+ return HandleSpecialValues(value, result_builder);
+ }
+
+ if (precision < kMinPrecisionDigits || precision > kMaxPrecisionDigits) {
+ return false;
+ }
+
+ // Find a sufficiently precise decimal representation of n.
+ int decimal_point;
+ bool sign;
+ // Add one for the terminating null character.
+ const int kDecimalRepCapacity = kMaxPrecisionDigits + 1;
+ char decimal_rep[kDecimalRepCapacity];
+ int decimal_rep_length;
+
+ DoubleToAscii(value, PRECISION, precision, decimal_rep, kDecimalRepCapacity,
+ &sign, &decimal_rep_length, &decimal_point);
+ DCHECK_LE(decimal_rep_length, precision);
+
+ bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0);
+ if (sign && (value != 0.0 || !unique_zero)) {
+ result_builder->AddCharacter('-');
+ }
+
+ // The exponent if we print the number as x.xxeyyy. That is with the
+ // decimal point after the first digit.
+ int exponent = decimal_point - 1;
+
+ int extra_zero = ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) ? 1 : 0;
+ if ((-decimal_point + 1 > max_leading_padding_zeroes_in_precision_mode_) ||
+ (decimal_point - precision + extra_zero >
+ max_trailing_padding_zeroes_in_precision_mode_)) {
+ // Fill buffer to contain 'precision' digits.
+ // Usually the buffer is already at the correct length, but 'DoubleToAscii'
+ // is allowed to return less characters.
+ for (int i = decimal_rep_length; i < precision; ++i) {
+ decimal_rep[i] = '0';
+ }
+
+ CreateExponentialRepresentation(decimal_rep, precision, exponent,
+ result_builder);
+ } else {
+ CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point,
+ Max(0, precision - decimal_point),
+ result_builder);
+ }
+ return true;
+}
+
+static BignumDtoaMode DtoaToBignumDtoaMode(
+ DoubleToStringConverter::DtoaMode dtoa_mode) {
+ switch (dtoa_mode) {
+ case DoubleToStringConverter::SHORTEST:
+ return BIGNUM_DTOA_SHORTEST;
+ case DoubleToStringConverter::FIXED:
+ return BIGNUM_DTOA_FIXED;
+ case DoubleToStringConverter::PRECISION:
+ return BIGNUM_DTOA_PRECISION;
+ default:
+ UNREACHABLE();
+ return BIGNUM_DTOA_SHORTEST; // To silence compiler.
+ }
+}
+
+void DoubleToStringConverter::DoubleToAscii(double v,
+ DtoaMode mode,
+ int requested_digits,
+ char* buffer,
+ int buffer_length,
+ bool* sign,
+ int* length,
+ int* point) {
+ Vector<char> vector(buffer, buffer_length);
+ DCHECK(!Double(v).IsSpecial());
+ DCHECK(mode == SHORTEST || requested_digits >= 0);
+
+ if (Double(v).Sign() < 0) {
+ *sign = true;
+ v = -v;
+ } else {
+ *sign = false;
+ }
+
+ if (mode == PRECISION && requested_digits == 0) {
+ vector[0] = '\0';
+ *length = 0;
+ return;
+ }
+
+ if (v == 0) {
+ vector[0] = '0';
+ vector[1] = '\0';
+ *length = 1;
+ *point = 1;
+ return;
+ }
+
+ bool fast_worked;
+ switch (mode) {
+ case SHORTEST:
+ fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST, 0, vector, length, point);
+ break;
+ case FIXED:
+ fast_worked = FastFixedDtoa(v, requested_digits, vector, length, point);
+ break;
+ case PRECISION:
+ fast_worked = FastDtoa(v, FAST_DTOA_PRECISION, requested_digits, vector,
+ length, point);
+ break;
+ default:
+ UNREACHABLE();
+ fast_worked = false;
+ }
+ if (fast_worked)
+ return;
+
+ // If the fast dtoa didn't succeed use the slower bignum version.
+ BignumDtoaMode bignum_mode = DtoaToBignumDtoaMode(mode);
+ BignumDtoa(v, bignum_mode, requested_digits, vector, length, point);
+ vector[*length] = '\0';
+}
+
+// Maximum number of significant digits in decimal representation.
+// The longest possible double in decimal representation is
+// (2^53 - 1) * 2 ^ -1074 that is (2 ^ 53 - 1) * 5 ^ 1074 / 10 ^ 1074
+// (768 digits). If we parse a number whose first digits are equal to a
+// mean of 2 adjacent doubles (that could have up to 769 digits) the result
+// must be rounded to the bigger one unless the tail consists of zeros, so
+// we don't need to preserve all the digits.
+const int kMaxSignificantDigits = 772;
+
+static double SignedZero(bool sign) {
+ return sign ? -0.0 : 0.0;
+}
+
+double StringToDoubleConverter::StringToDouble(
+ const char* input,
+ size_t length,
+ size_t* processed_characters_count) {
+ const char* current = input;
+ const char* end = input + length;
+
+ *processed_characters_count = 0;
+
+ // To make sure that iterator dereferencing is valid the following
+ // convention is used:
+ // 1. Each '++current' statement is followed by check for equality to 'end'.
+ // 3. If 'current' becomes equal to 'end' the function returns or goes to
+ // 'parsing_done'.
+ // 4. 'current' is not dereferenced after the 'parsing_done' label.
+ // 5. Code before 'parsing_done' may rely on 'current != end'.
+ if (current == end)
+ return 0.0;
+
+ // The longest form of simplified number is: "-<significant digits>.1eXXX\0".
+ const int kBufferSize = kMaxSignificantDigits + 10;
+ char buffer[kBufferSize]; // NOLINT: size is known at compile time.
+ int buffer_pos = 0;
+
+ // Exponent will be adjusted if insignificant digits of the integer part
+ // or insignificant leading zeros of the fractional part are dropped.
+ int exponent = 0;
+ int significant_digits = 0;
+ int insignificant_digits = 0;
+ bool nonzero_digit_dropped = false;
+ bool sign = false;
+
+ if (*current == '+' || *current == '-') {
+ sign = (*current == '-');
+ ++current;
+ if (current == end)
+ return 0.0;
+ }
+
+ bool leading_zero = false;
+ if (*current == '0') {
+ ++current;
+ if (current == end) {
+ *processed_characters_count = current - input;
+ return SignedZero(sign);
+ }
+
+ leading_zero = true;
+
+ // Ignore leading zeros in the integer part.
+ while (*current == '0') {
+ ++current;
+ if (current == end) {
+ *processed_characters_count = current - input;
+ return SignedZero(sign);
+ }
+ }
+ }
+
+ // Copy significant digits of the integer part (if any) to the buffer.
+ while (*current >= '0' && *current <= '9') {
+ if (significant_digits < kMaxSignificantDigits) {
+ DCHECK_LT(buffer_pos, kBufferSize);
+ buffer[buffer_pos++] = static_cast<char>(*current);
+ significant_digits++;
+ } else {
+ insignificant_digits++; // Move the digit into the exponential part.
+ nonzero_digit_dropped = nonzero_digit_dropped || *current != '0';
+ }
+ ++current;
+ if (current == end)
+ goto parsing_done;
+ }
+
+ if (*current == '.') {
+ ++current;
+ if (current == end) {
+ if (significant_digits == 0 && !leading_zero) {
+ return 0.0;
+ } else {
+ goto parsing_done;
+ }
+ }
+
+ if (significant_digits == 0) {
+ // Integer part consists of 0 or is absent. Significant digits start after
+ // leading zeros (if any).
+ while (*current == '0') {
+ ++current;
+ if (current == end) {
+ *processed_characters_count = current - input;
+ return SignedZero(sign);
+ }
+ exponent--; // Move this 0 into the exponent.
+ }
+ }
+
+ // There is a fractional part.
+ while (*current >= '0' && *current <= '9') {
+ if (significant_digits < kMaxSignificantDigits) {
+ DCHECK_LT(buffer_pos, kBufferSize);
+ buffer[buffer_pos++] = static_cast<char>(*current);
+ significant_digits++;
+ exponent--;
+ } else {
+ // Ignore insignificant digits in the fractional part.
+ nonzero_digit_dropped = nonzero_digit_dropped || *current != '0';
+ }
+ ++current;
+ if (current == end)
+ goto parsing_done;
+ }
+ }
+
+ if (!leading_zero && exponent == 0 && significant_digits == 0) {
+ // If leading_zeros is true then the string contains zeros.
+ // If exponent < 0 then string was [+-]\.0*...
+ // If significant_digits != 0 the string is not equal to 0.
+ // Otherwise there are no digits in the string.
+ return 0.0;
+ }
+
+ // Parse exponential part.
+ if (*current == 'e' || *current == 'E') {
+ ++current;
+ if (current == end) {
+ --current;
+ goto parsing_done;
+ }
+ char sign = 0;
+ if (*current == '+' || *current == '-') {
+ sign = static_cast<char>(*current);
+ ++current;
+ if (current == end) {
+ current -= 2;
+ goto parsing_done;
+ }
+ }
+
+ if (*current < '0' || *current > '9') {
+ if (sign)
+ --current;
+ --current;
+ goto parsing_done;
+ }
+
+ const int max_exponent = INT_MAX / 2;
+ DCHECK_LE(-max_exponent / 2, exponent);
+ DCHECK_LE(exponent, max_exponent / 2);
+ int num = 0;
+ do {
+ // Check overflow.
+ int digit = *current - '0';
+ if (num >= max_exponent / 10 &&
+ !(num == max_exponent / 10 && digit <= max_exponent % 10)) {
+ num = max_exponent;
+ } else {
+ num = num * 10 + digit;
+ }
+ ++current;
+ } while (current != end && *current >= '0' && *current <= '9');
+
+ exponent += (sign == '-' ? -num : num);
+ }
+
+parsing_done:
+ exponent += insignificant_digits;
+
+ if (nonzero_digit_dropped) {
+ buffer[buffer_pos++] = '1';
+ exponent--;
+ }
+
+ DCHECK_LT(buffer_pos, kBufferSize);
+ buffer[buffer_pos] = '\0';
+
+ double converted = Strtod(Vector<const char>(buffer, buffer_pos), exponent);
+ *processed_characters_count = current - input;
+ return sign ? -converted : converted;
+}
+
+} // namespace double_conversion
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/dtoa/double-conversion.h b/chromium/third_party/blink/renderer/platform/wtf/dtoa/double-conversion.h
new file mode 100644
index 00000000000..173d49999e7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/dtoa/double-conversion.h
@@ -0,0 +1,377 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_DOUBLE_CONVERSION_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_DOUBLE_CONVERSION_H_
+
+#include "third_party/blink/renderer/platform/wtf/dtoa/utils.h"
+
+namespace WTF {
+
+namespace double_conversion {
+
+class DoubleToStringConverter {
+ public:
+ // When calling ToFixed with a double > 10^kMaxFixedDigitsBeforePoint
+ // or a requested_digits parameter > kMaxFixedDigitsAfterPoint then the
+ // function returns false.
+ static const int kMaxFixedDigitsBeforePoint = 60;
+ static const int kMaxFixedDigitsAfterPoint = 60;
+
+ // When calling ToExponential with a requested_digits
+ // parameter > kMaxExponentialDigits then the function returns false.
+ static const int kMaxExponentialDigits = 120;
+
+ // When calling ToPrecision with a requested_digits
+ // parameter < kMinPrecisionDigits or requested_digits > kMaxPrecisionDigits
+ // then the function returns false.
+ static const int kMinPrecisionDigits = 1;
+ static const int kMaxPrecisionDigits = 120;
+
+ enum Flags {
+ NO_FLAGS = 0,
+ EMIT_POSITIVE_EXPONENT_SIGN = 1,
+ EMIT_TRAILING_DECIMAL_POINT = 2,
+ EMIT_TRAILING_ZERO_AFTER_POINT = 4,
+ UNIQUE_ZERO = 8
+ };
+
+ // Flags should be a bit-or combination of the possible Flags-enum.
+ // - NO_FLAGS: no special flags.
+ // - EMIT_POSITIVE_EXPONENT_SIGN: when the number is converted into exponent
+ // form, emits a '+' for positive exponents. Example: 1.2e+2.
+ // - EMIT_TRAILING_DECIMAL_POINT: when the input number is an integer and is
+ // converted into decimal format then a trailing decimal point is appended.
+ // Example: 2345.0 is converted to "2345.".
+ // - EMIT_TRAILING_ZERO_AFTER_POINT: in addition to a trailing decimal point
+ // emits a trailing '0'-character. This flag requires the
+ // EXMIT_TRAILING_DECIMAL_POINT flag.
+ // Example: 2345.0 is converted to "2345.0".
+ // - UNIQUE_ZERO: "-0.0" is converted to "0.0".
+ //
+ // Infinity symbol and nan_symbol provide the string representation for these
+ // special values. If the string is NULL and the special value is encountered
+ // then the conversion functions return false.
+ //
+ // The exponent_character is used in exponential representations. It is
+ // usually 'e' or 'E'.
+ //
+ // When converting to the shortest representation the converter will
+ // represent input numbers in decimal format if they are in the interval
+ // [10^decimal_in_shortest_low; 10^decimal_in_shortest_high[
+ // (lower boundary included, greater boundary excluded).
+ // Example: with decimal_in_shortest_low = -6 and
+ // decimal_in_shortest_high = 21:
+ // ToShortest(0.000001) -> "0.000001"
+ // ToShortest(0.0000001) -> "1e-7"
+ // ToShortest(111111111111111111111.0) -> "111111111111111110000"
+ // ToShortest(100000000000000000000.0) -> "100000000000000000000"
+ // ToShortest(1111111111111111111111.0) -> "1.1111111111111111e+21"
+ //
+ // When converting to precision mode the converter may add
+ // max_leading_padding_zeroes before returning the number in exponential
+ // format.
+ // Example with max_leading_padding_zeroes_in_precision_mode = 6.
+ // ToPrecision(0.0000012345, 2) -> "0.0000012"
+ // ToPrecision(0.00000012345, 2) -> "1.2e-7"
+ // Similarily the converter may add up to
+ // max_trailing_padding_zeroes_in_precision_mode in precision mode to avoid
+ // returning an exponential representation. A zero added by the
+ // EMIT_TRAILING_ZERO_AFTER_POINT flag is counted for this limit.
+ // Examples for max_trailing_padding_zeroes_in_precision_mode = 1:
+ // ToPrecision(230.0, 2) -> "230"
+ // ToPrecision(230.0, 2) -> "230." with EMIT_TRAILING_DECIMAL_POINT.
+ // ToPrecision(230.0, 2) -> "2.3e2" with EMIT_TRAILING_ZERO_AFTER_POINT.
+ DoubleToStringConverter(int flags,
+ const char* infinity_symbol,
+ const char* nan_symbol,
+ char exponent_character,
+ int decimal_in_shortest_low,
+ int decimal_in_shortest_high,
+ int max_leading_padding_zeroes_in_precision_mode,
+ int max_trailing_padding_zeroes_in_precision_mode)
+ : flags_(flags),
+ infinity_symbol_(infinity_symbol),
+ nan_symbol_(nan_symbol),
+ exponent_character_(exponent_character),
+ decimal_in_shortest_low_(decimal_in_shortest_low),
+ decimal_in_shortest_high_(decimal_in_shortest_high),
+ max_leading_padding_zeroes_in_precision_mode_(
+ max_leading_padding_zeroes_in_precision_mode),
+ max_trailing_padding_zeroes_in_precision_mode_(
+ max_trailing_padding_zeroes_in_precision_mode) {
+ // When 'trailing zero after the point' is set, then 'trailing point'
+ // must be set too.
+ DCHECK(((flags & EMIT_TRAILING_DECIMAL_POINT) != 0) ||
+ !((flags & EMIT_TRAILING_ZERO_AFTER_POINT) != 0));
+ }
+
+ // Returns a converter following the EcmaScript specification.
+ static const DoubleToStringConverter& EcmaScriptConverter();
+
+ // Computes the shortest string of digits that correctly represent the input
+ // number. Depending on decimal_in_shortest_low and decimal_in_shortest_high
+ // (see constructor) it then either returns a decimal representation, or an
+ // exponential representation.
+ // Example with decimal_in_shortest_low = -6,
+ // decimal_in_shortest_high = 21,
+ // EMIT_POSITIVE_EXPONENT_SIGN activated, and
+ // EMIT_TRAILING_DECIMAL_POINT deactived:
+ // ToShortest(0.000001) -> "0.000001"
+ // ToShortest(0.0000001) -> "1e-7"
+ // ToShortest(111111111111111111111.0) -> "111111111111111110000"
+ // ToShortest(100000000000000000000.0) -> "100000000000000000000"
+ // ToShortest(1111111111111111111111.0) -> "1.1111111111111111e+21"
+ //
+ // Note: the conversion may round the output if the returned string
+ // is accurate enough to uniquely identify the input-number.
+ // For example the most precise representation of the double 9e59 equals
+ // "899999999999999918767229449717619953810131273674690656206848", but
+ // the converter will return the shorter (but still correct) "9e59".
+ //
+ // Returns true if the conversion succeeds. The conversion always succeeds
+ // except when the input value is special and no infinity_symbol or
+ // nan_symbol has been given to the constructor.
+ bool ToShortest(double value, StringBuilder* result_builder) const;
+
+ // Computes a decimal representation with a fixed number of digits after the
+ // decimal point. The last emitted digit is rounded.
+ //
+ // Examples:
+ // ToFixed(3.12, 1) -> "3.1"
+ // ToFixed(3.1415, 3) -> "3.142"
+ // ToFixed(1234.56789, 4) -> "1234.5679"
+ // ToFixed(1.23, 5) -> "1.23000"
+ // ToFixed(0.1, 4) -> "0.1000"
+ // ToFixed(1e30, 2) -> "1000000000000000019884624838656.00"
+ // ToFixed(0.1, 30) -> "0.100000000000000005551115123126"
+ // ToFixed(0.1, 17) -> "0.10000000000000001"
+ //
+ // If requested_digits equals 0, then the tail of the result depends on
+ // the EMIT_TRAILING_DECIMAL_POINT and EMIT_TRAILING_ZERO_AFTER_POINT.
+ // Examples, for requested_digits == 0,
+ // let EMIT_TRAILING_DECIMAL_POINT and EMIT_TRAILING_ZERO_AFTER_POINT be
+ // - false and false: then 123.45 -> 123
+ // 0.678 -> 1
+ // - true and false: then 123.45 -> 123.
+ // 0.678 -> 1.
+ // - true and true: then 123.45 -> 123.0
+ // 0.678 -> 1.0
+ //
+ // Returns true if the conversion succeeds. The conversion always succeeds
+ // except for the following cases:
+ // - the input value is special and no infinity_symbol or nan_symbol has
+ // been provided to the constructor,
+ // - 'value' > 10^kMaxFixedDigitsBeforePoint, or
+ // - 'requested_digits' > kMaxFixedDigitsAfterPoint.
+ // The last two conditions imply that the result will never contain more than
+ // 1 + kMaxFixedDigitsBeforePoint + 1 + kMaxFixedDigitsAfterPoint characters
+ // (one additional character for the sign, and one for the decimal point).
+ bool ToFixed(double value,
+ int requested_digits,
+ StringBuilder* result_builder) const;
+
+ // Computes a representation in exponential format with requested_digits
+ // after the decimal point. The last emitted digit is rounded.
+ // If requested_digits equals -1, then the shortest exponential representation
+ // is computed.
+ //
+ // Examples with EMIT_POSITIVE_EXPONENT_SIGN deactivated, and
+ // exponent_character set to 'e'.
+ // ToExponential(3.12, 1) -> "3.1e0"
+ // ToExponential(5.0, 3) -> "5.000e0"
+ // ToExponential(0.001, 2) -> "1.00e-3"
+ // ToExponential(3.1415, -1) -> "3.1415e0"
+ // ToExponential(3.1415, 4) -> "3.1415e0"
+ // ToExponential(3.1415, 3) -> "3.142e0"
+ // ToExponential(123456789000000, 3) -> "1.235e14"
+ // ToExponential(1000000000000000019884624838656.0, -1) -> "1e30"
+ // ToExponential(1000000000000000019884624838656.0, 32) ->
+ // "1.00000000000000001988462483865600e30"
+ // ToExponential(1234, 0) -> "1e3"
+ //
+ // Returns true if the conversion succeeds. The conversion always succeeds
+ // except for the following cases:
+ // - the input value is special and no infinity_symbol or nan_symbol has
+ // been provided to the constructor,
+ // - 'requested_digits' > kMaxExponentialDigits.
+ // The last condition implies that the result will never contain more than
+ // kMaxExponentialDigits + 8 characters (the sign, the digit before the
+ // decimal point, the decimal point, the exponent character, the
+ // exponent's sign, and at most 3 exponent digits).
+ bool ToExponential(double value,
+ int requested_digits,
+ StringBuilder* result_builder) const;
+
+ // Computes 'precision' leading digits of the given 'value' and returns them
+ // either in exponential or decimal format, depending on
+ // max_{leading|trailing}_padding_zeroes_in_precision_mode (given to the
+ // constructor).
+ // The last computed digit is rounded.
+ //
+ // Example with max_leading_padding_zeroes_in_precision_mode = 6.
+ // ToPrecision(0.0000012345, 2) -> "0.0000012"
+ // ToPrecision(0.00000012345, 2) -> "1.2e-7"
+ // Similarily the converter may add up to
+ // max_trailing_padding_zeroes_in_precision_mode in precision mode to avoid
+ // returning an exponential representation. A zero added by the
+ // EMIT_TRAILING_ZERO_AFTER_POINT flag is counted for this limit.
+ // Examples for max_trailing_padding_zeroes_in_precision_mode = 1:
+ // ToPrecision(230.0, 2) -> "230"
+ // ToPrecision(230.0, 2) -> "230." with EMIT_TRAILING_DECIMAL_POINT.
+ // ToPrecision(230.0, 2) -> "2.3e2" with EMIT_TRAILING_ZERO_AFTER_POINT.
+ // Examples for max_trailing_padding_zeroes_in_precision_mode = 3, and no
+ // EMIT_TRAILING_ZERO_AFTER_POINT:
+ // ToPrecision(123450.0, 6) -> "123450"
+ // ToPrecision(123450.0, 5) -> "123450"
+ // ToPrecision(123450.0, 4) -> "123500"
+ // ToPrecision(123450.0, 3) -> "123000"
+ // ToPrecision(123450.0, 2) -> "1.2e5"
+ //
+ // Returns true if the conversion succeeds. The conversion always succeeds
+ // except for the following cases:
+ // - the input value is special and no infinity_symbol or nan_symbol has
+ // been provided to the constructor,
+ // - precision < kMinPericisionDigits
+ // - precision > kMaxPrecisionDigits
+ // The last condition implies that the result will never contain more than
+ // kMaxPrecisionDigits + 7 characters (the sign, the decimal point, the
+ // exponent character, the exponent's sign, and at most 3 exponent digits).
+ bool ToPrecision(double value,
+ int precision,
+ StringBuilder* result_builder) const;
+
+ enum DtoaMode {
+ // Produce the shortest correct representation.
+ // For example the output of 0.299999999999999988897 is (the less accurate
+ // but correct) 0.3.
+ SHORTEST,
+ // Produce a fixed number of digits after the decimal point.
+ // For instance fixed(0.1, 4) becomes 0.1000
+ // If the input number is big, the output will be big.
+ FIXED,
+ // Fixed number of digits (independent of the decimal point).
+ PRECISION
+ };
+
+ // The maximal number of digits that are needed to emit a double in base 10.
+ // A higher precision can be achieved by using more digits, but the shortest
+ // accurate representation of any double will never use more digits than
+ // kBase10MaximalLength.
+ // Note that DoubleToAscii null-terminates its input. So the given buffer
+ // should be at least kBase10MaximalLength + 1 characters long.
+ static const int kBase10MaximalLength = 17;
+
+ // Converts the given double 'v' to ascii.
+ // The result should be interpreted as buffer * 10^(point-length).
+ //
+ // The output depends on the given mode:
+ // - SHORTEST: produce the least amount of digits for which the internal
+ // identity requirement is still satisfied. If the digits are printed
+ // (together with the correct exponent) then reading this number will give
+ // 'v' again. The buffer will choose the representation that is closest to
+ // 'v'. If there are two at the same distance, than the one farther away
+ // from 0 is chosen (halfway cases - ending with 5 - are rounded up).
+ // In this mode the 'requested_digits' parameter is ignored.
+ // - FIXED: produces digits necessary to print a given number with
+ // 'requested_digits' digits after the decimal point. The produced digits
+ // might be too short in which case the caller has to fill the remainder
+ // with '0's.
+ // Example: toFixed(0.001, 5) is allowed to return buffer="1", point=-2.
+ // Halfway cases are rounded towards +/-Infinity (away from 0). The call
+ // toFixed(0.15, 2) thus returns buffer="2", point=0.
+ // The returned buffer may contain digits that would be truncated from the
+ // shortest representation of the input.
+ // - PRECISION: produces 'requested_digits' where the first digit is not '0'.
+ // Even though the length of produced digits usually equals
+ // 'requested_digits', the function is allowed to return fewer digits, in
+ // which case the caller has to fill the missing digits with '0's.
+ // Halfway cases are again rounded away from 0.
+ // DoubleToAscii expects the given buffer to be big enough to hold all
+ // digits and a terminating null-character. In SHORTEST-mode it expects a
+ // buffer of at least kBase10MaximalLength + 1. In all other modes the
+ // requested_digits parameter (+ 1 for the null-character) limits the size of
+ // the output. The given length is only used in debug mode to ensure the
+ // buffer is big enough.
+ static void DoubleToAscii(double v,
+ DtoaMode mode,
+ int requested_digits,
+ char* buffer,
+ int buffer_length,
+ bool* sign,
+ int* length,
+ int* point);
+
+ private:
+ // If the value is a special value (NaN or Infinity) constructs the
+ // corresponding string using the configured infinity/nan-symbol.
+ // If either of them is NULL or the value is not special then the
+ // function returns false.
+ bool HandleSpecialValues(double value, StringBuilder* result_builder) const;
+ // Constructs an exponential representation (i.e. 1.234e56).
+ // The given exponent assumes a decimal point after the first decimal digit.
+ void CreateExponentialRepresentation(const char* decimal_digits,
+ int length,
+ int exponent,
+ StringBuilder* result_builder) const;
+ // Creates a decimal representation (i.e 1234.5678).
+ void CreateDecimalRepresentation(const char* decimal_digits,
+ int length,
+ int decimal_point,
+ int digits_after_point,
+ StringBuilder* result_builder) const;
+
+ const int flags_;
+ const char* const infinity_symbol_;
+ const char* const nan_symbol_;
+ const char exponent_character_;
+ const int decimal_in_shortest_low_;
+ const int decimal_in_shortest_high_;
+ const int max_leading_padding_zeroes_in_precision_mode_;
+ const int max_trailing_padding_zeroes_in_precision_mode_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(DoubleToStringConverter);
+};
+
+class StringToDoubleConverter {
+ public:
+ // Performs the conversion.
+ // The output parameter 'processed_characters_count' is set to the number
+ // of characters that have been processed to read the number.
+ static double StringToDouble(const char* buffer,
+ size_t length,
+ size_t* processed_characters_count);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(StringToDoubleConverter);
+};
+
+} // namespace double_conversion
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_DOUBLE_CONVERSION_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/dtoa/double.h b/chromium/third_party/blink/renderer/platform/wtf/dtoa/double.h
new file mode 100644
index 00000000000..eee1c2dd998
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/dtoa/double.h
@@ -0,0 +1,249 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_DOUBLE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_DOUBLE_H_
+
+#include "third_party/blink/renderer/platform/wtf/dtoa/diy-fp.h"
+
+namespace WTF {
+
+namespace double_conversion {
+
+// We assume that doubles and uint64_t have the same endianness.
+static uint64_t double_to_uint64(double d) {
+ return BitCast<uint64_t>(d);
+}
+static double uint64_to_double(uint64_t d64) {
+ return BitCast<double>(d64);
+}
+
+// Helper functions for doubles.
+class Double {
+ public:
+ static const uint64_t kSignMask = UINT64_2PART_C(0x80000000, 00000000);
+ static const uint64_t kExponentMask = UINT64_2PART_C(0x7FF00000, 00000000);
+ static const uint64_t kSignificandMask = UINT64_2PART_C(0x000FFFFF, FFFFFFFF);
+ static const uint64_t kHiddenBit = UINT64_2PART_C(0x00100000, 00000000);
+ static const int kPhysicalSignificandSize = 52; // Excludes the hidden bit.
+ static const int kSignificandSize = 53;
+
+ Double() : d64_(0) {}
+ explicit Double(double d) : d64_(double_to_uint64(d)) {}
+ explicit Double(uint64_t d64) : d64_(d64) {}
+ explicit Double(DiyFp diy_fp) : d64_(DiyFpToUint64(diy_fp)) {}
+
+ // The value encoded by this Double must be greater or equal to +0.0.
+ // It must not be special (infinity, or NaN).
+ DiyFp AsDiyFp() const {
+ DCHECK_GT(Sign(), 0);
+ DCHECK(!IsSpecial());
+ return DiyFp(Significand(), Exponent());
+ }
+
+ // The value encoded by this Double must be strictly greater than 0.
+ DiyFp AsNormalizedDiyFp() const {
+ DCHECK_GT(Value(), 0.0);
+ uint64_t f = Significand();
+ int e = Exponent();
+
+ // The current double could be a denormal.
+ while ((f & kHiddenBit) == 0) {
+ f <<= 1;
+ e--;
+ }
+ // Do the final shifts in one go.
+ f <<= DiyFp::kSignificandSize - kSignificandSize;
+ e -= DiyFp::kSignificandSize - kSignificandSize;
+ return DiyFp(f, e);
+ }
+
+ // Returns the double's bit as uint64.
+ uint64_t AsUint64() const { return d64_; }
+
+ // Returns the next greater double. Returns +infinity on input +infinity.
+ double NextDouble() const {
+ if (d64_ == kInfinity)
+ return Double(kInfinity).Value();
+ if (Sign() < 0 && Significand() == 0) {
+ // -0.0
+ return 0.0;
+ }
+ if (Sign() < 0) {
+ return Double(d64_ - 1).Value();
+ } else {
+ return Double(d64_ + 1).Value();
+ }
+ }
+
+ int Exponent() const {
+ if (IsDenormal())
+ return kDenormalExponent;
+
+ uint64_t d64 = AsUint64();
+ int biased_e =
+ static_cast<int>((d64 & kExponentMask) >> kPhysicalSignificandSize);
+ return biased_e - kExponentBias;
+ }
+
+ uint64_t Significand() const {
+ uint64_t d64 = AsUint64();
+ uint64_t significand = d64 & kSignificandMask;
+ if (!IsDenormal()) {
+ return significand + kHiddenBit;
+ } else {
+ return significand;
+ }
+ }
+
+ // Returns true if the double is a denormal.
+ bool IsDenormal() const {
+ uint64_t d64 = AsUint64();
+ return (d64 & kExponentMask) == 0;
+ }
+
+ // We consider denormals not to be special.
+ // Hence only Infinity and NaN are special.
+ bool IsSpecial() const {
+ uint64_t d64 = AsUint64();
+ return (d64 & kExponentMask) == kExponentMask;
+ }
+
+ bool IsNan() const {
+ uint64_t d64 = AsUint64();
+ return ((d64 & kExponentMask) == kExponentMask) &&
+ ((d64 & kSignificandMask) != 0);
+ }
+
+ bool IsInfinite() const {
+ uint64_t d64 = AsUint64();
+ return ((d64 & kExponentMask) == kExponentMask) &&
+ ((d64 & kSignificandMask) == 0);
+ }
+
+ int Sign() const {
+ uint64_t d64 = AsUint64();
+ return (d64 & kSignMask) == 0 ? 1 : -1;
+ }
+
+ // Precondition: the value encoded by this Double must be greater or equal
+ // than +0.0.
+ DiyFp UpperBoundary() const {
+ DCHECK_GT(Sign(), 0);
+ return DiyFp(Significand() * 2 + 1, Exponent() - 1);
+ }
+
+ // Computes the two boundaries of this.
+ // The bigger boundary (m_plus) is normalized. The lower boundary has the same
+ // exponent as m_plus.
+ // Precondition: the value encoded by this Double must be greater than 0.
+ void NormalizedBoundaries(DiyFp* out_m_minus, DiyFp* out_m_plus) const {
+ DCHECK_GT(Value(), 0.0);
+ DiyFp v = this->AsDiyFp();
+ bool significand_is_zero = (v.F() == kHiddenBit);
+ DiyFp plus = DiyFp::Normalize(DiyFp((v.F() << 1) + 1, v.E() - 1));
+ DiyFp minus;
+ if (significand_is_zero && v.E() != kDenormalExponent) {
+ // The boundary is closer. Think of v = 1000e10 and v- = 9999e9.
+ // Then the boundary (== (v - v-)/2) is not just at a distance of 1e9 but
+ // at a distance of 1e8.
+ // The only exception is for the smallest normal: the largest denormal is
+ // at the same distance as its successor.
+ // Note: denormals have the same exponent as the smallest normals.
+ minus = DiyFp((v.F() << 2) - 1, v.E() - 2);
+ } else {
+ minus = DiyFp((v.F() << 1) - 1, v.E() - 1);
+ }
+ minus.set_f(minus.F() << (minus.E() - plus.E()));
+ minus.set_e(plus.E());
+ *out_m_plus = plus;
+ *out_m_minus = minus;
+ }
+
+ double Value() const { return uint64_to_double(d64_); }
+
+ // Returns the significand size for a given order of magnitude.
+ // If v = f*2^e with 2^p-1 <= f <= 2^p then p+e is v's order of magnitude.
+ // This function returns the number of significant binary digits v will have
+ // once it's encoded into a double. In almost all cases this is equal to
+ // kSignificandSize. The only exceptions are denormals. They start with
+ // leading zeroes and their effective significand-size is hence smaller.
+ static int SignificandSizeForOrderOfMagnitude(int order) {
+ if (order >= (kDenormalExponent + kSignificandSize)) {
+ return kSignificandSize;
+ }
+ if (order <= kDenormalExponent)
+ return 0;
+ return order - kDenormalExponent;
+ }
+
+ static double Infinity() { return Double(kInfinity).Value(); }
+
+ static double NaN() { return Double(kNaN).Value(); }
+
+ private:
+ static const int kExponentBias = 0x3FF + kPhysicalSignificandSize;
+ static const int kDenormalExponent = -kExponentBias + 1;
+ static const int kMaxExponent = 0x7FF - kExponentBias;
+ static const uint64_t kInfinity = UINT64_2PART_C(0x7FF00000, 00000000);
+ static const uint64_t kNaN = UINT64_2PART_C(0x7FF80000, 00000000);
+
+ const uint64_t d64_;
+
+ static uint64_t DiyFpToUint64(DiyFp diy_fp) {
+ uint64_t significand = diy_fp.F();
+ int exponent = diy_fp.E();
+ while (significand > kHiddenBit + kSignificandMask) {
+ significand >>= 1;
+ exponent++;
+ }
+ if (exponent >= kMaxExponent) {
+ return kInfinity;
+ }
+ if (exponent < kDenormalExponent) {
+ return 0;
+ }
+ while (exponent > kDenormalExponent && (significand & kHiddenBit) == 0) {
+ significand <<= 1;
+ exponent--;
+ }
+ uint64_t biased_exponent;
+ if (exponent == kDenormalExponent && (significand & kHiddenBit) == 0) {
+ biased_exponent = 0;
+ } else {
+ biased_exponent = static_cast<uint64_t>(exponent + kExponentBias);
+ }
+ return (significand & kSignificandMask) |
+ (biased_exponent << kPhysicalSignificandSize);
+ }
+};
+
+} // namespace double_conversion
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_DOUBLE_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/dtoa/fast-dtoa.cc b/chromium/third_party/blink/renderer/platform/wtf/dtoa/fast-dtoa.cc
new file mode 100644
index 00000000000..9e703653d3d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/dtoa/fast-dtoa.cc
@@ -0,0 +1,743 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "third_party/blink/renderer/platform/wtf/dtoa/fast-dtoa.h"
+
+#include "third_party/blink/renderer/platform/wtf/dtoa/cached-powers.h"
+#include "third_party/blink/renderer/platform/wtf/dtoa/diy-fp.h"
+#include "third_party/blink/renderer/platform/wtf/dtoa/double.h"
+
+namespace WTF {
+
+namespace double_conversion {
+
+// The minimal and maximal target exponent define the range of w's binary
+// exponent, where 'w' is the result of multiplying the input by a cached power
+// of ten.
+//
+// A different range might be chosen on a different platform, to optimize digit
+// generation, but a smaller range requires more powers of ten to be cached.
+static const int kMinimalTargetExponent = -60;
+static const int kMaximalTargetExponent = -32;
+
+// Adjusts the last digit of the generated number, and screens out generated
+// solutions that may be inaccurate. A solution may be inaccurate if it is
+// outside the safe interval, or if we cannot prove that it is closer to the
+// input than a neighboring representation of the same length.
+//
+// Input: * buffer containing the digits of too_high / 10^kappa
+// * the buffer's length
+// * distance_too_high_w == (too_high - w).f() * unit
+// * unsafe_interval == (too_high - too_low).f() * unit
+// * rest = (too_high - buffer * 10^kappa).f() * unit
+// * ten_kappa = 10^kappa * unit
+// * unit = the common multiplier
+// Output: returns true if the buffer is guaranteed to contain the closest
+// representable number to the input.
+// Modifies the generated digits in the buffer to approach (round towards) w.
+static bool RoundWeed(Vector<char> buffer,
+ int length,
+ uint64_t distance_too_high_w,
+ uint64_t unsafe_interval,
+ uint64_t rest,
+ uint64_t ten_kappa,
+ uint64_t unit) {
+ uint64_t small_distance = distance_too_high_w - unit;
+ uint64_t big_distance = distance_too_high_w + unit;
+ // Let w_low = too_high - big_distance, and
+ // w_high = too_high - small_distance.
+ // Note: w_low < w < w_high
+ //
+ // The real w (* unit) must lie somewhere inside the interval
+ // ]w_low; w_high[ (often written as "(w_low; w_high)")
+
+ // Basically the buffer currently contains a number in the unsafe interval
+ // ]too_low; too_high[ with too_low < w < too_high
+ //
+ // too_high - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ // ^v 1 unit ^ ^ ^ ^
+ // boundary_high --------------------- . . . .
+ // ^v 1 unit . . . .
+ // - - - - - - - - - - - - - - - - - - - + - - + - - - - - - . .
+ // . . ^ . .
+ // . big_distance . . .
+ // . . . . rest
+ // small_distance . . . .
+ // v . . . .
+ // w_high - - - - - - - - - - - - - - - - - - . . . .
+ // ^v 1 unit . . . .
+ // w ---------------------------------------- . . . .
+ // ^v 1 unit v . . .
+ // w_low - - - - - - - - - - - - - - - - - - - - - . . .
+ // . . v
+ // buffer --------------------------------------------------+-------+--------
+ // . .
+ // safe_interval .
+ // v .
+ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .
+ // ^v 1 unit .
+ // boundary_low ------------------------- unsafe_interval
+ // ^v 1 unit v
+ // too_low - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ //
+ //
+ // Note that the value of buffer could lie anywhere inside the range too_low
+ // to too_high.
+ //
+ // boundary_low, boundary_high and w are approximations of the real boundaries
+ // and v (the input number). They are guaranteed to be precise up to one unit.
+ // In fact the error is guaranteed to be strictly less than one unit.
+ //
+ // Anything that lies outside the unsafe interval is guaranteed not to round
+ // to v when read again.
+ // Anything that lies inside the safe interval is guaranteed to round to v
+ // when read again.
+ // If the number inside the buffer lies inside the unsafe interval but not
+ // inside the safe interval then we simply do not know and bail out (returning
+ // false).
+ //
+ // Similarly we have to take into account the imprecision of 'w' when finding
+ // the closest representation of 'w'. If we have two potential
+ // representations, and one is closer to both w_low and w_high, then we know
+ // it is closer to the actual value v.
+ //
+ // By generating the digits of too_high we got the largest (closest to
+ // too_high) buffer that is still in the unsafe interval. In the case where
+ // w_high < buffer < too_high we try to decrement the buffer.
+ // This way the buffer approaches (rounds towards) w.
+ // There are 3 conditions that stop the decrementation process:
+ // 1) the buffer is already below w_high
+ // 2) decrementing the buffer would make it leave the unsafe interval
+ // 3) decrementing the buffer would yield a number below w_high and farther
+ // away than the current number. In other words:
+ // (buffer{-1} < w_high) && w_high - buffer{-1} > buffer - w_high
+ // Instead of using the buffer directly we use its distance to too_high.
+ // Conceptually rest ~= too_high - buffer
+ // We need to do the following tests in this order to avoid over- and
+ // underflows.
+ DCHECK_LE(rest, unsafe_interval);
+ while (rest < small_distance && // Negated condition 1
+ unsafe_interval - rest >= ten_kappa && // Negated condition 2
+ (rest + ten_kappa < small_distance || // buffer{-1} > w_high
+ small_distance - rest >= rest + ten_kappa - small_distance)) {
+ buffer[length - 1]--;
+ rest += ten_kappa;
+ }
+
+ // We have approached w+ as much as possible. We now test if approaching w-
+ // would require changing the buffer. If yes, then we have two possible
+ // representations close to w, but we cannot decide which one is closer.
+ if (rest < big_distance && unsafe_interval - rest >= ten_kappa &&
+ (rest + ten_kappa < big_distance ||
+ big_distance - rest > rest + ten_kappa - big_distance)) {
+ return false;
+ }
+
+ // Weeding test.
+ // The safe interval is [too_low + 2 ulp; too_high - 2 ulp]
+ // Since too_low = too_high - unsafe_interval this is equivalent to
+ // [too_high - unsafe_interval + 4 ulp; too_high - 2 ulp]
+ // Conceptually we have: rest ~= too_high - buffer
+ return (2 * unit <= rest) && (rest <= unsafe_interval - 4 * unit);
+}
+
+// Rounds the buffer upwards if the result is closer to v by possibly adding
+// 1 to the buffer. If the precision of the calculation is not sufficient to
+// round correctly, return false.
+// The rounding might shift the whole buffer in which case the kappa is
+// adjusted. For example "99", kappa = 3 might become "10", kappa = 4.
+//
+// If 2*rest > ten_kappa then the buffer needs to be round up.
+// rest can have an error of +/- 1 unit. This function accounts for the
+// imprecision and returns false, if the rounding direction cannot be
+// unambiguously determined.
+//
+// Precondition: rest < ten_kappa.
+static bool RoundWeedCounted(Vector<char> buffer,
+ int length,
+ uint64_t rest,
+ uint64_t ten_kappa,
+ uint64_t unit,
+ int* kappa) {
+ DCHECK_LT(rest, ten_kappa);
+ // The following tests are done in a specific order to avoid overflows. They
+ // will work correctly with any uint64 values of rest < ten_kappa and unit.
+ //
+ // If the unit is too big, then we don't know which way to round. For example
+ // a unit of 50 means that the real number lies within rest +/- 50. If
+ // 10^kappa == 40 then there is no way to tell which way to round.
+ if (unit >= ten_kappa)
+ return false;
+ // Even if unit is just half the size of 10^kappa we are already completely
+ // lost. (And after the previous test we know that the expression will not
+ // over/underflow.)
+ if (ten_kappa - unit <= unit)
+ return false;
+ // If 2 * (rest + unit) <= 10^kappa we can safely round down.
+ if ((ten_kappa - rest > rest) && (ten_kappa - 2 * rest >= 2 * unit)) {
+ return true;
+ }
+ // If 2 * (rest - unit) >= 10^kappa, then we can safely round up.
+ if ((rest > unit) && (ten_kappa - (rest - unit) <= (rest - unit))) {
+ // Increment the last digit recursively until we find a non '9' digit.
+ buffer[length - 1]++;
+ for (int i = length - 1; i > 0; --i) {
+ if (buffer[i] != '0' + 10)
+ break;
+ buffer[i] = '0';
+ buffer[i - 1]++;
+ }
+ // If the first digit is now '0'+ 10 we had a buffer with all '9's. With the
+ // exception of the first digit all digits are now '0'. Simply switch the
+ // first digit to '1' and adjust the kappa. Example: "99" becomes "10" and
+ // the power (the kappa) is increased.
+ if (buffer[0] == '0' + 10) {
+ buffer[0] = '1';
+ (*kappa) += 1;
+ }
+ return true;
+ }
+ return false;
+}
+
+static const uint32_t kTen4 = 10000;
+static const uint32_t kTen5 = 100000;
+static const uint32_t kTen6 = 1000000;
+static const uint32_t kTen7 = 10000000;
+static const uint32_t kTen8 = 100000000;
+static const uint32_t kTen9 = 1000000000;
+
+// Returns the biggest power of ten that is less than or equal to the given
+// number. We furthermore receive the maximum number of bits 'number' has.
+// If number_bits == 0 then 0^-1 is returned
+// The number of bits must be <= 32.
+// Precondition: number < (1 << (number_bits + 1)).
+static void BiggestPowerTen(uint32_t number,
+ int number_bits,
+ uint32_t* power,
+ int* exponent) {
+ DCHECK_LT(number, (uint32_t)(1 << (number_bits + 1)));
+
+ switch (number_bits) {
+ case 32:
+ case 31:
+ case 30:
+ if (kTen9 <= number) {
+ *power = kTen9;
+ *exponent = 9;
+ break;
+ }
+ FALLTHROUGH;
+ case 29:
+ case 28:
+ case 27:
+ if (kTen8 <= number) {
+ *power = kTen8;
+ *exponent = 8;
+ break;
+ }
+ FALLTHROUGH;
+ case 26:
+ case 25:
+ case 24:
+ if (kTen7 <= number) {
+ *power = kTen7;
+ *exponent = 7;
+ break;
+ }
+ FALLTHROUGH;
+ case 23:
+ case 22:
+ case 21:
+ case 20:
+ if (kTen6 <= number) {
+ *power = kTen6;
+ *exponent = 6;
+ break;
+ }
+ FALLTHROUGH;
+ case 19:
+ case 18:
+ case 17:
+ if (kTen5 <= number) {
+ *power = kTen5;
+ *exponent = 5;
+ break;
+ }
+ FALLTHROUGH;
+ case 16:
+ case 15:
+ case 14:
+ if (kTen4 <= number) {
+ *power = kTen4;
+ *exponent = 4;
+ break;
+ }
+ FALLTHROUGH;
+ case 13:
+ case 12:
+ case 11:
+ case 10:
+ if (1000 <= number) {
+ *power = 1000;
+ *exponent = 3;
+ break;
+ }
+ FALLTHROUGH;
+ case 9:
+ case 8:
+ case 7:
+ if (100 <= number) {
+ *power = 100;
+ *exponent = 2;
+ break;
+ }
+ FALLTHROUGH;
+ case 6:
+ case 5:
+ case 4:
+ if (10 <= number) {
+ *power = 10;
+ *exponent = 1;
+ break;
+ }
+ FALLTHROUGH;
+ case 3:
+ case 2:
+ case 1:
+ if (1 <= number) {
+ *power = 1;
+ *exponent = 0;
+ break;
+ }
+ FALLTHROUGH;
+ case 0:
+ *power = 0;
+ *exponent = -1;
+ break;
+ default:
+ // Following assignments are here to silence compiler warnings.
+ *power = 0;
+ *exponent = 0;
+ UNREACHABLE();
+ }
+}
+
+// Generates the digits of input number w.
+// w is a floating-point number (DiyFp), consisting of a significand and an
+// exponent. Its exponent is bounded by kMinimalTargetExponent and
+// kMaximalTargetExponent.
+// Hence -60 <= w.e() <= -32.
+//
+// Returns false if it fails, in which case the generated digits in the buffer
+// should not be used.
+// Preconditions:
+// * low, w and high are correct up to 1 ulp (unit in the last place). That
+// is, their error must be less than a unit of their last digits.
+// * low.e() == w.e() == high.e()
+// * low < w < high, and taking into account their error: low~ <= high~
+// * kMinimalTargetExponent <= w.e() <= kMaximalTargetExponent
+// Postconditions: returns false if procedure fails.
+// otherwise:
+// * buffer is not null-terminated, but len contains the number of digits.
+// * buffer contains the shortest possible decimal digit-sequence
+// such that LOW < buffer * 10^kappa < HIGH, where LOW and HIGH are the
+// correct values of low and high (without their error).
+// * if more than one decimal representation gives the minimal number of
+// decimal digits then the one closest to W (where W is the correct value
+// of w) is chosen.
+// Remark: this procedure takes into account the imprecision of its input
+// numbers. If the precision is not enough to guarantee all the postconditions
+// then false is returned. This usually happens rarely (~0.5%).
+//
+// Say, for the sake of example, that
+// w.e() == -48, and w.f() == 0x1234567890abcdef
+// w's value can be computed by w.f() * 2^w.e()
+// We can obtain w's integral digits by simply shifting w.f() by -w.e().
+// -> w's integral part is 0x1234
+// w's fractional part is therefore 0x567890abcdef.
+// Printing w's integral part is easy (simply print 0x1234 in decimal).
+// In order to print its fraction we repeatedly multiply the fraction by 10 and
+// get each digit. Example the first digit after the point would be computed by
+// (0x567890abcdef * 10) >> 48. -> 3
+// The whole thing becomes slightly more complicated because we want to stop
+// once we have enough digits. That is, once the digits inside the buffer
+// represent 'w' we can stop. Everything inside the interval low - high
+// represents w. However we have to pay attention to low, high and w's
+// imprecision.
+static bool DigitGen(DiyFp low,
+ DiyFp w,
+ DiyFp high,
+ Vector<char> buffer,
+ int* length,
+ int* kappa) {
+ DCHECK_EQ(low.E(), w.E());
+ DCHECK_EQ(w.E(), high.E());
+ DCHECK_LE(low.F() + 1, high.F() - 1);
+ DCHECK_LE(kMinimalTargetExponent, w.E());
+ DCHECK_LE(w.E(), kMaximalTargetExponent);
+ // low, w and high are imprecise, but by less than one ulp (unit in the
+ // last place). If we remove (resp. add) 1 ulp from low (resp. high) we
+ // are certain that the new numbers are outside of the interval we want
+ // the final representation to lie in. Inversely adding (resp. removing) 1
+ // ulp from low (resp. high) would yield numbers that are certain to lie
+ // in the interval. We will use this fact later on. We will now start by
+ // generating the digits within the uncertain interval. Later we will weed
+ // out representations that lie outside the safe interval and thus _might_
+ // lie outside the correct interval.
+ uint64_t unit = 1;
+ DiyFp too_low = DiyFp(low.F() - unit, low.E());
+ DiyFp too_high = DiyFp(high.F() + unit, high.E());
+ // too_low and too_high are guaranteed to lie outside the interval we want
+ // the generated number in.
+ DiyFp unsafe_interval = DiyFp::Minus(too_high, too_low);
+ // We now cut the input number into two parts: the integral digits and the
+ // fractionals. We will not write any decimal separator though, but adapt
+ // kappa instead.
+ // Reminder: we are currently computing the digits (stored inside the
+ // buffer) such that: too_low < buffer * 10^kappa < too_high We use
+ // too_high for the digit_generation and stop as soon as possible. If we
+ // stop early we effectively round down.
+ DiyFp one = DiyFp(static_cast<uint64_t>(1) << -w.E(), w.E());
+ // Division by one is a shift.
+ uint32_t integrals = static_cast<uint32_t>(too_high.F() >> -one.E());
+ // Modulo by one is an and.
+ uint64_t fractionals = too_high.F() & (one.F() - 1);
+ uint32_t divisor;
+ int divisor_exponent;
+ BiggestPowerTen(integrals, DiyFp::kSignificandSize - (-one.E()), &divisor,
+ &divisor_exponent);
+ *kappa = divisor_exponent + 1;
+ *length = 0;
+ // Loop invariant: buffer = too_high / 10^kappa (integer division)
+ // The invariant holds for the first iteration: kappa has been initialized
+ // with the divisor exponent + 1. And the divisor is the biggest power of
+ // ten that is smaller than integrals.
+ while (*kappa > 0) {
+ char digit = static_cast<char>(integrals / divisor);
+ buffer[*length] = '0' + digit;
+ (*length)++;
+ integrals %= divisor;
+ (*kappa)--;
+ // Note that kappa now equals the exponent of the divisor and that the
+ // invariant thus holds again.
+ uint64_t rest =
+ (static_cast<uint64_t>(integrals) << -one.E()) + fractionals;
+ // Invariant: too_high = buffer * 10^kappa + DiyFp(rest, one.e())
+ // Reminder: unsafe_interval.e() == one.e()
+ if (rest < unsafe_interval.F()) {
+ // Rounding down (by not emitting the remaining digits) yields a
+ // number that lies within the unsafe interval.
+ return RoundWeed(buffer, *length, DiyFp::Minus(too_high, w).F(),
+ unsafe_interval.F(), rest,
+ static_cast<uint64_t>(divisor) << -one.E(), unit);
+ }
+ divisor /= 10;
+ }
+
+ // The integrals have been generated. We are at the point of the decimal
+ // separator. In the following loop we simply multiply the remaining digits by
+ // 10 and divide by one. We just need to pay attention to multiply associated
+ // data (like the interval or 'unit'), too.
+ // Note that the multiplication by 10 does not overflow, because w.e >= -60
+ // and thus one.e >= -60.
+ DCHECK_GE(one.E(), -60);
+ DCHECK_LT(fractionals, one.F());
+ DCHECK_GE(UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF) / 10, one.F());
+ while (true) {
+ fractionals *= 10;
+ unit *= 10;
+ unsafe_interval.set_f(unsafe_interval.F() * 10);
+ // Integer division by one.
+ char digit = static_cast<char>(fractionals >> -one.E());
+ buffer[*length] = '0' + digit;
+ (*length)++;
+ fractionals &= one.F() - 1; // Modulo by one.
+ (*kappa)--;
+ if (fractionals < unsafe_interval.F()) {
+ return RoundWeed(buffer, *length, DiyFp::Minus(too_high, w).F() * unit,
+ unsafe_interval.F(), fractionals, one.F(), unit);
+ }
+ }
+}
+
+// Generates (at most) requested_digits digits of input number w.
+// w is a floating-point number (DiyFp), consisting of a significand and an
+// exponent. Its exponent is bounded by kMinimalTargetExponent and
+// kMaximalTargetExponent.
+// Hence -60 <= w.e() <= -32.
+//
+// Returns false if it fails, in which case the generated digits in the buffer
+// should not be used.
+// Preconditions:
+// * w is correct up to 1 ulp (unit in the last place). That
+// is, its error must be strictly less than a unit of its last digit.
+// * kMinimalTargetExponent <= w.e() <= kMaximalTargetExponent
+//
+// Postconditions: returns false if procedure fails.
+// otherwise:
+// * buffer is not null-terminated, but length contains the number of
+// digits.
+// * the representation in buffer is the most precise representation of
+// requested_digits digits.
+// * buffer contains at most requested_digits digits of w. If there are less
+// than requested_digits digits then some trailing '0's have been removed.
+// * kappa is such that
+// w = buffer * 10^kappa + eps with |eps| < 10^kappa / 2.
+//
+// Remark: This procedure takes into account the imprecision of its input
+// numbers. If the precision is not enough to guarantee all the postconditions
+// then false is returned. This usually happens rarely, but the failure-rate
+// increases with higher requested_digits.
+static bool DigitGenCounted(DiyFp w,
+ int requested_digits,
+ Vector<char> buffer,
+ int* length,
+ int* kappa) {
+ DCHECK_LE(kMinimalTargetExponent, w.E());
+ DCHECK_LE(w.E(), kMaximalTargetExponent);
+ DCHECK_GE(kMinimalTargetExponent, -60);
+ DCHECK_LE(kMaximalTargetExponent, -32);
+ // w is assumed to have an error less than 1 unit. Whenever w is scaled we
+ // also scale its error.
+ uint64_t w_error = 1;
+ // We cut the input number into two parts: the integral digits and the
+ // fractional digits. We don't emit any decimal separator, but adapt kappa
+ // instead. Example: instead of writing "1.2" we put "12" into the buffer
+ // and increase kappa by 1.
+ DiyFp one = DiyFp(static_cast<uint64_t>(1) << -w.E(), w.E());
+ // Division by one is a shift.
+ uint32_t integrals = static_cast<uint32_t>(w.F() >> -one.E());
+ // Modulo by one is an and.
+ uint64_t fractionals = w.F() & (one.F() - 1);
+ uint32_t divisor;
+ int divisor_exponent;
+ BiggestPowerTen(integrals, DiyFp::kSignificandSize - (-one.E()), &divisor,
+ &divisor_exponent);
+ *kappa = divisor_exponent + 1;
+ *length = 0;
+
+ // Loop invariant: buffer = w / 10^kappa (integer division)
+ // The invariant holds for the first iteration: kappa has been initialized
+ // with the divisor exponent + 1. And the divisor is the biggest power of
+ // ten that is smaller than 'integrals'.
+ while (*kappa > 0) {
+ char digit = static_cast<char>(integrals / divisor);
+ buffer[*length] = '0' + digit;
+ (*length)++;
+ requested_digits--;
+ integrals %= divisor;
+ (*kappa)--;
+ // Note that kappa now equals the exponent of the divisor and that the
+ // invariant thus holds again.
+ if (requested_digits == 0)
+ break;
+ divisor /= 10;
+ }
+
+ if (requested_digits == 0) {
+ uint64_t rest =
+ (static_cast<uint64_t>(integrals) << -one.E()) + fractionals;
+ return RoundWeedCounted(buffer, *length, rest,
+ static_cast<uint64_t>(divisor) << -one.E(), w_error,
+ kappa);
+ }
+
+ // The integrals have been generated. We are at the point of the decimal
+ // separator. In the following loop we simply multiply the remaining digits by
+ // 10 and divide by one. We just need to pay attention to multiply associated
+ // data (the 'unit'), too.
+ // Note that the multiplication by 10 does not overflow, because w.e >= -60
+ // and thus one.e >= -60.
+ DCHECK_GE(one.E(), -60);
+ DCHECK_LT(fractionals, one.F());
+ DCHECK_GE(UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF) / 10, one.F());
+ while (requested_digits > 0 && fractionals > w_error) {
+ fractionals *= 10;
+ w_error *= 10;
+ // Integer division by one.
+ char digit = static_cast<char>(fractionals >> -one.E());
+ buffer[*length] = '0' + digit;
+ (*length)++;
+ requested_digits--;
+ fractionals &= one.F() - 1; // Modulo by one.
+ (*kappa)--;
+ }
+ if (requested_digits != 0)
+ return false;
+ return RoundWeedCounted(buffer, *length, fractionals, one.F(), w_error,
+ kappa);
+}
+
+// Provides a decimal representation of v.
+// Returns true if it succeeds, otherwise the result cannot be trusted.
+// There will be *length digits inside the buffer (not null-terminated).
+// If the function returns true then
+// v == (double) (buffer * 10^decimal_exponent).
+// The digits in the buffer are the shortest representation possible: no
+// 0.09999999999999999 instead of 0.1. The shorter representation will even be
+// chosen even if the longer one would be closer to v.
+// The last digit will be closest to the actual v. That is, even if several
+// digits might correctly yield 'v' when read again, the closest will be
+// computed.
+static bool Grisu3(double v,
+ Vector<char> buffer,
+ int* length,
+ int* decimal_exponent) {
+ DiyFp w = Double(v).AsNormalizedDiyFp();
+ // boundary_minus and boundary_plus are the boundaries between v and its
+ // closest floating-point neighbors. Any number strictly between
+ // boundary_minus and boundary_plus will round to v when convert to a double.
+ // Grisu3 will never output representations that lie exactly on a boundary.
+ DiyFp boundary_minus, boundary_plus;
+ Double(v).NormalizedBoundaries(&boundary_minus, &boundary_plus);
+ DCHECK_EQ(boundary_plus.E(), w.E());
+ DiyFp ten_mk; // Cached power of ten: 10^-k
+ int mk; // -k
+ int ten_mk_minimal_binary_exponent =
+ kMinimalTargetExponent - (w.E() + DiyFp::kSignificandSize);
+ int ten_mk_maximal_binary_exponent =
+ kMaximalTargetExponent - (w.E() + DiyFp::kSignificandSize);
+ PowersOfTenCache::GetCachedPowerForBinaryExponentRange(
+ ten_mk_minimal_binary_exponent, ten_mk_maximal_binary_exponent, &ten_mk,
+ &mk);
+ DCHECK_LE(kMinimalTargetExponent,
+ w.E() + ten_mk.E() + DiyFp::kSignificandSize);
+ DCHECK_GE(kMaximalTargetExponent,
+ w.E() + ten_mk.E() + DiyFp::kSignificandSize);
+ // Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a
+ // 64 bit significand and ten_mk is thus only precise up to 64 bits.
+
+ // The DiyFp::Times procedure rounds its result, and ten_mk is approximated
+ // too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now
+ // off by a small amount.
+ // In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w.
+ // In other words: let f = scaled_w.f() and e = scaled_w.e(), then
+ // (f-1) * 2^e < w*10^k < (f+1) * 2^e
+ DiyFp scaled_w = DiyFp::Times(w, ten_mk);
+ DCHECK_EQ(scaled_w.E(),
+ boundary_plus.E() + ten_mk.E() + DiyFp::kSignificandSize);
+ // In theory it would be possible to avoid some recomputations by computing
+ // the difference between w and boundary_minus/plus (a power of 2) and to
+ // compute scaled_boundary_minus/plus by subtracting/adding from
+ // scaled_w. However the code becomes much less readable and the speed
+ // enhancements are not terriffic.
+ DiyFp scaled_boundary_minus = DiyFp::Times(boundary_minus, ten_mk);
+ DiyFp scaled_boundary_plus = DiyFp::Times(boundary_plus, ten_mk);
+
+ // DigitGen will generate the digits of scaled_w. Therefore we have
+ // v == (double) (scaled_w * 10^-mk).
+ // Set decimal_exponent == -mk and pass it to DigitGen. If scaled_w is not an
+ // integer than it will be updated. For instance if scaled_w == 1.23 then
+ // the buffer will be filled with "123" und the decimal_exponent will be
+ // decreased by 2.
+ int kappa;
+ bool result = DigitGen(scaled_boundary_minus, scaled_w, scaled_boundary_plus,
+ buffer, length, &kappa);
+ *decimal_exponent = -mk + kappa;
+ return result;
+}
+
+// The "counted" version of grisu3 (see above) only generates requested_digits
+// number of digits. This version does not generate the shortest representation,
+// and with enough requested digits 0.1 will at some point print as 0.9999999...
+// Grisu3 is too imprecise for real halfway cases (1.5 will not work) and
+// therefore the rounding strategy for halfway cases is irrelevant.
+static bool Grisu3Counted(double v,
+ int requested_digits,
+ Vector<char> buffer,
+ int* length,
+ int* decimal_exponent) {
+ DiyFp w = Double(v).AsNormalizedDiyFp();
+ DiyFp ten_mk; // Cached power of ten: 10^-k
+ int mk; // -k
+ int ten_mk_minimal_binary_exponent =
+ kMinimalTargetExponent - (w.E() + DiyFp::kSignificandSize);
+ int ten_mk_maximal_binary_exponent =
+ kMaximalTargetExponent - (w.E() + DiyFp::kSignificandSize);
+ PowersOfTenCache::GetCachedPowerForBinaryExponentRange(
+ ten_mk_minimal_binary_exponent, ten_mk_maximal_binary_exponent, &ten_mk,
+ &mk);
+ DCHECK_LE(kMinimalTargetExponent,
+ w.E() + ten_mk.E() + DiyFp::kSignificandSize);
+ DCHECK_GE(kMaximalTargetExponent,
+ w.E() + ten_mk.E() + DiyFp::kSignificandSize);
+ // Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a
+ // 64 bit significand and ten_mk is thus only precise up to 64 bits.
+
+ // The DiyFp::Times procedure rounds its result, and ten_mk is approximated
+ // too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now
+ // off by a small amount.
+ // In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w.
+ // In other words: let f = scaled_w.f() and e = scaled_w.e(), then
+ // (f-1) * 2^e < w*10^k < (f+1) * 2^e
+ DiyFp scaled_w = DiyFp::Times(w, ten_mk);
+
+ // We now have (double) (scaled_w * 10^-mk).
+ // DigitGen will generate the first requested_digits digits of scaled_w and
+ // return together with a kappa such that scaled_w ~= buffer * 10^kappa. (It
+ // will not always be exactly the same since DigitGenCounted only produces a
+ // limited number of digits.)
+ int kappa;
+ bool result =
+ DigitGenCounted(scaled_w, requested_digits, buffer, length, &kappa);
+ *decimal_exponent = -mk + kappa;
+ return result;
+}
+
+bool FastDtoa(double v,
+ FastDtoaMode mode,
+ int requested_digits,
+ Vector<char> buffer,
+ int* length,
+ int* decimal_point) {
+ DCHECK_GT(v, 0);
+ DCHECK(!Double(v).IsSpecial());
+
+ bool result = false;
+ int decimal_exponent = 0;
+ switch (mode) {
+ case FAST_DTOA_SHORTEST:
+ result = Grisu3(v, buffer, length, &decimal_exponent);
+ break;
+ case FAST_DTOA_PRECISION:
+ result =
+ Grisu3Counted(v, requested_digits, buffer, length, &decimal_exponent);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ if (result) {
+ *decimal_point = *length + decimal_exponent;
+ buffer[*length] = '\0';
+ }
+ return result;
+}
+
+} // namespace double_conversion
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/dtoa/fast-dtoa.h b/chromium/third_party/blink/renderer/platform/wtf/dtoa/fast-dtoa.h
new file mode 100644
index 00000000000..e1c7305c5f5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/dtoa/fast-dtoa.h
@@ -0,0 +1,88 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_FAST_DTOA_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_FAST_DTOA_H_
+
+#include "third_party/blink/renderer/platform/wtf/dtoa/utils.h"
+
+namespace WTF {
+
+namespace double_conversion {
+
+enum FastDtoaMode {
+ // Computes the shortest representation of the given input. The returned
+ // result will be the most accurate number of this length. Longer
+ // representations might be more accurate.
+ FAST_DTOA_SHORTEST,
+ // Computes a representation where the precision (number of digits) is
+ // given as input. The precision is independent of the decimal point.
+ FAST_DTOA_PRECISION
+};
+
+// FastDtoa will produce at most kFastDtoaMaximalLength digits. This does not
+// include the terminating '\0' character.
+static const int kFastDtoaMaximalLength = 17;
+
+// Provides a decimal representation of v.
+// The result should be interpreted as buffer * 10^(point - length).
+//
+// Precondition:
+// * v must be a strictly positive finite double.
+//
+// Returns true if it succeeds, otherwise the result can not be trusted.
+// There will be *length digits inside the buffer followed by a null terminator.
+// If the function returns true and mode equals
+// - FAST_DTOA_SHORTEST, then
+// the parameter requested_digits is ignored.
+// The result satisfies
+// v == (double) (buffer * 10^(point - length)).
+// The digits in the buffer are the shortest representation possible. E.g.
+// if 0.099999999999 and 0.1 represent the same double then "1" is returned
+// with point = 0.
+// The last digit will be closest to the actual v. That is, even if several
+// digits might correctly yield 'v' when read again, the buffer will contain
+// the one closest to v.
+// - FAST_DTOA_PRECISION, then
+// the buffer contains requested_digits digits.
+// the difference v - (buffer * 10^(point-length)) is closest to zero for
+// all possible representations of requested_digits digits.
+// If there are two values that are equally close, then FastDtoa returns
+// false.
+// For both modes the buffer must be large enough to hold the result.
+bool FastDtoa(double d,
+ FastDtoaMode mode,
+ int requested_digits,
+ Vector<char> buffer,
+ int* length,
+ int* decimal_point);
+
+} // namespace double_conversion
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_FAST_DTOA_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/dtoa/fixed-dtoa.cc b/chromium/third_party/blink/renderer/platform/wtf/dtoa/fixed-dtoa.cc
new file mode 100644
index 00000000000..693d84ea4e5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/dtoa/fixed-dtoa.cc
@@ -0,0 +1,410 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "third_party/blink/renderer/platform/wtf/dtoa/fixed-dtoa.h"
+
+#include <math.h>
+
+#include "third_party/blink/renderer/platform/wtf/dtoa/double.h"
+
+namespace WTF {
+
+namespace double_conversion {
+
+// Represents a 128bit type. This class should be replaced by a native type on
+// platforms that support 128bit integers.
+class UInt128 {
+ public:
+ UInt128() : high_bits_(0), low_bits_(0) {}
+ UInt128(uint64_t high, uint64_t low) : high_bits_(high), low_bits_(low) {}
+
+ void Multiply(uint32_t multiplicand) {
+ uint64_t accumulator;
+
+ accumulator = (low_bits_ & kMask32) * multiplicand;
+ uint32_t part = static_cast<uint32_t>(accumulator & kMask32);
+ accumulator >>= 32;
+ accumulator = accumulator + (low_bits_ >> 32) * multiplicand;
+ low_bits_ = (accumulator << 32) + part;
+ accumulator >>= 32;
+ accumulator = accumulator + (high_bits_ & kMask32) * multiplicand;
+ part = static_cast<uint32_t>(accumulator & kMask32);
+ accumulator >>= 32;
+ accumulator = accumulator + (high_bits_ >> 32) * multiplicand;
+ high_bits_ = (accumulator << 32) + part;
+ DCHECK_EQ((accumulator >> 32), 0u);
+ }
+
+ void Shift(int shift_amount) {
+ DCHECK_LE(-64, shift_amount);
+ DCHECK_LE(shift_amount, 64);
+ if (shift_amount == 0) {
+ return;
+ } else if (shift_amount == -64) {
+ high_bits_ = low_bits_;
+ low_bits_ = 0;
+ } else if (shift_amount == 64) {
+ low_bits_ = high_bits_;
+ high_bits_ = 0;
+ } else if (shift_amount <= 0) {
+ high_bits_ <<= -shift_amount;
+ high_bits_ += low_bits_ >> (64 + shift_amount);
+ low_bits_ <<= -shift_amount;
+ } else {
+ low_bits_ >>= shift_amount;
+ low_bits_ += high_bits_ << (64 - shift_amount);
+ high_bits_ >>= shift_amount;
+ }
+ }
+
+ // Modifies *this to *this MOD (2^power).
+ // Returns *this DIV (2^power).
+ int DivModPowerOf2(int power) {
+ if (power >= 64) {
+ int result = static_cast<int>(high_bits_ >> (power - 64));
+ high_bits_ -= static_cast<uint64_t>(result) << (power - 64);
+ return result;
+ } else {
+ uint64_t part_low = low_bits_ >> power;
+ uint64_t part_high = high_bits_ << (64 - power);
+ int result = static_cast<int>(part_low + part_high);
+ high_bits_ = 0;
+ low_bits_ -= part_low << power;
+ return result;
+ }
+ }
+
+ bool IsZero() const { return high_bits_ == 0 && low_bits_ == 0; }
+
+ int BitAt(int position) {
+ if (position >= 64) {
+ return static_cast<int>(high_bits_ >> (position - 64)) & 1;
+ } else {
+ return static_cast<int>(low_bits_ >> position) & 1;
+ }
+ }
+
+ private:
+ static const uint64_t kMask32 = 0xFFFFFFFF;
+ // Value == (high_bits_ << 64) + low_bits_
+ uint64_t high_bits_;
+ uint64_t low_bits_;
+};
+
+static const int kDoubleSignificandSize = 53; // Includes the hidden bit.
+
+static void FillDigits32FixedLength(uint32_t number,
+ int requested_length,
+ Vector<char> buffer,
+ int* length) {
+ for (int i = requested_length - 1; i >= 0; --i) {
+ buffer[(*length) + i] = '0' + number % 10;
+ number /= 10;
+ }
+ *length += requested_length;
+}
+
+static void FillDigits32(uint32_t number, Vector<char> buffer, int* length) {
+ int number_length = 0;
+ // We fill the digits in reverse order and exchange them afterwards.
+ while (number != 0) {
+ char digit = number % 10;
+ number /= 10;
+ buffer[(*length) + number_length] = '0' + digit;
+ number_length++;
+ }
+ // Exchange the digits.
+ int i = *length;
+ int j = *length + number_length - 1;
+ while (i < j) {
+ char tmp = buffer[i];
+ buffer[i] = buffer[j];
+ buffer[j] = tmp;
+ i++;
+ j--;
+ }
+ *length += number_length;
+}
+
+static void FillDigits64FixedLength(uint64_t number,
+ int,
+ Vector<char> buffer,
+ int* length) {
+ const uint32_t kTen7 = 10000000;
+ // For efficiency cut the number into 3 uint32_t parts, and print those.
+ uint32_t part2 = static_cast<uint32_t>(number % kTen7);
+ number /= kTen7;
+ uint32_t part1 = static_cast<uint32_t>(number % kTen7);
+ uint32_t part0 = static_cast<uint32_t>(number / kTen7);
+
+ FillDigits32FixedLength(part0, 3, buffer, length);
+ FillDigits32FixedLength(part1, 7, buffer, length);
+ FillDigits32FixedLength(part2, 7, buffer, length);
+}
+
+static void FillDigits64(uint64_t number, Vector<char> buffer, int* length) {
+ const uint32_t kTen7 = 10000000;
+ // For efficiency cut the number into 3 uint32_t parts, and print those.
+ uint32_t part2 = static_cast<uint32_t>(number % kTen7);
+ number /= kTen7;
+ uint32_t part1 = static_cast<uint32_t>(number % kTen7);
+ uint32_t part0 = static_cast<uint32_t>(number / kTen7);
+
+ if (part0 != 0) {
+ FillDigits32(part0, buffer, length);
+ FillDigits32FixedLength(part1, 7, buffer, length);
+ FillDigits32FixedLength(part2, 7, buffer, length);
+ } else if (part1 != 0) {
+ FillDigits32(part1, buffer, length);
+ FillDigits32FixedLength(part2, 7, buffer, length);
+ } else {
+ FillDigits32(part2, buffer, length);
+ }
+}
+
+static void RoundUp(Vector<char> buffer, int* length, int* decimal_point) {
+ // An empty buffer represents 0.
+ if (*length == 0) {
+ buffer[0] = '1';
+ *decimal_point = 1;
+ *length = 1;
+ return;
+ }
+ // Round the last digit until we either have a digit that was not '9' or until
+ // we reached the first digit.
+ buffer[(*length) - 1]++;
+ for (int i = (*length) - 1; i > 0; --i) {
+ if (buffer[i] != '0' + 10) {
+ return;
+ }
+ buffer[i] = '0';
+ buffer[i - 1]++;
+ }
+ // If the first digit is now '0' + 10, we would need to set it to '0' and add
+ // a '1' in front. However we reach the first digit only if all following
+ // digits had been '9' before rounding up. Now all trailing digits are '0' and
+ // we simply switch the first digit to '1' and update the decimal-point
+ // (indicating that the point is now one digit to the right).
+ if (buffer[0] == '0' + 10) {
+ buffer[0] = '1';
+ (*decimal_point)++;
+ }
+}
+
+// The given fractionals number represents a fixed-point number with binary
+// point at bit (-exponent).
+// Preconditions:
+// -128 <= exponent <= 0.
+// 0 <= fractionals * 2^exponent < 1
+// The buffer holds the result.
+// The function will round its result. During the rounding-process digits not
+// generated by this function might be updated, and the decimal-point variable
+// might be updated. If this function generates the digits 99 and the buffer
+// already contained "199" (thus yielding a buffer of "19999") then a
+// rounding-up will change the contents of the buffer to "20000".
+static void FillFractionals(uint64_t fractionals,
+ int exponent,
+ int fractional_count,
+ Vector<char> buffer,
+ int* length,
+ int* decimal_point) {
+ DCHECK_LE(-128, exponent);
+ DCHECK_LE(exponent, 0);
+ // 'fractionals' is a fixed-point number, with binary point at bit
+ // (-exponent). Inside the function the non-converted remainder of fractionals
+ // is a fixed-point number, with binary point at bit 'point'.
+ if (-exponent <= 64) {
+ // One 64 bit number is sufficient.
+ DCHECK(fractionals >> 56 == 0);
+ int point = -exponent;
+ for (int i = 0; i < fractional_count; ++i) {
+ if (fractionals == 0)
+ break;
+ // Instead of multiplying by 10 we multiply by 5 and adjust the point
+ // location. This way the fractionals variable will not overflow.
+ // Invariant at the beginning of the loop: fractionals < 2^point.
+ // Initially we have: point <= 64 and fractionals < 2^56
+ // After each iteration the point is decremented by one.
+ // Note that 5^3 = 125 < 128 = 2^7.
+ // Therefore three iterations of this loop will not overflow fractionals
+ // (even without the subtraction at the end of the loop body). At this
+ // time point will satisfy point <= 61 and therefore fractionals < 2^point
+ // and any further multiplication of fractionals by 5 will not overflow.
+ fractionals *= 5;
+ point--;
+ char digit = static_cast<char>(fractionals >> point);
+ buffer[*length] = '0' + digit;
+ (*length)++;
+ fractionals -= static_cast<uint64_t>(digit) << point;
+ }
+ // If the first bit after the point is set we have to round up.
+ if (((fractionals >> (point - 1)) & 1) == 1) {
+ RoundUp(buffer, length, decimal_point);
+ }
+ } else { // We need 128 bits.
+ DCHECK_LT(64, -exponent);
+ DCHECK_LE(-exponent, 128);
+ UInt128 fractionals128 = UInt128(fractionals, 0);
+ fractionals128.Shift(-exponent - 64);
+ int point = 128;
+ for (int i = 0; i < fractional_count; ++i) {
+ if (fractionals128.IsZero())
+ break;
+ // As before: instead of multiplying by 10 we multiply by 5 and adjust the
+ // point location.
+ // This multiplication will not overflow for the same reasons as before.
+ fractionals128.Multiply(5);
+ point--;
+ char digit = static_cast<char>(fractionals128.DivModPowerOf2(point));
+ buffer[*length] = '0' + digit;
+ (*length)++;
+ }
+ if (fractionals128.BitAt(point - 1) == 1) {
+ RoundUp(buffer, length, decimal_point);
+ }
+ }
+}
+
+// Removes leading and trailing zeros.
+// If leading zeros are removed then the decimal point position is adjusted.
+static void TrimZeros(Vector<char> buffer, int* length, int* decimal_point) {
+ while (*length > 0 && buffer[(*length) - 1] == '0') {
+ (*length)--;
+ }
+ int first_non_zero = 0;
+ while (first_non_zero < *length && buffer[first_non_zero] == '0') {
+ first_non_zero++;
+ }
+ if (first_non_zero != 0) {
+ for (int i = first_non_zero; i < *length; ++i) {
+ buffer[i - first_non_zero] = buffer[i];
+ }
+ *length -= first_non_zero;
+ *decimal_point -= first_non_zero;
+ }
+}
+
+bool FastFixedDtoa(double v,
+ int fractional_count,
+ Vector<char> buffer,
+ int* length,
+ int* decimal_point) {
+ const uint32_t kMaxUInt32 = 0xFFFFFFFF;
+ uint64_t significand = Double(v).Significand();
+ int exponent = Double(v).Exponent();
+ // v = significand * 2^exponent (with significand a 53bit integer).
+ // If the exponent is larger than 20 (i.e. we may have a 73bit number) then we
+ // don't know how to compute the representation. 2^73 ~= 9.5*10^21.
+ // If necessary this limit could probably be increased, but we don't need
+ // more.
+ if (exponent > 20)
+ return false;
+ if (fractional_count > 20)
+ return false;
+ *length = 0;
+ // At most kDoubleSignificandSize bits of the significand are non-zero.
+ // Given a 64 bit integer we have 11 0s followed by 53 potentially non-zero
+ // bits: 0..11*..0xxx..53*..xx
+ if (exponent + kDoubleSignificandSize > 64) {
+ // The exponent must be > 11.
+ //
+ // We know that v = significand * 2^exponent.
+ // And the exponent > 11.
+ // We simplify the task by dividing v by 10^17.
+ // The quotient delivers the first digits, and the remainder fits into a 64
+ // bit number.
+ // Dividing by 10^17 is equivalent to dividing by 5^17*2^17.
+ const uint64_t kFive17 = UINT64_2PART_C(0xB1, A2BC2EC5); // 5^17
+ uint64_t divisor = kFive17;
+ int divisor_power = 17;
+ uint64_t dividend = significand;
+ uint32_t quotient;
+ uint64_t remainder;
+ // Let v = f * 2^e with f == significand and e == exponent.
+ // Then need q (quotient) and r (remainder) as follows:
+ // v = q * 10^17 + r
+ // f * 2^e = q * 10^17 + r
+ // f * 2^e = q * 5^17 * 2^17 + r
+ // If e > 17 then
+ // f * 2^(e-17) = q * 5^17 + r/2^17
+ // else
+ // f = q * 5^17 * 2^(17-e) + r/2^e
+ if (exponent > divisor_power) {
+ // We only allow exponents of up to 20 and therefore (17 - e) <= 3
+ dividend <<= exponent - divisor_power;
+ quotient = static_cast<uint32_t>(dividend / divisor);
+ remainder = (dividend % divisor) << divisor_power;
+ } else {
+ divisor <<= divisor_power - exponent;
+ quotient = static_cast<uint32_t>(dividend / divisor);
+ remainder = (dividend % divisor) << exponent;
+ }
+ FillDigits32(quotient, buffer, length);
+ FillDigits64FixedLength(remainder, divisor_power, buffer, length);
+ *decimal_point = *length;
+ } else if (exponent >= 0) {
+ // 0 <= exponent <= 11
+ significand <<= exponent;
+ FillDigits64(significand, buffer, length);
+ *decimal_point = *length;
+ } else if (exponent > -kDoubleSignificandSize) {
+ // We have to cut the number.
+ uint64_t integrals = significand >> -exponent;
+ uint64_t fractionals = significand - (integrals << -exponent);
+ if (integrals > kMaxUInt32) {
+ FillDigits64(integrals, buffer, length);
+ } else {
+ FillDigits32(static_cast<uint32_t>(integrals), buffer, length);
+ }
+ *decimal_point = *length;
+ FillFractionals(fractionals, exponent, fractional_count, buffer, length,
+ decimal_point);
+ } else if (exponent < -128) {
+ // This configuration (with at most 20 digits) means that all digits must be
+ // 0.
+ DCHECK_LE(fractional_count, 20);
+ buffer[0] = '\0';
+ *length = 0;
+ *decimal_point = -fractional_count;
+ } else {
+ *decimal_point = 0;
+ FillFractionals(significand, exponent, fractional_count, buffer, length,
+ decimal_point);
+ }
+ TrimZeros(buffer, length, decimal_point);
+ buffer[*length] = '\0';
+ if ((*length) == 0) {
+ // The string is empty and the decimal_point thus has no importance. Mimick
+ // Gay's dtoa and and set it to -fractional_count.
+ *decimal_point = -fractional_count;
+ }
+ return true;
+}
+
+} // namespace double_conversion
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/dtoa/fixed-dtoa.h b/chromium/third_party/blink/renderer/platform/wtf/dtoa/fixed-dtoa.h
new file mode 100644
index 00000000000..e206ad67438
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/dtoa/fixed-dtoa.h
@@ -0,0 +1,63 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_FIXED_DTOA_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_FIXED_DTOA_H_
+
+#include "third_party/blink/renderer/platform/wtf/dtoa/utils.h"
+
+namespace WTF {
+
+namespace double_conversion {
+
+// Produces digits necessary to print a given number with
+// 'fractional_count' digits after the decimal point.
+// The buffer must be big enough to hold the result plus one terminating null
+// character.
+//
+// The produced digits might be too short in which case the caller has to fill
+// the gaps with '0's.
+// Example: FastFixedDtoa(0.001, 5, ...) is allowed to return buffer = "1", and
+// decimal_point = -2.
+// Halfway cases are rounded towards +/-Infinity (away from 0). The call
+// FastFixedDtoa(0.15, 2, ...) thus returns buffer = "2", decimal_point = 0.
+// The returned buffer may contain digits that would be truncated from the
+// shortest representation of the input.
+//
+// This method only works for some parameters. If it can't handle the input it
+// returns false. The output is null-terminated when the function succeeds.
+bool FastFixedDtoa(double v,
+ int fractional_count,
+ Vector<char> buffer,
+ int* length,
+ int* decimal_point);
+
+} // namespace double_conversion
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_FIXED_DTOA_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/dtoa/strtod.cc b/chromium/third_party/blink/renderer/platform/wtf/dtoa/strtod.cc
new file mode 100644
index 00000000000..867356101f2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/dtoa/strtod.cc
@@ -0,0 +1,447 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "third_party/blink/renderer/platform/wtf/dtoa/strtod.h"
+
+#include <limits.h>
+#include <stdarg.h>
+
+#include "third_party/blink/renderer/platform/wtf/dtoa/bignum.h"
+#include "third_party/blink/renderer/platform/wtf/dtoa/cached-powers.h"
+#include "third_party/blink/renderer/platform/wtf/dtoa/double.h"
+
+namespace WTF {
+
+namespace double_conversion {
+
+// 2^53 = 9007199254740992.
+// Any integer with at most 15 decimal digits will hence fit into a double
+// (which has a 53bit significand) without loss of precision.
+static const int kMaxExactDoubleIntegerDecimalDigits = 15;
+// 2^64 = 18446744073709551616 > 10^19
+static const int kMaxUint64DecimalDigits = 19;
+
+// Max double: 1.7976931348623157 x 10^308
+// Min non-zero double: 4.9406564584124654 x 10^-324
+// Any x >= 10^309 is interpreted as +infinity.
+// Any x <= 10^-324 is interpreted as 0.
+// Note that 2.5e-324 (despite being smaller than the min double) will be read
+// as non-zero (equal to the min non-zero double).
+static const int kMaxDecimalPower = 309;
+static const int kMinDecimalPower = -324;
+
+// 2^64 = 18446744073709551616
+static const uint64_t kMaxUint64 = UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF);
+
+// clang-format off
+static const double exact_powers_of_ten[] = {
+ 1.0, // 10^0
+ 10.0,
+ 100.0,
+ 1000.0,
+ 10000.0,
+ 100000.0,
+ 1000000.0,
+ 10000000.0,
+ 100000000.0,
+ 1000000000.0,
+ 10000000000.0, // 10^10
+ 100000000000.0,
+ 1000000000000.0,
+ 10000000000000.0,
+ 100000000000000.0,
+ 1000000000000000.0,
+ 10000000000000000.0,
+ 100000000000000000.0,
+ 1000000000000000000.0,
+ 10000000000000000000.0,
+ 100000000000000000000.0, // 10^20
+ 1000000000000000000000.0,
+ // 10^22 = 0x21e19e0c9bab2400000 = 0x878678326eac9 * 2^22
+ 10000000000000000000000.0};
+// clang-format on
+
+static const int kExactPowersOfTenSize = ARRAY_SIZE(exact_powers_of_ten);
+
+// Maximum number of significant digits in the decimal representation.
+// In fact the value is 772 (see conversions.cc), but to give us some margin
+// we round up to 780.
+static const int kMaxSignificantDecimalDigits = 780;
+
+static Vector<const char> TrimLeadingZeros(Vector<const char> buffer) {
+ for (int i = 0; i < buffer.length(); i++) {
+ if (buffer[i] != '0') {
+ return buffer.SubVector(i, buffer.length());
+ }
+ }
+ return Vector<const char>(buffer.Start(), 0);
+}
+
+static Vector<const char> TrimTrailingZeros(Vector<const char> buffer) {
+ for (int i = buffer.length() - 1; i >= 0; --i) {
+ if (buffer[i] != '0') {
+ return buffer.SubVector(0, i + 1);
+ }
+ }
+ return Vector<const char>(buffer.Start(), 0);
+}
+
+static void TrimToMaxSignificantDigits(Vector<const char> buffer,
+ int exponent,
+ char* significant_buffer,
+ int* significant_exponent) {
+ for (int i = 0; i < kMaxSignificantDecimalDigits - 1; ++i) {
+ significant_buffer[i] = buffer[i];
+ }
+ // The input buffer has been trimmed. Therefore the last digit must be
+ // different from '0'.
+ DCHECK_NE(buffer[buffer.length() - 1], '0');
+ // Set the last digit to be non-zero. This is sufficient to guarantee
+ // correct rounding.
+ significant_buffer[kMaxSignificantDecimalDigits - 1] = '1';
+ *significant_exponent =
+ exponent + (buffer.length() - kMaxSignificantDecimalDigits);
+}
+
+// Reads digits from the buffer and converts them to a uint64.
+// Reads in as many digits as fit into a uint64.
+// When the string starts with "1844674407370955161" no further digit is read.
+// Since 2^64 = 18446744073709551616 it would still be possible read another
+// digit if it was less or equal than 6, but this would complicate the code.
+static uint64_t ReadUint64(Vector<const char> buffer,
+ int* number_of_read_digits) {
+ uint64_t result = 0;
+ int i = 0;
+ while (i < buffer.length() && result <= (kMaxUint64 / 10 - 1)) {
+ int digit = buffer[i++] - '0';
+ DCHECK_LE(0, digit);
+ DCHECK_LE(digit, 9);
+ result = 10 * result + digit;
+ }
+ *number_of_read_digits = i;
+ return result;
+}
+
+// Reads a DiyFp from the buffer.
+// The returned DiyFp is not necessarily normalized.
+// If remaining_decimals is zero then the returned DiyFp is accurate.
+// Otherwise it has been rounded and has error of at most 1/2 ulp.
+static void ReadDiyFp(Vector<const char> buffer,
+ DiyFp* result,
+ int* remaining_decimals) {
+ int read_digits;
+ uint64_t significand = ReadUint64(buffer, &read_digits);
+ if (buffer.length() == read_digits) {
+ *result = DiyFp(significand, 0);
+ *remaining_decimals = 0;
+ } else {
+ // Round the significand.
+ if (buffer[read_digits] >= '5') {
+ significand++;
+ }
+ // Compute the binary exponent.
+ int exponent = 0;
+ *result = DiyFp(significand, exponent);
+ *remaining_decimals = buffer.length() - read_digits;
+ }
+}
+
+static bool DoubleStrtod(Vector<const char> trimmed,
+ int exponent,
+ double* result) {
+#if !defined(DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS)
+ // On x86 the floating-point stack can be 64 or 80 bits wide. If it is
+ // 80 bits wide (as is the case on Linux) then double-rounding occurs and the
+ // result is not accurate.
+ // We know that Windows32 uses 64 bits and is therefore accurate.
+ // Note that the ARM simulator is compiled for 32bits. It therefore exhibits
+ // the same problem.
+ return false;
+#endif
+ if (trimmed.length() <= kMaxExactDoubleIntegerDecimalDigits) {
+ int read_digits;
+ // The trimmed input fits into a double.
+ // If the 10^exponent (resp. 10^-exponent) fits into a double too then we
+ // can compute the result-double simply by multiplying (resp. dividing) the
+ // two numbers.
+ // This is possible because IEEE guarantees that floating-point operations
+ // return the best possible approximation.
+ if (exponent < 0 && -exponent < kExactPowersOfTenSize) {
+ // 10^-exponent fits into a double.
+ *result = static_cast<double>(ReadUint64(trimmed, &read_digits));
+ DCHECK_EQ(read_digits, trimmed.length());
+ *result /= exact_powers_of_ten[-exponent];
+ return true;
+ }
+ if (0 <= exponent && exponent < kExactPowersOfTenSize) {
+ // 10^exponent fits into a double.
+ *result = static_cast<double>(ReadUint64(trimmed, &read_digits));
+ DCHECK_EQ(read_digits, trimmed.length());
+ *result *= exact_powers_of_ten[exponent];
+ return true;
+ }
+ int remaining_digits =
+ kMaxExactDoubleIntegerDecimalDigits - trimmed.length();
+ if ((0 <= exponent) &&
+ (exponent - remaining_digits < kExactPowersOfTenSize)) {
+ // The trimmed string was short and we can multiply it with
+ // 10^remaining_digits. As a result the remaining exponent now fits
+ // into a double too.
+ *result = static_cast<double>(ReadUint64(trimmed, &read_digits));
+ DCHECK_EQ(read_digits, trimmed.length());
+ *result *= exact_powers_of_ten[remaining_digits];
+ *result *= exact_powers_of_ten[exponent - remaining_digits];
+ return true;
+ }
+ }
+ return false;
+}
+
+// Returns 10^exponent as an exact DiyFp.
+// The given exponent must be in the range [1; kDecimalExponentDistance[.
+static DiyFp AdjustmentPowerOfTen(int exponent) {
+ DCHECK_LT(0, exponent);
+ DCHECK_LT(exponent, PowersOfTenCache::kDecimalExponentDistance);
+ // Simply hardcode the remaining powers for the given decimal exponent
+ // distance.
+ DCHECK_EQ(PowersOfTenCache::kDecimalExponentDistance, 8);
+ switch (exponent) {
+ case 1:
+ return DiyFp(UINT64_2PART_C(0xa0000000, 00000000), -60);
+ case 2:
+ return DiyFp(UINT64_2PART_C(0xc8000000, 00000000), -57);
+ case 3:
+ return DiyFp(UINT64_2PART_C(0xfa000000, 00000000), -54);
+ case 4:
+ return DiyFp(UINT64_2PART_C(0x9c400000, 00000000), -50);
+ case 5:
+ return DiyFp(UINT64_2PART_C(0xc3500000, 00000000), -47);
+ case 6:
+ return DiyFp(UINT64_2PART_C(0xf4240000, 00000000), -44);
+ case 7:
+ return DiyFp(UINT64_2PART_C(0x98968000, 00000000), -40);
+ default:
+ UNREACHABLE();
+ return DiyFp(0, 0);
+ }
+}
+
+// If the function returns true then the result is the correct double.
+// Otherwise it is either the correct double or the double that is just below
+// the correct double.
+static bool DiyFpStrtod(Vector<const char> buffer,
+ int exponent,
+ double* result) {
+ DiyFp input;
+ int remaining_decimals;
+ ReadDiyFp(buffer, &input, &remaining_decimals);
+ // Since we may have dropped some digits the input is not accurate.
+ // If remaining_decimals is different than 0 than the error is at most
+ // .5 ulp (unit in the last place).
+ // We don't want to deal with fractions and therefore keep a common
+ // denominator.
+ const int kDenominatorLog = 3;
+ const int kDenominator = 1 << kDenominatorLog;
+ // Move the remaining decimals into the exponent.
+ exponent += remaining_decimals;
+ int64_t error = (remaining_decimals == 0 ? 0 : kDenominator / 2);
+
+ int old_e = input.E();
+ input.Normalize();
+ error <<= old_e - input.E();
+
+ DCHECK_LE(exponent, PowersOfTenCache::kMaxDecimalExponent);
+ if (exponent < PowersOfTenCache::kMinDecimalExponent) {
+ *result = 0.0;
+ return true;
+ }
+ DiyFp cached_power;
+ int cached_decimal_exponent;
+ PowersOfTenCache::GetCachedPowerForDecimalExponent(exponent, &cached_power,
+ &cached_decimal_exponent);
+
+ if (cached_decimal_exponent != exponent) {
+ int adjustment_exponent = exponent - cached_decimal_exponent;
+ DiyFp adjustment_power = AdjustmentPowerOfTen(adjustment_exponent);
+ input.Multiply(adjustment_power);
+ if (kMaxUint64DecimalDigits - buffer.length() >= adjustment_exponent) {
+ // The product of input with the adjustment power fits into a 64 bit
+ // integer.
+ DCHECK_EQ(DiyFp::kSignificandSize, 64);
+ } else {
+ // The adjustment power is exact. There is hence only an error of 0.5.
+ error += kDenominator / 2;
+ }
+ }
+
+ input.Multiply(cached_power);
+ // The error introduced by a multiplication of a*b equals
+ // error_a + error_b + error_a*error_b/2^64 + 0.5
+ // Substituting a with 'input' and b with 'cached_power' we have
+ // error_b = 0.5 (all cached powers have an error of less than 0.5 ulp),
+ // error_ab = 0 or 1 / kDenominator > error_a*error_b/ 2^64
+ int error_b = kDenominator / 2;
+ int error_ab = (error == 0 ? 0 : 1); // We round up to 1.
+ int fixed_error = kDenominator / 2;
+ error += error_b + error_ab + fixed_error;
+
+ old_e = input.E();
+ input.Normalize();
+ error <<= old_e - input.E();
+
+ // See if the double's significand changes if we add/subtract the error.
+ int order_of_magnitude = DiyFp::kSignificandSize + input.E();
+ int effective_significand_size =
+ Double::SignificandSizeForOrderOfMagnitude(order_of_magnitude);
+ int precision_digits_count =
+ DiyFp::kSignificandSize - effective_significand_size;
+ if (precision_digits_count + kDenominatorLog >= DiyFp::kSignificandSize) {
+ // This can only happen for very small denormals. In this case the
+ // half-way multiplied by the denominator exceeds the range of an uint64.
+ // Simply shift everything to the right.
+ int shift_amount = (precision_digits_count + kDenominatorLog) -
+ DiyFp::kSignificandSize + 1;
+ input.set_f(input.F() >> shift_amount);
+ input.set_e(input.E() + shift_amount);
+ // We add 1 for the lost precision of error, and kDenominator for
+ // the lost precision of input.f().
+ error = (error >> shift_amount) + 1 + kDenominator;
+ precision_digits_count -= shift_amount;
+ }
+ // We use uint64_ts now. This only works if the DiyFp uses uint64_ts too.
+ DCHECK_EQ(DiyFp::kSignificandSize, 64);
+ DCHECK_LT(precision_digits_count, 64);
+ uint64_t one64 = 1;
+ uint64_t precision_bits_mask = (one64 << precision_digits_count) - 1;
+ uint64_t precision_bits = input.F() & precision_bits_mask;
+ uint64_t half_way = one64 << (precision_digits_count - 1);
+ precision_bits *= kDenominator;
+ half_way *= kDenominator;
+ DiyFp rounded_input(input.F() >> precision_digits_count,
+ input.E() + precision_digits_count);
+ if (precision_bits >= half_way + error) {
+ rounded_input.set_f(rounded_input.F() + 1);
+ }
+ // If the last_bits are too close to the half-way case than we are too
+ // inaccurate and round down. In this case we return false so that we can
+ // fall back to a more precise algorithm.
+
+ *result = Double(rounded_input).Value();
+ if (half_way - error < precision_bits && precision_bits < half_way + error) {
+ // Too imprecise. The caller will have to fall back to a slower version.
+ // However the returned number is guaranteed to be either the correct
+ // double, or the next-lower double.
+ return false;
+ } else {
+ return true;
+ }
+}
+
+// Returns the correct double for the buffer*10^exponent.
+// The variable guess should be a close guess that is either the correct double
+// or its lower neighbor (the nearest double less than the correct one).
+// Preconditions:
+// buffer.length() + exponent <= kMaxDecimalPower + 1
+// buffer.length() + exponent > kMinDecimalPower
+// buffer.length() <= kMaxDecimalSignificantDigits
+static double BignumStrtod(Vector<const char> buffer,
+ int exponent,
+ double guess) {
+ if (guess == Double::Infinity()) {
+ return guess;
+ }
+
+ DiyFp upper_boundary = Double(guess).UpperBoundary();
+
+ DCHECK_LE(buffer.length() + exponent, kMaxDecimalPower + 1);
+ DCHECK_GT(buffer.length() + exponent, kMinDecimalPower);
+ DCHECK_LE(buffer.length(), kMaxSignificantDecimalDigits);
+ // Make sure that the Bignum will be able to hold all our numbers.
+ // Our Bignum implementation has a separate field for exponents. Shifts will
+ // consume at most one bigit (< 64 bits).
+ // ln(10) == 3.3219...
+ DCHECK_LT(((kMaxDecimalPower + 1) * 333 / 100), Bignum::kMaxSignificantBits);
+ Bignum input;
+ Bignum boundary;
+ input.AssignDecimalString(buffer);
+ boundary.AssignUInt64(upper_boundary.F());
+ if (exponent >= 0) {
+ input.MultiplyByPowerOfTen(exponent);
+ } else {
+ boundary.MultiplyByPowerOfTen(-exponent);
+ }
+ if (upper_boundary.E() > 0) {
+ boundary.ShiftLeft(upper_boundary.E());
+ } else {
+ input.ShiftLeft(-upper_boundary.E());
+ }
+ int comparison = Bignum::Compare(input, boundary);
+ if (comparison < 0) {
+ return guess;
+ } else if (comparison > 0) {
+ return Double(guess).NextDouble();
+ } else if ((Double(guess).Significand() & 1) == 0) {
+ // Round towards even.
+ return guess;
+ } else {
+ return Double(guess).NextDouble();
+ }
+}
+
+double Strtod(Vector<const char> buffer, int exponent) {
+ Vector<const char> left_trimmed = TrimLeadingZeros(buffer);
+ Vector<const char> trimmed = TrimTrailingZeros(left_trimmed);
+ exponent += left_trimmed.length() - trimmed.length();
+ if (trimmed.length() == 0)
+ return 0.0;
+ if (trimmed.length() > kMaxSignificantDecimalDigits) {
+ char significant_buffer[kMaxSignificantDecimalDigits];
+ int significant_exponent;
+ TrimToMaxSignificantDigits(trimmed, exponent, significant_buffer,
+ &significant_exponent);
+ return Strtod(
+ Vector<const char>(significant_buffer, kMaxSignificantDecimalDigits),
+ significant_exponent);
+ }
+ if (exponent + trimmed.length() - 1 >= kMaxDecimalPower) {
+ return Double::Infinity();
+ }
+ if (exponent + trimmed.length() <= kMinDecimalPower) {
+ return 0.0;
+ }
+
+ double guess;
+ if (DoubleStrtod(trimmed, exponent, &guess) ||
+ DiyFpStrtod(trimmed, exponent, &guess)) {
+ return guess;
+ }
+ return BignumStrtod(trimmed, exponent, guess);
+}
+
+} // namespace double_conversion
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/dtoa/strtod.h b/chromium/third_party/blink/renderer/platform/wtf/dtoa/strtod.h
new file mode 100644
index 00000000000..827c52a62d4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/dtoa/strtod.h
@@ -0,0 +1,45 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_STRTOD_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_STRTOD_H_
+
+#include "third_party/blink/renderer/platform/wtf/dtoa/utils.h"
+
+namespace WTF {
+
+namespace double_conversion {
+
+// The buffer must only contain digits in the range [0-9]. It must not
+// contain a dot or a sign. It must not start with '0', and must not be empty.
+double Strtod(Vector<const char> buffer, int exponent);
+
+} // namespace double_conversion
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_STRTOD_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/dtoa/utils.h b/chromium/third_party/blink/renderer/platform/wtf/dtoa/utils.h
new file mode 100644
index 00000000000..19a222edfd0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/dtoa/utils.h
@@ -0,0 +1,305 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_UTILS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_UTILS_H_
+
+#include <string.h>
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+#define UNIMPLEMENTED NOTREACHED
+#define UNREACHABLE NOTREACHED
+
+// Double operations detection based on target architecture.
+// Linux uses a 80bit wide floating point stack on x86. This induces double
+// rounding, which in turn leads to wrong results.
+// An easy way to test if the floating-point operations are correct is to
+// evaluate: 89255.0/1e22. If the floating-point stack is 64 bits wide then
+// the result is equal to 89255e-22.
+// The best way to test this, is to create a division-function and to compare
+// the output of the division with the expected result. (Inlining must be
+// disabled.)
+// On Linux,x86 89255e-22 != Div_double(89255.0/1e22)
+#if defined(_M_X64) || defined(__x86_64__) || defined(__ARMEL__) || \
+ defined(__aarch64__) || defined(__MIPSEL__)
+#define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1
+#elif defined(_M_IX86) || defined(__i386__)
+#if defined(_WIN32)
+// Windows uses a 64bit wide floating point stack.
+#define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1
+#else
+#undef DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS
+#endif // _WIN32
+#else
+#error Target architecture was not detected as supported by Double-Conversion.
+#endif
+
+#if defined(_WIN32) && !defined(__MINGW32__)
+
+typedef signed char int8_t;
+typedef unsigned char uint8_t;
+typedef short int16_t; // NOLINT
+typedef unsigned short uint16_t; // NOLINT
+typedef int int32_t;
+typedef unsigned int uint32_t;
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+// intptr_t and friends are defined in crtdefs.h through stdio.h.
+
+#else
+
+#include <stdint.h>
+
+#endif
+
+// The following macro works on both 32 and 64-bit platforms.
+// Usage: instead of writing 0x1234567890123456
+// write UINT64_2PART_C(0x12345678,90123456);
+#define UINT64_2PART_C(a, b) (((static_cast<uint64_t>(a) << 32) + 0x##b##u))
+
+// The expression ARRAY_SIZE(a) is a compile-time constant of type
+// size_t which represents the number of elements of the given
+// array. You should only use ARRAY_SIZE on statically allocated
+// arrays.
+#define ARRAY_SIZE(a) \
+ ((sizeof(a) / sizeof(*(a))) / \
+ static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
+
+// A macro to disallow the evil copy constructor and operator= functions
+// This should be used in the private: declarations for a class
+#ifndef DISALLOW_COPY_AND_ASSIGN
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&); \
+ void operator=(const TypeName&)
+#endif // DISALLOW_COPY_AND_ASSIGN
+
+// 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() = delete; \
+ DISALLOW_COPY_AND_ASSIGN(TypeName)
+
+namespace WTF {
+
+namespace double_conversion {
+
+static const int kCharSize = sizeof(char);
+
+// Returns the maximum of the two parameters.
+template <typename T>
+static T Max(T a, T b) {
+ return a < b ? b : a;
+}
+
+// Returns the minimum of the two parameters.
+template <typename T>
+static T Min(T a, T b) {
+ return a < b ? a : b;
+}
+
+inline int StrLength(const char* string) {
+ size_t length = strlen(string);
+ DCHECK_EQ(length, static_cast<size_t>(static_cast<int>(length)));
+ return static_cast<int>(length);
+}
+
+// This is a simplified version of V8's Vector class.
+template <typename T>
+class Vector {
+ public:
+ Vector() : start_(NULL), length_(0) {}
+ Vector(T* data, int length) : start_(data), length_(length) {
+ DCHECK(length == 0 || (length > 0 && data != nullptr));
+ }
+
+ // Returns a vector using the same backing storage as this one,
+ // spanning from and including 'from', to but not including 'to'.
+ Vector<T> SubVector(int from, int to) {
+ DCHECK_LE(to, length_);
+ DCHECK_LT(from, to);
+ DCHECK_LE(0, from);
+ return Vector<T>(Start() + from, to - from);
+ }
+
+ // Returns the length of the vector.
+ int length() const { return length_; }
+
+ // Returns whether or not the vector is empty.
+ bool is_empty() const { return length_ == 0; }
+
+ // Returns the pointer to the start of the data in the vector.
+ T* Start() const { return start_; }
+
+ // Access individual vector elements.
+ T& operator[](int index) const {
+ CHECK_LE(0, index);
+ CHECK_LT(index, length_);
+ return start_[index];
+ }
+
+ T& First() { return start_[0]; }
+
+ T& Last() { return start_[length_ - 1]; }
+
+ private:
+ T* start_;
+ int length_;
+};
+
+// Helper class for building result strings in a character buffer. The
+// purpose of the class is to use safe operations that checks the
+// buffer bounds on all operations in debug mode.
+class StringBuilder {
+ public:
+ StringBuilder(char* buffer, int size) : buffer_(buffer, size), position_(0) {}
+
+ ~StringBuilder() {
+ if (!is_finalized())
+ Finalize();
+ }
+
+ int size() const { return buffer_.length(); }
+
+ // Get the current position in the builder.
+ int GetPosition() const {
+ DCHECK(!is_finalized());
+ return position_;
+ }
+
+ // Set the current position in the builder.
+ void SetPosition(int position) {
+ DCHECK(!is_finalized());
+ SECURITY_DCHECK(position < size());
+ position_ = position;
+ }
+
+ // Reset the position.
+ void Reset() { position_ = 0; }
+
+ // Add a single character to the builder. It is not allowed to add
+ // 0-characters; use the Finalize() method to terminate the string
+ // instead.
+ void AddCharacter(char c) {
+ DCHECK_NE(c, '\0');
+ DCHECK(!is_finalized());
+ DCHECK_LT(position_, buffer_.length());
+ buffer_[position_++] = c;
+ }
+
+ // Add an entire string to the builder. Uses strlen() internally to
+ // compute the length of the input string.
+ void AddString(const char* s) { AddSubstring(s, StrLength(s)); }
+
+ // Add the first 'n' characters of the given string 's' to the
+ // builder. The input string must have enough characters.
+ void AddSubstring(const char* s, int n) {
+ DCHECK(!is_finalized());
+ DCHECK_LT(position_ + n, buffer_.length());
+ SECURITY_DCHECK(static_cast<size_t>(n) <= strlen(s));
+ memcpy(&buffer_[position_], s, n * kCharSize);
+ position_ += n;
+ }
+
+ // Add character padding to the builder. If count is non-positive,
+ // nothing is added to the builder.
+ void AddPadding(char c, int count) {
+ for (int i = 0; i < count; i++) {
+ AddCharacter(c);
+ }
+ }
+
+ // Finalize the string by 0-terminating it and returning the buffer.
+ char* Finalize() {
+ DCHECK(!is_finalized());
+ DCHECK_LT(position_, buffer_.length());
+ buffer_[position_] = '\0';
+ // Make sure nobody managed to add a 0-character to the
+ // buffer while building the string.
+ DCHECK_EQ(strlen(buffer_.Start()), static_cast<size_t>(position_));
+ position_ = -1;
+ DCHECK(is_finalized());
+ return buffer_.Start();
+ }
+
+ private:
+ Vector<char> buffer_;
+ int position_;
+
+ bool is_finalized() const { return position_ < 0; }
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(StringBuilder);
+};
+
+// The type-based aliasing rule allows the compiler to assume that pointers of
+// different types (for some definition of different) never alias each other.
+// Thus the following code does not work:
+//
+// float f = foo();
+// int fbits = *(int*)(&f);
+//
+// The compiler 'knows' that the int pointer can't refer to f since the types
+// don't match, so the compiler may cache f in a register, leaving random data
+// in fbits. Using C++ style casts makes no difference, however a pointer to
+// char data is assumed to alias any other pointer. This is the 'memcpy
+// exception'.
+//
+// Bit_cast uses the memcpy exception to move the bits from a variable of one
+// type of a variable of another type. Of course the end result is likely to
+// be implementation dependent. Most compilers (gcc-4.2 and MSVC 2005)
+// will completely optimize BitCast away.
+//
+// There is an additional use for BitCast.
+// Recent gccs will warn when they see casts that may result in breakage due to
+// the type-based aliasing rule. If you have checked that there is no breakage
+// you can use BitCast to cast one pointer type to another. This confuses gcc
+// enough that it can no longer see that you have cast one pointer type to
+// another thus avoiding the warning.
+template <class Dest, class Source>
+inline Dest BitCast(const Source& source) {
+ // Compile time assertion: sizeof(Dest) == sizeof(Source)
+ // A compile error here means your Dest and Source have different sizes.
+ static_assert(sizeof(Dest) == sizeof(Source), "sizes should be equal");
+
+ Dest dest;
+ memcpy(&dest, &source, sizeof(dest));
+ return dest;
+}
+
+template <class Dest, class Source>
+inline Dest BitCast(Source* source) {
+ return BitCast<Dest>(reinterpret_cast<uintptr_t>(source));
+}
+
+} // namespace double_conversion
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DTOA_UTILS_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/dtoa_test.cc b/chromium/third_party/blink/renderer/platform/wtf/dtoa_test.cc
new file mode 100644
index 00000000000..f00c28852b8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/dtoa_test.cc
@@ -0,0 +1,42 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/wtf/dtoa.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace WTF {
+
+TEST(DtoaTest, TestNumberToFixedPrecisionString) {
+ NumberToStringBuffer buffer;
+
+ // There should be no trailing decimal or zeros.
+ NumberToFixedPrecisionString(0.0, 6, buffer);
+ EXPECT_STREQ("0", buffer);
+
+ // Up to 6 leading zeros.
+ NumberToFixedPrecisionString(0.00000123123123, 6, buffer);
+ EXPECT_STREQ("0.00000123123", buffer);
+
+ NumberToFixedPrecisionString(0.000000123123123, 6, buffer);
+ EXPECT_STREQ("1.23123e-7", buffer);
+
+ // Up to 6 places before the decimal.
+ NumberToFixedPrecisionString(123123.123, 6, buffer);
+ EXPECT_STREQ("123123", buffer);
+
+ NumberToFixedPrecisionString(1231231.23, 6, buffer);
+ EXPECT_STREQ("1.23123e+6", buffer);
+
+ // Don't strip trailing zeros in exponents.
+ // http://crbug.com/545711
+ NumberToFixedPrecisionString(0.000000000123123, 6, buffer);
+ EXPECT_STREQ("1.23123e-10", buffer);
+
+ // FIXME: Trailing zeros before exponents should be stripped.
+ NumberToFixedPrecisionString(0.0000000001, 6, buffer);
+ EXPECT_STREQ("1.00000e-10", buffer);
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/dynamic_annotations.cc b/chromium/third_party/blink/renderer/platform/wtf/dynamic_annotations.cc
new file mode 100644
index 00000000000..aedfdf7747e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/dynamic_annotations.cc
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "third_party/blink/renderer/platform/wtf/dynamic_annotations.h"
+
+#if defined(WTF_USE_DYNAMIC_ANNOTATIONS) && \
+ !defined(WTF_USE_DYNAMIC_ANNOTATIONS_NOIMPL)
+
+// Identical code folding(-Wl,--icf=all) countermeasures.
+// This makes all Annotate* functions different, which prevents the linker from
+// folding them.
+#ifdef __COUNTER__
+#define DYNAMIC_ANNOTATIONS_IMPL \
+ volatile short lineno = (__LINE__ << 8) + __COUNTER__; \
+ (void)lineno;
+#else
+#define DYNAMIC_ANNOTATIONS_IMPL \
+ volatile short lineno = (__LINE__ << 8); \
+ (void)lineno;
+#endif
+
+void WTFAnnotateBenignRaceSized(const char*,
+ int,
+ const volatile void*,
+ long,
+ const char*) {
+ DYNAMIC_ANNOTATIONS_IMPL
+}
+
+void WTFAnnotateHappensBefore(const char*, int, const volatile void*) {
+ DYNAMIC_ANNOTATIONS_IMPL
+}
+
+void WTFAnnotateHappensAfter(const char*, int, const volatile void*) {
+ DYNAMIC_ANNOTATIONS_IMPL
+}
+
+#endif // defined(WTF_USE_DYNAMIC_ANNOTATIONS) &&
+ // !defined(WTF_USE_DYNAMIC_ANNOTATIONS_NOIMPL)
diff --git a/chromium/third_party/blink/renderer/platform/wtf/dynamic_annotations.h b/chromium/third_party/blink/renderer/platform/wtf/dynamic_annotations.h
new file mode 100644
index 00000000000..a85b6c8d933
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/dynamic_annotations.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DYNAMIC_ANNOTATIONS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DYNAMIC_ANNOTATIONS_H_
+
+/* This file defines dynamic annotations for use with dynamic analysis
+ * tool such as ThreadSanitizer, Valgrind, etc.
+ *
+ * Dynamic annotation is a source code annotation that affects
+ * the generated code (that is, the annotation is not a comment).
+ * Each such annotation is attached to a particular
+ * instruction and/or to a particular object (address) in the program.
+ *
+ * By using dynamic annotations a developer can give more details to the dynamic
+ * analysis tool to improve its precision.
+ *
+ * In C/C++ program the annotations are represented as C macros.
+ * With the default build flags, these macros are empty, hence don't affect
+ * performance of a compiled binary.
+ * If dynamic annotations are enabled, they just call no-op functions.
+ * The dynamic analysis tools can intercept these functions and replace them
+ * with their own implementations.
+ *
+ * See http://code.google.com/p/data-race-test/wiki/DynamicAnnotations for more
+ * information.
+ */
+
+#include "third_party/blink/renderer/platform/wtf/wtf_export.h"
+
+#if defined(WTF_USE_DYNAMIC_ANNOTATIONS)
+/* Tell data race detector that we're not interested in reports on the given
+ * address range. */
+#define WTF_ANNOTATE_BENIGN_RACE_SIZED(address, size, description) \
+ WTFAnnotateBenignRaceSized(__FILE__, __LINE__, address, size, description)
+#define WTF_ANNOTATE_BENIGN_RACE(pointer, description) \
+ WTFAnnotateBenignRaceSized(__FILE__, __LINE__, pointer, sizeof(*(pointer)), \
+ description)
+
+/* Annotations for user-defined synchronization mechanisms.
+ * These annotations can be used to define happens-before arcs in user-defined
+ * synchronization mechanisms: the race detector will infer an arc from
+ * the former to the latter when they share the same argument pointer.
+ *
+ * The most common case requiring annotations is atomic reference counting:
+ * bool deref() {
+ * ANNOTATE_HAPPENS_BEFORE(&ref_count_);
+ * if (!atomicDecrement(&ref_count_)) {
+ * // ref_count_ is now 0
+ * ANNOTATE_HAPPENS_AFTER(&ref_count_);
+ * // "return true; happens-after each atomicDecrement of ref_count_"
+ * return true;
+ * }
+ * return false;
+ * }
+ */
+#define WTF_ANNOTATE_HAPPENS_BEFORE(address) \
+ WTFAnnotateHappensBefore(__FILE__, __LINE__, address)
+#define WTF_ANNOTATE_HAPPENS_AFTER(address) \
+ WTFAnnotateHappensAfter(__FILE__, __LINE__, address)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* Don't use these directly, use the above macros instead. */
+WTF_EXPORT void WTFAnnotateBenignRaceSized(const char* file,
+ int line,
+ const volatile void* memory,
+ long size,
+ const char* description);
+WTF_EXPORT void WTFAnnotateHappensBefore(const char* file,
+ int line,
+ const volatile void* address);
+WTF_EXPORT void WTFAnnotateHappensAfter(const char* file,
+ int line,
+ const volatile void* address);
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#else // defined(WTF_USE_DYNAMIC_ANNOTATIONS)
+/* These macros are empty when dynamic annotations are not enabled so you can
+ * use them without affecting the performance of release binaries. */
+#define WTF_ANNOTATE_BENIGN_RACE_SIZED(address, size, description)
+#define WTF_ANNOTATE_BENIGN_RACE(pointer, description)
+#define WTF_ANNOTATE_HAPPENS_BEFORE(address)
+#define WTF_ANNOTATE_HAPPENS_AFTER(address)
+#endif // defined(WTF_USE_DYNAMIC_ANNOTATIONS)
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_DYNAMIC_ANNOTATIONS_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/experimental/README.md b/chromium/third_party/blink/renderer/platform/wtf/experimental/README.md
new file mode 100644
index 00000000000..0600fc88194
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/experimental/README.md
@@ -0,0 +1,17 @@
+# WTF Experimental
+
+This directory is a place for work-in-progress implementation of [HashTable v2].
+
+**Warning**: The content of this directory is not ready for production use.
+Don't use files in this directory without prior consent of the author.
+
+## Experimental files policy
+
+The use of experimental files must be closely controled and monitored by
+the author. If any anomalies caused by files in this directory are observed,
+please contact the author (essentially yutak@chromium.org) immediately.
+
+When files in this directory become ready for production use, they should be
+moved out of this directory.
+
+[HashTable v2]: https://docs.google.com/document/d/1-N-vUWg790wJc1CHCC4C_v-HtQNg9n9w5Y_6KtaKFlU/edit?usp=sharing
diff --git a/chromium/third_party/blink/renderer/platform/wtf/experimental/container_type_operations.h b/chromium/third_party/blink/renderer/platform/wtf/experimental/container_type_operations.h
new file mode 100644
index 00000000000..4163d777cd1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/experimental/container_type_operations.h
@@ -0,0 +1,620 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_EXPERIMENTAL_CONTAINER_TYPE_OPERATIONS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_EXPERIMENTAL_CONTAINER_TYPE_OPERATIONS_H_
+
+// *****************************************************************************
+// EXPERIMENTAL: DO NOT USE IN PRODUCTION CODE YET!
+// *****************************************************************************
+
+#include <type_traits>
+#include <utility>
+
+namespace WTF {
+namespace experimental {
+
+// ContainerTypeOperations defines type operations in containers that are not
+// specific to container implementation, such as placement initialization and
+// copying or moving elements.
+//
+// The class template ContainerTypeOperations can be specialized so you can
+// define customized operations such as fast copy with memcpy().
+//
+// TODO(yutak): Implement good enough defaults for common data types such as
+// fundamental types or scoped_refptr<T>, so in most cases people don't have to
+// care about those functions.
+//
+// ====================================
+// ContainerTypeOperations requirements
+// ====================================
+//
+// Each ContainerTypeOperations implementation must meet all the requiements
+// marked as "minimal complete definition" below. The absent functions will be
+// supplemented with sensible defaults created from present functions.
+//
+// Naming conventions used below:
+//
+// uninitialized_storage (or simply "uninitialized")
+// A variable name indicating the memory it points to is in the
+// container's storage, and is uninitialized, that is, T's constructor
+// is not called on it.
+// storage
+// A variable name indicating the memory it points to is in the
+// container's storage, and is initialized.
+// value
+// A variable name indicating the memory it points to is not necessarily
+// in the container's storage. Generally, it indicates a value specified
+// by the user of the container.
+// T
+// The main value type of the container. The container's storage will
+// contain an array of Ts.
+// InT
+// A type name that's possibly different from T. The actual type depends
+// on what the user code has specified to a member function of a
+// container. This is useful to avoid type conversions to T, and this
+// technique is called "heterogeneous lookup" (introduced in C++14
+// version of std containers).
+//
+// In the actual traits implementation, the corresponding function does
+// not have to be a function template. It can be a set of overloaded
+// functions, or even a combination of function templates and
+// functions, as long as all the desirable input types can be accepted.
+// For example, suppose you are implementing Assign() explained below.
+// In this case, both of the following are OK:
+//
+// (a) Accepts any input value
+// template <typename InT>
+// static void Assign(T& storage, InT&& value);
+//
+// (b) Accepts String and const char* (suppose T is String-ish)
+// static void Assign(T& storage, const String& value);
+// static void Assign(T& storage, const char* value);
+//
+// At least, T must be acceptable as a replacement of InT in any
+// function definitions.
+//
+// 1. Default initialization
+//
+// static void DefaultInitialize(T& uninitialized);
+// static void DefaultInitializeRange(T* uninitialized_begin,
+// T* uninitialized_end);
+//
+// Minimal complete definition:
+// DefaultInitialize() or DefaultInitializeRange().
+//
+// After one of these functions is called, the memory range will become
+// initialized.
+//
+// 2. Destruction
+//
+// static void Destruct(T& storage);
+// static void DestructRange(T* storage_begin,
+// T* storage_end);
+//
+// Minimal complete definition: Destruct() or DestructRange().
+//
+// After one of these functions is called, the memory range will become
+// uninitialized.
+//
+// 3. Copy and move
+//
+// static void Assign(T& storage, const InT& value); // Copy
+// static void Assign(T& storage, InT&& value); // Move
+// // Note: you can implement both in one function template if you use
+// // "forwarding reference".
+//
+// static void CopyRange(const InT* value_begin,
+// const InT* value_end,
+// T* storage_begin);
+// static void MoveRange(InT* value_begin,
+// InT* value_end,
+// T* storage_begin);
+//
+// static void CopyOverlappingRange(const InT* value_begin,
+// const InT* value_end,
+// T* storage_begin);
+// static void MoveOverlappingRange(InT* value_begin,
+// InT* value_end,
+// T* storage_begin);
+//
+// Minimal complete definition: Copying Assign().
+//
+// The storage will remain initialized even after moving, as if you call
+// std::move(). You need to destruct the elements separately.
+//
+// 4. Uninitialized copy and fill
+//
+// static void UninitializedCopy(const InT* value_begin,
+// const InT* value_end,
+// T* uninitialized_begin);
+//
+// static void UninitializedFill(const InT& value,
+// T* uninitialized_begin,
+// T* uninitialized_end);
+//
+// Minimal complete definition: None (Will be implemented as initialize +
+// copy).
+//
+// 5. Equality
+//
+// static bool Equal(const T& stored_value,
+// const InT& other_value);
+// static bool EqualRange(const T* storage_begin,
+// const T* storage_end,
+// const InT* other_begin);
+//
+// Minimal complete definition: Equal() or EqualRange().
+
+//
+// GenericContainerTypeOperations
+//
+
+// This class template defines type operations that work for any type T.
+template <typename T>
+struct GenericContainerTypeOperations {
+ static void DefaultInitialize(T& uninitialized);
+
+ static void Destruct(T& storage);
+
+ template <typename InT>
+ static void Assign(T& storage, InT&& value);
+
+ template <typename InT>
+ static void UninitializedCopy(const InT* value_begin,
+ const InT* value_end,
+ T* uninitialized_begin);
+
+ template <typename InT>
+ static void UninitializedFill(const InT& value,
+ T* uninitialized_begin,
+ T* uninitialized_end);
+
+ template <typename InT>
+ static bool Equal(const T& stored_value, const InT& other_value);
+};
+
+//
+// ContainerTypeOperations
+//
+
+// The global definition of ContainerTypeOperations.
+template <typename T>
+struct ContainerTypeOperations : GenericContainerTypeOperations<T> {};
+
+// TODO(yutak): More specializations for POD types and smart pointers.
+
+//
+// CompleteContainerTypeOperations
+//
+
+// CompleteContainerTypeOperations supplements missing functions as defined
+// above and creates a class template that has every function listed above.
+
+namespace internal {
+// This internal block contains all the implementation detail needed to
+// supplement the missing functions in ContainerTypeOperations<T>.
+
+// Define a predicate struct named Has##FunctionName##Function that tests the
+// existence of a static member function |FunctionName| in TypeOperation taking
+// the arguments specified in the "..." part of the macro.
+#define DEFINE_FUNCTION_DETECTOR(FunctionName, ...) \
+ template <typename TypeOperations, typename T> \
+ auto test##FunctionName##Function(int)->decltype( \
+ TypeOperations::FunctionName(__VA_ARGS__), std::true_type()); \
+ template <typename TypeOperations, typename T> \
+ std::false_type test##FunctionName##Function(long); \
+ template <typename TypeOperations, typename T> \
+ struct Has##FunctionName##Function \
+ : decltype(test##FunctionName##Function<TypeOperations, T>(0)) {}
+// A semicolon is required after the macro use.
+
+// In the following, we define a number of class templates named
+// <FunctionName>Supplement, which provides the default implementation of the
+// function if it is missing in TypeOperations.
+
+DEFINE_FUNCTION_DETECTOR(DefaultInitialize, std::declval<T&>());
+DEFINE_FUNCTION_DETECTOR(DefaultInitializeRange,
+ std::declval<T*>(),
+ std::declval<T*>());
+
+template <typename TypeOperations,
+ typename T,
+ bool hasDefaultInitializeFunction =
+ HasDefaultInitializeFunction<TypeOperations, T>::value,
+ bool hasDefaultInitializeRangeFunction =
+ HasDefaultInitializeRangeFunction<TypeOperations, T>::value>
+struct DefaultInitializeSupplement;
+
+template <typename TypeOperations, typename T>
+struct DefaultInitializeSupplement<TypeOperations, T, true, true> {};
+
+template <typename TypeOperations, typename T>
+struct DefaultInitializeSupplement<TypeOperations, T, true, false> {
+ static void DefaultInitializeRange(T* uninitialized_begin,
+ T* uninitialized_end) {
+ for (T* uninitialized = uninitialized_begin;
+ uninitialized != uninitialized_end; ++uninitialized) {
+ TypeOperations::DefaultInitialize(*uninitialized);
+ }
+ }
+};
+
+template <typename TypeOperations, typename T>
+struct DefaultInitializeSupplement<TypeOperations, T, false, true> {
+ static void DefaultInitialize(T& uninitialized) {
+ TypeOperations::DefaultInitializeRange(&uninitialized, &uninitialized + 1);
+ }
+};
+
+// DefaultInitializeSupplement<TypeOperations, T, false, false> is invalid.
+
+DEFINE_FUNCTION_DETECTOR(Destruct, std::declval<T&>());
+DEFINE_FUNCTION_DETECTOR(DestructRange, std::declval<T*>(), std::declval<T*>());
+
+template <typename TypeOperations,
+ typename T,
+ bool hasDestructFunction =
+ HasDestructFunction<TypeOperations, T>::value,
+ bool hasDestructRangeFunction =
+ HasDestructRangeFunction<TypeOperations, T>::value>
+struct DestructSupplement;
+
+template <typename TypeOperations, typename T>
+struct DestructSupplement<TypeOperations, T, true, true> {};
+
+template <typename TypeOperations, typename T>
+struct DestructSupplement<TypeOperations, T, true, false> {
+ static void DestructRange(T* storage_begin, T* storage_end) {
+ for (T* storage = storage_begin; storage != storage_end; ++storage) {
+ TypeOperations::Destruct(*storage);
+ }
+ }
+};
+
+template <typename TypeOperations, typename T>
+struct DestructSupplement<TypeOperations, T, false, true> {
+ static void Destruct(T& storage) {
+ TypeOperations::DestructRange(&storage, &storage + 1);
+ }
+};
+
+// DestructSupplement<TypeOperations, T, false, false> is invalid.
+
+DEFINE_FUNCTION_DETECTOR(CopyRange,
+ std::declval<const T*>(),
+ std::declval<const T*>(),
+ std::declval<T*>());
+
+template <typename TypeOperations,
+ typename T,
+ bool hasCopyRangeFunction =
+ HasCopyRangeFunction<TypeOperations, T>::value>
+struct CopyRangeSupplement;
+
+template <typename TypeOperations, typename T>
+struct CopyRangeSupplement<TypeOperations, T, true> {};
+
+template <typename TypeOperations, typename T>
+struct CopyRangeSupplement<TypeOperations, T, false> {
+ template <typename InT>
+ static void CopyRange(const InT* value_begin,
+ const InT* value_end,
+ T* storage_begin) {
+ T* storage = storage_begin;
+ for (const InT *value = value_begin; value < value_end;
+ ++value, ++storage) {
+ TypeOperations::Assign(*storage, *value);
+ }
+ }
+};
+
+DEFINE_FUNCTION_DETECTOR(MoveRange,
+ std::declval<T*>(),
+ std::declval<T*>(),
+ std::declval<T*>());
+
+template <typename TypeOperations,
+ typename T,
+ bool hasMoveRangeFunction =
+ HasMoveRangeFunction<TypeOperations, T>::value>
+struct MoveRangeSupplement;
+
+template <typename TypeOperations, typename T>
+struct MoveRangeSupplement<TypeOperations, T, true> {};
+
+template <typename TypeOperations, typename T>
+struct MoveRangeSupplement<TypeOperations, T, false> {
+ template <typename InT>
+ static void MoveRange(InT* value_begin, InT* value_end, T* storage_begin) {
+ T* storage = storage_begin;
+ for (InT *value = value_begin; value != value_end; ++value, ++storage) {
+ TypeOperations::Assign(*storage, std::move(*value));
+ }
+ }
+};
+
+DEFINE_FUNCTION_DETECTOR(CopyOverlappingRange,
+ std::declval<const T*>(),
+ std::declval<const T*>(),
+ std::declval<T*>());
+
+template <typename TypeOperations,
+ typename T,
+ bool hasCopyOverlappingRangeFunction =
+ HasCopyOverlappingRangeFunction<TypeOperations, T>::value>
+struct CopyOverlappingRangeSupplement;
+
+template <typename TypeOperations, typename T>
+struct CopyOverlappingRangeSupplement<TypeOperations, T, true> {};
+
+template <typename TypeOperations, typename T>
+struct CopyOverlappingRangeSupplement<TypeOperations, T, false> {
+ template <typename InT>
+ static void CopyOverlappingRange(const T* value_begin,
+ const T* value_end,
+ InT* storage_begin) {
+ const void* value_address = reinterpret_cast<const void*>(value_begin);
+ const void* storage_address = reinterpret_cast<const void*>(storage_begin);
+
+ if (value_address > storage_address) {
+ // Copy forward.
+ //
+ // SupplementedTypeOperations::CopyRange() cannot be used here, because
+ // user-supplied CopyRange() may not support overlapping cases.
+ //
+ // Our own implementation is safe, so we delegate the work to
+ // CopyRangeSupplement<TypeOperations, T, false>.
+ CopyRangeSupplement<TypeOperations, T, false>::CopyRange(
+ value_begin, value_end, storage_begin);
+ } else if (value_address < storage_address) {
+ // Copy backward.
+ if (value_begin == value_end)
+ return;
+ const T* value = value_end;
+ T* storage = storage_begin + (value_end - value_begin);
+ do {
+ --value;
+ --storage;
+ TypeOperations::Assign(*storage, *value);
+ } while (value != value_begin);
+ }
+ }
+};
+
+DEFINE_FUNCTION_DETECTOR(MoveOverlappingRange,
+ std::declval<T*>(),
+ std::declval<T*>(),
+ std::declval<T*>());
+
+template <typename TypeOperations,
+ typename T,
+ bool hasMoveOverlappingRange =
+ HasMoveOverlappingRangeFunction<TypeOperations, T>::value>
+struct MoveOverlappingRangeSupplement;
+
+template <typename TypeOperations, typename T>
+struct MoveOverlappingRangeSupplement<TypeOperations, T, true> {};
+
+template <typename TypeOperations, typename T>
+struct MoveOverlappingRangeSupplement<TypeOperations, T, false> {
+ template <typename InT>
+ static void MoveOverlappingRange(T* value_begin,
+ T* value_end,
+ InT* storage_begin) {
+ const void* value_address = reinterpret_cast<const void*>(value_begin);
+ const void* storage_address = reinterpret_cast<const void*>(storage_begin);
+
+ if (value_address > storage_address) {
+ // Move forward.
+ MoveRangeSupplement<TypeOperations, T, false>::MoveRange(
+ value_begin, value_end, storage_begin);
+ } else if (value_address < storage_address) {
+ // Move backward.
+ if (value_begin == value_end)
+ return;
+ const T* value = value_end;
+ T* storage = storage_begin + (value_end - value_begin);
+ do {
+ --value;
+ --storage;
+ TypeOperations::Assign(*storage, std::move(*value));
+ } while (value != value_begin);
+ }
+ }
+};
+
+DEFINE_FUNCTION_DETECTOR(UninitializedCopy,
+ std::declval<const T*>(),
+ std::declval<const T*>(),
+ std::declval<T*>());
+
+template <typename TypeOperations,
+ typename T,
+ bool hasUninitializedCopy =
+ HasUninitializedCopyFunction<TypeOperations, T>::value>
+struct UninitializedCopySupplement;
+
+template <typename TypeOperations, typename T>
+struct UninitializedCopySupplement<TypeOperations, T, true> {};
+
+template <typename TypeOperations, typename T>
+struct UninitializedCopySupplement<TypeOperations, T, false> {
+ template <typename InT>
+ static void UninitializedCopy(const InT* value_begin,
+ const InT* value_end,
+ T* uninitialized_begin) {
+ // To pick the correct version of DefaultInitialize().
+ struct SupplementedTypeOperations
+ : TypeOperations,
+ DefaultInitializeSupplement<TypeOperations, T> {};
+ T* uninitialized = uninitialized_begin;
+ for (const InT *value = value_begin; value != value_end;
+ ++value, ++uninitialized) {
+ SupplementedTypeOperations::DefaultInitialize(*uninitialized);
+ SupplementedTypeOperations::Assign(*uninitialized, *value);
+ }
+ }
+};
+
+DEFINE_FUNCTION_DETECTOR(UninitializedFill,
+ std::declval<const T&>(),
+ std::declval<T*>(),
+ std::declval<T*>());
+
+template <typename TypeOperations,
+ typename T,
+ bool hasUninitializedFill =
+ HasUninitializedFillFunction<TypeOperations, T>::value>
+struct UninitializedFillSupplement;
+
+template <typename TypeOperations, typename T>
+struct UninitializedFillSupplement<TypeOperations, T, true> {};
+
+template <typename TypeOperations, typename T>
+struct UninitializedFillSupplement<TypeOperations, T, false> {
+ template <typename InT>
+ static void UninitializedFill(const InT& value,
+ T* uninitialized_begin,
+ T* uninitialized_end) {
+ struct SupplementedTypeOperations
+ : TypeOperations,
+ DefaultInitializeSupplement<TypeOperations, T> {};
+ for (T* uninitialized = uninitialized_begin;
+ uninitialized != uninitialized_end; ++uninitialized) {
+ SupplementedTypeOperations::DefaultInitialize(*uninitialized);
+ SupplementedTypeOperations::Assign(*uninitialized, value);
+ }
+ }
+};
+
+DEFINE_FUNCTION_DETECTOR(Equal,
+ std::declval<const T&>(),
+ std::declval<const T&>());
+DEFINE_FUNCTION_DETECTOR(EqualRange,
+ std::declval<const T*>(),
+ std::declval<const T*>(),
+ std::declval<const T*>());
+
+template <typename TypeOperations,
+ typename T,
+ bool hasEqual = HasEqualFunction<TypeOperations, T>::value,
+ bool hasEqualRange = HasEqualRangeFunction<TypeOperations, T>::value>
+struct EqualSupplement;
+
+template <typename TypeOperations, typename T>
+struct EqualSupplement<TypeOperations, T, true, true> {};
+
+template <typename TypeOperations, typename T>
+struct EqualSupplement<TypeOperations, T, true, false> {
+ template <typename InT>
+ static bool EqualRange(const T* storage_begin,
+ const T* storage_end,
+ const InT* other_begin) {
+ const InT* other = other_begin;
+ for (const T *storage = storage_begin; storage != storage_end;
+ ++storage, ++other) {
+ if (!TypeOperations::Equal(*storage, *other))
+ return false;
+ }
+ return true;
+ }
+};
+
+template <typename TypeOperations, typename T>
+struct EqualSupplement<TypeOperations, T, false, true> {
+ template <typename InT>
+ static bool Equal(const T& stored_value, const InT& other_value) {
+ return TypeOperations::EqualRange(&stored_value, &stored_value + 1,
+ &other_value);
+ }
+};
+
+// EqualSupplement<TypeOperations, T, false, false> is invalid.
+
+#undef DEFINE_FUNCTION_DETECTOR
+
+} // namespace internal
+
+// Finally, CompleteContainerTypeOperations is defined by concerting all the
+// supplement classes. This can be applied to any ContainerTypeOperations
+// conforming to the requirements above.
+template <typename TypeOperations, typename T>
+struct CompleteContainerTypeOperations
+ : TypeOperations,
+ internal::DefaultInitializeSupplement<TypeOperations, T>,
+ internal::DestructSupplement<TypeOperations, T>,
+ internal::CopyRangeSupplement<TypeOperations, T>,
+ internal::MoveRangeSupplement<TypeOperations, T>,
+ internal::CopyOverlappingRangeSupplement<TypeOperations, T>,
+ internal::MoveOverlappingRangeSupplement<TypeOperations, T>,
+ internal::UninitializedCopySupplement<TypeOperations, T>,
+ internal::UninitializedFillSupplement<TypeOperations, T>,
+ internal::EqualSupplement<TypeOperations, T> {};
+
+//
+// CompletedContainerTypeOperations
+//
+
+// Complete*d*ContainerTypeOperations is ContainerTypeOperations<T> with
+// supplemented functions.
+template <typename T>
+struct CompletedContainerTypeOperations
+ : CompleteContainerTypeOperations<ContainerTypeOperations<T>, T> {};
+
+//
+// GenericContainerTypeOperations<T> definitions
+//
+
+template <typename T>
+void GenericContainerTypeOperations<T>::DefaultInitialize(T& uninitialized) {
+ new (&uninitialized) T;
+}
+
+template <typename T>
+void GenericContainerTypeOperations<T>::Destruct(T& storage) {
+ storage.~T();
+}
+
+template <typename T>
+template <typename InT>
+void GenericContainerTypeOperations<T>::Assign(T& storage, InT&& value) {
+ storage = std::forward<InT>(value);
+}
+
+template <typename T>
+template <typename InT>
+void GenericContainerTypeOperations<T>::UninitializedCopy(
+ const InT* value_begin,
+ const InT* value_end,
+ T* uninitialized_begin) {
+ const InT* value = value_begin;
+ T* uninitialized = uninitialized_begin;
+ for (; value != value_end; ++value, ++uninitialized)
+ new (uninitialized) T(*value);
+}
+
+template <typename T>
+template <typename InT>
+void GenericContainerTypeOperations<T>::UninitializedFill(
+ const InT& value,
+ T* uninitialized_begin,
+ T* uninitialized_end) {
+ for (T* uninitialized = uninitialized_begin;
+ uninitialized != uninitialized_end; ++uninitialized) {
+ new (uninitialized) T(value);
+ }
+}
+
+template <typename T>
+template <typename InT>
+bool GenericContainerTypeOperations<T>::Equal(const T& stored_value,
+ const InT& other_value) {
+ return stored_value == other_value;
+}
+
+} // namespace experimental
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_EXPERIMENTAL_CONTAINER_TYPE_OPERATIONS_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/experimental/container_type_operations_test.cc b/chromium/third_party/blink/renderer/platform/wtf/experimental/container_type_operations_test.cc
new file mode 100644
index 00000000000..dfd75956b99
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/experimental/container_type_operations_test.cc
@@ -0,0 +1,83 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/wtf/experimental/container_type_operations.h"
+
+#include <cstring>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace WTF {
+namespace experimental {
+
+namespace {
+
+struct Pod {
+ int a;
+ int* b;
+ char c[7];
+};
+
+bool operator==(const Pod& left, const Pod& right) {
+ return left.a == right.a && left.b == right.b &&
+ memcmp(left.c, right.c, sizeof(left.c)) == 0;
+}
+
+} // namespace
+
+template <typename T>
+class ContainerTypeOperationsTest : public testing::Test {};
+
+TYPED_TEST_CASE_P(ContainerTypeOperationsTest);
+
+TYPED_TEST_P(ContainerTypeOperationsTest, Completeness) {
+ using Ops = CompletedContainerTypeOperations<TypeParam>;
+ // Call all funcions in Ops and make sure they are callable.
+
+ constexpr size_t SIZE = 4;
+ // We need to initialize the content of |data| to let the compiler think
+ // accessing this memory is safe. If the compiler detects undefined behavior,
+ // it may "optimize" the code in an unexpected way and make the assertions
+ // fail.
+ alignas(TypeParam) char data[sizeof(TypeParam) * SIZE] = {0};
+ TypeParam* storage = reinterpret_cast<TypeParam*>(data);
+
+ Ops::DefaultInitialize(*storage);
+ Ops::Destruct(*storage);
+
+ Ops::DefaultInitializeRange(storage, storage + SIZE);
+ Ops::DestructRange(storage, storage + SIZE);
+
+ Ops::DefaultInitializeRange(storage, storage + SIZE);
+ // We also need to zero-initialize this variable for the same reason as above.
+ TypeParam lvalue{};
+ Ops::Assign(*storage, lvalue);
+ Ops::Assign(*storage, TypeParam()); // rvalue.
+ Ops::CopyRange(storage, storage + 2, storage + 2);
+ Ops::MoveRange(storage, storage + 2, storage + 2);
+ Ops::CopyOverlappingRange(storage, storage + 3, storage + 1);
+ Ops::MoveOverlappingRange(storage, storage + 3, storage + 1);
+
+ Ops::DestructRange(storage, storage + 2);
+ Ops::UninitializedCopy(storage + 2, storage + 4, storage);
+ Ops::DestructRange(storage, storage + 2);
+ Ops::UninitializedFill(lvalue, storage, storage + 2);
+
+ // The first element may be moved. Let's make sure it is in some valid state.
+ Ops::Assign(*storage, lvalue);
+
+ EXPECT_TRUE(Ops::Equal(*storage, *storage));
+ EXPECT_TRUE(Ops::EqualRange(storage, storage + SIZE, storage));
+
+ Ops::DestructRange(storage, storage + SIZE);
+}
+
+REGISTER_TYPED_TEST_CASE_P(ContainerTypeOperationsTest, Completeness);
+
+using PodTestTypes = testing::Types<int, char, int*, Pod>;
+
+INSTANTIATE_TYPED_TEST_CASE_P(Pod, ContainerTypeOperationsTest, PodTestTypes);
+
+} // namespace experimental
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/forward.h b/chromium/third_party/blink/renderer/platform/wtf/forward.h
new file mode 100644
index 00000000000..70aaf3091be
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/forward.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2006, 2009, 2011 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_FORWARD_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_FORWARD_H_
+
+#include <stddef.h>
+#include "third_party/blink/renderer/platform/wtf/compiler.h"
+
+template <typename T>
+class scoped_refptr;
+
+namespace WTF {
+
+template <typename T>
+class StringBuffer;
+class PartitionAllocator;
+template <typename T,
+ size_t inlineCapacity = 0,
+ typename Allocator = PartitionAllocator>
+class Vector;
+
+class ArrayBuffer;
+class ArrayBufferView;
+class ArrayPiece;
+class AtomicString;
+class BigInt64Array;
+class BigUint64Array;
+class CString;
+class Float32Array;
+class Float64Array;
+class Int8Array;
+class Int16Array;
+class Int32Array;
+class OrdinalNumber;
+class String;
+class StringBuilder;
+class StringImpl;
+class StringView;
+class Uint8Array;
+class Uint8ClampedArray;
+class Uint16Array;
+class Uint32Array;
+
+} // namespace WTF
+
+using WTF::Vector;
+
+using WTF::ArrayBuffer;
+using WTF::ArrayBufferView;
+using WTF::ArrayPiece;
+using WTF::AtomicString;
+using WTF::BigInt64Array;
+using WTF::BigUint64Array;
+using WTF::CString;
+using WTF::Float32Array;
+using WTF::Float64Array;
+using WTF::Int8Array;
+using WTF::Int16Array;
+using WTF::Int32Array;
+using WTF::String;
+using WTF::StringBuffer;
+using WTF::StringBuilder;
+using WTF::StringImpl;
+using WTF::StringView;
+using WTF::Uint8Array;
+using WTF::Uint8ClampedArray;
+using WTF::Uint16Array;
+using WTF::Uint32Array;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_FORWARD_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/functional.h b/chromium/third_party/blink/renderer/platform/wtf/functional.h
new file mode 100644
index 00000000000..83dc4141696
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/functional.h
@@ -0,0 +1,423 @@
+/*
+ * Copyright (C) 2011 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_FUNCTIONAL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_FUNCTIONAL_H_
+
+#include <utility>
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/threading/thread_checker.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/type_traits.h"
+
+namespace blink {
+template <typename T>
+class Member;
+template <typename T>
+class WeakMember;
+}
+
+namespace WTF {
+
+// Functional.h provides a very simple way to bind a function pointer and
+// arguments together into a function object that can be stored, copied and
+// invoked, similar to boost::bind and std::bind in C++11.
+
+// To create a same-thread callback, use WTF::Bind() or WTF::BindRepeating().
+// Use the former to create a callback that's called only once, and use the
+// latter for a callback that may be called multiple times.
+//
+// WTF::Bind() and WTF::BindRepeating() returns base::OnceCallback and
+// base::RepeatingCallback respectively. See //docs/callback.md for how to use
+// those types.
+
+// Thread Safety:
+//
+// WTF::Bind(), WTF::BindRepeating and base::{Once,Repeating}Callback should
+// be used for same-thread closures only, i.e. the closures must be created,
+// executed and destructed on the same thread.
+// Use crossThreadBind() and CrossThreadClosure if the function/task is called
+// or destructed on a (potentially) different thread from the current thread.
+//
+// Currently, WTF::CrossThreadClosure does not distinguish Once and Repeating
+// usage.
+
+// WTF::Bind() / WTF::BindRepeating() and move semantics
+// =====================================================
+//
+// For unbound parameters, there are two ways to pass movable arguments:
+//
+// 1) Pass by rvalue reference.
+//
+// void YourFunction(Argument&& argument) { ... }
+// base::OnceCallback<void(Argument&&)> functor =
+// Bind(&YourFunction);
+//
+// 2) Pass by value.
+//
+// void YourFunction(Argument argument) { ... }
+// base::OnceCallback<void(Argument)> functor = Bind(&YourFunction);
+//
+// Note that with the latter there will be *two* move constructions happening,
+// because there needs to be at least one intermediary function call taking an
+// argument of type "Argument" (i.e. passed by value). The former case does not
+// require any move constructions inbetween.
+//
+// For bound parameters (arguments supplied on the creation of a functor), you
+// can move your argument into the internal storage of the functor by supplying
+// an rvalue to that argument (this is done in wrap() of ParamStorageTraits).
+// However, to make the functor be able to get called multiple times, the
+// stored object does not get moved out automatically when the underlying
+// function is actually invoked. If you want to make an argument "auto-passed",
+// you can do so by wrapping your bound argument with WTF::Passed() function, as
+// shown below:
+//
+// void YourFunction(Argument argument)
+// {
+// // |argument| is passed from the internal storage of functor.
+// ...
+// }
+//
+// ...
+// base::OnceClosure functor = Bind(&YourFunction, WTF::Passed(Argument()));
+// ...
+// std::move(functor).Run();
+//
+// The underlying function must receive the argument wrapped by WTF::Passed() by
+// rvalue reference or by value.
+//
+// Obviously, if you create a functor this way, you shouldn't call the functor
+// twice or more; after the second call, the passed argument may be invalid.
+
+template <typename T>
+class PassedWrapper final {
+ public:
+ explicit PassedWrapper(T&& scoper) : scoper_(std::move(scoper)) {}
+ PassedWrapper(PassedWrapper&& other) : scoper_(std::move(other.scoper_)) {}
+ T MoveOut() const { return std::move(scoper_); }
+
+ private:
+ mutable T scoper_;
+};
+
+template <typename T>
+PassedWrapper<T> Passed(T&& value) {
+ static_assert(
+ !std::is_reference<T>::value,
+ "You must pass an rvalue to WTF::passed() so it can be moved. Add "
+ "std::move() if necessary.");
+ static_assert(!std::is_const<T>::value,
+ "|value| must not be const so it can be moved.");
+ return PassedWrapper<T>(std::move(value));
+}
+
+template <typename T>
+class RetainedRefWrapper final {
+ public:
+ explicit RetainedRefWrapper(T* ptr) : ptr_(ptr) {}
+ explicit RetainedRefWrapper(scoped_refptr<T> ptr) : ptr_(std::move(ptr)) {}
+ T* get() const { return ptr_.get(); }
+
+ private:
+ scoped_refptr<T> ptr_;
+};
+
+template <typename T>
+RetainedRefWrapper<T> RetainedRef(T* ptr) {
+ return RetainedRefWrapper<T>(ptr);
+}
+
+template <typename T>
+RetainedRefWrapper<T> RetainedRef(scoped_refptr<T> ptr) {
+ return RetainedRefWrapper<T>(std::move(ptr));
+}
+
+template <typename T>
+class UnretainedWrapper final {
+ public:
+ explicit UnretainedWrapper(T* ptr) : ptr_(ptr) {}
+ T* Value() const { return ptr_; }
+
+ private:
+ T* ptr_;
+};
+
+template <typename T>
+class CrossThreadUnretainedWrapper final {
+ public:
+ explicit CrossThreadUnretainedWrapper(T* ptr) : ptr_(ptr) {}
+ T* Value() const { return ptr_; }
+
+ private:
+ T* ptr_;
+};
+
+template <typename T>
+UnretainedWrapper<T> Unretained(T* value) {
+ static_assert(!WTF::IsGarbageCollectedType<T>::value,
+ "WTF::Unretained() + GCed type is forbidden");
+ return UnretainedWrapper<T>(value);
+}
+
+template <typename T>
+CrossThreadUnretainedWrapper<T> CrossThreadUnretained(T* value) {
+ static_assert(!WTF::IsGarbageCollectedType<T>::value,
+ "CrossThreadUnretained() + GCed type is forbidden");
+ return CrossThreadUnretainedWrapper<T>(value);
+}
+
+namespace internal {
+
+template <size_t, typename T>
+struct CheckGCedTypeRestriction {
+ static_assert(!std::is_pointer<T>::value,
+ "Raw pointers are not allowed to bind into WTF::Function. Wrap "
+ "it with either WrapPersistent, WrapWeakPersistent, "
+ "WrapCrossThreadPersistent, WrapCrossThreadWeakPersistent, "
+ "RefPtr or unretained.");
+ static_assert(!IsSubclassOfTemplate<T, blink::Member>::value &&
+ !IsSubclassOfTemplate<T, blink::WeakMember>::value,
+ "Member and WeakMember are not allowed to bind into "
+ "WTF::Function. Wrap it with either WrapPersistent, "
+ "WrapWeakPersistent, WrapCrossThreadPersistent or "
+ "WrapCrossThreadWeakPersistent.");
+ static_assert(!WTF::IsGarbageCollectedType<T>::value,
+ "GCed type is forbidden as a bound parameters.");
+};
+
+template <typename Index, typename... Args>
+struct CheckGCedTypeRestrictions;
+
+template <size_t... Ns, typename... Args>
+struct CheckGCedTypeRestrictions<std::index_sequence<Ns...>, Args...>
+ : CheckGCedTypeRestriction<Ns, Args>... {
+ static constexpr bool ok = true;
+};
+
+} // namespace internal
+
+#if DCHECK_IS_ON()
+
+template <typename CallbackType,
+ typename RunType = typename CallbackType::RunType>
+class ThreadCheckingCallbackWrapper;
+
+// This class wraps a callback and applies thread checking on its construction,
+// destruction and invocation (on Run()).
+template <typename CallbackType, typename R, typename... Args>
+class ThreadCheckingCallbackWrapper<CallbackType, R(Args...)> {
+ public:
+ explicit ThreadCheckingCallbackWrapper(CallbackType callback)
+ : callback_(std::move(callback)) {}
+
+ ~ThreadCheckingCallbackWrapper() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ }
+
+ R Run(Args... args) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ return RunInternal(&callback_, std::forward<Args>(args)...);
+ }
+
+ bool IsCancelled() const { return callback_.IsCancelled(); }
+
+ private:
+ static R RunInternal(base::RepeatingCallback<R(Args...)>* callback,
+ Args&&... args) {
+ return callback->Run(std::forward<Args>(args)...);
+ }
+
+ static R RunInternal(base::OnceCallback<R(Args...)>* callback,
+ Args&&... args) {
+ return std::move(*callback).Run(std::forward<Args>(args)...);
+ }
+
+ SEQUENCE_CHECKER(sequence_checker_);
+ CallbackType callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadCheckingCallbackWrapper);
+};
+
+} // namespace WTF
+
+namespace base {
+
+template <typename CallbackType,
+ typename R,
+ typename... Args,
+ typename... BoundArgs>
+struct CallbackCancellationTraits<
+ R (WTF::ThreadCheckingCallbackWrapper<CallbackType>::*)(Args...),
+ std::tuple<
+ std::unique_ptr<WTF::ThreadCheckingCallbackWrapper<CallbackType>>,
+ BoundArgs...>> {
+ static constexpr bool is_cancellable = true;
+
+ template <typename Functor, typename Receiver, typename... RunArgs>
+ static bool IsCancelled(const Functor&,
+ const Receiver& receiver,
+ const RunArgs&...) {
+ return receiver->IsCancelled();
+ }
+};
+
+} // namespace base
+
+namespace WTF {
+
+#endif
+
+template <typename Signature>
+class CrossThreadFunction;
+
+template <typename R, typename... Args>
+class CrossThreadFunction<R(Args...)> {
+ USING_FAST_MALLOC(CrossThreadFunction);
+
+ public:
+ CrossThreadFunction() = default;
+ explicit CrossThreadFunction(base::Callback<R(Args...)> callback)
+ : callback_(std::move(callback)) {}
+ ~CrossThreadFunction() = default;
+
+ CrossThreadFunction(const CrossThreadFunction&) = delete;
+ CrossThreadFunction& operator=(const CrossThreadFunction&) = delete;
+
+ CrossThreadFunction(CrossThreadFunction&& other) = default;
+ CrossThreadFunction& operator=(CrossThreadFunction&& other) = default;
+
+ R Run(Args... args) const & {
+ return callback_.Run(std::forward<Args>(args)...);
+ }
+
+ R Run(Args... args) && {
+ return std::move(callback_).Run(std::forward<Args>(args)...);
+ }
+
+ bool IsCancelled() const { return callback_.IsCancelled(); }
+ void Reset() { callback_.Reset(); }
+ explicit operator bool() const { return static_cast<bool>(callback_); }
+
+ friend base::Callback<R(Args...)> ConvertToBaseCallback(
+ CrossThreadFunction function) {
+ return std::move(function.callback_);
+ }
+
+ private:
+ base::Callback<R(Args...)> callback_;
+};
+
+// Note: now there is WTF::Bind()and WTF::BindRepeating(). See the comment block
+// above for the correct usage of those.
+template <typename FunctionType, typename... BoundParameters>
+base::OnceCallback<base::MakeUnboundRunType<FunctionType, BoundParameters...>>
+Bind(FunctionType function, BoundParameters&&... bound_parameters) {
+ static_assert(internal::CheckGCedTypeRestrictions<
+ std::index_sequence_for<BoundParameters...>,
+ std::decay_t<BoundParameters>...>::ok,
+ "A bound argument uses a bad pattern.");
+ auto cb = base::BindOnce(function,
+ std::forward<BoundParameters>(bound_parameters)...);
+#if DCHECK_IS_ON()
+ using UnboundRunType =
+ base::MakeUnboundRunType<FunctionType, BoundParameters...>;
+ using WrapperType =
+ ThreadCheckingCallbackWrapper<base::OnceCallback<UnboundRunType>>;
+ cb = base::BindOnce(&WrapperType::Run,
+ std::make_unique<WrapperType>(std::move(cb)));
+#endif
+ return cb;
+}
+
+template <typename FunctionType, typename... BoundParameters>
+base::RepeatingCallback<
+ base::MakeUnboundRunType<FunctionType, BoundParameters...>>
+BindRepeating(FunctionType function, BoundParameters&&... bound_parameters) {
+ static_assert(internal::CheckGCedTypeRestrictions<
+ std::index_sequence_for<BoundParameters...>,
+ std::decay_t<BoundParameters>...>::ok,
+ "A bound argument uses a bad pattern.");
+ auto cb = base::BindRepeating(
+ function, std::forward<BoundParameters>(bound_parameters)...);
+#if DCHECK_IS_ON()
+ using UnboundRunType =
+ base::MakeUnboundRunType<FunctionType, BoundParameters...>;
+ using WrapperType =
+ ThreadCheckingCallbackWrapper<base::RepeatingCallback<UnboundRunType>>;
+ cb = base::BindRepeating(&WrapperType::Run,
+ std::make_unique<WrapperType>(std::move(cb)));
+#endif
+ return cb;
+}
+
+template <typename T>
+using CrossThreadRepeatingFunction = CrossThreadFunction<T>;
+using CrossThreadRepeatingClosure = CrossThreadFunction<void()>;
+using CrossThreadClosure = CrossThreadFunction<void()>;
+
+} // namespace WTF
+
+namespace base {
+
+template <typename T>
+struct BindUnwrapTraits<WTF::RetainedRefWrapper<T>> {
+ static T* Unwrap(const WTF::RetainedRefWrapper<T>& wrapped) {
+ return wrapped.get();
+ }
+};
+
+template <typename T>
+struct BindUnwrapTraits<WTF::PassedWrapper<T>> {
+ static T Unwrap(const WTF::PassedWrapper<T>& wrapped) {
+ return wrapped.MoveOut();
+ }
+};
+
+template <typename T>
+struct BindUnwrapTraits<WTF::UnretainedWrapper<T>> {
+ static T* Unwrap(const WTF::UnretainedWrapper<T>& wrapped) {
+ return wrapped.Value();
+ }
+};
+
+template <typename T>
+struct BindUnwrapTraits<WTF::CrossThreadUnretainedWrapper<T>> {
+ static T* Unwrap(const WTF::CrossThreadUnretainedWrapper<T>& wrapped) {
+ return wrapped.Value();
+ }
+};
+
+} // namespace base
+
+using WTF::CrossThreadUnretained;
+
+using WTF::CrossThreadFunction;
+using WTF::CrossThreadClosure;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_FUNCTIONAL_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/functional_test.cc b/chromium/third_party/blink/renderer/platform/wtf/functional_test.cc
new file mode 100644
index 00000000000..8f792f90f8f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/functional_test.cc
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2011 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/wtf/functional.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/memory/weak_ptr.h"
+#include "base/test/gtest_util.h"
+#include "base/threading/thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/leak_annotations.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_test_helper.h"
+
+namespace WTF {
+
+class UnwrappedClass {
+ public:
+ explicit UnwrappedClass(int value) : value_(value) {}
+
+ int Value() const { return value_; }
+
+ private:
+ int value_;
+};
+
+class HasWeakPtrSupport {
+ public:
+ HasWeakPtrSupport() : weak_ptr_factory_(this) {}
+
+ base::WeakPtr<HasWeakPtrSupport> GetWeakPtr() {
+ return weak_ptr_factory_.GetWeakPtr();
+ }
+
+ void RevokeAll() { weak_ptr_factory_.InvalidateWeakPtrs(); }
+
+ void Increment(int* counter) { ++*counter; }
+
+ private:
+ base::WeakPtrFactory<HasWeakPtrSupport> weak_ptr_factory_;
+};
+
+} // namespace WTF
+
+namespace WTF {
+namespace {
+
+class A {
+ public:
+ explicit A(int i) : i_(i) {}
+ virtual ~A() = default;
+
+ int F() { return i_; }
+ int AddF(int j) { return i_ + j; }
+ virtual int Overridden() { return 42; }
+
+ private:
+ int i_;
+};
+
+class B : public A {
+ public:
+ explicit B(int i) : A(i) {}
+
+ int F() { return A::F() + 1; }
+ int AddF(int j) { return A::AddF(j) + 1; }
+ int Overridden() override { return 43; }
+};
+
+class Number {
+ public:
+ REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE();
+ static scoped_refptr<Number> Create(int value) {
+ return base::AdoptRef(new Number(value));
+ }
+
+ ~Number() { value_ = 0; }
+
+ int Value() const { return value_; }
+
+ int RefCount() const { return ref_count_; }
+ bool HasOneRef() const { return ref_count_ == 1; }
+
+ // For RefPtr support.
+ void Adopted() const {}
+ void AddRef() const { ++ref_count_; }
+ void Release() const {
+ if (--ref_count_ == 0)
+ delete this;
+ }
+
+ private:
+ explicit Number(int value) : value_(value) {}
+
+ int value_;
+ mutable int ref_count_ = 1;
+};
+
+class CountGeneration {
+ public:
+ CountGeneration() : copies_(0) {}
+ CountGeneration(const CountGeneration& other) : copies_(other.copies_ + 1) {}
+
+ int Copies() const { return copies_; }
+
+ private:
+ // Copy/move-assignment is not needed in the test.
+ CountGeneration& operator=(const CountGeneration&) = delete;
+ CountGeneration& operator=(CountGeneration&&) = delete;
+
+ int copies_;
+};
+
+TEST(FunctionalTest, WeakPtr) {
+ HasWeakPtrSupport obj;
+ int counter = 0;
+ base::RepeatingClosure bound =
+ WTF::BindRepeating(&HasWeakPtrSupport::Increment, obj.GetWeakPtr(),
+ WTF::Unretained(&counter));
+
+ bound.Run();
+ EXPECT_FALSE(bound.IsCancelled());
+ EXPECT_EQ(1, counter);
+
+ obj.RevokeAll();
+ EXPECT_TRUE(bound.IsCancelled());
+ bound.Run();
+ EXPECT_EQ(1, counter);
+}
+
+void MakeClosure(base::OnceClosure** closure_out) {
+ *closure_out = new base::OnceClosure(WTF::Bind([] {}));
+ LEAK_SANITIZER_IGNORE_OBJECT(*closure_out);
+}
+
+TEST(FunctionalTest, ThreadRestriction) {
+ base::OnceClosure* closure = nullptr;
+
+ base::Thread thread("testing");
+ thread.Start();
+ thread.task_runner()->PostTask(FROM_HERE,
+ base::BindOnce(&MakeClosure, &closure));
+ thread.Stop();
+
+ ASSERT_TRUE(closure);
+ EXPECT_DCHECK_DEATH(std::move(*closure).Run());
+ EXPECT_DCHECK_DEATH(delete closure);
+}
+
+} // anonymous namespace
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/get_ptr.h b/chromium/third_party/blink/renderer/platform/wtf/get_ptr.h
new file mode 100644
index 00000000000..751e0dc9b0d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/get_ptr.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_GET_PTR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_GET_PTR_H_
+
+template <typename>
+class scoped_refptr;
+
+namespace WTF {
+
+template <typename T>
+inline T* GetPtr(T* p) {
+ return p;
+}
+
+template <typename T>
+inline T* GetPtr(T& p) {
+ return &p;
+}
+
+template <typename T>
+inline T* GetPtr(const scoped_refptr<T>& p) {
+ return p.get();
+}
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_GET_PTR_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/hash_counted_set.h b/chromium/third_party/blink/renderer/platform/wtf/hash_counted_set.h
new file mode 100644
index 00000000000..25f781809e2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/hash_counted_set.h
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2005, 2006, 2008 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_HASH_COUNTED_SET_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_HASH_COUNTED_SET_H_
+
+#include "base/macros.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/partition_allocator.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace WTF {
+
+// An unordered hash set that keeps track of how many times you added an item to
+// the set. The iterators have fields ->key and ->value that return the set
+// members and their counts, respectively.
+template <typename Value,
+ typename HashFunctions = typename DefaultHash<Value>::Hash,
+ typename Traits = HashTraits<Value>,
+ typename Allocator = PartitionAllocator>
+class HashCountedSet {
+ USE_ALLOCATOR(HashCountedSet, Allocator);
+
+ private:
+ typedef HashMap<Value,
+ unsigned,
+ HashFunctions,
+ Traits,
+ HashTraits<unsigned>,
+ Allocator>
+ ImplType;
+
+ public:
+ typedef Value ValueType;
+ using value_type = ValueType;
+ typedef typename ImplType::iterator iterator;
+ typedef typename ImplType::const_iterator const_iterator;
+ typedef typename ImplType::AddResult AddResult;
+
+ HashCountedSet() {
+ static_assert(Allocator::kIsGarbageCollected ||
+ !IsPointerToGarbageCollectedType<Value>::value,
+ "Cannot put raw pointers to garbage-collected classes into "
+ "an off-heap HashCountedSet. Use "
+ "HeapHashCountedSet<Member<T>> instead.");
+ }
+
+ void swap(HashCountedSet& other) { impl_.swap(other.impl_); }
+
+ unsigned size() const { return impl_.size(); }
+ unsigned Capacity() const { return impl_.capacity(); }
+ bool IsEmpty() const { return impl_.IsEmpty(); }
+
+ // Iterators iterate over pairs of values (called key) and counts (called
+ // value).
+ iterator begin() { return impl_.begin(); }
+ iterator end() { return impl_.end(); }
+ const_iterator begin() const { return impl_.begin(); }
+ const_iterator end() const { return impl_.end(); }
+
+ iterator find(const ValueType& value) { return impl_.find(value); }
+ const_iterator find(const ValueType& value) const {
+ return impl_.find(value);
+ }
+ bool Contains(const ValueType& value) const { return impl_.Contains(value); }
+ unsigned count(const ValueType& value) const { return impl_.at(value); }
+
+ // Increases the count if an equal value is already present the return value
+ // is a pair of an iterator to the new value's location, and a bool that is
+ // true if an new entry was added.
+ AddResult insert(const ValueType&);
+
+ // Generalized add(), adding the value N times.
+ AddResult insert(const ValueType&, unsigned);
+
+ // Reduces the count of the value, and removes it if count goes down to
+ // zero, returns true if the value is removed.
+ bool erase(const ValueType& value) { return erase(find(value)); }
+ bool erase(iterator);
+
+ // Removes the value, regardless of its count.
+ void RemoveAll(const ValueType& value) { RemoveAll(find(value)); }
+ void RemoveAll(iterator);
+
+ // Clears the whole set.
+ void clear() { impl_.clear(); }
+
+ Vector<Value> AsVector() const;
+
+ template <typename VisitorDispatcher, typename A = Allocator>
+ std::enable_if_t<A::kIsGarbageCollected> Trace(VisitorDispatcher visitor) {
+ impl_.Trace(visitor);
+ }
+
+ private:
+ ImplType impl_;
+
+ DISALLOW_COPY_AND_ASSIGN(HashCountedSet);
+};
+
+template <typename T, typename U, typename V, typename W>
+inline typename HashCountedSet<T, U, V, W>::AddResult
+HashCountedSet<T, U, V, W>::insert(const ValueType& value, unsigned count) {
+ DCHECK_GT(count, 0u);
+ AddResult result = impl_.insert(value, 0);
+ result.stored_value->value += count;
+ return result;
+}
+
+template <typename T, typename U, typename V, typename W>
+inline typename HashCountedSet<T, U, V, W>::AddResult
+HashCountedSet<T, U, V, W>::insert(const ValueType& value) {
+ return insert(value, 1u);
+}
+
+template <typename T, typename U, typename V, typename W>
+inline bool HashCountedSet<T, U, V, W>::erase(iterator it) {
+ if (it == end())
+ return false;
+
+ unsigned old_val = it->value;
+ DCHECK(old_val);
+ unsigned new_val = old_val - 1;
+ if (new_val) {
+ it->value = new_val;
+ return false;
+ }
+
+ impl_.erase(it);
+ return true;
+}
+
+template <typename T, typename U, typename V, typename W>
+inline void HashCountedSet<T, U, V, W>::RemoveAll(iterator it) {
+ if (it == end())
+ return;
+
+ impl_.erase(it);
+}
+
+template <typename Value,
+ typename HashFunctions,
+ typename Traits,
+ typename Allocator,
+ typename VectorType>
+inline void CopyToVector(
+ const HashCountedSet<Value, HashFunctions, Traits, Allocator>& collection,
+ VectorType& vector) {
+ {
+ // Disallow GC across resize allocation, see crbug.com/568173
+ typename VectorType::GCForbiddenScope scope;
+ vector.resize(collection.size());
+ }
+
+ auto it = collection.begin();
+ auto end = collection.end();
+ for (unsigned i = 0; it != end; ++it, ++i)
+ vector[i] = (*it).key;
+}
+
+template <typename T, typename U, typename V, typename W>
+inline Vector<T> HashCountedSet<T, U, V, W>::AsVector() const {
+ Vector<T> vector;
+ CopyToVector(*this, vector);
+ return vector;
+}
+
+} // namespace WTF
+
+using WTF::HashCountedSet;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_HASH_COUNTED_SET_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/hash_functions.h b/chromium/third_party/blink/renderer/platform/wtf/hash_functions.h
new file mode 100644
index 00000000000..5a8fb2cef78
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/hash_functions.h
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2005, 2006, 2008 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_HASH_FUNCTIONS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_HASH_FUNCTIONS_H_
+
+#include <stdint.h>
+#include <memory>
+#include <type_traits>
+#include "base/bit_cast.h"
+#include "base/memory/scoped_refptr.h"
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+
+namespace WTF {
+
+template <size_t size>
+struct IntTypes;
+template <>
+struct IntTypes<1> {
+ typedef int8_t SignedType;
+ typedef uint8_t UnsignedType;
+};
+template <>
+struct IntTypes<2> {
+ typedef int16_t SignedType;
+ typedef uint16_t UnsignedType;
+};
+template <>
+struct IntTypes<4> {
+ typedef int32_t SignedType;
+ typedef uint32_t UnsignedType;
+};
+template <>
+struct IntTypes<8> {
+ typedef int64_t SignedType;
+ typedef uint64_t UnsignedType;
+};
+
+// integer hash function
+
+// Thomas Wang's 32 Bit Mix Function:
+// http://www.cris.com/~Ttwang/tech/inthash.htm
+inline unsigned HashInt(uint32_t key) {
+ key += ~(key << 15);
+ key ^= (key >> 10);
+ key += (key << 3);
+ key ^= (key >> 6);
+ key += ~(key << 11);
+ key ^= (key >> 16);
+ return key;
+}
+
+inline unsigned HashInt(uint16_t key16) {
+ uint32_t key = key16;
+ return HashInt(key);
+}
+
+inline unsigned HashInt(uint8_t key8) {
+ uint32_t key = key8;
+ return HashInt(key);
+}
+
+// Thomas Wang's 64 bit Mix Function:
+// http://www.cris.com/~Ttwang/tech/inthash.htm
+inline unsigned HashInt(uint64_t key) {
+ key += ~(key << 32);
+ key ^= (key >> 22);
+ key += ~(key << 13);
+ key ^= (key >> 8);
+ key += (key << 3);
+ key ^= (key >> 15);
+ key += ~(key << 27);
+ key ^= (key >> 31);
+ return static_cast<unsigned>(key);
+}
+
+// Compound integer hash method:
+// http://opendatastructures.org/versions/edition-0.1d/ods-java/node33.html#SECTION00832000000000000000
+inline unsigned HashInts(unsigned key1, unsigned key2) {
+ unsigned short_random1 = 277951225; // A random 32-bit value.
+ unsigned short_random2 = 95187966; // A random 32-bit value.
+ uint64_t long_random = 19248658165952623LL; // A random, odd 64-bit value.
+
+ uint64_t product =
+ long_random * short_random1 * key1 + long_random * short_random2 * key2;
+ unsigned high_bits = static_cast<unsigned>(
+ product >> (8 * (sizeof(uint64_t) - sizeof(unsigned))));
+ return high_bits;
+}
+
+template <typename T>
+struct IntHash {
+ static unsigned GetHash(T key) {
+ return HashInt(
+ static_cast<typename IntTypes<sizeof(T)>::UnsignedType>(key));
+ }
+ static bool Equal(T a, T b) { return a == b; }
+ static const bool safe_to_compare_to_empty_or_deleted = true;
+};
+
+template <typename T>
+struct FloatHash {
+ typedef typename IntTypes<sizeof(T)>::UnsignedType Bits;
+ static unsigned GetHash(T key) { return HashInt(bit_cast<Bits>(key)); }
+ static bool Equal(T a, T b) { return bit_cast<Bits>(a) == bit_cast<Bits>(b); }
+ static const bool safe_to_compare_to_empty_or_deleted = true;
+};
+
+// pointer identity hash function
+
+template <typename T>
+struct PtrHash {
+ static unsigned GetHash(T* key) {
+#if defined(COMPILER_MSVC)
+#pragma warning(push)
+// work around what seems to be a bug in MSVC's conversion warnings
+#pragma warning(disable : 4244)
+#endif
+ return IntHash<uintptr_t>::GetHash(reinterpret_cast<uintptr_t>(key));
+#if defined(COMPILER_MSVC)
+#pragma warning(pop)
+#endif
+ }
+ static bool Equal(T* a, T* b) { return a == b; }
+ static bool Equal(std::nullptr_t, T* b) { return !b; }
+ static bool Equal(T* a, std::nullptr_t) { return !a; }
+ static const bool safe_to_compare_to_empty_or_deleted = true;
+};
+
+template <typename T>
+struct RefPtrHash : PtrHash<T> {
+ using PtrHash<T>::GetHash;
+ static unsigned GetHash(const scoped_refptr<T>& key) {
+ return GetHash(key.get());
+ }
+ using PtrHash<T>::Equal;
+ static bool Equal(const scoped_refptr<T>& a, const scoped_refptr<T>& b) {
+ return a == b;
+ }
+ static bool Equal(T* a, const scoped_refptr<T>& b) { return a == b; }
+ static bool Equal(const scoped_refptr<T>& a, T* b) { return a == b; }
+};
+
+template <typename T>
+struct UniquePtrHash : PtrHash<T> {
+ using PtrHash<T>::GetHash;
+ static unsigned GetHash(const std::unique_ptr<T>& key) {
+ return GetHash(key.get());
+ }
+ static bool Equal(const std::unique_ptr<T>& a, const std::unique_ptr<T>& b) {
+ return a == b;
+ }
+ static bool Equal(const std::unique_ptr<T>& a, const T* b) {
+ return a.get() == b;
+ }
+ static bool Equal(const T* a, const std::unique_ptr<T>& b) {
+ return a == b.get();
+ }
+};
+
+// Useful compounding hash functions.
+inline void AddIntToHash(unsigned& hash, unsigned key) {
+ hash = ((hash << 5) + hash) + key; // Djb2
+};
+
+inline void AddFloatToHash(unsigned& hash, float value) {
+ AddIntToHash(hash, FloatHash<float>::GetHash(value));
+};
+
+// Default hash function for each type.
+template <typename T>
+struct DefaultHash;
+
+// Actual implementation of DefaultHash.
+//
+// The case of |isIntegral| == false is not implemented. If you see a compile
+// error saying DefaultHashImpl<T, false> is not defined, that's because the
+// default hash functions for T are not defined. You need to implement them
+// yourself.
+template <typename T, bool isIntegral>
+struct DefaultHashImpl;
+
+template <typename T>
+struct DefaultHashImpl<T, true> {
+ using Hash = IntHash<typename std::make_unsigned<T>::type>;
+};
+
+// Canonical implementation of DefaultHash.
+template <typename T>
+struct DefaultHash
+ : DefaultHashImpl<T, std::is_integral<T>::value || std::is_enum<T>::value> {
+};
+
+// Specializations of DefaultHash follow.
+template <>
+struct DefaultHash<float> {
+ using Hash = FloatHash<float>;
+};
+template <>
+struct DefaultHash<double> {
+ using Hash = FloatHash<double>;
+};
+
+// Specializations for pointer types.
+template <typename T>
+struct DefaultHash<T*> {
+ using Hash = PtrHash<T>;
+};
+template <typename T>
+struct DefaultHash<scoped_refptr<T>> {
+ using Hash = RefPtrHash<T>;
+};
+template <typename T>
+struct DefaultHash<std::unique_ptr<T>> {
+ using Hash = UniquePtrHash<T>;
+};
+
+// Specializations for pairs.
+
+// Generic case (T or U is non-integral):
+template <typename T, typename U, bool areBothIntegral>
+struct PairHashImpl {
+ static unsigned GetHash(const std::pair<T, U>& p) {
+ return HashInts(DefaultHash<T>::Hash::GetHash(p.first),
+ DefaultHash<U>::Hash::GetHash(p.second));
+ }
+ static bool Equal(const std::pair<T, U>& a, const std::pair<T, U>& b) {
+ return DefaultHash<T>::Hash::Equal(a.first, b.first) &&
+ DefaultHash<U>::Hash::Equal(a.second, b.second);
+ }
+ static const bool safe_to_compare_to_empty_or_deleted =
+ DefaultHash<T>::Hash::safe_to_compare_to_empty_or_deleted &&
+ DefaultHash<U>::Hash::safe_to_compare_to_empty_or_deleted;
+};
+
+// Special version for pairs of integrals:
+template <typename T, typename U>
+struct PairHashImpl<T, U, true> {
+ static unsigned GetHash(const std::pair<T, U>& p) {
+ return HashInts(p.first, p.second);
+ }
+ static bool Equal(const std::pair<T, U>& a, const std::pair<T, U>& b) {
+ return PairHashImpl<T, U, false>::Equal(
+ a, b); // Refer to the generic version.
+ }
+ static const bool safe_to_compare_to_empty_or_deleted =
+ PairHashImpl<T, U, false>::safe_to_compare_to_empty_or_deleted;
+};
+
+// Combined version:
+template <typename T, typename U>
+struct PairHash
+ : PairHashImpl<T,
+ U,
+ std::is_integral<T>::value && std::is_integral<U>::value> {};
+
+template <typename T, typename U>
+struct DefaultHash<std::pair<T, U>> {
+ using Hash = PairHash<T, U>;
+};
+
+} // namespace WTF
+
+using WTF::DefaultHash;
+using WTF::IntHash;
+using WTF::PtrHash;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_HASH_FUNCTIONS_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/hash_iterators.h b/chromium/third_party/blink/renderer/platform/wtf/hash_iterators.h
new file mode 100644
index 00000000000..fa6a563c6fc
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/hash_iterators.h
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2007 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_HASH_ITERATORS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_HASH_ITERATORS_H_
+
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace WTF {
+
+template <typename HashTableType, typename KeyType, typename MappedType>
+struct HashTableConstKeysIterator;
+template <typename HashTableType, typename KeyType, typename MappedType>
+struct HashTableConstValuesIterator;
+template <typename HashTableType, typename KeyType, typename MappedType>
+struct HashTableKeysIterator;
+template <typename HashTableType, typename KeyType, typename MappedType>
+struct HashTableValuesIterator;
+
+template <typename HashTableType, typename KeyType, typename MappedType>
+struct HashTableConstIteratorAdapter<HashTableType,
+ KeyValuePair<KeyType, MappedType>> {
+ STACK_ALLOCATED();
+
+ private:
+ typedef KeyValuePair<KeyType, MappedType> ValueType;
+
+ public:
+ typedef HashTableConstKeysIterator<HashTableType, KeyType, MappedType>
+ KeysIterator;
+ typedef HashTableConstValuesIterator<HashTableType, KeyType, MappedType>
+ ValuesIterator;
+
+ HashTableConstIteratorAdapter() = default;
+ HashTableConstIteratorAdapter(
+ const typename HashTableType::const_iterator& impl)
+ : impl_(impl) {}
+
+ const ValueType* Get() const { return (const ValueType*)impl_.Get(); }
+ const ValueType& operator*() const { return *Get(); }
+ const ValueType* operator->() const { return Get(); }
+
+ HashTableConstIteratorAdapter& operator++() {
+ ++impl_;
+ return *this;
+ }
+ // postfix ++ intentionally omitted
+
+ KeysIterator Keys() { return KeysIterator(*this); }
+ ValuesIterator Values() { return ValuesIterator(*this); }
+
+ typename HashTableType::const_iterator impl_;
+};
+
+template <typename HashTableType, typename KeyType, typename MappedType>
+struct HashTableIteratorAdapter<HashTableType,
+ KeyValuePair<KeyType, MappedType>> {
+ STACK_ALLOCATED();
+
+ private:
+ typedef KeyValuePair<KeyType, MappedType> ValueType;
+
+ public:
+ typedef HashTableKeysIterator<HashTableType, KeyType, MappedType>
+ KeysIterator;
+ typedef HashTableValuesIterator<HashTableType, KeyType, MappedType>
+ ValuesIterator;
+
+ HashTableIteratorAdapter() = default;
+ HashTableIteratorAdapter(const typename HashTableType::iterator& impl)
+ : impl_(impl) {}
+
+ ValueType* Get() const { return (ValueType*)impl_.Get(); }
+ ValueType& operator*() const { return *Get(); }
+ ValueType* operator->() const { return Get(); }
+
+ HashTableIteratorAdapter& operator++() {
+ ++impl_;
+ return *this;
+ }
+ // postfix ++ intentionally omitted
+
+ operator HashTableConstIteratorAdapter<HashTableType, ValueType>() {
+ typename HashTableType::const_iterator i = impl_;
+ return i;
+ }
+
+ KeysIterator Keys() { return KeysIterator(*this); }
+ ValuesIterator Values() { return ValuesIterator(*this); }
+
+ typename HashTableType::iterator impl_;
+};
+
+template <typename HashTableType, typename KeyType, typename MappedType>
+struct HashTableConstKeysIterator {
+ STACK_ALLOCATED();
+
+ private:
+ typedef HashTableConstIteratorAdapter<HashTableType,
+ KeyValuePair<KeyType, MappedType>>
+ ConstIterator;
+
+ public:
+ HashTableConstKeysIterator(const ConstIterator& impl) : impl_(impl) {}
+
+ const KeyType* Get() const { return &(impl_.Get()->key); }
+ const KeyType& operator*() const { return *Get(); }
+ const KeyType* operator->() const { return Get(); }
+
+ HashTableConstKeysIterator& operator++() {
+ ++impl_;
+ return *this;
+ }
+ // postfix ++ intentionally omitted
+
+ ConstIterator impl_;
+};
+
+template <typename HashTableType, typename KeyType, typename MappedType>
+struct HashTableConstValuesIterator {
+ STACK_ALLOCATED();
+
+ private:
+ typedef HashTableConstIteratorAdapter<HashTableType,
+ KeyValuePair<KeyType, MappedType>>
+ ConstIterator;
+
+ public:
+ HashTableConstValuesIterator(const ConstIterator& impl) : impl_(impl) {}
+
+ const MappedType* Get() const { return &(impl_.Get()->value); }
+ const MappedType& operator*() const { return *Get(); }
+ const MappedType* operator->() const { return Get(); }
+
+ HashTableConstValuesIterator& operator++() {
+ ++impl_;
+ return *this;
+ }
+ // postfix ++ intentionally omitted
+
+ ConstIterator impl_;
+};
+
+template <typename HashTableType, typename KeyType, typename MappedType>
+struct HashTableKeysIterator {
+ STACK_ALLOCATED();
+
+ private:
+ typedef HashTableIteratorAdapter<HashTableType,
+ KeyValuePair<KeyType, MappedType>>
+ Iterator;
+ typedef HashTableConstIteratorAdapter<HashTableType,
+ KeyValuePair<KeyType, MappedType>>
+ ConstIterator;
+
+ public:
+ HashTableKeysIterator(const Iterator& impl) : impl_(impl) {}
+
+ KeyType* Get() const { return &(impl_.Get()->key); }
+ KeyType& operator*() const { return *Get(); }
+ KeyType* operator->() const { return Get(); }
+
+ HashTableKeysIterator& operator++() {
+ ++impl_;
+ return *this;
+ }
+ // postfix ++ intentionally omitted
+
+ operator HashTableConstKeysIterator<HashTableType, KeyType, MappedType>() {
+ ConstIterator i = impl_;
+ return i;
+ }
+
+ Iterator impl_;
+};
+
+template <typename HashTableType, typename KeyType, typename MappedType>
+struct HashTableValuesIterator {
+ STACK_ALLOCATED();
+
+ private:
+ typedef HashTableIteratorAdapter<HashTableType,
+ KeyValuePair<KeyType, MappedType>>
+ Iterator;
+ typedef HashTableConstIteratorAdapter<HashTableType,
+ KeyValuePair<KeyType, MappedType>>
+ ConstIterator;
+
+ public:
+ HashTableValuesIterator(const Iterator& impl) : impl_(impl) {}
+
+ MappedType* Get() const { return &(impl_.Get()->value); }
+ MappedType& operator*() const { return *Get(); }
+ MappedType* operator->() const { return Get(); }
+
+ HashTableValuesIterator& operator++() {
+ ++impl_;
+ return *this;
+ }
+ // postfix ++ intentionally omitted
+
+ operator HashTableConstValuesIterator<HashTableType, KeyType, MappedType>() {
+ ConstIterator i = impl_;
+ return i;
+ }
+
+ Iterator impl_;
+};
+
+template <typename T, typename U, typename V>
+inline bool operator==(const HashTableConstKeysIterator<T, U, V>& a,
+ const HashTableConstKeysIterator<T, U, V>& b) {
+ return a.impl_ == b.impl_;
+}
+
+template <typename T, typename U, typename V>
+inline bool operator!=(const HashTableConstKeysIterator<T, U, V>& a,
+ const HashTableConstKeysIterator<T, U, V>& b) {
+ return a.impl_ != b.impl_;
+}
+
+template <typename T, typename U, typename V>
+inline bool operator==(const HashTableConstValuesIterator<T, U, V>& a,
+ const HashTableConstValuesIterator<T, U, V>& b) {
+ return a.impl_ == b.impl_;
+}
+
+template <typename T, typename U, typename V>
+inline bool operator!=(const HashTableConstValuesIterator<T, U, V>& a,
+ const HashTableConstValuesIterator<T, U, V>& b) {
+ return a.impl_ != b.impl_;
+}
+
+template <typename T, typename U, typename V>
+inline bool operator==(const HashTableKeysIterator<T, U, V>& a,
+ const HashTableKeysIterator<T, U, V>& b) {
+ return a.impl_ == b.impl_;
+}
+
+template <typename T, typename U, typename V>
+inline bool operator!=(const HashTableKeysIterator<T, U, V>& a,
+ const HashTableKeysIterator<T, U, V>& b) {
+ return a.impl_ != b.impl_;
+}
+
+template <typename T, typename U, typename V>
+inline bool operator==(const HashTableValuesIterator<T, U, V>& a,
+ const HashTableValuesIterator<T, U, V>& b) {
+ return a.impl_ == b.impl_;
+}
+
+template <typename T, typename U, typename V>
+inline bool operator!=(const HashTableValuesIterator<T, U, V>& a,
+ const HashTableValuesIterator<T, U, V>& b) {
+ return a.impl_ != b.impl_;
+}
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_HASH_ITERATORS_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/hash_map.h b/chromium/third_party/blink/renderer/platform/wtf/hash_map.h
new file mode 100644
index 00000000000..f5f1c9e00fe
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/hash_map.h
@@ -0,0 +1,764 @@
+/*
+ * Copyright (C) 2005, 2006, 2007, 2008, 2011 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_HASH_MAP_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_HASH_MAP_H_
+
+#include <initializer_list>
+#include "third_party/blink/renderer/platform/wtf/allocator/partition_allocator.h"
+#include "third_party/blink/renderer/platform/wtf/construct_traits.h"
+#include "third_party/blink/renderer/platform/wtf/hash_table.h"
+
+namespace WTF {
+
+template <typename KeyTraits, typename MappedTraits>
+struct HashMapValueTraits;
+
+struct KeyValuePairKeyExtractor {
+ STATIC_ONLY(KeyValuePairKeyExtractor);
+ template <typename T>
+ static const typename T::KeyType& Extract(const T& p) {
+ return p.key;
+ }
+};
+
+// Note: empty or deleted key values are not allowed, using them may lead to
+// undefined behavior. For pointer keys this means that null pointers are not
+// allowed; for integer keys 0 or -1 can't be used as a key. This restriction
+// can be lifted if you supply custom key traits.
+template <typename KeyArg,
+ typename MappedArg,
+ typename HashArg = typename DefaultHash<KeyArg>::Hash,
+ typename KeyTraitsArg = HashTraits<KeyArg>,
+ typename MappedTraitsArg = HashTraits<MappedArg>,
+ typename Allocator = PartitionAllocator>
+class HashMap {
+ USE_ALLOCATOR(HashMap, Allocator);
+
+ private:
+ typedef KeyTraitsArg KeyTraits;
+ typedef MappedTraitsArg MappedTraits;
+ typedef HashMapValueTraits<KeyTraits, MappedTraits> ValueTraits;
+
+ public:
+ typedef typename KeyTraits::TraitType KeyType;
+ typedef const typename KeyTraits::PeekInType& KeyPeekInType;
+ typedef typename MappedTraits::TraitType MappedType;
+ typedef typename ValueTraits::TraitType ValueType;
+ using value_type = ValueType;
+
+ private:
+ typedef typename MappedTraits::PeekOutType MappedPeekType;
+
+ typedef HashArg HashFunctions;
+
+ typedef HashTable<KeyType,
+ ValueType,
+ KeyValuePairKeyExtractor,
+ HashFunctions,
+ ValueTraits,
+ KeyTraits,
+ Allocator>
+ HashTableType;
+
+ class HashMapKeysProxy;
+ class HashMapValuesProxy;
+
+ public:
+ HashMap() {
+ static_assert(Allocator::kIsGarbageCollected ||
+ !IsPointerToGarbageCollectedType<KeyArg>::value,
+ "Cannot put raw pointers to garbage-collected classes into "
+ "an off-heap HashMap. Use HeapHashMap<> instead.");
+ static_assert(Allocator::kIsGarbageCollected ||
+ !IsPointerToGarbageCollectedType<MappedArg>::value,
+ "Cannot put raw pointers to garbage-collected classes into "
+ "an off-heap HashMap. Use HeapHashMap<> instead.");
+ }
+
+#if DUMP_HASHTABLE_STATS_PER_TABLE
+ void DumpStats() { impl_.DumpStats(); }
+#endif
+ HashMap(const HashMap&) = default;
+ HashMap& operator=(const HashMap&) = default;
+ HashMap(HashMap&&) = default;
+ HashMap& operator=(HashMap&&) = default;
+
+ // For example, HashMap<int, int>({{1, 11}, {2, 22}, {3, 33}}) will give you
+ // a HashMap containing a mapping {1 -> 11, 2 -> 22, 3 -> 33}.
+ HashMap(std::initializer_list<ValueType> elements);
+ HashMap& operator=(std::initializer_list<ValueType> elements);
+
+ typedef HashTableIteratorAdapter<HashTableType, ValueType> iterator;
+ typedef HashTableConstIteratorAdapter<HashTableType, ValueType>
+ const_iterator;
+ typedef typename HashTableType::AddResult AddResult;
+
+ void swap(HashMap& ref) { impl_.swap(ref.impl_); }
+
+ unsigned size() const;
+ unsigned Capacity() const;
+ void ReserveCapacityForSize(unsigned size) {
+ impl_.ReserveCapacityForSize(size);
+ }
+
+ bool IsEmpty() const;
+
+ // iterators iterate over pairs of keys and values
+ iterator begin();
+ iterator end();
+ const_iterator begin() const;
+ const_iterator end() const;
+
+ HashMapKeysProxy& Keys() { return static_cast<HashMapKeysProxy&>(*this); }
+ const HashMapKeysProxy& Keys() const {
+ return static_cast<const HashMapKeysProxy&>(*this);
+ }
+
+ HashMapValuesProxy& Values() {
+ return static_cast<HashMapValuesProxy&>(*this);
+ }
+ const HashMapValuesProxy& Values() const {
+ return static_cast<const HashMapValuesProxy&>(*this);
+ }
+
+ iterator find(KeyPeekInType);
+ const_iterator find(KeyPeekInType) const;
+ bool Contains(KeyPeekInType) const;
+ MappedPeekType at(KeyPeekInType) const;
+
+ // replaces value but not key if key is already present return value is a
+ // pair of the iterator to the key location, and a boolean that's true if a
+ // new value was actually added
+ template <typename IncomingKeyType, typename IncomingMappedType>
+ AddResult Set(IncomingKeyType&&, IncomingMappedType&&);
+
+ // does nothing if key is already present return value is a pair of the
+ // iterator to the key location, and a boolean that's true if a new value
+ // was actually added
+ template <typename IncomingKeyType, typename IncomingMappedType>
+ AddResult insert(IncomingKeyType&&, IncomingMappedType&&);
+
+ void erase(KeyPeekInType);
+ void erase(iterator);
+ void clear();
+ template <typename Collection>
+ void RemoveAll(const Collection& to_be_removed) {
+ WTF::RemoveAll(*this, to_be_removed);
+ }
+
+ MappedType Take(KeyPeekInType); // efficient combination of get with remove
+
+ // An alternate version of find() that finds the object by hashing and
+ // comparing with some other type, to avoid the cost of type
+ // conversion. HashTranslator must have the following function members:
+ // static unsigned hash(const T&);
+ // static bool equal(const ValueType&, const T&);
+ template <typename HashTranslator, typename T>
+ iterator Find(const T&);
+ template <typename HashTranslator, typename T>
+ const_iterator Find(const T&) const;
+ template <typename HashTranslator, typename T>
+ bool Contains(const T&) const;
+
+ // An alternate version of insert() that finds the object by hashing and
+ // comparing with some other type, to avoid the cost of type conversion if
+ // the object is already in the table. HashTranslator must have the
+ // following function members:
+ // static unsigned hash(const T&);
+ // static bool equal(const ValueType&, const T&);
+ // static translate(ValueType&, const T&, unsigned hashCode);
+ template <typename HashTranslator,
+ typename IncomingKeyType,
+ typename IncomingMappedType>
+ AddResult Insert(IncomingKeyType&&, IncomingMappedType&&);
+
+ static bool IsValidKey(KeyPeekInType);
+
+ template <typename VisitorDispatcher, typename A = Allocator>
+ std::enable_if_t<A::kIsGarbageCollected> Trace(VisitorDispatcher visitor) {
+ impl_.Trace(visitor);
+ }
+
+ private:
+ template <typename IncomingKeyType, typename IncomingMappedType>
+ AddResult InlineAdd(IncomingKeyType&&, IncomingMappedType&&);
+
+ HashTableType impl_;
+};
+
+template <typename KeyArg,
+ typename MappedArg,
+ typename HashArg,
+ typename KeyTraitsArg,
+ typename MappedTraitsArg,
+ typename Allocator>
+class HashMap<KeyArg,
+ MappedArg,
+ HashArg,
+ KeyTraitsArg,
+ MappedTraitsArg,
+ Allocator>::HashMapKeysProxy : private HashMap<KeyArg,
+ MappedArg,
+ HashArg,
+ KeyTraitsArg,
+ MappedTraitsArg,
+ Allocator> {
+ DISALLOW_NEW();
+
+ public:
+ typedef HashMap<KeyArg,
+ MappedArg,
+ HashArg,
+ KeyTraitsArg,
+ MappedTraitsArg,
+ Allocator>
+ HashMapType;
+ typedef typename HashMapType::iterator::KeysIterator iterator;
+ typedef typename HashMapType::const_iterator::KeysIterator const_iterator;
+
+ iterator begin() { return HashMapType::begin().Keys(); }
+
+ iterator end() { return HashMapType::end().Keys(); }
+
+ const_iterator begin() const { return HashMapType::begin().Keys(); }
+
+ const_iterator end() const { return HashMapType::end().Keys(); }
+
+ private:
+ friend class HashMap;
+
+ HashMapKeysProxy() = delete;
+ HashMapKeysProxy(const HashMapKeysProxy&) = delete;
+ HashMapKeysProxy& operator=(const HashMapKeysProxy&) = delete;
+ ~HashMapKeysProxy() = delete;
+};
+
+template <typename KeyArg,
+ typename MappedArg,
+ typename HashArg,
+ typename KeyTraitsArg,
+ typename MappedTraitsArg,
+ typename Allocator>
+class HashMap<KeyArg,
+ MappedArg,
+ HashArg,
+ KeyTraitsArg,
+ MappedTraitsArg,
+ Allocator>::HashMapValuesProxy : private HashMap<KeyArg,
+ MappedArg,
+ HashArg,
+ KeyTraitsArg,
+ MappedTraitsArg,
+ Allocator> {
+ DISALLOW_NEW();
+
+ public:
+ typedef HashMap<KeyArg,
+ MappedArg,
+ HashArg,
+ KeyTraitsArg,
+ MappedTraitsArg,
+ Allocator>
+ HashMapType;
+ typedef typename HashMapType::iterator::ValuesIterator iterator;
+ typedef typename HashMapType::const_iterator::ValuesIterator const_iterator;
+
+ iterator begin() { return HashMapType::begin().Values(); }
+
+ iterator end() { return HashMapType::end().Values(); }
+
+ const_iterator begin() const { return HashMapType::begin().Values(); }
+
+ const_iterator end() const { return HashMapType::end().Values(); }
+
+ private:
+ friend class HashMap;
+
+ HashMapValuesProxy() = delete;
+ HashMapValuesProxy(const HashMapValuesProxy&) = delete;
+ HashMapValuesProxy& operator=(const HashMapValuesProxy&) = delete;
+ ~HashMapValuesProxy() = delete;
+};
+
+template <typename KeyTraits, typename MappedTraits>
+struct HashMapValueTraits : KeyValuePairHashTraits<KeyTraits, MappedTraits> {
+ STATIC_ONLY(HashMapValueTraits);
+ static const bool kHasIsEmptyValueFunction = true;
+ static bool IsEmptyValue(
+ const typename KeyValuePairHashTraits<KeyTraits, MappedTraits>::TraitType&
+ value) {
+ return IsHashTraitsEmptyValue<KeyTraits>(value.key);
+ }
+};
+
+template <typename ValueTraits, typename HashFunctions, typename Allocator>
+struct HashMapTranslator {
+ STATIC_ONLY(HashMapTranslator);
+ template <typename T>
+ static unsigned GetHash(const T& key) {
+ return HashFunctions::GetHash(key);
+ }
+ template <typename T, typename U>
+ static bool Equal(const T& a, const U& b) {
+ return HashFunctions::Equal(a, b);
+ }
+ template <typename T, typename U, typename V>
+ static void Translate(T& location, U&& key, V&& mapped) {
+ location.key = std::forward<U>(key);
+ ValueTraits::ValueTraits::Store(std::forward<V>(mapped), location.value);
+ }
+};
+
+template <typename ValueTraits, typename Translator>
+struct HashMapTranslatorAdapter {
+ STATIC_ONLY(HashMapTranslatorAdapter);
+ template <typename T>
+ static unsigned GetHash(const T& key) {
+ return Translator::GetHash(key);
+ }
+ template <typename T, typename U>
+ static bool Equal(const T& a, const U& b) {
+ return Translator::Equal(a, b);
+ }
+ template <typename T, typename U, typename V>
+ static void Translate(T& location, U&& key, V&& mapped, unsigned hash_code) {
+ Translator::Translate(location.key, std::forward<U>(key), hash_code);
+ ValueTraits::ValueTraits::store(std::forward<V>(mapped), location.value);
+ }
+};
+
+template <typename T,
+ typename U,
+ typename V,
+ typename W,
+ typename X,
+ typename Y>
+HashMap<T, U, V, W, X, Y>::HashMap(std::initializer_list<ValueType> elements) {
+ if (elements.size())
+ impl_.ReserveCapacityForSize(elements.size());
+ for (const ValueType& element : elements)
+ insert(element.key, element.value);
+}
+
+template <typename T,
+ typename U,
+ typename V,
+ typename W,
+ typename X,
+ typename Y>
+auto HashMap<T, U, V, W, X, Y>::operator=(
+ std::initializer_list<ValueType> elements) -> HashMap& {
+ *this = HashMap(std::move(elements));
+ return *this;
+}
+
+template <typename T,
+ typename U,
+ typename V,
+ typename W,
+ typename X,
+ typename Y>
+inline unsigned HashMap<T, U, V, W, X, Y>::size() const {
+ return impl_.size();
+}
+
+template <typename T,
+ typename U,
+ typename V,
+ typename W,
+ typename X,
+ typename Y>
+inline unsigned HashMap<T, U, V, W, X, Y>::Capacity() const {
+ return impl_.Capacity();
+}
+
+template <typename T,
+ typename U,
+ typename V,
+ typename W,
+ typename X,
+ typename Y>
+inline bool HashMap<T, U, V, W, X, Y>::IsEmpty() const {
+ return impl_.IsEmpty();
+}
+
+template <typename T,
+ typename U,
+ typename V,
+ typename W,
+ typename X,
+ typename Y>
+inline typename HashMap<T, U, V, W, X, Y>::iterator
+HashMap<T, U, V, W, X, Y>::begin() {
+ return impl_.begin();
+}
+
+template <typename T,
+ typename U,
+ typename V,
+ typename W,
+ typename X,
+ typename Y>
+inline typename HashMap<T, U, V, W, X, Y>::iterator
+HashMap<T, U, V, W, X, Y>::end() {
+ return impl_.end();
+}
+
+template <typename T,
+ typename U,
+ typename V,
+ typename W,
+ typename X,
+ typename Y>
+inline typename HashMap<T, U, V, W, X, Y>::const_iterator
+HashMap<T, U, V, W, X, Y>::begin() const {
+ return impl_.begin();
+}
+
+template <typename T,
+ typename U,
+ typename V,
+ typename W,
+ typename X,
+ typename Y>
+inline typename HashMap<T, U, V, W, X, Y>::const_iterator
+HashMap<T, U, V, W, X, Y>::end() const {
+ return impl_.end();
+}
+
+template <typename T,
+ typename U,
+ typename V,
+ typename W,
+ typename X,
+ typename Y>
+inline typename HashMap<T, U, V, W, X, Y>::iterator
+HashMap<T, U, V, W, X, Y>::find(KeyPeekInType key) {
+ return impl_.find(key);
+}
+
+template <typename T,
+ typename U,
+ typename V,
+ typename W,
+ typename X,
+ typename Y>
+inline typename HashMap<T, U, V, W, X, Y>::const_iterator
+HashMap<T, U, V, W, X, Y>::find(KeyPeekInType key) const {
+ return impl_.find(key);
+}
+
+template <typename T,
+ typename U,
+ typename V,
+ typename W,
+ typename X,
+ typename Y>
+inline bool HashMap<T, U, V, W, X, Y>::Contains(KeyPeekInType key) const {
+ return impl_.Contains(key);
+}
+
+template <typename T,
+ typename U,
+ typename V,
+ typename W,
+ typename X,
+ typename Y>
+template <typename HashTranslator, typename TYPE>
+inline typename HashMap<T, U, V, W, X, Y>::iterator
+HashMap<T, U, V, W, X, Y>::Find(const TYPE& value) {
+ return impl_
+ .template Find<HashMapTranslatorAdapter<ValueTraits, HashTranslator>>(
+ value);
+}
+
+template <typename T,
+ typename U,
+ typename V,
+ typename W,
+ typename X,
+ typename Y>
+template <typename HashTranslator, typename TYPE>
+inline typename HashMap<T, U, V, W, X, Y>::const_iterator
+HashMap<T, U, V, W, X, Y>::Find(const TYPE& value) const {
+ return impl_
+ .template Find<HashMapTranslatorAdapter<ValueTraits, HashTranslator>>(
+ value);
+}
+
+template <typename T,
+ typename U,
+ typename V,
+ typename W,
+ typename X,
+ typename Y>
+template <typename HashTranslator, typename TYPE>
+inline bool HashMap<T, U, V, W, X, Y>::Contains(const TYPE& value) const {
+ return impl_
+ .template Contains<HashMapTranslatorAdapter<ValueTraits, HashTranslator>>(
+ value);
+}
+
+template <typename T,
+ typename U,
+ typename V,
+ typename W,
+ typename X,
+ typename Allocator>
+template <typename IncomingKeyType, typename IncomingMappedType>
+typename HashMap<T, U, V, W, X, Allocator>::AddResult
+HashMap<T, U, V, W, X, Allocator>::InlineAdd(IncomingKeyType&& key,
+ IncomingMappedType&& mapped) {
+ return impl_.template insert<
+ HashMapTranslator<ValueTraits, HashFunctions, Allocator>>(
+ std::forward<IncomingKeyType>(key),
+ std::forward<IncomingMappedType>(mapped));
+}
+
+template <typename T,
+ typename U,
+ typename V,
+ typename W,
+ typename X,
+ typename Y>
+template <typename IncomingKeyType, typename IncomingMappedType>
+typename HashMap<T, U, V, W, X, Y>::AddResult HashMap<T, U, V, W, X, Y>::Set(
+ IncomingKeyType&& key,
+ IncomingMappedType&& mapped) {
+ AddResult result = InlineAdd(std::forward<IncomingKeyType>(key),
+ std::forward<IncomingMappedType>(mapped));
+ if (!result.is_new_entry) {
+ // The inlineAdd call above found an existing hash table entry; we need
+ // to set the mapped value.
+ //
+ // It's safe to call std::forward again, because |mapped| isn't moved if
+ // there's an existing entry.
+ MappedTraits::Store(std::forward<IncomingMappedType>(mapped),
+ result.stored_value->value);
+ }
+ return result;
+}
+
+template <typename T,
+ typename U,
+ typename V,
+ typename W,
+ typename X,
+ typename Y>
+template <typename HashTranslator,
+ typename IncomingKeyType,
+ typename IncomingMappedType>
+auto HashMap<T, U, V, W, X, Y>::Insert(IncomingKeyType&& key,
+ IncomingMappedType&& mapped)
+ -> AddResult {
+ return impl_.template AddPassingHashCode<
+ HashMapTranslatorAdapter<ValueTraits, HashTranslator>>(
+ std::forward<IncomingKeyType>(key),
+ std::forward<IncomingMappedType>(mapped));
+}
+
+template <typename T,
+ typename U,
+ typename V,
+ typename W,
+ typename X,
+ typename Y>
+template <typename IncomingKeyType, typename IncomingMappedType>
+typename HashMap<T, U, V, W, X, Y>::AddResult HashMap<T, U, V, W, X, Y>::insert(
+ IncomingKeyType&& key,
+ IncomingMappedType&& mapped) {
+ return InlineAdd(std::forward<IncomingKeyType>(key),
+ std::forward<IncomingMappedType>(mapped));
+}
+
+template <typename T,
+ typename U,
+ typename V,
+ typename W,
+ typename X,
+ typename Y>
+typename HashMap<T, U, V, W, X, Y>::MappedPeekType
+HashMap<T, U, V, W, X, Y>::at(KeyPeekInType key) const {
+ const ValueType* entry = impl_.Lookup(key);
+ if (!entry)
+ return MappedTraits::Peek(MappedTraits::EmptyValue());
+ return MappedTraits::Peek(entry->value);
+}
+
+template <typename T,
+ typename U,
+ typename V,
+ typename W,
+ typename X,
+ typename Y>
+inline void HashMap<T, U, V, W, X, Y>::erase(iterator it) {
+ impl_.erase(it.impl_);
+}
+
+template <typename T,
+ typename U,
+ typename V,
+ typename W,
+ typename X,
+ typename Y>
+inline void HashMap<T, U, V, W, X, Y>::erase(KeyPeekInType key) {
+ erase(find(key));
+}
+
+template <typename T,
+ typename U,
+ typename V,
+ typename W,
+ typename X,
+ typename Y>
+inline void HashMap<T, U, V, W, X, Y>::clear() {
+ impl_.clear();
+}
+
+template <typename T,
+ typename U,
+ typename V,
+ typename W,
+ typename X,
+ typename Y>
+auto HashMap<T, U, V, W, X, Y>::Take(KeyPeekInType key) -> MappedType {
+ iterator it = find(key);
+ if (it == end())
+ return MappedTraits::EmptyValue();
+ MappedType result = std::move(it->value);
+ erase(it);
+ return result;
+}
+
+template <typename T,
+ typename U,
+ typename V,
+ typename W,
+ typename X,
+ typename Y>
+inline bool HashMap<T, U, V, W, X, Y>::IsValidKey(KeyPeekInType key) {
+ if (KeyTraits::IsDeletedValue(key))
+ return false;
+
+ if (HashFunctions::safe_to_compare_to_empty_or_deleted) {
+ if (key == KeyTraits::EmptyValue())
+ return false;
+ } else {
+ if (IsHashTraitsEmptyValue<KeyTraits>(key))
+ return false;
+ }
+
+ return true;
+}
+
+template <typename T,
+ typename U,
+ typename V,
+ typename W,
+ typename X,
+ typename Y>
+bool operator==(const HashMap<T, U, V, W, X, Y>& a,
+ const HashMap<T, U, V, W, X, Y>& b) {
+ if (a.size() != b.size())
+ return false;
+
+ typedef typename HashMap<T, U, V, W, X, Y>::const_iterator const_iterator;
+
+ const_iterator a_end = a.end();
+ const_iterator b_end = b.end();
+ for (const_iterator it = a.begin(); it != a_end; ++it) {
+ const_iterator b_pos = b.find(it->key);
+ if (b_pos == b_end || it->value != b_pos->value)
+ return false;
+ }
+
+ return true;
+}
+
+template <typename T,
+ typename U,
+ typename V,
+ typename W,
+ typename X,
+ typename Y>
+inline bool operator!=(const HashMap<T, U, V, W, X, Y>& a,
+ const HashMap<T, U, V, W, X, Y>& b) {
+ return !(a == b);
+}
+
+template <typename T,
+ typename U,
+ typename V,
+ typename W,
+ typename X,
+ typename Y,
+ typename Z>
+inline void CopyKeysToVector(const HashMap<T, U, V, W, X, Y>& collection,
+ Z& vector) {
+ typedef
+ typename HashMap<T, U, V, W, X, Y>::const_iterator::KeysIterator iterator;
+
+ {
+ // Disallow GC during resize allocation; see crbugs 568173 and 823612.
+ // The element copy doesn't need to be in this scope because garbage
+ // collection can only remove elements from collection if its keys are
+ // WeakMembers, in which case copying them doesn't perform a heap
+ // allocation.
+ typename Z::GCForbiddenScope scope;
+ vector.resize(collection.size());
+ }
+
+ iterator it = collection.begin().Keys();
+ iterator end = collection.end().Keys();
+ for (unsigned i = 0; it != end; ++it, ++i)
+ vector[i] = *it;
+}
+
+template <typename T,
+ typename U,
+ typename V,
+ typename W,
+ typename X,
+ typename Y,
+ typename Z>
+inline void CopyValuesToVector(const HashMap<T, U, V, W, X, Y>& collection,
+ Z& vector) {
+ typedef typename HashMap<T, U, V, W, X, Y>::const_iterator::ValuesIterator
+ iterator;
+
+ // Disallow GC during resize allocation and copy operations (which may also
+ // perform allocations and therefore cause elements of collection to be
+ // removed); see crbugs 568173 and 823612.
+ typename Z::GCForbiddenScope scope;
+
+ vector.resize(collection.size());
+
+ iterator it = collection.begin().Values();
+ iterator end = collection.end().Values();
+ for (unsigned i = 0; it != end; ++it, ++i)
+ vector[i] = *it;
+}
+
+} // namespace WTF
+
+using WTF::HashMap;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_HASH_MAP_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/hash_map_test.cc b/chromium/third_party/blink/renderer/platform/wtf/hash_map_test.cc
new file mode 100644
index 00000000000..f9f187c6938
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/hash_map_test.cc
@@ -0,0 +1,602 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/wtf/hash_map.h"
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "base/memory/scoped_refptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_test_helper.h"
+
+namespace WTF {
+
+int DummyRefCounted::ref_invokes_count_ = 0;
+
+namespace {
+
+using IntHashMap = HashMap<int, int>;
+
+TEST(HashMapTest, IteratorComparison) {
+ IntHashMap map;
+ map.insert(1, 2);
+ EXPECT_TRUE(map.begin() != map.end());
+ EXPECT_FALSE(map.begin() == map.end());
+
+ IntHashMap::const_iterator begin = map.begin();
+ EXPECT_TRUE(begin == map.begin());
+ EXPECT_TRUE(map.begin() == begin);
+ EXPECT_TRUE(begin != map.end());
+ EXPECT_TRUE(map.end() != begin);
+ EXPECT_FALSE(begin != map.begin());
+ EXPECT_FALSE(map.begin() != begin);
+ EXPECT_FALSE(begin == map.end());
+ EXPECT_FALSE(map.end() == begin);
+}
+
+struct TestDoubleHashTraits : HashTraits<double> {
+ static const unsigned kMinimumTableSize = 8;
+};
+
+using DoubleHashMap =
+ HashMap<double, int64_t, DefaultHash<double>::Hash, TestDoubleHashTraits>;
+
+int BucketForKey(double key) {
+ return DefaultHash<double>::Hash::GetHash(key) &
+ (TestDoubleHashTraits::kMinimumTableSize - 1);
+}
+
+TEST(HashMapTest, DoubleHashCollisions) {
+ // The "clobber" key here is one that ends up stealing the bucket that the -0
+ // key originally wants to be in. This makes the 0 and -0 keys collide and
+ // the test then fails unless the FloatHash::equals() implementation can
+ // distinguish them.
+ const double kClobberKey = 6;
+ const double kZeroKey = 0;
+ const double kNegativeZeroKey = -kZeroKey;
+
+ DoubleHashMap map;
+
+ map.insert(kClobberKey, 1);
+ map.insert(kZeroKey, 2);
+ map.insert(kNegativeZeroKey, 3);
+
+ EXPECT_EQ(BucketForKey(kClobberKey), BucketForKey(kNegativeZeroKey));
+ EXPECT_EQ(1, map.at(kClobberKey));
+ EXPECT_EQ(2, map.at(kZeroKey));
+ EXPECT_EQ(3, map.at(kNegativeZeroKey));
+}
+
+using OwnPtrHashMap = HashMap<int, std::unique_ptr<DestructCounter>>;
+
+TEST(HashMapTest, OwnPtrAsValue) {
+ int destruct_number = 0;
+ OwnPtrHashMap map;
+ map.insert(1, std::make_unique<DestructCounter>(1, &destruct_number));
+ map.insert(2, std::make_unique<DestructCounter>(2, &destruct_number));
+
+ DestructCounter* counter1 = map.at(1);
+ EXPECT_EQ(1, counter1->Get());
+ DestructCounter* counter2 = map.at(2);
+ EXPECT_EQ(2, counter2->Get());
+ EXPECT_EQ(0, destruct_number);
+
+ for (OwnPtrHashMap::iterator iter = map.begin(); iter != map.end(); ++iter) {
+ std::unique_ptr<DestructCounter>& own_counter = iter->value;
+ EXPECT_EQ(iter->key, own_counter->Get());
+ }
+ ASSERT_EQ(0, destruct_number);
+
+ std::unique_ptr<DestructCounter> own_counter1 = map.Take(1);
+ EXPECT_EQ(own_counter1.get(), counter1);
+ EXPECT_FALSE(map.Contains(1));
+ EXPECT_EQ(0, destruct_number);
+
+ map.erase(2);
+ EXPECT_FALSE(map.Contains(2));
+ EXPECT_EQ(0UL, map.size());
+ EXPECT_EQ(1, destruct_number);
+
+ own_counter1.reset();
+ EXPECT_EQ(2, destruct_number);
+}
+
+TEST(HashMapTest, RefPtrAsKey) {
+ bool is_deleted = false;
+ DummyRefCounted::ref_invokes_count_ = 0;
+ scoped_refptr<DummyRefCounted> ptr =
+ base::AdoptRef(new DummyRefCounted(is_deleted));
+ EXPECT_EQ(0, DummyRefCounted::ref_invokes_count_);
+ HashMap<scoped_refptr<DummyRefCounted>, int> map;
+ map.insert(ptr, 1);
+ // Referenced only once (to store a copy in the container).
+ EXPECT_EQ(1, DummyRefCounted::ref_invokes_count_);
+ EXPECT_EQ(1, map.at(ptr));
+
+ DummyRefCounted* raw_ptr = ptr.get();
+
+ EXPECT_TRUE(map.Contains(raw_ptr));
+ EXPECT_NE(map.end(), map.find(raw_ptr));
+ EXPECT_TRUE(map.Contains(ptr));
+ EXPECT_NE(map.end(), map.find(ptr));
+ EXPECT_EQ(1, DummyRefCounted::ref_invokes_count_);
+
+ ptr = nullptr;
+ EXPECT_FALSE(is_deleted);
+
+ map.erase(raw_ptr);
+ EXPECT_EQ(1, DummyRefCounted::ref_invokes_count_);
+ EXPECT_TRUE(is_deleted);
+ EXPECT_TRUE(map.IsEmpty());
+}
+
+TEST(HashMaptest, RemoveAdd) {
+ DummyRefCounted::ref_invokes_count_ = 0;
+ bool is_deleted = false;
+
+ typedef HashMap<int, scoped_refptr<DummyRefCounted>> Map;
+ Map map;
+
+ scoped_refptr<DummyRefCounted> ptr =
+ base::AdoptRef(new DummyRefCounted(is_deleted));
+ EXPECT_EQ(0, DummyRefCounted::ref_invokes_count_);
+
+ map.insert(1, ptr);
+ // Referenced only once (to store a copy in the container).
+ EXPECT_EQ(1, DummyRefCounted::ref_invokes_count_);
+ EXPECT_EQ(ptr, map.at(1));
+
+ ptr = nullptr;
+ EXPECT_FALSE(is_deleted);
+
+ map.erase(1);
+ EXPECT_EQ(1, DummyRefCounted::ref_invokes_count_);
+ EXPECT_TRUE(is_deleted);
+ EXPECT_TRUE(map.IsEmpty());
+
+ // Add and remove until the deleted slot is reused.
+ for (int i = 1; i < 100; i++) {
+ bool is_deleted2 = false;
+ scoped_refptr<DummyRefCounted> ptr2 =
+ base::AdoptRef(new DummyRefCounted(is_deleted2));
+ map.insert(i, ptr2);
+ EXPECT_FALSE(is_deleted2);
+ ptr2 = nullptr;
+ EXPECT_FALSE(is_deleted2);
+ map.erase(i);
+ EXPECT_TRUE(is_deleted2);
+ }
+}
+
+class SimpleClass {
+ public:
+ explicit SimpleClass(int v) : v_(v) {}
+ int V() { return v_; }
+
+ private:
+ int v_;
+};
+using IntSimpleMap = HashMap<int, std::unique_ptr<SimpleClass>>;
+
+TEST(HashMapTest, AddResult) {
+ IntSimpleMap map;
+ IntSimpleMap::AddResult result = map.insert(1, nullptr);
+ EXPECT_TRUE(result.is_new_entry);
+ EXPECT_EQ(1, result.stored_value->key);
+ EXPECT_EQ(nullptr, result.stored_value->value.get());
+
+ SimpleClass* simple1 = new SimpleClass(1);
+ result.stored_value->value = base::WrapUnique(simple1);
+ EXPECT_EQ(simple1, map.at(1));
+
+ IntSimpleMap::AddResult result2 =
+ map.insert(1, std::make_unique<SimpleClass>(2));
+ EXPECT_FALSE(result2.is_new_entry);
+ EXPECT_EQ(1, result.stored_value->key);
+ EXPECT_EQ(1, result.stored_value->value->V());
+ EXPECT_EQ(1, map.at(1)->V());
+}
+
+TEST(HashMapTest, AddResultVectorValue) {
+ using IntVectorMap = HashMap<int, Vector<int>>;
+ IntVectorMap map;
+ IntVectorMap::AddResult result = map.insert(1, Vector<int>());
+ EXPECT_TRUE(result.is_new_entry);
+ EXPECT_EQ(1, result.stored_value->key);
+ EXPECT_EQ(0u, result.stored_value->value.size());
+
+ result.stored_value->value.push_back(11);
+ EXPECT_EQ(1u, map.find(1)->value.size());
+ EXPECT_EQ(11, map.find(1)->value.front());
+
+ IntVectorMap::AddResult result2 = map.insert(1, Vector<int>());
+ EXPECT_FALSE(result2.is_new_entry);
+ EXPECT_EQ(1, result.stored_value->key);
+ EXPECT_EQ(1u, result.stored_value->value.size());
+ EXPECT_EQ(11, result.stored_value->value.front());
+ EXPECT_EQ(11, map.find(1)->value.front());
+}
+
+class InstanceCounter {
+ public:
+ InstanceCounter() { ++counter_; }
+ InstanceCounter(const InstanceCounter& another) { ++counter_; }
+ ~InstanceCounter() { --counter_; }
+ static int counter_;
+};
+int InstanceCounter::counter_ = 0;
+
+TEST(HashMapTest, ValueTypeDestructed) {
+ InstanceCounter::counter_ = 0;
+ HashMap<int, InstanceCounter> map;
+ map.Set(1, InstanceCounter());
+ map.clear();
+ EXPECT_EQ(0, InstanceCounter::counter_);
+}
+
+TEST(HashMapTest, MoveOnlyValueType) {
+ using TheMap = HashMap<int, MoveOnlyHashValue>;
+ TheMap map;
+ {
+ TheMap::AddResult add_result = map.insert(1, MoveOnlyHashValue(10));
+ EXPECT_TRUE(add_result.is_new_entry);
+ EXPECT_EQ(1, add_result.stored_value->key);
+ EXPECT_EQ(10, add_result.stored_value->value.Value());
+ }
+ auto iter = map.find(1);
+ ASSERT_TRUE(iter != map.end());
+ EXPECT_EQ(1, iter->key);
+ EXPECT_EQ(10, iter->value.Value());
+
+ iter = map.find(2);
+ EXPECT_TRUE(iter == map.end());
+
+ // Try to add more to trigger rehashing.
+ for (int i = 2; i < 32; ++i) {
+ TheMap::AddResult add_result = map.insert(i, MoveOnlyHashValue(i * 10));
+ EXPECT_TRUE(add_result.is_new_entry);
+ EXPECT_EQ(i, add_result.stored_value->key);
+ EXPECT_EQ(i * 10, add_result.stored_value->value.Value());
+ }
+
+ iter = map.find(1);
+ ASSERT_TRUE(iter != map.end());
+ EXPECT_EQ(1, iter->key);
+ EXPECT_EQ(10, iter->value.Value());
+
+ iter = map.find(7);
+ ASSERT_TRUE(iter != map.end());
+ EXPECT_EQ(7, iter->key);
+ EXPECT_EQ(70, iter->value.Value());
+
+ {
+ TheMap::AddResult add_result = map.Set(9, MoveOnlyHashValue(999));
+ EXPECT_FALSE(add_result.is_new_entry);
+ EXPECT_EQ(9, add_result.stored_value->key);
+ EXPECT_EQ(999, add_result.stored_value->value.Value());
+ }
+
+ map.erase(11);
+ iter = map.find(11);
+ EXPECT_TRUE(iter == map.end());
+
+ MoveOnlyHashValue one_thirty(map.Take(13));
+ EXPECT_EQ(130, one_thirty.Value());
+ iter = map.find(13);
+ EXPECT_TRUE(iter == map.end());
+
+ map.clear();
+}
+
+TEST(HashMapTest, MoveOnlyKeyType) {
+ // The content of this test is similar to the test above, except that the
+ // types of key and value are swapped.
+ using TheMap = HashMap<MoveOnlyHashValue, int>;
+ TheMap map;
+ {
+ TheMap::AddResult add_result = map.insert(MoveOnlyHashValue(1), 10);
+ EXPECT_TRUE(add_result.is_new_entry);
+ EXPECT_EQ(1, add_result.stored_value->key.Value());
+ EXPECT_EQ(10, add_result.stored_value->value);
+ }
+ auto iter = map.find(MoveOnlyHashValue(1));
+ ASSERT_TRUE(iter != map.end());
+ EXPECT_EQ(1, iter->key.Value());
+ EXPECT_EQ(10, iter->value);
+
+ iter = map.find(MoveOnlyHashValue(2));
+ EXPECT_TRUE(iter == map.end());
+
+ for (int i = 2; i < 32; ++i) {
+ TheMap::AddResult add_result = map.insert(MoveOnlyHashValue(i), i * 10);
+ EXPECT_TRUE(add_result.is_new_entry);
+ EXPECT_EQ(i, add_result.stored_value->key.Value());
+ EXPECT_EQ(i * 10, add_result.stored_value->value);
+ }
+
+ iter = map.find(MoveOnlyHashValue(1));
+ ASSERT_TRUE(iter != map.end());
+ EXPECT_EQ(1, iter->key.Value());
+ EXPECT_EQ(10, iter->value);
+
+ iter = map.find(MoveOnlyHashValue(7));
+ ASSERT_TRUE(iter != map.end());
+ EXPECT_EQ(7, iter->key.Value());
+ EXPECT_EQ(70, iter->value);
+
+ {
+ TheMap::AddResult add_result = map.Set(MoveOnlyHashValue(9), 999);
+ EXPECT_FALSE(add_result.is_new_entry);
+ EXPECT_EQ(9, add_result.stored_value->key.Value());
+ EXPECT_EQ(999, add_result.stored_value->value);
+ }
+
+ map.erase(MoveOnlyHashValue(11));
+ iter = map.find(MoveOnlyHashValue(11));
+ EXPECT_TRUE(iter == map.end());
+
+ int one_thirty = map.Take(MoveOnlyHashValue(13));
+ EXPECT_EQ(130, one_thirty);
+ iter = map.find(MoveOnlyHashValue(13));
+ EXPECT_TRUE(iter == map.end());
+
+ map.clear();
+}
+
+TEST(HashMapTest, MoveShouldNotMakeCopy) {
+ HashMap<int, CountCopy> map;
+ int counter = 0;
+ map.insert(1, CountCopy(counter));
+
+ HashMap<int, CountCopy> other(map);
+ counter = 0;
+ map = std::move(other);
+ EXPECT_EQ(0, counter);
+
+ counter = 0;
+ HashMap<int, CountCopy> yet_another(std::move(map));
+ EXPECT_EQ(0, counter);
+}
+
+TEST(HashMapTest, UniquePtrAsKey) {
+ using Pointer = std::unique_ptr<int>;
+ using Map = HashMap<Pointer, int>;
+ Map map;
+ int* one_pointer = new int(1);
+ {
+ Map::AddResult add_result = map.insert(Pointer(one_pointer), 1);
+ EXPECT_TRUE(add_result.is_new_entry);
+ EXPECT_EQ(one_pointer, add_result.stored_value->key.get());
+ EXPECT_EQ(1, *add_result.stored_value->key);
+ EXPECT_EQ(1, add_result.stored_value->value);
+ }
+ auto iter = map.find(one_pointer);
+ ASSERT_TRUE(iter != map.end());
+ EXPECT_EQ(one_pointer, iter->key.get());
+ EXPECT_EQ(1, iter->value);
+
+ Pointer nonexistent(new int(42));
+ iter = map.find(nonexistent.get());
+ EXPECT_TRUE(iter == map.end());
+
+ // Insert more to cause a rehash.
+ for (int i = 2; i < 32; ++i) {
+ Map::AddResult add_result = map.insert(Pointer(new int(i)), i);
+ EXPECT_TRUE(add_result.is_new_entry);
+ EXPECT_EQ(i, *add_result.stored_value->key);
+ EXPECT_EQ(i, add_result.stored_value->value);
+ }
+
+ iter = map.find(one_pointer);
+ ASSERT_TRUE(iter != map.end());
+ EXPECT_EQ(one_pointer, iter->key.get());
+ EXPECT_EQ(1, iter->value);
+
+ EXPECT_EQ(1, map.Take(one_pointer));
+ // From now on, |onePointer| is a dangling pointer.
+
+ iter = map.find(one_pointer);
+ EXPECT_TRUE(iter == map.end());
+}
+
+TEST(HashMapTest, UniquePtrAsValue) {
+ using Pointer = std::unique_ptr<int>;
+ using Map = HashMap<int, Pointer>;
+ Map map;
+ {
+ Map::AddResult add_result = map.insert(1, Pointer(new int(1)));
+ EXPECT_TRUE(add_result.is_new_entry);
+ EXPECT_EQ(1, add_result.stored_value->key);
+ EXPECT_EQ(1, *add_result.stored_value->value);
+ }
+ auto iter = map.find(1);
+ ASSERT_TRUE(iter != map.end());
+ EXPECT_EQ(1, iter->key);
+ EXPECT_EQ(1, *iter->value);
+
+ int* one_pointer = map.at(1);
+ EXPECT_TRUE(one_pointer);
+ EXPECT_EQ(1, *one_pointer);
+
+ iter = map.find(42);
+ EXPECT_TRUE(iter == map.end());
+
+ for (int i = 2; i < 32; ++i) {
+ Map::AddResult add_result = map.insert(i, Pointer(new int(i)));
+ EXPECT_TRUE(add_result.is_new_entry);
+ EXPECT_EQ(i, add_result.stored_value->key);
+ EXPECT_EQ(i, *add_result.stored_value->value);
+ }
+
+ iter = map.find(1);
+ ASSERT_TRUE(iter != map.end());
+ EXPECT_EQ(1, iter->key);
+ EXPECT_EQ(1, *iter->value);
+
+ Pointer one(map.Take(1));
+ ASSERT_TRUE(one);
+ EXPECT_EQ(1, *one);
+
+ Pointer empty(map.Take(42));
+ EXPECT_TRUE(!empty);
+
+ iter = map.find(1);
+ EXPECT_TRUE(iter == map.end());
+
+ {
+ Map::AddResult add_result = map.insert(1, std::move(one));
+ EXPECT_TRUE(add_result.is_new_entry);
+ EXPECT_EQ(1, add_result.stored_value->key);
+ EXPECT_EQ(1, *add_result.stored_value->value);
+ }
+}
+
+TEST(HashMapTest, MoveOnlyPairKeyType) {
+ using Pair = std::pair<MoveOnlyHashValue, int>;
+ using TheMap = HashMap<Pair, int>;
+ TheMap map;
+ {
+ TheMap::AddResult add_result =
+ map.insert(Pair(MoveOnlyHashValue(1), -1), 10);
+ EXPECT_TRUE(add_result.is_new_entry);
+ EXPECT_EQ(1, add_result.stored_value->key.first.Value());
+ EXPECT_EQ(-1, add_result.stored_value->key.second);
+ EXPECT_EQ(10, add_result.stored_value->value);
+ }
+ auto iter = map.find(Pair(MoveOnlyHashValue(1), -1));
+ ASSERT_TRUE(iter != map.end());
+ EXPECT_EQ(1, iter->key.first.Value());
+ EXPECT_EQ(-1, iter->key.second);
+ EXPECT_EQ(10, iter->value);
+
+ iter = map.find(Pair(MoveOnlyHashValue(1), 0));
+ EXPECT_TRUE(iter == map.end());
+
+ for (int i = 2; i < 32; ++i) {
+ TheMap::AddResult add_result =
+ map.insert(Pair(MoveOnlyHashValue(i), -i), i * 10);
+ EXPECT_TRUE(add_result.is_new_entry);
+ EXPECT_EQ(i, add_result.stored_value->key.first.Value());
+ EXPECT_EQ(-i, add_result.stored_value->key.second);
+ EXPECT_EQ(i * 10, add_result.stored_value->value);
+ }
+
+ iter = map.find(Pair(MoveOnlyHashValue(1), -1));
+ ASSERT_TRUE(iter != map.end());
+ EXPECT_EQ(1, iter->key.first.Value());
+ EXPECT_EQ(-1, iter->key.second);
+ EXPECT_EQ(10, iter->value);
+
+ iter = map.find(Pair(MoveOnlyHashValue(7), -7));
+ ASSERT_TRUE(iter != map.end());
+ EXPECT_EQ(7, iter->key.first.Value());
+ EXPECT_EQ(-7, iter->key.second);
+ EXPECT_EQ(70, iter->value);
+
+ {
+ TheMap::AddResult add_result = map.Set(Pair(MoveOnlyHashValue(9), -9), 999);
+ EXPECT_FALSE(add_result.is_new_entry);
+ EXPECT_EQ(9, add_result.stored_value->key.first.Value());
+ EXPECT_EQ(-9, add_result.stored_value->key.second);
+ EXPECT_EQ(999, add_result.stored_value->value);
+ }
+
+ map.erase(Pair(MoveOnlyHashValue(11), -11));
+ iter = map.find(Pair(MoveOnlyHashValue(11), -11));
+ EXPECT_TRUE(iter == map.end());
+
+ int one_thirty = map.Take(Pair(MoveOnlyHashValue(13), -13));
+ EXPECT_EQ(130, one_thirty);
+ iter = map.find(Pair(MoveOnlyHashValue(13), -13));
+ EXPECT_TRUE(iter == map.end());
+
+ map.clear();
+}
+
+bool IsOneTwoThreeMap(const HashMap<int, int>& map) {
+ return map.size() == 3 && map.Contains(1) && map.Contains(2) &&
+ map.Contains(3) && map.at(1) == 11 && map.at(2) == 22 &&
+ map.at(3) == 33;
+};
+
+HashMap<int, int> ReturnOneTwoThreeMap() {
+ return {{1, 11}, {2, 22}, {3, 33}};
+};
+
+TEST(HashMapTest, InitializerList) {
+ HashMap<int, int> empty({});
+ EXPECT_TRUE(empty.IsEmpty());
+
+ HashMap<int, int> one({{1, 11}});
+ EXPECT_EQ(one.size(), 1u);
+ EXPECT_TRUE(one.Contains(1));
+ EXPECT_EQ(one.at(1), 11);
+
+ HashMap<int, int> one_two_three({{1, 11}, {2, 22}, {3, 33}});
+ EXPECT_EQ(one_two_three.size(), 3u);
+ EXPECT_TRUE(one_two_three.Contains(1));
+ EXPECT_TRUE(one_two_three.Contains(2));
+ EXPECT_TRUE(one_two_three.Contains(3));
+ EXPECT_EQ(one_two_three.at(1), 11);
+ EXPECT_EQ(one_two_three.at(2), 22);
+ EXPECT_EQ(one_two_three.at(3), 33);
+
+ // Put some jank so we can check if the assignments can clear them later.
+ empty.insert(9999, 99999);
+ one.insert(9999, 99999);
+ one_two_three.insert(9999, 99999);
+
+ empty = {};
+ EXPECT_TRUE(empty.IsEmpty());
+
+ one = {{1, 11}};
+ EXPECT_EQ(one.size(), 1u);
+ EXPECT_TRUE(one.Contains(1));
+ EXPECT_EQ(one.at(1), 11);
+
+ one_two_three = {{1, 11}, {2, 22}, {3, 33}};
+ EXPECT_EQ(one_two_three.size(), 3u);
+ EXPECT_TRUE(one_two_three.Contains(1));
+ EXPECT_TRUE(one_two_three.Contains(2));
+ EXPECT_TRUE(one_two_three.Contains(3));
+ EXPECT_EQ(one_two_three.at(1), 11);
+ EXPECT_EQ(one_two_three.at(2), 22);
+ EXPECT_EQ(one_two_three.at(3), 33);
+
+ // Other ways of construction: as a function parameter and in a return
+ // statement.
+ EXPECT_TRUE(IsOneTwoThreeMap({{1, 11}, {2, 22}, {3, 33}}));
+ EXPECT_TRUE(IsOneTwoThreeMap(ReturnOneTwoThreeMap()));
+}
+
+static_assert(!IsTraceable<HashMap<int, int>>::value,
+ "HashMap<int, int> must not be traceable.");
+
+} // anonymous namespace
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/hash_set.h b/chromium/third_party/blink/renderer/platform/wtf/hash_set.h
new file mode 100644
index 00000000000..01e3190cfc0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/hash_set.h
@@ -0,0 +1,331 @@
+/*
+ * Copyright (C) 2005, 2006, 2007, 2008, 2011 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_HASH_SET_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_HASH_SET_H_
+
+#include <initializer_list>
+#include "third_party/blink/renderer/platform/wtf/allocator/partition_allocator.h"
+#include "third_party/blink/renderer/platform/wtf/hash_table.h"
+
+namespace WTF {
+
+struct IdentityExtractor;
+
+// Note: empty or deleted values are not allowed, using them may lead to
+// undefined behavior. For pointer keys this means that null pointers are not
+// allowed; for integer keys 0 or -1 can't be used as a key. This restriction
+// can be lifted if you supply custom key traits.
+template <typename ValueArg,
+ typename HashArg = typename DefaultHash<ValueArg>::Hash,
+ typename TraitsArg = HashTraits<ValueArg>,
+ typename Allocator = PartitionAllocator>
+class HashSet {
+ USE_ALLOCATOR(HashSet, Allocator);
+
+ private:
+ typedef HashArg HashFunctions;
+ typedef TraitsArg ValueTraits;
+ typedef typename ValueTraits::PeekInType ValuePeekInType;
+
+ public:
+ typedef typename ValueTraits::TraitType ValueType;
+ using value_type = ValueType;
+
+ private:
+ typedef HashTable<ValueType,
+ ValueType,
+ IdentityExtractor,
+ HashFunctions,
+ ValueTraits,
+ ValueTraits,
+ Allocator>
+ HashTableType;
+
+ public:
+ typedef HashTableConstIteratorAdapter<HashTableType, ValueTraits> iterator;
+ typedef HashTableConstIteratorAdapter<HashTableType, ValueTraits>
+ const_iterator;
+ typedef typename HashTableType::AddResult AddResult;
+
+ HashSet() {
+ static_assert(Allocator::kIsGarbageCollected ||
+ !IsPointerToGarbageCollectedType<ValueArg>::value,
+ "Cannot put raw pointers to garbage-collected classes into "
+ "an off-heap HashSet. Use HeapHashSet<Member<T>> instead.");
+ }
+ HashSet(const HashSet&) = default;
+ HashSet& operator=(const HashSet&) = default;
+ HashSet(HashSet&&) = default;
+ HashSet& operator=(HashSet&&) = default;
+
+ HashSet(std::initializer_list<ValueType> elements);
+ HashSet& operator=(std::initializer_list<ValueType> elements);
+
+ void swap(HashSet& ref) { impl_.swap(ref.impl_); }
+
+ unsigned size() const;
+ unsigned Capacity() const;
+ bool IsEmpty() const;
+
+ void ReserveCapacityForSize(unsigned size) {
+ impl_.ReserveCapacityForSize(size);
+ }
+
+ iterator begin() const;
+ iterator end() const;
+
+ iterator find(ValuePeekInType) const;
+ bool Contains(ValuePeekInType) const;
+
+ // An alternate version of find() that finds the object by hashing and
+ // comparing with some other type, to avoid the cost of type
+ // conversion. HashTranslator must have the following function members:
+ // static unsigned hash(const T&);
+ // static bool equal(const ValueType&, const T&);
+ template <typename HashTranslator, typename T>
+ iterator Find(const T&) const;
+ template <typename HashTranslator, typename T>
+ bool Contains(const T&) const;
+
+ // The return value is a pair of an iterator to the new value's location,
+ // and a bool that is true if an new entry was added.
+ template <typename IncomingValueType>
+ AddResult insert(IncomingValueType&&);
+
+ // An alternate version of add() that finds the object by hashing and
+ // comparing with some other type, to avoid the cost of type conversion if
+ // the object is already in the table. HashTranslator must have the
+ // following function members:
+ // static unsigned hash(const T&);
+ // static bool equal(const ValueType&, const T&);
+ // static translate(ValueType&, T&&, unsigned hashCode);
+ template <typename HashTranslator, typename T>
+ AddResult AddWithTranslator(T&&);
+
+ void erase(ValuePeekInType);
+ void erase(iterator);
+ void clear();
+ template <typename Collection>
+ void RemoveAll(const Collection& to_be_removed) {
+ WTF::RemoveAll(*this, to_be_removed);
+ }
+
+ ValueType Take(iterator);
+ ValueType Take(ValuePeekInType);
+ ValueType TakeAny();
+
+ template <typename VisitorDispatcher, typename A = Allocator>
+ std::enable_if_t<A::kIsGarbageCollected> Trace(VisitorDispatcher visitor) {
+ impl_.Trace(visitor);
+ }
+
+ private:
+ HashTableType impl_;
+};
+
+struct IdentityExtractor {
+ STATIC_ONLY(IdentityExtractor);
+ template <typename T>
+ static const T& Extract(const T& t) {
+ return t;
+ }
+};
+
+template <typename Translator>
+struct HashSetTranslatorAdapter {
+ STATIC_ONLY(HashSetTranslatorAdapter);
+ template <typename T>
+ static unsigned GetHash(const T& key) {
+ return Translator::GetHash(key);
+ }
+ template <typename T, typename U>
+ static bool Equal(const T& a, const U& b) {
+ return Translator::Equal(a, b);
+ }
+ template <typename T, typename U, typename V>
+ static void Translate(T& location, U&& key, const V&, unsigned hash_code) {
+ Translator::Translate(location, std::forward<U>(key), hash_code);
+ }
+};
+
+template <typename Value,
+ typename HashFunctions,
+ typename Traits,
+ typename Allocator>
+HashSet<Value, HashFunctions, Traits, Allocator>::HashSet(
+ std::initializer_list<ValueType> elements) {
+ if (elements.size())
+ impl_.ReserveCapacityForSize(elements.size());
+ for (const ValueType& element : elements)
+ insert(element);
+}
+
+template <typename Value,
+ typename HashFunctions,
+ typename Traits,
+ typename Allocator>
+auto HashSet<Value, HashFunctions, Traits, Allocator>::operator=(
+ std::initializer_list<ValueType> elements) -> HashSet& {
+ *this = HashSet(std::move(elements));
+ return *this;
+}
+
+template <typename T, typename U, typename V, typename W>
+inline unsigned HashSet<T, U, V, W>::size() const {
+ return impl_.size();
+}
+
+template <typename T, typename U, typename V, typename W>
+inline unsigned HashSet<T, U, V, W>::Capacity() const {
+ return impl_.Capacity();
+}
+
+template <typename T, typename U, typename V, typename W>
+inline bool HashSet<T, U, V, W>::IsEmpty() const {
+ return impl_.IsEmpty();
+}
+
+template <typename T, typename U, typename V, typename W>
+inline typename HashSet<T, U, V, W>::iterator HashSet<T, U, V, W>::begin()
+ const {
+ return impl_.begin();
+}
+
+template <typename T, typename U, typename V, typename W>
+inline typename HashSet<T, U, V, W>::iterator HashSet<T, U, V, W>::end() const {
+ return impl_.end();
+}
+
+template <typename T, typename U, typename V, typename W>
+inline typename HashSet<T, U, V, W>::iterator HashSet<T, U, V, W>::find(
+ ValuePeekInType value) const {
+ return impl_.find(value);
+}
+
+template <typename Value,
+ typename HashFunctions,
+ typename Traits,
+ typename Allocator>
+inline bool HashSet<Value, HashFunctions, Traits, Allocator>::Contains(
+ ValuePeekInType value) const {
+ return impl_.Contains(value);
+}
+
+template <typename Value,
+ typename HashFunctions,
+ typename Traits,
+ typename Allocator>
+template <typename HashTranslator, typename T>
+typename HashSet<Value, HashFunctions, Traits, Allocator>::
+ iterator inline HashSet<Value, HashFunctions, Traits, Allocator>::Find(
+ const T& value) const {
+ return impl_.template Find<HashSetTranslatorAdapter<HashTranslator>>(value);
+}
+
+template <typename Value,
+ typename HashFunctions,
+ typename Traits,
+ typename Allocator>
+template <typename HashTranslator, typename T>
+inline bool HashSet<Value, HashFunctions, Traits, Allocator>::Contains(
+ const T& value) const {
+ return impl_.template Contains<HashSetTranslatorAdapter<HashTranslator>>(
+ value);
+}
+
+template <typename T, typename U, typename V, typename W>
+template <typename IncomingValueType>
+inline typename HashSet<T, U, V, W>::AddResult HashSet<T, U, V, W>::insert(
+ IncomingValueType&& value) {
+ return impl_.insert(std::forward<IncomingValueType>(value));
+}
+
+template <typename Value,
+ typename HashFunctions,
+ typename Traits,
+ typename Allocator>
+template <typename HashTranslator, typename T>
+inline typename HashSet<Value, HashFunctions, Traits, Allocator>::AddResult
+HashSet<Value, HashFunctions, Traits, Allocator>::AddWithTranslator(T&& value) {
+ // Forward only the first argument, because the second argument isn't actually
+ // used in HashSetTranslatorAdapter.
+ return impl_
+ .template InsertPassingHashCode<HashSetTranslatorAdapter<HashTranslator>>(
+ std::forward<T>(value), value);
+}
+
+template <typename T, typename U, typename V, typename W>
+inline void HashSet<T, U, V, W>::erase(iterator it) {
+ impl_.erase(it.impl_);
+}
+
+template <typename T, typename U, typename V, typename W>
+inline void HashSet<T, U, V, W>::erase(ValuePeekInType value) {
+ erase(find(value));
+}
+
+template <typename T, typename U, typename V, typename W>
+inline void HashSet<T, U, V, W>::clear() {
+ impl_.clear();
+}
+
+template <typename T, typename U, typename V, typename W>
+inline auto HashSet<T, U, V, W>::Take(iterator it) -> ValueType {
+ if (it == end())
+ return ValueTraits::EmptyValue();
+
+ ValueType result = std::move(const_cast<ValueType&>(*it));
+ erase(it);
+
+ return result;
+}
+
+template <typename T, typename U, typename V, typename W>
+inline auto HashSet<T, U, V, W>::Take(ValuePeekInType value) -> ValueType {
+ return Take(find(value));
+}
+
+template <typename T, typename U, typename V, typename W>
+inline auto HashSet<T, U, V, W>::TakeAny() -> ValueType {
+ return Take(begin());
+}
+
+template <typename C, typename W>
+inline void CopyToVector(const C& collection, W& vector) {
+ typedef typename C::const_iterator iterator;
+
+ {
+ // Disallow GC across resize allocation, see crbug.com/568173
+ typename W::GCForbiddenScope scope;
+ vector.resize(collection.size());
+ }
+
+ iterator it = collection.begin();
+ iterator end = collection.end();
+ for (unsigned i = 0; it != end; ++it, ++i)
+ vector[i] = *it;
+}
+
+} // namespace WTF
+
+using WTF::HashSet;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_HASH_SET_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/hash_set_test.cc b/chromium/third_party/blink/renderer/platform/wtf/hash_set_test.cc
new file mode 100644
index 00000000000..98468aa16c1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/hash_set_test.cc
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2012 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/wtf/hash_set.h"
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_test_helper.h"
+
+namespace WTF {
+
+int* const CountCopy::kDeletedValue =
+ reinterpret_cast<int*>(static_cast<uintptr_t>(-1));
+
+namespace {
+
+template <unsigned size>
+void TestReserveCapacity();
+template <>
+void TestReserveCapacity<0>() {}
+template <unsigned size>
+void TestReserveCapacity() {
+ HashSet<int> test_set;
+
+ // Initial capacity is zero.
+ EXPECT_EQ(0UL, test_set.Capacity());
+
+ test_set.ReserveCapacityForSize(size);
+ const unsigned initial_capacity = test_set.Capacity();
+ const unsigned kMinimumTableSize = HashTraits<int>::kMinimumTableSize;
+
+ // reserveCapacityForSize should respect minimumTableSize.
+ EXPECT_GE(initial_capacity, kMinimumTableSize);
+
+ // Adding items up to size should never change the capacity.
+ for (size_t i = 0; i < size; ++i) {
+ test_set.insert(i + 1); // Avoid adding '0'.
+ EXPECT_EQ(initial_capacity, test_set.Capacity());
+ }
+
+ // Adding items up to less than half the capacity should not change the
+ // capacity.
+ unsigned capacity_limit = initial_capacity / 2 - 1;
+ for (size_t i = size; i < capacity_limit; ++i) {
+ test_set.insert(i + 1);
+ EXPECT_EQ(initial_capacity, test_set.Capacity());
+ }
+
+ // Adding one more item increases the capacity.
+ test_set.insert(capacity_limit + 1);
+ EXPECT_GT(test_set.Capacity(), initial_capacity);
+
+ TestReserveCapacity<size - 1>();
+}
+
+TEST(HashSetTest, ReserveCapacity) {
+ TestReserveCapacity<128>();
+}
+
+TEST(HashSetTest, HashSetOwnPtr) {
+ bool deleted1 = false, deleted2 = false;
+
+ typedef HashSet<std::unique_ptr<Dummy>> OwnPtrSet;
+ OwnPtrSet set;
+
+ Dummy* ptr1 = new Dummy(deleted1);
+ {
+ // AddResult in a separate scope to avoid assertion hit,
+ // since we modify the container further.
+ HashSet<std::unique_ptr<Dummy>>::AddResult res1 =
+ set.insert(base::WrapUnique(ptr1));
+ EXPECT_EQ(ptr1, res1.stored_value->get());
+ }
+
+ EXPECT_FALSE(deleted1);
+ EXPECT_EQ(1UL, set.size());
+ OwnPtrSet::iterator it1 = set.find(ptr1);
+ EXPECT_NE(set.end(), it1);
+ EXPECT_EQ(ptr1, (*it1).get());
+
+ Dummy* ptr2 = new Dummy(deleted2);
+ {
+ HashSet<std::unique_ptr<Dummy>>::AddResult res2 =
+ set.insert(base::WrapUnique(ptr2));
+ EXPECT_EQ(res2.stored_value->get(), ptr2);
+ }
+
+ EXPECT_FALSE(deleted2);
+ EXPECT_EQ(2UL, set.size());
+ OwnPtrSet::iterator it2 = set.find(ptr2);
+ EXPECT_NE(set.end(), it2);
+ EXPECT_EQ(ptr2, (*it2).get());
+
+ set.erase(ptr1);
+ EXPECT_TRUE(deleted1);
+
+ set.clear();
+ EXPECT_TRUE(deleted2);
+ EXPECT_TRUE(set.IsEmpty());
+
+ deleted1 = false;
+ deleted2 = false;
+ {
+ OwnPtrSet set;
+ set.insert(std::make_unique<Dummy>(deleted1));
+ set.insert(std::make_unique<Dummy>(deleted2));
+ }
+ EXPECT_TRUE(deleted1);
+ EXPECT_TRUE(deleted2);
+
+ deleted1 = false;
+ deleted2 = false;
+ std::unique_ptr<Dummy> own_ptr1;
+ std::unique_ptr<Dummy> own_ptr2;
+ ptr1 = new Dummy(deleted1);
+ ptr2 = new Dummy(deleted2);
+ {
+ OwnPtrSet set;
+ set.insert(base::WrapUnique(ptr1));
+ set.insert(base::WrapUnique(ptr2));
+ own_ptr1 = set.Take(ptr1);
+ EXPECT_EQ(1UL, set.size());
+ own_ptr2 = set.TakeAny();
+ EXPECT_TRUE(set.IsEmpty());
+ }
+ EXPECT_FALSE(deleted1);
+ EXPECT_FALSE(deleted2);
+
+ EXPECT_EQ(ptr1, own_ptr1.get());
+ EXPECT_EQ(ptr2, own_ptr2.get());
+}
+
+TEST(HashSetTest, HashSetRefPtr) {
+ bool is_deleted = false;
+ scoped_refptr<DummyRefCounted> ptr =
+ base::AdoptRef(new DummyRefCounted(is_deleted));
+ EXPECT_EQ(0, DummyRefCounted::ref_invokes_count_);
+ HashSet<scoped_refptr<DummyRefCounted>> set;
+ set.insert(ptr);
+ // Referenced only once (to store a copy in the container).
+ EXPECT_EQ(1, DummyRefCounted::ref_invokes_count_);
+
+ DummyRefCounted* raw_ptr = ptr.get();
+
+ EXPECT_TRUE(set.Contains(raw_ptr));
+ EXPECT_NE(set.end(), set.find(raw_ptr));
+ EXPECT_TRUE(set.Contains(ptr));
+ EXPECT_NE(set.end(), set.find(ptr));
+
+ ptr = nullptr;
+ EXPECT_FALSE(is_deleted);
+
+ set.erase(raw_ptr);
+ EXPECT_TRUE(is_deleted);
+ EXPECT_TRUE(set.IsEmpty());
+ EXPECT_EQ(1, DummyRefCounted::ref_invokes_count_);
+}
+
+TEST(HashSetTest, MoveShouldNotMakeCopy) {
+ HashSet<CountCopy> set;
+ int counter = 0;
+ set.insert(CountCopy(&counter));
+
+ HashSet<CountCopy> other(set);
+ counter = 0;
+ set = std::move(other);
+ EXPECT_EQ(0, counter);
+
+ counter = 0;
+ HashSet<CountCopy> yet_another(std::move(set));
+ EXPECT_EQ(0, counter);
+}
+
+TEST(HashSetTest, MoveOnlyValue) {
+ using TheSet = HashSet<MoveOnlyHashValue>;
+ TheSet set;
+ {
+ TheSet::AddResult add_result = set.insert(MoveOnlyHashValue(1, 1));
+ EXPECT_TRUE(add_result.is_new_entry);
+ EXPECT_EQ(1, add_result.stored_value->Value());
+ EXPECT_EQ(1, add_result.stored_value->Id());
+ }
+ auto iter = set.find(MoveOnlyHashValue(1));
+ ASSERT_TRUE(iter != set.end());
+ EXPECT_EQ(1, iter->Value());
+
+ iter = set.find(MoveOnlyHashValue(2));
+ EXPECT_TRUE(iter == set.end());
+
+ for (int i = 2; i < 32; ++i) {
+ TheSet::AddResult add_result = set.insert(MoveOnlyHashValue(i, i));
+ EXPECT_TRUE(add_result.is_new_entry);
+ EXPECT_EQ(i, add_result.stored_value->Value());
+ EXPECT_EQ(i, add_result.stored_value->Id());
+ }
+
+ iter = set.find(MoveOnlyHashValue(1));
+ ASSERT_TRUE(iter != set.end());
+ EXPECT_EQ(1, iter->Value());
+ EXPECT_EQ(1, iter->Id());
+
+ iter = set.find(MoveOnlyHashValue(7));
+ ASSERT_TRUE(iter != set.end());
+ EXPECT_EQ(7, iter->Value());
+ EXPECT_EQ(7, iter->Id());
+
+ {
+ TheSet::AddResult add_result = set.insert(
+ MoveOnlyHashValue(7, 777)); // With different ID for identification.
+ EXPECT_FALSE(add_result.is_new_entry);
+ EXPECT_EQ(7, add_result.stored_value->Value());
+ EXPECT_EQ(7, add_result.stored_value->Id());
+ }
+
+ set.erase(MoveOnlyHashValue(11));
+ iter = set.find(MoveOnlyHashValue(11));
+ EXPECT_TRUE(iter == set.end());
+
+ MoveOnlyHashValue thirteen(set.Take(MoveOnlyHashValue(13)));
+ EXPECT_EQ(13, thirteen.Value());
+ EXPECT_EQ(13, thirteen.Id());
+ iter = set.find(MoveOnlyHashValue(13));
+ EXPECT_TRUE(iter == set.end());
+
+ set.clear();
+}
+
+TEST(HashSetTest, UniquePtr) {
+ using Pointer = std::unique_ptr<int>;
+ using Set = HashSet<Pointer>;
+ Set set;
+ int* one_pointer = new int(1);
+ {
+ Set::AddResult add_result = set.insert(Pointer(one_pointer));
+ EXPECT_TRUE(add_result.is_new_entry);
+ EXPECT_EQ(one_pointer, add_result.stored_value->get());
+ EXPECT_EQ(1, **add_result.stored_value);
+ }
+ auto iter = set.find(one_pointer);
+ ASSERT_TRUE(iter != set.end());
+ EXPECT_EQ(one_pointer, iter->get());
+
+ Pointer nonexistent(new int(42));
+ iter = set.find(nonexistent.get());
+ EXPECT_TRUE(iter == set.end());
+
+ // Insert more to cause a rehash.
+ for (int i = 2; i < 32; ++i) {
+ Set::AddResult add_result = set.insert(Pointer(new int(i)));
+ EXPECT_TRUE(add_result.is_new_entry);
+ EXPECT_EQ(i, **add_result.stored_value);
+ }
+
+ iter = set.find(one_pointer);
+ ASSERT_TRUE(iter != set.end());
+ EXPECT_EQ(one_pointer, iter->get());
+
+ Pointer one(set.Take(one_pointer));
+ ASSERT_TRUE(one);
+ EXPECT_EQ(one_pointer, one.get());
+
+ Pointer empty(set.Take(nonexistent.get()));
+ EXPECT_TRUE(!empty);
+
+ iter = set.find(one_pointer);
+ EXPECT_TRUE(iter == set.end());
+
+ // Re-insert to the deleted slot.
+ {
+ Set::AddResult add_result = set.insert(std::move(one));
+ EXPECT_TRUE(add_result.is_new_entry);
+ EXPECT_EQ(one_pointer, add_result.stored_value->get());
+ EXPECT_EQ(1, **add_result.stored_value);
+ }
+}
+
+bool IsOneTwoThreeSet(const HashSet<int>& set) {
+ return set.size() == 3 && set.Contains(1) && set.Contains(2) &&
+ set.Contains(3);
+}
+
+HashSet<int> ReturnOneTwoThreeSet() {
+ return {1, 2, 3};
+}
+
+TEST(HashSetTest, InitializerList) {
+ HashSet<int> empty({});
+ EXPECT_TRUE(empty.IsEmpty());
+
+ HashSet<int> one({1});
+ EXPECT_EQ(1u, one.size());
+ EXPECT_TRUE(one.Contains(1));
+
+ HashSet<int> one_two_three({1, 2, 3});
+ EXPECT_EQ(3u, one_two_three.size());
+ EXPECT_TRUE(one_two_three.Contains(1));
+ EXPECT_TRUE(one_two_three.Contains(2));
+ EXPECT_TRUE(one_two_three.Contains(3));
+
+ // Put some jank so we can check if the assignments later can clear them.
+ empty.insert(9999);
+ one.insert(9999);
+ one_two_three.insert(9999);
+
+ empty = {};
+ EXPECT_TRUE(empty.IsEmpty());
+
+ one = {1};
+ EXPECT_EQ(1u, one.size());
+ EXPECT_TRUE(one.Contains(1));
+
+ one_two_three = {1, 2, 3};
+ EXPECT_EQ(3u, one_two_three.size());
+ EXPECT_TRUE(one_two_three.Contains(1));
+ EXPECT_TRUE(one_two_three.Contains(2));
+ EXPECT_TRUE(one_two_three.Contains(3));
+
+ one_two_three = {3, 1, 1, 2, 1, 1, 3};
+ EXPECT_EQ(3u, one_two_three.size());
+ EXPECT_TRUE(one_two_three.Contains(1));
+ EXPECT_TRUE(one_two_three.Contains(2));
+ EXPECT_TRUE(one_two_three.Contains(3));
+
+ // Other ways of construction: as a function parameter and in a return
+ // statement.
+ EXPECT_TRUE(IsOneTwoThreeSet({1, 2, 3}));
+ EXPECT_TRUE(IsOneTwoThreeSet(ReturnOneTwoThreeSet()));
+}
+
+enum TestEnum {
+ kItem0,
+};
+
+enum class TestEnumClass : unsigned char {
+ kItem0,
+};
+
+TEST(HashSetTest, HasTraitsForEnum) {
+ // Ensure that enum hash keys are buildable.
+ HashSet<TestEnum> set1;
+ HashSet<TestEnumClass> set2;
+ HashSet<std::pair<TestEnum, TestEnumClass>> set3;
+}
+
+static_assert(!IsTraceable<HashSet<int>>::value,
+ "HashSet<int, int> must not be traceable.");
+
+} // anonymous namespace
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/hash_table.cc b/chromium/third_party/blink/renderer/platform/wtf/hash_table.cc
new file mode 100644
index 00000000000..c191d951ba1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/hash_table.cc
@@ -0,0 +1,102 @@
+/*
+ Copyright (C) 2005 Apple Inc. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "third_party/blink/renderer/platform/wtf/hash_table.h"
+
+#if DUMP_HASHTABLE_STATS || DUMP_HASHTABLE_STATS_PER_TABLE
+
+#include <iomanip>
+#include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
+
+namespace WTF {
+
+static Mutex& hashTableStatsMutex() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(Mutex, mutex, ());
+ return mutex;
+}
+
+HashTableStats& HashTableStats::instance() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(HashTableStats, stats, ());
+ return stats;
+}
+
+void HashTableStats::copy(const HashTableStats* other) {
+ numAccesses = other->numAccesses;
+ numRehashes = other->numRehashes;
+ numRemoves = other->numRemoves;
+ numReinserts = other->numReinserts;
+
+ maxCollisions = other->maxCollisions;
+ numCollisions = other->numCollisions;
+ memcpy(collisionGraph, other->collisionGraph, sizeof(collisionGraph));
+}
+
+void HashTableStats::recordCollisionAtCount(int count) {
+ // The global hash table singleton needs to be atomically updated.
+ if (this == &instance()) {
+ MutexLocker locker(hashTableStatsMutex());
+ RecordCollisionAtCountWithoutLock(count);
+ } else {
+ RecordCollisionAtCountWithoutLock(count);
+ }
+}
+
+void HashTableStats::RecordCollisionAtCountWithoutLock(int count) {
+ if (count > maxCollisions)
+ maxCollisions = count;
+ numCollisions++;
+ collisionGraph[count]++;
+}
+
+void HashTableStats::DumpStats() {
+ // Lock the global hash table singleton while dumping.
+ if (this == &instance()) {
+ MutexLocker locker(hashTableStatsMutex());
+ DumpStatsWithoutLock();
+ } else {
+ DumpStatsWithoutLock();
+ }
+}
+
+void HashTableStats::DumpStatsWithoutLock() {
+ std::stringstream collision_str;
+ collision_str << std::fixed << std::setprecision(2);
+ for (int i = 1; i <= maxCollisions; i++) {
+ collision_str << " " << collisionGraph[i] << " lookups with exactly "
+ << i << " collisions ("
+ << (100.0 * (collisionGraph[i] - collisionGraph[i + 1]) /
+ numAccesses)
+ << "% , " << (100.0 * collisionGraph[i] / numAccesses)
+ << "% with this many or more)\n";
+ }
+
+ DLOG(INFO) << std::fixed << std::setprecision(2)
+ << "WTF::HashTable statistics:\n"
+ << " " << numAccesses << " accesses\n"
+ << " " << numCollisions << " total collisions, average "
+ << (1.0 * (numAccesses + numCollisions) / numAccesses)
+ << " probes per access\n"
+ << " longest collision chain: " << maxCollisions << "\n"
+ << collision_str.str() << " " << numRehashes << " rehashes\n"
+ << " " << numReinserts << " reinserts";
+}
+
+} // namespace WTF
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/wtf/hash_table.h b/chromium/third_party/blink/renderer/platform/wtf/hash_table.h
new file mode 100644
index 00000000000..a43fd8a2c23
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/hash_table.h
@@ -0,0 +1,2266 @@
+/*
+ * Copyright (C) 2005, 2006, 2007, 2008, 2011, 2012 Apple Inc. All rights
+ * reserved.
+ * Copyright (C) 2008 David Levin <levin@chromium.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_HASH_TABLE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_HASH_TABLE_H_
+
+#include <memory>
+
+#include "third_party/blink/renderer/platform/wtf/alignment.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/partition_allocator.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/conditional_destructor.h"
+#include "third_party/blink/renderer/platform/wtf/construct_traits.h"
+#include "third_party/blink/renderer/platform/wtf/hash_traits.h"
+
+#if !defined(DUMP_HASHTABLE_STATS)
+#define DUMP_HASHTABLE_STATS 0
+#endif
+
+#if !defined(DUMP_HASHTABLE_STATS_PER_TABLE)
+#define DUMP_HASHTABLE_STATS_PER_TABLE 0
+#endif
+
+#if DUMP_HASHTABLE_STATS
+#include "third_party/blink/renderer/platform/wtf/atomics.h"
+#include "third_party/blink/renderer/platform/wtf/threading.h"
+#endif
+
+#if DUMP_HASHTABLE_STATS_PER_TABLE
+#include <type_traits>
+#include "third_party/blink/renderer/platform/wtf/DataLog.h"
+#endif
+
+#if DUMP_HASHTABLE_STATS
+#if DUMP_HASHTABLE_STATS_PER_TABLE
+
+#define UPDATE_PROBE_COUNTS() \
+ ++probeCount; \
+ HashTableStats::instance().recordCollisionAtCount(probeCount); \
+ ++perTableProbeCount; \
+ stats_->recordCollisionAtCount(perTableProbeCount)
+#define UPDATE_ACCESS_COUNTS() \
+ AtomicIncrement(&HashTableStats::instance().numAccesses); \
+ int probeCount = 0; \
+ ++stats_->numAccesses; \
+ int perTableProbeCount = 0
+#else
+#define UPDATE_PROBE_COUNTS() \
+ ++probeCount; \
+ HashTableStats::instance().recordCollisionAtCount(probeCount)
+#define UPDATE_ACCESS_COUNTS() \
+ AtomicIncrement(&HashTableStats::instance().numAccesses); \
+ int probeCount = 0
+#endif
+#else
+#if DUMP_HASHTABLE_STATS_PER_TABLE
+#define UPDATE_PROBE_COUNTS() \
+ ++perTableProbeCount; \
+ stats_->recordCollisionAtCount(perTableProbeCount)
+#define UPDATE_ACCESS_COUNTS() \
+ ++stats_->numAccesses; \
+ int perTableProbeCount = 0
+#else
+#define UPDATE_PROBE_COUNTS() \
+ do { \
+ } while (0)
+#define UPDATE_ACCESS_COUNTS() \
+ do { \
+ } while (0)
+#endif
+#endif
+
+namespace WTF {
+
+// This is for tracing inside collections that have special support for weak
+// pointers.
+//
+// Structure:
+// - |Trace|: Traces the contents and returns true if there are still unmarked
+// objects left to process
+//
+// Default implementation for non-weak types is to use the regular non-weak
+// TraceTrait. Default implementation for types with weakness is to
+// call |TraceInCollection| on the type's trait.
+template <WeakHandlingFlag weakness, typename T, typename Traits>
+struct TraceInCollectionTrait;
+
+#if DUMP_HASHTABLE_STATS || DUMP_HASHTABLE_STATS_PER_TABLE
+struct WTF_EXPORT HashTableStats {
+ HashTableStats()
+ : numAccesses(0),
+ numRehashes(0),
+ numRemoves(0),
+ numReinserts(0),
+ maxCollisions(0),
+ numCollisions(0),
+ collisionGraph() {}
+
+ // The following variables are all atomically incremented when modified.
+ int numAccesses;
+ int numRehashes;
+ int numRemoves;
+ int numReinserts;
+
+ // The following variables are only modified in the recordCollisionAtCount
+ // method within a mutex.
+ int maxCollisions;
+ int numCollisions;
+ int collisionGraph[4096];
+
+ void copy(const HashTableStats* other);
+ void recordCollisionAtCount(int count);
+ void DumpStats();
+
+ static HashTableStats& instance();
+
+ template <typename VisitorDispatcher>
+ void trace(VisitorDispatcher) {}
+
+ private:
+ void RecordCollisionAtCountWithoutLock(int count);
+ void DumpStatsWithoutLock();
+};
+
+#if DUMP_HASHTABLE_STATS_PER_TABLE
+template <typename Allocator, bool isGCType = Allocator::kIsGarbageCollected>
+class HashTableStatsPtr;
+
+template <typename Allocator>
+class HashTableStatsPtr<Allocator, false> final {
+ STATIC_ONLY(HashTableStatsPtr);
+
+ public:
+ static std::unique_ptr<HashTableStats> Create() {
+ return std::make_unique<HashTableStats>();
+ }
+
+ static std::unique_ptr<HashTableStats> copy(
+ const std::unique_ptr<HashTableStats>& other) {
+ if (!other)
+ return nullptr;
+ return std::make_unique<HashTableStats>(*other);
+ }
+
+ static void swap(std::unique_ptr<HashTableStats>& stats,
+ std::unique_ptr<HashTableStats>& other) {
+ stats.swap(other);
+ }
+};
+
+template <typename Allocator>
+class HashTableStatsPtr<Allocator, true> final {
+ STATIC_ONLY(HashTableStatsPtr);
+
+ public:
+ static HashTableStats* Create() {
+ // TODO(cavalcantii): fix this.
+ return new HashTableStats;
+ }
+
+ static HashTableStats* copy(const HashTableStats* other) {
+ if (!other)
+ return nullptr;
+ HashTableStats* obj = Create();
+ obj->copy(other);
+ return obj;
+ }
+
+ static void swap(HashTableStats*& stats, HashTableStats*& other) {
+ std::swap(stats, other);
+ }
+};
+#endif
+#endif
+
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+class HashTable;
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+class HashTableIterator;
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+class HashTableConstIterator;
+template <typename Value,
+ typename HashFunctions,
+ typename HashTraits,
+ typename Allocator>
+class LinkedHashSet;
+template <WeakHandlingFlag x,
+ typename T,
+ typename U,
+ typename V,
+ typename W,
+ typename X,
+ typename Y,
+ typename Z>
+struct WeakProcessingHashTableHelper;
+
+typedef enum { kHashItemKnownGood } HashItemKnownGoodTag;
+
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+class HashTableConstIterator final {
+ DISALLOW_NEW();
+
+ private:
+ typedef HashTable<Key,
+ Value,
+ Extractor,
+ HashFunctions,
+ Traits,
+ KeyTraits,
+ Allocator>
+ HashTableType;
+ typedef HashTableIterator<Key,
+ Value,
+ Extractor,
+ HashFunctions,
+ Traits,
+ KeyTraits,
+ Allocator>
+ iterator;
+ typedef HashTableConstIterator<Key,
+ Value,
+ Extractor,
+ HashFunctions,
+ Traits,
+ KeyTraits,
+ Allocator>
+ const_iterator;
+ typedef Value ValueType;
+ using value_type = ValueType;
+ typedef typename Traits::IteratorConstGetType GetType;
+ typedef const ValueType* PointerType;
+
+ friend class HashTable<Key,
+ Value,
+ Extractor,
+ HashFunctions,
+ Traits,
+ KeyTraits,
+ Allocator>;
+ friend class HashTableIterator<Key,
+ Value,
+ Extractor,
+ HashFunctions,
+ Traits,
+ KeyTraits,
+ Allocator>;
+
+ void SkipEmptyBuckets() {
+ while (position_ != end_position_ &&
+ HashTableType::IsEmptyOrDeletedBucket(*position_))
+ ++position_;
+ }
+
+ HashTableConstIterator(PointerType position,
+ PointerType end_position,
+ const HashTableType* container)
+ : position_(position),
+ end_position_(end_position)
+#if DCHECK_IS_ON()
+ ,
+ container_(container),
+ container_modifications_(container->Modifications())
+#endif
+ {
+ SkipEmptyBuckets();
+ }
+
+ HashTableConstIterator(PointerType position,
+ PointerType end_position,
+ const HashTableType* container,
+ HashItemKnownGoodTag)
+ : position_(position),
+ end_position_(end_position)
+#if DCHECK_IS_ON()
+ ,
+ container_(container),
+ container_modifications_(container->Modifications())
+#endif
+ {
+#if DCHECK_IS_ON()
+ DCHECK_EQ(container_modifications_, container_->Modifications());
+#endif
+ }
+
+ void CheckModifications() const {
+#if DCHECK_IS_ON()
+ // HashTable and collections that build on it do not support
+ // modifications while there is an iterator in use. The exception is
+ // ListHashSet, which has its own iterators that tolerate modification
+ // of the underlying set.
+ DCHECK_EQ(container_modifications_, container_->Modifications());
+ DCHECK(!container_->AccessForbidden());
+#endif
+ }
+
+ public:
+ HashTableConstIterator() = default;
+
+ GetType Get() const {
+ CheckModifications();
+ return position_;
+ }
+ typename Traits::IteratorConstReferenceType operator*() const {
+ return Traits::GetToReferenceConstConversion(Get());
+ }
+ GetType operator->() const { return Get(); }
+
+ const_iterator& operator++() {
+ DCHECK_NE(position_, end_position_);
+ CheckModifications();
+ ++position_;
+ SkipEmptyBuckets();
+ return *this;
+ }
+
+ // postfix ++ intentionally omitted
+
+ // Comparison.
+ bool operator==(const const_iterator& other) const {
+ return position_ == other.position_;
+ }
+ bool operator!=(const const_iterator& other) const {
+ return position_ != other.position_;
+ }
+ bool operator==(const iterator& other) const {
+ return *this == static_cast<const_iterator>(other);
+ }
+ bool operator!=(const iterator& other) const {
+ return *this != static_cast<const_iterator>(other);
+ }
+
+ std::ostream& PrintTo(std::ostream& stream) const {
+ if (position_ == end_position_)
+ return stream << "iterator representing <end>";
+ // TODO(tkent): Change |position_| to |*position_| to show the
+ // pointed object. It requires a lot of new stream printer functions.
+ return stream << "iterator pointing to " << position_;
+ }
+
+ private:
+ PointerType position_;
+ PointerType end_position_;
+#if DCHECK_IS_ON()
+ const HashTableType* container_;
+ int64_t container_modifications_;
+#endif
+};
+
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename Hash,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+std::ostream& operator<<(std::ostream& stream,
+ const HashTableConstIterator<Key,
+ Value,
+ Extractor,
+ Hash,
+ Traits,
+ KeyTraits,
+ Allocator>& iterator) {
+ return iterator.PrintTo(stream);
+}
+
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+class HashTableIterator final {
+ DISALLOW_NEW();
+
+ private:
+ typedef HashTable<Key,
+ Value,
+ Extractor,
+ HashFunctions,
+ Traits,
+ KeyTraits,
+ Allocator>
+ HashTableType;
+ typedef HashTableIterator<Key,
+ Value,
+ Extractor,
+ HashFunctions,
+ Traits,
+ KeyTraits,
+ Allocator>
+ iterator;
+ typedef HashTableConstIterator<Key,
+ Value,
+ Extractor,
+ HashFunctions,
+ Traits,
+ KeyTraits,
+ Allocator>
+ const_iterator;
+ typedef Value ValueType;
+ typedef typename Traits::IteratorGetType GetType;
+ typedef ValueType* PointerType;
+
+ friend class HashTable<Key,
+ Value,
+ Extractor,
+ HashFunctions,
+ Traits,
+ KeyTraits,
+ Allocator>;
+
+ HashTableIterator(PointerType pos,
+ PointerType end,
+ const HashTableType* container)
+ : iterator_(pos, end, container) {}
+ HashTableIterator(PointerType pos,
+ PointerType end,
+ const HashTableType* container,
+ HashItemKnownGoodTag tag)
+ : iterator_(pos, end, container, tag) {}
+
+ public:
+ HashTableIterator() = default;
+
+ // default copy, assignment and destructor are OK
+
+ GetType Get() const { return const_cast<GetType>(iterator_.Get()); }
+ typename Traits::IteratorReferenceType operator*() const {
+ return Traits::GetToReferenceConversion(Get());
+ }
+ GetType operator->() const { return Get(); }
+
+ iterator& operator++() {
+ ++iterator_;
+ return *this;
+ }
+
+ // postfix ++ intentionally omitted
+
+ // Comparison.
+ bool operator==(const iterator& other) const {
+ return iterator_ == other.iterator_;
+ }
+ bool operator!=(const iterator& other) const {
+ return iterator_ != other.iterator_;
+ }
+ bool operator==(const const_iterator& other) const {
+ return iterator_ == other;
+ }
+ bool operator!=(const const_iterator& other) const {
+ return iterator_ != other;
+ }
+
+ operator const_iterator() const { return iterator_; }
+ std::ostream& PrintTo(std::ostream& stream) const {
+ return iterator_.PrintTo(stream);
+ }
+
+ private:
+ const_iterator iterator_;
+};
+
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename Hash,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+std::ostream& operator<<(std::ostream& stream,
+ const HashTableIterator<Key,
+ Value,
+ Extractor,
+ Hash,
+ Traits,
+ KeyTraits,
+ Allocator>& iterator) {
+ return iterator.PrintTo(stream);
+}
+
+using std::swap;
+
+template <typename T,
+ typename Allocator,
+ typename Traits,
+ bool enterGCForbiddenScope>
+struct Mover {
+ STATIC_ONLY(Mover);
+ static void Move(T&& from, T& to) {
+ to.~T();
+ ConstructTraits<T, Traits, Allocator>::ConstructAndNotifyElement(
+ &to, std::move(from));
+ }
+};
+
+template <typename T, typename Allocator, typename Traits>
+struct Mover<T, Allocator, Traits, true> {
+ STATIC_ONLY(Mover);
+ static void Move(T&& from, T& to) {
+ to.~T();
+ Allocator::EnterGCForbiddenScope();
+ ConstructTraits<T, Traits, Allocator>::ConstructAndNotifyElement(
+ &to, std::move(from));
+ Allocator::LeaveGCForbiddenScope();
+ }
+};
+
+template <typename HashFunctions, typename Traits, typename Allocator>
+class IdentityHashTranslator {
+ STATIC_ONLY(IdentityHashTranslator);
+
+ public:
+ template <typename T>
+ static unsigned GetHash(const T& key) {
+ return HashFunctions::GetHash(key);
+ }
+ template <typename T, typename U>
+ static bool Equal(const T& a, const U& b) {
+ return HashFunctions::Equal(a, b);
+ }
+ template <typename T, typename U, typename V>
+ static void Translate(T& location, U&&, V&& value) {
+ location = std::forward<V>(value);
+ }
+};
+
+template <typename HashTableType, typename ValueType>
+struct HashTableAddResult final {
+ STACK_ALLOCATED();
+ HashTableAddResult(const HashTableType* container,
+ ValueType* stored_value,
+ bool is_new_entry)
+ : stored_value(stored_value),
+ is_new_entry(is_new_entry)
+#if ENABLE_SECURITY_ASSERT
+ ,
+ container_(container),
+ container_modifications_(container->Modifications())
+#endif
+ {
+ ALLOW_UNUSED_LOCAL(container);
+ DCHECK(container);
+ }
+
+ ValueType* stored_value;
+ bool is_new_entry;
+
+#if ENABLE_SECURITY_ASSERT
+ ~HashTableAddResult() {
+ // If rehash happened before accessing storedValue, it's
+ // use-after-free. Any modification may cause a rehash, so we check for
+ // modifications here.
+
+ // Rehash after accessing storedValue is harmless but will assert if the
+ // AddResult destructor takes place after a modification. You may need
+ // to limit the scope of the AddResult.
+ SECURITY_DCHECK(container_modifications_ == container_->Modifications());
+ }
+
+ private:
+ const HashTableType* container_;
+ const int64_t container_modifications_;
+#endif
+};
+
+template <typename Value, typename Extractor, typename KeyTraits>
+struct HashTableHelper {
+ STATIC_ONLY(HashTableHelper);
+ static bool IsEmptyBucket(const Value& value) {
+ return IsHashTraitsEmptyValue<KeyTraits>(Extractor::Extract(value));
+ }
+ static bool IsDeletedBucket(const Value& value) {
+ return KeyTraits::IsDeletedValue(Extractor::Extract(value));
+ }
+ static bool IsEmptyOrDeletedBucket(const Value& value) {
+ return IsEmptyBucket(value) || IsDeletedBucket(value);
+ }
+};
+
+template <typename HashTranslator,
+ typename KeyTraits,
+ bool safeToCompareToEmptyOrDeleted>
+struct HashTableKeyChecker {
+ STATIC_ONLY(HashTableKeyChecker);
+ // There's no simple generic way to make this check if
+ // safeToCompareToEmptyOrDeleted is false, so the check always passes.
+ template <typename T>
+ static bool CheckKey(const T&) {
+ return true;
+ }
+};
+
+template <typename HashTranslator, typename KeyTraits>
+struct HashTableKeyChecker<HashTranslator, KeyTraits, true> {
+ STATIC_ONLY(HashTableKeyChecker);
+ template <typename T>
+ static bool CheckKey(const T& key) {
+ // FIXME : Check also equality to the deleted value.
+ return !HashTranslator::Equal(KeyTraits::EmptyValue(), key);
+ }
+};
+
+// Note: empty or deleted key values are not allowed, using them may lead to
+// undefined behavior. For pointer keys this means that null pointers are not
+// allowed unless you supply custom key traits.
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+class HashTable final
+ : public ConditionalDestructor<HashTable<Key,
+ Value,
+ Extractor,
+ HashFunctions,
+ Traits,
+ KeyTraits,
+ Allocator>,
+ Allocator::kIsGarbageCollected> {
+ DISALLOW_NEW();
+
+ public:
+ typedef HashTableIterator<Key,
+ Value,
+ Extractor,
+ HashFunctions,
+ Traits,
+ KeyTraits,
+ Allocator>
+ iterator;
+ typedef HashTableConstIterator<Key,
+ Value,
+ Extractor,
+ HashFunctions,
+ Traits,
+ KeyTraits,
+ Allocator>
+ const_iterator;
+ typedef Traits ValueTraits;
+ typedef Key KeyType;
+ typedef typename KeyTraits::PeekInType KeyPeekInType;
+ typedef Value ValueType;
+ typedef Extractor ExtractorType;
+ typedef KeyTraits KeyTraitsType;
+ typedef IdentityHashTranslator<HashFunctions, Traits, Allocator>
+ IdentityTranslatorType;
+ typedef HashTableAddResult<HashTable, ValueType> AddResult;
+
+ HashTable();
+ void Finalize() {
+ DCHECK(!Allocator::kIsGarbageCollected);
+ if (LIKELY(!table_))
+ return;
+ EnterAccessForbiddenScope();
+ DeleteAllBucketsAndDeallocate(table_, table_size_);
+ LeaveAccessForbiddenScope();
+ table_ = nullptr;
+ }
+
+ HashTable(const HashTable&);
+ HashTable(HashTable&&);
+ void swap(HashTable&);
+ HashTable& operator=(const HashTable&);
+ HashTable& operator=(HashTable&&);
+
+ // When the hash table is empty, just return the same iterator for end as
+ // for begin. This is more efficient because we don't have to skip all the
+ // empty and deleted buckets, and iterating an empty table is a common case
+ // that's worth optimizing.
+ iterator begin() { return IsEmpty() ? end() : MakeIterator(table_); }
+ iterator end() { return MakeKnownGoodIterator(table_ + table_size_); }
+ const_iterator begin() const {
+ return IsEmpty() ? end() : MakeConstIterator(table_);
+ }
+ const_iterator end() const {
+ return MakeKnownGoodConstIterator(table_ + table_size_);
+ }
+
+ unsigned size() const {
+ DCHECK(!AccessForbidden());
+ return key_count_;
+ }
+ unsigned Capacity() const {
+ DCHECK(!AccessForbidden());
+ return table_size_;
+ }
+ bool IsEmpty() const {
+ DCHECK(!AccessForbidden());
+ return !key_count_;
+ }
+
+ void ReserveCapacityForSize(unsigned size);
+
+ template <typename IncomingValueType>
+ AddResult insert(IncomingValueType&& value) {
+ return insert<IdentityTranslatorType>(
+ Extractor::Extract(value), std::forward<IncomingValueType>(value));
+ }
+
+ // A special version of insert() that finds the object by hashing and
+ // comparing with some other type, to avoid the cost of type conversion if the
+ // object is already in the table.
+ template <typename HashTranslator, typename T, typename Extra>
+ AddResult insert(T&& key, Extra&&);
+ template <typename HashTranslator, typename T, typename Extra>
+ AddResult InsertPassingHashCode(T&& key, Extra&&);
+
+ iterator find(KeyPeekInType key) { return Find<IdentityTranslatorType>(key); }
+ const_iterator find(KeyPeekInType key) const {
+ return Find<IdentityTranslatorType>(key);
+ }
+ bool Contains(KeyPeekInType key) const {
+ return Contains<IdentityTranslatorType>(key);
+ }
+
+ template <typename HashTranslator, typename T>
+ iterator Find(const T&);
+ template <typename HashTranslator, typename T>
+ const_iterator Find(const T&) const;
+ template <typename HashTranslator, typename T>
+ bool Contains(const T&) const;
+
+ void erase(KeyPeekInType);
+ void erase(iterator);
+ void erase(const_iterator);
+ void clear();
+
+ static bool IsEmptyBucket(const ValueType& value) {
+ return IsHashTraitsEmptyValue<KeyTraits>(Extractor::Extract(value));
+ }
+ static bool IsDeletedBucket(const ValueType& value) {
+ return KeyTraits::IsDeletedValue(Extractor::Extract(value));
+ }
+ static bool IsEmptyOrDeletedBucket(const ValueType& value) {
+ return HashTableHelper<ValueType, Extractor,
+ KeyTraits>::IsEmptyOrDeletedBucket(value);
+ }
+
+ ValueType* Lookup(KeyPeekInType key) {
+ return Lookup<IdentityTranslatorType, KeyPeekInType>(key);
+ }
+ const ValueType* Lookup(KeyPeekInType key) const {
+ return Lookup<IdentityTranslatorType, KeyPeekInType>(key);
+ }
+ template <typename HashTranslator, typename T>
+ ValueType* Lookup(const T&);
+ template <typename HashTranslator, typename T>
+ const ValueType* Lookup(const T&) const;
+
+ template <typename VisitorDispatcher, typename A = Allocator>
+ std::enable_if_t<A::kIsGarbageCollected> Trace(VisitorDispatcher);
+
+#if DCHECK_IS_ON()
+ void EnterAccessForbiddenScope() {
+ DCHECK(!access_forbidden_);
+ access_forbidden_ = true;
+ }
+ void LeaveAccessForbiddenScope() { access_forbidden_ = false; }
+ bool AccessForbidden() const { return access_forbidden_; }
+ int64_t Modifications() const { return modifications_; }
+ void RegisterModification() { modifications_++; }
+ // HashTable and collections that build on it do not support modifications
+ // while there is an iterator in use. The exception is ListHashSet, which
+ // has its own iterators that tolerate modification of the underlying set.
+ void CheckModifications(int64_t mods) const {
+ DCHECK_EQ(mods, modifications_);
+ }
+#else
+ ALWAYS_INLINE void EnterAccessForbiddenScope() {}
+ ALWAYS_INLINE void LeaveAccessForbiddenScope() {}
+ ALWAYS_INLINE bool AccessForbidden() const { return false; }
+ ALWAYS_INLINE int64_t Modifications() const { return 0; }
+ ALWAYS_INLINE void RegisterModification() {}
+ ALWAYS_INLINE void CheckModifications(int64_t mods) const {}
+#endif
+
+ private:
+ static ValueType* AllocateTable(unsigned size);
+ static void DeleteAllBucketsAndDeallocate(ValueType* table, unsigned size);
+
+ typedef std::pair<ValueType*, bool> LookupType;
+ typedef std::pair<LookupType, unsigned> FullLookupType;
+
+ LookupType LookupForWriting(const Key& key) {
+ return LookupForWriting<IdentityTranslatorType>(key);
+ }
+ template <typename HashTranslator, typename T>
+ FullLookupType FullLookupForWriting(const T&);
+ template <typename HashTranslator, typename T>
+ LookupType LookupForWriting(const T&);
+
+ void erase(const ValueType*);
+
+ bool ShouldExpand() const {
+ return (key_count_ + deleted_count_) * kMaxLoad >= table_size_;
+ }
+ bool MustRehashInPlace() const {
+ return key_count_ * kMinLoad < table_size_ * 2;
+ }
+ bool ShouldShrink() const {
+ // isAllocationAllowed check should be at the last because it's
+ // expensive.
+ return key_count_ * kMinLoad < table_size_ &&
+ table_size_ > KeyTraits::kMinimumTableSize &&
+ !Allocator::IsObjectResurrectionForbidden() &&
+ Allocator::IsAllocationAllowed();
+ }
+ ValueType* Expand(ValueType* entry = nullptr);
+ void Shrink() { Rehash(table_size_ / 2, nullptr); }
+
+ ValueType* ExpandBuffer(unsigned new_table_size, ValueType* entry, bool&);
+ ValueType* RehashTo(ValueType* new_table,
+ unsigned new_table_size,
+ ValueType* entry);
+ ValueType* Rehash(unsigned new_table_size, ValueType* entry);
+ ValueType* Reinsert(ValueType&&);
+
+ static void InitializeBucket(ValueType& bucket);
+ static void DeleteBucket(const ValueType& bucket) {
+ bucket.~ValueType();
+ Traits::ConstructDeletedValue(const_cast<ValueType&>(bucket),
+ Allocator::kIsGarbageCollected);
+ }
+
+ FullLookupType MakeLookupResult(ValueType* position,
+ bool found,
+ unsigned hash) {
+ return FullLookupType(LookupType(position, found), hash);
+ }
+
+ iterator MakeIterator(ValueType* pos) {
+ return iterator(pos, table_ + table_size_, this);
+ }
+ const_iterator MakeConstIterator(const ValueType* pos) const {
+ return const_iterator(pos, table_ + table_size_, this);
+ }
+ iterator MakeKnownGoodIterator(ValueType* pos) {
+ return iterator(pos, table_ + table_size_, this, kHashItemKnownGood);
+ }
+ const_iterator MakeKnownGoodConstIterator(const ValueType* pos) const {
+ return const_iterator(pos, table_ + table_size_, this, kHashItemKnownGood);
+ }
+
+ static const unsigned kMaxLoad = 2;
+ static const unsigned kMinLoad = 6;
+
+ unsigned TableSizeMask() const {
+ size_t mask = table_size_ - 1;
+ DCHECK_EQ((mask & table_size_), 0u);
+ return mask;
+ }
+
+ void SetEnqueued() { queue_flag_ = true; }
+ void ClearEnqueued() { queue_flag_ = false; }
+ bool Enqueued() { return queue_flag_; }
+
+ ValueType* table_;
+ unsigned table_size_;
+ unsigned key_count_;
+#if DCHECK_IS_ON()
+ unsigned deleted_count_ : 30;
+ unsigned queue_flag_ : 1;
+ unsigned access_forbidden_ : 1;
+ unsigned modifications_;
+#else
+ unsigned deleted_count_ : 31;
+ unsigned queue_flag_ : 1;
+#endif
+
+#if DUMP_HASHTABLE_STATS_PER_TABLE
+ public:
+ mutable
+ typename std::conditional<Allocator::kIsGarbageCollected,
+ HashTableStats*,
+ std::unique_ptr<HashTableStats>>::type stats_;
+ void DumpStats() {
+ if (stats_) {
+ stats_->DumpStats();
+ }
+ }
+#endif
+
+ template <WeakHandlingFlag x,
+ typename T,
+ typename U,
+ typename V,
+ typename W,
+ typename X,
+ typename Y,
+ typename Z>
+ friend struct WeakProcessingHashTableHelper;
+ template <typename T, typename U, typename V, typename W>
+ friend class LinkedHashSet;
+};
+
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+inline HashTable<Key,
+ Value,
+ Extractor,
+ HashFunctions,
+ Traits,
+ KeyTraits,
+ Allocator>::HashTable()
+ : table_(nullptr),
+ table_size_(0),
+ key_count_(0),
+ deleted_count_(0),
+ queue_flag_(false)
+#if DCHECK_IS_ON()
+ ,
+ access_forbidden_(false),
+ modifications_(0)
+#endif
+#if DUMP_HASHTABLE_STATS_PER_TABLE
+ ,
+ stats_(nullptr)
+#endif
+{
+ static_assert(Allocator::kIsGarbageCollected ||
+ (!IsPointerToGarbageCollectedType<Key>::value &&
+ !IsPointerToGarbageCollectedType<Value>::value),
+ "Cannot put raw pointers to garbage-collected classes into an "
+ "off-heap collection.");
+}
+
+inline unsigned DoubleHash(unsigned key) {
+ key = ~key + (key >> 23);
+ key ^= (key << 12);
+ key ^= (key >> 7);
+ key ^= (key << 2);
+ key ^= (key >> 20);
+ return key;
+}
+
+inline unsigned CalculateCapacity(unsigned size) {
+ for (unsigned mask = size; mask; mask >>= 1)
+ size |= mask; // 00110101010 -> 00111111111
+ return (size + 1) * 2; // 00111111111 -> 10000000000
+}
+
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+void HashTable<Key,
+ Value,
+ Extractor,
+ HashFunctions,
+ Traits,
+ KeyTraits,
+ Allocator>::ReserveCapacityForSize(unsigned new_size) {
+ unsigned new_capacity = CalculateCapacity(new_size);
+ if (new_capacity < KeyTraits::kMinimumTableSize)
+ new_capacity = KeyTraits::kMinimumTableSize;
+
+ if (new_capacity > Capacity()) {
+ CHECK(!static_cast<int>(
+ new_capacity >>
+ 31)); // HashTable capacity should not overflow 32bit int.
+ Rehash(new_capacity, nullptr);
+ }
+}
+
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+template <typename HashTranslator, typename T>
+inline Value*
+HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::
+ Lookup(const T& key) {
+ // Call the const version of Lookup<HashTranslator, T>().
+ return const_cast<Value*>(
+ const_cast<const HashTable*>(this)->Lookup<HashTranslator>(key));
+}
+
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+template <typename HashTranslator, typename T>
+inline const Value*
+HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::
+ Lookup(const T& key) const {
+ DCHECK(!AccessForbidden());
+ DCHECK((HashTableKeyChecker<
+ HashTranslator, KeyTraits,
+ HashFunctions::safe_to_compare_to_empty_or_deleted>::CheckKey(key)));
+ const ValueType* table = table_;
+ if (!table)
+ return nullptr;
+
+ size_t k = 0;
+ size_t size_mask = TableSizeMask();
+ unsigned h = HashTranslator::GetHash(key);
+ size_t i = h & size_mask;
+
+ UPDATE_ACCESS_COUNTS();
+
+ while (1) {
+ const ValueType* entry = table + i;
+
+ if (HashFunctions::safe_to_compare_to_empty_or_deleted) {
+ if (HashTranslator::Equal(Extractor::Extract(*entry), key))
+ return entry;
+
+ if (IsEmptyBucket(*entry))
+ return nullptr;
+ } else {
+ if (IsEmptyBucket(*entry))
+ return nullptr;
+
+ if (!IsDeletedBucket(*entry) &&
+ HashTranslator::Equal(Extractor::Extract(*entry), key))
+ return entry;
+ }
+ UPDATE_PROBE_COUNTS();
+ if (!k)
+ k = 1 | DoubleHash(h);
+ i = (i + k) & size_mask;
+ }
+}
+
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+template <typename HashTranslator, typename T>
+inline typename HashTable<Key,
+ Value,
+ Extractor,
+ HashFunctions,
+ Traits,
+ KeyTraits,
+ Allocator>::LookupType
+HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::
+ LookupForWriting(const T& key) {
+ DCHECK(!AccessForbidden());
+ DCHECK(table_);
+ RegisterModification();
+
+ ValueType* table = table_;
+ size_t k = 0;
+ size_t size_mask = TableSizeMask();
+ unsigned h = HashTranslator::GetHash(key);
+ size_t i = h & size_mask;
+
+ UPDATE_ACCESS_COUNTS();
+
+ ValueType* deleted_entry = nullptr;
+
+ while (1) {
+ ValueType* entry = table + i;
+
+ if (IsEmptyBucket(*entry))
+ return LookupType(deleted_entry ? deleted_entry : entry, false);
+
+ if (HashFunctions::safe_to_compare_to_empty_or_deleted) {
+ if (HashTranslator::Equal(Extractor::Extract(*entry), key))
+ return LookupType(entry, true);
+
+ if (IsDeletedBucket(*entry))
+ deleted_entry = entry;
+ } else {
+ if (IsDeletedBucket(*entry))
+ deleted_entry = entry;
+ else if (HashTranslator::Equal(Extractor::Extract(*entry), key))
+ return LookupType(entry, true);
+ }
+ UPDATE_PROBE_COUNTS();
+ if (!k)
+ k = 1 | DoubleHash(h);
+ i = (i + k) & size_mask;
+ }
+}
+
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+template <typename HashTranslator, typename T>
+inline typename HashTable<Key,
+ Value,
+ Extractor,
+ HashFunctions,
+ Traits,
+ KeyTraits,
+ Allocator>::FullLookupType
+HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::
+ FullLookupForWriting(const T& key) {
+ DCHECK(!AccessForbidden());
+ DCHECK(table_);
+ RegisterModification();
+
+ ValueType* table = table_;
+ size_t k = 0;
+ size_t size_mask = TableSizeMask();
+ unsigned h = HashTranslator::GetHash(key);
+ size_t i = h & size_mask;
+
+ UPDATE_ACCESS_COUNTS();
+
+ ValueType* deleted_entry = nullptr;
+
+ while (1) {
+ ValueType* entry = table + i;
+
+ if (IsEmptyBucket(*entry))
+ return MakeLookupResult(deleted_entry ? deleted_entry : entry, false, h);
+
+ if (HashFunctions::safe_to_compare_to_empty_or_deleted) {
+ if (HashTranslator::Equal(Extractor::Extract(*entry), key))
+ return MakeLookupResult(entry, true, h);
+
+ if (IsDeletedBucket(*entry))
+ deleted_entry = entry;
+ } else {
+ if (IsDeletedBucket(*entry))
+ deleted_entry = entry;
+ else if (HashTranslator::Equal(Extractor::Extract(*entry), key))
+ return MakeLookupResult(entry, true, h);
+ }
+ UPDATE_PROBE_COUNTS();
+ if (!k)
+ k = 1 | DoubleHash(h);
+ i = (i + k) & size_mask;
+ }
+}
+
+template <bool emptyValueIsZero>
+struct HashTableBucketInitializer;
+
+template <>
+struct HashTableBucketInitializer<false> {
+ STATIC_ONLY(HashTableBucketInitializer);
+ template <typename Traits, typename Allocator, typename Value>
+ static void Initialize(Value& bucket) {
+ ConstructTraits<Value, Traits, Allocator>::ConstructAndNotifyElement(
+ &bucket, Traits::EmptyValue());
+ }
+};
+
+template <>
+struct HashTableBucketInitializer<true> {
+ STATIC_ONLY(HashTableBucketInitializer);
+ template <typename Traits, typename Allocator, typename Value>
+ static void Initialize(Value& bucket) {
+ // This initializes the bucket without copying the empty value. That
+ // makes it possible to use this with types that don't support copying.
+ // The memset to 0 looks like a slow operation but is optimized by the
+ // compilers.
+ memset(&bucket, 0, sizeof(bucket));
+ }
+};
+
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+inline void
+HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::
+ InitializeBucket(ValueType& bucket) {
+ HashTableBucketInitializer<Traits::kEmptyValueIsZero>::template Initialize<
+ Traits, Allocator>(bucket);
+}
+
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+template <typename HashTranslator, typename T, typename Extra>
+typename HashTable<Key,
+ Value,
+ Extractor,
+ HashFunctions,
+ Traits,
+ KeyTraits,
+ Allocator>::AddResult
+HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::
+ insert(T&& key, Extra&& extra) {
+ DCHECK(!AccessForbidden());
+ DCHECK(Allocator::IsAllocationAllowed());
+ if (!table_)
+ Expand();
+
+ DCHECK(table_);
+
+ ValueType* table = table_;
+ size_t k = 0;
+ size_t size_mask = TableSizeMask();
+ unsigned h = HashTranslator::GetHash(key);
+ size_t i = h & size_mask;
+
+ UPDATE_ACCESS_COUNTS();
+
+ ValueType* deleted_entry = nullptr;
+ ValueType* entry;
+ while (1) {
+ entry = table + i;
+
+ if (IsEmptyBucket(*entry))
+ break;
+
+ if (HashFunctions::safe_to_compare_to_empty_or_deleted) {
+ if (HashTranslator::Equal(Extractor::Extract(*entry), key))
+ return AddResult(this, entry, false);
+
+ if (IsDeletedBucket(*entry))
+ deleted_entry = entry;
+ } else {
+ if (IsDeletedBucket(*entry))
+ deleted_entry = entry;
+ else if (HashTranslator::Equal(Extractor::Extract(*entry), key))
+ return AddResult(this, entry, false);
+ }
+ UPDATE_PROBE_COUNTS();
+ if (!k)
+ k = 1 | DoubleHash(h);
+ i = (i + k) & size_mask;
+ }
+
+ RegisterModification();
+
+ if (deleted_entry) {
+ // Overwrite any data left over from last use, using placement new or
+ // memset.
+ InitializeBucket(*deleted_entry);
+ entry = deleted_entry;
+ --deleted_count_;
+ }
+
+ HashTranslator::Translate(*entry, std::forward<T>(key),
+ std::forward<Extra>(extra));
+ DCHECK(!IsEmptyOrDeletedBucket(*entry));
+ // Translate constructs an element so we need to notify using the trait. Avoid
+ // doing that in the translator so that they can be easily customized.
+ ConstructTraits<ValueType, Traits, Allocator>::NotifyNewElements(entry, 1);
+
+ ++key_count_;
+
+ if (ShouldExpand()) {
+ entry = Expand(entry);
+ } else if (Traits::kWeakHandlingFlag == kWeakHandling && ShouldShrink()) {
+ // When weak hash tables are processed by the garbage collector,
+ // elements with no other strong references to them will have their
+ // table entries cleared. But no shrinking of the backing store is
+ // allowed at that time, as allocations are prohibited during that
+ // GC phase.
+ //
+ // With that weak processing taking care of removals, explicit
+ // erase()s of elements is rarely done. Which implies that the
+ // weak hash table will never be checked if it can be shrunk.
+ //
+ // To prevent weak hash tables with very low load factors from
+ // developing, we perform it when adding elements instead.
+ entry = Rehash(table_size_ / 2, entry);
+ }
+
+ return AddResult(this, entry, true);
+}
+
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+template <typename HashTranslator, typename T, typename Extra>
+typename HashTable<Key,
+ Value,
+ Extractor,
+ HashFunctions,
+ Traits,
+ KeyTraits,
+ Allocator>::AddResult
+HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::
+ InsertPassingHashCode(T&& key, Extra&& extra) {
+ DCHECK(!AccessForbidden());
+ DCHECK(Allocator::IsAllocationAllowed());
+ if (!table_)
+ Expand();
+
+ FullLookupType lookup_result = FullLookupForWriting<HashTranslator>(key);
+
+ ValueType* entry = lookup_result.first.first;
+ bool found = lookup_result.first.second;
+ unsigned h = lookup_result.second;
+
+ if (found)
+ return AddResult(this, entry, false);
+
+ RegisterModification();
+
+ if (IsDeletedBucket(*entry)) {
+ InitializeBucket(*entry);
+ --deleted_count_;
+ }
+
+ HashTranslator::Translate(*entry, std::forward<T>(key),
+ std::forward<Extra>(extra), h);
+ DCHECK(!IsEmptyOrDeletedBucket(*entry));
+ // Translate constructs an element so we need to notify using the trait. Avoid
+ // doing that in the translator so that they can be easily customized.
+ ConstructTraits<ValueType, Traits, Allocator>::NotifyNewElements(entry, 1);
+
+ ++key_count_;
+ if (ShouldExpand())
+ entry = Expand(entry);
+
+ return AddResult(this, entry, true);
+}
+
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+Value*
+HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::
+ Reinsert(ValueType&& entry) {
+ DCHECK(table_);
+ RegisterModification();
+ DCHECK(!LookupForWriting(Extractor::Extract(entry)).second);
+ DCHECK(
+ !IsDeletedBucket(*(LookupForWriting(Extractor::Extract(entry)).first)));
+#if DUMP_HASHTABLE_STATS
+ AtomicIncrement(&HashTableStats::instance().numReinserts);
+#endif
+#if DUMP_HASHTABLE_STATS_PER_TABLE
+ ++stats_->numReinserts;
+#endif
+ Value* new_entry = LookupForWriting(Extractor::Extract(entry)).first;
+ Mover<ValueType, Allocator, Traits,
+ Traits::template NeedsToForbidGCOnMove<>::value>::Move(std::move(entry),
+ *new_entry);
+
+ return new_entry;
+}
+
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+template <typename HashTranslator, typename T>
+inline typename HashTable<Key,
+ Value,
+ Extractor,
+ HashFunctions,
+ Traits,
+ KeyTraits,
+ Allocator>::iterator
+HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::
+ Find(const T& key) {
+ ValueType* entry = Lookup<HashTranslator>(key);
+ if (!entry)
+ return end();
+
+ return MakeKnownGoodIterator(entry);
+}
+
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+template <typename HashTranslator, typename T>
+inline typename HashTable<Key,
+ Value,
+ Extractor,
+ HashFunctions,
+ Traits,
+ KeyTraits,
+ Allocator>::const_iterator
+HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::
+ Find(const T& key) const {
+ const ValueType* entry = Lookup<HashTranslator>(key);
+ if (!entry)
+ return end();
+
+ return MakeKnownGoodConstIterator(entry);
+}
+
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+template <typename HashTranslator, typename T>
+bool HashTable<Key,
+ Value,
+ Extractor,
+ HashFunctions,
+ Traits,
+ KeyTraits,
+ Allocator>::Contains(const T& key) const {
+ return Lookup<HashTranslator>(key);
+}
+
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+void HashTable<Key,
+ Value,
+ Extractor,
+ HashFunctions,
+ Traits,
+ KeyTraits,
+ Allocator>::erase(const ValueType* pos) {
+ RegisterModification();
+#if DUMP_HASHTABLE_STATS
+ AtomicIncrement(&HashTableStats::instance().numRemoves);
+#endif
+#if DUMP_HASHTABLE_STATS_PER_TABLE
+ ++stats_->numRemoves;
+#endif
+
+ EnterAccessForbiddenScope();
+ DeleteBucket(*pos);
+ LeaveAccessForbiddenScope();
+ ++deleted_count_;
+ --key_count_;
+
+ if (ShouldShrink())
+ Shrink();
+}
+
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+inline void
+HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::
+ erase(iterator it) {
+ if (it == end())
+ return;
+ erase(it.iterator_.position_);
+}
+
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+inline void
+HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::
+ erase(const_iterator it) {
+ if (it == end())
+ return;
+ erase(it.position_);
+}
+
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+inline void
+HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::
+ erase(KeyPeekInType key) {
+ erase(find(key));
+}
+
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+Value*
+HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::
+ AllocateTable(unsigned size) {
+ size_t alloc_size = size * sizeof(ValueType);
+ ValueType* result;
+ // Assert that we will not use memset on things with a vtable entry. The
+ // compiler will also check this on some platforms. We would like to check
+ // this on the whole value (key-value pair), but std::is_polymorphic will
+ // return false for a pair of two types, even if one of the components is
+ // polymorphic.
+ static_assert(
+ !Traits::kEmptyValueIsZero || !std::is_polymorphic<KeyType>::value,
+ "empty value cannot be zero for things with a vtable");
+ static_assert(Allocator::kIsGarbageCollected ||
+ ((!AllowsOnlyPlacementNew<KeyType>::value ||
+ !IsTraceable<KeyType>::value) &&
+ (!AllowsOnlyPlacementNew<ValueType>::value ||
+ !IsTraceable<ValueType>::value)),
+ "Cannot put DISALLOW_NEW_EXCEPT_PLACEMENT_NEW objects that "
+ "have trace methods into an off-heap HashTable");
+
+ if (Traits::kEmptyValueIsZero) {
+ result = Allocator::template AllocateZeroedHashTableBacking<ValueType,
+ HashTable>(
+ alloc_size);
+ } else {
+ result = Allocator::template AllocateHashTableBacking<ValueType, HashTable>(
+ alloc_size);
+ for (unsigned i = 0; i < size; i++)
+ InitializeBucket(result[i]);
+ }
+ return result;
+}
+
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+void HashTable<Key,
+ Value,
+ Extractor,
+ HashFunctions,
+ Traits,
+ KeyTraits,
+ Allocator>::DeleteAllBucketsAndDeallocate(ValueType* table,
+ unsigned size) {
+ if (!IsTriviallyDestructible<ValueType>::value) {
+ for (unsigned i = 0; i < size; ++i) {
+ // This code is called when the hash table is cleared or resized. We
+ // have allocated a new backing store and we need to run the
+ // destructors on the old backing store, as it is being freed. If we
+ // are GCing we need to both call the destructor and mark the bucket
+ // as deleted, otherwise the destructor gets called again when the
+ // GC finds the backing store. With the default allocator it's
+ // enough to call the destructor, since we will free the memory
+ // explicitly and we won't see the memory with the bucket again.
+ if (Allocator::kIsGarbageCollected) {
+ if (!IsEmptyOrDeletedBucket(table[i]))
+ DeleteBucket(table[i]);
+ } else {
+ if (!IsDeletedBucket(table[i]))
+ table[i].~ValueType();
+ }
+ }
+ }
+ // Notify if this is a weak table since immediately freeing a weak hash table
+ // backing may cause a use-after-free when the weak callback is called.
+ Allocator::FreeHashTableBacking(table,
+ Traits::kWeakHandlingFlag == kWeakHandling);
+}
+
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+Value*
+HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::
+ Expand(Value* entry) {
+ unsigned new_size;
+ if (!table_size_) {
+ new_size = KeyTraits::kMinimumTableSize;
+ } else if (MustRehashInPlace()) {
+ new_size = table_size_;
+ } else {
+ new_size = table_size_ * 2;
+ CHECK_GT(new_size, table_size_);
+ }
+
+ return Rehash(new_size, entry);
+}
+
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+Value*
+HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::
+ ExpandBuffer(unsigned new_table_size, Value* entry, bool& success) {
+ success = false;
+ DCHECK_LT(table_size_, new_table_size);
+ CHECK(!Allocator::IsObjectResurrectionForbidden());
+ if (!Allocator::ExpandHashTableBacking(table_,
+ new_table_size * sizeof(ValueType)))
+ return nullptr;
+
+ success = true;
+
+ Value* new_entry = nullptr;
+ unsigned old_table_size = table_size_;
+ ValueType* original_table = table_;
+
+ ValueType* temporary_table = AllocateTable(old_table_size);
+ for (unsigned i = 0; i < old_table_size; i++) {
+ if (&table_[i] == entry)
+ new_entry = &temporary_table[i];
+ if (IsEmptyOrDeletedBucket(table_[i])) {
+ DCHECK_NE(&table_[i], entry);
+ if (Traits::kEmptyValueIsZero) {
+ memset(&temporary_table[i], 0, sizeof(ValueType));
+ } else {
+ InitializeBucket(temporary_table[i]);
+ }
+ } else {
+ Mover<ValueType, Allocator, Traits,
+ Traits::template NeedsToForbidGCOnMove<>::value>::
+ Move(std::move(table_[i]), temporary_table[i]);
+ table_[i].~ValueType();
+ }
+ }
+ table_ = temporary_table;
+ Allocator::BackingWriteBarrier(table_);
+
+ if (Traits::kEmptyValueIsZero) {
+ memset(original_table, 0, new_table_size * sizeof(ValueType));
+ } else {
+ for (unsigned i = 0; i < new_table_size; i++)
+ InitializeBucket(original_table[i]);
+ }
+ new_entry = RehashTo(original_table, new_table_size, new_entry);
+
+ EnterAccessForbiddenScope();
+ DeleteAllBucketsAndDeallocate(temporary_table, old_table_size);
+ LeaveAccessForbiddenScope();
+
+ return new_entry;
+}
+
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+Value*
+HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::
+ RehashTo(ValueType* new_table, unsigned new_table_size, Value* entry) {
+ unsigned old_table_size = table_size_;
+ ValueType* old_table = table_;
+
+#if DUMP_HASHTABLE_STATS
+ if (old_table_size != 0)
+ AtomicIncrement(&HashTableStats::instance().numRehashes);
+#endif
+
+#if DUMP_HASHTABLE_STATS_PER_TABLE
+ if (old_table_size != 0)
+ ++stats_->numRehashes;
+#endif
+
+ table_ = new_table;
+ Allocator::BackingWriteBarrier(table_);
+ table_size_ = new_table_size;
+
+ Value* new_entry = nullptr;
+ for (unsigned i = 0; i != old_table_size; ++i) {
+ if (IsEmptyOrDeletedBucket(old_table[i])) {
+ DCHECK_NE(&old_table[i], entry);
+ continue;
+ }
+ Value* reinserted_entry = Reinsert(std::move(old_table[i]));
+ if (&old_table[i] == entry) {
+ DCHECK(!new_entry);
+ new_entry = reinserted_entry;
+ }
+ }
+
+ deleted_count_ = 0;
+
+#if DUMP_HASHTABLE_STATS_PER_TABLE
+ if (!stats_)
+ stats_ = HashTableStatsPtr<Allocator>::Create();
+#endif
+
+ return new_entry;
+}
+
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+Value*
+HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::
+ Rehash(unsigned new_table_size, Value* entry) {
+ unsigned old_table_size = table_size_;
+ ValueType* old_table = table_;
+
+#if DUMP_HASHTABLE_STATS
+ if (old_table_size != 0)
+ AtomicIncrement(&HashTableStats::instance().numRehashes);
+#endif
+
+#if DUMP_HASHTABLE_STATS_PER_TABLE
+ if (old_table_size != 0)
+ ++stats_->numRehashes;
+#endif
+
+ // The Allocator::kIsGarbageCollected check is not needed. The check is just
+ // a static hint for a compiler to indicate that Base::expandBuffer returns
+ // false if Allocator is a PartitionAllocator.
+ if (Allocator::kIsGarbageCollected && new_table_size > old_table_size) {
+ bool success;
+ Value* new_entry = ExpandBuffer(new_table_size, entry, success);
+ if (success)
+ return new_entry;
+ }
+
+ ValueType* new_table = AllocateTable(new_table_size);
+ Value* new_entry = RehashTo(new_table, new_table_size, entry);
+
+ EnterAccessForbiddenScope();
+ DeleteAllBucketsAndDeallocate(old_table, old_table_size);
+ LeaveAccessForbiddenScope();
+
+ return new_entry;
+}
+
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+void HashTable<Key,
+ Value,
+ Extractor,
+ HashFunctions,
+ Traits,
+ KeyTraits,
+ Allocator>::clear() {
+ RegisterModification();
+ if (!table_)
+ return;
+
+ EnterAccessForbiddenScope();
+ DeleteAllBucketsAndDeallocate(table_, table_size_);
+ LeaveAccessForbiddenScope();
+ table_ = nullptr;
+ table_size_ = 0;
+ key_count_ = 0;
+}
+
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::
+ HashTable(const HashTable& other)
+ : table_(nullptr),
+ table_size_(0),
+ key_count_(0),
+ deleted_count_(0),
+ queue_flag_(false)
+#if DCHECK_IS_ON()
+ ,
+ access_forbidden_(false),
+ modifications_(0)
+#endif
+#if DUMP_HASHTABLE_STATS_PER_TABLE
+ ,
+ stats_(HashTableStatsPtr<Allocator>::copy(other.stats_))
+#endif
+{
+ if (other.size())
+ ReserveCapacityForSize(other.size());
+ // Copy the hash table the dumb way, by adding each element to the new
+ // table. It might be more efficient to copy the table slots, but it's not
+ // clear that efficiency is needed.
+ for (const auto& element : other)
+ insert(element);
+}
+
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::
+ HashTable(HashTable&& other)
+ : table_(nullptr),
+ table_size_(0),
+ key_count_(0),
+ deleted_count_(0),
+ queue_flag_(false)
+#if DCHECK_IS_ON()
+ ,
+ access_forbidden_(false),
+ modifications_(0)
+#endif
+#if DUMP_HASHTABLE_STATS_PER_TABLE
+ ,
+ stats_(HashTableStatsPtr<Allocator>::copy(other.stats_))
+#endif
+{
+ swap(other);
+}
+
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+void HashTable<Key,
+ Value,
+ Extractor,
+ HashFunctions,
+ Traits,
+ KeyTraits,
+ Allocator>::swap(HashTable& other) {
+ DCHECK(!AccessForbidden());
+ std::swap(table_, other.table_);
+ Allocator::BackingWriteBarrier(table_);
+ Allocator::BackingWriteBarrier(other.table_);
+ std::swap(table_size_, other.table_size_);
+ std::swap(key_count_, other.key_count_);
+ // std::swap does not work for bit fields.
+ unsigned deleted = deleted_count_;
+ deleted_count_ = other.deleted_count_;
+ other.deleted_count_ = deleted;
+ DCHECK(!queue_flag_);
+ DCHECK(!other.queue_flag_);
+
+#if DCHECK_IS_ON()
+ std::swap(modifications_, other.modifications_);
+#endif
+
+#if DUMP_HASHTABLE_STATS_PER_TABLE
+ HashTableStatsPtr<Allocator>::swap(stats_, other.stats_);
+#endif
+}
+
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>&
+HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::
+operator=(const HashTable& other) {
+ HashTable tmp(other);
+ swap(tmp);
+ return *this;
+}
+
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>&
+HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::
+operator=(HashTable&& other) {
+ swap(other);
+ return *this;
+}
+
+template <WeakHandlingFlag weakHandlingFlag,
+ typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+struct WeakProcessingHashTableHelper;
+
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+struct WeakProcessingHashTableHelper<kNoWeakHandling,
+ Key,
+ Value,
+ Extractor,
+ HashFunctions,
+ Traits,
+ KeyTraits,
+ Allocator> {
+ STATIC_ONLY(WeakProcessingHashTableHelper);
+ static void Process(typename Allocator::Visitor* visitor, void* closure) {}
+ static void EphemeronIteration(typename Allocator::Visitor* visitor,
+ void* closure) {}
+};
+
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+struct WeakProcessingHashTableHelper<kWeakHandling,
+ Key,
+ Value,
+ Extractor,
+ HashFunctions,
+ Traits,
+ KeyTraits,
+ Allocator> {
+ STATIC_ONLY(WeakProcessingHashTableHelper);
+
+ using HashTableType = HashTable<Key,
+ Value,
+ Extractor,
+ HashFunctions,
+ Traits,
+ KeyTraits,
+ Allocator>;
+ using ValueType = typename HashTableType::ValueType;
+
+ // Used for purely weak and for weak-and-strong tables (ephemerons).
+ static void Process(typename Allocator::Visitor* visitor, void* closure) {
+ HashTableType* table = reinterpret_cast<HashTableType*>(closure);
+ // During incremental marking, the table may be freed after the callback has
+ // been registered.
+ if (!table->table_)
+ return;
+
+ // Only trace the backing store. Its fields will be processed below.
+ Allocator::template TraceHashTableBackingOnly<ValueType, HashTableType>(
+ visitor, table->table_, &(table->table_));
+ // Now perform weak processing (this is a no-op if the backing was
+ // accessible through an iterator and was already marked strongly).
+ for (ValueType* element = table->table_ + table->table_size_ - 1;
+ element >= table->table_; element--) {
+ if (!HashTableType::IsEmptyOrDeletedBucket(*element)) {
+ // At this stage calling trace can make no difference
+ // (everything is already traced), but we use the return value
+ // to remove things from the collection.
+
+ if (!TraceInCollectionTrait<kWeakHandling, ValueType, Traits>::IsAlive(
+ *element)) {
+ table->RegisterModification();
+ HashTableType::DeleteBucket(*element); // Also calls the destructor.
+ table->deleted_count_++;
+ table->key_count_--;
+ // We don't rehash the backing until the next add or delete,
+ // because that would cause allocation during GC.
+ }
+ }
+ }
+ }
+
+ // Called repeatedly for tables that have both weak and strong pointers.
+ static void EphemeronIteration(typename Allocator::Visitor* visitor,
+ void* closure) {
+ HashTableType* table = reinterpret_cast<HashTableType*>(closure);
+ // During incremental marking, the table may be freed after the callback has
+ // been registered.
+ if (!table->table_)
+ return;
+ // Check the hash table for elements that we now know will not be
+ // removed by weak processing. Those elements need to have their strong
+ // pointers traced.
+ for (ValueType* element = table->table_ + table->table_size_ - 1;
+ element >= table->table_; element--) {
+ if (!HashTableType::IsEmptyOrDeletedBucket(*element)) {
+ TraceInCollectionTrait<kWeakHandling, ValueType, Traits>::Trace(
+ visitor, *element);
+ }
+ }
+ }
+};
+
+template <typename Key,
+ typename Value,
+ typename Extractor,
+ typename HashFunctions,
+ typename Traits,
+ typename KeyTraits,
+ typename Allocator>
+template <typename VisitorDispatcher, typename A>
+std::enable_if_t<A::kIsGarbageCollected>
+HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::
+ Trace(VisitorDispatcher visitor) {
+ if (!table_)
+ return;
+
+ if (Traits::kWeakHandlingFlag == kNoWeakHandling) {
+ // Strong HashTable.
+ DCHECK(IsTraceableInCollectionTrait<Traits>::value);
+ Allocator::template TraceHashTableBackingStrongly<ValueType, HashTable>(
+ visitor, table_, &table_);
+ } else {
+ // Weak HashTable. The HashTable may be held alive strongly from somewhere
+ // else, e.g., an iterator.
+
+ // Trace the table weakly. For marking this will result in delaying the
+ // processing until the end of the atomic pause. It is safe to trace
+ // weakly multiple times.
+ Allocator::template TraceHashTableBackingWeakly<ValueType, HashTable>(
+ visitor, table_, &table_,
+ WeakProcessingHashTableHelper<Traits::kWeakHandlingFlag, Key, Value,
+ Extractor, HashFunctions, Traits,
+ KeyTraits, Allocator>::Process,
+ this);
+
+ if (IsTraceableInCollectionTrait<Traits>::value) {
+ // Mix of strong and weak fields. We use an approach similar to ephemeron
+ // marking to find a fixed point, c.f.:
+ // - http://dl.acm.org/citation.cfm?doid=263698.263733
+ // - http://www.jucs.org/jucs_14_21/eliminating_cycles_in_weak
+ // Adding the table for ephemeron marking delays marking any elements in
+ // the backing until regular marking is finished.
+ Allocator::RegisterWeakTable(
+ visitor, this,
+ WeakProcessingHashTableHelper<
+ Traits::kWeakHandlingFlag, Key, Value, Extractor, HashFunctions,
+ Traits, KeyTraits, Allocator>::EphemeronIteration);
+ }
+ }
+}
+
+// iterator adapters
+
+template <typename HashTableType, typename Traits>
+struct HashTableConstIteratorAdapter {
+ STACK_ALLOCATED();
+ HashTableConstIteratorAdapter() = default;
+ HashTableConstIteratorAdapter(
+ const typename HashTableType::const_iterator& impl)
+ : impl_(impl) {}
+ typedef typename Traits::IteratorConstGetType GetType;
+ typedef
+ typename HashTableType::ValueTraits::IteratorConstGetType SourceGetType;
+
+ GetType Get() const {
+ return const_cast<GetType>(SourceGetType(impl_.Get()));
+ }
+ typename Traits::IteratorConstReferenceType operator*() const {
+ return Traits::GetToReferenceConstConversion(Get());
+ }
+ GetType operator->() const { return Get(); }
+
+ HashTableConstIteratorAdapter& operator++() {
+ ++impl_;
+ return *this;
+ }
+ // postfix ++ intentionally omitted
+
+ typename HashTableType::const_iterator impl_;
+};
+
+template <typename HashTable, typename Traits>
+std::ostream& operator<<(
+ std::ostream& stream,
+ const HashTableConstIteratorAdapter<HashTable, Traits>& iterator) {
+ return stream << iterator.impl_;
+}
+
+template <typename HashTableType, typename Traits>
+struct HashTableIteratorAdapter {
+ STACK_ALLOCATED();
+ typedef typename Traits::IteratorGetType GetType;
+ typedef typename HashTableType::ValueTraits::IteratorGetType SourceGetType;
+
+ HashTableIteratorAdapter() = default;
+ HashTableIteratorAdapter(const typename HashTableType::iterator& impl)
+ : impl_(impl) {}
+
+ GetType Get() const {
+ return const_cast<GetType>(SourceGetType(impl_.get()));
+ }
+ typename Traits::IteratorReferenceType operator*() const {
+ return Traits::GetToReferenceConversion(Get());
+ }
+ GetType operator->() const { return Get(); }
+
+ HashTableIteratorAdapter& operator++() {
+ ++impl_;
+ return *this;
+ }
+ // postfix ++ intentionally omitted
+
+ operator HashTableConstIteratorAdapter<HashTableType, Traits>() {
+ typename HashTableType::const_iterator i = impl_;
+ return i;
+ }
+
+ typename HashTableType::iterator impl_;
+};
+
+template <typename HashTable, typename Traits>
+std::ostream& operator<<(
+ std::ostream& stream,
+ const HashTableIteratorAdapter<HashTable, Traits>& iterator) {
+ return stream << iterator.impl_;
+}
+
+template <typename T, typename U>
+inline bool operator==(const HashTableConstIteratorAdapter<T, U>& a,
+ const HashTableConstIteratorAdapter<T, U>& b) {
+ return a.impl_ == b.impl_;
+}
+
+template <typename T, typename U>
+inline bool operator!=(const HashTableConstIteratorAdapter<T, U>& a,
+ const HashTableConstIteratorAdapter<T, U>& b) {
+ return a.impl_ != b.impl_;
+}
+
+template <typename T, typename U>
+inline bool operator==(const HashTableIteratorAdapter<T, U>& a,
+ const HashTableIteratorAdapter<T, U>& b) {
+ return a.impl_ == b.impl_;
+}
+
+template <typename T, typename U>
+inline bool operator!=(const HashTableIteratorAdapter<T, U>& a,
+ const HashTableIteratorAdapter<T, U>& b) {
+ return a.impl_ != b.impl_;
+}
+
+// All 4 combinations of ==, != and Const,non const.
+template <typename T, typename U>
+inline bool operator==(const HashTableConstIteratorAdapter<T, U>& a,
+ const HashTableIteratorAdapter<T, U>& b) {
+ return a.impl_ == b.impl_;
+}
+
+template <typename T, typename U>
+inline bool operator!=(const HashTableConstIteratorAdapter<T, U>& a,
+ const HashTableIteratorAdapter<T, U>& b) {
+ return a.impl_ != b.impl_;
+}
+
+template <typename T, typename U>
+inline bool operator==(const HashTableIteratorAdapter<T, U>& a,
+ const HashTableConstIteratorAdapter<T, U>& b) {
+ return a.impl_ == b.impl_;
+}
+
+template <typename T, typename U>
+inline bool operator!=(const HashTableIteratorAdapter<T, U>& a,
+ const HashTableConstIteratorAdapter<T, U>& b) {
+ return a.impl_ != b.impl_;
+}
+
+template <typename Collection1, typename Collection2>
+inline void RemoveAll(Collection1& collection,
+ const Collection2& to_be_removed) {
+ if (collection.IsEmpty() || to_be_removed.IsEmpty())
+ return;
+ typedef typename Collection2::const_iterator CollectionIterator;
+ CollectionIterator end(to_be_removed.end());
+ for (CollectionIterator it(to_be_removed.begin()); it != end; ++it)
+ collection.erase(*it);
+}
+
+} // namespace WTF
+
+#include "third_party/blink/renderer/platform/wtf/hash_iterators.h"
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_HASH_TABLE_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/hash_table_deleted_value_type.h b/chromium/third_party/blink/renderer/platform/wtf/hash_table_deleted_value_type.h
new file mode 100644
index 00000000000..f440336e301
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/hash_table_deleted_value_type.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_HASH_TABLE_DELETED_VALUE_TYPE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_HASH_TABLE_DELETED_VALUE_TYPE_H_
+
+namespace WTF {
+
+enum HashTableDeletedValueType { kHashTableDeletedValue };
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_HASH_TABLE_DELETED_VALUE_TYPE_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/hash_traits.h b/chromium/third_party/blink/renderer/platform/wtf/hash_traits.h
new file mode 100644
index 00000000000..59ae473c8ee
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/hash_traits.h
@@ -0,0 +1,470 @@
+/*
+ * Copyright (C) 2005, 2006, 2007, 2008, 2011, 2012 Apple Inc. All rights
+ * reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_HASH_TRAITS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_HASH_TRAITS_H_
+
+#include <string.h> // For memset.
+#include <limits>
+#include <memory>
+#include <type_traits>
+#include <utility>
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/hash_functions.h"
+#include "third_party/blink/renderer/platform/wtf/hash_table_deleted_value_type.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/type_traits.h"
+
+namespace WTF {
+
+template <bool isInteger, typename T>
+struct GenericHashTraitsBase;
+template <bool is_enum, typename T>
+struct EnumOrGenericHashTraits;
+template <typename T>
+struct HashTraits;
+
+template <typename T>
+struct GenericHashTraitsBase<false, T> {
+ // The emptyValueIsZero flag is used to optimize allocation of empty hash
+ // tables with zeroed memory.
+ static const bool kEmptyValueIsZero = false;
+
+ // The hasIsEmptyValueFunction flag allows the hash table to automatically
+ // generate code to check for the empty value when it can be done with the
+ // equality operator, but allows custom functions for cases like String that
+ // need them.
+ static const bool kHasIsEmptyValueFunction = false;
+
+// The starting table size. Can be overridden when we know beforehand that a
+// hash table will have at least N entries.
+#if defined(MEMORY_SANITIZER_INITIAL_SIZE)
+ static const unsigned kMinimumTableSize = 1;
+#else
+ static const unsigned kMinimumTableSize = 8;
+#endif
+
+ // When a hash table backing store is traced, its elements will be
+ // traced if their class type has a trace method. However, weak-referenced
+ // elements should not be traced then, but handled by the weak processing
+ // phase that follows.
+ template <typename U = void>
+ struct IsTraceableInCollection {
+ static const bool value = IsTraceable<T>::value && !IsWeak<T>::value;
+ };
+
+ // The NeedsToForbidGCOnMove flag is used to make the hash table move
+ // operations safe when GC is enabled: if a move constructor invokes
+ // an allocation triggering the GC then it should be invoked within GC
+ // forbidden scope.
+ template <typename U = void>
+ struct NeedsToForbidGCOnMove {
+ // TODO(yutak): Consider using of std:::is_trivially_move_constructible
+ // when it is accessible.
+ static const bool value = !std::is_pod<T>::value;
+ };
+
+ static const WeakHandlingFlag kWeakHandlingFlag =
+ IsWeak<T>::value ? kWeakHandling : kNoWeakHandling;
+
+ static constexpr bool kCanHaveDeletedValue = true;
+};
+
+// Default integer traits disallow both 0 and -1 as keys (max value instead of
+// -1 for unsigned).
+template <typename T>
+struct GenericHashTraitsBase<true, T> : GenericHashTraitsBase<false, T> {
+ static const bool kEmptyValueIsZero = true;
+ static void ConstructDeletedValue(T& slot, bool) {
+ slot = static_cast<T>(-1);
+ }
+ static bool IsDeletedValue(T value) { return value == static_cast<T>(-1); }
+};
+
+template <typename T>
+struct GenericHashTraits
+ : GenericHashTraitsBase<std::is_integral<T>::value, T> {
+ typedef T TraitType;
+ typedef T EmptyValueType;
+
+ static T EmptyValue() { return T(); }
+
+ // Type for functions that do not take ownership, such as contains.
+ typedef const T& PeekInType;
+ typedef T* IteratorGetType;
+ typedef const T* IteratorConstGetType;
+ typedef T& IteratorReferenceType;
+ typedef const T& IteratorConstReferenceType;
+ static IteratorReferenceType GetToReferenceConversion(IteratorGetType x) {
+ return *x;
+ }
+ static IteratorConstReferenceType GetToReferenceConstConversion(
+ IteratorConstGetType x) {
+ return *x;
+ }
+
+ template <typename IncomingValueType>
+ static void Store(IncomingValueType&& value, T& storage) {
+ storage = std::forward<IncomingValueType>(value);
+ }
+
+ // Type for return value of functions that do not transfer ownership, such
+ // as get.
+ // FIXME: We could change this type to const T& for better performance if we
+ // figured out a way to handle the return value from emptyValue, which is a
+ // temporary.
+ typedef T PeekOutType;
+ static const T& Peek(const T& value) { return value; }
+};
+
+template <typename T>
+struct EnumOrGenericHashTraits<false, T> : GenericHashTraits<T> {};
+
+// Default traits for an enum type. 0 is very popular, and -1 is also popular.
+// So we use -128 and -127.
+template <typename T>
+struct EnumOrGenericHashTraits<true, T> : GenericHashTraits<T> {
+ static const bool kEmptyValueIsZero = false;
+ static T EmptyValue() { return static_cast<T>(-128); }
+ static void ConstructDeletedValue(T& slot, bool) {
+ slot = static_cast<T>(-127);
+ }
+ static bool IsDeletedValue(T value) { return value == static_cast<T>(-127); }
+};
+
+template <typename T>
+struct HashTraits : EnumOrGenericHashTraits<std::is_enum<T>::value, T> {};
+
+template <typename T>
+struct FloatHashTraits : GenericHashTraits<T> {
+ static T EmptyValue() { return std::numeric_limits<T>::infinity(); }
+ static void ConstructDeletedValue(T& slot, bool) {
+ slot = -std::numeric_limits<T>::infinity();
+ }
+ static bool IsDeletedValue(T value) {
+ return value == -std::numeric_limits<T>::infinity();
+ }
+};
+
+template <>
+struct HashTraits<float> : FloatHashTraits<float> {};
+template <>
+struct HashTraits<double> : FloatHashTraits<double> {};
+
+// Default unsigned traits disallow both 0 and max as keys -- use these traits
+// to allow zero and disallow max - 1.
+template <typename T>
+struct UnsignedWithZeroKeyHashTraits : GenericHashTraits<T> {
+ static const bool kEmptyValueIsZero = false;
+ static T EmptyValue() { return std::numeric_limits<T>::max(); }
+ static void ConstructDeletedValue(T& slot, bool) {
+ slot = std::numeric_limits<T>::max() - 1;
+ }
+ static bool IsDeletedValue(T value) {
+ return value == std::numeric_limits<T>::max() - 1;
+ }
+};
+
+template <typename P>
+struct HashTraits<P*> : GenericHashTraits<P*> {
+ static const bool kEmptyValueIsZero = true;
+ static void ConstructDeletedValue(P*& slot, bool) {
+ slot = reinterpret_cast<P*>(-1);
+ }
+ static bool IsDeletedValue(P* value) {
+ return value == reinterpret_cast<P*>(-1);
+ }
+};
+
+template <typename T>
+struct SimpleClassHashTraits : GenericHashTraits<T> {
+ static const bool kEmptyValueIsZero = true;
+ template <typename U = void>
+ struct NeedsToForbidGCOnMove {
+ static const bool value = false;
+ };
+ static void ConstructDeletedValue(T& slot, bool) {
+ new (NotNull, &slot) T(kHashTableDeletedValue);
+ }
+ static bool IsDeletedValue(const T& value) {
+ return value.IsHashTableDeletedValue();
+ }
+};
+
+template <typename P>
+struct HashTraits<scoped_refptr<P>> : SimpleClassHashTraits<scoped_refptr<P>> {
+ static_assert(sizeof(void*) == sizeof(scoped_refptr<P>),
+ "Unexpected RefPtr size."
+ " RefPtr needs to be single pointer to support deleted value.");
+
+ class RefPtrValuePeeker {
+ DISALLOW_NEW();
+
+ public:
+ ALWAYS_INLINE RefPtrValuePeeker(P* p) : ptr_(p) {}
+ template <typename U>
+ RefPtrValuePeeker(const scoped_refptr<U>& p) : ptr_(p.get()) {}
+
+ ALWAYS_INLINE operator P*() const { return ptr_; }
+
+ private:
+ P* ptr_;
+ };
+
+ typedef std::nullptr_t EmptyValueType;
+ static EmptyValueType EmptyValue() { return nullptr; }
+
+ static const bool kHasIsEmptyValueFunction = true;
+ static bool IsEmptyValue(const scoped_refptr<P>& value) { return !value; }
+
+ static bool IsDeletedValue(const scoped_refptr<P>& value) {
+ return *reinterpret_cast<void* const*>(&value) ==
+ reinterpret_cast<const void*>(-1);
+ }
+
+ static void ConstructDeletedValue(scoped_refptr<P>& slot, bool zero_value) {
+ *reinterpret_cast<void**>(&slot) = reinterpret_cast<void*>(-1);
+ }
+
+ typedef RefPtrValuePeeker PeekInType;
+ typedef scoped_refptr<P>* IteratorGetType;
+ typedef const scoped_refptr<P>* IteratorConstGetType;
+ typedef scoped_refptr<P>& IteratorReferenceType;
+ typedef const scoped_refptr<P>& IteratorConstReferenceType;
+ static IteratorReferenceType GetToReferenceConversion(IteratorGetType x) {
+ return *x;
+ }
+ static IteratorConstReferenceType GetToReferenceConstConversion(
+ IteratorConstGetType x) {
+ return *x;
+ }
+
+ static void Store(scoped_refptr<P> value, scoped_refptr<P>& storage) {
+ storage = std::move(value);
+ }
+
+ typedef P* PeekOutType;
+ static PeekOutType Peek(const scoped_refptr<P>& value) { return value.get(); }
+};
+
+template <typename T>
+struct HashTraits<std::unique_ptr<T>>
+ : SimpleClassHashTraits<std::unique_ptr<T>> {
+ using EmptyValueType = std::nullptr_t;
+ static EmptyValueType EmptyValue() { return nullptr; }
+
+ static const bool kHasIsEmptyValueFunction = true;
+ static bool IsEmptyValue(const std::unique_ptr<T>& value) { return !value; }
+
+ using PeekInType = T*;
+
+ static void Store(std::unique_ptr<T>&& value, std::unique_ptr<T>& storage) {
+ storage = std::move(value);
+ }
+
+ using PeekOutType = T*;
+ static PeekOutType Peek(const std::unique_ptr<T>& value) {
+ return value.get();
+ }
+
+ static void ConstructDeletedValue(std::unique_ptr<T>& slot, bool) {
+ // Dirty trick: implant an invalid pointer to unique_ptr. Destructor isn't
+ // called for deleted buckets, so this is okay.
+ new (NotNull, &slot) std::unique_ptr<T>(reinterpret_cast<T*>(1u));
+ }
+ static bool IsDeletedValue(const std::unique_ptr<T>& value) {
+ return value.get() == reinterpret_cast<T*>(1u);
+ }
+};
+
+template <>
+struct HashTraits<String> : SimpleClassHashTraits<String> {
+ static const bool kHasIsEmptyValueFunction = true;
+ static bool IsEmptyValue(const String&);
+ static bool IsDeletedValue(const String& value);
+ static void ConstructDeletedValue(String& slot, bool zero_value);
+};
+
+// This struct template is an implementation detail of the
+// isHashTraitsEmptyValue function, which selects either the emptyValue function
+// or the isEmptyValue function to check for empty values.
+template <typename Traits, bool hasEmptyValueFunction>
+struct HashTraitsEmptyValueChecker;
+template <typename Traits>
+struct HashTraitsEmptyValueChecker<Traits, true> {
+ template <typename T>
+ static bool IsEmptyValue(const T& value) {
+ return Traits::IsEmptyValue(value);
+ }
+};
+template <typename Traits>
+struct HashTraitsEmptyValueChecker<Traits, false> {
+ template <typename T>
+ static bool IsEmptyValue(const T& value) {
+ return value == Traits::EmptyValue();
+ }
+};
+template <typename Traits, typename T>
+inline bool IsHashTraitsEmptyValue(const T& value) {
+ return HashTraitsEmptyValueChecker<
+ Traits, Traits::kHasIsEmptyValueFunction>::IsEmptyValue(value);
+}
+
+template <typename FirstTraitsArg, typename SecondTraitsArg>
+struct PairHashTraits
+ : GenericHashTraits<std::pair<typename FirstTraitsArg::TraitType,
+ typename SecondTraitsArg::TraitType>> {
+ typedef FirstTraitsArg FirstTraits;
+ typedef SecondTraitsArg SecondTraits;
+ typedef std::pair<typename FirstTraits::TraitType,
+ typename SecondTraits::TraitType>
+ TraitType;
+ typedef std::pair<typename FirstTraits::EmptyValueType,
+ typename SecondTraits::EmptyValueType>
+ EmptyValueType;
+
+ static const bool kEmptyValueIsZero =
+ FirstTraits::kEmptyValueIsZero && SecondTraits::kEmptyValueIsZero;
+ static EmptyValueType EmptyValue() {
+ return std::make_pair(FirstTraits::EmptyValue(),
+ SecondTraits::EmptyValue());
+ }
+
+ static const bool kHasIsEmptyValueFunction =
+ FirstTraits::kHasIsEmptyValueFunction ||
+ SecondTraits::kHasIsEmptyValueFunction;
+ static bool IsEmptyValue(const TraitType& value) {
+ return IsHashTraitsEmptyValue<FirstTraits>(value.first) &&
+ IsHashTraitsEmptyValue<SecondTraits>(value.second);
+ }
+
+ static const unsigned kMinimumTableSize = FirstTraits::kMinimumTableSize;
+
+ static void ConstructDeletedValue(TraitType& slot, bool zero_value) {
+ FirstTraits::ConstructDeletedValue(slot.first, zero_value);
+ // For GC collections the memory for the backing is zeroed when it is
+ // allocated, and the constructors may take advantage of that,
+ // especially if a GC occurs during insertion of an entry into the
+ // table. This slot is being marked deleted, but If the slot is reused
+ // at a later point, the same assumptions around memory zeroing must
+ // hold as they did at the initial allocation. Therefore we zero the
+ // value part of the slot here for GC collections.
+ if (zero_value)
+ memset(reinterpret_cast<void*>(&slot.second), 0, sizeof(slot.second));
+ }
+ static bool IsDeletedValue(const TraitType& value) {
+ return FirstTraits::IsDeletedValue(value.first);
+ }
+};
+
+template <typename First, typename Second>
+struct HashTraits<std::pair<First, Second>>
+ : public PairHashTraits<HashTraits<First>, HashTraits<Second>> {};
+
+template <typename KeyTypeArg, typename ValueTypeArg>
+struct KeyValuePair {
+ typedef KeyTypeArg KeyType;
+
+ template <typename IncomingKeyType, typename IncomingValueType>
+ KeyValuePair(IncomingKeyType&& key, IncomingValueType&& value)
+ : key(std::forward<IncomingKeyType>(key)),
+ value(std::forward<IncomingValueType>(value)) {}
+
+ template <typename OtherKeyType, typename OtherValueType>
+ KeyValuePair(KeyValuePair<OtherKeyType, OtherValueType>&& other)
+ : key(std::move(other.key)), value(std::move(other.value)) {}
+
+ KeyTypeArg key;
+ ValueTypeArg value;
+};
+
+template <typename KeyTraitsArg, typename ValueTraitsArg>
+struct KeyValuePairHashTraits
+ : GenericHashTraits<KeyValuePair<typename KeyTraitsArg::TraitType,
+ typename ValueTraitsArg::TraitType>> {
+ typedef KeyTraitsArg KeyTraits;
+ typedef ValueTraitsArg ValueTraits;
+ typedef KeyValuePair<typename KeyTraits::TraitType,
+ typename ValueTraits::TraitType>
+ TraitType;
+ typedef KeyValuePair<typename KeyTraits::EmptyValueType,
+ typename ValueTraits::EmptyValueType>
+ EmptyValueType;
+
+ static const bool kEmptyValueIsZero =
+ KeyTraits::kEmptyValueIsZero && ValueTraits::kEmptyValueIsZero;
+ static EmptyValueType EmptyValue() {
+ return KeyValuePair<typename KeyTraits::EmptyValueType,
+ typename ValueTraits::EmptyValueType>(
+ KeyTraits::EmptyValue(), ValueTraits::EmptyValue());
+ }
+
+ template <typename U = void>
+ struct IsTraceableInCollection {
+ static const bool value = IsTraceableInCollectionTrait<KeyTraits>::value ||
+ IsTraceableInCollectionTrait<ValueTraits>::value;
+ };
+
+ template <typename U = void>
+ struct NeedsToForbidGCOnMove {
+ static const bool value =
+ KeyTraits::template NeedsToForbidGCOnMove<>::value ||
+ ValueTraits::template NeedsToForbidGCOnMove<>::value;
+ };
+
+ static const WeakHandlingFlag kWeakHandlingFlag =
+ (KeyTraits::kWeakHandlingFlag == kWeakHandling ||
+ ValueTraits::kWeakHandlingFlag == kWeakHandling)
+ ? kWeakHandling
+ : kNoWeakHandling;
+
+ static const unsigned kMinimumTableSize = KeyTraits::kMinimumTableSize;
+
+ static void ConstructDeletedValue(TraitType& slot, bool zero_value) {
+ KeyTraits::ConstructDeletedValue(slot.key, zero_value);
+ // See similar code in this file for why we need to do this.
+ if (zero_value)
+ memset(reinterpret_cast<void*>(&slot.value), 0, sizeof(slot.value));
+ }
+ static bool IsDeletedValue(const TraitType& value) {
+ return KeyTraits::IsDeletedValue(value.key);
+ }
+};
+
+template <typename Key, typename Value>
+struct HashTraits<KeyValuePair<Key, Value>>
+ : public KeyValuePairHashTraits<HashTraits<Key>, HashTraits<Value>> {};
+
+template <typename T>
+struct NullableHashTraits : public HashTraits<T> {
+ static const bool kEmptyValueIsZero = false;
+ static T EmptyValue() { return reinterpret_cast<T>(1); }
+};
+
+} // namespace WTF
+
+using WTF::HashTraits;
+using WTF::PairHashTraits;
+using WTF::NullableHashTraits;
+using WTF::SimpleClassHashTraits;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_HASH_TRAITS_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/hex_number.h b/chromium/third_party/blink/renderer/platform/wtf/hex_number.h
new file mode 100644
index 00000000000..06f038c7c16
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/hex_number.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2011 Research In Motion Limited. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_HEX_NUMBER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_HEX_NUMBER_H_
+
+#include "third_party/blink/renderer/platform/wtf/text/string_concatenate.h"
+
+namespace WTF {
+
+namespace Internal {
+
+const LChar kLowerHexDigits[17] = "0123456789abcdef";
+const LChar kUpperHexDigits[17] = "0123456789ABCDEF";
+
+} // namespace Internal
+
+class HexNumber final {
+ STATIC_ONLY(HexNumber);
+
+ public:
+ enum HexConversionMode { kLowercase, kUppercase };
+
+ template <typename T>
+ static inline void AppendByteAsHex(unsigned char byte,
+ T& destination,
+ HexConversionMode mode = kUppercase) {
+ const LChar* hex_digits = HexDigitsForMode(mode);
+ destination.Append(hex_digits[byte >> 4]);
+ destination.Append(hex_digits[byte & 0xF]);
+ }
+
+ static inline void AppendByteAsHex(unsigned char byte,
+ Vector<LChar>& destination,
+ HexConversionMode mode = kUppercase) {
+ const LChar* hex_digits = HexDigitsForMode(mode);
+ destination.push_back(hex_digits[byte >> 4]);
+ destination.push_back(hex_digits[byte & 0xF]);
+ }
+
+ static inline void AppendByteAsHex(unsigned char byte,
+ Vector<char>& destination,
+ HexConversionMode mode = kUppercase) {
+ const LChar* hex_digits = HexDigitsForMode(mode);
+ destination.push_back(hex_digits[byte >> 4]);
+ destination.push_back(hex_digits[byte & 0xF]);
+ }
+
+ template <typename T>
+ static inline void AppendUnsignedAsHex(unsigned number,
+ T& destination,
+ HexConversionMode mode = kUppercase) {
+ const LChar* hex_digits = HexDigitsForMode(mode);
+ Vector<LChar, 8> result;
+ do {
+ result.push_front(hex_digits[number % 16]);
+ number >>= 4;
+ } while (number > 0);
+
+ destination.Append(result.data(), result.size());
+ }
+
+ // Same as appendUnsignedAsHex, but using exactly 'desiredDigits' for the
+ // conversion.
+ template <typename T>
+ static inline void AppendUnsignedAsHexFixedSize(
+ unsigned number,
+ T& destination,
+ unsigned desired_digits,
+ HexConversionMode mode = kUppercase) {
+ DCHECK(desired_digits);
+
+ const LChar* hex_digits = HexDigitsForMode(mode);
+ Vector<LChar, 8> result;
+ do {
+ result.push_front(hex_digits[number % 16]);
+ number >>= 4;
+ } while (result.size() < desired_digits);
+
+ DCHECK_EQ(result.size(), desired_digits);
+ destination.Append(result.data(), result.size());
+ }
+
+ private:
+ static inline const LChar* HexDigitsForMode(HexConversionMode mode) {
+ return mode == kLowercase ? Internal::kLowerHexDigits
+ : Internal::kUpperHexDigits;
+ }
+};
+
+} // namespace WTF
+
+using WTF::HexNumber;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_HEX_NUMBER_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/leak_annotations.h b/chromium/third_party/blink/renderer/platform/wtf/leak_annotations.h
new file mode 100644
index 00000000000..d55c200e058
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/leak_annotations.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ * Copyright (C) 2013 Samsung Electronics. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_LEAK_ANNOTATIONS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_LEAK_ANNOTATIONS_H_
+
+// This file defines macros for working with LeakSanitizer, allowing memory
+// and allocations to be registered as exempted from LSan consideration.
+
+#include "base/macros.h"
+#if defined(LEAK_SANITIZER)
+#include "third_party/blink/renderer/platform/wtf/address_sanitizer.h"
+#include "third_party/blink/renderer/platform/wtf/type_traits.h"
+#endif
+
+namespace WTF {
+
+#if defined(LEAK_SANITIZER)
+class LeakSanitizerDisabler {
+ public:
+ LeakSanitizerDisabler() { __lsan_disable(); }
+
+ ~LeakSanitizerDisabler() { __lsan_enable(); }
+
+ DISALLOW_COPY_AND_ASSIGN(LeakSanitizerDisabler);
+};
+
+// WTF_INTERNAL_LEAK_SANITIZER_DISABLED_SCOPE: all allocations made in the
+// current scope will be exempted from LSan consideration. Only to be
+// used internal to wtf/, Blink should use LEAK_SANITIZER_DISABLED_SCOPE
+// elsewhere.
+//
+// TODO(sof): once layering rules allow wtf/ to make use of the Oilpan
+// infrastructure, remove this macro.
+#define WTF_INTERNAL_LEAK_SANITIZER_DISABLED_SCOPE \
+ WTF::LeakSanitizerDisabler leakSanitizerDisabler; \
+ static_cast<void>(0)
+
+// LEAK_SANITIZER_IGNORE_OBJECT(X): the heap object referenced by pointer X
+// will be ignored by LSan.
+//
+// "Ignorance" means that LSan's reachability traversal is stopped short
+// upon encountering an ignored memory chunk. Consequently, LSan will not
+// scan an ignored memory chunk for live, reachable pointers. However, should
+// those embedded pointers be reachable by some other path, they will be
+// reported as leaking.
+#define LEAK_SANITIZER_IGNORE_OBJECT(X) __lsan_ignore_object(X)
+
+// If the object pointed to by the static local is on the Oilpan heap, a strong
+// Persistent<> is created to keep the pointed-to heap object alive. This makes
+// both the Persistent<> and the heap object _reachable_ by LeakSanitizer's leak
+// detection pass. We do not want these intentional leaks to be reported by
+// LSan, hence the static local is registered with Oilpan
+// (see RegisterStaticLocalReference<> below.)
+//
+// Upon Blink shutdown, all the registered statics are released and a final
+// round of GCs are performed to sweep out their now-unreachable object graphs.
+// The end result being a tidied heap that the LeakSanitizer can then scan to
+// report real leaks.
+//
+// The CanRegisterStaticLocalReference<> and RegisterStaticLocalReference<>
+// templates arrange for this -- for a class type T, a registerStatic()
+// implementation is provided if "T* T::registerAsStaticReference(T*)" is a
+// method on T (inherited or otherwise.)
+//
+// An empty, trivial registerStatic() method is provided for all other class
+// types T.
+template <typename T>
+class CanRegisterStaticLocalReference {
+ typedef char YesType;
+ typedef struct NoType { char padding[8]; } NoType;
+
+ // Check if class T has public method "T* registerAsStaticReference()".
+ template <typename V>
+ static YesType checkHasRegisterAsStaticReferenceMethod(
+ V* p,
+ typename std::enable_if<IsSubclass<
+ V,
+ typename std::remove_pointer<decltype(
+ p->registerAsStaticReference())>::type>::value>::type* = 0);
+ template <typename V>
+ static NoType checkHasRegisterAsStaticReferenceMethod(...);
+
+ public:
+ static const bool value =
+ sizeof(YesType) + sizeof(T) ==
+ sizeof(checkHasRegisterAsStaticReferenceMethod<T>(nullptr)) + sizeof(T);
+};
+
+template <typename T, bool = CanRegisterStaticLocalReference<T>::value>
+class RegisterStaticLocalReference {
+ public:
+ static T* registerStatic(T* ptr) { return ptr; }
+};
+
+template <typename T>
+class RegisterStaticLocalReference<T, true> {
+ public:
+ static T* registerStatic(T* ptr) {
+ return static_cast<T*>(ptr->registerAsStaticReference());
+ }
+};
+
+#define LEAK_SANITIZER_REGISTER_STATIC_LOCAL(Type, Object) \
+ WTF::RegisterStaticLocalReference<Type>::registerStatic(Object)
+#else
+#define WTF_INTERNAL_LEAK_SANITIZER_DISABLED_SCOPE
+#define LEAK_SANITIZER_IGNORE_OBJECT(X) ((void)0)
+#define LEAK_SANITIZER_REGISTER_STATIC_LOCAL(Type, Object) Object
+#endif // defined(LEAK_SANITIZER)
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_LEAK_ANNOTATIONS_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/linked_hash_set.h b/chromium/third_party/blink/renderer/platform/wtf/linked_hash_set.h
new file mode 100644
index 00000000000..794a22ef2b5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/linked_hash_set.h
@@ -0,0 +1,945 @@
+/*
+ * Copyright (C) 2005, 2006, 2007, 2008, 2011, 2012 Apple Inc. All rights
+ * reserved.
+ * Copyright (C) 2011, Benjamin Poulain <ikipou@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_LINKED_HASH_SET_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_LINKED_HASH_SET_H_
+
+#include "base/macros.h"
+#include "third_party/blink/renderer/platform/wtf/address_sanitizer.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/partition_allocator.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+
+namespace WTF {
+
+// LinkedHashSet provides a Set interface like HashSet, but also has a
+// predictable iteration order. It has O(1) insertion, removal, and test for
+// containership. It maintains a linked list through its contents such that
+// iterating it yields values in the order in which they were inserted.
+//
+// LinkedHashSet iterators are invalidated by mutation of the set. This means,
+// for example, that you cannot modify the container while iterating
+// over it (this will DCHECK in debug). Instead, you should either copy the
+// entries to a vector before iterating, or maintain a separate list of pending
+// updates.
+//
+// Unlike ListHashSet, this container supports WeakMember<T>.
+template <typename Value,
+ typename HashFunctions,
+ typename HashTraits,
+ typename Allocator>
+class LinkedHashSet;
+
+template <typename LinkedHashSet>
+class LinkedHashSetIterator;
+template <typename LinkedHashSet>
+class LinkedHashSetConstIterator;
+template <typename LinkedHashSet>
+class LinkedHashSetReverseIterator;
+template <typename LinkedHashSet>
+class LinkedHashSetConstReverseIterator;
+
+template <typename Value, typename HashFunctions, typename Allocator>
+struct LinkedHashSetTranslator;
+template <typename Value, typename Allocator>
+struct LinkedHashSetExtractor;
+template <typename Value, typename ValueTraits, typename Allocator>
+struct LinkedHashSetTraits;
+
+class LinkedHashSetNodeBase {
+ DISALLOW_NEW();
+
+ public:
+ LinkedHashSetNodeBase() : prev_(this), next_(this) {}
+
+ NO_SANITIZE_ADDRESS
+ void Unlink() {
+ if (!next_)
+ return;
+ DCHECK(prev_);
+ DCHECK(next_->prev_ == this);
+ DCHECK(prev_->next_ == this);
+ next_->prev_ = prev_;
+ prev_->next_ = next_;
+ }
+
+ ~LinkedHashSetNodeBase() { Unlink(); }
+
+ void InsertBefore(LinkedHashSetNodeBase& other) {
+ other.next_ = this;
+ other.prev_ = prev_;
+ prev_->next_ = &other;
+ prev_ = &other;
+ DCHECK(other.next_);
+ DCHECK(other.prev_);
+ DCHECK(next_);
+ DCHECK(prev_);
+ }
+
+ void InsertAfter(LinkedHashSetNodeBase& other) {
+ other.prev_ = this;
+ other.next_ = next_;
+ next_->prev_ = &other;
+ next_ = &other;
+ DCHECK(other.next_);
+ DCHECK(other.prev_);
+ DCHECK(next_);
+ DCHECK(prev_);
+ }
+
+ LinkedHashSetNodeBase(LinkedHashSetNodeBase* prev,
+ LinkedHashSetNodeBase* next)
+ : prev_(prev), next_(next) {
+ DCHECK((prev && next) || (!prev && !next));
+ }
+
+ LinkedHashSetNodeBase* prev_;
+ LinkedHashSetNodeBase* next_;
+
+ protected:
+ // If we take a copy of a node we can't copy the next and prev pointers,
+ // since they point to something that does not point at us. This is used
+ // inside the shouldExpand() "if" in HashTable::add.
+ LinkedHashSetNodeBase(const LinkedHashSetNodeBase& other)
+ : prev_(nullptr), next_(nullptr) {}
+
+ LinkedHashSetNodeBase(LinkedHashSetNodeBase&& other)
+ : prev_(other.prev_), next_(other.next_) {
+ other.prev_ = nullptr;
+ other.next_ = nullptr;
+ if (next_) {
+ prev_->next_ = this;
+ next_->prev_ = this;
+ }
+ }
+
+ private:
+ // Should not be used.
+ LinkedHashSetNodeBase& operator=(const LinkedHashSetNodeBase& other) = delete;
+};
+
+template <typename ValueArg, typename Allocator>
+class LinkedHashSetNode : public LinkedHashSetNodeBase {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ LinkedHashSetNode(const ValueArg& value,
+ LinkedHashSetNodeBase* prev,
+ LinkedHashSetNodeBase* next)
+ : LinkedHashSetNodeBase(prev, next), value_(value) {}
+
+ LinkedHashSetNode(LinkedHashSetNode&& other)
+ : LinkedHashSetNodeBase(std::move(other)),
+ value_(std::move(other.value_)) {}
+
+ ValueArg value_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(LinkedHashSetNode);
+};
+
+template <typename ValueArg,
+ typename HashFunctions = typename DefaultHash<ValueArg>::Hash,
+ typename TraitsArg = HashTraits<ValueArg>,
+ typename Allocator = PartitionAllocator>
+class LinkedHashSet {
+ USE_ALLOCATOR(LinkedHashSet, Allocator);
+
+ private:
+ typedef ValueArg Value;
+ typedef TraitsArg Traits;
+ typedef LinkedHashSetNode<Value, Allocator> Node;
+ typedef LinkedHashSetNodeBase NodeBase;
+ typedef LinkedHashSetTranslator<Value, HashFunctions, Allocator>
+ NodeHashFunctions;
+ typedef LinkedHashSetTraits<Value, Traits, Allocator> NodeHashTraits;
+
+ typedef HashTable<Node,
+ Node,
+ IdentityExtractor,
+ NodeHashFunctions,
+ NodeHashTraits,
+ NodeHashTraits,
+ Allocator>
+ ImplType;
+
+ public:
+ typedef LinkedHashSetIterator<LinkedHashSet> iterator;
+ friend class LinkedHashSetIterator<LinkedHashSet>;
+ typedef LinkedHashSetConstIterator<LinkedHashSet> const_iterator;
+ friend class LinkedHashSetConstIterator<LinkedHashSet>;
+
+ typedef LinkedHashSetReverseIterator<LinkedHashSet> reverse_iterator;
+ friend class LinkedHashSetReverseIterator<LinkedHashSet>;
+ typedef LinkedHashSetConstReverseIterator<LinkedHashSet>
+ const_reverse_iterator;
+ friend class LinkedHashSetConstReverseIterator<LinkedHashSet>;
+
+ struct AddResult final {
+ STACK_ALLOCATED();
+ AddResult(const typename ImplType::AddResult& hash_table_add_result)
+ : stored_value(&hash_table_add_result.stored_value->value_),
+ is_new_entry(hash_table_add_result.is_new_entry) {}
+
+ Value* stored_value;
+ bool is_new_entry;
+ };
+
+ typedef typename HashTraits<Value>::PeekInType ValuePeekInType;
+
+ LinkedHashSet();
+ LinkedHashSet(const LinkedHashSet&);
+ LinkedHashSet(LinkedHashSet&&);
+ LinkedHashSet& operator=(const LinkedHashSet&);
+ LinkedHashSet& operator=(LinkedHashSet&&);
+
+ // Needs finalization. The anchor needs to unlink itself from the chain.
+ ~LinkedHashSet();
+
+ static void Finalize(void* pointer) {
+ reinterpret_cast<LinkedHashSet*>(pointer)->~LinkedHashSet();
+ }
+ void FinalizeGarbageCollectedObject() { Finalize(this); }
+
+ void Swap(LinkedHashSet&);
+
+ unsigned size() const { return impl_.size(); }
+ unsigned Capacity() const { return impl_.Capacity(); }
+ bool IsEmpty() const { return impl_.IsEmpty(); }
+
+ iterator begin() { return MakeIterator(FirstNode()); }
+ iterator end() { return MakeIterator(Anchor()); }
+ const_iterator begin() const { return MakeConstIterator(FirstNode()); }
+ const_iterator end() const { return MakeConstIterator(Anchor()); }
+
+ reverse_iterator rbegin() { return MakeReverseIterator(LastNode()); }
+ reverse_iterator rend() { return MakeReverseIterator(Anchor()); }
+ const_reverse_iterator rbegin() const {
+ return MakeConstReverseIterator(LastNode());
+ }
+ const_reverse_iterator rend() const {
+ return MakeConstReverseIterator(Anchor());
+ }
+
+ Value& front();
+ const Value& front() const;
+ void RemoveFirst();
+
+ Value& back();
+ const Value& back() const;
+ void pop_back();
+
+ iterator find(ValuePeekInType);
+ const_iterator find(ValuePeekInType) const;
+ bool Contains(ValuePeekInType) const;
+
+ // An alternate version of find() that finds the object by hashing and
+ // comparing with some other type, to avoid the cost of type conversion.
+ // The HashTranslator interface is defined in HashSet.
+ template <typename HashTranslator, typename T>
+ iterator Find(const T&);
+ template <typename HashTranslator, typename T>
+ const_iterator Find(const T&) const;
+ template <typename HashTranslator, typename T>
+ bool Contains(const T&) const;
+
+ // The return value of insert is a pair of a pointer to the stored value,
+ // and a bool that is true if an new entry was added.
+ template <typename IncomingValueType>
+ AddResult insert(IncomingValueType&&);
+
+ // Same as insert() except that the return value is an
+ // iterator. Useful in cases where it's needed to have the
+ // same return value as find() and where it's not possible to
+ // use a pointer to the storedValue.
+ template <typename IncomingValueType>
+ iterator AddReturnIterator(IncomingValueType&&);
+
+ // Add the value to the end of the collection. If the value was already in
+ // the list, it is moved to the end.
+ template <typename IncomingValueType>
+ AddResult AppendOrMoveToLast(IncomingValueType&&);
+
+ // Add the value to the beginning of the collection. If the value was already
+ // in the list, it is moved to the beginning.
+ template <typename IncomingValueType>
+ AddResult PrependOrMoveToFirst(IncomingValueType&&);
+
+ template <typename IncomingValueType>
+ AddResult InsertBefore(ValuePeekInType before_value,
+ IncomingValueType&& new_value);
+ template <typename IncomingValueType>
+ AddResult InsertBefore(iterator it, IncomingValueType&& new_value) {
+ return impl_.template insert<NodeHashFunctions>(
+ std::forward<IncomingValueType>(new_value), it.GetNode());
+ }
+
+ void erase(ValuePeekInType);
+ void erase(iterator);
+ void clear() { impl_.clear(); }
+ template <typename Collection>
+ void RemoveAll(const Collection& other) {
+ WTF::RemoveAll(*this, other);
+ }
+
+ template <typename VisitorDispatcher>
+ void Trace(VisitorDispatcher visitor) {
+ impl_.Trace(visitor);
+ // Should the underlying table be moved by GC, register a callback
+ // that fixes up the interior pointers that the (Heap)LinkedHashSet keeps.
+ if (impl_.table_) {
+ Allocator::RegisterBackingStoreCallback(
+ visitor, impl_.table_, MoveBackingCallback,
+ reinterpret_cast<void*>(&anchor_));
+ }
+ }
+
+ int64_t Modifications() const { return impl_.Modifications(); }
+ void CheckModifications(int64_t mods) const {
+ impl_.CheckModifications(mods);
+ }
+
+ private:
+ Node* Anchor() { return reinterpret_cast<Node*>(&anchor_); }
+ const Node* Anchor() const { return reinterpret_cast<const Node*>(&anchor_); }
+ Node* FirstNode() { return reinterpret_cast<Node*>(anchor_.next_); }
+ const Node* FirstNode() const {
+ return reinterpret_cast<const Node*>(anchor_.next_);
+ }
+ Node* LastNode() { return reinterpret_cast<Node*>(anchor_.prev_); }
+ const Node* LastNode() const {
+ return reinterpret_cast<const Node*>(anchor_.prev_);
+ }
+
+ iterator MakeIterator(const Node* position) {
+ return iterator(position, this);
+ }
+ const_iterator MakeConstIterator(const Node* position) const {
+ return const_iterator(position, this);
+ }
+ reverse_iterator MakeReverseIterator(const Node* position) {
+ return reverse_iterator(position, this);
+ }
+ const_reverse_iterator MakeConstReverseIterator(const Node* position) const {
+ return const_reverse_iterator(position, this);
+ }
+
+ static void MoveBackingCallback(void* anchor,
+ void* from,
+ void* to,
+ size_t size) {
+ // Note: the hash table move may have been overlapping; linearly scan the
+ // entire table and fixup interior pointers into the old region with
+ // correspondingly offset ones into the new.
+ size_t table_size = size / sizeof(Node);
+ Node* table = reinterpret_cast<Node*>(to);
+ NodeBase* from_start = reinterpret_cast<NodeBase*>(from);
+ NodeBase* from_end =
+ reinterpret_cast<NodeBase*>(reinterpret_cast<uintptr_t>(from) + size);
+ for (Node* element = table + table_size - 1; element >= table; element--) {
+ Node& node = *element;
+ if (ImplType::IsEmptyOrDeletedBucket(node))
+ continue;
+ if (node.next_ >= from_start && node.next_ < from_end) {
+ size_t diff = reinterpret_cast<uintptr_t>(node.next_) -
+ reinterpret_cast<uintptr_t>(from);
+ node.next_ =
+ reinterpret_cast<NodeBase*>(reinterpret_cast<uintptr_t>(to) + diff);
+ }
+ if (node.prev_ >= from_start && node.prev_ < from_end) {
+ size_t diff = reinterpret_cast<uintptr_t>(node.prev_) -
+ reinterpret_cast<uintptr_t>(from);
+ node.prev_ =
+ reinterpret_cast<NodeBase*>(reinterpret_cast<uintptr_t>(to) + diff);
+ }
+ }
+ NodeBase* anchor_node = reinterpret_cast<NodeBase*>(anchor);
+ if (anchor_node->next_ >= from_start && anchor_node->next_ < from_end) {
+ size_t diff = reinterpret_cast<uintptr_t>(anchor_node->next_) -
+ reinterpret_cast<uintptr_t>(from);
+ anchor_node->next_ =
+ reinterpret_cast<NodeBase*>(reinterpret_cast<uintptr_t>(to) + diff);
+ }
+ if (anchor_node->prev_ >= from_start && anchor_node->prev_ < from_end) {
+ size_t diff = reinterpret_cast<uintptr_t>(anchor_node->prev_) -
+ reinterpret_cast<uintptr_t>(from);
+ anchor_node->prev_ =
+ reinterpret_cast<NodeBase*>(reinterpret_cast<uintptr_t>(to) + diff);
+ }
+ }
+
+ ImplType impl_;
+ NodeBase anchor_;
+};
+
+template <typename Value, typename HashFunctions, typename Allocator>
+struct LinkedHashSetTranslator {
+ STATIC_ONLY(LinkedHashSetTranslator);
+ typedef LinkedHashSetNode<Value, Allocator> Node;
+ typedef LinkedHashSetNodeBase NodeBase;
+ typedef typename HashTraits<Value>::PeekInType ValuePeekInType;
+ static unsigned GetHash(const Node& node) {
+ return HashFunctions::GetHash(node.value_);
+ }
+ static unsigned GetHash(const ValuePeekInType& key) {
+ return HashFunctions::GetHash(key);
+ }
+ static bool Equal(const Node& a, const ValuePeekInType& b) {
+ return HashFunctions::Equal(a.value_, b);
+ }
+ static bool Equal(const Node& a, const Node& b) {
+ return HashFunctions::Equal(a.value_, b.value_);
+ }
+ template <typename IncomingValueType>
+ static void Translate(Node& location,
+ IncomingValueType&& key,
+ NodeBase* anchor) {
+ anchor->InsertBefore(location);
+ location.value_ = std::forward<IncomingValueType>(key);
+ }
+
+ // Empty (or deleted) slots have the next_ pointer set to null, but we
+ // don't do anything to the other fields, which may contain junk.
+ // Therefore you can't compare a newly constructed empty value with a
+ // slot and get the right answer.
+ static const bool safe_to_compare_to_empty_or_deleted = false;
+};
+
+template <typename Value, typename Allocator>
+struct LinkedHashSetExtractor {
+ STATIC_ONLY(LinkedHashSetExtractor);
+ static const Value& Extract(const LinkedHashSetNode<Value, Allocator>& node) {
+ return node.value_;
+ }
+};
+
+template <typename Value, typename ValueTraitsArg, typename Allocator>
+struct LinkedHashSetTraits
+ : public SimpleClassHashTraits<LinkedHashSetNode<Value, Allocator>> {
+ STATIC_ONLY(LinkedHashSetTraits);
+ typedef LinkedHashSetNode<Value, Allocator> Node;
+ typedef ValueTraitsArg ValueTraits;
+
+ // The slot is empty when the next_ field is zero so it's safe to zero
+ // the backing.
+ static const bool kEmptyValueIsZero = true;
+
+ static const bool kHasIsEmptyValueFunction = true;
+ static bool IsEmptyValue(const Node& node) { return !node.next_; }
+
+ static const int kDeletedValue = -1;
+
+ static void ConstructDeletedValue(Node& slot, bool) {
+ slot.next_ = reinterpret_cast<Node*>(kDeletedValue);
+ }
+ static bool IsDeletedValue(const Node& slot) {
+ return slot.next_ == reinterpret_cast<Node*>(kDeletedValue);
+ }
+
+ // Whether we need to trace and do weak processing depends on the traits of
+ // the type inside the node.
+ template <typename U = void>
+ struct IsTraceableInCollection {
+ STATIC_ONLY(IsTraceableInCollection);
+ static const bool value =
+ ValueTraits::template IsTraceableInCollection<>::value;
+ };
+ static const WeakHandlingFlag kWeakHandlingFlag =
+ ValueTraits::kWeakHandlingFlag;
+};
+
+template <typename LinkedHashSetType>
+class LinkedHashSetIterator {
+ DISALLOW_NEW();
+
+ private:
+ typedef typename LinkedHashSetType::Node Node;
+ typedef typename LinkedHashSetType::Traits Traits;
+
+ typedef typename LinkedHashSetType::Value& ReferenceType;
+ typedef typename LinkedHashSetType::Value* PointerType;
+
+ typedef LinkedHashSetConstIterator<LinkedHashSetType> const_iterator;
+
+ Node* GetNode() { return const_cast<Node*>(iterator_.GetNode()); }
+
+ protected:
+ LinkedHashSetIterator(const Node* position, LinkedHashSetType* container)
+ : iterator_(position, container) {}
+
+ public:
+ // Default copy, assignment and destructor are OK.
+
+ PointerType Get() const { return const_cast<PointerType>(iterator_.Get()); }
+ ReferenceType operator*() const { return *Get(); }
+ PointerType operator->() const { return Get(); }
+
+ LinkedHashSetIterator& operator++() {
+ ++iterator_;
+ return *this;
+ }
+ LinkedHashSetIterator& operator--() {
+ --iterator_;
+ return *this;
+ }
+
+ // Postfix ++ and -- intentionally omitted.
+
+ // Comparison.
+ bool operator==(const LinkedHashSetIterator& other) const {
+ return iterator_ == other.iterator_;
+ }
+ bool operator!=(const LinkedHashSetIterator& other) const {
+ return iterator_ != other.iterator_;
+ }
+
+ operator const_iterator() const { return iterator_; }
+
+ protected:
+ const_iterator iterator_;
+ template <typename T, typename U, typename V, typename W>
+ friend class LinkedHashSet;
+};
+
+template <typename LinkedHashSetType>
+class LinkedHashSetConstIterator {
+ DISALLOW_NEW();
+
+ private:
+ typedef typename LinkedHashSetType::Node Node;
+ typedef typename LinkedHashSetType::Traits Traits;
+
+ typedef const typename LinkedHashSetType::Value& ReferenceType;
+ typedef const typename LinkedHashSetType::Value* PointerType;
+
+ const Node* GetNode() const { return static_cast<const Node*>(position_); }
+
+ protected:
+ LinkedHashSetConstIterator(const LinkedHashSetNodeBase* position,
+ const LinkedHashSetType* container)
+ : position_(position)
+#if DCHECK_IS_ON()
+ ,
+ container_(container),
+ container_modifications_(container->Modifications())
+#endif
+ {
+ }
+
+ public:
+ PointerType Get() const {
+ CheckModifications();
+ return &static_cast<const Node*>(position_)->value_;
+ }
+ ReferenceType operator*() const { return *Get(); }
+ PointerType operator->() const { return Get(); }
+
+ LinkedHashSetConstIterator& operator++() {
+ DCHECK(position_);
+ CheckModifications();
+ position_ = position_->next_;
+ return *this;
+ }
+
+ LinkedHashSetConstIterator& operator--() {
+ DCHECK(position_);
+ CheckModifications();
+ position_ = position_->prev_;
+ return *this;
+ }
+
+ // Postfix ++ and -- intentionally omitted.
+
+ // Comparison.
+ bool operator==(const LinkedHashSetConstIterator& other) const {
+ return position_ == other.position_;
+ }
+ bool operator!=(const LinkedHashSetConstIterator& other) const {
+ return position_ != other.position_;
+ }
+
+ private:
+ const LinkedHashSetNodeBase* position_;
+#if DCHECK_IS_ON()
+ void CheckModifications() const {
+ container_->CheckModifications(container_modifications_);
+ }
+ const LinkedHashSetType* container_;
+ int64_t container_modifications_;
+#else
+ void CheckModifications() const {}
+#endif
+ template <typename T, typename U, typename V, typename W>
+ friend class LinkedHashSet;
+ friend class LinkedHashSetIterator<LinkedHashSetType>;
+};
+
+template <typename LinkedHashSetType>
+class LinkedHashSetReverseIterator
+ : public LinkedHashSetIterator<LinkedHashSetType> {
+ typedef LinkedHashSetReverseIterator<LinkedHashSetType> reverse_iterator;
+ typedef LinkedHashSetIterator<LinkedHashSetType> Superclass;
+ typedef LinkedHashSetConstReverseIterator<LinkedHashSetType>
+ const_reverse_iterator;
+ typedef typename LinkedHashSetType::Node Node;
+
+ protected:
+ LinkedHashSetReverseIterator(const Node* position,
+ LinkedHashSetType* container)
+ : Superclass(position, container) {}
+
+ public:
+ LinkedHashSetReverseIterator& operator++() {
+ Superclass::operator--();
+ return *this;
+ }
+ LinkedHashSetReverseIterator& operator--() {
+ Superclass::operator++();
+ return *this;
+ }
+
+ // Postfix ++ and -- intentionally omitted.
+
+ operator const_reverse_iterator() const {
+ return *reinterpret_cast<const_reverse_iterator*>(
+ const_cast<reverse_iterator*>(this));
+ }
+
+ template <typename T, typename U, typename V, typename W>
+ friend class LinkedHashSet;
+};
+
+template <typename LinkedHashSetType>
+class LinkedHashSetConstReverseIterator
+ : public LinkedHashSetConstIterator<LinkedHashSetType> {
+ typedef LinkedHashSetConstIterator<LinkedHashSetType> Superclass;
+ typedef typename LinkedHashSetType::Node Node;
+
+ public:
+ LinkedHashSetConstReverseIterator(const Node* position,
+ const LinkedHashSetType* container)
+ : Superclass(position, container) {}
+
+ LinkedHashSetConstReverseIterator& operator++() {
+ Superclass::operator--();
+ return *this;
+ }
+ LinkedHashSetConstReverseIterator& operator--() {
+ Superclass::operator++();
+ return *this;
+ }
+
+ // Postfix ++ and -- intentionally omitted.
+
+ template <typename T, typename U, typename V, typename W>
+ friend class LinkedHashSet;
+};
+
+inline void SwapAnchor(LinkedHashSetNodeBase& a, LinkedHashSetNodeBase& b) {
+ DCHECK(a.prev_);
+ DCHECK(a.next_);
+ DCHECK(b.prev_);
+ DCHECK(b.next_);
+ swap(a.prev_, b.prev_);
+ swap(a.next_, b.next_);
+ if (b.next_ == &a) {
+ DCHECK_EQ(b.prev_, &a);
+ b.next_ = &b;
+ b.prev_ = &b;
+ } else {
+ b.next_->prev_ = &b;
+ b.prev_->next_ = &b;
+ }
+ if (a.next_ == &b) {
+ DCHECK_EQ(a.prev_, &b);
+ a.next_ = &a;
+ a.prev_ = &a;
+ } else {
+ a.next_->prev_ = &a;
+ a.prev_->next_ = &a;
+ }
+}
+
+inline void swap(LinkedHashSetNodeBase& a, LinkedHashSetNodeBase& b) {
+ DCHECK_NE(a.next_, &a);
+ DCHECK_NE(b.next_, &b);
+ swap(a.prev_, b.prev_);
+ swap(a.next_, b.next_);
+ if (b.next_) {
+ b.next_->prev_ = &b;
+ b.prev_->next_ = &b;
+ }
+ if (a.next_) {
+ a.next_->prev_ = &a;
+ a.prev_->next_ = &a;
+ }
+}
+
+template <typename T, typename U, typename V, typename Allocator>
+inline LinkedHashSet<T, U, V, Allocator>::LinkedHashSet() {
+ static_assert(
+ Allocator::kIsGarbageCollected ||
+ !IsPointerToGarbageCollectedType<T>::value,
+ "Cannot put raw pointers to garbage-collected classes into "
+ "an off-heap LinkedHashSet. Use HeapLinkedHashSet<Member<T>> instead.");
+}
+
+template <typename T, typename U, typename V, typename W>
+inline LinkedHashSet<T, U, V, W>::LinkedHashSet(const LinkedHashSet& other)
+ : anchor_() {
+ const_iterator end = other.end();
+ for (const_iterator it = other.begin(); it != end; ++it)
+ insert(*it);
+}
+
+template <typename T, typename U, typename V, typename W>
+inline LinkedHashSet<T, U, V, W>::LinkedHashSet(LinkedHashSet&& other)
+ : anchor_() {
+ Swap(other);
+}
+
+template <typename T, typename U, typename V, typename W>
+inline LinkedHashSet<T, U, V, W>& LinkedHashSet<T, U, V, W>::operator=(
+ const LinkedHashSet& other) {
+ LinkedHashSet tmp(other);
+ Swap(tmp);
+ return *this;
+}
+
+template <typename T, typename U, typename V, typename W>
+inline LinkedHashSet<T, U, V, W>& LinkedHashSet<T, U, V, W>::operator=(
+ LinkedHashSet&& other) {
+ Swap(other);
+ return *this;
+}
+
+template <typename T, typename U, typename V, typename W>
+inline void LinkedHashSet<T, U, V, W>::Swap(LinkedHashSet& other) {
+ impl_.swap(other.impl_);
+ SwapAnchor(anchor_, other.anchor_);
+}
+
+template <typename T, typename U, typename V, typename Allocator>
+inline LinkedHashSet<T, U, V, Allocator>::~LinkedHashSet() {
+ // The destructor of anchor_ will implicitly be called here, which will
+ // unlink the anchor from the collection.
+}
+
+template <typename T, typename U, typename V, typename W>
+inline T& LinkedHashSet<T, U, V, W>::front() {
+ DCHECK(!IsEmpty());
+ return FirstNode()->value_;
+}
+
+template <typename T, typename U, typename V, typename W>
+inline const T& LinkedHashSet<T, U, V, W>::front() const {
+ DCHECK(!IsEmpty());
+ return FirstNode()->value_;
+}
+
+template <typename T, typename U, typename V, typename W>
+inline void LinkedHashSet<T, U, V, W>::RemoveFirst() {
+ DCHECK(!IsEmpty());
+ impl_.erase(static_cast<Node*>(anchor_.next_));
+}
+
+template <typename T, typename U, typename V, typename W>
+inline T& LinkedHashSet<T, U, V, W>::back() {
+ DCHECK(!IsEmpty());
+ return LastNode()->value_;
+}
+
+template <typename T, typename U, typename V, typename W>
+inline const T& LinkedHashSet<T, U, V, W>::back() const {
+ DCHECK(!IsEmpty());
+ return LastNode()->value_;
+}
+
+template <typename T, typename U, typename V, typename W>
+inline void LinkedHashSet<T, U, V, W>::pop_back() {
+ DCHECK(!IsEmpty());
+ impl_.erase(static_cast<Node*>(anchor_.prev_));
+}
+
+template <typename T, typename U, typename V, typename W>
+inline typename LinkedHashSet<T, U, V, W>::iterator
+LinkedHashSet<T, U, V, W>::find(ValuePeekInType value) {
+ LinkedHashSet::Node* node =
+ impl_.template Lookup<LinkedHashSet::NodeHashFunctions, ValuePeekInType>(
+ value);
+ if (!node)
+ return end();
+ return MakeIterator(node);
+}
+
+template <typename T, typename U, typename V, typename W>
+inline typename LinkedHashSet<T, U, V, W>::const_iterator
+LinkedHashSet<T, U, V, W>::find(ValuePeekInType value) const {
+ const LinkedHashSet::Node* node =
+ impl_.template Lookup<LinkedHashSet::NodeHashFunctions, ValuePeekInType>(
+ value);
+ if (!node)
+ return end();
+ return MakeConstIterator(node);
+}
+
+template <typename Translator>
+struct LinkedHashSetTranslatorAdapter {
+ STATIC_ONLY(LinkedHashSetTranslatorAdapter);
+ template <typename T>
+ static unsigned GetHash(const T& key) {
+ return Translator::GetHash(key);
+ }
+ template <typename T, typename U>
+ static bool Equal(const T& a, const U& b) {
+ return Translator::Equal(a.value_, b);
+ }
+};
+
+template <typename Value, typename U, typename V, typename W>
+template <typename HashTranslator, typename T>
+inline typename LinkedHashSet<Value, U, V, W>::iterator
+LinkedHashSet<Value, U, V, W>::Find(const T& value) {
+ typedef LinkedHashSetTranslatorAdapter<HashTranslator> TranslatedFunctions;
+ const LinkedHashSet::Node* node =
+ impl_.template Lookup<TranslatedFunctions, const T&>(value);
+ if (!node)
+ return end();
+ return MakeIterator(node);
+}
+
+template <typename Value, typename U, typename V, typename W>
+template <typename HashTranslator, typename T>
+inline typename LinkedHashSet<Value, U, V, W>::const_iterator
+LinkedHashSet<Value, U, V, W>::Find(const T& value) const {
+ typedef LinkedHashSetTranslatorAdapter<HashTranslator> TranslatedFunctions;
+ const LinkedHashSet::Node* node =
+ impl_.template Lookup<TranslatedFunctions, const T&>(value);
+ if (!node)
+ return end();
+ return MakeConstIterator(node);
+}
+
+template <typename Value, typename U, typename V, typename W>
+template <typename HashTranslator, typename T>
+inline bool LinkedHashSet<Value, U, V, W>::Contains(const T& value) const {
+ return impl_
+ .template Contains<LinkedHashSetTranslatorAdapter<HashTranslator>>(value);
+}
+
+template <typename T, typename U, typename V, typename W>
+inline bool LinkedHashSet<T, U, V, W>::Contains(ValuePeekInType value) const {
+ return impl_.template Contains<NodeHashFunctions>(value);
+}
+
+template <typename Value,
+ typename HashFunctions,
+ typename Traits,
+ typename Allocator>
+template <typename IncomingValueType>
+typename LinkedHashSet<Value, HashFunctions, Traits, Allocator>::AddResult
+LinkedHashSet<Value, HashFunctions, Traits, Allocator>::insert(
+ IncomingValueType&& value) {
+ return impl_.template insert<NodeHashFunctions>(
+ std::forward<IncomingValueType>(value), &anchor_);
+}
+
+template <typename T, typename U, typename V, typename W>
+template <typename IncomingValueType>
+typename LinkedHashSet<T, U, V, W>::iterator
+LinkedHashSet<T, U, V, W>::AddReturnIterator(IncomingValueType&& value) {
+ typename ImplType::AddResult result =
+ impl_.template insert<NodeHashFunctions>(
+ std::forward<IncomingValueType>(value), &anchor_);
+ return MakeIterator(result.stored_value);
+}
+
+template <typename T, typename U, typename V, typename W>
+template <typename IncomingValueType>
+typename LinkedHashSet<T, U, V, W>::AddResult
+LinkedHashSet<T, U, V, W>::AppendOrMoveToLast(IncomingValueType&& value) {
+ typename ImplType::AddResult result =
+ impl_.template insert<NodeHashFunctions>(
+ std::forward<IncomingValueType>(value), &anchor_);
+ Node* node = result.stored_value;
+ if (!result.is_new_entry) {
+ node->Unlink();
+ anchor_.InsertBefore(*node);
+ }
+ return result;
+}
+
+template <typename T, typename U, typename V, typename W>
+template <typename IncomingValueType>
+typename LinkedHashSet<T, U, V, W>::AddResult
+LinkedHashSet<T, U, V, W>::PrependOrMoveToFirst(IncomingValueType&& value) {
+ typename ImplType::AddResult result =
+ impl_.template insert<NodeHashFunctions>(
+ std::forward<IncomingValueType>(value), anchor_.next_);
+ Node* node = result.stored_value;
+ if (!result.is_new_entry) {
+ node->Unlink();
+ anchor_.InsertAfter(*node);
+ }
+ return result;
+}
+
+template <typename T, typename U, typename V, typename W>
+template <typename IncomingValueType>
+typename LinkedHashSet<T, U, V, W>::AddResult
+LinkedHashSet<T, U, V, W>::InsertBefore(ValuePeekInType before_value,
+ IncomingValueType&& new_value) {
+ return InsertBefore(find(before_value),
+ std::forward<IncomingValueType>(new_value));
+}
+
+template <typename T, typename U, typename V, typename W>
+inline void LinkedHashSet<T, U, V, W>::erase(iterator it) {
+ if (it == end())
+ return;
+ impl_.erase(it.GetNode());
+}
+
+template <typename T, typename U, typename V, typename W>
+inline void LinkedHashSet<T, U, V, W>::erase(ValuePeekInType value) {
+ erase(find(value));
+}
+
+template <typename T, typename Allocator>
+inline void swap(LinkedHashSetNode<T, Allocator>& a,
+ LinkedHashSetNode<T, Allocator>& b) {
+ typedef LinkedHashSetNodeBase Base;
+ // The key and value cannot be swapped atomically, and it would be
+ // wrong to have a GC when only one was swapped and the other still
+ // contained garbage (eg. from a previous use of the same slot).
+ // Therefore we forbid a GC until both the key and the value are
+ // swapped.
+ Allocator::EnterGCForbiddenScope();
+ swap(static_cast<Base&>(a), static_cast<Base&>(b));
+ swap(a.value_, b.value_);
+ Allocator::LeaveGCForbiddenScope();
+}
+
+} // namespace WTF
+
+using WTF::LinkedHashSet;
+
+#endif /* WTF_LinkedHashSet_h */
diff --git a/chromium/third_party/blink/renderer/platform/wtf/list_hash_set.h b/chromium/third_party/blink/renderer/platform/wtf/list_hash_set.h
new file mode 100644
index 00000000000..610bdff1b01
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/list_hash_set.h
@@ -0,0 +1,1139 @@
+/*
+ * Copyright (C) 2005, 2006, 2007, 2008, 2011, 2012 Apple Inc. All rights
+ * reserved.
+ * Copyright (C) 2011, Benjamin Poulain <ikipou@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_LIST_HASH_SET_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_LIST_HASH_SET_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/wtf/allocator/partition_allocator.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+
+namespace WTF {
+
+// ListHashSet provides a Set interface like HashSet, but also has a
+// predictable iteration order. It has O(1) insertion, removal, and test for
+// containership. It maintains a linked list through its contents such that
+// iterating it yields values in the order in which they were inserted.
+//
+// ListHashSet iterators are not invalidated by mutation of the collection,
+// unless they point to removed items. This means, for example, that you can
+// safely modify the container while iterating over it, as long as you don't
+// remove the current item.
+//
+// Prefer to use LinkedHashSet instead where possible
+// (https://crbug.com/614112). We would like to eventually remove ListHashSet
+// in favor of LinkedHashSet, because the latter supports WeakMember<T>.
+template <typename Value,
+ size_t inlineCapacity,
+ typename HashFunctions,
+ typename Allocator>
+class ListHashSet;
+
+template <typename Set>
+class ListHashSetIterator;
+template <typename Set>
+class ListHashSetConstIterator;
+template <typename Set>
+class ListHashSetReverseIterator;
+template <typename Set>
+class ListHashSetConstReverseIterator;
+
+template <typename ValueArg>
+class ListHashSetNodeBase;
+template <typename ValueArg, typename Allocator>
+class ListHashSetNode;
+template <typename ValueArg, size_t inlineCapacity>
+struct ListHashSetAllocator;
+
+template <typename HashArg>
+struct ListHashSetNodeHashFunctions;
+template <typename HashArg>
+struct ListHashSetTranslator;
+
+// Note that for a ListHashSet you cannot specify the HashTraits as a template
+// argument. It uses the default hash traits for the ValueArg type.
+template <typename ValueArg,
+ size_t inlineCapacity = 256,
+ typename HashArg = typename DefaultHash<ValueArg>::Hash,
+ typename AllocatorArg =
+ ListHashSetAllocator<ValueArg, inlineCapacity>>
+class ListHashSet
+ : public ConditionalDestructor<
+ ListHashSet<ValueArg, inlineCapacity, HashArg, AllocatorArg>,
+ AllocatorArg::kIsGarbageCollected> {
+ typedef AllocatorArg Allocator;
+ USE_ALLOCATOR(ListHashSet, Allocator);
+
+ typedef ListHashSetNode<ValueArg, Allocator> Node;
+ typedef HashTraits<Node*> NodeTraits;
+ typedef ListHashSetNodeHashFunctions<HashArg> NodeHash;
+ typedef ListHashSetTranslator<HashArg> BaseTranslator;
+
+ typedef HashTable<Node*,
+ Node*,
+ IdentityExtractor,
+ NodeHash,
+ NodeTraits,
+ NodeTraits,
+ typename Allocator::TableAllocator>
+ ImplType;
+ typedef HashTableIterator<Node*,
+ Node*,
+ IdentityExtractor,
+ NodeHash,
+ NodeTraits,
+ NodeTraits,
+ typename Allocator::TableAllocator>
+ ImplTypeIterator;
+ typedef HashTableConstIterator<Node*,
+ Node*,
+ IdentityExtractor,
+ NodeHash,
+ NodeTraits,
+ NodeTraits,
+ typename Allocator::TableAllocator>
+ ImplTypeConstIterator;
+
+ typedef HashArg HashFunctions;
+
+ public:
+ typedef ValueArg ValueType;
+ typedef HashTraits<ValueType> ValueTraits;
+ typedef typename ValueTraits::PeekInType ValuePeekInType;
+
+ typedef ListHashSetIterator<ListHashSet> iterator;
+ typedef ListHashSetConstIterator<ListHashSet> const_iterator;
+ friend class ListHashSetIterator<ListHashSet>;
+ friend class ListHashSetConstIterator<ListHashSet>;
+
+ typedef ListHashSetReverseIterator<ListHashSet> reverse_iterator;
+ typedef ListHashSetConstReverseIterator<ListHashSet> const_reverse_iterator;
+ friend class ListHashSetReverseIterator<ListHashSet>;
+ friend class ListHashSetConstReverseIterator<ListHashSet>;
+
+ struct AddResult final {
+ STACK_ALLOCATED();
+ friend class ListHashSet<ValueArg, inlineCapacity, HashArg, AllocatorArg>;
+ AddResult(Node* node, bool is_new_entry)
+ : stored_value(&node->value_),
+ is_new_entry(is_new_entry),
+ node_(node) {}
+ ValueType* stored_value;
+ bool is_new_entry;
+
+ private:
+ Node* node_;
+ };
+
+ ListHashSet();
+ ListHashSet(const ListHashSet&);
+ ListHashSet(ListHashSet&&);
+ ListHashSet& operator=(const ListHashSet&);
+ ListHashSet& operator=(ListHashSet&&);
+ void Finalize();
+
+ void Swap(ListHashSet&);
+
+ unsigned size() const { return impl_.size(); }
+ unsigned Capacity() const { return impl_.Capacity(); }
+ bool IsEmpty() const { return impl_.IsEmpty(); }
+
+ iterator begin() { return MakeIterator(head_); }
+ iterator end() { return MakeIterator(nullptr); }
+ const_iterator begin() const { return MakeConstIterator(head_); }
+ const_iterator end() const { return MakeConstIterator(nullptr); }
+
+ reverse_iterator rbegin() { return MakeReverseIterator(tail_); }
+ reverse_iterator rend() { return MakeReverseIterator(nullptr); }
+ const_reverse_iterator rbegin() const {
+ return MakeConstReverseIterator(tail_);
+ }
+ const_reverse_iterator rend() const {
+ return MakeConstReverseIterator(nullptr);
+ }
+
+ ValueType& front();
+ const ValueType& front() const;
+ void RemoveFirst();
+
+ ValueType& back();
+ const ValueType& back() const;
+ void pop_back();
+
+ iterator find(ValuePeekInType);
+ const_iterator find(ValuePeekInType) const;
+ bool Contains(ValuePeekInType) const;
+
+ // An alternate version of find() that finds the object by hashing and
+ // comparing with some other type, to avoid the cost of type conversion.
+ // The HashTranslator interface is defined in HashSet.
+ template <typename HashTranslator, typename T>
+ iterator Find(const T&);
+ template <typename HashTranslator, typename T>
+ const_iterator Find(const T&) const;
+ template <typename HashTranslator, typename T>
+ bool Contains(const T&) const;
+
+ // The return value of insert is a pair of a pointer to the stored value, and
+ // a bool that is true if an new entry was added.
+ template <typename IncomingValueType>
+ AddResult insert(IncomingValueType&&);
+
+ // Same as insert() except that the return value is an iterator. Useful in
+ // cases where it's needed to have the same return value as find() and where
+ // it's not possible to use a pointer to the storedValue.
+ template <typename IncomingValueType>
+ iterator AddReturnIterator(IncomingValueType&&);
+
+ // Add the value to the end of the collection. If the value was already in
+ // the list, it is moved to the end.
+ template <typename IncomingValueType>
+ AddResult AppendOrMoveToLast(IncomingValueType&&);
+
+ // Add the value to the beginning of the collection. If the value was
+ // already in the list, it is moved to the beginning.
+ template <typename IncomingValueType>
+ AddResult PrependOrMoveToFirst(IncomingValueType&&);
+
+ template <typename IncomingValueType>
+ AddResult InsertBefore(ValuePeekInType before_value,
+ IncomingValueType&& new_value);
+ template <typename IncomingValueType>
+ AddResult InsertBefore(iterator, IncomingValueType&&);
+
+ void erase(ValuePeekInType value) { return erase(find(value)); }
+ void erase(iterator);
+ void clear();
+ template <typename Collection>
+ void RemoveAll(const Collection& other) {
+ WTF::RemoveAll(*this, other);
+ }
+
+ ValueType Take(iterator);
+ ValueType Take(ValuePeekInType);
+ ValueType TakeFirst();
+
+ template <typename VisitorDispatcher>
+ void Trace(VisitorDispatcher);
+
+ private:
+ void Unlink(Node*);
+ void UnlinkAndDelete(Node*);
+ void AppendNode(Node*);
+ void PrependNode(Node*);
+ void InsertNodeBefore(Node* before_node, Node* new_node);
+ void DeleteAllNodes();
+ Allocator* GetAllocator() const { return allocator_provider_.Get(); }
+ void CreateAllocatorIfNeeded() {
+ allocator_provider_.CreateAllocatorIfNeeded();
+ }
+ void Deallocate(Node* node) const { allocator_provider_.deallocate(node); }
+
+ iterator MakeIterator(Node* position) { return iterator(this, position); }
+ const_iterator MakeConstIterator(Node* position) const {
+ return const_iterator(this, position);
+ }
+ reverse_iterator MakeReverseIterator(Node* position) {
+ return reverse_iterator(this, position);
+ }
+ const_reverse_iterator MakeConstReverseIterator(Node* position) const {
+ return const_reverse_iterator(this, position);
+ }
+
+ ImplType impl_;
+ Node* head_;
+ Node* tail_;
+ typename Allocator::AllocatorProvider allocator_provider_;
+};
+
+// ListHashSetNode has this base class to hold the members because the MSVC
+// compiler otherwise gets into circular template dependencies when trying to do
+// sizeof on a node.
+template <typename ValueArg>
+class ListHashSetNodeBase {
+ DISALLOW_NEW();
+
+ protected:
+ template <typename U>
+ explicit ListHashSetNodeBase(U&& value) : value_(std::forward<U>(value)) {}
+
+ public:
+ ValueArg value_;
+ ListHashSetNodeBase* prev_ = nullptr;
+ ListHashSetNodeBase* next_ = nullptr;
+#if DCHECK_IS_ON()
+ bool is_allocated_ = true;
+#endif
+};
+
+// This allocator is only used for non-Heap ListHashSets.
+template <typename ValueArg, size_t inlineCapacity>
+struct ListHashSetAllocator : public PartitionAllocator {
+ typedef PartitionAllocator TableAllocator;
+ typedef ListHashSetNode<ValueArg, ListHashSetAllocator> Node;
+ typedef ListHashSetNodeBase<ValueArg> NodeBase;
+
+ class AllocatorProvider {
+ DISALLOW_NEW();
+
+ public:
+ AllocatorProvider() : allocator_(nullptr) {}
+ void CreateAllocatorIfNeeded() {
+ if (!allocator_)
+ allocator_ = new ListHashSetAllocator;
+ }
+
+ void ReleaseAllocator() {
+ delete allocator_;
+ allocator_ = nullptr;
+ }
+
+ void Swap(AllocatorProvider& other) {
+ std::swap(allocator_, other.allocator_);
+ }
+
+ void Deallocate(Node* node) const {
+ DCHECK(allocator_);
+ allocator_->Deallocate(node);
+ }
+
+ ListHashSetAllocator* Get() const {
+ DCHECK(allocator_);
+ return allocator_;
+ }
+
+ private:
+ // Not using std::unique_ptr as this pointer should be deleted at
+ // releaseAllocator() method rather than at destructor.
+ ListHashSetAllocator* allocator_;
+ };
+
+ ListHashSetAllocator()
+ : free_list_(Pool()), is_done_with_initial_free_list_(false) {
+ memset(pool_, 0, sizeof(pool_));
+ }
+
+ Node* AllocateNode() {
+ Node* result = free_list_;
+
+ if (!result)
+ return static_cast<Node*>(WTF::Partitions::FastMalloc(
+ sizeof(NodeBase), WTF_HEAP_PROFILER_TYPE_NAME(Node)));
+
+#if DCHECK_IS_ON()
+ DCHECK(!result->is_allocated_);
+#endif
+
+ Node* next = result->Next();
+#if DCHECK_IS_ON()
+ DCHECK(!next || !next->is_allocated_);
+#endif
+ if (!next && !is_done_with_initial_free_list_) {
+ next = result + 1;
+ if (next == PastPool()) {
+ is_done_with_initial_free_list_ = true;
+ next = nullptr;
+ } else {
+ DCHECK(InPool(next));
+#if DCHECK_IS_ON()
+ DCHECK(!next->is_allocated_);
+#endif
+ }
+ }
+ free_list_ = next;
+
+ return result;
+ }
+
+ void Deallocate(Node* node) {
+ if (InPool(node)) {
+#if DCHECK_IS_ON()
+ node->is_allocated_ = false;
+#endif
+ node->next_ = free_list_;
+ free_list_ = node;
+ return;
+ }
+
+ WTF::Partitions::FastFree(node);
+ }
+
+ bool InPool(Node* node) { return node >= Pool() && node < PastPool(); }
+
+ static void TraceValue(typename PartitionAllocator::Visitor* visitor,
+ Node* node) {}
+
+ private:
+ Node* Pool() { return reinterpret_cast_ptr<Node*>(pool_); }
+ Node* PastPool() { return Pool() + kPoolSize; }
+
+ Node* free_list_;
+ bool is_done_with_initial_free_list_;
+#if defined(MEMORY_SANITIZER_INITIAL_SIZE)
+ // The allocation pool for nodes is one big chunk that ASAN has no insight
+ // into, so it can cloak errors. Make it as small as possible to force nodes
+ // to be allocated individually where ASAN can see them.
+ static const size_t kPoolSize = 1;
+#else
+ static const size_t kPoolSize = inlineCapacity;
+#endif
+ alignas(NodeBase) char pool_[sizeof(NodeBase) * kPoolSize];
+};
+
+template <typename ValueArg, typename AllocatorArg>
+class ListHashSetNode : public ListHashSetNodeBase<ValueArg> {
+ public:
+ typedef AllocatorArg NodeAllocator;
+ typedef ValueArg Value;
+
+ template <typename U>
+ ListHashSetNode(U&& value)
+ : ListHashSetNodeBase<ValueArg>(std::forward<U>(value)) {}
+
+ void* operator new(size_t, NodeAllocator* allocator) {
+ static_assert(
+ sizeof(ListHashSetNode) == sizeof(ListHashSetNodeBase<ValueArg>),
+ "please add any fields to the base");
+ return allocator->AllocateNode();
+ }
+
+ void SetWasAlreadyDestructed() {
+ if (NodeAllocator::kIsGarbageCollected &&
+ !IsTriviallyDestructible<ValueArg>::value)
+ this->prev_ = UnlinkedNodePointer();
+ }
+
+ bool WasAlreadyDestructed() const {
+ DCHECK(NodeAllocator::kIsGarbageCollected);
+ return this->prev_ == UnlinkedNodePointer();
+ }
+
+ static void Finalize(void* pointer) {
+ // No need to waste time calling finalize if it's not needed.
+ DCHECK(!IsTriviallyDestructible<ValueArg>::value);
+ ListHashSetNode* self = reinterpret_cast_ptr<ListHashSetNode*>(pointer);
+
+ // Check whether this node was already destructed before being unlinked
+ // from the collection.
+ if (self->WasAlreadyDestructed())
+ return;
+
+ self->value_.~ValueArg();
+ }
+ void FinalizeGarbageCollectedObject() { Finalize(this); }
+
+ void Destroy(NodeAllocator* allocator) {
+ this->~ListHashSetNode();
+ SetWasAlreadyDestructed();
+ allocator->Deallocate(this);
+ }
+
+ template <typename VisitorDispatcher>
+ void Trace(VisitorDispatcher visitor) {
+ // The conservative stack scan can find nodes that have been removed
+ // from the set and destructed. We don't need to trace these, and it
+ // would be wrong to do so, because the class will not expect the trace
+ // method to be called after the destructor. It's an error to remove a
+ // node from the ListHashSet while an iterator is positioned at that
+ // node, so there should be no valid pointers from the stack to a
+ // destructed node.
+ if (WasAlreadyDestructed())
+ return;
+ NodeAllocator::TraceValue(visitor, this);
+ visitor->Trace(Next());
+ visitor->Trace(Prev());
+ }
+
+ ListHashSetNode* Next() const {
+ return reinterpret_cast<ListHashSetNode*>(this->next_);
+ }
+ ListHashSetNode* Prev() const {
+ return reinterpret_cast<ListHashSetNode*>(this->prev_);
+ }
+
+ // Don't add fields here, the ListHashSetNodeBase and this should have the
+ // same size.
+
+ static ListHashSetNode* UnlinkedNodePointer() {
+ return reinterpret_cast<ListHashSetNode*>(-1);
+ }
+
+ template <typename HashArg>
+ friend struct ListHashSetNodeHashFunctions;
+};
+
+template <typename HashArg>
+struct ListHashSetNodeHashFunctions {
+ STATIC_ONLY(ListHashSetNodeHashFunctions);
+ template <typename T>
+ static unsigned GetHash(const T& key) {
+ return HashArg::GetHash(key->value_);
+ }
+ template <typename T>
+ static bool Equal(const T& a, const T& b) {
+ return HashArg::Equal(a->value_, b->value_);
+ }
+ static const bool safe_to_compare_to_empty_or_deleted = false;
+};
+
+template <typename Set>
+class ListHashSetIterator {
+ DISALLOW_NEW();
+
+ private:
+ typedef typename Set::const_iterator const_iterator;
+ typedef typename Set::Node Node;
+ typedef typename Set::ValueType ValueType;
+ typedef ValueType& ReferenceType;
+ typedef ValueType* PointerType;
+
+ ListHashSetIterator(const Set* set, Node* position)
+ : iterator_(set, position) {}
+
+ public:
+ ListHashSetIterator() = default;
+
+ // default copy, assignment and destructor are OK
+
+ PointerType Get() const { return const_cast<PointerType>(iterator_.Get()); }
+ ReferenceType operator*() const { return *Get(); }
+ PointerType operator->() const { return Get(); }
+
+ ListHashSetIterator& operator++() {
+ ++iterator_;
+ return *this;
+ }
+ ListHashSetIterator& operator--() {
+ --iterator_;
+ return *this;
+ }
+
+ // Postfix ++ and -- intentionally omitted.
+
+ // Comparison.
+ bool operator==(const ListHashSetIterator& other) const {
+ return iterator_ == other.iterator_;
+ }
+ bool operator!=(const ListHashSetIterator& other) const {
+ return iterator_ != other.iterator_;
+ }
+
+ operator const_iterator() const { return iterator_; }
+
+ template <typename VisitorDispatcher>
+ void Trace(VisitorDispatcher visitor) {
+ iterator_.Trace(visitor);
+ }
+
+ private:
+ Node* GetNode() { return iterator_.GetNode(); }
+
+ const_iterator iterator_;
+
+ template <typename T, size_t inlineCapacity, typename U, typename V>
+ friend class ListHashSet;
+};
+
+template <typename Set>
+class ListHashSetConstIterator {
+ DISALLOW_NEW();
+
+ private:
+ typedef typename Set::const_iterator const_iterator;
+ typedef typename Set::Node Node;
+ typedef typename Set::ValueType ValueType;
+ typedef const ValueType& ReferenceType;
+ typedef const ValueType* PointerType;
+
+ friend class ListHashSetIterator<Set>;
+
+ ListHashSetConstIterator(const Set* set, Node* position)
+ : set_(set), position_(position) {}
+
+ public:
+ ListHashSetConstIterator() = default;
+
+ PointerType Get() const { return &position_->value_; }
+ ReferenceType operator*() const { return *Get(); }
+ PointerType operator->() const { return Get(); }
+
+ ListHashSetConstIterator& operator++() {
+ DCHECK(position_);
+ position_ = position_->Next();
+ return *this;
+ }
+
+ ListHashSetConstIterator& operator--() {
+ DCHECK_NE(position_, set_->head_);
+ if (!position_)
+ position_ = set_->tail_;
+ else
+ position_ = position_->Prev();
+ return *this;
+ }
+
+ // Postfix ++ and -- intentionally omitted.
+
+ // Comparison.
+ bool operator==(const ListHashSetConstIterator& other) const {
+ return position_ == other.position_;
+ }
+ bool operator!=(const ListHashSetConstIterator& other) const {
+ return position_ != other.position_;
+ }
+
+ template <typename VisitorDispatcher>
+ void Trace(VisitorDispatcher visitor) {
+ visitor->Trace(*set_);
+ visitor->Trace(position_);
+ }
+
+ private:
+ Node* GetNode() { return position_; }
+
+ const Set* set_;
+ Node* position_;
+
+ template <typename T, size_t inlineCapacity, typename U, typename V>
+ friend class ListHashSet;
+};
+
+template <typename Set>
+class ListHashSetReverseIterator {
+ DISALLOW_NEW();
+
+ private:
+ typedef typename Set::const_reverse_iterator const_reverse_iterator;
+ typedef typename Set::Node Node;
+ typedef typename Set::ValueType ValueType;
+ typedef ValueType& ReferenceType;
+ typedef ValueType* PointerType;
+
+ ListHashSetReverseIterator(const Set* set, Node* position)
+ : iterator_(set, position) {}
+
+ public:
+ ListHashSetReverseIterator() = default;
+
+ // default copy, assignment and destructor are OK
+
+ PointerType Get() const { return const_cast<PointerType>(iterator_.Get()); }
+ ReferenceType operator*() const { return *Get(); }
+ PointerType operator->() const { return Get(); }
+
+ ListHashSetReverseIterator& operator++() {
+ ++iterator_;
+ return *this;
+ }
+ ListHashSetReverseIterator& operator--() {
+ --iterator_;
+ return *this;
+ }
+
+ // Postfix ++ and -- intentionally omitted.
+
+ // Comparison.
+ bool operator==(const ListHashSetReverseIterator& other) const {
+ return iterator_ == other.iterator_;
+ }
+ bool operator!=(const ListHashSetReverseIterator& other) const {
+ return iterator_ != other.iterator_;
+ }
+
+ operator const_reverse_iterator() const { return iterator_; }
+
+ template <typename VisitorDispatcher>
+ void Trace(VisitorDispatcher visitor) {
+ iterator_.trace(visitor);
+ }
+
+ private:
+ Node* GetNode() { return iterator_.node(); }
+
+ const_reverse_iterator iterator_;
+
+ template <typename T, size_t inlineCapacity, typename U, typename V>
+ friend class ListHashSet;
+};
+
+template <typename Set>
+class ListHashSetConstReverseIterator {
+ DISALLOW_NEW();
+
+ private:
+ typedef typename Set::reverse_iterator reverse_iterator;
+ typedef typename Set::Node Node;
+ typedef typename Set::ValueType ValueType;
+ typedef const ValueType& ReferenceType;
+ typedef const ValueType* PointerType;
+
+ friend class ListHashSetReverseIterator<Set>;
+
+ ListHashSetConstReverseIterator(const Set* set, Node* position)
+ : set_(set), position_(position) {}
+
+ public:
+ ListHashSetConstReverseIterator() = default;
+
+ PointerType Get() const { return &position_->value_; }
+ ReferenceType operator*() const { return *Get(); }
+ PointerType operator->() const { return Get(); }
+
+ ListHashSetConstReverseIterator& operator++() {
+ DCHECK(position_);
+ position_ = position_->Prev();
+ return *this;
+ }
+
+ ListHashSetConstReverseIterator& operator--() {
+ DCHECK_NE(position_, set_->tail_);
+ if (!position_)
+ position_ = set_->head_;
+ else
+ position_ = position_->Next();
+ return *this;
+ }
+
+ // Postfix ++ and -- intentionally omitted.
+
+ // Comparison.
+ bool operator==(const ListHashSetConstReverseIterator& other) const {
+ return position_ == other.position_;
+ }
+ bool operator!=(const ListHashSetConstReverseIterator& other) const {
+ return position_ != other.position_;
+ }
+
+ template <typename VisitorDispatcher>
+ void Trace(VisitorDispatcher visitor) {
+ visitor->Trace(*set_);
+ visitor->Trace(position_);
+ }
+
+ private:
+ Node* GetNode() { return position_; }
+
+ const Set* set_;
+ Node* position_;
+
+ template <typename T, size_t inlineCapacity, typename U, typename V>
+ friend class ListHashSet;
+};
+
+template <typename HashFunctions>
+struct ListHashSetTranslator {
+ STATIC_ONLY(ListHashSetTranslator);
+ template <typename T>
+ static unsigned GetHash(const T& key) {
+ return HashFunctions::GetHash(key);
+ }
+ template <typename T, typename U>
+ static bool Equal(const T& a, const U& b) {
+ return HashFunctions::Equal(a->value_, b);
+ }
+ template <typename T, typename U, typename V>
+ static void Translate(T*& location, U&& key, const V& allocator) {
+ location = new (const_cast<V*>(&allocator)) T(std::forward<U>(key));
+ }
+};
+
+template <typename T, size_t inlineCapacity, typename U, typename Allocator>
+inline ListHashSet<T, inlineCapacity, U, Allocator>::ListHashSet()
+ : head_(nullptr), tail_(nullptr) {
+ static_assert(
+ Allocator::kIsGarbageCollected ||
+ !IsPointerToGarbageCollectedType<T>::value,
+ "Cannot put raw pointers to garbage-collected classes into "
+ "an off-heap ListHashSet. Use HeapListHashSet<Member<T>> instead.");
+}
+
+template <typename T, size_t inlineCapacity, typename U, typename V>
+inline ListHashSet<T, inlineCapacity, U, V>::ListHashSet(
+ const ListHashSet& other)
+ : head_(nullptr), tail_(nullptr) {
+ const_iterator end = other.end();
+ for (const_iterator it = other.begin(); it != end; ++it)
+ insert(*it);
+}
+
+template <typename T, size_t inlineCapacity, typename U, typename V>
+inline ListHashSet<T, inlineCapacity, U, V>::ListHashSet(ListHashSet&& other)
+ : head_(nullptr), tail_(nullptr) {
+ Swap(other);
+}
+
+template <typename T, size_t inlineCapacity, typename U, typename V>
+inline ListHashSet<T, inlineCapacity, U, V>&
+ListHashSet<T, inlineCapacity, U, V>::operator=(const ListHashSet& other) {
+ ListHashSet tmp(other);
+ Swap(tmp);
+ return *this;
+}
+
+template <typename T, size_t inlineCapacity, typename U, typename V>
+inline ListHashSet<T, inlineCapacity, U, V>&
+ListHashSet<T, inlineCapacity, U, V>::operator=(ListHashSet&& other) {
+ Swap(other);
+ return *this;
+}
+
+template <typename T, size_t inlineCapacity, typename U, typename V>
+inline void ListHashSet<T, inlineCapacity, U, V>::Swap(ListHashSet& other) {
+ impl_.swap(other.impl_);
+ std::swap(head_, other.head_);
+ std::swap(tail_, other.tail_);
+ allocator_provider_.Swap(other.allocator_provider_);
+}
+
+template <typename T, size_t inlineCapacity, typename U, typename V>
+inline void ListHashSet<T, inlineCapacity, U, V>::Finalize() {
+ static_assert(!Allocator::kIsGarbageCollected,
+ "heap allocated ListHashSet should never call finalize()");
+ DeleteAllNodes();
+ allocator_provider_.ReleaseAllocator();
+}
+
+template <typename T, size_t inlineCapacity, typename U, typename V>
+inline T& ListHashSet<T, inlineCapacity, U, V>::front() {
+ DCHECK(!IsEmpty());
+ return head_->value_;
+}
+
+template <typename T, size_t inlineCapacity, typename U, typename V>
+inline void ListHashSet<T, inlineCapacity, U, V>::RemoveFirst() {
+ DCHECK(!IsEmpty());
+ impl_.erase(head_);
+ UnlinkAndDelete(head_);
+}
+
+template <typename T, size_t inlineCapacity, typename U, typename V>
+inline const T& ListHashSet<T, inlineCapacity, U, V>::front() const {
+ DCHECK(!IsEmpty());
+ return head_->value_;
+}
+
+template <typename T, size_t inlineCapacity, typename U, typename V>
+inline T& ListHashSet<T, inlineCapacity, U, V>::back() {
+ DCHECK(!IsEmpty());
+ return tail_->value_;
+}
+
+template <typename T, size_t inlineCapacity, typename U, typename V>
+inline const T& ListHashSet<T, inlineCapacity, U, V>::back() const {
+ DCHECK(!IsEmpty());
+ return tail_->value_;
+}
+
+template <typename T, size_t inlineCapacity, typename U, typename V>
+inline void ListHashSet<T, inlineCapacity, U, V>::pop_back() {
+ DCHECK(!IsEmpty());
+ impl_.erase(tail_);
+ UnlinkAndDelete(tail_);
+}
+
+template <typename T, size_t inlineCapacity, typename U, typename V>
+inline typename ListHashSet<T, inlineCapacity, U, V>::iterator
+ListHashSet<T, inlineCapacity, U, V>::find(ValuePeekInType value) {
+ ImplTypeIterator it = impl_.template Find<BaseTranslator>(value);
+ if (it == impl_.end())
+ return end();
+ return MakeIterator(*it);
+}
+
+template <typename T, size_t inlineCapacity, typename U, typename V>
+inline typename ListHashSet<T, inlineCapacity, U, V>::const_iterator
+ListHashSet<T, inlineCapacity, U, V>::find(ValuePeekInType value) const {
+ ImplTypeConstIterator it = impl_.template Find<BaseTranslator>(value);
+ if (it == impl_.end())
+ return end();
+ return MakeConstIterator(*it);
+}
+
+template <typename Translator>
+struct ListHashSetTranslatorAdapter {
+ STATIC_ONLY(ListHashSetTranslatorAdapter);
+ template <typename T>
+ static unsigned GetHash(const T& key) {
+ return Translator::GetHash(key);
+ }
+ template <typename T, typename U>
+ static bool Equal(const T& a, const U& b) {
+ return Translator::Equal(a->value_, b);
+ }
+};
+
+template <typename ValueType, size_t inlineCapacity, typename U, typename V>
+template <typename HashTranslator, typename T>
+inline typename ListHashSet<ValueType, inlineCapacity, U, V>::iterator
+ListHashSet<ValueType, inlineCapacity, U, V>::Find(const T& value) {
+ ImplTypeConstIterator it =
+ impl_.template Find<ListHashSetTranslatorAdapter<HashTranslator>>(value);
+ if (it == impl_.end())
+ return end();
+ return MakeIterator(*it);
+}
+
+template <typename ValueType, size_t inlineCapacity, typename U, typename V>
+template <typename HashTranslator, typename T>
+inline typename ListHashSet<ValueType, inlineCapacity, U, V>::const_iterator
+ListHashSet<ValueType, inlineCapacity, U, V>::Find(const T& value) const {
+ ImplTypeConstIterator it =
+ impl_.template Find<ListHashSetTranslatorAdapter<HashTranslator>>(value);
+ if (it == impl_.end())
+ return end();
+ return MakeConstIterator(*it);
+}
+
+template <typename ValueType, size_t inlineCapacity, typename U, typename V>
+template <typename HashTranslator, typename T>
+inline bool ListHashSet<ValueType, inlineCapacity, U, V>::Contains(
+ const T& value) const {
+ return impl_.template Contains<ListHashSetTranslatorAdapter<HashTranslator>>(
+ value);
+}
+
+template <typename T, size_t inlineCapacity, typename U, typename V>
+inline bool ListHashSet<T, inlineCapacity, U, V>::Contains(
+ ValuePeekInType value) const {
+ return impl_.template Contains<BaseTranslator>(value);
+}
+
+template <typename T, size_t inlineCapacity, typename U, typename V>
+template <typename IncomingValueType>
+typename ListHashSet<T, inlineCapacity, U, V>::AddResult
+ListHashSet<T, inlineCapacity, U, V>::insert(IncomingValueType&& value) {
+ CreateAllocatorIfNeeded();
+ // The second argument is a const ref. This is useful for the HashTable
+ // because it lets it take lvalues by reference, but for our purposes it's
+ // inconvenient, since it constrains us to be const, whereas the allocator
+ // actually changes when it does allocations.
+ auto result = impl_.template insert<BaseTranslator>(
+ std::forward<IncomingValueType>(value), *this->GetAllocator());
+ if (result.is_new_entry)
+ AppendNode(*result.stored_value);
+ return AddResult(*result.stored_value, result.is_new_entry);
+}
+
+template <typename T, size_t inlineCapacity, typename U, typename V>
+template <typename IncomingValueType>
+typename ListHashSet<T, inlineCapacity, U, V>::iterator
+ListHashSet<T, inlineCapacity, U, V>::AddReturnIterator(
+ IncomingValueType&& value) {
+ return MakeIterator(insert(std::forward<IncomingValueType>(value)).node_);
+}
+
+template <typename T, size_t inlineCapacity, typename U, typename V>
+template <typename IncomingValueType>
+typename ListHashSet<T, inlineCapacity, U, V>::AddResult
+ListHashSet<T, inlineCapacity, U, V>::AppendOrMoveToLast(
+ IncomingValueType&& value) {
+ CreateAllocatorIfNeeded();
+ auto result = impl_.template insert<BaseTranslator>(
+ std::forward<IncomingValueType>(value), *this->GetAllocator());
+ Node* node = *result.stored_value;
+ if (!result.is_new_entry)
+ Unlink(node);
+ AppendNode(node);
+ return AddResult(*result.stored_value, result.is_new_entry);
+}
+
+template <typename T, size_t inlineCapacity, typename U, typename V>
+template <typename IncomingValueType>
+typename ListHashSet<T, inlineCapacity, U, V>::AddResult
+ListHashSet<T, inlineCapacity, U, V>::PrependOrMoveToFirst(
+ IncomingValueType&& value) {
+ CreateAllocatorIfNeeded();
+ auto result = impl_.template insert<BaseTranslator>(
+ std::forward<IncomingValueType>(value), *this->GetAllocator());
+ Node* node = *result.stored_value;
+ if (!result.is_new_entry)
+ Unlink(node);
+ PrependNode(node);
+ return AddResult(*result.stored_value, result.is_new_entry);
+}
+
+template <typename T, size_t inlineCapacity, typename U, typename V>
+template <typename IncomingValueType>
+typename ListHashSet<T, inlineCapacity, U, V>::AddResult
+ListHashSet<T, inlineCapacity, U, V>::InsertBefore(
+ iterator it,
+ IncomingValueType&& new_value) {
+ CreateAllocatorIfNeeded();
+ auto result = impl_.template insert<BaseTranslator>(
+ std::forward<IncomingValueType>(new_value), *this->GetAllocator());
+ if (result.is_new_entry)
+ InsertNodeBefore(it.GetNode(), *result.stored_value);
+ return AddResult(*result.stored_value, result.is_new_entry);
+}
+
+template <typename T, size_t inlineCapacity, typename U, typename V>
+template <typename IncomingValueType>
+typename ListHashSet<T, inlineCapacity, U, V>::AddResult
+ListHashSet<T, inlineCapacity, U, V>::InsertBefore(
+ ValuePeekInType before_value,
+ IncomingValueType&& new_value) {
+ CreateAllocatorIfNeeded();
+ return InsertBefore(find(before_value),
+ std::forward<IncomingValueType>(new_value));
+}
+
+template <typename T, size_t inlineCapacity, typename U, typename V>
+inline void ListHashSet<T, inlineCapacity, U, V>::erase(iterator it) {
+ if (it == end())
+ return;
+ impl_.erase(it.GetNode());
+ UnlinkAndDelete(it.GetNode());
+}
+
+template <typename T, size_t inlineCapacity, typename U, typename V>
+inline void ListHashSet<T, inlineCapacity, U, V>::clear() {
+ DeleteAllNodes();
+ impl_.clear();
+ head_ = nullptr;
+ tail_ = nullptr;
+}
+
+template <typename T, size_t inlineCapacity, typename U, typename V>
+auto ListHashSet<T, inlineCapacity, U, V>::Take(iterator it) -> ValueType {
+ if (it == end())
+ return ValueTraits::EmptyValue();
+
+ impl_.erase(it.GetNode());
+ ValueType result = std::move(it.GetNode()->value_);
+ UnlinkAndDelete(it.GetNode());
+
+ return result;
+}
+
+template <typename T, size_t inlineCapacity, typename U, typename V>
+auto ListHashSet<T, inlineCapacity, U, V>::Take(ValuePeekInType value)
+ -> ValueType {
+ return Take(find(value));
+}
+
+template <typename T, size_t inlineCapacity, typename U, typename V>
+auto ListHashSet<T, inlineCapacity, U, V>::TakeFirst() -> ValueType {
+ DCHECK(!IsEmpty());
+ impl_.erase(head_);
+ ValueType result = std::move(head_->value_);
+ UnlinkAndDelete(head_);
+
+ return result;
+}
+
+template <typename T, size_t inlineCapacity, typename U, typename Allocator>
+void ListHashSet<T, inlineCapacity, U, Allocator>::Unlink(Node* node) {
+ if (!node->prev_) {
+ DCHECK_EQ(node, head_);
+ head_ = node->Next();
+ } else {
+ DCHECK_NE(node, head_);
+ node->prev_->next_ = node->next_;
+ }
+
+ if (!node->next_) {
+ DCHECK_EQ(node, tail_);
+ tail_ = node->Prev();
+ } else {
+ DCHECK_NE(node, tail_);
+ node->next_->prev_ = node->prev_;
+ }
+}
+
+template <typename T, size_t inlineCapacity, typename U, typename V>
+void ListHashSet<T, inlineCapacity, U, V>::UnlinkAndDelete(Node* node) {
+ Unlink(node);
+ node->Destroy(this->GetAllocator());
+}
+
+template <typename T, size_t inlineCapacity, typename U, typename V>
+void ListHashSet<T, inlineCapacity, U, V>::AppendNode(Node* node) {
+ node->prev_ = tail_;
+ node->next_ = nullptr;
+
+ if (tail_) {
+ DCHECK(head_);
+ tail_->next_ = node;
+ } else {
+ DCHECK(!head_);
+ head_ = node;
+ }
+
+ tail_ = node;
+}
+
+template <typename T, size_t inlineCapacity, typename U, typename V>
+void ListHashSet<T, inlineCapacity, U, V>::PrependNode(Node* node) {
+ node->prev_ = nullptr;
+ node->next_ = head_;
+
+ if (head_)
+ head_->prev_ = node;
+ else
+ tail_ = node;
+
+ head_ = node;
+}
+
+template <typename T, size_t inlineCapacity, typename U, typename V>
+void ListHashSet<T, inlineCapacity, U, V>::InsertNodeBefore(Node* before_node,
+ Node* new_node) {
+ if (!before_node)
+ return AppendNode(new_node);
+
+ new_node->next_ = before_node;
+ new_node->prev_ = before_node->prev_;
+ if (before_node->prev_)
+ before_node->prev_->next_ = new_node;
+ before_node->prev_ = new_node;
+
+ if (!new_node->prev_)
+ head_ = new_node;
+}
+
+template <typename T, size_t inlineCapacity, typename U, typename V>
+void ListHashSet<T, inlineCapacity, U, V>::DeleteAllNodes() {
+ if (!head_)
+ return;
+
+ for (Node *node = head_, *next = head_->Next(); node;
+ node = next, next = node ? node->Next() : nullptr)
+ node->Destroy(this->GetAllocator());
+}
+
+template <typename T, size_t inlineCapacity, typename U, typename V>
+template <typename VisitorDispatcher>
+void ListHashSet<T, inlineCapacity, U, V>::Trace(VisitorDispatcher visitor) {
+ static_assert(HashTraits<T>::kWeakHandlingFlag == kNoWeakHandling,
+ "HeapListHashSet does not support weakness, consider using "
+ "HeapLinkedHashSet instead.");
+ // This marks all the nodes and their contents live that can be accessed
+ // through the HashTable. That includes m_head and m_tail so we do not have
+ // to explicitly trace them here.
+ impl_.Trace(visitor);
+}
+
+} // namespace WTF
+
+using WTF::ListHashSet;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_LIST_HASH_SET_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/list_hash_set_test.cc b/chromium/third_party/blink/renderer/platform/wtf/list_hash_set_test.cc
new file mode 100644
index 00000000000..07059ab9cdb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/list_hash_set_test.cc
@@ -0,0 +1,734 @@
+/*
+ * Copyright (C) 2012 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/wtf/list_hash_set.h"
+
+#include <memory>
+#include <type_traits>
+#include "base/memory/ptr_util.h"
+#include "base/memory/scoped_refptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/linked_hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_test_helper.h"
+
+namespace WTF {
+
+namespace {
+
+template <typename Set>
+class ListOrLinkedHashSetTest : public testing::Test {};
+
+using SetTypes =
+ testing::Types<ListHashSet<int>, ListHashSet<int, 1>, LinkedHashSet<int>>;
+TYPED_TEST_CASE(ListOrLinkedHashSetTest, SetTypes);
+
+TYPED_TEST(ListOrLinkedHashSetTest, RemoveFirst) {
+ using Set = TypeParam;
+ Set list;
+ list.insert(-1);
+ list.insert(0);
+ list.insert(1);
+ list.insert(2);
+ list.insert(3);
+
+ EXPECT_EQ(-1, list.front());
+ EXPECT_EQ(3, list.back());
+
+ list.RemoveFirst();
+ EXPECT_EQ(0, list.front());
+
+ list.pop_back();
+ EXPECT_EQ(2, list.back());
+
+ list.RemoveFirst();
+ EXPECT_EQ(1, list.front());
+
+ list.RemoveFirst();
+ EXPECT_EQ(2, list.front());
+
+ list.RemoveFirst();
+ EXPECT_TRUE(list.IsEmpty());
+}
+
+TYPED_TEST(ListOrLinkedHashSetTest, AppendOrMoveToLastNewItems) {
+ using Set = TypeParam;
+ Set list;
+ typename Set::AddResult result = list.AppendOrMoveToLast(1);
+ EXPECT_TRUE(result.is_new_entry);
+ result = list.insert(2);
+ EXPECT_TRUE(result.is_new_entry);
+ result = list.AppendOrMoveToLast(3);
+ EXPECT_TRUE(result.is_new_entry);
+
+ EXPECT_EQ(list.size(), 3UL);
+
+ // The list should be in order 1, 2, 3.
+ typename Set::iterator iterator = list.begin();
+ EXPECT_EQ(1, *iterator);
+ ++iterator;
+ EXPECT_EQ(2, *iterator);
+ ++iterator;
+ EXPECT_EQ(3, *iterator);
+ ++iterator;
+}
+
+TYPED_TEST(ListOrLinkedHashSetTest, AppendOrMoveToLastWithDuplicates) {
+ using Set = TypeParam;
+ Set list;
+
+ // Add a single element twice.
+ typename Set::AddResult result = list.insert(1);
+ EXPECT_TRUE(result.is_new_entry);
+ result = list.AppendOrMoveToLast(1);
+ EXPECT_FALSE(result.is_new_entry);
+ EXPECT_EQ(1UL, list.size());
+
+ list.insert(2);
+ list.insert(3);
+ EXPECT_EQ(3UL, list.size());
+
+ // Appending 2 move it to the end.
+ EXPECT_EQ(3, list.back());
+ result = list.AppendOrMoveToLast(2);
+ EXPECT_FALSE(result.is_new_entry);
+ EXPECT_EQ(2, list.back());
+
+ // Inverse the list by moving each element to end end.
+ result = list.AppendOrMoveToLast(3);
+ EXPECT_FALSE(result.is_new_entry);
+ result = list.AppendOrMoveToLast(2);
+ EXPECT_FALSE(result.is_new_entry);
+ result = list.AppendOrMoveToLast(1);
+ EXPECT_FALSE(result.is_new_entry);
+ EXPECT_EQ(3UL, list.size());
+
+ typename Set::iterator iterator = list.begin();
+ EXPECT_EQ(3, *iterator);
+ ++iterator;
+ EXPECT_EQ(2, *iterator);
+ ++iterator;
+ EXPECT_EQ(1, *iterator);
+ ++iterator;
+}
+
+TYPED_TEST(ListOrLinkedHashSetTest, PrependOrMoveToFirstNewItems) {
+ using Set = TypeParam;
+ Set list;
+ typename Set::AddResult result = list.PrependOrMoveToFirst(1);
+ EXPECT_TRUE(result.is_new_entry);
+ result = list.insert(2);
+ EXPECT_TRUE(result.is_new_entry);
+ result = list.PrependOrMoveToFirst(3);
+ EXPECT_TRUE(result.is_new_entry);
+
+ EXPECT_EQ(list.size(), 3UL);
+
+ // The list should be in order 3, 1, 2.
+ typename Set::iterator iterator = list.begin();
+ EXPECT_EQ(3, *iterator);
+ ++iterator;
+ EXPECT_EQ(1, *iterator);
+ ++iterator;
+ EXPECT_EQ(2, *iterator);
+ ++iterator;
+}
+
+TYPED_TEST(ListOrLinkedHashSetTest, PrependOrMoveToLastWithDuplicates) {
+ using Set = TypeParam;
+ Set list;
+
+ // Add a single element twice.
+ typename Set::AddResult result = list.insert(1);
+ EXPECT_TRUE(result.is_new_entry);
+ result = list.PrependOrMoveToFirst(1);
+ EXPECT_FALSE(result.is_new_entry);
+ EXPECT_EQ(1UL, list.size());
+
+ list.insert(2);
+ list.insert(3);
+ EXPECT_EQ(3UL, list.size());
+
+ // Prepending 2 move it to the beginning.
+ EXPECT_EQ(1, list.front());
+ result = list.PrependOrMoveToFirst(2);
+ EXPECT_FALSE(result.is_new_entry);
+ EXPECT_EQ(2, list.front());
+
+ // Inverse the list by moving each element to the first position.
+ result = list.PrependOrMoveToFirst(1);
+ EXPECT_FALSE(result.is_new_entry);
+ result = list.PrependOrMoveToFirst(2);
+ EXPECT_FALSE(result.is_new_entry);
+ result = list.PrependOrMoveToFirst(3);
+ EXPECT_FALSE(result.is_new_entry);
+ EXPECT_EQ(3UL, list.size());
+
+ typename Set::iterator iterator = list.begin();
+ EXPECT_EQ(3, *iterator);
+ ++iterator;
+ EXPECT_EQ(2, *iterator);
+ ++iterator;
+ EXPECT_EQ(1, *iterator);
+ ++iterator;
+}
+
+TYPED_TEST(ListOrLinkedHashSetTest, Find) {
+ using Set = TypeParam;
+ Set set;
+ set.insert(-1);
+ set.insert(0);
+ set.insert(1);
+ set.insert(2);
+ set.insert(3);
+
+ {
+ const Set& ref = set;
+ typename Set::const_iterator it = ref.find(2);
+ EXPECT_EQ(2, *it);
+ ++it;
+ EXPECT_EQ(3, *it);
+ --it;
+ --it;
+ EXPECT_EQ(1, *it);
+ }
+ {
+ Set& ref = set;
+ typename Set::iterator it = ref.find(2);
+ EXPECT_EQ(2, *it);
+ ++it;
+ EXPECT_EQ(3, *it);
+ --it;
+ --it;
+ EXPECT_EQ(1, *it);
+ }
+}
+
+TYPED_TEST(ListOrLinkedHashSetTest, InsertBefore) {
+ using Set = TypeParam;
+ bool can_modify_while_iterating =
+ !std::is_same<Set, LinkedHashSet<int>>::value;
+ Set set;
+ set.insert(-1);
+ set.insert(0);
+ set.insert(2);
+ set.insert(3);
+
+ typename Set::iterator it = set.find(2);
+ EXPECT_EQ(2, *it);
+ set.InsertBefore(it, 1);
+ if (!can_modify_while_iterating)
+ it = set.find(2);
+ ++it;
+ EXPECT_EQ(3, *it);
+ EXPECT_EQ(5u, set.size());
+ --it;
+ --it;
+ EXPECT_EQ(1, *it);
+ if (can_modify_while_iterating) {
+ set.erase(-1);
+ set.erase(0);
+ set.erase(2);
+ set.erase(3);
+ EXPECT_EQ(1u, set.size());
+ EXPECT_EQ(1, *it);
+ ++it;
+ EXPECT_EQ(it, set.end());
+ --it;
+ EXPECT_EQ(1, *it);
+ set.InsertBefore(it, -1);
+ set.InsertBefore(it, 0);
+ set.insert(2);
+ set.insert(3);
+ }
+ set.InsertBefore(2, 42);
+ set.InsertBefore(-1, 103);
+ EXPECT_EQ(103, set.front());
+ if (!can_modify_while_iterating)
+ it = set.find(1);
+ ++it;
+ EXPECT_EQ(42, *it);
+ EXPECT_EQ(7u, set.size());
+}
+
+TYPED_TEST(ListOrLinkedHashSetTest, AddReturnIterator) {
+ using Set = TypeParam;
+ bool can_modify_while_iterating =
+ !std::is_same<Set, LinkedHashSet<int>>::value;
+ Set set;
+ set.insert(-1);
+ set.insert(0);
+ set.insert(1);
+ set.insert(2);
+
+ typename Set::iterator it = set.AddReturnIterator(3);
+ EXPECT_EQ(3, *it);
+ --it;
+ EXPECT_EQ(2, *it);
+ EXPECT_EQ(5u, set.size());
+ --it;
+ EXPECT_EQ(1, *it);
+ --it;
+ EXPECT_EQ(0, *it);
+ it = set.AddReturnIterator(4);
+ if (can_modify_while_iterating) {
+ set.erase(3);
+ set.erase(2);
+ set.erase(1);
+ set.erase(0);
+ set.erase(-1);
+ EXPECT_EQ(1u, set.size());
+ }
+ EXPECT_EQ(4, *it);
+ ++it;
+ EXPECT_EQ(it, set.end());
+ --it;
+ EXPECT_EQ(4, *it);
+ if (can_modify_while_iterating) {
+ set.InsertBefore(it, -1);
+ set.InsertBefore(it, 0);
+ set.InsertBefore(it, 1);
+ set.InsertBefore(it, 2);
+ set.InsertBefore(it, 3);
+ }
+ EXPECT_EQ(6u, set.size());
+ it = set.AddReturnIterator(5);
+ EXPECT_EQ(7u, set.size());
+ set.erase(it);
+ EXPECT_EQ(6u, set.size());
+ EXPECT_EQ(4, set.back());
+}
+
+TYPED_TEST(ListOrLinkedHashSetTest, Swap) {
+ using Set = TypeParam;
+ int num = 10;
+ Set set0;
+ Set set1;
+ Set set2;
+ for (int i = 0; i < num; ++i) {
+ set1.insert(i + 1);
+ set2.insert(num - i);
+ }
+
+ typename Set::iterator it1 = set1.begin();
+ typename Set::iterator it2 = set2.begin();
+ for (int i = 0; i < num; ++i, ++it1, ++it2) {
+ EXPECT_EQ(*it1, i + 1);
+ EXPECT_EQ(*it2, num - i);
+ }
+ EXPECT_EQ(set0.begin(), set0.end());
+ EXPECT_EQ(it1, set1.end());
+ EXPECT_EQ(it2, set2.end());
+
+ // Shift sets: 2->1, 1->0, 0->2
+ set1.Swap(set2); // Swap with non-empty sets.
+ set0.Swap(set2); // Swap with an empty set.
+
+ it1 = set0.begin();
+ it2 = set1.begin();
+ for (int i = 0; i < num; ++i, ++it1, ++it2) {
+ EXPECT_EQ(*it1, i + 1);
+ EXPECT_EQ(*it2, num - i);
+ }
+ EXPECT_EQ(it1, set0.end());
+ EXPECT_EQ(it2, set1.end());
+ EXPECT_EQ(set2.begin(), set2.end());
+
+ int removed_index = num >> 1;
+ set0.erase(removed_index + 1);
+ set1.erase(num - removed_index);
+
+ it1 = set0.begin();
+ it2 = set1.begin();
+ for (int i = 0; i < num; ++i, ++it1, ++it2) {
+ if (i == removed_index)
+ ++i;
+ EXPECT_EQ(*it1, i + 1);
+ EXPECT_EQ(*it2, num - i);
+ }
+ EXPECT_EQ(it1, set0.end());
+ EXPECT_EQ(it2, set1.end());
+}
+
+TYPED_TEST(ListOrLinkedHashSetTest, IteratorsConvertToConstVersions) {
+ using Set = TypeParam;
+ Set set;
+ set.insert(42);
+ typename Set::iterator it = set.begin();
+ typename Set::const_iterator cit = it;
+ typename Set::reverse_iterator rit = set.rbegin();
+ typename Set::const_reverse_iterator crit = rit;
+ // Use the variables to make the compiler happy.
+ ASSERT_EQ(*cit, *crit);
+}
+
+class DummyRefCounted : public RefCounted<DummyRefCounted> {
+ public:
+ DummyRefCounted(bool& is_deleted) : is_deleted_(is_deleted) {
+ is_deleted_ = false;
+ }
+ ~DummyRefCounted() { is_deleted_ = true; }
+ void AddRef() {
+ WTF::RefCounted<DummyRefCounted>::AddRef();
+ ++ref_invokes_count_;
+ }
+
+ static int ref_invokes_count_;
+
+ private:
+ bool& is_deleted_;
+};
+
+int DummyRefCounted::ref_invokes_count_ = 0;
+
+template <typename Set>
+class ListOrLinkedHashSetRefPtrTest : public testing::Test {};
+
+using RefPtrSetTypes =
+ testing::Types<ListHashSet<scoped_refptr<DummyRefCounted>>,
+ ListHashSet<scoped_refptr<DummyRefCounted>, 1>,
+ LinkedHashSet<scoped_refptr<DummyRefCounted>>>;
+TYPED_TEST_CASE(ListOrLinkedHashSetRefPtrTest, RefPtrSetTypes);
+
+TYPED_TEST(ListOrLinkedHashSetRefPtrTest, WithRefPtr) {
+ using Set = TypeParam;
+ bool is_deleted = false;
+ DummyRefCounted::ref_invokes_count_ = 0;
+ scoped_refptr<DummyRefCounted> ptr =
+ base::AdoptRef(new DummyRefCounted(is_deleted));
+ EXPECT_EQ(0, DummyRefCounted::ref_invokes_count_);
+
+ Set set;
+ set.insert(ptr);
+ // Referenced only once (to store a copy in the container).
+ EXPECT_EQ(1, DummyRefCounted::ref_invokes_count_);
+ EXPECT_EQ(ptr, set.front());
+ EXPECT_EQ(1, DummyRefCounted::ref_invokes_count_);
+
+ DummyRefCounted* raw_ptr = ptr.get();
+
+ EXPECT_TRUE(set.Contains(ptr));
+ EXPECT_TRUE(set.Contains(raw_ptr));
+ EXPECT_EQ(1, DummyRefCounted::ref_invokes_count_);
+
+ ptr = nullptr;
+ EXPECT_FALSE(is_deleted);
+ EXPECT_EQ(1, DummyRefCounted::ref_invokes_count_);
+
+ set.erase(raw_ptr);
+ EXPECT_TRUE(is_deleted);
+
+ EXPECT_EQ(1, DummyRefCounted::ref_invokes_count_);
+}
+
+TYPED_TEST(ListOrLinkedHashSetRefPtrTest, ExerciseValuePeekInType) {
+ using Set = TypeParam;
+ Set set;
+ bool is_deleted = false;
+ bool is_deleted2 = false;
+
+ scoped_refptr<DummyRefCounted> ptr =
+ base::AdoptRef(new DummyRefCounted(is_deleted));
+ scoped_refptr<DummyRefCounted> ptr2 =
+ base::AdoptRef(new DummyRefCounted(is_deleted2));
+
+ typename Set::AddResult add_result = set.insert(ptr);
+ EXPECT_TRUE(add_result.is_new_entry);
+ set.find(ptr);
+ const Set& const_set(set);
+ const_set.find(ptr);
+ EXPECT_TRUE(set.Contains(ptr));
+ typename Set::iterator it = set.AddReturnIterator(ptr);
+ set.AppendOrMoveToLast(ptr);
+ set.PrependOrMoveToFirst(ptr);
+ set.InsertBefore(ptr, ptr);
+ set.InsertBefore(it, ptr);
+ EXPECT_EQ(1u, set.size());
+ set.insert(ptr2);
+ ptr2 = nullptr;
+ set.erase(ptr);
+
+ EXPECT_FALSE(is_deleted);
+ ptr = nullptr;
+ EXPECT_TRUE(is_deleted);
+
+ EXPECT_FALSE(is_deleted2);
+ set.RemoveFirst();
+ EXPECT_TRUE(is_deleted2);
+
+ EXPECT_EQ(0u, set.size());
+}
+
+struct Simple {
+ Simple(int value) : value_(value){};
+ int value_;
+};
+
+struct Complicated {
+ Complicated(int value) : simple_(value) { objects_constructed_++; }
+
+ Complicated(const Complicated& other) : simple_(other.simple_) {
+ objects_constructed_++;
+ }
+
+ Simple simple_;
+ static int objects_constructed_;
+
+ private:
+ Complicated() = delete;
+};
+
+int Complicated::objects_constructed_ = 0;
+
+struct ComplicatedHashFunctions {
+ static unsigned GetHash(const Complicated& key) { return key.simple_.value_; }
+ static bool Equal(const Complicated& a, const Complicated& b) {
+ return a.simple_.value_ == b.simple_.value_;
+ }
+};
+struct ComplexityTranslator {
+ static unsigned GetHash(const Simple& key) { return key.value_; }
+ static bool Equal(const Complicated& a, const Simple& b) {
+ return a.simple_.value_ == b.value_;
+ }
+};
+
+template <typename Set>
+class ListOrLinkedHashSetTranslatorTest : public testing::Test {};
+
+using TranslatorSetTypes =
+ testing::Types<ListHashSet<Complicated, 256, ComplicatedHashFunctions>,
+ ListHashSet<Complicated, 1, ComplicatedHashFunctions>,
+ LinkedHashSet<Complicated, ComplicatedHashFunctions>>;
+TYPED_TEST_CASE(ListOrLinkedHashSetTranslatorTest, TranslatorSetTypes);
+
+TYPED_TEST(ListOrLinkedHashSetTranslatorTest, ComplexityTranslator) {
+ using Set = TypeParam;
+ Set set;
+ set.insert(Complicated(42));
+ int base_line = Complicated::objects_constructed_;
+
+ typename Set::iterator it =
+ set.template Find<ComplexityTranslator>(Simple(42));
+ EXPECT_NE(it, set.end());
+ EXPECT_EQ(base_line, Complicated::objects_constructed_);
+
+ it = set.template Find<ComplexityTranslator>(Simple(103));
+ EXPECT_EQ(it, set.end());
+ EXPECT_EQ(base_line, Complicated::objects_constructed_);
+
+ const Set& const_set(set);
+
+ typename Set::const_iterator const_iterator =
+ const_set.template Find<ComplexityTranslator>(Simple(42));
+ EXPECT_NE(const_iterator, const_set.end());
+ EXPECT_EQ(base_line, Complicated::objects_constructed_);
+
+ const_iterator = const_set.template Find<ComplexityTranslator>(Simple(103));
+ EXPECT_EQ(const_iterator, const_set.end());
+ EXPECT_EQ(base_line, Complicated::objects_constructed_);
+}
+
+TEST(ListHashSetTest, WithOwnPtr) {
+ bool deleted1 = false, deleted2 = false;
+
+ typedef ListHashSet<std::unique_ptr<Dummy>> OwnPtrSet;
+ OwnPtrSet set;
+
+ Dummy* ptr1 = new Dummy(deleted1);
+ {
+ // AddResult in a separate scope to avoid assertion hit,
+ // since we modify the container further.
+ OwnPtrSet::AddResult res1 = set.insert(base::WrapUnique(ptr1));
+ EXPECT_EQ(res1.stored_value->get(), ptr1);
+ }
+
+ EXPECT_FALSE(deleted1);
+ EXPECT_EQ(1UL, set.size());
+ OwnPtrSet::iterator it1 = set.find(ptr1);
+ EXPECT_NE(set.end(), it1);
+ EXPECT_EQ(ptr1, (*it1).get());
+
+ Dummy* ptr2 = new Dummy(deleted2);
+ {
+ OwnPtrSet::AddResult res2 = set.insert(base::WrapUnique(ptr2));
+ EXPECT_EQ(res2.stored_value->get(), ptr2);
+ }
+
+ EXPECT_FALSE(deleted2);
+ EXPECT_EQ(2UL, set.size());
+ OwnPtrSet::iterator it2 = set.find(ptr2);
+ EXPECT_NE(set.end(), it2);
+ EXPECT_EQ(ptr2, (*it2).get());
+
+ set.erase(ptr1);
+ EXPECT_TRUE(deleted1);
+
+ set.clear();
+ EXPECT_TRUE(deleted2);
+ EXPECT_TRUE(set.IsEmpty());
+
+ deleted1 = false;
+ deleted2 = false;
+ {
+ OwnPtrSet set;
+ set.insert(std::make_unique<Dummy>(deleted1));
+ set.insert(std::make_unique<Dummy>(deleted2));
+ }
+ EXPECT_TRUE(deleted1);
+ EXPECT_TRUE(deleted2);
+
+ deleted1 = false;
+ deleted2 = false;
+ std::unique_ptr<Dummy> own_ptr1;
+ std::unique_ptr<Dummy> own_ptr2;
+ ptr1 = new Dummy(deleted1);
+ ptr2 = new Dummy(deleted2);
+ {
+ OwnPtrSet set;
+ set.insert(base::WrapUnique(ptr1));
+ set.insert(base::WrapUnique(ptr2));
+ own_ptr1 = set.TakeFirst();
+ EXPECT_EQ(1UL, set.size());
+ own_ptr2 = set.Take(ptr2);
+ EXPECT_TRUE(set.IsEmpty());
+ }
+ EXPECT_FALSE(deleted1);
+ EXPECT_FALSE(deleted2);
+
+ EXPECT_EQ(ptr1, own_ptr1.get());
+ EXPECT_EQ(ptr2, own_ptr2.get());
+}
+
+template <typename Set>
+class ListOrLinkedHashSetCountCopyTest : public testing::Test {};
+
+using CountCopySetTypes = testing::Types<ListHashSet<CountCopy>,
+ ListHashSet<CountCopy, 1>,
+ LinkedHashSet<CountCopy>>;
+TYPED_TEST_CASE(ListOrLinkedHashSetCountCopyTest, CountCopySetTypes);
+
+TYPED_TEST(ListOrLinkedHashSetCountCopyTest,
+ MoveConstructionShouldNotMakeCopy) {
+ using Set = TypeParam;
+ Set set;
+ int counter = 0;
+ set.insert(CountCopy(&counter));
+
+ counter = 0;
+ Set other(std::move(set));
+ EXPECT_EQ(0, counter);
+}
+
+TYPED_TEST(ListOrLinkedHashSetCountCopyTest, MoveAssignmentShouldNotMakeACopy) {
+ using Set = TypeParam;
+ Set set;
+ int counter = 0;
+ set.insert(CountCopy(&counter));
+
+ Set other(set);
+ counter = 0;
+ set = std::move(other);
+ EXPECT_EQ(0, counter);
+}
+
+template <typename Set>
+class ListOrLinkedHashSetMoveOnlyTest : public testing::Test {};
+
+using MoveOnlySetTypes = testing::Types<ListHashSet<MoveOnlyHashValue>,
+ ListHashSet<MoveOnlyHashValue, 1>,
+ LinkedHashSet<MoveOnlyHashValue>>;
+TYPED_TEST_CASE(ListOrLinkedHashSetMoveOnlyTest, MoveOnlySetTypes);
+
+TYPED_TEST(ListOrLinkedHashSetMoveOnlyTest, MoveOnlyValue) {
+ using Set = TypeParam;
+ using AddResult = typename Set::AddResult;
+ Set set;
+ {
+ AddResult add_result = set.insert(MoveOnlyHashValue(1, 1));
+ EXPECT_TRUE(add_result.is_new_entry);
+ EXPECT_EQ(1, add_result.stored_value->Value());
+ EXPECT_EQ(1, add_result.stored_value->Id());
+ }
+ {
+ AddResult add_result = set.insert(MoveOnlyHashValue(1, 111));
+ EXPECT_FALSE(add_result.is_new_entry);
+ EXPECT_EQ(1, add_result.stored_value->Value());
+ EXPECT_EQ(1, add_result.stored_value->Id());
+ }
+ auto iter = set.find(MoveOnlyHashValue(1));
+ ASSERT_TRUE(iter != set.end());
+ EXPECT_EQ(1, iter->Value());
+ EXPECT_EQ(1, iter->Id());
+
+ iter = set.find(MoveOnlyHashValue(2));
+ EXPECT_TRUE(iter == set.end());
+
+ // ListHashSet and LinkedHashSet have several flavors of add().
+ iter = set.AddReturnIterator(MoveOnlyHashValue(2, 2));
+ EXPECT_EQ(2, iter->Value());
+ EXPECT_EQ(2, iter->Id());
+
+ iter = set.AddReturnIterator(MoveOnlyHashValue(2, 222));
+ EXPECT_EQ(2, iter->Value());
+ EXPECT_EQ(2, iter->Id());
+
+ {
+ AddResult add_result = set.AppendOrMoveToLast(MoveOnlyHashValue(3, 3));
+ EXPECT_TRUE(add_result.is_new_entry);
+ EXPECT_EQ(3, add_result.stored_value->Value());
+ EXPECT_EQ(3, add_result.stored_value->Id());
+ }
+ {
+ AddResult add_result = set.PrependOrMoveToFirst(MoveOnlyHashValue(4, 4));
+ EXPECT_TRUE(add_result.is_new_entry);
+ EXPECT_EQ(4, add_result.stored_value->Value());
+ EXPECT_EQ(4, add_result.stored_value->Id());
+ }
+ {
+ AddResult add_result =
+ set.InsertBefore(MoveOnlyHashValue(4), MoveOnlyHashValue(5, 5));
+ EXPECT_TRUE(add_result.is_new_entry);
+ EXPECT_EQ(5, add_result.stored_value->Value());
+ EXPECT_EQ(5, add_result.stored_value->Id());
+ }
+ {
+ iter = set.find(MoveOnlyHashValue(5));
+ ASSERT_TRUE(iter != set.end());
+ AddResult add_result = set.InsertBefore(iter, MoveOnlyHashValue(6, 6));
+ EXPECT_TRUE(add_result.is_new_entry);
+ EXPECT_EQ(6, add_result.stored_value->Value());
+ EXPECT_EQ(6, add_result.stored_value->Id());
+ }
+
+ // ... but they don't have any pass-out (like take()) methods.
+
+ set.erase(MoveOnlyHashValue(3));
+ set.clear();
+}
+
+} // anonymous namespace
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/locker.h b/chromium/third_party/blink/renderer/platform/wtf/locker.h
new file mode 100644
index 00000000000..b594e45f434
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/locker.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2008 Apple 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_LOCKER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_LOCKER_H_
+
+#include "base/macros.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace WTF {
+
+template <typename T>
+class Locker final {
+ STACK_ALLOCATED();
+
+ public:
+ Locker(T& lockable) : lockable_(lockable) { lockable_.lock(); }
+ ~Locker() { lockable_.unlock(); }
+
+ private:
+ T& lockable_;
+
+ DISALLOW_COPY_AND_ASSIGN(Locker);
+};
+
+} // namespace WTF
+
+using WTF::Locker;
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/wtf/math_extras.h b/chromium/third_party/blink/renderer/platform/wtf/math_extras.h
new file mode 100644
index 00000000000..2e3d0749e3a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/math_extras.h
@@ -0,0 +1,442 @@
+/*
+ * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_MATH_EXTRAS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_MATH_EXTRAS_H_
+
+#include <cmath>
+#include <cstddef>
+#include <limits>
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/cpu.h"
+
+#if defined(COMPILER_MSVC)
+// Make math.h behave like other platforms.
+#define _USE_MATH_DEFINES
+// Even if math.h was already included, including math.h again with
+// _USE_MATH_DEFINES adds the extra defines.
+#include <math.h>
+#include <stdint.h>
+#endif
+
+#if defined(OS_OPENBSD)
+#include <machine/ieee.h>
+#include <sys/types.h>
+#endif
+
+const double piDouble = M_PI;
+const float piFloat = static_cast<float>(M_PI);
+
+const double piOverTwoDouble = M_PI_2;
+const float piOverTwoFloat = static_cast<float>(M_PI_2);
+
+const double piOverFourDouble = M_PI_4;
+const float piOverFourFloat = static_cast<float>(M_PI_4);
+
+const double twoPiDouble = piDouble * 2.0;
+const float twoPiFloat = piFloat * 2.0f;
+
+#if defined(COMPILER_MSVC)
+
+// VS2013 has most of the math functions now, but we still need to work
+// around various differences in behavior of Inf.
+
+// Work around a bug in Win, where atan2(+-infinity, +-infinity) yields NaN
+// instead of specific values.
+inline double wtf_atan2(double x, double y) {
+ double posInf = std::numeric_limits<double>::infinity();
+ double negInf = -std::numeric_limits<double>::infinity();
+ double nan = std::numeric_limits<double>::quiet_NaN();
+
+ double result = nan;
+
+ if (x == posInf && y == posInf)
+ result = piOverFourDouble;
+ else if (x == posInf && y == negInf)
+ result = 3 * piOverFourDouble;
+ else if (x == negInf && y == posInf)
+ result = -piOverFourDouble;
+ else if (x == negInf && y == negInf)
+ result = -3 * piOverFourDouble;
+ else
+ result = ::atan2(x, y);
+
+ return result;
+}
+
+// Work around a bug in the Microsoft CRT, where fmod(x, +-infinity) yields NaN
+// instead of x.
+inline double wtf_fmod(double x, double y) {
+ return (!std::isinf(x) && std::isinf(y)) ? x : fmod(x, y);
+}
+
+// Work around a bug in the Microsoft CRT, where pow(NaN, 0) yields NaN instead
+// of 1.
+inline double wtf_pow(double x, double y) {
+ return y == 0 ? 1 : pow(x, y);
+}
+
+#define atan2(x, y) wtf_atan2(x, y)
+#define fmod(x, y) wtf_fmod(x, y)
+#define pow(x, y) wtf_pow(x, y)
+
+#endif // defined(COMPILER_MSVC)
+
+inline double deg2rad(double d) {
+ return d * piDouble / 180.0;
+}
+inline double rad2deg(double r) {
+ return r * 180.0 / piDouble;
+}
+inline double deg2grad(double d) {
+ return d * 400.0 / 360.0;
+}
+inline double grad2deg(double g) {
+ return g * 360.0 / 400.0;
+}
+inline double turn2deg(double t) {
+ return t * 360.0;
+}
+inline double deg2turn(double d) {
+ return d / 360.0;
+}
+inline double rad2grad(double r) {
+ return r * 200.0 / piDouble;
+}
+inline double grad2rad(double g) {
+ return g * piDouble / 200.0;
+}
+inline double turn2grad(double t) {
+ return t * 400;
+}
+inline double grad2turn(double g) {
+ return g / 400;
+}
+inline double rad2turn(double r) {
+ return r / twoPiDouble;
+}
+inline double turn2rad(double t) {
+ return t * twoPiDouble;
+}
+
+inline float deg2rad(float d) {
+ return d * piFloat / 180.0f;
+}
+inline float rad2deg(float r) {
+ return r * 180.0f / piFloat;
+}
+inline float deg2grad(float d) {
+ return d * 400.0f / 360.0f;
+}
+inline float grad2deg(float g) {
+ return g * 360.0f / 400.0f;
+}
+inline float turn2deg(float t) {
+ return t * 360.0f;
+}
+inline float deg2turn(float d) {
+ return d / 360.0f;
+}
+inline float rad2grad(float r) {
+ return r * 200.0f / piFloat;
+}
+inline float grad2rad(float g) {
+ return g * piFloat / 200.0f;
+}
+inline float turn2grad(float t) {
+ return t * 400;
+}
+inline float grad2turn(float g) {
+ return g / 400;
+}
+
+// clampTo() is implemented by templated helper classes (to allow for partial
+// template specialization) as well as several helper functions.
+
+// This helper function can be called when we know that:
+// (1) The type signednesses match so the compiler will not produce signed vs.
+// unsigned warnings
+// (2) The default type promotions/conversions are sufficient to handle things
+// correctly
+template <typename LimitType, typename ValueType>
+inline LimitType clampToDirectComparison(ValueType value,
+ LimitType min,
+ LimitType max) {
+ if (value >= max)
+ return max;
+ return (value <= min) ? min : static_cast<LimitType>(value);
+}
+
+// For any floating-point limits, or integral limits smaller than long long, we
+// can cast the limits to double without losing precision; then the only cases
+// where |value| can't be represented accurately as a double are the ones where
+// it's outside the limit range anyway. So doing all comparisons as doubles
+// will give correct results.
+//
+// In some cases, we can get better performance by using
+// clampToDirectComparison(). We use a templated class to switch between these
+// two cases (instead of simply using a conditional within one function) in
+// order to only compile the clampToDirectComparison() code for cases where it
+// will actually be used; this prevents the compiler from emitting warnings
+// about unsafe code (even though we wouldn't actually be executing that code).
+template <bool canUseDirectComparison, typename LimitType, typename ValueType>
+class ClampToNonLongLongHelper;
+template <typename LimitType, typename ValueType>
+class ClampToNonLongLongHelper<true, LimitType, ValueType> {
+ STATIC_ONLY(ClampToNonLongLongHelper);
+
+ public:
+ static inline LimitType clampTo(ValueType value,
+ LimitType min,
+ LimitType max) {
+ return clampToDirectComparison(value, min, max);
+ }
+};
+
+template <typename LimitType, typename ValueType>
+class ClampToNonLongLongHelper<false, LimitType, ValueType> {
+ STATIC_ONLY(ClampToNonLongLongHelper);
+
+ public:
+ static inline LimitType clampTo(ValueType value,
+ LimitType min,
+ LimitType max) {
+ const double doubleValue = static_cast<double>(value);
+ if (doubleValue >= static_cast<double>(max))
+ return max;
+ if (doubleValue <= static_cast<double>(min))
+ return min;
+ // If the limit type is integer, we might get better performance by
+ // casting |value| (as opposed to |doubleValue|) to the limit type.
+ return std::numeric_limits<LimitType>::is_integer
+ ? static_cast<LimitType>(value)
+ : static_cast<LimitType>(doubleValue);
+ }
+};
+
+// The unspecialized version of this templated class handles clamping to
+// anything other than [unsigned] long long int limits. It simply uses the
+// class above to toggle between the "fast" and "safe" clamp implementations.
+template <typename LimitType, typename ValueType>
+class ClampToHelper {
+ public:
+ static inline LimitType clampTo(ValueType value,
+ LimitType min,
+ LimitType max) {
+ // We only use clampToDirectComparison() when the integerness and
+ // signedness of the two types matches.
+ //
+ // If the integerness of the types doesn't match, then at best
+ // clampToDirectComparison() won't be much more efficient than the
+ // cast-everything-to-double method, since we'll need to convert to
+ // floating point anyway; at worst, we risk incorrect results when
+ // clamping a float to a 32-bit integral type due to potential precision
+ // loss.
+ //
+ // If the signedness doesn't match, clampToDirectComparison() will
+ // produce warnings about comparing signed vs. unsigned, which are apt
+ // since negative signed values will be converted to large unsigned ones
+ // and we'll get incorrect results.
+ return ClampToNonLongLongHelper <
+ std::numeric_limits<LimitType>::is_integer ==
+ std::numeric_limits<ValueType>::is_integer &&
+ std::numeric_limits<LimitType>::is_signed ==
+ std::numeric_limits<ValueType>::is_signed,
+ LimitType, ValueType > ::clampTo(value, min, max);
+ }
+};
+
+// Clamping to [unsigned] long long int limits requires more care. These may
+// not be accurately representable as doubles, so instead we cast |value| to the
+// limit type. But that cast is undefined if |value| is floating point and
+// outside the representable range of the limit type, so we also have to check
+// for that case explicitly.
+template <typename ValueType>
+class ClampToHelper<long long int, ValueType> {
+ STATIC_ONLY(ClampToHelper);
+
+ public:
+ static inline long long int clampTo(ValueType value,
+ long long int min,
+ long long int max) {
+ if (!std::numeric_limits<ValueType>::is_integer) {
+ if (value > 0) {
+ if (static_cast<double>(value) >=
+ static_cast<double>(std::numeric_limits<long long int>::max()))
+ return max;
+ } else if (static_cast<double>(value) <=
+ static_cast<double>(
+ std::numeric_limits<long long int>::min())) {
+ return min;
+ }
+ }
+ // Note: If |value| were unsigned long long int, it could be larger than
+ // the largest long long int, and this code would be wrong; we handle
+ // this case with a separate full specialization below.
+ return clampToDirectComparison(static_cast<long long int>(value), min, max);
+ }
+};
+
+// This specialization handles the case where the above partial specialization
+// would be potentially incorrect.
+template <>
+class ClampToHelper<long long int, unsigned long long int> {
+ STATIC_ONLY(ClampToHelper);
+
+ public:
+ static inline long long int clampTo(unsigned long long int value,
+ long long int min,
+ long long int max) {
+ if (max <= 0 || value >= static_cast<unsigned long long int>(max))
+ return max;
+ const long long int longLongValue = static_cast<long long int>(value);
+ return (longLongValue <= min) ? min : longLongValue;
+ }
+};
+
+// This is similar to the partial specialization that clamps to long long int,
+// but because the lower-bound check is done for integer value types as well, we
+// don't need a <unsigned long long int, long long int> full specialization.
+template <typename ValueType>
+class ClampToHelper<unsigned long long int, ValueType> {
+ STATIC_ONLY(ClampToHelper);
+
+ public:
+ static inline unsigned long long int clampTo(ValueType value,
+ unsigned long long int min,
+ unsigned long long int max) {
+ if (value <= 0)
+ return min;
+ if (!std::numeric_limits<ValueType>::is_integer) {
+ if (static_cast<double>(value) >=
+ static_cast<double>(
+ std::numeric_limits<unsigned long long int>::max()))
+ return max;
+ }
+ return clampToDirectComparison(static_cast<unsigned long long int>(value),
+ min, max);
+ }
+};
+
+template <typename T>
+inline T defaultMaximumForClamp() {
+ return std::numeric_limits<T>::max();
+}
+// This basically reimplements C++11's std::numeric_limits<T>::lowest().
+template <typename T>
+inline T defaultMinimumForClamp() {
+ return std::numeric_limits<T>::min();
+}
+template <>
+inline float defaultMinimumForClamp<float>() {
+ return -std::numeric_limits<float>::max();
+}
+template <>
+inline double defaultMinimumForClamp<double>() {
+ return -std::numeric_limits<double>::max();
+}
+
+// And, finally, the actual function for people to call.
+template <typename LimitType, typename ValueType>
+inline LimitType clampTo(ValueType value,
+ LimitType min = defaultMinimumForClamp<LimitType>(),
+ LimitType max = defaultMaximumForClamp<LimitType>()) {
+ DCHECK(!std::isnan(static_cast<double>(value)));
+ DCHECK_LE(min, max); // This also ensures |min| and |max| aren't NaN.
+ return ClampToHelper<LimitType, ValueType>::clampTo(value, min, max);
+}
+
+inline bool isWithinIntRange(float x) {
+ return x > static_cast<float>(std::numeric_limits<int>::min()) &&
+ x < static_cast<float>(std::numeric_limits<int>::max());
+}
+
+static size_t greatestCommonDivisor(size_t a, size_t b) {
+ return b ? greatestCommonDivisor(b, a % b) : a;
+}
+
+inline size_t lowestCommonMultiple(size_t a, size_t b) {
+ return a && b ? a / greatestCommonDivisor(a, b) * b : 0;
+}
+
+#ifndef UINT64_C
+#if defined(COMPILER_MSVC)
+#define UINT64_C(c) c##ui64
+#else
+#define UINT64_C(c) c##ull
+#endif
+#endif
+
+// Calculate d % 2^{64}.
+inline void doubleToInteger(double d, unsigned long long& value) {
+ if (std::isnan(d) || std::isinf(d)) {
+ value = 0;
+ } else {
+ // -2^{64} < fmodValue < 2^{64}.
+ double fmodValue =
+ fmod(trunc(d), std::numeric_limits<unsigned long long>::max() + 1.0);
+ if (fmodValue >= 0) {
+ // 0 <= fmodValue < 2^{64}.
+ // 0 <= value < 2^{64}. This cast causes no loss.
+ value = static_cast<unsigned long long>(fmodValue);
+ } else {
+ // -2^{64} < fmodValue < 0.
+ // 0 < fmodValueInUnsignedLongLong < 2^{64}. This cast causes no loss.
+ unsigned long long fmodValueInUnsignedLongLong =
+ static_cast<unsigned long long>(-fmodValue);
+ // -1 < (std::numeric_limits<unsigned long long>::max() -
+ // fmodValueInUnsignedLongLong)
+ // < 2^{64} - 1.
+ // 0 < value < 2^{64}.
+ value = std::numeric_limits<unsigned long long>::max() -
+ fmodValueInUnsignedLongLong + 1;
+ }
+ }
+}
+
+namespace WTF {
+
+inline unsigned FastLog2(unsigned i) {
+ unsigned log2 = 0;
+ if (i & (i - 1))
+ log2 += 1;
+ if (i >> 16)
+ log2 += 16, i >>= 16;
+ if (i >> 8)
+ log2 += 8, i >>= 8;
+ if (i >> 4)
+ log2 += 4, i >>= 4;
+ if (i >> 2)
+ log2 += 2, i >>= 2;
+ if (i >> 1)
+ log2 += 1;
+ return log2;
+}
+
+} // namespace WTF
+
+#endif // #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_MATH_EXTRAS_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/math_extras_test.cc b/chromium/third_party/blink/renderer/platform/wtf/math_extras_test.cc
new file mode 100644
index 00000000000..aedfb303757
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/math_extras_test.cc
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2012 Intel Corporation
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/wtf/math_extras.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace WTF {
+
+TEST(MathExtrasTest, Lrint) {
+ EXPECT_EQ(-8, lrint(-7.5));
+ EXPECT_EQ(-8, lrint(-8.5));
+ EXPECT_EQ(0, lrint(-0.5));
+ EXPECT_EQ(0, lrint(0.5));
+ EXPECT_EQ(0, lrint(-0.5));
+ EXPECT_EQ(1, lrint(1.3));
+ EXPECT_EQ(2, lrint(1.7));
+ EXPECT_EQ(0, lrint(0));
+ EXPECT_EQ(0, lrint(-0));
+ if (sizeof(long int) == 8) {
+ // Largest double number with 0.5 precision and one halfway rounding case
+ // below.
+ EXPECT_EQ(pow(2.0, 52), lrint(pow(2.0, 52) - 0.5));
+ EXPECT_EQ(pow(2.0, 52) - 2, lrint(pow(2.0, 52) - 1.5));
+ // Smallest double number with 0.5 precision and one halfway rounding case
+ // above.
+ EXPECT_EQ(-pow(2.0, 52), lrint(-pow(2.0, 52) + 0.5));
+ EXPECT_EQ(-pow(2.0, 52) + 2, lrint(-pow(2.0, 52) + 1.5));
+ }
+}
+
+TEST(MathExtrasTest, clampToIntLong) {
+ if (sizeof(long) == sizeof(int))
+ return;
+
+ long max_int = std::numeric_limits<int>::max();
+ long min_int = std::numeric_limits<int>::min();
+ long overflow_int = max_int + 1;
+ long underflow_int = min_int - 1;
+
+ EXPECT_GT(overflow_int, max_int);
+ EXPECT_LT(underflow_int, min_int);
+
+ EXPECT_EQ(max_int, clampTo<int>(max_int));
+ EXPECT_EQ(min_int, clampTo<int>(min_int));
+
+ EXPECT_EQ(max_int, clampTo<int>(overflow_int));
+ EXPECT_EQ(min_int, clampTo<int>(underflow_int));
+}
+
+TEST(MathExtrasTest, clampToIntLongLong) {
+ long long max_int = std::numeric_limits<int>::max();
+ long long min_int = std::numeric_limits<int>::min();
+ long long overflow_int = max_int + 1;
+ long long underflow_int = min_int - 1;
+
+ EXPECT_GT(overflow_int, max_int);
+ EXPECT_LT(underflow_int, min_int);
+
+ EXPECT_EQ(max_int, clampTo<int>(max_int));
+ EXPECT_EQ(min_int, clampTo<int>(min_int));
+
+ EXPECT_EQ(max_int, clampTo<int>(overflow_int));
+ EXPECT_EQ(min_int, clampTo<int>(underflow_int));
+}
+
+TEST(MathExtrasTest, clampToIntFloat) {
+ float max_int = static_cast<float>(std::numeric_limits<int>::max());
+ float min_int = static_cast<float>(std::numeric_limits<int>::min());
+ float overflow_int = max_int * 1.1f;
+ float underflow_int = min_int * 1.1f;
+
+ EXPECT_GT(overflow_int, max_int);
+ EXPECT_LT(underflow_int, min_int);
+
+ EXPECT_EQ(max_int, clampTo<int>(max_int));
+ EXPECT_EQ(min_int, clampTo<int>(min_int));
+
+ EXPECT_EQ(max_int, clampTo<int>(overflow_int));
+ EXPECT_EQ(min_int, clampTo<int>(underflow_int));
+
+ // This value and the value one greater are typically represented the same
+ // way when stored in a 32-bit float. Make sure clamping does not cause us
+ // to erroneously jump to the larger value.
+ int near_float_precision_limit = 2147483520;
+ EXPECT_EQ(near_float_precision_limit,
+ clampTo<int>(static_cast<float>(near_float_precision_limit), 0,
+ near_float_precision_limit + 1));
+ EXPECT_EQ(-near_float_precision_limit,
+ clampTo<int>(static_cast<float>(-near_float_precision_limit),
+ -near_float_precision_limit - 1, 0));
+}
+
+TEST(MathExtrasTest, clampToIntDouble) {
+ int max_int = std::numeric_limits<int>::max();
+ int min_int = std::numeric_limits<int>::min();
+ double almost_overflow_int = max_int - 0.5;
+ double overflow_int = max_int + 0.5;
+ double almost_underflow_int = min_int + 0.5;
+ double underflow_int = min_int - 0.5;
+
+ EXPECT_LT(almost_overflow_int, max_int);
+ EXPECT_GT(overflow_int, max_int);
+ EXPECT_GT(almost_underflow_int, min_int);
+ EXPECT_LT(underflow_int, min_int);
+
+ EXPECT_EQ(max_int, clampTo<int>(static_cast<double>(max_int)));
+ EXPECT_EQ(min_int, clampTo<int>(static_cast<double>(min_int)));
+
+ EXPECT_EQ(max_int - 1, clampTo<int>(almost_overflow_int));
+ EXPECT_EQ(max_int, clampTo<int>(overflow_int));
+ EXPECT_EQ(min_int + 1, clampTo<int>(almost_underflow_int));
+ EXPECT_EQ(min_int, clampTo<int>(underflow_int));
+}
+
+TEST(MathExtrasTest, clampToFloatDouble) {
+ double max_float = std::numeric_limits<float>::max();
+ double min_float = -max_float;
+ double overflow_float = max_float * 1.1;
+ double underflow_float = min_float * 1.1;
+
+ EXPECT_GT(overflow_float, max_float);
+ EXPECT_LT(underflow_float, min_float);
+
+ EXPECT_EQ(max_float, clampTo<float>(max_float));
+ EXPECT_EQ(min_float, clampTo<float>(min_float));
+
+ EXPECT_EQ(max_float, clampTo<float>(overflow_float));
+ EXPECT_EQ(min_float, clampTo<float>(underflow_float));
+
+ EXPECT_EQ(max_float, clampTo<float>(std::numeric_limits<float>::infinity()));
+ EXPECT_EQ(min_float, clampTo<float>(-std::numeric_limits<float>::infinity()));
+}
+
+TEST(MathExtrasTest, clampToDouble) {
+ EXPECT_EQ(0.0, clampTo<double>(0));
+ EXPECT_EQ(0.0, clampTo<double>(0.0f));
+ EXPECT_EQ(0.0, clampTo<double>(0ULL));
+ EXPECT_EQ(3.5, clampTo<double>(std::numeric_limits<unsigned long long>::max(),
+ 0.0, 3.5));
+}
+
+TEST(MathExtrasText, clampToLongLongDouble) {
+ double overflow_ll =
+ static_cast<double>(std::numeric_limits<long long>::max()) * 2;
+ EXPECT_EQ(std::numeric_limits<long long>::max(),
+ clampTo<long long>(overflow_ll));
+ EXPECT_EQ(std::numeric_limits<long long>::min(),
+ clampTo<long long>(-overflow_ll));
+}
+
+TEST(MathExtrasText, clampToUnsignedLongLongDouble) {
+ double overflow_ull =
+ static_cast<double>(std::numeric_limits<unsigned long long>::max()) * 2;
+ EXPECT_EQ(std::numeric_limits<unsigned long long>::max(),
+ clampTo<unsigned long long>(overflow_ull));
+ EXPECT_EQ(std::numeric_limits<unsigned long long>::min(),
+ clampTo<unsigned long long>(-overflow_ull));
+}
+
+TEST(MathExtrasTest, clampToUnsignedUnsignedLong) {
+ if (sizeof(unsigned long) == sizeof(unsigned))
+ return;
+
+ unsigned long max_unsigned = std::numeric_limits<unsigned>::max();
+ unsigned long overflow_unsigned = max_unsigned + 1;
+
+ EXPECT_GT(overflow_unsigned, max_unsigned);
+
+ EXPECT_EQ(max_unsigned, clampTo<unsigned>(max_unsigned));
+
+ EXPECT_EQ(max_unsigned, clampTo<unsigned>(overflow_unsigned));
+ EXPECT_EQ(0u, clampTo<unsigned>(-1));
+}
+
+TEST(MathExtrasTest, clampToUnsignedUnsignedLongLong) {
+ unsigned long long max_unsigned = std::numeric_limits<unsigned>::max();
+ unsigned long long overflow_unsigned = max_unsigned + 1;
+
+ EXPECT_GT(overflow_unsigned, max_unsigned);
+
+ EXPECT_EQ(max_unsigned, clampTo<unsigned>(max_unsigned));
+
+ EXPECT_EQ(max_unsigned, clampTo<unsigned>(overflow_unsigned));
+ EXPECT_EQ(0u, clampTo<unsigned>(-1));
+}
+
+TEST(MathExtrasTest, clampToLongLongUnsignedLongLong) {
+ long long max_long_long_ll = std::numeric_limits<long long>::max();
+ unsigned long long max_long_long_ull = max_long_long_ll;
+ unsigned long long overflow_long_long = max_long_long_ull + 1;
+
+ EXPECT_GT(overflow_long_long, max_long_long_ull);
+
+ EXPECT_EQ(max_long_long_ll, clampTo<long long>(max_long_long_ull));
+ EXPECT_EQ(max_long_long_ll - 1, clampTo<long long>(max_long_long_ull - 1));
+ EXPECT_EQ(max_long_long_ll, clampTo<long long>(overflow_long_long));
+
+ EXPECT_EQ(-3LL, clampTo<long long>(2ULL, -5LL, -3LL));
+}
+
+TEST(MathExtrasTest, clampToUnsignedLongLongInt) {
+ EXPECT_EQ(0ULL, clampTo<unsigned long long>(-1));
+ EXPECT_EQ(0ULL, clampTo<unsigned long long>(0));
+ EXPECT_EQ(1ULL, clampTo<unsigned long long>(1));
+}
+
+TEST(MathExtrasTest, clampToUnsignedLongLongUnsignedLongLong) {
+ EXPECT_EQ(0ULL, clampTo<unsigned long long>(0ULL));
+ EXPECT_EQ(1ULL, clampTo<unsigned long long>(0ULL, 1ULL, 2ULL));
+ EXPECT_EQ(2ULL, clampTo<unsigned long long>(3ULL, 1ULL, 2ULL));
+ EXPECT_EQ(0xFFFFFFFFFFFFFFF5ULL,
+ clampTo<unsigned long long>(0xFFFFFFFFFFFFFFF5ULL));
+}
+
+// Make sure that various +-inf cases are handled properly (they aren't
+// by default on VS).
+TEST(MathExtrasTest, infinityMath) {
+ double pos_inf = std::numeric_limits<double>::infinity();
+ double neg_inf = -std::numeric_limits<double>::infinity();
+ double nan = std::numeric_limits<double>::quiet_NaN();
+
+ EXPECT_EQ(M_PI_4, atan2(pos_inf, pos_inf));
+ EXPECT_EQ(3.0 * M_PI_4, atan2(pos_inf, neg_inf));
+ EXPECT_EQ(-M_PI_4, atan2(neg_inf, pos_inf));
+ EXPECT_EQ(-3.0 * M_PI_4, atan2(neg_inf, neg_inf));
+
+ EXPECT_EQ(0.0, fmod(0.0, pos_inf));
+ EXPECT_EQ(7.0, fmod(7.0, pos_inf));
+ EXPECT_EQ(-7.0, fmod(-7.0, pos_inf));
+ EXPECT_EQ(0.0, fmod(0.0, neg_inf));
+ EXPECT_EQ(7.0, fmod(7.0, neg_inf));
+ EXPECT_EQ(-7.0, fmod(-7.0, neg_inf));
+
+ EXPECT_EQ(1.0, pow(5.0, 0.0));
+ EXPECT_EQ(1.0, pow(-5.0, 0.0));
+ EXPECT_EQ(1.0, pow(nan, 0.0));
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/noncopyable.h b/chromium/third_party/blink/renderer/platform/wtf/noncopyable.h
new file mode 100644
index 00000000000..ca986b38a51
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/noncopyable.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2006, 2010 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_NONCOPYABLE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_NONCOPYABLE_H_
+
+#define WTF_MAKE_NONCOPYABLE(ClassName) \
+ private: \
+ ClassName(const ClassName&) = delete; \
+ ClassName& operator=(const ClassName&) = delete
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_NONCOPYABLE_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/not_found.h b/chromium/third_party/blink/renderer/platform/wtf/not_found.h
new file mode 100644
index 00000000000..765f0be3103
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/not_found.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2008 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_NOT_FOUND_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_NOT_FOUND_H_
+
+#include <stddef.h>
+
+namespace WTF {
+const size_t kNotFound = static_cast<size_t>(-1);
+}
+
+using WTF::kNotFound;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_NOT_FOUND_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/optional.h b/chromium/third_party/blink/renderer/platform/wtf/optional.h
new file mode 100644
index 00000000000..83184cdff67
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/optional.h
@@ -0,0 +1,37 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_OPTIONAL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_OPTIONAL_H_
+
+#include "base/optional.h"
+#include "base/stl_util.h"
+
+namespace WTF {
+
+// WTF::Optional is base::Optional. See base/optional.h for documentation.
+//
+// A clang plugin enforces that garbage collected types are not allocated
+// outside of the heap. GC containers such as HeapVector are allowed though.
+template <typename T>
+using Optional = base::Optional<T>;
+
+constexpr base::nullopt_t nullopt = base::nullopt;
+constexpr base::in_place_t in_place = base::in_place;
+
+template <typename T>
+constexpr Optional<typename std::decay<T>::type> make_optional(T&& value) {
+ return base::make_optional(std::forward<T>(value));
+}
+
+template <typename T>
+T* OptionalOrNullptr(Optional<T>& optional) {
+ return base::OptionalOrNullptr(optional);
+}
+
+} // namespace WTF
+
+using WTF::Optional;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_OPTIONAL_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/optional_test.cc b/chromium/third_party/blink/renderer/platform/wtf/optional_test.cc
new file mode 100644
index 00000000000..8f8424a7636
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/optional_test.cc
@@ -0,0 +1,58 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace WTF {
+namespace {
+
+struct IntBox {
+ IntBox(int n) : number(n) {}
+ int number;
+};
+
+class DestructionNotifier {
+ public:
+ DestructionNotifier(bool& flag) : flag_(flag) {}
+ ~DestructionNotifier() { flag_ = true; }
+
+ private:
+ bool& flag_;
+};
+
+TEST(OptionalTest, BooleanTest) {
+ Optional<int> optional;
+ EXPECT_FALSE(optional);
+ optional.emplace(0);
+ EXPECT_TRUE(optional);
+}
+
+TEST(OptionalTest, Dereference) {
+ Optional<int> optional;
+ optional.emplace(1);
+ EXPECT_EQ(1, *optional);
+
+ Optional<IntBox> optional_intbox;
+ optional_intbox.emplace(42);
+ EXPECT_EQ(42, optional_intbox->number);
+}
+
+TEST(OptionalTest, DestructorCalled) {
+ // Destroying a disengaged optional shouldn't do anything.
+ { Optional<DestructionNotifier> optional; }
+
+ // Destroying an engaged optional should call the destructor.
+ bool is_destroyed = false;
+ {
+ Optional<DestructionNotifier> optional;
+ optional.emplace(is_destroyed);
+ EXPECT_FALSE(is_destroyed);
+ }
+ EXPECT_TRUE(is_destroyed);
+}
+
+} // namespace
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/process_metrics.h b/chromium/third_party/blink/renderer/platform/wtf/process_metrics.h
new file mode 100644
index 00000000000..3c5e18ae9a6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/process_metrics.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_PROCESS_METRICS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_PROCESS_METRICS_H_
+
+#include "base/process/process_metrics.h"
+
+namespace WTF {
+
+size_t GetMallocUsage() {
+ std::unique_ptr<base::ProcessMetrics> metric(
+ base::ProcessMetrics::CreateCurrentProcessMetrics());
+ return metric->GetMallocUsage();
+}
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_PROCESS_METRICS_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/ref_counted.h b/chromium/third_party/blink/renderer/platform/wtf/ref_counted.h
new file mode 100644
index 00000000000..868457071a3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/ref_counted.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_REF_COUNTED_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_REF_COUNTED_H_
+
+#include "base/memory/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace WTF {
+
+template <typename T, typename Traits>
+class RefCounted;
+
+template <typename T>
+struct DefaultRefCountedTraits {
+ static void Destruct(const T* x) {
+ WTF::RefCounted<T, DefaultRefCountedTraits>::DeleteInternal(x);
+ }
+};
+
+template <typename T, typename Traits = DefaultRefCountedTraits<T>>
+class RefCounted : public base::RefCounted<T, Traits> {
+ // Put |T| in here instead of |RefCounted| so the heap profiler reports |T|
+ // instead of |RefCounted<T>|. This does not affect overloading of operator
+ // new.
+ USING_FAST_MALLOC(T);
+
+ public:
+ REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE();
+
+ private:
+ friend struct DefaultRefCountedTraits<T>;
+
+ template <typename U>
+ static void DeleteInternal(const U* x) {
+ delete x;
+ }
+};
+
+// Allows subclasses to use the default copy constructor.
+template <typename T>
+class RefCountedCopyable : public RefCounted<T> {
+ protected:
+ RefCountedCopyable() = default;
+ RefCountedCopyable(const RefCountedCopyable&) : RefCounted<T>() {}
+};
+
+} // namespace WTF
+
+using WTF::RefCounted;
+using WTF::RefCountedCopyable;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_REF_COUNTED_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/ref_ptr_test.cc b/chromium/third_party/blink/renderer/platform/wtf/ref_ptr_test.cc
new file mode 100644
index 00000000000..4b8a2c93c39
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/ref_ptr_test.cc
@@ -0,0 +1,107 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/scoped_refptr.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_impl.h"
+#include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
+
+namespace WTF {
+namespace {
+
+TEST(RefPtrTest, Basic) {
+ scoped_refptr<StringImpl> string;
+ EXPECT_TRUE(!string);
+ string = StringImpl::Create("test");
+ EXPECT_TRUE(!!string);
+ string = nullptr;
+ EXPECT_TRUE(!string);
+}
+
+TEST(RefPtrTest, MoveAssignmentOperator) {
+ scoped_refptr<StringImpl> a = StringImpl::Create("a");
+ scoped_refptr<StringImpl> b = StringImpl::Create("b");
+ b = std::move(a);
+ EXPECT_TRUE(!!b);
+ EXPECT_TRUE(!a);
+}
+
+class RefCountedClass : public RefCounted<RefCountedClass> {};
+
+TEST(RefPtrTest, ConstObject) {
+ // This test is only to ensure we force the compilation of a const RefCounted
+ // object to ensure the generated code compiles.
+ scoped_refptr<const RefCountedClass> ptr_to_const =
+ base::AdoptRef(new RefCountedClass());
+}
+
+class CustomDeleter;
+
+struct Deleter {
+ static void Destruct(const CustomDeleter*);
+};
+
+class CustomDeleter : public RefCounted<CustomDeleter, Deleter> {
+ public:
+ explicit CustomDeleter(bool* deleted) : deleted_(deleted) {}
+
+ private:
+ friend struct Deleter;
+ ~CustomDeleter() = default;
+
+ bool* deleted_;
+};
+
+void Deleter::Destruct(const CustomDeleter* obj) {
+ EXPECT_FALSE(*obj->deleted_);
+ *obj->deleted_ = true;
+ delete obj;
+}
+
+TEST(RefPtrTest, CustomDeleter) {
+ bool deleted = false;
+ scoped_refptr<CustomDeleter> obj =
+ base::AdoptRef(new CustomDeleter(&deleted));
+ EXPECT_FALSE(deleted);
+ obj = nullptr;
+ EXPECT_TRUE(deleted);
+}
+
+class CustomDeleterThreadSafe;
+
+struct DeleterThreadSafe {
+ static void Destruct(const CustomDeleterThreadSafe*);
+};
+
+class CustomDeleterThreadSafe
+ : public ThreadSafeRefCounted<CustomDeleterThreadSafe, DeleterThreadSafe> {
+ public:
+ explicit CustomDeleterThreadSafe(bool* deleted) : deleted_(deleted) {}
+
+ private:
+ friend struct DeleterThreadSafe;
+ ~CustomDeleterThreadSafe() = default;
+
+ bool* deleted_;
+};
+
+void DeleterThreadSafe::Destruct(const CustomDeleterThreadSafe* obj) {
+ EXPECT_FALSE(*obj->deleted_);
+ *obj->deleted_ = true;
+ delete obj;
+}
+
+TEST(RefPtrTest, CustomDeleterThreadSafe) {
+ bool deleted = false;
+ scoped_refptr<CustomDeleterThreadSafe> obj =
+ base::AdoptRef(new CustomDeleterThreadSafe(&deleted));
+ EXPECT_FALSE(deleted);
+ obj = nullptr;
+ EXPECT_TRUE(deleted);
+}
+
+} // namespace
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/ref_vector.h b/chromium/third_party/blink/renderer/platform/wtf/ref_vector.h
new file mode 100644
index 00000000000..d43dfbea088
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/ref_vector.h
@@ -0,0 +1,52 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_REF_VECTOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_REF_VECTOR_H_
+
+#include <utility>
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+template <typename T>
+class RefVector : public RefCounted<RefVector<T>> {
+ public:
+ static scoped_refptr<RefVector> Create() {
+ return base::AdoptRef(new RefVector<T>);
+ }
+ static scoped_refptr<RefVector> Create(const Vector<T>& vector) {
+ return base::AdoptRef(new RefVector<T>(vector));
+ }
+ static scoped_refptr<RefVector> Create(Vector<T>&& vector) {
+ return base::AdoptRef(new RefVector<T>(std::move(vector)));
+ }
+ scoped_refptr<RefVector> Copy() { return Create(GetVector()); }
+
+ const T& operator[](size_t i) const { return vector_[i]; }
+ T& operator[](size_t i) { return vector_[i]; }
+ const T& at(size_t i) const { return vector_.at(i); }
+ T& at(size_t i) { return vector_.at(i); }
+
+ bool operator==(const RefVector& o) const { return vector_ == o.vector_; }
+ bool operator!=(const RefVector& o) const { return vector_ != o.vector_; }
+
+ size_t size() const { return vector_.size(); }
+ bool IsEmpty() const { return !size(); }
+ void push_back(const T& decoration) { vector_.push_back(decoration); }
+ const Vector<T>& GetVector() const { return vector_; }
+
+ private:
+ Vector<T> vector_;
+ RefVector() = default;
+ RefVector(const Vector<T>& vector) : vector_(vector) {}
+ RefVector(Vector<T>&& vector) : vector_(vector) {}
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_REF_VECTOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/retain_ptr.h b/chromium/third_party/blink/renderer/platform/wtf/retain_ptr.h
new file mode 100644
index 00000000000..5d0eb049411
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/retain_ptr.h
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2005, 2006, 2007, 2008, 2010 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_RETAIN_PTR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_RETAIN_PTR_H_
+
+#include <algorithm>
+#include <type_traits>
+#include <utility>
+#include "third_party/blink/renderer/platform/wtf/compiler.h"
+#include "third_party/blink/renderer/platform/wtf/hash_table_deleted_value_type.h"
+#include "third_party/blink/renderer/platform/wtf/hash_traits.h"
+#include "third_party/blink/renderer/platform/wtf/type_traits.h"
+
+#ifdef __OBJC__
+#import <Foundation/Foundation.h>
+#endif
+
+#ifndef CF_RELEASES_ARGUMENT
+#define CF_RELEASES_ARGUMENT
+#endif
+
+#ifndef NS_RELEASES_ARGUMENT
+#define NS_RELEASES_ARGUMENT
+#endif
+
+namespace WTF {
+
+// Unlike most most of our smart pointers, RetainPtr can take either the pointer
+// type or the pointed-to type, so both RetainPtr<NSDictionary> and
+// RetainPtr<CFDictionaryRef> will work.
+
+enum AdoptCFTag { kAdoptCF };
+enum AdoptNSTag { kAdoptNS };
+
+#ifdef __OBJC__
+inline void AdoptNSReference(id ptr) {
+ if (ptr) {
+ CFRetain(ptr);
+ [ptr release];
+ }
+}
+#endif
+
+template <typename T>
+class RetainPtr {
+ public:
+ typedef typename std::remove_pointer<T>::type ValueType;
+ typedef ValueType* PtrType;
+
+ RetainPtr() : ptr_(nullptr) {}
+ RetainPtr(PtrType ptr) : ptr_(ptr) {
+ if (ptr)
+ CFRetain(ptr);
+ }
+
+ RetainPtr(AdoptCFTag, PtrType ptr) : ptr_(ptr) {}
+ RetainPtr(AdoptNSTag, PtrType ptr) : ptr_(ptr) { AdoptNSReference(ptr); }
+
+ RetainPtr(const RetainPtr& o) : ptr_(o.ptr_) {
+ if (PtrType ptr = ptr_)
+ CFRetain(ptr);
+ }
+
+ RetainPtr(RetainPtr&& o) : ptr_(o.LeakRef()) {}
+
+ // Hash table deleted values, which are only constructed and never copied or
+ // destroyed.
+ RetainPtr(HashTableDeletedValueType) : ptr_(HashTableDeletedValue()) {}
+ bool IsHashTableDeletedValue() const {
+ return ptr_ == HashTableDeletedValue();
+ }
+
+ ~RetainPtr() {
+ if (PtrType ptr = ptr_)
+ CFRelease(ptr);
+ }
+
+ template <typename U>
+ RetainPtr(const RetainPtr<U>&);
+
+ void Clear();
+ WARN_UNUSED_RESULT PtrType LeakRef();
+
+ PtrType Get() const { return ptr_; }
+ PtrType operator->() const { return ptr_; }
+
+ bool operator!() const { return !ptr_; }
+ explicit operator bool() const { return ptr_; }
+
+ RetainPtr& operator=(const RetainPtr&);
+ template <typename U>
+ RetainPtr& operator=(const RetainPtr<U>&);
+ RetainPtr& operator=(PtrType);
+ template <typename U>
+ RetainPtr& operator=(U*);
+
+ RetainPtr& operator=(RetainPtr&&);
+ template <typename U>
+ RetainPtr& operator=(RetainPtr<U>&&);
+
+ RetainPtr& operator=(std::nullptr_t) {
+ Clear();
+ return *this;
+ }
+
+ void AdoptCF(PtrType);
+ void AdoptNS(PtrType);
+
+ void Swap(RetainPtr&);
+
+ private:
+ static PtrType HashTableDeletedValue() {
+ return reinterpret_cast<PtrType>(-1);
+ }
+
+ PtrType ptr_;
+};
+
+template <typename T>
+template <typename U>
+inline RetainPtr<T>::RetainPtr(const RetainPtr<U>& o) : ptr_(o.Get()) {
+ if (PtrType ptr = ptr_)
+ CFRetain(ptr);
+}
+
+template <typename T>
+inline void RetainPtr<T>::Clear() {
+ if (PtrType ptr = ptr_) {
+ ptr_ = nullptr;
+ CFRelease(ptr);
+ }
+}
+
+template <typename T>
+inline typename RetainPtr<T>::PtrType RetainPtr<T>::LeakRef() {
+ PtrType ptr = ptr_;
+ ptr_ = nullptr;
+ return ptr;
+}
+
+template <typename T>
+inline RetainPtr<T>& RetainPtr<T>::operator=(const RetainPtr<T>& o) {
+ PtrType optr = o.Get();
+ if (optr)
+ CFRetain(optr);
+ PtrType ptr = ptr_;
+ ptr_ = optr;
+ if (ptr)
+ CFRelease(ptr);
+ return *this;
+}
+
+template <typename T>
+template <typename U>
+inline RetainPtr<T>& RetainPtr<T>::operator=(const RetainPtr<U>& o) {
+ PtrType optr = o.Get();
+ if (optr)
+ CFRetain(optr);
+ PtrType ptr = ptr_;
+ ptr_ = optr;
+ if (ptr)
+ CFRelease(ptr);
+ return *this;
+}
+
+template <typename T>
+inline RetainPtr<T>& RetainPtr<T>::operator=(PtrType optr) {
+ if (optr)
+ CFRetain(optr);
+ PtrType ptr = ptr_;
+ ptr_ = optr;
+ if (ptr)
+ CFRelease(ptr);
+ return *this;
+}
+
+template <typename T>
+template <typename U>
+inline RetainPtr<T>& RetainPtr<T>::operator=(U* optr) {
+ if (optr)
+ CFRetain(optr);
+ PtrType ptr = ptr_;
+ ptr_ = optr;
+ if (ptr)
+ CFRelease(ptr);
+ return *this;
+}
+
+template <typename T>
+inline RetainPtr<T>& RetainPtr<T>::operator=(RetainPtr<T>&& o) {
+ AdoptCF(o.LeakRef());
+ return *this;
+}
+
+template <typename T>
+template <typename U>
+inline RetainPtr<T>& RetainPtr<T>::operator=(RetainPtr<U>&& o) {
+ AdoptCF(o.LeakRef());
+ return *this;
+}
+
+template <typename T>
+inline void RetainPtr<T>::AdoptCF(PtrType optr) {
+ PtrType ptr = ptr_;
+ ptr_ = optr;
+ if (ptr)
+ CFRelease(ptr);
+}
+
+template <typename T>
+inline void RetainPtr<T>::AdoptNS(PtrType optr) {
+ AdoptNSReference(optr);
+
+ PtrType ptr = ptr_;
+ ptr_ = optr;
+ if (ptr)
+ CFRelease(ptr);
+}
+
+template <typename T>
+inline void RetainPtr<T>::Swap(RetainPtr<T>& o) {
+ std::swap(ptr_, o.ptr_);
+}
+
+template <typename T>
+inline void swap(RetainPtr<T>& a, RetainPtr<T>& b) {
+ a.Swap(b);
+}
+
+template <typename T, typename U>
+inline bool operator==(const RetainPtr<T>& a, const RetainPtr<U>& b) {
+ return a.Get() == b.Get();
+}
+
+template <typename T, typename U>
+inline bool operator==(const RetainPtr<T>& a, U* b) {
+ return a.Get() == b;
+}
+
+template <typename T, typename U>
+inline bool operator==(T* a, const RetainPtr<U>& b) {
+ return a == b.Get();
+}
+
+template <typename T, typename U>
+inline bool operator!=(const RetainPtr<T>& a, const RetainPtr<U>& b) {
+ return a.Get() != b.Get();
+}
+
+template <typename T, typename U>
+inline bool operator!=(const RetainPtr<T>& a, U* b) {
+ return a.Get() != b;
+}
+
+template <typename T, typename U>
+inline bool operator!=(T* a, const RetainPtr<U>& b) {
+ return a != b.Get();
+}
+
+template <typename T>
+WARN_UNUSED_RESULT inline RetainPtr<T> AdoptCF(T CF_RELEASES_ARGUMENT);
+template <typename T>
+inline RetainPtr<T> AdoptCF(T o) {
+ return RetainPtr<T>(kAdoptCF, o);
+}
+
+template <typename T>
+WARN_UNUSED_RESULT inline RetainPtr<T> AdoptNS(T NS_RELEASES_ARGUMENT);
+template <typename T>
+inline RetainPtr<T> AdoptNS(T o) {
+ return RetainPtr<T>(kAdoptNS, o);
+}
+
+template <typename T>
+struct HashTraits<RetainPtr<T>> : SimpleClassHashTraits<RetainPtr<T>> {};
+
+template <typename T>
+struct RetainPtrHash
+ : PtrHash<
+ typename std::remove_pointer<typename RetainPtr<T>::PtrType>::type> {
+ using Base = PtrHash<
+ typename std::remove_pointer<typename RetainPtr<T>::PtrType>::type>;
+ using Base::GetHash;
+ static unsigned GetHash(const RetainPtr<T>& key) { return hash(key.Get()); }
+ using Base::Equal;
+ static bool Equal(const RetainPtr<T>& a, const RetainPtr<T>& b) {
+ return a == b;
+ }
+ static bool Equal(typename RetainPtr<T>::PtrType a, const RetainPtr<T>& b) {
+ return a == b;
+ }
+ static bool Equal(const RetainPtr<T>& a, typename RetainPtr<T>::PtrType b) {
+ return a == b;
+ }
+};
+
+template <typename T>
+struct DefaultHash<RetainPtr<T>> {
+ using Hash = RetainPtrHash<T>;
+};
+
+} // namespace WTF
+
+using WTF::kAdoptCF;
+using WTF::kAdoptNS;
+using WTF::AdoptCF;
+using WTF::AdoptNS;
+using WTF::RetainPtr;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_RETAIN_PTR_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/saturated_arithmetic.h b/chromium/third_party/blink/renderer/platform/wtf/saturated_arithmetic.h
new file mode 100644
index 00000000000..5f54bc2fd94
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/saturated_arithmetic.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2012, 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_SATURATED_ARITHMETIC_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_SATURATED_ARITHMETIC_H_
+
+#include "base/compiler_specific.h"
+#include "base/numerics/clamped_math.h"
+#include "build/build_config.h"
+
+#include <stdint.h>
+
+#include <limits>
+
+#if defined(ARCH_CPU_ARM_FAMILY) && defined(ARCH_CPU_32_BITS) && \
+ defined(COMPILER_GCC) && !defined(OS_NACL) && __OPTIMIZE__
+
+// If we're building ARM 32-bit on GCC we replace the C++ versions with some
+// native ARM assembly for speed.
+#include "third_party/blink/renderer/platform/wtf/saturated_arithmetic_arm.h"
+
+#else
+
+namespace WTF {
+
+ALWAYS_INLINE int GetMaxSaturatedSetResultForTesting(int fractional_shift) {
+ // For C version the set function maxes out to max int, this differs from
+ // the ARM asm version, see saturated_arithmetic_arm.h for the equivalent asm
+ // version.
+ return std::numeric_limits<int>::max();
+}
+
+ALWAYS_INLINE int GetMinSaturatedSetResultForTesting(int fractional_shift) {
+ return std::numeric_limits<int>::min();
+}
+
+template <int fractional_shift>
+ALWAYS_INLINE int SaturatedSet(int value) {
+ const int kIntMaxForLayoutUnit =
+ std::numeric_limits<int>::max() >> fractional_shift;
+
+ const int kIntMinForLayoutUnit =
+ std::numeric_limits<int>::min() >> fractional_shift;
+
+ if (value > kIntMaxForLayoutUnit)
+ return std::numeric_limits<int>::max();
+
+ if (value < kIntMinForLayoutUnit)
+ return std::numeric_limits<int>::min();
+
+ return static_cast<unsigned>(value) << fractional_shift;
+}
+
+template <int fractional_shift>
+ALWAYS_INLINE int SaturatedSet(unsigned value) {
+ const unsigned kIntMaxForLayoutUnit =
+ std::numeric_limits<int>::max() >> fractional_shift;
+
+ if (value >= kIntMaxForLayoutUnit)
+ return std::numeric_limits<int>::max();
+
+ return value << fractional_shift;
+}
+
+} // namespace WTF.
+
+#endif // CPU(ARM) && COMPILER(GCC)
+
+namespace WTF {
+using base::ClampAdd;
+using base::ClampSub;
+using base::MakeClampedNum;
+} // namespace WTF.
+
+using WTF::ClampAdd;
+using WTF::ClampSub;
+using WTF::MakeClampedNum;
+using WTF::SaturatedSet;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_SATURATED_ARITHMETIC_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/saturated_arithmetic_arm.h b/chromium/third_party/blink/renderer/platform/wtf/saturated_arithmetic_arm.h
new file mode 100644
index 00000000000..c6f172bec90
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/saturated_arithmetic_arm.h
@@ -0,0 +1,78 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_SATURATED_ARITHMETIC_ARM_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_SATURATED_ARITHMETIC_ARM_H_
+
+#include <limits>
+
+namespace WTF {
+
+inline int GetMaxSaturatedSetResultForTesting(int fractional_shift) {
+ // For ARM Asm version the set function maxes out to the biggest
+ // possible integer part with the fractional part zero'd out.
+ // e.g. 0x7fffffc0.
+ return std::numeric_limits<int>::max() & ~((1 << fractional_shift) - 1);
+}
+
+inline int GetMinSaturatedSetResultForTesting(int fractional_shift) {
+ return std::numeric_limits<int>::min();
+}
+
+template <int fractional_shift>
+inline int SaturatedSet(int value) {
+ // Figure out how many bits are left for storing the integer part of
+ // the fixed point number, and saturate our input to that
+ enum { Saturate = 32 - fractional_shift };
+
+ int result;
+
+ // The following ARM code will Saturate the passed value to the number of
+ // bits used for the whole part of the fixed point representation, then
+ // shift it up into place. This will result in the low <FractionShift> bits
+ // all being 0's. When the value saturates this gives a different result
+ // to from the C++ case; in the C++ code a saturated value has all the low
+ // bits set to 1 (for a +ve number at least). This cannot be done rapidly
+ // in ARM ... we live with the difference, for the sake of speed.
+
+ asm("ssat %[output],%[saturate],%[value]\n\t"
+ "lsl %[output],%[shift]"
+ : [output] "=r"(result)
+ : [value] "r"(value), [saturate] "n"(Saturate),
+ [shift] "n"(fractional_shift));
+
+ return result;
+}
+
+template <int fractional_shift>
+inline int SaturatedSet(unsigned value) {
+ // Here we are being passed an unsigned value to saturate,
+ // even though the result is returned as a signed integer. The ARM
+ // instruction for unsigned saturation therefore needs to be given one
+ // less bit (i.e. the sign bit) for the saturation to work correctly; hence
+ // the '31' below.
+ enum { Saturate = 31 - fractional_shift };
+
+ // The following ARM code will Saturate the passed value to the number of
+ // bits used for the whole part of the fixed point representation, then
+ // shift it up into place. This will result in the low <FractionShift> bits
+ // all being 0's. When the value saturates this gives a different result
+ // to from the C++ case; in the C++ code a saturated value has all the low
+ // bits set to 1. This cannot be done rapidly in ARM, so we live with the
+ // difference, for the sake of speed.
+
+ int result;
+
+ asm("usat %[output],%[saturate],%[value]\n\t"
+ "lsl %[output],%[shift]"
+ : [output] "=r"(result)
+ : [value] "r"(value), [saturate] "n"(Saturate),
+ [shift] "n"(fractional_shift));
+
+ return result;
+}
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_SATURATED_ARITHMETIC_ARM_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/saturated_arithmetic_test.cc b/chromium/third_party/blink/renderer/platform/wtf/saturated_arithmetic_test.cc
new file mode 100644
index 00000000000..0933021dd43
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/saturated_arithmetic_test.cc
@@ -0,0 +1,69 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <limits>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/saturated_arithmetic.h"
+
+namespace WTF {
+
+TEST(SaturatedArithmeticTest, SetSigned) {
+ int int_max = std::numeric_limits<int>::max();
+ int int_min = std::numeric_limits<int>::min();
+
+ const int kFractionBits = 6;
+ const int kIntMaxForLayoutUnit = int_max >> kFractionBits;
+ const int kIntMinForLayoutUnit = int_min >> kFractionBits;
+
+ EXPECT_EQ(0, SaturatedSet<kFractionBits>(0));
+
+ // Internally the max number we can represent (without saturating)
+ // is all the (non-sign) bits set except for the bottom n fraction bits
+ const int max_internal_representation = int_max ^ ((1 << kFractionBits) - 1);
+ EXPECT_EQ(max_internal_representation,
+ SaturatedSet<kFractionBits>(kIntMaxForLayoutUnit));
+
+ EXPECT_EQ(GetMaxSaturatedSetResultForTesting(kFractionBits),
+ SaturatedSet<kFractionBits>(kIntMaxForLayoutUnit + 100));
+
+ EXPECT_EQ((kIntMaxForLayoutUnit - 100) << kFractionBits,
+ SaturatedSet<kFractionBits>(kIntMaxForLayoutUnit - 100));
+
+ EXPECT_EQ(GetMinSaturatedSetResultForTesting(kFractionBits),
+ SaturatedSet<kFractionBits>(kIntMinForLayoutUnit));
+
+ EXPECT_EQ(GetMinSaturatedSetResultForTesting(kFractionBits),
+ SaturatedSet<kFractionBits>(kIntMinForLayoutUnit - 100));
+
+ // Shifting negative numbers left has undefined behavior, so use
+ // multiplication instead of direct shifting here.
+ EXPECT_EQ((kIntMinForLayoutUnit + 100) * (1 << kFractionBits),
+ SaturatedSet<kFractionBits>(kIntMinForLayoutUnit + 100));
+}
+
+TEST(SaturatedArithmeticTest, SetUnsigned) {
+ int int_max = std::numeric_limits<int>::max();
+
+ const int kFractionBits = 6;
+ const int kIntMaxForLayoutUnit = int_max >> kFractionBits;
+
+ EXPECT_EQ(0, SaturatedSet<kFractionBits>((unsigned)0));
+
+ EXPECT_EQ(GetMaxSaturatedSetResultForTesting(kFractionBits),
+ SaturatedSet<kFractionBits>((unsigned)kIntMaxForLayoutUnit));
+
+ const unsigned kOverflowed = kIntMaxForLayoutUnit + 100;
+ EXPECT_EQ(GetMaxSaturatedSetResultForTesting(kFractionBits),
+ SaturatedSet<kFractionBits>(kOverflowed));
+
+ const unsigned kNotOverflowed = kIntMaxForLayoutUnit - 100;
+ EXPECT_EQ((kIntMaxForLayoutUnit - 100) << kFractionBits,
+ SaturatedSet<kFractionBits>(kNotOverflowed));
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/scoped_logger.cc b/chromium/third_party/blink/renderer/platform/wtf/scoped_logger.cc
new file mode 100644
index 00000000000..d61c9c81ba4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/scoped_logger.cc
@@ -0,0 +1,101 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/wtf/scoped_logger.h"
+
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/thread_specific.h"
+
+#if DCHECK_IS_ON()
+
+namespace WTF {
+
+ScopedLogger::ScopedLogger(bool condition, const char* format, ...)
+ : parent_(condition ? Current() : nullptr), multiline_(false) {
+ if (!condition)
+ return;
+
+ va_list args;
+ va_start(args, format);
+ Init(format, args);
+ va_end(args);
+}
+
+ScopedLogger::~ScopedLogger() {
+ if (Current() == this) {
+ if (multiline_)
+ Indent();
+ else
+ Print(" ");
+ Print(")\n");
+ Current() = parent_;
+ }
+}
+
+void ScopedLogger::SetPrintFuncForTests(PrintFunctionPtr ptr) {
+ print_func_ = ptr;
+};
+
+void ScopedLogger::Init(const char* format, va_list args) {
+ Current() = this;
+ if (parent_)
+ parent_->WriteNewlineIfNeeded();
+ Indent();
+ Print("( ");
+ print_func_(format, args);
+}
+
+void ScopedLogger::WriteNewlineIfNeeded() {
+ if (!multiline_) {
+ Print("\n");
+ multiline_ = true;
+ }
+}
+
+void ScopedLogger::Indent() {
+ if (parent_) {
+ parent_->Indent();
+ PrintIndent();
+ }
+}
+
+void ScopedLogger::Log(const char* format, ...) {
+ if (Current() != this)
+ return;
+
+ va_list args;
+ va_start(args, format);
+
+ WriteNewlineIfNeeded();
+ Indent();
+ PrintIndent();
+ print_func_(format, args);
+ Print("\n");
+
+ va_end(args);
+}
+
+void ScopedLogger::Print(const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ print_func_(format, args);
+ va_end(args);
+}
+
+void ScopedLogger::PrintIndent() {
+ Print(" ");
+}
+
+ScopedLogger*& ScopedLogger::Current() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadSpecific<ScopedLogger*>, ref, ());
+ return *ref;
+}
+
+ScopedLogger::PrintFunctionPtr ScopedLogger::print_func_ =
+ vprintf_stderr_common;
+
+} // namespace WTF
+
+#endif // DCHECK_IS_ON
diff --git a/chromium/third_party/blink/renderer/platform/wtf/scoped_logger.h b/chromium/third_party/blink/renderer/platform/wtf/scoped_logger.h
new file mode 100644
index 00000000000..f297a6b722a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/scoped_logger.h
@@ -0,0 +1,71 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_SCOPED_LOGGER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_SCOPED_LOGGER_H_
+
+#include "base/gtest_prod_util.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_export.h"
+
+namespace WTF {
+
+#if !DCHECK_IS_ON()
+
+#define WTF_CREATE_SCOPED_LOGGER(...) ((void)0)
+#define WTF_CREATE_SCOPED_LOGGER_IF(...) ((void)0)
+#define WTF_APPEND_SCOPED_LOGGER(...) ((void)0)
+
+#else
+
+// ScopedLogger wraps log messages in parentheses, with indentation proportional
+// to the number of instances. This makes it easy to see the flow of control in
+// the output, particularly when instrumenting recursive functions.
+//
+// NOTE: This class is a debugging tool, not intended for use by checked-in
+// code. Please do not remove it.
+//
+class WTF_EXPORT ScopedLogger {
+ public:
+ // The first message is passed to the constructor. Additional messages for
+ // the same scope can be added with log(). If condition is false, produce no
+ // output and do not create a scope.
+ PRINTF_FORMAT(3, 4) ScopedLogger(bool condition, const char* format, ...);
+ ~ScopedLogger();
+ PRINTF_FORMAT(2, 3) void Log(const char* format, ...);
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(ScopedLoggerTest, ScopedLogger);
+ using PrintFunctionPtr = void (*)(const char* format, va_list args);
+
+ // Note: not thread safe.
+ static void SetPrintFuncForTests(PrintFunctionPtr);
+
+ void Init(const char* format, va_list args);
+ void WriteNewlineIfNeeded();
+ void Indent();
+ void Print(const char* format, ...);
+ void PrintIndent();
+ static ScopedLogger*& Current();
+
+ ScopedLogger* const parent_;
+ bool multiline_; // The ')' will go on the same line if there is only one
+ // entry.
+ static PrintFunctionPtr print_func_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedLogger);
+};
+
+#define WTF_CREATE_SCOPED_LOGGER(name, ...) \
+ WTF::ScopedLogger name(true, __VA_ARGS__)
+#define WTF_CREATE_SCOPED_LOGGER_IF(name, condition, ...) \
+ WTF::ScopedLogger name(condition, __VA_ARGS__)
+#define WTF_APPEND_SCOPED_LOGGER(name, ...) (name.Log(__VA_ARGS__))
+
+#endif // !DCHECK_IS_ON()
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_SCOPED_LOGGER_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/scoped_logger_test.cc b/chromium/third_party/blink/renderer/platform/wtf/scoped_logger_test.cc
new file mode 100644
index 00000000000..789509fbcf6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/scoped_logger_test.cc
@@ -0,0 +1,49 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/wtf/scoped_logger.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+
+namespace WTF {
+
+#if DCHECK_IS_ON()
+static const int kPrinterBufferSize = 256;
+static char g_buffer[kPrinterBufferSize];
+static StringBuilder g_builder;
+
+static void Vprint(const char* format, va_list args) {
+ int written = vsnprintf(g_buffer, kPrinterBufferSize, format, args);
+ if (written > 0 && written < kPrinterBufferSize)
+ g_builder.Append(g_buffer);
+}
+
+TEST(ScopedLoggerTest, ScopedLogger) {
+ ScopedLogger::SetPrintFuncForTests(Vprint);
+ {
+ WTF_CREATE_SCOPED_LOGGER(a, "a1");
+ {
+ WTF_CREATE_SCOPED_LOGGER_IF(b, false, "b1");
+ {
+ WTF_CREATE_SCOPED_LOGGER(c, "c");
+ { WTF_CREATE_SCOPED_LOGGER(d, "d %d %s", -1, "hello"); }
+ }
+ WTF_APPEND_SCOPED_LOGGER(b, "b2");
+ }
+ WTF_APPEND_SCOPED_LOGGER(a, "a2 %.1f", 0.5);
+ }
+
+ EXPECT_EQ(
+ "( a1\n"
+ " ( c\n"
+ " ( d -1 hello )\n"
+ " )\n"
+ " a2 0.5\n"
+ ")\n",
+ g_builder.ToString());
+};
+#endif // DCHECK_IS_ON()
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/size_assertions.h b/chromium/third_party/blink/renderer/platform/wtf/size_assertions.h
new file mode 100644
index 00000000000..a5de40f4a18
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/size_assertions.h
@@ -0,0 +1,29 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_SIZE_ASSERTIONS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_SIZE_ASSERTIONS_H_
+
+namespace WTF {
+
+// The ASSERT_SIZE macro can be used to check that a given struct is the same
+// size as a class. This is useful to visualize where the space is being used in
+// a class, as well as give a useful compile error message when the size doesn't
+// match the expected value.
+template <class T, class U>
+struct assert_size {
+ template <int ActualSize, int ExpectedSize>
+ struct assertSizeEqual {
+ static_assert(ActualSize == ExpectedSize, "Class should stay small");
+ static const bool kInnerValue = true;
+ };
+ static const bool value = assertSizeEqual<sizeof(T), sizeof(U)>::kInnerValue;
+};
+
+} // namespace WTF
+
+#define ASSERT_SIZE(className, sameSizeAsClassName) \
+ static_assert(WTF::assert_size<className, sameSizeAsClassName>::value, "");
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_SIZE_ASSERTIONS_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/size_limits.cc b/chromium/third_party/blink/renderer/platform/wtf/size_limits.cc
new file mode 100644
index 00000000000..db26bdfc3f4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/size_limits.cc
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2010 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 <memory>
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/container_annotations.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/thread_restriction_verifier.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace WTF {
+
+struct SameSizeAsRefCounted {
+ uint32_t a;
+ // Don't add anything here because this should stay small.
+};
+
+template <typename T, unsigned inlineCapacity = 0>
+struct SameSizeAsVectorWithInlineCapacity;
+
+template <typename T>
+struct SameSizeAsVectorWithInlineCapacity<T, 0> {
+ void* buffer_pointer;
+ unsigned capacity;
+ unsigned size;
+};
+
+template <typename T, unsigned inlineCapacity>
+struct SameSizeAsVectorWithInlineCapacity {
+ SameSizeAsVectorWithInlineCapacity<T, 0> base_capacity;
+#if !defined(ANNOTATE_CONTIGUOUS_CONTAINER)
+ T inline_buffer[inlineCapacity];
+#endif
+};
+
+#if !DCHECK_IS_ON()
+static_assert(sizeof(RefCounted<int>) == sizeof(SameSizeAsRefCounted),
+ "RefCounted should stay small");
+#endif
+
+static_assert(sizeof(std::unique_ptr<int>) == sizeof(int*),
+ "std::unique_ptr should stay small");
+static_assert(sizeof(scoped_refptr<RefCounted<int>>) == sizeof(int*),
+ "RefPtr should stay small");
+static_assert(sizeof(String) == sizeof(int*), "String should stay small");
+static_assert(sizeof(AtomicString) == sizeof(String),
+ "AtomicString should stay small");
+static_assert(sizeof(Vector<int>) ==
+ sizeof(SameSizeAsVectorWithInlineCapacity<int>),
+ "Vector should stay small");
+static_assert(sizeof(Vector<int, 1>) ==
+ sizeof(SameSizeAsVectorWithInlineCapacity<int, 1>),
+ "Vector should stay small");
+static_assert(sizeof(Vector<int, 2>) ==
+ sizeof(SameSizeAsVectorWithInlineCapacity<int, 2>),
+ "Vector should stay small");
+static_assert(sizeof(Vector<int, 3>) ==
+ sizeof(SameSizeAsVectorWithInlineCapacity<int, 3>),
+ "Vector should stay small");
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/stack_util.cc b/chromium/third_party/blink/renderer/platform/wtf/stack_util.cc
new file mode 100644
index 00000000000..10f1c0df11f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/stack_util.cc
@@ -0,0 +1,200 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/wtf/stack_util.h"
+
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/threading.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_thread_data.h"
+
+#if defined(OS_WIN)
+#include <stddef.h>
+#include <windows.h>
+#include <winnt.h>
+#elif defined(__GLIBC__)
+extern "C" void* __libc_stack_end; // NOLINT
+#endif
+
+namespace WTF {
+
+size_t GetUnderestimatedStackSize() {
+// FIXME: ASAN bot uses a fake stack as a thread stack frame,
+// and its size is different from the value which APIs tells us.
+#if defined(ADDRESS_SANITIZER)
+ return 0;
+#endif
+
+// FIXME: On Mac OSX and Linux, this method cannot estimate stack size
+// correctly for the main thread.
+
+#if defined(__GLIBC__) || defined(OS_ANDROID) || defined(OS_FREEBSD) || \
+ defined(OS_FUCHSIA)
+ // pthread_getattr_np() can fail if the thread is not invoked by
+ // pthread_create() (e.g., the main thread of webkit_unit_tests).
+ // If so, a conservative size estimate is returned.
+
+ pthread_attr_t attr;
+ int error;
+#if defined(OS_FREEBSD)
+ pthread_attr_init(&attr);
+ error = pthread_attr_get_np(pthread_self(), &attr);
+#else
+ error = pthread_getattr_np(pthread_self(), &attr);
+#endif
+ if (!error) {
+ void* base;
+ size_t size;
+ error = pthread_attr_getstack(&attr, &base, &size);
+ CHECK(!error);
+ pthread_attr_destroy(&attr);
+ return size;
+ }
+#if defined(OS_FREEBSD)
+ pthread_attr_destroy(&attr);
+#endif
+
+ // Return a 512k stack size, (conservatively) assuming the following:
+ // - that size is much lower than the pthreads default (x86 pthreads has a 2M
+ // default.)
+ // - no one is running Blink with an RLIMIT_STACK override, let alone as
+ // low as 512k.
+ //
+ return 512 * 1024;
+#elif defined(OS_MACOSX)
+ // pthread_get_stacksize_np() returns too low a value for the main thread on
+ // OSX 10.9,
+ // http://mail.openjdk.java.net/pipermail/hotspot-dev/2013-October/011369.html
+ //
+ // Multiple workarounds possible, adopt the one made by
+ // https://github.com/robovm/robovm/issues/274
+ // (cf.
+ // https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html
+ // on why hardcoding sizes is reasonable.)
+ if (pthread_main_np()) {
+#if defined(IOS)
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ size_t guardSize = 0;
+ pthread_attr_getguardsize(&attr, &guardSize);
+ // Stack size for the main thread is 1MB on iOS including the guard page
+ // size.
+ return (1 * 1024 * 1024 - guardSize);
+#else
+ // Stack size for the main thread is 8MB on OSX excluding the guard page
+ // size.
+ return (8 * 1024 * 1024);
+#endif
+ }
+ return pthread_get_stacksize_np(pthread_self());
+#elif defined(OS_WIN) && defined(COMPILER_MSVC)
+ return WTFThreadData::ThreadStackSize();
+#else
+#error "Stack frame size estimation not supported on this platform."
+ return 0;
+#endif
+}
+
+void* GetStackStart() {
+#if defined(__GLIBC__) || defined(OS_ANDROID) || defined(OS_FREEBSD) || \
+ defined(OS_FUCHSIA)
+ pthread_attr_t attr;
+ int error;
+#if defined(OS_FREEBSD)
+ pthread_attr_init(&attr);
+ error = pthread_attr_get_np(pthread_self(), &attr);
+#else
+ error = pthread_getattr_np(pthread_self(), &attr);
+#endif
+ if (!error) {
+ void* base;
+ size_t size;
+ error = pthread_attr_getstack(&attr, &base, &size);
+ CHECK(!error);
+ pthread_attr_destroy(&attr);
+ return reinterpret_cast<uint8_t*>(base) + size;
+ }
+#if defined(OS_FREEBSD)
+ pthread_attr_destroy(&attr);
+#endif
+#if defined(__GLIBC__)
+ // pthread_getattr_np can fail for the main thread. In this case
+ // just like NaCl we rely on the __libc_stack_end to give us
+ // the start of the stack.
+ // See https://code.google.com/p/nativeclient/issues/detail?id=3431.
+ return __libc_stack_end;
+#else
+ NOTREACHED();
+ return nullptr;
+#endif
+#elif defined(OS_MACOSX)
+ return pthread_get_stackaddr_np(pthread_self());
+#elif defined(OS_WIN) && defined(COMPILER_MSVC)
+// On Windows stack limits for the current thread are available in
+// the thread information block (TIB). Its fields can be accessed through
+// FS segment register on x86 and GS segment register on x86_64.
+#ifdef _WIN64
+ return reinterpret_cast<void*>(__readgsqword(offsetof(NT_TIB64, StackBase)));
+#else
+ return reinterpret_cast<void*>(__readfsdword(offsetof(NT_TIB, StackBase)));
+#endif
+#else
+#error Unsupported getStackStart on this platform.
+#endif
+}
+
+namespace internal {
+
+uintptr_t g_main_thread_stack_start = 0;
+uintptr_t g_main_thread_underestimated_stack_size = 0;
+
+void InitializeMainThreadStackEstimate() {
+ // getStackStart is exclusive, not inclusive (i.e. it points past the last
+ // page of the stack in linear order). So, to ensure an inclusive comparison,
+ // subtract here and below.
+ g_main_thread_stack_start =
+ reinterpret_cast<uintptr_t>(GetStackStart()) - sizeof(void*);
+
+ size_t underestimated_stack_size = GetUnderestimatedStackSize();
+ if (underestimated_stack_size > sizeof(void*)) {
+ underestimated_stack_size = underestimated_stack_size - sizeof(void*);
+ }
+ g_main_thread_underestimated_stack_size = underestimated_stack_size;
+}
+
+#if defined(OS_WIN) && defined(COMPILER_MSVC)
+size_t ThreadStackSize() {
+ // Notice that we cannot use the TIB's StackLimit for the stack end, as i
+ // tracks the end of the committed range. We're after the end of the reserved
+ // stack area (most of which will be uncommitted, most times.)
+ MEMORY_BASIC_INFORMATION stack_info;
+ memset(&stack_info, 0, sizeof(MEMORY_BASIC_INFORMATION));
+ size_t result_size =
+ VirtualQuery(&stack_info, &stack_info, sizeof(MEMORY_BASIC_INFORMATION));
+ DCHECK_GE(result_size, sizeof(MEMORY_BASIC_INFORMATION));
+ uint8_t* stack_end = reinterpret_cast<uint8_t*>(stack_info.AllocationBase);
+
+ uint8_t* stack_start = reinterpret_cast<uint8_t*>(WTF::GetStackStart());
+ CHECK(stack_start);
+ CHECK_GT(stack_start, stack_end);
+ size_t thread_stack_size = static_cast<size_t>(stack_start - stack_end);
+ // When the third last page of the reserved stack is accessed as a
+ // guard page, the second last page will be committed (along with removing
+ // the guard bit on the third last) _and_ a stack overflow exception
+ // is raised.
+ //
+ // We have zero interest in running into stack overflow exceptions while
+ // marking objects, so simply consider the last three pages + one above
+ // as off-limits and adjust the reported stack size accordingly.
+ //
+ // http://blogs.msdn.com/b/satyem/archive/2012/08/13/thread-s-stack-memory-management.aspx
+ // explains the details.
+ CHECK_GT(thread_stack_size, 4u * 0x1000);
+ thread_stack_size -= 4 * 0x1000;
+ return thread_stack_size;
+}
+#endif
+
+} // namespace internal
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/stack_util.h b/chromium/third_party/blink/renderer/platform/wtf/stack_util.h
new file mode 100644
index 00000000000..b94f759bc1b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/stack_util.h
@@ -0,0 +1,48 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_STACK_UTIL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_STACK_UTIL_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_export.h"
+
+namespace WTF {
+
+WTF_EXPORT size_t GetUnderestimatedStackSize();
+WTF_EXPORT void* GetStackStart();
+
+namespace internal {
+
+WTF_EXPORT extern uintptr_t g_main_thread_stack_start;
+WTF_EXPORT extern uintptr_t g_main_thread_underestimated_stack_size;
+
+WTF_EXPORT void InitializeMainThreadStackEstimate();
+
+#if defined(OS_WIN) && defined(COMPILER_MSVC)
+size_t ThreadStackSize();
+#endif
+
+} // namespace internal
+
+// Returns true if the function is not called on the main thread. Note carefully
+// that this function may have false positives, i.e. it can return true even if
+// we are on the main thread. If the function returns false, we are certainly
+// on the main thread.
+inline bool MayNotBeMainThread() {
+ uintptr_t dummy;
+ uintptr_t address_diff =
+ internal::g_main_thread_stack_start - reinterpret_cast<uintptr_t>(&dummy);
+ // This is a fast way to judge if we are in the main thread.
+ // If |&dummy| is within |s_mainThreadUnderestimatedStackSize| byte from
+ // the stack start of the main thread, we judge that we are in
+ // the main thread.
+ return address_diff >= internal::g_main_thread_underestimated_stack_size;
+}
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_STACK_UTIL_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/static_constructors.h b/chromium/third_party/blink/renderer/platform/wtf/static_constructors.h
new file mode 100644
index 00000000000..38d15e5178a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/static_constructors.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_STATIC_CONSTRUCTORS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_STATIC_CONSTRUCTORS_H_
+
+// We need to avoid having static constructors. This is accomplished by defining
+// a static array of the appropriate size and alignment, and defining a const
+// reference that points to the buffer. During initialization, the object will
+// be constructed with placement new into the buffer. This works with MSVC, GCC,
+// and Clang without producing dynamic initialization code even at -O0. The only
+// downside is that all external translation units will have to emit one more
+// load, while a real global could be referenced directly by absolute or
+// relative addressing.
+
+// Use an array of pointers instead of an array of char in case there is some
+// alignment issue.
+#define DEFINE_GLOBAL(type, name) \
+ void* name##Storage[(sizeof(type) + sizeof(void*) - 1) / sizeof(void*)]; \
+ const type& name = *reinterpret_cast<type*>(&name##Storage)
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_STATIC_CONSTRUCTORS_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/std_lib_extras.h b/chromium/third_party/blink/renderer/platform/wtf/std_lib_extras.h
new file mode 100644
index 00000000000..9ed1567de78
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/std_lib_extras.h
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2008 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_STD_LIB_EXTRAS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_STD_LIB_EXTRAS_H_
+
+#include <cstddef>
+
+#include "base/macros.h"
+#include "base/numerics/safe_conversions.h"
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/leak_annotations.h"
+#include "third_party/blink/renderer/platform/wtf/type_traits.h"
+
+#if DCHECK_IS_ON()
+#include "third_party/blink/renderer/platform/wtf/threading.h"
+#endif
+
+#define DEFINE_STATIC_LOCAL_IMPL(Type, Name, Arguments, allow_cross_thread) \
+ static WTF::StaticSingleton<Type> s_##Name( \
+ [&]() { return new WTF::StaticSingleton<Type>::WrapperType Arguments; }, \
+ [&](void* leaked_ptr) { \
+ new (leaked_ptr) WTF::StaticSingleton<Type>::WrapperType Arguments; \
+ }); \
+ Type& Name = s_##Name.Get(allow_cross_thread)
+
+// Use |DEFINE_STATIC_LOCAL()| to declare and define a static local variable
+// (|static T;|) so that it is leaked and its destructors are not called at
+// exit. T may also be a Blink garbage collected object, in which case it is
+// wrapped up by an off-heap |Persistent<T>| reference to the object, keeping
+// it alive across GCs.
+//
+// A |DEFINE_STATIC_LOCAL()| static should only be used on the thread it was
+// created on.
+//
+#define DEFINE_STATIC_LOCAL(Type, Name, Arguments) \
+ DEFINE_STATIC_LOCAL_IMPL(Type, Name, Arguments, false)
+
+// |DEFINE_THREAD_SAFE_STATIC_LOCAL()| is the cross-thread accessible variant
+// of |DEFINE_STATIC_LOCAL()|; use it if the singleton can be accessed by
+// multiple threads.
+//
+// TODO: rename as DEFINE_CROSS_THREAD_STATIC_LOCAL() ?
+#define DEFINE_THREAD_SAFE_STATIC_LOCAL(Type, Name, Arguments) \
+ DEFINE_STATIC_LOCAL_IMPL(Type, Name, Arguments, true)
+
+namespace blink {
+template <typename T>
+class Persistent;
+
+} // namespace blink
+
+namespace WTF {
+
+template <typename Type>
+class StaticSingleton final {
+ public:
+ template <typename T,
+ bool = WTF::IsGarbageCollectedType<T>::value &&
+ !WTF::IsPersistentReferenceType<T>::value>
+ struct Wrapper {
+ using type = T;
+
+ static T& Unwrap(T* singleton) { return *singleton; }
+ };
+
+ template <typename T>
+ struct Wrapper<T, true> {
+ using type = blink::Persistent<T>;
+
+ static T& Unwrap(blink::Persistent<T>* singleton) {
+ DCHECK(singleton);
+ // If this assert triggers, you're supplying an empty ("()") 'Arguments'
+ // argument to DEFINE_STATIC_LOCAL() - it must be the heap object you wish
+ // to create as a static singleton and wrapped up with a Persistent
+ // reference.
+ DCHECK(*singleton);
+ return **singleton;
+ }
+ };
+
+ using WrapperType = typename Wrapper<Type>::type;
+
+ // To cooperate with leak detection(LSan) for Blink garbage collected objects,
+ // the objects owned by persistent local statics will in some cases have to be
+ // finalized prior to leak checking. This only applies to static references to
+ // Blink heap objects and what they transitively hold on to. Hence the
+ // LEAK_SANITIZER_REGISTER_STATIC_LOCAL() use, it taking care of the grungy
+ // details.
+
+ template <typename HeapNew, typename PlacementNew>
+ StaticSingleton(const HeapNew& heap_new, const PlacementNew& placement_new)
+ : instance_(heap_new, placement_new)
+#if DCHECK_IS_ON()
+ ,
+ safely_initialized_(WTF::IsBeforeThreadCreated()),
+ thread_(WTF::internal::CurrentThreadSyscall())
+#endif
+ {
+ LEAK_SANITIZER_REGISTER_STATIC_LOCAL(WrapperType, instance_.Get());
+ }
+
+ Type& Get(bool allow_cross_thread_use) {
+#if DCHECK_IS_ON()
+ DCHECK(IsNotRacy(allow_cross_thread_use));
+#endif
+ ALLOW_UNUSED_LOCAL(allow_cross_thread_use);
+ return Wrapper<Type>::Unwrap(instance_.Get());
+ }
+
+ operator Type&() { return Get(); }
+
+ private:
+#if DCHECK_IS_ON()
+
+ bool IsNotRacy(bool allow_cross_thread_use) const {
+ // Make sure that singleton is safely initialized, or
+ // keeps being called on the same thread if cross-thread
+ // use is not permitted.
+ return allow_cross_thread_use || safely_initialized_ ||
+ thread_ == WTF::internal::CurrentThreadSyscall();
+ }
+#endif
+ template <typename T, bool is_small = sizeof(T) <= 32>
+ class InstanceStorage {
+ public:
+ template <typename HeapNew, typename PlacementNew>
+ InstanceStorage(const HeapNew& heap_new, const PlacementNew&)
+ : pointer_(heap_new()) {}
+ T* Get() { return pointer_; }
+
+ private:
+ T* pointer_;
+ };
+
+ template <typename T>
+ class InstanceStorage<T, true> {
+ public:
+ template <typename HeapNew, typename PlacementNew>
+ InstanceStorage(const HeapNew&, const PlacementNew& placement_new) {
+ placement_new(&object_);
+ }
+ T* Get() { return reinterpret_cast<T*>(object_); }
+
+ private:
+ alignas(T) char object_[sizeof(T)];
+ };
+
+ InstanceStorage<WrapperType> instance_;
+#if DCHECK_IS_ON()
+ bool safely_initialized_;
+ ThreadIdentifier thread_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(StaticSingleton);
+};
+
+} // namespace WTF
+
+// Use this to declare and define a static local pointer to a ref-counted object
+// so that it is leaked so that the object's destructors are not called at
+// exit. This macro should be used with ref-counted objects rather than
+// DEFINE_STATIC_LOCAL macro, as this macro does not lead to an extra memory
+// allocation.
+#define DEFINE_STATIC_REF(type, name, arguments) \
+ static type* name = [](scoped_refptr<type> o) { \
+ if (o) \
+ o->AddRef(); \
+ return o.get(); \
+ }(arguments);
+
+/*
+ * The reinterpret_cast<Type1*>([pointer to Type2]) expressions - where
+ * sizeof(Type1) > sizeof(Type2) - cause the following warning on ARM with GCC:
+ * increases required alignment of target type.
+ *
+ * An implicit or an extra static_cast<void*> bypasses the warning.
+ * For more info see the following bugzilla entries:
+ * - https://bugs.webkit.org/show_bug.cgi?id=38045
+ * - http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43976
+ */
+#if defined(ARCH_CPU_ARMEL) && defined(COMPILER_GCC)
+template <typename Type>
+bool isPointerTypeAlignmentOkay(Type* ptr) {
+ return !(reinterpret_cast<intptr_t>(ptr) % __alignof__(Type));
+}
+
+template <typename TypePtr>
+TypePtr reinterpret_cast_ptr(void* ptr) {
+ DCHECK(isPointerTypeAlignmentOkay(reinterpret_cast<TypePtr>(ptr)));
+ return reinterpret_cast<TypePtr>(ptr);
+}
+
+template <typename TypePtr>
+TypePtr reinterpret_cast_ptr(const void* ptr) {
+ DCHECK(isPointerTypeAlignmentOkay(reinterpret_cast<TypePtr>(ptr)));
+ return reinterpret_cast<TypePtr>(ptr);
+}
+#else
+template <typename Type>
+bool isPointerTypeAlignmentOkay(Type*) {
+ return true;
+}
+#define reinterpret_cast_ptr reinterpret_cast
+#endif
+
+template <typename TypePtr>
+NO_SANITIZE_UNRELATED_CAST
+TypePtr unsafe_reinterpret_cast_ptr(void* ptr) {
+#if defined(ARCH_CPU_ARMEL) && defined(COMPILER_GCC)
+ DCHECK(isPointerTypeAlignmentOkay(reinterpret_cast<TypePtr>(ptr)));
+#endif
+ return reinterpret_cast<TypePtr>(ptr);
+}
+
+template <typename TypePtr>
+NO_SANITIZE_UNRELATED_CAST
+TypePtr unsafe_reinterpret_cast_ptr(const void* ptr) {
+#if defined(ARCH_CPU_ARMEL) && defined(COMPILER_GCC)
+ DCHECK(isPointerTypeAlignmentOkay(reinterpret_cast<TypePtr>(ptr)));
+#endif
+ return reinterpret_cast<TypePtr>(ptr);
+}
+
+namespace WTF {
+
+template <typename To, typename From>
+inline To SafeCast(From value) {
+ return base::checked_cast<To>(value);
+}
+
+// Use the following macros to prevent errors caused by accidental
+// implicit casting of function arguments. For example, this can
+// be used to prevent overflows from non-promoting conversions.
+//
+// Example:
+//
+// HAS_STRICTLY_TYPED_ARG
+// void sendData(void* data, STRICTLY_TYPED_ARG(size))
+// {
+// ALLOW_NUMERIC_ARG_TYPES_PROMOTABLE_TO(size_t);
+// ...
+// }
+//
+// The previous example will prevent callers from passing, for example, an
+// 'int'. On a 32-bit build, it will prevent use of an 'unsigned long long'.
+#define HAS_STRICTLY_TYPED_ARG template <typename ActualArgType>
+#define STRICTLY_TYPED_ARG(argName) ActualArgType argName
+#define STRICT_ARG_TYPE(ExpectedArgType) \
+ static_assert(std::is_same<ActualArgType, ExpectedArgType>::value, \
+ "Strictly typed argument must be of type '" #ExpectedArgType \
+ "'.")
+#define ALLOW_NUMERIC_ARG_TYPES_PROMOTABLE_TO(ExpectedArgType) \
+ static_assert( \
+ std::numeric_limits<ExpectedArgType>::is_integer == \
+ std::numeric_limits<ActualArgType>::is_integer, \
+ "Conversion between integer and non-integer types not allowed."); \
+ static_assert(sizeof(ExpectedArgType) >= sizeof(ActualArgType), \
+ "Truncating conversions not allowed."); \
+ static_assert(!std::numeric_limits<ActualArgType>::is_signed || \
+ std::numeric_limits<ExpectedArgType>::is_signed, \
+ "Signed to unsigned conversion not allowed."); \
+ static_assert((sizeof(ExpectedArgType) != sizeof(ActualArgType)) || \
+ (std::numeric_limits<ActualArgType>::is_signed == \
+ std::numeric_limits<ExpectedArgType>::is_signed), \
+ "Unsigned to signed conversion not allowed for types with " \
+ "identical size (could overflow).");
+
+// Macro that returns a compile time constant with the length of an array, but
+// gives an error if passed a non-array.
+template <typename T, size_t Size>
+char (&ArrayLengthHelperFunction(T (&)[Size]))[Size];
+// GCC needs some help to deduce a 0 length array.
+#if defined(COMPILER_GCC)
+template <typename T>
+char (&ArrayLengthHelperFunction(T (&)[0]))[0];
+#endif
+#define WTF_ARRAY_LENGTH(array) sizeof(::WTF::ArrayLengthHelperFunction(array))
+
+} // namespace WTF
+
+using WTF::SafeCast;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_STD_LIB_EXTRAS_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/string_extras.h b/chromium/third_party/blink/renderer/platform/wtf/string_extras.h
new file mode 100644
index 00000000000..816b12a58e5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/string_extras.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2006, 2010 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_STRING_EXTRAS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_STRING_EXTRAS_H_
+
+#include <string.h>
+#include "build/build_config.h"
+
+#if defined(OS_POSIX)
+#include <strings.h>
+#endif
+
+#if defined(COMPILER_MSVC)
+// FIXME: why a compiler check instead of OS? also, these should be HAVE checks
+
+inline int strncasecmp(const char* s1, const char* s2, size_t len) {
+ return _strnicmp(s1, s2, len);
+}
+
+inline int strcasecmp(const char* s1, const char* s2) {
+ return _stricmp(s1, s2);
+}
+
+#endif // defined(COMPILER_MSVC)
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_STRING_EXTRAS_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/string_hasher.h b/chromium/third_party/blink/renderer/platform/wtf/string_hasher.h
new file mode 100644
index 00000000000..6c048bab2f2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/string_hasher.h
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2005, 2006, 2008, 2010, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_STRING_HASHER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_STRING_HASHER_H_
+
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
+
+namespace WTF {
+
+// Paul Hsieh's SuperFastHash
+// http://www.azillionmonkeys.com/qed/hash.html
+
+// LChar data is interpreted as Latin-1-encoded (zero extended to 16 bits).
+
+// NOTE: The hash computation here must stay in sync with
+// build/scripts/hasher.py.
+
+// Golden ratio. Arbitrary start value to avoid mapping all zeros to a hash
+// value of zero.
+static const unsigned kStringHashingStartValue = 0x9E3779B9U;
+
+class StringHasher {
+ DISALLOW_NEW();
+
+ public:
+ static const unsigned kFlagCount =
+ 8; // Save 8 bits for StringImpl to use as flags.
+
+ StringHasher()
+ : hash_(kStringHashingStartValue),
+ has_pending_character_(false),
+ pending_character_(0) {}
+
+ // The hasher hashes two characters at a time, and thus an "aligned" hasher is
+ // one where an even number of characters have been added. Callers that
+ // always add characters two at a time can use the "assuming aligned"
+ // functions.
+ void AddCharactersAssumingAligned(UChar a, UChar b) {
+ DCHECK(!has_pending_character_);
+ hash_ += a;
+ hash_ = (hash_ << 16) ^ ((b << 11) ^ hash_);
+ hash_ += hash_ >> 11;
+ }
+
+ void AddCharacter(UChar character) {
+ if (has_pending_character_) {
+ has_pending_character_ = false;
+ AddCharactersAssumingAligned(pending_character_, character);
+ return;
+ }
+
+ pending_character_ = character;
+ has_pending_character_ = true;
+ }
+
+ void AddCharacters(UChar a, UChar b) {
+ if (has_pending_character_) {
+#if DCHECK_IS_ON()
+ has_pending_character_ = false;
+#endif
+ AddCharactersAssumingAligned(pending_character_, a);
+ pending_character_ = b;
+#if DCHECK_IS_ON()
+ has_pending_character_ = true;
+#endif
+ return;
+ }
+
+ AddCharactersAssumingAligned(a, b);
+ }
+
+ template <typename T, UChar Converter(T)>
+ void AddCharactersAssumingAligned(const T* data, unsigned length) {
+ DCHECK(!has_pending_character_);
+
+ bool remainder = length & 1;
+ length >>= 1;
+
+ while (length--) {
+ AddCharactersAssumingAligned(Converter(data[0]), Converter(data[1]));
+ data += 2;
+ }
+
+ if (remainder)
+ AddCharacter(Converter(*data));
+ }
+
+ template <typename T>
+ void AddCharactersAssumingAligned(const T* data, unsigned length) {
+ AddCharactersAssumingAligned<T, DefaultConverter>(data, length);
+ }
+
+ template <typename T, UChar Converter(T)>
+ void AddCharacters(const T* data, unsigned length) {
+ if (has_pending_character_ && length) {
+ has_pending_character_ = false;
+ AddCharactersAssumingAligned(pending_character_, Converter(*data++));
+ --length;
+ }
+ AddCharactersAssumingAligned<T, Converter>(data, length);
+ }
+
+ template <typename T>
+ void AddCharacters(const T* data, unsigned length) {
+ AddCharacters<T, DefaultConverter>(data, length);
+ }
+
+ unsigned HashWithTop8BitsMasked() const {
+ unsigned result = AvalancheBits();
+
+ // Reserving space from the high bits for flags preserves most of the hash's
+ // value, since hash lookup typically masks out the high bits anyway.
+ result &= (1U << (sizeof(result) * 8 - kFlagCount)) - 1;
+
+ // This avoids ever returning a hash code of 0, since that is used to
+ // signal "hash not computed yet". Setting the high bit maintains
+ // reasonable fidelity to a hash code of 0 because it is likely to yield
+ // exactly 0 when hash lookup masks out the high bits.
+ if (!result)
+ result = 0x80000000 >> kFlagCount;
+
+ return result;
+ }
+
+ unsigned GetHash() const {
+ unsigned result = AvalancheBits();
+
+ // This avoids ever returning a hash code of 0, since that is used to
+ // signal "hash not computed yet". Setting the high bit maintains
+ // reasonable fidelity to a hash code of 0 because it is likely to yield
+ // exactly 0 when hash lookup masks out the high bits.
+ if (!result)
+ result = 0x80000000;
+
+ return result;
+ }
+
+ template <typename T, UChar Converter(T)>
+ static unsigned ComputeHashAndMaskTop8Bits(const T* data, unsigned length) {
+ StringHasher hasher;
+ hasher.AddCharactersAssumingAligned<T, Converter>(data, length);
+ return hasher.HashWithTop8BitsMasked();
+ }
+
+ template <typename T>
+ static unsigned ComputeHashAndMaskTop8Bits(const T* data, unsigned length) {
+ return ComputeHashAndMaskTop8Bits<T, DefaultConverter>(data, length);
+ }
+
+ template <typename T, UChar Converter(T)>
+ static unsigned ComputeHash(const T* data, unsigned length) {
+ StringHasher hasher;
+ hasher.AddCharactersAssumingAligned<T, Converter>(data, length);
+ return hasher.GetHash();
+ }
+
+ template <typename T>
+ static unsigned ComputeHash(const T* data, unsigned length) {
+ return ComputeHash<T, DefaultConverter>(data, length);
+ }
+
+ static unsigned HashMemory(const void* data, unsigned length) {
+ // FIXME: Why does this function use the version of the hash that drops the
+ // top 8 bits? We want that for all string hashing so we can use those
+ // bits in StringImpl and hash strings consistently, but I don't see why
+ // we'd want that for general memory hashing.
+ DCHECK(!(length % 2));
+ return ComputeHashAndMaskTop8Bits<UChar>(static_cast<const UChar*>(data),
+ length / sizeof(UChar));
+ }
+
+ template <size_t length>
+ static unsigned HashMemory(const void* data) {
+ static_assert(!(length % 2), "length must be a multiple of two");
+ return HashMemory(data, length);
+ }
+
+ private:
+ static UChar DefaultConverter(UChar character) { return character; }
+
+ static UChar DefaultConverter(LChar character) { return character; }
+
+ unsigned AvalancheBits() const {
+ unsigned result = hash_;
+
+ // Handle end case.
+ if (has_pending_character_) {
+ result += pending_character_;
+ result ^= result << 11;
+ result += result >> 17;
+ }
+
+ // Force "avalanching" of final 31 bits.
+ result ^= result << 3;
+ result += result >> 5;
+ result ^= result << 2;
+ result += result >> 15;
+ result ^= result << 10;
+
+ return result;
+ }
+
+ unsigned hash_;
+ bool has_pending_character_;
+ UChar pending_character_;
+};
+
+} // namespace WTF
+
+using WTF::StringHasher;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_STRING_HASHER_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/string_hasher_test.cc b/chromium/third_party/blink/renderer/platform/wtf/string_hasher_test.cc
new file mode 100644
index 00000000000..0e9143b8d20
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/string_hasher_test.cc
@@ -0,0 +1,430 @@
+/*
+ * Copyright (C) 2013 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/wtf/string_hasher.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace WTF {
+
+namespace {
+
+const LChar kNullLChars[2] = {0, 0};
+const UChar kNullUChars[2] = {0, 0};
+
+const unsigned kEmptyStringHash = 0x4EC889EU;
+const unsigned kSingleNullCharacterHash = 0x3D3ABF44U;
+
+const LChar kTestALChars[6] = {0x41, 0x95, 0xFF, 0x50, 0x01, 0};
+const UChar kTestAUChars[6] = {0x41, 0x95, 0xFF, 0x50, 0x01, 0};
+const UChar kTestBUChars[6] = {0x41, 0x95, 0xFFFF, 0x1080, 0x01, 0};
+
+const unsigned kTestAHash1 = 0xEA32B004;
+const unsigned kTestAHash2 = 0x93F0F71E;
+const unsigned kTestAHash3 = 0xCB609EB1;
+const unsigned kTestAHash4 = 0x7984A706;
+const unsigned kTestAHash5 = 0x0427561F;
+
+const unsigned kTestBHash1 = 0xEA32B004;
+const unsigned kTestBHash2 = 0x93F0F71E;
+const unsigned kTestBHash3 = 0x59EB1B2C;
+const unsigned kTestBHash4 = 0xA7BCCC0A;
+const unsigned kTestBHash5 = 0x79201649;
+
+} // anonymous namespace
+
+TEST(StringHasherTest, StringHasher) {
+ StringHasher hasher;
+
+ // The initial state of the hasher.
+ EXPECT_EQ(kEmptyStringHash, hasher.GetHash());
+ EXPECT_EQ(kEmptyStringHash & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+}
+
+TEST(StringHasherTest, StringHasher_addCharacter) {
+ StringHasher hasher;
+
+ // Hashing a single character.
+ hasher = StringHasher();
+ hasher.AddCharacter(0);
+ EXPECT_EQ(kSingleNullCharacterHash, hasher.GetHash());
+ EXPECT_EQ(kSingleNullCharacterHash & 0xFFFFFF,
+ hasher.HashWithTop8BitsMasked());
+
+ // Hashing five characters, checking the intermediate state after each is
+ // added.
+ hasher = StringHasher();
+ hasher.AddCharacter(kTestAUChars[0]);
+ EXPECT_EQ(kTestAHash1, hasher.GetHash());
+ EXPECT_EQ(kTestAHash1 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher.AddCharacter(kTestAUChars[1]);
+ EXPECT_EQ(kTestAHash2, hasher.GetHash());
+ EXPECT_EQ(kTestAHash2 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher.AddCharacter(kTestAUChars[2]);
+ EXPECT_EQ(kTestAHash3, hasher.GetHash());
+ EXPECT_EQ(kTestAHash3 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher.AddCharacter(kTestAUChars[3]);
+ EXPECT_EQ(kTestAHash4, hasher.GetHash());
+ EXPECT_EQ(kTestAHash4 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher.AddCharacter(kTestAUChars[4]);
+ EXPECT_EQ(kTestAHash5, hasher.GetHash());
+ EXPECT_EQ(kTestAHash5 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+
+ // Hashing a second set of five characters, including non-Latin-1 characters.
+ hasher = StringHasher();
+ hasher.AddCharacter(kTestBUChars[0]);
+ EXPECT_EQ(kTestBHash1, hasher.GetHash());
+ EXPECT_EQ(kTestBHash1 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher.AddCharacter(kTestBUChars[1]);
+ EXPECT_EQ(kTestBHash2, hasher.GetHash());
+ EXPECT_EQ(kTestBHash2 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher.AddCharacter(kTestBUChars[2]);
+ EXPECT_EQ(kTestBHash3, hasher.GetHash());
+ EXPECT_EQ(kTestBHash3 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher.AddCharacter(kTestBUChars[3]);
+ EXPECT_EQ(kTestBHash4, hasher.GetHash());
+ EXPECT_EQ(kTestBHash4 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher.AddCharacter(kTestBUChars[4]);
+ EXPECT_EQ(kTestBHash5, hasher.GetHash());
+ EXPECT_EQ(kTestBHash5 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+}
+
+TEST(StringHasherTest, StringHasher_addCharacters) {
+ StringHasher hasher;
+
+ // Hashing zero characters.
+ hasher = StringHasher();
+ hasher.AddCharacters(static_cast<LChar*>(nullptr), 0);
+ EXPECT_EQ(kEmptyStringHash, hasher.GetHash());
+ EXPECT_EQ(kEmptyStringHash & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.AddCharacters(kNullLChars, 0);
+ EXPECT_EQ(kEmptyStringHash, hasher.GetHash());
+ EXPECT_EQ(kEmptyStringHash & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.AddCharacters(static_cast<UChar*>(nullptr), 0);
+ EXPECT_EQ(kEmptyStringHash, hasher.GetHash());
+ EXPECT_EQ(kEmptyStringHash & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.AddCharacters(kNullUChars, 0);
+ EXPECT_EQ(kEmptyStringHash, hasher.GetHash());
+ EXPECT_EQ(kEmptyStringHash & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+
+ // Hashing one character.
+ hasher = StringHasher();
+ hasher.AddCharacters(kNullLChars, 1);
+ EXPECT_EQ(kSingleNullCharacterHash, hasher.GetHash());
+ EXPECT_EQ(kSingleNullCharacterHash & 0xFFFFFF,
+ hasher.HashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.AddCharacters(kNullUChars, 1);
+ EXPECT_EQ(kSingleNullCharacterHash, hasher.GetHash());
+ EXPECT_EQ(kSingleNullCharacterHash & 0xFFFFFF,
+ hasher.HashWithTop8BitsMasked());
+
+ // Hashing five characters, all at once.
+ hasher = StringHasher();
+ hasher.AddCharacters(kTestALChars, 5);
+ EXPECT_EQ(kTestAHash5, hasher.GetHash());
+ EXPECT_EQ(kTestAHash5 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.AddCharacters(kTestAUChars, 5);
+ EXPECT_EQ(kTestAHash5, hasher.GetHash());
+ EXPECT_EQ(kTestAHash5 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.AddCharacters(kTestBUChars, 5);
+ EXPECT_EQ(kTestBHash5, hasher.GetHash());
+ EXPECT_EQ(kTestBHash5 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+
+ // Hashing five characters, in groups of two, then the last one.
+ hasher = StringHasher();
+ hasher.AddCharacters(kTestALChars, 2);
+ EXPECT_EQ(kTestAHash2, hasher.GetHash());
+ EXPECT_EQ(kTestAHash2 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher.AddCharacters(kTestALChars + 2, 2);
+ EXPECT_EQ(kTestAHash4, hasher.GetHash());
+ EXPECT_EQ(kTestAHash4 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher.AddCharacters(kTestALChars + 4, 1);
+ EXPECT_EQ(kTestAHash5, hasher.GetHash());
+ EXPECT_EQ(kTestAHash5 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.AddCharacters(kTestALChars, 2);
+ hasher.AddCharacters(kTestALChars + 2, 2);
+ hasher.AddCharacters(kTestALChars + 4, 1);
+ EXPECT_EQ(kTestAHash5, hasher.GetHash());
+ EXPECT_EQ(kTestAHash5 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.AddCharacters(kTestAUChars, 2);
+ EXPECT_EQ(kTestAHash2, hasher.GetHash());
+ EXPECT_EQ(kTestAHash2 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher.AddCharacters(kTestAUChars + 2, 2);
+ EXPECT_EQ(kTestAHash4, hasher.GetHash());
+ EXPECT_EQ(kTestAHash4 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher.AddCharacters(kTestAUChars + 4, 1);
+ EXPECT_EQ(kTestAHash5, hasher.GetHash());
+ EXPECT_EQ(kTestAHash5 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.AddCharacters(kTestAUChars, 2);
+ hasher.AddCharacters(kTestAUChars + 2, 2);
+ hasher.AddCharacters(kTestAUChars + 4, 1);
+ EXPECT_EQ(kTestAHash5, hasher.GetHash());
+ EXPECT_EQ(kTestAHash5 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.AddCharacters(kTestBUChars, 2);
+ EXPECT_EQ(kTestBHash2, hasher.GetHash());
+ EXPECT_EQ(kTestBHash2 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher.AddCharacters(kTestBUChars + 2, 2);
+ EXPECT_EQ(kTestBHash4, hasher.GetHash());
+ EXPECT_EQ(kTestBHash4 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher.AddCharacters(kTestBUChars + 4, 1);
+ EXPECT_EQ(kTestBHash5, hasher.GetHash());
+ EXPECT_EQ(kTestBHash5 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.AddCharacters(kTestBUChars, 2);
+ hasher.AddCharacters(kTestBUChars + 2, 2);
+ hasher.AddCharacters(kTestBUChars + 4, 1);
+ EXPECT_EQ(kTestBHash5, hasher.GetHash());
+ EXPECT_EQ(kTestBHash5 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+
+ // Hashing five characters, the first three, then the last two.
+ hasher = StringHasher();
+ hasher.AddCharacters(kTestALChars, 3);
+ EXPECT_EQ(kTestAHash3, hasher.GetHash());
+ EXPECT_EQ(kTestAHash3 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher.AddCharacters(kTestALChars + 3, 2);
+ EXPECT_EQ(kTestAHash5, hasher.GetHash());
+ EXPECT_EQ(kTestAHash5 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.AddCharacters(kTestALChars, 3);
+ EXPECT_EQ(kTestAHash3, hasher.GetHash());
+ EXPECT_EQ(kTestAHash3 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher.AddCharacters(kTestALChars + 3, 2);
+ EXPECT_EQ(kTestAHash5, hasher.GetHash());
+ EXPECT_EQ(kTestAHash5 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.AddCharacters(kTestAUChars, 3);
+ EXPECT_EQ(kTestAHash3, hasher.GetHash());
+ EXPECT_EQ(kTestAHash3 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher.AddCharacters(kTestAUChars + 3, 2);
+ EXPECT_EQ(kTestAHash5, hasher.GetHash());
+ EXPECT_EQ(kTestAHash5 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.AddCharacters(kTestAUChars, 3);
+ EXPECT_EQ(kTestAHash3, hasher.GetHash());
+ EXPECT_EQ(kTestAHash3 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher.AddCharacters(kTestAUChars + 3, 2);
+ EXPECT_EQ(kTestAHash5, hasher.GetHash());
+ EXPECT_EQ(kTestAHash5 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.AddCharacters(kTestBUChars, 3);
+ EXPECT_EQ(kTestBHash3, hasher.GetHash());
+ EXPECT_EQ(kTestBHash3 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher.AddCharacters(kTestBUChars + 3, 2);
+ EXPECT_EQ(kTestBHash5, hasher.GetHash());
+ EXPECT_EQ(kTestBHash5 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.AddCharacters(kTestBUChars, 3);
+ hasher.AddCharacters(kTestBUChars + 3, 2);
+ EXPECT_EQ(kTestBHash5, hasher.GetHash());
+ EXPECT_EQ(kTestBHash5 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+}
+
+TEST(StringHasherTest, StringHasher_addCharactersAssumingAligned) {
+ StringHasher hasher;
+
+ // Hashing zero characters.
+ hasher = StringHasher();
+ hasher.AddCharactersAssumingAligned(static_cast<LChar*>(nullptr), 0);
+ EXPECT_EQ(kEmptyStringHash, hasher.GetHash());
+ EXPECT_EQ(kEmptyStringHash & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.AddCharactersAssumingAligned(kNullLChars, 0);
+ EXPECT_EQ(kEmptyStringHash, hasher.GetHash());
+ EXPECT_EQ(kEmptyStringHash & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.AddCharactersAssumingAligned(static_cast<UChar*>(nullptr), 0);
+ EXPECT_EQ(kEmptyStringHash, hasher.GetHash());
+ EXPECT_EQ(kEmptyStringHash & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.AddCharactersAssumingAligned(kNullUChars, 0);
+ EXPECT_EQ(kEmptyStringHash, hasher.GetHash());
+ EXPECT_EQ(kEmptyStringHash & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+
+ // Hashing one character.
+ hasher = StringHasher();
+ hasher.AddCharactersAssumingAligned(kNullLChars, 1);
+ EXPECT_EQ(kSingleNullCharacterHash, hasher.GetHash());
+ EXPECT_EQ(kSingleNullCharacterHash & 0xFFFFFF,
+ hasher.HashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.AddCharactersAssumingAligned(kNullUChars, 1);
+ EXPECT_EQ(kSingleNullCharacterHash, hasher.GetHash());
+ EXPECT_EQ(kSingleNullCharacterHash & 0xFFFFFF,
+ hasher.HashWithTop8BitsMasked());
+
+ // Hashing five characters, all at once.
+ hasher = StringHasher();
+ hasher.AddCharactersAssumingAligned(kTestALChars, 5);
+ EXPECT_EQ(kTestAHash5, hasher.GetHash());
+ EXPECT_EQ(kTestAHash5 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.AddCharactersAssumingAligned(kTestAUChars, 5);
+ EXPECT_EQ(kTestAHash5, hasher.GetHash());
+ EXPECT_EQ(kTestAHash5 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.AddCharactersAssumingAligned(kTestBUChars, 5);
+ EXPECT_EQ(kTestBHash5, hasher.GetHash());
+ EXPECT_EQ(kTestBHash5 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+
+ // Hashing five characters, in groups of two, then the last one.
+ hasher = StringHasher();
+ hasher.AddCharactersAssumingAligned(kTestALChars, 2);
+ EXPECT_EQ(kTestAHash2, hasher.GetHash());
+ EXPECT_EQ(kTestAHash2 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher.AddCharactersAssumingAligned(kTestALChars + 2, 2);
+ EXPECT_EQ(kTestAHash4, hasher.GetHash());
+ EXPECT_EQ(kTestAHash4 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher.AddCharactersAssumingAligned(kTestALChars + 4, 1);
+ EXPECT_EQ(kTestAHash5, hasher.GetHash());
+ EXPECT_EQ(kTestAHash5 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.AddCharactersAssumingAligned(kTestALChars, 2);
+ hasher.AddCharactersAssumingAligned(kTestALChars + 2, 2);
+ hasher.AddCharactersAssumingAligned(kTestALChars + 4, 1);
+ EXPECT_EQ(kTestAHash5, hasher.GetHash());
+ EXPECT_EQ(kTestAHash5 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.AddCharactersAssumingAligned(kTestAUChars, 2);
+ EXPECT_EQ(kTestAHash2, hasher.GetHash());
+ EXPECT_EQ(kTestAHash2 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher.AddCharactersAssumingAligned(kTestAUChars + 2, 2);
+ EXPECT_EQ(kTestAHash4, hasher.GetHash());
+ EXPECT_EQ(kTestAHash4 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher.AddCharactersAssumingAligned(kTestAUChars + 4, 1);
+ EXPECT_EQ(kTestAHash5, hasher.GetHash());
+ EXPECT_EQ(kTestAHash5 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.AddCharactersAssumingAligned(kTestAUChars, 2);
+ hasher.AddCharactersAssumingAligned(kTestAUChars + 2, 2);
+ hasher.AddCharactersAssumingAligned(kTestAUChars + 4, 1);
+ EXPECT_EQ(kTestAHash5, hasher.GetHash());
+ EXPECT_EQ(kTestAHash5 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.AddCharactersAssumingAligned(kTestBUChars, 2);
+ EXPECT_EQ(kTestBHash2, hasher.GetHash());
+ EXPECT_EQ(kTestBHash2 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher.AddCharactersAssumingAligned(kTestBUChars + 2, 2);
+ EXPECT_EQ(kTestBHash4, hasher.GetHash());
+ EXPECT_EQ(kTestBHash4 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher.AddCharactersAssumingAligned(kTestBUChars + 4, 1);
+ EXPECT_EQ(kTestBHash5, hasher.GetHash());
+ EXPECT_EQ(kTestBHash5 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher = StringHasher();
+ hasher.AddCharactersAssumingAligned(kTestBUChars, 2);
+ hasher.AddCharactersAssumingAligned(kTestBUChars + 2, 2);
+ hasher.AddCharactersAssumingAligned(kTestBUChars + 4, 1);
+ EXPECT_EQ(kTestBHash5, hasher.GetHash());
+ EXPECT_EQ(kTestBHash5 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+
+ // Hashing five characters, first two characters one at a time,
+ // then two more, then the last one.
+ hasher = StringHasher();
+ hasher.AddCharacter(kTestBUChars[0]);
+ EXPECT_EQ(kTestBHash1, hasher.GetHash());
+ EXPECT_EQ(kTestBHash1 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher.AddCharacter(kTestBUChars[1]);
+ EXPECT_EQ(kTestBHash2, hasher.GetHash());
+ EXPECT_EQ(kTestBHash2 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher.AddCharactersAssumingAligned(kTestBUChars[2], kTestBUChars[3]);
+ EXPECT_EQ(kTestBHash4, hasher.GetHash());
+ EXPECT_EQ(kTestBHash4 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+ hasher.AddCharactersAssumingAligned(kTestBUChars + 4, 1);
+ EXPECT_EQ(kTestBHash5, hasher.GetHash());
+ EXPECT_EQ(kTestBHash5 & 0xFFFFFF, hasher.HashWithTop8BitsMasked());
+}
+
+TEST(StringHasherTest, StringHasher_computeHash) {
+ EXPECT_EQ(kEmptyStringHash,
+ StringHasher::ComputeHash(static_cast<LChar*>(nullptr), 0));
+ EXPECT_EQ(kEmptyStringHash, StringHasher::ComputeHash(kNullLChars, 0));
+ EXPECT_EQ(kEmptyStringHash,
+ StringHasher::ComputeHash(static_cast<UChar*>(nullptr), 0));
+ EXPECT_EQ(kEmptyStringHash, StringHasher::ComputeHash(kNullUChars, 0));
+
+ EXPECT_EQ(kSingleNullCharacterHash,
+ StringHasher::ComputeHash(kNullLChars, 1));
+ EXPECT_EQ(kSingleNullCharacterHash,
+ StringHasher::ComputeHash(kNullUChars, 1));
+
+ EXPECT_EQ(kTestAHash5, StringHasher::ComputeHash(kTestALChars, 5));
+ EXPECT_EQ(kTestAHash5, StringHasher::ComputeHash(kTestAUChars, 5));
+ EXPECT_EQ(kTestBHash5, StringHasher::ComputeHash(kTestBUChars, 5));
+}
+
+TEST(StringHasherTest, StringHasher_computeHashAndMaskTop8Bits) {
+ EXPECT_EQ(kEmptyStringHash & 0xFFFFFF,
+ StringHasher::ComputeHashAndMaskTop8Bits(
+ static_cast<LChar*>(nullptr), 0));
+ EXPECT_EQ(kEmptyStringHash & 0xFFFFFF,
+ StringHasher::ComputeHashAndMaskTop8Bits(kNullLChars, 0));
+ EXPECT_EQ(kEmptyStringHash & 0xFFFFFF,
+ StringHasher::ComputeHashAndMaskTop8Bits(
+ static_cast<UChar*>(nullptr), 0));
+ EXPECT_EQ(kEmptyStringHash & 0xFFFFFF,
+ StringHasher::ComputeHashAndMaskTop8Bits(kNullUChars, 0));
+
+ EXPECT_EQ(kSingleNullCharacterHash & 0xFFFFFF,
+ StringHasher::ComputeHashAndMaskTop8Bits(kNullLChars, 1));
+ EXPECT_EQ(kSingleNullCharacterHash & 0xFFFFFF,
+ StringHasher::ComputeHashAndMaskTop8Bits(kNullUChars, 1));
+
+ EXPECT_EQ(kTestAHash5 & 0xFFFFFF,
+ StringHasher::ComputeHashAndMaskTop8Bits(kTestALChars, 5));
+ EXPECT_EQ(kTestAHash5 & 0xFFFFFF,
+ StringHasher::ComputeHashAndMaskTop8Bits(kTestAUChars, 5));
+ EXPECT_EQ(kTestBHash5 & 0xFFFFFF,
+ StringHasher::ComputeHashAndMaskTop8Bits(kTestBUChars, 5));
+}
+
+TEST(StringHasherTest, StringHasher_hashMemory) {
+ EXPECT_EQ(kEmptyStringHash & 0xFFFFFF, StringHasher::HashMemory(nullptr, 0));
+ EXPECT_EQ(kEmptyStringHash & 0xFFFFFF,
+ StringHasher::HashMemory(kNullUChars, 0));
+ EXPECT_EQ(kEmptyStringHash & 0xFFFFFF, StringHasher::HashMemory<0>(nullptr));
+ EXPECT_EQ(kEmptyStringHash & 0xFFFFFF,
+ StringHasher::HashMemory<0>(kNullUChars));
+
+ EXPECT_EQ(kSingleNullCharacterHash & 0xFFFFFF,
+ StringHasher::HashMemory(kNullUChars, 2));
+ EXPECT_EQ(kSingleNullCharacterHash & 0xFFFFFF,
+ StringHasher::HashMemory<2>(kNullUChars));
+
+ EXPECT_EQ(kTestAHash5 & 0xFFFFFF, StringHasher::HashMemory(kTestAUChars, 10));
+ EXPECT_EQ(kTestAHash5 & 0xFFFFFF, StringHasher::HashMemory<10>(kTestAUChars));
+ EXPECT_EQ(kTestBHash5 & 0xFFFFFF, StringHasher::HashMemory(kTestBUChars, 10));
+ EXPECT_EQ(kTestBHash5 & 0xFFFFFF, StringHasher::HashMemory<10>(kTestBUChars));
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/terminated_array.h b/chromium/third_party/blink/renderer/platform/wtf/terminated_array.h
new file mode 100644
index 00000000000..772b9f16f7e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/terminated_array.h
@@ -0,0 +1,130 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TERMINATED_ARRAY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TERMINATED_ARRAY_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
+#include "third_party/blink/renderer/platform/wtf/vector_traits.h"
+
+namespace WTF {
+
+// TerminatedArray<T> represents a sequence of elements of type T in which each
+// element knows whether it is the last element in the sequence or not. For this
+// check type T must provide isLastInArray method.
+// TerminatedArray<T> can only be constructed by TerminatedArrayBuilder<T>.
+template <typename T>
+class TerminatedArray {
+ DISALLOW_NEW();
+
+ public:
+ // When TerminatedArray::Allocator implementations grow the backing
+ // store, old is copied into the new and larger block.
+ static_assert(VectorTraits<T>::kCanMoveWithMemcpy,
+ "Array elements must be memory copyable");
+
+ T& at(size_t index) { return reinterpret_cast<T*>(this)[index]; }
+ const T& at(size_t index) const {
+ return reinterpret_cast<const T*>(this)[index];
+ }
+
+ template <typename U>
+ class iterator_base final {
+ STACK_ALLOCATED();
+
+ public:
+ iterator_base& operator++() {
+ if (val_->IsLastInArray()) {
+ val_ = nullptr;
+ } else {
+ ++val_;
+ }
+ return *this;
+ }
+
+ U& operator*() const { return *val_; }
+
+ bool operator==(const iterator_base& other) const {
+ return val_ == other.val_;
+ }
+ bool operator!=(const iterator_base& other) const {
+ return !(*this == other);
+ }
+
+ private:
+ iterator_base(U* val) : val_(val) {}
+
+ U* val_;
+
+ friend class TerminatedArray;
+ };
+
+ typedef iterator_base<T> iterator;
+ typedef iterator_base<const T> const_iterator;
+
+ iterator begin() { return iterator(reinterpret_cast<T*>(this)); }
+ const_iterator begin() const {
+ return const_iterator(reinterpret_cast<const T*>(this));
+ }
+
+ iterator end() { return iterator(nullptr); }
+ const_iterator end() const { return const_iterator(nullptr); }
+
+ size_t size() const {
+ size_t count = 0;
+ for (const_iterator it = begin(); it != end(); ++it)
+ count++;
+ return count;
+ }
+
+ // Match Allocator semantics to be able to use
+ // std::unique_ptr<TerminatedArray>.
+ void operator delete(void* p) { ::WTF::Partitions::FastFree(p); }
+
+ private:
+ // Allocator describes how TerminatedArrayBuilder should create new instances
+ // of TerminateArray and manage their lifetimes.
+ struct Allocator {
+ STATIC_ONLY(Allocator);
+ using BackendAllocator = PartitionAllocator;
+ using PassPtr = std::unique_ptr<TerminatedArray>;
+ using Ptr = std::unique_ptr<TerminatedArray>;
+
+ static PassPtr Release(Ptr& ptr) { return ptr.release(); }
+
+ static PassPtr Create(size_t capacity) {
+ return base::WrapUnique(
+ static_cast<TerminatedArray*>(WTF::Partitions::FastMalloc(
+ WTF::Partitions::ComputeAllocationSize(capacity, sizeof(T)),
+ WTF_HEAP_PROFILER_TYPE_NAME(T))));
+ }
+
+ static PassPtr Resize(Ptr ptr, size_t capacity) {
+ return base::WrapUnique(
+ static_cast<TerminatedArray*>(WTF::Partitions::FastRealloc(
+ ptr.release(),
+ WTF::Partitions::ComputeAllocationSize(capacity, sizeof(T)),
+ WTF_HEAP_PROFILER_TYPE_NAME(T))));
+ }
+ };
+
+ // Prohibit construction. Allocator makes TerminatedArray instances for
+ // TerminatedArrayBuilder by pointer casting.
+ TerminatedArray() = delete;
+
+ template <typename, template <typename> class>
+ friend class TerminatedArrayBuilder;
+
+ DISALLOW_COPY_AND_ASSIGN(TerminatedArray);
+};
+
+} // namespace WTF
+
+using WTF::TerminatedArray;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TERMINATED_ARRAY_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/terminated_array_builder.h b/chromium/third_party/blink/renderer/platform/wtf/terminated_array_builder.h
new file mode 100644
index 00000000000..9beb7a3745b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/terminated_array_builder.h
@@ -0,0 +1,84 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TERMINATED_ARRAY_BUILDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TERMINATED_ARRAY_BUILDER_H_
+
+#include "base/macros.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/construct_traits.h"
+
+namespace WTF {
+
+template <typename T, template <typename> class ArrayType = TerminatedArray>
+class TerminatedArrayBuilder {
+ STACK_ALLOCATED();
+
+ public:
+ explicit TerminatedArrayBuilder(
+ typename ArrayType<T>::Allocator::PassPtr array)
+ : array_(array), count_(0), capacity_(0) {
+ if (!array_)
+ return;
+ capacity_ = count_ = array_->size();
+ DCHECK(array_->at(count_ - 1).IsLastInArray());
+ }
+
+ void Grow(size_t count) {
+ DCHECK(count);
+ if (!array_) {
+ DCHECK(!count_);
+ DCHECK(!capacity_);
+ capacity_ = count;
+ array_ = ArrayType<T>::Allocator::Create(capacity_);
+ } else {
+ DCHECK(array_->at(count_ - 1).IsLastInArray());
+ capacity_ += count;
+ array_ = ArrayType<T>::Allocator::Resize(
+ ArrayType<T>::Allocator::Release(array_), capacity_);
+ array_->at(count_ - 1).SetLastInArray(false);
+ }
+ array_->at(capacity_ - 1).SetLastInArray(true);
+ }
+
+ void Append(const T& item) {
+ CHECK_LT(count_, capacity_);
+ DCHECK(!item.IsLastInArray());
+ array_->at(count_++) = item;
+ ConstructTraits<T, VectorTraits<T>,
+ typename ArrayType<T>::Allocator::BackendAllocator>::
+ NotifyNewElements(&array_->at(count_ - 1), 1);
+ if (count_ == capacity_)
+ array_->at(capacity_ - 1).SetLastInArray(true);
+ }
+
+ typename ArrayType<T>::Allocator::PassPtr Release() {
+ CHECK_EQ(count_, capacity_);
+ AssertValid();
+ return ArrayType<T>::Allocator::Release(array_);
+ }
+
+ private:
+#if DCHECK_IS_ON()
+ void AssertValid() {
+ for (size_t i = 0; i < count_; ++i) {
+ bool is_last_in_array = (i + 1 == count_);
+ DCHECK_EQ(array_->at(i).IsLastInArray(), is_last_in_array);
+ }
+ }
+#else
+ void AssertValid() {}
+#endif
+
+ typename ArrayType<T>::Allocator::Ptr array_;
+ size_t count_;
+ size_t capacity_;
+
+ DISALLOW_COPY_AND_ASSIGN(TerminatedArrayBuilder);
+};
+
+} // namespace WTF
+
+using WTF::TerminatedArrayBuilder;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TERMINATED_ARRAY_BUILDER_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/testing/run_all_tests.cc b/chromium/third_party/blink/renderer/platform/wtf/testing/run_all_tests.cc
new file mode 100644
index 00000000000..6d8ddcbc850
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/testing/run_all_tests.cc
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2013 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 "base/test/test_suite.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
+#include "third_party/blink/renderer/platform/wtf/wtf.h"
+
+int main(int argc, char** argv) {
+ WTF::Partitions::Initialize(nullptr);
+ WTF::Initialize(nullptr);
+ return base::RunUnitTestsUsingBaseTestSuite(argc, argv);
+}
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/OWNERS b/chromium/third_party/blink/renderer/platform/wtf/text/OWNERS
new file mode 100644
index 00000000000..fbe28ba841c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/OWNERS
@@ -0,0 +1,8 @@
+per-file character_names.h=drott@chromium.org
+per-file text_codec*.*=jsbell@chromium.org
+per-file text_codec*.*=jshin@chromium.org
+per-file text_encoding*.*=jshin@chromium.org
+per-file text_encoding*.*=jshin@chromium.org
+
+# TEAM: platform-architecture-dev@chromium.org
+# COMPONENT: Blink>Internals>WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/ascii_fast_path.h b/chromium/third_party/blink/renderer/platform/wtf/text/ascii_fast_path.h
new file mode 100644
index 00000000000..84b044c0a9f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/ascii_fast_path.h
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2011, 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_ASCII_FAST_PATH_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_ASCII_FAST_PATH_H_
+
+#include <stdint.h>
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/wtf/alignment.h"
+#include "third_party/blink/renderer/platform/wtf/cpu.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
+
+#if defined(OS_MACOSX) && defined(ARCH_CPU_X86_FAMILY)
+#include <emmintrin.h>
+#endif
+
+namespace WTF {
+
+// Assuming that a pointer is the size of a "machine word", then
+// uintptr_t is an integer type that is also a machine word.
+typedef uintptr_t MachineWord;
+const uintptr_t kMachineWordAlignmentMask = sizeof(MachineWord) - 1;
+
+inline bool IsAlignedToMachineWord(const void* pointer) {
+ return !(reinterpret_cast<uintptr_t>(pointer) & kMachineWordAlignmentMask);
+}
+
+template <typename T>
+inline T* AlignToMachineWord(T* pointer) {
+ return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(pointer) &
+ ~kMachineWordAlignmentMask);
+}
+
+template <size_t size, typename CharacterType>
+struct NonASCIIMask;
+template <>
+struct NonASCIIMask<4, UChar> {
+ static inline uint32_t Value() { return 0xFF80FF80U; }
+};
+template <>
+struct NonASCIIMask<4, LChar> {
+ static inline uint32_t Value() { return 0x80808080U; }
+};
+template <>
+struct NonASCIIMask<8, UChar> {
+ static inline uint64_t Value() { return 0xFF80FF80FF80FF80ULL; }
+};
+template <>
+struct NonASCIIMask<8, LChar> {
+ static inline uint64_t Value() { return 0x8080808080808080ULL; }
+};
+
+template <typename CharacterType>
+inline bool IsAllASCII(MachineWord word) {
+ return !(word & NonASCIIMask<sizeof(MachineWord), CharacterType>::Value());
+}
+
+// Note: This function assume the input is likely all ASCII, and
+// does not leave early if it is not the case.
+template <typename CharacterType>
+inline bool CharactersAreAllASCII(const CharacterType* characters,
+ size_t length) {
+ DCHECK_GT(length, 0u);
+ MachineWord all_char_bits = 0;
+ const CharacterType* end = characters + length;
+
+ // Prologue: align the input.
+ while (!IsAlignedToMachineWord(characters) && characters != end) {
+ all_char_bits |= *characters;
+ ++characters;
+ }
+
+ // Compare the values of CPU word size.
+ const CharacterType* word_end = AlignToMachineWord(end);
+ const size_t kLoopIncrement = sizeof(MachineWord) / sizeof(CharacterType);
+ while (characters < word_end) {
+ all_char_bits |= *(reinterpret_cast_ptr<const MachineWord*>(characters));
+ characters += kLoopIncrement;
+ }
+
+ // Process the remaining bytes.
+ while (characters != end) {
+ all_char_bits |= *characters;
+ ++characters;
+ }
+
+ MachineWord non_ascii_bit_mask =
+ NonASCIIMask<sizeof(MachineWord), CharacterType>::Value();
+ return !(all_char_bits & non_ascii_bit_mask);
+}
+
+inline void CopyLCharsFromUCharSource(LChar* destination,
+ const UChar* source,
+ size_t length) {
+#if defined(OS_MACOSX) && defined(ARCH_CPU_X86_FAMILY)
+ const uintptr_t kMemoryAccessSize =
+ 16; // Memory accesses on 16 byte (128 bit) alignment
+ const uintptr_t kMemoryAccessMask = kMemoryAccessSize - 1;
+
+ size_t i = 0;
+ for (; i < length && !IsAlignedTo<kMemoryAccessMask>(&source[i]); ++i) {
+ DCHECK(!(source[i] & 0xff00));
+ destination[i] = static_cast<LChar>(source[i]);
+ }
+
+ const uintptr_t kSourceLoadSize =
+ 32; // Process 32 bytes (16 UChars) each iteration
+ const size_t kUcharsPerLoop = kSourceLoadSize / sizeof(UChar);
+ if (length > kUcharsPerLoop) {
+ const size_t end_length = length - kUcharsPerLoop + 1;
+ for (; i < end_length; i += kUcharsPerLoop) {
+#if DCHECK_IS_ON()
+ for (unsigned check_index = 0; check_index < kUcharsPerLoop;
+ ++check_index)
+ DCHECK(!(source[i + check_index] & 0xff00));
+#endif
+ __m128i first8u_chars =
+ _mm_load_si128(reinterpret_cast<const __m128i*>(&source[i]));
+ __m128i second8u_chars =
+ _mm_load_si128(reinterpret_cast<const __m128i*>(&source[i + 8]));
+ __m128i packed_chars = _mm_packus_epi16(first8u_chars, second8u_chars);
+ _mm_storeu_si128(reinterpret_cast<__m128i*>(&destination[i]),
+ packed_chars);
+ }
+ }
+
+ for (; i < length; ++i) {
+ DCHECK(!(source[i] & 0xff00));
+ destination[i] = static_cast<LChar>(source[i]);
+ }
+#elif defined(COMPILER_GCC) && WTF_CPU_ARM_NEON && \
+ !defined(ARCH_CPU_BIG_ENDIAN) && defined(NDEBUG)
+ const LChar* const end = destination + length;
+ const uintptr_t kMemoryAccessSize = 8;
+
+ if (length >= (2 * kMemoryAccessSize) - 1) {
+ // Prefix: align dst on 64 bits.
+ const uintptr_t kMemoryAccessMask = kMemoryAccessSize - 1;
+ while (!IsAlignedTo<kMemoryAccessMask>(destination))
+ *destination++ = static_cast<LChar>(*source++);
+
+ // Vector interleaved unpack, we only store the lower 8 bits.
+ const uintptr_t length_left = end - destination;
+ const LChar* const simd_end = end - (length_left % kMemoryAccessSize);
+ do {
+ asm("vld2.8 { d0-d1 }, [%[SOURCE]] !\n\t"
+ "vst1.8 { d0 }, [%[DESTINATION],:64] !\n\t"
+ : [SOURCE] "+r"(source), [DESTINATION] "+r"(destination)
+ :
+ : "memory", "d0", "d1");
+ } while (destination != simd_end);
+ }
+
+ while (destination != end)
+ *destination++ = static_cast<LChar>(*source++);
+#else
+ for (size_t i = 0; i < length; ++i) {
+ DCHECK(!(source[i] & 0xff00));
+ destination[i] = static_cast<LChar>(source[i]);
+ }
+#endif
+}
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_ASCII_FAST_PATH_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/atomic_string.cc b/chromium/third_party/blink/renderer/platform/wtf/text/atomic_string.cc
new file mode 100644
index 00000000000..75ceaf12c2c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/atomic_string.cc
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2013 Apple Inc. All rights
+ * reserved.
+ * Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com>
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+
+#include "third_party/blink/renderer/platform/wtf/dtoa.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string_table.h"
+#include "third_party/blink/renderer/platform/wtf/text/integer_to_string_conversion.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_impl.h"
+
+namespace WTF {
+
+static_assert(sizeof(AtomicString) == sizeof(String),
+ "AtomicString and String must be same size");
+
+AtomicString::AtomicString(const LChar* chars, unsigned length)
+ : string_(AtomicStringTable::Instance().Add(chars, length)) {}
+
+AtomicString::AtomicString(const UChar* chars, unsigned length)
+ : string_(AtomicStringTable::Instance().Add(chars, length)) {}
+
+AtomicString::AtomicString(const UChar* chars)
+ : string_(AtomicStringTable::Instance().Add(
+ chars,
+ chars ? LengthOfNullTerminatedString(chars) : 0)) {}
+
+scoped_refptr<StringImpl> AtomicString::AddSlowCase(StringImpl* string) {
+ DCHECK(!string->IsAtomic());
+ return AtomicStringTable::Instance().Add(string);
+}
+
+AtomicString AtomicString::FromUTF8(const char* chars, size_t length) {
+ if (!chars)
+ return g_null_atom;
+ if (!length)
+ return g_empty_atom;
+ return AtomicString(
+ AtomicStringTable::Instance().AddUTF8(chars, chars + length));
+}
+
+AtomicString AtomicString::FromUTF8(const char* chars) {
+ if (!chars)
+ return g_null_atom;
+ if (!*chars)
+ return g_empty_atom;
+ return AtomicString(AtomicStringTable::Instance().AddUTF8(chars, nullptr));
+}
+
+AtomicString AtomicString::DeprecatedLower() const {
+ // Note: This is a hot function in the Dromaeo benchmark.
+ StringImpl* impl = this->Impl();
+ if (UNLIKELY(!impl))
+ return *this;
+ scoped_refptr<StringImpl> new_impl = impl->LowerUnicode();
+ if (LIKELY(new_impl == impl))
+ return *this;
+ return AtomicString(String(std::move(new_impl)));
+}
+
+AtomicString AtomicString::LowerASCII() const {
+ StringImpl* impl = this->Impl();
+ if (UNLIKELY(!impl))
+ return *this;
+ scoped_refptr<StringImpl> new_impl = impl->LowerASCII();
+ if (LIKELY(new_impl == impl))
+ return *this;
+ return AtomicString(String(std::move(new_impl)));
+}
+
+AtomicString AtomicString::UpperASCII() const {
+ StringImpl* impl = this->Impl();
+ if (UNLIKELY(!impl))
+ return *this;
+ return AtomicString(impl->UpperASCII());
+}
+
+template <typename IntegerType>
+static AtomicString IntegerToAtomicString(IntegerType input) {
+ IntegerToStringConverter<IntegerType> converter(input);
+ return AtomicString(converter.Characters8(), converter.length());
+}
+
+AtomicString AtomicString::Number(int number) {
+ return IntegerToAtomicString(number);
+}
+
+AtomicString AtomicString::Number(unsigned number) {
+ return IntegerToAtomicString(number);
+}
+
+AtomicString AtomicString::Number(long number) {
+ return IntegerToAtomicString(number);
+}
+
+AtomicString AtomicString::Number(unsigned long number) {
+ return IntegerToAtomicString(number);
+}
+
+AtomicString AtomicString::Number(long long number) {
+ return IntegerToAtomicString(number);
+}
+
+AtomicString AtomicString::Number(unsigned long long number) {
+ return IntegerToAtomicString(number);
+}
+
+AtomicString AtomicString::Number(double number, unsigned precision) {
+ NumberToStringBuffer buffer;
+ return AtomicString(NumberToFixedPrecisionString(number, precision, buffer));
+}
+
+std::ostream& operator<<(std::ostream& out, const AtomicString& s) {
+ return out << s.GetString();
+}
+
+#ifndef NDEBUG
+void AtomicString::Show() const {
+ string_.Show();
+}
+#endif
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/atomic_string.h b/chromium/third_party/blink/renderer/platform/wtf/text/atomic_string.h
new file mode 100644
index 00000000000..837d1a82a20
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/atomic_string.h
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2008 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_ATOMIC_STRING_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_ATOMIC_STRING_H_
+
+#include <cstring>
+#include <iosfwd>
+
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/hash_table_deleted_value_type.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_view.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_export.h"
+
+namespace WTF {
+
+struct AtomicStringHash;
+
+// An AtomicString instance represents a string, and multiple AtomicString
+// instances can share their string storage if the strings are
+// identical. Comparing two AtomicString instances is much faster than comparing
+// two String instances because we just check string storage identity.
+//
+// AtomicString instances are not thread-safe. An AtomicString instance created
+// in a thread must be used only in the creator thread. If multiple threads
+// access a single AtomicString instance, we have race condition of a reference
+// count in StringImpl, and would hit a runtime CHECK in
+// AtomicStringTable::remove().
+//
+// Exception: g_null_atom and g_empty_atom, are shared in multiple threads, and
+// are never stored in AtomicStringTable.
+class WTF_EXPORT AtomicString {
+ USING_FAST_MALLOC(AtomicString);
+
+ public:
+ // The function is defined in StringStatics.cpp.
+ static void Init();
+
+ AtomicString() = default;
+ AtomicString(const LChar* chars)
+ : AtomicString(chars,
+ chars ? strlen(reinterpret_cast<const char*>(chars)) : 0) {
+ }
+ AtomicString(const char* chars)
+ : AtomicString(reinterpret_cast<const LChar*>(chars)) {}
+ AtomicString(const LChar* chars, unsigned length);
+ AtomicString(const UChar* chars, unsigned length);
+ AtomicString(const UChar* chars);
+ AtomicString(const char16_t* chars)
+ : AtomicString(reinterpret_cast<const UChar*>(chars)) {}
+
+ template <size_t inlineCapacity>
+ explicit AtomicString(const Vector<UChar, inlineCapacity>& vector)
+ : AtomicString(vector.data(), vector.size()) {}
+
+ // Constructing an AtomicString from a String / StringImpl can be expensive if
+ // the StringImpl is not already atomic.
+ explicit AtomicString(StringImpl* impl) : string_(Add(impl)) {}
+ explicit AtomicString(const String& s) : string_(Add(s.Impl())) {}
+
+ explicit operator bool() const { return !IsNull(); }
+ operator const String&() const { return string_; }
+ const String& GetString() const { return string_; }
+
+ StringImpl* Impl() const { return string_.Impl(); }
+
+ bool Is8Bit() const { return string_.Is8Bit(); }
+ const LChar* Characters8() const { return string_.Characters8(); }
+ const UChar* Characters16() const { return string_.Characters16(); }
+ unsigned length() const { return string_.length(); }
+
+ UChar operator[](unsigned i) const { return string_[i]; }
+
+ // Find characters.
+ size_t find(UChar c, unsigned start = 0) const {
+ return string_.find(c, start);
+ }
+ size_t find(LChar c, unsigned start = 0) const {
+ return string_.find(c, start);
+ }
+ size_t find(char c, unsigned start = 0) const {
+ return find(static_cast<LChar>(c), start);
+ }
+ size_t Find(CharacterMatchFunctionPtr match_function,
+ unsigned start = 0) const {
+ return string_.Find(match_function, start);
+ }
+
+ // Find substrings.
+ size_t Find(const StringView& value,
+ unsigned start = 0,
+ TextCaseSensitivity case_sensitivity = kTextCaseSensitive) const {
+ return string_.Find(value, start, case_sensitivity);
+ }
+
+ // Unicode aware case insensitive string matching. Non-ASCII characters might
+ // match to ASCII characters. This function is rarely used to implement web
+ // platform features.
+ size_t FindIgnoringCase(const StringView& value, unsigned start = 0) const {
+ return string_.FindIgnoringCase(value, start);
+ }
+
+ // ASCII case insensitive string matching.
+ size_t FindIgnoringASCIICase(const StringView& value,
+ unsigned start = 0) const {
+ return string_.FindIgnoringASCIICase(value, start);
+ }
+
+ bool Contains(char c) const { return find(c) != kNotFound; }
+ bool Contains(
+ const StringView& value,
+ TextCaseSensitivity case_sensitivity = kTextCaseSensitive) const {
+ return Find(value, 0, case_sensitivity) != kNotFound;
+ }
+
+ // Find the last instance of a single character or string.
+ size_t ReverseFind(UChar c, unsigned start = UINT_MAX) const {
+ return string_.ReverseFind(c, start);
+ }
+ size_t ReverseFind(const StringView& value, unsigned start = UINT_MAX) const {
+ return string_.ReverseFind(value, start);
+ }
+
+ bool StartsWith(
+ const StringView& prefix,
+ TextCaseSensitivity case_sensitivity = kTextCaseSensitive) const {
+ return string_.StartsWith(prefix, case_sensitivity);
+ }
+ bool StartsWithIgnoringCase(const StringView& prefix) const {
+ return string_.StartsWithIgnoringCase(prefix);
+ }
+ bool StartsWithIgnoringASCIICase(const StringView& prefix) const {
+ return string_.StartsWithIgnoringASCIICase(prefix);
+ }
+ bool StartsWith(UChar character) const {
+ return string_.StartsWith(character);
+ }
+
+ bool EndsWith(
+ const StringView& suffix,
+ TextCaseSensitivity case_sensitivity = kTextCaseSensitive) const {
+ return string_.EndsWith(suffix, case_sensitivity);
+ }
+ bool EndsWith(UChar character) const { return string_.EndsWith(character); }
+
+ // Returns a lowercase version of the string. This function might
+ // convert non-ASCII characters to ASCII characters. For example,
+ // DeprecatedLower() for U+212A is 'k'.
+ // This function is rarely used to implement web platform features.
+ // See crbug.com/627682.
+ // This function is deprecated. We should use LowerASCII(), or introduce
+ // LowerUnicode().
+ AtomicString DeprecatedLower() const;
+
+ // Returns a lowercase/uppercase version of the string.
+ // These functions convert ASCII characters only.
+ AtomicString LowerASCII() const;
+ AtomicString UpperASCII() const;
+
+ // See comments in WTFString.h.
+ int ToInt(bool* ok = nullptr) const { return string_.ToInt(ok); }
+ double ToDouble(bool* ok = nullptr) const { return string_.ToDouble(ok); }
+ float ToFloat(bool* ok = nullptr) const { return string_.ToFloat(ok); }
+
+ static AtomicString Number(int);
+ static AtomicString Number(unsigned);
+ static AtomicString Number(long);
+ static AtomicString Number(unsigned long);
+ static AtomicString Number(long long);
+ static AtomicString Number(unsigned long long);
+
+ static AtomicString Number(double, unsigned precision = 6);
+
+ bool IsNull() const { return string_.IsNull(); }
+ bool IsEmpty() const { return string_.IsEmpty(); }
+
+#ifdef __OBJC__
+ AtomicString(NSString* s) : string_(Add((CFStringRef)s)) {}
+ operator NSString*() const { return string_; }
+#endif
+ // AtomicString::fromUTF8 will return a null string if
+ // the input data contains invalid UTF-8 sequences.
+ // NOTE: Passing a zero size means use the whole string.
+ static AtomicString FromUTF8(const char*, size_t length);
+ static AtomicString FromUTF8(const char*);
+
+ CString Ascii() const { return string_.Ascii(); }
+ CString Latin1() const { return string_.Latin1(); }
+ CString Utf8(UTF8ConversionMode mode = kLenientUTF8Conversion) const {
+ return string_.Utf8(mode);
+ }
+
+ size_t CharactersSizeInBytes() const {
+ return string_.CharactersSizeInBytes();
+ }
+
+ bool IsSafeToSendToAnotherThread() const {
+ return string_.IsSafeToSendToAnotherThread();
+ }
+
+#ifndef NDEBUG
+ void Show() const;
+#endif
+
+ private:
+ friend struct HashTraits<AtomicString>;
+
+ String string_;
+
+ ALWAYS_INLINE static scoped_refptr<StringImpl> Add(StringImpl* r) {
+ if (!r || r->IsAtomic())
+ return r;
+ return AddSlowCase(r);
+ }
+ static scoped_refptr<StringImpl> AddSlowCase(StringImpl*);
+#if defined(OS_MACOSX)
+ static scoped_refptr<StringImpl> Add(CFStringRef);
+#endif
+};
+
+inline bool operator==(const AtomicString& a, const AtomicString& b) {
+ return a.Impl() == b.Impl();
+}
+inline bool operator==(const AtomicString& a, const String& b) {
+ // We don't use equalStringView so we get the isAtomic() optimization inside
+ // WTF::equal.
+ return Equal(a.Impl(), b.Impl());
+}
+inline bool operator==(const String& a, const AtomicString& b) {
+ return b == a;
+}
+inline bool operator==(const AtomicString& a, const char* b) {
+ return EqualStringView(a, b);
+}
+inline bool operator==(const char* a, const AtomicString& b) {
+ return b == a;
+}
+
+inline bool operator!=(const AtomicString& a, const AtomicString& b) {
+ return a.Impl() != b.Impl();
+}
+inline bool operator!=(const AtomicString& a, const String& b) {
+ return !(a == b);
+}
+inline bool operator!=(const String& a, const AtomicString& b) {
+ return !(a == b);
+}
+inline bool operator!=(const AtomicString& a, const char* b) {
+ return !(a == b);
+}
+inline bool operator!=(const char* a, const AtomicString& b) {
+ return !(a == b);
+}
+
+// Define external global variables for the commonly used atomic strings.
+// These are only usable from the main thread.
+WTF_EXPORT extern const AtomicString& g_null_atom;
+WTF_EXPORT extern const AtomicString& g_empty_atom;
+WTF_EXPORT extern const AtomicString& g_star_atom;
+WTF_EXPORT extern const AtomicString& g_xml_atom;
+WTF_EXPORT extern const AtomicString& g_xmlns_atom;
+WTF_EXPORT extern const AtomicString& g_xlink_atom;
+WTF_EXPORT extern const AtomicString& g_http_atom;
+WTF_EXPORT extern const AtomicString& g_https_atom;
+
+// AtomicStringHash is the default hash for AtomicString
+template <typename T>
+struct DefaultHash;
+template <>
+struct DefaultHash<AtomicString> {
+ typedef AtomicStringHash Hash;
+};
+
+// Pretty printer for gtest and base/logging.*. It prepends and appends
+// double-quotes, and escapes chracters other than ASCII printables.
+WTF_EXPORT std::ostream& operator<<(std::ostream&, const AtomicString&);
+
+inline StringView::StringView(const AtomicString& string,
+ unsigned offset,
+ unsigned length)
+ : StringView(string.Impl(), offset, length) {}
+inline StringView::StringView(const AtomicString& string, unsigned offset)
+ : StringView(string.Impl(), offset) {}
+inline StringView::StringView(const AtomicString& string)
+ : StringView(string.Impl()) {}
+
+} // namespace WTF
+
+WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(AtomicString);
+
+using WTF::AtomicString;
+using WTF::g_null_atom;
+using WTF::g_empty_atom;
+using WTF::g_star_atom;
+using WTF::g_xml_atom;
+using WTF::g_xmlns_atom;
+using WTF::g_xlink_atom;
+
+#include "third_party/blink/renderer/platform/wtf/text/string_concatenate.h"
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_ATOMIC_STRING_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/atomic_string_cf.cc b/chromium/third_party/blink/renderer/platform/wtf/text/atomic_string_cf.cc
new file mode 100644
index 00000000000..9a6442d12a9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/atomic_string_cf.cc
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2012 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string_table.h"
+
+#if defined(OS_MACOSX)
+
+#include <CoreFoundation/CoreFoundation.h>
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+
+namespace WTF {
+
+scoped_refptr<StringImpl> AtomicString::Add(CFStringRef string) {
+ if (!string)
+ return nullptr;
+
+ CFIndex length = CFStringGetLength(string);
+
+ if (const LChar* ptr = reinterpret_cast<const LChar*>(
+ CFStringGetCStringPtr(string, kCFStringEncodingISOLatin1)))
+ return AtomicStringTable::Instance().Add(ptr, length);
+
+ if (const UniChar* ptr = CFStringGetCharactersPtr(string))
+ return AtomicStringTable::Instance().Add(
+ reinterpret_cast<const UChar*>(ptr), length);
+
+ Vector<UniChar, 1024> uchar_buffer(length);
+ CFStringGetCharacters(string, CFRangeMake(0, length), uchar_buffer.data());
+ return AtomicStringTable::Instance().Add(
+ reinterpret_cast<const UChar*>(uchar_buffer.data()), length);
+}
+
+} // namespace WTF
+
+#endif // defined(OS_MACOSX)
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h b/chromium/third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h
new file mode 100644
index 00000000000..6c96808a5d9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2008 Apple 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_ATOMIC_STRING_HASH_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_ATOMIC_STRING_HASH_H_
+
+#include "third_party/blink/renderer/platform/wtf/hash_traits.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+
+namespace WTF {
+
+struct AtomicStringHash {
+ static unsigned GetHash(const AtomicString& key) {
+ return key.Impl()->ExistingHash();
+ }
+
+ static bool Equal(const AtomicString& a, const AtomicString& b) {
+ return a == b;
+ }
+
+ static const bool safe_to_compare_to_empty_or_deleted = false;
+};
+
+// AtomicStringHash is the default hash for AtomicString
+template <>
+struct HashTraits<AtomicString> : SimpleClassHashTraits<AtomicString> {
+ // Unlike other types, we can return a const reference for AtomicString's
+ // empty value (g_null_atom).
+ typedef const AtomicString& PeekOutType;
+
+ static const AtomicString& EmptyValue() { return g_null_atom; }
+ static PeekOutType Peek(const AtomicString& value) { return value; }
+
+ static const bool kHasIsEmptyValueFunction = true;
+ static bool IsEmptyValue(const AtomicString& value) { return value.IsNull(); }
+
+ static bool IsDeletedValue(const AtomicString& value) {
+ return HashTraits<String>::IsDeletedValue(value.string_);
+ }
+
+ static void ConstructDeletedValue(AtomicString& slot, bool zero_value) {
+ HashTraits<String>::ConstructDeletedValue(slot.string_, zero_value);
+ }
+};
+
+} // namespace WTF
+
+using WTF::AtomicStringHash;
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/atomic_string_table.cc b/chromium/third_party/blink/renderer/platform/wtf/text/atomic_string_table.cc
new file mode 100644
index 00000000000..f2cba80a33f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/atomic_string_table.cc
@@ -0,0 +1,228 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string_table.h"
+
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/text/utf8.h"
+
+namespace WTF {
+
+AtomicStringTable::AtomicStringTable() {
+ for (StringImpl* string : StringImpl::AllStaticStrings().Values())
+ Add(string);
+}
+
+AtomicStringTable::~AtomicStringTable() {
+ for (StringImpl* string : table_) {
+ if (!string->IsStatic()) {
+ DCHECK(string->IsAtomic());
+ string->SetIsAtomic(false);
+ }
+ }
+}
+
+void AtomicStringTable::ReserveCapacity(unsigned size) {
+ table_.ReserveCapacityForSize(size);
+}
+
+template <typename T, typename HashTranslator>
+scoped_refptr<StringImpl> AtomicStringTable::AddToStringTable(const T& value) {
+ HashSet<StringImpl*>::AddResult add_result =
+ table_.AddWithTranslator<HashTranslator>(value);
+
+ // If the string is newly-translated, then we need to adopt it.
+ // The boolean in the pair tells us if that is so.
+ return add_result.is_new_entry ? base::AdoptRef(*add_result.stored_value)
+ : *add_result.stored_value;
+}
+
+template <typename CharacterType>
+struct HashTranslatorCharBuffer {
+ const CharacterType* s;
+ unsigned length;
+};
+
+typedef HashTranslatorCharBuffer<UChar> UCharBuffer;
+struct UCharBufferTranslator {
+ static unsigned GetHash(const UCharBuffer& buf) {
+ return StringHasher::ComputeHashAndMaskTop8Bits(buf.s, buf.length);
+ }
+
+ static bool Equal(StringImpl* const& str, const UCharBuffer& buf) {
+ return WTF::Equal(str, buf.s, buf.length);
+ }
+
+ static void Translate(StringImpl*& location,
+ const UCharBuffer& buf,
+ unsigned hash) {
+ auto string = StringImpl::Create8BitIfPossible(buf.s, buf.length);
+ if (string)
+ string->AddRef();
+ location = string.get();
+ location->SetHash(hash);
+ location->SetIsAtomic(true);
+ }
+};
+
+struct HashAndUTF8Characters {
+ unsigned hash;
+ const char* characters;
+ unsigned length;
+ unsigned utf16_length;
+};
+
+struct HashAndUTF8CharactersTranslator {
+ static unsigned GetHash(const HashAndUTF8Characters& buffer) {
+ return buffer.hash;
+ }
+
+ static bool Equal(StringImpl* const& string,
+ const HashAndUTF8Characters& buffer) {
+ if (buffer.utf16_length != string->length())
+ return false;
+
+ // If buffer contains only ASCII characters UTF-8 and UTF16 length are the
+ // same.
+ if (buffer.utf16_length != buffer.length) {
+ if (string->Is8Bit()) {
+ const LChar* characters8 = string->Characters8();
+ return Unicode::EqualLatin1WithUTF8(
+ characters8, characters8 + string->length(), buffer.characters,
+ buffer.characters + buffer.length);
+ }
+ const UChar* characters16 = string->Characters16();
+ return Unicode::EqualUTF16WithUTF8(
+ characters16, characters16 + string->length(), buffer.characters,
+ buffer.characters + buffer.length);
+ }
+
+ if (string->Is8Bit()) {
+ const LChar* string_characters = string->Characters8();
+
+ for (unsigned i = 0; i < buffer.length; ++i) {
+ DCHECK(IsASCII(buffer.characters[i]));
+ if (string_characters[i] != buffer.characters[i])
+ return false;
+ }
+
+ return true;
+ }
+
+ const UChar* string_characters = string->Characters16();
+
+ for (unsigned i = 0; i < buffer.length; ++i) {
+ DCHECK(IsASCII(buffer.characters[i]));
+ if (string_characters[i] != buffer.characters[i])
+ return false;
+ }
+
+ return true;
+ }
+
+ static void Translate(StringImpl*& location,
+ const HashAndUTF8Characters& buffer,
+ unsigned hash) {
+ UChar* target;
+ scoped_refptr<StringImpl> new_string =
+ StringImpl::CreateUninitialized(buffer.utf16_length, target);
+
+ bool is_all_ascii;
+ const char* source = buffer.characters;
+ if (Unicode::ConvertUTF8ToUTF16(&source, source + buffer.length, &target,
+ target + buffer.utf16_length,
+ &is_all_ascii) != Unicode::kConversionOK)
+ NOTREACHED();
+
+ if (is_all_ascii)
+ new_string = StringImpl::Create(buffer.characters, buffer.length);
+
+ new_string->AddRef();
+ location = new_string.get();
+ location->SetHash(hash);
+ location->SetIsAtomic(true);
+ }
+};
+
+scoped_refptr<StringImpl> AtomicStringTable::Add(const UChar* s,
+ unsigned length) {
+ if (!s)
+ return nullptr;
+
+ if (!length)
+ return StringImpl::empty_;
+
+ UCharBuffer buffer = {s, length};
+ return AddToStringTable<UCharBuffer, UCharBufferTranslator>(buffer);
+}
+
+typedef HashTranslatorCharBuffer<LChar> LCharBuffer;
+struct LCharBufferTranslator {
+ static unsigned GetHash(const LCharBuffer& buf) {
+ return StringHasher::ComputeHashAndMaskTop8Bits(buf.s, buf.length);
+ }
+
+ static bool Equal(StringImpl* const& str, const LCharBuffer& buf) {
+ return WTF::Equal(str, buf.s, buf.length);
+ }
+
+ static void Translate(StringImpl*& location,
+ const LCharBuffer& buf,
+ unsigned hash) {
+ auto string = StringImpl::Create(buf.s, buf.length);
+ string->AddRef();
+ location = string.get();
+ location->SetHash(hash);
+ location->SetIsAtomic(true);
+ }
+};
+
+scoped_refptr<StringImpl> AtomicStringTable::Add(const LChar* s,
+ unsigned length) {
+ if (!s)
+ return nullptr;
+
+ if (!length)
+ return StringImpl::empty_;
+
+ LCharBuffer buffer = {s, length};
+ return AddToStringTable<LCharBuffer, LCharBufferTranslator>(buffer);
+}
+
+StringImpl* AtomicStringTable::Add(StringImpl* string) {
+ if (!string->length())
+ return StringImpl::empty_;
+
+ StringImpl* result = *table_.insert(string).stored_value;
+
+ if (!result->IsAtomic())
+ result->SetIsAtomic(true);
+
+ DCHECK(!string->IsStatic() || result->IsStatic());
+ return result;
+}
+
+scoped_refptr<StringImpl> AtomicStringTable::AddUTF8(
+ const char* characters_start,
+ const char* characters_end) {
+ HashAndUTF8Characters buffer;
+ buffer.characters = characters_start;
+ buffer.hash = Unicode::CalculateStringHashAndLengthFromUTF8MaskingTop8Bits(
+ characters_start, characters_end, buffer.length, buffer.utf16_length);
+
+ if (!buffer.hash)
+ return nullptr;
+
+ return AddToStringTable<HashAndUTF8Characters,
+ HashAndUTF8CharactersTranslator>(buffer);
+}
+
+void AtomicStringTable::Remove(StringImpl* string) {
+ DCHECK(string->IsAtomic());
+ auto iterator = table_.find(string);
+ CHECK_NE(iterator, table_.end());
+ table_.erase(iterator);
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/atomic_string_table.h b/chromium/third_party/blink/renderer/platform/wtf/text/atomic_string_table.h
new file mode 100644
index 00000000000..c6420832336
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/atomic_string_table.h
@@ -0,0 +1,66 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_ATOMIC_STRING_TABLE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_ATOMIC_STRING_TABLE_H_
+
+#include "base/macros.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_impl.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_export.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_thread_data.h"
+
+namespace WTF {
+
+// The underlying storage that keeps the map of unique AtomicStrings. This is
+// not thread safe and each WTFThreadData has one.
+class WTF_EXPORT AtomicStringTable final {
+ USING_FAST_MALLOC(AtomicStringTable);
+
+ public:
+ AtomicStringTable();
+ ~AtomicStringTable();
+
+ // Gets the shared table for the current thread.
+ static AtomicStringTable& Instance() {
+ return WtfThreadData().GetAtomicStringTable();
+ }
+
+ // Used by system initialization to preallocate enough storage for all of
+ // the static strings.
+ void ReserveCapacity(unsigned size);
+
+ // Inserting strings into the table. Note that the return value from adding
+ // a UChar string may be an LChar string as the table will attempt to
+ // convert the string to save memory if possible.
+ StringImpl* Add(StringImpl*);
+ scoped_refptr<StringImpl> Add(const LChar* chars, unsigned length);
+ scoped_refptr<StringImpl> Add(const UChar* chars, unsigned length);
+
+ // Adding UTF8.
+ // Returns null if the characters contain invalid utf8 sequences.
+ // Pass null for the charactersEnd to automatically detect the length.
+ scoped_refptr<StringImpl> AddUTF8(const char* characters_start,
+ const char* characters_end);
+
+ // This is for ~StringImpl to unregister a string before destruction since
+ // the table is holding weak pointers. It should not be used directly.
+ void Remove(StringImpl*);
+
+ private:
+ template <typename T, typename HashTranslator>
+ inline scoped_refptr<StringImpl> AddToStringTable(const T& value);
+
+ HashSet<StringImpl*> table_;
+
+ DISALLOW_COPY_AND_ASSIGN(AtomicStringTable);
+};
+
+} // namespace WTF
+
+using WTF::AtomicStringTable;
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/atomic_string_test.cc b/chromium/third_party/blink/renderer/platform/wtf/text/atomic_string_test.cc
new file mode 100644
index 00000000000..9b0a02494aa
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/atomic_string_test.cc
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2013 Samsung Electronics. 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 "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace WTF {
+
+TEST(AtomicStringTest, Number) {
+ int int_value = 1234;
+ EXPECT_EQ("1234", AtomicString::Number(int_value));
+ int_value = -1234;
+ EXPECT_EQ("-1234", AtomicString::Number(int_value));
+ unsigned unsigned_value = 1234u;
+ EXPECT_EQ("1234", AtomicString::Number(unsigned_value));
+ long long_value = 6553500;
+ EXPECT_EQ("6553500", AtomicString::Number(long_value));
+ long_value = -6553500;
+ EXPECT_EQ("-6553500", AtomicString::Number(long_value));
+ unsigned long unsigned_long_value = 4294967295u;
+ EXPECT_EQ("4294967295", AtomicString::Number(unsigned_long_value));
+ long long longlong_value = 9223372036854775807;
+ EXPECT_EQ("9223372036854775807", AtomicString::Number(longlong_value));
+ longlong_value = -9223372036854775807;
+ EXPECT_EQ("-9223372036854775807", AtomicString::Number(longlong_value));
+ unsigned long long unsigned_long_long_value = 18446744073709551615u;
+ EXPECT_EQ("18446744073709551615",
+ AtomicString::Number(unsigned_long_long_value));
+ double double_value = 1234.56;
+ EXPECT_EQ("1234.56", AtomicString::Number(double_value));
+ double_value = 1234.56789;
+ EXPECT_EQ("1234.56789", AtomicString::Number(double_value, 9));
+}
+
+TEST(AtomicStringTest, ImplEquality) {
+ AtomicString foo("foo");
+ AtomicString bar("bar");
+ AtomicString baz("baz");
+ AtomicString foo2("foo");
+ AtomicString baz2("baz");
+ AtomicString bar2("bar");
+ EXPECT_EQ(foo.Impl(), foo2.Impl());
+ EXPECT_EQ(bar.Impl(), bar2.Impl());
+ EXPECT_EQ(baz.Impl(), baz2.Impl());
+ EXPECT_NE(foo.Impl(), bar.Impl());
+ EXPECT_NE(foo.Impl(), baz.Impl());
+ EXPECT_NE(bar.Impl(), baz.Impl());
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/base64.cc b/chromium/third_party/blink/renderer/platform/wtf/text/base64.cc
new file mode 100644
index 00000000000..c9d584ffa00
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/base64.cc
@@ -0,0 +1,272 @@
+/*
+ Copyright (C) 2000-2001 Dawit Alemayehu <adawit@kde.org>
+ Copyright (C) 2006 Alexey Proskuryakov <ap@webkit.org>
+ Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
+ Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License (LGPL)
+ version 2 as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ USA.
+
+ This code is based on the java implementation in HTTPClient
+ package by Ronald Tschalaer Copyright (C) 1996-1999.
+*/
+
+#include "third_party/blink/renderer/platform/wtf/text/base64.h"
+
+#include <limits.h>
+
+namespace WTF {
+
+static const char kBase64EncMap[64] = {
+ 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B,
+ 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56,
+ 0x57, 0x58, 0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72,
+ 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x30, 0x31, 0x32,
+ 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2B, 0x2F};
+
+static const char kBase64DecMap[128] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3F,
+ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12,
+ 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24,
+ 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30,
+ 0x31, 0x32, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+String Base64Encode(const char* data,
+ unsigned length,
+ Base64EncodePolicy policy) {
+ Vector<char> result;
+ Base64Encode(data, length, result, policy);
+ return String(result.data(), result.size());
+}
+
+void Base64Encode(const char* data,
+ unsigned len,
+ Vector<char>& out,
+ Base64EncodePolicy policy) {
+ out.clear();
+ if (!len)
+ return;
+
+ // If the input string is pathologically large, just return nothing.
+ // Note: Keep this in sync with the "outLength" computation below.
+ // Rather than being perfectly precise, this is a bit conservative.
+ const unsigned kMaxInputBufferSize = UINT_MAX / 77 * 76 / 4 * 3 - 2;
+ if (len > kMaxInputBufferSize)
+ return;
+
+ unsigned sidx = 0;
+ unsigned didx = 0;
+
+ unsigned out_length = ((len + 2) / 3) * 4;
+
+ // Deal with the 76 character per line limit specified in RFC 2045.
+ bool insert_l_fs = (policy == kBase64InsertLFs && out_length > 76);
+ if (insert_l_fs)
+ out_length += ((out_length - 1) / 76);
+
+ int count = 0;
+ out.Grow(out_length);
+
+ // 3-byte to 4-byte conversion + 0-63 to ascii printable conversion
+ if (len > 1) {
+ while (sidx < len - 2) {
+ if (insert_l_fs) {
+ if (count && !(count % 76))
+ out[didx++] = '\n';
+ count += 4;
+ }
+ out[didx++] = kBase64EncMap[(data[sidx] >> 2) & 077];
+ out[didx++] = kBase64EncMap[((data[sidx + 1] >> 4) & 017) |
+ ((data[sidx] << 4) & 077)];
+ out[didx++] = kBase64EncMap[((data[sidx + 2] >> 6) & 003) |
+ ((data[sidx + 1] << 2) & 077)];
+ out[didx++] = kBase64EncMap[data[sidx + 2] & 077];
+ sidx += 3;
+ }
+ }
+
+ if (sidx < len) {
+ if (insert_l_fs && (count > 0) && !(count % 76))
+ out[didx++] = '\n';
+
+ out[didx++] = kBase64EncMap[(data[sidx] >> 2) & 077];
+ if (sidx < len - 1) {
+ out[didx++] = kBase64EncMap[((data[sidx + 1] >> 4) & 017) |
+ ((data[sidx] << 4) & 077)];
+ out[didx++] = kBase64EncMap[(data[sidx + 1] << 2) & 077];
+ } else {
+ out[didx++] = kBase64EncMap[(data[sidx] << 4) & 077];
+ }
+ }
+
+ // Add padding
+ while (didx < out.size()) {
+ out[didx] = '=';
+ ++didx;
+ }
+}
+
+bool Base64Decode(const Vector<char>& in,
+ Vector<char>& out,
+ CharacterMatchFunctionPtr should_ignore_character,
+ Base64DecodePolicy policy) {
+ out.clear();
+
+ // If the input string is pathologically large, just return nothing.
+ if (in.size() > UINT_MAX)
+ return false;
+
+ return Base64Decode(in.data(), in.size(), out, should_ignore_character,
+ policy);
+}
+
+template <typename T>
+static inline bool Base64DecodeInternal(
+ const T* data,
+ unsigned length,
+ Vector<char>& out,
+ CharacterMatchFunctionPtr should_ignore_character,
+ Base64DecodePolicy policy) {
+ out.clear();
+ if (!length)
+ return true;
+
+ out.Grow(length);
+
+ unsigned equals_sign_count = 0;
+ unsigned out_length = 0;
+ bool had_error = false;
+ for (unsigned idx = 0; idx < length; ++idx) {
+ UChar ch = data[idx];
+ if (ch == '=') {
+ ++equals_sign_count;
+ // There should never be more than 2 padding characters.
+ if (policy == kBase64ValidatePadding && equals_sign_count > 2) {
+ had_error = true;
+ break;
+ }
+ } else if (('0' <= ch && ch <= '9') || ('A' <= ch && ch <= 'Z') ||
+ ('a' <= ch && ch <= 'z') || ch == '+' || ch == '/') {
+ if (equals_sign_count) {
+ had_error = true;
+ break;
+ }
+ out[out_length++] = kBase64DecMap[ch];
+ } else if (!should_ignore_character || !should_ignore_character(ch)) {
+ had_error = true;
+ break;
+ }
+ }
+
+ if (out_length < out.size())
+ out.Shrink(out_length);
+
+ if (had_error)
+ return false;
+
+ if (!out_length)
+ return !equals_sign_count;
+
+ // There should be no padding if length is a multiple of 4.
+ // We use (outLength + equalsSignCount) instead of length because we don't
+ // want to account for ignored characters.
+ if (policy == kBase64ValidatePadding && equals_sign_count &&
+ (out_length + equals_sign_count) % 4)
+ return false;
+
+ // Valid data is (n * 4 + [0,2,3]) characters long.
+ if ((out_length % 4) == 1)
+ return false;
+
+ // 4-byte to 3-byte conversion
+ out_length -= (out_length + 3) / 4;
+ if (!out_length)
+ return false;
+
+ unsigned sidx = 0;
+ unsigned didx = 0;
+ if (out_length > 1) {
+ while (didx < out_length - 2) {
+ out[didx] = (((out[sidx] << 2) & 255) | ((out[sidx + 1] >> 4) & 003));
+ out[didx + 1] =
+ (((out[sidx + 1] << 4) & 255) | ((out[sidx + 2] >> 2) & 017));
+ out[didx + 2] = (((out[sidx + 2] << 6) & 255) | (out[sidx + 3] & 077));
+ sidx += 4;
+ didx += 3;
+ }
+ }
+
+ if (didx < out_length)
+ out[didx] = (((out[sidx] << 2) & 255) | ((out[sidx + 1] >> 4) & 003));
+
+ if (++didx < out_length)
+ out[didx] = (((out[sidx + 1] << 4) & 255) | ((out[sidx + 2] >> 2) & 017));
+
+ if (out_length < out.size())
+ out.Shrink(out_length);
+
+ return true;
+}
+
+bool Base64Decode(const char* data,
+ unsigned length,
+ Vector<char>& out,
+ CharacterMatchFunctionPtr should_ignore_character,
+ Base64DecodePolicy policy) {
+ return Base64DecodeInternal<LChar>(reinterpret_cast<const LChar*>(data),
+ length, out, should_ignore_character,
+ policy);
+}
+
+bool Base64Decode(const UChar* data,
+ unsigned length,
+ Vector<char>& out,
+ CharacterMatchFunctionPtr should_ignore_character,
+ Base64DecodePolicy policy) {
+ return Base64DecodeInternal<UChar>(data, length, out, should_ignore_character,
+ policy);
+}
+
+bool Base64Decode(const String& in,
+ Vector<char>& out,
+ CharacterMatchFunctionPtr should_ignore_character,
+ Base64DecodePolicy policy) {
+ if (in.IsEmpty())
+ return Base64DecodeInternal<LChar>(nullptr, 0, out, should_ignore_character,
+ policy);
+ if (in.Is8Bit())
+ return Base64DecodeInternal<LChar>(in.Characters8(), in.length(), out,
+ should_ignore_character, policy);
+ return Base64DecodeInternal<UChar>(in.Characters16(), in.length(), out,
+ should_ignore_character, policy);
+}
+
+String Base64URLEncode(const char* data,
+ unsigned length,
+ Base64EncodePolicy policy) {
+ return Base64Encode(data, length, policy).Replace('+', '-').Replace('/', '_');
+}
+
+String NormalizeToBase64(const String& encoding) {
+ return String(encoding).Replace('-', '+').Replace('_', '/');
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/base64.h b/chromium/third_party/blink/renderer/platform/wtf/text/base64.h
new file mode 100644
index 00000000000..1d3c2cf9e3c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/base64.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2006 Alexey Proskuryakov <ap@webkit.org>
+ * Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com>
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_BASE64_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_BASE64_H_
+
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_export.h"
+
+namespace WTF {
+
+enum Base64EncodePolicy { kBase64DoNotInsertLFs, kBase64InsertLFs };
+
+enum Base64DecodePolicy { kBase64DoNotValidatePadding, kBase64ValidatePadding };
+
+WTF_EXPORT void Base64Encode(const char*,
+ unsigned,
+ Vector<char>&,
+ Base64EncodePolicy = kBase64DoNotInsertLFs);
+WTF_EXPORT void Base64Encode(const Vector<char>&,
+ Vector<char>&,
+ Base64EncodePolicy = kBase64DoNotInsertLFs);
+WTF_EXPORT void Base64Encode(const CString&,
+ Vector<char>&,
+ Base64EncodePolicy = kBase64DoNotInsertLFs);
+WTF_EXPORT String Base64Encode(const char*,
+ unsigned,
+ Base64EncodePolicy = kBase64DoNotInsertLFs);
+WTF_EXPORT String Base64Encode(const Vector<char>&,
+ Base64EncodePolicy = kBase64DoNotInsertLFs);
+WTF_EXPORT String Base64Encode(const Vector<unsigned char>&,
+ Base64EncodePolicy = kBase64DoNotInsertLFs);
+WTF_EXPORT String Base64Encode(const CString&,
+ Base64EncodePolicy = kBase64DoNotInsertLFs);
+
+WTF_EXPORT bool Base64Decode(
+ const String&,
+ Vector<char>&,
+ CharacterMatchFunctionPtr should_ignore_character = nullptr,
+ Base64DecodePolicy = kBase64DoNotValidatePadding);
+WTF_EXPORT bool Base64Decode(
+ const Vector<char>&,
+ Vector<char>&,
+ CharacterMatchFunctionPtr should_ignore_character = nullptr,
+ Base64DecodePolicy = kBase64DoNotValidatePadding);
+WTF_EXPORT bool Base64Decode(
+ const char*,
+ unsigned,
+ Vector<char>&,
+ CharacterMatchFunctionPtr should_ignore_character = nullptr,
+ Base64DecodePolicy = kBase64DoNotValidatePadding);
+WTF_EXPORT bool Base64Decode(
+ const UChar*,
+ unsigned,
+ Vector<char>&,
+ CharacterMatchFunctionPtr should_ignore_character = nullptr,
+ Base64DecodePolicy = kBase64DoNotValidatePadding);
+
+// Given an encoding in either base64 or base64url, returns a normalized
+// encoding in plain base64.
+WTF_EXPORT String NormalizeToBase64(const String&);
+
+WTF_EXPORT String Base64URLEncode(const char*,
+ unsigned,
+ Base64EncodePolicy = kBase64DoNotInsertLFs);
+
+inline void Base64Encode(const Vector<char>& in,
+ Vector<char>& out,
+ Base64EncodePolicy policy) {
+ Base64Encode(in.data(), in.size(), out, policy);
+}
+
+inline void Base64Encode(const CString& in,
+ Vector<char>& out,
+ Base64EncodePolicy policy) {
+ Base64Encode(in.data(), in.length(), out, policy);
+}
+
+inline String Base64Encode(const Vector<char>& in, Base64EncodePolicy policy) {
+ return Base64Encode(in.data(), in.size(), policy);
+}
+
+inline String Base64Encode(const Vector<unsigned char>& in,
+ Base64EncodePolicy policy) {
+ return Base64Encode(reinterpret_cast<const char*>(in.data()), in.size(),
+ policy);
+}
+
+inline String Base64Encode(const CString& in, Base64EncodePolicy policy) {
+ return Base64Encode(in.data(), in.length(), policy);
+}
+
+} // namespace WTF
+
+using WTF::Base64EncodePolicy;
+using WTF::kBase64DoNotInsertLFs;
+using WTF::kBase64InsertLFs;
+using WTF::Base64DecodePolicy;
+using WTF::kBase64DoNotValidatePadding;
+using WTF::kBase64ValidatePadding;
+using WTF::Base64Encode;
+using WTF::Base64Decode;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_BASE64_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/character_names.h b/chromium/third_party/blink/renderer/platform/wtf/text/character_names.h
new file mode 100644
index 00000000000..93fab69b977
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/character_names.h
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2007, 2009, 2010 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_CHARACTER_NAMES_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_CHARACTER_NAMES_H_
+
+#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
+
+namespace WTF {
+namespace Unicode {
+
+// Names here are taken from the Unicode standard.
+
+// Most of these are UChar constants, not UChar32, which makes them
+// more convenient for WebCore code that mostly uses UTF-16.
+
+const UChar kActivateArabicFormShapingCharacter = 0x206D;
+const UChar kActivateSymmetricSwappingCharacter = 0x206B;
+const UChar32 kAegeanWordSeparatorLineCharacter = 0x10100;
+const UChar32 kAegeanWordSeparatorDotCharacter = 0x10101;
+const UChar kArabicLetterMarkCharacter = 0x061C;
+const UChar kBlackCircleCharacter = 0x25CF;
+const UChar kBlackSquareCharacter = 0x25A0;
+const UChar kBlackUpPointingTriangleCharacter = 0x25B2;
+const UChar kBulletCharacter = 0x2022;
+const UChar kBullseyeCharacter = 0x25CE;
+const UChar32 kCancelTag = 0xE007F;
+const UChar kCarriageReturnCharacter = 0x000D;
+const UChar kCombiningEnclosingCircleBackslashCharacter = 0x20E0;
+const UChar kCombiningEnclosingKeycapCharacter = 0x20E3;
+const UChar kDeleteCharacter = 0x007F;
+const UChar kEthiopicPrefaceColonCharacter = 0x1366;
+const UChar kEthiopicWordspaceCharacter = 0x1361;
+const UChar kHeavyBlackHeartCharacter = 0x2764;
+const UChar32 kEyeCharacter = 0x1F441;
+const UChar32 kBoyCharacter = 0x1F466;
+const UChar32 kGirlCharacter = 0x1F467;
+const UChar32 kManCharacter = 0x1F468;
+const UChar32 kWomanCharacter = 0x1F469;
+const UChar32 kKissMarkCharacter = 0x1F48B;
+const UChar32 kFamilyCharacter = 0x1F46A;
+const UChar kFemaleSignCharacter = 0x2640;
+const UChar kFirstStrongIsolateCharacter = 0x2068;
+const UChar kFisheyeCharacter = 0x25C9;
+const UChar kFullstopCharacter = 0x002E;
+const UChar kHebrewPunctuationGereshCharacter = 0x05F3;
+const UChar kHebrewPunctuationGershayimCharacter = 0x05F4;
+const UChar kHiraganaLetterSmallACharacter = 0x3041;
+const UChar kHorizontalEllipsisCharacter = 0x2026;
+const UChar kHyphenCharacter = 0x2010;
+const UChar kHyphenMinusCharacter = 0x002D;
+const UChar kIdeographicCommaCharacter = 0x3001;
+const UChar kIdeographicFullStopCharacter = 0x3002;
+#if defined(USING_SYSTEM_ICU)
+const UChar ideographicSpaceCharacter = 0x3000;
+#endif
+const UChar kInhibitArabicFormShapingCharacter = 0x206C;
+const UChar kInhibitSymmetricSwappingCharacter = 0x206A;
+const UChar kLatinCapitalLetterIWithDotAbove = 0x0130;
+const UChar kLatinSmallLetterDotlessI = 0x0131;
+const UChar kLeftDoubleQuotationMarkCharacter = 0x201C;
+const UChar kLeftSingleQuotationMarkCharacter = 0x2018;
+const UChar32 kLeftSpeechBubbleCharacter = 0x1F5E8;
+const UChar kLeftToRightEmbedCharacter = 0x202A;
+const UChar kLeftToRightIsolateCharacter = 0x2066;
+const UChar kLeftToRightMarkCharacter = 0x200E;
+const UChar kLeftToRightOverrideCharacter = 0x202D;
+const UChar kLineSeparator = 0x2028;
+const UChar kLineTabulationCharacter = 0x000B;
+const UChar kLowLineCharacter = 0x005F;
+const UChar kMaleSignCharacter = 0x2642;
+const UChar kMinusSignCharacter = 0x2212;
+const UChar kNewlineCharacter = 0x000A;
+const UChar kFormFeedCharacter = 0x000C;
+const UChar kNationalDigitShapesCharacter = 0x206E;
+const UChar kNominalDigitShapesCharacter = 0x206F;
+const UChar kNoBreakSpaceCharacter = 0x00A0;
+const UChar kObjectReplacementCharacter = 0xFFFC;
+const UChar kParagraphSeparator = 0x2029;
+const UChar kPopDirectionalFormattingCharacter = 0x202C;
+const UChar kPopDirectionalIsolateCharacter = 0x2069;
+const UChar32 kRainbowCharacter = 0x1F308;
+const UChar kReplacementCharacter = 0xFFFD;
+const UChar kReverseSolidusCharacter = 0x005C;
+const UChar kRightDoubleQuotationMarkCharacter = 0x201D;
+const UChar kRightSingleQuotationMarkCharacter = 0x2019;
+const UChar kRightToLeftEmbedCharacter = 0x202B;
+const UChar kRightToLeftIsolateCharacter = 0x2067;
+const UChar kRightToLeftMarkCharacter = 0x200F;
+const UChar kRightToLeftOverrideCharacter = 0x202E;
+const UChar kSesameDotCharacter = 0xFE45;
+const UChar kSmallLetterSharpSCharacter = 0x00DF;
+const UChar kSolidusCharacter = 0x002F;
+const UChar kSoftHyphenCharacter = 0x00AD;
+const UChar kSpaceCharacter = 0x0020;
+const UChar kStaffOfAesculapiusCharacter = 0x2695;
+const UChar kTabulationCharacter = 0x0009;
+const UChar32 kTagDigitZero = 0xE0030;
+const UChar32 kTagDigitNine = 0xE0039;
+const UChar32 kTagLatinSmallLetterA = 0xE0061;
+const UChar32 kTagLatinSmallLetterZ = 0xE007A;
+const UChar kTibetanMarkIntersyllabicTshegCharacter = 0x0F0B;
+const UChar kTibetanMarkDelimiterTshegBstarCharacter = 0x0F0C;
+const UChar32 kUgariticWordDividerCharacter = 0x1039F;
+const UChar kVariationSelector15Character = 0xFE0E;
+const UChar kVariationSelector16Character = 0xFE0F;
+const UChar32 kWavingWhiteFlagCharacter = 0x1F3F3;
+const UChar kWhiteBulletCharacter = 0x25E6;
+const UChar kWhiteCircleCharacter = 0x25CB;
+const UChar kWhiteSesameDotCharacter = 0xFE46;
+const UChar kWhiteUpPointingTriangleCharacter = 0x25B3;
+const UChar kYenSignCharacter = 0x00A5;
+const UChar kZeroWidthJoinerCharacter = 0x200D;
+const UChar kZeroWidthNonJoinerCharacter = 0x200C;
+const UChar kZeroWidthSpaceCharacter = 0x200B;
+const UChar kZeroWidthNoBreakSpaceCharacter = 0xFEFF;
+const UChar32 kMaxCodepoint = 0x10ffff;
+
+} // namespace Unicode
+} // namespace WTF
+
+using WTF::Unicode::kAegeanWordSeparatorLineCharacter;
+using WTF::Unicode::kAegeanWordSeparatorDotCharacter;
+using WTF::Unicode::kActivateArabicFormShapingCharacter;
+using WTF::Unicode::kActivateSymmetricSwappingCharacter;
+using WTF::Unicode::kArabicLetterMarkCharacter;
+using WTF::Unicode::kBlackCircleCharacter;
+using WTF::Unicode::kBlackSquareCharacter;
+using WTF::Unicode::kBlackUpPointingTriangleCharacter;
+using WTF::Unicode::kBulletCharacter;
+using WTF::Unicode::kBullseyeCharacter;
+using WTF::Unicode::kCancelTag;
+using WTF::Unicode::kCarriageReturnCharacter;
+using WTF::Unicode::kCombiningEnclosingKeycapCharacter;
+using WTF::Unicode::kCombiningEnclosingCircleBackslashCharacter;
+using WTF::Unicode::kEthiopicPrefaceColonCharacter;
+using WTF::Unicode::kEthiopicWordspaceCharacter;
+using WTF::Unicode::kEyeCharacter;
+using WTF::Unicode::kFamilyCharacter;
+using WTF::Unicode::kFemaleSignCharacter;
+using WTF::Unicode::kFirstStrongIsolateCharacter;
+using WTF::Unicode::kFisheyeCharacter;
+using WTF::Unicode::kFormFeedCharacter;
+using WTF::Unicode::kFullstopCharacter;
+using WTF::Unicode::kHebrewPunctuationGereshCharacter;
+using WTF::Unicode::kHebrewPunctuationGershayimCharacter;
+using WTF::Unicode::kHiraganaLetterSmallACharacter;
+using WTF::Unicode::kHorizontalEllipsisCharacter;
+using WTF::Unicode::kHyphenCharacter;
+using WTF::Unicode::kHyphenMinusCharacter;
+using WTF::Unicode::kIdeographicCommaCharacter;
+using WTF::Unicode::kIdeographicFullStopCharacter;
+#if defined(USING_SYSTEM_ICU)
+using WTF::Unicode::ideographicSpaceCharacter;
+#endif
+using WTF::Unicode::kInhibitArabicFormShapingCharacter;
+using WTF::Unicode::kInhibitSymmetricSwappingCharacter;
+using WTF::Unicode::kLatinCapitalLetterIWithDotAbove;
+using WTF::Unicode::kLatinSmallLetterDotlessI;
+using WTF::Unicode::kLeftDoubleQuotationMarkCharacter;
+using WTF::Unicode::kLeftSingleQuotationMarkCharacter;
+using WTF::Unicode::kLeftSpeechBubbleCharacter;
+using WTF::Unicode::kLeftToRightEmbedCharacter;
+using WTF::Unicode::kLeftToRightIsolateCharacter;
+using WTF::Unicode::kLeftToRightMarkCharacter;
+using WTF::Unicode::kLeftToRightOverrideCharacter;
+using WTF::Unicode::kLineSeparator;
+using WTF::Unicode::kLowLineCharacter;
+using WTF::Unicode::kMaleSignCharacter;
+using WTF::Unicode::kMinusSignCharacter;
+using WTF::Unicode::kNewlineCharacter;
+using WTF::Unicode::kNationalDigitShapesCharacter;
+using WTF::Unicode::kNominalDigitShapesCharacter;
+using WTF::Unicode::kNoBreakSpaceCharacter;
+using WTF::Unicode::kObjectReplacementCharacter;
+using WTF::Unicode::kParagraphSeparator;
+using WTF::Unicode::kPopDirectionalFormattingCharacter;
+using WTF::Unicode::kPopDirectionalIsolateCharacter;
+using WTF::Unicode::kRainbowCharacter;
+using WTF::Unicode::kReplacementCharacter;
+using WTF::Unicode::kReverseSolidusCharacter;
+using WTF::Unicode::kRightDoubleQuotationMarkCharacter;
+using WTF::Unicode::kRightSingleQuotationMarkCharacter;
+using WTF::Unicode::kRightToLeftEmbedCharacter;
+using WTF::Unicode::kRightToLeftIsolateCharacter;
+using WTF::Unicode::kRightToLeftMarkCharacter;
+using WTF::Unicode::kRightToLeftOverrideCharacter;
+using WTF::Unicode::kSesameDotCharacter;
+using WTF::Unicode::kSmallLetterSharpSCharacter;
+using WTF::Unicode::kSoftHyphenCharacter;
+using WTF::Unicode::kSolidusCharacter;
+using WTF::Unicode::kSpaceCharacter;
+using WTF::Unicode::kStaffOfAesculapiusCharacter;
+using WTF::Unicode::kTabulationCharacter;
+using WTF::Unicode::kTagDigitZero;
+using WTF::Unicode::kTagDigitNine;
+using WTF::Unicode::kTagLatinSmallLetterA;
+using WTF::Unicode::kTagLatinSmallLetterZ;
+using WTF::Unicode::kTibetanMarkIntersyllabicTshegCharacter;
+using WTF::Unicode::kTibetanMarkDelimiterTshegBstarCharacter;
+using WTF::Unicode::kUgariticWordDividerCharacter;
+using WTF::Unicode::kVariationSelector15Character;
+using WTF::Unicode::kVariationSelector16Character;
+using WTF::Unicode::kWavingWhiteFlagCharacter;
+using WTF::Unicode::kWhiteBulletCharacter;
+using WTF::Unicode::kWhiteCircleCharacter;
+using WTF::Unicode::kWhiteSesameDotCharacter;
+using WTF::Unicode::kWhiteUpPointingTriangleCharacter;
+using WTF::Unicode::kYenSignCharacter;
+using WTF::Unicode::kZeroWidthJoinerCharacter;
+using WTF::Unicode::kZeroWidthNonJoinerCharacter;
+using WTF::Unicode::kZeroWidthSpaceCharacter;
+using WTF::Unicode::kZeroWidthNoBreakSpaceCharacter;
+using WTF::Unicode::kMaxCodepoint;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_CHARACTER_NAMES_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/collator.h b/chromium/third_party/blink/renderer/platform/wtf/text/collator.h
new file mode 100644
index 00000000000..5b08e319d80
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/collator.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2008 Apple 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_COLLATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_COLLATOR_H_
+
+#include "base/macros.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_export.h"
+
+struct UCollator;
+
+namespace WTF {
+
+class WTF_EXPORT Collator {
+ USING_FAST_MALLOC(Collator);
+
+ public:
+ enum Result { kEqual = 0, kGreater = 1, kLess = -1 };
+
+ // From ICU's uloc.h (ULOC_FULLNAME_CAPACITY)
+ static const size_t kUlocFullnameCapacity = 157;
+
+ // Parsing is lenient; e.g. language identifiers (such as "en-US") are
+ // accepted, too.
+ explicit Collator(const char* locale);
+
+ ~Collator();
+ void SetOrderLowerFirst(bool);
+
+ static std::unique_ptr<Collator> UserDefault();
+
+ Result Collate(const ::UChar*, size_t, const ::UChar*, size_t) const;
+
+ private:
+ void CreateCollator() const;
+ void ReleaseCollator();
+ void SetEquivalentLocale(const char*, char*);
+ mutable UCollator* collator_;
+
+ char* locale_;
+ char equivalent_locale_[kUlocFullnameCapacity];
+ bool lower_first_;
+
+ DISALLOW_COPY_AND_ASSIGN(Collator);
+};
+
+} // namespace WTF
+
+using WTF::Collator;
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/cstring.cc b/chromium/third_party/blink/renderer/platform/wtf/text/cstring.cc
new file mode 100644
index 00000000000..63c4c4382cc
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/cstring.cc
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2003, 2006, 2008, 2009, 2010, 2012 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/wtf/text/cstring.h"
+
+#include <string.h>
+#include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
+#include "third_party/blink/renderer/platform/wtf/ascii_ctype.h"
+
+namespace WTF {
+
+scoped_refptr<CStringImpl> CStringImpl::CreateUninitialized(size_t length,
+ char*& data) {
+ // TODO(esprehn): This doesn't account for the NUL.
+ CHECK_LT(length,
+ (std::numeric_limits<unsigned>::max() - sizeof(CStringImpl)));
+
+ // The +1 is for the terminating NUL character.
+ size_t size = sizeof(CStringImpl) + length + 1;
+ CStringImpl* buffer = static_cast<CStringImpl*>(
+ Partitions::BufferMalloc(size, WTF_HEAP_PROFILER_TYPE_NAME(CStringImpl)));
+ data = reinterpret_cast<char*>(buffer + 1);
+ data[length] = '\0';
+ return base::AdoptRef(new (buffer) CStringImpl(length));
+}
+
+void CStringImpl::operator delete(void* ptr) {
+ Partitions::BufferFree(ptr);
+}
+
+CString::CString(const char* chars, size_t length) {
+ if (!chars) {
+ DCHECK_EQ(length, 0u);
+ return;
+ }
+ char* data;
+ buffer_ = CStringImpl::CreateUninitialized(length, data);
+ memcpy(data, chars, length);
+}
+
+bool CString::IsSafeToSendToAnotherThread() const {
+ return !buffer_ || buffer_->HasOneRef();
+}
+
+bool operator==(const CString& a, const CString& b) {
+ if (a.IsNull() != b.IsNull())
+ return false;
+ if (a.length() != b.length())
+ return false;
+ return !memcmp(a.data(), b.data(), a.length());
+}
+
+bool operator==(const CString& a, const char* b) {
+ if (a.IsNull() != !b)
+ return false;
+ if (!b)
+ return true;
+ return !strcmp(a.data(), b);
+}
+
+std::ostream& operator<<(std::ostream& ostream, const CString& string) {
+ if (string.IsNull())
+ return ostream << "<null>";
+
+ ostream << '"';
+ for (size_t index = 0; index < string.length(); ++index) {
+ // Print shorthands for select cases.
+ char character = string.data()[index];
+ switch (character) {
+ case '\t':
+ ostream << "\\t";
+ break;
+ case '\n':
+ ostream << "\\n";
+ break;
+ case '\r':
+ ostream << "\\r";
+ break;
+ case '"':
+ ostream << "\\\"";
+ break;
+ case '\\':
+ ostream << "\\\\";
+ break;
+ default:
+ if (IsASCIIPrintable(character)) {
+ ostream << character;
+ } else {
+ // Print "\xHH" for control or non-ASCII characters.
+ ostream << "\\x";
+ if (character >= 0 && character < 0x10)
+ ostream << "0";
+ ostream.setf(std::ios_base::hex, std::ios_base::basefield);
+ ostream.setf(std::ios::uppercase);
+ ostream << (character & 0xff);
+ }
+ break;
+ }
+ }
+ return ostream << '"';
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/cstring.h b/chromium/third_party/blink/renderer/platform/wtf/text/cstring.h
new file mode 100644
index 00000000000..797714e7c97
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/cstring.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2003, 2006, 2008, 2009, 2010, 2012 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_CSTRING_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_CSTRING_H_
+
+#include <string.h>
+
+#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/partition_allocator.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_export.h"
+
+namespace WTF {
+
+// CStringImpl is an immutable ref-counted storage for the characters in a
+// CString. It's analogous to a StringImpl but may contain any arbitrary
+// sequence of bytes. The data is always allocated 1 longer than length() and is
+// null terminated.
+class WTF_EXPORT CStringImpl : public RefCounted<CStringImpl> {
+ public:
+ // CStringImpls are allocated out of the WTF buffer partition.
+ void* operator new(size_t, void* ptr) { return ptr; }
+ void operator delete(void*);
+
+ static scoped_refptr<CStringImpl> CreateUninitialized(size_t length,
+ char*& data);
+
+ const char* data() const { return reinterpret_cast<const char*>(this + 1); }
+ size_t length() const { return length_; }
+
+ private:
+ explicit CStringImpl(size_t length) : length_(length) {}
+
+ const unsigned length_;
+
+ DISALLOW_COPY_AND_ASSIGN(CStringImpl);
+};
+
+// A container for an immutable ref-counted null-terminated char array. This is
+// analogous to a WTF::String but does not require the contained bytes to be
+// valid Latin1 or UTF-16. Instead a CString can contain any arbitrary bytes.
+class WTF_EXPORT CString {
+ USING_FAST_MALLOC(CString);
+
+ public:
+ // Construct a null string, distinguishable from an empty string.
+ CString() = default;
+
+ // Construct a string from arbitrary bytes.
+ CString(const char* chars) : CString(chars, chars ? strlen(chars) : 0) {}
+ CString(const char*, size_t length);
+
+ // Construct a string referencing an existing buffer.
+ CString(CStringImpl* buffer) : buffer_(buffer) {}
+ CString(scoped_refptr<CStringImpl> buffer) : buffer_(std::move(buffer)) {}
+
+ static CString CreateUninitialized(size_t length, char*& data) {
+ return CStringImpl::CreateUninitialized(length, data);
+ }
+
+ // The bytes of the string, always NUL terminated. May be null.
+ const char* data() const { return buffer_ ? buffer_->data() : nullptr; }
+
+ // The length of the data(), *not* including the NUL terminator.
+ size_t length() const { return buffer_ ? buffer_->length() : 0; }
+
+ bool IsNull() const { return !buffer_; }
+
+ bool IsSafeToSendToAnotherThread() const;
+
+ CStringImpl* Impl() const { return buffer_.get(); }
+
+ private:
+ scoped_refptr<CStringImpl> buffer_;
+};
+
+WTF_EXPORT bool operator==(const CString& a, const CString& b);
+inline bool operator!=(const CString& a, const CString& b) {
+ return !(a == b);
+}
+WTF_EXPORT bool operator==(const CString& a, const char* b);
+inline bool operator!=(const CString& a, const char* b) {
+ return !(a == b);
+}
+
+// Pretty printer for gtest and base/logging.*. It prepends and appends
+// double-quotes, and escapes characters other than ASCII printables.
+WTF_EXPORT std::ostream& operator<<(std::ostream&, const CString&);
+
+} // namespace WTF
+
+using WTF::CString;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_CSTRING_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/cstring_test.cc b/chromium/third_party/blink/renderer/platform/wtf/text/cstring_test.cc
new file mode 100644
index 00000000000..ad788734f69
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/cstring_test.cc
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2012 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/wtf/text/cstring.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include <sstream>
+
+namespace WTF {
+
+namespace {
+
+CString PrintedString(const CString& string) {
+ std::ostringstream output;
+ output << string;
+ const std::string& result = output.str();
+ return CString(result.data(), result.length());
+}
+
+} // anonymous namespace
+
+TEST(CStringTest, NullStringConstructor) {
+ CString string;
+ EXPECT_TRUE(string.IsNull());
+ EXPECT_EQ(static_cast<const char*>(nullptr), string.data());
+ EXPECT_EQ(static_cast<size_t>(0), string.length());
+
+ CString string_from_char_pointer(static_cast<const char*>(nullptr));
+ EXPECT_TRUE(string_from_char_pointer.IsNull());
+ EXPECT_EQ(static_cast<const char*>(nullptr), string_from_char_pointer.data());
+ EXPECT_EQ(static_cast<size_t>(0), string_from_char_pointer.length());
+
+ CString string_from_char_and_length(static_cast<const char*>(nullptr), 0);
+ EXPECT_TRUE(string_from_char_and_length.IsNull());
+ EXPECT_EQ(static_cast<const char*>(nullptr),
+ string_from_char_and_length.data());
+ EXPECT_EQ(static_cast<size_t>(0), string_from_char_and_length.length());
+}
+
+TEST(CStringTest, EmptyEmptyConstructor) {
+ const char* empty_string = "";
+ CString string(empty_string);
+ EXPECT_FALSE(string.IsNull());
+ EXPECT_EQ(static_cast<size_t>(0), string.length());
+ EXPECT_EQ(0, string.data()[0]);
+
+ CString string_with_length(empty_string, 0);
+ EXPECT_FALSE(string_with_length.IsNull());
+ EXPECT_EQ(static_cast<size_t>(0), string_with_length.length());
+ EXPECT_EQ(0, string_with_length.data()[0]);
+}
+
+TEST(CStringTest, EmptyRegularConstructor) {
+ const char* reference_string = "WebKit";
+
+ CString string(reference_string);
+ EXPECT_FALSE(string.IsNull());
+ EXPECT_EQ(strlen(reference_string), string.length());
+ EXPECT_STREQ(reference_string, string.data());
+
+ CString string_with_length(reference_string, 6);
+ EXPECT_FALSE(string_with_length.IsNull());
+ EXPECT_EQ(strlen(reference_string), string_with_length.length());
+ EXPECT_STREQ(reference_string, string_with_length.data());
+}
+
+TEST(CStringTest, UninitializedConstructor) {
+ char* buffer;
+ CString empty_string = CString::CreateUninitialized(0, buffer);
+ EXPECT_FALSE(empty_string.IsNull());
+ EXPECT_EQ(buffer, empty_string.data());
+ EXPECT_EQ(0, buffer[0]);
+
+ const size_t kLength = 25;
+ CString uninitialized_string = CString::CreateUninitialized(kLength, buffer);
+ EXPECT_FALSE(uninitialized_string.IsNull());
+ EXPECT_EQ(buffer, uninitialized_string.data());
+ EXPECT_EQ(0, uninitialized_string.data()[kLength]);
+}
+
+TEST(CStringTest, ZeroTerminated) {
+ const char* reference_string = "WebKit";
+ CString string_with_length(reference_string, 3);
+ EXPECT_EQ(0, string_with_length.data()[3]);
+}
+
+TEST(CStringTest, Comparison) {
+ // Comparison with another CString.
+ CString a;
+ CString b;
+ EXPECT_TRUE(a == b);
+ EXPECT_FALSE(a != b);
+ a = "a";
+ b = CString();
+ EXPECT_FALSE(a == b);
+ EXPECT_TRUE(a != b);
+ a = "a";
+ b = "b";
+ EXPECT_FALSE(a == b);
+ EXPECT_TRUE(a != b);
+ a = "a";
+ b = "a";
+ EXPECT_TRUE(a == b);
+ EXPECT_FALSE(a != b);
+ a = "a";
+ b = "aa";
+ EXPECT_FALSE(a == b);
+ EXPECT_TRUE(a != b);
+ a = "";
+ b = "";
+ EXPECT_TRUE(a == b);
+ EXPECT_FALSE(a != b);
+ a = "";
+ b = CString();
+ EXPECT_FALSE(a == b);
+ EXPECT_TRUE(a != b);
+ a = "a";
+ b = "";
+ EXPECT_FALSE(a == b);
+ EXPECT_TRUE(a != b);
+
+ // Comparison with a const char*.
+ CString c;
+ const char* d = nullptr;
+ EXPECT_TRUE(c == d);
+ EXPECT_FALSE(c != d);
+ c = "c";
+ d = nullptr;
+ EXPECT_FALSE(c == d);
+ EXPECT_TRUE(c != d);
+ c = CString();
+ d = "d";
+ EXPECT_FALSE(c == d);
+ EXPECT_TRUE(c != d);
+ c = "c";
+ d = "d";
+ EXPECT_FALSE(c == d);
+ EXPECT_TRUE(c != d);
+ c = "c";
+ d = "c";
+ EXPECT_TRUE(c == d);
+ EXPECT_FALSE(c != d);
+ c = "c";
+ d = "cc";
+ EXPECT_FALSE(c == d);
+ EXPECT_TRUE(c != d);
+ c = "cc";
+ d = "c";
+ EXPECT_FALSE(c == d);
+ EXPECT_TRUE(c != d);
+ c = "";
+ d = "";
+ EXPECT_TRUE(c == d);
+ EXPECT_FALSE(c != d);
+ c = "";
+ d = nullptr;
+ EXPECT_FALSE(c == d);
+ EXPECT_TRUE(c != d);
+ c = CString();
+ d = "";
+ EXPECT_FALSE(c == d);
+ EXPECT_TRUE(c != d);
+ c = "a";
+ d = "";
+ EXPECT_FALSE(c == d);
+ EXPECT_TRUE(c != d);
+ c = "";
+ d = "b";
+ EXPECT_FALSE(c == d);
+ EXPECT_TRUE(c != d);
+}
+
+TEST(CStringTest, Printer) {
+ EXPECT_STREQ("<null>", PrintedString(CString()).data());
+ EXPECT_STREQ("\"abc\"", PrintedString("abc").data());
+ EXPECT_STREQ("\"\\t\\n\\r\\\"\\\\\"", PrintedString("\t\n\r\"\\").data());
+ EXPECT_STREQ("\"\\xFF\\x00\\x01xyz\"",
+ PrintedString(CString("\xff\0\x01xyz", 6)).data());
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/icu/collator_icu.cc b/chromium/third_party/blink/renderer/platform/wtf/text/icu/collator_icu.cc
new file mode 100644
index 00000000000..743f827f1e5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/icu/collator_icu.cc
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2008 Apple 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/wtf/text/collator.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unicode/ucol.h>
+#include <memory>
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/threading.h"
+#include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
+
+namespace WTF {
+
+static UCollator* g_cached_collator;
+static char g_cached_equivalent_locale[Collator::kUlocFullnameCapacity];
+static Mutex& CachedCollatorMutex() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(Mutex, mutex, ());
+ return mutex;
+}
+
+Collator::Collator(const char* locale)
+ : collator_(nullptr),
+ locale_(locale ? strdup(locale) : nullptr),
+ lower_first_(false) {
+ SetEquivalentLocale(locale_, equivalent_locale_);
+}
+
+std::unique_ptr<Collator> Collator::UserDefault() {
+ return std::make_unique<Collator>(nullptr);
+}
+
+Collator::~Collator() {
+ ReleaseCollator();
+ free(locale_);
+}
+
+void Collator::SetOrderLowerFirst(bool lower_first) {
+ lower_first_ = lower_first;
+}
+
+Collator::Result Collator::Collate(const UChar* lhs,
+ size_t lhs_length,
+ const UChar* rhs,
+ size_t rhs_length) const {
+ if (!collator_)
+ CreateCollator();
+
+ return static_cast<Result>(
+ ucol_strcoll(collator_, lhs, lhs_length, rhs, rhs_length));
+}
+
+void Collator::CreateCollator() const {
+ DCHECK(!collator_);
+ UErrorCode status = U_ZERO_ERROR;
+
+ {
+ Locker<Mutex> lock(CachedCollatorMutex());
+ if (g_cached_collator) {
+ UColAttributeValue cached_collator_lower_first =
+ ucol_getAttribute(g_cached_collator, UCOL_CASE_FIRST, &status);
+ DCHECK(U_SUCCESS(status));
+
+ if (0 == strcmp(g_cached_equivalent_locale, equivalent_locale_) &&
+ ((UCOL_LOWER_FIRST == cached_collator_lower_first && lower_first_) ||
+ (UCOL_UPPER_FIRST == cached_collator_lower_first &&
+ !lower_first_))) {
+ collator_ = g_cached_collator;
+ g_cached_collator = nullptr;
+ g_cached_equivalent_locale[0] = 0;
+ return;
+ }
+ }
+ }
+
+ collator_ = ucol_open(locale_, &status);
+ if (U_FAILURE(status)) {
+ status = U_ZERO_ERROR;
+ collator_ =
+ ucol_open("", &status); // Fallback to Unicode Collation Algorithm.
+ }
+ DCHECK(U_SUCCESS(status));
+
+ ucol_setAttribute(collator_, UCOL_CASE_FIRST,
+ lower_first_ ? UCOL_LOWER_FIRST : UCOL_UPPER_FIRST,
+ &status);
+ DCHECK(U_SUCCESS(status));
+
+ ucol_setAttribute(collator_, UCOL_NORMALIZATION_MODE, UCOL_ON, &status);
+ DCHECK(U_SUCCESS(status));
+}
+
+void Collator::ReleaseCollator() {
+ {
+ Locker<Mutex> lock(CachedCollatorMutex());
+ if (g_cached_collator)
+ ucol_close(g_cached_collator);
+ g_cached_collator = collator_;
+ strncpy(g_cached_equivalent_locale, equivalent_locale_,
+ kUlocFullnameCapacity);
+ collator_ = nullptr;
+ }
+ collator_ = nullptr;
+}
+
+void Collator::SetEquivalentLocale(const char* locale,
+ char* equivalent_locale) {
+ UErrorCode status = U_ZERO_ERROR;
+ UBool is_available;
+ ucol_getFunctionalEquivalent(equivalent_locale, kUlocFullnameCapacity,
+ "collation", locale, &is_available, &status);
+ if (U_FAILURE(status))
+ strcpy(equivalent_locale, "root");
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/icu/unicode_icu.h b/chromium/third_party/blink/renderer/platform/wtf/text/icu/unicode_icu.h
new file mode 100644
index 00000000000..ffa022cf090
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/icu/unicode_icu.h
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2006 George Staikos <staikos@kde.org>
+ * Copyright (C) 2006 Alexey Proskuryakov <ap@nypop.com>
+ * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_ICU_UNICODE_ICU_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_ICU_UNICODE_ICU_H_
+
+#include <unicode/uchar.h>
+#include <unicode/ustring.h>
+
+namespace WTF {
+
+namespace Unicode {
+
+enum CharDirection {
+ kLeftToRight = U_LEFT_TO_RIGHT,
+ kRightToLeft = U_RIGHT_TO_LEFT,
+ kEuropeanNumber = U_EUROPEAN_NUMBER,
+ kEuropeanNumberSeparator = U_EUROPEAN_NUMBER_SEPARATOR,
+ kEuropeanNumberTerminator = U_EUROPEAN_NUMBER_TERMINATOR,
+ kArabicNumber = U_ARABIC_NUMBER,
+ kCommonNumberSeparator = U_COMMON_NUMBER_SEPARATOR,
+ kBlockSeparator = U_BLOCK_SEPARATOR,
+ kSegmentSeparator = U_SEGMENT_SEPARATOR,
+ kWhiteSpaceNeutral = U_WHITE_SPACE_NEUTRAL,
+ kOtherNeutral = U_OTHER_NEUTRAL,
+ kLeftToRightEmbedding = U_LEFT_TO_RIGHT_EMBEDDING,
+ kLeftToRightOverride = U_LEFT_TO_RIGHT_OVERRIDE,
+ kRightToLeftArabic = U_RIGHT_TO_LEFT_ARABIC,
+ kRightToLeftEmbedding = U_RIGHT_TO_LEFT_EMBEDDING,
+ kRightToLeftOverride = U_RIGHT_TO_LEFT_OVERRIDE,
+ kPopDirectionalFormat = U_POP_DIRECTIONAL_FORMAT,
+ kNonSpacingMark = U_DIR_NON_SPACING_MARK,
+ kBoundaryNeutral = U_BOUNDARY_NEUTRAL
+};
+
+enum CharDecompositionType {
+ kDecompositionNone = U_DT_NONE,
+ kDecompositionCanonical = U_DT_CANONICAL,
+ kDecompositionCompat = U_DT_COMPAT,
+ kDecompositionCircle = U_DT_CIRCLE,
+ kDecompositionFinal = U_DT_FINAL,
+ kDecompositionFont = U_DT_FONT,
+ kDecompositionFraction = U_DT_FRACTION,
+ kDecompositionInitial = U_DT_INITIAL,
+ kDecompositionIsolated = U_DT_ISOLATED,
+ kDecompositionMedial = U_DT_MEDIAL,
+ kDecompositionNarrow = U_DT_NARROW,
+ kDecompositionNoBreak = U_DT_NOBREAK,
+ kDecompositionSmall = U_DT_SMALL,
+ kDecompositionSquare = U_DT_SQUARE,
+ kDecompositionSub = U_DT_SUB,
+ kDecompositionSuper = U_DT_SUPER,
+ kDecompositionVertical = U_DT_VERTICAL,
+ kDecompositionWide = U_DT_WIDE,
+};
+
+enum CharCategory {
+ kNoCategory = 0,
+ kOther_NotAssigned = U_MASK(U_GENERAL_OTHER_TYPES),
+ kLetter_Uppercase = U_MASK(U_UPPERCASE_LETTER),
+ kLetter_Lowercase = U_MASK(U_LOWERCASE_LETTER),
+ kLetter_Titlecase = U_MASK(U_TITLECASE_LETTER),
+ kLetter_Modifier = U_MASK(U_MODIFIER_LETTER),
+ kLetter_Other = U_MASK(U_OTHER_LETTER),
+
+ kMark_NonSpacing = U_MASK(U_NON_SPACING_MARK),
+ kMark_Enclosing = U_MASK(U_ENCLOSING_MARK),
+ kMark_SpacingCombining = U_MASK(U_COMBINING_SPACING_MARK),
+
+ kNumber_DecimalDigit = U_MASK(U_DECIMAL_DIGIT_NUMBER),
+ kNumber_Letter = U_MASK(U_LETTER_NUMBER),
+ kNumber_Other = U_MASK(U_OTHER_NUMBER),
+
+ kSeparator_Space = U_MASK(U_SPACE_SEPARATOR),
+ kSeparator_Line = U_MASK(U_LINE_SEPARATOR),
+ kSeparator_Paragraph = U_MASK(U_PARAGRAPH_SEPARATOR),
+
+ kOther_Control = U_MASK(U_CONTROL_CHAR),
+ kOther_Format = U_MASK(U_FORMAT_CHAR),
+ kOther_PrivateUse = U_MASK(U_PRIVATE_USE_CHAR),
+ kOther_Surrogate = U_MASK(U_SURROGATE),
+
+ kPunctuation_Dash = U_MASK(U_DASH_PUNCTUATION),
+ kPunctuation_Open = U_MASK(U_START_PUNCTUATION),
+ kPunctuation_Close = U_MASK(U_END_PUNCTUATION),
+ kPunctuation_Connector = U_MASK(U_CONNECTOR_PUNCTUATION),
+ kPunctuation_Other = U_MASK(U_OTHER_PUNCTUATION),
+
+ kSymbol_Math = U_MASK(U_MATH_SYMBOL),
+ kSymbol_Currency = U_MASK(U_CURRENCY_SYMBOL),
+ kSymbol_Modifier = U_MASK(U_MODIFIER_SYMBOL),
+ kSymbol_Other = U_MASK(U_OTHER_SYMBOL),
+
+ kPunctuation_InitialQuote = U_MASK(U_INITIAL_PUNCTUATION),
+ kPunctuation_FinalQuote = U_MASK(U_FINAL_PUNCTUATION)
+};
+
+inline UChar32 FoldCase(UChar32 c) {
+ return u_foldCase(c, U_FOLD_CASE_DEFAULT);
+}
+
+inline int FoldCase(UChar* result,
+ int result_length,
+ const UChar* src,
+ int src_length,
+ bool* error) {
+ UErrorCode status = U_ZERO_ERROR;
+ int real_length = u_strFoldCase(result, result_length, src, src_length,
+ U_FOLD_CASE_DEFAULT, &status);
+ *error = !U_SUCCESS(status);
+ return real_length;
+}
+
+inline int ToLower(UChar* result,
+ int result_length,
+ const UChar* src,
+ int src_length,
+ bool* error) {
+ UErrorCode status = U_ZERO_ERROR;
+ int real_length =
+ u_strToLower(result, result_length, src, src_length, "", &status);
+ *error = !!U_FAILURE(status);
+ return real_length;
+}
+
+inline UChar32 ToLower(UChar32 c) {
+ return u_tolower(c);
+}
+
+inline UChar32 ToUpper(UChar32 c) {
+ return u_toupper(c);
+}
+
+inline int ToUpper(UChar* result,
+ int result_length,
+ const UChar* src,
+ int src_length,
+ bool* error) {
+ UErrorCode status = U_ZERO_ERROR;
+ int real_length =
+ u_strToUpper(result, result_length, src, src_length, "", &status);
+ *error = !!U_FAILURE(status);
+ return real_length;
+}
+
+inline UChar32 ToTitleCase(UChar32 c) {
+ return u_totitle(c);
+}
+
+inline bool IsArabicChar(UChar32 c) {
+ return ublock_getCode(c) == UBLOCK_ARABIC;
+}
+
+inline bool IsAlphanumeric(UChar32 c) {
+ return !!u_isalnum(c);
+}
+
+inline bool IsSeparatorSpace(UChar32 c) {
+ return u_charType(c) == U_SPACE_SEPARATOR;
+}
+
+inline bool IsPrintableChar(UChar32 c) {
+ return !!u_isprint(c);
+}
+
+inline bool IsPunct(UChar32 c) {
+ return !!u_ispunct(c);
+}
+
+inline bool HasLineBreakingPropertyComplexContext(UChar32 c) {
+ return u_getIntPropertyValue(c, UCHAR_LINE_BREAK) == U_LB_COMPLEX_CONTEXT;
+}
+
+inline UChar32 MirroredChar(UChar32 c) {
+ return u_charMirror(c);
+}
+
+inline CharCategory Category(UChar32 c) {
+ return static_cast<CharCategory>(U_GET_GC_MASK(c));
+}
+
+inline CharDirection Direction(UChar32 c) {
+ return static_cast<CharDirection>(u_charDirection(c));
+}
+
+inline bool IsLower(UChar32 c) {
+ return !!u_islower(c);
+}
+
+inline uint8_t CombiningClass(UChar32 c) {
+ return u_getCombiningClass(c);
+}
+
+inline CharDecompositionType DecompositionType(UChar32 c) {
+ return static_cast<CharDecompositionType>(
+ u_getIntPropertyValue(c, UCHAR_DECOMPOSITION_TYPE));
+}
+
+inline int Umemcasecmp(const UChar* a, const UChar* b, int len) {
+ return u_memcasecmp(a, b, len, U_FOLD_CASE_DEFAULT);
+}
+
+} // namespace Unicode
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_ICU_UNICODE_ICU_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/integer_to_string_conversion.h b/chromium/third_party/blink/renderer/platform/wtf/text/integer_to_string_conversion.h
new file mode 100644
index 00000000000..807617186a9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/integer_to_string_conversion.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All Rights Reserved.
+ * Copyright (C) 2012 Patrick Gansterer <paroga@paroga.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_INTEGER_TO_STRING_CONVERSION_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_INTEGER_TO_STRING_CONVERSION_H_
+
+#include <limits>
+#include <type_traits>
+#include "base/numerics/safe_conversions.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
+
+namespace WTF {
+
+// TODO(esprehn): See if we can generalize IntToStringT in
+// base/strings/string_number_conversions.cc, and use unsigned type expansion
+// optimization here instead of CheckedNumeric::UnsignedAbs().
+template <typename IntegerType>
+class IntegerToStringConverter {
+ public:
+ static_assert(std::is_integral<IntegerType>::value,
+ "IntegerType must be a type of integer.");
+
+ explicit IntegerToStringConverter(IntegerType input) {
+ LChar* end = buffer_ + WTF_ARRAY_LENGTH(buffer_);
+ begin_ = end;
+
+ // We need to switch to the unsigned type when negating the value since
+ // abs(INT_MIN) == INT_MAX + 1.
+ bool is_negative = base::IsValueNegative(input);
+ UnsignedIntegerType value = is_negative ? 0u - input : input;
+
+ do {
+ --begin_;
+ DCHECK_NE(begin_, buffer_);
+ *begin_ = static_cast<LChar>((value % 10) + '0');
+ value /= 10;
+ } while (value);
+
+ if (is_negative) {
+ --begin_;
+ DCHECK_NE(begin_, buffer_);
+ *begin_ = static_cast<LChar>('-');
+ }
+
+ length_ = static_cast<unsigned>(end - begin_);
+ }
+
+ const LChar* Characters8() const { return begin_; }
+ unsigned length() const { return length_; }
+
+ private:
+ using UnsignedIntegerType = typename std::make_unsigned<IntegerType>::type;
+ static const size_t kBufferSize = 3 * sizeof(UnsignedIntegerType) +
+ std::numeric_limits<IntegerType>::is_signed;
+
+ LChar buffer_[kBufferSize];
+ LChar* begin_;
+ unsigned length_;
+};
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_INTEGER_TO_STRING_CONVERSION_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/integer_to_string_conversion_test.cc b/chromium/third_party/blink/renderer/platform/wtf/text/integer_to_string_conversion_test.cc
new file mode 100644
index 00000000000..440c3426c90
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/integer_to_string_conversion_test.cc
@@ -0,0 +1,18 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/wtf/text/integer_to_string_conversion.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_view.h"
+
+namespace WTF {
+
+TEST(IntegerToStringConversionTest, SimpleIntConversion) {
+ const IntegerToStringConverter<int> conv(100500);
+ EXPECT_EQ(StringView(conv.Characters8(), conv.length()),
+ StringView("100500"));
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/number_parsing_options.h b/chromium/third_party/blink/renderer/platform/wtf/text/number_parsing_options.h
new file mode 100644
index 00000000000..d2cd46a6f1e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/number_parsing_options.h
@@ -0,0 +1,51 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_NUMBER_PARSING_OPTIONS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_NUMBER_PARSING_OPTIONS_H_
+
+#include "base/logging.h"
+
+namespace WTF {
+
+// Copyable and immutable object representing number parsing flags.
+class NumberParsingOptions {
+ public:
+ static constexpr unsigned kNone = 0;
+ static constexpr unsigned kAcceptTrailingGarbage = 1;
+ static constexpr unsigned kAcceptLeadingPlus = 1 << 1;
+ static constexpr unsigned kAcceptLeadingTrailingWhitespace = 1 << 2;
+ static constexpr unsigned kAcceptMinusZeroForUnsigned = 1 << 3;
+
+ // 'Strict' behavior for WTF::String.
+ static constexpr unsigned kStrict =
+ kAcceptLeadingPlus | kAcceptLeadingTrailingWhitespace;
+ // Non-'Strict' behavior for WTF::String.
+ static constexpr unsigned kLoose = kStrict | kAcceptTrailingGarbage;
+
+ // This constructor allows implicit conversion from unsigned.
+ NumberParsingOptions(unsigned options) : options_(options) {
+ DCHECK_LT(options, 1u << 4) << "NumberParsingOptions should be built with "
+ "a combination of "
+ "NumberParsingOptions::kFoo constants.";
+ }
+
+ bool AcceptTrailingGarbage() const {
+ return options_ & kAcceptTrailingGarbage;
+ }
+ bool AcceptLeadingPlus() const { return options_ & kAcceptLeadingPlus; }
+ bool AcceptWhitespace() const {
+ return options_ & kAcceptLeadingTrailingWhitespace;
+ }
+ bool AcceptMinusZeroForUnsigned() const {
+ return options_ & kAcceptMinusZeroForUnsigned;
+ }
+
+ private:
+ unsigned options_;
+};
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_NUMBER_PARSING_OPTIONS_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/parsing_utilities.h b/chromium/third_party/blink/renderer/platform/wtf/text/parsing_utilities.h
new file mode 100644
index 00000000000..02066e7bf68
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/parsing_utilities.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_PARSING_UTILITIES_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_PARSING_UTILITIES_H_
+
+namespace WTF {
+
+template <typename CharType>
+bool SkipExactly(const CharType*& position,
+ const CharType* end,
+ CharType delimiter) {
+ if (position < end && *position == delimiter) {
+ ++position;
+ return true;
+ }
+ return false;
+}
+
+template <typename CharType, bool characterPredicate(CharType)>
+bool SkipExactly(const CharType*& position, const CharType* end) {
+ if (position < end && characterPredicate(*position)) {
+ ++position;
+ return true;
+ }
+ return false;
+}
+
+template <typename CharType>
+bool SkipToken(const CharType*& position,
+ const CharType* end,
+ const char* token) {
+ const CharType* current = position;
+ while (current < end && *token) {
+ if (*current != *token)
+ return false;
+ ++current;
+ ++token;
+ }
+ if (*token)
+ return false;
+
+ position = current;
+ return true;
+}
+
+template <typename CharType>
+void SkipUntil(const CharType*& position,
+ const CharType* end,
+ CharType delimiter) {
+ while (position < end && *position != delimiter)
+ ++position;
+}
+
+template <typename CharType, bool characterPredicate(CharType)>
+void SkipUntil(const CharType*& position, const CharType* end) {
+ while (position < end && !characterPredicate(*position))
+ ++position;
+}
+
+template <typename CharType, bool characterPredicate(CharType)>
+void SkipWhile(const CharType*& position, const CharType* end) {
+ while (position < end && characterPredicate(*position))
+ ++position;
+}
+
+template <typename CharType, bool characterPredicate(CharType)>
+void ReverseSkipWhile(const CharType*& position, const CharType* start) {
+ while (position >= start && characterPredicate(*position))
+ --position;
+}
+
+} // namespace WTF
+
+using WTF::SkipExactly;
+using WTF::SkipToken;
+using WTF::SkipUntil;
+using WTF::SkipWhile;
+using WTF::ReverseSkipWhile;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_PARSING_UTILITIES_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/string_buffer.h b/chromium/third_party/blink/renderer/platform/wtf/text/string_buffer.h
new file mode 100644
index 00000000000..f9d563264e0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/string_buffer.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2008, 2010 Apple 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:
+ *
+ * 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 Apple Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_STRING_BUFFER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_STRING_BUFFER_H_
+
+#include "base/macros.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_impl.h"
+#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
+
+namespace WTF {
+
+template <typename CharType>
+class StringBuffer {
+ DISALLOW_NEW();
+
+ public:
+ StringBuffer() = default;
+
+ explicit StringBuffer(unsigned length) {
+ CharType* characters;
+ data_ = StringImpl::CreateUninitialized(length, characters);
+ }
+
+ ~StringBuffer() = default;
+
+ void Shrink(unsigned new_length);
+
+ unsigned length() const { return data_ ? data_->length() : 0; }
+ CharType* Characters() {
+ return length() ? const_cast<CharType*>(data_->GetCharacters<CharType>())
+ : nullptr;
+ }
+
+ CharType& operator[](unsigned i) {
+ SECURITY_DCHECK(i < length());
+ return Characters()[i];
+ }
+
+ scoped_refptr<StringImpl> Release() { return std::move(data_); }
+
+ private:
+ scoped_refptr<StringImpl> data_;
+
+ DISALLOW_COPY_AND_ASSIGN(StringBuffer);
+};
+
+template <typename CharType>
+void StringBuffer<CharType>::Shrink(unsigned new_length) {
+ DCHECK(data_);
+ if (data_->length() == new_length)
+ return;
+ data_ = data_->Substring(0, new_length);
+}
+
+} // namespace WTF
+
+using WTF::StringBuffer;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_STRING_BUFFER_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/string_buffer_test.cc b/chromium/third_party/blink/renderer/platform/wtf/text/string_buffer_test.cc
new file mode 100644
index 00000000000..944dc68cf68
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/string_buffer_test.cc
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "third_party/blink/renderer/platform/wtf/text/string_buffer.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace WTF {
+
+TEST(StringBufferTest, Initial) {
+ StringBuffer<LChar> buf1;
+ EXPECT_EQ(0u, buf1.length());
+ EXPECT_FALSE(buf1.Characters());
+
+ StringBuffer<LChar> buf2(0);
+ EXPECT_EQ(0u, buf2.length());
+ EXPECT_FALSE(buf2.Characters());
+
+ StringBuffer<LChar> buf3(1);
+ EXPECT_EQ(1u, buf3.length());
+ EXPECT_TRUE(buf3.Characters());
+}
+
+TEST(StringBufferTest, shrink) {
+ StringBuffer<LChar> buf(2);
+ EXPECT_EQ(2u, buf.length());
+ buf[0] = 'a';
+ buf[1] = 'b';
+
+ buf.Shrink(1);
+ EXPECT_EQ(1u, buf.length());
+ EXPECT_EQ('a', buf[0]);
+
+ buf.Shrink(0);
+ EXPECT_EQ(0u, buf.length());
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/string_builder.cc b/chromium/third_party/blink/renderer/platform/wtf/text/string_builder.cc
new file mode 100644
index 00000000000..46e7df001ce
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/string_builder.cc
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2012 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+
+#include <algorithm>
+#include "third_party/blink/renderer/platform/wtf/dtoa.h"
+#include "third_party/blink/renderer/platform/wtf/text/integer_to_string_conversion.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace WTF {
+
+String StringBuilder::ToString() {
+ if (!length_)
+ return g_empty_string;
+ if (string_.IsNull()) {
+ if (is8_bit_)
+ string_ = String(Characters8(), length_);
+ else
+ string_ = String(Characters16(), length_);
+ ClearBuffer();
+ }
+ return string_;
+}
+
+AtomicString StringBuilder::ToAtomicString() {
+ if (!length_)
+ return g_empty_atom;
+ if (string_.IsNull()) {
+ if (is8_bit_)
+ string_ = AtomicString(Characters8(), length_);
+ else
+ string_ = AtomicString(Characters16(), length_);
+ ClearBuffer();
+ }
+ return AtomicString(string_);
+}
+
+String StringBuilder::Substring(unsigned start, unsigned length) const {
+ if (start >= length_)
+ return g_empty_string;
+ if (!string_.IsNull())
+ return string_.Substring(start, length);
+ length = std::min(length, length_ - start);
+ if (is8_bit_)
+ return String(Characters8() + start, length);
+ return String(Characters16() + start, length);
+}
+
+void StringBuilder::Swap(StringBuilder& builder) {
+ std::swap(string_, builder.string_);
+ std::swap(buffer_, builder.buffer_);
+ std::swap(length_, builder.length_);
+ std::swap(is8_bit_, builder.is8_bit_);
+}
+
+void StringBuilder::ClearBuffer() {
+ if (is8_bit_)
+ delete buffer8_;
+ else
+ delete buffer16_;
+ buffer_ = nullptr;
+}
+
+void StringBuilder::Clear() {
+ ClearBuffer();
+ string_ = String();
+ length_ = 0;
+ is8_bit_ = true;
+}
+
+unsigned StringBuilder::Capacity() const {
+ if (!HasBuffer())
+ return 0;
+ if (is8_bit_)
+ return buffer8_->capacity();
+ return buffer16_->capacity();
+}
+
+void StringBuilder::ReserveCapacity(unsigned new_capacity) {
+ if (is8_bit_)
+ EnsureBuffer8(new_capacity);
+ else
+ EnsureBuffer16(new_capacity);
+}
+
+void StringBuilder::Resize(unsigned new_size) {
+ DCHECK_LE(new_size, length_);
+ string_ = string_.Left(new_size);
+ length_ = new_size;
+ if (HasBuffer()) {
+ if (is8_bit_)
+ buffer8_->resize(new_size);
+ else
+ buffer16_->resize(new_size);
+ }
+}
+
+void StringBuilder::CreateBuffer8(unsigned added_size) {
+ DCHECK(!HasBuffer());
+ DCHECK(is8_bit_);
+ buffer8_ = new Buffer8;
+ // createBuffer is called right before appending addedSize more bytes. We
+ // want to ensure we have enough space to fit m_string plus the added
+ // size.
+ //
+ // We also ensure that we have at least the initialBufferSize of extra space
+ // for appending new bytes to avoid future mallocs for appending short
+ // strings or single characters. This is a no-op if m_length == 0 since
+ // initialBufferSize() is the same as the inline capacity of the vector.
+ // This allows doing append(string); append('\0') without extra mallocs.
+ buffer8_->ReserveInitialCapacity(length_ +
+ std::max(added_size, InitialBufferSize()));
+ length_ = 0;
+ Append(string_);
+ string_ = String();
+}
+
+void StringBuilder::CreateBuffer16(unsigned added_size) {
+ DCHECK(is8_bit_ || !HasBuffer());
+ Buffer8 buffer8;
+ unsigned length = length_;
+ if (buffer8_) {
+ buffer8_->swap(buffer8);
+ delete buffer8_;
+ }
+ buffer16_ = new Buffer16;
+ // See createBuffer8's call to reserveInitialCapacity for why we do this.
+ buffer16_->ReserveInitialCapacity(length_ +
+ std::max(added_size, InitialBufferSize()));
+ is8_bit_ = false;
+ length_ = 0;
+ if (!buffer8.IsEmpty()) {
+ Append(buffer8.data(), length);
+ return;
+ }
+ Append(string_);
+ string_ = String();
+}
+
+void StringBuilder::Append(const UChar* characters, unsigned length) {
+ if (!length)
+ return;
+ DCHECK(characters);
+
+ // If there's only one char we use append(UChar) instead since it will
+ // check for latin1 and avoid converting to 16bit if possible.
+ if (length == 1) {
+ Append(*characters);
+ return;
+ }
+
+ EnsureBuffer16(length);
+ buffer16_->Append(characters, length);
+ length_ += length;
+}
+
+void StringBuilder::Append(const LChar* characters, unsigned length) {
+ if (!length)
+ return;
+ DCHECK(characters);
+
+ if (is8_bit_) {
+ EnsureBuffer8(length);
+ buffer8_->Append(characters, length);
+ length_ += length;
+ return;
+ }
+
+ EnsureBuffer16(length);
+ buffer16_->Append(characters, length);
+ length_ += length;
+}
+
+template <typename IntegerType>
+static void AppendIntegerInternal(StringBuilder& builder, IntegerType input) {
+ IntegerToStringConverter<IntegerType> converter(input);
+ builder.Append(converter.Characters8(), converter.length());
+}
+
+void StringBuilder::AppendNumber(int number) {
+ AppendIntegerInternal(*this, number);
+}
+
+void StringBuilder::AppendNumber(unsigned number) {
+ AppendIntegerInternal(*this, number);
+}
+
+void StringBuilder::AppendNumber(long number) {
+ AppendIntegerInternal(*this, number);
+}
+
+void StringBuilder::AppendNumber(unsigned long number) {
+ AppendIntegerInternal(*this, number);
+}
+
+void StringBuilder::AppendNumber(long long number) {
+ AppendIntegerInternal(*this, number);
+}
+
+void StringBuilder::AppendNumber(unsigned long long number) {
+ AppendIntegerInternal(*this, number);
+}
+
+void StringBuilder::AppendNumber(double number, unsigned precision) {
+ NumberToStringBuffer buffer;
+ Append(NumberToFixedPrecisionString(number, precision, buffer));
+}
+
+void StringBuilder::erase(unsigned index) {
+ if (index >= length_)
+ return;
+
+ if (is8_bit_) {
+ EnsureBuffer8(0);
+ buffer8_->EraseAt(index);
+ } else {
+ EnsureBuffer16(0);
+ buffer16_->EraseAt(index);
+ }
+ --length_;
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/string_builder.h b/chromium/third_party/blink/renderer/platform/wtf/text/string_builder.h
new file mode 100644
index 00000000000..ae74e0fc253
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/string_builder.h
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2009, 2010, 2012, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2012 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_STRING_BUILDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_STRING_BUILDER_H_
+
+#include "base/macros.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_view.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_export.h"
+
+namespace WTF {
+
+class WTF_EXPORT StringBuilder {
+ public:
+ StringBuilder() : buffer_(nullptr), length_(0), is8_bit_(true) {}
+
+ ~StringBuilder() { Clear(); }
+
+ void Append(const UChar*, unsigned length);
+ void Append(const LChar*, unsigned length);
+
+ ALWAYS_INLINE void Append(const char* characters, unsigned length) {
+ Append(reinterpret_cast<const LChar*>(characters), length);
+ }
+
+ void Append(const StringBuilder& other) {
+ if (!other.length_)
+ return;
+
+ if (!length_ && !HasBuffer() && !other.string_.IsNull()) {
+ string_ = other.string_;
+ length_ = other.string_.length();
+ is8_bit_ = other.string_.Is8Bit();
+ return;
+ }
+
+ if (other.Is8Bit())
+ Append(other.Characters8(), other.length_);
+ else
+ Append(other.Characters16(), other.length_);
+ }
+
+ // NOTE: The semantics of this are different than StringView(..., offset,
+ // length) in that an invalid offset or invalid length is a no-op instead of
+ // an error.
+ // TODO(esprehn): We should probably unify the semantics instead.
+ void Append(const StringView& string, unsigned offset, unsigned length) {
+ unsigned extent = offset + length;
+ if (extent < offset || extent > string.length())
+ return;
+
+ // We can't do this before the above check since StringView's constructor
+ // doesn't accept invalid offsets or lengths.
+ Append(StringView(string, offset, length));
+ }
+
+ void Append(const StringView& string) {
+ if (string.IsEmpty())
+ return;
+
+ // If we're appending to an empty builder, and there is not a buffer
+ // (reserveCapacity has not been called), then share the impl if
+ // possible.
+ //
+ // This is important to avoid string copies inside dom operations like
+ // Node::textContent when there's only a single Text node child, or
+ // inside the parser in the common case when flushing buffered text to
+ // a Text node.
+ StringImpl* impl = string.SharedImpl();
+ if (!length_ && !HasBuffer() && impl) {
+ string_ = impl;
+ length_ = impl->length();
+ is8_bit_ = impl->Is8Bit();
+ return;
+ }
+
+ if (string.Is8Bit())
+ Append(string.Characters8(), string.length());
+ else
+ Append(string.Characters16(), string.length());
+ }
+
+ void Append(UChar c) {
+ if (is8_bit_ && c <= 0xFF) {
+ Append(static_cast<LChar>(c));
+ return;
+ }
+ EnsureBuffer16(1);
+ buffer16_->push_back(c);
+ ++length_;
+ }
+
+ void Append(LChar c) {
+ if (!is8_bit_) {
+ Append(static_cast<UChar>(c));
+ return;
+ }
+ EnsureBuffer8(1);
+ buffer8_->push_back(c);
+ ++length_;
+ }
+
+ void Append(char c) { Append(static_cast<LChar>(c)); }
+
+ void Append(UChar32 c) {
+ if (U_IS_BMP(c)) {
+ Append(static_cast<UChar>(c));
+ return;
+ }
+ Append(U16_LEAD(c));
+ Append(U16_TRAIL(c));
+ }
+
+ void AppendNumber(int);
+ void AppendNumber(unsigned);
+ void AppendNumber(long);
+ void AppendNumber(unsigned long);
+ void AppendNumber(long long);
+ void AppendNumber(unsigned long long);
+ void AppendNumber(double, unsigned precision = 6);
+
+ void erase(unsigned);
+
+ String ToString();
+ AtomicString ToAtomicString();
+ String Substring(unsigned start, unsigned length) const;
+
+ unsigned length() const { return length_; }
+ bool IsEmpty() const { return !length_; }
+
+ unsigned Capacity() const;
+ void ReserveCapacity(unsigned new_capacity);
+
+ // TODO(esprehn): Rename to shrink().
+ void Resize(unsigned new_size);
+
+ UChar operator[](unsigned i) const {
+ SECURITY_DCHECK(i < length_);
+ if (is8_bit_)
+ return Characters8()[i];
+ return Characters16()[i];
+ }
+
+ const LChar* Characters8() const {
+ DCHECK(is8_bit_);
+ if (!length())
+ return nullptr;
+ if (!string_.IsNull())
+ return string_.Characters8();
+ DCHECK(buffer8_);
+ return buffer8_->data();
+ }
+
+ const UChar* Characters16() const {
+ DCHECK(!is8_bit_);
+ if (!length())
+ return nullptr;
+ if (!string_.IsNull())
+ return string_.Characters16();
+ DCHECK(buffer16_);
+ return buffer16_->data();
+ }
+
+ bool Is8Bit() const { return is8_bit_; }
+
+ void Clear();
+ void Swap(StringBuilder&);
+
+ private:
+ static const unsigned kInlineBufferSize = 16;
+ static unsigned InitialBufferSize() { return kInlineBufferSize; }
+
+ typedef Vector<LChar, kInlineBufferSize> Buffer8;
+ typedef Vector<UChar, kInlineBufferSize> Buffer16;
+
+ void EnsureBuffer8(unsigned added_size) {
+ DCHECK(is8_bit_);
+ if (!HasBuffer())
+ CreateBuffer8(added_size);
+ }
+
+ void EnsureBuffer16(unsigned added_size) {
+ if (is8_bit_ || !HasBuffer())
+ CreateBuffer16(added_size);
+ }
+
+ void CreateBuffer8(unsigned added_size);
+ void CreateBuffer16(unsigned added_size);
+ void ClearBuffer();
+ bool HasBuffer() const { return buffer_; }
+
+ String string_;
+ union {
+ Buffer8* buffer8_;
+ Buffer16* buffer16_;
+ void* buffer_;
+ };
+ unsigned length_;
+ bool is8_bit_;
+
+ DISALLOW_COPY_AND_ASSIGN(StringBuilder);
+};
+
+template <typename CharType>
+bool Equal(const StringBuilder& s, const CharType* buffer, unsigned length) {
+ if (s.length() != length)
+ return false;
+
+ if (s.Is8Bit())
+ return Equal(s.Characters8(), buffer, length);
+
+ return Equal(s.Characters16(), buffer, length);
+}
+
+template <typename CharType>
+bool DeprecatedEqualIgnoringCase(const StringBuilder& s,
+ const CharType* buffer,
+ unsigned length) {
+ if (s.length() != length)
+ return false;
+
+ if (s.Is8Bit())
+ return DeprecatedEqualIgnoringCase(s.Characters8(), buffer, length);
+
+ return DeprecatedEqualIgnoringCase(s.Characters16(), buffer, length);
+}
+
+// Unicode aware case insensitive string matching. Non-ASCII characters might
+// match to ASCII characters. This function is rarely used to implement web
+// platform features.
+// This function is deprecated. We should introduce EqualIgnoringASCIICase() or
+// EqualIgnoringUnicodeCase(). See crbug.com/627682
+inline bool DeprecatedEqualIgnoringCase(const StringBuilder& s,
+ const char* string) {
+ return DeprecatedEqualIgnoringCase(s, reinterpret_cast<const LChar*>(string),
+ strlen(string));
+}
+
+template <typename StringType>
+bool Equal(const StringBuilder& a, const StringType& b) {
+ if (a.length() != b.length())
+ return false;
+
+ if (!a.length())
+ return true;
+
+ if (a.Is8Bit()) {
+ if (b.Is8Bit())
+ return Equal(a.Characters8(), b.Characters8(), a.length());
+ return Equal(a.Characters8(), b.Characters16(), a.length());
+ }
+
+ if (b.Is8Bit())
+ return Equal(a.Characters16(), b.Characters8(), a.length());
+ return Equal(a.Characters16(), b.Characters16(), a.length());
+}
+
+inline bool operator==(const StringBuilder& a, const StringBuilder& b) {
+ return Equal(a, b);
+}
+inline bool operator!=(const StringBuilder& a, const StringBuilder& b) {
+ return !Equal(a, b);
+}
+inline bool operator==(const StringBuilder& a, const String& b) {
+ return Equal(a, b);
+}
+inline bool operator!=(const StringBuilder& a, const String& b) {
+ return !Equal(a, b);
+}
+inline bool operator==(const String& a, const StringBuilder& b) {
+ return Equal(b, a);
+}
+inline bool operator!=(const String& a, const StringBuilder& b) {
+ return !Equal(b, a);
+}
+
+} // namespace WTF
+
+using WTF::StringBuilder;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_STRING_BUILDER_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/string_builder_test.cc b/chromium/third_party/blink/renderer/platform/wtf/text/string_builder_test.cc
new file mode 100644
index 00000000000..c3066c2e92e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/string_builder_test.cc
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ * Copyright (C) 2013 Apple 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 "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace WTF {
+
+namespace {
+
+void ExpectBuilderContent(const String& expected,
+ const StringBuilder& builder) {
+ // Not using builder.toString() because it changes internal state of builder.
+ if (builder.Is8Bit())
+ EXPECT_EQ(expected, String(builder.Characters8(), builder.length()));
+ else
+ EXPECT_EQ(expected, String(builder.Characters16(), builder.length()));
+}
+
+void ExpectEmpty(const StringBuilder& builder) {
+ EXPECT_EQ(0U, builder.length());
+ EXPECT_TRUE(builder.IsEmpty());
+ EXPECT_EQ(nullptr, builder.Characters8());
+}
+
+} // namespace
+
+TEST(StringBuilderTest, DefaultConstructor) {
+ StringBuilder builder;
+ ExpectEmpty(builder);
+}
+
+TEST(StringBuilderTest, Append) {
+ StringBuilder builder;
+ builder.Append(String("0123456789"));
+ ExpectBuilderContent("0123456789", builder);
+ builder.Append("abcd");
+ ExpectBuilderContent("0123456789abcd", builder);
+ builder.Append("efgh", 3);
+ ExpectBuilderContent("0123456789abcdefg", builder);
+ builder.Append("");
+ ExpectBuilderContent("0123456789abcdefg", builder);
+ builder.Append('#');
+ ExpectBuilderContent("0123456789abcdefg#", builder);
+
+ builder.ToString(); // Test after reifyString().
+ StringBuilder builder1;
+ builder.Append("", 0);
+ ExpectBuilderContent("0123456789abcdefg#", builder);
+ builder1.Append(builder.Characters8(), builder.length());
+ builder1.Append("XYZ");
+ builder.Append(builder1.Characters8(), builder1.length());
+ ExpectBuilderContent("0123456789abcdefg#0123456789abcdefg#XYZ", builder);
+
+ StringBuilder builder2;
+ builder2.ReserveCapacity(100);
+ builder2.Append("xyz");
+ const LChar* characters = builder2.Characters8();
+ builder2.Append("0123456789");
+ EXPECT_EQ(characters, builder2.Characters8());
+
+ StringBuilder builder3;
+ builder3.Append("xyz", 1, 2);
+ ExpectBuilderContent("yz", builder3);
+
+ StringBuilder builder4;
+ builder4.Append("abc", 5, 3);
+ ExpectEmpty(builder4);
+
+ StringBuilder builder5;
+ builder5.Append(StringView(StringView("def"), 1, 1));
+ ExpectBuilderContent("e", builder5);
+
+ // append() has special code paths for String backed StringView instead of
+ // just char* backed ones.
+ StringBuilder builder6;
+ builder6.Append(String("ghi"), 1, 2);
+ ExpectBuilderContent("hi", builder6);
+
+ // Test appending UChar32 characters to StringBuilder.
+ StringBuilder builder_for_u_char32_append;
+ UChar32 fraktur_a_char = 0x1D504;
+ // The fraktur A is not in the BMP, so it's two UTF-16 code units long.
+ builder_for_u_char32_append.Append(fraktur_a_char);
+ EXPECT_FALSE(builder_for_u_char32_append.Is8Bit());
+ EXPECT_EQ(2U, builder_for_u_char32_append.length());
+ builder_for_u_char32_append.Append(static_cast<UChar32>('A'));
+ EXPECT_EQ(3U, builder_for_u_char32_append.length());
+ const UChar result_array[] = {U16_LEAD(fraktur_a_char),
+ U16_TRAIL(fraktur_a_char), 'A'};
+ ExpectBuilderContent(String(result_array, WTF_ARRAY_LENGTH(result_array)),
+ builder_for_u_char32_append);
+}
+
+TEST(StringBuilderTest, AppendSharingImpl) {
+ String string("abc");
+ StringBuilder builder1;
+ builder1.Append(string);
+ EXPECT_EQ(string.Impl(), builder1.ToString().Impl());
+ EXPECT_EQ(string.Impl(), builder1.ToAtomicString().Impl());
+
+ StringBuilder builder2;
+ builder2.Append(string, 0, string.length());
+ EXPECT_EQ(string.Impl(), builder2.ToString().Impl());
+ EXPECT_EQ(string.Impl(), builder2.ToAtomicString().Impl());
+}
+
+TEST(StringBuilderTest, ToString) {
+ StringBuilder builder;
+ builder.Append("0123456789");
+ String string = builder.ToString();
+ EXPECT_EQ(String("0123456789"), string);
+ EXPECT_EQ(string.Impl(), builder.ToString().Impl());
+
+ // Changing the StringBuilder should not affect the original result of
+ // toString().
+ builder.Append("abcdefghijklmnopqrstuvwxyz");
+ EXPECT_EQ(String("0123456789"), string);
+
+ // Changing the StringBuilder should not affect the original result of
+ // toString() in case the capacity is not changed.
+ builder.ReserveCapacity(200);
+ string = builder.ToString();
+ EXPECT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyz"), string);
+ builder.Append("ABC");
+ EXPECT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyz"), string);
+
+ // Changing the original result of toString() should not affect the content of
+ // the StringBuilder.
+ String string1 = builder.ToString();
+ EXPECT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABC"), string1);
+ string1.append("DEF");
+ EXPECT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABC"),
+ builder.ToString());
+ EXPECT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABCDEF"), string1);
+
+ // Resizing the StringBuilder should not affect the original result of
+ // toString().
+ string1 = builder.ToString();
+ builder.Resize(10);
+ builder.Append("###");
+ EXPECT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABC"), string1);
+}
+
+TEST(StringBuilderTest, Clear) {
+ StringBuilder builder;
+ builder.Append("0123456789");
+ builder.Clear();
+ ExpectEmpty(builder);
+}
+
+TEST(StringBuilderTest, Array) {
+ StringBuilder builder;
+ builder.Append("0123456789");
+ EXPECT_EQ('0', static_cast<char>(builder[0]));
+ EXPECT_EQ('9', static_cast<char>(builder[9]));
+ builder.ToString(); // Test after reifyString().
+ EXPECT_EQ('0', static_cast<char>(builder[0]));
+ EXPECT_EQ('9', static_cast<char>(builder[9]));
+}
+
+TEST(StringBuilderTest, Resize) {
+ StringBuilder builder;
+ builder.Append("0123456789");
+ builder.Resize(10);
+ EXPECT_EQ(10U, builder.length());
+ ExpectBuilderContent("0123456789", builder);
+ builder.Resize(8);
+ EXPECT_EQ(8U, builder.length());
+ ExpectBuilderContent("01234567", builder);
+
+ builder.ToString();
+ builder.Resize(7);
+ EXPECT_EQ(7U, builder.length());
+ ExpectBuilderContent("0123456", builder);
+ builder.Resize(0);
+ ExpectEmpty(builder);
+}
+
+TEST(StringBuilderTest, Erase) {
+ StringBuilder builder;
+ builder.Append(String("01234"));
+ // Erase from String.
+ builder.erase(3);
+ ExpectBuilderContent("0124", builder);
+ // Erase from buffer.
+ builder.erase(1);
+ ExpectBuilderContent("024", builder);
+}
+
+TEST(StringBuilderTest, Erase16) {
+ StringBuilder builder;
+ builder.Append(String(u"\uFF10\uFF11\uFF12\uFF13\uFF14"));
+ // Erase from String.
+ builder.erase(3);
+ ExpectBuilderContent(u"\uFF10\uFF11\uFF12\uFF14", builder);
+ // Erase from buffer.
+ builder.erase(1);
+ ExpectBuilderContent(u"\uFF10\uFF12\uFF14", builder);
+}
+
+TEST(StringBuilderTest, EraseLast) {
+ StringBuilder builder;
+ builder.Append("01234");
+ builder.erase(4);
+ ExpectBuilderContent("0123", builder);
+}
+
+TEST(StringBuilderTest, Equal) {
+ StringBuilder builder1;
+ StringBuilder builder2;
+ EXPECT_TRUE(builder1 == builder2);
+ EXPECT_TRUE(Equal(builder1, static_cast<LChar*>(nullptr), 0));
+ EXPECT_TRUE(builder1 == String());
+ EXPECT_TRUE(String() == builder1);
+ EXPECT_TRUE(builder1 != String("abc"));
+
+ builder1.Append("123");
+ builder1.ReserveCapacity(32);
+ builder2.Append("123");
+ builder1.ReserveCapacity(64);
+ EXPECT_TRUE(builder1 == builder2);
+ EXPECT_TRUE(builder1 == String("123"));
+ EXPECT_TRUE(String("123") == builder1);
+
+ builder2.Append("456");
+ EXPECT_TRUE(builder1 != builder2);
+ EXPECT_TRUE(builder2 != builder1);
+ EXPECT_TRUE(String("123") != builder2);
+ EXPECT_TRUE(builder2 != String("123"));
+ builder2.ToString(); // Test after reifyString().
+ EXPECT_TRUE(builder1 != builder2);
+
+ builder2.Resize(3);
+ EXPECT_TRUE(builder1 == builder2);
+
+ builder1.ToString(); // Test after reifyString().
+ EXPECT_TRUE(builder1 == builder2);
+}
+
+TEST(StringBuilderTest, ToAtomicString) {
+ StringBuilder builder;
+ builder.Append("123");
+ AtomicString atomic_string = builder.ToAtomicString();
+ EXPECT_EQ(String("123"), atomic_string);
+
+ builder.ReserveCapacity(256);
+ for (int i = builder.length(); i < 128; i++)
+ builder.Append('x');
+ AtomicString atomic_string1 = builder.ToAtomicString();
+ EXPECT_EQ(128u, atomic_string1.length());
+ EXPECT_EQ('x', atomic_string1[127]);
+
+ // Later change of builder should not affect the atomic string.
+ for (int i = builder.length(); i < 256; i++)
+ builder.Append('x');
+ EXPECT_EQ(128u, atomic_string1.length());
+
+ String string = builder.ToString();
+ AtomicString atomic_string2 = builder.ToAtomicString();
+ // They should share the same StringImpl.
+ EXPECT_EQ(atomic_string2.Impl(), string.Impl());
+}
+
+TEST(StringBuilderTest, ToAtomicStringOnEmpty) {
+ { // Default constructed.
+ StringBuilder builder;
+ AtomicString atomic_string = builder.ToAtomicString();
+ EXPECT_EQ(g_empty_atom, atomic_string);
+ }
+ { // With capacity.
+ StringBuilder builder;
+ builder.ReserveCapacity(64);
+ AtomicString atomic_string = builder.ToAtomicString();
+ EXPECT_EQ(g_empty_atom, atomic_string);
+ }
+ { // AtomicString constructed from a null string.
+ StringBuilder builder;
+ builder.Append(String());
+ AtomicString atomic_string = builder.ToAtomicString();
+ EXPECT_EQ(g_empty_atom, atomic_string);
+ }
+ { // AtomicString constructed from an empty string.
+ StringBuilder builder;
+ builder.Append(g_empty_string);
+ AtomicString atomic_string = builder.ToAtomicString();
+ EXPECT_EQ(g_empty_atom, atomic_string);
+ }
+ { // AtomicString constructed from an empty StringBuilder.
+ StringBuilder builder;
+ StringBuilder empty_builder;
+ builder.Append(empty_builder);
+ AtomicString atomic_string = builder.ToAtomicString();
+ EXPECT_EQ(g_empty_atom, atomic_string);
+ }
+ { // AtomicString constructed from an empty char* string.
+ StringBuilder builder;
+ builder.Append("", 0);
+ AtomicString atomic_string = builder.ToAtomicString();
+ EXPECT_EQ(g_empty_atom, atomic_string);
+ }
+ { // Cleared StringBuilder.
+ StringBuilder builder;
+ builder.Append("WebKit");
+ builder.Clear();
+ AtomicString atomic_string = builder.ToAtomicString();
+ EXPECT_EQ(g_empty_atom, atomic_string);
+ }
+}
+
+TEST(StringBuilderTest, Substring) {
+ { // Default constructed.
+ StringBuilder builder;
+ String substring = builder.Substring(0, 10);
+ EXPECT_EQ(g_empty_string, substring);
+ }
+ { // With capacity.
+ StringBuilder builder;
+ builder.ReserveCapacity(64);
+ builder.Append("abc");
+ String substring = builder.Substring(2, 10);
+ EXPECT_EQ(String("c"), substring);
+ }
+}
+
+TEST(StringBuilderTest, AppendNumberDoubleUChar) {
+ const double kSomeNumber = 1.2345;
+ StringBuilder reference;
+ reference.Append(kReplacementCharacter); // Make it UTF-16.
+ reference.Append(String::Number(kSomeNumber));
+ StringBuilder test;
+ test.Append(kReplacementCharacter);
+ test.AppendNumber(kSomeNumber);
+ EXPECT_EQ(reference, test);
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/string_concatenate.cc b/chromium/third_party/blink/renderer/platform/wtf/text/string_concatenate.cc
new file mode 100644
index 00000000000..fee8323d744
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/string_concatenate.cc
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2014 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "third_party/blink/renderer/platform/wtf/text/string_concatenate.h"
+
+#include "third_party/blink/renderer/platform/wtf/text/string_impl.h"
+
+void WTF::StringTypeAdapter<char*>::WriteTo(LChar* destination) const {
+ for (unsigned i = 0; i < length_; ++i)
+ destination[i] = static_cast<LChar>(buffer_[i]);
+}
+
+void WTF::StringTypeAdapter<char*>::WriteTo(UChar* destination) const {
+ for (unsigned i = 0; i < length_; ++i) {
+ unsigned char c = buffer_[i];
+ destination[i] = c;
+ }
+}
+
+WTF::StringTypeAdapter<LChar*>::StringTypeAdapter(LChar* buffer)
+ : buffer_(buffer), length_(strlen(reinterpret_cast<char*>(buffer))) {}
+
+void WTF::StringTypeAdapter<LChar*>::WriteTo(LChar* destination) const {
+ memcpy(destination, buffer_, length_ * sizeof(LChar));
+}
+
+void WTF::StringTypeAdapter<LChar*>::WriteTo(UChar* destination) const {
+ StringImpl::CopyChars(destination, buffer_, length_);
+}
+
+WTF::StringTypeAdapter<const UChar*>::StringTypeAdapter(const UChar* buffer)
+ : buffer_(buffer), length_(LengthOfNullTerminatedString(buffer)) {}
+
+void WTF::StringTypeAdapter<const UChar*>::WriteTo(UChar* destination) const {
+ memcpy(destination, buffer_, length_ * sizeof(UChar));
+}
+
+WTF::StringTypeAdapter<const char*>::StringTypeAdapter(const char* buffer)
+ : buffer_(buffer), length_(strlen(buffer)) {}
+
+void WTF::StringTypeAdapter<const char*>::WriteTo(LChar* destination) const {
+ memcpy(destination, buffer_, static_cast<size_t>(length_) * sizeof(LChar));
+}
+
+void WTF::StringTypeAdapter<const char*>::WriteTo(UChar* destination) const {
+ for (unsigned i = 0; i < length_; ++i) {
+ unsigned char c = buffer_[i];
+ destination[i] = c;
+ }
+}
+
+WTF::StringTypeAdapter<const LChar*>::StringTypeAdapter(const LChar* buffer)
+ : buffer_(buffer), length_(strlen(reinterpret_cast<const char*>(buffer))) {}
+
+void WTF::StringTypeAdapter<const LChar*>::WriteTo(LChar* destination) const {
+ memcpy(destination, buffer_, static_cast<size_t>(length_) * sizeof(LChar));
+}
+
+void WTF::StringTypeAdapter<const LChar*>::WriteTo(UChar* destination) const {
+ StringImpl::CopyChars(destination, buffer_, length_);
+}
+
+void WTF::StringTypeAdapter<StringView>::WriteTo(LChar* destination) const {
+ DCHECK(Is8Bit());
+ StringImpl::CopyChars(destination, view_.Characters8(), view_.length());
+}
+
+void WTF::StringTypeAdapter<StringView>::WriteTo(UChar* destination) const {
+ if (Is8Bit())
+ StringImpl::CopyChars(destination, view_.Characters8(), view_.length());
+ else
+ StringImpl::CopyChars(destination, view_.Characters16(), view_.length());
+}
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/string_concatenate.h b/chromium/third_party/blink/renderer/platform/wtf/text/string_concatenate.h
new file mode 100644
index 00000000000..94ec2410355
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/string_concatenate.h
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2010 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_STRING_CONCATENATE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_STRING_CONCATENATE_H_
+
+#include <string.h>
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+#ifndef WTFString_h
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#endif
+
+namespace WTF {
+
+template <typename StringType>
+class StringTypeAdapter {
+ DISALLOW_NEW();
+};
+
+template <>
+class StringTypeAdapter<char> {
+ DISALLOW_NEW();
+
+ public:
+ explicit StringTypeAdapter<char>(char buffer) : buffer_(buffer) {}
+
+ unsigned length() const { return 1; }
+ bool Is8Bit() const { return true; }
+
+ void WriteTo(LChar* destination) const { *destination = buffer_; }
+ void WriteTo(UChar* destination) const { *destination = buffer_; }
+
+ private:
+ const unsigned char buffer_;
+};
+
+template <>
+class StringTypeAdapter<LChar> {
+ DISALLOW_NEW();
+
+ public:
+ explicit StringTypeAdapter<LChar>(LChar buffer) : buffer_(buffer) {}
+
+ unsigned length() const { return 1; }
+ bool Is8Bit() const { return true; }
+
+ void WriteTo(LChar* destination) const { *destination = buffer_; }
+ void WriteTo(UChar* destination) const { *destination = buffer_; }
+
+ private:
+ const LChar buffer_;
+};
+
+template <>
+class StringTypeAdapter<UChar> {
+ DISALLOW_NEW();
+
+ public:
+ explicit StringTypeAdapter<UChar>(UChar buffer) : buffer_(buffer) {}
+
+ unsigned length() const { return 1; }
+ bool Is8Bit() const { return buffer_ <= 0xff; }
+
+ void WriteTo(LChar* destination) const {
+ DCHECK(Is8Bit());
+ *destination = static_cast<LChar>(buffer_);
+ }
+
+ void WriteTo(UChar* destination) const { *destination = buffer_; }
+
+ private:
+ const UChar buffer_;
+};
+
+template <>
+class WTF_EXPORT StringTypeAdapter<char*> {
+ DISALLOW_NEW();
+
+ public:
+ explicit StringTypeAdapter<char*>(char* buffer)
+ : buffer_(buffer), length_(strlen(buffer)) {}
+
+ unsigned length() const { return length_; }
+ bool Is8Bit() const { return true; }
+
+ void WriteTo(LChar* destination) const;
+ void WriteTo(UChar* destination) const;
+
+ private:
+ const char* buffer_;
+ unsigned length_;
+};
+
+template <>
+class WTF_EXPORT StringTypeAdapter<LChar*> {
+ DISALLOW_NEW();
+
+ public:
+ explicit StringTypeAdapter<LChar*>(LChar* buffer);
+
+ unsigned length() const { return length_; }
+ bool Is8Bit() const { return true; }
+
+ void WriteTo(LChar* destination) const;
+ void WriteTo(UChar* destination) const;
+
+ private:
+ const LChar* buffer_;
+ const unsigned length_;
+};
+
+template <>
+class WTF_EXPORT StringTypeAdapter<const UChar*> {
+ DISALLOW_NEW();
+
+ public:
+ explicit StringTypeAdapter(const UChar* buffer);
+
+ unsigned length() const { return length_; }
+ bool Is8Bit() const { return false; }
+
+ void WriteTo(LChar*) const { CHECK(false); }
+ void WriteTo(UChar* destination) const;
+
+ private:
+ const UChar* buffer_;
+ const unsigned length_;
+};
+
+template <>
+class WTF_EXPORT StringTypeAdapter<const char*> {
+ DISALLOW_NEW();
+
+ public:
+ explicit StringTypeAdapter<const char*>(const char* buffer);
+
+ unsigned length() const { return length_; }
+ bool Is8Bit() const { return true; }
+
+ void WriteTo(LChar* destination) const;
+ void WriteTo(UChar* destination) const;
+
+ private:
+ const char* buffer_;
+ const unsigned length_;
+};
+
+template <>
+class WTF_EXPORT StringTypeAdapter<const LChar*> {
+ DISALLOW_NEW();
+
+ public:
+ explicit StringTypeAdapter<const LChar*>(const LChar* buffer);
+
+ unsigned length() const { return length_; }
+ bool Is8Bit() const { return true; }
+
+ void WriteTo(LChar* destination) const;
+ void WriteTo(UChar* destination) const;
+
+ private:
+ const LChar* buffer_;
+ const unsigned length_;
+};
+
+template <>
+class WTF_EXPORT StringTypeAdapter<StringView> {
+ DISALLOW_NEW();
+
+ public:
+ explicit StringTypeAdapter(const StringView& view) : view_(view) {}
+
+ unsigned length() const { return view_.length(); }
+ bool Is8Bit() const { return view_.Is8Bit(); }
+
+ void WriteTo(LChar* destination) const;
+ void WriteTo(UChar* destination) const;
+
+ private:
+ const StringView view_;
+};
+
+template <>
+class StringTypeAdapter<String> : public StringTypeAdapter<StringView> {
+ public:
+ explicit StringTypeAdapter(const String& string)
+ : StringTypeAdapter<StringView>(string) {}
+};
+
+template <>
+class StringTypeAdapter<AtomicString> : public StringTypeAdapter<StringView> {
+ public:
+ explicit StringTypeAdapter(const AtomicString& string)
+ : StringTypeAdapter<StringView>(string) {}
+};
+
+} // namespace WTF
+
+#include "third_party/blink/renderer/platform/wtf/text/string_operators.h"
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/string_hash.h b/chromium/third_party/blink/renderer/platform/wtf/text/string_hash.h
new file mode 100644
index 00000000000..170451c724b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/string_hash.h
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2006, 2007, 2008, 2012, 2013 Apple Inc. All rights reserved
+ * Copyright (C) Research In Motion Limited 2009. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_STRING_HASH_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_STRING_HASH_H_
+
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/hash_traits.h"
+#include "third_party/blink/renderer/platform/wtf/string_hasher.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+
+namespace WTF {
+
+inline bool HashTraits<String>::IsEmptyValue(const String& value) {
+ return value.IsNull();
+}
+
+inline bool HashTraits<String>::IsDeletedValue(const String& value) {
+ return HashTraits<scoped_refptr<StringImpl>>::IsDeletedValue(value.impl_);
+}
+
+inline void HashTraits<String>::ConstructDeletedValue(String& slot,
+ bool zero_value) {
+ HashTraits<scoped_refptr<StringImpl>>::ConstructDeletedValue(slot.impl_,
+ zero_value);
+}
+
+// The hash() functions on StringHash and CaseFoldingHash do not support null
+// strings. get(), contains(), and add() on HashMap<String,..., StringHash>
+// cause a null-pointer dereference when passed null strings.
+
+// FIXME: We should really figure out a way to put the computeHash function
+// that's currently a member function of StringImpl into this file so we can be
+// a little closer to having all the nearly-identical hash functions in one
+// place.
+
+struct StringHash {
+ STATIC_ONLY(StringHash);
+ static unsigned GetHash(StringImpl* key) { return key->GetHash(); }
+ static inline bool Equal(const StringImpl* a, const StringImpl* b) {
+ return EqualNonNull(a, b);
+ }
+
+ static unsigned GetHash(const scoped_refptr<StringImpl>& key) {
+ return key->GetHash();
+ }
+ static bool Equal(const scoped_refptr<StringImpl>& a,
+ const scoped_refptr<StringImpl>& b) {
+ return Equal(a.get(), b.get());
+ }
+
+ static unsigned GetHash(const String& key) { return key.Impl()->GetHash(); }
+ static bool Equal(const String& a, const String& b) {
+ return Equal(a.Impl(), b.Impl());
+ }
+
+ static const bool safe_to_compare_to_empty_or_deleted = false;
+};
+
+class CaseFoldingHash {
+ STATIC_ONLY(CaseFoldingHash);
+
+ public:
+ static unsigned GetHash(const UChar* data, unsigned length) {
+ return StringHasher::ComputeHashAndMaskTop8Bits<UChar, FoldCase<UChar>>(
+ data, length);
+ }
+
+ static unsigned GetHash(StringImpl* str) {
+ if (str->Is8Bit())
+ return GetHash(str->Characters8(), str->length());
+ return GetHash(str->Characters16(), str->length());
+ }
+
+ static unsigned GetHash(const LChar* data, unsigned length) {
+ return StringHasher::ComputeHashAndMaskTop8Bits<LChar, FoldCase<LChar>>(
+ data, length);
+ }
+
+ static inline unsigned GetHash(const char* data, unsigned length) {
+ return CaseFoldingHash::GetHash(reinterpret_cast<const LChar*>(data),
+ length);
+ }
+
+ static inline unsigned GetHash(const char* data) {
+ return CaseFoldingHash::GetHash(reinterpret_cast<const LChar*>(data),
+ strlen(data));
+ }
+
+ static inline bool Equal(const StringImpl* a, const StringImpl* b) {
+ DCHECK(a);
+ DCHECK(b);
+ // Save one branch inside each StringView by derefing the StringImpl,
+ // and another branch inside the compare function by skipping the null
+ // checks.
+ return DeprecatedEqualIgnoringCaseAndNullity(*a, *b);
+ }
+
+ static unsigned GetHash(const scoped_refptr<StringImpl>& key) {
+ return GetHash(key.get());
+ }
+
+ static bool Equal(const scoped_refptr<StringImpl>& a,
+ const scoped_refptr<StringImpl>& b) {
+ return Equal(a.get(), b.get());
+ }
+
+ static unsigned GetHash(const String& key) { return GetHash(key.Impl()); }
+ static unsigned GetHash(const AtomicString& key) {
+ return GetHash(key.Impl());
+ }
+ static bool Equal(const String& a, const String& b) {
+ return Equal(a.Impl(), b.Impl());
+ }
+ static bool Equal(const AtomicString& a, const AtomicString& b) {
+ return (a == b) || Equal(a.Impl(), b.Impl());
+ }
+
+ static const bool safe_to_compare_to_empty_or_deleted = false;
+
+ private:
+ // Private so no one uses this in the belief that it will return the
+ // correctly-folded code point in all cases (see comment below).
+ template <typename T>
+ static inline UChar FoldCase(T ch) {
+ if (std::is_same<T, LChar>::value)
+ return StringImpl::kLatin1CaseFoldTable[ch];
+ // It's possible for WTF::Unicode::foldCase() to return a 32-bit value
+ // that's not representable as a UChar. However, since this is rare and
+ // deterministic, and the result of this is merely used for hashing, go
+ // ahead and clamp the value.
+ return static_cast<UChar>(WTF::Unicode::FoldCase(ch));
+ }
+};
+
+// This hash can be used in cases where the key is a hash of a string, but we
+// don't want to store the string. It's not really specific to string hashing,
+// but all our current uses of it are for strings.
+struct AlreadyHashed : IntHash<unsigned> {
+ STATIC_ONLY(AlreadyHashed);
+ static unsigned GetHash(unsigned key) { return key; }
+
+ // To use a hash value as a key for a hash table, we need to eliminate the
+ // "deleted" value, which is negative one. That could be done by changing
+ // the string hash function to never generate negative one, but this works
+ // and is still relatively efficient.
+ static unsigned AvoidDeletedValue(unsigned hash) {
+ DCHECK(hash);
+ unsigned new_hash = hash | (!(hash + 1) << 31);
+ DCHECK(new_hash);
+ DCHECK_NE(new_hash, 0xFFFFFFFF);
+ return new_hash;
+ }
+};
+
+} // namespace WTF
+
+using WTF::AlreadyHashed;
+using WTF::CaseFoldingHash;
+using WTF::StringHash;
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/string_impl.cc b/chromium/third_party/blink/renderer/platform/wtf/text/string_impl.cc
new file mode 100644
index 00000000000..ec6efec57ab
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/string_impl.cc
@@ -0,0 +1,2005 @@
+/*
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * (C) 2001 Dirk Mueller ( mueller@kde.org )
+ * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2013 Apple Inc. All
+ * rights reserved.
+ * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "third_party/blink/renderer/platform/wtf/text/string_impl.h"
+
+#include <algorithm>
+#include <memory>
+#include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
+#include "third_party/blink/renderer/platform/wtf/dynamic_annotations.h"
+#include "third_party/blink/renderer/platform/wtf/leak_annotations.h"
+#include "third_party/blink/renderer/platform/wtf/static_constructors.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string_table.h"
+#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_buffer.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_to_number.h"
+
+using std::numeric_limits;
+
+namespace WTF {
+
+// As of Jan 2017, StringImpl needs 2 * sizeof(int) + 29 bits of data, and
+// sizeof(ThreadRestrictionVerifier) is 16 bytes. Thus, in DCHECK mode the
+// class may be padded to 32 bytes.
+#if DCHECK_IS_ON()
+static_assert(sizeof(StringImpl) <= 8 * sizeof(int),
+ "StringImpl should stay small");
+#else
+static_assert(sizeof(StringImpl) <= 3 * sizeof(int),
+ "StringImpl should stay small");
+#endif
+
+void* StringImpl::operator new(size_t size) {
+ DCHECK_EQ(size, sizeof(StringImpl));
+ return Partitions::BufferMalloc(size, "WTF::StringImpl");
+}
+
+void StringImpl::operator delete(void* ptr) {
+ Partitions::BufferFree(ptr);
+}
+
+inline StringImpl::~StringImpl() {
+ DCHECK(!IsStatic());
+
+ if (IsAtomic())
+ AtomicStringTable::Instance().Remove(this);
+}
+
+void StringImpl::DestroyIfNotStatic() const {
+ if (!IsStatic())
+ delete this;
+}
+
+void StringImpl::UpdateContainsOnlyASCII() const {
+ contains_only_ascii_ = Is8Bit()
+ ? CharactersAreAllASCII(Characters8(), length())
+ : CharactersAreAllASCII(Characters16(), length());
+ needs_ascii_check_ = false;
+}
+
+bool StringImpl::IsSafeToSendToAnotherThread() const {
+ if (IsStatic())
+ return true;
+ // AtomicStrings are not safe to send between threads as ~StringImpl()
+ // will try to remove them from the wrong AtomicStringTable.
+ if (IsAtomic())
+ return false;
+ if (HasOneRef())
+ return true;
+ return false;
+}
+
+#if DCHECK_IS_ON()
+std::string StringImpl::AsciiForDebugging() const {
+ CString ascii = String(IsolatedCopy()->Substring(0, 128)).Ascii();
+ return std::string(ascii.data(), ascii.length());
+}
+#endif
+
+scoped_refptr<StringImpl> StringImpl::CreateUninitialized(unsigned length,
+ LChar*& data) {
+ if (!length) {
+ data = nullptr;
+ return empty_;
+ }
+
+ // Allocate a single buffer large enough to contain the StringImpl
+ // struct as well as the data which it contains. This removes one
+ // heap allocation from this call.
+ StringImpl* string = static_cast<StringImpl*>(Partitions::BufferMalloc(
+ AllocationSize<LChar>(length), "WTF::StringImpl"));
+
+ data = reinterpret_cast<LChar*>(string + 1);
+ return base::AdoptRef(new (string) StringImpl(length, kForce8BitConstructor));
+}
+
+scoped_refptr<StringImpl> StringImpl::CreateUninitialized(unsigned length,
+ UChar*& data) {
+ if (!length) {
+ data = nullptr;
+ return empty_;
+ }
+
+ // Allocate a single buffer large enough to contain the StringImpl
+ // struct as well as the data which it contains. This removes one
+ // heap allocation from this call.
+ StringImpl* string = static_cast<StringImpl*>(Partitions::BufferMalloc(
+ AllocationSize<UChar>(length), "WTF::StringImpl"));
+
+ data = reinterpret_cast<UChar*>(string + 1);
+ return base::AdoptRef(new (string) StringImpl(length));
+}
+
+static StaticStringsTable& StaticStrings() {
+ DEFINE_STATIC_LOCAL(StaticStringsTable, static_strings, ());
+ return static_strings;
+}
+
+#if DCHECK_IS_ON()
+static bool g_allow_creation_of_static_strings = true;
+#endif
+
+const StaticStringsTable& StringImpl::AllStaticStrings() {
+ return StaticStrings();
+}
+
+void StringImpl::FreezeStaticStrings() {
+ DCHECK(IsMainThread());
+
+#if DCHECK_IS_ON()
+ g_allow_creation_of_static_strings = false;
+#endif
+}
+
+unsigned StringImpl::highest_static_string_length_ = 0;
+
+DEFINE_GLOBAL(StringImpl, g_global_empty);
+DEFINE_GLOBAL(StringImpl, g_global_empty16_bit);
+// Callers need the global empty strings to be non-const.
+StringImpl* StringImpl::empty_ = const_cast<StringImpl*>(&g_global_empty);
+StringImpl* StringImpl::empty16_bit_ =
+ const_cast<StringImpl*>(&g_global_empty16_bit);
+void StringImpl::InitStatics() {
+ new ((void*)empty_) StringImpl(kConstructEmptyString);
+ new ((void*)empty16_bit_) StringImpl(kConstructEmptyString16Bit);
+ WTF_ANNOTATE_BENIGN_RACE(StringImpl::empty_,
+ "Benign race on the reference counter of a static "
+ "string created by StringImpl::empty");
+ WTF_ANNOTATE_BENIGN_RACE(StringImpl::empty16_bit_,
+ "Benign race on the reference counter of a static "
+ "string created by StringImpl::empty16Bit");
+}
+
+StringImpl* StringImpl::CreateStatic(const char* string,
+ unsigned length,
+ unsigned hash) {
+#if DCHECK_IS_ON()
+ DCHECK(g_allow_creation_of_static_strings);
+#endif
+ DCHECK(string);
+ DCHECK(length);
+
+ StaticStringsTable::const_iterator it = StaticStrings().find(hash);
+ if (it != StaticStrings().end()) {
+ DCHECK(!memcmp(string, it->value + 1, length * sizeof(LChar)));
+ return it->value;
+ }
+
+ // Allocate a single buffer large enough to contain the StringImpl
+ // struct as well as the data which it contains. This removes one
+ // heap allocation from this call.
+ CHECK_LE(length,
+ ((std::numeric_limits<unsigned>::max() - sizeof(StringImpl)) /
+ sizeof(LChar)));
+ size_t size = sizeof(StringImpl) + length * sizeof(LChar);
+
+ WTF_INTERNAL_LEAK_SANITIZER_DISABLED_SCOPE;
+ StringImpl* impl = static_cast<StringImpl*>(
+ Partitions::BufferMalloc(size, "WTF::StringImpl"));
+
+ LChar* data = reinterpret_cast<LChar*>(impl + 1);
+ impl = new (impl) StringImpl(length, hash, kStaticString);
+ memcpy(data, string, length * sizeof(LChar));
+#if DCHECK_IS_ON()
+ impl->AssertHashIsCorrect();
+#endif
+
+ DCHECK(IsMainThread());
+ highest_static_string_length_ =
+ std::max(highest_static_string_length_, length);
+ StaticStrings().insert(hash, impl);
+ WTF_ANNOTATE_BENIGN_RACE(impl,
+ "Benign race on the reference counter of a static "
+ "string created by StringImpl::createStatic");
+
+ return impl;
+}
+
+void StringImpl::ReserveStaticStringsCapacityForSize(unsigned size) {
+#if DCHECK_IS_ON()
+ DCHECK(g_allow_creation_of_static_strings);
+#endif
+ StaticStrings().ReserveCapacityForSize(size);
+}
+
+scoped_refptr<StringImpl> StringImpl::Create(const UChar* characters,
+ unsigned length) {
+ if (!characters || !length)
+ return empty_;
+
+ UChar* data;
+ scoped_refptr<StringImpl> string = CreateUninitialized(length, data);
+ memcpy(data, characters, length * sizeof(UChar));
+ return string;
+}
+
+scoped_refptr<StringImpl> StringImpl::Create(const LChar* characters,
+ unsigned length) {
+ if (!characters || !length)
+ return empty_;
+
+ LChar* data;
+ scoped_refptr<StringImpl> string = CreateUninitialized(length, data);
+ memcpy(data, characters, length * sizeof(LChar));
+ return string;
+}
+
+scoped_refptr<StringImpl> StringImpl::Create8BitIfPossible(
+ const UChar* characters,
+ unsigned length) {
+ if (!characters || !length)
+ return empty_;
+
+ LChar* data;
+ scoped_refptr<StringImpl> string = CreateUninitialized(length, data);
+
+ for (size_t i = 0; i < length; ++i) {
+ if (characters[i] & 0xff00)
+ return Create(characters, length);
+ data[i] = static_cast<LChar>(characters[i]);
+ }
+
+ return string;
+}
+
+scoped_refptr<StringImpl> StringImpl::Create(const LChar* string) {
+ if (!string)
+ return empty_;
+ size_t length = strlen(reinterpret_cast<const char*>(string));
+ CHECK_LE(length, numeric_limits<unsigned>::max());
+ return Create(string, length);
+}
+
+bool StringImpl::ContainsOnlyWhitespace() {
+ // FIXME: The definition of whitespace here includes a number of characters
+ // that are not whitespace from the point of view of LayoutText; I wonder if
+ // that's a problem in practice.
+ if (Is8Bit()) {
+ for (unsigned i = 0; i < length_; ++i) {
+ UChar c = Characters8()[i];
+ if (!IsASCIISpace(c))
+ return false;
+ }
+
+ return true;
+ }
+
+ for (unsigned i = 0; i < length_; ++i) {
+ UChar c = Characters16()[i];
+ if (!IsASCIISpace(c))
+ return false;
+ }
+ return true;
+}
+
+scoped_refptr<StringImpl> StringImpl::Substring(unsigned start,
+ unsigned length) const {
+ if (start >= length_)
+ return empty_;
+ unsigned max_length = length_ - start;
+ if (length >= max_length) {
+ // RefPtr has trouble dealing with const arguments. It should be updated
+ // so this const_cast is not necessary.
+ if (!start)
+ return const_cast<StringImpl*>(this);
+ length = max_length;
+ }
+ if (Is8Bit())
+ return Create(Characters8() + start, length);
+
+ return Create(Characters16() + start, length);
+}
+
+UChar32 StringImpl::CharacterStartingAt(unsigned i) {
+ if (Is8Bit())
+ return Characters8()[i];
+ if (U16_IS_SINGLE(Characters16()[i]))
+ return Characters16()[i];
+ if (i + 1 < length_ && U16_IS_LEAD(Characters16()[i]) &&
+ U16_IS_TRAIL(Characters16()[i + 1]))
+ return U16_GET_SUPPLEMENTARY(Characters16()[i], Characters16()[i + 1]);
+ return 0;
+}
+
+unsigned StringImpl::CopyTo(UChar* buffer,
+ unsigned start,
+ unsigned max_length) const {
+ unsigned number_of_characters_to_copy =
+ std::min(length() - start, max_length);
+ if (!number_of_characters_to_copy)
+ return 0;
+ if (Is8Bit())
+ CopyChars(buffer, Characters8() + start, number_of_characters_to_copy);
+ else
+ CopyChars(buffer, Characters16() + start, number_of_characters_to_copy);
+ return number_of_characters_to_copy;
+}
+
+scoped_refptr<StringImpl> StringImpl::LowerASCII() {
+ // First scan the string for uppercase and non-ASCII characters:
+ if (Is8Bit()) {
+ unsigned first_index_to_be_lowered = length_;
+ for (unsigned i = 0; i < length_; ++i) {
+ LChar ch = Characters8()[i];
+ if (IsASCIIUpper(ch)) {
+ first_index_to_be_lowered = i;
+ break;
+ }
+ }
+
+ // Nothing to do if the string is all ASCII with no uppercase.
+ if (first_index_to_be_lowered == length_) {
+ return this;
+ }
+
+ LChar* data8;
+ scoped_refptr<StringImpl> new_impl = CreateUninitialized(length_, data8);
+ memcpy(data8, Characters8(), first_index_to_be_lowered);
+
+ for (unsigned i = first_index_to_be_lowered; i < length_; ++i) {
+ LChar ch = Characters8()[i];
+ data8[i] = IsASCIIUpper(ch) ? ToASCIILower(ch) : ch;
+ }
+ return new_impl;
+ }
+ bool no_upper = true;
+ UChar ored = 0;
+
+ const UChar* end = Characters16() + length_;
+ for (const UChar* chp = Characters16(); chp != end; ++chp) {
+ if (IsASCIIUpper(*chp))
+ no_upper = false;
+ ored |= *chp;
+ }
+ // Nothing to do if the string is all ASCII with no uppercase.
+ if (no_upper && !(ored & ~0x7F))
+ return this;
+
+ CHECK_LE(length_, static_cast<unsigned>(numeric_limits<unsigned>::max()));
+ unsigned length = length_;
+
+ UChar* data16;
+ scoped_refptr<StringImpl> new_impl = CreateUninitialized(length_, data16);
+
+ for (unsigned i = 0; i < length; ++i) {
+ UChar c = Characters16()[i];
+ data16[i] = IsASCIIUpper(c) ? ToASCIILower(c) : c;
+ }
+ return new_impl;
+}
+
+scoped_refptr<StringImpl> StringImpl::LowerUnicode() {
+ // Note: This is a hot function in the Dromaeo benchmark, specifically the
+ // no-op code path up through the first 'return' statement.
+
+ // First scan the string for uppercase and non-ASCII characters:
+ if (Is8Bit()) {
+ unsigned first_index_to_be_lowered = length_;
+ for (unsigned i = 0; i < length_; ++i) {
+ LChar ch = Characters8()[i];
+ if (UNLIKELY(IsASCIIUpper(ch) || ch & ~0x7F)) {
+ first_index_to_be_lowered = i;
+ break;
+ }
+ }
+
+ // Nothing to do if the string is all ASCII with no uppercase.
+ if (first_index_to_be_lowered == length_)
+ return this;
+
+ LChar* data8;
+ scoped_refptr<StringImpl> new_impl = CreateUninitialized(length_, data8);
+ memcpy(data8, Characters8(), first_index_to_be_lowered);
+
+ for (unsigned i = first_index_to_be_lowered; i < length_; ++i) {
+ LChar ch = Characters8()[i];
+ data8[i] = UNLIKELY(ch & ~0x7F) ? static_cast<LChar>(Unicode::ToLower(ch))
+ : ToASCIILower(ch);
+ }
+
+ return new_impl;
+ }
+
+ bool no_upper = true;
+ UChar ored = 0;
+
+ const UChar* end = Characters16() + length_;
+ for (const UChar* chp = Characters16(); chp != end; ++chp) {
+ if (UNLIKELY(IsASCIIUpper(*chp)))
+ no_upper = false;
+ ored |= *chp;
+ }
+ // Nothing to do if the string is all ASCII with no uppercase.
+ if (no_upper && !(ored & ~0x7F))
+ return this;
+
+ CHECK_LE(length_, static_cast<unsigned>(numeric_limits<int32_t>::max()));
+ int32_t length = length_;
+
+ if (!(ored & ~0x7F)) {
+ UChar* data16;
+ scoped_refptr<StringImpl> new_impl = CreateUninitialized(length_, data16);
+
+ for (int32_t i = 0; i < length; ++i) {
+ UChar c = Characters16()[i];
+ data16[i] = ToASCIILower(c);
+ }
+ return new_impl;
+ }
+
+ // Do a slower implementation for cases that include non-ASCII characters.
+ UChar* data16;
+ scoped_refptr<StringImpl> new_impl = CreateUninitialized(length_, data16);
+
+ bool error;
+ int32_t real_length =
+ Unicode::ToLower(data16, length, Characters16(), length_, &error);
+ if (!error && real_length == length)
+ return new_impl;
+
+ new_impl = CreateUninitialized(real_length, data16);
+ Unicode::ToLower(data16, real_length, Characters16(), length_, &error);
+ if (error)
+ return this;
+ return new_impl;
+}
+
+scoped_refptr<StringImpl> StringImpl::UpperUnicode() {
+ // This function could be optimized for no-op cases the way LowerUnicode() is,
+ // but in empirical testing, few actual calls to UpperUnicode() are no-ops, so
+ // it wouldn't be worth the extra time for pre-scanning.
+
+ CHECK_LE(length_, static_cast<unsigned>(numeric_limits<int32_t>::max()));
+ int32_t length = length_;
+
+ if (Is8Bit()) {
+ LChar* data8;
+ scoped_refptr<StringImpl> new_impl = CreateUninitialized(length_, data8);
+
+ // Do a faster loop for the case where all the characters are ASCII.
+ LChar ored = 0;
+ for (int i = 0; i < length; ++i) {
+ LChar c = Characters8()[i];
+ ored |= c;
+ data8[i] = ToASCIIUpper(c);
+ }
+ if (!(ored & ~0x7F))
+ return new_impl;
+
+ // Do a slower implementation for cases that include non-ASCII Latin-1
+ // characters.
+ int number_sharp_s_characters = 0;
+
+ // There are two special cases.
+ // 1. latin-1 characters when converted to upper case are 16 bit
+ // characters.
+ // 2. Lower case sharp-S converts to "SS" (two characters)
+ for (int32_t i = 0; i < length; ++i) {
+ LChar c = Characters8()[i];
+ if (UNLIKELY(c == kSmallLetterSharpSCharacter))
+ ++number_sharp_s_characters;
+ UChar upper = static_cast<UChar>(Unicode::ToUpper(c));
+ if (UNLIKELY(upper > 0xff)) {
+ // Since this upper-cased character does not fit in an 8-bit string, we
+ // need to take the 16-bit path.
+ goto upconvert;
+ }
+ data8[i] = static_cast<LChar>(upper);
+ }
+
+ if (!number_sharp_s_characters)
+ return new_impl;
+
+ // We have numberSSCharacters sharp-s characters, but none of the other
+ // special characters.
+ new_impl = CreateUninitialized(length_ + number_sharp_s_characters, data8);
+
+ LChar* dest = data8;
+
+ for (int32_t i = 0; i < length; ++i) {
+ LChar c = Characters8()[i];
+ if (c == kSmallLetterSharpSCharacter) {
+ *dest++ = 'S';
+ *dest++ = 'S';
+ } else {
+ *dest++ = static_cast<LChar>(Unicode::ToUpper(c));
+ }
+ }
+
+ return new_impl;
+ }
+
+upconvert:
+ scoped_refptr<StringImpl> upconverted = UpconvertedString();
+ const UChar* source16 = upconverted->Characters16();
+
+ UChar* data16;
+ scoped_refptr<StringImpl> new_impl = CreateUninitialized(length_, data16);
+
+ // Do a faster loop for the case where all the characters are ASCII.
+ UChar ored = 0;
+ for (int i = 0; i < length; ++i) {
+ UChar c = source16[i];
+ ored |= c;
+ data16[i] = ToASCIIUpper(c);
+ }
+ if (!(ored & ~0x7F))
+ return new_impl;
+
+ // Do a slower implementation for cases that include non-ASCII characters.
+ bool error;
+ int32_t real_length =
+ Unicode::ToUpper(data16, length, source16, length_, &error);
+ if (!error && real_length == length)
+ return new_impl;
+ new_impl = CreateUninitialized(real_length, data16);
+ Unicode::ToUpper(data16, real_length, source16, length_, &error);
+ if (error)
+ return this;
+ return new_impl;
+}
+
+scoped_refptr<StringImpl> StringImpl::UpperASCII() {
+ if (Is8Bit()) {
+ LChar* data8;
+ scoped_refptr<StringImpl> new_impl = CreateUninitialized(length_, data8);
+
+ for (unsigned i = 0; i < length_; ++i) {
+ LChar c = Characters8()[i];
+ data8[i] = IsASCIILower(c) ? ToASCIIUpper(c) : c;
+ }
+ return new_impl;
+ }
+
+ UChar* data16;
+ scoped_refptr<StringImpl> new_impl = CreateUninitialized(length_, data16);
+
+ for (unsigned i = 0; i < length_; ++i) {
+ UChar c = Characters16()[i];
+ data16[i] = IsASCIILower(c) ? ToASCIIUpper(c) : c;
+ }
+ return new_impl;
+}
+
+static inline bool LocaleIdMatchesLang(const AtomicString& locale_id,
+ const StringView& lang) {
+ CHECK_GE(lang.length(), 2u);
+ CHECK_LE(lang.length(), 3u);
+ if (!locale_id.Impl() || !locale_id.Impl()->StartsWithIgnoringCase(lang))
+ return false;
+ if (locale_id.Impl()->length() == lang.length())
+ return true;
+ const UChar maybe_delimiter = (*locale_id.Impl())[lang.length()];
+ return maybe_delimiter == '-' || maybe_delimiter == '_' ||
+ maybe_delimiter == '@';
+}
+
+typedef int32_t (*icuCaseConverter)(UChar*,
+ int32_t,
+ const UChar*,
+ int32_t,
+ const char*,
+ UErrorCode*);
+
+static scoped_refptr<StringImpl> CaseConvert(const UChar* source16,
+ size_t length,
+ icuCaseConverter converter,
+ const char* locale,
+ StringImpl* original_string) {
+ UChar* data16;
+ size_t target_length = length;
+ scoped_refptr<StringImpl> output =
+ StringImpl::CreateUninitialized(length, data16);
+ do {
+ UErrorCode status = U_ZERO_ERROR;
+ target_length =
+ converter(data16, target_length, source16, length, locale, &status);
+ if (U_SUCCESS(status)) {
+ if (length > 0)
+ return output->Substring(0, target_length);
+ return output;
+ }
+ if (status != U_BUFFER_OVERFLOW_ERROR)
+ return original_string;
+ // Expand the buffer.
+ output = StringImpl::CreateUninitialized(target_length, data16);
+ } while (true);
+}
+
+scoped_refptr<StringImpl> StringImpl::LowerUnicode(
+ const AtomicString& locale_identifier) {
+ // Use the more optimized code path most of the time.
+ // Only Turkic (tr and az) languages and Lithuanian requires
+ // locale-specific lowercasing rules. Even though CLDR has el-Lower,
+ // it's identical to the locale-agnostic lowercasing. Context-dependent
+ // handling of Greek capital sigma is built into the common lowercasing
+ // function in ICU.
+ const char* locale_for_conversion = nullptr;
+ if (LocaleIdMatchesLang(locale_identifier, "tr") ||
+ LocaleIdMatchesLang(locale_identifier, "az"))
+ locale_for_conversion = "tr";
+ else if (LocaleIdMatchesLang(locale_identifier, "lt"))
+ locale_for_conversion = "lt";
+ else
+ return LowerUnicode();
+
+ CHECK_LE(length_, static_cast<unsigned>(numeric_limits<int32_t>::max()));
+ int length = length_;
+
+ scoped_refptr<StringImpl> upconverted = UpconvertedString();
+ const UChar* source16 = upconverted->Characters16();
+ return CaseConvert(source16, length, u_strToLower, locale_for_conversion,
+ this);
+}
+
+scoped_refptr<StringImpl> StringImpl::UpperUnicode(
+ const AtomicString& locale_identifier) {
+ // Use the more-optimized code path most of the time.
+ // Only Turkic (tr and az) languages, Greek and Lithuanian require
+ // locale-specific uppercasing rules.
+ const char* locale_for_conversion = nullptr;
+ if (LocaleIdMatchesLang(locale_identifier, "tr") ||
+ LocaleIdMatchesLang(locale_identifier, "az"))
+ locale_for_conversion = "tr";
+ else if (LocaleIdMatchesLang(locale_identifier, "el"))
+ locale_for_conversion = "el";
+ else if (LocaleIdMatchesLang(locale_identifier, "lt"))
+ locale_for_conversion = "lt";
+ else
+ return UpperUnicode();
+
+ CHECK_LE(length_, static_cast<unsigned>(numeric_limits<int32_t>::max()));
+ int length = length_;
+
+ scoped_refptr<StringImpl> upconverted = UpconvertedString();
+ const UChar* source16 = upconverted->Characters16();
+
+ return CaseConvert(source16, length, u_strToUpper, locale_for_conversion,
+ this);
+}
+
+scoped_refptr<StringImpl> StringImpl::Fill(UChar character) {
+ if (!(character & ~0x7F)) {
+ LChar* data;
+ scoped_refptr<StringImpl> new_impl = CreateUninitialized(length_, data);
+ for (unsigned i = 0; i < length_; ++i)
+ data[i] = static_cast<LChar>(character);
+ return new_impl;
+ }
+ UChar* data;
+ scoped_refptr<StringImpl> new_impl = CreateUninitialized(length_, data);
+ for (unsigned i = 0; i < length_; ++i)
+ data[i] = character;
+ return new_impl;
+}
+
+scoped_refptr<StringImpl> StringImpl::FoldCase() {
+ CHECK_LE(length_, static_cast<unsigned>(numeric_limits<int32_t>::max()));
+ int32_t length = length_;
+
+ if (Is8Bit()) {
+ // Do a faster loop for the case where all the characters are ASCII.
+ LChar* data;
+ scoped_refptr<StringImpl> new_impl = CreateUninitialized(length_, data);
+ LChar ored = 0;
+
+ for (int32_t i = 0; i < length; ++i) {
+ LChar c = Characters8()[i];
+ data[i] = ToASCIILower(c);
+ ored |= c;
+ }
+
+ if (!(ored & ~0x7F))
+ return new_impl;
+
+ // Do a slower implementation for cases that include non-ASCII Latin-1
+ // characters.
+ for (int32_t i = 0; i < length; ++i)
+ data[i] = static_cast<LChar>(Unicode::ToLower(Characters8()[i]));
+
+ return new_impl;
+ }
+
+ // Do a faster loop for the case where all the characters are ASCII.
+ UChar* data;
+ scoped_refptr<StringImpl> new_impl = CreateUninitialized(length_, data);
+ UChar ored = 0;
+ for (int32_t i = 0; i < length; ++i) {
+ UChar c = Characters16()[i];
+ ored |= c;
+ data[i] = ToASCIILower(c);
+ }
+ if (!(ored & ~0x7F))
+ return new_impl;
+
+ // Do a slower implementation for cases that include non-ASCII characters.
+ bool error;
+ int32_t real_length =
+ Unicode::FoldCase(data, length, Characters16(), length_, &error);
+ if (!error && real_length == length)
+ return new_impl;
+ new_impl = CreateUninitialized(real_length, data);
+ Unicode::FoldCase(data, real_length, Characters16(), length_, &error);
+ if (error)
+ return this;
+ return new_impl;
+}
+
+scoped_refptr<StringImpl> StringImpl::Truncate(unsigned length) {
+ if (length >= length_)
+ return this;
+ if (Is8Bit())
+ return Create(Characters8(), length);
+ return Create(Characters16(), length);
+}
+
+template <class UCharPredicate>
+inline scoped_refptr<StringImpl> StringImpl::StripMatchedCharacters(
+ UCharPredicate predicate) {
+ if (!length_)
+ return empty_;
+
+ unsigned start = 0;
+ unsigned end = length_ - 1;
+
+ // skip white space from start
+ while (start <= end &&
+ predicate(Is8Bit() ? Characters8()[start] : Characters16()[start]))
+ ++start;
+
+ // only white space
+ if (start > end)
+ return empty_;
+
+ // skip white space from end
+ while (end && predicate(Is8Bit() ? Characters8()[end] : Characters16()[end]))
+ --end;
+
+ if (!start && end == length_ - 1)
+ return this;
+ if (Is8Bit())
+ return Create(Characters8() + start, end + 1 - start);
+ return Create(Characters16() + start, end + 1 - start);
+}
+
+class UCharPredicate final {
+ STACK_ALLOCATED();
+
+ public:
+ inline UCharPredicate(CharacterMatchFunctionPtr function)
+ : function_(function) {}
+
+ inline bool operator()(UChar ch) const { return function_(ch); }
+
+ private:
+ const CharacterMatchFunctionPtr function_;
+};
+
+class SpaceOrNewlinePredicate final {
+ STACK_ALLOCATED();
+
+ public:
+ inline bool operator()(UChar ch) const { return IsSpaceOrNewline(ch); }
+};
+
+scoped_refptr<StringImpl> StringImpl::StripWhiteSpace() {
+ return StripMatchedCharacters(SpaceOrNewlinePredicate());
+}
+
+scoped_refptr<StringImpl> StringImpl::StripWhiteSpace(
+ IsWhiteSpaceFunctionPtr is_white_space) {
+ return StripMatchedCharacters(UCharPredicate(is_white_space));
+}
+
+template <typename CharType>
+ALWAYS_INLINE scoped_refptr<StringImpl> StringImpl::RemoveCharacters(
+ const CharType* characters,
+ CharacterMatchFunctionPtr find_match) {
+ const CharType* from = characters;
+ const CharType* fromend = from + length_;
+
+ // Assume the common case will not remove any characters
+ while (from != fromend && !find_match(*from))
+ ++from;
+ if (from == fromend)
+ return this;
+
+ StringBuffer<CharType> data(length_);
+ CharType* to = data.Characters();
+ unsigned outc = from - characters;
+
+ if (outc)
+ memcpy(to, characters, outc * sizeof(CharType));
+
+ while (true) {
+ while (from != fromend && find_match(*from))
+ ++from;
+ while (from != fromend && !find_match(*from))
+ to[outc++] = *from++;
+ if (from == fromend)
+ break;
+ }
+
+ data.Shrink(outc);
+
+ return data.Release();
+}
+
+scoped_refptr<StringImpl> StringImpl::RemoveCharacters(
+ CharacterMatchFunctionPtr find_match) {
+ if (Is8Bit())
+ return RemoveCharacters(Characters8(), find_match);
+ return RemoveCharacters(Characters16(), find_match);
+}
+
+scoped_refptr<StringImpl> StringImpl::Remove(unsigned start,
+ unsigned length_to_remove) {
+ if (length_to_remove <= 0)
+ return this;
+ if (start >= length_)
+ return this;
+
+ length_to_remove = std::min(length_ - start, length_to_remove);
+ unsigned removed_end = start + length_to_remove;
+
+ if (Is8Bit()) {
+ StringBuffer<LChar> buffer(length_ - length_to_remove);
+ CopyChars(buffer.Characters(), Characters8(), start);
+ CopyChars(buffer.Characters() + start, Characters8() + removed_end,
+ length_ - removed_end);
+ return buffer.Release();
+ }
+ StringBuffer<UChar> buffer(length_ - length_to_remove);
+ CopyChars(buffer.Characters(), Characters16(), start);
+ CopyChars(buffer.Characters() + start, Characters16() + removed_end,
+ length_ - removed_end);
+ return buffer.Release();
+}
+
+template <typename CharType, class UCharPredicate>
+inline scoped_refptr<StringImpl> StringImpl::SimplifyMatchedCharactersToSpace(
+ UCharPredicate predicate,
+ StripBehavior strip_behavior) {
+ StringBuffer<CharType> data(length_);
+
+ const CharType* from = GetCharacters<CharType>();
+ const CharType* fromend = from + length_;
+ int outc = 0;
+ bool changed_to_space = false;
+
+ CharType* to = data.Characters();
+
+ if (strip_behavior == kStripExtraWhiteSpace) {
+ while (true) {
+ while (from != fromend && predicate(*from)) {
+ if (*from != ' ')
+ changed_to_space = true;
+ ++from;
+ }
+ while (from != fromend && !predicate(*from))
+ to[outc++] = *from++;
+ if (from != fromend)
+ to[outc++] = ' ';
+ else
+ break;
+ }
+
+ if (outc > 0 && to[outc - 1] == ' ')
+ --outc;
+ } else {
+ for (; from != fromend; ++from) {
+ if (predicate(*from)) {
+ if (*from != ' ')
+ changed_to_space = true;
+ to[outc++] = ' ';
+ } else {
+ to[outc++] = *from;
+ }
+ }
+ }
+
+ if (static_cast<unsigned>(outc) == length_ && !changed_to_space)
+ return this;
+
+ data.Shrink(outc);
+
+ return data.Release();
+}
+
+scoped_refptr<StringImpl> StringImpl::SimplifyWhiteSpace(
+ StripBehavior strip_behavior) {
+ if (Is8Bit())
+ return StringImpl::SimplifyMatchedCharactersToSpace<LChar>(
+ SpaceOrNewlinePredicate(), strip_behavior);
+ return StringImpl::SimplifyMatchedCharactersToSpace<UChar>(
+ SpaceOrNewlinePredicate(), strip_behavior);
+}
+
+scoped_refptr<StringImpl> StringImpl::SimplifyWhiteSpace(
+ IsWhiteSpaceFunctionPtr is_white_space,
+ StripBehavior strip_behavior) {
+ if (Is8Bit())
+ return StringImpl::SimplifyMatchedCharactersToSpace<LChar>(
+ UCharPredicate(is_white_space), strip_behavior);
+ return StringImpl::SimplifyMatchedCharactersToSpace<UChar>(
+ UCharPredicate(is_white_space), strip_behavior);
+}
+
+int StringImpl::ToInt(NumberParsingOptions options, bool* ok) const {
+ if (Is8Bit())
+ return CharactersToInt(Characters8(), length_, options, ok);
+ return CharactersToInt(Characters16(), length_, options, ok);
+}
+
+unsigned StringImpl::ToUInt(NumberParsingOptions options, bool* ok) const {
+ if (Is8Bit())
+ return CharactersToUInt(Characters8(), length_, options, ok);
+ return CharactersToUInt(Characters16(), length_, options, ok);
+}
+
+unsigned StringImpl::HexToUIntStrict(bool* ok) {
+ if (Is8Bit()) {
+ return HexCharactersToUInt(Characters8(), length_,
+ NumberParsingOptions::kStrict, ok);
+ }
+ return HexCharactersToUInt(Characters16(), length_,
+ NumberParsingOptions::kStrict, ok);
+}
+
+int64_t StringImpl::ToInt64(NumberParsingOptions options, bool* ok) const {
+ if (Is8Bit())
+ return CharactersToInt64(Characters8(), length_, options, ok);
+ return CharactersToInt64(Characters16(), length_, options, ok);
+}
+
+uint64_t StringImpl::ToUInt64(NumberParsingOptions options, bool* ok) const {
+ if (Is8Bit())
+ return CharactersToUInt64(Characters8(), length_, options, ok);
+ return CharactersToUInt64(Characters16(), length_, options, ok);
+}
+
+double StringImpl::ToDouble(bool* ok) {
+ if (Is8Bit())
+ return CharactersToDouble(Characters8(), length_, ok);
+ return CharactersToDouble(Characters16(), length_, ok);
+}
+
+float StringImpl::ToFloat(bool* ok) {
+ if (Is8Bit())
+ return CharactersToFloat(Characters8(), length_, ok);
+ return CharactersToFloat(Characters16(), length_, ok);
+}
+
+// Table is based on ftp://ftp.unicode.org/Public/UNIDATA/CaseFolding.txt
+const UChar StringImpl::kLatin1CaseFoldTable[256] = {
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008,
+ 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011,
+ 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a,
+ 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c,
+ 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035,
+ 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e,
+ 0x003f, 0x0040, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070,
+ 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079,
+ 0x007a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062,
+ 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b,
+ 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074,
+ 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d,
+ 0x007e, 0x007f, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086,
+ 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,
+ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098,
+ 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, 0x00a0, 0x00a1,
+ 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, 0x00a8, 0x00a9, 0x00aa,
+ 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, 0x00b0, 0x00b1, 0x00b2, 0x00b3,
+ 0x00b4, 0x03bc, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc,
+ 0x00bd, 0x00be, 0x00bf, 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5,
+ 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee,
+ 0x00ef, 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00d7,
+ 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00df, 0x00e0,
+ 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, 0x00e9,
+ 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x00f0, 0x00f1, 0x00f2,
+ 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb,
+ 0x00fc, 0x00fd, 0x00fe, 0x00ff,
+};
+
+bool DeprecatedEqualIgnoringCase(const LChar* a,
+ const LChar* b,
+ unsigned length) {
+ DCHECK_GE(length, 0u);
+ if (a == b)
+ return true;
+ while (length--) {
+ if (StringImpl::kLatin1CaseFoldTable[*a++] !=
+ StringImpl::kLatin1CaseFoldTable[*b++])
+ return false;
+ }
+ return true;
+}
+
+bool DeprecatedEqualIgnoringCase(const UChar* a,
+ const UChar* b,
+ unsigned length) {
+ DCHECK_GE(length, 0u);
+ if (a == b)
+ return true;
+ return !Unicode::Umemcasecmp(a, b, length);
+}
+
+bool DeprecatedEqualIgnoringCase(const UChar* a,
+ const LChar* b,
+ unsigned length) {
+ while (length--) {
+ if (Unicode::FoldCase(*a++) != StringImpl::kLatin1CaseFoldTable[*b++])
+ return false;
+ }
+ return true;
+}
+
+size_t StringImpl::Find(CharacterMatchFunctionPtr match_function,
+ unsigned start) {
+ if (Is8Bit())
+ return WTF::Find(Characters8(), length_, match_function, start);
+ return WTF::Find(Characters16(), length_, match_function, start);
+}
+
+template <typename SearchCharacterType, typename MatchCharacterType>
+ALWAYS_INLINE static size_t FindInternal(
+ const SearchCharacterType* search_characters,
+ const MatchCharacterType* match_characters,
+ unsigned index,
+ unsigned search_length,
+ unsigned match_length) {
+ // Optimization: keep a running hash of the strings,
+ // only call equal() if the hashes match.
+
+ // delta is the number of additional times to test; delta == 0 means test only
+ // once.
+ unsigned delta = search_length - match_length;
+
+ unsigned search_hash = 0;
+ unsigned match_hash = 0;
+
+ for (unsigned i = 0; i < match_length; ++i) {
+ search_hash += search_characters[i];
+ match_hash += match_characters[i];
+ }
+
+ unsigned i = 0;
+ // keep looping until we match
+ while (search_hash != match_hash ||
+ !Equal(search_characters + i, match_characters, match_length)) {
+ if (i == delta)
+ return kNotFound;
+ search_hash += search_characters[i + match_length];
+ search_hash -= search_characters[i];
+ ++i;
+ }
+ return index + i;
+}
+
+size_t StringImpl::Find(const StringView& match_string, unsigned index) {
+ if (UNLIKELY(match_string.IsNull()))
+ return kNotFound;
+
+ unsigned match_length = match_string.length();
+
+ // Optimization 1: fast case for strings of length 1.
+ if (match_length == 1) {
+ if (Is8Bit())
+ return WTF::Find(Characters8(), length(), match_string[0], index);
+ return WTF::Find(Characters16(), length(), match_string[0], index);
+ }
+
+ if (UNLIKELY(!match_length))
+ return std::min(index, length());
+
+ // Check index & matchLength are in range.
+ if (index > length())
+ return kNotFound;
+ unsigned search_length = length() - index;
+ if (match_length > search_length)
+ return kNotFound;
+
+ if (Is8Bit()) {
+ if (match_string.Is8Bit())
+ return FindInternal(Characters8() + index, match_string.Characters8(),
+ index, search_length, match_length);
+ return FindInternal(Characters8() + index, match_string.Characters16(),
+ index, search_length, match_length);
+ }
+ if (match_string.Is8Bit())
+ return FindInternal(Characters16() + index, match_string.Characters8(),
+ index, search_length, match_length);
+ return FindInternal(Characters16() + index, match_string.Characters16(),
+ index, search_length, match_length);
+}
+
+template <typename SearchCharacterType, typename MatchCharacterType>
+ALWAYS_INLINE static size_t FindIgnoringCaseInternal(
+ const SearchCharacterType* search_characters,
+ const MatchCharacterType* match_characters,
+ unsigned index,
+ unsigned search_length,
+ unsigned match_length) {
+ // delta is the number of additional times to test; delta == 0 means test only
+ // once.
+ unsigned delta = search_length - match_length;
+
+ unsigned i = 0;
+ // keep looping until we match
+ while (!DeprecatedEqualIgnoringCase(search_characters + i, match_characters,
+ match_length)) {
+ if (i == delta)
+ return kNotFound;
+ ++i;
+ }
+ return index + i;
+}
+
+size_t StringImpl::FindIgnoringCase(const StringView& match_string,
+ unsigned index) {
+ if (UNLIKELY(match_string.IsNull()))
+ return kNotFound;
+
+ unsigned match_length = match_string.length();
+ if (!match_length)
+ return std::min(index, length());
+
+ // Check index & matchLength are in range.
+ if (index > length())
+ return kNotFound;
+ unsigned search_length = length() - index;
+ if (match_length > search_length)
+ return kNotFound;
+
+ if (Is8Bit()) {
+ if (match_string.Is8Bit())
+ return FindIgnoringCaseInternal(Characters8() + index,
+ match_string.Characters8(), index,
+ search_length, match_length);
+ return FindIgnoringCaseInternal(Characters8() + index,
+ match_string.Characters16(), index,
+ search_length, match_length);
+ }
+ if (match_string.Is8Bit())
+ return FindIgnoringCaseInternal(Characters16() + index,
+ match_string.Characters8(), index,
+ search_length, match_length);
+ return FindIgnoringCaseInternal(Characters16() + index,
+ match_string.Characters16(), index,
+ search_length, match_length);
+}
+
+template <typename SearchCharacterType, typename MatchCharacterType>
+ALWAYS_INLINE static size_t FindIgnoringASCIICaseInternal(
+ const SearchCharacterType* search_characters,
+ const MatchCharacterType* match_characters,
+ unsigned index,
+ unsigned search_length,
+ unsigned match_length) {
+ // delta is the number of additional times to test; delta == 0 means test only
+ // once.
+ unsigned delta = search_length - match_length;
+
+ unsigned i = 0;
+ // keep looping until we match
+ while (!EqualIgnoringASCIICase(search_characters + i, match_characters,
+ match_length)) {
+ if (i == delta)
+ return kNotFound;
+ ++i;
+ }
+ return index + i;
+}
+
+size_t StringImpl::FindIgnoringASCIICase(const StringView& match_string,
+ unsigned index) {
+ if (UNLIKELY(match_string.IsNull()))
+ return kNotFound;
+
+ unsigned match_length = match_string.length();
+ if (!match_length)
+ return std::min(index, length());
+
+ // Check index & matchLength are in range.
+ if (index > length())
+ return kNotFound;
+ unsigned search_length = length() - index;
+ if (match_length > search_length)
+ return kNotFound;
+
+ if (Is8Bit()) {
+ if (match_string.Is8Bit())
+ return FindIgnoringASCIICaseInternal(Characters8() + index,
+ match_string.Characters8(), index,
+ search_length, match_length);
+ return FindIgnoringASCIICaseInternal(Characters8() + index,
+ match_string.Characters16(), index,
+ search_length, match_length);
+ }
+ if (match_string.Is8Bit())
+ return FindIgnoringASCIICaseInternal(Characters16() + index,
+ match_string.Characters8(), index,
+ search_length, match_length);
+ return FindIgnoringASCIICaseInternal(Characters16() + index,
+ match_string.Characters16(), index,
+ search_length, match_length);
+}
+
+size_t StringImpl::ReverseFind(UChar c, unsigned index) {
+ if (Is8Bit())
+ return WTF::ReverseFind(Characters8(), length_, c, index);
+ return WTF::ReverseFind(Characters16(), length_, c, index);
+}
+
+template <typename SearchCharacterType, typename MatchCharacterType>
+ALWAYS_INLINE static size_t ReverseFindInternal(
+ const SearchCharacterType* search_characters,
+ const MatchCharacterType* match_characters,
+ unsigned index,
+ unsigned length,
+ unsigned match_length) {
+ // Optimization: keep a running hash of the strings,
+ // only call equal if the hashes match.
+
+ // delta is the number of additional times to test; delta == 0 means test only
+ // once.
+ unsigned delta = std::min(index, length - match_length);
+
+ unsigned search_hash = 0;
+ unsigned match_hash = 0;
+ for (unsigned i = 0; i < match_length; ++i) {
+ search_hash += search_characters[delta + i];
+ match_hash += match_characters[i];
+ }
+
+ // keep looping until we match
+ while (search_hash != match_hash ||
+ !Equal(search_characters + delta, match_characters, match_length)) {
+ if (!delta)
+ return kNotFound;
+ --delta;
+ search_hash -= search_characters[delta + match_length];
+ search_hash += search_characters[delta];
+ }
+ return delta;
+}
+
+size_t StringImpl::ReverseFind(const StringView& match_string, unsigned index) {
+ if (UNLIKELY(match_string.IsNull()))
+ return kNotFound;
+
+ unsigned match_length = match_string.length();
+ unsigned our_length = length();
+ if (!match_length)
+ return std::min(index, our_length);
+
+ // Optimization 1: fast case for strings of length 1.
+ if (match_length == 1) {
+ if (Is8Bit())
+ return WTF::ReverseFind(Characters8(), our_length, match_string[0],
+ index);
+ return WTF::ReverseFind(Characters16(), our_length, match_string[0], index);
+ }
+
+ // Check index & matchLength are in range.
+ if (match_length > our_length)
+ return kNotFound;
+
+ if (Is8Bit()) {
+ if (match_string.Is8Bit())
+ return ReverseFindInternal(Characters8(), match_string.Characters8(),
+ index, our_length, match_length);
+ return ReverseFindInternal(Characters8(), match_string.Characters16(),
+ index, our_length, match_length);
+ }
+ if (match_string.Is8Bit())
+ return ReverseFindInternal(Characters16(), match_string.Characters8(),
+ index, our_length, match_length);
+ return ReverseFindInternal(Characters16(), match_string.Characters16(), index,
+ our_length, match_length);
+}
+
+bool StringImpl::StartsWith(UChar character) const {
+ return length_ && (*this)[0] == character;
+}
+
+bool StringImpl::StartsWith(const StringView& prefix) const {
+ if (prefix.length() > length())
+ return false;
+ if (Is8Bit()) {
+ if (prefix.Is8Bit())
+ return Equal(Characters8(), prefix.Characters8(), prefix.length());
+ return Equal(Characters8(), prefix.Characters16(), prefix.length());
+ }
+ if (prefix.Is8Bit())
+ return Equal(Characters16(), prefix.Characters8(), prefix.length());
+ return Equal(Characters16(), prefix.Characters16(), prefix.length());
+}
+
+bool StringImpl::StartsWithIgnoringCase(const StringView& prefix) const {
+ if (prefix.length() > length())
+ return false;
+ if (Is8Bit()) {
+ if (prefix.Is8Bit()) {
+ return DeprecatedEqualIgnoringCase(Characters8(), prefix.Characters8(),
+ prefix.length());
+ }
+ return DeprecatedEqualIgnoringCase(Characters8(), prefix.Characters16(),
+ prefix.length());
+ }
+ if (prefix.Is8Bit()) {
+ return DeprecatedEqualIgnoringCase(Characters16(), prefix.Characters8(),
+ prefix.length());
+ }
+ return DeprecatedEqualIgnoringCase(Characters16(), prefix.Characters16(),
+ prefix.length());
+}
+
+bool StringImpl::StartsWithIgnoringASCIICase(const StringView& prefix) const {
+ if (prefix.length() > length())
+ return false;
+ if (Is8Bit()) {
+ if (prefix.Is8Bit())
+ return EqualIgnoringASCIICase(Characters8(), prefix.Characters8(),
+ prefix.length());
+ return EqualIgnoringASCIICase(Characters8(), prefix.Characters16(),
+ prefix.length());
+ }
+ if (prefix.Is8Bit())
+ return EqualIgnoringASCIICase(Characters16(), prefix.Characters8(),
+ prefix.length());
+ return EqualIgnoringASCIICase(Characters16(), prefix.Characters16(),
+ prefix.length());
+}
+
+bool StringImpl::EndsWith(UChar character) const {
+ return length_ && (*this)[length_ - 1] == character;
+}
+
+bool StringImpl::EndsWith(const StringView& suffix) const {
+ if (suffix.length() > length())
+ return false;
+ unsigned start_offset = length() - suffix.length();
+ if (Is8Bit()) {
+ if (suffix.Is8Bit())
+ return Equal(Characters8() + start_offset, suffix.Characters8(),
+ suffix.length());
+ return Equal(Characters8() + start_offset, suffix.Characters16(),
+ suffix.length());
+ }
+ if (suffix.Is8Bit())
+ return Equal(Characters16() + start_offset, suffix.Characters8(),
+ suffix.length());
+ return Equal(Characters16() + start_offset, suffix.Characters16(),
+ suffix.length());
+}
+
+bool StringImpl::EndsWithIgnoringCase(const StringView& suffix) const {
+ if (suffix.length() > length())
+ return false;
+ unsigned start_offset = length() - suffix.length();
+ if (Is8Bit()) {
+ if (suffix.Is8Bit()) {
+ return DeprecatedEqualIgnoringCase(Characters8() + start_offset,
+ suffix.Characters8(), suffix.length());
+ }
+ return DeprecatedEqualIgnoringCase(Characters8() + start_offset,
+ suffix.Characters16(), suffix.length());
+ }
+ if (suffix.Is8Bit()) {
+ return DeprecatedEqualIgnoringCase(Characters16() + start_offset,
+ suffix.Characters8(), suffix.length());
+ }
+ return DeprecatedEqualIgnoringCase(Characters16() + start_offset,
+ suffix.Characters16(), suffix.length());
+}
+
+bool StringImpl::EndsWithIgnoringASCIICase(const StringView& suffix) const {
+ if (suffix.length() > length())
+ return false;
+ unsigned start_offset = length() - suffix.length();
+ if (Is8Bit()) {
+ if (suffix.Is8Bit())
+ return EqualIgnoringASCIICase(Characters8() + start_offset,
+ suffix.Characters8(), suffix.length());
+ return EqualIgnoringASCIICase(Characters8() + start_offset,
+ suffix.Characters16(), suffix.length());
+ }
+ if (suffix.Is8Bit())
+ return EqualIgnoringASCIICase(Characters16() + start_offset,
+ suffix.Characters8(), suffix.length());
+ return EqualIgnoringASCIICase(Characters16() + start_offset,
+ suffix.Characters16(), suffix.length());
+}
+
+scoped_refptr<StringImpl> StringImpl::Replace(UChar old_c, UChar new_c) {
+ if (old_c == new_c)
+ return this;
+
+ if (Find(old_c) == kNotFound)
+ return this;
+
+ unsigned i;
+ if (Is8Bit()) {
+ if (new_c <= 0xff) {
+ LChar* data;
+ LChar old_char = static_cast<LChar>(old_c);
+ LChar new_char = static_cast<LChar>(new_c);
+
+ scoped_refptr<StringImpl> new_impl = CreateUninitialized(length_, data);
+
+ for (i = 0; i != length_; ++i) {
+ LChar ch = Characters8()[i];
+ if (ch == old_char)
+ ch = new_char;
+ data[i] = ch;
+ }
+ return new_impl;
+ }
+
+ // There is the possibility we need to up convert from 8 to 16 bit,
+ // create a 16 bit string for the result.
+ UChar* data;
+ scoped_refptr<StringImpl> new_impl = CreateUninitialized(length_, data);
+
+ for (i = 0; i != length_; ++i) {
+ UChar ch = Characters8()[i];
+ if (ch == old_c)
+ ch = new_c;
+ data[i] = ch;
+ }
+
+ return new_impl;
+ }
+
+ UChar* data;
+ scoped_refptr<StringImpl> new_impl = CreateUninitialized(length_, data);
+
+ for (i = 0; i != length_; ++i) {
+ UChar ch = Characters16()[i];
+ if (ch == old_c)
+ ch = new_c;
+ data[i] = ch;
+ }
+ return new_impl;
+}
+
+// TODO(esprehn): Passing a null replacement is the same as empty string for
+// this method but all others treat null as a no-op. We should choose one
+// behavior.
+scoped_refptr<StringImpl> StringImpl::Replace(unsigned position,
+ unsigned length_to_replace,
+ const StringView& string) {
+ position = std::min(position, length());
+ length_to_replace = std::min(length_to_replace, length() - position);
+ unsigned length_to_insert = string.length();
+ if (!length_to_replace && !length_to_insert)
+ return this;
+
+ CHECK_LT((length() - length_to_replace),
+ (numeric_limits<unsigned>::max() - length_to_insert));
+
+ if (Is8Bit() && (string.IsNull() || string.Is8Bit())) {
+ LChar* data;
+ scoped_refptr<StringImpl> new_impl = CreateUninitialized(
+ length() - length_to_replace + length_to_insert, data);
+ memcpy(data, Characters8(), position * sizeof(LChar));
+ if (!string.IsNull())
+ memcpy(data + position, string.Characters8(),
+ length_to_insert * sizeof(LChar));
+ memcpy(data + position + length_to_insert,
+ Characters8() + position + length_to_replace,
+ (length() - position - length_to_replace) * sizeof(LChar));
+ return new_impl;
+ }
+ UChar* data;
+ scoped_refptr<StringImpl> new_impl = CreateUninitialized(
+ length() - length_to_replace + length_to_insert, data);
+ if (Is8Bit())
+ for (unsigned i = 0; i < position; ++i)
+ data[i] = Characters8()[i];
+ else
+ memcpy(data, Characters16(), position * sizeof(UChar));
+ if (!string.IsNull()) {
+ if (string.Is8Bit())
+ for (unsigned i = 0; i < length_to_insert; ++i)
+ data[i + position] = string.Characters8()[i];
+ else
+ memcpy(data + position, string.Characters16(),
+ length_to_insert * sizeof(UChar));
+ }
+ if (Is8Bit()) {
+ for (unsigned i = 0; i < length() - position - length_to_replace; ++i)
+ data[i + position + length_to_insert] =
+ Characters8()[i + position + length_to_replace];
+ } else {
+ memcpy(data + position + length_to_insert,
+ Characters16() + position + length_to_replace,
+ (length() - position - length_to_replace) * sizeof(UChar));
+ }
+ return new_impl;
+}
+
+scoped_refptr<StringImpl> StringImpl::Replace(UChar pattern,
+ const StringView& replacement) {
+ if (replacement.IsNull())
+ return this;
+ if (replacement.Is8Bit())
+ return Replace(pattern, replacement.Characters8(), replacement.length());
+ return Replace(pattern, replacement.Characters16(), replacement.length());
+}
+
+scoped_refptr<StringImpl> StringImpl::Replace(UChar pattern,
+ const LChar* replacement,
+ unsigned rep_str_length) {
+ DCHECK(replacement);
+
+ size_t src_segment_start = 0;
+ unsigned match_count = 0;
+
+ // Count the matches.
+ while ((src_segment_start = Find(pattern, src_segment_start)) != kNotFound) {
+ ++match_count;
+ ++src_segment_start;
+ }
+
+ // If we have 0 matches then we don't have to do any more work.
+ if (!match_count)
+ return this;
+
+ CHECK(!rep_str_length ||
+ match_count <= numeric_limits<unsigned>::max() / rep_str_length);
+
+ unsigned replace_size = match_count * rep_str_length;
+ unsigned new_size = length_ - match_count;
+ CHECK_LT(new_size, (numeric_limits<unsigned>::max() - replace_size));
+
+ new_size += replace_size;
+
+ // Construct the new data.
+ size_t src_segment_end;
+ unsigned src_segment_length;
+ src_segment_start = 0;
+ unsigned dst_offset = 0;
+
+ if (Is8Bit()) {
+ LChar* data;
+ scoped_refptr<StringImpl> new_impl = CreateUninitialized(new_size, data);
+
+ while ((src_segment_end = Find(pattern, src_segment_start)) != kNotFound) {
+ src_segment_length = src_segment_end - src_segment_start;
+ memcpy(data + dst_offset, Characters8() + src_segment_start,
+ src_segment_length * sizeof(LChar));
+ dst_offset += src_segment_length;
+ memcpy(data + dst_offset, replacement, rep_str_length * sizeof(LChar));
+ dst_offset += rep_str_length;
+ src_segment_start = src_segment_end + 1;
+ }
+
+ src_segment_length = length_ - src_segment_start;
+ memcpy(data + dst_offset, Characters8() + src_segment_start,
+ src_segment_length * sizeof(LChar));
+
+ DCHECK_EQ(dst_offset + src_segment_length, new_impl->length());
+
+ return new_impl;
+ }
+
+ UChar* data;
+ scoped_refptr<StringImpl> new_impl = CreateUninitialized(new_size, data);
+
+ while ((src_segment_end = Find(pattern, src_segment_start)) != kNotFound) {
+ src_segment_length = src_segment_end - src_segment_start;
+ memcpy(data + dst_offset, Characters16() + src_segment_start,
+ src_segment_length * sizeof(UChar));
+
+ dst_offset += src_segment_length;
+ for (unsigned i = 0; i < rep_str_length; ++i)
+ data[i + dst_offset] = replacement[i];
+
+ dst_offset += rep_str_length;
+ src_segment_start = src_segment_end + 1;
+ }
+
+ src_segment_length = length_ - src_segment_start;
+ memcpy(data + dst_offset, Characters16() + src_segment_start,
+ src_segment_length * sizeof(UChar));
+
+ DCHECK_EQ(dst_offset + src_segment_length, new_impl->length());
+
+ return new_impl;
+}
+
+scoped_refptr<StringImpl> StringImpl::Replace(UChar pattern,
+ const UChar* replacement,
+ unsigned rep_str_length) {
+ DCHECK(replacement);
+
+ size_t src_segment_start = 0;
+ unsigned match_count = 0;
+
+ // Count the matches.
+ while ((src_segment_start = Find(pattern, src_segment_start)) != kNotFound) {
+ ++match_count;
+ ++src_segment_start;
+ }
+
+ // If we have 0 matches then we don't have to do any more work.
+ if (!match_count)
+ return this;
+
+ CHECK(!rep_str_length ||
+ match_count <= numeric_limits<unsigned>::max() / rep_str_length);
+
+ unsigned replace_size = match_count * rep_str_length;
+ unsigned new_size = length_ - match_count;
+ CHECK_LT(new_size, (numeric_limits<unsigned>::max() - replace_size));
+
+ new_size += replace_size;
+
+ // Construct the new data.
+ size_t src_segment_end;
+ unsigned src_segment_length;
+ src_segment_start = 0;
+ unsigned dst_offset = 0;
+
+ if (Is8Bit()) {
+ UChar* data;
+ scoped_refptr<StringImpl> new_impl = CreateUninitialized(new_size, data);
+
+ while ((src_segment_end = Find(pattern, src_segment_start)) != kNotFound) {
+ src_segment_length = src_segment_end - src_segment_start;
+ for (unsigned i = 0; i < src_segment_length; ++i)
+ data[i + dst_offset] = Characters8()[i + src_segment_start];
+
+ dst_offset += src_segment_length;
+ memcpy(data + dst_offset, replacement, rep_str_length * sizeof(UChar));
+
+ dst_offset += rep_str_length;
+ src_segment_start = src_segment_end + 1;
+ }
+
+ src_segment_length = length_ - src_segment_start;
+ for (unsigned i = 0; i < src_segment_length; ++i)
+ data[i + dst_offset] = Characters8()[i + src_segment_start];
+
+ DCHECK_EQ(dst_offset + src_segment_length, new_impl->length());
+
+ return new_impl;
+ }
+
+ UChar* data;
+ scoped_refptr<StringImpl> new_impl = CreateUninitialized(new_size, data);
+
+ while ((src_segment_end = Find(pattern, src_segment_start)) != kNotFound) {
+ src_segment_length = src_segment_end - src_segment_start;
+ memcpy(data + dst_offset, Characters16() + src_segment_start,
+ src_segment_length * sizeof(UChar));
+
+ dst_offset += src_segment_length;
+ memcpy(data + dst_offset, replacement, rep_str_length * sizeof(UChar));
+
+ dst_offset += rep_str_length;
+ src_segment_start = src_segment_end + 1;
+ }
+
+ src_segment_length = length_ - src_segment_start;
+ memcpy(data + dst_offset, Characters16() + src_segment_start,
+ src_segment_length * sizeof(UChar));
+
+ DCHECK_EQ(dst_offset + src_segment_length, new_impl->length());
+
+ return new_impl;
+}
+
+scoped_refptr<StringImpl> StringImpl::Replace(const StringView& pattern,
+ const StringView& replacement) {
+ if (pattern.IsNull() || replacement.IsNull())
+ return this;
+
+ unsigned pattern_length = pattern.length();
+ if (!pattern_length)
+ return this;
+
+ unsigned rep_str_length = replacement.length();
+ size_t src_segment_start = 0;
+ unsigned match_count = 0;
+
+ // Count the matches.
+ while ((src_segment_start = Find(pattern, src_segment_start)) != kNotFound) {
+ ++match_count;
+ src_segment_start += pattern_length;
+ }
+
+ // If we have 0 matches, we don't have to do any more work
+ if (!match_count)
+ return this;
+
+ unsigned new_size = length_ - match_count * pattern_length;
+ CHECK(!rep_str_length ||
+ match_count <= numeric_limits<unsigned>::max() / rep_str_length);
+
+ CHECK_LE(new_size,
+ (numeric_limits<unsigned>::max() - match_count * rep_str_length));
+
+ new_size += match_count * rep_str_length;
+
+ // Construct the new data
+ size_t src_segment_end;
+ unsigned src_segment_length;
+ src_segment_start = 0;
+ unsigned dst_offset = 0;
+ bool src_is8_bit = Is8Bit();
+ bool replacement_is8_bit = replacement.Is8Bit();
+
+ // There are 4 cases:
+ // 1. This and replacement are both 8 bit.
+ // 2. This and replacement are both 16 bit.
+ // 3. This is 8 bit and replacement is 16 bit.
+ // 4. This is 16 bit and replacement is 8 bit.
+ if (src_is8_bit && replacement_is8_bit) {
+ // Case 1
+ LChar* data;
+ scoped_refptr<StringImpl> new_impl = CreateUninitialized(new_size, data);
+ while ((src_segment_end = Find(pattern, src_segment_start)) != kNotFound) {
+ src_segment_length = src_segment_end - src_segment_start;
+ memcpy(data + dst_offset, Characters8() + src_segment_start,
+ src_segment_length * sizeof(LChar));
+ dst_offset += src_segment_length;
+ memcpy(data + dst_offset, replacement.Characters8(),
+ rep_str_length * sizeof(LChar));
+ dst_offset += rep_str_length;
+ src_segment_start = src_segment_end + pattern_length;
+ }
+
+ src_segment_length = length_ - src_segment_start;
+ memcpy(data + dst_offset, Characters8() + src_segment_start,
+ src_segment_length * sizeof(LChar));
+
+ DCHECK_EQ(dst_offset + src_segment_length, new_impl->length());
+
+ return new_impl;
+ }
+
+ UChar* data;
+ scoped_refptr<StringImpl> new_impl = CreateUninitialized(new_size, data);
+ while ((src_segment_end = Find(pattern, src_segment_start)) != kNotFound) {
+ src_segment_length = src_segment_end - src_segment_start;
+ if (src_is8_bit) {
+ // Case 3.
+ for (unsigned i = 0; i < src_segment_length; ++i)
+ data[i + dst_offset] = Characters8()[i + src_segment_start];
+ } else {
+ // Case 2 & 4.
+ memcpy(data + dst_offset, Characters16() + src_segment_start,
+ src_segment_length * sizeof(UChar));
+ }
+ dst_offset += src_segment_length;
+ if (replacement_is8_bit) {
+ // Cases 2 & 3.
+ for (unsigned i = 0; i < rep_str_length; ++i)
+ data[i + dst_offset] = replacement.Characters8()[i];
+ } else {
+ // Case 4
+ memcpy(data + dst_offset, replacement.Characters16(),
+ rep_str_length * sizeof(UChar));
+ }
+ dst_offset += rep_str_length;
+ src_segment_start = src_segment_end + pattern_length;
+ }
+
+ src_segment_length = length_ - src_segment_start;
+ if (src_is8_bit) {
+ // Case 3.
+ for (unsigned i = 0; i < src_segment_length; ++i)
+ data[i + dst_offset] = Characters8()[i + src_segment_start];
+ } else {
+ // Cases 2 & 4.
+ memcpy(data + dst_offset, Characters16() + src_segment_start,
+ src_segment_length * sizeof(UChar));
+ }
+
+ DCHECK_EQ(dst_offset + src_segment_length, new_impl->length());
+
+ return new_impl;
+}
+
+scoped_refptr<StringImpl> StringImpl::UpconvertedString() {
+ if (Is8Bit())
+ return String::Make16BitFrom8BitSource(Characters8(), length_)
+ .ReleaseImpl();
+ return this;
+}
+
+static inline bool StringImplContentEqual(const StringImpl* a,
+ const StringImpl* b) {
+ unsigned a_length = a->length();
+ unsigned b_length = b->length();
+ if (a_length != b_length)
+ return false;
+
+ if (a->Is8Bit()) {
+ if (b->Is8Bit())
+ return Equal(a->Characters8(), b->Characters8(), a_length);
+
+ return Equal(a->Characters8(), b->Characters16(), a_length);
+ }
+
+ if (b->Is8Bit())
+ return Equal(a->Characters16(), b->Characters8(), a_length);
+
+ return Equal(a->Characters16(), b->Characters16(), a_length);
+}
+
+bool Equal(const StringImpl* a, const StringImpl* b) {
+ if (a == b)
+ return true;
+ if (!a || !b)
+ return false;
+ if (a->IsAtomic() && b->IsAtomic())
+ return false;
+
+ return StringImplContentEqual(a, b);
+}
+
+template <typename CharType>
+inline bool EqualInternal(const StringImpl* a,
+ const CharType* b,
+ unsigned length) {
+ if (!a)
+ return !b;
+ if (!b)
+ return false;
+
+ if (a->length() != length)
+ return false;
+ if (a->Is8Bit())
+ return Equal(a->Characters8(), b, length);
+ return Equal(a->Characters16(), b, length);
+}
+
+bool Equal(const StringImpl* a, const LChar* b, unsigned length) {
+ return EqualInternal(a, b, length);
+}
+
+bool Equal(const StringImpl* a, const UChar* b, unsigned length) {
+ return EqualInternal(a, b, length);
+}
+
+bool Equal(const StringImpl* a, const LChar* b) {
+ if (!a)
+ return !b;
+ if (!b)
+ return !a;
+
+ unsigned length = a->length();
+
+ if (a->Is8Bit()) {
+ const LChar* a_ptr = a->Characters8();
+ for (unsigned i = 0; i != length; ++i) {
+ LChar bc = b[i];
+ LChar ac = a_ptr[i];
+ if (!bc)
+ return false;
+ if (ac != bc)
+ return false;
+ }
+
+ return !b[length];
+ }
+
+ const UChar* a_ptr = a->Characters16();
+ for (unsigned i = 0; i != length; ++i) {
+ LChar bc = b[i];
+ if (!bc)
+ return false;
+ if (a_ptr[i] != bc)
+ return false;
+ }
+
+ return !b[length];
+}
+
+bool EqualNonNull(const StringImpl* a, const StringImpl* b) {
+ DCHECK(a);
+ DCHECK(b);
+ if (a == b)
+ return true;
+
+ return StringImplContentEqual(a, b);
+}
+
+bool EqualIgnoringNullity(StringImpl* a, StringImpl* b) {
+ if (!a && b && !b->length())
+ return true;
+ if (!b && a && !a->length())
+ return true;
+ return Equal(a, b);
+}
+
+template <typename CharacterType1, typename CharacterType2>
+int CodePointCompareIgnoringASCIICase(unsigned l1,
+ unsigned l2,
+ const CharacterType1* c1,
+ const CharacterType2* c2) {
+ const unsigned lmin = l1 < l2 ? l1 : l2;
+ unsigned pos = 0;
+ while (pos < lmin && ToASCIILower(*c1) == ToASCIILower(*c2)) {
+ ++c1;
+ ++c2;
+ ++pos;
+ }
+
+ if (pos < lmin)
+ return (ToASCIILower(c1[0]) > ToASCIILower(c2[0])) ? 1 : -1;
+
+ if (l1 == l2)
+ return 0;
+
+ return (l1 > l2) ? 1 : -1;
+}
+
+int CodePointCompareIgnoringASCIICase(const StringImpl* string1,
+ const LChar* string2) {
+ unsigned length1 = string1 ? string1->length() : 0;
+ size_t length2 = string2 ? strlen(reinterpret_cast<const char*>(string2)) : 0;
+
+ if (!string1)
+ return length2 > 0 ? -1 : 0;
+
+ if (!string2)
+ return length1 > 0 ? 1 : 0;
+
+ if (string1->Is8Bit())
+ return CodePointCompareIgnoringASCIICase(length1, length2,
+ string1->Characters8(), string2);
+ return CodePointCompareIgnoringASCIICase(length1, length2,
+ string1->Characters16(), string2);
+}
+
+UChar32 ToUpper(UChar32 c, const AtomicString& locale_identifier) {
+ if (!locale_identifier.IsNull()) {
+ if (LocaleIdMatchesLang(locale_identifier, "tr") ||
+ LocaleIdMatchesLang(locale_identifier, "az")) {
+ if (c == 'i')
+ return kLatinCapitalLetterIWithDotAbove;
+ if (c == kLatinSmallLetterDotlessI)
+ return 'I';
+ } else if (LocaleIdMatchesLang(locale_identifier, "lt")) {
+ // TODO(rob.buis) implement upper-casing rules for lt
+ // like in StringImpl::upper(locale).
+ }
+ }
+
+ return Unicode::ToUpper(c);
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/string_impl.h b/chromium/third_party/blink/renderer/platform/wtf/text/string_impl.h
new file mode 100644
index 00000000000..158d81ca9ba
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/string_impl.h
@@ -0,0 +1,855 @@
+/*
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2013 Apple Inc. All rights
+ * reserved.
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_STRING_IMPL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_STRING_IMPL_H_
+
+#include <limits.h>
+#include <string.h>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/wtf/ascii_ctype.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/string_hasher.h"
+#include "third_party/blink/renderer/platform/wtf/text/ascii_fast_path.h"
+#include "third_party/blink/renderer/platform/wtf/text/number_parsing_options.h"
+#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_export.h"
+
+#if DCHECK_IS_ON()
+#include "third_party/blink/renderer/platform/wtf/thread_restriction_verifier.h"
+#endif
+
+#if defined(OS_MACOSX)
+typedef const struct __CFString* CFStringRef;
+#endif
+
+#ifdef __OBJC__
+@class NSString;
+#endif
+
+namespace WTF {
+
+struct AlreadyHashed;
+template <typename>
+class RetainPtr;
+
+enum TextCaseSensitivity {
+ kTextCaseSensitive,
+ kTextCaseASCIIInsensitive,
+
+ // Unicode aware case insensitive matching. Non-ASCII characters might match
+ // to ASCII characters. This flag is rarely used to implement web platform
+ // features.
+ kTextCaseUnicodeInsensitive
+};
+
+enum StripBehavior { kStripExtraWhiteSpace, kDoNotStripWhiteSpace };
+
+typedef bool (*CharacterMatchFunctionPtr)(UChar);
+typedef bool (*IsWhiteSpaceFunctionPtr)(UChar);
+typedef HashMap<unsigned, StringImpl*, AlreadyHashed> StaticStringsTable;
+
+// You can find documentation about this class in this doc:
+// https://docs.google.com/document/d/1kOCUlJdh2WJMJGDf-WoEQhmnjKLaOYRbiHz5TiGJl14/edit?usp=sharing
+class WTF_EXPORT StringImpl {
+ private:
+ // StringImpls are allocated out of the WTF buffer partition.
+ void* operator new(size_t);
+ void* operator new(size_t, void* ptr) { return ptr; }
+ void operator delete(void*);
+
+ // Used to construct static strings, which have an special refCount that can
+ // never hit zero. This means that the static string will never be
+ // destroyed, which is important because static strings will be shared
+ // across threads & ref-counted in a non-threadsafe manner.
+ enum ConstructEmptyStringTag { kConstructEmptyString };
+ explicit StringImpl(ConstructEmptyStringTag)
+ : ref_count_(1),
+ length_(0),
+ hash_(0),
+ contains_only_ascii_(true),
+ needs_ascii_check_(false),
+ is_atomic_(false),
+ is8_bit_(true),
+ is_static_(true) {
+ // Ensure that the hash is computed so that AtomicStringHash can call
+ // existingHash() with impunity. The empty string is special because it
+ // is never entered into AtomicString's HashKey, but still needs to
+ // compare correctly.
+ GetHash();
+ }
+
+ enum ConstructEmptyString16BitTag { kConstructEmptyString16Bit };
+ explicit StringImpl(ConstructEmptyString16BitTag)
+ : ref_count_(1),
+ length_(0),
+ hash_(0),
+ contains_only_ascii_(true),
+ needs_ascii_check_(false),
+ is_atomic_(false),
+ is8_bit_(false),
+ is_static_(true) {
+ GetHash();
+ }
+
+ // FIXME: there has to be a less hacky way to do this.
+ enum Force8Bit { kForce8BitConstructor };
+ StringImpl(unsigned length, Force8Bit)
+ : ref_count_(1),
+ length_(length),
+ hash_(0),
+ contains_only_ascii_(!length),
+ needs_ascii_check_(static_cast<bool>(length)),
+ is_atomic_(false),
+ is8_bit_(true),
+ is_static_(false) {
+ DCHECK(length_);
+ }
+
+ StringImpl(unsigned length)
+ : ref_count_(1),
+ length_(length),
+ hash_(0),
+ contains_only_ascii_(!length),
+ needs_ascii_check_(static_cast<bool>(length)),
+ is_atomic_(false),
+ is8_bit_(false),
+ is_static_(false) {
+ DCHECK(length_);
+ }
+
+ enum StaticStringTag { kStaticString };
+ StringImpl(unsigned length, unsigned hash, StaticStringTag)
+ : ref_count_(1),
+ length_(length),
+ hash_(hash),
+ contains_only_ascii_(!length),
+ needs_ascii_check_(static_cast<bool>(length)),
+ is_atomic_(false),
+ is8_bit_(true),
+ is_static_(true) {}
+
+ public:
+ REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE();
+ static StringImpl* empty_;
+ static StringImpl* empty16_bit_;
+
+ ~StringImpl();
+
+ static void InitStatics();
+
+ static StringImpl* CreateStatic(const char* string,
+ unsigned length,
+ unsigned hash);
+ static void ReserveStaticStringsCapacityForSize(unsigned size);
+ static void FreezeStaticStrings();
+ static const StaticStringsTable& AllStaticStrings();
+ static unsigned HighestStaticStringLength() {
+ return highest_static_string_length_;
+ }
+
+ static scoped_refptr<StringImpl> Create(const UChar*, unsigned length);
+ static scoped_refptr<StringImpl> Create(const LChar*, unsigned length);
+ static scoped_refptr<StringImpl> Create8BitIfPossible(const UChar*,
+ unsigned length);
+ template <size_t inlineCapacity>
+ static scoped_refptr<StringImpl> Create8BitIfPossible(
+ const Vector<UChar, inlineCapacity>& vector) {
+ return Create8BitIfPossible(vector.data(), vector.size());
+ }
+
+ ALWAYS_INLINE static scoped_refptr<StringImpl> Create(const char* s,
+ unsigned length) {
+ return Create(reinterpret_cast<const LChar*>(s), length);
+ }
+ static scoped_refptr<StringImpl> Create(const LChar*);
+ ALWAYS_INLINE static scoped_refptr<StringImpl> Create(const char* s) {
+ return Create(reinterpret_cast<const LChar*>(s));
+ }
+
+ static scoped_refptr<StringImpl> CreateUninitialized(unsigned length,
+ LChar*& data);
+ static scoped_refptr<StringImpl> CreateUninitialized(unsigned length,
+ UChar*& data);
+
+ unsigned length() const { return length_; }
+ bool Is8Bit() const { return is8_bit_; }
+
+ ALWAYS_INLINE const LChar* Characters8() const {
+ DCHECK(Is8Bit());
+ return reinterpret_cast<const LChar*>(this + 1);
+ }
+ ALWAYS_INLINE const UChar* Characters16() const {
+ DCHECK(!Is8Bit());
+ return reinterpret_cast<const UChar*>(this + 1);
+ }
+ ALWAYS_INLINE const void* Bytes() const {
+ return reinterpret_cast<const void*>(this + 1);
+ }
+
+ template <typename CharType>
+ ALWAYS_INLINE const CharType* GetCharacters() const;
+
+ size_t CharactersSizeInBytes() const {
+ return length() * (Is8Bit() ? sizeof(LChar) : sizeof(UChar));
+ }
+
+ bool IsAtomic() const { return is_atomic_; }
+ void SetIsAtomic(bool is_atomic) { is_atomic_ = is_atomic; }
+
+ bool IsStatic() const { return is_static_; }
+
+ bool ContainsOnlyASCII() const;
+
+ bool IsSafeToSendToAnotherThread() const;
+
+ // The high bits of 'hash' are always empty, but we prefer to store our
+ // flags in the low bits because it makes them slightly more efficient to
+ // access. So, we shift left and right when setting and getting our hash
+ // code.
+ void SetHash(unsigned hash) const {
+ DCHECK(!HasHash());
+ // Multiple clients assume that StringHasher is the canonical string
+ // hash function.
+ DCHECK(hash == (Is8Bit() ? StringHasher::ComputeHashAndMaskTop8Bits(
+ Characters8(), length_)
+ : StringHasher::ComputeHashAndMaskTop8Bits(
+ Characters16(), length_)));
+ hash_ = hash;
+ DCHECK(hash); // Verify that 0 is a valid sentinel hash value.
+ }
+
+ bool HasHash() const { return hash_ != 0; }
+
+ unsigned ExistingHash() const {
+ DCHECK(HasHash());
+ return hash_;
+ }
+
+ unsigned GetHash() const {
+ if (HasHash())
+ return ExistingHash();
+ return HashSlowCase();
+ }
+
+ ALWAYS_INLINE bool HasOneRef() const {
+#if DCHECK_IS_ON()
+ DCHECK(IsStatic() || verifier_.IsSafeToUse()) << AsciiForDebugging();
+#endif
+ return ref_count_ == 1;
+ }
+
+ ALWAYS_INLINE void AddRef() const {
+#if DCHECK_IS_ON()
+ DCHECK(IsStatic() || verifier_.OnRef(ref_count_)) << AsciiForDebugging();
+#endif
+ ++ref_count_;
+ }
+
+ ALWAYS_INLINE void Release() const {
+#if DCHECK_IS_ON()
+ DCHECK(IsStatic() || verifier_.OnDeref(ref_count_))
+ << AsciiForDebugging() << " " << CurrentThread();
+#endif
+ if (!--ref_count_)
+ DestroyIfNotStatic();
+ }
+
+ ALWAYS_INLINE void Adopted() const {}
+
+ // FIXME: Does this really belong in StringImpl?
+ template <typename T>
+ static void CopyChars(T* destination,
+ const T* source,
+ unsigned num_characters) {
+ memcpy(destination, source, num_characters * sizeof(T));
+ }
+
+ ALWAYS_INLINE static void CopyChars(UChar* destination,
+ const LChar* source,
+ unsigned num_characters) {
+ for (unsigned i = 0; i < num_characters; ++i)
+ destination[i] = source[i];
+ }
+
+ // Some string features, like refcounting and the atomicity flag, are not
+ // thread-safe. We achieve thread safety by isolation, giving each thread
+ // its own copy of the string.
+ scoped_refptr<StringImpl> IsolatedCopy() const;
+
+ scoped_refptr<StringImpl> Substring(unsigned pos,
+ unsigned len = UINT_MAX) const;
+
+ UChar operator[](unsigned i) const {
+ SECURITY_DCHECK(i < length_);
+ if (Is8Bit())
+ return Characters8()[i];
+ return Characters16()[i];
+ }
+ UChar32 CharacterStartingAt(unsigned);
+
+ bool ContainsOnlyWhitespace();
+
+ int ToInt(NumberParsingOptions, bool* ok) const;
+ unsigned ToUInt(NumberParsingOptions, bool* ok) const;
+ int64_t ToInt64(NumberParsingOptions, bool* ok) const;
+ uint64_t ToUInt64(NumberParsingOptions, bool* ok) const;
+
+ unsigned HexToUIntStrict(bool* ok);
+
+ // FIXME: Like NumberParsingOptions::kStrict, these give false for "ok" when
+ // there is trailing garbage. Like NumberParsingOptions::kLoose, these return
+ // the value when there is trailing garbage. It would be better if these were
+ // more consistent with the above functions instead.
+ double ToDouble(bool* ok = nullptr);
+ float ToFloat(bool* ok = nullptr);
+
+ scoped_refptr<StringImpl> LowerUnicode();
+ scoped_refptr<StringImpl> LowerASCII();
+ scoped_refptr<StringImpl> UpperUnicode();
+ scoped_refptr<StringImpl> UpperASCII();
+ scoped_refptr<StringImpl> LowerUnicode(const AtomicString& locale_identifier);
+ scoped_refptr<StringImpl> UpperUnicode(const AtomicString& locale_identifier);
+
+ scoped_refptr<StringImpl> Fill(UChar);
+ // FIXME: Do we need fill(char) or can we just do the right thing if UChar is
+ // ASCII?
+ scoped_refptr<StringImpl> FoldCase();
+
+ scoped_refptr<StringImpl> Truncate(unsigned length);
+
+ scoped_refptr<StringImpl> StripWhiteSpace();
+ scoped_refptr<StringImpl> StripWhiteSpace(IsWhiteSpaceFunctionPtr);
+ scoped_refptr<StringImpl> SimplifyWhiteSpace(
+ StripBehavior = kStripExtraWhiteSpace);
+ scoped_refptr<StringImpl> SimplifyWhiteSpace(
+ IsWhiteSpaceFunctionPtr,
+ StripBehavior = kStripExtraWhiteSpace);
+
+ scoped_refptr<StringImpl> RemoveCharacters(CharacterMatchFunctionPtr);
+ template <typename CharType>
+ ALWAYS_INLINE scoped_refptr<StringImpl> RemoveCharacters(
+ const CharType* characters,
+ CharacterMatchFunctionPtr);
+
+ // Remove characters between [start, start+lengthToRemove). The range is
+ // clamped to the size of the string. Does nothing if start >= length().
+ scoped_refptr<StringImpl> Remove(unsigned start,
+ unsigned length_to_remove = 1);
+
+ // Find characters.
+ size_t Find(LChar character, unsigned start = 0);
+ size_t Find(char character, unsigned start = 0);
+ size_t Find(UChar character, unsigned start = 0);
+ size_t Find(CharacterMatchFunctionPtr, unsigned index = 0);
+
+ // Find substrings.
+ size_t Find(const StringView&, unsigned index = 0);
+ // Unicode aware case insensitive string matching. Non-ASCII characters might
+ // match to ASCII characters. This function is rarely used to implement web
+ // platform features.
+ size_t FindIgnoringCase(const StringView&, unsigned index = 0);
+ size_t FindIgnoringASCIICase(const StringView&, unsigned index = 0);
+
+ size_t ReverseFind(UChar, unsigned index = UINT_MAX);
+ size_t ReverseFind(const StringView&, unsigned index = UINT_MAX);
+
+ bool StartsWith(UChar) const;
+ bool StartsWith(const StringView&) const;
+ bool StartsWithIgnoringCase(const StringView&) const;
+ bool StartsWithIgnoringASCIICase(const StringView&) const;
+
+ bool EndsWith(UChar) const;
+ bool EndsWith(const StringView&) const;
+ bool EndsWithIgnoringCase(const StringView&) const;
+ bool EndsWithIgnoringASCIICase(const StringView&) const;
+
+ // Replace parts of the string.
+ scoped_refptr<StringImpl> Replace(UChar pattern, UChar replacement);
+ scoped_refptr<StringImpl> Replace(UChar pattern,
+ const StringView& replacement);
+ scoped_refptr<StringImpl> Replace(const StringView& pattern,
+ const StringView& replacement);
+ scoped_refptr<StringImpl> Replace(unsigned index,
+ unsigned length_to_replace,
+ const StringView& replacement);
+
+ scoped_refptr<StringImpl> UpconvertedString();
+
+ // Copy characters from string starting at |start| up until |maxLength| or
+ // the end of the string is reached. Returns the actual number of characters
+ // copied.
+ unsigned CopyTo(UChar* buffer, unsigned start, unsigned max_length) const;
+
+ // Append characters from this string into a buffer. Expects the buffer to
+ // have the methods:
+ // append(const UChar*, unsigned length);
+ // append(const LChar*, unsigned length);
+ // StringBuilder and Vector conform to this protocol.
+ template <typename BufferType>
+ void AppendTo(BufferType&,
+ unsigned start = 0,
+ unsigned length = UINT_MAX) const;
+
+ // Prepend characters from this string into a buffer. Expects the buffer to
+ // have the methods:
+ // prepend(const UChar*, unsigned length);
+ // prepend(const LChar*, unsigned length);
+ // Vector conforms to this protocol.
+ template <typename BufferType>
+ void PrependTo(BufferType&,
+ unsigned start = 0,
+ unsigned length = UINT_MAX) const;
+
+#if defined(OS_MACOSX)
+ RetainPtr<CFStringRef> CreateCFString();
+#endif
+#ifdef __OBJC__
+ operator NSString*();
+#endif
+
+ static const UChar kLatin1CaseFoldTable[256];
+
+ private:
+ template <typename CharType>
+ static size_t AllocationSize(unsigned length) {
+ CHECK_LE(length,
+ ((std::numeric_limits<unsigned>::max() - sizeof(StringImpl)) /
+ sizeof(CharType)));
+ return sizeof(StringImpl) + length * sizeof(CharType);
+ }
+
+ scoped_refptr<StringImpl> Replace(UChar pattern,
+ const LChar* replacement,
+ unsigned replacement_length);
+ scoped_refptr<StringImpl> Replace(UChar pattern,
+ const UChar* replacement,
+ unsigned replacement_length);
+
+ template <class UCharPredicate>
+ scoped_refptr<StringImpl> StripMatchedCharacters(UCharPredicate);
+ template <typename CharType, class UCharPredicate>
+ scoped_refptr<StringImpl> SimplifyMatchedCharactersToSpace(UCharPredicate,
+ StripBehavior);
+ NEVER_INLINE unsigned HashSlowCase() const;
+
+ void DestroyIfNotStatic() const;
+ void UpdateContainsOnlyASCII() const;
+
+#if DCHECK_IS_ON()
+ std::string AsciiForDebugging() const;
+#endif
+
+ static unsigned highest_static_string_length_;
+
+#if DCHECK_IS_ON()
+ void AssertHashIsCorrect() {
+ DCHECK(HasHash());
+ DCHECK_EQ(ExistingHash(), StringHasher::ComputeHashAndMaskTop8Bits(
+ Characters8(), length()));
+ }
+#endif
+
+ private:
+#if DCHECK_IS_ON()
+ mutable ThreadRestrictionVerifier verifier_;
+#endif
+ mutable unsigned ref_count_;
+ const unsigned length_;
+ mutable unsigned hash_ : 24;
+ mutable unsigned contains_only_ascii_ : 1;
+ mutable unsigned needs_ascii_check_ : 1;
+ unsigned is_atomic_ : 1;
+ const unsigned is8_bit_ : 1;
+ const unsigned is_static_ : 1;
+
+ DISALLOW_COPY_AND_ASSIGN(StringImpl);
+};
+
+template <>
+ALWAYS_INLINE const LChar* StringImpl::GetCharacters<LChar>() const {
+ return Characters8();
+}
+
+template <>
+ALWAYS_INLINE const UChar* StringImpl::GetCharacters<UChar>() const {
+ return Characters16();
+}
+
+WTF_EXPORT bool Equal(const StringImpl*, const StringImpl*);
+WTF_EXPORT bool Equal(const StringImpl*, const LChar*);
+inline bool Equal(const StringImpl* a, const char* b) {
+ return Equal(a, reinterpret_cast<const LChar*>(b));
+}
+WTF_EXPORT bool Equal(const StringImpl*, const LChar*, unsigned);
+WTF_EXPORT bool Equal(const StringImpl*, const UChar*, unsigned);
+inline bool Equal(const StringImpl* a, const char* b, unsigned length) {
+ return Equal(a, reinterpret_cast<const LChar*>(b), length);
+}
+inline bool Equal(const LChar* a, StringImpl* b) {
+ return Equal(b, a);
+}
+inline bool Equal(const char* a, StringImpl* b) {
+ return Equal(b, reinterpret_cast<const LChar*>(a));
+}
+WTF_EXPORT bool EqualNonNull(const StringImpl* a, const StringImpl* b);
+
+ALWAYS_INLINE bool StringImpl::ContainsOnlyASCII() const {
+ if (needs_ascii_check_)
+ UpdateContainsOnlyASCII();
+ return contains_only_ascii_;
+}
+
+template <typename CharType>
+ALWAYS_INLINE bool Equal(const CharType* a,
+ const CharType* b,
+ unsigned length) {
+ return !memcmp(a, b, length * sizeof(CharType));
+}
+
+ALWAYS_INLINE bool Equal(const LChar* a, const UChar* b, unsigned length) {
+ for (unsigned i = 0; i < length; ++i) {
+ if (a[i] != b[i])
+ return false;
+ }
+ return true;
+}
+
+ALWAYS_INLINE bool Equal(const UChar* a, const LChar* b, unsigned length) {
+ return Equal(b, a, length);
+}
+
+// Unicode aware case insensitive string matching. Non-ASCII characters might
+// match to ASCII characters. These functions are rarely used to implement web
+// platform features.
+// These functions are deprecated. Use EqualIgnoringASCIICase(), or introduce
+// EqualIgnoringUnicodeCase(). See crbug.com/627682
+WTF_EXPORT bool DeprecatedEqualIgnoringCase(const LChar*,
+ const LChar*,
+ unsigned length);
+WTF_EXPORT bool DeprecatedEqualIgnoringCase(const UChar*,
+ const LChar*,
+ unsigned length);
+inline bool DeprecatedEqualIgnoringCase(const LChar* a,
+ const UChar* b,
+ unsigned length) {
+ return DeprecatedEqualIgnoringCase(b, a, length);
+}
+WTF_EXPORT bool DeprecatedEqualIgnoringCase(const UChar*,
+ const UChar*,
+ unsigned length);
+
+WTF_EXPORT bool EqualIgnoringNullity(StringImpl*, StringImpl*);
+
+template <typename CharacterTypeA, typename CharacterTypeB>
+inline bool EqualIgnoringASCIICase(const CharacterTypeA* a,
+ const CharacterTypeB* b,
+ unsigned length) {
+ for (unsigned i = 0; i < length; ++i) {
+ if (ToASCIILower(a[i]) != ToASCIILower(b[i]))
+ return false;
+ }
+ return true;
+}
+
+WTF_EXPORT int CodePointCompareIgnoringASCIICase(const StringImpl*,
+ const LChar*);
+
+inline size_t Find(const LChar* characters,
+ unsigned length,
+ LChar match_character,
+ unsigned index = 0) {
+ // Some clients rely on being able to pass index >= length.
+ if (index >= length)
+ return kNotFound;
+ const LChar* found = static_cast<const LChar*>(
+ memchr(characters + index, match_character, length - index));
+ return found ? found - characters : kNotFound;
+}
+
+inline size_t Find(const UChar* characters,
+ unsigned length,
+ UChar match_character,
+ unsigned index = 0) {
+ while (index < length) {
+ if (characters[index] == match_character)
+ return index;
+ ++index;
+ }
+ return kNotFound;
+}
+
+ALWAYS_INLINE size_t Find(const UChar* characters,
+ unsigned length,
+ LChar match_character,
+ unsigned index = 0) {
+ return Find(characters, length, static_cast<UChar>(match_character), index);
+}
+
+inline size_t Find(const LChar* characters,
+ unsigned length,
+ UChar match_character,
+ unsigned index = 0) {
+ if (match_character & ~0xFF)
+ return kNotFound;
+ return Find(characters, length, static_cast<LChar>(match_character), index);
+}
+
+template <typename CharacterType>
+inline size_t Find(const CharacterType* characters,
+ unsigned length,
+ char match_character,
+ unsigned index = 0) {
+ return Find(characters, length, static_cast<LChar>(match_character), index);
+}
+
+inline size_t Find(const LChar* characters,
+ unsigned length,
+ CharacterMatchFunctionPtr match_function,
+ unsigned index = 0) {
+ while (index < length) {
+ if (match_function(characters[index]))
+ return index;
+ ++index;
+ }
+ return kNotFound;
+}
+
+inline size_t Find(const UChar* characters,
+ unsigned length,
+ CharacterMatchFunctionPtr match_function,
+ unsigned index = 0) {
+ while (index < length) {
+ if (match_function(characters[index]))
+ return index;
+ ++index;
+ }
+ return kNotFound;
+}
+
+template <typename CharacterType>
+inline size_t ReverseFind(const CharacterType* characters,
+ unsigned length,
+ CharacterType match_character,
+ unsigned index = UINT_MAX) {
+ if (!length)
+ return kNotFound;
+ if (index >= length)
+ index = length - 1;
+ while (characters[index] != match_character) {
+ if (!index--)
+ return kNotFound;
+ }
+ return index;
+}
+
+ALWAYS_INLINE size_t ReverseFind(const UChar* characters,
+ unsigned length,
+ LChar match_character,
+ unsigned index = UINT_MAX) {
+ return ReverseFind(characters, length, static_cast<UChar>(match_character),
+ index);
+}
+
+inline size_t ReverseFind(const LChar* characters,
+ unsigned length,
+ UChar match_character,
+ unsigned index = UINT_MAX) {
+ if (match_character & ~0xFF)
+ return kNotFound;
+ return ReverseFind(characters, length, static_cast<LChar>(match_character),
+ index);
+}
+
+inline size_t StringImpl::Find(LChar character, unsigned start) {
+ if (Is8Bit())
+ return WTF::Find(Characters8(), length_, character, start);
+ return WTF::Find(Characters16(), length_, character, start);
+}
+
+ALWAYS_INLINE size_t StringImpl::Find(char character, unsigned start) {
+ return Find(static_cast<LChar>(character), start);
+}
+
+inline size_t StringImpl::Find(UChar character, unsigned start) {
+ if (Is8Bit())
+ return WTF::Find(Characters8(), length_, character, start);
+ return WTF::Find(Characters16(), length_, character, start);
+}
+
+inline unsigned LengthOfNullTerminatedString(const UChar* string) {
+ size_t length = 0;
+ while (string[length] != UChar(0))
+ ++length;
+ CHECK_LE(length, std::numeric_limits<unsigned>::max());
+ return static_cast<unsigned>(length);
+}
+
+template <size_t inlineCapacity>
+bool EqualIgnoringNullity(const Vector<UChar, inlineCapacity>& a,
+ StringImpl* b) {
+ if (!b)
+ return !a.size();
+ if (a.size() != b->length())
+ return false;
+ if (b->Is8Bit())
+ return Equal(a.data(), b->Characters8(), b->length());
+ return Equal(a.data(), b->Characters16(), b->length());
+}
+
+template <typename CharacterType1, typename CharacterType2>
+static inline int CodePointCompare(unsigned l1,
+ unsigned l2,
+ const CharacterType1* c1,
+ const CharacterType2* c2) {
+ const unsigned lmin = l1 < l2 ? l1 : l2;
+ unsigned pos = 0;
+ while (pos < lmin && *c1 == *c2) {
+ ++c1;
+ ++c2;
+ ++pos;
+ }
+
+ if (pos < lmin)
+ return (c1[0] > c2[0]) ? 1 : -1;
+
+ if (l1 == l2)
+ return 0;
+
+ return (l1 > l2) ? 1 : -1;
+}
+
+static inline int CodePointCompare8(const StringImpl* string1,
+ const StringImpl* string2) {
+ return CodePointCompare(string1->length(), string2->length(),
+ string1->Characters8(), string2->Characters8());
+}
+
+static inline int CodePointCompare16(const StringImpl* string1,
+ const StringImpl* string2) {
+ return CodePointCompare(string1->length(), string2->length(),
+ string1->Characters16(), string2->Characters16());
+}
+
+static inline int CodePointCompare8To16(const StringImpl* string1,
+ const StringImpl* string2) {
+ return CodePointCompare(string1->length(), string2->length(),
+ string1->Characters8(), string2->Characters16());
+}
+
+static inline int CodePointCompare(const StringImpl* string1,
+ const StringImpl* string2) {
+ if (!string1)
+ return (string2 && string2->length()) ? -1 : 0;
+
+ if (!string2)
+ return string1->length() ? 1 : 0;
+
+ bool string1_is8_bit = string1->Is8Bit();
+ bool string2_is8_bit = string2->Is8Bit();
+ if (string1_is8_bit) {
+ if (string2_is8_bit)
+ return CodePointCompare8(string1, string2);
+ return CodePointCompare8To16(string1, string2);
+ }
+ if (string2_is8_bit)
+ return -CodePointCompare8To16(string2, string1);
+ return CodePointCompare16(string1, string2);
+}
+
+static inline bool IsSpaceOrNewline(UChar c) {
+ // Use IsASCIISpace() for basic Latin-1.
+ // This will include newlines, which aren't included in Unicode DirWS.
+ return c <= 0x7F
+ ? WTF::IsASCIISpace(c)
+ : WTF::Unicode::Direction(c) == WTF::Unicode::kWhiteSpaceNeutral;
+}
+
+inline scoped_refptr<StringImpl> StringImpl::IsolatedCopy() const {
+ if (Is8Bit())
+ return Create(Characters8(), length_);
+ return Create(Characters16(), length_);
+}
+
+template <typename BufferType>
+inline void StringImpl::AppendTo(BufferType& result,
+ unsigned start,
+ unsigned length) const {
+ unsigned number_of_characters_to_copy = std::min(length, length_ - start);
+ if (!number_of_characters_to_copy)
+ return;
+ if (Is8Bit())
+ result.Append(Characters8() + start, number_of_characters_to_copy);
+ else
+ result.Append(Characters16() + start, number_of_characters_to_copy);
+}
+
+template <typename BufferType>
+inline void StringImpl::PrependTo(BufferType& result,
+ unsigned start,
+ unsigned length) const {
+ unsigned number_of_characters_to_copy = std::min(length, length_ - start);
+ if (!number_of_characters_to_copy)
+ return;
+ if (Is8Bit())
+ result.Prepend(Characters8() + start, number_of_characters_to_copy);
+ else
+ result.Prepend(Characters16() + start, number_of_characters_to_copy);
+}
+
+// TODO(rob.buis) possibly find a better place for this method.
+// Turns a UChar32 to uppercase based on localeIdentifier.
+WTF_EXPORT UChar32 ToUpper(UChar32, const AtomicString& locale_identifier);
+
+struct StringHash;
+
+// StringHash is the default hash for StringImpl* and scoped_refptr<StringImpl>
+template <typename T>
+struct DefaultHash;
+template <>
+struct DefaultHash<StringImpl*> {
+ typedef StringHash Hash;
+};
+template <>
+struct DefaultHash<scoped_refptr<StringImpl>> {
+ typedef StringHash Hash;
+};
+
+} // namespace WTF
+
+using WTF::StringImpl;
+using WTF::kTextCaseASCIIInsensitive;
+using WTF::kTextCaseUnicodeInsensitive;
+using WTF::kTextCaseSensitive;
+using WTF::TextCaseSensitivity;
+using WTF::Equal;
+using WTF::EqualNonNull;
+using WTF::LengthOfNullTerminatedString;
+using WTF::ReverseFind;
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/string_impl_cf.cc b/chromium/third_party/blink/renderer/platform/wtf/text/string_impl_cf.cc
new file mode 100644
index 00000000000..5ba1b63868e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/string_impl_cf.cc
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2006, 2009, 2012 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "third_party/blink/renderer/platform/wtf/text/string_impl.h"
+
+#include "build/build_config.h"
+
+#if defined(OS_MACOSX)
+
+#include <CoreFoundation/CoreFoundation.h>
+#include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
+#include "third_party/blink/renderer/platform/wtf/retain_ptr.h"
+#include "third_party/blink/renderer/platform/wtf/threading.h"
+
+namespace WTF {
+
+namespace StringWrapperCFAllocator {
+
+static StringImpl* g_current_string;
+
+static const void* Retain(const void* info) {
+ return info;
+}
+
+static void Release(const void*) {
+ NOTREACHED();
+}
+
+static CFStringRef CopyDescription(const void*) {
+ return CFSTR("WTF::String-based allocator");
+}
+
+static void* Allocate(CFIndex size, CFOptionFlags, void*) {
+ StringImpl* underlying_string = 0;
+ if (IsMainThread()) {
+ underlying_string = g_current_string;
+ if (underlying_string) {
+ g_current_string = 0;
+ underlying_string
+ ->AddRef(); // Balanced by call to deref in deallocate below.
+ }
+ }
+ StringImpl** header = static_cast<StringImpl**>(WTF::Partitions::FastMalloc(
+ sizeof(StringImpl*) + size, WTF_HEAP_PROFILER_TYPE_NAME(StringImpl*)));
+ *header = underlying_string;
+ return header + 1;
+}
+
+static void* Reallocate(void* pointer, CFIndex new_size, CFOptionFlags, void*) {
+ size_t new_allocation_size = sizeof(StringImpl*) + new_size;
+ StringImpl** header = static_cast<StringImpl**>(pointer) - 1;
+ DCHECK(!*header);
+ header = static_cast<StringImpl**>(WTF::Partitions::FastRealloc(
+ header, new_allocation_size, WTF_HEAP_PROFILER_TYPE_NAME(StringImpl*)));
+ return header + 1;
+}
+
+static void DeallocateOnMainThread(void* header_pointer) {
+ StringImpl** header = static_cast<StringImpl**>(header_pointer);
+ StringImpl* underlying_string = *header;
+ DCHECK(underlying_string);
+ underlying_string->Release(); // Balanced by call to ref in allocate above.
+ WTF::Partitions::FastFree(header);
+}
+
+static void Deallocate(void* pointer, void*) {
+ StringImpl** header = static_cast<StringImpl**>(pointer) - 1;
+ StringImpl* underlying_string = *header;
+ if (!underlying_string) {
+ WTF::Partitions::FastFree(header);
+ } else {
+ if (!IsMainThread()) {
+ internal::CallOnMainThread(&DeallocateOnMainThread, header);
+ } else {
+ underlying_string
+ ->Release(); // Balanced by call to ref in allocate above.
+ WTF::Partitions::FastFree(header);
+ }
+ }
+}
+
+static CFIndex PreferredSize(CFIndex size, CFOptionFlags, void*) {
+ // FIXME: If FastMalloc provided a "good size" callback, we'd want to use it
+ // here. Note that this optimization would help performance for strings
+ // created with the allocator that are mutable, and those typically are only
+ // created by callers who make a new string using the old string's allocator,
+ // such as some of the call sites in CFURL.
+ return size;
+}
+
+static CFAllocatorRef Create() {
+ CFAllocatorContext context = {
+ 0, 0, Retain, Release, CopyDescription,
+ Allocate, Reallocate, Deallocate, PreferredSize};
+ return CFAllocatorCreate(0, &context);
+}
+
+static CFAllocatorRef Allocator() {
+ static CFAllocatorRef allocator = Create();
+ return allocator;
+}
+
+} // namespace StringWrapperCFAllocator
+
+RetainPtr<CFStringRef> StringImpl::CreateCFString() {
+ // Since garbage collection isn't compatible with custom allocators, we
+ // can't use the NoCopy variants of CFStringCreate*() when GC is enabled.
+ if (!length_ || !IsMainThread()) {
+ if (Is8Bit())
+ return AdoptCF(CFStringCreateWithBytes(
+ 0, reinterpret_cast<const UInt8*>(Characters8()), length_,
+ kCFStringEncodingISOLatin1, false));
+ return AdoptCF(CFStringCreateWithCharacters(
+ 0, reinterpret_cast<const UniChar*>(Characters16()), length_));
+ }
+ CFAllocatorRef allocator = StringWrapperCFAllocator::Allocator();
+
+ // Put pointer to the StringImpl in a global so the allocator can store it
+ // with the CFString.
+ DCHECK(!StringWrapperCFAllocator::g_current_string);
+ StringWrapperCFAllocator::g_current_string = this;
+
+ CFStringRef string;
+ if (Is8Bit())
+ string = CFStringCreateWithBytesNoCopy(
+ allocator, reinterpret_cast<const UInt8*>(Characters8()), length_,
+ kCFStringEncodingISOLatin1, false, kCFAllocatorNull);
+ else
+ string = CFStringCreateWithCharactersNoCopy(
+ allocator, reinterpret_cast<const UniChar*>(Characters16()), length_,
+ kCFAllocatorNull);
+ // CoreFoundation might not have to allocate anything, we clear currentString
+ // in case we did not execute allocate().
+ StringWrapperCFAllocator::g_current_string = 0;
+
+ return AdoptCF(string);
+}
+
+// On StringImpl creation we could check if the allocator is the
+// StringWrapperCFAllocator. If it is, then we could find the original
+// StringImpl and just return that. But to do that we'd have to compute the
+// offset from CFStringRef to the allocated block; the CFStringRef is *not* at
+// the start of an allocated block. Testing shows 1000x more calls to
+// createCFString than calls to the create functions with the appropriate
+// allocator, so it's probably not urgent optimize that case.
+
+} // namespace WTF
+
+#endif // defined(OS_MACOSX)
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/string_impl_mac.mm b/chromium/third_party/blink/renderer/platform/wtf/text/string_impl_mac.mm
new file mode 100644
index 00000000000..d2ac0e193ed
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/string_impl_mac.mm
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2006, 2009 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "third_party/blink/renderer/platform/wtf/text/string_impl.h"
+
+#import <CoreFoundation/CFBase.h>
+#import <Foundation/NSObject.h>
+#include "third_party/blink/renderer/platform/wtf/retain_ptr.h"
+
+namespace WTF {
+
+// Use HardAutorelease to return an object made by a CoreFoundation
+// "create" or "copy" function as an autoreleased and garbage collected
+// object. CF objects need to be "made collectable" for autorelease to work
+// properly under GC.
+
+static inline id HardAutorelease(CFTypeRef object) {
+ if (object)
+ CFMakeCollectable(object);
+ [(id)object autorelease];
+ return (id)object;
+}
+
+StringImpl::operator NSString*() {
+ return HardAutorelease(CreateCFString().LeakRef());
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/string_impl_test.cc b/chromium/third_party/blink/renderer/platform/wtf/text/string_impl_test.cc
new file mode 100644
index 00000000000..6f967bc148b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/string_impl_test.cc
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2012 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/wtf/text/string_impl.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace WTF {
+
+TEST(StringImplTest, Create8Bit) {
+ scoped_refptr<StringImpl> test_string_impl = StringImpl::Create("1224");
+ EXPECT_TRUE(test_string_impl->Is8Bit());
+}
+
+TEST(StringImplTest, Latin1CaseFoldTable) {
+ LChar symbol = 0xff;
+ while (symbol--) {
+ EXPECT_EQ(Unicode::FoldCase(symbol),
+ StringImpl::kLatin1CaseFoldTable[symbol]);
+ }
+}
+
+TEST(StringImplTest, LowerASCII) {
+ scoped_refptr<StringImpl> test_string_impl = StringImpl::Create("link");
+ EXPECT_TRUE(test_string_impl->Is8Bit());
+ EXPECT_TRUE(StringImpl::Create("a\xE1")->Is8Bit());
+
+ EXPECT_TRUE(Equal(test_string_impl.get(),
+ StringImpl::Create("link")->LowerASCII().get()));
+ EXPECT_TRUE(Equal(test_string_impl.get(),
+ StringImpl::Create("LINK")->LowerASCII().get()));
+ EXPECT_TRUE(Equal(test_string_impl.get(),
+ StringImpl::Create("lInk")->LowerASCII().get()));
+
+ EXPECT_TRUE(Equal(StringImpl::Create("LINK")->LowerUnicode().get(),
+ StringImpl::Create("LINK")->LowerASCII().get()));
+ EXPECT_TRUE(Equal(StringImpl::Create("lInk")->LowerUnicode().get(),
+ StringImpl::Create("lInk")->LowerASCII().get()));
+
+ EXPECT_TRUE(Equal(StringImpl::Create("a\xE1").get(),
+ StringImpl::Create("A\xE1")->LowerASCII().get()));
+ EXPECT_TRUE(Equal(StringImpl::Create("a\xC1").get(),
+ StringImpl::Create("A\xC1")->LowerASCII().get()));
+
+ EXPECT_FALSE(Equal(StringImpl::Create("a\xE1").get(),
+ StringImpl::Create("a\xC1")->LowerASCII().get()));
+ EXPECT_FALSE(Equal(StringImpl::Create("A\xE1").get(),
+ StringImpl::Create("A\xC1")->LowerASCII().get()));
+
+ static const UChar kTest[5] = {0x006c, 0x0069, 0x006e, 0x006b, 0}; // link
+ static const UChar kTestCapitalized[5] = {0x004c, 0x0049, 0x004e, 0x004b,
+ 0}; // LINK
+
+ scoped_refptr<StringImpl> test_string_impl16 = StringImpl::Create(kTest, 4);
+ EXPECT_FALSE(test_string_impl16->Is8Bit());
+
+ EXPECT_TRUE(Equal(test_string_impl16.get(),
+ StringImpl::Create(kTest, 4)->LowerASCII().get()));
+ EXPECT_TRUE(
+ Equal(test_string_impl16.get(),
+ StringImpl::Create(kTestCapitalized, 4)->LowerASCII().get()));
+
+ static const UChar kTestWithNonASCII[3] = {0x0061, 0x00e1, 0}; // a\xE1
+ static const UChar kTestWithNonASCIIComparison[3] = {0x0061, 0x00c1,
+ 0}; // a\xC1
+ static const UChar kTestWithNonASCIICapitalized[3] = {0x0041, 0x00e1,
+ 0}; // A\xE1
+
+ // Make sure we support scoped_refptr<const StringImpl>.
+ scoped_refptr<const StringImpl> const_ref = test_string_impl->IsolatedCopy();
+ DCHECK(const_ref->HasOneRef());
+ EXPECT_TRUE(Equal(
+ StringImpl::Create(kTestWithNonASCII, 2).get(),
+ StringImpl::Create(kTestWithNonASCIICapitalized, 2)->LowerASCII().get()));
+ EXPECT_FALSE(Equal(
+ StringImpl::Create(kTestWithNonASCII, 2).get(),
+ StringImpl::Create(kTestWithNonASCIIComparison, 2)->LowerASCII().get()));
+}
+
+TEST(StringImplTest, UpperASCII) {
+ scoped_refptr<StringImpl> test_string_impl = StringImpl::Create("LINK");
+ EXPECT_TRUE(test_string_impl->Is8Bit());
+ EXPECT_TRUE(StringImpl::Create("a\xE1")->Is8Bit());
+
+ EXPECT_TRUE(Equal(test_string_impl.get(),
+ StringImpl::Create("link")->UpperASCII().get()));
+ EXPECT_TRUE(Equal(test_string_impl.get(),
+ StringImpl::Create("LINK")->UpperASCII().get()));
+ EXPECT_TRUE(Equal(test_string_impl.get(),
+ StringImpl::Create("lInk")->UpperASCII().get()));
+
+ EXPECT_TRUE(Equal(StringImpl::Create("LINK")->UpperUnicode().get(),
+ StringImpl::Create("LINK")->UpperASCII().get()));
+ EXPECT_TRUE(Equal(StringImpl::Create("lInk")->UpperUnicode().get(),
+ StringImpl::Create("lInk")->UpperASCII().get()));
+
+ EXPECT_TRUE(Equal(StringImpl::Create("A\xE1").get(),
+ StringImpl::Create("a\xE1")->UpperASCII().get()));
+ EXPECT_TRUE(Equal(StringImpl::Create("A\xC1").get(),
+ StringImpl::Create("a\xC1")->UpperASCII().get()));
+
+ EXPECT_FALSE(Equal(StringImpl::Create("A\xE1").get(),
+ StringImpl::Create("a\xC1")->UpperASCII().get()));
+ EXPECT_FALSE(Equal(StringImpl::Create("A\xE1").get(),
+ StringImpl::Create("A\xC1")->UpperASCII().get()));
+
+ static const UChar kTest[5] = {0x006c, 0x0069, 0x006e, 0x006b, 0}; // link
+ static const UChar kTestCapitalized[5] = {0x004c, 0x0049, 0x004e, 0x004b,
+ 0}; // LINK
+
+ scoped_refptr<StringImpl> test_string_impl16 =
+ StringImpl::Create(kTestCapitalized, 4);
+ EXPECT_FALSE(test_string_impl16->Is8Bit());
+
+ EXPECT_TRUE(Equal(test_string_impl16.get(),
+ StringImpl::Create(kTest, 4)->UpperASCII().get()));
+ EXPECT_TRUE(
+ Equal(test_string_impl16.get(),
+ StringImpl::Create(kTestCapitalized, 4)->UpperASCII().get()));
+
+ static const UChar kTestWithNonASCII[3] = {0x0061, 0x00e1, 0}; // a\xE1
+ static const UChar kTestWithNonASCIIComparison[3] = {0x0061, 0x00c1,
+ 0}; // a\xC1
+ static const UChar kTestWithNonASCIICapitalized[3] = {0x0041, 0x00e1,
+ 0}; // A\xE1
+
+ // Make sure we support scoped_refptr<const StringImpl>.
+ scoped_refptr<const StringImpl> const_ref = test_string_impl->IsolatedCopy();
+ DCHECK(const_ref->HasOneRef());
+ EXPECT_TRUE(
+ Equal(StringImpl::Create(kTestWithNonASCIICapitalized, 2).get(),
+ StringImpl::Create(kTestWithNonASCII, 2)->UpperASCII().get()));
+ EXPECT_FALSE(Equal(
+ StringImpl::Create(kTestWithNonASCIICapitalized, 2).get(),
+ StringImpl::Create(kTestWithNonASCIIComparison, 2)->UpperASCII().get()));
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/string_mac.mm b/chromium/third_party/blink/renderer/platform/wtf/text/string_mac.mm
new file mode 100644
index 00000000000..d1690b69299
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/string_mac.mm
@@ -0,0 +1,53 @@
+/**
+ * Copyright (C) 2006 Apple Computer, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+#include <CoreFoundation/CFString.h>
+
+namespace WTF {
+
+String::String(NSString* str) {
+ if (!str)
+ return;
+
+ CFIndex size = CFStringGetLength(reinterpret_cast<CFStringRef>(str));
+ if (size == 0)
+ impl_ = StringImpl::empty_;
+ else {
+ Vector<LChar, 1024> lchar_buffer(size);
+ CFIndex used_buf_len;
+ CFIndex convertedsize =
+ CFStringGetBytes(reinterpret_cast<CFStringRef>(str),
+ CFRangeMake(0, size), kCFStringEncodingISOLatin1, 0,
+ false, lchar_buffer.data(), size, &used_buf_len);
+ if ((convertedsize == size) && (used_buf_len == size)) {
+ impl_ = StringImpl::Create(lchar_buffer.data(), size);
+ return;
+ }
+
+ Vector<UChar, 1024> uchar_buffer(size);
+ CFStringGetCharacters(reinterpret_cast<CFStringRef>(str),
+ CFRangeMake(0, size), uchar_buffer.data());
+ impl_ = StringImpl::Create(uchar_buffer.data(), size);
+ }
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/string_operators.h b/chromium/third_party/blink/renderer/platform/wtf/text/string_operators.h
new file mode 100644
index 00000000000..d4447d37c8c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/string_operators.h
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights
+ * reserved.
+ * Copyright (C) Research In Motion Limited 2011. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_STRING_OPERATORS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_STRING_OPERATORS_H_
+
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_concatenate.h"
+
+namespace WTF {
+
+template <typename StringType1, typename StringType2>
+class StringAppend final {
+ STACK_ALLOCATED();
+
+ public:
+ StringAppend(StringType1 string1, StringType2 string2);
+
+ operator String() const;
+ operator AtomicString() const;
+
+ unsigned length() const;
+ bool Is8Bit() const;
+
+ void WriteTo(LChar* destination) const;
+ void WriteTo(UChar* destination) const;
+
+ private:
+ const StringType1 string1_;
+ const StringType2 string2_;
+};
+
+template <typename StringType1, typename StringType2>
+StringAppend<StringType1, StringType2>::StringAppend(StringType1 string1,
+ StringType2 string2)
+ : string1_(string1), string2_(string2) {}
+
+template <typename StringType1, typename StringType2>
+StringAppend<StringType1, StringType2>::operator String() const {
+ if (Is8Bit()) {
+ LChar* buffer;
+ scoped_refptr<StringImpl> result =
+ StringImpl::CreateUninitialized(length(), buffer);
+ WriteTo(buffer);
+ return result;
+ }
+ UChar* buffer;
+ scoped_refptr<StringImpl> result =
+ StringImpl::CreateUninitialized(length(), buffer);
+ WriteTo(buffer);
+ return result;
+}
+
+template <typename StringType1, typename StringType2>
+StringAppend<StringType1, StringType2>::operator AtomicString() const {
+ return AtomicString(static_cast<String>(*this));
+}
+
+template <typename StringType1, typename StringType2>
+bool StringAppend<StringType1, StringType2>::Is8Bit() const {
+ StringTypeAdapter<StringType1> adapter1(string1_);
+ StringTypeAdapter<StringType2> adapter2(string2_);
+ return adapter1.Is8Bit() && adapter2.Is8Bit();
+}
+
+template <typename StringType1, typename StringType2>
+void StringAppend<StringType1, StringType2>::WriteTo(LChar* destination) const {
+ DCHECK(Is8Bit());
+ StringTypeAdapter<StringType1> adapter1(string1_);
+ StringTypeAdapter<StringType2> adapter2(string2_);
+ adapter1.WriteTo(destination);
+ adapter2.WriteTo(destination + adapter1.length());
+}
+
+template <typename StringType1, typename StringType2>
+void StringAppend<StringType1, StringType2>::WriteTo(UChar* destination) const {
+ StringTypeAdapter<StringType1> adapter1(string1_);
+ StringTypeAdapter<StringType2> adapter2(string2_);
+ adapter1.WriteTo(destination);
+ adapter2.WriteTo(destination + adapter1.length());
+}
+
+template <typename StringType1, typename StringType2>
+unsigned StringAppend<StringType1, StringType2>::length() const {
+ StringTypeAdapter<StringType1> adapter1(string1_);
+ StringTypeAdapter<StringType2> adapter2(string2_);
+ unsigned total = adapter1.length() + adapter2.length();
+ // Guard against overflow.
+ CHECK_GE(total, adapter1.length());
+ CHECK_GE(total, adapter2.length());
+ return total;
+}
+
+template <typename StringType1, typename StringType2>
+class StringTypeAdapter<StringAppend<StringType1, StringType2>> {
+ STACK_ALLOCATED();
+
+ public:
+ StringTypeAdapter<StringAppend<StringType1, StringType2>>(
+ const StringAppend<StringType1, StringType2>& buffer)
+ : buffer_(buffer) {}
+
+ unsigned length() const { return buffer_.length(); }
+ bool Is8Bit() const { return buffer_.Is8Bit(); }
+
+ void WriteTo(LChar* destination) const { buffer_.WriteTo(destination); }
+ void WriteTo(UChar* destination) const { buffer_.WriteTo(destination); }
+
+ private:
+ const StringAppend<StringType1, StringType2>& buffer_;
+};
+
+inline StringAppend<const char*, String> operator+(const char* string1,
+ const String& string2) {
+ return StringAppend<const char*, String>(string1, string2);
+}
+
+inline StringAppend<const char*, AtomicString> operator+(
+ const char* string1,
+ const AtomicString& string2) {
+ return StringAppend<const char*, AtomicString>(string1, string2);
+}
+
+inline StringAppend<const char*, StringView> operator+(
+ const char* string1,
+ const StringView& string2) {
+ return StringAppend<const char*, StringView>(string1, string2);
+}
+
+template <typename U, typename V>
+inline StringAppend<const char*, StringAppend<U, V>> operator+(
+ const char* string1,
+ const StringAppend<U, V>& string2) {
+ return StringAppend<const char*, StringAppend<U, V>>(string1, string2);
+}
+
+inline StringAppend<const UChar*, String> operator+(const UChar* string1,
+ const String& string2) {
+ return StringAppend<const UChar*, String>(string1, string2);
+}
+
+inline StringAppend<const UChar*, AtomicString> operator+(
+ const UChar* string1,
+ const AtomicString& string2) {
+ return StringAppend<const UChar*, AtomicString>(string1, string2);
+}
+
+inline StringAppend<const UChar*, StringView> operator+(
+ const UChar* string1,
+ const StringView& string2) {
+ return StringAppend<const UChar*, StringView>(string1, string2);
+}
+
+template <typename U, typename V>
+inline StringAppend<const UChar*, StringAppend<U, V>> operator+(
+ const UChar* string1,
+ const StringAppend<U, V>& string2) {
+ return StringAppend<const UChar*, StringAppend<U, V>>(string1, string2);
+}
+
+template <typename T>
+StringAppend<String, T> operator+(const String& string1, T string2) {
+ return StringAppend<String, T>(string1, string2);
+}
+
+template <typename T>
+StringAppend<AtomicString, T> operator+(const AtomicString& string1,
+ T string2) {
+ return StringAppend<AtomicString, T>(string1, string2);
+}
+
+template <typename T>
+StringAppend<StringView, T> operator+(const StringView& string1, T string2) {
+ return StringAppend<StringView, T>(string1, string2);
+}
+
+template <typename U, typename V, typename W>
+StringAppend<StringAppend<U, V>, W> operator+(const StringAppend<U, V>& string1,
+ W string2) {
+ return StringAppend<StringAppend<U, V>, W>(string1, string2);
+}
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_STRING_OPERATORS_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/string_statics.cc b/chromium/third_party/blink/renderer/platform/wtf/text/string_statics.cc
new file mode 100644
index 00000000000..d0013cde895
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/string_statics.cc
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2010 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 "third_party/blink/renderer/platform/wtf/text/string_statics.h"
+
+#include "third_party/blink/renderer/platform/wtf/dynamic_annotations.h"
+#include "third_party/blink/renderer/platform/wtf/static_constructors.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_impl.h"
+
+namespace WTF {
+
+WTF_EXPORT DEFINE_GLOBAL(AtomicString, g_null_atom);
+WTF_EXPORT DEFINE_GLOBAL(AtomicString, g_empty_atom);
+WTF_EXPORT DEFINE_GLOBAL(AtomicString, g_star_atom);
+WTF_EXPORT DEFINE_GLOBAL(AtomicString, g_xml_atom);
+WTF_EXPORT DEFINE_GLOBAL(AtomicString, g_xmlns_atom);
+WTF_EXPORT DEFINE_GLOBAL(AtomicString, g_xlink_atom);
+WTF_EXPORT DEFINE_GLOBAL(AtomicString, g_http_atom);
+WTF_EXPORT DEFINE_GLOBAL(AtomicString, g_https_atom);
+
+// This is not an AtomicString because it is unlikely to be used as an
+// event/element/attribute name, so it shouldn't pollute the AtomicString hash
+// table.
+WTF_EXPORT DEFINE_GLOBAL(String, g_xmlns_with_colon);
+
+WTF_EXPORT DEFINE_GLOBAL(String, g_empty_string);
+WTF_EXPORT DEFINE_GLOBAL(String, g_empty_string16_bit);
+
+NEVER_INLINE unsigned StringImpl::HashSlowCase() const {
+ if (Is8Bit())
+ SetHash(StringHasher::ComputeHashAndMaskTop8Bits(Characters8(), length_));
+ else
+ SetHash(StringHasher::ComputeHashAndMaskTop8Bits(Characters16(), length_));
+ return ExistingHash();
+}
+
+void AtomicString::Init() {
+ DCHECK(IsMainThread());
+
+ new (NotNull, (void*)&g_null_atom) AtomicString;
+ new (NotNull, (void*)&g_empty_atom) AtomicString("");
+}
+
+template <unsigned charactersCount>
+scoped_refptr<StringImpl> AddStaticASCIILiteral(
+ const char (&characters)[charactersCount]) {
+ unsigned length = charactersCount - 1;
+ unsigned hash = StringHasher::ComputeHashAndMaskTop8Bits(
+ reinterpret_cast<const LChar*>(characters), length);
+ return base::AdoptRef(StringImpl::CreateStatic(characters, length, hash));
+}
+
+void StringStatics::Init() {
+ DCHECK(IsMainThread());
+
+ StringImpl::InitStatics();
+ new (NotNull, (void*)&g_empty_string) String(StringImpl::empty_);
+ new (NotNull, (void*)&g_empty_string16_bit) String(StringImpl::empty16_bit_);
+
+ // FIXME: These should be allocated at compile time.
+ new (NotNull, (void*)&g_star_atom) AtomicString("*");
+ new (NotNull, (void*)&g_xml_atom) AtomicString(AddStaticASCIILiteral("xml"));
+ new (NotNull, (void*)&g_xmlns_atom)
+ AtomicString(AddStaticASCIILiteral("xmlns"));
+ new (NotNull, (void*)&g_xlink_atom)
+ AtomicString(AddStaticASCIILiteral("xlink"));
+ new (NotNull, (void*)&g_xmlns_with_colon) String("xmlns:");
+ new (NotNull, (void*)&g_http_atom)
+ AtomicString(AddStaticASCIILiteral("http"));
+ new (NotNull, (void*)&g_https_atom)
+ AtomicString(AddStaticASCIILiteral("https"));
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/string_statics.h b/chromium/third_party/blink/renderer/platform/wtf/text/string_statics.h
new file mode 100644
index 00000000000..0fcd19f80e4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/string_statics.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_STRING_STATICS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_STRING_STATICS_H_
+
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_export.h"
+
+namespace WTF {
+
+class StringStatics {
+ STATIC_ONLY(StringStatics);
+
+ public:
+ WTF_EXPORT static void Init();
+};
+}
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/string_to_number.cc b/chromium/third_party/blink/renderer/platform/wtf/text/string_to_number.cc
new file mode 100644
index 00000000000..a3d4dd927e0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/string_to_number.cc
@@ -0,0 +1,320 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/wtf/text/string_to_number.h"
+
+#include <type_traits>
+#include "third_party/blink/renderer/platform/wtf/ascii_ctype.h"
+#include "third_party/blink/renderer/platform/wtf/dtoa.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_impl.h"
+
+namespace WTF {
+
+template <int base>
+bool IsCharacterAllowedInBase(UChar);
+
+template <>
+bool IsCharacterAllowedInBase<10>(UChar c) {
+ return IsASCIIDigit(c);
+}
+
+template <>
+bool IsCharacterAllowedInBase<16>(UChar c) {
+ return IsASCIIHexDigit(c);
+}
+
+template <typename IntegralType, typename CharType, int base>
+static inline IntegralType ToIntegralType(const CharType* data,
+ size_t length,
+ NumberParsingOptions options,
+ NumberParsingResult* parsing_result) {
+ static_assert(std::is_integral<IntegralType>::value,
+ "IntegralType must be an integral type.");
+ static constexpr IntegralType kIntegralMax =
+ std::numeric_limits<IntegralType>::max();
+ static constexpr IntegralType kIntegralMin =
+ std::numeric_limits<IntegralType>::min();
+ static constexpr bool kIsSigned =
+ std::numeric_limits<IntegralType>::is_signed;
+ DCHECK(parsing_result);
+
+ IntegralType value = 0;
+ NumberParsingResult result = NumberParsingResult::kError;
+ bool is_negative = false;
+ bool overflow = false;
+ const bool accept_minus = kIsSigned || options.AcceptMinusZeroForUnsigned();
+
+ if (!data)
+ goto bye;
+
+ if (options.AcceptWhitespace()) {
+ while (length && IsSpaceOrNewline(*data)) {
+ --length;
+ ++data;
+ }
+ }
+
+ if (accept_minus && length && *data == '-') {
+ --length;
+ ++data;
+ is_negative = true;
+ } else if (length && options.AcceptLeadingPlus() && *data == '+') {
+ --length;
+ ++data;
+ }
+
+ if (!length || !IsCharacterAllowedInBase<base>(*data))
+ goto bye;
+
+ while (length && IsCharacterAllowedInBase<base>(*data)) {
+ --length;
+ IntegralType digit_value;
+ CharType c = *data;
+ if (IsASCIIDigit(c))
+ digit_value = c - '0';
+ else if (c >= 'a')
+ digit_value = c - 'a' + 10;
+ else
+ digit_value = c - 'A' + 10;
+
+ if (is_negative) {
+ if (!kIsSigned && options.AcceptMinusZeroForUnsigned()) {
+ if (digit_value != 0) {
+ result = NumberParsingResult::kError;
+ overflow = true;
+ }
+ } else {
+ // Overflow condition:
+ // value * base - digit_value < kIntegralMin
+ // <=> value < (kIntegralMin + digit_value) / base
+ // We must be careful of rounding errors here, but the default rounding
+ // mode (round to zero) works well, so we can use this formula as-is.
+ if (value < (kIntegralMin + digit_value) / base) {
+ result = NumberParsingResult::kOverflowMin;
+ overflow = true;
+ }
+ }
+ } else {
+ // Overflow condition:
+ // value * base + digit_value > kIntegralMax
+ // <=> value > (kIntegralMax + digit_value) / base
+ // Ditto regarding rounding errors.
+ if (value > (kIntegralMax - digit_value) / base) {
+ result = NumberParsingResult::kOverflowMax;
+ overflow = true;
+ }
+ }
+
+ if (!overflow) {
+ if (is_negative)
+ value = base * value - digit_value;
+ else
+ value = base * value + digit_value;
+ }
+ ++data;
+ }
+
+ if (options.AcceptWhitespace()) {
+ while (length && IsSpaceOrNewline(*data)) {
+ --length;
+ ++data;
+ }
+ }
+
+ if (length == 0 || options.AcceptTrailingGarbage()) {
+ if (!overflow)
+ result = NumberParsingResult::kSuccess;
+ } else {
+ // Even if we detected overflow, we return kError for trailing garbage.
+ result = NumberParsingResult::kError;
+ }
+bye:
+ *parsing_result = result;
+ return result == NumberParsingResult::kSuccess ? value : 0;
+}
+
+template <typename IntegralType, typename CharType, int base>
+static inline IntegralType ToIntegralType(const CharType* data,
+ size_t length,
+ NumberParsingOptions options,
+ bool* ok) {
+ NumberParsingResult result;
+ IntegralType value = ToIntegralType<IntegralType, CharType, base>(
+ data, length, options, &result);
+ if (ok)
+ *ok = result == NumberParsingResult::kSuccess;
+ return value;
+}
+
+unsigned CharactersToUInt(const LChar* data,
+ size_t length,
+ NumberParsingOptions options,
+ NumberParsingResult* result) {
+ return ToIntegralType<unsigned, LChar, 10>(data, length, options, result);
+}
+
+unsigned CharactersToUInt(const UChar* data,
+ size_t length,
+ NumberParsingOptions options,
+ NumberParsingResult* result) {
+ return ToIntegralType<unsigned, UChar, 10>(data, length, options, result);
+}
+
+unsigned HexCharactersToUInt(const LChar* data,
+ size_t length,
+ NumberParsingOptions options,
+ bool* ok) {
+ return ToIntegralType<unsigned, LChar, 16>(data, length, options, ok);
+}
+
+unsigned HexCharactersToUInt(const UChar* data,
+ size_t length,
+ NumberParsingOptions options,
+ bool* ok) {
+ return ToIntegralType<unsigned, UChar, 16>(data, length, options, ok);
+}
+
+int CharactersToInt(const LChar* data,
+ size_t length,
+ NumberParsingOptions options,
+ bool* ok) {
+ return ToIntegralType<int, LChar, 10>(data, length, options, ok);
+}
+
+int CharactersToInt(const UChar* data,
+ size_t length,
+ NumberParsingOptions options,
+ bool* ok) {
+ return ToIntegralType<int, UChar, 10>(data, length, options, ok);
+}
+
+unsigned CharactersToUInt(const LChar* data,
+ size_t length,
+ NumberParsingOptions options,
+ bool* ok) {
+ return ToIntegralType<unsigned, LChar, 10>(data, length, options, ok);
+}
+
+unsigned CharactersToUInt(const UChar* data,
+ size_t length,
+ NumberParsingOptions options,
+ bool* ok) {
+ return ToIntegralType<unsigned, UChar, 10>(data, length, options, ok);
+}
+
+int64_t CharactersToInt64(const LChar* data,
+ size_t length,
+ NumberParsingOptions options,
+ bool* ok) {
+ return ToIntegralType<int64_t, LChar, 10>(data, length, options, ok);
+}
+
+int64_t CharactersToInt64(const UChar* data,
+ size_t length,
+ NumberParsingOptions options,
+ bool* ok) {
+ return ToIntegralType<int64_t, UChar, 10>(data, length, options, ok);
+}
+
+uint64_t CharactersToUInt64(const LChar* data,
+ size_t length,
+ NumberParsingOptions options,
+ bool* ok) {
+ return ToIntegralType<uint64_t, LChar, 10>(data, length, options, ok);
+}
+
+uint64_t CharactersToUInt64(const UChar* data,
+ size_t length,
+ NumberParsingOptions options,
+ bool* ok) {
+ return ToIntegralType<uint64_t, UChar, 10>(data, length, options, ok);
+}
+
+enum TrailingJunkPolicy { kDisallowTrailingJunk, kAllowTrailingJunk };
+
+template <typename CharType, TrailingJunkPolicy policy>
+static inline double ToDoubleType(const CharType* data,
+ size_t length,
+ bool* ok,
+ size_t& parsed_length) {
+ size_t leading_spaces_length = 0;
+ while (leading_spaces_length < length &&
+ IsASCIISpace(data[leading_spaces_length]))
+ ++leading_spaces_length;
+
+ double number = ParseDouble(data + leading_spaces_length,
+ length - leading_spaces_length, parsed_length);
+ if (!parsed_length) {
+ if (ok)
+ *ok = false;
+ return 0.0;
+ }
+
+ parsed_length += leading_spaces_length;
+ if (ok)
+ *ok = policy == kAllowTrailingJunk || parsed_length == length;
+ return number;
+}
+
+double CharactersToDouble(const LChar* data, size_t length, bool* ok) {
+ size_t parsed_length;
+ return ToDoubleType<LChar, kDisallowTrailingJunk>(data, length, ok,
+ parsed_length);
+}
+
+double CharactersToDouble(const UChar* data, size_t length, bool* ok) {
+ size_t parsed_length;
+ return ToDoubleType<UChar, kDisallowTrailingJunk>(data, length, ok,
+ parsed_length);
+}
+
+double CharactersToDouble(const LChar* data,
+ size_t length,
+ size_t& parsed_length) {
+ return ToDoubleType<LChar, kAllowTrailingJunk>(data, length, nullptr,
+ parsed_length);
+}
+
+double CharactersToDouble(const UChar* data,
+ size_t length,
+ size_t& parsed_length) {
+ return ToDoubleType<UChar, kAllowTrailingJunk>(data, length, nullptr,
+ parsed_length);
+}
+
+float CharactersToFloat(const LChar* data, size_t length, bool* ok) {
+ // FIXME: This will return ok even when the string fits into a double but
+ // not a float.
+ size_t parsed_length;
+ return static_cast<float>(ToDoubleType<LChar, kDisallowTrailingJunk>(
+ data, length, ok, parsed_length));
+}
+
+float CharactersToFloat(const UChar* data, size_t length, bool* ok) {
+ // FIXME: This will return ok even when the string fits into a double but
+ // not a float.
+ size_t parsed_length;
+ return static_cast<float>(ToDoubleType<UChar, kDisallowTrailingJunk>(
+ data, length, ok, parsed_length));
+}
+
+float CharactersToFloat(const LChar* data,
+ size_t length,
+ size_t& parsed_length) {
+ // FIXME: This will return ok even when the string fits into a double but
+ // not a float.
+ return static_cast<float>(ToDoubleType<LChar, kAllowTrailingJunk>(
+ data, length, nullptr, parsed_length));
+}
+
+float CharactersToFloat(const UChar* data,
+ size_t length,
+ size_t& parsed_length) {
+ // FIXME: This will return ok even when the string fits into a double but
+ // not a float.
+ return static_cast<float>(ToDoubleType<UChar, kAllowTrailingJunk>(
+ data, length, nullptr, parsed_length));
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/string_to_number.h b/chromium/third_party/blink/renderer/platform/wtf/text/string_to_number.h
new file mode 100644
index 00000000000..a80984d577d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/string_to_number.h
@@ -0,0 +1,159 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_STRING_TO_NUMBER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_STRING_TO_NUMBER_H_
+
+#include "third_party/blink/renderer/platform/wtf/text/number_parsing_options.h"
+#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_export.h"
+
+namespace WTF {
+
+enum class NumberParsingResult {
+ kSuccess,
+ kError,
+ // For UInt functions, kOverflowMin never happens. Negative numbers are
+ // treated as kError. This behavior matches to the HTML standard.
+ // https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#rules-for-parsing-non-negative-integers
+ kOverflowMin,
+ kOverflowMax,
+};
+
+// string -> int.
+WTF_EXPORT int CharactersToInt(const LChar*,
+ size_t,
+ NumberParsingOptions,
+ bool* ok);
+WTF_EXPORT int CharactersToInt(const UChar*,
+ size_t,
+ NumberParsingOptions,
+ bool* ok);
+
+// string -> unsigned.
+WTF_EXPORT unsigned HexCharactersToUInt(const LChar*,
+ size_t,
+ NumberParsingOptions,
+ bool* ok);
+WTF_EXPORT unsigned HexCharactersToUInt(const UChar*,
+ size_t,
+ NumberParsingOptions,
+ bool* ok);
+WTF_EXPORT unsigned CharactersToUInt(const LChar*,
+ size_t,
+ NumberParsingOptions,
+ bool* ok);
+WTF_EXPORT unsigned CharactersToUInt(const UChar*,
+ size_t,
+ NumberParsingOptions,
+ bool* ok);
+
+// NumberParsingResult versions of CharactersToUInt. They can detect
+// overflow. |NumberParsingResult*| should not be nullptr;
+WTF_EXPORT unsigned CharactersToUInt(const LChar*,
+ size_t,
+ NumberParsingOptions,
+ NumberParsingResult*);
+WTF_EXPORT unsigned CharactersToUInt(const UChar*,
+ size_t,
+ NumberParsingOptions,
+ NumberParsingResult*);
+
+// string -> int64_t.
+WTF_EXPORT int64_t CharactersToInt64(const LChar*,
+ size_t,
+ NumberParsingOptions,
+ bool* ok);
+WTF_EXPORT int64_t CharactersToInt64(const UChar*,
+ size_t,
+ NumberParsingOptions,
+ bool* ok);
+
+// string -> uint64_t.
+WTF_EXPORT uint64_t CharactersToUInt64(const LChar*,
+ size_t,
+ NumberParsingOptions,
+ bool* ok);
+WTF_EXPORT uint64_t CharactersToUInt64(const UChar*,
+ size_t,
+ NumberParsingOptions,
+ bool* ok);
+
+// FIXME: Like the strict functions above, these give false for "ok" when there
+// is trailing garbage. Like the non-strict functions above, these return the
+// value when there is trailing garbage. It would be better if these were more
+// consistent with the above functions instead.
+
+// string -> double.
+//
+// These functions accepts:
+// - leading '+'
+// - numbers without leading zeros such as ".5"
+// - numbers ending with "." such as "3."
+// - scientific notation
+// - leading whitespace (IsASCIISpace, not IsHTMLSpace)
+// - no trailing whitespace
+// - no trailing garbage
+// - no numbers such as "NaN" "Infinity"
+//
+// A huge absolute number which a double can't represent is accepted, and
+// +Infinity or -Infinity is returned.
+//
+// A small absolute numbers which a double can't represent is accepted, and
+// 0 is returned
+WTF_EXPORT double CharactersToDouble(const LChar*, size_t, bool* ok);
+WTF_EXPORT double CharactersToDouble(const UChar*, size_t, bool* ok);
+
+// |parsed_length| will have the length of characters which was parsed as a
+// double number. It will be 0 if the input string isn't a number. It will be
+// smaller than |length| if the input string contains trailing
+// whiespace/garbage.
+WTF_EXPORT double CharactersToDouble(const LChar*,
+ size_t length,
+ size_t& parsed_length);
+WTF_EXPORT double CharactersToDouble(const UChar*,
+ size_t length,
+ size_t& parsed_length);
+
+// string -> float.
+//
+// These functions accepts:
+// - leading '+'
+// - numbers without leading zeros such as ".5"
+// - numbers ending with "." such as "3."
+// - scientific notation
+// - leading whitespace (IsASCIISpace, not IsHTMLSpace)
+// - no trailing whitespace
+// - no trailing garbage
+// - no numbers such as "NaN" "Infinity"
+//
+// A huge absolute number which a float can't represent is accepted, and
+// +Infinity or -Infinity is returned.
+//
+// A small absolute numbers which a float can't represent is accepted, and
+// 0 is returned
+WTF_EXPORT float CharactersToFloat(const LChar*, size_t, bool* ok);
+WTF_EXPORT float CharactersToFloat(const UChar*, size_t, bool* ok);
+
+// |parsed_length| will have the length of characters which was parsed as a
+// flaot number. It will be 0 if the input string isn't a number. It will be
+// smaller than |length| if the input string contains trailing
+// whiespace/garbage.
+WTF_EXPORT float CharactersToFloat(const LChar*,
+ size_t length,
+ size_t& parsed_length);
+WTF_EXPORT float CharactersToFloat(const UChar*,
+ size_t length,
+ size_t& parsed_length);
+
+} // namespace WTF
+
+using WTF::CharactersToInt;
+using WTF::CharactersToUInt;
+using WTF::CharactersToInt64;
+using WTF::CharactersToUInt64;
+using WTF::CharactersToDouble;
+using WTF::CharactersToFloat;
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/string_to_number_test.cc b/chromium/third_party/blink/renderer/platform/wtf/text/string_to_number_test.cc
new file mode 100644
index 00000000000..3885d875f85
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/string_to_number_test.cc
@@ -0,0 +1,385 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/wtf/text/string_to_number.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include <cstring>
+
+namespace WTF {
+
+TEST(StringToNumberTest, CharactersToInt) {
+#define EXPECT_VALID(string, options, expectedValue) \
+ do { \
+ bool ok; \
+ int value = CharactersToInt(reinterpret_cast<const LChar*>(string), \
+ std::strlen(string), \
+ NumberParsingOptions::options, &ok); \
+ EXPECT_TRUE(ok); \
+ EXPECT_EQ(value, expectedValue); \
+ } while (false)
+
+#define EXPECT_INVALID(string, options) \
+ do { \
+ bool ok; \
+ CharactersToInt(reinterpret_cast<const LChar*>(string), \
+ std::strlen(string), NumberParsingOptions::options, &ok); \
+ EXPECT_FALSE(ok); \
+ } while (false)
+
+ EXPECT_VALID("1", kStrict, 1);
+ EXPECT_VALID("2", kStrict, 2);
+ EXPECT_VALID("9", kStrict, 9);
+ EXPECT_VALID("10", kStrict, 10);
+ EXPECT_VALID("0", kStrict, 0);
+ EXPECT_VALID("-0", kStrict, 0);
+ EXPECT_VALID("-1", kStrict, -1);
+ EXPECT_VALID("-2", kStrict, -2);
+ EXPECT_VALID("-9", kStrict, -9);
+ EXPECT_VALID("-10", kStrict, -10);
+ EXPECT_VALID("+0", kStrict, 0);
+ EXPECT_VALID("+1", kStrict, 1);
+ EXPECT_INVALID("+1", kNone);
+ EXPECT_VALID("+2", kStrict, 2);
+ EXPECT_VALID("+9", kStrict, 9);
+ EXPECT_VALID("+10", kStrict, 10);
+ EXPECT_VALID("00", kStrict, 0);
+ EXPECT_VALID("+00", kStrict, 0);
+ EXPECT_VALID("-00", kStrict, 0);
+ EXPECT_VALID("01", kStrict, 1);
+ EXPECT_VALID("-01", kStrict, -1);
+ EXPECT_VALID("00000000000000000000", kStrict, 0);
+ EXPECT_VALID(" 3 ", kStrict, 3);
+ EXPECT_INVALID(" 3 ", kNone);
+ EXPECT_VALID(" 3 pt", kLoose, 3);
+ EXPECT_INVALID(" 3 pt", kStrict);
+ EXPECT_VALID("3px", kAcceptTrailingGarbage, 3);
+ EXPECT_INVALID("a", kStrict);
+ EXPECT_INVALID("1a", kStrict);
+ EXPECT_INVALID("a1", kStrict);
+ EXPECT_INVALID("-a", kStrict);
+ EXPECT_INVALID("", kStrict);
+ EXPECT_INVALID("-", kStrict);
+ EXPECT_INVALID("--1", kStrict);
+ EXPECT_INVALID("++1", kStrict);
+ EXPECT_INVALID("+-1", kStrict);
+ EXPECT_INVALID("-+1", kStrict);
+ EXPECT_INVALID("0-", kStrict);
+ EXPECT_INVALID("0+", kStrict);
+
+ EXPECT_VALID("2147483647", kStrict, 2147483647);
+ EXPECT_VALID("02147483647", kStrict, 2147483647);
+ EXPECT_INVALID("2147483648", kStrict);
+ EXPECT_INVALID("2147483649", kStrict);
+ EXPECT_INVALID("2147483650", kStrict);
+ EXPECT_INVALID("2147483700", kStrict);
+ EXPECT_INVALID("2147484000", kStrict);
+ EXPECT_INVALID("2200000000", kStrict);
+ EXPECT_INVALID("3000000000", kStrict);
+ EXPECT_INVALID("10000000000", kStrict);
+ EXPECT_VALID("-2147483647", kStrict, -2147483647);
+ EXPECT_VALID("-2147483648", kStrict, -2147483647 - 1);
+ EXPECT_INVALID("-2147483649", kStrict);
+ EXPECT_INVALID("-2147483650", kStrict);
+ EXPECT_INVALID("-2147483700", kStrict);
+ EXPECT_INVALID("-2147484000", kStrict);
+ EXPECT_INVALID("-2200000000", kStrict);
+ EXPECT_INVALID("-3000000000", kStrict);
+ EXPECT_INVALID("-10000000000", kStrict);
+
+#undef EXPECT_VALID
+#undef EXPECT_INVALID
+}
+
+TEST(StringToNumberTest, CharactersToUInt) {
+#define EXPECT_VALID(string, options, expectedValue) \
+ do { \
+ bool ok; \
+ unsigned value = CharactersToUInt(reinterpret_cast<const LChar*>(string), \
+ std::strlen(string), \
+ NumberParsingOptions::options, &ok); \
+ EXPECT_TRUE(ok); \
+ EXPECT_EQ(value, expectedValue); \
+ } while (false)
+
+#define EXPECT_INVALID(string, options) \
+ do { \
+ bool ok; \
+ CharactersToUInt(reinterpret_cast<const LChar*>(string), \
+ std::strlen(string), NumberParsingOptions::options, &ok); \
+ EXPECT_FALSE(ok); \
+ } while (false)
+
+ EXPECT_VALID("1", kStrict, 1u);
+ EXPECT_VALID("2", kStrict, 2u);
+ EXPECT_VALID("9", kStrict, 9u);
+ EXPECT_VALID("10", kStrict, 10u);
+ EXPECT_VALID("0", kStrict, 0u);
+ EXPECT_VALID("+0", kStrict, 0u);
+ EXPECT_VALID("+1", kStrict, 1u);
+ EXPECT_VALID("+2", kStrict, 2u);
+ EXPECT_VALID("+9", kStrict, 9u);
+ EXPECT_VALID("+10", kStrict, 10u);
+ EXPECT_INVALID("+10", kNone);
+ EXPECT_VALID("00", kStrict, 0u);
+ EXPECT_VALID("+00", kStrict, 0u);
+ EXPECT_VALID("01", kStrict, 1u);
+ EXPECT_VALID("00000000000000000000", kStrict, 0u);
+ EXPECT_INVALID("a", kStrict);
+ EXPECT_INVALID("1a", kStrict);
+ EXPECT_INVALID("a1", kStrict);
+ EXPECT_INVALID("-a", kStrict);
+ EXPECT_INVALID("", kStrict);
+ EXPECT_INVALID("-", kStrict);
+ EXPECT_INVALID("-0", kStrict);
+ EXPECT_VALID("-0", kAcceptMinusZeroForUnsigned, 0u);
+ EXPECT_INVALID("-1", kStrict);
+ EXPECT_INVALID("-1", kAcceptMinusZeroForUnsigned);
+ EXPECT_INVALID("-2", kStrict);
+ EXPECT_INVALID("-9", kStrict);
+ EXPECT_INVALID("-10", kStrict);
+ EXPECT_INVALID("-00", kStrict);
+ EXPECT_VALID("-00", kAcceptMinusZeroForUnsigned, 0u);
+ EXPECT_INVALID("-01", kStrict);
+ EXPECT_INVALID("--1", kStrict);
+ EXPECT_INVALID("++1", kStrict);
+ EXPECT_INVALID("+-1", kStrict);
+ EXPECT_INVALID("-+1", kStrict);
+ EXPECT_INVALID("0-", kStrict);
+ EXPECT_INVALID("0+", kStrict);
+
+ EXPECT_VALID("2147483647", kStrict, 2147483647u);
+ EXPECT_VALID("02147483647", kStrict, 2147483647u);
+ EXPECT_VALID("2147483648", kStrict, 2147483648u);
+ EXPECT_VALID("4294967295", kStrict, 4294967295u);
+ EXPECT_VALID("0004294967295", kStrict, 4294967295u);
+ EXPECT_INVALID("4294967296", kStrict);
+ EXPECT_INVALID("4294967300", kStrict);
+ EXPECT_INVALID("4300000000", kStrict);
+ EXPECT_INVALID("5000000000", kStrict);
+ EXPECT_INVALID("10000000000", kStrict);
+ EXPECT_INVALID("-2147483647", kStrict);
+ EXPECT_INVALID("-2147483648", kStrict);
+ EXPECT_INVALID("-2147483649", kStrict);
+ EXPECT_INVALID("-2147483650", kStrict);
+ EXPECT_INVALID("-2147483700", kStrict);
+ EXPECT_INVALID("-2147484000", kStrict);
+ EXPECT_INVALID("-2200000000", kStrict);
+ EXPECT_INVALID("-3000000000", kStrict);
+ EXPECT_INVALID("-10000000000", kStrict);
+
+#undef EXPECT_VALID
+#undef EXPECT_INVALID
+}
+
+TEST(StringToNumberTest, HexCharactersToUInt) {
+#define EXPECT_VALID(string, expectedValue) \
+ do { \
+ bool ok; \
+ unsigned value = HexCharactersToUInt( \
+ reinterpret_cast<const LChar*>(string), std::strlen(string), \
+ NumberParsingOptions::kStrict, &ok); \
+ EXPECT_TRUE(ok); \
+ EXPECT_EQ(value, expectedValue); \
+ } while (false)
+
+#define EXPECT_INVALID(string) \
+ do { \
+ bool ok; \
+ HexCharactersToUInt(reinterpret_cast<const LChar*>(string), \
+ std::strlen(string), NumberParsingOptions::kStrict, \
+ &ok); \
+ EXPECT_FALSE(ok); \
+ } while (false)
+
+ EXPECT_VALID("1", 1u);
+ EXPECT_VALID("a", 0xAu);
+ EXPECT_VALID("A", 0xAu);
+ EXPECT_VALID("+a", 0xAu);
+ EXPECT_VALID("+A", 0xAu);
+ EXPECT_INVALID("-a");
+ EXPECT_INVALID("-A");
+
+ EXPECT_VALID("7fffffff", 0x7FFFFFFFu);
+ EXPECT_VALID("80000000", 0x80000000u);
+ EXPECT_VALID("fffffff0", 0xFFFFFFF0u);
+ EXPECT_VALID("ffffffff", 0xFFFFFFFFu);
+ EXPECT_VALID("00ffffffff", 0xFFFFFFFFu);
+ EXPECT_INVALID("100000000");
+ EXPECT_INVALID("7fffffff0");
+ EXPECT_INVALID("-7fffffff");
+ EXPECT_INVALID("-80000000");
+ EXPECT_INVALID("-80000001");
+ EXPECT_INVALID("-8000000a");
+ EXPECT_INVALID("-8000000f");
+ EXPECT_INVALID("-80000010");
+ EXPECT_INVALID("-90000000");
+ EXPECT_INVALID("-f0000000");
+ EXPECT_INVALID("-fffffff0");
+ EXPECT_INVALID("-ffffffff");
+
+#undef EXPECT_VALID
+#undef EXPECT_INVALID
+}
+
+NumberParsingResult ParseUInt(const char* str, unsigned* value) {
+ NumberParsingResult result;
+ *value =
+ CharactersToUInt(reinterpret_cast<const LChar*>(str), std::strlen(str),
+ NumberParsingOptions::kStrict, &result);
+ return result;
+}
+
+TEST(StringToNumberTest, NumberParsingState) {
+ unsigned value;
+ EXPECT_EQ(NumberParsingResult::kOverflowMax,
+ ParseUInt("10000000000", &value));
+ EXPECT_EQ(NumberParsingResult::kError, ParseUInt("10000000000abc", &value));
+ EXPECT_EQ(NumberParsingResult::kError, ParseUInt("-10000000000", &value));
+ EXPECT_EQ(NumberParsingResult::kError, ParseUInt("-0", &value));
+ EXPECT_EQ(NumberParsingResult::kSuccess, ParseUInt("10", &value));
+}
+
+void ParseDouble(const char* str, double expected_value) {
+ bool ok;
+ double value = CharactersToDouble(reinterpret_cast<const LChar*>(str),
+ std::strlen(str), &ok);
+ EXPECT_TRUE(ok) << "\"" << str << "\"";
+ EXPECT_EQ(expected_value, value);
+}
+
+void FailToParseDouble(const char* str) {
+ bool ok;
+ CharactersToDouble(reinterpret_cast<const LChar*>(str), std::strlen(str),
+ &ok);
+ EXPECT_FALSE(ok) << "\"" << str << "\"";
+}
+
+TEST(StringToNumberTest, CharactersToDouble) {
+ FailToParseDouble("");
+ ParseDouble("0", 0.0);
+ ParseDouble("-0", 0.0);
+ ParseDouble("1.5", 1.5);
+ ParseDouble("+1.5", 1.5);
+ FailToParseDouble("+");
+ FailToParseDouble("-");
+ ParseDouble(".5", 0.5);
+ ParseDouble("1.", 1);
+ FailToParseDouble(".");
+ ParseDouble("1e-100", 1e-100);
+ ParseDouble("1e100", 1e+100);
+ ParseDouble(" 1.5", 1.5);
+ FailToParseDouble("1.5 ");
+ FailToParseDouble("1.5px");
+ FailToParseDouble("NaN");
+ FailToParseDouble("nan");
+ FailToParseDouble("Infinity");
+ FailToParseDouble("infinity");
+ FailToParseDouble("Inf");
+ FailToParseDouble("inf");
+ ParseDouble("1e+4000", std::numeric_limits<double>::infinity());
+ ParseDouble("-1e+4000", -std::numeric_limits<double>::infinity());
+ ParseDouble("1e-4000", 0);
+ FailToParseDouble("1e");
+ FailToParseDouble("1e-");
+ FailToParseDouble("1e+");
+ FailToParseDouble("1e3.");
+ FailToParseDouble("1e3.5");
+ FailToParseDouble("1e.3");
+}
+
+size_t ParseDouble(const char* str) {
+ size_t parsed;
+ CharactersToDouble(reinterpret_cast<const LChar*>(str), std::strlen(str),
+ parsed);
+ return parsed;
+}
+
+TEST(StringToNumberTest, CharactersToDoubleParsedLength) {
+ EXPECT_EQ(0u, ParseDouble(""));
+ EXPECT_EQ(0u, ParseDouble(" "));
+ EXPECT_EQ(0u, ParseDouble("+"));
+ EXPECT_EQ(0u, ParseDouble("-"));
+ EXPECT_EQ(0u, ParseDouble("."));
+ EXPECT_EQ(0u, ParseDouble(" "));
+ EXPECT_EQ(4u, ParseDouble(" 123"));
+ EXPECT_EQ(4u, ParseDouble(" 123 "));
+ EXPECT_EQ(4u, ParseDouble(" 123px"));
+ EXPECT_EQ(5u, ParseDouble("1.234"));
+ EXPECT_EQ(5u, ParseDouble("1.234e"));
+ EXPECT_EQ(7u, ParseDouble("1.234e1"));
+}
+
+void ParseFloat(const char* str, float expected_value) {
+ bool ok;
+ float value = CharactersToFloat(reinterpret_cast<const LChar*>(str),
+ std::strlen(str), &ok);
+ EXPECT_TRUE(ok) << "\"" << str << "\"";
+ EXPECT_EQ(expected_value, value);
+}
+
+void FailToParseFloat(const char* str) {
+ bool ok;
+ CharactersToFloat(reinterpret_cast<const LChar*>(str), std::strlen(str), &ok);
+ EXPECT_FALSE(ok) << "\"" << str << "\"";
+}
+
+TEST(StringToNumberTest, CharactersToFloat) {
+ FailToParseFloat("");
+ ParseFloat("0", 0.0f);
+ ParseFloat("-0", 0.0f);
+ ParseFloat("1.5", 1.5f);
+ ParseFloat("+1.5", 1.5f);
+ FailToParseFloat("+");
+ FailToParseFloat("-");
+ ParseFloat(".5", 0.5f);
+ ParseFloat("1.", 1.0f);
+ FailToParseFloat(".");
+ ParseFloat("1e-40", 1e-40f);
+ ParseFloat("1e30", 1e+30f);
+ ParseFloat(" 1.5", 1.5f);
+ FailToParseFloat("1.5 ");
+ FailToParseFloat("1.5px");
+ FailToParseFloat("NaN");
+ FailToParseFloat("nan");
+ FailToParseFloat("Infinity");
+ FailToParseFloat("infinity");
+ FailToParseFloat("Inf");
+ FailToParseFloat("inf");
+ ParseFloat("1e+4000", std::numeric_limits<float>::infinity());
+ ParseFloat("-1e+4000", -std::numeric_limits<float>::infinity());
+ ParseFloat("1e+100", std::numeric_limits<float>::infinity());
+ ParseFloat("-1e+100", -std::numeric_limits<float>::infinity());
+ ParseFloat("1e-4000", 0);
+ FailToParseFloat("1e");
+ FailToParseFloat("1e-");
+ FailToParseFloat("1e+");
+ FailToParseFloat("1e3.");
+ FailToParseFloat("1e3.5");
+ FailToParseFloat("1e.3");
+}
+
+size_t ParseFloat(const char* str) {
+ size_t parsed;
+ CharactersToFloat(reinterpret_cast<const LChar*>(str), std::strlen(str),
+ parsed);
+ return parsed;
+}
+
+TEST(StringToNumberTest, CharactersToFloatParsedLength) {
+ EXPECT_EQ(0u, ParseFloat(""));
+ EXPECT_EQ(0u, ParseFloat(" "));
+ EXPECT_EQ(0u, ParseFloat("+"));
+ EXPECT_EQ(0u, ParseFloat("-"));
+ EXPECT_EQ(0u, ParseFloat("."));
+ EXPECT_EQ(0u, ParseFloat(" "));
+ EXPECT_EQ(4u, ParseFloat(" 123"));
+ EXPECT_EQ(4u, ParseFloat(" 123 "));
+ EXPECT_EQ(4u, ParseFloat(" 123px"));
+ EXPECT_EQ(5u, ParseFloat("1.234"));
+ EXPECT_EQ(5u, ParseFloat("1.234e"));
+ EXPECT_EQ(7u, ParseFloat("1.234e1"));
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h b/chromium/third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h
new file mode 100644
index 00000000000..8ff99a6a625
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_STRING_UTF8_ADAPTOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_STRING_UTF8_ADAPTOR_H_
+
+#include "base/strings/string_piece.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace WTF {
+
+// This class lets you get UTF-8 data out of a String without mallocing a
+// separate buffer to hold the data if the String happens to be 8 bit and
+// contain only ASCII characters.
+class StringUTF8Adaptor final {
+ DISALLOW_NEW();
+
+ public:
+ StringUTF8Adaptor(const String& string,
+ UTF8ConversionMode mode = kLenientUTF8Conversion)
+ : data_(nullptr), length_(0) {
+ if (string.IsEmpty())
+ return;
+ // Unfortunately, 8 bit WTFStrings are encoded in Latin-1 and GURL uses
+ // UTF-8 when processing 8 bit strings. If |relative| is entirely ASCII, we
+ // luck out and can avoid mallocing a new buffer to hold the UTF-8 data
+ // because UTF-8 and Latin-1 use the same code units for ASCII code points.
+ if (string.Is8Bit() && string.ContainsOnlyASCII()) {
+ data_ = reinterpret_cast<const char*>(string.Characters8());
+ length_ = string.length();
+ } else {
+ utf8_buffer_ = string.Utf8(mode);
+ data_ = utf8_buffer_.data();
+ length_ = utf8_buffer_.length();
+ }
+ }
+
+ const char* Data() const { return data_; }
+ size_t length() const { return length_; }
+
+ base::StringPiece AsStringPiece() const {
+ return base::StringPiece(data_, length_);
+ }
+
+ private:
+ CString utf8_buffer_;
+ const char* data_;
+ size_t length_;
+};
+
+} // namespace WTF
+
+using WTF::StringUTF8Adaptor;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_STRING_UTF8_ADAPTOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/string_view.cc b/chromium/third_party/blink/renderer/platform/wtf/text/string_view.cc
new file mode 100644
index 00000000000..f578dd6a381
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/string_view.cc
@@ -0,0 +1,108 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/wtf/text/string_view.h"
+
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_impl.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace WTF {
+
+StringView::StringView(const UChar* chars)
+ : StringView(chars, chars ? LengthOfNullTerminatedString(chars) : 0) {}
+
+#if DCHECK_IS_ON()
+StringView::~StringView() {
+ DCHECK(impl_);
+ DCHECK(!impl_->HasOneRef() || impl_->IsStatic())
+ << "StringView does not own the StringImpl, it "
+ "must not have the last ref.";
+}
+#endif
+
+String StringView::ToString() const {
+ if (IsNull())
+ return String();
+ if (IsEmpty())
+ return g_empty_string;
+ if (StringImpl* impl = SharedImpl())
+ return impl;
+ if (Is8Bit())
+ return String(Characters8(), length_);
+ return StringImpl::Create8BitIfPossible(Characters16(), length_);
+}
+
+AtomicString StringView::ToAtomicString() const {
+ if (IsNull())
+ return g_null_atom;
+ if (IsEmpty())
+ return g_empty_atom;
+ if (StringImpl* impl = SharedImpl())
+ return AtomicString(impl);
+ if (Is8Bit())
+ return AtomicString(Characters8(), length_);
+ return AtomicString(Characters16(), length_);
+}
+
+bool EqualStringView(const StringView& a, const StringView& b) {
+ if (a.IsNull() || b.IsNull())
+ return a.IsNull() == b.IsNull();
+ if (a.length() != b.length())
+ return false;
+ if (a.Is8Bit()) {
+ if (b.Is8Bit())
+ return Equal(a.Characters8(), b.Characters8(), a.length());
+ return Equal(a.Characters8(), b.Characters16(), a.length());
+ }
+ if (b.Is8Bit())
+ return Equal(a.Characters16(), b.Characters8(), a.length());
+ return Equal(a.Characters16(), b.Characters16(), a.length());
+}
+
+bool DeprecatedEqualIgnoringCaseAndNullity(const StringView& a,
+ const StringView& b) {
+ if (a.length() != b.length())
+ return false;
+ if (a.Is8Bit()) {
+ if (b.Is8Bit()) {
+ return DeprecatedEqualIgnoringCase(a.Characters8(), b.Characters8(),
+ a.length());
+ }
+ return DeprecatedEqualIgnoringCase(a.Characters8(), b.Characters16(),
+ a.length());
+ }
+ if (b.Is8Bit()) {
+ return DeprecatedEqualIgnoringCase(a.Characters16(), b.Characters8(),
+ a.length());
+ }
+ return DeprecatedEqualIgnoringCase(a.Characters16(), b.Characters16(),
+ a.length());
+}
+
+bool DeprecatedEqualIgnoringCase(const StringView& a, const StringView& b) {
+ if (a.IsNull() || b.IsNull())
+ return a.IsNull() == b.IsNull();
+ return DeprecatedEqualIgnoringCaseAndNullity(a, b);
+}
+
+bool EqualIgnoringASCIICase(const StringView& a, const StringView& b) {
+ if (a.IsNull() || b.IsNull())
+ return a.IsNull() == b.IsNull();
+ if (a.length() != b.length())
+ return false;
+ if (a.Is8Bit()) {
+ if (b.Is8Bit())
+ return EqualIgnoringASCIICase(a.Characters8(), b.Characters8(),
+ a.length());
+ return EqualIgnoringASCIICase(a.Characters8(), b.Characters16(),
+ a.length());
+ }
+ if (b.Is8Bit())
+ return EqualIgnoringASCIICase(a.Characters16(), b.Characters8(),
+ a.length());
+ return EqualIgnoringASCIICase(a.Characters16(), b.Characters16(), a.length());
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/string_view.h b/chromium/third_party/blink/renderer/platform/wtf/text/string_view.h
new file mode 100644
index 00000000000..ea7e299075d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/string_view.h
@@ -0,0 +1,284 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_STRING_VIEW_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_STRING_VIEW_H_
+
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/get_ptr.h"
+#if DCHECK_IS_ON()
+#include "base/memory/scoped_refptr.h"
+#endif
+#include <cstring>
+#include "third_party/blink/renderer/platform/wtf/text/string_impl.h"
+#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
+
+namespace WTF {
+
+class AtomicString;
+class String;
+
+// A string like object that wraps either an 8bit or 16bit byte sequence
+// and keeps track of the length and the type, it does NOT own the bytes.
+//
+// Since StringView does not own the bytes creating a StringView from a String,
+// then calling clear() on the String will result in a use-after-free. Asserts
+// in ~StringView attempt to enforce this for most common cases.
+//
+// See base/strings/string_piece.h for more details.
+class WTF_EXPORT StringView {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ // Null string.
+ StringView() { Clear(); }
+
+ // From a StringView:
+ StringView(const StringView&, unsigned offset, unsigned length);
+ StringView(const StringView& view, unsigned offset)
+ : StringView(view, offset, view.length_ - offset) {}
+
+ // From a StringImpl:
+ StringView(const StringImpl*);
+ StringView(const StringImpl*, unsigned offset);
+ StringView(const StringImpl*, unsigned offset, unsigned length);
+
+ // From a non-null StringImpl.
+ StringView(const StringImpl& impl)
+ : impl_(const_cast<StringImpl*>(&impl)),
+ bytes_(impl.Bytes()),
+ length_(impl.length()) {}
+
+ // From a non-null StringImpl, avoids the null check.
+ StringView(StringImpl& impl)
+ : impl_(&impl), bytes_(impl.Bytes()), length_(impl.length()) {}
+ StringView(StringImpl&, unsigned offset);
+ StringView(StringImpl&, unsigned offset, unsigned length);
+
+ // From an String, implemented in String.h
+ inline StringView(const String&, unsigned offset, unsigned length);
+ inline StringView(const String&, unsigned offset);
+ inline StringView(const String&);
+
+ // From an AtomicString, implemented in AtomicString.h
+ inline StringView(const AtomicString&, unsigned offset, unsigned length);
+ inline StringView(const AtomicString&, unsigned offset);
+ inline StringView(const AtomicString&);
+
+ // From a literal string or LChar buffer:
+ StringView(const LChar* chars, unsigned length)
+ : impl_(StringImpl::empty_), characters8_(chars), length_(length) {}
+ StringView(const char* chars, unsigned length)
+ : StringView(reinterpret_cast<const LChar*>(chars), length) {}
+ StringView(const LChar* chars)
+ : StringView(chars,
+ chars ? strlen(reinterpret_cast<const char*>(chars)) : 0) {}
+ StringView(const char* chars)
+ : StringView(reinterpret_cast<const LChar*>(chars)) {}
+
+ // From a wide literal string or UChar buffer.
+ StringView(const UChar* chars, unsigned length)
+ : impl_(StringImpl::empty16_bit_),
+ characters16_(chars),
+ length_(length) {}
+ StringView(const UChar* chars);
+ StringView(const char16_t* chars)
+ : StringView(reinterpret_cast<const UChar*>(chars)) {}
+
+#if DCHECK_IS_ON()
+ ~StringView();
+#endif
+
+ bool IsNull() const { return !bytes_; }
+ bool IsEmpty() const { return !length_; }
+
+ unsigned length() const { return length_; }
+
+ bool Is8Bit() const {
+ DCHECK(impl_);
+ return impl_->Is8Bit();
+ }
+
+ void Clear();
+
+ UChar operator[](unsigned i) const {
+ SECURITY_DCHECK(i < length());
+ if (Is8Bit())
+ return Characters8()[i];
+ return Characters16()[i];
+ }
+
+ const LChar* Characters8() const {
+ DCHECK(Is8Bit());
+ return characters8_;
+ }
+
+ const UChar* Characters16() const {
+ DCHECK(!Is8Bit());
+ return characters16_;
+ }
+
+ UChar32 CodepointAt(unsigned i) const {
+ SECURITY_DCHECK(i < length());
+ if (Is8Bit())
+ return (*this)[i];
+ UChar32 codepoint;
+ U16_GET(Characters16(), 0, i, length(), codepoint);
+ return codepoint;
+ }
+
+ const void* Bytes() const { return bytes_; }
+
+ // This is not named impl() like String because it has different semantics.
+ // String::impl() is never null if String::isNull() is false. For StringView
+ // sharedImpl() can be null if the StringView was created with a non-zero
+ // offset, or a length that made it shorter than the underlying impl.
+ StringImpl* SharedImpl() const {
+ // If this StringView is backed by a StringImpl, and was constructed
+ // with a zero offset and the same length we can just access the impl
+ // directly since this == StringView(m_impl).
+ if (impl_->Bytes() == Bytes() && length_ == impl_->length())
+ return GetPtr(impl_);
+ return nullptr;
+ }
+
+ String ToString() const;
+ AtomicString ToAtomicString() const;
+
+ template <bool isSpecialCharacter(UChar)>
+ bool IsAllSpecialCharacters() const;
+
+ private:
+ void Set(const StringImpl&, unsigned offset, unsigned length);
+
+// We use the StringImpl to mark for 8bit or 16bit, even for strings where
+// we were constructed from a char pointer. So m_impl->bytes() might have
+// nothing to do with this view's bytes().
+#if DCHECK_IS_ON()
+ scoped_refptr<StringImpl> impl_;
+#else
+ StringImpl* impl_;
+#endif
+ union {
+ const LChar* characters8_;
+ const UChar* characters16_;
+ const void* bytes_;
+ };
+ unsigned length_;
+};
+
+inline StringView::StringView(const StringView& view,
+ unsigned offset,
+ unsigned length)
+ : impl_(view.impl_), length_(length) {
+ SECURITY_DCHECK(offset + length <= view.length());
+ if (Is8Bit())
+ characters8_ = view.Characters8() + offset;
+ else
+ characters16_ = view.Characters16() + offset;
+}
+
+inline StringView::StringView(const StringImpl* impl) {
+ if (!impl) {
+ Clear();
+ return;
+ }
+ impl_ = const_cast<StringImpl*>(impl);
+ length_ = impl->length();
+ bytes_ = impl->Bytes();
+}
+
+inline StringView::StringView(const StringImpl* impl, unsigned offset) {
+ impl ? Set(*impl, offset, impl->length() - offset) : Clear();
+}
+
+inline StringView::StringView(const StringImpl* impl,
+ unsigned offset,
+ unsigned length) {
+ impl ? Set(*impl, offset, length) : Clear();
+}
+
+inline StringView::StringView(StringImpl& impl, unsigned offset) {
+ Set(impl, offset, impl.length() - offset);
+}
+
+inline StringView::StringView(StringImpl& impl,
+ unsigned offset,
+ unsigned length) {
+ Set(impl, offset, length);
+}
+
+inline void StringView::Clear() {
+ length_ = 0;
+ bytes_ = nullptr;
+ impl_ = StringImpl::empty_; // mark as 8 bit.
+}
+
+inline void StringView::Set(const StringImpl& impl,
+ unsigned offset,
+ unsigned length) {
+ SECURITY_DCHECK(offset + length <= impl.length());
+ length_ = length;
+ impl_ = const_cast<StringImpl*>(&impl);
+ if (impl.Is8Bit())
+ characters8_ = impl.Characters8() + offset;
+ else
+ characters16_ = impl.Characters16() + offset;
+}
+
+// Unicode aware case insensitive string matching. Non-ASCII characters might
+// match to ASCII characters. These functions are rarely used to implement web
+// platform features.
+// These functions are deprecated. Use EqualIgnoringASCIICase(), or introduce
+// EqualIgnoringUnicodeCase(). See crbug.com/627682
+WTF_EXPORT bool DeprecatedEqualIgnoringCase(const StringView&,
+ const StringView&);
+WTF_EXPORT bool DeprecatedEqualIgnoringCaseAndNullity(const StringView&,
+ const StringView&);
+
+WTF_EXPORT bool EqualIgnoringASCIICase(const StringView&, const StringView&);
+
+// TODO(esprehn): Can't make this an overload of WTF::equal since that makes
+// calls to equal() that pass literal strings ambiguous. Figure out if we can
+// replace all the callers with equalStringView and then rename it to equal().
+WTF_EXPORT bool EqualStringView(const StringView&, const StringView&);
+
+inline bool operator==(const StringView& a, const StringView& b) {
+ return EqualStringView(a, b);
+}
+
+inline bool operator!=(const StringView& a, const StringView& b) {
+ return !(a == b);
+}
+
+template <bool isSpecialCharacter(UChar), typename CharacterType>
+inline bool IsAllSpecialCharacters(const CharacterType* characters,
+ size_t length) {
+ for (size_t i = 0; i < length; ++i) {
+ if (!isSpecialCharacter(characters[i]))
+ return false;
+ }
+ return true;
+}
+
+template <bool isSpecialCharacter(UChar)>
+inline bool StringView::IsAllSpecialCharacters() const {
+ size_t len = length();
+ if (!len)
+ return true;
+
+ return Is8Bit() ? WTF::IsAllSpecialCharacters<isSpecialCharacter, LChar>(
+ Characters8(), len)
+ : WTF::IsAllSpecialCharacters<isSpecialCharacter, UChar>(
+ Characters16(), len);
+}
+
+} // namespace WTF
+
+using WTF::StringView;
+using WTF::EqualIgnoringASCIICase;
+using WTF::DeprecatedEqualIgnoringCase;
+using WTF::IsAllSpecialCharacters;
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/string_view_test.cc b/chromium/third_party/blink/renderer/platform/wtf/text/string_view_test.cc
new file mode 100644
index 00000000000..c36168d83a4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/string_view_test.cc
@@ -0,0 +1,464 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/wtf/text/string_view.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_impl.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace WTF {
+
+const char kChars[] = "12345";
+const LChar* const kChars8 = reinterpret_cast<const LChar*>(kChars);
+const UChar* const kChars16 = reinterpret_cast<const UChar*>(u"12345");
+
+TEST(StringViewTest, ConstructionStringImpl8) {
+ scoped_refptr<StringImpl> impl8_bit = StringImpl::Create(kChars8, 5);
+
+ // StringView(StringImpl*);
+ ASSERT_TRUE(StringView(impl8_bit.get()).Is8Bit());
+ EXPECT_FALSE(StringView(impl8_bit.get()).IsNull());
+ EXPECT_EQ(impl8_bit->Characters8(),
+ StringView(impl8_bit.get()).Characters8());
+ EXPECT_EQ(impl8_bit->length(), StringView(impl8_bit.get()).length());
+ EXPECT_STREQ(kChars, StringView(impl8_bit.get()).ToString().Utf8().data());
+
+ // StringView(StringImpl*, unsigned offset);
+ ASSERT_TRUE(StringView(impl8_bit.get(), 2).Is8Bit());
+ EXPECT_FALSE(StringView(impl8_bit.get(), 2).IsNull());
+ EXPECT_EQ(impl8_bit->Characters8() + 2,
+ StringView(impl8_bit.get(), 2).Characters8());
+ EXPECT_EQ(3u, StringView(impl8_bit.get(), 2).length());
+ EXPECT_EQ(StringView("345"), StringView(impl8_bit.get(), 2));
+ EXPECT_STREQ("345", StringView(impl8_bit.get(), 2).ToString().Utf8().data());
+
+ // StringView(StringImpl*, unsigned offset, unsigned length);
+ ASSERT_TRUE(StringView(impl8_bit.get(), 2, 1).Is8Bit());
+ EXPECT_FALSE(StringView(impl8_bit.get(), 2, 1).IsNull());
+ EXPECT_EQ(impl8_bit->Characters8() + 2,
+ StringView(impl8_bit.get(), 2, 1).Characters8());
+ EXPECT_EQ(1u, StringView(impl8_bit.get(), 2, 1).length());
+ EXPECT_EQ(StringView("3"), StringView(impl8_bit.get(), 2, 1));
+ EXPECT_STREQ("3", StringView(impl8_bit.get(), 2, 1).ToString().Utf8().data());
+}
+
+TEST(StringViewTest, ConstructionStringImpl16) {
+ scoped_refptr<StringImpl> impl16_bit = StringImpl::Create(kChars16, 5);
+
+ // StringView(StringImpl*);
+ ASSERT_FALSE(StringView(impl16_bit.get()).Is8Bit());
+ EXPECT_FALSE(StringView(impl16_bit.get()).IsNull());
+ EXPECT_EQ(impl16_bit->Characters16(),
+ StringView(impl16_bit.get()).Characters16());
+ EXPECT_EQ(impl16_bit->length(), StringView(impl16_bit.get()).length());
+ EXPECT_STREQ(kChars, StringView(impl16_bit.get()).ToString().Utf8().data());
+
+ // StringView(StringImpl*, unsigned offset);
+ ASSERT_FALSE(StringView(impl16_bit.get(), 2).Is8Bit());
+ EXPECT_FALSE(StringView(impl16_bit.get(), 2).IsNull());
+ EXPECT_EQ(impl16_bit->Characters16() + 2,
+ StringView(impl16_bit.get(), 2).Characters16());
+ EXPECT_EQ(3u, StringView(impl16_bit.get(), 2).length());
+ EXPECT_EQ(StringView("345"), StringView(impl16_bit.get(), 2));
+ EXPECT_STREQ("345", StringView(impl16_bit.get(), 2).ToString().Utf8().data());
+
+ // StringView(StringImpl*, unsigned offset, unsigned length);
+ ASSERT_FALSE(StringView(impl16_bit.get(), 2, 1).Is8Bit());
+ EXPECT_FALSE(StringView(impl16_bit.get(), 2, 1).IsNull());
+ EXPECT_EQ(impl16_bit->Characters16() + 2,
+ StringView(impl16_bit.get(), 2, 1).Characters16());
+ EXPECT_EQ(1u, StringView(impl16_bit.get(), 2, 1).length());
+ EXPECT_EQ(StringView("3"), StringView(impl16_bit.get(), 2, 1));
+ EXPECT_STREQ("3",
+ StringView(impl16_bit.get(), 2, 1).ToString().Utf8().data());
+}
+
+TEST(StringViewTest, ConstructionStringImplRef8) {
+ scoped_refptr<StringImpl> impl8_bit = StringImpl::Create(kChars8, 5);
+
+ // StringView(StringImpl&);
+ ASSERT_TRUE(StringView(*impl8_bit).Is8Bit());
+ EXPECT_FALSE(StringView(*impl8_bit).IsNull());
+ EXPECT_EQ(impl8_bit->Characters8(), StringView(*impl8_bit).Characters8());
+ EXPECT_EQ(impl8_bit->length(), StringView(*impl8_bit).length());
+ EXPECT_STREQ(kChars, StringView(*impl8_bit).ToString().Utf8().data());
+
+ // StringView(StringImpl&, unsigned offset);
+ ASSERT_TRUE(StringView(*impl8_bit, 2).Is8Bit());
+ EXPECT_FALSE(StringView(*impl8_bit, 2).IsNull());
+ EXPECT_EQ(impl8_bit->Characters8() + 2,
+ StringView(*impl8_bit, 2).Characters8());
+ EXPECT_EQ(3u, StringView(*impl8_bit, 2).length());
+ EXPECT_EQ(StringView("345"), StringView(*impl8_bit, 2));
+ EXPECT_STREQ("345", StringView(*impl8_bit, 2).ToString().Utf8().data());
+
+ // StringView(StringImpl&, unsigned offset, unsigned length);
+ ASSERT_TRUE(StringView(*impl8_bit, 2, 1).Is8Bit());
+ EXPECT_FALSE(StringView(*impl8_bit, 2, 1).IsNull());
+ EXPECT_EQ(impl8_bit->Characters8() + 2,
+ StringView(*impl8_bit, 2, 1).Characters8());
+ EXPECT_EQ(1u, StringView(*impl8_bit, 2, 1).length());
+ EXPECT_EQ(StringView("3"), StringView(*impl8_bit, 2, 1));
+ EXPECT_STREQ("3", StringView(*impl8_bit, 2, 1).ToString().Utf8().data());
+}
+
+TEST(StringViewTest, ConstructionStringImplRef16) {
+ scoped_refptr<StringImpl> impl16_bit = StringImpl::Create(kChars16, 5);
+
+ // StringView(StringImpl&);
+ ASSERT_FALSE(StringView(*impl16_bit).Is8Bit());
+ EXPECT_FALSE(StringView(*impl16_bit).IsNull());
+ EXPECT_EQ(impl16_bit->Characters16(), StringView(*impl16_bit).Characters16());
+ EXPECT_EQ(impl16_bit->length(), StringView(*impl16_bit).length());
+ EXPECT_STREQ(kChars, StringView(*impl16_bit).ToString().Utf8().data());
+
+ // StringView(StringImpl&, unsigned offset);
+ ASSERT_FALSE(StringView(*impl16_bit, 2).Is8Bit());
+ EXPECT_FALSE(StringView(*impl16_bit, 2).IsNull());
+ EXPECT_EQ(impl16_bit->Characters16() + 2,
+ StringView(*impl16_bit, 2).Characters16());
+ EXPECT_EQ(3u, StringView(*impl16_bit, 2).length());
+ EXPECT_EQ(StringView("345"), StringView(*impl16_bit, 2));
+ EXPECT_STREQ("345", StringView(*impl16_bit, 2).ToString().Utf8().data());
+
+ // StringView(StringImpl&, unsigned offset, unsigned length);
+ ASSERT_FALSE(StringView(*impl16_bit, 2, 1).Is8Bit());
+ EXPECT_FALSE(StringView(*impl16_bit, 2, 1).IsNull());
+ EXPECT_EQ(impl16_bit->Characters16() + 2,
+ StringView(*impl16_bit, 2, 1).Characters16());
+ EXPECT_EQ(1u, StringView(*impl16_bit, 2, 1).length());
+ EXPECT_EQ(StringView("3"), StringView(*impl16_bit, 2, 1));
+ EXPECT_STREQ("3", StringView(*impl16_bit, 2, 1).ToString().Utf8().data());
+}
+
+TEST(StringViewTest, ConstructionString8) {
+ String string8_bit = String(StringImpl::Create(kChars8, 5));
+
+ // StringView(const String&);
+ ASSERT_TRUE(StringView(string8_bit).Is8Bit());
+ EXPECT_FALSE(StringView(string8_bit).IsNull());
+ EXPECT_EQ(string8_bit.Characters8(), StringView(string8_bit).Characters8());
+ EXPECT_EQ(string8_bit.length(), StringView(string8_bit).length());
+ EXPECT_STREQ(kChars, StringView(string8_bit).ToString().Utf8().data());
+
+ // StringView(const String&, unsigned offset);
+ ASSERT_TRUE(StringView(string8_bit, 2).Is8Bit());
+ EXPECT_FALSE(StringView(string8_bit, 2).IsNull());
+ EXPECT_EQ(string8_bit.Characters8() + 2,
+ StringView(string8_bit, 2).Characters8());
+ EXPECT_EQ(3u, StringView(string8_bit, 2).length());
+ EXPECT_EQ(StringView("345"), StringView(string8_bit, 2));
+ EXPECT_STREQ("345", StringView(string8_bit, 2).ToString().Utf8().data());
+
+ // StringView(const String&, unsigned offset, unsigned length);
+ ASSERT_TRUE(StringView(string8_bit, 2, 1).Is8Bit());
+ EXPECT_FALSE(StringView(string8_bit, 2, 1).IsNull());
+ EXPECT_EQ(string8_bit.Characters8() + 2,
+ StringView(string8_bit, 2, 1).Characters8());
+ EXPECT_EQ(1u, StringView(string8_bit, 2, 1).length());
+ EXPECT_EQ(StringView("3"), StringView(string8_bit, 2, 1));
+ EXPECT_STREQ("3", StringView(string8_bit, 2, 1).ToString().Utf8().data());
+}
+
+TEST(StringViewTest, ConstructionString16) {
+ String string16_bit = String(StringImpl::Create(kChars16, 5));
+
+ // StringView(const String&);
+ ASSERT_FALSE(StringView(string16_bit).Is8Bit());
+ EXPECT_FALSE(StringView(string16_bit).IsNull());
+ EXPECT_EQ(string16_bit.Characters16(),
+ StringView(string16_bit).Characters16());
+ EXPECT_EQ(string16_bit.length(), StringView(string16_bit).length());
+ EXPECT_STREQ(kChars, StringView(string16_bit).ToString().Utf8().data());
+
+ // StringView(const String&, unsigned offset);
+ ASSERT_FALSE(StringView(string16_bit, 2).Is8Bit());
+ EXPECT_FALSE(StringView(string16_bit, 2).IsNull());
+ EXPECT_EQ(string16_bit.Characters16() + 2,
+ StringView(string16_bit, 2).Characters16());
+ EXPECT_EQ(3u, StringView(string16_bit, 2).length());
+ EXPECT_EQ(StringView("345"), StringView(string16_bit, 2));
+ EXPECT_STREQ("345", StringView(string16_bit, 2).ToString().Utf8().data());
+
+ // StringView(const String&, unsigned offset, unsigned length);
+ ASSERT_FALSE(StringView(string16_bit, 2, 1).Is8Bit());
+ EXPECT_FALSE(StringView(string16_bit, 2, 1).IsNull());
+ EXPECT_EQ(string16_bit.Characters16() + 2,
+ StringView(string16_bit, 2, 1).Characters16());
+ EXPECT_EQ(1u, StringView(string16_bit, 2, 1).length());
+ EXPECT_EQ(StringView("3"), StringView(string16_bit, 2, 1));
+ EXPECT_STREQ("3", StringView(string16_bit, 2, 1).ToString().Utf8().data());
+}
+
+TEST(StringViewTest, ConstructionAtomicString8) {
+ AtomicString atom8_bit = AtomicString(StringImpl::Create(kChars8, 5));
+
+ // StringView(const AtomicString&);
+ ASSERT_TRUE(StringView(atom8_bit).Is8Bit());
+ EXPECT_FALSE(StringView(atom8_bit).IsNull());
+ EXPECT_EQ(atom8_bit.Characters8(), StringView(atom8_bit).Characters8());
+ EXPECT_EQ(atom8_bit.length(), StringView(atom8_bit).length());
+ EXPECT_STREQ(kChars, StringView(atom8_bit).ToString().Utf8().data());
+
+ // StringView(const AtomicString&, unsigned offset);
+ ASSERT_TRUE(StringView(atom8_bit, 2).Is8Bit());
+ EXPECT_FALSE(StringView(atom8_bit, 2).IsNull());
+ EXPECT_EQ(atom8_bit.Characters8() + 2,
+ StringView(atom8_bit, 2).Characters8());
+ EXPECT_EQ(3u, StringView(atom8_bit, 2).length());
+ EXPECT_EQ(StringView("345"), StringView(atom8_bit, 2));
+ EXPECT_STREQ("345", StringView(atom8_bit, 2).ToString().Utf8().data());
+
+ // StringView(const AtomicString&, unsigned offset, unsigned length);
+ ASSERT_TRUE(StringView(atom8_bit, 2, 1).Is8Bit());
+ EXPECT_FALSE(StringView(atom8_bit, 2, 1).IsNull());
+ EXPECT_EQ(atom8_bit.Characters8() + 2,
+ StringView(atom8_bit, 2, 1).Characters8());
+ EXPECT_EQ(1u, StringView(atom8_bit, 2, 1).length());
+ EXPECT_EQ(StringView("3"), StringView(atom8_bit, 2, 1));
+ EXPECT_STREQ("3", StringView(atom8_bit, 2, 1).ToString().Utf8().data());
+}
+
+TEST(StringViewTest, ConstructionAtomicString16) {
+ AtomicString atom16_bit = AtomicString(StringImpl::Create(kChars16, 5));
+
+ // StringView(const AtomicString&);
+ ASSERT_FALSE(StringView(atom16_bit).Is8Bit());
+ EXPECT_FALSE(StringView(atom16_bit).IsNull());
+ EXPECT_EQ(atom16_bit.Characters16(), StringView(atom16_bit).Characters16());
+ EXPECT_EQ(atom16_bit.length(), StringView(atom16_bit).length());
+ EXPECT_STREQ(kChars, StringView(atom16_bit).ToString().Utf8().data());
+
+ // StringView(const AtomicString&, unsigned offset);
+ ASSERT_FALSE(StringView(atom16_bit, 2).Is8Bit());
+ EXPECT_FALSE(StringView(atom16_bit, 2).IsNull());
+ EXPECT_EQ(atom16_bit.Characters16() + 2,
+ StringView(atom16_bit, 2).Characters16());
+ EXPECT_EQ(3u, StringView(atom16_bit, 2).length());
+ EXPECT_EQ(StringView("345"), StringView(atom16_bit, 2));
+ EXPECT_STREQ("345", StringView(atom16_bit, 2).ToString().Utf8().data());
+
+ // StringView(const AtomicString&, unsigned offset, unsigned length);
+ ASSERT_FALSE(StringView(atom16_bit, 2, 1).Is8Bit());
+ EXPECT_FALSE(StringView(atom16_bit, 2, 1).IsNull());
+ EXPECT_EQ(atom16_bit.Characters16() + 2,
+ StringView(atom16_bit, 2, 1).Characters16());
+ EXPECT_EQ(1u, StringView(atom16_bit, 2, 1).length());
+ EXPECT_EQ(StringView("3"), StringView(atom16_bit, 2, 1));
+ EXPECT_STREQ("3", StringView(atom16_bit, 2, 1).ToString().Utf8().data());
+}
+
+TEST(StringViewTest, ConstructionStringView8) {
+ StringView view8_bit = StringView(kChars8, 5);
+
+ // StringView(StringView&);
+ ASSERT_TRUE(StringView(view8_bit).Is8Bit());
+ EXPECT_FALSE(StringView(view8_bit).IsNull());
+ EXPECT_EQ(view8_bit.Characters8(), StringView(view8_bit).Characters8());
+ EXPECT_EQ(view8_bit.length(), StringView(view8_bit).length());
+ EXPECT_STREQ(kChars, StringView(view8_bit).ToString().Utf8().data());
+
+ // StringView(const StringView&, unsigned offset);
+ ASSERT_TRUE(StringView(view8_bit, 2).Is8Bit());
+ EXPECT_FALSE(StringView(view8_bit, 2).IsNull());
+ EXPECT_EQ(view8_bit.Characters8() + 2,
+ StringView(view8_bit, 2).Characters8());
+ EXPECT_EQ(3u, StringView(view8_bit, 2).length());
+ EXPECT_EQ(StringView("345"), StringView(view8_bit, 2));
+ EXPECT_STREQ("345", StringView(view8_bit, 2).ToString().Utf8().data());
+
+ // StringView(const StringView&, unsigned offset, unsigned length);
+ ASSERT_TRUE(StringView(view8_bit, 2, 1).Is8Bit());
+ EXPECT_FALSE(StringView(view8_bit, 2, 1).IsNull());
+ EXPECT_EQ(view8_bit.Characters8() + 2,
+ StringView(view8_bit, 2, 1).Characters8());
+ EXPECT_EQ(1u, StringView(view8_bit, 2, 1).length());
+ EXPECT_EQ(StringView("3"), StringView(view8_bit, 2, 1));
+ EXPECT_STREQ("3", StringView(view8_bit, 2, 1).ToString().Utf8().data());
+}
+
+TEST(StringViewTest, ConstructionStringView16) {
+ StringView view16_bit = StringView(kChars16, 5);
+
+ // StringView(StringView&);
+ ASSERT_FALSE(StringView(view16_bit).Is8Bit());
+ EXPECT_FALSE(StringView(view16_bit).IsNull());
+ EXPECT_EQ(view16_bit.Characters16(), StringView(view16_bit).Characters16());
+ EXPECT_EQ(view16_bit.length(), StringView(view16_bit).length());
+ EXPECT_EQ(kChars, StringView(view16_bit).ToString());
+
+ // StringView(const StringView&, unsigned offset);
+ ASSERT_FALSE(StringView(view16_bit, 2).Is8Bit());
+ EXPECT_FALSE(StringView(view16_bit, 2).IsNull());
+ EXPECT_EQ(view16_bit.Characters16() + 2,
+ StringView(view16_bit, 2).Characters16());
+ EXPECT_EQ(3u, StringView(view16_bit, 2).length());
+ EXPECT_EQ(StringView("345"), StringView(view16_bit, 2));
+ EXPECT_STREQ("345", StringView(view16_bit, 2).ToString().Utf8().data());
+
+ // StringView(const StringView&, unsigned offset, unsigned length);
+ ASSERT_FALSE(StringView(view16_bit, 2, 1).Is8Bit());
+ EXPECT_FALSE(StringView(view16_bit, 2, 1).IsNull());
+ EXPECT_EQ(view16_bit.Characters16() + 2,
+ StringView(view16_bit, 2, 1).Characters16());
+ EXPECT_EQ(1u, StringView(view16_bit, 2, 1).length());
+ EXPECT_EQ(StringView("3"), StringView(view16_bit, 2, 1));
+ EXPECT_STREQ("3", StringView(view16_bit, 2, 1).ToString().Utf8().data());
+}
+
+TEST(StringViewTest, ConstructionLiteral8) {
+ // StringView(const LChar* chars);
+ ASSERT_TRUE(StringView(kChars8).Is8Bit());
+ EXPECT_FALSE(StringView(kChars8).IsNull());
+ EXPECT_EQ(kChars8, StringView(kChars8).Characters8());
+ EXPECT_EQ(5u, StringView(kChars8).length());
+ EXPECT_STREQ(kChars, StringView(kChars8).ToString().Utf8().data());
+
+ // StringView(const char* chars);
+ ASSERT_TRUE(StringView(kChars).Is8Bit());
+ EXPECT_FALSE(StringView(kChars).IsNull());
+ EXPECT_EQ(kChars8, StringView(kChars).Characters8());
+ EXPECT_EQ(5u, StringView(kChars).length());
+ EXPECT_STREQ(kChars, StringView(kChars).ToString().Utf8().data());
+
+ // StringView(const LChar* chars, unsigned length);
+ ASSERT_TRUE(StringView(kChars8, 2).Is8Bit());
+ EXPECT_FALSE(StringView(kChars8, 2).IsNull());
+ EXPECT_EQ(2u, StringView(kChars8, 2).length());
+ EXPECT_EQ(StringView("12"), StringView(kChars8, 2));
+ EXPECT_STREQ("12", StringView(kChars8, 2).ToString().Utf8().data());
+
+ // StringView(const char* chars, unsigned length);
+ ASSERT_TRUE(StringView(kChars, 2).Is8Bit());
+ EXPECT_FALSE(StringView(kChars, 2).IsNull());
+ EXPECT_EQ(2u, StringView(kChars, 2).length());
+ EXPECT_EQ(StringView("12"), StringView(kChars, 2));
+ EXPECT_STREQ("12", StringView(kChars, 2).ToString().Utf8().data());
+}
+
+TEST(StringViewTest, ConstructionLiteral16) {
+ // StringView(const UChar* chars);
+ ASSERT_FALSE(StringView(kChars16).Is8Bit());
+ EXPECT_FALSE(StringView(kChars16).IsNull());
+ EXPECT_EQ(kChars16, StringView(kChars16).Characters16());
+ EXPECT_EQ(5u, StringView(kChars16).length());
+ EXPECT_EQ(String(kChars16), StringView(kChars16).ToString().Utf8().data());
+
+ // StringView(const UChar* chars, unsigned length);
+ ASSERT_FALSE(StringView(kChars16, 2).Is8Bit());
+ EXPECT_FALSE(StringView(kChars16, 2).IsNull());
+ EXPECT_EQ(kChars16, StringView(kChars16, 2).Characters16());
+ EXPECT_EQ(StringView("12"), StringView(kChars16, 2));
+ EXPECT_EQ(StringView(reinterpret_cast<const UChar*>(u"12")),
+ StringView(kChars16, 2));
+ EXPECT_EQ(2u, StringView(kChars16, 2).length());
+ EXPECT_EQ(String("12"), StringView(kChars16, 2).ToString());
+}
+
+TEST(StringViewTest, IsEmpty) {
+ EXPECT_FALSE(StringView(kChars).IsEmpty());
+ EXPECT_TRUE(StringView(kChars, 0).IsEmpty());
+ EXPECT_FALSE(StringView(String(kChars)).IsEmpty());
+ EXPECT_TRUE(StringView(String(kChars), 5).IsEmpty());
+ EXPECT_TRUE(StringView(String(kChars), 4, 0).IsEmpty());
+ EXPECT_TRUE(StringView().IsEmpty());
+ EXPECT_TRUE(StringView("").IsEmpty());
+ EXPECT_TRUE(StringView(reinterpret_cast<const UChar*>(u"")).IsEmpty());
+ EXPECT_FALSE(StringView(kChars16).IsEmpty());
+}
+
+TEST(StringViewTest, ToString) {
+ EXPECT_EQ(g_empty_string.Impl(), StringView("").ToString().Impl());
+ EXPECT_EQ(g_null_atom.Impl(), StringView().ToString().Impl());
+ // NOTE: All the construction tests also check toString().
+}
+
+TEST(StringViewTest, ToAtomicString) {
+ EXPECT_EQ(g_null_atom.Impl(), StringView().ToAtomicString());
+ EXPECT_EQ(g_empty_atom.Impl(), StringView("").ToAtomicString());
+ EXPECT_EQ(AtomicString("12"), StringView(kChars8, 2).ToAtomicString());
+ // AtomicString will convert to 8bit if possible when creating the string.
+ EXPECT_EQ(AtomicString("12").Impl(),
+ StringView(kChars16, 2).ToAtomicString().Impl());
+}
+
+TEST(StringViewTest, ToStringImplSharing) {
+ String string(kChars);
+ EXPECT_EQ(string.Impl(), StringView(string).SharedImpl());
+ EXPECT_EQ(string.Impl(), StringView(string).ToString().Impl());
+ EXPECT_EQ(string.Impl(), StringView(string).ToAtomicString().Impl());
+}
+
+TEST(StringViewTest, NullString) {
+ EXPECT_TRUE(StringView().IsNull());
+ EXPECT_TRUE(StringView(String()).IsNull());
+ EXPECT_TRUE(StringView(AtomicString()).IsNull());
+ EXPECT_TRUE(StringView(static_cast<const char*>(nullptr)).IsNull());
+ StringView view(kChars);
+ EXPECT_FALSE(view.IsNull());
+ view.Clear();
+ EXPECT_TRUE(view.IsNull());
+ EXPECT_EQ(String(), StringView());
+ EXPECT_TRUE(StringView().ToString().IsNull());
+ EXPECT_FALSE(EqualStringView(StringView(), ""));
+ EXPECT_TRUE(EqualStringView(StringView(), StringView()));
+ EXPECT_FALSE(EqualStringView(StringView(), "abc"));
+ EXPECT_FALSE(EqualStringView("abc", StringView()));
+ EXPECT_FALSE(EqualIgnoringASCIICase(StringView(), ""));
+ EXPECT_TRUE(EqualIgnoringASCIICase(StringView(), StringView()));
+ EXPECT_FALSE(EqualIgnoringASCIICase(StringView(), "abc"));
+ EXPECT_FALSE(EqualIgnoringASCIICase("abc", StringView()));
+}
+
+TEST(StringViewTest, IndexAccess) {
+ StringView view8(kChars8);
+ EXPECT_EQ('1', view8[0]);
+ EXPECT_EQ('3', view8[2]);
+ StringView view16(kChars16);
+ EXPECT_EQ('1', view16[0]);
+ EXPECT_EQ('3', view16[2]);
+}
+
+TEST(StringViewTest, EqualIgnoringASCIICase) {
+ static const char* link8 = "link";
+ static const char* link_caps8 = "LINK";
+ static const char* non_ascii8 = "a\xE1";
+ static const char* non_ascii_caps8 = "A\xE1";
+ static const char* non_ascii_invalid8 = "a\xC1";
+
+ static const UChar kLink16[5] = {0x006c, 0x0069, 0x006e, 0x006b, 0}; // link
+ static const UChar kLinkCaps16[5] = {0x004c, 0x0049, 0x004e, 0x004b,
+ 0}; // LINK
+ static const UChar kNonASCII16[3] = {0x0061, 0x00e1, 0}; // a\xE1
+ static const UChar kNonASCIICaps16[3] = {0x0041, 0x00e1, 0}; // A\xE1
+ static const UChar kNonASCIIInvalid16[3] = {0x0061, 0x00c1, 0}; // a\xC1
+
+ EXPECT_TRUE(EqualIgnoringASCIICase(StringView(kLink16), link8));
+ EXPECT_TRUE(EqualIgnoringASCIICase(StringView(kLink16), kLinkCaps16));
+ EXPECT_TRUE(EqualIgnoringASCIICase(StringView(kLink16), link_caps8));
+ EXPECT_TRUE(EqualIgnoringASCIICase(StringView(link8), link_caps8));
+ EXPECT_TRUE(EqualIgnoringASCIICase(StringView(link8), kLink16));
+
+ EXPECT_TRUE(EqualIgnoringASCIICase(StringView(non_ascii8), non_ascii_caps8));
+ EXPECT_TRUE(EqualIgnoringASCIICase(StringView(non_ascii8), kNonASCIICaps16));
+ EXPECT_TRUE(EqualIgnoringASCIICase(StringView(kNonASCII16), kNonASCIICaps16));
+ EXPECT_TRUE(EqualIgnoringASCIICase(StringView(kNonASCII16), non_ascii_caps8));
+ EXPECT_FALSE(
+ EqualIgnoringASCIICase(StringView(non_ascii8), non_ascii_invalid8));
+ EXPECT_FALSE(
+ EqualIgnoringASCIICase(StringView(non_ascii8), kNonASCIIInvalid16));
+
+ EXPECT_TRUE(EqualIgnoringASCIICase(StringView("link"), "lInK"));
+ EXPECT_FALSE(EqualIgnoringASCIICase(StringView("link"), "INKL"));
+ EXPECT_FALSE(
+ EqualIgnoringASCIICase(StringView("link"), "link different length"));
+ EXPECT_FALSE(
+ EqualIgnoringASCIICase(StringView("link different length"), "link"));
+
+ EXPECT_TRUE(EqualIgnoringASCIICase(StringView(""), ""));
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/text_codec.cc b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec.cc
new file mode 100644
index 00000000000..e1526e29798
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec.cc
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2004, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2006 Alexey Proskuryakov <ap@nypop.com>
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/wtf/text/text_codec.h"
+
+namespace WTF {
+
+TextCodec::~TextCodec() = default;
+
+int TextCodec::GetUnencodableReplacement(
+ unsigned code_point,
+ UnencodableHandling handling,
+ UnencodableReplacementArray replacement) {
+ switch (handling) {
+ case kEntitiesForUnencodables:
+ snprintf(replacement, sizeof(UnencodableReplacementArray), "&#%u;",
+ code_point);
+ return static_cast<int>(strlen(replacement));
+ case kURLEncodedEntitiesForUnencodables:
+ snprintf(replacement, sizeof(UnencodableReplacementArray),
+ "%%26%%23%u%%3B", code_point);
+ return static_cast<int>(strlen(replacement));
+
+ case kCSSEncodedEntitiesForUnencodables:
+ snprintf(replacement, sizeof(UnencodableReplacementArray), "\\%x ",
+ code_point);
+ return static_cast<int>(strlen(replacement));
+ }
+ NOTREACHED();
+ replacement[0] = 0;
+ return 0;
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/text_codec.h b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec.h
new file mode 100644
index 00000000000..5bc0c2793b4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2004, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2006 Alexey Proskuryakov <ap@nypop.com>
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_TEXT_CODEC_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_TEXT_CODEC_H_
+
+#include <memory>
+#include "base/macros.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace WTF {
+
+class TextEncoding;
+
+// Specifies what will happen when a character is encountered that is
+// not encodable in the character set.
+enum UnencodableHandling {
+ // Encodes the character as an XML entity. For example, U+06DE
+ // would be "&#1758;" (0x6DE = 1758 in octal).
+ kEntitiesForUnencodables,
+
+ // Encodes the character as en entity as above, but escaped
+ // non-alphanumeric characters. This is used in URLs.
+ // For example, U+6DE would be "%26%231758%3B".
+ kURLEncodedEntitiesForUnencodables,
+
+ // Encodes the character as a CSS entity. For example U+06DE
+ // would be \06de. See: https://www.w3.org/TR/css-syntax-3/#escaping
+ kCSSEncodedEntitiesForUnencodables,
+};
+
+typedef char UnencodableReplacementArray[32];
+
+enum FlushBehavior {
+ // More bytes are coming, don't flush the codec.
+ kDoNotFlush = 0,
+
+ // A fetch has hit EOF. Some codecs handle fetches differently, for compat
+ // reasons.
+ kFetchEOF,
+
+ // Do a full flush of the codec.
+ kDataEOF
+};
+
+static_assert(!kDoNotFlush, "DoNotFlush should be falsy");
+static_assert(kFetchEOF, "FetchEOF should be truthy");
+static_assert(kDataEOF, "DataEOF should be truthy");
+
+class WTF_EXPORT TextCodec {
+ USING_FAST_MALLOC(TextCodec);
+
+ public:
+ TextCodec() = default;
+ virtual ~TextCodec();
+
+ String Decode(const char* str,
+ size_t length,
+ FlushBehavior flush = kDoNotFlush) {
+ bool ignored;
+ return Decode(str, length, flush, false, ignored);
+ }
+
+ virtual String Decode(const char*,
+ size_t length,
+ FlushBehavior,
+ bool stop_on_error,
+ bool& saw_error) = 0;
+ virtual CString Encode(const UChar*, size_t length, UnencodableHandling) = 0;
+ virtual CString Encode(const LChar*, size_t length, UnencodableHandling) = 0;
+
+ // Fills a null-terminated string representation of the given
+ // unencodable character into the given replacement buffer.
+ // The length of the string (not including the null) will be returned.
+ static int GetUnencodableReplacement(unsigned code_point,
+ UnencodableHandling,
+ UnencodableReplacementArray);
+
+ DISALLOW_COPY_AND_ASSIGN(TextCodec);
+};
+
+typedef void (*EncodingNameRegistrar)(const char* alias, const char* name);
+
+typedef std::unique_ptr<TextCodec> (
+ *NewTextCodecFunction)(const TextEncoding&, const void* additional_data);
+typedef void (*TextCodecRegistrar)(const char* name,
+ NewTextCodecFunction,
+ const void* additional_data);
+
+} // namespace WTF
+
+using WTF::TextCodec;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_TEXT_CODEC_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_ascii_fast_path.h b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_ascii_fast_path.h
new file mode 100644
index 00000000000..d03a90f0f1a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_ascii_fast_path.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_TEXT_CODEC_ASCII_FAST_PATH_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_TEXT_CODEC_ASCII_FAST_PATH_H_
+
+#include "third_party/blink/renderer/platform/wtf/text/ascii_fast_path.h"
+
+namespace WTF {
+
+template <size_t size>
+struct UCharByteFiller;
+template <>
+struct UCharByteFiller<4> {
+ static void Copy(LChar* destination, const uint8_t* source) {
+ memcpy(destination, source, 4);
+ }
+
+ static void Copy(UChar* destination, const uint8_t* source) {
+ destination[0] = source[0];
+ destination[1] = source[1];
+ destination[2] = source[2];
+ destination[3] = source[3];
+ }
+};
+template <>
+struct UCharByteFiller<8> {
+ static void Copy(LChar* destination, const uint8_t* source) {
+ memcpy(destination, source, 8);
+ }
+
+ static void Copy(UChar* destination, const uint8_t* source) {
+ destination[0] = source[0];
+ destination[1] = source[1];
+ destination[2] = source[2];
+ destination[3] = source[3];
+ destination[4] = source[4];
+ destination[5] = source[5];
+ destination[6] = source[6];
+ destination[7] = source[7];
+ }
+};
+
+inline void CopyASCIIMachineWord(LChar* destination, const uint8_t* source) {
+ UCharByteFiller<sizeof(WTF::MachineWord)>::Copy(destination, source);
+}
+
+inline void CopyASCIIMachineWord(UChar* destination, const uint8_t* source) {
+ UCharByteFiller<sizeof(WTF::MachineWord)>::Copy(destination, source);
+}
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_TEXT_CODEC_ASCII_FAST_PATH_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_icu.cc b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_icu.cc
new file mode 100644
index 00000000000..69ff38631c8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_icu.cc
@@ -0,0 +1,740 @@
+/*
+ * Copyright (C) 2004, 2006, 2007, 2008, 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Alexey Proskuryakov <ap@nypop.com>
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/wtf/text/text_codec_icu.h"
+
+#include <memory>
+
+#include <unicode/ucnv.h>
+#include <unicode/ucnv_cb.h>
+#include <memory>
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/string_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+#include "third_party/blink/renderer/platform/wtf/threading.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_thread_data.h"
+
+namespace WTF {
+
+const size_t kConversionBufferSize = 16384;
+
+ICUConverterWrapper::~ICUConverterWrapper() {
+ if (converter)
+ ucnv_close(converter);
+}
+
+static UConverter*& CachedConverterICU() {
+ return WtfThreadData().CachedConverterICU().converter;
+}
+
+std::unique_ptr<TextCodec> TextCodecICU::Create(const TextEncoding& encoding,
+ const void*) {
+ return base::WrapUnique(new TextCodecICU(encoding));
+}
+
+namespace {
+bool IncludeAlias(const char* alias) {
+#if !defined(USING_SYSTEM_ICU)
+ // Chromium's build of ICU includes *-html aliases to manage the encoding
+ // labels defined in the Encoding Standard, but these must not be
+ // web-exposed.
+ const char* kSuffix = "-html";
+ const size_t kSuffixLength = 5;
+ size_t alias_length = strlen(alias);
+ if ((alias_length >= kSuffixLength) &&
+ !strcmp(alias + alias_length - kSuffixLength, kSuffix))
+ return false;
+#endif
+ return true;
+}
+} // namespace
+
+void TextCodecICU::RegisterEncodingNames(EncodingNameRegistrar registrar) {
+ // We register Hebrew with logical ordering using a separate name.
+ // Otherwise, this would share the same canonical name as the
+ // visual ordering case, and then TextEncoding could not tell them
+ // apart; ICU treats these names as synonyms.
+ registrar("ISO-8859-8-I", "ISO-8859-8-I");
+
+ int32_t num_encodings = ucnv_countAvailable();
+ for (int32_t i = 0; i < num_encodings; ++i) {
+ const char* name = ucnv_getAvailableName(i);
+ UErrorCode error = U_ZERO_ERROR;
+#if !defined(USING_SYSTEM_ICU)
+ const char* primary_standard = "HTML";
+ const char* secondary_standard = "MIME";
+#else
+ const char* primary_standard = "MIME";
+ const char* secondary_standard = "IANA";
+#endif
+ const char* standard_name =
+ ucnv_getStandardName(name, primary_standard, &error);
+ if (U_FAILURE(error) || !standard_name) {
+ error = U_ZERO_ERROR;
+ // Try IANA to pick up 'windows-12xx' and other names
+ // which are not preferred MIME names but are widely used.
+ standard_name = ucnv_getStandardName(name, secondary_standard, &error);
+ if (U_FAILURE(error) || !standard_name)
+ continue;
+ }
+
+#if defined(USING_SYSTEM_ICU)
+ // Explicitly do not support UTF-32. https://crbug.com/417850
+ // Bundled ICU does not return these names.
+ if (!strcmp(standard_name, "UTF-32") ||
+ !strcmp(standard_name, "UTF-32LE") ||
+ !strcmp(standard_name, "UTF-32BE")) {
+ continue;
+ }
+#endif
+
+// A number of these aliases are handled in Chrome's copy of ICU, but
+// Chromium can be compiled with the system ICU.
+
+// 1. Treat GB2312 encoding as GBK (its more modern superset), to match other
+// browsers.
+// 2. On the Web, GB2312 is encoded as EUC-CN or HZ, while ICU provides a native
+// encoding for encoding GB_2312-80 and several others. So, we need to
+// override this behavior, too.
+#if defined(USING_SYSTEM_ICU)
+ if (!strcmp(standard_name, "GB2312") ||
+ !strcmp(standard_name, "GB_2312-80")) {
+ standard_name = "GBK";
+ // Similarly, EUC-KR encodings all map to an extended version, but
+ // per HTML5, the canonical name still should be EUC-KR.
+ } else if (!strcmp(standard_name, "EUC-KR") ||
+ !strcmp(standard_name, "KSC_5601") ||
+ !strcmp(standard_name, "cp1363")) {
+ standard_name = "EUC-KR";
+ // And so on.
+ } else if (!strcasecmp(standard_name, "iso-8859-9")) {
+ // This name is returned in different case by ICU 3.2 and 3.6.
+ standard_name = "windows-1254";
+ } else if (!strcmp(standard_name, "TIS-620")) {
+ standard_name = "windows-874";
+ }
+#endif
+
+ registrar(standard_name, standard_name);
+
+ uint16_t num_aliases = ucnv_countAliases(name, &error);
+ DCHECK(U_SUCCESS(error));
+ if (U_SUCCESS(error))
+ for (uint16_t j = 0; j < num_aliases; ++j) {
+ error = U_ZERO_ERROR;
+ const char* alias = ucnv_getAlias(name, j, &error);
+ DCHECK(U_SUCCESS(error));
+ if (U_SUCCESS(error) && alias != standard_name && IncludeAlias(alias))
+ registrar(alias, standard_name);
+ }
+ }
+
+ // These two entries have to be added here because ICU's converter table
+ // cannot have both ISO-8859-8-I and ISO-8859-8.
+ registrar("csISO88598I", "ISO-8859-8-I");
+ registrar("logical", "ISO-8859-8-I");
+
+#if defined(USING_SYSTEM_ICU)
+ // Additional alias for MacCyrillic not present in ICU.
+ registrar("maccyrillic", "x-mac-cyrillic");
+
+ // Additional aliases that historically were present in the encoding
+ // table in WebKit on Macintosh that don't seem to be present in ICU.
+ // Perhaps we can prove these are not used on the web and remove them.
+ // Or perhaps we can get them added to ICU.
+ registrar("x-mac-roman", "macintosh");
+ registrar("x-mac-ukrainian", "x-mac-cyrillic");
+ registrar("cn-big5", "Big5");
+ registrar("x-x-big5", "Big5");
+ registrar("cn-gb", "GBK");
+ registrar("csgb231280", "GBK");
+ registrar("x-euc-cn", "GBK");
+ registrar("x-gbk", "GBK");
+ registrar("koi", "KOI8-R");
+ registrar("visual", "ISO-8859-8");
+ registrar("winarabic", "windows-1256");
+ registrar("winbaltic", "windows-1257");
+ registrar("wincyrillic", "windows-1251");
+ registrar("iso-8859-11", "windows-874");
+ registrar("iso8859-11", "windows-874");
+ registrar("dos-874", "windows-874");
+ registrar("wingreek", "windows-1253");
+ registrar("winhebrew", "windows-1255");
+ registrar("winlatin2", "windows-1250");
+ registrar("winturkish", "windows-1254");
+ registrar("winvietnamese", "windows-1258");
+ registrar("x-cp1250", "windows-1250");
+ registrar("x-cp1251", "windows-1251");
+ registrar("x-euc", "EUC-JP");
+ registrar("x-windows-949", "EUC-KR");
+ registrar("KSC5601", "EUC-KR");
+ registrar("x-uhc", "EUC-KR");
+ registrar("shift-jis", "Shift_JIS");
+
+ // Alternative spelling of ISO encoding names.
+ registrar("ISO8859-1", "ISO-8859-1");
+ registrar("ISO8859-2", "ISO-8859-2");
+ registrar("ISO8859-3", "ISO-8859-3");
+ registrar("ISO8859-4", "ISO-8859-4");
+ registrar("ISO8859-5", "ISO-8859-5");
+ registrar("ISO8859-6", "ISO-8859-6");
+ registrar("ISO8859-7", "ISO-8859-7");
+ registrar("ISO8859-8", "ISO-8859-8");
+ registrar("ISO8859-8-I", "ISO-8859-8-I");
+ registrar("ISO8859-9", "ISO-8859-9");
+ registrar("ISO8859-10", "ISO-8859-10");
+ registrar("ISO8859-13", "ISO-8859-13");
+ registrar("ISO8859-14", "ISO-8859-14");
+ registrar("ISO8859-15", "ISO-8859-15");
+ // No need to have an entry for ISO8859-16. ISO-8859-16 has just one label
+ // listed in WHATWG Encoding Living Standard, http://encoding.spec.whatwg.org/
+
+ // Additional aliases present in the WHATWG Encoding Standard
+ // and Firefox (as of Oct 2014), but not in the upstream ICU.
+ // Three entries for windows-1252 need not be listed here because
+ // TextCodecLatin1 registers them.
+ registrar("csiso58gb231280", "GBK");
+ registrar("csiso88596e", "ISO-8859-6");
+ registrar("csiso88596i", "ISO-8859-6");
+ registrar("csiso88598e", "ISO-8859-8");
+ registrar("gb_2312", "GBK");
+ registrar("iso88592", "ISO-8859-2");
+ registrar("iso88593", "ISO-8859-3");
+ registrar("iso88594", "ISO-8859-4");
+ registrar("iso88595", "ISO-8859-5");
+ registrar("iso88596", "ISO-8859-6");
+ registrar("iso88597", "ISO-8859-7");
+ registrar("iso88598", "ISO-8859-8");
+ registrar("iso88599", "windows-1254");
+ registrar("iso885910", "ISO-8859-10");
+ registrar("iso885911", "windows-874");
+ registrar("iso885913", "ISO-8859-13");
+ registrar("iso885914", "ISO-8859-14");
+ registrar("iso885915", "ISO-8859-15");
+ registrar("iso_8859-2", "ISO-8859-2");
+ registrar("iso_8859-3", "ISO-8859-3");
+ registrar("iso_8859-4", "ISO-8859-4");
+ registrar("iso_8859-5", "ISO-8859-5");
+ registrar("iso_8859-6", "ISO-8859-6");
+ registrar("iso_8859-7", "ISO-8859-7");
+ registrar("iso_8859-8", "ISO-8859-8");
+ registrar("iso_8859-9", "windows-1254");
+ registrar("iso_8859-15", "ISO-8859-15");
+ registrar("koi8_r", "KOI8-R");
+ registrar("x-cp1253", "windows-1253");
+ registrar("x-cp1254", "windows-1254");
+ registrar("x-cp1255", "windows-1255");
+ registrar("x-cp1256", "windows-1256");
+ registrar("x-cp1257", "windows-1257");
+ registrar("x-cp1258", "windows-1258");
+#endif
+}
+
+void TextCodecICU::RegisterCodecs(TextCodecRegistrar registrar) {
+ // See comment above in registerEncodingNames.
+ registrar("ISO-8859-8-I", Create, nullptr);
+
+ int32_t num_encodings = ucnv_countAvailable();
+ for (int32_t i = 0; i < num_encodings; ++i) {
+ const char* name = ucnv_getAvailableName(i);
+ UErrorCode error = U_ZERO_ERROR;
+ const char* standard_name = ucnv_getStandardName(name, "MIME", &error);
+ if (!U_SUCCESS(error) || !standard_name) {
+ error = U_ZERO_ERROR;
+ standard_name = ucnv_getStandardName(name, "IANA", &error);
+ if (!U_SUCCESS(error) || !standard_name)
+ continue;
+ }
+#if defined(USING_SYSTEM_ICU)
+ // Explicitly do not support UTF-32. https://crbug.com/417850
+ // Bundled ICU does not return these names.
+ if (!strcmp(standard_name, "UTF-32") ||
+ !strcmp(standard_name, "UTF-32LE") ||
+ !strcmp(standard_name, "UTF-32BE")) {
+ continue;
+ }
+#endif
+ registrar(standard_name, Create, nullptr);
+ }
+}
+
+TextCodecICU::TextCodecICU(const TextEncoding& encoding)
+ : encoding_(encoding) {}
+
+TextCodecICU::~TextCodecICU() {
+ ReleaseICUConverter();
+}
+
+void TextCodecICU::ReleaseICUConverter() const {
+ if (converter_icu_) {
+ UConverter*& cached_converter = CachedConverterICU();
+ if (cached_converter)
+ ucnv_close(cached_converter);
+ cached_converter = converter_icu_;
+ converter_icu_ = nullptr;
+ }
+}
+
+void TextCodecICU::CreateICUConverter() const {
+ DCHECK(!converter_icu_);
+
+#if defined(USING_SYSTEM_ICU)
+ const char* name = encoding_.GetName();
+ needs_gbk_fallbacks_ =
+ name[0] == 'G' && name[1] == 'B' && name[2] == 'K' && !name[3];
+#endif
+
+ UErrorCode err;
+
+ UConverter*& cached_converter = CachedConverterICU();
+ if (cached_converter) {
+ err = U_ZERO_ERROR;
+ const char* cached_name = ucnv_getName(cached_converter, &err);
+ if (U_SUCCESS(err) && encoding_ == TextEncoding(cached_name)) {
+ converter_icu_ = cached_converter;
+ cached_converter = nullptr;
+ return;
+ }
+ }
+
+ err = U_ZERO_ERROR;
+ converter_icu_ = ucnv_open(encoding_.GetName(), &err);
+ DLOG_IF(ERROR, err == U_AMBIGUOUS_ALIAS_WARNING)
+ << "ICU ambiguous alias warning for encoding: " << encoding_.GetName();
+ if (converter_icu_)
+ ucnv_setFallback(converter_icu_, TRUE);
+}
+
+int TextCodecICU::DecodeToBuffer(UChar* target,
+ UChar* target_limit,
+ const char*& source,
+ const char* source_limit,
+ int32_t* offsets,
+ bool flush,
+ UErrorCode& err) {
+ UChar* target_start = target;
+ err = U_ZERO_ERROR;
+ ucnv_toUnicode(converter_icu_, &target, target_limit, &source, source_limit,
+ offsets, flush, &err);
+ return target - target_start;
+}
+
+class ErrorCallbackSetter final {
+ STACK_ALLOCATED();
+
+ public:
+ ErrorCallbackSetter(UConverter* converter, bool stop_on_error)
+ : converter_(converter), should_stop_on_encoding_errors_(stop_on_error) {
+ if (should_stop_on_encoding_errors_) {
+ UErrorCode err = U_ZERO_ERROR;
+ ucnv_setToUCallBack(converter_, UCNV_TO_U_CALLBACK_STOP, nullptr,
+ &saved_action_, &saved_context_, &err);
+ DCHECK_EQ(err, U_ZERO_ERROR);
+ }
+ }
+ ~ErrorCallbackSetter() {
+ if (should_stop_on_encoding_errors_) {
+ UErrorCode err = U_ZERO_ERROR;
+ const void* old_context;
+ UConverterToUCallback old_action;
+ ucnv_setToUCallBack(converter_, saved_action_, saved_context_,
+ &old_action, &old_context, &err);
+ DCHECK_EQ(old_action, UCNV_TO_U_CALLBACK_STOP);
+ DCHECK(!old_context);
+ DCHECK_EQ(err, U_ZERO_ERROR);
+ }
+ }
+
+ private:
+ UConverter* converter_;
+ bool should_stop_on_encoding_errors_;
+ const void* saved_context_;
+ UConverterToUCallback saved_action_;
+};
+
+String TextCodecICU::Decode(const char* bytes,
+ size_t length,
+ FlushBehavior flush,
+ bool stop_on_error,
+ bool& saw_error) {
+ // Get a converter for the passed-in encoding.
+ if (!converter_icu_) {
+ CreateICUConverter();
+ DCHECK(converter_icu_);
+ if (!converter_icu_) {
+ DLOG(ERROR)
+ << "error creating ICU encoder even though encoding was in table";
+ return String();
+ }
+ }
+
+ ErrorCallbackSetter callback_setter(converter_icu_, stop_on_error);
+
+ StringBuilder result;
+
+ UChar buffer[kConversionBufferSize];
+ UChar* buffer_limit = buffer + kConversionBufferSize;
+ const char* source = reinterpret_cast<const char*>(bytes);
+ const char* source_limit = source + length;
+ int32_t* offsets = nullptr;
+ UErrorCode err = U_ZERO_ERROR;
+
+ do {
+ int uchars_decoded =
+ DecodeToBuffer(buffer, buffer_limit, source, source_limit, offsets,
+ flush != kDoNotFlush, err);
+ result.Append(buffer, uchars_decoded);
+ } while (err == U_BUFFER_OVERFLOW_ERROR);
+
+ if (U_FAILURE(err)) {
+ // flush the converter so it can be reused, and not be bothered by this
+ // error.
+ do {
+ DecodeToBuffer(buffer, buffer_limit, source, source_limit, offsets, true,
+ err);
+ } while (source < source_limit);
+ saw_error = true;
+ }
+
+#if !defined(USING_SYSTEM_ICU)
+ // Chrome's copy of ICU does not have the issue described below.
+ return result.ToString();
+#else
+ String resultString = result.ToString();
+
+ // <http://bugs.webkit.org/show_bug.cgi?id=17014>
+ // Simplified Chinese pages use the code A3A0 to mean "full-width space", but
+ // ICU decodes it as U+E5E5.
+ if (!strcmp(encoding_.GetName(), "GBK")) {
+ if (!strcasecmp(encoding_.GetName(), "gb18030"))
+ resultString.Replace(0xE5E5, ideographicSpaceCharacter);
+ // Make GBK compliant to the encoding spec and align with GB18030
+ resultString.Replace(0x01F9, 0xE7C8);
+ // FIXME: Once https://www.w3.org/Bugs/Public/show_bug.cgi?id=28740#c3
+ // is resolved, add U+1E3F => 0xE7C7.
+ }
+
+ return resultString;
+#endif
+}
+
+#if defined(USING_SYSTEM_ICU)
+// U+01F9 and U+1E3F have to be mapped to xA8xBF and xA8xBC per the encoding
+// spec, but ICU converter does not have them.
+static UChar FallbackForGBK(UChar32 character) {
+ switch (character) {
+ case 0x01F9:
+ return 0xE7C8; // mapped to xA8xBF by ICU.
+ case 0x1E3F:
+ return 0xE7C7; // mapped to xA8xBC by ICU.
+ }
+ return 0;
+}
+#endif
+
+// Generic helper for writing escaped entities using the specfied
+// UnencodableHandling.
+static void FormatEscapedEntityCallback(const void* context,
+ UConverterFromUnicodeArgs* from_u_args,
+ const UChar* code_units,
+ int32_t length,
+ UChar32 code_point,
+ UConverterCallbackReason reason,
+ UErrorCode* err,
+ UnencodableHandling handling) {
+ if (reason == UCNV_UNASSIGNED) {
+ *err = U_ZERO_ERROR;
+
+ UnencodableReplacementArray entity;
+ int entity_len =
+ TextCodec::GetUnencodableReplacement(code_point, handling, entity);
+ String entity_u(entity, entity_len);
+ entity_u.Ensure16Bit();
+ const UChar* entity_u_pointers[2] = {
+ entity_u.Characters16(), entity_u.Characters16() + entity_u.length(),
+ };
+ ucnv_cbFromUWriteUChars(from_u_args, entity_u_pointers,
+ entity_u_pointers[1], 0, err);
+ } else {
+ UCNV_FROM_U_CALLBACK_ESCAPE(context, from_u_args, code_units, length,
+ code_point, reason, err);
+ }
+}
+
+static void NumericEntityCallback(const void* context,
+ UConverterFromUnicodeArgs* from_u_args,
+ const UChar* code_units,
+ int32_t length,
+ UChar32 code_point,
+ UConverterCallbackReason reason,
+ UErrorCode* err) {
+ FormatEscapedEntityCallback(context, from_u_args, code_units, length,
+ code_point, reason, err,
+ kEntitiesForUnencodables);
+}
+
+// Invalid character handler when writing escaped entities in CSS encoding for
+// unrepresentable characters. See the declaration of TextCodec::encode for
+// more.
+static void CssEscapedEntityCallback(const void* context,
+ UConverterFromUnicodeArgs* from_u_args,
+ const UChar* code_units,
+ int32_t length,
+ UChar32 code_point,
+ UConverterCallbackReason reason,
+ UErrorCode* err) {
+ FormatEscapedEntityCallback(context, from_u_args, code_units, length,
+ code_point, reason, err,
+ kCSSEncodedEntitiesForUnencodables);
+}
+
+// Invalid character handler when writing escaped entities in HTML/XML encoding
+// for unrepresentable characters. See the declaration of TextCodec::encode for
+// more.
+static void UrlEscapedEntityCallback(const void* context,
+ UConverterFromUnicodeArgs* from_u_args,
+ const UChar* code_units,
+ int32_t length,
+ UChar32 code_point,
+ UConverterCallbackReason reason,
+ UErrorCode* err) {
+ FormatEscapedEntityCallback(context, from_u_args, code_units, length,
+ code_point, reason, err,
+ kURLEncodedEntitiesForUnencodables);
+}
+
+#if defined(USING_SYSTEM_ICU)
+// Substitutes special GBK characters, escaping all other unassigned entities.
+static void GbkCallbackEscape(const void* context,
+ UConverterFromUnicodeArgs* from_unicode_args,
+ const UChar* code_units,
+ int32_t length,
+ UChar32 code_point,
+ UConverterCallbackReason reason,
+ UErrorCode* err) {
+ UChar out_char;
+ if (reason == UCNV_UNASSIGNED && (out_char = FallbackForGBK(code_point))) {
+ const UChar* source = &out_char;
+ *err = U_ZERO_ERROR;
+ ucnv_cbFromUWriteUChars(from_unicode_args, &source, source + 1, 0, err);
+ return;
+ }
+ NumericEntityCallback(context, from_unicode_args, code_units, length,
+ code_point, reason, err);
+}
+
+// Combines both gbkCssEscapedEntityCallback and GBK character substitution.
+static void GbkCssEscapedEntityCallack(
+ const void* context,
+ UConverterFromUnicodeArgs* from_unicode_args,
+ const UChar* code_units,
+ int32_t length,
+ UChar32 code_point,
+ UConverterCallbackReason reason,
+ UErrorCode* err) {
+ if (reason == UCNV_UNASSIGNED) {
+ if (UChar out_char = FallbackForGBK(code_point)) {
+ const UChar* source = &out_char;
+ *err = U_ZERO_ERROR;
+ ucnv_cbFromUWriteUChars(from_unicode_args, &source, source + 1, 0, err);
+ return;
+ }
+ CssEscapedEntityCallback(context, from_unicode_args, code_units, length,
+ code_point, reason, err);
+ return;
+ }
+ UCNV_FROM_U_CALLBACK_ESCAPE(context, from_unicode_args, code_units, length,
+ code_point, reason, err);
+}
+
+// Combines both gbkUrlEscapedEntityCallback and GBK character substitution.
+static void GbkUrlEscapedEntityCallack(
+ const void* context,
+ UConverterFromUnicodeArgs* from_unicode_args,
+ const UChar* code_units,
+ int32_t length,
+ UChar32 code_point,
+ UConverterCallbackReason reason,
+ UErrorCode* err) {
+ if (reason == UCNV_UNASSIGNED) {
+ if (UChar out_char = FallbackForGBK(code_point)) {
+ const UChar* source = &out_char;
+ *err = U_ZERO_ERROR;
+ ucnv_cbFromUWriteUChars(from_unicode_args, &source, source + 1, 0, err);
+ return;
+ }
+ UrlEscapedEntityCallback(context, from_unicode_args, code_units, length,
+ code_point, reason, err);
+ return;
+ }
+ UCNV_FROM_U_CALLBACK_ESCAPE(context, from_unicode_args, code_units, length,
+ code_point, reason, err);
+}
+
+static void GbkCallbackSubstitute(const void* context,
+ UConverterFromUnicodeArgs* from_unicode_args,
+ const UChar* code_units,
+ int32_t length,
+ UChar32 code_point,
+ UConverterCallbackReason reason,
+ UErrorCode* err) {
+ UChar out_char;
+ if (reason == UCNV_UNASSIGNED && (out_char = FallbackForGBK(code_point))) {
+ const UChar* source = &out_char;
+ *err = U_ZERO_ERROR;
+ ucnv_cbFromUWriteUChars(from_unicode_args, &source, source + 1, 0, err);
+ return;
+ }
+ UCNV_FROM_U_CALLBACK_SUBSTITUTE(context, from_unicode_args, code_units,
+ length, code_point, reason, err);
+}
+#endif // USING_SYSTEM_ICU
+
+class TextCodecInput final {
+ STACK_ALLOCATED();
+
+ public:
+ TextCodecInput(const TextEncoding& encoding,
+ const UChar* characters,
+ size_t length)
+ : begin_(characters), end_(characters + length) {}
+
+ TextCodecInput(const TextEncoding& encoding,
+ const LChar* characters,
+ size_t length) {
+ buffer_.ReserveInitialCapacity(length);
+ for (size_t i = 0; i < length; ++i)
+ buffer_.push_back(characters[i]);
+ begin_ = buffer_.data();
+ end_ = begin_ + buffer_.size();
+ }
+
+ const UChar* begin() const { return begin_; }
+ const UChar* end() const { return end_; }
+
+ private:
+ const UChar* begin_;
+ const UChar* end_;
+ Vector<UChar> buffer_;
+};
+
+CString TextCodecICU::EncodeInternal(const TextCodecInput& input,
+ UnencodableHandling handling) {
+ const UChar* source = input.begin();
+ const UChar* end = input.end();
+
+ UErrorCode err = U_ZERO_ERROR;
+
+ switch (handling) {
+ case kEntitiesForUnencodables:
+#if !defined(USING_SYSTEM_ICU)
+ ucnv_setFromUCallBack(converter_icu_, NumericEntityCallback, nullptr,
+ nullptr, nullptr, &err);
+#else
+ ucnv_setFromUCallBack(
+ converter_icu_,
+ needs_gbk_fallbacks_ ? GbkCallbackEscape : NumericEntityCallback, 0,
+ 0, 0, &err);
+#endif
+ break;
+ case kURLEncodedEntitiesForUnencodables:
+#if !defined(USING_SYSTEM_ICU)
+ ucnv_setFromUCallBack(converter_icu_, UrlEscapedEntityCallback, nullptr,
+ nullptr, nullptr, &err);
+#else
+ ucnv_setFromUCallBack(converter_icu_,
+ needs_gbk_fallbacks_ ? GbkUrlEscapedEntityCallack
+ : UrlEscapedEntityCallback,
+ 0, 0, 0, &err);
+#endif
+ break;
+ case kCSSEncodedEntitiesForUnencodables:
+#if !defined(USING_SYSTEM_ICU)
+ ucnv_setFromUCallBack(converter_icu_, CssEscapedEntityCallback, nullptr,
+ nullptr, nullptr, &err);
+#else
+ ucnv_setFromUCallBack(converter_icu_,
+ needs_gbk_fallbacks_ ? GbkCssEscapedEntityCallack
+ : CssEscapedEntityCallback,
+ 0, 0, 0, &err);
+#endif
+ break;
+ }
+
+ DCHECK(U_SUCCESS(err));
+ if (U_FAILURE(err))
+ return CString();
+
+ Vector<char> result;
+ size_t size = 0;
+ do {
+ char buffer[kConversionBufferSize];
+ char* target = buffer;
+ char* target_limit = target + kConversionBufferSize;
+ err = U_ZERO_ERROR;
+ ucnv_fromUnicode(converter_icu_, &target, target_limit, &source, end,
+ nullptr, true, &err);
+ size_t count = target - buffer;
+ result.Grow(size + count);
+ memcpy(result.data() + size, buffer, count);
+ size += count;
+ } while (err == U_BUFFER_OVERFLOW_ERROR);
+
+ return CString(result.data(), size);
+}
+
+template <typename CharType>
+CString TextCodecICU::EncodeCommon(const CharType* characters,
+ size_t length,
+ UnencodableHandling handling) {
+ if (!length)
+ return "";
+
+ if (!converter_icu_)
+ CreateICUConverter();
+ if (!converter_icu_)
+ return CString();
+
+ TextCodecInput input(encoding_, characters, length);
+ return EncodeInternal(input, handling);
+}
+
+CString TextCodecICU::Encode(const UChar* characters,
+ size_t length,
+ UnencodableHandling handling) {
+ return EncodeCommon(characters, length, handling);
+}
+
+CString TextCodecICU::Encode(const LChar* characters,
+ size_t length,
+ UnencodableHandling handling) {
+ return EncodeCommon(characters, length, handling);
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_icu.h b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_icu.h
new file mode 100644
index 00000000000..64c3715750a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_icu.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2004, 2006, 2007, 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Alexey Proskuryakov <ap@nypop.com>
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_TEXT_CODEC_ICU_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_TEXT_CODEC_ICU_H_
+
+#include <unicode/utypes.h>
+#include <memory>
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_codec.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
+
+typedef struct UConverter UConverter;
+
+namespace WTF {
+
+class TextCodecInput;
+
+class TextCodecICU final : public TextCodec {
+ public:
+ static void RegisterEncodingNames(EncodingNameRegistrar);
+ static void RegisterCodecs(TextCodecRegistrar);
+
+ ~TextCodecICU() override;
+
+ private:
+ TextCodecICU(const TextEncoding&);
+ WTF_EXPORT static std::unique_ptr<TextCodec> Create(const TextEncoding&,
+ const void*);
+
+ String Decode(const char*,
+ size_t length,
+ FlushBehavior,
+ bool stop_on_error,
+ bool& saw_error) override;
+ CString Encode(const UChar*, size_t length, UnencodableHandling) override;
+ CString Encode(const LChar*, size_t length, UnencodableHandling) override;
+
+ template <typename CharType>
+ CString EncodeCommon(const CharType*, size_t length, UnencodableHandling);
+ CString EncodeInternal(const TextCodecInput&, UnencodableHandling);
+
+ void CreateICUConverter() const;
+ void ReleaseICUConverter() const;
+
+ int DecodeToBuffer(UChar* buffer,
+ UChar* buffer_limit,
+ const char*& source,
+ const char* source_limit,
+ int32_t* offsets,
+ bool flush,
+ UErrorCode&);
+
+ TextEncoding encoding_;
+ mutable UConverter* converter_icu_ = nullptr;
+#if defined(USING_SYSTEM_ICU)
+ mutable bool needs_gbk_fallbacks_ = false;
+#endif
+
+ FRIEND_TEST_ALL_PREFIXES(TextCodecICUTest, IgnorableCodePoint);
+};
+
+struct ICUConverterWrapper {
+ USING_FAST_MALLOC(ICUConverterWrapper);
+
+ public:
+ ICUConverterWrapper() : converter(nullptr) {}
+ ~ICUConverterWrapper();
+
+ UConverter* converter;
+
+ DISALLOW_COPY_AND_ASSIGN(ICUConverterWrapper);
+};
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_TEXT_CODEC_ICU_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_icu_test.cc b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_icu_test.cc
new file mode 100644
index 00000000000..1208102176a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_icu_test.cc
@@ -0,0 +1,36 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/wtf/text/text_codec_icu.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace WTF {
+
+TEST(TextCodecICUTest, IgnorableCodePoint) {
+ TextEncoding iso2022jp("iso-2022-jp");
+ std::unique_ptr<TextCodec> codec = TextCodecICU::Create(iso2022jp, nullptr);
+ Vector<UChar> source;
+ source.push_back('a');
+ source.push_back(kZeroWidthJoinerCharacter);
+ CString encoded =
+ codec->Encode(source.data(), source.size(), kEntitiesForUnencodables);
+ EXPECT_STREQ("a&#8205;", encoded.data());
+ const String source2(u"ABC~¤•★星🌟星★•¤~XYZ");
+ const CString encoded2(codec->Encode(source2.GetCharacters<UChar>(),
+ source2.length(),
+ kEntitiesForUnencodables));
+ const String source3(u"ABC~&#164;&#8226;★星&#127775;星★&#8226;&#164;~XYZ");
+ const CString encoded3(codec->Encode(source3.GetCharacters<UChar>(),
+ source3.length(),
+ kEntitiesForUnencodables));
+ EXPECT_STREQ(encoded3.data(), encoded2.data());
+ EXPECT_STREQ(
+ "ABC~&#164;&#8226;\x1B$B!z@1\x1B(B&#127775;\x1B$B@1!z\x1B(B&#8226;&#164;~"
+ "XYZ",
+ encoded2.data());
+}
+}
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_latin1.cc b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_latin1.cc
new file mode 100644
index 00000000000..966ebbcfa36
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_latin1.cc
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2004, 2006, 2008 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/wtf/text/text_codec_latin1.h"
+
+#include <memory>
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_buffer.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_codec_ascii_fast_path.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace WTF {
+
+static const UChar kTable[256] = {
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, // 00-07
+ 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, // 08-0F
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, // 10-17
+ 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, // 18-1F
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, // 20-27
+ 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, // 28-2F
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, // 30-37
+ 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, // 38-3F
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, // 40-47
+ 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, // 48-4F
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, // 50-57
+ 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, // 58-5F
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, // 60-67
+ 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, // 68-6F
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, // 70-77
+ 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, // 78-7F
+ 0x20AC, 0x0081, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, // 80-87
+ 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008D, 0x017D, 0x008F, // 88-8F
+ 0x0090, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, // 90-97
+ 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x009D, 0x017E, 0x0178, // 98-9F
+ 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, // A0-A7
+ 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, // A8-AF
+ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, // B0-B7
+ 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, // B8-BF
+ 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, // C0-C7
+ 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, // C8-CF
+ 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, // D0-D7
+ 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, // D8-DF
+ 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, // E0-E7
+ 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, // E8-EF
+ 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, // F0-F7
+ 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF // F8-FF
+};
+
+void TextCodecLatin1::RegisterEncodingNames(EncodingNameRegistrar registrar) {
+ // Taken from the alias table at https://encoding.spec.whatwg.org/
+ registrar("windows-1252", "windows-1252");
+ registrar("ANSI_X3.4-1968", "windows-1252");
+ registrar("ASCII", "windows-1252");
+ registrar("cp1252", "windows-1252");
+ registrar("cp819", "windows-1252");
+ registrar("csISOLatin1", "windows-1252");
+ registrar("IBM819", "windows-1252");
+ registrar("ISO-8859-1", "windows-1252");
+ registrar("iso-ir-100", "windows-1252");
+ registrar("iso8859-1", "windows-1252");
+ registrar("iso88591", "windows-1252");
+ registrar("iso_8859-1", "windows-1252");
+ registrar("iso_8859-1:1987", "windows-1252");
+ registrar("l1", "windows-1252");
+ registrar("latin1", "windows-1252");
+ registrar("US-ASCII", "windows-1252");
+ registrar("x-cp1252", "windows-1252");
+}
+
+static std::unique_ptr<TextCodec> NewStreamingTextDecoderWindowsLatin1(
+ const TextEncoding&,
+ const void*) {
+ return std::make_unique<TextCodecLatin1>();
+}
+
+void TextCodecLatin1::RegisterCodecs(TextCodecRegistrar registrar) {
+ registrar("windows-1252", NewStreamingTextDecoderWindowsLatin1, nullptr);
+
+ // ASCII and Latin-1 both decode as Windows Latin-1 although they retain
+ // unique identities.
+ registrar("ISO-8859-1", NewStreamingTextDecoderWindowsLatin1, nullptr);
+ registrar("US-ASCII", NewStreamingTextDecoderWindowsLatin1, nullptr);
+}
+
+String TextCodecLatin1::Decode(const char* bytes,
+ size_t length,
+ FlushBehavior,
+ bool,
+ bool&) {
+ LChar* characters;
+ if (!length)
+ return g_empty_string;
+ String result = String::CreateUninitialized(length, characters);
+
+ const uint8_t* source = reinterpret_cast<const uint8_t*>(bytes);
+ const uint8_t* end = reinterpret_cast<const uint8_t*>(bytes + length);
+ const uint8_t* aligned_end = AlignToMachineWord(end);
+ LChar* destination = characters;
+
+ while (source < end) {
+ if (IsASCII(*source)) {
+ // Fast path for ASCII. Most Latin-1 text will be ASCII.
+ if (IsAlignedToMachineWord(source)) {
+ while (source < aligned_end) {
+ MachineWord chunk = *reinterpret_cast_ptr<const MachineWord*>(source);
+
+ if (!IsAllASCII<LChar>(chunk))
+ goto useLookupTable;
+
+ CopyASCIIMachineWord(destination, source);
+ source += sizeof(MachineWord);
+ destination += sizeof(MachineWord);
+ }
+
+ if (source == end)
+ break;
+ }
+ *destination = *source;
+ } else {
+ useLookupTable:
+ if (kTable[*source] > 0xff)
+ goto upConvertTo16Bit;
+
+ *destination = static_cast<LChar>(kTable[*source]);
+ }
+
+ ++source;
+ ++destination;
+ }
+
+ return result;
+
+upConvertTo16Bit:
+ UChar* characters16;
+ String result16 = String::CreateUninitialized(length, characters16);
+
+ UChar* destination16 = characters16;
+
+ // Zero extend and copy already processed 8 bit data
+ LChar* ptr8 = characters;
+ LChar* end_ptr8 = destination;
+
+ while (ptr8 < end_ptr8)
+ *destination16++ = *ptr8++;
+
+ // Handle the character that triggered the 16 bit path
+ *destination16 = kTable[*source];
+ ++source;
+ ++destination16;
+
+ while (source < end) {
+ if (IsASCII(*source)) {
+ // Fast path for ASCII. Most Latin-1 text will be ASCII.
+ if (IsAlignedToMachineWord(source)) {
+ while (source < aligned_end) {
+ MachineWord chunk = *reinterpret_cast_ptr<const MachineWord*>(source);
+
+ if (!IsAllASCII<LChar>(chunk))
+ goto useLookupTable16;
+
+ CopyASCIIMachineWord(destination16, source);
+ source += sizeof(MachineWord);
+ destination16 += sizeof(MachineWord);
+ }
+
+ if (source == end)
+ break;
+ }
+ *destination16 = *source;
+ } else {
+ useLookupTable16:
+ *destination16 = kTable[*source];
+ }
+
+ ++source;
+ ++destination16;
+ }
+
+ return result16;
+}
+
+template <typename CharType>
+static CString EncodeComplexWindowsLatin1(const CharType* characters,
+ size_t length,
+ UnencodableHandling handling) {
+ size_t target_length = length;
+ Vector<char> result(target_length);
+ char* bytes = result.data();
+
+ size_t result_length = 0;
+ for (size_t i = 0; i < length;) {
+ UChar32 c;
+ // If CharType is LChar the U16_NEXT call reads a byte and increments;
+ // since the convention is that LChar is already latin1 this is safe.
+ U16_NEXT(characters, i, length, c);
+ // If input was a surrogate pair (non-BMP character) then we overestimated
+ // the length.
+ if (c > 0xffff)
+ --target_length;
+ unsigned char b = static_cast<unsigned char>(c);
+ // Do an efficient check to detect characters other than 00-7F and A0-FF.
+ if (b != c || (c & 0xE0) == 0x80) {
+ // Look for a way to encode this with Windows Latin-1.
+ for (b = 0x80; b < 0xA0; ++b) {
+ if (kTable[b] == c)
+ goto gotByte;
+ }
+ // No way to encode this character with Windows Latin-1.
+ UnencodableReplacementArray replacement;
+ int replacement_length =
+ TextCodec::GetUnencodableReplacement(c, handling, replacement);
+ DCHECK_GT(replacement_length, 0);
+ // Only one char was initially reserved per input character, so grow if
+ // necessary.
+ target_length += replacement_length - 1;
+ if (target_length > result.size()) {
+ result.Grow(target_length);
+ bytes = result.data();
+ }
+ memcpy(bytes + result_length, replacement, replacement_length);
+ result_length += replacement_length;
+ continue;
+ }
+ gotByte:
+ bytes[result_length++] = b;
+ }
+
+ return CString(bytes, result_length);
+}
+
+template <typename CharType>
+CString TextCodecLatin1::EncodeCommon(const CharType* characters,
+ size_t length,
+ UnencodableHandling handling) {
+ {
+ char* bytes;
+ CString string = CString::CreateUninitialized(length, bytes);
+
+ // Convert the string a fast way and simultaneously do an efficient check to
+ // see if it's all ASCII.
+ UChar ored = 0;
+ for (size_t i = 0; i < length; ++i) {
+ UChar c = characters[i];
+ bytes[i] = static_cast<char>(c);
+ ored |= c;
+ }
+
+ if (!(ored & 0xFF80))
+ return string;
+ }
+
+ // If it wasn't all ASCII, call the function that handles more-complex cases.
+ return EncodeComplexWindowsLatin1(characters, length, handling);
+}
+
+CString TextCodecLatin1::Encode(const UChar* characters,
+ size_t length,
+ UnencodableHandling handling) {
+ return EncodeCommon(characters, length, handling);
+}
+
+CString TextCodecLatin1::Encode(const LChar* characters,
+ size_t length,
+ UnencodableHandling handling) {
+ return EncodeCommon(characters, length, handling);
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_latin1.h b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_latin1.h
new file mode 100644
index 00000000000..63d30daf60f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_latin1.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2004, 2006 Apple Computer, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_TEXT_CODEC_LATIN1_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_TEXT_CODEC_LATIN1_H_
+
+#include "third_party/blink/renderer/platform/wtf/text/text_codec.h"
+
+namespace WTF {
+
+class TextCodecLatin1 final : public TextCodec {
+ public:
+ static void RegisterEncodingNames(EncodingNameRegistrar);
+ static void RegisterCodecs(TextCodecRegistrar);
+
+ private:
+ String Decode(const char*,
+ size_t length,
+ FlushBehavior,
+ bool stop_on_error,
+ bool& saw_error) override;
+ CString Encode(const UChar*, size_t length, UnencodableHandling) override;
+ CString Encode(const LChar*, size_t length, UnencodableHandling) override;
+
+ template <typename CharType>
+ CString EncodeCommon(const CharType*, size_t length, UnencodableHandling);
+};
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_TEXT_CODEC_LATIN1_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_replacement.cc b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_replacement.cc
new file mode 100644
index 00000000000..fc60e1a357c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_replacement.cc
@@ -0,0 +1,60 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/wtf/text/text_codec_replacement.h"
+
+#include <memory>
+#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace WTF {
+
+TextCodecReplacement::TextCodecReplacement()
+ : replacement_error_returned_(false) {}
+
+void TextCodecReplacement::RegisterEncodingNames(
+ EncodingNameRegistrar registrar) {
+ // Taken from the alias table at·https://encoding.spec.whatwg.org/
+ registrar("replacement", "replacement");
+ registrar("csiso2022kr", "replacement");
+ registrar("hz-gb-2312", "replacement");
+ registrar("iso-2022-cn", "replacement");
+ registrar("iso-2022-cn-ext", "replacement");
+ registrar("iso-2022-kr", "replacement");
+}
+
+static std::unique_ptr<TextCodec> NewStreamingTextDecoderReplacement(
+ const TextEncoding&,
+ const void*) {
+ return std::make_unique<TextCodecReplacement>();
+}
+
+void TextCodecReplacement::RegisterCodecs(TextCodecRegistrar registrar) {
+ registrar("replacement", NewStreamingTextDecoderReplacement, nullptr);
+}
+
+String TextCodecReplacement::Decode(const char*,
+ size_t length,
+ FlushBehavior,
+ bool,
+ bool& saw_error) {
+ // https://encoding.spec.whatwg.org/#replacement-decoder
+
+ // 1. If byte is end-of-stream, return finished.
+ if (!length)
+ return String();
+
+ // 2. If replacement error returned flag is unset, set the replacement
+ // error returned flag and return error.
+ if (!replacement_error_returned_) {
+ replacement_error_returned_ = true;
+ saw_error = true;
+ return String(&kReplacementCharacter, 1);
+ }
+
+ // 3. Return finished.
+ return String();
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_replacement.h b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_replacement.h
new file mode 100644
index 00000000000..12f8b3f8e81
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_replacement.h
@@ -0,0 +1,37 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_TEXT_CODEC_REPLACEMENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_TEXT_CODEC_REPLACEMENT_H_
+
+#include "third_party/blink/renderer/platform/wtf/text/text_codec.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_codec_utf8.h"
+
+namespace WTF {
+
+// The "replacement" encoding exists to prevent attacks that abuse a mismatch
+// between encodings supported on the server and the client. The encoder is
+// the same as UTF-8; and for a non-empty input the decoder emits U+FFFD and
+// terminates. See: https://encoding.spec.whatwg.org/#replacement and
+// https://encoding.spec.whatwg.org/#output-encodings
+class TextCodecReplacement final : public TextCodecUTF8 {
+ public:
+ TextCodecReplacement();
+
+ static void RegisterEncodingNames(EncodingNameRegistrar);
+ static void RegisterCodecs(TextCodecRegistrar);
+
+ private:
+ String Decode(const char*,
+ size_t length,
+ FlushBehavior,
+ bool stop_on_error,
+ bool& saw_error) override;
+
+ bool replacement_error_returned_;
+};
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_TEXT_CODEC_REPLACEMENT_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_replacement_test.cc b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_replacement_test.cc
new file mode 100644
index 00000000000..b5d94405b62
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_replacement_test.cc
@@ -0,0 +1,60 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/wtf/text/text_codec_replacement.h"
+
+#include <memory>
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_codec.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_encoding_registry.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace WTF {
+
+namespace {
+
+// Just one example, others are listed in the codec implementation.
+const char* g_replacement_alias = "iso-2022-kr";
+
+TEST(TextCodecReplacement, Aliases) {
+ EXPECT_TRUE(TextEncoding("replacement").IsValid());
+ EXPECT_TRUE(TextEncoding("rEpLaCeMeNt").IsValid());
+
+ EXPECT_TRUE(TextEncoding(g_replacement_alias).IsValid());
+ EXPECT_STREQ("replacement", TextEncoding(g_replacement_alias).GetName());
+}
+
+TEST(TextCodecReplacement, DecodesToFFFD) {
+ TextEncoding encoding(g_replacement_alias);
+ std::unique_ptr<TextCodec> codec(NewTextCodec(encoding));
+
+ bool saw_error = false;
+ const char kTestCase[] = "hello world";
+ size_t test_case_size = sizeof(kTestCase) - 1;
+
+ const String result =
+ codec->Decode(kTestCase, test_case_size, kDataEOF, false, saw_error);
+ EXPECT_TRUE(saw_error);
+ ASSERT_EQ(1u, result.length());
+ EXPECT_EQ(0xFFFDU, result[0]);
+}
+
+TEST(TextCodecReplacement, EncodesToUTF8) {
+ TextEncoding encoding(g_replacement_alias);
+ std::unique_ptr<TextCodec> codec(NewTextCodec(encoding));
+
+ // "Kanji" in Chinese characters.
+ const UChar kTestCase[] = {0x6F22, 0x5B57};
+ size_t test_case_size = WTF_ARRAY_LENGTH(kTestCase);
+ CString result =
+ codec->Encode(kTestCase, test_case_size, kEntitiesForUnencodables);
+
+ EXPECT_STREQ("\xE6\xBC\xA2\xE5\xAD\x97", result.data());
+}
+
+} // namespace
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_test.cc b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_test.cc
new file mode 100644
index 00000000000..2387a374037
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_test.cc
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2016 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 "third_party/blink/renderer/platform/wtf/text/text_codec.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace WTF {
+
+namespace {
+
+TEST(TextCodec, HTMLEntityEncoding) {
+ UnencodableReplacementArray replacement;
+ int size = TextCodec::GetUnencodableReplacement(
+ 0xE003, kEntitiesForUnencodables, replacement);
+ EXPECT_EQ(size, 8);
+ EXPECT_EQ(std::string(replacement), "&#57347;");
+ EXPECT_EQ(replacement[8], 0);
+}
+
+TEST(TextCodec, URLEntityEncoding) {
+ UnencodableReplacementArray replacement;
+ int size = TextCodec::GetUnencodableReplacement(
+ 0xE003, kURLEncodedEntitiesForUnencodables, replacement);
+ EXPECT_EQ(size, 14);
+ EXPECT_EQ(std::string(replacement), "%26%2357347%3B");
+ EXPECT_EQ(replacement[14], 0);
+}
+
+TEST(TextCodec, CSSEntityEncoding) {
+ UnencodableReplacementArray replacement;
+ int size = TextCodec::GetUnencodableReplacement(
+ 0xE003, kCSSEncodedEntitiesForUnencodables, replacement);
+ EXPECT_EQ(size, 6);
+ EXPECT_EQ(std::string(replacement), "\\e003 ");
+ EXPECT_EQ(replacement[6], 0);
+}
+
+} // anonymous namespace
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_user_defined.cc b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_user_defined.cc
new file mode 100644
index 00000000000..635815a83b6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_user_defined.cc
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2007, 2008 Apple, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/wtf/text/text_codec_user_defined.h"
+
+#include <memory>
+
+#include <memory>
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_buffer.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace WTF {
+
+void TextCodecUserDefined::RegisterEncodingNames(
+ EncodingNameRegistrar registrar) {
+ registrar("x-user-defined", "x-user-defined");
+}
+
+static std::unique_ptr<TextCodec> NewStreamingTextDecoderUserDefined(
+ const TextEncoding&,
+ const void*) {
+ return std::make_unique<TextCodecUserDefined>();
+}
+
+void TextCodecUserDefined::RegisterCodecs(TextCodecRegistrar registrar) {
+ registrar("x-user-defined", NewStreamingTextDecoderUserDefined, nullptr);
+}
+
+String TextCodecUserDefined::Decode(const char* bytes,
+ size_t length,
+ FlushBehavior,
+ bool,
+ bool&) {
+ StringBuilder result;
+ result.ReserveCapacity(length);
+
+ for (size_t i = 0; i < length; ++i) {
+ signed char c = bytes[i];
+ result.Append(static_cast<UChar>(c & 0xF7FF));
+ }
+
+ return result.ToString();
+}
+
+template <typename CharType>
+static CString EncodeComplexUserDefined(const CharType* characters,
+ size_t length,
+ UnencodableHandling handling) {
+ size_t target_length = length;
+ Vector<char> result(target_length);
+ char* bytes = result.data();
+
+ size_t result_length = 0;
+ for (size_t i = 0; i < length;) {
+ UChar32 c;
+ // TODO(jsbell): Will the input for x-user-defined ever be LChars?
+ U16_NEXT(characters, i, length, c);
+ // If the input was a surrogate pair (non-BMP character) then we
+ // overestimated the length.
+ if (c > 0xffff)
+ --target_length;
+ signed char signed_byte = static_cast<signed char>(c);
+ if ((signed_byte & 0xF7FF) == c) {
+ bytes[result_length++] = signed_byte;
+ } else {
+ // No way to encode this character with x-user-defined.
+ UnencodableReplacementArray replacement;
+ int replacement_length =
+ TextCodec::GetUnencodableReplacement(c, handling, replacement);
+ DCHECK_GT(replacement_length, 0);
+ // Only one char was initially reserved per input character, so grow if
+ // necessary.
+ target_length += replacement_length - 1;
+ if (target_length > result.size()) {
+ result.Grow(target_length);
+ bytes = result.data();
+ }
+ memcpy(bytes + result_length, replacement, replacement_length);
+ result_length += replacement_length;
+ }
+ }
+
+ return CString(bytes, result_length);
+}
+
+template <typename CharType>
+CString TextCodecUserDefined::EncodeCommon(const CharType* characters,
+ size_t length,
+ UnencodableHandling handling) {
+ char* bytes;
+ CString result = CString::CreateUninitialized(length, bytes);
+
+ // Convert the string a fast way and simultaneously do an efficient check to
+ // see if it's all ASCII.
+ UChar ored = 0;
+ for (size_t i = 0; i < length; ++i) {
+ UChar c = characters[i];
+ bytes[i] = static_cast<char>(c);
+ ored |= c;
+ }
+
+ if (!(ored & 0xFF80))
+ return result;
+
+ // If it wasn't all ASCII, call the function that handles more-complex cases.
+ return EncodeComplexUserDefined(characters, length, handling);
+}
+
+CString TextCodecUserDefined::Encode(const UChar* characters,
+ size_t length,
+ UnencodableHandling handling) {
+ return EncodeCommon(characters, length, handling);
+}
+
+CString TextCodecUserDefined::Encode(const LChar* characters,
+ size_t length,
+ UnencodableHandling handling) {
+ return EncodeCommon(characters, length, handling);
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_user_defined.h b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_user_defined.h
new file mode 100644
index 00000000000..a4abfdf5a9b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_user_defined.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2007 Apple, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_TEXT_CODEC_USER_DEFINED_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_TEXT_CODEC_USER_DEFINED_H_
+
+#include "third_party/blink/renderer/platform/wtf/text/text_codec.h"
+
+namespace WTF {
+
+class TextCodecUserDefined final : public TextCodec {
+ public:
+ static void RegisterEncodingNames(EncodingNameRegistrar);
+ static void RegisterCodecs(TextCodecRegistrar);
+
+ private:
+ String Decode(const char*,
+ size_t length,
+ FlushBehavior,
+ bool stop_on_error,
+ bool& saw_error) override;
+ CString Encode(const UChar*, size_t length, UnencodableHandling) override;
+ CString Encode(const LChar*, size_t length, UnencodableHandling) override;
+
+ template <typename CharType>
+ CString EncodeCommon(const CharType*, size_t length, UnencodableHandling);
+};
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_TEXT_CODEC_USER_DEFINED_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_utf16.cc b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_utf16.cc
new file mode 100644
index 00000000000..1a30e0d699d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_utf16.cc
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2004, 2006, 2008, 2010 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/wtf/text/text_codec_utf16.h"
+
+#include <memory>
+#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_buffer.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace WTF {
+
+void TextCodecUTF16::RegisterEncodingNames(EncodingNameRegistrar registrar) {
+ registrar("UTF-16LE", "UTF-16LE");
+ registrar("UTF-16BE", "UTF-16BE");
+
+ registrar("ISO-10646-UCS-2", "UTF-16LE");
+ registrar("UCS-2", "UTF-16LE");
+ registrar("UTF-16", "UTF-16LE");
+ registrar("Unicode", "UTF-16LE");
+ registrar("csUnicode", "UTF-16LE");
+ registrar("unicodeFEFF", "UTF-16LE");
+
+ registrar("unicodeFFFE", "UTF-16BE");
+}
+
+static std::unique_ptr<TextCodec> NewStreamingTextDecoderUTF16LE(
+ const TextEncoding&,
+ const void*) {
+ return std::make_unique<TextCodecUTF16>(true);
+}
+
+static std::unique_ptr<TextCodec> NewStreamingTextDecoderUTF16BE(
+ const TextEncoding&,
+ const void*) {
+ return std::make_unique<TextCodecUTF16>(false);
+}
+
+void TextCodecUTF16::RegisterCodecs(TextCodecRegistrar registrar) {
+ registrar("UTF-16LE", NewStreamingTextDecoderUTF16LE, nullptr);
+ registrar("UTF-16BE", NewStreamingTextDecoderUTF16BE, nullptr);
+}
+
+String TextCodecUTF16::Decode(const char* bytes,
+ size_t length,
+ FlushBehavior flush,
+ bool,
+ bool& saw_error) {
+ // For compatibility reasons, ignore flush from fetch EOF.
+ const bool really_flush = flush != kDoNotFlush && flush != kFetchEOF;
+
+ if (!length) {
+ if (really_flush && (have_lead_byte_ || have_lead_surrogate_)) {
+ have_lead_byte_ = have_lead_surrogate_ = false;
+ saw_error = true;
+ return String(&kReplacementCharacter, 1);
+ }
+ return String();
+ }
+
+ const unsigned char* p = reinterpret_cast<const unsigned char*>(bytes);
+ const size_t num_bytes = length + have_lead_byte_;
+ const bool will_have_extra_byte = num_bytes & 1;
+ const size_t num_chars_in = num_bytes / 2;
+ const size_t max_chars_out = num_chars_in + (have_lead_surrogate_ ? 1 : 0) +
+ (really_flush && will_have_extra_byte ? 1 : 0);
+
+ StringBuffer<UChar> buffer(max_chars_out);
+ UChar* q = buffer.Characters();
+
+ for (size_t i = 0; i < num_chars_in; ++i) {
+ UChar c;
+ if (have_lead_byte_) {
+ c = little_endian_ ? (lead_byte_ | (p[0] << 8))
+ : ((lead_byte_ << 8) | p[0]);
+ have_lead_byte_ = false;
+ ++p;
+ } else {
+ c = little_endian_ ? (p[0] | (p[1] << 8)) : ((p[0] << 8) | p[1]);
+ p += 2;
+ }
+
+ // TODO(jsbell): If necessary for performance, m_haveLeadByte handling
+ // can be pulled out and this loop split into distinct cases for
+ // big/little endian. The logic from here to the end of the loop is
+ // constant with respect to m_haveLeadByte and m_littleEndian.
+
+ if (have_lead_surrogate_ && U_IS_TRAIL(c)) {
+ *q++ = lead_surrogate_;
+ have_lead_surrogate_ = false;
+ *q++ = c;
+ } else {
+ if (have_lead_surrogate_) {
+ have_lead_surrogate_ = false;
+ saw_error = true;
+ *q++ = kReplacementCharacter;
+ }
+
+ if (U_IS_LEAD(c)) {
+ have_lead_surrogate_ = true;
+ lead_surrogate_ = c;
+ } else if (U_IS_TRAIL(c)) {
+ saw_error = true;
+ *q++ = kReplacementCharacter;
+ } else {
+ *q++ = c;
+ }
+ }
+ }
+
+ DCHECK(!have_lead_byte_);
+ if (will_have_extra_byte) {
+ have_lead_byte_ = true;
+ lead_byte_ = p[0];
+ }
+
+ if (really_flush && (have_lead_byte_ || have_lead_surrogate_)) {
+ have_lead_byte_ = have_lead_surrogate_ = false;
+ saw_error = true;
+ *q++ = kReplacementCharacter;
+ }
+
+ buffer.Shrink(q - buffer.Characters());
+
+ return String::Adopt(buffer);
+}
+
+CString TextCodecUTF16::Encode(const UChar* characters,
+ size_t length,
+ UnencodableHandling) {
+ // We need to be sure we can double the length without overflowing.
+ // Since the passed-in length is the length of an actual existing
+ // character buffer, each character is two bytes, and we know
+ // the buffer doesn't occupy the entire address space, we can
+ // assert here that doubling the length does not overflow size_t
+ // and there's no need for a runtime check.
+ DCHECK_LE(length, std::numeric_limits<size_t>::max() / 2);
+
+ char* bytes;
+ CString result = CString::CreateUninitialized(length * 2, bytes);
+
+ // FIXME: CString is not a reasonable data structure for encoded UTF-16, which
+ // will have null characters inside it. Perhaps the result of encode should
+ // not be a CString.
+ if (little_endian_) {
+ for (size_t i = 0; i < length; ++i) {
+ UChar c = characters[i];
+ bytes[i * 2] = static_cast<char>(c);
+ bytes[i * 2 + 1] = c >> 8;
+ }
+ } else {
+ for (size_t i = 0; i < length; ++i) {
+ UChar c = characters[i];
+ bytes[i * 2] = c >> 8;
+ bytes[i * 2 + 1] = static_cast<char>(c);
+ }
+ }
+
+ return result;
+}
+
+CString TextCodecUTF16::Encode(const LChar* characters,
+ size_t length,
+ UnencodableHandling) {
+ // In the LChar case, we do actually need to perform this check in release. :)
+ CHECK_LE(length, std::numeric_limits<size_t>::max() / 2);
+
+ char* bytes;
+ CString result = CString::CreateUninitialized(length * 2, bytes);
+
+ if (little_endian_) {
+ for (size_t i = 0; i < length; ++i) {
+ bytes[i * 2] = characters[i];
+ bytes[i * 2 + 1] = 0;
+ }
+ } else {
+ for (size_t i = 0; i < length; ++i) {
+ bytes[i * 2] = 0;
+ bytes[i * 2 + 1] = characters[i];
+ }
+ }
+
+ return result;
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_utf16.h b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_utf16.h
new file mode 100644
index 00000000000..5a3935a238d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_utf16.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2004, 2006 Apple Computer, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_TEXT_CODEC_UTF16_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_TEXT_CODEC_UTF16_H_
+
+#include "third_party/blink/renderer/platform/wtf/text/text_codec.h"
+
+namespace WTF {
+
+class TextCodecUTF16 final : public TextCodec {
+ public:
+ static void RegisterEncodingNames(EncodingNameRegistrar);
+ static void RegisterCodecs(TextCodecRegistrar);
+
+ TextCodecUTF16(bool little_endian) : little_endian_(little_endian) {}
+
+ String Decode(const char*,
+ size_t length,
+ FlushBehavior,
+ bool stop_on_error,
+ bool& saw_error) override;
+ CString Encode(const UChar*, size_t length, UnencodableHandling) override;
+ CString Encode(const LChar*, size_t length, UnencodableHandling) override;
+
+ private:
+ bool little_endian_;
+ bool have_lead_byte_ = false;
+ unsigned char lead_byte_;
+ bool have_lead_surrogate_ = false;
+ UChar lead_surrogate_;
+};
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_TEXT_CODEC_UTF16_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_utf8.cc b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_utf8.cc
new file mode 100644
index 00000000000..3a5a0d71b5d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_utf8.cc
@@ -0,0 +1,502 @@
+/*
+ * Copyright (C) 2004, 2006, 2008, 2011 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/wtf/text/text_codec_utf8.h"
+
+#include <memory>
+#include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_buffer.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_codec_ascii_fast_path.h"
+
+namespace WTF {
+
+// We'll use nonCharacter* constants to signal invalid utf-8.
+// The number in the name signals how many input bytes were invalid.
+const int kNonCharacter1 = -1;
+const int kNonCharacter2 = -2;
+const int kNonCharacter3 = -3;
+
+bool IsNonCharacter(int character) {
+ return character >= kNonCharacter3 && character <= kNonCharacter1;
+}
+
+std::unique_ptr<TextCodec> TextCodecUTF8::Create(const TextEncoding&,
+ const void*) {
+ return base::WrapUnique(new TextCodecUTF8());
+}
+
+void TextCodecUTF8::RegisterEncodingNames(EncodingNameRegistrar registrar) {
+ registrar("UTF-8", "UTF-8");
+
+ // Additional aliases that originally were present in the encoding
+ // table in WebKit on Macintosh, and subsequently added by
+ // TextCodecICU. Perhaps we can prove some are not used on the web
+ // and remove them.
+ registrar("unicode11utf8", "UTF-8");
+ registrar("unicode20utf8", "UTF-8");
+ registrar("utf8", "UTF-8");
+ registrar("x-unicode20utf8", "UTF-8");
+
+ // Additional aliases present in the WHATWG Encoding Standard
+ // (http://encoding.spec.whatwg.org/)
+ // and Firefox (24), but not in ICU 4.6.
+ registrar("unicode-1-1-utf-8", "UTF-8");
+}
+
+void TextCodecUTF8::RegisterCodecs(TextCodecRegistrar registrar) {
+ registrar("UTF-8", Create, nullptr);
+}
+
+static inline int NonASCIISequenceLength(uint8_t first_byte) {
+ static const uint8_t kLengths[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ return kLengths[first_byte];
+}
+
+static inline int DecodeNonASCIISequence(const uint8_t* sequence,
+ unsigned length) {
+ DCHECK(!IsASCII(sequence[0]));
+ if (length == 2) {
+ DCHECK_LE(sequence[0], 0xDF);
+ if (sequence[0] < 0xC2)
+ return kNonCharacter1;
+ if (sequence[1] < 0x80 || sequence[1] > 0xBF)
+ return kNonCharacter1;
+ return ((sequence[0] << 6) + sequence[1]) - 0x00003080;
+ }
+ if (length == 3) {
+ DCHECK_GE(sequence[0], 0xE0);
+ DCHECK_LE(sequence[0], 0xEF);
+ switch (sequence[0]) {
+ case 0xE0:
+ if (sequence[1] < 0xA0 || sequence[1] > 0xBF)
+ return kNonCharacter1;
+ break;
+ case 0xED:
+ if (sequence[1] < 0x80 || sequence[1] > 0x9F)
+ return kNonCharacter1;
+ break;
+ default:
+ if (sequence[1] < 0x80 || sequence[1] > 0xBF)
+ return kNonCharacter1;
+ }
+ if (sequence[2] < 0x80 || sequence[2] > 0xBF)
+ return kNonCharacter2;
+ return ((sequence[0] << 12) + (sequence[1] << 6) + sequence[2]) -
+ 0x000E2080;
+ }
+ DCHECK_EQ(length, 4u);
+ DCHECK_GE(sequence[0], 0xF0);
+ DCHECK_LE(sequence[0], 0xF4);
+ switch (sequence[0]) {
+ case 0xF0:
+ if (sequence[1] < 0x90 || sequence[1] > 0xBF)
+ return kNonCharacter1;
+ break;
+ case 0xF4:
+ if (sequence[1] < 0x80 || sequence[1] > 0x8F)
+ return kNonCharacter1;
+ break;
+ default:
+ if (sequence[1] < 0x80 || sequence[1] > 0xBF)
+ return kNonCharacter1;
+ }
+ if (sequence[2] < 0x80 || sequence[2] > 0xBF)
+ return kNonCharacter2;
+ if (sequence[3] < 0x80 || sequence[3] > 0xBF)
+ return kNonCharacter3;
+ return ((sequence[0] << 18) + (sequence[1] << 12) + (sequence[2] << 6) +
+ sequence[3]) -
+ 0x03C82080;
+}
+
+static inline UChar* AppendCharacter(UChar* destination, int character) {
+ DCHECK(!IsNonCharacter(character));
+ DCHECK(!U_IS_SURROGATE(character));
+ if (U_IS_BMP(character)) {
+ *destination++ = static_cast<UChar>(character);
+ } else {
+ *destination++ = U16_LEAD(character);
+ *destination++ = U16_TRAIL(character);
+ }
+ return destination;
+}
+
+void TextCodecUTF8::ConsumePartialSequenceBytes(int num_bytes) {
+ DCHECK_GE(partial_sequence_size_, num_bytes);
+ partial_sequence_size_ -= num_bytes;
+ memmove(partial_sequence_, partial_sequence_ + num_bytes,
+ partial_sequence_size_);
+}
+
+void TextCodecUTF8::HandleError(int character,
+ UChar*& destination,
+ bool stop_on_error,
+ bool& saw_error) {
+ saw_error = true;
+ if (stop_on_error)
+ return;
+ // Each error generates a replacement character and consumes 1-3 bytes.
+ *destination++ = kReplacementCharacter;
+ DCHECK(IsNonCharacter(character));
+ int num_bytes_consumed = -character;
+ DCHECK_GE(num_bytes_consumed, 1);
+ DCHECK_LE(num_bytes_consumed, 3);
+ ConsumePartialSequenceBytes(num_bytes_consumed);
+}
+
+template <>
+bool TextCodecUTF8::HandlePartialSequence<LChar>(LChar*& destination,
+ const uint8_t*& source,
+ const uint8_t* end,
+ bool flush,
+ bool,
+ bool&) {
+ DCHECK(partial_sequence_size_);
+ do {
+ if (IsASCII(partial_sequence_[0])) {
+ *destination++ = partial_sequence_[0];
+ ConsumePartialSequenceBytes(1);
+ continue;
+ }
+ int count = NonASCIISequenceLength(partial_sequence_[0]);
+ if (!count)
+ return true;
+
+ if (count > partial_sequence_size_) {
+ if (count - partial_sequence_size_ > end - source) {
+ if (!flush) {
+ // The new data is not enough to complete the sequence, so
+ // add it to the existing partial sequence.
+ memcpy(partial_sequence_ + partial_sequence_size_, source,
+ end - source);
+ partial_sequence_size_ += end - source;
+ return false;
+ }
+ // An incomplete partial sequence at the end is an error, but it will
+ // create a 16 bit string due to the replacementCharacter. Let the 16
+ // bit path handle the error.
+ return true;
+ }
+ memcpy(partial_sequence_ + partial_sequence_size_, source,
+ count - partial_sequence_size_);
+ source += count - partial_sequence_size_;
+ partial_sequence_size_ = count;
+ }
+ int character = DecodeNonASCIISequence(partial_sequence_, count);
+ if (character & ~0xff)
+ return true;
+
+ partial_sequence_size_ -= count;
+ *destination++ = static_cast<LChar>(character);
+ } while (partial_sequence_size_);
+
+ return false;
+}
+
+template <>
+bool TextCodecUTF8::HandlePartialSequence<UChar>(UChar*& destination,
+ const uint8_t*& source,
+ const uint8_t* end,
+ bool flush,
+ bool stop_on_error,
+ bool& saw_error) {
+ DCHECK(partial_sequence_size_);
+ do {
+ if (IsASCII(partial_sequence_[0])) {
+ *destination++ = partial_sequence_[0];
+ ConsumePartialSequenceBytes(1);
+ continue;
+ }
+ int count = NonASCIISequenceLength(partial_sequence_[0]);
+ if (!count) {
+ HandleError(kNonCharacter1, destination, stop_on_error, saw_error);
+ if (stop_on_error)
+ return false;
+ continue;
+ }
+ if (count > partial_sequence_size_) {
+ if (count - partial_sequence_size_ > end - source) {
+ if (!flush) {
+ // The new data is not enough to complete the sequence, so
+ // add it to the existing partial sequence.
+ memcpy(partial_sequence_ + partial_sequence_size_, source,
+ end - source);
+ partial_sequence_size_ += end - source;
+ return false;
+ }
+ // An incomplete partial sequence at the end is an error.
+ HandleError(kNonCharacter1, destination, stop_on_error, saw_error);
+ if (stop_on_error)
+ return false;
+ continue;
+ }
+ memcpy(partial_sequence_ + partial_sequence_size_, source,
+ count - partial_sequence_size_);
+ source += count - partial_sequence_size_;
+ partial_sequence_size_ = count;
+ }
+ int character = DecodeNonASCIISequence(partial_sequence_, count);
+ if (IsNonCharacter(character)) {
+ HandleError(character, destination, stop_on_error, saw_error);
+ if (stop_on_error)
+ return false;
+ continue;
+ }
+
+ partial_sequence_size_ -= count;
+ destination = AppendCharacter(destination, character);
+ } while (partial_sequence_size_);
+
+ return false;
+}
+
+String TextCodecUTF8::Decode(const char* bytes,
+ size_t length,
+ FlushBehavior flush,
+ bool stop_on_error,
+ bool& saw_error) {
+ // Each input byte might turn into a character.
+ // That includes all bytes in the partial-sequence buffer because
+ // each byte in an invalid sequence will turn into a replacement character.
+ StringBuffer<LChar> buffer(partial_sequence_size_ + length);
+
+ const uint8_t* source = reinterpret_cast<const uint8_t*>(bytes);
+ const uint8_t* end = source + length;
+ const uint8_t* aligned_end = AlignToMachineWord(end);
+ LChar* destination = buffer.Characters();
+
+ do {
+ if (partial_sequence_size_) {
+ // Explicitly copy destination and source pointers to avoid taking
+ // pointers to the local variables, which may harm code generation by
+ // disabling some optimizations in some compilers.
+ LChar* destination_for_handle_partial_sequence = destination;
+ const uint8_t* source_for_handle_partial_sequence = source;
+ if (HandlePartialSequence(destination_for_handle_partial_sequence,
+ source_for_handle_partial_sequence, end, flush,
+ stop_on_error, saw_error)) {
+ source = source_for_handle_partial_sequence;
+ goto upConvertTo16Bit;
+ }
+ destination = destination_for_handle_partial_sequence;
+ source = source_for_handle_partial_sequence;
+ if (partial_sequence_size_)
+ break;
+ }
+
+ while (source < end) {
+ if (IsASCII(*source)) {
+ // Fast path for ASCII. Most UTF-8 text will be ASCII.
+ if (IsAlignedToMachineWord(source)) {
+ while (source < aligned_end) {
+ MachineWord chunk =
+ *reinterpret_cast_ptr<const MachineWord*>(source);
+ if (!IsAllASCII<LChar>(chunk))
+ break;
+ CopyASCIIMachineWord(destination, source);
+ source += sizeof(MachineWord);
+ destination += sizeof(MachineWord);
+ }
+ if (source == end)
+ break;
+ if (!IsASCII(*source))
+ continue;
+ }
+ *destination++ = *source++;
+ continue;
+ }
+ int count = NonASCIISequenceLength(*source);
+ int character;
+ if (count == 0) {
+ character = kNonCharacter1;
+ } else {
+ if (count > end - source) {
+ SECURITY_DCHECK(end - source <
+ static_cast<ptrdiff_t>(sizeof(partial_sequence_)));
+ DCHECK(!partial_sequence_size_);
+ partial_sequence_size_ = end - source;
+ memcpy(partial_sequence_, source, partial_sequence_size_);
+ source = end;
+ break;
+ }
+ character = DecodeNonASCIISequence(source, count);
+ }
+ if (IsNonCharacter(character)) {
+ saw_error = true;
+ if (stop_on_error)
+ break;
+
+ goto upConvertTo16Bit;
+ }
+ if (character > 0xff)
+ goto upConvertTo16Bit;
+
+ source += count;
+ *destination++ = static_cast<LChar>(character);
+ }
+ } while (flush && partial_sequence_size_);
+
+ buffer.Shrink(destination - buffer.Characters());
+
+ return String::Adopt(buffer);
+
+upConvertTo16Bit:
+ StringBuffer<UChar> buffer16(partial_sequence_size_ + length);
+
+ UChar* destination16 = buffer16.Characters();
+
+ // Copy the already converted characters
+ for (LChar* converted8 = buffer.Characters(); converted8 < destination;)
+ *destination16++ = *converted8++;
+
+ do {
+ if (partial_sequence_size_) {
+ // Explicitly copy destination and source pointers to avoid taking
+ // pointers to the local variables, which may harm code generation by
+ // disabling some optimizations in some compilers.
+ UChar* destination_for_handle_partial_sequence = destination16;
+ const uint8_t* source_for_handle_partial_sequence = source;
+ HandlePartialSequence(destination_for_handle_partial_sequence,
+ source_for_handle_partial_sequence, end, flush,
+ stop_on_error, saw_error);
+ destination16 = destination_for_handle_partial_sequence;
+ source = source_for_handle_partial_sequence;
+ if (partial_sequence_size_)
+ break;
+ }
+
+ while (source < end) {
+ if (IsASCII(*source)) {
+ // Fast path for ASCII. Most UTF-8 text will be ASCII.
+ if (IsAlignedToMachineWord(source)) {
+ while (source < aligned_end) {
+ MachineWord chunk =
+ *reinterpret_cast_ptr<const MachineWord*>(source);
+ if (!IsAllASCII<LChar>(chunk))
+ break;
+ CopyASCIIMachineWord(destination16, source);
+ source += sizeof(MachineWord);
+ destination16 += sizeof(MachineWord);
+ }
+ if (source == end)
+ break;
+ if (!IsASCII(*source))
+ continue;
+ }
+ *destination16++ = *source++;
+ continue;
+ }
+ int count = NonASCIISequenceLength(*source);
+ int character;
+ if (count == 0) {
+ character = kNonCharacter1;
+ } else {
+ if (count > end - source) {
+ SECURITY_DCHECK(end - source <
+ static_cast<ptrdiff_t>(sizeof(partial_sequence_)));
+ DCHECK(!partial_sequence_size_);
+ partial_sequence_size_ = end - source;
+ memcpy(partial_sequence_, source, partial_sequence_size_);
+ source = end;
+ break;
+ }
+ character = DecodeNonASCIISequence(source, count);
+ }
+ if (IsNonCharacter(character)) {
+ saw_error = true;
+ if (stop_on_error)
+ break;
+ // Each error generates one replacement character and consumes the
+ // 'largest subpart' of the incomplete character.
+ // Note that the nonCharacterX constants go from -1..-3 and contain
+ // the negative of number of bytes comprising the broken encoding
+ // detected. So subtracting c (when isNonCharacter(c)) adds the number
+ // of broken bytes.
+ *destination16++ = kReplacementCharacter;
+ source -= character;
+ continue;
+ }
+ source += count;
+ destination16 = AppendCharacter(destination16, character);
+ }
+ } while (flush && partial_sequence_size_);
+
+ buffer16.Shrink(destination16 - buffer16.Characters());
+
+ return String::Adopt(buffer16);
+}
+
+template <typename CharType>
+CString TextCodecUTF8::EncodeCommon(const CharType* characters, size_t length) {
+ // The maximum number of UTF-8 bytes needed per UTF-16 code unit is 3.
+ // BMP characters take only one UTF-16 code unit and can take up to 3 bytes
+ // (3x).
+ // Non-BMP characters take two UTF-16 code units and can take up to 4 bytes
+ // (2x).
+ CHECK_LE(length, std::numeric_limits<size_t>::max() / 3);
+ Vector<uint8_t> bytes(length * 3);
+
+ size_t i = 0;
+ size_t bytes_written = 0;
+ while (i < length) {
+ UChar32 character;
+ U16_NEXT(characters, i, length, character);
+ // U16_NEXT will simply emit a surrogate code point if an unmatched
+ // surrogate is encountered; we must convert it to a
+ // U+FFFD (REPLACEMENT CHARACTER) here.
+ if (0xD800 <= character && character <= 0xDFFF)
+ character = kReplacementCharacter;
+ U8_APPEND_UNSAFE(bytes.data(), bytes_written, character);
+ }
+
+ return CString(reinterpret_cast<char*>(bytes.data()), bytes_written);
+}
+
+CString TextCodecUTF8::Encode(const UChar* characters,
+ size_t length,
+ UnencodableHandling) {
+ return EncodeCommon(characters, length);
+}
+
+CString TextCodecUTF8::Encode(const LChar* characters,
+ size_t length,
+ UnencodableHandling) {
+ return EncodeCommon(characters, length);
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_utf8.h b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_utf8.h
new file mode 100644
index 00000000000..9f84436bcc4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_utf8.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2011 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_TEXT_CODEC_UTF8_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_TEXT_CODEC_UTF8_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/wtf/text/text_codec.h"
+
+namespace WTF {
+
+class TextCodecUTF8 : public TextCodec {
+ public:
+ static void RegisterEncodingNames(EncodingNameRegistrar);
+ static void RegisterCodecs(TextCodecRegistrar);
+
+ protected:
+ TextCodecUTF8() : partial_sequence_size_(0) {}
+
+ private:
+ static std::unique_ptr<TextCodec> Create(const TextEncoding&, const void*);
+
+ String Decode(const char*,
+ size_t length,
+ FlushBehavior,
+ bool stop_on_error,
+ bool& saw_error) override;
+ CString Encode(const UChar*, size_t length, UnencodableHandling) override;
+ CString Encode(const LChar*, size_t length, UnencodableHandling) override;
+
+ template <typename CharType>
+ CString EncodeCommon(const CharType* characters, size_t length);
+
+ template <typename CharType>
+ bool HandlePartialSequence(CharType*& destination,
+ const uint8_t*& source,
+ const uint8_t* end,
+ bool flush,
+ bool stop_on_error,
+ bool& saw_error);
+ void HandleError(int character,
+ UChar*& destination,
+ bool stop_on_error,
+ bool& saw_error);
+ void ConsumePartialSequenceBytes(int num_bytes);
+
+ int partial_sequence_size_;
+ uint8_t partial_sequence_[U8_MAX_LENGTH];
+};
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_TEXT_CODEC_UTF8_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_utf8_test.cc b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_utf8_test.cc
new file mode 100644
index 00000000000..bbc96c5ec56
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_utf8_test.cc
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/renderer/platform/wtf/text/text_codec_utf8.h"
+
+#include <memory>
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_codec.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_encoding_registry.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace WTF {
+
+namespace {
+
+TEST(TextCodecUTF8, DecodeAscii) {
+ TextEncoding encoding("UTF-8");
+ std::unique_ptr<TextCodec> codec(NewTextCodec(encoding));
+
+ const char kTestCase[] = "HelloWorld";
+ size_t test_case_size = sizeof(kTestCase) - 1;
+
+ bool saw_error = false;
+ const String& result =
+ codec->Decode(kTestCase, test_case_size, kDataEOF, false, saw_error);
+ EXPECT_FALSE(saw_error);
+ ASSERT_EQ(test_case_size, result.length());
+ for (size_t i = 0; i < test_case_size; ++i) {
+ EXPECT_EQ(kTestCase[i], result[i]);
+ }
+}
+
+TEST(TextCodecUTF8, DecodeChineseCharacters) {
+ TextEncoding encoding("UTF-8");
+ std::unique_ptr<TextCodec> codec(NewTextCodec(encoding));
+
+ // "Kanji" in Chinese characters.
+ const char kTestCase[] = "\xe6\xbc\xa2\xe5\xad\x97";
+ size_t test_case_size = sizeof(kTestCase) - 1;
+
+ bool saw_error = false;
+ const String& result =
+ codec->Decode(kTestCase, test_case_size, kDataEOF, false, saw_error);
+ EXPECT_FALSE(saw_error);
+ ASSERT_EQ(2u, result.length());
+ EXPECT_EQ(0x6f22U, result[0]);
+ EXPECT_EQ(0x5b57U, result[1]);
+}
+
+TEST(TextCodecUTF8, Decode0xFF) {
+ TextEncoding encoding("UTF-8");
+ std::unique_ptr<TextCodec> codec(NewTextCodec(encoding));
+
+ bool saw_error = false;
+ const String& result = codec->Decode("\xff", 1, kDataEOF, false, saw_error);
+ EXPECT_TRUE(saw_error);
+ ASSERT_EQ(1u, result.length());
+ EXPECT_EQ(0xFFFDU, result[0]);
+}
+
+} // namespace
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/text_encoding.cc b/chromium/third_party/blink/renderer/platform/wtf/text/text_encoding.cc
new file mode 100644
index 00000000000..92d840d79cf
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/text_encoding.cc
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2004, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Alexey Proskuryakov <ap@nypop.com>
+ * Copyright (C) 2007-2009 Torch Mobile, Inc.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
+
+#include <memory>
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_encoding_registry.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/threading.h"
+
+namespace WTF {
+
+TextEncoding::TextEncoding(const char* name)
+ : name_(AtomicCanonicalTextEncodingName(name)) {
+}
+
+TextEncoding::TextEncoding(const String& name)
+ : name_(AtomicCanonicalTextEncodingName(name)) {
+}
+
+String TextEncoding::Decode(const char* data,
+ size_t length,
+ bool stop_on_error,
+ bool& saw_error) const {
+ if (!name_)
+ return String();
+
+ return NewTextCodec(*this)->Decode(data, length, kDataEOF, stop_on_error,
+ saw_error);
+}
+
+CString TextEncoding::Encode(const String& string,
+ UnencodableHandling handling) const {
+ if (!name_)
+ return CString();
+
+ if (string.IsEmpty())
+ return "";
+
+ std::unique_ptr<TextCodec> text_codec = NewTextCodec(*this);
+ CString encoded_string;
+ if (string.Is8Bit())
+ encoded_string =
+ text_codec->Encode(string.Characters8(), string.length(), handling);
+ else
+ encoded_string =
+ text_codec->Encode(string.Characters16(), string.length(), handling);
+ return encoded_string;
+}
+
+bool TextEncoding::UsesVisualOrdering() const {
+ if (NoExtendedTextEncodingNameUsed())
+ return false;
+
+ static const char* const kA = AtomicCanonicalTextEncodingName("ISO-8859-8");
+ return name_ == kA;
+}
+
+bool TextEncoding::IsNonByteBasedEncoding() const {
+ if (NoExtendedTextEncodingNameUsed()) {
+ return *this == UTF16LittleEndianEncoding() ||
+ *this == UTF16BigEndianEncoding();
+ }
+
+ return *this == UTF16LittleEndianEncoding() ||
+ *this == UTF16BigEndianEncoding();
+}
+
+const TextEncoding& TextEncoding::ClosestByteBasedEquivalent() const {
+ if (IsNonByteBasedEncoding())
+ return UTF8Encoding();
+ return *this;
+}
+
+// HTML5 specifies that UTF-8 be used in form submission when a form is is a
+// part of a document in UTF-16 probably because UTF-16 is not a byte-based
+// encoding and can contain 0x00.
+const TextEncoding& TextEncoding::EncodingForFormSubmission() const {
+ if (IsNonByteBasedEncoding())
+ return UTF8Encoding();
+ return *this;
+}
+
+const TextEncoding& ASCIIEncoding() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(const TextEncoding, global_ascii_encoding,
+ ("ASCII"));
+ return global_ascii_encoding;
+}
+
+const TextEncoding& Latin1Encoding() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(const TextEncoding, global_latin1_encoding,
+ ("latin1"));
+ return global_latin1_encoding;
+}
+
+const TextEncoding& UTF16BigEndianEncoding() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(
+ const TextEncoding, global_utf16_big_endian_encoding, ("UTF-16BE"));
+ return global_utf16_big_endian_encoding;
+}
+
+const TextEncoding& UTF16LittleEndianEncoding() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(
+ const TextEncoding, global_utf16_little_endian_encoding, ("UTF-16LE"));
+ return global_utf16_little_endian_encoding;
+}
+
+const TextEncoding& UTF8Encoding() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(const TextEncoding, global_utf8_encoding,
+ ("UTF-8"));
+ DCHECK(global_utf8_encoding.IsValid());
+ return global_utf8_encoding;
+}
+
+const TextEncoding& WindowsLatin1Encoding() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(
+ const TextEncoding, global_windows_latin1_encoding, ("WinLatin1"));
+ return global_windows_latin1_encoding;
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/text_encoding.h b/chromium/third_party/blink/renderer/platform/wtf/text/text_encoding.h
new file mode 100644
index 00000000000..07dc5941ce0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/text_encoding.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2004, 2006, 2007, 2008, 2009 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_TEXT_ENCODING_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_TEXT_ENCODING_H_
+
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_codec.h"
+#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_export.h"
+
+namespace WTF {
+
+class WTF_EXPORT TextEncoding final {
+ USING_FAST_MALLOC(TextEncoding);
+
+ public:
+ TextEncoding() : name_(nullptr) {}
+ explicit TextEncoding(const char* name);
+ explicit TextEncoding(const String& name);
+
+ bool IsValid() const { return name_; }
+ const char* GetName() const { return name_; }
+ bool UsesVisualOrdering() const;
+ const TextEncoding& ClosestByteBasedEquivalent() const;
+ const TextEncoding& EncodingForFormSubmission() const;
+
+ String Decode(const char* str, size_t length) const {
+ bool ignored;
+ return Decode(str, length, false, ignored);
+ }
+ String Decode(const char*,
+ size_t length,
+ bool stop_on_error,
+ bool& saw_error) const;
+
+ CString Encode(const String&, UnencodableHandling) const;
+
+ bool IsNonByteBasedEncoding() const;
+
+ private:
+ const char* name_;
+};
+
+inline bool operator==(const TextEncoding& a, const TextEncoding& b) {
+ return a.GetName() == b.GetName();
+}
+inline bool operator!=(const TextEncoding& a, const TextEncoding& b) {
+ return a.GetName() != b.GetName();
+}
+
+WTF_EXPORT const TextEncoding& ASCIIEncoding();
+WTF_EXPORT const TextEncoding& Latin1Encoding();
+WTF_EXPORT const TextEncoding& UTF16BigEndianEncoding();
+WTF_EXPORT const TextEncoding& UTF16LittleEndianEncoding();
+WTF_EXPORT const TextEncoding& UTF8Encoding();
+WTF_EXPORT const TextEncoding& WindowsLatin1Encoding();
+
+} // namespace WTF
+
+using WTF::ASCIIEncoding;
+using WTF::Latin1Encoding;
+using WTF::UTF16BigEndianEncoding;
+using WTF::UTF16LittleEndianEncoding;
+using WTF::UTF8Encoding;
+using WTF::WindowsLatin1Encoding;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_TEXT_ENCODING_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/text_encoding_registry.cc b/chromium/third_party/blink/renderer/platform/wtf/text/text_encoding_registry.cc
new file mode 100644
index 00000000000..9b69e7fa23b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/text_encoding_registry.cc
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2006, 2007, 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2007-2009 Torch Mobile, Inc.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/wtf/text/text_encoding_registry.h"
+
+#include <memory>
+#include "third_party/blink/renderer/platform/wtf/ascii_ctype.h"
+#include "third_party/blink/renderer/platform/wtf/atomics.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/string_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_codec_icu.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_codec_latin1.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_codec_replacement.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_codec_user_defined.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_codec_utf16.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_codec_utf8.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
+#include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace WTF {
+
+const size_t kMaxEncodingNameLength = 63;
+
+// Hash for all-ASCII strings that does case folding.
+struct TextEncodingNameHash {
+ static bool Equal(const char* s1, const char* s2) {
+ char c1;
+ char c2;
+ do {
+ c1 = *s1++;
+ c2 = *s2++;
+ if (ToASCIILower(c1) != ToASCIILower(c2))
+ return false;
+ } while (c1 && c2);
+ return !c1 && !c2;
+ }
+
+ // This algorithm is the one-at-a-time hash from:
+ // http://burtleburtle.net/bob/hash/hashfaq.html
+ // http://burtleburtle.net/bob/hash/doobs.html
+ static unsigned GetHash(const char* s) {
+ unsigned h = WTF::kStringHashingStartValue;
+ for (;;) {
+ char c = *s++;
+ if (!c) {
+ h += (h << 3);
+ h ^= (h >> 11);
+ h += (h << 15);
+ return h;
+ }
+ h += ToASCIILower(c);
+ h += (h << 10);
+ h ^= (h >> 6);
+ }
+ }
+
+ static const bool safe_to_compare_to_empty_or_deleted = false;
+};
+
+struct TextCodecFactory {
+ NewTextCodecFunction function;
+ const void* additional_data;
+ TextCodecFactory(NewTextCodecFunction f = nullptr, const void* d = nullptr)
+ : function(f), additional_data(d) {}
+};
+
+typedef HashMap<const char*, const char*, TextEncodingNameHash>
+ TextEncodingNameMap;
+typedef HashMap<const char*, TextCodecFactory> TextCodecMap;
+
+static Mutex& EncodingRegistryMutex() {
+ // We don't have to use AtomicallyInitializedStatic here because
+ // this function is called on the main thread for any page before
+ // it is used in worker threads.
+ DEFINE_STATIC_LOCAL(Mutex, mutex, ());
+ return mutex;
+}
+
+static TextEncodingNameMap* g_text_encoding_name_map;
+static TextCodecMap* g_text_codec_map;
+
+namespace {
+static unsigned g_did_extend_text_codec_maps = 0;
+
+ALWAYS_INLINE unsigned AtomicDidExtendTextCodecMaps() {
+ return AcquireLoad(&g_did_extend_text_codec_maps);
+}
+
+ALWAYS_INLINE void AtomicSetDidExtendTextCodecMaps() {
+ ReleaseStore(&g_did_extend_text_codec_maps, 1);
+}
+} // namespace
+
+#if ERROR_DISABLED
+
+static inline void checkExistingName(const char*, const char*) {}
+
+#else
+
+static void CheckExistingName(const char* alias, const char* atomic_name) {
+ const char* old_atomic_name = g_text_encoding_name_map->at(alias);
+ if (!old_atomic_name)
+ return;
+ if (old_atomic_name == atomic_name)
+ return;
+ // Keep the warning silent about one case where we know this will happen.
+ if (strcmp(alias, "ISO-8859-8-I") == 0 &&
+ strcmp(old_atomic_name, "ISO-8859-8-I") == 0 &&
+ strcasecmp(atomic_name, "iso-8859-8") == 0)
+ return;
+ LOG(ERROR) << "alias " << alias << " maps to " << old_atomic_name
+ << " already, but someone is trying to make it map to "
+ << atomic_name;
+}
+
+#endif
+
+static bool IsUndesiredAlias(const char* alias) {
+ // Reject aliases with version numbers that are supported by some back-ends
+ // (such as "ISO_2022,locale=ja,version=0" in ICU).
+ for (const char* p = alias; *p; ++p) {
+ if (*p == ',')
+ return true;
+ }
+ // 8859_1 is known to (at least) ICU, but other browsers don't support this
+ // name - and having it caused a compatibility
+ // problem, see bug 43554.
+ if (0 == strcmp(alias, "8859_1"))
+ return true;
+ return false;
+}
+
+static void AddToTextEncodingNameMap(const char* alias, const char* name) {
+ DCHECK_LE(strlen(alias), kMaxEncodingNameLength);
+ if (IsUndesiredAlias(alias))
+ return;
+ const char* atomic_name = g_text_encoding_name_map->at(name);
+ DCHECK(strcmp(alias, name) == 0 || atomic_name);
+ if (!atomic_name)
+ atomic_name = name;
+ CheckExistingName(alias, atomic_name);
+ g_text_encoding_name_map->insert(alias, atomic_name);
+}
+
+static void AddToTextCodecMap(const char* name,
+ NewTextCodecFunction function,
+ const void* additional_data) {
+ const char* atomic_name = g_text_encoding_name_map->at(name);
+ DCHECK(atomic_name);
+ g_text_codec_map->insert(atomic_name,
+ TextCodecFactory(function, additional_data));
+}
+
+static void BuildBaseTextCodecMaps() {
+ DCHECK(IsMainThread());
+ DCHECK(!g_text_codec_map);
+ DCHECK(!g_text_encoding_name_map);
+
+ g_text_codec_map = new TextCodecMap;
+ g_text_encoding_name_map = new TextEncodingNameMap;
+
+ TextCodecLatin1::RegisterEncodingNames(AddToTextEncodingNameMap);
+ TextCodecLatin1::RegisterCodecs(AddToTextCodecMap);
+
+ TextCodecUTF8::RegisterEncodingNames(AddToTextEncodingNameMap);
+ TextCodecUTF8::RegisterCodecs(AddToTextCodecMap);
+
+ TextCodecUTF16::RegisterEncodingNames(AddToTextEncodingNameMap);
+ TextCodecUTF16::RegisterCodecs(AddToTextCodecMap);
+
+ TextCodecUserDefined::RegisterEncodingNames(AddToTextEncodingNameMap);
+ TextCodecUserDefined::RegisterCodecs(AddToTextCodecMap);
+}
+
+static void ExtendTextCodecMaps() {
+ TextCodecReplacement::RegisterEncodingNames(AddToTextEncodingNameMap);
+ TextCodecReplacement::RegisterCodecs(AddToTextCodecMap);
+
+ TextCodecICU::RegisterEncodingNames(AddToTextEncodingNameMap);
+ TextCodecICU::RegisterCodecs(AddToTextCodecMap);
+}
+
+std::unique_ptr<TextCodec> NewTextCodec(const TextEncoding& encoding) {
+ MutexLocker lock(EncodingRegistryMutex());
+
+ DCHECK(g_text_codec_map);
+ TextCodecFactory factory = g_text_codec_map->at(encoding.GetName());
+ DCHECK(factory.function);
+ return factory.function(encoding, factory.additional_data);
+}
+
+const char* AtomicCanonicalTextEncodingName(const char* name) {
+ if (!name || !name[0])
+ return nullptr;
+ if (!g_text_encoding_name_map)
+ BuildBaseTextCodecMaps();
+
+ MutexLocker lock(EncodingRegistryMutex());
+
+ if (const char* atomic_name = g_text_encoding_name_map->at(name))
+ return atomic_name;
+ if (AtomicDidExtendTextCodecMaps())
+ return nullptr;
+ ExtendTextCodecMaps();
+ AtomicSetDidExtendTextCodecMaps();
+ return g_text_encoding_name_map->at(name);
+}
+
+template <typename CharacterType>
+const char* AtomicCanonicalTextEncodingName(const CharacterType* characters,
+ size_t length) {
+ char buffer[kMaxEncodingNameLength + 1];
+ size_t j = 0;
+ for (size_t i = 0; i < length; ++i) {
+ char c = static_cast<char>(characters[i]);
+ if (j == kMaxEncodingNameLength || c != characters[i])
+ return nullptr;
+ buffer[j++] = c;
+ }
+ buffer[j] = 0;
+ return AtomicCanonicalTextEncodingName(buffer);
+}
+
+const char* AtomicCanonicalTextEncodingName(const String& alias) {
+ if (!alias.length())
+ return nullptr;
+
+ if (alias.Contains('\0'))
+ return nullptr;
+
+ if (alias.Is8Bit())
+ return AtomicCanonicalTextEncodingName<LChar>(alias.Characters8(),
+ alias.length());
+
+ return AtomicCanonicalTextEncodingName<UChar>(alias.Characters16(),
+ alias.length());
+}
+
+bool NoExtendedTextEncodingNameUsed() {
+ return !AtomicDidExtendTextCodecMaps();
+}
+
+Vector<String> TextEncodingAliasesForTesting() {
+ Vector<String> results;
+ {
+ MutexLocker lock(EncodingRegistryMutex());
+ if (!g_text_encoding_name_map)
+ BuildBaseTextCodecMaps();
+ if (!AtomicDidExtendTextCodecMaps()) {
+ ExtendTextCodecMaps();
+ AtomicSetDidExtendTextCodecMaps();
+ }
+ CopyKeysToVector(*g_text_encoding_name_map, results);
+ }
+ return results;
+}
+
+#ifndef NDEBUG
+void DumpTextEncodingNameMap() {
+ unsigned size = g_text_encoding_name_map->size();
+ fprintf(stderr, "Dumping %u entries in WTF::TextEncodingNameMap...\n", size);
+
+ MutexLocker lock(EncodingRegistryMutex());
+
+ for (const auto& it : *g_text_encoding_name_map)
+ fprintf(stderr, "'%s' => '%s'\n", it.key, it.value);
+}
+#endif
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/text_encoding_registry.h b/chromium/third_party/blink/renderer/platform/wtf/text/text_encoding_registry.h
new file mode 100644
index 00000000000..33717cf0a21
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/text_encoding_registry.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2006, 2007 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_TEXT_ENCODING_REGISTRY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_TEXT_ENCODING_REGISTRY_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_export.h"
+
+namespace WTF {
+
+class TextCodec;
+class TextEncoding;
+
+// Use TextResourceDecoder::decode to decode resources, since it handles BOMs.
+// Use TextEncoding::encode to encode, since it takes care of normalization.
+WTF_EXPORT std::unique_ptr<TextCodec> NewTextCodec(const TextEncoding&);
+
+// Only TextEncoding should use the following functions directly.
+const char* AtomicCanonicalTextEncodingName(const char* alias);
+template <typename CharacterType>
+const char* AtomicCanonicalTextEncodingName(const CharacterType*, size_t);
+const char* AtomicCanonicalTextEncodingName(const String&);
+bool NoExtendedTextEncodingNameUsed();
+
+// Exposed for testing (via window.internals) that the set of supported
+// encodings exactly matches what is specified.
+WTF_EXPORT Vector<String> TextEncodingAliasesForTesting();
+
+#ifndef NDEBUG
+void DumpTextEncodingNameMap();
+#endif
+
+} // namespace WTF
+
+using WTF::NewTextCodec;
+using WTF::AtomicCanonicalTextEncodingName;
+using WTF::NoExtendedTextEncodingNameUsed;
+#ifndef NDEBUG
+using WTF::DumpTextEncodingNameMap;
+#endif
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_TEXT_ENCODING_REGISTRY_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/text_encoding_test.cc b/chromium/third_party/blink/renderer/platform/wtf/text/text_encoding_test.cc
new file mode 100644
index 00000000000..a689bd54672
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/text_encoding_test.cc
@@ -0,0 +1,54 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace WTF {
+
+namespace {
+
+TEST(TextEncoding, NonByteBased) {
+ EXPECT_FALSE(TextEncoding("utf-8").IsNonByteBasedEncoding());
+ EXPECT_TRUE(TextEncoding("utf-16").IsNonByteBasedEncoding());
+ EXPECT_TRUE(TextEncoding("utf-16le").IsNonByteBasedEncoding());
+ EXPECT_TRUE(TextEncoding("utf-16be").IsNonByteBasedEncoding());
+ EXPECT_FALSE(TextEncoding("windows-1252").IsNonByteBasedEncoding());
+ EXPECT_FALSE(TextEncoding("gbk").IsNonByteBasedEncoding());
+}
+
+TEST(TextEncoding, ClosestByteBased) {
+ EXPECT_STREQ("UTF-8",
+ TextEncoding("utf-8").ClosestByteBasedEquivalent().GetName());
+ EXPECT_STREQ("UTF-8",
+ TextEncoding("utf-16").ClosestByteBasedEquivalent().GetName());
+ EXPECT_STREQ("UTF-8",
+ TextEncoding("utf-16le").ClosestByteBasedEquivalent().GetName());
+ EXPECT_STREQ("UTF-8",
+ TextEncoding("utf-16be").ClosestByteBasedEquivalent().GetName());
+ EXPECT_STREQ(
+ "windows-1252",
+ TextEncoding("windows-1252").ClosestByteBasedEquivalent().GetName());
+ EXPECT_STREQ("GBK",
+ TextEncoding("gbk").ClosestByteBasedEquivalent().GetName());
+}
+
+TEST(TextEncoding, EncodingForFormSubmission) {
+ EXPECT_STREQ("UTF-8",
+ TextEncoding("utf-8").EncodingForFormSubmission().GetName());
+ EXPECT_STREQ("UTF-8",
+ TextEncoding("utf-16").EncodingForFormSubmission().GetName());
+ EXPECT_STREQ("UTF-8",
+ TextEncoding("utf-16le").EncodingForFormSubmission().GetName());
+ EXPECT_STREQ("UTF-8",
+ TextEncoding("utf-16be").EncodingForFormSubmission().GetName());
+ EXPECT_STREQ(
+ "windows-1252",
+ TextEncoding("windows-1252").EncodingForFormSubmission().GetName());
+ EXPECT_STREQ("GBK",
+ TextEncoding("gbk").EncodingForFormSubmission().GetName());
+}
+}
+}
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/text_position.cc b/chromium/third_party/blink/renderer/platform/wtf/text/text_position.cc
new file mode 100644
index 00000000000..4cb297143aa
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/text_position.cc
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2013, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/wtf/text/text_position.h"
+
+#include <algorithm>
+#include <memory>
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+
+namespace WTF {
+
+std::unique_ptr<Vector<unsigned>> GetLineEndings(const String& text) {
+ std::unique_ptr<Vector<unsigned>> result(
+ std::make_unique<Vector<unsigned>>());
+
+ unsigned start = 0;
+ while (start < text.length()) {
+ size_t line_end = text.find('\n', start);
+ if (line_end == kNotFound)
+ break;
+
+ result->push_back(static_cast<unsigned>(line_end));
+ start = line_end + 1;
+ }
+ result->push_back(text.length());
+
+ return result;
+}
+
+OrdinalNumber TextPosition::ToOffset(const Vector<unsigned>& line_endings) {
+ unsigned line_start_offset =
+ line_ != OrdinalNumber::First()
+ ? line_endings.at(line_.ZeroBasedInt() - 1) + 1
+ : 0;
+ return OrdinalNumber::FromZeroBasedInt(line_start_offset +
+ column_.ZeroBasedInt());
+}
+
+TextPosition TextPosition::FromOffsetAndLineEndings(
+ unsigned offset,
+ const Vector<unsigned>& line_endings) {
+ const unsigned* found_line_ending =
+ std::lower_bound(line_endings.begin(), line_endings.end(), offset);
+ int line_index = found_line_ending - &line_endings.at(0);
+ unsigned line_start_offset =
+ line_index > 0 ? line_endings.at(line_index - 1) + 1 : 0;
+ int column = offset - line_start_offset;
+ return TextPosition(OrdinalNumber::FromZeroBasedInt(line_index),
+ OrdinalNumber::FromZeroBasedInt(column));
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/text_position.h b/chromium/third_party/blink/renderer/platform/wtf/text/text_position.h
new file mode 100644
index 00000000000..54f1d1855ff
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/text_position.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2010, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_TEXT_POSITION_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_TEXT_POSITION_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_export.h"
+
+namespace WTF {
+
+// An abstract number of element in a sequence. The sequence has a first
+// element. This type should be used instead of integer because 2
+// contradicting traditions can call a first element '0' or '1' which makes
+// integer type ambiguous.
+class OrdinalNumber final {
+ DISALLOW_NEW();
+
+ public:
+ static OrdinalNumber FromZeroBasedInt(int zero_based_int) {
+ return OrdinalNumber(zero_based_int);
+ }
+ static OrdinalNumber FromOneBasedInt(int one_based_int) {
+ return OrdinalNumber(one_based_int - 1);
+ }
+ OrdinalNumber() : zero_based_value_(0) {}
+
+ int ZeroBasedInt() const { return zero_based_value_; }
+ int OneBasedInt() const { return zero_based_value_ + 1; }
+
+ bool operator==(OrdinalNumber other) const {
+ return zero_based_value_ == other.zero_based_value_;
+ }
+ bool operator!=(OrdinalNumber other) const { return !((*this) == other); }
+
+ static OrdinalNumber First() { return OrdinalNumber(0); }
+ static OrdinalNumber BeforeFirst() { return OrdinalNumber(-1); }
+
+ private:
+ OrdinalNumber(int zero_based_int) : zero_based_value_(zero_based_int) {}
+ int zero_based_value_;
+};
+
+// TextPosition structure specifies coordinates within an text resource. It is
+// used mostly
+// for saving script source position.
+class TextPosition final {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ TextPosition(OrdinalNumber line, OrdinalNumber column)
+ : line_(line), column_(column) {}
+ TextPosition() = default;
+ bool operator==(const TextPosition& other) const {
+ return line_ == other.line_ && column_ == other.column_;
+ }
+ bool operator!=(const TextPosition& other) const {
+ return !((*this) == other);
+ }
+ WTF_EXPORT OrdinalNumber ToOffset(const Vector<unsigned>&);
+
+ // A 'minimum' value of position, used as a default value.
+ static TextPosition MinimumPosition() {
+ return TextPosition(OrdinalNumber::First(), OrdinalNumber::First());
+ }
+
+ // A value with line value less than a minimum; used as an impossible
+ // position.
+ static TextPosition BelowRangePosition() {
+ return TextPosition(OrdinalNumber::BeforeFirst(),
+ OrdinalNumber::BeforeFirst());
+ }
+
+ // A value corresponding to a position with given offset within text having
+ // the specified line ending offsets.
+ WTF_EXPORT static TextPosition FromOffsetAndLineEndings(
+ unsigned,
+ const Vector<unsigned>&);
+
+ OrdinalNumber line_;
+ OrdinalNumber column_;
+};
+
+WTF_EXPORT std::unique_ptr<Vector<unsigned>> GetLineEndings(const String&);
+
+} // namespace WTF
+
+using WTF::OrdinalNumber;
+
+using WTF::TextPosition;
+
+using WTF::GetLineEndings;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_TEXT_POSITION_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/unicode.h b/chromium/third_party/blink/renderer/platform/wtf/text/unicode.h
new file mode 100644
index 00000000000..04a28a529a4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/unicode.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2006 George Staikos <staikos@kde.org>
+ * Copyright (C) 2006, 2008, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2007-2009 Torch Mobile, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_UNICODE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_UNICODE_H_
+
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+// Define platform neutral 8 bit character type (L is for Latin-1).
+typedef unsigned char LChar;
+
+#include "third_party/blink/renderer/platform/wtf/text/icu/unicode_icu.h"
+
+static_assert(sizeof(UChar) == 2, "UChar should be two bytes");
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_UNICODE_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/utf8.cc b/chromium/third_party/blink/renderer/platform/wtf/text/utf8.cc
new file mode 100644
index 00000000000..8794311524c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/utf8.cc
@@ -0,0 +1,476 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com>
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/wtf/text/utf8.h"
+
+#include "third_party/blink/renderer/platform/wtf/ascii_ctype.h"
+#include "third_party/blink/renderer/platform/wtf/string_hasher.h"
+#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
+
+namespace WTF {
+namespace Unicode {
+
+inline int InlineUTF8SequenceLengthNonASCII(char b0) {
+ if ((b0 & 0xC0) != 0xC0)
+ return 0;
+ if ((b0 & 0xE0) == 0xC0)
+ return 2;
+ if ((b0 & 0xF0) == 0xE0)
+ return 3;
+ if ((b0 & 0xF8) == 0xF0)
+ return 4;
+ return 0;
+}
+
+inline int InlineUTF8SequenceLength(char b0) {
+ return IsASCII(b0) ? 1 : InlineUTF8SequenceLengthNonASCII(b0);
+}
+
+// Once the bits are split out into bytes of UTF-8, this is a mask OR-ed
+// into the first byte, depending on how many bytes follow. There are
+// as many entries in this table as there are UTF-8 sequence types.
+// (I.e., one byte sequence, two byte... etc.). Remember that sequences
+// for *legal* UTF-8 will be 4 or fewer bytes total.
+static const unsigned char kFirstByteMark[7] = {0x00, 0x00, 0xC0, 0xE0,
+ 0xF0, 0xF8, 0xFC};
+
+ConversionResult ConvertLatin1ToUTF8(const LChar** source_start,
+ const LChar* source_end,
+ char** target_start,
+ char* target_end) {
+ ConversionResult result = kConversionOK;
+ const LChar* source = *source_start;
+ char* target = *target_start;
+ while (source < source_end) {
+ UChar32 ch;
+ unsigned short bytes_to_write = 0;
+ const UChar32 kByteMask = 0xBF;
+ const UChar32 kByteMark = 0x80;
+ const LChar* old_source =
+ source; // In case we have to back up because of target overflow.
+ ch = static_cast<unsigned short>(*source++);
+
+ // Figure out how many bytes the result will require
+ if (ch < (UChar32)0x80)
+ bytes_to_write = 1;
+ else
+ bytes_to_write = 2;
+
+ target += bytes_to_write;
+ if (target > target_end) {
+ source = old_source; // Back up source pointer!
+ target -= bytes_to_write;
+ result = kTargetExhausted;
+ break;
+ }
+ switch (bytes_to_write) {
+ case 2:
+ *--target = (char)((ch | kByteMark) & kByteMask);
+ ch >>= 6;
+ FALLTHROUGH;
+ case 1:
+ *--target = (char)(ch | kFirstByteMark[bytes_to_write]);
+ }
+ target += bytes_to_write;
+ }
+ *source_start = source;
+ *target_start = target;
+ return result;
+}
+
+ConversionResult ConvertUTF16ToUTF8(const UChar** source_start,
+ const UChar* source_end,
+ char** target_start,
+ char* target_end,
+ bool strict) {
+ ConversionResult result = kConversionOK;
+ const UChar* source = *source_start;
+ char* target = *target_start;
+ while (source < source_end) {
+ UChar32 ch;
+ unsigned short bytes_to_write = 0;
+ const UChar32 kByteMask = 0xBF;
+ const UChar32 kByteMark = 0x80;
+ const UChar* old_source =
+ source; // In case we have to back up because of target overflow.
+ ch = static_cast<unsigned short>(*source++);
+ // If we have a surrogate pair, convert to UChar32 first.
+ if (ch >= 0xD800 && ch <= 0xDBFF) {
+ // If the 16 bits following the high surrogate are in the source buffer...
+ if (source < source_end) {
+ UChar32 ch2 = static_cast<unsigned short>(*source);
+ // If it's a low surrogate, convert to UChar32.
+ if (ch2 >= 0xDC00 && ch2 <= 0xDFFF) {
+ ch = ((ch - 0xD800) << 10) + (ch2 - 0xDC00) + 0x0010000;
+ ++source;
+ } else if (strict) { // it's an unpaired high surrogate
+ --source; // return to the illegal value itself
+ result = kSourceIllegal;
+ break;
+ }
+ } else { // We don't have the 16 bits following the high surrogate.
+ --source; // return to the high surrogate
+ result = kSourceExhausted;
+ break;
+ }
+ } else if (strict) {
+ // UTF-16 surrogate values are illegal in UTF-32
+ if (ch >= 0xDC00 && ch <= 0xDFFF) {
+ --source; // return to the illegal value itself
+ result = kSourceIllegal;
+ break;
+ }
+ }
+ // Figure out how many bytes the result will require
+ if (ch < (UChar32)0x80) {
+ bytes_to_write = 1;
+ } else if (ch < (UChar32)0x800) {
+ bytes_to_write = 2;
+ } else if (ch < (UChar32)0x10000) {
+ bytes_to_write = 3;
+ } else if (ch < (UChar32)0x110000) {
+ bytes_to_write = 4;
+ } else {
+ bytes_to_write = 3;
+ ch = kReplacementCharacter;
+ }
+
+ target += bytes_to_write;
+ if (target > target_end) {
+ source = old_source; // Back up source pointer!
+ target -= bytes_to_write;
+ result = kTargetExhausted;
+ break;
+ }
+ switch (bytes_to_write) {
+ case 4:
+ *--target = (char)((ch | kByteMark) & kByteMask);
+ ch >>= 6;
+ FALLTHROUGH;
+ case 3:
+ *--target = (char)((ch | kByteMark) & kByteMask);
+ ch >>= 6;
+ FALLTHROUGH;
+ case 2:
+ *--target = (char)((ch | kByteMark) & kByteMask);
+ ch >>= 6;
+ FALLTHROUGH;
+ case 1:
+ *--target = (char)(ch | kFirstByteMark[bytes_to_write]);
+ }
+ target += bytes_to_write;
+ }
+ *source_start = source;
+ *target_start = target;
+ return result;
+}
+
+// This must be called with the length pre-determined by the first byte.
+// If presented with a length > 4, this returns false. The Unicode
+// definition of UTF-8 goes up to 4-byte sequences.
+static bool IsLegalUTF8(const unsigned char* source, int length) {
+ unsigned char a;
+ const unsigned char* srcptr = source + length;
+ switch (length) {
+ default:
+ return false;
+ case 4:
+ if ((a = (*--srcptr)) < 0x80 || a > 0xBF)
+ return false;
+ FALLTHROUGH;
+ case 3:
+ if ((a = (*--srcptr)) < 0x80 || a > 0xBF)
+ return false;
+ FALLTHROUGH;
+ case 2:
+ if ((a = (*--srcptr)) > 0xBF)
+ return false;
+
+ // no fall-through in this inner switch
+ switch (*source) {
+ case 0xE0:
+ if (a < 0xA0)
+ return false;
+ break;
+ case 0xED:
+ if (a > 0x9F)
+ return false;
+ break;
+ case 0xF0:
+ if (a < 0x90)
+ return false;
+ break;
+ case 0xF4:
+ if (a > 0x8F)
+ return false;
+ break;
+ default:
+ if (a < 0x80)
+ return false;
+ }
+ FALLTHROUGH;
+
+ case 1:
+ if (*source >= 0x80 && *source < 0xC2)
+ return false;
+ }
+ if (*source > 0xF4)
+ return false;
+ return true;
+}
+
+// Magic values subtracted from a buffer value during UTF8 conversion.
+// This table contains as many values as there might be trailing bytes
+// in a UTF-8 sequence.
+static const UChar32 kOffsetsFromUTF8[6] = {0x00000000UL,
+ 0x00003080UL,
+ 0x000E2080UL,
+ 0x03C82080UL,
+ static_cast<UChar32>(0xFA082080UL),
+ static_cast<UChar32>(0x82082080UL)};
+
+static inline UChar32 ReadUTF8Sequence(const char*& sequence, unsigned length) {
+ UChar32 character = 0;
+
+ switch (length) {
+ case 6:
+ character += static_cast<unsigned char>(*sequence++);
+ character <<= 6;
+ FALLTHROUGH;
+ case 5:
+ character += static_cast<unsigned char>(*sequence++);
+ character <<= 6;
+ FALLTHROUGH;
+ case 4:
+ character += static_cast<unsigned char>(*sequence++);
+ character <<= 6;
+ FALLTHROUGH;
+ case 3:
+ character += static_cast<unsigned char>(*sequence++);
+ character <<= 6;
+ FALLTHROUGH;
+ case 2:
+ character += static_cast<unsigned char>(*sequence++);
+ character <<= 6;
+ FALLTHROUGH;
+ case 1:
+ character += static_cast<unsigned char>(*sequence++);
+ }
+
+ return character - kOffsetsFromUTF8[length - 1];
+}
+
+ConversionResult ConvertUTF8ToUTF16(const char** source_start,
+ const char* source_end,
+ UChar** target_start,
+ UChar* target_end,
+ bool* source_all_ascii,
+ bool strict) {
+ ConversionResult result = kConversionOK;
+ const char* source = *source_start;
+ UChar* target = *target_start;
+ UChar or_all_data = 0;
+ while (source < source_end) {
+ int utf8_sequence_length = InlineUTF8SequenceLength(*source);
+ if (source_end - source < utf8_sequence_length) {
+ result = kSourceExhausted;
+ break;
+ }
+ // Do this check whether lenient or strict
+ if (!IsLegalUTF8(reinterpret_cast<const unsigned char*>(source),
+ utf8_sequence_length)) {
+ result = kSourceIllegal;
+ break;
+ }
+
+ UChar32 character = ReadUTF8Sequence(source, utf8_sequence_length);
+
+ if (target >= target_end) {
+ source -= utf8_sequence_length; // Back up source pointer!
+ result = kTargetExhausted;
+ break;
+ }
+
+ if (U_IS_BMP(character)) {
+ // UTF-16 surrogate values are illegal in UTF-32
+ if (U_IS_SURROGATE(character)) {
+ if (strict) {
+ source -= utf8_sequence_length; // return to the illegal value itself
+ result = kSourceIllegal;
+ break;
+ }
+ *target++ = kReplacementCharacter;
+ or_all_data |= kReplacementCharacter;
+ } else {
+ *target++ = static_cast<UChar>(character); // normal case
+ or_all_data |= character;
+ }
+ } else if (U_IS_SUPPLEMENTARY(character)) {
+ // target is a character in range 0xFFFF - 0x10FFFF
+ if (target + 1 >= target_end) {
+ source -= utf8_sequence_length; // Back up source pointer!
+ result = kTargetExhausted;
+ break;
+ }
+ *target++ = U16_LEAD(character);
+ *target++ = U16_TRAIL(character);
+ or_all_data = 0xffff;
+ } else {
+ if (strict) {
+ source -= utf8_sequence_length; // return to the start
+ result = kSourceIllegal;
+ break; // Bail out; shouldn't continue
+ } else {
+ *target++ = kReplacementCharacter;
+ or_all_data |= kReplacementCharacter;
+ }
+ }
+ }
+ *source_start = source;
+ *target_start = target;
+
+ if (source_all_ascii)
+ *source_all_ascii = !(or_all_data & ~0x7f);
+
+ return result;
+}
+
+unsigned CalculateStringHashAndLengthFromUTF8MaskingTop8Bits(
+ const char* data,
+ const char* data_end,
+ unsigned& data_length,
+ unsigned& utf16_length) {
+ if (!data)
+ return 0;
+
+ StringHasher string_hasher;
+ data_length = 0;
+ utf16_length = 0;
+
+ while (data < data_end || (!data_end && *data)) {
+ if (IsASCII(*data)) {
+ string_hasher.AddCharacter(*data++);
+ data_length++;
+ utf16_length++;
+ continue;
+ }
+
+ int utf8_sequence_length = InlineUTF8SequenceLengthNonASCII(*data);
+ data_length += utf8_sequence_length;
+
+ if (!data_end) {
+ for (int i = 1; i < utf8_sequence_length; ++i) {
+ if (!data[i])
+ return 0;
+ }
+ } else if (data_end - data < utf8_sequence_length) {
+ return 0;
+ }
+
+ if (!IsLegalUTF8(reinterpret_cast<const unsigned char*>(data),
+ utf8_sequence_length))
+ return 0;
+
+ UChar32 character = ReadUTF8Sequence(data, utf8_sequence_length);
+ DCHECK(!IsASCII(character));
+
+ if (U_IS_BMP(character)) {
+ // UTF-16 surrogate values are illegal in UTF-32
+ if (U_IS_SURROGATE(character))
+ return 0;
+ string_hasher.AddCharacter(static_cast<UChar>(character)); // normal case
+ utf16_length++;
+ } else if (U_IS_SUPPLEMENTARY(character)) {
+ string_hasher.AddCharacters(static_cast<UChar>(U16_LEAD(character)),
+ static_cast<UChar>(U16_TRAIL(character)));
+ utf16_length += 2;
+ } else {
+ return 0;
+ }
+ }
+
+ return string_hasher.HashWithTop8BitsMasked();
+}
+
+template <typename CharType>
+ALWAYS_INLINE bool EqualWithUTF8Internal(const CharType* a,
+ const CharType* a_end,
+ const char* b,
+ const char* b_end) {
+ while (b < b_end) {
+ if (IsASCII(*b)) {
+ if (*a++ != *b++)
+ return false;
+ continue;
+ }
+
+ int utf8_sequence_length = InlineUTF8SequenceLengthNonASCII(*b);
+
+ if (b_end - b < utf8_sequence_length)
+ return false;
+
+ if (!IsLegalUTF8(reinterpret_cast<const unsigned char*>(b),
+ utf8_sequence_length))
+ return 0;
+
+ UChar32 character = ReadUTF8Sequence(b, utf8_sequence_length);
+ DCHECK(!IsASCII(character));
+
+ if (U_IS_BMP(character)) {
+ // UTF-16 surrogate values are illegal in UTF-32
+ if (U_IS_SURROGATE(character))
+ return false;
+ if (*a++ != character)
+ return false;
+ } else if (U_IS_SUPPLEMENTARY(character)) {
+ if (*a++ != U16_LEAD(character))
+ return false;
+ if (*a++ != U16_TRAIL(character))
+ return false;
+ } else {
+ return false;
+ }
+ }
+
+ return a == a_end;
+}
+
+bool EqualUTF16WithUTF8(const UChar* a,
+ const UChar* a_end,
+ const char* b,
+ const char* b_end) {
+ return EqualWithUTF8Internal(a, a_end, b, b_end);
+}
+
+bool EqualLatin1WithUTF8(const LChar* a,
+ const LChar* a_end,
+ const char* b,
+ const char* b_end) {
+ return EqualWithUTF8Internal(a, a_end, b, b_end);
+}
+
+} // namespace Unicode
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/utf8.h b/chromium/third_party/blink/renderer/platform/wtf/text/utf8.h
new file mode 100644
index 00000000000..1bbde324962
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/utf8.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2007 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_UTF8_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_UTF8_H_
+
+#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_export.h"
+
+namespace WTF {
+namespace Unicode {
+
+typedef enum {
+ kConversionOK, // conversion successful
+ kSourceExhausted, // partial character in source, but hit end
+ kTargetExhausted, // insuff. room in target for conversion
+ kSourceIllegal // source sequence is illegal/malformed
+} ConversionResult;
+
+// These conversion functions take a "strict" argument. When this flag is set to
+// strict, both irregular sequences and isolated surrogates will cause an error.
+// When the flag is set to lenient, both irregular sequences and isolated
+// surrogates are converted.
+//
+// Whether the flag is strict or lenient, all illegal sequences will cause an
+// error return. This includes sequences such as: <F4 90 80 80>, <C0 80>, or
+// <A0> in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code must
+// check for illegal sequences.
+//
+// When the flag is set to lenient, characters over 0x10FFFF are converted to
+// the replacement character; otherwise (when the flag is set to strict) they
+// constitute an error.
+
+WTF_EXPORT ConversionResult
+ConvertUTF8ToUTF16(const char** source_start,
+ const char* source_end,
+ UChar** target_start,
+ UChar* target_end,
+ bool* is_source_all_ascii = nullptr,
+ bool strict = true);
+
+WTF_EXPORT ConversionResult ConvertLatin1ToUTF8(const LChar** source_start,
+ const LChar* source_end,
+ char** target_start,
+ char* target_end);
+
+WTF_EXPORT ConversionResult ConvertUTF16ToUTF8(const UChar** source_start,
+ const UChar* source_end,
+ char** target_start,
+ char* target_end,
+ bool strict = true);
+
+WTF_EXPORT unsigned CalculateStringHashAndLengthFromUTF8MaskingTop8Bits(
+ const char* data,
+ const char* data_end,
+ unsigned& data_length,
+ unsigned& utf16_length);
+
+WTF_EXPORT bool EqualUTF16WithUTF8(const UChar* a,
+ const UChar* a_end,
+ const char* b,
+ const char* b_end);
+WTF_EXPORT bool EqualLatin1WithUTF8(const LChar* a,
+ const LChar* a_end,
+ const char* b,
+ const char* b_end);
+
+} // namespace Unicode
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_UTF8_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/wtf_string.cc b/chromium/third_party/blink/renderer/platform/wtf/text/wtf_string.cc
new file mode 100644
index 00000000000..fe4c2dba491
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/wtf_string.cc
@@ -0,0 +1,803 @@
+/*
+ * (C) 1999 Lars Knoll (knoll@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2010, 2012 Apple Inc. All rights
+ * reserved.
+ * Copyright (C) 2007-2009 Torch Mobile, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+#include <stdarg.h>
+#include <algorithm>
+#include "base/strings/string_util.h"
+#include "third_party/blink/renderer/platform/wtf/ascii_ctype.h"
+#include "third_party/blink/renderer/platform/wtf/dtoa.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/text/integer_to_string_conversion.h"
+#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
+#include "third_party/blink/renderer/platform/wtf/text/utf8.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace WTF {
+
+// Construct a string with UTF-16 data.
+String::String(const UChar* characters, unsigned length)
+ : impl_(characters ? StringImpl::Create(characters, length) : nullptr) {}
+
+// Construct a string with UTF-16 data, from a null-terminated source.
+String::String(const UChar* str) {
+ if (!str)
+ return;
+ impl_ = StringImpl::Create(str, LengthOfNullTerminatedString(str));
+}
+
+// Construct a string with latin1 data.
+String::String(const LChar* characters, unsigned length)
+ : impl_(characters ? StringImpl::Create(characters, length) : nullptr) {}
+
+String::String(const char* characters, unsigned length)
+ : impl_(characters
+ ? StringImpl::Create(reinterpret_cast<const LChar*>(characters),
+ length)
+ : nullptr) {}
+
+void String::append(const StringView& string) {
+ if (string.IsEmpty())
+ return;
+ if (!impl_) {
+ impl_ = string.ToString().ReleaseImpl();
+ return;
+ }
+
+ // FIXME: This is extremely inefficient. So much so that we might want to
+ // take this out of String's API. We can make it better by optimizing the
+ // case where exactly one String is pointing at this StringImpl, but even
+ // then it's going to require a call into the allocator every single time.
+
+ if (impl_->Is8Bit() && string.Is8Bit()) {
+ LChar* data;
+ CHECK_LE(string.length(),
+ std::numeric_limits<unsigned>::max() - impl_->length());
+ scoped_refptr<StringImpl> new_impl = StringImpl::CreateUninitialized(
+ impl_->length() + string.length(), data);
+ memcpy(data, impl_->Characters8(), impl_->length() * sizeof(LChar));
+ memcpy(data + impl_->length(), string.Characters8(),
+ string.length() * sizeof(LChar));
+ impl_ = std::move(new_impl);
+ return;
+ }
+
+ UChar* data;
+ CHECK_LE(string.length(),
+ std::numeric_limits<unsigned>::max() - impl_->length());
+ scoped_refptr<StringImpl> new_impl =
+ StringImpl::CreateUninitialized(impl_->length() + string.length(), data);
+
+ if (impl_->Is8Bit())
+ StringImpl::CopyChars(data, impl_->Characters8(), impl_->length());
+ else
+ StringImpl::CopyChars(data, impl_->Characters16(), impl_->length());
+
+ if (string.Is8Bit())
+ StringImpl::CopyChars(data + impl_->length(), string.Characters8(),
+ string.length());
+ else
+ StringImpl::CopyChars(data + impl_->length(), string.Characters16(),
+ string.length());
+
+ impl_ = std::move(new_impl);
+}
+
+template <typename CharacterType>
+inline void String::AppendInternal(CharacterType c) {
+ // FIXME: This is extremely inefficient. So much so that we might want to
+ // take this out of String's API. We can make it better by optimizing the
+ // case where exactly one String is pointing at this StringImpl, but even
+ // then it's going to require a call into the allocator every single time.
+ if (!impl_) {
+ impl_ = StringImpl::Create(&c, 1);
+ return;
+ }
+
+ // FIXME: We should be able to create an 8 bit string via this code path.
+ UChar* data;
+ CHECK_LT(impl_->length(), std::numeric_limits<unsigned>::max());
+ scoped_refptr<StringImpl> new_impl =
+ StringImpl::CreateUninitialized(impl_->length() + 1, data);
+ if (impl_->Is8Bit())
+ StringImpl::CopyChars(data, impl_->Characters8(), impl_->length());
+ else
+ StringImpl::CopyChars(data, impl_->Characters16(), impl_->length());
+ data[impl_->length()] = c;
+ impl_ = std::move(new_impl);
+}
+
+void String::append(LChar c) {
+ AppendInternal(c);
+}
+
+void String::append(UChar c) {
+ AppendInternal(c);
+}
+
+int CodePointCompare(const String& a, const String& b) {
+ return CodePointCompare(a.Impl(), b.Impl());
+}
+
+int CodePointCompareIgnoringASCIICase(const String& a, const char* b) {
+ return CodePointCompareIgnoringASCIICase(a.Impl(),
+ reinterpret_cast<const LChar*>(b));
+}
+
+template <typename CharType>
+scoped_refptr<StringImpl> InsertInternal(scoped_refptr<StringImpl> impl,
+ const CharType* characters_to_insert,
+ unsigned length_to_insert,
+ unsigned position) {
+ if (!length_to_insert)
+ return impl;
+
+ DCHECK(characters_to_insert);
+ UChar* data; // FIXME: We should be able to create an 8 bit string here.
+ CHECK_LE(length_to_insert,
+ std::numeric_limits<unsigned>::max() - impl->length());
+ scoped_refptr<StringImpl> new_impl =
+ StringImpl::CreateUninitialized(impl->length() + length_to_insert, data);
+
+ if (impl->Is8Bit())
+ StringImpl::CopyChars(data, impl->Characters8(), position);
+ else
+ StringImpl::CopyChars(data, impl->Characters16(), position);
+
+ StringImpl::CopyChars(data + position, characters_to_insert,
+ length_to_insert);
+
+ if (impl->Is8Bit())
+ StringImpl::CopyChars(data + position + length_to_insert,
+ impl->Characters8() + position,
+ impl->length() - position);
+ else
+ StringImpl::CopyChars(data + position + length_to_insert,
+ impl->Characters16() + position,
+ impl->length() - position);
+
+ return new_impl;
+}
+
+void String::insert(const StringView& string, unsigned position) {
+ if (string.IsEmpty()) {
+ if (string.IsNull())
+ return;
+ if (IsNull())
+ impl_ = string.ToString().ReleaseImpl();
+ return;
+ }
+
+ if (position >= length()) {
+ if (string.Is8Bit())
+ append(string);
+ else
+ append(string);
+ return;
+ }
+
+ DCHECK(impl_);
+ if (string.Is8Bit())
+ impl_ = InsertInternal(std::move(impl_), string.Characters8(),
+ string.length(), position);
+ else
+ impl_ = InsertInternal(std::move(impl_), string.Characters16(),
+ string.length(), position);
+}
+
+UChar32 String::CharacterStartingAt(unsigned i) const {
+ if (!impl_ || i >= impl_->length())
+ return 0;
+ return impl_->CharacterStartingAt(i);
+}
+
+void String::Ensure16Bit() {
+ if (IsNull())
+ return;
+ if (!Is8Bit())
+ return;
+ if (unsigned length = this->length())
+ impl_ = Make16BitFrom8BitSource(impl_->Characters8(), length).ReleaseImpl();
+ else
+ impl_ = StringImpl::empty16_bit_;
+}
+
+void String::Truncate(unsigned length) {
+ if (impl_)
+ impl_ = impl_->Truncate(length);
+}
+
+void String::Remove(unsigned start, unsigned length_to_remove) {
+ if (impl_)
+ impl_ = impl_->Remove(start, length_to_remove);
+}
+
+String String::Substring(unsigned pos, unsigned len) const {
+ if (!impl_)
+ return String();
+ return impl_->Substring(pos, len);
+}
+
+String String::DeprecatedLower() const {
+ if (!impl_)
+ return String();
+ return impl_->LowerUnicode();
+}
+
+String String::LowerUnicode(const AtomicString& locale_identifier) const {
+ if (!impl_)
+ return String();
+ return impl_->LowerUnicode(locale_identifier);
+}
+
+String String::UpperUnicode(const AtomicString& locale_identifier) const {
+ if (!impl_)
+ return String();
+ return impl_->UpperUnicode(locale_identifier);
+}
+
+String String::LowerASCII() const {
+ if (!impl_)
+ return String();
+ return impl_->LowerASCII();
+}
+
+String String::UpperASCII() const {
+ if (!impl_)
+ return String();
+ return impl_->UpperASCII();
+}
+
+String String::StripWhiteSpace() const {
+ if (!impl_)
+ return String();
+ return impl_->StripWhiteSpace();
+}
+
+String String::StripWhiteSpace(IsWhiteSpaceFunctionPtr is_white_space) const {
+ if (!impl_)
+ return String();
+ return impl_->StripWhiteSpace(is_white_space);
+}
+
+String String::SimplifyWhiteSpace(StripBehavior strip_behavior) const {
+ if (!impl_)
+ return String();
+ return impl_->SimplifyWhiteSpace(strip_behavior);
+}
+
+String String::SimplifyWhiteSpace(IsWhiteSpaceFunctionPtr is_white_space,
+ StripBehavior strip_behavior) const {
+ if (!impl_)
+ return String();
+ return impl_->SimplifyWhiteSpace(is_white_space, strip_behavior);
+}
+
+String String::RemoveCharacters(CharacterMatchFunctionPtr find_match) const {
+ if (!impl_)
+ return String();
+ return impl_->RemoveCharacters(find_match);
+}
+
+String String::FoldCase() const {
+ if (!impl_)
+ return String();
+ return impl_->FoldCase();
+}
+
+String String::Format(const char* format, ...) {
+ va_list args;
+
+ // TODO(esprehn): base uses 1024, maybe we should use a bigger size too.
+ static const unsigned kDefaultSize = 256;
+ Vector<char, kDefaultSize> buffer(kDefaultSize);
+
+ va_start(args, format);
+ int length = base::vsnprintf(buffer.data(), buffer.size(), format, args);
+ va_end(args);
+
+ // TODO(esprehn): This can only happen if there's an encoding error, what's
+ // the locale set to inside blink? Can this happen? We should probably CHECK
+ // instead.
+ if (length < 0)
+ return String();
+
+ if (static_cast<unsigned>(length) >= buffer.size()) {
+ // vsnprintf doesn't include the NUL terminator in the length so we need to
+ // add space for it when growing.
+ buffer.Grow(length + 1);
+
+ // We need to call va_end() and then va_start() each time we use args, as
+ // the contents of args is undefined after the call to vsnprintf according
+ // to http://man.cx/snprintf(3)
+ //
+ // Not calling va_end/va_start here happens to work on lots of systems, but
+ // fails e.g. on 64bit Linux.
+ va_start(args, format);
+ length = base::vsnprintf(buffer.data(), buffer.size(), format, args);
+ va_end(args);
+ }
+
+ CHECK_LT(static_cast<unsigned>(length), buffer.size());
+ return String(reinterpret_cast<const LChar*>(buffer.data()), length);
+}
+
+String String::EncodeForDebugging() const {
+ if (IsNull())
+ return "<null>";
+
+ String str;
+ str.append('"');
+ for (unsigned index = 0; index < length(); ++index) {
+ // Print shorthands for select cases.
+ UChar character = (*impl_)[index];
+ switch (character) {
+ case '\t':
+ str.append("\\t");
+ break;
+ case '\n':
+ str.append("\\n");
+ break;
+ case '\r':
+ str.append("\\r");
+ break;
+ case '"':
+ str.append("\\\"");
+ break;
+ case '\\':
+ str.append("\\\\");
+ break;
+ default:
+ if (IsASCIIPrintable(character)) {
+ str.append(static_cast<char>(character));
+ } else {
+ // Print "\uXXXX" for control or non-ASCII characters.
+ str.append("\\u");
+ std::stringstream out;
+ out.width(4);
+ out.fill('0');
+ out.setf(std::ios_base::hex, std::ios_base::basefield);
+ out.setf(std::ios::uppercase);
+ out << character;
+ str.append(out.str().c_str());
+ }
+ break;
+ }
+ }
+ str.append('"');
+ return str;
+}
+
+template <typename IntegerType>
+static String IntegerToString(IntegerType input) {
+ IntegerToStringConverter<IntegerType> converter(input);
+ return StringImpl::Create(converter.Characters8(), converter.length());
+}
+
+String String::Number(int number) {
+ return IntegerToString(number);
+}
+
+String String::Number(unsigned number) {
+ return IntegerToString(number);
+}
+
+String String::Number(long number) {
+ return IntegerToString(number);
+}
+
+String String::Number(unsigned long number) {
+ return IntegerToString(number);
+}
+
+String String::Number(long long number) {
+ return IntegerToString(number);
+}
+
+String String::Number(unsigned long long number) {
+ return IntegerToString(number);
+}
+
+String String::Number(double number, unsigned precision) {
+ NumberToStringBuffer buffer;
+ return String(NumberToFixedPrecisionString(number, precision, buffer));
+}
+
+String String::NumberToStringECMAScript(double number) {
+ NumberToStringBuffer buffer;
+ return String(NumberToString(number, buffer));
+}
+
+String String::NumberToStringFixedWidth(double number,
+ unsigned decimal_places) {
+ NumberToStringBuffer buffer;
+ return String(NumberToFixedWidthString(number, decimal_places, buffer));
+}
+
+int String::ToIntStrict(bool* ok) const {
+ if (!impl_) {
+ if (ok)
+ *ok = false;
+ return 0;
+ }
+ return impl_->ToInt(NumberParsingOptions::kStrict, ok);
+}
+
+unsigned String::ToUIntStrict(bool* ok) const {
+ if (!impl_) {
+ if (ok)
+ *ok = false;
+ return 0;
+ }
+ return impl_->ToUInt(NumberParsingOptions::kStrict, ok);
+}
+
+unsigned String::HexToUIntStrict(bool* ok) const {
+ if (!impl_) {
+ if (ok)
+ *ok = false;
+ return 0;
+ }
+ return impl_->HexToUIntStrict(ok);
+}
+
+int64_t String::ToInt64Strict(bool* ok) const {
+ if (!impl_) {
+ if (ok)
+ *ok = false;
+ return 0;
+ }
+ return impl_->ToInt64(NumberParsingOptions::kStrict, ok);
+}
+
+uint64_t String::ToUInt64Strict(bool* ok) const {
+ if (!impl_) {
+ if (ok)
+ *ok = false;
+ return 0;
+ }
+ return impl_->ToUInt64(NumberParsingOptions::kStrict, ok);
+}
+
+int String::ToInt(bool* ok) const {
+ if (!impl_) {
+ if (ok)
+ *ok = false;
+ return 0;
+ }
+ return impl_->ToInt(NumberParsingOptions::kLoose, ok);
+}
+
+unsigned String::ToUInt(bool* ok) const {
+ if (!impl_) {
+ if (ok)
+ *ok = false;
+ return 0;
+ }
+ return impl_->ToUInt(NumberParsingOptions::kLoose, ok);
+}
+
+double String::ToDouble(bool* ok) const {
+ if (!impl_) {
+ if (ok)
+ *ok = false;
+ return 0.0;
+ }
+ return impl_->ToDouble(ok);
+}
+
+float String::ToFloat(bool* ok) const {
+ if (!impl_) {
+ if (ok)
+ *ok = false;
+ return 0.0f;
+ }
+ return impl_->ToFloat(ok);
+}
+
+String String::IsolatedCopy() const {
+ if (!impl_)
+ return String();
+ return impl_->IsolatedCopy();
+}
+
+bool String::IsSafeToSendToAnotherThread() const {
+ return !impl_ || impl_->IsSafeToSendToAnotherThread();
+}
+
+void String::Split(const StringView& separator,
+ bool allow_empty_entries,
+ Vector<String>& result) const {
+ result.clear();
+
+ unsigned start_pos = 0;
+ size_t end_pos;
+ while ((end_pos = Find(separator, start_pos)) != kNotFound) {
+ if (allow_empty_entries || start_pos != end_pos)
+ result.push_back(Substring(start_pos, end_pos - start_pos));
+ start_pos = end_pos + separator.length();
+ }
+ if (allow_empty_entries || start_pos != length())
+ result.push_back(Substring(start_pos));
+}
+
+void String::Split(UChar separator,
+ bool allow_empty_entries,
+ Vector<String>& result) const {
+ result.clear();
+
+ unsigned start_pos = 0;
+ size_t end_pos;
+ while ((end_pos = find(separator, start_pos)) != kNotFound) {
+ if (allow_empty_entries || start_pos != end_pos)
+ result.push_back(Substring(start_pos, end_pos - start_pos));
+ start_pos = end_pos + 1;
+ }
+ if (allow_empty_entries || start_pos != length())
+ result.push_back(Substring(start_pos));
+}
+
+CString String::Ascii() const {
+ // Printable ASCII characters 32..127 and the null character are
+ // preserved, characters outside of this range are converted to '?'.
+
+ unsigned length = this->length();
+ if (!length) {
+ char* character_buffer;
+ return CString::CreateUninitialized(length, character_buffer);
+ }
+
+ if (this->Is8Bit()) {
+ const LChar* characters = this->Characters8();
+
+ char* character_buffer;
+ CString result = CString::CreateUninitialized(length, character_buffer);
+
+ for (unsigned i = 0; i < length; ++i) {
+ LChar ch = characters[i];
+ character_buffer[i] = ch && (ch < 0x20 || ch > 0x7f) ? '?' : ch;
+ }
+
+ return result;
+ }
+
+ const UChar* characters = this->Characters16();
+
+ char* character_buffer;
+ CString result = CString::CreateUninitialized(length, character_buffer);
+
+ for (unsigned i = 0; i < length; ++i) {
+ UChar ch = characters[i];
+ character_buffer[i] =
+ ch && (ch < 0x20 || ch > 0x7f) ? '?' : static_cast<char>(ch);
+ }
+
+ return result;
+}
+
+CString String::Latin1() const {
+ // Basic Latin1 (ISO) encoding - Unicode characters 0..255 are
+ // preserved, characters outside of this range are converted to '?'.
+
+ unsigned length = this->length();
+
+ if (!length)
+ return CString("", 0);
+
+ if (Is8Bit())
+ return CString(reinterpret_cast<const char*>(this->Characters8()), length);
+
+ const UChar* characters = this->Characters16();
+
+ char* character_buffer;
+ CString result = CString::CreateUninitialized(length, character_buffer);
+
+ for (unsigned i = 0; i < length; ++i) {
+ UChar ch = characters[i];
+ character_buffer[i] = ch > 0xff ? '?' : static_cast<char>(ch);
+ }
+
+ return result;
+}
+
+// Helper to write a three-byte UTF-8 code point to the buffer, caller must
+// check room is available.
+static inline void PutUTF8Triple(char*& buffer, UChar ch) {
+ DCHECK_GE(ch, 0x0800);
+ *buffer++ = static_cast<char>(((ch >> 12) & 0x0F) | 0xE0);
+ *buffer++ = static_cast<char>(((ch >> 6) & 0x3F) | 0x80);
+ *buffer++ = static_cast<char>((ch & 0x3F) | 0x80);
+}
+
+CString String::Utf8(UTF8ConversionMode mode) const {
+ unsigned length = this->length();
+
+ if (!length)
+ return CString("", 0);
+
+ // Allocate a buffer big enough to hold all the characters
+ // (an individual UTF-16 UChar can only expand to 3 UTF-8 bytes).
+ // Optimization ideas, if we find this function is hot:
+ // * We could speculatively create a CStringImpl to contain 'length'
+ // characters, and resize if necessary (i.e. if the buffer contains
+ // non-ascii characters). (Alternatively, scan the buffer first for
+ // ascii characters, so we know this will be sufficient).
+ // * We could allocate a CStringImpl with an appropriate size to
+ // have a good chance of being able to write the string into the
+ // buffer without reallocing (say, 1.5 x length).
+ if (length > std::numeric_limits<unsigned>::max() / 3)
+ return CString();
+ Vector<char, 1024> buffer_vector(length * 3);
+
+ char* buffer = buffer_vector.data();
+
+ if (Is8Bit()) {
+ const LChar* characters = this->Characters8();
+
+ Unicode::ConversionResult result =
+ Unicode::ConvertLatin1ToUTF8(&characters, characters + length, &buffer,
+ buffer + buffer_vector.size());
+ // (length * 3) should be sufficient for any conversion
+ DCHECK_NE(result, Unicode::kTargetExhausted);
+ } else {
+ const UChar* characters = this->Characters16();
+
+ if (mode == kStrictUTF8ConversionReplacingUnpairedSurrogatesWithFFFD) {
+ const UChar* characters_end = characters + length;
+ char* buffer_end = buffer + buffer_vector.size();
+ while (characters < characters_end) {
+ // Use strict conversion to detect unpaired surrogates.
+ Unicode::ConversionResult result = Unicode::ConvertUTF16ToUTF8(
+ &characters, characters_end, &buffer, buffer_end, true);
+ DCHECK_NE(result, Unicode::kTargetExhausted);
+ // Conversion fails when there is an unpaired surrogate. Put
+ // replacement character (U+FFFD) instead of the unpaired
+ // surrogate.
+ if (result != Unicode::kConversionOK) {
+ DCHECK_LE(0xD800, *characters);
+ DCHECK_LE(*characters, 0xDFFF);
+ // There should be room left, since one UChar hasn't been
+ // converted.
+ DCHECK_LE(buffer + 3, buffer_end);
+ PutUTF8Triple(buffer, kReplacementCharacter);
+ ++characters;
+ }
+ }
+ } else {
+ bool strict = mode == kStrictUTF8Conversion;
+ Unicode::ConversionResult result =
+ Unicode::ConvertUTF16ToUTF8(&characters, characters + length, &buffer,
+ buffer + buffer_vector.size(), strict);
+ // (length * 3) should be sufficient for any conversion
+ DCHECK_NE(result, Unicode::kTargetExhausted);
+
+ // Only produced from strict conversion.
+ if (result == Unicode::kSourceIllegal) {
+ DCHECK(strict);
+ return CString();
+ }
+
+ // Check for an unconverted high surrogate.
+ if (result == Unicode::kSourceExhausted) {
+ if (strict)
+ return CString();
+ // This should be one unpaired high surrogate. Treat it the same
+ // was as an unpaired high surrogate would have been handled in
+ // the middle of a string with non-strict conversion - which is
+ // to say, simply encode it to UTF-8.
+ DCHECK_EQ(characters + 1, this->Characters16() + length);
+ DCHECK_GE(*characters, 0xD800);
+ DCHECK_LE(*characters, 0xDBFF);
+ // There should be room left, since one UChar hasn't been
+ // converted.
+ DCHECK_LE(buffer + 3, buffer + buffer_vector.size());
+ PutUTF8Triple(buffer, *characters);
+ }
+ }
+ }
+
+ return CString(buffer_vector.data(), buffer - buffer_vector.data());
+}
+
+String String::Make8BitFrom16BitSource(const UChar* source, size_t length) {
+ if (!length)
+ return g_empty_string;
+
+ LChar* destination;
+ String result = String::CreateUninitialized(length, destination);
+
+ CopyLCharsFromUCharSource(destination, source, length);
+
+ return result;
+}
+
+String String::Make16BitFrom8BitSource(const LChar* source, size_t length) {
+ if (!length)
+ return g_empty_string16_bit;
+
+ UChar* destination;
+ String result = String::CreateUninitialized(length, destination);
+
+ StringImpl::CopyChars(destination, source, length);
+
+ return result;
+}
+
+String String::FromUTF8(const LChar* string_start, size_t length) {
+ CHECK_LE(length, std::numeric_limits<unsigned>::max());
+
+ if (!string_start)
+ return String();
+
+ if (!length)
+ return g_empty_string;
+
+ if (CharactersAreAllASCII(string_start, length))
+ return StringImpl::Create(string_start, length);
+
+ Vector<UChar, 1024> buffer(length);
+ UChar* buffer_start = buffer.data();
+
+ UChar* buffer_current = buffer_start;
+ const char* string_current = reinterpret_cast<const char*>(string_start);
+ if (Unicode::ConvertUTF8ToUTF16(
+ &string_current, reinterpret_cast<const char*>(string_start + length),
+ &buffer_current,
+ buffer_current + buffer.size()) != Unicode::kConversionOK)
+ return String();
+
+ unsigned utf16_length = buffer_current - buffer_start;
+ DCHECK_LT(utf16_length, length);
+ return StringImpl::Create(buffer_start, utf16_length);
+}
+
+String String::FromUTF8(const LChar* string) {
+ if (!string)
+ return String();
+ return FromUTF8(string, strlen(reinterpret_cast<const char*>(string)));
+}
+
+String String::FromUTF8(const CString& s) {
+ return FromUTF8(s.data());
+}
+
+String String::FromUTF8WithLatin1Fallback(const LChar* string, size_t size) {
+ String utf8 = FromUTF8(string, size);
+ if (!utf8)
+ return String(string, size);
+ return utf8;
+}
+
+std::ostream& operator<<(std::ostream& out, const String& string) {
+ return out << string.EncodeForDebugging().Utf8().data();
+}
+
+#ifndef NDEBUG
+void String::Show() const {
+ DLOG(INFO) << *this;
+}
+#endif
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/wtf_string.h b/chromium/third_party/blink/renderer/platform/wtf/text/wtf_string.h
new file mode 100644
index 00000000000..361322374f6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/wtf_string.h
@@ -0,0 +1,675 @@
+/*
+ * (C) 1999 Lars Knoll (knoll@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012, 2013 Apple Inc.
+ * All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_WTF_STRING_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_WTF_STRING_H_
+
+// This file would be called String.h, but that conflicts with <string.h>
+// on systems without case-sensitive file systems.
+
+#include <iosfwd>
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/compiler.h"
+#include "third_party/blink/renderer/platform/wtf/hash_table_deleted_value_type.h"
+#include "third_party/blink/renderer/platform/wtf/text/ascii_fast_path.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_impl.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_view.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_export.h"
+
+#ifdef __OBJC__
+#include <objc/objc.h>
+#endif
+
+namespace WTF {
+
+class CString;
+struct StringHash;
+
+enum UTF8ConversionMode {
+ kLenientUTF8Conversion,
+ kStrictUTF8Conversion,
+ kStrictUTF8ConversionReplacingUnpairedSurrogatesWithFFFD
+};
+
+#define DISPATCH_CASE_OP(caseSensitivity, op, args) \
+ ((caseSensitivity == kTextCaseSensitive) \
+ ? op args \
+ : (caseSensitivity == kTextCaseASCIIInsensitive) \
+ ? op##IgnoringASCIICase args \
+ : op##IgnoringCase args)
+
+// You can find documentation about this class in this doc:
+// https://docs.google.com/document/d/1kOCUlJdh2WJMJGDf-WoEQhmnjKLaOYRbiHz5TiGJl14/edit?usp=sharing
+class WTF_EXPORT String {
+ USING_FAST_MALLOC(String);
+
+ public:
+ // Construct a null string, distinguishable from an empty string.
+ String() = default;
+
+ // Construct a string with UTF-16 data.
+ String(const UChar* characters, unsigned length);
+
+ // Construct a string by copying the contents of a vector.
+ // This method will never create a null string. Vectors with size() == 0
+ // will return the empty string.
+ // NOTE: This is different from String(vector.data(), vector.size())
+ // which will sometimes return a null string when vector.data() is null
+ // which can only occur for vectors without inline capacity.
+ // See: https://bugs.webkit.org/show_bug.cgi?id=109792
+ template <size_t inlineCapacity>
+ explicit String(const Vector<UChar, inlineCapacity>&);
+
+ // Construct a string with UTF-16 data, from a null-terminated source.
+ String(const UChar*);
+ String(const char16_t* chars)
+ : String(reinterpret_cast<const UChar*>(chars)) {}
+
+ // Construct a string with latin1 data.
+ String(const LChar* characters, unsigned length);
+ String(const char* characters, unsigned length);
+
+ // Construct a string with latin1 data, from a null-terminated source.
+ String(const LChar* characters)
+ : String(reinterpret_cast<const char*>(characters)) {}
+ String(const char* characters)
+ : String(characters, characters ? strlen(characters) : 0) {}
+
+ // Construct a string referencing an existing StringImpl.
+ String(StringImpl* impl) : impl_(impl) {}
+ String(scoped_refptr<StringImpl> impl) : impl_(std::move(impl)) {}
+
+ void swap(String& o) { impl_.swap(o.impl_); }
+
+ template <typename CharType>
+ static String Adopt(StringBuffer<CharType>& buffer) {
+ if (!buffer.length())
+ return StringImpl::empty_;
+ return String(buffer.Release());
+ }
+
+ explicit operator bool() const { return !IsNull(); }
+ bool IsNull() const { return !impl_; }
+ bool IsEmpty() const { return !impl_ || !impl_->length(); }
+
+ StringImpl* Impl() const { return impl_.get(); }
+ scoped_refptr<StringImpl> ReleaseImpl() { return std::move(impl_); }
+
+ unsigned length() const {
+ if (!impl_)
+ return 0;
+ return impl_->length();
+ }
+
+ const LChar* Characters8() const {
+ if (!impl_)
+ return nullptr;
+ DCHECK(impl_->Is8Bit());
+ return impl_->Characters8();
+ }
+
+ const UChar* Characters16() const {
+ if (!impl_)
+ return nullptr;
+ DCHECK(!impl_->Is8Bit());
+ return impl_->Characters16();
+ }
+
+ ALWAYS_INLINE const void* Bytes() const {
+ if (!impl_)
+ return nullptr;
+ return impl_->Bytes();
+ }
+
+ // Return characters8() or characters16() depending on CharacterType.
+ template <typename CharacterType>
+ inline const CharacterType* GetCharacters() const;
+
+ bool Is8Bit() const { return impl_->Is8Bit(); }
+
+ CString Ascii() const;
+ CString Latin1() const;
+ CString Utf8(UTF8ConversionMode = kLenientUTF8Conversion) const;
+
+ UChar operator[](unsigned index) const {
+ if (!impl_ || index >= impl_->length())
+ return 0;
+ return (*impl_)[index];
+ }
+
+ static String Number(int);
+ static String Number(unsigned);
+ static String Number(long);
+ static String Number(unsigned long);
+ static String Number(long long);
+ static String Number(unsigned long long);
+
+ static String Number(double, unsigned precision = 6);
+
+ // Number to String conversion following the ECMAScript definition.
+ static String NumberToStringECMAScript(double);
+ static String NumberToStringFixedWidth(double, unsigned decimal_places);
+
+ // Find characters.
+ size_t find(UChar c, unsigned start = 0) const {
+ return impl_ ? impl_->Find(c, start) : kNotFound;
+ }
+ size_t find(LChar c, unsigned start = 0) const {
+ return impl_ ? impl_->Find(c, start) : kNotFound;
+ }
+ size_t find(char c, unsigned start = 0) const {
+ return find(static_cast<LChar>(c), start);
+ }
+ size_t Find(CharacterMatchFunctionPtr match_function,
+ unsigned start = 0) const {
+ return impl_ ? impl_->Find(match_function, start) : kNotFound;
+ }
+
+ // Find substrings.
+ size_t Find(const StringView& value,
+ unsigned start = 0,
+ TextCaseSensitivity case_sensitivity = kTextCaseSensitive) const {
+ return impl_
+ ? DISPATCH_CASE_OP(case_sensitivity, impl_->Find, (value, start))
+ : kNotFound;
+ }
+
+ // Unicode aware case insensitive string matching. Non-ASCII characters might
+ // match to ASCII characters. This function is rarely used to implement web
+ // platform features.
+ size_t FindIgnoringCase(const StringView& value, unsigned start = 0) const {
+ return impl_ ? impl_->FindIgnoringCase(value, start) : kNotFound;
+ }
+
+ // ASCII case insensitive string matching.
+ size_t FindIgnoringASCIICase(const StringView& value,
+ unsigned start = 0) const {
+ return impl_ ? impl_->FindIgnoringASCIICase(value, start) : kNotFound;
+ }
+
+ bool Contains(char c) const { return find(c) != kNotFound; }
+ bool Contains(
+ const StringView& value,
+ TextCaseSensitivity case_sensitivity = kTextCaseSensitive) const {
+ return Find(value, 0, case_sensitivity) != kNotFound;
+ }
+
+ // Find the last instance of a single character or string.
+ size_t ReverseFind(UChar c, unsigned start = UINT_MAX) const {
+ return impl_ ? impl_->ReverseFind(c, start) : kNotFound;
+ }
+ size_t ReverseFind(const StringView& value, unsigned start = UINT_MAX) const {
+ return impl_ ? impl_->ReverseFind(value, start) : kNotFound;
+ }
+
+ UChar32 CharacterStartingAt(unsigned) const;
+
+ bool StartsWith(
+ const StringView& prefix,
+ TextCaseSensitivity case_sensitivity = kTextCaseSensitive) const {
+ return impl_
+ ? DISPATCH_CASE_OP(case_sensitivity, impl_->StartsWith, (prefix))
+ : prefix.IsEmpty();
+ }
+ bool StartsWithIgnoringCase(const StringView& prefix) const {
+ return impl_ ? impl_->StartsWithIgnoringCase(prefix) : prefix.IsEmpty();
+ }
+ bool StartsWithIgnoringASCIICase(const StringView& prefix) const {
+ return impl_ ? impl_->StartsWithIgnoringASCIICase(prefix)
+ : prefix.IsEmpty();
+ }
+ bool StartsWith(UChar character) const {
+ return impl_ ? impl_->StartsWith(character) : false;
+ }
+
+ bool EndsWith(
+ const StringView& suffix,
+ TextCaseSensitivity case_sensitivity = kTextCaseSensitive) const {
+ return impl_ ? DISPATCH_CASE_OP(case_sensitivity, impl_->EndsWith, (suffix))
+ : suffix.IsEmpty();
+ }
+ bool EndsWithIgnoringCase(const StringView& prefix) const {
+ return impl_ ? impl_->EndsWithIgnoringCase(prefix) : prefix.IsEmpty();
+ }
+ bool EndsWithIgnoringASCIICase(const StringView& prefix) const {
+ return impl_ ? impl_->EndsWithIgnoringASCIICase(prefix) : prefix.IsEmpty();
+ }
+ bool EndsWith(UChar character) const {
+ return impl_ ? impl_->EndsWith(character) : false;
+ }
+
+ void append(const StringView&);
+ void append(LChar);
+ void append(char c) { append(static_cast<LChar>(c)); }
+ void append(UChar);
+ void insert(const StringView&, unsigned pos);
+
+ // TODO(esprehn): replace strangely both modifies this String *and* return a
+ // value. It should only do one of those.
+ String& Replace(UChar pattern, UChar replacement) {
+ if (impl_)
+ impl_ = impl_->Replace(pattern, replacement);
+ return *this;
+ }
+ String& Replace(UChar pattern, const StringView& replacement) {
+ if (impl_)
+ impl_ = impl_->Replace(pattern, replacement);
+ return *this;
+ }
+ String& Replace(const StringView& pattern, const StringView& replacement) {
+ if (impl_)
+ impl_ = impl_->Replace(pattern, replacement);
+ return *this;
+ }
+ String& replace(unsigned index,
+ unsigned length_to_replace,
+ const StringView& replacement) {
+ if (impl_)
+ impl_ = impl_->Replace(index, length_to_replace, replacement);
+ return *this;
+ }
+
+ void Fill(UChar c) {
+ if (impl_)
+ impl_ = impl_->Fill(c);
+ }
+
+ void Ensure16Bit();
+
+ void Truncate(unsigned length);
+ void Remove(unsigned start, unsigned length = 1);
+
+ String Substring(unsigned pos, unsigned len = UINT_MAX) const;
+ String Left(unsigned len) const { return Substring(0, len); }
+ String Right(unsigned len) const { return Substring(length() - len, len); }
+
+ // Returns a lowercase version of the string. This function might convert
+ // non-ASCII characters to ASCII characters. For example, DeprecatedLower()
+ // for U+212A is 'k'.
+ // This function is rarely used to implement web platform features. See
+ // crbug.com/627682.
+ // This function is deprecated. We should use LowerASCII() or introduce
+ // LowerUnicode().
+ String DeprecatedLower() const;
+
+ // |locale_identifier| is case-insensitive, and accepts either of "aa_aa" or
+ // "aa-aa". Empty/null |locale_identifier| indicates locale-independent
+ // Unicode case conversion.
+ String LowerUnicode(const AtomicString& locale_identifier) const;
+ String UpperUnicode(const AtomicString& locale_identifier) const;
+
+ // Returns a lowercase version of the string.
+ // This function converts ASCII characters only.
+ String LowerASCII() const;
+ // Returns a uppercase version of the string.
+ // This function converts ASCII characters only.
+ String UpperASCII() const;
+
+ String StripWhiteSpace() const;
+ String StripWhiteSpace(IsWhiteSpaceFunctionPtr) const;
+ String SimplifyWhiteSpace(StripBehavior = kStripExtraWhiteSpace) const;
+ String SimplifyWhiteSpace(IsWhiteSpaceFunctionPtr,
+ StripBehavior = kStripExtraWhiteSpace) const;
+
+ String RemoveCharacters(CharacterMatchFunctionPtr) const;
+ template <bool isSpecialCharacter(UChar)>
+ bool IsAllSpecialCharacters() const;
+
+ // Return the string with case folded for case insensitive comparison.
+ String FoldCase() const;
+
+ // Takes a printf format and args and prints into a String.
+ // This function supports Latin-1 characters only.
+ PRINTF_FORMAT(1, 2) static String Format(const char* format, ...);
+
+ // Returns a version suitable for gtest and base/logging.*. It prepends and
+ // appends double-quotes, and escapes chracters other than ASCII printables.
+ String EncodeForDebugging() const;
+
+ // Returns an uninitialized string. The characters needs to be written
+ // into the buffer returned in data before the returned string is used.
+ // Failure to do this will have unpredictable results.
+ static String CreateUninitialized(unsigned length, UChar*& data) {
+ return StringImpl::CreateUninitialized(length, data);
+ }
+ static String CreateUninitialized(unsigned length, LChar*& data) {
+ return StringImpl::CreateUninitialized(length, data);
+ }
+
+ void Split(const StringView& separator,
+ bool allow_empty_entries,
+ Vector<String>& result) const;
+ void Split(const StringView& separator, Vector<String>& result) const {
+ Split(separator, false, result);
+ }
+ void Split(UChar separator,
+ bool allow_empty_entries,
+ Vector<String>& result) const;
+ void Split(UChar separator, Vector<String>& result) const {
+ Split(separator, false, result);
+ }
+
+ // Copy characters out of the string. See StringImpl.h for detailed docs.
+ unsigned CopyTo(UChar* buffer, unsigned start, unsigned max_length) const {
+ return impl_ ? impl_->CopyTo(buffer, start, max_length) : 0;
+ }
+ template <typename BufferType>
+ void AppendTo(BufferType&,
+ unsigned start = 0,
+ unsigned length = UINT_MAX) const;
+ template <typename BufferType>
+ void PrependTo(BufferType&,
+ unsigned start = 0,
+ unsigned length = UINT_MAX) const;
+
+ // Convert the string into a number.
+
+ // The following ToFooStrict functions accept:
+ // - leading '+'
+ // - leading Unicode whitespace
+ // - trailing Unicode whitespace
+ // - no "-0" (ToUIntStrict and ToUInt64Strict)
+ // - no out-of-range numbers which the resultant type can't represent
+ //
+ // If the input string is not acceptable, 0 is returned and |*ok| becomes
+ // |false|.
+ //
+ // We can use these functions to implement a Web Platform feature only if the
+ // input string is already valid according to the specification of the
+ // feature.
+ int ToIntStrict(bool* ok = nullptr) const;
+ unsigned ToUIntStrict(bool* ok = nullptr) const;
+ unsigned HexToUIntStrict(bool* ok) const;
+ int64_t ToInt64Strict(bool* ok = nullptr) const;
+ uint64_t ToUInt64Strict(bool* ok = nullptr) const;
+
+ // The following ToFoo functions accept:
+ // - leading '+'
+ // - leading Unicode whitespace
+ // - trailing garbage
+ // - no "-0" (ToUInt and ToUInt64)
+ // - no out-of-range numbers which the resultant type can't represent
+ //
+ // If the input string is not acceptable, 0 is returned and |*ok| becomes
+ // |false|.
+ //
+ // We can use these functions to implement a Web Platform feature only if the
+ // input string is already valid according to the specification of the
+ // feature.
+ int ToInt(bool* ok = nullptr) const;
+ unsigned ToUInt(bool* ok = nullptr) const;
+
+ // These functions accepts:
+ // - leading '+'
+ // - numbers without leading zeros such as ".5"
+ // - numbers ending with "." such as "3."
+ // - scientific notation
+ // - leading whitespace (IsASCIISpace, not IsHTMLSpace)
+ // - no trailing whitespace
+ // - no trailing garbage
+ // - no numbers such as "NaN" "Infinity"
+ //
+ // A huge absolute number which a double/float can't represent is accepted,
+ // and +Infinity or -Infinity is returned.
+ //
+ // A small absolute numbers which a double/float can't represent is accepted,
+ // and 0 is returned
+ //
+ // If the input string is not acceptable, 0.0 is returned and |*ok| becomes
+ // |false|.
+ //
+ // We can use these functions to implement a Web Platform feature only if the
+ // input string is already valid according to the specification of the
+ // feature.
+ //
+ // FIXME: Like the strict functions above, these give false for "ok" when
+ // there is trailing garbage. Like the non-strict functions above, these
+ // return the value when there is trailing garbage. It would be better if
+ // these were more consistent with the above functions instead.
+ double ToDouble(bool* ok = nullptr) const;
+ float ToFloat(bool* ok = nullptr) const;
+
+ String IsolatedCopy() const;
+ bool IsSafeToSendToAnotherThread() const;
+
+#ifdef __OBJC__
+ String(NSString*);
+
+ // This conversion maps null string to "", which loses the meaning of null
+ // string, but we need this mapping because AppKit crashes when passed nil
+ // NSStrings.
+ operator NSString*() const {
+ if (!impl_)
+ return @"";
+ return *impl_;
+ }
+#endif
+
+ static String Make8BitFrom16BitSource(const UChar*, size_t);
+ template <size_t inlineCapacity>
+ static String Make8BitFrom16BitSource(
+ const Vector<UChar, inlineCapacity>& buffer) {
+ return Make8BitFrom16BitSource(buffer.data(), buffer.size());
+ }
+
+ static String Make16BitFrom8BitSource(const LChar*, size_t);
+
+ // String::fromUTF8 will return a null string if
+ // the input data contains invalid UTF-8 sequences.
+ static String FromUTF8(const LChar*, size_t);
+ static String FromUTF8(const LChar*);
+ static String FromUTF8(const char* s, size_t length) {
+ return FromUTF8(reinterpret_cast<const LChar*>(s), length);
+ }
+ static String FromUTF8(const char* s) {
+ return FromUTF8(reinterpret_cast<const LChar*>(s));
+ }
+ static String FromUTF8(const CString&);
+
+ // Tries to convert the passed in string to UTF-8, but will fall back to
+ // Latin-1 if the string is not valid UTF-8.
+ static String FromUTF8WithLatin1Fallback(const LChar*, size_t);
+ static String FromUTF8WithLatin1Fallback(const char* s, size_t length) {
+ return FromUTF8WithLatin1Fallback(reinterpret_cast<const LChar*>(s),
+ length);
+ }
+
+ bool ContainsOnlyASCII() const {
+ return !impl_ || impl_->ContainsOnlyASCII();
+ }
+ bool ContainsOnlyLatin1() const;
+ bool ContainsOnlyWhitespace() const {
+ return !impl_ || impl_->ContainsOnlyWhitespace();
+ }
+
+ size_t CharactersSizeInBytes() const {
+ return impl_ ? impl_->CharactersSizeInBytes() : 0;
+ }
+
+#ifndef NDEBUG
+ // For use in the debugger.
+ void Show() const;
+#endif
+
+ private:
+ friend struct HashTraits<String>;
+
+ template <typename CharacterType>
+ void AppendInternal(CharacterType);
+
+ scoped_refptr<StringImpl> impl_;
+};
+
+#undef DISPATCH_CASE_OP
+
+inline bool operator==(const String& a, const String& b) {
+ // We don't use equalStringView here since we want the isAtomic() fast path
+ // inside WTF::equal.
+ return Equal(a.Impl(), b.Impl());
+}
+inline bool operator==(const String& a, const char* b) {
+ return EqualStringView(a, b);
+}
+inline bool operator==(const char* a, const String& b) {
+ return b == a;
+}
+
+inline bool operator!=(const String& a, const String& b) {
+ return !(a == b);
+}
+inline bool operator!=(const String& a, const char* b) {
+ return !(a == b);
+}
+inline bool operator!=(const char* a, const String& b) {
+ return !(a == b);
+}
+
+inline bool EqualIgnoringNullity(const String& a, const String& b) {
+ return EqualIgnoringNullity(a.Impl(), b.Impl());
+}
+
+template <size_t inlineCapacity>
+inline bool EqualIgnoringNullity(const Vector<UChar, inlineCapacity>& a,
+ const String& b) {
+ return EqualIgnoringNullity(a, b.Impl());
+}
+
+inline void swap(String& a, String& b) {
+ a.swap(b);
+}
+
+// Definitions of string operations
+
+template <size_t inlineCapacity>
+String::String(const Vector<UChar, inlineCapacity>& vector)
+ : impl_(vector.size() ? StringImpl::Create(vector.data(), vector.size())
+ : StringImpl::empty_) {}
+
+template <>
+inline const LChar* String::GetCharacters<LChar>() const {
+ DCHECK(Is8Bit());
+ return Characters8();
+}
+
+template <>
+inline const UChar* String::GetCharacters<UChar>() const {
+ DCHECK(!Is8Bit());
+ return Characters16();
+}
+
+inline bool String::ContainsOnlyLatin1() const {
+ if (IsEmpty())
+ return true;
+
+ if (Is8Bit())
+ return true;
+
+ const UChar* characters = Characters16();
+ UChar ored = 0;
+ for (size_t i = 0; i < impl_->length(); ++i)
+ ored |= characters[i];
+ return !(ored & 0xFF00);
+}
+
+#ifdef __OBJC__
+// This is for situations in WebKit where the long standing behavior has been
+// "nil if empty", so we try to maintain longstanding behavior for the sake of
+// entrenched clients
+inline NSString* NsStringNilIfEmpty(const String& str) {
+ return str.IsEmpty() ? nil : (NSString*)str;
+}
+#endif
+
+WTF_EXPORT int CodePointCompare(const String&, const String&);
+
+inline bool CodePointCompareLessThan(const String& a, const String& b) {
+ return CodePointCompare(a.Impl(), b.Impl()) < 0;
+}
+
+WTF_EXPORT int CodePointCompareIgnoringASCIICase(const String&, const char*);
+
+template <bool isSpecialCharacter(UChar)>
+inline bool String::IsAllSpecialCharacters() const {
+ return StringView(*this).IsAllSpecialCharacters<isSpecialCharacter>();
+}
+
+template <typename BufferType>
+void String::AppendTo(BufferType& result,
+ unsigned position,
+ unsigned length) const {
+ if (!impl_)
+ return;
+ impl_->AppendTo(result, position, length);
+}
+
+template <typename BufferType>
+void String::PrependTo(BufferType& result,
+ unsigned position,
+ unsigned length) const {
+ if (!impl_)
+ return;
+ impl_->PrependTo(result, position, length);
+}
+
+// StringHash is the default hash for String
+template <typename T>
+struct DefaultHash;
+template <>
+struct DefaultHash<String> {
+ typedef StringHash Hash;
+};
+
+// Shared global empty string.
+WTF_EXPORT extern const String& g_empty_string;
+WTF_EXPORT extern const String& g_empty_string16_bit;
+WTF_EXPORT extern const String& g_xmlns_with_colon;
+
+// Pretty printer for gtest and base/logging.*. It prepends and appends
+// double-quotes, and escapes chracters other than ASCII printables.
+WTF_EXPORT std::ostream& operator<<(std::ostream&, const String&);
+
+inline StringView::StringView(const String& string,
+ unsigned offset,
+ unsigned length)
+ : StringView(string.Impl(), offset, length) {}
+inline StringView::StringView(const String& string, unsigned offset)
+ : StringView(string.Impl(), offset) {}
+inline StringView::StringView(const String& string)
+ : StringView(string.Impl()) {}
+
+} // namespace WTF
+
+WTF_ALLOW_MOVE_AND_INIT_WITH_MEM_FUNCTIONS(String);
+
+using WTF::CString;
+using WTF::kStrictUTF8Conversion;
+using WTF::kStrictUTF8ConversionReplacingUnpairedSurrogatesWithFFFD;
+using WTF::String;
+using WTF::g_empty_string;
+using WTF::g_empty_string16_bit;
+using WTF::CharactersAreAllASCII;
+using WTF::Equal;
+using WTF::Find;
+using WTF::IsSpaceOrNewline;
+
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_WTF_STRING_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/wtf_string_test.cc b/chromium/third_party/blink/renderer/platform/wtf/text/wtf_string_test.cc
new file mode 100644
index 00000000000..85ea71d6991
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/wtf_string_test.cc
@@ -0,0 +1,500 @@
+/*
+ * Copyright (C) 2012 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+#include <limits>
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+
+namespace WTF {
+
+TEST(StringTest, CreationFromLiteral) {
+ String string_from_literal("Explicit construction syntax");
+ EXPECT_EQ(strlen("Explicit construction syntax"),
+ string_from_literal.length());
+ EXPECT_TRUE(string_from_literal == "Explicit construction syntax");
+ EXPECT_TRUE(string_from_literal.Is8Bit());
+ EXPECT_TRUE(String("Explicit construction syntax") == string_from_literal);
+}
+
+TEST(StringTest, ASCII) {
+ CString output;
+
+ // Null String.
+ output = String().Ascii();
+ EXPECT_STREQ("", output.data());
+
+ // Empty String.
+ output = g_empty_string.Ascii();
+ EXPECT_STREQ("", output.data());
+
+ // Regular String.
+ output = String("foobar").Ascii();
+ EXPECT_STREQ("foobar", output.data());
+}
+
+namespace {
+
+void TestNumberToStringECMAScript(double number, const char* reference) {
+ CString number_string = String::NumberToStringECMAScript(number).Latin1();
+ EXPECT_STREQ(reference, number_string.data());
+}
+
+} // anonymous namespace
+
+TEST(StringTest, NumberToStringECMAScriptBoundaries) {
+ typedef std::numeric_limits<double> Limits;
+
+ // Infinity.
+ TestNumberToStringECMAScript(Limits::infinity(), "Infinity");
+ TestNumberToStringECMAScript(-Limits::infinity(), "-Infinity");
+
+ // NaN.
+ TestNumberToStringECMAScript(-Limits::quiet_NaN(), "NaN");
+
+ // Zeros.
+ TestNumberToStringECMAScript(0, "0");
+ TestNumberToStringECMAScript(-0, "0");
+
+ // Min-Max.
+ TestNumberToStringECMAScript(Limits::min(), "2.2250738585072014e-308");
+ TestNumberToStringECMAScript(Limits::max(), "1.7976931348623157e+308");
+}
+
+TEST(StringTest, NumberToStringECMAScriptRegularNumbers) {
+ // Pi.
+ TestNumberToStringECMAScript(piDouble, "3.141592653589793");
+ TestNumberToStringECMAScript(piFloat, "3.1415927410125732");
+ TestNumberToStringECMAScript(piOverTwoDouble, "1.5707963267948966");
+ TestNumberToStringECMAScript(piOverTwoFloat, "1.5707963705062866");
+ TestNumberToStringECMAScript(piOverFourDouble, "0.7853981633974483");
+ TestNumberToStringECMAScript(piOverFourFloat, "0.7853981852531433");
+
+ // e.
+ const double kE = 2.71828182845904523536028747135266249775724709369995;
+ TestNumberToStringECMAScript(kE, "2.718281828459045");
+
+ // c, speed of light in m/s.
+ const double kC = 299792458;
+ TestNumberToStringECMAScript(kC, "299792458");
+
+ // Golen ratio.
+ const double kPhi = 1.6180339887498948482;
+ TestNumberToStringECMAScript(kPhi, "1.618033988749895");
+}
+
+TEST(StringTest, ReplaceWithLiteral) {
+ // Cases for 8Bit source.
+ String test_string = "1224";
+ EXPECT_TRUE(test_string.Is8Bit());
+ test_string.Replace('2', "");
+ EXPECT_STREQ("14", test_string.Utf8().data());
+
+ test_string = "1224";
+ EXPECT_TRUE(test_string.Is8Bit());
+ test_string.Replace('2', "3");
+ EXPECT_STREQ("1334", test_string.Utf8().data());
+
+ test_string = "1224";
+ EXPECT_TRUE(test_string.Is8Bit());
+ test_string.Replace('2', "555");
+ EXPECT_STREQ("15555554", test_string.Utf8().data());
+
+ test_string = "1224";
+ EXPECT_TRUE(test_string.Is8Bit());
+ test_string.Replace('3', "NotFound");
+ EXPECT_STREQ("1224", test_string.Utf8().data());
+
+ // Cases for 16Bit source.
+ // U+00E9 (=0xC3 0xA9 in UTF-8) is e with accent.
+ test_string = String::FromUTF8("r\xC3\xA9sum\xC3\xA9");
+ EXPECT_FALSE(test_string.Is8Bit());
+ test_string.Replace(UChar(0x00E9), "e");
+ EXPECT_STREQ("resume", test_string.Utf8().data());
+
+ test_string = String::FromUTF8("r\xC3\xA9sum\xC3\xA9");
+ EXPECT_FALSE(test_string.Is8Bit());
+ test_string.Replace(UChar(0x00E9), "");
+ EXPECT_STREQ("rsum", test_string.Utf8().data());
+
+ test_string = String::FromUTF8("r\xC3\xA9sum\xC3\xA9");
+ EXPECT_FALSE(test_string.Is8Bit());
+ test_string.Replace('3', "NotFound");
+ EXPECT_STREQ("r\xC3\xA9sum\xC3\xA9", test_string.Utf8().data());
+}
+
+TEST(StringTest, ComparisonOfSameStringVectors) {
+ Vector<String> string_vector;
+ string_vector.push_back("one");
+ string_vector.push_back("two");
+
+ Vector<String> same_string_vector;
+ same_string_vector.push_back("one");
+ same_string_vector.push_back("two");
+
+ EXPECT_EQ(string_vector, same_string_vector);
+}
+
+TEST(WTF, SimplifyWhiteSpace) {
+ String extra_spaces(" Hello world ");
+ EXPECT_EQ(String("Hello world"), extra_spaces.SimplifyWhiteSpace());
+ EXPECT_EQ(String(" Hello world "),
+ extra_spaces.SimplifyWhiteSpace(WTF::kDoNotStripWhiteSpace));
+
+ String extra_spaces_and_newlines(" \nHello\n world\n ");
+ EXPECT_EQ(String("Hello world"),
+ extra_spaces_and_newlines.SimplifyWhiteSpace());
+ EXPECT_EQ(
+ String(" Hello world "),
+ extra_spaces_and_newlines.SimplifyWhiteSpace(WTF::kDoNotStripWhiteSpace));
+
+ String extra_spaces_and_tabs(" \nHello\t world\t ");
+ EXPECT_EQ(String("Hello world"), extra_spaces_and_tabs.SimplifyWhiteSpace());
+ EXPECT_EQ(
+ String(" Hello world "),
+ extra_spaces_and_tabs.SimplifyWhiteSpace(WTF::kDoNotStripWhiteSpace));
+}
+
+struct CaseFoldingTestData {
+ const char* source_description;
+ const char* source;
+ const char** locale_list;
+ size_t locale_list_length;
+ const char* expected;
+};
+
+// \xC4\xB0 = U+0130 (capital dotted I)
+// \xC4\xB1 = U+0131 (lowercase dotless I)
+const char* g_turkic_input = "Isi\xC4\xB0 \xC4\xB0s\xC4\xB1I";
+const char* g_greek_input =
+ "\xCE\x9F\xCE\x94\xCE\x8C\xCE\xA3 \xCE\x9F\xCE\xB4\xCF\x8C\xCF\x82 "
+ "\xCE\xA3\xCE\xBF \xCE\xA3\xCE\x9F o\xCE\xA3 \xCE\x9F\xCE\xA3 \xCF\x83 "
+ "\xE1\xBC\x95\xCE\xBE";
+const char* g_lithuanian_input =
+ "I \xC3\x8F J J\xCC\x88 \xC4\xAE \xC4\xAE\xCC\x88 \xC3\x8C \xC3\x8D "
+ "\xC4\xA8 xi\xCC\x87\xCC\x88 xj\xCC\x87\xCC\x88 x\xC4\xAF\xCC\x87\xCC\x88 "
+ "xi\xCC\x87\xCC\x80 xi\xCC\x87\xCC\x81 xi\xCC\x87\xCC\x83 XI X\xC3\x8F XJ "
+ "XJ\xCC\x88 X\xC4\xAE X\xC4\xAE\xCC\x88";
+
+const char* g_turkic_locales[] = {
+ "tr", "tr-TR", "tr_TR", "tr@foo=bar", "tr-US", "TR", "tr-tr", "tR",
+ "az", "az-AZ", "az_AZ", "az@foo=bar", "az-US", "Az", "AZ-AZ",
+};
+const char* g_non_turkic_locales[] = {
+ "en", "en-US", "en_US", "en@foo=bar", "EN", "En",
+ "ja", "el", "fil", "fi", "lt",
+};
+const char* g_greek_locales[] = {
+ "el", "el-GR", "el_GR", "el@foo=bar", "el-US", "EL", "el-gr", "eL",
+};
+const char* g_non_greek_locales[] = {
+ "en", "en-US", "en_US", "en@foo=bar", "EN", "En",
+ "ja", "tr", "az", "fil", "fi", "lt",
+};
+const char* g_lithuanian_locales[] = {
+ "lt", "lt-LT", "lt_LT", "lt@foo=bar", "lt-US", "LT", "lt-lt", "lT",
+};
+// Should not have "tr" or "az" because "lt" and 'tr/az' rules conflict with
+// each other.
+const char* g_non_lithuanian_locales[] = {
+ "en", "en-US", "en_US", "en@foo=bar", "EN", "En", "ja", "fil", "fi", "el",
+};
+
+TEST(StringTest, ToUpperLocale) {
+ CaseFoldingTestData test_data_list[] = {
+ {
+ "Turkic input", g_turkic_input, g_turkic_locales,
+ sizeof(g_turkic_locales) / sizeof(const char*),
+ "IS\xC4\xB0\xC4\xB0 \xC4\xB0SII",
+ },
+ {
+ "Turkic input", g_turkic_input, g_non_turkic_locales,
+ sizeof(g_non_turkic_locales) / sizeof(const char*),
+ "ISI\xC4\xB0 \xC4\xB0SII",
+ },
+ {
+ "Greek input", g_greek_input, g_greek_locales,
+ sizeof(g_greek_locales) / sizeof(const char*),
+ "\xCE\x9F\xCE\x94\xCE\x9F\xCE\xA3 \xCE\x9F\xCE\x94\xCE\x9F\xCE\xA3 "
+ "\xCE\xA3\xCE\x9F \xCE\xA3\xCE\x9F \x4F\xCE\xA3 \xCE\x9F\xCE\xA3 "
+ "\xCE\xA3 \xCE\x95\xCE\x9E",
+ },
+ {
+ "Greek input", g_greek_input, g_non_greek_locales,
+ sizeof(g_non_greek_locales) / sizeof(const char*),
+ "\xCE\x9F\xCE\x94\xCE\x8C\xCE\xA3 \xCE\x9F\xCE\x94\xCE\x8C\xCE\xA3 "
+ "\xCE\xA3\xCE\x9F \xCE\xA3\xCE\x9F \x4F\xCE\xA3 \xCE\x9F\xCE\xA3 "
+ "\xCE\xA3 \xE1\xBC\x9D\xCE\x9E",
+ },
+ {
+ "Lithuanian input", g_lithuanian_input, g_lithuanian_locales,
+ sizeof(g_lithuanian_locales) / sizeof(const char*),
+ "I \xC3\x8F J J\xCC\x88 \xC4\xAE \xC4\xAE\xCC\x88 \xC3\x8C \xC3\x8D "
+ "\xC4\xA8 XI\xCC\x88 XJ\xCC\x88 X\xC4\xAE\xCC\x88 XI\xCC\x80 "
+ "XI\xCC\x81 XI\xCC\x83 XI X\xC3\x8F XJ XJ\xCC\x88 X\xC4\xAE "
+ "X\xC4\xAE\xCC\x88",
+ },
+ {
+ "Lithuanian input", g_lithuanian_input, g_non_lithuanian_locales,
+ sizeof(g_non_lithuanian_locales) / sizeof(const char*),
+ "I \xC3\x8F J J\xCC\x88 \xC4\xAE \xC4\xAE\xCC\x88 \xC3\x8C \xC3\x8D "
+ "\xC4\xA8 XI\xCC\x87\xCC\x88 XJ\xCC\x87\xCC\x88 "
+ "X\xC4\xAE\xCC\x87\xCC\x88 XI\xCC\x87\xCC\x80 XI\xCC\x87\xCC\x81 "
+ "XI\xCC\x87\xCC\x83 XI X\xC3\x8F XJ XJ\xCC\x88 X\xC4\xAE "
+ "X\xC4\xAE\xCC\x88",
+ },
+ };
+
+ for (size_t i = 0; i < sizeof(test_data_list) / sizeof(test_data_list[0]);
+ ++i) {
+ const char* expected = test_data_list[i].expected;
+ String source = String::FromUTF8(test_data_list[i].source);
+ for (size_t j = 0; j < test_data_list[i].locale_list_length; ++j) {
+ const char* locale = test_data_list[i].locale_list[j];
+ EXPECT_STREQ(expected, source.UpperUnicode(locale).Utf8().data())
+ << test_data_list[i].source_description << "; locale=" << locale;
+ }
+ }
+}
+
+TEST(StringTest, ToLowerLocale) {
+ CaseFoldingTestData test_data_list[] = {
+ {
+ "Turkic input", g_turkic_input, g_turkic_locales,
+ sizeof(g_turkic_locales) / sizeof(const char*),
+ "\xC4\xB1sii is\xC4\xB1\xC4\xB1",
+ },
+ {
+ "Turkic input", g_turkic_input, g_non_turkic_locales,
+ sizeof(g_non_turkic_locales) / sizeof(const char*),
+ // U+0130 is lowercased to U+0069 followed by U+0307
+ "isii\xCC\x87 i\xCC\x87s\xC4\xB1i",
+ },
+ {
+ "Greek input", g_greek_input, g_greek_locales,
+ sizeof(g_greek_locales) / sizeof(const char*),
+ "\xCE\xBF\xCE\xB4\xCF\x8C\xCF\x82 \xCE\xBF\xCE\xB4\xCF\x8C\xCF\x82 "
+ "\xCF\x83\xCE\xBF \xCF\x83\xCE\xBF \x6F\xCF\x82 \xCE\xBF\xCF\x82 "
+ "\xCF\x83 \xE1\xBC\x95\xCE\xBE",
+ },
+ {
+ "Greek input", g_greek_input, g_non_greek_locales,
+ sizeof(g_greek_locales) / sizeof(const char*),
+ "\xCE\xBF\xCE\xB4\xCF\x8C\xCF\x82 \xCE\xBF\xCE\xB4\xCF\x8C\xCF\x82 "
+ "\xCF\x83\xCE\xBF \xCF\x83\xCE\xBF \x6F\xCF\x82 \xCE\xBF\xCF\x82 "
+ "\xCF\x83 \xE1\xBC\x95\xCE\xBE",
+ },
+ {
+ "Lithuanian input", g_lithuanian_input, g_lithuanian_locales,
+ sizeof(g_lithuanian_locales) / sizeof(const char*),
+ "i \xC3\xAF j j\xCC\x87\xCC\x88 \xC4\xAF \xC4\xAF\xCC\x87\xCC\x88 "
+ "i\xCC\x87\xCC\x80 i\xCC\x87\xCC\x81 i\xCC\x87\xCC\x83 "
+ "xi\xCC\x87\xCC\x88 xj\xCC\x87\xCC\x88 x\xC4\xAF\xCC\x87\xCC\x88 "
+ "xi\xCC\x87\xCC\x80 xi\xCC\x87\xCC\x81 xi\xCC\x87\xCC\x83 xi "
+ "x\xC3\xAF xj xj\xCC\x87\xCC\x88 x\xC4\xAF x\xC4\xAF\xCC\x87\xCC\x88",
+ },
+ {
+ "Lithuanian input", g_lithuanian_input, g_non_lithuanian_locales,
+ sizeof(g_non_lithuanian_locales) / sizeof(const char*),
+ "\x69 \xC3\xAF \x6A \x6A\xCC\x88 \xC4\xAF \xC4\xAF\xCC\x88 \xC3\xAC "
+ "\xC3\xAD \xC4\xA9 \x78\x69\xCC\x87\xCC\x88 \x78\x6A\xCC\x87\xCC\x88 "
+ "\x78\xC4\xAF\xCC\x87\xCC\x88 \x78\x69\xCC\x87\xCC\x80 "
+ "\x78\x69\xCC\x87\xCC\x81 \x78\x69\xCC\x87\xCC\x83 \x78\x69 "
+ "\x78\xC3\xAF \x78\x6A \x78\x6A\xCC\x88 \x78\xC4\xAF "
+ "\x78\xC4\xAF\xCC\x88",
+ },
+ };
+
+ for (size_t i = 0; i < sizeof(test_data_list) / sizeof(test_data_list[0]);
+ ++i) {
+ const char* expected = test_data_list[i].expected;
+ String source = String::FromUTF8(test_data_list[i].source);
+ for (size_t j = 0; j < test_data_list[i].locale_list_length; ++j) {
+ const char* locale = test_data_list[i].locale_list[j];
+ EXPECT_STREQ(expected, source.LowerUnicode(locale).Utf8().data())
+ << test_data_list[i].source_description << "; locale=" << locale;
+ }
+ }
+}
+
+TEST(StringTest, StartsWithIgnoringUnicodeCase) {
+ // [U+017F U+212A i a] starts with "sk".
+ EXPECT_TRUE(
+ String::FromUTF8("\xC5\xBF\xE2\x84\xAAia").StartsWithIgnoringCase("sk"));
+}
+
+TEST(StringTest, StartsWithIgnoringASCIICase) {
+ String all_ascii("LINK");
+ String all_ascii_lower_case("link");
+ EXPECT_TRUE(all_ascii.StartsWithIgnoringASCIICase(all_ascii_lower_case));
+ String all_ascii_mixed_case("lInK");
+ EXPECT_TRUE(all_ascii.StartsWithIgnoringASCIICase(all_ascii_mixed_case));
+ String all_ascii_different("foo");
+ EXPECT_FALSE(all_ascii.StartsWithIgnoringASCIICase(all_ascii_different));
+ String non_ascii = String::FromUTF8("LIN\xE2\x84\xAA");
+ EXPECT_FALSE(all_ascii.StartsWithIgnoringASCIICase(non_ascii));
+ EXPECT_TRUE(
+ all_ascii.StartsWithIgnoringASCIICase(non_ascii.DeprecatedLower()));
+
+ EXPECT_FALSE(non_ascii.StartsWithIgnoringASCIICase(all_ascii));
+ EXPECT_FALSE(non_ascii.StartsWithIgnoringASCIICase(all_ascii_lower_case));
+ EXPECT_FALSE(non_ascii.StartsWithIgnoringASCIICase(all_ascii_mixed_case));
+ EXPECT_FALSE(non_ascii.StartsWithIgnoringASCIICase(all_ascii_different));
+}
+
+TEST(StringTest, EndsWithIgnoringASCIICase) {
+ String all_ascii("LINK");
+ String all_ascii_lower_case("link");
+ EXPECT_TRUE(all_ascii.EndsWithIgnoringASCIICase(all_ascii_lower_case));
+ String all_ascii_mixed_case("lInK");
+ EXPECT_TRUE(all_ascii.EndsWithIgnoringASCIICase(all_ascii_mixed_case));
+ String all_ascii_different("foo");
+ EXPECT_FALSE(all_ascii.EndsWithIgnoringASCIICase(all_ascii_different));
+ String non_ascii = String::FromUTF8("LIN\xE2\x84\xAA");
+ EXPECT_FALSE(all_ascii.EndsWithIgnoringASCIICase(non_ascii));
+ EXPECT_TRUE(all_ascii.EndsWithIgnoringASCIICase(non_ascii.DeprecatedLower()));
+
+ EXPECT_FALSE(non_ascii.EndsWithIgnoringASCIICase(all_ascii));
+ EXPECT_FALSE(non_ascii.EndsWithIgnoringASCIICase(all_ascii_lower_case));
+ EXPECT_FALSE(non_ascii.EndsWithIgnoringASCIICase(all_ascii_mixed_case));
+ EXPECT_FALSE(non_ascii.EndsWithIgnoringASCIICase(all_ascii_different));
+}
+
+TEST(StringTest, EqualIgnoringASCIICase) {
+ String all_ascii("LINK");
+ String all_ascii_lower_case("link");
+ EXPECT_TRUE(EqualIgnoringASCIICase(all_ascii, all_ascii_lower_case));
+ String all_ascii_mixed_case("lInK");
+ EXPECT_TRUE(EqualIgnoringASCIICase(all_ascii, all_ascii_mixed_case));
+ String all_ascii_different("foo");
+ EXPECT_FALSE(EqualIgnoringASCIICase(all_ascii, all_ascii_different));
+ String non_ascii = String::FromUTF8("LIN\xE2\x84\xAA");
+ EXPECT_FALSE(EqualIgnoringASCIICase(all_ascii, non_ascii));
+ EXPECT_TRUE(EqualIgnoringASCIICase(all_ascii, non_ascii.DeprecatedLower()));
+
+ EXPECT_FALSE(EqualIgnoringASCIICase(non_ascii, all_ascii));
+ EXPECT_FALSE(EqualIgnoringASCIICase(non_ascii, all_ascii_lower_case));
+ EXPECT_FALSE(EqualIgnoringASCIICase(non_ascii, all_ascii_mixed_case));
+ EXPECT_FALSE(EqualIgnoringASCIICase(non_ascii, all_ascii_different));
+}
+
+TEST(StringTest, FindIgnoringASCIICase) {
+ String needle = String::FromUTF8("a\xCC\x88qa\xCC\x88");
+
+ // Multiple matches, non-overlapping
+ String haystack1 = String::FromUTF8(
+ "aA\xCC\x88QA\xCC\x88sA\xCC\x88qa\xCC\x88rfi\xC3\xA4q\xC3\xA4");
+ EXPECT_EQ(1u, haystack1.FindIgnoringASCIICase(needle));
+ EXPECT_EQ(7u, haystack1.FindIgnoringASCIICase(needle, 2));
+ EXPECT_EQ(kNotFound, haystack1.FindIgnoringASCIICase(needle, 8));
+
+ // Multiple matches, overlapping
+ String haystack2 = String::FromUTF8("aA\xCC\x88QA\xCC\x88qa\xCC\x88rfi");
+ EXPECT_EQ(1u, haystack2.FindIgnoringASCIICase(needle));
+ EXPECT_EQ(4u, haystack2.FindIgnoringASCIICase(needle, 2));
+ EXPECT_EQ(kNotFound, haystack2.FindIgnoringASCIICase(needle, 5));
+}
+
+TEST(StringTest, DeprecatedLower) {
+ EXPECT_STREQ("link", String("LINK").DeprecatedLower().Ascii().data());
+ EXPECT_STREQ("link", String("lInk").DeprecatedLower().Ascii().data());
+ EXPECT_STREQ("lin\xE1k",
+ String("lIn\xC1k").DeprecatedLower().Latin1().data());
+ // U+212A -> k
+ EXPECT_STREQ(
+ "link",
+ String::FromUTF8("LIN\xE2\x84\xAA").DeprecatedLower().Utf8().data());
+}
+
+TEST(StringTest, Ensure16Bit) {
+ String string8("8bit");
+ EXPECT_TRUE(string8.Is8Bit());
+ string8.Ensure16Bit();
+ EXPECT_FALSE(string8.Is8Bit());
+ EXPECT_EQ("8bit", string8);
+
+ String string16(reinterpret_cast<const UChar*>(u"16bit"));
+ EXPECT_FALSE(string16.Is8Bit());
+ string16.Ensure16Bit();
+ EXPECT_FALSE(string16.Is8Bit());
+ EXPECT_EQ("16bit", string16);
+
+ String empty8(StringImpl::empty_);
+ EXPECT_TRUE(empty8.Is8Bit());
+ empty8.Ensure16Bit();
+ EXPECT_FALSE(empty8.Is8Bit());
+ EXPECT_TRUE(empty8.IsEmpty());
+ EXPECT_FALSE(empty8.IsNull());
+
+ String empty16(StringImpl::empty16_bit_);
+ EXPECT_FALSE(empty16.Is8Bit());
+ empty16.Ensure16Bit();
+ EXPECT_FALSE(empty16.Is8Bit());
+ EXPECT_TRUE(empty16.IsEmpty());
+ EXPECT_FALSE(empty16.IsNull());
+
+ String null_string;
+ null_string.Ensure16Bit();
+ EXPECT_TRUE(null_string.IsNull());
+}
+
+CString ToCStringThroughPrinter(const String& string) {
+ std::ostringstream output;
+ output << string;
+ const std::string& result = output.str();
+ return CString(result.data(), result.length());
+}
+
+TEST(StringTest, StringPrinter) {
+ EXPECT_EQ(CString("\"Hello!\""), ToCStringThroughPrinter("Hello!"));
+ EXPECT_EQ(CString("\"\\\"\""), ToCStringThroughPrinter("\""));
+ EXPECT_EQ(CString("\"\\\\\""), ToCStringThroughPrinter("\\"));
+ EXPECT_EQ(
+ CString("\"\\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\""),
+ ToCStringThroughPrinter(String("\x00\x01\x02\x03\x04\x05\x06\x07", 8)));
+ EXPECT_EQ(
+ CString("\"\\u0008\\t\\n\\u000B\\u000C\\r\\u000E\\u000F\""),
+ ToCStringThroughPrinter(String("\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", 8)));
+ EXPECT_EQ(
+ CString("\"\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017\""),
+ ToCStringThroughPrinter(String("\x10\x11\x12\x13\x14\x15\x16\x17", 8)));
+ EXPECT_EQ(
+ CString("\"\\u0018\\u0019\\u001A\\u001B\\u001C\\u001D\\u001E\\u001F\""),
+ ToCStringThroughPrinter(String("\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F", 8)));
+ EXPECT_EQ(CString("\"\\u007F\\u0080\\u0081\""),
+ ToCStringThroughPrinter("\x7F\x80\x81"));
+ EXPECT_EQ(CString("\"\""), ToCStringThroughPrinter(g_empty_string));
+ EXPECT_EQ(CString("<null>"), ToCStringThroughPrinter(String()));
+
+ static const UChar kUnicodeSample[] = {0x30C6, 0x30B9,
+ 0x30C8}; // "Test" in Japanese.
+ EXPECT_EQ(CString("\"\\u30C6\\u30B9\\u30C8\""),
+ ToCStringThroughPrinter(
+ String(kUnicodeSample, WTF_ARRAY_LENGTH(kUnicodeSample))));
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/thread_restriction_verifier.h b/chromium/third_party/blink/renderer/platform/wtf/thread_restriction_verifier.h
new file mode 100644
index 00000000000..6a1dd19a4b2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/thread_restriction_verifier.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_THREAD_RESTRICTION_VERIFIER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_THREAD_RESTRICTION_VERIFIER_H_
+
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+#if DCHECK_IS_ON()
+
+#include "third_party/blink/renderer/platform/wtf/threading.h"
+
+namespace WTF {
+
+// Verifies that a class is used in a way that respects its lack of
+// thread-safety. The default mode is to verify that the object will only be
+// used on a single thread. The thread gets captured when setShared(true) is
+// called. The mode may be changed by calling useMutexMode (or
+// turnOffVerification).
+class ThreadRestrictionVerifier {
+ public:
+ ThreadRestrictionVerifier() : shared_(false), owning_thread_(0) {}
+
+ // Call onRef() before refCount is incremented in ref(). Returns whether the
+ // ref() is safe.
+ template <typename COUNTERTYPE>
+ bool OnRef(COUNTERTYPE ref_count) {
+ // Start thread verification as soon as the ref count gets to 2. This
+ // heuristic reflects the fact that items are often created on one
+ // thread and then given to another thread to be used.
+ // FIXME: Make this restriction tigher. Especially as we move to more
+ // common methods for sharing items across threads like
+ // CrossThreadCopier.h
+ // We should be able to add a "detachFromThread" method to make this
+ // explicit.
+ if (ref_count == 1)
+ SetShared(true);
+ return IsSafeToUse();
+ }
+
+ // Call onDeref() before refCount is decremented in deref(). Returns whether
+ // the deref() is safe.
+ template <typename COUNTERTYPE>
+ bool OnDeref(COUNTERTYPE ref_count) {
+ bool safe = IsSafeToUse();
+ // Stop thread verification when the ref goes to 1 because it
+ // is safe to be passed to another thread at this point.
+ if (ref_count == 2)
+ SetShared(false);
+ return safe;
+ }
+
+ // Is it OK to use the object at this moment on the current thread?
+ bool IsSafeToUse() const {
+ return !shared_ || owning_thread_ == CurrentThread();
+ }
+
+ private:
+ // Indicates that the object may (or may not) be owned by more than one place.
+ void SetShared(bool shared) {
+ bool previously_shared = shared_;
+ shared_ = shared;
+
+ if (!shared_)
+ return;
+
+ DCHECK_NE(shared, previously_shared);
+ // Capture the current thread to verify that subsequent ref/deref happen on
+ // this thread.
+ owning_thread_ = CurrentThread();
+ }
+
+ bool shared_;
+
+ ThreadIdentifier owning_thread_;
+};
+
+} // namespace WTF
+
+#endif // DCHECK_IS_ON()
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_THREAD_RESTRICTION_VERIFIER_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h b/chromium/third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h
new file mode 100644
index 00000000000..0880e051e0f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2007, 2008, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com)
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_THREAD_SAFE_REF_COUNTED_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_THREAD_SAFE_REF_COUNTED_H_
+
+#include "base/memory/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace WTF {
+
+template <typename T, typename Traits>
+class ThreadSafeRefCounted;
+
+template <typename T>
+struct DefaultThreadSafeRefCountedTraits {
+ static void Destruct(const T* x) {
+ WTF::ThreadSafeRefCounted<
+ T, DefaultThreadSafeRefCountedTraits>::DeleteInternal(x);
+ }
+};
+
+template <typename T, typename Traits = DefaultThreadSafeRefCountedTraits<T>>
+class ThreadSafeRefCounted : public base::RefCountedThreadSafe<T, Traits> {
+ // Put |T| in here instead of |RefCounted| so the heap profiler reports |T|
+ // instead of |RefCounted<T>|. This does not affect overloading of operator
+ // new.
+ USING_FAST_MALLOC(T);
+
+ public:
+ REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE();
+
+ private:
+ friend struct DefaultThreadSafeRefCountedTraits<T>;
+
+ template <typename U>
+ static void DeleteInternal(const U* x) {
+ delete x;
+ }
+};
+
+} // namespace WTF
+
+using WTF::ThreadSafeRefCounted;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_THREAD_SAFE_REF_COUNTED_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/thread_specific.h b/chromium/third_party/blink/renderer/platform/wtf/thread_specific.h
new file mode 100644
index 00000000000..44179d6970b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/thread_specific.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2009 Jian Li <jianli@chromium.org>
+ * Copyright (C) 2012 Patrick Gansterer <paroga@paroga.com>
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_THREAD_SPECIFIC_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_THREAD_SPECIFIC_H_
+
+#include "base/macros.h"
+#include "base/threading/thread_local_storage.h"
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/partition_allocator.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
+#include "third_party/blink/renderer/platform/wtf/stack_util.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/wtf.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_export.h"
+
+namespace WTF {
+
+template <typename T>
+class ThreadSpecific {
+ USING_FAST_MALLOC(ThreadSpecific);
+
+ public:
+ ThreadSpecific() : slot_(&Destroy){};
+ bool
+ IsSet(); // Useful as a fast check to see if this thread has set this value.
+ T* operator->();
+ operator T*();
+ T& operator*();
+
+ private:
+ // Not implemented. It's technically possible to destroy a thread specific
+ // key, but one would need to make sure that all values have been destroyed
+ // already (usually, that all threads that used it have exited). It's
+ // unlikely that any user of this call will be in that situation - and having
+ // a destructor defined can be confusing, given that it has such strong
+ // pre-requisites to work correctly.
+ ~ThreadSpecific() = delete;
+
+ T* Get() { return static_cast<T*>(slot_.Get()); }
+
+ void Set(T* ptr) {
+ DCHECK(!Get());
+ slot_.Set(ptr);
+ }
+
+ void static Destroy(void* ptr);
+
+ // This member must only be accessed or modified on the main thread.
+ T* main_thread_storage_ = nullptr;
+ base::ThreadLocalStorage::Slot slot_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadSpecific);
+};
+
+template <typename T>
+inline void ThreadSpecific<T>::Destroy(void* ptr) {
+ // Never call destructors on the main thread. This is fine because Blink no
+ // longer has a graceful shutdown sequence. Be careful to call this function
+ // (which can be re-entrant) while the pointer is still set, to avoid lazily
+ // allocating WTFThreadData after it is destroyed.
+ if (IsMainThread())
+ return;
+
+ // The memory was allocated via Partitions::FastZeroedMalloc, and then the
+ // object was placement-newed. To destroy, we must call the delete expression,
+ // and then free the memory manually.
+ T* instance = static_cast<T*>(ptr);
+ instance->~T();
+ Partitions::FastFree(ptr);
+}
+
+template <typename T>
+inline bool ThreadSpecific<T>::IsSet() {
+ return !!Get();
+}
+
+template <typename T>
+inline ThreadSpecific<T>::operator T*() {
+ T* off_thread_ptr;
+#if defined(__GLIBC__) || defined(OS_ANDROID) || defined(OS_FREEBSD)
+ // TLS is fast on these platforms.
+ // TODO(csharrison): Qualify this statement for Android.
+ const bool kMainThreadAlwaysChecksTLS = true;
+ T** ptr = &off_thread_ptr;
+ off_thread_ptr = static_cast<T*>(Get());
+#else
+ const bool kMainThreadAlwaysChecksTLS = false;
+ T** ptr = &main_thread_storage_;
+ if (UNLIKELY(MayNotBeMainThread())) {
+ off_thread_ptr = static_cast<T*>(Get());
+ ptr = &off_thread_ptr;
+ }
+#endif
+ // Set up thread-specific value's memory pointer before invoking constructor,
+ // in case any function it calls needs to access the value, to avoid
+ // recursion.
+ if (UNLIKELY(!*ptr)) {
+ *ptr = static_cast<T*>(Partitions::FastZeroedMalloc(
+ sizeof(T), WTF_HEAP_PROFILER_TYPE_NAME(T)));
+
+ // Even if we didn't realize we're on the main thread, we might still be.
+ // We need to double-check so that |main_thread_storage_| is populated.
+ if (!kMainThreadAlwaysChecksTLS && UNLIKELY(ptr != &main_thread_storage_) &&
+ IsMainThread()) {
+ main_thread_storage_ = *ptr;
+ }
+
+ Set(*ptr);
+ new (NotNull, *ptr) T;
+ }
+ return *ptr;
+}
+
+template <typename T>
+inline T* ThreadSpecific<T>::operator->() {
+ return operator T*();
+}
+
+template <typename T>
+inline T& ThreadSpecific<T>::operator*() {
+ return *operator T*();
+}
+
+} // namespace WTF
+
+using WTF::ThreadSpecific;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_THREAD_SPECIFIC_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/threading.h b/chromium/third_party/blink/renderer/platform/wtf/threading.h
new file mode 100644
index 00000000000..91af79bcd49
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/threading.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2007, 2008, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com)
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_THREADING_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_THREADING_H_
+
+#include <stdint.h>
+
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/wtf/atomics.h"
+#include "third_party/blink/renderer/platform/wtf/type_traits.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_export.h"
+
+namespace WTF {
+
+#if defined(OS_WIN)
+typedef uint32_t ThreadIdentifier;
+#else
+typedef intptr_t ThreadIdentifier;
+#endif
+
+namespace internal {
+WTF_EXPORT ThreadIdentifier CurrentThreadSyscall();
+} // namespace internal
+
+// Initializes global state required by |currentThread|.
+// Needs to be called once during program execution, before |currentThread|.
+WTF_EXPORT void InitializeCurrentThread();
+
+WTF_EXPORT ThreadIdentifier CurrentThread();
+
+#if DCHECK_IS_ON()
+WTF_EXPORT bool IsBeforeThreadCreated();
+WTF_EXPORT void WillCreateThread();
+#endif
+
+} // namespace WTF
+
+using WTF::ThreadIdentifier;
+using WTF::CurrentThread;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_THREADING_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/threading_primitives.h b/chromium/third_party/blink/renderer/platform/wtf/threading_primitives.h
new file mode 100644
index 00000000000..7777fe96d7c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/threading_primitives.h
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2007, 2008, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com)
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_THREADING_PRIMITIVES_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_THREADING_PRIMITIVES_H_
+
+#include "base/macros.h"
+#include "base/thread_annotations.h"
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/locker.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_export.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+#if defined(OS_POSIX)
+#include <pthread.h>
+#endif
+
+namespace WTF {
+
+#if defined(OS_POSIX)
+struct PlatformMutex {
+ pthread_mutex_t internal_mutex_;
+#if DCHECK_IS_ON()
+ size_t recursion_count_;
+#endif
+};
+typedef pthread_cond_t PlatformCondition;
+#elif defined(OS_WIN)
+struct PlatformMutex {
+ CRITICAL_SECTION internal_mutex_;
+ size_t recursion_count_;
+};
+typedef CONDITION_VARIABLE PlatformCondition;
+#else
+typedef void* PlatformMutex;
+typedef void* PlatformCondition;
+#endif
+
+class WTF_EXPORT MutexBase {
+ USING_FAST_MALLOC(MutexBase);
+
+ public:
+ ~MutexBase();
+
+ void lock();
+ void unlock();
+#if DCHECK_IS_ON()
+ bool Locked() { return mutex_.recursion_count_ > 0; }
+#endif
+
+ public:
+ PlatformMutex& Impl() { return mutex_; }
+
+ protected:
+ MutexBase(bool recursive);
+
+ PlatformMutex mutex_;
+
+ DISALLOW_COPY_AND_ASSIGN(MutexBase);
+};
+
+class LOCKABLE WTF_EXPORT Mutex : public MutexBase {
+ public:
+ Mutex() : MutexBase(false) {}
+ bool TryLock() EXCLUSIVE_TRYLOCK_FUNCTION(true);
+
+ // lock() and unlock() are overridden solely for the purpose of annotating
+ // them. The compiler is expected to optimize the calls away.
+ void lock() EXCLUSIVE_LOCK_FUNCTION() { MutexBase::lock(); }
+ void unlock() UNLOCK_FUNCTION() { MutexBase::unlock(); }
+};
+
+class WTF_EXPORT RecursiveMutex : public MutexBase {
+ public:
+ RecursiveMutex() : MutexBase(true) {}
+ bool TryLock();
+};
+
+class SCOPED_LOCKABLE MutexLocker final {
+ STACK_ALLOCATED();
+
+ public:
+ MutexLocker(Mutex& mutex) EXCLUSIVE_LOCK_FUNCTION(mutex) : mutex_(mutex) {
+ mutex_.lock();
+ }
+ ~MutexLocker() UNLOCK_FUNCTION() { mutex_.unlock(); }
+
+ private:
+ Mutex& mutex_;
+
+ DISALLOW_COPY_AND_ASSIGN(MutexLocker);
+};
+
+using RecursiveMutexLocker = Locker<RecursiveMutex>;
+
+class MutexTryLocker final {
+ STACK_ALLOCATED();
+
+ public:
+ MutexTryLocker(Mutex& mutex) : mutex_(mutex), locked_(mutex.TryLock()) {}
+ ~MutexTryLocker() {
+ if (locked_)
+ mutex_.unlock();
+ }
+
+ bool Locked() const { return locked_; }
+
+ private:
+ Mutex& mutex_;
+ bool locked_;
+
+ DISALLOW_COPY_AND_ASSIGN(MutexTryLocker);
+};
+
+class WTF_EXPORT ThreadCondition final {
+ USING_FAST_MALLOC(ThreadCondition); // Only HeapTest.cpp requires.
+
+ public:
+ ThreadCondition();
+ ~ThreadCondition();
+
+ void Wait(Mutex&);
+ // Returns true if the condition was signaled before absoluteTime, false if
+ // the absoluteTime was reached or is in the past.
+ // The absoluteTime is in seconds, starting on January 1, 1970. The time is
+ // assumed to use the same time zone as WTF::currentTime().
+ bool TimedWait(Mutex&, double absolute_time);
+ void Signal();
+ void Broadcast();
+
+ private:
+ PlatformCondition condition_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadCondition);
+};
+
+#if defined(OS_WIN)
+// The absoluteTime is in seconds, starting on January 1, 1970. The time is
+// assumed to use the same time zone as WTF::currentTime().
+// Returns an interval in milliseconds suitable for passing to one of the Win32
+// wait functions (e.g., ::WaitForSingleObject).
+DWORD AbsoluteTimeToWaitTimeoutInterval(double absolute_time);
+#endif
+
+} // namespace WTF
+
+using WTF::MutexBase;
+using WTF::Mutex;
+using WTF::RecursiveMutex;
+using WTF::MutexLocker;
+using WTF::MutexTryLocker;
+using WTF::RecursiveMutexLocker;
+using WTF::ThreadCondition;
+
+#if defined(OS_WIN)
+using WTF::AbsoluteTimeToWaitTimeoutInterval;
+#endif
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_THREADING_PRIMITIVES_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/threading_pthreads.cc b/chromium/third_party/blink/renderer/platform/wtf/threading_pthreads.cc
new file mode 100644
index 00000000000..57e06eddf01
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/threading_pthreads.cc
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2007, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com)
+ * Copyright (C) 2011 Research In Motion Limited. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "third_party/blink/renderer/platform/wtf/threading.h"
+
+#include "build/build_config.h"
+
+#if defined(OS_POSIX)
+
+#include <errno.h>
+#include <limits.h>
+#include <sched.h>
+#include <sys/time.h>
+#include "third_party/blink/renderer/platform/wtf/date_math.h"
+#include "third_party/blink/renderer/platform/wtf/dtoa/double-conversion.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/thread_specific.h"
+#include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_thread_data.h"
+
+#if defined(OS_MACOSX)
+#include <objc/objc-auto.h>
+#endif
+
+#if defined(OS_LINUX)
+#include <sys/syscall.h>
+#endif
+
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+#include <unistd.h>
+#endif
+
+namespace WTF {
+
+namespace internal {
+
+ThreadIdentifier CurrentThreadSyscall() {
+#if defined(OS_MACOSX)
+ return pthread_mach_thread_np(pthread_self());
+#elif defined(OS_LINUX)
+ return syscall(__NR_gettid);
+#elif defined(OS_ANDROID)
+ return gettid();
+#else
+ return reinterpret_cast<uintptr_t>(pthread_self());
+#endif
+}
+
+} // namespace internal
+
+void InitializeThreading() {
+ // This should only be called once.
+ WTFThreadData::Initialize();
+
+ InitializeDates();
+ // Force initialization of static DoubleToStringConverter converter variable
+ // inside EcmaScriptConverter function while we are in single thread mode.
+ double_conversion::DoubleToStringConverter::EcmaScriptConverter();
+}
+
+namespace {
+pthread_key_t g_current_thread_key;
+bool g_current_thread_key_initialized = false;
+} // namespace
+
+void InitializeCurrentThread() {
+ DCHECK(!g_current_thread_key_initialized);
+ int error = pthread_key_create(&g_current_thread_key, nullptr);
+ CHECK(!error);
+ g_current_thread_key_initialized = true;
+}
+
+ThreadIdentifier CurrentThread() {
+ // This doesn't use WTF::ThreadSpecific (e.g. WTFThreadData) because
+ // ThreadSpecific now depends on currentThread. It is necessary to avoid this
+ // or a similar loop:
+ //
+ // currentThread
+ // -> wtfThreadData
+ // -> ThreadSpecific::operator*
+ // -> isMainThread
+ // -> currentThread
+ static_assert(sizeof(ThreadIdentifier) <= sizeof(void*),
+ "ThreadIdentifier must fit in a void*.");
+ DCHECK(g_current_thread_key_initialized);
+ void* value = pthread_getspecific(g_current_thread_key);
+ if (UNLIKELY(!value)) {
+ value = reinterpret_cast<void*>(
+ static_cast<intptr_t>(internal::CurrentThreadSyscall()));
+ DCHECK(value);
+ pthread_setspecific(g_current_thread_key, value);
+ }
+ return reinterpret_cast<intptr_t>(pthread_getspecific(g_current_thread_key));
+}
+
+MutexBase::MutexBase(bool recursive) {
+ pthread_mutexattr_t attr;
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype(
+ &attr, recursive ? PTHREAD_MUTEX_RECURSIVE : PTHREAD_MUTEX_NORMAL);
+
+ int result = pthread_mutex_init(&mutex_.internal_mutex_, &attr);
+ DCHECK_EQ(result, 0);
+#if DCHECK_IS_ON()
+ mutex_.recursion_count_ = 0;
+#endif
+
+ pthread_mutexattr_destroy(&attr);
+}
+
+MutexBase::~MutexBase() {
+ int result = pthread_mutex_destroy(&mutex_.internal_mutex_);
+ DCHECK_EQ(result, 0);
+}
+
+void MutexBase::lock() {
+ int result = pthread_mutex_lock(&mutex_.internal_mutex_);
+ DCHECK_EQ(result, 0);
+#if DCHECK_IS_ON()
+ ++mutex_.recursion_count_;
+#endif
+}
+
+void MutexBase::unlock() {
+#if DCHECK_IS_ON()
+ DCHECK(mutex_.recursion_count_);
+ --mutex_.recursion_count_;
+#endif
+ int result = pthread_mutex_unlock(&mutex_.internal_mutex_);
+ DCHECK_EQ(result, 0);
+}
+
+// There is a separate tryLock implementation for the Mutex and the
+// RecursiveMutex since on Windows we need to manually check if tryLock should
+// succeed or not for the non-recursive mutex. On Linux the two implementations
+// are equal except we can assert the recursion count is always zero for the
+// non-recursive mutex.
+bool Mutex::TryLock() {
+ int result = pthread_mutex_trylock(&mutex_.internal_mutex_);
+ if (result == 0) {
+#if DCHECK_IS_ON()
+ // The Mutex class is not recursive, so the recursionCount should be
+ // zero after getting the lock.
+ DCHECK(!mutex_.recursion_count_);
+ ++mutex_.recursion_count_;
+#endif
+ return true;
+ }
+ if (result == EBUSY)
+ return false;
+
+ NOTREACHED();
+ return false;
+}
+
+bool RecursiveMutex::TryLock() {
+ int result = pthread_mutex_trylock(&mutex_.internal_mutex_);
+ if (result == 0) {
+#if DCHECK_IS_ON()
+ ++mutex_.recursion_count_;
+#endif
+ return true;
+ }
+ if (result == EBUSY)
+ return false;
+
+ NOTREACHED();
+ return false;
+}
+
+ThreadCondition::ThreadCondition() {
+ pthread_cond_init(&condition_, nullptr);
+}
+
+ThreadCondition::~ThreadCondition() {
+ pthread_cond_destroy(&condition_);
+}
+
+void ThreadCondition::Wait(Mutex& mutex) {
+ PlatformMutex& platform_mutex = mutex.Impl();
+ int result = pthread_cond_wait(&condition_, &platform_mutex.internal_mutex_);
+ DCHECK_EQ(result, 0);
+#if DCHECK_IS_ON()
+ ++platform_mutex.recursion_count_;
+#endif
+}
+
+bool ThreadCondition::TimedWait(Mutex& mutex, double absolute_time) {
+ if (absolute_time < CurrentTime())
+ return false;
+
+ if (absolute_time > INT_MAX) {
+ Wait(mutex);
+ return true;
+ }
+
+ int time_seconds = static_cast<int>(absolute_time);
+ int time_nanoseconds = static_cast<int>((absolute_time - time_seconds) * 1E9);
+
+ timespec target_time;
+ target_time.tv_sec = time_seconds;
+ target_time.tv_nsec = time_nanoseconds;
+
+ PlatformMutex& platform_mutex = mutex.Impl();
+ int result = pthread_cond_timedwait(
+ &condition_, &platform_mutex.internal_mutex_, &target_time);
+#if DCHECK_IS_ON()
+ ++platform_mutex.recursion_count_;
+#endif
+ return result == 0;
+}
+
+void ThreadCondition::Signal() {
+ int result = pthread_cond_signal(&condition_);
+ DCHECK_EQ(result, 0);
+}
+
+void ThreadCondition::Broadcast() {
+ int result = pthread_cond_broadcast(&condition_);
+ DCHECK_EQ(result, 0);
+}
+
+#if DCHECK_IS_ON()
+static bool g_thread_created = false;
+
+Mutex& GetThreadCreatedMutex() {
+ static Mutex g_thread_created_mutex;
+ return g_thread_created_mutex;
+}
+
+bool IsBeforeThreadCreated() {
+ MutexLocker locker(GetThreadCreatedMutex());
+ return !g_thread_created;
+}
+
+void WillCreateThread() {
+ MutexLocker locker(GetThreadCreatedMutex());
+ g_thread_created = true;
+}
+#endif
+
+} // namespace WTF
+
+#endif // defined(OS_POSIX)
diff --git a/chromium/third_party/blink/renderer/platform/wtf/threading_win.cc b/chromium/third_party/blink/renderer/platform/wtf/threading_win.cc
new file mode 100644
index 00000000000..76bef7f83ef
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/threading_win.cc
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ * Copyright (C) 2009 Torch Mobile, 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:
+ *
+ * 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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.
+ */
+
+/*
+ * There are numerous academic and practical works on how to implement
+ * pthread_cond_wait/pthread_cond_signal/pthread_cond_broadcast
+ * functions on Win32. Here is one example:
+ * http://www.cs.wustl.edu/~schmidt/win32-cv-1.html which is widely credited as
+ * a 'starting point' of modern attempts. There are several more or less proven
+ * implementations, one in Boost C++ library (http://www.boost.org) and another
+ * in pthreads-win32 (http://sourceware.org/pthreads-win32/).
+ *
+ * The number of articles and discussions is the evidence of significant
+ * difficulties in implementing these primitives correctly. The brief search
+ * of revisions, ChangeLog entries, discussions in comp.programming.threads and
+ * other places clearly documents numerous pitfalls and performance problems
+ * the authors had to overcome to arrive to the suitable implementations.
+ * Optimally, WebKit would use one of those supported/tested libraries
+ * directly. To roll out our own implementation is impractical, if even for
+ * the lack of sufficient testing. However, a faithful reproduction of the code
+ * from one of the popular supported libraries seems to be a good compromise.
+ *
+ * The early Boost implementation
+ * (http://www.boxbackup.org/trac/browser/box/nick/win/lib/win32/boost_1_32_0/libs/thread/src/condition.cpp?rev=30)
+ * is identical to pthreads-win32
+ * (http://sourceware.org/cgi-bin/cvsweb.cgi/pthreads/pthread_cond_wait.c?rev=1.10&content-type=text/x-cvsweb-markup&cvsroot=pthreads-win32).
+ * Current Boost uses yet another (although seemingly equivalent) algorithm
+ * which came from their 'thread rewrite' effort.
+ *
+ * This file includes timedWait/signal/broadcast implementations translated to
+ * WebKit coding style from the latest algorithm by Alexander Terekhov and
+ * Louis Thomas, as captured here:
+ * http://sourceware.org/cgi-bin/cvsweb.cgi/pthreads/pthread_cond_wait.c?rev=1.10&content-type=text/x-cvsweb-markup&cvsroot=pthreads-win32
+ * It replaces the implementation of their previous algorithm, also documented
+ * in the same source above. The naming and comments are left very close to
+ * original to enable easy cross-check.
+ *
+ * The corresponding Pthreads-win32 License is included below, and CONTRIBUTORS
+ * file which it refers to is added to source directory (as
+ * CONTRIBUTORS.pthreads-win32).
+ */
+
+/*
+ * Pthreads-win32 - POSIX Threads Library for Win32
+ * Copyright(C) 1998 John E. Bossom
+ * Copyright(C) 1999,2005 Pthreads-win32 contributors
+ *
+ * Contact Email: rpj@callisto.canberra.edu.au
+ *
+ * The current list of contributors is contained
+ * in the file CONTRIBUTORS included with the source
+ * code distribution. The list can also be seen at the
+ * following World Wide Web location:
+ * http://sources.redhat.com/pthreads-win32/contributors.html
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library in the file COPYING.LIB;
+ * if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include "third_party/blink/renderer/platform/wtf/threading.h"
+
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+
+#include <errno.h>
+#include <process.h>
+#include <windows.h>
+#include "third_party/blink/renderer/platform/wtf/date_math.h"
+#include "third_party/blink/renderer/platform/wtf/dtoa/double-conversion.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/thread_specific.h"
+#include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_thread_data.h"
+
+namespace WTF {
+
+// THREADNAME_INFO comes from
+// <http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx>.
+#pragma pack(push, 8)
+typedef struct tagTHREADNAME_INFO {
+ DWORD dw_type; // must be 0x1000
+ LPCSTR sz_name; // pointer to name (in user addr space)
+ DWORD dw_thread_id; // thread ID (-1=caller thread)
+ DWORD dw_flags; // reserved for future use, must be zero
+} THREADNAME_INFO;
+#pragma pack(pop)
+
+namespace internal {
+
+ThreadIdentifier CurrentThreadSyscall() {
+ return static_cast<ThreadIdentifier>(GetCurrentThreadId());
+}
+
+} // namespace internal
+
+void InitializeThreading() {
+ // This should only be called once.
+ WTFThreadData::Initialize();
+
+ InitializeDates();
+ // Force initialization of static DoubleToStringConverter converter variable
+ // inside EcmaScriptConverter function while we are in single thread mode.
+ double_conversion::DoubleToStringConverter::EcmaScriptConverter();
+}
+
+namespace {
+DWORD g_current_thread_key;
+bool g_current_thread_key_initialized = false;
+} // namespace
+
+void InitializeCurrentThread() {
+ DCHECK(!g_current_thread_key_initialized);
+
+ // This key is never destroyed.
+ g_current_thread_key = ::TlsAlloc();
+
+ CHECK_NE(g_current_thread_key, TLS_OUT_OF_INDEXES);
+ g_current_thread_key_initialized = true;
+}
+
+ThreadIdentifier CurrentThread() {
+ // This doesn't use WTF::ThreadSpecific (e.g. WTFThreadData) because
+ // ThreadSpecific now depends on currentThread. It is necessary to avoid this
+ // or a similar loop:
+ //
+ // currentThread
+ // -> wtfThreadData
+ // -> ThreadSpecific::operator*
+ // -> isMainThread
+ // -> currentThread
+ static_assert(sizeof(ThreadIdentifier) <= sizeof(void*),
+ "ThreadIdentifier must fit in a void*.");
+ DCHECK(g_current_thread_key_initialized);
+ void* value = ::TlsGetValue(g_current_thread_key);
+ if (UNLIKELY(!value)) {
+ value = reinterpret_cast<void*>(internal::CurrentThreadSyscall());
+ DCHECK(value);
+ ::TlsSetValue(g_current_thread_key, value);
+ }
+ return reinterpret_cast<intptr_t>(::TlsGetValue(g_current_thread_key));
+}
+
+MutexBase::MutexBase(bool recursive) {
+ mutex_.recursion_count_ = 0;
+ InitializeCriticalSection(&mutex_.internal_mutex_);
+}
+
+MutexBase::~MutexBase() {
+ DeleteCriticalSection(&mutex_.internal_mutex_);
+}
+
+void MutexBase::lock() {
+ EnterCriticalSection(&mutex_.internal_mutex_);
+ ++mutex_.recursion_count_;
+}
+
+void MutexBase::unlock() {
+ DCHECK(mutex_.recursion_count_);
+ --mutex_.recursion_count_;
+ LeaveCriticalSection(&mutex_.internal_mutex_);
+}
+
+bool Mutex::TryLock() {
+ // This method is modeled after the behavior of pthread_mutex_trylock,
+ // which will return an error if the lock is already owned by the
+ // current thread. Since the primitive Win32 'TryEnterCriticalSection'
+ // treats this as a successful case, it changes the behavior of several
+ // tests in WebKit that check to see if the current thread already
+ // owned this mutex (see e.g., IconDatabase::getOrCreateIconRecord)
+ DWORD result = TryEnterCriticalSection(&mutex_.internal_mutex_);
+
+ if (result != 0) { // We got the lock
+ // If this thread already had the lock, we must unlock and return
+ // false since this is a non-recursive mutex. This is to mimic the
+ // behavior of POSIX's pthread_mutex_trylock. We don't do this
+ // check in the lock method (presumably due to performance?). This
+ // means lock() will succeed even if the current thread has already
+ // entered the critical section.
+ if (mutex_.recursion_count_ > 0) {
+ LeaveCriticalSection(&mutex_.internal_mutex_);
+ return false;
+ }
+ ++mutex_.recursion_count_;
+ return true;
+ }
+
+ return false;
+}
+
+bool RecursiveMutex::TryLock() {
+ // CRITICAL_SECTION is recursive/reentrant so TryEnterCriticalSection will
+ // succeed if the current thread is already in the critical section.
+ DWORD result = TryEnterCriticalSection(&mutex_.internal_mutex_);
+ if (result == 0) { // We didn't get the lock.
+ return false;
+ }
+ ++mutex_.recursion_count_;
+ return true;
+}
+
+ThreadCondition::ThreadCondition() {
+ InitializeConditionVariable(&condition_);
+}
+
+ThreadCondition::~ThreadCondition() {}
+
+void ThreadCondition::Wait(Mutex& mutex) {
+ PlatformMutex& platform_mutex = mutex.Impl();
+ BOOL result = SleepConditionVariableCS(
+ &condition_, &platform_mutex.internal_mutex_, INFINITE);
+ DCHECK_NE(result, 0);
+ ++platform_mutex.recursion_count_;
+}
+
+bool ThreadCondition::TimedWait(Mutex& mutex, double absolute_time) {
+ DWORD interval = AbsoluteTimeToWaitTimeoutInterval(absolute_time);
+
+ if (!interval) {
+ // Consider the wait to have timed out, even if our condition has already
+ // been signaled, to match the pthreads implementation.
+ return false;
+ }
+
+ PlatformMutex& platform_mutex = mutex.Impl();
+ BOOL result = SleepConditionVariableCS(
+ &condition_, &platform_mutex.internal_mutex_, interval);
+ ++platform_mutex.recursion_count_;
+
+ return result != 0;
+}
+
+void ThreadCondition::Signal() {
+ WakeConditionVariable(&condition_);
+}
+
+void ThreadCondition::Broadcast() {
+ WakeAllConditionVariable(&condition_);
+}
+
+DWORD AbsoluteTimeToWaitTimeoutInterval(double absolute_time) {
+ double current_time = WTF::CurrentTime();
+
+ // Time is in the past - return immediately.
+ if (absolute_time < current_time)
+ return 0;
+
+ // Time is too far in the future (and would overflow unsigned long) - wait
+ // forever.
+ if (absolute_time - current_time > static_cast<double>(INT_MAX) / 1000.0)
+ return INFINITE;
+
+ return static_cast<DWORD>((absolute_time - current_time) * 1000.0);
+}
+
+#if DCHECK_IS_ON()
+static bool g_thread_created = false;
+
+Mutex& GetThreadCreatedMutex() {
+ static Mutex g_thread_created_mutex;
+ return g_thread_created_mutex;
+}
+
+bool IsBeforeThreadCreated() {
+ MutexLocker locker(GetThreadCreatedMutex());
+ return !g_thread_created;
+}
+
+void WillCreateThread() {
+ MutexLocker locker(GetThreadCreatedMutex());
+ g_thread_created = true;
+}
+#endif
+
+} // namespace WTF
+
+#endif // defined(OS_WIN)
diff --git a/chromium/third_party/blink/renderer/platform/wtf/time.cc b/chromium/third_party/blink/renderer/platform/wtf/time.cc
new file mode 100644
index 00000000000..a296644c104
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/time.cc
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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 "third_party/blink/renderer/platform/wtf/time.h"
+
+#include "base/time/time.h"
+
+namespace WTF {
+
+static TimeFunction g_mock_time_function_for_testing = nullptr;
+
+double CurrentTime() {
+ if (g_mock_time_function_for_testing)
+ return g_mock_time_function_for_testing();
+ return base::Time::Now().ToDoubleT();
+}
+
+TimeFunction SetTimeFunctionsForTesting(TimeFunction new_function) {
+ TimeFunction old_function = g_mock_time_function_for_testing;
+ g_mock_time_function_for_testing = new_function;
+ return old_function;
+}
+
+TimeFunction GetTimeFunctionForTesting() {
+ return g_mock_time_function_for_testing;
+}
+
+TimeTicks CurrentTimeTicks() {
+ if (g_mock_time_function_for_testing) {
+ return base::TimeTicks() +
+ base::TimeDelta::FromSecondsD(g_mock_time_function_for_testing());
+ }
+ return base::TimeTicks::Now();
+}
+
+double CurrentTimeTicksInSeconds() {
+ return CurrentTimeTicks().since_origin().InSecondsF();
+}
+
+double CurrentTimeTicksInMilliseconds() {
+ return CurrentTimeTicks().since_origin().InMillisecondsF();
+}
+
+TimeTicks TimeTicksFromSeconds(double seconds) {
+ return TimeTicks() + TimeDelta::FromSecondsD(seconds);
+}
+
+double TimeTicksInSeconds(TimeTicks ticks) {
+ return (ticks - TimeTicks()).InSecondsF();
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/time.h b/chromium/third_party/blink/renderer/platform/wtf/time.h
new file mode 100644
index 00000000000..29d7643192c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/time.h
@@ -0,0 +1,75 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TIME_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TIME_H_
+
+#include "base/time/time.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_export.h"
+
+namespace WTF {
+// Provides thin wrappers around the following basic time types from
+// base/time package:
+//
+// - WTF::TimeDelta is an alias for base::TimeDelta and represents a duration
+// of time.
+// - WTF::TimeTicks wraps base::TimeTicks and represents a monotonic time
+// value.
+// - WTF::Time is an alias for base::Time and represents a wall time value.
+//
+// For usage guideline please see the documentation in base/time/time.h
+
+using TimeDelta = base::TimeDelta;
+using Time = base::Time;
+using TimeTicks = base::TimeTicks;
+
+// Returns the current UTC time in seconds, counted from January 1, 1970.
+// Precision varies depending on platform but is usually as good or better
+// than a millisecond.
+WTF_EXPORT double CurrentTime();
+
+// Same thing, in milliseconds.
+inline double CurrentTimeMS() {
+ return CurrentTime() * 1000.0;
+}
+
+using TimeFunction = double (*)();
+
+// Make all the time functions (currentTime(), monotonicallyIncreasingTime(),
+// systemTraceTime()) return the result of the supplied function. Returns the
+// pointer to the old time function. For both setting and getting, nullptr
+// means using the default timing function returning the actual time.
+WTF_EXPORT TimeFunction SetTimeFunctionsForTesting(TimeFunction);
+
+// Allows wtf/Time.h to use the same mock time function
+WTF_EXPORT TimeFunction GetTimeFunctionForTesting();
+
+// Monotonically increasing clock time since an arbitrary and unspecified origin
+// time. Mockable using SetTimeFunctionsForTesting().
+WTF_EXPORT TimeTicks CurrentTimeTicks();
+// Convenience functions that return seconds and milliseconds since the origin
+// time. Prefer CurrentTimeTicks() where possible to avoid potential unit
+// confusion errors.
+WTF_EXPORT double CurrentTimeTicksInSeconds();
+WTF_EXPORT double CurrentTimeTicksInMilliseconds();
+
+WTF_EXPORT TimeTicks TimeTicksFromSeconds(double);
+WTF_EXPORT double TimeTicksInSeconds(TimeTicks);
+
+} // namespace WTF
+
+using WTF::CurrentTime;
+using WTF::CurrentTimeMS;
+using WTF::CurrentTimeTicks;
+using WTF::CurrentTimeTicksInMilliseconds;
+using WTF::CurrentTimeTicksInSeconds;
+using WTF::SetTimeFunctionsForTesting;
+using WTF::Time;
+using WTF::TimeDelta;
+using WTF::TimeFunction;
+using WTF::TimeTicks;
+using WTF::TimeTicksFromSeconds;
+using WTF::TimeTicksInSeconds;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TIME_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/tree_node.h b/chromium/third_party/blink/renderer/platform/wtf/tree_node.h
new file mode 100644
index 00000000000..b25022a62b9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/tree_node.h
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TREE_NODE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TREE_NODE_H_
+
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+namespace WTF {
+
+//
+// TreeNode is generic, ContainerNode-like linked tree data structure.
+// There are a few notable difference between TreeNode and Node:
+//
+// * Each TreeNode node is NOT ref counted. The user have to retain its
+// lifetime somehow.
+// FIXME: lifetime management could be parameterized so that ref counted
+// implementations can be used.
+// * It checks invalid input. The callers have to ensure that given
+// parameter is sound.
+// * There is no branch-leaf difference. Every node can be a parent of other
+// node.
+//
+// FIXME: oilpan: Trace tree node edges to ensure we don't have dangling
+// pointers.
+// As it is used in HTMLImport it is safe since they all die together.
+template <class T>
+class TreeNode {
+ public:
+ typedef T NodeType;
+
+ TreeNode()
+ : next_(nullptr),
+ previous_(nullptr),
+ parent_(nullptr),
+ first_child_(nullptr),
+ last_child_(nullptr) {}
+
+ NodeType* Next() const { return next_; }
+ NodeType* Previous() const { return previous_; }
+ NodeType* Parent() const { return parent_; }
+ NodeType* FirstChild() const { return first_child_; }
+ NodeType* LastChild() const { return last_child_; }
+ NodeType* Here() const {
+ return static_cast<NodeType*>(const_cast<TreeNode*>(this));
+ }
+
+ bool Orphan() const {
+ return !parent_ && !next_ && !previous_ && !first_child_ && !last_child_;
+ }
+ bool HasChildren() const { return first_child_; }
+
+ void InsertBefore(NodeType* new_child, NodeType* ref_child) {
+ DCHECK(!new_child->Parent());
+ DCHECK(!new_child->Next());
+ DCHECK(!new_child->Previous());
+
+ DCHECK(!ref_child || this == ref_child->Parent());
+
+ if (!ref_child) {
+ AppendChild(new_child);
+ return;
+ }
+
+ NodeType* new_previous = ref_child->Previous();
+ new_child->parent_ = Here();
+ new_child->next_ = ref_child;
+ new_child->previous_ = new_previous;
+ ref_child->previous_ = new_child;
+ if (new_previous)
+ new_previous->next_ = new_child;
+ else
+ first_child_ = new_child;
+ }
+
+ void AppendChild(NodeType* child) {
+ DCHECK(!child->Parent());
+ DCHECK(!child->Next());
+ DCHECK(!child->Previous());
+
+ child->parent_ = Here();
+
+ if (!last_child_) {
+ DCHECK(!first_child_);
+ last_child_ = first_child_ = child;
+ return;
+ }
+
+ DCHECK(!last_child_->next_);
+ NodeType* old_last = last_child_;
+ last_child_ = child;
+
+ child->previous_ = old_last;
+ old_last->next_ = child;
+ }
+
+ NodeType* RemoveChild(NodeType* child) {
+ DCHECK_EQ(child->Parent(), this);
+
+ if (first_child_ == child)
+ first_child_ = child->Next();
+ if (last_child_ == child)
+ last_child_ = child->Previous();
+
+ NodeType* old_next = child->Next();
+ NodeType* old_previous = child->Previous();
+ child->parent_ = child->next_ = child->previous_ = nullptr;
+
+ if (old_next)
+ old_next->previous_ = old_previous;
+ if (old_previous)
+ old_previous->next_ = old_next;
+
+ return child;
+ }
+
+ void TakeChildrenFrom(NodeType* old_parent) {
+ DCHECK_NE(old_parent, this);
+ while (old_parent->HasChildren()) {
+ NodeType* child = old_parent->FirstChild();
+ old_parent->RemoveChild(child);
+ this->AppendChild(child);
+ }
+ }
+
+ private:
+ NodeType* next_;
+ NodeType* previous_;
+ NodeType* parent_;
+ NodeType* first_child_;
+ NodeType* last_child_;
+};
+
+template <class T>
+inline typename TreeNode<T>::NodeType* TraverseNext(
+ const TreeNode<T>* current,
+ const TreeNode<T>* stay_within = nullptr) {
+ if (typename TreeNode<T>::NodeType* next = current->FirstChild())
+ return next;
+ if (current == stay_within)
+ return nullptr;
+ if (typename TreeNode<T>::NodeType* next = current->Next())
+ return next;
+ for (typename TreeNode<T>::NodeType* parent = current->Parent(); parent;
+ parent = parent->Parent()) {
+ if (parent == stay_within)
+ return nullptr;
+ if (typename TreeNode<T>::NodeType* next = parent->Next())
+ return next;
+ }
+
+ return nullptr;
+}
+
+template <class T>
+inline typename TreeNode<T>::NodeType* TraverseFirstPostOrder(
+ const TreeNode<T>* current) {
+ typename TreeNode<T>::NodeType* first = current->Here();
+ while (first->FirstChild())
+ first = first->FirstChild();
+ return first;
+}
+
+template <class T>
+inline typename TreeNode<T>::NodeType* TraverseNextPostOrder(
+ const TreeNode<T>* current,
+ const TreeNode<T>* stay_within = nullptr) {
+ if (current == stay_within)
+ return nullptr;
+
+ typename TreeNode<T>::NodeType* next = current->Next();
+ if (!next)
+ return current->Parent();
+ while (next->FirstChild())
+ next = next->FirstChild();
+ return next;
+}
+
+} // namespace WTF
+
+using WTF::TreeNode;
+using WTF::TraverseNext;
+using WTF::TraverseNextPostOrder;
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/wtf/tree_node_test.cc b/chromium/third_party/blink/renderer/platform/wtf/tree_node_test.cc
new file mode 100644
index 00000000000..da43be7a339
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/tree_node_test.cc
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2012 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/wtf/tree_node.h"
+
+#include "base/memory/scoped_refptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+
+namespace WTF {
+
+class TestTree : public RefCounted<TestTree>, public TreeNode<TestTree> {
+ public:
+ static scoped_refptr<TestTree> Create() {
+ return base::AdoptRef(new TestTree());
+ }
+};
+
+TEST(TreeNodeTest, AppendChild) {
+ scoped_refptr<TestTree> root = TestTree::Create();
+ scoped_refptr<TestTree> first_child = TestTree::Create();
+ scoped_refptr<TestTree> last_child = TestTree::Create();
+
+ root->AppendChild(first_child.get());
+ EXPECT_EQ(root->FirstChild(), first_child.get());
+ EXPECT_EQ(root->LastChild(), first_child.get());
+ EXPECT_EQ(first_child->Parent(), root.get());
+
+ root->AppendChild(last_child.get());
+ EXPECT_EQ(root->FirstChild(), first_child.get());
+ EXPECT_EQ(root->LastChild(), last_child.get());
+ EXPECT_EQ(last_child->Previous(), first_child.get());
+ EXPECT_EQ(first_child->Next(), last_child.get());
+ EXPECT_EQ(last_child->Parent(), root.get());
+}
+
+TEST(TreeNodeTest, InsertBefore) {
+ scoped_refptr<TestTree> root = TestTree::Create();
+ scoped_refptr<TestTree> first_child = TestTree::Create();
+ scoped_refptr<TestTree> middle_child = TestTree::Create();
+ scoped_refptr<TestTree> last_child = TestTree::Create();
+
+ // Inserting single node
+ root->InsertBefore(last_child.get(), nullptr);
+ EXPECT_EQ(last_child->Parent(), root.get());
+ EXPECT_EQ(root->FirstChild(), last_child.get());
+ EXPECT_EQ(root->LastChild(), last_child.get());
+
+ // Then prepend
+ root->InsertBefore(first_child.get(), last_child.get());
+ EXPECT_EQ(first_child->Parent(), root.get());
+ EXPECT_EQ(root->FirstChild(), first_child.get());
+ EXPECT_EQ(root->LastChild(), last_child.get());
+ EXPECT_EQ(first_child->Next(), last_child.get());
+ EXPECT_EQ(first_child.get(), last_child->Previous());
+
+ // Inserting in the middle
+ root->InsertBefore(middle_child.get(), last_child.get());
+ EXPECT_EQ(middle_child->Parent(), root.get());
+ EXPECT_EQ(root->FirstChild(), first_child.get());
+ EXPECT_EQ(root->LastChild(), last_child.get());
+ EXPECT_EQ(middle_child->Previous(), first_child.get());
+ EXPECT_EQ(middle_child->Next(), last_child.get());
+ EXPECT_EQ(first_child->Next(), middle_child.get());
+ EXPECT_EQ(last_child->Previous(), middle_child.get());
+}
+
+TEST(TreeNodeTest, RemoveSingle) {
+ scoped_refptr<TestTree> root = TestTree::Create();
+ scoped_refptr<TestTree> child = TestTree::Create();
+ scoped_refptr<TestTree> null_node;
+
+ root->AppendChild(child.get());
+ root->RemoveChild(child.get());
+ EXPECT_EQ(child->Next(), null_node.get());
+ EXPECT_EQ(child->Previous(), null_node.get());
+ EXPECT_EQ(child->Parent(), null_node.get());
+ EXPECT_EQ(root->FirstChild(), null_node.get());
+ EXPECT_EQ(root->LastChild(), null_node.get());
+}
+
+class Trio {
+ public:
+ Trio()
+ : root(TestTree::Create()),
+ first_child(TestTree::Create()),
+ middle_child(TestTree::Create()),
+ last_child(TestTree::Create()) {}
+
+ void AppendChildren() {
+ root->AppendChild(first_child.get());
+ root->AppendChild(middle_child.get());
+ root->AppendChild(last_child.get());
+ }
+
+ scoped_refptr<TestTree> root;
+ scoped_refptr<TestTree> first_child;
+ scoped_refptr<TestTree> middle_child;
+ scoped_refptr<TestTree> last_child;
+};
+
+TEST(TreeNodeTest, RemoveMiddle) {
+ Trio trio;
+ trio.AppendChildren();
+
+ trio.root->RemoveChild(trio.middle_child.get());
+ EXPECT_TRUE(trio.middle_child->Orphan());
+ EXPECT_EQ(trio.first_child->Next(), trio.last_child.get());
+ EXPECT_EQ(trio.last_child->Previous(), trio.first_child.get());
+ EXPECT_EQ(trio.root->FirstChild(), trio.first_child.get());
+ EXPECT_EQ(trio.root->LastChild(), trio.last_child.get());
+}
+
+TEST(TreeNodeTest, RemoveLast) {
+ scoped_refptr<TestTree> null_node;
+ Trio trio;
+ trio.AppendChildren();
+
+ trio.root->RemoveChild(trio.last_child.get());
+ EXPECT_TRUE(trio.last_child->Orphan());
+ EXPECT_EQ(trio.middle_child->Next(), null_node.get());
+ EXPECT_EQ(trio.root->FirstChild(), trio.first_child.get());
+ EXPECT_EQ(trio.root->LastChild(), trio.middle_child.get());
+}
+
+TEST(TreeNodeTest, RemoveFirst) {
+ scoped_refptr<TestTree> null_node;
+ Trio trio;
+ trio.AppendChildren();
+
+ trio.root->RemoveChild(trio.first_child.get());
+ EXPECT_TRUE(trio.first_child->Orphan());
+ EXPECT_EQ(trio.middle_child->Previous(), null_node.get());
+ EXPECT_EQ(trio.root->FirstChild(), trio.middle_child.get());
+ EXPECT_EQ(trio.root->LastChild(), trio.last_child.get());
+}
+
+TEST(TreeNodeTest, TakeChildrenFrom) {
+ scoped_refptr<TestTree> new_parent = TestTree::Create();
+ Trio trio;
+ trio.AppendChildren();
+
+ new_parent->TakeChildrenFrom(trio.root.get());
+
+ EXPECT_FALSE(trio.root->HasChildren());
+ EXPECT_TRUE(new_parent->HasChildren());
+ EXPECT_EQ(trio.first_child.get(), new_parent->FirstChild());
+ EXPECT_EQ(trio.middle_child.get(), new_parent->FirstChild()->Next());
+ EXPECT_EQ(trio.last_child.get(), new_parent->LastChild());
+}
+
+class TrioWithGrandChild : public Trio {
+ public:
+ TrioWithGrandChild() : grand_child(TestTree::Create()) {}
+
+ void AppendChildren() {
+ Trio::AppendChildren();
+ middle_child->AppendChild(grand_child.get());
+ }
+
+ scoped_refptr<TestTree> grand_child;
+};
+
+TEST(TreeNodeTest, TraverseNext) {
+ TrioWithGrandChild trio;
+ trio.AppendChildren();
+
+ TestTree* order[] = {trio.root.get(), trio.first_child.get(),
+ trio.middle_child.get(), trio.grand_child.get(),
+ trio.last_child.get()};
+
+ unsigned order_index = 0;
+ for (TestTree *node = trio.root.get(); node;
+ node = TraverseNext(node), order_index++)
+ EXPECT_EQ(node, order[order_index]);
+ EXPECT_EQ(order_index, sizeof(order) / sizeof(TestTree*));
+}
+
+TEST(TreeNodeTest, TraverseNextPostORder) {
+ TrioWithGrandChild trio;
+ trio.AppendChildren();
+
+ TestTree* order[] = {trio.first_child.get(), trio.grand_child.get(),
+ trio.middle_child.get(), trio.last_child.get(),
+ trio.root.get()};
+
+ unsigned order_index = 0;
+ for (TestTree *node = TraverseFirstPostOrder(trio.root.get()); node;
+ node = TraverseNextPostOrder(node), order_index++)
+ EXPECT_EQ(node, order[order_index]);
+ EXPECT_EQ(order_index, sizeof(order) / sizeof(TestTree*));
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/type_traits.h b/chromium/third_party/blink/renderer/platform/wtf/type_traits.h
new file mode 100644
index 00000000000..7fd0ecc9cb9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/type_traits.h
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2009, 2010 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPE_TRAITS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPE_TRAITS_H_
+
+#include <cstddef>
+#include <type_traits>
+#include <utility>
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/wtf/compiler.h"
+
+namespace WTF {
+
+// Returns a string that contains the type name of |T| as a substring.
+template <typename T>
+inline const char* GetStringWithTypeName() {
+ return WTF_PRETTY_FUNCTION;
+}
+
+template <typename T>
+struct IsWeak {
+ static const bool value = false;
+};
+
+enum WeakHandlingFlag {
+ kNoWeakHandling,
+ kWeakHandling,
+};
+
+template <typename T>
+struct IsTriviallyDestructible {
+ // TODO(slangley): crbug.com/783060 - std::is_trivially_destructible behaves
+ // differently on across platforms.
+ static constexpr bool value =
+ __has_trivial_destructor(T) && std::is_destructible<T>::value;
+};
+
+template <typename T, typename U>
+struct IsSubclass {
+ private:
+ typedef char YesType;
+ struct NoType {
+ char padding[8];
+ };
+
+ static YesType SubclassCheck(U*);
+ static NoType SubclassCheck(...);
+ static T* t_;
+
+ public:
+ static const bool value = sizeof(SubclassCheck(t_)) == sizeof(YesType);
+};
+
+template <typename T, template <typename... V> class U>
+struct IsSubclassOfTemplate {
+ private:
+ typedef char YesType;
+ struct NoType {
+ char padding[8];
+ };
+
+ template <typename... W>
+ static YesType SubclassCheck(U<W...>*);
+ static NoType SubclassCheck(...);
+ static T* t_;
+
+ public:
+ static const bool value = sizeof(SubclassCheck(t_)) == sizeof(YesType);
+};
+
+template <typename T, template <typename V, size_t W> class U>
+struct IsSubclassOfTemplateTypenameSize {
+ private:
+ typedef char YesType;
+ struct NoType {
+ char padding[8];
+ };
+
+ template <typename X, size_t Y>
+ static YesType SubclassCheck(U<X, Y>*);
+ static NoType SubclassCheck(...);
+ static T* t_;
+
+ public:
+ static const bool value = sizeof(SubclassCheck(t_)) == sizeof(YesType);
+};
+
+template <typename T, template <typename V, size_t W, typename X> class U>
+struct IsSubclassOfTemplateTypenameSizeTypename {
+ private:
+ typedef char YesType;
+ struct NoType {
+ char padding[8];
+ };
+
+ template <typename Y, size_t Z, typename A>
+ static YesType SubclassCheck(U<Y, Z, A>*);
+ static NoType SubclassCheck(...);
+ static T* t_;
+
+ public:
+ static const bool value = sizeof(SubclassCheck(t_)) == sizeof(YesType);
+};
+
+} // namespace WTF
+
+namespace blink {
+
+class Visitor;
+
+} // namespace blink
+
+namespace WTF {
+
+template <typename T>
+class IsTraceable {
+ typedef char YesType;
+ typedef struct NoType { char padding[8]; } NoType;
+
+ // Note that this also checks if a superclass of V has a trace method.
+ template <typename V>
+ static YesType CheckHasTraceMethod(
+ V* v,
+ blink::Visitor* p = nullptr,
+ typename std::enable_if<
+ std::is_same<decltype(v->Trace(p)), void>::value>::type* g = nullptr);
+ template <typename V>
+ static NoType CheckHasTraceMethod(...);
+
+ public:
+ // We add sizeof(T) to both sides here, because we want it to fail for
+ // incomplete types. Otherwise it just assumes that incomplete types do not
+ // have a trace method, which may not be true.
+ static const bool value = sizeof(YesType) + sizeof(T) ==
+ sizeof(CheckHasTraceMethod<T>(nullptr)) + sizeof(T);
+};
+
+// Convenience template wrapping the IsTraceableInCollection template in
+// Collection Traits. It helps make the code more readable.
+template <typename Traits>
+class IsTraceableInCollectionTrait {
+ public:
+ static const bool value = Traits::template IsTraceableInCollection<>::value;
+};
+
+template <typename T, typename U>
+struct IsTraceable<std::pair<T, U>> {
+ static const bool value = IsTraceable<T>::value || IsTraceable<U>::value;
+};
+
+// This is used to check that DISALLOW_NEW_EXCEPT_PLACEMENT_NEW objects are not
+// stored in off-heap Vectors, HashTables etc.
+template <typename T>
+struct AllowsOnlyPlacementNew {
+ private:
+ using YesType = char;
+ struct NoType {
+ char padding[8];
+ };
+
+ template <typename U>
+ static YesType CheckMarker(typename U::IsAllowOnlyPlacementNew*);
+ template <typename U>
+ static NoType CheckMarker(...);
+
+ public:
+ static const bool value = sizeof(CheckMarker<T>(nullptr)) == sizeof(YesType);
+};
+
+template <typename T>
+class IsGarbageCollectedType {
+ typedef char YesType;
+ typedef struct NoType { char padding[8]; } NoType;
+
+ static_assert(sizeof(T), "T must be fully defined");
+
+ using NonConstType = typename std::remove_const<T>::type;
+ template <typename U>
+ static YesType CheckGarbageCollectedType(
+ typename U::IsGarbageCollectedTypeMarker*);
+ template <typename U>
+ static NoType CheckGarbageCollectedType(...);
+
+ // Separately check for GarbageCollectedMixin, which declares a different
+ // marker typedef, to avoid resolution ambiguity for cases like
+ // IsGarbageCollectedType<B> over:
+ //
+ // class A : public GarbageCollected<A>, public GarbageCollectedMixin {
+ // USING_GARBAGE_COLLECTED_MIXIN(A);
+ // ...
+ // };
+ // class B : public A, public GarbageCollectedMixin { ... };
+ //
+ template <typename U>
+ static YesType CheckGarbageCollectedMixinType(
+ typename U::IsGarbageCollectedMixinMarker*);
+ template <typename U>
+ static NoType CheckGarbageCollectedMixinType(...);
+
+ public:
+ static const bool value =
+ (sizeof(YesType) ==
+ sizeof(CheckGarbageCollectedType<NonConstType>(nullptr))) ||
+ (sizeof(YesType) ==
+ sizeof(CheckGarbageCollectedMixinType<NonConstType>(nullptr)));
+};
+
+template <>
+class IsGarbageCollectedType<void> {
+ public:
+ static const bool value = false;
+};
+
+template <typename T>
+class IsPersistentReferenceType {
+ typedef char YesType;
+ typedef struct NoType { char padding[8]; } NoType;
+
+ template <typename U>
+ static YesType CheckPersistentReferenceType(
+ typename U::IsPersistentReferenceTypeMarker*);
+ template <typename U>
+ static NoType CheckPersistentReferenceType(...);
+
+ public:
+ static const bool value =
+ (sizeof(YesType) == sizeof(CheckPersistentReferenceType<T>(nullptr)));
+};
+
+template <typename T,
+ bool = std::is_function<typename std::remove_const<
+ typename std::remove_pointer<T>::type>::type>::value ||
+ std::is_void<typename std::remove_const<
+ typename std::remove_pointer<T>::type>::type>::value>
+class IsPointerToGarbageCollectedType {
+ public:
+ static const bool value = false;
+};
+
+template <typename T>
+class IsPointerToGarbageCollectedType<T*, false> {
+ public:
+ static const bool value = IsGarbageCollectedType<T>::value;
+};
+
+} // namespace WTF
+
+using WTF::IsGarbageCollectedType;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPE_TRAITS_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/type_traits_test.cc b/chromium/third_party/blink/renderer/platform/wtf/type_traits_test.cc
new file mode 100644
index 00000000000..ced43d3258e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/type_traits_test.cc
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2009, 2010 Google Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "third_party/blink/renderer/platform/wtf/type_traits.h"
+
+#include "base/macros.h"
+#include "build/build_config.h"
+
+// No gtest tests; only static_assert checks.
+
+namespace WTF {
+
+namespace {
+
+struct VirtualClass {
+ virtual void A() {}
+};
+static_assert(!std::is_trivially_move_assignable<VirtualClass>::value,
+ "VirtualClass should not be trivially move assignable");
+
+struct DestructorClass {
+ ~DestructorClass() = default;
+};
+static_assert(std::is_trivially_move_assignable<DestructorClass>::value,
+ "DestructorClass should be trivially move assignable");
+static_assert(std::is_trivially_copy_assignable<DestructorClass>::value,
+ "DestructorClass should be trivially copy assignable");
+static_assert(std::is_default_constructible<DestructorClass>::value,
+ "DestructorClass should be default constructible");
+
+struct MixedPrivate {
+ int M2() { return m2; }
+ int m1;
+
+ private:
+ int m2;
+};
+static_assert(std::is_trivially_move_assignable<MixedPrivate>::value,
+ "MixedPrivate should be trivially move assignable");
+static_assert(std::is_trivially_copy_assignable<MixedPrivate>::value,
+ "MixedPrivate should be trivially copy assignable");
+static_assert(std::is_trivially_default_constructible<MixedPrivate>::value,
+ "MixedPrivate should have a trivial default constructor");
+struct JustPrivate {
+ int M2() { return m2; }
+
+ private:
+ int m2;
+};
+static_assert(std::is_trivially_move_assignable<JustPrivate>::value,
+ "JustPrivate should be trivially move assignable");
+static_assert(std::is_trivially_copy_assignable<JustPrivate>::value,
+ "JustPrivate should be trivially copy assignable");
+static_assert(std::is_trivially_default_constructible<JustPrivate>::value,
+ "JustPrivate should have a trivial default constructor");
+struct JustPublic {
+ int m2;
+};
+static_assert(std::is_trivially_move_assignable<JustPublic>::value,
+ "JustPublic should be trivially move assignable");
+static_assert(std::is_trivially_copy_assignable<JustPublic>::value,
+ "JustPublic should be trivially copy assignable");
+static_assert(std::is_trivially_default_constructible<JustPublic>::value,
+ "JustPublic should have a trivial default constructor");
+struct NestedInherited : public JustPublic, JustPrivate {
+ float m3;
+};
+static_assert(std::is_trivially_move_assignable<NestedInherited>::value,
+ "NestedInherited should be trivially move assignable");
+static_assert(std::is_trivially_copy_assignable<NestedInherited>::value,
+ "NestedInherited should be trivially copy assignable");
+static_assert(std::is_trivially_default_constructible<NestedInherited>::value,
+ "NestedInherited should have a trivial default constructor");
+struct NestedOwned {
+ JustPublic m1;
+ JustPrivate m2;
+ float m3;
+};
+
+static_assert(std::is_trivially_move_assignable<NestedOwned>::value,
+ "NestedOwned should be trivially move assignable");
+static_assert(std::is_trivially_copy_assignable<NestedOwned>::value,
+ "NestedOwned should be trivially copy assignable");
+static_assert(std::is_trivially_default_constructible<NestedOwned>::value,
+ "NestedOwned should have a trivial default constructor");
+
+class NonCopyableClass {
+ DISALLOW_COPY_AND_ASSIGN(NonCopyableClass);
+};
+#if 0 // Compilers don't get this "right" yet if using = delete.
+static_assert(!IsTriviallyMoveAssignable<NonCopyableClass>::value, "NonCopyableClass should not be trivially move assignable");
+static_assert(!IsTriviallyCopyAssignable<NonCopyableClass>::value, "NonCopyableClass should not be trivially copy assignable");
+static_assert(IsTriviallyDefaultConstructible<NonCopyableClass>::value, "NonCopyableClass should have a trivial default constructor");
+#endif // 0
+
+template <typename T>
+class TestBaseClass {};
+
+class TestDerivedClass : public TestBaseClass<int> {};
+
+static_assert((IsSubclass<TestDerivedClass, TestBaseClass<int>>::value),
+ "Derived class should be a subclass of its base");
+static_assert((!IsSubclass<TestBaseClass<int>, TestDerivedClass>::value),
+ "Base class should not be a sublass of a derived class");
+static_assert((IsSubclassOfTemplate<TestDerivedClass, TestBaseClass>::value),
+ "Derived class should be a subclass of template from its base");
+
+typedef int IntArray[];
+typedef int IntArraySized[4];
+
+#if !defined(COMPILER_MSVC) || defined(__clang__)
+
+class AssignmentDeleted final {
+ private:
+ AssignmentDeleted& operator=(const AssignmentDeleted&) = delete;
+};
+
+static_assert(!std::is_copy_assignable<AssignmentDeleted>::value,
+ "AssignmentDeleted isn't copy assignable.");
+static_assert(!std::is_move_assignable<AssignmentDeleted>::value,
+ "AssignmentDeleted isn't move assignable.");
+
+class AssignmentPrivate final {
+ private:
+ AssignmentPrivate& operator=(const AssignmentPrivate&);
+};
+
+static_assert(!std::is_copy_assignable<AssignmentPrivate>::value,
+ "AssignmentPrivate isn't copy assignable.");
+static_assert(!std::is_move_assignable<AssignmentPrivate>::value,
+ "AssignmentPrivate isn't move assignable.");
+
+class CopyAssignmentDeleted final {
+ public:
+ CopyAssignmentDeleted& operator=(CopyAssignmentDeleted&&);
+
+ private:
+ CopyAssignmentDeleted& operator=(const CopyAssignmentDeleted&) = delete;
+};
+
+static_assert(!std::is_copy_assignable<CopyAssignmentDeleted>::value,
+ "CopyAssignmentDeleted isn't copy assignable.");
+static_assert(std::is_move_assignable<CopyAssignmentDeleted>::value,
+ "CopyAssignmentDeleted is move assignable.");
+
+class CopyAssignmentPrivate final {
+ public:
+ CopyAssignmentPrivate& operator=(CopyAssignmentPrivate&&);
+
+ private:
+ CopyAssignmentPrivate& operator=(const CopyAssignmentPrivate&);
+};
+
+static_assert(!std::is_copy_assignable<CopyAssignmentPrivate>::value,
+ "CopyAssignmentPrivate isn't copy assignable.");
+static_assert(std::is_move_assignable<CopyAssignmentPrivate>::value,
+ "CopyAssignmentPrivate is move assignable.");
+
+class CopyAssignmentUndeclared final {
+ public:
+ CopyAssignmentUndeclared& operator=(CopyAssignmentUndeclared&&);
+};
+
+static_assert(!std::is_copy_assignable<CopyAssignmentUndeclared>::value,
+ "CopyAssignmentUndeclared isn't copy assignable.");
+static_assert(std::is_move_assignable<CopyAssignmentUndeclared>::value,
+ "CopyAssignmentUndeclared is move assignable.");
+
+class Assignable final {
+ public:
+ Assignable& operator=(const Assignable&);
+};
+
+static_assert(std::is_copy_assignable<Assignable>::value,
+ "Assignable is copy assignable.");
+static_assert(std::is_move_assignable<Assignable>::value,
+ "Assignable is move assignable.");
+
+class AssignableImplicit final {};
+
+static_assert(std::is_copy_assignable<AssignableImplicit>::value,
+ "AssignableImplicit is copy assignable.");
+static_assert(std::is_move_assignable<AssignableImplicit>::value,
+ "AssignableImplicit is move assignable.");
+
+#endif // !defined(COMPILER_MSVC) || defined(__clang__)
+
+class DefaultConstructorDeleted final {
+ private:
+ DefaultConstructorDeleted() = delete;
+};
+
+class DestructorDeleted final {
+ private:
+ ~DestructorDeleted() = delete;
+};
+
+static_assert(
+ !std::is_trivially_default_constructible<DefaultConstructorDeleted>::value,
+ "DefaultConstructorDeleted must not be trivially default constructible.");
+
+static_assert(!IsTriviallyDestructible<DestructorDeleted>::value,
+ "DestructorDeleted must not be trivially destructible.");
+
+#define EnsurePtrConvertibleArgDecl(From, To) \
+ typename std::enable_if<std::is_convertible<From*, To*>::value>::type* = \
+ nullptr
+
+template <typename T>
+class Wrapper {
+ public:
+ template <typename U>
+ Wrapper(const Wrapper<U>&, EnsurePtrConvertibleArgDecl(U, T)) {}
+};
+
+class ForwardDeclarationOnlyClass;
+
+static_assert(std::is_convertible<Wrapper<TestDerivedClass>,
+ Wrapper<TestDerivedClass>>::value,
+ "EnsurePtrConvertibleArgDecl<T, T> should pass");
+
+static_assert(std::is_convertible<Wrapper<TestDerivedClass>,
+ Wrapper<const TestDerivedClass>>::value,
+ "EnsurePtrConvertibleArgDecl<T, const T> should pass");
+
+static_assert(!std::is_convertible<Wrapper<const TestDerivedClass>,
+ Wrapper<TestDerivedClass>>::value,
+ "EnsurePtrConvertibleArgDecl<const T, T> should not pass");
+
+static_assert(std::is_convertible<Wrapper<ForwardDeclarationOnlyClass>,
+ Wrapper<ForwardDeclarationOnlyClass>>::value,
+ "EnsurePtrConvertibleArgDecl<T, T> should pass if T is not a "
+ "complete type");
+
+static_assert(
+ std::is_convertible<Wrapper<ForwardDeclarationOnlyClass>,
+ Wrapper<const ForwardDeclarationOnlyClass>>::value,
+ "EnsurePtrConvertibleArgDecl<T, const T> should pass if T is not a "
+ "complete type");
+
+static_assert(!std::is_convertible<Wrapper<const ForwardDeclarationOnlyClass>,
+ Wrapper<ForwardDeclarationOnlyClass>>::value,
+ "EnsurePtrConvertibleArgDecl<const T, T> should not pass if T is "
+ "not a complete type");
+
+static_assert(
+ std::is_convertible<Wrapper<TestDerivedClass>,
+ Wrapper<TestBaseClass<int>>>::value,
+ "EnsurePtrConvertibleArgDecl<U, T> should pass if U is a subclass of T");
+
+static_assert(!std::is_convertible<Wrapper<TestBaseClass<int>>,
+ Wrapper<TestDerivedClass>>::value,
+ "EnsurePtrConvertibleArgDecl<U, T> should not pass if U is a "
+ "base class of T");
+
+} // anonymous namespace
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer.cc b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer.cc
new file mode 100644
index 00000000000..3a92200dd9a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer.cc
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2009 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer.h"
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_view.h"
+
+namespace WTF {
+
+bool ArrayBuffer::Transfer(ArrayBufferContents& result) {
+ DCHECK(!IsShared());
+ scoped_refptr<ArrayBuffer> keep_alive(this);
+
+ if (!contents_.Data()) {
+ result.Neuter();
+ return false;
+ }
+
+ bool all_views_are_neuterable = true;
+ for (ArrayBufferView* i = first_view_; i; i = i->next_view_) {
+ if (!i->IsNeuterable())
+ all_views_are_neuterable = false;
+ }
+
+ if (all_views_are_neuterable) {
+ contents_.Transfer(result);
+
+ while (first_view_) {
+ ArrayBufferView* current = first_view_;
+ RemoveView(current);
+ current->Neuter();
+ }
+
+ is_neutered_ = true;
+ } else {
+ // TODO(https://crbug.com/763038): See original bug at
+ // https://crbug.com/254728. Copying the buffer instead of transferring is
+ // not spec compliant but was added for a WebAudio bug fix. The only time
+ // this branch is taken is when attempting to transfer an AudioBuffer's
+ // channel data ArrayBuffer.
+ contents_.CopyTo(result);
+ if (!result.Data())
+ return false;
+ }
+
+ return true;
+}
+
+bool ArrayBuffer::ShareContentsWith(ArrayBufferContents& result) {
+ DCHECK(IsShared());
+ scoped_refptr<ArrayBuffer> keep_alive(this);
+
+ if (!contents_.DataShared()) {
+ result.Neuter();
+ return false;
+ }
+
+ contents_.ShareWith(result);
+ return true;
+}
+
+void ArrayBuffer::AddView(ArrayBufferView* view) {
+ view->buffer_ = this;
+ view->prev_view_ = nullptr;
+ view->next_view_ = first_view_;
+ if (first_view_)
+ first_view_->prev_view_ = view;
+ first_view_ = view;
+}
+
+void ArrayBuffer::RemoveView(ArrayBufferView* view) {
+ DCHECK_EQ(this, view->buffer_.get());
+ if (view->next_view_)
+ view->next_view_->prev_view_ = view->prev_view_;
+ if (view->prev_view_)
+ view->prev_view_->next_view_ = view->next_view_;
+ if (first_view_ == view)
+ first_view_ = view->next_view_;
+ view->prev_view_ = view->next_view_ = nullptr;
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer.h b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer.h
new file mode 100644
index 00000000000..7f3bb0b902a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer.h
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2009 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_ARRAY_BUFFER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_ARRAY_BUFFER_H_
+
+#include "base/allocator/partition_allocator/oom.h"
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_contents.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_export.h"
+
+namespace WTF {
+
+class ArrayBuffer;
+class ArrayBufferView;
+
+class WTF_EXPORT ArrayBuffer : public RefCounted<ArrayBuffer> {
+ public:
+ static inline scoped_refptr<ArrayBuffer> Create(unsigned num_elements,
+ unsigned element_byte_size);
+ static inline scoped_refptr<ArrayBuffer> Create(ArrayBuffer*);
+ static inline scoped_refptr<ArrayBuffer> Create(const void* source,
+ unsigned byte_length);
+ static inline scoped_refptr<ArrayBuffer> Create(ArrayBufferContents&);
+
+ static inline scoped_refptr<ArrayBuffer> CreateOrNull(
+ unsigned num_elements,
+ unsigned element_byte_size);
+
+ // Only for use by DOMArrayBuffer::CreateUninitializedOrNull().
+ static inline scoped_refptr<ArrayBuffer> CreateUninitializedOrNull(
+ unsigned num_elements,
+ unsigned element_byte_size);
+
+ static inline scoped_refptr<ArrayBuffer> CreateShared(
+ unsigned num_elements,
+ unsigned element_byte_size);
+ static inline scoped_refptr<ArrayBuffer> CreateShared(const void* source,
+ unsigned byte_length);
+
+ inline void* Data();
+ inline const void* Data() const;
+ inline void* DataShared();
+ inline const void* DataShared() const;
+ inline void* DataMaybeShared();
+ inline const void* DataMaybeShared() const;
+ inline unsigned ByteLength() const;
+
+ // Creates a new ArrayBuffer object with copy of bytes in this object
+ // ranging from |begin| upto but not including |end|.
+ inline scoped_refptr<ArrayBuffer> Slice(int begin, int end) const;
+ inline scoped_refptr<ArrayBuffer> Slice(int begin) const;
+
+ void AddView(ArrayBufferView*);
+ void RemoveView(ArrayBufferView*);
+
+ bool Transfer(ArrayBufferContents&);
+ bool ShareContentsWith(ArrayBufferContents&);
+ bool IsNeutered() const { return is_neutered_; }
+ bool IsShared() const { return contents_.IsShared(); }
+
+ ~ArrayBuffer() = default;
+
+ protected:
+ inline explicit ArrayBuffer(ArrayBufferContents&);
+
+ private:
+ static inline scoped_refptr<ArrayBuffer> Create(
+ unsigned num_elements,
+ unsigned element_byte_size,
+ ArrayBufferContents::InitializationPolicy);
+ static inline scoped_refptr<ArrayBuffer> CreateOrNull(
+ unsigned num_elements,
+ unsigned element_byte_size,
+ ArrayBufferContents::InitializationPolicy);
+ static inline scoped_refptr<ArrayBuffer> CreateShared(
+ unsigned num_elements,
+ unsigned element_byte_size,
+ ArrayBufferContents::InitializationPolicy);
+
+ inline scoped_refptr<ArrayBuffer> SliceImpl(unsigned begin,
+ unsigned end) const;
+ inline unsigned ClampIndex(int index) const;
+ static inline int ClampValue(int x, int left, int right);
+
+ ArrayBufferContents contents_;
+ ArrayBufferView* first_view_;
+ bool is_neutered_;
+};
+
+int ArrayBuffer::ClampValue(int x, int left, int right) {
+ DCHECK_LE(left, right);
+ if (x < left)
+ x = left;
+ if (right < x)
+ x = right;
+ return x;
+}
+
+scoped_refptr<ArrayBuffer> ArrayBuffer::Create(unsigned num_elements,
+ unsigned element_byte_size) {
+ return Create(num_elements, element_byte_size,
+ ArrayBufferContents::kZeroInitialize);
+}
+
+scoped_refptr<ArrayBuffer> ArrayBuffer::Create(ArrayBuffer* other) {
+ // TODO(binji): support creating a SharedArrayBuffer by copying another
+ // ArrayBuffer?
+ DCHECK(!other->IsShared());
+ return ArrayBuffer::Create(other->Data(), other->ByteLength());
+}
+
+scoped_refptr<ArrayBuffer> ArrayBuffer::Create(const void* source,
+ unsigned byte_length) {
+ ArrayBufferContents contents(byte_length, 1, ArrayBufferContents::kNotShared,
+ ArrayBufferContents::kDontInitialize);
+ if (UNLIKELY(!contents.Data()))
+ OOM_CRASH();
+ scoped_refptr<ArrayBuffer> buffer = base::AdoptRef(new ArrayBuffer(contents));
+ memcpy(buffer->Data(), source, byte_length);
+ return buffer;
+}
+
+scoped_refptr<ArrayBuffer> ArrayBuffer::Create(ArrayBufferContents& contents) {
+ CHECK(contents.DataMaybeShared());
+ return base::AdoptRef(new ArrayBuffer(contents));
+}
+
+scoped_refptr<ArrayBuffer> ArrayBuffer::CreateOrNull(
+ unsigned num_elements,
+ unsigned element_byte_size) {
+ return CreateOrNull(num_elements, element_byte_size,
+ ArrayBufferContents::kZeroInitialize);
+}
+
+scoped_refptr<ArrayBuffer> ArrayBuffer::CreateUninitializedOrNull(
+ unsigned num_elements,
+ unsigned element_byte_size) {
+ return CreateOrNull(num_elements, element_byte_size,
+ ArrayBufferContents::kDontInitialize);
+}
+
+scoped_refptr<ArrayBuffer> ArrayBuffer::Create(
+ unsigned num_elements,
+ unsigned element_byte_size,
+ ArrayBufferContents::InitializationPolicy policy) {
+ ArrayBufferContents contents(num_elements, element_byte_size,
+ ArrayBufferContents::kNotShared, policy);
+ if (UNLIKELY(!contents.Data()))
+ OOM_CRASH();
+ return base::AdoptRef(new ArrayBuffer(contents));
+}
+
+scoped_refptr<ArrayBuffer> ArrayBuffer::CreateOrNull(
+ unsigned num_elements,
+ unsigned element_byte_size,
+ ArrayBufferContents::InitializationPolicy policy) {
+ ArrayBufferContents contents(num_elements, element_byte_size,
+ ArrayBufferContents::kNotShared, policy);
+ if (!contents.Data())
+ return nullptr;
+ return base::AdoptRef(new ArrayBuffer(contents));
+}
+
+scoped_refptr<ArrayBuffer> ArrayBuffer::CreateShared(
+ unsigned num_elements,
+ unsigned element_byte_size) {
+ return CreateShared(num_elements, element_byte_size,
+ ArrayBufferContents::kZeroInitialize);
+}
+
+scoped_refptr<ArrayBuffer> ArrayBuffer::CreateShared(const void* source,
+ unsigned byte_length) {
+ ArrayBufferContents contents(byte_length, 1, ArrayBufferContents::kShared,
+ ArrayBufferContents::kDontInitialize);
+ CHECK(contents.DataShared());
+ scoped_refptr<ArrayBuffer> buffer = base::AdoptRef(new ArrayBuffer(contents));
+ memcpy(buffer->DataShared(), source, byte_length);
+ return buffer;
+}
+
+scoped_refptr<ArrayBuffer> ArrayBuffer::CreateShared(
+ unsigned num_elements,
+ unsigned element_byte_size,
+ ArrayBufferContents::InitializationPolicy policy) {
+ ArrayBufferContents contents(num_elements, element_byte_size,
+ ArrayBufferContents::kShared, policy);
+ CHECK(contents.DataShared());
+ return base::AdoptRef(new ArrayBuffer(contents));
+}
+
+ArrayBuffer::ArrayBuffer(ArrayBufferContents& contents)
+ : first_view_(nullptr), is_neutered_(false) {
+ if (contents.IsShared())
+ contents.ShareWith(contents_);
+ else
+ contents.Transfer(contents_);
+}
+
+void* ArrayBuffer::Data() {
+ return contents_.Data();
+}
+
+const void* ArrayBuffer::Data() const {
+ return contents_.Data();
+}
+
+void* ArrayBuffer::DataShared() {
+ return contents_.DataShared();
+}
+
+const void* ArrayBuffer::DataShared() const {
+ return contents_.DataShared();
+}
+
+void* ArrayBuffer::DataMaybeShared() {
+ return contents_.DataMaybeShared();
+}
+
+const void* ArrayBuffer::DataMaybeShared() const {
+ return contents_.DataMaybeShared();
+}
+
+unsigned ArrayBuffer::ByteLength() const {
+ return contents_.DataLength();
+}
+
+scoped_refptr<ArrayBuffer> ArrayBuffer::Slice(int begin, int end) const {
+ return SliceImpl(ClampIndex(begin), ClampIndex(end));
+}
+
+scoped_refptr<ArrayBuffer> ArrayBuffer::Slice(int begin) const {
+ return SliceImpl(ClampIndex(begin), ByteLength());
+}
+
+scoped_refptr<ArrayBuffer> ArrayBuffer::SliceImpl(unsigned begin,
+ unsigned end) const {
+ unsigned size = begin <= end ? end - begin : 0;
+ return ArrayBuffer::Create(static_cast<const char*>(Data()) + begin, size);
+}
+
+unsigned ArrayBuffer::ClampIndex(int index) const {
+ unsigned current_length = ByteLength();
+ if (index < 0)
+ index = current_length + index;
+ return ClampValue(index, 0, current_length);
+}
+
+} // namespace WTF
+
+using WTF::ArrayBuffer;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_ARRAY_BUFFER_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_builder.cc b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_builder.cc
new file mode 100644
index 00000000000..b35101e3fd3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_builder.cc
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_builder.h"
+
+#include <limits>
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+namespace WTF {
+
+static const int kDefaultBufferCapacity = 32768;
+
+ArrayBufferBuilder::ArrayBufferBuilder()
+ : bytes_used_(0), variable_capacity_(true) {
+ buffer_ = ArrayBuffer::Create(kDefaultBufferCapacity, 1);
+}
+
+bool ArrayBufferBuilder::ExpandCapacity(unsigned size_to_increase) {
+ unsigned current_buffer_size = buffer_->ByteLength();
+
+ // If the size of the buffer exceeds max of unsigned, it can't be grown any
+ // more.
+ if (size_to_increase > std::numeric_limits<unsigned>::max() - bytes_used_)
+ return false;
+
+ unsigned new_buffer_size = bytes_used_ + size_to_increase;
+
+ // Grow exponentially if possible.
+ unsigned exponential_growth_new_buffer_size =
+ std::numeric_limits<unsigned>::max();
+ if (current_buffer_size <= std::numeric_limits<unsigned>::max() / 2)
+ exponential_growth_new_buffer_size = current_buffer_size * 2;
+ if (exponential_growth_new_buffer_size > new_buffer_size)
+ new_buffer_size = exponential_growth_new_buffer_size;
+
+ // Copy existing data in current buffer to new buffer.
+ scoped_refptr<ArrayBuffer> new_buffer =
+ ArrayBuffer::Create(new_buffer_size, 1);
+ if (!new_buffer)
+ return false;
+
+ memcpy(new_buffer->Data(), buffer_->Data(), bytes_used_);
+ buffer_ = new_buffer;
+ return true;
+}
+
+unsigned ArrayBufferBuilder::Append(const char* data, unsigned length) {
+ DCHECK_GT(length, 0u);
+
+ unsigned current_buffer_size = buffer_->ByteLength();
+
+ DCHECK_LE(bytes_used_, current_buffer_size);
+
+ unsigned remaining_buffer_space = current_buffer_size - bytes_used_;
+
+ unsigned bytes_to_save = length;
+
+ if (length > remaining_buffer_space) {
+ if (variable_capacity_) {
+ if (!ExpandCapacity(length))
+ return 0;
+ } else {
+ bytes_to_save = remaining_buffer_space;
+ }
+ }
+
+ memcpy(static_cast<char*>(buffer_->Data()) + bytes_used_, data,
+ bytes_to_save);
+ bytes_used_ += bytes_to_save;
+
+ return bytes_to_save;
+}
+
+scoped_refptr<ArrayBuffer> ArrayBufferBuilder::ToArrayBuffer() {
+ // Fully used. Return m_buffer as-is.
+ if (buffer_->ByteLength() == bytes_used_)
+ return buffer_;
+
+ return buffer_->Slice(0, bytes_used_);
+}
+
+String ArrayBufferBuilder::ToString() {
+ return String(static_cast<const char*>(buffer_->Data()), bytes_used_);
+}
+
+void ArrayBufferBuilder::ShrinkToFit() {
+ DCHECK_LE(bytes_used_, buffer_->ByteLength());
+
+ if (buffer_->ByteLength() > bytes_used_)
+ buffer_ = buffer_->Slice(0, bytes_used_);
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_builder.h b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_builder.h
new file mode 100644
index 00000000000..f521b13aa89
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_builder.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_ARRAY_BUFFER_BUILDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_ARRAY_BUFFER_BUILDER_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer.h"
+
+namespace WTF {
+
+// A utility class to build an ArrayBuffer instance. Validity must be checked
+// by isValid() before using an instance.
+class WTF_EXPORT ArrayBufferBuilder final {
+ // Disallow copying since it's expensive and we don't want code to do it by
+ // accident.
+ USING_FAST_MALLOC(ArrayBufferBuilder);
+
+ public:
+ // Creates an ArrayBufferBuilder using the default capacity.
+ ArrayBufferBuilder();
+
+ ArrayBufferBuilder(unsigned capacity)
+ : bytes_used_(0), variable_capacity_(true) {
+ buffer_ = ArrayBuffer::Create(capacity, 1);
+ }
+
+ bool IsValid() const { return buffer_.get(); }
+
+ // Appending empty data is not allowed.
+ unsigned Append(const char* data, unsigned length);
+
+ // Returns the accumulated data as an ArrayBuffer instance. If needed,
+ // creates a new ArrayBuffer instance and copies contents from the internal
+ // buffer to it. Otherwise, returns a RefPtr pointing to the internal
+ // buffer.
+ scoped_refptr<ArrayBuffer> ToArrayBuffer();
+
+ // Converts the accumulated data into a String using the default encoding.
+ String ToString();
+
+ // Number of bytes currently accumulated.
+ unsigned ByteLength() const { return bytes_used_; }
+
+ // Number of bytes allocated.
+ unsigned Capacity() const { return buffer_->ByteLength(); }
+
+ void ShrinkToFit();
+
+ const void* Data() const { return buffer_->Data(); }
+
+ // If set to false, the capacity won't be expanded and when appended data
+ // overflows, the overflowed part will be dropped.
+ void SetVariableCapacity(bool value) { variable_capacity_ = value; }
+
+ private:
+ // Expands the size of m_buffer to size + m_bytesUsed bytes. Returns true
+ // iff successful. If reallocation is needed, copies only data in
+ // [0, m_bytesUsed) range.
+ bool ExpandCapacity(unsigned size);
+
+ unsigned bytes_used_;
+ bool variable_capacity_;
+ scoped_refptr<ArrayBuffer> buffer_;
+
+ DISALLOW_COPY_AND_ASSIGN(ArrayBufferBuilder);
+};
+
+} // namespace WTF
+
+using WTF::ArrayBufferBuilder;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_ARRAY_BUFFER_BUILDER_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_builder_test.cc b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_builder_test.cc
new file mode 100644
index 00000000000..3fca4bbf394
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_builder_test.cc
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_builder.h"
+
+#include <limits.h>
+#include <string.h>
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+namespace WTF {
+
+TEST(ArrayBufferBuilderTest, Constructor) {
+ ArrayBufferBuilder zero_builder(0);
+ EXPECT_EQ(0u, zero_builder.ByteLength());
+ EXPECT_EQ(0u, zero_builder.Capacity());
+
+ ArrayBufferBuilder small_builder(1024);
+ EXPECT_EQ(0u, zero_builder.ByteLength());
+ EXPECT_EQ(1024u, small_builder.Capacity());
+
+ ArrayBufferBuilder big_builder(2048);
+ EXPECT_EQ(0u, zero_builder.ByteLength());
+ EXPECT_EQ(2048u, big_builder.Capacity());
+}
+
+TEST(ArrayBufferBuilderTest, Append) {
+ const char kData[] = "HelloWorld";
+ size_t data_size = sizeof(kData) - 1;
+
+ ArrayBufferBuilder builder(2 * data_size);
+
+ EXPECT_EQ(data_size, builder.Append(kData, data_size));
+ EXPECT_EQ(data_size, builder.ByteLength());
+ EXPECT_EQ(data_size * 2, builder.Capacity());
+
+ EXPECT_EQ(data_size, builder.Append(kData, data_size));
+ EXPECT_EQ(data_size * 2, builder.ByteLength());
+ EXPECT_EQ(data_size * 2, builder.Capacity());
+
+ EXPECT_EQ(data_size, builder.Append(kData, data_size));
+ EXPECT_EQ(data_size * 3, builder.ByteLength());
+ EXPECT_GE(builder.Capacity(), data_size * 3);
+}
+
+TEST(ArrayBufferBuilderTest, AppendRepeatedly) {
+ const char kData[] = "HelloWorld";
+ size_t data_size = sizeof(kData) - 1;
+
+ ArrayBufferBuilder builder(37); // Some number coprime with dataSize.
+
+ for (size_t i = 1; i < 1000U; ++i) {
+ EXPECT_EQ(data_size, builder.Append(kData, data_size));
+ EXPECT_EQ(data_size * i, builder.ByteLength());
+ EXPECT_GE(builder.Capacity(), data_size * i);
+ }
+}
+
+TEST(ArrayBufferBuilderTest, DefaultConstructorAndAppendRepeatedly) {
+ const char kData[] = "HelloWorld";
+ size_t data_size = sizeof(kData) - 1;
+
+ ArrayBufferBuilder builder;
+
+ for (size_t i = 1; i < 4000U; ++i) {
+ EXPECT_EQ(data_size, builder.Append(kData, data_size));
+ EXPECT_EQ(data_size * i, builder.ByteLength());
+ EXPECT_GE(builder.Capacity(), data_size * i);
+ }
+}
+
+TEST(ArrayBufferBuilderTest, AppendFixedCapacity) {
+ const char kData[] = "HelloWorld";
+ size_t data_size = sizeof(kData) - 1;
+
+ ArrayBufferBuilder builder(15);
+ builder.SetVariableCapacity(false);
+
+ EXPECT_EQ(data_size, builder.Append(kData, data_size));
+ EXPECT_EQ(data_size, builder.ByteLength());
+ EXPECT_EQ(15u, builder.Capacity());
+
+ EXPECT_EQ(5u, builder.Append(kData, data_size));
+ EXPECT_EQ(15u, builder.ByteLength());
+ EXPECT_EQ(15u, builder.Capacity());
+
+ EXPECT_EQ(0u, builder.Append(kData, data_size));
+ EXPECT_EQ(15u, builder.ByteLength());
+ EXPECT_EQ(15u, builder.Capacity());
+}
+
+TEST(ArrayBufferBuilderTest, ToArrayBuffer) {
+ const char kData1[] = "HelloWorld";
+ size_t data1_size = sizeof(kData1) - 1;
+
+ const char kData2[] = "GoodbyeWorld";
+ size_t data2_size = sizeof(kData2) - 1;
+
+ ArrayBufferBuilder builder(1024);
+ builder.Append(kData1, data1_size);
+ builder.Append(kData2, data2_size);
+
+ const char kExpected[] = "HelloWorldGoodbyeWorld";
+ size_t expected_size = sizeof(kExpected) - 1;
+
+ scoped_refptr<ArrayBuffer> result = builder.ToArrayBuffer();
+ ASSERT_EQ(data1_size + data2_size, result->ByteLength());
+ ASSERT_EQ(expected_size, result->ByteLength());
+ EXPECT_EQ(0, memcmp(kExpected, result->Data(), expected_size));
+}
+
+TEST(ArrayBufferBuilderTest, ToArrayBufferSameAddressIfExactCapacity) {
+ const char kData[] = "HelloWorld";
+ size_t data_size = sizeof(kData) - 1;
+
+ ArrayBufferBuilder builder(data_size);
+ builder.Append(kData, data_size);
+
+ scoped_refptr<ArrayBuffer> result1 = builder.ToArrayBuffer();
+ scoped_refptr<ArrayBuffer> result2 = builder.ToArrayBuffer();
+ EXPECT_EQ(result1.get(), result2.get());
+}
+
+TEST(ArrayBufferBuilderTest, ToString) {
+ const char kData1[] = "HelloWorld";
+ size_t data1_size = sizeof(kData1) - 1;
+
+ const char kData2[] = "GoodbyeWorld";
+ size_t data2_size = sizeof(kData2) - 1;
+
+ ArrayBufferBuilder builder(1024);
+ builder.Append(kData1, data1_size);
+ builder.Append(kData2, data2_size);
+
+ const char kExpected[] = "HelloWorldGoodbyeWorld";
+ size_t expected_size = sizeof(kExpected) - 1;
+
+ String result = builder.ToString();
+ EXPECT_EQ(expected_size, result.length());
+ for (unsigned i = 0; i < result.length(); ++i)
+ EXPECT_EQ(kExpected[i], result[i]);
+}
+
+TEST(ArrayBufferBuilderTest, ShrinkToFitNoAppend) {
+ ArrayBufferBuilder builder(1024);
+ EXPECT_EQ(1024u, builder.Capacity());
+ builder.ShrinkToFit();
+ EXPECT_EQ(0u, builder.ByteLength());
+ EXPECT_EQ(0u, builder.Capacity());
+}
+
+TEST(ArrayBufferBuilderTest, ShrinkToFit) {
+ const char kData[] = "HelloWorld";
+ size_t data_size = sizeof(kData) - 1;
+
+ ArrayBufferBuilder builder(32);
+
+ EXPECT_EQ(data_size, builder.Append(kData, data_size));
+ EXPECT_EQ(data_size, builder.ByteLength());
+ EXPECT_EQ(32u, builder.Capacity());
+
+ builder.ShrinkToFit();
+ EXPECT_EQ(data_size, builder.ByteLength());
+ EXPECT_EQ(data_size, builder.Capacity());
+}
+
+TEST(ArrayBufferBuilderTest, ShrinkToFitFullyUsed) {
+ const char kData[] = "HelloWorld";
+ size_t data_size = sizeof(kData) - 1;
+
+ ArrayBufferBuilder builder(data_size);
+ const void* internal_address = builder.Data();
+
+ EXPECT_EQ(data_size, builder.Append(kData, data_size));
+ EXPECT_EQ(data_size, builder.ByteLength());
+ EXPECT_EQ(data_size, builder.Capacity());
+
+ builder.ShrinkToFit();
+ // Reallocation should not happen.
+ EXPECT_EQ(internal_address, builder.Data());
+ EXPECT_EQ(data_size, builder.ByteLength());
+ EXPECT_EQ(data_size, builder.Capacity());
+}
+
+TEST(ArrayBufferBuilderTest, ShrinkToFitAfterGrowth) {
+ const char kData[] = "HelloWorld";
+ size_t data_size = sizeof(kData) - 1;
+
+ ArrayBufferBuilder builder(5);
+
+ EXPECT_EQ(data_size, builder.Append(kData, data_size));
+ EXPECT_GE(builder.Capacity(), data_size);
+ builder.ShrinkToFit();
+ EXPECT_EQ(data_size, builder.ByteLength());
+ EXPECT_EQ(data_size, builder.Capacity());
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_contents.cc b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_contents.cc
new file mode 100644
index 00000000000..053babce105
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_contents.cc
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2013 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "build/build_config.h"
+#include "third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_contents.h"
+
+#include <string.h>
+#include "base/allocator/partition_allocator/partition_alloc.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+namespace WTF {
+
+void ArrayBufferContents::DefaultAdjustAmountOfExternalAllocatedMemoryFunction(
+ int64_t diff) {
+ // Do nothing by default.
+}
+
+ArrayBufferContents::AdjustAmountOfExternalAllocatedMemoryFunction
+ ArrayBufferContents::adjust_amount_of_external_allocated_memory_function_ =
+ DefaultAdjustAmountOfExternalAllocatedMemoryFunction;
+
+#if DCHECK_IS_ON()
+ArrayBufferContents::AdjustAmountOfExternalAllocatedMemoryFunction
+ ArrayBufferContents::
+ last_used_adjust_amount_of_external_allocated_memory_function_;
+#endif
+
+ArrayBufferContents::ArrayBufferContents()
+ : holder_(base::AdoptRef(new DataHolder())) {}
+
+ArrayBufferContents::ArrayBufferContents(
+ unsigned num_elements,
+ unsigned element_byte_size,
+ SharingType is_shared,
+ ArrayBufferContents::InitializationPolicy policy)
+ : holder_(base::AdoptRef(new DataHolder())) {
+ // Do not allow 32-bit overflow of the total size.
+ size_t total_size = num_elements * element_byte_size;
+ if (num_elements) {
+ if (total_size / num_elements != element_byte_size) {
+ return;
+ }
+ }
+
+ holder_->AllocateNew(total_size, is_shared, policy);
+}
+
+ArrayBufferContents::ArrayBufferContents(DataHandle data,
+ SharingType is_shared)
+ : holder_(base::AdoptRef(new DataHolder())) {
+ if (data) {
+ holder_->Adopt(std::move(data), is_shared);
+ } else {
+ // Allow null data if size is 0 bytes, make sure data is valid pointer.
+ // (PartitionAlloc guarantees valid pointer for size 0)
+ holder_->AllocateNew(0, is_shared, kZeroInitialize);
+ }
+}
+
+ArrayBufferContents::~ArrayBufferContents() = default;
+
+void ArrayBufferContents::Neuter() {
+ holder_ = nullptr;
+}
+
+void ArrayBufferContents::Transfer(ArrayBufferContents& other) {
+ DCHECK(!IsShared());
+ DCHECK(!other.holder_->Data());
+ other.holder_ = holder_;
+ Neuter();
+}
+
+void ArrayBufferContents::ShareWith(ArrayBufferContents& other) {
+ DCHECK(IsShared());
+ DCHECK(!other.holder_->Data());
+ other.holder_ = holder_;
+}
+
+void ArrayBufferContents::CopyTo(ArrayBufferContents& other) {
+ DCHECK(!holder_->IsShared() && !other.holder_->IsShared());
+ other.holder_->CopyMemoryFrom(*holder_);
+}
+
+void* ArrayBufferContents::AllocateMemoryWithFlags(size_t size,
+ InitializationPolicy policy,
+ int flags) {
+ void* data = PartitionAllocGenericFlags(
+ Partitions::ArrayBufferPartition(), flags, size,
+ WTF_HEAP_PROFILER_TYPE_NAME(ArrayBufferContents));
+ if (policy == kZeroInitialize && data)
+ memset(data, '\0', size);
+ return data;
+}
+
+void* ArrayBufferContents::AllocateMemoryOrNull(size_t size,
+ InitializationPolicy policy) {
+ return AllocateMemoryWithFlags(size, policy, base::PartitionAllocReturnNull);
+}
+
+void ArrayBufferContents::FreeMemory(void* data) {
+ Partitions::ArrayBufferPartition()->Free(data);
+}
+
+ArrayBufferContents::DataHandle ArrayBufferContents::CreateDataHandle(
+ size_t size,
+ InitializationPolicy policy) {
+ return DataHandle(ArrayBufferContents::AllocateMemoryOrNull(size, policy),
+ size, FreeMemory);
+}
+
+ArrayBufferContents::DataHolder::DataHolder()
+ : data_(nullptr, 0, FreeMemory),
+ is_shared_(kNotShared),
+ has_registered_external_allocation_(false) {}
+
+ArrayBufferContents::DataHolder::~DataHolder() {
+ if (has_registered_external_allocation_)
+ AdjustAmountOfExternalAllocatedMemory(-static_cast<int64_t>(DataLength()));
+
+ is_shared_ = kNotShared;
+}
+
+void ArrayBufferContents::DataHolder::AllocateNew(size_t length,
+ SharingType is_shared,
+ InitializationPolicy policy) {
+ DCHECK(!data_);
+ DCHECK(!has_registered_external_allocation_);
+
+ data_ = CreateDataHandle(length, policy);
+ if (!data_)
+ return;
+
+ is_shared_ = is_shared;
+
+ AdjustAmountOfExternalAllocatedMemory(length);
+}
+
+void ArrayBufferContents::DataHolder::Adopt(DataHandle data,
+ SharingType is_shared) {
+ DCHECK(!data_);
+ DCHECK(!has_registered_external_allocation_);
+
+ data_ = std::move(data);
+ is_shared_ = is_shared;
+
+ AdjustAmountOfExternalAllocatedMemory(data.DataLength());
+}
+
+void ArrayBufferContents::DataHolder::CopyMemoryFrom(const DataHolder& source) {
+ DCHECK(!data_);
+ DCHECK(!has_registered_external_allocation_);
+
+ data_ = CreateDataHandle(source.DataLength(), kDontInitialize);
+ if (!data_)
+ return;
+
+ memcpy(data_.Data(), source.Data(), source.DataLength());
+
+ AdjustAmountOfExternalAllocatedMemory(source.DataLength());
+}
+
+void ArrayBufferContents::DataHolder::
+ RegisterExternalAllocationWithCurrentContext() {
+ DCHECK(!has_registered_external_allocation_);
+ AdjustAmountOfExternalAllocatedMemory(static_cast<int64_t>(DataLength()));
+}
+
+void ArrayBufferContents::DataHolder::
+ UnregisterExternalAllocationWithCurrentContext() {
+ if (!has_registered_external_allocation_)
+ return;
+ AdjustAmountOfExternalAllocatedMemory(-static_cast<int64_t>(DataLength()));
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_contents.h b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_contents.h
new file mode 100644
index 00000000000..809229caa87
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_contents.h
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2013 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_ARRAY_BUFFER_CONTENTS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_ARRAY_BUFFER_CONTENTS_H_
+
+#include "base/allocator/partition_allocator/page_allocator.h"
+#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/wtf.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_export.h"
+
+namespace WTF {
+
+class WTF_EXPORT ArrayBufferContents {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ using AdjustAmountOfExternalAllocatedMemoryFunction = void (*)(int64_t diff);
+ // Types that need to be used when injecting external memory.
+ // DataHandle allows specifying a deleter which will be invoked when
+ // DataHandle instance goes out of scope. If the data memory is allocated
+ // using ArrayBufferContents::AllocateMemoryOrNull, it is necessary to specify
+ // ArrayBufferContents::FreeMemory as the DataDeleter. Most clients would want
+ // to use ArrayBufferContents::CreateDataHandle, which allocates memory and
+ // specifies the correct deleter.
+ using DataDeleter = void (*)(void* data);
+
+ enum class AllocationKind { kNormal, kReservation };
+
+ class DataHandle {
+ DISALLOW_COPY_AND_ASSIGN(DataHandle);
+
+ public:
+ DataHandle(void* data, size_t length, DataDeleter deleter)
+ : allocation_base_(data),
+ allocation_length_(length),
+ data_(data),
+ data_length_(length),
+ kind_(AllocationKind::kNormal),
+ deleter_(deleter) {}
+ DataHandle(void* allocation_base,
+ size_t allocation_length,
+ void* data,
+ size_t data_length,
+ AllocationKind kind,
+ DataDeleter deleter)
+ : allocation_base_(allocation_base),
+ allocation_length_(allocation_length),
+ data_(data),
+ data_length_(data_length),
+ kind_(kind),
+ deleter_(deleter) {
+ DCHECK(reinterpret_cast<uintptr_t>(allocation_base_) <=
+ reinterpret_cast<uintptr_t>(data_));
+ DCHECK(reinterpret_cast<uintptr_t>(data_) + data_length_ <=
+ reinterpret_cast<uintptr_t>(allocation_base_) +
+ allocation_length_);
+ }
+ // Move constructor
+ DataHandle(DataHandle&& other) { *this = std::move(other); }
+ ~DataHandle() {
+ if (!allocation_base_)
+ return;
+ DCHECK(reinterpret_cast<uintptr_t>(allocation_base_) <=
+ reinterpret_cast<uintptr_t>(data_));
+ DCHECK(reinterpret_cast<uintptr_t>(data_) + data_length_ <=
+ reinterpret_cast<uintptr_t>(allocation_base_) +
+ allocation_length_);
+ switch (kind_) {
+ case AllocationKind::kNormal:
+ DCHECK(deleter_);
+ deleter_(data_);
+ return;
+ case AllocationKind::kReservation:
+ base::FreePages(allocation_base_, allocation_length_);
+ return;
+ }
+ }
+
+ // Move operator
+ DataHandle& operator=(DataHandle&& other) {
+ allocation_base_ = other.allocation_base_;
+ allocation_length_ = other.allocation_length_;
+ data_ = other.data_;
+ data_length_ = other.data_length_;
+ kind_ = other.kind_;
+ deleter_ = other.deleter_;
+ other.allocation_base_ = nullptr;
+ return *this;
+ }
+
+ void* AllocationBase() const { return allocation_base_; }
+ size_t AllocationLength() const { return allocation_length_; }
+
+ void* Data() const { return data_; }
+ size_t DataLength() const { return data_length_; }
+
+ ArrayBufferContents::AllocationKind GetAllocationKind() const {
+ return kind_;
+ }
+
+ operator bool() const { return allocation_base_; }
+
+ private:
+ void* allocation_base_;
+ size_t allocation_length_;
+
+ void* data_;
+ size_t data_length_;
+
+ ArrayBufferContents::AllocationKind kind_;
+ DataDeleter deleter_;
+ };
+
+ enum InitializationPolicy { kZeroInitialize, kDontInitialize };
+
+ enum SharingType {
+ kNotShared,
+ kShared,
+ };
+
+ ArrayBufferContents();
+ ArrayBufferContents(unsigned num_elements,
+ unsigned element_byte_size,
+ SharingType is_shared,
+ InitializationPolicy);
+ ArrayBufferContents(DataHandle,
+ SharingType is_shared);
+ ArrayBufferContents(ArrayBufferContents&&) = default;
+
+ ~ArrayBufferContents();
+
+ ArrayBufferContents& operator=(ArrayBufferContents&&) = default;
+
+ void Neuter();
+
+ void* Data() const {
+ DCHECK(!IsShared());
+ return DataMaybeShared();
+ }
+ void* DataShared() const {
+ DCHECK(IsShared());
+ return DataMaybeShared();
+ }
+ void* DataMaybeShared() const { return holder_ ? holder_->Data() : nullptr; }
+ size_t DataLength() const { return holder_ ? holder_->DataLength() : 0; }
+ bool IsShared() const { return holder_ ? holder_->IsShared() : false; }
+
+ void Transfer(ArrayBufferContents& other);
+ void ShareWith(ArrayBufferContents& other);
+ void CopyTo(ArrayBufferContents& other);
+
+ static void* AllocateMemoryOrNull(size_t, InitializationPolicy);
+ static void FreeMemory(void*);
+ static DataHandle CreateDataHandle(size_t, InitializationPolicy);
+ static void Initialize(
+ AdjustAmountOfExternalAllocatedMemoryFunction function) {
+ DCHECK(IsMainThread());
+ DCHECK_EQ(adjust_amount_of_external_allocated_memory_function_,
+ DefaultAdjustAmountOfExternalAllocatedMemoryFunction);
+ adjust_amount_of_external_allocated_memory_function_ = function;
+ }
+
+ void RegisterExternalAllocationWithCurrentContext() {
+ if (holder_)
+ holder_->RegisterExternalAllocationWithCurrentContext();
+ }
+
+ void UnregisterExternalAllocationWithCurrentContext() {
+ if (holder_)
+ holder_->UnregisterExternalAllocationWithCurrentContext();
+ }
+
+ private:
+ static void* AllocateMemoryWithFlags(size_t, InitializationPolicy, int);
+
+ static void DefaultAdjustAmountOfExternalAllocatedMemoryFunction(
+ int64_t diff);
+
+ class DataHolder : public ThreadSafeRefCounted<DataHolder> {
+ DISALLOW_COPY_AND_ASSIGN(DataHolder);
+
+ public:
+ DataHolder();
+ ~DataHolder();
+
+ void AllocateNew(size_t length,
+ SharingType is_shared,
+ InitializationPolicy);
+ void Adopt(DataHandle, SharingType is_shared);
+ void CopyMemoryFrom(const DataHolder& source);
+
+ const void* Data() const { return data_.Data(); }
+ void* Data() { return data_.Data(); }
+ size_t DataLength() const { return data_.DataLength(); }
+ bool IsShared() const { return is_shared_ == kShared; }
+
+ void RegisterExternalAllocationWithCurrentContext();
+ void UnregisterExternalAllocationWithCurrentContext();
+
+ private:
+ void AdjustAmountOfExternalAllocatedMemory(int64_t diff) {
+ has_registered_external_allocation_ =
+ !has_registered_external_allocation_;
+ DCHECK(!diff || (has_registered_external_allocation_ == (diff > 0)));
+ CheckIfAdjustAmountOfExternalAllocatedMemoryIsConsistent();
+ adjust_amount_of_external_allocated_memory_function_(diff);
+ }
+
+ void AdjustAmountOfExternalAllocatedMemory(size_t diff) {
+ AdjustAmountOfExternalAllocatedMemory(static_cast<int64_t>(diff));
+ }
+
+ void CheckIfAdjustAmountOfExternalAllocatedMemoryIsConsistent() {
+ DCHECK(adjust_amount_of_external_allocated_memory_function_);
+
+#if DCHECK_IS_ON()
+ // Make sure that the function actually used is always the same.
+ // Shouldn't be updated during its use.
+ if (!last_used_adjust_amount_of_external_allocated_memory_function_) {
+ last_used_adjust_amount_of_external_allocated_memory_function_ =
+ adjust_amount_of_external_allocated_memory_function_;
+ }
+ DCHECK_EQ(adjust_amount_of_external_allocated_memory_function_,
+ last_used_adjust_amount_of_external_allocated_memory_function_);
+#endif
+ }
+
+ DataHandle data_;
+ SharingType is_shared_;
+ bool has_registered_external_allocation_;
+ };
+
+ scoped_refptr<DataHolder> holder_;
+ static AdjustAmountOfExternalAllocatedMemoryFunction
+ adjust_amount_of_external_allocated_memory_function_;
+#if DCHECK_IS_ON()
+ static AdjustAmountOfExternalAllocatedMemoryFunction
+ last_used_adjust_amount_of_external_allocated_memory_function_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(ArrayBufferContents);
+};
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_ARRAY_BUFFER_CONTENTS_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_view.cc b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_view.cc
new file mode 100644
index 00000000000..e88d98b2d2d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_view.cc
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2009 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_view.h"
+
+#include "third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer.h"
+
+namespace WTF {
+
+ArrayBufferView::ArrayBufferView(scoped_refptr<ArrayBuffer> buffer,
+ unsigned byte_offset)
+ : byte_offset_(byte_offset),
+ is_neuterable_(true),
+ buffer_(std::move(buffer)),
+ prev_view_(nullptr),
+ next_view_(nullptr) {
+ base_address_ =
+ buffer_ ? (static_cast<char*>(buffer_->DataMaybeShared()) + byte_offset_)
+ : nullptr;
+ if (buffer_)
+ buffer_->AddView(this);
+}
+
+ArrayBufferView::~ArrayBufferView() {
+ if (buffer_)
+ buffer_->RemoveView(this);
+}
+
+void ArrayBufferView::Neuter() {
+ buffer_ = nullptr;
+ byte_offset_ = 0;
+}
+
+const char* ArrayBufferView::TypeName() {
+ switch (GetType()) {
+ case kTypeInt8:
+ return "Int8";
+ break;
+ case kTypeUint8:
+ return "UInt8";
+ break;
+ case kTypeUint8Clamped:
+ return "UInt8Clamped";
+ break;
+ case kTypeInt16:
+ return "Int16";
+ break;
+ case kTypeUint16:
+ return "UInt16";
+ break;
+ case kTypeInt32:
+ return "Int32";
+ break;
+ case kTypeUint32:
+ return "Uint32";
+ break;
+ case kTypeBigInt64:
+ return "BigInt64";
+ break;
+ case kTypeBigUint64:
+ return "BigUint64";
+ break;
+ case kTypeFloat32:
+ return "Float32";
+ break;
+ case kTypeFloat64:
+ return "Float64";
+ break;
+ case kTypeDataView:
+ return "DataView";
+ break;
+ }
+ NOTREACHED();
+ return "Unknown";
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_view.h b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_view.h
new file mode 100644
index 00000000000..238290301f6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_view.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2009 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_ARRAY_BUFFER_VIEW_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_ARRAY_BUFFER_VIEW_H_
+
+#include <limits.h>
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_export.h"
+
+namespace WTF {
+
+class WTF_EXPORT ArrayBufferView : public RefCounted<ArrayBufferView> {
+ public:
+ enum ViewType {
+ kTypeInt8,
+ kTypeUint8,
+ kTypeUint8Clamped,
+ kTypeInt16,
+ kTypeUint16,
+ kTypeInt32,
+ kTypeUint32,
+ kTypeFloat32,
+ kTypeFloat64,
+ kTypeBigInt64,
+ kTypeBigUint64,
+ kTypeDataView
+ };
+ virtual ViewType GetType() const = 0;
+ const char* TypeName();
+
+ ArrayBuffer* Buffer() const { return buffer_.get(); }
+
+ void* BaseAddress() const {
+ DCHECK(!IsShared());
+ return base_address_;
+ }
+ void* BaseAddressMaybeShared() const { return base_address_; }
+
+ unsigned ByteOffset() const { return byte_offset_; }
+
+ virtual unsigned ByteLength() const = 0;
+ virtual unsigned TypeSize() const = 0;
+
+ void SetNeuterable(bool flag) { is_neuterable_ = flag; }
+ bool IsNeuterable() const { return is_neuterable_; }
+ bool IsShared() const { return buffer_ ? buffer_->IsShared() : false; }
+
+ virtual ~ArrayBufferView();
+
+ protected:
+ ArrayBufferView(scoped_refptr<ArrayBuffer>, unsigned byte_offset);
+
+ inline bool SetImpl(ArrayBufferView*, unsigned byte_offset);
+
+ // Helper to verify that a given sub-range of an ArrayBuffer is
+ // within range.
+ template <typename T>
+ static bool VerifySubRange(const ArrayBuffer* buffer,
+ unsigned byte_offset,
+ unsigned num_elements) {
+ if (!buffer)
+ return false;
+ if (sizeof(T) > 1 && byte_offset % sizeof(T))
+ return false;
+ if (byte_offset > buffer->ByteLength())
+ return false;
+ unsigned remaining_elements =
+ (buffer->ByteLength() - byte_offset) / sizeof(T);
+ if (num_elements > remaining_elements)
+ return false;
+ return true;
+ }
+
+ virtual void Neuter();
+
+ // This is the address of the ArrayBuffer's storage, plus the byte offset.
+ void* base_address_;
+
+ unsigned byte_offset_ : 31;
+ unsigned is_neuterable_ : 1;
+
+ private:
+ friend class ArrayBuffer;
+ scoped_refptr<ArrayBuffer> buffer_;
+ ArrayBufferView* prev_view_;
+ ArrayBufferView* next_view_;
+};
+
+bool ArrayBufferView::SetImpl(ArrayBufferView* array, unsigned byte_offset) {
+ if (byte_offset > ByteLength() ||
+ byte_offset + array->ByteLength() > ByteLength() ||
+ byte_offset + array->ByteLength() < byte_offset) {
+ // Out of range offset or overflow
+ return false;
+ }
+
+ char* base = static_cast<char*>(BaseAddress());
+ memmove(base + byte_offset, array->BaseAddress(), array->ByteLength());
+ return true;
+}
+
+} // namespace WTF
+
+using WTF::ArrayBufferView;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_ARRAY_BUFFER_VIEW_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_piece.cc b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_piece.cc
new file mode 100644
index 00000000000..921586c6111
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_piece.cc
@@ -0,0 +1,67 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/wtf/typed_arrays/array_piece.h"
+
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer.h"
+#include "third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_view.h"
+
+namespace WTF {
+
+ArrayPiece::ArrayPiece() {
+ InitNull();
+}
+
+ArrayPiece::ArrayPiece(void* data, unsigned byte_length) {
+ InitWithData(data, byte_length);
+}
+
+ArrayPiece::ArrayPiece(ArrayBuffer* buffer) {
+ if (buffer) {
+ InitWithData(buffer->Data(), buffer->ByteLength());
+ } else {
+ InitNull();
+ }
+}
+
+ArrayPiece::ArrayPiece(ArrayBufferView* buffer) {
+ if (buffer) {
+ InitWithData(buffer->BaseAddress(), buffer->ByteLength());
+ } else {
+ InitNull();
+ }
+}
+
+bool ArrayPiece::IsNull() const {
+ return is_null_;
+}
+
+void* ArrayPiece::Data() const {
+ DCHECK(!IsNull());
+ return data_;
+}
+
+unsigned char* ArrayPiece::Bytes() const {
+ return static_cast<unsigned char*>(Data());
+}
+
+unsigned ArrayPiece::ByteLength() const {
+ DCHECK(!IsNull());
+ return byte_length_;
+}
+
+void ArrayPiece::InitWithData(void* data, unsigned byte_length) {
+ byte_length_ = byte_length;
+ data_ = data;
+ is_null_ = false;
+}
+
+void ArrayPiece::InitNull() {
+ byte_length_ = 0;
+ data_ = nullptr;
+ is_null_ = true;
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_piece.h b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_piece.h
new file mode 100644
index 00000000000..24c1e282bbe
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/array_piece.h
@@ -0,0 +1,57 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_ARRAY_PIECE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_ARRAY_PIECE_H_
+
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_export.h"
+
+namespace WTF {
+
+// This class is for passing around un-owned bytes as a pointer + length.
+// It supports implicit conversion from several other data types.
+//
+// ArrayPiece has the concept of being "null". This is different from an empty
+// byte range. It is invalid to call methods other than isNull() on such
+// instances.
+//
+// IMPORTANT: The data contained by ArrayPiece is NOT OWNED, so caution must be
+// taken to ensure it is kept alive.
+class WTF_EXPORT ArrayPiece {
+ DISALLOW_NEW();
+
+ public:
+ // Constructs a "null" ArrayPiece object.
+ ArrayPiece();
+
+ ArrayPiece(void* data, unsigned byte_length);
+
+ // Constructs an ArrayPiece from the given ArrayBuffer. If the input is a
+ // nullptr, then the constructed instance will be isNull().
+ ArrayPiece(ArrayBuffer*);
+ ArrayPiece(ArrayBufferView*);
+
+ bool IsNull() const;
+ void* Data() const;
+ unsigned char* Bytes() const;
+ unsigned ByteLength() const;
+
+ protected:
+ void InitWithData(void* data, unsigned byte_length);
+
+ private:
+ void InitNull();
+
+ void* data_;
+ unsigned byte_length_;
+ bool is_null_;
+};
+
+} // namespace WTF
+
+using WTF::ArrayPiece;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_ARRAY_PIECE_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/bigint64_array.h b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/bigint64_array.h
new file mode 100644
index 00000000000..07a52592e1d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/bigint64_array.h
@@ -0,0 +1,71 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_BIGINT64_ARRAY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_BIGINT64_ARRAY_H_
+
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/typed_arrays/typed_array_base.h"
+
+namespace WTF {
+
+class BigInt64Array final : public TypedArrayBase<int64_t> {
+ public:
+ static inline scoped_refptr<BigInt64Array> Create(unsigned length);
+ static inline scoped_refptr<BigInt64Array> Create(const int64_t* array,
+ unsigned length);
+ static inline scoped_refptr<BigInt64Array> Create(scoped_refptr<ArrayBuffer>,
+ unsigned byte_offset,
+ unsigned length);
+
+ // Should only be used when it is known the entire array will be filled. Do
+ // not return these results directly to JavaScript without filling first.
+ static inline scoped_refptr<BigInt64Array> CreateUninitialized(
+ unsigned length);
+
+ using TypedArrayBase<int64_t>::Set;
+
+ void Set(unsigned index, int64_t value) {
+ if (index >= TypedArrayBase<int64_t>::length_)
+ return;
+ TypedArrayBase<int64_t>::Data()[index] = value;
+ }
+
+ ViewType GetType() const override { return kTypeBigInt64; }
+
+ private:
+ inline BigInt64Array(scoped_refptr<ArrayBuffer>,
+ unsigned byte_offset,
+ unsigned length);
+ // Make constructor visible to superclass.
+ friend class TypedArrayBase<int64_t>;
+};
+
+scoped_refptr<BigInt64Array> BigInt64Array::Create(unsigned length) {
+ return TypedArrayBase<int64_t>::Create<BigInt64Array>(length);
+}
+
+scoped_refptr<BigInt64Array> BigInt64Array::Create(const int64_t* array,
+ unsigned length) {
+ return TypedArrayBase<int64_t>::Create<BigInt64Array>(array, length);
+}
+
+scoped_refptr<BigInt64Array> BigInt64Array::Create(
+ scoped_refptr<ArrayBuffer> buffer,
+ unsigned byte_offset,
+ unsigned length) {
+ return TypedArrayBase<int64_t>::Create<BigInt64Array>(std::move(buffer),
+ byte_offset, length);
+}
+
+BigInt64Array::BigInt64Array(scoped_refptr<ArrayBuffer> buffer,
+ unsigned byte_offset,
+ unsigned length)
+ : TypedArrayBase<int64_t>(std::move(buffer), byte_offset, length) {}
+
+} // namespace WTF
+
+using WTF::BigInt64Array;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_BIGINT64_ARRAY_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/biguint64_array.h b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/biguint64_array.h
new file mode 100644
index 00000000000..d6a1861fdde
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/biguint64_array.h
@@ -0,0 +1,71 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_BIGUINT64_ARRAY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_BIGUINT64_ARRAY_H_
+
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/typed_arrays/typed_array_base.h"
+
+namespace WTF {
+
+class BigUint64Array final : public TypedArrayBase<uint64_t> {
+ public:
+ static inline scoped_refptr<BigUint64Array> Create(unsigned length);
+ static inline scoped_refptr<BigUint64Array> Create(const uint64_t* array,
+ unsigned length);
+ static inline scoped_refptr<BigUint64Array> Create(scoped_refptr<ArrayBuffer>,
+ unsigned byte_offset,
+ unsigned length);
+
+ // Should only be used when it is known the entire array will be filled. Do
+ // not return these results directly to JavaScript without filling first.
+ static inline scoped_refptr<BigUint64Array> CreateUninitialized(
+ unsigned length);
+
+ using TypedArrayBase<uint64_t>::Set;
+
+ void Set(unsigned index, uint64_t value) {
+ if (index >= TypedArrayBase<uint64_t>::length_)
+ return;
+ TypedArrayBase<uint64_t>::Data()[index] = value;
+ }
+
+ ViewType GetType() const override { return kTypeBigUint64; }
+
+ private:
+ inline BigUint64Array(scoped_refptr<ArrayBuffer>,
+ unsigned byte_offset,
+ unsigned length);
+ // Make constructor visible to superclass.
+ friend class TypedArrayBase<uint64_t>;
+};
+
+scoped_refptr<BigUint64Array> BigUint64Array::Create(unsigned length) {
+ return TypedArrayBase<uint64_t>::Create<BigUint64Array>(length);
+}
+
+scoped_refptr<BigUint64Array> BigUint64Array::Create(const uint64_t* array,
+ unsigned length) {
+ return TypedArrayBase<uint64_t>::Create<BigUint64Array>(array, length);
+}
+
+scoped_refptr<BigUint64Array> BigUint64Array::Create(
+ scoped_refptr<ArrayBuffer> buffer,
+ unsigned byte_offset,
+ unsigned length) {
+ return TypedArrayBase<uint64_t>::Create<BigUint64Array>(std::move(buffer),
+ byte_offset, length);
+}
+
+BigUint64Array::BigUint64Array(scoped_refptr<ArrayBuffer> buffer,
+ unsigned byte_offset,
+ unsigned length)
+ : TypedArrayBase<uint64_t>(std::move(buffer), byte_offset, length) {}
+
+} // namespace WTF
+
+using WTF::BigUint64Array;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_BIGUINT64_ARRAY_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/float32_array.h b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/float32_array.h
new file mode 100644
index 00000000000..b31717b0c8a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/float32_array.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_FLOAT32_ARRAY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_FLOAT32_ARRAY_H_
+
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/typed_arrays/typed_array_base.h"
+
+namespace WTF {
+
+class Float32Array final : public TypedArrayBase<float> {
+ public:
+ static inline scoped_refptr<Float32Array> Create(unsigned length);
+ static inline scoped_refptr<Float32Array> Create(const float* array,
+ unsigned length);
+ static inline scoped_refptr<Float32Array> Create(scoped_refptr<ArrayBuffer>,
+ unsigned byte_offset,
+ unsigned length);
+
+ static inline scoped_refptr<Float32Array> CreateOrNull(unsigned length);
+ static inline scoped_refptr<Float32Array> CreateUninitializedOrNull(
+ unsigned length);
+
+ using TypedArrayBase<float>::Set;
+
+ void Set(unsigned index, double value) {
+ if (index >= TypedArrayBase<float>::length_)
+ return;
+ TypedArrayBase<float>::Data()[index] = static_cast<float>(value);
+ }
+
+ ViewType GetType() const override { return kTypeFloat32; }
+
+ private:
+ inline Float32Array(scoped_refptr<ArrayBuffer>,
+ unsigned byte_offset,
+ unsigned length);
+ // Make constructor visible to superclass.
+ friend class TypedArrayBase<float>;
+};
+
+scoped_refptr<Float32Array> Float32Array::Create(unsigned length) {
+ return TypedArrayBase<float>::Create<Float32Array>(length);
+}
+
+scoped_refptr<Float32Array> Float32Array::Create(const float* array,
+ unsigned length) {
+ return TypedArrayBase<float>::Create<Float32Array>(array, length);
+}
+
+scoped_refptr<Float32Array> Float32Array::Create(
+ scoped_refptr<ArrayBuffer> buffer,
+ unsigned byte_offset,
+ unsigned length) {
+ return TypedArrayBase<float>::Create<Float32Array>(std::move(buffer),
+ byte_offset, length);
+}
+
+scoped_refptr<Float32Array> Float32Array::CreateOrNull(unsigned length) {
+ return TypedArrayBase<float>::CreateOrNull<Float32Array>(length);
+}
+
+scoped_refptr<Float32Array> Float32Array::CreateUninitializedOrNull(
+ unsigned length) {
+ return TypedArrayBase<float>::CreateUninitializedOrNull<Float32Array>(length);
+}
+
+Float32Array::Float32Array(scoped_refptr<ArrayBuffer> buffer,
+ unsigned byte_offset,
+ unsigned length)
+ : TypedArrayBase<float>(std::move(buffer), byte_offset, length) {}
+
+} // namespace WTF
+
+using WTF::Float32Array;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_FLOAT32_ARRAY_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/float64_array.h b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/float64_array.h
new file mode 100644
index 00000000000..94d8e3452fb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/float64_array.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_FLOAT64_ARRAY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_FLOAT64_ARRAY_H_
+
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/typed_arrays/typed_array_base.h"
+
+namespace WTF {
+
+class Float64Array final : public TypedArrayBase<double> {
+ public:
+ static inline scoped_refptr<Float64Array> Create(unsigned length);
+ static inline scoped_refptr<Float64Array> Create(const double* array,
+ unsigned length);
+ static inline scoped_refptr<Float64Array> Create(scoped_refptr<ArrayBuffer>,
+ unsigned byte_offset,
+ unsigned length);
+
+ // Should only be used when it is known the entire array will be filled. Do
+ // not return these results directly to JavaScript without filling first.
+ static inline scoped_refptr<Float64Array> CreateUninitialized(
+ unsigned length);
+
+ using TypedArrayBase<double>::Set;
+
+ void Set(unsigned index, double value) {
+ if (index >= TypedArrayBase<double>::length_)
+ return;
+ TypedArrayBase<double>::Data()[index] = static_cast<double>(value);
+ }
+
+ ViewType GetType() const override { return kTypeFloat64; }
+
+ private:
+ inline Float64Array(scoped_refptr<ArrayBuffer>,
+ unsigned byte_offset,
+ unsigned length);
+ // Make constructor visible to superclass.
+ friend class TypedArrayBase<double>;
+};
+
+scoped_refptr<Float64Array> Float64Array::Create(unsigned length) {
+ return TypedArrayBase<double>::Create<Float64Array>(length);
+}
+
+scoped_refptr<Float64Array> Float64Array::Create(const double* array,
+ unsigned length) {
+ return TypedArrayBase<double>::Create<Float64Array>(array, length);
+}
+
+scoped_refptr<Float64Array> Float64Array::Create(
+ scoped_refptr<ArrayBuffer> buffer,
+ unsigned byte_offset,
+ unsigned length) {
+ return TypedArrayBase<double>::Create<Float64Array>(std::move(buffer),
+ byte_offset, length);
+}
+
+Float64Array::Float64Array(scoped_refptr<ArrayBuffer> buffer,
+ unsigned byte_offset,
+ unsigned length)
+ : TypedArrayBase<double>(std::move(buffer), byte_offset, length) {}
+
+} // namespace WTF
+
+using WTF::Float64Array;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_FLOAT64_ARRAY_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/int16_array.h b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/int16_array.h
new file mode 100644
index 00000000000..4005c8e038f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/int16_array.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2009 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_INT16_ARRAY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_INT16_ARRAY_H_
+
+#include "third_party/blink/renderer/platform/wtf/typed_arrays/integral_typed_array_base.h"
+
+namespace WTF {
+
+class ArrayBuffer;
+
+class Int16Array final : public IntegralTypedArrayBase<short> {
+ public:
+ static inline scoped_refptr<Int16Array> Create(unsigned length);
+ static inline scoped_refptr<Int16Array> Create(const short* array,
+ unsigned length);
+ static inline scoped_refptr<Int16Array> Create(scoped_refptr<ArrayBuffer>,
+ unsigned byte_offset,
+ unsigned length);
+
+ using TypedArrayBase<short>::Set;
+ using IntegralTypedArrayBase<short>::Set;
+
+ ViewType GetType() const override { return kTypeInt16; }
+
+ private:
+ inline Int16Array(scoped_refptr<ArrayBuffer>,
+ unsigned byte_offset,
+ unsigned length);
+ // Make constructor visible to superclass.
+ friend class TypedArrayBase<short>;
+};
+
+scoped_refptr<Int16Array> Int16Array::Create(unsigned length) {
+ return TypedArrayBase<short>::Create<Int16Array>(length);
+}
+
+scoped_refptr<Int16Array> Int16Array::Create(const short* array,
+ unsigned length) {
+ return TypedArrayBase<short>::Create<Int16Array>(array, length);
+}
+
+scoped_refptr<Int16Array> Int16Array::Create(scoped_refptr<ArrayBuffer> buffer,
+ unsigned byte_offset,
+ unsigned length) {
+ return TypedArrayBase<short>::Create<Int16Array>(std::move(buffer),
+ byte_offset, length);
+}
+
+Int16Array::Int16Array(scoped_refptr<ArrayBuffer> buffer,
+ unsigned byte_offset,
+ unsigned length)
+ : IntegralTypedArrayBase<short>(std::move(buffer), byte_offset, length) {}
+
+} // namespace WTF
+
+using WTF::Int16Array;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_INT16_ARRAY_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/int32_array.h b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/int32_array.h
new file mode 100644
index 00000000000..12497813d40
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/int32_array.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_INT32_ARRAY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_INT32_ARRAY_H_
+
+#include "third_party/blink/renderer/platform/wtf/typed_arrays/integral_typed_array_base.h"
+
+namespace WTF {
+
+class Int32Array final : public IntegralTypedArrayBase<int> {
+ public:
+ static inline scoped_refptr<Int32Array> Create(unsigned length);
+ static inline scoped_refptr<Int32Array> Create(const int* array,
+ unsigned length);
+ static inline scoped_refptr<Int32Array> Create(scoped_refptr<ArrayBuffer>,
+ unsigned byte_offset,
+ unsigned length);
+
+ using TypedArrayBase<int>::Set;
+ using IntegralTypedArrayBase<int>::Set;
+
+ ViewType GetType() const override { return kTypeInt32; }
+
+ private:
+ inline Int32Array(scoped_refptr<ArrayBuffer>,
+ unsigned byte_offset,
+ unsigned length);
+ // Make constructor visible to superclass.
+ friend class TypedArrayBase<int>;
+};
+
+scoped_refptr<Int32Array> Int32Array::Create(unsigned length) {
+ return TypedArrayBase<int>::Create<Int32Array>(length);
+}
+
+scoped_refptr<Int32Array> Int32Array::Create(const int* array,
+ unsigned length) {
+ return TypedArrayBase<int>::Create<Int32Array>(array, length);
+}
+
+scoped_refptr<Int32Array> Int32Array::Create(scoped_refptr<ArrayBuffer> buffer,
+ unsigned byte_offset,
+ unsigned length) {
+ return TypedArrayBase<int>::Create<Int32Array>(std::move(buffer), byte_offset,
+ length);
+}
+
+Int32Array::Int32Array(scoped_refptr<ArrayBuffer> buffer,
+ unsigned byte_offset,
+ unsigned length)
+ : IntegralTypedArrayBase<int>(std::move(buffer), byte_offset, length) {}
+
+} // namespace WTF
+
+using WTF::Int32Array;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_INT32_ARRAY_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/int8_array.h b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/int8_array.h
new file mode 100644
index 00000000000..35d35dee6ea
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/int8_array.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_INT8_ARRAY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_INT8_ARRAY_H_
+
+#include "third_party/blink/renderer/platform/wtf/typed_arrays/integral_typed_array_base.h"
+
+namespace WTF {
+
+class ArrayBuffer;
+
+class Int8Array final : public IntegralTypedArrayBase<signed char> {
+ public:
+ static inline scoped_refptr<Int8Array> Create(unsigned length);
+ static inline scoped_refptr<Int8Array> Create(const signed char* array,
+ unsigned length);
+ static inline scoped_refptr<Int8Array> Create(scoped_refptr<ArrayBuffer>,
+ unsigned byte_offset,
+ unsigned length);
+
+ using TypedArrayBase<signed char>::Set;
+ using IntegralTypedArrayBase<signed char>::Set;
+
+ ViewType GetType() const override { return kTypeInt8; }
+
+ private:
+ inline Int8Array(scoped_refptr<ArrayBuffer>,
+ unsigned byte_offset,
+ unsigned length);
+ // Make constructor visible to superclass.
+ friend class TypedArrayBase<signed char>;
+};
+
+scoped_refptr<Int8Array> Int8Array::Create(unsigned length) {
+ return TypedArrayBase<signed char>::Create<Int8Array>(length);
+}
+
+scoped_refptr<Int8Array> Int8Array::Create(const signed char* array,
+ unsigned length) {
+ return TypedArrayBase<signed char>::Create<Int8Array>(array, length);
+}
+
+scoped_refptr<Int8Array> Int8Array::Create(scoped_refptr<ArrayBuffer> buffer,
+ unsigned byte_offset,
+ unsigned length) {
+ return TypedArrayBase<signed char>::Create<Int8Array>(std::move(buffer),
+ byte_offset, length);
+}
+
+Int8Array::Int8Array(scoped_refptr<ArrayBuffer> buffer,
+ unsigned byte_offset,
+ unsigned length)
+ : IntegralTypedArrayBase<signed char>(std::move(buffer),
+ byte_offset,
+ length) {}
+
+} // namespace WTF
+
+using WTF::Int8Array;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_INT8_ARRAY_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/integral_typed_array_base.h b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/integral_typed_array_base.h
new file mode 100644
index 00000000000..65075ec98af
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/integral_typed_array_base.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ * Copyright (c) 2010, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_INTEGRAL_TYPED_ARRAY_BASE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_INTEGRAL_TYPED_ARRAY_BASE_H_
+
+#include <limits>
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/typed_arrays/typed_array_base.h"
+
+namespace WTF {
+
+// Base class for all WebGL<T>Array types holding integral
+// (non-floating-point) values.
+template <typename T>
+class IntegralTypedArrayBase : public TypedArrayBase<T> {
+ public:
+ void Set(unsigned index, double value) {
+ if (index >= TypedArrayBase<T>::length_)
+ return;
+ if (std::isnan(value)) // Clamp NaN to 0
+ value = 0;
+ // The double cast is necessary to get the correct wrapping
+ // for out-of-range values with Int32Array and Uint32Array.
+ TypedArrayBase<T>::Data()[index] =
+ static_cast<T>(static_cast<int64_t>(value));
+ }
+
+ protected:
+ IntegralTypedArrayBase(scoped_refptr<ArrayBuffer> buffer,
+ unsigned byte_offset,
+ unsigned length)
+ : TypedArrayBase<T>(std::move(buffer), byte_offset, length) {}
+};
+
+} // namespace WTF
+
+using WTF::IntegralTypedArrayBase;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_INTEGRAL_TYPED_ARRAY_BASE_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/typed_array_base.h b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/typed_array_base.h
new file mode 100644
index 00000000000..675c9981b14
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/typed_array_base.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ * Copyright (c) 2010, 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_TYPED_ARRAY_BASE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_TYPED_ARRAY_BASE_H_
+
+#include "third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer.h"
+#include "third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_view.h"
+
+namespace WTF {
+
+template <typename T>
+class TypedArrayBase : public ArrayBufferView {
+ public:
+ typedef T ValueType;
+
+ T* Data() const { return static_cast<T*>(BaseAddress()); }
+ T* DataMaybeShared() const {
+ return static_cast<T*>(BaseAddressMaybeShared());
+ }
+
+ bool Set(TypedArrayBase<T>* array, unsigned offset) {
+ return SetImpl(array, offset * sizeof(T));
+ }
+
+ // Overridden from ArrayBufferView. This must be public because of
+ // rules about inheritance of members in template classes, and
+ // because it is accessed via pointers to subclasses.
+ unsigned length() const { return length_; }
+
+ unsigned ByteLength() const final { return length_ * sizeof(T); }
+
+ unsigned TypeSize() const final { return sizeof(T); }
+
+ // Invoked by the indexed getter. Does not perform range checks; caller
+ // is responsible for doing so and returning undefined as necessary.
+ T Item(unsigned index) const {
+ SECURITY_DCHECK(index < TypedArrayBase<T>::length_);
+ return TypedArrayBase<T>::Data()[index];
+ }
+
+ protected:
+ TypedArrayBase(scoped_refptr<ArrayBuffer> buffer,
+ unsigned byte_offset,
+ unsigned length)
+ : ArrayBufferView(std::move(buffer), byte_offset), length_(length) {}
+
+ template <class Subclass>
+ static scoped_refptr<Subclass> Create(unsigned length) {
+ scoped_refptr<ArrayBuffer> buffer = ArrayBuffer::Create(length, sizeof(T));
+ return Create<Subclass>(std::move(buffer), 0, length);
+ }
+
+ template <class Subclass>
+ static scoped_refptr<Subclass> Create(const T* array, unsigned length) {
+ scoped_refptr<Subclass> a = Create<Subclass>(length);
+ if (a)
+ for (unsigned i = 0; i < length; ++i)
+ a->Set(i, array[i]);
+ return a;
+ }
+
+ template <class Subclass>
+ static scoped_refptr<Subclass> Create(scoped_refptr<ArrayBuffer> buffer,
+ unsigned byte_offset,
+ unsigned length) {
+ CHECK(VerifySubRange<T>(buffer.get(), byte_offset, length));
+ return base::AdoptRef(new Subclass(std::move(buffer), byte_offset, length));
+ }
+
+ template <class Subclass>
+ static scoped_refptr<Subclass> CreateOrNull(unsigned length) {
+ scoped_refptr<ArrayBuffer> buffer =
+ ArrayBuffer::CreateOrNull(length, sizeof(T));
+ if (!buffer)
+ return nullptr;
+ return Create<Subclass>(std::move(buffer), 0, length);
+ }
+
+ template <class Subclass>
+ static scoped_refptr<Subclass> CreateUninitializedOrNull(unsigned length) {
+ scoped_refptr<ArrayBuffer> buffer =
+ ArrayBuffer::CreateUninitializedOrNull(length, sizeof(T));
+ if (!buffer)
+ return nullptr;
+ return Create<Subclass>(std::move(buffer), 0, length);
+ }
+
+ void Neuter() final {
+ ArrayBufferView::Neuter();
+ length_ = 0;
+ }
+
+ // We do not want to have to access this via a virtual function in subclasses,
+ // which is why it is protected rather than private.
+ unsigned length_;
+};
+
+} // namespace WTF
+
+using WTF::TypedArrayBase;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_TYPED_ARRAY_BASE_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/uint16_array.h b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/uint16_array.h
new file mode 100644
index 00000000000..c8b2f62332a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/uint16_array.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_UINT16_ARRAY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_UINT16_ARRAY_H_
+
+#include "third_party/blink/renderer/platform/wtf/typed_arrays/integral_typed_array_base.h"
+
+namespace WTF {
+
+class ArrayBuffer;
+
+class Uint16Array final : public IntegralTypedArrayBase<unsigned short> {
+ public:
+ static inline scoped_refptr<Uint16Array> Create(unsigned length);
+ static inline scoped_refptr<Uint16Array> Create(const unsigned short* array,
+ unsigned length);
+ static inline scoped_refptr<Uint16Array> Create(scoped_refptr<ArrayBuffer>,
+ unsigned byte_offset,
+ unsigned length);
+
+ using TypedArrayBase<unsigned short>::Set;
+ using IntegralTypedArrayBase<unsigned short>::Set;
+
+ ViewType GetType() const override { return kTypeUint16; }
+
+ private:
+ inline Uint16Array(scoped_refptr<ArrayBuffer>,
+ unsigned byte_offset,
+ unsigned length);
+ // Make constructor visible to superclass.
+ friend class TypedArrayBase<unsigned short>;
+};
+
+scoped_refptr<Uint16Array> Uint16Array::Create(unsigned length) {
+ return TypedArrayBase<unsigned short>::Create<Uint16Array>(length);
+}
+
+scoped_refptr<Uint16Array> Uint16Array::Create(const unsigned short* array,
+ unsigned length) {
+ return TypedArrayBase<unsigned short>::Create<Uint16Array>(array, length);
+}
+
+scoped_refptr<Uint16Array> Uint16Array::Create(
+ scoped_refptr<ArrayBuffer> buffer,
+ unsigned byte_offset,
+ unsigned length) {
+ return TypedArrayBase<unsigned short>::Create<Uint16Array>(
+ std::move(buffer), byte_offset, length);
+}
+
+Uint16Array::Uint16Array(scoped_refptr<ArrayBuffer> buffer,
+ unsigned byte_offset,
+ unsigned length)
+ : IntegralTypedArrayBase<unsigned short>(std::move(buffer),
+ byte_offset,
+ length) {}
+
+} // namespace WTF
+
+using WTF::Uint16Array;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_UINT16_ARRAY_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/uint32_array.h b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/uint32_array.h
new file mode 100644
index 00000000000..1533c27b163
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/uint32_array.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_UINT32_ARRAY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_UINT32_ARRAY_H_
+
+#include "third_party/blink/renderer/platform/wtf/typed_arrays/integral_typed_array_base.h"
+
+namespace WTF {
+
+class ArrayBuffer;
+
+class Uint32Array final : public IntegralTypedArrayBase<unsigned> {
+ public:
+ static inline scoped_refptr<Uint32Array> Create(unsigned length);
+ static inline scoped_refptr<Uint32Array> Create(const unsigned* array,
+ unsigned length);
+ static inline scoped_refptr<Uint32Array> Create(scoped_refptr<ArrayBuffer>,
+ unsigned byte_offset,
+ unsigned length);
+
+ using TypedArrayBase<unsigned>::Set;
+ using IntegralTypedArrayBase<unsigned>::Set;
+
+ ViewType GetType() const override { return kTypeUint32; }
+
+ private:
+ inline Uint32Array(scoped_refptr<ArrayBuffer>,
+ unsigned byte_offset,
+ unsigned length);
+ // Make constructor visible to superclass.
+ friend class TypedArrayBase<unsigned>;
+};
+
+scoped_refptr<Uint32Array> Uint32Array::Create(unsigned length) {
+ return TypedArrayBase<unsigned>::Create<Uint32Array>(length);
+}
+
+scoped_refptr<Uint32Array> Uint32Array::Create(const unsigned* array,
+ unsigned length) {
+ return TypedArrayBase<unsigned>::Create<Uint32Array>(array, length);
+}
+
+scoped_refptr<Uint32Array> Uint32Array::Create(
+ scoped_refptr<ArrayBuffer> buffer,
+ unsigned byte_offset,
+ unsigned length) {
+ return TypedArrayBase<unsigned>::Create<Uint32Array>(std::move(buffer),
+ byte_offset, length);
+}
+
+Uint32Array::Uint32Array(scoped_refptr<ArrayBuffer> buffer,
+ unsigned byte_offset,
+ unsigned length)
+ : IntegralTypedArrayBase<unsigned>(std::move(buffer), byte_offset, length) {
+}
+
+} // namespace WTF
+
+using WTF::Uint32Array;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_UINT32_ARRAY_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/uint8_array.h b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/uint8_array.h
new file mode 100644
index 00000000000..5e43e6500ca
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/uint8_array.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_UINT8_ARRAY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_UINT8_ARRAY_H_
+
+#include "third_party/blink/renderer/platform/wtf/typed_arrays/integral_typed_array_base.h"
+
+namespace WTF {
+
+class ArrayBuffer;
+
+class Uint8Array : public IntegralTypedArrayBase<unsigned char> {
+ public:
+ static inline scoped_refptr<Uint8Array> Create(unsigned length);
+ static inline scoped_refptr<Uint8Array> Create(const unsigned char* array,
+ unsigned length);
+ static inline scoped_refptr<Uint8Array> Create(scoped_refptr<ArrayBuffer>,
+ unsigned byte_offset,
+ unsigned length);
+
+ using TypedArrayBase<unsigned char>::Set;
+ using IntegralTypedArrayBase<unsigned char>::Set;
+
+ ViewType GetType() const override { return kTypeUint8; }
+
+ protected:
+ inline Uint8Array(scoped_refptr<ArrayBuffer>,
+ unsigned byte_offset,
+ unsigned length);
+ // Make constructor visible to superclass.
+ friend class TypedArrayBase<unsigned char>;
+};
+
+scoped_refptr<Uint8Array> Uint8Array::Create(unsigned length) {
+ return TypedArrayBase<unsigned char>::Create<Uint8Array>(length);
+}
+
+scoped_refptr<Uint8Array> Uint8Array::Create(const unsigned char* array,
+ unsigned length) {
+ return TypedArrayBase<unsigned char>::Create<Uint8Array>(array, length);
+}
+
+scoped_refptr<Uint8Array> Uint8Array::Create(scoped_refptr<ArrayBuffer> buffer,
+ unsigned byte_offset,
+ unsigned length) {
+ return TypedArrayBase<unsigned char>::Create<Uint8Array>(std::move(buffer),
+ byte_offset, length);
+}
+
+Uint8Array::Uint8Array(scoped_refptr<ArrayBuffer> buffer,
+ unsigned byte_offset,
+ unsigned length)
+ : IntegralTypedArrayBase<unsigned char>(std::move(buffer),
+ byte_offset,
+ length) {}
+
+} // namespace WTF
+
+using WTF::Uint8Array;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_UINT8_ARRAY_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/uint8_clamped_array.h b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/uint8_clamped_array.h
new file mode 100644
index 00000000000..9ffa2324b1b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/typed_arrays/uint8_clamped_array.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ * Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_UINT8_CLAMPED_ARRAY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_UINT8_CLAMPED_ARRAY_H_
+
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/typed_arrays/uint8_array.h"
+
+namespace WTF {
+
+class Uint8ClampedArray final : public Uint8Array {
+ public:
+ static inline scoped_refptr<Uint8ClampedArray> Create(unsigned length);
+ static inline scoped_refptr<Uint8ClampedArray> Create(
+ const unsigned char* array,
+ unsigned length);
+ static inline scoped_refptr<Uint8ClampedArray>
+ Create(scoped_refptr<ArrayBuffer>, unsigned byte_offset, unsigned length);
+
+ using TypedArrayBase<unsigned char>::Set;
+ inline void Set(unsigned index, double value);
+
+ ViewType GetType() const override { return kTypeUint8Clamped; }
+
+ private:
+ inline Uint8ClampedArray(scoped_refptr<ArrayBuffer>,
+ unsigned byte_offset,
+ unsigned length);
+ // Make constructor visible to superclass.
+ friend class TypedArrayBase<unsigned char>;
+};
+
+scoped_refptr<Uint8ClampedArray> Uint8ClampedArray::Create(unsigned length) {
+ return TypedArrayBase<unsigned char>::Create<Uint8ClampedArray>(length);
+}
+
+scoped_refptr<Uint8ClampedArray> Uint8ClampedArray::Create(
+ const unsigned char* array,
+ unsigned length) {
+ return TypedArrayBase<unsigned char>::Create<Uint8ClampedArray>(array,
+ length);
+}
+
+scoped_refptr<Uint8ClampedArray> Uint8ClampedArray::Create(
+ scoped_refptr<ArrayBuffer> buffer,
+ unsigned byte_offset,
+ unsigned length) {
+ return TypedArrayBase<unsigned char>::Create<Uint8ClampedArray>(
+ std::move(buffer), byte_offset, length);
+}
+
+void Uint8ClampedArray::Set(unsigned index, double value) {
+ if (index >= length_)
+ return;
+ if (std::isnan(value) || value < 0)
+ value = 0;
+ else if (value > 255)
+ value = 255;
+ Data()[index] = static_cast<unsigned char>(lrint(value));
+}
+
+Uint8ClampedArray::Uint8ClampedArray(scoped_refptr<ArrayBuffer> buffer,
+ unsigned byte_offset,
+ unsigned length)
+ : Uint8Array(std::move(buffer), byte_offset, length) {}
+
+} // namespace WTF
+
+using WTF::Uint8ClampedArray;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TYPED_ARRAYS_UINT8_CLAMPED_ARRAY_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/vector.h b/chromium/third_party/blink/renderer/platform/wtf/vector.h
new file mode 100644
index 00000000000..322422cfef4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/vector.h
@@ -0,0 +1,1999 @@
+/*
+ * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_VECTOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_VECTOR_H_
+
+#include <string.h>
+#include <algorithm>
+#include <initializer_list>
+#include <iterator>
+#include <utility>
+
+#include "base/macros.h"
+#include "base/template_util.h"
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/wtf/alignment.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/partition_allocator.h"
+#include "third_party/blink/renderer/platform/wtf/conditional_destructor.h"
+#include "third_party/blink/renderer/platform/wtf/construct_traits.h"
+#include "third_party/blink/renderer/platform/wtf/container_annotations.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h" // For default Vector template parameters.
+#include "third_party/blink/renderer/platform/wtf/hash_table_deleted_value_type.h"
+#include "third_party/blink/renderer/platform/wtf/not_found.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/vector_traits.h"
+
+// For ASAN builds, disable inline buffers completely as they cause various
+// issues.
+#ifdef ANNOTATE_CONTIGUOUS_CONTAINER
+#define INLINE_CAPACITY 0
+#else
+#define INLINE_CAPACITY inlineCapacity
+#endif
+
+namespace WTF {
+
+#if defined(MEMORY_SANITIZER_INITIAL_SIZE)
+static const size_t kInitialVectorSize = 1;
+#else
+#ifndef WTF_VECTOR_INITIAL_SIZE
+#define WTF_VECTOR_INITIAL_SIZE 4
+#endif
+static const size_t kInitialVectorSize = WTF_VECTOR_INITIAL_SIZE;
+#endif
+
+template <typename T, size_t inlineBuffer, typename Allocator>
+class Deque;
+
+//
+// Vector Traits
+//
+
+// Bunch of traits for Vector are defined here, with which you can customize
+// Vector's behavior. In most cases the default traits are appropriate, so you
+// usually don't have to specialize those traits by yourself.
+//
+// The behavior of the implementation below can be controlled by VectorTraits.
+// If you want to change the behavior of your type, take a look at VectorTraits
+// (defined in VectorTraits.h), too.
+
+template <bool needsDestruction, typename T>
+struct VectorDestructor;
+
+template <typename T>
+struct VectorDestructor<false, T> {
+ STATIC_ONLY(VectorDestructor);
+ static void Destruct(T*, T*) {}
+};
+
+template <typename T>
+struct VectorDestructor<true, T> {
+ STATIC_ONLY(VectorDestructor);
+ static void Destruct(T* begin, T* end) {
+ for (T* cur = begin; cur != end; ++cur)
+ cur->~T();
+ }
+};
+
+template <bool unusedSlotsMustBeZeroed, typename T>
+struct VectorUnusedSlotClearer;
+
+template <typename T>
+struct VectorUnusedSlotClearer<false, T> {
+ STATIC_ONLY(VectorUnusedSlotClearer);
+ static void Clear(T*, T*) {}
+#if DCHECK_IS_ON()
+ static void CheckCleared(const T*, const T*) {}
+#endif
+};
+
+template <typename T>
+struct VectorUnusedSlotClearer<true, T> {
+ STATIC_ONLY(VectorUnusedSlotClearer);
+ static void Clear(T* begin, T* end) {
+ memset(reinterpret_cast<void*>(begin), 0, sizeof(T) * (end - begin));
+ }
+
+#if DCHECK_IS_ON()
+ static void CheckCleared(const T* begin, const T* end) {
+ const unsigned char* unused_area =
+ reinterpret_cast<const unsigned char*>(begin);
+ const unsigned char* end_address =
+ reinterpret_cast<const unsigned char*>(end);
+ DCHECK_GE(end_address, unused_area);
+ for (int i = 0; i < end_address - unused_area; ++i)
+ DCHECK(!unused_area[i]);
+ }
+#endif
+};
+
+template <bool canInitializeWithMemset, typename T, typename Allocator>
+struct VectorInitializer;
+
+template <typename T, typename Allocator>
+struct VectorInitializer<false, T, Allocator> {
+ STATIC_ONLY(VectorInitializer);
+ static void Initialize(T* begin, T* end) {
+ for (T* cur = begin; cur != end; ++cur)
+ new (NotNull, cur) T;
+ }
+};
+
+template <typename T, typename Allocator>
+struct VectorInitializer<true, T, Allocator> {
+ STATIC_ONLY(VectorInitializer);
+ static void Initialize(T* begin, T* end) {
+ memset(begin, 0,
+ reinterpret_cast<char*>(end) - reinterpret_cast<char*>(begin));
+ }
+};
+
+template <bool canMoveWithMemcpy, typename T, typename Allocator>
+struct VectorMover;
+
+template <typename T, typename Allocator>
+struct VectorMover<false, T, Allocator> {
+ STATIC_ONLY(VectorMover);
+ static void Move(T* src, T* src_end, T* dst) {
+ while (src != src_end) {
+ ConstructTraits<T, VectorTraits<T>, Allocator>::ConstructAndNotifyElement(
+ dst, std::move(*src));
+ src->~T();
+ ++dst;
+ ++src;
+ }
+ }
+ static void MoveOverlapping(T* src, T* src_end, T* dst) {
+ if (src > dst) {
+ Move(src, src_end, dst);
+ } else {
+ T* dst_end = dst + (src_end - src);
+ while (src != src_end) {
+ --src_end;
+ --dst_end;
+ ConstructTraits<T, VectorTraits<T>,
+ Allocator>::ConstructAndNotifyElement(dst_end,
+ std::move(
+ *src_end));
+ src_end->~T();
+ }
+ }
+ }
+ static void Swap(T* src, T* src_end, T* dst) {
+ std::swap_ranges(src, src_end, dst);
+ const size_t len = src_end - src;
+ ConstructTraits<T, VectorTraits<T>, Allocator>::NotifyNewElements(src, len);
+ ConstructTraits<T, VectorTraits<T>, Allocator>::NotifyNewElements(dst, len);
+ }
+};
+
+template <typename T, typename Allocator>
+struct VectorMover<true, T, Allocator> {
+ STATIC_ONLY(VectorMover);
+ static void Move(const T* src, const T* src_end, T* dst) {
+ if (LIKELY(dst && src)) {
+ memcpy(dst, src,
+ reinterpret_cast<const char*>(src_end) -
+ reinterpret_cast<const char*>(src));
+ ConstructTraits<T, VectorTraits<T>, Allocator>::NotifyNewElements(
+ dst, src_end - src);
+ }
+ }
+ static void MoveOverlapping(const T* src, const T* src_end, T* dst) {
+ if (LIKELY(dst && src)) {
+ memmove(dst, src,
+ reinterpret_cast<const char*>(src_end) -
+ reinterpret_cast<const char*>(src));
+ ConstructTraits<T, VectorTraits<T>, Allocator>::NotifyNewElements(
+ dst, src_end - src);
+ }
+ }
+ static void Swap(T* src, T* src_end, T* dst) {
+ std::swap_ranges(reinterpret_cast<char*>(src),
+ reinterpret_cast<char*>(src_end),
+ reinterpret_cast<char*>(dst));
+ const size_t len = src_end - src;
+ ConstructTraits<T, VectorTraits<T>, Allocator>::NotifyNewElements(src, len);
+ ConstructTraits<T, VectorTraits<T>, Allocator>::NotifyNewElements(dst, len);
+ }
+};
+
+template <bool canCopyWithMemcpy, typename T, typename Allocator>
+struct VectorCopier;
+
+template <typename T, typename Allocator>
+struct VectorCopier<false, T, Allocator> {
+ STATIC_ONLY(VectorCopier);
+ template <typename U>
+ static void UninitializedCopy(const U* src, const U* src_end, T* dst) {
+ while (src != src_end) {
+ ConstructTraits<T, VectorTraits<T>, Allocator>::ConstructAndNotifyElement(
+ dst, *src);
+ ++dst;
+ ++src;
+ }
+ }
+};
+
+template <typename T, typename Allocator>
+struct VectorCopier<true, T, Allocator> {
+ STATIC_ONLY(VectorCopier);
+ static void UninitializedCopy(const T* src, const T* src_end, T* dst) {
+ if (LIKELY(dst && src)) {
+ memcpy(dst, src,
+ reinterpret_cast<const char*>(src_end) -
+ reinterpret_cast<const char*>(src));
+ ConstructTraits<T, VectorTraits<T>, Allocator>::NotifyNewElements(
+ dst, src_end - src);
+ }
+ }
+ template <typename U>
+ static void UninitializedCopy(const U* src, const U* src_end, T* dst) {
+ VectorCopier<false, T, Allocator>::UninitializedCopy(src, src_end, dst);
+ }
+};
+
+template <bool canFillWithMemset, typename T, typename Allocator>
+struct VectorFiller;
+
+template <typename T, typename Allocator>
+struct VectorFiller<false, T, Allocator> {
+ STATIC_ONLY(VectorFiller);
+ static void UninitializedFill(T* dst, T* dst_end, const T& val) {
+ while (dst != dst_end) {
+ ConstructTraits<T, VectorTraits<T>, Allocator>::ConstructAndNotifyElement(
+ dst, T(val));
+ ++dst;
+ }
+ }
+};
+
+template <typename T, typename Allocator>
+struct VectorFiller<true, T, Allocator> {
+ STATIC_ONLY(VectorFiller);
+ static void UninitializedFill(T* dst, T* dst_end, const T& val) {
+ static_assert(sizeof(T) == sizeof(char), "size of type should be one");
+#if defined(COMPILER_GCC) && defined(_FORTIFY_SOURCE)
+ if (!__builtin_constant_p(dst_end - dst) || (!(dst_end - dst)))
+ memset(dst, val, dst_end - dst);
+#else
+ memset(dst, val, dst_end - dst);
+#endif
+ }
+};
+
+template <bool canCompareWithMemcmp, typename T>
+struct VectorComparer;
+
+template <typename T>
+struct VectorComparer<false, T> {
+ STATIC_ONLY(VectorComparer);
+ static bool Compare(const T* a, const T* b, size_t size) {
+ DCHECK(a);
+ DCHECK(b);
+ return std::equal(a, a + size, b);
+ }
+};
+
+template <typename T>
+struct VectorComparer<true, T> {
+ STATIC_ONLY(VectorComparer);
+ static bool Compare(const T* a, const T* b, size_t size) {
+ DCHECK(a);
+ DCHECK(b);
+ return memcmp(a, b, sizeof(T) * size) == 0;
+ }
+};
+
+template <typename T>
+struct VectorElementComparer {
+ STATIC_ONLY(VectorElementComparer);
+ template <typename U>
+ static bool CompareElement(const T& left, const U& right) {
+ return left == right;
+ }
+};
+
+template <typename T>
+struct VectorElementComparer<std::unique_ptr<T>> {
+ STATIC_ONLY(VectorElementComparer);
+ template <typename U>
+ static bool CompareElement(const std::unique_ptr<T>& left, const U& right) {
+ return left.get() == right;
+ }
+};
+
+// A collection of all the traits used by Vector. This is basically an
+// implementation detail of Vector, and you probably don't want to change this.
+// If you want to customize Vector's behavior, you should specialize
+// VectorTraits instead (defined in VectorTraits.h).
+template <typename T, typename Allocator>
+struct VectorTypeOperations {
+ STATIC_ONLY(VectorTypeOperations);
+ static void Destruct(T* begin, T* end) {
+ VectorDestructor<VectorTraits<T>::kNeedsDestruction, T>::Destruct(begin,
+ end);
+ }
+
+ static void Initialize(T* begin, T* end) {
+ VectorInitializer<VectorTraits<T>::kCanInitializeWithMemset, T,
+ Allocator>::Initialize(begin, end);
+ }
+
+ static void Move(T* src, T* src_end, T* dst) {
+ VectorMover<VectorTraits<T>::kCanMoveWithMemcpy, T, Allocator>::Move(
+ src, src_end, dst);
+ }
+
+ static void MoveOverlapping(T* src, T* src_end, T* dst) {
+ VectorMover<VectorTraits<T>::kCanMoveWithMemcpy, T,
+ Allocator>::MoveOverlapping(src, src_end, dst);
+ }
+
+ static void Swap(T* src, T* src_end, T* dst) {
+ VectorMover<VectorTraits<T>::kCanMoveWithMemcpy, T, Allocator>::Swap(
+ src, src_end, dst);
+ }
+
+ static void UninitializedCopy(const T* src, const T* src_end, T* dst) {
+ VectorCopier<VectorTraits<T>::kCanCopyWithMemcpy, T,
+ Allocator>::UninitializedCopy(src, src_end, dst);
+ }
+
+ static void UninitializedFill(T* dst, T* dst_end, const T& val) {
+ VectorFiller<VectorTraits<T>::kCanFillWithMemset, T,
+ Allocator>::UninitializedFill(dst, dst_end, val);
+ }
+
+ static bool Compare(const T* a, const T* b, size_t size) {
+ return VectorComparer<VectorTraits<T>::kCanCompareWithMemcmp, T>::Compare(
+ a, b, size);
+ }
+
+ template <typename U>
+ static bool CompareElement(const T& left, U&& right) {
+ return VectorElementComparer<T>::CompareElement(left,
+ std::forward<U>(right));
+ }
+};
+
+//
+// VectorBuffer
+//
+
+// VectorBuffer is an implementation detail of Vector and Deque. It manages
+// Vector's underlying buffer, and does operations like allocation or
+// expansion.
+//
+// Not meant for general consumption.
+
+template <typename T, bool hasInlineCapacity, typename Allocator>
+class VectorBufferBase {
+ DISALLOW_NEW();
+
+ public:
+ void AllocateBuffer(size_t new_capacity) {
+ DCHECK(new_capacity);
+ DCHECK_LE(new_capacity,
+ Allocator::template MaxElementCountInBackingStore<T>());
+ size_t size_to_allocate = AllocationSize(new_capacity);
+ if (hasInlineCapacity)
+ buffer_ =
+ Allocator::template AllocateInlineVectorBacking<T>(size_to_allocate);
+ else
+ buffer_ = Allocator::template AllocateVectorBacking<T>(size_to_allocate);
+ capacity_ = size_to_allocate / sizeof(T);
+ Allocator::BackingWriteBarrier(buffer_);
+ }
+
+ void AllocateExpandedBuffer(size_t new_capacity) {
+ DCHECK(new_capacity);
+ size_t size_to_allocate = AllocationSize(new_capacity);
+ if (hasInlineCapacity)
+ buffer_ =
+ Allocator::template AllocateInlineVectorBacking<T>(size_to_allocate);
+ else
+ buffer_ = Allocator::template AllocateExpandedVectorBacking<T>(
+ size_to_allocate);
+ capacity_ = size_to_allocate / sizeof(T);
+ Allocator::BackingWriteBarrier(buffer_);
+ }
+
+ size_t AllocationSize(size_t capacity) const {
+ return Allocator::template QuantizedSize<T>(capacity);
+ }
+
+ T* Buffer() { return buffer_; }
+ const T* Buffer() const { return buffer_; }
+ size_t capacity() const { return capacity_; }
+
+ void ClearUnusedSlots(T* from, T* to) {
+ // If the vector backing is garbage-collected and needs tracing or
+ // finalizing, we clear out the unused slots so that the visitor or the
+ // finalizer does not cause a problem when visiting the unused slots.
+ VectorUnusedSlotClearer<
+ Allocator::kIsGarbageCollected &&
+ (VectorTraits<T>::kNeedsDestruction ||
+ IsTraceableInCollectionTrait<VectorTraits<T>>::value),
+ T>::Clear(from, to);
+ }
+
+ void CheckUnusedSlots(const T* from, const T* to) {
+#if DCHECK_IS_ON() && !defined(ANNOTATE_CONTIGUOUS_CONTAINER)
+ VectorUnusedSlotClearer<
+ Allocator::kIsGarbageCollected &&
+ (VectorTraits<T>::kNeedsDestruction ||
+ IsTraceableInCollectionTrait<VectorTraits<T>>::value),
+ T>::CheckCleared(from, to);
+#endif
+ }
+
+ // |end| is exclusive, a la STL.
+ struct OffsetRange final {
+ OffsetRange() : begin(0), end(0) {}
+ explicit OffsetRange(size_t begin, size_t end) : begin(begin), end(end) {
+ DCHECK_LE(begin, end);
+ }
+ bool empty() const { return begin == end; }
+ size_t begin;
+ size_t end;
+ };
+
+ protected:
+ VectorBufferBase() : buffer_(nullptr), capacity_(0) {}
+
+ VectorBufferBase(T* buffer, size_t capacity)
+ : buffer_(buffer), capacity_(capacity) {}
+
+ VectorBufferBase(HashTableDeletedValueType value)
+ : buffer_(reinterpret_cast<T*>(-1)) {}
+ bool IsHashTableDeletedValue() const {
+ return buffer_ == reinterpret_cast<T*>(-1);
+ }
+
+ T* buffer_;
+ unsigned capacity_;
+ unsigned size_;
+
+ DISALLOW_COPY_AND_ASSIGN(VectorBufferBase);
+};
+
+template <typename T,
+ size_t inlineCapacity,
+ typename Allocator = PartitionAllocator>
+class VectorBuffer;
+
+template <typename T, typename Allocator>
+class VectorBuffer<T, 0, Allocator>
+ : protected VectorBufferBase<T, false, Allocator> {
+ private:
+ using Base = VectorBufferBase<T, false, Allocator>;
+
+ public:
+ using OffsetRange = typename Base::OffsetRange;
+
+ VectorBuffer() = default;
+
+ explicit VectorBuffer(size_t capacity) {
+ // Calling malloc(0) might take a lock and may actually do an allocation
+ // on some systems.
+ if (capacity)
+ AllocateBuffer(capacity);
+ }
+
+ void Destruct() {
+ DeallocateBuffer(buffer_);
+ buffer_ = nullptr;
+ }
+
+ void DeallocateBuffer(T* buffer_to_deallocate) {
+ Allocator::FreeVectorBacking(buffer_to_deallocate);
+ }
+
+ bool ExpandBuffer(size_t new_capacity) {
+ size_t size_to_allocate = AllocationSize(new_capacity);
+ if (Allocator::ExpandVectorBacking(buffer_, size_to_allocate)) {
+ capacity_ = size_to_allocate / sizeof(T);
+ return true;
+ }
+ return false;
+ }
+
+ inline bool ShrinkBuffer(size_t new_capacity) {
+ DCHECK_LT(new_capacity, capacity());
+ size_t size_to_allocate = AllocationSize(new_capacity);
+ if (Allocator::ShrinkVectorBacking(buffer_, AllocationSize(capacity()),
+ size_to_allocate)) {
+ capacity_ = size_to_allocate / sizeof(T);
+ return true;
+ }
+ return false;
+ }
+
+ void ResetBufferPointer() {
+ buffer_ = nullptr;
+ capacity_ = 0;
+ }
+
+ // See the other specialization for the meaning of |thisHole| and |otherHole|.
+ // They are irrelevant in this case.
+ void SwapVectorBuffer(VectorBuffer<T, 0, Allocator>& other,
+ OffsetRange this_hole,
+ OffsetRange other_hole) {
+ static_assert(VectorTraits<T>::kCanSwapUsingCopyOrMove,
+ "Cannot swap HeapVectors of TraceWrapperMembers.");
+ std::swap(buffer_, other.buffer_);
+ Allocator::BackingWriteBarrier(buffer_);
+ Allocator::BackingWriteBarrier(other.buffer_);
+ std::swap(capacity_, other.capacity_);
+ std::swap(size_, other.size_);
+ }
+
+ using Base::AllocateBuffer;
+ using Base::AllocationSize;
+
+ using Base::Buffer;
+ using Base::capacity;
+
+ using Base::ClearUnusedSlots;
+ using Base::CheckUnusedSlots;
+
+ bool HasOutOfLineBuffer() const {
+ // When inlineCapacity is 0 we have an out of line buffer if we have a
+ // buffer.
+ return Buffer();
+ }
+
+ T** BufferSlot() { return &buffer_; }
+
+ protected:
+ using Base::size_;
+
+ private:
+ using Base::buffer_;
+ using Base::capacity_;
+};
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+class VectorBuffer : protected VectorBufferBase<T, true, Allocator> {
+ private:
+ using Base = VectorBufferBase<T, true, Allocator>;
+
+ public:
+ using OffsetRange = typename Base::OffsetRange;
+
+ VectorBuffer() : Base(InlineBuffer(), inlineCapacity) {}
+
+ VectorBuffer(HashTableDeletedValueType value) : Base(value) {}
+ bool IsHashTableDeletedValue() const {
+ return Base::IsHashTableDeletedValue();
+ }
+
+ explicit VectorBuffer(size_t capacity)
+ : Base(InlineBuffer(), inlineCapacity) {
+ if (capacity > inlineCapacity)
+ Base::AllocateBuffer(capacity);
+ }
+
+ void Destruct() {
+ DeallocateBuffer(buffer_);
+ buffer_ = nullptr;
+ }
+
+ NEVER_INLINE void ReallyDeallocateBuffer(T* buffer_to_deallocate) {
+ Allocator::FreeInlineVectorBacking(buffer_to_deallocate);
+ }
+
+ void DeallocateBuffer(T* buffer_to_deallocate) {
+ if (UNLIKELY(buffer_to_deallocate != InlineBuffer()))
+ ReallyDeallocateBuffer(buffer_to_deallocate);
+ }
+
+ bool ExpandBuffer(size_t new_capacity) {
+ DCHECK_GT(new_capacity, inlineCapacity);
+ if (buffer_ == InlineBuffer())
+ return false;
+
+ size_t size_to_allocate = AllocationSize(new_capacity);
+ if (Allocator::ExpandInlineVectorBacking(buffer_, size_to_allocate)) {
+ capacity_ = size_to_allocate / sizeof(T);
+ return true;
+ }
+ return false;
+ }
+
+ inline bool ShrinkBuffer(size_t new_capacity) {
+ DCHECK_LT(new_capacity, capacity());
+ if (new_capacity <= inlineCapacity) {
+ // We need to switch to inlineBuffer. Vector::shrinkCapacity will
+ // handle it.
+ return false;
+ }
+ DCHECK_NE(buffer_, InlineBuffer());
+ size_t new_size = AllocationSize(new_capacity);
+ if (!Allocator::ShrinkInlineVectorBacking(
+ buffer_, AllocationSize(capacity()), new_size))
+ return false;
+ capacity_ = new_size / sizeof(T);
+ return true;
+ }
+
+ void ResetBufferPointer() {
+ buffer_ = InlineBuffer();
+ capacity_ = inlineCapacity;
+ }
+
+ void AllocateBuffer(size_t new_capacity) {
+ // FIXME: This should DCHECK(!buffer_) to catch misuse/leaks.
+ if (new_capacity > inlineCapacity)
+ Base::AllocateBuffer(new_capacity);
+ else
+ ResetBufferPointer();
+ }
+
+ void AllocateExpandedBuffer(size_t new_capacity) {
+ if (new_capacity > inlineCapacity)
+ Base::AllocateExpandedBuffer(new_capacity);
+ else
+ ResetBufferPointer();
+ }
+
+ size_t AllocationSize(size_t capacity) const {
+ if (capacity <= inlineCapacity)
+ return kInlineBufferSize;
+ return Base::AllocationSize(capacity);
+ }
+
+ // Swap two vector buffers, both of which have the same non-zero inline
+ // capacity.
+ //
+ // If the data is in an out-of-line buffer, we can just pass the pointers
+ // across the two buffers. If the data is in an inline buffer, we need to
+ // either swap or move each element, depending on whether each slot is
+ // occupied or not.
+ //
+ // Further complication comes from the fact that VectorBuffer is also used as
+ // the backing store of a Deque. Deque allocates the objects like a ring
+ // buffer, so there may be a "hole" (unallocated region) in the middle of the
+ // buffer. This function assumes elements in a range [buffer_, buffer_ +
+ // size_) are all allocated except for elements within |thisHole|. The same
+ // applies for |other.buffer_| and |otherHole|.
+ void SwapVectorBuffer(VectorBuffer<T, inlineCapacity, Allocator>& other,
+ OffsetRange this_hole,
+ OffsetRange other_hole) {
+ using TypeOperations = VectorTypeOperations<T, Allocator>;
+
+ static_assert(VectorTraits<T>::kCanSwapUsingCopyOrMove,
+ "Cannot swap HeapVectors of TraceWrapperMembers.");
+
+ if (Buffer() != InlineBuffer() && other.Buffer() != other.InlineBuffer()) {
+ // The easiest case: both buffers are non-inline. We just need to swap the
+ // pointers.
+ std::swap(buffer_, other.buffer_);
+ Allocator::BackingWriteBarrier(buffer_);
+ Allocator::BackingWriteBarrier(other.buffer_);
+ std::swap(capacity_, other.capacity_);
+ std::swap(size_, other.size_);
+ return;
+ }
+
+ Allocator::EnterGCForbiddenScope();
+
+ // Otherwise, we at least need to move some elements from one inline buffer
+ // to another.
+ //
+ // Terminology: "source" is a place from which elements are copied, and
+ // "destination" is a place to which elements are copied. thisSource or
+ // otherSource can be empty (represented by nullptr) when this range or
+ // other range is in an out-of-line buffer.
+ //
+ // We first record which range needs to get moved and where elements in such
+ // a range will go. Elements in an inline buffer will go to the other
+ // buffer's inline buffer. Elements in an out-of-line buffer won't move,
+ // because we can just swap pointers of out-of-line buffers.
+ T* this_source_begin = nullptr;
+ size_t this_source_size = 0;
+ T* this_destination_begin = nullptr;
+ if (Buffer() == InlineBuffer()) {
+ this_source_begin = Buffer();
+ this_source_size = size_;
+ this_destination_begin = other.InlineBuffer();
+ if (!this_hole.empty()) { // Sanity check.
+ DCHECK_LT(this_hole.begin, this_hole.end);
+ DCHECK_LE(this_hole.end, this_source_size);
+ }
+ } else {
+ // We don't need the hole information for an out-of-line buffer.
+ this_hole.begin = this_hole.end = 0;
+ }
+ T* other_source_begin = nullptr;
+ size_t other_source_size = 0;
+ T* other_destination_begin = nullptr;
+ if (other.Buffer() == other.InlineBuffer()) {
+ other_source_begin = other.Buffer();
+ other_source_size = other.size_;
+ other_destination_begin = InlineBuffer();
+ if (!other_hole.empty()) {
+ DCHECK_LT(other_hole.begin, other_hole.end);
+ DCHECK_LE(other_hole.end, other_source_size);
+ }
+ } else {
+ other_hole.begin = other_hole.end = 0;
+ }
+
+ // Next, we mutate members and do other bookkeeping. We do pointer swapping
+ // (for out-of-line buffers) here if we can. From now on, don't assume
+ // buffer() or capacity() maintains their original values.
+ std::swap(capacity_, other.capacity_);
+ if (this_source_begin &&
+ !other_source_begin) { // Our buffer is inline, theirs is not.
+ DCHECK_EQ(Buffer(), InlineBuffer());
+ DCHECK_NE(other.Buffer(), other.InlineBuffer());
+ ANNOTATE_DELETE_BUFFER(buffer_, inlineCapacity, size_);
+ buffer_ = other.Buffer();
+ other.buffer_ = other.InlineBuffer();
+ std::swap(size_, other.size_);
+ ANNOTATE_NEW_BUFFER(other.buffer_, inlineCapacity, other.size_);
+ Allocator::BackingWriteBarrier(buffer_);
+ } else if (!this_source_begin &&
+ other_source_begin) { // Their buffer is inline, ours is not.
+ DCHECK_NE(Buffer(), InlineBuffer());
+ DCHECK_EQ(other.Buffer(), other.InlineBuffer());
+ ANNOTATE_DELETE_BUFFER(other.buffer_, inlineCapacity, other.size_);
+ other.buffer_ = Buffer();
+ buffer_ = InlineBuffer();
+ std::swap(size_, other.size_);
+ ANNOTATE_NEW_BUFFER(buffer_, inlineCapacity, size_);
+ Allocator::BackingWriteBarrier(other.buffer_);
+ } else { // Both buffers are inline.
+ DCHECK(this_source_begin);
+ DCHECK(other_source_begin);
+ DCHECK_EQ(Buffer(), InlineBuffer());
+ DCHECK_EQ(other.Buffer(), other.InlineBuffer());
+ ANNOTATE_CHANGE_SIZE(buffer_, inlineCapacity, size_, other.size_);
+ ANNOTATE_CHANGE_SIZE(other.buffer_, inlineCapacity, other.size_, size_);
+ std::swap(size_, other.size_);
+ }
+
+ // We are ready to move elements. We determine an action for each "section",
+ // which is a contiguous range such that all elements in the range are
+ // treated similarly.
+ size_t section_begin = 0;
+ while (section_begin < inlineCapacity) {
+ // To determine the end of this section, we list up all the boundaries
+ // where the "occupiedness" may change.
+ size_t section_end = inlineCapacity;
+ if (this_source_begin && section_begin < this_source_size)
+ section_end = std::min(section_end, this_source_size);
+ if (!this_hole.empty() && section_begin < this_hole.begin)
+ section_end = std::min(section_end, this_hole.begin);
+ if (!this_hole.empty() && section_begin < this_hole.end)
+ section_end = std::min(section_end, this_hole.end);
+ if (other_source_begin && section_begin < other_source_size)
+ section_end = std::min(section_end, other_source_size);
+ if (!other_hole.empty() && section_begin < other_hole.begin)
+ section_end = std::min(section_end, other_hole.begin);
+ if (!other_hole.empty() && section_begin < other_hole.end)
+ section_end = std::min(section_end, other_hole.end);
+
+ DCHECK_LT(section_begin, section_end);
+
+ // Is the |sectionBegin|-th element of |thisSource| occupied?
+ bool this_occupied = false;
+ if (this_source_begin && section_begin < this_source_size) {
+ // Yes, it's occupied, unless the position is in a hole.
+ if (this_hole.empty() || section_begin < this_hole.begin ||
+ section_begin >= this_hole.end)
+ this_occupied = true;
+ }
+ bool other_occupied = false;
+ if (other_source_begin && section_begin < other_source_size) {
+ if (other_hole.empty() || section_begin < other_hole.begin ||
+ section_begin >= other_hole.end)
+ other_occupied = true;
+ }
+
+ if (this_occupied && other_occupied) {
+ // Both occupied; swap them. In this case, one's destination must be the
+ // other's source (i.e. both ranges are in inline buffers).
+ DCHECK_EQ(this_destination_begin, other_source_begin);
+ DCHECK_EQ(other_destination_begin, this_source_begin);
+ TypeOperations::Swap(this_source_begin + section_begin,
+ this_source_begin + section_end,
+ other_source_begin + section_begin);
+ } else if (this_occupied) {
+ // Move from ours to theirs.
+ TypeOperations::Move(this_source_begin + section_begin,
+ this_source_begin + section_end,
+ this_destination_begin + section_begin);
+ Base::ClearUnusedSlots(this_source_begin + section_begin,
+ this_source_begin + section_end);
+ } else if (other_occupied) {
+ // Move from theirs to ours.
+ TypeOperations::Move(other_source_begin + section_begin,
+ other_source_begin + section_end,
+ other_destination_begin + section_begin);
+ Base::ClearUnusedSlots(other_source_begin + section_begin,
+ other_source_begin + section_end);
+ } else {
+ // Both empty; nothing to do.
+ }
+
+ section_begin = section_end;
+ }
+
+ Allocator::LeaveGCForbiddenScope();
+ }
+
+ using Base::Buffer;
+ using Base::capacity;
+
+ bool HasOutOfLineBuffer() const {
+ return Buffer() && Buffer() != InlineBuffer();
+ }
+
+ T** BufferSlot() { return &buffer_; }
+
+ protected:
+ using Base::size_;
+
+ private:
+ using Base::buffer_;
+ using Base::capacity_;
+
+ static const size_t kInlineBufferSize = inlineCapacity * sizeof(T);
+ T* InlineBuffer() { return unsafe_reinterpret_cast_ptr<T*>(inline_buffer_); }
+ const T* InlineBuffer() const {
+ return unsafe_reinterpret_cast_ptr<const T*>(inline_buffer_);
+ }
+
+ alignas(T) char inline_buffer_[kInlineBufferSize];
+ template <typename U, size_t inlineBuffer, typename V>
+ friend class Deque;
+
+ DISALLOW_COPY_AND_ASSIGN(VectorBuffer);
+};
+
+//
+// Vector
+//
+
+// Vector is a container that works just like std::vector. WTF's Vector has
+// several extra functionalities: inline buffer, behavior customization via
+// traits, and Oilpan support. Those are explained in the sections below.
+//
+// Vector is the most basic container, which stores its element in a contiguous
+// buffer. The buffer is expanded automatically when necessary. The elements
+// are automatically moved to the new buffer. This event is called a
+// reallocation. A reallocation takes O(N)-time (N = number of elements), but
+// its occurrences are rare, so its time cost should not be significant,
+// compared to the time cost of other operations to the vector.
+//
+// Time complexity of key operations is as follows:
+//
+// * Indexed access -- O(1)
+// * Insertion or removal of an element at the end -- amortized O(1)
+// * Other insertion or removal -- O(N)
+// * Swapping with another vector -- O(1)
+//
+// 1. Iterator invalidation semantics
+//
+// Vector provides STL-compatible iterators and reverse iterators. Iterators
+// are _invalidated_ on certain occasions. Reading an invalidated iterator
+// causes undefined behavior.
+//
+// Iterators are invalidated on the following situations:
+//
+// * When a reallocation happens on a vector, all the iterators for that
+// vector will be invalidated.
+// * Some member functions invalidate part of the existing iterators for
+// the vector; see comments on the individual functions.
+// * [Oilpan only] Heap compaction invalidates all the iterators for any
+// HeapVectors. This means you can only store an iterator on stack, as
+// a local variable.
+//
+// In this context, pointers or references to an element of a Vector are
+// essentially equivalent to iterators, in that they also become invalid
+// whenever corresponding iterators are invalidated.
+//
+// 2. Inline buffer
+//
+// Vectors may have an _inline buffer_. An inline buffer is a storage area
+// that is contained in the vector itself, along with other metadata like
+// size_. It is used as a storage space when the vector's elements fit in
+// that space. If the inline buffer becomes full and further space is
+// necessary, an out-of-line buffer is allocated in the heap, and it will
+// take over the role of the inline buffer.
+//
+// The existence of an inline buffer is indicated by non-zero |inlineCapacity|
+// template argument. The value represents the number of elements that can be
+// stored in the inline buffer. Zero |inlineCapacity| means the vector has no
+// inline buffer.
+//
+// An inline buffer increases the size of the Vector instances, and, in trade
+// for that, it gives you several performance benefits, as long as the number
+// of elements do not exceed |inlineCapacity|:
+//
+// * No heap allocation will be made.
+// * Memory locality will improve.
+//
+// Generally, having an inline buffer is useful for vectors that (1) are
+// frequently accessed or modified, and (2) contain only a few elements at
+// most.
+//
+// 3. Behavior customization
+//
+// You usually do not need to customize Vector's behavior, since the default
+// behavior is appropriate for normal usage. The behavior is controlled by
+// VectorTypeOperations traits template above. Read VectorTypeOperations
+// and VectorTraits if you want to change the behavior for your types (i.e.
+// if you really want faster vector operations).
+//
+// The default traits basically do the following:
+//
+// * Skip constructor call and fill zeros with memset for simple types;
+// * Skip destructor call for simple types;
+// * Copy or move by memcpy for simple types; and
+// * Customize the comparisons for smart pointer types, so you can look
+// up a std::unique_ptr<T> element with a raw pointer, for instance.
+//
+// 4. Oilpan
+//
+// If you want to store garbage collected objects in Vector, (1) use HeapVector
+// (defined in HeapAllocator.h) instead of Vector, and (2) make sure your
+// garbage-collected type is wrapped with Member, like:
+//
+// HeapVector<Member<Node>> nodes;
+//
+// Unlike normal garbage-collected objects, a HeapVector object itself is
+// NOT a garbage-collected object, but its backing buffer is allocated in
+// Oilpan heap, and it may still carry garbage-collected objects.
+//
+// Even though a HeapVector object is not garbage-collected, you still need
+// to trace it, if you stored it in your class. Also, you can allocate it
+// as a local variable. This is useful when you want to build a vector locally
+// and put it in an on-heap vector with swap().
+//
+// Also, heap compaction, which may happen at any time when Blink code is not
+// running (i.e. Blink code does not appear in the call stack), may invalidate
+// existing iterators for any HeapVectors. So, essentially, you should always
+// allocate an iterator on stack (as a local variable), and you should not
+// store iterators in another heap object.
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+class Vector
+ : private VectorBuffer<T, INLINE_CAPACITY, Allocator>,
+ // Heap-allocated vectors with no inlineCapacity never need a destructor.
+ public ConditionalDestructor<Vector<T, INLINE_CAPACITY, Allocator>,
+ (INLINE_CAPACITY == 0) &&
+ Allocator::kIsGarbageCollected> {
+ USE_ALLOCATOR(Vector, Allocator);
+ using Base = VectorBuffer<T, INLINE_CAPACITY, Allocator>;
+ using TypeOperations = VectorTypeOperations<T, Allocator>;
+ using OffsetRange = typename Base::OffsetRange;
+
+ public:
+ using ValueType = T;
+ using value_type = T;
+
+ using iterator = T*;
+ using const_iterator = const T*;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+ // Create an empty vector.
+ inline Vector();
+ // Create a vector containing the specified number of default-initialized
+ // elements.
+ inline explicit Vector(size_t);
+ // Create a vector containing the specified number of elements, each of which
+ // is copy initialized from the specified value.
+ inline Vector(size_t, const T&);
+
+ // HashTable support
+ Vector(HashTableDeletedValueType value) : Base(value) {}
+ bool IsHashTableDeletedValue() const {
+ return Base::IsHashTableDeletedValue();
+ }
+
+ // Copying.
+ Vector(const Vector&);
+ template <size_t otherCapacity>
+ explicit Vector(const Vector<T, otherCapacity, Allocator>&);
+
+ Vector& operator=(const Vector&);
+ template <size_t otherCapacity>
+ Vector& operator=(const Vector<T, otherCapacity, Allocator>&);
+
+ // Moving.
+ Vector(Vector&&);
+ Vector& operator=(Vector&&);
+
+ // Construct with an initializer list. You can do e.g.
+ // Vector<int> v({1, 2, 3});
+ // or
+ // v = {4, 5, 6};
+ Vector(std::initializer_list<T> elements);
+ Vector& operator=(std::initializer_list<T> elements);
+
+ // Basic inquiry about the vector's state.
+ //
+ // capacity() is the maximum number of elements that the Vector can hold
+ // without a reallocation. It can be zero.
+ size_t size() const { return size_; }
+ size_t capacity() const { return Base::capacity(); }
+ bool IsEmpty() const { return !size(); }
+
+ // at() and operator[]: Obtain the reference of the element that is located
+ // at the given index. The reference may be invalidated on a reallocation.
+ //
+ // at() can be used in cases like:
+ // pointerToVector->at(1);
+ // instead of:
+ // (*pointerToVector)[1];
+ T& at(size_t i) {
+ CHECK_LT(i, size());
+ return Base::Buffer()[i];
+ }
+ const T& at(size_t i) const {
+ CHECK_LT(i, size());
+ return Base::Buffer()[i];
+ }
+
+ T& operator[](size_t i) { return at(i); }
+ const T& operator[](size_t i) const { return at(i); }
+
+ // Return a pointer to the front of the backing buffer. Those pointers get
+ // invalidated on a reallocation.
+ T* data() { return Base::Buffer(); }
+ const T* data() const { return Base::Buffer(); }
+
+ // Iterators and reverse iterators. They are invalidated on a reallocation.
+ iterator begin() { return data(); }
+ iterator end() { return begin() + size_; }
+ const_iterator begin() const { return data(); }
+ const_iterator end() const { return begin() + size_; }
+
+ reverse_iterator rbegin() { return reverse_iterator(end()); }
+ reverse_iterator rend() { return reverse_iterator(begin()); }
+ const_reverse_iterator rbegin() const {
+ return const_reverse_iterator(end());
+ }
+ const_reverse_iterator rend() const {
+ return const_reverse_iterator(begin());
+ }
+
+ // Quick access to the first and the last element. It is invalid to call
+ // these functions when the vector is empty.
+ T& front() { return at(0); }
+ const T& front() const { return at(0); }
+ T& back() { return at(size() - 1); }
+ const T& back() const { return at(size() - 1); }
+
+ // Searching.
+ //
+ // Comparisons are done in terms of compareElement(), which is usually
+ // operator==(). find() and reverseFind() returns an index of the element
+ // that is found first. If no match is found, kNotFound will be returned.
+ template <typename U>
+ bool Contains(const U&) const;
+ template <typename U>
+ size_t Find(const U&) const;
+ template <typename U>
+ size_t ReverseFind(const U&) const;
+
+ // Resize the vector to the specified size.
+ //
+ // These three functions are essentially similar. They differ in that
+ // (1) shrink() has a DCHECK to make sure the specified size is not more than
+ // size(), and (2) grow() has a DCHECK to make sure the specified size is
+ // not less than size().
+ //
+ // When a vector shrinks, the extra elements in the back will be destructed.
+ // All the iterators pointing to a to-be-destructed element will be
+ // invalidated.
+ //
+ // When a vector grows, new elements will be added in the back, and they
+ // will be default-initialized. A reallocation may happen in this case.
+ void Shrink(size_t);
+ void Grow(size_t);
+ void resize(size_t);
+
+ // Increase the capacity of the vector to at least |newCapacity|. The
+ // elements in the vector are not affected. This function does not shrink
+ // the size of the backing buffer, even if |newCapacity| is small. This
+ // function may cause a reallocation.
+ void ReserveCapacity(size_t new_capacity);
+
+ // This is similar to reserveCapacity() but must be called immediately after
+ // the vector is default-constructed.
+ void ReserveInitialCapacity(size_t initial_capacity);
+
+ // Shrink the backing buffer so it can contain exactly |size()| elements.
+ // This function may cause a reallocation.
+ void ShrinkToFit() { ShrinkCapacity(size()); }
+
+ // Shrink the backing buffer if at least 50% of the vector's capacity is
+ // unused. If it shrinks, the new buffer contains roughly 25% of unused
+ // space. This function may cause a reallocation.
+ void ShrinkToReasonableCapacity() {
+ if (size() * 2 < capacity())
+ ShrinkCapacity(size() + size() / 4 + 1);
+ }
+
+ // Remove all the elements. This function actually releases the backing
+ // buffer, thus any iterators will get invalidated (including begin()).
+ void clear() { ShrinkCapacity(0); }
+
+ // Insertion to the back. All of these functions except uncheckedAppend() may
+ // cause a reallocation.
+ //
+ // push_back(value)
+ // Insert a single element to the back.
+ // emplace_back(args...)
+ // Insert a single element constructed as T(args...) to the back. The
+ // element is constructed directly on the backing buffer with placement
+ // new.
+ // append(buffer, size)
+ // appendVector(vector)
+ // appendRange(begin, end)
+ // Insert multiple elements represented by (1) |buffer| and |size|
+ // (for append), (2) |vector| (for appendVector), or (3) a pair of
+ // iterators (for appendRange) to the back. The elements will be copied.
+ // uncheckedAppend(value)
+ // Insert a single element like push_back(), but this function assumes
+ // the vector has enough capacity such that it can store the new element
+ // without a reallocation. Using this function could improve the
+ // performance when you append many elements repeatedly.
+ template <typename U>
+ void push_back(U&&);
+ template <typename... Args>
+ T& emplace_back(Args&&...);
+ ALWAYS_INLINE T& emplace_back() {
+ Grow(size_ + 1);
+ return back();
+ }
+ template <typename U>
+ void Append(const U*, size_t);
+ template <typename U, size_t otherCapacity, typename V>
+ void AppendVector(const Vector<U, otherCapacity, V>&);
+ template <typename Iterator>
+ void AppendRange(Iterator begin, Iterator end);
+ template <typename U>
+ void UncheckedAppend(U&&);
+
+ // Insertion to an arbitrary position. All of these functions will take
+ // O(size())-time. All of the elements after |position| will be moved to
+ // the new locations. |position| must be no more than size(). All of these
+ // functions may cause a reallocation. In any case, all the iterators
+ // pointing to an element after |position| will be invalidated.
+ //
+ // insert(position, value)
+ // Insert a single element at |position|.
+ // insert(position, buffer, size)
+ // InsertVector(position, vector)
+ // Insert multiple elements represented by either |buffer| and |size|
+ // or |vector| at |position|. The elements will be copied.
+ template <typename U>
+ void insert(size_t position, U&&);
+ template <typename U>
+ void insert(size_t position, const U*, size_t);
+ template <typename U, size_t otherCapacity, typename OtherAllocator>
+ void InsertVector(size_t position,
+ const Vector<U, otherCapacity, OtherAllocator>&);
+
+ // Insertion to the front. All of these functions will take O(size())-time.
+ // All of the elements in the vector will be moved to the new locations.
+ // All of these functions may cause a reallocation. In any case, all the
+ // iterators pointing to any element in the vector will be invalidated.
+ //
+ // push_front(value)
+ // Insert a single element to the front.
+ // push_front(buffer, size)
+ // prependVector(vector)
+ // Insert multiple elements represented by either |buffer| and |size| or
+ // |vector| to the front. The elements will be copied.
+ template <typename U>
+ void push_front(U&&);
+ template <typename U>
+ void push_front(const U*, size_t);
+ template <typename U, size_t otherCapacity, typename OtherAllocator>
+ void PrependVector(const Vector<U, otherCapacity, OtherAllocator>&);
+
+ // Remove an element or elements at the specified position. These functions
+ // take O(size())-time. All of the elements after the removed ones will be
+ // moved to the new locations. All the iterators pointing to any element
+ // after |position| will be invalidated.
+ void EraseAt(size_t position);
+ void EraseAt(size_t position, size_t length);
+ iterator erase(iterator position);
+
+ // Remove the last element. Unlike remove(), (1) this function is fast, and
+ // (2) only iterators pointing to the last element will be invalidated. Other
+ // references will remain valid.
+ void pop_back() {
+ DCHECK(!IsEmpty());
+ Shrink(size() - 1);
+ }
+
+ // Filling the vector with the same value. If the vector has shrinked or
+ // growed as a result of this call, those events may invalidate some
+ // iterators. See comments for shrink() and grow().
+ //
+ // fill(value, size) will resize the Vector to |size|, and then copy-assign
+ // or copy-initialize all the elements.
+ //
+ // fill(value) is a synonym for fill(value, size()).
+ void Fill(const T&, size_t);
+ void Fill(const T& val) { Fill(val, size()); }
+
+ // Swap two vectors quickly.
+ void swap(Vector& other) {
+ Base::SwapVectorBuffer(other, OffsetRange(), OffsetRange());
+ }
+
+ // Reverse the contents.
+ void Reverse();
+
+ // Maximum element count supported; allocating a vector
+ // buffer with a larger count will fail.
+ static size_t MaxCapacity() {
+ return Allocator::template MaxElementCountInBackingStore<T>();
+ }
+
+ // Off-GC-heap vectors: Destructor should be called.
+ // On-GC-heap vectors: Destructor should be called for inline buffers (if
+ // any) but destructor shouldn't be called for vector backing since it is
+ // managed by the traced GC heap.
+ void Finalize() {
+ if (!INLINE_CAPACITY) {
+ if (LIKELY(!Base::Buffer()))
+ return;
+ }
+ ANNOTATE_DELETE_BUFFER(begin(), capacity(), size_);
+ if (LIKELY(size_) &&
+ !(Allocator::kIsGarbageCollected && this->HasOutOfLineBuffer())) {
+ TypeOperations::Destruct(begin(), end());
+ size_ = 0; // Partial protection against use-after-free.
+ }
+
+ Base::Destruct();
+ }
+
+ void FinalizeGarbageCollectedObject() { Finalize(); }
+
+ template <typename VisitorDispatcher, typename A = Allocator>
+ std::enable_if_t<A::kIsGarbageCollected> Trace(VisitorDispatcher);
+
+ class GCForbiddenScope {
+ STACK_ALLOCATED();
+
+ public:
+ GCForbiddenScope() { Allocator::EnterGCForbiddenScope(); }
+ ~GCForbiddenScope() { Allocator::LeaveGCForbiddenScope(); }
+ };
+
+ protected:
+ using Base::CheckUnusedSlots;
+ using Base::ClearUnusedSlots;
+
+ private:
+ void ExpandCapacity(size_t new_min_capacity);
+ T* ExpandCapacity(size_t new_min_capacity, T*);
+ T* ExpandCapacity(size_t new_min_capacity, const T* data) {
+ return ExpandCapacity(new_min_capacity, const_cast<T*>(data));
+ }
+
+ template <typename U>
+ U* ExpandCapacity(size_t new_min_capacity, U*);
+ void ShrinkCapacity(size_t new_capacity);
+ template <typename U>
+ void AppendSlowCase(U&&);
+
+ // This is to prevent compilation of deprecated calls like 'vector.erase(0)'.
+ void erase(std::nullptr_t) = delete;
+
+ using Base::size_;
+ using Base::Buffer;
+ using Base::SwapVectorBuffer;
+ using Base::AllocateBuffer;
+ using Base::AllocationSize;
+};
+
+//
+// Vector out-of-line implementation
+//
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline Vector<T, inlineCapacity, Allocator>::Vector() {
+ static_assert(!std::is_polymorphic<T>::value ||
+ !VectorTraits<T>::kCanInitializeWithMemset,
+ "Cannot initialize with memset if there is a vtable");
+ static_assert(Allocator::kIsGarbageCollected ||
+ !AllowsOnlyPlacementNew<T>::value || !IsTraceable<T>::value,
+ "Cannot put DISALLOW_NEW_EXCEPT_PLACEMENT_NEW objects that "
+ "have trace methods into an off-heap Vector");
+ static_assert(Allocator::kIsGarbageCollected ||
+ !IsPointerToGarbageCollectedType<T>::value,
+ "Cannot put raw pointers to garbage-collected classes into "
+ "an off-heap Vector. Use HeapVector<Member<T>> instead.");
+
+ ANNOTATE_NEW_BUFFER(begin(), capacity(), 0);
+ size_ = 0;
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline Vector<T, inlineCapacity, Allocator>::Vector(size_t size) : Base(size) {
+ static_assert(!std::is_polymorphic<T>::value ||
+ !VectorTraits<T>::kCanInitializeWithMemset,
+ "Cannot initialize with memset if there is a vtable");
+ static_assert(Allocator::kIsGarbageCollected ||
+ !AllowsOnlyPlacementNew<T>::value || !IsTraceable<T>::value,
+ "Cannot put DISALLOW_NEW_EXCEPT_PLACEMENT_NEW objects that "
+ "have trace methods into an off-heap Vector");
+ static_assert(Allocator::kIsGarbageCollected ||
+ !IsPointerToGarbageCollectedType<T>::value,
+ "Cannot put raw pointers to garbage-collected classes into "
+ "an off-heap Vector. Use HeapVector<Member<T>> instead.");
+
+ ANNOTATE_NEW_BUFFER(begin(), capacity(), size);
+ size_ = size;
+ TypeOperations::Initialize(begin(), end());
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline Vector<T, inlineCapacity, Allocator>::Vector(size_t size, const T& val)
+ : Base(size) {
+ // TODO(yutak): Introduce these assertions. Some use sites call this function
+ // in the context where T is an incomplete type.
+ //
+ // static_assert(!std::is_polymorphic<T>::value ||
+ // !VectorTraits<T>::canInitializeWithMemset,
+ // "Cannot initialize with memset if there is a vtable");
+ // static_assert(Allocator::isGarbageCollected ||
+ // !AllowsOnlyPlacementNew<T>::value ||
+ // !IsTraceable<T>::value,
+ // "Cannot put DISALLOW_NEW_EXCEPT_PLACEMENT_NEW objects that "
+ // "have trace methods into an off-heap Vector");
+ // static_assert(Allocator::isGarbageCollected ||
+ // !IsPointerToGarbageCollectedType<T>::value,
+ // "Cannot put raw pointers to garbage-collected classes into "
+ // "an off-heap Vector. Use HeapVector<Member<T>> instead.");
+
+ ANNOTATE_NEW_BUFFER(begin(), capacity(), size);
+ size_ = size;
+ TypeOperations::UninitializedFill(begin(), end(), val);
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+Vector<T, inlineCapacity, Allocator>::Vector(const Vector& other)
+ : Base(other.capacity()) {
+ ANNOTATE_NEW_BUFFER(begin(), capacity(), other.size());
+ size_ = other.size();
+ TypeOperations::UninitializedCopy(other.begin(), other.end(), begin());
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+template <size_t otherCapacity>
+Vector<T, inlineCapacity, Allocator>::Vector(
+ const Vector<T, otherCapacity, Allocator>& other)
+ : Base(other.capacity()) {
+ ANNOTATE_NEW_BUFFER(begin(), capacity(), other.size());
+ size_ = other.size();
+ TypeOperations::UninitializedCopy(other.begin(), other.end(), begin());
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+Vector<T, inlineCapacity, Allocator>& Vector<T, inlineCapacity, Allocator>::
+operator=(const Vector<T, inlineCapacity, Allocator>& other) {
+ if (UNLIKELY(&other == this))
+ return *this;
+
+ if (size() > other.size()) {
+ Shrink(other.size());
+ } else if (other.size() > capacity()) {
+ clear();
+ ReserveCapacity(other.size());
+ DCHECK(begin());
+ }
+
+ ANNOTATE_CHANGE_SIZE(begin(), capacity(), size_, other.size());
+ std::copy(other.begin(), other.begin() + size(), begin());
+ TypeOperations::UninitializedCopy(other.begin() + size(), other.end(), end());
+ size_ = other.size();
+
+ return *this;
+}
+
+inline bool TypelessPointersAreEqual(const void* a, const void* b) {
+ return a == b;
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+template <size_t otherCapacity>
+Vector<T, inlineCapacity, Allocator>& Vector<T, inlineCapacity, Allocator>::
+operator=(const Vector<T, otherCapacity, Allocator>& other) {
+ // If the inline capacities match, we should call the more specific
+ // template. If the inline capacities don't match, the two objects
+ // shouldn't be allocated the same address.
+ DCHECK(!TypelessPointersAreEqual(&other, this));
+
+ if (size() > other.size()) {
+ Shrink(other.size());
+ } else if (other.size() > capacity()) {
+ clear();
+ ReserveCapacity(other.size());
+ DCHECK(begin());
+ }
+
+ ANNOTATE_CHANGE_SIZE(begin(), capacity(), size_, other.size());
+ std::copy(other.begin(), other.begin() + size(), begin());
+ TypeOperations::UninitializedCopy(other.begin() + size(), other.end(), end());
+ size_ = other.size();
+
+ return *this;
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+Vector<T, inlineCapacity, Allocator>::Vector(
+ Vector<T, inlineCapacity, Allocator>&& other) {
+ size_ = 0;
+ // It's a little weird to implement a move constructor using swap but this
+ // way we don't have to add a move constructor to VectorBuffer.
+ swap(other);
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+Vector<T, inlineCapacity, Allocator>& Vector<T, inlineCapacity, Allocator>::
+operator=(Vector<T, inlineCapacity, Allocator>&& other) {
+ swap(other);
+ return *this;
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+Vector<T, inlineCapacity, Allocator>::Vector(std::initializer_list<T> elements)
+ : Base(elements.size()) {
+ ANNOTATE_NEW_BUFFER(begin(), capacity(), elements.size());
+ size_ = elements.size();
+ TypeOperations::UninitializedCopy(elements.begin(), elements.end(), begin());
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+Vector<T, inlineCapacity, Allocator>& Vector<T, inlineCapacity, Allocator>::
+operator=(std::initializer_list<T> elements) {
+ if (size() > elements.size()) {
+ Shrink(elements.size());
+ } else if (elements.size() > capacity()) {
+ clear();
+ ReserveCapacity(elements.size());
+ DCHECK(begin());
+ }
+
+ ANNOTATE_CHANGE_SIZE(begin(), capacity(), size_, elements.size());
+ std::copy(elements.begin(), elements.begin() + size_, begin());
+ TypeOperations::UninitializedCopy(elements.begin() + size_, elements.end(),
+ end());
+ size_ = elements.size();
+
+ return *this;
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+template <typename U>
+bool Vector<T, inlineCapacity, Allocator>::Contains(const U& value) const {
+ return Find(value) != kNotFound;
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+template <typename U>
+size_t Vector<T, inlineCapacity, Allocator>::Find(const U& value) const {
+ const T* b = begin();
+ const T* e = end();
+ for (const T* iter = b; iter < e; ++iter) {
+ if (TypeOperations::CompareElement(*iter, value))
+ return iter - b;
+ }
+ return kNotFound;
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+template <typename U>
+size_t Vector<T, inlineCapacity, Allocator>::ReverseFind(const U& value) const {
+ const T* b = begin();
+ const T* iter = end();
+ while (iter > b) {
+ --iter;
+ if (TypeOperations::CompareElement(*iter, value))
+ return iter - b;
+ }
+ return kNotFound;
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+void Vector<T, inlineCapacity, Allocator>::Fill(const T& val, size_t new_size) {
+ if (size() > new_size) {
+ Shrink(new_size);
+ } else if (new_size > capacity()) {
+ clear();
+ ReserveCapacity(new_size);
+ DCHECK(begin());
+ }
+
+ ANNOTATE_CHANGE_SIZE(begin(), capacity(), size_, new_size);
+ std::fill(begin(), end(), val);
+ TypeOperations::UninitializedFill(end(), begin() + new_size, val);
+ size_ = new_size;
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+void Vector<T, inlineCapacity, Allocator>::ExpandCapacity(
+ size_t new_min_capacity) {
+ size_t old_capacity = capacity();
+ size_t expanded_capacity = old_capacity;
+ // We use a more aggressive expansion strategy for Vectors with inline
+ // storage. This is because they are more likely to be on the stack, so the
+ // risk of heap bloat is minimized. Furthermore, exceeding the inline
+ // capacity limit is not supposed to happen in the common case and may
+ // indicate a pathological condition or microbenchmark.
+ if (INLINE_CAPACITY) {
+ expanded_capacity *= 2;
+ // Check for integer overflow, which could happen in the 32-bit build.
+ CHECK_GT(expanded_capacity, old_capacity);
+ } else {
+ // This cannot integer overflow.
+ // On 64-bit, the "expanded" integer is 32-bit, and any encroachment
+ // above 2^32 will fail allocation in allocateBuffer(). On 32-bit,
+ // there's not enough address space to hold the old and new buffers. In
+ // addition, our underlying allocator is supposed to always fail on >
+ // (2^31 - 1) allocations.
+ expanded_capacity += (expanded_capacity / 4) + 1;
+ }
+ ReserveCapacity(std::max(
+ new_min_capacity,
+ std::max(static_cast<size_t>(kInitialVectorSize), expanded_capacity)));
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+T* Vector<T, inlineCapacity, Allocator>::ExpandCapacity(size_t new_min_capacity,
+ T* ptr) {
+ if (ptr < begin() || ptr >= end()) {
+ ExpandCapacity(new_min_capacity);
+ return ptr;
+ }
+ size_t index = ptr - begin();
+ ExpandCapacity(new_min_capacity);
+ return begin() + index;
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+template <typename U>
+inline U* Vector<T, inlineCapacity, Allocator>::ExpandCapacity(
+ size_t new_min_capacity,
+ U* ptr) {
+ ExpandCapacity(new_min_capacity);
+ return ptr;
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline void Vector<T, inlineCapacity, Allocator>::resize(size_t size) {
+ if (size <= size_) {
+ TypeOperations::Destruct(begin() + size, end());
+ ClearUnusedSlots(begin() + size, end());
+ ANNOTATE_CHANGE_SIZE(begin(), capacity(), size_, size);
+ } else {
+ if (size > capacity())
+ ExpandCapacity(size);
+ ANNOTATE_CHANGE_SIZE(begin(), capacity(), size_, size);
+ TypeOperations::Initialize(end(), begin() + size);
+ }
+
+ size_ = size;
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+void Vector<T, inlineCapacity, Allocator>::Shrink(size_t size) {
+ DCHECK_LE(size, size_);
+ TypeOperations::Destruct(begin() + size, end());
+ ClearUnusedSlots(begin() + size, end());
+ ANNOTATE_CHANGE_SIZE(begin(), capacity(), size_, size);
+ size_ = size;
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+void Vector<T, inlineCapacity, Allocator>::Grow(size_t size) {
+ DCHECK_GE(size, size_);
+ if (size > capacity())
+ ExpandCapacity(size);
+ ANNOTATE_CHANGE_SIZE(begin(), capacity(), size_, size);
+ TypeOperations::Initialize(end(), begin() + size);
+ size_ = size;
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+void Vector<T, inlineCapacity, Allocator>::ReserveCapacity(
+ size_t new_capacity) {
+ if (UNLIKELY(new_capacity <= capacity()))
+ return;
+ T* old_buffer = begin();
+ if (!old_buffer) {
+ Base::AllocateBuffer(new_capacity);
+ return;
+ }
+#ifdef ANNOTATE_CONTIGUOUS_CONTAINER
+ size_t old_capacity = capacity();
+#endif
+ // The Allocator::isGarbageCollected check is not needed. The check is just
+ // a static hint for a compiler to indicate that Base::expandBuffer returns
+ // false if Allocator is a PartitionAllocator.
+ if (Allocator::kIsGarbageCollected && Base::ExpandBuffer(new_capacity)) {
+ ANNOTATE_CHANGE_CAPACITY(begin(), old_capacity, size_, capacity());
+ return;
+ }
+ // Reallocating a backing buffer may resurrect a dead object.
+ CHECK(!Allocator::IsObjectResurrectionForbidden());
+
+ T* old_end = end();
+ Base::AllocateExpandedBuffer(new_capacity);
+ ANNOTATE_NEW_BUFFER(begin(), capacity(), size_);
+ TypeOperations::Move(old_buffer, old_end, begin());
+ ClearUnusedSlots(old_buffer, old_end);
+ ANNOTATE_DELETE_BUFFER(old_buffer, old_capacity, size_);
+ Base::DeallocateBuffer(old_buffer);
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline void Vector<T, inlineCapacity, Allocator>::ReserveInitialCapacity(
+ size_t initial_capacity) {
+ DCHECK(!size_);
+ DCHECK(capacity() == INLINE_CAPACITY);
+ if (initial_capacity > INLINE_CAPACITY) {
+ ANNOTATE_DELETE_BUFFER(begin(), capacity(), size_);
+ Base::AllocateBuffer(initial_capacity);
+ ANNOTATE_NEW_BUFFER(begin(), capacity(), size_);
+ }
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+void Vector<T, inlineCapacity, Allocator>::ShrinkCapacity(size_t new_capacity) {
+ if (new_capacity >= capacity())
+ return;
+
+ if (new_capacity < size())
+ Shrink(new_capacity);
+
+ T* old_buffer = begin();
+#ifdef ANNOTATE_CONTIGUOUS_CONTAINER
+ size_t old_capacity = capacity();
+#endif
+ if (new_capacity > 0) {
+ if (Base::ShrinkBuffer(new_capacity)) {
+ ANNOTATE_CHANGE_CAPACITY(begin(), old_capacity, size_, capacity());
+ return;
+ }
+
+ if (Allocator::IsObjectResurrectionForbidden())
+ return;
+
+ T* old_end = end();
+ Base::AllocateBuffer(new_capacity);
+ if (begin() != old_buffer) {
+ ANNOTATE_NEW_BUFFER(begin(), capacity(), size_);
+ TypeOperations::Move(old_buffer, old_end, begin());
+ ClearUnusedSlots(old_buffer, old_end);
+ ANNOTATE_DELETE_BUFFER(old_buffer, old_capacity, size_);
+ }
+ } else {
+ Base::ResetBufferPointer();
+#ifdef ANNOTATE_CONTIGUOUS_CONTAINER
+ if (old_buffer != begin()) {
+ ANNOTATE_NEW_BUFFER(begin(), capacity(), size_);
+ ANNOTATE_DELETE_BUFFER(old_buffer, old_capacity, size_);
+ }
+#endif
+ }
+
+ Base::DeallocateBuffer(old_buffer);
+}
+
+// Templatizing these is better than just letting the conversion happen
+// implicitly.
+template <typename T, size_t inlineCapacity, typename Allocator>
+template <typename U>
+ALWAYS_INLINE void Vector<T, inlineCapacity, Allocator>::push_back(U&& val) {
+ DCHECK(Allocator::IsAllocationAllowed());
+ if (LIKELY(size() != capacity())) {
+ ANNOTATE_CHANGE_SIZE(begin(), capacity(), size_, size_ + 1);
+ ConstructTraits<T, VectorTraits<T>, Allocator>::ConstructAndNotifyElement(
+ end(), std::forward<U>(val));
+ ++size_;
+ return;
+ }
+
+ AppendSlowCase(std::forward<U>(val));
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+template <typename... Args>
+ALWAYS_INLINE T& Vector<T, inlineCapacity, Allocator>::emplace_back(
+ Args&&... args) {
+ DCHECK(Allocator::IsAllocationAllowed());
+ if (UNLIKELY(size() == capacity()))
+ ExpandCapacity(size() + 1);
+
+ ANNOTATE_CHANGE_SIZE(begin(), capacity(), size_, size_ + 1);
+ T* t =
+ ConstructTraits<T, VectorTraits<T>, Allocator>::ConstructAndNotifyElement(
+ end(), std::forward<Args>(args)...);
+ ++size_;
+ return *t;
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+template <typename U>
+void Vector<T, inlineCapacity, Allocator>::Append(const U* data,
+ size_t data_size) {
+ DCHECK(Allocator::IsAllocationAllowed());
+ size_t new_size = size_ + data_size;
+ if (new_size > capacity()) {
+ data = ExpandCapacity(new_size, data);
+ DCHECK(begin());
+ }
+ CHECK_GE(new_size, size_);
+ T* dest = end();
+ ANNOTATE_CHANGE_SIZE(begin(), capacity(), size_, new_size);
+ VectorCopier<VectorTraits<T>::kCanCopyWithMemcpy, T,
+ Allocator>::UninitializedCopy(data, &data[data_size], dest);
+ size_ = new_size;
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+template <typename U>
+NEVER_INLINE void Vector<T, inlineCapacity, Allocator>::AppendSlowCase(
+ U&& val) {
+ DCHECK_EQ(size(), capacity());
+
+ typename std::remove_reference<U>::type* ptr = &val;
+ ptr = ExpandCapacity(size() + 1, ptr);
+ DCHECK(begin());
+
+ ANNOTATE_CHANGE_SIZE(begin(), capacity(), size_, size_ + 1);
+ ConstructTraits<T, VectorTraits<T>, Allocator>::ConstructAndNotifyElement(
+ end(), std::forward<U>(*ptr));
+ ++size_;
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+template <typename U, size_t otherCapacity, typename OtherAllocator>
+inline void Vector<T, inlineCapacity, Allocator>::AppendVector(
+ const Vector<U, otherCapacity, OtherAllocator>& val) {
+ Append(val.begin(), val.size());
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+template <typename Iterator>
+void Vector<T, inlineCapacity, Allocator>::AppendRange(Iterator begin,
+ Iterator end) {
+ for (Iterator it = begin; it != end; ++it)
+ push_back(*it);
+}
+
+// This version of append saves a branch in the case where you know that the
+// vector's capacity is large enough for the append to succeed.
+template <typename T, size_t inlineCapacity, typename Allocator>
+template <typename U>
+ALWAYS_INLINE void Vector<T, inlineCapacity, Allocator>::UncheckedAppend(
+ U&& val) {
+#ifdef ANNOTATE_CONTIGUOUS_CONTAINER
+ // Vectors in ASAN builds don't have inlineCapacity.
+ push_back(std::forward<U>(val));
+#else
+ DCHECK_LT(size(), capacity());
+ ConstructTraits<T, VectorTraits<T>, Allocator>::ConstructAndNotifyElement(
+ end(), std::forward<U>(val));
+ ++size_;
+#endif
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+template <typename U>
+inline void Vector<T, inlineCapacity, Allocator>::insert(size_t position,
+ U&& val) {
+ DCHECK(Allocator::IsAllocationAllowed());
+ CHECK_LE(position, size());
+ typename std::remove_reference<U>::type* data = &val;
+ if (size() == capacity()) {
+ data = ExpandCapacity(size() + 1, data);
+ DCHECK(begin());
+ }
+ ANNOTATE_CHANGE_SIZE(begin(), capacity(), size_, size_ + 1);
+ T* spot = begin() + position;
+ TypeOperations::MoveOverlapping(spot, end(), spot + 1);
+ ConstructTraits<T, VectorTraits<T>, Allocator>::ConstructAndNotifyElement(
+ spot, std::forward<U>(*data));
+ ++size_;
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+template <typename U>
+void Vector<T, inlineCapacity, Allocator>::insert(size_t position,
+ const U* data,
+ size_t data_size) {
+ DCHECK(Allocator::IsAllocationAllowed());
+ CHECK_LE(position, size());
+ size_t new_size = size_ + data_size;
+ if (new_size > capacity()) {
+ data = ExpandCapacity(new_size, data);
+ DCHECK(begin());
+ }
+ CHECK_GE(new_size, size_);
+ ANNOTATE_CHANGE_SIZE(begin(), capacity(), size_, new_size);
+ T* spot = begin() + position;
+ TypeOperations::MoveOverlapping(spot, end(), spot + data_size);
+ VectorCopier<VectorTraits<T>::kCanCopyWithMemcpy, T,
+ Allocator>::UninitializedCopy(data, &data[data_size], spot);
+ size_ = new_size;
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+template <typename U, size_t otherCapacity, typename OtherAllocator>
+inline void Vector<T, inlineCapacity, Allocator>::InsertVector(
+ size_t position,
+ const Vector<U, otherCapacity, OtherAllocator>& val) {
+ insert(position, val.begin(), val.size());
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+template <typename U>
+inline void Vector<T, inlineCapacity, Allocator>::push_front(U&& val) {
+ insert(0, std::forward<U>(val));
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+template <typename U>
+void Vector<T, inlineCapacity, Allocator>::push_front(const U* data,
+ size_t data_size) {
+ insert(0, data, data_size);
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+template <typename U, size_t otherCapacity, typename OtherAllocator>
+inline void Vector<T, inlineCapacity, Allocator>::PrependVector(
+ const Vector<U, otherCapacity, OtherAllocator>& val) {
+ insert(0, val.begin(), val.size());
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline void Vector<T, inlineCapacity, Allocator>::EraseAt(size_t position) {
+ CHECK_LT(position, size());
+ T* spot = begin() + position;
+ spot->~T();
+ TypeOperations::MoveOverlapping(spot + 1, end(), spot);
+ ClearUnusedSlots(end() - 1, end());
+ ANNOTATE_CHANGE_SIZE(begin(), capacity(), size_, size_ - 1);
+ --size_;
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline auto Vector<T, inlineCapacity, Allocator>::erase(iterator position)
+ -> iterator {
+ size_t index = position - begin();
+ EraseAt(index);
+ return begin() + index;
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline void Vector<T, inlineCapacity, Allocator>::EraseAt(size_t position,
+ size_t length) {
+ SECURITY_DCHECK(position <= size());
+ if (!length)
+ return;
+ CHECK_LE(position + length, size());
+ T* begin_spot = begin() + position;
+ T* end_spot = begin_spot + length;
+ TypeOperations::Destruct(begin_spot, end_spot);
+ TypeOperations::MoveOverlapping(end_spot, end(), begin_spot);
+ ClearUnusedSlots(end() - length, end());
+ ANNOTATE_CHANGE_SIZE(begin(), capacity(), size_, size_ - length);
+ size_ -= length;
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline void Vector<T, inlineCapacity, Allocator>::Reverse() {
+ for (size_t i = 0; i < size_ / 2; ++i)
+ std::swap(at(i), at(size_ - 1 - i));
+}
+
+template <typename T, size_t inlineCapacity, typename Allocator>
+inline void swap(Vector<T, inlineCapacity, Allocator>& a,
+ Vector<T, inlineCapacity, Allocator>& b) {
+ a.Swap(b);
+}
+
+template <typename T,
+ size_t inlineCapacityA,
+ size_t inlineCapacityB,
+ typename Allocator>
+bool operator==(const Vector<T, inlineCapacityA, Allocator>& a,
+ const Vector<T, inlineCapacityB, Allocator>& b) {
+ if (a.size() != b.size())
+ return false;
+ if (a.IsEmpty())
+ return true;
+ return VectorTypeOperations<T, Allocator>::Compare(a.data(), b.data(),
+ a.size());
+}
+
+template <typename T,
+ size_t inlineCapacityA,
+ size_t inlineCapacityB,
+ typename Allocator>
+inline bool operator!=(const Vector<T, inlineCapacityA, Allocator>& a,
+ const Vector<T, inlineCapacityB, Allocator>& b) {
+ return !(a == b);
+}
+
+// Only defined for HeapAllocator. Used when visiting vector object.
+template <typename T, size_t inlineCapacity, typename Allocator>
+template <typename VisitorDispatcher, typename A>
+std::enable_if_t<A::kIsGarbageCollected>
+Vector<T, inlineCapacity, Allocator>::Trace(VisitorDispatcher visitor) {
+ static_assert(Allocator::kIsGarbageCollected,
+ "Garbage collector must be enabled.");
+ if (!Buffer())
+ return;
+ if (this->HasOutOfLineBuffer()) {
+ Allocator::TraceVectorBacking(visitor, Buffer(), Base::BufferSlot());
+ } else {
+ // Inline buffer requires tracing immediately.
+ const T* buffer_begin = Buffer();
+ const T* buffer_end = Buffer() + size();
+ if (IsTraceableInCollectionTrait<VectorTraits<T>>::value) {
+ for (const T* buffer_entry = buffer_begin; buffer_entry != buffer_end;
+ buffer_entry++) {
+ Allocator::template Trace<VisitorDispatcher, T, VectorTraits<T>>(
+ visitor, *const_cast<T*>(buffer_entry));
+ }
+ CheckUnusedSlots(Buffer() + size(), Buffer() + capacity());
+ }
+ }
+}
+
+} // namespace WTF
+
+namespace base {
+
+#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 7
+// Workaround for g++7 and earlier family.
+// Due to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80654, without this
+// Optional<WTF::Vector<T>> where T is non-copyable causes a compile error.
+// As we know it is not trivially copy constructible, explicitly declare so.
+//
+// It completes the declaration in base/template_util.h that was provided
+// for std::vector
+template <typename T>
+struct is_trivially_copy_constructible<WTF::Vector<T>> : std::false_type {};
+#endif
+
+} // namespace base
+
+using WTF::Vector;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_VECTOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/vector_test.cc b/chromium/third_party/blink/renderer/platform/wtf/vector_test.cc
new file mode 100644
index 00000000000..53302a62071
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/vector_test.cc
@@ -0,0 +1,676 @@
+/*
+ * Copyright (C) 2011 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "third_party/blink/renderer/platform/wtf/vector.h"
+
+#include <memory>
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_test_helper.h"
+
+namespace WTF {
+
+HashSet<void*> g_constructed_wrapped_ints;
+unsigned LivenessCounter::live_ = 0;
+
+namespace {
+
+TEST(VectorTest, Basic) {
+ Vector<int> int_vector;
+ EXPECT_TRUE(int_vector.IsEmpty());
+ EXPECT_EQ(0ul, int_vector.size());
+ EXPECT_EQ(0ul, int_vector.capacity());
+}
+
+TEST(VectorTest, Reverse) {
+ Vector<int> int_vector;
+ int_vector.push_back(10);
+ int_vector.push_back(11);
+ int_vector.push_back(12);
+ int_vector.push_back(13);
+ int_vector.Reverse();
+
+ EXPECT_EQ(13, int_vector[0]);
+ EXPECT_EQ(12, int_vector[1]);
+ EXPECT_EQ(11, int_vector[2]);
+ EXPECT_EQ(10, int_vector[3]);
+
+ int_vector.push_back(9);
+ int_vector.Reverse();
+
+ EXPECT_EQ(9, int_vector[0]);
+ EXPECT_EQ(10, int_vector[1]);
+ EXPECT_EQ(11, int_vector[2]);
+ EXPECT_EQ(12, int_vector[3]);
+ EXPECT_EQ(13, int_vector[4]);
+}
+
+TEST(VectorTest, EraseAtIndex) {
+ Vector<int> int_vector;
+ int_vector.push_back(0);
+ int_vector.push_back(1);
+ int_vector.push_back(2);
+ int_vector.push_back(3);
+
+ EXPECT_EQ(4u, int_vector.size());
+ EXPECT_EQ(0, int_vector[0]);
+ EXPECT_EQ(1, int_vector[1]);
+ EXPECT_EQ(2, int_vector[2]);
+ EXPECT_EQ(3, int_vector[3]);
+
+ int_vector.EraseAt(2, 0);
+ EXPECT_EQ(4u, int_vector.size());
+ EXPECT_EQ(2, int_vector[2]);
+
+ int_vector.EraseAt(2, 1);
+ EXPECT_EQ(3u, int_vector.size());
+ EXPECT_EQ(3, int_vector[2]);
+
+ int_vector.EraseAt(0, 0);
+ EXPECT_EQ(3u, int_vector.size());
+ EXPECT_EQ(0, int_vector[0]);
+
+ int_vector.EraseAt(0);
+ EXPECT_EQ(2u, int_vector.size());
+ EXPECT_EQ(1, int_vector[0]);
+}
+
+TEST(VectorTest, Erase) {
+ Vector<int> int_vector({0, 1, 2, 3});
+
+ EXPECT_EQ(4u, int_vector.size());
+ EXPECT_EQ(0, int_vector[0]);
+ EXPECT_EQ(1, int_vector[1]);
+ EXPECT_EQ(2, int_vector[2]);
+ EXPECT_EQ(3, int_vector[3]);
+
+ auto first = int_vector.erase(int_vector.begin());
+ EXPECT_EQ(3u, int_vector.size());
+ EXPECT_EQ(1, *first);
+ EXPECT_EQ(int_vector.begin(), first);
+
+ auto last = std::lower_bound(int_vector.begin(), int_vector.end(), 3);
+ auto end = int_vector.erase(last);
+ EXPECT_EQ(2u, int_vector.size());
+ EXPECT_EQ(int_vector.end(), end);
+}
+
+TEST(VectorTest, Iterator) {
+ Vector<int> int_vector;
+ int_vector.push_back(10);
+ int_vector.push_back(11);
+ int_vector.push_back(12);
+ int_vector.push_back(13);
+
+ Vector<int>::iterator it = int_vector.begin();
+ Vector<int>::iterator end = int_vector.end();
+ EXPECT_TRUE(end != it);
+
+ EXPECT_EQ(10, *it);
+ ++it;
+ EXPECT_EQ(11, *it);
+ ++it;
+ EXPECT_EQ(12, *it);
+ ++it;
+ EXPECT_EQ(13, *it);
+ ++it;
+
+ EXPECT_TRUE(end == it);
+}
+
+TEST(VectorTest, ReverseIterator) {
+ Vector<int> int_vector;
+ int_vector.push_back(10);
+ int_vector.push_back(11);
+ int_vector.push_back(12);
+ int_vector.push_back(13);
+
+ Vector<int>::reverse_iterator it = int_vector.rbegin();
+ Vector<int>::reverse_iterator end = int_vector.rend();
+ EXPECT_TRUE(end != it);
+
+ EXPECT_EQ(13, *it);
+ ++it;
+ EXPECT_EQ(12, *it);
+ ++it;
+ EXPECT_EQ(11, *it);
+ ++it;
+ EXPECT_EQ(10, *it);
+ ++it;
+
+ EXPECT_TRUE(end == it);
+}
+
+typedef WTF::Vector<std::unique_ptr<DestructCounter>> OwnPtrVector;
+
+TEST(VectorTest, OwnPtr) {
+ int destruct_number = 0;
+ OwnPtrVector vector;
+ vector.push_back(std::make_unique<DestructCounter>(0, &destruct_number));
+ vector.push_back(std::make_unique<DestructCounter>(1, &destruct_number));
+ EXPECT_EQ(2u, vector.size());
+
+ std::unique_ptr<DestructCounter>& counter0 = vector.front();
+ ASSERT_EQ(0, counter0->Get());
+ int counter1 = vector.back()->Get();
+ ASSERT_EQ(1, counter1);
+ ASSERT_EQ(0, destruct_number);
+
+ size_t index = 0;
+ for (OwnPtrVector::iterator iter = vector.begin(); iter != vector.end();
+ ++iter) {
+ std::unique_ptr<DestructCounter>* ref_counter = iter;
+ EXPECT_EQ(index, static_cast<size_t>(ref_counter->get()->Get()));
+ EXPECT_EQ(index, static_cast<size_t>((*ref_counter)->Get()));
+ index++;
+ }
+ EXPECT_EQ(0, destruct_number);
+
+ for (index = 0; index < vector.size(); index++) {
+ std::unique_ptr<DestructCounter>& ref_counter = vector[index];
+ EXPECT_EQ(index, static_cast<size_t>(ref_counter->Get()));
+ }
+ EXPECT_EQ(0, destruct_number);
+
+ EXPECT_EQ(0, vector[0]->Get());
+ EXPECT_EQ(1, vector[1]->Get());
+ vector.EraseAt(0);
+ EXPECT_EQ(1, vector[0]->Get());
+ EXPECT_EQ(1u, vector.size());
+ EXPECT_EQ(1, destruct_number);
+
+ std::unique_ptr<DestructCounter> own_counter1 = std::move(vector[0]);
+ vector.EraseAt(0);
+ ASSERT_EQ(counter1, own_counter1->Get());
+ ASSERT_EQ(0u, vector.size());
+ ASSERT_EQ(1, destruct_number);
+
+ own_counter1.reset();
+ EXPECT_EQ(2, destruct_number);
+
+ size_t count = 1025;
+ destruct_number = 0;
+ for (size_t i = 0; i < count; i++)
+ vector.push_front(std::make_unique<DestructCounter>(i, &destruct_number));
+
+ // Vector relocation must not destruct std::unique_ptr element.
+ EXPECT_EQ(0, destruct_number);
+ EXPECT_EQ(count, vector.size());
+
+ OwnPtrVector copy_vector;
+ vector.swap(copy_vector);
+ EXPECT_EQ(0, destruct_number);
+ EXPECT_EQ(count, copy_vector.size());
+ EXPECT_EQ(0u, vector.size());
+
+ copy_vector.clear();
+ EXPECT_EQ(count, static_cast<size_t>(destruct_number));
+}
+
+TEST(VectorTest, MoveOnlyType) {
+ WTF::Vector<MoveOnly> vector;
+ vector.push_back(MoveOnly(1));
+ vector.push_back(MoveOnly(2));
+ EXPECT_EQ(2u, vector.size());
+
+ ASSERT_EQ(1, vector.front().Value());
+ ASSERT_EQ(2, vector.back().Value());
+
+ vector.EraseAt(0);
+ EXPECT_EQ(2, vector[0].Value());
+ EXPECT_EQ(1u, vector.size());
+
+ MoveOnly move_only(std::move(vector[0]));
+ vector.EraseAt(0);
+ ASSERT_EQ(2, move_only.Value());
+ ASSERT_EQ(0u, vector.size());
+
+ size_t count = vector.capacity() + 1;
+ for (size_t i = 0; i < count; i++)
+ vector.push_back(
+ MoveOnly(i + 1)); // +1 to distinguish from default-constructed.
+
+ // Reallocation did not affect the vector's content.
+ EXPECT_EQ(count, vector.size());
+ for (size_t i = 0; i < vector.size(); i++)
+ EXPECT_EQ(static_cast<int>(i + 1), vector[i].Value());
+
+ WTF::Vector<MoveOnly> other_vector;
+ vector.swap(other_vector);
+ EXPECT_EQ(count, other_vector.size());
+ EXPECT_EQ(0u, vector.size());
+
+ vector = std::move(other_vector);
+ EXPECT_EQ(count, vector.size());
+}
+
+TEST(VectorTest, SwapWithInlineCapacity) {
+ const size_t kInlineCapacity = 2;
+ Vector<WrappedInt, kInlineCapacity> vector_a;
+ vector_a.push_back(WrappedInt(1));
+ Vector<WrappedInt, kInlineCapacity> vector_b;
+ vector_b.push_back(WrappedInt(2));
+
+ EXPECT_EQ(vector_a.size(), vector_b.size());
+ vector_a.swap(vector_b);
+
+ EXPECT_EQ(1u, vector_a.size());
+ EXPECT_EQ(2, vector_a.at(0).Get());
+ EXPECT_EQ(1u, vector_b.size());
+ EXPECT_EQ(1, vector_b.at(0).Get());
+
+ vector_a.push_back(WrappedInt(3));
+
+ EXPECT_GT(vector_a.size(), vector_b.size());
+ vector_a.swap(vector_b);
+
+ EXPECT_EQ(1u, vector_a.size());
+ EXPECT_EQ(1, vector_a.at(0).Get());
+ EXPECT_EQ(2u, vector_b.size());
+ EXPECT_EQ(2, vector_b.at(0).Get());
+ EXPECT_EQ(3, vector_b.at(1).Get());
+
+ EXPECT_LT(vector_a.size(), vector_b.size());
+ vector_a.swap(vector_b);
+
+ EXPECT_EQ(2u, vector_a.size());
+ EXPECT_EQ(2, vector_a.at(0).Get());
+ EXPECT_EQ(3, vector_a.at(1).Get());
+ EXPECT_EQ(1u, vector_b.size());
+ EXPECT_EQ(1, vector_b.at(0).Get());
+
+ vector_a.push_back(WrappedInt(4));
+ EXPECT_GT(vector_a.size(), kInlineCapacity);
+ vector_a.swap(vector_b);
+
+ EXPECT_EQ(1u, vector_a.size());
+ EXPECT_EQ(1, vector_a.at(0).Get());
+ EXPECT_EQ(3u, vector_b.size());
+ EXPECT_EQ(2, vector_b.at(0).Get());
+ EXPECT_EQ(3, vector_b.at(1).Get());
+ EXPECT_EQ(4, vector_b.at(2).Get());
+
+ vector_b.swap(vector_a);
+}
+
+#if defined(ANNOTATE_CONTIGUOUS_CONTAINER)
+TEST(VectorTest, ContainerAnnotations) {
+ Vector<int> vector_a;
+ vector_a.push_back(10);
+ vector_a.ReserveCapacity(32);
+
+ volatile int* int_pointer_a = vector_a.data();
+ EXPECT_DEATH(int_pointer_a[1] = 11, "container-overflow");
+ vector_a.push_back(11);
+ int_pointer_a[1] = 11;
+ EXPECT_DEATH(int_pointer_a[2] = 12, "container-overflow");
+ EXPECT_DEATH((void)int_pointer_a[2], "container-overflow");
+ vector_a.ShrinkToFit();
+ vector_a.ReserveCapacity(16);
+ int_pointer_a = vector_a.data();
+ EXPECT_DEATH((void)int_pointer_a[2], "container-overflow");
+
+ Vector<int> vector_b(vector_a);
+ vector_b.ReserveCapacity(16);
+ volatile int* int_pointer_b = vector_b.data();
+ EXPECT_DEATH((void)int_pointer_b[2], "container-overflow");
+
+ Vector<int> vector_c((Vector<int>(vector_a)));
+ volatile int* int_pointer_c = vector_c.data();
+ EXPECT_DEATH((void)int_pointer_c[2], "container-overflow");
+ vector_c.push_back(13);
+ vector_c.swap(vector_b);
+
+ volatile int* int_pointer_b2 = vector_b.data();
+ volatile int* int_pointer_c2 = vector_c.data();
+ int_pointer_b2[2] = 13;
+ EXPECT_DEATH((void)int_pointer_b2[3], "container-overflow");
+ EXPECT_DEATH((void)int_pointer_c2[2], "container-overflow");
+
+ vector_b = vector_c;
+ volatile int* int_pointer_b3 = vector_b.data();
+ EXPECT_DEATH((void)int_pointer_b3[2], "container-overflow");
+}
+#endif // defined(ANNOTATE_CONTIGUOUS_CONTAINER)
+
+class Comparable {};
+bool operator==(const Comparable& a, const Comparable& b) {
+ return true;
+}
+
+template <typename T>
+void Compare() {
+ EXPECT_TRUE(Vector<T>() == Vector<T>());
+ EXPECT_FALSE(Vector<T>(1) == Vector<T>(0));
+ EXPECT_FALSE(Vector<T>() == Vector<T>(1));
+ EXPECT_TRUE(Vector<T>(1) == Vector<T>(1));
+
+ Vector<T, 1> vector_with_inline_capacity;
+ EXPECT_TRUE(vector_with_inline_capacity == Vector<T>());
+ EXPECT_FALSE(vector_with_inline_capacity == Vector<T>(1));
+}
+
+TEST(VectorTest, Compare) {
+ Compare<int>();
+ Compare<Comparable>();
+ Compare<WTF::String>();
+}
+
+TEST(VectorTest, AppendFirst) {
+ Vector<WTF::String> vector;
+ vector.push_back("string");
+ // Test passes if it does not crash (reallocation did not make
+ // the input reference stale).
+ size_t limit = vector.capacity() + 1;
+ for (size_t i = 0; i < limit; i++)
+ vector.push_back(vector.front());
+
+ limit = vector.capacity() + 1;
+ for (size_t i = 0; i < limit; i++)
+ vector.push_back(const_cast<const WTF::String&>(vector.front()));
+}
+
+// The test below is for the following issue:
+//
+// https://bugs.chromium.org/p/chromium/issues/detail?id=592767
+//
+// where deleted copy assignment operator made canMoveWithMemcpy true because
+// of the implementation of std::is_trivially_move_assignable<T>.
+
+class MojoMoveOnlyType final {
+ public:
+ MojoMoveOnlyType();
+ MojoMoveOnlyType(MojoMoveOnlyType&&);
+ MojoMoveOnlyType& operator=(MojoMoveOnlyType&&);
+ ~MojoMoveOnlyType();
+
+ private:
+ MojoMoveOnlyType(const MojoMoveOnlyType&) = delete;
+ void operator=(const MojoMoveOnlyType&) = delete;
+};
+
+static_assert(!std::is_trivially_move_assignable<MojoMoveOnlyType>::value,
+ "MojoMoveOnlyType isn't trivially move assignable.");
+static_assert(!std::is_trivially_copy_assignable<MojoMoveOnlyType>::value,
+ "MojoMoveOnlyType isn't trivially copy assignable.");
+
+static_assert(!VectorTraits<MojoMoveOnlyType>::kCanMoveWithMemcpy,
+ "MojoMoveOnlyType can't be moved with memcpy.");
+static_assert(!VectorTraits<MojoMoveOnlyType>::kCanCopyWithMemcpy,
+ "MojoMoveOnlyType can't be copied with memcpy.");
+
+class VectorWithDifferingInlineCapacityTest
+ : public testing::TestWithParam<size_t> {};
+
+template <size_t inlineCapacity>
+void TestVectorDestructorAndConstructorCallsWhenSwappingWithInlineCapacity() {
+ LivenessCounter::live_ = 0;
+ LivenessCounter counter;
+ EXPECT_EQ(0u, LivenessCounter::live_);
+
+ Vector<scoped_refptr<LivenessCounter>, inlineCapacity> vector;
+ Vector<scoped_refptr<LivenessCounter>, inlineCapacity> vector2;
+ vector.push_back(&counter);
+ vector2.push_back(&counter);
+ EXPECT_EQ(2u, LivenessCounter::live_);
+
+ for (unsigned i = 0; i < 13; i++) {
+ for (unsigned j = 0; j < 13; j++) {
+ vector.clear();
+ vector2.clear();
+ EXPECT_EQ(0u, LivenessCounter::live_);
+
+ for (unsigned k = 0; k < j; k++)
+ vector.push_back(&counter);
+ EXPECT_EQ(j, LivenessCounter::live_);
+ EXPECT_EQ(j, vector.size());
+
+ for (unsigned k = 0; k < i; k++)
+ vector2.push_back(&counter);
+ EXPECT_EQ(i + j, LivenessCounter::live_);
+ EXPECT_EQ(i, vector2.size());
+
+ vector.swap(vector2);
+ EXPECT_EQ(i + j, LivenessCounter::live_);
+ EXPECT_EQ(i, vector.size());
+ EXPECT_EQ(j, vector2.size());
+
+ unsigned size = vector.size();
+ unsigned size2 = vector2.size();
+
+ for (unsigned k = 0; k < 5; k++) {
+ vector.swap(vector2);
+ std::swap(size, size2);
+ EXPECT_EQ(i + j, LivenessCounter::live_);
+ EXPECT_EQ(size, vector.size());
+ EXPECT_EQ(size2, vector2.size());
+
+ vector2.push_back(&counter);
+ vector2.EraseAt(0);
+ }
+ }
+ }
+}
+
+TEST(VectorTest, SwapWithConstructorsAndDestructors) {
+ TestVectorDestructorAndConstructorCallsWhenSwappingWithInlineCapacity<0>();
+ TestVectorDestructorAndConstructorCallsWhenSwappingWithInlineCapacity<2>();
+ TestVectorDestructorAndConstructorCallsWhenSwappingWithInlineCapacity<10>();
+}
+
+template <size_t inlineCapacity>
+void TestVectorValuesMovedAndSwappedWithInlineCapacity() {
+ Vector<unsigned, inlineCapacity> vector;
+ Vector<unsigned, inlineCapacity> vector2;
+
+ for (unsigned size = 0; size < 13; size++) {
+ for (unsigned size2 = 0; size2 < 13; size2++) {
+ vector.clear();
+ vector2.clear();
+ for (unsigned i = 0; i < size; i++)
+ vector.push_back(i);
+ for (unsigned i = 0; i < size2; i++)
+ vector2.push_back(i + 42);
+ EXPECT_EQ(size, vector.size());
+ EXPECT_EQ(size2, vector2.size());
+ vector.swap(vector2);
+ for (unsigned i = 0; i < size; i++)
+ EXPECT_EQ(i, vector2[i]);
+ for (unsigned i = 0; i < size2; i++)
+ EXPECT_EQ(i + 42, vector[i]);
+ }
+ }
+}
+
+TEST(VectorTest, ValuesMovedAndSwappedWithInlineCapacity) {
+ TestVectorValuesMovedAndSwappedWithInlineCapacity<0>();
+ TestVectorValuesMovedAndSwappedWithInlineCapacity<2>();
+ TestVectorValuesMovedAndSwappedWithInlineCapacity<10>();
+}
+
+TEST(VectorTest, UniquePtr) {
+ using Pointer = std::unique_ptr<int>;
+ Vector<Pointer> vector;
+ vector.push_back(Pointer(new int(1)));
+ vector.ReserveCapacity(2);
+ vector.UncheckedAppend(Pointer(new int(2)));
+ vector.insert(2, Pointer(new int(3)));
+ vector.push_front(Pointer(new int(0)));
+
+ ASSERT_EQ(4u, vector.size());
+ EXPECT_EQ(0, *vector[0]);
+ EXPECT_EQ(1, *vector[1]);
+ EXPECT_EQ(2, *vector[2]);
+ EXPECT_EQ(3, *vector[3]);
+
+ vector.Shrink(3);
+ EXPECT_EQ(3u, vector.size());
+ vector.Grow(4);
+ ASSERT_EQ(4u, vector.size());
+ EXPECT_TRUE(!vector[3]);
+ vector.EraseAt(3);
+ vector[0] = Pointer(new int(-1));
+ ASSERT_EQ(3u, vector.size());
+ EXPECT_EQ(-1, *vector[0]);
+}
+
+bool IsOneTwoThree(const Vector<int>& vector) {
+ return vector.size() == 3 && vector[0] == 1 && vector[1] == 2 &&
+ vector[2] == 3;
+}
+
+Vector<int> ReturnOneTwoThree() {
+ return {1, 2, 3};
+}
+
+TEST(VectorTest, InitializerList) {
+ Vector<int> empty({});
+ EXPECT_TRUE(empty.IsEmpty());
+
+ Vector<int> one({1});
+ ASSERT_EQ(1u, one.size());
+ EXPECT_EQ(1, one[0]);
+
+ Vector<int> one_two_three({1, 2, 3});
+ ASSERT_EQ(3u, one_two_three.size());
+ EXPECT_EQ(1, one_two_three[0]);
+ EXPECT_EQ(2, one_two_three[1]);
+ EXPECT_EQ(3, one_two_three[2]);
+
+ // Put some jank so we can check if the assignments later can clear them.
+ empty.push_back(9999);
+ one.push_back(9999);
+ one_two_three.push_back(9999);
+
+ empty = {};
+ EXPECT_TRUE(empty.IsEmpty());
+
+ one = {1};
+ ASSERT_EQ(1u, one.size());
+ EXPECT_EQ(1, one[0]);
+
+ one_two_three = {1, 2, 3};
+ ASSERT_EQ(3u, one_two_three.size());
+ EXPECT_EQ(1, one_two_three[0]);
+ EXPECT_EQ(2, one_two_three[1]);
+ EXPECT_EQ(3, one_two_three[2]);
+
+ // Other ways of construction: as a function parameter and in a return
+ // statement.
+ EXPECT_TRUE(IsOneTwoThree({1, 2, 3}));
+ EXPECT_TRUE(IsOneTwoThree(ReturnOneTwoThree()));
+
+ // The tests below correspond to the cases in the "if" branch in
+ // operator=(std::initializer_list<T>).
+
+ // Shrinking.
+ Vector<int, 1> vector1(3); // capacity = 3.
+ vector1 = {1, 2};
+ ASSERT_EQ(2u, vector1.size());
+ EXPECT_EQ(1, vector1[0]);
+ EXPECT_EQ(2, vector1[1]);
+
+ // Expanding.
+ Vector<int, 1> vector2(3);
+ vector2 = {1, 2, 3, 4};
+ ASSERT_EQ(4u, vector2.size());
+ EXPECT_EQ(1, vector2[0]);
+ EXPECT_EQ(2, vector2[1]);
+ EXPECT_EQ(3, vector2[2]);
+ EXPECT_EQ(4, vector2[3]);
+
+ // Exact match.
+ Vector<int, 1> vector3(3);
+ vector3 = {1, 2, 3};
+ ASSERT_EQ(3u, vector3.size());
+ EXPECT_EQ(1, vector3[0]);
+ EXPECT_EQ(2, vector3[1]);
+ EXPECT_EQ(3, vector3[2]);
+}
+
+TEST(VectorTest, Optional) {
+ Optional<Vector<int>> vector;
+ EXPECT_FALSE(vector);
+ vector.emplace(3);
+ EXPECT_TRUE(vector);
+ EXPECT_EQ(3u, vector->size());
+}
+
+TEST(VectorTest, emplace_back) {
+ struct Item {
+ Item() = default;
+ explicit Item(int value1) : value1(value1), value2() {}
+ Item(int value1, int value2) : value1(value1), value2(value2) {}
+ int value1;
+ int value2;
+ };
+
+ Vector<Item> vector;
+ vector.emplace_back(1, 2);
+ vector.emplace_back(3, 4);
+ vector.emplace_back(5);
+ vector.emplace_back();
+
+ EXPECT_EQ(4u, vector.size());
+
+ EXPECT_EQ(1, vector[0].value1);
+ EXPECT_EQ(2, vector[0].value2);
+
+ EXPECT_EQ(3, vector[1].value1);
+ EXPECT_EQ(4, vector[1].value2);
+
+ EXPECT_EQ(5, vector[2].value1);
+ EXPECT_EQ(0, vector[2].value2);
+
+ EXPECT_EQ(0, vector[3].value1);
+ EXPECT_EQ(0, vector[3].value2);
+
+ // Test returned value.
+ Item& item = vector.emplace_back(6, 7);
+ EXPECT_EQ(6, item.value1);
+ EXPECT_EQ(7, item.value2);
+}
+
+static_assert(VectorTraits<int>::kCanCopyWithMemcpy,
+ "int should be copied with memcopy.");
+static_assert(VectorTraits<char>::kCanCopyWithMemcpy,
+ "char should be copied with memcpy.");
+static_assert(VectorTraits<LChar>::kCanCopyWithMemcpy,
+ "LChar should be copied with memcpy.");
+static_assert(VectorTraits<UChar>::kCanCopyWithMemcpy,
+ "UChar should be copied with memcpy.");
+
+class UnknownType;
+static_assert(VectorTraits<UnknownType*>::kCanCopyWithMemcpy,
+ "Pointers should be copied with memcpy.");
+
+static_assert(!IsTraceable<Vector<int>>::value,
+ "Vector<int> must not be traceable.");
+
+} // anonymous namespace
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/vector_traits.h b/chromium/third_party/blink/renderer/platform/wtf/vector_traits.h
new file mode 100644
index 00000000000..c813151348e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/vector_traits.h
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_VECTOR_TRAITS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_VECTOR_TRAITS_H_
+
+#include <memory>
+#include <type_traits>
+#include <utility>
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/type_traits.h"
+
+namespace WTF {
+
+template <typename T>
+struct VectorTraitsBase {
+ static const bool kNeedsDestruction = !IsTriviallyDestructible<T>::value;
+
+ static constexpr bool kCanInitializeWithMemset =
+ std::is_trivially_default_constructible<T>::value;
+ // true iff memset(slot, 0, size) constructs an unused slot value that is
+ // valid for Oilpan to trace and if the value needs destruction, its
+ // destructor can be invoked over. The zero'ed value representing an unused
+ // slot in the vector's backing storage; it does not have to be equal to
+ // what its constructor(s) would create, only be valid for those two uses.
+ static constexpr bool kCanClearUnusedSlotsWithMemset =
+ std::is_trivially_destructible<T>::value &&
+ (!IsTraceable<T>::value || (std::is_trivially_constructible<T>::value &&
+ std::is_trivially_copyable<T>::value));
+
+ static constexpr bool kCanMoveWithMemcpy =
+ std::is_trivially_move_assignable<T>::value;
+ static constexpr bool kCanCopyWithMemcpy =
+ std::is_trivially_copy_assignable<T>::value;
+ static constexpr bool kCanFillWithMemset =
+ std::is_default_constructible<T>::value && (sizeof(T) == sizeof(char));
+ static constexpr bool kCanCompareWithMemcmp =
+ std::is_scalar<T>::value; // Types without padding.
+
+ // Supports swapping elements using regular std::swap semantics.
+ static const bool kCanSwapUsingCopyOrMove = true;
+
+ template <typename U = void>
+ struct IsTraceableInCollection {
+ static const bool value = IsTraceable<T>::value;
+ };
+ // We don't support weak handling in vectors.
+ static const WeakHandlingFlag kWeakHandlingFlag = kNoWeakHandling;
+
+ // Vectors do not support deleting values.
+ static constexpr bool kCanHaveDeletedValue = false;
+ static bool IsDeletedValue(T value) { return false; }
+};
+
+template <typename T>
+struct VectorTraits : VectorTraitsBase<T> {};
+
+// Classes marked with SimpleVectorTraits will use memmov, memcpy, memcmp
+// instead of constructors, copy operators, etc for initialization, move and
+// comparison.
+template <typename T>
+struct SimpleClassVectorTraits : VectorTraitsBase<T> {
+ static const bool kCanInitializeWithMemset = true;
+ static const bool kCanClearUnusedSlotsWithMemset = true;
+ static const bool kCanMoveWithMemcpy = true;
+ static const bool kCanCompareWithMemcmp = true;
+};
+
+// We know std::unique_ptr and RefPtr are simple enough that initializing to 0
+// and moving with memcpy (and then not destructing the original) will totally
+// work.
+template <typename P>
+struct VectorTraits<scoped_refptr<P>>
+ : SimpleClassVectorTraits<scoped_refptr<P>> {
+ // scoped_refptr cannot be copied using memcpy as the internals (e.g. ref
+ // count) depend on properly constructing the object.
+ static const bool kCanCopyWithMemcpy = false;
+};
+
+template <typename P>
+struct VectorTraits<std::unique_ptr<P>>
+ : SimpleClassVectorTraits<std::unique_ptr<P>> {
+ // std::unique_ptr -> std::unique_ptr has a very particular structure that
+ // tricks the normal type traits into thinking that the class is "trivially
+ // copyable".
+ static const bool kCanCopyWithMemcpy = false;
+};
+static_assert(VectorTraits<scoped_refptr<int>>::kCanInitializeWithMemset,
+ "inefficient RefPtr Vector");
+static_assert(VectorTraits<scoped_refptr<int>>::kCanMoveWithMemcpy,
+ "inefficient RefPtr Vector");
+static_assert(VectorTraits<scoped_refptr<int>>::kCanCompareWithMemcmp,
+ "inefficient RefPtr Vector");
+static_assert(VectorTraits<std::unique_ptr<int>>::kCanInitializeWithMemset,
+ "inefficient std::unique_ptr Vector");
+static_assert(VectorTraits<std::unique_ptr<int>>::kCanMoveWithMemcpy,
+ "inefficient std::unique_ptr Vector");
+static_assert(VectorTraits<std::unique_ptr<int>>::kCanCompareWithMemcmp,
+ "inefficient std::unique_ptr Vector");
+
+template <typename First, typename Second>
+struct VectorTraits<std::pair<First, Second>> {
+ typedef VectorTraits<First> FirstTraits;
+ typedef VectorTraits<Second> SecondTraits;
+
+ static const bool kNeedsDestruction =
+ FirstTraits::kNeedsDestruction || SecondTraits::kNeedsDestruction;
+ static const bool kCanInitializeWithMemset =
+ FirstTraits::kCanInitializeWithMemset &&
+ SecondTraits::kCanInitializeWithMemset;
+ static const bool kCanMoveWithMemcpy =
+ FirstTraits::kCanMoveWithMemcpy && SecondTraits::kCanMoveWithMemcpy;
+ static const bool kCanCopyWithMemcpy =
+ FirstTraits::kCanCopyWithMemcpy && SecondTraits::kCanCopyWithMemcpy;
+ static const bool kCanFillWithMemset = false;
+ static const bool kCanCompareWithMemcmp =
+ FirstTraits::kCanCompareWithMemcmp && SecondTraits::kCanCompareWithMemcmp;
+ static const bool kCanClearUnusedSlotsWithMemset =
+ FirstTraits::kCanClearUnusedSlotsWithMemset &&
+ SecondTraits::kCanClearUnusedSlotsWithMemset;
+ // Supports swapping elements using regular std::swap semantics.
+ static const bool kCanSwapUsingCopyOrMove = true;
+ template <typename U = void>
+ struct IsTraceableInCollection {
+ static const bool value =
+ IsTraceableInCollectionTrait<FirstTraits>::value ||
+ IsTraceableInCollectionTrait<SecondTraits>::value;
+ };
+ // We don't support weak handling in vectors.
+ static const WeakHandlingFlag kWeakHandlingFlag = kNoWeakHandling;
+
+ // Vectors do not support deleting values.
+ static constexpr bool kCanHaveDeletedValue = false;
+ static bool IsDeletedValue(std::pair<First, Second> value) { return false; }
+};
+
+} // namespace WTF
+
+#define WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(ClassName) \
+ namespace WTF { \
+ static_assert(!std::is_trivially_default_constructible<ClassName>::value || \
+ !std::is_trivially_move_assignable<ClassName>::value || \
+ !std::is_scalar<ClassName>::value, \
+ "macro not needed"); \
+ template <> \
+ struct VectorTraits<ClassName> : SimpleClassVectorTraits<ClassName> {}; \
+ }
+
+#define WTF_ALLOW_MOVE_AND_INIT_WITH_MEM_FUNCTIONS(ClassName) \
+ namespace WTF { \
+ static_assert(!std::is_trivially_default_constructible<ClassName>::value || \
+ !std::is_trivially_move_assignable<ClassName>::value, \
+ "macro not needed"); \
+ template <> \
+ struct VectorTraits<ClassName> : VectorTraitsBase<ClassName> { \
+ static const bool kCanInitializeWithMemset = true; \
+ static const bool kCanClearUnusedSlotsWithMemset = true; \
+ static const bool kCanMoveWithMemcpy = true; \
+ }; \
+ }
+
+#define WTF_ALLOW_INIT_WITH_MEM_FUNCTIONS(ClassName) \
+ namespace WTF { \
+ static_assert(!std::is_trivially_default_constructible<ClassName>::value, \
+ "macro not needed"); \
+ template <> \
+ struct VectorTraits<ClassName> : VectorTraitsBase<ClassName> { \
+ static const bool kCanInitializeWithMemset = true; \
+ static const bool kCanClearUnusedSlotsWithMemset = true; \
+ }; \
+ }
+
+#define WTF_ALLOW_CLEAR_UNUSED_SLOTS_WITH_MEM_FUNCTIONS(ClassName) \
+ namespace WTF { \
+ static_assert(!std::is_trivially_default_constructible<ClassName>::value, \
+ "macro not needed"); \
+ template <> \
+ struct VectorTraits<ClassName> : VectorTraitsBase<ClassName> { \
+ static const bool kCanClearUnusedSlotsWithMemset = true; \
+ }; \
+ }
+
+using WTF::VectorTraits;
+using WTF::SimpleClassVectorTraits;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_VECTOR_TRAITS_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/wtf.cc b/chromium/third_party/blink/renderer/platform/wtf/wtf.cc
new file mode 100644
index 00000000000..d283ca542f9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/wtf.cc
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2013 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 "third_party/blink/renderer/platform/wtf/wtf.h"
+
+#include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+#include "third_party/blink/renderer/platform/wtf/stack_util.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_statics.h"
+#include "third_party/blink/renderer/platform/wtf/thread_specific.h"
+#include "third_party/blink/renderer/platform/wtf/threading.h"
+#include "third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_contents.h"
+
+namespace WTF {
+
+extern void InitializeThreading();
+
+bool g_initialized;
+void (*g_call_on_main_thread_function)(MainThreadFunction, void*);
+ThreadIdentifier g_main_thread_identifier;
+
+namespace internal {
+
+void CallOnMainThread(MainThreadFunction* function, void* context) {
+ (*g_call_on_main_thread_function)(function, context);
+}
+
+} // namespace internal
+
+bool IsMainThread() {
+ return CurrentThread() == g_main_thread_identifier;
+}
+
+void Initialize(void (*call_on_main_thread_function)(MainThreadFunction,
+ void*)) {
+ // WTF, and Blink in general, cannot handle being re-initialized.
+ // Make that explicit here.
+ CHECK(!g_initialized);
+ g_initialized = true;
+ InitializeCurrentThread();
+ g_main_thread_identifier = CurrentThread();
+
+ InitializeThreading();
+
+ g_call_on_main_thread_function = call_on_main_thread_function;
+ internal::InitializeMainThreadStackEstimate();
+ AtomicString::Init();
+ StringStatics::Init();
+}
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/wtf.h b/chromium/third_party/blink/renderer/platform/wtf/wtf.h
new file mode 100644
index 00000000000..312b69d2f15
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/wtf.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_WTF_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_WTF_H_
+
+#include "third_party/blink/renderer/platform/wtf/compiler.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_export.h"
+
+namespace WTF {
+
+typedef void MainThreadFunction(void*);
+
+// This function must be called exactly once from the main thread before using
+// anything else in WTF.
+WTF_EXPORT void Initialize(void (*)(MainThreadFunction, void*));
+WTF_EXPORT bool IsMainThread();
+
+namespace internal {
+void CallOnMainThread(MainThreadFunction*, void* context);
+} // namespace internal
+
+} // namespace WTF
+
+using WTF::IsMainThread;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_WTF_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/wtf_export.h b/chromium/third_party/blink/renderer/platform/wtf/wtf_export.h
new file mode 100644
index 00000000000..da6d1245a4a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/wtf_export.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2013 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_WTF_EXPORT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_WTF_EXPORT_H_
+
+#if !defined(WTF_IMPLEMENTATION)
+#define WTF_IMPLEMENTATION 0
+#endif
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+#if WTF_IMPLEMENTATION
+#define WTF_EXPORT __declspec(dllexport)
+#else
+#define WTF_EXPORT __declspec(dllimport)
+#endif
+#else // defined(WIN32)
+#define WTF_EXPORT __attribute__((visibility("default")))
+#endif
+#else // defined(COMPONENT_BUILD)
+#define WTF_EXPORT
+#endif
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_WTF_EXPORT_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/wtf_test_helper.h b/chromium/third_party/blink/renderer/platform/wtf/wtf_test_helper.h
new file mode 100644
index 00000000000..94bb7909b73
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/wtf_test_helper.h
@@ -0,0 +1,250 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_WTF_TEST_HELPER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_WTF_TEST_HELPER_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/hash_functions.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+
+namespace WTF {
+
+class DestructCounter {
+ public:
+ explicit DestructCounter(int i, int* destruct_number)
+ : i_(i), destruct_number_(destruct_number) {}
+
+ ~DestructCounter() { ++(*destruct_number_); }
+ int Get() const { return i_; }
+
+ private:
+ int i_;
+ int* destruct_number_;
+};
+
+class MoveOnly {
+ public:
+ explicit MoveOnly(int i = 0) : i_(i) {}
+
+ MoveOnly(MoveOnly&& other) : i_(other.i_) { other.i_ = 0; }
+
+ MoveOnly& operator=(MoveOnly&& other) {
+ if (this != &other) {
+ i_ = other.i_;
+ other.i_ = 0;
+ }
+ return *this;
+ }
+
+ int Value() const { return i_; }
+
+ private:
+ int i_;
+
+ DISALLOW_COPY_AND_ASSIGN(MoveOnly);
+};
+
+class MoveOnlyHashValue {
+ public:
+ // kEmpty and kDeleted have special meanings when MoveOnlyHashValue is used as
+ // the key of a hash table.
+ enum { kEmpty = 0, kDeleted = -1, kMovedOut = -2 };
+
+ explicit MoveOnlyHashValue(int value = kEmpty, int id = 0)
+ : value_(value), id_(id) {}
+ MoveOnlyHashValue(MoveOnlyHashValue&& other)
+ : value_(other.value_), id_(other.id_) {
+ other.value_ = kMovedOut;
+ other.id_ = 0;
+ }
+ MoveOnlyHashValue& operator=(MoveOnlyHashValue&& other) {
+ value_ = other.value_;
+ id_ = other.id_;
+ other.value_ = kMovedOut;
+ other.id_ = 0;
+ return *this;
+ }
+
+ int Value() const { return value_; }
+ // id() is used for distinguishing MoveOnlys with the same value().
+ int Id() const { return id_; }
+
+ private:
+ MoveOnlyHashValue(const MoveOnlyHashValue&) = delete;
+ MoveOnlyHashValue& operator=(const MoveOnlyHashValue&) = delete;
+
+ int value_;
+ int id_;
+};
+
+struct MoveOnlyHashTraits : public GenericHashTraits<MoveOnlyHashValue> {
+ // This is actually true, but we pretend that it's false to disable the
+ // optimization.
+ static const bool kEmptyValueIsZero = false;
+
+ static const bool kHasIsEmptyValueFunction = true;
+ static bool IsEmptyValue(const MoveOnlyHashValue& value) {
+ return value.Value() == MoveOnlyHashValue::kEmpty;
+ }
+ static void ConstructDeletedValue(MoveOnlyHashValue& slot, bool) {
+ slot = MoveOnlyHashValue(MoveOnlyHashValue::kDeleted);
+ }
+ static bool IsDeletedValue(const MoveOnlyHashValue& value) {
+ return value.Value() == MoveOnlyHashValue::kDeleted;
+ }
+};
+
+struct MoveOnlyHash {
+ static unsigned GetHash(const MoveOnlyHashValue& value) {
+ return DefaultHash<int>::Hash::GetHash(value.Value());
+ }
+ static bool Equal(const MoveOnlyHashValue& left,
+ const MoveOnlyHashValue& right) {
+ return DefaultHash<int>::Hash::Equal(left.Value(), right.Value());
+ }
+ static const bool safe_to_compare_to_empty_or_deleted = true;
+};
+
+template <>
+struct HashTraits<MoveOnlyHashValue> : public MoveOnlyHashTraits {};
+
+template <>
+struct DefaultHash<MoveOnlyHashValue> {
+ using Hash = MoveOnlyHash;
+};
+
+class CountCopy final {
+ public:
+ static int* const kDeletedValue;
+
+ CountCopy() : counter_(nullptr) {}
+ explicit CountCopy(int* counter) : counter_(counter) {}
+ explicit CountCopy(int& counter) : counter_(&counter) {}
+ CountCopy(const CountCopy& other) : counter_(other.counter_) {
+ if (counter_ && counter_ != kDeletedValue)
+ ++*counter_;
+ }
+ CountCopy& operator=(const CountCopy& other) {
+ counter_ = other.counter_;
+ if (counter_ && counter_ != kDeletedValue)
+ ++*counter_;
+ return *this;
+ }
+
+ const int* Counter() const { return counter_; }
+
+ private:
+ int* counter_;
+};
+
+struct CountCopyHashTraits : public GenericHashTraits<CountCopy> {
+ static const bool kEmptyValueIsZero = false;
+ static const bool kHasIsEmptyValueFunction = true;
+ static bool IsEmptyValue(const CountCopy& value) { return !value.Counter(); }
+ static void ConstructDeletedValue(CountCopy& slot, bool) {
+ slot = CountCopy(CountCopy::kDeletedValue);
+ }
+ static bool IsDeletedValue(const CountCopy& value) {
+ return value.Counter() == CountCopy::kDeletedValue;
+ }
+};
+
+struct CountCopyHash : public PtrHash<const int*> {
+ static unsigned GetHash(const CountCopy& value) {
+ return PtrHash<const int>::GetHash(value.Counter());
+ }
+ static bool Equal(const CountCopy& left, const CountCopy& right) {
+ return PtrHash<const int>::Equal(left.Counter(), right.Counter());
+ }
+ static const bool safe_to_compare_to_empty_or_deleted = true;
+};
+
+template <>
+struct HashTraits<CountCopy> : public CountCopyHashTraits {};
+
+template <>
+struct DefaultHash<CountCopy> {
+ using Hash = CountCopyHash;
+};
+
+class DummyRefCounted : public RefCounted<DummyRefCounted> {
+ public:
+ DummyRefCounted(bool& is_deleted) : is_deleted_(is_deleted) {
+ is_deleted_ = false;
+ }
+ ~DummyRefCounted() {
+ DCHECK(!is_deleted_);
+ is_deleted_ = true;
+ }
+
+ void AddRef() {
+ DCHECK(!is_deleted_);
+ WTF::RefCounted<DummyRefCounted>::AddRef();
+ ++ref_invokes_count_;
+ }
+
+ void Release() {
+ DCHECK(!is_deleted_);
+ WTF::RefCounted<DummyRefCounted>::Release();
+ }
+
+ static int ref_invokes_count_;
+
+ private:
+ bool& is_deleted_;
+};
+
+struct Dummy {
+ Dummy(bool& deleted) : deleted(deleted) {}
+
+ ~Dummy() { deleted = true; }
+
+ bool& deleted;
+};
+
+// WrappedInt class will fail if it was memmoved or memcpyed.
+extern HashSet<void*> g_constructed_wrapped_ints;
+class WrappedInt {
+ public:
+ WrappedInt(int i = 0) : original_this_ptr_(this), i_(i) {
+ g_constructed_wrapped_ints.insert(this);
+ }
+
+ WrappedInt(const WrappedInt& other) : original_this_ptr_(this), i_(other.i_) {
+ g_constructed_wrapped_ints.insert(this);
+ }
+
+ WrappedInt& operator=(const WrappedInt& other) {
+ i_ = other.i_;
+ return *this;
+ }
+
+ ~WrappedInt() {
+ EXPECT_EQ(original_this_ptr_, this);
+ EXPECT_TRUE(g_constructed_wrapped_ints.Contains(this));
+ g_constructed_wrapped_ints.erase(this);
+ }
+
+ int Get() const { return i_; }
+
+ private:
+ void* original_this_ptr_;
+ int i_;
+};
+
+class LivenessCounter {
+ public:
+ void AddRef() { live_++; }
+ void Release() { live_--; }
+
+ static unsigned live_;
+};
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_WTF_TEST_HELPER_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/wtf_thread_data.cc b/chromium/third_party/blink/renderer/platform/wtf/wtf_thread_data.cc
new file mode 100644
index 00000000000..91bdf2e9caf
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/wtf_thread_data.cc
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2008, 2010 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 "third_party/blink/renderer/platform/wtf/wtf_thread_data.h"
+
+#include "third_party/blink/renderer/platform/wtf/stack_util.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string_table.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_codec_icu.h"
+
+namespace WTF {
+
+ThreadSpecific<WTFThreadData>* WTFThreadData::static_data_;
+
+WTFThreadData::WTFThreadData()
+ : atomic_string_table_(new AtomicStringTable),
+ cached_converter_icu_(new ICUConverterWrapper),
+ thread_id_(internal::CurrentThreadSyscall()) {}
+
+WTFThreadData::~WTFThreadData() = default;
+
+void WTFThreadData::Initialize() {
+ DCHECK(!WTFThreadData::static_data_);
+ WTFThreadData::static_data_ = new ThreadSpecific<WTFThreadData>;
+ WtfThreadData();
+}
+
+#if defined(OS_WIN) && defined(COMPILER_MSVC)
+size_t WTFThreadData::ThreadStackSize() {
+ // Needed to bootstrap WTFThreadData on Windows, because this value is needed
+ // before the main thread data is fully initialized.
+ if (!WTFThreadData::static_data_->IsSet())
+ return internal::ThreadStackSize();
+
+ WTFThreadData& data = WtfThreadData();
+ if (!data.thread_stack_size_)
+ data.thread_stack_size_ = internal::ThreadStackSize();
+ return data.thread_stack_size_;
+}
+#endif
+
+} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/wtf_thread_data.h b/chromium/third_party/blink/renderer/platform/wtf/wtf_thread_data.h
new file mode 100644
index 00000000000..c45674e3df8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/wtf_thread_data.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2008 Apple 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:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_WTF_THREAD_DATA_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_WTF_THREAD_DATA_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/thread_specific.h"
+#include "third_party/blink/renderer/platform/wtf/threading.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_export.h"
+
+namespace WTF {
+
+class AtomicStringTable;
+struct ICUConverterWrapper;
+
+class WTF_EXPORT WTFThreadData {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ WTFThreadData();
+ ~WTFThreadData();
+
+ AtomicStringTable& GetAtomicStringTable() { return *atomic_string_table_; }
+
+ ICUConverterWrapper& CachedConverterICU() { return *cached_converter_icu_; }
+
+ ThreadIdentifier ThreadId() const { return thread_id_; }
+
+ // Must be called on the main thread before any callers to wtfThreadData().
+ static void Initialize();
+
+#if defined(OS_WIN) && defined(COMPILER_MSVC)
+ static size_t ThreadStackSize();
+#endif
+
+ private:
+ std::unique_ptr<AtomicStringTable> atomic_string_table_;
+ std::unique_ptr<ICUConverterWrapper> cached_converter_icu_;
+
+ ThreadIdentifier thread_id_;
+
+#if defined(OS_WIN) && defined(COMPILER_MSVC)
+ size_t thread_stack_size_ = 0u;
+#endif
+
+ static ThreadSpecific<WTFThreadData>* static_data_;
+ friend WTFThreadData& WtfThreadData();
+
+ DISALLOW_COPY_AND_ASSIGN(WTFThreadData);
+};
+
+inline WTFThreadData& WtfThreadData() {
+ DCHECK(WTFThreadData::static_data_);
+ return **WTFThreadData::static_data_;
+}
+
+} // namespace WTF
+
+using WTF::WTFThreadData;
+using WTF::WtfThreadData;
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_WTF_THREAD_DATA_H_